第十五章 反射
15.1 反射机制
-
反射机制允许程序在执行期借助于Reflection API 取得任何类的内部信息(比如成员变量、构造器、成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
-
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
15.1.1 Java 反射机制可以完成
-
在运行时判断任意一个对象所属的类
-
在运行时构造任意一个类得对象
-
在运行时得到任意一个类所具有的成员变量和方法
-
在运行时调用任意一个对象的成员变量和方法
-
生成动态代理
public class Reflection01 {
public static void main(String[] args) throws Exception {
//1. 使用 Properties 类, 读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src/re.properties"));
String classFullPath = properties.get("classFullPath") + "";
String method = properties.get("method").toString();
//2. 创建对象 -> 反射机制
//new classFullPath();
//3. 使用反射机制解决
//(1) 加载类,返回 Class 类型的对象 cls
Class cls = Class.forName(classFullPath);//Class 指的是 一个类,类名为 Class
//(2) 通过 cls 得到加载的类 com.ljn.Cat 的对象实例
Object o = cls.newInstance();
//(3) 通过 cls 得到加载的类 com.ljn.Cat 的 method "hi" 的方法对象
// 即 在反射中,可以把方法视为 对象(万物皆对象)
Method method1 = cls.getMethod(method);
//(4) 通过 method1 来调用方法
// 即 通过方法对象来实现调用方法
method1.invoke(o);//传统方法 对象.方法(), 反射机制 方法.invoke(对象)
//java.lang.reflect.Field:代表类的成员变量,Field 对象表示某个类的成员变量
//得到 name 字段
//getField 不能得到私有属性
Field ageField = cls.getField("age");
System.out.println(ageField.get(o));//传统方法 对象.成员变量 反射机制: 成员变量对象.get(对象)
//java.lang.reflect.Constructor:代表类的构造方法,Constructor 对象表示 构造器
Constructor constructor = cls.getConstructor();//()中 可以指定构造器参数类型,返回无参构造器
System.out.println(constructor);
Constructor constructor1 = cls.getConstructor(String.class, int.class);
System.out.println(constructor1);
}
}
15.1.2 反射的优缺点
-
优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
-
缺点:使用反射基本是解释执行,对执行速度有影响
15.2 Class 类
15.2.1 基本介绍
-
Class 也是类,因此也继承Object
-
Class 类对象不是 new 出来的,而是系统创建的
-
对于某个类的Class 类对象,在内存中只有一份,因为类只加载一次
-
每个类的实例都会记得自己是由哪个 Class 实例所生成
-
通过 Class 对象可以完整地得到一个类的完整结构,通过一系列 API
-
Class 对象是存放在 堆的
-
类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)
15.2.2 Class 类常用方法
public class Class {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classAllPath = "com.sty.Car";
//1. 获取到 Car类 对应的 类对象
//<?> 表示不确定的 java 类型
Class<?> cls = Class.forName(classAllPath);
//2. 输出 cls
System.out.println(cls);//显示 cls 对象, 是哪个类的 Class 对象
System.out.println(cls.getClass());//运行类型 java.lang.Class
//3. 得到包名
System.out.println(cls.getPackage().getName());//包名
//4. 得到全类名
System.out.println(cls.getName());
//5. 通过 cls 创建对象实例
Car car = (Car) cls.newInstance();
System.out.println(car);//toString
//6. 通过反射 获取属性
Field brand = cls.getField("brand");
System.out.println(brand.get(car));
//7. 通过反射给属性赋值
brand.set(car, "奔驰");
System.out.println(brand.get(car));
//8. 希望可以得到所有的属性/字段
Field[] fields = cls.getFields();
for (Field f : fields) {
System.out.println(f.getName());//属性/字段的 各个名称
}
}
}
15.2.3 获取Class 类对象 6 种方式
public class GetClass_ {
public static void main(String[] args) throws ClassNotFoundException {
//1. Class.forName()
//应用场景
//多用于配置文件,读取类的全路径,加载类
String classAllPath = "com.sty.Car"; //通过读取配置文件,获取得到
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);
//2. 类名.class
//应用场景
//多用于参数传递
Class<Car> cls2 = Car.class;
System.out.println(cls2);
//3. 对象.getClass()
//有对象实例
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
//4. 通过类加载器【4种】来获取到类的Class 对象
//(1) 先得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//(2) 通过类加载器得到 Class 对象
Class<?> cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
//cls1, cls2, cls3, cls4 是同一个 Class 对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
//5. 基本数据(int,char,boolean,short,float,double,byte,long) 按如下方式得到 Class 对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
System.out.println(integerClass);
System.out.println(characterClass);
//6. 基本数据类型对应的包装类,可以通过 .TYPE 得到 Class 对象
Class<Integer> type1 = Integer.TYPE;
System.out.println(type1);
Class<Double> type2 = Double.TYPE;
System.out.println(type2);
System.out.println(integerClass.hashCode());
System.out.println(type1.hashCode());
}
}
15.3 类加载
15.3.1 基本说明
-
反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载
-
静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
-
动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不报错,降低了依赖性
-
15.3.2 类加载时机
-
当创建对象时(new)
-
当子类被加载时,父类也加载
-
调用类中的静态成员时
-
通过反射
15.4 反射获取类的结构信息
-
第一组:java.lang.Class 类
-
//第一组方法 API @Test public void api_01() throws ClassNotFoundException { //得到Class对象 Class<?> personCls = Class.forName("com.sty.reflection.Person"); //获取全类名 System.out.println(personCls.getName()); //获取简单类名 System.out.println(personCls.getSimpleName()); //获取所有 public 修饰的属性,包含本类以及父类的 Field[] fields = personCls.getFields(); for (Field field : fields) {//快捷键 fields.for System.out.println(field.getName()); } //获取本类中所有属性 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField.getName()); } //获取所有 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()); } //获取所有 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()); } //以 Package 形式,返回包信息 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); } //以 Annotation [] 形式,返回注解信息 Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } }
-
第二组:java.lang.reflect.Field 类
-
//第二组 API @Test public void api_02() throws ClassNotFoundException { //得到Class对象 Class<?> personCls = Class.forName("com.sty.reflection.Person"); //获取本类中所有属性 //规定: /* 默认修饰符 :0 public :1 private :2 protected :4 static :8 final : 16 */ Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField.getName() + " 该属性的修饰符值=" + declaredField.getModifiers() + " 该属性的类型=" + declaredField.getType()); } }
-
第三组:java.lang.reflect.Method 类
-
//获取本类中所有方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod.getName() + " 该方法的访问修饰符值=" + declaredMethod.getModifiers() + " 该方法返回类型=" + declaredMethod.getReturnType()); //输出当前这个方法的形参数组情况 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("该方法的形参类型=" + parameterType); } }
-
第四组:java.lang.reflect.Constructor 类
-
//获取本类中所有构造器 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); } }
15.5 反射调用性能优化
关闭访问检查
-
Method 和 Field 、Constructor 对象都有 setAccessible() 方法
-
setAccessible 作用是启动和禁用访问安全检查的开关
-
参数值为 true 表示 反射的对象在使用时取消访问检查,提高反射的效率。参数值为false 则表示反射的对象执行访问检查