只有光头才能变强。html
文本已收录至个人GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3yjava
今天来简单写一下Java的反射。原本没打算写反射这个知识点的,只是很多的读者都问过我:“你的知识点好像缺了反射阿。能不能补一下?”mysql
这周末也有点空了,因此来写写我对反射的简单理解。这篇是入门文章,没有高深的知识点,但愿能对新人有帮助。若是文章有错的地方,麻烦在评论区友善评论指出~git
Java经常使用和重要的知识点我都写过(如今已有200+篇技术原创),若是想看的同窗,不妨关注个人GitHub,便可获取个人全部原创文章。程序员
在学习Java基础的时候,通常都会学过反射。我在初学反射的时候,并不能理解反射是用来干吗的。学了一些API发现:“明明我本身能直接new一个对象,为何它要绕一个圈子,先拿到Class对象,再调用Class对象的方法来建立对象呢,这不是多余吗?”github
相信不少人在初学反射的时候也都会有这个想法(我就不相信就只有我一我的这么蠢!!)sql
并且在搜索相关资料的时候,通常也仅仅是讲解反射的一系列API,始终是不了解反射到底是有什么用,这篇文章来告诉你吧。以为不错,给我点个赞呗数据库
首先咱们来看一段代码:app
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教程在网上有很是很是多,也足够通俗易懂了。在入门的时候,其实掌握如下几种也差很少了:
/* 下面是我初学反射时作的笔记,应该能够帮到你们,代码我就不贴了。(Java3y你值得关注) */ 想要使用反射,我先要获得class文件对象,其实也就是获得Class类的对象 Class类主要API: 成员变量 - Field 成员方法 - Constructor 构造方法 - Method 获取class文件对象的方式: 1:Object类的getClass()方法 2:数据类型的静态属性class 3: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的时候其实我我的认为仍是比较难理解为何须要反射的,由于没有必定的代码量下,很难理解为何我要绕一个圈子去搞反射这一套。
我如今认为用反射主要有两个缘由:
我一直在文章中都在强调,学某一项技术以前,必定要理解为何要学这项技术,因此个人文章通常会花比较长的幅度上讲为何。
下面我来举几个例子来帮助你们理解
相信你们都写过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的例子,就是经过反射来加载驱动的。
相信你们学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能作到?其实就是经过反射来作的。
相信你也有过的经历:
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() { @Override public 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」。
在公众号下回复「888」便可获取!!
本已收录至个人GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y
求点赞 求关注️ 求分享👥 求留言💬 对我来讲真的 很是有用!!!