您的位置:首页 > 理论基础 > 计算机网络

传智播客 Java网络公开课四泛型与动态代理

2009-10-23 22:02 776 查看
张老师的公开课讲得比较深入,没有JAVA基础知识的人,理解起来是很困难的。不过对于有一定的基础,需要了解更深入的原理的人来说正好适合。听完前面的知识点后,感觉掌握了很多以前没注意的或者不知道的东西。接下来继续学习新特泛型。jdk中原有的集合类中存在的一个问题是:一个集合类中中能存入任何类型。虽然这看上去很方便,但是在实际应用中,我们往往只是需要装入特定类型,而不是什么类型都要往集合里装。因为什么都能往集合里装,所以拿出来的也需要自己转型,这带来了很多麻烦。JDK5则引入了泛型,以后希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,这样你就无法加入指定类型以外的数据,编译器会去除掉“类型”信息,使运行效率不受影响。例如:
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(1);
//collection.add(1L);
//collection.add("abc");
如上面代码一样定义集合,以后该集合就只能加入Integer类型的对象,由于自动打包机制的存在,相当于可以存储int型的数据。被注释掉的代码表明,这样定义以后,如果继续加入其它类型的数据将导致报错。同时,以后取出来也很方便,不用手动的转型。为了兼容JDK5以前的写法。下面两种写法都是可以的,但是会出现警告:
参数化类型引用一个原始类型的对象
Collection<String> c = new Vector();
原始类型引用一个参数化类型的对象
Collection c = new Vector<String>();
了解完简单的泛型后,再了解一下?通配符,它的灵活性相当的好。有时候要定义一个泛型的引用变量,例如,定义一个方法,打印一个集合中的数据,集合的类型变量该如何参数化呢?使用print(Collection c)又回避了泛型,不是我们想要的,也不能使用print(Collection<Object> c)进行方法定义,因为这个类型只能指向Object类型的,这样写就又回到集合中什么都能存放的原始形态了。如果定义成下面这样是不允许的,编译器报告错误:
Collection<Object> c = new ArrayList<String>();
因为两者类型不一样,所以装Object类型的集合是不能指向装String类型的集合的。由于Object是String的父类,这个理解起来有点困难。可以这样来理解,一个什么都能装的集合是一种类型,另外一个只能装String类型的集合也是一种类型,虽然这两个集合装的东西存在继承关系,但是这两个集合确不存在继承关系,它们属于不同的类型。为了解决这个问题,用?通配符就可以了。但是?通配符定义的变量主要用作引用,调用与参数化无关的方法,如果要调用与参数化相关的方法,那么必须在使用?通配符引用之前调用,否则就会报错。同时?通配符还定义该引用能指向的范围(无论是向上,还是下,都包括自己)。
向下限定通配符:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
向上限定通配符:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
在JAVA基础增强的课程中最后一个讲述的就是代理。代理解决什么问题,为已存在的目标类的方法增加一些系统功能。如果采用工厂模式和配置文件进行管理,以后也很容易就可以去掉增加的功能。以前经常接触静态代理,基本上就是写一个类将要代理的类的对象做成该类的成员变量,然后重新写这个类的所有方法,在这些方法里加入新的功能,甚至还能直接添加新的功能。现在能够让jvm帮我们创建代理类,这就是动态代理类。下面一段代码就是动态创建一个类:
Class clazz = Proxy.getProxyClass(ProxyTest.class.getClassLoader(), Collection.class);
System.out.println(clazz.getName());
让jvm帮我们创建一个类,我们需要为它提供哪些信息呢?第一个是这个类有哪些方法,即告诉它实现哪些接口,第二个是产生的类应该被哪个加载器加载,即类加载器对象,第三个就是它生成的类中的方法的代码是怎样的,需要告诉它,因此可以把需要的代码写在一个约定好了接口的对象的方法中,把对象传给它,它调用这个方法,即相当于插入了需要的代码。如下所示:
Class clazz = Proxy.getProxyClass(ProxyTest.class.getClassLoader(), Collection.class);
Constructor constructor = clazz.getConstructor(InvocationHandler.class);
Collection proxy = (Collection)constructor.newInstance(new InvocationHandler(){
Collection target = new ArrayList();
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before :" + method.getName());
Object retVal = method.invoke(target,args);
System.out.println("after :" + method.getName());
if(method.getName().equals("size")) return ((Integer)retVal)/2;
return retVal;
}
}
如代码所示,先通过Proxy类的getProxyClass方法产生并获得一个Class类型的对象class,然后就可以获得该动态代理类的构造方法。需要注意的是,动态代理类的构造方法的参数类型是InvocationHandler。关键在产生动态代理类的对象,它需要的构造方法的参数是用一个实现了InvocationHandler接口的匿名内部类产生的。在这个匿名内部类中,实现接口的invoke方法,在调用目标代理类的方法前后都可以加入需要的功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: