+ - 0:00:00
Notes for current slide
Notes for next slide

Advanced Programming

223MI, 558EC

Eric Medvet

A.Y. 2019/2020

1 / 667

Lecturer

Eric Medvet

Research interests:

  • Evolutionary Computation
  • Machine Learning applications

Labs:

2 / 667

Materials

Teacher slides:

  • available here
  • might be updated during the course

Intended usage:

  • slides should contain every concept that has to be taught/learnt
  • but, slides are designed for consumption during a lecture, they might be suboptimal for self-consumption \rightarrow take notes!
3 / 667

Exam

Two options:

  1. project + written test + home assignments
    • grade: weighted average with weights 60%, 30%, 10%
  2. project + written test + oral exam
    • grade: weighted average with weights 40%, 30%, 30%

Failed if at least one part is graded <6/10.

4 / 667

Home assignments

Home assignments:

  • short exercise assigned during the course
  • will start in the classroom, under teacher's supervision
  • student's output to be submitted within the deadline, one for each assignment
  • grade, for each assignment:
    • 0/10: not submitted or missed deadline
    • up to 5/10: submitted, but not working
    • up to 8/10: submitted and almost working
    • 10/10: submitted and fully working
  • grades communicated during the course

More details at first assignment.

5 / 667

List of exercises and home assignements

Just for reference:

  1. Anagrams (also assignment)
  2. Equivalence (also assignment)
  3. File array
  4. Compressed file array (also assignment)
  5. Word counter server
6 / 667

Project

Project:

  • assigned at the end of the course
  • student's output: software, tests, brief document
  • to be submitted within the exam date
  • grade:
    • 0/10: not submitted or missed deadline
    • 5/10 to 10/10: submitted, depending on
    • quality of code
    • software structure
    • document (mainly clarity)
    • test coverage
    • degree of working

More details at project assignment.

7 / 667

Written test and oral exam

Written test:

  • a few questions with short open answers and short programming exercises

Oral exam:

  • questions and short programming exercises
8 / 667

Course content

In brief:

  1. Java as object-oriented programming language
  2. Tools and methods for programming
  3. Distributed programming

See the syllabus!

9 / 667

Exercises

There we'll be several exercises:

  • approx. 20h on 72h
  • sofware design and implementation exercises
  • bring your own device
  • in classroom
    • with the teacher actively investigating about your progresses

Practice is fundamental!

Computational thinking is an outcome of the coding practice. See Nardelli, Enrico. "Do we really need computational thinking?." Communications of the ACM 62.2 (2019): 32-35.

10 / 667

You?

  • How many of you already know object-oriented programming?
  • How many of you already know Java?
11 / 667

Java: what and why?

12 / 667

Why Java?

Practical motivations:

  • write once, run anywhere
  • large community support, solid tools, many APIs
  • free

Teaching-related motivations:

  • favors abstraction
  • realizes many key concepts of programming and languages
13 / 667

Language, platform

Java is both:

  • a programming language
  • a software platform

We will focus on:

  • (mainly) the programming language
  • (secondarily) the Java 2 Platform, Standard Edition (J2SE)
14 / 667

Java platform

Mailnly composed of:

  • an execution engine
  • a compiler
  • a set of specifications
  • a set of libraries, with Application Program Interfaces (APIs)

Platforms (also called editions) differ mainly in the libraries. Most common editions:

  • Java 2 Platform, Standard Edition (J2SE): for general-purpose, desktop applications
  • Java 2 Platform, Enterprise Edition (J2EE): J2SE + APIs/specifications for multi-tier client-server enterprise applications
15 / 667

Main tools

What is needed for developing in Java?

  • Java Development Kit (JDK), absolutely necessary
  • API documentation (briefly javadoc)
  • Integrated Development Environment (IDE)
    • many options
    • when used proficiently, makes development more efficient and effective
16 / 667

JDKs

One JDK per (vendor, platform, version, os+architecture).

A few options:

17 / 667

JDK versions

We are currently at version 13. History from Wikipedia:

Java Version history

18 / 667

Java IDEs

Many excellent options.

Classic, desktop-based:

Cloud-based:

19 / 667

IDE: your best friend

Apache NetBeans screenshot Jetbrains IntelliJ IDEA screenshot Eclipse IDE screenshot

Pros:

  • makes typing much faster
  • greatly helps following conventions
  • makes software lifecycle operations faster
  • helps finding errors

Cons:

  • steep learning curve
  • may hide some programming/development concepts
20 / 667

Basic operations

Compiling and executing

21 / 667

.java vs. .class files

  • The source code is in one or more .java files
  • The executable code is in one or more .class files

Compilation: obtaining a .class from .java

javac Greeter.java
22 / 667

Class-file

.java contains exactly one class definition (common case)

public class Greeter {
//...
}

Or it contains more than one class definitions, at most one non-inner has the public modifier (we will see):

public class Greeter {
//...
}
class Helper {
//...
}
public class Greeter {
//...
public class InnerHelper {
//...
}
}

Compilation results always in one .class for each class definition!

23 / 667

Execution

An application is a .class compiled from a .java that has a "special function" main

public class Greeter {
public static void main(String[] args) {
\\...
}
}

An application can be executed:

java Greeter

Or java Greeter.class, or in other ways

24 / 667

Class and the virtual machine

  • A .class file contains executable code in a binary format (bytecode)
  • The bytecode is interpreted by a program (java) that simulates a machine: the Java Virtual Machine (JVM)

JVM:

  • like a real machine, but simulated: it has an instruction set and manages memory
  • agnostic with respect to the physical machine
  • does not know Java language, knows bytecode
25 / 667

JVM language

An example of the JVM specification, the instruction iadd:

Operand Stack

..., value1, value2 \rightarrow ..., result

Description

Both value1 and value2 must be of type int. The values are popped from the operand stack. The int result is value1 + value2. The result is pushed onto the operand stack.

The result is the 32 low-order bits of the true mathematical result in a sufficiently wide two's-complement format, represented as a value of type int. If overflow occurs, then the sign of the result may not be the same as the sign of the mathematical sum of the two values.

26 / 667

Portability

A .class executable can be executed on any machine for which a program exists that simulates the JVM (i.e., a java).

"One" .java compiled to "one" .class, can be executed:

  • on Linux x64, with the java program for Linux x64
  • on Solaris SPARC x64, with the java program for Solaris SPARC x64
  • Mac OS X, Windows, Android, ...

\rightarrow Write once, run anywhere

27 / 667

Who executes what

Note:

  • the OS executes java
  • java executes the bytecode
    • simulates the JVM which executes the bytecode
  • the OS cannot execute the bytecode!
28 / 667

Where is java?

java is part of the Java Runtime Environment (JRE), which includes also necessary libraries. The JRE is part of the JDK.

The JDK contains (mainly):

  • the compiler javac
  • the JRE (including java)
  • many compiled classes (.class files)
  • other development tools

It does not contain:

  • the classes source code
  • the documentation
29 / 667

Inside the JDK

eric@cpu:~$ ls /usr/lib/jvm/java-11-openjdk-amd64 -la
totale 48
drwxr-xr-x 2 root root 4096 feb 6 12:08 bin
drwxr-xr-x 4 root root 4096 feb 6 12:08 conf
drwxr-xr-x 3 root root 4096 feb 6 12:08 include
drwxr-xr-x 6 root root 4096 feb 6 12:08 lib
...
eric@cpu:~$ ls /usr/lib/jvm/java-11-openjdk-amd64/bin/ -la
totale 632
-rwxr-xr-x 1 root root 14576 gen 15 16:14 jar
-rwxr-xr-x 1 root root 14576 gen 15 16:14 jarsigner
-rwxr-xr-x 1 root root 14560 gen 15 16:14 java
-rwxr-xr-x 1 root root 14608 gen 15 16:14 javac
-rwxr-xr-x 1 root root 14608 gen 15 16:14 javadoc
...

Usually the JDK can be installed on the OS; but it can be simply uncompressed somewhere.

30 / 667

Documentation and source code

Do you need them?

  • documentation: yes, in practice
  • source code: no, but it can be useful for understanding

How to access the documentation?

How to access the source code?

  • download it
  • download it in the IDE, access through the IDE (ctrl+click)
31 / 667

Consuming the documentation

Javadoc, online

Javadoc, online

32 / 667

Java: interpreted or compiled?

Both!

  • .java to .class through compilation
  • .class is then interpreted by java for being executed on OS

Actually, things are much more complex:

  • on the fly optimization
  • recompilation
  • compilation to native code
  • ...
33 / 667

Intepretation and efficiency

Is intepretation efficient?

  • unrelevant question

Relevant question: is my software fast enough?

  • it depends: measure!

If not?

  1. profile
  2. find opportunities for improvement
  3. improve

(that is: write good code!)

The problem is rarely in interpretation!

34 / 667

Beyond speed of execution

When choosing a language/platform, speed of execution is just one factor.

Other factors:

  • code maintainability
  • documentation/support availability
  • quality of development tools
  • libraries availability
  • ...
  • previous knowledge
  • ...

In general, consider all costs and assess options.

35 / 667

Basic concepts

36 / 667

Hello goal!

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Goal: deep understanding of this code

  • not just what it "does"
  • but the role of every part of the code

Take code highlighting with care!

37 / 667

Objects

"Things" (entities that can be manipulated in the code) are objects

  • objects exist
  • but do not have a name

The code manipulates objects through references

  • references have a symbolic name, called identifier

Both can be created.

object ≠ reference

38 / 667

Creating objects and references

String s;
  • create a reference to object of type String with identifier s (briefly: reference s)
    • a reference has always a name!
  • no object is created
s
String s = new String("Content");
  • create reference s of type String
  • create object of type String and init it with "Content"
  • make s reference the new object
sString
39 / 667

Many references

String s1 = new String("Content");
String s2 = s1;
  • create reference s1 and make it reference the new objects
  • create reference s2 and make it reference the object referenced by s1
s1s2String

Objects+references diagram: where? when?

  • for now: in the memory of the JVM, conceptually.
  • at runtime
40 / 667

No references

new String("Content1");
new String("Content2");
  • create object of type String and init it with "Content1"
  • create object of type String and init it with "Content2"
StringString

The two objects are not referenced: they cannot be manipulated!

41 / 667

Changing referenced object

String s1 = new String("Content1");
String s2 = new String("Content2");
s1 = s2;
  • create reference s1, create new String inited with "Content1", and make s1 reference it
  • create reference s2, create new String inited with "Content2", and make s2 reference it
  • make s1 reference the object referenced by s2

After 2nd line:

s1s2StringString

After 3rd line:

s1s2StringString
42 / 667

Objects+references diagram

String dog1 = new String("Simba");
String dog2 = new String("Gass");
new String("Chip");
String squirrel2 = new String("Chop");
dog1 = dog2;
dog2 = dog1;
squirrel2 = squirrel2;

Draw the diagram

  • after the 3rd line
  • after the last line
43 / 667

Manipulating objects

Key questions:

  • which operations can be applied on an object?
  • how to specify them in the code?
  • can operations have parameters?
44 / 667

Manipulating objects

Answers (in brief):

  • which operations can be applied on an object?
    • every object is an instance of a type
    • a type defines operations applicable to instances
  • how to specify them in the code?
    • with the dot notation: refName.opName()
  • can operations have input and output parameters?
    • input parameters can be specified between round brackets: refName.opName(otherRefName1, otherRefName2)
    • output can be referenced: retRefName = refName.opName()

We are just scratching the surface; we'll go deeper ahead.

45 / 667

Classes

A class is a type

  • every object is an instance of a class
  • (in a Java sofware) there are several classes
    • some are part of the Java language
    • some are part of APIs
  • every application includes at least one class, defined by the developer
46 / 667

Classes

class ≠ object

Some expressions that are clues of misunderstanding:

  • "I wrote the code of an object"
  • "X references a class"

Some correct expressions that hide complexity:

  • "instantiating a X" (where X is a class)
    • means "creating an object of class X"
47 / 667

Primitive types

Some types are not classes, but primitive types:

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

We'll discuss them later.

48 / 667

Using classes

49 / 667

Methods

An operation applicable on a object is a method of the corresponding class.

Every method has a signature, consisting of:

  • name
  • sequence of types of input parameters (name not relevant)
  • type of output parameter (aka return type)
    • possibly void, if there is no output

Examples (class String):

  • char charAt(int index)
  • int indexOf​(String str, int fromIndex)
  • String replace​(CharSequence target, CharSequence replacement)
  • void getChars​(int srcBegin, int srcEnd, char[] dst, int dstBegin)
50 / 667

Method invocation

String s = new String("Hello!");
int l = s.length();
  • execute (who?) the operation defined by the method lenght() on the object referenced by s
    • briefly: invoke length() on s
  • create reference l of type int
  • make l reference the object created upon the execution of the operation
slStringint
51 / 667

Can a method be invoked?

String s = new String("Hello!");
int l = s.length();

The compiler (javac) verifies (also) that:

  • the type of s has a method with name length
    • briefly: s has a method length
  • the method is used consistently with the signature
    • input and output types are correct

This check:

  • is done at compile time (by the compiler)
  • is much harder than it appears: we'll see
  • is hugely useful for avoiding errors and designing good sofware

Colloquially, Java is said to be strongly typed.

52 / 667

Method overloading

A class can have many methods:

  • with the same name
  • but different input parameters

From another point of view: at most one method with the same (name, input parameters), regardless of the output type.

String s = new String("Hello!");
PrintStream ps = new PrintStream(/*...*/);
ps.println(s);
Type Method Description
void println() Terminates the current line by writing the line separator string.
void println​(boolean x) Prints a boolean and then terminate the line.
void println​(char x) Prints a character and then terminate the line.
void println​(char[] x) Prints an array of characters and then terminate the line.
void println​(double x) Prints a double and then terminate the line.
void println​(float x) Prints a float and then terminate the line.
void println​(int x) Prints an integer and then terminate the line.
void println​(long x) Prints a long and then terminate the line.
void println​(String x) Prints a String and then terminate the line.
53 / 667

How many methods?

A lot of classes, a lot of methods!

You have to:

  • use the documentation (briefly: javadoc)
  • and/or proficiently use the IDE
    • autocompletion! (Ctrl+space)

Often, the signature alone is sufficient for understanding what the method does (e.g., int length() of String).

This is because who wrote the code for String correcly chose the signature: in particular the name.

There are naming conventions!

54 / 667

Naming conventions

Extremely important!

  • code is not just for machines, it's for humans
  • with code, a human tells another human about a procedure

Many of them: some just for Java, some broader.

  • we'll see part of them, gradually

The degree to which naming conventions are followed concurs in determining the quality of the code.

There are many sources online: e.g., JavaPoint

55 / 667

Classes and methods

Class:

  • a noun, a name of an actor: e.g., Dog, Sorter, Tree
  • representative of the entity the class represents
  • upper camel case (aka dromedary case): HttpServerResponse

Method:

  • a verb, a name of an action: e.g., bark, sort, countNodes
    • with acceptable exceptions: e.g., size, nodes
  • representative of the operation the method performs
  • lower camel case: removeLastChar
56 / 667

References

Reference:

  • consistent with the corresponding type
  • representative of the specific use of the referenced object:
    • not every String has to be referenced by s
    • specific! e.g., numOfElements is better than n
  • lower camel case

God gave us IDEs, IDEs give us autocompletion! Don't spare on chars.

57 / 667

Associative dot operator

String s = new String(" shout!");
s = s.trim().toUpperCase();
  • invoke trim() on object referenced by s
  • invoke toUpperCase() on the object resulting from trim() invocation
  • make s reference the object resulting from toUpperCase() invocation
Type Method Description
String toUpperCase() Converts all of the characters in this String to upper case using the rules of the default locale.
String trim() Returns a string whose value is this string, with all leading and trailing space removed, where space is defined as any character whose codepoint is less than or equal to 'U+0020' (the space character).
58 / 667

Objects and references diagram

String s = new String(" shout!");
s = s.trim().toUpperCase();

After 1st line:

sString

After 2nd line:

sStringStringString
59 / 667

Constructor

Every class T has (at least) one special method called constructor that, when invoked:

  • results in the creation of a new object of class T
  • inits the new object

Special syntax for invocation using the new keyword:

Greeter greeter = new Greeter();

There are a few exceptions to this syntax:

  • String s = "hi!"; same of String s = new String("hi!");
60 / 667

Initialization

What happens with initialization depends on the constructor.

Class Date: The class Date represents a specific instant in time, with millisecond precision.

Modifier Constructor Description
Date() Allocates a Date object and initializes it so that it represents the time at which it was allocated, measured to the nearest millisecond.

Probably two different initialization outcomes:

Date now = new Date();
// some code doing long things
Date laterThanNow = new Date();
61 / 667

Multiple constructors

A class C can have more than one constructors:

  • at most one with the same input parameters
  • (the name and the return type are always the same)
    • name: the very same name of the class (e.g., Date \rightarrow Date())
    • return type: C (e.g., Date \rightarrow Date)

Class String:

Modifier Constructor Description
String() Initializes a newly created String object so that it represents an empty character sequence.
String​(char[] value) Allocates a new String so that it represents the sequence of characters currently contained in the character array argument.
String​(String original) Initializes a newly created String object so that it represents the same sequence of characters as the argument; in other words, the newly created string is a copy of the argument string.
62 / 667

Other example: Socket

Class Socket: This class implements client sockets (also called just "sockets"). A socket is an endpoint for communication between two machines.

Modifier Constructor Description
Socket() Creates an unconnected Socket.
Socket​(String host, int port) Creates a stream socket and connects it to the specified port number on the named host.
Socket​(String host, int port, boolean stream) Deprecated. Use DatagramSocket instead for UDP transport.
Socket​(String host, int port, InetAddress localAddr, int localPort) Creates a socket and connects it to the specified remote host on the specified remote port.
Socket​(InetAddress address, int port) Creates a stream socket and connects it to the specified port number at the specified IP address.
Socket​(InetAddress host, int port, boolean stream) Deprecated. Use DatagramSocket instead for UDP transport.
Socket​(InetAddress address, int port, InetAddress localAddr, int localPort) Creates a socket and connects it to the specified remote address on the specified remote port.
Socket​(Proxy proxy) Creates an unconnected socket, specifying the type of proxy, if any, that should be used regardless of any other settings.
protected Socket​(SocketImpl impl) Creates an unconnected Socket with a user-specified SocketImpl.
63 / 667

Information hiding

The user of a class (that is, a developer possibly different than the one who developed the class):

  • can use the class and operates on it
  • does not know how it is coded

Class Date:

Type Method Description
boolean after​(Date when) Tests if this date is after the specified date.
boolean before​(Date when) Tests if this date is before the specified date.
long getTime() Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object.
String toString() Converts this Date object to a String of the form:

We can do non-trivial manipulation of dates (meant as entities) through the class Date without knowing its code!

64 / 667

Modularity

The user of a class knows:

  • which operations exist, how to use them, what they do

He/she does not know:

  • what's inside the object
  • how exactly an operation works

The state of the object and the code of the class might change, but the user is not required to be notified of changes!

\rightarrow Modularity: everyone takes care of only some part of the sofware!

65 / 667

Coding classes

66 / 667

Problem: complex numbers

Goal: manipulating complex numbers

By examples (i.e., point of view of the user):

Complex c1 = new Complex(7.46, -3.4567);
Complex c2 = new Complex(0.1, 9.81);
Complex c3 = c1.add(c2);
// same for subtract(), multiply(), divide()
double norm = c2.getNorm();
double angle = c2.getAngle();
String s = c2.toString();
double real = c2.getReal();
double imaginary = c2.getImaginary();
67 / 667

"Solution": Complex.java

public class Complex {
private double real;
private double imaginary;
public Complex(double real, double imaginary) {
this.real = real;
this.imaginary = imaginary;
}
public double getReal() {
return real;
}
public double getImaginary() { /* ... */ }
public Complex add(Complex other) {
return new Complex(
real + other.real,
imaginary + other.imaginary
);
}
/* other methods */
}

IDE: code generation, auto formatting.

68 / 667

What's behind the solution?

Domain knowledge:

  • what is a complex number? what are its composing parts? (entities)
  • what are the required operations?

Knowledge of the programming language:

  • how to represent the entities in Java?
  • how to perform the operations in Java?

You need both!

  • you can rely on external help, but you still need to know what you want to do
    • Google/stackoverflow: how to do X in Java?
69 / 667

Objects+references diagram

Complex c1 = new Complex(7.46, -3.4567);
Complex c2 = new Complex(0.1, 9.81);
c1Complexdoubledoublec2Complexdoubledouble
70 / 667

Objects+references diagram

Complex c1 = new Complex(7.46, -3.4567);
Complex c2 = new Complex(0.1, 9.81);
Complex c3 = c1.add(c2);
double norm = c2.getNorm();
c1Complexdoubledoublec2Complexdoubledoublec3Complexdoubledoublenormdouble
71 / 667

Field

A field is an object contained in an object:

public class Complex {
private double real;
private double imaginary;
/* ... */
}
Complex c1 = new Complex(1.1, 2.2);

The fields of an object constitute its state.

They are referenced, there is an identifier:

c1Complexrealimaginarydoubledouble
72 / 667

Accessing fields

Can be manipuleted using dot notation, like methods:

public Complex add(Complex other) {
return new Complex(
real + other.real,
imaginary + other.imaginary
);
}
73 / 667

Access modifiers: private, public

Keywords that specify where an identifier is visible, i.e., where it is lecit to use it for accessing the field or method or other, we'll see.

  • private: visible only within the code of its class
  • public: visible everywhere

For brevity, we avoid discussing about syntax: but the Java language specification describes exactly where/how every keyword can be used.

74 / 667

private fields

File Complex.java:

public class Complex {
private double real;
/* here real can be used */
}
/* here real can not be used */

File ComplexCalculator.java:

public class ComplexCalculator {
public Complex add(Complex c1, Complex c2) {
/* here real can not be used */
}
}
75 / 667

private or public: how to choose?

Ideally, it depends of the nature of the entity that the class represents (domain knowledge!)

But (general rule of thumb):

  • fields should be private
    • because they represent the state of the object, and we want to avoid that the state is manipulated from "outside"
  • methods should be public
    • because they are operations that can be performed on the object
    • unless we use a method for describing a "partial" operation that is reused frequently by other public operations: in this case, the method should be private
76 / 667

this identifier

It is the identifier of the reference that references the object on which the method, where this is, is being executed.

public class Complex {
public Complex add(Complex other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
);
}
}

this is a keyword.

77 / 667

Implicit this

Can be omitted:

public class Complex {
public Complex add(Complex other) {
return new Complex(
real + other.real,
imaginary + other.imaginary
);
}
}

real and this.real reference the same object.

78 / 667

this for disambiguation

Sometimes it is necessary for disambiguation:

public class Complex {
private double real;
private double imaginary;
public Complex(double real, double imaginary) {
this.real = real;
this.imaginary = imaginary;
}
}

Before the line, real and this.real do not reference the same object.

This is the typical structure of a constructor: name of input parameters match the name of fields. As usual, the IDE is your friend and can write this code automaticaly!

79 / 667

Packages

80 / 667

Exported identifiers

A class (i.e., a .class file) exports some identifiers of:

  • class or classes, but one per .class file
  • methods
  • fields

Access modifier can be omitted: default access modifier:

  • private: not visible (~ not exported)
  • public: visible
  • default: visible
81 / 667

Package

A package is a set of classes with a name

  • we'll see how to define it

A package exports the identifiers of its classes:

  • private: not visible (~ not exported)
  • public: visible
  • default: visible only within the package

