當前位置:首頁 » 編程語言 » javaclass載入

javaclass載入

發布時間: 2022-05-14 20:44:49

1. java 類載入先後順序

父類態代碼塊
父類靜態屬性
//以上根據代碼順序
子類靜態屬性
子類靜態代碼塊
//以上根據代碼順序
父類屬性
父類代碼塊
//以上根據代碼順序
父類構造器
子類屬性
子類代碼塊
//以上根據代碼順序
子類構造方法
子類方法
父類方法
父類靜態方法
子類靜態方法

package com.zz;

/**
* Created by @摩西摩西 2020/3/21 22:29
**/
public class Father {
public String name=Son.getmetod("父類屬性");
public Father(){
System.out.println("父類構造器");
}
{
System.out.println("父類代碼塊");
}
public void fatherMethod(){
System.out.println("父類方法");
}

static {
System.out.println("父類態代碼塊");
}
static String name1=Son.getmetod("父類靜態屬性");
public static void fatherStaticMethod(){
System.out.println("父類靜態方法");
}
}
package com.zz;

/**
* Created by @摩西摩西 2020/3/21 22:32
**/
public class Son extends Father {
public String name = getmetod("子類屬性");

public Son() {
System.out.println("子類構造方法");
}

{
System.out.println("子類代碼塊");
}
public void sonMethod(){
System.out.println("子類方法");
}

public static String name1 = getmetod("子類靜態屬性");

static {
System.out.println("子類靜態代碼塊");
}

public static void sonStaticMethod() {
System.out.println("子類靜態方法");
}

public static String getmetod(String s) {
System.out.println(s);
return s;
}

public static void main(String[] args) {
Son son= new Son();
son.sonMethod();
son.fatherMethod();
fatherStaticMethod();
sonStaticMethod();
}
}

2. Java中用import導入類和用Class方法載入類有什麼區別

import僅僅包含導入操作,並不包含將位元組碼文件載入進內存這一動作,將位元組碼文件載入進內存是後續的實例化操作完成的。例如通過import導入了一堆包和類,但是後續什麼都沒用(沒用實例化),那麼導入的東西是不會被載入進內存的。而且import是編譯期的,如果你在後續代碼中沒有使用到你導入的內容,那麼import語句甚至不會編譯和執行。查看位元組碼文件可以看出,import的作用就是對你程序中要用到(實例)的東西進行署名(signature),當程序運行的時候好知道你實例化的對象的類的位元組碼文件去哪裡找。
而Class.forName方法包含的動作是:根據給出的全類名(方法的參數)找到對應的位元組碼文件,並將位元組碼文件通過ClassLoader載入進內存中生成Class類對象(方法的返回值就是Class類對象)。
這些就是二者的區別了。

3. Java:如何編寫自己的Java類載入器

給你簡單介紹一下類載入器

1.類載入器就載入位元組碼文件(.class)

