# Probleme mit nextInt() und Exception



## ernst (16. Feb 2016)

Hallo allerseits,
1)
Mit dem Scanner kann man mit nextInt() Zahlen eingeben.
Gibt man eine Zeichenkette von Ziffern ein, wird diese in den Tastaturpuffer geschrieben.
nextInt() wandelt diese Zeichenkette in eine Zahl um und _leert_ dabei den Tastaturpuffer.

2)
Gibt man dagegen keine Zeichenkette von Ziffern ein, wie z.B. "e" wird eine Exception erzeugt und der Tastaturpuffer - so meine Vermutung -  dabei _nicht_ gelöscht.
Meine Vermutung kommt von dem Programm unten:
Wenn man z.B. die Zeichenkette "e" über Tastatur eingibt, so kommt man in eine Endlos-Schleife. Das Programm hält auch nicht mehr an, weil im Tastaturpuffer immer "e" steht.

3)
Ist meine Vermutung korrekt?

4)
Warum steht in der Doku von Oracle nichts von meiner Vermutung ?

5)
Gibt es eine offizielle Doku, in der diese Bugs von Java dokumentiert werden ?

6)
Auf der Seite:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6351708
wird dieser Bug auch nicht aufgelistet.

7)
Ist die Seite
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6351708
eine offizielle Doku aller Java Bugs bzw. wo findet man so eine offizielle Doku ?

mfg
Ernst



=====================================================

```
/*
P R O G R A M M D O K U M E N T A T I O N:
nextInt() liest aus dem Tastaturpuffer eine Zeichenkette wie z.B. "123"
und wandelt diese in eine ganze zahl um.
Wenn man allerdings eine Zeichenkette einliest, wie z.B. "e" oder "12x"
kann diese nicht in eine Zahl umgewandelt werden.
Dann wird eine Exception ausgelöst.

Problem:
beim Auslesen einer Zeichenkette, die nicht in eine Zahl umgewandelt werden
kann wie z.B. "e" wird dieser "Restmüll" NICHT aus dem Tastaturpuffer
entfernt. Deswegen kommt das Programm unten in eine Endlos-Schleife.

Offizieller, anerkannter Fehler, siehe:
[URL]http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6351708[/URL]
*/
package scannerproblem1;
import java.util.Scanner;
/**
*
* @author lzbarner
*/
public class Startklasse {

  public static void main(String[] args) {
    System.out.println("Eingabe Integer:");
    System.out.println("Ihre Eingabe war: " + getInput());
  }

 
    protected static int getInput() {
      boolean isCorrectInput = false;
      int inputValue = 0;
      Scanner sc = new Scanner(System.in);

      while (!isCorrectInput) {
        try {
        // Bei einer Eingabe wie z.B. "e" wird dieser Schrott nicht
        // aus dem Tastaturpuffer entfernt. Das Programm springt
        // korrkterweise in den catch-Block. Danach kommt es wieder
        // zu der Zeile: inputValue = sc.nextInt();
        // Weil aber immer noch der Schrott "e" im Tastaturpuffer
        // steht, muß das Programm nicht anhalten und auf eine neue
        // Eingabe warten.
        inputValue = sc.nextInt();
        isCorrectInput = true;
        } catch (Exception ex) {
           System.out.println("Eingabe war nicht vom Typ Integer, bitte erneut   
                                        eingeben:");
           // System.out.println("sc.Next()="+sc.next());
           //ex.printStackTrace();
      }
    }
  return inputValue;
  }
}
```


----------



## Joose (16. Feb 2016)

Kein Bug.
Einen passenden Link hast du ja schon gefunden: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6351708
Dort steht ganz unten dass die Methoden so arbeiten wie sie spezifiziert wurden.

EDIT: Bitte Code immer in Code-Tags packen [java] .. der code ... [/java]


----------



## Dukel (16. Feb 2016)

Wieso machst du dies mit der While Schleife?

```
try {
            inputValue = sc.nextInt();
        } catch (Exception e) {
            System.out.println("Eingabe war nicht vom Typ Integer, bitte erneut eingeben:");
            //e.printStackTrace();
        }
```

Dein Problem ist, das nicht nach einer weiteren Eingabe gefragt wird, da dies außerhalb der Methode passiert.

EDIT: Ach ja. Du musst den Scanner in die While Schleife nehmen, dann funktioniert das auch.


```
while (!isCorrectInput) {
       Scanner sc = new Scanner(System.in);
       try {
         inputValue = sc.nextInt();
         isCorrectInput = true;
       } catch (Exception ex) {
         System.out.println("Eingabe war nicht vom Typ Integer, bitte erneut eingeben:");
       }
     }
```


----------



## Joose (16. Feb 2016)

@Dukel warum keine Schleife? Er will ja solange etwas eingeben müssen bis eine Zahl eingegeben wurde.
Der Scanner muss nicht in der Schleife initialisiert werden, wozu auch? Mittels den "next...." Methoden wird nach neuen Input geschaut.


----------



