day42-反射01
Java反射01
1.反射(reflection)机制
1.1反射机制问题
一个需求引出反射
请看下面问题:
- 根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi
classfullpath=li.reflection.Cat method=hi
使用现有的技术,你能做的到吗?
-
这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码的情况下来控制程序,也符合设计模式的ocp原则(开闭原则)
开闭原则:不修改源码,扩展功能
例子:
re.properties:
classfullpath=li.reflection.Cat method=cry
Cat:
package li.reflection; public class Cat { private String name = "招财猫"; public void hi() { System.out.println("hi " + name); } public void cry() { System.out.println(name + " cry"); } }
ReflectionQuestion:
package li.reflection; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties; public class ReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //根据配置文件 re.properties 指定信息,创建Cat对象 并调用方法hi //1.传统方法 new 对象 -->调用方法 //Cat cat = new Cat(); //cat.hi(); //2.尝试使用读取文件的方法 //2.1使用properties类,可以读写配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("src\\re.properties")); String classfullpatch = properties.get("classfullpath").toString(); String methodName = properties.get("method").toString(); System.out.println("classfullpath" + classfullpatch); System.out.println("method" + methodName); //2.2创建对象 //使用传统的方法行不通 //3.使用反射机制解决 //3.1加载类,返回一个Class类型的对象cls(这里的Class就是一个类,他的类名就叫Class) Class cls = Class.forName(classfullpatch); //3.2通过cls得到加载的类 li.reflection.Cat 的一个对象实例 Object o = cls.newInstance(); System.out.println("o的运行类型=" + o.getClass());//o的运行类型 //3.3通过 cls 得你加载的类 li.reflection.Cat 的methodName"hi" 的方法对象 // 即:在反射机制中,可以把方法视为一个对象(万物皆对象) Method method1 = cls.getMethod(methodName); //3.4通过 method1 调用方法:即通过方法对象来实现调用方法 System.out.println("========"); method1.invoke(o);//传统方法: 对象.方法() , 反射机制:方法.invoke(对象) //意义在与反射机制可以通过不求该源码就完成功能的拓展 /** * 例如,在Cat类中有两个方法,hi()和cry(),现在要求将调用用的hi方法改为调用cry方法, * 这时候只需要在配置文件re.properties中修改引用的方法名即可 * 不需像想传统方法一样,需要在源码中修改 */ } }