public class FileClassLoader extends ClassLoader {String rootDir=null;public FileClassLoader(String rootDir) {this.rootDir = rootDir;}@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {//首先檢查是否已經被載入了。Class<?> c = findLoadedClass(className);String path = rootDir + "/" + className.replace('.', '/') + ".class";if (c != null) {return c;} else {/*雙親委託模式*/ClassLoader loaderParent = this.getParent();c = loaderParent.loadClass(className);if (c != null) {return c;} else {/*如果再不行的話,就再進行載入。因為位元組碼的本質就是一個位元組數組*/InputStream is = null;ByteArrayOutputStream outputStream = new ByteArrayOutputStream();try {is = new FileInputStream(path);byte[] buffer = new byte[1024];int len = 0;while ((len = is.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}c = defineClass(className, buffer, 0, buffer.length);}catch (Exception e) {e.printStackTrace();}finally {if (is != null) {try {is.close();}catch (IOException e) {e.printStackTrace();}}}}return c;}}

}

/*

相同的類載入器對同一個類進行載入,得到的hascode是相同的

* 不同的類載入器對同一類進行載入,得到的hascode是不一樣的*/public class Demo {public static void main(String[] args) {FileClassLoader loader = new FileClassLoader("c://myjava");FileClassLoader loader2=new FileClassLoader("c://myjava");try {Class<?> c = loader.findClass("com.lg.test.HelloWorld");Class<?> c0=loader.findClass("com.lg.test.HelloWorld");Class<?> c1=loader2.findClass("com.lg.test.HelloWorld");Class<?> c2=loader.findClass("com.lg.test.Demo01");Class<?> c3=loader.findClass("java.lang.String");System.out.println(c.hashCode());System.out.println(c.getClassLoader());System.out.println(c0.hashCode());System.out.println(c0.getClassLoader());System.out.println(c1.hashCode());System.out.println(c1.getClassLoader());System.out.println(c2.hashCode());System.out.println(c2.getClassLoader());System.out.println(c3.hashCode());System.out.println(c3.getClassLoader());}catch (ClassNotFoundException e) {e.printStackTrace();}}}

4. java程序什麼是類載入

你可以了解一下
深入理解java虛擬機,
java類載入
是因為在運行時當它需要引用某個類的時候,會先去載入這個類,就是去讀這個class文件到內存裡面來,現在還沒有創建這個類的實例,
你可以理解為
你寫的java代碼在編譯成class文件後,並沒有去執行,或者去引用他,只有程序運行時它將要使用這個類的時候才會去讀取這個class文件,這時候叫做類載入,當緩存了這個類文件之後,後面的創建類的時候都會去引用它,因為類載入是運行是載入,所以java代碼在運行時是有辦法修改的,同樣java類載入也允許載入一個遠程的class文件,也代表它是支持程序不停止時更新程序代碼的。

5. Java編程細節 java是怎麼載入類的

一.載入 通過類的全限定名轉換為二進制位元組流,在jvm堆中生成代表這個Class的對象,作為方法區域的方法入口.
二.連接 1.驗證:驗證class的位元組流是否對jvm虛擬機造成傷害,是否符合jvm的規范,這里包含幾個驗證. 2.准備:為類的static變數賦初始值,其中不包含類其它實例成員的初始化. 3.解析:將常量池內的符號引用替換成直接引用三.初始化 這里是static{}塊,構造函數,代碼塊{}的執行過程.四.使用 對象的屬性,方法等調用操作.五.銷毀 jvm通過確定對象沒有引用後進行gc操作.

6. java類載入順序

記住 3 條原則:
1、父類優先於子類
2、屬性優先於代碼塊優先於構造方法
3、靜態優先於非靜態
因此,類載入順序為:
父類靜態變數->父類靜態語句塊->子類靜態變數->子類靜態語句塊->父類普通成員變數->父類動態語句塊->父類構造器->子類普通成員變數->子類動態語句塊->子類構造器

7. 怎麼 classloader 載入一個java源文件

1.類載入器深入剖析
Java虛擬機與程序的生命周期 :
當我們執行一個java程序的時候 , 會啟動一個JVM進程 , 當程序執行完之後 , JVM進程就消亡了 ;
在如下情況下JVM將結束聲明周期 :
System.exit(int)方法 , 當執行這個方法的時候 , 虛擬機會退出 ; 這個方法傳入一個整形參數 , 這個參數是狀態嗎 : 如果這個整形是 0 的話 , 就是正常退出 , 如果不是0的話 , 就是異常退出 ;
程序正常結束 ;
程序執行過程中 , 遇到了異常或錯誤 , 而異常終止 : 如果我們在程序中出現了異常 , 而不去處理 , 會將異常一直拋給main函數 , main函數會將異常拋給JVM , JVM如果處理不了異常 , JVM就會異常退出 ;
由於操作系統出現錯誤導致JVM進程終止 : JVM所依賴的平台出現錯誤 , 導致JVM終止 ;

2.類的載入,連接和初始化
載入 : 查找並載入類的二進制數據 , 將class位元組碼文件載入到內存中 ;
連接 :

-
驗證
: 確保被載入的類的正確性 , 使用javac 編譯工具生成的位元組碼文件能通過驗證 , 如果不是由javac編譯生成的位元組碼文件 , 如果自己生成的位元組碼文件不符合JVM虛擬機對位元組碼文件的要求的話 , 可能會出現驗證通不過的情況 ; 比如說隨便拿一個文件 , 將後綴名直接修改為.class , 這樣的位元組碼文件肯定不合法 ;

-
准備
: 為類的靜態變數分配內存 , 並將其初始化為默認值 ;

-
解析
: 把類中的符號引用轉為直接引用 ;
初始化 : 為類的靜態變數賦予正確的初始值(正確的值指的是用戶賦的值) ;
-好像這個與連接階段的准備有些重復 , 在連接的准備階段只是賦予初始變數 , 如果用戶給這個變數賦了初始值 , 那麼這個變數在連接的准備階段仍然會賦予初始值 ;

-在這個階段 , 才會真正的將初始值賦給靜態變數 ;

Java程序對類的使用方式有 主動使用 和 被動使用 ;
所有的JVM實現 , 必須在每個類或者介面 , 被java程序 「首次主動使用」 時才初始化他們 ;
主動使用 :
創建類的實例 ;
訪問某個類或介面的靜態變數 , 或者對該靜態變數賦值 ;
調用類的靜態方法 ;
反射 : Class.forName(「類名」) ;
初始化一個類的子類 , 看做對父類的主動使用 ;
java虛擬機啟動的時候 , 被標明啟動類的類 , 即包含main方法的類 , 程序的入口 ;

除了上面6種主動使用之外 , 其它的情況均為被動使用 , 其它情況都不會執行第三步初始化 ;

3.類的載入
(1)概念

類的載入 : 指的是將類的.class文件中的二進制數據讀入到內存中 , 將其放在運行時數據區的方法區內 , 然後再堆區創建一個java.lang.Class對象 , 用來封裝類在方法區內的數據結構 ;
反射 : 反射就是跟句堆區的位元組碼文件 , 獲取方法去的數據結構 ;
解析 : Class對象是由JVM自己創建的 , 所有的對象都是經過Class對象創建 , 這個Class對象是反射的入口, 通過Class對象 , 可以關聯到目標class位元組碼文件的內部結構 ;

所有的類對應的Class對象都是唯一的一個 , 這個類是由JVM進行創建的 , 並且只有JVM才會創建Class對象 ;

類載入的最終產品是位於堆區中的Class對象 , Class對象封裝了類在方法區內的數據結構 , 並且向Java程序員提供了訪問方法區內的數據結構的介面(反射用的介面) ;

(2)載入.class文件的方式
從本地系統中直接載入 : 編譯好的.class位元組碼文件直接從硬碟中載入 ;
通過網路下載.class文件 : 將class位元組碼文件放在網路空間中 , 使用URLClassLoader來載入在網路上的.class位元組碼文件 , 使用默認的父親委託機制載入位元組碼文件 ;
從zip , jar 等壓縮文件中載入位元組碼文件 : 在開發的時候 , 導入jar包 , 就是這種方式 ;
從專有的資料庫中提取位元組碼文件 ;
將java源文件動態編譯為位元組碼文件 ;

(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());
//自定義的類使用的是應用類載入器(系統載入器)
Class clazz2 = Class.forName("jvm.C");
System.out.println(clazz2.getClassLoader());
}
}
class C{}
執行結果 :
null
Java代碼
[email protected]
-擴展類載入器 ( Extension ) : Java編寫 ;
-系統類載入器(應用載入器) ( System ) : Java編寫 ;