## ernst (16. Feb 2016)

Joose hat gesagt.:


> Kein Bug.
> Einen passenden Link hast du ja schon gefunden: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6351708
> Dort steht ganz unten dass die Methoden so arbeiten wie sie spezifiziert wurden.
> [/java]



Wie wurden sie spezifiziert?
Ich habe nicht gelesen, daß der Tastaturpuffer geleert wird.
Außer der Info in
http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html
"When a scanner throws an InputMismatchException, the scanner will not pass the token that caused the exception, so that it may be retrieved or skipped via some other method."


mfg
Ernst


----------



## Joose (16. Feb 2016)

In diesem Fall habe ich nur kurz zusammengefasst was man beim Link lesen kann 

Aber du schreibst es ja selber du hast nichts davon gelesen das der Buffer geleert wird im Fehlerfall. Daher sollte man auch nicht davon ausgehen.


----------



## ernst (16. Feb 2016)

Joose hat gesagt.:


> In diesem Fall habe ich nur kurz zusammengefasst was man beim Link lesen kann
> 
> Aber du schreibst es ja selber du hast nichts davon gelesen das der Buffer geleert wird im Fehlerfall. Daher sollte man auch nicht davon ausgehen.


Im Fall einer Zeichenkette aus Ziffern wie z.B. "123" steht aber auch nicht, daß der Tastaturpuffer geleert wird. Er wird aber geleert.
Wie soll man dann aber mit dieser Methode nextInt() arbeiten, wenn man nicht weiß, ob der Tastaturpuffer geleert wird oder nicht?

mfg
Ernst


----------



## kneitzel (16. Feb 2016)

Also das Verhalten ist zum einen genau das, was erwartet wird. Wenn ein Token zurück gegeben wird, dann sind Daten gelesen und werden damit aus dem Stream entfernt.
Wenn eine Exception geworfen wird, dann wären die Daten ja weg, wenn sie dadurch aus dem Stream entfernt worden wären. Das wäre ein potentieller Datenverlust. Also unwahrscheinlich, dass etwas so implementiert wurde.

Aber damit das Verhalten klar ist, wurde es entsprechend dokumentiert. Die Stelle ist ja schon zitiert worden.


----------



## Dukel (16. Feb 2016)

Joose hat gesagt.:


> @Dukel warum keine Schleife? Er will ja solange etwas eingeben müssen bis eine Zahl eingegeben wurde.
> Der Scanner muss nicht in der Schleife initialisiert werden, wozu auch? Mittels den "next...." Methoden wird nach neuen Input geschaut.



Siehe mein Edit. Ohne die Schleife war mein erster Gedanke.


----------



## ernst (16. Feb 2016)

kneitzel hat gesagt.:


> Also das Verhalten ist zum einen genau das, was erwartet wird. Wenn ein Token zurück gegeben wird, dann sind Daten gelesen und werden damit aus dem Stream entfernt.
> Wenn eine Exception geworfen wird, dann wären die Daten ja weg, wenn sie dadurch aus dem Stream entfernt worden wären. Das wäre ein potentieller Datenverlust. Also unwahrscheinlich, dass etwas so implementiert wurde.


Was ist aber anders, wenn das Objekt sc der Klasse Scanner _innerhalb_ der while-Schleife deklariert und initialisiert wird?
Warum funktioniert es da (keine Endlos-Schleife bei Eingabe von z.B. "e") ?

mfg
E


----------



## kneitzel (16. Feb 2016)

Ich kenne die Implementation von Scanner nicht im Detail. Möglich, dass der Scanner das Token aus dem Stream liest. Damit ist das Token in der Scanner Instanz und nicht mehr auf dem Stream. Wenn Du jetzt ein neues Scanner Objekt erzeugst auf dem Stream, dann kann es nur noch das lesen, was da auf dem Stream ist.

Das ist aber ein Pattern, dass extrem unschön ist. Von einem Stream sollte genau an einer Stelle gelesen werden und nicht von mehreren Instanzen.


----------



## ernst (16. Feb 2016)

Zitat:
"Ich kenne die Implementation von Scanner nicht im Detail. Möglich, dass der Scanner das Token aus dem Stream liest. Damit ist das Token in der Scanner Instanz und nicht mehr auf dem Stream. Wenn Du jetzt ein neues Scanner Objekt erzeugst auf dem Stream, dann kann es nur noch das lesen, was da auf dem Stream ist."
Ich verstehe diese Argumentation nicht.
Wenn man "e" eingibt befindet sich das "e" immer noch im Tastaturpuffer (= Stream ?) und wird durch nextInt() nicht ausgelesen und dort gelöscht. 
Warum sollte diese Regel nicht mehr gelten, wenn sich der Ort der Deklaration ändert bzw. von diesem abhängig sein?

mfg
E


----------



## JStein52 (16. Feb 2016)

ernst hat gesagt.:


> Ich verstehe diese Argumentation nicht.


