Java反射-笔记

1、反射的概述

1.1、动态语言vs静态语言

  • 动态语言:在运行时代码可有根据某些条件改变自身结构 javascript、PHP、python

  • 静态语言:在运行时代码不可变的语言 java、C、C++。 Java不是动态语言,但Java可以称之为"准动态语言”。即Java有一定的动态性,、我们可以利用反射机制获得类似动态语言的特性。

1.2、Reflection

  • Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象( 一个类只有一个Class对象), 这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以称之为反射

1.3、反射的功能

1.4、反射的优缺点

  • 优点:可以实现动态创建对象和编译,体现出恒大的灵活性

  • 缺点:对性能有影响。使用反射基本上是一种操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于 直接执行相同的操作

1.5、反射的作业API

1.6、获取反射对象

  1. Class类

  1. Class类的成常用方法

  • 用 Class.forName("类的路径"),就可以获取到指定类的class对象

1 Preson p = new Student();
2 3 //方式二:通过Class的forName()获取;
4 Class c2 = Class.forName("Test.Student");
5 System.out.println(c2.hashCode());
  • 用Object中的getClass()方法,来获取到调用该方法类的class对象

1 //方式一:通过对象.getClass()获取(多态形式就是获取new一方的class类)
2 Class c1=p.getClass();
3 System.out.println(c1.hashCode());

  • 用已知具体类来"."class,来获取到具体类的class对象,该方法最为安全可靠

1 //方法三:通过类.class获取
2 Class c3 = Teacher.class;
3 System.out.println(c3.hashCode());

 

  • 基本内置类型包装类都有一个TYPE属性,因为他的返回值是Class类型,所以可以用他来创建一个仅限基本内置类型的Class类

1 //方法四:基本内置类型包装类,可以用TYPT属性来获取class类
2 Class c4=Integer.TYPE;
3 System.out.println(c4.hashCode());

 

 1 Preson p = new Student();
 2 System.out.println(p);
 3  4 //因为方试一~方试三的class是一个类的,而一个类在内存中只有一个Class对象,所以哈希值相同
 5 //当一个类被加载后,类的整个结构都会被封装到class对象中
 6 //方式一:通过对象.getClass()获取(多态形式就是获取new一方的class类)
 7 Class c1 = p.getClass();
 8 System.out.println(c1.hashCode());
 9 10 //方式二:通过Class的forName()获取;
11 Class c2 = Class.forName("Test.Student");
12 System.out.println(c2.hashCode());
13 14 //方法三:通过类.class获取
15 Class c3 = Teacher.class;
16 System.out.println(c3.hashCode());
17 18 //方法四:基本内置类型包装类,可以用TYPT属性来获取class类
19 Class c4 = Integer.TYPE;
20 System.out.println(c4.hashCode());
21 22 //通过子类的Class获取到父类的Class 子类Class.getSuperclass();
23 Class c5 = c1.getSuperclass();
24 System.out.println(c5.hashCode());

 

1.7、有哪些类可以有Class对象

示例:

 1 Class c1 = Object.class; //
 2 Class c2 = Comparable.class; //接口
 3 Class c3 = int[].class; //因为数组
 4 Class c4 = Enum.class; //枚举
 5 Class c5 = Override.class; //注解
 6 Class c6 = Integer.class; //基本数据类型
 7 Class c7 = void.class; //void
 8 Class c8 = Class.class; //Class
 9 Class c9 = int[][].class; //二维数组
10 11 //数组中只要类型和维度相同的数组,就是同一个Class.(一个类只有一个class)
12 int[] i1 = new int[10];
13 int[] i2 = new int[100];
14 System.out.println(i1.getClass().hashCode());
15 System.out.println(i2.getClass().hashCode());

 

2、Java Class内存分析

2.1、类的加载:

