• Java学习——反射


    Java学习——反射

    摘要:本文主要讲述了什么是反射,使用反射有什么好处,以及如何使用反射。

    部分内容来自以下博客:

    https://www.cnblogs.com/tech-bird/p/3525336.html

    https://www.cnblogs.com/jiaoyiping/p/6130355.html

    什么是反射

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

    加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

    Class类

    Class类是什么

    Class是用来描述类的类,封装了当前对象所对应的类的信息

    一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。

    Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等。

    对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。

    Class对象只能由系统建立对象,一个类(而不是一个对象)在JVM中只会有一个Class实例

    通过Class可以完整地得到一个类中的所有被加载的结构。

    Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。

    Java中的哪些类型可以有Class类的实例

    类,接口,枚举,注解,数组,基本数据类型,void。

    常用方法

    static Class<?> forName(String className):返回指定类名的Class对象。

    T newInstance():返回该Class对象对应的类的一个实例。

    Field[] getFields():获取所有的公共属性。

    Field[] getDeclaredFields():获取所有的属性。

    Field getField(String name):获取指定名称的公共属性。

    Field getDeclaredField(String name):获取指定名称的属性。

    Method[] getMethods():获取所有的公共方法。

    Method[] getDeclaredMethods():获取所有的方法。

    Method getMethod(String name, Class<?>... parameterTypes):返回指定名称、参数列表的公共方法。

    Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回指定名称、参数列表的方法。

    Constructor<?>[] getConstructors():获取所有的公共构造方法。

    Constructor<?>[] getDeclaredConstructors():获取所有的构造方法。

    Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定参数列表的公共构造方法。

    Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数列表的构造方法。

    <A extends Annotation> A getAnnotation(Class<A> annotationClass):获取指定类型的公共注解。

    <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):获取指定类型的注解。

    Annotation[] getAnnotations():获取所有的公共注解。

    Annotation[] getDeclaredAnnotations():获取所有的注解。

    Class<?>[] getInterfaces():获取实现的所有接口。

    Type[] getGenericInterfaces():获取带泛型的接口。

    Class<? super T> getSuperclass():获取继承的父类。

    Type getGenericSuperclass():获取带泛型的父类。

    String getName():返回全类名。

    Package getPackage():返回包名。

    int getModifiers():返回修饰符。

    ClassLoader getClassLoader():返回类加载器。

    InputStream getResourceAsStream(String name):返回读取的文件流。

    如何获取Class类的实例

    Class没有公共构造方法。

    Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。

    Java程序经过编译以后,生成关于类的字节码文件。在解释运行时,需要将类的字节码文件加载到内存中(通过JVM的类的加载器实现的加载)。此时加载到内存中的类,我们就称作运行时类,此类本身就充当了java.lang.Class的一个实例。

    反过来讲:一个Class的实例就对应着一个加载到内存中的运行时类。

    获取Class类的实例有四种方式:

    1)直接调用运行时类的.class属性

    1 Class clazz = String.class;

    2)通过调用运行时类的对象的getClass()方法

    1 Class clazz = "www.atguigu.com".getClass();

    3)调用Class的静态方法forName()获取

    1 Class clazz = Class.forName("refl.Person");

    4)使用类的加载器

    1 ClassLoader cl = this.getClass().getClassLoader();
    2 Class clazz = cl.loadClass("refl.Person");

    使用forName()和loadClass()的区别

    相同:

    使用Class的静态方法forName()和使用ClassLoader的成员方法loadClass()都能获取指定类的Class实例。

    也都会进行类的加载,即将.class文件读取到JVM中。

    不同:

    在调用Class的静态方法forName()时,除了会进行类的加载(即将.class文件加载到JVM中)以外,还会进行类的初始化(即初始化静态成员变量和执行静态代码块)。但调用forName()方法并不会执行构造方法创建对象,只要在调用了成员方法newInstance()以后,才会调用空参的构造方法创建对象。

    当调用ClassLoader的成员方法loadClass()时,只进行类的加载(即将.class文件加载到JVM中),不会进行类的初始化(即初始化静态成员变量和执行静态代码块),也不会执行构造方法。

    ClassLoader类

    类的加载过程

    当程序主动使用了某个类时,该类还未被加载到内存中,那么系统会通过下面三个步骤对该类进行初始化。:

    1)类的加载:将类的class文件读入内存,并为之创建一个Class对象。由类加载器完成。

    2)类的连接:将类的二进制数据合并到JRE中。

    3)类的初始化:JVM负责对类进行初始化。

    何时会进行类的初始化

    1)类的主动引用一定会发生类的初始化:

    当虚拟机启动,先初始化main方法所在的类

    使用new关键字创建了一个类的对象。

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

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

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

    2)类的被动引用不会发生类的初始化

    当访问一个静态域时,只有真正声明这个域的类才会被初始化。

    当通过子类引用父类的静态变量,不会导致子类初始化。

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

    引用常量不会触发此类的初始化,常量在链接阶段就存入调用类的常量池中了。

    类加载器的作用

    将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

    类加载器的分类

    引导类加载器:用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。无法直接获取。

    扩展类加载器:负责jre/lib/ext目录下的JAR包或–D java.ext.dirs指定目录下的JAR包装入工作库。

    系统类加载器:负责java –classpath或–D java.class.path所指的目录下的类与JAR包装入工作,最常用的加载器。

    常用方法

    Class<?> loadClass(String name):将Class文件加载到内存时,并不会执行类的初始化。

    static ClassLoader getSystemClassLoader():获取系统类加载器。

    ClassLoader getParent():获取当前类加载器的上级类加载器。

    static InputStream getSystemResourceAsStream(String name):和getResourceAsStream用法类似。

    InputStream getResourceAsStream(String name):默认则是从ClassPath根下获取,path不能以'/'开头,最终是由ClassLoader获取资源。

    如何获取类加载器

    获取系统类加载器(可以获取): ClassLoader.getSystemClassLoader(); 

    获取扩展类加载器(可以获取): ClassLoader.getSystemClassLoader().getParent(); 

    获取引导类加载器(不可以获取): ClassLoader.getSystemClassLoader().getParent().getParent(); 

    测试类加载器

    测试代码如下:

     1 public static void main(String[] args) {
     2     // 获取系统类加载器
     3     ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
     4     System.out.println(appClassLoader);
     5     // 获取扩展类加载器
     6     ClassLoader extClassLoader = appClassLoader.getParent();
     7     System.out.println(extClassLoader);
     8     // 获取引导类加载器
     9     ClassLoader bootClassLoader = extClassLoader.getParent();
    10     System.out.println(bootClassLoader);
    11     // 自定义类使用的是系统类加载器
    12     System.out.println(ClassLoaderTest.class.getClassLoader());
    13     // JDK提供的类使用的是引导类加载器
    14     System.out.println(String.class.getClassLoader());
    15 }

    运行结果如下:

    1 sun.misc.Launcher$AppClassLoader@18b4aac2
    2 sun.misc.Launcher$ExtClassLoader@6d9c638
    3 null
    4 sun.misc.Launcher$AppClassLoader@18b4aac2
    5 null

    测试加载资源

    测试代码如下:

    1 // InputStream is1 = ClassLoader.getSystemClassLoader().getResourceAsStream("test.txt");// null
    2 // InputStream is2 = ClassLoader.getSystemClassLoader().getResourceAsStream("/com/test/test.txt");// null
    3 InputStream is3 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/test/test.txt");
    4 InputStream is4 = ClassLoader.getSystemResourceAsStream("com/test/test.txt");

    使用反射

    获取类的信息

     1 // 根据类名获取类的Class实例
     2 Class<?> cla = Class.forName("test.Student");
     3 // 获取全类名
     4 System.out.println("cla.getName() >>> " + cla.getName());// cla.getName() >>> test.Student
     5 // 获取包名
     6 System.out.println("cla.getPackage() >>> " + cla.getPackage());// cla.getPackage() >>> package test
     7 // 获取访问修饰符
     8 System.out.println("cla.getModifiers() >>> " + cla.getModifiers());// cla.getModifiers() >>> 0
     9 // 获取翻译后的访问修饰符
    10 System.out.println("cla.getModifiers() >>> " + Modifier.toString(cla.getModifiers()));// cla.getModifiers() >>> 
    11 // 获取加载器
    12 System.out.println("cla.getClassLoader() >>> " + cla.getClassLoader());// cla.getClassLoader() >>> sun.misc.Launcher$AppClassLoader@18b4aac2
    13 // 获取父类
    14 Class<?> superclass = cla.getSuperclass();
    15 System.out.println("cla.getSuperclass() >>> " + superclass);// cla.getSuperclass() >>> class test.People
    16 // 获取带泛型的父类
    17 Type genericSuperclass = cla.getGenericSuperclass();
    18 System.out.println("cla.getGenericSuperclass() >>> " + genericSuperclass);
    19 // 获取带泛型的父类的泛型类型
    20 ParameterizedType paramType = (ParameterizedType) genericSuperclass;
    21 Type[] actualTypeArguments = paramType.getActualTypeArguments();
    22 for (Type type : actualTypeArguments) {
    23     System.out.println("genericSuperclass.getActualTypeArguments() >>> " + type.getTypeName());
    24 }
    25 // 获取接口
    26 Class<?>[] interfaces = cla.getInterfaces();
    27 for (Class<?> interfaceClass : interfaces) {
    28     System.out.println("cla.getInterfaces() >>> " + interfaceClass);
    29     // cla.getInterfaces() >>> interface test.School
    30     // cla.getInterfaces() >>> interface test.Home
    31 }
    32 // 获取带泛型的接口
    33 Type[] genericInterfaces = cla.getGenericInterfaces();
    34 for (Type genericInterface : genericInterfaces) {
    35     System.out.println("cla.getGenericInterfaces() >>> " + genericInterface);
    36     // cla.getGenericInterfaces() >>> test.School<J>
    37     // cla.getGenericInterfaces() >>> test.Home<K>
    38 }
    39 // 使用反射创建类的实例
    40 Student student = (Student) cla.newInstance();// >>> Student()
    41 System.out.println("student.toString() >>> " + student);// student.toString() >>> [age:0][grade:0.0][sex:null]

    获取类的属性

     1 // 获取所有的公共属性,包括父类的公共属性
     2 Field[] fields = cla.getFields();
     3 for (Field field : fields) {
     4     System.out.println("cla.getFields() >>> " + field);
     5     // cla.getFields() >>> public int test.Student.age
     6     // cla.getFields() >>> public java.lang.String test.People.name
     7 }
     8 // 获取已声明的所有属性,包括非公共属性,但不包括父类的属性
     9 Field[] declaredFields = cla.getDeclaredFields();
    10 for (Field field : declaredFields) {
    11     System.out.println("cla.getDeclaredFields() >>> " + field);
    12     // cla.getDeclaredFields() >>> public int test.Student.age
    13     // cla.getDeclaredFields() >>> double test.Student.grade
    14     // cla.getDeclaredFields() >>> private java.lang.String test.Student.sex
    15 }
    16 // 获取指定名称的属性
    17 // Field sex = cla.getField("sex");
    18 Field sex = cla.getDeclaredField("sex");
    19 System.out.println("cla.getDeclaredField() >>> " + sex);// cla.getDeclaredField() >>> private java.lang.String test.Student.sex
    20 // 获取访问修饰符
    21 System.out.println("sex.getModifiers() >>> " + Modifier.toString(sex.getModifiers()));// sex.getModifiers() >>> private
    22 // 获取类型
    23 System.out.println("sex.getType() >>> " + sex.getType().getName());// sex.getType() >>> java.lang.String
    24 // 获取名称
    25 System.out.println("sex.getName() >>> " + sex.getName());// sex.getName() >>> sex
    26 // 获取和修改属性值,非公共属性需要执行setAccessible()方法
    27 sex.setAccessible(true);
    28 System.out.println("sex.get() >>> " + sex.get(student));// sex.get() >>> null
    29 sex.set(student, "男");
    30 System.out.println("student.getSex() >>> " + student.getSex());// student.getSex() >>> 男

    获取类的方法

     1 // 获取所有的公共方法,包括父类的公共方法和接口的公共方法
     2 Method[] methods = cla.getMethods();
     3 for (Method method : methods) {
     4     System.out.println("cla.getMethods() >>> " + method);
     5     // cla.getMethods() >>> public int test.Student.getAge()
     6     // cla.getMethods() >>> public void test.Student.setAge(int)
     7     // cla.getMethods() >>> public double test.Student.getGrade()
     8     // cla.getMethods() >>> public void test.Student.setGrade(double)
     9     // cla.getMethods() >>> public java.lang.String test.Student.getSex()
    10     // cla.getMethods() >>> public void test.Student.setSex(java.lang.String)
    11     // cla.getMethods() >>> public java.lang.String test.Student.toString()
    12     // cla.getMethods() >>> public void test.Student.showSchool(java.lang.Object)
    13     // cla.getMethods() >>> public void test.Student.showHome(java.lang.Object)
    14     // cla.getMethods() >>> public java.lang.String test.People.getName()
    15     // cla.getMethods() >>> public void test.People.setName(java.lang.String)
    16     // cla.getMethods() >>> public boolean java.lang.Object.equals(java.lang.Object)
    17     // cla.getMethods() >>> public native int java.lang.Object.hashCode()
    18     // cla.getMethods() >>> public final native java.lang.Class java.lang.Object.getClass()
    19     // cla.getMethods() >>> public final native void java.lang.Object.notify()
    20     // cla.getMethods() >>> public final native void java.lang.Object.notifyAll()
    21     // cla.getMethods() >>> public final void java.lang.Object.wait() throws java.lang.InterruptedException
    22     // cla.getMethods() >>> public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    23     // cla.getMethods() >>> public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    24     // cla.getMethods() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException
    25 }
    26 // 获取已声明的所有方法,包括非公共方法和重写的方法,但不包括父类的方法
    27 Method[] declaredMethods = cla.getDeclaredMethods();
    28 for (Method method : declaredMethods) {
    29     System.out.println("cla.getDeclaredMethods() >>> " + method);
    30     // cla.getDeclaredMethods() >>> public int test.Student.getAge()
    31     // cla.getDeclaredMethods() >>> public void test.Student.setAge(int)
    32     // cla.getDeclaredMethods() >>> public double test.Student.getGrade()
    33     // cla.getDeclaredMethods() >>> public void test.Student.setGrade(double)
    34     // cla.getDeclaredMethods() >>> public java.lang.String test.Student.getSex()
    35     // cla.getDeclaredMethods() >>> public void test.Student.setSex(java.lang.String)
    36     // cla.getDeclaredMethods() >>> public java.lang.String test.Student.toString()
    37     // cla.getDeclaredMethods() >>> public void test.Student.showSchool(java.lang.Object)
    38     // cla.getDeclaredMethods() >>> public void test.Student.showHome(java.lang.Object)
    39     // cla.getDeclaredMethods() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException
    40     // cla.getDeclaredMethods() >>> private void test.Student.doTestStudent()
    41 }
    42 // 获取指定名称和参数类型的方法
    43 // Method testStudent = cla.getMethod("testStudent", String.class);
    44 Method testStudent = cla.getDeclaredMethod("testStudent", String.class);
    45 System.out.println("cla.getDeclaredMethod() >>> " + testStudent);// cla.getDeclaredMethod() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException
    46 // 获取注解
    47 Annotation[] annotations = testStudent.getAnnotations();
    48 for (Annotation annotation : annotations) {
    49     System.out.println("testStudent.getAnnotations() >>> " + annotation);
    50     // testStudent.getAnnotations() >>> @java.lang.Deprecated()
    51 }
    52 // 获取访问修饰符
    53 System.out.println("testStudent.getModifiers() >>> " + Modifier.toString(testStudent.getModifiers()));// testStudent.getModifiers() >>> public
    54 // 获取返回值类型
    55 System.out.println("testStudent.getReturnType() >>> " + testStudent.getReturnType().getName());// testStudent.getReturnType() >>> java.lang.String
    56 // 获取名称
    57 System.out.println("testStudent.getName() >>> " + testStudent.getName());// testStudent.getName() >>> testStudent
    58 // 获取参数
    59 Parameter[] parameters = testStudent.getParameters();
    60 for (Parameter parameter : parameters) {
    61     System.out.println("testStudent.getParameters() >>> " + parameter.getName());
    62     // testStudent.getParameters() >>> name
    63 }
    64 // 获取参数列表
    65 Class[] parameterTypes = testStudent.getParameterTypes();
    66 for (Class parameterType : parameterTypes) {
    67     System.out.println("testStudent.getParameterTypes() >>> " + parameterType.getName());
    68     // testStudent.getParameterTypes() >>> java.lang.String
    69 }
    70 // 获取异常
    71 Class[] exceptionTypes = testStudent.getExceptionTypes();
    72 for (Class exceptionType : exceptionTypes) {
    73     System.out.println("testStudent.getExceptionTypes() >>> " + exceptionType.getName());
    74     // testStudent.getExceptionTypes() >>> java.lang.NullPointerException
    75     // testStudent.getExceptionTypes() >>> java.lang.ArithmeticException
    76 }
    77 // 调用方法,非公共属性需要执行setAccessible()方法
    78 Object testStudentResult = testStudent.invoke(student, "abc");// >>> testStudent()
    79 System.out.println("testStudent.invoke() >>> " + testStudentResult);// testStudent.invoke() >>> abc
    80 Method doTestStudent = cla.getDeclaredMethod("doTestStudent");
    81 // Object doTestStudentResult = doTestStudent.invoke(student);// java.lang.IllegalAccessException
    82 doTestStudent.setAccessible(true);
    83 Object doTestStudentResult = doTestStudent.invoke(student);// >>> doTestStudent()
    84 System.out.println("doTestStudent.invoke() >>> " + doTestStudentResult);// doTestStudent.invoke() >>> null

    获取类的构造方法

     1 // 获取所有的公共构造方法
     2 Constructor<?>[] constructors = cla.getConstructors();
     3 for (Constructor<?> constructor : constructors) {
     4     System.out.println("cla.getConstructors() >>> " + constructor);
     5     // cla.getConstructors() >>> public test.Student()
     6     // cla.getConstructors() >>> public test.Student(java.lang.String)
     7 }
     8 // 获取所有的构造方法,包括非公共构造方法
     9 Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors();
    10 for (Constructor<?> constructor : declaredConstructors) {
    11     System.out.println("cla.getDeclaredConstructors() >>> " + constructor);
    12     // cla.getDeclaredConstructors() >>> public test.Student()
    13     // cla.getDeclaredConstructors() >>> public test.Student(java.lang.String)
    14     // cla.getDeclaredConstructors() >>> private test.Student(int,double,java.lang.String)
    15 }
    16 // 获取参数类型的构造方法
    17 // Constructor<?> peopleConstructor = cla.getConstructor(String.class);
    18 Constructor<?> studentConstructor = cla.getDeclaredConstructor(int.class, double.class, String.class);
    19 System.out.println("cla.getDeclaredConstructor() >>> " + studentConstructor);// cla.getDeclaredConstructor() >>> private test.Student(int,double,java.lang.String)
    20 // 调用方法,非公共属性需要执行setAccessible()方法
    21 // student = (Student) studentConstructor.newInstance(10, 3.5, "女");// java.lang.IllegalAccessException
    22 studentConstructor.setAccessible(true);
    23 student = (Student) studentConstructor.newInstance(10, 3.5, "女");
    24 System.out.println("student.toString() >>> " + student);// student.toString() >>> [age:10][grade:3.5][sex:女]
  • 相关阅读:
    js点击左右滚动+默认自动滚动类
    左右箭头+按钮+左右滑动幻灯
    超酷视频播放器详细地址
    经典123幻灯切换
    js左右滚动幻灯
    js数组操作
    js删除数据的几种方法
    【转】MySQL的各种timeout
    Mysql 分段统计
    【转】linux yum命令详解
  • 原文地址:https://www.cnblogs.com/shamao/p/10984331.html
Copyright © 2020-2023  润新知