反射-Java生态的根基

反射

1.机制

1.1 快速入门

package com.huier.po;

public class Person {

    public Person(){
        System.out.println("Person类的无参构造器被调用!");
    }

    public void say(){
        System.out.println("Hello reflection!");
    }
}

package com.huier.reflection;

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class QuickStart01 {
    public static void main(String[] args) throws Exception {
        //1.读取.properties配置信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\reflection.properties"));
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //2.打印配置信息
        System.out.println("全类名:"+className);
        System.out.println("方法名:"+methodName);

        //3.获取Person的class文件并创建成为一个对象
        Class cla = Class.forName(className);

        //4.使用Person的Class对象逆向创建Person对象
        Object person = cla.newInstance();//调用无参构造器

        //5.使用Person的Class对象获取所有的方法对象
        Method method = cla.getMethod(methodName);

        //6.Method对象调用Person的方法
        method.invoke(person);//需要传入执行方法的具体对象
    }
}
#properties文件默认整个都是字符串类型,不需要再额外的添加双引号,否则会将双引号作为内容的一部分
className=com.huier.po.Person
methodName=say

总结:和传统的创建对象的方式相比可能会显得比较繁琐,但是在对代码的修改层面会有很大的优点。比起传统的new关键字创建对象,反射可以通过全类名来创建对象,通过方法名来调用方法,所以可以通过读取外部文件创建对象。

假设一个创建了许多对象的代码,如果让你修改这些对象的类型,传统方式下只能一个一个手动修改,而在反射的机制下只需要修改外部资源即可。

1.2 机制

在开始学习java时,我们需要先编写.java文件,然后通过javac.exe进行编译成.class文件,再通过java.exe进行运行。在执行了java.exe后不是直接运行在jvm虚拟机中,而是要先加载到内存中,然后再到jvm虚拟机中。

在加载类到内存中之后,java设计者在堆区域会创建一个以这个.class文件为模板的Class对象,这个对象包含了类的所有信息,以这个对象为模板使用传统的new关键字方式创建对象,继而可以对象.getClass()获得Class对象。而我们现在使用的反射技术,是对这个工程的逆向运转,先获取Class对象,然后创建普通对象。

        //传统方式
        //1.new获取普通对象
        Person p = new Person();
        //2.获取Class模板对象
        Class aClass = p.getClass();

Java程序

代码阶段->javac.exe->代码阶段->java.exe->类加载阶段->加载阶段->运行阶段

1.4 反射机制的作用

  1. 运行时判断对象所属的类:每一个类只有一个模板对象[Class对象]
  2. 创建对象实例
  3. 获取、操作类的信息
  4. 动态代理[方法的增强等]

1.5 反射主要的类

  • Class:模板
  • Method:封装方法
  • Field:封装变量
  • Constructor:封装构造方法

1.6 优缺点

优点:解耦合,动态创建对象,Java生态圈良好的基石。

缺点:使用步骤较为复杂,执行速度有所影响。

1.7 关闭访问检查

Method和Field、Constructor对象都有setAccessible()方法,我们可以传参为true,表示关闭访问检查[默认false],可以在一定程度上优化执行速度,但是不建议。

2.Class类

2.1 特点

  • 继承Object类
  • 系统创建,无法new
  • Class对象内存中只有一份[模板一份就够了]
  • 实例知道模板是谁[Class对象]
  • Class对象存放在堆中

