Title Text

Title Text

Title Text

Title Text

Introduction to programming

ADVANCED JAVA

Access Control

Access level modifiers determine whether other classes can use a particular field or invoke a particular method.

ACcess levels

Live-coding

Access Control

JAVA EXCEPTIONS

What is an exception

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions.

 

The Java language uses exceptions to handle errors and other exceptional events. 

 

When an error occurs within a method, the method creates an object and hands it off to the runtime system. This object contains information about the error, including its type and the state of the program when the error occurred.

Types of exceptions

Exception throwing

When a method throws an exception, the JVM tries to find something to handle it.

 

The set of possible "somethings" to handle the exception is the list of methods called to get to the method where the error occurred. This list of methods is known as the call stack.

Exception throwing

class MyClass {

    public void myMethod() throws Exception {
        if(error) {
            throw new Exception();
        }
    }
}

CATCHING EXCEPTIONS

class MyClass {

    public void myMethod() throws Exception{
        if(error) {
            throw new Exception();
        }
    }
}
class Main {

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        
        try{
            myClass.myMethod();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

CReating custom EXCEPTIONS

public class MyException extends Exception{

    public MyException(String message) {
        super(message);
    }

    public MyException (){
        super("A custom exception.");
    }
}

USing custom EXCEPTIONS

class MyClass {

    public void myMethod() throws MyException{
        if(error) {
            throw new MyException();
        }
    }
}
class Main {

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        
        try{
            myClass.myMethod();
        } catch (MyException e) {
            System.out.println(e.getMessage());
        } catch (Exception e){
            System.out.println(e.getMessage());
        }
    }
}

THe finally block

class MyClass {

    public void myMethod() throws MyException{
        if(error) {
            throw new MyException();
        }
    }
}
class Main {

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        
        try{
            myClass.myMethod();
        } catch (MyException e) {
            System.out.println(e.getMessage());
        } catch (Exception e){
            System.out.println(e.getMessage());
        } finally {
            System.out.println("This will always happen, regardless of an exception being thrown or not";
        }
    }
}

Chained exceptions

class MyClass {

    public void myMethod(){
        try {
            myMethod2();
        } catch (MyException e) {
            System.out.println (e.getMessage());
        }
    }
    
    private void myMethod2() throws MyException {
        myMethod3();
    }
    
    private void myMethod3() throws MyException {
    	if(error) {
            throw new MyException();
        }
    }
}

EXERCISE

JAVA I/O

INPUT/OUTPUT OPERATIONS

"Every input results in an output."

This is the fundamental idea in which computers are based on.

 

Input - signals or data received by the system

 

Output - signals or data sent from the system.

java.io

The Java platform has a package called java.io that provides system input and output through data streams, serialization and the file system.

Java streams

An I/O stream represents an input source or an output destination. A Stream can represent many different kinds of sources and destinations, and support many different kinds of data.

 

A program uses an input stream to read data from a source and an output stream to write data to a destination. Both operations occur one item at a time.

INPUT BYTE streams

Programs use byte streams to perform input and output of 8-bit bytes. All input byte stream classes are descended from InputStream.

// READING BYTES FROM A FILE

public static void main(String[] args) {

    // OPEN AN INPUT STREAM WITH A FILE PATH AS THE SOURCE
    FileInputStream fileInputStream = new FileInputStream("file_path");

    // READ ONE BYTE; THIS IS A BLOCKING METHOD
    int b = fileInputStream.read(); // returns the next byte read, or -1

    // READ MULTIPLE BYTES
    byte[] buffer = new byte[1024];
    int num = fileInputStream.read(buffer); // returns the total number of bytes read, or -1 
        
    // ALWAYS CLOSE THE STREAMS
    fileInputStream.close();
}

OUTPUT BYTE streams

All output byte stream classes are descended from OutputStream interfaces.

// WRITE BYTES TO A FILE

public static void main(String[] args) {

    FileInputStream fileInputStream = new FileInputStream("file_path");

    // OPEN AN OUTPUT STREAM WITH A FILE PATH AS THE DESTINATION
    FileOutputStream fileOutputStream = new FileOutputStream("file_path");

    // WRITE ONE BYTE TO A FILE
    fileOutputStream.write(fileInputStream.read());

    // WRITE MULTIPLE BYTES TO A FILE
    String message = "This is a very profound message";
    fileOutputStream.write(message.getBytes());

    // ALWAYS CLOSE THE STREAMS
    fileOutputStream.close();
}

EXERCISE

Write a program capable of writing the contents of a file into another file.

CHARACTER STREAMS

Byte Streams should only be used to perform very low-level I/O. 

However, it's important to discuss them, since all the more complicated streams are built on top of byte streams.

 

The Java platform stores character values using Unicode conventions.

CHARACTER ENCODING SCHEMEs

Computers are able to represent letters by associating each character to a number. 

The lists of recognised characters are called encoding schemes.

CHARACTER ENCODING SCHEMEs

Unfortunately for us, there are many different character encoding schemes, i.e., there are many ways to map between bytes, code points and characters.

A few of them include:

ASCII 

ISO 8859-1 

UTF-8 

UTF-16 

UTF-32 

INPUT CHARACTER STREAMS

All input character stream classes are descendent from Reader.

// WRITE BYTES TO A FILE

public static void main(String[] args) {

    FileReader fileReader = new FileReader("file_path");
    char[] buffer = new char[1024];

    // READ THE FIRST 1024 CHARACTERS FROM THE FILE; USING THE DEFAULT ENCODING SCHEME
    int num = fileReader.read(buffer);

    fileReader.close();
}

OUTPUT CHARACTER STREAMS

All output character stream classes are descendent from Writer.

// WRITE BYTES TO A FILE

public static void main(String[] args) {

    FileWriter fileWriter = new FileWriter("file_path");
    fileWriter.write("Some pretty message to write to a file.");
    fileWriter.close();
}

BUFFERED STREAMS

The examples we've seen so far are handled directly by the OS. This can make a program much less efficient. 

 

To reduce this kind of overhead, the Java platform implements buffered I/O streams.

INPUT BUFFERED STREAMS

public static void main(String[] args) throws IOException {
        
    // THE BUFFERED READER WRAPS THE FILE READER
    BufferedReader reader = new BufferedReader(new FileReader("file_path"));

    String line = "";
    String result = "";

    while((line = reader.readLine()) != null) {
        result += line + "\n";
    }

    reader.close();
}

OUTPUT BUFFERED STREAMS

public static void main(String[] args) throws IOException {

    BufferedWriter writer = new BufferedWriter(new FileWriter("file_path"));
    writer.write(text);

    // IF THE BUFFER IS NOT FULL, FLUSH WILL FORCE WRITE
    writer.flush(); 
    
    // AUTO-FLUSH IS DONE ON CLOSE
    writer.close();
}

EXERCISE

Write a Java program capable of doing the following:

 

