前言

只有光头才能变强。文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y

今天来简单写一下Java的反射。本来没打算写反射这个知识点的,只是不少的读者都问过我:“你的知识点好像缺了反射阿。能不能补一下?”

这周末也有点空了,所以来写写我对反射的简单理解。这篇是入门文章,没有高深的知识点,希望能对新人有帮助。如果文章有错的地方,麻烦在评论区友善评论指出~

Java常用和重要的知识点我都写过(现在已有200+篇技术原创),如果想看的同学,不妨关注我的GitHub,即可获取我的所有原创文章。

一、序言

在学习Java基础的时候,一般都会学过反射。我在初学反射的时候,并不能理解反射是用来干嘛的。学了一些API发现:“明明我自己能直接new一个对象,为什么它要绕一个圈子,先拿到Class对象,再调用Class对象的方法来创建对象呢,这不是多余吗?

相信很多人在初学反射的时候也都会有这个想法(我就不相信就只有我一个人这么蠢!!)

而且在搜索相关资料的时候,一般也仅仅是讲解反射的一系列API,始终是不了解反射究竟是有什么用,这篇文章来告诉你吧。觉得不错,给我点个赞呗

二、引出Class对象

首先我们来看一段代码:

public class Demo {// 自建了一个Student类class Student{}public static void main(String[] args) {// 将Object 强转成Student类Object o = new Object();Student s = (Student) o;}
}

我们在IDE编写这一段代码的时候,不会出现任何的错误。但是等我们执行的时候,我们会知道这肯定强转失败了

那么“Java”(实质上JVM)是怎么知道我们写的强转有没有问题的呢?可以依赖Class对象来协助判断。

如果看过我写JVM的那篇文章的同学应该都知道一个对象的加载过程,如果没看过的同学可以再去看看,顺便在这里给大家复习一下:

  • 一个.java的文件经过javac命令编译成功后,得到一个.class的文件
  • 当我们执行了初始化操作(有可能是new、有可能是子类初始化 父类也一同被初始化、也有可能是反射...等),会将.class文件通过类加载器装载到jvm
  • .class文件加载器加载到jvm中,又分了好几个步骤,其中包括 加载、连接和初始化
  • 其中在加载的时候,会在Java堆中创建一个java.lang.Class类的对象,这个Class对象代表着类相关的信息

既然说,Class对象代表着类相关的信息,那说明只要类有什么东西,在Class对象我都能找得到。我们打开IDE看看里边的方法:

于是我们可以通过Class对象来判断对象的真正类型

三、反射介绍

其实反射就是围绕着Class对象和java.lang.reflect类库来学习,就是各种的API

比如上面截图的Method/Field/Constructor这些都是在java.lang.reflect类库下,正是因为这些类库的学习并不难,所以我才一直没写反射的文章。

我并不是说这些API我都能记住,只是这些API教程在网上有非常非常多,也足够通俗易懂了。在入门的时候,其实掌握以下几种也差不多了:

  • 知道获取Class对象的几种途径
  • 通过Class对象创建出对象,获取出构造器,成员变量,方法
  • 通过反射的API修改成员变量的值,调用方法
/*下面是我初学反射时做的笔记,应该可以帮到大家,代码我就不贴了。(Java3y你值得关注)
*/
想要使用反射,我先要得到class文件对象,其实也就是得到Class类的对象
Class类主要API:成员变量  - Field成员方法  - Constructor构造方法  - Method
获取class文件对象的方式:1:Object类的getClass()方法2:数据类型的静态属性class3:Class类中的静态方法:public static Class ForName(String className)
--------------------------------
获取成员变量并使用1: 获取Class对象2:通过Class对象获取Constructor对象3:Object obj = Constructor.newInstance()创建对象4:Field field = Class.getField("指定变量名")获取单个成员变量对象5:field.set(obj,"") 为obj对象的field字段赋值
如果需要访问私有或者默认修饰的成员变量1:Class.getDeclaredField()获取该成员变量对象2:setAccessible() 暴力访问
---------------------------------
通过反射调用成员方法1:获取Class对象2:通过Class对象获取Constructor对象3:Constructor.newInstance()创建对象4:通过Class对象获取Method对象  ------getMethod("方法名");5: Method对象调用invoke方法实现功能
如果调用的是私有方法那么需要暴力访问1: getDeclaredMethod()2: setAccessiable();

