类加载器与反射概述

简介: 类加载器与反射概述

类加载器

概述

当我们要使用一个类的时候,这个类还未被加载到内存中,就会启动类加载器

类加载器是负责加载类的对象。将class文件(硬盘)加载到内存生成Class对象。

类加载器会做3件事情:

​ 1.加载

​ a.会把要使用的类(Person.class文件)加载到内存中

​ b.会为class文件,创建一个对象,这个对象叫class文件对象

​ ⒉连接

​ 3.类的初始化

类的初始化

  1. 创建类的实例
  2. 类的静态变量,或者为静态变量赋值
  3. 类的静态方法
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  5. 初始化某个类的子类
  6. 直接使用java.exe命令来运行某个主类

1594948744373.png

类加载器的组成与继承关系

类加载器的组成

  • BootstrapClassLoader:根类加载器

    ​ 也被称为引导类加载器,负责Java核心类的加载

    ​ 比如System,String等。

  • ExtClassLoader:扩展类加载器

    ​ 负责JRE的扩展目录中jar包的加载。

    ​ 在JDK中JRE的lib目录下ext目录

  • AppClassLoader:系统类加载器

    ​ 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

类加载器的继承关系

  • 类加载器之间的继承关系:

    AppClassLoader extends ExtClassLoader extends BootstrapClassLoader(c语言) extends ClassLoader

  • 所有的类加载器都是 java.lang.ClassLoader 的子类

类加载器01.png

  • 类加载器加载机制:全盘负责委托机制

    ​ 全盘负责:A类如果要使用B类(在内存中不存在),A类加载器必须负责加载B类。

类加载器02.png

委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。

​ 如果加载,将直接使用。

​ 如果没有加载,自己再加载。

  • 采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象。

类加载器的获取

java.lang.ClassLoader:类加载器是负责加载类的对象。负责把类加载到内存中,并为类创建一个class文件对象

获取类加载器的方式:

/* Class类中的方法 */
ClassLoader getClassLoader()     // 返回该类的类加载器

/* ClassLoader类中的成员方法 */
ClassLoader getParent()         // 返回委托的父类加载器

示例

import sun.security.ec.SunEC;

public class Demo01ClassLoader {
    public static void main(String[] args) {
        //直接获取根类加载器
        ClassLoader c = String.class.getClassLoader();
        System.out.println(c);//null

        //直接获取扩展类加载器
        ClassLoader c = SunEC.class.getClassLoader();
        System.out.println(c);//sun.misc.Launcher$ExtClassLoader@7ea987ac
        
        //获取类加载器
        Class clazz = Demo01ClassLoader.class;
        ClassLoader c1 = clazz.getClassLoader();
        System.out.println(c1);//sun.misc.Launcher$AppClassLoader@18b4aac2

        ClassLoader c2 = c1.getParent();
        System.out.println(c2);//sun.misc.Launcher$ExtClassLoader@4554617c

        ClassLoader c3 = c2.getParent();
        System.out.println(c3);//null 根类加载器,不是java语言编写的,获取不到
    }
}

反射

概述

反射前提:类已经进入到内存中生成class文件对象

反射:使用class文件对象,对class文件进行解剖获取class文件中的成员变量、成员方法、构造方法

反射的好处:提高代码的复用性。对于任意的一个类,都可以使用相同的方法进行解剖。框架的底层大量的使用了反射技术 ==> 框架灵魂

获取class文件对象的方式

class文件对象是由类加载器创建的,我们无权创建和销毁,我们可以获取class文件对象,使用class文件对象中的方法 ==> 实现反射

技术反射:使用class文件对象中的方法对内存中的class文件进行解剖

​ 获取class文件中的成员变量,成员方法,构造方法,这就是反射