Was verstehst du daran nicht ? Ist zwar nur eine Annahme aber ist doch plausibel. Wenn der "alte" Scanner die Eingabe (hier das "e") schon aus dem Inputstream ausgelesen hat dann findet der nächste Scanner natürlich nichts mehr.


----------



## kneitzel (16. Feb 2016)

Also eine bildliche Beschreibung:
Mal angenommen Du legst Zettel mit irgendwas drauf immer auf einen Stapel (Da bist Du dann sozusagen die Tastatur). 
Ich bin dann der Scanner - und meine Aufgabe ist es, immer den untersten Zettel vom Stapel zu nehmen und auszuwerten. 
Nun werde ich angesprochen: Gib mir mal bitte die nächste Zahl. Also nehme ich den untersten Zettel und schaue drauf. Da sehe ich aber jetzt ein "e" also sage ich Dir: Sorry, da ist keine Zahl drauf! Und behalte den Zettel aber in der Hand!
Jetzt kann man mich erneut ansprechen: gib mit die nächste Zahl. Ich habe aber noch den Zettel in der Hand und da steht weiterhin "e" drauf. Also werde ich wieder keine Zahl nennen können.
Man könnte mir sagen: Lege den Zettel weg. Dann wäre es möglich, mich erneut nach einer Zahl zu fragen. Da ich keinen Zettel in der Hand halte, müsste ich mir den nächsten Zettel nehmen.
Oder man könnte hingehen und sagen: Du bist ja blöd - willst mir einfach keine Zahl nennen. Daher suchst Du Dir jemand anderes, der sich einen Zettel nehmen kann und Dir die Zahl sagen kann.

Das kann ganz böse sein, denn wenn ich nicht mehr Zettel auswerten soll gehe ich hin und zerstöre den ganzen Papierstapel mit allen Zetteln und dem Platz für den Stapel. Andere können dann auch keine Zettel mehr wegnehmen. (Das ist die close() Methode. Close schließt auch den Stream.)

Diese eigenen Buffer sind teilweise normale Dinge. Ich muss ja Daten, die ankommen, lesen, bis ich ein Token zusammen habe. Ein Beispiel aus der Vergangenheit war z.B. bei mir das Lesen von Daten aus einem Netzwerk Stream: Die Daten sind in Paketen gekommen und waren dabei aber nicht in meine "Token" getrennt. Trenner war bei mir ein Newline und ich musste also immer Daten lesen bis ich endlich ein Newline hatte. Dann konnte ich die Nachricht erst auswerten.


----------



## kneitzel (16. Feb 2016)

Und da ich Vermutungen hasse und mich das selbst etwas wurmt habe ich einfach einmal die Implementation der Scanner Klasse im openjdk angesehen:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/8cc500af2454/src/share/classes/java/util/Scanner.java

Zum einen findet man direkt einen internen Buffer:

```
// Internal buffer used to hold input
    private CharBuffer buf;
    // Size of internal character buffer
    private static final int BUFFER_SIZE = 1024; // change to 1024;
    // The index into the buffer currently held by the Scanner
    private int position;
```

Und dann findet man im Code natürlich auch die Nutzung des Caches:

```
/**
    * Scans the next token of the input as an <tt>int</tt>.
    *
    * <p> An invocation of this method of the form
    * <tt>nextInt()</tt> behaves in exactly the same way as the
    * invocation <tt>nextInt(radix)</tt>, where <code>radix</code>
    * is the default radix of this scanner.
    *
    * @return the <tt>int</tt> scanned from the input
    * @throws InputMismatchException
    *        if the next token does not match the <i>Integer</i>
    *        regular expression, or is out of range
    * @throws NoSuchElementException if input is exhausted
    * @throws IllegalStateException if this scanner is closed
    */
    public int nextInt() {
        return nextInt(defaultRadix);
    }
    /**
    * Scans the next token of the input as an <tt>int</tt>.
    * This method will throw <code>InputMismatchException</code>
    * if the next token cannot be translated into a valid int value as
    * described below. If the translation is successful, the scanner advances
    * past the input that matched.
    *
    * <p> If the next token matches the <a
    * href="#Integer-regex"><i>Integer</i></a> regular expression defined
    * above then the token is converted into an <tt>int</tt> value as if by
    * removing all locale specific prefixes, group separators, and locale
    * specific suffixes, then mapping non-ASCII digits into ASCII
    * digits via {@link Character#digit Character.digit}, prepending a
    * negative sign (-) if the locale specific negative prefixes and suffixes
    * were present, and passing the resulting string to
    * {@link Integer#parseInt(String, int) Integer.parseInt} with the
    * specified radix.
    *
    * @param radix the radix used to interpret the token as an int value
    * @return the <tt>int</tt> scanned from the input
    * @throws InputMismatchException
    *        if the next token does not match the <i>Integer</i>
    *        regular expression, or is out of range
    * @throws NoSuchElementException if input is exhausted
    * @throws IllegalStateException if this scanner is closed
    */
    public int nextInt(int radix) {
        // Check cached result
        if ((typeCache != null) && (typeCache instanceof Integer)
            && this.radix == radix) {
            int val = ((Integer)typeCache).intValue();
            useTypeCache();
            return val;
        }
        setRadix(radix);
        clearCaches();
        // Search for next int
        try {
            String s = next(integerPattern());
            if (matcher.group(SIMPLE_GROUP_INDEX) == null)
                s = processIntegerToken(s);
            return Integer.parseInt(s, radix);
        } catch (NumberFormatException nfe) {
            position = matcher.start(); // don't skip bad token
            throw new InputMismatchException(nfe.getMessage());
        }
    }
```

Das erst einmal nur als Anhalt dafür, dass meine Vermutung richtig ist. Natürlich müsste man noch die next Funktion genauer ansehen. Dort sieht man dann die Nutzung des buffers im Detail mit der Funktion getCompleteTokenInBuffer.

Also ich gehe dem jetzt nicht noch mehr im Detail nach - was ich jetzt gesehen habe reicht mir soweit.


----------



## ernst (16. Feb 2016)

kneitzel hat gesagt.:


> Also das Verhalten ist zum einen genau das, was erwartet wird. Wenn ein Token zurück gegeben wird, dann sind Daten gelesen und werden damit aus dem Stream entfernt.


ok, aber _nur_ wenn es eine Zeichenkette ist, wie z.B. "123"



> Wenn eine Exception geworfen wird, dann wären die Daten ja weg, wenn sie dadurch aus dem Stream entfernt worden wären. Das wäre ein potentieller Datenverlust. Also unwahrscheinlich, dass etwas so implementiert wurde.


Genau und deswegen ist das "e" immer noch auf dem Stapel
Also auch bei deinem Stapel-Modell.



> Mal angenommen Du legst Zettel mit irgendwas drauf immer auf einen Stapel (Da bist Du dann sozusagen die Tastatur).
> Ich bin dann der Scanner - und meine Aufgabe ist es, immer den untersten Zettel vom Stapel zu nehmen und auszuwerten.
> Nun werde ich angesprochen: Gib mir mal bitte die nächste Zahl. Also nehme ich den untersten Zettel und schaue drauf. Da sehe ich aber jetzt ein "e" also sage ich Dir: Sorry, da ist keine Zahl drauf! Und behalte den Zettel aber in der Hand!


Nein, er ist nicht in deiner Hand. Du hast ihn nur gelesen. Er ist immer noch auf dem Stapel (sonst
potentieller Datenverlust --> Zitat von dir).
Wo habe ich dich falsch verstanden ?

mfg
E


----------



## JStein52 (16. Feb 2016)

ernst hat gesagt.:


> Wo habe ich dich falsch verstanden


Du hast nicht verstanden dass der Zettel nicht mehr auf dem Stapel ist. Du hast doch gesehen dass der InputStream gelesen wurde und die Daten im Scanner-Cache gehalten werden.


----------



## kneitzel (16. Feb 2016)

Du hast den Stream. Und Du hast den Scanner. Der Scanner hat einen eigenen Zwischenspeicher. Daher sind die Daten aus dem Stream raus und im Scanner drin. Das ist doch auch das Verhalten, dass Du beobachtet hast, wenn ich Dich richtig verstanden habe.

Und ein Datenverlust droht nicht, so Du sauber arbeitest. Sieh Streams als eine Art Rohr an. Du hast nur einen Eingang und einen Ausgang. Wenn Du aber meinst, viele Ausgänge zu haben, dann ist es Dein Problem und nicht das Problem des Streams.


----------



## ernst (17. Feb 2016)

Erst mal Dank an alle für die vielen Feedbacks.


kneitzel hat gesagt.:


> Du hast den Stream. Und Du hast den Scanner. Der Scanner hat einen eigenen Zwischenspeicher. Daher sind die Daten aus dem Stream raus und im Scanner drin. Das ist doch auch das Verhalten, dass Du beobachtet hast, wenn ich Dich richtig verstanden habe.


Ok, das kann ich nachvollziehen.
Aber müsste diese Beschreibung und dieses Verhalten nicht in die Doku zu der Klasse Scanner?



> Und ein Datenverlust droht nicht, so Du sauber arbeitest. Sieh Streams als eine Art Rohr an. Du hast nur einen Eingang und einen Ausgang. Wenn Du aber meinst, viele Ausgänge zu haben, dann ist es Dein Problem und nicht das Problem des Streams.


Was meinst du mit "viele Ausgänge" im Zusammenhang mit diesem Thread bzw. dem von mir beschriebenen Problem ?

mfg
E


----------



## kneitzel (17. Feb 2016)

Also in der Programmierumgebung würde es bedeuten, dass ich auf einem Stream mehrere "Abnehmer" erzeuge. Also z.B. zwei Scanner auf einem Stream. Das ist einfach ein No Go.

Und im Übertragenen Sinne wäre es eine Wasserleitung an der mehrere Wasserhähne sind. Wasser, das am ersten Wasserhahn vorbei gelaufen ist, wird nicht mehr aus dem ersten Wasserhahn laufen. Es ist keine saubere Abnahme von Werten.

