Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
目录
反射的概念
加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
动态语言与静态语言
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或者是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
主要动态语言:Object - C、C#、JavaScript、PHP、Python、Erlang
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。
主要静态语言:Java、C、C++
Java反射机制提供的功能
1. 在运行时判断任意一个对象所属的类
2. 在运行时构造任意一个类的对象
3. 在运行时判断任意一个类所具有的成员变量和方法
4. 在运行时获取泛型信息
5. 在运行时调用任意一个对象的成员变量和方法
6. 在运行时处理注解
7. 生成动态代理
反射类的应用
反射的强大之处就是可以调用类的私有结构
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
@Test
public void test() throws Exception {
//普通实例化不可以通过 ReflectionTest.Person 类的对象调用其内部私有结构,但是反射可以
//1. 通过反射,创建 ReflectionTest.Person 类的对象
Class clazz = Person.class;
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Tom",12);
Person p = (Person)obj;
System.out.println(p.toString());
//2. 通过反射,调用对象指定的属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
//set():参数1:指明设置哪个对象的属性;参数2:将此属性值设置为多少
age.set(p,10);
System.out.println(p.toString());
//调用方法
//getDeclaredMethod():参数1:指明获取的方法名称;参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show");
//invoke():参数1:方法的调用者;参数2:给方法形参赋值的实参
show.invoke(p);
//调用静态方法
Method shouDesc = clazz.getDeclaredMethod("showDesc");
shouDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则invoke()返回null
Object returnVal = shouDesc.invoke(null);
System.out.println(returnVal);
//3. 通过反射,调用 ReflectionTest.Person 类的私有结构
//调用私有构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person)cons1.newInstance("jerry");
System.out.println(p1);
//调用私有属性
//获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//保证当前属性可访问
name.setAccessible(true);
//获取、设置指定对象的此属性值
name.set(p1,"jack");
System.out.println(p1);
//调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1, "中国");
System.out.println(nation);
}
}
class Person{
private String name;
public int age;
@Override
public String toString() {
return "ReflectionTest.Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("hello,people");
}
private String showNation(String nation){
System.out.println("我的国籍是:" + nation);
return nation;
}
private static void showDesc(){
System.out.println("showDesc()");
}
}
>>> ReflectionTest.Person{name='Tom', age=12}
ReflectionTest.Person{name='Tom', age=10}
hello,people
showDesc()
null
ReflectionTest.Person{name='jerry', age=0}
ReflectionTest.Person{name='jack', age=0}
我的国籍是:中国
中国
如何看待反射与封装性
通过直接 new 的方式或反射的方式都可以调用公共的结构,开发中我们经常使用直接 new 的方式
什么时候用反射的方式呢?我们要充分考虑反射的动态特征,比如前后端的交互,我们经常在后端 Java 代码中使用反射的方式
反射机制与封装性并不矛盾,封装性只是不建议我们去使用私有结构,让我们使用方法来调用私有结构。反射机制则是可以直接调用,没有建议与不建议之说