相信我,去搜索引擎看一会,你就学会了。反射的API并不难学,一般人学不懂反射因为不知道反射究竟能干什么,下面我来讲讲我的讲解。

四、为什么需要反射

在初学Java的时候其实我个人认为还是比较难理解为什么需要反射的,因为没有一定的代码量下,很难理解为什么我要绕一个圈子去搞反射这一套。

我现在认为用反射主要有两个原因:

  • 提高程序的灵活性
  • 屏蔽掉实现的细节,让使用者更加方便好用

我一直在文章中都在强调,学某一项技术之前,一定要理解为什么要学这项技术,所以我的文章一般会花比较长的幅度上讲为什么。

下面我来举几个例子来帮助大家理解

4.1 案例一(JDBC)

相信大家都写过jdbc的代码,我贴一小段,大家回顾一下:

Class.forName("com.mysql.jdbc.Driver");//获取与数据库连接的对象-Connetcion
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java3y", "root", "root");//获取执行sql语句的statement对象
statement = connection.createStatement();//执行sql语句,拿到结果集
resultSet = statement.executeQuery("SELECT * FROM users");

后来为什么要变成下面这种形式呢?

//获取配置文件的读入流
InputStream inputStream = UtilsDemo.class.getClassLoader().getResourceAsStream("db.properties");Properties properties = new Properties();
properties.load(inputStream);//获取配置文件的信息
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");//加载驱动类
Class.forName(driver);

理由很简单,人们不想修改代码。只要存在有变动的地方,我写在配置里边,不香吗?但凡有一天,我的username,password,url甚至是数据库都改了,我都能够通过修改配置的方式去实现。

不需要动我丝毫的代码,改下配置就完事了,这就能提供程序的灵活性。

有人可能会问:“那还是要改啊,我改代码也很快啊,你改配置不也是要改吗”。

其实不一样的,我举个例子:

  • 三歪写了一个JDBC组件,把各种配置都写死在代码上,比如上面的driver/username/数据库连接数等等。现在三歪不干了,要跑路了。
  • 敖丙来接手三歪的代码,敖丙刚开始接手项目,公司说要换数据库。敖丙给领导说:这玩意,我改改配置就好了,几分钟完事。
  • 敖丙找了半天都没找到配置的地方,由于三歪写的代码又臭又烂,找了半天才找到入口和对应的位置。

改代码的风险要比改配置大,即便不知道代码的实现都能通过改配置来完成要做的事。

这种就能通过可配的,其内部很可能就是通过反射来做的。

这里只是说可能,但不全是。有的可配的参数可能就仅仅只是配置,跟反射无关。但上面jdbc的例子,就是通过反射来加载驱动的。

4.2 案例二(SpringMVC)

相信大家学SpringMVC之前都学过Servlet的吧,如果没学过,建议看我的文章再复复习。

我当时学MVC框架的时候给我带来印象最深的是什么,本来需要各种getParameter(),现在只要通过约定好JavaBean的字段名,就能把值填充进去了。

还是上代码吧,这是我们当时学Servlet的现状:

//通过html的name属性,获取到值
String username = request.getParameter("username");
String password = request.getParameter("password");
String gender = request.getParameter("gender");//复选框和下拉框有多个值,获取到多个值
String[] hobbies = request.getParameterValues("hobbies");
String[] address = request.getParameterValues("address");//获取到文本域的值
String description = request.getParameter("textarea");//得到隐藏域的值
String hiddenValue = request.getParameter("aaa");

我们学到SpringMVC的时候是怎么样的:

@RequestMapping(value = "/save")
@ResponseBody
public String taskSave(PushConfig pushConfig) {// 直接使用  String name= pushConfig.getName();
}

为什么SpringMVC能做到?其实就是通过反射来做的。

相信你也有过的经历:

  • 如果你的JavaBean的属性名跟传递过来的参数名不一致,那就“自动组装”失败了。因为反射只能根据参数名去找字段名,如果不一致,那肯定set不进去了。所以就组装失败了呀~

如果在使用框架的时候,为什么我们往往写上JavaBean,保持字段名与参数名相同,就能“自动”得到对应的值呢。这就是反射的好处。

屏蔽掉实现的细节,让使用者更加方便好用

五、我们写反射的代码多吗?

