首页 > 技术文章 > Java 注解和反射 - 总结概述

gltou 2021-12-06 10:58 原文

注解

什么是注解

  • 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<CLASS<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             <clinit>(){
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 }

推荐阅读