1.什么是反射
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
加载类后,在堆中就产生了一个class类型的对象,这个对象包含了类的完整结构的信息,通过这个对象得到类的结构。这个class对象就像一面镜子,透过这个镜子可以看到类的结构,所以形象的称之为“反射”
反射机制是框架的灵魂,一个java程序员不能不会使用反射😶🌫️没有反射机制,java将一无是处
2.Hello,java反射
使用实例:
Cat类: public class Cat { private String name; private String age; public void hi() { System.out.println("喵喵喵~"); } }
测试类:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 反射入门 * 通过字符串形式的类的路径和方法信息调用类的方法 */ public class reflectionTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { // 类的路径 String classPath = "reflection.Cat"; // 要执行类的方法名称 String classMethod = "hi"; // 加载类 Class cls = Class.forName(classPath); // 通过cls得到你加载的类Cat的对象实例 Object o = cls.newInstance(); // 查看o的运行类型,为Cat类型 System.out.println(o.getClass()); // class reflection.Cat // 得到加载的类的方法对象 // 在反射中,可以把方法视为对象 Method method = cls.getMethod(classMethod); // 通过method实例调用方法:即通过方法对象来实现调用方法 method.invoke(o); // 喵喵喵~ } }
3.java程序运行的三个阶段
反射的基本原理:🙌
4.反射相关类
现在我们来完善一下上面的测试类:😊
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 反射入门 * 通过字符串形式的类的路径和方法信息调用类的方法 */ public class reflectionTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { // 类的路径 String classPath = "reflection.Cat"; // 要执行类的方法名称 String classMethod = "hi"; // 加载类 Class cls = Class.forName(classPath); // 通过cls得到你加载的类Cat的对象实例 Object o = cls.newInstance(); // 查看o的运行类型,为Cat类型 System.out.println(o.getClass()); // class reflection.Cat // 得到加载的类的方法对象 // 在反射中,可以把方法视为对象 Method method = cls.getMethod(classMethod); // 通过method实例调用方法:即通过方法对象来实现调用方法 method.invoke(o); // 喵喵喵~ // 得到name字段的信息 // 注意:getField不能得到私有的属性信息 Field nameField = cls.getField("name"); System.out.println(nameField.get(o)); // 豹猫 // 得到构造器 Constructor constructor = cls.getConstructor(); System.out.println(constructor); // public reflection.Cat() } }
5.反射的优化
反射调用方法的效率比普通方法调用的效率低很多
在使用反射调用方法时,可以关闭安全访问检测,这样会提高反射调用的效率
例如:下面是一个实例:
// 在反射中,可以把方法视为对象 Method method = cls.getMethod(classMethod); // 在反射调用方法时,取消访问检测 method.setAccessible(true); // 通过method实例调用方法:即通过方法对象来实现调用方法 method.invoke(o);
6.Class类分析
Class也是类,因此也继承Object类
Class对象不是new出来的,而是系统创建的(通过loadClass方法)
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }
对于某个类的Class对象,在内存中只有一份,因为类只加载一次
每个对象实例都会记录自己是由哪一个Class实例生成的
通过Class对象可以完整的得到一个类的结构
Class对象存在于堆中
类的字节码二进制数据是放在方法区里面的,有的地方称为类的元数据
Class对象方法演示:
实例:
Cat类:
public class Cat { public String name = "豹猫"; public String age; public void hi() { System.out.println("喵喵喵~"); } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } }
ClassMethod类:
import java.lang.reflect.Field; /** * 演示Class类的常用方法 */ public class ClassMethod { public static void main(String[] args) throws Exception { String classAllPath = "reflection.Cat"; // 获取Cat类对应的Class对象 Class<?> aClass = Class.forName(classAllPath); // 显示aClass对象是哪一个类的Class对象 System.out.println(aClass); // class reflection.Cat // 输出aClass对象的运行类型 System.out.println(aClass.getClass()); // class java.lang.Class // 得到包名 System.out.println(aClass.getPackage().getName()); // reflection // 得到全类名 System.out.println(aClass.getName()); // reflection.Cat // 创建对象实例 Cat cat = (Cat) aClass.newInstance(); System.out.println(cat); // Cat{name='豹猫', age='null'} // 通过反射获取属性 Field name = aClass.getField("name"); System.out.println(name.get(cat)); // 豹猫 // 通过反射给属性设置值 name.set(cat,"汤圆"); System.out.println(name.get(cat)); // 汤圆 // 得到所有的属性 Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println(field.getName()); // name // age } } }
7.获取Class对象的六种方式
获取Class对象细分可以分为六种方式:🎶
现在我们使用代码进行演示:
/** * 获取Class对象的六种方式 */ public class GetClass { public static void main(String[] args) throws ClassNotFoundException { String classAllPath = "reflection.Cat"; // 1.forName获取。多用于配置文件读取类的全路径,加载类 // 2.类名.class。多用于参数传递 System.out.println(String.class); // class java.lang.String System.out.println(Cat.class); // class reflection.Cat // 3.对象.getClass。适用于已经存在对象实例的情况 Cat cat = new Cat(); System.out.println(cat.getClass()); // class reflection.Cat // 4.通过类加载器获取Class对象 // (1)得到类加载器 ClassLoader classLoader = cat.getClass().getClassLoader(); // (2)通过类加载器得到Class对象 Class<?> cls = classLoader.loadClass(classAllPath); System.out.println(cls); // class reflection.Cat // 5.基本数据类型通过.class获取Class对象 Class<Integer> integerClass = int.class; Class<Character> characterClass = char.class; Class<Boolean> booleanClass = boolean.class; System.out.println(integerClass); // int // 6.基本数据类型的包装类可以通过.TYPE得到Class对象 Class<Integer> type = Integer.TYPE; System.out.println(type); // int } }
那么,那些类型拥有Class对象呢?😶🌫️
外部类
接口
数组(含二维数组)
注解
枚举
基本数据类型
包装类
成员内部类
8.类加载机制
动态加载和静态加载
静态加载:
在编译时加载的类,如果没有则直接报错,依赖性太强
动态加载:
运行时加载相应的类,如果运行时没有用到相应的类,则不会进行加载,改善了依赖性的问题
通过new创建实例的方式就是典型的静态加载,通过反射创建对象是典型的动态加载🎈
类加载时机:
创建对象时
子类被加载
调用类中的静态成员时
通过反射(动态加载)
类加载流程概述
类加载的三个阶段:😒
类加载后的内存布局:🤷♂️
整体布局:👌
加载阶段
JVM在该阶段的主要目的是将字节码从不同的数据源转化为二进制字节流加载到内存中,并生成一个代表该类的Class对象
连接阶段
验证:
目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不会危害虚拟机自身的安全
包括:文件格式的验证,元数据验证,字节码验证,符号引用验证
准备:
JVM会在该阶段对静态变量分配内存并默认初始化(分配默认值)。这些变量所使用的内存都将在方法区中进行分配
解析:
虚拟机将常量池内的符号引用替换为直接应用的过程
初始化
<clinit>()方法依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法。其他线程都要阻塞等待
9.通过反射获取类的结构信息
我们使用一段程序来演示所有的常用方法:🎍
import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 通过反射获取类的结构信息 */ public class ReflectionUtils { public static void main(String[] args) throws ClassNotFoundException { // 得到Class对象 Class<?> personCls = Class.forName("reflection.Person"); // 得到全类名 System.out.println(personCls.getName()); // 得到简单类名 System.out.println(personCls.getSimpleName()); // 获取所有的public修饰的属性,包含父类的 Field[] fields = personCls.getFields(); for (Field field : fields) { System.out.println(field.getName()); } // 获取本类的所有属性,不包含父类 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { // 打印属性名字和等级和类型 // 默认:0,public:1,private:2,protected:4,static:8,final:16 System.out.println(declaredField.getName() + "\t" + declaredField.getModifiers() + "\t" + declaredField.getType()); } // 获取所有public方法,包含父类的 Method[] methods = personCls.getMethods(); for (Method method : methods) { System.out.println(method.getName()); } // 获取本类的所有方法和等级和返回类型和参数类型数组 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod.getName() + "\t" + declaredMethod.getModifiers() + "\t" + declaredMethod.getReturnType()); // 获取方法的参数类型数组 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(parameterType); } } // 获取所有的public构造器,不包含父类的 Constructor<?>[] constructors = personCls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor.getName()); } // 获取所有的构造器,包含私有的,还有构造器的参数类型 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor.getName()); Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(parameterType); } } // 返回包信息 System.out.println(personCls.getPackage()); // 以Class形式返回父类的信息 Class<?> superclass = personCls.getSuperclass(); System.out.println(superclass); // 以Class形式返回接口信息 Class<?>[] interfaces = personCls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface.getName()); } // 返回注解信息 Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } } } interface IA {} interface IB {} @Deprecated class Person extends Something implements IA, IB { public String name; protected int level; private int age; String job; public Person() { } public Person(String name) { this.name = name; } private Person(String name, int level, int age, String job) { this.name = name; this.level = level; this.age = age; this.job = job; } public void show(String content, int code) {} protected void hi() {} void say() {} private void hei() {} } class Something { public String hobby; }