Java字符串索引超出范围异常

时间:2019-03-11 15:41:32

标签: java string exception indexoutofboundsexception stringbuilder

是的,我知道如何解决此问题。我当时正在研究String类和String Builder类之间的API。这是我的问题:

StringBuilder sb = new StringBuilder("wibble");
String s = new String("wobble");

sb.charAt(index) //Can throw Index Out Of Bounds Exception
s.charAt(index) //Can throw Index Out of Bounds Exception

sb.substring(start, end) //Can throw STRING index out of bounds exception
s.substring(start, end) //Only throws Index out of Bounds Exception (no String)

因此,当它决定引发StringIndexOutOfBoundsException与IndexOutOfBoundsException时,这是没有意义的。是的,我知道StringIndex是一个子异常。在String和StringBuilder中,具有相同实现的某些方法将引发相同的异常,而在具有相同实现的某些方法中,StringBuilder将引发子异常。那为什么呢?

为避免人们滚动浏览答案,并感谢2个人回答了问题,尽管我只能选择一个作为回答,这两个人都有助于澄清: Java Oracle Doc People在String的子字符串中打了一个错字。应该是StringIndexOutOfBoundsException。与StringBuilder中的CharAt()相同

2 个答案:

答案 0 :(得分:1)

这取决于您使用的是哪种JDK实现。

我做了一些挖掘,发现了public char charAt(int index)类的已实现方法StringBuilder。 (注意:我正在使用Oracle JDK 1.8 )。

因此,在这种情况下,StringBuilder类是AbstractStringBuilder的子类。这是实现程序员的一种选择,因为您不会在docs中找到它。

我发现charAt函数是在AbstractStringBuilder基类上实现的。这是我系统中有问题的代码的一些屏幕截图。 (请记住,特定的JDK是Oracle&Java 1.8 。)

enter image description here

enter image description here

更新

我实际上运行了一些代码来测试抛出了哪些异常,并发现了一些有趣的东西。我的环境中的charAt()函数也会引发java.lang.StringIndexOutOfBoundsException。这是我运行以对其进行测试的代码:

public class StringVsStringBuilder {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder("one");
        String string = "two";

        try {
            builder.charAt(10); // Clearly out of bounds
        } catch (IndexOutOfBoundsException e) {
            System.out.println(String.format(
                    "Exception encountered accessing character out of index bounds on variable 'builder': %s",
                    e.getClass()
            ));
        }

        try {
            string.charAt(10); // Clearly out of bounds
        } catch (IndexOutOfBoundsException e) {
            System.out.println(String.format(
                    "Exception encountered accessing character out of index bounds on variable 'string': %s",
                    e.getClass()
            ));
        }
    }
}

这是运行(Windows 10、64位,Oracle JDK 1.8.0_191)时的输出:

Exception encountered accessing character out of index bounds on variable 'builder': class java.lang.StringIndexOutOfBoundsException
Exception encountered accessing character out of index bounds on variable 'string': class java.lang.StringIndexOutOfBoundsException

Process finished with exit code 0

要解决评论中的一些问题,

  •   

    [{AbstractStringBuilder]不在文档中,这意味着我们不能用它编写程序吗?

    一个通常跟随另一个。 AbstractStringBuilder类是java.lang包专用的,这意味着不能从该包外部访问它。这意味着您将无法在代码中导入和使用此类。这是一部分代码的完美示例,该部分代码不是系统公共API的一部分(在本例中为JDK本身),在这种情况下,出于某些原因,将不会对其进行记录:

    1. 您不能使用它
    2. 实施细节可能会不断变化
    3. 它不应影响公共API(系统的文档部分)的使用
  •   

    如果charAt()中的StringBuilder抛出StringIndexOutOfBoundsException,那么为什么文档会简单地说IndexOutOfBoundsException

    这是因为这是根据类的设计意图对charAt()函数进行的描述的行为。由于您可以为异常创建任意数量的子类(就像任何其他非final的Java类一样),因此实现程序员总是要选择从a返回或抛出异常的最佳方式。功能,只要该功能遵守约定的合同(接口,文档规范,抽象类等)即可。

    话虽如此,您可以立即实现JDK并选择自己的IndexOutOfBoundsException子类来引发这种情况。如果我要在代码中使用StringBuilder.charAt(),则会捕获文档中描述的异常,因为这将使我具有最大的灵活性/可移植性,而不必冒依赖于实现细节的风险。关于此主题的整本书已经写完了,因此我将在这里进行讨论:始终尝试捕获您知道可能会抛出的最具体的异常,这意味着,如果其他实现有可能不会抛出StringOutOfBoundsException,那么您最好只捕获IndexOutOfBoundsException,因为它肯定是这种类型(或其子类)。

更新2

在这种情况下,您几乎可以忽略我上面提到的所有内容,而只关注以下内容:

charAt()java.lang.CharSequence中描述的接口方法。这意味着对于任何实现类,尝试访问序列范围之外的索引时将引发一个IndexOutOfBoundsException问题。该接口描述了一般行为,因此不必根据StringStringBuilderAlphabetSoup或任何其他特定实现来定义此行为。如果那些暗示。决定将这种异常归类,并在每种情况下给出更具体的错误信息,这很好,但是它不会更改总协定。如果您正在编写通用字符处理系统,则可能会写接口,而不在乎底层的impl。在这种情况下,您只会了解(或关心)IndexOutOfBoundsException,而不会了解其他任何信息。

答案 1 :(得分:1)

这似乎是文档错字。

在Java HotSpot(TM)10.0.1中,String.substring确实抛出了StringIndexOutOfBoundsException

public String substring(int beginIndex, int endIndex) {
    int length = length();
    checkBoundsBeginEnd(beginIndex, endIndex, length); //here
    int subLen = endIndex - beginIndex;
    if (beginIndex == 0 && endIndex == length) {
        return this;
    }
    return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                      : StringUTF16.newString(value, beginIndex, subLen);
}


static void checkBoundsBeginEnd(int begin, int end, int length) {
    if (begin < 0 || begin > end || end > length) {
        throw new StringIndexOutOfBoundsException(
            "begin " + begin + ", end " + end + ", length " + length);
    }
}