您的位置 首页 java

Java基础——反射技术

一、反射介绍

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());//输出类名
}
}  

运行结果:

Java基础——反射技术

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());
}
}  

运行结果:

Java基础——反射技术

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);
}
}  

运行结果:

Java基础——反射技术

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.处理注解。

文章来源:智云一二三科技

文章标题:Java基础——反射技术

文章地址:https://www.zhihuclub.com/182271.shtml

关于作者: 智云科技

热门文章

网站地图