欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java面向对象编程6——注解与反射

程序员文章站 2022-06-03 23:52:47
...

Java面向对象编程6——注解与反射

1、注解概述

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制

Java 语言中的类、方法、变量、参数和包等都可以被标注。Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标

可简单理解为标签

2、元注解

元注解顾名思义我们可以理解为注解的注解

@Retention(注解保留时期)

  • @Retention(RetentionPolicy.SOURCE),注解仅存在于源码中,在class字节码文件中不包含

  • @Retention(RetentionPolicy.CLASS), 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得

  • @Retention(RetentionPolicy.RUNTIME), 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

@Target(注解作用的范围)

  • @Target(ElementType.TYPE) 作用接口、类、枚举、注解
  • @Target(ElementType.FIELD) 作用属性字段、枚举的常量
  • @Target(ElementType.METHOD) 作用方法
  • @Target(ElementType.PARAMETER) 作用方法参数
  • @Target(ElementType.CONSTRUCTOR) 作用构造函数
  • @Target(ElementType.LOCAL_VARIABLE)作用局部变量
  • @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
  • @Target(ElementType.PACKAGE) 作用于包
  • @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
  • @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
}

@Documented(文档)

  • Document的英文意思是文档。它的作用是能够将注解中的元素包含到 Javadoc 中去
@Documented
public @interface MyAnnotation {
}

@Inherited(继承)

  • Inherited的英文意思是继承
  • 并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Inherited
public @interface MyAnnotation {
}
@MyAnnotation
public class Test {
}

class Test1 extends Test{
}

注解 MyAnnotation被 @Inherited 修饰,之后类 Test 被 MyAnnotation 注解,类 Test1 继承 Test,那么类 Test1 也拥有 MyAnnotation 这个注解。

@Repeatable(可重复)

  • Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义
//相当于一堆注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
    Value[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
    String id() default "value";
}
public class Test {
    @Value("hello")
    @Value("world")
    public static void test(String var1, String var2) {
        System.out.println(var1 + " " + var2);
    }

    public static void main(String[] args) {
        Method[] methods = Test.class.getMethods();
        for (Method method : methods){
            if (method.getName().equals("test")) {
                Annotation[] annotations = method.getDeclaredAnnotations();
                System.out.println(annotations.length);
                System.out.println(method.getName() + " = " + Arrays.toString(annotations));
            }
        }
    }
}
  • 在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组

  • 注解中属性可以有默认值,默认值需要用 default 关键值指定

  • 如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内

3、注解的提取(反射)

这里我们暂时先使用以下反射,稍后详细讲述反射

@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
    Value[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
    String id() default "id";
    String name() default "name";
}
@Value(id = "1",name = "zhangsan")
@Value(id = "2",name = "lisi")
public class Test {
    public static void main(String[] args) {
        if (Test.class.isAnnotationPresent(Values.class)){
            Values annotation = Test.class.getAnnotation(Values.class);
            Value[] values = annotation.value();
            for (Value value : values) {
                System.out.println("id="+value.id());
                System.out.println("name="+value.name());
            }
        }
    }
}
id=1
name=zhangsan
id=2
name=lisi

属性、方法上的注解照样是可以的,同样还是要假手于反射

@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
    Value[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
    String id() default "id";
    String name() default "name";
}
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@Value(id = "-1",name = "yoya")
@Value(id = "0",name = "ruoye")
public class Test {
    @Value(id = "1",name = "zhangsan")
    public int a;
    @Value(id = "2",name = "lisi")
    public void todo(){

    }

    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(Values.class);
        if ( hasAnnotation ) {
            Values values = Test.class.getAnnotation(Values.class);
            //获取类的注解
            System.out.println("类注解======================");
            System.out.println(values);
        }
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Value value = a.getAnnotation(Value.class);
            if ( value != null ) {
                System.out.println("属性注解======================");
                System.out.println(value.id());
                System.out.println(value.name());
            }
            Method testMethod = Test.class.getDeclaredMethod("todo");
            if (testMethod!=null) {
                // 获取方法中的注解
                System.out.println("方法注解======================");
                Value declaredAnnotation = testMethod.getDeclaredAnnotation(Value.class);
                if ( value != null ){
                    System.out.println(declaredAnnotation.id());
                    System.out.println(declaredAnnotation.name());
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

4、反射(框架设计的灵魂)

4.1、反射是什么

  • 将类的各个组成部分封装为其他对象,这就是反射机制

  • 可以在程序运行过程中,操作这些对象

  • 可以解耦,提高程序的可扩展性

  • 过多的反射会影响性能

4.2、获取Class对象的三种方式

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

4.2.1、通过该类的对象去获取到对应的Class对象

一般不使用这种方式

public class Student {
}
public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        System.out.println(student.getClass());
        System.out.println(student.getClass().getName());
    }
}
class com.ruoye.Student
com.ruoye.Student

4.2.2、通过类名.class静态属性获取

需要导包

public class Student {
}
public class Test {
    public static void main(String[] args) {
        System.out.println(Student.class);
    }
}
class com.ruoye.Student

4.2.3、通过Class类中的静态方法 forName()方法获取

public class Student {
}
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<Student> studentClass = (Class<Student>) Class.forName("com.ruoye.Student");
        System.out.println(studentClass.getName());
    }
}
com.ruoye.Student

