-------android培训、java培训、期待与您交流! ----------
注解(Annotation)
JDK提供的基本注解:
1. @Deprecated:说明某个方法,属性或者类已经被废弃,建议不要使用。
2. @Override:让编译器检查某个方法是否是重写了父类的方法。
3. @SuppressWarnings:让编译器在编译时不提示警告。
总结:注解就相当于一种标记,在程序中加了注解就等于打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具,和其他程序可以用反射来了解你的类及各种元素上有何种标记,看你有什么标记,就去干相应的事。标记可以加载包,类,字段,方法,方法的参数以及局部变量上。
元注解
@Retention(RetentionPolicy.RUNTIME):说明被该Annotation注解的Annotation会把注解的信息保留在内存中的字节码中
注解的三个生命周期:
RetentionPolicy.RUNTIME:字节码阶段//@Deprecated
RetentionPolicy.CLASS:class文件中
RetentionPolicy.SOURCE:java源文件中//@Override,@SuppressWarnings
@Target:注解作用于类的哪个成分,值为ElementType枚举值
Target的默认值为任何元素。
为注解增加属性:
public@interface MyAnnotation{
String color();//为注解增加了color属性,使用时@MyAnnotation(color=”red”)
String value();//特殊属性,在使用时可以不用写属性名。
}
可以为属性添加默认值:Stringcolor()default “red”;
数组属性使用方式:属性名={value1,value2};
注解类型的属性:假设有元注解类型@MetaAnnotation
MetaAnnotationannotationAttr()default @ MetaAnnotation(“值”);//设置默认值方式。
泛型
泛型是提供给javac编译器使用的。可以限定结合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,是程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往反省集合中加入其它类型的数据。
ArrayList<Integer>a = new ArrayList();
ArrayList<String> b = newArrayList();
System.out.println(a.getClass()== b.getClass());//同一份字节码
System.out.println(a.getClass());//返回java.util.ArrayList
参数化类型不考虑类型参数的继承关系:
Vector<Object>v = new Vector<String>();//错误
Vector<String>v = new Vector<Object>();//也错误
这两个类型是没有转化关系的类型。改成如下即可:
Vector v = newVector<String>();
Vector<Object>v2 = v;
泛型的通配符扩展应用
“?”表示任意类型。
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
限定通配符的上边界:
正确:Vector<? ExtendsNumber> x = new Vector<Integer>();
错误:Vector<? ExtendsNumber> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? superInteger> x = new Vector<Number>();
错误:Vector<? superInteger > x = new Vector<Byte>();
提示:限定通配符总是包括自己。
泛型方法定义:
public static<T> T add(T x, T y){
}
用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回值类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
只有引用类型才能作为泛型方法的实际参数。
除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符。
普通方法,构造方法,和静态方法中都可以使用泛型。编译器也不允许创建类型变量的数组。
也可以用类型变量表示异常,成为参数化的异常,可以用于方法的throws列表中。但是不能catch子句中。
在泛型中可以同时有多个类型参数。在定义他们的尖括号中用逗号分隔。
泛型方法练习
强制转化类型:
public static <T> T autoConvert(Object obj){
return (T)obj;
}
调用:Object obj= “ddd”;
String x = autoConvert(obj);
当一个类型变量用来表达两个参数之间后者参数和返回值之间的关系时,即同一个类型变量在方法签名的两处都被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用泛型方法。
注意:
1. 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
2. 当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
获得方法的参数化类型:
public class ReflectTest {
public static void main(String[]args)throws Exception {
Method method= ReflectTest.class.getMethod("add",Vector.class);
Type[] types = method.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getRawType()); System.out.println(pType.getActualTypeArguments()[0]);
}
public static void add(Vector<Date>obj){
}
}
类加载器
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:
BootStrap<—ExtClassLoader<—AppClassLoader//父子关系
BootStrap不是java类,System类就是由这个类加载器加载的。
BootStrap:加载JRE/lib/rt.jar
ExtClassLoader:加载JRE/lib/ext/*.jar
AppClassLoader:加载CLASSPATH指定的所有jar或目录
类加载器的委托机制
当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
1. 首先当前线程的类加载器去加载线程中的第一个类。
2. 如果类A中引用了类B,java虚拟机将使用加载类A的加载器来加载类B
3. 还可以直接调用ClassLoader.loadClass()方法来制定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上一级类加载器。
写自定义类加载器:继承自ClassLoader抽象类,覆盖findClass方法(可以写自己的逻辑,最终调用defineClass得到字节码)。
代理
1. 要为系统中的各种借口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式将是一件非常麻烦的事情。
2. JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即为动态代理类。
3. JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
4. CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
5. 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
a. 在调用目标方法之前
b. 在调用目标方法之后
c. 在调用目标方法前后
d. 在处理目标方法异常的catch块中。
StringBuffer 和StringBuilder的区别:
1. 在应用上基本一样,都是动态的往字符串上添加内容。
2. 单线程的情况下StringBuilder的效率要高一点。如果在多线程的情况下要用StringBuffer。
通过反射方式打印Proxy类的构造方法和所有方法的列表。发现该类没有无参的构造函数,只有一个含一个参数的构造方法,方法参数类型为“InvocationHandler”。
总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
1. 生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
2. 产生的类字节码必须有一个关联的类加载器对象。
3. 生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了的接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象。它是在创建动态类的实例对象的构造方法时传递进去的。
动态代理对象在调用目标对象的方法时:
会将调用方法的Method对象传到内部的Handler对象,调用handler对象的invoke方法时该Method对象被传递到invoke方法上。实际调用过程是调用该Method对象的invoke方法,该Method对象的invoke方法作用于目标对象上,并将返回值返回到该代理对象上。如下代码示例:
Collection collection = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] { Collection.class },
new InvocationHandler() {
ArrayList aList = new ArrayList();
@Override
public Object invoke(Objectproxy, Method method, Object[] args) throws Throwable {
return method.invoke(aList,args);//调用目标对象的方法,返回值传递到该动态代理对象上。
}
}
);
collection.add("123");
collection.add("ddd");