There are no many reasons for using the default access modifier; please always specify one between public and private (or protected, we'll see...)

82 / 667

Package name

A sequence of letters separated by dots, e.g.:

  • it.units.erallab
  • java.util
  • java.util.logging

There is no formal hierarchy, nor relationship:

  • java.util.logging is not a "subpackage" of java.util
  • they are just different packages
83 / 667

Why and how?

Why package names?

  • to avoid name clash that may be likely for classes representing common concepts
    • e.g., User, Point

How to name packages? (naming conventions)

  • usually, (lowercase) reversed institution/company name + product + internal product organization, e.g., it.units.erallab.hmsrobots.objects.immutable
    • it.units.erallab: institution
    • hmsrobots: product
    • objects.immutable: internal organization

Beyond names:

  • packages impact on file organization (we'll see, in directories)
84 / 667

Packages and modules

Since Java 9, a new, higher level abstraction for code entities organization has been introduced: Java Platform Module System (JPMS), briefly modules.

  • they are interesting and useful, but...
  • we'll complitely ignore
  • you can build rather complex software without knowing them
85 / 667

API documentation

86 / 667

Fully qualified name

Every class is identified unequivocally by its Fully Qualified Name (FQN):

FQN = package name + class name

  • java.net.Socket
  • it.units.erallab.hmsrobots.objects.immutable.Component

Everywhere in the code, every* class can be identified by its FQN.

double r = 10;
it.units.shapes.Circle circle = new it.units.shapes.Circle(r);
double area = circle.area();

*: provided it is available; we'll see

87 / 667

The import keyword

Writing always the FQN can make the source code verbose, yet it is necessary, otherwise the compiler does not know which class we are referring to.

Solution (shorthand): import

import it.units.shapes.Circle;
public class ShapeCompound {
private Circle circle;
/* ... */
}

"import package.X" means "dear compiler, whenever I write X in this code, I mean package.X"

  • the compiler internally always uses the FQN
88 / 667

Star import

It might be useful to "import" all the classes in a package:

  • it can be done with import it.units.shapes.*;

Coding conventions suggest not to do it:

  • risk: one might import an unneeded class with the same simple name of another class of the same package (e.g., java.awt.Event and it.units.timetable.Event)
  • lack of motivation
    • IDEs add imports for you automatically, and can remove them partially automatically (autocompletion!)
    • so don't be lazy and messy
89 / 667

import for the developer

import is an optimization for the developer:

  • recall: code is not just for the machine

import does not import code, classes, objects...

90 / 667

Point of view of the compiler

When the compiler processes the source code (.java) and finds a class identifier C:

  • it needs to know its methods and fields (name, signature, modifiers)
  • to be able to check if their usage is legit

If without FQN, the compiler looks for C definition:

  • in the source code (.java)
  • in the same package (directory)
  • in the packages imported with star import
91 / 667

java.lang package

All classes of the java.lang packages are available by default:

  • import java.lang.* is implicitly present in every source code
92 / 667

Syntax of FQNs

Package and class name cannot be identified just by looking at the FQN:

E.g., it.units.UglySw.Point:

  • can be the name of a package
  • or can be a FQN where:
    • Point is a class
    • or, UglySw.Point and UglySw are classes (we'll see)
    • or, units.UglySw.Point, units.UglySw, and units are classes, but the first is disrespectful of naming conventions!

Common case and convention: package name are all lowercase!

93 / 667

static field

A field of a class C can be defined with the static non-access modifier:

  • the corresponding object is unique, that is, the same for every instance of C
  • it always exists, even if no instances of C exist
    • if instantiated

From another point of view:

  • the reference to a static field is shared (i.e., the same) among the zero or more instances of the class
94 / 667

static field: diagram

public class Greeter {
public static String msg;
private String name;
/* ... */
}
Greeter g1 = new Greeter();
Greeter g2 = new Greeter();
g1GreetermsgnameStringStringg2GreetermsgnameStringString
95 / 667

static method

Also a method of a class C can be defined with static:

  • when invoked, it is not applied on the instance of C
  • can be invoked even if no instances of C exist, with a special syntax

The method code cannot access field or use other methods of C that are not static:

  • they might not exist!
  • the compiler performs the check (at compile time)
96 / 667

static method: syntax

public class Greeter {
private static String msg;
private String name;
/* ... */
public static String sayMessage() {
return msg;
}
}
String msg = Greeter.sayMessage(); /* OK! */
Greeter greeter = new Greeter();
greeter.sayMessage(); /* Syntax is ok, but avoid this form */
  • greeter.sayMessage() is bad because it suggests that the instance greeter is somehow involved in this operation, whereas it is indeed not involved!
  • only the "class" Greeter is involved
97 / 667

When to use static for methods?

Conceptually, a method should be static if it represents an operation that does not involve an entity represented by an instance of the class:

public class Dog {
public static Dog getCutest(Dog dog1, Dog dog2) { /* ... */ }
public static Dog fromString(String name) { /* ... */ }
}

static should be used also for private method, when condition above is met, even if they are not visible from outside (but other co-developers see it!)

98 / 667

Hello goal!

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Goal: deep understanding of this code

  • not just what it "does"
  • but the role of every part of the code
    • keywords
    • names
99 / 667

main signature

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
  • public \rightarrow main has to be invoked "directly" by the JVM upon execution (java Greeter): it has to be accessible
  • static \rightarrow invokable without having an already existing instance of Greeter
  • void \rightarrow does not return anything

public static void main(String[]) is the signature required by Java if you want to use the method as an execution entry point!

  • only the name of the input parameter can be modified
100 / 667

What is System.out?

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
  • println is a method, since it is invoked ()
  • System.out might be, in principle:
    1. the FQN of a class (and hence println is static in the class out)
    2. a field of a class (and hence out is static in the class System)

There is no package System (and out would be a class name out of conventions), hence 2 holds: System is a class

101 / 667

"Where" is System?

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Where is System?

  • more precisely, how can the compiler check that we correctly use out field of System?

There is no import, hence it can be:

  1. there a System.class in the same directory of this .class, hence we wrote a System.java
  2. System is in the java.lang package, that is always imported by default

2 holds: java.lang.System is the FQN of System

102 / 667

"What" is out?

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Look at System class documentation (fields):

Modifier and type Field Description
static PrintStream err The "standard" error output stream.
static InputStream in The "standard" input stream.
static PrintStream out The "standard" output stream.

\rightarrow out is a field of type PrintStream that represents the standard output.

What's the standard output? "Typically this stream corresponds to display output or another output destination specified by the host environment or user."

103 / 667

What is println()? How is it used?

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Look at PrintStream class documentation (methods):

Type Field Description
void println​(int x) Prints an integer and then terminate the line.
void println​(long x) Prints a long and then terminate the line.
void println​(Object x) Prints an Object and then terminate the line.
void println​(String x) Prints a String and then terminate the line.

"Hello World!" is a string literal and corresponds to new String("Hello World!")

  • hence println​(String x) is the used method
104 / 667

Involved classes

public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

"Involved" classes:

  • defined here: Greeter
  • defined elsewhere, used here: System, String, PrintStream
    • System, String in java.lang; PrintStream in java.io
105 / 667

No import

No need to import PrintStream:

  • import does not "load code"
  • import says to the compiler that we will use a simple name for FQN in the source code
    • but we do not use PrintStream in the source code
    • there is an import for PrintStream in System.java (or FQN)
import java.io.PrintStream;
/* ... */
public class System {
public static PrintStream out;
/* ... */
}
106 / 667

Write your first class

(and some other key concept)

107 / 667

Goal

Write an application that, given a word ww and a number nn, gives nn anagrams of ww.

108 / 667

Natural language and specification

"Write an application that, given a word ww and a number nn, gives nn anagrams of ww."

Natural language is ambiguous: this description leaves a lot of choices (and hence responsability) to the designer/developer:

  • "an application": for which platform? are there technological constraints? (tech)
  • "given": how? command line? file? standard input? (tech)
  • "a word ww": what is a word? (domain)
  • "a number nn": which kind of number? natural or real? (domain)
  • "anagrams": what is an anagram? (domain)
  • "nn anagrams": what if there are no enough anagrams? which ones, if more than nn? (domain)

It's up to you to take these decisions!

109 / 667

More precise goal

In this particular case:

  • a Java application, i.e., a class with a main()
  • ww via standard input, nn via command line (customer)
  • a word is a non-empty sequence of word characters (regex [A-Za-z]+)
  • nn is natural
  • anagram:
    • in general "a word or phrase formed by rearranging the letters of a different word or phrase" (from Wikipedia)
    • here (customer): permutation of multisets with repetitions
  • show up to nn, whichever you prefer
110 / 667

Basic building blocks

for achieving the goal

111 / 667

Execution flow control

Usual constructs available:

  • if then else
  • while
  • for*
  • switch*
  • break
  • continue
  • return

I assume you know them!

*: with enhanced syntax that we will see later

112 / 667

Basic I/O

Where:

  • input from standard input (aka stdin, typically, "the keyboard")
  • output to standard output (aka stdout)

Of what:

  • String
  • primitive types

We'll see later how to do I/O of other things to/from other places

113 / 667

Output to standard output

Use System.out, static field out of class java.lang.System: it's of class java.io.PrintStream

Type Field Description
void println() Terminates the current line by writing the line separator string.
void println​(boolean x) Prints a boolean and then terminate the line.
void println​(char x) Prints a character and then terminate the line.
void println​(char[] x) Prints an array of characters and then terminate the line.
void println​(double x) Prints a double and then terminate the line.
void println​(float x) Prints a float and then terminate the line.
void println​(int x) Prints an integer and then terminate the line.
void println​(long x) Prints a long and then terminate the line.
void println​(Object x) Prints an Object and then terminate the line.
void println​(String x) Prints a String and then terminate the line.

The same for print(), that does not terminate the line.

And a very practical printf(), that we'll discuss later

114 / 667

Input from stdin with BufferedReader

BufferedReader reader = new BufferedReader(
new InputStreamReader(System.in)
);
/* ... */
String line = reader.readLine();
  • readLine() reads one line at once
    • Java takes care of using the proper line termination criterion (\n or \r\n) depending on the host OS how does it know?
  • directly reads only strings
  • requires adding throws Exception after the () in the signature of main
    • we ignore why, for now
115 / 667

Input from stdin with Scanner

Scanner scanner = new Scanner(System.in);
/* ... */
String s = scanner.next();
int n = scanner.nextInt();
double d = scanner.nextDouble();
  • next(), nextInt(), ... do three things:
    1. reads one line from the InputStream it's built on
    2. splits the line in tokens (sep = " ")
    3. converts and returns the first token of proper type
  • if the line "has" >1 tokens, they are consumed in subsequent calls of next*()
116 / 667

String to primitive type

Here, useful if you use BufferedReader for input.

String line = reader.readLine();
int n = Integer.parseInt(line);

Class Integer

Modif. and type Field Description
static int parseInt​(String s) Parses the string argument as a signed decimal integer.

Similar for other primitive types:

  • Float.parseFloat(String)
  • Double.parseDouble(String)
  • ...
117 / 667

Arrays

Array: fixed-length sequence of objects of the same type

  • each object is accessed with the operator [] applied to the reference to the array
  • 0-based indexing
String[] firstNames;
String[] lastNames = new String[3];
lastNames[1] = new String("Medvet");
  • Creates a reference firstNames to String[]; does not create the array; does not create the elements
  • Creates a reference lastNames to String[]; create an array of 3 elements; does not create the elements
  • Creates a String and makes lastNames[1] (2nd in the array) reference it
118 / 667

Diagram

String[] firstNames;
String[] lastNames = new String[3];
lastNames[1] = new String("Medvet");
firstNameslastNamesString[]012String
119 / 667

Conventions

Name of arrays (i.e., identfiers of references to arrays):

  • plural form of the corresponding reference
    • Person[] persons, Person[] employees, ...

Definition:

  • Person persons[] is the same of Person[] persons, but the latter is much better:
    • it makes evident that the type is an array, rather than the identifier
120 / 667

Array creation

String[] dogNames = {"Simba", "Gass"};
//same of new String[]{"Simba", "Gass"}

is the same of

String[] dogNames = new String[2];
dogNames[0] = "Simba"; //same of = new String("Simba");
dogNames[1] = "Gass";
121 / 667

Array lenght

The type array has a field length of type int that contains the array size:

  • never changes for the a given array
  • cannot be written
String[] dogNames = {"Simba", "Gass"};
System.out.println(dogNames.length); //prints 2
dogNames = new String[3];
System.out.println(dogNames.length); //prints 3

dogNames.length = 5; does not compile!

122 / 667

Iterating over array elements

"Traditional" for syntax:

String[] dogNames = {"Simba", "Gass"};
for (int i = 0; i<dogNames.length; i++) {
System.out.println("Dog " + i + " name is " + dogNames[i]);
}

Enhanced for (or for-each) syntax:

String[] dogNames = {"Simba", "Gass"};
for (String dogName : dogNames) {
System.out.println("A dog name is " + dogName);
}
  • the index is not available inside the loop
  • can be applied to other types too (we'll see)
123 / 667

Varargs

In a signature of a method, the last input parameter, if of type array, can be specified with the ... syntax instead of []:

public static double max(double... values) { /* 1 ... */}

From the inside, exactly the same of []:

public static double max(double[] values) { /* 2 ... */}

From the outside, i.e., where the method is invoked, ... enables invokation with variable number of parameters (of the same type):

double max = max(4, 3.14, -1.1); //values ≅ double[3]; OK for 1
max = max(); //values ≅ double[0]; OK for 1
max = max(new double[2]{1, 2}); //Ok!; OK for 1 and 2

Since Java 5.0. Mathematically speaking, varargs allows to define variadic functions.

124 / 667

About modifiers

public static double max(double... values) {
double max = values[0];
for (int i = 1; i<values.length; i++) {
max = (max > values[i]) ? max : values[i];
}
return max;
}

condition ? expr1 : expr2 is the ternary operator.

  • Of which class might be a method?
  • Why static?
  • What happens with max()?
125 / 667

Why only last input parameter?

// does NOT compile!
public static int intersectionSize(String... as, String... bs) {
/* ... */
}
intersectionSize("hello", "world", "cruel", "world");

What is as and what is bs?

  • undecidable!

Java designers could have allowed for some exceptional case that, under some conditions, are not misinterpretable:

  • method(ClassA..., ClassC)
  • method(ClassA..., ClassB...)

But they opted for clarity at the expense of expressiveness.

126 / 667

Command line arguments

Available as content of main only arguments:

public class ArgLister {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
}
eric@cpu:~$ java ArgLister Hello World
Hello
World

Possible limitations and syntax variations depending on the host OS

127 / 667

Diagram

public class ArgLister {
public static void main(String[] args) {
for (String arg : args) {
System.out.println("Arg: " + arg);
}
}
}

java prepares args before invoking main()

At the beginning of main() after java ArgLister Hello World What inside the 1st and 2nd iteration of the for loop?

argsString[]01StringString
128 / 667

Operating with Strings

Creation (String constructors):

  • empty: String s = new String();
  • specified: String s = new String("hi!");
    • the same of String s = "hi!";
  • same of another: String s = new String(otherString);
  • ...
129 / 667

String methods

Many!

A few examples from the javadoc of String

Modif. and type Field Description
int compareTo​(String anotherString) Compares two strings lexicographically.
int compareToIgnoreCase​(String str) Compares two strings lexicographically, ignoring case differences.
boolean endsWith​(String suffix) Tests if this string ends with the specified suffix.
int indexOf​(int ch) Returns the index within this string of the first occurrence of the specified character.
int indexOf​(int ch, int fromIndex) Returns the index within this string of the first occurrence of the specified character, starting the search at the specified index.
int indexOf​(String str) Returns the index within this string of the first occurrence of the specified substring.
int indexOf​(String str, int fromIndex) Returns the index within this string of the first occurrence of the specified substring, starting at the specified index.
int length() Returns the length of this string.
boolean matches​(String regex) Tells whether or not this string matches the given regular expression.
String replaceAll​(String regex, String replacement) Replaces each substring of this string that matches the given regular expression with the given replacement.
130 / 667

String.format()

Modif. and type Field Description
static String format​(String format, Object... args) Returns a formatted string using the specified format string and arguments.

See also Formatter class and printf() in PrintStream class; here the syntax.

String reference = String.format(
"FPR=%4.2f\tFNR=%4.2f%n",
fp / n,
fn / p
);
//results in FPR=0.11 FNR=0.10

We'll se why Object...

131 / 667

Strings are immutable

1st sentence of the 2nd para of String docs: "Strings are constant; their values cannot be changed after they are created."

Apparently the docs are self-contradictory:

Modif. and type Field Description
String concat​(String str) Concatenates the specified string to the end of this string.
String toUpperCase() Converts all of the characters in this String to upper case using the rules of the default locale.

Apparently!

132 / 667

String.concat()

133 / 667

Immutable Strings: diagram

String s1, s2;
s1 = "hamburger";
s2 = s1.substring(3, 7);
System.out.print("s1 = ");
System.out.println(s1);
s1 = s1.replace('a', 'b');
String[] names ={ "John", "Fitzgerald", "Kennedy"}
String firstInit, middleInit, lastInit;
firstInit = names[0].substring(0, 1);
middleInit = names[1].substring(0, 1);
lastInit = names[2].substring(0, 1);
firstInit.concat(middleInit).concat(lastInit);

Draw the diagram

  • after the line starting with String[]
  • after the last line
134 / 667

Concatenation

Besides concat(), + operator (alternative syntax):

String s1 = "today is ";
String s2 = s1.concat(" Tuesday").concat("!");

2nd line is the same of:

String s2 = s1 + " Tuesday" + "!";

+ is associative on the left:

  • "today is Tuesday" is obtained before "today is Tuesday!"
135 / 667

String + other type

If one of the operands of + is of type String then, at runtime:

  • a String representation of the operand is obtained
  • the concatenation of the two Strings is done
int age = 41;
String statement = "I'm " + age + " years old";

What are the String and the non-String operands?

For the case when the non-String operand is not a primitive type, we'll see the underlying mechanism in detail.

136 / 667

Primitive types

A few differences with respect to classes:

  • they are not created with new
  • they do not have methods (no constructors) and fields
    • no dot notation

They can be operands of (binary) operators.

int n = 512;
double d = 0d;
char a = 'a';
boolean flag = false;
flag = flag || true;
double k = 3 * d - Math.sqrt(4.11);

sqrt() modifiers?

137 / 667

Initialization of primitive types

If not explicitly initialized:

  • if field of an object, initialized to default value
    • 0 for numbers, false for boolean
  • if local variable, code does not compile
138 / 667

Initialization of arrays of primitive types

Consistently, for arrays:

  • elements of primitive type arrays are initialized to default value
int[] marks;
double[] ages = new double[3];
marksagesdouble[]012doubledoubledouble
139 / 667

Inline field initialization

Fields can be initialized inline:

public class Greeter {
private String greet = "Hello";
public void greet() {
System.out.println(greet + " World!");
}
}

Field initialization is executed before the first statement of any constructor.

140 / 667

Field initialization

public class Greeter {
private String greet = "Hello";
private String target;
private int n;
public void greet() {
System.out.println(greet + " " + target);
System.out.println(n);
}
}
greetStringtargetnint
141 / 667

= on primitive types

Assign operator = copies the content, instead of referencing the same object.

String s1 = "hello!";
String s2 = s1;
s1Strings2
double d1 = 3.14;
double d2 = d1;
d1doubled2double

Things are a bit more complex, we'll see...

142 / 667

null

A special "value" that can be referenced by a reference of any non-primitive type.

Fields:

  • non-primitive fields are implicitly set to null
  • recall: primitive fields are set to their default values

Local variables:

  • if not initialized, compilation error!
143 / 667

Referencing null

A reference referencing null is (approx.) not referencing anything.

String s1 = "hi";
String s2 = null;
String s3 = null;
s1Strings2s3
null is not of any particular type; we will omit gray part of the diagram
public class Greeter {
private String s1 = "hi";
private String s2 = null;
private String s3;
private int n;
}
s1Strings2s3nint
144 / 667

Dereferencing

String s1 = "hi";
String s2 = s1;
s1 = null;
boolean isNull = s1 == null;
s1Strings2isNullboolean
145 / 667

Ready for first exercise!

146 / 667

Onlide IDE repl.it

repl.it online IDE

  • register
  • enjoy compilation (javac) and execution (java)
    • note the run button
147 / 667

Code neatly!

Your code is your representation of a problem and its solution.

When someone looks at your code, she/he is entering your mind:

  • messy code \rightarrow messy representation \rightarrow messy mind \rightarrow messy you!
  • do you want to look messy?
    • to your collaborator, your boss, your employee
    • to your teacher
148 / 667

Code review

At least 4 sources of mess (increasingly harder to spot):

  • code formatting
  • naming
  • implementation of algorithm
  • algorithm

Code review \rightarrow the process of checking the code by looking at the source code:

  • routinely performed within big tech companies
  • there is a "IEEE Standard for Software Reviews and Audits" (IEEE 1028-2008)
  • not cheap!
149 / 667

Anagrams ~2h, 1st home assignement

Write an application that, given a word ww and a number nn, gives nn anagrams of ww. (Go to detailed specifications, also in next slide)

  • multiset of permutations of multiset (i.e., with repetitions)
  • with capitalized anagrams

Hints:

  • take inspiration from the Internet
    • but please make at least the effort of finding the proper search query
  • if you already master "advanced" Java features, try to ignore them, at least initially

If/when done:

  1. redo it on a full, desktop IDE
  2. profile your code
150 / 667

More precise goal

In this particular case:

  • a Java application, i.e., a class with a main()
  • ww via standard input, nn via command line (customer)
  • a word is a non-empty sequence of word characters (regex [A-Za-z]+)
  • nn is natural
  • anagram:
    • in general "a word or phrase formed by rearranging the letters of a different word or phrase" (from Wikipedia)
    • here (customer): permutation of multisets with repetitions
  • show up to nn, whichever you prefer
151 / 667

Inheritance and polymorhism

152 / 667

Printing a Date

import java.util.Date;
public class Greeter {
public static void main(String[] args) {
System.out.print("Hello World! Today is: ");
System.out.println(new Date());
}
}
eric@cpu:~$ java Greeter
Hello World! Today is: Mon Mar 16 10:36:13 UTC 2020

It works!

153 / 667

println and Date

PrintStream class:

Type Field Description
void println() Terminates the current line by writing the line separator string.
void println​(boolean x) Prints a boolean and then terminate the line.
void println​(char x) Prints a character and then terminate the line.
void println​(char[] x) Prints an array of characters and then terminate the line.
void println​(double x) Prints a double and then terminate the line.
void println​(float x) Prints a float and then terminate the line.
void println​(int x) Prints an integer and then terminate the line.
void println​(long x) Prints a long and then terminate the line.
void println​(Object x) Prints an Object and then terminate the line.
void println​(String x) Prints a String and then terminate the line.

No println for Date!

  • why does the code compile?
  • why and how does it execute?
    • what println is invokated at runtime?
    • why the printed string makes sense? (Mon Mar 16 10:36:13 UTC 2020 is actually a date!)
154 / 667

Similarly

import java.util.Date;
public class Greeter {
public static void main(String[] args) {
System.out.println("Hello World! Today is: " + new Date());
}
}
eric@cpu:~$ java Greeter
Hello World! Today is: Mon Mar 16 10:36:13 UTC 2020

+ operator on String and Date:

  • why does it compile?
  • why and how does it execute?
155 / 667

Inheritance

156 / 667

Inheritance

It is static property of the language

  • it has effect at compile-time

It allows to define a new type A starting from existing type B:

  • only new or changed parts (methods/fields) are defined
157 / 667

Syntax

public class Derived extends Base {
/* ... */
}

We (the developer) mean: "Dear compiler":

  • this Derived class is identical to the Base class
  • I'll define new fields and methods
    • methods and fields cannot be hidden (or "removed")
  • I'll redefine existing fields and methods
    • for methods, by re-using the very same signature

We say that Derived extends (or inherits from, or derives from) Base.

158 / 667

Object

Every class implicitly derives Object (when not explicitly deriving from another class):

  • every class has the methods and fields of Object
public class Greeter {
/* ... */
}

is the same of:

public class Greeter extends Object {
/* ... */
}

"Surprisingly", Object is a class, not an object...

159 / 667

Inheritance tree

A class can inherit from another class, that inherits from another class, ...

public class EnhancedGreeter extends Greeter { /* ... */ }
public class Greeter { /* ... */ }

EnhancedGreeter has methods and fields of Greeter and of Object.

Since a class can be derived from many classes, inheritance relationships form a tree.

160 / 667

Inheritance in the documentation

Package java.io

Class Reader

java.lang.Object
java.io.Reader

Reader has all the fields and methods of Object

  • it may have some more methods and/or fields
  • some methods may behave differently

Package java.io

Class BufferedReader

java.lang.Object
java.io.Reader
java.io.BufferedReader

BufferedReader has all the fields and methods of Reader and Object

  • it may have some more methods and/or fields
  • some methods may behave differently
161 / 667

What methods are inherited?

Specified in the doc ("methods declared in class ..."):

162 / 667

Point of view of Base developer

public class Base {
/* my stuff! */
}

I do not need to know if, how, when, who, where, (and why) will derive Base!

But still write clean code!

E.g., Object developers did not know we would have been writing some Java code right now

163 / 667

Point of view of Derived developer

public class Derived extends Base {
/* my incremental stuff! */
}

I do not need to have the source code of Base

  • to compile, nor to execute
  • I need the .class, though

E.g., "no one" needs the source code of Object!

164 / 667

Constructor and inheritance

public class Derived extends Base {
public Derived() {
/* init things */
}
}
  • Who (which code) initializes inherited fields (those of Base)?
  • When?

Note (before the answers) that:

  • Base fields might be private: what code other than that of Base can operate on them?
  • suppose that a Derived method is called within the Derived():
    • it can use Base methods (it's legit)
    • those methods can use Base fields: who should be the responsible for their initialization?
165 / 667

Constructor derived class

public class Derived extends Base {
public Derived() {
Base();
/* init things */
}
}

The compiler inserts an (implicit, wrt to code) call to derived class constructor Base()!

  • Who (which code) initializes inherited fields (those of Base)?
    • Base()
  • When?
    • before any statement of Derived()
166 / 667

Many constructors/no default constructor

What if Base does not have the no-args constructor?

The compiler requires the developer to specify which Base constructor to use

  • and its parameters

Syntax: super(...)

167 / 667

super() constructor

public class Greeter {
public Greeter(String name) {
/* init things */
}
}
public class EnhancedGreeter extends Greeter {
public EnhancedGreeter(String firstName, String lastName) {
super(firstName + lastName);
/* init things */
}
}

Ok!

  • code compiles
168 / 667

super() constructor: not working

public class Base {
public Base(int n) { /* ... */ }
public Base(double v) { /* ... */ }
}
public class Derived extends Base {
private int m;
public Derived() {
m = 0;
super(5);
/* ... */
}
}

Not ok!

  • does not compile: super() has to be invoked first
169 / 667

super() constructor: not working

public class Base {
public Base(int n) { /* ... */ }
public Base(double v) { /* ... */ }
}
public class Derived extends Base {
private int m;
public Derived() {
m = 0;
/* ... */
}
}

Not ok!

  • does not compile: super() with no args does not exist
170 / 667

super() constructor: working

public class Base {
public Base(int n) { /* ... */ }
public Base(double v) { /* ... */ }
public Base() { /* ... */ }
}
public class Derived extends Base {
private int m;
public Derived() {
m = 0;
/* ... */
}
}

Ok!

  • code compiles

Which constructor of Base is invoked?

171 / 667

Inheritance and inline initialization

public static class Base {
public int n = 1;
}
public static class Derived extends Base {
public int m = n + 1;
}
Derived derived = new Derived();
System.out.printf("m=%d%n", derived.m); // -> m=2

"Field initialization is executed before the first statement of any constructor." \rightarrow any of this class!

  • super() is executed before any inline initialization

What is derived.n?

172 / 667

Polymorphism

173 / 667

Reference type and inheritance

public class Derived extends Base { /* ... */ }

Any code that works with a Base can work also with a Derived.

  • more formally: a reference to type Base can (i.e., the compiler says ok) reference an object of type Derived
public void use(Base base) { /* ... */ }
Base base = new Derived(); // OK!!!
use(new Base()); // OK, sure!
use(new Derived()); // OK!!!
174 / 667

Using Derived

public void use(Base base) { /* ... */ }
use(new Derived()); // OK!!!

Why does it compile? (use() was written to work with a Base)

  • Derived has all fields and methods of Base
    • maybe with different behaviors (methods, but signature is exactly the same)
    • maybe it has also other methods/fields
  • \rightarrow any dot notation valid on a Base is also valid on a Derived!

"has all fields", but recall visibility!

175 / 667

Inheritance, philosophycally

Derived has all fields and methods of Base.

Syllogism: anything being a Derived is also a Base

public class LivingBeing { /* ... */ }
public class Animal extends LivingBeing { /* ... */ }
public class Mammal extends Animal { /* ... */ }
public class Dog extends Mammal { /* ... */ }
public class Chihuahua extends Dog { /* ... */ }

A Chihuahua is a Dog, a Mammal, ..., and an Object.

But, please, avoid over-using inheritance.

There's a debate about inheritance being good or bad. Indeed, this is not the most appropriate example of using inheritance...

176 / 667

Be general

Rule: use the most general definition!

public void putLeash(Dog dog) { /* ... */ }

is better than:

public void putLeash(Chihuahua dog) { /* ... */ }

even if in your code you will putLeash() only on Chihuahuas (but provided that you have the inheritance Dog \rightarrow Chihuahua).

177 / 667

... but not too much

Similarly:

public Milk milk(Mammal mammal) { /* ... */ }

is better than:

public Milk milk(Cow cow) { /* ... */ }

However:

public Milk milk(Animal animal) { /* ... */ }

is wrong!

  • but (as is here) compiles!
  • wrong, because conceptually you cannot milk every animal
178 / 667

Removing methods?

public class Derived extends Base { /* ... */ }

Why cannot Derived remove some methods/fields of Base?

  • more specifically, why the language does not even have a construct for doing this?

Why cannot Derived reduce visibility of methods/fields of Base?

  • more specifically, why the compiler does not accept it?

Because otherwise "Derived has all fields and methods of Base" would not be true!

  • so we could not write use(new Derived()), nor println(new Date()), and inheritance would be useless!
179 / 667

Same code, difference behavior

public void greet(Person person) {
System.out.println("Hi, " + person.getFullName());
}
public class Person {
/* ... */
public String getFullName() {
return firstName + " " + lastName;
};
}
public class Doc extends Person {
/* ... */
public String getFullName() {
return "Dr. " + firstName + " " + lastName;
};
}

Outcome is different with the same greet() and same fields content!

180 / 667

Polymorphism

It is a dynamic consequence of inheritance:

  • it has effect at runtime

It results in the same code to have different behaviors depending on the type of the parameter.

181 / 667

Different behavior

public class Derived extends Base { /* ... */ }
public void use(Base base) { /* ... */ }
Base base = new Derived();

Outcome might be "different" than that with = new Base().

use(new Base()); // OK, sure!
use(new Derived()); // OK!!!

Outcomes might be "different".

Developer of use() did not know that someone would have derived Base:

  • they knew how to use a Base
  • who wrote Derived decided that any Derived is a Base
182 / 667

Not the opposite!

Obviously...

public Base createBase() { /* ... */ }
Derived derived;
derived = createBase();
  1. Derived derived: developer \rightarrow compiler
    • please, take note (and make sure) that with any operation defined in Derived can be applied to object referenced by derived
  2. derived = createBase(): compiler \rightarrow developer
    • no! I cannot meet the requirement ("make sure that") because the object returned by createBase() might not be a Derived

It might also be a Derived, but cannot guarantee...

183 / 667

Might be...

public Base createBase() {
if (System.currentTimeMillis() % 2 == 0) {
return new Base();
}
return new Derived();
}

(Purely evil! Don't do it at home!)

Derived derived;
derived = createBase();

createBase() can really return a Derived...

What if I (developer) am sure that it is a Derived?

184 / 667

Downcasting

Derived derived;
derived = (Derived) createBase();

What if I (developer) am sure that it is a Derived?

Syntax: (Derived) base

  • "changes" type of downcast reference
  • works only if Derived derives (directly or indirectly) from the class of base

Why "works only..."?

  • because return in createBase() must take a Base, so there's no reason to accept something that cannot happen!

Still, at runtime, there is a check at every invokation.

185 / 667

instanceof

Binary operator (keyword) for checking the type of a object at runtime:

  • evaluates to boolean
boolean isString = ref instanceof String;

Formally, a instanceof B evaluates to true if and only if object referenced by a might be legitimately referenced by a reference b to objects of type B.

public class Derived extends Base { /* ... */ }
Derived derived = new Derived();
boolean b1 = derived instanceof Object; // -> true
boolean b2 = derived instanceof Base; // -> true
boolean b3 = derived instanceof Derived; // -> true
186 / 667

Typical usage

Object obj = someOddMethod();
if (obj instanceof String) {
String string = (String) obj;
System.out.println(string.toUpperCase());
// obj.toUpperCase() would not compile!
}

Since JDK 14 (March 2020):

Object obj = someOddMethod();
if (obj instanceof String string) {
System.out.println(string.toUpperCase());
}

Just a language shorthand! Called Pattern matching with instanceof

187 / 667

Why?

import java.util.Date;
public class Greeter {
public static void main(String[] args) {
System.out.print("Hello World! Today is: ");
System.out.println(new Date());
}
}
  • Why does the code compile?
  • Why and how does it execute?
    • What println is invokated at runtime?
    • Why the printed string makes sense?
188 / 667

Why does it compile?

PrintStream class:

Type Field Description
void println​(Object x) Prints an Object and then terminate the line.

Date extends Object and there is a PrintStream.println(Object)!

  • a Date has everything an Object has
  • println() says it knows how to print on Object

Ok, but then?

189 / 667

toString()

Object class:

Type Field Description
String toString() Returns a string representation of the object.

Returns a string representation of the object. In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.

The toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character '@', and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of:
getClass().getName() + '@' + Integer.toHexString(hashCode())

Hence:

  • every object has a toString() method
  • every object can be represented as string
190 / 667

Overriding toString()

In general, the toString method returns a string that "textually represents" this object. The result should be a concise but informative representation that is easy for a person to read. It is recommended that all subclasses override this method.

The developer of a class may (and should!) redefine (= override) toString():

  • returns a string that "textually represents" this object
  • concise but informative
  • easy for a person to read

IDEs have an option for writing a decent toString() for you (Alt+Ins in Netbeans)

191 / 667

toString() of Date

public String toString()

Converts this Date object to a String of the form:

dow mon dd hh:mm:ss zzz yyyy

where:

  • dow is the day of the week (Sun, Mon, Tue, Wed, Thu, Fri, Sat).
  • mon is the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec).
  • dd is the day of the month (01 through 31), as two decimal digits.
  • hh is the hour of the day (00 through 23), as two decimal digits.
  • mm is the minute within the hour (00 through 59), as two decimal digits.
  • ss is the second within the minute (00 through 61), as two decimal digits.
  • zzz is the time zone (and may reflect daylight saving time). Standard time zone abbreviations include those recognized by the method parse. If time zone information is not available, then zzz is empty - that is, it consists of no characters at all.
  • yyyy is the year, as four decimal digits.
192 / 667

Why does it work?

import java.util.Date;
public class Greeter {
public static void main(String[] args) {
System.out.print("Hello World! Today is: ");
System.out.println(new Date());
}
}

In PrintStream:

public void println(Object x) {
if ( x == null ) {
println("null");
} else {
println(x.toString());
}
}
193 / 667

Delegation and information hiding

public void println(Object x) {
if ( x == null ) {
println("null");
} else {
println(x.toString());
}
}

The developers of PrintStream just took care of how to print an Object

  • check for null inside println()
  • delegation to Object toString() for the rest

No need for knowledge about if, how, when, who, where, (and why) would have derived Object!

194 / 667

Why?

System.out.println("Now is " + new Date());

Why does the code compile?

  • because + with a String as 1st operand "accepts" anything as 2nd operand

Why and how does it execute?

  • because the compiler translated + ref to something like
    (ref == null) ? "null" : ref.toString()
    (if ref is not a reference to primitive type)
195 / 667

How does polymorphism work?

(briefly)

196 / 667

Own class "field"

At runtime, every object o has a description of its class:

  • it is an object of class Class
  • it is "shared" among all instances of the class of o
    • approx. like a static field

It can be obtained with the getClass() method of Object:

  • hence, since it is in Object, every o has getClass()
    • since every object has all the methods declared in Object

Object class:

Type Field Description
Class<?> getClass() Returns the runtime class of this Object.

Ignore the <?> for now; we'll see it later

197 / 667

Diagram

StringStringStringClassDateDateClass
198 / 667

What's inside the description?

Conceptually, inside the Class of a class X:

  • methods of X (i.e., declared in X)
  • fields of X
  • ...

We'll see later more about this!

public class Base {
public void doBase() { /.../ }
}
public class Derived extends Base {
public void doDerived() { /.../ }
}
Derived d = new Derived();
methods contains doDerived, not doBase
dDerivedClassmethodsfields
199 / 667

Reference to superclass

  • and the class of the base type, possibly Object
dDerivedClassmethodsfieldssuperclassClassmethodsfieldssuperclassClassmethodsfieldssuperclass
  • methods of d.getClass() contains doDerived
  • methods of d.getClass().superclass contains doBase
  • methods of d.getClass().superclass.superclass contains toString, getClass, ...

Approximately: types and names of fields/methods are different

200 / 667

Which method?

System.out.println(derived); // -> derived.toString()
public void println(Object x) {
if ( x == null ) {
println("null");
} else {
println(x.toString());
}
}

Conceptually:

  1. get Class c of derived
  2. get methods of c
  3. do methods contains toString()
    • yes, use that method
    • no, set c to c.superclass and go back to 2

In the worst case, c is eventually the Class describing Object

201 / 667

Class and Object

Class is a class; Object is a class.

  • you can use object as identifier of a reference to objects of class Object
  • you cannot use class as identifier of a reference to objects of class Class
    • because class is a keyword

Many developers and examples use clazz...

202 / 667

Methods of Object

Object class:

Type Field Description
boolean equals​(Object obj) Indicates whether some other object is "equal to" this one.
Class<?> getClass() Returns the runtime class of this Object.
String toString() Returns a string representation of the object.

equals(): indicates whether some other object is "equal to" this one.

What's the difference with respect to ==?

203 / 667

== (and !=)

a == b

  • if a and b of different primitive types**, or of primitive and non-primitive**, or of non compatible* non-primitive types, then code does not compile
  • else if a and b of same primitive type, then evaluates to boolean true iff a and b have the same value, otherwise evaluates to false
  • else evaluates to boolean true if a and b reference the same object or both do not reference any object (i.e., they "are" null), otherwise evaluates to false

a != b

  • if it compiles, evaluates to the negation of a == b

*: "compatible" means that types are the same or one derives from the other

204 / 667

Same object!

String s1 = "hi!";
String s2 = "hi!";
String s3 = s2;
boolean b;
b = s1 == s2; // -> false
b = s2 == s3; // -> true
b = s1.length() == s2.length(); // -> true
s1Strings2Strings3booleanintint
205 / 667

equals()

Object: "indicates whether some other object is "equal to" this one".

Actual meaning depend on the type, that possibly overrides equals()

  • String: "true if and only if [...] represents the same sequence of characters [...]"
  • Date: "true if and only if [...] represents the same point in time, to the millisecond [...]"
  • PrintStream: does not override equals()
    • the developers of PrintStream decided that the "equal to" notion of Object hold for PrintStreams
206 / 667

equals() documentation in Object

Indicates whether some other object is "equal to" this one.

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).

Two things in this documentation:

  • what's the general semantics of equals()
  • how does the specific implementation in Object work
207 / 667

equals() in Object

"the most discriminating possible equivalence relation":

public class Object {
/* ... */
public boolean equals(Object other) {
if (other == null) {
return false;
}
return this == other;
}
}

Note: this cannot be null!

208 / 667

Overriding equals

Semantics level requirements:

  • equivalence conceptually sound for the specific case
  • reflexivity, symmetry, transitiveness, consistency, null

Syntax level requirement:

  • comply with the signature
    public boolean equals(Object other)
    • the argument is of type Object also for the deriving type!

The compiler:

  • does check fulfillment of syntax requirement
  • cannot check fulfillment of semantics requirements
    • big responsability of the developer
209 / 667

Implementing equivalence

It depends on the case, i.e., on the type; it should correspond to equivalence of the represented concepts.

Given type X, should answer the question:

  • are x1 and x2 the same X?

E.g., equivalence of persons: are two persons the same?

  • in the real world:
    • formed by the same atoms? (probably too strict)
    • same first and last name? (probably too loose)
  • in the application:
    • depends of what aspects of real world are modeled
210 / 667

Equivalent persons

public class Person {
private String firstName;
private String lastName;
private Date birthDate;
}

This class models a person by her/his first name, last name, and birth date:

  • it's a developer's decision

At most (in strictness), equals() can say that "two persons are the same iff they have the same first name, same last name, and same birth date".

  • what if two "different" persons has the same name and birth date? (Marco Rossi!)
  • what if a person changes name?
211 / 667

Equivalent persons with fiscal code

public class Person {
private String firstName;
private String lastName;
private Date birthDate;
private String fiscalCode;
}

This class models a person with [...] and fiscal code.

By domain knowledge, the developer can decide that "two persons are the same iff they have the same fiscal code".

212 / 667

Simple Person.equals()

public class Person {
private String firstName;
private String lastName;
private Date birthDate;
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (!(other instanceof Person)) {
return false;
}
if (!firstName.equals(((Person) other).firstName)) {
return false;
}
if (!lastName.equals(((Person) other).lastName)) {
return false;
}
if (!birthDate.equals(((Person) other).birthDate)) {
return false;
}
return true;
}
}
  • (Person) to downcast other as Person
  • assumes fields are not null
213 / 667

Person.equals() with fiscal code

public class Person {
private String firstName;
private String lastName;
private Date birthDate;
private String fiscalCode;
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (!(other instanceof Person)) {
return false;
}
if (!fiscalCode.equals(((Person) other).fiscalCode)) {
return false;
}
return true;
}
}

We (the developers) decided that two persons are the same if they have the same fiscal code, regardless of name and birth date!

214 / 667

IDEs and equals()

As for toString(), IDEs have an option for writing equals() for you (Alt+Ins in Netbeans). E.g., outcome with fiscalCode:

public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Person other = (Person) obj;
if (!Objects.equals(
this.fiscalCode,
other.fiscalCode
)) {
return false;
}
return true;
}
  • this == obj: a performance optimization
  • same getClass() different than instanceof!
  • Objects.equals() takes care of checking if field is null

Ignore final for now

215 / 667

Objects

This class consists of static utility methods for operating on objects, or checking certain conditions before operation.

A class with utility methods, static by design.

Objects class:

Type Field Description
static int checkIndex​(int index, int length) Checks if the index is within the bounds of the range from 0 (inclusive) to length (exclusive).
static boolean deepEquals​(Object a, Object b) Returns true if the arguments are deeply equal to each other and false otherwise.
static boolean equals​(Object a, Object b) Returns true if the arguments are equal to each other and false otherwise.
static boolean isNull​(Object obj) Returns true if the provided reference is null otherwise returns false.

"deeply equal": equal, but also with arrays.

Many other similar classes in and out the JDK, e.g., Arrays:

  • static fill(), sort(), ...
216 / 667

instanceof vs. getClass()

a instanceof B

  • true iff object referenced by a might be legitimately referenced by a reference b to objects of type B

a.getClass() == b.getClass():

  • true iff at runtime class of object referenced by a is exactly the same (==) of class of object referenced by b

a.getClass() == B.class

  • X.class is (approx.) an implicit static reference to the object representing the class X
  • true iff at runtime class of object referenced by a is exactly B
217 / 667

instanceof vs. getClass(): example

public class Person { /* ... */ }
public class Employee extends Person { /* ... */ }
Person p = new Person( /* ... */ );
Employee e = new Employee( /* ... */ );
boolean b1 = e instanceof Person; // -> true
boolean b2 = p.getClass() == e.getClass(); // -> false
pPersonClassClassClasseEmployeeb1booleanb2boolean

Is an employee equal to the person being the employee? Big question: it depends on how you model the reality.

218 / 667

equals() on complex types

equals() should represent the equivalence that is inherently defined by the representation defined by the type.

public class BagOfInts {
private int[] values;
}

Models X=M(Z)={f:ZN}X = \mathcal{M}(\mathbb{Z}) = \{f: \mathbb{Z} \to \mathbb{N}\}. (bag == multiset)

Equiv. of x1,x2x_1, x_2 considers:

  • length
  • items
public class SequenceOfInts {
private int[] values;
}

Models X=iNZiX = \bigcup_{i \in \mathbb{N}}\mathbb{Z}^i.

Equiv. of x1,x2x_1, x_2 considers:

  • length
  • items
  • order of items

XX "is" the class, xXx \in X is the object
M(A)\mathcal{M}(A) is my notation for the set of multisets built with elements of the set AA.

219 / 667

Everything in the name!

X=M(Z)X = \mathcal{M}(\mathbb{Z})

class BagOfInts {
private int[] values;
}

X=iNZiX = \bigcup_{i \in \mathbb{N}}\mathbb{Z}^i

class SequenceOfInts {
private int[] values;
}

Exactly the same code, different models!

Difference in models should be captured by the name of the type!

220 / 667

Multiple inheritance

public class Vehicle { /* ... */ }
public class MotorVehicle extends Vehicle {
public void startEngine() { /* ... */ }
}
public class Motorboat extends MotorVehicle {
public void startEngine() { System.out.println("bl bl bl"); }
public void turnPropeller() { /* ... */ }
}
public class Car extends MotorVehicle {
public void startEngine() { System.out.println("brum brum"); }
public void turnWheels() { /* ... */ }
}
  • Every MotorVehicle is a Vehicle
  • Every Motorboat is a MotorVehicle, thus also a Vehicle
  • Every Car is a MotorVehicle, thus also a Vehicle.
  • In general, a Car is not a Motorboat!
221 / 667

Here comes the amphibious!

Fiat 6640

Ohhh, surprise!

There is a Car that is also a Motorboat!

It is a Fiat 6640

public class AmphibiousVehicle extends Motorboat, Car { // NOOOO!
public void doAmphibiousFancyThings() { /* ... */ }
}

Does not compile! No multiple inheritance!

222 / 667

Why not?

public class AmphibiousVehicle extends Motorboat, Car { // NOOOO!
public void doAmphibiousFancyThings() { /* ... */ }
}
AmphibiousVehicle fiat6640 = new AmphibiousVehicle();
fiat6640.startEngine();

Should it result in bl bl bl or in brum brum?

  • it's certain that there is a startEngine(), since both Motorboat and Car extends MotorVehicle: which one should be invoked? same for equals(), toString(), ...
  • same for fields

Java syntax does not allow the developer to specify which one!

223 / 667

Why not?

"it was a precise decision of Java developers"

They might have allowed multiple inheritance from A, B only when A and B intersection of overridden methods was empty:

public class A { public void doA() { /*...*/ } }
public class B { public void doB() { /*...*/ } }
public class C extends A, B { public void doC() { /*...*/ } }

Looks legit! A c can doA(), doB(), doC(), and do toString()!

What if then the developer of A override toString()!

  • Disaster! The same code of C does not compile anymore!

There is a mechanism in Java for modeling the amphibious; we'll see it!

224 / 667

Equivalence ~2h, 2nd home assignement

  1. Design and implement a class that represents X=M(R)X = \mathcal{M}(\mathbb{R})
    • with a proper equals()
    • with a proper toString()
    • with a proper constructor that takes 0+ values R\in \mathbb{R}
  2. Write an application that reads from stdin two comma-separated lists of real numbers, creates x1,x2Xx_1, x_2 \in X, and outputs a text message saying if x1,x2x_1, x_2 are the same or not
    • user gives two separated inputs
    • comma separates values within each input

There are other ways for implementing multisets (and sets, sequences, ...); we'll see them.

Do not use the collection framework. Do not use Guava Multiset

225 / 667

More on

parameters, visibility of identifiers, memory

226 / 667

What happens to parameters?

public class Person {
private String name;
public Person(String name) { this.name = name; }
public String getName(){ return name; }
public void setName(String name){ this.name = name; }
}
public void modify(Person p) {
p.setName(p.getName().toUpperCase());
//no return!
}
Person eric = new Person("Eric");
modify(eric);
System.out.println(eric.getName());

What's the name here? Are the changes visible to the caller?

Is the parameter passed by value or by reference?

227 / 667

Parameter passing

By value:

  • the called receives a copy of the object

By reference:

  • the called receives a copy of the reference

In Java:

  • primitive types are passed by value
  • non-primitive types are passed by reference
228 / 667

Passing non-primitive

public void modify(Person p) { // B
p.setName(p.getName().toUpperCase());
}
Person eric = new Person("Eric"); // A
modify(eric); // C

At A:

ericPersonString

At B:

PersonStringp
By reference:
p is a copy of eric

At C:

ericPersonStringString
Change is visible!
229 / 667

Passing primitive

public void uselesslyModify(int n) { // B
n = 18;
}
int age = 41; // A
uselesslyModify(age); // C

At A:

ageint

At B:

intnint
By value:
41 is a copy of 41

At C:

ageint
Change is not visible!
230 / 667

final

A modifier (keyword):

  • applicable to classes, methods, fields, variables
  • static: effects only at compile-time

Different effects depending on type of definition:

  • class
  • method
  • fields and variables
231 / 667

final class

Cannot be extended!

  • i.e., the compiler raises an error when compiling a class definition that attempts to extend a final class
public final class Greeter {
/* ... */
}
public class EnhancedGreeter extends Greeter {
/* ... */
}

EnhancedGreeter does not compile!

232 / 667

final method

Cannot be overriden!

  • i.e., the compiler raises an error when compiling a class definition, extending a class, that attempts to override a final method of the extended class
public class Greeter {
public final String greet() { return "Hello!" };
}
public class EnhancedGreeter extends Greeter {
public String greet() { /* ... */ }
}

EnhancedGreeter does not compile!

  • neither with public final String greet()
233 / 667

final fields and variables

Cannot be reassigned!

  • i.e., cannot be assigned more than once
    • regardless of being primitive or not
  • in practice, they are constants
public class Greeter {
private final String msg = "Hi!";
public void update(String msg) {
this.msg = msg;
}
}

Does not compile!

public class Greeter {
public void update(final int n) {
n = 18;
}
public void doThings() {
final String s = "hi!";
s = "hello";
}
}

Does not compile:

  • for n = 18
  • for s = "hello"
234 / 667

Why/when using final?

Recall, compile-time only effects!

  • the developer asks the compiler to check usage of final things
    • developer to her/him-self "message"
    • developer to other developer "message"
  • no effects at runtime

Use cases:

  • constants
    • fields very common
  • limiting re-usability
    • classes and methods rare
  • remarking immutability
    • variables rather rare good practice
235 / 667

Constants with final

Object-wise constant:

  • an immutable field that can have different values across different instances of the same class
  • just final usually private

Class-wise constant:

  • a global constant, i.e., a placeholder for a hard-coded value
  • final static
236 / 667

Object-wise constant

public class Person {
private String firstName;
private String lastName;
private final Date birthDate;
private final String fiscalCode;
public Person(Date birthDate, String fiscalCode) {
this.birthDate = birthDate;
this.fiscalCode = fiscalCode;
}
}

A developer's decision on how to model the "reality":

  • for a given Person, birthDate and fiscalCode never change!
    • they are immutable
  • for a given Person, firstName and lastName can change

final fields have to be initialized!

237 / 667

Class-wise constant

public class Doc extends Person {
private final static String TITLE_PREFIX = "Dr.";
public String getFullName() {
return TITLE_PREFIX + " " + firstName + " " + lastName;
};
}

"Dr." is just a costant that we may re-use in many places

  • final static
  • avoid the risk to type it differently
  • ease changing the value all-at-once
  • public if meant to be used outside (e.g., Math.PI)

Naming convention is different for final static constants:

  • all uppercase
  • components separated by _ (underscore)
238 / 667

Remarking immutability

When parameter is a primitive type:

public void update(final int n) {
/* ... */
}

Reminds me (the developer) that changing n would not have effect!

  • i.e., dear compiler, tell me if I forget this and attempt to wrongly reassign n

In some cases, the IDE suggests to add the final modifier.

239 / 667

Also for non-primitive!

public void capitalizeName(final Person p) {
p.setName(
p.getName().substring(0, 1).toUpperCase() +
p.getName().substring(1).toLowerCase()
);
}

No reason for reassigning p!

  • if you'd reassign p, you were modifying another Person object, not on the passed one (meant to be modified)!
240 / 667

Remarking immutability of variables

Person[] teachers = new Person[] {alberto, eric};
for (final Person teacher : teachers) {
capitalizeName(teacher);
}

No reason for reassigning teacher!

  • if you'd reassign it, you were not operating on the array element!
241 / 667
Person spareTeacher = new Person(/* ... */);
Person[] teachers = new Person[] {alberto, eric};
for (Person teacher : teachers) {
if (teacher.isIll()) { // A
teacher = spareTeacher;
} // B
}

At A, 1st iteration:

albertoPersonericPersonspareTeacherPersonteachersPerson[]01teacher

At B, 2nd iter., assume eric is ill:

albertoPersonericPersonspareTeacherPersonteachersPerson[]01teacher
242 / 667

Scope and lifetime

243 / 667

Scope vs. lifetime

Scope of an identifier:

  • code portion where the identifier can be legitimately used (i.e., where it is visible)
  • static property

Lifetime of an object or reference:

  • time interval when the object or reference exists
  • dynamic property

In general:

  • depend on language
  • depend on entity type
244 / 667

Scope: reference

Declared in method signature (aka argument variable):

  • visible everywhere in the method

Declared in method code (aka local variable):

  • visible from declaration to the end of block
    • a block is a sequence of statements enclosed by curly brackets { }
245 / 667

Example

public String capitalize(final String string) {
String capitalized = "";
for (final String token : string.split(" ")) {
String head = token.substring(0, 1);
String remaining = token.substring(1);
capitalized = capitalized
+ head.toUpperCase() + remaining.toLowerCase() + " ";
}
return capitalized;
}
  • Five identifiers: string, capitalized, token, head, remaining
  • Five different scopes

What is the scope for each identifier (line of start, line of end)?

Do you see other issues in this code? (At least 4...)

246 / 667

Scope: fields, class, method

Determined by access modifier:

  • private, protected, default, public

protected:

  • visible only in a class extending (directly or indirectly) the class where protected is used

Fields are also known as instance variables

247 / 667

Lifetime

Reference and primitive object:

  • exists only during the execution of the block

Non-primitive object:

  • exists at least as long as it is referenced
  • when non referenced, might exist or not exist
    • in practice it's the same: it cannot be used!
248 / 667

Example

public int doThings(String s) {
int p = 2;
String s2 = s.trim();
int l = s2.length(); // B
return l / p;
}
public void run() {
String name = "simba "; // A
int n = doThings(name); // C
}

At A:

nameString

At B:

Stringspints2Stringlint

At C:

nameStringStringnint
249 / 667

JVM memory

(some more details)

250 / 667

Overview

JVM memory is organized in:

  • heap
    • long-lived data
    • global
  • stack
    • short-lived data
    • organized in blocks

Many other differences that we do not need to know.

Stack has faster access; one stack per thread; stack is much smaller

251 / 667

Heap vs. stack: storing

Stored in heap:

  • every non-primitive object
    • everything that is created with new
    • with its fields, primitive and non-primitive

Stored in stack:

  • every reference
  • every primitive object not being a field
  • i.e., argument and local variables "created" within methods
252 / 667

Heap vs. stack: freeing

Heap:

  • "cleaned" every while we'll see soon

Stack (organized in blocks):

  • one new block created at each method invocation
  • block removed (memory becomes free) just after invocation
253 / 667

Stack and primitive types

Primitive type objects in the stack: the identifier identifies ("is") the object, instead of the reference

This explains the differences:

  • lifetime like references
  • parameter passing by value
    • "reference by value" is like "object by reference"
  • assignement creates copy of value
    • reference assignement creates copy of reference
  • == compares content
    • content of reference is the "address" of referenced object

And:

  • primitive types cannot be null
254 / 667

Primitive like references

int iString* s=s

String* 0xAAF3 references the String stored at 0xAAF3 in the heap. We'll continue to use the previous notation.

Primitive types has a precise size in memory:

Type Size
byte 1 byte
short 2 bytes
int 4 bytes
long 8 bytes
Type Size
float 4 bytes
double 8 bytes
char 2 bytes
boolean *

Also references!

4 bytes (heap < 32 GB)
8 bytes (heap ≥ 32 GB)

*: JVM dependent

\rightarrow JVM knows in advance how much stack reserve for a method invocation!

255 / 667

Diagram: updated syntax

public static void main(String[] args) {
int l = args.length;
}
eric@cpu:~$ java Greeter eric simba
JVM memoryStackHeapmain()int largsString[]01lengthStringString
256 / 667

Example updated

public int doThings(String s) {
int p = 2;
String s2 = s.trim();
int l = s2.length(); // B
return l / p;
}
eric@cpu:~$ java Greeter
public static void main(String[] args) {
(new Main()).run();
}
public void run() {
String name = "simba "; // A
int n = doThings(name); // C
}
StackHeapmain()argsString[]lengthrun()nameString

Also String has its fields; we omit for compactness.

257 / 667

Example updated

public int doThings(String s) {
int p = 2;
String s2 = s.trim();
int l = s2.length(); // B
return l / p;
}
eric@cpu:~$ java Greeter
public static void main(String[] args) {
(new Main()).run();
}
public void run() {
String name = "simba "; // A
int n = doThings(name); // C
}
StackHeapmain()argsString[]lengthrun()nameStringdoThings()int pint lss2String
258 / 667

Example updated

public int doThings(String s) {
int p = 2;
String s2 = s.trim();
int l = s2.length(); // B
return l / p;
}
eric@cpu:~$ java Greeter
public static void main(String[] args) {
(new Main()).run();
}
public void run() {
String name = "simba "; // A
int n = doThings(name); // C
}
StackHeapmain()argsString[]lengthrun()nameStringStringint n
259 / 667

Freeing the memory

StackHeapmain()argsString[]lengthrun()nameStringStringint n
  • doThings() block in the stack freed just after invocation
  • String "simba" no more referenced, hence useless \rightarrow garbage
    • who/when/how frees the corresponding heap space?
260 / 667

Garbage

public class Person {
private int age;
private String name;
private Person[] friends;
}
//main()
Person eric = new Person();
StackHeapmain()Personagenamefriendseric
261 / 667

Complex garbage

StackHeapmain()PersonagenamefriendsStringPerson[]01PersonagenamefriendsStringPerson[]0PersonagenamefriendsStringPerson[]0saraargsString[]
length field for arrays omitted
What is garbage here?
262 / 667

Garbage collection

The JVM itself takes care of removing the garbage from the heap:

  • decides when
  • decides what garbage to remove
  • trafe-off between avoiding overhead and having some free space

The component of the JVM doing this cleaning is the garbage collector (GC)

  • different GCs, different how
263 / 667

System.gc()

The developer may suggest the JVM to do garbage collection:

System class:

Type Field Description
static void gc() Runs the garbage collector in the Java Virtual Machine.

Runs the garbage collector in the Java Virtual Machine.

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for reuse by the Java Virtual Machine. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all unused objects. There is no guarantee that this effort will recycle any particular number of unused objects, reclaim any particular amount of space, or complete at any particular time, if at all, before the method returns or ever.

Just a kind request...

264 / 667

Before GC

Before GC, the developer was responsible for freeing unused memory.

  • e.g., malloc() \rightarrow free()

Responsability \rightarrow source of problems, when misbehavior

  • forget to call free() \rightarrow out of memory
  • free() on wrong address \rightarrow invalid write
  • write over than reserved malloc() \rightarrow possible chaos

No more problems with automatic garbage collection!

265 / 667

GC: cost

GC can be tricky:

  • unused objects can form graph, possibly acyclic: what is actually garbage?
  • garbage can be large

Doing GC takes time!

  • "unpredictably" long
  • "unpredictable" when

My be undesired in very specific scenarios.

But:

  • you can "tune" your GC setting
  • you can ask for GC when the moment is suitable
    • by calling System.gc() and hoping for the best
266 / 667

Setting the size of available memory

JVM (java) parameters:

  • heap
    • starting size: -Xms
    • max size: -Xmx
  • stack
    • size: -Xss
eric@cpu:~$ java MyBigApplication -Xmx8G

When exceeded:

  • java.lang.OutOfMemoryError: Java heap space
    • GC made the effort, but failed!
  • java.lang.StackOverFlowError
267 / 667

Wrapper classes

There are cases when it is better (or required) to operate on basic types as non-primitive rather than primitive.

The JDK contains a set of wrapper classes, one for each primitive type:

  • they are immutable (like Strings)
  • they can be stored in the heap

Wrapper classes

  • Integer for int with different name
  • Double for double
  • Boolean for boolean
  • Character for char with different name
  • ...

There's also a Void class

268 / 667

Constants and constructor

Integer class: the same for the others

The Integer class wraps a value of the primitive type int in an object. An object of type Integer contains a single field whose type is int.

In addition, this class provides several methods for converting an int to a String and a String to an int, as well as other constants and methods useful when dealing with an int.

Constants:

Modifier and type Field Description
static int BYTES The number of bytes used to represent an int value in two's complement binary form.
static int MAX_VALUE A constant holding the maximum value an int can have, 2^31-1.
static int MIN_VALUE A constant holding the minimum value an int can have, -2^31.

Constructors:

Constructor Description
Integer​(int value) Deprecated. It is rarely appropriate to use this constructor.
Integer​(String s) Deprecated. It is rarely appropriate to use this constructor.
269 / 667

Methods

"Like" constructors: only the third is "really" a constructor-like method

Modifier and type Field Description
static int parseInt​(String s) Parses the string argument as a signed decimal integer.
static int parseInt​(String s, int radix) Parses the string argument as a signed integer in the radix specified by the second argument.
static Integer valueOf​(int i) Returns an Integer instance representing the specified int value.
int n = Integer.parseInt("1100110", 2); // -> 102

Others:

Modifier and type Field Description
int intValue() Returns the value of this Integer as an int.
long longValue() Returns the value of this Integer as a long after a widening primitive conversion.
270 / 667

Autoboxing, autounboxing

Compiler performs obvious implicit translations:

public void doIntThings(int n) { /* ... */ }
public void doIntegerThings(Integer n) { /* ... */ }

Original:

Integer i = 3;
doIntThings(i);
int n = 3;
doIntegerThings(n);
Integer i = 2;
i++;

Translated:

Integer i = Integer.valueOf(3);
doIntThings(i.intValue());
int n = 3;
doIntegerThings(Integer.valueOf(n));
Integer i = Integer.valueOf(2);
i = Integer.valueOf(i.intValue()+1);

In general, use int when possible!

271 / 667

Diagram

Integer i = Integer.valueOf(2);
i = Integer.valueOf(i.intValue()+1);
StackHeapiInteger
StackHeapiIntegerInteger
int i = 2;
i++;
StackHeapint i
StackHeapint i
272 / 667

Boxing and equals

There is no unboxing for ==!!!

Integer n = 300;
Integer m = 300;
int k = 300;
System.out.printf("n ?= m -> %b%n", n==m); // -> false!!!
System.out.printf("n ?= k -> %b%n", n==k); // -> true!!!

%n in printf() is translated to newline!

  • In general, use int when possible!
  • If using Integer, use == with care!
    • use equals()!

What happens with (n+1)==(m+1)?

IDEs sometimes warns about misuse of ==

273 / 667

"Advanced" Input/Output (I/O)

with streams

274 / 667

Basic I/O

We already know how to do I/O

  • of basic types (primitive and Strings)
  • from/to stdin/stdout
BufferedReader reader = new BufferedReader(
new InputStreamReader(System.in)
);
/* ... */
String line = reader.readLine();
Scanner scanner = new Scanner(System.in);
/* ... */
String s = scanner.next();
int n = scanner.nextInt();
double d = scanner.nextDouble();

What about other types and other source/target devices?

275 / 667

Abstraction: I/O stream

Stream: endpoints, direction, type of data

  • a stream of X between A and B

Two basic models:

  • output stream to device where data can be written as byte[]
  • input stream from a device where data can be read as byte[]
byte[]
byte[]
276 / 667

Interaction with a stream

Usage (key steps):

  1. create the stream associating with the device
  2. read or write byte[]

Device: file, "network", ...

277 / 667

Beyond basic models

Other more complex models in which there is some processing of the data:

  • compression/decompression of byte[]
  • transformation of byte[] to/from other types
  • ...
278 / 667

InputStream and OutputStream

All I/O streams extend one between InputStream and OutputStream.

They can be composed:

  • a stream can be build over another stream: the "result" is still a stream (of the same direction)
  • data stream through streams an instance of the filter pattern
  • each stream "is" the device for the stream above
Composition of OutputStreams
Composition of InputStreams
279 / 667

Point of view of the "user"

No need to change the code the "user" of the stream is a developer:

  • for different devices \leftarrow polymorphism
  • for different processings \leftarrow polymorphism
  • also for more complex (wrt byte[]) data types \leftarrow composition
280 / 667

How many I/O streams?

A lot!

We'll see a subset, for most common

  • devices
  • data transformation
  • processing
281 / 667

I/O of byte[]

282 / 667

OutputStream class

This abstract class is the superclass of all classes representing an output stream of bytes. An output stream accepts output bytes and sends them to some sink.

Methods:

Mod. and Type Method Description
void close() Closes this output stream and releases any system resources associated with this stream.
void flush() Flushes this output stream and forces any buffered output bytes to be written out.
static OutputStream nullOutputStream() Returns a new OutputStream which discards all bytes.
void write​(byte[] b) Writes b.length bytes from the specified byte array to this output stream.
void write​(byte[] b, int off, int len) Writes len bytes from the specified byte array starting at offset off to this output stream.
abstract void write​(int b) Writes the specified byte to this output stream.
  • Writes only byte[]
  • Subclasses take devices OutputStream is abstract, cannot be instantiated
    • OutputStream.nullOutputStream() gives a device discarding written byte[]
283 / 667

Writing byte[]

OutputStream class:

Mod. and Type Method Description
void write​(byte[] b, int off, int len) Writes len bytes from the specified byte array starting at offset off to this output stream.

Writes len bytes from the specified byte array starting at offset off to this output stream. The general contract for write(b, off, len) is that some of the bytes in the array b are written to the output stream in order; element b[off] is the first byte written and b[off+len-1] is the last byte written by this operation.

Other methods:

  • write(b) is the same as write(b, 0, b.length)
284 / 667

Use of write()

byte[] data = /* ... */
OutputStream os = /* ... */
os.write(data, 0, 3); //A
os.write(data, 3, 2); //B
os.write(new byte[2]); //C

The device abstraction (might be a real device, or another OutputStream) of os:

  • after A: contains 3 bytes; next write will start at 3 (0-indexes)
  • after B: 5 bytes; cursor (= next write) at 5
  • after C: 7 bytes; cursor at 7
285 / 667

Subclasses of OutputStream

write​(byte[] b, int off, int len)

The general contract for write(b, off, len) is that some of the bytes in the array b are written to the output stream in order; element b[off] is the first byte written and b[off+len-1] is the last byte written by this operation.

"The general contract is [...]": a message for:

  1. the developer extending OutputStream
  2. the curious user (another developer)
    • who can, in principle, ignore how the stream writes the data

write(byte[] b)

The general contract for write(b) is that it should have exactly the same effect as the call write(b, 0, b.length).

286 / 667

Extending OutputStream

In principle, just write(int b) can be overridden! Actually, must be, we'll see... (In class description)

Applications that need to define a subclass of OutputStream must always provide at least a method that writes one byte of output.

But: (In write​(byte[] b, int off, int len))

The write method of OutputStream calls the write method of one argument on each of the bytes to be written out. Subclasses are encouraged to override this method and provide a more efficient implementation.

287 / 667

Internals

public void write(byte[] b, int off, int len) {
for (int i = off; i<off+len; i++) {
write(b[i]);
}
}

Subclasses are encouraged to override this method and provide a more efficient implementation.

Means:
if there is a fixed cost (overhead) in writing on the device, regardless of data size, write more than one bytes at once

288 / 667

Associating with a device

File:

OutputStream os = new FileOutputStream(/* ... */);
byte[] data = /* ... */
os.write(data);

Network (TCP):

Socket socket = /* ... */
OutputStream os = socket.getOutputStream();
byte[] data = /* ... */
os.write(data);

Memory:

OutputStream os = new ByteArrayOutputStream();
byte[] data = /* ... */
os.write(data);
289 / 667

FileOutputStream

Package java.io

Class FileOutputStream

java.lang.Object
java.io.OutputStream
java.io.FileOutputStream

A file output stream is an output stream for writing data to a File or to a FileDescriptor. Whether or not a file is available or may be created depends upon the underlying platform. Some platforms, in particular, allow a file to be opened for writing by only one FileOutputStream (or other file-writing object) at a time. In such situations the constructors in this class will fail if the file involved is already open.

"depends upon the underlying platform": there is still a physical machine under the JVM!

Constructors:

Constructor Description
FileOutputStream​(File file) Creates a file output stream to write to the file represented by the specified File object.
FileOutputStream​(File file, boolean append) Creates a file output stream to write to the file represented by the specified File object.
FileOutputStream​(String name) Creates a file output stream to write to the file with the specified name.
FileOutputStream​(String name, boolean append) Creates a file output stream to write to the file with the specified name.
290 / 667

From Socket

Socket class:

Mod. and Type Method Description
OutputStream getOutputStream() Returns an output stream for this socket.

The user does not need to know the actual class of the output stream returned by getOutputStream().

291 / 667

ByteArrayOutputStream

Package java.io

Class ByteArrayOutputStream

java.lang.Object
java.io.OutputStream
java.io.ByteArrayOutputStream

This class implements an output stream in which the data is written into a byte array. The buffer automatically grows as data is written to it. The data can be retrieved using toByteArray() and toString().

Mod. and Type Method Description
int size() Returns the current size of the buffer.
byte[] toByteArray() Creates a newly allocated byte array.

Usage:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] data = /* ... */
baos.write(data);
byte[] written = baos.toByteArray();
System.out.println(Arrays.equals(data, written)); // -> true

Why? As a fake device, to do to byte[] conversion, ...

292 / 667

End of Stream (EOS)

  • A logical marker saying that there is no data after it
  • Inserted by close()
byte[] data = /* ... */
OutputStream os = /* ... */
os.write(data, 0, 3); //A
os.close(); //B

Usually, no other write() are possible after EOS has written.

293 / 667

EOS and close()

From OutputStream.close():

Closes this output stream and releases any system resources associated with this stream. The general contract of close is that it closes the output stream. A closed stream cannot perform output operations and cannot be reopened.

The close method of OutputStream does nothing.

OutputStream is the base class, no specific device:

  • "system resources" are the device
  • "Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has been closed [...]"
294 / 667

InputStream class

This abstract class is the superclass of all classes representing an input stream of bytes.

Methods (some):

Mod. and Type Method Description
abstract int read() Reads the next byte of data from the input stream.
int read​(byte[] b) Reads some number of bytes from the input stream and stores them into the buffer array b.
int read​(byte[] b, int off, int len) Reads up to len bytes of data from the input stream into an array of bytes.
  • Reads only byte[]
  • Subclasses take devices InputStream is abstract, cannot be instantiated
    • InputStream.nullInputStream() gives a device with no bytes to read (EOS is at position 0)
295 / 667

Reading byte[]

