01.了解和入门注解的应用
(1)@SuppressWarnings("deprecation")//方法过时了,该注解告诉编译器编译时不要提示(压缩警告)
(2)@Deprecated//声明该方法已经过时了
(3) @Override//注解这个方法是覆盖父类方法
02. 自定义注解及其应用
(1)@Retention(注解生命周期)元注解的三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应java源文件-->class文件-->内存中的字节码。
(2)@Target(ElemenType.METHOD)表示注解可以哪个成分上面
03.注解的总结
注解相当于一种标记,加了注解就等于打上了某种标记,这些标记在javac编译器编译时起作用。标记可以加在包,类,字段。方法,方法的参数以及局部变量上。
六、java5的泛型
04.泛型的内部原理及应用
(1)没有使用泛型时,只要是对象,不管是什么类型的对象 ,进都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全,并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便
(2)在JDK1.5中,你还可以按原来的方式将各种不同类型的数据装到一个集合中 ,但编译器会报告unchecked警告。
(3)泛型是提供给Javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型信息,使程序运行效率不爱影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法方法即可。
(4)ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
—— 整个称为ArrayList<E>泛型类型.
——ArrayList<E>中的E称为类型变量或类型参数。
—— 整个ArrayList<Integer>称为参数化的类型。
——ArrayList<E>中的Inetger称为类型参数的实例或实际类型参数。
——ArrayList<E>中的<>念 typeof.
—— ArrayList称为原始类型。
(5)参数化类型与原始类型的兼容性:
—— 参数化类型可以引用一个原始类型的对象。编译报告警告,例如,
Collection<String> c = new Vecton()
—— 原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vecton<String>()
(6)参数化类型不考虑类型参数的继承关系:
——Vector<String> v =new Vector<Object>();//错误
——Vector<Object> v =new Vector<String>();//也错误
(7)在创建数组实例时,数组的元素不能使用参数化的类型,例如
Vector<Integer> VectorList[] = new Vector<Integer>[10];
05.泛型的通配符扩展应用
(1)使用?通配符可以引用其它各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
(2)通配符的扩展(方法也可以使用,并且可以用&来指定多个边界)
—— 限定通配符的上边界:
Vector<? extendsNumber>x = new Vector<Integer>();
表示 ?通配仅限于Number和其子类。
—— 限定通配符的下边界:
Vector<? superInteger>x = new Vector<Number>();
表示 ?通配仅限于Integer和其父类。
06.泛型集合的综合应用案例
package cn.itcast.day2;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class GenericTest {
public static void main(String[] args) {
HashMap<String,Integer> maps = new HashMap<String,Integer>();
maps.put("li",23);
maps.put("zhang",22);
maps.put("wang",21);
//要对进行迭代只能得到一个Set集合,通过Set集合进行迭代
//定义一个Set集合,Set集合的元素是一个个Map.Entry<String,Integer>类型数据
Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
//对Set集合进行迭代
for(Map.Entry<String, Integer> entry:entrySet){
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
}
07.自定义泛型及其应用
(1)泛型参数只能是引用类型不能是基本类型,例子说明如下package cn.itcast.day2;
public class GenericTest2 {
publicstatic void main(String[] args){
swap(newString[]{"asb","egder","dfeg"},0,2);
/*泛型参数只能是引用类型不能是基本类型,但是下面的add()为什么可以用呢
swap(newint[]{1,2,3},0,2);
因为1.5有自动装箱功能,它会把int转成Integer,而int[]是个对象,
有可能你要的就是int[]数组所以不自动装箱
*/ add(3,5);
}
privatestatic <T> void swap(T[] a,int i,int j){
Ttmp=a[i];
a[i]=a[j];
a[j]=tmp;
}
privatestatic<T> T add(T x,T y){
returnnull;
}
}
(2)用下面的代码说明对异常如何采用泛型:
private static <T extends Exception> sayHello() throws
{
try{
}catch(Exception e){
throw (T)e;
}
}
(3)在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分隔,例如
public static <第一个,第二个,第三个,...,最后一个> V getValue(K key){returnmap.get(key)}
(4)如果一个类里面的泛型方法每次执行都用同一类型的的对象,那么就可以把此类定义为泛型类。
08.通过反射获得泛型的实际类型参数,例子如下
public class GenericTest2 {
public static void main(String[] agrs){
//获取applyVector中的Vector的字节码
Method applyMethod = GenericTest.class.getMethod("applyVector",Vector.class);
//获取参数的泛型类型,可能有多个参数所以用数组接收
Type[] types = applyMethod.getGenericParameterTypes();
//调用方法,传递一个参数types[0]
ParameterizedType pType = (ParameterizedType) types[0];
//得到原始的类型
System.out.println(pType.getRawType());
//得到实例参数类型
System.out.println(pType.getActualTypeArguments()[0]);
}
public static void applyVector(Vector<Date> v1){
}
}
七、类加载器
09.简要
(1)类加载器就是加载类的工具,本身也是一个Java类。
(2)Java虚拟机中中可以安装多个加载器,系统默认三主要的类加载器,每个类负责加载特定位置的类:BootStrap、ExtClassLoader、AppClassLoader
(3)BootStrap是第一存在一个加载器是BootStrap是开启的时候就存在不需要加载。
10.每个类加载器加载类时,又先委托级其上级类加载器。
(1)当所有祖宗类加载器没有加载到类,回到发上发起者类加载器,还加载不了,则抛
ClassNotFoundException
八、动态代理技术11.程序中的代理
(1)要为已存在的多个具有相同接口的目标类的个个方法增加一些系统功能,例如异常处理、日志、计算方法的运行时间、事务管理、等等。
(2)编写一个目标类具有相同接口的代理类的每个方法调用目标类的相同方法,并在调用
方法时加上系统功能的代码。
12.面向方面的编程(Aspect Orienten Program,简称AOP)
—— 系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面。
13.动态代理技术
(1) 要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!
(2)JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
(3)JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的支柱类只有用作具有相同接口的目标类的代理。
(4)CGLIB库可以动态生成一个类的子类,一个类的子类也可以用途该类的借,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
(5)代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下
四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
15.分析JVM动态生成的类,例子如下
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException{
//获得一份动态类的字节码
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();
//拼一个字符串列表,初始值为name,StringBuffer(多线程)和StringBuilder(单线程)都是动态的对字符串添加数据,区别在线程多少的应用上
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
//返回一个构造函数的所有参数
Class[] clazzParams = constructor.getParameterTypes();
//迭代所有构造函数的参数
for(Class clazzParam : clazzParams){
//把一个参数添加到sBuilder动态字符串中,并在后面追加','
sBuilder.append(clazzParam.getName()).append(',');
}
//如果有参数(因为不知道类型当类型为String时为null,int时为0)去掉最后一个','
if(clazzParams!=null&&clazzParams.length!=0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
//获得对应字节码的方法,并遍历输出
System.out.println("----------begin constructors list(方法)---------");
Method[] menthods = clazzProxy1.getMethods();
for(Method menthod : menthods){
String name = menthod.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = menthod.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());
}
System.out.println("----------begin constructors list(方法)---------");
//得到相应参数的构造方法
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
//InvocationHandler接口的一个简单实现类(子类)
class MyIvnocationHander implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
//第一种方式,调用得到的构造方法,但是InvocationHandler是一个接口,所以不能直接new,要通过子类实现上面就是一个自己做的实现类
Collection proxy1 = (Collection) constructor.newInstance(new MyIvnocationHander());
//打印为null是因为它的toString()返回值为Null
System.out.println(proxy1);
//调用没有返回值的方法没有错
proxy1.clear();
//调用有返回值的方法错误,因为调用方法的同时也调用了实现类invoke方法所以返回null,但size()要把它换为int数据所以出现错误
//proxy1.size();
//第二种方式直接一步到位,因为只有一个实现类,new一个实现类当成一个参数,更方便
Collection proxy2 = (Collection) constructor.newInstance(new InvocationHandler(){
@Override
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 {
long beginTime = System.currentTimeMillis();
//去调用被代理类的方法
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+"running time of"+(endTime-beginTime));
return retVal;
}
}
);
proxy3.add("sdjkg");
proxy3.add("5487541");
proxy3.add(";ljkhvbjnk");
System.out.println( proxy3.size());
}
}
16.客户端程序调用代理方法时涉及三要素,如:
Cllen程序调用ObjProry.add("abc")方法时,涉及三要素:ObjProry对象、add方法、"abc"参数分别对应以下方法的参数
Class Proxy${
add(Objectobject){
return handler.invoke(Object Prory,Method add,Object[] args)
}
}
17.为什么动态类的实例对象的getClass()返回正确结果呢?
调用代理对象的从Object类继承的hashCode,equals或toString这几个方法时,代理对象将调用请求转发给InvocationHander对象,对于其他方法,则不转发调用请求。