一篇文章彻底弄懂 Java 反射的使用
文章引用自:一篇文章彻底弄懂 Java 反射的使用
原文链接:https://zhuanlan.zhihu.com/p/378412723
说到Java反射
,必须先把 Java 的字节码搞明白了,也就是 Class
, 大 Class
在之前的文章中,我们知道了Java的大Class
就是类的字节码,就是一个普通的类,里面保存的是类的信息,还不太明白Java的大Class
的,可以先看一下之前的文章 一篇文章彻底搞懂Java的大Class到底是什么
先想一个问题
1. 给我们一个类,我们如何使用?
这还不简单,通过这个类,创建一个类的对象,再通过这个对象,调用类的方法或者属性
比如有一个类叫 Student
, 里面有一个 name
字段和一个 age
字段,还有3个方法, 源码如下:
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 | package com.model; public class Student { private String name; private int age; public Student(){ } public Student(String name, int age){ this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } public void show(){ System.out.println( "name=" + this .name + " age=" + this .age); } } |
上面的代码很简单,应该都能看懂,我们以这个Student
类来实验
回到上面的问题:如何使用这个类? ,代码如下:
1 2 3 4 5 6 7 8 9 | //1 创建一个对象 Student s = new Student(); //2 调用对象的方法 s.setName( "李雷" ); s.setAge( 23 ); //3 打印一下 s.show(); |
打印的结果下: name=李雷 age=23
上面就是和 反射
相反的通过正常的方式创建一个类的对象,然后通过对象调用类的方法
其实我们还可以根据类的字节码来创建对象,然后调用类的方法 也就是通过某个类的 Class ,来创建对象,然后调用类的方法
2. 如何获取类的 Class 呢?
有3
个方法,以Student
为例,演示如下:
第一种:通过 Class.forName("com.model.Student") 来获取Student的 Class
代码如下:
1 | Class cls = Class.forName( "com.model.Student" ); |
第二种:通过 Student.class
1 | Class cls = Student. class |
第三种:通过类的对象来获取,调用类的对象的 getClass()方法
1 2 | Student s = new Student(); Class cls = s.getClass() |
以上就是三种方法获取一个类的 Class
的方法,必须要牢记,尤其是前 2 个,用的最多
3. 如何通过Class
来创建对象,进而来调用类的方法或者属性呢?
- 第一步:获取类的 Class 对象
- 第二步:获取对应的方法的字节码
Method
以及 构造函数的字节码Constructor
,或者字段的字节码Field
- 第三步:通过
Constructor
的newInstance()
方法生成一个类的对象 - 第四步:通过调用
Method
的invoke()
方法完成调用类的代码
代码演示如下:
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 | //第一步:获取Student的 Class 对象,即Student的字节码 Class cls = Class.forName( "com.model.Student" ); //第二步:获取无参的构造方法的字节码,当然也可以获取有参的 Constructor constructor = cls.getConstructor(); //第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj Object obj = constructor.newInstance(); //第四步:获取 setName 方法的字节码,注意参数传方法的名字以及方法中参数的字节码 // 获取了setName的字节码 method,调用方法必须要有一个对象,所以上面的obj对象就是用来此处的 // 一定要传进行 Method method = cls.getMethod( "setName" , String. class ); method.invoke(obj, "待兔" ); //和上面类似,只不过这次 getMethod 的第二个参数传的是 int.class //因为第二个参数是int类型 Method method1 = cls.getMethod( "setAge" , int . class ); method1.invoke(obj, 23 ); //和上面类似 ,只不过 show()方法是无参的,所以 getMethod 只需要传方法的名字"show" 即可 Method showMethod = cls.getMethod( "show" ); //最后:调用showMethod方法,通过调用showMethod的invoke方法,里面传入前面创建的obj对象 //就达到了调用对象的show方法 showMethod.invoke(obj); |
通过上面的代码演示可以看出,在不知道 Student
类型的情况下,我们只需要知道 Student
类的全类名(包名+类名) 也就是com.model.Student
,就可以获取到 Student
类的
和直接通过 Student s = new Student(); s.show();
这种方法不一样的是,上面是在运行时通过字符串值知道要运行的类是com.model.Student
所以,反射就是在运行的时候 ,才知道这个类是什么,并且可以在运行的时候 ,获取这个类的完整信息,并调用对应的方法
4. 常用的反射API
4.1 在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象
获取Class对象有三种方法,上面已经讲过,这里再次贴出来,加深印象
- 使用 Class.forName 静态方法,前提是你知道类的全类名
Class cls = Class.forName("com.model.Student");
,其实这种方法就是加载类的 - 使用类的 .class 方法
Class cls = Student.class
不过这种方法,只适合在编译时就知道操作的 Class - 使用类对象的 getClass() 方法。
Student s = new Student();
Class cls = s.getClass()
4.2 获取所有类的的方法
可以通过 Class对象 getMethods()或者 cls.getDeclaredMethods() 来获取所有的方法的字节码
两者的区别是:getMethods()获取的方法包括父类的,getDeclaredMethods() 获取的是子类的
演示 getMethods()
1 2 3 4 | Method[] methods = cls.getMethods(); for (Method m : methods) { System.out.println(m.getName()); } |
输出出下:
getName setName setAge show getAge wait wait wait equals toString hashCode getClass notify notifyAll
可以看到,输出了很多父类中的方法(Object类中的方法)
再来看一下 getDeclaredMethods() 方法
1 2 3 4 | Method[] methods = cls.getDeclaredMethods(); for (Method m : methods) { System.out.println(m.getName()); } |
输出如下:
getName setName setAge show getAge
可以看到,只有子类自己的方法,并没有父类的方法
5. 通过反射创建类的对象需要注意的点
上面我们通过 Constructor
对象的newInstance()
方法,来创建对象 其实还有一种方法,也可以使用 Class
对象的newInstance()
5.1 第一种:通过 Class 对象 newInstance()
方法
1 2 | Class cls = Class.forName( "com.model.Student" ); Student obj = (Student) cls.newInstance(); |
5.2 第二种:通过 Constructor 对象的 newInstance() 方法
1 2 3 4 5 6 7 8 | //第一步:获取Student的 Class 对象,即Student的字节码 Class cls = Class.forName( "com.model.Student" ); //第二步:获取无参的构造方法的字节码,当然也可以获取有参的 Constructor constructor = cls.getConstructor(); //第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj Object obj = constructor.newInstance(); |
::: warning 通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。 下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。 :::
1 2 3 | Class cls = Class.forName( "com.model.Student" ); Constructor constructor = cls.getConstructor(String. class , int . class ); Object obj = constructor.newInstance( "tom" , 23 ); |
通过上面的讲解,应该对反射的用法有了个大致的了解了,Class有很多方法,感兴趣的可以自己写个helloworld
调试一下 不过怎么说,还是要先弄明白 Class
到底是什么,知道了 Class
的本质 ,再来看反射,就很容易了。
出处:http://www.cnblogs.com/lingyejun/
若本文如对您有帮助,不妨点击一下右下角的【推荐】。
如果您喜欢或希望看到更多我的文章,可扫描二维码关注我的微信公众号《翎野君》。
转载文章请务必保留出处和署名,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现