Class.forname和ClassLoader.loadClass的源码分析

最近在研读《深入理解java虚拟机:JVM高继特性与最佳实践》第二版,

今天想起来很久前,写数据库连接,使用Class.forName,当时没有深究,所以便简单的看了下源码,顺便做了以下记录:

以下为简单的数据库连接代码:        
     String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost:3306/db_iot"; String username = "root"; String password = "123456"; Connection conn = null; try { Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,username,password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }
//Class.forName源码:
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    return forName0(className, true,      //这里的true是是否初始化的标志,默认初始化
                    ClassLoader.getClassLoader(Reflection.getCallerClass()));
}

  

  

结合JVM的相关知识:类加载分为 加载,连接(验证,准备(类静态成员设置类型默认值),解析),初始化(类静态成员初始化,不包括构造器的初始化),使用,卸载

  //以下为com.mysql.jdbc.Driver的静态代码块
  static {
        try {
            DriverManager.registerDriver(new Driver());//registerDriver方法会将driver存入到类DriverManger的静态成员中去
        } catch (SQLException var1) {
            throw new RuntimeException("Can\'t register driver!");
        }
    }

  因此,使用Class.forName(driver);的结果是:driver被初始化,driver被加载的DriverManager中,可被查看及使用。

但如果使用classLoader.loadClass("className");则是另一回事了,下面是classLoader.loadClass的源码:

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);//看jdk的注解知道,参数false表示是否解析,即尚未初始化
    }

  所以,若使用classLoader.loadClass,则driver并不会被加载道DriverManager中。

 

 

以下为补充的类加载步骤:

@.加载:

1.通过“类全名”定位到类文件,并获取该类文件的二进制字节流;

2.class文件二进制字节流中的静态数据结构转换为方法区中动态的运行时数据结构;

3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区中该类相关数据的访问入口。

@.连接-验证:

1.验证文件的各式是否正确:是否以魔数0xCAFEBABE开头,紧接着魔数是不是正确的主次版本号,主版本号最小45等等验证;

2.验证元数据是否合法:该类是否有父类(除了java.lang.object外都当有父类),是否继承了final修饰的类等等;

3.验证字节码是否安全:确保被验证类的方法在运行时不会做出危害虚拟机安全的行为,如保证跳转命令不会跳转到方法体意外的字节码命令上;

4.符号应用验证:对当前类意外的信息进行匹配性验证,如通过“类全限定名”是否可找到对应的类,方法是否可访问等等。

需要重视的是:Java类在编译期会被检查验证,但是我们无法保证,class文件不会被篡改,所以,虚拟机的验证期是非常重要的。

@连接-准备:

1.为类成员变量分配内存设置类型默认值(比如int,则设为0),这些内存都将在方法区中进行分配,但需要注意的是,若静态成员变量被final修饰,则此时会直接赋值,而不是取类型默认值。

@连接-解析:

1.,将类的符号应用(单纯的符号字面量,不涉及jvm内存)转为直接应用(直接定位到目标的内存地址);

@初始化:

1.“初始化”是类加载过程的最后一步,在此阶段,类的静态成员将会被赋值(静态方法将会被执行);

 

上面反复提到了方法区,以下为方法区的解释:

方法区:方法区是jvm运行时数据结构中的一部分,为所有线程共享(线程安全)。

方法区中主要存放类信息:

1.类的全限定名,直接超类的全限定名(如果是Object,则没有超类),类的类型(类还是接口),类的访问修饰符(public,abstract,final等),所有的直接接口的全限定名,常量池;

2.常量池中主要存放该类的 字段,方法信息,类变量信息,装在该类的装载器的引用,类型引用等。

------------------------------------------写到后来,就成了背诵了,卡壳了还要翻书---------------------------------------------------------

posted @ 2017-09-06 10:45  it馅儿包子  阅读(1219)  评论(0编辑  收藏  举报