2.2 常用方法

  • static forName:获得Class对象
  • newInstance:实例化对象
  • getName:获得全类名
  • getSuperClass:获得父类Class对象
  • getInterfaces:获得实现的接口
  • gettClassLoader:获得类加载器
  • getConstructors:获得public构造器
  • getDeclareFields:获得所有属性
  • getMethods:获得public方法
    public static void main(String[] args) throws Exception {
        String className = "com.huier.po.Person";

        //1.代码阶段获取Class对象
        Class<?> cla = Class.forName(className);

        //2.使用无参构造器创建对象
        Object person = cla.newInstance();//Person类的无参构造器被调用!

        //3.获取全类名
        String name = cla.getName();
        System.out.println("全类名:"+name);//com.huier.po.Person

        //4.获取父类的Class对象
        Class<?> superclass = cla.getSuperclass();
        System.out.println("父的Class的对象:"+superclass);//class java.lang.Object

        //5.获取所有实现的接口
        Class<?>[] interfaces = cla.getInterfaces();
        System.out.println("遍历实现的接口Class对象");
        Arrays.stream(interfaces).forEach(System.out::println);

        //6.获取类加载器
        ClassLoader classLoader = cla.getClassLoader();
        System.out.println("类加载器:"+classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //7.获取所有public修饰的构造器
        Constructor<?>[] constructors = cla.getConstructors();
        System.out.println("遍历所有public修饰的构造器");
        Arrays.stream(constructors).forEach(System.out::println);//public com.huier.po.Person()

        //8.获取所有属性
        Field[] declaredFields = cla.getDeclaredFields();
        System.out.println("遍历所有属性");
        /*
        private java.lang.Integer com.huier.po.Person.age
        public java.lang.String com.huier.po.Person.name
         */
        Arrays.stream(declaredFields).forEach(System.out::println);

        //9。获取所有public修饰的方法
        Method[] methods = cla.getMethods();
        System.out.println("遍历所有public修饰的方法");
        /*
        public void com.huier.po.Person.say()
        public final void java.lang.Object.wait() throws java.lang.InterruptedException
        public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
        public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
        public boolean java.lang.Object.equals(java.lang.Object)
        public java.lang.String java.lang.Object.toString()
        public native int java.lang.Object.hashCode()
        public final native java.lang.Class java.lang.Object.getClass()
        public final native void java.lang.Object.notify()
        public final native void java.lang.Object.notifyAll()
         */
        Arrays.stream(methods).forEach(System.out::println);
    }

2.3 获取Class对象

java程序的三个阶段都可以获取到Class对象

javac.exe->[代码阶段]:Class.forName(),多用来读取配置文件

java.exe->[类加载阶段]:1.获得类加载器;2.load()方法

加载阶段:类.class,多用来参数传递

runtime[运行阶段]:对象.getClass(),多用来获得对象

特殊

基本数据类型.class

包装类型.TYPE

    public static void main(String[] args) throws Exception {
        String className = "com.huier.po.Person";

        //1.代码阶段
        Class<?> cla1 = Class.forName(className);

        //2.类加载阶段
            //a:获得类加载器
        ClassLoader classLoader = GetClassObject.class.getClassLoader();
            //b:调用load方法
        Class<?> cla2 = classLoader.loadClass(className);


        //3.加载阶段
        Class<Person> cla3 = Person.class;

        //4.运行阶段
        Person p = new Person();
        Class cla4 = p.getClass();

        System.out.println("代码阶段:"+cla1.hashCode());
        System.out.println("类加载阶段:"+cla2.hashCode());
        System.out.println("加载阶段:"+cla3.hashCode());
        System.out.println("运行阶段:"+cla4.hashCode());

        //5.特殊
        Class iCla1 = int.class;
        Class iCla2 = Integer.TYPE;

        System.out.println("int:"+iCla1.hashCode());
        System.out.println("Integer:"+iCla2.hashCode());
    }

Class对象在堆内存中只会存在一份,类加载也只有一次。

2.4 Class对象的存在

  • 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • 接口
  • 数组
  • 枚举
  • 注解
  • 基本数据类型
  • void

3.类加载

3.1 加载

静态加载:编译时加载相关的类,new关键字实例化对象。

动态加载:运行时加载所需要的类,反射实例化对象。

3.2 类加载时机

  • 首次创建对象时
  • 子类被加载且父类未被加载时
  • 调用类的静态成员时且类未被加载时
  • 反射且未被加载时

3.3 解析

在这里插入图片描述

类加载阶段

  • loading[加载]:将类的.class文件读入内存,并创建Class对象,由类加载器完成。
  • linking[连接,中间三个]:将类的二进制数据合并到jre中。
    • 验证:文件安全的校验,可以使用-Xverify:none参数关闭大部分的类验证措施,缩短时间。
    • 准备:静态变量默认初始化并分配空间[方法区],如果是final修饰的静态资源不用等到初始化阶段,会直接赋值所给的值而不是初始值。
    • 解析:符号引用转成直接引用,比如:A在美国 B在中国->A的经纬度 B的经纬度。
  • initialization[初始化]:JVM负责对类的静态资源初始化,这里程序员可以控制书写具体的静态资源的值,前两步都有JVM实现且不可干预。Class对象只有一份的关键点:clinit方法,多线程环境下被正确的加锁、同步,如果多个线程同时去初始化一个类,最终只会有一个去初始化该类。

4.反射获取类的结构信息

Class

  • 获取全类名
  • 获取简单类名
  • 获取所有public修饰的属性
  • 获取所有属性包括私有
  • 获取所有public修饰的方法
  • 获取所有方法包括私有
  • 获取所有构造方法
  • 获取类所在的包
  • 以Class形式返回父类信息
  • 以Class[]形式返回接口信息
  • 以Anno[]形式返回注解信息
    public static void main(String[] args) throws Exception {
        String className = "com.huier.po.Person";

        //0.获取Class对象
        Class<?> cla = Class.forName(className);

        //1.获取全类名
        String name = cla.getName();

        //2.获取简单类名
        String simpleName = cla.getSimpleName();

        //3.获取public属性
        Field[] fields = cla.getFields();

        //4.获取所有属性
        Field[] declaredFields = cla.getDeclaredFields();

        //5.获取public方法
        Method[] methods = cla.getMethods();

        //6.获取所有方法
        Method[] declaredMethods = cla.getDeclaredMethods();

        //7.获取public构造器
        Constructor<?>[] constructors = cla.getConstructors();

        //8.获取所有构造器
        Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors();

        //9.获得类所在包
        Package aPackage = cla.getPackage();

        //10.获得父Class对象
        Class<?> superclass = cla.getSuperclass();

        //11.获得实现的接口信息
        Class<?>[] interfaces = cla.getInterfaces();

        //12.获得注解信息
        Annotation[] annotations = cla.getAnnotations();
    }

Field

  • 以int返回修饰符[多个int值相加]
  • 以Class形式返回属性类型
  • 返回属性名字

Method

  • 以int返回修饰符[多个int值相加]
  • 以Class形式获取返回类型
  • 返回方法名
  • 以Class[]形式返回参数列表

5.反射相关类

5.1 反射创建对象

  • 调用类中public修饰的无参构造器
  • 调用类中指定构造器

5.2 Class类相关方法

  • newInstance:调用类中public修饰的无参构造器
  • getConstructor(Class…):根据参数列表,获取对应的public构造器对象
  • getDeclaredConstructor(Class…):根据参数列表,获取所有对应的构造器对象
  • getField(String…):根据属性名,获取对应的public属性
  • getDeclaredField(String…):根据属性名,获取所有对应属性
  • getMethod(String…,Class…):根据方法名,获取对应的public方法
  • getDeclaredMethod(Stirng…,Class…):根据方法名,获取所有对应方法

5.3 Constructor类相关方法

  • setAccessible:爆破,使用反射可以访问私有内容
  • newInstance(Object…):调用构造器

5.4 Field类相关方法

  • setAccessible:爆破,使用反射可以访问私有内容

  • set(Object,value):为属性设置值,如果是静态属性第一个参数可以是null

  • get(Object):获得属性值,如果是静态属性参数可以是null

5.5 Method类相关方法

  • setAccessible:爆破,使用反射可以访问私有内容
  • invoke(Object,args):执行方法,如果是静态属性第一个参数可以是null

注意点:所有的私有属性、构造器、方法在操作前都需要进行爆破操作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值