反射:
首先,反射是框架的灵魂,Java框架底层都是用反射机制+xml配置等来实现的。
什么是反射?
反射机制是在运行状态中,它为Java提供一种“操作对象”的能力,在运行状态下,通过Class文件对象,可以调用到任何类里面的属性、方法、以及构造方法,包括私有的,所有的类在反射机制面前都是透明的
自己的概括:通过Class文件对象可以看到这个类里面的所有东西,并且可以使用和修改
反射的前提是获取Class文件对象((字节码对象),那么一共有三种方式获取:
- Class.forName(“全类名”) ----通过Class类的静态方法(最常用)
- 类名.class
- 对象.getClass()
//方式1:获取字节码对象,Class.forName("全类名")
Class cla1= Class.forName("Study01.Person");//方式2: 类名.Class
Class cla2= Person.class;//方式3:对象.getClass();
Person per =newPerson();
Class cla3= per.getClass();//这三个class对象都是由Person这个类生成的//那么我们看一下这三个字节码对象是不是同一个:
System.out.println(cla1==cla2);
System.out.println(cla2==cla3);//输出结果: 两个true
结论:
- 字节码对象在类加载的时候就产生,并且只有一个
- 无论哪种方式获取字节码对象都是同一个字节码对象
通过反射来获取类中的属性:
获取到Class字节码对象后,我们就可以通过字节码对象来获取到我们想要获取的类的属性、方法、构
造方法、以及private修饰的。
部分Class方法:(全部的可以查阅官方文档)
查阅地址:
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html
Demo演示:
1、建一个Person类,里面有两个public和两个private的属性(不设置构造和get/set,就是看反射能不能得到里面的值)
publicclassPerson{private String name;//名字privateint age =18;//年龄publicint ID =123;//身份证public String Sex;//性别@Overridepublic String toString(){return"姓名"+name+"年龄:"+age+"ID:"+ID+"性别:"+Sex;}}
测试类:
publicclassTest{publicstaticvoidmain(String[] args)throws ClassNotFoundException {//获取Class文件对象,用最常用的通过Class类的静态方法
Class per= Class.forName("Test01.Person");//这里是传入全路径!!从最外层的包名开始!//使用getFields()方法获取全部被public修饰的属性(方法上面的截图有)//并且返回的是Field类型的数组
Field fields[]= per.getFields();for(Field field:fields){
System.out.println(field);}}}
输出:
我们成功的获取到了Person类中全部public属性
2、也可以获取全部的属性,包括私有的:(其他代码就不重写啦)
for(Field field : per.getDeclaredFields()){
System.out.println(field);}
输出:
3、获取公有的属性,并且修改这个值:
Field f = per.getField("Sex");
System.out.println(f);//获取一个对象:
Object obj = per.getConstructor().newInstance();//修改值:
f.set(obj,"男");
Person p =(Person)obj;
System.out.println(p.Sex);
输出:
4、获取私有的属性,并且修改这个值:
这里把上面修改公有属性的值也连起来:
Person p =(Person)obj;//获取公有字段并调用,并修改
Field f = per.getField("Sex");//获取一个对象:
Object obj = per.getConstructor().newInstance();
f.set(obj,"男");//将Sex的属性修改成了 男//调用私有的属性,并修改
f = per.getDeclaredField("name");//在访问私有的属性的值之前,先要设置运行访问↓//在访问之前忽略访问权限的检查,叫暴力反射
f.setAccessible(true);
f.set(obj,"张三");
System.out.println("Person里面的信息是:"+p.toString());}}
输出:
通过反射来获取类中的方法(公有、私有、构造):
Person类:
publicclassPerson{private String name;//名字privateint age =18;//年龄publicint ID =123;//身份证public String Sex ;//性别//构造:publicPerson(){}publicPerson(String name,int age,int ID, String sex){this.name = name;this.age = age;this.ID = ID;
Sex = sex;}//无参公有方法:publicvoideat(){
System.out.println("我会吃饭");}//有参公有方法:publicvoideat(String food){
System.out.println("我在吃:"+food);}//有参私有方法privatevoidplay(String name){
System.out.println(name+"在玩");}}
测试类:
publicclassTest{publicstaticvoidmain(String[] args)throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {//获取到Person以及父类Object里面的public方法:
System.out.println("-----获取到Person以及父类Object里面的public方法↓-----");for(Method method : Person.class.getMethods()){
System.out.println(method);
System.out.println("方法名:"+ method.getName());}//获取到Person里面的方法,包括私有
System.out.println("-----获取到Person里面的方法,包括私有↓-----");for(Method method:Person.class.getDeclaredMethods()){
System.out.println(method.getName()+" ");}//按照方法名获取到Person中的eat方法:
System.out.println("-----根据方法名获取到Person类中的eat方法↓-----");
Method earMethod1 = Person.class.getMethod("eat");
Person per =newPerson();//通过invoke(Object,param...)来调用指定的方法
earMethod1.invoke(per);//使用反射调用有参方法;
System.out.println("-----使用反射调用有参方法(传入参数)↓-----");
Method earMethod2 = Person.class.getMethod("eat",String.class);
earMethod2.invoke(per,"牛肉");//通过暴力反射获取到私有的play方法:
System.out.println("-----通过暴力反射获取到私有的play方法传入参数)↓-----");
Method earMethod3 = Person.class.getDeclaredMethod("play", String.class);//在访问私有的属性的方法之前,先要设置运行访问
earMethod3.setAccessible(true);
earMethod3.invoke(per,"小王");}
输出:
-----获取到Person以及父类Object里面的public方法↓-----publicvoid Test02.Person.eat(java.lang.String)
方法名:eat
publicvoid Test02.Person.eat()
方法名:eat
publicfinalvoid java.lang.Object.wait()throws java.lang.InterruptedException
方法名:wait
publicfinalvoid java.lang.Object.wait(long,int)throws java.lang.InterruptedException
方法名:wait
publicfinalnativevoid java.lang.Object.wait(long)throws java.lang.InterruptedException
方法名:wait
publicboolean java.lang.Object.equals(java.lang.Object)
方法名:equals
public java.lang.String java.lang.Object.toString()
方法名:toString
publicnativeint java.lang.Object.hashCode()
方法名:hashCode
publicfinalnative java.lang.Class java.lang.Object.getClass()
方法名:getClass
publicfinalnativevoid java.lang.Object.notify()
方法名:notify
publicfinalnativevoid java.lang.Object.notifyAll()
方法名:notifyAll
-----获取到Person里面的方法,包括私有↓-----
eat
eat
play
-----根据方法名获取到Person类中的eat方法↓-----
我会吃饭
-----使用反射调用有参方法(传入参数)↓-----
我在吃:牛肉
-----通过暴力反射获取到私有的play方法传入参数)↓-----
小王在玩
Process finished with exit code 0
小结:
以上就是小应学长对反射的理解和方法的应用(当然还有很多方法,这里就不一一举例,大家可以查看官网文档和其他技术博客开学习),其实我刚开始接触反射的时候也很难理解反射的概念,也是通过大量的视频和查看其他的博客来结合每个人的见解,这篇文章也有很多不足之处,欢迎大家批评指正,一起共同进步。
当然反射也有缺点(查阅其他博客的知识):
- 性能问题: 使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
- 反射会模糊程序内部逻辑:一般开发人员希望在源代码中看到程序内部的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。其实反射代码比直接的代码更复杂。
版权归原作者 热爱飞行的小应学长 所有, 如有侵权,请联系我们删除。