在对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
根据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:
如果基础类是数组类,则其
public
,private
和protected
修饰符与其组件类型的修饰符相同。如果此Class
代表原始类型或void
,则其public
修饰符始终是正确的,并且其protected
和private
修饰符始终是错误的。如果此对象代表数组类,原始类型或void
,则其final
修饰符始终是正确的,并且其interface
修饰符始终是错误的。其其他修饰符的值未通过此规范确定。
因此,Javadoc指定原始类型为public
和final
。这是直观的。这将是这两种情况下选择的原因。
从技术上讲,原始类型上的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(),但是它将是公开的。希望会有所帮助。