Class diagrams
[W3.1a] Tools → UML → Class Diagrams → Introduction → What
[W3.1b] Tools → UML → Class Diagrams → Classes → What
[W3.1c] Tools → UML → Class Diagrams → Associations → What
[W3.1d] Tools → UML → Class Diagrams → Associations → Navigability
[W3.1e] Tools → UML → Class Diagrams → Associations → Labels
Object diagrams
Class-level members
[W3.2a] Paradigms → OOP → Classes → Class-Level Members
[W3.2b] Tools → UML → Class Diagrams → Class-Level Members → Class-Level Members
[W3.2c] C++ to Java → Classes → Class-Level Members :
Enumerations
[W3.2d] Paradigms → OOP → Classes → Enumerations
[W3.2e] C++ to Java → Miscellaneous Topics → Enumerations
Java varargs
[W3.4a] Implementation → Error Handling → Introduction → What
[W3.4b] Implementation → Error Handling → Exceptions → What
[W3.4c] C++ to Java → Exceptions → What are Exceptions?
[W3.4d] Implementation → Error Handling → Exceptions → How
[W3.4e] C++ to Java → Exceptions → How to Use Exceptions
[W3.4f] Implementation → Error Handling → Exceptions → When
Can explain/identify class diagrams
UML class diagrams describe the structure (but not the behavior) of an OOP solution. These are possibly the most often used diagrams in the industry and an indispensable tool for an OO programmer.
An example class diagram:
Can draw UML classes
The basic UML notations used to represent a class:
A Table
class shown in UML notation:
class Table{
Integer number;
Chair[] chairs = null;
Integer getNumber(){
//...
}
void setNumber(Integer n){
//...
}
}
class Table:
def __init__(self):
self.number = 0
self.chairs = []
def get_number(self):
# ...
def set_number(self, n):
# ...
The 'Operations' compartment and/or the 'Attributes' compartment may be omitted if such details are not important for the task at hand. 'Attributes' always appear above the 'Operations' compartment. All operations should be in one compartment rather than each operation in a separate compartment. Same goes for attributes.
The visibility of attributes and operations is used to indicate the level of access allowed for each attribute or operation. The types of visibility and their exact meanings depend on the programming language used. Here are some common visibilities and how they are indicated in a class diagram:
+
: public-
: private#
: protected~
: package privatevisibility | Java | Python |
---|---|---|
- private |
private |
at least two leading underscores (and at most one trailing underscores) in the name |
# protected |
protected |
one leading underscore in the name |
+ public |
public |
all other cases |
~ package private |
default visibility | not applicable |
Table
class with visibilities shown:
Which of these follow the correct UML notation?
Draw a UML diagram to represent the Car
class as given below. Include visibilities.
class Car{
Engine engine;
private List<Wheel> wheels = null;
public String model;
public void drive(int speed){
move(speed);
}
private void move(int speed){
...
}
}
You may omit self
from method signatures in the class diagram.
class Car:
def __init__(self, model):
self._engine = None # type: Engine
self.__wheels = [] # type: a list of Wheel objects
self.model = model # type: string
def drive(self, speed): # speed is an int
self.move(speed)
def __move(self, speed):
pass
Can interpret simple associations in a class diagram
We use a solid line to show an association between two classes.
This example shows an association between the Admin
class and the Student
class:
Can interpret association navigabilities in class diagrams
We use arrow heads to indication the navigability of an association.
Logic
is aware of Minefield
, but Minefield
is not aware of Logic
class Logic{
Minefield minefield;
}
class Minefield{
...
}
class Logic:
def __init__(self, minefield):
self.minefield = minefield
# ...
class Minefield:
# ...
Here is an example of a bidirectional navigability; each class is aware of the other.
Navigability can be shown in class diagrams as well as object diagrams.
According to this object diagram the given Logic
object is associated with and aware of two MineField
objects.
(a)
Explanation: The diagram indicates that Unit
object should know about the Item
object. In the implementation, the Unit
object will hold a Unit
object in one of its variables.
Can explain/use association labels in class diagrams
Association labels describe the meaning of the association. The arrow head indicates the direction in which the label is to be read.
In this example, the same association is described using two different labels.
Admin
class is associated with Student
class because an Admin
object uses a Student
object.Admin
class is associated with Student
class because a Student
object is used by an Admin
object.
Can explain/identify object diagrams
An object diagram shows an object structure at a given point of time.
An example object diagram:
Can draw UML objects
Notation:
Notes:
car1:Car
are underlined.objectName:ClassName
is meant to say 'an instance of ClassName
identified as objectName
'.:Car
which is meant to say 'an unnamed instance of a Car object'.Some example objects:
Draw a UML diagram to represent the Car
object created by the following code.
class Car{
private double price;
private int speed;
Car(double price, int speed){
//...
}
}
// somewhere else in the code
Car myCar = new Car(13.5, 200);
class Car:
def __init__(self, price, speed):
self.__price = price # type: float
self.__speed = speed # type: int
# somewhere else in the code
my_car = Car(13.5, 200);
Can interpret simple associations among objects
A solid line indicates an association between two objects.
An example object diagram showing two associations:
Can explain class-level members
While all objects of a class has the same attributes, each object has its own copy of the attribute value.
All Person
objects have the Name
attribute but the value of that attribute varies between Person
objects.
However, some attributes are not suitable to be maintained by individual objects. Instead, they should be maintained centrally, shared by all objects of the class. They are like ‘global variables’ but attached to a specific class. Such variables whose value is shared by all instances of a class are called class-level attributes.
The attribute totalPersons
should be maintained centrally and shared by all Person
objects rather than copied at each Person
object.
Similarly, when a normal method is being called, a message is being sent to the receiving object and the result may depend on the receiving object.
Sending the getName()
message to Adam
object results in the response "Adam"
while sending the same message to the Beth
object gets the response "Beth"
.
However, there can be methods related to a specific class but not suitable for sending message to a specific object of that class. Such methods that are called using the class instead of a specific instance are called class-level methods.
The method getTotalPersons()
is not suitable to send to a specific Person
object because a specific object of the Person
class should not know about the total number of Person
objects.
Class-level attributes and methods are collectively called class-level members (also called static members sometimes because some programming languages use the keyword static
to identify class-level members). They are to be accessed using the class name rather than an instance of the class.
Which of these are suitable as class-level variables?
Player
, variable: totalScore
Course
, variable: totalStudents
Task
, variable: totalPendingTasks
ArrayList
, variable: total
(i.e., total items in a given ArrayList
object)(c)
Explanation: totalPendingTasks
should not be managed by individual Task
objects and therefore suitable to be maintained as a class-level variable. The other variables should be managed at instance level as their value varies from instance to instance. e.g., totalStudents
for one Course
object will differ from totalStudents
of another.
Can interpret class-level members in class diagrams
In UML class diagrams, underlines denote class-level attributes and variables.
In the class below, totalStudents
attribute and the getTotalStudents
method are class-level.
Can use class-level members
The content below is an extract from -- Java Tutorial, with slight adaptations.
When a number of objects are created from the same class blueprint, they each have their own distinct copies of instance variables. In the case of a Bicycle
class, the instance variables are gear, and speed. Each Bicycle object has its own values for these variables, stored in different memory locations.
Sometimes, you want to have variables that are common to all objects. This is accomplished with the static
modifier. Fields that have the static
modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. Every instance of the class shares a class variable, which is in one fixed location in memory. Any object can change the value of a class variable, but class variables can also be manipulated without creating an instance of the class.
Suppose you want to create a number of Bicycle objects and assign each a serial number, beginning with 1 for the first object. This ID number is unique to each object and is therefore an instance variable. At the same time, you need a field to keep track of how many Bicycle
objects have been created so that you know what ID to assign to the next one. Such a field is not related to any individual object, but to the class as a whole. For this you need a class variable, numberOfBicycles, as follows:
public class Bicycle {
private int gear;
private int speed;
// an instance variable for the object ID
private int id;
// a class variable for the number of Bicycle objects instantiated
private static int numberOfBicycles = 0;
...
}
Class variables are referenced by the class name itself, as in Bicycle.numberOfBicycles
This makes it clear that they are class variables.
The Java programming language supports static methods as well as static variables. Static methods, which have the static
modifier in their declarations, should be invoked with the class name, without the need for creating an instance of the class, as in ClassName.methodName(args)
The static
modifier, in combination with the final
modifier, is also used to define constants. The final modifier indicates that the value of this field cannot change.For example, the following variable declaration defines a constant named PI, whose value is an approximation of pi (the ratio of the circumference of a circle to its diameter):
static final double PI = 3.141592653589793;
Here is an example with class-level variables and class-level methods:
public class Bicycle {
private int gear;
private int speed;
private int id;
private static int numberOfBicycles = 0;
public Bicycle(int startSpeed, int startGear) {
gear = startGear;
speed = startSpeed;
numberOfBicycles++;
id = numberOfBicycles;
}
public int getID() {
return id;
}
public static int getNumberOfBicycles() {
return numberOfBicycles;
}
public int getGear(){
return gear;
}
public void setGear(int newValue) {
gear = newValue;
}
public int getSpeed() {
return speed;
}
// ...
}
Explanation of System.out.println(...)
:
out
is a class-level public attribute of the System
class.println
is a instance level method of the out
object.Consider the Circle
class below:
public class Circle {
private int x;
private int y;
private double radius;
public Circle(){
this(0, 0, 0);
}
public Circle(int x, int y, double radius){
setX(x);
setY(y);
setRadius(radius);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = Math.max(radius, 0);
}
public int getArea(){
double area = Math.PI * Math.pow(radius, 2);
return (int)area;
}
}
Update it as follows so that code given below produces the given output.
getMaxRadius
method that returns the maximum radius that has been used in all Circle
objects created thus far.public class Main {
public static void main(String[] args) {
Circle c = new Circle();
System.out.println("max radius used so far : " + Circle.getMaxRadius());
c = new Circle(0, 0, 10);
System.out.println("max radius used so far : " + Circle.getMaxRadius());
c = new Circle(0, 0, -15);
System.out.println("max radius used so far : " + Circle.getMaxRadius());
c.setRadius(12);
System.out.println("max radius used so far : " + Circle.getMaxRadius());
}
}
max radius used so far : 0.0
max radius used so far : 10.0
max radius used so far : 10.0
max radius used so far : 12.0
You can use a static
variable maxRadius
to track the maximum value used for the radius
attribute so far.
Partial solution:
public void setRadius(double radius) {
this.radius = Math.max(radius, 0);
if (maxRadius < this.radius){
// ...
}
}
Can explain the meaning of enumerations
An Enumeration is a fixed set of values that can be considered as a data type. An enumeration is often useful when using a regular data type such as int
or String
would allow invalid values to be assigned to a variable.
Suppose you want a variable called priority
to store the priority of something. There are only three priority levels: high, medium, and low. You can declare the variable priority
as of type int
and use only values 2
, 1
, and 0
to indication the three priority levels. However, this opens the possibility of an invalid values such as 9
being assigned to it. But if you define an enumeration type called Priority
that has three values HIGH
, MEDIUM
, LOW
only, a variable of type Priority
will never be assigned an invalid value because the compiler is able to catch such an error.
Priority
: HIGH
, MEDIUM
, LOW
Can use Java enumerations
You can define an enum type by using the enum
keyword. Because they are constants, the names of an enum type's fields are in uppercase letters e.g., FLAG_SUCCESS
by convention.
Defining an enumeration to represent days of a week (code to be put in the Day.java
file):
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
Some examples of using the Day
enumeration defined above:
Day today = Day.MONDAY;
Day[] holidays = new Day[]{Day.SATURDAY, Day.SUNDAY};
switch (today) {
case SATURDAY:
case SUNDAY:
System.out.println("It's the weekend");
break;
default:
System.out.println("It's a week day");
}
Note that while enumerations are usually a simple set of fixed values, Java enumerations can have behaviors too, as explained in this tutorial from -- Java Tutorial
Define an enumeration named Priority
. Add the missing describe
method to the code below so that it produces the output given.
public class Main {
// Add your method here
public static void main(String[] args) {
describe("Red", Priority.HIGH);
describe("Orange", Priority.MEDIUM);
describe("Blue", Priority.MEDIUM);
describe("Green", Priority.LOW);
}
}
Red indicates high priority
Orange indicates medium priority
Blue indicates medium priority
Green indicates low priority
Use a switch
statement to select between possible values for Priority
.
public static void describe(String color, Priority p) {
switch (p) {
case LOW:
System.out.println(color + " indicates low priority");
break;
// ...
}
}
Code for the enumeration is given below:
public enum Priority {
HIGH, MEDIUM, LOW
}
Can use Java varargs feature
Variable Arguments (Varargs) is a syntactic sugar type feature that allows writing a methods that can take a variable number of arguments.
The search
method below can be called as search()
, search("book")
, search("book", "paper")
, etc.
public static void search(String ... keywords){
// method body
}
Resources:
Can explain the meaning of inheritance
The OOP concept Inheritance allows you to define a new class based on an existing class.
For example, you can use inheritance to define an EvaluationReport
class based on an existing Report
class so that the EvaluationReport
class does not have to duplicate data/behaviors that are already implemented in the Report
class. The EvaluationReport
can inherit the wordCount
attribute and the print()
method from the base class Report
.
A superclass is said to be more general than the subclass. Conversely, a subclass is said to be more specialized than the superclass.
Applying inheritance on a group of similar classes can result in the common parts among classes being extracted into more general classes.
Man
and Woman
behaves the same way for certain things. However, the two classes cannot be simply replaced with a more general class Person
because of the need to distinguish between Man
and Woman
for certain other things. A solution is to add the Person
class as a superclass (to contain the code common to men and woment) and let Man
and Woman
inherit from Person
class.
Inheritance implies the derived class can be considered as a sub-type of the base class (and the base class is a super-type of the derived class), resulting in an is a relationship.
Inheritance does not necessarily mean a sub-type relationship exists. However, the two often go hand-in-hand. For simplicity, at this point let us assume inheritance implies a sub-type relationship.
To continue the previous example,
Woman
is a Person
Man
is a Person
Inheritance relationships through a chain of classes can result in inheritance hierarchies (aka inheritance trees).
Two inheritance hierarchies/trees are given below. Note that the triangle points to the parent class. Observe how the Parrot
is a Bird
as well as it is an Animal
.
Multiple Inheritance is when a class inherits directly from multiple classes. Multiple inheritance among classes is allowed in some languages (e.g., Python, C++) but not in other languages (e.g., Java, C#).
The Honey
class inherits from the Food
class and the Medicine
class because honey can be consumed as a food as well as a medicine (in some oriental medicine practices). Similarly, a Car
is an Vehicle
, an Asset
and a Liability
.
Which of these are correct?
(a) (b) (c) (d)
Explanation: (e) is incorrect. Because subclasses inherit behavior from the superclass, any changes to the superclass could affect subclasses.
Can interpret class inheritance in class diagrams
You can use a triangle and a solid line (not to be confused with an arrow) to indicate class inheritance.
Notation:
Examples: The Car
class inherits from the Vehicle
class. The Cat
and Dog
classes inherit from the Pet
class.
Can explain method overloading
Method overloading is when there are multiple methods with the same name but different type signatures. Overloading is used to indicate that multiple operations do similar things but take different parameters.
Type Signature: The type signature of an operation is the type sequence of the parameters. The return type and parameter names are not part of the type signature. However, the parameter order is significant.
Example:
Method | Type Signature |
---|---|
int add(int X, int Y) |
(int, int) |
void add(int A, int B) |
(int, int) |
void m(int X, double Y) |
(int, double) |
void m(double X, int Y) |
(double, int) |
In the case below, the calculate
method is overloaded because the two methods have the same name but different type signatures (String)
and (int)
calculate(String): void
calculate(int): void
Can use basic inheritance
Given below is an extract from the -- Java Tutorial, with slight adaptations.
A class that is derived from another class is called a subclass (also a derived class, extended class, or child class). The class from which the subclass is derived is called a superclass (also a base class or a parent class).
A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.
Every class has one and only one direct superclass (single inheritance), except the Object
class, which has no superclass, . In the absence of any other explicit superclass, every class is implicitly a subclass of Object
. Classes can be derived from classes that are derived from classes that are derived from classes, and so on, and ultimately derived from the topmost class, Object
. Such a class is said to be descended from all the classes in the inheritance chain stretching back to Object
. Java does not support multiple inheritance among classes.
The java.lang.Object
class defines and implements behavior common to all classes—including the ones that you write. In the Java platform, many classes derive directly from Object
, other classes derive from some of those classes, and so on, forming a single hierarchy of classes.
The keyword extends
indicates one class inheriting from another.
Here is the sample code for a possible implementation of a Bicycle
class and a MountainBike
class that is a subclass of the Bicycle
:
public class Bicycle {
public int gear;
public int speed;
public Bicycle(int startSpeed, int startGear) {
gear = startGear;
speed = startSpeed;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}
public class MountainBike extends Bicycle {
// the MountainBike subclass adds one field
public int seatHeight;
// the MountainBike subclass has one constructor
public MountainBike(int startHeight, int startSpeed, int startGear) {
super(startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass adds one method
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
A subclass inherits all the fields and methods of the superclass. In the example above, MountainBike
inherits all the fields and methods of Bicycle
and adds the field seatHeight
and a method to set it.
If your method overrides one of its superclass's methods, you can invoke the overridden method through the use of the keyword super
. You can also use super
to refer to a
Consider this class, Superclass
and a subclass, called Subclass
, that overrides printMethod()
:
public class Superclass {
public void printMethod() {
System.out.println("Printed in Superclass.");
}
}
public class Subclass extends Superclass {
// overrides printMethod in Superclass
public void printMethod() {
super.printMethod();
System.out.println("Printed in Subclass");
}
public static void main(String[] args) {
Subclass s = new Subclass();
s.printMethod();
}
}
Printed in Superclass.
Printed in Subclass
Within Subclass
, the simple name printMethod()
refers to the one declared in Subclass
, which overrides the one in Superclass
. So, to refer to printMethod()
inherited from Superclass
, Subclass
must use a qualified name, using super
as shown.
A subclass constructor can invoke the superclass constructor. Invocation of a superclass constructor must be the first line in the subclass constructor.
The syntax for calling a superclass constructor is super()
(which invokes the no-argument constructor of the superclass) or super(parameters)
(to invoke the superclass constructor with a matching parameter list).
The following example illustrates how to use the super
keyword to invoke a superclass's constructor. Recall from the Bicycle
example that MountainBike
is a subclass of Bicycle
. Here is the MountainBike
(subclass) constructor that calls the superclass constructor and then adds some initialization code of its own (i.e., seatHeight = startHeight;
):
public MountainBike(int startHeight, int startSpeed, int startGear) {
super(startSpeed, startGear);
seatHeight = startHeight;
}
Note: If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the superclass does not have a no-argument constructor, you will get a compile-time error. Object
does have such a constructor, so if Object
is the only superclass, there is no problem.
Access level modifiers determine whether other classes can use a particular field or invoke a particular method. Given below is a simplified version of Java access modifiers, assuming you have not yet started placing your classes in different packages i.e., all classes are placed in the root level. A full explanation of access modifiers is given in a later topic.
There are two levels of access control:
At the class level:
public
: the class is visible to all other classespublic
At the member level:
public
: the class is visible to all other classespublic
protected
: same as public
private
: the member can only be accessed in its own classBackground: Suppose we are creating a software to manage various tasks a person has to do. Two types of such tasks are,
The Task
class is given below:
public class Task {
protected String description;
public Task(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
Todo
class that inherits from the Task
class.
boolean
field isDone
to indicate whether the todo is done or not done.isDone()
method to access the isDone
field and a setDone(boolean)
method to set the isDone
field.Deadline
class that inherits from the Todo
class that you implemented in the previous step. It should have,
String
field by
to store the details of when the task to be done e.g., Jan 25th 5pm
getBy()
method to access the value of the by
field, and a corresponding setBy(String)
method.Deadline(String description, String by)
The expected behavior of the two classes is as follows:
public class Main {
public static void main(String[] args) {
// create a todo task and print details
Todo t = new Todo("Read a good book");
System.out.println(t.getDescription());
System.out.println(t.isDone());
// change todo fields and print again
t.setDone(true);
System.out.println(t.isDone());
// create a deadline task and print details
Deadline d = new Deadline("Read textbook", "Nov 16");
System.out.println(d.getDescription());
System.out.println(d.isDone());
System.out.println(d.getBy());
// change deadline details and print again
d.setDone(true);
d.setBy("Postponed to Nov 18th");
System.out.println(d.isDone());
System.out.println(d.getBy());
}
}
Read a good book
false
true
Read textbook
false
Nov 16
true
Postponed to Nov 18th
Todo
class is given below. You can follow a similar approach for the Deadline
class.
public class Todo extends Task {
protected boolean isDone;
public Todo(String description) {
super(description);
isDone = false;
}
}
Can explain error handling
Well-written applications include error-handling code that allows them to recover gracefully from unexpected errors. When an error occurs, the application may need to request user intervention, or it may be able to recover on its own. In extreme cases, the application may log the user off or shut down the system. --Microsoft
Can explain exceptions
Exceptions are used to deal with 'unusual' but not entirely unexpected situations that the program might encounter at run time.
Exception:
The term exception is shorthand for the phrase "exceptional event." An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions. –- Java Tutorial (Oracle Inc.)
Examples:
Can explain Java Exceptions
Given below is an extract from the -- Java Tutorial, with some adaptations.
There are three basic categories of exceptions In Java:
Error
, RuntimeException
, and their subclasses. Suppose an application prompts a user for an input file name, then opens the file by passing the name to the constructor for java.io.FileReader. Normally, the user provides the name of an existing, readable file, so the construction of the FileReader
object succeeds, and the execution of the application proceeds normally. But sometimes the user supplies the name of a nonexistent file, and the constructor throws java.io.FileNotFoundException
. A well-written program will catch this exception and notify the user of the mistake, possibly prompting for a corrected file name.
Error
and its subclasses. Suppose that an application successfully opens a file for input, but is unable to read the file because of a hardware or system malfunction. The unsuccessful read will throw java.io.IOError
. An application might choose to catch this exception, in order to notify the user of the problem — but it also might make sense for the program to print a stack trace and exit.
RuntimeException
and its subclasses. These usually indicate programming bugs, such as logic errors or improper use of an API. Consider the application described previously that passes a file name to the constructor for FileReader. If a logic error causes a null to be passed to the constructor, the constructor will throw NullPointerException
. The application can catch this exception, but it probably makes more sense to eliminate the bug that caused the exception to occur.
Errors and runtime exceptions are collectively known as unchecked exceptions.
Can explain how exception handling is done typically
Most languages allow code that encountered an "exceptional" situation to encapsulate details of the situation in an Exception object and throw/raise that object so that another piece of code can catch it and deal with it. This is especially useful when the code that encountered the unusual situation does not know how to deal with it.
The extract below from the -- Java Tutorial (with slight adaptations) explains how exceptions are typically handled.
When an error occurs at some point in the execution, the code being executed creates an exception object and hands it off to the runtime system. The exception object contains information about the error, including its type and the state of the program when the error occurred. Creating an exception object and handing it to the runtime system is called throwing an exception.
After a method throws an exception, the runtime system attempts to find something to handle it in the
The exception handler chosen is said to catch the exception. If the runtime system exhaustively searches all the methods on the call stack without finding an appropriate exception handler, the program terminates.
Advantages of exception handling in this way:
Which are benefits of exceptions?
(a) (c)
Explanation: Exceptions cannot prevent problems in the environment. They can only be used to handle and recover from such problems.
Can use Java Exceptions
The content below uses extracts from the -- Java Tutorial, with some adaptations.
A program can catch exceptions by using a combination of the try
, catch
blocks.
try
block identifies a block of code in which an exception can occur.catch
block identifies a block of code, known as an exception handler, that can handle a particular type of exception. The writeList()
method below calls a method process()
that can cause two type of exceptions. It uses a try-catch construct to deal with each exception.
public void writeList() {
print("starting method");
try {
print("starting process");
process();
print("finishing process");
} catch (IndexOutOfBoundsException e) {
print("caught IOOBE");
} catch (IOException e) {
print("caught IOE");
}
print("finishing method");
}
Some possible outputs:
No exceptions | IOException |
IndexOutOfBoundsException |
---|---|---|
starting method starting process finishing process finishing method |
starting method starting process caught IOE finishing method |
starting method starting process caught IOOBE finishing method |
You can use a finally
block to specify code that is guaranteed to execute with or without the exception. This is the right place to close files, recover resources, and otherwise clean up after the code enclosed in the try
block.
The writeList()
method below has a finally
block:
public void writeList() {
print("starting method");
try {
print("starting process");
process();
print("finishing process");
} catch (IndexOutOfBoundsException e) {
print("caught IOOBE");
} catch (IOException e) {
print("caught IOE");
} finally {
// clean up
print("cleaning up");
}
print("finishing method");
}
Some possible outputs:
No exceptions | IOException |
IndexOutOfBoundsException |
---|---|---|
starting method starting process finishing process cleaning up finishing method |
starting method starting process caught IOE cleaning up finishing method |
starting method starting process caught IOOBE cleaning up finishing method |
The try
statement should contain at least one catch
block or a finally block and may have multiple catch
blocks.
The class of the exception object indicates the type of exception thrown. The exception object can contain further information about the error, including an error message.
You can use the throw
statement to throw an exception. The throw statement requires a
Here's an example of a throw
statement.
if (size == 0) {
throw new EmptyStackException();
}
In Java, Checked exceptions are subject to the Catch or Specify Requirement: code that might throw checked exceptions must be enclosed by either of the following:
try
statement that catches the exception. The try
must provide a handler for the exception.throws
clause that lists the exception.Unchecked exceptions are not required to follow to the Catch or Specify Requirement but you can apply the requirement to them too.
Here's an example of a method specifying that it throws certain checked exceptions:
public void writeList() throws IOException, IndexOutOfBoundsException {
print("starting method");
process();
print("finishing method");
}
Some possible outputs:
No exceptions | IOException |
IndexOutOfBoundsException |
---|---|---|
starting method finishing method |
starting method |
starting method |
Java comes with a collection of built-in exception classes that you can use. When they are not enough, it is possible to create your own exception classes.
The Main
class below parses a string descriptor of a rectangle of the format "WIDTHxHEIGHT"
e.g., "3x4"
and prints the area of the rectangle.
public class Main {
public static void printArea(String descriptor){
//TODO: modify the code below
System.out.println(descriptor + "=" + calculateArea(descriptor));
}
private static int calculateArea(String descriptor) {
//TODO: modify the code below
String[] dimensions = descriptor.split("x");
return Integer.parseInt(dimensions[0]) * Integer.parseInt(dimensions[1]);
}
public static void main(String[] args) {
printArea("3x4");
printArea("5x5");
}
}
3x4=12
5x5=25
Update the code of printArea
to print an error message if WIDTH
and/or HEIGHT
are not numbers e.g., "Ax4"
calculateArea
will throw the unchecked exception NumberFormatException
if the code tries to parse a non-number to an integer.
Update the code of printArea
to print an error message if the descriptor is missing WIDTH
and/or HEIGHT
e.g., "x4"
calculateArea
will throw the unchecked exception IndexOutOfBoundsException
if one or both dimensions are missing.
Update the code of calculateArea
to throw the checked exception IllegalShapeException
if there are more than 2 dimensions e.g., "5x4x3"
and update the printArea
to print an error message for those cases. Here is the code for the IllegalShapeException.java
public class IllegalShapeException extends Exception {
//no other code needed
}
Here is the expected behavior after you have done the above changes:
public class Main {
//...
public static void main(String[] args) {
printArea("3x4");
printArea("3xy");
printArea("3x");
printArea("3");
printArea("3x4x5");
}
}
3x4=12
WIDTH or HEIGHT is not a number: 3xy
WIDTH or HEIGHT is missing: 3x
WIDTH or HEIGHT is missing: 3
Too many dimensions: 3x4x5
public class Main {
public static void printArea(String descriptor){
try {
System.out.println(descriptor + "=" + calculateArea(descriptor));
} catch (NumberFormatException e) {
System.out.println("WIDTH or HEIGHT is not a number: " + descriptor);
} // add more catch blocks here
}
private static int calculateArea(String descriptor) throws IllegalShapeException {
String[] dimensions = descriptor.split("x");
//throw IllegalShapeException here if dimensions.length > 2
return Integer.parseInt(dimensions[0]) * Integer.parseInt(dimensions[1]);
}
}
Can avoid using exceptions to control normal workflow
In general, use exceptions only for 'unusual' conditions. Use normal return
statements to pass control to the caller for conditions that are 'normal'.