java镜子之反射篇

注解和反射是Java中非常重要的知识,一些优秀开源的框架都是大量运用了反射+注解这,比如Spring,MyBatis,反射可以帮助大家去阅读这些框架的源码实现。

注解

Annotation主要是位于java.lang.annotation包下,

  • Annotation

    • 不是程序本身,对程序做出解释

    • 可以被其他程序读取

  • 格式

    • 可以再package,class,method, field

    • 还可以在代码中存在,可以添加一些参数值

内置注解

常见的内置注解有许多,比如下面的三个

  • @Override 重写的注解,当子类重写父类的方法时,我们会加上这个注解

  • @Deprecated 这个注解主要是用来提示程序员当前方法可能存在危险,尽量不要使用

  • @SuppressWarnings 用于镇压警告信息

元注解

负责注解其他注解,这样大家可能觉得元注解并不是很好懂,但是如果给大家一些源码来看的话,大家应该都见过

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
  • @Target 用于描述注解的使用范围(ElementType)

  • @Retention 表示需要在什么级别保存该注释信息,用于描述生命周期(Type, Method)

  • @Ducumented 该注解将被包含在javadoc中

  • @Inherited 说明子类可以继承父类中的该注解

自定义注解

使用@interface自定义注解,我们可以定义一个供自己使用的注解,在注解的的定义中,如果注解只有一个参数的哈,并且这个参数为value的话,我们可以不用写value=…吧啦吧啦,直接参数就可以,default为默认值

  • 如果有填参数,就一定要填或者设置默认值。
/**
 * 定义一个注解
 * @author 大勇
 * @Target 表示我们的注解可以应用在哪些地方
 * @Retention 表示我们的注解在什么地方还有效
 * runtime> class> sources
 * @Documented 表示是否将我们的注解生成Javadoc中
 * @Inherited 子类可以继承父类的注解
 */
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIpublicME)
public @interface MyAnnotation02 {

    /**
     * 注解的参数
     */
    String name() default "";

    int age() default 0;

    int id() default -1;

    String[] schools() default {"北京大学", "清华大学"};
}

反射

反射,我们先来看一下反射的定义:

反射(Reflection)就是在运行状态中,对于任何一个类都可以知道这个类的所有属性和方法和调用任意一对象的任意方法和属性,并且能改变它的属性。

  • 这也是Java可以成为准静态语言的关键所在。

java.lang.reflect

正常方式通过new实例化 --> 获取实例化对象

反射方式实例化对象 --> method: getClass --> 得到类的信息

反射的机制作用

  • 判断任意一个对象所属的类

  • 构造一个类的对象

  • 判断一个类所具有的成员变量和方法

  • 调用一个对象的成员变量和方法

  • 处理注解

  • 生成动态代理

Class类中有getClass(),通过反射后可以获取到这个类的属性、方法和构造器、某个类实现了什么接口。

Class的一些方法

forName(String name);               返回指定类名name的Class对象
newInstance()                       调用缺省构造函数,返回Class对象的一个实例
getName()                           返回该Class对象所表示的实体的名称
getSuperClass()                     返回当前Class对象的父类的Class对象
getInterfaces()                     返回当前Class对象的接口
getClassLoader()                    返回当前Class对象的类加载器
getConstructors()                   返回一个包含某些Constructor对象的数组
getMethod(String name, Class... T)  返回一个Method对象,对象的形参为ParamType
getDeclaredFields()                 返回Field对象的


获取Class对象(一个Class只有一个Class对象)

public class Test03 {

    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:" + person.name);
        // 一、获取对象实例
        Class<? extends Person> c1 = person.getClass();
        // 二、forName
        Class<?> c2 = Class.forName("reflection.Student");
        // 三、类名
        Class c3 = Student.class;
        // 四、基本内置类型的包装类都有一个Type属性
        Class<Integer> c4 = Integer.TYPE;
        // 五、获取父类类型
        Class<?> c5 = c1.getSuperclass();
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4);
        System.out.println(c5);
    }
}

类的初始化

一定发生类初始化

  • 初始化main方法所在的类

  • new一个类的对象

  • 调用类的静态成员(除了final)和静态方法

  • 使用java.lang.reflect包的方法对类进行反射调用

  • 初始化一个类,如果其没有被初始化,则先会初始化它的父类

不会发生类的初始化

  • 当访问一个静态域时,只有真在声明这个域的类才会被初始化。(子类引用父类的静态变量,不会导致子类初始化)

  • 通过数组定义类,不会触发此类的初始化

  • 引用常量不会触发此类的初始化

public class test05 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.m);
    }
}
class A {

    static {
        System.out.println("A类静态代码块");
        m = 300;
    }
    static int m = 100;

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

在这里插入图片描述

**但是这里并不能说明静态代码块大于静态成员变量的初始化顺序,因为其实应该是平级的,如果静态成员变量放在静态代码块上面就会发现这个值是300。

**

类加载器

获取类的信息

setAccessible方法可以关闭安全检查,关闭后可以提升反射的速度

Exception in thread "main" java.lang.IllegalAccessException: Class reflection.test09 can not access a member of class reflection.User with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)

查看类加载器

public class test07 {

    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 classLoader = Class.forName("reflection.test07").getClassLoader();
        System.out.println(classLoader);

        // 获取系统加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));

        // 双亲委派机制

