Java-注解与反射

1 简介

为什么要学这个?

未来要学的框架的底层原理就是注解与反射

2 注解

Annotation
注解可以被其他程序(编译器)读取
存在形式
可以在包、类、方法、field上使用注解

@SuppressWarnings(value="unchecked")
public static void method(String param){
	
}

有的注解需要参数,有的注解不需要参数,具体看具体注解的代码

2.1 内置注解

  1. @Override
  2. @Deprecated
  3. 。。。

2.2 元注解

负责注解其他注解

  1. @Target
  2. @Retention :标注该注解在合适有效
  3. @Document
  4. @Inherited

2.2.1 测试Target元注解

Target元注解作用:表示注解可以用于哪些地方

使用@Interface定义一个注解,在其上方使用@Target元注解对MyAnnotation进行注解,随后,定义test方法,使用MyAnnotation进行注解。

public class Main {
    public static void main(String[] args) {

    }

    @MyAnnotation
    public void test(){

    }
}

// 定义一个注解(仿写@Override)
@Target(value = ElementType.METHOD)  // 元注解,注解注解的注解
@interface MyAnnotation{

}

注意:如果@MyAnnotation注解放在类上,则报错!

在元注解的参数中传入数组,则该注解可同时用于多个地方

@MyAnnotation
public class Main {
    public static void main(String[] args) {

    }

    @MyAnnotation
    public void test(){

    }
}

// 定义一个注解(仿写@Override)
@Target(value = {ElementType.METHOD, ElementType.TYPE})  // 元注解,注解注解的注解
@interface MyAnnotation{

}

2.2.2 Retention元注解

@Retention(value = RetentionPolicy.RUNTIME)
表示这个注解在运行时还有效
顺序:source -> class -> runtime
即runtime>class>source

// 定义一个注解(仿写@Override)
@Target(value = {ElementType.METHOD, ElementType.TYPE})  // 元注解,注解注解的注解
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation{

}

2.2.3 Documented元注解

表示是否将注解生成在JAVAdoc中

 @Documented
@interface MyAnnotation{

}

2.2.4 Inherited元注解

表示子类可以继承父类的注解

@Inherited
@interface MyAnnotation{

}

2.3 自定义注解

使用@interface
定义注解参数方式:类型 参数名()
可以使用default设置默认参数,如果默认参数设置为-1表示不存在

如果注解只有一个参数,那么在使用的时候可以不指定参数,直接传值

@MyAnnotation(name = "halo", schools = {"OC", "OUC"})
public class Main {
    public static void main(String[] args) {

    }

    @MyAnnotation(name = "helo", schools = {"OC", "OUC"})
    public void test(){

    }
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    String name() default "";  // 注解的参数,不是方法
    int age() default 10;
    int id() default -1;    // 默认值设置为-1 表示不存在

    String[] schools() default {"QUST", "qust"};
}

3 反射

Reflection
反射机制可以让程序在执行期间获得任何类的内部信息,并且可以操作任意对象的内部属性和方法,即通过对象反向去获得类的信息
在这里插入图片描述

3.1 动态语言与静态语言

  1. 动态语言:在运行时可以改变结构的语言,在运行时代码可以根据某些条件改变自身的结构
  2. 静态语言:运行时结构不可变的语言就是静态语言

3.2 反射 代码演示

代码表示了可以通过类的定义去实例化一个Class对象,这个对象包含了类的全部结构信息,即使用一个对象来表示一个类

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        // 通过反射获取类的Class对象
        Class student = Class.forName("Student");
        System.out.println(student);

        Class student1 = Class.forName("Student");
        Class student2 = Class.forName("Student");
        Class student3 = Class.forName("Student");
        Class student4 = Class.forName("Student");

        // hashcode相同表示是同一个类
        // 类被加载后,其整个结构都会被封装在CLass对象中
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());
        System.out.println(student3.hashCode());
        System.out.println(student4.hashCode());

    }
}


class Student{
    private String name;
    private int id;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

3.3 Object类的getClass方法

getClass()获得的返回值与Class.forName()获取的Class对象是相同的

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Reflection reflection = new Reflection();

