22

在对 Java 类型进行一些反思的过程中,我遇到了一个我不理解的奇怪现象。

检查int其修饰符返回public,abstractfinal. 我理解publicand final,但是abstracton 原始类型的存在对我来说并不明显。为什么会这样?

编辑:我不是在反思,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
4

7 回答 7

7

根据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

因此,如APIint.class中所述,可能只是在系统中用于表示目的。Integer也有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());
    }
}
于 2012-11-01T16:08:53.877 回答
5

如果你跑

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

你得到

public abstract final

可能是因为你不能对它进行子类化——即最终的,你不能实例化它——即抽象的。

来自 Oracle 的抽象方法和类

抽象类不能被实例化,但它们可以被子类化。

事实上,它也是最终的意味着它不能是子类。

于 2012-11-01T15:51:42.003 回答
3

来自 JVM 规范:

抽象类是不完整的或被认为不完整的类。只有抽象类可以有抽象方法,也就是已经声明但还没有实现的方法。

如果一个类的定义是完整的,并且不需要或 > 不需要子类,则可以将其声明为最终类。因为 final 类永远不会有任何子类,所以 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是因为它们被认为是不完整的。

于 2012-11-01T19:22:39.647 回答
1

int.class也可以作为Integer.TYPE表示原始类型的 Class 实例来访问int

鉴于您无法实例化此类的对象(因为 int 不是实例,它们是原始类型),我认为将类型标记为abstract.

目前我在JLS中找不到对此的参考。

于 2012-11-01T16:11:17.073 回答
0

也许这只是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();
}
于 2019-07-17T07:11:24.940 回答
0

为什么 Java 原始类型的修饰符是 public、abstract 和 final?

用于状态的 Java 11 javadoc :Class.getModifiers()

如果底层类是一个数组类,那么它的public,privateprotected修饰符与其组件类型的修饰符相同。如果 thisClass表示原始类型 or void,则其public修饰符始终为真,其protectedprivate修饰符始终为假。如果此对象表示数组类、原始类型或void,则其final修饰符始终为真,其interface修饰符始终为假。它的其他修饰符的值不是由本规范确定的

所以 javadoc 指定原始类型是publicand final。这很直观。这将是在这两种情况下进行选择的原因。

从技术上讲,abstract对原始类型没有任何意义。原始类型不是类,并且不能使用new. 然而,考虑到其他 Java API 的工作方式(例如反射方法查找),有必要拥有Class代表所有 Java 类型的对象。因此,Integer.TYPE==int.class和类似的需要存在。

无论如何,无论出于何种原因,规范对表示原始类型的abstracta 上的修饰符说“未指定” 。Class

但是,Class修饰符表示为整数中的位,并且 JVM 实现必须为该整数中的“抽象”位位置返回一个值。虽然根据规范,这两种选择(1 或 0)都没有任何意义,但对于Java 工程师(可能早在 1990 年代)来说,在原始类型的情况下为位选择2的特定值比 1 更好(比如说)在运行时随机返回 1 或 0。

所以底线是:

  • 的选择abstract是(或可能)完全是任意的,所以不要赋予它任何意义。
  • 如果您依赖当前的行为abstract,那么理论上存在您的代码将在 Java 的未来版本中中断的风险......如果他们改变了行为。

1 - 在我看来,这更好,因为ClassAPI 的用户比非确定性行为更容易具有确定性行为。而且以这种方式实现它很可能更简单。
2 - 我们很可能永远无法确定做出具体选择的原因。也许有人扔了硬币?没关系。

于 2021-06-12T02:23:47.503 回答
-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,则它必须是抽象的,如果您查看的是具体的 Class.GetID(),但它将是公共的。希望有帮助。

于 2012-11-01T15:51:02.290 回答