array.length
,而对于字符串,我们则使用了一个方法str.length()
?这样做有什么原因吗?
首先,让我强调三种相似的目的的不同方法。
length
-- 数组 (int[]
, double[]
, String[]
) -- 用于获取数组的长度
length()
-- 字符串相关的对象 (String
, StringBuilder
, 等等) -- 用于获取字符串的长度
size()
-- 集合对象 (ArrayList
, Set
, 等等) -- 用于获取集合的大小
现在忘掉length()
,只考虑length
和size()
。
length
不是一个方法,因此它完全可以理解为它不能用于对象。它只适用于数组。
size()
的名称描述得更好,因为它是一个方法,所以它将用于那些使用集合(集合框架)的对象,就像我之前提到的那样。
现在来看length()
:
String不是原始数组(因此无法使用.length
),也不是集合(因此我们无法使用.size()
),这就是为什么我们还需要另一个方法length()
(保持差异并服务于目的)。
作为回答为什么?
我发现它有用,易于记忆和使用,而且友好。
public class LengthTest {
public static void main(String[] args) {
int[] array = {12,1,4};
String string = "Hoo";
System.out.println(array.length);
System.out.println(string.length());
}
}
削减字节码中不太重要的部分,运行javap -c
命令对类进行反编译,最后两行输出如下:
20: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: arraylength
25: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
28: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_2
32: invokevirtual #5; //Method java/lang/String.length:()I
35: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
在第一种情况下(20-25),代码仅向JVM请求数组的大小(在JNI中,这将是对GetArrayLength()的调用),而在字符串情况下(28-35),它需要进行方法调用以获取长度。
在上世纪90年代中期,没有好的JIT和其他东西,仅拥有java.util.Vector(或类似的东西)而不是一个行为非常快速的语言构造会完全影响性能。他们当然可以将属性掩盖为方法调用并在编译器中处理它,但我认为在不是真正的类的东西上具有方法会更加令人困惑。
考虑以下内容:
int[] myArray = new int[10];
String myString = "hello world!";
List<int> myList = new ArrayList<int>();
myArray.length // Gives the length of the array
myString.length() // Gives the length of the string
myList.size() // Gives the length of the list
length()
避免了相同信息的重复。另一个理由是,使用方法length()
有助于强调字符串的不可变性,尽管数组的大小也是不可改变的。.length
是Java的一次性属性。它用于查找单维数组的大小。
.length()
是一个方法。它用于查找String
的长度。它避免了值的重复。
我曾经学过,对于数组来说,不会通过方法获取其长度,因为有以下担忧:程序员会在进入循环之前将长度分配给一个局部变量(例如for循环中的条件使用了数组的长度)。程序员可能会这样做以减少函数调用(从而提高性能)。问题是,在循环期间长度可能会发生改变,但变量不会。
对象是类实例或数组。
因此,在Java中,数组确实具有非常特殊的作用。我不知道为什么。
有人可能会认为,当前实现的数组对于更好的性能至关重要。但那是一个内部结构,不应该被公开。
我同意Fredrik的观点,即智能编译器优化是更好的选择。这也将解决问题,即使您对数组使用属性,也无法解决字符串和其他(不可变)集合类型的问题,因为例如`string`基于`char`数组,如您可以在`String`类定义中看到的:当然,他们本可以将属性掩盖为方法调用,并在编译器中处理它,但我认为在某些情况下,在不是真正的类上拥有方法会更加混乱。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; // ...
我不同意这会更加混乱的说法,因为数组确实继承了 java.lang.Object
的所有方法。
作为一名工程师,我真的不喜欢答案是“因为一直都是这样”,希望有一个更好的答案。但在这种情况下,似乎就是这样。
简而言之:
在我看来,这是Java的设计缺陷,不应该以这种方式实现。