一、反射(reflection) 反射: 加载完类之后,在堆中会产生一个 Class 类 对象(一个类只有一个 Class 类对象),这个类对象包含了该类的所有信息。 反射相关的主要类(反射中, 万物皆对象):
java.lang.Class 代表一个类 Class 对象表示某个类加载后再堆中的对象 java.lang.reflect.Method 代表类的方法 Method 对象表示某个类的方法 java.lang.reflect.Field 代表类的成员变量 Field 对象表示某个类的成员变量 java.lang.refect.Constructor 代表类的构造方法 Constructor 对象表示构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class CatDemo1 { public String name = "波斯喵" ; public CatDemo1 () { } public CatDemo1 (String name) { this .name = name; } public void hi () { System.out.println("招财猫~" ); } public void food () { System.out.println("猫吃鱼,狗吃肉~" ); } }
1 2 3 4 // classCat.properties className =com.reflection.CatDemo1 method :food
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Demo1 { public static void main (String[] args) throws Exception { Properties properties = new Properties (); properties.load(new FileInputStream ("src/classCat.properties" )); String className = properties.getProperty("className" ); System.out.println("className = " + className); String method = properties.getProperty("method" ); System.out.println("method = " + method); Class<?> cls = Class.forName(className); Object o = cls.newInstance(); Method clsMethod = cls.getMethod(method); clsMethod.invoke(o); Field name = cls.getField("name" ); System.out.println(name.get(o)); Constructor constructors = cls.getConstructor(); System.out.println(constructors); Constructor constructors1 = cls.getConstructor(String.class); System.out.println(constructors1); } }
1. Class 类 Class 也是类,也继承了 Object 类; Class 类对象不是 new 出来的,而是系统创建的; 对于某个类的 Class 类对象,在内存中只有一份,因此类只加载一次; 每个类的实例都会记得自己是由那个 Class 实例做生成; 通过 Class 对象可以完整地得到一个类的完成结构,通过一系列 API 操作; Class 对象时存在堆中; 类的字节码二进制数据,是放在方法区的,有的地方称为元数据(包括方法代码,变量名,方法名等) 反射优化:
Method 和 Field、Constructor 对象都有 setAccessible() 方法; setAccessible 作用是启动和禁用访问安全检查的开关true : 表示反射的对象在使用时取消访问检查, 提高反射效率; false : 表示反射对象执行访问检查; class 类的常用方法:
方法名 功能说明 static Class forName(String name) 返回指定类名 name 的 class 对象 Object newInstance() 调用缺省构造函数, 返回该 Class 对象的一个实例 getName() 返回此 Class 对象所表示的实体(类、接口等)名称 Class getSuperClass() 返回当前 Class 对象的父类的 Class 对象 Class [] getInterfaces() 获取当前 Class 对象的接口 ClassLoader getClassLoader() 返回该类的类加载器 Class getSuperclass() 返回表示此 Class 所表示的实体的 超类的 Class constructor[] getConstructors() 返回一个包含某些 Constructor 对象的数组 Field[] getDeclaredFields() 返回 Field 对象的一个数组 Method getMethod 返回一个 Method 对象, 此对象的形参类型为 paramType
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class ClassDemo2 { public static void main (String[] args) throws Exception { String pathAll = "com.reflection.Car" ; Class<?> cls = Class.forName(pathAll); System.out.println(cls); System.out.println(cls.getClass()); System.out.println(cls.getPackage().getName()); System.out.println(cls.getName()); Car car = (Car) cls.getConstructor().newInstance(); System.out.println(car); Field brand = cls.getField("brand" ); System.out.println(brand.get(car)); brand.set(car, "大奔" ); System.out.println(brand.get(car)); Field[] fields = cls.getFields(); for (int i = 0 ; i < fields.length; i++) { System.out.println(fields[i].get(car)); } } }
获取 Class 类对象的方法:
已知全类名, 且该类在类路径下, 可通过 Class 类的静态方法 forName() 获取;如 Class cls = Class.forName(“com.reflection.Car”); 主要应用于: 读取配置文件, 读取类全路径, 加载类 已知具体的类, 通过类的 Class 获取, 该方法最 安全可靠, 性能最高Class cls = Car.class; 主要应用于: 参数传递, 如通过反射得到对应构造器对象; 已知某个类的实例, 调用该实例的 getClass() 方法获取 class 对象Class cls = 对象.getClass() 主要应用于 : 通过创建好的对象, 获取 class 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class GetClass1 { public static void main (String[] args) throws Exception { String classPath = "com.reflection.Car" ; Class<?> cls1 = Class.forName(classPath); System.out.println(cls1); Class<Car> cls2 = Car.class; System.out.println(cls2); Car car = new Car (); Class<? extends Car > cls3 = car.getClass(); System.out.println(cls3); ClassLoader classLoader = car.getClass().getClassLoader(); Class<?> cls4 = classLoader.loadClass(classPath); System.out.println(cls4); } }
2. 类加载
静态加载 : 编译时加载相关的类, 如果没有则报错, 依赖性太强 动态加载 : 运行时加载需要的类, 如果运行时不用该类, 则不报错, 降低依赖性; 类加载的时机:
当创建对象时( new) - 静态加载; 当子类被加载时 - 静态加载; 调用类中的静态成员时 - 静态加载; 通过反射 - 动态加载;
类加载的三个阶段:
加载阶段 : 将字节码文件从不同的数据源 (可能是 class 文件、也可能是 jar 包、甚至网络) 转换为 二进制字节流加载到内存中 , 并生成一个代表该类的 java.long.Class 对象连接阶段 :验证 : 为了确保 Class 文件的 字节流中包含的信息符合当前 JVM 的要求 , 且不危害 JVM 自身安全;包括: 文件格式验证、元数据验证、字节码验证 和 符号引用验证 可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施, 缩短 JVM 类加载的时间; 准备 : 对静态变量 分配内存并初始化 (对应数据类的默认初始值, 如 0, null, false 等), 这些变量的初始值都会在方法区中进行分配;public int n1 = 10;
: 是实例属性, 在准备阶段不会分配内存public static int n2 = 20;
: 是静态变量, 会分配内存, 初始值是 0, 而不是 20;public static final int n3 = 30;
: 是常量, 一旦不知就不可变; 解析 : JVM 将常量池内的符号引用替换为直接引用的过程; 初始化阶段 : 真正开始执行类中定义的 Java 程序代码, 此阶段是执行 <clinit>()
方法的过程;<clinit>()
: 是由编译器按语句在源文件中出现的顺序, 依次自动收集类中的所有 静态变量 的赋值动作和静态代码块 中的语句, 并进行合并JVM 会保证一个类 <clinit>()
方法在多线程环境中被正确的加锁、同步, 如果多线程同时去初始化一个类, 那么只会有一个线程去执行这个类 <clinit>()
方法, 其他线程都会阻塞等待, 直到活动线程执行完毕; 3. 获取类的结构信息 1. 类对象 getName 获取全类名 getSimpleName 获取简单类名 getFields 获取所有 public 修饰的属性, 包括本类以及父类的 getDeclaredFields 获取本类中所有属性 getMethods 获取所有 public 修饰的方法, 包括 本类以及父类的 getDeclaredMethods 获取本类中的所有方法 getConstructors 获取所有 public 修饰的本类构造器 getDeclaredConstructors 获取本类中所有的构造器 getPackage 以 package 形式返回 包信息 getSuperClass 以 class 形式返回 父类信息 getInterfaces 以 class[] 形式返回 接口信息 getAnnotations 以 Annotation[] 形式返回注释信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public class ReflectionDemo1 { @Test public void api_01 () throws Exception { Class<?> cls = Class.forName("com.reflection.Person" ); System.out.println(cls.getName()); System.out.println(cls.getSimpleName()); Field[] fields = cls.getFields(); for (Field field : fields) { System.out.println("本类以及父类的public属性" +field.getName()); } Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("获取本类中所有属性" + declaredField.getName()); } Method[] methods = cls.getMethods(); for (Method method : methods) { System.out.println("本类以及父类的public方法" +method.getName()); } Method[] declaredMethods = cls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("获取本类中的所有方法" + declaredMethod.getName()); } Constructor<?>[] constructors = cls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("本类以及父类的public构造器" + constructor.getName()); } Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("获取本类中所有的构造器" +declaredConstructor); } Package aPackage = cls.getPackage(); System.out.println(aPackage); Class<?> superclass = cls.getSuperclass(); System.out.println(superclass); Class<?>[] interfaces = cls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface.getName()); } Annotation[] annotations = cls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } } } class A { public String hobby; } class Person extends A { public String name; protected int age; String job; private double sal; public void m1 () {} protected void m2 () {} void m3 () {} private void m4 () {} }
2. 属性对象 命令 说明 getModifiers 以 int 形式返回修饰符, 默认修饰符 为 0 [ public 为 1, private 为 2, protected 为 4, static 为 8, final 为 16] 如: protected static int age ; 返回 int 值为 12 getType 以 Class 形式返回类型 getName 返回属性名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class ReflectionDemo1 { @Test public void api_01 () throws Exception { Class<?> cls = Class.forName("com.reflection.Person" ); Field[] fields1 = cls.getDeclaredFields(); for (Field field : fields1) { System.out.println("int 形式返回修饰符: " + field.getName() +"=" +field.getModifiers() + " " + field.getType()); } } } class A { public String hobby; } class Person extends A { public String name; protected static int age; String job; private double sal; public void m1 () {} protected void m2 () {} void m3 () {} private void m4 () {} } int 形式返回修饰符: name=1 class java .lang.String nameint 形式返回修饰符: age=12 int ageint 形式返回修饰符: job=0 class java .lang.String jobint 形式返回修饰符: sal=2 double sal
3. 方法对象 命令 说明 getModifiers 以 int 形式返回修饰符, 默认修饰符 为 0[ public 为 1, private 为 2, protected 为 4, static 为 8, final 为 16] 如: protected static int age ; 返回 int 值为 12 getReturnType 以 Class 形式获取, 返回类型 getName 返回方法名 getParameterTypes 以 Class[] 返回参数类型数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class ReflectionDemo1 { @Test public void api_01 () throws Exception { Class<?> cls = Class.forName("com.reflection.Person" ); Method[] methods = cls.getDeclaredMethods(); for (Method method : methods) { System.out.println("本类中所有的方法: " + method.getName() + " 修饰符值=" + method.getModifiers() +" " + method.getReturnType()); Class<?>[] types = method.getParameterTypes(); for (Class<?> type : types) { System.out.println("形参数据类型" +type.getName()); } } } } class A { public String hobby; } class Person extends A { public String name; protected static int age; String job; private double sal; public String m1 (String name, int age, String job) { return "" ; } protected void m2 () {} void m3 () {} private void m4 () {} } 本类中所有的方法: m3 修饰符值=0 void 本类中所有的方法: m2 修饰符值=4 void 本类中所有的方法: m1 修饰符值=1 class java .lang.String 形参数据类型java.lang.String 形参数据类型int 形参数据类型java.lang.String 本类中所有的方法: m4 修饰符值=2 void
4. 构造器对象 命令 说明 getModifiers 以 int 形式返回修饰符 getName 返回构造器(全类名) getParameterTypes 以 Class[] 返回参数类型数组
4. 通过反射创建对象 Class 类 相关方法:
命令 说明 newInstance 调用类中的无参构造器, 获取对应类的对象 getConstructor(Class…) 根据参数列表, 获取对应的 public 构造器对象 getDecalaredConstructor(Class…) 根据参数列表, 获取对应的所有构造器对象
Constructor 类相关方法:
命令 说明 setAccessible 爆破, 使反射可以访问 private 构造器 newInstance(object…) 调用构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class Demo2 { public static void main (String[] args) throws Exception { Class<?> userClass = Class.forName("com.reflection.User" ); User user = (User) userClass.getConstructor().newInstance(); System.out.println(user); Constructor<?> constructor = userClass.getConstructor(String.class); Object o = constructor.newInstance("宝塔镇河妖" ); System.out.println(o); Constructor<?> dc = userClass.getDeclaredConstructor(int .class, String.class); dc.setAccessible(true ); Object o1 = dc.newInstance(200 , "小妖怪" ); System.out.println(o1); } } class User { private int age = 10 ; private String name = "天王盖地虎" ; public User () {} public User (String name) { this .name = name; } private User (int age, String name) { this .age = age; this .name = name; } @Override public String toString () { return "User{" + "age=" + age + ", name='" + name + '\'' + '}' ; } }
5. 通过反射访问属性 根据属性名获取 Field 对象Field f = class 对象.getDeclaredField(属性名); 爆破 : f.setAccessible(true); 访问:f.set(o, 值); // o 表示对象 syso(f.get(o)); 如果是静态属性, 则 set 和 get 中的参数 o, 可以写成 null 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Demo3 { public static void main (String[] args) throws Exception { Class<?> cla = Class.forName("com.reflection.Student" ); Object o = cla.getConstructor().newInstance(); Field age = cla.getField("age" ); age.set(o, 88 ); System.out.println(o); System.out.println(age.get(o)); Field name = cla.getDeclaredField("name" ); name.setAccessible(true ); name.set(null , "李四" ); System.out.println(o); System.out.println(name.get(null )); } } class Student { public int age; private static String name; public Student () {} @Override public String toString () { return "Student{" + "age=" + age + ",name=" + name +'}' ; } }
6. 通过反射访问方法 根据方法名 和 参数列表获取 Method 方法对象;Method m = 类对象.getDeclared Method(方法名, XX.class); 获取对象: Object o = 类对象.newInstance(); 爆破 : m.setAccessible(true); 访问: Object returnValue = m.invoke(o.实参列表); 在反射中, 如果有返回值, 统一返回 Object 类型, 但是运行类型和定义类型一致 如果是静态方法, 则 invoke 的参数 o, 可以写成 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Demo4 { public static void main (String[] args) throws Exception{ Class<?> cls = Class.forName("com.reflection.Boss" ); Object o = cls.getConstructor().newInstance(); Method hi = cls.getMethod("hi" , String.class); hi.invoke(o, "王五" ); Method say = cls.getDeclaredMethod("say" , int .class, String.class, char .class); say.setAccessible(true ); Object o1 = say.invoke(null , 30 , "赵六" , '叼' ); System.out.println(o1); } } class Boss { public int age; public static String name; public Boss () { } private static String say (int n, String s, char c) { return n+" " +s+" " +c; } public void hi (String s) { System.out.println("hi " + s); } }