Java_反射和代理

1.Java反射

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

- RTTI(Run-Time Type identification)
- Java反射机制

RTTI:在运行时,识别一个对象的类型。当从数组中取出元素时,会自动将结果转型回Shape。这是RTTI最基本的使用形式,因为在Java中,所有的类型转换都是在运行时进行正确检查的。
大部分代码尽可能少的了解对象的具体类型,而是只与对象家族中的一个通用表示打交道。

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

Class类是Java一个基础类,每装载一个新类的时候,Java虚拟机就会在Java堆中,创建一个Class实例,这个实例就代表这个Class类型,通过实例获取类型信息。该类中的一些方法如下:

Method[] getMethods()  //返回实例中的所有方法
 Field[] getFields()   //返回实例中的成员变量
 Constructor[] getDeclaredConstructors() // 返回构造方法

Object类中的方法:
hashCode() / equals() / clone() / toString() / notify() / wait()

利用Class类来创建实例:

创建Class类的一个对象,返回一个类的引用
Class cls = Class.forName("Airplane")//返回一个类型
通过类的引用创建实例
cls.newlnstace(); //通过newlnstace创建实例,一般调用默认的构造函数

class Airplane {
				public String toString() {
					return("in airplane");
				}
}
public class Createlnstance {
				public static void main (String[] args) throws Exception{
								Class c1 = null;
								Object ap;
								c1 = Class.forName("Airplane"); //创建Class类的一个对象,描述了类Airplane
								System.out.print(c1);
								ap = c1.newlnstance;//创建实例的另外一种办法
								System.out.println(ap.toString());
				}
}

Java反射例子----Method类的invoke

Method类中 invoke 反射 invoke方法对带有指定参数的指定对象,调用由此Method对象表示的基础方法。

import java.lang.reflect.*;

public class ClassA {
		public void add (Integer param1, lnteger param2) { //定义成员方法add
			System.out.println(param1.intValue()+param2.intValue());
		}
		public void StringAdd(String abc) { //定义成员方法StringAdd
			System.out.println("out"+abc);
		}
		public static void main(String[] args) {
			try {
				Method mth = ClassA.class.getMethod("add",new Class[] {lnteger.class,lnteger.class});
				//声明方法类型的对象mth,属于类ClassA的方法
				mth.invoke(ClassA.class.newInstance(),new lnteger(1), new lnteger(2));
				//得到ClassA的实例,并传递参数,最后执行mth方法
				Method mth1= ClassA.class.getMethod("StringAdd",new Class[] {Sting.class});
				mth1.invoke(ClassA.class.newlnstance(),"--test");
			}catch (Exception  e) {}
		}
	}

代理模式:在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端或目标对象之间起到中介的作用。
如客户端由于权限原因不能访问目标对象。
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
代理模式一般涉及到的角色:

  • 抽象角色:声明真实对象和代理对象的共同接口。
  • 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供于真实对象相同的接口以便在任何时刻都能够替代真实对象。同时,代理对象可以在执行真实操作时,附加其他操作,相当于对真实对象进行封装。
    真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

静态代理的例子:

//真实对象和代理对象的共同接口
abstract class Subject {
	public abstrcat void request();
}
//真实角色
class RealSubject extends Subject {
	public void request () {
		System.out.println("From Real Subject");
	}
}
//客户端
public class Client {
	public static void main (String[] args) {
		Subject subject = new  ProxySubject();
		subject.request();
		}
}
//代理角色
class ProxySubject extends Subject {
	//代理角色对象内部含有对真实对象的引用
	private RealSubject realSubject;
	@Override
	public void request() {
		//在真实角色操作之前所附加的操纵
		preRequest();
		if (null==realSubject) {
			realSubject = new RealSubject();
		}//真实角色所完成的事情。
		realSubject.request();
		//在真实角色操作之后所附加的操作
		postRequest();
	}
	private void preRequest(){
		System.out.println("Pre Request.");
	}
	prevate void postRequest() {
		System.out.println("Post Request");
	}
}

静态代理优缺点:

  • 优点:业务类只需关注业务逻辑本身,保证了业务类的重用性。这也是代理的共有优点。
  • 缺点:代理对象的接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任

了。

~~

Java动态代理:

~~
java.lang.reflect.Proxy : Java动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

//该方法用于获得指定代理对象所关联的调用处理器
static lnvocationHandle getlnvocationHandler(Objcet proxy)