1.2反射机制
1.2.1Java Reflection
- 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能够操作对象的属性以及方法。反射在设计模式和框架底层都会用到
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到了的结构,所以形象地称之为:反射
例如:一个Person类型的实例对象叫 p
则 p 对象 - -> 类型 Person类
Class 对象 cls --> 类型 Class 类(这个类的名字就叫Class)
1.2.2Java反射机制原理图
- Java反射机制可以完成
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
1.2.3反射相关类
反射相关的主要类:
- java.lang.Class:代表一个类,Class对象表示某个类加载过后在堆中的对象
- java.lang.reflect.Method:表示类的方法(Method对象表示某个类的方法)
- java.lang.reflect.Field:表示类的成员变量(Field对象表示某个类的成员变量)
- java.lang.reflect.Constructor:表示类的构造方法(Constructor对象表示某个类的构造器)
这些类在 java.lang.reflect
例子:
Cat:
package li.reflection; public class Cat { private String 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 + " cry"); } }
re.properties:
classfullpath=li.reflection.Cat method=hi
Reflection01:
package li.reflection; import java.io.FileInputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Properties; public class Reflection01 { public static void main(String[] args) throws Exception { //使用properties类,读写配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("src\\re.properties")); String classfullpatch = properties.get("classfullpath").toString(); String methodName = properties.get("method").toString(); System.out.println("classfullpath" + classfullpatch); System.out.println("method" + methodName); //反射机制 //1.加载类,返回一个Class类型的对象cls(这里的Class就是一个类,他的类名就叫Class) Class cls = Class.forName(classfullpatch); //通过cls得到加载的类 li.reflection.Cat 的一个对象实例 Object o = cls.newInstance(); System.out.println("o的运行类型=" + o.getClass());//o的运行类型 //2.通过 cls得到加载的类 li.reflection.Cat 的methodName"hi" 的方法对象 // 即:在反射机制中,可以把方法视为一个对象(万物皆对象) Method method1 = cls.getMethod(methodName); //通过 method1 调用方法:即通过方法对象来实现调用方法 System.out.println("========"); method1.invoke(o);//传统方法: 对象.方法() , 反射机制:方法.invoke(对象) //3.java.lang.reflect.Field:表示类的成员变量(Field对象表示某个类的成员变量) //得到name字段 //getField不能得到私有的属性 Field nameField = cls.getField("age"); System.out.println(nameField.get(o));//传统方法:对象.成员变量 , 反射:成员变量的对象.get(对象) //4.java.lang.reflect.Constructor:表示类的构造方法(Constructor对象表示某个类的构造器) Constructor constructor1 = cls.getConstructor();//()中可以指定构造器的类型,这里返回无参构造器 System.out.println(constructor1);//Cat() Constructor constructor2 = cls.getConstructor(String.class);//这里传入的String.class 就是String类的Class对象 System.out.println(constructor2);//Cat(String name) } }

1.2.4反射优点和缺点
- 优点:可以动态地创建和使用对象(也是底层框架的核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
- 缺点:使用反射机制基本是解释执行,对执行速度有影响
- 调用反射优化-关闭访问检查
- Method和Field、Constructor对象都有setAccessible()方法
- setAccessible作用是启动和禁用服务安全检查的开关
- 参数值为true表示 反射的对象在使用时 取消访问检查,提高反射效率。参数值为false则表示反射的对象执行访问检查

例子:测试反射调用的性能 和优化方案
package li.reflection; import java.lang.reflect.Method; //测试反射调用的性能 和 优化方案 public class Reflection02 { public static void main(String[] args) throws Exception { m1(); m2(); m3(); } //传统方法,调用hi public static void m1() { Cat cat = new Cat(); long start = System.currentTimeMillis(); for (int i = 0; i < 900000000; i++) { cat.hi(); } long end = System.currentTimeMillis(); System.out.println("m1() 耗时=" + (end - start)); } //反射机制调用hi public static void m2() throws Exception { //拿到Cat类的Class对象 Class cls = Class.forName("li.reflection.Cat");//这里为了方便,就不读文件了 //构造Cat类的对象 Object o = cls.newInstance(); //得到Cat类的成员方法 Method hi = cls.getMethod("hi"); long start = System.currentTimeMillis(); for (int i = 0; i < 900000000; i++) { hi.invoke(o);//反射机制调用方法 } long end = System.currentTimeMillis(); System.out.println("m2() 耗时=" + (end - start)); } //反射调用优化 public static void m3() throws Exception { //拿到Cat类的Class对象 Class cls = Class.forName("li.reflection.Cat");//这里为了方便,就不读文件了 //构造Cat类的对象 Object o = cls.newInstance(); //得到Cat类的成员方法 Method hi = cls.getMethod("hi"); hi.setAccessible(true);//在反射调用方法时,取消访问检查 long start = System.currentTimeMillis(); for (int i = 0; i < 900000000; i++) { hi.invoke(o);//反射机制调用方法 } long end = System.currentTimeMillis(); System.out.println("m3()反射调用优化耗时=" + (end - start)); } }

2.Class类
2.1基本介绍
-
Class类也是类,因此也继承Object类
-
Class类对象不是new出来的,而是系统创建的
-
对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
-
每个类的实例都会记得自己是由哪个Class实例所生成
-
通过Class对象可以得到一个类的完整结构(通过一系列API)
-
Class对象是存放在堆的
-
类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)
当我们加载完类之后,除了会在堆里生成一个Class类对象,还会在方法区生成一个类的字节码二进制数据(元数据)
例子:
package li.reflection.class_; import li.reflection.Cat; //对Class类的特点的梳理 public class Class01 { public static void main(String[] args) throws ClassNotFoundException { //1.Class类对象不是new出来的,而是系统创建的 //1.1.传统的 new对象 /**通过ClassLoader类中的loadClass方法: * public Class<?> loadClass(String name) throws ClassNotFoundException { * return loadClass(name, false); * } */ //Cat cat = new Cat(); //1.2反射的方式 /**在这里debug,需要先将上面的Cat cat = new Cat();注释掉,因为同一个类只加载一次,否则看不到loadClass方法 * (这里也验证了:3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次) * 仍然是通过 ClassLoader类的loadClass方法加载 Cat类的 Class对象 * public Class<?> loadClass(String name) throws ClassNotFoundException { * return loadClass(name, false); * } */ Class cls1 = Class.forName("li.reflection.Cat"); //2.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次 Class cls2 = Class.forName("li.reflection.Cat"); //这里输出的hashCode是相同的,说明cls1和cls2是同一个Class类对象 System.out.println(cls1.hashCode());//1554874502 System.out.println(cls2.hashCode());//1554874502 } }
Class类对象不是new出来的,而是系统创建的:
- 在
Cat cat = new Cat();
处打上断点,点击force step into,可以看到
- 注释
Cat cat = new Cat();
,在Class cls1 = Class.forName("li.reflection.Cat");
处打上断点,可以看到 仍然是通过 ClassLoader类加载 Cat类的 Class对
2.2Class类常用方法
public static Class<?> forName(String className)//传入完整的“包.类”名称实例化Class对象 public Constructor[] getContructors() //得到一个类的全部的构造方法 public Field[] getDeclaredFields()//得到本类中单独定义的全部属性 public Field[] getFields()//得到本类继承而来的全部属性 public Method[] getMethods()//得到一个类的全部方法 public Method getMethod(String name,Class..parameterType)//返回一个Method对象,并设置一个方法中的所有参数类型 public Class[] getInterfaces() //得到一个类中锁实现的全部接口 public String getName() //得到一个类完整的“包.类”名称 public Package getPackage() //得到一个类的包 public Class getSuperclass() //得到一个类的父类 public Object newInstance() //根据Class定义的类实例化对象 public Class<?> getComponentType() //返回表示数组类型的Class public boolean isArray() //判断此class是否是一个数组
「其他文章」
- 记一次批量更新整型类型的列 → 探究 UPDATE 的使用细节
- 编码中的Adapter,不仅是一种设计模式,更是一种架构理念与解决方案
- 线程池底层原理详解与源码分析
- 30分钟掌握 Webpack
- 线性回归大结局(岭(Ridge)、 Lasso回归原理、公式推导),你想要的这里都有
- Django 之路由层
- 【前端必会】webpack loader 到底是什么
- day42-反射01
- 中心化决议管理——云端分析
- HashMap底层原理及jdk1.8源码解读
- 详解JS中 call 方法的实现
- 打印 Logger 日志时,需不需要再封装一下工具类?
- 初识设计模式 - 代理模式
- 设计模式---享元模式
- 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码
- [ML从入门到入门] 支持向量机:从SVM的推导过程到SMO的收敛性讨论
- 从应用访问Pod元数据-DownwardApi的应用
- Springboot之 Mybatis 多数据源实现
- Java 泛型程序设计
- CAS核心思想、底层实现