java 反射 new class_Java高级特性-反射:不写死在代码,还怎么 new 对象?
反射是 Java 的一个高级特性,大量用在各种开源框架上。
在开源框架中,往往以同一套算法,来应对不同的数据结构。比如,Spring 的依赖注入,我们不用自己 new 对象了,这工作交给 Spring 去做。
然而,我们要 new 一个对象,就得写在代码上。但 Spring 肯定猜不到我们的类叫什么名字,那 Spring 又是怎么把对象给 new 出来的呢?
这就离不开反射。
反射的意义与作用
Java 有两种操作类的方式,分别是:非反射、反射。
先来说第一种方式,非反射。
非反射,就是根据代码,静态地操作类。比如,下面这段代码:public class Application {
public static void main(String[] args) {
// 创建一个用户对象
ClientUser client = new ClientUser();
}
}
这个 main() 方法很简单,就是创建一个用户对象。整个过程是这样的,在 JVM 运行前,你必须先想好要创建哪些对象,然后写在代码上,最后你运行 main() 方法,JVM 给你创建一个用户对象。
简单来说,你写好代码,扔给 JVM 运行,运行完就没了。
在这种情况下,程序员必须控制一切,创建什么对象得提前写死在代码上。比如,我要多创建一个商户对象,那就得改代码:public class Application {
public static void main(String[] args) {
// 创建一个用户对象
ClientUser client = new ClientUser();
// 创建一个商户对象
ShopUser shop = new ShopUser();
// 省略无数 new 操作
}
}
如果按照这种做法,只要需求一变,程序员就得改代码,工作效率很低。比如说,你碰上复杂些的项目,不光得创建对象,还得 set 成员变量。这样一来,每新加一个对象,你就得改一堆代码,迟早得累死。
那这些工作能简化吗?
这要用到第二种操作类的方式,反射。反射是一种动态操作类的机制。比如,我要创建一堆对象,那不用提前写死在代码,而是放在配置文件或者数据库上,等到程序运行的时候,再读取配置文件创建对象。
还是上面的代码,经过反射的改造,就变成这个样子:public class Application {
// 模拟配置文件
private static Set configs = new HashSet<>();
static {
configs.add("com.jiarupc.reflection.ShopUser");
configs.add("com.jiarupc.reflection.ClientUser");
// 省略无数配置
}
public static void main(String[] args) throws Exception {
// 读取配置文件
for (String config : configs) {
// 通过配置文件,获取类的Class对象
Class clazz = Class.forName(config);
// 创建对象
Object object = clazz.newInstance();
System.out.println(object);
}
}
}
当你运行 main() 方法的时候,程序会先读取配置文件,然后根据配置文件创建对象。用了反射后,你有没有发现,工作变轻松了。一旦新加对象,我们只要加一行配置文件,不用动其它地方。
看到这儿,你是不是想起某些开源框架?比如,Spring 的依赖注入。// 加上一行注解,Spring 就接管这个类的创建工作
@Service
public class UserService {
// 省略业务代码...
}
你在某个类上加一行注解(这相当于加一行配置),Spring 就帮你接管这个类,你不用操心怎么创建对象了。而 Spring 之所以能接管你这个类,是因为利用了 Java 的反射。
简单来说,我们如果用好反射,能减少大量重复的代码。
接下来,我们来看看反射能做什么吧~
反射获取类信息
如果你想操作一个类,那得知道这个类的信息。比如,有哪些变量,有哪些构造器,有哪些方法...没有这些信息,你连代码都没法写,更别谈反射了。
限于篇幅,我们主要讲怎么获取类的 Class 对象、成员变量、方法。
Class 对象只有 JVM 才能创建,里面有一个类的所有信息,包括:成员变量、方法、构造器等等。
换句话说,创建 Class 对象是 JVM 的事,我们不用管。但想通过反射来操作一个类,得先拿到这个类的 Class 对象,这有三种方式:1. Class.forName("类的全限定名")
2. 实例对象.getClass()
3. 类名.class
你可以看下面的代码:public class User {
public static void main(String[] args) throws Exception {
// 1. Class.forName("类的全限定名")
Class clazz1 = Class.forName("com.jiarupc.reflection.User");
// 2. 实例对象.getClass()
User user = new User();
Class clazz2 = user.getClass();
// 3. 类名.class
Class clazz3 = ClientUser.class;
}
}
当你通过这三种方式,拿到 Class 对象后,就可以用反射获取类的信息了。
Field 对象代表类的成员变量。我们想拿到一个类的成员变量,可以调用 Class 对象的四个方法。1. Field getField(String name) - 获取公共字段
2. Field[] getFields() - 获取所有公共字段
3. Field getDeclaredField(String name) - 获取成员变量
4. Field[] getDeclaredFields() - 获取所有成员变量
我们尝试下获取所有成员变量,代码逻辑是这样的:通过 User 类的全限定名,获取 Class 对象,然后调用 getDeclaredFields() 方法,拿到 User 类的全部成员变量,最后把变量名、类型输出到控制台。public class User {
// 唯一标识
private Long id;
// 用户名
private String username;
public static void main(String[] args) throws Exception {
// 获取类的 Class 对象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 获取类的所有成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String msg = String.format("变量名:%s, 类型:%s", field.getName(), field.getType());
System.out.println(msg);
}
}
}
Method 对象代表类的方法。要拿到一个类的方法,Class 对象同样提供了四个方法:1. Method getMethod(String name, Class[] params) - 通过方法名、传入参数,获取公共方法
2. Method[] getMethods() - 获取所有公共方法
3. Method getDeclaredMethod(String name, Class[] params) - 通过方法名、传入参数,获取任何方法
4. Method[] getDeclaredMethods() - 获取所有方法
同样的,我们尝试下获取所有方法,先通过 User 类的全限定名,获取 Class 对象,然后调用 getDeclaredMethods() 方法,拿到 User 类的全部成员方法,最后把方法名、形参数量输出到控制台。public class User {
// 唯一标识
private Long id;
// 用户名
private String username;
public static void main(String[] args) throws Exception {
// 获取类的 Class 对象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 获取类的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String msg = String.format("方法名:%s, 形参数量:%s", method.getName(), method.getParameterCount());
System.out.println(msg);
}
}
}
看到这儿,你应该能知道:怎么通过反射获取类的信息。
首先,获取类的 Class 对象有三种方式;然后,获取类的成员变量,这对应着 Field 对象;最后,获取类的方法,这对应着 Method 对象。
然而,反射不止能拿到类的信息,还能操作类。
反射操作类
反射能玩出很多花样,但我认为最重要的是:创建对象和调用方法。
创建对象是一切的前提。对反射来说,如果没有创建对象,那我们只能看看这个类的信息。比如,有什么成员变量,有什么方法之类的。而如果你想操作一个类,那么第一步就是创建对象。
你想要创建对象,必须调用类的构造器。这分为两种情况,最简单的是:你写了一个类,但没有写构造器,那这个类会自带一个无参的构造器,这就好办了。public class User {
// 唯一标识
private Long id;
// 用户名
private String username;
public static void main(String[] args) throws Exception {
// 获取类的 Class 对象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 创建对象
Object object = clazz.newInstance();
System.out.println(object);
}
}
我们先获取类的 Class 对象,然后调用 newInstance()。
但还有一种情况,我不用 Java 自带的构造器,而是自己写。这种情况会复杂一些,你得指定传入参数的类型,先拿到构造器,再调用 newInstance() 方法。public class User {
// 唯一标识
private Long id;
// 用户名
private String username;
// 构造器1
public User(Long id) {
this.id = id;
this.username = null;
}
// 构造器2
public User(Long id, String username) {
this.id = id;
this.username = username;
}
public static void main(String[] args) throws Exception {
// 获取类的 Class 对象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 通过传入参数,获取构造器,再创建对象
Constructor constructor = clazz.getConstructor(Long.class, String.class);
Object object = constructor.newInstance(1L, "jiarupc");
System.out.println(object);
}
}
我们要在一开始就设置 id 和 username,那么你得传入参数的类型,先找到构造器2-constructor;然后,传入 id 和 username 到 constructor.newInstance() 方法,就能得到一个用户对象。
当拿到构造器,并创建好对象后,我们就可以调用对象的方法了。
调用对象的方法分为两步:第一步,找到方法;第二步,调用方法。这听起来是非常简单,事实上也非常简单。你可以看下面的代码。public class User {
// 唯一标识
private Long id;
// 用户名
private String username;
// ..忽略 set/get 方法
public static void main(String[] args) throws Exception {
// 获取类的 Class 对象
Class clazz = Class.forName("com.jiarupc.reflection.User");
// 创建对象
Object object = clazz.newInstance();
System.out.println(object);
// 通过方法名、传入参数,找到方法-setUsername
Method method = clazz.getMethod("setUsername", String.class);
// 调用 object 对象的 setUsername() 方法
method.invoke(object, "JerryWu");
// 输出所有成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String msg = String.format("变量名:%s, 变量值:%s", field.getName(), field.get(object));
System.out.println(msg);
}
}
}
我们通过方法名-setUsername、参数类型-String,找到 setUsername 方法;然后,传入参数-username 到 method.invoke(),执行setUsername 方法;最后,输出所有成员变量,验证一下结果。
写在最后
反射是一种动态操作类的机制,它有两个用处。
第一个用处,通过反射,我们可以拿到一个类的信息,包括:成员变量、方法、构造器等等。
第二个用处,通过反射,我们可以操作一个类,包括:创建对象、调用对象的方法、修改对象的成员变量。
因为框架要以同一套算法,来应对不同的数据结构。所以,开源框架大量用到了反射。比如,Spring 的依赖注入就离不开反射。
java 反射 new class_Java高级特性-反射:不写死在代码,还怎么 new 对象?相关推荐
- Java 高级特性 --- 反射
From:Java 高级特性 --- 反射:https://www.jianshu.com/p/9be58ee20dee From:Java 基础之 --- 反射(非常重要):https://blog ...
- java数据导出ex_Java高级特性注解:注解实现Excel导出功能
作者:JerryWu来源:SegmentFault 思否社区 注解是 Java 的一个高级特性,Spring 更是以注解为基础,发展出一套"注解驱动编程". 这听起来高大上,但毕竟 ...
- 读书笔记之《深入理解Java虚拟机:JVM高级特性与最佳实践》
本篇带来的是周志明老师编写的<深入理解Java虚拟机:JVM高级特性与最佳实践>,十分硬核! 全书共分为 5 部分,围绕内存管理.执行子系统.程序编译与优化.高效并发等核心主题对JVM进行 ...
- 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)读书笔记
前言 我在读 深入理解java虚拟机 这本书,把整体其中的关键点标记了,希望自己对它有个不一样的理解,也希望大家能看看这本写的很好的书 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) pd ...
- Java核心技术卷2 高级特性 学习笔记(5)
参考:Java核心技术卷2 高级特性 第九章 Java技术提供了三种确保安全的机制: 语言设计特性(对数组的边界进行检查,无不受检查的类型转换,无指针算法等). 访问控制机制,用于控制代码能够执行的 ...
- 最新:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明
本书一共分为五个部分:走近Java.自动内存管理.虚拟机执行子系统.程序编译与代码优化. 高效并发.各个部分之间基本上是互相独立的,没有必然的前后依赖关系,读者可以从任何一个感兴 趣的专题开始阅读,但 ...
- 新书推荐 | Java核心技术 卷II 高级特性(原书第11版)
新书推荐 <Java核心技术 卷II 高级特性(原书第11版)> 长按二维码 了解及购买 全新第11版!针对Java SE9.10.11全面更新!Java领域极具影响力和价值的著作之一,与 ...
- Java高级特性——反射
定义 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射 ...
- 【Java书笔记】:《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》第2部分-自动内存管理,第3部分-虚拟机执行子系统,第5部分-高效并发
作者:周志明 整理者GitHub:https://github.com/starjuly/UnderstandingTheJVM 第2部分-自动内存管理 第2章 Java内存区域与内存溢出异常 2.2 ...
最新文章
- 基于Sentinel的Redis3.2高可用方案
- centos 日志审计_Linux\CentOS中auditd安全审计工具的使用
- redis list命令操作
- linux shell 提示符设置
- 常用的MySQL图形化管理软件
- mac java maven 设置
- python和sas哪个有用考研_金融学研究生用好计量软件的好处呢? stata sas s-plus python R哪个更有重要呢?...
- 他曾经负债2.5亿,如今身价超过500亿
- windows系统中创建线程常用哪几个函数,有什么区别?
- 计算机二级C选择题精华
- 企业网站DDOS防护解决方案
- 问题五:C++中const是干嘛用的
- FreeBSD NetBSD OpenBSD DragonFlyBSD 操作系统
- svchost.exe病毒-任务栏怎么也关闭不掉的搜索框
- JSON的生成和解析
- 【STM32】标准库 菜鸟入门 GPIO输入
- oracle查询三个月前的时间
- 小米4手机选择图库发送图片时崩溃
- 全球5G设备商最新排名
- Webpack 打包commonjs 和esmodule 模块的产物对比
热门文章
- python中文处理
- Twitter创始人Jack Dorsey的每日必做和不做清单
- python 优先队列_python中使用优先队列
- AUTOSAR从入门到精通100讲(十)-DoIP协议介绍
- C语言switch怎么算,超级新手,用switch写了个计算器程序,求指导
- php生成txt文件_php 批量生成html,txt文件的实现代码
- java的原生数据类型_Java中的8种原生数据类型(Primitive Data Types)分析
- Android killer软件闪退的解决办法?
- matlab工序能力分析,《MATLAB编程与系统仿真》课程考核说明
- java中 下列不合法的语句_在Java中,下列( )是不合法的赋值语句。_学小易找答案...