This tutorial is about basic and advanced features of Java. Please note that some features, “advanced streams” for instance, are implemented from Java ver. 8 only.
Java 15 is available from 2020 and its specification may be found here…
Go to this dedicated page for features, “modules” for instance, beyond Java 8.
The big picture: Java is recognized as “The language of Internet” despite strong competitors like JavaScript, Python, C#, C++…
Billions of server computers, desktop computers, devices (meters, TVs, smarphones, blu-ray players…) and smart cards use Java as core programming language.
Java evolved with key versions: Java, Java 2, Java 5 (enumerated types, generics…), Java 6, 7, 8 (“advanced streams”…), 9 (modules…) 10, 11…
First rose Pascal, CoBOL, ForTran, BASIC (Beginner's All-purpose Symbolic Instruction Code)… and later on… C, C++, Java, JavaScript, Python…
The way languages' popularity is nowadays measured depends upon data extracted from the Web.
For example, the TIOBE Index
proposes a languages' ranking.
Styles of programming pervade IT domains: business, embedded systems and infrastructures, pedagogy, science, Web… based on several programming paradigms:
Imperative… versus declarative
From event-based to asynchronous
Functional (from LisP to lambda expressions)
Logical (from Prolog to Drools)
Object… versus structured… versus unstructured (from Simula 67 to <who's next?>)
From parallel to concurrent
Web (from network programming to the Internet of Things - IoT)
Any other style?
Java SE virtual machine.
Java types are primitive (boolean with false and true as possible values), char, byte, short (16-bit long), int, long (64-bit long), float and double.
Otherwise, they are constructed as data structures by means of the class and interface keywords.
Rule(s)
Differently than C++, void cannot be used as the type of a variable. However, as a Java construct, void is itself the unique instance of a type.
More precisely, java.lang.Void conveys us to the type of void as follows:
try {
assert (java.lang.Void.class == void.class); // The 'Void' type *IS NOT* itself the direct type of 'void'
assert (java.lang.Void.TYPE == void.class); // Yes, it works!
} catch (AssertionError ae) { // Run program with '-ea' option
System.err.println(ae.getMessage()); // Error comes from first assertion!
}
Differently from C++, Java primitive types have fixed sizes. For example, the size of short is 16-bit long: [Short.MIN_VALUE (- 215) Short.MAX_VALUE (215 - 1)].
Other common types are: java.lang.Object, java.lang.String, java.lang.Short, etc. From Java 5, there is a seamless mapping (the idea of “autoboxing”) between primitive types and their counterparts as constructed types in the core library, i.e., java.lang.
Example
float x = 1.F;
Float y = new Float(1.F); // Heavy style, to be avoided…
float z = x + y; // Autoboxing: 'float' and 'Float' are seamlessly compatible
Rule(s)
The type of a variable can be tested by means of the instanceof operator with limitations.
Examples
int i;
assert(i instanceof int); // Type of a variable like 'i' having a primitive type like 'int' cannot be tested (compilation error)
assert(i instanceof Integer); // Compilation error as well
String surname = "Barbier";
assert(surname instanceof String); // This works (no assertion error at execution time)! Don't forget that 'java.lang.String' is not a primitive but a constructed type
public class Type {
boolean attribute0;
byte attribute1;
short attribute2;
int attribute3;
long attribute4;
float attribute5;
double attribute6;
char attribute7;
public static void main(String[] args) {
Type object = new Type();
object.attribute0 = true;
System.out.println(object.attribute0); // 'true'
object.attribute1 = '\r';
System.out.println(object.attribute1); // '13'
object.attribute2 = 0x7777;
System.out.println(object.attribute2); // '30583'
object.attribute3 = 0xFFFFFFFF;
System.out.println(object.attribute3); // '-1'
object.attribute4 = Long.MAX_VALUE;
System.out.println(object.attribute4); // '9223372036854775807'
object.attribute5 = 6.5E1F;
System.out.println(object.attribute5); // '65.0'
object.attribute6 = 7.2e-2D;
System.out.println(object.attribute6); // '0.072'
object.attribute7 = 66;
System.out.println(object.attribute7); // 'B'
}
}
Java primitive arrays have elements, whose type may be primitive or not.
Example
Object tab[] = new Object[1000];
String another_tab[] = new String[1000];
assert(tab instanceof Object[]);
assert(another_tab instanceof Object[]);
// Initialization:
Object my_tab[] = new Object[]{new Object()}; // 'my_tab' is created with one object inside at position '0'
assert(my_tab.length == 1);
Rule(s)
The use of primitive arrays is questionable since more
flexible “containers” exist in Java for a long time.
Nonetheless, primitive arrays keep a good interoperability with advanced Java constructs,
streams in Java 8 for instance.
Example (use of method reference: :: mechanism)
public class Primitive_array_concatenation {
private final static String[] _Author = {"Franck", "Barbier"};
// Delimited word: [^\w] <=> [^a-zA-Z_0-9]
private final static java.util.regex.Pattern _Word = java.util.regex.Pattern.compile("[^\\w]");
public static void main(String[] args) {
String string = "Primitive array concatenation from";
String[] words = _Word.split(string);
String[] concatenation1 = java.util.Arrays.copyOf(words, words.length + _Author.length);
System.arraycopy(_Author, 0, concatenation1, concatenation1.length - _Author.length, _Author.length);
// Java 8, cost?
String[] concatenation2 = java.util.stream.Stream.of(words, _Author).flatMap(java.util.stream.Stream::of).toArray(String[]::new);
// 'of': stream of two arrays
// 'flatMap': stream of the two arrays' elements
}
}
public static final byte Celsius = 0;
public static final byte Fahrenheit = 1;
public static final byte Kelvin = 2;
public Temperature(float value,byte unit)
…
Temperature t = new Temperature(18.F,3); // Error comes from the fact that '3' does not embody a meaningful value for temperature units!
Rule(s)
From Java 5, enumerated types exist.
Example
public enum Temperature_unit {Celsius, Fahrenheit, Kelvin}
…
public Temperature(float value,Temperature_unit unit) // 'Temperature_unit' is simply used as a type
…
Temperature t = new Temperature(18.F,Temperature_unit.Celsius); // Later on, values of 'Temperature_unit' may be passed as arguments
…
for(Temperature_unit unit : Temperature_unit.values()) System.out.print(“\t” + unit); // Java enumerated types have nice facilities like iterating
Constructed types are built from the class keyword.
OO programming relies on the class/instance duality: a class is a data structure plus functions (operations or “methods” in Java), which are inseparable of data (attributes or fields) while an instance is a class incarnation (a.k.a. “object”) at execution time. In short, a class is a “mold” for to-be-created instances.
OO programming mainly leads us to reuse existing classes in core and external libraries.
Example
java.util.Date my_date = new java.util.Date(0L); // January 1, 1970, 00:00:00 GMT
Rule(s)
However, OO programming may also lead us to design new classes.
Example
final class Program {
Temperature _target_temperature;
java.util.Calendar _time;
}
public class Programmable_thermostat … {
private Program[] _program = new Program[8]; // An attribute named '_program' as an array of 8 values, each having 'Program' as type
…
In OO programming, attributes aim at being hidden (Parnas' information hiding principle) to avoid any direct (unsafe) access from any code outside the class.
Instead, methods are generally exposed. Only internal (reusable) computations are hidden, leading to inherent (“private”) methods.
In this spirit, the Smalltalk programming language philosophy is interesting: attributes are by default not accessible while functions are. To create some access (read and/or write), getters and setters are required.
Example (Smalltalk)
get_x
"return of _x"
^_x.
set_x: x
"assign _x with x"
_x := x.
Rule(s)
Java supports the encapsulation principle with the private and protected keywords.
There is no reason at all to set attributes to the public visibility except constant attributes (final keyword).
Example
public class My_class {
public float a; // To be avoided, absolutely!
public final float b = 1963.F;
private double _c;
…
}
…
My_class mc = new My_class();
mc.a = 1.F; // OK because 'public'
mc.b = 2.F; // Compilation error because 'final'
System.out.println(mc.b); // OK because 'public'
mc._c = 3.; // Compilation error because 'private'
System.out.println(mc._c); // Compilation error because 'private'
Rule(s)
In OO programming, operations (“methods” in Java) are either visible from outside or for internal use only (private modifier).
Example
public class My_class {
…
private void _f() {…};
public void g() {…}; // '_f' is probably called inside 'g' and/or in other methods…
}
…
My_class mc = new My_class();
mc._f(); // Compilation error because 'private'
mc.g(); // OK because 'public'
Class attributes are identified by the static keyword.
Instead of “instance attributes”, class attributes are shared among instances.
In other words, one may also consider that class attributes belong to the class itself and the class' instances just get an access.
Example
public class Temperature {
public static final float Min = -273.15F; // In Celsius
…
if(_value < Min) … // Or 'Temperature.Min' when one accesses this (public) attribute from outside the 'Temperature' class
Rule(s)
Class methods are also identified by the static keyword.
Instead of “instance methods”, class methods deal with class attributes.
Example
final public class Leap_year_utility { // See -'final' and inheritance- section to understand why 'final' is here used
final public static boolean Leap_year(final java.util.Calendar calendar) { // See -More on 'static'- section to understand why 'final' is here used
return ((calendar.get(java.util.Calendar.YEAR) % 4 == 0) && (calendar.get(java.util.Calendar.YEAR) % 100 != 0)) || (calendar.get(java.util.Calendar.YEAR) % 400 == 0);
}
}
Visibility and encapsulation are illustrated by the iceberg metaphor that faithfully represents the idea of encapsulation. The hidden part is called “the implementation” while the visible part is called “the interface”.
Example
private java.util.Calendar _birth_date; // Implementation is by definition hidden, i.e., 'private' here
private static java.util.Calendar _Now() {
return new java.util.GregorianCalendar();
}
…
public int age() { // Interface is by definition visible, i.e., 'public' here
return _Now().get(java.util.Calendar.YEAR) - _birth_date.get(java.util.Calendar.YEAR);
}
Visibility possibilities are private, “package” (default), protected and public.
Note that the “package” visibility is not associated with any keyword.
Example
public class Human_being {
java.util.Calendar _birth_date; // "package" visibility
…
public class Family { // The 'Human_being.java' and 'Family.java' files must be in the same directory
java.util.Set<Human_being> _members = new java.util.HashSet<Human_being>();
…
public int last_child_birth_year() {
int result = 0;
for (Human_being member : _members) {
if (result < member._birth_date.get(java.util.Calendar.YEAR)) { // Access to '_birth_date' succeeds because 'Human_being' and 'Family' are in the same package
result = member._birth_date.get(java.util.Calendar.YEAR); // Access to '_birth_date' succeeds because 'Human_being' and 'Family' are in the same package
}
}
return result;
}
}
Rule(s)
Contrary to C++, protected also implies “package”! So, protected in Java has implications for both descendant classes (inheritance) and classes in the same package, i.e., the same directory.
Contrary to Eiffel, private in Java is not “really” private…
Example
public class Prisoner {
private String _file_number;
public boolean equals(Object p) {
if(p instanceof Prisoner) {
return (this._file_number.equals(((Prisoner)p)._file_number)); // 'this' accesses to '_file_number' of 'p'; this is not really private!
}
return false;
}
…
Rule(s)
Contrary to C++, Java classes have a visibility which is most of the time public. At most one class may be declared public in a file.
Other visibility possibilities for classes are “package” and private.
Example
// This entire code is included in the 'My_class.java' file
class Class_with_package_visibility { // "package" visibility ('My_class' is probably a user class, but other classes in the same package may use it!)
…
}
public class My_class {
private class Class_with_private_visibility { // 'private' visibility (such a declaration is allowed inside the user class only!)
…
}
…
}
Naming space in Java relies on hierarchical directories of the Operating System (OS).
Reusing this code requires localisation through the import clause.
Example
package my_code;
…
public class X { …
package my_code.my_extra_code; // Absolute path from 'classpath' OS variable
…
import my_code.X; // 'import my_code.*;' -> all is imported
…
public class Y extends X { … // 'X' has been previously imported
public class Y extends my_code.X { … // Alternative: no import, absolute path to 'X'
Rule(s)
The classpath OS variable plays a central role. Accordingly, it must be setup, for instance, within UNIX setup in relation with figure above.
Java supports the notion of block initialization along with that of constructor for a class.
The constructor's name is that of the class; it is, by default, a “classical” function without arguments.
Contrary to C++, Java has no destructor notion.
Rule(s)
The Java compiler (like that of C++) generates a default constructor for a class.
However, one often requires constructors with arguments for initializations typically. Any user-defined constructor (with or without arguments) cancels the default one.
For this reason, if one intends to keep a no-argument constructor then one needs its re-introduction!
Contrary to C++, Java releases memory in a deferred way. Namely, unaccessible objects are automatically collected to free memory based on a garbage collector
supplied by
the Java Virtual Machine (JVM).
Garbage collecting in Java is associated with the finalize method in java.lang.Object.
Example
public class Student {
{ // This code is executed before every constructor:
System.out.println("Block initializer of 'Student'");
}
public Student() { … // The no-argument constructor is re-introduced, cancelling the initial compiler's no-argument constructor
public Student(String student_id) {
this(); // Calling a constructor (the prior one) inside another!
… // Additional stuff here…
}
…
Rule(s)
Having several constructors in the same class is named constructor overloading. Note that overloading also applies for common methods that have the same name and a variation on their arguments : number and/or types.
Inheritance is a foundation of OO programming. The principle behind inheritance is the fact that data structures are extended from existing ones. This amounts to adding attributes (structural inheritance) and/or methods (behavioral inheritance).
The way of dealing with inheritance in Java is the use of the extends keyword.
Note that Java supports single inheritance between classes and multiple inheritance between classes and interfaces,
between interfaces as well.
abstract public class Compte_bancaire {
private int _id;
protected float _solde;
…
public class Compte_cheque extends Compte_bancaire {
protected float _taux_interet;
protected float _seuil;
…
public class Compte_epargne_logement extends Compte_epargne {
public final static float Taux_interet = Livret_A.Taux_interet * 2.F / 3.F; // Fixed rate in the French law
public float taux_interet() {
return Taux_interet;
}
…
abstract public class Compte_bancaire {
…
abstract public float taux_interet(); // Abstract method
public void appliquer_taux_interet() {
_cumul_interets = _solde * (1.F + (taux_interet() / 100.F));
}
}
public class Compte_cheque extends Compte_bancaire {
…
public float taux_interet() { // Abstract nature is removed when overriding
return _taux_interet;
}
public void appliquer_taux_interet() { // Overriding as well
if(_solde > _seuil) super.appliquer_taux_interet();
}
}
Rule(s)
Accessing to properties of ancestor classes in Java obeys precise rules.
Example
// Assumption: 'Daughter' inherits from 'Mother', which inherits from 'Grandmother'. One wants in 'Daughter' to access to 'jewel' (not 'private') in 'Mother':
super.jewel;
// One now wants in 'Daughter' to access to 'other_jewel' (not 'private') in 'Grandmother':
super.super.other_jewel; // Compilation error! Contrary to C++, Java prevents such an access
Rule(s)
Overriding private methods is impossible in Java.
Example
public class Note {
private static int _Increment = 10;
private final java.io.StringReader _source;
public final static String Default_header = "Note: ";
protected StringBuffer _text;
private void initialization() { // 'private' prevents overriding -> to allow overriding, possibly use 'protected' instead
_text = new StringBuffer(Default_header);
}
public Note(java.io.StringReader source) {
_source = source;
initialization(); // Polymorphism does not apply since it is indicated 'private' above
}
…
public class Confidential_note extends Note {
public final static String Default_header = "Confidential note: ";
private void initialization() { // Overriding does not work because 'private' in 'Note'
_text = new StringBuffer(Default_header);
}
public Confidential_note(java.io.StringReader source) {
super(source);
}
…
Note cn = new Confidential_note(new java.io.StringReader("Franck Barbier"));
Polymorphism is intimately associated with inheritance. Polymorphism relies on the testing of objects' type at run-time to choose the “right” code.
Example
Compte_cheque cc = new Compte_cheque(1963,10500.F,2.F,10000.F);
Compte_bancaire cb = cc; // 'cb' points to 'cc' since their mutual types are compatible through inheritance (see inheritance tree above)
cb.appliquer_taux_interet(); // 'appliquer_taux_interet' in 'Compte_cheque' is run since the runtime type of 'cb' is 'Compte_cheque'
cb = cel; // Assumption: 'cel' is a direct instance of 'Compte_epargne_logement'
cb.appliquer_taux_interet(); // Polymorphism again
Rule(s)
Polymorphism is strongly grounded on the fact that “higher” types in the hierarchy may dynamically “point/refer” to lower ones (contravariance).
Example
Animal a1 = new Cat(); // Assumption: 'Cat' directly or indirectly inherits from 'Animal'
The notion of interface as first-class type construct is native in Java from its birth.
Interfaces are close to abstract classes having, up to Java 7, no concrete methods at all (declared or inherited). As an illustration,
java.util.Comparator<T> is a famous predefined interface for comparing (e.g., sorting) “compliant” objects.
Example
public class Task implements java.util.Comparator<Task> {
private int _priority;
public Task(int priority) {
_priority = priority;
}
@Override
public int compare(Task t1, Task t2) {
if (t1._priority < t2._priority) {
return -1; // '-1' (or any negative) is a convention/contract imposed by 'java.util.Comparator<T>'
}
if (t1._priority == t2._priority) {
return 0; // '0' is a convention/contract imposed by 'java.util.Comparator<T>'
}
return 1; // '1' (or any positive) is a convention/contract imposed by 'java.util.Comparator<T>'
}
}
Interfaces are types. For example, the java.lang.Integer class implements the java.lang.Comparable<T> interface with T equals to java.lang.Integer.
Example
Comparable<Integer> i = new Comparable<>(); // Compilation error: attempt to (directly) instantiate an interface
Comparable<Integer> i = new Integer(0); // Instead, 'i' has for (design) type 'Comparable<Integer>' while it has 'Integer' as (runtime) compatible type
Rule(s)
The key advantage of interfaces is the fact that predefined code may be written based on an anonymous T type.
Later on, T may be replaced by a known type provided that this type complies (like T) with java.lang.Comparable<T>.
Example Sort_illustration.Java.zip
public class Sort_illustration<T extends Comparable<T>> {
java.util.Vector<T> _implementation; // Don't forget allocation somewhere else!
…
void sort(int left, int right) { // Naive (slow) sort
assert (left < right);
for (int i = left; i < right; i++) {
for (int j = i + 1; j <= right; j++) {
_cost++;
if (_implementation.elementAt(i).compareTo(_implementation.elementAt(j)) > 0) {
T temp = _implementation.elementAt(i);
_implementation.setElementAt(_implementation.elementAt(j), i);
_implementation.setElementAt(temp, j);
}
}
}
}
…
public class Elephant implements Comparable<Elephant> {
private float _weight;
public int compareTo(Elephant e) {
if(this._weight < e. _weight) return -1;
…
}
…
Sort_illustration<Elephant> elephant_zoo = new Sort_illustration<>(…); // 'elephant_zoo' is sortable
public interface I {
String S = "S in I";
void f();
}
public interface J {
String S = "S in J";
void f();
}
public interface K extends I, J { …
public class C implements K { …
public interface I {
String S = "S in I";
void f();
void g(I i);
I h();
}
public interface J {
String S = "S in J";
void f();
void g(J j);
J h();
}
public interface K extends I, J { … // Compilation error about 'h'
public class C implements K {
public void f() { // Compilation error about 'g': 'g' from 'I' or 'g' from 'J'?
g(this);
}
public void g(I i) {
}
public void g(J j) {
}
public static void main(String[] args) {
System.out.println(I.S);
}
}
From Java 8, interfaces, like classes, may have static methods.
The other issue in Java 8 is the introduction of default methods
using the default prefix keyword. Due to the fact that
default methods make interfaces and abstract classes very similar, a remaining difference is the fact
that abstract classes have constructors while interfaces have not.
As of Java 9, methods may be declared private.
Example (with misconception on line 41) From_Java_9.Java.zip
enum Size {
S, M, L, XL, XXL
};
interface Proboscidea {
// From Java 8 (i.e., 'static' and 'default'):
static String Reign() {
return _French_name() + " in Animalia reign";
}
default String description() { // 'default' => 'public'
return "Animal owning a trunk, whose size is " + trunk_size(); // Body is mandatory for default methods...
}
default boolean still_exists_() { // 'default' => 'public'
return true; // Body is mandatory for default methods...
}
default String trunk_size() {
return _trunk_size() == Size.S ? "small" : ""; // To be enhanced: test of values of 'Size' enumerated type
}
// From Java 9 (i.e., 'private'):
private static String _French_name() {
return "Proboscidien";
}
private Size _trunk_size() { // 'private' methods *CANNOT* be used in descendants...
return Size.S; // 'private' methods cannot be abstract...
}
}
public class Mammutidae implements Proboscidea {
@Override
public boolean still_exists_() {
return false; // Overriding occurs here...
}
private Size _trunk_size() { // Arrrgggglll... Overriding *FAILS*!
return Size.XXL;
}
}
Genericity is the ability to express the behavior of a class by using anonymous types. Roughly speaking, a stack has a behavior (pop, push...), which does not depend upon the type of its stored elements (e.g., Character, Integer).
Thus, genericity allows the possibility of designing a class by referring to T as “any type”.
Example
public class Bound_stack<T> extends java.util.ArrayDeque<T> { // Note that 'java.util.Stack<T>' is deprecated in Java
private final int _capacity;
Bound_stack(final int capacity) {
_capacity = capacity;
}
public boolean full() {
return _capacity == size(); // From 'java.util.ArrayDeque<T>' without redefinition
}
public void push(final T t) {
if (full()) {
throw new RuntimeException("push");
}
super.push(t); // Omitting 'super' creates recursion
}
}
…
public static void main(String[] args) {
Bound_stack<Character> bs = new Bound_stack<>(10);
bs.push('A');
bs.push('B');
// bs.removeFirst(); // 'A' would be removed while it's illegal for a stack!
bs.pop(); // 'A'
}
public class Bank<T extends Compte_bancaire> { // See inheritance tree above, look at 'Compte_bancaire'
private java.util.Deque<T> _bank_accounts = new java.util.ArrayDeque<>();
…
public T greater_bank_account() { // Say, the account in the bank with the greater balance
T result = null;
for(java.util.Iterator<T> i = _bank_accounts.iterator();i.hasNext();) {
T temp = i.next();
if(result == null) result = temp;
if(temp.compareTo(result) > 0) result = temp;
}
return result;
}
…
Bank<Compte_bancaire> a_bank = new Bank<>(); // See inheritance tree above, look at 'Compte_bancaire'
Bank<Compte_cheque> another_bank = new Bank<>(); // See inheritance tree above, 'Compte_cheque' inherits from 'Compte_bancaire'
public boolean has_bigger_customer_than(Bank<T> bank) {
return this.greater_bank_account()._solde > bank.greater_bank_account()._solde ? true : false;
}
…
Bank<Compte_bancaire> b1 = new Bank<>();
Bank<Compte_cheque> b2 = new Bank<>();
boolean result = b1.has_bigger_customer_than(b2); // Compilation error
Rule(s)
The has_bigger_customer_than method is signed with Bank<T> bank, but, in this method's body, this (b1 plays this role) has not the same direct type that the bank parameter (b2 plays this role).
Example (appropriate solution)
public boolean has_bigger_customer_than(Bank<?> bank) { … // 'has_bigger_customer_than' is now signed in a different way to remove the compilation error problem
…
Bank<?> a_bank = another_bank; // This is the generalized approach to avoid problems!
public class Bank<T extends Compte_bancaire> {
…
public void absorption(Bank<T> target_bank) {
while(! _bank_accounts.isEmpty()) target_bank.add(_bank_accounts.removeFirst());
}
…
Bank<Compte_bancaire> b1 = new Bank<>();
Bank<Compte_cheque> b2 = new Bank<>();
…
b1.absorption(b2); // Sound compilation error because one may add 'T' objects to 'b2' such that 'T' <= 'Compte_bancaire', but 'T' > 'Compte_cheque'
…
b2.absorption(b1); // In contrast, this compilation error is rather inconsistent, why?
…
public void absorption(Bank<? extends Compte_bancaire> target_bank) { … // 'absorption' is signed in a different way, but problems still persist with 'b1.absorption(b2);'
…
public void absorption(Bank<? super T> target_bank) { … // This is the single solution, i.e., using the 'super' keyword
Generic arrays may be used instead of primitive arrays
Example
java.util.Vector<Character> tab = new java.util.Vector<>(2); // 'new java.util.Vector<Character>(2)' creates redundancy…
// tab.set(0,'a'); // Execution error: out of bounds, why?
tab.add(0,'a'); // This works
tab.add(1,'b'); // Again, this works
tab.add(2,'c'); // Very lucky!
Conversion of primitive arrays into generic arrays
Example
Short tab[] = {13, 7, 6, 4, 12, 11, 8, 3}; // Caution: 'short tab[]' does not work!
java.util.Collections.sort(java.util.Arrays.asList(tab)); // 'tab' is converted into 'java.util.List<Short>' and next sorted
for (Short s : tab) { // Initial 'tab' array is physically sorted by side effect
System.out.print("-" + s); // '-3-4-6-7-8-11-12-13' is displayed
}
Conversion of generic arrays into primitive arrays
Example
java.util.Deque<Short> deque = new java.util.ArrayDeque()<>
deque.addLast((short) 13);
…
Short other_tab[] = deque.toArray(new Short[0]);
Parameterized (generic) classes with T do not support arrays of type T
Example
public class My_class<T> {
T tab_containing_T_elements[]; // Declaration is OK
My_class() {
tab_containing_T_elements = new T[1000]; // However, instantiation failed at compilation time: 'error: generic array creation'
}
…
As an illustration, the java.util.Collection<E> type weirdly offers two methods for transforming a collection into a primitive array:
Object[] toArray()
<T> T[] toArray(T[] tab)
The second form illustrates what is a generic method, i.e., a method parameterized by T, which is totally different from E that parameterizesjava.util.Collection<E>!
The two forms are somehow redundant. The former remains for backward compatibility.
Example (first form)
java.util.Deque<Short> deque = new java.util.ArrayDeque<>();
deque.addLast((short)13);
deque.addLast((short)7);
…
Short result[] = (Short[])deque.toArray(); // Mandatory cast from 'Object[]' to 'Short[]'
Example (second form)
Short result[] = deque.toArray(new Short[0]); // Great, no cast!
Rule(s)
In fact, the unique parameter (absent in the former version) allows us to control the type of the return result and where it is stored.
Example (second form)
Byte parameter[] = new Byte[0];
Byte result[] = deque.toArray(parameter);
assert (parameter == result && parameter.length == result.length);
Exception management is a programming style that favors the implementation of a defense strategy when facing up abnormal behaviors, failures or, worst, stronger errors that may come from the Operating System (OS) itself (e.g., no more memory).
Promoted by Ada, exception management relies on a self-contained thread of control compared to the “normal” program execution.
Exceptions are raised and caught depending upon the chosen defense strategy.
In OO programming, exceptions are plain objects and, in Java, they are instantiated from a predefined (extensible) hierarchy of classes.
public Temperature(float value,Temperature_unit unit) throws Exception,Invalid_temperature_exception { // A constructor may have declared exceptions as any Java method
switch(unit) {
case Celsius: _value = value;
break;
case Fahrenheit: _value = (value - 32.F) * 5.F / 9.F;
break;
case Kelvin: _value = value + Min;
break;
default: throw new Exception("Illegal temperature unit"); // Raising
}
if(_value < Min) throw new Invalid_temperature_exception(_value); // Raising
…
}
…
Temperature t;
try {
t = new Temperature(18.F,Temperature_unit.Celsius);
}
catch(Exception e) { // Catching: normal program execution is diverted from here
System.exit(1); // Horrible style, to be avoided!
} // Normal execution goes on from here when the 'e' exception does not occur
public class Invalid_temperature_exception extends Exception {
private float _value; // Exceptions may have attributes as ordinary objects
public Invalid_temperature_exception(float value) {
super("Invalid temperature"); // One calls the one-argument constructor of 'java.lang.Exception'
_value = value;
}
public float value() { // Exceptions may have methods as ordinary objects
return _value;
}
}
Temperature t = new Temperature();
try {
t.decrement();
}
catch(Invalid_temperature_exception ite) {
// 'ite' analysis, partial correction, etc.
throw ite; // Routing for final processing
}
finally {
// Resource release, etc.
}
try {
// Any Java statement
}
catch(Throwable t) { // Generic catching is useless
if(t instanceof NullPointerException) … // Very bad idea because it seems that we intend to test 't' against all of its possible types?
}
…
My_class c1 = new My_class(),c2 = null;
try {
c2 = (My_class)c1.clone(); // 'clone' in 'java.lang.Object' by default raises an exception
}
catch(CloneNotSupportedException cnse) { // Good style: closer probable exceptions are caught first!
// 'cnse' as instance of 'CloneNotSupportedException' (including subtypes) is caught and processed…
}
catch(Throwable t) {
// Any other exception whose type does not comply with 'CloneNotSupportedException'
// However, one may note that poor information is available since one only knows that 't instanceof CloneNotSupportedException == false'
}
try {
// Java statement possibly raising occurrences of 'NoSuchAlgorithmException' or 'UnsupportedEncodingException'
}
catch(java.security.NoSuchAlgorithmException | java.io.UnsupportedEncodingException e) { // The processing of the 'e' exception is shared by two exception types
// 'e' is instance of 'NoSuchAlgorithmException' or 'UnsupportedEncodingException' (including subtypes of both)…
}
Rule(s)
Multi-catch cannot be concerned with exception types that relate to each other through inheritance.
Example
java.net.URL url = null;
try {
url = new java.net.URL("https://api.fr.carrefour.io/v1/openapi/items");
javax.net.ssl.HttpsURLConnection connection = (javax.net.ssl.HttpsURLConnection) url.openConnection();
…
// 'java.io.MalformedURLException' inherits from 'java.io.IOException' that prevents multi-catch:
} catch (java.net.MalformedURLException murle) { // Daughter class first...
…
} catch (java.io.IOException ioe) { // Mother class next...
…
}
public void decrement() { // No declared exception since, potentially, an instance of 'AssertionError' may be raised…
_value -= _step;
assert(_value >= Min);
}
…
Temperature t = new Temperature();
try {
t.decrement();
} catch(AssertionError ae) { // Run program with '-ea' option
// 'ae' as instance of 'AssertionError' (including subtypes) is caught and processed…
}
Rule(s)
In fact, assertions in Java are the support for contract programming
(contracts are preconditions, invariants and postconditions),
a paradigm that comes from the Eiffel OO programming language. However, the Eiffel style is graceful compared to Java.
Example
class SHELL
feature
… -- Public properties
feature {NONE}
Enter : CHARACTER is '\r';
Zero : INTEGER is 0;
Buffer_size : INTEGER is 256;
buffer : ARRAY[CHARACTER];
cursor : INTEGER
buffer_cleaning is
require -- precondition
not buffer.Void
do
buffer.clear_all;
ensure -- postcondition
buffer.all_cleared
rescue
if … then
retry
end; -- buffer_cleaning
… -- Other private properties
invariant -- invariant
cursor <= Buffer_size
end -- Shell
The java.lang.Object class is the root of the Java inheritance tree.
By default, unless an explicit inheritance declaration, any constructed class inherits from java.lang.Object.
So, extends java.lang.Object is an optional expression. This behavior is fairly different from C++.
Multithreading related method(s) in java.lang.Object
Object identity related method(s) in java.lang.Object
protected Object clone() throws CloneNotSupportedException
boolean equals(Object o)
int hashCode()
Example Hashtable_illustration.Java.zip
String s1 = "Franck";
String s2 = "Franck";
assert (s1 != s2); // 's1' and 's2' are NOT the same memory object
assert (s1.equals(s2)); // However, 's1' equals to 's2'
assert (s1.hashCode() == s2.hashCode());
Utility related method(s) in java.lang.Object
String toString()
Finalization related method(s) in java.lang.Object
From Java 5, Java supports covariance (on return types).
As a result, the following example shows that cloning in both Female and Male is really is considered as an overriding of cloning in Human_being.
Example Covariance.Java.zip
public class Human_being {
java.util.Calendar _birth_date;
…
public Human_being cloning() {
System.out.print("cloning in Human_being...\n");
return new Human_being(_birth_date.get(java.util.Calendar.DAY_OF_MONTH), _birth_date.get(java.util.Calendar.MONTH), _birth_date.get(java.util.Calendar.YEAR));
}
public class Female extends Human_being {
…
public Female cloning() {
System.out.print("cloning in Female...\n");
return new Female(_birth_date.get(java.util.Calendar.DAY_OF_MONTH), _birth_date.get(java.util.Calendar.MONTH), _birth_date.get(java.util.Calendar.YEAR));
}
public class Male extends Human_being {
…
public Male cloning() {
System.out.print("cloning in Male...\n");
return new Male(_birth_date.get(java.util.Calendar.DAY_OF_MONTH), _birth_date.get(java.util.Calendar.MONTH), _birth_date.get(java.util.Calendar.YEAR));
}
Human_being FranckBarbier = new Male(11, 1, 1963);
Human_being clone = FranckBarbier.cloning(); // 'cloning in Male...' is displayed => covariance applies in Java!
Rule(s)
Covariance on return types is an original principle of the Eiffel OO programming language that,
again, offers a graceful style compared to Java.
Example
class Human_being
feature
cloning : like Current is do Result := Current end;
…
end
x,y : Female -- As direct descendant of 'Human_being'
!!x -- Instantiation
y := x.cloning -- OK, 'cloning' returns a 'Female' object
Like many other languages (C++, JavaScript, Python…), Java allows the possibility of having a
variable number of arguments for a method.
Example Covariance.Java.zip
public class Family {
java.util.Set<Human_being> _members = new java.util.HashSet<Human_being>();
…
public void births(Human_being... children) {
for (Human_being c : children) {
_members.add(c);
}
}
…
}
…
// Usage:
family.births(new Human_being(6, 12, 1993), new Human_being(15, 4, 1996), new Human_being(22, 2, 2001));
// Alternative usage:
Human_being kids[] = {new Human_being(6, 12, 1993), new Human_being(15, 4, 1996), new Human_being(22, 2, 2001)};
family.births(kids);
Introspection is the ability of a programming language to be self-described.
In practice, types become at run-time (leaf) objects of a given meta-type. java.lang.Class<T> plays this role in Java.
Example
Class<?> class_of_Temperature = Temperature.class; // Access from '.class' Java native operator
Class<?> class_of_Temperature_unit = Temperature.Temperature_unit.class;
assert (class_of_Temperature == class_of_Temperature_unit.getEnclosingClass());
Temperature ambient_temperature = new Temperature(18.F, Temperature.Temperature_unit.Celsius);
assert (class_of_Temperature == ambient_temperature.getClass()); // Access from 'getClass' Java method in 'java.lang.Class<T>'
Rule(s)
The java.lang.reflect API offers powerful tools, like the java.lang.reflect.Method meta-type, to access Java from “its deep interior”.
Example Introspection.Java.zip
java.lang.reflect.Method methods[] = ambient_temperature.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
Class<?> types_of_parameters[] = methods[i].getParameterTypes();
Object arguments[] = null;
if (methods[i].getName().equals("toString") && methods[i].isVarArgs() == false && methods[i].getParameterTypes().length == 0) {
System.out.println("Calling 'toString' by introspection on 'ambient_temperature': " + methods[i].invoke(ambient_temperature, arguments));
}
}
public class Individual implements java.io.Serializable {
private transient int _age; // '_age' is made 'transient' because its value may be obsolete at the time when 'Individual' objects are reloaded
…
import java.io.*;
…
java.beans.XMLEncoder e = new java.beans.XMLEncoder(new BufferedOutputStream(new FileOutputStream ("gpao.xml")));
e.writeObject(gpao);
e.close();
…
java.beans.XMLDecoder d = new java.beans.XMLDecoder(new BufferedInputStream(new FileInputStream ("gpao.xml")));
GPAO gpao = (GPAO)d.readObject();
d.close();
…
static final long Serial_version_id = 1L; // Versioning
Anonymous classes have no name. They are created on the fly. Their main purpose is avoiding excessive inheritance, i.e.,
systematically creating descendant classes from other classes or interfaces.
Rule(s)
As an illustration, java.util.TimerTask is an abstract class. It has a single run method that requires a definition through an inheriting class. Timers accept timer task objects as arguments for later executing run at key instants, e.g., each second.
To avoid the creation of such a class (that involves the creation of an associated .java file) for only defining run, an anonymous class may be useful.
Example
public class Anonymous_class_test {
public static void main(String[] args) {
java.util.TimerTask timer_task = new java.util.TimerTask() {
@Override
public void run() {
System.out.println("A behavior…");
}
};
System.out.println(timer_task.getClass().getName()); // 'Anonymous_class_test$1' is displayed!
…
Rule(s)
Anonymous_class_test$1 is the registered name of the anonymous class in the Java Virtual Machine (JVM). However, the excessive use of anonymous classes leads to code, which might be maintainable with great difficulties!
Anonymous classes can also come from double brace initialization.
Example
java.util.Set<Mammutidae> presidents = new java.util.HashSet<>() { // <- Creation of an anonymous inner class, which extends 'java.util.HashSet'
{ // <- Block initializer
add(new Mammutidae("Macron"));
add(new Mammutidae("Poutine"));
add(new Mammutidae("Trump"));
}
};
System.out.println(presidents.getClass().getName()); // 'Main$1' is displayed!
Functional interfaces have only one (abstract) method. In the Java library, functional interfaces are annotated with @java.lang.FunctionalInterface
and are intimately associated with lambda expressions.
Rule(s)
As an illustration, java.lang.Runnable is a functional interface.
It has a single run method that requires a (deferred) definition through an inheriting class.
Threads require runnable objects as arguments for later executing (with the start method) run in split threads of control.
To avoid the creation of such a class (that involves the creation of an associated .java file) for only defining run, a lambda expression may be useful.
Example Lambda_expression.Java.zip
public class Functional_interface_test {
public static void main(String[] args) {
Runnable to_do = () -> { // No reference to the 'run' method is required since 'java.lang.Runnable' only owns this single method.
try {
Thread.sleep(1000L); // Nothing to do for 1 sec. <=> a kind of delay!
} catch (InterruptedException ie) {
java.util.logging.Logger.getLogger("Why it doesn't work?").log(java.util.logging.Level.SEVERE, null, ie);
}
};
new Thread(to_do).start();
…
Expressiveness
Rule(s)
Syntax of lambda expressions is interesting because it is compact.
Example Lambda_expression.Java.zip
@FunctionalInterface
public interface My_functional_interface {
void my_print(Object o);
}
…
My_functional_interface mfi = (o) -> System.out.println(o.toString()); // Java 7, which is deeply inspired by JavaScript!
mfi.my_print("This is Java 7 style...");
…
My_functional_interface mfi2 = System.out::println; // This is called "method reference"
mfi2.my_print("But Java 8 goes beyond...");
Example Heap.Java.zip
java.util.Comparator<Short> comparator = (Short i, Short j) -> {
return i - j;
};
Example (instead of…)
java.util.Comparator<Short> comparator = new java.util.Comparator<>() {
@Override
public int compare(Short i, Short j) {
if (i < j) {
return -1;
}
if (i == j) {
return 0;
}
// if (i > j) {
// return 1;
// }
return 1; // Mandatory without 'if'
}
};
Pitfall
Example (more than one method prevents the utilization of a lambda expression)
// @FunctionalInterface -> compilation error because of *TWO* methods!
public interface A_given_interface {
void a_given_method();
void another_given_method();
}
…
A_given_interface get_one() {
return new A_given_interface() {
@Override
public void a_given_method() {
throw new UnsupportedOperationException("Don't be lazy! Please define body...");
}
@Override
public void another_given_method() {
throw new UnsupportedOperationException("Don't be lazy! Please define body...");
}
};
}
In conjunction with streams…
Rule(s)
Lambda expressions are natural counterparts of stream programming.
final static Terminal[] _Terminals;
…
static { // This code is launched before any statement in the 'main' program:
int number_of_terminals = …;
_Terminals = new Terminal[number_of_terminals];
}
Static method hiding using final
Example Final_and_static_methods.Java.zip
public class My_class_mother {
final static void My_method() {
System.out.println("I'm both a 'final' and 'static' method in " + My_class_mother.class.getSimpleName() + ", what does it mean?");
}
}
public class My_class extends My_class_mother {
// static void My_method() { // Static methods can be 'hidden' instead of 'overridden', but 'final' in 'My_class_mother' prevents hidding here
// }
public static void main(String[] args) {
My_class.My_method(); // Normal style, 'final' in 'My_class_mother' prevents any hidding in 'My_class'
My_class mc = new My_class();
mc.My_method(); // Weird Java style through instances!
}
}
The idea of try-with-resource(s) statement block from Java 7 solves historical problems.
Typically, having errors when executing any kind of operation on a file invites us to close it in a safely manner. However, the close operation on the java.util.stream.BaseStream<T,S extends BaseStream<T,S>> class itself raises an exception in case of closing problems.
Before Java 7
Example
My_class c = new My_class();
String filename = "c.My_class.ser"; // 'public class My_class implements java.io.Serializable…'
java.io.FileOutputStream fos = null;
java.io.ObjectOutputStream ous = null;
try {
fos = new java.io.FileOutputStream(filename);
ous = new java.io.ObjectOutputStream(fos);
ous.writeObject(c);
}
catch (java.io.FileNotFoundException fnfe) {/* 'fos = new java.io.FileOutputStream(filename);' failed */}
catch (java.io.IOException ioe) {/* 'ous = new java.io.ObjectOutputStream(fos);' or 'ous.writeObject(c);' failed */}
finally {
if (ous != null) ous.close(); // Compilation problem here!
}
After Java 7
Rule(s)
The java.lang.AutoCloseable interface (implemented by java.io.ObjectOutputStream) is now totally useful.
Example Try_with_resources.Java.zip
try (java.io.ObjectOutputStream ous = new java.io.ObjectOutputStream(fos)) {
ous.writeObject(c);
}
catch (java.io.IOException ioe) {/* 'ous.writeObject(c);' failed, but 'ous' is nonetheless closed */}
From Java 7, java.nio.file.FileSystems, java.nio.file.Files
or java.nio.file.Paths (list is not exhaustive, see also: Oracle Java 8 API)
are utility classes for quicker and non-blocking file manipulation.
In this scope, one may imagine the creation a My_Web subfolder (with deletion if already existing) from an existing Web folder.
Next a template index.html file in Web is copied to My_Web.