从JDK1.5开始,Java增加了对元数据的支持,也就是Annotation(注解),它是一种元数据形式。属于Java中的一种数据类型,其地位和类、接口、数组、枚举这些都是一样的。
简单理解注解其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用Annotation可以在不改变原有逻辑的情况下,在源文件中加入一些补充信息,代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
注释:简单来说注释是对代码的解释和说明类似于使用说明,其目的是提高代码的可读性。它只存在于我们的java源代码中,对于编译和运行没有任何的作用,是不会被编译到我们的class文件当中的
注解:注解主要是通过标注包、类、字段、方法、局部变量、方法参数等元素,告诉JVM这些元素的附加信息。它可以通过配置,在运行中让JVM去读取它,并完成对应的操作,一般我们会通过反射来获取某个元素上标注的注解。
最大的区别在于注解可以被编译器打包进class文件而注释不行,总结一句话就是注释是给我们人看的,而注解则是给编译器和JVM看的。
例如我们书写如下代码:
public class Test{
public string toString(int x){
return "我还没有秃";
}
这个示例中我们想重写Test类的toString()方法,那么toString()方法被重写了吗?
答案肯定是否定的,因为Object定义的toString()方法是一个无参方法,这种写法属于是方法的重载。编译器会认为这是一个全新的方法,故而也不会报错。
这时我们如果在toString方法前,加上一个@Override注解后,编译就会发生错误。
public class Test{
@Override
public string toString(int x){
return "我还没有秃";
}
这是为什么呢?
因为加上@Override注解之后,编译器会强制检测该方法是否重写了父类方法。如果没有,则编译错误。那么从这个例子中我们就能看出,@Override这个Annotation注解,不仅仅是给人读的,同样还会影响到编译器。可以看到注解Annotation会在编译期,影响到编译器的编译动作。所以注解Annotation不是注释。
注解 | 作用 |
---|---|
@author | 标明开发该类模版的作者,多个作者之间使用,分割 |
@version | 标明该类模块的版本 |
@see | 参考转向,也就是相关主题 |
@since | 从哪个版本开始增加的 |
@param | 对方法中某参数的说明,如果没有参数就不能写 |
@return | 对方法返回值的说明,如果方法的返回值类型是void就不能写 |
@exception | 对方法可能抛出的异常进行说明,如果方法没有用thorws显示抛出的异常就不能写 |
其中:
/**
* @author 琦玉
* @version 1.0
*/
public class Test{
/**
* 程序的主方法,程序的入口
* @param args String[] 命令行参数
* @see Student
*/
public static void main(String[] args) {
new Student();
}
/**
*
* @param age 年龄
* @return String
* @exception RuntimeException 当年龄大于100的时候
* @exception IndexOutOfBoundsException 当年龄小于0的时候
*/
public String play(int age){
if (age>100){
throw new RuntimeException();
}
if (age<0){
throw new IndexOutOfBoundsException();
}
return “变秃”;
}
}
}
关键字 | 被抑制的警告类型 |
---|---|
all | 抑制所有警告 |
deprecation | 抑制使用了过时的类或方法的警告 |
fallthrough | 抑制switch块中没有break语句警告 |
finally | 抑制finally块不能正常完成的警告 |
rawtypes | 抑制没有使用泛型的警告 |
serial | 抑制可序列化类没有使用序列化ID的警告 |
unchecked | 抑制未检查操作的警告 |
unused | 抑制变量或方法申明定义后未使用的警告 |
public class AnnotationTest {
public static void main(String[] args) {
@SuppressWarnings("unused") int a = 10;
}
@Deprecated
public void print() {
System.out.println(" 过时的方法");
}
@Override
public String toString() {
return " 重写的 toString 方法() " ;
}
}
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
@Transactional(propagation = Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED, readOnly = false, timeout = 3)
public void buyBook(String username,String isbn){
//1.查询书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新库存
bookShopDao.updateBookStock(isbn);
//3.更新用户的余额
bookShopDao.updateUserAccount(username,price);
}
<tx:advice transaction-manager="dataSourceTransactionManager" id="txAdvice">
<tx:attributes>
<tx:method name="buyBook" propagation="REQUIRES_NEW"
isolation="READ_COMMITTED" read-only="false" timeout="3" />
tx:attributes>
tx:advice>
在开发中,开发者有时候需要自定义注解完成某些配置。自定义注解使用的基本流程如下:
@MyAnnotation(value = "琦玉")
public class MyAnnotationTest {
public static void main(String[] args) {
Class cla = MyAnnotationTest.class;
Annotation an = cla.getAnnotation(MyAnnotation.class);
MyAnnotation m = (MyAnnotation) an;
String info = m.value();
System.out.println(info);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnnotation {
String value() default "111111" ;
}
元注解是专门修饰注解的注解,是为了更好的设计自定义注解的细节而专门设计的。
JDK1.5中提供的四个4个标准的meta-annotation类型,分别是:
@Target注解,是专门用来限定某个自定义注解能被应用到Java代码的那些位置。它使用一个枚举类型ElementType定义了限定范围的选择。
@Target的使用:
枚举值 | 注解能够被应用的地方 |
---|---|
ElementType.ANNOTTION_TYPE | 注解类型的声明 |
ElementType.CONSTRUCTOR | 构造方法的声明 |
ElementType.FIELD | 属性的声明 |
ElementType.LOCAL.VARIABLE | 局部变量的声明 |
ElementType.METHOD | 方法的声明 |
ElementType.PACKAGE | 包的声明 |
ElementType.PARAMETER | 方法参数的声明 |
ElementType.TYPE | 类、接口及枚举的声明 |
只能用于修饰一个Annotation定义,用于指定该Annotation的生命周期,@Rentention包含一个RetentionPolicy类型的成员变量,使用@Rentention时必须为该value成员变量指定值:
@Retention(RetentionPolicy.RUNTIME) //RetentionPolicy.CLASS/RetentionPolicy.SOURCE
public @interface MyAnnotation{ //MyAnnotation的生命力被保持到运行阶段。运行时可以通过反射的方式获得注解信息
public String name();
int age();
int[] array();
}
@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到javadoc文档中。默认情况下,javadoc是不包括注解的。
@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。
定义注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
public String name();
int age() default 18;
int[] scores();
}
定义学生类,使用TestAnnotation 注解:
@TestAnnotation(name="tom",age=24,scores={80,65,92})
public class Student {
age元素被重新赋值为24
}
@TestAnnotation(name="tom", scores={80,65,92})
public class Student {
age元素使用默认值18
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {}
@TestAnnotation
public class Test {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String value();
}
@TestAnnotation(“琦玉”)
public class Test {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String[] addresses();
}
@TestAnnotation(addresses=“NYC”)
public class Test {
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String param();
}
public class Test {
public void speak(
@TestAnnotation(param="name") String userName){
}}
只有当一个注解使用了@Retention(RetentionPolicy.RUNTIME)修饰,才表示该注解能够保持到运行期,才能在运行期获到它。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
public String name();
int age() default 18;
int[] scores();
}
@TestAnnotation(name="琦玉",age=24,scores={80,65,92})
public class Student {}
Class studentClass = Student.class;
//判断Student类中,是否定义了@TestAnnotation注解
if(studentClass.isAnnotationPresent(TestAnnotation.class)) {
//得到Student类中定义的注解
TestAnnotation anno = (TestAnnotation)
studentClass.getAnnotation(TestAnnotation.class);
//得到注解元素的值
System.out.println(anno.name()+" "+
anno.age()+" "+anno.scores());
}
XML与注解在配置这个应用场景中互补性是非常高的。XML的缺点就是注解的优点,反之亦然。这也是目前在JavaEE的各大框架中XML+Annotation配置流的原因。
一般来说,把与代码关联度不高,并且改动可能性大的配置写在XML文件中。专门配置java代码级别关联度的,以后改动度小的则使用注解Annotation。