最近看到一篇外文,阐释了为什么java不是100%的面向对象,外文翻译如下:
几年前,当我开始学习Java时,我知道Java遵循 面向对象的编程范例,Java中的所有东西都是一个对象 - 一个String(在C中是一个char数组)或一个数组本身。
但后来我发现人们在互联网上说Java实际上不是纯粹的面向对象,因为Java中的所有内容都不是对象; 例如:
- 所有基本类型(char,boolean,byte,short,int,long,float,double)都不是对象,因为我们无法对它们执行任何类似对象的操作(使用“。”和调用方法)。
- 我还发现有些人说所有静态内容(变量和方法)都不属于任何对象,因此它们是非对象的东西。
由于我的知识匮乏和经验不足,我很容易接受这些原因并开始相信Java不是纯粹的面向对象的编程语言。
但后来我发现JVM为每个对象创建了两个对象:
- 对象本身。
- 还有一个Class级别对象(由ClassName.class语法引用),只有在Classloader将类加载到内存时才会创建一次。该类的所有静态内容都属于此Class对象,该类的所有其他对象都引用所有静态内容的此类级对象。
例如,在以下语句中,将创建两个对象:
Employee emp = new Employee();
一个是emp本身,另一个是employee类的类级对象,我们可以通过Employee.class引用它。此类级别对象包含Employee类的所有静态内容,它是变量或方法。如果我们通过emp对象访问任何静态内容,它指向Employee.class对象来访问它。
这就是为什么静态变量为每个对象更改的原因,即使我们为单个emp对象更改它,因为所有emp对象都指向Employee.class对象中该变量的相同副本。
现在第二个点被取消,因为静态内容属于一个对象。但第一点仍然存在,我们仍然在Java中有原始数据类型,它们不是对象。
如前所述,原始类型不是对象,因为我们无法对它们执行任何与对象相关的功能。为了克服这个问题,Java为每个基本类型引入了Wrapper类(例如,Integer用于int,Long用于long,Character用于char)。现在我们可以为基本类型创建对象,并对它们执行所有与对象相关的操作。
由于自动装箱(自动拆箱装箱,装箱拆箱),我们可以直接将原始文字分配给其Wrapper类参考。但是我们仍然无法对原始变量执行这些操作 - 我们总是需要创建相应Wrapper类的对象。
例如:
整数 obj = new Integer(5); //这里我们可以做i.toString();
int i = 5 ; //但我们不能在这里做i.toString()
到目前为止,很明显原始类型不是对象,但实际上这是最终用户的观点(Java开发人员是Java的最终用户,因为我们正在使用它,而不是创建它)。
JVM在内部将所有基本类型视为对象,并且可以在源代码或类Class的Javadoc中找到对此的证明。根据类Class的源代码:
类Class的实例表示正在运行的Java应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组也属于一个类,它反映为一个Class对象,由具有相同元素类型和维数的所有数组共享。原始Java类型(boolean,byte,char,short,int,long,float和double)和关键字void也表示为Class对象。
和Class.isPrimitive()方法的Javadoc代码:
public boolean isPrimitive()
确定指定的Class对象是否表示基本类型。
有九个预定义的Class对象来表示八种基本类型和void。它们由Java虚拟机创建,并且与它们表示的基本类型具有相同的名称,即boolean,byte,char,short,int,long,float和double。
这些对象只能通过以下公共静态final变量访问,并且是此方法返回true的唯一Class对象。
返回:
当且仅当此类表示基本类型时返回 true
自:
JDK1.1
参见:
Boolean.TYPE, Character.TYPE, 字节.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
如果打开类Class的Javadoc 并对单词“primitive”执行Ctrl + F,您会发现很多理由认为JVM在内部将所有原始类型视为对象。
让我们打开上面的 Integer.TYPE条目。在本节中,您将找到:
public static final Class < Integer > TYPE
表示基本类型int的Class实例。
如果您使用Eclipse在程序中编写以下行:
Integer.TYPE i = 5;
您将收到编译错误,指出无法将Integer.TYPE解析为带有Eclipse提示的类型,以将其更改为int。
我们为什么要使用原始类型
如果JVM为所有基本类型创建对象,为什么我们需要使用基本类型而不是使用相应的Wrapper类创建对象?这是因为JVM在内部为原始类型创建了这些本机对象,并且这些对象非常轻量级,并且比它们各自的包装类对象更优化; 因此他们的功能较少(例如我们不能在他们身上调用方法,因为他们没有任何功能)。
我们应该使用原始类型:
- 因为它们很快(例如,以下程序需要9秒才能在我的机器上运行,而如果我将Long sum转换为长总数需要0秒...如果这表明我们使用原语的任何指示)。
public static void main(String[] args) {
long millis = System.currentTimeMillis();
Long sum = 0L; // uses Long, not long
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
System.out.println((System.currentTimeMillis() - millis) / 1000);
}
- 它们允许我们使用本机相等运算符“==”:
new Integer(3) == new Integer(3); // false
new Integer(100) == new Integer(100); // false
Integer.valueOf(5) == Integer.valueOf(5); //true
Integer.valueOf(200) == Integer.valueOf(200); //false
这里的第四个语句输出false,因为256个整数最接近零[-128; 缓存由JVM缓存,因此它们返回相同的对象。但是,超出该范围,它们不会被缓存,因此会创建一个新对象。
所以我们可以说JVM在内部将所有原始类型视为对象,但我们不能以这种方式使用它们; 相反,我们有Wrapper类。
原文:https://dzone.com/articles/why-java-is-purely-object-oriented-language-or-why-1