Java基础--注解、反射

2019-08-09 00:00:00 java 基础

一、注解(Annotation)

1、什么是注解?

  从JDK5开始,Java增加了Annotation(注解),Annotation是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。

2、Annotation与注释的区别:

  (1)Annotation不是程序本身,可以对程序进行解释,此处可以理解为注释。但是Annotation可以被其他程序(比如编译器)读取,并进行处理。
  (2)注解与注释最大的区别就是注解存在被处理的流程,即注解是会被程序处理的。

3、注解的格式:

  (1)以 “@注释名” 的形式在代码中存在。
  (2)注解可以附加在程序元素( 包、类、构造器、方法、成员变量、参数、局域变量 )上面,为其添加额外的辅助信息,可以通过反射机制访问这些数据。
  (3)Annotation不能运行,其只有成员变量,没有方法。Annotation与public、final等修饰符地位类似,属于程序元素的一部分,但不能作为程序元素使用。

4、常见注解:

  (1)@Override
    定义在java.lang.Override中,此注释只用于修饰方法,表示重写一个父类的方法。

【举例:】
 @Override
 public String toString() {
     return "Hello";
}

  (2)@Deprecated

    定义在java.land.Deprecated中,此注释可用于修饰方法、属性、类,表示该方法、类、属性不推荐使用(废弃)。在方法、类、属性上会有一条删除线(形如toString())。

【举例:】
@Deprecated
public String toString() {
    return "TimerTaskDemo []";
}

  (3)@SuppressWarnings
    定义在java.lang.SuppressWarnings中,用来阻止编译时的警告信息。其使用时需要设置参数。

【参数为:】
deprecation,使用了过时的类或方法的警告。
unchecked,执行了未检查的转换时的异常,比如集合未指定泛型。
fallthrough,当在switch语句发生case穿透时的警告。
path,当类路径、源文件路径不存在时的警告。
serial,可序列化类缺少serialVersionUID时的警告。
finally,任何finally不能完成时的警告。
all,以上所有警告。

【格式:】
@SuppressWarnings("all")
或者
@SuppressWarnings(value = { "serial", "unchecked" })

 

5、元注解:

  (1)元注解的作用就是负责注解其他的注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。存在于java.lang.annotation中。

  (2)元注解分类:
    @Target
    @Retention
    @Documented
    @Inherited

  (3)@Target元注解:
    用于描述注解的使用范围。Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

【格式:】
public @interface Target
{
  ElementType[] value();
}

【参数:ElementType】
CONSTRUCTOR:用于描述构造器
FIELD:用于描述成员变量
LOCAL_VARIABLE:用于描述局部变量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述参数
TYPE:用于描述类、接口(包括注解类型) 或enum声明

【举例:】
@Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE})

  (4)@Retention元注解:
    用于描述注解的声明周期。某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

【格式:】
public @interface Retention
{
  RetentionPolicy value();
}

【参数:RetentionPoicy】
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在运行时有效(即运行时保留,可以通过反射机制读取)

【举例:】
@Retention(RetentionPolicy.SOURCE)

  (5)@Documented元注解:
    用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

  (6)@Inherited元注解:
    @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:
    @Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
    当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

 

6、自定义注解:

  (1)自定义注解时,需要使用@interface用来声明一个注解,其会自动继承java.lang.annotation.Annotation接口。

【格式:】
public @interface 注解名 {定义体}
【或者:】
public @interface 注解名 {
  类型 value() default 默认值;   //这里是参数,不是抽象方法。
}

其中定义体实质是声明了一个配置参数(注:此处不是抽象方法)。
1、方法名指的是参数名。
2、返回值类型指的是参数的类型(只能为基本类型、Class、String、enum、Annotation类型、以及以上所有类型的数组)。
3、可以通过default来声明参数的默认值。
4、如果只有一个参数,那么参数名(方法名)一般为value。
5、只能使用public, default两个权限修饰符。  

  (2)方法:

判断类或者方法是否有注解
    boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

获得注解对象
    <A extends Annotation> A getAnnotation(Class<A> annotationClass)  //获取指定注解
    Annotation[] getAnnotations()  //获取当前元素上的所有注解
【举例:】
package com.test;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldDemo {
    String columnName() default "";

    String type() default "";

    int length() default 10;
}

@Table("student")
class Student {
    @FieldDemo(columnName = "id", length = 10, type = "int")
    private int id;