4.3、通过Class对象获取到该类的构造器

public class Student {
    private int num;
    private String name;

    public Student() {
    }

    private Student(int num) {
        this.num = num;
    }

    public Student(String name) {
        this.name = name;
    }

    private Student(int num, String name) {
        this.num = num;
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<Student> studentClass = Student.class;
        System.out.println("所有构造函数================");
        Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("公有构造函数=================");
        Constructor<?>[] constructors = studentClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("指定参数的所有构造器==============");
        Constructor<Student> constructor = studentClass.getDeclaredConstructor(int.class);
        System.out.println(constructor);
        System.out.println("指定参数的公开构造器==============");
        Constructor<Student> constructor1 = studentClass.getConstructor(String.class);
        System.out.println(constructor1);
    }
}
所有构造函数================
private com.ruoye.Student(int,java.lang.String)
public com.ruoye.Student(java.lang.String)
private com.ruoye.Student(int)
public com.ruoye.Student()
公有构造函数=================
public com.ruoye.Student(java.lang.String)
public com.ruoye.Student()
指定参数的所有构造器==============
private com.ruoye.Student(int)
指定参数的公开构造器==============
public com.ruoye.Student(java.lang.String)

4.4、通过获取到的构造器创建对象

public class Student {
    private int num;
    private String name;

    public Student() {
    }

    private Student(int num) {
        this.num = num;
    }

    public Student(String name) {
        this.name = name;
    }

    private Student(int num, String name) {
        this.num = num;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "num=" + num +
                ", name='" + name + '\'' +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<Student> studentClass = Student.class;
        Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class, String.class);
        //暴力反射
        declaredConstructor.setAccessible(true);
        Student zhangsan = declaredConstructor.newInstance(1, "zhangsan");
        System.out.println(zhangsan);
    }
}

4.5、通过Class对象获取成员变量

public class Student {
    public boolean flag;
    private int num;
    private String name;

    public Student() {
    }

    private Student(int num) {
        this.num = num;
    }

    public Student(String name) {
        this.name = name;
    }

    private Student(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "num=" + num +
                ", name='" + name + '\'' +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<Student> studentClass = Student.class;
        System.out.println("获取所有字段==============");
        Field[] fields = studentClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        System.out.println("获取公有字段==============");
        Field[] fields1 = studentClass.getFields();
        for (Field field : fields1) {
            System.out.println(field.getName());
        }
        System.out.println("获取特定字段===========");
        Field num = studentClass.getDeclaredField("num");
        System.out.println(num.getName());
        System.out.println("为字段设置值============");
        Student student = studentClass.getDeclaredConstructor(null).newInstance();
        student.setName("ruoye");
        System.out.println(student.toString());
    }
}

4.6、通过Class对象获取到该类的方法

public class Student {
    private void sleep(int a){
        System.out.println("sleep");
    }
    public void study(String name){
        System.out.println("study");
    }
}
public class Test {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<Student> studentClass = Student.class;
        System.out.println("获取所有方法================");
        Method[] declaredMethods = studentClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("获取公有方法(包含父类的方法)================");
        Method[] declaredMethods1 = studentClass.getMethods();
        for (Method declaredMethod : declaredMethods1) {
            System.out.println(declaredMethod);
        }
        System.out.println("获取指定公共方法================");
        Method study = studentClass.getMethod("study", String.class);
        System.out.println(study);
        System.out.println("获取指定方法================");
        Method sleep = studentClass.getDeclaredMethod("sleep", int.class);
        Student student = studentClass.getDeclaredConstructor(null).newInstance();
        sleep.setAccessible(true);
        System.out.println(sleep);
        //唤醒方法
        sleep.invoke(student,1);
    }
}

4.7、通过Method对象调用指定方法

public class Student {
    private void sleep(int a){
        System.out.println("sleep:"+a);
    }
    public void study(String name){
        System.out.println(name+":study");
    }
    public static void run(String name){
        System.out.println(name+":run");
    }
}
public class Test {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<Student> studentClass = Student.class;

        Method sleep = studentClass.getDeclaredMethod("sleep", int.class);
        Student student = studentClass.getDeclaredConstructor(null).newInstance();
        sleep.setAccessible(true);
        System.out.println(sleep);
        sleep.invoke(student,1);

        Method run = studentClass.getDeclaredMethod("run", String.class);
        System.out.println(run);
        //方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null
        //方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null
        //方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化
        run.invoke(null,"zhangsan");
    }
}
相关标签: ReJava java