Und diese Buffer müssen nicht explizit erwähnt werden aus meiner Sicht. Das ist eine Logik, die gekapselt ist und die Dich nicht zu interessieren hat. Und es ist ein übliches, normales Vorgehen


----------



## ernst (17. Feb 2016)

kneitzel hat gesagt.:


> Also in der Programmierumgebung würde es bedeuten, dass ich auf einem Stream mehrere "Abnehmer" erzeuge. Also z.B. zwei Scanner auf einem Stream. Das ist einfach ein No Go.


wenn man _innerhalb_ der while-Schleife (wie oben vorgeschlagen wurde) eine Objektvariable vom Typ Scanner deklariert und initialisiert, dann hat man den von dir geschilderten Fall: mehree Abnehmer auf dem gleichen Stream ( = Tastaturpuffer). Ist das also ein no-go ?



> Und diese Buffer müssen nicht explizit erwähnt werden aus meiner Sicht. Das ist eine Logik, die gekapselt ist und die Dich nicht zu interessieren hat. Und es ist ein übliches, normales Vorgehen


Was gekapselt ist, geht mich nichts an. Das sehe ich ein.
Wie ich aber nextInt() verwenden kann geht den Nutzer der Funktion schon etwas an.
Und daß "zwei Scanner auf einem Stream" ein no-go sind geht den Nutzer der Funktion auch etwas an. Das sollte dann schon dokumentiert sein.

mfg
E


----------



## kneitzel (17. Feb 2016)

Also ich sehe hier mehrere Punkte:

a) Wie geht man an eine Entwicklungsarbeit heran?
Meine Meinung nach erfordert das immer eine intensive Recherche. Sowohl zum eigentlichen Problemgebiet (Man muss die Aufgabenstellung verstanden haben) aber auch zu den eingesetzten Hilfsmitteln. Dazu gehören Tools und Frameworks.
So muss jemand die Klassen kennen, die man nutzen will. Wer die Klasse Scanner nutzen will, der sollte die Klasse auch kennen. Und dazu gehört dann das Lesen der Dokumentation. Und die Klasse Scanner hat eine sehr große Dokumentation.
Desweiteren sollte man die implementierten Interfaces kennen. So gibt es das Closable Interface. Eine Instanz, die dies implementiert, will geschlossen werden. Da Java keine "native"/"unmanaged" Dinge kennt, ist es hier nicht so wichtig, wie in .Net das Disposable. (Es sei denn, man hat JNI im Einsatz. Dann wird es ebenso wichtig!) Etwas, das geschlossen werden will, sollte auch geschlossen werden. Dafür gibt es dann auch extra so Dinge wie try with resources.

b) Unsaubere Entwicklung ist ein Problem
Es ist einfach ein Problem, wenn jemand unsauber entwickelt. Da kommt zwar eine Exception aber ich muss ja nicht verstehen, was da abgeht. Ich frickel mir dann schon eine Lösung zusammen. Closable? Interessiert mich nicht!
So geht das nicht. Wenn Du da einen Scanner nutzt, dann ruf doch auch mal close auf oder nutz try with ressources. Und schon merkst Du, dass Dein angedachtes Konstrukt so eben nicht funktioniert.
Lies die Dokumentation der Klasse - und schon findest Du Hinweise, was bei der Exception zu beachten ist.

c) Inhalt der Dokumentation von Scanner und wo sollen diese Informationen rein?
Ein Punkt ist, in wie weit gewisse Dokumentation nicht in den "Kopf" der Doku gehört sondern in die Beschreibung der Klasse. Aber das würde bedeuten, dass bei relativ vielen Funktionen genau dieser Hinweis zu der Exception rein müsste. Aber es macht doch keinen Sinn, einen Kommentar X mal zu kopieren. Wer eine Klasse verwendet, wird doch immer die Doku der Klasse lesen.
Ein anderer Punkt ist die Frage, was alles dokumentiert gehört. Genereller Umgang mit Streams gehört da meiner Meinung nach nicht rein. Scanner ist ja kein Stream sondern nutzt den Stream nur. Wer also etwas über Streams erfahren möchte, der sollte doch bitte die Dokumentation dort lesen.
Ebenso ist evtl. die Frage zu beantworten, was an Grundlagen noch in eine Referenz wie hinein gehört.
Es gibt ja durchaus mehrere Dokumentationen und da muss man dann genau überlegen, wo was hinein gehört. So gibt es neben der Dokumentation des Frameworks ja auch noch grundlegende Dokumentation: JLS / JVMS fällt mir da so ein. Da können gewisse Grundlagen auch erläutert sein, denn es gibt ja auch Schnittstellen zu eben genau diesen Dokumenten.