获取class文件对象有三种方式:class文件对象是由类加载器创建的,具有唯一性,我们通过3种方式获取的都是同一个对象

  • 使用Object类中的方法getClass获取

    Class<?> getClass()     // 返回此 Object 的运行时类。
  • java会为每种数据类型都赋予一个class属性,这个class属性返回的就是class文件对象

    基本数据类型: int.class,double.class,char.class,boolean.class...

    引用数据类型: int[].class,ArrayList.class,String.class,Person.class

  • 可以使用Class类中的静态方法forName获取

    static Class<?> forName(String className)         // 返回与带有给定字符串名的类或接口相关联的 Class 对象
    /* 参数:
            String className:全类名(包名+类名) 可以确定类的唯一性
            com.cormorant.demo01init.Person
       注:数据库 jdbc技术:使用Class类中的forName方法,获取class文件对象,会执行类中的静态代码块
    */

Class类常用方法

String getSimpleName()             // 获取类名
String getName()                 // 获取全类名(包名+类名)
ClassLoader getClassLoader()     // 返回该类的类加载器

    
// 获取类的指定公共构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes)
// 获取类或接口的指定构造方法。(包含公共、保护、默认(包)访问和私有方法)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
/* 参数:
        ...可变参数:调用的方法,参数是可变参数,实际参数可以传递任意个(不传递,1,2,3,4,...)
        Class<?>... parameterTypes:传递构造方法参数列表的class文件对象
            public Person() ==>() 没有参数不传递
            public Person(String name, int age, String sex)==>(String.class,int.class,String.class)
            private Person(String name, int age)==>(String.class,int.class)
   
    注意:类中没有指定的构造方法,会抛出NoSuchMethodException:没有这个方法异常
*/
Constructor<?>[] getConstructors()                // 获取类的所有公共构造方法
Constructor<?>[] getDeclaredConstructors()      // 获取类声明的所有构造方法(包含公共、保护、默认(包)访问和私有方法)


// 获取指定公共成员方法
Method getMethod(String name, Class<?>... parameterTypes)
// 获取指定已声明方法(包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
/*    参数:
        String name:要获取的方法的名称
        Class<?>... parameterTypes:方法参数列表的class文件对象

    注意:获取指定的方法不存在,会抛出NoSuchMethodException:没有这个方法异常
*/
Method[] getMethods()             // 获取本类|父类|接口继承的所有公共的成员方法
Method[] getDeclaredMethods()      // 获取本类声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
    

Class<?>[] getInterfaces()         // 确定此对象所表示的类或接口实现的接口。

使用反射技术实例化对象

java.lang.reflect.Constructor:描述构造方法的类

成员方法:

T newInstance(Object... initargs)     // 使用构造方法实例化对象(创建)
/*    参数:
        Object... initargs:传递创建对象使用的实际参数  new Person("张三", 18, "男");
    返回值:
           T:返回创建好的对象,类型默认使用Object类型   Object obj = new Person("张三", 18, "男");

    私有构造方法是没有权限使用的,要使用会抛出IllegalAccessException:非法访问异常
    解决:可以使用Constructor类的父类AccessibleObject类中的方法解决
        java.lang.reflect.AccessibleObject类
            void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。
                值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                值为 false 则指示反射的对象应该实施 Java 语言访问检查。
    注意:暴力反射不推荐,破坏了类的封装性(私有,不让外界使用)
*/

示例

import java.lang.reflect.Constructor;

/*
    使用反射技术获取构造方法并实例化对象
*/
public class Demo02Constructor {
    public static void main(String[] args) throws Exception {
        /* 使用反射技术获取无参构造方法并实例化对象的简化方式 */
        //1.获取Person类的class文件对象
        Class clazz = Class.forName("com.cormorant.demo03reflect.Person");
        //Object obj = clazz.getConstructor().newInstance();
        //2.直接使用class文件对象中的方法newInstance实例化对象
        Object obj = clazz.newInstance();
        System.out.println(obj);//Person{name='null', age=0, sex='null'}
        //如果想要使用Person特有的方法,需要向下转型
        Person p = (Person)obj;
        p.setName("周华健");
        System.out.println(p.getName());
        
        /* 使用反射技术获取有参构造方法并实例化对象 */
        Constructor con2 = clazz.getConstructor(String.class, int.class, String.class);
        Object obj2 = con2.newInstance("张三", 18, "男");    //此方法就相当于 new Person("张三", 18, "男");
        System.out.println(obj2);    //Person{name='张三', age=18, sex='男'}
        
        
        /* 暴力反射:私有构造方法实例化对象 */
        Constructor con3 = clazz.getDeclaredConstructor(String.class, int.class);
        con3.setAccessible(true);    //取消java语言的访问检查==>暴力反射
        Object obj3 = con3.newInstance("老王", 60);
        System.out.println(obj3);    //Person{name='老王', age=60, sex='null'}
    }
}

