Day32.反射机制(Reflection)、获取Class类实例、类的加载与ClassLoader

目录

类的加载过程

 类的加载 与 类加载器(ClassLoader)的理解

双亲委托机制(双亲委派)

反射机制概述

 java.lang.Class类

反射机制有什么用? 


类的加载过程*

(1)加载:load

         将class文件加载到内存

(2)链接:link

        ① 验证:校验被加载的class文件的合法性,并且不会危害虚拟机的自身安全
                       (所有的java的字节码文件 cafebaby 开头)。

        ② 准备为 类变量(成员变量) 分配内存 (方法区中) 并设置默认值 (0,null,false),为静态常量赋初始值 (常量池中)。

        ③ 解析:把字节码中的符号引用 (类似全类名) 替换为对应的直接地址引用 

(3)初始化:initialize

        1. 静态成员变量 显示赋值语句

        2. 静态代码块内容

  • 注意 哪些操作会导致类初始化? <clinit>()

        1.创建对象 (new)

        2.使用了当前类中的静态方法 (静态方法、静态属性)

        3.反射操作

        注:类的初始化只会执行一次 (双亲委派机制)

  • 哪些操作会导致类初始化滞后?

        滞后:已经使用了类型,但是没有导致类的初始化发生

        1.创建该类型数组

        2.使用了类中的静态常量

        3.子类使用了从父类继承的静态资源,只会导致父类初始化,不会导致子类初始化

 

类的加载 与 类加载器(ClassLoader)的理解

不同的类通过不同的类加载器完成加载

类加载器的分类:

(1)引导类加载器(Bootstrap ClassLoader又称为根类加载器

          它负责加载jre/lib/rt.jar核心类库,它本身不是Java代码实现的(HotSpot VM 中C++实现的),也不是ClassLoader的子类,获取它的对象时往往返回null。

(2)扩展类加载器 (Extension ClassLoader)。 

        它负责加载jre/lib/ext扩展库,它是ClassLoader的子类,Java代码编写。

(3)应用程序类加载器 (Application ClassLoader)

        也称为系统类加载器System Class Loader,它负责加载项目的classpath路径下的类,它是ClassLoader的子类,Java代码编写。(加载自己写的类)

(4)自定义类加载器。(略)

        Tomcat

        //对于自定义类,使用类加载器进行加载
        ClassLoader classLoader = test.class.getClassLoader();
        System.out.println(classLoader);    //sun.misc.Launcher$AppClassLoader@18b4aac2

        //调用系统类加载器的gerParent();获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();  //获取上一层
        System.out.println(classLoader1);   //sun.misc.Launcher$ExtClassLoader@4554617c

        //调用扩展类加载器的getParent();无法获取引导类加载器
        //引导类加载器主要负责加载java的核心类库,无法加载自定义类
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);   //null BootstrapClassLoader用C++编写,无法获取

双亲委托机制(双亲委派)

如果一个类加载器收到了类加载请求,它并不会自己先加载,而是把这个请求委托给父类的加载器去执行,父类加载失败返回子类处理。如果都不能则抛出 ClassNotFoundException。

  • 尝试篡改核心类

自定义类:java.lang.String (没用,找不到main方法)
自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)

为什么需要双亲委派模型?

① 避免类的重复加载
② 保护程序安全,防止核心API被随意篡改

反射机制概述

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

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

4a9b6dcf1226404fb9512ba676d72a51.png

  • 动态语言 vs 静态语言

05fe591243084e29aab365f4481494a5.png

 java.lang.Class类

关于java.lang.Class类的理解

  • 类的加载过程:

    程序在经过javac.exe命令(编译)后,会生成一个或多个字节码文件(.class结尾)。(编译)接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。过程就称为"类的加载"

  • 加载到内存中的类,我们就称为运行时类 ---此运行时类,就作为Class的一个实例。

  • 换句话说,Class的实例就对应着一个运行时类。这个Class对象封装了类在方法区内的数据信息,并且向Java程序员提供了访问方法区内的类数据信息的接口。

  • 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类

