Java類加載器原理

ClassLoader介紹

類加載器是負(fù)責(zé)加載類的一個(gè)對(duì)象,ClassLoader是一個(gè)抽象類田柔。最常見(jiàn)的加載策略是根據(jù)的類的全名俐巴,然后找到這個(gè)類的class文件,然后從文件讀取這個(gè)類的數(shù)據(jù)加載到JVM硬爆。每個(gè)類都能通過(guò)getClassLoader方法獲取加載這個(gè)類的類加載器欣舵。

數(shù)組類的類對(duì)象不是由類加載器創(chuàng)建的,而是根據(jù)Java運(yùn)行時(shí)的需要自動(dòng)創(chuàng)建的缀磕。 Class#getClassLoader()返回的數(shù)組類的類加載器與其元素類型的類加載器相同;如果元素類型是基本類型缘圈,則數(shù)組類沒(méi)有類加載器。

應(yīng)用程序可以繼承 ClassLoader來(lái)自定義自己的累加器 袜蚕,以便擴(kuò)展Java虛擬機(jī)動(dòng)態(tài)加載類的方式糟把。

類使用雙親委派模型來(lái)加載類,ClassLoader的每個(gè)實(shí)例都有一個(gè)父類加載器牲剃。當(dāng)一個(gè)類加載加載類時(shí)遣疯,它會(huì)把這個(gè)類加載請(qǐng)求委派給它的父類加載器來(lái)加載。JVM的內(nèi)置類加載器是"bootstrap class loader"”凿傅,它的parent為null缠犀,但可以作為ClassLoader實(shí)例的父級(jí)。

通常狭归,Java虛擬機(jī)以與平臺(tái)相關(guān)的方式從本地文件系統(tǒng)加載類夭坪。例如,在UNIX系統(tǒng)上过椎,虛擬機(jī)從CLASSPATH環(huán)境變量定義的目錄中加載類室梅。 但是,某些類可能不是來(lái)自文件疚宇,它們可能來(lái)自其他來(lái)源亡鼠,例如網(wǎng)絡(luò),或者它們可以由應(yīng)用程序構(gòu)建敷待。方法defineClass(String间涵,byte [],int榜揖,int)將字節(jié)數(shù)組轉(zhuǎn)換為類Class的實(shí)例勾哩,也可以使用 Class#newInstance創(chuàng)建此新定義的類的實(shí)例抗蠢。

例如,應(yīng)用程序可以創(chuàng)建網(wǎng)絡(luò)類加載器以從服務(wù)器下載類文件思劳。示例代碼可能如下所示:

 ClassLoader loader = new NetworkClassLoader(host,port);
 Object main = loader.loadClass("Main", true).newInstance();

NetworkClassLoader必須實(shí)現(xiàn) findClass方法來(lái)加載類,以及自定義加載動(dòng)作通過(guò)loadClassData()方法從網(wǎng)絡(luò)加載類迅矛。一旦下載了構(gòu)成類的字節(jié),它應(yīng)該使用defineClass方法 來(lái)創(chuàng)建一個(gè)類實(shí)例潜叛。示例實(shí)現(xiàn)是:

  class NetworkClassLoader extends ClassLoader {
 *         String host;
 *         int port;
 *
 *         public Class findClass(String name) {
 *             byte[] b = loadClassData(name);
 *             return defineClass(name, b, 0, b.length);
 *         }
 *
 *         private byte[] loadClassData(String name) {
 *             // load the class data from the connection
 *            
 *         }
 *     }

findClass方法參數(shù)name要符合JVM定義的規(guī)范秽褒,以下這些都是合法的類名稱:

 1   "java.lang.String"
 2   "javax.swing.JSpinner$DefaultEditor"
 3  "java.security.KeyStore$Builder$FileBuilder$1"
 4   "java.net.URLClassLoader$3$1"

何時(shí)出發(fā)類加載動(dòng)作?

