Java反射

(一)虚拟机类加载机制

 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被java虚拟机直接使用的java类型----Class类型,这就是虚拟机的类加载机制.
>通俗讲:【类加载就是把磁盘的字节码文件数据变为内存的Class对象】


1.2类加载过程

当程序要使用某个类时,如果该类还未被加载到内存中,系统会通过加载,连接,初始化三步来实现对这个类的加载.
1)加载 
         就是指将class文件读入内存,并为之创建一个Class对象.
         任何类被使用时系统都会建立一个Class对象
2)连接
         验证是否有正确的内部结构,并和其他类协调一致
         准备负责为类的静态成员分配内存,并设置默认初始化值
         解析将类的二进制数据中的符号引用替换为直接引用

 3)初始化 

1.3类的加载时机【类被使用的才会被加载
        1.创建类的实例
        2.类的静态成员使用
        3.类的静态方法调用
        4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
        5.初始化某个类的子类
        6.直接使用java.exe命令来运行某个主类

(二)类加载器
类加载器是负责加载Class类【字节码文件数据】的对象,将class文件(硬盘)数据加载到内存中,并为之生成对应的java.lang.Class对象.

2.2类加载器的分类


>1.Bootstrap ClassLoader 引导类加载器
                     也被称为根类加载器,负责Java核心类的加载,比如System,String等.
>2.Extension ClassLoader 扩展类加载器
                     负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录.
>3.Application ClassLoader 系统类加载器
                    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径.
>4.自定义类加载器
       开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求.
 

 2.3双亲委派机制
>        1.双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器.每个类加载器都是如此,只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载.
>        2.双亲委派模型工作过程:
>            1)当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成.    
>            2)当Extension ClassLoader收到一个类加载请求时,他也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成.  
>            3)如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载. 
>            4)如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载. 
>            5)如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载. 
>            6)如果均加载失败,就会抛出ClassNotFoundException异常.

3.比如:
        加载一个自定义的类Person,触发了系统类加载器,不加载委托给父类【扩展类加载器去加载】,父类接收到请求后又委托给父类的的父类【根类加载器】,根加载器尝试加载这个类,由于Person自定义的类不在jdk的核心类库中,所以加载失败,请求重新回到扩展类加载器,尝试加载这个类,由于Person类是自定义的在jre的lib下面找不到对应的类,也加载失败,请求重新回到系统类加载器,尝试加载,在第三方的相关资源处【自己书写代码的包中,导入的jar包中等查找对应的类】找到了加载成功,没有找到报错。
 

