通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(不修改源码,且能扩容功能),这样的需求在框架里特别多。
常用内容:反射的定义,使用场景,实现反射用到的类,优缺点,实现方式。
反射机制
反射机制可以完成的功能
反射相关类
程序加载的三个阶段
反射的优点和缺点
反射的优点
- 可扩展性:可以动态的创建和使用对象(Spring框架的底层原理)。
透明性:对于任意一个类,都能够知道这个类的所有字段和方法,并且使用他们。
反射的缺点
性能开销:反射涉及了动态类型的解析,操作的效率要比 非反射操作低得多。
- 安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。
内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有属性和方法),使用反射可能导致代码功能失调,破坏抽象性,可移植性等。
优化方法

xxx.setAccessible(true); //在反射调用方法时,取消访问检查
Class类
基本介绍

public class Class01 {public static void main(String[] args) throws ClassNotFoundException {Class a1 = Class.forName("reflection.Car");Class a2 = Class.forName("reflection.Car");System.out.println(a1.hashCode()); //1134712904System.out.println(a2.hashCode()); //1134712904//hashCode相同,说明Class只被系统创建一次}}
常用方法

public class Class01 {public static void main(String[] args) throws ... {//1. 类的完整路径String classAllPath = "reflection.Car";//2. 获取到Car类 对应的Class对象,<?>表示不确定的Java类型Class<?> cls = Class.forName(classAllPath);System.out.println(cls); //显示哪个类的Class对象 class reflection.CarSystem.out.println(cls.getClass());//输出cls的运行类型 class java.lang.Class//3. 得到包名System.out.println(cls.getPackage().getName()); //reflection//4. 得到全类名System.out.println(cls.getName()); //reflection.Car//5. 通过cls创建对象实例Car car = (Car)cls.newInstance();//6. 通过反射获取属性 brandField 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 field : fields) {System.out.println(field.getName());}}}
获取Class对象的方法

//1. Class.forNameString classAllPath = "reflection.Car";Class<?> cls1 = Class.forName(classAllPath);System.out.println(cls1); //class reflection.Car//2. 类名.classClass cls2 = Car.class;System.out.println(cls2); //class reflection.Car
```java
//3. 对象.getClass()
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3); //class reflection.Car
//4. 通过类加载器来获取类的Class对象 //(1) 先得到类加载器 ClassLoader classLoader = car.getClass().getClassLoader(); //(2) 通过类加载器得到Class对象 Class cls4 = classLoader.loadClass(“reflection.Car”); System.out.println(cls4); //class reflection.Car
```java//5. 基本数据类型Class<Integer> integerClass = int.class;Class<Boolean> booleanClass = boolean.class;System.out.println(integerClass); //int//6. 基本数据类型对应的包装类Class<Integer> type = Integer.TYPE;Class<Boolean> type1 = Boolean.TYPE;System.out.println(type); //intSystem.out.println(integerClass.hashCode()); //1766822961System.out.println(type.hashCode()); //1766822961
哪些类型有Class对象
类加载
静态加载与动态加载
静态加载:编译时加载需要的类,如果没有定义该类则报错,依赖性太强。
动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类也不会报错,降低了依赖性。
public static void main(String[] args) throws ... {//new Dog() 是静态加载,因此必须编写Dog类Dog dog = new Dog();//Person类是动态加载,因此没有编写Person类编译器也不会报错,只有当动态加载该类时才会报错 Class cls = Class.forName("reflection.Person");}
类加载时机
类加载过程图
类加载的各个阶段
加载阶段
连接阶段——验证
连接阶段——准备

//n1 是实例属性,不是静态变量,因此在准备阶段不会分配内存public int n1 = 10;//n2 是静态变量,分配内存。默认初始化为0,而不是20public static int n2 = 20;//n3 是常量,分配内存。它一旦被赋值就不变,因此直接初始化为30public static final int n3 = 30;
连接阶段——解析

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。而直接引用就是指向目标的指针等。
初始化阶段

对出现的顺序的理解:对下面这段代码: static int num = 100; 在静态代码块上方,因此会被 num = 300覆盖。
class B{public static void main(String[] args) {System.out.println(B.num); // 300}static int num = 100; //在静态代码块前出现static{System.out.println("B 静态代码块被执行");num = 300;}}
如果按照下面这样写,就不会被 num = 300覆盖,最终num = 100。
static{System.out.println("B 静态代码块被执行");num = 300;}static int num = 100; //在静态代码块后出现
通过反射获取类的结构信息
java.lang.Class类
java.lang.reflect.Field类
java.lang.reflect.Method类

java.lang.reflect.Constructor类
通过反射创建对象
方式一:调用类中的public修饰的无参构造器(Java11中已弃用)。
方式二:调用类中的指定构造器(如果该构造器是private,要用到暴力破解)。
Class类相关方法
Constructor类相关方法 
public static void main(String[] args) throws ...{//1. 先获取到User类的Class对象Class<?> userClass = Class.forName("reflection.User");//2. 通过public无参构造器创建实例User user1= (User) userClass.newInstance(); //该方法已弃用System.out.println(user1);//3. 用public有参构造器创建实例Constructor<?> constructor = userClass.getConstructor(String.class);//()里是构造器对应的参数User user = (User)constructor.newInstance("ss");System.out.println(user); //不是同一个对象//4. 通过非public的有参构造器创造实例//得到private的构造器方法,要用 getDeclaredConstructorConstructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);//创建实例,要用到 暴力破解declaredConstructor.setAccessible(true);//关闭检查User user2 = (User)declaredConstructor.newInstance(100,"sssss");System.out.println(user2);}
通过反射访问类中的成员

class User{private int age;private String name;public void say(){System.out.println("say方法被调用");}public void say(String name){System.out.println("带有参数的say方法被调用");}public static void say(String name,int age){System.out.println("静态say方法被调用");}private void hi(){System.out.println("private方法被调用");}}
public static void main(String[] args) throws ... {//1. 先获取到User类的Class对象Class<?> userClass = Class.forName("reflection.User");//2. 得到一个对象Object o = userClass.newInstance();//3. 获取public方法(不带参数)Method say = userClass.getMethod("say");say.invoke(o);//4. 获取public方法(带参数)Method say1 = userClass.getMethod("say", String.class); //要把参数的Class写入say1.invoke(o,"aaa");//5. 获取static方法Method say2 = userClass.getMethod("say", String.class, int.class);//因为 hi2对应的是static方法,因此不用写对象(写也可以)say2.invoke(null,"aaa",12);//6. 获取private方法Method hi = userClass.getDeclaredMethod("hi");hi.setAccessible(true); //爆破hi.invoke(o);}
如果方法有返回值,统一返回Object,但是它的运行类型(getClass)和方法定义的返回类型一致。