//该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass (ClassLoader loader, Class[] interfaces)

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

//该方法用于为指定类装载器,一组接口,及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interface, lnvocationHandler h)

java.lang.reflect.InvocationHandler : 调用处理器接口,自定义了一个invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。

Object invoke (Object proxy, Method method, Object[] args)
//该方法负责集中处理动态代理类上的所有方法调用。第一个参数是代理类实例,第二个参数是被调用的方法对象,第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托实例上执行

Javad动态代理实例

抽象角色:
interface Subject {
	public void request();
}
真实角色:实现了Subject的request()方法。
class RealSubject implements Subject {
	public RealSubject() {}
	public void request() {
	System.out.println("From real subjcet.");
	}
}
代理角色:必须继承lnvocaionHandler
import java.lang.reflect.Method;
import java.lang.reflect.lnvocationHandler;
class DynamicSubject implements InvocationHandler {
	private Object sub;
	public DynamicSubject() {}
	public DynamicSubject(Object obj) {
				sub=obj;
	}
	public Object invoke(Object proxy ,Method method, Obect[] args) throws Throwable {
				System.out.println("before calling" + method);
				method.invoke(sub,args);
				System.out.println("after calling " + method);
				return null;
		}
 }
 客户端调用:
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 public class Client {static public void main (String[] args) throws Throws {
 	RealSubject rs = new ReallSubject();  //指定被代理类
 	InvocationHandler ds = new DynamicSubject(rs);
 	Class cls = rs.getClass();//以下是一次性生成代理
 	Subject subject = (Subject) Proxy.newProxylnstance(cls.getClassLoader(),cls.getInterfaces(),ds);
 	subject.request();
 	}
 }
   

动态代理特点:

  • 包:如果所代理的接口都是public的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非public的接口,那么它将被定义在该接口所在包,这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;
    类修饰符:该代理类具有final和public修饰符,意味着它可以被所有的类访问,但不能被再度继承;
  • 类名:格式是 ”$ProxyN"
    其中N是一个逐一递增的阿拉伯数字,代表Proxy类第N次生成动态代理类,值得注意一点的是,并不是每次调用Proxy的静态方法创建创建动态代理类都会是的N值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会返回先前已经创建好的代理类的类对象,而不会再尝试区创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
  • 优点:于静态代理类相比,最大的好处是接口中声明的所有方法都被转移到调用处理器的一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多 的时候,可以进行灵活处理,而不需要像静态代理那样每一个方法中进行中转。
  • 缺点:仅支持interface代理

java反射扩展 ——JVM加载类原理

JVM类加载的种类:
JVM自带的默认加载器

  • 根类加载器: bootstrap , 由C++编写, 所有Java程序无法获得。
  • 扩展类加载器:由Java编写。
  • 系统类,应用类加载器: 由Java编写。
  • 用户自定义的类加载器: Java.lang.ClassLoader的子类,用户可以定制类的加载方式。每一个类都包含了加载它的ClassLoader的一个引用——getClassLoader()。
    如果返回的是null, 证明加载他的ClassLoader是根加载器bootstrap。

类的加载方式:

  • 本地编译好class中直接加载
  • 网络加载: java.net.URLClassLoader可以加载url指定的类
  • 从jar, zip 等等压缩文件加载类,自动解析jar文件找到class文件去加载
  • 从Java源代码文件动态编译为class文件。

类的加载步骤:
加载:
连接:包括 验证 准备 解析
类的初始化:

ClassLoader的加载顺序:
加载顺序:根加载器 =>扩展类加载器=>应用类加载器=>用户自定义加载器
如果到最后一层都再加载不了,就出现ClassNotFoundException异常。

ClassLoader加载Class的过程:

  • 1.检测此class是否载入过(即哎在cache中是否有次 Class),如果有跳到第8步,如果没有跳到第2步。
  • 2.如果parent classloader不存在(没有parent,拿parent一定是bootstrap classloader了),则跳到第4步。
  • 3.请求parent classloader载入,如果成功到第8步,不成功到第5步。
  • 4.请求JVM从bootstrap classloader中载入,如果成功到第5步。
  • 5.寻找Class文件(从与此classloader相关的类路径中寻找)。如果找不到则到第7步。
  • 6.从文件中载入Class,到第8步。
  • 7.抛出ClassNotFoundException。
  • 8.返回Class。

你可能感兴趣的:(Java_反射和代理)