黑马程序员_泛型、类加载器、代理类、AOP

时间:2023-02-17 17:19:17

---------------------- android培训java培训、期待与您交流! ----------------------


 JDK1.5新特性——泛型

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

用到泛型的一些情况:集合、反射

泛型中的?通配符
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数有关的方法。

泛型中的?通配符的扩展:
限定通配符的上边界:
extends表示仅限于Number或限于Number的子类。
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
super表示仅限于Integer的父类,限定下边界,最低往下也必须是Integer。
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();

限定通配符总是包括自己。



泛型的应用:HashMap不能遍历 Map.Entry<>可以遍历
HashMap<String,Integer> maps = new HashMap<String,Integer>();
maps.put("jeff",22);
maps.put("ice",22);
maps.put("juice", 1);

Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
for(Map.Entry<String, Integer> entry : entrySet){
System.out.println(entry);
}

泛型的内部原理及更深应用:

ArrayList<String> arrList1 = new ArrayList<String>();
ArrayList<Integer> arrList2 = new ArrayList<Integer>();
System.out.println(arrList1.getClass() == arrList2.getClass());
结果为true
经过编译后arrList1与arrList2用.getClass()获取字节码是相同的。
用反射为其arrList2 加String类型的数据
arrList2.getClass().getMethod("add",Object.class).invoke(arrList2,"abc");
System.out.println(arrList2.get(1));
可以打印出对应位置的"abc"

定义泛型的方法:
用于放置泛型类型的参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
只有引用类型才能作为泛型类型的参数,不能为基本类型。

private static <T> void swap(T[] a,int i,int j){
T temp = a[i];
a[i] = a[j];
a[j] = temp;
}

类型推断
add(3,5);
Number x1 = add(3.5,3);
Object x2 = add(3,"abc");

除了在用用泛型是可以使用extends限定符,在定义泛型是也可以使用extends限定符。
例如,ClassgetAnnotation()方法的定义,并且可以用&来制订多个边界,如<V extends Senrializable & cloneable> void method(){}

普通方法、构造方法和静态方法中都可以使用泛型,编译器也不允许创建类型变量的数组。

也可以用类型变量表示异常,成为参数化的异常,可以用于方法的throws列表中,但不能用于catch子句中。
private static <T extends Exception> SayHello() throws T {}
在泛型中可以同时有多个类型的参数,在定义它们的尖括号中用逗号分开,例如:
public static<K,V> getValue(K key){return map.get(key);}



------自定义泛型需要再看视频------————————
自定义泛型方法的练习与类型推断总结:

编译器判断泛型方法的时机类型参数的过程称为过程类型判断,类型推断相当于知觉推断的,其实现方法是一种非常复杂的过程。
方法返回类型不确定,或参数类型不确定,则定义泛型的方法。
泛型方法前面返回值之前用<T>说明类型,就为泛型方法。
之后传的什么类型,实际就是什么类型。

根据调用反省方法时时机传递的参数类型或返回值的类型来推断,具体规则如下:
1、当某个类型变量只在整个参数列表中的所有参数和返回值中一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭感觉推断出来,及时直接根据调用方法是传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4)-->static <E> void swap(E[] a,int i,int j)
2、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法是这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) --> static <T> add(T a,T b)
3、当某个类型变量在整个参数列表中的所有参数和返回值中多处被应用了,如果调用方法是这多处的实际应用类型对应到了不同的类型,切没有使用返回值,这时候区多个参数中的最大交集类型,例如,下面的语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) --> static <T> void fill(T[] a,T v)
4、当某个类型变量在整个参数类型列表中的所有参数和返回值中的多数被应用了,如果调用方法是这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候有限考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译器将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x(3,3.5f) static <T> T add(T a,T b)

自定义泛型类的应用:
Dao缩写——data acess object-->crud 对数据库的增删改查 creat、read、update、delete

如果类的实例对象中多处都要用到一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型是,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型语法如下:

