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

2021/9/3 22:06:00

本文主要是介绍Java面向对象编程篇6——注解与反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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");
    }
}


这篇关于Java面向对象编程篇6——注解与反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程