類加載的觸發(fā)可以分為隱式加載和顯示加載威兜。

隱式加載

隱式加載包括以下幾種情況:

  • 遇到new销斟、getstatic、putstatic椒舵、invokestatic這4條字節(jié)碼指令時(shí)
  • 對(duì)類進(jìn)行反射調(diào)用時(shí)
  • 當(dāng)初始化一個(gè)類時(shí)蚂踊,如果其父類還沒(méi)有初始化,優(yōu)先加載其父類并初始化
  • 虛擬機(jī)啟動(dòng)時(shí)逮栅,需指定一個(gè)包含main函數(shù)的主類悴势,優(yōu)先加載并初始化這個(gè)主類

顯示加載

顯示加載包含以下幾種情況:

  • 通過(guò)ClassLoader的loadClass方法
  • 通過(guò)Class.forName
  • 通過(guò)ClassLoader的findClass方法
被加載的類存放在哪里?

JDK8之前會(huì)加載到內(nèi)存中的方法區(qū)措伐。
從JDK8到現(xiàn)在為止特纤,會(huì)加載到元數(shù)據(jù)區(qū)。

都有哪些ClassLoader侥加?

整個(gè)JVM平臺(tái)提供三類ClassLoader捧存。

Bootstrap ClassLoader

加載JVM自身工作需要的類,它由JVM自己實(shí)現(xiàn)担败。它會(huì)加載JAVA_HOME/jre/lib下的文件

ExtClassLoader

它是JVM的一部分昔穴,由sun.misc.Launcher.ExtClassLoader實(shí)現(xiàn),他會(huì)加載JAVA_HOME/jre/lib/ext目錄中的文件(或由System.getProperty("java.ext.dirs")所指定的文件)提前。

AppClassLoader

應(yīng)用類加載器吗货,我們工作中接觸最多的也是這個(gè)類加載器,它由sun.misc.Launcher.AppClassLoader實(shí)現(xiàn)狈网。它加載由System.getProperty("java.class.path")指定目錄下的文件宙搬,也就是我們通常說(shuō)的classpath路徑。

雙親委派模型

雙親委派模型原理

從JDK1.2之后拓哺,類加載器引入了雙親委派模型勇垛,其模型圖如下:



其中,兩個(gè)用戶自定義類加載器的父加載器是AppClassLoader士鸥,AppClassLoader的父加載器是ExtClassLoader闲孤,ExtClassLoader是沒(méi)有父類加載器的,在代碼中烤礁,ExtClassLoader的父類加載器為null讼积。BootstrapClassLoader也并沒(méi)有子類肥照,因?yàn)樗耆蒍VM實(shí)現(xiàn)。

雙親委派模型的原理是:當(dāng)一個(gè)類加載器接收到類加載請(qǐng)求時(shí)勤众,首先會(huì)請(qǐng)求其父類加載器加載建峭,每一層都是如此,當(dāng)父類加載器無(wú)法找到這個(gè)類時(shí)(根據(jù)類的全限定名稱)决摧,子類加載器才會(huì)嘗試自己去加載。

此模型解決的問(wèn)題

為什么要使用雙親委派模型呢凑兰?它可以解決什么問(wèn)題呢掌桩?
親委派模型是JDK1.2之后引入的。根據(jù)雙親委派模型原理姑食,可以試想波岛,沒(méi)有雙親委派模型時(shí),如果用戶自己寫(xiě)了一個(gè)全限定名為java.lang.Object的類音半,并用自己的類加載器去加載则拷,同時(shí)BootstrapClassLoader加載了rt.jar包中的JDK本身的java.lang.Object,這樣內(nèi)存中就存在兩份Object類了曹鸠,此時(shí)就會(huì)出現(xiàn)很多問(wèn)題煌茬,例如根據(jù)全限定名無(wú)法定位到具體的類。
有了雙親委派模型后彻桃,所有的類加載操作都會(huì)優(yōu)先委派給父類加載器坛善,這樣一來(lái),即使用戶自定義了一個(gè)java.lang.Object邻眷,但由于BootstrapClassLoader已經(jīng)檢測(cè)到自己加載了這個(gè)類眠屎,用戶自定義的類加載器就不會(huì)再重復(fù)加載了。
所以肆饶,雙親委派模型能夠保證類在內(nèi)存中的唯一性改衩。

