Java反射机制基础详解
作者:smileNicky 发布时间:2023-07-17 04:36:29
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 + '\'' +
'}';
}
}
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));
}
2、反射机制原理
Java反射是Java实现动态语言的关键,也就是通过反射实现类动态加载
静态加载: 在编译时加载相关的类,如果找不到类就会报错,依赖性比较强动态加载:在运行时加载需要的类,在项目跑起来之后,调用才会报错,降低了依赖性
例子:静态加载,如下代码,如果找不到类的情况,代码编译都不通过
User user = new User();
而动态加载,就是反射的情况,是可以先编译通过的,然后在调用代码时候,也就是运行时才会报错
Class<?> cls = Class.forName("com.example.core.example.reflection.User");
Object obj = cls.newInstance();
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();
打个断点,debug进行看看源码,可以看到传统的直接new一个对象也是通过类加载器loadClass拿到的
java反射方式,通用通过调试看看:
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
同样本质也是通过ClassLoad再通过类的全类名
3.2、Class类对象的获取方法 Class.forname()
Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
应用场景:多用于读取类全路径,加载类
具体类.class
已经知道具体类的情况,通过具体类的class属性获取
Class u = User.class;
应用场景:多用于用于参数传递
对象.getClass
已经创建好对象的情况,直接通过对象实例获取class对象
Class cls = user.getClass();
应用场景:通过创建好的对象,获取Class对象
ClassLoader获取
ClassLoader cl = user.getClass().getClassLoader();
Class cls = cl.loadClass("com.example.core.example.reflection.domain.User");
基本数据类型
Class cls = int.class;
包装类
基本数据类型对应的包装类可以通过.TYPE得到Class类对象
Class cls = Integer.TYPE;
3.3 、可以获取Class对象的类型
1、外部类,成员内部类,静态内部类,局部内部类,匿名内部类
2、interface:接口
3、数组
4、enum:枚举
5、annotation:注解
6、基本数据类型
7、void8、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);
}
}
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));
}
}
传统调用方法执行时间: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);
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));
}
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));
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);
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));
}
}
来源:https://blog.csdn.net/u014427391/article/details/119518788


猜你喜欢
- 注:代码已托管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,项目是myb
- 左值右值定义:左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式).int
- 本文实例讲述了C#文件和字节流的转换方法。分享给大家供大家参考。具体实现方法如下:1、读取文件,并转换为字节流FileStream fs =
- 第一个Lambda表达式在Lambda出现之前,如果我们需要写一个多线程可能需要下面这种方式:Runnable runnable = new
- Android中Uri和Path之间的转换原因调用系统拍照应用,拍照后要保存图片,那么我们需要指定一个存储图片路径的Uri。这就涉及到如何将
- 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongodb.co
- 本文实例讲述了C#不重复输出一个数组中所有元素的方法。分享给大家供大家参考。具体如下:1.算法描述0)输入合法性校验1)建立临时数组:与原数
- C和指针相关基础知识:内存的分配(谭浩强版)1、整型变量的地址与浮点型/字符型变量的地址区别?(整型变量/浮点型变量的区别是什么)2、int
- Jetty和tomcat的比较Tomcat和Jetty都是一种Servlet引擎,他们都支持标准的servlet规范和JavaEE的规范。架
- JWT可以理解为一个加密的字符串,里面由三部分组成:头部(Header)、负载(Payload)、签名(signature)由base64加
- 前言本篇文章讲的是Kotlin 自定义view之实现标尺控件Ruler,以选择身高、体重等。开发中,当我们需要获取用户的身高和体重等信息时,
- 1. java 位掩码java 位掩码,在java开发中很少有场景会用到掩码,但是当系统中需要判断某个对象是否有 某些权限时,可以通过位掩码
- 首先看一看什么是装箱和拆箱?简单的来说:装箱就是值类型转换为引用类型;拆箱就是引用类型转换为值类型。值类型,包括原类型(Sbyte、Byte
- 静默安装就是偷偷的把一个应用安装到手机上,就是屏蔽确认框,通过反射只能写个主要的代码,这个是在linux编译用到,因为静默安装需要调用系统服
- springboot开启事务很简单,只需要一个注解@Transactional 就可以了。因为在springboot中已经默认对jpa、jd
- 应朋友们反馈的Android基础薄弱的问题,决定出一套Android基础教程,帮助大家复习,巩固Android基础,今天要讲的是Androi
- 背景产品想对多次快速点击做一下优化,想要的效果就是双击不会打开多次但是从开发角度来说,我可以用kotlin的拓展方法来调整这个,但是之前的历
- 一、简介图像直方图的反向投影是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。当我们已知图像中某个物体的大体位置时,可以通过概率分
- 本文实例分析了Android多线程。分享给大家供大家参考,具体如下:在Android下面也有多线程的概念,在C/C++中,子线程可以是一个函
- Invoke Phing targets这个插件主要是读取xml形式包括自动化测试打包部署的配置文件,然后根据流程走下来。用ph