一、概念

官方概念
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
1201

被折叠的 条评论
为什么被折叠?



