【引入】
一、反射概述动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
主要动态语言: Object--c、c JavaScript PHP、 Python等
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如ava、C、C++。
虽然 java不是动态语言,但Java可以称之为“准动态语言”即java有一定的动态性我们可以利用反射机制获得类似动态语言的特性。java的动态性让编程的时候更加灵活!
反射(Reflect)是Java被视为动态语言的关键,反射机制允许程序在执行期间,通过Reflect ApI取得任何类的内部信息,同时直接操作对象的内部属性以及方法。
获取Class对象的三种方式:
- Class.forName("全类名"):将字节码加载进内存,返回class对象。
- 类名.class :通过类型属性class获取。
- 对象.getClass(): 通过方法调用获取。
public class Reflect1 {
public static void main(String[] args) throws ClassNotFoundException {
//1、forName() ------> source(源代码阶段)
Class> person1 = Class.forName("com.reflect.model.Person");
System.out.println(person1);
//2、.Class文件 ------> class (字节码阶段)
Class person2 = Person.class;
System.out.println(person2);
//3、getClass ------> runtime(运行时阶段)
Person person3=new Person();
Class extends Person> person3Class = person3.getClass();
System.out.println(person3Class);
System.out.println("=============================");
System.out.println(person1==person2);
System.out.println(person2==person3Class);
}
}
二、Class对象的主要功能概述
1、获取成员变量
| Field[] getFields() | 返回一个包含Field对象的数组,反映由这个class对象表示的类或接口的所有可访问的公共字段。 |
| Field getField(String name) | 返回一个Field对象,该对象反映由这个class对象表示的类或接口的指定公共成员字段。name参数是一个String,指定所需字段的简单名称。 |
| Field[] getDeclaredFields() | 返回一个Field对象数组,反映由这个class对象表示的类或接口声明的所有字段。这包括公共、受保护、默认(包)访问和私有字段,但不包括继承字段。 |
| Field getDeclaredField | 返回一个Field对象,该对象反映由这个class对象表示的类或接口的指定声明字段。name参数是一个String,它指定所需字段的简单名称。 |
public class Reflect2_1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Class cls = Class.forName("com.reflect.model.Person");
//1、变量
System.out.println("================1、变量===============");
Field msg = cls.getField("msg");
Person person=new Person();
System.out.println(msg.get(person));
msg.set(person,"hello");
System.out.println(msg.get(person));
Field age = cls.getDeclaredField("age");
age.setAccessible(true);
System.out.println(age.get(person));
age.set(person,12);
System.out.println(age.get(person));
}
}
2、获取成员方法
| Method getMethod(String name, Class>... parameterTypes) | 返回一个Method对象,该对象反映由这个class对象表示的类或接口的指定公共成员方法。name参数是一个String,用于指定所需方法的简单名称。parameterTypes参数是一个Class对象数组,按声明的顺序标识方法的形式参数类型。如果parameterTypes为空,则将其视为空数组。 |
| Method[] getMethods() | 返回一个包含Method对象的数组,该数组反映由这个class对象表示的类或接口的所有公共方法,包括由类或接口声明的方法和从超类和超接口继承的方法。 |
| Method[] getDeclaredMethods() | 返回一个包含Method对象的数组,该数组反映由这个class对象表示的类或接口的所有声明方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 |
| Method getDeclaredMethod(String name, Class>... parameterTypes) | 返回一个Method对象,该对象反映由这个class对象表示的类或接口的指定声明方法。name参数是一个String,用于指定所需方法的简单名称,parameterTypes参数是一个Class对象数组,按声明的顺序标识方法的形式参数类型。 |
public class Reflect2_2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Class cls = Class.forName("com.reflect.model.Person");
//2、方法
System.out.println("=================2、方法==============");
Method setAge = cls.getMethod("setAge", int.class);
System.out.println(person.toString());
setAge.invoke(person, 23);
System.out.println(person.toString());
}
}
3、获取构造方法
| Constructor>[] getConstructors() | 返回一个包含构造函数对象的数组,该对象反映由这个class对象表示的类的所有公共构造函数。如果类没有公共构造函数,或者类是数组类,或者类反映了原始类型或void,则返回长度为O的数组。 |
| Constructor | 返回一个构造函数对象,该对象反映由这个class对象表示的类的指定公共构造函数。parameterTypes形参是一个Class对象数组,按声明的顺序标识构造函数的形式形参类型。如果此Class对象表示在非静态上下文中声明的内部类,则形式形参类型包括显式的封闭实例作为第一个形参。 |
public class Reflect2_3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Class cls = Class.forName("com.reflect.model.Person");
//3、构造器
System.out.println("================3、构造器=============");
Person person2 = (Person) cls.newInstance();
System.out.println(person2.toString());
Constructor constructor = cls.getConstructor();
Person person3 = (Person) constructor.newInstance();
System.out.println(person3.toString());
}
}
三、反射应用实例
【需求】不改变代码,可以创建任意对象,运行任一方法
src目录下创建 info.properties文件
读取配置文件,获取对象,执行方法
public class Reflect3 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//1、获取配置信息
Properties pro=new Properties();
ClassLoader classLoader = Reflect3.class.getClassLoader();
InputStream input = classLoader.getResourceAsStream("info.properties");
pro.load(input);
//2、获取配置文件数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3、将该类加载进入内存
Class cls= Class.forName(className);
//4、通过反射执行方法
Cat cat= (Cat) cls.newInstance();
Method method = cls.getMethod(methodName,String.class);
method.invoke(cat,"你好!");
}
}
四、反射的应用场景
1、在运行时分析代码
Field类有一个getType方法,用来返回描述字段类型的一个对象,这个对象的类型同样是Class Method 和Constructor类有报告参数类型的方法,Method类还有一个报告返回类型的方法这三个类都有一个名为 getModifiers方法,它将返回一个整数,用不同的0/1位描述所使用的修饰符,如 public和 static另外,还可以利用javalang. reflect包中 Modifier类的静态方法分析 getModifiers返回的这个整数。例如,可以使用 Nodifier类中的 isPublic、 isPrivate或isFinal判断方法或构造器是 public、 private还是 final我们需要做的就是在 getModifiers回的整数上调用 Modifier类中适当的方法,另外,还可以利用 Modifier. toString方法将修饰符打印出来。
2、运行时检查对象
如利用Field[] getDeclaredFields() 方法,获取全部子段,编写一个通用的toString方法( )。
3、实现泛型数组操作代码
Class> getComponentType() :返回表示数组组件类型的Class。如果这个类不表示数组类,这个方法返回null。
五、反射破坏单例模式由于反射的特殊性,导致很多时候,需要单例模式的场景下,反射直接回破坏单例模式。如通过getConstructors( ) 获取构造器,利用构造器方法newInstance()直接创建对象,单例模式由此失效。但是有一种对象,天生就是单例模式——枚举
public enum SingleEnum {
INSTANCE;
private static SingleEnum singleEnum=null;
private SingleEnum(){
}
static class User{
private static User user=null;
private User(){
}
public static User getInstance() {
if(user!=null){
user=new User();
}
return user;
}
}
public SingleEnum getInstance() {
return INSTANCE;
}
}
class Newtest{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Constructor userConstructor = SingleEnum.class.getDeclaredConstructor(String.class,int.class);
userConstructor.setAccessible(true);
SingleEnum u1 = userConstructor.newInstance();
System.out.println(u1);
}
}
【运行结果】
无法通过反射创建对象。



