欢迎使Java 反射(reflect)深度理解

Java 反射(reflect)深度理解

1. 反射是为了解决什么问题?

需求: 根据配置文件指定信息,创建Cat对象调用sayHi方法

classfullpath=com.zhj.reflection.Cat
method=sayHi # 如果需要调用sayByb只需改配置文件

解决方案: 反射

通过外部文件,在不改变源码的情况下(只修改配置文件),控制程序,符合设计模式的OCP原则,在学习框架时这种需求特别多

Properties properties = new Properties();
properties.load(new FileInputStream("src\\main\\resources\\re.properties"));
String classFullPath = properties.get("classFullPath").toString();
String method = properties.get("method").toString();
System.out.println("----------------");
System.out.println("classfullpath = " + classFullPath + "  method = " + method);
// 创建对象  不能使用new 的方式
// new classfullpath()
// 使用反射技术实现
// 1.获得加载类
Class cls = Class.forName(classFullPath);
// 2.通过 class 可以获得一个加载的对象实例 就像镜子反射出的对象几乎一模一样
Object obj = cls.newInstance();
System.out.println(obj.getClass());
// 3.获得对象的方法
Method objMethod = cls.getMethod(method);
// 4.对象执行方法
objMethod.invoke(obj);

2 反射的原理

编码–>Javac编译–>

ClassLoader类加载器加载–>Class类对象/堆(Field[]、Constructor[]、Method[])–>

Runtime运行阶段

反射就是在程序运行阶段可以根据全类名获取得到类加载时加载的类,从而得到反射出来的对象。

Java反射机制可以完成:

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类对象
  3. 在运行时得到任意一个类的所有成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理

3 反射的实现

反射相关的主要类

  1. java.lang.Class 代表一个类, Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method 代表类的方法
  3. java.lang.reflect.Field 代表类的成员变量(属性)
  4. java.lang.reflect.Constructor 代表类的构造方法
// java.lang.reflect.Field 代表成员变量
// 得到name字段
// getField不能获得私有属性
Field nameField = cls.getField("age");
System.out.println(nameField.get(obj));
// 获得构造器
Constructor constructor = cls.getConstructor();
System.out.println(constructor);
// 获得有参构造器
Constructor constructorHasName = cls.getConstructor(String.class);
System.out.println(constructorHasName);

反射获取的类信息

  1. getName 获取全类名
  2. getSimpleName 获取简单类名
  3. getFileds 获取所有public修饰的属性,包含本类及父类
  4. getDeclareFields 获取本类的所有属性
  5. getMethods 获取所有public修饰的方法,包含本类及父类
  6. getDeclareMethods 获取本类的所有方法
  7. getConstuctors 获取所有public修饰的构造器,包含本类,不包含父类
  8. getDeclaretConstuctors 获取本类的所有构造器
  9. getPackage 以Package形式返回包信息
  10. getSuperClass 以Class形式返回父类信息
  11. getInterfaces 以Class[]形式返回接口信息
  12. getAnnotations 以Annotation[]形式返回注解信息
public class ReflectUtils {

    public static void main(String[] args) throws ClassNotFoundException {
        api_g01();
    }

    public static void api_g01() throws ClassNotFoundException {
        Class personCls = Class.forName("com.zhj.reflection.Person");
        System.out.println(personCls.getName());
        System.out.println(personCls.getSimpleName());
        System.out.println(personCls.getPackage());
        System.out.println("所有接口信息:");
        for (Class anInterface : personCls.getInterfaces()) {
            System.out.println(anInterface);
        }
        System.out.println("本类与父类的public属性:");
        for (Field field : personCls.getFields()) {
            System.out.println(field);
        }
        System.out.println("本类所有的属性:");
        for (Field declaredField : personCls.getDeclaredFields()) {
            System.out.println(declaredField);
        }
        System.out.println("本类与父类的public方法:");
        for (Method method : personCls.getMethods()) {
            System.out.println(method);
        }
        System.out.println("本类的所有方法:");
        for (Method declaredMethod : personCls.getDeclaredMethods()) {
            System.out.println(declaredMethod);
        }
        System.out.println("本类的public构造器:");
        for (Constructor constructor : personCls.getConstructors()) {
            System.out.println(constructor);
        }
        System.out.println("本类所有的构造器:");
        for (Constructor declaredConstructor : personCls.getDeclaredConstructors()) {
            System.out.println(declaredConstructor);
        }
        System.out.println("本类与父类的public注解:");
        for (Annotation annotation : personCls.getAnnotations()) {
            System.out.println(annotation);
        }
        System.out.println("本类所有的注解:");
        for (Annotation declaredAnnotation : personCls.getDeclaredAnnotations()) {
            System.out.println(declaredAnnotation);
        }
    }

}