    @FieldDemo(columnName = "name", length = 20, type = "varchar")
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class AnnotationDemo {
    public static void main(String[] args) {
        try {
            // 获取Student类的信息
            Class classDemo = Class.forName("com.test.Student");
            // Class<Student> classDemo = (Class<Student>)Class.forName("com.test.Student");
            System.out.println(classDemo); // 输出class com.test.Student

            // 获取当前元素上的所有注解,此时获取的是@Table
            Annotation[] annotations = classDemo.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            } // 输出@com.test.Table(value=student)

            // 直接获取指定的某注解
            Table table = (Table) classDemo.getAnnotation(Table.class);
            System.out.println(table.value()); // 输出student

            // 获取类的属性的注解
            Field field = classDemo.getDeclaredField("name");
            // 获取指定注解
            FieldDemo fieldDemo = field.getAnnotation(FieldDemo.class);
            // 输出name varchar 20
            System.out.println(fieldDemo.columnName() + " " + fieldDemo.type() + " " + fieldDemo.length());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

二、反射机制

1、什么是反射?

  JAVA反射机制指的是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;即这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
  JAVA中与反射相关的类,放在java.lang.reflect包中。

2、Class类

  Class对象包含了一个类的完整的结构信息。通过Class对象,可以对类进行操作,即为反射。
  (1)规则:
    Class类拥有泛型作为定义。
    Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
    Class类没有public构造方法。
    Class对象是在加载类时由Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。
    一个类只有一个Class对象。

  (2)内置class实例(class对象):

byte.classshort.classint.classlong.classchar.classfloat.classdouble.classboolean.classvoid.class.
注:
int.class != Integer.classint.class == Integer.Type  。

  (3)对于数组类型class实例:
    每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。

int[] a = new int[100];
int[] b = new int[10];
long[] c = new long[10];
int[][] d = new int[10][2];
System.out.println(a.getClass());//输出class [I
System.out.println(b.getClass());//输出class [I
System.out.println(c.getClass());//输出class [J
System.out.println(d.getClass());//输出class [[I
System.out.println(a.getClass() == b.getClass());//输出true

  (4)获取Class实例的方法:

【方法1:】
根据传入的参数动态装载一个类,并且做类的初始化。
    Class.forName()方法

【方法2:】
获得对象运行时所指的真正对象(多态的场合返回子类的类名)。
    Class.getClass() 方法

【方法3:】
JVM将使用类A的类装载器,将类A装入内存(前提是:类A还没有装入内存),不对类A做类的初始化工作.返回类A的Class的对象。
    A.class属性

  (5)通过Class实例创建对象:

Class.newInstance()方法 。调用默认构造函数,获得一个实例

Class.newInstance方法与new的区别
newInstance: 弱类型。低效率。只能调用无参构造。
new:强类型。相对高效。能调用任何public构造。

  (6)常用方法:

【获得构造器:】
Constructor<T> getDeclaredConstructor(Class<?>...)  获得指定构造方法
Constructor<?>[] getDeclaredConstructors()    获得所有构造方法(声明顺序)
Constructor<T> getConstructor(Class<?>...)    获得权限为public的指定构造方法
Constructor<?>[] getConstructors()    获得权限为public的所有构造方法

【获得普通方法(成员方法):】
Method[] getDeclaredMethods()   获得该类中定义的所有方法(不包含父类继承)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)    根据该类中定义的指定方法(不包含父类继承)
Method[] getMethods()  获得权限为public的所有的方法 (包含父类继承)
Method getMethod(String name, Class<?>... parameterTypes)    获得权限为public的指定的方法 (包含父类继承)

【获得属性(成员变量):】
Field[] getDeclaredFields()   获得该类中定义的所有属性(不包含继承)
Field getDeclaredField(String name)获得该类中定义的指定属性(不包含继承)
Field[] getFields()  获得该类中所有public的属性(包含继承)
Field getField (String name)  获得该类中指定的public属性(包含继承)

【获得内部类:】
Class<?>[] getDeclaredClasses()  获得所有内部类 (不包含继承)
Class<?>[] getClasses()   获得所有权限为public的内部类(包含继承)

【其他:】
Package getPackage()   获得Package对象
String getName()     获得类的全称,即包名+类名
String getSimpleName()  获得类的简称,即类名
Class<? super T> getSuperclass()  获得继承的类
Class<?>[] getInterfaces()  获得实现的接口

  (7)获得构造器后,可以进行的操作

《Java基础--注解、反射》

  (8)获得成员方法后,可以进行的操作

《Java基础--注解、反射》

  (9)获得成员变量后,可以进行的操作

《Java基础--注解、反射》

package com.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class ReflectionDemo {
    public static void main(String[] args) {
        try {
            // 加载Teacher.class对象
            Class<Teacher> teacherClass = (Class<Teacher>) Class.forName("com.test.Teacher");

            // 获取无参构造器,若Teacher类没有无参构造方法,则会报错
            Teacher teacher = teacherClass.newInstance();
            System.out.println(teacher + ", " + teacher.getName() + ", " + teacher.getAge());

            // 获取有参构造器
            Constructor<Teacher> constructor = teacherClass.getDeclaredConstructor(String.class, int.class);
            Teacher teacher2 = constructor.newInstance("tom", 20);
            System.out.println(teacher2 + ", " + teacher2.getName() + ", " + teacher2.getAge());

            // 获取成员方法
            Teacher teacher3 = teacherClass.newInstance();
            Method method = teacherClass.getDeclaredMethod("setAge", int.class);
            method.invoke(teacher3, 30);
            System.out.println(teacher3.getAge());

            Method method2 = teacherClass.getDeclaredMethod("getAge");
            System.out.println(method2.invoke(teacher3));

            // 获取成员变量
            Teacher teacher4 = teacherClass.newInstance();
            Field field = teacherClass.getDeclaredField("age");
            field.setAccessible(true);// 忽略安全检查,可以获取private类型的数据,破坏封装性。
            System.out.println(field.get(teacher4));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【结果:】
com.test.Teacher@2a139a55, null, 0
com.test.Teacher@15db9742, tom, 20
30
30
0

3、反射机制性能问题

  setAccessible,是启用和禁用安全检查的开关,其值为true时,表示禁用Java语言访问的安全性检查,为false时,表示启用安全性检查,将其值设为true,可以提高反射的效率。

 

未完待续。。。。

 

相关文章