什么是注解
概念
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.
作用
Annotations have a number of uses, among them:
Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
Runtime processing — Some annotations are available to be examined at runtime.
標(biāo)記,用于告訴編譯器一些信息
編譯時動態(tài)處理,如動態(tài)生成代碼
運行時動態(tài)處理,如得到注解信息
參考:
Java Annotation
Java Reflection - Annotations
注解的使用
格式
一個注解由一個@符號后面跟字符串組成,例如:
@Entity
java注解里面一般包含一些元素,這些元素類似于屬性或者參數(shù),可以用來設(shè)置值,比如我們有一個包含兩個元素的@Entity注解:
@Entity(name="mzw", job="碼農(nóng)")
該注解有兩個元素,name和job,分別賦予了元素值。
位置
注解可以用于描述一個類、接口、方法、方法參數(shù)、字段、局部變量等。
- 方法:
@Override
public void mySuperMethod() { ... }
@SuppressWarnings(value = "unchecked")
public void myMethod() { ... }
- 類
@Author(
name = "zphuan",
date = "3/24/2017"
)
public class MyClass() { ... }
- 方法參數(shù)
pulic void myMethod(@RequestParam String s){ ... }
- 局部變量
@Autowired
private MyClass myClass;
細(xì)節(jié)
如果注解沒有參數(shù),則不用加參數(shù),如:@Override
如果注解只有一個參數(shù),那么參數(shù)名字可以省略,如:
@SuppressWarnings("unchecked")
public void myMethod() { ... }
- 一個地方可以使用多個注解,如:
@Author(name = "Jane Doe")
@EBook
public class MyClass { ... }
- 可以重復(fù)使用注解,不過只有在java SE 8 才支持。比如:
@Author(name = "Jane Doe")
@Author(name = "John Smith")
public class MyClass { ... }
表明有兩個人對該類進行了代碼編寫。
內(nèi)置注解
java本身提供了三個內(nèi)置注解:
- @Override
注解是一個編譯時注解,它主要用在一個子類的方法中,當(dāng)被注解的子類的方法在父類中找不到與之匹配的方法時,編譯器會報錯。 - @Deprecated
可以用來描述一個類、方法或者字段,表示java不贊成使用這些被描述的對象,如果我們使用了這些類、方法或者字段,編譯器會給我們警告。 - @SuppressWarnings
作用是使編譯器忽略掉編譯器警告。比如,如果我們的一個方法調(diào)用了一個@Deprecated方法,或者做了一個不安全的類型轉(zhuǎn)換,此時編譯器會生成一個警告。如果我們不想看到這些警告,我們就可以使用@SuppressWarnings注解忽略掉這些警告。
自定義注解
元注解
元注解就是用來描述注解的注解,在java里面有下面幾個元注解:
- @Documented
作用是告訴JavaDoc工具,當(dāng)前注解本身也要顯示在Java Doc中。 - @Retention
用來定義注解的范圍,有下面三個范圍:
- RetentionPolicy.SOURCE
注解只存在于源碼中,不會存在于.class文件中,在編譯時會被忽略掉。 - RetentionPolicy.CLASS
注解只存在于.class文件中,在編譯期有效,但是在運行期會被忽略掉,這也是默認(rèn)范圍。 - RetentionPolicy.RUNTIME
在運行期有效,JVM在運行期通過反射獲得注解信息。
- @Target
用于指定注解作用于java的哪些元素,未標(biāo)注則表示可修飾所有。
ElementType.ANNOTATION_TYPE ?Annotation type declaration
ElementType.CONSTRUCTOR ?Constructor declaration
ElementType.FIELD ?Field declaration (includes enum constants)
ElementType.LOCAL_VARIABLE ?Local variable declaration
ElementType.METHOD ?Method declaration
ElementType.PACKAGE ?Package declaration.
ElementType.PARAMETER ?Parameter declaration
ElementType.TYPE ?Class, interface (including annotation type), or enum declaration
- @Inherited
注解表示當(dāng)前注解會被注解類的子類繼承。
參考:
Java Doc
注解解析
運行時注解
首先,先定義一個注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
public String name();
public String job();
}
上面通過元注解的定義,可以看出來自定義的注解是運行時注解,可以修飾所有的類型。
類注解
@MyAnnotation(name = "mzw", job = "給類添加了一個注解")
public class TestAnnotation { ... }
現(xiàn)在當(dāng)程序運行起來的時候我想要獲取到TestAnnotation中的@MyAnnotation的注解信息:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyAnnotation annotation = TestAnnotation.class.getAnnotation(MyAnnotation.class);
Log.e("mzw", "name == " + annotation.name());
Log.e("mzw", "value == " + annotation.value());
}
}
運行后執(zhí)行結(jié)果為:
24603-24603/demo.remer.myannotation E/mzw: name == mzw
24603-24603/demo.remer.myannotation E/mzw: value == 給類添加了一個注解
方法注解
public class TestAnnotation {
@MyAnnotation(name = "mzw", value = "給方法添加了一個注解")
public void test(){
}
}
當(dāng)程序運行時,可以獲取到類中test方法注解的信息:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Class clazz = Class.forName(TestAnnotation.class.getName());
for (Method method : clazz.getMethods()) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
if (annotation != null) {
Log.e("mzw", "name === " + method.getName() + "||" + annotation.name());
Log.e("mzw", "value == " + method.getName() + "||" + annotation.value());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
運行執(zhí)行的結(jié)果為:
20791-20791/demo.remer.myannotation E/mzw: name === test||mzw
20791-20791/demo.remer.myannotation E/mzw: value == test||給方法添加了一個注解
參數(shù)注解
public class TestAnnotation {
public static void test(@MyAnnotation(name = "參數(shù)", value = "參數(shù)上的注解") String parameter){ ... }
}
運行程序,可以獲取到方法上的參數(shù)注解信息:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Class clazz = Class.forName(TestAnnotation.class.getName());
for (Method method : clazz.getMethods()) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();
int i = 0;
for (Annotation[] annotations : parameterAnnotations) {
Class parameterType = parameterTypes[i++];
for (Annotation annotation : annotations) {
if (annotation instanceof MyAnnotation) {
MyAnnotation myAnnotation = (MyAnnotation) annotation;
Log.e("mzw", "param === " + parameterType.getName());
Log.e("mzw", "name === " + myAnnotation.name());
Log.e("mzw", "value === " + myAnnotation.value());
}
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
運行執(zhí)行的結(jié)果為:
17392-17392/demo.remer.myannotation E/mzw: param === java.lang.String
17392-17392/demo.remer.myannotation E/mzw: name === 參數(shù)
17392-17392/demo.remer.myannotation E/mzw: value === 參數(shù)上的注解
注意
isAnnotationPresent(AnnotationName.class) 可以判斷Target是否被某個注解修飾