一、反射介绍
1.1什么是反射
Java反射机制是Java语言一个很重要的特性,它使得Java具有了“动态性”。在Java程序运行时,对于任意的一个类,我们不能知道这个类有哪些属性和方法;对于任意的一个对象,我们又不能调用它任意的方法!这种动态获取类的信息以及动态调用对象方法的功能就来自于Java语言的反射(Reflection)机制。
1.2反射的作用
简单来说两个作用,RTTI(运行时类型识别)和DC(动态创建)。
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
二、创建对象过程
2.1创建Java对象的三个阶段

2.2创建对象时内存结构
Usersuser=newUsers();

实际上,在加载任何一个类时都会在方法区中建立“这个类对应的Class对象”,由于“Class对象”包含了这个类的整个结构信息,所以可以通过这个“Class对象”来操作这个类。
要使用一个类,首先要加载类;加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。可以通过这个对象知道类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以,形象的称之为:反射。因此,“Class对象”是反射机制的核心。
三、反射的具体实现
3.1 获取Class对象的三种方式
1.通过 getClass()方法。
2.通过.class 静态属性。
3.通过 Class 类中的静态方法 forName()。
package cn.pxy.test;
/**
* 创建Users类,用来测试获取对象的三种方式
* @author 胖咸鱼
*
*/public class Users {
private String username;
private int userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
}
package cn.pxy.test;
/**
* 测试获取Class对象的三种方式
* @author 胖咸鱼
*
*/public class GetClass1 {
public static void main(String[] args) throws ClassNotFoundException {
Users users=new Users();
Class clazz1=users.getClass();//方式一,运用getClass方法
Class clazz2=Users.class;//方式二,通过.class静态属性获取class对象
Class clazz3=Class.forName("cn.pxy.test.Users");//方式三,通过forName()获取class对象
//由于系统针对每个类只会创建一个class对象,因此clazz1、2、3实际指向的是同一个对象
System.out.println(clazz1==clazz2);
System.out.println(clazz1==clazz3);
//获取类名
System.out.println(clazz1.getName());//输出包名+类名
System.out.println(clazz1.getSimpleName());//输出类名
}
}
运行结果:

3.2.获取类的构造方法
方法介绍:
方法名 | 描述 |
getDeclaredConstructors() | 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法 |
getConstructors() | 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class对象所表示的类的所有公共(public)构造方法 |
getConstructor(Class<?>… parameterTypes) | 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共(public)构造方法。 |
getDeclaredConstructor(Class<?>… parameterTypes) | 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 |
方法使用 :
修改Users类,用来测试获取构造方法:
package cn.pxy.test;
/**
* 创建Users类,用来测试获取对象的三种方式
* @author 胖咸鱼
*
*/public class Users {
private String username;
private int userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
public Users(String username,int age) {
this.userage=userage;
this.username=username;
}
public Users(String username) {
this.username=username;
}
private Users(int userage) {
this.userage=userage;
}
public Users() {
}
}
获取构造方法:
package cn.pxy.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class GetConstructor {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clazz=Users.class;
//返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表 示的类声明的所有构造方法。
Constructor[] arr=clazz.getDeclaredConstructors();//获取所有构造方法
for(Constructor c:arr) {
System.out.println(c);
}
System.out.println("-------------");
//返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共(public)构造方法。
Constructor[] arr1=clazz.getConstructors();
for(Constructor c:arr1) {
System.out.println(c);
}
System.out.println("--------------------");
//返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类 或接口的指定构造方法。
Constructor c=clazz.getDeclaredConstructor(int.class);//带参,int的构造方法
System.out.println(c);
System.out.println("--------------");
//返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指 定公共(public)构造方法。
Constructor c1=clazz.getConstructor(null);
System.out.println(c1);
System.out.println("----------");
/**
* 通过构造方法创建对象
*/Constructor c2=clazz.getDeclaredConstructor(String.class,int.class);//带参,int、String的构造方法
System.out.println(c);
Object o=c2.newInstance("胖咸鱼",18);
Users users=(Users)o;//强制类型转换
System.out.println(users.getUsername()+"t"+users.getUserage());
}
}
运行结果:

3.3获取类的成员变量
方法介绍:
方法名 | 描述 |
getFields() | 返回 Field 类型的一个数组,其中包含 Field 对象的所有公共(public)字段。 |
getDeclaredFields() | 返回 Field 类型的一个数组,其中包含 Field 对象的所有字段。 |
getField(String fieldName) | 返回一个公共成员的 Field 指定对象。 |
getDeclaredField(String fieldName) | 返回一个 Field 指定对象。 |
方法使用:
package cn.pxy.test;
import java.lang.reflect.Field;
public class GetField {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
Class clazz=Users.class;
Field[] fields=clazz.getFields();//获取public修饰的属性
for(Field f:fields) {
System.out.println(f);
}
System.out.println("---------");
Field[] fields2=clazz.getDeclaredFields();//获取所有属性
for(Field f:fields2) {
System.out.println(f);
}
System.out.println("-------------");
Field field=clazz.getField("userage");//通过名字获取Field对象
System.out.println(field);
System.out.println("-----------");
Field field2=clazz.getDeclaredField("username");
System.out.println(field2);
/**
* 操作成员变量
*/Field f=clazz.getField("userage");
//对象实例化
Object obj=clazz.newInstance();
//为成员变量赋新值
f.set(obj, 20);
//获取成员变量的值
Object o=f.get(obj);
System.out.println(o);
}
}
运行结果:

