(2011年java基础)高新技术(2)

本文详细介绍了Java泛型的概念、使用方法及其注意事项,并深入探讨了动态代理技术的实现方式,包括如何创建动态代理类及其实现的具体步骤。


泛型

什么是泛型?

l泛型是提供给javac编译器使用的,可以限定集合中的输入类型是1.5的新特性,

<高级泛型难度很高,所以讲解不会特别专业>


使用泛型有什么好处?

没有使用泛型时,只要是对象,不关是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定的类型,
集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换
这样就更方便


泛型的通配符

通配符的使用:?
 可以指向任意类型,
 总结,使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用于参数化无关的方法,不能调用与参数化有关的方法


 通配符的扩展:
 限定通配符的上边界:
 ?extends Number
 限定通配符的下边界:
 ?super Integer


 限定通配符总是包括自己


如何自定义泛型?

 定义泛型的方法
 1,只有引用类型才可以作为泛型的方法的实际参数


 2,用&来指定多个边界<V extends Serializable&cloneable> void mehtod(){}


 练习题:采用自定义泛型方法的方式打印出任意参数化类型的集合中的所有内容

在这种情况下,前面的通配符方案比泛型方法更有效,

当一个类型变量用来表示两个参数之间或者参数和返回值之间关系时,

即同一个类型变量在方法前面的两处被使用,

或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,

才需要使用泛型方法。


public static void printCollection(Collection<?> collection){
		//?可以指向任意类型,但是传入后不能调用一个与参数有关系的方法
		//这个是错误的,collection.add(1);
		System.out.println(collection.size());
		for(Object o :collection){
			
			System.out.println(o);
		}
//		collection.add(new Object());要使用这类型参数的时候,就要用T来定义泛型,否则可以用?	
	}
	
	public static <T>void copyArray(Collection <T> collection,T[] t){
		int i = 0;
		//这个方法就必要定义T,因为传入了两个参数用来做运算。
		for(T t1:collection){
			t[i] = t1;
			i++;
		}	
	}
	
<演示代码何时用T或者?>



3,类型推断T:
GeneriesTest2.copyArray(new Collection<String> , s2);//为什么集合可以指明T?因为泛型里面有规定了<String>
copyArrtoArr(new Date[10],new String[10]);//取共有父类,数组没有统一规定就取共有的

Number i1 = add(3.5,3);
Object o = add(3,"abc");
类型向上转型,取最大公约数

private static <T> T add(T x,T y){
		
		return null;
	}




编辑器判断泛型的方法的实际类型参数的过程成为类型推断,类型推断是相对于知觉推断的。
根据调用泛型方法时实际传递的参数类型或者返回值的类型来判断
      1,类型变量在一处应用,根据调用方法时该处的实际类型来确定
      2,多处应用,对应同一种类型,根据传入类型确定
      3,多处应用,对应不同类型,根据类型最大交集类型
      4,多处应用包括返回值,优先考虑返回值类型


定义泛型的类
public class GeneriesDao<T>
对所有成员进行统一约束



泛型的注意事项:


1,参数化类型与原始类型的兼容性

Collection<String> c = new Vector();
Collection c = new Vector<String>();

以上代码不安全,只会提警告,但是可以运行


2,参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>();
Vector<Object> v = new Vector<String>()
两者都是错误的

3,在创建数组实例时,数组的元素不能使用参数化类型

例如:
Vector<Integer> vectorlist[] = new Vector<Integer>[10];错误


4,在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型


5,当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和静态方法调用。

因为静态成员是被所有参数化的类共享的,
所以静态成员不应该有(类级别)的类型参数
public static void update2(T t){

}

------------------------------------------------------------------------------------------------------------------------------------------

类加载器

什么是类加载器?

就是加载类的工具,但是类加载器也是java类,但是有一个加载器不是java类,它是加载了必要的类的加载器叫BootStrap


