0

我希望这个程序给我编译错误,但它编译成功,运行时出现运行时异常。

class substr11
{

public static void main(String args[])
{

String s = "abcde";

System.out.println(s.substring(1,-1));

}


}

编译器错误应该能够解析(字符串在正文中初始化)并发现此字符串操作会导致编译错误对吗?。有人能告诉我为什么它没有抛出编译错误吗?

线程“main”中的异常 java.lang.StringIndexOutOfBoundsException:String ind ex 超出范围:-2 at java.lang.String.substring(Unknown Source) at substr11.main(substr11.java:9)

4

7 回答 7

3

编译错误是编译器无法编译代码(语法错误、缺少符号等)。

在您的程序中并非如此,您按预期将两个ints 传递给String#substring,并且您没有遗漏任何东西(没有丢失的类,括号可以,分号是应该的),那么编译器为什么要关心呢?

如果代码已编译但由于某些异常(如ArrayIndexOutOfBounds )而崩溃,则会收到异常

于 2013-08-22T07:40:55.403 回答
1

编译器从不解析或优化为方法调用。即使子字符串操作可能无效并且具有硬连线参数,但这并不重要,因为它不会解析整个方法调用。

该调用可能会产生副作用,或者仅在可能包括实例、静态变量甚至库选择的复杂条件下引发运行时异常。

编译错误可能包括无效的语法,但不是这样的。方法调用有效。也许某个字符串可以将这组参数用于其子字符串方法。编译器不会对该字符串执行复杂的分析。

如果这样做,编译将永远花费,并且会出现很多很多错误。

于 2013-08-22T07:40:45.913 回答
1

您的代码中没有编译时错误。调用substring()需要两个 int,这是对substringmethod的有效调用。

编译器不检查 String 内容,因此它看不到这会在运行时引发异常。

就编译器而言,该s变量仅被假定为 a String,对其内容没有任何假设。在这种情况下,它被分配了一个常数值,但它可能是一些不可估量的东西,例如数据库查询结果或其他任何东西。这同样适用于int函数的参数。

总而言之,正如 hexafraction 在他的回答中指出的那样,编译器不会对输入执行复杂的分析。开发人员负责对输入进行必要的检查,以避免运行时或检查异常,或相应地捕获它们。

于 2013-08-22T07:41:36.697 回答
1

编译器不知道操作的结果是什么。他知道s.substring()需要 2 个 int 参数,并且他确保调用是正确的(s.substring(1,-1)必须是 int 参数)。

于 2013-08-22T07:44:45.077 回答
0

抽出一些时间阅读 Spec JLS#11.2

异常的编译时检查

Java 编程语言要求程序包含检查异常的处理程序,这些异常可能由方法或构造函数的执行引起。对于每个可能结果的检查异常,方法(§8.4.6)或构造函数(§8.8.5)的 throws 子句必须提及该异常的类或该异常类的超类之一(§ 11.2.3)。

这种编译时检查是否存在异常处理程序旨在减少未正确处理的异常数量。throws 子句中命名的检查异常类(第 11.1.1 节)是方法或构造函数的实现者和用户之间合同的一部分。覆盖方法的 throws 子句不能指定此方法将导致抛出任何已检查的异常,而覆盖方法的 throws 子句不允许抛出该异常(第 8.4.8.3 节)。

和异常层次结构

java.lang.Object
  java.lang.Throwable
    java.lang.Exception
     java.lang.RuntimeException
       java.lang.IndexOutOfBoundsException
         java.lang.StringIndexOutOfBoundsException

异常发生在运行时而不是编译器时。

于 2013-08-22T07:41:19.200 回答
0

不要混淆编译器工作,简而言之,它只是将您的代码翻译成低级指令和代码执行。在您的情况下,程序应该调用许多子函数来“理解”您的代码中存在逻辑错误。

于 2013-08-22T07:42:23.523 回答
0

有人能告诉我为什么它没有抛出编译错误吗?

因为它不是编译错误,Java 程序员(和他们的经理!)有权依赖这不是编译错误!!

在另一个世界中,Java 语言可能被指定为要求(或允许)编译器尝试评估该substring调用,并将其标记为错误。

但是,在这个世界上,Java 编译器只允许为“常量表达式”(如JLS 15.28中指定)执行此操作。根据规范,任何包含方法调用的表达式都不是“常量表达式”。因此,实际上,substring调用必须在运行时进行评估,并且必须给您一个运行时异常。


那么为什么Java是这样设计的呢?

您需要询问 Java 设计人员...

...但我认为这是两件事的结合:

  • Java 1.0 语言必须提前发布,否则就会错过市场机会。

  • 然后是向后兼容性的总体要求……这样旧的 Java 程序将随着语言的发展继续工作。

于 2013-08-22T07:53:22.650 回答