        Class aClass = reflection.getClass();

    }
}

3.4 Class类的创建方式

Person、Teacher、Student三个类之间的关系:

class Person{
    public String name;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student() {
        this.name = "Student";
    }
}

class Teacher extends Person{
    public Teacher() {
        this.name = "Teacher";
    }
}

3.4.1 使用类实例对象获取Class类

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Person p1 = new Student();
        System.out.println("This man is:" + p1.name);

        // Class类获得方式1:通过实例对象获得
        Class aClass = p1.getClass();
	}
}

3.4.2 使用Class类的forName方法

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Person p1 = new Student();
        System.out.println("This man is:" + p1.name);

        // Class类获得方式1:通过实例对象获得
        Class aClass = p1.getClass();

        // 获得方式2:使用forname获得
        Class student = Class.forName("Student");

        System.out.println(aClass.hashCode());
        System.out.println(student.hashCode());
	}
}

3.4.3 使用类名.class属性

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Person p1 = new Student();
        System.out.println("This man is:" + p1.name);
        // 方式3:通过类名.class获得
        Class<Student> studentClass = Student.class;
        System.out.println(studentClass.hashCode());
	}
}

3.4.4 使用内置属性包装类TYPE属性

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Person p1 = new Student();
        System.out.println("This man is:" + p1.name);
        // 方式4:基本内置属性的包装类有一个TYPE属性
        Class<Integer> type = Integer.TYPE;
        System.out.println(type.hashCode());
	}
}

3.4.5 通过Class对象获取父类

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Person p1 = new Student();
        System.out.println("This man is:" + p1.name);

        // Class类获得方式1:通过实例对象获得
        Class aClass = p1.getClass();
        // 获得父类类型
        Class superclass = aClass.getSuperclass();
        System.out.println(superclass);
    }
}

3.5 什么类型有class对象?

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Object.class;    // 类
        Class c2 = Runnable.class;  // 接口
        Class c3 = String[].class;  // 数组
        Class c4 = int[][].class;   // 二维数组
        Class c5 = Override.class;    // 注解
        Class c6 = ElementType.class;   // 枚举类型
        Class c7 = Integer.class;   // 基本类型
        Class c8 = void.class;   // void类型
        Class c9 = Class.class;   // Class自身

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
}
output:
class java.lang.Object
interface java.lang.Runnable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class

3.6 Java内存分析类加载过程

public class Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        A a = new A();
        System.out.println(A.c);
    }
}

class A{
    static {
        System.out.println("A类静态代码区");
        c = 300;
    }

    public A() {
        System.out.println("A类无参构造");
    }

    static int c = 100;
}
output:
A类静态代码区
A类无参构造
100

证明了在类加载时,先执行静态代码区,然后执行无参构造,静态变量初始化将静态代码区覆盖掉了,那么背后的加载过程具体是怎样的家呢?

内存加载步骤分析:

  1. 类的代码被加载到方法区(一个特殊作用的堆)
  2. 执行类加载过程,为每一个类创建一个Class对象,保存在堆中
  3. 链接,链接结束后,静态变量被默认初始化为默认值
  4. <clinit>()初始化,将静态代码汇集到一起执行,因为300赋值在前,100语句在后,所以c的值为100

在这里插入图片描述

3.7 类的初始化

3.7.1 主动引用

public class Reflection {

    static {
        System.out.println("Reflection类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        // 主动引用
        Son son = new Son();
    }
}

class Father{
    static {
        System.out.println("父类被加载");
    }

    static int b = 2;
}

class Son extends Father{
    static {
        System.out.println("子类被加载");
        m = 300;
    }

    static int m = 100;
    static final int M = 1;
}
output:
Reflection类被加载
父类被加载
子类被加载
public class Reflection {

    static {
        System.out.println("Reflection类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {

        // 反射也会产生主动引用
        Class son1 = Class.forName("Son");
    }
}

3.7.2 被动引用

public class Reflection {

