JavaSE笔记(2.2)Java基础 - 反射

前言

反射算是Java中很强很重要的技术,尤其是在spring中,它有两个重要的特点依赖注入、反转控制,就是运用反射
反射的理解:通过class获得类对象,然后分解映射class对象中的各种元素(构造方法、成员变量等)为一个个对象

反射

动态语言

动态语言(Dynamic programming Language -动态语言或动态编程语言),是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化
静态类型语言:(Statically Typed Language-静态类型语言)静态类型语言与动态类型语言刚好相反,它的数据类型是在编译期间检查的,也就是说在写程序时要声明所有变量的数据类型
JavaScript 就是动态语言,除此之外 Ruby,Python 等也属于动态语言
而 C、C++则不属于动态语言。从反射角度说 JAVA 属于半动态语言
最明显的就是JavaScript里的var数据类型,在运行时确定数据类型

反射机制

在这里插入图片描述反射机制:在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法
这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制

反射的应用场合

Java 程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。

编译时类型由声明对象时实用的类型来决定
运行时类型是由该变量指向的对象类型决定

两种类型不同,就会出现多态,即子类对象赋值给父类引用变量

Person p=new Student(); 其中编译时类型为Person,运行时类型为Student,Person为Student的父类

编译时根本无法预知对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射

反射的API

反射 API 用来生成 JVM 中的类、接口或则对象的信息。

  1. Class 类:反射的核心类,可以获取类的属性,方法等信息。
  2. Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性
    值。
  3. Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或
    者执行方法。
  4. Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。

反射使用步骤(获取 Class 对象、调用对象方法)

  1. 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方
    法。
  2. 调用 Class 类中的方法,既就是反射的使用阶段。
  3. 使用反射 API 来操作这些信息。

获得类对象:
创建一个类Student:

package com.company.entity;

public class Student {
    private int id;
    private String name;
    public String myclass;

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

    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;
    }
}

有3种方法

  • 调用某个对象的 getClass()方法
        Student student=new Student();
        Class studentClass=student.getClass();
  • 调用某个类的 class 属性来获取该类对应的 Class 对象
        Class studentClass=Student.class;
  • 使用 Class 类中的 forName()静态方法(最安全/性能最好)
 		Class studentClass=Class.forName("com.company.entity.Student");
        Method[] method=studentClass.getDeclaredMethods();
        Field[] field=studentClass.getDeclaredFields();
        Constructor[] constructors=studentClass.getDeclaredConstructors();
        System.out.println("Student类的所有方法:");
        for(Method m:method) {
            System.out.println(m.toString());
        }
        System.out.println("Student类的所有成员变量:");
        for (Field field1:field){
            System.out.println(field1.toString());
        }
        System.out.println("Student类的所有构造方法:");
        for (Constructor constructor:constructors){
            System.out.println(constructor.toString());
        }

在这里插入图片描述

到这大概可以看出反射的作用:把Java类的各个成分(成员变量、构造方法、方法等)分解映射成一个个对象

使用反射机制

通过反射绕开泛型
java中集合的泛型是防止错误输入的;只在编译阶段有效,只要绕过编译就无效,在运行时候,是不区分输入什么

		ArrayList list1=new ArrayList();
        ArrayList<String> list2=new ArrayList<String>();

        Class c1=list1.getClass();
        Class c2=list2.getClass();
        //在运行期间泛型无效,所以c1和c2应该是一样的。
        System.out.println(c1==c2);//true
        //由于泛型失效,所以此时list当然可以添加任何对象
        Method m=c2.getMethod("add",Object.class);
        m.invoke(list2,20);//向list2集合中添加一个int 型的值;绕过编译
        System.out.println(list2.toString());

在这里插入图片描述

源码实现

大佬的解释:Java基础之—反射(非常重要)
深入分析Java方法反射的实现原理

Class类:获得类对象

在这里插入图片描述

class类的加载过程:
在这里插入图片描述

最常用的方法forName():

	 public static Class<?> forName(String className)
            throws ClassNotFoundException {
        //这个方法获取调用该方法的类,是为了获得该类的类加载器,来加载驱动
        Class<?> caller = sun.reflect.Reflection.getCallerClass();
        //forName0是一个本地方法,有4个参数
        //className表示要加载器的类的全路径名
        //initialize 布尔值,true表示加载并初始化该类,false表示加载不初始化该类
        //loader:表示使用哪个类加载器,使用调用者的类加载器
        //caller:表示调用forname方法的类
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
    
     public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }

看看注释(谷歌翻译):

	 *返回与该类关联的{@code Class}对象,或者
     *具有给定字符串名称的接口。调用此方法是
     * 相当于:
     *
     * <blockquote>
     * {@code Class.forName(className,true,currentLoader)}
     * </ blockquote>
     *
     *其中{@code currentLoader}表示的定义类加载器
     *当前的类。
     *
     * <p>例如,以下代码片段返回
     *名为类的运行时{@code Class}描述符
     * {@code java.lang.Thread}*
     * <blockquote>
     * {@code Class t = Class.forName(“ java.lang.Thread”)}
     * </ blockquote>
     * <p>
     *调用{@code forName(“ X”)}会导致名为
     * {@code X}要初始化。
     *
     * @param className所需类的完全限定名称。
     * @返回带有该类的{@code Class}对象
     *指定名称。
     * @exception LinkageError如果链接失败
     * @exception ExceptionInInitializerError如果引发了初始化
     *通过此方法失败
     * @Exception ClassNotFoundException如果无法找到该类

