反射机制分析

一、概念

官方概念

java反射机制是在运行状态中,

对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个属性和方法;

这种动态获取的信息及动态调用对象的的方法功能成为java反射机制。

要想解剖一个类,必须先要获取到该类的class字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象.

我的理解:

JVM把.class文件加载到方法区,然后创建class对象到堆中(这里的对象不是我们new的对象,这是类的类型对象)。然而并不是所有的类都会加载到虚拟内存中,这样会占用大量的内存,JVM会首先把保证程序运行的基础类一次性加载到内存中。

只有在堆中有class对象,我们才可以new对象。

在Java中,无论是类还是接口,实例对象在JVM的内存逻辑模型中都会存在Super和Class指针,分别指向根类(Object类)和反射类(Class类,就是类的类型对象)

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI。这项信息纪录了每个对象所属的类。JVM通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类就是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。

字节码对象的生成:源文件--->字节码文件-->字节码对象

总之,反射的目的就是动态的获取class对象,然后就可以进行实例化对象。

大部分框架底层都是反射比如Spring等。在Spring中,Spring通过扫描标有service注解的类,然后实例化后放进容器内,用哪个实例化就实例化那个,并不是所有的都加载。

Spring框架是层与层直接解耦的“神器”,例如:我们不必在Service中使用new Dao 的方式创建Dao对象,那样具体的Dao实现就与Service层耦合死了,我们可以通过 工厂+反射+XML配置的方式降低层与层之间的耦合性       

二.API

Man

@Data
public class Person extends Man {
    public Person() {
    }

    public Person(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public String name;
    protected Integer age;
    private String sex;


    public void method1() {
        System.out.println("method1");
    }

    private void method2() {
        System.out.println("method2");
    }

    protected void method3() {
        System.out.println("method3");
    }
}

Person

@Data
public class Person extends Man {
    public Person() {
    }

    public Person(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public String name;
    protected Integer age;
    private String sex;


    public void method1() {
        System.out.println("method1");
    }

    private void method2() {
        System.out.println("method2");
    }

    protected void method3() {
        System.out.println("method3");
    }
}

通过反射获得class对象,然后创建构造方法,然后可以实例化对象

//目的:通过反射获得字节码对象并操作内部的组件

(1)获得类的字节码对象(class对象)

获得运行时的java的字节码对象Class的方式有三种:

1)通过类的全限定名获得:Class.forName(类的全限定名);(例:数据库驱动,spring底层)

2)通过类名获得:类名.class(注意这种方法是懒加载,不会初始化static)

3)通过实例对象获得:实例对象.getClass();

获得类的字节码对象的方式

// 方式一 Class<?> clazz1 = Class.forName("cn.guxl.p2_reflect.Person"); // 方式二 Class<?> clazz2 = Person.class; // 方式三 Person person = new Person(); Class<?> clazz3 = person.getClass();

获得字节码对象Class之后,可以获得无参和有参构造方法,然后可以通过构造方法调用newInstance()创建对象

(2)获得构造方法

无参构造-实例化对象

  • public Constructor<T> getConstructor()
//获取无参构造
Constructor<?> constructor1 = clazz1.getConstructor();
//实例对象
Person person1 = (Person) constructor1.newInstance();

有参构造-实例化对象

  • public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取有参构造
Constructor<?> constructor2 = clazz1.getConstructor(String.class, Integer.class,String.class);
Person p2 = (Person) constructor2.newInstance("张三", 22,"男");

(3)获得属性

  • public Field[] getFields() // 所有的公共(public)的字段,包括父类
  • public Field getField(String name) //获取单个(public)的字段,包括父类
  • public Field[] getDeclaredFields() // 所有的包括public、private和proteced,不包括父类的申明字段。
  • public Field getDeclaredField(String name) //获取单个的包括public、private和proteced,不包括父类的申明字段。
// getFields()
System.out.println("~~getFields()~~~");
Field[] fields = clazz1.getFields();
for (Field files : fields ) {
    System.out.println(files.getName());
}
//getField()
System.out.println("~~getField()~~~");
Field name = clazz1.getField("name");
System.out.println(name.getName());
Field sub_name = clazz1.getField("sub_ame");
System.out.println(sub_name.getName());

//getDeclaredFields()
System.out.println("~~getDeclaredFields()~~~");
Field[] declaredFields = clazz1.getDeclaredFields();
for (Field files : declaredFields ) {
    System.out.println(files.getName());
}
//getDeclareField()
System.out.println("~~getDeclaredField()~~~");
Field name1 = clazz1.getDeclaredField("name");
System.out.println(name1.getName());
Field age = clazz1.getDeclaredField("age");
System.out.println(age.getName());
Field sex = clazz1.getDeclaredField("sex");
System.out.println(sex.getName());