雙親委派模型實(shí)現(xiàn)原理

下面從源碼的角度看一下雙親委派模型的實(shí)現(xiàn)。
JVM在加載一個(gè)class時(shí)會(huì)先調(diào)用classloader的loadClassInternal方法驯镊,該方法源碼如下

// This method is invoked by the virtual machine to load a class.
private Class<?> loadClassInternal(String name)
    throws ClassNotFoundException
{
    // For backward compatibility, explicitly lock on 'this' when
    // the current class loader is not parallel capable.
    if (parallelLockMap == null) {
        synchronized (this) {
             return loadClass(name);
        }
    } else {
        return loadClass(name);
    }
}

該方法里面做的事兒就是調(diào)用了loadClass方法葫督,loadClass方法的實(shí)現(xiàn)如下

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 先查看這個(gè)類是否已經(jīng)被自己加載了
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 如果有父類加載器,先委派給父類加載器來(lái)加載
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 如果父類加載器為null阿宅,說(shuō)明ExtClassLoader也沒(méi)有找到目標(biāo)類候衍,則調(diào)用BootstrapClassLoader來(lái)查找
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            // 如果都沒(méi)有找到,調(diào)用findClass方法洒放,嘗試自己加載這個(gè)類
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

源碼中已經(jīng)給出了幾個(gè)關(guān)鍵步驟的說(shuō)明蛉鹿。
代碼中調(diào)用BootstrapClassLoader的地方實(shí)際是調(diào)用的native方法。
由此可見(jiàn)往湿,雙親委派模型實(shí)現(xiàn)的核心就是這個(gè)loadClass方法妖异。

實(shí)現(xiàn)自己的類加載器

類加載器的作用

類加載器有啥作用呢惋戏?我們?cè)倩氐缴厦娴脑创a。
從上文我們知道JVM通過(guò)loadClass方法來(lái)查找類他膳,所以响逢,他的第一個(gè)作用也是最重要的:在指定的路徑下查找class文件(各個(gè)類加載器的掃描路徑在上文已經(jīng)給出)。

然后棕孙,當(dāng)父類加載器都說(shuō)沒(méi)有加載過(guò)目標(biāo)類時(shí)舔亭,他會(huì)嘗試自己加載目標(biāo)類,這就調(diào)用了findClass方法蟀俊,可以看一下findClass方法的定義:

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

可以發(fā)現(xiàn)他要求返回一個(gè)Class對(duì)象實(shí)例钦铺,這里我通過(guò)一個(gè)實(shí)現(xiàn)類sun.rmi.rmic.iiop.ClassPathLoader來(lái)說(shuō)明一下findClass都干了什么。

protected Class findClass(String var1) throws ClassNotFoundException {
    // 從指定路徑加載指定名稱的class的字節(jié)流
    byte[] var2 = this.loadClassData(var1);
    // 通過(guò)ClassLoader的defineClass來(lái)創(chuàng)建class對(duì)象實(shí)例
    return this.defineClass(var1, var2, 0, var2.length);
}

他做的事情在注釋中已經(jīng)給出肢预,可以看到矛洞,最終是通過(guò)defineClass方法來(lái)實(shí)例化class對(duì)象的。
另外可以發(fā)現(xiàn)烫映,class文件字節(jié)的獲取和處理我們是可以控制的沼本。所以,第二個(gè)作用:我們可以在字節(jié)流解析這一步做一些自定義的處理锭沟。 例如抽兆,加解密。