类加载的示例:

 1 class A{
 2     static {
 3         System.out.println("A的静态代码块");
 4         m=300;
 5     }
 6     static int m=100;
 7     public A() {
 8         System.out.println("A的无参构造方法");
 9     }
10 }
11 public class Text4 {
12     public static void main(String[] args) {
13         A a=new A();
14         System.out.println(a.m);
15         /*
16         1.加载到内存,产生一个类对应Class对象
17         2.链接,链接结束后m= 0
18         3.初始化:将类变量与静态代码块语句合并
19             <clinit>(){
20                 System. out. println("A类静态代码块初始化") ;
21                 m = 300;
22                 m = 100;
23             }
24             所以最终m=100
25          */
26     }
27 }

2.2、类什么时候会发生初始化

 1 class fu{
 2     static {
 3         System.out.println("父类被加载");
 4     }
 5     static int a=0;
 6 }
 7 class zi extends fu{
 8     static {
 9         System.out.println("子类被加载");
10     }
11     static int f=111;
12     final static int M=100;
13 }

 

  • 类的主动引用(一定会发生类的初始化)

类主动引用来初始化的示例:

1 System.out.println("main");
2 //主动引用 初始化
3 //初始化子类时,如果父类没有初始化,那么先初始化父类
4 5 new zi(); //创建实例化,会初始化
6 Class.forName("Test.zi"); //创建指定类的class,会初始化
7 System.out.println(zi.f); //调用自身的静态元素,初始化
  • 类的被动引用(不会发生类的初始化)

类被动引用不初始化的示例:

1 //被动引用 不初始化:
2 System.out.println(zi.a); //子类调用父类的静态元素时,子类不会被初始化,而父类会
3 zi[] z = new zi[1]; //引用类型的数据,不会触发初始化
4 System.out.println(zi.M); //引用常量不会触发初始化

2.3、类加载器

类加载器的作用:用来把类(class)装载进内存。

JVM 双亲委派机制图:

比如自己定义的一个String类,创建String对象运行测试时,不会创建到自定义的String类,而是会创建启动类加载器的String类

获取类加载器的示例:

 1 //获取系统类的加载器
 2 ClassLoader c1 = ClassLoader.getSystemClassLoader();
 3 System.out.println(c1);
 4 //获取系统类加载器的父类加载器-->扩展类加载器
 5 ClassLoader c2 = c1.getParent();
 6 System.out.println(c2);
 7 //获取扩展类加载器的父类加载器-->根(引导)类加载器(C/C++) 引导类加载器无法直接获取
 8 ClassLoader c3 = c2.getParent();
 9 System.out.println(c3);
10 //测试当前类是那一个加载器加载的
11 ClassLoader c4 = Text6.class.getClassLoader();
12 System.out.println(c4);
13 //测试jdk内置的类是谁加载的 因为jdk内置的类是java核心库的类,也无法直接获取
14 ClassLoader c5 = Object.class.getClassLoader();
15 System.out.println(c5);
16 17 //获取系统类加载器可以加载的路径
18 System.out.println(System.getProperty("java.class.path"));

3、获取运行时类的完整结构

 1 Class c1 = Class.forName("Test.User");
 2  3 //获取类名
 4 System.out.println(c1.getName()); //获取包名+类名
 5 System.out.println(c1.getSimpleName()); //获取类名
 6 System.out.println("==============================");
 7  8 //获取类的属性
 9 Field[] f1 = c1.getFields(); //只能找到public修饰的属性
