Java——それは本当に「純粋なオブジェクト指向」なのでしょうか? Java の世界を詳しく調べて、それを証明してみましょう。
私が初めて Java を学び始めた最初の数年間、Java は「オブジェクト指向プログラミング パラダイム」に従っていることを本で学びました。 Java の世界ではすべてがオブジェクトであり、文字列 (String) もオブジェクトです (C 言語では文字列は文字配列です)。当時、私は Java をオブジェクト指向言語だと思っていました。
しかしその後、インターネット上で多くの開発者が「Java の世界のすべてがオブジェクトであるわけではないため、Java は実際には純粋なオブジェクト指向ではない」と言っているのを目にしました。彼らの議論の多くは、次の 2 つの点に要約できます:
すべての静的コンテンツ (静的キーによって変更される変数とメソッド) はどのオブジェクトにも属さないため、これらは非オブジェクトのものです。
すべての基本型 (char、boolean、byte、short、int、long、float、double) はオブジェクトではありません。これは、通常のオブジェクトと同様の操作 (例: オブジェクトのプロパティにアクセスするには「.」を使用し、方法)。
当時、私は個人的な知識や経験が乏しいため、上記の主張を簡単に信じてしまい、また「Java は純粋なオブジェクト指向プログラミング言語ではない」と考えるようになりました。
その後、JVM の学習プロセス中に、新しい発見をしました。
JVM は、オブジェクトを作成するとき、実際に 2 つのオブジェクトを作成します:
1 つはインスタンス オブジェクトです。
もう 1 つは Class オブジェクトです。 Class オブジェクトは JVM に 1 回だけロードされます。クラスの静的メソッドと静的プロパティも一緒にロードされます。JVM は、Class オブジェクトを使用して特定のインスタンス オブジェクト (上記のオブジェクトなど) を作成します。
たとえば、次の Java ステートメントでは、2 つのオブジェクトが作成されます:
Employee emp = new Employee();
1 つはインスタンス オブジェクト emp、もう 1 つは Class オブジェクトで、この Class オブジェクトは Employee.class を通じて参照できます。このクラスによって定義されたすべての静的変数と静的メソッドと同時に、emp オブジェクトを介して静的コンテンツにアクセスすると、それが実際に指しているオブジェクトが Employee.class であることがわかります。
これにより、別の謎も明らかになります。両方のオブジェクトの変更は、同じオブジェクト内の Employee.class コンテンツにあるため、なぜ 1 つのオブジェクト (emp か emp2) で静的コンテンツが変更され、同時に別のオブジェクトでも変更されるのかということです。
さて、上記の第一引数を取り消します。なぜなら、静的コンテンツは確かにオブジェクトに属していることが確認されているからです。
しかし、2 番目の引数も確認したいと思います。前述したように、プリミティブ型は Java のオブジェクトではなく、オブジェクトのような操作を行うことはできません。この問題を解決するために、Java は各プリミティブ型に対応するパッケージング クラスを正式に開始しました (例: Integer は int に対応し、Long は long に対応し、Character は char に対応します)。そのため、実際にパッケージングを作成できるようになりました。プリミティブ型のオブジェクトを作成すると同時に、それらに対してオブジェクト関連の操作を実行します。また、自動アンボックス化のおかげで、プリミティブ型の値を対応するラッパー クラスへの参照に割り当てることができます。しかし、これらのプリミティブ型に対してオブジェクト操作を行うことはまだできません。対応するラッパー クラスのオブジェクトを作成する必要があります。
例:
Integer obj = new Integer(5); // here we can do i.toString(); int i = 5; // but we can't do i.toString() here
これまでのところ、エンドユーザーの観点からは、「元のカテゴリはオブジェクトではない」ことが確認できます。 (Java 開発者は、Java を作成するのではなく使用するため、Java のエンド ユーザーになります)。
JVM の観点から見ると、新しい発見があるでしょう:
実際、JVM の観点から見ると、JVM はすべての「プリミティブ型」をオブジェクトとして扱います。これは、Class クラスのソース コードを通じて証明できます。 Javadoc での Class クラスの説明
java.lang.Class クラスのソース コードによると、このクラスの注釈は次のとおりです:
Java 公式説明:
クラス Class のインスタンスはクラスとインターフェイスを表します。実行中の Java アプリケーションでは、列挙型はクラスの一種であり、アノテーションはインターフェイスの一種であり、同じ要素の型と数を持つすべての配列によって共有される Class オブジェクトとして反映されます。ディメンション。プリミティブ Java 型 (boolean、byte、char、short、int、long、float、double)、およびキーワード void も Class オブジェクトとして表されます。
参考翻訳:
Class类的实例表示正在运行的Java应用程序的类和接口。像枚举是一种类和注解则是一种接口。每个数组也属于被反射作为由具有相同的元素类型和尺寸的数目的所有阵列共享一类对象的类。原始的Java类型(boolean, byte, char, short, int, long, float, and double)和关键字void也表示为Class对象。
同时也根据Javadoc中对Class.isPrimitive()方法的定义,来判断
Java官方描述:
public boolean isPrimitive()
Determines if the specified Class object represents a primitive type.
There are nine predefined Class objects to represent the eight primitive types and void. These are created by the Java Virtual Machine, and have the same names as t he primitive types that they represent, namely boolean,byte, char, short, int, long, float, and double.
These objects may only be accessed via the following public static final variables, and are the only Class objects for which this method returns true.
Returns:
true if and only if this class represents a primitive type
Since:
JDK1.1
参考翻译:
public boolean isPrimitive()
判断指定的Class对象是否代表一个基本类型。
一共有9种设定好的Class对象来表示对应的基本类型和void关键字。这些对象都是由JVM创建的。…
return当且仅当该类表示一个真正的基本类型
以上都说明,在JVM内部,其实原始类型就是对象。
当你打开 Javadoc 对 Class 类的定义中,通过 “CTRL+F ” 查找关键字 “primitive”, 将会发现证据在表面 “在JVM里,它把基本类型当作对象来处理的”。
我们可以再来看一个例子: Integer.TYPE,在这部分文档清晰记录着:
Java官方描述:
public static final Class
TYPE
The Class instance representing the primitive type int.
以上都说明,在JVM内部,其实原始类型就是对象。
那么,既然说 “JVM”会为所有的基本类型创建一个对象,那我们为什么还那么常用 “原始类型”, 而不是直接使用对应的包装类对象呢?
这是因为,为 “原始类型” 创建的对象,在JVM内部是很轻量级的,相对与我们直接创建的对应包装类对象做了许多优化; 也正因为轻量的缘故,这些原始类的功能就比较少(例如我们不能调用其内部的方法,因为他们内部已经优化成没有方法了)
使用实际的例子来说明,为什么我们更应该使用 “原始类型”:
“原始类型”有更快的速度(例如,下面的代码执行,在我们的机器上需要9秒,但当我把 Long 改成 long 之后,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” 。这个是因在 [-128; 127] 这个区间的265个整数会被 JVM 缓存存放, 所以在这个区间, JVM返回相同的对象;然而,超出这个区间, JVM就不再有缓存了,将会创建新的对象,所以结果是不等的。
所以总结一下是: 在JVM内部,原始类型就是被当作对象来处理的。但是我们开发者直接把 “原始类型” 当作对象使用,开发者应该使用对应的包装来。
以上がJava が純粋なオブジェクト指向言語であるかどうかを検証するための詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。