Java基础知识之反射机制简介

举报
yd_273762914 发表于 2021/08/13 00:20:41 2021/08/13
【摘要】 系列博客专栏: JVM系列博客专栏SpringBoot系列博客 1、什么是Java反射机制? 在程序运行中动态地获取类的相关属性,同时调用对象的方法和获取属性,这种机制被称之为Java反射机制 下面给出一个反射的简单例子: import lombok.Data; @Data public class User { public String usernam...

系列博客专栏:

1、什么是Java反射机制?

在程序运行中动态地获取类的相关属性,同时调用对象的方法和获取属性,这种机制被称之为Java反射机制

下面给出一个反射的简单例子:


import lombok.Data;

@Data
public class User { public String username; private String password; @Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
public static void reflectionSimpleExample() throws Exception{ User user = new User(); System.out.println(user.toString());
	// 获取对应类的class Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User"); Object obj = cls.newInstance(); System.out.println(obj);
	// 获取成员变量,注意要是public的 Field field = cls.getField("username"); System.out.println(field.get(obj));

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2、反射机制原理

Java反射是Java实现动态语言的关键,也就是通过反射实现类动态加载

  • 静态加载: 在编译时加载相关的类,如果找不到类就会报错,依赖性比较强
  • 动态加载:在运行时加载需要的类,在项目跑起来之后,调用才会报错,降低了依赖性

例子:静态加载,如下代码,如果找不到类的情况,代码编译都不通过

User user = new User();

  
 
  • 1

而动态加载,就是反射的情况,是可以先编译通过的,然后在调用代码时候,也就是运行时才会报错

 Class<?> cls = Class.forName("com.example.core.example.reflection.User");
Object obj = cls.newInstance();

  
 
  • 1
  • 2

Exception in thread “main” java.lang.ClassNotFoundException: com.example.core.example.reflection.User

java中的反射允许程序在执行期借助jdk中Reflection API来获取类的内部信息,比如成员变量、成员方法、构造方法等等,并能操作类的属性和方法

java中反射的实现和jvm和类加载机制有一定的关系,加载好类之后,在jvm的堆中会产生一个class类型的对象,这个class类包括了类的完整结构信息,通过这个class对象就可以获取到类的结构信息,所以形象地称之为java反射

在这里插入图片描述

3、Class类介绍

3.1、Class类基本介绍

然后这个Class类是什么?看下uml类图:
在这里插入图片描述
Class也是类,因此也继承Object类

Class不是直接new出来的,而是经过系统创建的

User user = new User();

  
 
  • 1

打个断点,debug进行看看源码,可以看到传统的直接new一个对象也是通过类加载器loadClass拿到的
在这里插入图片描述
java反射方式,通用通过调试看看:

 Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");

  
 
  • 1

同样本质也是通过ClassLoad再通过类的全类名
在这里插入图片描述

3.2、Class类对象的获取方法

  • Class.forname()
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");

  
 
  • 1

应用场景:多用于读取类全路径,加载类

  • 具体类.class
    已经知道具体类的情况,通过具体类的class属性获取
Class u = User.class;

  
 
  • 1

应用场景:多用于用于参数传递

  • 对象.getClass
    已经创建好对象的情况,直接通过对象实例获取class对象
Class cls = user.getClass();

  
 
  • 1

应用场景:通过创建好的对象,获取Class对象

  • ClassLoader获取
ClassLoader cl = user.getClass().getClassLoader();
Class cls = cl.loadClass("com.example.core.example.reflection.domain.User");

  
 
  • 1
  • 2
  • 基本数据类型
Class cls = int.class;

  
 
  • 1
  • 包装类
    基本数据类型对应的包装类可以通过.TYPE得到Class类对象
Class cls = Integer.TYPE;

  
 
  • 1

3.3 、可以获取Class对象的类型

  • 1、外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  • 2、interface:接口
  • 3、数组
  • 4、enum:枚举
  • 5、annotation:注解
  • 6、基本数据类型
  • 7、void
  • 8、Class
  • … ,等等

import com.example.core.example.reflection.domain.User;
import lombok.ToString;

import java.util.List;

public class GetClassObjectExample { public static void main(String[] args) { // 外部类 Class<User> cls1 = User.class; // 接口 Class<List> cls2 = List.class; // 数组 Class<Integer[]> cls3 = Integer[].class; // 二维数组 Class<String[][]> cls4 = String[][].class; // 注解 Class<lombok.ToString> cls5 = ToString.class; // 枚举 Class<Thread.State> cls6 = Thread.State.class; // 基本数据类型 Class<Long> cls7 = Long.class; // void 数据类型 Class<Void> cls8 = Void.class; // Class Class<Class> cls9 = Class.class; System.out.println(cls1); System.out.println(cls2); System.out.println(cls3); System.out.println(cls4); System.out.println(cls5); System.out.println(cls6); System.out.println(cls7); System.out.println(cls8); System.out.println(cls9); }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

4、java反射的作用?

  • 可以通过外部类的全路径名创建对象,并使用这些类
  • 可以枚举出类的全部成员,包括构造函数、属性、方法
  • 利用反射 API 访问类的私有成员

5、反射API主要类

  • 1、java.lang.Class:代表一个类,表示某个类在jvm堆中的对象
  • 2、 java.lang.reflect.Method:代表类的方法
  • 3、 java.lang.reflect.Field:代表类的成员变量
  • 4、 java.lang.reflect.Constructor:代表类额构造方法

6、Java反射的优缺点

  • 优点:使用Java反射可以灵活动态地创建和使用对象,反射是框架的底层支撑
  • 缺点:使用Java反射,基本就是解释执行的,对执行速度是有影响的

7、反射调用的优化方法

前面介绍了Java反射虽然很灵活,但是缺点就是调用时候比较慢,相对直接new对象来说,情况是怎么样的?下面通过例子进行试验:



import com.example.core.example.reflection.domain.User;

import java.lang.reflect.Method;

public class TestReflectionExample { private static final Integer TOTAL_COUNT = 1000000; public static void main(String[] args) throws Exception{ test0(); test1(); test2(); } public static void test0() { long start = System.currentTimeMillis(); User user = new User(); for (int i = 0 ; i < TOTAL_COUNT; i++) { user.hello(); } System.out.println(String.format("传统调用方法执行时间:%d" , System.currentTimeMillis() - start)); } public static void test1() throws Exception{ long start = System.currentTimeMillis(); Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User"); Object obj = cls.newInstance(); Method hello = cls.getMethod("hello"); for (int i = 0 ; i < TOTAL_COUNT; i++) { hello.invoke(obj); } System.out.println(String.format("反射方法调用执行时间:%d" , System.currentTimeMillis() - start)); } public static void test2() throws Exception{ long start = System.currentTimeMillis(); Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User"); Object obj = cls.newInstance(); Method hello = cls.getMethod("hello"); // 关键,取消调用反射方法时的安全检查 hello.setAccessible(true); for (int i = 0 ; i < TOTAL_COUNT; i++) { hello.invoke(obj); } System.out.println(String.format("优化后的反射方法调用执行时间:%d" , System.currentTimeMillis() - start)); }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

传统调用方法执行时间:19
反射方法调用执行时间:112
优化后的反射方法调用执行时间:50

8、反射的基本使用例子

  • java.lang.Class类
方法名 作用
getName 获取全类名
getSimpleName 获取简单类名
getFields 获取所有public修饰的属性、包括本类以及父类的
getDeclaredFields 获取本类中所有的属性
getMethods 获取所有的public修饰的方法,包括本类以及父类的
getDeclaredMethod 获取本类中所有方法
getConstructors 获取所有public修饰的构造器,只有本类
getDeclaredConstructors 获取本类中所有构造器
getPackage Package形式返回 包信息
getSuperClass Class形式返回父信息
getInterfaces Class[]形式返回父接口信息
getAnnotations Annotation[]形式返回注解信息
String allClassName = "com.example.core.example.reflection.domain.User";
// 通过全类名获取class对象
Class<?> cls = Class.forName(allClassName);
System.out.println(cls);
// 通过对象获取class
System.out.println(cls.getClass());
// 获取全类名
System.out.println(cls.getName());
// 获取包名
System.out.println(cls.getClass().getPackage().getName());
// 获取对象实例
Object obj = cls.newInstance();
System.out.println(obj);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • java.lang.reflect.Field类
方法名 作用
getModifiers 以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getType 以Class形式返回类型
getName 返回属性名称
// 获取类属性
Field field = cls.getField("username");
field.set(obj , "admin");
System.out.println(field.get(obj));
// 获取所有类属性,private的成员变量没有权限访问
Field[] fields = cls.getFields();
for (Field field1 : fields) { System.out.println(field1.get(obj));
}
// 获取所有类属性包括private成员变量
Field[] allFields = cls.getDeclaredFields();
for (Field afield : allFields) { // 开放权限,私有的成员变量也能打印出来 afield.setAccessible(true); System.out.println(afield.get(obj));
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • java.lang.reflect.Method类
方法名 作用
getModifiers 以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getName 返回方法名
getReturnType 以class形式返回类型
getParmeterTypes 以Class[] 返回参数类型数组
// 获取class方法,同样默认情况不能获取private的方法
Method method = cls.getMethod("hello");
System.out.println(method.invoke(obj));

  
 
  • 1
  • 2
  • 3
  • java.lang.reflect.Constructor类
方法名 作用
getModifiers 以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getName 返回方法名
getParmeterTypes 以Class[] 返回参数类型数组
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
System.out.println(obj);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

9、反射开放权限操作

在我们使用Java反射获取class的private成员变量或者方法时,这种情况是不允许获取的,不过可以通过开放权限的方式来处理,通过设置setAccessible(true);即可


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * <pre>
 * 开放java反射权限,允许调用类的private属性或者方法
 * </pre>
 * <p>
 * <pre>
 * @author mazq
 * 修改记录
 * 修改后版本: 修改人:  修改日期: 2021/08/09 19:10  修改内容:
 * </pre>
 */
public class AccessibleReflectionExample { public static void main(String[] args) throws Exception{ test(); } public static void test() throws Exception{ // 创建class实例对象 Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User"); Constructor<?> con = cls.getDeclaredConstructor(); con.setAccessible(true); Object obj = con.newInstance(); // 获取私有成员变量 Field pwd = cls.getDeclaredField("password"); // 开放私有变量的访问权限 pwd.setAccessible(true); pwd.set(obj , "123456"); // 私有方法调用 Method method = cls.getDeclaredMethod("priToString"); method.setAccessible(true); System.out.println(method.invoke(obj)); }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

Exception in thread “main” java.lang.IllegalAccessException: Class com.example.core.example.reflection.AccessibleReflectionExample can not access a member of class com.example.core.example.reflection.domain.User with modifiers “private”
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)

文章来源: blog.csdn.net,作者:smileNicky,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/u014427391/article/details/119518788

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。