一、前言
今天閱讀帆哥代碼的時候,看到了之前沒有見過的新東西, 比如java自定義注解類,如何獲取注解,如何反射內部類,this$0是什么意思? 於是乎,學習並整理了一下。
二、代碼示例
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field;
//自定義注解類 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String name() default "hjzgg"; } public class Main { public Main(Class cls) { Field[] fields = cls.getDeclaredFields(); TestAnnotation obj = null; try { obj = (TestAnnotation)cls.getConstructors()[0].newInstance(this);//獲取內部類對象 } catch (Exception e) { e.printStackTrace(); } for(Field field : fields) { System.out.println(field.getName() + " " + field.getType().getName()); if(!field.getName().equals("this$0")) { MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);//獲取注解類 String name = annotation.name(); field.setAccessible(true); try { switch(name) { case "hjzgg": switch(field.getType().getName()) { case "int": case "java.lang.Integer": field.set(obj, 555); break; case "java.lang.String": field.set(obj, "hehe"); break; } break; case "lxkdd": switch(field.getType().getName()) { case "int": case "java.lang.Integer": field.set(obj, 555); break; case "java.lang.String": field.set(obj, "hehe"); break; } break; default: break; } } catch (Exception e) { e.printStackTrace(); } } } System.out.println(obj); } public static void main(String[] args) throws InstantiationException, IllegalAccessException { new Main(TestAnnotation.class); } class TestAnnotation{ public TestAnnotation(){} @MyAnnotation(name="lxkdd") private int x; @MyAnnotation private String y; public int getX() { return x; } public void setX(int x) { this.x = x; } public String getY() { return y; } public void setY(String y) { this.y = y; } @Override public String toString() { return "x: " + x + ", y: " + y; } } }
三、代碼分析
1.如何編寫自定義注解
public @interface MyAnnotation { String value() default "hahaha"; }
感覺等價於
public class MyAnnotation extends java.lang.annotation.Annotation{ private String value = "hahaha"; public void setValue(String value){ this.value = value; } public String getValue(){ return value; } }
自定義注解類規則
@interface實際上是繼承了java.lang.annotation.Annotation,所以定義annotation時不能繼承其他annotation或interface. java.lang.annotation.Retention告訴編譯器如何對待 Annotation,使用Retention時,需要提供java.lang.annotation.RetentionPolicy的枚舉值.
public enum RetentionPolicy { SOURCE, // 編譯器處理完Annotation后不存儲在class中 CLASS, // 編譯器把Annotation存儲在class中,這是默認值 RUNTIME // 編譯器把Annotation存儲在class中,可以由虛擬機讀取,反射需要 }
java.lang.annotation.Target告訴編譯器Annotation使用在哪些地方,使用需要指定java.lang.annotation.ElementType的枚舉值.
public enum ElementType { TYPE, // 指定適用點為 class, interface, enum FIELD, // 指定適用點為 field METHOD, // 指定適用點為 method PARAMETER, // 指定適用點為 method 的 parameter CONSTRUCTOR, // 指定適用點為 constructor LOCAL_VARIABLE, // 指定使用點為 局部變量 ANNOTATION_TYPE, //指定適用點為 annotation 類型 PACKAGE // 指定適用點為 package }
java.lang.annotation.Documented用於指定該Annotation是否可以寫入javadoc中.
java.lang.annotation.Inherited用於指定該Annotation用於父類時是否能夠被子類繼承.
示例
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented //這個Annotation可以被寫入javadoc @Inherited //這個Annotation 可以被繼承 @Target({ElementType.CONSTRUCTOR,ElementType.METHOD}) //表示這個Annotation只能用於注釋 構造子和方法 @Retention(RetentionPolicy.CLASS) //表示這個Annotation存入class但vm不讀取 public @interface MyAnnotation { String value() default "hahaha"; }
2.如何獲取自定義注解
java.lang.reflect.AnnotatedElement接口提供了四個方法來訪問Annotation
public Annotation getAnnotation(Class annotationType); public Annotation[] getAnnotations(); public Annotation[] getDeclaredAnnotations(); public boolean isAnnotationPresent(Class annotationType);
來自:http://blog.csdn.net/foamflower/article/details/5946451
Class、Constructor、Field、Method、Package等都實現了該接口,可以通過這些方法訪問Annotation信息,前提是要訪問的Annotation指定Retention為RUNTIME.
Java內置的annotation有Override Deprecated SuppressWarnings.
Override只用於方法,它指明注釋的方法重寫父類的方法,如果不是,則編譯器報錯.
Deprecated指明該方法不建議使用.
SuppressWarnings告訴編譯器:我知道我的代碼沒問題.
3.this$0是什么意思?
public class Outer {//this$0 public class FirstInner {//this$1 public class SecondInner {//this$2 public class ThirdInner { } } }
}
說一個場景:當我們拿到了一個內部類的對象Inner,但是又想獲取其對應的外部類Outer,那么就可以通過this$0來獲取。this$0就是內部類所自動保留的一個指向所在外部類的引用。
//通過工具獲取到Inner實例對象 Outer.Inner inner = getInner(); //獲取內部類Inner的一個字段this$0信息 //this$0特指該內部類所在的外部類的引用,不需要手動定義,編譯時自動加上 Filed outerField = inner.getClass().getDeclaredField("this$0"); //this$0是私有的,提升訪問權限 outerField.setAccessible(true); //拿到該字段上的實例值 Outer outer = (Outer)outerField.get(inner);
4.java如何反射內部類
Class<?> cls = Class.forName("package.OuterClass$InnerClass"); or Class<?> cls = OuterClass.InnerClass.class;
(1)OuterClass.InnerClass obj = (OuterClass.InnerClass)cls.getConstructors()[0].newInstance(new OuterClass());
(2)OuterClass.InnerClass obj = (OuterClass.InnerClass)cls.getConstructor(OuterClass.class).newInstance(new OuterClass());
由此可見,內部類的無參構造器在通過反射機制獲取時,要指定其父類參數才可以獲得,否則將報如下異常:
java.lang.NoSuchMethodException: com.hjzgg.OuterClass$InnerClass.<init>() at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.getConstructor(Class.java:1825)
