You are on page 1of 31

JAVA Development

Call stack, Exceptions, Enums,


Unit testing, TDD
Call Stack
Call stacks

When running programs, methods can call other methods a large number of times,
therefore forming call chains or call stacks.

All these calls are stored on a stack data structure (LIFO):


● first element on this stack: the call chain initiator - the method interested in the
end result and/or able to handle any unforeseen situation (exception)
● every time a method is called: a new element is pushed to the stack
● every time a method returns “normally”: it is removed from the stack
Call stacks - example

class CallStack { public static void main(String[] args) {


CallStack example = new CallStack();
void method1(int param) throws Exception { try {
// try { //call initiator - first in call stack
method2(param); example.method1(5);
// } catch (Exception e) { } catch (Exception e) {
// return; e.printStackTrace();
// } }
} }

void method2(int param) throws Exception {


method3(param); Result:
} java.lang.Exception: Exception!
at CallStack.method3(CallStack.java:18)
void method3(int param) throws Exception { at CallStack.method2(CallStack.java:14)
throw new Exception("Exception!"); at CallStack.method1(CallStack.java:7)
} at CallStack.main(CallStack.java:24)
}
Exceptions
Exceptions

Exception (exceptional event) = a problem that arises during program execution

● when an exception occurs the normal flow of the program is disrupted and the
program/application terminates abnormally
● (some of these) exceptions must be handled explicitly in code.

Example of exceptions:
● runtime: NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException
● checked: IOException, FileNotFoundException, SQLException
● errors: OutOfMemoryError, StackOverflowError, IOError
Exceptions

● Exceptions in Java = just regular classes but with some specific supertypes

● Exception type (and required handling) - determined by the place in the class hierarchy:
○ Throwable - base class from every “exceptional situation” -> checked

■ Error - a kind of unrecoverable situation, which usually ends in JVM crash (e.g.
out of memory, out of stack due to infinite recursion...) -> unchecked
● note: you should not try to handle/recover from these errors!

■ Exception - the base class for ALL Java exceptions


● RuntimeException - base class for all exceptions that might occur at
runtime -> unchecked
● all other exceptions -> checked
Exceptions
Exceptions - Checked vs Unchecked

Two major types of exceptions:

- Checked:
- require explicit handling by code:
- need to be declared by methods that might throw them
- need to be handled by caller methods (which call such method which may throw
exceptions), by either:
- catch the exception (to handle it locally)
- or: declare that it will throw it further
- are the ones that do not extend RuntimeException or Error

- Unchecked (or: runtime):


- don’t require explicit handling by code (but they may be handled explicitly...):
- can be freely thrown, methods don’t need to declare throwing them
- no restrictions on callers (not required to catch/handle these exceptions in any way)
- are the ones that extend RuntimeException or Error
Exceptions - How to deal with them?

Since checked exception are the most restrictive, we will refer mostly to them in the following.
try {
//do some work
} catch (Exception e) {
//do something in case of exception
} finally {
//do something, in any case
}

● this is a full exception handling block: try-catch-finally


● these can be used in various combinations: as above (full block), try-catch, try-finally,
and, starting with Java 7, only try (try-with-resources - will be discussed later)
Exceptions - Throwing

Declaring that a method throws exceptions:

void someMethod() throws ExceptionType1, ExceptionType2, … {


//…
if (someCondition) {
throw new ExceptionType1();
}
throw new ExceptionType2();
}

● exceptions can be thrown using the throw keyword on instances of a Throwable class
● methods which may throw a type of (checked) exceptions must declare this in the method’s signature
by using the throws keyword
● thrown exceptions must be handled by the calling method or else further thrown
○ if not handled, the JVM will eventually catch all exceptions (and terminate the thread)
Exceptions - Catching

Exceptions that are thrown by methods can be caught by other methods further down the call stack:

try {
someMethodThrowingExceptions();
} catch (ExceptionType1 | ExceptionType2 e) {
//do something for these exception types
} catch (Exception e) {
//do something for other exception types (still extending Exception), like logging
throw e; //rethrow the caught exception
}

● exceptions are caught using the catch keyword


● if two or more exception types are handled in the same way, they can be caught on the same branch,
separating their type with |
● from the catch clause they can be rethrown, even as different exceptions (provided that the method
signature declares the new type with ‘throws’)
Exceptions - Catching

● when catching exception, the compiler try {


matches them top-down in the catch list someMethod();
} catch (Exception e) {
} catch (IOException e) { //-> compile error:
● that’s why related exceptions (from the // IOException is more specific than Exception
same class hierarchy) must be caught }
from the most specific to the most
generic
try {
someMethod();
● as a general guideline, try to avoid
} catch (IOException e) { //more specific 1st!
catching top exception types directly, } catch (Exception e) { //more generic later
like Exception (or even worse: Error, //OK!!!
Throwable) }
Exceptions - Finally

But what if we need something always done, This is where finally comes into play:
even in case of exceptions?
try {
try { fridge.open();
fridge.open(); Eggs eggs = fridge.getEggs();
Eggs eggs = fridge.getEggs(); fridge.close();
fridge.close();
cook(eggs);
cook(eggs);
eat(eggs);
eat(eggs);
} finally {
cleanMess();
} catch (Exception e) { cleanMess();
cleanMess(); }
throw e;
} ● finally block is always executed (with few
possible exceptions)
We should always clean the mess, even when we
● it can be paired up with only a try, if
might have not successfully finished eating!
someone else handles the exception
Exceptions - General Considerations

Rules:
● Declaring thrown exceptions: declared types can be broader than the actually thrown
types (if I throw IOException, I can declare Exception - a supertype - in the signature)
● Overriding: the overriding method must declare that it throws same exceptions as the
base method, or some subtypes of them (can be of a narrower type)

General guidelines:
● Always better to perform some checks, when possible, rather than getting an exception
● Don’t use exceptions for regular control flow, but only for exceptional situations! (avoid
throwing them when not needed / other solutions exist)
● Don’t abuse runtime exceptions, even though they are easier to manage.
Enums
Enums

- An enum type is a special Java data type that allows variables to take values from a
specific set of predefined constants
- The variable must be equal to one of the valid ones defined by the enum
- Because they are constants, they respect the constant Java naming convention

enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}

