Class類
Java中所有的類、接口、枚舉、注解、數組、基本數據類型、void關鍵字,都有Class對象。通過Class對象可以得到類的完整結構,一個Class對象在jvm中只有一個實例。
獲取類實例測試代碼
package com.kuang;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("這個人是 " + person.name);
//方式一 通過getClass()獲取類
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二 通過forName獲取類 可以獲取到靜態的類
Class c2 = Class.forName("com.kuang.Student");
System.out.println(c2.hashCode());
//方式三 通過類名.class獲得
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());
//方式4 基本內置類型的 `包裝類` 都有一個Type屬性 通過該屬性獲得
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
// 獲取父類的類型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person {
public String name;
public Person() {}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person {
public Student() {
this.name = "學生";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "老師";
}
}
package com.kuang;
//哪些類有class對象
import java.lang.annotation.ElementType;
public class Test2 {
public static void main(String[] args) {
Class c1 = Object.class; //類
Class c2 = Comparable.class;//接口
Class c3 = String[].class;//一維數組
Class c4 = int[][].class;//二位數據
Class c5 = Override.class;//注解
Class c6 = ElementType.class;//枚舉
Class c7 = Integer.class;//基本類型的包裝類
Class c8 = void.class;//void
Class c9 = Class.class;///Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//輸出結果 注意數組的輸出結果有點特殊
// class java.lang.Object
// interface java.lang.Comparable
// class [Ljava.lang.String;
// class [[I
// interface java.lang.Override
// class java.lang.annotation.ElementType
// class java.lang.Integer
// void
// class java.lang.Class
}
}
Class類實際是一個泛型類。
Class常用方法
JAVA內存分析
java類裝載過程分為3步:
1、加載
Jvm把class文件字節碼加載到內存中,並將這些靜態數據裝換成運行時數據區中方法區的類型數據,在運行時數據區堆中生成一個代表這個類的java.lang.Class對象,作為方法區類數據的訪問入口。
注:方法區不僅僅是存放方法,它還存放的是類的類型信息(class對象)。
2、鏈接
執行下面的校驗、准備和解析步驟,其中解析步驟是可選的
-
校驗:檢查加載的class文件的正確性和安全性
-
准備:為類變量(static)分配存儲空間並設置類變量初始值(變量類型的默認值),類變量隨類型信息存放在方法區中,生命周期很長,使用不當很容易造成內存泄漏。
-
解析:jvm將常量池內的符號引用(常量名)轉換為直接引用(地址)
3、初始化
執行類變量賦值和靜態代碼塊
Class.forName和ClassLoader的區別?
在了解了類裝載過程之后我們繼續比較二者區別:
Classloder.loaderClass(String name)
其實該方法內部調用的是:Classloder. loadClass(name, false)
方法:Classloder. loadClass(String name, boolean resolve)
a:參數name代表類的全限定類名
b:參數resolve代表是否解析,resolve為true是解析該類
Class.forName(String name)
其實該方法內部調用的是:Class.forName(className, true, ClassLoader.getClassLoader(caller))
方法:Class.forName0(String name, boolean initialize, ClassLoader loader)
參數name代表全限定類名
參數initialize表示是否初始化該類,為true是初始化該類
參數loader 對應的類加載器
兩者最大的區別
- Class.forName除了將類的.class文件加載到jvm中之外,還會對類進行解釋,執行類中的static塊。也會加載靜態方法。
- classloader只干一件事情,就是將.class文件加載到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。
- Class.forName(name,initialize,loader)帶參數也可控制是否加載static塊。並且只有調用了newInstance()方法才執行構造函數,創建類的對象。
測試代碼
package com.kuang;
public class ClassloaderAndForNameTest {
public static void main(String[] args) {
String wholeNameLine = "com.kuang.Line";
String wholeNamePoint = "com.kuang.Point";
System.out.println("下面是測試Classloader的效果");
testClassloader(wholeNameLine, wholeNamePoint);
System.out.println();
System.out.println("下面是測試Class.forName的效果");
testForName(wholeNameLine, wholeNamePoint);
}
/**
* classloader
* @param wholeNameLine
* @param wholeNamePoint
*/
private static void testClassloader(String wholeNameLine, String wholeNamePoint) {
Class<?> line;
Class<?> point;
ClassLoader loader = ClassLoader.getSystemClassLoader();
try {
line = loader.loadClass(wholeNameLine);
point = loader.loadClass(wholeNamePoint);
System.out.println("line " + line.getName());
System.out.println("point " + point.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Class.forName
* @param wholeNameLine
* @param wholeNamePoint
*/
private static void testForName(String wholeNameLine, String wholeNamePoint) {
try {
Class<?> line = Class.forName(wholeNameLine);
Class<?> point = Class.forName(wholeNamePoint);
System.out.println("line " + line.getName());
System.out.println("point " + point.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Point {
static {
System.out.println("靜態代碼塊執行: loading point");
}
public static String s = getString();
private static String getString() {
System.out.println("靜態方法執行給靜態變量賦值:loading point");
return "mask";
}
public static void test() {
System.out.println("普通靜態方法執行:loading point");
}
{
System.out.println("point普通代碼塊");
}
public Point() {
System.out.println("point構造方法執行");
}
}
class Line {
static {
System.out.println("靜態代碼塊執行: loading line");
}
public static String s = getString();
private static String getString() {
System.out.println("給靜態變量賦值的靜態方法執行:loading line");
return "mask";
}
public static void test() {
System.out.println("普通靜態方法執行:loading line");
}
{
System.out.println("普通代碼塊");
}
public Line() {
System.out.println("構造方法執行");
}
}
//輸出結果:
//下面是測試Classloader的效果
//line com.kuang.Line
//point com.kuang.Point
//
//下面是測試Class.forName的效果
//靜態代碼塊執行: loading line
//給靜態變量賦值的靜態方法執行:loading line
//靜態代碼塊執行: loading point
//靜態方法執行給靜態變量賦值:loading point
//line com.kuang.Line
//point com.kuang.Point