Mod. and Type Method Description
int read​(byte[] b, int off, int len) Reads up to len bytes of data from the input stream into an array of bytes.

Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made to read as many as len bytes, but a smaller number may be read. The number of bytes actually read is returned as an integer.

Attempt? We'll see...

Other methods:

  • read(b) is the same as read(b, 0, b.length)
  • ...
296 / 667

Use of read()

byte[] buffer = new byte[100];
InputStream is = /* ... */ //A
is.read(buffer, 0, 4); //B
is.read(buffer, 4, 1); //C

The device abstraction (might be a real device, or another InputStream) of is:

  • after A: cursor at 0
  • after B: cursor at 4
  • after C: cursor at 5
297 / 667

Associating with a device

File:

InputStream is = new FileInputStream(/* ... */);
byte[] buffer = new byte[100];
is.read(buffer, 0, 10);

Network (TCP):

Socket socket = /* ... */
InputStream is = socket.getInputStream();
byte[] buffer = new byte[100];
is.read(buffer, 0, 10);

Memory:

InputStream is = new ByteArrayInputStream(/* ... */);
byte[] buffer = new byte[100];
is.read(buffer, 0, 10);
298 / 667

FileInputStream

Package java.io

Class FileInputStream

java.lang.Object
java.io.InputStream
java.io.FileInputStream

A FileInputStream obtains input bytes from a file in a file system. What files are available depends on the host environment.

Constructors:

Constructor Description
FileInputStream​(File file) Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system.
FileInputStream​(String name) Creates a FileInputStream by opening a connection to an actual file, the file named by the path name name in the file system.

Note: no boolean append! (obviously)

299 / 667

ByteArrayInputStream

Package java.io

Class ByteArrayInputStream

java.lang.Object
java.io.InputStream
java.io.ByteArrayInputStream

A ByteArrayInputStream contains an internal buffer that contains bytes that may be read from the stream. An internal counter keeps track of the next byte to be supplied by the read method.

"contains an internal buffer" \rightarrow the device!

Constructors:

Constructor Description
ByteArrayInputStream​(byte[] buf) Creates a ByteArrayInputStream so that it uses buf as its buffer array.
300 / 667

Difference in abstraction

OutputStream

os.write(new byte[2]);
os.write(new byte[3]);

No limit* on writeable data:

  • no pre-existing EOS

*: in principle

InputStream

is.read(new byte[2]);
is.read(new byte[3]);

Readable data is limited:

  • an EOS exists!
301 / 667

Input from files and byte[]

Input from file with FileInputStream:

  • created with FileInputStream(File file) we'll see a File is
  • the file exists, hence it has a size, hence a EOS

Input from byte[] with ByteArrayInputStream:

  • created with ByteArrayInputStream(byte[] data)
  • the data exists, hence it has a size, hence a EOS
302 / 667

Output to files and byte[]

Output to file with FileOutputStream:

  • created with, e.g., FileOutputStream(File file, boolean append)
  • create the file (if not existing and possible), or write at the end of file \rightarrow no limits to the writeable data!
    • possible limits related to underlying OS (obviously)

Output to byte[] with ByteArrayOutputStream:

  • "The buffer automatically grows as data is written to it."
303 / 667

Attempt of reading (with EOS)

Before: device still contains 5 bytes, the EOS

is.read(buf, 0, 2); //OK!

Before: device still contains 3 bytes, then EOS

is.read(buf, 0, 5); //?
304 / 667

Attempt of reading (without EOS)

Before: device contains 3 ready bytes

is.read(buf, 0, 5); //?

Other data might arrive (be ready) in the future...

Before: device contains no ready bytes

is.read(buf, 0, 5); //?

Other data might arrive (be ready) in the future...

305 / 667

read()

Mod. and Type Method Description
int read​(byte[] b, int off, int len) Reads up to len bytes of data from the input stream into an array of bytes.

Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made to read as many as len bytes, but a smaller number may be read. The number of bytes actually read is returned as an integer.

This method blocks until input data is available, end of file is detected, or an exception is thrown.

If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b.

Attempt to read len bytes:

  • if next byte in device is EOS, return immediately -1
  • if 0 bytes ready: block and wait for data
  • if \ge len ready, read len bytes and return immediately len
  • otherwise, read n<n < len bytes and return immediately nn
306 / 667

Example

Suppose the device:

  • contains 5 bytes from time t=0t=0 to t=10t=10 (seconds)
  • receives other 3 bytes, with a trailing EOS, at t=10t=10
is.read(buf, 0, 3);
// t=0 -> t~0, ret 3
is.read(buf, 0, 3);
// t~0 -> t~0, ret 2
is.read(buf, 0, 3); // BLOCK!
// t~0 -> t~10, ret 3
is.read(buf, 0, 3);
// t~10 -> t~10, ret -1
307 / 667

Blocking read()

"The device receives": the InputStream receives for the underlying level, eventually from a physical devices

Keyboard \rightarrow InputStream System.in

  • the user types (and hit enter) \rightarrow InputStream receives bytes

Network connection \rightarrow InputStream getInputStream()

  • data from the network arrives to this socket \rightarrow receives bytes
308 / 667

Internally

read​(byte[] b, int off, int len) in InputStream:

The read(b, off, len) method for class InputStream simply calls the method read() repeatedly.

Mod. and Type Method Description
abstract int read() Reads the next byte of data from the input stream.
Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.

read() read one byte, but returns an int: why?

  • because the semantics of the return value includes a special value (-1) representing EOS
  • domain of byte does not include -1!
309 / 667

-1 in read() and write()

Other alternatives for representing EOS:

  • returning a Byte, null for EOS
    • object creation overhead; uses heap, slower
  • throwing an exception we ignore exceptions now; we'll see
    • EOS is not an exception, indeed; it's the norm

Java inventors chose to use an int as return value

  • for consistency read(byte[], int, int) also returns an int
  • for consistency write(int) in OutputStream takes an int!
Mod. and Type Method Description
abstract void write(int b) Writes the specified byte to this output stream.
Writes the specified byte to this output stream. The general contract for write is that one byte is written to the output stream. The byte to be written is the eight low-order bits of the argument b. The 24 high-order bits of b are ignored.
310 / 667

File

Surprisingly, the File class does not represent a file!

Package java.io

Class File

java.lang.Object
java.io.File

An abstract representation of file and directory pathnames.

User interfaces and operating systems use system-dependent pathname strings to name files and directories. This class presents an abstract, system-independent view of hierarchical pathnames.

  • "An abstract representation of file and directory pathnames".
  • "system-independent view of hierarchical pathnames"

(a rather long description follows)

311 / 667

File system-independent view

312 / 667

Not a file!

No methods for reading and writing!

Methods for:

  • removing, renaming, listing (File represents dirs too), ...
Mod. and Type Method Description
boolean canExecute() Tests whether the application can execute the file denoted by this abstract pathname.
boolean canRead() Tests whether the application can read the file denoted by this abstract pathname.
boolean canWrite() Tests whether the application can modify the file denoted by this abstract pathname.
boolean delete() Deletes the file or directory denoted by this abstract pathname.
boolean exists() Tests whether the file or directory denoted by this abstract pathname exists.
boolean isDirectory() Tests whether the file denoted by this abstract pathname is a directory.
String[] list() Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname.
File[] listFiles() Returns an array of abstract pathnames denoting the files in the directory denoted by this abstract pathname.
boolean mkdir() Creates the directory named by this abstract pathname.
boolean renameTo​(File dest) Renames the file denoted by this abstract pathname.
boolean setExecutable​(boolean executable) A convenience method to set the owner's execute permission for this abstract pathname.
313 / 667

Open with FileOutputStream

FileOutputStream​(File file) and FileOutputStream​(String name):

  • if file/name does not exist, creates new one if OS says it's possible
  • puts the cursor at 0

\rightarrow existing content is cancelled!

  • if you want to append, use the constructors with boolean append!
    • puts cursor at the position equal to the length of file
314 / 667

Example: file copy

We want to develop an application that:

  • receives two file names f1,f2f_1, f_2 as command line arguments
  • copies the content of file named f1f_1 to f2f_2
    • assume file f1f_1 exists and is readable
    • overwrite file f2f_2 if it exists
eric@cpu:~$ java FileCopier slides.zip copy-of-slides.zip
315 / 667

Sketch

  1. build FileInputStream from String f1f_1; build FileOutputStream from String f2f_2
  2. iterate until EOS
    1. read byte[] from FileInputStream
    2. write byte[] to FileOutputStream
byte[]byte[]
316 / 667

Stopping criterion

"Iterate until EOS"

Suppose to use a buffer of nn bytes (i.e., to read nn bytes for iteration):

  • every iteration but second-last and last reads nn bytes
  • second-last reads n\le n
  • last return -1 \rightarrow EOS
317 / 667

Possible code

public class FileCopier {
public static void main(String[] args) throws FileNotFoundException, IOException {
InputStream is = new FileInputStream(args[0]);
OutputStream os = new FileOutputStream(args[1]);
byte[] buffer = new byte[1024];
while (true) {
int nOfBytes = is.read(buffer);
if (nOfBytes == -1) {
break;
}
os.write(buffer, 0, nOfBytes);
}
is.close();
os.close();
}
}

Ignore throws FileNotFoundException, IOException for now.

318 / 667

Make it more general

public class Util {
public static void copyAndClose(InputStream is, OutputStream os) throws FileNotFoundException, IOException {
byte[] buffer = new byte[1024];
while (true) {
int nOfBytes = is.read(buffer);
if (nOfBytes == -1) {
break;
}
os.write(buffer, 0, nOfBytes);
}
is.close();
os.close();
}
}

copyAndClose() "ignores" the specific kind of streams (devices):

  • while developing/compiling: inheritance
  • while executing: polymorphism
319 / 667

I/O of primitive types

320 / 667

Output with DataOutputStream

Package java.io

Class DataOutputStream

java.lang.Object
java.io.OutputStream
java.io.FilterOutputStream
java.io.DataOutputStream

A data output stream lets an application write primitive Java data types to an output stream in a portable way. An application can then use a data input stream to read the data back in.

Constructor Description
DataOutputStream​(OutputStream out) Creates a new data output stream to write data to the specified underlying output stream.
Mod. and Type Method Description
void writeDouble​(double v) Converts the double argument to a long using the doubleToLongBits method in class Double, and then writes that long value to the underlying output stream as an 8-byte quantity, high byte first.
void writeFloat​(float v) Converts the float argument to an int using the floatToIntBits method in class Float, and then writes that int value to the underlying output stream as a 4-byte quantity, high byte first.
void writeInt​(int v) Writes an int to the underlying output stream as four bytes, high byte first.
void writeLong​(long v) Writes a long to the underlying output stream as eight bytes, high byte first.

"converts [...] high byte first" \rightarrow portable way

321 / 667

Input with DataInputStream

Package java.io

Class DataInputStream

java.lang.Object
java.io.InputStream
java.io.FilterInputStream
java.io.DataInputStream

A data input stream lets an application read primitive Java data types from an underlying input stream in a machine-independent way. An application uses a data output stream to write data that can later be read by a data input stream.

Constructor Description
DataInputStream​(InputStream in) Creates a DataInputStream that uses the specified underlying InputStream.
Mod. and Type Method Description
double readDouble() See the general contract of the readDouble method of DataInput.
float readFloat() See the general contract of the readFloat method of DataInput.
int readInt() See the general contract of the readInt method of DataInput.
long readLong() See the general contract of the readLong method of DataInput.

"machine-independent" == "portable"

322 / 667

Filter pattern

DataOutputStream extends FilterOutputStream

This class is the superclass of all classes that filter output streams. These streams sit on top of an already existing output stream (the underlying output stream) which it uses as its basic sink of data, but possibly transforming the data along the way or providing additional functionality.

DataInputStream extends FilterInputStream

A FilterInputStream contains some other input stream, which it uses as its basic source of data, possibly transforming the data along the way or providing additional functionality.

323 / 667

Abstraction

writeDouble()writeBoolean()
readDouble()readBoolean()
324 / 667

Portability

Java developers built DataInputStream and DataOutputStream together:

  • they read and write primitive types to byte[] accordingly
  • every machine reads/write byte[] in the same way

\Rightarrow portability!

E.g.:

  • file written on a OS is readable from another OS
  • data sent via socket is readable on the other endpoint

But...

325 / 667

Portability vs. protocol

Portability is granted for the single data item!

At the application level, parties (reader and writer) have to act accordingly!

  • in practice, they have to share a protocol
326 / 667

"Real-life" example

Alice and Bob meet on chat:

  • A: tell me your age, weight, and annual net income in k€
  • B: 65, 95, 40
  • A: 🙅

Creative Bob:

  • B (unfair): 40, 65, 95
  • A: 😻

What happened?

  • each single number was correctly communicated!
  • application protocol (order) was not respected (in the 2nd case)
    • with this data, Alice has no way for telling if Bob is complying the protocol!
    • maybe with other data, e.g., 20, 75, 40
327 / 667

Java streams example

Application saves data on file:

writeInt()writeDouble()writeInt()
Writes 16 bytes:
  • 4 for 1st int
  • 8 for double
  • 4 for 2nd int

Application loads same file:

readInt()readFloat()readInt()
Reads 12 bytes:
  • 4 for 1st int
  • 4 for float
  • 4 for 2nd int
328 / 667

Disaster!

Writes 16 bytes:

  • 4 for 1st int
  • 8 for double
  • 4 for 2nd int

Reads 12 bytes:

  • 4 for 1st int
  • 4 for float
  • 4 for 2nd int

Read data "is different" than written data:

  • and no way to realize it at runtime
329 / 667

File array ~3h

  1. Design and implement a class FileArray that mantains a binary file-backed array of int values, with constructors/methods to:
    • build a new array with nn random values U({0,,210})\sim U(\{0,\dots,2^{10}\})
    • load an existing array
    • print the array
    • increment all elements of the arrays
  2. Write an application that "uses" FileArray
    • receives m1m \ge 1 parameters at command line
    • 1st is a file pathname
      • if not existing, create with nU({1,,25})n \sim U(\{1,\dots,2^5\}), otherwise, load
    • from 2nd on, a list of one-char commands:
      • i for increment
      • p for print
330 / 667

FileArray

public class FileArray {
// loads an existing file
public FileArray(String filePathName) { /* ... */ }
// creates new file with n random elements
public FileArray(String filePathName, int n) { /* ... */ }
// pretty print with at most 5 aligned elements per row
public void print();
//increment all elements
public void incrementAll();
}

Hints:

  • for (pseudo)random generation, see Random.nextInt()
  • "ignore" exceptions
  • use two "internal" methods:
    • read: file \rightarrow int[]
    • write: int[] \rightarrow file
    • e.g.: print=read,print; incrementAll=read,inc,write
331 / 667

Application using FileArray

Assuming test.bin exists with 8 values:

eric@cpu:~$ java FileArrayTester test.bin p i i p
[00-04] 8 73 12 4 99
[05-09] 13 23 75 33 9
[10-12] 18 6 78
[00-04] 10 75 14 6 101
[05-09] 15 25 77 35 11
[10-12] 20 8 80

And the file content is left accordingly.

Note:

  • alignment and (zero-padded aligned) indexes
  • column width is the shortest possible (2 at 1st p, 3 at 2nd p)

Hint: use printf or String.format(), e.g., %3d

332 / 667

Compression with GZIPOutputStream

Package java.io

Class GZIPOutputStream

java.lang.Object
java.io.OutputStream
java.io.FilterOutputStream
java.util.zip.DeflaterOutputStream
java.util.zip.GZIPOutputStream

This class implements a stream filter for writing compressed data in the GZIP file format.

Constructor Description
GZIPOutputStream​(OutputStream out) Creates a new output stream with a default buffer size.
Mod. and Type Method Description
void finish() Finishes writing compressed data to the output stream without closing the underlying stream.
void write​(byte[] buf, int off, int len) Writes array of bytes to the compressed output stream.
333 / 667

Decompression with GZIPInputStream

Package java.io

Class GZIPInputStream

java.lang.Object
java.io.InputStream
java.io.FilterInputStream
java.util.zip.InflaterInputStream
java.util.zip.GZIPInputStream

This class implements a stream filter for reading compressed data in the GZIP file format.

Constructor Description
GZIPInputStream​(InputStream in) Creates a new input stream with a default buffer size.
Mod. and Type Method Description
void close() Closes this input stream and releases any system resources associated with the stream.
int read​(byte[] buf, int off, int len) Reads uncompressed data into an array of bytes.
334 / 667

GZIP vs. zip

There are also ZipOutputStream and ZipInputStream:

  • GZIP streams "just" compress/decompress binary data
  • Zip streams operate with files
    • they can contain files and directories
335 / 667

GZIP file array ~1h, 3rd home assign.

  1. Design and implement a class GZIPFileArray that extends FileArray and mantains a compressed binary file-backed array of int values
  2. Write an application that "uses" FileArray or GZIPFileArray
    • receives m1m \ge 1 parameters at command line
    • 1st is a file pathname
      • if not existing, create with nU({1,,25})n \sim U(\{1,\dots,2^5\}), otherwise, load
      • if filename ends with .zip, use GZIPFileArray, otherwise use FileArray
    • from 2nd on, a list of one-char commands (see specifications)
336 / 667

Buffered I/O

337 / 667

OS and I/O

Most devices are actually read/written by the OS: not, e.g., ByteArrayOutputStream

  • OS performs reads and writes of data chunks of a size sos_o that depends on the OS and the devices
    • e.g., 8 KB for files on disk
  • invoking OS is costly

Applications may need to read/write data chunks with size sasos_a \ne s_o

  • if sa<sos_a < s_o, OS is invoked more often than needed
    \rightarrow inefficiency!
Application
338 / 667

Writes to device

Application

Application to OutputStream:

  • write() 100 bytes, write() 1000 bytes, write() 100 bytes

OutputStream (JVM) to OS:

  • write() 100 bytes, read() 1000 bytes, write() 100 bytes

OS to device:

  • hopefully something optimal
339 / 667

Solution: buffered stream

A filter stream with a buffer.

BufferedOutputStream:

  1. takes write requests as usual
  2. puts data in a buffer; if full makes write requests to the underlying stream
    • or if explicitly instructed to

BufferedInputStream:

  1. takes read requests as usual
  2. if in buffer, takes from buffer, otherwise fill the buffer with a read from underlying stream
340 / 667

Writes to device with buffer

Assume buffer of 1000 bytes.

Application

Application to BufferedOutputStream:

  • write() 100 bytes, write() 1000 bytes, write() 100 bytes, ...

BufferedOutputStream to OutputStream and OutputStream to OS:

  • write() 1000 bytes, ...
341 / 667

Reads from device with buffer

Assume buffer of 1000 bytes.

Application

Application to BufferedInputStream:

  • read() 100 bytes, read() 1000 bytes, read() 100 bytes, ...

BufferedInputStream to InputStream and InputStream to OS:

  • read() 1000 bytes, read() 1000 bytes, ...
342 / 667

BufferedInputStream

Package java.io

Class BufferedInputStream

java.lang.Object
java.io.InputStream
java.io.FilterInputStream
java.io.BufferedInputStream

A BufferedInputStream adds functionality to another input stream-namely, the ability to buffer the input and to support the mark and reset methods. When the BufferedInputStream is created, an internal buffer array is created. As bytes from the stream are read or skipped, the internal buffer is refilled as necessary from the contained input stream, many bytes at a time.

Constructor Description
BufferedInputStream​(InputStream in) Creates a BufferedInputStream and saves its argument, the input stream in, for later use.
BufferedInputStream​(InputStream in, int size) Creates a BufferedInputStream with the specified buffer size, and saves its argument, the input stream in, for later use.

Use with read methods from InputStream.

343 / 667

BufferedOutputStream

Package java.io

Class BufferedOutputStream

java.lang.Object
java.io.OutputStream
java.io.FilterOutputStream
java.io.BufferedOutputStream

The class implements a buffered output stream. By setting up such an output stream, an application can write bytes to the underlying output stream without necessarily causing a call to the underlying system for each byte written.

Constructor Description
BufferedOutputStream​(OutputStream out) Creates a new buffered output stream to write data to the specified underlying output stream.
BufferedOutputStream​(OutputStream out, int size) Creates a new buffered output stream to write data to the specified underlying output stream with the specified buffer size.

Use with write methods from OutputStream.

Redefines flush().

344 / 667

flush()

BufferedOutputStream:

  1. takes write requests as usual
  2. puts data in a buffer; if full makes write requests to the underlying stream
    • or if explicitly instructed to \rightarrow flush()

public void flush()
Flushes this buffered output stream. This forces any buffered output bytes to be written out to the underlying output stream.

  • Invoking flush() propagates down to the JVM-OS boundary, not beyond!
  • It does not guarantee that the data is actually written to the physical device!
Application
345 / 667

When to use buffered streams?

Always!

  • Code that uses a I/O stream, can use a I/O buffered stream too!
  • Good practice:
OutputStream os = new BufferedOutputStream(
new FileOutputStream(file)
);

Should you flush()?

  • if your application is robust, you don't need to: content is flushed at close()
  • flush() needed only for output stream:
    • BufferedInputStream reads from below more at least the requested data, possibly more
346 / 667

I/O of text data

347 / 667

Binary vs. text data

  • Binary data is byte[]
  • Text data is char[]

A char is encoded with one or more bytes: the precise way each char is encoded is specified in a charset.

  • Java deals with charsets with the Charset class
    • in most cases, you don't need to mess with Charset
348 / 667

I/O of text data

Many application do I/O of text data, rather than binary data:

  • HTTP protocol
  • CSV files
  • ...

The JDK provides classes for text I/O that take care of byte[] \leftrightarrow char[].
"Same" abstraction of byte[] streams, but with char[]:

  • Writer, Reader instead of OutputStream, InputStream
  • can be composed with the filter pattern
  • can be associated with device
349 / 667

Abstraction

Output of text:

char[]char[]

Input of text:

char[]char[]
] ]
350 / 667

Output with Writer

Package java.io

Class Writer

java.lang.Object
java.io.Writer

Abstract class for writing to character streams. The only methods that a subclass must implement are write(char[], int, int), flush(), and close(). Most subclasses, however, will override some of the methods defined here in order to provide higher efficiency, additional functionality, or both.

Mod. and Type Method Description
Writer append​(char c) Appends the specified character to this writer.
Writer append​(CharSequence csq) Appends the specified character sequence to this writer.
Writer append​(CharSequence csq, int start, int end) Appends a subsequence of the specified character sequence to this writer.
abstract void close() Closes the stream, flushing it first.
abstract void flush() Flushes the stream.
static Writer nullWriter() Returns a new Writer which discards all characters.
void write​(char[] cbuf) Writes an array of characters.
abstract void write​(char[] cbuf, int off, int len) Writes a portion of an array of characters.
void write​(int c) Writes a single character.
void write​(String str) Writes a string.
void write​(String str, int off, int len) Writes a portion of a string.
351 / 667

char[] are Strings

write​(String str) might use String toCharArray():

public void write​(String str) throws IOException {
write(str.toCharArray());
}

Strings are CharSequences:

public Writer append​(CharSequence csq) throws IOException {
write(csq.toString());
}
352 / 667

append()

Mod. and Type Method Description
Writer append​(char c) Appends the specified character to this writer.
Writer append​(CharSequence csq) Appends the specified character sequence to this writer.
Writer append​(CharSequence csq, int start, int end) Appends a subsequence of the specified character sequence to this writer.

append() methods return a Writer, actually this Writer

  • can be used with chain invocation
Writer writer = /* ... */;
writer
.append("Lorem ipsum dolor sit amet, ")
.append("consectetur adipiscing elit, ")
.append("sed do eiusmod tempor incididunt ")
.append("ut labore et dolore magna aliqua.");
353 / 667

Writing text to files with FileWriter

Package java.io

Class FileWriter

java.lang.Object
java.io.Writer
java.io.OutputStreamWriter
java.io.FileWriter

Writes text to character files using a default buffer size. Encoding from characters to bytes uses either a specified charset or the platform's default charset.

Whether or not a file is available or may be created depends upon the underlying platform. Some platforms, in particular, allow a file to be opened for writing by only one FileWriter (or other file-writing object) at a time. In such situations the constructors in this class will fail if the file involved is already open.

The FileWriter is meant for writing streams of characters. For writing streams of raw bytes, consider using a FileOutputStream.

Constructor Description
FileWriter​(String fileName, Charset charset, boolean append) Constructs a FileWriter given a file name, charset and a boolean indicating whether to append the data written.
Writer writer = new FileWriter("file.txt");
writer.write("Hello world!");
writer.close();
354 / 667

Writing text to OutputStreamWriter

Package java.io

Class OutputStreamWriter

java.lang.Object
java.io.Writer
.java.io.OutputStreamWriter

An OutputStreamWriter is a bridge from character streams to byte streams: Characters written to it are encoded into bytes using a specified charset. The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.

Each invocation of a write() method causes the encoding converter to be invoked on the given character(s). The resulting bytes are accumulated in a buffer before being written to the underlying output stream. Note that the characters passed to the write() methods are not buffered.

Constructor Description
OutputStreamWriter​(OutputStream out) Creates an OutputStreamWriter that uses the default character encoding.
OutputStreamWriter​(OutputStream out, String charsetName) Creates an OutputStreamWriter that uses the named charset.
OutputStreamWriter​(OutputStream out, Charset cs) Creates an OutputStreamWriter that uses the given charset.
OutputStreamWriter​(OutputStream out, CharsetEncoder enc) Creates an OutputStreamWriter that uses the given charset encoder.
OutputStream os = /* ... */;
Writer writer = new OutputStreamWriter(os);

