(Java)注解和反射

注解和反射

一. 注解

注解(Annotation)是Java提供的设置程序中元素的关联信息和元数据(MetaData)的方法,它是一个接口,程序可以通过反射获取指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息。

1.1 元注解

import java.lang.annotation.*;

//测试原注解
@MyAnnotation
public class Test01 {

    public void test() {

    }

}

//定义一个注解
//Target 表示我们的注解可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})

//Retention 表示我们的注解在什么地方有效   runtime>class>source
@Retention(value = RetentionPolicy.RUNTIME)

//Documented 表示是否将我们的注解生成在JAVAdoc中
@Documented

//Inherited  子类可以继承父类的注解
@Inherited
@interface MyAnnotation{

}

1.2 内置注解

import java.util.ArrayList;

//内置注解
public class Test03 {

    @Override  //重写的注解
    public String toString() {
        return "Test03{}";
    }


    @Deprecated  //不推荐使用,存在更好的
    public static void test() {
        System.out.println("Deprecated");
    }

    @SuppressWarnings("all")  //镇压警告
    public void test2() {
        ArrayList arrayList = new ArrayList();
    }

    public static void main(String[] args) {
        test();
    }

}

1.3 自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
public class Demo01 {
    //注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
    @MyAnnotation(name="伊泽瑞尔",schools = "战争学院")
    public void test1(){

    }
    //一个参数的注解可以直接写值
    @MyAnnotation1("魔法猫咪")
    public void test2(){

    }
    //设置生效类型
    @Target({ElementType.TYPE,ElementType.METHOD})
    //设置作用范围
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation{
        String name() default "";
        int age() default 0;
        int id() default -1;
        String[] schools() default {"",""};
    }
    //设置生效类型
    @Target({ElementType.TYPE,ElementType.METHOD})
    //设置作用范围
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation1{
        //一个参数的注解一般用value作为参数
        String value();
    }
}

二. 反射

2.1 什么是反射

Java c c++是静态语言,Java可以通过反射获得类似动态语言的特性。(准动态语言)

动态语言是指在运行阶段可以改变其结构

反射 优点:

  • 可以动态创建对象和编译,灵活

反射 缺点:

  • 对象性能有影响,总是慢于直接执行相同的操作

2.2 Class类

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

2.3 创建Class类的方式

package com.company.demo11.reflection;

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:"+person.name);
        //1.通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());
        //2.forName获得
        Class c2 = Class.forName("com.company.demo11.reflection.Student");
        System.out.println(c2.hashCode());
        //3.通过类名.class获得
        Class c3=Student.class;
        System.out.println(c3.hashCode());
        //4.基本内置类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
        //获取父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
class Person {
    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 = "学生";
    }
}
class Teacher extends Person {
    public Teacher() {
        this.name = "老师";
    }
}

在这里插入图片描述

2.4 所有类型的Class

package com.company.demo11.reflection;

import java.lang.annotation.ElementType;
//所有类型的Class
public class Demo02 {
    public static void main(String[] args) {
        Class c1 = Object.class;  //类
        Class c2 = Comparable.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);

        //只要元素类型与维度一样,就是同一个Class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }

}

在这里插入图片描述

2.5 类加载机制内存分析

JVM的类加载分为5个阶段:加载,验证,准备,解析,初始化。在类初始化完成后就可以使用该类的信息,在一个类不再被需要时可以从JVM中卸载。如图所示:

在这里插入图片描述
代码测试

public class Test {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
        /**
         * 1.加载到内存,会产生一个类对应的Class对象
         * 2.链接,链接结束后m=0  链接:把Java的二进制代码合并到JVM运行状态之中的过程
         * 3.初始化
         * <clinit>(){
         *      System.out.println("A类静态代码块初始化");
         *      m = 100;
         *      m = 200;
         * }
         * m = 200
         */
    }

}
class A{
    static {
        System.out.println("A类的静态代码块初始化");
        m=100;
    }
    static int m = 200;

    public A() {
        System.out.println("A类的无参构造方法初始化");
    }
}

在这里插入图片描述
内存分析如图
在这里插入图片描述

2.6 类的初始化

主要通过执行类构造器的方法为类进行初始化。方法是在编译阶段由编译器自动收集类中静态语句块和变量的赋值操作组成的。JVM规定,只有在父类的方法都执行成功后,子类中的方法才可以被执行。在一个类中既没有静态变量赋值操作也没有静态语句块时,编译器不会为该类生成方法。

在发生以下几种情况时,JVM不会执行类的初始化流程。

  • 常量在编译时会将其常量值存入使用该常量的类的常量池中,该过程不需要调用常量所在的类,因此不会触发该常量类的初始化。
  • 在子类引用父类的静态字段时,不会触发子类的初始化,只会触发父类的初始化。
  • 定义对象数组,不会触发该类的初始化,只是开辟了一块空间。
  • 在使用类名获取Class对象时不会触发类的初始化。
  • 在使用Class.forName加载指定的类时,可以通过initialize参数设置是否需要对类进行初始化。
  • 在使用ClassLoader默认的loadClass方法加载类时不会触发该类的初始化