#### 2.4ClassLoader类
      1.ClassLoader 叫做类加载器.虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流” 也就是.class字节码文件,这个动作放到java虚拟机外部去实现,以便让应用程序自己决定去如何获取所需要的类,实现这个动作的代模块称之为“类加载器”.

     2.ClassLoader的方法: 
         static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
         ClassLoader getParent():返回父类加载器进行委派
        `ClassLoader.getResourceAsStream():

ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//Application ClassLoader【系统类加载器】
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//Extension ClassLoader【扩展类加载器)
//注意扩展类加载器的父类为引导类加载器【Bootstrap ClassLoader】,由于它是使用C++写的,所以我们java访问不了
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//所以此处为null

 (三)反射应用

 3.1反射机制
> 反射是指在代码运行时去获取一个类对象的变量和方法信息.然后通过获取到的信息来创建对象,调用方法的一种机制.
> 由于这种动态性可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展.

3.2获取Class类的对象
>1.Class类: Class类型的实例表示正在运行的java应用程序的类或者接口.
>2.Class类的对象: 想获取和操作类中的内容,首先要获取类的字节码对象(Class类对象),每一个正在运行的类,都有对应的字节码对象,获取了类的字节码对象,就可以使用这个对象的所有方法,这些方法都定义在Class类型中.
>3.三种获取Class类对象的方式:【使用反射技术操作Person类中的相关资源为例】
   1)类名.class属性

//类名.类名属性
Class<Person> personClass = Person.class;

 2)对象名.getClass()方法

//类对象.getClass
Person person = new Person();
Class<? extends Person> aClass = person.getClass();

3)Class.forName(全类名)方法

//Class.forName(权限定类名)
Class<?> aClass1 = Class.forName("com.tlc.reflect.Person");

3.3反射获取构造方法并使用
  >1.Class类获取构造方法对象:
 Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组

Class<?> aClass = Class.forName("com.tlc.reflect.Person");
Constructor<?>[] con = aClass.getConstructors();

Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组

Constructor<?>[] dec = aClass.getDeclaredConstructors();

Constructor getConstructor(Class<?>... parameterTypes):返回单个公共构造方法对象

Constructor<?> constructor = aClass.getConstructor(String.class, int.class);

Constructor getDeclaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象

Constructor<?> dConstructor = aClass.getDeclaredConstructor(String.class, int.class);

2.Constructor类型:
         1)表示构造方法类型,这个类的每个对象,都是一个确定的,具体的构造方法
        2)构造方法对象应该具有的功能: 获取构造方法各种信息(构造方法修饰符、构造方法名称、构造方法的参数列表、构造方法的注解),最基本的一个功能就是,创建对象.

3.Constructor类用于创建对象的方法:
         T newInstance(Object...initargs) :根据指定的构造方法创建对象,参数为构造方法实际参数

//有参构造创建对象
Object o = dConstructor.newInstance("阿弥陀佛", 998);
//提升作用域
Person person = null;
//向下转型
if(o instanceof Person) {
    person = (Person)o;
}
System.out.println(person);

 3.4反射获取成员变量并使用
        >1.Class类获取成员变量对象:
        Field[] getFields():返回所有公共成员变量对象的数组

 Field[] fields = aClass.getFields();

        Field[] getDeclaredFields():返回所有成员变量对象的数组

Field[] declaredFields = aClass.getDeclaredFields();

        Field getField(String name):返回单个公共成员变量对象

//获取单个public修饰的成员变量
Field age1 = aClass.getField("age");

        Field getDeclaredField(String name):返回单个成员变量对象

//获取单个【任意权限】的成员变量
Field age = aClass.getDeclaredField("name");

2.Field类型: 表示一个成员变量类型,每个对象都是一个具体的成员变量
        作用: 获取成员变量的各种信息(修饰符、注解、名称),做各种数据类型的转换.
3.Field类用于给成员变量赋值的方法:
        set(Object obj, Object value): 用于给obj对象的,该成员变量,赋value值
4.Field类获取成员变量值的方法:
        get(Object obj): 用于获取obj对象的指定成员变量值

        Field age = aClass.getDeclaredField("name");
        //操作属性的值【属性对象自己操作内部的属性】
        // 得到一个Person对象:
        // 方式一:new Person()
        // 方式二:采用反射技术操作构造对象创建对应字节码文件类的对象
        // 方式三:直接使用Class对象获取
        Object o = aClass.newInstance();
        age.set(o, 13);
        //由于属性被封装,直接操作不了,所以就需要后面的暴力反射了
        System.out.println(age.get(o));

 3.5获取类中的成员方法并执行
>1.Class类获取成员方法对象:

        Method[] getMethods():返回所有公共成员变量对象的数组

 Method[] methods = aClass.getMethods();

        Method[] getDeclaredMethods():返回所有成员变量对象的数组

Method[] methods1 = aClass.getDeclaredMethods();

         Method getMethod(Class<?>... parameterTypes):返回单个公共成员变量对象

Method method = aClass.getMethod("方法名", String.class);

         Method getDeclaredMethod(Class<?>... parameterTypes):返回单个成员变量对象

Method method1 = aClass.getDeclaredMethod("方法名", int.class);

2.Method类型: 表示一个成员方法类型,每个对象都是一个具体的成员方法
         作用: 获取成员方法的对象,调用执行该方法.
3.Method类用于给成员方法执行的方法:
          invork(Object o ,实参);

        Object o = aClass.newInstance();
        //获得Method对象
        Method method = aClass.getMethod("show", String.class);
        //调用方法
        method.invoke(o, "苗苗");

获取对象的几种方式【方法和属性只有对象才有权调用】:

1.直接new对象;

2.使用反射对象的构造创建对象;

3.反射对象直接newInstance( );

应用代码:

package com.tlc.reflect;
/**
 * 定义一个配置文件run.properties里面有全限定类名和方法名:
 *定义一个类, 类中包含一个方法: public int getSum(int x, int y), 做两数求和
 * 读取配置文件内容,使用反射运行Person类中的对应方法getSum
 */

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Goddess {
    public static void main(String[] args) {
        //1.正向调用getSum方法(1·获取方法所在类对象;1.对象调用方法)
        Demo demo = new Demo();
        int sum = demo.getSum(12, 12);
        System.out.println(sum);

        //2.利用反射调用方法【1.获取方法所在类的反射对象;2.反射对象获取方法对象】
        try {
            //创建一个属性集对象获取配置文件的信息
            Properties properties = new Properties();
            properties.load(new FileInputStream("Item/mm/resource/run.properties"));
            //获取配置文件中的权限定类名和方法名
            String className = properties.getProperty("className");
            String methodName = properties.getProperty("methodName");
            //这里可以先查看是否获取
            System.out.println(className);
            System.out.println(methodName);

            Class<?> aClass = Class.forName(className);
            Method method = aClass.getMethod(methodName, int.class, int.class);
            //反射对象实例化对象
            Object o = aClass.newInstance();
            //方法对象调用方法
            Object invoke = method.invoke(o, 12, 12);
            System.out.println(invoke);
        } catch (ClassNotFoundException | NoSuchMethodException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

getName方法所在类:

public class Demo {
    public int getSum(int x, int y) {
        return x + y;
    }
}

配置文件【run.properties】: 

【利用反射完成泛型檫除】 

ArrayList<String> list = new ArrayList<>();
//获取Class对象
Class<? extends ArrayList> listClass = list.getClass();
//获取add方法对象
Method add = listClass.getMethod("add", Object.class);
add.invoke(list, 1314);
System.out.println(list);

 3.6暴力反射
1.通过Class类中:
>    getDeclaredXXX方法: 可以获取类中的所有声明的成员(属性、方法、构造),私有的成员也可以获取到.但是私有成员进行访问使用时,会因为权限问题导致失败,因此就需要暴力反射解决访问私有的问题。
2.修改该对象的访问权限:
>   AccessibleObject类是Field,Method和Constructor对象的基类. 它提供了将反射对象标记为在使用它时抑制默认Java语言访问控制检查的功能.
>    setAccessible(boolean flag): true的值表示反射对象应该在使用时抑制Java语言访问检查,false的值表示反映的对象应该强制执行Java语言访问检查.
3.一旦设定当前对象可以访问,私有的成员也可以被访问,被修改.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值