在对Java类型进行一些反思的过程中,我遇到了一个我不理解的奇怪现象。
检查int
的修饰符会返回public
,abstract
和final
。我理解public
和final
,但原始类型上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
答案 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 API,Integer.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)
抽象类是一个不完整的类,或者被认为不完整的类。 只有抽象类可能有抽象方法,即 声明但尚未实现的方法。
如果一个类的定义是完整的并且不需要子类,那么可以将它声明为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 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();
}
答案 5 :(得分:0)
为什么 Java 原始类型的修饰符是 public、abstract 和 final?
Class.getModifiers()
的 Java 11 javadoc 声明:
如果底层类是数组类,则其 public
、private
和 protected
修饰符与其组件类型的修饰符相同。如果此 Class
表示原始类型或 void
,则其 public
修饰符始终为 true,其 protected
和 private
修饰符始终为 false。如果此对象表示数组类、原始类型或 void
,则其 final
修饰符始终为 true,其 interface
修饰符始终为 false。 它的其他修饰符的值不是由这个规范决定的。
因此 javadoc 指定原始类型为 public
和 final
。这很直观。这将是这两种情况下选择的原因。
从技术上讲,原始类型的 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(),它必须是抽象的,但它将是公共的。 希望有所帮助。