用戶自定義的類載入器 :
-自定義的類載入器都是java.lang.ClassLoader子類 ;
-用戶可以定製類的載入方式

String類是由根類載入器進行載入的 , 我們可以調用Class對象的

關於代理中創建對象的類載入器 : 創建代理對象的時候 , 動態創建一個類 , 然後使用指定的類載入器將這個類載入到內存中 , 然後用載入到內存中的類生成代理對象 ;
創建代理對象的方法 : newProxyInstance(ClassLoader loader , Class [] Interfaces , InvocationHandler h )
loader 是定義的代理類的類載入器 , 中間的介面數組是代理類的要實現的介面列表 , h 是指派方法調用的調用處理程序 ;

類載入器並不需要在某個類被 「首次主動使用」 時再載入它 :
-預載入機制 : JVM規范允許類載入器在預料某個類將要被使用的時就預先載入它 ;
-報錯時機 : 如果在預載入的過程中遇到了位元組碼文件缺失或者存在錯誤的情況 , 類載入器會在程序首次主動使用(上面提到的六種情況)該類的時候報錯(LinkageError錯誤) ;
-不報錯時機 : 如果這個錯誤的位元組碼文件所對應的類一直沒有被使用 , 那麼類載入器就不會報告錯誤 ,即便有錯誤也不會報錯 ;