Aber das sind nur so Gedanken von mir und meine Sichtweise. Ich bin niemand, der dies festlegen würde oder da irgendwie ausschlaggebend wäre. Es gibt ja auch Wege, Vorschläge und Bugreports einzubringen, so dass man so Themen auch an Verantwortliche herantragen kann. Auch wichtig: Meine Sichtweise sollte nicht wertend aufgefasst werden. Jeder soll so arbeiten, wie er möchte. Und die Anforderungen an jemanden, der Vollzeit Entwickler ist, dürfte anders sein als die Anforderung an einen Hobby-Entwickler oder Anfänger.


----------



## ernst (17. Feb 2016)

1)


kneitzel hat gesagt.:


> Also ich sehe hier mehrere Punkte:
> a) Wie geht man an eine Entwicklungsarbeit heran?
> ...


Danke für deine Sicht der Dinge

2)
Kannst du mir noch meine andere Frage im letzten Posting beantworten?
"wenn man _innerhalb_ der while-Schleife (wie oben vorgeschlagen wurde) eine Objektvariable vom Typ Scanner deklariert und initialisiert, dann hat man den von dir geschilderten Fall: mehrere Abnehmer auf dem gleichen Stream ( = Tastaturpuffer). Ist das also ein no-go ?"

mfg
E


----------



## kneitzel (17. Feb 2016)

Ja, das ist ein no-go. Denn zum einen setzt man auf einen Stream nicht mehrfach auf (IMHO) und zum anderen wird eine Closable Instanz nicht geschlossen. (Sobald Du das machst, würde dieses Konstrukt ehh nicht mehr funktionieren.)


----------



## ernst (17. Feb 2016)

kneitzel hat gesagt.:


> Ja, das ist ein no-go. Denn zum einen setzt man auf einen Stream nicht mehrfach auf (IMHO) und zum anderen wird eine Closable Instanz nicht geschlossen. (Sobald Du das machst, würde dieses Konstrukt ehh nicht mehr funktionieren.)


1)
Was meinst du mit "zum anderen wird eine Closable Instanz nicht geschlossen"?
2)
In meinem Programm wurde _außerhalb_ einer while-Schleife deklariert:
Scanner sc = new Scanner(System.in);
Wo wurde diese geschlossen?
Meinst du so etwas wie sc.close() ??

mfg
E


----------



## kneitzel (17. Feb 2016)

https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html
"All Implemented Interfaces:
Closeable, AutoCloseable, Iterator<String>"

Das wichtige Interface ist AutoCloseable:
https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html
"A resource that must be closed when it is no longer needed."

Somit steht die Aussage aus der Dokumentation im Raum, dass Scanner geschlossen werden muss, wenn es nicht mehr benötigt wird.

Und Du hast Scanner *nicht* geschlossen. Das ist ein Kritikpunkt. Mag man drauf verzichten wollen, gerade bei kleinen Applikationen. Was interessieren mich Ressourcen, wenn die App nach 1 Min ehh wieder beendet ist? Das ist dann dieses "Jeder soll so entwickeln, wie er möchte oder es für richtig findet". Aber bei Diskussionen um Clean Code und so fällt dies meiner Meinung nach durch (Ist meine Meinung, mag auch andere Meinungen geben. Aber die Dokumentation habe ich jetzt einfach einmal wieder gegeben in kleinen Auszügen.)


----------



## ernst (18. Feb 2016)

kneitzel hat gesagt.:


> https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html
> "All Implemented Interfaces:
> Closeable, AutoCloseable, Iterator<String>"
> 
> ...



1)
Ich gebe dir natürlich vollkommen recht, daß man sauberen Code produzieren soll und die Regeln und Empfehlungen der Doku beachten muß, sonst kann man sich nicht beklagen, wenn es irgendwo klemmt.

2)
In dem Programm unten habe ich meine frühere Version etwas abgeändert.
In der while-Schleife wird ein Scannerobjekt immer wieder angelegt, dann aus dem Tastaturpuffer gelesen und danach das Scannerobjekt wieder geschlossen (zerstört) und damit der Tastaturpuffer wieder gelöscht.
Warum geht das Programm aber wieder in eine Endlosschleife, wenn man z.B. "e" eingibt?

mfg
E




```
package scannerproblem2;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;
import java.util.Scanner;
public class Startklasse {
  public static void main(String[] args) {
  System.out.println("Eingabe Integer:");
  System.out.println("Ihre Eingabe war: " + getInput());
  }

 
  protected static int getInput() {
    boolean isCorrectInput = false;
    int inputValue = 0;
    // Scanner sc = new Scanner(System.in);

    while (!isCorrectInput) {
      Scanner sc = new Scanner(System.in); 
      try {
        inputValue = sc.nextInt();
        isCorrectInput = true;
      } catch (InputMismatchException ex) {
        System.out.println("Eingabe war vom Typ InputMismatchException, bitte erneut eingeben:");
      } catch (NoSuchElementException ex) {
      System.out.println("Eingabe war vom Typ NoSuchElementException, bitte erneut eingeben:");
      } catch (IllegalStateException ex) {
      System.out.println("Eingabe war vom Typ IllegalStateException, bitte erneut eingeben:");
      }
      sc.close(); 
    }
  return inputValue;
  }
}
```


