注解 反射 泛型

290 阅读4分钟

注解

想像代码具有生命,注解就是对于代码中某些鲜活个体的贴上去的一张标签。简化来讲,注解如同一张标签

注解通过 @interface 关键字进行定义

public @interface TestAnnotation {
}

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

@Retention Retention 的英文意为保留期的意思

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。 @Documented 顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。

@Target Target 是目标的意思,@Target 指定了注解运用的地方。 @Target 有下面的取值

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

@Inherited Inherited 是继承的意思,但是它并不是说注解本身可以继承,是被注解的类可以继承。如Test注解被@Inherited注解,类A被Test注解,类B继承类A,那么类 B 也拥有 Test 这个注解

@Repeatable Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。 什么样的注解会多次应用呢?通常是注解的值可以同时取多个。

举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {
    Person[]  value();
}
@Repeatable(Persons.class)
@interface Person{
    String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}

@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。

@interface Persons {
    Person[]  value();
}

注解的属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    public int id() default -1;
    public String msg() default "Hi";
}

注解的提取

注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

然后通过 getAnnotation() 或getAnnotations()获取注解对象

@TestAnnotation(msg="hello")
public class Test {
    @Check(value="hi")
    int a;
    @Perform
    public void testMethod(){}
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //获取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if ( testMethod != null ) {
                // 获取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

总结

如果注解难于理解,你就把它类同于标签,标签为了解释事物,注解为了解释代码。

  • 注解的基本语法,创建如同接口,但是多了个 @ 符号。
  • 注解的元注解。
  • 注解的属性。
  • 注解主要给编译器及工具类型的软件用的。
  • 注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。

参考blog.csdn.net/qq140451009…

反射

Class对象提供了如下常用方法:

public Constructor getConstructor(Class<?>…parameterTypes)

public Method getMethod(String name,Class<?>… parameterTypes)

public Field getField(String name)

public Constructor getDeclaredConstructor(Class<?>…parameterTypes)

public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

public Field getDeclaredField(String name)

简单说一下得到类的字节码的几种方式

  1. Class.forName("com.cj.test.Person");
  2. 对象.getClass();
  3. 类名.class;
@Test//public String m3(String name,int age)
	public void test3() throws Exception{
		Class clazz = Person.class;
		Person p = (Person) clazz.newInstance();
		Method m = clazz.getMethod("m3", String.class,int.class);
		String returnValue = (String)m.invoke(p, "张三",23);
		System.out.println(returnValue);
	}
	
	@Test//private int age = 18;
	public void test2() throws Exception{
		Class clazz = Person.class;
		Person p = (Person)clazz.newInstance();
		Field f = clazz.getDeclaredField("age");
		f.setAccessible(true);
		int age = (Integer)f.get(p);
		System.out.println(age);
		
		f.set(p, 28);
		age = (Integer)f.get(p);
		System.out.println(age);

泛型

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

泛型类 泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来

泛型方法 我们可能就仅仅在某一个方法上需要使用泛型....外界仅仅是关心该方法,不关心类其他的属性...

泛型接口

静态方法要使用泛型参数,必须定义为泛型方法

//定义泛型方法.. 一定要有泛型定义<T> 才算是泛型方法
    public <T> void show(T t) {
        System.out.println(t);

    }

通配符 ? 设定通配符上限 List<? extends Number> 设定通配符下限 <? super Type>

泛型擦除 只作用于编译期,生成的class文件中将不再带有泛形信息,这个过程称之为“擦除”。