protected final Class<?> defineClass(String name, byte[] b, int off, int len)
    throws ClassFormatError
{
    return defineClass(name, b, off, len, null);
}

被final掉了冈钦,沒(méi)辦法覆寫(xiě)郊丛,所以這里看似不能做什么事兒了。

小結(jié)一下:

  • 通過(guò)loadClass在指定的路徑下查找文件瞧筛。
  • 通過(guò)findClass方法解析class字節(jié)流厉熟,并實(shí)例化class對(duì)象。

什么時(shí)候需要自己實(shí)現(xiàn)類加載器

當(dāng)JDK提供的類加載器實(shí)現(xiàn)無(wú)法滿足我們的需求時(shí)较幌,才需要自己實(shí)現(xiàn)類加載器揍瑟。
根據(jù)上述類加載器的作用,可能有以下幾個(gè)場(chǎng)景需要自己實(shí)現(xiàn)類加載器

  • 當(dāng)需要在自定義的目錄中查找class文件時(shí)(或網(wǎng)絡(luò)獲日)
  • class被類加載器加載前的加解密(代碼加密領(lǐng)域)

如何實(shí)現(xiàn)自己的類加載器

接下來(lái)绢片,實(shí)現(xiàn)一個(gè)在自定義class類路徑中查找并加載class的自定義類加載器。

package com.lordx.sprintbootdemo.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 自定義ClassLoader
 * 功能:可自定義class文件的掃描路徑
 * @author zhiminxu 
 */
// 繼承ClassLoader岛琼,獲取基礎(chǔ)功能
public class TestClassLoader extends ClassLoader {

    // 自定義的class掃描路徑
    private String classPath;

    public TestClassLoader(String classPath) {
        this.classPath = classPath;
    }

    // 覆寫(xiě)ClassLoader的findClass方法
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // getDate方法會(huì)根據(jù)自定義的路徑掃描class底循,并返回class的字節(jié)
        byte[] classData = getDate(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            // 生成class實(shí)例
            return defineClass(name, classData, 0, classData.length);
        }
    }


    private byte[] getDate(String name) {
        // 拼接目標(biāo)class文件路徑
        String path = classPath + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while ((num = is.read(buffer)) != -1) {
                stream.write(buffer, 0 ,num);
            }
            return stream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

使用自定義的類加載器

package com.lordx.sprintbootdemo.classloader;

public class MyClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        // 自定義class類路徑
        String classPath = "/Users/zhiminxu/developer/classloader";
        // 自定義的類加載器實(shí)現(xiàn):TestClassLoader
        TestClassLoader testClassLoader = new TestClassLoader(classPath);
        // 通過(guò)自定義類加載器加載
        Class<?> object = testClassLoader.loadClass("ClassLoaderTest");
        // 這里的打印應(yīng)該是我們自定義的類加載器:TestClassLoader
        System.out.println(object.getClassLoader());
    }
}

跟ClassLoader相關(guān)的幾個(gè)異常

ClassNotFoundException

這個(gè)異常,相信大家經(jīng)常遇到槐瑞。
那么熙涤,到底啥原因?qū)е聮伋鲞@個(gè)異常呢?
看一下ClassLoader的源碼,在JVM調(diào)用loadClassInternal的方法中祠挫,就會(huì)拋出這個(gè)異常那槽。
其聲明如下:

// This method is invoked by the virtual machine to load a class.
private Class<?> loadClassInternal(String name)
    throws ClassNotFoundException
{
    // For backward compatibility, explicitly lock on 'this' when
    // the current class loader is not parallel capable.
    if (parallelLockMap == null) {
        synchronized (this) {
             return loadClass(name);
        }
    } else {
        return loadClass(name);
    }
}

這里面loadClass方法會(huì)拋出這個(gè)異常,再來(lái)看loadClass方法

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

