Class類簡介


Class類

Java中所有的類、接口、枚舉、注解、數組、基本數據類型、void關鍵字,都有Class對象。通過Class對象可以得到類的完整結構,一個Class對象在jvm中只有一個實例。

image-20201015142716166

獲取類實例測試代碼

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類實際是一個泛型類。

image-20201015143706339

Class常用方法

image-20201015143918733

JAVA內存分析

image-20201015163057611

java類裝載過程分為3步:

image-20201015230405350

image-20201015155306516

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


免責聲明!

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



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