注解 Annotation(java5的新特性)
注解是用于向开发工具或编译器传递信息的,
一个注解就是一个类,注解相当于一种标记,加上标记以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无标记,看你有什么标记,就去做相应的事。标记可以在包,类,字段,方法,及方法的参数以及局部变量上。
java提供了三个基本的注解
a、 压缩警告标记:@SuppressWarnings("deprecation"),在调用过时的方法时使用,告诉编译器不要发出过时警告
b、 声明过时标记,@Deprecated:生命方法已过时:例如
@Deprecated
public static void sayHello(){
System.out.println("hi,body");
}
c、 表覆盖标记,@Override:表示此为方法覆盖
使用自己的注解
1、 写注解类。因为注解就相当于一个类,使用一个注解(即使用@符号后面加上注解),就相当于产生了该注解类的一个实例。要在源程序中应用某个注解,得先准备好这个注解类。就像你要调用某个类,得先写好这个类
2、 应用注解到某个类
3、 对应用了注解类的类,利用反射进行相关操作。
写注解:
mport java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cn.itcast.day1.EnumTest;
@Retention(RetentionPolicy.RUNTIME)//注解的注解:元注解
/*此标记注明编写的这个注解保留到运行时。因为注解的生命周期不确定,编写出注解后要用于某个类,该类在被javac编译成class文件时,注解可能被抹去,使得class文件中无注解信息;也可能在class文件中有注解信息,但在类加载器加载class文件进入内存,转换成最终的二进制文件时,注解信息被抹去了,因此注解信息可能存在的地方有:源程序;class文件;内存(运行时)
所以@Retention 有三种取值:RetentionPolicy.SOURCE,RetentionPolicy.CLASS, RetentionPolicy.RUNTIME,分别对应源程序;class文件;内存,默认的是在class文件的阶段*/
@Target({ElementType.METHOD,ElementType.TYPE})
//指明此注解应用的地方,ElementType为enum
public @interface ItcastAnnotation {
}
使用并进行操作
public class AnnotationTest {
@ItcastAnnotation()//使用注解(后无分号)
public static void main(String[] args) throws Exception{
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))
//判断是否采用了注解
{
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println();
}
}
为注解增加属性(注解是一个类,自然也有属性)
例如:
public @interface ItcastAnnotation {
String color();
}
为注解ItcastAnnotation添加了一个属性color,类型为String。
使用时:
public class AnnotationTest {
@ItcastAnnotation(color="red")//使用注解(后无分号)
public static void main(String[] args) throws Exception{
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))
//判断是否采用了注解
{
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println();
System.out.println(annotation.color());
}
}
当属性名为value,并且只有这一个属性或其他的属性都采用默认值时,可以省略属性名和等号:
public @interface ItcastAnnotation {
String color() default "blue";
String value();
}
使用时:@ItcastAnnotation("xyz")
System.out.println(annotation.value());
注解属性的类型可以是8种基本类型,也可以是String,枚举,Class类型,注解,以及这些类型的数组
数组作为属性的类型:
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
}
使用时:@ItcastAnnotation(color="red",value="abc",arrayAttr={1,2,3})
数组的元素只有一个时,可以省略大括号:
@ItcastAnnotation(color="red",value="abc",arrayAttr=1})
枚举作为属性的类型:
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
//采用的这个枚举是在EnumTest类中定义的枚举:TrafficLamp
}
使用时:@ItcastAnnotation(lamp =EnumTest.TrafficLamp.BULE,color="red",value="abc",arrayAttr=1)
注解作为属性的类型:
在一个文件中定义注解MetaAnnotation;
public @interface MetaAnnotation {
String value();
}
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}
使用:
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",value="abc",arrayAttr=1)
泛型 generic(java5新特性)
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定的类型,集合中只能存储进同一类型的对象,这样更安全,并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,从而不需要进行强制类型转换。
不使用泛型时:
ArrayList collection1 = new ArrayList();
collection1.add(1);
collection1.add(1L);
collection1.add("abc");
//不使用泛型,可以将各种类型的对象放进集合中,取出来时需强制类型转换,但是不容易知道放进去的对象的类型,有时候会出错
int i = (Integer)collection1.get(1);//此句就无法运行
使用泛型时:
ArrayList<String> collection2 = new ArrayList<String>();
collection2.add("abc");
/*泛型会告知编译器,集合所接受的类型,因此只能放进所指定的类型,否则在编译时就会出错,而编译器再编译完成后会去掉类型信息(此处为String),以免影响程序的运行*/
String element = collection2.get(0);//不需要强制类型转换
ArrayList<Integer> collection3 = new ArrayList<Integer>();
System.out.println(collection3.getClass() == collection2.getClass());
因为泛型只是让编译器看的,而运行时类型信息已经没有了,所以上句返回结果为true
也因为如此,同时编写下面两个方法是错误的,
public static void applyVector(Vector<Date> v1){
}
public static void applyVector(Vector<Integer> v1){
}
依照上面的原理,我们可以通过反射向collection3中加入其他类型的数据,因为对于加入了内存的字节码已经没有了类型信息:
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
//返回abc,而没有强制转换,因为便以其自认为已经知道了集合中的类型
一些术语:
称在整个ArrayList<E>为泛型类型,而ArrayList为原始类型
ArrayList<E>中的E为变量,称为类型参数或类型变量
ArrayList<Integer>中的Integer称为实际类型参数或类型参数的实例
ArrayList<Integer>中的<>读作typeof
参数化类型与原始类型的兼容性
参数化类型可以引用原始类型的对象:Collection<String> c=new Vector()
原始类型可以引用参数化类型的对象:Collection c=new Vector<String>()
参数化类型不考虑类型参数的继承关系:
Vector<String> v=new Vector<Object>(); //错
Vector<Object> v=new Vector<String>(); //错
在创建数组时,数组的元素不能使用参数化类型:
Vector<String> v[]=new Vector<Object>[3];//错
泛型?号通配符
?号通配符可以引用各种参数化的类型,?号通配符定义的变量主要用作引用,使用?号通配符定义的变量时,可以调用与参数化无关的方法,不能调用与参数有关的方法
通配符的扩展:
限定通配符的上边界,Vector<? extends Number> x=new Vector<Integer>();
? extends Number表示可以接受Number及其子类
限定通配符的下边界:Vector<? super Number> x=new Vector<Integer>();
? super Number表示可以接受Number及其父类
自定义泛型
泛型方法
例如:下面的表示可以接受任何类型的add方法,
private static <T> T add(T x,T y){
//T表示任何类型,<T>应该放在返回类型前面,紧挨着反返回类型
return null;
}
又如,下面的方法表示可以交换任意类型的数组中的元素:
private static <T> void swap(T[] a,int i,int j){
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
但调用时, swap(new String[]{"abc","xyz","itcast"},1,2);//不会出错
swap(new int[]{1,3,5,4,5},3,4);//会出错
因为,只有引用类型才能作为泛型方法的参数,而基本数据类型不可以。
但调用add(3,5);也不会出错,这是因为在调用的时候参数被自动装箱成Integer,
在调用swap(new int[]{1,3,5,4,5},3,4);之所以出错是因为数组已经是引用类型了。
在定义泛型时也可以使用extends限定符,如:
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
//表示T类型应为Annotation及其子类
也可与使用&来限定多个边界,如:<V extends Serializable&cloneable>void method(){}
在泛型中可以同时使用多个类型参数,在定义它们的<>中用逗号分开:
public static <K,V> getValue(K key){return map.get(Key);}
普通方法、构造方法,静态方法中都可以使用泛型。也可以使用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但不能用于catch的参数中:
public static <T entends Exception> void sayHello()throws T{
Try{
}
Catch(Exception e)//不能用T
{throw (T)e;}
}
定义一个方法,可以将Object类型的转为其他类型:
private static <T> T autoConvert(Object obj){
return (T)obj;
}
例如:
Object obj = "abc";
String x3 = autoConvert(obj);//该方法的返回的类型由定义的类型决定
比较下面这两种方法:
public static void printCollection(Collection<?> collection){
System.out.println(collection.size());
for(Object obj : collection){
System.out.println(obj);
}
}
public static <T> void printCollection2(Collection<T> collection){
System.out.println(collection.size());
for(Object obj : collection){
System.out.println(obj);
}
}
前者使用了?通配符而后者使用的是自定义泛型,当一个类型变量用来表达两个参数之间或参数和返回值之间的关系时,使用泛型方法,并且使用泛型也可以操作与类型有关的方法。
比较下面这两种方法:
public static <T> void copy1(Collection<T> dest,T[] src){
……;
}
public static <T> void copy2(T[] dest,T[] src){
……..;
}
copy2(new Date[10],new String[10]);//不出错
copy1(new Vector<Date>(),new String[10]);//编译出错
此处有关参数类型的类型推断,第一种情况,会取两个参数类型的交集类型,实际参数类型为Object,所以编译没有问题;而第二种情况涉及到参数类型的类型推断的传递性,实际的类型则根据参数化的Vector实例将类型变量直接确定为String类型,所以编译出错。
自定义泛型类型
如果类的实例对象中的多出都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候要采用泛型类型的方式进行定义,也就是类型级别的泛型。
如:
public class GenericDao<E> {
public void add(E x){
}
public E findById(int id){
return null;
}
}
当然在该类中也可以定义采用新的泛型的方法
注意:在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型
用于泛型类型的变量,只能用于实例变量和方法,不能被静态变量和静态方法调用。
通过反射获得泛型的实际类型参数
通过调用该泛型的方法获得,该方法或采用泛型为形参或采用泛型为返回值
public static void applyVector(Vector<Date> v1){
}
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
//ParameterizedType 表示参数化类型,如 Collection<String>。
System.out.println(pType.getRawType());//获得原始类型,返回java.util.Vector
System.out.println(pType.getActualTypeArguments()[0]);
/*获得实际类型,返回java.util.Data; getActualTypeArguments()返回type类型的数组,因为此处泛型中只有一个类型,所以使用getActualTypeArguments()[0]