什么是注解?
注解(Annotation),又被称为Java标注,它和接口类等一样,都属于一种数据类型,它是JDK1.5版本之后引入的新特性,它为源程序提供了一种关联任何元素的方法。也可以将注解理解为是一个标签,是加在类、方法、变量、属性上的标签。
注解的作用以及作用范围
注解经常要与放搭配使用,是Java提供的一套机制,让我们可以对方法、类、参数、包、域以及变量等添加标准(即附上某些信息),在以后某个时段通过反射将标注的信息提取出来以供使用。
它的作用范围有:在编译期间有效、运行期间有效、以及源码期间有效。
编译期间有效:@Override(表示重写)、@Deprecated(表示已过时、不推荐使用)、@Suppresswarming(抑制警告显示),这一类的注解可以帮助我们告诉编译器一些信息。
运行期间有效:@Test
源码期间有效:@Author、@Since、@See,它可以用于帮助我们对当前的源码生成帮助文档。如下:
元注解
元注解简单来说就是注解的注解,它的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明,元注解主要有以下4个。
@Target
@Target 指定了注解能被运用的地方(即注解的作用域),具体的作用域参考如下:
取值 | 作用域 |
---|---|
ElementType.TYPE | 作用于类、接口。 |
ElementType.CONSTRUCTOR | 作用于构造器 |
ElementType.FIELD | 作用于类成员变量 |
ElementType.LOCAL_VARIABLE | 用于描述局部变量 |
ElementType.METHOD | 作用于方法 |
ElementType.PACKAGE | 作用于包 |
ElementType.PARAMETER | 作用于参数 |
@Documented
它的作用是能够将注解中的元素包含到 Javadoc 中去。
@Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Retention
用于定义该Annotation被保留的时间长短
- RetentionPolicy.SOURCE 注解只在源码阶段保留
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们
JDK出现的3种注解
@Override
java.lang.Override 是一个marker annotation类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。
@Deprecated
Deprecated也是一种marker annotation。当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。
注意:@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过时、它应当如何被禁止或者替代的描述)
@SuppressWarnings
此注解能告诉Java编译器关闭对类、方法及成员变量的警告。
有时编译时会提出一些警告,对于这些警告有的隐藏着Bug,有的是无法避免的,对于某些不想看到的警告信息,可以通过这个注解来屏蔽。
SuppressWarning不是一个marker annotation。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。
自定义注解@Test
@MyTest
package com.gzgs.demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义MyTest注解模拟实现@Test
* @author SDL
*
*/
//说明注解的作用域以及作用范围,这里分别是运行期间有效、以及作用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
public long timeout() default -1;
}
UserDao
package com.gzgs.demo;
public class UserDao {
static {
System.out.println("执行了");
}
///这里凡是加了MyTest的方法都会被执行,就像是@Test一样
@MyTest
public void method_1() {
// TODO 自动生成的方法存根
System.out.println("hello1");
}
@MyTest
public void method_2() {
// TODO 自动生成的方法存根
System.out.println("hello2");
}
@MyTest
public void method_3() {
// TODO 自动生成的方法存根
System.out.println("hello3");
}
}
MyJunit
package com.gzgs.demo;
import java.lang.reflect.Method;
public class MyJunit {
public static void main(String[] args) throws Exception {
//通过放射获取字节码文件
Class clazz=Class.forName("com.gzgs.demo.UserDao");
//通过字节码文件获取UserDao所有的方法
Method[] methods = clazz.getMethods();
//遍历所有的方法,判断是否含有@MyTest注解
for (Method method : methods) {
if(method.isAnnotationPresent(MyTest.class)) {
//System.out.println(method.getName());
//如果有就执行该方法
method.invoke(new UserDao());
}
}
}
}
运行结果
什么是反射?
反射其实就是Java的一种机制,利用这种机制我们可以获取已经加载到JVM中的Java对象,进而实现访问、检测、和修改和实例化该对象的功能,非常变态,可以获取该Java对象的构造方法、方法、接口等。
怎么使用反射?
使用反射机制的最重要的一步就是获取对象的class(字节码)文件,有三种方法可以获得。
- class clazz= Class.forName(“UserDao”);
- Class clazz = UserDao.class;
- Class clazz = new UserDao().getClass();
接下来获取了class的对象之后就可以调用方法了,下面是从《Java 从入门到精通》这本书上截的一些关于常用方法的图。摘抄自第九章第三节,Class对象与Java反射。