加载器的究竟是怎样的一个顺序?如何加载?

Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在寻找和装载一个类的途中,先由BootStrap找寻有无适合的类,要是没有就向下查询;
ExtClassLoader
|---AppClassLoader

<以下是类加载器的结构图>


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


注意:
对于如果在上层目录找到的类,但是类里面已经错误了,就是无法运行了,那么就会抛出异常,不会在继续寻找


对于类加载器,我觉得暂时我才理解了它的原理,对于自定义加载器,可能日后翻阅相关书籍再补上这些知识。
------------------------------------------------------------------------------------------------------------------------------------------

代理


什么是代理?

其实按照教程来讲可能会有点不是很容易理解,其实java中的代理是对存在相同接口的目标类进行额外的功能扩展,而实现这个功能的类叫做代理类


代理类用处?

很多时候我们需要临时扩展指定接口的目标类的功能,换个说法,我们要测试或者检查这些目标类的功能,所以要在这些功能上加上自己的测试代码


那么如何增加呢?

这个问题就产生了代理

代理的实现:

编写一个与目标类具有相同接口的代理类,
代理类的每个方法调用目标类的相同方法,
并在调用方法时加上系统功能的代码。


AOP

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),
AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,
这与直接在方法中编写切面代码的运行效果是一样的
------------------------------------------------------切面
func1         func2            func3
{             {                {
....            ....              ......
}             }                }
------------------------------------------------------切面
l

在java中如何创建自己的代理类呢?

这就是涉及到动态代理技术

JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
JVM生成的动态类只能用作具有相同接口的目标类的代理
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中

package cn.itcast.day3;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

public class ProxyTest {

