1 Star 0 Fork 165

ElonChung / Java-Review

forked from flatfish / Java-Review 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
java-注解和反射.md 21.77 KB
一键复制 编辑 原始数据 按行查看 历史
icanci 提交于 2020-09-07 23:09 . :fire:更新文件夹

注解和反射

什么是注解

  • Annotation 是从JDK5.0 开始引入的新技术
  • 注解的作用
    • 不是程序本身,可以堆程序做出解释
    • 可以被其他程序所读取
  • 注解的格式
    • 注解是以'@注释名'在代码中存在的,还可以添加一些参数值,例如:@Value(value = "hello")
  • 注解在那里使用
    • 可以附加在package、class、method、field上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

常见的内置注解

  • @Override:定义在java.lang.Override 中,此注解只用来修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
  • @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修饰方法、属性、类,表示已经过期,不建议程序员使用这样的元素,通常是因为它比较危险,并且有更好的选择
  • @SuppressWarnings() :定义在java.lang.@SuppressWarnings 中,用来有抑制编译中的警告信息

元注解

  • 元注解的作用就是负责注解其他注解,Java定义了4个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型做说明
  • 这些类型和它们所支持的类,java.lang.annotation 包中可以找到(@Target @Retention @Document @Inherited)
    • @Target 用于描述注解的使用范围,也就是注解可以使用在什么地方
    • @Retention 表示需要在什么级别保存该注解信息,用于描述注解的声明周期
      • SOURCE < CLASS < RUNTIME
    • @Document 说明该注解将被包含在 javaDoc中
    • @Inherited 说明子类可以 继承 父类的该注解
@Target(value = {ElementType.METHOD, ElementType.TYPE})
// runtime > class > source
@Retention(value = RetentionPolicy.RUNTIME)
// 表示是否将我们的注解生成在JavaDoc中
@Documented
// 表示子类可以继承父类的注解
@Inherited
public @interface MyAnno {

}

自定义注解

  • 使用**@interface**自定义注解的时候,自动继承了 java.lang.annotation.Annotataion 接口
  • 分析
    • @interface用来声明一个注解 格式 public @interface MyAnno {}
    • 其中的每一个方法实际上就是声明了一个配置参数
    • 方法的名称就是参数的名称
    • 返回值的类型就是参数的类型(返回值类型只能是基本类型,Class、String、enum)
    • 可以使用default来声明参数的默认值
    • 如果只有一个参数成员,一般参数名为 value
    • 注解元素必须要有值,我们定义注解元素的时候, 经常使用空字符串,0作为默认值
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno2 {
    // 注解的参数: 参数类型+参数名();
    String name() default "";

    int age() default 0;

    // 如果默认为 -1 ,就是不存在
    int id() default -1;

    String[] schools() default {};
}
public class Test02 {
    // 注解可以显示赋值,也可以使用默认值

    @MyAnno2(name = "ic", age = 12)
    public static void main(String[] args) {

    }
}

如果注解只有一个内容,那么就只要 写 value,就可以直接放值,如果是多共个,value 就必须使用

反射机制

Java反射机制概述

动态语言和静态语言

动态语言

  • 是一类可以在运行时改变其结构的语言
  • Object-c、C#、JavaScript、PHP、Python

静态语言

  • 与动态语言对应的,运行时结构不可以改变的语言,如 Java、C、C++
  • Java不是动态语言,但是Java可以称之为"准动态语言",也就是Java有一定的动态性

反射

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性和方法

Class c = Class.forName("java.lang.String");

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象,获得类的所有的属性方法

  • 正常方式:引入需要的“包类”名称 -> 通过 new 实例化 -> 取得实例化对象
  • 反射方式:实例化对象 -> getClass()方法 -> 得到完整的”包类“名称

优点

  • 可以实现动态创建对象和编译,体现出很大的灵活性

缺点

  • 对性能有影响,使用反射基本上是一种解释操作,我们可以高速JVM,我们希望做什么并且它能满足我们的需求,这类操作总是慢于直接执行相同的操作。

