Java:为什么Array.length没有()但String.length()呢?

为什么当我们得到数组的长度时,我们不必放置()

int [] ar; System.out.println(ar.length); // no parentheses 

但对于一个字符串,它将是

 String st; System.out.println(st.length()); 

为什么Array.length没有()但String.length()呢?

在句法层面,它真正归结为“历史原因”和“因为那是Java(前)1.0语言设计者决定这样做”。 唯一能够真正告诉你原因的人是语言设计师。

然而:

  • length字段是特殊的,因为它实际上在数组对象的头中,并且获取数组的长度使用特殊的字节码。

  • length字段不能通过任何方式更改危险的本机代码黑客。 这样做非常重要,以避免堆损坏等问题。相比之下,甚至可以使用reflection修改普通对象的final 。)

在这些方面,arrays上的length与任何其他领域完全不同。


从(pre-)Java的早期开始,在API中使用暴露字段已经过时了。

大多数人都会说这是件好事。 通过将长度信息作为字符串,集合等上的length()size()方法公开,类库设计允许Java实现者更改相应类型的表示。 如果直接暴露字段,则无法(在Java中)。

不同之处是:

  • 数组的length与实际内容无关 – 这是维度! 数组的length可以是10,但只包含2个元素。
  • 包含元素的对象(字符串是一个包含多个字符的对象)可能内部有一定数量的对象。 此计数可能会有所不同,具体取决于您是添加还是删除元素。 所以length()方法是关于实际内容的!

原因是,数组是一种语言结构 – 因此,每当你想知道关于数组的东西时(它的长度或其他)你必须从JVM中检索一个固定值,而不是调用一个评估某些东西的方法。

Array Class只是实际array -construct的代理。)


从技术上讲,一个array (注意小a )只是内存中array.length元素的内存分配。 (这就是为什么你不能简单地调整数组的大小,但是在更改维度时总是需要创建一个新数组 – 它需要在内存中重新定位(在C中称为realloc ),因此有足够的空间来放置array.length * elementSize位。

另一方面,List不关心它的元素在内存中的实际位置,因此允许随时动态resize。 (因此它可以为一个方法size()因为它没有维度,只需要返回有关它内容的信息。)

这是访问字段(没有括号)和调用方法(需要括号,即使方法没有参数)之间的区别。

从历史上看,数组除了字段之外还可以有一个方法。 除了方法之外,字符串可以有一个字段。 字段更简单,这可能是数组使用它的原因,但至少有两个原因可以让String有一个长度方法:

  1. String实现了CharSequence接口,该接口定义了一个length()方法,因此String必须具有该方法。 (定义字段不是接口的一个特性,除了静态的最终常量。)字符串可以有长度作为方法字段,但这会分散注意力。

  2. 灵活性。 String的内部实现随着时间的推移而发生了变化:曾一度使用内部char数组和单独的offsetlength字段,这些字段定义了char数组中的一个区域。 这样做是为了使子串可以共享父字符串的char数组(具有不同的偏移量和长度),而不是复制数组。 后来发现这种优化很少有用,所以现在,String只有一个正确大小的char数组,而length方法读取数组的长度字段。 字符串不再具有自己的长度字段,甚至不是私有字段,但如果它是类的公共API的一部分,则它们无法删除它,这会使实验内部实现的灵活性降低。

Array的length是一个int字段,但String(和其他对象)通常使用getter方法。

请注意,两者的区别在于字符串的“长度”只是它已经存储在其内部char数组中的信息量,但数组的length字段只是它的容量。