输出:

~~getFields()~~~

name

sub_ame

~~getField()~~~

name

sub_ame

~~getDeclaredFields()~~~

name

age

sex

~~getDeclaredField()~~~

name

age

sex

(4)获得方法

  • public Method[] getMethods() // 所有的公共(public)的方法,包括父类
  • public Method getMethod() //获取单个(public)的方法,包括父类
  • public Method[] getDeclaredMethods() // 所有的包括public、private和proteced,不包括父类的申明字段。
  • public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 单个的包括public、private和proteced,不包括父类的申明字段。

方法获取

//clazz1.getMethods();
System.out.println("~~getMethods()~~~");
Method[] methods = clazz1.getMethods();
for (Method method : methods) {
    System.out.println(method.getName());
}
//clazz1.getMethod();
System.out.println("~~getMethod()~~~");
Method method1 = clazz1.getMethod("method1");
System.out.println(method1.getName());
Method sub_method1 = clazz1.getMethod("sub_method1");
System.out.println(sub_method1.getName());
//clazz1.getDeclaredMethods();
System.out.println("~~getDeclaredMethods()~~~");
Method[] declaredMethods = clazz1.getDeclaredMethods();
for (Method method : declaredMethods) {
    System.out.println(method.getName());
}
//clazz1.getDeclaredMethod();
System.out.println("~~getDeclaredMethod()~~~");
Method method2 = clazz1.getDeclaredMethod("method2");
System.out.println(method2.getName());

方法执行

  • public Object invoke(Object obj, Object... args) //方法执行
System.out.println("~~invoke()~~~");
Person person = (Person) clazz1.newInstance();
method1.invoke(person);
sub_method1.invoke(person);
// 如果不这是设置为true private method2无法执行
method2.setAccessible(true);
method1d.invoke(person);
method2.invoke(person);
method3.invoke(person);

method.setAccessible(true);//暴力访问如果不设置无法执行private方法

method.invoke(Object obj, Object... args)

(5)通过类加载器获得文件的路径

Class对象有一个获得加载该字节码文件的类加载器的方法,通过类加载器,可以过得classpath下的文件的路径

ClassLoader classLoader = clazz1.getClassLoader();
//classLoader就是加载classpath路径下的字节码文件
//引申:classloader就是加载的src下的内容
//类加载器有获得src下的某一个资源的能力
//getResource(String path)参数是一个地址,这个地址是相对于src的

获取src下的问件的路径

System.out.println("~~getResource()~~~");
ClassLoader classLoader = clazz1.getClassLoader();
String path1 = classLoader.getResource("db.properties").getPath();
String path2 = classLoader.getResource("").getPath();
System.out.println(path1);
System.out.println(path2);

此外,对于读写配置文件,例如properties文件,Java还提供了getResourceAsStream(“文件”)方法返回一个InputStream供读取文件: 

InputStream is = this.getClass().getClassLoader().getResourceAsStream(“conf.properties”); ​

(6)setAccessible()方法

1. private类型的field调用get(obj)

2.private类型的method调用invoke

3.提升反射性能

System.out.println("~~setAccessible()~~~");
//1.private属性无法使用field.get(obj)
System.out.println("~~private field~~~");
Field sex1 = clazz1.getDeclaredField("sex");
Person person2 = (Person) clazz1.newInstance();
person2.setSex("男");
sex1.setAccessible(true);
System.out.println(sex1.get(person2));
//2.private类型方法执行
Person person3 = (Person) clazz1.newInstance();
Method method21 = clazz1.getDeclaredMethod("method2");
method21.setAccessible(true);
method21.invoke(person3);
//3.提升反射性能

反射性能测试

public class TestAccessible {

    public static void test01() {
        Person person = new Person();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            person.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方法执行1000万次" + (endTime - startTime) + "ms");
    }

    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Class c1 = person.getClass();
        Method getName = c1.getMethod("getName");//getName方法被得到
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            getName.invoke(person, null);//传入gameName方法来使用getName()
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射执行1000万次" + (endTime - startTime) + "ms");
    }

    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Class c1 = person.getClass();
        Method getName = c1.getMethod("getName");//Method类专门接受方法
        getName.setAccessible(true);//表示不检测方法是否为public或者private
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            getName.invoke(person, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("关闭检测执行1000万次" + (endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}

普通方法执行1000万次2794ms

反射执行1000万次20176ms

关闭检测执行1000万次11320ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值