There is some buffering towards underlying OutputStream.

355 / 667

Writing "in memory"

CharArrayWriter

This class implements a character buffer that can be used as an Writer. The buffer automatically grows when data is written to the stream. The data can be retrieved using toCharArray() and toString().
Note: Invoking close() on this class has no effect, and methods of this class can be called after the stream has closed without generating an IOException.

StringWriter:

A character stream that collects its output in a string buffer, which can then be used to construct a string.
Closing a StringWriter has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.

356 / 667

Writing with buffer

Package java.io

Class BufferedWriter

java.lang.Object
java.io.Writer
java.io.BufferedWriter

The buffer size may be specified, or the default size may be accepted. The default is large enough for most purposes.

A newLine() method is provided, which uses the platform's own notion of line separator as defined by the system property line.separator. Not all platforms use the newline character ('\n') to terminate lines. Calling this method to terminate each output line is therefore preferred to writing a newline character directly.

In general, a Writer sends its output immediately to the underlying character or byte stream. Unless prompt output is required, it is advisable to wrap a BufferedWriter around any Writer whose write() operations may be costly, such as FileWriters and OutputStreamWriters.

Constructor Description
BufferedWriter​(Writer out) Creates a buffered character-output stream that uses a default-sized output buffer.
BufferedWriter​(Writer out, int sz) Creates a new buffered character-output stream that uses an output buffer of the given size.

With flush().

357 / 667

Input with Reader

Package java.io

Class Reader

java.lang.Object
java.io.Reader

Abstract class for reading character streams. The only methods that a subclass must implement are read(char[], int, int) and close(). Most subclasses, however, will override some of the methods defined here in order to provide higher efficiency, additional functionality, or both.

Mod. and Type Method Description
abstract void close() Closes the stream and releases any system resources associated with it.
static Reader nullReader() Returns a new Reader that reads no characters.
int read() Reads a single character.
int read​(char[] cbuf) Reads characters into an array.
abstract int read​(char[] cbuf, int off, int len) Reads characters into a portion of an array.

read() works as for InputStream, but:

  • no methods for reading directly to String!
358 / 667

Reading from...

  • File with FileReader
    • Constructor: FileReader​(File file, Charset charset)
  • Any InputStream with InputStreamReader​
    • Constructor: InputStreamReader​(InputStream in, Charset cs)
  • char[] and String:
    • CharArrayReader​(char[] buf)
    • StringReader​(String s)
359 / 667

Reading with buffer

BufferedReader:

Reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines.
The buffer size may be specified, or the default size may be used. The default is large enough for most purposes.

public String readLine() throws IOException

Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), a carriage return followed immediately by a line feed, or by reaching the end-of-file (EOF).

Returns:
A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached without reading any characters

  • Returns null if EOS is reached (null acts as -1).
360 / 667

Common usage

BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(fileName))
);
while (true) {
String line = br.readLine();
if (line == null) {
break;
}
/* do things with the line */
}
br.close();

We are ignoring the management of the exceptions.
We'll see a more elegant way later.

361 / 667

Printing anything

362 / 667

PrintStream

An output stream (extends OutputStream) that:

  • add methods for printing anything
    • primitive types, Strings, objects
    • "printing" means outputing bytes of string representation
  • never throws exceptions
  • (buffered, optionally with auto flushing)

System.out is a PrintStream

No corresponding class for input:

  • System.in is a "plain" InputStream with no added methods and with exceptions
  • why? for outuput, the application has control on what to print, for input, no
363 / 667

PrintStream

Package java.io

Class PrintStream

java.lang.Object
java.io.OutputStream
java.io.FilterOutputStream
java.io.PrintStream

A PrintStream adds functionality to another output stream, namely the ability to print representations of various data values conveniently. Two other features are provided as well. Unlike other output streams, a PrintStream never throws an IOException; instead, exceptional situations merely set an internal flag that can be tested via the checkError method. Optionally, a PrintStream can be created so as to flush automatically; this means that the flush method is automatically invoked after a byte array is written, one of the println methods is invoked, or a newline character or byte ('\n') is written.

All characters printed by a PrintStream are converted into bytes using the given encoding or charset, or platform's default character encoding if not specified. The PrintWriter class should be used in situations that require writing characters rather than bytes.

Constructor Description
PrintStream​(OutputStream out, boolean autoFlush, Charset charset) Creates a new print stream, with the specified OutputStream, automatic line flushing and charset.

Many other constructors:

  • note the Charset argument, for conversion to strings
364 / 667

Printing anything

Mod. and Type Method Description
PrintStream append​(char c) Appends the specified character to this output stream.
PrintStream append​(CharSequence csq) Appends the specified character sequence to this output stream.
PrintStream append​(CharSequence csq, int start, int end) Appends a subsequence of the specified character sequence to this output stream.
boolean checkError() Flushes the stream and checks its error state.
protected void clearError() Clears the internal error state of this stream.
PrintStream format​(String format, Object... args) Writes a formatted string to this output stream using the specified format string and arguments.
PrintStream format​(Locale l, String format, Object... args) Writes a formatted string to this output stream using the specified format string and arguments.
void print​(long l) Prints a long integer.
void print​(Object obj) Prints an object.
void print​(String s) Prints a string.
PrintStream printf​(String format, Object... args) A convenience method to write a formatted string to this output stream using the specified format string and arguments.

And many others...

365 / 667

PrintWriter

Basically, a PrintStream for underlying char[] streams:

Package java.io

Class PrintWriter

java.lang.Object
java.io.Writer
java.io.PrintWriter

Prints formatted representations of objects to a text-output stream. This class implements all of the print methods found in PrintStream. It does not contain methods for writing raw bytes, for which a program should use unencoded byte streams.

Unlike the PrintStream class, if automatic flushing is enabled it will be done only when one of the println, printf, or format methods is invoked, rather than whenever a newline character happens to be output. These methods use the platform's own notion of line separator rather than the newline character.

Methods in this class never throw I/O exceptions, although some of its constructors may. The client may inquire as to whether any errors have occurred by invoking checkError().

366 / 667

Socket I/O

367 / 667

Background

Basic notions:

  • every host has a unique IP address
  • every process on an host has a unique port number

TCP connection: a pair of "pipes" transferring bytes

  • every byte sent on one endpoint reaches the other endpoint
  • bytes arrives with the same order they have been sent
  • no duplication

Server: a process waiting for connection requests on a port

Client: a process requesting a connection to a server

368 / 667

InetAddress

Package java.net

Class InetAddress

java.lang.Object
java.net.InetAddress

This class represents an Internet Protocol (IP) address.

Mod. and Type Method Description
static InetAddress[] getAllByName​(String host) Given the name of a host, returns an array of its IP addresses, based on the configured name service on the system.
static InetAddress getByAddress​(byte[] addr) Returns an InetAddress object given the raw IP address.
static InetAddress getByAddress​(String host, byte[] addr) Creates an InetAddress based on the provided host name and IP address.
static InetAddress getByName​(String host) Determines the IP address of a host, given the host's name.
static InetAddress getLocalHost() Returns the address of the local host.

No public constructors; many contructor-like methods.

369 / 667

Socket

Package java.net

Class Socket

java.lang.Object
java.net.Socket

This class implements client sockets (also called just "sockets"). A socket is an endpoint for communication between two machines.

Constructor Description
Socket() Creates an unconnected Socket.
Socket​(String host, int port) Creates a stream socket and connects it to the specified port number on the named host.
Socket​(InetAddress address, int port) Creates a stream socket and connects it to the specified port number at the specified IP address.
Mod. and Type Method Description
void close() Closes this socket.
OutputStream getOutputStream() Returns an output stream for this socket.
InputStream getInputStream() Returns an input stream for this socket.

Sample usage on client side:

Socket socket = new Socket("theserver.org", 10000);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
/* do I/O things */
370 / 667

ServerSocket

Package java.net

Class ServerSocket

java.lang.Object
java.net.ServerSocket

This class implements server sockets. A server socket waits for requests to come in over the network.

Constructor Description
ServerSocket​(int port) Creates a server socket, bound to the specified port.
Mod. and Type Method Description
Socket accept() Listens for a connection to be made to this socket and accepts it.
void close() Closes this socket.

Sample usage on server side:

ServerSocket serverSocket = new ServerSocket(10000);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
/* do I/O things */
371 / 667

Blocking accept()

public Socket accept() throws IOException

Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.

372 / 667

Example: uppercaser server

Protocol (upon connection):

  • client sends one text line ll
  • server replies with l=ll'=l converted to uppercase
    • if l=l'= BYE, server closes connection; otherwise waits for next line

Server:

  • listens on port 10000
  • handles 1 client at a time
  • never terminates

No bad things can happen: e.g., client does not close the connection.

373 / 667

SimpleUppercaseServer

public class SimpleUppercaserServer {
private static final int PORT = 10000;
private static final String QUIT_COMMAND = "BYE";
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(PORT);
while (true) {
Socket socket = serverSocket.accept();
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
while (true) {
String line = br.readLine();
bw.write(line.toUpperCase() + System.lineSeparator());
bw.flush();
if (line.toUpperCase().equals(QUIT_COMMAND)) {
break;
}
}
socket.close();
}
}
}

imports omitted for brevity

374 / 667

Socket close()

public void close() throws IOException

Closes this socket.

Once a socket has been closed, it is not available for further networking use (i.e. can't be reconnected or rebound). A new socket needs to be created.

Closing this socket will also close the socket's InputStream and OutputStream.

375 / 667

LineClient

public class LineClient {
public static void main(String[] args) throws IOException {
InetAddress serverInetAddress;
if (args.length > 1) {
serverInetAddress = InetAddress.getByName(args[0]);
} else {
serverInetAddress = InetAddress.getLocalHost();
}
Socket socket = new Socket(serverInetAddress, 10000);
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
for (int i = 0; i < 10; i++) {
String sent = String.format("Hello world n. %d!", i);
bw.write(sent + System.lineSeparator());
bw.flush();
String received = br.readLine();
System.out.printf("Sent: %s%nReceived: %s%n",
sent, received
);
}
bw.write("bye" + System.lineSeparator());
bw.flush();
socket.close();
}
}

imports omitted for brevity

376 / 667

Make it more general: line processing server

Protocol (upon connection):

  • client sends one text line ll
  • if l=lquitl=l_\text{quit}, server closes connection, otherwise replies with processed line l=p(l)l'=p(l)

Server:

  • listens on port nportn_\text{port}
  • handles 1 client at a time motivation for Simple prefix...
  • never terminates
  • designed to be extended
  • p:p: String \to String, lquitl_\text{quit}, port number are parameters

No bad things can happen: e.g., client does not close the connection.

377 / 667

SimpleLineProcessingServer

public class SimpleLineProcessingServer {
private final int port;
private final String quitCommand;
public SimpleLineProcessingServer(int port, String quitCommand) {
this.port = port;
this.quitCommand = quitCommand;
}
public void run() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String line = br.readLine();
if (line.equals(quitCommand)) {
break;
}
bw.write(process(line) + System.lineSeparator());
bw.flush();
}
socket.close();
}
}
protected String process(String input) {
return input;
}
}
378 / 667

Method as parameter

p:p: String \to String is a parameter.

pp is valued by overriding process() while extending SimpleLineProcessingServer:

protected String process(String input) {
return input.toUpperCase();
}

We'll see another, radically different, option.

379 / 667

Add some logging

Log on an OutputStream received as parameter:

  • current date/time, client IP at connection
  • current date/time, client IP, number of requests at disconnection
eric@cpu:~$ java SimpleLineProcessingServer 10000 bye
[2020-04-30 18:17:54] Connection from /127.0.0.1.
[2020-04-30 18:18:06] Disconnection of /127.0.0.1 after 2 requests.
380 / 667

Constructor and fields

public class SimpleLineProcessingServer {
private final int port;
private final String quitCommand;
private final PrintStream ps;
public SimpleLineProcessingServer(int port, String quitCommand, OutputStream os) {
this.port = port;
this.quitCommand = quitCommand;
ps = new PrintStream(os);
}
/* ... */
protected String process(String input) {
return input;
}
}

We use a PrintStream because:

  • we log text
  • it does some buffering
  • (we are lazy and don't care about errors while logging)

No need to close ps here, because the server is supposed to never terminate.

381 / 667

run() \rightarrow run()+handleClient()

public void run() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
handleClient(socket);
}
}
protected void handleClient(Socket socket) throws IOException {
ps.printf("[%1$tY-%1$tm-%1$td %1$tT] Connection from %2$s.%n", System.currentTimeMillis(), socket.getInetAddress());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
int requestsCounter = 0;
while (true) {
String line = br.readLine();
if (line.equals(quitCommand)) {
break;
}
bw.write(process(line) + System.lineSeparator());
bw.flush();
requestsCounter = requestsCounter + 1;
}
socket.close();
ps.printf("[%1$tY-%1$tm-%1$td %1$tT] Disconnection of %2$s after %3$d requests.%n", System.currentTimeMillis(), socket.getInetAddress(), requestsCounter);
}

Might define a private void log(String) method.

For serious logging, use java.util.logging.Logger.

382 / 667

Word counter server ~1h

  1. Design and implement a line processing server application (i.e., a class that extends SimpleLineProcessingServer with a main()) that:
    • returns the number of words in the input line ll
    • exits with "bye"
    • listens on port 10000
  2. Test it using telnet
    • try removing flush() call

It likely won't work on repl.

383 / 667

Vector processing server

Protocol (upon connection):

  • client sends one real vector v\vec{v}
  • if v=0|\vec{v}|=0, server closes connection, otherwise sends as reply the processed line v=p(v)\vec{v}'=p(\vec{v})

Protocol detail: "send real vector" v\vec{v}

  1. send one int (4 bytes) n=vn=|\vec{v}|
  2. send nn doubles (8 bytes each)

Server:

  • listens on port 10000, handles 1 client at a time, never terminates
384 / 667

SimpleRealVectorProcessingServer: handleClient()

protected void handleClient(Socket socket) throws IOException {
ps.printf("[%1$tY-%1$tm-%1$td %1$tT] Connection from %2$s.%n", System.currentTimeMillis(), socket.getInetAddress());
int requestsCounter = 0;
DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
while (true) {
double[] input = readVector(dis);
if (input.length == 0) {
break;
}
writeVector(dos, process(input));
requestsCounter = requestsCounter + 1;
}
socket.close();
ps.printf("[%1$tY-%1$tm-%1$td %1$tT] Disconnection of %2$s after %3$d requests.%n", System.currentTimeMillis(), socket.getInetAddress(), requestsCounter);
}

Might extend SimpleLineProcessingServer.

385 / 667

Protocol: readVector(), writeVector()

private double[] readVector(DataInputStream dis) throws IOException {
int length = dis.readInt();
double[] values = new double[length];
for (int i = 0; i<values.length; i++) {
values[i] = dis.readDouble();
}
return values;
}
private void writeVector(DataOutputStream dos, double[] values) throws IOException {
dos.writeInt(values.length);
for (double value : values) {
dos.writeDouble(value);
}
dos.flush();
}
protected double[] process(double[] input) {
return input;
}

Might have defined EnhancedDataInputStream, EnhancedDataOutputStream:

  • with double[] readDoubleArray() and void writeDoubleArray(double[])
386 / 667

Message-oriented protocol

Previous examples implicitly defined a message-oriented protocol:

  • SimpleLineProcessingServer: a message is a text line
  • SimpleRealVectorProcessingServer: a message is vector encoded in a specified way

TCP is not message-oriented!

387 / 667

TCP: byte stream

TCP guarantees that:

  • every byte sent on one endpoint reaches the other endpoint
  • bytes arrive with the same order they have been sent
  • no duplication

but it does not guarantee that one write() of mm bytes on one endpoint corresponds to exactly one read() of mm bytes on the other endpoint!

In practice:

  • n1n \ge 1 reads might be needed to collect mm bytes
  • the last might include "other" bytes
  • eventually, mm bytes arrive
388 / 667

Example (of wrong solution)

Client CC:

byte[] oBuffer = /* ... */
os.write(oBuffer);

Assume oBuffer is ll \le 1024 at runtime.

Server SS connected to CC:

byte[] iBuffer = new byte[1024];
int n = is.read(iBuffer);
  • n might be something between 1 and ll
  • iBuffer might actually contain only a (leading) portion of what oBuffer contained on the other endpoint!
389 / 667

Even worse

Client CC (oBuffer1, oBuffer2 lengths are l1l_1, l2l_2):

os.write(oBuffer1);
os.write(oBuffer2);

Server SS connected to CC:

int n1 = is.read(iBuffer1);
int n2 = is.read(iBuffer2);

Possible outcomes:

  • n1 =l1=l_1, n2 =l2=l_2 (fluke!)
  • n1 l1\le l_1, n2 l2\le l_2
  • n1 >l1> l_1, n2 l2(\le l_2-( n1 l1)-l_1)
  • ...
390 / 667

Protocol with byte[] messages

You cannot assume that 1 read() gives 1 message!

You need a protocol for taking the byte[] resulting from the concatenation of 1+ read()s and dividing it in messages.

If your protocol works with higher level encoding, this job can be partially done by JDK classes. E.g.:

  • text lines: BufferedReader reads as many bytes for reaching the new line character
  • double array: the first 4 bytes (DataInputStream) specify how many other bytes need to be read (at least)
391 / 667

Multithreading

392 / 667

Get rid of Simple...

public void run() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
handleClient(socket);
}
}

accept() wait for connection requests and blocks until one client sends one.
handleClient() returns only upon completion of client handling.

\Downarrow

This server handles at most 1 client at a time.

393 / 667

One at a time

Possible other clients hang until the handled one disconnects.

  1. SS is on accept()
  2. Client C1C_1 connects to SS; SS is on handleClient()
  3. Client C2C_2 "connects" to SS; OS of SS takes the connection request and waits for SS to handle it
  4. ...
  5. C1C_1 says bye; SS goes to accept()

This server is not particularly useful!

  • that's why we called it Simple...Server
394 / 667

Goal

A server that can handle many client at a time.

public void run() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
handleClient(socket);
}
}

1+n1+n concurrent processes:

  • one "always" waiting for connection requests (accept())
  • nn, one for each client (handleClient())
395 / 667

Thread

Thread = execution flow

  • the JVM can execute more than one thread at a time
  • a thread can be blocked (e.g., waiting for input) while the others continue to run

Process vs. thread:

  • processes are concurrent flows managed by the OS
  • thread are concurrent flows managed by the JVM
  • both the JVM and the OS can exploit the hardware to actually run flows at the same time
    • (otherwise) they time-share the CPU
396 / 667

Thread

Package java.lang

Class Thread

java.lang.Object
java.lang.Thread

A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.

Mod. and Type Method Description
void start() Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.
void run() If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called; otherwise, this method does nothing and returns.

Two methods: we see the first.

  1. define a class TT that extends Thread and overrides run()
  2. invoke TT.start() (that will invoke run())
397 / 667

Example

public class SlowCounter extends Thread {
public void run() {
for (int i = 0; i < 10000; i++) {
if (i % 1000 == 0) {
System.out.printf("%s: %2d%n", toString(), i / 1000);
}
}
}
}
SlowCounter c1 = new SlowCounter();
SlowCounter c2 = new SlowCounter();
c1.start();
c2.start();
Thread[Thread-0,5,main]: 0
Thread[Thread-1,5,main]: 0
Thread[Thread-0,5,main]: 1
Thread[Thread-1,5,main]: 1
Thread[Thread-0,5,main]: 2
Thread[Thread-1,5,main]: 2
Thread[Thread-0,5,main]: 3
Thread[Thread-1,5,main]: 3
Thread[Thread-0,5,main]: 4
Thread[Thread-1,5,main]: 4
Thread[Thread-0,5,main]: 5
...
Thread[Thread-0,5,main]: 9
Thread[Thread-1,5,main]: 9

The JVM executes until the last thread has ended.

398 / 667

start()

SlowCounter c1 = new SlowCounter();
SlowCounter c2 = new SlowCounter();
c1.start();
c2.start();

When start() is invoked:

  1. the JVM starts a new thread, i.e., a new exeucution flow
  2. the execution flow of the caller immediately returns
  3. the new execution flow goes on with run()

Thread is the execution flow; Thread is the class.

399 / 667

start() vs. run()

Invoking run() does not cause the JVM to start a new thread!

SlowCounter c1 = new SlowCounter();
SlowCounter c2 = new SlowCounter();
c1.run();
c2.run();
Thread[Thread-0,5,main]: 0
Thread[Thread-0,5,main]: 1
Thread[Thread-0,5,main]: 2
Thread[Thread-0,5,main]: 3
Thread[Thread-0,5,main]: 4
Thread[Thread-0,5,main]: 5
Thread[Thread-0,5,main]: 6
Thread[Thread-0,5,main]: 7
Thread[Thread-0,5,main]: 8
Thread[Thread-0,5,main]: 9
Thread[Thread-1,5,main]: 0
Thread[Thread-1,5,main]: 1
Thread[Thread-1,5,main]: 2
Thread[Thread-1,5,main]: 3
Thread[Thread-1,5,main]: 4
Thread[Thread-1,5,main]: 5
Thread[Thread-1,5,main]: 6
Thread[Thread-1,5,main]: 7
Thread[Thread-1,5,main]: 8
Thread[Thread-1,5,main]: 9
400 / 667

Thread and memory

Each thread:

  • has its own stack
  • share the unique heap
401 / 667

Diagram

class SlowCounter extends Thread {
public void run() {
PrintStream ps = System.out;
for (int i = 0; i < 10; i++) {
ps.println(i);
}
}
}
public static void main(String[] args) {
SlowCounter c1 = new SlowCounter();
SlowCounter c2 = new SlowCounter();
c1.start();
c2.start();
}

At some point during the execution:

Stack T0Stack T1Stack T2Heapmain()start()start()run()run()int iint ipspsSlowCounterSlowCounterPrintStreamc1c2

out is static in System!

402 / 667

Good practice

Assume CC extends Thread is the class that does the job concurrently.

Put in CC constructor all and only the things that has to be shared.

403 / 667

Multithreaded LineProcessingServer

public class LineProcessingServer {
private final int port;
private final String quitCommand;
public LineProcessingServer(int port, String quitCommand) {
this.port = port;
this.quitCommand = quitCommand;
}
public void run() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
ClientHandler clientHandler = new ClientHandler(socket, quitCommand);
clientHandler.start();
}
}
}
404 / 667

ClientHandler

public class ClientHandler extends Thread {
private final Socket socket;
private final String quitCommand;
public ClientHandler(Socket socket, String quitCommand) {
this.socket = socket;
this.quitCommand = quitCommand;
}
public void run() {
/* ... */
}
protected String process(String input) {
return input;
}
}

Note: run() signature cannot be modified

  • cannot add throws IOException
405 / 667

ClientHandler.run()

public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String line = br.readLine();
if (line.equals(quitCommand)) {
socket.close();
break;
}
bw.write(process(line) + System.lineSeparator());
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Basically the same as before...

  • with a lot of try, catch, finally... because throws cannot be used; we'll see
406 / 667

"Usage" of LineProcessingServer

  1. Define a class HH that:
    • extends ClientHandler
    • overrides process()
  2. Define a class SS that:
    • extends (or "is like") LineProcessingServer
    • instantiates HH

A bit cumbersome:

  • word counter: define H1H_1 and S1S_1
  • uppercaser: define H2H_2 and S2S_2
407 / 667

Concurrent execution

The same method of the same object can be executed at the same time by different threads!

Idea:

  • the server class SS specify the application details:
    • how to process one request
    • what is the quit command
  • ClientHandler just knows how to handle one client
    • i.e., manage the request-response interaction
    • delegates to SS for actual server behavior
408 / 667

Better LineProcessingServer

public class LineProcessingServer {
private final int port;
private final String quitCommand;
public LineProcessingServer(int port, String quitCommand) {
this.port = port;
this.quitCommand = quitCommand;
}
public void run() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
ClientHandler clientHandler = new ClientHandler(socket, this);
clientHandler.start();
}
}
public String process(String input) {
return input;
}
public String getQuitCommand() {
return quitCommand;
}
}
409 / 667

Agnostic ClientHandler

public class ClientHandler extends Thread {
private final Socket socket;
private final LineProcessingServer server;
public ClientHandler(Socket socket, LineProcessingServer server) {
this.socket = socket;
this.server = server;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String line = br.readLine();
if (line.equals(server.getQuitCommand())) {
socket.close();
break;
}
bw.write(server.process(line) + System.lineSeparator());
bw.flush();
}
} catch (IOException e) { /* ... */
} finally { /* ... */ }
}
}
410 / 667

"Usage" of better LineProcessingServer

  1. Define a class SS that:
    • extends LineProcessingServer and overrides process()

Elegant!

Note:

  • at runtime, many threads may execute process() at the same time

Possible source of big problems!

411 / 667

Thread scheduling

The JVM:

  • knows the set TT of existing threads and at which bytecode instruction they are
  • knows the subset TTT' \subseteq T of not blocked threads
  • knows the set CC of available cores
    • hardware or virtualized, provided by the OS

At each time step:

  • for each core in CC
    • select one thread in TT' and executes the next bytecode instruction
412 / 667

Thread interference

\Rightarrow threads execute concurrenly only apparently!

Suppose:

  • only one core (C=1|C|=1)
  • thread t1t_1 next instructions: a1,b1,c1a_1, b_1, c_1
  • thread t2t_2 next instructions: a2,b2,c2a_2, b_2, c_2

Some possible actual executions:

  • a1,b1,c1,a2,b2,c2a_1, b_1, c_1, a_2, b_2, c_2
  • a1,a2,b1,b2,c1,c2a_1, a_2, b_1, b_2, c_1, c_2
  • a2,b2,a1,c2,b1,c1a_2, b_2, a_1, c_2, b_1, c_1
  • ...

Is a2,b1,a1,c2,b2,c1a_2, b_1, a_1, c_2, b_2, c_1 possible?

413 / 667

Bytecode and Java statements

One Java statement:

System.out.printf("a+b=%d%n", a + b);

1\rightarrow \gg 1 bytecode instructions!

  • in a multithreaded execution, this statement execution might be interleaved with other statements execution

Even worse!

  • exact sequence of execution is not predictable!
  • source of very bad bugs:
    • not easily reproducible
    • difficult to spot
414 / 667

Counter example

public class Counter {
private int c = 0;
public int incAndGet() {
c = c + 1;
return c;
}
public int decAndGet() {
c = c - 1;
return c;
}
}
Counter c = new Counter();
(new IncThread(c)).start();
(new DecThread(c)).start();
public class IncThread extends Thread {
private final Counter c;
public IncThread(Counter c) {
this.c = c;
}
public void run() {
System.out.print(c.incAndGet() + " ");
}
}
public class DecThread extends Thread {
/* ... */
}

Possible outputs:

  • 1 0 ("lucky")
  • 0 0
  • -1 0

Eventually c=0; meanwhile could be -1, 0, 1.

415 / 667

synchronized

Methods defined with the synchronized modifier cannot be executed on the same object by more than one thread at the same time.

  • from the point of view of the executing thread, the method execution is atomic

Atomicity is guaranteed just on the method!

416 / 667

AtomicCounter

public class AtomicCounter {
private int c = 0;
public synchronized int incAndGet() {
c = c + 1;
return c;
}
public synchronized int decAndGet() {
c = c - 1;
return c;
}
}
Counter c = new Counter();
(new IncThread()).start();
(new DecThread()).start();
public class IncThread extends Thread {
private final Counter c;
public CounterThread(Counter c) {
this.c = c;
}
public void run() {
System.out.print(c.inc() + " ");
}
}
public class DecThread extends Thread {
/* ... */
}

Possible outputs:

  • 1 0
  • -1 0

0 0 is no more possible!

417 / 667

Thread-safe classes

A method invocation outcome in case of multiple threads:

  • may be not specified (i.e., not predictable)
  • may cause an exception to be thrown

The method/class is said to be not thread-safe.

Since atomicity is a common requirement, the JDK provides:

418 / 667

synchronized block

synchronized can be applied to code blocks, instead of to entire methods:

public class CounterThread extends Thread {
public void run() {
Counter c = /* ... */
for (int i = 0; i < 10; i++) {
synchronized (c) {
System.out.print(c.get() + " -> ");
c.inc();
System.out.println(c.get());
}
}
}
}

The synchronized block can be executed on the same Counter object (c, here) by at most one thread at a time.

What's the output with/without synchronized with two threads on the same counter (i.e., same instance)?

What if c is instantiated with new Counter() in run()?

419 / 667

Exceptions

420 / 667

The old way

The old way of dealing with errors:

  • the language does not define the notion of error
  • each software establishes a convention for dealing with errors

Example, a function:

  1. detects an error condition
  2. propagates the information to the caller through a "special" return value
421 / 667

Examples: C fwrite

Syntax:

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

Description:

This function writes size * nmemb bytes from ptr to stream.

Return value:

The number of items of size nmemb written or -1 on error.

"or -1 on error" is the convention

422 / 667

C atoi

Syntax:

#include <stlib.h>
int atoi(const char *str);

Description:

Converts as much of the string as possible to an equivalent integer value.

Return value:

The equivalent value, or zero if the string does not represent a number.

"or zero [...] if [...]" is the convention

  • returned value is in the domain of legit values!
  • caller has to explicitly check for errors
423 / 667

Detecting vs. handling

"caller has to explicitly check for errors"

What if the developer of the caller code does not check?

  1. the error exists
    • indeed, it is detected by the called code
  2. the caller gets a value that is wrong, but goes on
  3. some time later, a bigger error occurs, but the cause is hardly detectable

The problem is that the detected error is not handled!
More broadly, the problem is that the language permits the developer to forget about error handling.

424 / 667

Sketch of possible solution

The language:

  • considers the error as a key concept
  • requires to define two kinds of flows in the source code:
    • normal: when no error occurs
    • anomalous: when an error occurs
      • when in anomalous flow, there is always one error!

The developer uses the anomalous flow code to handle the error.

The compiler notifies the developer if she/he forgets to define the anomalous flow when needed.

425 / 667

Normal \leftrightarrow anomalous