反射主要的API

java.lang.Class 代表一个类
java.lang.reflect.Method 代表类的方法
java.lang.reflect.Field 代表类的成员变量
java.lang.reflect.Constructor 代表类的构造器

理解Class类并获取Class实例

public class Test01 {
    public static void main(String[] args) throws Exception {
        // 通过反射获取Class对象
        Class<?> clazz = Class.forName("cn.icanci.reflection.User");
        Class<?> clazz2 = Class.forName("cn.icanci.reflection.User");
        Class<?> clazz3 = Class.forName("cn.icanci.reflection.User");
        // 一个类在内存中只有一个Class对象
        // 一个类被加载之后,类的整个结构都会被封装在Class对象中
        System.out.println(clazz == clazz2);
        System.out.println(clazz2 == clazz3);
        User user = (User) clazz.newInstance();
        user.setUsername("hello");
        System.out.println(user);
    }
}

class User {
    private String username;
    private int id;

    private int age;
	
    // 省略 getter 、 setter 、 toString ...
}

Class类

在Object类中定义了一下方法,此方法被所有的子类继承

public final native Class<?> getClass();

上面的方法返回值的类型是一个Class类,此类事Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解:可以通过对象反射求出类的名称

对象照镜子可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象,一个Class类型的对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void[])

  • Class 本身就是一个类
  • Class只能由系统创建对象
  • 一个加载的累在JVM中只会有一个Class实例
  • 一个Class实例对象对应的是一个加载到JVM中的一个class文件
  • 每个类的实例都会记得自己由哪个Class实例生成
  • 通过Class类可以完整的得到一个类中的所有的被加载的结构
  • Class类是Reflecttion可以的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类的常用方法

方法名 功能说明
static ClassforName(String name) 返回指定类名name的Class对象
Object newInstance() 调用缺省构造函数,返回一个Class对象的一个实例
getName() 返回此Class对象所表示的实体(类,接口,数组或者void)的名称
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class[] getinterfaces() 返回当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Method getMethod(String name,Class T) 返回一个Method对象,此对象类型为paramType
Field[] getDeclareds() 返回Field对象的有一个数组

获取Class类的实例

类的加载ClassLoader

类的加载 -> 类的连接 - > 类的初始化

public class Test03 {
    static {
        System.out.println("main类被初始化");
    }

    public static void main(String[] args) throws Exception {
        // 会主动引用的案例
        // 创建对象
        // Son son = new Son();
        // 通过反射
        // Class<?> clazz = Class.forName("cn.icanci.reflection.Son");
        // 不回产生类的引用的方法,此时子类没有被初始化
        // System.out.println(Son.b);
        // 初始化数组也不回
        // Son[] sons = new Son[5];
        // 常量不回引起子类和父类的初始化
        System.out.println(Son.M);
    }
}

class Father {
    static int b = 2;

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

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

    static int m = 100;

    static final int M = 1;
}
public class Test05 {
    public static void main(String[] args) throws Exception {
        // 获取系统的类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        // 获取系统的类加载器 -> 拓展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        // 获取拓展类的加载器 父类加载器 -> 根加载器(C/C++)  根加载器 无法打印
        ClassLoader root = parent.getParent();
        System.out.println(root);

        // 测试当前类是哪个加载器加载的
        ClassLoader classLoaderTest05 = Class.forName("cn.icanci.reflection.Test05").getClassLoader();
        System.out.println(classLoaderTest05);
        // 测试JDK内部类是哪个加载器加载的 根加载器 无法打印
        ClassLoader classLoaderObject = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoaderObject);

