反射 p1 反射机制
反射机制
引出反射
-
这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码的情况下,来控制程序,也符合设计模式的OCP原则(开闭原则:不修改源码,扩展功能)。
-
代码演示:
re.properties文件:
classfullpath=com.hspedu.Cat method=cry
Cat类:
package com.hspedu; /** * @author: 86199 * @date: 2023/5/18 10:09 * @description: */ public class Cat { private Strinag name = "招财猫"; public int age = 10; public Cat() { } public Cat(String name) { this.name = name; } public void hi(){ System.out.println("hi" + name); } public void cry(){ System.out.println(name + "喵喵叫"); } }
ReflectionQuestion:
package com.hspedu.reflection.question; import com.hspedu.Cat; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties; /** * @author: 86199 * @date: 2023/5/18 10:14 * @description: */ public class ReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //普通方式 // Cat cat = new Cat(); // cat.hi(); //IO流,使用Preperties类读写配置文件 Properties properties = new Properties(); properties.load( new FileReader("src\\com\\hspedu\\re.properties")); String classfullpath = properties.get("classfullpath").toString(); String methodName = properties.getProperty("method"); System.out.println(classfullpath); System.out.println(methodName); //使用反射解决 //(1)加载类,返回Class类型的对象 cls Class cls = Class.forName(classfullpath); //(2)通过cls 得到你的加载的类 com.hspedu.Cat 的对象实例 Object o = cls.newInstance(); System.out.println("o 的运行类型 = " + o.getClass());//运行类型 //(3)通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法对象 // 即:在反射中,可以把方法视为对象(万物皆对象) Method method = cls.getMethod(methodName); //(4) 通过method调用方法:即通过方法对象类实现调用方法 System.out.println("============================"); method.invoke(o); //可以直接通过修改配置文件,而不是源码来改变调用的方法 } } /*运行结果: com.hspedu.Cat cry o 的运行类型 = class com.hspedu.Cat ============================ 招财猫喵喵叫 */
反射机制
- 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到;
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射。
- 在启动时,包含main方法的类被加载。它会加载所有需要的类。这些被加载的类又要加载它们需要的类,以此类推。
-
反射原理图
-
弹幕摘要:
- 加载的过程在方法区,加载结果(对象)在堆区,不知道对不对;
- 类加载是将数据放入的内存的方法区中,然后在堆创建一个class对象,说白了就是堆中的class是用来封装方法区数据的类
-
疑惑:
-
那为什么需要这样一个Class对象呢?
- Class类的对象的作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要。
- 是这样的,当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器子系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。也就是说,Class对象对于类的实例化具有非常重要的意义。没它就没法new新对象和引用静态成员变量。
————————————————
版权声明:本文为CSDN博主「一只野生饭卡丘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Searchin_R/article/details/84591735
-
反射相关的主要类
- java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象;
- java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法;
- java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量;
- java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器;
-
代码演示
package com.hspedu.reflection; import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Properties; /** * @author: 86199 * @date: 2023/5/20 10:52 * @description: */ public class Reflection01 { public static void main(String[] args) throws Exception{ //1. IO流,使用Preperties类读写配置文件 Properties properties = new Properties(); properties.load( new FileReader("src\\com\\hspedu\\re.properties")); String classfullpath = properties.get("classfullpath").toString(); String methodName = properties.getProperty("method"); //2. 使用反射解决 //(1)加载类,返回Class类型的对象 cls Class cls = Class.forName(classfullpath); //(2)通过cls 得到你的加载的类 com.hspedu.Cat 的对象实例 Object o = cls.newInstance(); System.out.println("o 的运行类型 = " + o.getClass());//运行类型 //(3)通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法对象 // 即:在反射中,可以把方法视为对象(万物皆对象) Method method = cls.getMethod(methodName); //(4) 通过method调用方法:即通过方法对象类实现调用方法 System.out.println("============================"); method.invoke(o); //java.lang.reflect.Field:代表类的成员变量,Filed对象白哦是某个类的成员变量 //得到age字段 //getField不能得到私有属性 Field ageFiled = cls.getField("age"); System.out.println(ageFiled.get(o)); //java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器 Constructor constructor = cls.getConstructor();//()中可以指定构造器参数类型,不指定返回无参构造器 System.out.println(constructor);//Cat() Constructor constructor1 = cls.getConstructor(String.class);//传入String类的Class对象 System.out.println(constructor1);//Cat(java.lang.String) } }
-
反射的优缺点:
-
优点:可以动态的创建和使用对象(也是框架的底层核心),使用灵活,没有反射机制,框架技术就失去了底层支撑。
-
缺点:使用反射基本是解释执行,对执行速度有影响。
-
反射调用优化——关闭访问检查
- Method和Field、Constructor对象都有setAccessible()方法;
- setAccessible()作用是启动和禁用访问安全检查的开关;
- 参数值为true表示反射的对象在使用时取消访问检查,可以提高反射的效率。参数值为false则表示反射的对象执行访问检查。
-
代码演示:
package com.hspedu.reflection; import com.hspedu.Cat; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author: 86199 * @date: 2023/5/20 11:20 * @description: 测试反射调用的性能,和优化方案 */ public class Reflection02 { public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { m1(); m2(); m3(); } //这里先把hi()方法中的打印注释了 //传统方法调用方法hi public static void m1(){ Cat cat = new Cat(); long start = System.currentTimeMillis(); for (int i = 0; i < 90000000; i++){ cat.hi(); } long end = System.currentTimeMillis(); System.out.println("m1() 耗时 = " + (end - start)); } //反射机制调用方法hi public static void m2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class cls = Class.forName("com.hspedu.Cat"); Object o = cls.newInstance(); Method method = cls.getMethod("hi"); long start = System.currentTimeMillis(); for (int i = 0; i < 90000000; i++){ method.invoke(o); } long end = System.currentTimeMillis(); System.out.println("m2() 耗时 = " + (end - start)); } //反射优化:关闭访问检测 public static void m3() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class cls = Class.forName("com.hspedu.Cat"); Object o = cls.newInstance(); Method method = cls.getMethod("hi"); method.setAccessible(true);//取消在访问调用方法时的访问检查 long start = System.currentTimeMillis(); for (int i = 0; i < 90000000; i++){ method.invoke(o); } long end = System.currentTimeMillis(); System.out.println("m3() 耗时 = " + (end - start)); } } /*运行结果 m1() 耗时 = 3 m2() 耗时 = 197 m3() 耗时 = 168 */