为什么Java原始类型的修饰符`public`,`abstract`,& `final`?

时间:2012-11-01 15:46:30

标签: java reflection

在对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

7 个答案:

答案 0 :(得分:6)

根据JLS 8.1.1.1 - Abstract Classes

  

抽象类是一个不完整或被认为不完整的类。

根据定义,可能没有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类型,这让我觉得这主要用于Reflection。不过,这只是猜想。


如果有人感兴趣,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());
    }
}

答案 1 :(得分:5)

如果你跑

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

你得到了

public abstract final

可能是因为你不能对它进行分类 - 即最终,并且你无法实例化它 - 即抽象。

来自Oracle的Abstract Methods and Classes

  

抽象类无法实例化,但它们可以是子类。

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

答案 2 :(得分:2)

来自JVM规范:

  

抽象类是一个不完整的类,或者被认为不完整的类。   只有抽象类可能有抽象方法,即   声明但尚未实现的方法。

     

如果一个类的定义是完整的并且不需要子类,那么可以将它声明为final。需要。因为最后一堂课从来没有   任何子类,最终类的方法都不能在a中重写   子类。一个类不能是最终的和抽象的,因为   这类课程的实施永远无法完成。

根据specifications,一个班不能同时是抽象的和最终的。 然而,似乎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的原因是因为,它们被认为不完整。

答案 3 :(得分:1)

int.class也可以Integer.TYPE访问,并且是表示基本类型int的Class实例。

鉴于您无法实例化此类的对象(因为int不是实例,它们是基本类型),我认为将类型标记为abstract是有意义的。

目前我无法在JLS中找到对此的引用。

答案 4 :(得分:0)

也许这只是primitive classnormal 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();
}

答案 5 :(得分:0)

<块引用>

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

Class.getModifiers() 的 Java 11 javadoc 声明:

<块引用>

如果底层类是数组类,则其 publicprivateprotected 修饰符与其组件类型的修饰符相同。如果此 Class 表示原始类型或 void,则其 public 修饰符始终为 true,其 protectedprivate 修饰符始终为 false。如果此对象表示数组类、原始类型或 void,则其 final 修饰符始终为 true,其 interface 修饰符始终为 false。 它的其他修饰符的值不是由这个规范决定的

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

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

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

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

所以底线是:

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

1 - 在我看来,它更好,因为对于 Class API 的用户来说,确定性行为比非确定性行为更容易。而且以这种方式实现它很可能更简单。
2 - 我们很可能永远不会确切知道为什么做出特定选择。也许有人扔了一枚硬币?没关系。

答案 6 :(得分:-1)

如果您反映到从抽象类继承的类,然后尝试深入了解该实例的基类,则在抽象类中定义为抽象的属性将没有值,但它是该类的一部分定义。

以下是一个例子:

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,那么如果您查看concreteClass.GetID(),它必须是抽象的,但它将是公共的。 希望有所帮助。