为什么 Java 基元类型的修饰符是公共的、抽象的和最终的?



在对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。另外,根据整数API,Integer.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规格:

抽象类是不完整的类,或者被考虑到不完整。 只有抽象类可能具有抽象方法,也就是说 声明但尚未实施的方法。

如果其定义完成并且不需要或不需要子类,则可以声明一类最终。因为最后一堂课从来没有 任何子类,最后类的方法都不能在 子类。班级不能既是最终又是抽象的,因为 实施此类课程永远无法完成。

根据规格,类不能既是抽象又是最终的。但是但是,JVM似乎并不将原始类型视为类,从技术上讲是正确的,因为原始类型是不是类,并且由JVM提供给语言运行时(使用Class getPrimitiveClass(const char *name))。

SO 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的类实例。

鉴于您无法实例化此类的对象(因为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原始类型的修饰符公开,摘要和最终?

Class.getModifiers()状态的Java 11 Javadoc:

如果基础类是数组类,则其publicprivateprotected修饰符与其组件类型的修饰符相同。如果此Class代表原始类型或void,则其public修饰符始终是正确的,并且其protectedprivate修饰符始终是错误的。如果此对象代表数组类,原始类型或void,则其final修饰符始终是正确的,并且其interface修饰符始终是错误的。其其他修饰符的值未通过此规范确定

因此,Javadoc指定原始类型为publicfinal。这是直观的。这将是这两种情况下选择的原因。

从技术上讲,原始类型上的abstract没有意义。原始类型不是类,使用new不能立即。但是,鉴于其他Java API的工作方式(例如反射方法查找),必须具有代表所有Java类型的Class对象。因此, Integer.TYPE == int.class且需要存在类似的需要。

无论如何,无论出于何种原因,规格都在说"未指定"。对于代表原始类型的abstract的CC_53修饰符。

但是,Class修饰符在整数中表示为位,JVM实现必须返回"摘要"的值。位置在该整数中。虽然没有选择(1或0)根据规格有任何意义,但对于一位Java工程师(可能回到1990年代)来说,它是/更好的 1 ,可以选择 2 原始类型中位的特定值,而不是(例如)在运行时随机返回1或0。

所以最重要的是:

  • abstract的选择是(或可能是)是完全任意的,所以不要归因于此。
  • 如果您依靠abstract的当前行为,但是,存在A 理论风险,您的代码将在将来的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;
    }
}

因此,如果您正在反映脓肿,并查看了GetID,则必须从定义上进行抽象,如果您查看Concreteclass.getID(),但是它将是公开的。希望会有所帮助。

最新更新