----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
l 泛型:
需要用泛型:设置集合的元素类型,反射的Constructor所属
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器阻止非法输入;
去类型化——编译器在编译完了之后会去掉类型信息,字节码之中不存在泛型;
new Collection<Integer>().getClass().
getMethod("add",Object.class).invoke(collection,"abc");
所以这句不会报错!
参数化的类型:表示已经规定过参数的类型,ArrayList<String>
ArrayList<String>之中的String称为类型参数的实例,实际参数类型
ArrayList是原始类型,<>念成typeof
l 参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译器报告警告
原始类型可以引用一个参数化类型的对象,编译器报告警告
l 参数化类型不考虑类型参数的继承关系:
Vector<String> v=new Vector<Object>();//错误
Vector<Object> v=new Vector<String>();//也错误
Vector v1=new Vector<String>();
Vector<Object> v=v1;——————这两句编译器是不会报错的,可以
l 创建数组实例时,数组的元素不能使用参数化的类型
Vector<Integer> vector[]=new Vector<Integer>[10];————错误
l 通配符“?”
操作任意类型的集合——
function(Collection<?> collection){
不能使用与参数类型有关的方法
for(Object obj:collection){SOP(obj);}
——可以,因为这里匹配的是元素和Object
}
通配符的扩展:
<? extends Number>只能匹配Number或者Number的子类
<? super Integer>只能匹配Integer或者Integer的父类
Class<?> x;
Class<String> y=String.class;
//y=x;——会报错
x=y;——没问题
Class<?> z=String.class;
l 泛型的实际应用:
java中的泛型类似于C++中的模板,但是仅限于表面,java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型信息保证类型安全,然后再生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型无法被接受,这会为java厂商升级器JVM造成障碍,所以,java泛型采用了可以完全在编译器中实现的擦除方法
————泛型方法
例子:泛型的定义
1,private static <T> T add(T x,T y){
return<T> x+y;
}
类型的推断:取元素类型的交集,算上父类
int x=add(3,5);————int
Number y=add(3.5,3);——Number
Object z=add(3,"abc");——Object
2,private static <T> void swap(T[] arr,int i,intj)
{
T temp=a[i];
a[i]=a[j];
a[j]=temp;
}
泛型<T>之中的T只能是引用数据类型,不能是基本数据类型——因为会引起混淆:
如上,如果进行swap(new int[3],3,5);会报告编译错误,这是因为编译器不会对new int[3]中的int进行自动拆装箱,因为new int[3]本身已经是对象,编译器不确定开发者的意图是要操作数组对象,还是要操作数组中的元素。
l 定义泛型和使用泛型都可以使用extends来限定
public <A extends Annotation> A getAnnotation(Class<A> annotationClass){};
<V extends Serializable&Comparable> void method(){};
l 泛型用于异常的时候,可以被throws但是不可以被catch:
就是——不可以catch(T e)——这里类型是不确定的,不被允许
l 多个泛型参数在尖括号之中用逗号隔开,<K,V>
练习:
1,自动将Object类型的对象转换成其他类型
public <T> T change(Object obj){
return (T)obj;
}
2,将任意类型的数组中的元素填充为相应类型的某个对象
public <T> void fillArray(T[] arr,T obj){
for(int i=0;i<a.length;i++){
a[i]=obj;
}
}
3,打印任意参数化类型的集合中的所有内容
————这种情况下,前面的通配符方案要比泛型方法更有效,当一个类型变量用来表达两个参数之间或者参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用泛型方法。
public static <T> void printCollection(Collection<T> coll){
for(Object obj:coll){
System.out.println(obj);
}
//用<?>通配符的话就不能再使用add方法来添加
}
4,把任意类型的集合中的数据安全的复制到相应类型的数组中
public static <T> void copy1(Collection<T> dest,T[] src){
}————这里用的是类型传递,直接传递给T
public static <T> void copy2(T[] dest,T[] src)
————这里两个实际参数的类型不一样的时候,用的是类型推断,
当有返回值的时候,优先通过返回值来确定泛型类型
————泛型类
如果类的实例对象中的多处都要用到同一个反省参数,级这些地方引用的泛型参数类型要保持一致,这时候就要采用泛型类的方式进行定义:
如:
public class GenericDao<T>{
private T fileld;
public void save(T obj){}
public T getById(int id){}
}
类级别的泛型是根据引用该类名是指定的类型信息来参数化类型变量的
如:
GenericDao<String> dao=null;
new GenericDao<String>();
注意:
在堆泛型类型进行参数化时,类型参数的实例化必须是引用类型,不能使基本类型
当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用,因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数
————静态成员是属于类的,类的泛型只有在建立对象之后才有具体类型
l 通过反射获得泛型的参数化类型
知道一个变量不能得到他的泛型类型,但是这个变量如果是某个方法的参数就可以通过方法的反射机制的方法来获取该方法参数的泛型类型
Type[] types=applyMethod.getGenericParameterType();
ParameterizedType pType=(ParameterizedType)types[0];
pType.getRawType();//原始类型
pType.getActulTypeArguments()[0];//获取实际类型
l 类加载器
要用到某个类的时候,会通过类加载器把对应的二进制码文件加载进内存
java虚拟机中可以安装多个类中加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:
BootStrap,ExtClassLoader,AppClassLoader
类加载器也是java类,因为其他事java类的类加载器本身也要被加载器来加载,显然必须有第一个类加载器不是java类,就是BootStrap——是C++编写的一段代码,存在于java内核中。
System类就是由BootStrap加载进内存的。
例子:
获取类加载器的名字:
ClassLoaderTest.class.getClassLoader().getClass().getName();
————sun.misc.Launcher$AppClassLoader
System.class.getClassLoader();
————null___其实是BootStrap把System加载进内存的,但是它不是类
类加载器之间的父子关系和管辖范围:
BootStrap ————>JRE/lib/rt.jar
ExtClassLoader————> JRE/lib/ext/*.jar
AppClassLoader————> ClassPath指定的所有jar或目录
↑
MyClassLoader————> 自定义的类加载器,可以进行加密解密
l 类加载器的委托机制:
1,当java虚拟机要加载一个类时:
首先当前线程的类加载器去加载线程中的第一个类
如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
2,每个类加载器加载类时,优先委托给其上级加载器
当所有父类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不找发起者加载器的子类加载器,因为没有getChild方法
习题:
能不能自己写一个类叫java.lang.System——为了不让我们写System类,类加载器采用委托机制,这样可以保证父类加载器优先找到类被使用,这样总是使用java系统提供的System类。
如果非要写,必须自己书写类加载器,而且需要屏蔽委托机制
l 自定义类加载器
ClassLoader是一个抽象类,之中有Loadclass()和findClass()方法
如果要用委托机制,就复写findClass方法;
如果要自己找,需要复写loadClass方法
要让自定义加载器加载指定类,必须保证父类加载器不能加载
模板方法设计模式:操作的流程都一样,但是自己的操作不一样
父类——>loadClass/findclass——>得到class文件——>转换成字节码文件defineClass——>得到字节码文件
子类1(自己干)
子类2(自己干)
l 一个类加载器的高级问题分析
编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布看到打印结果为WebAppClassloader
要使用tomcat
l 代理的概念与作用
要为已存在的类添加一些系统功能,例如异常处理,日志,计算方法的运行时间等;
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能代码
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端的程序,在配置文件中配置是使用目标类还是代理类,这样以后很容易切换;譬如,想要日志功能就配置代理类,否则配置目标类,这样增加系统功能很容易,以后允许一段时间后,想去掉系统功能也很容易
l 面向方面的编程AOP(Aspect oriented program)
AOP的目标就是要使交叉业务模块化,交叉业务的编程问题即为面向方面的编程
可以采用将切面代码移动到原始方法的周围,这与直接在方法里面开始调用的结果看起开是一样的
l 动态代理技术
JVM可以在运行期动态生成类的字节码,这种动态生成的类玩玩呗用做代理类,及动态代理类
JVM生成的动态类必须实现一个或者多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理
CGLIB库(开源项目)可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以要为一个没有实现接口的类生成动态代理类,可以使用CGLIB库
代理方法中可以在如下四个位置加上系统功能代码:
1,目标方法之前
2,目标方法之后
3,目标方法前后
4,目标方法的catch块之中
l 分析动态代理技术
Proxy类:getProxyClass(loader,class……interface)静态方法
Proxy.newProxyInstance(loader,class[] interfaces,hander);
工厂类BeanFactory+配置文件
代理类需要特殊处理--用代理的方法建立对象,
不是代理的话直接newInstance建立对象
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------