您的位置:首页 > 职场人生

黑马程序员——java学习日记八

2015-10-22 20:22 435 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、泛型的由来

JDK5之前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。jdk5中的泛型允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生的问题,转变为编译时的问题,以此提高程序的可读性和稳定性。

二、泛型示例

import java.util.*;

class GenericDemo
{
public static void main(String[] args)
{
ArrayList<String> al = new ArrayList<String>();//创建一个集合

al.add("abc01");//向集合中添加元素
al.add("abc0991");
al.add("abc014");

Iterator<String> it = al.iterator();//遍历集合

while(it.hasNext())
{
String s = it.next();//取出集合中每个元素
System.out.println(s+":"+s.length());
}
}
}
1 自定义泛型类:
/*
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义Object来完成扩展。
现在定义泛型来完成扩展。
*/
class Utils<E> //泛型类如何定义--在类名后面写上一对<>里面是参数E
{
private E q;//参数E取代Object类

public void setObject(E q)
{
this.q =q;
}

public E getObject()
{
return q;
}
}
2 自定义泛型方法
//静态方法不可以访问类上定义的泛型
/*
泛型类定义的泛型,在整个类中有效。
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
*/
class Demo <T>
{
public void show(T t)
{
System.out.println("show:"+t);
}

public <Q> void print(Q q)
{
System.out.println("print:"+q);
}

public static <W> void method(W t)
{
System.out.println("method:"+t);
}
}


3 自定义泛型接口

//当一个类实现这个接口时,类就变成了一个泛型类
interface Inter<T> //定义方法类似于定义泛型类
{
void show(T t);
}
class InterImpl<T> implements Inter<T>
{
public void show(T t)
{
System.out.println("show :"+t);
}
}


4 类型变量的限定

有时,泛型类或泛型方法需要对类型变量加以约束,例如,将一个类型变量T限制为实现了Comparable接口,可以写成<T extends Comparable>

泛型的几个注意事项:

1 使用泛型时,泛型类型必须为引用类型,不能是基本数据类型。

2 泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上挡住向集合中插入非法数据,但编译器编译完带有泛型的java程序后,生产的class文件中将不能再带有泛型信息,这个过程称为“泛型擦除”。

3 泛型的基本术语,以ArrayList<E>为例:ArrayList<E>中的E称为类型参数变量,ArrayList<Integer>中的Integer称为实际类型参数,整个称为ArrayList<E>泛型类型,整个ArrayList<Integer>称为参数化类型(ParameterizedType)。

4 类型检查是针对引用,谁是一个引用,用这个引用调用泛型方法,就会对这个引用调用的方法进行类型检测,而无关它  真正引用的对象。例如 ArrayList<Integer> list = ... 这 里list就是一个引用。

5 各个具体的参数化类型之间不存在继承关系。例如ArrayList<Integer>和ArrayList<Object>没有任何关系。

三 程序中的代理

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

1、程序中的代理

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

2、代理的简单演示

示例:加入你拥有一个X类

class X
{
void sayHello()
{
syso:hello,itcast
}
}
XProxy---------X类的代理类
XProxy
{
void sayHello()
{
satrttime
X.sayHello();
endtime
}
}
3、动态代理技术

JVM可以在运行时期动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理,那么可以选择CGLIB库。

代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:

1 在调用目标方法之前

2 在调用目标方法之后

3 在调用目标方法前后

4 在处理目标方法异常的catch块中

示例:

创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数

编程列出动态类中的所有构造方法和参数签名

编程列出动态类中的所有方法和参数签名

创建动态类的实例对象:

 1 用反射获得构造方法

 2 编写一个最简单的InvocationHandler类

 3 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的示例对象传进去

 4 打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

 5 将创建动态类的实例对象的代理改成匿名内部类的形式编写

public class ProxyTest2 {

/*
Proxy--- Class<?> getProxyClass(类加载器,接口)---生产动态类
*/
public static void main(String[] args) throws Exception {
//生成一个动态类,该类中有Collection接口的方法
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy1.getName());

System.out.println("----------------begin constructors list------------");
Constructor[] constructors = clazzProxy1.getConstructors();//获取到动态类的构造函数集
for(Constructor constructor : constructors)
{
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = constructor.getParameterTypes();

4000
for(Class clazzParam : clazzParams)
{
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams != null || clazzParams.length != 0 )
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
/*打印结果:
----------------begin constructors list------------
$Proxy0(java.lang.reflect.InvocationHandler)
*/

System.out.println("----------------begin method list------------");
Method[] methods = clazzProxy1.getMethods();//获取动态类的方法集合
for(Method method : methods)
{
String name = method.getName(); //获取每一个方法的名称
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams)
{
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams != null || clazzParams.length != 0 )
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}

/*打印结果:
----------------begin method list------------
hashCode()
add(java.lang.Object)
clear()
equals(java.lang.Object)
toString()
contains(java.lang.Object)
addAll(java.util.Collection)
iterator()
size()
toArray()
toArray([Ljava.lang.Object;)
remove(java.lang.Object)
isEmpty()
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
getInvocationHandler(java.lang.Object)
getClass()
wait()
wait(long,int)
wait(long)
notify()
notifyAll()
*/

//------------------begin create instance object-----------------------
//利用反射获取构造函数
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
//编写一个最简单的InvocationHandler类
class MyInvocationHandler implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
//调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的示例对象传进去
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler());
//打印创建的对象和调用对象的没有返回值的方法
//System.out.println(proxy1.size())//报告异常,这是因为调用size()会导致调用invoke(),而它返回null,所以无法打印

Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){//使用匿名内部类方式获取一个动态类对象
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});

Collection proxy3 = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
//目标类----内部类中的一个成员
ArrayList target = new ArrayList();
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
Object retVal = method.invoke(target, args);
return retVal;
}
}
);

System.out.println(proxy3.getClass().getSuperclass().getName());//$Proxy0
proxy3.add("lili");
System.out.println(proxy3.size());
}

}
总结:让JVM创建动态类,需要给它提供如下信息:

1 生产的类中有哪些方法,通过让它实现一些接口的方式进行告知(生产动态类需要一些接口)

2 产生的类字节码必须有一个关联的类加载器对象(生产动态类需要一个类加载器)

3 生产的类中的方法的代码也由我们提供,把我们的代码写在一个约定好了接口对象的方法中,然后把接口对象传递给它(InvocationHandler对象),它调用我们的方法,即相当于插入了我的代码。提供执行代码的对象就是那InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

4 利用Proxy.newInstance方法可以直接一步就创建出代理对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: