Java工程师知识树 / Java基础
反射(Reflection)的概述
反射的由来:
加载完类之后,在堆内存的方法区中就会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。我们可以通过这个对象看到这个类的结构,这个对象就像一面镜子,透过这个镜子看到了类的结构,所以,通过这样获取类信息的方式形象的称之为,反射。
Java反射机制就是允许Java程序在运行期间借助Reflection API取得任何类的内部信息,并能直接操作对象的内部属性和方法。
使用反射获取对象以及之后操作的前提条件:必须先得到代表字节码的Class类,Class类用于表示.class文件(字节码)
反射在日常工作中用的倒不是很多,而被广泛应用于框架设计之中,反射是框架设计的灵魂。
反射的优缺点:
优点:反射可以实现动态创建对象和编译,体现出很大的灵活性。
缺点:使用反射对性能有一定影响。慢与直接创建对象调用方法。
反射使用实例:
public class Test {
public static void main(String[] args) throws Exception {
long start1 = System.currentTimeMillis();
User user = new User();
for (int i = 0; i < 1000000; i++) {
user.getName();
}
long end1 = System.currentTimeMillis();
System.out.println("普通方式执行:" + (end1 - start1) + "ms");
long start2 = System.currentTimeMillis();
Class userClass = user.getClass();
Method getName = userClass.getDeclaredMethod("getName", null);
for (int i = 0; i < 10000000; i++) {
getName.invoke(user, null);
}
long end2 = System.currentTimeMillis();
System.out.println("反射方式执行:" + (end2 - start2) + "ms");
long start3 = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
getName.invoke(user, null);
}
long end3 = System.currentTimeMillis();
System.out.println("反射方式关闭检测执行:" + (end3 - start3) + "ms");
}
}
class User {
private String name;
public void getName() {
}
}
获得Class类的方式
- 如果已有具体的类,通过类的class属性获取,最为安全可靠且性能最高的方法。(Class xxx=XXX.class;)
- 已知某个类的实例,调用此实例的getClass()方法获取Class对象。(Class xxx=xxx1.getClass();)
- 已知一个类的全名且在类路径下,可以通过Class类的静态方法forName()获取,需要处理异常ClassNotFoundException(Class xxx=ClassforName(“yyy.XXX”);)
- 内置基本数据类型可以直接使用类名.Type
- 还可以用ClassLoader
public class TestReflection {
public static void main(String[] args) throws ClassNotFoundException {
//获取Class对象
Student student = new Student();
//Class xxx=XXX.class
Class aClass = Student.class;
//Class xxx=xxx1.getClass();
Class bClass = student.getClass();
//Class xxx=ClassforName(“yyy.XXX”);
Class cClass = Class.forName("com.wechat.management.reflection.Student");
//.Type
Class integerClass = Integer.TYPE;
Class intClass = int.class;
//ClassLoader
ClassLoader loader = ClassLoader.getSystemClassLoader();//获取加载器对象
Class classString = loader.loadClass("java.lang.String");
Class dClass = loader.loadClass("com.wechat.management.reflection.Student");
System.out.println(aClass.hashCode());
System.out.println(bClass.hashCode());
System.out.println(cClass.hashCode());
System.out.println(dClass.hashCode());
}
}
class Student{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在运行期间,一个类,只有一个Class对象产生。
反射最常用的方式为Class xxx=ClassforName(“yyy.XXX”)
。
Class xxx=XXX.class
对象都有了还要反射干什么。Class xxx=xxx1.getClass()
需要导入类的包,依赖太强,不导包就抛编译错误。一般使用Class xxx=ClassforName(“yyy.XXX”)
,参数可以传入也可写在配置文件中。
Class类的常用方法
- 类的构造方法:Class类提供了用于获取某个类的构造方法。
Constructor getConstructor(Class>... parameterTypes) 根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors() 返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class>... parameterTypes) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)
- 类的属性:Class类提供了获取成员属性的方法
Field getField(String name) 根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields() 返回具有public属性的成员变量的数组
Field getDeclaredField(String name) 根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredFields() 返回所有成员变量组成的数组(不分public和非public属性)
- 类的方法:Class类提供了获取成员方法的方法
Method getMethod(String name, Class>... parameterTypes) 根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods() 返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class>... parameterTypes) 根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性)
哪些类型可以有Class对象
- class:外部类、成员(成员内部类、静态内部类),局部内部类,匿名内部类。
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
- .....
public class Test5 {
public static void main(String[] args) {
Class c1= Object.class;//类
Class c2= Runnable.class;//接口
Class c3= String[].class;//一维二维数组
Class c4= int[][].class;//基本数据类型数组
Class c5= Override.class;//注解
Class c6= ElementType.class;//枚举
Class c7= Integer.class;//基本数据类型
Class c8= void.class;//void
Class c9= Class.class;//Class
}
}
setAccessible
setAccessible为启动和禁用安全检查的开关,为了安全默认为false (启动)
setAccessible启动与关闭测试:
package com.wechat.management.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestSetAccessible {
/**
* 测试方法
*/
public String getName() {
return "test";
}
//普通方式调用
public static void test01() {
TestSetAccessible user = new TestSetAccessible();
//开始时间
long starTime = System.currentTimeMillis();
//模拟10亿次不断获取名字所需要的时间
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
//结束时间
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次:" + (endTime - starTime) + "ms");
}
//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
TestSetAccessible user = new TestSetAccessible();
Class c1 = user.getClass();
//获取指定方法
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(false);//false为开启检测 默认为也是false,为了安全
//开始时间
long starTime = System.currentTimeMillis();
//模拟10亿次不断获取名字所需要的时间
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
//结束时间
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次:" + (endTime - starTime) + "ms");
}
//反射方式调用 关闭检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
TestSetAccessible user = new TestSetAccessible();
Class c1 = user.getClass();
//获取指定方法
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);//true为关闭检测
//开始时间
long starTime = System.currentTimeMillis();
//模拟10亿次不断获取名字所需要的时间
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
//结束时间
long endTime = System.currentTimeMillis();
System.out.println("关闭检测执行10亿次:" + (endTime - starTime) + "ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();//普通方式执行10亿次:3ms
test02();//反射方式执行10亿次:3226ms
test03();//关闭检测执行10亿次:2011ms
//如果反射调用次数多的话,可以关闭这个检测 提高程序的一个效率
}
}