Skip to content
UoL CS Notes

COMP122 (Java Object-Oriented Programming)

Version Control with git

COMP122 Labs

A full textbook on git as available called: ProGit.

Local Setup

The bare minimum of setup is to set the user.name and user.email:

$ git config --global user.name "Ben Weston"
$ git config --global user.email sgbweso@liverpool.ac.uk

Creating a Repository

To initialise a new repository you can do the following inside a new folder:

$ git init

To check the status of the repo use:

$ git status

Adding Files

To stage a new file that you have made, run the following:

$ git add newFile.txt

To commit this file to the database you can use the following:

$ git commit -m "Adding the newest file ever."

To use a text editor to draft the commit message, ommit the -m flag.

You can see your history of coimmits by using:

$ git log

Changing and Reverting Files

To add the changes for all modified (but not new) files:

$ git commit -a -m "Adding changes."

To remove to the previous commit of a file:

$ git checkout file.txt

To pick a particular commit view the commit history and run the following:

$ git log
$ git checkout <commit identifier> file.txt

You can also just use the identifier prefix.

Input & Output (I/O) - 2

COMP122 Labs

There are many I/O classes, view the full Oracle I/O tutorial here.

Scanner

java.util.Scanner splits up an input into tokens that can be read one at a time.

You can scan through these using the has.Next() and look for specific types using other methods:

double sum = 0;
try (Scanner scan = new Scanner("20.40 notadouble 30.45 gawef 49.15")) {
	while(scan.hasNext()) {
		if(scan.hasNextDouble()) {
			sum += scan.nextDouble();
		}
		else {
			scan.next();
		}
	}
}
System.out.println(sum);

By using a try with resource, Java will close the Scanner after is is finished being used.

Scanner vs Reader

Scanner has pretty all of the functionality of BufferedReader, including a nextLine() function that allows you to break up a file line by line and skip the remaining tokens on a given line. That said, it is slower and more expensive than BufferedReader, which is the preferred option if you just want text. Further, the nextLine() functionality can make the scanning process messy, as it can potentially skip a lot of tokens, and was not really designed for the use case we discussed last week. A common use case is thus to use a BufferedReader to read a file in as Strings, and then use a Scanner to break these Strings into tokens.

BufferedWriter

To write to a file you can use the following example code:

String test = "This is a test."
try(BufferedWriter bw = Files.newBufferedWriter(outPath)) {
	bw.write(test);
}

By using a try with resource, the file is written out when the try has executed.

To force the buffer to be written use bw.flush().

You should avoid closing the stream until you are sure that you are done writing to a file.

Build Tools (Gradle)

COMP122 Labs

Installation

Refer to the installation notes for full install details. In summary, install the gradle package:

$ yay -S gradle

Setting up a Java Gradle Project

This will be following the documentation for setting up a new java project.

  1. Create a new project directory:

     $ mkdir demo
     $ cd demo
    
  2. Make a new gradle project using the step-by-step wizard.

     $ gradle init
    
    • For project type choose application (2).
    • As implementation language chose Java (3).
    • As test framework select “JUnit Jupiter” (4).
    • For the other questions just select the default value.
  3. Several files and a folder hierarchy are created. This includes a simple “Hello World” program.
  4. To test, build and run your code execute in the root of the project:

     $ ./gradlew run
    

    The program is run after the output:

     > Task :run
    

Gradle Tasks

Gradle has has several tasks:

  • To clean the project directory:

      $ ./gradlew clean
    

    This will remove all compiler class files.

  • To compile but not run your project:

      $ ./gradlew compileJava
    

    This will compiler all java source files in app/src/main/java and output the compiler bytecode into app/build/main.

To view additional help run:

$ ./gradlew help

Regular Expressions (Regex)

COMP122 Labs

Consider the following text as a test for regular expressions:

It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. During the battle, Rebel spies managed to steal secret plans to the Empire’s ultimate weapon, the DEATH STAR, an armored space station with enough power to destroy an entire planet. Pursued by the Empire’s sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy…

Regex in Java

The Java classes for regular expressions are Pattern and Matcher in java.util.regex.

The standard usage is to define a regular expression that we wish to match, and compile this Pattern. We then take the string that we wish to match and compare our regular expression against this with Matcher.

Matcher Objects

Let us say we wished to search for a specific string, such as the word “Rebel”.

To do this we must first define a matching pattern for the regular expression. As we are trying to match the word “Rebel”, in our input we wish to match the characters “R”, “e”, “b”, “e”, “l”, in sequence.

This is a common operation and so it is made easy in Regex, to match this the corresponding string is "Rebel":

Pattern pattern = Pattern.compile("Rebel");
Matcher matcher = pattern.matcher(input);

Querying a Matcher Object

Now that we have a matched Matcher object, we can query this to find the results of our matching. There are three core methods that we will use. start(), end(), and find().

matcher.start() returns the integer of the 0th position of the first occurrence of the matching regex within the input array. Unsurprisingly matcher.end() tells us the integer position that the regex stops matching. If we call matcher.find() this will return true if the input string has another occurrence of the regex, and will move onto this part of the string. If no more matches remain, it will return false.

This means the easiest way to work with this is in a while loop, as this method will exit the loop once it has reached the end of the String:

while(matcher.find()) {
    System.out.println(matcher.start());
    System.out.println(matcher.end());
    System.out.println(input.substring(matcher.start(), matcher.end()));
}

If we run this, we see that it prints the start and end integers of each match in the input string, and we can use the substring(start, end) method to display each of our matches.

$ java Rebel
29
34
Rebel
158
163
Rebel

Or

| is used as or in regex. To match two strings you could use the following:

Pattern pattern = Pattern.compile("Rebel|Empire");

Character Sets/Ranges

To define a character set use []. You can set the range of characters like so: [0-9].

This will match a single character in that set.

Set Boundaries

To define how many times to match a character set you can use a qualifier with {}.

For example, [A-Z]{5} would match with all sequences of file letters which are fully capitalised. We can also use ranges: [A-Z]{5-7}.

There are also some looser qualifiers:

  • + - One or more.
  • * - Zero or more.

If we made the regex [A-Z]* this would also match with each space in our string as this matches zero or more. We must also tell the regex to only start scanning for matching patterns once we have reached a boundary:

  • ^ Start of line anchor.
  • \\b Start or end of a word.
    • Word boundary.
  • $ End of line anchor.

Wildcards

To save writing sets for commonly matched values, the following wildcards exist:

  • \d - A single digit from 0-9.
  • \w - A single letter, number or underscore.
  • \s - A single whitespace character including space, tab and newline.
  • . - Any single character apart from newline.

    To match for a full stop then you can escape with \. The backslash would also need escaping for Java.

To match the opposite of these wildcards you can use the uppercase version. For example, \D will match non-numerical characters.

In Java you must escape backslashes, \, when you want to write them in a String literal. This makes \d into \\d and so on.

Grouping