LinkageError : 這個錯誤是Error的子類 , 程序不能處理這些錯誤 , 這些錯誤都是由虛擬機來處理 , 這個錯誤表示出錯的是子類 , 在一定程序上依賴於另一個類 , 在編譯了前面一個類的時候 , 與後面所依賴的類出現了不兼容的情況 ;
例如 : 我們使用了jdk 1.6 在編譯一個程序 , 但是運行環境是jre1.5的 , 就會出現LinkageError錯誤 ;
4.類的連接
(1)定義
類被載入之後 , 就進入鏈接階段 ; 鏈接 : 將已讀入內存的二進制數據合並到虛擬機的運行時環境中去 ;
鏈接顧名思義就是講類與類之間進行關聯 , 例如我們在類A中調用了類B , 在鏈接過程中 , 就將A與B進行鏈接 ,將面向對象語言轉化為面向過程語言 ;

(2)類的驗證
類文件的結構檢查 : 確保類文件遵從java類文件的固定格式 , 開始類的描述 , 聲明 , 方法調用格式等 ;
語義檢查 : 確保類本身符合java語言的語法規定 , 比如final類型的類沒有子類 , final類型的方法沒有被覆蓋 ,在eclipse中這種錯誤編譯的時候不能通過 , 但是通過其他的方法可以生成錯誤的位元組碼文件 , 這里就是檢測惡意生成的位元組碼文件 ;
位元組碼驗證 : 確保位元組碼流可以被JVM安全的執行 , 位元組碼流代表java方法(包括靜態方法和實例方法) , 它是由被稱作操作碼的單位元組指令組成的序列 , 每一個操作碼後面跟著一個或多個操作數 , 位元組碼驗證步驟會檢查每個操作碼是否合法 , 即是否有著合法的操作數 ;
下面是指令碼組成的序列 , 類似於微指令 :

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 p
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 二進制兼容性的驗證 : 確保相互引用的類之間協調一致的 ; 例如在A類的a()方法中調用B類的b()方法 , JVM在驗證A類的時候 , 會驗證B類的b()方法 , 加入b()方法不存在 , 或者版本不兼容(A,B兩類使用不同的JDK版本編譯) , 會拋出NoSuchMethodError錯誤 ;

(3)准備階段
在准備階段 , JVM為類的靜態變數分配內存空間 , 並設置默認的初始值 . 例如下面的Sample類 , 在准備階段 ,為int類型的靜態變數分配4個位元組 , 並賦予初始值 0 ; 為long 類型的靜態變數 b , 分配8個位元組 , 並賦予初始值 0 ;
PS : 在java中基本類型變數佔用的空間是一定的 , java運行在JVM上的 , 在C中 , 就要根據平台變化而變化了 ;

public class Sample {
Java代碼
private static int a = 1 ;
private static long b ;
static {
b = 2 ;
}
(4)類的解析
在解析階段 , JVM 會把類的二進制數據中的符號引用替換為直接引用 , 例如在A類中的a()方法引用B類中的b()方法 ;
在A類的二進制數據中包含了一個對B類的b()方法的符號引用 , 這個符號引用由b()方法的全名和相關的描述符組成 , 在Java解析階段 , 就會把這個符號引用替換為指針 , 這個指針就是C語言中的指針了 , 該指針指向B類的b()方法在方法區中的內存位置 , 這個指針就是直接引用 ;

5.類的初始化
在初始化階段 , Java虛擬機執行類的初始化操作 , 為類的靜態變數賦予初始值 , 在程序中 , 靜態變數初始化有兩種途徑 :
直接在聲明處進行初始化 , 例如下面的Sample中的 變數a ;
在靜態代碼塊中進行初始化 , 例如下面的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 ;
}
}
執行結果 : 1 0