調(diào)用了重寫(xiě)的loadClass方法

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 查看findLoadedClass的聲明等舔,沒(méi)有拋出這個(gè)異常
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 這里會(huì)拋骚灸,但同樣是調(diào)loadClass方法,無(wú)需關(guān)注
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // catch住沒(méi)有拋慌植,因?yàn)橐谙旅鎳L試自己獲取class
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                // 關(guān)鍵:這里會(huì)拋
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

再來(lái)看findClass方法

/**
 * Finds the class with the specified <a href="#name">binary name</a>.
 * This method should be overridden by class loader implementations that
 * follow the delegation model for loading classes, and will be invoked by
 * the {@link #loadClass <tt>loadClass</tt>} method after checking the
 * parent class loader for the requested class.  The default implementation
 * throws a <tt>ClassNotFoundException</tt>.
 *
 * @param  name
 *         The <a href="#name">binary name</a> of the class
 *
 * @return  The resulting <tt>Class</tt> object
 *
 * @throws  ClassNotFoundException
 *          If the class could not be found
 *
 * @since  1.2
 */
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

果然這里拋出的甚牲,注釋中拋出這個(gè)異常的原因是說(shuō):當(dāng)這個(gè)class無(wú)法被找到時(shí)拋出。這也就是為什么在上面自定義的類加載器中蝶柿,覆寫(xiě)findClass方法時(shí)鳖藕,如果沒(méi)有找到class要拋出這個(gè)異常的原因。
至此只锭,這個(gè)異常拋出的原因就明確了:在雙親委派模型的所有相關(guān)類加載器中,目標(biāo)類在每個(gè)類加載器的掃描路徑中都不存在時(shí)院尔,會(huì)拋出這個(gè)異常蜻展。

NoClassDefFoundError

這也是個(gè)經(jīng)常會(huì)碰到的異常,而且不熟悉的同學(xué)可能經(jīng)常搞不清楚什么時(shí)候拋出ClassNotFoundException邀摆,什么時(shí)候拋出NoClassDefFoundError纵顾。

我們還是到ClassLoader中搜一下這個(gè)異常,可以發(fā)現(xiàn)在defineClass方法中可能拋出這個(gè)異常栋盹,defineClass方法源碼如下:

/**
 * ... 忽略注釋和參數(shù)以及返回值的說(shuō)明施逾,直接看異常聲明
 * 
 * @throws  ClassFormatError
 *          If the data did not contain a valid class
 *
 * 在這里。由于NoClassDefFoundError是Error下的例获,所以不用顯示throws
 * @throws  NoClassDefFoundError
 *          If <tt>name</tt> is not equal to the <a href="#name">binary
 *          name</a> of the class specified by <tt>b</tt>
 *
 * @throws  IndexOutOfBoundsException
 *          If either <tt>off</tt> or <tt>len</tt> is negative, or if
 *          <tt>off+len</tt> is greater than <tt>b.length</tt>.
 *
 * @throws  SecurityException
 *          If an attempt is made to add this class to a package that
 *          contains classes that were signed by a different set of
 *          certificates than this class, or if <tt>name</tt> begins with
 *          "<tt>java.</tt>".
 */
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                     ProtectionDomain protectionDomain)
    throws ClassFormatError
{
    protectionDomain = preDefineClass(name, protectionDomain);
    String source = defineClassSourceLocation(protectionDomain);
    Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
    postDefineClass(c, protectionDomain);
    return c;
}

繼續(xù)追蹤里面的方法汉额,可以發(fā)現(xiàn)是preDefineClass方法拋出的,這個(gè)方法源碼如下:

/* Determine protection domain, and check that:
    - not define java.* class,
    - signer of this class matches signers for the rest of the classes in
      package.
*/
private ProtectionDomain preDefineClass(String name,
                                        ProtectionDomain pd)
{
    // 這里顯示拋出榨汤,可發(fā)現(xiàn)是在目標(biāo)類名校驗(yàn)不通過(guò)時(shí)拋出的
    if (!checkName(name))
        throw new NoClassDefFoundError("IllegalName: " + name);

    // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
    // relies on the fact that spoofing is impossible if a class has a name
    // of the form "java.*"
    if ((name != null) && name.startsWith("java.")) {
        throw new SecurityException
            ("Prohibited package name: " +
             name.substring(0, name.lastIndexOf('.')));
    }
    if (pd == null) {
        pd = defaultDomain;
    }

    if (name != null) checkCerts(name, pd.getCodeSource());

    return pd;
}

這個(gè)校驗(yàn)的源碼如下

// true if the name is null or has the potential to be a valid binary name
private boolean checkName(String name) {
    if ((name == null) || (name.length() == 0))
        return true;
    if ((name.indexOf('/') != -1)
        || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
        return false;
    return true;
}

所以蠕搜,這個(gè)異常拋出的原因是:類名校驗(yàn)未通過(guò)。
但這個(gè)異常其實(shí)不止在ClassLoader中拋出收壕,其他地方妓灌,例如框架中,web容器中都有可能拋出蜜宪,還要具體問(wèn)題具體分析虫埂。

參考

https://mp.weixin.qq.com/s/2uCVsSCWE3Z8s484DOYACw

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市圃验,隨后出現(xiàn)的幾起案子掉伏,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岖免,死亡現(xiàn)場(chǎng)離奇詭異岳颇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)颅湘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門话侧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人闯参,你說(shuō)我怎么就攤上這事瞻鹏。” “怎么了鹿寨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵新博,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我脚草,道長(zhǎng)赫悄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任馏慨,我火速辦了婚禮埂淮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘写隶。我一直安慰自己倔撞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布慕趴。 她就那樣靜靜地躺著痪蝇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冕房。 梳的紋絲不亂的頭發(fā)上躏啰,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音耙册,去河邊找鬼丙唧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛觅玻,可吹牛的內(nèi)容都是我干的想际。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼溪厘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼胡本!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起畸悬,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侧甫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體披粟,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咒锻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了守屉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惑艇。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拇泛,靈堂內(nèi)的尸體忽然破棺而出滨巴,到底是詐尸還是另有隱情,我是刑警寧澤俺叭,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布恭取,位于F島的核電站,受9級(jí)特大地震影響熄守,放射性物質(zhì)發(fā)生泄漏蜈垮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一裕照、第九天 我趴在偏房一處隱蔽的房頂上張望窃款。 院中可真熱鬧,春花似錦牍氛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蜒茄,卻和暖如春唉擂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檀葛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工玩祟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屿聋。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓空扎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親润讥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子转锈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存, 并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)楚殿、轉(zhuǎn)換解析和初始化撮慨, 最終形成可以被虛擬機(jī)直接使...
    好好學(xué)習(xí)Sun閱讀 1,205評(píng)論 0 3
  • 每個(gè)編寫(xiě)的”.java”拓展名類文件都存儲(chǔ)著需要執(zhí)行的程序邏輯,這些”.java”文件經(jīng)過(guò)Java編譯器編譯成拓展...
    QuantRuu閱讀 555評(píng)論 0 49
  • 目錄 一、類加載器 還記得類加載機(jī)制嗎砌溺?類加載機(jī)制的各階段是加載影涉、連接(驗(yàn)證、準(zhǔn)備规伐、解析)蟹倾、初始化、使用楷力、卸載喊式。可...
    J先生有點(diǎn)兒屁閱讀 1,189評(píng)論 2 8
  • 轉(zhuǎn)發(fā):本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布 ClassLoader翻譯過(guò)來(lái)就是類加載...
    尼爾君閱讀 535評(píng)論 0 1
  • JVM把class文件加載到內(nèi)存里面萧朝,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)岔留、準(zhǔn)備、解析和初始化检柬,最終能夠被形成被JVM可以直接使用的J...
    天生小包閱讀 377評(píng)論 0 0