使用反射技术运行类的成员方法

java.lang.reflect.Method:描述成员方法的类

String getName()     // 以 String 形式返回此 Method 对象表示的方法名称
    
Object invoke(Object obj, Object... args)  运行成员方法
/*    参数:
        Object obj:传递一个对象,运行哪个类中的方法,就需要传递哪个类的对象
                    运行Person类中的方法,需要传递Person对象(反射快速创建对象的方式获取)
        Object... args:运行方法需要传递的实际参数
    返回值:
        Object:成员方法的返回值
                方法的返回值类型不是void,Object就是方法的返回值
                方法的返回值类型是void,方法没有返回值,返回null
        
    私有方法没有权限运行,会抛出非法访问异常:IllegalAccessException
    解决:暴力反射,使用Method的父类AccessibleObject类中的方法setAccessible取消java的语言访问检查
*/

示例

package com.itheima.demo03reflect;

import java.lang.reflect.Method;

/*
    使用反射技术获取类中的成员方法,并运行获取到成员方法(重点)
 */
public class Demo03Method {
    public static void main(String[] args) throws Exception {
        //1.获取Person类的class文件对象
        Class clazz = Class.forName("com.itheima.demo03reflect.Person");

        //2.使用class文件对象中的方法getMethod|getMethods获取类中的(公共)成员方法Method
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);    
                /* public java.lang.String com.itheima.demo03reflect.Person.getName() */
        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);

        //3.使用成员方法Method类中的方法invoke,运行获取到的方法
        Object obj = clazz.newInstance();    //new Person();
        //public void setName(String name)
        Object v2 = setNameMethod.invoke(obj,"柳岩");//就相当于使用setName方法给成员变量name赋值
        System.out.println("v2:"+v2);    //v2:null setName方法没有返回值
        Object v3 = getNameMethod.invoke(obj);//就相当于使用getName方法获取成员变量name的值
        System.out.println("v3:"+v3);    //v3:柳岩

        
        /*
            使用暴力反射运行私有方法:使用Method的父类AccessibleObject类中的方法setAccessible取消java的语言访问检查
         */
        showMethod.setAccessible(true);
        showMethod.invoke(obj);    //Person类的私有成员方法!
    }
}
相关文章
|
设计模式 Java
Java反射(Class、反射实例化、反射与单例、获取类结构)附带相关面试题
1.了解反射,2.Class类的三种实例化方法,3.反射机制与对象实例化,4.反射与单例设计模式,5.通过反射获取类结构的信息
318 0
|
4月前
|
设计模式 存储 安全
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)
121 0
|
7月前
|
存储 缓存 前端开发
类加载与类加载器概述
类加载与类加载器概述
65 6
|
存储 前端开发 安全
【Java基础增强】类加载器和反射
1.类加载器 1.1类加载器【理解】 作用 负责将.class文件(存储的物理文件)加载在到内存中
43 0
|
存储 安全 前端开发
类加载器及反射简单笔记
类加载器及反射简单笔记
97 0
|
安全 Java
Java基础进阶反射-类加载器和双亲委派机制
Java基础进阶反射-类加载器和双亲委派机制
|
前端开发 编译器 容器
Java反射->什么是反射?->获取方式
Java反射->什么是反射?->获取方式
Java反射->什么是反射?->获取方式
|
存储 缓存 前端开发
类加载器&反射&模块化
类加载器&反射&模块化
112 0
类加载器&反射&模块化