To match a second regex from a previous group you can use (). For example From:(.*) will save all of the characters after From: to a specific group.

When using groups the regular expression will match the entire regular expression and save this as the first (0th) group. It will then work through each set of brackets and save these in subsequent groups. We access each of these groups via their integer index and the group() method.

Pattern pattern = Pattern.compile("From:(.*)");
Matcher matcher = pattern.matcher(spam);
System.out.println("After From: " + matcher.group(1));

Input & Output (I/O)l - 1

COMP122 Labs

Due to the large number of I/O classes in java.io.Files, there is a class called:

java.nio.Files

This class consists of several static function calls that cater to common use cases.

You should look in this class before looking elsewhere.

Reading from File

A classic approach to reading from a text file uses a BufferedReader to read the file line by line, and looks something like this:

File file = new File(filePathAsString);
try(BufferedReader br = Files.newBufferedReader(file.toPath())) {
    String str = "";
    while ((str = br.readLine()) != null) {
        //do some logic with str
    }
} catch(IOException ioe) {
    throw ioe;
}
  1. The first line of this code creates a java.io.File class from a file path String (e.g. “myDirectory/myFile.txt”).
  2. The second line uses the Files.newBufferedReader method to instantiate a BufferedReader. It takes as its first argument the Path object associated to a file, and then allows for optional parameters that alter things like the character set. We omit these so it will have default settings and character set, UTF-8. This line is an example of try with resources, which is syntactic sugar for a lengthy try/catch construct.

    By declaring br inside the try, we ensure that it will be closed, which prevents resource leaks. If we didn’t use try with resources, we’d need to call br.close() in a finally block.

  3. The third line just initializes an empty String.
  4. The strange looking fourth line tries to read the next line of the file into str using BufferedReader’s readLine method. Once this operation is performed, the result of that assignment is null checked, and if str has become null we know we’ve reached the end of the file. So, this is just a loop that says read every line in as a String until the end of file.

    readLine() discards the trailing newline (\n), so if you wanted to reproduce the file’s content exactly you would need to add this back to the string.

  5. The code enclosed by the while loop is where you would do your logic using the newly read str. There is then a catch block that throws an IOException if the BufferedReader runs into one.

The code above is a perfectly functional way to handle file I/O, but java.nio.file.Files offers a couple of simpler alternatives to cover this use case.

Using java.nio.file.Files

Lines as an ArrayList

The first, and what is probably most useful, is Files.readAllLines(path), which returns a List of every line, in order. That looks like this:

File file = new File(filePathAsString);
ArrayList<String> yourList = (ArrayList<String>) Files.readAllLines(file.toPath());

In two lines of code we now have every line of the file, ordered, in an ArrayList we can traverse with a simple for loop. This code practically is just a tidier repackaging of the code above, where the inner loop adds str to a List<String> initialized as empty before the try.

Original File as String

Another two-liner if you just want the whole file as one big String with the white space and new lines of the file preserved, is given below:

File file = new File(filePathAsString);
String yourFilesContent = Files.readString(file.toPath());

Both the readAllLines and readString methods throw IOExceptions. This is a checked exception type so you need to throw or catch them (as in the initial example).

These methods are not performance optimised, so if you need to read a very large file in a performance intensive setting (e.g. developing an industry strength library) these aren’t the way to go. However, in most cases both are fine.

Streams

COMP122 Lectures

Streams are an endless flow of data from a source to a destination:

graph LR
Source -->|write| 010010
010010 -->|read| Destination

Streams are objects. There are different classes for various types of sources and destinations.

Streams as Object

The standard library java.io implements streams in an object-oriented way.

The InputStream class is read from:

graph LR
subgraph InputStream
Source -->|write| 010010
end
010010 -->|read| Destination

The OutputStream class is written to:

graph LR
Source -->|write| 010010
subgraph OutputStream
010010 -->|read| Destination
end

Stream Contents

There are several basic types of data that can be transported by a stream:

  • Basic Input/OutputStreams are byte-based:
    • You can read/write a byte = 8 bits.
    • For example, public int InputStream.read() reads the next bytes of datat from the input stream.
  • java.io.Reader/Writer are based on characters:
    • char, or Character, store UTF-16 encoded characters.
    • public int Reader.read() reads the next character.

      This reads an int as the primitive type char is syntactic sugar for a 16-bit integer. This means that you need to cast the return type into a (char).

  • ObjectInputStream and ObjectOutputStream represent streams that can send whole objects.
    • Works as long as the object belongs to a class that implements the Serialisable interface.
    • public Object ObjectInputStream.readObject() reads the next object.

Standard Streams

The standard streams include:

  • stdin
  • stdout
  • stderr

These are represented in the following classes:

classDiagram
InputStream <|-- in
OutputStream <|.. PrintStream
PrintStream <|-- out
PrintStream <|-- err
class InputStream {
    +read() in
    +read(byte[] b) int
}
class OutputStream {
    +write(int b) void
    +write(byte[] b) void
}
class PrintStream {
    +print(boolean s) void
    +print(char c) void
    +print(char[] s) void
    +print(String s) void
    +println(String s) void
}

in, out and err in this diagram are representing the objects:

  • System.in
  • System.out
  • System.err

Java I/O Summary

  • All of Java’s input/output is based on Streams
  • InputStream, OutputStream:
    • Based on bytes.
  • Reader, Writer:
    • Based on characters.
  • There are many derived types in java.io:
    • File Access
    • Network Access

Files

COMP122 Lectures

File Handles

This is an object that represents a file on the disk (a path in the file system).

java.io.File is used to represent files and provides methods to interact with the file system.

You can call the following methods on a File:

  • Deleting
  • Renaming
  • Check if it exists:
    • Check if it is a file
    • Check if it is a folder
classDiagram
class File{
	+delete() boolean
    +renameTo(File dest) boolean
    +isDirectory() boolean
    +isFile() boolean
    +getPath() String
}

Renaming

import java.io.File;
...