        // 如何获取系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        /**
         * 哪些类被加载
         *
         * D:\jdk\jdk1.8\jre\lib\charsets.jar;
         * D:\jdk\jdk1.8\jre\lib\deploy.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\access-bridge-64.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\cldrdata.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\dnsns.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\jaccess.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\jfxrt.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\localedata.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\nashorn.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\sunec.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\sunjce_provider.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\sunmscapi.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\sunpkcs11.jar;
         * D:\jdk\jdk1.8\jre\lib\ext\zipfs.jar;
         * D:\jdk\jdk1.8\jre\lib\javaws.jar;
         * D:\jdk\jdk1.8\jre\lib\jce.jar;
         * D:\jdk\jdk1.8\jre\lib\jfr.jar;
         * D:\jdk\jdk1.8\jre\lib\jfxswt.jar;
         * D:\jdk\jdk1.8\jre\lib\jsse.jar;
         * D:\jdk\jdk1.8\jre\lib\management-agent.jar;
         * D:\jdk\jdk1.8\jre\lib\plugin.jar;
         * D:\jdk\jdk1.8\jre\lib\resources.jar;
         * D:\jdk\jdk1.8\jre\lib\rt.jar;
         * E:\IdeaHome\maven\annotation\out\production\annotation;
         * D:\idea2020.1\IntelliJ IDEA 2020.1\lib\idea_rt.jar
         */
    }
}

创建运行时类的对象

获取运行时类的完整结构

  • 通过反射获取运行时类的完整结构
    • Field
    • Method
    • Constructor
    • SuperClass
    • Interface
    • Annotation
  • 实现的全部接口
  • 所继承的父类
  • 全部的构造器
  • 全部的方法
  • 全部的Field
  • 注解
  • .... 其他

获取运行时类的完整结构

public class Test06 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("cn.icanci.reflection.User");
        // 获得类的名字
        String name = clazz.getName();
        System.out.println(name);
        // 获得类的的简单名字
        System.out.println(clazz.getSimpleName());

        // 获得类的属性
        // 只能找到public属性
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        // 能够找到所有的属性
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        // 获得指定属性 只能是 public 的否则会报错
//        System.out.println(clazz.getField("username"));
        System.out.println(clazz.getDeclaredField("username"));
        // 获得类的方法,
        // 获得本类和父类的全部方法
        System.out.println("=========================================");
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("=========================================");
        // 获取本类的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        System.out.println("=========================================");
        // 获取指定的方法
        // 参数就是重载
        Method getUsername = clazz.getMethod("getUsername", null);
        Method setUsername = clazz.getMethod("setUsername", String.class);
        System.out.println(getUsername);
        System.out.println(setUsername);

        // 获得指定的构造器 只能获取共有构造器
        System.out.println("=========================================");
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("=========================================");
        // 能获取共有构造器和私有构造器
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("* " + declaredConstructor);
        }
        System.out.println("=========================================");
        // 获得指定的构造器
        Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(int.class);
        System.out.println(declaredConstructor);
    }
}

有了Class对象,能做什么?

  • 创建类的对象:调用Class对象的newInstance()方法
    • 类必须有一个无参数的构造器
    • 类的构造器的访问权限需要足够
  • 步骤:
    • 通过Class类的 getDeclaredConstruor(Class ... prarmeterTypes) 取得本类的指定形参类型的构造器
    • 向构造器的形参中传递一个对象参数进去,里面包含了构造器中所需的各个参数
    • 通过 Constantor实例化对象
    • 不能直接操作属性 需要设置 安全检测 username.setAccessible(true);
public class Test07 {
    public static void main(String[] args) throws Exception {

        // 获得Class对象
        Class<?> userClazz = Class.forName("cn.icanci.reflection.User");
        // 构建一个对象
        // 调用无参构造器
        // User user = (User) userClazz.newInstance();
        // System.out.println(user);
        // Constructor<?> userCons = userClazz.getConstructor(String.class, int.class, int.class);
        // User user2 = (User) userCons.newInstance("ic", 12, 34);
        // System.out.println(user2);

        // 通过反射调用普通方法
        User user3 = (User) userClazz.newInstance();
        // 通过反射获取以恶个方法
        Method setUsername = userClazz.getDeclaredMethod("setUsername", String.class);
        setUsername.setAccessible(true);
        setUsername.invoke(user3, "ic");
        System.out.println(user3.getUsername());

        // 通过反射操作属性
        User user4 = (User) userClazz.newInstance();
        Field username = userClazz.getDeclaredField("username");
        username.setAccessible(true);
        username.set(user4, "ic2");
        System.out.println(user4.getUsername());
    }
}