----------



## thecain (18. Feb 2016)

Es ist davon abzuraten in einem Loop mehere Scanner zu erstellen. v.a. wenn System.in geschlossen wird.
Wenn du den internen Buffer löschen willst, kannst du dies mit einem nextLine machen.

Beispiel:

```
public class Startklasse {
    public static void main(String[] args) {
        System.out.println("Eingabe Integer:");
        System.out.println("Ihre Eingabe war: " + getInput());
    }


    protected static int getInput() {
        boolean isCorrectInput = false;
        int inputValue = 0;
        // Scanner sc = new Scanner(System.in);
        Scanner sc = new Scanner(System.in);
        while (!isCorrectInput) {

            try {
                inputValue = sc.nextInt();
                isCorrectInput = true;
            } catch (InputMismatchException ex) {
                System.out.println("Eingabe war vom Typ InputMismatchException, bitte erneut eingeben:");
            } catch (NoSuchElementException ex) {
                System.out.println("Eingabe war vom Typ NoSuchElementException, bitte erneut eingeben:");
            } catch (IllegalStateException ex) {
                System.out.println("Eingabe war vom Typ IllegalStateException, bitte erneut eingeben:");
            }
            sc.nextLine();
        }
        sc.close();
        return inputValue;
    }
}
```


----------



## kneitzel (18. Feb 2016)

Bitte schau genau, welche Exceptions kommen und lies nach, was die Exception bedeutet.
Nur die erste Exception kommt wegen dem "e". Danach kommt eine andere Exception. Und der solltest Du einmal nachgehen.
Die Antwort steht in der Doku vom Scanner - wichtig ist, was die Funktion close() alles macht.


----------



## ernst (18. Feb 2016)

kneitzel hat gesagt.:


> Bitte schau genau, welche Exceptions kommen und lies nach, was die Exception bedeutet.
> Nur die erste Exception kommt wegen dem "e". Danach kommt eine andere Exception. Und der solltest Du einmal nachgehen.
> Die Antwort steht in der Doku vom Scanner - wichtig ist, was die Funktion close() alles macht.



Es wird genau eine Exception
"InputMismatchException" ausgegeben und dann beliebig viele Exceptions
"NoSuchElementException".
Die Antwort in der Doku:
"When a Scanner is closed, it will close its input source if the source implements the Closeable interface."
hilft mir da nicht weiter und die Antwort ( bei nextInt() )
"NoSuchElementException - if input is exhausted"  sagt mir auch nicht, warum es zur Endlos-Schleife kommt.

mfg
E


----------



## kneitzel (18. Feb 2016)

Also wenn ein Stream geschlossen worden, dann kann aus diesem nicht mehr gelesen werden. Also ist folgendes passiert:
- Du öffnest einen Scanner auf System.in. Soweit ganz gut.
- Du rufst nextInt() auf. Dadurch liest der Scanner so lange von System.in, bis eine Zeile eingegeben wurde und die dann ausgewertet werden kann (Mindestens die Zeile ist dann im Scanner und nicht mehr im Stream. Das ist aber nebensächlich).
- Beim Auswerten fällt dem Scanner nun auf, dass es kein Integer ist. Also wird die InputMismatchException geworfen. Die Eingabe bleibt aber weiterhin im Scanner.
- Nun rufst Du close auf dem Scanner auf. Dadurch wird System.in auch geschlossen.
- Hinweis: Wenn Du nach dem close() noch einmal versuchst, etwas zu lesen, würdest Du die IllegalStateException bekommen. Scanner ist in einem Status, in dem dieses Lesen nicht mehr erlaubt ist.
- Nun erstellst Du erneut einen Scanner auf System.in. System.in ist geschlossen, d.h. da sind keine Zeichen mehr zu lesen.
- Nun rufst Du nextInt aus. Da aber keine Zeichen mehr gelesen werden können (beim Lesen aus einer Datei entspräche dies etwa dem Dateiende), kommt nun die NoSuchElementExeption. Es gibt kein Element mehr, das gelesen / ausgewertet werden könnte.
- Nun rufst du wieder close auf, das ändert aber nicht mehr viel, da der Stream ja schon geschlossen ist. 
- Nun fängst Du wieder bei dem Punkt mit einem neuen Scanner an. Das am Stream hat sich ja nichts geändert, so dass Du wieder in die NoSuchElementException hinein läufst.


----------



## ernst (19. Feb 2016)

kneitzel hat gesagt.:


> Also wenn ein Stream geschlossen worden, dann kann aus diesem nicht mehr gelesen werden. Also ist folgendes passiert:
> ...
> - Nun rufst Du close auf dem Scanner auf. Dadurch wird System.in auch geschlossen.geändert...



