反射的概念
反射(Reflection),允许程序获取任何类的内部信息,并且能够直接操作任意对象的内部属性及方法。反射机制是动态语言的关键。
反射的功能
- 判断任意对象所属的类
- 构造任意类的对象
- 判断任意一个类具有的成员变量和方法
- 调用任意一个对象的成员变量和方法
- 生成动态代理
反射的基础——“Class”类
import person.Person;
/*
* 《java.lang.Class》
* 反射机制的源头。
* 当创建一个类的时候,通过编译(javac.exe),生成对应的.class文件。
* 之后使用java.exe加载(JVM的类加载器进行加载)此.class文件,加载到
* 内存之后,就是一个“运行时类”,存在缓存区。
* 1.每一个运行时类只加载一次!
* 2.有了Class的实例之后,可以进行如下操作:
* ->创建对应的运行时类的对象
* ->获取运行时类的完整结构
* (属性、方法、构造器、父类、所在包、异常)
* ->调用运行时类的指定结构(属性、方法、构造器)
* ->反射的应用:动态代理
*/
public class TestClass {
public TestClass() {
}
public static void main(String[] args) throws Exception {
//1.调用运行时类本身的.class属性
Class<?> cls1 = Person.class;
System.out.println(cls1.getName());
Class<?> cls2 = String.class;
System.out.println(cls2.getName());
//2.通过运行时类的对象获取
Person p = new Person();
Class<?> cls3 = p.getClass();
System.out.println(cls3.getName());
//3.通过Class的静态方法获取
String className = "person.Person";
Class<?> cls4 = Class.forName(className);
System.out.println(cls4.getName());
}
}
反射相关的API
- java.lang.Class
代表一个类 - java.lang.reflect.Method
代表类的方法 - java.lang.reflect.Field
代表类的成员变量 - java.lang.reflect.Constructor
代表类的构造方法
反射机制运用举例
自定义的person.Person类,包含属性及其get、set方法,还有一个空的构造器。
public class Person {
public String name;
private int age;
public Person() {
}
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;
}
//更改了toString方法即改变了print呈现。
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public void show() {
System.out.println("show方法:"+this);
}
}
运用反射机制创建一个类,并调用其类内部方法
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.Test;
import person.Person;
/*
* 《反射机制》
* 动态语言的关键,允许程序获得任何类的内部信息,并且能够直接操作
* 任意对象的内部属性及方法。
*/
public class TestReflection {
public TestReflection() {
}
//以之前实例化对象的方式创建类
@Test
public void test1(){
Person p = new Person();
p.setAge(10);
p.setName("kola");
//因为是重写了toString方法,所以调用print是重写了的
System.out.println(p);
p.show();
}
//使用反射的方式创建一个对象,并调用其中的属性与方法
@Test
public void test2() throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
//创建一个person类的对象,可以使用<>修饰符限定;
Class<Person> cls1 = Person.class;
System.out.println(cls1);
//1.创建cls1对应的<>内的类对象
Person p1 = cls1.newInstance();
System.out.println(p1);
//创建一个不用修饰符修饰的Class类
Class cls2 = Person.class;
System.out.println(cls2);
//1.如果在创建Class时没有限定类,那么就要用强制转换符转换。
Person p2 = (Person)cls2.newInstance();
System.out.println(p2);
//2.通过反射调用类的指定属性
//这里只能用可访问的属性
Field f1 = cls1.getField("name");
//改成大写
f1.set(p1, "KOLA");
System.out.println(p1);
//若要访问被保护或私有的属性,使用.getDeclaredField
//并且设置.setAccessible(true)。
Field f2 = cls1.getDeclaredField("age");
f2.setAccessible(true);
f2.set(p1, 20);
System.out.println(p1);
//3.通过反射调运类的指定方法
//调用无输入的方法
Method m1 = cls1.getMethod("show");
m1.invoke(p1);
//调用有输入的方法
Method m2 = cls1.getMethod("setName", String.class);
m2.invoke(p1, "Kola");
m1.invoke(p1);
}
}
总结
在我看来反射机制是Java提供给程序员一种极度抽象地调用各种类及类属性和方法的机制,让程序员可以以一种统一的编写方式调用各种不同的类。这种方式十分切合动态代理的思想,这也就让动态代理成为了反射的一个重要应用。