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:
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:
- Ask for user input
- Get the list of files present in a directory and write the name of those files in a new text file
- Check if a particular file is present in a particular directory
- 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
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
EXERCISE
The Word Histogram
Snake game
Exercise
The Snake Game
Advanced Java
By Soraia Veríssimo
Advanced Java
- 1,986