  1. Ask for user input
  2. Get the list of files present in a directory and write the name of those files in a new text file
  3. Check if a particular file is present in a particular directory
  4. Create a new file in the directory and name specified by the user

NESted classes

Nested classes

The Java programming language allows you to define a class within another class.

LIVE CODING

Nested classes - A Summary

A nested class is a member of its enclosing class.

 

Non-static nested classes have access to other members of the enclosing class, even if they are declared private.

 

Static nested classes do not have access to other members of the enclosing class (unless they're also static).

 

A nested class can be declared private, public, protected, or package private, contrary to outer classes, which can only be declared as public or package private.

Why use nested classes?

It's a way to logically group classes that are only used in one place: if a class is only useful to another class, then it makes sense to embed it inside that class.

 

It increases encapsulation.

 

It can lead to more readable and maintainable code, by reducing class file pollution.

EXERCISE

Add the filtering functionality to our Directory Analyser. 

It should now be able to list all the files that start with a combination of letters.

 

Check the FilenameFilter class and use an anonymous class for this exercise.

THE DYNAMIC

CONTAINERS EXERCISE

THE DYNAMIC ARRAY

The node contAINER

collections framework

The collections Framework

The iterator

An Iterator is an object that can be used to loop through collections, like the LinkedList and ArrayList we've talked about before. Iterating is a technical term for looping.

The Iterable Interface

Implementing this interface  forces us to implement the iterator() method. This allows the object to be the target of the enhanced-for loop statement.

 

Iterating through our NodeContainer class is uglier than it needs to be. If we make it implement Iterable, that operation becomes a lot cleaner.

public interface Interable {

    Iterator iterator();
}
public interface Iterator {

    Object next();
    boolean hasNext();
}

LIVE CODING

Making NodeContainer implement Iterable.

LIVE CODING

Storing Object objects in the NodeContainer: the cost of too much flexibility.

Generics

A generic type is a generic class or interface that is parameterised over types

public class Box {

    private Object object;

    public void set(Object object) { 
        this.object = object; 
    }
    
    public Object get() { 
        return object; 
    }
}
public class Box<T> {

    private T object;

    public void set(T object) { 
        this.object = object; 
    }
    
    public T get() { 
        return object; 
    }
}

The normal version

The generic version

<T>

T is a type parameter.

Type parameters go inside angled brackets.

T defines a generic type that will be specified at the time of creation of a Box object.

public class Main {

    public static void main(String[] args){
    
        // CREATING A BOX OF BALLS
        Box<Ball> ballBox = new Box<>();
        
        ballBox.set(new Ball("football"));
        ballBox.set(new Ball("tennis"));
        balBox.set(new Pencil()); // COMPILING ERROR
    }
}

Type parameter naming conventions

By convention, type parameter names are single, uppercase letters.

 

The most commonly used type parameter names are:

 

  • E - Element 
  • K - Key
  • N - Number
  • T - Type
  • V - Value

Type parameter naming conventions

By convention, type parameter names are single, uppercase letters.

 

The most commonly used type parameter names are:

 

  • E - Element 
  • K - Key
  • N - Number
  • T - Type
  • V - Value

EXERCISE

Turn the NodeContainer class type safe using generics.

The Collection Interface

A Collection represents a group of objects known as its elements.

It contains methods that perform basic operations.

public interface Collection<E> {

    int size(); 
    boolean isEmpty(); 
    boolean contains(Object element); 
    boolean add(E element); 
    boolean remove(Object element); 
    Iterator<E> iterator();
    (...)
}

The Collection Interface

It also contains methods that operate on entire collections. We call these bulk operations.

public interface Collection<E> {

    (...)
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c); 
    void clear();
}

The List Interface

A List is an ordered Collection.

Lists may contain duplicate elements.

 

When we implemented the two dynamic containers on the last exercise, we were actually implementing very simple versions of two types of lists: LinkedList and ArrayList.

 

LinkedList - Sequencial implementation of the List interface. All of the operations performed will traverse the whole list from the beginning or the end (depending on how close the specified index is).

 

ArrayList - Resizable-array implementation of the List interface. 

The QUEUE Interface

A Queue is a collection for holding elements prior to processing. 

Queues inherit the methods of Collection, and add other insertion/extraction/inspection methods.

 

Typically, a queue orders elements in a FIFO manner.

 

Specific implementations include the PriorityQueue, LinkedList and BlockingQueue.

Implementations of queue interface

LinkedList - As seen before, LinkedList implements both List and Queue Interfaces.

Adding/accessing/removing elements can be performed from both ends.

public static void main(String[] args) {
    Queue<Integer> integerQueue2 = new LinkedList<>();

    integerQueue2.add(10);
    integerQueue2.add(5);
    integerQueue2.add(12);

    Iterator it2 = integerQueue2.iterator();

    while (it2.hasNext() != false) {
    	System.out.print(it2.next() + " ");
    }
}

Implementations of queue interface

PriorityQueue - Elements of the priority queue are ordered according to their natural ordering.

Elements inserted into a PriorityQueue must implement the Comparable interface.

public static void main(String[] args) {
    Queue<Integer> integerQueue1 = new PriorityQueue<>();

    integerQueue1.add(10);
    integerQueue1.add(5);
    integerQueue1.add(12);


    while (!integerQueue1.isEmpty()) {
    	System.out.print(integerQueue1.remove() + " ");
    }
}

EXERCISE

The To-do list

THE SET INTERFACE

A Set is a collection that contains only the methods inherited from Collection, and it adds the restriction that duplicate elements are not allowed.

 

Specific implementations include HashSet, TreeSet, etc. Different implementations may use different storage methods, allow for navigation or sorting...

 

The ground rule for any of these implementations is that any two elements e1 and e2 may not be equal: e1.equals(e2).

Set Implementations

TreeSet - a navigable set where the elements are ordered, according to their natural order or a comparator. Elements must implement Comparable interface.

 

HashSet - implements the Set interface, backed by a hash table. A hashcode is created and used to store the element in the table.

equals()

equals() - The default implementation of this method in the Object class states that equality is the same as object identity. This method may be overriden to change an object's definition of equality.

This method should be reflexive, symmetrical, transitive and consistent.

public class Person {
    
    private String name;
    private int age;

    public Person(String name, int age) {...}

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        
        Person person = (Person) o;
        return age == person.age && name.equals(person.name);
    }
}

HAshcode()

hashcode() - Objects that are equal to each other must return the same hashcode. However, the contrary isn't true: different objects might return the same hashcode. The value of hashcode may only change if a property that is used by equals() changes.

public class Person {
    
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

THE EQUALS-HASHCODE CONTRACT

If you override the equals() method, you should also override the hashcode method.

EXERCISE

The Dupe Finder

THE MAP INTERFACE

A Map is an object that maps keys to values.

A Map can't contain duplicate keys, and each key can map to at most one value.

 

Some Map concrete implementations include HashMap, TreeMap, and LinkedHashMap.

 

HashMap - Based on an hash table. It makes no guarantees regarding the order of the map. 

EXERCISE

The Word Histogram

Snake game

Exercise

The Snake Game

Advanced Java

By Soraia Veríssimo

Advanced Java

  • 1,771