深入Java反射机制

目录

 

1 Java反射机制

1.1 RTTI

1.2 Java反射机制是啥

1.2.1 类Class

1.2.2 Object类

1.2.3 利用Class类来创建实例

2 Java静态代理

2.1 代理模式一般涉及到的角色

2.2 静态代理例子

2.3 静态代理的优缺点

2.3.1 优点

2.3.2 缺点

3 Java动态代理

3.1 java.lang.reflect.Proxy

3.2 java.lang.reflect.InvocationHandler

3.3 动态代理实例

3.4 动态代理的优缺点


1 Java反射机制

获取Java运行时的类型信息有两种方法

1. RTTI(Run-Time Type Identification):运行时的类型的识别

2. Java反射机制

1.1 RTTI

Shape类,三个子类,Circle,Square,Triangle

有一个数组,要去保存Shape类型的对象,实际上存的都是它的子类对象。

当从数组中取出元素时,会自动将结果转型回Shape。这是RTTI最基本的使用形式,因为在Java中,所有的类型转换都是在运行时进行正确的检查的。

大部分代码尽可能少地了解对象的具体类型,而至于对象家族中的一个通用表示打交道。如上述例子中的Shape。

1.2 Java反射机制是啥

运行状态中,能知道任意一个类的所有属性和方法;能调用任意一个对象的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

1.2.1 类Class

程序运行时,java系统会一直对所有对象进行运行时类型识别,这项信息记录了每个对象所属的类。通过专门的类可以访问这些信息。用来保存这些信息的类是Class类

Class类是Java一个基础类,每装载一个新类时,JVM就会在Java堆中,创建一个Class的实例,这个实例就代表这个Class类型,通过实例获取类型信息。

Method[] 	getMethods()

Field[] 	getFields()

Constructor<?>[] 	getDeclaredConstructors()

1.2.2 Object类

clone()
Creates and returns a copy of this object.

boolean 	equals(Object obj)

protected void 	finalize()
Called by the garbage collector on an object 

int 	hashCode()

void 	notify()

void 	notifyAll()

String 	toString()

void 	wait()

1.2.3 利用Class类来创建实例

构造Class对象有3种方式

 Class.forName()

Class clazz;
try {
	clazz = Class.forName("java.lang.String");
	//clazz.newInstance() is deprecated
	Object obj = clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
	e.printStackTrace();
}

类.class

Class stringClass = String.class;
System.out.println(stringClass);

Object.getClass()

String str = "s";
System.out.println(str.getClass());

运行结果:class java.lang.String

2 Java静态代理

某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的作用:为其他对象提供一种代理以控制对这个对象的访问。

2.1 代理模式一般涉及到的角色

抽象角色:声明真实对象和代理对象的共同接口

代理角色:代理对象角色内含有对真实对象的引用

真实角色:代理角色所代表的真实对象,即最终要引用的对象

2.2 静态代理例子

//真实对象和代理对象的共同接口
abstract class Subject {
	public abstract void request();
}

//真实角色
class RealSubject extends Subject{

	public void request() {
		System.out.println("From Real Subject!");
	}
}

//代理角色
class ProxySubject extends Subject{
	//代理对象内部含有对真实对象的引用
	private RealSubject realSubject;

	public void request() {
		//在真实角色操作之前所附加的操作
		preRequest();
		//真实角色所完成的事情
		if(null == realSubject)
			realSubject = new RealSubject();
		realSubject.request();
		//在真实角色操作之后所附加的操作
		postRequest();
	}
	
	private void preRequest() {
		System.out.println("Pre Request...");
	}
	
	private void postRequest() {
		System.out.println("Post Request...");
	}
}
public class Client {
	public static void main(String[] args) {
		Subject subject = new ProxySubject();
		subject.request();
	}
}

2.3 静态代理的优缺点

2.3.1 优点

业务类只需关注业务逻辑本身,保证了业务类的重用性。

2.3.2 缺点

代理对象的一个接口只服务于一种类型的对象,若代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。

若接口增加一个方法,除了所有实现类要实现这个方法外,所有代理类也需实现此方法。增加了代码维护的复杂度。

3 Java动态代理

3.1 java.lang.reflect.Proxy

Java动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其对象。

//Returns the invocation handler for the specified proxy instance
//获取指定代理对象所关联的调用处理器
static InvocationHandler 	getInvocationHandler(Object proxy)

//Returns the Class object for a proxy class given a class loader and an array of interfaces
//获取关联于指定类加载器和一组接口的动态代理类的类对象
static Class<?> 	getProxyClass(ClassLoader loader, Class[] interfaces)

//用于判断指定类对象是否是一个动态代理类
static boolean 	isProxyClass(Class<?> cl)

//用于为指定类加载器,一组接口及调用处理器生成动态代理类实例
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

3.2 java.lang.reflect.InvocationHandler

这是调用处理器接口,自定义了一个invoke()方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。

Objectinvoke(Object proxy, Method method, Object[] args)

该方法负责集中处理动态代理类上的所有方法调用。第一个参数是代理类实例,第二个参数是被调用的方法对象,第三个参数是调用参数

3.3 动态代理实例

interface Subject1 {
	public void request1();
}
public interface Subject2 {
	public void request2();
}
public class RealSubject implements Subject1,Subject2{

	public RealSubject() {}
	
	public void request1() {
		System.out.println("From real subject1.");
	}

	public void request2() {
		System.out.println("From real subject2.");
	}
}

class DynamicSubject implements InvocationHandler{
	
	private Object sub;
	
	public DynamicSubject() {}
	
	public DynamicSubject(Object obj) {
		sub = obj;
	}
	
    //第一个参数是代理类实例,第二个参数是被调用的方法对象,第三个参数是调用参数
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("before calling" + method);
		method.invoke(sub, args);
		System.out.println("after calling" + method);
		return null;
	}
}
public class Client {
	public static void main(String[] args) {
		
		RealSubject realSubject = new RealSubject();
		
		InvocationHandler dynamicSubject = new DynamicSubject(realSubject);

		Class clazz = realSubject.getClass();
		
		//生成动态代理实例
		Subject1 subject1 = 
				(Subject1)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), dynamicSubject);
		
		//自动调用dynamicSubject.invoke()
		subject1.request1();
		
		//只需改下接口就可以了
		Subject2 subject2 = 
				(Subject2)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), dynamicSubject);
		
		subject2.request2();
	}
}

3.4 动态代理的优缺点

深入学习Java的无聊日子


本文学习自

学堂在线-清华大学-许斌《JAVA程序设计进阶》

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值