Danke für diese tiefgehenden Infos.
1)
"Nun rufst Du close auf dem Scanner auf. Dadurch wird System.in auch geschlossen."
Ok, aber in der Doku des Scanner steht:
"When a Scanner is closed, it will close its input source if the source implements the Closeable interface"
Wo hat aber System das Interface Closeable implementiert.
In der Doku habe ich nicht entdeckt

2)
Ein einziges sc.close() bewirkt also, daß man nichts mehr überTastatur eingeben kann.
Das verwudert mich etwas.
Deswegen meine Frage:
Wie kann man System.in wieder öffnen, 
damit wieder etwas über Tastatur eingegeben werden kann?

mfg
E



eingeben


----------



## thecain (19. Feb 2016)

ernst hat gesagt.:


> Wie kann man System.in wieder öffnen,
> damit wieder etwas über Tastatur eingegeben werden kann?


Nein, deswegen soll man den auch nicht schliessen, wenn man ihn noch braucht. Aber eine Lösung wie es Funktioniert hab ich ja gepostet.

Wenn du unbedingt was basteln willst, kannst du noch den Übergabewert an den Scanner anpassen.
http://stackoverflow.com/questions/14962082/close-scanner-without-closing-system-in


----------



## kneitzel (19. Feb 2016)

Also um auf Deine Fragen einzugehen:
1) Schauen wir uns System an: https://docs.oracle.com/javase/7/docs/api/java/lang/System.html
in ist ein InputStream: https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html
und implementiert Closable, Autoclosable.

2) System.in ist nicht so einfach neu zu öffnen. Daher sollte man es nicht schließen. Daher ist die Lösung auch niemals, einfach ein neues Scanner Objekt zu öffnen. Statt dessen öffnet man einen Scanner und behält diese eine Instanz, so lange man eben diese benötigt.
Und wenn dann ein Problem wie in diesem Thread auftritt, dass ein nextInt eben eine TypeMismatchException wirft, dann kümmert man sich einfach um die falsche Eingabe. Diese könnte per skip übersprungen oder z.B. durch das Auslesen der ganzen Zeile verarbeitet werden.


----------



## ernst (20. Feb 2016)

kneitzel hat gesagt.:


> Also um auf Deine Fragen einzugehen:
> 1) Schauen wir uns System an: https://docs.oracle.com/javase/7/docs/api/java/lang/System.html
> in ist ein InputStream: https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html
> und implementiert Closable, Autoclosable.


Du hast recht!

[/QUOTE]
2) System.in ist nicht so einfach neu zu öffnen. Daher sollte man es nicht schließen. Daher ist die Lösung auch niemals, einfach ein neues Scanner Objekt zu öffnen. Statt dessen öffnet man einen Scanner und behält diese eine Instanz, so lange man eben diese benötigt.
[/QUOTE]
Ok, aber angenommen man schließt System.in mit z.B. sc.close().
Ein einziges sc.close() bewirkt also, daß man nichts mehr überTastatur eingeben kann.
Kann man dann _nie_ mehr innerhalb seines _ganzen_ Quellcodes etwas überTastatur eingeben?
Oder gilt das nur auf die umgebende Methode (und wenn ja warum) ?

mfg
E


----------



## kneitzel (20. Feb 2016)

Nein, es ist nicht möglich, die Console erneut zu öffnen. Und hier würde auch die Regel gelten, dass Du System.in nicht zu schließen hast, da Du es nicht geöffnet hast.

Wenn Du viel mit dem scanner arbeiten willst und musst, dann würde ich hier evtl. eine eigene Klasse schreiben, die einen Scanner hält und diesen für alle verfügbar macht. Ein Singleton Pattern würde mir da gerade in den Sinn kommen. Wichtig wäre, dass man hier auch Dinge wie Multithreading berücksichtigt.

Es gibt da viele Ideen und Vorschläge. http://stackoverflow.com/questions/...ossible-to-re-open-system-in-after-closing-it wäre so ein Thread. Die vorgeschlagene Lösung dort ist aber in meinen Augen nicht wirklich akzeptabel aus Gründen, die wir ja prinzipiell hier schon erörtert haben. (Dort wird dann einfach das close() überschrieben und so lassen sich dann viele Scanner auf einem Stream öffnen. Nur jeder Scanner hat dann halt auch seinen internen Buffer und Daten, die einmal gelesen wurden vom Stream stehen dann einem anderen Scanner nicht mehr zur Verfügung. Die Gefahr eines Datenverlustes ist also denkbar. 

Scanner selbst ist final, daher kann man davon nicht ableiten. Aber eine Klasse GlobalConsoleScanner wäre denkbar, die einen private Konstruktor hat, einen getScanner() Aufruf der halt die Singleton Instanz erzeugt und dann alle Funktionen von Scanner erneut implementiert und der die Aufrufe 1:1 an den internen Scanner weiter gibt. Ein close() gibt es dann nicht und es wird auch keinen parametrisierten Konstruktor geben.

Aber das ist auch nur etwas, das ich so aus dem Stehgreif behaupten würde. Das Design müsste man sich genau ansehen um dann evtl. weitere Verbesserungen zu machen.

Konrad


----------

