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):假设fileChoosenull,结果也是串联的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() 中初始化,即使它在另一个中初始化的主要内容,如果未能解决你的问题,请参考以下文章

13JAVA常见类(Scanner类String类)

关于 Java 的 Scanner类!!!!

在java 中,String cmd=scanner.nextLine();String help

Java基础12---String类

Java基础12---String类

Java 的 Scanner vs String.split() vs StringTokenizer;我应该使用哪个?