要怎樣在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是等價的。