    static {
        System.out.println("Reflection类被加载");
    }


    public static void main(String[] args) throws ClassNotFoundException {
        // 不会产生类的引用的方法
        // 子类调用父类的静态变量,不会调用类的初始化
        System.out.println(Son.b);
    }
}
Reflection类被加载
父类被加载
2
public class Reflection {

    static {
        System.out.println("Reflection类被加载");
    }


    public static void main(String[] args) throws ClassNotFoundException {
        // 不会产生类的引用的方法
        // 通过数组定义类的引用,不会触发这个类的初始化
        Son[] sons = new Son[10];
    }
}
output:
Reflection类被加载
public class Reflection {

    static {
        System.out.println("Reflection类被加载");
    }
    
    public static void main(String[] args) throws ClassNotFoundException {
        // 不会产生类的引用的方法
        // 调用子类常量池中的常量
        System.out.println(Son.M);
    }
}
output:
Reflection类被加载
1

因为类的常量在链接阶段就被分配内存,所以触发不到类的初始化

在这里插入图片描述

3.8 类加载器

在这里插入图片描述

在这里插入图片描述

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器 --> 扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取扩展类加载器的父类加载器 --> 根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
    }
}
output:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4554617c
null

根加载器获取不到,所以,返回值为null

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器 --> 扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取扩展类加载器的父类加载器 --> 根加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        // 测试当前类是由哪个加载器加载的
        ClassLoader reflection = Class.forName("Reflection").getClassLoader();
        System.out.println(reflection);

        // 测试JDK内置类是由哪个加载器加载的
        ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);
    }
}
output:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4554617c
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

由结果可见,自己定义的类由系统类加载器加载,而JDK内置类由根加载器加载

如何获得系统类加载器可以加载的路径

String property = System.getProperty("java.class.path");

3.9 获取类的运行时结构

获取类的任何信息

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class<?> person = Class.forName("Person");

        // 获取类的名字
        System.out.println(person.getName());   // 带包名
        System.out.println(person.getSimpleName());

        // 获取类的属性,但只能找到public的属性
        for (Field field : person.getFields()) {
            System.out.println(field);
        }
        System.out.println("=============");
        /// 获取类的属性,可以找到全部的属性
        for (Field declaredField : person.getDeclaredFields()) {
            System.out.println(declaredField);
        }

        // 获得指定属性的值
        Field name = person.getDeclaredField("name");
        System.out.println(name);

        System.out.println("=================");
        // 获得类的方法
        // 获得本类和父类的所有方法
        for (Method method : person.getMethods()) {
            System.out.println(method);
        }

        System.out.println("=================");
        // 使用Declared
        // 只能获得本类的所有方法
        for (Method declaredMethod : person.getDeclaredMethods()) {
            System.out.println(declaredMethod);
        }

        System.out.println("=================");
        // 获得指定方法,第二个参数传参
        // 需要参数的原因:避免函数重载引发冲突
        Method getName = person.getMethod("getName", null);
        Method setName = person.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        System.out.println("=================");
        // 获得指定的构造器
        Constructor<?>[] constructor = person.getConstructors();
        for (Constructor<?> constructor1 : constructor) {
            System.out.println(constructor1);
        }
        System.out.println("=================");
        for (Constructor<?> declaredConstructor : person.getDeclaredConstructors()) {
            System.out.println(declaredConstructor);
        }

    }
}

class Person{
    private String name;
    private int age;
    private int id;

    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 int getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }

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

3.10 通过反射动态创建类对象

注意!!使用Class对象newInstance方法新建对象需要该类有一个无参数的构造器,否则程序会报错

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

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Class person = Class.forName("Person");

        // 构造一个对象
        Person p1 = (Person) person.newInstance();
        System.out.println(p1);
    }
}

class Person{
    private String name;
    private int age;
    private int id;

    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 int getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }

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

    public Person() {
    }
}

3.10.1 解决无参构造器问题