public class Test01 {
    static {
        System.out.print("main类被加载 ");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //在主动引用时,会初始化
        //1.主动引用,父类没被引用,会先加载他的父类
//       Son son = new Son();//main类被加载 父类被加载 子类被加载
        //2.反射也会产生主动引用
//        Class.forName("com.company.demo11.reflection.Son");//main类被加载 父类被加载 子类被加载
        //不会产生类的引用方法
        //1.子类引用父类的静态字段时,不会触发子类的初始化,只会触发父类的初始化
//        System.out.println(Son.b);//main类被加载 父类被加载 2
        //2.定义对象数组,不会触发该类的初始化,只是开辟了一块空间
//        Son[] sons = new Son[8];//main类被加载
        //3.常量调用不会初始化
//        System.out.println(Son.M);//main类被加载 1
        //4.在使用类名获取Class对象时不会触发类的初始化
//        Class c = Son.class;//main类被加载
        
    }
}
class Father{
    static int b =2;
    static {
        System.out.print("父类被加载 ");
    }
}
class Son extends Father{

    static {
        System.out.print("子类被加载 ");
        m=100;
    }
    static int m = 200;
    static final int M = 1;
}

2.7 类加载器

JVM提供了3种类加载器,分别是启动类加载器,扩展类加载器和应用程序类加载器。如图:
在这里插入图片描述

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取应用程序类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //获取应用程序类加载器的父类加载器扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@4554617c
        //获取扩展类加载器的父类加载器启动类加载器---->无法访问,返回null
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//null
        //测试当前类是那个类加载器加载的
        ClassLoader classLoader = Class.forName("com.company.demo11.reflection.Test02").getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //测试JDK内置的类是谁加载的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);//null
        //如何获得系统类加载器可以加载的路径 如果类不在这些地方则读取不到
        System.out.println(System.getProperty("java.class.path"));
        //双亲委派机制:多重检测,保证安全性
        // 比如自己定义一个java.lang.String类,要去加载,它先看用户类加载器有没有--->扩展类加载器--->根加载器
        //如果有自己写的就无效

        /**
         D:\work\java\jdk1.8\jre\lib\charsets.jar;
         D:\work\java\jdk1.8\jre\lib\deploy.jar;
         D:\work\java\jdk1.8\jre\lib\ext\access-bridge-64.jar;
         :\work\java\jdk1.8\jre\lib\ext\cldrdata.jar;
         D:\work\java\jdk1.8\jre\lib\ext\dnsns.jar;
         D:\work\java\jdk1.8\jre\lib\ext\jaccess.jar;
         D:\work\java\jdk1.8\jre\lib\ext\jfxrt.jar;
         D:\work\java\jdk1.8\jre\lib\ext\localedata.jar;
         D:\work\java\jdk1.8\jre\lib\ext\nashorn.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunec.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunjce_provider.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunmscapi.jar;
         D:\work\java\jdk1.8\jre\lib\ext\sunpkcs11.jar;
         D:\work\java\jdk1.8\jre\lib\ext\zipfs.jar;
         D:\work\java\jdk1.8\jre\lib\javaws.jar;
         D:\work\java\jdk1.8\jre\lib\jce.jar;
         D:\work\java\jdk1.8\jre\lib\jfr.jar;
         D:\work\java\jdk1.8\jre\lib\jfxswt.jar;
         D:\work\java\jdk1.8\jre\lib\jsse.jar;
         D:\work\java\jdk1.8\jre\lib\management-agent.jar;
         D:\work\java\jdk1.8\jre\lib\plugin.jar;
         D:\work\java\jdk1.8\jre\lib\resources.jar;
         D:\work\java\jdk1.8\jre\lib\rt.jar;
         E:\注解和反射\out\production\注解和反射;
         D:\work\idea\lib\idea_rt.jar
         */

    }
}

2.7.1 双亲委派机制

JVM通过双亲委派机制对类进行加载。
双亲委派机制是指一个类在收到类加载请求后不会尝试自己加载这个类,而是把类加载请求向上委派给其父类去完成,其父类在接收到该类加载请求后又会将其委派给自己的父类,以此类推,这样所有的的类加载请求都被向上委派到启动类加载器中。
若父类加载器在接收到类加载请求后发现自己也无法加载该类(通常原因是该类的Class文件在父类的类加载器路径中不存在),则父类会将该信息反馈给子类并向下委派子类加载器加载该类,直到类被成功加载,若找不到该类,则JVM会抛出ClassNotFound异常。

双亲委派类加载机制的类加载流程如图:
在这里插入图片描述

2.9 获取运行时类的完整结构

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