- all enums extend java.lang.Enum


- because multiple inheritance is not possible, an enum cannot extend anything else
Enums - Examples

enum Day { class EnumTest {


MONDAY, TUESDAY, WEDNESDAY,
String tellItLikeItIs(Day day) {
THURSDAY, FRIDAY,
switch (day) {
SATURDAY, SUNDAY case MONDAY:
} return "Monday :(";

case FRIDAY:
return "Friday :)"

case SATURDAY:
case SUNDAY:
return "Weekend :D!!!";

default:
return "Midweek days…";
}
}
}
Enums - Examples

enum Planet {
EARTH (5.976e+24, 6.37814e6), An enum can also have properties:
MARS (6.421e+23, 3.3972e6);
● in this case, all values must define
private final double mass; //kg
private final double radius; //meters those properties
● the constructor must have private or
Planet(double mass, double radius) { package-level access
this.mass = mass;
this.radius = radius; ○ you cannot call an enum
} constructor yourself

static final double G = 6.673E-11;


● also methods can be added to work
double surfaceGravity() { with those properties
return G * mass / (radius * radius);
}
}
Unit Testing
Unit Testing

A unit = the smallest possible testable fragment of code


● Usually a method

Automated unit tests:


● Code that is designed to test these units, in isolation, that can be ran as many
times as wished

Unit testing framework:


● A library that makes writing such tests easier
Context of testing

What should unit tests cover?


● valid inputs
● invalid inputs
● errors, exceptions and events
● boundary conditions
● everything that might break
Context of testing

For the a method like: int divide(int i, int j);


● valid inputs: 10/2, 7/3, 100/-5
● invalid inputs: 100/0, 0/0
● errors, exceptions and events: can I pass null to the function?
● boundary conditions: 0/100, MAX_INT/100, 100/MAX_INT
● everything that might break: ???
Tests - Benefits

• much less debug time

• code proven to meet requirements

• near zero defects

• shorter development cycles


Tests as Documentation

• most devs do not read the written documentation for a software

• devs like to code and when they want to understand a class or operation, they
will first look at a sample code

• well-written unit tests prove a working specification of your code

• acceptance tests define exactly your critical requirements

• regression test suite, particularly with a test-first approach, effectively becomes


detailed executable specifications
Unit Tests

Most Java unit tests use JUnit:


import org.junit.Assert;
import org.junit.Test;

class DivideTests {

@Test
public void testZeroDividedByAnythingIsZero() {
Assert.assertEquals(0, Main.divide(0, 1));
Assert.assertEquals(0, Main.divide(0, 100));
Assert.assertEquals(0, Main.divide(0, -5));
}
}

Note: IntelliJ IDEA supports it out of the box.


Unit Testing

If it's worth building, it's worth testing.


If it's not worth testing, why are you wasting
your time working on it ?
Test Driven Development
TDD – main steps

● add a new Test


● run all tests, check that the
new one fails
● add/update some code
● run tests
● update code until new test
passes (+all others)

● refactor code to improve


it and make easy to read
● check all tests still pass

● repeat...
Questions?
Extra reading:

https://www.baeldung.com/java-exceptions
https://docs.oracle.com/javase/tutorial/essential/exceptions/

http://tutorials.jenkov.com/java/enums.html

http://users.csc.calpoly.edu/~djanzen/research/TDD08/cdesai/IntroducingJUnit/IntroducingJUnit.html

You might also like