interface IA {

}
interface IB {

}

class Biology {
    public String height;

    public void m() {

    }

    Biology() {

    }

    public Biology(String height) {

    }
}

@Deprecated
class Person extends Biology implements IA,IB {

    public String name = "大强";
    protected int age = 11;
    String job = "程序员";
    private double sal;

    // 方法m1
    public void m1() {

    }
    // 方法m2
    protected void m2() {

    }
    // 方法m3
    void m3() {

    }
    // 方法m4
    private void m4() {

    }

    Person() {

    }
}

java.lang.Field 类

  1. getModifiers 以int类型返回修饰符

    默认是0 public是1 private是2 protected是4 static是8 final是16

  2. getType 以Class返回类型

  3. getName 返回属性名

java.lang.Method 类

  1. getModifiers 以int类型返回修饰符

    默认是0 public是1 private是2 protected是4 static是8 final是16

  2. getReturnType 以Class返回类型

  3. getName 返回属性名

  4. getParameterTypes 以Class[]返回类型

java.lang.Constructor 类

  1. getModifiers 以int类型返回修饰符

    默认是0 public是1 private是2 protected是4 static是8 final是16

  2. getName 返回属性名

  3. getParameterTypes 以Class[]返回类型

通过反射创建对象

  1. 方式一:调用类中的public修饰的无参构造器

  2. 方式二:调用类中的指定构造器

  3. Class 类相关方法

    new Instance:调用类中的无参构造器,获取对应类的对象

    getConstructor(Class…clazz):根据参数列表,获取对应的构造器对象

    getDecalaredConstructor(Class…clazz):更具参数列表,获取对应的构造器对象

  4. Constructor 类相关方法

    setAccessible:爆破

    newInstance(Object…obj):调用构造器

public class ReflectCreateObj {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 1.获取到user类的Class对象
        Class<?> cls = Class.forName("com.zhj.reflection.User");
        // 2.通过public的无参构造器创建实例
        Object obj1 = cls.newInstance();
        System.out.println(obj1);
        // 3.通过public的有参构造器创建实例
        Constructor<?> constructor = cls.getConstructor(String.class);
        Object zhj = constructor.newInstance("zhj");
        System.out.println(zhj);
        // 4.通过非public的有参构造器创建实例
        Constructor<?> declaredConstructor = cls.getDeclaredConstructor(int.class, String.class);
        // Object xm = declaredConstructor.newInstance(21, "xm"); 非法的访问异常
        // 使用反射可以访问私有的,暴力破解
        declaredConstructor.setAccessible(true);
        Object xm = declaredConstructor.newInstance(21, "xm");
        System.out.println(xm);
    }

}

class User {
    private int age;
    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 + '\'' +
                '}';
    }
}

通过反射访问属性

Class personCls = Class.forName("com.zhj.reflection.Person");
Object o = personCls.newInstance();
System.out.println(o);
System.out.println("本类所有的属性:");
for (Field declaredField : personCls.getDeclaredFields()) {
     System.out.println(declaredField);
     System.out.println("属性修饰符:" + declaredField.getModifiers());
     System.out.println("属性类型:" + declaredField.getType());
     System.out.println("属性注解:");
	 for (Annotation annotation : declaredField.getAnnotations()) {
           System.out.println(annotation);
      }
      System.out.println("----------------------------------------");
        }
Field name = personCls.getField("name");
name.set(o,"zhj");
Field sal = personCls.getDeclaredField("sal");
// sal.set(o,3000); 非法
sal.setAccessible(true);
sal.set(o,3000);
// 静态属性第一个参数可以为null
System.out.println("update obj:" + o);

通过反射访问方法

  1. 根据方法名和参数列表获取方法

    Method m = clazz.getDeclaredMetod(方法名,xx.class)

  2. 获取对象 Object o = clazz.newInstance()

  3. 爆破 m.setAccessible(true)

  4. 访问 Object returnvalue = m.invoke(o,实例列表)

  5. 注意:如果静态方法invoke的参数o,可以为null

4 反射的特点

4.1 优点

  • 可以动态的创建和使用对象(框架底层核心)
  • 使用灵活
  • 没有反射机制框架技术就失去了底层支撑

4.2 缺点

  • 解释执行,影响执行速度

4.3 优化