获取CLass对象的四种方式:

       //获取Class的实例的方式(前三种方式使用较多)
       //方式一:调用运行时类的属性: .class
        Class clazz1 = Person.class;
        System.out.println(clazz1);

        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        System.out.println(clazz1 == clazz2);   //true

        //方式三:调用Class的静态方法: forName(String classPath(全类名))//包名+类名
        Class clazz3 = Class.forName("com.Reflection.Person");
        System.out.println(clazz3);

        //方式四:使用类的加载器: ClassLoader
        Class personClass = Person.class;
        ClassLoader classLoader = personClass.getClassLoader();
        Class clazz4 = classLoader.loadClass("Temp");
        
        //Class对象是单例的,永远只有一个
        System.out.println(clazz3 == clazz4);   //true

Class实例可以是哪些结构?

Class实例可以是那些结构的说明

//Class实例可以是那些结构的说明:

    Class c1 = Object.class;
    Class c2 = Comparable.class;
    Class c3 = String[].class;
    Class c4 = int[][].class;
    Class c5 = ElementType.class;
    Class c6 = Override.class;
    Class c7 = int.class;
    Class c8 = void.class;
    Class c9 = Class.class;

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

体验动态性:通过反射,灵活创建对象:

    @Test
    public void test1() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Scanner in = new Scanner(System.in);

        System.out.println("请输入要创建对象的全路径名:");
        String message = in.next();

        Class<?> aClass = Class.forName(message);

        Object o = aClass.newInstance();

        System.out.println("o = " + o);
    } + o);

 

Java反射机制研究及应用

Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理
  •  反射相关API

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类当方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造器

...

  •  代码测试: 反射前后对比
//反射之前,对于Person类的操作
    @Test
    public void test1(){

        //1. 创建Person类的对象(Person类的实例化)
        Person p1 = new Person("Tom",12);

        //2. 通过对象,调用其内部的属性和方法
        //调用属性
        p1.age = 10;
        System.out.println(p1);
        //调用方法
        p1.show();

        //对于Person类外部,不可以通过Person类对象调用其内部私有结构
        //比如: name、showNation()、私有构造器

    }


//反射之后,对于Person类的操作
    @Test
    public void test2() throws Exception {
        Class clazz = Person.class;
        //1. 通过反射,创建Person类的对象
        Constructor cons = clazz.getConstructor(String.class,int.class);
        Object obj = cons.newInstance("Tom", 12);
        Person p = (Person) obj;
        System.out.println(p);      //Person{name='Tom', age=12}

        //2. 通过反射,调用对象指定的属性、方法
        //调用属性
        Field age = clazz.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p);      //Person{name='Tom', age=10}
        //调用方法
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);             //你好,我是一个人

        System.out.println("======================");

        //通过反射,可以调用Person类的私有结构的
        //比如: 私有的构造器、方法、属性
        //调用私有构造器
        Constructor con1 = clazz.getDeclaredConstructor(String.class);
        con1.setAccessible(true);
        Person p1 = (Person) con1.newInstance("Jerry");
        System.out.println(p1);         //Person{name='Jerry', age=0}
        //调用私有属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"PePe");
        System.out.println(p1);         //Person{name='PePe', age=0}
        //调用私有方法
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation = (String) showNation.invoke(p1,"中国");//相当于String nation = p1.shouNation("中国");
        System.out.println(nation);
    }

//疑问? 通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?
    //建议: 直接new的方式。
//我们什么时候会使用: 反射的方式。 反射的特征: 动态性
//疑问? 反射的机制与面向对象的封装性是不是矛盾的?如何看待两个技术?
    //不矛盾,

反射机制有什么用? 

通过java语言的反射机制可以操作字节码文件

java.lang.reflect包下

反射机制相关的重要的类有哪些:

代表整个字节码,一个类型 java.lang.class

代表字节码方法字节码 java.lang.reflect.Method

代表字节码文件构造方法 java.lang.reflect.Constructor

代表字节码文件属性字节码 java.lang.reflect.Field

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值