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、获取反射对象
-
Class类

- 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()方法创建对象
-
使用该方法创建的对象,必须要有一个无参构造器
-
类的构造器需要足够的权限 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获取的构造器在创建对象
如果需要类中有参构造方法来创建对象,有一下步骤:
-
通过Class对象getDeclaredConstructor(Class... 参数class)方法来获取到指定构造方法,将需要的参数类型class,传进该方法的形参数组
-
通过该构造器对象.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实体类的方法,步骤如下:
-
先获取到指定class的实体对象
-
在通过该class类的getDeclaredMethod(方法名,参数类型)方法获取class中指定方法对象(set)
-
用该方法对象调用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实体类的属性,步骤如下:
-
同样也是先获取到指定class创建的实体对象
-
在通过class类的getDeclaredField("属性名")获取指定属性对象
-
用该属性(方法)调用set(指定该属性所属的对象,实参值)来激活该属性(方法)。与上面激活方法相同
-
注意:如果该属性或方法权限是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、通过反射获取到泛型信息, 步骤如下:
-
获取指定类的指定方法。
-
用该方法对象调用getGenericParameterTypes() 或getGenericReturnType()方法用来分别获取该方法对象的泛型参数类型、泛型返回类型
-
判断该参数类型、返回值类型是否是参数化类型(即带<>的泛型)
-
有则将该类型强转为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、通过反射操作注解
操作的方法:
-
getAnnotations():用来获取该class类里面的所有类位置的注解
-
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 }



浙公网安备 33010602011771号