3.4.获取类的方法
方法介绍:
方法名 | 描述 |
getMethods()
| 返回一个 Method 类型的数组,其中包含 所有公共(public)方法。 |
getDeclaredMethods()
| 返回一个 Method 类型的数组,其中包含 所有方法。 |
getMethod(String name, Class<?>… parameterTypes)
| 返回一个公共的 Method 方法对象。 |
getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回一个方法 Method 对象 |
方法使用:
修改Users类用来测试获取方法(添加了一个方法):
package cn.pxy.test;
/**
* 创建Users类,用来测试获取对象的三种方式
* @author 胖咸鱼
*
*/public class Users {
private String username;
public int userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
public Users(String username,int userage) {
this.userage=userage;
this.username=username;
}
public Users(String username) {
this.username=username;
}
private Users(int userage) {
this.userage=userage;
}
public Users() {
}
private void pangxianyu() {
System.out.println("Hello 胖咸鱼先生!");
}
}
测试:
package cn.pxy.test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class GetMethod {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Class clazz=Users.class;
Method[] methods=clazz.getMethods();//获取public修饰的方法
for(Method m:methods) {
System.out.println(m);
}
System.out.println("-----------");
Method[] methods2=clazz.getDeclaredMethods();//获取所有方法
for(Method m:methods2) {
System.out.println(m);
}
System.out.println("------------");
Method method=clazz.getMethod("setUserage", int.class);//根据指定名字和参数获取方法
System.out.println(method.getName());
System.out.println("------------");
Method method2=clazz.getDeclaredMethod("pangxianyu");
System.out.println(method2.getName());
/**
* 调用方法
*/Method method3=clazz.getMethod("setUsername", String.class);
//实例化对象
Object obj=clazz.newInstance();
//通过setUserName赋值
method3.invoke(obj, "胖咸鱼");
//通过getUserName获取值
Method method4=clazz.getMethod("getUsername");
Object value=method4.invoke(obj);
System.out.println(value);
}
}
运行结果:

3.5.获取类的其他信息
package cn.pxy.test;
public class GetClassInfo {
public static void main(String[] args) {
Class clazz=Users.class;
//获取类名
String className=clazz.getName();
System.out.println(className);
//获取包名
Package p=clazz.getPackage();
System.out.println(p.getName());
//获取超类(即直接父类)
Class superClass=clazz.getSuperclass();
System.out.println(superClass.getName());
//获取类的所有实现的接口,没有则不返回
Class[] interfaces=clazz.getInterfaces();
for(Class inter:interfaces) {
System.out.println(inter.getName());
}
}
}
运行结果:

3.6.反射应用案例
package cn.pxy.test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ReflectDemo rd=new ReflectDemo();
if(args!=null&&args.length>0) {
//获取ReflectDemo的class对象
Class clazz=rd.getClass();
//通过反射获取ReflectDemo下的所有方法
Method[] methods=clazz.getMethods();
for(String str:args) {
for(int i=0;i<methods.length;i++) {
if(str.equalsIgnoreCase(methods[i].getName())) {
methods[i].invoke(rd);
break;
}
}
}
}else {//方法执行的默认顺序
rd.method1();
rd.method2();
rd.method3();
}
}
}
四、反射机制的效率
由于 Java 反射是要解析字节码,将内存中的对象进行解析,包括了一些动态类型,而JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!
接下来我们做个简单的测试来直接感受一下反射的效率。
4.1 反射机制的效率测试
package cn.pxy.test;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) {
try {
//反射耗时
Class clazz=Class.forName("cn.pxy.test.Users");
Users users=(Users) clazz.newInstance();
long reflectStart=System.currentTimeMillis();
Method method=clazz.getMethod("setUsername", String.class);
for(int i=0;i<100000000;i++) {
method.invoke(users, "胖咸鱼");
}
long reflectEnd=System.currentTimeMillis();
//非反射的耗时
long start=System.currentTimeMillis();
Users u=new Users();
for(int i=0;i<100000000;i++) {
u.setUsername("胖咸鱼");
}
long end=System.currentTimeMillis();
System.out.println("反射执行时间:"+(reflectEnd-reflectStart));
System.out.println("普通方式执行时间:"+(end-start));
}catch (Exception e){
e.printStackTrace();
}
}
}
运行结果:

4.2 setAccessible 方法
setAccessible()方法 :
setAccessible 是启用和禁用访问安全检查的开关。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查;默认值为 false。
由于 JDK 的安全检查耗时较多.所以通过 setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的。
package cn.pxy.test;
import java.lang.reflect.Field;
public class Test2 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Users users=new Users();
Class clazz=users.getClass();
Field field=clazz.getDeclaredField("username");
//忽略安全检查,private修饰的成员(变量和方法)语法上只能在本类中进行访问,
//但是也有例外,如果使用的是反射技术,可以通过setAccessible(true)进行暴力访问。
field.setAccessible(true);
field.set(users, "胖咸鱼");
Object o=field.get(users);
System.out.println(o);
}
}
五、总结
Java 反射机制是 Java 语言一个很重要的特性,它使得 Java 具有了“动态性”。
反射机制的优点 :更灵活、更开放。
反射机制的缺点 :降低程序执行的效率、增加代码维护的困难。
获取 Class 类的对象的三种方式 :运用 getClass()、运用.class 语法、运用 Class.forName()(最常被使用)。
反射机制的常见操作:
1.动态加载类、动态获取类的信息(属性、方法、构造器)。
2.动态构造对象。
3.动态调用类和对象的任意方法。
4.动态调用和处理属性。
5.获取泛型信息。
6.处理注解。