簡體   English   中英

為什么 Java 原始類型的修飾符是 public、abstract 和 final?

[英]Why are Java primitive types' modifiers public, abstract, and final?

在對Java類型做一些反思的過程中,我遇到了一個我不明白的怪事。

檢查int的修飾符會返回publicabstractfinal 我理解publicfinal ,但是基本類型上的abstract對我來說並不明顯。 為什么會這樣?

編輯:我不是在思考Integer而是在int

import java.lang.reflect.Modifier;

public class IntegerReflection {
    public static void main(final String[] args) {
        System.out.println(String.format("int.class == Integer.class -> %b", int.class == Integer.class));
        System.out.println(String.format("int.class modifiers: %s", Modifier.toString(int.class.getModifiers())));
        System.out.println(String.format("Integer.class modifiers: %s", Modifier.toString(Integer.class.getModifiers())));
    }
}

運行時的輸出:

int.class == Integer.class -> false
int.class modifiers: public abstract final
Integer.class modifiers: public final

根據JLS 8.1.1.1 - 抽象類

抽象類是不完整或被認為不完整的類。

根據定義,不能有int.class實例。 你不能編譯這種代碼:

int a = new int();

int沒有構造函數。 沒有創建對象。 int.class甚至不擴展Object 如果您運行以下代碼行,您將獲得null作為結果。

System.out.println(int.class.getSuperclass());

所以因為你永遠不可能有一個真正的int.class實例,它被定義為abstract 此外,根據Integer APIInteger.TYPE字段(包含int.class )是一個僅表示原始類型的類。

下面的代碼證明了這一點:

int a = 4;
System.out.println(int.class.isInstance(a));

這將返回false

因此, int.class可能只是在系統中用於表示目的,如Integer API 中所述。 還有一個void.class類型但沒有null.class類型這一事實讓我認為這主要用於反射。 不過,這只是猜測。


如果有人感興趣, int.class基本上不包含反射包識別的任何內容,並且可能只是一個虛擬類。 如果運行以下代碼,您將看到它沒有構造函數、字段和方法。

Method[] intMethods = int.class.getMethods();

if(intMethods.length == 0) {
    System.out.println("No methods.");
}
else {
    for(Method method : intMethods) {
        System.out.println(method.getName());
    }
}

Constructor[] intConstructors = int.class.getConstructors();

if(intConstructors.length == 0) {
    System.out.println("No constructors.");
}
else {
    for(Constructor constructor: intConstructors) {
        System.out.println(constructor.getName());
    }
}

Field[] intFields = int.class.getFields();

if(intFields.length == 0) {
    System.out.println("No fields.");
}
else {
    for(Field field: intFields) {
        System.out.println(field.getName());
    }
}

如果你跑

System.out.println(Modifier.toString(int.class.getModifiers()));

你得到

public abstract final

可能是因為你不能對它進行子類化——即最終的,並且你不能實例化它——即抽象的。

來自 Oracle 的抽象方法和類

抽象類不能被實例化,但它們可以被子類化。

事實是它也最終意味着它不能是子類。

來自 JVM 規范:

抽象類是不完整或被認為不完整的類。 只有抽象類才可能有抽象方法,即聲明了但尚未實現的方法。

如果一個類的定義是完整的並且不需要或不需要子類,則可以將其聲明為 final。 因為 final 類從來沒有任何子類,所以不能在子類中覆蓋 final 類的方法。 一個類不能既是最終的又是抽象的,因為這樣一個類的實現永遠不可能完成。

根據規范,一個類不能同時是 Abstract 和 Final。 但是,JVM 似乎沒有將原始類型視為類,這在技術上是正確的,因為原始類型不是類並且由 JVM 提供給語言運行時(使用Class getPrimitiveClass(const char *name) )。

所以int和其他所有原始類型,

> a. Should be accessible from within the language: Make it `public` 
> b. Should not be extensible                     : Make it `final` 
> c. Should not be instantiated with `new`        : Make it `abstract`.

我從 JVM 規范中得出的關於原始類型為什么是abstract是因為,它們被認為是不完整的。

int.class也可以作為Integer.TYPE訪問,並且是表示原始類型int的 Class 實例。

鑒於您無法實例化此類的對象(因為 int 不是實例,它們是原始類型),我認為將類型標記為abstract是有意義的。

目前我在JLS 中找不到對它的引用。

也許這只是primitive class不同於normal class的特殊標記。

JVM代碼實現:

mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr<mirror::Class> primitive_class,
                                                     Primitive::Type type) {

  Handle<mirror::Class> h_class(hs.NewHandle(primitive_class));
  // public final static
  h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
  h_class->SetPrimitiveType(type);
  h_class->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());

  // ...
  return h_class.Get();
}

為什么 Java 原始類型的修飾符是 public、abstract 和 final?

Class.getModifiers()的 Java 11 javadoc指出:

如果底層類是數組類,則其publicprivateprotected修飾符與其組件類型的修飾符相同。 如果這個Class表示一個基本類型或void ,其public修飾符始終是真實的,它的protectedprivate修飾符始終為false。 如果此對象表示數組類、原始類型或void ,則其final修飾符始終為 true,其interface修飾符始終為 false。 它的其他修飾符的值不是由本規范確定的

因此 javadoc 指定原始類型是publicfinal 這很直觀。 這將是在這兩種情況下選擇的原因。

從技術上講,對原始類型進行abstract是沒有意義的。 原始類型不是類,不能使用new實例化。 然而,考慮到其他 Java API 的工作方式(例如反射方法查找),有必要擁有代表所有 Java 類型的Class對象。 因此, Integer.TYPE == int.class和類似的需要存在。

無論如何,無論出於何種原因,規范對代表原始類型的Classabstract修飾符說“未指定”。

但是, Class修飾符表示為整數中的位,JVM 實現必須返回該整數中“抽象”位位置的值。 雖然根據規范,這兩種選擇(1 或 0)都沒有任何意義,但對於一位 Java 工程師(可能早在 1990 年代)來說,在原始類型情況下為位選擇2特定值比 / 更好1 (比如說)在運行時隨機返回 1 或 0。

所以底線是:

  • abstract的選擇是(或可能是)完全任意的,所以不要賦予它任何意義。
  • 如果您依賴當前的abstract行為,那么理論上存在您的代碼將在 Java 的未來版本中崩潰的風險……如果他們改變了行為。

1 - 在我看來,它更好,因為對於Class API 的用戶來說,確定性行為比非確定性行為更容易。 以這種方式實現它很可能更簡單。
2 - 我們很可能永遠不會確切知道為什么做出特定選擇。 也許有人扔了一枚硬幣? 沒關系。

如果您反映到從抽象類繼承的類中,然后嘗試挖掘該實例的基類,則在抽象類中定義為抽象的屬性將沒有價值,但它是類定義的一部分。

這是一個例子:

public class absBase{

    private string name;
    private int ID;

    public abstract int GetID();
}

public class concreteClass:absBase{
    @Override
    public int GetID(){
        return ID + 1;
    }
}

因此,如果您正在反映 absBase,並查看 GetID,那么根據定義,它必須是抽象的,如果您查看creteClass.GetID(),但它是公開的。 希望有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM