Problema com Exception in thread "main" java.util.NoSuchElementException

Tenho o seguinte método:

public static void operandos() {
    	System.out.println("Quantos operandos terá?");
    	Scanner sc = new Scanner(System.in);
    	qntdOperandos = sc.nextInt();

        for (int i = 0; i < operandos.size(); i++) {
          System.out.printf("Operando número %d:\n", i);
          temp = sc.nextDouble();
          operandos.add(temp);
        }

        sc.close();
    }

Toda vez que compilo meu código, me deparo com o seguinte erro:

Exception in thread “main” java.util.NoSuchElementException

  • at java.base/java.util.Scanner.throwFor(Scanner.java:937)*
  • at java.base/java.util.Scanner.next(Scanner.java:1594)*
  • at java.base/java.util.Scanner.nextInt(Scanner.java:2258)*
  • at java.base/java.util.Scanner.nextInt(Scanner.java:2212)*
  • at Leitura.operandos(Leitura.java:58)*
  • at Leitura.menu(Leitura.java:30)*
  • at Main.main(Main.java:8)*

Cheguei a pesquisar na Internet, mas parece que esse é um erro genérico, porque cada site diz que pode ser uma coisa (com respostas extremamente vagas). Tentei várias coisas, mas nada resolveu.

Alguém pode me dar uma luz?

Você utiliza a classe Scanner em outra parte do programa? Se sim tenta criar uma variável global para a utilizar em qualquer lugar e só fecha ela no final do main.

1 curtida

Sim, utilizava na main pra capturar a seleção do menu. Fiz o que disse, e agora ele simplesmente não pega meu input e pula para um método seguinte do operandos() na main(). Isso é um problema de buffer?

Estou perguntando isso porque lembro de algo do tipo na linguagem de programação em C.

Tem como postar o código todo?

Não sei informar.

Para ler do teclado usa apenas o nextLine()
Com os restantes métodos podes ter problemas com o buffer (por exemplo o nextInt lê um inteiro mas na realidade tu colocas um inteiro e um \n quando introduzes os valores)

1 curtida

Exatamente.
Quando ler do teclado utilize somente nextLine(), pois é o único método que consome a quebra de linha.
Qualquer coisa, dê uma lida neste post.

1 curtida

Então, o nextInt, nextDouble, etc são indicados para qual uso (se apenas o nextLine é usado para entrada pelo teclado)?

Para ler de ficheiro

Ficheiro tu diz “arquivo”?

@pmlm @staroski tentei utilizar somente nextLine e ainda assim ele pula para o próximo método. Para ajudar a solucionar esse problema de vez, vou deixar o código para vocês darem uma olhada e me ajudarem com uma code review (sou iniciante em Java).

Segue o código:
Main.java
public class Main {

	public static void main(String[] args) {
		Leitura leitura = new Leitura();

		leitura.menu();
	}

}

Leitura.java

import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;

public class Leitura {
  private static Double total;
  private static String temp;
  private static List<Double> operandos = new ArrayList<Double>();
  private static Operacoes operacoes = new Operacoes();
  private static Scanner sc = new Scanner(System.in);

  public Leitura() {
      total = 0.0;
      operacoes = new Operacoes();
  }

	public static void menu() {

		System.out.println("Escolha uma opera\u00e7\u00e3o abaixo selecionando o n\u00famero respectivo no \u00edndice da opera\u00e7\u00e3o: \n 1. Soma \n 2. Subtra\u00e7\u00e3o \n 3. Multiplica\u00e7\u00e3o \n 4. Divis\u00e3o \n");

		temp = sc.nextLine();

    int escolhido = Integer.parseInt(temp);

		switch (escolhido) {
			case 1:
				operandos();
				total = operacoes.soma(operandos);
				System.out.println("\nResultado final: " + total);
				break;
			case 2:
				operandos();
			  total = operacoes.subtracao(operandos);
				System.out.println("\nResultado final: " + total);
				break;
			case 3: 
				operandos();
				total = operacoes.multiplicacao(operandos);
				System.out.println("\nResultado final: " + total);
				break;
			case 4:
				operandos();
				total = operacoes.divisao(operandos);
				System.out.println("\nResultado final: " + total);
				break;
			default:
				System.out.println("\nERRO! Par\u00e2metro inv\u00e1lido");
				break;
		}

    sc.close();
	}

	public static void operandos() {
    int qntdOperandos;

		System.out.println("Quantos operandos terá?");
		temp = sc.nextLine();
    qntdOperandos = Integer.parseInt(temp);

    for (int i = 0; i < operandos.size(); i++) {
      System.out.printf("Operando número %d:\n", i);
      temp = sc.nextLine();
      double operandoDouble = Double.parseDouble(temp);
      operandos.add(operandoDouble);
    }
	}
}

Operacoes.java

import java.util.List;
import java.util.ArrayList;

public class Operacoes {
    public static Double totalAcc;

    public Operacoes() {
      this.totalAcc = 0.0;
    }
    
    public static double soma(List<Double> operandos) {
        for (int i = 0; i < operandos.size(); i++) {
          totalAcc = totalAcc + operandos.get(i);
        }

        return totalAcc;
    }