Suppose:

  • a method mm called by a method mm'
  • at some point in mm an error occurs

If mm has an anomalous flow:

  1. mm switches to anomalous flow and, when completed, switches back to normal flow
  2. eventually execution goes back to mm' in normal flow

Otherwise:

  1. mm returns immediately to mm' as an error
  2. an error occurs in mm' where it called mm
426 / 667

Error propagation

If an error occurs in mm:

  • and mm has not anomalous flow
  • and the caller mm' has no anomalous flow
  • and the caller mm'' has no anomalous flow
  • ...

then the execution halts immediately.

\Rightarrow impossible that an error goes unhandled causing (bigger) errors later!

427 / 667

Errors in Java

Language:

  • what is error in Java?
  • how to define the normal and anomalous flow?
  • how to switch to anomalous flow?

Design:

  • when to handle an error? (and when to propagate?)
  • how to handle an error?
  • when to "create" an error?
428 / 667

Exception

What is an error in Java?

Package java.lang

Class Exception

java.lang.Object
java.lang.Throwable
java.lang.Exception

The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.

  • errors (as implicitly defined before) are "conditions that a reasonable application might want to catch" \rightarrow anomalous events that result in conditions that should be taken with care by the developer
    • exceptions to the normality
  • can (and should) be extended to represent specific kinds of exceptions to the normality
429 / 667

Subclasses of Exception

One:

Package java.net

Class UnknownHostException

java.lang.Object
java.lang.Throwable
java.lang.Exception
java.io.IOException
java.net.UnknownHostException

Thrown to indicate that the IP address of a host could not be determined.

Another one:

Package javax.sound.sampled

Class UnsupportedAudioFileException

java.lang.Object
java.lang.Throwable
java.lang.Exception
javax.sound.sampled.UnsupportedAudioFileException

An UnsupportedAudioFileException is an exception indicating that an operation failed because a file did not contain valid data of a recognized file type and format.

430 / 667

Message and cause

Constructor Description
Exception() Constructs a new exception with null as its detail message.
Exception​(String message) Constructs a new exception with the specified detail message.
Exception​(String message, Throwable cause) Constructs a new exception with the specified detail message and cause.

An Exception may have a message and a cause (another Exception):

  • set by constructor
431 / 667

try-catch

How to define the normal and anomalous flow?

doThings() {
try {
/* N */
} catch (UnknownHostException e) {
/* A1 */
} catch (InvalidTypeException e) {
/* A2 */
}
}
  • NN: normal flow
  • A1A_1: anomalous flow if UnknownHostException occurs
  • A2A_2: anomalous flow if InvalidTypeException occurs

If exception ee of type EE occurs while executing the normal flow, the JVM checks (in order) if one catch block exists for EE:

  • if found, execution goes to the 1st stament of the catch block
    • and e references the object ee of type EE
  • otherwise, execution goes to the caller of doThings() carrying the exception ee
432 / 667

Enforcement of exception handling

The compiler:

  • given a statement ss, knows exactly if it can generate one or more exceptions and their types
  • compiles a block of code C=(s1,s2,)C = (s_1, s_2 , \dots) only if either:
    • no exceptions can be generatedby any statement siCs_i \in C
    • or CC is in a try block for which there is a catch for any possible exception generated by CC (with inheritance) (exception catched)
    • or the developer explicitly defined the method containing CC as a method that can generate the exceptions possibly generated by CC (exception declared)
433 / 667

throws

How does the compiler if a statement generate an exception?
How does the developer specifies that a method can generate an exception?

public String getLastName(String fullName) throws MalformedNameException {
/* ... */
}

The developer declares that getLastName() might throw an exception of type MalformedNameException (throws clause)

A method may be declared to throw more than one exceptions:

public void doHardJob() throws TooHardException, TooLazyException {
/* ... */
}
434 / 667

throws and inheritance

throws clause is part of the method signature.

If a class CC' extends a class CC with a method mm, CC' cannot "increase" the throws clause of mm.

public class Worker {
void work() {
/* ... */
}
}
public class LazyWorker extends Worker {
void work() throws TooHardException {
/* ... */
}
}

Code using a Worker expects work() to work, regardless of the actual object being a LazyWorker.

Declared throws clauses may be instead "decreased".

435 / 667

Stricter throws clause

Ok! TirelessWorker might throw, but never throws TooHardException.

public class Worker {
void work() throws TooHardException {
/* ... */
}
}
public class TirelessWorker extends Worker {
void work() {
/* ... */
}
}

Ok! PreciseWorker might throw Exceptions, but throws only ForbiddenByUnionsException.

public class Worker {
void work() throws Exception {
/* ... */
}
}
public class PreciseWorker extends Worker {
void work() throws ForbiddenByUnionsException {
/* ... */
}
}
  • assuming ForbiddenByUnionsException extends Exception
436 / 667

throw

How to switch to anomalous flow? (i.e., how to throw an exception?)

public String getLastName(String fullName) throws MalformedNameException {
String[] pieces = fullName.split(" ");
if (pieces.length == 0) {
throw new MalformedNameException("Empty name!");
}
return pieces[pieces.length-1];
}

If/when the execution reaches the throw ee statement, the anomalous flows starts with the exception ee.

  • in this case, there is no try-catch, hence the method is defined with throws EE (with ee of type EE, or of subclass of EE)
437 / 667

throws vs. throw

throw is an action:

  • do it now!

throws is part of a definition:

  • the method may do it
438 / 667

Compiler checks

The compiler checks if exception is absent, catched, or declared.

It also considers:

  • actual need for catching or declaration
  • inheritance among Exceptions
439 / 667

Examples: not catched, not declared

public String getLastName(String fullName) throws MalformedNameException {
/* ... */
}
public void showName(String fullName) {
String lastName = getLastName(fullName);
System.out.println(lastName);
}

Does not compile because:

  • getLastName() throws MalformedNameException and it is not catched, nor declared.
440 / 667

Not thrown, but declared

public void showName(String fullName) throws MalformedNameException {
System.out.println(fullName);
}

Compiles, but IDEs usually warn that code does not throw MalformedNameException

441 / 667

Declared with superclass

public void showName(String fullName) throws Exception, MalformedNameException {
String[] pieces = fullName.split(" ");
if (pieces.length == 0) {
throw new MalformedNameException("Empty name!");
}
return pieces[pieces.length-1];
}

Compiles, but IDEs usually warn that declaring MalformedNameException is useless because a more general exception can be thrown.

442 / 667

Not thrown, but catched

public void showName(String fullName) {
try {
System.out.println(fullName);
} catch (MalformedNameException e) {
/* ... */
}
}

Does not compile because:

  • code does not throw MalformedNameException
443 / 667

Catched by superclass

public void showName(String fullName) {
try {
System.out.println(fullName);
} catch (Exception e) {
/* ... */
} catch (MalformedNameException e) {
/* ... */
}
}

Does not compile because:

  • first catch catches "everything", second one cannot be triggered
444 / 667

finally

try {
/* normal flow */
} catch (Exception e) {
/* anomalous flow */
} finally {
/* finally flow */
}

Allows to define a trailing execution flow that:

  • is always executed
  • does not affect the normal/anomalous state
445 / 667

Example

public void doThings(int n) {
try {
System.out.print("n1");
if (n == 0) {
throw new Exception();
}
System.out.print("n2");
} catch (Exception e) {
System.out.print("a");
} finally {
System.out.print("f");
}
System.out.println("n3");
}

Invoked with n=1 (no exception thrown):

  • n1 n2 f n3

Invoked with n=0 (Exception thrown):

  • n1 a f n3
446 / 667

finally without catch

finally does not affect the normal/anomalous state!

public void doThings(int n)
throws Exception {
try {
System.out.print("n1");
if (n == 0) {
throw new Exception();
}
System.out.print("n2");
} finally {
System.out.print("f");
}
System.out.println("n3");
}
public void externalDoThings(int n) {
try {
System.out.println("en1");
doThings(n);
System.out.println("en2");
} catch (Exception e) {
System.out.println("ea");
}
System.out.println("en3");
}

externalDoThings(1) invoked:

  • en1 n1 n2 f n3 en2 en3
  • remains in normal state when exiting from doThings()

externalDoThings(0) invoked:

  • en1 n1 f ea en3
  • remains in anomalous state when exiting from doThings()
447 / 667

Anomalous is relative

The condition of a flow of being anomalous is relative, not absolute:

  • given flow ff, ff' is anomalous with respect to ff if an exception ee was thrown in ff
try {
throw new Exception("problem"); // A
} catch (Exception e) {
try {
throw new Exception("further problem"); // B
} catch (Exception furtherE) {
/* ... C */
}
}

B is anomalous wrt A; C is anomalous wrt B

  • in C, two exceptions exists: e and furtherE
448 / 667

Unchecked exceptions, throwables

Package java.lang

Class Exception

java.lang.Object
java.lang.Throwable
java.lang.Exception

Package java.lang

Class Error

java.lang.Object
java.lang.Throwable
java.lang.Error

Package java.lang

Class RuntimeException

java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException

The language specifies that:

  • only Throwables (and subclasses) can be argument of throw, throws and catch
  • every thrown Exception (and subclasses) has to be catched or declared, unless it is a RuntimeException (or subclasses)
    • RuntimeExceptions are unchecked exceptions

Throwable is also extended by Error:

  • Errors (and subclasses) are unchecked exceptions too
449 / 667

RuntimeException

Package java.lang

Class RuntimeException

java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException

RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine.

RuntimeException and its subclasses are unchecked exceptions. Unchecked exceptions do not need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.

Basically, can be thrown anywhere.

They can be catched, but you are not compelled to.

450 / 667

NullPointerException

The queen of unchecked exceptions!

Package java.lang

Class NullPointerException

java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.NullPointerException

Thrown when an application attempts to use null in a case where an object is required. These include:

  • Calling the instance method of a null object.
  • Accessing or modifying the field of a null object.
  • Taking the length of null as if it were an array.
  • Accessing or modifying the slots of null as if it were an array.
  • Throwing null as if it were a Throwable value.

Applications should throw instances of this class to indicate other illegal uses of the null object.

Examples (common cases):

String s = //.., rets null
s = s.toUpperCase();
int[] a = //.., rets null
int l = a.length;
int[] a = //.., rets null
a[0] = 0;
No throw, it is the JVM itself that generates the NullPointerException (in these examples).
451 / 667

Package java.lang

Class ArrayIndexOutOfBoundsException

java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException

Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.

452 / 667

Errors

Package java.lang

Class Error

java.lang.Object
java.lang.Throwable
java.lang.Error

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it.

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur. That is, Error and its subclasses are regarded as unchecked exceptions for the purposes of compile-time checking of exceptions.

Exception: "conditions that a reasonable application might want to catch"

  • come on, you can fix it!

Error: "problems that a reasonable application should not try to catch"

  • you are doomed, it's too late!
453 / 667

No more heap:

Package java.lang

Class OutOfMemoryError

java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.VirtualMachineError
java.lang.OutOfMemoryError

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

No more stack (too much recursion!):

Package java.lang

Class StackOverflowError

java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.VirtualMachineError
java.lang.StackOverflowError

Thrown when a stack overflow occurs because an application recurses too deeply.

454 / 667

Throwable (and subclasses) usage

Constructors:

Constructor Description
Throwable() Constructs a new throwable with null as its detail message.
Throwable(String message) Constructs a new throwable with the specified detail message.
Throwable(String message, Throwable cause) Constructs a new throwable with the specified detail message and cause.

Methods (key ones):

Mod. and Type Method Description
String getMessage() Returns the detail message string of this throwable.
void printStackTrace() Prints this throwable and its backtrace to the standard error stream.
void printStackTrace​(PrintStream s) Prints this throwable and its backtrace to the specified print stream.
void printStackTrace​(PrintWriter s) Prints this throwable and its backtrace to the specified print writer.

printStackTrace() (= printStackTrace(System.err)):

  • super useful for debugging
  • should not be used in production-level software
    • most IDEs warn if used
455 / 667

Stack trace

public static void dec(int n)
throws Exception {
if (n == 0) {
throw new Exception("surprise!");
}
dec(n - 1);
}
public static void main(String[] args) {
try {
dec(5);
} catch (Exception e) {
e.printStackTrace();
}
}
java.lang.Exception: surprise!
at it.units.erallab.hmsrobots.objects.Robot.dec(Test.java:185)
at it.units.erallab.hmsrobots.objects.Robot.dec(Test.java:185)
at it.units.erallab.hmsrobots.objects.Robot.dec(Test.java:185)
at it.units.erallab.hmsrobots.objects.Robot.dec(Test.java:185)
at it.units.erallab.hmsrobots.objects.Robot.dec(Test.java:185)
at it.units.erallab.hmsrobots.objects.Robot.dec(Test.java:185)
at it.units.erallab.hmsrobots.objects.Robot.main(Test.java:191)

Shows method, source code file, and source code line of the statement in which the flow switched to anomalous

  • i.e., where throw is, when not a JVM thrown throwable
456 / 667

Errors in Java

Language:

  • what is error in Java?
    • Throwable, Error, Exception, RuntimeException
  • how to define the normal and anomalous flow?
    • try-catch-finally
  • how to switch to anomalous flow?
    • throw

Design:

  • when to handle an error? (and when to propagate?)
  • how to handle an error?
  • when to "create" an error?
457 / 667

How to design with exceptions

Design:

  • when to handle an error? (and when to propagate?)
  • how to handle an error?
  • when to "create" an error?

No, one-fits-all answers.

  • some basic good practice guidelines
  • many finer guidelines
458 / 667

Handle or propagate?

When to handle an error?
I.e., an exception of type EE in a method mm.

Key idea:

  • if the developer of mm knows how to handle the anomalous condition related to EE, she/he should handle it!

E.g.: String getWebPageTitleIfAny(String url), with MalformedURLException

  • bad URL? no page, no title (IfAny): catch and return ""

E.g.: Configuration loadFromFile(String fileName) with FileNotFoundException

  • no file? I don't know what Configuration to return: propagate
459 / 667

Design by contract

A methodology for software design: here just in a nutshell.

For each method define:

  • preconditions met by input
    • responsability of the caller (input)
  • postconditions met by output
    • responsability of the called (output)

Then, the method should return abruptly in anomalous state (i.e., throw or propagate an exception, in Java) if and only if:

  • preconditions are not met
  • postconditions cannot be met despite preconditions being met
    • "cannot" means for serious unsolvable problems

Some IDEs help the developer in adopting this methodology.

460 / 667

How to handle? When to throw?

How to handle an error?

  • in the way you know to handle it to meet postconditions

When to "create" an error?
(beyond propagation)

  • when preconditions are not met
  • when postcondition cannot
public String getLastName(String fullName) throws MalformedNameException {
String[] pieces = fullName.split(" ");
if (pieces.length == 0) {
throw new MalformedNameException("Empty name!");
}
return pieces[pieces.length-1];
}
461 / 667

Creation vs. propagation

Good practice (in general): hide inner implementation

If component/library/sofware SS finds an exception EE' related to specific component/library/sofware SS' used in SS, it should not propagate it, but should instead create a new exception EE related to SS.

Code using SS should not be needed to know SS'.

462 / 667

Common mistake: fake handling

Never catch without handling (just to make the code compile)!

try {
doRiskyThing();
} catch (BadThingHappenedException e) {
// ignore
}

Never means never:

  • "I put an empty catch, I'll fix it later"
    • forgot, huge problems found much later, hardly
    • common when working with Threads

If you don't know how to handle, you shouldn't catch.

  • If you need to catch it, put, at least, a e.printStackTrace().
463 / 667

Threads and missed handling

run() cannot be redefined with a throws clause:
\Rightarrow every exception has to be handled

  • even if you don't know how

If no methods handles the exception (e.g., NullPointerException), the thread is interrupted:

  • if the main thread, the application stops
  • otherwise, just the thread silently stops
464 / 667

Common mistake: lazy catch all

try {
doRiskyThing();
} catch (Exception e) {
/* handling code for both */
}
public void doRiskyThing()
throws BadFooException, BadBarException {
/* ... */
}

The (lazy) developer handles BadFooException and BadBarException in the same way.

  • but the catch block might be executed also in other conditions, e.g., NullPointerException

Correct way:

try {
doRiskyThing();
} catch (BadFooException e) {
/* handling code for foo */
} catch (BadBarException e) {
/* handling code for bar */
}
465 / 667

Multicatch

From Java 7, lazyness is no more a motivation:

try {
doRiskyThing();
} catch (BadFooException|BadBarException e) {
/* handling code for both */
}

At compile-time, e is of the most specific supertype of both BadFooException and BadBarException.

Just a shorthand.

466 / 667

Summarizing

It's a matter of responsability:

  • if preconditions are met and postconditions can be met, it's called responsability \rightarrow handle
  • if preconditions are not met, it's caller responsability \rightarrow propagate
  • if called cannot meet postconditions, for serious problems beyond its responsability \rightarrow propagate

In practice:

  • always log, if forced to handle
  • hide internals
467 / 667

Exceptions and OS resources

468 / 667

OS resources

Premise 1: typical workflow

  1. open: "OS, prepare to let me use that resource"
  2. use: "OS, do this thing on that resource"
  3. close: "OS, I've done"

Premise 2: anomalous outcome

  • any operation on OS resources (in particular open and use) may end abruptly
    • JDK components reflect this by throwing exceptions (e.g., streams)
469 / 667

Closing

Close on OSs resource is remarkably important!

Most OSs release resources automatically when a process terminate.

They do not release resources when the process is running.
\Rightarrow a long-running process has the responsability or closing its resources.

  • if it "forgets" to, eventually the OS will kill the process
    • "eventually" might mean after hours, days, months of execution: hardly predictable
470 / 667

Always close!

Always close!

"Always" means also in anomalous flow!

471 / 667

Closing with exception: suboptimal solution

try {
Resource r = open();
r.use();
r.close();
} catch (AException e) {
log(e);
r.close();
} catch (BException e) {
log(e);
r.close();
}

Problems:

  • what if ExceptionC is thrown (e.g., NullPointerException)?
  • r is not defined in catch blocks
  • (minor) close code repeated 3 times

open(), use(), close() are not actual method invokations; they are placeholders.

472 / 667

Better: closing in finally

Resource r;
try {
r = open();
r.use();
} catch (AException e) {
log(e);
} catch (BException e) {
log(e);
} finally {
r.close();
}

Nice: finally block is always (by definition) executed!

Problems:

  • what if the exception is thrown in open()?
    • r is not initialized; r.close() will throw a NullPointerException!
  • (minor) so verbose!
473 / 667

Closing: the correct (but old) way

Resource r;
try {
r = open();
r.use();
} catch (AException e) {
log(e);
} catch (BException e) {
log(e);
} finally {
if (r != null) {
r.close();
}
}

Ok, but still verbose...

474 / 667

The new correct way

try (Resource r = open()) {
r.use();
} catch (AException e) {
log(e);
} catch (BException e) {
log(e);
}

try-with-resources

  • since Java 7.

try gets a declaration argument where one or more resources are:

  • declared (Resource r)
  • and instantiated (r = open())

When exiting the try block, r is closed (if not null):

  • before entering the catch block, if any
    • sort of anticipated finally
  • without affecting the normal/anomalous state
475 / 667

Multiple resources

byte[] data = new byte[100];
try (
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile)
) {
while (true) {
int nOfReadBytes = fis.read(data);
if (nOfReadBytes == -1) {
break;
}
fos.write(data, 0, nOfReadBytes);
}
} catch (IOException e) {
System.err.printf("Cannot copy due to %s", e);
}

Resources are closed in the reversed order they were opened.

476 / 667

Without catch

public void log(String msg, String filePath) throws IOException {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filePath))) {
bw.append(msg + System.lineSeparator());
}
}

The BufferedWriter is always closed, but exceptions are not handled (here):

  • the compiler requires to handle or add the throws clause
  • normal/anomalous state is not affected
477 / 667

What is a resource?

Every class that implements the AutoCloseable interface.

(We still do not know interfaces, but...)
In practice, every class with a close() method.

  • all the I/O classes: streams, Readers, Writers, ...
478 / 667

AutoCloseable

479 / 667

Robust servers

480 / 667

Goal

Protocol (upon connection):

  • client sends one text line ll
  • if l=lquitl=l_\text{quit}, server closes connection, otherwise replies with processed line l=p(l)l'=p(l)

Server:

  • listens on port nportn_\text{port}
  • handles many client at a time
  • never terminates (really!)
    • bad things can happen: e.g., client closes the connection
    • correctly releases OS resources
  • designed to be extended
  • p:p: String \to String, lquitl_\text{quit}, port number are parameters
481 / 667

RobustLineProcessingServer

public class RobustLineProcessingServer extends LineProcessingServer {
public RobustLineProcessingServer(int port, String quitCommand) {
super(port, quitCommand);
}
public void run() throws IOException { /* ... */ }
}
  • extends LineProcessingServer
    • inherits fields (make protected in superclass)
    • inherits process() and getQuitCommand()
482 / 667

run()

public void run() throws IOException {
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
Socket socket;
try {
socket = serverSocket.accept();
ClientHandler clientHandler = new RobustClientHandler(socket, this);
clientHandler.start();
} catch (IOException e) {
System.err.printf("Cannot accept connection due to %s", e);
}
}
}
}
  • Constructor ServerSocket() may throw an IOException, but we do not know how to manage it (cannot meet postcondition "stay alive"):
    \Rightarrow propagate
  • accept() may throw an IOException: server still need to stay alive: \Rightarrow handle
    • nothing special, unfortunate client, just log
    • no try-with-resources: close() will be managed by ClientHandler
483 / 667

RobustClientHandler

public class RobustClientHandler extends ClientHandler {
public RobustClientHandler(Socket socket, LineProcessingServer lineProcessingServer) {
super(socket, lineProcessingServer);
}
public void run() { /* ... */ }
}
  • extends ClientHandler
  • does not need to know that the server is the robust version
    • inheritance
484 / 667

run()

public void run() {
try (socket) {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String line = br.readLine();
if (line == null) {
System.err.println("Client abruptly closed connection");
break;
}
if (line.equals(lineProcessingServer.getQuitCommand())) {
break;
}
bw.write(lineProcessingServer.process(line) + System.lineSeparator());
bw.flush();
}
} catch (IOException e) {
System.err.printf("IO error: %s", e);
}
}
  • readLine() returns null if EOS reached, that happens when the client closes the connection
    • line.equals() would have thrown a NullPointerException what if not handled?
  • try (socket): possible since Java 9
485 / 667

IOException and network

public void run() {
try (socket) {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String line = br.readLine();
if (line == null) {
System.err.println("Client abruptly closed connection");
break;
}
if (line.equals(lineProcessingServer.getQuitCommand())) {
break;
}
bw.write(lineProcessingServer.process(line) + System.lineSeparator());
bw.flush();
}
} catch (IOException e) {
System.err.printf("IO error: %s", e);
}
}

Suppose we get an IOException at n+1n+1-th readLine():

  • has the last nn-th message arrived?
  • have all the previous 1,,n1, \dots, n messages arrived?
486 / 667

TCP guarantees

TCP guarantees that:

  • every byte sent on one endpoint reaches the other endpoint
  • bytes arrive with the same order they have been sent
  • no duplication

... if no problems occur.

Otherwise, OS notifies the application (here, the JVM) that TCP failed.

487 / 667

Message-oriented protocol

Client CC: write request, read response.
Server SS: read request, write response.

Iteration in detail:

  • write request (1)
    • COSCC \rightarrow \text{OS}_C
    • OSCdeviceC\text{OS}_C \rightarrow \text{device}_C
    • deviceCdeviceS\text{device}_C \rightarrow \text{device}_S
    • deviceSOSS\text{device}_S \rightarrow \text{OS}_S
  • read request (2)
    • OSSS\text{OS}_S \rightarrow S
  • write response (3)
    • SOSCS \rightarrow \text{OS}_C
    • OSSdeviceS\text{OS}_S \rightarrow \text{device}_S
    • deviceSdeviceC\text{device}_S \rightarrow \text{device}_C
    • deviceCOSC\text{device}_C \rightarrow \text{OS}_C
  • read response (4)
    • OSCC\text{OS}_C \rightarrow C
488 / 667

Exception in readLine()

  • write request (1)
    • COSCC \rightarrow \text{OS}_C
    • OSCdeviceC\text{OS}_C \rightarrow \text{device}_C
    • deviceCdeviceS\text{device}_C \rightarrow \text{device}_S
    • deviceSOSS\text{device}_S \rightarrow \text{OS}_S
  • read request (2)
    • OSSS\text{OS}_S \rightarrow S IOException!
  • write response (3)
    • SOSSS \rightarrow \text{OS}_S (last ok call)
    • OSSdeviceS\text{OS}_S \rightarrow \text{device}_S
    • deviceSdeviceC\text{device}_S \rightarrow \text{device}_C
    • deviceCOSC\text{device}_C \rightarrow \text{OS}_C
  • read response (4)
    • OSCC\text{OS}_C \rightarrow C

CC never sends a request before consuming a response: hence if readLine() worked with the nn-th request, CC received the n1n-1-th response.

  • not possible to say when, where, who (client, network) failed after that
  • nn-th response might have arrived or not
489 / 667

Even worse!

Pipelined message-oriented protocol:

Client CC: write kk requests, read kk responses.
Server SS: read hh requests, write hh responses.

490 / 667

How to manage?

It depends on the application.

Request examples:

  1. "show my balance"
  2. "buy a new pots and pans set"
  3. "new hearth available"

For 1, CC can simply send again the request.
For 2 and 3, much harder.

491 / 667

More on

class loading and documentation

492 / 667

Beyond compilation

public class Main {
public static void main(String[] args) {
Greeter greeter = new Greeter("hi");
greeter.greet("Eric");
}
}

We know how javac compiled this code (Main.java), and what checks it performed while compiling.

How does java execute the corresponding Main.class?

493 / 667

.class content

A .class contains:

  • methods code
  • constructors code
  • fields name and type
  • ...

When JVM starts executing Main.class:

  • it "has" the code for Main.main()
  • it does not "have" (initially) the code for Greeter(), Greeter.greet()
494 / 667

Class loading on demand

The JVM needs to use a .class file when:

  • a constructor is executed
  • a method is executed (static or not)
  • a static field is accessed

Whenever the JVM needs to use a .class file:

  • it looks for it in memory
  • if not present, it looks for it in the file system and loads it

Hence, a .class is loaded in memory at the first time:

  • a constructor is executed
  • a static method is executed (e.g., main())
  • a static field is accessed
495 / 667

Example

public class Main {
public static void main(String[] args) {
Greeter greeter = new Greeter("hi");
greeter.greet("Eric");
}
}
public class Greeter {
private final String formula;
public Greeter(String formula) {
this.formula = formula;
}
public void greet(String name) {
System.out.println(
formula + " " + name
);
}
}
eric@cpu:~$ java Main
hi Eric

Sequence of loaded classes (approximation):

  1. String.class
  2. Object.class
  3. Main.class
  4. Greeter.class
  5. System.class
  6. ...
  7. PrintStream.class
  8. ...

Recall: every constructor starts by invoking the constructor of the superclass (possibly implicitly).

496 / 667

Consistency check

Static consistency:

  • checked by javac at compile time
  • is every usage of field/method/constructor legit?

Dynamic consistency:

  • checked by java at runtime
  • is every usage of field/method/constructor still legit?

Both are checked using .class files!

  • while compiling Main.java, javac checks Greeter usages using Greeter.class
  • while executing Main.class, java checks Greeter usages using Greeter.class
497 / 667

.class content

A .class contains:

  • methods code and signature
  • constructors code and signature
  • fields name and type
  • ...

What if static consistency not verified?

  • the compilation fails

What if dynamic consistency not verified?

  • .class not found \rightarrow ClassNotFoundException
  • method/constructor not found in .class \rightarrow NoSuchMethodException
  • field not found in .class \rightarrow NoSuchFieldException
498 / 667

Autocompilation

Suppose you wrote Main.java and Greeter.java.

While compiling Main.java to Main.class, javac needs Greeter.class:

  • if not found, but Greeter.java is found, javac automatically compiles Greeter.java to Greeter.class (and places it in the proper location)

Where does javac look for other .java files?

Where do javac/java look for .class files?

499 / 667

Where are .java files?

.java files must be organized in directories corresponding to packages.

Suppose source root directory is ~/java-projects/APFinalProject/src/main/java common choice of many IDEs, then:

  • it.units.approject.Main must be a Main.java in ~/java-projects/APFinalProject/src/main/java/it/units/approject
  • it.units.approject.util.Parser must be a Parser.java in ~/java-projects/APFinalProject/src/main/java/it/units/approject/util
  • ...

IDEs take care of managing proper location of all files, including sources. At least for most common cases.

500 / 667

Where are .class files?

Many options:

  1. in the JDK directories (for JDK classes)
  2. in the same directory of the corresponding .java files (old way)
  3. in a directory tree equal to the .java one (i.e., based on packages) but rooted elsewhere (e.g., at ~/java-projects/APFinalProject/target/) (common easy way)
  4. in a .jar file placed properly

Multiple options can hold together, usually for different kind of classes:

  • 3 for "this" software
  • 4 for other software used by "this" software
501 / 667

Building

