Java高新技术4(注解与泛型)
1. 注解 (Annotation):
/*
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记, 没加,则等于没有某种标记,以后,javac 编译器, 开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记, 看你有什么标记,就去干相应的事。 标记可以加在包,类,字段,方法,方法的参数以及局部变量上。 看 java.lang 包,可看到 JDK 中提供的最基本的 annotation。
*/
三种基本注解:
public class AnnotationTest {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> args </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">@SuppressWarnings("deprecation")</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告 </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会取消编译器在编译时对方法过时发出的警告(SuppressWarning:抑制警告) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">即使main方法中用到过时方法也不会发出警告</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO Auto-generated method stub </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">System.runFinalizersOnExit(true);</span>
DeprecatedTest.say();
}
}
class DeprecatedTest{
@Deprecated//表明 say 方法已过时, 这样做好处: 以前仍使用该代码的开发人员依然程序可以执行成功
//后来的开发人员在看到这个标记: 哥们,这个方法过时了,尽量别用, 你看看有没有别的替代方法
//在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
public static void say(){
System.out.println("....");
}
}class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}@Override//加上这个说明一定要复写,不复写在编译时期发生:
//AnnotationTest.java:45: 错误: 方法不会覆盖或实现超类型的方法
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}}
注解的生命周期:
/* Retention(保留): 注解的生命周期: RetentionPolicy.SOURCE: 仅仅给编译器看的, 编译器检查完丢掉 例如:@Override 强制要求复写掉父类方法 -> 不复写编译时期报错 @SuppressWarnings 抑制警告 -> 编译器一看 不让我警告, 得我什么也不报 RetentionPolicy.CLASS:(默认) 从.java 保留到.class 但是被类加载器加载到虚拟机中时, 会去掉 例如:通过反射将拿不到注解类的实例 RetentionPolicy.RUNTIME: 直到运行阶段注解依然存在 例如:@Deprecated: 不但在编译时期检测方法是否过时,在运行时期依然会检测方法的字节码 */自定义注解与为注解添加属性:
元注解: 注解上的注解, 例如 @Retention,@Target
package com.itheima.day3;
public @interface MetaAnnotation {
String value() default "Meta";
}
/*
反编译:
public interface com.itheima.day3.MetaAnnotation
extends java.lang.annotation.Annotation {public abstract java.lang.String value();
}
*/package com.itheima.day3;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import com.itheima.day1.TrafficLamp;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})//TYPE:Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
//在这里 TYPE 表示: 类、接口(包括注释类型)或枚举声明
//Target 这个注解指明下面的自定义注解可以使用的位置
public @interface CustomAnnotation {
//有一个抽象方法
public abstract String color() default "black";//为注解增加了一个属性 color
// 指定默认值为 black
String value() default "white";//增加一个 value 属性TrafficLamp lamp() default TrafficLamp.RED; //lamp 属性类型为枚举 Class<?> cls() default CustomAnnotation.class;//属性类型为 Class<span style="color: rgba(0, 0, 255, 1)">int</span>[] arrAttri() <span style="color: rgba(0, 0, 255, 1)">default</span> {1,2,3};<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">arrAttri属性类型为int[]</span>
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">较为复杂</span> MetaAnnotation ma() <span style="color: rgba(0, 0, 255, 1)">default</span> @MetaAnnotation("MetaAnnotation");<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">属性类型为一个注解类型, </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">默认值是MetaAnnotation一个实例对象</span>
}
/
ElementType 的枚举常量:
ANNOTATION_TYPE
注释类型声明
CONSTRUCTOR
构造方法声明
FIELD
字段声明(包括枚举常量)
LOCAL_VARIABLE
局部变量声明
METHOD
方法声明
PACKAGE
包声明
PARAMETER
参数声明
TYPE
类、接口(包括注释类型)或枚举声明
/使用注解中的属性:
package com.itheima.day3;
import java.lang.reflect.Method;
import com.itheima.day1.TrafficLamp;
@CustomAnnotation
public class AnnotationDemo {</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> args </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> CustomAnnotation getAnnotationOnMethod(String methodName)<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception{ Method method</span>=AnnotationDemo.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getMethod(methodName); </span><span style="color: rgba(0, 0, 255, 1)">return</span> method.getAnnotation(CustomAnnotation.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); } @CustomAnnotation(</span>"black") <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">应用注解的属性 </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果注解类中有一个属性为String value();(不一定为String,只要属性名为value均可) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">可以将value="black"等价于"black"(即其他属性都采用默认值或者你只有一个value属性) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">例如:@Retention(RetentionPolicy.RUNTIME)-->内部属性RetentionPolicy value();</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> value()<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception{ CustomAnnotation ca</span>=getAnnotationOnMethod("value"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(</span>"value: "+ca.value());<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">black</span>
}
@CustomAnnotation(arrAttri</span>={4,5})<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果数组元素只有一个简写为arrAttri=6</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> arrAttri()<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception{ CustomAnnotation ca</span>=getAnnotationOnMethod("arrAttri"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(</span>"arrAttri: "+ca.arrAttri().length);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">2 </span>
}
}@CustomAnnotation(lamp</span>=<span style="color: rgba(0, 0, 0, 1)">TrafficLamp.RED) </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> lamp ()<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception{ CustomAnnotation ca</span>=getAnnotationOnMethod("lamp"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(</span>"lamp: "+ca.lamp().nextLamp());<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">GREEN </span>
@CustomAnnotation(ma</span>=@MetaAnnotation("ma"<span style="color: rgba(0, 0, 0, 1)">)) </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> metaAnnotation ()<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception{ CustomAnnotation ca</span>=getAnnotationOnMethod("metaAnnotation"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(</span>"ma: "+<span style="color: rgba(0, 0, 0, 1)">ca.ma().value());
}
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
CustomAnnotation ca=AnnotationDemo.class.getAnnotation
(CustomAnnotation.class);//将获取类 AnnotationDemo 上的 CustomAnnotation 注解的一个实例, 没有返回 null
System.out.println(ca+"\n");value(); arrAttri(); lamp(); metaAnnotation(); }
}
2. 泛型问题总结:
①反射与泛型:
/*
泛型术语:
ArrayList<E>: 泛型类型
:E 称为类型变量 / 类型参数
ArrayList<Integer>: 参数化的类型
:Integer 称为实际类型参数
:<>typeof
ArrayList: 原始类型 (rawtype)
*/package com.itheima.day4;
import java.lang.reflect.Constructor;
public class GenericDemo1 {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> args </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> main(String[] args)<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO Auto-generated method stub </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">反射与泛型</span> <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Class<T>: T - 由此 Class 对象建模的类的类型。例如,String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>。 Constructor<T> T -在其中声明构造方法的类。 public T newInstance(Object... initargs)//返回值类型为T </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> Constructor</span><String> con=String.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getConstructor(); String str</span>=<span style="color: rgba(0, 0, 0, 1)">con.newInstance(); }
}
/
未使用泛型前, 集合中可以存入任意引用类型的元素 (Object)-> 安全隐患 -> 在取出元素时进行强转 -> 运行时可能发生 ClassCastException
使用泛型后, 限定集合中的元素类型为一个特定类型, 集合中只能存储同一个类型的对象
1. 将运行时期的的安全隐患转移到了编译时期
2. 避免了强制转换的麻烦
/②一个纠结的错误:
public class GenericDemo2 {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> args </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> main(String[] args)<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception{ </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO Auto-generated method stub</span> List<Integer> al=<span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<Integer><span style="color: rgba(0, 0, 0, 1)">(); System.out.println(al.getClass()); al.getClass().getMethod(</span>"add",Object.<span style="color: rgba(0, 0, 255, 1)">class</span>).invoke(al,"abc"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(al.get(</span>0));<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">根据实际参数参数类型Integer确定调用println(Object obj)</span></span>
List</span><String> al_2=<span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<String><span style="color: rgba(0, 0, 0, 1)">(); System.out.println(al_2.getClass()); al_2.getClass().getMethod(</span>"add",Object.<span style="color: rgba(0, 0, 255, 1)">class</span>).invoke(al_2,1<span style="color: rgba(0, 0, 0, 1)">); System.out.println(al_2.get(</span>0));<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">根据实际参数参数类型String确定调用println(String obj)
//因此会引发 ClassCastException
}
}
③异常与泛型
//对异常使用泛型 public static <T extends Exception> void genericException()throws T{ try{
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span>(Exception e){<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">catch中的形参必须明确不能写 T e,catch要明确捕获的异常</span> <span style="color: rgba(0, 0, 255, 1)">throw</span> (T)e;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">e一定是Exception或其子类</span>
}
}④类型参数的类型推断:
/* 类型参数的类型推断总结: 1. static <E> void swap(E[] a, int i, int j)swap(new String[3],3,4) -> 推断出 E 为 String 2. static <T> void add(T a, T b) add(3,5) T->Integer
static <T> void add(T a, T b)
add(3.1,5)
T->Number// 取两者最小父类static <T> T add(T a, T b)
Number num=add(3.1,5)
这时候以返回值的实际参数类型为主:String->T->String, 编译器报错,Number num=add(3.1,5) 可以
3.
static <T> void copy(T[] a,T[] b)
copy(new Integer[5],new String[5])
OK, 编译器推断出类型变量 T 为 Numberstatic <T> void copy(Collection<T> a , T[] b) copy(new Vector<String>(), new Integer[5]) 编译器报错:<span style="color: rgba(255, 0, 0, 1)">根据参数化的Vector类实例将类型变量直接确定为String类型-> T就是String,那么第二个形参的 类型变量T为String</span>
*/
⑤如何获取泛型中的实际类型参数?
package com.itheima.day4;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.lang.reflect.ParameterizedType;
public class GenericReflect {System.out.println(actualType[0]+"\n"+actualType[1]);}</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> args </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> main(String[] args)<span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO Auto-generated method stub </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如何通过反射来获得泛型中的实际类型参数? </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">必须通过一个方法来获取,因为方法对象提供有获取形参类型的方法,由于泛型的实际类型参数在编译后被擦除,不能直接通过反射获取</span></span> Method method=GenericReflect.<span style="color: rgba(0, 0, 255, 1)">class</span>.getMethod("getParas",HashMap.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); Type[] type</span>=method.getGenericParameterTypes();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果形参类型是参数化类型,则为其返回的 Type 对象必须实际反映源代码中使用的实际类型参数。 </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果形参类型是类型变量或参数化类型,则创建它。否则将解析它。 </span> System.out.println(type[0<span style="color: rgba(0, 0, 0, 1)">]); Type[] actualType</span>=((ParameterizedType)type[0]).getActualTypeArguments();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回表示此类型实际类型参数的 Type 对象的数组。</span>
/*通过反射获取泛型中的实际类型参数*/ public static void getParas(HashMap<Integer,String> hs){}
}