Java Scanner 没有在一个 string() 中初始化,即使它在另一个中初始化
Posted
技术标签:
【中文标题】Java Scanner 没有在一个 string() 中初始化,即使它在另一个中初始化【英文标题】:Java Scanner not initializing in one string() even though it does in another one 【发布时间】:2021-12-23 09:54:11 【问题描述】:同一类型的扫描仪方法在一个地方工作,但在另一个地方却不行...... 如果更改任何答案,我将使用 eclipse 作为我的代码编辑器。如果这里没有,所有变量都已在别处声明。
如果有人知道如何解决这个问题,我将永远感激你:)
这个正在工作:
private String questionPicker(String str)
question = (int)(Math.random()*42);
System.out.println(question);
fileChoose = str.toUpperCase();
String returnee;
Scanner is = null;
try
is = new Scanner(new FileInputStream("triviaQ"+fileChoose+".txt"));
catch(FileNotFoundException z)
System.out.println("Error 004: File retrieve failed.");
skipLines(is, question);
returnee = is.nextLine();
is.close();
return returnee;
这个不工作:
public String getAnswer()
String returnee;
Scanner ls = null;
try
ls = new Scanner(new FileInputStream("triviaA"+fileChoose+".txt"));
catch(FileNotFoundException z)
System.out.println("Error 004: File retrieve failed.");
skipLines(ls, question);
returnee = ls.nextLine();
ls.close();
return returnee;
错误信息:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "java.util.Scanner.nextLine()" because "ls" is null
at jepp.JepQA.getAnswer(JepQA.java:60)
at jepp.JepGui.actionPerformed(JepGui.java:268)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6617)
at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
at java.desktop/java.awt.Component.processEvent(Component.java:6382)
at java.desktop/java.awt.Container.processEvent(Container.java:2264)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4993)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2322)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4825)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4934)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4563)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4504)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2308)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2773)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4825)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
【问题讨论】:
这能回答你的问题吗? What is a NullPointerException, and how do I fix it? 为什么你捕捉到FileNotFoundException
异常却像文件存在一样继续你的代码?
【参考方案1】:
仅仅捕获异常是不够的;您还必须相应地更改代码路径。
您的代码具有以下形式:
Scanner ls = null;
try
ls = new Scanner(...);
catch(FileNotFoundException z)
// print error message
// do something with ls
如果发生异常,则在打印错误消息后继续执行,并且当 do something with ls 代码执行时,ls
仍将为空,这就是您得到NullPointerException
的原因。
要修复,要么退出异常方法:
Scanner ls = null;
try
ls = new Scanner(...);
catch(FileNotFoundException z)
// print error message
return;
// do something with ls
或者将使用ls
的代码移到try
内:
try
Scanner ls = new Scanner(...);
// do something with ls
catch(FileNotFoundException z)
// print error message
还要注意上一个版本,我们不仅保存了一行代码(赋值和声明为一行),而且ls
的范围仅限于try
块;限制范围是良好的编码习惯。
【讨论】:
我们可以从您的回答中学习:发现了设计问题,概述了(反)模式 - 一个简短的配方并专注于基本要素(尝试/使用和异常处理)。希望您不介意我将您的第二个配方应用于 OPs 代码 - 作为(过度)扩展版本?️【参考方案2】:投资调试 NullPointerExceptions(也称为 NPE)。您可以咨询link commented by Progman。
第 1 步:堆栈跟踪分析
从上到下遵循异常(此处为 NPE):
java.lang.NullPointerException:无法调用“java.util.Scanner.nextLine()”,因为“ls”为空
在 jepp.JepQA.getAnswer(JepQA.java:60)
在 jepp.JepGui.actionPerformed(JepGui.java:268)
您可以从堆栈跟踪的前 3 行中看到:
-
您的
Scanner
变量ls
为空。这就是为什么像 ls.nextLine()
这样的任何方法调用都会因 NPE 而失败。
失败的调用在 JepQA
类中,在文件第 60 行的 getAnswer
方法内
方法getAnswer
被一个动作处理程序调用,方法actionPerformed
在你的JepGui
第 2 步:为什么是 null
对象?
在getAnswer
方法中,您将ls
初始化为null
- 没关系。
然后,在 try-block 中,在 FileInputStream 上打开 Scanner,该 FileInputStream 连接到 path 给定的文件:
try
ls = new Scanner(new FileInputStream("triviaA"+fileChoose+".txt"));
catch(FileNotFoundException z)
System.out.println("Error 004: File retrieve failed.");
这个Scanner赋值,就是InputStream的开启失败了,于是ls
又变成了null
。
原因可能是:
-
传递给
FileInputStream
constructor 的String
参数是null
。检查或调试打印"triviaA"+fileChoose+".txt"
。还要确保fileChoose
本身不是null
,否则作为参数传递的整个连接表达式也将是null
。
打开流或 Sanner 时出现 FileNotFoundException
之类的异常。但是,为什么它没有被捕获并打印出来作为错误?
所以,肯定还有其他问题!回到(1):假设fileChoose
是null
,结果也是串联的pathname(文件名)。然后从构造函数FileInputStream(String) or FileInputStream(File)
的文档中阅读,它可能会在内部创建一个File
from the pathname given as String
并查看那里的解释:
抛出:
NullPointerException
- 如果 pathname 参数是null
更新: 感谢k314159's comment根据我的假设伪造推理:
可能的根本原因 (2):标准输出(控制台)上显示错误,使用 GUI 时可以轻松查看。 不可能的根本原因 (3):不可能是因为如果fileChoose = null
则连接的路径参数将是 "triviaAnull.txt"
。
说明:
(3) 在null-concatenating demo 中进行了测试。因此,如果传递了无效的路径参数,这将导致ls = new FileInputStream(..)
行出现FileNotFoundException
,因此永远无法达到观察到的ls
NPE。相反,抛出的异常必须已被捕获。由于没有实施返回处理,因此错误会打印到控制台(GUI 上未显示),并且流程会继续以错误的ls = null
进行,直到观察到在ls.nextLine()
处抛出的 NPE。
所以 (2) 必须是最可能的根本原因。但是,FileNotFoundException
可能是由 fileChoose = null
或传递给构造 Scanner 实例的任何其他无效路径引起的。
结论
您作为参数 pathname 传递给 FileInputStream
构造函数的文件名 为 null 也可能无效,因为 fileChoose
变量为 @ 987654372@ 否则。
比较您指定为fileChoose = str.toUpperCase();
的其他工作方法questionPicker(String str)
。如果您在这里将str
作为null
传递,那么它也可能会抛出NPE。
如何避免:验证/测试空输入
添加一个保护声明(参见Fail-fast 原则)。
例如使用Objects.requireNonNull(fileChoose, "answer/question file must not be null");
,其中第二个是fileChoose
为空时打印的错误消息。
另请参阅: Why should one use Objects.requireNonNull()?
解决方案
作为Bohemian's answer correctly spotted 设计问题:
语句的顺序和依赖关系(ls
相关)
try/catch/finally 模式在 I/O-Stream 事务中的异常应用
如果这些是粗略的概述,这里有一个完整的前后示例,并解释了 cmets。
之前:
String returnee; // consider a default return value to avoid any null at the end
Scanner ls = null; // a null Scanner is useless, consider initializing inside try (local scope is safer)!
try // try block can encompass the whole attempt with every dependent statement
ls = new Scanner(new FileInputStream("triviaA"+fileChoose+".txt")); // test filename at least for non-null, before try block!
catch(FileNotFoundException z) //
System.out.println("Error 004: File retrieve failed."); // perfect to log
// but how to handle: return or assign returnee with a default
skipLines(ls, question); // depends on ls, so put inside try-block
returnee = ls.nextLine(); // depends on ls, so put inside try-block
ls.close(); // safe resources: close inside finally-block!
return returnee; // what if this is (still) null
结合波西米亚的第二个轮廓图案:
try-catch-finally 块中的所有内容之后:
// default return
String returnee = ""; // default return value
// guard-statement with fail-fast
if (fileChoose == null)
return returnee; // returns the default or throw
String filename = "triviaA"+fileChoose+".txt"; // can not fail, because validated before. Worst case, e.g. least "triviaA.txt"
try // block of ls dependent statements
Scanner ls = new Scanner(new FileInputStream(filename)); // filename not-null
skipLines(ls, question); // safer, because in local/try scope
returnee = ls.nextLine(); // safer, but may throw NoSuchElementException
catch(FileNotFoundException notFound) //
System.out.println("Error 004: File retrieve failed: " + notFound.getMessage()); // perfect to log
// but how to handle: return or assign returnee with a default
catch(NoSuchElementException noElement)
// log
// and handle!
finally
if (ls != null) ls.close(); // always close (if not null)
return returnee; // Avoid null, at least a default. Benefit: no NPE-risk for the caller.
另见:什么Sanner.nextLine()
might throw
【讨论】:
您的第 2 点和第 3 点有缺陷。我认为没有打印异常,因为 OP 使用的是 GUI,并且控制台上打印了错误如果有一个。你对第 3 点的解释不可能是正确的,因为给FileInputStream
的路径是"triviaA"+fileChoose+".txt"
,如果fileChoose
为空,那么传递给构造函数的路径就是"triviaAnull.txt"
。
@k314159。现在我将你的合理推理作为更新来纠正我有缺陷的假设。在处理输入和处理错误时,我们应该始终考虑人类用户并反思 UI。以上是关于Java Scanner 没有在一个 string() 中初始化,即使它在另一个中初始化的主要内容,如果未能解决你的问题,请参考以下文章
在java 中,String cmd=scanner.nextLine();String help
Java 的 Scanner vs String.split() vs StringTokenizer;我应该使用哪个?