调用指定的方法 Object invoke(Object obj,Object ... args)

  • Object 对应原方法的返回值,若方法无返回值,此时返回null
  • 若原方法为静态方法,此时形参 Object obj 可为 null
  • 若原方法列表为空,则 Object [] args 可以为null
  • 若原方法声明为 private,则需要在调用此invoke()方法之前,显示调用对象 的setAccessible(true) 才能访问 private方法和属性 。否则报错

反射操作泛型

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,避免数据的安全性和免去强制类型转换的问题,但是一旦,编译完成,所有和泛型有关的类型会全部擦除
  • 为了通过反射操作这些类型,Java新增了ParameteriedType GenericArrayType TypeVarilable 和 WildcardType 几种类型标识不能被归一待Class类中但是和原始类型齐名的类型
  • ParameteriedType:表示一种参数化类型 比如 :Collection
  • GenericArrayType:表示一种元素类型是参数化类型或者剋下变量的数组类型
  • TypeVarilable:是各种类型变量的公共父接口
  • WildcardType :代表一种通配符类型表达式
public class Test08 {
   public void test01(Map<String, User> map, List<User> list) {
       System.out.println("Test08.test01");
   }

   public Map<String, User> test02() {
       System.out.println("Test08.test02");
       return null;
   }

   public static void main(String[] args) throws Exception {
       Method test01 = Test08.class.getMethod("test01", Map.class, List.class);
       Type[] getGenericParameterTypes = test01.getGenericParameterTypes();
       for (Type getGenericParameterType : getGenericParameterTypes) {
           System.out.println("* " + getGenericParameterType);
           if (getGenericParameterType instanceof ParameterizedType) {
               Type[] actualTypeArguments = ((ParameterizedType) getGenericParameterType).getActualTypeArguments();
               for (Type actualTypeArgument : actualTypeArguments) {
                   System.out.println(actualTypeArgument);
               }
           }
       }
       System.out.println("========================");
       Method test02 = Test08.class.getMethod("test02");
       Type genericReturnType = test02.getGenericReturnType();
       System.out.println(genericReturnType);
       if (genericReturnType instanceof ParameterizedType) {
           Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
           for (Type actualTypeArgument : actualTypeArguments) {
               System.out.println(actualTypeArgument);
           }
       }
   }
}

反射操作注解

  • getAnnotations
  • getAnnotation

练习ORM

public class Test12 {
    public static void main(String[] args) throws Exception {
        // 1. 获取对象
        Class<?> clazz = Class.forName("cn.icanci.reflection.Student2");
        // 2. 通过反射获取注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        // 获得 value的值
        Table table = clazz.getAnnotation(Table.class);
        String value = table.value();
        System.out.println(value);
        // 获得类指定的注解
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        Column nameAnnotation = name.getAnnotation(Column.class);
        System.out.println(nameAnnotation.columnName());
        System.out.println(nameAnnotation.type());
        System.out.println(nameAnnotation.length());

        // 获得全部的注解的值
        System.out.println("=====================");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            Column annotation = declaredField.getAnnotation(Column.class);
            System.out.println(annotation.columnName());
            System.out.println(annotation.type());
            System.out.println(annotation.length());
        }
    }
}

@Table(value = "student2")
class Student2 {
    @Column(columnName = "id", type = "int", length = "11")
    private int id;
    @Column(columnName = "age", type = "int", length = "12")
    private int age;
    @Column(columnName = "name", type = "varchar", length = "13")
    private String name;

    public Student2() {
    }

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

    @Override
    public String toString() {
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", 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;
    }

    public String getName() {
        return name;
    }

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

/**
 * 类命的注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

/**
 * 属性的注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column {
    String columnName();

    String type();

    String length();
}
1
https://gitee.com/elonchung/Java-Review.git
git@gitee.com:elonchung/Java-Review.git
elonchung
Java-Review
Java-Review
master

搜索帮助