    public static double subtracao(List<Double> operandos) {
        for (int i = 0; i < operandos.size(); i++) {
          totalAcc = totalAcc - operandos.get(i);
        }

        return totalAcc;
    }
    
    public static double divisao(List<Double> operandos) {
        for (int i = 0; i < operandos.size(); i++) {
          totalAcc = totalAcc / operandos.get(i);
        }

        return totalAcc;
    }

    public static double multiplicacao(List<Double> operandos) {
        for (int i = 0; i < operandos.size(); i++) {
          totalAcc = totalAcc * operandos.get(i);
        }

        return totalAcc;
    }
}

Sejam sinceros quanto a code review! Gostaria muito de um feedback.

Aquele for não está a comparar com a variavel errada? operandos aqui é sempre uma lista vazia.

Bem observado, @pmlm.

Agora tudo funcionou perfeitamente! Você tem mais algum comentário ou dica para meu código?

Olha eu n tenho muita experiencia, mais estou vendo que vc está adicionando e contando o tamanho ao mesmo tempo, Olha:

public static void operandos() {
    	System.out.println("Quantos operandos terá?");
    	Scanner sc = new Scanner(System.in);
    	qntdOperandos = sc.nextInt();

        for (int i = 0; i < operandos.size(); i++) {// Aqui 
          System.out.printf("Operando número %d:\n", i);
          temp = sc.nextDouble();
          operandos.add(temp); //Aqui
        }

        sc.close();
    }

Nessas duas linhas vc esta pegando o tamanho de uma array, se a array for fazia vai dar esse erro, se posteriormente você pegar um indicie dela, e se ela for 1 o programa vai entrar em um loop infinito, pois o tamaho da array sempre vai ser maior que a variavel "i"
Começe arrumando por isso:

public static void operandos() {
    	System.out.println("Quantos operandos terá?");
    	Scanner sc = new Scanner(System.in);
    	qntdOperandos = sc.nextInt();

        for (int i = 0; i < qntdOperando; i++) {
          System.out.printf("Operando número %d:\n", i);
          temp = sc.nextDouble();
          operandos.add(temp);
        }

        sc.close();
    }

Espero ter ajudado, (ser for isso arrumar o bug, se se esqueça de marcar como resposta ok)

1 curtida

Obrigado pela observação. Já havia reparado esse trecho do código posteriormente ao postar o código aqui no fórum.

Ainda assim, muito obrigado pela observação!

Para ler de outros InputStreams que não sejam o teclado.

1 curtida

Um problema aí é que em Operacoes tudo está static. Sendo assim, só existe um totalAcc compartilhado entre todas as instâncias. Por exemplo, e se eu quisesse ter 2 operações diferentes e independentes?

// cria 2 instâncias de Operacoes
Operacoes op = new Operacoes();
Operacoes outraOp = new Operacoes();

// usa uma das operações
op.soma(Arrays.asList(1.0, 2.0));
// o valor de totalAcc da outra é alterado (na verdade é o mesmo, compartilhado entre as duas operações)
System.out.println(outraOp.totalAcc); // 3.0

Então você tem que decidir: deve ser possível ter 2 instâncias de Operacoes, cada uma com seu total?


Se não deve ser possível ter 2 instâncias diferentes, então nem faz sentido criar uma instância, você poderia usar simplesmente:

total = Operacoes.soma(operandos); // não precisa criar a variável operacoes, nem chamar new Operacoes()

Como os métodos também são estáticos, eles podem ser chamados diretamente através da própria classe. Criar instâncias nesse caso (com new) não tem nenhum ganho, na verdade nem faria sentido, já que tudo na classe é static.


Mas se você quer a possibilidade de ter várias instâncias diferentes, cada uma com seu total, então deve remover o static de tudo:

public class Operacoes {
    private Double totalAcc;

    public Operacoes() {
        this.totalAcc = 0.0;
    }

    public Double getTotalAcc() { // criar um getter para o campo
        return totalAcc;
    }

    public double soma(List<Double> operandos) {
        for (Double valor : operandos) {
            totalAcc += valor;
        }
        return totalAcc;
    }

    etc... (fazer o mesmo para os outros métodos)
}

Agora sim podemos ter operações independentes, cada uma com seu totalAcc:

Operacoes op = new Operacoes();
Operacoes outraOp = new Operacoes();
op.soma(Arrays.asList(1.0, 2.0));

// total de uma operação não é mais afetado pela outra
System.out.println(outraOp.getTotalAcc()); // 0.0

Repare também que criei um getter para o campo totalAcc, mas não criei um setter. Fiz isso porque obter o valor total acumulado faz sentido, mas mudá-lo diretamente não.

Da forma que você havia feito, com o campo public, era possível fazer isso:

Operacoes.soma(Arrays.asList(1.0, 2.0));
Operacoes.totalAcc = 99999.0; // muda o total para um valor nada a ver

Ou seja, era possível mudar o total para um valor completamente nada a ver. Mantendo o campo private e só criando um getter, eu controlo melhor quem pode alterá-lo - no caso, somente as operações podem mudar esse valor, evitando esses problemas.