// 关闭检查 提高效率
// 反射取消方法访问检查
objMethod.setAccessible(true);

5 Class 类

  1. Class 也是类,因此也继承Object类
  2. Class类对象不是new 出来的,而是系统创建的
  3. 对于某个类的Class类对象,在内容中只有一份,因为类只加载一次
  4. 每个类的实列都会记得自己是由哪个Class实例所生成
  5. 通过Class可以完整的得到一个类的完整结构,通过一系列API
  6. Class对象是存放在堆的
  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码

变量名,方法名

Cat cat = new Cat();

// 反射方式 也会进入loadClass 类的加载只能有一次  先获取classLoader加载器
Class cls = Class.forName("com.zhj.reflection.Cat");
System.out.println(cls);
System.out.println(cls.getClass());
System.out.println(cat.getClass());
System.out.println(cls.getPackage().getName());
System.out.println(cls.getName());
Object o = cls.newInstance();

获取Class对象的六种方式

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        // 1.编译阶段  配置文件
        Class<?> class1 = Class.forName("com.zhj.reflection.Cat");
        System.out.println(class1);
        // 2.类加载阶段  参数传递
        Class class2 = Cat.class;
        Constructor constructorHasName = class1.getConstructor(String.class);
        System.out.println(class2);
        // 3.已经知道对象了
        Cat cat = new Cat();
        Class class3 = cat.getClass();
        System.out.println(class3);
        // 4.通过类加载器来获取
        ClassLoader classLoader = cat.getClass().getClassLoader();
        Class<?> class4 = classLoader.loadClass(class1.getName());
        System.out.println(class4);
        // 5.基本数据类型
        Class intCls1 = int.class;
        System.out.println(intCls1);
        // 6. 基本数据类型对应包装类
        Class intCls2 = Integer.TYPE;
        System.out.println(intCls2);
        Class integerCls = Integer.class;
        System.out.println(integerCls);
    }

如下类型都有Class对象

  • 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  • interface 接口
  • 数组
  • enum 枚举
  • annotion 注解
  • 基本数据类型
  • void
Class cls1 = String.class;
Class cls2 = Serializable.class;
Class cls3 = Integer[].class;
Class cls4 = float[][].class;
Class cls5 = Deprecated.class;
Class cls6 = Thread.State.class;
Class cls7 = long.class;
Class cls8 = void.class;
Class cls9 = Class.class;

6 类加载

静态加载:编译时加载相关的类,如果没有则报错,依赖性太强

动态加载:运行时才加载该类,编译时不会检查,即使不存在也不会报错,降低了依赖性

通过反射可以实现动态加载

类的加载时机

  • 创建时(new) 静态
  • 子类加载,父类也加载 静态
  • 调用类中的成员 静态
  • 反射 动态

源码 – javac --> 字节码文件 --java–> ( 类加载Loading --> 三个阶段 验证,准备,解析 初始化 --> 初始化)

连接阶段

  • 验证

    1. 目的为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
    2. 包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
    3. 可以考虑使用 - Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载时间
  • 准备

    1. JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0,0L,null,false等

    2. // 属性-成员变量-字段
      // 1. n1 是实例属性,不是静态变量,因此在准备阶段,是不会分配内存
      // 2. n2 是静态变量,因此在准备阶段,会分配内存 n2 默认初始化0 ,而不是20
      // 3. n3 是常量,它和静态变量不一样,因为一旦赋值就不变 n3=30
      public int n1 = 10;
      public static int n2 = 20;
      public static final int n3 = 30;
      
  • 解析

    1. 虚拟机将常量池内的符号引用替换为直接引用的过程

初始化

  • 初始化才开始执行类中定义的Java程序代码,此阶段师执行()方法的过程
  • ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有的静态变量的赋值动作和静态代码块中的语句,并进行合并
  • 虚拟机会保证一个类的()方法在多线程环境中被正确的加锁,同步,如果多个线程同时初始化一个类,那么只有一个现场去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。
/**
 * 1.加载B类,并生成B的class
 * 2.连接 num = 0
 * 3.初始化
 * 自动收集所有静态变量的赋值动作和静态代码块的语句
 * clinit() {
 *   System.out.println("static code");
 *   // num = 300; 合并num = 300无效
 *   num = 100;
 * }
 
 * 保证内存只有一个类对象
 * synchronized (getClassLoadingLock(name)) {
 *          // First, check if the class has already been loaded
 *  }
 */
 // new B();
 // B.class.newInstance();
 // 使用B的属性也会加载B.class
 System.out.println(B.num);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值