Java - 为啥 str.substring(str.length()) 是可接受的代码行? [关闭]

Posted

技术标签:

【中文标题】Java - 为啥 str.substring(str.length()) 是可接受的代码行? [关闭]【英文标题】:Java - Why is str.substring(str.length()) an acceptable line of code? [closed]Java - 为什么 str.substring(str.length()) 是可接受的代码行? [关闭] 【发布时间】:2016-02-06 11:06:46 【问题描述】:

长话短说,为什么 Java 不为这行代码抛出 IndexOutOfBoundsException?

str.substring(str.length())

仅仅是IndexOutOfBoundsException的定义吗?为了编程方便/对称,使开始/结束索引采用相同范围的值?这只是甲骨文的决定吗?是否只有一个特殊情况可以使用包含起始索引来处理这种情况?还是有什么深层次的原因……

我阅读了文档,他们说它只返回空字符串 ("")。但我想知道这是否值得担心。它会改变吗?我想不,但我想听听别人的意见。我有一些代码依赖于这样的行来工作,因为我使用 substring 和 indexOf(...)+1 来拆分一些行,我真的不想在代码行周围放置不必要的逻辑。

【问题讨论】:

好吧,Oracle 无缘无故地破坏向后兼容性将是一个相当大的决定,所以我认为你是安全的。 为什么该代码抛出IndexOutOfBoundsException?它的行为得到了很好的定义,并且有据可查(作为返回对空String 的引用)。 Java 在维护兼容性方面有着良好的记录,在这里进行更改没有任何好处。我相信您可以信赖这种行为。 也与List.subList一致,例如docs.oracle.com/javase/7/docs/api/java/util/… 【参考方案1】:

另一种看待它的方式是

str.substring(0, x) + str.substring(x)

总是等价于s,其中0 <= x <= str.length()

为了解决 x == str.length() 不一致和烦人的单一情况 - 例如,您必须将特殊情况写入解析循环。

另见documentation for StringIndexOutOfBoundsException

由 String 方法抛出以指示索引为负数或大于字符串的大小。对于某些方法,例如charAt方法,当索引等于字符串的大小时也会抛出这个异常。

注意第二句 - charAt has 在索引等于字符串长度时抛出异常,因为在该位置没有 char 可以返回。但从技术上讲,在那个位置有一个有效的String - 它是一个零长度的String,即""

这与java中的其他“切片”操作一致——例如,

list.subList(list.size(), list.size())

将返回一个空列表,而不是抛出异常。

【讨论】:

你的答案确实是正确的。但是为什么会不一致/烦人,因为api用户不会关心底层实现是什么样的。但他希望 API 更精确,这意味着从/到 .length(超出 char 数组)调用子字符串应该抛出 StringIndexOutOfBoundsException @rajuGT API 精确的,并记录在案。文档明确指出它会抛出“IndexOutOfBoundsException - 如果beginIndex 为负数或大于此字符串对象的长度。”在str.length() 案例中可能存在一些引发异常的论点,但也有很多(恕我直言,更好)的论点,因为它是这样的。在没有确定正确答案的情况下,您必须选择一个。然后记录下来。这就是他们所做的。另请参阅docs.oracle.com/javase/7/docs/api/java/lang/… 了解 when 它被抛出 @rajuGT 或者我误解了你的意思......也许这里的区别是你不是试图获取数组元素,而是数组的一个切片和一个零长度切片很好。请注意,这与例如一致。 List.subList - docs.oracle.com/javase/7/docs/api/java/util/…【参考方案2】:

如 java docs 中所述,仅当 beginIndex 为 大于此 String 对象的长度时,才会引发 IndexOutOfBoundsException。

还可以查看 java 文档中的以下示例

"emptiness".substring(9) returns "" (an empty string)

评论 中所述,但 charAt 方法并不相同。根据文档和 API 用户的观点,此方法是正确和精确的。

public char charAt(int index) 
    if ((index < 0) || (index >= count))      //          >= operator is used
        throw new StringIndexOutOfBoundsException(index);
    
    return value[index + offset];

【讨论】:

是的,我看到的就是这个。例如,对我来说为什么会这样仍然没有意义,因为访问长度为 x 的数组的索引 x 会抛出 ArrayIndexOutOfBoundsException 而不是空对象。对我来说似乎不一致,但我想我不应该担心。我怀疑他们会改变一些会无缘无故破坏向后兼容性的东西。 我刚刚查看了与此问题相关的所有邮件讨论。但找不到相关信息。收到后我会更新。但从代码的角度来看,此方法调用 substring(beginIndex, count) 其中 count 是字符串/字符数组中的字符数。并且重载的方法正在检查条件if (beginIndex &gt; endIndex) throw new StringIndexOutOfBoundsException(endIndex - beginIndex); 它不是>=。这可能是由于遵循规范,但我不确定。甚至 str.substring(str.length(), str.length()) 也可以工作,因为没有使用 >= 或 【参考方案3】:

你说你已经阅读了文档,所以你知道它返回空字符串,因为这就是文档所说的。但根本原因如下:

出于子字符串的目的,字符串的索引被解释为在字符串中的字符之间。所以索引 0 在第一个字符之前,索引 1 在第一个和第二个字符之间,索引 str.length() 在最后一个字符之后。在最后一个字符之后开始的子字符串是“”。

在子字符串的两个参数版本中更有意义,以这种方式定义它可以避免在提取可能包含或不包含最后一个字符的子字符串时编写特殊情况逻辑。

【讨论】:

好的,现在对我来说更有意义了。我记得很久以前我研究过的其他语言也是这样做的。谢谢!【参考方案4】:

你的提议不合逻辑。这几行代码

System.out.println("1 " + "***".substring(0));
System.out.println("2 " + "***".substring(1));
System.out.println("3 " + "***".substring(2));
System.out.println("4 " + "***".substring(3));

生产

1 ***
2 **
3 *
4

第四行输出遵循其他 3 行的模式。抛出异常没有任何意义。

【讨论】:

【参考方案5】:

这是substring(beginindex) javadoc 的文档。

返回一个新字符串,它是该字符串的子字符串。子字符串以指定索引处的字符开始并延伸到该字符串的末尾。

例子:

"unhappy".substring(2) 返回 "happy"

“Harbison”.substring(3) 返回“野牛”

"emptiness".substring(9) 返回 ""(一个空字符串)

参数: beginIndex 起始索引,含。

返回: 指定的子字符串。

抛出: IndexOutOfBoundsException - 如果 beginIndex 为负数或大于此 String 对象的长度。

基本上是因为如果beginindex &gt; length 会抛出,如果beginindex == length 会返回一个空的String

正如@Kayaman 指出的那样,Oracle 不会为了改变这一点而破坏对其他 java 版本的向后兼容性。

【讨论】:

以上是关于Java - 为啥 str.substring(str.length()) 是可接受的代码行? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

JAVA基础之String基本操作

Java学习笔记(trim()与substring())

java如何去掉字符串最后一个字符

js截取字符串

如何删除字符串最后一位&JAVA&Android&amp;AndroidStudio

Java SubString截取字符串