Java学习之路(一)

JavaWeb基础学习之注解

1. Annotation(注解)介绍:

(1)Java5开始,Java开始对元数据的支持,也就是Annotation(注解);
(2)所有的Annotation都是java.lang.annotation接口的子接口,所以 Annotation是一种特殊的接口(枚举也是特殊的类)
(3)@interface Override {} —> interface Override extends java.lang.annotation.Annotation{};
(4)注解用来为程序元素(类,方法,成员变量等)设置元数据,注解,标签,Annotation都是一体。
使用注解需要注意,必须有三方参与才有意义:
1)得有注解标签本身;
2)被贴的程序元素(类,字段,构造器,方法,参数,包等)
3)由第三方的程序使用反射的手段来赋予注解特殊的功能

2.Annotation(注解)简单使用代码实例:

import java.util.List;

public class AnnoDemo extends Object{
    //重载父类方法
	@Override
	public String toString() {
		return null ;	
	}
	//消除警告,实际问题并未解决
	@SuppressWarnings({ "rawtypes", "unused" })
	public void show() {
		List list = null;
	}
}

Annotation贴在类,字段,构造器,方法,参数,包等上

3.常见的注解及其用法

JDK5中提供了三个注解
@Override:标记重写父类的方法
@Deprecated:标记过时/不推荐使用的方法和类
@SuppressWarings:抑制编译器发出的警告,仅仅是看不到警告(问题依然存在)
@SuppressWarings(“all”):抑制所有的警告
注意:虽然Java5才出现注解,但是Java.util.Date在Java1.1就过时了,这个时候该如何进行注解呢?

/**
     * Allocates a <code>Date</code> object and initializes it so that
     * it represents midnight, local time, at the beginning of the day
     * specified by the <code>year</code>, <code>month</code>, and
     * <code>date</code> arguments.
     *
     * @param   year    the year minus 1900.
     * @param   month   the month between 0-11.
     * @param   date    the day of the month between 1-31.
     * @see     java.util.Calendar
     * @deprecated As of JDK version 1.1,
     * replaced by <code>Calendar.set(year + 1900, month, date)</code>
     * or <code>GregorianCalendar(year + 1900, month, date)</code>.
     */
    @Deprecated
    public Date(int year, int month, int date) {
        this(year, month, date, 0, 0, 0);
    }

原来是通过文档注释来实现注解功能:

/**
	 * 
	 * @deprecated
	 */	public void show2() {
		java.util.Date d = new Date();
	}

在JDK7中新增了一个注解:
@SafeVarargs:抑制堆污染发出的警告(问题依然存在)一个方法中同时出现可变参数和泛型

     @SafeVarargs
	 public static <T> List<T> asList(T... a){
		return null;
	 }

4.注解的参数问题

(1)为什么有的注解允许接收参数,有的却不可以?
比如:@SuppressWarings(“all”),@SuppressWarnings({ “rawtypes”, “unused” }),而@Override就没有
通过阅读各自的源代码发现,@SuppressWarnings有一个value的抽象方法,其他没有。

public @interface SuppressWarnings {
    /**
     * The set of warnings that are to be suppressed by the compiler in the
     * annotated element.  Duplicate names are permitted.  The second and
     * successive occurrences of a name are ignored.  The presence of
     * unrecognized warning names is <i>not</i> an error: Compilers must
     * ignore any warning names they do not recognize.  They are, however,
     * free to emit a warning if an annotation contains an unrecognized
     * warning name.
     *
     * <p>Compiler vendors should document the warning names they support in
     * conjunction with this annotation type. They are encouraged to cooperate
     * to ensure that the same names work across multiple compilers.
     */
    String[] value();
}

(2)为什么有的注解可以贴在类/方法/变量上,而有的只能贴在方法上?
元注解@Target决定了该注解可以贴在什么地方(元注解中介绍)

5.元注解

(1)什么是元注解?
元注解:在定义注解的时候用来贴在注解上的注解,用来限定注解的用法
(2)元注解有哪些?
@Retention,@Target,@Documented,@Inherited
其中@Documented,@Inherited只需了解
@Documented:表示注解会被Javadoc指令编辑到API中
@Inherited:表示注解会遗传给子类
(3)重要元注解(@Retention,@Target)

  • @Retention:决定注解可以保存到哪个时期
    注解的有效期有3个都封装在枚举:RetentionPolicy
    RetentionPolicy.SOURCE:表示注解只会存在于源文件中,不会被编译到字节码中
    RetentionPolicy.CLASS:表示注解会被编译到字节码中,但是JVM不加载注解
    RetentionPolicy.RUNTIME:表示注解会被编译到字节中,会随着字节码的加载而进入JVM,因此可以反射性地读取。
    在这里插入图片描述
    我们开发中的自定义的注解的有效期都要使用RUNTIME,这样才能在程序运行时使用反射赋予注解功能
  • @Target:决定该注解可以贴在什么地方
    可以贴注解的地方很多,都封装在枚举:ElementType
    ElementType.ANNOTATION_TYPE:贴在注解上
    ElementType.FIELD:贴在字段上(包括枚举常量)
    ElementType.METHOD:贴在方法上
    ElementType.TYPE:贴在类、接口或枚举上
    ElementType.CONSTRUCTOR:贴在构造方法上
    ElementType.PACKAGE:贴在包上(极少使用)
    ElementType.PARAMETER:贴在参数上
    ElementType.LOCAL_VARIABLE:贴在局部变量上

