Thinking in java-37 类加载器与RTTI

来源:互联网 发布:vscode开发python 编辑:程序博客网 时间:2024/05/16 13:42

1. 运行时类型确定RTTI

这里可以找到关于该内容的详细资料。
Intent: Let you find the exact type of an object even if your handle is a base of your object.
目的:RTTI可以在我们用基类引用处理对象时,运行时确认该对象的实际类型。

class Shape{    public void draw(){        System.out.println("Shape drawing.");    }}class Square extends Shape{    @Override    public void draw(){        System.out.println("Square drawing.");    }}class Triangle extends Shape{    @Override    public void draw(){        System.out.println("Triangle drawing.");    }}public class TestRTTI{    public static void main(String[] args){        ArrayList<Shape> list = new ArrayList<>();        list.add(new Square());        list.add(new Triangle());        list.add(new Square());        for(Shape s: list)            s.draw();    }}

2. Class 对象

点击查看详细内容。
每个Java程序中的对象都是某个类的实例instance。在我们装载了给定类的代码后,关于该类本身的呈现方式就是一个class对象。 class对象是Class类的一个实例。
类对象创建有2个条件:
1). 该类的一个实例对象第一次被创建的时候;
2). 当我们显式创建一个类对象的时候。

显式地创建类对象有2种方式:
1). 通过Class类的静态方法:Class.forName():

Class cl = Class.forName("fully_qualified_classname");

2). 通过: Classname.class方式(class literal)

Class cl = Classname.class;

两者比较:
1). 通过类的字面值方式(class literal): 该方式创建Class对象是在编译时被编译器检查的,因而更加安全;缺点是:缺乏灵活性。
2). Class.forName()方式,优点:更加灵活。比如,我们可以在执行时才提供类的字符串名称。缺点:Class.forName(String className) 可能抛出一个checked 异常ClassNotFoundException。

3. Java ClassLoader

我们知道,Java语言的一大优势是它具备跨平台的特性。当我们编译一个java文件时,JVM把它转换为与平台和机器独立的字节码文件,并把这些文件存储为 .class文件。之后,当我们想要使用类的时候,Java ClassLoader会把该class文件装载入内存中。
Java自带有三种类型的built-in ClassLoader:
这里写图片描述
1. Bootstrap ClassLoader: 它装载的是JDK的内部文件,典型的如: rt.jar和其他核心的类文件,比如java.lang.*包中的类文件。
2. Extension ClassLoader: 它装载的是JDK扩展目录中的类文件,java.ext.dirs路径下的文件是由该装载器装载的。
3. System ClassLoader: 装载的是当前classpath中的文件,一般可以通过设置-cp或 -classpath 命令行指定。从开发者角度来讲,是最重要的classloader, 装载的是由classpath环境变量指定的目录和jar文件下的class文件。

package com.fqyuan.test;import java.util.HashMap;import sun.net.spi.nameservice.dns.DNSNameService;public class TestClassLoader {    public static void main(String[] args) {        System.out.println(HashMap.class.getClassLoader());        System.out.println(DNSNameService.class.getClassLoader());        System.out.println(TestClassLoader.class.getClassLoader());    }}//Running result:nullsun.misc.Launcher$ExtClassLoader@75b84c92sun.misc.Launcher$AppClassLoader@2a139a55

4. AppClassLoader 和 SystemClassLoader 关系

Q: Difference between AppClassLoader and SystemClassLoader? 类装载器中的AppClassLoader 和 SystemClassLoader 有什么区别?
A: AppClassLoader and SystemClassLoader are the same.

5. 类加载器遵循的三个原则

  • 指派原则(Delegation principle)
    这里写图片描述
    Bootstrap ClassLoader: 负责从rt.jar中装载标准的JDK类文件,该类装载器是java中所有其他类装载器的父类。
    Extension ClassLoader: 会指派类装载请求给它的父类装载器Bootstrap, 如果指派不成功,则从jre/lib/ext目录中装载类文件。
    System ClassLoader/Application ClassLoader: Application classloader是Extension ClassLoader的子类装载器,实现的是sun.misc.Launcher$AppClassLoader类。

  • 可视性原则(Visibility principle):根据可见性原则,子类类加载器可以看到由父类类加载器所加载的类,但是反之不成立。

  • 唯一性原则(Uniqueness principle):被父类类加载器所加载的类,不应该再次被子类加载器再次加载,即一个类只需要加载一次。

6. RTTI & Reflection两者的差别是什么呢?和Class的关系是什么呢?

Java语言中有一类特殊的类Class, 基于Class类,java实现了RTTI和Reflection机制。我们可以把’Class’类看作’.class’文件的通常形式,这类文件中通常包含了attribute/methods/name/type(属性、方法、姓名、类别,这些都是元数据)。所以可以说,每个’.class’文件都是这个特殊的类’Class’的一个实例对象。

  1. 你所写程序中的每一个类都有一个’Class’对象与之对应,每当我们编辑并编译一个新的类时,可以看作一个Class对象也被创建并存储在以.class命名的文件中。
  2. 所有的类文件被动态装载如JVM中,发生时间是在第一次使用该类时。
  3. 某个.class 文件被装载如内存的时间是,当程序第一次引用该类的静态成员时,注:构造方法也是一个类的静态方法。

Java RTTI

  1. 我们首先得到一个Class类型的引用:
Class c = Class.forName(String strName);c.isIterface(); //determine if it is an interfacec.getSuperCLass(); //Return the class representing the super classes of the entity.
  1. 三种不同形式的 RTTI。
    a). down cast: (Circle)shape[I]
    b). 代表该对象Class可以用于查询运行时信息,比如getSuperClass().
    c). instanceof方法,确定某个实例对象是否为某个类的实例。

Java Reflection

  1. 反射机制其实也是关于运行时类信息的。一种定义式解释:反射是用来代指能检视系统中其他代码(包括其自身)的一种机制。
  2. RTTI和Reflection不同之处在于:RTTI机制,在编译时编译器会会打开并检视class文件,也就是说:我们可以以一种正常的方式调用一个对象的所有方法。而对于反射机制而言,.class 文件在编译时不可用,class文件是被Runtime environment所打开并检查的。
  3. 如下例子:使用反射机制得到运行时方法信息,并在编译时不知道该方法签名的基础上运行该方法。
    假设:java中有一个不知道其具体类型的对象,并且想要调用一个该对象的某个方法。常规的调用方式是不可用的,因为并不知道具体的类类型。
//Traditonal way not applicable.UnknownClass instance = new UnknownClass();instance.unknownMethod();//Use Reflection:Method method = unknownObject.getClass().getMethod("unknownMethod",null);method.invoke(unknownObject, null);