JAVA 反射机制
Java Reflaction in Action中有这么一句话,反射是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。通俗的讲就是反射可以在运行时根据指定的类名获得类的信息。
平时开发中我们实例化一个类的过程一般是这样的:引入需要的“包类”的名称–>通过new实例化–>取得实例化对象
而JAVA得反射机制允许我们将这个过程反过来:实例化对象–>getClass()方法–>得到完整的“包类”名称
JAVA反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射机制的使用
我们通过一个简单的例子来说明反射机制的使用:
创建一个Creature类,如下:
public class Creature<T> {
public double weight;
public void breath(){
System.out.println("呼吸!");
}
}
创建一个Person类继承Creature类,为了让该类复杂一点,我们实现Comparable接口,声明一些简单的属性和方法,如下:
public class Person extends Creature<String> implements Comparable {
public String name;
private int 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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
public Person(String name) {
super();
this.name = name;
}
public Person(int age) {
super();
this.age = age;
}
public void show() {
System.out.println("我是一个人");
}
public void display(String nation) {
System.out.println("我来自:" + nation);
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Object o) {
return 0;
}
}
在Java中java.lang.Class是反射的源头,我们创建了一个类,通过编译(javac.exe),生成对应的.class文件,之后使用JVM的类加载器加载此.class文件,此.class文件加载到内存之后,就是一个运行时类,会被存放在缓存区,那么这个运行时类本身就是一个Class的实例!
如果类的实现没有发生改变,那么该类的.class文件只会被生成和加载一次,并且一个类在JVM中只会有一个Class实例。
我们创建一个测试类TestReflection来使用JAVA反射机制中的常用方法。
获取Class的实例
@Test
public void test() throws ClassNotFoundException {
// 1.调用运行时类本身的.class属性
Class myClass = Person.class;
System.out.println(myClass.getName());
// 2.通过运行时类的对象获取
Person p = new Person();
Class myClass1 = p.getClass();
System.out.println(myClass1.getName());
// 3.通过Class的静态方法获取
Class myClass2 = Class.forName("test.Person");
System.out.println(myClass2.getName());
// 4.通过类的加载器
ClassLoader classLoader = this.getClass().getClassLoader();
Class myClass3 = classLoader.loadClass("test.Person");
System.out.println(myClass3.getName());
}
四条输出语句输出的结果都是test.Person(包名.类名),这四种方法在使用上并没有优劣之分。
创建运行时类的对象
@Test
public void test4() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
String className = "test.Person";
Class myClass = Class.forName(className);
Person p = (Person) myClass.newInstance();//调用无参的构造方法
p.setAge(10);
p.setName("Tom");
System.out.println(p);
}
输出:
Person [name=Tom, age=10]
获取运行时类的属性
@Test
public void test5(){
Class myClass = Person.class;
//1.获取对应的运行时类的属性
//(1)getFields()只能获取到运行时类中及其父类中声明为public的属性
Field[] fields = myClass.getFields();
for(Field f : fields){
System.out.println(f);
}
System.out.println();
//(2)getDeclaredFields() 获取运行时类本身声明的所有属性
Field[] fields1 = myClass.getDeclaredFields();
for(Field f : fields1){
System.out.println(f);
}
System.out.println();
//2.获取属性的各个部分的内容
Field[] fields2 = myClass.getDeclaredFields();
for(Field f : fields2){
//变量名
System.out.print("属性名:"+f.getName());
//权限修饰符
int i = f.getModifiers();
String modifer = Modifier.toString(i);
System.out.print(" 权限修饰符:"+modifer);
//变量类型
System.out.print(" 属性类型:"+f.getType());
System.out.println();
}
}
输出:
public java.lang.String test.Person.name
public double test.Creature.weight
public java.lang.String test.Person.name
private int test.Person.age
属性名:name 权限修饰符:public 属性类型:class java.lang.String
属性名:age 权限修饰符:private 属性类型:int
获取父类和接口
@Test
public void test6(){
Class myClass = Person.class;
Class parentClass = myClass.getSuperclass();
System.out.println("Person的父类是:"+parentClass.getName());
Class inters[] = myClass.getInterfaces();
System.out.print("Person 实现的接口:");
for(Class c : inters){
System.out.println(c.getName());
}
}
输出:
Person的父类是:test.Creature
Person 实现的接口:java.lang.Comparable
调用运行时类的指定方法
@Test
public void test7() throws Exception{
Class myClass = Person.class;
// 1.创建myClass对应的运行时类Person的对象
Person p = (Person) myClass.newInstance();
// 2.通过反射调用运行时类的属性
// public
Field f1 = myClass.getField("name");
f1.set(p, "Tom");
// 非public
Field f2 = myClass.getDeclaredField("age");
f2.setAccessible(true);
f2.set(p, 10);
// 3.通过反射调用运行时类的指定方法
Method m1 = myClass.getMethod("show");
m1.invoke(p);
Method m2 = myClass.getMethod("display", String.class);
m2.invoke(p, "US");
System.out.println(p);
}
输出:
我是一个人
我来自:US
Person [name=Tom, age=10]
动态代理
在JAVA中要实现动态代理就要用到反射机制的特性,代码如下:
//业务接口
interface Subject {
void action();
}
// 业务接口实现类
class RealSubject implements Subject {
public void action() {
System.out.println("我是被代理类");
}
}
// 动态的代理类
class MyInvocationHandler implements InvocationHandler {
Object obj;
// 给被代理类的对象实例化,返回一个代理类的对象
public Object blind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), this);
}
// 当通过代理类的对象发起对被重写的方法的调用时,都会转化为对如下的invoke方法的调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnVal = method.invoke(obj, args);
return returnVal;
}
}
public class TestProxy {
public static void main(String[] args) {
// 被代理对象
RealSubject real = new RealSubject();
// 创建一个实现了InvocationHandler接口的对象
MyInvocationHandler handler = new MyInvocationHandler();
// 调用blind方法,动态的返回一个同样实现了real所在类实现的接口的代理类的对象
Object obj = handler.blind(real);
Subject sub = (Subject) obj;// 此时sub就是代理类对象
sub.action();// 调用的是MyInvocationHandler类的invoke方法
}
}
笔者水平有限,若有错漏,欢迎指正,如果转载以及CV操作,请务必注明出处,谢谢!
版权声明:本文为博主原创文章,未经博主允许不得转载。