forName(String className)方法通过输入的类地址找到我们想要的类,获得类加载器获得驱动
forName(String name, boolean initialize,ClassLoader loader)多了自己设定的initialize(初始化设定)、loader(类加载器)

Method类:获得类对象中的方法
源码:
getDeclaredMethods返回的是所有类对象的方法

 public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }

可以看看getDeclaredMethods方法的注释(谷歌翻译):

*返回一个包含{@code Method}对象的数组,这些对象反映了所有
     *{@code代表的类或接口的声明方法
     * Class}对象,包括publicprotecteddefault(包)
     *访问和私有方法,但不包括继承的方法。
     *
     * <p>如果此{@code Class}对象表示具有多个
     *声明的方法具有相同的名称和参数类型,但不同
     *返回类型,则返回的数组具有一个{@code Method}对象,用于
     *每种这样的方法。
     *
     * <p>如果此{@code Class}对象表示具有类的类型
     *初始化方法{@code <clinit>},然后返回的数组
     * <em>不是</ em>具有相应的{@code方法}对象。
     *
     * <p>如果此{@code Class}对象表示不包含的类或接口
     *声明的方法,则返回的数组长度为0*
     * <p>如果此{@code Class}对象表示数组类型,则为原语
     *类型或void,则返回的数组长度为0*
     * <p>返回数组中的元素未排序,也不在任何元素中
     *特定顺序。
     *
     * @返回代表所有对象的{@code方法}对象的数组
     *此类的声明方法
     * @抛出SecurityException
     *如果存在安全管理者<i> s </ i>,并且任何
     *满足以下条件:
     *
     * <ul>
     *
     * <li>调用者的类加载器与
     *此类的类加载器并调用
     * {@link SecurityManager#checkPermission
     * s.checkPermission}方法,
     * {@code RuntimePermission(“ accessDeclaredMembers”)}
     *拒绝访问此类中已声明的方法
     *
     * <li>调用者的类加载器与或
     *当前类的类加载器的祖先,
     *调用{@link SecurityManager#checkPackageAccess
     * s.checkPackageAccess()}拒绝访问包
     *此类

checkMemberAccess方法是什么?

  private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
        final SecurityManager s = System.getSecurityManager();
        if (s != null) {
            /* Default policy allows access to all {@link Member#PUBLIC} members,
             * as well as access to classes that have the same class loader as the caller.
             * In all other cases, it requires RuntimePermission("accessDeclaredMembers")
             * permission.
             */
            final ClassLoader ccl = ClassLoader.getClassLoader(caller);
            final ClassLoader cl = getClassLoader0();
            if (which != Member.PUBLIC) {
                if (ccl != cl) {
                    s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
                }
            }
            this.checkPackageAccess(ccl, checkProxyInterfaces);
        }
    }

看不懂,看看注释

/*

*检查是否允许客户端访问成员。如果访问被拒绝,引发SecurityException。

*此方法还强制执行包访问。

*默认策略:允许所有客户端使用普通Java访问

*控制。

*/

那copyMethods返回的是什么?

 private static Method[] copyMethods(Method[] arg) {
        Method[] out = new Method[arg.length];
        ReflectionFactory fact = getReflectionFactory();
        for (int i = 0; i < arg.length; i++) {
            out[i] = fact.copyMethod(arg[i]);
        }
        return out;
    }

大致就是检查class类对象的方法?后面返回 copyMethods返回的数组

Field类:获得类对象的成员变量
源码:
getDeclaredFields方法返回的是所有成员变量

    public Field[] getDeclaredFields() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }

跟Method类似,返回copyFields的数组

Constructor类:获得构造方法
源码:
getDeclaredConstructors返回的是所有构造方法

    public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(false));
    }

看看注释(谷歌翻译):

	 *返回{@code Constructor}对象的数组,这些对象反映了所有
     *由此表示的类声明的构造函数
     * {@code Class}对象。这些是公共的,受保护的,默认的
     *(包)访问权限和私有构造函数。数组中的元素
     *返回的不是排序的,并且没有任何特定的顺序。如果
     *类具有默认构造函数,它包含在返回的数组中。
     *如果此{@code Class},则此方法返回长度为0的数组
     *对象表示接口,原始类型,数组类或
     *无效。
     *
     * <p>参见<em> Java语言规范</ em>,第8.2节。
     *
     * @返回代表所有对象的{@code Constructor}对象的数组
     *声明为此类的构造方法
     * @抛出SecurityException
     *如果存在安全管理者<i> s </ i>,并且任何
     *满足以下条件:
     *
     * <ul>
     *
     * <li>调用者的类加载器与
     *此类的类加载器并调用
     * {@link SecurityManager#checkPermission
     * s.checkPermission}方法,
     * {@code RuntimePermission(“ accessDeclaredMembers”)}
     *拒绝访问此类中已声明的构造方法
     *
     * <li>调用者的类加载器与或
     *当前类的类加载器的祖先,
     *调用{@link SecurityManager#checkPackageAccess
     * s.checkPackageAccess()}拒绝访问包
     *此类

Java反射大致就这样,源码一环套一环,多看看源码、注释了解一下就好
在Spring中反射是个关键技术

发布了49 篇原创文章 · 获赞 0 · 访问量 1234

猜你喜欢

转载自blog.csdn.net/key_768/article/details/104219814
今日推荐