Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
java反射到底有什么用_翻正反射和状态反射的区别,希望能够帮助你!!!。
视频功能审核通过了,可以看视频啦!
建议一定要花十几分钟时间把视频看完,然后再结合博客里的内容来理解
看完后,相信对你了解Java中的反射一定会有所帮助!
注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了
记得点关注啊,视频里的wx二维码失效了,wx搜索:“聊5毛钱的java” 或 扫码关注公众号,欢迎一起学习交流
快扫码关注啦!关注可领取博主的Java学习视频+资料,保证都是干货
用最通俗易懂的话来说一说Java中的反射机制
Java中创建对象大概有这几种方式:
1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式
2、使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去
3、使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象
上边是Java中常见的创建对象的三种方式,其实除了上边的三种还有另外一种方式,就是接下来我们要讨论的 “反射”
反射就是把Java类中的各个组成部分进行解剖,并映射成一个个的Java对象,拿到这些对象后可以做一些事情。
既然说反射是解剖Java类中的各个组成部分,所以说咱们得知道一个类中有哪些部分?
例如,一个类有:构造方法,方法,成员变量(字段),等信息,利用反射技术咱们可以把这些组成部分映射成一个个对象
拿到映射后的构造方法,可以用它来生成对象;拿到映射后的方法,可以调用它来执行对应的方法;拿到映射后的字段,可以用它来获取或改变对应字段的值;
说完反射的概念后,咱们说一下反射能干什么?
一般来说反射是用来做框架的,或者说可以做一些抽象度比较高的底层代码,反射在日常的开发中用到的不多,但是咱们还必须搞懂它,因为搞懂了反射以后,可以帮助咱们理解框架的一些原理。所以说有一句很经典的话:反射是框架设计的灵魂。现在说完这个可能还不太能理解,不急,等下说完一个快速入门的例子后,应该会稍微有点感觉
刚才已经说过,反射是对一个类进行解剖,想解剖一个东西,前提是首先你得拿到这个东西,那么怎么得到咱们想解剖的类呢?
首先大家要明白一点,咱们写的代码是存储在后缀名是 .java的文件里的,但是它会被编译,最终真正去执行的是编译后的 .class文件。Java是面向对象的语言,一切皆对象,所以java认为 这些编译后的 class文件,这种事物也是一种对象,它也给抽象成了一种类,这个类就是Class,大家可以去AIP里看一下这个类
所以拿到这个类后,就相当于拿到了咱们想解剖的类,那怎么拿到这个类?
看API文档后,有一个方法forName(String className); 而且是一个静态的方法,这样咱们就可以得到想反射的类了
到这里,看Class clazz = Class.forName("com.cj.test.Person");这个应该有点感觉了吧
Class.forName("com.cj.test.Person");因为这个方法里接收的是个字符串,字符串的话,我们就可以写在配置文件里,然后利用反射生成我们需要的对象,这才是我们想要的。很多框架里都有类似的配置
我们知道一个类里一般有构造函数、方法、成员变量(字段/属性)这三部分组成
翻阅API文档,可以看到
Class对象提供了如下常用方法:
public Constructor getConstructor(Class<?>…parameterTypes)
public Method getMethod(String name,Class<?>… parameterTypes)
public Field getField(String name)
public Constructor getDeclaredConstructor(Class<?>…parameterTypes)
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)
public Field getDeclaredField(String name)
这些方法分别用于帮咱们从类中解剖出构造函数、方法和成员变量(属性)。
然后把解剖出来的部分,分别用Constructor、Method、Field对象表示。
可以看到 默认的无参构造方法执行了
从上边的例子看出,要想反射,首先第一步就是得到类的字节码
所以简单说一下得到类的字节码的几种方式
(1)、Class.forName("com.cj.test.Person"); 这就是上边我们用的方式
(2)、对象.getClass();
(3)、类名.class;
注意:在反射私有的构造函数时,用普通的clazz.getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法 clazz.getDeclaredConstructor(int.class);//读取私有的构造函数,用这个方法读取完还需要设置一下暴力反射才可以
c.setAccessible(true);//暴力反射
package com.cj.test; import java.util.Date; public class Person { public Person(){ System.out.println("默认的无参构造方法执行了"); } public Person(String name){ System.out.println("姓名:"+name); } public Person(String name,int age){ System.out.println(name+"="+age); } private Person(int age){ System.out.println("年龄:"+age); } public void m1() { System.out.println("m1"); } public void m2(String name) { System.out.println(name); } public String m3(String name,int age) { System.out.println(name+":"+age); return "aaa"; } private void m4(Date d) { System.out.println(d); } public static void m5() { System.out.println("m5"); } public static void m6(String[] strs) { System.out.println(strs.length); } public static void main(String[] args) { System.out.println("main"); } }
package com.cj.test; import java.lang.reflect.Method; import java.util.Date; import org.junit.Test; public class Demo2 { @Test//public void m1() public void test1() throws Exception{ Class clazz = Class.forName("com.cj.test.Person"); Person p = (Person)clazz.newInstance(); Method m = clazz.getMethod("m1", null); m.invoke(p, null); } @Test//public void m2(String name) public void test2() throws Exception{ Class clazz = Person.class; Person p = (Person) clazz.newInstance(); Method m = clazz.getMethod("m2", String.class); m.invoke(p, "张三"); } @Test//public String m3(String name,int age) public void test3() throws Exception{ Class clazz = Person.class; Person p = (Person) clazz.newInstance(); Method m = clazz.getMethod("m3", String.class,int.class); String returnValue = (String)m.invoke(p, "张三",23); System.out.println(returnValue); } @Test//private void m4(Date d) public void test4() throws Exception{ Class clazz = Person.class; Person p = (Person) clazz.newInstance(); Method m = clazz.getDeclaredMethod("m4", Date.class); m.setAccessible(true); m.invoke(p,new Date()); } @Test//public static void m5() public void test5() throws Exception{ Class clazz = Person.class; Method m = clazz.getMethod("m5", null); m.invoke(null,null); } @Test//private static void m6(String[] strs) public void test6() throws Exception{ Class clazz = Person.class; Method m = clazz.getDeclaredMethod("m6",String[].class); m.setAccessible(true); m.invoke(null,(Object)new String[]{"a","b"}); } @Test public void test7() throws Exception{ Class clazz = Person.class; Method m = clazz.getMethod("main",String[].class); m.invoke(null,new Object[]{new String[]{"a","b"}}); } }
这是因为 jdk1.4和jdk1.5处理invoke方法有区别
1.5:public Object invoke(Object obj,Object…args)
1.4:public Object invoke(Object obj,Object[] args)
由于JDK1.4和1.5对invoke方法的处理有区别, 所以在反射类似于main(String[] args) 这种参数是数组的方法时需要特殊处理
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数个数不对的问题。
上述问题的解决方法:
(1)mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
这种方式,由于你传的是一个数组的参数,所以为了向下兼容1.4的语法,javac遇到数组会给你拆开成多个参数,但是由于咱们这个Object[ ] 数组里只有一个元素值,所以就算它拆也没关系
(2)mainMethod.invoke(null,(Object)new String[]{"xxx"});
这种方式相当于你传的参数是一个对象,而不是数组,所以就算是按照1.4的语法它也不会拆,所以问题搞定
编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
对上边的描述进行一下总结:在反射方法时,如果方法的参数是一个数组,考虑到向下兼容问题,会按照JDK1.4的语法来对待(JVM会把传递的数组参数拆开,拆开就会报参数的个数不匹配的错误)
解决办法:防止JVM拆开你的数组
方式一:把数组看做是一个Object对象
方式二:重新构建一个Object数组,那个参数数组作为唯一的元素存在。
package com.cj.test; import java.util.Date; public class Person { public String name="李四"; private int age = 18; public static Date time; public int getAge() { return age; } public Person(){ System.out.println("默认的无参构造方法执行了"); } public Person(String name){ System.out.println("姓名:"+name); } public Person(String name,int age){ System.out.println(name+"="+age); } private Person(int age){ System.out.println("年龄:"+age); } public void m1() { System.out.println("m1"); } public void m2(String name) { System.out.println(name); } public String m3(String name,int age) { System.out.println(name+":"+age); return "aaa"; } private void m4(Date d) { System.out.println(d); } public static void m5() { System.out.println("m5"); } public static void m6(String[] strs) { System.out.println(strs.length); } public static void main(String[] args) { System.out.println("main"); } }
package com.cj.test; import java.lang.reflect.Field; import java.util.Date; import org.junit.Test; public class Demo3 { //public String name="李四"; @Test public void test1() throws Exception{ Class clazz = Person.class; Person p = (Person)clazz.newInstance(); Field f = clazz.getField("name"); String s = (String)f.get(p); System.out.println(s); //更改name的值 f.set(p, "王六"); System.out.println(p.name); } @Test//private int age = 18; public void test2() throws Exception{ Class clazz = Person.class; Person p = (Person)clazz.newInstance(); Field f = clazz.getDeclaredField("age"); f.setAccessible(true); int age = (Integer)f.get(p); System.out.println(age); f.set(p, 28); age = (Integer)f.get(p); System.out.println(age); } @Test//public static Date time; public void test3() throws Exception{ Class clazz = Person.class; Field f = clazz.getField("time"); f.set(null, new Date()); System.out.println(Person.time); } }
以上就是自己对Java中反射的一些学习总结,欢迎大家留言一起学习、讨论
看完上边有关反射的东西, 对常用框架里的配置文件是不是有点思路了
上边是Spring配置文件里的常见的bean配置,这看起来是不是可以用反射很轻易的就可以实现:解析xml然后把xml里的内容作为参数,利用反射创建对象。
拓展:
1、除了上述的Spring配置文件里会用到反射生成bean对象,其他常见的MVC框架,比如Struts2、SpringMVC等等一些框架里还有很多地方都会用到反射。
前端夜页面录入的一些信息通过表单或者其他形式传入后端,后端框架就可以利用反射生成对应的对象,并利用反射操作它的set、get方法把前端传来的信息封装到对象里。
感兴趣的话可以看下这篇:利用Java反射模拟一个Struts2框架 Struts2主要核心设计 手动实现Struts2核心代码,这篇里边包含了XML解析、反射的东西,模拟了一个Struts2的核心代码
2、框架的代码里经常需要利用反射来操作对象的set、get方法,来把程序的数据封装到Java对象中去。
如果每次都使用反射来操作对象的set、get方法进行设置值和取值的话,过于麻烦,所以JDK里提供了一套API,专门用于操作Java对象的属性(set/get方法),这就是内省
关于内省相关的内容我也整理了一篇文章,感兴趣可以点击:Java反射——内省(Introspector)以及BeanUtils内省框架
3、平常用到的框架,除了配置文件的形式,现在很多都使用了注解的形式。
其实注解也和反射息息相关:使用反射也能轻而易举的拿到类、字段、方法上的注解,然后编写注解解析器对这些注解进行解析,做一些相关的处理
所以说不管是配置文件还是注解的形式,它们都和反射有关。注解和自定义注解的内容,最近也抽时间大概整理了一下,感兴趣的小可爱可以点击了解:Java中的注解以及自定义注解
写在最后:反射是框架的灵魂,具备反射知识和思想,是看懂框架的基础。希望看完文章后对你能有所帮助。
铁子们,如果觉得文章对你有所帮助,可以点关注,点赞
也可以关注下公众号:扫码 或 wx搜索:“聊5毛钱的java” ,欢迎一起学习交流,关注公众号可领取博主的Java学习视频+资料,保证都是干货
3Q~
纯手敲原创不易,如果觉得对你有帮助,可以打赏支持一下,哈哈,感谢~
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。