反射概述
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能 直接操作任意对象的内部属性及方法。
加载完类以后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像是一面镜子,透过这个镜子,看到类的结构,称之为反射
Class类
对象反射以后可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了那些接口。
对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定的某个结构的有关信息。
- Class本身也是一个类
- Class对象只能由系统创建‘
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成的
- 通过Class可以完整的得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想加载,运行的类,唯有先获得相应的Class对象
哪些类型有Class对象
- class:外部类。成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
类的初始化
类的主动引用(一定发生类的初始化):
- 虚拟机启动,首先初始化main方法所在类
- new一个类对象
- 调用类的静态成员(除了final常量)和静态方法
- 调用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,其父类没有被初始化,先初始化它的父类
类的被动引用(不会发生类的初始化):
- 当访问一个静态域时,只有真正声明这个域才会被初始化,如:当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类的引用,不会触发此类初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类常量池中)
通过反射获得类的结构
package com.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1=Class.forName("com.reflection.Person");
System.out.println(c1.getName());//获取包和类名
System.out.println(c1.getSimpleName());//获取类名
System.out.println("==================");
Field[] fields=c1.getFields();//获取public属性
for (Field field:fields){
System.out.println(field);
}
Field[] fields1=c1.getDeclaredFields();//获取所有属性
for (Field field:fields1){
System.out.println(field);
}
Field field=c1.getDeclaredField("name");//获取指定属性
System.out.println(field);
System.out.println("==================");
Method[] methods=c1.getMethods();//获取类及其父类的public方法
for(Method method:methods){
System.out.println("获得public的:"+method);
}
Method[] methods1=c1.getDeclaredMethods();//获取本类的所有方法(private,public,protected)
for (Method method:methods1){
System.out.println(method);
}
Method method=c1.getMethod("setName", String.class);//获取指定的方法 后面的参数为所需的值的class 便于区分重载的方法
System.out.println(method);
System.out.println("==================");
Constructor[] constructors=c1.getConstructors();//获取所有的public构造方法
for (Constructor constructor:constructors){
System.out.println(constructor);
}
Constructor[] constructors1=c1.getDeclaredConstructors();//获取所有的全部构造方法
for (Constructor constructor:constructors1){
System.out.println(constructor);
}
Constructor constructor=c1.getConstructor(String.class,int.class,int.class);//获取指定的构造器
System.out.println(constructor);
}
}
通过反射 创建对象 调用方法 设置属性
package com.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test6 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1=Class.forName("com.reflection.Person");
Person person= (Person) c1.newInstance();//调用无参构造函数 已经过时
System.out.println(person);
Person person1=(Person) c1.getDeclaredConstructor().newInstance();//调用无参构造
System.out.println(person1);
//调用有参构造函数并且赋值
Person person2=(Person) c1.getDeclaredConstructor(String.class,int.class,int.class).newInstance("陈旭",18,001);
System.out.println(person2);
//反射调用方法
Person person3=(Person) c1.getDeclaredConstructor().newInstance();
//反射获取方法
Method setName=c1.getMethod("setName", String.class);
//invoke()激活方法,第一个参数为方法作用的对象,第二个为方法参数
setName.invoke(person3,"晨晨");
System.out.println(person3);
//反射调用属性
Person person4=(Person) c1.getDeclaredConstructor().newInstance();
Field field=c1.getDeclaredField("name");
//设置属性 第一个为该属性的对象 第二个为属性的值
//如果属性是private的无法直接访问 需要关闭权限检测
//field.setAccessible(true)关闭安全检测
field.setAccessible(true);
field.set(person4,"花花");
System.out.println(person4);
}
}
结果:
"D:\Program Files (x86)\jdk13\bin\java.exe" --enable-preview "-javaagent:D:\IDEA\IntelliJ IDEA Community Edition 2020.1.3\lib\idea_rt.jar=55644:D:\IDEA\IntelliJ IDEA Community Edition 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath D:\IDEA\JavaSE\out\production\基础语法 com.reflection.Test6
Person{name='null', age=0, id=0}
Person{name='null', age=0, id=0}
Person{name='陈旭', age=18, id=1}
Person{name='晨晨', age=0, id=0}
Person{name='花花', age=0, id=0}
Process finished with exit code 0
附录:setAccessible
Method和Field,Constructor对象都有setAccessible()方法
setAccessible()方法是用于启动和禁用访问安全检查的开关
参数值为true则指示反射的对象在使用时应该取消Java语言的访问检查
- 提高反射效率,若代码中必须使用反射,且该代码句使用频率较高 设为true
- 使原本无法访问的私有成员可以被访问
参数值false则指示反射的对象应该实施Java语言访问检查
性能对比分析
package com.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test7 {
public static void test1(){
Person person=new Person();
long startTime=System.currentTimeMillis();
for (int i = 0; i <1000000000 ; i++) {
person.getName();
}
long endTime=System.currentTimeMillis();
System.out.println("普通方式:"+(endTime-startTime)+"ms");
}
public static void test2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c1=Class.forName("com.reflection.Person");
Person person=(Person) c1.getDeclaredConstructor().newInstance();
Method getName=c1.getMethod("getName",null);
long startTime=System.currentTimeMillis();
for (int i = 0; i <1000000000 ; i++) {
getName.invoke(person,null);
}
long endTime=System.currentTimeMillis();
System.out.println("反射方式:"+(endTime-startTime)+"ms");
}
public static void test3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c1=Class.forName("com.reflection.Person");
Person person=(Person) c1.getDeclaredConstructor().newInstance();
Method getName=c1.getMethod("getName",null);
getName.setAccessible(true);
long startTime=System.currentTimeMillis();
for (int i = 0; i <1000000000 ; i++) {
getName.invoke(person,null);
}
long endTime=System.currentTimeMillis();
System.out.println("去除检测的反射方式:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
test1();
test2();
test3();
}
}
结果:
"D:\Program Files (x86)\jdk13\bin\java.exe" --enable-preview "-javaagent:D:\IDEA\IntelliJ IDEA Community Edition 2020.1.3\lib\idea_rt.jar=55926:D:\IDEA\IntelliJ IDEA Community Edition 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath D:\IDEA\JavaSE\out\production\基础语法 com.reflection.Test7
普通方式:16ms
反射方式:2319ms
去除检测的反射方式:1118ms
Process finished with exit code 0
通过反射获取注解
package com.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class Test8 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1=Class.forName("com.reflection.Person3");
//通过反射获取类注解
Annotation[] annotations=c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取类注解的值
//注解强行转化为MyAnnotation类型
MyAnnotation annotation=(MyAnnotation) c1.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value());
//通过反射获取属性注解
Field f=c1.getDeclaredField("name");
MyAnnotation1 myAnnotation1=f.getAnnotation(MyAnnotation1.class);
System.out.println(myAnnotation1.name());
System.out.println(myAnnotation1.type());
System.out.println(myAnnotation1.length());
}
}
@MyAnnotation("person_db")
class Person3{
@MyAnnotation1(name = "Name",type = "String",length = 100)
private String name;
@MyAnnotation1(name = "Age",type = "int",length = 10)
private int age;
@MyAnnotation1(name = "id",type = "int",length = 10)
private int id;
public Person3() {
}
public Person3(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
public String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
public String name();
public String type();
public int length();
}
结果:
"D:\Program Files (x86)\jdk13\bin\java.exe" --enable-preview "-javaagent:D:\IDEA\IntelliJ IDEA Community Edition 2020.1.3\lib\idea_rt.jar=56322:D:\IDEA\IntelliJ IDEA Community Edition 2020.1.3\bin" -Dfile.encoding=UTF-8 -classpath D:\IDEA\JavaSE\out\production\基础语法 com.reflection.Test8
@com.reflection.MyAnnotation(value="person_db")
person_db
Name
String
100
Process finished with exit code 0