	/**
	 * @param args
	 */
	public static void main(String[] args)throws Exception  {
		// TODO Auto-generated method stub
		Class classProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		//获取实现参数接口的代理类
		System.out.println(classProxy1.getName());
		
		
		
		Constructor [] constructors = classProxy1.getConstructors();

		/*$Proxy0()
		$Proxy0(InvocationHandler,int)*/
		System.out.println("----------begin Conustructor list---------------");
		for(Constructor constructor : constructors){
			String s = constructor.getName();
			 StringBuilder sBuilder = new StringBuilder(s);
			sBuilder.append("(");
			Class [] classParams = constructor.getParameterTypes();
			for(Class clazzParams :classParams){
				sBuilder.append(clazzParams.getName()).append(",");		
			}
			if(classParams != null&&classParams.length != 0)
				sBuilder.deleteCharAt(sBuilder.length()-1);
			sBuilder.append(")");
			System.out.println(sBuilder.toString());
		}
		
		
		
		Method [] Methods = classProxy1.getMethods();
		
		/*$Proxy0()
		$Proxy0(InvocationHandler,int)*/
		System.out.println("----------begin Method list---------------");
		for(Method metod : Methods){
			String s = metod.getName();
			StringBuilder methodString = new StringBuilder(s);
			methodString.append("(");
			Class [] classParams = metod.getParameterTypes();
			for(Class clazzParams :classParams){
				methodString.append(clazzParams.getName()).append(",");		
			}
			if(classParams != null&&classParams.length != 0)
				methodString.deleteCharAt(methodString.length()-1);
			methodString.append(")");
			System.out.println(methodString.toString());
		}
		
		
		
		
		
		System.out.println("----------begin create instance Object list---------------");
		
		//创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。
		/*创建动态类的实例对象
		1,用反射获得构造方法
		2,编写一个最简单的InvocationHandler类
		3,调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
			打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
		4,将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。*/
		Constructor constructor  = classProxy1.getConstructor(InvocationHandler.class);
		class MyInvocationHandler1 implements InvocationHandler{

			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				
				return null;
			}
			
		}
		Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());
		proxy1.clear();
		//proxy1.size();空指针异常
		Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
			}	
		});
		
		;
		Collection proxy3 = 
			//返回的是动态代理类,里面的方法全是已经修改过的,所以表面形式是一样,但是内里的方法是不同的
			//利用了反射原理对方法进行重做,传入对象,方法和变量,利用method.invoke就可以实现代理
			//代理里面持有InvocationHandler对象,调用方法的时候就是调用InvocationHandler类里面的invoke方法
			//但是传入的Collection对象不是实现对象,要在InvocationHandler中创建需要操作的对象例如下列的ArrayList
			
			
			
			
			(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
					new Class[]{Collection.class}, 
					new InvocationHandler(){
				//直接创建实现参数接口的代理类对象
				//方法自动重写。
				
				ArrayList target = new ArrayList();//实际操作对象,proxy只是代理集合类,不是集合类
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
				//涉及三要素:objProxy对象、add方法、“abc”参数

						throws Throwable {
					
					long startTime = System.currentTimeMillis();
					Object retVal = method.invoke(target, args);
					long endTime = System.currentTimeMillis();
					System.out.println(method.getName()+"runtime....."+(endTime-startTime));
					
					return retVal;
					//return method.invoke(proxy,args) 死循环
				}
				
			});
		
		proxy3.add("123");
		//每掉用一次add就去找invacationHandler的子类传入生成动态类对象的invoke方法
		//涉及三要素:proxy3对象、add方法、“123”参数

		proxy3.add("dfg");
		//add方法运行原理:
		/*boolean add(Object obj){
			handler.invoke(this,this.getClass().getMethod("add"),obj);
		}*/

		proxy3.add("ffg");
		System.out.println(proxy3.size());
		
		/*
		 * 以上方法有两个不足,第一ArrayList要自身编译,第二功能代码要写固定
		 * 所以利用面向切面的编程,利用两个对象封装了,方法和操作对象给InvacationHandler
		 * */
		
		
		
		System.out.println("----------begin create instance autoObject2 list---------------");
		/*实现操作对象和封装了方法的对象传入*/

			
			
		final ArrayList target = new ArrayList();	
		Collection proxy4 = (Collection)getProxy(target,new MyAdvice());
		
		proxy4.add("aaa");
		proxy4.add("bbb");
		proxy4.add("ccc");
		System.out.println(proxy4.size());
		
		
	}

	private static Object getProxy(final Object target,final Advice advice) {
		//target是实际操作的对象,Advice是自身现实了功能的对象,
		//作为InvocationHandler子类对象invoke方法的调用
		//Advice是一个接口,这个接口就自定义了invoke主方法前和后的抽象方法,
		//我们只要实现其子类和复写方法即可,这样就不用把功能写死了
		Object proxy4 = 
			Proxy.newProxyInstance(target.getClass().getClassLoader(),
					target.getClass().getInterfaces(), 
					new InvocationHandler(){
				
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)

						throws Throwable {
					advice.beforeMethod(method);			
					Object retVal = method.invoke(target, args);	
					advice.afterMethod(method);
					
					
					return retVal;
				}
				
			});
		return proxy4;
		
		
		
	}

}


如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、
还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,
以后运行一段时间后,又想去掉系统功能也很容易。

<以下是工厂模式和配置文件的方式构造的代码>

package cn.itcast.day3.aopframework;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import cn.itcast.day3.Advice;

public class BeanFactory {
	
	//通过配置文件来选择创建代理或者是对象。Bean工厂..
	Properties props = new Properties();
	public BeanFactory(InputStream ips){
		try {
			props.load(ips);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public Object getBean(String name){
		String className = props.getProperty(name);
		Class clazz = null;
		Object bean = null;
		
			try {
				
				clazz  = Class.forName(className);
				bean = clazz.newInstance();
				
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
	
		
		//对javabean来说,必须有一个不带参数的构造方法
		if(bean instanceof ProxyFactoryBean){
			ProxyFactoryBean pfb = (ProxyFactoryBean)bean;
			
			try {
				Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
				Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
				pfb.setAdvice(advice);
				pfb.setTarget(target);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
			Object proxy = pfb.getProxy();
			return proxy;
		}
		return bean;
	}
}
<以下是图示>







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值