10 //      for(Field f1:f) {
11 //          System.out.println(f1);
12 //      }
13 f1 = c1.getDeclaredFields(); //可以找到全部属性
14 for (Field f: f1) {
15     System.out.println(f);
16 }
17 18 //获取指定属性
19 //      Field f2=c1.getField("name");
20 Field f2 = c1.getDeclaredField("name");
21 System.out.println(f2);
22 23 System.out.println("==============================");
24 25 //获得类的方法
26 Method[] m1 = c1.getMethods(); //获取本类以及父类的全部public方法
27 for (Method m: m1) {
28     System.out.println("getMethods:" + m);
29 }
30 m1 = c1.getDeclaredMethods();
31 for (Method m: m1) {
32     System.out.println("getDeclaredMethods:" + m);
33 }
34 35 //获取指定的方法 如有重载,需要指定路线来获取到需要的方法
36 Method m2 = c1.getMethod("getName", null);
37 System.out.println("指定方法:" + m2);
38 m2 = c1.getMethod("setName", String.class);
39 System.out.println("指定方法:" + m2);
40 41 System.out.println("==============================");
42 43 //获取类的构造器方法
44 Constructor[] con1 = c1.getConstructors(); //获取public修饰的构造器
45 for (Constructor con: con1) {
46     System.out.println("public:" + con);
47 }
48 con1 = c1.getDeclaredConstructors(); //获取全部构造器
49 for (Constructor con: con1) {
50     System.out.println("全部:" + con);
51 }
52 53 //获取指定构造器
54 Constructor con2 = c1.getDeclaredConstructor(int.class, String.class, int.class);
55 System.out.println(con2);

 

4、通过Class动态创建对象执行方法

4.1、创建用class对象创建该class对应实体类的对象

可以调用class对象的newIstance()方法创建对象

  1. 使用该方法创建的对象,必须要有一个无参构造器

  2. 类的构造器需要足够的权限 public

1 //获取class类
2 Class c1 = Class.forName("Test.User");
3 /*
4     通过newInstance()创建该class对象的实例化
5     该方法实例化的类,必须要有无参构造方法(不是默认的,要显示的)
6     可以认为他就是调用显示的无参构造器
7 */
8 User user1 = (User) c1.newInstance();
9 System.out.println(user1);

 

通过class获取的构造器在创建对象

如果需要类中有参构造方法来创建对象,有一下步骤:

  1. 通过Class对象getDeclaredConstructor(Class... 参数class)方法来获取到指定构造方法,将需要的参数类型class,传进该方法的形参数组

  2. 通过该构造器对象.newInstance(参数值s),获取到该构造方法创建的对象,并为构造方法参数赋值

1 //通过class获取构造器来创建对象
2 //获取到指定的构造器
3 Constructor con = c1.getDeclaredConstructor(int.class, String.class, int.class);
4 //通过newInstance()创建指定构造方法的对象,如果是有参构造方法可以设置参数
5 User user2 = (User) con.newInstance(1, "小明", 15);
6 System.out.println(user2);

通过反射来调用指定class实体类的方法,步骤如下:

  1. 先获取到指定class的实体对象

  2. 在通过该class类的getDeclaredMethod(方法名,参数类型)方法获取class中指定方法对象(set)

  3. 用该方法对象调用invoke(指定该方法所属的对象,实参值)激活该方法

1 //通过反射调用普通方法
2 User user3 = (User) c1.newInstance(); //通过class获取实例对象
3 Method setName = c1.getDeclaredMethod("setName", String.class); //获取class中指定的方法(参数)
4 setName.invoke(user3, "我是user3对象的方法"); //激活该方法,指定该方法是那个对象的,并且传参
5 System.out.println(user3.getName());

通过反射老操作class实体类的属性,步骤如下:

  1. 同样也是先获取到指定class创建的实体对象

  2. 在通过class类的getDeclaredField("属性名")获取指定属性对象

  3. 用该属性(方法)调用set(指定该属性所属的对象,实参值)来激活该属性(方法)。与上面激活方法相同

  4. 注意:如果该属性或方法权限是private,那么要操作到该属性或方法,则需要用获取到的属性(方法)对象调用setAccessible(true)来关闭安全检查

1 //通过反射操作属性
2 User user4 = (User) c1.newInstance(); //获取到class实体对象
3 Field name = c1.getDeclaredField("name"); //获取到该class的name属性
4 //不能直接操作私有属性(方法),需要关闭安全检查setAccessible(true)
5 name.setAccessible(true);
6 //该方法与反射调用方法的invoke()方法类似
7 name.set(user4, "我是user4的name属性值");
8 System.out.println(user4.getName());

性能对比:

  • 正常调用操作,速度最快一般在(9ms左右)

  • 用反射来调用操作,比会比较慢,如果要频繁使用反射,则可以是用setAccessible方法来提高性能