使用Class类的getDeclaredConstructor方法获取类的构造器,配置好构造器后使用构造器newInstance进行构造对象

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class person = Class.forName("Person");

        // 构造一个对象
        Constructor p1 = person.getDeclaredConstructor(String.class, int.class, int.class);
        Person p = (Person) p1.newInstance("hahaha", 1, 2);
        System.out.println(p);
    }
}

3.10.2 通过反射调用普通方法

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class person = Class.forName("Person");

        // 通过反射创建了一个对象
        Person p1 = (Person) person.newInstance();
        // 通过反射获取一个方法
        Method setName = person.getDeclaredMethod("setName", String.class);
        setName.invoke(p1, "haha"); // 激活
        System.out.println(p1.getName());
    }
}
output:
haha

3.10.3 通过反射操作属性

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class person = Class.forName("Person");

        // 通过反射创建了一个对象
        Person p1 = (Person) person.newInstance();
        // 通过反射获取一个方法
        Field name = person.getDeclaredField("name");
        name.set(p1, "666");
        System.out.println(p1.getName());
    }
}

上述代码运行时会报错,因为要操作的name属性时private,但反射机制仍然可以解决这个问题,

使用name.setAccessible()方法关闭安全检测

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class person = Class.forName("Person");

        // 通过反射创建了一个对象
        Person p1 = (Person) person.newInstance();
        // 通过反射获取一个方法
        Field name = person.getDeclaredField("name");
        
        // 取消安全检测
        name.setAccessible(true);
        name.set(p1, "666");
        
        System.out.println(p1.getName());
    }
}

3.11 分析setAccessible性能影响

输出类的方法10亿次,三种方法在运行时间上的比较。

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 普通方式调用
        Person person = new Person();
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            person.getName();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方法执行10亿次需要:" + (endTime - startTime));

        // 反射方式调用
        Class aClass = person.getClass();
        Method getName = aClass.getDeclaredMethod("getName", null);

        startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(person, null);
        }

        endTime = System.currentTimeMillis();
        System.out.println("反射方法执行10亿次需要:" + (endTime - startTime));


        // 反射方式调用 但关闭安全检测
        getName = aClass.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(person, null);
        }

        endTime = System.currentTimeMillis();
        System.out.println("反射方法、关闭安全检测执行10亿次需要:" + (endTime - startTime));

    }
}
output:
普通方法执行10亿次需要:4
反射方法执行10亿次需要:2242
反射方法、关闭安全检测执行10亿次需要:1874

3.12 反射操作泛型

public class Reflection {

    public void method01(Map<String, Person> map, List<Person> list){
        System.out.println("method01");
    }

    public Map<String, Person> method02(){
        System.out.println("method02");
        return null;
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        // 参数是泛型
        Method method01 = Reflection.class.getMethod("method01", Map.class, List.class);
        Type[] genericParameterTypes = method01.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        // 返回值是泛型
        Method method02 = Reflection.class.getMethod("method02");
        Type genericReturnType = method02.getGenericReturnType();
        System.out.println(genericReturnType);
        if(genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
}

3.13 反射操作注解

ORM:Object Relation M

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.List;
import java.util.Map;

public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class aClass = Class.forName("Person");

        // 通过反射获得注解
        for (Annotation annotation : aClass.getAnnotations()) {
            System.out.println(annotation);
        }

        // 获得注解的value的值
        Table table = (Table) aClass.getAnnotation(Table.class);
        String value = table.value();
        System.out.println(value);

        // 获得类指定的注解
        Field name = aClass.getDeclaredField("name");
        Field1 annotation = name.getAnnotation(Field1.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.length());
        System.out.println(annotation.type());
    }
}
// 类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}

// 属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field1{
    String columnName();
    String type();
    int length();
}

@Table("db_student")
class Person{
    @Field1(columnName = "db_name", type = "String", length = 10)
    private String name;
    @Field1(columnName = "db_age", type = "int", length = 10)
    private int age;
    @Field1(columnName = "db_id", type = "int", length = 3)
    private int id;

    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 int getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }

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

    public Person() {
    }
}
@Table(value=db_student)
db_student
db_name
10
String
  • 35
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值