Java十分钟精通反射机制原理
什么是反射?
反射机制是在运行状态中,它为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 = new Person(); 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,就是看反射能不能得到里面的值)
public class Person { private String name; //名字 private int age = 18; //年龄 public int ID = 123; //身份证 public String Sex; //性别 @Override public String toString(){ return "姓名"+name+"年龄:"+age+"ID:"+ID+"性别:"+Sex; } }
测试类:
public class Test { public static void main(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类:
public class Person { private String name; //名字 private int age = 18; //年龄 public int ID = 123; //身份证 public String Sex ; //性别 //构造: public Person() {} public Person(String name, int age, int ID, String sex) { this.name = name; this.age = age; this.ID = ID; Sex = sex; } //无参公有方法: public void eat(){ System.out.println("我会吃饭"); } //有参公有方法: public void eat(String food){ System.out.println("我在吃:"+food); } //有参私有方法 private void play(String name){ System.out.println(name+"在玩"); } }
测试类:
public class Test { public static void main(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 = new Person(); //通过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方法↓-----
public void Test02.Person.eat(java.lang.String)
方法名:eat
public void Test02.Person.eat()
方法名:eat
public final void java.lang.Object.wait() throws java.lang.InterruptedException
方法名:wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
方法名:wait
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
方法名:wait
public boolean java.lang.Object.equals(java.lang.Object)
方法名:equals
public java.lang.String java.lang.Object.toString()
方法名:toString
public native int java.lang.Object.hashCode()
方法名:hashCode
public final native java.lang.Class java.lang.Object.getClass()
方法名:getClass
public final native void java.lang.Object.notify()
方法名:notify
public final native void java.lang.Object.notifyAll()
方法名:notifyAll
小结:
以上就是小应学长对反射的理解和方法的应用(当然还有很多方法,这里就不一一举例,大家可以查看官网文档和其他技术博客开学习),其实我刚开始接触反射的时候也很难理解反射的概念,也是通过大量的视频和查看其他的博客来结合每个人的见解,这篇文章也有很多不足之处,欢迎大家批评指正,一起共同进步。
当然反射也有缺点(查阅其他博客的知识):
- 性能问题: 使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
- 反射会模糊程序内部逻辑:一般开发人员希望在源代码中看到程序内部的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。其实反射代码比直接的代码更复杂。
到此这篇关于Java十分钟精通反射机制原理的文章就介绍到这了,更多相关Java 反射机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!