反射(reflection)需求引起反射
- 根据配置文件 re.properties 创建Cat对象并调用指定信息的方法
classfullpath = com.hspedu.Catmethod = hi
- 学习框架中有许多这样的需求,即在不修改源代码的情况下,通过外部文件配置。控制程序也符合设计模式的ocp原则(开关原则:不修改源代码和扩展功能)。
package com.hspedu.reflection.question;import com.hspedu.Cat;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Properties;/** * 引入反射问题 */@SuppressWarnings({"all"})public class ReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //根据配置文件 re.properties 指定信息, 创建Cat对象并调用方法hi //传统的方式 new 对象 -》 调用方法/////// Cat cat = new Cat();// cat.hi(); ===> cat.cry() 修改源码. ///我们试着去做 -> 明白反射 //1. 使用Properties 类, 可读写配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("src\\re.properties")); String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat" String methodName = properties.get("method").toString();//"hi" System.out.println("classfullpath=" + classfullpath); System.out.println("method=" + methodName); //2. 创建对象 , 传统的方法,行不通 =》 反射机制 //new classfullpath(); // classfullpath 这是一个字符串,正式应该是new 类名() //3. 采用反射机制解决 //(1) 加载类, clss返回Class类型的对象 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 method1 = cls.getMethod(methodName); //(4) 通过method1 调用方法: 即通过方法对象实现调用方法 System.out.println=============================; method1.invoke(o); //传统方法 对象、方法() , 反射机制 方法.invoke(对象) }}
Java的反射机制 Reflection
- 反射机制允许程序在执行期间使用
Reflection API
获取任何类型的内部信息(如成员变量、构造器、成员方法等),并可以操作对象的属性和方法。反射将用于设计模式和框架底部。 - 加载完类后,在堆中产生Class类型的对象( 一个类只有一个Class对象),包含类的完整结构信息。通过这个对象获得类的结构。这个Class对象就像一面镜子,透过这面镜子可以看到类的结构。因此,图像被称为反射。
类比:
p对象 ---> Person类型
clss对象Class ---> Class类型
Java 反射机制原理示意图 Java 可以完成反射机制- 在运行过程中判断任何对象所属的类别
- 在运行过程中构建任何类别的对象
- 在运行过程中获得任何类别的成员变量和方法
- 在运行过程中调用任何对象的成员变量和方法
- 生成动态代理
- java.lang.Class:代表一个类,Class对象表示某个类加载后堆中的对
- java.lang.reflect.Method:代表性的方法, Method对象表示某一类的方法
- java.lang.reflect.Field:Field对象代表某一类的成员变量,表示某一类的成员变量
- java.lang.reflect.Constructor:Constructor对象代表构造方法
java中的这些类别.lang.reflection
package com.hspedu.reflection;import java.io.FileInputStream;import java.io.FileNotFoundException;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 { //1. 使用Properties 类, 可读写配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("src\\re.properties")); String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat" String methodName = properties.get("method").toString();//"hi" //2. 采用反射机制解决 //(1) 加载类, clss返回Class类型的对象 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 method1 = cls.getMethod(methodName); //(4) 通过method1 调用方法: 即通过方法对象实现调用方法 System.out.println=============================; method1.invoke(o); //传统方法 对象、方法() , 反射机制 方法.invoke(对象) // java.lang.reflect.Field: 代表成员变量, Field对象表示某一类的成员变量 // 无法获得私有属性的getfield Field nameField = cls.getField("age"); // System.out.println(nameField.get(o)); // 传统写法 对象。成员变量 , 反射: 成员变量对象.get(对象) //java.lang.reflect.Constructor: 代表性的结构方法, Constructor对象表示构造器 构造器参数类型可以在//()中指定, 返回无参构造器 Constructor constructor = cls.getConstructor(); System.out.println(constructor);//Cat() Constructor constructor2 = cls.getConstructor(String.class); ///老师在这里介绍的 String.class 是StringClass对象 System.out.println(constructor2);//Cat(String name) }}
反射优缺点
- 优点:可以动态创建和使用对象(也是框架底层的核心),使用灵活,框架技术在没有反射机制的情况下会失去底层支撑。
- 缺点:反射的使用基本上是对执行速度的解释.
- Method 和 Field、constructor对象 setAccessible() 方法
- setacesible的功能是启动和禁止访问安全检查的开关
- 参数值为true,表示反射对象在使用时取消访问检查,提高反射效率。参数值为false,表示反射对象进行访问检查。
package com.hspedu.reflection;import com.hspedu.Cat;import java.io.FileInputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * 测试反射调用的性能,和优化方案 */public class Reflection02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { m1(); m2(); m3(); } ////传统方法调用hi public static void m1() { Cat cat = new Cat(); long start = System.currentTimeMillis(); for (int i = 0; i < 90; i++) { cat.hi(); } long end = System.currentTimeMillis(); System.out.println("m1() 耗时=" + (end - start)); } ////反射机制调用方法hii public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class cls = Class.forName("com.hspedu.Cat"); Object o = cls.newInstance(); 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 ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class cls = Class.forName("com.hspedu.Cat"); Object o = cls.newInstance(); 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)); }}
Class 类基本介绍
- Class也是类,所以也继承了Object类[类图]
- Class类对象不是new出来的,而是系统创建的[演示]
- 对于某一类的Class对象,内存中只有一个,因为该类只加载一次[演示]
- 每个类别的例子都会记住自己是由哪个Class 实例所生成
- 一个类的完整结构可以通过Class对象完全获得,通过一系列API
- Class的对象存储在堆中
- 字节码二进制数据放置在方法区,有些地方称为元数据(包括方法代码, 变量名、方法名、访问权限等。) https://www.zhihu.com/question/38496907[示意图]
package com.hspedu.reflection.class_;import com.hspedu.Cat;import java.util.ArrayList;/** * 梳理Class特征 */public class Class01 { public static void main(String[] args) throws ClassNotFoundException { ///看看Class类型图 //1. Class也是类,因此,Object类也继承了 //Class //2. Class类对象不是new,而是由系统创建的 //(1) 传统的new对象 /* clasloader public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } */ //Cat cat = new Cat(); //(2) 反射方式, 刚才老师没有debug到达 Classloader类 loadClass, 原因是,我没有取消Cat cat = new Cat(); 类只会加载一次,下面刚加载就不会再加载了 /* classloader类, 仍然是通过 Classloader类加载Cat类 Class对象 public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } */ Class cls1 = Class.forName("com.hspedu.Cat"); //3. 对于某一类的Class对象,内存中只有一个,因为该类只加载一次!!!!!!!!!! Class cls2 = Class.forName("com.hspedu.Cat"); System.out.println(cls1.hashCode()); System.out.println(cls2.hashCode()); // 相等 Class cls3 = Class.forName("com.hspedu.Dog"); System.out.println(cls3.hashCode()); }}
Class 常用的类别方法
package com.hspedu.reflection.class_;import com.hspedu.Car;import java.lang.reflect.Field;/** * 演示Class类常用方法 */public class Class02 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { String classAllPath = "com.hspedu.Car"; //1 . 获得Car类 对应的 Class对象 //<?> Java类型表示不确定 Class<?> cls = Class.forName(classAllPath); //2. 输出cls System.out.println(cls); //显示cls对象, Class对象是什么样的Class? com.hspedu.Car System.out.println(cls.getClass());///输出cls操作类型 java.lang.Class //3. 得到包名 System.out.println(cls.getPackage().getName());//包名 com.hspedu //4. 得到全类名 System.out.println(cls.getName()); // com.hspedu.Car //5. 通过cls创建对象实例 Car car = (Car) cls.newInstance(); System.out.println(car);//car.toString() //6. 属性是通过反射获得的 brand Field brand = cls.getField("brand"); System.out.println(brand.get(car));//宝马 //7. 通过反射给属性赋值 brand.set(car, "奔驰"); System.out.println(brand.get(car));//奔驰 //8 我希望你能得到所有的属性(字段) System.out.println(========所有字段属性=====); Field[] fields = cls.getFields(); for (Field f : fields) { System.out.println(f.getName());//名称 } }}
获取Class 类对象
1.前提:已知一个类的全类名,可以通过Class类的静态方法在类路径下使用 forName()
获取,可以抛出 ClassNotFoundException
,实例:
Class cls1 = Class.forName("java.lang.Cat");
应用场景:主要用于文件配置、读取类全路径、加载类.
2.前提:如果已知具体类别,通过类别 class 该方法最安全可靠,程序性能最高,例如:
Class cls2 = Cat.class
应用场景:主要用于参数传输,如通过反射获得相应的结构对象.
3.前提:已知某一类的实例,调用该实例 getClass() 获取Class对象的方法,例如:
Class clazz =对象.getClass();//操作类型
应用场景:通过创建一个好的对象获得Class对象.
4.其他方式
ClassLoader cls =对象.getClass(.getClassLoaderO;Class clazz4 = cl.loadClass(“类的全类名”);
5.基本数据 (int, char,boolean.float,double,byte,long,short) 通过以下方式获得Class对象:
Class cls =基本数据类型.class
6.基本数据类型对应的包装类可以通过.TYPE获得Class对象:
Class cls = 包装类.TYPE
package com.hspedu.reflection.class_;import com.hspedu.Car;/** * 以各种方式(6)演示Class对象 */public class GetClass_ { public static void main(String[] args) throws ClassNotFoundException { //1. Class.forName String classAllPath = "com.hspedu.Car"; ///通过读取配置文件获取 Class<?> cls1 = Class.forName(classAllPath); System.out.println(cls1); //2. 类名.class , 应用场景: 用于参数传输(例如,构造对象是通过反射获得的(例如) 介入参数是 类名.class) Class cls2 = Car.class; System.out.println(cls2); //3. 对象.getClass(), 应用场景,有对象实例 Car car = new Car(); Class cls3 = car.getClass(); System.out.println(cls3); //4. Class对象通过类加载器[4类加载器]获得 //(1)先得到类加载器 car ClassLoader classLoader = car.getClass().getClassLoader(); //(2)通过类加载器获得Class对象 Class cls4 = classLoader.loadClass(classAllPath); System.out.println(cls4); //cls1 , cls2 , cls3 , cls4 其实是同一个对象,因为一个类只能有一个class对象 System.out.println(cls1.hashCode()); System.out.println(cls2.hashCode()); System.out.println(cls3.hashCode()); System.out.println(cls4.hashCode()); //5. 基本数据(int, char,boolean,float,double,byte,long,short) Class对象按以下方式获得 Class<Integer> integerClass = int.class; Class<Character> characterClass = char.class; Class<Boolean> booleanClass = boolean.class; System.out.println(integerClass);//int //6. 包装类对应于基本数据类型,可以通过 .TYPE 获得Class对象 Class<Integer> type1 = Integer.TYPE; Class<Character> type2 = Character.TYPE; ///其他包装类BOOLEAN, DOUBLE, LONG,类似BYTE System.out.println(type1); System.out.println(integerClass.hashCode());// 是同一个 System.out.println(type1.hashCode());// 是同一个 }}
哪种类型的Classs? 以下类型的对象是Classs 对象
- 外部、成员内部、静态内部、局部内部、匿名内部
- interface:接口
- 数组
- enum:枚举
- annotation:注解
- 基本数据类型
- void
package com.hspedu.reflection.class_;import java.io.Serializable;/** * 哪种类型的Class对象演示? */public class AllTypeClass { public static void main(String[] args) { Class<String> cls1 = String.class;//外部类 Class<Serializable> cls2 = Serializable.class;//接口 Class<Integer[]> cls3 = Integer[].class;//数组 Class<float[][]> cls4 = float[][].class;///二维数组 Class<Deprecated> cls5 = Deprecated.class;//注解 Class<Thread.State> cls6 = Thread.State.class;//枚举 Class<Long> cls7 = long.class;//基本数据类型 Class<Void> cls8 = void.class;//void数据类型 Class<Class> cls9 = Class.class;// System.out.println(cls1); System.out.println(cls2); System.out.println(cls3); System.out.println(cls4); System.out.println(cls5); System.out.println(cls6); System.out.println(cls7); System.out.println(cls8); System.out.println(cls9); }}
类加载的基本说明
反射机制是java实现动态语言的关键,即通过反射实现动态加载。
- 静态加载:编译时加载相关类别。如果没有,就会报错,依赖性太强。
- 动态加载:如果运行时不需要加载所需的类别,即使没有,也不会报错,降低依赖性。
- 创建对象时(new) ///静态加载
- 当子类加载时,父类也加载 ///静态加载
- 调用静态成员时,调用静态成员 ///静态加载
- 通过反射 ///动态加载
Class.forName("com.test.Cat");
类加载过程图加载后,方法区存储类字节码二进制文件,堆积区创建相应类的Class对象。
各阶段加载任务完成注:这里是针对类的加载阶段,不是new阶段,所以是针对静态成员的。
加载阶段JVM现阶段的主要目的是将字节码从不同的数据源(可能是class文件、jar包甚至网络)转换为二进制字节流,加载到内存中,生成代表此类的java.lang.Class对象。
连接阶段-验证- 目的是确保 字节流中包含的Class文件的信息符合当前虚拟机的要求,不会危及虚拟机本身的安全。
- 包括:文件格式验证(是否从魔数oxcafebabe开始)、元数据验证、字节码验证和符号引用验证[例说明]。
- 可考虑使用
-Xverify:none
关闭大部分类验证措施,缩短虚拟机加载时间。
- JVM将默认初始化静态变量(对应数据类型的默认初始值,如0、OL、null、false等。).这些变量中使用的内存将在方法区分配。
package com.hspedu.reflection.classload_;public class clasload02 { public static void main(String[] args) { }}class A { //属性-成员变量-字段 //分析加载的链接阶段-准备 如何处理属性? //1. n1 是实例属性, 它不是静态变量,因此在准备阶段不会分配内存 //2. n2 它是一种静态变量,分配内存 n2 是默认初始化 0 ,而不是20。初始化子阶段为20。 //3. n3 是 static final 是 常量, 他不同于静态变量, 因为赋值一旦不变 因此n3 = 30 public int n1 = 10; public static int n2 = 20; public static final int n3 = 30;}
连接阶段-分析
虚拟机用直接引用代替常量池中的符号。这个过程是由jvm机自动完成的。
Initialization初始化- Java程序代码只有在初始化阶段才真正开始执行,这是执行()方法的过程。
- ()方法是编译器自动收集类中所有静态变量的赋值动作和静态代码块中的句子,并根据句子出现在源文件中的顺序合并。
- 虚拟机会确保一个类的()方法在多线程环境中被正确锁定和同步。如果多个线程同时初始化一个类,则只有一个线程执行该类的0)方法,其他线程需要阻止等待,直到活动线程执行()方法完成
正是因为这种机制,才能保证某一类在内存中, 只有一个Class对象。
package com.hspedu.reflection.classload_;/** * 演示加载-初始阶段 */public class clasload03 { public static void main(String[] args) throws ClassNotFoundException { //1. 加载B类,并生成 b的class对象 //2. 链接 num = 0 //3. 初始化阶段 // 所有静态变量的赋值动作和静态代码块中的句子依次自动收集,并合并 /* clinit() { // 按顺序合并 System.out.println("B 静态代码块被执行”); //num = 300; num = 100; } 合并: num = 100 */ //new B();//类加载 //System.out.println(B.num);//100, 如果直接使用类的静态属性,也会导致类的加载 ///看加载类时,有同步机制控制制 /* protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //正是因为这种机制,才能保证某一类在内存中, 只有一个Class对象 synchronized (getClassLoadingLock(name)) { //... } } */ B b = new B(); }}class B { static { System.out.println("B 静态代码块被执行”); num = 300; } static int num = 100; //如果直接调用静态变量,没有new对象,构造器就不会被执行 public B() {//构造器 System.out.println("B() 构造器被执行”); }}
第一组通过反射获取结构信息: java.lang.Class 类
- getName:获取全类名
- getSimpleName:获取简单的类名
- getFields:获取所有public修改的属性,包括本类和父类
- getDeclared Fields:在这一类中获取所有属性
- getMethods:所有Public修改方法,包括本类和父类
- getDeclaredMethods:在这个类别中获得所有的方法
- getConstructors:获取本类所有public修改的结构器
- getDeclaredConstructors:获取本类所有构造器
- getPackage:以Package的形式返回包信息
- getSuperClass:以Class的形式返回父类信息
- getlnterfaces:以Class[形式返回接口信息
- getAnnotations:以Annotation0的形式返回注解信息
- getModifiers: 以int形式返回修饰符[注:默认修饰符为0,public为1,private为2,protected为4,static为8, final是16], public(1) + static (8) =9
- getType:以Class形式返回类型(属性对应类class对象)
- getName:返回属性名
- getModifiers: 以int形式返回修饰符[注:默认修饰符为0,public为1,private为2,protected为4,static为8, final是16]
- getReturnType: 以Class的形式获得返回类型
- getName: 返回方法名
- getParameterTypes: Class[]返回参数类型的数组
- getModifiers: 以int的形式返回修饰符
- getName: 返回结构器名(全类名)
- getParameterTypes: Class[]返回参数类型的数组
package com.hspedu.reflection;import org.junit.jupiter.api.Test;import java.lang.annotation.Annotation;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;/** * 演示如何通过反射获取结构信息 */public class ReflectionUtils { public static void main(String[] args) { } @Test public void api_02() throws ClassNotFoundException, NoSuchMethodException { ///得到Class对象 Class<?> personCls = Class.forName("com.hspedu.reflection.Person"); //getDeclaredFields:在这一类中获取所有属性 //规定 说明: 默认修饰符 是0 , public 是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(”本类所有属性=” + declaredField.getName() + " 该属性的修饰符值=” + declaredField.getModifiers() + " 该属性类型=” + declaredField.getType()); } //getDeclaredMethods:在这个类别中获得所有的方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(”本类所有方法=” + declaredMethod.getName() + " 访问修改符值=” + declaredMethod.getModifiers() + " 该方法返回类型“该方法返回类型” + declaredMethod.getReturnType()); ////输出本方法的形状参数组 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(”该方法形参类型=” + parameterType); } } //getDeclaredConstructors:获取本类所有构造器 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(=======================); System.out.println(”本类所有构造器=” + declaredConstructor.getName());///这里的老师只是输出了名字 Class<?>[] parameterTypes = declaredConstructor.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(“构造器形参类型=” + parameterType); } } } // API的第一组方法 @Test public void api_01() throws ClassNotFoundException, NoSuchMethodException { ///得到Class对象 Class<?> personCls = Class.forName("com.hspedu.reflection.Person"); //getName:获取全类名 System.out.println(personCls.getName());//com.hspedu.reflection.Person //getSimpleName:获取简单的类名 System.out.println(personCls.getSimpleName());//Person //getFields: 获取public修改的所有属性,包括本类和父类 Field[] fields = personCls.getFields(); for (Field field : fields) {///增强for System.out.println(”这类和父类的属性=” + field.getName()); } //getDeclaredFields: 在这一类中获取所有属性 Field[] declaredFields = personCls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(”本类所有属性=” + declaredField.getName()); } //getMethods: 获取所有public修改方法,包括本类和父类 Method[] methods = personCls.getMethods(); for (Method method : methods) { System.out.println(”这类和父类的方法=” + method.getName()); } //getDeclaredMethods:在这个类别中获得所有的方法 Method[] declaredMethods = personCls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(”本类所有方法=” + declaredMethod.getName()); } //getConstructors: 获取所有public修改的结构器,包含本类 Constructor<?>[] constructors = personCls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(此类结构器=” + constructor.getName()); } //getDeclaredConstructors:获取本类所有构造器 Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(”本类所有构造器=” + declaredConstructor.getName());///这里的老师只是输出了名字 } //getPackage:以Package的形式返回 包信息 System.out.println(personCls.getPackage());//com.hspedu.reflection //getSuperClass:以Class的形式返回父类信息 Class<?> superclass = personCls.getSuperclass(); System.out.println(“父类class对象=” + superclass);// //getInterfaces:以Class[]的形式返回接口信息 Class<?>[] interfaces = personCls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(“接口信息=”” + anInterface); } //getAnnotations:Annotation[] 注解信息的形式返回 Annotation[] annotations = personCls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(“注解信息=”” + annotation);//注解 } }}class A { public String hobby; public void hi() { } public A() { } public A(String name) { }}interface IA {}interface IB {}@Deprecatedclass Person extends A implements IA, IB { //属性 public String name; protected static int age; // 4 + 8 = 12 String job; private double sal; //构造器 public Person() { } public Person(String name) { } //私有的 private Person(String name, int age) { } //方法 public void m1(String name, int age, double sal) { } protected String m2() { return null; } void m3() { } private void m4() { }}
对象是通过反射创建的
- 方法一:调用public修改的无参构造器
- 方法二:调用类中的指定结构
- Class相关方法
- newlnstance:调用类中的无参结构器,获取相应类的对象
- getConstructor(Class..clazz):根据参数列表,获取相应的public构造器对象
- getDecalaredConstructor(Class..clazz):根据参数列表获取所有相应的结构对象
- Constructor类相关方法
- setAccessible:暴破 (private结构器/方法/属性可以通过反射访问, 在反射前,都是纸老虎)
constructor.setAccessible(true);
- newlnstance(Object...obj):调用构造器
测试1:通过反射创建某种对象需要public 的无参构造
测试2:通过调用特定的构造器来创建某种对象
package com.hspedu.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;/** * 演示通过反射机制创建实例 */public class ReflecCreateInstance { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //1. 首先获得UserClass对象 Class<?> userClass = Class.forName("com.hspedu.reflection.User"); //2. 通过public的无参构造器创建实例 Object o = userClass.newInstance(); System.out.println(o); //3. 通过public的参构造器创建实例 /* constructor 对象就是 public User(String name) {//public有参构造器 this.name = name; } */ //3.1 首先得到相应的结构器 Constructor<?> constructor = userClass.getConstructor(String.class); //3.2 创建实例,并传入实参 Object hsp = constructor.newInstance("hsp"); System.out.println("hsp=" + hsp); //4. 通过非public的参构造器创建实例 //4.1 得到private的结构对象 Constructor<?> constructor = userClass.getDeclaredConstructor(int.class, String.class); //4.2 创建实例 ///暴力[暴力破解] , private构造器/方法/属性可以通过反射访问, 反射面前,都是纸老虎 constructor.setAccessible(true); Object user2 = constructor.newInstance(100, “张三丰”); System.out.println(user2=” + user2); }}class User { ///User类 private int age = 10; private String name = "教育"; public User() {//无参 public } public User(String name) {//public有参构造器 this.name = name; } private User(int age, String name) {//private 有参构造器 this.age = age; this.name = name; } public String toString() { return "User [age=" + age + ", name=" + name + "]"; }}
通过反射访问类成员访问属性
- Field对象根据属性名获得 f = clazz对象.getDeclaredField(属性名);
- 暴破:
f.setAccessible(true);
//f是Field - 访问
f.set(o, 值);////o表示对象sysoso(f.get(o));///o表示对象
- 注:如果是静态属性,set和get中的参数o可以写成nulll
package com.hspedu.reflection;import java.lang.reflect.Field;/** * 演示反射操作属性 */public class ReflecAccessProperty { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { //1. Student对应于Student类 Class对象 Class<?> stuClass = Class.forName("com.hspedu.reflection.Student"); //2. 创建对象 Object o = stuClass.newInstance();//o Studentt的操作类型是Student System.out.println(o.getClass());//Student //3. 通过使用反射获得 age 属性对象 Field age = stuClass.getField("age"); age.set(o, 88);////通过反射操作属性 System.out.println(o);// System.out.println(age.get(o));////返回age属性的值 //4. namee使用反射操作 属性 Field name = stuClass.getDeclaredField("name"); ///对name 进行暴破, 可操作privatete 属性 name.setAccessible(true); //name.set(o, "老"); name.set(null, "老~");// 因为name是static属性,所以在类加载时已经存在了,因此 o 也可以写null System.out.println(o); System.out.println(name.get(o)); // 获取属性值 System.out.println(name.get(null));// 获取属性值, name必须要求staticc是staticc }}class Student {//类 public int age; private static String name; public Student() {//构造器 } public String toString() { return "Student [age=" + age + ", name=" + name + "]"; }}
访问方法
- Method方法对象根据方法名和参数列表获取:
Method m = clazz.getDeclaredMethod(方法名, X.class);////所有这类方法的获取
- 获取对象:
Object o=clazz.newlnstance();
- 暴破:
m.setAccessible(true);
- 访问:
Object returnValue = m.invoke(o,实参列表);///o是对象
- 注:如果是静态方法,invoke的参数o可以写成null!
package com.hspedu.reflection;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * 通过反射调用演示方法 */public class ReflecAccessMethod { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { //1. 得到Boss类对应的Class对象 Class<?> bossCls = Class.forName("com.hspedu.reflection.Boss"); //2. 创建对象 Object o = bossCls.newInstance(); //3. 调用publichi方法 //Method hi = bossCls.getMethod("hi", String.class);//OK //3.1 获取hi方法对象 Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK //3.2 调用 hi.invoke(o, “教育~”); //4. 调用private static 方法 //4.1 得到 say 方法对象 Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class); //4.2 因为say方法是private, 因此,需要突破的原理与上述结构器和属性相同 say.setAccessible(true); System.out.println(say.invoke(o, 100, "张三", '男')); //4.3 因为say方法是static,也可以这样调用 ,可以输入null System.out.println(say.invoke(null, 200, "李四", '女')); //5. 如果方法在反射中有返回值,统一返回Object , 但是,他的操作类型和方法定义的返回类型是一致的 Object reVal = say.invoke(null, 300, "王五", '男'); System.out.println("reVal 运行类型=“ + reVal.getClass());//String ///演示返回案例 Method m1 = bossCls.getDeclaredMethod("m1"); Object reVal2 = m1.invoke(o); System.out.println("reVal2运行类型=“ + reVal2.getClass());//Monster }}class Monster {}class Boss {//类 public int age; private static String name; public Boss() {//构造器 } public Monster m1() { return new Monster(); } private static String say(int n, String s, char c) {///静态方法 return n + " " + s + " " + c; } public void hi(String s) {//普通public方法 System.out.println("hi " + s); }}
本章作业
练习1 :私有成员的变量通过反射修改
- Privatetest定义为私有name属性,属性值为hellokityty
- 公共方法提供getname
- 创建Privatetest类,利用Class类获取私有name属性,修改私有name属性值,并通过调用getname()打印name属性值
class PrivateTest{private String name = "hellokitty";public String getName(){return name;}}
package com.hspedu.reflection.homework;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Homework01 { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException { /** * 定义Privatetest类,具有私有name属性,属性值为hellokitty * 公共方法提供getname * 创建Privatetest类,利用Class类获取私有name属性,修改私有name属性值,打印name属性值并调用getname()的方法 */ //1. 得到 Class对象对应于Privatetetest类 Class<PrivateTest> privateTestClass = PrivateTest.class; //2. 对象实例的创建 PrivateTest privateTestObj = privateTestClass.newInstance(); //3. 获得name属性对象 Field name = privateTestClass.getDeclaredField("name");//name属性privatee属性 //4. 突破name name.setAccessible(true); name.set(privateTestObj, "天龙八部"; //5. 得到getname方法的对象 Method getName = privateTestClass.getMethod("getName"); //6. 因为getname() 是public,所有直接调用 Object invoke = getName.invoke(privateTestObj); System.out.println(name属性值=” + invoke);//天龙八部 }}class PrivateTest { private String name = "hellokitty"; //默认无参构造器 public String getName() { return name; }}
练习2 :使用反射和File完成以下功能
- Fileclass对象采用classforname方法获取
- 在控制台打印File类的所有结构
- File对象是通过newlnstance创建的,Eile对象是创建的:\mynew.txt文件提示:创建文件的正常写作方法如下:File file = new File("d:1laa.txt");//内存file.createnewfile;///方法可以真正创建文件
package com.hspedu.reflection.homework;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Homework02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /** * Fileclasss采用classforname方法获取 对象 * 在控制台打印File类的所有结构 * File对象是通过newinstance创建的,并创建E:\mynew.txt文件 */ //1. classforname方法获得Fileclasss 对象 Class<?> fileCls = Class.forName("java.io.File"); //2. 获得所有结构器 Constructor<?>[] declaredConstructors = fileCls.getDeclaredConstructors(); ///遍历输出 for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(File结构器=” + declaredConstructor); } //3. 指定的得到 public java.io.File(java.lang.String) Constructor<?> declaredConstructor = fileCls.getDeclaredConstructor(String.class); String fileAllPath = "e:\\mynew.txt"; Object file = declaredConstructor.newInstance(fileAllPath);///创建File对象 //4. 得到createnewfile 的方法对象 Method createNewFile = fileCls.getMethod("createNewFile"); createNewFile.invoke(file);//创建文件,调用的是 createNewFile ///file的操作类型是file System.out.println(file.getClass()); System.out.println(“成功创建文件” + fileAllPath); }}