下面的方法可以对传进来什么类和返回什么类。
public class GenericDao<T>{
public void add(E x){
}
public E findById(int id){
}
public void delete(T obj){
}
public void delete(int id){
}
public void update(T obj){
}
public Set<T> findByConditions(String where){
return null;
}
........
}
在用的时候需要指定对应类型——类级别的泛型是i根据引用该类名时指定的类型信息来参数化类型变量的。
GenericDao<ReflectPoint> dao= new GenericDao<ReflectPoint>();

在泛型类型中需注意事宜:
1、在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
2、当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类共享的,所以静态成员不应该有类级别的类型参数。


通过反射获得泛型的实际类型参数:

——————————————————————



类加载器及其委托机制的深入分析:
java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个负责加载特定位置的类:
加载器名: BootStrap、 ExtClassLoader、 AppClassLoader
对应管辖范围:JRE/lib/rt.jar、Jre/lib/ext/*.jar、CLASSPATH指定目录的所有jar或目录
类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。


黑马程序员_泛型、类加载器、代理类、AOP


java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类的装载器对象时,需要为其制定一个父级类装载器对象或默认采用系统类装载器为其父类加载。



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

每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再取找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多少儿子,找哪个呢?



自定义类加载器的编写原理分析:
写自己的类加载器,字节的类加载器覆盖findClass(),就可以获取到对应的字节码,将这些字节码交给definClass()方法将其变成Class对象。

模板方法设计模式
父类-->loadClass/findClass()/得到Class文件转换成字节码-->definClass()把自己数组变成Class
子类1(自己干)
子类2(自己干)
总体流程在父类规定好,子类继承父类方法,父类定义局部细节为抽象方法,子类填写对应抽象方法,流程在父类中已经规定好。



编写对class文件进行加密的工具类:
编写和测试自己编写的解密类加载器:
类加载器的一个高级问题的实验分析:





分析代理类的作用与原理及AOP概念:

什么是代理:
要为已存在的多个具有相同接口的目标类各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、实物管理、等等。
代理原理可以理解为,
代理类(代理类中的方法为调用目标类中方法并扩展)和目标类实现同一个接口,客户端调用程序直接对接口操作。

AOP(Aspect Oriented Program面向方面的变成):
交叉业务:安全、实物、日志等功能要贯穿到好多个模块中。
AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果一样。


AOP

1.       系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

                 安全       事务         日志

a)         Student Service ----------|-----------|----------|----------

b)         CourseService  ----------|-----------|----------|----------

c)         MiscService   ------------|-----------|----------|----------

2.       用具体的程序代码描述交叉业务:

a)         method1           method2           method3

b)         {                           {                           {

c)         ------------------------------------------------切面

d)         ------      --------         ------

e)         ------------------------------------------------切面

f)          }                           }                           }

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

a)         --------------------------------------------------切面

b)         func1                  func2                  func3

c)         {                           {                           {

d)         ---                        ---                        ---

e)         }                           }                           }

f)          -------------------------------------------------切面

4.       使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。



动态代理技术:
JVM可以在运行期动态生成出类的字节码,这种动态生成类旺旺被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类和代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标和相应方法对外返回目标返回结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1、在调用目标方法之前
2、在调用目标方法之后
3、在调用目标方法前后
4、在处理目标方法异常的catch块中



代理架构图


黑马程序员_泛型、类加载器、代理类、AOP




创建动态类及查看其方法列表信息:
创建动态类的实例对象及调用其方法:
完成InvocationHandler对象的内部功能:
分析InvocationHandler对象的运行原理:
动态类生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
总结分析动态代理类的设计原理与结构:
运行原理:
客户端调用代理,代理的构造方法接受一个Handler,客户端调用代理的各个方法,代理各个方法将调用请求转给handler对象的invoke方法(可以添加新的方法),handler将对应请求分发给对应目标的相应请求。
把需要的功能封装成对象,即把切面你的代码封装成对象,以对象的形式传递,并执行对象就等于执行了切面的代码。
编写可生成代理和插入通告的通用方法:
实现类似spring的可配置的AOP框架:

工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接受代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util,ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
---------------------- android培训java培训、期待与您交流! ---------------------- 详细请查看: http://edu.csdn.net/heima