Java 注解和反射 - 总结概述
注解
什么是注解
- Annotation是从JDK5.0开始引入的新技术
- Annotation的作用:
- 不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别)
- 可以被其他程序(比如:编译器等)读取
- Annotation的格式:
- 注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value="unchecked")
- Annotation在哪里使用?
- 可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
内置注解
- @Override:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明
- @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择
- @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了
- @SuppressWarinings("all")
- @SuppressWarinings("unchecked")
- @SuppressWarinings(value={"unchecked","deprecation"})
- 等等.......
1 //什么是注解 2 public class Test01 extends Object { 3 4 //@Override 重写的注解 5 @Override 6 public String toString() { 7 return super.toString(); 8 } 9 10 //Deprecated 不推荐程序员使用,但是可以使用,或者存在更好的方式 11 @Deprecated 12 public static void test(){ 13 System.out.println("Deprecated"); 14 } 15 16 //镇压警告 17 @SuppressWarnings("all") 18 public void test02(){ 19 List list = new ArrayList(); 20 } 21 public static void main(String[] args) { 22 test(); 23 } 24 }
元注解
- 元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
- 这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)
- @Target:用来描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用来描述注解的生命周期(SOURCE
RUNTIME) - @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
1 public class Test02 { 2 3 @MyAnnotation 4 public void test(){ 5 } 6 } 7 8 //定义一个注解 9 //Target 表示我们的注解可以用在哪些地方:ElementType.METHOD(用在方法上),ElementType.TYPE(可以用在类上) 10 @Target(value = {ElementType.METHOD,ElementType.TYPE}) 11 12 //Retention 表示我们的注解在什么地方有效;RUNTIME:运行时,CLASS:编译,SOURCE:源码 13 //runtime>class>source 14 @Retention(value = RetentionPolicy.RUNTIME) 15 16 //Documented 表示是否将我们的注解生成在JavaDoc文档中 17 @Documented 18 19 //Inherited 子类可以继承父类的注解 20 @Inherited 21 @interface MyAnnotation{}
自定义注解
- 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
- 分析:
- @interface用来声明一个注解,格式:public @interface 注解名{定义内容}
- 其中的每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
1 //自定义注解 2 public class Test03 { 3 4 @MyAnnotation02(age = 18,name = "张三") 5 public void test(){} 6 7 @MyAnnotation03("李四") 8 public void test02(){} 9 } 10 11 @Target({ElementType.TYPE,ElementType.METHOD}) 12 @Retention(RetentionPolicy.RUNTIME) 13 @interface MyAnnotation02{ 14 //注解的参数格式:参数类型 + 参数名(); 15 String name() default ""; //default默认该参数为空,使用该注解,此参数不需要必传 16 int age(); 17 int id() default -1;//如果默认值为-1,代表不存在 18 String[] schools() default {"北京大学"}; 19 } 20 21 @Target({ElementType.TYPE,ElementType.METHOD}) 22 @Retention(RetentionPolicy.RUNTIME) 23 @interface MyAnnotation03{ 24 String value(); //注解只有一个参数的时候,建议参数名为value,这样使用该注解时,就不需要在输入参数名,直接输入值即可 25 }
反射
静态VS动态语言
动态语言
- 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点讲就是在运行时代码可以根据某些条件改变自身结构
- 主要动态语言:Object-C、C#、JavaScript、PHP、Python等
静态语言
- 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如:Java、C、C++
- Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活
Java Reflection反射
- Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
- Class c = Class.forName("java.lang.String")
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,通过这个镜子看到类的结构,所以,我们形象的称之为:反射
- Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- .....
- Java反射的优点:可以实现动态创建对象和编译,体现出很大的灵活性
- Java反射的缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作
- 反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- .......
1 //什么叫反射 2 public class Test01 { 3 public static void main(String[] args) throws ClassNotFoundException { 4 //通过反射获取类的class对象 5 Class c1 = Class.forName("reflection.User"); 6 System.out.println(c1); 7 8 Class c2 = Class.forName("reflection.User"); 9 Class c3 = Class.forName("reflection.User"); 10 Class c4 = Class.forName("reflection.User"); 11 12 //一个类在内存中只有一个class对象 13 //一个类被加载后,类的整个结构都会被封装在class对象中 14 System.out.println(c2.hashCode()); 15 System.out.println(c3.hashCode()); 16 System.out.println(c4.hashCode()); 17 } 18 } 19 20 //实体类:pojo,entity 21 class User{ 22 private String name; 23 private int id; 24 private int age; 25 26 public User() { 27 } 28 29 public User(String name, int id, int age) { 30 this.name = name; 31 this.id = id; 32 this.age = age; 33 } 34 35 @Override 36 public String toString() { 37 return "User{" + 38 "name='" + name + '\'' + 39 ", id=" + id + 40 ", age=" + age + 41 '}'; 42 } 43 44 public String getName() { 45 return name; 46 } 47 48 public void setName(String name) { 49 this.name = name; 50 } 51 52 public int getId() { 53 return id; 54 } 55 56 public void setId(int id) { 57 this.id = id; 58 } 59 60 public int getAge() { 61 return age; 62 } 63 64 public void setAge(int age) { 65 this.age = age; 66 } 67 }
得到Class类的几种方式
1 //测试class类的创建方式有哪些 2 public class Test02 { 3 public static void main(String[] args) throws ClassNotFoundException { 4 Person person = new Student(); 5 System.out.println("这个人是:"+person.name); 6 7 //方式一:通过对象获得 8 Class c1 = person.getClass(); 9 System.out.println(c1.hashCode()); 10 11 //方式二:forname获得 12 Class c2 = Class.forName("reflection.Student"); 13 System.out.println(c2.hashCode()); 14 15 //方式三:通过类名.class获得 16 Class c3 = Student.class; 17 System.out.println(c3.hashCode()); 18 19 //方式四:基本内置类型的包装类都有一个Type属性 20 Class c4 = Integer.TYPE; 21 System.out.println(c4); 22 23 //获得父类类型 24 Class c5 = c1.getSuperclass(); 25 System.out.println(c5); 26 } 27 } 28 29 class Person{ 30 public String name; 31 32 public Person() { 33 } 34 35 public Person(String name) { 36 this.name = name; 37 } 38 39 @Override 40 public String toString() { 41 return "Person{" + 42 "name='" + name + '\'' + 43 '}'; 44 } 45 } 46 47 class Student extends Person{ 48 public Student(){ 49 this.name="学生"; 50 } 51 } 52 53 class Teacher extends Person{ 54 public Teacher(){ 55 this.name="老师"; 56 } 57 }
所有类型的Class对象
1 //所有类型的class 2 public class Test03 { 3 public static void main(String[] args) { 4 Class c1 = Object.class; //类 5 Class c2 = Comparable.class; //接口 6 Class c3 = String[].class; //一维数组 7 Class c4 = int[][].class;//二维数组 8 Class c5 = Override.class;//注解 9 Class c6 = ElementType.class;//枚举 10 Class c7 = Integer.class;//基本数据类型 11 Class c8 = void.class;//void 12 Class c9 = Class.class;//Class 13 14 System.out.println(c1); 15 System.out.println(c2); 16 System.out.println(c3); 17 System.out.println(c4); 18 System.out.println(c5); 19 System.out.println(c6); 20 System.out.println(c7); 21 System.out.println(c8); 22 System.out.println(c9); 23 24 //只要元素类型与维度一样,就是同一个Class 25 int[] a = new int[10]; 26 int[] b = new int[100]; 27 System.out.println(a.getClass().hashCode()); 28 System.out.println(b.getClass().hashCode()); 29 } 30 }
获取类的运行时结构
1 //反射的作用:获得类的信息 2 public class Test08 { 3 public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { 4 Class c1 = Class.forName("reflection.User"); 5 6 //1.获得类的名字 7 System.out.println(c1.getName()); //类名+包名 8 System.out.println(c1.getSimpleName());//类名 9 10 //2.获得类的属性 11 //2.1只能找到public属性 12 Field[] fields = c1.getFields(); 13 for(Field f : fields){ 14 System.out.println(f); 15 } 16 //2.2可以找到所有属性 17 Field[] fields1 = c1.getDeclaredFields(); 18 for(Field f : fields1){ 19 System.out.println(f); 20 } 21 //2.3只能找到public的指定属性 22 Field top = c1.getField("top"); 23 System.out.println(top); 24 //2.4找到类的指定属性 25 Field name = c1.getDeclaredField("name"); 26 System.out.println(name); 27 28 //3.获得类的方法 29 //3.1 获得本类及其父类的全部public方法 30 Method[] methods = c1.getMethods(); 31 for (Method method : methods) { 32 System.out.println(method); 33 } 34 //3.2 获得本类的所有方法 35 Method[] declaredMethods = c1.getDeclaredMethods(); 36 for (Method declaredMethod : declaredMethods) { 37 System.out.println(declaredMethod); 38 } 39 //3.3 获得指定的方法 40 //获得指定方法一定要加参数,因为牵扯到方法的重载,方法名一样,参数不一样;不传参数,不知道你要获得哪个方法;参数丢类型即可 41 Method getName = c1.getMethod("getName", null); 42 Method setName = c1.getMethod("setName", String.class); 43 System.out.println(getName); 44 System.out.println(setName); 45 46 //4.获得指定的构造器 47 //4.1 获得本类public的构造方法 48 Constructor[] constructors = c1.getConstructors(); 49 for (Constructor constructor : constructors) { 50 System.out.println(constructor); 51 } 52 //4.2 获得本类所有的构造方法 53 Constructor[] declaredConstructors = c1.getDeclaredConstructors(); 54 for (Constructor declaredConstructor : declaredConstructors) { 55 System.out.println(declaredConstructor); 56 } 57 //4.3 获得指定的构造器 58 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); 59 System.out.println(declaredConstructor); 60 } 61 } 62 63 64 //实体类:pojo,entity 65 class User{ 66 private String name; 67 private int id; 68 private int age; 69 70 public int top=180; 71 72 public User() { 73 } 74 75 public User(String name, int id, int age) { 76 this.name = name; 77 this.id = id; 78 this.age = age; 79 } 80 81 @Override 82 public String toString() { 83 return "User{" + 84 "name='" + name + '\'' + 85 ", id=" + id + 86 ", age=" + age + 87 '}'; 88 } 89 90 public String getName() { 91 return name; 92 } 93 94 public void setName(String name) { 95 this.name = name; 96 } 97 98 public int getId() { 99 return id; 100 } 101 102 public void setId(int id) { 103 this.id = id; 104 } 105 106 public int getAge() { 107 return age; 108 } 109 110 public void setAge(int age) { 111 this.age = age; 112 } 113 }
动态创建对象执行方法
1 //动态的创建对象,通过反射 2 public class Test09 { 3 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { 4 //获得class对象 5 Class c1 = Class.forName("reflection.User"); 6 7 //构造一个对象 8 //User user = (User) c1.newInstance(); //本质上是调用了类的无参构造器。如果类没有无参构造器则会报错 9 //System.out.println(user); 10 11 //如果类没有无参构造器,怎么办? 12 //通过构造器创建对象 13 //Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class); 14 //User user1 = (User) constructor.newInstance("张三", 001, 20); 15 //System.out.println(user1); 16 17 //通过反射调用普通方法 18 User user2 = (User) c1.newInstance(); 19 //通过反射获取一个方法 20 Method setName = c1.getDeclaredMethod("setName", String.class); 21 22 //invoke:激活的意思 23 //(对象,“方法的值”) 24 setName.invoke(user2,"Gelaotou"); 25 System.out.println(user2.getName()); //Gelaotou 26 27 //通过反射操作属性 28 User user3 = (User) c1.newInstance(); 29 Field name = c1.getDeclaredField("name");//由于name是private私有属性,所以会报错 30 31 //不能直接操作私有属性,需要关闭程序的安全检测,私有的属性和方法使用setAccessible(true)关闭检测 32 name.setAccessible(true);//关闭程序的安全检测 33 34 name.set(user3,"张三"); 35 System.out.println(user3.getName()); 36 } 37 }1 //性能对比分析 2 public class Test10 { 3 4 //普通方式调用 5 public static void test01(){ 6 User user = new User(); 7 long startTime=System.currentTimeMillis(); 8 for (int i = 0; i < 100000000; i++) { 9 user.getName(); 10 } 11 12 long endTime=System.currentTimeMillis(); 13 System.out.println("普通方式执行,花费的时间为:"+(endTime-startTime)+"ms"); 14 } 15 16 //反射方式调用 17 public static void test02() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { 18 User user = new User(); 19 Class c1 = user.getClass(); 20 21 Method getName = c1.getDeclaredMethod("getName",null); 22 23 long startTime=System.currentTimeMillis(); 24 25 for (int i = 0; i < 100000000; i++) { 26 getName.invoke(user,null); 27 } 28 29 long endTime=System.currentTimeMillis(); 30 System.out.println("反射方式调用执行,花费的时间为:"+(endTime-startTime)+"ms"); 31 } 32 33 //反射方式调用 关闭检测 34 public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 35 User user = new User(); 36 Class c2 = user.getClass(); 37 38 39 Method getName = c2.getDeclaredMethod("getName", null); 40 getName.setAccessible(true); 41 42 long startTime=System.currentTimeMillis(); 43 44 for (int i = 0; i < 100000000; i++) { 45 getName.invoke(user,null); 46 } 47 48 long endTime=System.currentTimeMillis(); 49 System.out.println("反射方式调用 关闭检测执行,花费的时间为:"+(endTime-startTime)+"ms"); 50 51 } 52 53 public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { 54 test01(); //2ms 55 test02(); //216ms 56 test03(); //111ms 57 58 //总结:反射调用过多,关闭检测,可以提高性能 59 } 60 }
类加载内存分析
1 public class Test05 { 2 public static void main(String[] args) { 3 A a = new A(); 4 System.out.println(a.m); 5 /* 6 1.加载到内存,会产生一个类对应class对象 7 2.链接,链接结束后,m=0 8 3.初始化 9(){ 10 System.out.println("A类静态代码块初始化"); 11 m=300; 12 m=100; 13 } 14 m=100 15 */ 16 } 17 } 18 19 class A{ 20 21 static { 22 System.out.println("A类静态代码块初始化"); 23 m=300; 24 } 25 static int m =100; 26 27 public A() { 28 System.out.println("A类的无参构造初始化"); 29 } 30 }1 //运行结果 2 A类静态代码块初始化 3 A类的无参构造初始化 4 100
分析类的初始化
1 //测试类什么时候会初始化 2 public class Test06 { 3 static { 4 System.out.println("Test06类静态代码块被加载"); 5 } 6 7 public static void main(String[] args) throws ClassNotFoundException { 8 //1.主动引用 9 //Son son = new Son(); 10 /* 11 //输出结果: 12 Test06类静态代码块被加载 13 父类静态代码块被加载 14 子类静态代码块被加载 15 */ 16 17 //2.反射也会产生主动引用 18 //Class.forName("reflection.Son"); 19 /* 20 //输出结果: 21 Test06类静态代码块被加载 22 父类静态代码块被加载 23 子类静态代码块被加载 24 */ 25 26 //3.类的被动引用,通过子类调用父类的静态变量,不会导致子类的初始化 27 //System.out.println(Son.b); 28 /* 29 //输出结果,不会导致子类的初始化: 30 Test06类静态代码块被加载 31 父类静态代码块被加载 32 2 33 */ 34 35 //4.类的被动引用,通过数组,不会导致类的任何初始化 36 //Son[] array=new Son[5]; 37 /* 38 //输出结果: 39 Test06类静态代码块被加载 40 */ 41 42 //5.类的被动引用,访问常量,不会导致类的任何初始化 43 System.out.println(Son.n); 44 /* 45 //输出结果: 46 Test06类静态代码块被加载 47 1 48 */ 49 } 50 } 51 52 class Father{ 53 static int b = 2; 54 static { 55 System.out.println("父类静态代码块被加载"); 56 } 57 } 58 59 class Son extends Father{ 60 static { 61 System.out.println("子类静态代码块被加载"); 62 m=300; 63 } 64 65 static int m=100; 66 static final int n=1; 67 }
类加载器
1 public class Test07 { 2 public static void main(String[] args) throws ClassNotFoundException { 3 //获取系统类的加载器 4 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 5 System.out.println(systemClassLoader); 6 7 //获取系统类加载器的父类加载器--->即扩展类加载器 8 ClassLoader parent = systemClassLoader.getParent(); 9 System.out.println(parent); 10 11 //获取扩展类加载器的父类加载器--->即根加载器(C、C++) 12 ClassLoader parent1 = parent.getParent(); 13 System.out.println(parent1); 14 15 //测试当前类是哪个加载器加载的 16 ClassLoader aClass = Class.forName("reflection.Test07").getClassLoader(); 17 System.out.println(aClass); 18 //测试JDK内置的类是谁加载的 19 ClassLoader aClass1 = Class.forName("java.lang.Object").getClassLoader(); 20 System.out.println(aClass1); 21 22 //如何获得系统类加载器可以加载的路径 23 System.out.println(System.getProperty("java.class.path")); 24 } 25 }