How to Try to Get Input Again After Exception Java
Object-Oriented Programming with Java, part I + II
This material is licensed under the Creative Commons BY-NC-SA license, which means that you can use it and distribute it freely so long as you do not erase the names of the original authors. If you make changes in the material and want to distribute this altered version of the material, you have to license it with a similar free license. The use of the material for commercial use is prohibited without a separate agreement.
Authors: Arto Hellas, Matti Luukkainen
Translators to English: Emilia Hjelm, Alex H. Virtanen, Matti Luukkainen, Virpi Sumu, Birunthan Mohanathas, Etiënne Goossens
Extra material added by: Etiënne Goossens, Maurice Snoeren, Johan Talboom
The course is maintained by De Haagse Hogeschool
7. Input/output and exception handling
7.1 Exceptions
Exceptions are such situations where the program executions is different from our expectations. For instance, the program may have called a method of a null reference, in which case the user is thrown a NullPointerException
. If we try to retrieve a index outside a table, the user is thrown a IndexOutOfBoundsException
. All of them are a type of Exception
.
Exceptions are often caused by unexpected situations that occur when handling input and output of a program. For example: users can easily frustrate a program by entering input in an unexpected format.
We deal with exceptions by using the block try { } catch (Exception e) { }
. The code contained within the brackets which follows the keyword try
will not crash when an exception occurs in this part of the code. The code within the brackets which follow the keyword catch
defines what should happen when the try-code throws an exception. We also define the type of the exception we want to catch (catch (Exception e)
).
try { // code which can throw an exception } catch ( Exception e ) { // code which is executed in case of exception }
The parseInt method of class Integer
which turns a string into a number can throw a NumberFormatException
if its string parameter cannot be turned into a number. Now we implement a program that turns a user input String to a number.
Scanner reader = new Scanner ( System . in ); System . out . print ( "Write a number: " ); int num = Integer . parseInt ( reader . nextLine ());
Write a number: ~~tatti~~ ~~Exception in thread "..." java.lang.NumberFormatException: For input string: "tatti"~~
The program above throws an exception because the user digits an erroneous number. The program execution ends up with a malfunction, and it cannot continue. We add an exception management statement to our program. The call, which may throw an exception is written into the try
block, and the action which takes place in case of exception is written into the catch
block.
Scanner reader = new Scanner ( System . in ); System . out . print ( "Write a number: " ); try { int num = Integer . parseInt ( reader . nextLine ()); } catch ( Exception e ) { System . out . println ( "You haven't written a proper number." ); }
Write number: ~~5~~
Write number: ~~oh no!~~ You haven't written a proper number.
In case of exception, we move from the chunk of code defined by the try
keyword to the catch
chunk. Let's see this by adding a print statement after the Integer.parseInt
line in the try
chunk.
Scanner reader = new Scanner ( System . in ); System . out . print ( "Write a number: " ); try { int num = Integer . parseInt ( reader . nextLine ()); System . out . println ( "Looks good!" ); } catch ( Exception e ) { System . out . println ( "You haven't written a proper number." ); }
Write a number: ~~5~~ Looks good!
Write a number: ~~I won't!~~ you haven't written a proper number.
String I won't! is given as parameter to the method Integer.parseInt
, which throws an exception if the String
parameter can't be changed into a number. Note that the code in the catch
chunk is executed only in case of exception – otherwise the program do not arrive till there.
Let's make something more useful out of our number translator: let's do a method which keeps on asking to type a number till the user does it. The user can return only if they have typed the right number.
public int readNumber ( Scanner reader ) { boolean running = true ; while ( running ) { System . out . print ( "Write a number: " ); try { int num = Integer . parseInt ( reader . nextLine ()); return num ; } catch ( Exception e ) { System . out . println ( "You haven't written a proper number." ); } } }
The method readNumber
could work in the following way:
Write a number: ~~I won't!~~ You haven't written a proper number. Write a number: ~~Matti has a mushroom on his door.~~ You haven't written a proper number. Write a number: ~~43~~
7.1.1 Throwing Exceptions
Methods and constructors can throw exceptions. So far, there are two kinds of exceptions which can be thrown. There are the ones which have to be handled, and the ones which don't have to be dealt with. When we have to handle the exceptions, we do it either in a try-catch
chunk, or throwing them from a method
.
In the clock exercise of Introduction to Programming (not in the HHS-course), we explained that we can stop our program of one second, by calling the method Thread.sleep(1000)
. The method may throw an exception, which we must deal with. In fact, we handle the exception using the try-catch
sentence; in the following example we skip the exception, and we leave empty the catch
chunk.
try { // we sleep for 1000 milliseconds Thread . sleep ( 1000 ); } catch ( Exception e ) { // In case of exception, we do not do anything. }
It is also possible to avoid handling the exceptions in a method, and delegate the responsibility to the method caller. We delegate the responsibility of a method by using the statement throws Exception
.
public void sleep ( int sec ) throws Exception { Thread . sleep ( sec * 1000 ); // now we don't need the try-catch block }
The sleep
method is called in another method. Now, this other method can either handle the exception in a try-catch
block or delegate the responsibility forward. Sometimes, we delegate the responsibility of handling an exception till the very end, and even the main
method delegates it:
public class Main { public static void main ( String [] args ) throws Exception { // ... } }
In such cases, the exception ends up in Java's virtual machine, which interrupts the program in case there is an error which causes the problem.
There are some exceptions which the programmer does not always have to address, such as the NumberFormatException
which is thrown by Integer.parseInt
. Also the RuntimeExceptions
do not always require to be addressed. These are two examples of exceptions that don't have to be dealt with. The compiler cannot 'predict' that an exception will occur, so it does not force you to catch the exception.
We can throw an exception by using the throw
statement. For instance, if we want to throw an exception which was created in the class NumberFormatException
, we could use the statement throw new NumberFormatException()
.
Another exception which hasn't got to be addressed is IllegalArgumentException
. With IllegalArgumentException
we know that a method or a constructor has received an illegal value as parameter. For instance, we use the IllegalArgumentException
when we want to make sure that a parameter has received particular values.
In the example below we have the constructor public Grade(int grade). A constructor is a special method that will be explained later on when we introduce classes. For now you can consider it as a 'regular' method that expects parameters like any other method.
public class Grade { private int grade ; public Grade ( int grade ) { this . grade = grade ; } public int getGrade () { return this . grade ; } }
Next, we want to validate the value of the constructor parameter of our Grade
class. The grades in Finland are from 0 to 5. If the grade is something else, we want to throw an exception. We can add an if statement to our Grade class constructor, which checks whether the grade is outside range 0-5. If so, we throw an IllegalArgumentException
telling throw new IllegalArgumentException("The grade has to be between 0-5");
.
public class Grade { private int grade ; public Grade ( int grade ) { if ( grade < 0 || grade > 5 ) { throw new IllegalArgumentException ( "The grade has to be between 0-5" ); } this . grade = grade ; } public int getGrade () { return this . grade ; } }
Grade grade = new Grade ( 3 ); System . out . println ( grade . getGrade ()); Grade wrongGrade = new Grade ( 22 ); // it causes an exception, we don't continue
3 Exception in thread "..." java.lang.IllegalArgumentException: The grade has to be between 0-5
7.1.2 The Exception Information
The catch
block tells how we handle an exception, and it tells us what exception we should be prepared for: catch (Exception e)
. The exception information is saved into the e
variable.
try { // the code, which may throw an exception } catch ( Exception e ) { // the exception information is saved into the variable e }
The class Exception
can provide useful methods. For instance, the method printStackTrace()
prints a path which tells us where the exception came from. Let's check the following error printed by the method printStackTrace()
.
Exception in thread "main" java.lang.NullPointerException at package.Class.print(Class.java:43) at package.Class.main(Class.java:29)
Reading the stack trace happens button up. The lowest is the first call, i.e. the program execution has started from the main()
method of class Class
. At line 29 of the main method of Class
, we called the method print()
. Line 43 of the method print
caused a NullPointerException
. Exception information are extremely important to find out the origin of a problem.
Exercise 5-1: Method Argument Validation
Let's train method argument validation with the help of the
IllegalArgumentException
. The excercise layout shows two classesPerson
andCalculator
. Change the class in the following way:Exercise 5-1.1: Person Validation
The constructor of
Person
has to make sure its parameter'sname
variable is not null, empty, or longer than 40 characters. The age has also to be between 0-120. If one of the conditions above are not satisfied, the constructor has to throw anIllegalArgumentException
.Exercise 5-1.2: Calculator Validation
The
Calculator
methods have to be changed in the following way: the methodmultiplication
has to work only if its parameter is not negative (greater than or equal to 0). The methodbinomialCoefficient
has to work only if the parameters are not negative and the size of a subset is smaller than the set's size. If one of the methods receives invalid arguments when they are called, they have to throw aIllegalArgumentException
.Exercise 5-1.3: Exception handling
The
main
method crashes when anIllegalArgumentException
is thrown. Add proper exception handling so that the main method does not crash any more. Print the exception information, so that the program shows what goes wrong. NB: This part of the exercise is not verified automatically.
7.2 Reading from a file
A relevant part of programming is related to stored files, in one way or in another. Let's take the first steps in Java file handling. Java's API provides the class File, whose contents can be read using the already known Scanner class.
If we read the desciption of the File
API we notice the File
class has the constructor File(String pathname)
, which creates a new File instance by converting the given pathname string into an abstract pathname. This means the File
class constructor can be given the pathname of the file we want to open.
In the programming environment, files have got their own tab called Files, which contains all our project files. If we add a file to a project root – that is to say outside all folders – we can refer to it by writing only the its name. We create a file object by giving the file pathname to it as parameter:
File file = new File ( "file-name.txt" );
The System.in
input stream is not the only reading source we can give to the constructor of a Scanner
class. For instance, the reading source can be a file, in addition to the user keyboard. Scanner
provides the same methods to read a keyboard input and a file. In the following example, we open a file and we print all the text contained in the file using the System.out.println
statement. At the end, we close the file using the statement close
.
// The file we read File file = new File ( "filename.txt" ); Scanner reader = new Scanner ( file ); while ( reader . hasNextLine ()) { String line = reader . nextLine (); System . out . println ( line ); } reader . close ();
The Scanner
class constructor public Scanner(File source) (Constructs a new Scanner that produces values scanned from the specified file.) throws a FileNotFoundException when the specified file is not found. The FileNotFoundException
is different than RuntimeException,
and we have either to handle it or throw it forward. At this point, you only have to know that the programming environment tells you whether you have to handle the exception or not. Let's first create a try-catch block where we handle our file as soon as we open it.
public void readFile ( File f ) { // the file we read Scanner reader = null ; try { reader = new Scanner ( f ); } catch ( Exception e ) { System . out . println ( "We couldn't read the file. Error: " + e . getMessage ()); return ; // we exit the method } while ( reader . hasNextLine ()) { String line = reader . nextLine (); System . out . println ( line ); } reader . close (); }
Another option is to delegate the exception handling responsibility to the method caller. We delegate the exception handling responsibility by adding the definition throws ExceptionType
to the method. For instance, we can add throws Exception
because the type of all exceptions is Exception
. When a method has the attribute throws Exception
, whatever chunk of code which calls that method knows that it may throw an exception, and it should be prepared for it.
public void readFile ( File f ) throws Exception { // the file we read Scanner reader = new Scanner ( f ); while ( reader . hasNextLine ()) { String line = reader . nextLine (); System . out . println ( line ); } reader . close (); }
In the example, the method readFile
receives a file as parameter, and prints all the file lines. At the end, the reader is closed, and the file is closed with it, too. The attribute throws Exception
tells us that the method may throw an exception. Same kind of attributes can be added to all the methods that handle files.
Note that the Scanner
object's method nextLine
returns a string, but it does not return a new line at the end of it. If you want to read a file and still maintain the new lines, you can add a new line at the end of each line:
public String readFileString ( File f ) throws Exception { // the file we read Scanner reader = new Scanner ( f ); String string = "" ; while ( reader . hasNextLine ()) { String line = reader . nextLine (); string += line ; string += "\n" ; } reader . close (); return string ; }
Because we use the Scanner
class to read files, we have all Scanner
methods available for use. For instance the method hasNext()
returns the boolean value true if the file contains something more to read, and the method next()
reads the following word and returns a String
object.
The following program creates a Scanner
object which opens the file file.txt. Then, it prints every fifth word of the file.
File f = new File ( "file.txt" ); Scanner reader = new Scanner ( f ); int whichNumber = 0 ; while ( reader . hasNext ()) { whichNumber ++; String word = reader . next (); if ( whichNumber % 5 == 0 ) { System . out . println ( word ); } }
Below, you find the text contained in the file, followed by the program output.
Exception handling is the process of responding to the occurrence, during computation, of exceptions – anomalous or exceptional events requiring special processing – often changing the normal flow of program execution. ...
process occurrence, – requiring changing program
7.2.1 Character Set Issues
When we read a text file (or when we save something into a file), Java has to find out the character set used by the operating system. Knowledge of the character set is required both to save text on the computer harddisk in binary format, and to translate binary data into text.
There have been developed standard character sets, and "UTF-8" is the most common nowadays. UTF-8 character set contains both the alphabet letters of everyday use and more particular characters such as the Japanese kanji characters or the information need to read and save the chess pawns. From a simplified programming angle, we could think a character set both as a character-number hashmap and a number-character hashmap. The character-number hashmap shows what binary number is used to save each character into a file. The number-character hashmap shows how we can translate into characters the values we obtain reading a file.
Almost each operating system producer has also got their own standards. Some support and want to contribute to the use of open source standards, some do not. If you have got problems with the use of Scandinavian characters such as ä and ö (expecially Mac and Windows users), you can tell which character set you want to use when you create a Scanner
object. In this course, we always use the the "UTF-8" character set.
You can create a Scanner object which to read a file which uses the UTF-8 character set in the following way:
File f = new File ( "examplefile.txt" ); Scanner reader = new Scanner ( f , "UTF-8" );
Anther thing you can do to set up a character set is using an environment variable. Macintosh and Windows users can set up an the value of the environment variable JAVA_TOOL_OPTIONS
to the string -Dfile.encoding=UTF8
. In such case, Java always uses UTF-8 characters as a default.
Exercise 5-2: Printer
Fill in the gaps in the class
Printer
It has a constructor
public Printer(String t)
which receives aString
standing for the file name. It opens the file, reads all the lines and puts them into an ArrayList.The method
public void printLinesWhichContain(String word)
loops through the ArrayList and prints the lines that contain the word that is passed as a parameter. The lines are printed in the same order as they are inside the file. (lower and upper case make difference in this excercise; for instance, "test" and "Test" are not the considered equal);If the argument is an empty
String
, all of the file is printed.If the file is not found, the method delegates the exception with no need for a try-catch statement; the method simply has to be defined in the following way:
public Printer { public Printer ( String t ) throws Exception { // ... } // ... }
The file textFile has been place into the default package of your project to help the tests. When you define the file name of the constructor of
Printer
, you have to write src/textfile.txt. The file contains an extract of Kalevala, a Finnish epic poem:Siinä vanha Väinämöinen katseleikse käänteleikse Niin tuli kevätkäkönen näki koivun kasvavaksi Miksipä on tuo jätetty koivahainen kaatamatta Sanoi vanha Väinämöinen
The following example shows what the program should do:
Printer printer = new Printer ( "src/textfile.txt" ); printer . printLinesWhichContain ( "Väinämöinen" ); System . out . println ( "-----" ); printer . printLinesWhichContain ( "Frank Zappa" ); System . out . println ( "-----" ); printer . printLinesWhichContain ( "" ); System . out . println ( "-----" );
Prints:
Siinä vanha Väinämöinen Sanoi vanha Väinämöinen ----- ----- Siinä vanha Väinämöinen katseleikse käänteleikse Niin tuli kevätkäkönen näki koivun kasvavaksi Miksipä on tuo jätetty koivahainen kaatamatta Sanoi vanha Väinämöinen
In the project, you also find the whole Kalevala; the file name is src/kalevala.txt
Exercise 5-3: File Analysis
In this exercise, we create an application to calculate the number of lines and characters.
Implement the method readFile(). Read the file using a scanner and append all the lines to the variable 'str'. Add a 'new line' character at then end of every line.
You can decide yourself what to do if the constructor parameter file does not exist.
The file testFile has been place into the test package of your project to help the tests. When you define the file name of the constructor of
Analysis
, you have to write test/testfile.txt. The file contains the following text:there are 3 lines, and characters because line breaks are also characters
The following example shows what the program should do:
File file = new File ( "test/testfile.txt" ); Analysis analysis = new Analysis ( file ); System . out . println ( "Lines: " + analysis . lines ()); System . out . println ( "Characters: " + analysis . characters ());
Lines: 3 Characters: 74
7.3 Writing a File
In section 12.1, we learnt that reading from a file happened with the help of the classes Scanner
and File
. The class FileWriter provides the functionality to write to a file. The FileWriter
constructor is given as parameter a String
illustrating the file location.
FileWriter writer = new FileWriter ( "file.txt" ); writer . write ( "Hi file!\n" ); // the line break has to be written, too! writer . write ( "Adding text\n" ); writer . write ( "And more" ); writer . close (); // the call closes the file and makes sure the written text goes to the file
In the example we write the string "Hi file!" to the file "file.txt"; that is followed by a line break, and by more text. Note that when you use the write
method, it does not produce line breaks, but they have to be added later manually.
Both the FileWriter
constructor and the write
method may throw an exception, which has to be either handled or the responsibility has to be delegated to the calling method. The method which is given as parameter the file name and the text to write into it can look like the following.
public class FileHandler { public void writeToFile ( String fileName , String text ) throws Exception { FileWriter writer = new FileWriter ( fileName ); writer . write ( text ); writer . close (); } }
In the above method writeToFile
, we first create a FileWriter
object, which writes into the fileName
file stored at the location specified as parameter. After this, we write into the file using the write
method. The exception the constructor and write method can possibly throw has to be handled either with the help of a try-catch
block or delegating the responsibility. In the method writeToFile
the responsibility was delegated.
Let's create a main
method where we call the writeToFile
method of a FileHandler
object. The exception does not have to be handled in the main
method either, but the method can declare to throw possibly an exception throw the definition throws Exception
.
public static void main ( String [] args ) throws Exception { FileHandler handler = new FileHandler (); handler . writeToFile ( "diary.txt" , "Dear Diary, today was a nice day." ); }
When we call the method above, we create the file "diary.txt", where we write the text "Dear Diary, today was a nice day.". If the file exists already, the old content is erased and the new one is written. FileWriter
allows us to add text at the end of the already existing file by providing additional parameter boolean append
, without erasing the existing text. Let's add the method appendToFile()
to the class FileHandler
; the method appends the text received as parameter to the end of the file.
public class FileHandler { public void writeToFile ( String fileName , String text ) throws Exception { FileWriter writer = new FileWriter ( fileName ); writer . write ( text ); writer . close (); } public void appendToFile ( String fileName , String text ) throws Exception { FileWriter writer = new FileWriter ( fileName , true ); writer . write ( text ); writer . close (); } }
In most of the cases, instead of writing text at the end of a file with the method append
, it is easier to write all the file again.
Exercise 5-4: File Manager
Together with the exercise body, you find the class
FileManager
, which contains the method bodies to read a write a file.Exercise 5-4.1: File Reading
Implement the method
public ArrayList<String> read(String file)
to return the lines of the parameter file inArrayList
form, each file line being aString
contained by theArrayList
.There are two text files to help testing the project: src/testinput1.txt and src/testinput2.txt. The methods are supposed to be used in the following way:
public static void main ( String [] args ) throws FileNotFoundException , IOException { FileManager f = new FileManager (); for ( String line : f . read ( "src/testinput1.txt" )) { System . out . println ( line ); } }
The print output should look like the following
first second
Exercise 5-4.2: Writing a Line
Modify the method
public void save(String file, String text)
so that it writes the Stringtext
into thefile
. If the file already exists, overwrite its contents.Exercise 5-4.3: Writing an ArrayList
Modify the method
public void save(String file, ArrayList<String> texts)
so that it writes the content of the ArrayListtexts
into thefile
. If the file already exists, overwrite its contents.
end of week 5
Source: https://hhs-2018-se-dt-s2-apip.github.io/lesmateriaal/week5
0 Response to "How to Try to Get Input Again After Exception Java"
Postar um comentário