本文是我和武哥联合创作,已收录至我们的GitHub,欢迎大家给个Star:https://github.com/nxJava/nx_java 微信搜索:Java学习指南,关注这个专注于Java干货的公众号~
我们先看下反射的概念: Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。 简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的全限定名称(例如 那么既然有“反”,就应该有“正”,我们没学反射之前是怎么通过正向过程创建对象的呢? 例如我们想创建一个Student的对象,在这之前我们是不是应该创建Student类: 然后我们通过new的方式创建对象,并调用其方法: 好,到此为止我们通过正向的方式创建了一个Student对象,那么通过反射怎么创建Student对象?我们来看下: 以上两种方法都可以打印年龄19: java源文件从创建到运行会经历3个阶段,分别是源码阶段、Class类对象阶段和运行阶段。在java文件编译成class文件后,通过类加载器将class的信息加载进内存,在内存中class字节码文件被描述成3种对象信息,分别是成员变量对象、构造方法对象、成员方法对象,它们都是多个,所以是数组。这个Class对象阶段就是反射机制,我们通过Class可以得到类的所有信息,创建对象和调用其成员方法。 从上面的例子我们可以看出,如果你事先创建好了Student类,那么你可以直接创建Student对象并调用其方法,那么如果我和另外一位老铁分开开发程序,这个Student类是他写的,我们彼此开发完全独立,我需要在我的程序中创建Studnet对象,然后调用Student方法获取学生的年龄,这个时候可能这位老铁还没有完成Student类的开发,这怎么办? 这就用到了反射了,我在开发程序的时候只需要跟他约定好Studnet类的信息,例如类名称,字段名称、方法名称,我就可以通过反射的机制在他还未写Studnet类的时候动态创建Studnet对象并调用其方法了。等他的Studnet类完成创建之后,我们的代码合并部署运行,我就可以调用到Student的方法了。 其实在框架内部处处都是反射的应用,反射是框架的灵魂。比如我们最常用的Spring框架,在定义一个bean的时候我们可能会这么做: 在xml文件中定义一个bean,告诉Spring我需要一个Student对象,这个对象的名称是 生成完对象之后,Spring会把这个对象放在一个容器中,在实际需要的时候通过 我们常用的RPC框架,例如DUBBO,在接口调用的时候,也是应用反射机制。 RPC框架根据客户端的请求 :接口名称(interface)、方法名称(method)、参数类型(paramtype)、参数(params)等进行反射,dubbo接口之间通信的机制大概是这样的: 再比如我们在写代码的时候,创建一个字符串对象,调用其方法,我们是不是通过 “.“来获取方法的? idea在运行期间,通过反射获取到了String对象的全部方法,然后列举成一个列表,描述了方法的名称、参数和返回值,我们在使用的时候可以直接选择想要的方法,非常方便。 Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类: JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。JDK代理要求被代理的类必须实现接口,有很强的局限性。而CGLIB动态代理则没有此类强制性要求。简单的说,CGLIB会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。 一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler;使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑。 最简单粗暴的方法就是对着API撸! 获取class对象的三种方式 注意,同一个字节码.class文件只会被加载一次,无论哪种方式获取的Class对象都是同一个。 我们来具体演示下。 为了达到演示效果,Studnet类稍作修改,有四个属性,分别使用public、protected、default、private来修饰。 先看下getFields和getField方法 运行结果: 我们可以看到,getFields只获得了Student的name属性,由于age属性是protected修饰的,所以getField(“age”)抛出了NoSuchFieldException的异常。 那么怎么获得非public修饰的属性?我们可以通过getDeclaredFields和getDeclaredField(‘name’)两个方法来获取: 运行结果 那么结果会是什么样子的? 那么private修饰的属性我们如何从对象中获取其属性值?我们需要加上这一句: 忽略了访问权限修饰符的权限限制,我们就可以通过反射获取任意属性的属性值了。 注意:getDeclared***同样适用于Constructor和Method,分别是getDeclaredConstructor()和getDeclaredMethod(),通过setAccessible(true)这种方式可以忽略其访问限制,从而可以使用类私有的属性、构造器和方法。 获取构造器的作用是为了new对象,可以通过有参、无参的构造器创建对象,无参构造器创建的对象等同于Class提供的newInstance方式创建对象: 运行结果: 我们还是用Student类来演示,先给Student加上两个成员方法: 现在来利用反射调用下这两个方法: 运行结果: 运行结果: 反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类; 创作不易,如果您喜欢这篇文章的话,请你 + 评论 支持一下作者好吗?您的支持是我创作的源泉哦!喜欢Java,热衷学习的小伙伴可以加我微信: xia_qing2012 ,私聊我可以获取最新Java基础到进阶的全套学习资料。大家一起学习进步,成为大佬!
文章目录
1. 什么是反射?
1.1 反射概念
com.example.Student
),那么就可以通过反射机制来获得类的所有信息。1.2 举例说明反射机制
public class Student { private String name; // 姓名 private Integer age; // 年龄 private String address; // 地址 public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Student student = new Student(); // 创建对象 student.setAge(19); // 执行setAge方法 Integer age = student.getAge(); // 获取学生的年龄 System.out.println(age);
Class<?> clazz = Class.forName("Student"); //获取Student类 Object o = clazz.newInstance(); // 通过类创建对象 Method setAgeMethod = clazz.getMethod("setAge", Integer.class); // 获取setAge方法。第一个参数是方法名称,第二个参数是方法的参数类型列表,如果有多个参数可以传多个参数的类型 setAgeMethod.invoke(o, 19); // 执行setAge()方法 Method getAgeMethod = clazz.getMethod("getAge"); // 获取getAge方法 Object age = getAgeMethod.invoke(o); // 执行getAge方法 System.out.println(age);
这就是反射,在程序运行的时候动态创建类的实例,并通过实例调用类的方法。1.3 更深入地看下反射机制
2. 为什么学反射?
2.1 利用反射分工协作
2.2 Spring中的反射
<bean id="studentBean" class="com.example.Student"/>
com.example.Student
,那么Spring就会通过我们上面那种方式去生成对象:Class clazz = Class.forName("com.example.Student"); // 通过名称得到Class Object stuObj = clazz.newInstance(); // 生成对象
@Autoware
或者@Resource
等注解的方式去获取对象,或者通过ApplicationContext
获取bean对象。2.3 Dubbo中的反射
通过反射我们可以实现上面的流程:String className = request.getClassName(); // 获取类名称 Class<?> c = Class.forName(className); // 得到Class信息 Object serviceBean = c.newInstance(); // 创建对象 String methodName = request.getMethodName(); // 得到调用的接口名称 Class<?> paramTypes = request.getParamTypes(); // 获取接口的参数类型 Object[] params = request.getParams(); // 得到消费方调用接口的参数 Method method = c.getMethod(methodName, paramTypes); // 得到接口 method.invoke(serviceBean , params); // 执行接口调用
2.4 IDEA中的反射
你看通过”.”我们可以看到String对象的所有方法信息,这是怎么做到的?其实这也是利用了Java的反射机制。2.5 反射在动态代理中的应用
2.5.1 JDK动态代理
2.5.2 Cglib动态代理
2.5.3 Javassist代理
3. 怎么学反射?
3.1 class对象
Class.forName("全类名")
,将字节码文件加载进内存,获取class对象;Class clazz = Class.forName("Student"); // 可能会抛出ClassNotFoundException异常 Class clazz1 = Student.class; Class<?> clazz2 = new Student().getClass();
3.2 成员变量
public class Student { public String name; protected Integer age; String address; private String phone; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
Class clazz = Student.class; Field[] fields = clazz.getFields(); for (Field field : fields) { System.out.println("getFields======" + field); } Field field = clazz.getField("name"); System.out.println("getField name======" + field); Field field1 = clazz.getField("age"); System.out.println("getField age======" + field1);
Field[] declaredFields = clazz.getDeclaredFields(); // 获取所有已声明的属性 for (Field declaredField : declaredFields) { System.out.println("getDeclaredFields======" + declaredField); } Field ageField = clazz.getDeclaredField("age"); // 获取指定名称的已声明的属性 System.out.println("getDeclaredField age=========" + ageField);
可以看到我们拿到了类的所有已声明字段,那么拿到了这些字段我们就可以获取对象对应字段的值了,来试下:Student student = new Student(); // new一个Student对象 student.setName("小明"); // 设置属性值 student.setAge(20); student.setAddress("上海市"); student.setPhone("13011220099"); Field[] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("field=" + declaredField.getName() + ",value=" + declaredField.get(student)); // 获取属性值 }
可以看到,前3个属性的值打印出来了,也就是说非private修饰的属性可以通过declaredField.get(obj)的方式从对象中获取其属性值,但是private修饰的属性phone在获取属性值的时候会抛出java.lang.IllegalAccessException异常,无法访问。// 忽略访问权限修饰符的安全检查 declaredField.setAccessible(true);
3.3 构造方法
Constructor constructor = clazz.getConstructor(String.class, Integer.class); // Constructor 有参构造 Object stu = constructor.newInstance("张三", 12); // 创建对象 System.out.println("Constructor有参构造对象======" + stu); Constructor constructor1 = clazz.getConstructor(); // Constructor 无参构造 Object stu1 = constructor1.newInstance(); // 创建对象 System.out.println("Constructor无参构造======" + stu1); Object stu2 = clazz.newInstance(); // Constructor无参构造创建对象简写形式:Class创建对象 System.out.println("Class无参构造======" + stu2);
3.4 成员方法
public void eat() { System.out.println("eat"); } public void eat(String food) { System.out.println("eat " + food); }
Student student = new Student(); // 创建Student对象 Method eatMethod = clazz.getMethod("eat"); // 获取eat方法对象 eatMethod.invoke(student); // 执行eat方法 Method eatMethod1 = clazz.getMethod("eat", String.class); // 获取有参eat方法对象 eatMethod1.invoke(student, "香蕉"); // 执行有参eat方法
那么getMethods是不是获取Studnet定义的成员方法呢?Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); }
可以看到Object类定义的成员方法也被打印出来了,所以这里我们要注意getMethods()方法会获取类自身以及Object类中定义的所有成员方法。4. 反射的优缺点
4.1 优点
4.2 缺点
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算