代码示例

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

6.自定义注解和使用

(1)定义注解的语法:
@interface 注解名称
例如:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface VIP {
    //抽象的方法 称为属性
	int level();
	String info();
	String[] hobby();
	Gender sex();
}
enum Gender{
	MAN,WOMEN;
}

注解的抽象方法称为属性,如果要给这个属性赋予默认值可以在抽象方法后使用default值
(2)使用注解的语法:
@注解名[(属性名 = 属性值 , 属性名 = 属性值)]
例如:@VIP
注意:给属性赋值时如果只有一个属性要赋值且名称叫value时,可以省略不写
例如:@VIP(“XXX”)

//被贴的程序元素
@VIP(level=3, info = "高级会员", hobby = { "Java" }, sex = Gender.MAN)
public class Person {
}

注意:属性的返回值类型只能是基本类型/Class/注解/String/枚举以及它们各自的数组
不然会出错:
在这里插入图片描述

7.反射注解

(1)需求:
获取类上的注解信息(以上面的Person类为例)
注解可以贴在类、字段、方法上,也就是说注解可以成为类/字段/方法的一部分
如何获取到注解上的信息呢?
Class具有获取类上成员的方法
Method具有获取方法上成员的方法
要获取的注解在谁上面,就去哪个类中去找
(2)思路:
1:获取注解所在类的字节码对象
2:在字节码中获取出注解对象
3:调用抽象方法获取注解属性的值

public class App {
	public static void main(String[] args) {
		//1.获取注解所在类的字节码对象
		Class<Person> clz = Person.class;
		//2.在字节码中获取出注解对象
	    //获取VIP注解
		if(clz.isAnnotationPresent(VIP.class)) {
			VIP vip = clz.getAnnotation(VIP.class);
			//3.调用抽象方法获取注解属性的值
			System.out.println(vip.level());
			System.out.println(vip.info());
			System.out.println(vip.hobby()[1]);
			System.out.println(vip.sex());
		}
		//获取类上的所有注解
		/*Annotation[] annos = clz.getAnnotations();
		for(Annotation anno : annos)
		{
			System.out.println(anno);
		}*/
	}
}

8.模拟JUnit4

(1)模拟JUnit4.x,必须明白它的运行效果:
先执行@Before标注的方法,再运行@Test标注的方法,最后运行@After标注的方法
(2)思路:
1.开发出3个注解
2.把注解贴在测试类中
3.开发第三方程序赋予注解功能(执行的先后顺序)
1)获取要测试的字节码对象
2)获取字节码对象中的所有方法
3)归类区分带有不同注解的方法,分三类
4)迭代所有要执行的方法,在所有要执行的方法前先执行MyBefore注解的方法,在所有要执行的方法后再执行MyAfter注解的方法

MyBefore.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyBefore {
}

MyTest.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
}

MyAfter.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAfter {
}

MyUnitTest.java

public class MyUnitTest {
	//类中的方法就是被贴的程序元素
	@MyBefore
	public void init() {
		System.out.println("MyUnitTest.init()");
	}
	@MyAfter
	public void destory() {
		System.out.println("MyUnitTest.destory()");
	}
	@MyTest
	public void save() {
		System.out.println("MyUnitTest.save()");
	}
	@MyTest
	public void update() {
		System.out.println("MyUnitTest.update()");
	}
}

App.java

public class App {
	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException   {
		// TODO Auto-generated method stub
		//准备三个容器装分类出来的方法
		Method beforeMethod = null;
		Method afterMethod = null;
		List <Method> testMethods = new ArrayList<>();
		//3.开发第三方程序赋予注解功能
		//3.1获取到要测试的字节码对象
		Class<?> clz = MyUnitTest.class;
		//反射创建当前类的对象
		Object obj = clz.newInstance();
		//3.2获取字节码对象中的所有方法
		Method[] ms = clz.getMethods();
		//3.3归类区分带有不同注解的方法
		for(Method m : ms) {
			if(m.isAnnotationPresent(MyBefore.class)) {
				//代码来到这里说明有MyBefore注解
				beforeMethod = m;
			}else if(m.isAnnotationPresent(MyAfter.class)){
				//代码来到这里说明有MyAfter注解
				afterMethod = m;
			}else if(m.isAnnotationPresent(MyTest.class)){
				//代码来到这里说明有MyTest注解
				testMethods.add(m);
			}
		}
		//3.4迭代所有要执行的方法
		for (Method method : testMethods) {
			//在所有要执行的方法前先执行MyBefore注解的方法
			if(beforeMethod != null) {
				beforeMethod.invoke(obj);
			}
			//通过反射调用方法
			method.invoke(obj);
			//在所有要执行的方法后再执行MyAfter注解的方法
			if(afterMethod != null) {
				afterMethod.invoke(obj);
			}
		}
//		System.out.println(beforeMethod);
//		System.out.println(afterMethod);
//		System.out.println(testMethods);
	}
}

运行效果:
在这里插入图片描述
(努力前进中,本文作为学习笔记的复习,若有不足请指出)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值