Placing of .class is part of a complex build process that is managed by one or more of:

  • the IDE
  • build-automation tools (e.g., for Java, Maven or Gradle)
    • super powerful, but we'll not see them
    • do much more then automating the building process (that's more than just compilation)
      • manage dependencies
502 / 667

Documenting the software

503 / 667

How to document the software?

Big question: we'll not give an answer here.

504 / 667

Targets

  1. Final users
  2. Developers of other software using this software
  3. Developers of this software
    • including the future you
505 / 667

Transferring knowledge

For 2 and 3, good code is the first source of knowledge:

  • for 2, software structure, component names
  • for 3, software structure, component names, code quality

After that, knowledge can be transferred in many ways:

  • examples
  • wiki
  • ...

In some cases, providing an API documentation (aka javadoc) may be useful.

506 / 667

API documentation with javadoc

  1. Write the documentation with a proper syntax directly within the .java files
  2. Compile one or more of java files (with or without documentation) to a set of web documents using javadoc

Both steps are largely assisted by/automated with IDEs.

507 / 667

javadoc like documentation

package it.units;
/**
* A class for producing greeting strings with a provided formula.
*
*/
public class Greeter {
private final String formula;
/**
* Build a new greeter with the provided {@code String} as greeting formula.
*
* @param formula A greeting formula.
*/
public Greeter(String formula) {
this.formula = formula;
}
/**
* Produces a greeting string with the given {@code name}.
*
* @param name The name to be included in the greeting string.
* @return A {@code String} with a greeting of {@code name}
* with the formula of this {@code Greeter}.
*/
public String greet(String name) {
return formula + " " + name;
}
}
508 / 667

Syntax

  • /** marks the start of a javadoc comment (normal block comments start with /*)
  • javadoc tags start with @
  • some HTML syntax can be used
  • ...
509 / 667

Interfaces

(and other language features)

510 / 667

interface

Defines only the signature of zero or more methods:

public interface Listener {
void listen(Event event);
}
  • Definition syntax is the same of classes
    • but the compiler does not require method code
  • public is unnecessary
    • compilation fails with protected and private modifiers
  • Compiles to Listener.class

This might be an example of a code following the observer pattern; Event should be properly defined elsewhere.

511 / 667

The dictionary says

"a surface regarded as the common boundary of two bodies, spaces, or phases"

Neither body needs to know about the inner details of the other body:

  • the called does not know who is the caller
  • the caller does not know who is the called

Non public methods cannot be used from outside:
\Rightarrow they are an inner detail

512 / 667

Details and limitations

"Defines"

  • "only the signature"
    • Starting with Java 8, interfaces may also define methods code: default methods and static methods; we'll see, briefly.
  • "zero or more"
    • e.g., Serializable, Cloneable, ...
  • "methods"
    • cannot define constructors! Only methods.
513 / 667

Implementing an interface

Classes can implement zero or more interfaces:

public class StandardOutputListener implements Listener {
public void listen(Event event) {
System.out.println(event);
}
}
public class FileListener implements Listener, AutoCloseable {
public void listen(Event event) { /* ... */ }
public void close() { /* ... */ }
}

Implemented methods:

  • cannot reduce visibility (they have to be public)
  • cannot enlarge throws clause
514 / 667

Usage

Similar to class inheritance:

References to interface can reference implementing classes:

Listener listener = new FileListener();
listener.listen(event);

also in method signatures:

public static void broadcast(Event event, Listener[] listeners) {
for (Listener listener : listeners) {
listener.listen(event);
}
}
515 / 667

Instantiation

Interface cannot be instantiated!

Listener listener = new Listener(); // does not compile!

Which code should be executed when listener will be invoked?

  • interfaces define only signatures!
516 / 667

instanceof

Works as expected:

public class NullListener implements Listener {
public void listen(Event event) {
/* do nothing */
}
}
NullListener listener = new NullListener();
if (listener instanceof Listener) {
System.out.println("It is!")
}
517 / 667

But no class inheritance!

Implementing the same interface does not establish inheritance-related relationships.

NullListener listener = new StandardOutputListener(); //does not compile!
StandardOutputListener listener = new NullListener(); //does not compile!
518 / 667

Interface inheritance

An interface may extend one or more interfaces:

public interface MultipleListener extends Listener {
void listen(Event[] events);
}

A class implementing MultipleListener must define both methods:

  • void listen(Event[])
  • void listen(Event)
519 / 667

Multiple interface inheritance

"may extend one or more"

Why allowed with interfaces and not with classes?

Suppose a class CC extends CC' and CC'' and both have a method mm with the very same signature

  • which one has to be executed at runtime?

Suppose an interface II extends II' and II'' and both have a method mm with the very same signature

  • they do not have code not: no problems!
520 / 667

When to use interfaces?

Key motivation: define something that can perform some operations, without describing how they have to be performed

  • abstraction
  • reduced complexity
public interface Listener {
void listen(Event event);
}

Definition: "The listener listens"

Then, you can build whatever complex functionalities based on this definition.

521 / 667

Interface and abstraction

It's like when you manipulate mathematical concept:

"let f:RRf: \mathbb{R} \to \mathbb{R} be a function"

then you continue your reasoning and complex theory
without the need to specify the actual form of ff!

522 / 667

Future extension and new

Given an interface II and a sw SS doing complex things with that II:

  • today SS works with implementation AA of II
  • tomorrow same SS works "better" with "newer" implementation BB of II

Example:

  • II: interface Listener
  • AA: class StandardOutputListener implements Listener
  • BB: class CloudLogger implements Listener
523 / 667

Separation of concerns

Separation of concerns: xx is concerned only about being xx!

  • SS has been designed without any mention to AA!
    • no new AA!
  • AA and BB have been designed without any mention to SS

SS need not to be updated for using BB!

II is the point of contact (i.e., interface) between SS and AA/BB

524 / 667

Interface vs. class inheritance

The "same" goal might be achieved, in principle, with inheritance:

public class Listener {
public void listen(Event event) {
}
}

Key differences:

  • (conceptual) define just what, not how!
    • what if not void return? how to set it in empty behavior?
  • (conceptual & practical) there is no empty/default implementation of operations!
    • you cannot do new with interfaces
  • (practical) suppose to have class A doing operations of AA and class B doing operations of BB, how should one define CC doing operations of both AA and BB?
    • no multiple class inheritance
525 / 667

Example: line processing server

Protocol (upon connection):

  • client sends one text line ll
  • if l=lquitl=l_\text{quit}, server closes connection, otherwise replies with processed line l=p(l)l'=p(l)

Server:

  • listens on port nportn_\text{port}
  • handles many client at a time
  • never terminates (really!)
  • p:p: String \to String, lquitl_\text{quit}, port number are parameters
  • server needs not to be re-compiled for new pp
    • pp is passed at construction like port number and lquitl_\text{quit}
526 / 667

Line processor

p:p: String \to String

What is pp? It is a command processor that takes one string and gives one string.

When building the server, we do not need to know anything else.

public interface CommandProcessor {
String process(String input);
}

E.g.: "uppercaser"

public class Uppercaser implements CommandProcessor {
public String process(String input) {
return input.toUpperCase();
}
}
527 / 667

Enhanced LineProcessingServer

public class LineProcessingServer {
private final int port;
private final String quitCommand;
private final CommandProcessor processor;
public LineProcessingServer(int port, String quitCommand, CommandProcessor processor) {
/* ... */
}
public void run() throws IOException {
/* ... */
}
/* quitCommand and processor getters */
}

RobustClientHandler should be modified to do server.getProcessor().process() instead of server.process(); or LineProcessingServer might implement CommandProcessor. how?

This LineProcessingServer is written once and can be used many times for different kinds of processing by implementing many times CommandProcessor.

528 / 667

Usage

LineProcessingServer uppercaserServer = new LineProcessingServer(
10000,
"BYE",
new Uppercaser()
);
uppercaserServer.run();
LineProcessingServer tokenCounterServer = new LineProcessingServer(
10001,
"BYE",
new TokenCounter(" ")
);
tokenCounterServer.run();

How could you design/code TokenCounter?

529 / 667

abstract

Modifier that can be applied to class and method definitions.

abstract method:

  • defines only the signature (as for interfaces)

abstract class:

  • cannot be instantiated

If a class has at least one abstract method, it has to be defined as abstract.

The abstract class may be extended by a non-abstract class that implements the abstract methods, if any.

Naming convention: usually AbstractXX

530 / 667

Example

public abstract class AbstractFunction {
public abstract double compute(double x);
public double max(double[] xs) {
double max = Double.NEGATIVE_INFINITY;
for (double x : xs) {
max = Math.max(compute(x), max);
}
return max;
}
}
  • the actual computation is not defined
  • some other functionality is defined
    • here based on the abstract functionality
531 / 667

Level of abstraction

interfaces: 100% abstraction

  • no functionalities are defined

abstract classes: \le 100% abstraction

  • some functionality may be defined

Actually, with default and static methods, interfaces may define some functionality

Both cannot be instantiated!

532 / 667

Combining interface and abstract

public abstract class CountingListener implements Listener {
private int counter = 0;
public void listen(Event event) {
counter = counter + 1;
innerListen(event);
}
public int count() {
return counter;
}
protected abstract void innerListen(Event event);
}
533 / 667

Anonymous class

Classes implementing interfaces or extending other classes (including abstract ones) can be defined "on the fly" without a name:

public class EventGenerator {
private String prefix;
public void doThings() {
final PrintStream ps = /* ... */
Listener listener = new Listener() {
public void listen(Event event) {
ps.println(prefix + event.toString());
}
};
}
}
  • listener references an object that is instance of an anonymous class that implements Listener
  • ps.println() is not executed "here"
534 / 667

Ananymous vs. named

At compile time:

  • new type defined and immediately "forgot"; cannot be reused
  • can access fields and methods of enclosing class
  • instance cannot access local variables in its enclosing scope that are not declared as final (or effectively final)
  • ...

At runtime: no differences wrt "normal" definition:

  • the type does have a name (something like Main$1, depending on where it is defined)
535 / 667

Using LineProcessingServer

public class LineProcessingServer {
/* ... */
public LineProcessingServer(int port, String quitCommand, CommandProcessor processor) {
/* ... */
}
/* ... */
}

Using with anonymous class definition:

LineProcessingServer server = new LineProcessingServer(10000, "BYE", new CommandProcessor() {
public String process(String input) {
return input.toUpperCase();
}
});
536 / 667

default and static methods

Modifiers for methods defined in interfaces:

  • default methods provide a default implementation; implementing class may or may not override it
  • static methods provide a static method (like for regular class methods)

(Since Java 8)

537 / 667

Examples

public interface Listener {
void listen(Event event);
default void listen(Event[] events) {
for (Event event : events) {
listen(event);
}
}
default Listener andThen(final Listener other) { // then is a keyword
final Listener thisListener = this;
return new Listener() {
public void listen(Event event) {
thisListener.listen(event);
other.listen(event);
};
}
}
static Listener nullListener() { // null is a keyword
return new Listener() {
public void listen(Event event) {/* do nothing */ };
}
}
}
  • Listener nullListener() is a constructor-like utility method
  • this inside the anonymous class would refer the "anonymous" object
538 / 667

Usage

Listener listener = Listener.nullListener();
Listener listener = new StandardOutputListener()
.andThen(new FileListener(file));
539 / 667

A bit more complex example

public interface Listener {
void listen(Event event);
/* ... */
static Listener nonBlocking(final Listener inner) {
return new Listener() {
public void listen(final Event event) {
Thread t = new Thread() {
public void run() {
inner.listen(event);
}
}
t.start();
}
}
}
}
  • Two anonymous class definitions
    • verbose: we'll see an alternative
  • final: effect is on called, not on caller
540 / 667

Usage

Listener listener = Listener.nonBlocking(
new StandardOutputListener().andThen(new FileListener(pathName))
);
EventGenerator generator = new EventGenerator(listener);
generator.start();

The developers of EventGenerator just need to know void listen(Event event)!

541 / 667

abstract vs. default (and static)

Apparently, they achieve the same goal:

  • definition of type with partial functionalities

Key differences:

  • interface methods cannot use any instance state
    • they are not aware of it
  • adding a functionality to an abstract class requires all inherited types to be recompiled; not required for default methods
    • "retrofitting"

Some risk of misusage:

  • design/development experience helps
542 / 667

enum

A type representing a predefined (by developer) set of values.

  • an instance of an enum corresponds to one of those values.
public enum Gender {
MALE, FEMALE, OTHER;
}
public class Person {
private String firstName;
private String lastName;
private final Date birthDate;
private Gender gender;
}

Naming convention:

  • type name: noun (like classes)
  • values: name, all uppercase with _, like constants
543 / 667

Usage

enums values are specified with dot notation:

public class Person {
public String toString() {
String prefix;
if (gender.equals(Gender.FEMALE)) {
prefix = "Ms. ";
} else if (gender.equals(Gender.MALE)) {
prefix = "Mr. ";
}
return prefix + firstName + " " + lastName;
}
}

For enums, == works like equals().

Enums cannot be instantiated:

  • new Gender() does not compile!
544 / 667

enums with switch

public class Person {
public String toString() {
String prefix;
switch (gender) {
case FEMALE:
prefix = "Ms. ";
break;
case MALE:
prefix = "Mr. ";
break;
default:
prefix = "";
}
return prefix + firstName + " " + lastName;
}
}

No need to specify Gender..

545 / 667

Fields and methods

enums can have methods, fields, constructors.

  • constructors have to be called when defining values
public enum Gender {
MALE("Mr."), FEMALE("Ms."), OTHER("");
private final String prefix;
Gender(String prefix) {
this.prefix = prefix;
}
public String getPrefix() {
return prefix;
}
public String prefix(String string) {
return prefix.isEmpty() ? string : prefix + " " + string;
}
}
public String toString() {
return gender.prefix(firstName + " " + lastName);
}
546 / 667

Annotations

Very very briefly.

  • Can be used to annotate class and method definitions and fields (and other things)
  • Can have arguments
  • Can be defined
  • Can be retained:
    • at compile time
    • at runtime
    • both

For basic usage, no need to define new annotations.

547 / 667

An example

public class StandardOutputListener implements Listener {
@Override
public void listen(Event event) {
System.out.println(event);
}
}

Says to the compiler:

  • this method definition overrides another definition
548 / 667

@Override

549 / 667

Lambdas

550 / 667

One-method interfaces

In many cases, one interface models just one functionality by definining a single method.

"Our" examples:

  • String process(String) in CommandProcessor
  • void listen(Event) in Listener

JDK examples:

  • void run() in Runnable
    • concurrency can be obtained by instantiating a Thread with a Runnable (Thread(Runnable) constructor) and then invoking start()
551 / 667

Functional interface

Those interfaces model functions:

  • one way to map one input (possibly being defined by multiple arguments) to one output
  • without a state

The "users" of implementation of those interface just need the function definition, that is de-facto, fully contained in the single method implementation.

Yet, implementing them is verbose:

LineProcessingServer server = new LineProcessingServer(
10000, "BYE", new CommandProcessor() {
public String process(String input) {
return input.toUpperCase();
}
});
552 / 667

Lambda expressions

(Or, simply, lambdas; since Java 8)

A syntactic shorthand for implementing functional interfaces annotated with @FunctionalInterface:

  • "implementing" = "defining anonymous classes that implement"
@FunctionalInterface
public interface CommandProcessor {
String process(String input);
}
CommandProcessor p = (String input) -> {
return input.toUpperCase()
};
LineProcessingServer server = new LineProcessingServer(10000, "BYE", p);
553 / 667

Syntax

Main option:

  • list of arguments (possibly empty)
  • ->
  • body

The method name is not needed, because there is only one method!

The interface is not needed, the compiler can infer it from the context!

Recall: just a syntactic shorthand!

554 / 667

@FunctionalInterface

It can be applied to interfaces with one single method.
It can be omitted (but use it!)

An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.

If a type is annotated with this annotation type, compilers are required to generate an error message unless:

  • The type is an interface type and not an annotation type, enum, or class.
  • The annotated type satisfies the requirements of a functional interface.

However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.

555 / 667

Even more compact

In many cases, type of arguments can be omitted:

CommandProcessor p = (input) -> {
return input.toUpperCase()
};

If there is exactly one input argument, parentheses () can be omitted:

CommandProcessor p = input -> {return input.toUpperCase()};

If the body is composed by exactly one statement (possibly return), brackets {} can be omitted (with return, if present):

CommandProcessor p = input -> input.toUpperCase();
556 / 667

Method reference operator

If the statement consists only of the invocation of a method, it can be specified with the method reference operator :::

CommandProcessor p = input -> StringUtils::reverse;

where (might be):

public class StringUtils {
public static String reverse(String string) {
/* ... */
return reversed;
}
}

There is an actual reverse() in StringBuilder.

557 / 667

Usage in practice

Convert to uppercase:

server = new LineProcessingServer(10000, "BYE", s -> s.toUpperCase());

Prefix:

final String prefix = /* ... */;
server = new LineProcessingServer(10000, "BYE", s -> prefix + s);

Count tokens:

server = new LineProcessingServer(10000, "BYE", s -> s.split(" ").length);
558 / 667

More complex

@FunctionalInterface
public interface RealFunction {
double apply(double x);
default RealFunction composeWith(RealFunction other) {
return x -> other.apply(apply(x));
}
default RealFunction integrate(double x0, double step) {
return x -> {
double sum = 0;
for (double xv = x0; xv <= x; xv = xv + step) {
sum = sum + step * apply(xv);
}
return sum;
};
}
}
  • ff.composeWith(gg) gives fgf \circ g, with (fg)(x)=g(f(x))(f \circ g)(x) = g(f(x))
  • ff.integrate(x0x_0,Δx\Delta x) gives
    • tx0,x0+Δx,,xf(t)Δx\sum_{t \in {x_0, x_0+\Delta x, \dots, x}} f(t) \Delta x
    • i.e., x0xf(t)dt\approx \int_{x_0}^{x} f(t) dt

Both return a function!

559 / 667

Usage

public class RealFunctionUtils {
public static double max(RealFunction f) { /* ... */ };
public static double[] zeros(RealFunction f) { /* ... */ };
}
double[] zeros = RealFunctionUtils
.zeros(x -> (x * x + 1) / (1 + x));
RealFunction f = (x -> x + 1);
double max = RealFunctionUtils.max(f
.integrate(-1, 0.1)
.composeWith(x -> x * x + 1));

Recall: only a syntactic shorthand!

  • first is {xR:x2+11+x=0}\approx \{x \in \mathbb{R}: \frac{x^2+1}{1+x}=0\}
  • second is maxxR(fg)(x)\approx \max_{x \in \mathbb{R}} (f \circ g)(x), where
    • f(x)=1xt+1dtf(x) = \int_{-1}^x t+1 dt
    • g(x)=x2+1g(x) = x^2+1
560 / 667

Why?

Goals:

  • favor functional programming
    • the functiuon as a key concept in design
  • reduce verbosity

Largest concrete exploitment:

  • java.util.stream: we'll see them
561 / 667

Generics

562 / 667

Motivation

Suppose we want to design a class that:

  • represents a set of strings
  • allows to:
    • add a string to the set
    • count the number of strings in the set
    • check if a string is contained in the set
SetOfStrings set = new SetOfStrings();
set.add("hi");
set.add("world");
set.add("hello");
set.add("world");
System.out.println(set.size()); // -> 3
System.out.println(set.contains("dog")); // -> false
563 / 667

Possible solution

public class SetOfStrings {
private String[] strings = new String[0];
public void add(String toAddString) {
if (!contains(toAddString)) {
String[] newStrings = new String[strings.length + 1];
System.arraycopy(strings, 0, newStrings, 0, strings.length);
newStrings[newStrings.length - 1] = toAddString;
strings = newStrings;
}
}
public boolean contains(String otherString) {
for (String string : strings) {
if (string.equals(otherString)) {
return true;
}
}
return false;
}
public int size() {
return strings.length;
}
}

Key idea:

  • keep an array or current strings
    • enlarge when adding
  • contains() and add() check for equal string in array
564 / 667

Set of Person?

Suppose now we want a set of Persons.

Two options up to Java 1.5 (excluded):

  1. design and code a SetOfPersons almost identical to SetOfStrings
    • then a SetOfThiss, a SetOfThats, and so on...
  2. design and code a "general" Set that takes objects

The core functionality of SetOfStrings do not depend on any peculiar functionality of String (or Person, or This...).
The key functionality is equals(), that is present in (any) Object.

  • so, let's go for 2
  • also because cool developers always avoid writing more than one times the same code!
565 / 667

Set of Object (or just Set)

public class Set {
private Object[] items = new Object[0];
public void add(Object toAddItem) {
if (!contains(toAddItem)) {
Object[] newItems = new Object[items.length + 1];
System.arraycopy(items, 0, newItems, 0, items.length);
newItems[newItems.length - 1] = toAddItem;
items = newItems;
}
}
public boolean contains(Object otherItem) {
for (Object item : items) {
if (item.equals(otherItem)) {
return true;
}
}
return false;
}
public int size() {
return items.length;
}
}
566 / 667

Check on type

SetOfStrings:

  • a set: with set properties, add() and size() operations
  • of strings: it cannot contain other things
    • the compiler prevents to use it to contain other things!
SetOfStrings strings = new SetOfStrings();
strings.add("hi");
strings.add(new Person()); // does not compile!
567 / 667

No check!

Set:

  • a set: with set properties, add() and size() operations
  • no constraints on item type
Set persons = new Set();
persons.add(new Person());
persons.add("surprise!"); // does compile!!!

It'd be a responsability of the developer to correctly use Set.

568 / 667

Language limitation

"Usual" type consistency check:

  • "dear compiler, verify that doStuff(String) method, when used, takes objects of type String (or extends String)"

This type consistency check:

  • "dear compiler, verify that add(T) and contains(T), are both used with the same T, regardless of what actual T"

The second case was not supported by the language up to Java 1.5.

  • i.e., the language offered no syntax to allow the developer to ask the compiler to do that check
569 / 667

The solution: generics

Generic type: a type defined with one or more other types as parameter

E.g., "a set of strings":

  • set is the generic type
  • string is parameter (still a type)

Language:

  • how to define them?
  • how to use them?

Design:

  • when to use them?
570 / 667

Why from just 1.5?

Generics are an old concept in programming (see Generic Programming):

  • dates back to 70s
  • exist now in many languages: Python, Java, C#, C++ (with another name), TypeScript, ...

Java designers avoided, for a while, the costs of integrating generics into the language.

  • winning motivation was the collection framework
571 / 667

How to define?

public class Set<T> {
public void add(T toAddItem) { /* ... */ }
public boolean contains(T otherItem) { /* ... */ }
public int size() { /* ... */ };
}

T is not an actual type: it's the identifier of a parameter specifying a type.

Naming convention:

  • usually one single uppercase letter, being the first letter of a suitable name for the parameter
  • here: T for type
572 / 667

Usage of parameter

T can be used wherever a type would be used:

  • definition of fields (instance variables)
  • definition of arguments (argument variables)
  • definition of references (local variables)
  • definition of return type

But:

  • T cannot be used as constructor
    • new T() does not compile: what constructor should be invoked? what if not legit?
    • new T[] does not compile
  • T cannot be valued to primitive type
573 / 667

How to use?

Set<String> strings = new Set<String>();
strings.add("hi");
Set<Double> doubles = new Set<Double>();
doubles.add(3.14d);

String and Double are "values" for the parameter T:

  • they have to be actual types
  • or, if a parameter type identifier is defined in the context, a parameter type

This would not compile:

Set<String> strings = new Set<String>();
strings.add(3d);
strings.add(new Person("Eric", "Medvet"));
574 / 667

Arrays or primitive types

Does not compile!

Set<double> numbers = new Set<double>();

Use wrapper classes:

Set<Double> numbers = new Set<Double>(); //compiles!

Arrays are types!

Set<double[]> numbers = new Set<double[]>(); //compiles!
Set<String[]> sentences = new Set<String[]>(); //compiles!
575 / 667

Diamond operator <>

(Since Java 1.7)

Set<String> strings = new Set<>();
strings.add("hi");

In the constructor, the generic type parameter can be left unspecified (<> instead of <String>), because it can be inferred by the compiler.

576 / 667

Our Set<T>

public class Set<T> {
private Object[] items = new Object[0];
public void add(T toAddItem) {
if (!contains(toAddItem)) {
Object[] newItems = new Object[items.length + 1];
System.arraycopy(items, 0, newItems, 0, items.length);
newItems[newItems.length - 1] = toAddItem;
items = newItems;
}
}
public boolean contains(T otherItem) {
for (Object item : items) {
if (item.equals(otherItem)) {
return true;
}
}
return false;
}
public int size() {
return items.length;
}
}

Since T[] cannot be instantiated, this Set<T> internally uses Object[]:

  • completely invisible to the users of Set<T> (information hiding)
577 / 667

Multiple parameters, generic interface

@FunctionalInterface
public interface Function<A, B> {
B apply(A a);
}

An interface that represents a function from A to B that is able to do one thing:

  • given an A, returns a B
578 / 667

Examples

Function<Double, Double> squarer = new Function<>() {
@Override
public Double apply(Double x) {
return x * x;
}
};
double y = squarer.apply(2); // -> 4
Function<String, String> shortener = new Function<>() {
@Override
public String apply(String s) {
String acronym = "";
for (String token : s.split(" ")) {
acronym = acronym + token.substring(0, 1).toUpperCase();
}
return acronym;
}
};
String shortened = shortener.apply("Eric Medvet"); // -> EM
Function<String, Integer> tokenCounter = s -> s.split(" ").length;
int nOfTokens = tokenCounter.apply("I love generics!"); // -> 3
Function<String, Integer> length = String::length;
length.apply("wow"); // -> 3
579 / 667

Generic method

Methods (static or not) can have "local" type parameters:

@FunctionalInterface
public interface Function<A, B> {
B apply(A a);
default <C> Function<A, C> composeWith(Function<B, C> other) {
return a -> other.apply(apply(a));
}
}

Visibility:

  • A and B are visible everywhere (here)
  • C is visible only in composeWith() (signature and body)

Usage:

int n = tokenCounter.composeWith(n -> n*n).apply("Eric Medvet");

n is?

580 / 667

Unparametrized

Set set = new Set();
set.add("hi");
set.add(3.14); //autoboxing
set.add(new Object());

Legit, but the compiler emits a warning ("raw usage of parametrized type").

  • may lead to misleading usage

If one still wants to contain any object, it's better to make the type explicit:

Set<Object> set = new Set<>();
set.add("hi");
set.add(3.14); //autoboxing
set.add(new Object());
581 / 667

Generics and inheritance

Trivial:

public class Person { /* ... */ }
public class Worker extends Person { /* ... */ }
Set<Person> persons = new Set<>();
persons.add(new Person("Eric", "Medvet"));
persons.add(new Worker("Bruce", "Wayne", "businessman")); // -> OK

Recall:

  • compiler: "is a Worker able to do all the things that a Person can do?" yes!
582 / 667

With interfaces

public interface Container<T> {
void add(T t);
T getOne();
}
public class ArrayContainer<T> implements Container<T> {
/* ... */
}
public interface LimitedContainer<T> extends Container<T> {
boolean hasSpace();
}

interface LimitedContainer definition (i.e., without <T>) would not compile:

  • 1st <T> defines an identifier: T
  • 2nd <T> uses an identifier
    • if 1st absent, at 2nd compiler "cannot resolve symbol"
583 / 667

Instantiating type parameter

public class StringContainer implements Container<String> {
/* ... */
}

StringContainer is not a generic type!

584 / 667

Inheritance among generics

Assume:

class Worker extends Person {}
interface LimitedContainer<T> extends Container<T> {}
class ArrayContainer<T> implements Container<T> {}
class LimitedArrayContainer<T> implements LimitedContainer<T> {}

Ok:

Container<Person> persons = new ArrayContainer<>();
Container<Person> persons = new LimitedArrayContainer<>();

Not ok (does not compile):

Container<Person> persons = new ArrayContainer<Worker>();
Container<Person> persons = new LimitedArrayContainer<Worker>();
585 / 667

Reference: class or interface?

For the references: use always the most general type that is specific enough!

Container<Person> persons = new ArrayContainer<>();

Means: "dear all (other developers, future me), I am going to use only Container operations on persons"

  • another point of view: "what matters here is that persons is a Container"
ArrayContainer<Person> persons = new ArrayContainer<>();

Means: "I need to use ArrayContainer operations; Container ones are not enough"

  • it happens rarely
586 / 667

extends in parameter type

A generic can narrow the generality of the type parameter(s):

public interface Union<W extends Worker> {
void register(W worker);
W[] listAlphabetically();
}

The developer states that:

  • a Union makes sense only "of some kind of Workers" and they have the same type (or subtypes)
  • implementing those methods likely require using operations/features of Workers

For this usage, extends is used also with interfaces.

public class ComplexStuff<T extends Doer> { /* ... */ }
587 / 667

Wildcard ?

In some cases, any type is ok:

  • not in the definition of the generic type
  • usually in method signatures
public static int count(Set<?> set) { /* ... */ }
  • no need to know the type for counting
public static void removeShorterThan(
Set<? extends Sized> set,
double size
) { /* ... */ }
588 / 667

When to use generics?

Basically, every time you are modeling a XX of YY!

Particularly powerful with interface:

  • interface YY declares some capabilities
    • what, not how
  • XX defines some functionalities based on YY capabilities

By decoupling XX from YY, software may be made much clearer!

  • separation of concerns
589 / 667

JDK examples: Function<T,R>

590 / 667

Comparable<T>

591 / 667

Total ordering

Given aa and bb:

  • abbaa=ba \le b \land b \le a \Rightarrow a = b (antisymmetry)
  • abbcaca \le b \land b \le c \Rightarrow a \le c (transitivity)
  • abbaa \le b \lor b \le a (connexity)

interface Comparable<T> models this with a single method:

  • int compareTo(T other)
    • <0< 0 means this precedes other
    • >0> 0 means this succeeds other
    • =0= 0 means otherwise
592 / 667

interface Comparator<T>

"Similar" to Comparable<T>

  • int compare(T t1, T t2)
  • useful when one needs to compare objects of type that does not implement Comparable
  • many useful default methods

Comparator<T>, Comparable<T>:

  • both define a single method
  • only Comparator<T> is a @FunctionalInterface

Why?

593 / 667

Comparator<T>

594 / 667

Natural ordering

Natural ordering of a type is the ordering dictated by its compareTo()

  • i.e., the type must implement Comparable

E.g., natural ordering by last name:

public class Person implements Comparable<Person> {
private final String firstName;
private final String lastName;
private final Date birthDate;
/* getters */
public int compareTo(Person other) {
return lastName.compareTo(other.lastName); // not null safe
}
}
595 / 667

Custom ordering with Comparator

Comparator<Person> c = Comparator
.<Person>naturalOrder()
.thenComparing(Person::getBirthDate)
.thenComparing(Person::getFirstName)
.reversed();
  • <Person>naturalOrder() is a way of setting the value of a parameter type with no usage of the parameter in the arguments
  • Person::getBirthDate is a Function<Person, Comparable>

Result:

  1. Jack Zurrell, 01/03/1982
  2. Andrea Medvet, 11/10/2013
  3. Alice Medvet, 07/02/2011
  4. Eric Medvet, 02/03/1979
596 / 667

Key JDK APIs

