JAVA进阶学习-反射机制

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操作,请务必注明出处,谢谢!
版权声明:本文为博主原创文章,未经博主允许不得转载。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