        // Java.lang.String
        /**
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\charsets.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\access-bridge-64.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\cldrdata.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\dnsns.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\jaccess.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\jfxrt.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\localedata.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\nashorn.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\sunec.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\sunjce_provider.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\sunmscapi.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\sunpkcs11.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\ext\zipfs.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\jce.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\jfr.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\jfxswt.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\jsse.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\management-agent.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\resources.jar;
         * C:\Users\dy\.jdks\corretto-1.8.0_322\jre\lib\rt.jar;
         * D:\code\exam\out\production\exam;
         * D:\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar
         */
    }

}

Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。

ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。

AppClassLoader:主要负责加载应用程序的主函数类

双亲委派机制

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 查看当前这个类有没有被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 查找父加载器,交给父加载器
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                    // 找到Bootstrap类加载器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

当需要加载一个class时,先从AppClassLoader中查看是否加载过,如果没有加不需加载了,如果没有,就会拿到父加载器。父类中也会同样检查自己是否加载过,直到Bootstrap classLoader之前,如果加载过,都不会自己去加载。

反射方法的使用

获取类的成员变量、方法、构造器等方法

public class test08 {

    public static void main(String[] args) throws Exception {
        Class<?> c1 = Class.forName("reflection.User");
        // 获取类的名称
        // 包名 + 类名
        System.out.println(c1.getName());
        // 获取类名
        System.out.println(c1.getSimpleName());

        // 获取类的属性
        // 获取public
        Field[] publicField = c1.getFields();
        // 获取所有的属性
        Field[] fields = c1.getDeclaredFields();
        for (Field field: fields) {
            System.out.println(field);
        }
        // 获取指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        // 获取类的方法
        System.out.println("============");
        // 获取本类的所有方法和父类的public
        Method[] methods = c1.getMethods();
        for (Method method: methods) {
            System.out.println(method);
        }

        System.out.println("============");
        Method[] declaredMethods = c1.getDeclaredMethods();
        for (Method method: declaredMethods) {
            System.out.println(method);
        }
        System.out.println("============");
        // 获取指定方法
        Method getName = c1.getMethod("getName", null);
        System.out.println(getName);

        System.out.println("============");
        // 获取指定的构造器
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor: constructors) {
            System.out.println(constructor);
        }
        System.out.println("============");
        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor: constructors) {
            System.out.println(constructor);
        }
    }
}

调用类的方法、成员变量、构造器等

public class test09 {

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

        Class<?> c1 = Class.forName("reflection.User");

        // 构造一个对象  调用一个无参构造器 必须有一个无参数的构造器
        User user = (User) c1.newInstance();
        System.out.println(user);

        // 通过构造器创建对象
        Constructor<?> constructor = c1.getDeclaredConstructor(String.class, Long.class, Integer.class);
        User user1 = (User)constructor.newInstance("大勇", 1L, 18);
        System.out.println(user1);

        //
        User user3 = (User) c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);

        // invoke: 激活的意思
        // 对象,方法的值
        setName.invoke(user3, "大勇");
        System.out.println(user3);

        System.out.println("9999999999999999999999");
        // 通过反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        // 取消安全检查, 不能直接操作私有属性, 关闭属性或者方法
        name.setAccessible(true);
        name.set(user4, "大勇");
        System.out.println(user4);
    }
}

四种泛型

  • ParameterizedType: 表示一种参数化类型,比如Collection

  • GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型

  • TypeVariable: 是各种类型变量的公共父接口

  • WildcardType: 代表一种通配符类型表达式

public class Test11 extends Object{

    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 Exception{
        Method method = Test11.class.getMethod("test01", Map.class, List.class);

        Type[] genericParameterTypes = method.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);
                }
            }
        }
        System.out.println("====================");
        method = Test11.class.getMethod("test02", null);
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }

    }
}

实现的ORM小demo

public class Test12 {

    public static void main(String[] args) throws Exception{
        Class<?> c1 = Class.forName("reflection.Student2");
        Annotation[] annotations = c1.getAnnotations();

        // 反射获取注解
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        // 获取注解的值
        Table annotation = c1.getAnnotation(Table.class);
        System.out.println(annotation.value());

        // 获取类指定的注解
        Field name = c1.getDeclaredField("name");
        FieldToColumn fieldToColumn = name.getAnnotation(FieldToColumn.class);
        System.out.println(fieldToColumn.columnName());
        System.out.println(fieldToColumn.type());
    }


}

@Table("student")
class Student2 {
    @FieldToColumn(columnName = "id", type = "int")
    private int id;

    @FieldToColumn(columnName = "age", type = "int")
    private int age;

    @FieldToColumn(columnName = "name", type = "varchar")
    private String name;

    public Student2() {
    }

    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        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;
    }

    public String getName() {
        return name;
    }

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

/**
 * @author 大勇
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}


/**
 * @author 大勇
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldToColumn {
    String columnName();

    String type();
}();
}

总结

本文介绍了一些关于反射的知识,可能讲的不是很深,更加偏于使用方面,希望可以帮助大家对反射有更加深入的了解…
t age) {
this.age = age;
}

public String getName() {
    return name;
}

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

}

/**

  • @author 大勇
    */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Table {
    String value();
    }

/**

  • @author 大勇
    */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface FieldToColumn {
    String columnName();

    String type();
    }();
    }


## 总结

> 本文介绍了一些关于反射的知识,可能讲的不是很深,更加偏于使用方面,希望可以帮助大家对反射有更加深入的了解......
posted @ 2022-11-07 09:57  Leo哥coding~  阅读(16)  评论(0编辑  收藏  举报  来源