黑马程序员 java高新技术- 类加载器和动态代理

------- android培训java培训、期待与您交流! ----------

类加载器

类加载器就是加载类的工具,java虚拟机JVM运行类的第一件事就是将这个类的字节码加载进来,
即类加载器工具类的名称定位和生产类的字节码数据,然后返回给JVM。

java.lang.ClassLoader 类

类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。
如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。
一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。 
数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。
数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载
器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。

方法

getClassLoader() 返回该类的类加载器。返回类型 ClassLoader  
getParent()  返回该类加载器的父类加载器。 
loadClass(String name)  加载名称为 name的类,返回的结果是 java.lang.Class类的实例。 
findClass(String name)  查找名称为 name的类,返回的结果是 java.lang.Class类的实例。 
findLoadedClass(String name)  查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。 
defineClass(String name, byte[] b, int off, int len)  把字节数组 b中的内容转换成 Java 类,返回的结果是 java.lang.Class类的实例。这个方法被声明为 final的。
resolveClass(Class<?> c)  链接指定的 Java 类。


类加载器的委托机制

系统提供的类加载器主要有下面三个:
引导类加载器(bootstrap class loader):用来加载 Java 的核心库,是用原生代码来实现的
扩展类加载器(extensions class loader):用来加载 Java 的扩展库。
系统类加载器(system class loader):根据Java应用的类路径CLASSPATH来加载Java类。

一般来说,Java 应用的类都是由system class loader来完成加载的。
可以通过 ClassLoader.getSystemClassLoader()来获取它。

除了系统提供的类加载器以外,可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器。

所有的类加载器都有一个父类加载器,通过getParent()方法可以得到。
系统类加载器的父类加载器是扩展类加载器,而扩展类加载器的父类加载器是引导类加载器;
对于开发人员编写的类加载器来说,其父类加载器是加载此类加载器Java类的类加载器。

因为类加载器Java类如同其它的Java类一样,也是要由类加载器来加载的。一般来说,
开发人员编写的类加载器的父类加载器是系统类加载器。类加载器通过这种方式组织起来,
形成树状结构。树的根节点就是引导类加载器。

Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,
BootStrap,ExtClassLoader,AppClassLoader,每个类负责加载特点位置的类。

类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,
所以必须有第一个类加载器,它就是BootStrap

Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器时,
需要为指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。

public class ClassLoaderTest {
	public static void main(String[] args) {
		//返回类的完整名称
		System.out.println(ClassLoaderTest.class);  
		
		//以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
		System.out.println(ClassLoaderTest.class.getName());  
		
		// 返回源代码中给出的底层类的简称。
		System.out.println(ClassLoaderTest.class.getSimpleName()); 
		
		//返回该类的类加载器。
		System.out.println(ClassLoaderTest.class.getClassLoader());
		
		//返回本类的类加载器的字节码对象
		System.out.println(ClassLoaderTest.class.getClassLoader().getClass());
		
		//返回本类的类加载器的字节码对象的名称
		System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName()); 
		
		//返回系统类的加载器--没有
		System.out.println(System.class.getClassLoader());  
	}
}
/*
结果
class com.wgxin.ClassLoaderTest
com.wgxin.ClassLoaderTest
ClassLoaderTest
sun.misc.Launcher$AppClassLoader@43be2d65
class sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
null
*/

动态代理

程序中:除了当前类能够提供的功能外,还需要补充一些其他功能,最容易想到的是权限过滤,
如有一个类的某个功能,但由于安全原因只有某些用户才能调用这个类,此时就可以做一个该类的代理类,
要求所有的请求必须通过这个代理类,由该类代理类做权限判断,如果安全,则调用实际类的功能开始处理,

那么为什么要把权限过滤单独做成代理类,而不在原来类的方法里面做权限判断?
因为在程序设计中类具有单一性原则,就是每个能能尽可能的单一,如果把权限判断放在原来的类里面,
那么该类除了有自身的业务逻辑外,还要处理权限判断的业务,如果业务逻辑或者权限判断需要改动,
那么整个类都需要改变,这显然不是一个好的设计。

动态代理的基础类

java.lang.reflect:
Proxy 类
InvocationHandler 接口
这两个提供了生成动态代理类的功能

方法:
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 
返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。 
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 
h - 指派方法调用的调用处理程序

方法 static boolean isProxyClass(Class<?> cl) 
当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。

下面的实例实现获取动态代理类的构造函数和其他函数。

import java.lang.reflect.*;
import java.util.*;
public class ProxyFunction {
	public static void main(String[] args) throws Exception {
		//获取代理类的字节码
		Class proxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
		//打印字节码名字
		System.out.println("字节码的名字:"+proxy1.getName());
		System.out.println("构造函数的列表---------------------------------");
		//创建一个对象集,从字节码中获取构造函数
		Constructor[] constructors = proxy1.getConstructors();
		//遍历对象集,获取构造函数的名字
		for (Constructor constructor : constructors) {
			String name = constructor.getName();
			StringBuilder sBuilder = new StringBuilder(name);
			sBuilder.append('(');

			//获取构造函数的参数类型
			Class[] params = constructor.getParameterTypes();
			for (Class param : params) {
				sBuilder.append(param.getName()).append(',');
			}
			if (params != null && params.length != 0) {
				sBuilder.deleteCharAt(sBuilder.length() - 1);
			}
			sBuilder.append(')');
			System.out.println(sBuilder.toString());
		}

		System.out.println("方法列表----------------------------------------");
		//创建一个方法数组,获取方法
		Method[] methods = proxy1.getMethods();
		for (Method method : methods) {
			//获取方法名称存入缓冲区
			StringBuilder sBuilder = new StringBuilder(method.getName());
			sBuilder.append('(');
			//将此对象所表示的方法类型封装为一个数组
			Class[] params = method.getParameterTypes();
			
			//遍历方法类型数组
			for(Class param : params){
				sBuilder.append(param.getName()).append(',');
			}
			if(params != null && params.length != 0) {
				sBuilder.deleteCharAt(sBuilder.length() - 1);
			}
			sBuilder.append(')');
			System.out.println(sBuilder.toString());
		}
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值