一、前言
今天閱讀帆哥代碼的時候,看到了之前沒有見過的新東西, 比如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;@MyAnnotationprivate 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;}@Overridepublic 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)
?