Collections, Executors, Streams

597 / 667

Collections

598 / 667

Motivation

Model the concept of collection: groups of homogeneous items

  • and derived concepts (data structures)

Examples:

  • a set of XX
  • a list of YY
  • a queue of ZZ
599 / 667

Java collection framework

In the JDK:

  • a set of interfaces modeling key concepts
    • with operations
    • with "implicit" properties
  • a set of classes implementing those interfaces
600 / 667

What's inside?

Interfaces:

  • modeled concept is captured by the name

Classes, differ mainly in:

  • additional properties
  • behavior with respect to multithreading
  • performances
    • one class XX may have operation m()m() faster than another class XX', both implementing the same interface
601 / 667

Main interfaces

interface Collection<E>:

  • "A collection represents a group of objects, known as its elements."
    • E stays for element

interface Map<K,V>:

  • "An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value."
    • K stays for key, V stays for element
    • elsewhere called dictionary

Collection and Map are disjoint (with respect to inheritance) and Map is not precisely a collection. Yet it is traditionally considered part of the Java collection framework.

602 / 667

interface Collection<E>: key methods

Mod. and Type Method Description
boolean add​(E e) Ensures that this collection contains the specified element (optional operation).
boolean contains​(Object o) Returns true if this collection contains the specified element.
boolean remove​(Object o) Removes a single instance of the specified element from this collection, if it is present (optional operation).
int size() Returns the number of elements in this collection.
  • They all have the maximum generality that is specific enough to the concept of collection
    • you can do them on any collection
    • note the add() description: "ensures that [...] contains"
603 / 667

Details

Mod. and Type Method Description
boolean add​(E e) Ensures that this collection contains the specified element (optional operation).
boolean contains​(Object o) Returns true if this collection contains the specified element.
boolean remove​(Object o) Removes a single instance of the specified element from this collection, if it is present (optional operation).
int size() Returns the number of elements in this collection.
  • contains() and remove() take Object, rather than E: historical reasons, but conceptually sound
    • "does this box of oranges contains an apple?"; obviously no, but the question is legit
  • add() and remove() mutator methods, since they (potentially) modify the state, i.e., the group
    • modifying one element state is another thing
    • the boolean return value specifies if the collection actually mutated
604 / 667

Bulk operations

Mod. and Type Method Description
boolean addAll​(Collection<? extends E> c) Adds all of the elements in the specified collection to this collection (optional operation).
boolean containsAll​(Collection<?> c) Returns true if this collection contains all of the elements in the specified collection.
boolean removeAll​(Collection<?> c) Removes all of this collection's elements that are also contained in the specified collection (optional operation).
boolean retainAll​(Collection<?> c) Retains only the elements in this collection that are contained in the specified collection (optional operation).

"Derived" methods:

Mod. and Type Method Description
void clear() Removes all of the elements from this collection (optional operation).
boolean isEmpty() Returns true if this collection contains no elements.
605 / 667

toArray()

Mod. and Type Method Description
Object[] toArray() Returns an array containing all of the elements in this collection.
default <T> T[] toArray​(IntFunction<T[]> generator) Returns an array containing all of the elements in this collection, using the provided generator function to allocate the returned array.
<T> T[] toArray​(T[] a) Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array.

Why Collection<T>.toArray() returns an Object[] instead of a T[]?

  • Because T[] cannot be created.

The other two methods circumvent the limitation:

  • IntFunction<T[]> is a function from int to T[]
  • T[]

both provided by the caller.

606 / 667

Iterating over a collection

4 ways:

  • toArray() and then for (int i = 0; ...): ancient
  • iterator(): very old
  • for-each: current, proper way (if you don't need the index)
  • stream: fancy, we'll see
607 / 667

toArray() and for

Collection<Person> persons = /* ... */
Person[] personArray = persons.toArray(new Person[persons.size()]);
for (int i = 0; i < personArray.length; i++) {
Person person = personArray[i];
// do things
}

This way, we are actually iterating over a copy of the collection, rather than the collection itself.

  • the collection might change during the iteration

Naming convention: plural form of contained type:

  • Person \rightarrow persons (or qualified: nicePersons)
    • population would be ok
  • Axis \rightarrow axes
608 / 667

iterator()

In interface Collection<E>:

Mod. and Type Method Description
Iterator<E> iterator() Returns an iterator over the elements in this collection.

In interface Iterator<E>:

Mod. and Type Method Description
boolean hasNext() Returns true if the iteration has more elements.
E next() Returns the next element in the iteration.

Then:

Collection<Person> persons = /* ... */
Iterator<Person> iterator = persons.iterator();
while (iterator.hasNext())
Person person = iterator.next();
// do things
}
609 / 667

interface Iterable<T>

An interface representing iterable things:

Mod. and Type Method Description
Iterator<T> iterator() Returns an iterator over elements of type T.

interface Collection<E> extends Iterable<E>

610 / 667

for-each

Collection<Person> persons = /* ... */
for (Person person : persons) {
// do things
}

The for-each syntax applies to any Iterable, hence also to Collection.
The compiler translates this to the iterator() way of iterating over elements.

611 / 667

Iteration and order

Never assume that iterating over a collection will result in some specific ordering! Neither with repeatability!

  • unless it is a property of the actual type of the collection
Collection<String> strings = /* ... */
string.add("hi");
string.add("world");
for (String string : strings) {
System.out.println(string);
}
  • Might result in:
    • hi, world
    • or world, hi
  • Might be different in two executions
612 / 667

interface Set<E>

interface Set<E> extends Collection<E>

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

  • Models the (mathematical) concept of set
    • no duplicates, no ordering
  • "duplication" captured by equals()
  • No extra methods with respect to Collection<E>
Set<String> names = /* ... */
names.add("Eric");
names.add("Pippi");
names.add("Eric");
System.out.println(names.size()); // -> 2
613 / 667

No duplicates

"A collection that contains no duplicate elements"

  • it's stated in the documentation of an interface but:
    • corresponding constraint should be enforced in the implementation of add() (and similar)
    • the compiler cannot check if an implementation actually correctly enforce the constraint

\Rightarrow one might implement Set<E> violating this guarantee!

  • JDK implementations are correct
614 / 667

Set and equals()

public class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}

In a Set:

Set<Person> persons = /* ... */
persons.add(new Person("Eric", "Medvet"));
persons.add(new Person("Eric", "Medvet"));
System.out.println(persons); // -> 2

When coding types that might be used inside a Set (or a Map), always override equals()!

615 / 667

Duplicates and state change

Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

616 / 667

interface SortedSet<E>

interface SortedSet<E> extends Set<E>

A Set that further provides a total ordering on its elements. The elements are ordered using their natural ordering, or by a Comparator typically provided at sorted set creation time. The set's iterator will traverse the set in ascending element order. Several additional operations are provided to take advantage of the ordering.

  • No duplicates, ordering
    • iterating over elements has a predictable outcome
    • natural ordering of E or provided Comparator<E>
Mod. and Type Method Description
E first() Returns the first (lowest) element currently in this set.
SortedSet<E> headSet​(E toElement) Returns a view of the portion of this set whose elements are strictly less than toElement.
E last() Returns the last (highest) element currently in this set.
SortedSet<E> subSet​(E fromElement, E toElement) Returns a view of the portion of this set whose elements range from fromElement, inclusive, to toElement, exclusive.
SortedSet<E> tailSet​(E fromElement) Returns a view of the portion of this set whose elements are greater than or equal to fromElement.
617 / 667

Example

SortedSet<String> names = /* ... */
names.add("Eric");
names.add("Pippi");
names.add("Eric");
names.add("Alice");
for (String name : names) {
System.out.println(name);
}

Gives:

Alice
Eric
Pippi
  • because Strings has natural ordering
618 / 667

interface List<E>

interface List<E> extends Collection<E>

An ordered collection (also known as a sequence). The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.

Unlike sets, lists typically allow duplicate elements.

  • Models the (mathematical) concept of sequence
    • duplicates, "ordering"
      • quoted because it's unrelated to natural ordering, but determined by order of additions and removals

Basically, can be used in place of an array T[] where the size might change at runtime.

There is also a java.awt.List that often wins over this java.util.List while doing autocompletion...

619 / 667

List<E>: key methods

Mod. and Type Method Description
void add​(int index, E element) Inserts the specified element at the specified position in this list (optional operation).
boolean addAll​(int index, Collection<? extends E> c) Inserts all of the elements in the specified collection into this list at the specified position (optional operation).
E get​(int index) Returns the element at the specified position in this list.
int indexOf​(Object o) Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
E remove​(int index) Removes the element at the specified position in this list (optional operation).
E set​(int index, E element) Replaces the element at the specified position in this list with the specified element (optional operation).
default void sort​(Comparator<? super E> c) Sorts this list according to the order induced by the specified Comparator.
List<E> subList​(int fromIndex, int toIndex) Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.

Besides those in Collection<E>.

  • indexOf() uses equals()
620 / 667

Usage

List<String> knownJavaThings = /* ... */
knownJavaThings.add("interfaces");
knownJavaThings.add("class");
knownJavaThings.add("generics");
knownJavaThings.add("GUI");
knownJavaThings.add("serialization");
knownJavaThings.set(1, "classes"); // 0-based
knownJavaThings.remove(3);
System.out.println(knownJavaThings.get(3));

Outcome?

621 / 667

interface Map<K,V>

An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.

The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings. The order of a map is defined as the order in which the iterators on the map's collection views return their elements. Some map implementations, like the TreeMap class, make specific guarantees as to their order; others, like the HashMap class, do not.

Models a function from K to V \cup null

  • not actually a collection
  • provides views of keys and values
  • does not extend Iterable!
    • cannot be iterated with for-each
622 / 667

Map<K,V> key methods

Mod. and Type Method Description
V get​(Object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
V put​(K key, V value) Associates the specified value with the specified key in this map (optional operation).
V remove​(Object key) Removes the mapping for a key from this map if it is present (optional operation).
int size() Returns the number of key-value mappings in this map.

Bulk operations:

Mod. and Type Method Description
void clear() Removes all of the mappings from this map (optional operation).
void putAll​(Map<? extends K,​? extends V> m) Copies all of the mappings from the specified map to this map (optional operation).

Other (some):

Mod. and Type Method Description
boolean containsKey​(Object key) Returns true if this map contains a mapping for the specified key.
boolean containsValue​(Object value) Returns true if this map maps one or more keys to the specified value.
623 / 667

Map<K,V> views

Mod. and Type Method Description
Set<K> keySet() Returns a Set view of the keys contained in this map.
Set<Map.Entry<K,​ V>> entrySet() Returns a Set view of the mappings contained in this map.
Collection<V> values() Returns a Collection view of the values contained in this map.
  • view of keys keySet() is a Set<K>:
    • no duplicates, no ordering
  • view of values values() is a Collection<V>
    • duplicates, no ordering (the least specific collection)

interface Map.Entry<K,​V>

Mod. and Type Method Description
K getKey() Returns the key corresponding to this entry.
V getValue() Returns the value corresponding to this entry.
  • view of entries entrySet() is a Set<Map.Entry<K,​ V>>
    • no duplicates, no ordering
624 / 667

Iterating over a Map

Depending on the specific case:

Map<String, Integer> ages = /* ... */
ages.put("Eric", 41); //autoboxing
ages.put("Simba", 13);

Keys matter:

for (String key : ages.keySet()) { /* ... */ }

Values matter:

for (int value : ages.values()) { /* ... */ } //autounboxing

Both matter:

for (Map.Entry<String, Integer> entry : ages.entrySet()) {
String name = entry.getKey();
int age = entry.getValye();
/* ... */
}
625 / 667

interface SortedMap<K,V>

A Map that further provides a total ordering on its keys. The map is ordered according to the natural ordering of its keys, or by a Comparator typically provided at sorted map creation time. This order is reflected when iterating over the sorted map's collection views (returned by the entrySet, keySet and values methods). Several additional operations are provided to take advantage of the ordering. (This interface is the map analogue of SortedSet.)

Additional operations (some):

Mod. and Type Method Description
K firstKey() Returns the first (lowest) key currently in this map.
K lastKey() Returns the last (highest) key currently in this map.
SortedMap<K,​V> headMap​(K toKey) Returns a view of the portion of this map whose keys are strictly less than toKey.
SortedMap<K,​V> subMap​(K fromKey, K toKey) Returns a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive.
SortedMap<K,​V> tailMap​(K fromKey) Returns a view of the portion of this map whose keys are greater than or equal to fromKey.

keySet() still returns a Set<K> and not a SortedSet<K>

  • signature cannot be changed while overridding
  • casting, or use firstKey()/lastKey() methods
  • at runtime it is a SortedSet (in JDK implementations)!
626 / 667

Other collections (some)

interface Queue<E> extends Collection<E>

A collection designed for holding elements prior to processing. Besides basic Collection operations, queues provide additional insertion, extraction, and inspection operations. Each of these methods exists in two forms: one throws an exception if the operation fails, the other returns a special value (either null or false, depending on the operation).

interface Deque<E> extends Queue<E>

A linear collection that supports element insertion and removal at both ends. The name deque is short for "double ended queue" and is usually pronounced "deck". Most Deque implementations place no fixed limits on the number of elements they may contain, but this interface supports capacity-restricted deques as well as those with no fixed size limit.

interface BlockingQueue<E> extends Queue<E>

A Queue that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.

627 / 667

Implementations (most common)

  • Set \rightarrow HashSet, LinkedHashSet, EnumSet
  • SortedSet \rightarrow TreeSet
  • List \rightarrow ArrayList, LinkedList
  • Map \rightarrow HashMap, LinkedHashMap, EnumMap
  • SortedMap \rightarrow TreeMap

Recall: implementations differ in

  • additional properties
  • behavior with respect to multithreading
  • performances
628 / 667

Implementations differences

Key information (more on the documentation):

  • LinkedHash* give predictable iteration order (the "same" of insertion); Hash* do not
    • additional properties
    • particularly useful when looking for reproducibility!
      • should be coupled with proper seeding of Randoms; still hard to achieve in multithreading scenarios
  • Enum* are optimized for enums
    • performances
  • ArrayList fast in reading, slow in modifying; LinkedList slow in reading, fast in modifying
    • performance
629 / 667

Key methods

All implementations properly override toString(), equals() (and hashCode()).

List<String> names = new ArrayList<>();
names.add("Eric");
names.add("Simba");
System.out.println("Names = " + names);

Gives:

Names = [Eric, Simba]
630 / 667

Hash*, hashCode()

public int hashCode() in Object:

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.

The general contract of hashCode is:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

IDEs know this general contract and can the code for overridding hashCode() for us!

  • usually together and consistently with equals()
631 / 667

Constructors

All implementations have the default constructor:

List<String> names = new ArrayList<>();
SortedMap<Person, Double> marks = new TreeMap<>();

All have a constructor that creates a collection with the elements of another collection:

List<String> names = new ArrayList<>();
/* ... */
Set<String> uniqueNames = new HashSet<>(names);
  • it's not a view (neither a copy) of the other collection
  • each element is the same
632 / 667

Type of reference!

Use always the most general type!

Ok!

List<String> names = new ArrayList<>();

Not ok!

ArrayList<String> names = new ArrayList<>();
  • unless you really need names to be of type ArrayList<>, that is rare

Recall the deep meaning of "interface" and (one of) its goal(s):

  • abstraction: what is names?
  • reduced complexity: what are the properties of names that really matter?
633 / 667

Constructor-like default methods

Interfaces provide default constructor-like methods for doing contruct-and-fill! Resulting collections are unmodifiable!

Set:

Mod. and Type Method Description
static <E> Set<E> of() Returns an unmodifiable set containing zero elements.
static <E> Set<E> of​(E... elements) Returns an unmodifiable set containing an arbitrary number of elements.

List:

Mod. and Type Method Description
static <E> List<E> of() Returns an unmodifiable list containing zero elements.
static <E> List<E> of​(E... elements) Returns an unmodifiable list containing an arbitrary number of elements.

Unmodifiable!

List<String> names = List.of("Eric", "Simba");
names.add("Pippi"); // -> java.lang.UnsupportedOperationException
634 / 667

Multithreading

  • Most implementations are not thread-safe! (all the ones mentioned above)
    • they usually throw a ConcurrentModificationException
  • Some are thread-safe: e.g.,
    • class ConcurrentHashMap<K,​V> implements Map<K,V> (actually implements ConcurrentMap<K,V>)
    • class ConcurrentSkipListSet<E> implements SortedSet<E>
    • ...
635 / 667

Thread-safe views

Collections class (with the leading s!!!) provides utility static methods for obtaining thread-safe views of existing collections:

  • basically wrapping key methods in synchronized blocks
  • static * synchronized*(): e.g.,
    • static <T> SortedSet<T> synchronizedSortedSet​(SortedSet<T> s)
    • static <T> List<T> synchronizedList​(List<T> list)
  • thread-safety is guaranteed only when using the view!
  • thread-safety is guaranteed on the view, not on each element!
636 / 667

Collections

637 / 667

Big picture

Big picture of Java Collections Framework

From: ICT.social

638 / 667

Example

Given a sentence, return the word occurrences.

Design phase:

  • understand and properly state domain concepts:
    • sentence
    • word
    • word occurrences
  • find or code corresponding types
    • sentence \rightarrow String
    • word \rightarrow String
    • word occurrences \rightarrow Map<String, Integer>
639 / 667

Solution

public static Map<String, Integer> wordOccurrences(String sentence) {
String[] words = sentence.split("\\W+");
Map<String, Integer> occurrences = new HashMap<>();
for (String word : words) {
occurrences.put(
word,
occurrences.getOrDefault(word, 0) + 1
);
}
return occurrences;
}
System.out.println(wordOccurrences("today the cat is on the table."));
{the=2, today=1, cat=1, is=1, table=1, on=1}
  • \W is any non-word character
  • default V getOrDefault​(Object, V) returns the second argument if the key (1st argument) is not present
640 / 667

Sorted alphabetically

SortedMap<String, Integer> sortedOccurrences = new TreeMap<>(
wordOccurrences("today the cat is on the table.")
);
System.out.println(sortedOccurrences);
{cat=1, is=1, on=1, table=1, the=2, today=1}
  • further modification to the Map would not be reflected in the SortedMap
641 / 667

Sorted by increasing occurrences

List<Map.Entry<String, Integer>> entries = new ArrayList<>(
wordOccurrences("today the cat is on the table.").entrySet()
);
entries.sort(Comparator.comparing(Map.Entry::getValue));
System.out.println(entries);
[today=1, cat=1, is=1, table=1, on=1, the=2]
  • sort() line is equivalent to entries.sort((e1, e2) -> e1.getValue().compareTo(e2.getValue()))
  • note the [] instead of {}: come from ArrayList.toString() instead of HashMap.toString()
642 / 667

Other collections

Word occurrences: actually, this is a multiset of words

  • each element wWw \in W may appear 0 or more times
  • mathematically, often formalized as f:WNf: W \to \mathbb{N}
    • consistently, we modeled as Map<String, Integer>

Is there a multiset in the JDK?

643 / 667

Executors

644 / 667

Motivation

Simplify the design and development of code that executes tasks in asynchronous mode:

Key concepts:

  • task: a unit of processing with an input and an output
  • executor: a component that executes tasks

The task is not asynchronous/synchronous: that's a property of the execution.

645 / 667

interface Callable<V>

A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call.

Mod. and Type Method Description
V call() Computes a result, or throws an exception if unable to do so.

Model a task with a result:

  • V is the type of result (the task output)
    • the input is implicit in the class instantiating Callable
  • call() throws Exception
  • it is a @FunctionalInterface
646 / 667

Usage

final long x = 100000;
Callable<Long> sumOfFactorialsTask = () -> {
long sum = 0;
for (int i = 0; i < x; i++) {
sum = sum + factorial(i);
}
return sum;
};
try {
long result = sumOfFactorialsTask.call();
System.out.println("sum=" + result);
} catch (Exception e) {
System.err.println(String.format("Cannot compute due to %s", e));
}
public static long factorial(long n) {
return (n <= 1) ? n : (n * factorial(n - 1));
}
  • Synchronous execution!
    • the caller waits until the computation is done
  • i=0i=105i!\sum_{i=0}^{i=10^5} i!

Numbers here are too large...

647 / 667

interface Runnable

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.

Mod. and Type Method Description
void run() When an object implementing interface Runnable is used to create a thread, starting the thread causes the object's run method to be called in that separately executing thread.

Model a task without a result:

  • run() does not throw any (checked) exception
  • it is a @FunctionalInterface
  • much older than Callable in the JDK
  • Thread implements Runnable
648 / 667

Usage

Intended usage:

final long x = 100000;
new Thread(() -> {
long sum = 0;
for (int i = 0; i < x; i++) {
sum = sum + factorial(i);
}
}).start();
  • Thread​(Runnable target) is one of Thread constructors
  • Asynchronous execution!
    • the caller immediately return after start()
  • No easy way of getting the result!
    • may be taken with active polling, synchronization, ...
649 / 667

Callable<V> vs. Runnable

In Callable<V>:

The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.

The Executors class contains utility methods to convert from other common forms to Callable classes.

  • Executors class, with the trailing s
650 / 667

interface ExecutorService

An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks.

An ExecutorService can be shut down, which will cause it to reject new tasks. Two different methods are provided for shutting down an ExecutorService. The shutdown() method will allow previously submitted tasks to execute before terminating, while the shutdownNow() method prevents waiting tasks from starting and attempts to stop currently executing tasks. Upon termination, an executor has no tasks actively executing, no tasks awaiting execution, and no new tasks can be submitted. An unused ExecutorService should be shut down to allow reclamation of its resources.

Method submit extends base method Executor.execute(Runnable) by creating and returning a Future that can be used to cancel execution and/or wait for completion.

Models an executor with some additional properties:

  • implementations realize actual asynchronous executions

Key methods:

Mod. and Type Method Description
Future<?> submit​(Runnable task) Submits a Runnable task for execution and returns a Future representing that task.
<T> Future<T> submit​(Callable<T> task) Submits a value-returning task for execution and returns a Future representing the pending results of the task.
651 / 667

interface Future<V>

A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready.

Key method:

Mod. and Type Method Description
V get() Waits if necessary for the computation to complete, and then retrieves its result.

Models a future result:

  • the future result exists instantaneously
  • the actual result will exist in the future
  • get() blocks until the actual result is ready
652 / 667

Usage: Executor+Callable+Future

ExecutorService executorService = /* ... */
long n = 20;
Future<Long> future = executorService.submit(() -> factorial(n));
System.out.println("Computing");
try {
long result = future.get();
System.out.printf("%d!=%d%n", n, result);
} catch (InterruptedException | ExecutionException e) {
System.err.println(String.format("Cannot compute due to %s", e));
}
executorService.shutdown();
  • factorial(n) is done in asynchronously!
    • submit() returns immediately
  • caller blocks on long result = future.get();
653 / 667

Parallel execution of tasks

ExecutorService executorService = /* ... */
List<Callable<Long>> callables = new ArrayList<>();
for (int i = 0; i < 20; i++) {
final long n = i;
callables.add(() -> factorial(n));
}
try {
List<Future<Long>> futures = executorService.invokeAll(callables);
for (Future<Long> future : futures) {
System.out.printf("Got %d%n", future.get());
}
} catch (InterruptedException | ExecutionException e) {
System.err.println(String.format("Cannot compute due to %s", e));
}
  • All tasks are submitted together
  • Results are collected sequentially, but could made be ready in different order
  • Exceptions should be handled with greater care
    • invokeAll() throws InterruptedException
    • get() throws ExecutionException
654 / 667

Creating an ExecutorService

Many different implementations for different needs exist. They can be created with static methods of Executors:

Mod. and Type Method Description
static ExecutorService newCachedThreadPool() Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.
static ExecutorService newFixedThreadPool​(int nThreads) Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.
static ScheduledExecutorService newScheduledThreadPool​(int corePoolSize) Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.
  • newFixedThreadPool​(): n\le n threads run do computation at the same time \Rightarrow n\le n tasks are executed on this executor
  • interface ScheduledExecutorService: augments ExecutorService with methods for scheduled execution
    • schedule​(Callable<V> callable, long delay, TimeUnit unit)
    • scheduleAtFixedRate​(Runnable command, long initialDelay, long period, TimeUnit unit)
655 / 667

Example

Protocol (upon connection):

  • client sends one text line ll
  • if l=lquitl=l_\text{quit}, server closes connection, otherwise replies with processed line l=p(l)l'=p(l)

Server:

  • listens on port nportn_\text{port}
  • handles many client at a time, serves up to nn clients at the same time
  • never terminates
  • p:p: String \to String, lquitl_\text{quit}, port number, nn are parameters
656 / 667

Solution

public class ExecutorLineProcessingServer {
private final int port;
private final String quitCommand;
private final Function<String, String> commandProcessingFunction;
private final ExecutorService executorService;
public ExecutorLineProcessingServer(int port, String quitCommand, Function<String, String> commandProcessingFunction, int concurrentClients) {
this.port = port;
this.quitCommand = quitCommand;
this.commandProcessingFunction = commandProcessingFunction;
executorService = Executors.newFixedThreadPool(concurrentClients);
}
public void start() throws IOException {
/* ... */
}
}
657 / 667

All in one method

public void start() throws IOException {
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
try {
final Socket socket = serverSocket.accept();
executorService.submit(() -> {
try (socket) {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String command = br.readLine();
if (command == null) {
System.err.println("Client abruptly closed connection");
break;
}
if (command.equals(quitCommand)) {
break;
}
bw.write(commandProcessingFunction.apply(command) + System.lineSeparator());
bw.flush();
}
} catch (IOException e) {
System.err.printf("IO error: %s", e);
}
});
} catch (IOException e) {
System.err.printf("Cannot accept connection due to %s", e);
}
}
} finally {
executorService.shutdown();
}
}
658 / 667

Streams

(very briefly)

659 / 667

Motivation

Support functional-style operations on streams of elements.

  • corresponding code is much more concise and, hopefully, clearer

Stream: "a continuous flow or succession of anything"

  • not the group of elements, but the flow itself
  • not a collection

Example:

  • for each String: transform to uppercase, compute letter frequencies, sort by most uneven distribution
  • map-reduce
660 / 667

interface Stream<T>

(Since Java 8)

A sequence of elements supporting sequential and parallel aggregate operations.

From package java.util.stream docs:

The key abstraction introduced in this package is stream. The classes Stream, IntStream, LongStream, and DoubleStream are streams over objects and the primitive int, long and double types. Streams differ from collections in several ways:

  • No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
  • Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
  • Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
  • Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
  • Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.
661 / 667

Stream operations

Three phases:

  1. get from a source source (collection, array, ...)
  2. apply a pipeline of intermediate operations
    • transform a stream into another stream
  3. apply one terminal operation
    • stream to sink ("collection" or single value)
662 / 667

Obtaining a Stream<T>

From a source:

  • a Collection<T>:
    • Stream<T> stream()
    • Stream<T> parallelStream()
  • an array:
    • Stream<T> Arrays.stream(T[])
  • many other types:
    • Stream<String> lines() in BufferedReader
    • Stream<String> splitAsStream​(CharSequence input) in Pattern
    • ...
663 / 667

Some intermediate operations

Mod. and Type Method Description
Stream<T> distinct() Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.
Stream<T> filter​(Predicate<? super T> predicate) Returns a stream consisting of the elements of this stream that match the given predicate.
<R> Stream<R> map​(Function<? super T,​? extends R> mapper) Returns a stream consisting of the results of applying the given function to the elements of this stream.
DoubleStream mapToDouble​(ToDoubleFunction<? super T> mapper) Returns a DoubleStream consisting of the results of applying the given function to the elements of this stream.
IntStream mapToInt​(ToIntFunction<? super T> mapper) Returns an IntStream consisting of the results of applying the given function to the elements of this stream.
LongStream mapToLong​(ToLongFunction<? super T> mapper) Returns a LongStream consisting of the results of applying the given function to the elements of this stream.
Stream<T> sorted​(Comparator<? super T> comparator)

Returns a stream consisting of the elements of this stream, sorted according to the provided Comparator.

  • interface Predicate<T> is a @FunctionalInterface with a method boolean test​(T)
  • interface ToDoubleFunction<T> is a @FunctionalInterface with a method double applyAsDouble​(T) (same for int, long)

Function<? super T,​? extends R> can be applied to something more general than T (which includes T) and returns something more specific than R (which is certainly an R).

664 / 667

Some terminal operations

Mod. and Type Method Description
<R,​A> R collect​(Collector<? super T,​A,​R> collector) Performs a mutable reduction operation on the elements of this stream using a Collector.
T reduce​(T identity, BinaryOperator<T> accumulator) Performs a reduction on the elements of this stream, using the provided identity value and an associative accumulation function, and returns the reduced value.
Object[] toArray() Returns an array containing the elements of this stream.
<A> A[] toArray​(IntFunction<A[]> generator) Returns an array containing the elements of this stream, using the provided generator function to allocate the returned array, as well as any additional arrays that might be required for a partitioned execution or for resizing.
long count() Returns the count of elements in this stream.

And others such as average(), max(), min(), ... in numeric streams.

  • interface BinaryOperator<T> extends BiFunction<T,​T,​T> is a @FunctionalInterface with no further methods
  • interface BiFunction<T,​U,​R> is a @FunctionalInterface with a method R apply​(T, U)
665 / 667

Examples

Given a collection of person names (first+last), get the initials corresponding to names with more than 2 words in the names.

Collection<String> names = List.of(
"Andrea De Lorenzo",
"Eric Medvet",
"Alberto Bartoli",
"Felice Andrea Pellegrino"
);
names = names.stream()
.map(s -> s.split(" "))
.filter(ts -> ts.length>2)
.map(ts -> Arrays.stream(ts)
.map(s -> s.substring(0, 1))
.collect(Collectors.joining()))
.collect(Collectors.toList());
System.out.println(names);
Gives:
[ADL, FAP]
666 / 667

Another example

Given a collection of strings, compute the average number of consonants.

System.out.println(strings.stream()
.map(s -> s.toLowerCase().replaceAll("[aeiou]", ""))
.mapToInt(String::length)
.average()
.orElse(0d)
);
667 / 667

Lecturer

Eric Medvet

Research interests:

  • Evolutionary Computation
  • Machine Learning applications

Labs:

2 / 667
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow