22、java中的注解
注解是什么?
?????? 注解可以理解成注釋、標記、標簽的意思,用來標記類、方法等。就相當于現實生活中的一些事物,上邊貼一個標簽或者寫一些注釋性文字來描述它可以用來做什么、怎么用、何時用等信息。Java中的注解也是一樣的,用來表示被標記的部分可以做什么、怎么做、何時做等信息。
注解可以用來做什么?
??????? 注解具有生成文檔、在編譯時期對代碼進行檢查、運行時期可以動態的實現業務功能,降低耦合度等用途。
注解怎么用?
?????? Java中的注解可分為三類:內置的可以直接用于功能代碼的、內置的元注解、自定義注解,其中元注解用于來注釋自定義的注解,使用@interface關鍵字來聲明自定義注解,使用注解時直接@注解名即可。接下來使用看一下實際中的用法:
1、三個內置注解:
//@SuppressWarnings 用于消除警告 @SuppressWarnings("unused") public void warningTest() {//@SuppressWarnings("unused")int i = 0;int j = 0;System.out.println("就是不使用i"); }// @Override 用于表示重寫方法 @Override public void run() {super.run(); }//@Deprecated 表示被注釋的方法已經過時了 @Deprecated public void deprecatedTest() {}2、自定義注解、元注解:
// 如果沒有 @Documented ,定義的注解不會被javadoc寫入到文檔中,但是我測試著可以哎,了解即可 @Documented //@Retention 注解用于確定注解的保留期限 @Retention(RetentionPolicy.RUNTIME) // @Target 用于標記此注解可以使用的位置是什么:類、方法等 @Target(ElementType.TYPE)public @interface FirstPrintAnnotation {//這里可以定義設置的值,專業名詞:注解元素//類型只能是:基本數據類型、String、Class、Enum、Annotation//類似于定義方法似的定義一個值,用于設置數據//String value default “h” 或//String []values() ;//這個值在使用注解值一定要是一個確定的值String value(); }/*** 用來查看AnnontationTest類中用了注解@FirstPrintAnnotation的方法,并獲取方法名*/ public class AnnontationTest{public static void main(String[] args) {Class<AnnontationTest> clazz = AnnontationTest.class;Method[] methods = clazz.getMethods();for (Method method : methods) {FirstPrintAnnotation firstPrintAnnotation = method.getAnnotation(FirstPrintAnnotation.class);if (firstPrintAnnotation != null) {System.out.println("使用了注解FirstPrintAnnotationd的方法有:"+firstPrintAnnotation.value());}} }@FirstPrintAnnotation("test1") public void test1() {} @FirstPrintAnnotation("test2") public void test2() {} @FirstPrintAnnotation("test3") public void test3() { }}----------------------------------------------------------------- //@Retention 源碼 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention {RetentionPolicy value(); }//RetentionPolicy 代碼 //源碼解釋 //它們與 { Retention } 元注解類型一起使用,以指定注釋將被保留多長時間。 public enum RetentionPolicy {//注釋將被編譯器丟棄,就是只在源碼中用以下SOURCE,//注解由編譯器記錄在類文件中,不需要在運行時被JVM保留,默認情況,可以在class文件中用CLASS,//注釋將由編譯器記錄在類文件中,并在運行時由JVM保留,所以可以通過反射獲取,可在運行時類中獲取RUNTIME }------------------------------------------------------------------ //@Target 源碼 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target {ElementType[] value(); }//ElementType 源碼 //這個枚舉類型的常量提供了Java程序中可能出現注釋的語法位置的簡單分類。 public enum ElementType {//類、接口(包括注釋類型)或枚舉聲明TYPE,//字段聲明(包括枚舉常量)FIELD,//方法聲明METHOD,//正式的參數聲明PARAMETER,//構造函數聲明CONSTRUCTOR,//本地變量聲明LOCAL_VARIABLE,//注解類型聲明ANNOTATION_TYPE,//包說明PACKAGE,//類型參數聲明TYPE_PARAMETER,//類型的使用TYPE_USE }注解的實現原理是什么?
?????? 對編譯后的注解類進行反編譯,就會看到注解會被編譯成一個接口類型,并且繼承了Annotation接口,這些都是jvm隱式做的工作沒必要深究,所以注解不可以像平常類一樣使用new進行實例化,但是可以設置它存在于運行時期,所以可以通過反射的方式獲取并解析注解。那他到底是怎么工作的呢?通常處理注解都另寫一個注解處理器類,這里就不寫了,原理就是根據使用了注解的類、方法或者變量來通過反射技術獲取到注解,然后對相應代碼進行額外功能的添加。簡單的看一下如下代碼(其實就是上邊的實例代碼):
public static void main(String[] args) throws InstantiationException, IllegalAccessException {Class<AnnontationTest> clazz = AnnontationTest.class;Method[] methods = clazz.getMethods();for (Method method : methods) {FirstPrintAnnotation firstPrintAnnotation = method.getAnnotation(FirstPrintAnnotation.class);if (firstPrintAnnotation != null) {//所屬類型:com.sun.proxy.$Proxy1System.out.println("所屬類型:"+firstPrintAnnotation.getClass().getName());Class<?>[] classes = firstPrintAnnotation.getClass().getInterfaces();for (Class<?> class2 : classes) {//實現的接口:interface com.czp.annontation.FirstPrintAnnotationSystem.out.println("實現的接口:"+class2);}Class<?> superclass = firstPrintAnnotation.getClass().getSuperclass();//繼承的類:class java.lang.reflect.ProxySystem.out.println("繼承的類:"+superclass);//使用注解的值:test1//實際上是 代理對象調用的是AnnotationInvocationHandler 中的invoke方法//在此處打上斷點,測試即可firstPrintAnnotation.value();System.out.println("使用注解的值:"+firstPrintAnnotation.value());}}}??????? 因為此注解可以存在于運行時階段,所以可以通過反射技術獲取到它的運行時類的對象,打印出來是:class com.sun.proxy.$Proxy1。這樣我們就可以確定了,注解工作是依賴于java中的動態代理類,通過反射生成注解對應接口的對象,實際上是java的動態代理類,然后通過代理對象去調用注解元素,當使用代理類調用方法時,實際上是調用AnnotationInvocationHandler 的invoke 方法,在成員變量memberValues中獲取的值。看一下運行時的源碼:
//以根據方法反射注解為例分析//思路:先將被類的運行時期的注解放到一個Map中,然后根據傳入的注解類型獲取對應的注解的代理類//根據注解類型獲取對應的Map集合中的注解對象 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {Objects.requireNonNull(annotationClass);return annotationClass.cast(declaredAnnotations().get(annotationClass)); } //使用一個 Map<注解的運行時類,注解> 來存放注解信息 private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations; private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {//if (declaredAnnotations == null) {Executable root = getRoot();//Excecutable用于注釋共享。Method繼承自Excecutableif (root != null) {declaredAnnotations = root.declaredAnnotations();} else {declaredAnnotations = AnnotationParser.parseAnnotations(//一個注解分析器getAnnotationBytes(),sun.misc.SharedSecrets.getJavaLangAccess().getConstantPool(getDeclaringClass()),getDeclaringClass());}}return declaredAnnotations; }//獲取Map集合 public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(byte[] var0, ConstantPool var1, Class<?> var2) {if (var0 == null) {return Collections.emptyMap();} else {return parseAnnotations2(var0, var1, var2, (Class[])null);} }private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(byte[] var0, ConstantPool var1, Class<?> var2, Class<? extends Annotation>[] var3) {LinkedHashMap var4 = new LinkedHashMap();ByteBuffer var5 = ByteBuffer.wrap(var0);int var6 = var5.getShort() & '?';for(int var7 = 0; var7 < var6; ++var7) {Annotation var8 = parseAnnotation2(var5, var1, var2, false, var3);//這是獲取注解具體實現類的操作if (var8 != null) {Class var9 = var8.annotationType();//當注解可以到運行時期使用時,則添加到LinkedHashMap var4中if (AnnotationType.getInstance(var9).retention() == RetentionPolicy.RUNTIME && var4.put(var9, var8) != null) {throw new AnnotationFormatError("Duplicate annotation for class: " + var9 + ": " + var8);}}}return var4; }AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {Class[] var3 = var1.getInterfaces();if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {//初始化注解數組this.type = var1;this.memberValues = var2;} else {throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");} }public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);} else if (var5.length != 0) {throw new AssertionError("Too many parameters for an annotation method");} else {byte var7 = -1;switch (var4.hashCode()) {case -1776922004 :if (var4.equals("toString")) {var7 = 0;}break;case 147696667 :if (var4.equals("hashCode")) {var7 = 1;}break;case 1444986633 :if (var4.equals("annotationType")) {var7 = 2;}}switch (var7) {case 0 :return this.toStringImpl();case 1 :return this.hashCodeImpl();case 2 :return this.type;default :Object var6 = this.memberValues.get(var4);//在數組中獲取對應的值if (var6 == null) {throw new IncompleteAnnotationException(this.type, var4);} else if (var6 instanceof ExceptionProxy) {throw ((ExceptionProxy) var6).generateException();} else {if (var6.getClass().isArray() && Array.getLength(var6) != 0) {var6 = this.cloneArray(var6);}return var6;}}} }?????? 簡單介紹一下動態代理:就是在運行時期確定代理的類型。使用動態代理時需要使用都的兩個類是Proxy和InvacationHandler。做一個簡單的例子,代碼如下:
//被代理類的公共接口 public interface Subject {void show(); }-------------------------------------------------------------//被代理類的一個實現 RealSubject public class RealSubject implements Subject{@Overridepublic void show() {System.out.println("動態代理");} }//被代理類的一個實現 RealSubject public class RealSubject implements Subject{@Overridepublic void show() {System.out.println("動態代理");} }-------------------------------------------------------------//動態代理必須要實現InvocationHandler接口,而不是被代理類的公共接口 public class DynProxy<T> implements InvocationHandler{//被代理類對象private T t;//獲取代理對象public T getBlind(T t){this.t = t;return (T) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), this);}//必須要重寫這個方法@Overridepublic Object invoke(Object porxy, Method method, Object[] args) throws Throwable {return method.invoke(t, args);} }-------------------------------------------------------------//測試 public class Test {public static void main(String[] args) {//確定一下代理的公共接口類型DynProxy<Subject> proxy = new DynProxy<>();//獲取代理對象Subject blind = proxy.getBlind(new RealSubject());System.out.println(blind.getClass().getName());//com.sun.proxy.$Proxy0blind.show();} }?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的22、java中的注解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2998 元新低:松下 85mm F1.
- 下一篇: 23、java中的网编基础