Eric Medvet
Research interests:
Labs:
Teacher slides:
Intended usage:
Two options:
Failed if at least one part is graded <6/10.
Home assignments:
More details at first assignment.
Just for reference:
Project:
More details at project assignment.
Written test:
Oral exam:
In brief:
See the syllabus!
There we'll be several exercises:
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.
Practical motivations:
Teaching-related motivations:
Java is both:
We will focus on:
Mailnly composed of:
Platforms (also called editions) differ mainly in the libraries. Most common editions:
What is needed for developing in Java?
One JDK per (vendor, platform, version, os+architecture).
A few options:
Many excellent options.
Classic, desktop-based:
Cloud-based:
Pros:
Cons:
.java
vs. .class
files.java
files.class
filesCompilation: obtaining a .class
from .java
javac Greeter.java
.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!
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
.class
file contains executable code in a binary format (bytecode)java
) that simulates a machine: the Java Virtual Machine (JVM)JVM:
An example of the JVM specification, the instruction iadd
:
..., value1, value2 → ..., result
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.
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:
java
program for Linux x64java
program for Solaris SPARC x64→ Write once, run anywhere
Note:
java
java
executes the bytecodejava
?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):
javac
java
).class
files)It does not contain:
eric@cpu:~$ ls /usr/lib/jvm/java-11-openjdk-amd64 -latotale 48drwxr-xr-x 2 root root 4096 feb 6 12:08 bindrwxr-xr-x 4 root root 4096 feb 6 12:08 confdrwxr-xr-x 3 root root 4096 feb 6 12:08 includedrwxr-xr-x 6 root root 4096 feb 6 12:08 lib...
eric@cpu:~$ ls /usr/lib/jvm/java-11-openjdk-amd64/bin/ -latotale 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.
Do you need them?
How to access the documentation?
How to access the source code?
Both!
.java
to .class
through compilation.class
is then interpreted by java
for being executed on OSActually, things are much more complex:
Is intepretation efficient?
Relevant question: is my software fast enough?
If not?
(that is: write good code!)
The problem is rarely in interpretation!
When choosing a language/platform, speed of execution is just one factor.
Other factors:
In general, consider all costs and assess options.
public class Greeter { public static void main(String[] args) { System.out.println("Hello World!"); }}
Goal: deep understanding of this code
Take code highlighting with care!
"Things" (entities that can be manipulated in the code) are objects
The code manipulates objects through references
Both can be created.
object ≠ reference
String s;
String
with identifier s
(briefly: reference s
)String s = new String("Content");
s
of type String
String
and init it with "Content"
s
reference the new objectString s1 = new String("Content");String s2 = s1;
s1
and make it reference the new objectss2
and make it reference the object referenced by s1
Objects+references diagram: where? when?
new String("Content1");new String("Content2");
String
and init it with "Content1"
String
and init it with "Content2"
The two objects are not referenced: they cannot be manipulated!
String s1 = new String("Content1");String s2 = new String("Content2");s1 = s2;
s1
, create new String
inited with "Content1"
, and make s1
reference its2
, create new String
inited with "Content2"
, and make s2
reference its1
reference the object referenced by s2
After 2nd line:
After 3rd line:
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
Key questions:
Answers (in brief):
refName.opName()
refName.opName(otherRefName1, otherRefName2)
retRefName = refName.opName()
We are just scratching the surface; we'll go deeper ahead.
A class is a type
class ≠ object
Some expressions that are clues of misunderstanding:
Some correct expressions that hide complexity:
Some types are not classes, but primitive types:
boolean
char
byte
short
int
long
float
double
We'll discuss them later.
An operation applicable on a object is a method of the corresponding class.
Every method has a signature, consisting of:
void
, if there is no outputExamples (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)
String s = new String("Hello!");int l = s.length();
lenght()
on the object referenced by s
length()
on s
l
of type int
l
reference the object created upon the execution of the operationString s = new String("Hello!");int l = s.length();
The compiler (javac
) verifies (also) that:
s
has a method with name length
s
has a method length
This check:
Colloquially, Java is said to be strongly typed.
A class can have many methods:
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. |
Class PrintStream
A lot of classes, a lot of methods!
You have to:
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!
Extremely important!
Many of them: some just for Java, some broader.
The degree to which naming conventions are followed concurs in determining the quality of the code.
There are many sources online: e.g., JavaPoint
Class:
Dog
, Sorter
, Tree
HttpServerResponse
Method:
bark
, sort
, countNodes
size
, nodes
removeLastChar
Reference:
String
has to be referenced by s
numOfElements
is better than n
God gave us IDEs, IDEs give us autocompletion! Don't spare on chars.
String s = new String(" shout!");s = s.trim().toUpperCase();
trim()
on object referenced by s
toUpperCase()
on the object resulting from trim()
invocations
reference the object resulting from toUpperCase()
invocationType | 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). |
String s = new String(" shout!");s = s.trim().toUpperCase();
After 1st line:
After 2nd line:
Every class T has (at least) one special method called constructor that, when invoked:
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!");
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 thingsDate laterThanNow = new Date();
A class C can have more than one constructors:
Date
→ Date()
)Date
→ 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. |
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. |
The user of a class (that is, a developer possibly different than the one who developed the class):
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!
The user of a class knows:
He/she does not know:
The state of the object and the code of the class might change, but the user is not required to be notified of changes!
→ Modularity: everyone takes care of only some part of the sofware!
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();
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.
Domain knowledge:
Knowledge of the programming language:
You need both!
Complex c1 = new Complex(7.46, -3.4567);Complex c2 = new Complex(0.1, 9.81);
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();
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:
Can be manipuleted using dot notation, like methods:
public Complex add(Complex other) { return new Complex( real + other.real, imaginary + other.imaginary );}
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 classpublic
: visible everywhereFor brevity, we avoid discussing about syntax: but the Java language specification describes exactly where/how every keyword can be used.
private
fieldsFile 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 */ }}
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):
private
public
private
this
identifierIt 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.
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.
this
for disambiguationSometimes 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!
A class (i.e., a .class
file) exports some identifiers of:
.class
fileAccess modifier can be omitted: default access modifier:
private
: not visible (~ not exported)public
: visibleA package is a set of classes with a name
A package exports the identifiers of its classes:
private
: not visible (~ not exported)public
: visibleThere are no many reasons for using the default access modifier; please always specify one between public
and private
(or protected
, we'll see...)
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
Why package names?
User
, Point
How to name packages? (naming conventions)
it.units.erallab.hmsrobots.objects.immutable
it.units.erallab
: institutionhmsrobots
: productobjects.immutable
: internal organizationBeyond names:
Since Java 9, a new, higher level abstraction for code entities organization has been introduced: Java Platform Module System (JPMS), briefly modules.
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
import
keywordWriting 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
"
It might be useful to "import" all the classes in a package:
import it.units.shapes.*
;Coding conventions suggest not to do it:
java.awt.Event
and it.units.timetable.Event
)import
s for you automatically, and can remove them partially automatically (autocompletion!)import
for the developerimport
is an optimization for the developer:
import
does not import code, classes, objects...
When the compiler processes the source code (.java
) and finds a class identifier C
:
If without FQN, the compiler looks for C
definition:
.java
)java.lang
packageAll classes of the java.lang
packages are available by default:
import java.lang.*
is implicitly present in every source codePackage and class name cannot be identified just by looking at the FQN:
E.g., it.units.UglySw.Point
:
Point
is a classUglySw.Point
and UglySw
are classes (we'll see)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!
static
fieldA field of a class C can be defined with the static
non-access modifier:
From another point of view:
static
field: diagrampublic class Greeter { public static String msg; private String name; /* ... */}Greeter g1 = new Greeter();Greeter g2 = new Greeter();
static
methodAlso a method of a class C can be defined with static
:
The method code cannot access field or use other methods of C that are not static
:
static
method: syntaxpublic 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!Greeter
is involvedstatic
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!)
public class Greeter { public static void main(String[] args) { System.out.println("Hello World!"); }}
Goal: deep understanding of this code
main
signaturepublic class Greeter { public static void main(String[] args) { System.out.println("Hello World!"); }}
public
→ main
has to be invoked "directly" by the JVM upon execution (java Greeter
): it has to be accessiblestatic
→ invokable without having an already existing instance of Greeter
void
→ does not return anythingpublic static void main(String[])
is the signature required by Java if you want to use the method as an execution entry point!
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:println
is static in the class out
)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
System
?public class Greeter { public static void main(String[] args) { System.out.println("Hello World!"); }}
Where is System
?
out
field of System
?There is no import
, hence it can be:
System.class
in the same directory of this .class
, hence we wrote a System.java
System
is in the java.lang
package, that is always imported by default2 holds: java.lang.System
is the FQN of System
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. |
→ 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."
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!")
println(String x)
is the used methodpublic class Greeter { public static void main(String[] args) { System.out.println("Hello World!"); }}
"Involved" classes:
Greeter
System
, String
, PrintStream
System
, String
in java.lang
; PrintStream
in java.io
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 codePrintStream
in the source codeimport
for PrintStream
in System.java
(or FQN)import java.io.PrintStream;/* ... */public class System { public static PrintStream out; /* ... */}
Write an application that, given a word w and a number n, gives n anagrams of w.
"Write an application that, given a word w and a number n, gives n anagrams of w."
Natural language is ambiguous: this description leaves a lot of choices (and hence responsability) to the designer/developer:
It's up to you to take these decisions!
In this particular case:
main()
[A-Za-z]+
)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
Where:
Of what:
String
We'll see later how to do I/O of other things to/from other places
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
BufferedReader
BufferedReader reader = new BufferedReader( new InputStreamReader(System.in));/* ... */String line = reader.readLine();
readLine()
reads one line at once\n
or \r\n
) depending on the host OS how does it know?throws Exception
after the ()
in the signature of main
Scanner
Scanner scanner = new Scanner(System.in);/* ... */String s = scanner.next();int n = scanner.nextInt();double d = scanner.nextDouble();
next()
, nextInt()
, ... do three things:InputStream
it's built on" "
)next*()
String
to primitive typeHere, 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)
Array: fixed-length sequence of objects of the same type
[]
applied to the reference to the arrayString[] firstNames;String[] lastNames = new String[3];lastNames[1] = new String("Medvet");
firstNames
to String[]
; does not create the array; does not create the elementslastNames
to String[]
; create an array of 3 elements; does not create the elementsString
and makes lastNames[1]
(2nd in the array) reference itString[] firstNames;String[] lastNames = new String[3];lastNames[1] = new String("Medvet");
Name of arrays (i.e., identfiers of references to arrays):
Person[] persons
, Person[] employees
, ...Definition:
Person persons[]
is the same of Person[] persons
, but the latter is much better: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";
The type array has a field length
of type int
that contains the array size:
String[] dogNames = {"Simba", "Gass"};System.out.println(dogNames.length); //prints 2dogNames = new String[3];System.out.println(dogNames.length); //prints 3
dogNames.length = 5;
does not compile!
"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);}
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 1max = max(); //values ≅ double[0]; OK for 1max = max(new double[2]{1, 2}); //Ok!; OK for 1 and 2
Since Java 5.0. Mathematically speaking, varargs allows to define variadic functions.
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.
static
?max()
?// does NOT compile!public static int intersectionSize(String... as, String... bs) { /* ... */}intersectionSize("hello", "world", "cruel", "world");
What is as
and what is bs
?
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.
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 WorldHelloWorld
Possible limitations and syntax variations depending on the host OS
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?
String
sCreation (String
constructors):
String s = new String();
String s = new String("hi!");
String s = "hi!";
String s = new String(otherString);
String
methodsMany!
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. |
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...
String
s are immutable1st 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!
String.concat()
String
s: diagramString 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
String[]
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!"
String +
other typeIf one of the operands of +
is of type String
then, at runtime:
String
representation of the operand is obtainedString
s is doneint 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.
A few differences with respect to classes:
new
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?
If not explicitly initialized:
false
for boolean
Consistently, for arrays:
int[] marks;double[] ages = new double[3];
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.
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); }}
=
on primitive typesAssign operator =
copies the content, instead of referencing the same object.
String s1 = "hello!";String s2 = s1;
double d1 = 3.14;double d2 = d1;
Things are a bit more complex, we'll see...
null
A special "value" that can be referenced by a reference of any non-primitive type.
Fields:
null
Local variables:
null
A reference referencing null
is (approx.) not referencing anything.
String s1 = "hi";String s2 = null;String s3 = null;
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;}
String s1 = "hi";String s2 = s1;s1 = null;boolean isNull = s1 == null;
javac
) and execution (java
)Your code is your representation of a problem and its solution.
When someone looks at your code, she/he is entering your mind:
At least 4 sources of mess (increasingly harder to spot):
Code review → the process of checking the code by looking at the source code:
Write an application that, given a word w and a number n, gives n anagrams of w. (Go to detailed specifications, also in next slide)
Hints:
If/when done:
In this particular case:
main()
[A-Za-z]+
)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 GreeterHello World! Today is: Mon Mar 16 10:36:13 UTC 2020
It works!
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
!
println
is invokated at runtime?Mon Mar 16 10:36:13 UTC 2020
is actually a date!)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 GreeterHello World! Today is: Mon Mar 16 10:36:13 UTC 2020
+
operator on String
and Date
:
It is static property of the language
It allows to define a new type A starting from existing type B:
public class Derived extends Base { /* ... */}
We (the developer) mean: "Dear compiler":
Derived
class is identical to the Base
classWe say that Derived
extends (or inherits from, or derives from) Base
.
Object
Every class implicitly derives Object
(when not explicitly deriving from another class):
Object
public class Greeter { /* ... */}
is the same of:
public class Greeter extends Object { /* ... */}
"Surprisingly", Object
is a class, not an object...
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.
Package java.io
Class Reader
java.lang.Object
java.io.Reader
Reader
has all the fields and methods of Object
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
Specified in the doc ("methods declared in class ..."):
Base
developerpublic 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
Derived
developerpublic class Derived extends Base { /* my incremental stuff! */}
I do not need to have the source code of Base
.class
, thoughE.g., "no one" needs the source code of Object
!
public class Derived extends Base { public Derived() { /* init things */ }}
Base
)?Note (before the answers) that:
Base
fields might be private
: what code other than that of Base
can operate on them?Derived
method is called within the Derived()
:Base
methods (it's legit)Base
fields: who should be the responsible for their initialization?public class Derived extends Base { public Derived() { Base(); /* init things */ }}
The compiler inserts an (implicit, wrt to code) call to derived class constructor Base()
!
Base
)?Base()
Derived()
What if Base
does not have the no-args constructor?
The compiler requires the developer to specify which Base
constructor to use
Syntax: super(...)
super()
constructorpublic 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!
super()
constructor: not workingpublic 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!
super()
has to be invoked firstsuper()
constructor: not workingpublic class Base { public Base(int n) { /* ... */ } public Base(double v) { /* ... */ }}
public class Derived extends Base { private int m; public Derived() { m = 0; /* ... */ }}
Not ok!
super()
with no args does not existsuper()
constructor: workingpublic 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!
Which constructor of Base
is invoked?
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." → any of this class!
super()
is executed before any inline initializationWhat is derived.n
?
public class Derived extends Base { /* ... */ }
Any code that works with a Base
can work also with a Derived
.
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!!!
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
Base
is also valid on a Derived
!"has all fields", but recall visibility!
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...
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 Chihuahua
s (but provided that you have the inheritance Dog
→ Chihuahua
).
Similarly:
public Milk milk(Mammal mammal) { /* ... */ }
is better than:
public Milk milk(Cow cow) { /* ... */ }
However:
public Milk milk(Animal animal) { /* ... */ }
is wrong!
public class Derived extends Base { /* ... */ }
Why cannot Derived
remove some methods/fields of Base
?
Why cannot Derived
reduce visibility of methods/fields of Base
?
Because otherwise "Derived
has all fields and methods of Base
" would not be true!
use(new Derived())
, nor println(new Date())
, and inheritance would be useless!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!
It is a dynamic consequence of inheritance:
It results in the same code to have different behaviors depending on the type of the parameter.
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
:
Base
Derived
decided that any Derived
is a Base
Obviously...
public Base createBase() { /* ... */ }
Derived derived;derived = createBase();
Derived derived
: developer → compilerDerived
can be applied to object referenced by derived
derived = createBase()
: compiler → developercreateBase()
might not be a Derived
It might also be a Derived
, but cannot guarantee...
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
?
Derived derived;derived = (Derived) createBase();
What if I (developer) am sure that it is a Derived
?
Syntax: (Derived) base
Derived
derives (directly or indirectly) from the class of base
Why "works only..."?
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.
instanceof
Binary operator (keyword) for checking the type of a object at runtime:
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; // -> trueboolean b2 = derived instanceof Base; // -> trueboolean b3 = derived instanceof Derived; // -> true
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
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()); }}
println
is invokated at runtime?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)
!
Date
has everything an Object
hasprintln()
says it knows how to print on Object
Ok, but then?
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:
toString()
methodtoString()
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()
:
IDEs have an option for writing a decent toString()
for you (Alt+Ins
in Netbeans)
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.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()); }}
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
null
inside println()
Object
toString()
for the restNo need for knowledge about if, how, when, who, where, (and why) would have derived Object
!
System.out.println("Now is " + new Date());
Why does the code compile?
+
with a String
as 1st operand "accepts" anything as 2nd operandWhy and how does it execute?
+ ref
to something like(ref == null) ? "null" : ref.toString()
(if ref
is not a reference to primitive type)At runtime, every object o
has a description of its class:
Class
o
static
fieldIt can be obtained with the getClass()
method of Object
:
Object
, every o
has getClass()
Object
Object
class:
Type | Field | Description |
---|---|---|
Class<?> | getClass() | Returns the runtime class of this Object. |
Ignore the <?>
for now; we'll see it later
Conceptually, inside the Class
of a class 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
Object
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
System.out.println(derived); // -> derived.toString()
public void println(Object x) { if ( x == null ) { println("null"); } else { println(x.toString()); }}
Conceptually:
Class c
of derived
methods
of c
methods
contains toString()
c
to c.superclass
and go back to 2In the worst case, c
is eventually the Class
describing Object
Class
and Object
Class
is a class; Object
is a class.
object
as identifier of a reference to objects of class Object
class
as identifier of a reference to objects of class Class
class
is a keywordMany developers and examples use clazz
...
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 ==
?
==
(and !=
)a == b
a
and b
of different primitive types**, or of primitive and non-primitive**, or of non compatible* non-primitive types, then code does not compilea
and b
of same primitive type, then evaluates to boolean true
iff a
and b
have the same value, otherwise evaluates to false
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
a == b
*: "compatible" means that types are the same or one derives from the other
String s1 = "hi!";String s2 = "hi!";String s3 = s2;boolean b;b = s1 == s2; // -> falseb = s2 == s3; // -> trueb = s1.length() == s2.length(); // -> true
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()
PrintStream
decided that the "equal to" notion of Object
hold for PrintStream
sequals()
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:
x
, x.equals(x)
should return true
.x
and y
, x.equals(y)
should return true
if and only if y.equals(x)
returns true
.x
, y
, and z
, if x.equals(y)
returns true
and y.equals(z)
returns true
, then x.equals(z)
should return true
.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.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:
equals()
Object
workequals()
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
!
equals
Semantics level requirements:
null
Syntax level requirement:
public boolean equals(Object other)
Object
also for the deriving type!The compiler:
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:
x1
and x2
the same X
?E.g., equivalence of persons: are two persons the same?
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:
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".
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".
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
Person.equals()
with fiscal codepublic 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!
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 optimizationgetClass()
different than instanceof
!Objects.equals()
takes care of checking if field is null
Ignore final
for now
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
:
fill()
, sort()
, ...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
instanceof
vs. getClass()
: examplepublic class Person { /* ... */ }
public class Employee extends Person { /* ... */ }
Person p = new Person( /* ... */ );Employee e = new Employee( /* ... */ );boolean b1 = e instanceof Person; // -> trueboolean b2 = p.getClass() == e.getClass(); // -> false
Is an employee equal to the person being the employee? Big question: it depends on how you model the reality.
equals()
on complex typesequals()
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:Z→N}. (bag == multiset)
Equiv. of x1,x2 considers:
public class SequenceOfInts { private int[] values;}
Models X=⋃i∈NZi.
Equiv. of x1,x2 considers:
X "is" the class, x∈X is the object
M(A) is my notation for the set of multisets built with elements of the set A.
X=M(Z)
class BagOfInts { private int[] values;}
X=⋃i∈NZi
class SequenceOfInts { private int[] values;}
Exactly the same code, different models!
Difference in models should be captured by the name of the type!
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() { /* ... */ }}
MotorVehicle
is a Vehicle
Motorboat
is a MotorVehicle
, thus also a Vehicle
Car
is a MotorVehicle
, thus also a Vehicle
.Car
is not a Motorboat
!public class AmphibiousVehicle extends Motorboat, Car { // NOOOO! public void doAmphibiousFancyThings() { /* ... */ }}
Does not compile! No multiple inheritance!
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
?
startEngine()
, since both Motorboat
and Car
extends MotorVehicle
: which one should be invoked? same for equals()
, toString()
, ...Java syntax does not allow the developer to specify which one!
"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()
!
C
does not compile anymore!There is a mechanism in Java for modeling the amphibious; we'll see it!
equals()
toString()
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
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?
By value:
By reference:
In Java:
public void modify(Person p) { // B p.setName(p.getName().toUpperCase());}
Person eric = new Person("Eric"); // Amodify(eric); // C
At A:
At B:
By reference:p
is a copy of eric
At C:
Change is visible!public void uselesslyModify(int n) { // B n = 18;}
int age = 41; // AuselesslyModify(age); // C
At A:
At B:
By value:41
is a copy of 41
At C:
Change is not visible!final
A modifier (keyword):
Different effects depending on type of definition:
final
classCannot be extended!
final
classpublic final class Greeter { /* ... */}
public class EnhancedGreeter extends Greeter { /* ... */}
EnhancedGreeter
does not compile!
final
methodCannot be overriden!
final
method of the extended classpublic class Greeter { public final String greet() { return "Hello!" };}
public class EnhancedGreeter extends Greeter { public String greet() { /* ... */ }}
EnhancedGreeter
does not compile!
public final String greet()
final
fields and variablesCannot be reassigned!
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:
n = 18
s = "hello"
final
?Recall, compile-time only effects!
final
thingsUse cases:
final
Object-wise constant:
final
usually private
Class-wise constant:
final static
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":
Person
, birthDate
and fiscalCode
never change!Person
, firstName
and lastName
can changefinal
fields have to be initialized!
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
public
if meant to be used outside (e.g., Math.PI
)Naming convention is different for final static
constants:
_
(underscore)When parameter is a primitive type:
public void update(final int n) { /* ... */}
Reminds me (the developer) that changing n
would not have effect!
n
In some cases, the IDE suggests to add the final
modifier.
public void capitalizeName(final Person p) { p.setName( p.getName().substring(0, 1).toUpperCase() + p.getName().substring(1).toLowerCase() );}
No reason for reassigning p
!
p
, you were modifying another Person
object, not on the passed one (meant to be modified)!Person[] teachers = new Person[] {alberto, eric};for (final Person teacher : teachers) { capitalizeName(teacher);}
No reason for reassigning teacher
!
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:
At B, 2nd iter., assume eric
is ill:
Scope of an identifier:
Lifetime of an object or reference:
In general:
Declared in method signature (aka argument variable):
Declared in method code (aka local variable):
{
}
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;}
string
, capitalized
, token
, head
, remaining
What is the scope for each identifier (line of start, line of end)?
Do you see other issues in this code? (At least 4...)
Determined by access modifier:
private
, protected
, default, public
protected
:
protected
is usedFields are also known as instance variables
Reference and primitive object:
Non-primitive object:
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:
At B:
At C:
JVM memory is organized in:
Many other differences that we do not need to know.
Stack has faster access; one stack per thread; stack is much smaller
Stored in heap:
new
Stored in stack:
Heap:
Stack (organized in blocks):
Primitive type objects in the stack: the identifier identifies ("is") the object, instead of the reference
This explains the differences:
==
compares contentAnd:
null
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 knows in advance how much stack reserve for a method invocation!
public static void main(String[] args) { int l = args.length;}
eric@cpu:~$ java Greeter eric simba
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}
Also String
has its fields; we omit for compactness.
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}
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}
doThings()
block in the stack freed just after invocationString "simba"
no more referenced, hence useless → garbagepublic class Person { private int age; private String name; private Person[] friends;}
//main()Person eric = new Person();
length
field for arrays omittedThe JVM itself takes care of removing the garbage from the heap:
The component of the JVM doing this cleaning is the garbage collector (GC)
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...
Before GC, the developer was responsible for freeing unused memory.
malloc()
→ free()
Responsability → source of problems, when misbehavior
free()
→ out of memoryfree()
on wrong address → invalid writemalloc()
→ possible chaosNo more problems with automatic garbage collection!
GC can be tricky:
Doing GC takes time!
My be undesired in very specific scenarios.
But:
System.gc()
and hoping for the bestJVM (java
) parameters:
-Xms
-Xmx
-Xss
eric@cpu:~$ java MyBigApplication -Xmx8G
When exceeded:
java.lang.OutOfMemoryError: Java heap space
java.lang.StackOverFlowError
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:
String
s)Wrapper classes
Integer
for int
with different nameDouble
for double
Boolean
for boolean
Character
for char
with different nameThere's also a Void
class
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. |
"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. |
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!
Integer i = Integer.valueOf(2);i = Integer.valueOf(i.intValue()+1);
int i = 2;i++;
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!
int
when possible!Integer
, use ==
with care!equals()
!What happens with (n+1)==(m+1)
?
IDEs sometimes warns about misuse of ==
We already know how to do I/O
String
s)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?
Stream: endpoints, direction, type of data
Two basic models:
byte[]
byte[]
Usage (key steps):
byte[]
Device: file, "network", ...
Other more complex models in which there is some processing of the data:
byte[]
byte[]
to/from other typesInputStream
and OutputStream
All I/O streams extend one between InputStream
and OutputStream
.
They can be composed:
OutputStream
s
InputStream
s
No need to change the code the "user" of the stream is a developer:
byte[]
) data types ← compositionA lot!
We'll see a subset, for most common
byte[]
OutputStream
classThis 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. |
byte[]
OutputStream
is abstract, cannot be instantiatedOutputStream.nullOutputStream()
gives a device discarding written byte[]
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)
write()
byte[] data = /* ... */OutputStream os = /* ... */os.write(data, 0, 3); //Aos.write(data, 3, 2); //Bos.write(new byte[2]); //C
The device abstraction (might be a real device, or another OutputStream
) of os
:
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:
OutputStream
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)
.
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 ofOutputStream
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.
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
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);
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. |
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()
.
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, ...
close()
byte[] data = /* ... */OutputStream os = /* ... */os.write(data, 0, 3); //Aos.close(); //B
Usually, no other write()
are possible after EOS has written.
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 ofOutputStream
does nothing.
OutputStream
is the base class, no specific device:
ByteArrayOutputStream
has no effect. The methods in this class can be called after the stream has been closed [...]"InputStream
classThis 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. |
byte[]
InputStream
is abstract, cannot be instantiatedInputStream.nullInputStream()
gives a device with no bytes to read (EOS is at position 0)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)
read()
byte[] buffer = new byte[100];InputStream is = /* ... */ //Ais.read(buffer, 0, 4); //Bis.read(buffer, 4, 1); //C
The device abstraction (might be a real device, or another InputStream
) of is
:
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);
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)
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" → the device!
Constructors:
Constructor | Description |
---|---|
ByteArrayInputStream(byte[] buf) | Creates a ByteArrayInputStream so that it uses buf as its buffer array. |
OutputStream
os.write(new byte[2]);os.write(new byte[3]);
No limit* on writeable data:
*: in principle
InputStream
is.read(new byte[2]);is.read(new byte[3]);
Readable data is limited:
byte[]
Input from file with FileInputStream
:
FileInputStream(File file)
we'll see a File
isInput from byte[]
with ByteArrayInputStream
:
ByteArrayInputStream(byte[] data)
byte[]
Output to file with FileOutputStream
:
FileOutputStream(File file, boolean append)
Output to byte[]
with ByteArrayOutputStream
:
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); //?
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...
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:
len
ready, read len
bytes and return immediately len
len
bytes and return immediately nSuppose the device:
is.read(buf, 0, 3);// t=0 -> t~0, ret 3is.read(buf, 0, 3);// t~0 -> t~0, ret 2is.read(buf, 0, 3); // BLOCK!// t~0 -> t~10, ret 3is.read(buf, 0, 3);// t~10 -> t~10, ret -1
read()
"The device receives": the InputStream
receives for the underlying level, eventually from a physical devices
Keyboard → InputStream System.in
InputStream
receives bytesNetwork connection → InputStream getInputStream()
read(byte[] b, int off, int len)
in InputStream
:
The
read(b, off, len)
method for classInputStream
simply calls the methodread()
repeatedly.
Mod. and Type | Method | Description |
---|---|---|
abstract int | read() | Reads the next byte of data from the input stream. |
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?
-1
) representing EOSbyte
does not include -1
!-1
in read()
and write()
Other alternatives for representing EOS:
Byte
, null
for EOSJava inventors chose to use an int
as return value
read(byte[], int, int)
also returns an int
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. |
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.
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.
(a rather long description follows)
File
system-independent viewNo methods for reading and writing!
Methods for:
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. |
FileOutputStream
FileOutputStream(File file)
and FileOutputStream(String name)
:
file
/name
does not exist, creates new one if OS says it's possible→ existing content is cancelled!
boolean append
!We want to develop an application that:
eric@cpu:~$ java FileCopier slides.zip copy-of-slides.zip
FileInputStream
from String
f1; build FileOutputStream
from String
f2byte[]
from FileInputStream
byte[]
to FileOutputStream
"Iterate until EOS"
Suppose to use a buffer of n bytes (i.e., to read n bytes for iteration):
-1
→ EOSpublic 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.
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):
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" → portable way
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"
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.
Java developers built DataInputStream
and DataOutputStream
together:
byte[]
accordinglybyte[]
in the same way⇒ portability!
E.g.:
But...
Portability is granted for the single data item!
At the application level, parties (reader and writer) have to act accordingly!
Alice and Bob meet on chat:
Creative Bob:
What happened?
Application saves data on file:
Writes 16 bytes:int
double
int
Application loads same file:
Reads 12 bytes:int
float
int
Writes 16 bytes:
int
double
int
Reads 12 bytes:
int
float
int
Read data "is different" than written data:
FileArray
that mantains a binary file-backed array of int
values, with constructors/methods to:FileArray
i
for incrementp
for printFileArray
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:
Random
.nextInt()
int[]
int[]
→ fileFileArray
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:
p
, 3 at 2nd p
)Hint: use printf
or String.format()
, e.g., %3d
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. |
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. |
There are also ZipOutputStream
and ZipInputStream
:
GZIP
streams "just" compress/decompress binary dataZip
streams operate with filesGZIPFileArray
that extends FileArray
and mantains a compressed binary file-backed array of int
valuesFileArray
(see specifications)FileArray
or GZIPFileArray
.zip
, use GZIPFileArray
, otherwise use FileArray
Most devices are actually read/written by the OS: not, e.g., ByteArrayOutputStream
Applications may need to read/write data chunks with size sa=so
Application to OutputStream
:
write()
100 bytes, write()
1000 bytes, write()
100 bytesOutputStream
(JVM) to OS:
write()
100 bytes, read()
1000 bytes, write()
100 bytesOS to device:
A filter stream with a buffer.
BufferedOutputStream
:
BufferedInputStream
:
Assume buffer of 1000 bytes.
Application to BufferedOutputStream
:
write()
100 bytes, write()
1000 bytes, write()
100 bytes, ...BufferedOutputStream
to OutputStream
and OutputStream
to OS:
write()
1000 bytes, ...Assume buffer of 1000 bytes.
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, ...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
.
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()
.
flush()
BufferedOutputStream
:
flush()
public void flush()
Flushes this buffered output stream. This forces any buffered output bytes to be written out to the underlying output stream.
flush()
propagates down to the JVM-OS boundary, not beyond!Always!
OutputStream os = new BufferedOutputStream( new FileOutputStream(file));
Should you flush()
?
close()
flush()
needed only for output stream:BufferedInputStream
reads from below more at least the requested data, possibly morebyte[]
char[]
A char
is encoded with one or more bytes
: the precise way each char is encoded is specified in a charset.
Charset
classCharset
Many application do I/O of text data, rather than binary data:
The JDK provides classes for text I/O that take care of byte[]
↔ char[]
.
"Same" abstraction of byte[]
streams, but with char[]
:
Writer
, Reader
instead of OutputStream
, InputStream
Output of text:
Input of text:
] ]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. |
char[]
are String
swrite(String str)
might use String
toCharArray()
:
public void write(String str) throws IOException { write(str.toCharArray());}
String
s are CharSequence
s:
public Writer append(CharSequence csq) throws IOException { write(csq.toString());}
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
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.");
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();
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
.
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 usingtoCharArray()
andtoString()
.
Note: Invokingclose()
on this class has no effect, and methods of this class can be called after the stream has closed without generating anIOException
.
StringWriter
:
A character stream that collects its output in a string buffer, which can then be used to construct a string.
Closing aStringWriter
has no effect. The methods in this class can be called after the stream has been closed without generating anIOException
.
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()
.
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:
String
!FileReader
FileReader(File file, Charset charset)
InputStream
with InputStreamReader
InputStreamReader(InputStream in, Charset cs)
char[]
and String
:CharArrayReader(char[] buf)
StringReader(String s)
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
null
if EOS is reached (null
acts as -1).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.
PrintStream
An output stream (extends OutputStream
) that:
String
s, objectsSystem.out
is a PrintStream
No corresponding class for input:
System.in
is a "plain" InputStream
with no added methods and with exceptionsPrintStream
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:
Charset
argument, for conversion to stringsMod. 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...
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()
.
Basic notions:
TCP connection: a pair of "pipes" transferring bytes
Server: a process waiting for connection requests on a port
Client: a process requesting a connection to a server
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.
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 */
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 */
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.
Protocol (upon connection):
BYE
, server closes connection; otherwise waits for next lineServer:
No bad things can happen: e.g., client does not close the connection.
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(); } }}
import
s omitted for brevity
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
andOutputStream
.
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(); }}
import
s omitted for brevity
Protocol (upon connection):
Server:
Simple
prefix...String
→ String
, lquit, port number are parametersNo bad things can happen: e.g., client does not close the connection.
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; }}
p: String
→ String
is a parameter.
p is valued by overriding process()
while extending SimpleLineProcessingServer
:
protected String process(String input) { return input.toUpperCase();}
We'll see another, radically different, option.
Log on an OutputStream
received as parameter:
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.
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:
No need to close ps
here, because the server is supposed to never terminate.
run()
→ 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
.
SimpleLineProcessingServer
with a main()
) that:"bye"
flush()
callIt likely won't work on repl.
Protocol (upon connection):
Protocol detail: "send real vector" v
int
(4 bytes) n=∣v∣double
s (8 bytes each)Server:
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
.
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
:
double[] readDoubleArray()
and void writeDoubleArray(double[])
Previous examples implicitly defined a message-oriented protocol:
SimpleLineProcessingServer
: a message is a text lineSimpleRealVectorProcessingServer
: a message is vector encoded in a specified wayTCP is not message-oriented!
TCP guarantees that:
but it does not guarantee that one write()
of m bytes on one endpoint corresponds to exactly one read()
of m bytes on the other endpoint!
In practice:
read
s might be needed to collect m bytesClient C:
byte[] oBuffer = /* ... */os.write(oBuffer);
Assume oBuffer
is l≤ 1024 at runtime.
Server S connected to C:
byte[] iBuffer = new byte[1024];int n = is.read(iBuffer);
n
might be something between 1 and liBuffer
might actually contain only a (leading) portion of what oBuffer
contained on the other endpoint!Client C (oBuffer1
, oBuffer2
lengths are l1, l2):
os.write(oBuffer1);os.write(oBuffer2);
Server S connected to C:
int n1 = is.read(iBuffer1);int n2 = is.read(iBuffer2);
Possible outcomes:
n1
=l1, n2
=l2 (fluke!)n1
≤l1, n2
≤l2n1
>l1, n2
≤l2−( n1
−l1)byte[]
messagesYou 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.:
BufferedReader
reads as many bytes for reaching the new line characterDataInputStream
) specify how many other bytes need to be read (at least)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.
⇓
This server handles at most 1 client at a time.
Possible other clients hang until the handled one disconnects.
accept()
handleClient()
bye
; S goes to accept()
This server is not particularly useful!
Simple
...Server
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+n concurrent processes:
accept()
)handleClient()
)Thread = execution flow
Process vs. thread:
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.
Thread
and overrides run()
.start()
(that will invoke run()
)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]: 0Thread[Thread-1,5,main]: 0Thread[Thread-0,5,main]: 1Thread[Thread-1,5,main]: 1Thread[Thread-0,5,main]: 2Thread[Thread-1,5,main]: 2Thread[Thread-0,5,main]: 3Thread[Thread-1,5,main]: 3Thread[Thread-0,5,main]: 4Thread[Thread-1,5,main]: 4Thread[Thread-0,5,main]: 5...Thread[Thread-0,5,main]: 9Thread[Thread-1,5,main]: 9
The JVM executes until the last thread has ended.
start()
SlowCounter c1 = new SlowCounter();SlowCounter c2 = new SlowCounter();c1.start();c2.start();
When start()
is invoked:
run()
Thread is the execution flow; Thread
is the class.
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]: 0Thread[Thread-0,5,main]: 1Thread[Thread-0,5,main]: 2Thread[Thread-0,5,main]: 3Thread[Thread-0,5,main]: 4Thread[Thread-0,5,main]: 5Thread[Thread-0,5,main]: 6Thread[Thread-0,5,main]: 7Thread[Thread-0,5,main]: 8Thread[Thread-0,5,main]: 9Thread[Thread-1,5,main]: 0Thread[Thread-1,5,main]: 1Thread[Thread-1,5,main]: 2Thread[Thread-1,5,main]: 3Thread[Thread-1,5,main]: 4Thread[Thread-1,5,main]: 5Thread[Thread-1,5,main]: 6Thread[Thread-1,5,main]: 7Thread[Thread-1,5,main]: 8Thread[Thread-1,5,main]: 9
Each thread:
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:
out
is static
in System
!
Assume C extends Thread
is the class that does the job concurrently.
Put in C constructor all and only the things that has to be shared.
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(); } }}
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
throws IOException
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...
try
, catch
, finally
... because throws
cannot be used; we'll seeLineProcessingServer
ClientHandler
process()
LineProcessingServer
A bit cumbersome:
The same method of the same object can be executed at the same time by different threads!
Idea:
ClientHandler
just knows how to handle one clientLineProcessingServer
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; }}
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 { /* ... */ } }}
LineProcessingServer
LineProcessingServer
and overrides process()
Elegant!
Note:
process()
at the same timePossible source of big problems!
The JVM:
At each time step:
⇒ threads execute concurrenly only apparently!
Suppose:
Some possible actual executions:
Is a2,b1,a1,c2,b2,c1 possible?
One Java statement:
System.out.printf("a+b=%d%n", a + b);
→≫1 bytecode instructions!
Even worse!
Counter
examplepublic 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
.
synchronized
Methods defined with the synchronized
modifier cannot be executed on the same object by more than one thread at the same time.
Atomicity is guaranteed just on the method!
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!
A method invocation outcome in case of multiple threads:
The method/class is said to be not thread-safe.
Since atomicity is a common requirement, the JDK provides:
AtomicInteger
synchronized
blocksynchronized
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()
?
The old way of dealing with errors:
Example, a function:
fwrite
#include <stdio.h>size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
This function writes size
* nmemb
bytes from ptr
to stream
.
The number of items of size nmemb
written or -1 on error.
"or -1 on error" is the convention
atoi
#include <stlib.h>int atoi(const char *str);
Converts as much of the string as possible to an equivalent integer value.
The equivalent value, or zero if the string does not represent a number.
"or zero [...] if [...]" is the convention
"caller has to explicitly check for errors"
What if the developer of the caller code does not check?
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.
The language:
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.
Suppose:
If m has an anomalous flow:
Otherwise:
If an error occurs in m:
then the execution halts immediately.
⇒ impossible that an error goes unhandled causing (bigger) errors later!
Language:
Design:
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.
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.
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
):
try
-catch
How to define the normal and anomalous flow?
doThings() { try { /* N */ } catch (UnknownHostException e) { /* A1 */ } catch (InvalidTypeException e) { /* A2 */ }}
UnknownHostException
occursInvalidTypeException
occursIf exception e of type E occurs while executing the normal flow, the JVM checks (in order) if one catch
block exists for E:
catch
blocke
references the object e of type EdoThings()
carrying the exception eThe compiler:
try
block for which there is a catch
for any possible exception generated by C (with inheritance) (exception catched)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 { /* ... */}
throws
and inheritancethrows
clause is part of the method signature.
If a class C′ extends a class C with a method m, C′ cannot "increase" the throws
clause of m.
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".
throws
clauseOk! 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 Exception
s, but throws only ForbiddenByUnionsException
.
public class Worker { void work() throws Exception { /* ... */ }}
public class PreciseWorker extends Worker { void work() throws ForbiddenByUnionsException { /* ... */ }}
ForbiddenByUnionsException
extends Exception
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
e statement, the anomalous flows starts with the exception e.
try
-catch
, hence the method is defined with throws
E (with e of type E, or of subclass of E)throws
vs. throw
throw
is an action:
throws
is part of a definition:
The compiler checks if exception is absent, catched, or declared.
It also considers:
Exception
spublic 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.public void showName(String fullName) throws MalformedNameException { System.out.println(fullName);}
Compiles, but IDEs usually warn that code does not throw MalformedNameException
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.
public void showName(String fullName) { try { System.out.println(fullName); } catch (MalformedNameException e) { /* ... */ }}
Does not compile because:
MalformedNameException
public void showName(String fullName) { try { System.out.println(fullName); } catch (Exception e) { /* ... */ } catch (MalformedNameException e) { /* ... */ }}
Does not compile because:
catch
catches "everything", second one cannot be triggeredfinally
try { /* normal flow */} catch (Exception e) { /* anomalous flow */} finally { /* finally flow */}
Allows to define a trailing execution flow that:
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
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
doThings()
externalDoThings(0)
invoked:
en1 n1 f ea en3
doThings()
The condition of a flow of being anomalous is relative, not absolute:
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
e
and furtherE
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:
Throwable
s (and subclasses) can be argument of throw
, throws
and catch
Exception
(and subclasses) has to be catched or declared, unless it is a RuntimeException
(or subclasses)RuntimeException
s are unchecked exceptionsThrowable
is also extended by Error
:
Error
s (and subclasses) are unchecked exceptions tooRuntimeException
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.
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:
null
object.null
object.null
as if it were an array.null
as if it were an array.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 nulls = s.toUpperCase();
int[] a = //.., rets nullint l = a.length;
int[] a = //.., rets nulla[0] = 0;
throw
, it is the JVM itself that generates the NullPointerException
(in these examples).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.
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"
Error
: "problems that a reasonable application should not try to catch"
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.
Throwable
(and subclasses) usageConstructors:
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)
):
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
throw
is, when not a JVM thrown throwableLanguage:
Throwable
, Error
, Exception
, RuntimeException
try
-catch
-finally
throw
Design:
Design:
No, one-fits-all answers.
When to handle an error?
I.e., an exception of type E in a method m.
Key idea:
E.g.: String getWebPageTitleIfAny(String url)
, with MalformedURLException
IfAny
): catch and return ""
E.g.: Configuration loadFromFile(String fileName)
with FileNotFoundException
Configuration
to return: propagateA methodology for software design: here just in a nutshell.
For each method define:
Then, the method should return abruptly in anomalous state (i.e., throw or propagate an exception, in Java) if and only if:
Some IDEs help the developer in adopting this methodology.
How to handle an error?
When to "create" an error?
(beyond propagation)
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];}
Good practice (in general): hide inner implementation
If component/library/sofware S finds an exception E′ related to specific component/library/sofware S′ used in S, it should not propagate it, but should instead create a new exception E related to S.
Code using S should not be needed to know S′.
Never catch without handling (just to make the code compile)!
try { doRiskyThing();} catch (BadThingHappenedException e) { // ignore}
Never means never:
Thread
sIf you don't know how to handle, you shouldn't catch.
e.printStackTrace()
.Thread
s and missed handlingrun()
cannot be redefined with a throws
clause:
⇒ every exception has to be handled
If no methods handles the exception (e.g., NullPointerException
), the thread is interrupted:
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.
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 */}
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.
It's a matter of responsability:
In practice:
Premise 1: typical workflow
Premise 2: anomalous outcome
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.
⇒ a long-running process has the responsability or closing its resources.
Always close!
"Always" means also in anomalous flow!
try { Resource r = open(); r.use(); r.close();} catch (AException e) { log(e); r.close();} catch (BException e) { log(e); r.close();}
Problems:
ExceptionC
is thrown (e.g., NullPointerException
)?r
is not defined in catch
blocksclose
code repeated 3 timesopen()
, use()
, close()
are not actual method invokations; they are placeholders.
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:
open()
?r
is not initialized; r.close()
will throw a NullPointerException
!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...
try (Resource r = open()) { r.use(); } catch (AException e) { log(e);} catch (BException e) { log(e);}
try
-with-resources
try
gets a declaration argument where one or more resources are:
Resource r
)r = open()
)When exiting the try
block, r
is closed (if not null):
catch
block, if anyfinally
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.
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):
throws
clauseEvery class that implements the AutoCloseable
interface.
(We still do not know interfaces, but...)
In practice, every class with a close()
method.
Reader
s, Writer
s, ...AutoCloseable
Protocol (upon connection):
Server:
String
→ String
, lquit, port number are parametersRobustLineProcessingServer
public class RobustLineProcessingServer extends LineProcessingServer { public RobustLineProcessingServer(int port, String quitCommand) { super(port, quitCommand); } public void run() throws IOException { /* ... */ }}
extends LineProcessingServer
protected
in superclass)process()
and getQuitCommand()
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); } } }}
ServerSocket()
may throw an IOException
, but we do not know how to manage it (cannot meet postcondition "stay alive"):accept()
may throw an IOException
: server still need to stay alive:
⇒ handletry
-with-resources: close()
will be managed by ClientHandler
RobustClientHandler
public class RobustClientHandler extends ClientHandler { public RobustClientHandler(Socket socket, LineProcessingServer lineProcessingServer) { super(socket, lineProcessingServer); } public void run() { /* ... */ }}
extends ClientHandler
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 connectionline.equals()
would have thrown a NullPointerException
what if not handled?try (socket)
: possible since Java 9IOException
and networkpublic 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+1-th readLine()
:
TCP guarantees that:
... if no problems occur.
Otherwise, OS notifies the application (here, the JVM) that TCP failed.
Client C: write request, read response.
Server S: read request, write response.
Iteration in detail:
readLine()
IOException
!C never sends a request before consuming a response: hence if readLine()
worked with the n-th request, C received the n−1-th response.
Pipelined message-oriented protocol:
Client C: write k requests, read k responses.
Server S: read h requests, write h responses.
It depends on the application.
Request examples:
For 1, C can simply send again the request.
For 2 and 3, much harder.
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
?
.class
contentA .class
contains:
When JVM starts executing Main.class
:
Main.main()
Greeter()
, Greeter.greet()
The JVM needs to use a .class
file when:
Whenever the JVM needs to use a .class
file:
Hence, a .class
is loaded in memory at the first time:
main()
)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 Mainhi Eric
Sequence of loaded classes (approximation):
String.class
Object.class
Main.class
Greeter.class
System.class
PrintStream.class
Recall: every constructor starts by invoking the constructor of the superclass (possibly implicitly).
Static consistency:
javac
at compile timeDynamic consistency:
java
at runtimeBoth are checked using .class
files!
Main.java
, javac
checks Greeter
usages using Greeter.class
Main.class
, java
checks Greeter
usages using Greeter.class
.class
contentA .class
contains:
What if static consistency not verified?
What if dynamic consistency not verified?
.class
not found → ClassNotFoundException
.class
→ NoSuchMethodException
.class
→ NoSuchFieldException
Suppose you wrote Main.java
and Greeter.java
.
While compiling Main.java
to Main.class
, javac
needs Greeter.class
:
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?
.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.
.class
files?Many options:
.java
files (old way).java
one (i.e., based on packages) but rooted elsewhere (e.g., at ~/java-projects/APFinalProject/target/
) (common easy way).jar
file placed properlyMultiple options can hold together, usually for different kind of classes:
Placing of .class
is part of a complex build process that is managed by one or more of:
Big question: we'll not give an answer here.
For 2 and 3, good code is the first source of knowledge:
After that, knowledge can be transferred in many ways:
In some cases, providing an API documentation (aka javadoc) may be useful.
javadoc
.java
filesjava
files (with or without documentation) to a set of web documents using javadoc
Both steps are largely assisted by/automated with IDEs.
javadoc
like documentationpackage 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; }}
/**
marks the start of a javadoc comment (normal block comments start with /*
)@
interface
Defines only the signature of zero or more methods:
public interface Listener { void listen(Event event);}
class
espublic
is unnecessaryprotected
and private
modifiersListener.class
This might be an example of a code following the observer pattern; Event
should be properly defined elsewhere.
"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:
Non public
methods cannot be used from outside:
⇒ they are an inner detail
"Defines"
interface
s may also define methods code: default
methods and static
methods; we'll see, briefly.Serializable
, Cloneable
, ...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:
public
)throws
clauseSimilar 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); }}
Interface cannot be instantiated!
Listener listener = new Listener(); // does not compile!
Which code should be executed when listener
will be invoked?
interface
s define only signatures!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!")}
Implementing the same interface
does not establish inheritance-related relationships.
NullListener listener = new StandardOutputListener(); //does not compile!
StandardOutputListener listener = new NullListener(); //does not compile!
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)
"may extend one or more"
Why allowed with interface
s and not with class
es?
Suppose a class C extends C′ and C′′ and both have a method m with the very same signature
Suppose an interface I extends I′ and I′′ and both have a method m with the very same signature
Key motivation: define something that can perform some operations, without describing how they have to be performed
public interface Listener { void listen(Event event);}
Definition: "The listener listens"
Then, you can build whatever complex functionalities based on this definition.
It's like when you manipulate mathematical concept:
"let f:R→R be a function"
then you continue your reasoning and complex theory
without the need to specify the actual form of f!
new
Given an interface I and a sw S doing complex things with that I:
Example:
interface Listener
class StandardOutputListener implements Listener
class CloudLogger implements Listener
Separation of concerns: x is concerned only about being x!
new
A!S need not to be updated for using B!
I is the point of contact (i.e., interface) between S and A/B
The "same" goal might be achieved, in principle, with inheritance:
public class Listener { public void listen(Event event) { }}
Key differences:
void
return? how to set it in empty behavior?new
with interfacesA
doing operations of A and class B
doing operations of B, how should one define C doing operations of both A and B?Protocol (upon connection):
Server:
String
→ String
, lquit, port number are parametersp: String
→ String
What is p? 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(); }}
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
.
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
?
abstract
Modifier that can be applied to class and method definitions.
abstract
method:
abstract
class:
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 Abstract
X
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; }}
interface
s: 100% abstraction
abstract
classes: ≤ 100% abstraction
Actually, with default
and static
methods, interface
s may define some functionality
Both cannot be instantiated!
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);}
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"At compile time:
final
(or effectively final)At runtime: no differences wrt "normal" definition:
Main$1
, depending on where it is defined)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(); }});
default
and static
methodsModifiers for methods defined in interface
s:
default
methods provide a default implementation; implementing class may or may not override itstatic
methods provide a static method (like for regular class methods)(Since Java 8)
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 methodthis
inside the anonymous class would refer the "anonymous" objectListener listener = Listener.nullListener();
Listener listener = new StandardOutputListener() .andThen(new FileListener(file));
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(); } } }}
final
: effect is on called, not on callerListener 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)
!
abstract
vs. default
(and static
)Apparently, they achieve the same goal:
Key differences:
abstract
class requires all inherited types to be recompiled; not required for default
methodsSome risk of misusage:
enum
A type representing a predefined (by developer) set of values.
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:
_
, like constantsenum
s 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 enum
s, ==
works like equals()
.
Enums cannot be instantiated:
new Gender()
does not compile!enum
s 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.
.
enum
s can have methods, fields, constructors.
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);}
Very very briefly.
For basic usage, no need to define new annotations.
public class StandardOutputListener implements Listener { @Override public void listen(Event event) { System.out.println(event); }}
Says to the compiler:
@Override
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
Thread
with a Runnable
(Thread(Runnable)
constructor) and then invoking start()
Those interfaces model functions:
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(); } });
(Or, simply, lambdas; since Java 8)
A syntactic shorthand for implementing functional interfaces annotated with @FunctionalInterface
:
@FunctionalInterfacepublic interface CommandProcessor { String process(String input);}
CommandProcessor p = (String input) -> { return input.toUpperCase()};LineProcessingServer server = new LineProcessingServer(10000, "BYE", p);
Main option:
->
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!
@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:
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.
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();
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
.
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);
@FunctionalInterfacepublic 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; }; }}
.composeWith(
g)
gives f∘g, with (f∘g)(x)=g(f(x)).integrate(
x0,
Δx)
givesBoth return a function!
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!
Goals:
Largest concrete exploitment:
java.util.stream
: we'll see themSuppose we want to design a class that:
SetOfStrings set = new SetOfStrings();set.add("hi");set.add("world");set.add("hello");set.add("world");System.out.println(set.size()); // -> 3System.out.println(set.contains("dog")); // -> false
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:
contains()
and add()
check for equal string in arrayPerson
?Suppose now we want a set of Person
s.
Two options up to Java 1.5 (excluded):
SetOfPersons
almost identical to SetOfStrings
SetOfThiss
, a SetOfThats
, and so on...Set
that takes objectsThe 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
.
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; }}
SetOfStrings
:
add()
and size()
operationsSetOfStrings strings = new SetOfStrings();strings.add("hi");strings.add(new Person()); // does not compile!
Set
:
add()
and size()
operationsSet persons = new Set();persons.add(new Person());persons.add("surprise!"); // does compile!!!
It'd be a responsability of the developer to correctly use Set
.
"Usual" type consistency check:
doStuff(String)
method, when used, takes objects of type String
(or extends String
)"This type consistency check:
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.
Generic type: a type defined with one or more other types as parameter
E.g., "a set of strings":
Language:
Design:
Generics are an old concept in programming (see Generic Programming):
Java designers avoided, for a while, the costs of integrating generics into the language.
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:
T
for typeT
can be used wherever a type would be used:
But:
T
cannot be used as constructornew T()
does not compile: what constructor should be invoked? what if not legit?new T[]
does not compileT
cannot be valued to primitive typeSet<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
:
This would not compile:
Set<String> strings = new Set<String>();strings.add(3d);strings.add(new Person("Eric", "Medvet"));
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!
<>
(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.
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[]
:
Set<T>
(information hiding)interface
@FunctionalInterfacepublic 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:
A
, returns a B
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
Methods (static or not) can have "local" type parameters:
@FunctionalInterfacepublic 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?
Set set = new Set();set.add("hi");set.add(3.14); //autoboxingset.add(new Object());
Legit, but the compiler emits a warning ("raw usage of parametrized type").
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); //autoboxingset.add(new Object());
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:
Worker
able to do all the things that a Person
can do?" yes!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:
<T>
defines an identifier: T
<T>
uses an identifierpublic class StringContainer implements Container<String> { /* ... */}
StringContainer
is not a generic type!
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>();
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
"
persons
is a Container
"ArrayContainer<Person> persons = new ArrayContainer<>();
Means: "I need to use ArrayContainer
operations; Container
ones are not enough"
extends
in parameter typeA 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:
Union
makes sense only "of some kind of Worker
s" and they have the same type (or subtypes)Worker
sFor this usage, extends
is used also with interface
s.
public class ComplexStuff<T extends Doer> { /* ... */ }
?
In some cases, any type is ok:
public static int count(Set<?> set) { /* ... */ }
public static void removeShorterThan( Set<? extends Sized> set, double size) { /* ... */ }
Basically, every time you are modeling a X of Y!
Particularly powerful with interface:
interface
Y declares some capabilitiesBy decoupling X from Y, software may be made much clearer!
Function<T,R>
Comparable<T>
Given a and b:
interface Comparable<T>
models this with a single method:
int compareTo(T other)
this
precedes other
this
succeeds other
interface Comparator<T>
"Similar" to Comparable<T>
int compare(T t1, T t2)
Comparable
default
methodsComparator<T>
, Comparable<T>
:
Comparator<T>
is a @FunctionalInterface
Why?
Comparator<T>
Natural ordering of a type is the ordering dictated by its compareTo()
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 }}
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 argumentsPerson::getBirthDate
is a Function<Person, Comparable>
Result:
Model the concept of collection: groups of homogeneous items
Examples:
In the JDK:
Interfaces:
Classes, differ mainly in:
interface Collection<E>
:
E
stays for elementinterface Map<K,V>
:
K
stays for key, V
stays for elementCollection
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.
interface Collection<E>
: key methodsMod. 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. |
add()
description: "ensures that [...] contains"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 soundadd()
and remove()
mutator methods, since they (potentially) modify the state, i.e., the groupboolean
return value specifies if the collection actually mutatedMod. 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. |
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[]
?
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.
4 ways:
toArray()
and then for (int i = 0; ...)
: ancientiterator()
: very oldtoArray()
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.
Naming convention: plural form of contained type:
Person
→ persons
(or qualified: nicePersons
)population
would be okAxis
→ axes
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}
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>
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.
Never assume that iterating over a collection will result in some specific ordering! Neither with repeatability!
Collection<String> strings = /* ... */string.add("hi");string.add("world");for (String string : strings) { System.out.println(string);}
hi
, world
world
, hi
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.
equals()
Collection<E>
Set<String> names = /* ... */names.add("Eric");names.add("Pippi");names.add("Eric");System.out.println(names.size()); // -> 2
"A collection that contains no duplicate elements"
add()
(and similar)⇒ one might implement Set<E>
violating this guarantee!
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()
!
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.
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.
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 . |
SortedSet<String> names = /* ... */names.add("Eric");names.add("Pippi");names.add("Eric");names.add("Alice");for (String name : names) { System.out.println(name);}
Gives:
AliceEricPippi
String
s has natural orderinginterface 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.
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...
List<E>
: key methodsMod. 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()
List<String> knownJavaThings = /* ... */knownJavaThings.add("interfaces");knownJavaThings.add("class");knownJavaThings.add("generics");knownJavaThings.add("GUI");knownJavaThings.add("serialization");knownJavaThings.set(1, "classes"); // 0-basedknownJavaThings.remove(3);System.out.println(knownJavaThings.get(3));
Outcome?
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
∪ null
Iterable
!Map<K,V>
key methodsMod. 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. |
Map<K,V>
viewsMod. 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. |
keySet()
is a Set<K>
:values()
is a Collection<V>
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. |
entrySet()
is a Set<Map.Entry<K, V>>
Map
Depending on the specific case:
Map<String, Integer> ages = /* ... */ages.put("Eric", 41); //autoboxingages.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(); /* ... */}
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>
firstKey()
/lastKey()
methodsSortedSet
(in JDK implementations)!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.
Set
→ HashSet
, LinkedHashSet
, EnumSet
SortedSet
→ TreeSet
List
→ ArrayList
, LinkedList
Map
→ HashMap
, LinkedHashMap
, EnumMap
SortedMap
→ TreeMap
Recall: implementations differ in
Key information (more on the documentation):
LinkedHash*
give predictable iteration order (the "same" of insertion); Hash*
do notRandom
s; still hard to achieve in multithreading scenariosEnum*
are optimized for enum
sArrayList
fast in reading, slow in modifying; LinkedList
slow in reading, fast in modifyingAll 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]
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:
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.equals(Object)
method, then calling the hashCode
method on each of the two objects must produce the same integer result.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!
equals()
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);
Use always the most general type!
Ok!
List<String> names = new ArrayList<>();
Not ok!
ArrayList<String> names = new ArrayList<>();
names
to be of type ArrayList<>
, that is rareRecall the deep meaning of "interface" and (one of) its goal(s):
names
?names
that really matter?default
methodsInterfaces 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
ConcurrentModificationException
class ConcurrentHashMap<K,V> implements Map<K,V>
(actually implements ConcurrentMap<K,V>
)class ConcurrentSkipListSet<E> implements SortedSet<E>
Collections
class (with the leading s
!!!) provides utility static methods for obtaining thread-safe views of existing collections:
synchronized
blocksstatic * synchronized*()
: e.g.,static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
static <T> List<T> synchronizedList(List<T> list)
Collections
Given a sentence, return the word occurrences.
Design phase:
String
String
Map<String, Integer>
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 characterdefault V getOrDefault(Object, V)
returns the second argument if the key (1st argument) is not presentSortedMap<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}
Map
would not be reflected in the SortedMap
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()))
[]
instead of {}
: come from ArrayList.toString()
instead of HashMap.toString()
Word occurrences: actually, this is a multiset of words
Map<String, Integer>
Is there a multiset in the JDK?
Simplify the design and development of code that executes tasks in asynchronous mode:
Key concepts:
The task is not asynchronous/synchronous: that's a property of the execution.
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)Callable
call() throws Exception
@FunctionalInterface
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));}
Numbers here are too large...
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@FunctionalInterface
Callable
in the JDKThread implements Runnable
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
constructorsstart()
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
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:
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. |
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:
get()
blocks until the actual result is readyExecutor
+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 immediatelylong result = future.get();
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));}
invokeAll()
throws InterruptedException
get()
throws ExecutionException
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 threads run do computation at the same time ⇒ ≤n tasks are executed on this executorinterface ScheduledExecutorService
: augments ExecutorService
with methods for scheduled executionschedule(Callable<V> callable, long delay, TimeUnit unit)
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
Protocol (upon connection):
Server:
String
→ String
, lquit, port number, n are parameterspublic 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 { /* ... */ }}
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(); }}
Support functional-style operations on streams of elements.
Stream: "a continuous flow or succession of anything"
Example:
String
: transform to uppercase, compute letter frequencies, sort by most uneven distributioninterface 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:
Stream
obtained from a collection produces a new Stream
without the filtered elements, rather than removing elements from the source collection.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.limit(n)
or findFirst()
can allow computations on infinite streams to complete in finite time.Iterator
, a new stream must be generated to revisit the same elements of the source.Three phases:
Stream<T>
From a source:
Collection<T>
:Stream<T> stream()
Stream<T> parallelStream()
Stream<T> Arrays.stream(T[])
Stream<String> lines()
in BufferedReader
Stream<String> splitAsStream(CharSequence input)
in Pattern
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
).
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 methodsinterface BiFunction<T,U,R>
is a @FunctionalInterface
with a method R apply(T, U)
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);
[ADL, FAP]
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));
Eric Medvet
Research interests:
Labs:
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 |