分析 : 這段代碼與類的鏈接中的准備階段 和 初始化階段 有關系 , 准備階段是給靜態的欄位賦予默認值 , 初始化階段給靜態變數賦予正確的值 , 即用戶的值 ;
在主函數中 , 調用了類的靜態方法 , 相當於主動使用 , 這里調用了類的靜態方法 ;
之後進行連接的准備操作 , 給類中的靜態變數賦予初值 , singleton值為null , count1 與 count2 值為0 ;
執行初始化操作 , 給類中的靜態變數賦予正確的值 , 給singleton變數賦予正確的值 , 調用構造方法 , 此時count1與 count2執行自增操作 , 兩者都變成1 , 然後執行count1的賦予正確值操作 , 這里用戶沒有賦值操作 , count2 用戶進行了 賦值為0的操作 , 0將原來的1覆蓋掉了 , 因此結果為 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 ;
}
}
執行結果 : 1 1

在准備階段count1 和 count2 都賦值為0 , 然後在初始化階段 , 全部賦值為1 ;

8. java中類載入器是怎麼工作的

JVM將類載入過程分為三個步驟:裝載(Load),鏈接(Link)和初始化(Initialize)
鏈接又分為三個步驟,驗證、准備、解析
1) 裝載:查找並載入類的二進制數據;
2)鏈接:
驗證:確保被載入類的正確性;
准備:為類的靜態變數分配內存,並將其初始化為默認值;
解析:把類中的符號引用轉換為直接引用;
3)初始化:為類的靜態變數賦予正確的初始值;
那為什麼我要有驗證這一步驟呢?首先如果由編譯器生成的class文件,它肯定是符合JVM位元組碼格式的,但是萬一有高手自己寫一個class文件,讓JVM載入並運行,用於惡意用途,就不妙了,因此這個class文件要先過驗證這一關,不符合的話不會讓它繼續執行的,也是為了安全考慮吧。
准備階段和初始化階段看似有點牟盾,其實是不牟盾的,如果類中有語句:private static int a = 10,它的執行過程是這樣的,首先位元組碼文件被載入到內存後,先進行鏈接的驗證這一步驟,驗證通過後准備階段,給a分配內存,因為變數a是static的,所以此時a等於int類型的默認初始值0,即a=0,然後到解析(後面在說),到初始化這一步驟時,才把a的真正的值10賦給a,此時

9. 描述一下JVM載入class文件的原理

Java語言是一種具有動態性的解釋型語言,類(class)只有被載入到JVM中後才能運行。當運行指定程序時,JVM會將編譯生成的.class文件按照需求和一定的規則載入到內存中,並組織成為一個完整的Java應用程序。這個載入過程是由類載入器來完成的,具體來說,就是由ClassLoader和它的子類來實現的。類載入器本身也是一個類,其實質是把類文件從硬碟讀取到內存中。
類的載入方式分為隱式載入與顯式載入兩種。隱式載入指的是程序在使用new等方法創建對象時,會隱式地調用類的載入器把對應的類載入到JVM中。顯式載入指的是通過直接調用class.forName()方法來把所需要的類載入到JVM中。
任何一個工程項目都是由許多個類組成的,當程序啟動時,只把需要載入的類載入到JVM中,其他類只有被使用到的時候才會被載入,採用這種方法,一方面可以加快載入速度,另外一方面可以節約程序運行過程中對內存的開銷。此外,在Java語言中,每個類或介面都對應一個.class文件,這些文件可以被看成一個個可以被動態載入的單元,因此當只有部分類被修改時,只需要重新編譯變化的類即可,而不需要重新編譯所有文件,因此加快了編譯速度。

熱點內容
上傳群文件視頻 發布:2022-07-02 12:50:36 瀏覽:755
我的世界基岩版如何卡爆伺服器 發布:2022-07-02 12:47:43 瀏覽:60
樂思維編程 發布:2022-07-02 12:42:54 瀏覽:912
通用編程軟體 發布:2022-07-02 12:42:38 瀏覽:366
安卓怎麼讀硬碟 發布:2022-07-02 12:42:35 瀏覽:357
本機硬碟可以和存儲混著用嗎 發布:2022-07-02 12:41:00 瀏覽:689
node編譯腳本 發布:2022-07-02 12:39:42 瀏覽:786
慈溪學編程 發布:2022-07-02 12:38:22 瀏覽:775
海康雲存儲解決方案 發布:2022-07-02 12:38:10 瀏覽:37
手游穿越火線安卓怎麼跟蘋果一起打 發布:2022-07-02 12:36:30 瀏覽:189