JAVA深化篇_40—— 反射机制介绍
反射机制介绍
什么是反射
Java 反射机制是Java语言一个很重要的特性,它使得Java具有了“动态性”。在Java程序运行时,对于任意的一个类,我们能不能知道这个类有哪些属性和方法呢?对于任意的一个对象,我们又能不能调用它任意的方法?答案是肯定的!这种动态获取类的信息以及动态调用对象方法的功能就来自于Java 语言的反射(Reflection)机制。
反射的作用
简单来说两个作用,RTTI(运行时类型识别)和DC(动态创建)。
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
创建对象过程
Java创建对象的三个阶段
创建对象时内存结构
Users user = new Users();
实际上,我们在加载任何一个类时都会在方法区中建立“这个类对应的Class对象”,由于==“Class对象”包含了这个类的整个结构信息==,所以我们可以通过这个“Class对象”来操作这个类。
我们要使用一个类,首先要加载类;加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象知道类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以,我们形象的称之为:反射。 因此,“Class对象”是反射机制的核心。
反射的具体实现
获取Class对象的三种方式
- 通过getClass()方法;
- 通过.class 静态属性;
- 通过Class类中的静态方法forName();
创建Users类
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;
}
}
通过getClass()方法获取Class对象
/*
* 通过getClass()方法获取该类的Class对象
getClass()为Object类下的非静态方法,在使用时需要先实例化对象
*/
public class GetClass1 {
public static void main(String[] args) {
Users users = new Users();
Users users1 = new Users();
Class clazz = users.getClass();
System.out.println(clazz);
System.out.println(clazz.getName());
System.out.println(users.getClass() == users1.getClass());
}
}
通过forName()获取Class对象
/**
* 通过Class.forName("class Name")获取Class对象
*/
public class GetClass3 {
public static void main(String[] args)throws Exception {
Class clazz = Class.forName("com.java.Users");
Class clazz2 = Class.forName("com.java.Users");
System.out.println(clazz);
System.out.println(clazz.getName());
System.out.println(clazz == clazz2);
}
}
通过.class 静态属性获取Class对象
/**
* .class静态属性获取Class对象
*/
public class GetClass2 {
public static void main(String[] args) {
Class clazz = Users.class;
Class clazz2 = Users.class;
System.out.println(clazz);
System.out.println(clazz.getName());
System.out.println(clazz == clazz2);
}
}
获取类的构造方法
方法介绍
方法名 | 描述 |
---|---|
getDeclaredConstructors() | 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 |
getConstructors() | 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共(public)构造方法。 |
getConstructor(Class<?>… parameterTypes) | 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共(public)构造方法。 |
getDeclaredConstructor(Class<?>… parameterTypes) | 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 |
方法使用
修改Users类
public class Users {
private String username;
private int userage;
public Users(){
}
public Users(String username,int userage){
this.username= username;
this.userage=userage;
}
public Users(String username){
this.username= username;
}
private Users(int userage){
this.userage = 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 class GetConstructor {
public static void main(String[] args)throws Exception {
Class clazz = Users.class;
Constructor[] arr = clazz.getDeclaredConstructors();
for(Constructor c:arr){
System.out.println(c);
}
System.out.println("---------------------");
Constructor[] arr1 = clazz.getConstructors();
for(Constructor c:arr1){
System.out.println(c);
}
System.out.println("------------------------");
Constructor c = clazz.getDeclaredConstructor(int.class);
System.out.println(c);
System.out.println("------------------------");
Constructor c1 = clazz.getConstructor(null);
System.out.println(c1);
}
}
通过构造方法创建对象 newInstance()
/**
**通过反射实例化对象
*/
public class GetConstructor2 {
public static void main(String[] args) throws Exception{
//创建类对象
Class<Users> usersClass = Users.class;
//通过类对象获取指定的构造方法对象
Constructor<Users> constructor = usersClass.getConstructor(String.class, int.class);
//通过指定的构造方法对象创建对象
Users users = constructor.newInstance("zhangsan", 20);
System.out.println(users);
}
}
获取类的成员变量
方法介绍
方法名 | 描述 |
---|---|
getFields() | 返回Field类型的一个数组,其中包含 Field对象的所有公共(public)字段。 |
getDeclaredFields() | 返回Field类型的一个数组,其中包含 Field对象的所有字段。 |
getField(String fieldName) | 返回一个公共成员的Field指定对象。 |
getDeclaredField(String fieldName) | 返回一个 Field指定对象。 |
方法使用
修改Users类
public class Users {
private String username;
public int userage;
public Users(){
}
public Users(String username,int userage){
this.username= username;
this.userage=userage;
}
public Users(String username){
this.username= username;
}
private Users(int userage){
this.userage = 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 class GetField {
public static void main(String[] args) throws Exception{
Class<Users> usersClass = Users.class;
Field[] fields = usersClass.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("===================");
Field[] declaredFields = usersClass.getDeclaredFields();
for(Field field:declaredFields){
System.out.println(field);
}
System.out.println("=================");
Field userName = usersClass.getDeclaredField("userName");
System.out.println(userName);
System.out.println("=================");
Field userAge = usersClass.getField("userAge");
System.out.println(userAge);
}
}
操作成员变量 先实例化对象
public class GetField2 {
public static void main(String[] args)throws Exception {
//获取Users类的类对象
Class<Users> usersClass = Users.class;
//获取类的成员变量
Field userAge = usersClass.getField("userAge");
//通过构造方法实例化users对象
Users users = usersClass.getConstructor(null).newInstance();
//给指定成员变量赋值
userAge.set(users,20);
System.out.println(userAge.get(users));
}
}
获取类的方法
方法介绍
方法名 | 描述 |
---|---|
getMethods() | 返回一个Method类型的数组,其中包含 所有公共(public)方法。包含父类中的(public)方法!!!!! |
getDeclaredMethods() | 返回一个Method类型的数组,其中包含 所有方法。 |
getMethod(String name, Class<?>… parameterTypes) | 返回一个公共的Method方法对象。 |
getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回一个方法Method对象 |
方法使用
修改Users类
public class Users {
private String userName;
public int userAge;
private Users(String userName){
this.userName = userName;
}
public Users(String userName, int userAge){
this.userName = userName;
this.userAge = userAge;
}
public Users(){
}
public Users(int userAge){
this.userAge = 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;
}
private void sing(){
System.out.println("zhangsan爱唱歌");
}
@Override
public String toString() {
return "Users{" +
"userName='" + userName + '\'' +
", userAge=" + userAge +
'}';
}
}
获取方法
public class GetMethod {
public static void main(String[] args) throws Exception{
Class<Users> usersClass = Users.class;
Method[] classMethods = usersClass.getMethods();
for(Method method : classMethods){
System.out.println(method);
System.out.println(method.getName());
}
System.out.println("----------------");
Method[] declaredMethods = usersClass.getDeclaredMethods();
for(Method method : declaredMethods){
System.out.println(method);
System.out.println(method.getName());
}
System.out.println("=================");
Method setUserAge = usersClass.getMethod("setUserAge", int.class);
System.out.println(setUserAge.getName());
System.out.println(setUserAge);
System.out.println("===============");
Method sing = usersClass.getDeclaredMethod("sing");
System.out.println(sing);
System.out.println(sing.getName());
}
}
调用方法 invoke( )
public class GetMethod2 {
public static void main(String[] args)throws Exception {
//实例化类对象
Class<Users> usersClass = Users.class;
Method setUserName = usersClass.getMethod("setUserName", String.class);
//实例化对象
Users users = usersClass.getConstructor(null).newInstance();
//通过setUserName赋值
setUserName.invoke(users,"zhangsan");
//通过getUserName获取值
Method getUserName = usersClass.getMethod("getUserName");
Object userName = getUserName.invoke(users);
System.out.println(userName);
}
}
获取类的其他信息
public class GetClassInfo {
public static void main(String[] args) {
Class<Users> usersClass = Users.class;
//获取包名
Package usersClassPackage = usersClass.getPackage();
System.out.println(usersClassPackage);
System.out.println(usersClassPackage.getName());
//获取类名
String usersClassName = usersClass.getName();
System.out.println(usersClassName);
//获取超类
Class<? super Users> superclass = usersClass.getSuperclass();
System.out.println(superclass.getName());
//获取所有接口
Class<?>[] classInterfaces = usersClass.getInterfaces();
for(Class interfaces:classInterfaces){
System.out.println(interfaces.getName());
}
}
}
反射机制的效率
由于Java反射是要解析字节码,将内存中的对象进行解析,包括了一些动态类型,而JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!
接下来我们做个简单的测试来直接感受一下反射的效率。
反射机制的效率测试
public class Test {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("com.itbaizhan.Users");
Object o = aClass.getConstructor(null).newInstance();
Method setUsername = aClass.getMethod("setUserName",String.class);
long reflectStart = System.currentTimeMillis();
for(int i =0;i<100000000;i++){
setUsername.invoke(o,"zhangsan");
}
long reflectEnd = System.currentTimeMillis();
long start = System.currentTimeMillis();
Users u =new Users();
for(int i=0;i<100000000;i++){
u.setUserName("zhangsan");
}
long end = System.currentTimeMillis();
System.out.println("反射执行时间:"+(reflectEnd-reflectStart));
System.out.println("普通方式执行时间:"+(end-start));
}
}
反射执行时间:169
普通方式执行时间:0
setAccessible方法
setAccessible()方法:
setAccessible是启用和禁用访问安全检查的开关。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查;默认值为false。
由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的。
public class Test2 {
public static void main(String[] args) throws Exception{
Class<Users> usersClass = Users.class;
Users users = usersClass.getConstructor(null).newInstance();
Field userName = usersClass.getDeclaredField("userName");
//忽略安全检查
userName.setAccessible(true);
userName.set(users,"zhangsan");
Object o = userName.get(users);
System.out.println(o);
Method sing = usersClass.getDeclaredMethod("sing");
//忽略安全检查
sing.setAccessible(true);
sing.invoke(users);
}
}
反射总结
- Java 反射机制是Java语言一个很重要的特性,它使得Java具有了==“动态性”。==
- 反射机制的优点:
- 更灵活。
- 更开放。
- 反射机制的缺点:
- 降低程序执行的效率。
- 增加代码维护的困难。
- 获取Class类的对象的三种方式:
- 运用getClass()。(非静态方法,需要先实例化)
- 运用.class 语法。
- 运用Class.forName()(最常被使用)。
- 反射机制的常见操作
- 动态加载类、动态获取类的信息(属性、方法、构造器)。
- 动态构造对象。
- 动态调用类和对象的任意方法。
- 动态调用和处理属性。
- 获取泛型信息。
- 处理注解。