程序运行良好,但它给出了一个警告,这让我很困扰,我不明白为什么!
经过一些尝试,我意识到如果在第一个for循环之前执行myObj.close()
,它会正常关闭。
循环之前?好的消息灵通的也可以。循环之后?Resource leak
我发现使用一个新的变量而不是重用i
会使警告消失,但我仍然不明白为什么。
以下是部分代码:
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner myObj = new Scanner(System.in);
boolean chk = false;
System.out.println("ESERCIZIO 1");
int num = 0;
System.out.print("Inserire un numero: ");
do {
try {
num = Integer.parseInt(myObj.nextLine());
chk = true;
} catch (Exception e) {
System.out.print("Inserire un numero intero: ");
chk = false;
}
if (num <= 0 && chk == true) {
System.out.print("Inserire un numero positivo: ");
chk = false;
}
} while (chk == false);
if (num % 2 == 0) {
System.out.println("Pari");
} else {
System.out.println("Dispari");
}
System.out.println("nESERCIZIO 2");
int i = 0;
do {
if (i % 2 == 0 && i != 0) {
System.out.print(i + " ");
}
i += 2;
} while ((i / 2) < 10);
System.out.println("nnESERCIZIO 3");
int n = 3;
for (i = 0; i <= 10; i++) {
System.out.println(n + " * " + i + " = " + (i * n));
}
...
myObj.close();
在这一部分之后还有更多的for循环,也是如此。
正确的做法是忽略警告。
让我解释一下为什么intellij认为在这里发出警告是合适的,为什么intellii是错误的,为什么在没有"外部注释"的情况下intellij不能解决问题,我相信它已经为核心库中的无效键入做了这样的事,所以也许有一天这个问题会自行解决:
有3种类型的资源(表示数据流并具有紧密方法的事物):
直接表示操作系统级别句柄的资源
这些是为以下对象设计的警告:new FileInputStream()
、socket.getOutputStream()
等:如果您不关闭这些,例如因为在处理过程中发生异常,则操作系统级别的句柄将永远保持打开状态,因此,如果这种情况发生几次,在最好的情况下,您的虚拟机在剩余时间内无法打开任何文件或网络套接字,或者在最坏的情况下整个操作系统都会崩溃(幸运的是,大多数操作系统并没有那么愚蠢)。警告是为了避免这些情况,并迫使您使用带有资源的try,或try/finaly块,或将您自己的类型升级为本身就是筛选资源的类型(请参阅此列表中的第3项)。
不代表任何需要关闭的内容的资源
有些基本级别的资源不占用任何系统句柄。例如,new ByteArrayOutputStream()
;如果你不能关闭这些,绝对没有任何问题。当对象被垃圾收集时,它们占用的内存就会消失,你是否关闭它们也无关紧要。关闭它们没有坏处,但如果IDE警告您,这是一个不正确的警告。
筛选资源
这些是问题儿童。Java的设置允许您在筛选资源类型时包装资源,并希望您经常这样做。CCD_ 9中的CCD_ 7和CCD_。
在basis中,过滤器本身并不代表系统资源;它们没有操作系统级别的句柄,如果你想关闭它们,也不会发生什么不好的事情。然而,它们"包装"的资源(本例中的socket.getInputStream()
)通常会,尽管如果它是来自第二类(如new ByteArrayInputStream(byteArr)
)的资源,则这不适用。此外,编写确保原始(类型1)流关闭并忽略过滤器的代码是而不是资源泄漏,但通常是错误:如果您编写例如:
try (OutputStream raw = socket.getOutputStream()) {
OutputStreamWriter writer = new OutputStreamWriter(raw, UTF_8);
writer.send("Hello, World!n");
}
然后,您没有资源泄漏,但确实有一个错误:这实际上不会发送hello-world:它仍然是缓冲的,并且由于您没有关闭过滤流(OutputStreamWriter),数据永远不会被刷新。
因此,如果你只是按相反的顺序关闭所有的东西,通常会更简单。
不幸的是,"为了方便"(这早于java的try-with-resources构造,这解释了为什么要这样做),过滤器流几乎总是根据其API规范定义,关闭它们也会关闭它们正在包装的流:关闭OutputStreamWriter将清除所有数据,然后也会关闭您包装的套接字流。
这里有各种各样的问题,因为虽然你通常总是想冲洗,但有时你不想关闭。如果要混合内容(首先发送二进制数据,然后发送一些字符串,然后再次发送二进制数据),则需要包装要刷新但不关闭的StreamReaders/StreamWriters。
另一个例子是System.in
。您不想关闭它!
因此,从这个意义上说,如果自动分拣工具将过滤流视为而不是需要关闭,那就"更好"了。
不幸的是,intellij可以仅根据类型信息自动确定某个东西是资源(任何实现AutoClosable
的类基本上都有编译器/可编辑的可检查文档,它可以自动关闭,这通常也意味着它必须关闭以避免资源泄漏,但并不总是如此),但它无法根据类型信息确定关闭失败是否是资源泄漏。要是有就好了。
intellij的解决方案是手动整理一个已知类型的列表,并将其分类为特定的类别(类型1、类型2或类型3),但它会变得更复杂,因为"必须关闭"很好知道,但你仍然需要知道"哪个代码有责任这样做",这并不总是清楚的。例如,通常new X()
启动资源的人都应该关闭它,但例如,Files.newInputStream
被记录为将这样做的责任交给调用者,即使它不是构造函数调用,本身也不是资源对象的一部分(它是一个静态方法,因此它一开始就不是AutoClosable实例的一部分)。无法以自动方式提取此文档(它应该是一个注释;不幸的是,没有这样的东西。糟糕的API设计)。
这一切加起来就是一个简单而不幸的事实:IntelliJ只是在黑暗中疯狂地捅了一刀,它一直在猜测错误。这不是IntelliJ的错;它正在尽其所能。
尽管如此,这确实意味着,如果你的任务是在没有事先了解的情况下消除每一个警告,那么你将编写糟糕的代码。在这种情况下,一旦你明白intellij的担忧是你应该关闭过滤器以避免资源泄漏和刷新问题,你就可以通过意识到System.In实际上应该关闭而不是来"修复"潜在的问题,并且在相关的情况下冲洗任何缓冲的中间体。当你阅读的时候,事实并非如此,所以这部分很容易。
然后,如果你的IDE让你很难确定忽略这些警告,那就别无选择,只能叹息并禁用整个linting类别,因为编写更糟糕的代码来满足一个愚蠢的linting工具显然是一个摇尾巴的例子。当然,不要成为坏工具和坏规则的奴隶。