Java Class對象詳解


要怎樣在java里來使用一個類,首先必須先把類的.class字節碼文件加載進來,然后再進行連接對該類里的域分配內存,最后再調用構造器,如果該類有基類的話,會先去調用基類的構造器,總的來說,分為以下三個步驟。

1.根據環境變量找到並加載.class文件
2.為該類的非編譯時常量分配內存
3.調用該類的構造器

java里的所有類都有一個Class對象,通過這個Class對象我們能夠獲取此類的各種信息。
當某個字節碼文件被JVM加載的時候,Class對象就被創建。
Class類沒有構造方法,是內部的一個defineClass方法來創建此對象的,此對象與被加載的字節碼文件的類的類型相對應。

其實在java里包括基本數據類型(int short long byte float double boolean char),也包括了void

        System.out.println(int.class.getName());
		System.out.println(char.class.getName());
		System.out.println(short.class.getName());
		System.out.println(long.class.getName());
		System.out.println(byte.class.getName());
		System.out.println(float.class.getName());
		System.out.println(double.class.getName());
		System.out.println(boolean.class.getName());
		System.out.println(void.class.getName());

都有與之對應的class對象,同類型的類型也共享一個class對象。也包括了數組,所有同類型同維度的數組也共享一個class對象。

public class Main {
	public static void main(String[] args) {
		System.out.println(char[].class.getName());//[C
		System.out.println(char[][].class.getName());//[[C
	}
}

class的forName方法

同時class里有一個static的方法forName,可以讓我們顯示的來把一個類的.class文件加載至JVM虛擬機。

static Class< ? > forName(String className)

public class Main {
	public static void main(String[] args) throws Exception{
		Class a = Class.forName("A");
	}
}

class A{
	void print(){
		System.out.println("hello world");
	}
}

該方法返回的是一個Class對象,這個Class對象也可以添加泛型。
這樣的話我們就獲得一個與A類型對應的Class對象。
但是這時編譯器會強制的讓我們拋出或者捕獲這個異常,所以我們需要將它捕獲或者拋出。

接下來我們還能重載一個A類的private的構造器(注:默認的構造器是隱式的static,我們重載之后就不再是static),但是我們仍然能獲取它的class對象,因為調用構造器是在最后一步,而我們這里只是加載.class文件。

public class Main {
	public static void main(String[] args) throws ClassNotFoundException {
		Class a = Class.forName("A");
	}
}


class A{
	private A(){
		
	}
	void print(){
		System.out.println("hello world");
	}
}

這段代碼是毫無錯誤的

但是直到現在我們都還不能通過Class.forName來操作一個類,因為它只是簡單的加載而已,但是沒關系Class類還有一個newInstance方法,這個方法能幫助我們創建一個class類的實例,我們只需顯示的轉換一下類型即可操作A類.

public class Main {
	public static void main(String[] args) throws Exception {
		A a = (A)Class.forName("A").newInstance();
		a.print();
	}
}


class A{
	
	void print(){
		System.out.println("hello world");
	}
}

這時,其實我們得到的就是一個A類型的實例了。

但是如果我們這時把A的構造方法聲明為private呢?

public class Main {
	public static void main(String[] args) throws Exception {
		A a = (A)Class.forName("A").newInstance();
		a.print();
	}
}


class A{
	private A(){
		
	}
	void print(){
		System.out.println("hello world");
	}
}

編譯器是仍然不會報錯的,但是如果我們執行這段代碼就會發現會拋出一個異常,因為這是在運行時加載的,所以編譯器是無法察覺的。這也是相當危險的,所以一般情況下我們都會遵守用new來創建對象。

既然class是運行時對象,那么對於final static 聲明的域也是毫無作用的了,這么說的原因是用final static聲明的域是不需要動態的來分配內存的,因為它是一個編譯時常量。

到現在我們大概明白了class的含義和運用,那它和.class有什么聯系呢。
其實每個類也有一個.class的常量,我們稱為類字面常量,這個常量能夠返回該類的class對象,也能通過newIstance創建實例來操作。

運用類字面常量的好處就在於不用去拋出或者捕獲異常,所犯的錯誤在編譯時就能查找出來。

public class Main {
	public static void main(String[] args) throws Exception {
		A a = (A)A.class.newInstance();
		a.print();
	}
}


class A{
	void print(){
		System.out.println("hello world");
	}
}

封裝類的TYPE

這里以boolean類型來說明這個問題

System.out.println(boolean.class == Boolean.TYPE);//true

所以我們得出基本類型的.class 和 封裝類的TYPE是等價的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM