1.將你這段字符串輸出到一個文件里,用Java類文件的方式命名。
創(chuàng)新互聯(lián)是專業(yè)的蕭縣網(wǎng)站建設(shè)公司,蕭縣接單;提供網(wǎng)站設(shè)計、成都網(wǎng)站設(shè)計,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行蕭縣網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
2.調(diào)用外部javac命令將該文件編譯。
3.用類加載器(ClassLoad)動態(tài)加載新的class文件并用Class.forName()注冊該類,然后就可以正常使用了。
上面的每一步都能在baidu中找到實現(xiàn)方法,自己發(fā)揮吧。
首先 java網(wǎng)站是基于jdk運行的。你得先裝jdk。去官網(wǎng)下載一個jdk安裝。然后你得編譯你的項目,打包到容器(tomcat,weblogic,jboss等)中運行即可。再提醒一下就是,看看你的源代碼有木有用到數(shù)據(jù)庫或者其他類型的服務(wù)。如果有的話,你得裝數(shù)據(jù)庫或服務(wù),并且初始化數(shù)據(jù)。再啟動你的你的容器。
1.類加載器深入剖析
Java虛擬機與程序的生命周期 :
當(dāng)我們執(zhí)行一個java程序的時候 , 會啟動一個JVM進程 , 當(dāng)程序執(zhí)行完之后 , JVM進程就消亡了 ;
在如下情況下JVM將結(jié)束聲明周期 :
System.exit(int)方法 , 當(dāng)執(zhí)行這個方法的時候 , 虛擬機會退出 ; 這個方法傳入一個整形參數(shù) , 這個參數(shù)是狀態(tài)嗎 : 如果這個整形是 0 的話 , 就是正常退出 , 如果不是0的話 , 就是異常退出 ;
程序正常結(jié)束 ;
程序執(zhí)行過程中 , 遇到了異常或錯誤 , 而異常終止 : 如果我們在程序中出現(xiàn)了異常 , 而不去處理 , 會將異常一直拋給main函數(shù) , main函數(shù)會將異常拋給JVM , JVM如果處理不了異常 , JVM就會異常退出 ;
由于操作系統(tǒng)出現(xiàn)錯誤導(dǎo)致JVM進程終止 : JVM所依賴的平臺出現(xiàn)錯誤 , 導(dǎo)致JVM終止 ;
2.類的加載,連接和初始化
加載 : 查找并加載類的二進制數(shù)據(jù) , 將class字節(jié)碼文件加載到內(nèi)存中 ;
連接 :
-
驗證
: 確保被加載的類的正確性 , 使用javac 編譯工具生成的字節(jié)碼文件能通過驗證 , 如果不是由javac編譯生成的字節(jié)碼文件 , 如果自己生成的字節(jié)碼文件不符合JVM虛擬機對字節(jié)碼文件的要求的話 , 可能會出現(xiàn)驗證通不過的情況 ; 比如說隨便拿一個文件 , 將后綴名直接修改為.class , 這樣的字節(jié)碼文件肯定不合法 ;
-
準(zhǔn)備
: 為類的靜態(tài)變量分配內(nèi)存 , 并將其初始化為默認(rèn)值 ;
-
解析
: 把類中的符號引用轉(zhuǎn)為直接引用 ;
初始化 : 為類的靜態(tài)變量賦予正確的初始值(正確的值指的是用戶賦的值) ;
-好像這個與連接階段的準(zhǔn)備有些重復(fù) , 在連接的準(zhǔn)備階段只是賦予初始變量 , 如果用戶給這個變量賦了初始值 , 那么這個變量在連接的準(zhǔn)備階段仍然會賦予初始值 ;
-在這個階段 , 才會真正的將初始值賦給靜態(tài)變量 ;
Java程序?qū)︻惖氖褂梅绞接?主動使用 和 被動使用 ;
所有的JVM實現(xiàn) , 必須在每個類或者接口 , 被java程序 “首次主動使用” 時才初始化他們 ;
主動使用 :
創(chuàng)建類的實例 ;
訪問某個類或接口的靜態(tài)變量 , 或者對該靜態(tài)變量賦值 ;
調(diào)用類的靜態(tài)方法 ;
反射 : Class.forName(“類名”) ;
初始化一個類的子類 , 看做對父類的主動使用 ;
java虛擬機啟動的時候 , 被標(biāo)明啟動類的類 , 即包含main方法的類 , 程序的入口 ;
除了上面6種主動使用之外 , 其它的情況均為被動使用 , 其它情況都不會執(zhí)行第三步初始化 ;
3.類的加載
(1)概念
類的加載 : 指的是將類的.class文件中的二進制數(shù)據(jù)讀入到內(nèi)存中 , 將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi) , 然后再堆區(qū)創(chuàng)建一個java.lang.Class對象 , 用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu) ;
反射 : 反射就是跟句堆區(qū)的字節(jié)碼文件 , 獲取方法去的數(shù)據(jù)結(jié)構(gòu) ;
解析 : Class對象是由JVM自己創(chuàng)建的 , 所有的對象都是經(jīng)過Class對象創(chuàng)建 , 這個Class對象是反射的入口, 通過Class對象 , 可以關(guān)聯(lián)到目標(biāo)class字節(jié)碼文件的內(nèi)部結(jié)構(gòu) ;
所有的類對應(yīng)的Class對象都是唯一的一個 , 這個類是由JVM進行創(chuàng)建的 , 并且只有JVM才會創(chuàng)建Class對象 ;
類加載的最終產(chǎn)品是位于堆區(qū)中的Class對象 , Class對象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu) , 并且向Java程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口(反射用的接口) ;
(2)加載.class文件的方式
從本地系統(tǒng)中直接加載 : 編譯好的.class字節(jié)碼文件直接從硬盤中加載 ;
通過網(wǎng)絡(luò)下載.class文件 : 將class字節(jié)碼文件放在網(wǎng)絡(luò)空間中 , 使用URLClassLoader來加載在網(wǎng)絡(luò)上的.class字節(jié)碼文件 , 使用默認(rèn)的父親委托機制加載字節(jié)碼文件 ;
從zip , jar 等壓縮文件中加載字節(jié)碼文件 : 在開發(fā)的時候 , 導(dǎo)入jar包 , 就是這種方式 ;
從專有的數(shù)據(jù)庫中提取字節(jié)碼文件 ;
將java源文件動態(tài)編譯為字節(jié)碼文件 ;
(3)類加載器
l Java虛擬機自帶的類加載器 :
-根類加載器 ( Bootstrap ) : 是C++寫的 , 程序員無法再java代碼中獲取這個類 , 如果使用getClassLoader()方法獲取到的是一個null值 ;
package jvm;
Java代碼
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
//java.lang包下的類使用的是跟類加載器進行加載的
Class clazz = Class.forName("java.lang.String");
System.out.println(clazz.getClassLoader());
//自定義的類使用的是應(yīng)用類加載器(系統(tǒng)加載器)
Class clazz2 = Class.forName("jvm.C");
System.out.println(clazz2.getClassLoader());
}
}
class C{}
執(zhí)行結(jié)果 :
null
Java代碼
sun.misc.Launcher$AppClassLoader@1372a1a
-擴展類加載器 ( Extension ) : Java編寫 ;
-系統(tǒng)類加載器(應(yīng)用加載器) ( System ) : Java編寫 ;
用戶自定義的類加載器 :
-自定義的類加載器都是java.lang.ClassLoader子類 ;
-用戶可以定制類的加載方式
String類是由根類加載器進行加載的 , 我們可以調(diào)用Class對象的
關(guān)于代理中創(chuàng)建對象的類加載器 : 創(chuàng)建代理對象的時候 , 動態(tài)創(chuàng)建一個類 , 然后使用指定的類加載器將這個類加載到內(nèi)存中 , 然后用加載到內(nèi)存中的類生成代理對象 ;
創(chuàng)建代理對象的方法 : newProxyInstance(ClassLoader loader , Class [] Interfaces , InvocationHandler h )
loader 是定義的代理類的類加載器 , 中間的接口數(shù)組是代理類的要實現(xiàn)的接口列表 , h 是指派方法調(diào)用的調(diào)用處理程序 ;
類加載器并不需要在某個類被 “首次主動使用” 時再加載它 :
-預(yù)加載機制 : JVM規(guī)范允許類加載器在預(yù)料某個類將要被使用的時就預(yù)先加載它 ;
-報錯時機 : 如果在預(yù)加載的過程中遇到了字節(jié)碼文件缺失或者存在錯誤的情況 , 類加載器會在程序首次主動使用(上面提到的六種情況)該類的時候報錯(LinkageError錯誤) ;
-不報錯時機 : 如果這個錯誤的字節(jié)碼文件所對應(yīng)的類一直沒有被使用 , 那么類加載器就不會報告錯誤 ,即便有錯誤也不會報錯 ;
LinkageError : 這個錯誤是Error的子類 , 程序不能處理這些錯誤 , 這些錯誤都是由虛擬機來處理 , 這個錯誤表示出錯的是子類 , 在一定程序上依賴于另一個類 , 在編譯了前面一個類的時候 , 與后面所依賴的類出現(xiàn)了不兼容的情況 ;
例如 : 我們使用了jdk 1.6 在編譯一個程序 , 但是運行環(huán)境是jre1.5的 , 就會出現(xiàn)LinkageError錯誤 ;
4.類的連接
(1)定義
類被加載之后 , 就進入鏈接階段 ; 鏈接 : 將已讀入內(nèi)存的二進制數(shù)據(jù)合并到虛擬機的運行時環(huán)境中去 ;
鏈接顧名思義就是講類與類之間進行關(guān)聯(lián) , 例如我們在類A中調(diào)用了類B , 在鏈接過程中 , 就將A與B進行鏈接 ,將面向?qū)ο笳Z言轉(zhuǎn)化為面向過程語言 ;
(2)類的驗證
類文件的結(jié)構(gòu)檢查 : 確保類文件遵從java類文件的固定格式 , 開始類的描述 , 聲明 , 方法調(diào)用格式等 ;
語義檢查 : 確保類本身符合java語言的語法規(guī)定 , 比如final類型的類沒有子類 , final類型的方法沒有被覆蓋 ,在eclipse中這種錯誤編譯的時候不能通過 , 但是通過其他的方法可以生成錯誤的字節(jié)碼文件 , 這里就是檢測惡意生成的字節(jié)碼文件 ;
字節(jié)碼驗證 : 確保字節(jié)碼流可以被JVM安全的執(zhí)行 , 字節(jié)碼流代表java方法(包括靜態(tài)方法和實例方法) , 它是由被稱作操作碼的單字節(jié)指令組成的序列 , 每一個操作碼后面跟著一個或多個操作數(shù) , 字節(jié)碼驗證步驟會檢查每個操作碼是否合法 , 即是否有著合法的操作數(shù) ;
下面是指令碼組成的序列 , 類似于微指令 :
Jvm編譯指令代碼代碼
// Compiled from ByteToCharCp1122.java (version 1.5 : 49.0, super bit)
public class sun.io.ByteToCharCp1122 extends sun.io.ByteToCharSingleByte {
// Field descriptor #17 Lsun/nio/cs/ext/IBM1122;
private static final sun.nio.cs.ext.IBM1122 nioCoder;
// Method descriptor #18 ()Ljava/lang/String;
// Stack: 1, Locals: 1
public java.lang.String getCharacterEncoding();
0 ldc String "Cp1122" [1]
2 areturn
Line numbers:
[pc: 0, line: 25]
// Method descriptor #2 ()V
// Stack: 2, Locals: 1
public ByteToCharCp1122();
0 aload_0 [this]
1 invokespecial sun.io.ByteToCharSingleByte() [25]
4 aload_0 [this]
5 getstatic sun.io.ByteToCharCp1122.nioCoder : sun.nio.cs.ext.IBM1122 [23]
8 invokevirtual sun.nio.cs.ext.IBM1122.getDecoderSingleByteMappings() : java.lang.String [27]
11 putfield sun.io.ByteToCharSingleByte.byteToCharTable : java.lang.String [24]
14 return
Line numbers:
[pc: 0, line: 28]
[pc: 4, line: 29]
[pc: 14, line: 30]
// Method descriptor #2 ()V
// Stack: 2, Locals: 0
static {};
0 new sun.nio.cs.ext.IBM1122 [15]
3 dup
4 invokespecial sun.nio.cs.ext.IBM1122() [26]
7 putstatic sun.io.ByteToCharCp1122.nioCoder : sun.nio.cs.ext.IBM1122 [23]
10 return
Line numbers:
[pc: 0, line: 22]
}
l 二進制兼容性的驗證 : 確保相互引用的類之間協(xié)調(diào)一致的 ; 例如在A類的a()方法中調(diào)用B類的b()方法 , JVM在驗證A類的時候 , 會驗證B類的b()方法 , 加入b()方法不存在 , 或者版本不兼容(A,B兩類使用不同的JDK版本編譯) , 會拋出NoSuchMethodError錯誤 ;
(3)準(zhǔn)備階段
在準(zhǔn)備階段 , JVM為類的靜態(tài)變量分配內(nèi)存空間 , 并設(shè)置默認(rèn)的初始值 . 例如下面的Sample類 , 在準(zhǔn)備階段 ,為int類型的靜態(tài)變量分配4個字節(jié) , 并賦予初始值 0 ; 為long 類型的靜態(tài)變量 b , 分配8個字節(jié) , 并賦予初始值 0 ;
PS : 在java中基本類型變量占用的空間是一定的 , java運行在JVM上的 , 在C中 , 就要根據(jù)平臺變化而變化了 ;
public class Sample {
Java代碼
private static int a = 1 ;
private static long b ;
static {
b = 2 ;
}
(4)類的解析
在解析階段 , JVM 會把類的二進制數(shù)據(jù)中的符號引用替換為直接引用 , 例如在A類中的a()方法引用B類中的b()方法 ;
在A類的二進制數(shù)據(jù)中包含了一個對B類的b()方法的符號引用 , 這個符號引用由b()方法的全名和相關(guān)的描述符組成 , 在Java解析階段 , 就會把這個符號引用替換為指針 , 這個指針就是C語言中的指針了 , 該指針指向B類的b()方法在方法區(qū)中的內(nèi)存位置 , 這個指針就是直接引用 ;
5.類的初始化
在初始化階段 , Java虛擬機執(zhí)行類的初始化操作 , 為類的靜態(tài)變量賦予初始值 , 在程序中 , 靜態(tài)變量初始化有兩種途徑 :
直接在聲明處進行初始化 , 例如下面的Sample中的 變量a ;
在靜態(tài)代碼塊中進行初始化 , 例如下面的Sample中的變量b ;
Java代碼
public class Sample {
private static int a = 1 ;
private static long b ;
static {
b = 2 ;
}
}
6.面試題介紹
Java代碼
public class PrepareOrInit {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton.count1);
System.out.println(singleton.count2);
}
}
class Singleton{
private static Singleton singleton = new Singleton() ;
public static int count1 ;
public static int count2 = 0 ;
private Singleton(){
count1 ++ ;
count2 ++ ;
}
public static Singleton getInstance(){
return singleton ;
}
}
執(zhí)行結(jié)果 : 1 0
分析 : 這段代碼與類的鏈接中的準(zhǔn)備階段 和 初始化階段 有關(guān)系 , 準(zhǔn)備階段是給靜態(tài)的字段賦予默認(rèn)值 , 初始化階段給靜態(tài)變量賦予正確的值 , 即用戶的值 ;
在主函數(shù)中 , 調(diào)用了類的靜態(tài)方法 , 相當(dāng)于主動使用 , 這里調(diào)用了類的靜態(tài)方法 ;
之后進行連接的準(zhǔn)備操作 , 給類中的靜態(tài)變量賦予初值 , singleton值為null , count1 與 count2 值為0 ;
執(zhí)行初始化操作 , 給類中的靜態(tài)變量賦予正確的值 , 給singleton變量賦予正確的值 , 調(diào)用構(gòu)造方法 , 此時count1與 count2執(zhí)行自增操作 , 兩者都變成1 , 然后執(zhí)行count1的賦予正確值操作 , 這里用戶沒有賦值操作 , count2 用戶進行了 賦值為0的操作 , 0將原來的1覆蓋掉了 , 因此結(jié)果為 1 , 0 ;
Java代碼
public class PrepareOrInit {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton.count1);
System.out.println(singleton.count2);
}
}
class Singleton{
public static int count1 ;
public static int count2 = 0 ;
private static Singleton singleton = new Singleton() ;
private Singleton(){
count1 ++ ;
count2 ++ ;
}
public static Singleton getInstance(){
return singleton ;
}
}
執(zhí)行結(jié)果 : 1 1
在準(zhǔn)備階段count1 和 count2 都賦值為0 , 然后在初始化階段 , 全部賦值為1 ;