大部分程序员都是写业务代码的,大部分程序员都是维护老系统的,其实要我们自己写反射的代码的时候,真的不多。

从上面也看出,什么时候会写反射?写我们自己组件/框架的时候。如果想找个地练手一下反射,我觉得自定义注解是一个不错的选择。

因为现在用注解的地方很多,主要是够清晰简单(再也不用对着一堆的XML文件了,哈哈哈哈~)。

我初学的时候写过一段,可以简单参考一下,思路都差不多的哈。下面是使用的效果(使用自定义注解给不同的接口增加权限)

@permission("添加分类")
/*添加分类*/ void addCategory(Category category);/*查找分类*/
void findCategory(String id);@permission("查找分类")
/*查看分类*/ List<Category> getAllCategory();

返回一个代理的Service对象来处理自定义注解:

public class ServiceDaoFactory {private static final ServiceDaoFactory factory = new ServiceDaoFactory();private ServiceDaoFactory() {}public static ServiceDaoFactory getInstance() {return factory;}//需要判断该用户是否有权限public <T> T createDao(String className, Class<T> clazz, final User user) {System.out.println("添加分类进来了!");try {//得到该类的类型final T t = (T) Class.forName(className).newInstance();//返回一个动态代理对象出去return (T) Proxy.newProxyInstance(ServiceDaoFactory.class.getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, PrivilegeException {//判断用户调用的是什么方法String methodName = method.getName();System.out.println(methodName);//得到用户调用的真实方法,注意参数!!!Method method1 = t.getClass().getMethod(methodName,method.getParameterTypes());//查看方法上有没有注解permission permis = method1.getAnnotation(permission.class);//如果注解为空,那么表示该方法并不需要权限,直接调用方法即可if (permis == null) {return method.invoke(t, args);}//如果注解不为空,得到注解上的权限String privilege = permis.value();//设置权限【后面通过它来判断用户的权限有没有自己】Privilege p = new Privilege();p.setName(privilege);//到这里的时候,已经是需要权限了,那么判断用户是否登陆了if (user == null) {//这里抛出的异常是代理对象抛出的,sun公司会自动转换成运行期异常抛出,于是在Servlet上我们根据getCause()来判断是不是该异常,从而做出相对应的提示。throw new PrivilegeException("对不起请先登陆");}//执行到这里用户已经登陆了,判断用户有没有权限Method m = t.getClass().getMethod("findUserPrivilege", String.class);List<Privilege> list = (List<Privilege>) m.invoke(t, user.getId());//看下权限集合中有没有包含方法需要的权限。使用contains方法,在Privilege对象中需要重写hashCode和equals()if (!list.contains(p)) {//这里抛出的异常是代理对象抛出的,sun公司会自动转换成运行期异常抛出,于是在Servlet上我们根据getCause()来判断是不是该异常,从而做出相对应的提示。throw new PrivilegeException("您没有权限,请联系管理员!");}//执行到这里的时候,已经有权限了,所以可以放行了return method.invoke(t, args);}});} catch (Exception e) {new RuntimeException(e);}return null;}
}

最后

这篇反射跟网上的文章不太一样,网上的反射一般都是介绍反射的API如何使用。如果你觉得还不错,给我点赞吧 。想要看其他知识点的同学,可以给我留言,我可以酌情考虑写一下(哈哈哈哈,突然变大牌了)

这篇文章涉及到的其他知识点:JVM类的加载过程、注解、动态代理、SpringMVC、JDBC我都已经写过文章了,想要阅读的同学可以关注我的GitHub搜索相关关键字即可。

如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号「Java3y」。

本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y求点赞 求关注️ 求分享 求留言 对我来说真的 非常有用!!!

