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 的依赖注入就离不开反射。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367663.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EF Core利用Scaffold从根据数据库生成代码

在EF6 之前的时代&#xff0c;如果需要从数据库中生成代码&#xff0c;是可以直接在界面上操作的&#xff0c;而到了EF Core的时代&#xff0c;操作方式又有更简便的方式了&#xff0c;我们只需要记住以下这条指令。 Scaffold-DbContext "Server服务器地址;Database数据库…

如何通过CSS开启硬件加速来提高网站性能

你知道我们可以在浏览器中用css开启硬件加速&#xff0c;使GPU (Graphics Processing Unit) 发挥功能&#xff0c;从而提升性能吗&#xff1f; 现在大多数电脑的显卡都支持硬件加速。鉴于此&#xff0c;我们可以发挥GPU的力量&#xff0c;从而使我们的网站或应用表现的更为流畅…

Spring Security应用程序中的su和sudo

很久以前&#xff0c;我从事的项目具有很强大的功能。 有两个角色&#xff1a;用户和主管。 主管可以以任何方式更改系统中的任何文档&#xff0c;而用户则更受工作流约束的限制。 当普通用户对当前正在编辑和存储在HTTP会话中的文档有疑问时&#xff0c;主管可以介入&#xff…

示例介绍:JavaFX 8打印

我有一段时间没有写博客了&#xff0c;我想与其他人分享有关JavaFX的所有信息&#xff08;我的日常工作和家庭可能是借口&#xff09;。 对于那些是本博客的新手&#xff0c;我是JavaFX 2 Introduction by Example&#xff08;JIBE&#xff09;的作者&#xff0c; Java 7 Recip…

placeholder的使用

1.定义 placeholder 属性提供可描述输入字段预期值的提示信息 该提示会在输入字段为空时显示&#xff0c;并会在字段获得焦点时消失。 注释&#xff1a;placeholder 属性适用于以下的 <input> 类型&#xff1a;text, search, url, telephone, email 以及 password。 2.用…

字符串练习

字符串练习&#xff1a; http://news.gzcc.cn/html/2017/xiaoyuanxinwen_1027/8443.html 取得校园新闻的编号 trhttp://news.gzcc.cn/html/2017/xiaoyuanxinwen_1027/8443.html print(a[-14:-5])https://docs.python.org/3/library/turtle.html 产生python文档的网址 trhttps:/…

CSS清除行内元素之间的HTML空白

至今我还记得年轻是在IE6上开发的那些苦逼日子,特别希望IE浏览器采用 inline-block 的显示方式.行内块(inline-block)是非常有用的,特别是想要不用block和float来控制这些行内元素的margin,padding之时。问题来了,HTML源码中行内元素之间的空白有时候显示在屏幕上那是相当的讨厌…

int64 java_为什么json 不能使用 int64类型

json 简介jsON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集 。 JSON采用完全独立于语言的文本格式&#xff0…

Spring MVC自定义验证注释

在上一教程中&#xff0c;我展示了如何使用注释来验证表单 。 这对于简单的验证非常有用&#xff0c;但是最终&#xff0c;您需要验证一些现成的注释中没有的自定义规则。 例如&#xff0c;如果您需要根据输入的出生日期来验证用户已超过21岁&#xff0c;或者可能需要验证用户的…

Best Time to Buy and Sell Stock with Cooldown

https://soulmachine.gitbooks.io/algorithm-essentials/java/dp/best-time-to-buy-and-sell-stock-with-cooldown.html转载于:https://www.cnblogs.com/ZhiHao-queue/p/9521933.html

前期

转载于:https://www.cnblogs.com/joker157/p/8618091.html

解决IE8下body{ overflow:hidden;}无效的解决办法

css中IE8 body{ overflow:hidden;}无效的解决办法&#xff1a; 在页面html中使用: body{ overflow:hidden; } 在ie8下无效 &#xff0c;仍然有滚动条。 解决的办法如下&#xff1a; 替换为如下: html { overflow:hidden; } 这样就可以实现隐藏滚动条了 而且兼容目前所有的浏览器…

0基础能学mysql数据库吗_mysql学习入门:零基础如何使用mysql创建数据库表?

零基础如何自学Mysql创建数据库&#xff0c;是Mysql学习者必经之路&#xff0c;Mysql是受欢迎的关系数据库管理系统,WEB应用方面MySQL是很好的RDBMS应用软件之一。如何使用Mysql创建数据库表&#xff0c;打开Mysql学习进阶大门&#xff0c;就是今天MYSQL学习教程丁光辉博客认为…

使用ANTLR和Java创建外部DSL

在以前的一段时间里&#xff0c;我曾写过有关使用Java的内部DSL的文章。 在Martin Fowler撰写的《 领域特定语言 》一书中&#xff0c;他讨论了另一种称为外部DSL的DSL&#xff0c;其中DSL是用另一种语言编写的&#xff0c;然后由宿主语言进行解析以填充语义模型。 在前面的示…

vue跨域解决及打包

打包之前需要修改如下配置文件&#xff1a; 配置文件一&#xff1a;build>>>utils.js (修改publicPath:"../../" , 这样写是处理打包后找不到静态文件&#xff08;图片路径失效&#xff09;的问题) 配置文件二&#xff1a;config>>>index.js(修改a…

8. Oracle 联机重做日志文件(ONLINE LOG FILE)

转载自&#xff1a;http://blog.csdn.net/leshami/article/details/5749556 一、Oracle中的几类日志文件 Redo log files -->联机重做日志 Archive log files -->归档日志 Alert log files -->告警日志 Trace files -->跟踪日志 user_dump_…

Bootstrap中实现图片圆角效果

Bootstrap 对图片的支持。Bootstrap 提供了三个可对图片应用简单样式的 class&#xff1a; .img-rounded&#xff1a;添加 border-radius:6px 来获得图片圆角。.img-circle&#xff1a;添加 border-radius:500px 来让整个图片变成圆形。.img-thumbnail&#xff1a;添加一些内边…

java 唯一索引冲突_JPA merge联合唯一索引无效问题解决方案

问题JPA的merge()操作 是合并的意思&#xff0c;就是当保存的实体时&#xff0c;根据主键id划分&#xff0c;如果已存在&#xff0c;那么就是更新操作&#xff0c;如果不存在&#xff0c;就是新增操作但是这个仅针对 主键id 划分&#xff0c;对联合唯一索引 无效&#xff0c;两…

Spring MVC测试框架入门–第1部分

最新推出的主要Spring框架是Spring MVC测试框架&#xff0c;Spring Guys声称它是“一流的JUnit支持&#xff0c;可通过流畅的API测试客户端和服务器端Spring MVC代码” 1 。 在这个博客以及下一个博客中&#xff0c;我将看一看Spring的MVC测试框架&#xff0c;并将其应用于我现…

metaclass

用metaclass来指定类C的元类是MyTypeclass MyType:def __init__(cls, *args, **kwargs):print(here!)#由于metaclassMyType&#xff0c;所以执行到这一步的时候&#xff0c;会调用MyType的构造函数 class C(metaclassMyType):def __init__(self):pass对象后面跟()&#xff0c;是…