public class Demo03 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.company.demo11.reflection.User");
        //获取类的名字
        System.out.println(c1.getName());//com.company.demo11.reflection.User
        System.out.println(c1.getSimpleName());//User
        System.out.println("--------------------------------------");
        //获得类的属性
        Field[] fields = c1.getFields();//只能找到public属性
        fields = c1.getDeclaredFields();//找到全部属性
        for (Field field : fields) {
            System.out.println(field);
            /*
            private java.lang.String com.company.demo11.reflection.User.name
            private int com.company.demo11.reflection.User.id
            private int com.company.demo11.reflection.User.age
             */
        }
        System.out.println("--------------------------------------");
        //获取指定属性的值
        System.out.println(c1.getDeclaredField("name"));//private java.lang.String com.company.demo11.reflection.User.name
        System.out.println("--------------------------------------");
        //获取类的方法
        Method[] methods = c1.getMethods();//获取本类及其父类的全部public方法
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("--------------------------------------");
        Method[] declaredMethods = c1.getDeclaredMethods();//获取本类的所有方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("--------------------------------------");
        //获得指定的方法
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);//public java.lang.String com.company.demo11.reflection.User.getName()
        System.out.println(setName);//public void com.company.demo11.reflection.User.setName(java.lang.String)
        System.out.println("--------------------------------------");
        //获取构造器
        Constructor[] constructors = c1.getConstructors();//获取public的构造器
        for (Constructor constructor : constructors) {
            System.out.println("public构造器:"+constructor);
            /*
            public com.company.demo11.reflection.User(java.lang.String,int,int)
            public com.company.demo11.reflection.User()
             */
        }

        Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获取全部的构造器
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("全部构造器:"+declaredConstructor);
             /*
            public com.company.demo11.reflection.User(java.lang.String,int,int)
            public com.company.demo11.reflection.User()
             */
        }
        //获取指定的构造器
        Constructor constructor = c1.getConstructor(String.class, int.class,int.class);
        System.out.println("指定构造器"+constructor);//public com.company.demo11.reflection.User(java.lang.String,int,int)

    }
}

2.10 通过反射动态的创建对象

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

public class Demo04 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        //获得Class对象
        Class c1 = Class.forName("com.company.demo11.reflection.User");
        //创建一个对象,此操作必须要有无参构造方法
        User user1 = (User) c1.newInstance();
        System.out.println(user1);//调用无参构造方法 User{name='null', id=0, age=0}
        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User) constructor.newInstance("周星驰", 007, 18);
        System.out.println(user2);//User{name='周星驰', id=7, age=18}
        //通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        //通过反射获得一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke 激活 (对象,方法的值)
        setName.invoke(user3,"伊泽瑞尔");
        System.out.println(user3.getName());//伊泽瑞尔
        //通过反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有属性,关闭安全检测.
        name.setAccessible(true);
        name.set(user4,"魔法猫咪");
        System.out.println(user4.getName());//魔法猫咪

    }
}

在这里插入图片描述

2.11 反射操作泛型

/通过反射获取泛型
public class Test11 {

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

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


    public static void main(String[] args) throws NoSuchMethodException {
        /*
        #java.util.Map<java.lang.String, com.lcy.reflection.User>
        class java.lang.String
        class com.lcy.reflection.User
        #java.util.List<com.lcy.reflection.User>
        class com.lcy.reflection.User
        */
        //加载的方法和参数
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        //获得泛型的参数类型
        Type[] genericParameterTypes = method.getGenericParameterTypes();

        for (Type type :genericParameterTypes) {  //打印泛型
            System.out.println("#"+type);
            if (type instanceof ParameterizedType) {  //想知道里面的参数类型
                //强转获得真实的泛型参数信息
                Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
                for (Type temp :typeArguments) {
                    System.out.println(temp);
                }
            }
        }

        /*
        class java.lang.String
        class com.lcy.reflection.User
         */
        method = Test11.class.getMethod("test02",null);
        //获得返回值类型
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof  ParameterizedType) {
            Type[] types = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type temp :types) {
                System.out.println(temp);
            }
        }

    }



}

2.12 反射操作注解(重点)

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Demo05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //获取Class对象
        Class c1 = Class.forName("com.company.demo11.reflection.Student1");
        //通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//找到了外面的注解@com.company.demo11.reflection.Table(value=db_stu)
        }
        //获取注解value的值
        Table annotation = (Table) c1.getAnnotation(Table.class);
        System.out.println(annotation.value());//db_stu
        //获取类指定的注解
        Field f = c1.getDeclaredField("name");
        FieldZh annotation1 = f.getAnnotation(FieldZh.class);
        System.out.println(annotation1.columName());
        System.out.println(annotation1.type());
        System.out.println(annotation1.length());

    }

}
@Table("db_stu")
class Student1{
    @FieldZh(columName = "id",type = "int",length = 10)
    int id;
    @FieldZh(columName = "name",type = "varchar",length = 10)
    String name;
    @FieldZh(columName = "age",type = "int",length = 3)
    int age;

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

    public Student1() {
    }

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

    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 int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
//创建表的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}
//创建属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldZh{
    String columName();
    String type();
    int length();

}

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章