resultset需要关闭吗_Java程序员都需要懂的「反射」相关推荐

  1. Java程序员的典型工作过程有哪些_Java程序员都要经历哪些阶段

    原标题:Java程序员都要经历哪些阶段 程序员从菜鸟到优秀的过程是需要时间,经验,不断的进阶,小编和大家一起看看每个阶段都要经历哪些成长阵痛: 没有防卫 这个阶段很快就会过去,因为有无数的书本和&qu ...

  2. java cron表达式_Java 程序员都应该去使用一下这款强大的国产工具类库

    大家好,我是你们的导师, 我会坚持每天给大家整理一些干货内容(当然周末也要允许老师休息一下哈). 今天给大家分享一个很棒的Java工具类库:Hutool.可能有很多朋友已经知道这个类库了,甚至在已经在 ...

  3. 程序员都该懂的 UML 规范!

    作者 | 码匠笔记 责编 | 胡巍巍 在团队协作过程中最常见的就是开会.开会最常用的就是图,而图中最常见的就是流程图.时序图.类图,这三个图可以清楚的描述你想解释的内容. 学好类图不仅仅能帮助自己更清 ...

  4. 我敢说80% 的程序员都掉进了「老鼠赛跑」的陷阱

    loonggg 读完需要 2 分钟 速读仅需 1 分钟 大家好,我是你们的校长. 今天我给大家再次分享一下,我最近的一些读书的感想,思考起来,确实能够给自己带来一些真实的帮助和启发,希望大家在平时的工 ...

  5. 程序员都该懂点 HTTP

    作者:developerHaoz Github 地址:developerHaoz 说明:本文主要是对 HTTP 基础知识进行总结和归纳,毕竟做 IT 的,网络这一块还是很重要的 本文的主要内容 HTT ...

  6. Java程序员都要懂得知识点:反射

    本文分享自华为云社区<java知识点问题精选之反射>,原文作者:breakDraw . Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象, ...

  7. 程序员都需要懂的10种常见安全漏洞

    前言 我们日常开发中,很多小伙伴容易忽视安全漏洞问题,认为只要正常实现业务逻辑就可以了.其实,安全性才是最重要的.本文将跟大家一起学习常见的安全漏洞问题,希望对大家有帮助哈.如果本文有什么错误的话,希 ...

  8. Java 程序员都该懂的 HashMap

    HashMap 一直是非常常用的数据结构,也是面试中十分常问到的集合类型,今天就来说说 HashMap. 但是为什么要专门说明是 Java8 的 HashMap 呢?我们都知道,Java8 有很多大的 ...

  9. 据说:只有程序员才能看懂的图!

    欢迎关注专栏:Java架构技术进阶.里面有大量batj面试题集锦,还有各种技术分享,如有好文章也欢迎投稿哦. 「1」外包产品交付,给客户演示时 「2」与领导斗智斗勇,躲猫猫 「3」领导总是能识破程序员 ...

最新文章

  1. 20161124网络爬虫技术学习
  2. 希捷携全线企业级解决方案出席ODCC,Exos X18与Exos 2X14硬盘斩获两项大奖
  3. 运行从别处复制过来的linux可执行程序
  4. The connected J-Link is defective,Proper operation cannot be guaranteed......的解决办法
  5. linux ubuntu 安装samba ftp nfs tftp,Ubuntu配置TFTP和NFS和samba服务配置.doc
  6. hadoop环境搭建之伪分布集群环境搭建(单节点)
  7. 挺水的一门课,发现全系都过了,就自己挂了,这是一种什么样的感觉呢?
  8. Java笔试面试题二(常考问答)转
  9. 未来的C#之只读引用与结构体
  10. linux内核分析与移植,内核分析移植
  11. 洛谷 P1757 通天之分组背包
  12. C# DataGridView属性设置
  13. java汉字转拼音,pinyin4j简单介绍
  14. 史上最详细Proof处理教程--Elsevier平台(一)~
  15. 怎么设置计算机硬盘密码,移动硬盘如何设置密码【操作步骤】
  16. 幼儿园作业(毕业季)
  17. Wordpress用文件记录访客 IP和访问量
  18. android 西班牙_分析西班牙足球联赛(西甲)
  19. matlab可以仿真特殊电机,基于Matlab的异步电动机矢量控制系统的仿真研究
  20. 电脑任务栏消失解决办法

热门文章

  1. 全球IT公司100强排行榜出炉
  2. 技术人员的明天:35岁后我们做什么
  3. 总结一些C/C++的知识点
  4. 警惕、曝光一个不好的现象
  5. 傻子坐飞机问题的求解
  6. 从pheatmap无缝迁移至ComplexHeatmap
  7. Randomatic mac - AE随机摆动的字母图层效果脚本
  8. vue部署到服务器_利用Gulp实现代码自动化部署
  9. Python | Socket01 - 创建一个TCP服务器(阻塞+单线程),将TCP客户端发过来的字符串原路返回
  10. linux 下的csp 模块,linux kernel 中MIGRATE_TYPES的理解