File f1 = new File("output.txt");
File f2 = new File("stuff.out");
if(f1.rename.To(f2)) {
	System.out.println("File rename successful");
else {
	System.out.println("File rename failed");
}

This is not handled using exceptions.

Dealing with Path Differences

Windows uses \ and Unix uses / for directory separators.

To remove this use you can use the separator attribute of the File class:

import java.io.File;

String sep = File.separator;
File fh = new File("data"+ sep + "output.txt");

This will input the appropriate separator for the file-system.

Reading a File with FileReader

The following code will read in a file one character at a time and print it out:

File fileHandler = new File("this-file.txt");
FileReader fileReader = new FileReader(fileHandler);

int i;
while((i = fileReader.read()) != -1) {
	char c = (char)i;
	System.out.print(c);
}

.read() gets the contents one character at a time.

Opening a file that doesn’t exist will give an IOException. This must be done in a try catch block.

Exceptions - 2 - Exceptions in Java

COMP122 Lectures

When something unexpected happens:

  1. An Exception object is created.
  2. The interpreted looks for, and calls a matching handler.

Java Syntax

try {
	// dangerous code
	// may throw XException, YException, ...
}
catch (XException e) {
	// handle XExceptions
}
catch(YException e) {
	// handle YExceptions
}
...
finally {
	// will eventually get executed in any case
}

The first exception whose parameter type matches the exception is picked.

To throw an exception manually you would:

try {
	throw(e)	// if e already exists
	throw(new XExeption())	// throw a new exception
}
catch (XException e) {
	// handle XExceptions
}
catch(YException e) {
	// handle YExceptions
}

Catch or Declare

Java wants you to specify as part of a methods signature, which exceptions it may throw. This allows analysis at compile time.

Declared Exceptions

The syntax to declare is:

public String myMethod() throws XException {
	// method body may throw XExceptions
	// or call code that does
}

Caught Exceptions

public class ReadFromKeyboard {
	public static void main (String args[]) {
		// read one byte from stdin
		int myChar = System.in.read();
		// interpret that int as a char and print
		System.out.println((char) myChar);
	}
}

The call to System.in.read() can create exceptions.

Two fix this we could use one of the following methods:

import java.io.IOExceptions;

public class ReadFromKeyboard {
	public static void main (String args[]) throws IOException {
		// read one byte from stdin
		int myChar = System.in.read();
		// interpret that int as a char and print
		System.out.println((char) myChar);
	}
}

This is declaring the exception.

import java.io.IOExceptions;

public class ReadFromKeyboard {
	public static void main (String args[]) {
		try{
			// read one byte from stdin
			int myChar = System.in.read();
			// interpret that int as a char and print
			System.out.println((char) myChar);
		}
		catch(IOException e) {
			e.printStackTrace();
		}
	}
}

This is catching the exception.

This can be bad practice as developers can make useless catchall exception handlers.

Throwables

Throwables allow certain types of exception to not be declared.

java.lang defines the following classes that can be thrown:

  • Error classes is for non-recoverable situations.
  • Exception classes are for recoverable situations.

Unchecked Throwables

The compiler won’t complain for throwables part of the following super-classes:

  • Error
  • RuntimeException

java.util Exceptions

There are several exceptions already part of the java.util package:

  • ArithmeticException
    • Arithmetical operations such as dividing by zero.
  • ArrayIndexOutOfBoundsException
    • Trying to access an index that is negative or bigger than the size of an array.
  • FileNotFoundException
    • Trying to access a file that doesn’t exist or is otherwise not accessible.
  • NullPointerException
    • Reggering to memebers of a null object.
  • InputMismatchException
    • Unexpected format e.g for String args.

    This is quite useful.

  • StringsIndexOutOfBoundsException
    • Using an index that is negative or beyond the length of the String object.

Have a look for standard exceptions before making your own.

Exceptions - 1 - Code Patterns

COMP122 Lectures

Normal Program Flow

In normal program flow, one subroutine will call another while the original is put on hold:

graph LR
main -->|1| x
x -->|2| y
y -->|3| x
x -->|4| main

This is called the call stack.

Errors

There are several ways to handle errors if they occur in the call stack.

  • Account for all possible errors in the function that you call.
  • Pretend that the error didn’t happen and continue with the calling function.
  • Have the called function return an error code.
  • Have the called function raise an exception.
    • This reports that an anomaly has occurred.
    • If this is the case the interpreter will execute the exception handler from the calling function.
      • If there is no exception handler in the calling function we look further up the call stack until one or none is found.

Exception Handling

  • This method of error handling standardised error handing.
    • Compiler support.
  • Clean separation of error handling and normal code.

There are two aspects to exception handling:

  • To report an anomaly you raise or throw and exception.
  • To deal with possible exceptions, define suitable handler.

Common Pattern

try {
	do stuff;	// errors may happen
}
catch exception {
	deal with it;	// handler
}

The exception could have the following effects:

  • Report and re-throw.
  • Log and continue.

Exception handling in OOP

Exceptions are objects.

When an anomaly occurs an Exception object is created:

  • It represents the situation and has relevant info.
    • As attributes.
  • The environment looks for a handler based on its type.

Exception Classes

classDiagram
Exception <|-- FileNotFoundException
Exception <|-- DivByZeroException
Exception <|-- ArrayOutOfBoundsException
Exception: +get_trace() String
FileNotFoundException: +path String

You could also have a deeper hierarchy for particular types of exception:

classDiagram
Exception <|-- IOException
Exception <|-- DivByZeroException
Exception <|-- ArrayOutOfBoundsException
IOException <|-- FileNotFoundException
IOException <|-- FileAccessException
Exception: +get_trace() String
FileNotFoundException: +path String

Extended Pattern

This is a pattern that used multiple exception handlers from different classes:

try {
	// open a file for writing
}
catch (FileNotFoundException e) {
	// complain that e.path was invalid
}
catch (FileAccessException e) {
	// complain that file is read-only
}

Iterators

COMP122 Lectures

Using for loops on lists can be dangerous as their size is not static.

We can separate the traversal logic from the underlying collection by using iterators.

Iterator Design Pattern

Iterators provide a way to access elements of an aggregate object sequentially without exposing its underling representation.

  • Iterators are objects.
  • Single-use
    • Represents one complete iteration across the collection.
  • Can provide safe access to a collection.
  • Are often created by the collection itself.

Example

We can filer a List<Bill> using an Iterator as follows:

// get an iterator from the collection
Iterator<Bill> iter = bills.iterator();

while (iter.hasNext()){
	Bill b = iter.next();	// access next entry
	if (b.is_paid()){
		iter.remove();	// remove it from the list
	}
}

Java Iterators

  • Iterator<T> is a parameterised interface. You must specify the type of object stored in the collection.
  • Iterators provide a safe remove() method that removes the last element returned by its next() method.
  • This is different from the remove() method of the collection.

Alternative for Syntax

If you don’t need to write to the collection, so don’t need the iterator, then you can use the following syntax:

for (Bill b : bills){
	// System.out.println(b);
}

which is equivalent to:

Iterator<Bill> iter=bills.iterator();
while (iter.hasNext()){
	Bill b = iter.next();
	// System.out.println(b);
}

Collections (Generics, Stacks, Hash Maps)

COMP122 Lectures

Collections are containers to store objects:

  • They are parameterised by a base type.
  • They can be dynamically resized.
  • They can store and access content in different ways.

Example - List of Strings

import java.util.*;
List<String> customers;
customers = new ArrayList<String>();

customers.add("Ms. X");
customers.add("Mr. Y");
customers.add("Mx. Z");

int count = customers.size();
String next = customers.get(2);

This shows that we can get an element from a list of strings using x.get() and the size of a list with x.size().

From this code we can make the following UML diagram:

classDiagram
Collection <|-- List
List <|.. ArrayList
class Collection{
    <<Interface>>
}
class List{
    <<Interface>>
}

This is to say that a List is a type of Collection and ArrayList is an implementation of a List.

This is an example of generics/parameterised types as you can change String to another object.

Generics

You should be careful when using generics as they only accept objects and not primitives/base-types.

You must use a wrapper class such as Integer instead of int to overcome this.

Inside java.util

classDiagram
Collection <|-- List
Collection <|-- Set
Collection <|-- Queue
List <|.. ArrayList
List <|.. Linked List
List <|.. Vector
Set <|.. HashSet
Set <|.. LinkedHashSet
Set <|.. TreeSet
Queue <|.. Dequeue
Queue <|.. LinkedQueue
Queue <|.. PriorityQueue
Collection: <<Interface>>
List: <<Interface>>
Set: <<Interface>>
Queue: <<Interface>>

Stacks

Stack<Integer> st = new Stack<Integer>();

// push to the stack
st.push(12);
st.push(-1);
st.push(10);
System.out.println(st);

// pop the topmost stack element
int x = st.pop();

You can also call the size method from Collection to find the size of a stack.

Maps

  • Store (key, value) pairs.
  • Have two parameters (key type, value type).

A useful implementation is java.util.HashMap.

  • HashMap<K,V> requires that K.hashCode() exists.

HashMap Example

// create a mapping from strings to integers.
Map<String, Integer> wc = new HashMap<String, Integer>();

// store a (key, value) pair
wc.put("place", 3);

// access the value associated with a key
int number = wc.get("place");

// check if key has a value
bool contains = wc.containsKey("and");

// remove some pair completely
wc.remove("the");

Map is the interface and HashMap is the class that implements that interface.

Why Use Collections

To utilise established and well-tested implementations from common algorithmic data structures and tasks.

  • Avoid linked lists.
  • Maximised code-reuse.
  • Ensure inter-operability.

It is always best to use the standard library if an implementation exists.

Abstract Classes

COMP122 Labs

UML diagrams including abstract classes and interfaces can be drawn in the following way:

classDiagram
Degreeable <|.. Dog
Emailable <|.. ResearchCouncil
Billable <|.. ResearchCouncil
Degreeable <|.. Student
Billable <|.. Student
Emailable <|.. Person
Person <|-- Lecturer
Payable <|.. Lecturer
Person <|-- Student
Lecturer <|-- Professor
class Person{
    <<Abstract>>
    -name String
    -email String
    +greet() String
    +setName(String)
    +getName() String
    +setEmail(String)
    +getEmail() String
}
class Student{
    -grade int
    +setGrade(int)
    +getGrade() int
}
class Lecturer{
    -timetable String
    +setTimeTable(String)
    +getTimeTable() String
}
class Professor{
    -budget int
    +setBudget(int)
    +getBudget() int
}
class Degreeable{
    <<Interface>>
    +awardDegree()
}
class Billable{
    <<Interface>>
    +payBill(int)
}
class Emailable{
    <<Interface>>
    +sendEmail()
}
class Payable{
    +payAmount(int)
}
class Dog{
    -goodGirl Boolean
}
class ResearchCouncil{
    -name String
    -email String
    +greet() String
    +setName(String)
    +getName() String
    +setEmail(String)
    +getEmail() String
}

Interfaces

COMP122 Lectures

Interfaces allow you to abstract the workings of your code and provide simple interfaces to the user:

Lamp Example

public interface Switchable {
	public void switchOn();
	public void switchOff();
}

This defines the interface type that someone can call.

Switchable l = new Lamp();
l.switchOn();
l.switchOff();

This is the use for the interface.

public class Lamp implements Switchable {
	public void switchOn() {
		// method
	}
	public void switchOff() {
		// method
	}
}

This is the implementation of the interface.

Interfaces

In java, an interface is a specification which public methods must exist in a class that implements it.

This is like a contract that the front and back-end programmers must adhere to.

  • Interfaces define a type.
  • Typically only contains constants and methods signatures.
    • Not the method bodies.
  • Cannot be instantiated.
  • Can be implemented by a class, which then has to contain all method bodies declared in the interface.
  • Can be extended by other interfaces.
  • A class can implement more than one interface.

Extending Interfaces

A class can implement several interfaces. Such a class would have to implement all methods from all the interfaces it implements.

public class Lamp implements Switchable, Pluggable {
	// Switchable methods
	// Plugable methods
}

An interface can itself extend one or more other interfaces:

interface Dimmable extends Switchable, Plugable {
	...
}

This means that if you want to provide Dimmable then you have to provide implementations for all the methods in the other interfaces.

An interface cannot implement other interfaces:

public interface Dimmable implements Switchable {
	...
}

This does not work.

Implementing Multiple Interfaces in a class

A class can also extend from other classes while implementing interfaces:

public class Lamp extends Furnature implements Switchable, Pluggable {
	// Switchable methods
	// Plugable methods
}

Conventions

  • Interface identifiers end in “-able”:
    • java.Lang.Comparable
    • java.io.Serializable
  • All interface methods are public and abstract.
  • All attributes are public, static and final.

Interfaces vs. Abstract Classes

Abstract Classes Interfaces
Can only extend one superclass. Multiple inheritance between interfaces.
Can extend any class. Can only extend interfaces.
May have abstract and concrete methods. Only abstract methods.
protected methods allowed. Methods are public abstract.
No restriction on attribute modifiers. Only public static final variables.
  • The purpose of abstract classes is to provide abstraction when designing type hierarchies or class hierarchies.
  • Interfaces are for specifying the public facing services or to coordinate several groups of programmers/software.

Abstract Classes

COMP122 Lectures

We can stop instantiations of certain superclasses by making them abstract:

classDiagram
class Shape
<<abstract>> Shape
Shape : +colour String
Shape <|-- Circle
class Circle{
	+radius double
	+area() double
}

The name of this class should be in italics. If it is abstract.

I have used an annotation here which is also acceptable.

Code Example

public abstract class Shape {
	public String colour;
}
public class Circle extends Shape {
	public double radius;
	public double area() {
		return (radius * radius) * Math.PI;
	}
}
Shape s = new Circle();
Shape s = new Shape(); // not possible as Shape is abstract

Abstract Classes

  • Cannot be instantiated.
  • Can be extended and concrete (non-abstract) subclasses can be.

Abstract Methods

  • Do not provide a full implementation.
  • They have to be overrode by subclasses.

Example

public abstract class Shape {
	public String colour;
	public abstract double area();
	public String report() {
		return "My area is " + area();
	}
}

By stating abstract on the area we say that this method must be overrode by subclasses.

You can also call abstract methods before they are defined.

public class Circle extends Shape {
	public double radius;
	public double area() {
		return (radius * radius) * Math.PI;
	}
}
Shape s = new Circle();
double a = s.area();
double a = ((Circle) s).area();
String r = s.report();

If we implement an abstract method for Shape.area then the second line will work. If not we will have to type-cast like in the third line.

Summary

There are two good reasons for making a class `abstract:

  1. To prevent it from being instantiated.
  2. To enforce that concrete (instantiable) subclasses override.

Polymorphism

COMP122 Lectures

Class Hierarchy = Type Hierarchy

Every class defines a data type.

  • Subclasses therefore define sub-types.

Example

Every circle is also a shape and thus can be assigned to a variable of type Shape.

classDiagram
	Shape <|-- Circle
	class Shape{
		+colour String
		+toString() String
	}
	class Circle{
		+radius double
		+area() double
		+toString() String
	}
Shape s = new Circle();
s.colour = "red";
s.radius = 2.0; // this will fail 
s.to.String(); // this will call Circle.toString()

Assigning Variables Between Subclasses

Circle c = new Circle("red");
Triangle t = new Triangle ("blue");

Shape s = c; // makes a new shape initialised to c
System.out.println(s); // calls s.toString() from circle

s = t; // reassigns to triangle
System.out.println(s); // calls s.toString() from triangle

You can see that the Shape s inherits the methods from it’s assignment when assigned a variable of a subclass.

Type Casting

This is telling the compiler that an object is from a particular subclass and not of a superclass.

Shape s = new Circle();
Circle c = s; // will fail as not every Shape is Circle
Circle c = (Circle)s; // will pass as we know s is Circle

Method Overriding

COMP122 Lectures

Overriding a Superclass Method

A subclass inherits the public or protected attributes and methods in its superclass.

It can override an inherited method (identified by its signature) by re-implementing an inherited method.

Shapes Example

classDiagram
	Shape <|-- Circle
	class Shape{
		+colour String
		+toString() String
	}
	class Circle{
		+radius double
		+area() double
	}

Consider that the Shape.toString() function returns a statement like: “I am a colour shape.”

You may want to re-implement this for the subclass to be more specific.

Example in Java

The following code would emulate the behaviour above:

public class Shape {
	public String colour;
	public String toString() {
		return "I'm a " + colour + " shape!";
	}
}
public class Circle extends Shape {
	public double radius;
	public String toString() {
		return "I'm a " + colour + " circle!";
	}
	// Show print functions
	public static void main(String[] args) {
		Shape s = new Shape();
		s.colour = "red";
		System.out.println(s.toString());
		Circle c = new Circle();
		c.colour = "red";
		System.out.println(c.toString());
	}
}

Object Class

As all classes are subclasses of the Object class we can draw the following hierarchy:

classDiagram
	Shape <|-- Circle
	Object <|-- Shape
	Object: +toString() String
	class Shape{
		+colour String
		+toString() String
	}
	class Circle{
		+radius double
		+area() double
		+toString() String
	}

The Object class has a toString() method of its own so we have also overwritten it using Shape. This is the reason why you can always print a given object by calling:

className.toString();

Overriding vs. Overloading

  • Overriding - Involves providing several methods with the same name and parameter list, but declared in classes which are in a subclass/superclass relationship.
  • Overloading - Involves providing several methods with the same name, but different parameter lists.

Subclasses

COMP122 Lectures

Inheritance

This is a mechanism of creating a new subclass based on an existing superclass, retaining a similar implementation.

This allows the following two:

  • Abstraction - It allows to express an “is-a” relationship. Every instance of the subclass is also and instance of the superclass.
  • Code re-use - A subclass inherits methods/attributes and only needs to implement what’s new.

Shapes Example

Our existing example was left with the following classes:

classDiagram
    class Circle{
    +colour String
    +radius double
    +area() double
    }
    class Square{
    +colour String
    +corners int
    +side double
    +area() double
    }
    class Triangle{
    +colour String
    +corners int
    +side double
    +area() double
    }

We can make this into a super-class of Shape buy reassigning redundant variables:

classDiagram
    Shape <|-- Circle
    Shape <|-- Square
    Shape <|-- Triangle
    Shape: +colour String
    class Circle{
    +radius double
    +area() double
    }
    class Square{
    +corners int
    +side double
    +area() double
    }
    class Triangle{
    +corners int
    +side double
    +area() double
    }

You could also make an additional intermediate class:

classDiagram
    Shape <|-- Circle
    Shape <|-- Polygon
    Shape: +colour String
    Polygon: +corners int
    Polygon: +side double
    Polygon: +area() double
    Polygon <|-- Square
    Polygon <|-- Triangle
    class Circle{
    +radius double
    +area() double
    }

This tree is called a class hierarchy

Always exclude all attributes and methods from the superclass in subclasses.

classDiagram
classA --|> classB : Inheritance
classC --* classD : Composition
classE --o classF : Aggregation
classG --> classH : Association
classI -- classJ : Link(Solid)
classK ..> classL : Dependency
classM ..|> classN : Realization
classO .. classP : Link(Dashed)

Java Syntax for Subclass Definitions

The Java keyword extends is used to specify the subclass/superclass relationship in class definitions.

public class Triangle extends Polygon {
	// public int colour;	// inherited from Shape
	// public int corners;	// inherited from Polygon
	public double side;	// only in Triangle
	public double area; () {...}
}

Single vs. Multiple Inheritance

classDiagram
    Shape <|-- Triangle
    MusicalInstrument <|-- Triangle
    Shape: +colour String
    MusicalInstrument: +pitch int
    class Triangle{
    +corners int
    +side double
    +area() double
    }

In Java this is not possible, every class has exactly one superclass, with the exception of Object.

The exception is because Object is the root class, meaning every class is a sub-class of Object.

What is Inherited?

A subclass inherits all public or protected members of its superclass. This includes:

  • Attributes
  • Methods

This does not include constructor methods as they are not members of the super-class.

protected

This is similar to private fields or methods but they cannot be accessed from outside an instance directly.

A protected field or method is inherited whereas a private identifier is only accessible in the class where it is defined.

In UML diagrams, # depicts protected class members.

Uses of Inheritance

  • Inheritance can introduce more abstraction in the code.
  • It enhances core re-use.
  • It improves the code readability.
  • Properly applied, inheritance can reduce software maintenance costs.

Class Variables & Methods

COMP122 Lectures

Instance Variables

Ordinarily, attribute values belong to individual objects and methods describe the object’s individual response to messages.

An example is that two alarm clocks can each have their own type.

Class Variables

Using the keyword static you can declare that an attribute (or method) belongs to the class. This means that it is shared among all instances.

An example would be that all alarm clock from your manufacturer have the same make.

Static Variables & Methods

  • The values of static variables can be read and modified by all instances of the class, as well as from within static methods.

    Changing the make of one clock will affect all other clocks.

  • static methods cannot access instance variables or instance methods but only other static members.

    One clock cannot directly find out the time of another clock without a service.

  • static methods can be called directly on the class without instantiating an object. E.g. AlarmClock.ring();.

    static and public methods can be called without making an object.

Encapsulation

COMP122 Lectures

Public and Private Content

Attributes and methods can be public or private:

  • private members of a class cannot be accessed from outside the object.
  • public can be accessed from outside.
    • This can be done via the dot notation:
      int t = alarmClock.time;
      alarmClock.setAlarm(t+5);
    

    The public members form the object’s interface. This consists of the messages it can receive and the services it can offer.

Information Hiding

It is good practice to declare attributes private to avoid accidental outside interference or messy dependencies.

This principle is called information hiding or encapsulation.

Setting Private Attributes via Methods

You can introduce public methods whose sole job it is to get or set the value of private attributes.

  • Methods that modify attributes are called mutators.
  • Methods that retrieve the value of an attribute are called accessors.

It is convention to call them getX or setX for private attributes X.

Alarm Clock Example

classDiagram
class AlarmClock{
	-time int
	-alarm int
	+getTime()
    +setAlarm(int)
}

- means private and + means public.

AlarmClock a  new AlarmClock();
int t = a.getTime();
a.setAlarm(t + 5);

Constructors

COMP122 Lectures

We recall that classes are templates for creating objects and we can instantiate an object by using the keyword new.

Behind the scenes the following happen:

  • The JVM allocates memory to store the new object.
  • A constructor method is called to initialise it.

Constructor Methods

A constructor method is a special method that gets called when the object is created. It is intended to set up the object for later use.

The syntax for constructor methods is almost the same as for normal methods, except that:

  • It must be named just like the class.
  • It has no declared return type.

Example

public class Rectangle {
	private int side;
	private String colour;
	
	public Rectangle(int s, String c) { // constructor method
		side = s;
		colour = c;
	}
}

Can be instantiated by:

Rectangle r = new Rectangle(5, "red");

Multiple Constructors

A class can have more than one constructor (which requires the to have different signatures).

This can be useful to make an object that can change one attribute without dealing with the rest.

If you don;t define any constructors, the class will automatically get a trivial default constructor without arguments.

One Constructor can Call Another

public class Rectangle {
	private int side;
	private String colour;
	
	public Rectangle(int s, String c) { // line 5
		side = s;
		colour = c;
	}
	public Rectangle(int s) {
		this(s, "blue"); // call line 5
	}
}

This can set a default of blue an just set the side length.

Classes

COMP122 Lectures

There are two methods of creating objects:

  • Copying and adjusting existing ones.
  • Instantiating a template.

Classes

A class is a template of blueprint for objects:

  • It specifies which attributes and methods should exist.
  • An object can be an instance of the class.

For example you may have many instances of rectangles that follow the following class:

classDiagram
class Rectangle{
	-int size
	-int colour
	+area() int
}

This is an UML class diagram.

Class Diagrams

The boxes are laid out in the following order:

  1. Name
  2. Attributes
  3. Methods

Java Objects & Classes

To create objects in Java you need to:

  • Define a class.
  • Instantiate a new object of that class.

Defining a Class

public class Rectangle{
	// Attributes
	private int side;
	private int colour;
	// Methods
	public int area() {
		return side * side;	
	}
}

Instantiating an Object

Every class defines a type and so variables can be declared like this:

Rectangle r;

This defines a variable of the type Rectangle called r.

Object can be instantiated using the keyword new like this:

r = new Rectangle();

Objects

COMP122 Lectures

An object represents a specific, identifiable part of the world-model:

  • It incorporates (passive) attributes.
    • Variables.
  • It has (active) behaviours, services.
    • Methods.
  • Objects interact by sending messages.
    • Calling methods.

Here is an example of three objects:

Three shape objects each with their own attributes.

Here is an interaction between Russ, the ATM near the guild and his account:

graph LR
Russ -->|"requestBalance()"| ATM
ATM -->|152.50| Russ
ATM -->|"getBalance()"| BA[Bank Account]
BA -->|152.50| ATM

Russ has no idea about what happens inside the ATM and only able to directly interact with his account.

Javadoc

COMP122 Labs

Javadoc automatically creates docs from javadoc comments in your code.

Javadoc comments are multi-line comments that start with /**, instead of /*. You can write HTML, text and tags in them.

Generating Javadocs

Generating javadocs is done with the javadoc command:

 $ javadoc -d ./docs source.java

This will make a static page with source ./docs.

Tags

The following tags are available as sourced from: wikipedia.org/wiki/Javadoc.

The tags used in this lecture are highlighted in bold.

Tag & Parameter Usage Applies to Since
@author John Smith Describes an author. Class, Interface, Enum  
{@docRoot} Represents the relative path to the generated document’s root directory from any generated page. Class, Interface, Enum, Field, Method  
@version version Provides software version entry. Max one per Class or Interface. Class, Interface, Enum  
@since since-text Describes when this functionality has first existed. Class, Interface, Enum, Field, Method  
@see reference Provides a link to other element of documentation. Class, Interface, Enum, Field, Method  
@param name description Describes a method parameter. Method  
@return description Describes the return value. Method  
@exception classname description / @throws classname description Describes an exception that may be thrown from this method. Method  
@deprecated description Describes an outdated method. Class, Interface, Enum, Field, Method  
{@inheritDoc} Copies the description from the overridden method. Overriding Method 1.4.0
{@link reference} Link to other symbol. Class, Interface, Enum, Field, Method  
{@linkplain reference} Identical to {@link}, except the link’s label is displayed in plain text than code font. Class, Interface, Enum, Field, Method  
{@value #STATIC_FIELD} Return the value of a static field. Static Field 1.4.0
{@code literal} Formats literal text in the code font. It is equivalent to {@literal}. Class, Interface, Enum, Field, Method 1.5.0
{@literal literal} Denotes literal text. The enclosed text is interpreted as not containing HTML markup or nested javadoc tags. Class, Interface, Enum, Field, Method 1.5.0
{@serial literal} Used in the doc comment for a default serializable field. Field  
{@serialData literal} Documents the data written by the writeObject( ) or writeExternal( ) methods. Field, Method  
{@serialField literal} Documents an ObjectStreamField component. Field  

Seminar 3

COMP122 Seminars

Division by Zero in Java

Generally, will give no error and the result Infinity.

If you divide 0.0 by 0.0 then you will get the result NaN.

Code Blocks

Indentation is ignored by the compiler. You should use {} to properly define your blocks.

switch statements

This can we used as a case select statement.

switch (x) {
	case 0:
	case 1: // code to run
		break; // this will have it stop after the block.
	default: //default code to run
		break;
}

The break reserved word ceases execution of the switch block. The default case is not required.

Inherits from C/C++

int a = 0;
int b = 0;

int c = 12 > 10 ? ++a : b++;	// the ? can be used in place of an if

If the condition before ? is true then the first condition is executed. If false then the condition after : is executed.

To increment numbers ++ can be used. There can be two cases:

  • a++
    • This evaluates to the old value of a but increments a.
  • ++a
    • This evaluates to the increment of a and increments a.

A Random Walk Example

Generating a Random Value of 1/-1

Math.random() gives a decimal number in the range $0\leq x<1$.

You can use Math.floor(x) to concatenate to integer.

Math.floor(Math.random()) will give 0 or 1 randomly.

Assessment 1

This assessment is based around the Caesar Cipher.

Imports, Packages & the Classpath

COMP122 Lectures

When you are writing a large Java program you can run into the problem of a cluttered name-space. This means that you are running out of fresh names for your classes.

Packages

A Java package is a collection of related classes.

  • The full name of a Java class if of the form package.class.
  • Packages can be nested.
  • Packages from the “standard lib” start with java or javax.

Example

The System class is in package java.lang. So it’s full name is java.lang.System.

Import

The import statement introduces a class to the current name-space.

The use java.util.Scanner to get user input we can tell the compiler to include that class at the top of a source file:

import java.util.Scanner.

This is a convenience. Instead, without importing it, we cal still refer to java.util.Scanner by its full name.

Apropos Convenience

The java.lang package is automatically available to all classes without using the import statement. This is why we could refer directly to System and write:

System.out.println("Hello World");

instead of having to use its full package name:

java.lang.System.out.println("Hello World");

Creating Packages

To assign a class to a package, we need to do two things:

  1. Add a package statement to the source code:

     package mypackage;
    

    Classes without this live in an unnamed default package.

  2. Move the source file into a directory mypackage/ Both compiler and interpreter will look for class files in a directory names just like their package.

Packages are not required for the assignments.

Classpath

This is a list of directories from which packages are looked up.

  • javac and java both have command-line arguments to set the classpath.
  • The current directory is automatically added.
  • You can use the environment-variables to set the classpath. (CLASSPATH on Linux).

Conventions

  • Package names are usually all lower case to avoid conflict with class names.
  • It is common to use reversed domain names for packages.
    • Yields globally unique package names.
    • Avoids naming collisions between libraries from different sources.
      • org.w3c.dom

Loops

COMP122 Lectures

for Loops

These are used to repeat a block of code a fixed number of times:

for (initialisation;
	condition;
	update) {
		// stuff to repeat
}

Example

Print the first 10 integers:

for (int i = 1; i <= 10; i++) {
	System.out.println(i);
}

In this case the {} aren’t required as a single line statement is a block.

while Loops

while(condition) {
	// stuff to repeat
}

The condition is tested before running the loop. do while loops check after.

Example

This program adds integers until the user types 0:

//import java.util.Scanner;
Scanner input = new Scanner();

int total = 0;
int value = input.nextInt();

while (value != 0) {
	total += value;
	value = input.nextInt();
}
System.out.println(total);

Methods

COMP122 Lectures

Methods are named code blocks. They:

  • are defined inside a class definition.
  • can have arguments and return value.
    • They can return void.
  • correspond to functions or procedures in other languages.

Defining Methods

modifiers returnType methodName (parameters){
	// method body code here
}
  • modifiers determine how the method can be accessed.
  • returnType is the type of the returned value.
  • methodName an identifier.
  • parameters a (comma-separated) list of arguments, each given as a type and an identifier.

    Order matters!

  • // method body the code block that defines the method’s behaviour.

Example

public class MaximumDemo {
	
	public static int maximum(int a, int b) {
		if(a>= b)
			return a;
		else
			return b;
	}
}
  • public and static are modifiers.
  • int is the return value.
  • maximum is the identifier.
  • a and b are two arguments of type int.

Signatures

The modifiers, spelling of the identifier, types and ordering of the parameters together form the signature of the method.

A method is uniquely identified by it’s signature.

The following all have different signatures:

public static int max (int a, int b) {}
public static int maX (int a, int b) {}
public static int max (int a, double b) {}
public static int max (double b, int a) {}
public int max (int a, int b) {}

This is the same as the first above:

public static int max(int b, int a) {}

This is because the identifier of the parameter doesn’t change the signature.

main

The main method is a method like any other, except that the interpreter will look it up and call it when it starts.

We can access it’s command-line parameters via args:

public class Hello {
	public static void main(String[] args){
		System.out.print("Hello " + args[0] + "!");
	}
}

This will produce the following when run:

 $ java Hello Ben Weston
Hello Ben!

As the second argument is not used, it is not printed.

Conditionals

COMP122 Lectures

Imagine a program that does the following:

graph TD
i[Get Input]
i --> p[Positive?]
p -->|yes| Process
p -->|no| Complain
Process --> b[ ]
Complain --> b

if-else Statements in Java

if (condition) {
	// do stuff
}
else {
	// do something else
}

This means that if the condition holds the if bracket is executed and if not the else bracket is.

Relational Operators

The condition takes a boolean variable. Here are functions that take two inputs and return true or false:

Operator Meaning
> greater than
< less than
== equal to
>= greater than or equal to
<= less than or equal to
!= not equal to

Boolean Operators

Operator Meaning
! NOT
&& AND
|| OR
^ XOR

Initial Example

The initial example would be coded as follows:

int x = 5; // get input somehow

if (x > 0) {
	System.out.println("you gave me: " + x)
}
else {
	System.out.println(x + " is not positive!")
}

Arrays

COMP122 Lectures

Arrays are variable storing fixed-length lists of values with the same type.

For example we can have an integer array called count and type int[].

Array Types

For every type x there is a corresponding array type x[].

Declaring Array Variables

int[] numbers;
double[] reals;
boolean[] truthValues;

Creating Arrays

In order to use an array variable, the compiler needs to know the length of the array to allocate the appropriate chunk of memory.

There is a special statement that creates a new array:

double[] rates = new double[200];
boolean[] truthValues = new boolean[18];

// Length defined by another variable
int alphabetSize = 26;
int[] count = new int[alphabetSize];

Remarks on Arrays

  • Every array has a length attribute which tells you its size:

      int sizeOfAlphabet = count.length;
    
  • Array elements are accessed by index using square brackets:

      numberOfAs = count[0];
      numberOfCs = count[2];
    

Multi-dimensional Arrays

Every type x has an associated type x[]. This includes arrays. For example we can represent a chess board as 2D integer arrays.

int[][] board = new int [8][8];

You can also define arrays literally:

int[][] board = {
	{0,0,0,0,0,0,0,0},
	{0,0,0,0,0,0,0,0},
	...
	{0,0,0,0,0,0,0,0}
}

Variables

COMP122 Lectures

A variable is a named memory location that can be changed during the program’s execution. It has three properties:

  1. A type.
  2. A value.
  3. A name or identifier.

Identifiers

Not all identifiers are valid. They can be an sequence of alphanumeric characters, $ and _ unless:

  • They start with a number.
  • It is a keyword.

Identifiers are case sensitive.

Conventions

  • Class names start in capital letters.
  • Method and variable names start in lower case letters.
  • CamelCase is used if several words make up and identifier.

Example

In the following code all of the following keywords are identifiers:

HelloWorld, main, String, args, System, out and println.

public class HelloWorld {

	// Main Method
	public static void main(String[] args) {
		System.out.println(*"Hello World");
	}
}

Variable Declarations

A declaration statement declares a variable to be of a particular type and is of the form:

<type> <variableName>;

where <type> is a type and <variableName> a legal identifier.

double currentWeight;
int studentsLearningJava;
int maximumValue;

Variables must be declared before you can use them.

Variable Assignments

An assignment statement stores a value in a variable as follows:

<variableName> = <value>;
  • <variableName> may be any already declared identifier.
  • <value> is a value of the same type as <variableName>.

Declaration and Initialisation

We can also combine declaration a variable with the assignment of an initial value (initialisation) as follows:

<type> <variableName> = <value>;
double currentWeight = 122.5;
int studentsLearningJava = 78;

Data Types

COMP122 Lectures

Java is strongly typed. This means that all types consist of a value and a type.

Types of Types

There are two different types of type in Java.

Primitive

  • int, byte, short and long
  • float, and double
  • char
  • boolean

Composite

  • string
  • Arrays e.g. int[]

Literals

Literal values are the actual value of some type that occurs.

  • 0 or 574489 are literals of type int
  • true and false are literals of type boolean

Lab 1

COMP122 Labs

Variables

Testing equivalence of two floating point numbers should be done as follows to account for error:

double_1 == double_2 // Returns False
Math.abs(double_1 - double_2) < 0.000001; // Returns True

To use the maths library for mathematical constants use the following to import:

import java.lang.Math

You would use the following in your code when you need $\pi$.

Math.PI

Control Flow

If statements can be made and chained as follows:

if (grade > 70) {
  System.out.PrintLn("Congrats on your first!");
}

else if (grade > 60) {
  System.out.PrintLn("Nicely done, you got a 2:1!");
}

else if (grade > 50) {
  System.out.PrintLn("Looks like you could have passed some more of the unit tests, but your code still passes most. ");
}

else if (grade > 40) {
  System.out.PrintLn("You've passed this lab, but try and make sure you follow the specification closely and make a good attempt at each problem. Semi-functional code is always better than no code at all.");
}

else {
  System.out.PrintLn("You haven't managed to pass this lab, but don't worry as there is still plenty of time to go back and improve. Watch the lecture videos, try to follow what's needed in the labs, and ask the TA's for help in a lab session if there's any concepts you don't get, that's what they're there for!");
}

Remember to use curly braces {} and to only end the content of the if with ;.

The Compiler is Your Friend

COMP122 Seminars

Doc Comments

This is the proper way to write multi-line comments to summarise a code file. You can later compile these into doc files:

/**
* This program does such and such.
* @author Ben Weston <b.weston60@gmail.com>
*/

Strongly Typed Languages

In strongly typed languages you must explicitly say the type of a variable. You must also match your types appropriately when completing manipulations.

Strongly typed languages give the following benefits:

  • Prevent accidental (or malicious) memory access.
  • Capture preventable runtime errors at compile time.
    • Compiler complains, not your users.

Type Casting

Is when you assign a value of one primate data type to a variable of another type.

  1. Widening Casting (automatic)
    • byte $\rightarrow$ short $\rightarrow$ char $\rightarrow$ int $\rightarrow$ long $\rightarrow$ float $\rightarrow$ double
     int myInt = 9;
     double myDouble = myInt; // Assigns 9.0
    
  2. Narrowing Casting (manually)
    • double $\rightarrow$ float $\rightarrow$ long $\rightarrow$ int $\rightarrow$ char $\rightarrow$ short $\rightarrow$ byte
     double myDouble = 9.78;
     int myInt = (int) myDouble;
     System.out.println(myDouble); // Outputs 9.78
     System.out.println(myInt); // Outputs 9
    

Constants

Constant are variable whose values don’t change in runtime:

final <type> <CONSTANT_NAME> = <value>;

Here final is a keyword to denote that the following is a constant.

By convention identifiers denoting constants are in all caps.

Why Use Constants

Compiler optimisations will result in smaller, binary code with a smaller memory footprint.

It also helps with readability so that you know what the values in your code mean.

Finally it makes your code easier to maintain as changes propagate through the program.

Module Introduction

COMP122 Seminars

  • Mon-Tue (1-2h)
    • Watch videos, take a quiz, ask discussion questions.
  • Tue-Wed (3h)
    • Attend a lab and solve programming exercises.
  • Wed (1h)
    • Lecture, Q&A at 1pm.

Contact

Enquiries should be directed to Patrick Totzke at: totzke@liverpool.ac.uk.

The professor will also be on Gather on Wednesdays 2-3pm.

Introduction to OOP

OOP is based on the idea of interacting object which contain both data and procedures and are an instances of a whole “class” of similar objects.

  • Encapsulation
    • Grouping data and code that acts on it into a single unit.
  • Abstraction
    • Hiding implementation details from users.
  • Inheritance
    • Using known classes of objects as blueprints from more specific ones.
  • Polymorphics
    • Different behaviours of subclasses by reconfiguring methods.

Class Introduction

Suppose that we make a class of person. They have data associated with them and methods they can perform.

You can have many instances of this object with different data.

Subclasses

Extending this, a student is a person with additional data (such as studentID)

You can make a subclass that extends the original class. This means that you inherit the information from the parent class.

Polymorphism

This is the concept that different objects in different subclasses, but in the same superclass, can be addressed in the same way but retrieve different results.

An example of this is a student who is foreign may respond to a greeting in a different language.

Java Syntax

COMP122 Lectures

Java can be thought of as a sequence of statements with each statement ending with a semi-colon.

Comments

Everything after a double slash // is a comment.

Multi-line comments start with:

/*
and end in
*/

Blocks

Statements surrounded by curly brackets form a block. Blocks can be nested.

Example

/*
Author: Ben
The HelloWorld class implements an application that prints out "Hello World".
*/
public class HelloWorld {

	// Main Method
	public static void main(String[] args) {
		System.out.println("Hello World");
	}
}

The large block is a class definition which is named HelloWorld.

The main block is an actionable member of that class.

The class name must be the same as the name of the source code file in which it is defined (but with the extension .java

Java Keywords

There are some words that are reserved for the compiler can cannot be used for tother purposes such as variable names.

Main Methods

HelloWorld has only one method called main.

  • A class with a main method like this is called the application class.
  • The main method is the entry point of the application, from which the JVM begins the execution of the program.

Capitalisation and Indentation

  • Java is case sensitive so main is different to Main.
  • Indentation carries no meaning. The lines are ended by ;.

Code Conventions

  • Identifiers use CamelCase. Class names start in capitals; method and variable names in small letters.
  • Indent your code when nesting.
  • Add comments to explain what your code does.

Running Java Code

COMP122 Lectures

Java is an interpreted language that compiles into bytecode in order to make optimisations. To run a java program you would complete the following steps:

  1. Compile a source code file Hello.java with javac Hello.java. This will create a bytecode file Hello.class.
  2. Start the JVM and run Hello.class with java Hello.