注解的概念
/**
目标:注解的概念。
注解:
用在类上,方法上,成员变量,构造器,...上对成分进行编译约束,标记等操作的。
注解是JDK1.5的新特性。
注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息。
注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。
注解作用:
1.标记。
2.方法重写约束 @Override
3.函数式接口约束。 @FunctionalInterface.
4.现今最牛逼的框架技术多半都是在使用注解和反射。都是属于框架的底层基础技术。
我们之前用的注解都是别人写好的,今天我们自己来定义一下注解。
*/
public class AnnotationDemo01 {
}
@FunctionalInterface
interface A{
void test();
}
自定义注解
/**
目标:我们之前都是用别人写好的注解,今天我们自己来做注解。
自定义注解的格式:
修饰符 @interface 注解名{
// 注解属性
}
小结:
自定义注解用@interface关键字。
使用注解的格式:@注解名称。
注解默认可以标记很多地方。
*/
@Book
@MyTest
public class MyBook {
@Book
@MyTest
private MyBook(){
}
@Book
@MyTest
public static void main(@MyTest String[] args) {
@MyTest
@Book
int age = 12;
}
}
@interface Book{
}
@interface MyTest{
}
注解的属性
/**
目标:注解的属性:
属性的格式
- 格式1:数据类型 属性名();
- 格式2:数据类型 属性名() default 默认值;
属性适用的数据类型:
八种数据数据类型(int,short,long,double,byte
,char,boolean,float)
String,Class
以上类型的数组形式都支持
小结:
注解可以有属性,属性名必须带()
在用注解的时候,属性必须赋值,除非这个属性有默认值!!
*/
@MyBook(name="《精通Java基础》",authors = {"播仔","Dlei","播妞"} , price = 99.9 )
public class AnnotationDemo01 {
@MyBook(name="《精通MySQL数据库入门到删库跑路》",authors = {"小白","小黑"} ,
price = 19.9 , address = "北京")
public static void main(String[] args) {
}
}
// 自定义一个注解
@interface MyBook{
String name();
String[] authors(); // 数组
double price();
String address() default "广州";
}
注解的特殊属性_value
/**
目标:注解的特殊属性名称:value
value属性,如果只有一个value属性的情况下,
使用value属性的时候可以省略value名称不写!!
但是如果有多个属性,且多个属性没有默认值,那么value是不能省略的。
*/
//@Book(value = "/deleteBook.action")
//@Book("/deleteBook.action")
//@Book(value = "/deleteBook.action" , age = 12)
//@Book("/deleteBook.action")
public class AnnotationDemo01{
}
@interface Book{
String value();
int age() default 10;
}
元注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
目标:元注解
元注解是sun公司提供的。
元注解是用在自定义注解上的注解。
元注解是用来注解自定义注解的。
元注解有两个:
@Target:约束自定义注解只能在哪些地方使用,
-- 但是默认的注解可以在类,方法,构造器,成员变量,... 使用。
@Retention:申明注解的生命周期
-- 申明注解的作用范围:编译时,运行时。
@Target
* 作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
* 可使用的值定义在ElementType枚举类中,常用值如下
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE, 局部变量
@Retention
作用:用来标识注解的生命周期(有效存活范围)
* 可使用的值定义在RetentionPolicy枚举类中,常用值如下
* SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
* CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
* RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
小结:
@Target约束自定义注解可以标记的范围。
@Retention用来约束自定义注解的存活范围。
*/
public class AnnotationDemo01{
// @MyTest
private String name;
@MyTest
public static void main( String[] args) {
}
@MyTest
public void testRun(){
}
}
//@Target({ElementType.METHOD , ElementType.FIELD}) // 申明只能注解方法和成员变量!
@Target(ElementType.METHOD ) // 申明只能注解方法
@Retention(RetentionPolicy.RUNTIME) // 申明注解从写代码一直到运行还在,永远存活!!
@interface MyTest{
}
注解的解析
import org.junit.Test;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
目标:注解的解析
我们会使用注解注释一个类的成分,那么就设计到要解析出这些注解的数据。
开发中经常要知道一个类的成分上面到底有哪些注解,注解有哪些属性数据,这都需要进行注解的解析。
与注解解析相关的接口
1. Annotation: 注解类型,该类是所有注解的父类。注解都是一个Annotation的对象
2. AnnotatedElement:该接口定义了与注解解析相关的方法
所有的类成分Class, Method , Field , Constructor:都实现了AnnotatedElement接口
他们都拥有解析注解的能力:
a.Annotation[] getDeclaredAnnotations()
获得当前对象上使用的所有注解,返回注解数组。
b.T getDeclaredAnnotation(Class<T> annotationClass)
根据注解类型获得对应注解对象
c.boolean isAnnotationPresent(Class<Annotation> annotationClass)
判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false
解析注解数据的原理
* 注解在哪个成分上,我们就先拿哪个成分对象。
* 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
* 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
* 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
需求:(了解即可)
1. 定义注解Book,要求如下:
- 包含属性:String value() 书名
- 包含属性:double price() 价格,默认值为 100
- 包含属性:String[] authors() 多位作者
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:RUNTIME
2. 定义BookStore类,在类和成员方法上使用Book注解
3. 定义AnnotationDemo01测试类获取Book注解上的数据
*/
public class AnnotationDemo01 {
@Test
public void parseClass(){
// 1.定位Class类对象
Class c = BookStore.class ;
// 2.判断这个类上是否使用了某个注解
if(c.isAnnotationPresent(Book.class)){
// 3.获取这个注解对象
Book book = (Book) c.getDeclaredAnnotation(Book.class);
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
}
@Test
public void parseMethod() throws Exception {
// 1.定位Class类对象
Class c = BookStore.class ;
// 2.定位方法对象
Method run = c.getDeclaredMethod("run");
// 3.判断这个方法上是否使用了某个注解
if(run.isAnnotationPresent(Book.class)){
// 3.获取这个注解对象
Book book = (Book) run.getDeclaredAnnotation(Book.class);
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
}
}
@Book(value = "《Java基础到精通》" , price = 99.5 , authors = {"波仔","波妞"})
class BookStore{
@Book(value = "《Mybatis持久层框架》" , price = 199.5 , authors = {"dlei","播客"})
public void run(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD}) // 类和成员方法上使用
@Retention(RetentionPolicy.RUNTIME) // 注解永久存活
@interface Book{
String value();
double price() default 100;
String[] authors();
}
自定义注解模拟写一个Junit框架的基本使用
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
目标:自定义注解模拟写一个Junit框架的基本使用。
需求:定义若干个方法,只要加了MyTest注解,
就可以被自动触发执行。
分析:
(1)定义一个自定义注解MyTest.
-- 只能注解方法。
-- 存活范围一直都在。
(2)定义若干个方法,只要有@MyTest注解的方法就能被触发执行!!
没有这个注解的方法不能执行!!
小结:
注解和反射可以配合解决一些框架思想
注解可以实现标记的成分做特殊处理!!
*/
public class TestDemo{
@MyTest
public void test01(){
System.out.println("===test01===");
}
public void test02(){
System.out.println("===test02===");
}
@MyTest
public void test03(){
System.out.println("===test03===");
}
@MyTest
public void test04(){
System.out.println("===test04===");
}
public static void main(String[] args) throws Exception {
TestDemo t = new TestDemo();
// 模拟测试类的启动按钮,实现有注解标记的方法就要触发执行。
// 1.得到类对象
Class c = TestDemo.class;
// 2.获取类中全部方法对象
Method[] methods = c.getDeclaredMethods();
// 3.遍历全部方法,有注解就触发执行
for (Method method : methods) {
if(method.isAnnotationPresent(MyTest.class)){
// 触发此方法执行。
method.invoke(t);
}
}
}
}
@Target(ElementType.METHOD) // 只能注解方法!
@Retention(RetentionPolicy.RUNTIME) // 一直都活着
@interface MyTest{
}