黑马程序员——高新技术—代理类与类加载器

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


        在多线程中,不同功能模块在被各个不同线程进行调用时,在一个线程中,各模块需要用到的数据要保持一致性,而在另一个线程中被调用,这些数据又有所不同。此时可

用一个Map集合来实现此类需求,此Map集合的键为线程类,值为共享数据,

即事先在线程运行时,将当前线程与其对应的共享数据作为键值对存入Map集合中,当再次进行各线程操作时,在此Map集合中调出当前线程所对应的数据分配给各调用模块。

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class ThreadScopeShareVar {
	static Map<Thread,Integer> map=new HashMap<Thread,Integer>();
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(int i=0;i<2;i++){
			final int data=new Random().nextInt();
			new Thread(new Runnable(){

				@Override
				public void run() {
					// TODO Auto-generated method stub
					map.put(Thread.currentThread(), data);
					System.out.println(Thread.currentThread().getName()+" has put data:"+data);
					A();
					B();
				}
				
			}).start();
		}
	}
	public static void A(){
		int data=map.get(Thread.currentThread());
		System.out.println("A has gotten data from "+Thread.currentThread().getName()+":"+data);
	}
	public static void B(){
		int data=map.get(Thread.currentThread());
		System.out.println("B has gotten data from "+Thread.currentThread().getName()+":"+data);
	}
}


一、创建任务

        任务就是一个实现了Runnable接口的类。

        创建的时候实run方法即可。

二、执行任务

        通过java.util.concurrent.ExecutorService接口对象来执行任务,该接口对象通过工具类java.util.concurrent.Executors的静态方法来创建。

        Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService和 Callable 类的工厂和实用方法。

1、创建ExecutorService

        通过工具类java.util.concurrent.Executors的静态方法来创建。

        Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。

        比如,创建一个ExecutorService的实例,ExecutorService实际上是一个线程池的管理工具:

        ExecutorService executorService = Executors.newCachedThreadPool();

        ExecutorService executorService = Executors.newFixedThreadPool(6);

        ExecutorService executorService = Executors.newSingleThreadExecutor();

2、将任务添加到线程去执行

        当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。

三、关闭执行服务对象

        executorService.shutdown();

五、获取任务的执行的返回值

        在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被 ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的

(<T>task) 方法来执行,并且返回一个 <T>,其中<T>,是表示任务等待完成的 Future。

        public interface Callable<V>返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。Callable 接口类似于,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经

过检查的异常。

        类包含一些从其他普通形式转换成 Callable 类的实用方法。

        Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。

        当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。

        同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。

        代理类Proxy,

        程序中的代理:为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如异常处理、日志、计算方法的运行时间、事务管理等等,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。

        如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置使用目标类、还是代理类,这样以后很容易切换 AOP面向方面编程: 

        系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面 。



        类加载器用于加载类,各类加载器查找类的目录不同,例如AppClassLoad默认在classpath目录下进行类的查找,ExtClassLoader在ext文件夹下进行查找。最顶层的类加载器为BootStrap,类加载器使用委托机制,即由底层上交到顶层进行查找,顶

层加载器若没找到,则向下逐层查找。这样便于统一管理。

        自定义类要被放在AppClassLoader下,保证了委托机制。自定义类加载器需要指定查找目录,覆盖findClass方法。为了保证委托机制,loadClass方法可以不必覆盖。

        这里先定义一个MyClassloader,继承了ClassLoader。MyClassLoader中定义了一个对类文件进行加密的方法,一会儿要加载的便是进行了加密的这个类文件,故解密函数写在了findClass方法中。这样做更能体现自定义加载器的作用,因为类文件

加密后,其他类加载器加载时,findClass方法中的defineClass方法会抛出异常,故只有解密正确的类加载器才能加载成功。

        类加载器的委托机制 

        当java虚拟机要加载一个类时 到底指派哪个类加载器去加载呢?首先当前线程的类加载器去加载线程中的第一个类;如果类A中引用了类B,java虚拟机将使用加载类A的类加载器来加载类B;还可以直接调用ClassLoader.loadClass()方法来指定某个

类加载器去加载某个类。 

        每个类加载器加载类时,又先委托给其上级类加载器,当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了则抛出ClassNotFoundException,不会去找发起者类加载器的儿子。避免出现相同的字节码

        自定义类加载器,继承自ClassLoader类,loadClass方法与findClass方法,我们要做的是覆盖findClass方法,以便保留loadClass的流程,defineClass方法,父类loadClass/findClass/得到的class文件转换成字节码defineClass 范例:自定义

 个类加载器对加密的class文件进行解密,类加载器的一个高级问题的分析(Servlet):

        父类加载器加载的类无法引用只能被子类加载器加载的类。







package cn.itcast;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class MyClassloader extends ClassLoader{

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		String srcPath=args[0];
		String destDir=args[1];
		String fileName=srcPath.substring(srcPath.lastIndexOf("\\"));
		String destPath=destDir+fileName;
		FileInputStream fis=new FileInputStream(srcPath);
		FileOutputStream fos=new FileOutputStream(destPath);
		cypher(fis, fos);
		
	}
	private String classDir;
	public MyClassloader(String classDir){
		this.classDir=classDir;
	}
	@Override
	protected Class findClass(String name) throws ClassNotFoundException {
		String classPath=classDir+"\\"+name+".class";
		try {
			FileInputStream fis=new FileInputStream(classPath);
			ByteArrayOutputStream baos=new ByteArrayOutputStream();
			cypher(fis,baos);
			byte[] bytes=baos.toByteArray();
			Class clazz=defineClass(bytes, 0, bytes.length);
			return clazz;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return super.findClass(name);
	}
	public static void cypher(InputStream ips,OutputStream ops)throws Exception{
		int b=-1;
		while((b=ips.read())!=-1){
			b=b^255;
			ops.write(b);
		}
	}

}

        主函数用于对待加载的类进行加密,生成加密后的类。运行前需要配置主函数的参数列表String[] args,args[0]与args[1],分别为待加载类的绝对路径与加密文件输出目录。

        这里待加载的类为ClassLoaderAttachment


<span style="font-size:10px;">import java.util.Date;

public class ClassLoaderAttachment extends Date {
	public ClassLoaderAttachment(){
		super();
	}
	@Override
	public String toString(){
		return "hello,java";
	}
}</span><span style="font-size: 18px;">
</span>


        最后一个java文件用于建立MyClassLoader对象,进行加密类的加载:


<span style="font-size:10px;">package cn.itcast;

import java.util.Date;

public class ClassLoaderDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		/*System.out.println(ClassLoaderDemo.class.getClassLoader().getClass()
				.getName());
		System.out.println(System.class.getClassLoader());
		ClassLoader cl=ClassLoaderDemo.class.getClassLoader();
		while(cl!=null){
			System.out.println(cl.getClass().getName());
			cl=cl.getParent();
		}*/
//		System.out.println(new ClassLoaderAttachment().toString());
		MyClassloader mcl=new MyClassloader("userfolder");
		Class clazz=mcl.findClass("ClassLoaderAttachment");
		Date date=(Date)clazz.newInstance();
		System.out.println(date);
	}

}</span><span style="font-size: 18px;">
</span>















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值