5、通过反射获取到泛型信息, 步骤如下:

  1. 获取指定类的指定方法。

  2. 用该方法对象调用getGenericParameterTypes() 或getGenericReturnType()方法用来分别获取该方法对象的泛型参数类型、泛型返回类型

  3. 判断该参数类型、返回值类型是否是参数化类型(即带<>的泛型)

  4. 有则将该类型强转为ParameterizedType参数化类型,在调用getActualTypeArguments()方法获取参数化类型里面的类型(<>里面的类型)

 1 //反射操作泛型
 2 public class Text10 {
 3     public void text1(Map<String,User> map,List list) {
 4         System.out.println("获取有泛型的方法的泛型类型");
 5     }
 6     public Map<String,User> text2(){
 7         System.out.println("获取返回值是泛型的方法,泛型的类型");
 8         return null;
 9     }
10     public static void main(String[] args) throws Exception {
11         Method method=Text10.class.getDeclaredMethod("text1", Map.class,List.class);
12         Type[] type=method.getGenericParameterTypes();//获取指定方法泛型参数的类型
13         for(Type fx:type) {
14             System.out.println("泛型参数类型:"+fx);
15             if(fx instanceof ParameterizedType) {//判断该泛型参数类型是否是参数化类型(<>)
16                 Type[] t=((ParameterizedType) fx).getActualTypeArguments();//获取到<>里面的参数类型
17                 for(Type cs:t) {
18                     System.out.println("泛型参数化类型:"+cs);
19                 }
20             }
21         }
22         
23         System.out.println("=================================");
24         
25         method=Text10.class.getDeclaredMethod("text2", null);//获取泛型返回值方法
26         Type type1=method.getGenericReturnType();//获取到该方法返回值泛型的类型
27         System.out.println("泛型参数类型:"+type1);
28         if(type1 instanceof ParameterizedType) {//判断该泛型返回类型是否是参数化类型(<>)
29             Type[] t=((ParameterizedType) type1).getActualTypeArguments();//获取到<>里面的参数类型
30             for(Type cs:t) {
31                 System.out.println("泛型参数化类型:"+cs);
32             }
33         }
34     }
35 }

 

6、通过反射操作注解

操作的方法:

  1. getAnnotations():用来获取该class类里面的所有类位置的注解

  2. getAnnotation(该class的注解的class):获取该类的指定的类注解对象,可以用来调用注解的方法。

该方法可以被属性和方法等对象调用,就可以操作指定属性(方法) 的 属性(方法)位置的注解

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @interface Tab{
 4     String value();
 5 }
 6  7 @Target(ElementType.FIELD)
 8 @Retention(RetentionPolicy.RUNTIME)
 9 @interface Fild{
10     String col();
11     String type();
12     int length();
13 }
14 15 @Tab(value = "student")
16 class Student2{
17     @Fild(col = "id",type="int",length=11)
18     private int id;
19     @Fild(col = "age",type="int",length=11)
20     private int age;
21     @Fild(col = "name",type="varchar",length=24)
22     private String name;
23 }
24 25 //反射操作注解
26 public class Text11 {
27     public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
28         Class c1=Class.forName("Test.Student2");
29         
30         Annotation[] ann=c1.getAnnotations();//获取该calss类的所有注解(类位置上的注解)
31         for(Annotation a:ann) {
32             System.out.println(a);
33         }
34         
35         //获取类注解的方法值
36         Tab ann1=(Tab)c1.getAnnotation(Tab.class);//获取该类的类注解对象
37         System.out.println(ann1.value());
38         
39         //获取获取类中指定的注解(类里面的注解 如属性注解)
40         Field field=c1.getDeclaredField("name");//获取class中属性
41         Fild fild=(Fild)field.getAnnotation(Fild.class);//获取该属性的注解对象
42         System.out.println(fild.col());//调用注解信息
43         System.out.println(fild.length());
44         System.out.println(fild.type());
45     }
46 }

 

posted @ 2020-04-12 21:10  Beat_All  阅读(176)  评论(0)    收藏  举报