- (一) 注解 Annotation
Annotation(注解)是JDK5.0及以后版本引入的。
注解是以 @注解名 的形式标识
注解不会影响程序语义,只作为标识
注解是新的类型(与接口很相似),它与类、接口、枚举是在同一个层次,它们都称作为java的一个类型(TYPE)。
它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等
- 注解类
注释类型 是一种特殊的接口
用 @interface 声明如
public@interface MyAnnotation
{
属性
方法
}
注解可以有属性和方法,但只能定义 public abstract 的方法和 static final 的属性
所有定义的方法被编译器默认都是 public abstract
方法的返回类型可以是基本数据类型,String ,Class , 枚举 , 注解及这些类的数组
可以在方法声明中定义默认返回值
public@interface MyAnnotation
{
static final 属性名;
public abstract 返回类型方法名() default 默认值;
}
- 常见注解
(1) java.lang包下的注解
a ), @Override
Override 的源码
@Target(ElementType.METHOD) //只能用来注解方法
@Retention(RetentionPolicy.SOURCE) //只保留在源文件 , 编译后去掉
public @interface Override {
}
用在方法定义之前表示在重写父类某个方法 ,如果方法利用此注释类型进行注解但没有重写超类方法,
则编译器会生成一条错误消息
b ), @Deprecated其作用是标记某个过时的类或方法 , 不赞成使用
c), @SuppressWarnings
SuppressWarnings 的源码
//可以注解接口,类,枚举,注解类型,域,方法,参数,构造方法,局部变量
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE) //保留到源文件 , 编译后去掉
public @interface SuppressWarnings {
String[] value();
}
其作用是告诉编译器 不要发出被注解元素(以及包含在该元素中的所有程序元素)的某些警告信息
参数有
deprecation,使用了过时的类或方法时的警告
unchecked,执行了未检查的转换时的警告
fallthrough,当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path,在类路径、源文件路径等中有不存在的路径时的警告
serial,当在可序列化的类上缺少serialVersionUID 定义时的警告
finally ,任何 finally 子句不能正常完成时的警告
all,关于以上所有情况的警告
(2)java.lang.annotation包下的注解类 , 用来注解注解类
a), @Target表示该注解用于什么地方,Target 注解类源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
ElementType 是枚举
public enum ElementType {
TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR,
LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE
}
可能的 ElementType 参数包括:
ElementType.PACKAGE 包声明
ElementType.TYPE 类Class,接口Interface,枚举Enum, 注解类型Annotation 声明
ElementType.FIELD 域声明(包括 enum 实例)
ElementType.CONSTRUCTOR 构造器声明
ElementType.METHOD 方法声明
ElementType.PARAMETER 参数声明
ElementType.LOCAL_VARIABLE 局部变量声明
ElementType.ANNOTATION_TYPE 注解类型声明
b),@Retention 表示在什么级别保存该注解信息。Retention注解类源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
返回类型是枚举
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
可选的 RetentionPolicy 参数包括:
RetentionPolicy.SOURCE 注解将被编译器丢弃
RetentionPolicy.CLASS 注解在class文件中可用,但会被JVM丢弃
RetentionPolicy.RUNTIME JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
c), @Documented 声明注解类型信息可以显示到 java doc文档中
d), @Inherited 允许子类继承父类中的注解
- 注解的生命期
注解的生命期有三个 , 默认只保留到class文件 , 在运行时就会消失
可以通过在一个注解类前定义了@Retetion 明确保留到什么时期
- 源码(.java) ,
通过 Retetion(RetentionPolicy.SOURCE) 注解的注解类,注解只保留在一个源文件当中,当编译器将源文件编译成class文件时,编译器将其去掉
- 编译后文件(.class) ,
通过 Retetion(RetentionPolicy.CLASS)注解的注解类,注解在源码、编译好的.class文件中保留,当加载class文件到内存时,虚拟机将其去掉
- 运行时
通过 Retetion(RetentionPolicy.RUNTIME)注解的注解类,该注解在源码、编译好的.class文件中保留在程序运行期间都会存在内存当中。此时,我们可以通过反射来获得定义在某个类上的所有注解
注解的使用 : 定义注解-->声明注解-->使用注解-->注解信息的提取
- 通过反射获得注解
对于生命周期为运行期间的注解,都可以通过反射获得该元素上的注解实例。
- 1、声明在一个类中的注解可以通过该类Class对象的getAnnotation或getAnnotations方法获得。
- 2、声明在一个字段中的注解通过Field对象的getAnnotation或getAnnotations方法获得
- 3、声明在一个方法中的注解通过Method对象的getAnnotation或getAnnotations方法获得
- (二) 泛型(Generics)
泛型是提供给编译器检查用的 , 用来检查类型安全, 编译结束之后去掉类型信息.
类型参数可以应用于类,接口,方法中分别称为泛型类,泛型接口,泛型方法,泛型声明在<>中
JDK1.5以前没有泛型,只能通过Object 的引用类实现参数类型的"可变性" 利用java多态特性
使得Object 的引用可以指向所有的引用类, 而基本类型经过装箱也可以被Object引用指向
但是,通过参数通过Object 引用来接收,在对其特定操作时需要进行显式的强制转换 , 由于往往
对接收的实际参数不可预知,在编译时没有错误提示,但是有可能发生运行时异常 , 所以这种办法
是存在安全隐患的
- 泛型的好处:
更安全:
泛型的好处是在编译时就检查类型安全,将可能运行时出现的类型转换异常问题转移到了编译时
及时检查发现及时改正,避免后患
更简单:
省去了每次对集合元素进行操作时需要进行类型强转之后才能操作,使代码更加简单明了,提高代码重用率
泛型的好处在集合中的体现
集合引入泛型为集合(collections)提供编译时类型安全检查,无需每次对Collections
取得一个对象进行某些操作都要进行强制转换的, 还是会在运行时出现类型转换异常等安全问题
泛型是提供给Javac编译器使用的。可以限定集合中输入的类型,让编译器挡住原始程序的非法输入,
编译器编译带类型说明的集合时会去掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,
getClass()方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,只要能跳过
编译器,就可以往某个泛型集合中加入其它类型的数据
- 泛型中的各成分
ArrayList<E>类定义和ArrayList<Integer>类引用 中涉及如下术语:
整个称为 ArrayList<E>表示ArrayList被定义为泛型类型, ArrayList<E> 中的E称为类型变量或类型参数,
并没有对它进行具体类型的定义,它只是在定义ArrayList时的类型占位符 , 我们在定义ArrayList的实例时用
Integer绑定在E上 , 在ArrayList中所有的泛型方法中的E都会用Integer来替代 , 无论是方法的参数还是返回值。
整个ArrayList<Integer>称为参数化的类型,ArrayList<Integer>中的Integer称为类型参数的实例或实际
类型参数,ArrayList<Integer>中的<>念作typeof,<>是用来接收类型的 , ArrayList称为原始类型。
List<E> -->泛型类型 ,其中的E为类型变量或类型参数
List<String> -->参数化类型 , 其中的String为类型参数的实例或实际类型参数
List -->原始类型
泛型类或接口与一般的Java类,接口基本相同,只是在类和接口定义上多出来了用<>声明的类型参数。
- 泛型的规则:
- 1)通过<>来定义要操作的参数为引用数据类型(类类型,数组类型),而不是简单类型,
- 2)泛型的类型参数可以有多个,如 MyClass<X, Y, Z>。
- 3)泛型的类型参数可以使用extends语句做类型上限限定,<E extends Number>则E类型限定为Number及其子类
泛型的类型参数可以使用super语句在类型下限限定 , <T super Number>,表示T限定为Number或其父类
通配符上下限定
<? extends T>
<? super E>
- 4)泛型的类型参数可以是通配符类型.如:Class<?> type =Class.forName("java.lang.String");
- 5)所声明的类型参数在Java类中可以像一般的类型一样作为方法的参数和返回值,或是作为域和局部变量的类型。
- 6)定义在类上的泛型 不能用来声明静态变量, 静态方法不可以访问类上定义的泛型,静态方法要操作的数据类型不
确定 , 应将泛型定义在该方法上,
- 7)泛型方法与该方法所在的类是否是泛型类无关 , 或与所在的泛型类声明的泛型无关,泛型方法是将<类型参数列表>加
- 8)不能创建参数化类型数组,如:
Vector<Integer>[] vt = new Vector<Integer>[12];//错误
Vector<?>[] vt = new Vector[12];//正确
- 9)可以用类型变量表示异常(参数化异常)可以用在方法的throws声明
列表中,但不能用在catch子句中
private static <T extends Exception> method() throws T
{
try{
...
}catch(Exception e){//不允许catch(T e)
throw (T)e;
}
}
示例:
class ClassTest<X extends Number, Y, Z> {
private X x;//泛型成员变量
//private static Y y; //编译错误,泛型不能用在静态变量中
ClassTest(X x)
{
this.x=x;
}
public X getFirst() {//正确用法
return x;
}
public void setFirst(X x) {//正确用法
this.x=x;
}
public void wrong() {
Z z = new Z(); //编译错误,不能创建泛型对象
}
public static void PrintZ(Z z){//编译出错 普通静态方法不可以访问类上定义的泛型,
System.out.println("ZZZZ"+z);
}
//正确 , 若泛型类已经被参数化 ,想要调用该类方法操作其他类型,必须将泛型定义在方法上
public static <M> void printY(M y){
System.out.println("ZZZZ"+y);
}
}
- 泛型的定义:
- 1)泛型定义在泛型类上 ,
泛型类 public class Cs<T>{...}泛型类定义的泛型,在整个类中都有效,如果被方法调用,那么泛型类的对象明确要操作 的数据类型后,所有要操作的类型就已经固定
- 2)泛型定义在方法上 ,
泛型方法 public static <T extends className> void method(T t1 , T t2){...}为了让不同方法可以操作还不确定的不同的类型,可以将泛型定义在方法上,把泛型声明在返回类型与修饰符之间
- 3)泛型定义在接口上,
泛型接口public interface Cd<>{...}可以在实现的类中明确要操作的类型参数类型,也可以在调用时才明确操作的类型参数类型
- 自定义泛型类
class D<T>{}//D创建对象参数化时可以为一个任意类
class D1<T extends Number> { } //D1创建对象时限定参数化为 Number 或其子类
D1<Number> d1 = new D1<Number>();//正确
D1<String> d2 = new D1<String>();//错误
D1<Integer> d3 = new D1<Integer>();//正确
D1<Object> d4 = new D1<Object>();//错误
class D2<T extends Person & Comparable<T>> { }
D2类的创建对象参数化时泛型实际类型限定为实现了Comparable的Person类
或其实现了Comparable的Person子类
class C4<T,E,V>{}//限定了三种类型
- ?通配符
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用
<? super Number>和<? extends Number>形式的上下限声明不能用于定义泛型类或泛型方法,
但可以声明引用变量 如方法参数列表中的引用变量,创建对象时的引用变量的声明及返回类型中的声明
泛型类定义和泛型方法定义可以使用上限限定,且只能是<T extends className>形式的,但不能进行下限限定
new对象时要求类型是明确的而不能是上下限限定或<?>
引用变量可以使用<? super Number>和<? extends Number>形式上下限或<?>来限定允许引用的泛型对象
<T super Number> 形式的下限限定
泛型类对象引用变量的声明和调用构造函数声明
List s = new ArrayList<String>();//编译通过 , 但List还是原始类型 还没有参数化 , 仍然可以接收任意对象
s.add(2);//允许
s.add("daf");//允许
List<String> s = new ArrayList();//编译通过 , List 已经参数化 , 只能接收 String 对象
s.add("2");//允许 , 只能接收字符串
s.add(2);//不允许 , 只能接收字符串
List<String> s = new ArrayList<String>();//正确,效果跟上面的一样
s.add("2");//允许 , 只能接收字符串
s.add(2);//不允许 , 只能接收字符串
List<Object> s = new ArrayList<String>();//编译不通过
s.add("2");//不能将String类型集合转为Object类
List<String> s = new ArrayList<Object>();//编译不通过
s.add("2");//不能将Object集合转为String类
List s1; s1可以引用任意参数化的实现类对象
List<Number> s2 ; s2只能引用未声明泛型类型子类对象 或 参数化为Number类型的子类对象
List<? extends Number> s3; s3可以引用参数化为Number或Number子类的List实现类对象
List<? super Number> s4 ; s4可以引用参数化为Number或其父类超类的List实现类对象
List<?> s5; s5可以引用所有类参数化的List实现类对象
<?>等同于<? extends Object>
List<Object> s6; s6只能引用参数化为Object的List实现类对象
- 参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象 , 编译有警告
原始类型可以引用一个参数化类型的对象 , 编译有警告
参数化类型不考虑类型参数的继承关系 ,否则编译报错
但是以下不会报错
Vector v = new Vector<String>();
Vector<Object> v1 = v;
参加泛型类的数组实例声明或构造都不能使用参数化的类型,否则编译报错
如Vector<Integer> v = new Vector<Integer>[10];//出错
只在调用构造函数中参数化泛型是没有意义的,必须在引用变量中声明泛型实际类型,且在引用变量声明泛型类型后
只能引用构造函数泛型参数化为与引用上相一致的实际类型的对象或引用没有泛型声明的对象
class A{}
class B1 extends A{}
class B2 extends A{}
class C1 extends B1{}
class C2 extends B2{}
public class classDemo
{
public static void main(String[] args)
{
List<? extends B1> a;
a = new ArrayList<A>();//错误
a = new ArrayList<B1>();//正确
a = new ArrayList<B2>();//错误
a = new ArrayList<C1>();//正确
a = new ArrayList<C2>();//错误
List<? super B1> b ;
b = new ArrayList<A>();//正确
b = new ArrayList<B1>();//正确
b = new ArrayList<B2>();//错误
b = new ArrayList<C1>();//错误
b = new ArrayList<C2>(); //错误
List<Object> c;
c = new ArrayList<A>();//错误
c = new ArrayList<B1>();//错误
c = new ArrayList<B2>();//错误
c = new ArrayList<C1>();//错误
c = new ArrayList<C2>();//错误
List<?> d;
d = new ArrayList<A>();//正确
d = new ArrayList<B1>();//正确
d = new ArrayList<B2>();//正确
d = new ArrayList<C1>();//正确
d = new ArrayList<C2>();//正确
}
}
通配符?的泛型类引用变量可以引用其他各种参数化类型(包括Object)
只能调用与参数化无关的方法,不能调用与参数化有关的方法
Object的泛型类引用变量不能引用其子类的参数化类型对象
可以调用与参数化有关或无关的方法
如:
private void method1(List<?> lt) {
lt.add("abc");//错误 , add(E e)与类型参数有关
lt.size();//正确 , size()与类型参数无关
lt = new ArrayList<Integer>();//正确
lt = new ArrayList<Object>();//正确
}
而以下:
private void method2(List<Object> lt1){
lt1.add("abc");//正确
lt1.add(8);//正确
lt1.size();//正确
lt1 = new ArrayList<Integer>();//错误
lt1 = new ArrayList<Object>();//正确
lt1 = new ArrayList();//警告,类型不安全
}
带?通配符(<?>或< ? extends 类型>或<? super 类型>)的引用变量的引用
不能传给类型明确的参数化类型的引用变量 , 反过来则可以 如:
List<? extends Number> lt1 ;
ArrayList<Integer> lt2;
lt1 = lt2;//正确
List<Number> lt3;
ArrayList<? extends Number> lt4;
lt3 = lt4 ;//错误
- Java泛型方法的声明:
[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])
[]表示为可选的。返回类型和形式参数列表都可以是泛型类型变量
方法中传入或返回的泛型类型由调用方法时所设置的参数类型所决定。
可用extends 和 super 关键字分别来 指定 上限 和下限 类型。
泛型类是在创建对象的时候参数化是,创建实例之后,访问泛型类上声明的泛型的方法能操作的类型就确定了
泛型方法可以在方法调用的时候将泛型类型参数化,每次调用无需创建新对象
另外,对于一个static的方法而言,无法访问泛型类的类型参数,如果静态方法也定义为泛型类上的
类型变量占位符一致的泛型方法 , 则该泛型方法使用的参数类型独立于类上的参数变量.
如果要让static方法具备泛型,就必须使其成为泛型方法。
要定义泛型方法,只需将泛型参数列表置于返回值之前,就像下面这样:
import java.util.*;
public class New {
public static<K, V> Map<K, V> map() {
return new HashMap<K, V>();
}
public static void main(String[] args) {
Map<String, List<Integer>> map = map();
}
}
- 泛型中自动类型参数推断(type argument inference)
使用泛型类时,必须在创建对象的时候指定类型参数的值,即将泛型类的泛型实参化 ,
而调用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们传入的实际参
数找出具体的类型。比如传入的是基本的整数,编译默认将其自动装箱为Integer , 当然
传入的整数如果显式强制转换那就会装箱为对应类型这称为类型参数推断(type argument
inference)。因此,我们可以像调用普通方法一样调用泛型方法 , 而且就好像是泛型
方法被无限次地重载过。这些都节省了程序员很多的时间
泛型方法在接受参数时的类型推断:
public class
{
public static void main(String[] args)
{
List<Number> lt3;
ArrayList<? extends Number> lt4;
//lt3 = lt4 ;
Integer i1 = method(3,3);//正确
Number i2 = method(3,3);//正确
Object i3 = method(3,3);//正确
String s1 = method(3,"嗯哈");//错误
Object s2 = method(3,"嗯哈");//正确
Double d1 = method(3,2.3);//错误
Number d2 = method(3,2.3);//正确
Object d3 = method(3,2.3);//正确
}
public static <T> T method(T t1,T t2 )
{
return t1.hashCode()>t2.hashCode()?t1:t2;
}
}
对传入参数类型的自动类型推断是根据他们类型的公共类确定的
如果传入实际参数的是相同类型则可以推断为该类型
如果传入实际参数的是不同类型,那么推断为他们的公共父类类型
- 泛型类与非泛型类中定义泛型方法
1-定义泛型类 并使用泛型类操作不同类型数据
class Tool<T> {
public void show(T t) {//show方法在访问泛型类中声明的泛型
System.out.println("show:" + t);
}
//跟show方法一样,当泛型类对象产生后,该类泛型被参数化,故只能操作实际类型参数指定的数据类型
public void print(T t) {
System.out.println("print:" + t);
}
//showIt方法可以操作,该泛型类参数化后还不确定的数据类型,该类型可以在调用时确定
public <E>void showIt(E t){
System.out.println("showIt:" + t);
}
}
public class Demo {
public static void main(String[] args) {
Tool<Integer> t = new Tool<Integer>();//只能操作Integer
t.show(new Integer(3));
t.show(4);
t.print("hello java");//编译出错
t.showIt("hello java");//正确
t.showIt(new Integer(8));//正确
//此时print和show方法要操作不同类型的数据比较再创建一个泛型实例
Tool<String> s = new Tool<String>();//只能操作字符串
s.show(new Integer(3));//编译出错
s.print("hello java");//正确
t.showIt("hello java");//正确
t.showIt(new Integer(8));//正确
}
}
2--在非泛型类中定义泛型方法
//该类在创建对象时不会绑定实际操作类型参数,在方法调用的时候可以操作任意类型
class Tool{
public<T> void show(T t) {//这是方法参数类型的可变性
System.out.println("show" + t);
}
}
public class Demo {
/**
* @param args
*/
public static void main(String[] args) {
Tool t = new Tool();//创建一次对象 泛型方法可以操作不同类型数据
t.show(new Integer(3));
t.show(4);
t.print("hello java");
}
}
- 通过反射获取泛型信息
泛型是提供给编译器检查用的,源码编译完之后会去掉类型信息,将类的字节码交给
虚拟机时,类型信息已经不复存在,所以还可以通过反射的方式来给编译器前是泛型的
集合添加不同类型的元素
利用反射可以得知泛型方法的泛型信息
package day2;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;
public class GenericReflect
{
/**
* 通过反射获取泛型方法的有关信息
* 利用描述类型变量的TypeVariable类,获取泛型方法声明信息
* 利用描述参数化类型的ParameterizedType接口,获取泛型方法参数中泛型参数的原始类型和实际参数类型
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception
{
/*通过反射获得方法所在的类Class对象并反射得到一个泛型方法*/
Method m = GenericReflect.class.getDeclaredMethods()[1];
/*获知泛型方法Method描述的字符串形式*/
System.out.println("泛型方法的字符串形式: "+m.toGenericString());
/*1-通过getGenericReturnTypes()得到含类型参数的返回类型*/
System.out.println("返回类型:"+m.getGenericReturnType());
/*2-通过getGenericParameterTypes() 得到含泛型类型参数的方法参数列表*/
Type[] tp = m.getGenericParameterTypes();
for(int i = 0;i<tp.length;i++)
{
System.out.println("泛型方法的参数"+i+": "+tp[i].toString());
if(tp[i] instanceof ParameterizedType)
{
ParameterizedType pt = (ParameterizedType)tp[i];
System.out.println(" 此泛型参数: "+pt.getOwnerType());
System.out.println(" 此泛型参数的原始类型: "+pt.getRawType());
for(int j=0;j<pt.getActualTypeArguments().length;j++)
{
System.out.println(" 此泛型参数的实际类型参数: "+pt.getActualTypeArguments()[j]);
}
}
}
System.out.println();
/*3-得到泛型方法声明的TypeVariable的数组*/
TypeVariable[] tvs = m.getTypeParameters();
/*数组的元素是类型变量 ,每个元素可以调用TypeVariable的三个方法*/
for(int i = 0;i<tvs.length;i++)
{
System.out.println("泛型声明中类型变量名称"+i+": "+tvs[i].getName());//类型变量的名称
for(int j = 0;j<tvs[i].getBounds().length;j++)
{ /**/
System.out.println(" |---此类型变量的上边界: "+tvs[i].getBounds()[j]);
}
System.out.println("泛型声明中类型变量的一般声明: "+tvs[i].getGenericDeclaration());
System.out.println();
}
}
public static <T extends Number&Comparable , E> List<E> method(T t1, T t2,List<E>lt1,List<? extends T> lt2)
{
return null;
}
}