Lecture Notes - Functions
Lecture Notes - Functions
Functions
The loop shown will print out " Hello world !" ten times, which is good. However, this also has its
drawbacks. Suppose that you wish to print another message, say "Hello friend!", twenty times, in addition
to the above. Using just loops, there's only one way to do it: write another loop as follows —
Notice that the two loops above are very similar: they print a message, and they do it a certain number of
times. Apart from the message and the number of times the loops are executed, the two loops are
identical. But, you can ask if Java gives us a way to effectively capture this identicalness, which we can use
to our benefit. Turns out that functions (or more precisely, methods, in Java terminology) give us exactly
what we are looking for.
Functions are reusable pieces of code, which we can use as per our needs anywhere and any number of
times, of course, by not violating the basic rules of the Java language.
When discussing methods, we talk about their creation (called method definition), and their use (called
method call or method invocation).
You learnt two major types of functions, ones that returned a value and the others that didn’t. So, the first
method you looked at was printHelloWorld. Here is the code for this function:
public static void printHelloWorld() {
System.out.println("Hello World!");
}
This function, when called inside the main function, printed “Hello world!”. This function did not return any
value, which could be stored in a variable or further acted upon. This was a standalone function. When
called, it would execute the statement inside it but nothing more.
Then you looked at another function that returned a variable:
public static int test() {
int variable = 9;
return variable;
}
Defining Functions
Let's look at function definitions a bit more in detail. Here are the important components of a function
definition:
● Name: A method has a name, which is used to refer to it. In the example, the name of the methods
were printHelloWorld() and test(). A method can only be called by the proper name that is assigned
to it.
● Body: The body of a method captures whatever the method is supposed to do. It refers to the
statements inside the “{ } “ brackets.
● Return type: This signifies the type of the value the function computes. In the printHelloWorld
example, it didn’t return any data, so the return type was void. In the other example, the return
type was integer, and it was used to return an integer value.
● Access specifier: The public keyword in the method definition is the access specifier. You will learn
more about this in the next lectures. For now, all our methods in this session are public.
● Storage class specifier: The keyword storage in the method definition is called the storage class
specifier. You will learn more about this in the next lectures. In this lecture, all the methods are
static.
But, these functions you just went through were independent from the rest of the code inside the main
body. Moreover, such functions have very limited usage. You need functions to interact with the code.
The function should be able to take values from some variables and perform some operations on them.
Then, it will return something or display some result based on these values. Ideally, this is how it will help
you even more.
This could be done by using parameters, i.e., by passing input variables into the method.
After this, you went through another example where you used a function to compare two values passed
into it.
String result;
if (var1 > var2) {
result = var1 + " is larger than " + var2;
} else if (var1 == var2) {
result = "Both numbers are equal";
} else {
result = var2 + " is larger than " + var1;
}
return result;
}
}
Then, you saw how to read the content of your files using code. Here, we used the text “Alice in
Wonderland” to perform various operations. The operations included reading a single line, reading all lines,
and reading all words from a file.
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
You also learnt that a sentence different from a line. The ‘Enter’, or the hidden new line expression ‘\n’,
after each line, is recognised by the scanner in the code, and used to separate one line from another.
When instead of lines, you had to read all the words from the file, you used the hasNext() function instead
of hasNextLine().
Exceptions
Let’s recall what happens when you run a program:
However, when the program is running, it may run into problems or errors and stop its execution.
So, let’s look at an example where your compiler returns an exception error when running the code:
The exception was ‘main’ java.lang.ArithmeticException: / by zero. It states that there is a mathematical or
arithmetic error, and it is due to an attempted division by zero. This was because, division by 0 is not
defined.
The rest of the code, after this exception, is not executed. The code executes up to this point and stops
executing after this exception is encountered. Here, you understood what the source of the error could be.
However, the code following the source of the exception will not always be dependent on the source, i.e.
suppose that this division statement was independent of the rest of the code and was only a small part of
it. In that case, it’s not fair to stop the execution of the rest of the code just because there is an error in a
small independent part.
To wrap your head around this, you need to understand the concept of ‘exceptions’.
An exception, by definition, occurs when the flow of a program is disrupted and ends abnormally or
unexpectedly. You don’t want the program to end abnormally; instead, you need it to tell you what error
occurred, if any. For example, exceptions may occur when the user enters invalid data, or when a file
cannot be found. Exceptions may also be caused by a user input or a flaw in the program logic.
You handled the exception in the program above by using a ‘try and catch’ block, as shown below:
try {
int data = 50 / 0;
} catch (Exception e) {
System.out.println(e);
}
So, to catch the error and continue the code execution after the error, we’ll use the following try and catch
block:
try {
// code that can throw exceptions
} catch (Exception e) {
System.out.println(e);
}
A ‘try’ block is always followed by a ‘catch’ block. A try block contains the code where exceptions may
arise. If the exception occurs, it is caught by the catch block. However, the program doesn’t stop
altogether. Instead, the rest of the code in the catch block is executed. In other words, the catch block
protects your program from crashing completely and can help your program recover or terminate
gracefully from the unexpected exception.
There are two common types of exceptions:
1. Checked exceptions: These exceptions have to be taken care of while writing the program, since
they are checked at the time of compilation. If a statement in your code throws a checked
exception, then there must be a statement to either handle the exception, or you must specify the
exception using the ‘throws’ keyword. Otherwise, the program will give you a compilation error.
2. Unchecked exceptions: These exceptions are not checked during the program compilation. Hence,
there will be no compilation error even if they are not checked. But will cause your program to
crash and terminate unexpectedly.
You can handle checked exceptions using either the try and catch block or the Throws keyword, which
helps you specify the exception. When reading content from a file, you would recall that we wrote the
following highlighted statements:
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
This is because when you use the scan(file) statement, it throws an exception, FileNotFoundException. This
exception is checked during the program’s compilation. Hence, if there is no provision in the code to
handle it, or it isn’t specified, then there is a compilation error.
You can also write the above code using try and catch:
import java.util.Scanner;
import java.io.File;
try {
//creating File instance to reference text file in Java
File inputfile = new File("C:\\Users\\path");
Scanner scan = new Scanner(inputfile);
while (scan.hasNextLine() == true) {
String line = scan.nextLine();
System.out.println(line);
}
scan.close();
} catch (Exception e) {
System.out.print("File not found");
}
}
}
Now, you’ve written the part of the program that can throw an exception into the try block, so the program will try
and execute it. But if an exception is thrown, the exception will be intercepted or caught by the catch block, and the
instruction in the catch block will then be executed.
A ‘try and catch’ block is especially useful in cases where you need to read from a file. This checks if the exception
can be handled at the time of compilation. At other times, it is good to have this in place, especially if the code is
long and contains several independent parts. It helps you to identify which part threw the exception.
Then, you used the try and catch block to create a program that writes its output to a file:
import java.util.Scanner;
import java.io.FileWriter;
When you are building a large program, there may be many independent parts in the program that can
work independently from one another. Therefore, you don’t want an exception in one part of the program
to cause the entire program to crash completely, especially when other independent parts of the program
can continue to execute without issues.
Therefore, it is a good practice to wrap your code around try catch blocks, especially around pieces of code
that you suspect may cause exceptions. This will help you quarantine code that causes unexpected
exceptions, and prevent it from affecting other parts of the program.
Specifically, by wrapping code and exceptions within try catch blocks, you will be able to:
● allow other parts of the program to continue operation without interruption or
● allow other parts of the program to finish whatever they are working on and let the program reach
a state to terminate gracefully