5  Basic Features

Learning objectives

5.1 Fundamental syntax

5.1.1 Comments

Java allows both C-style comments (multi-lines):

/* this comment is so long
   that it needs two lines */

and comments on a single line:

  // comment on one line

5.1.2 Code blocks and Scope

Java code blocks are the same as in C

Each block is enclosed by braces { } and starts a new scope for the variables

Variables can be declared both at the beginning and in the middle of a block before their use (differently from ANSI C):

for (int i=0; i<10; i++){
  int   x = 12;
  ...
  int y;
  ...   
}

5.1.3 Control statements

  • Similar to C
    • if-else
    • switch
    • while
    • do-while
    • for
    • break
    • continue

Switch statements can be used with strings:

switch(season){
  case "summer":
  case "spring": temp = "hot";
                break;
  case "winter":
  case "fall": temp = "cold";
                break;
}

Compiler generates more efficient bytecode from switch using String objects than from chained if-then-else statements.

5.1.4 Boolean

Java has an explicit type ( boolean ) to represent logical values ( true , false )

Conditional constructs require boolean conditions. It is illegal to evaluate integer values as conditions.

int   x = 7; 
if(  x  ){ ... }   //NO

In Java relational operators are mandatory to check if a value is (not) zero:

if (x != 0){ ... }

This avoids mistakes that are common in C, e.g. 

if(x=0) ...

5.1.5 Passing parameters

Parameters are always passed by value they can be primitive types or object references. Only the object reference (pointer) is copied not the whole object.

5.1.6 Primitive Types

They are fefined in the language: int, double, boolean, etc.

The declaration of an instance of a primitive type:

  • Declares the instance name
  • Declares the type (name is associated to a specific type)
  • Allocates memory space for the value

The primitive types available in the Java language are reported in Table 5.1.

Table 5.1: Java primitive types
Type Size Encoding
boolean 1 bit -
char 16 bits Unicode UTF16
byte 8 bits Signed integer 2C
short 16 bits Signed integer 2C
int 32 bits Signed integer 2C
long 64 bits Signed integer 2C
float 32 bits IEEE 754 sp
double 64 bits IEEE 754 dp
void -

Concerning the boolean type it is important to remember that the logical size does not match the memory occupation.

5.1.7 Literals

  • Literals of type int, float, char, strings follow C syntax
    • 123 256789L 0xff34 123.75 0.12375e+3
    • 'a' '%' '\n' "prova" "prova\n"
    • in addition since Java 15, also multiline strings (a-la python)
    """
    ...
    """
  • Boolean literals (do not exist in C) are
    • true , false

5.1.8 Operators

Operators follow C syntax:

  • arithmetical + - * / %
  • relational == != > < >= <=
  • bitwise (int) & | ^ << >> ~
  • Assignment = += -= *= /= %= &= |= ^=
  • Increment ++ --
  • Logical && || ! ^.

Chars are considered like integers (e.g. switch)

Logical operators work only on boolean operands.

Relational operators return boolean values.

5.2 Classes and Objects

The Java language defines structural elements (i.e., types) that are used and declared at compile time:

  • Class
  • Primitive type

The execution involves dynamic elements (i.e., instances) that are active at run time

  • Reference
  • Variable
Table 5.2: Classes and primitive types
category type variable
type primitive int int i;
Class class Exam {} Exam e;
e = new Exam();

5.2.1 Class

A class is defined by developer (e.g., Exam ) or in the Java runtime libraries (e.g., String )

The declaration of a class type variable allocates memory for the reference (‘pointer’).

A reference variable (a variable whose type is a class) can be initialized to the special value null to indicate that it refers to no object. It is similar to the NULL value used for pointers in C. Class fields are initialized it with null, while local variables are considered non initialized and thus not usable.

Allocation and initialization of the object value are made later by its constructor

A class is an object descriptor, it defines the common structure of a set of objects. It consists of a set of members:

  • Attributes
  • Methods
  • Constructors

Example of class definition

public class Car {
  // attributes
  String color ;
  String brand;
  boolean turnedOn;

  // methods
  void turnOn(){
    turnedOn = true;
  }

  void paint(String newCol) {
    color = newCol; 
  } 

  void printState () { 
    IO.println ( " Car " + brand + " " + color ); 
    IO.println ( " the engine is " 
                              +( turnedOn ? " on " : " off " )); 
  } 
}

The corresponding UML representation is:

classDiagram
    class Employee {
        color: String
        brand: String
        turnedOn: boolean
        turnOn(): void
        paint(): void
        printState(): void
    }

Attributes (aka fields) describe the data that can be stored within objects. They are like variables, defined by:

  • Type
  • Name

Each object has its own copy of the attributes

Methods (aka operations) represent the messages that an object can accept, in the example above turnOn, paint, printState. Methods may accept arguments, e.g. paint( String ).

5.2.2 Object

An object is identified by:

  • Class, which defines its structure (in terms of attributes and methods)
  • State (values of attributes)
  • Internal unique identifier

An object can be accessed through a reference. Any object can be pointed to by one or more references, this is called aliasing.

class ExLifeCycle(){
public static void main(String[] args){
  // declare reference
  Car c;

  // create object
  c = new Car();

  // use object
   c.paint( "yellow " );
}  // reference is lost upon method exit
}

Objects and references example

1Car a1, a2;
2a1 = new Car();
3a1.paint( "yellow" );
4a2 = a1;
5a1 = null;
6a2 = null;
1
Two uninitialized references are created, they can’t be used in any way. Rememeber: a reference is not an object just a smart pointer.
2
An object is created and the “pointer” stored into the reference a1
3
Method paint() is invoked on the object through the reference a1
4
Two references point to the same object. This is known as *aliasing
5
Only one reference points to the object
6
No reference pointing to the object, which is unreachable and may be disposed of by the garbage collector

Objects Creation

Creation of an object is performed using the keyword new. It returns a reference to the area of memory containing the newly created object

Car m = new Car();

The keyword new:

  • Creates a new instance of the specific class
  • Allocates the required memory in the heap
  • Calls the constructor of the object
  • Returns a reference to the new object

5.2.3 Constructors

Constructor is a special method containing the operations (e.g. initialization of attributes) to be executed on each object as soon as it is created

It has no return type and is named like the class.

public class Car {
  boolean turnedOn;
  
  public Car(){
    turnedOn = false;
  }
}

Constructor may have parameters, e.g.

public class Car {
  String brand;
  
  public Car(String b){
    brand = b;
  }
}

This enables providing initial values for the state of the object on creation:

Car myCar = new Car("Ferrari");

Overloading of constructors is often used to make object creation more flexible.

If no constructor at all is declared, a default one with no arguments is provided implicitly by the compiler that does nothing.

Attributes are always initialized before the exceution of any possible constructor. Attributes are initialized with default values:

  • Numeric: 0 or 0L or 0.0 (zero)
  • Boolean: false
  • Reference: null

Note that the return type must not be declared for constructors. If a return type is inserted by mistake, the constructor is considered a method and it is not invoked upon instantiation! Usually IDEs signal this kind of mistake as a warning.

5.2.4 Current object – a.k.a. this

During the execution of a method it is possible to refer to the current object using the keyword this: the object upon which the method has been invoked.

In the methods this is used to refer to the current object and enable accessing attributes (and methods):

  void turnOn(){
    this.turnedOn = true;
  }

Note that this makes no sense within methods that are not invoked on an object, e.g. the main method.

5.2.5 Dotted notation

A method can be invoked using dotted notation

objectReference . method( parameters ... )

Example:

Car a = new Car();
a . turnOn();
a . paint( "Blue" );

Note:

If a method is invoked from within another method of the same object dotted notation is not mandatory, in such cases this is implied.

class Book {
  int pages;
  void readPage(int n) {}
  void readAll  () {
    for(int i=0; i <pages; i++){
      readPage(  i  ); // equivalent to this.readPage(i)
    }
  }  
}

It is not mandatory and it is automatically added by the compiler:

class Book {
  int pages;
  void readPage(int n) {}
  void   readAll  () {
    for(int   i  =0;   i  <pages;   i  ++){
      this . readPage(  i  );         
    }
  }  
}

Access to attributes is perfomed similarly, using the dotted notation

objectReference . attribute

A reference is used like a normal variable

Car a = new Car();
a  . color  = "Blue"  ;  //what’s wrong here?
boolean x = a.turnedOn  ;

Methods accessing attributes of the same object they were invoked upon do not need to use the object reference

class Car {
  String color;

  void paint(){
    color = "green";  //  * color refers to current obj
  }  
}

Athough the use of this is not mandatory, it can be useful in methods to disambiguate object attributes from local variables and arguments

class Car{
  String color;
  ...
  void paint(String color) {
    this.color = color;
  }
}

The explicit use of this for methods and attributes, is not dicated by any universal Java code convention. It is more a matter of personal taste. Nevertheless, conventions at team, project, or organization level might mandate or discourage it.

5.2.6 Chaining dotted notations

Multiple dotted notations can be combined in a single expression

Method chaining is a style of designing the methods of a class so that the continuous invocations of several methods can be expressed with a single statement instead of multiple separate statements.

public class Counter {
    private   int   value;
    public Counter reset(){
        value=0; 
    return this;
    }
    public Counter increment(  int   by){
        this.value  +=by; 
    return this;
    }

    public Counter print(){
        IO.println  (value);
        return this;
    }
}

If we want to perform multiple operations on a counter, the conventional approach is to have multiple statements:

Counter cnt = new Counter();
cnt.reset();
cnt.print();
cnt.increment(10);
cnt.print();
cnt.decrement(7);
cnt.print();

Since all the methods return this, it is possible to use an alternative compact, single statement, expression:

Counter cnt = new Counter();
cnt.reset().print()
   .increment(10).print()
   .decrement(7 ).print();

A very common example of chained notation is the user of println():

System . out . println("Hello world!");
  • System is a class in package java.lang
  • out is a (static) attribute of System referencing an object of type PrintStream (representing the standard output)
  • println() is a method of PrintStream which prints a text line followed by a new-line

5.2.7 Operations on references

The main operator that can be applied to references is the . (dot) that is usede to de-referentiate the reference and access the corresponding object.

There is NO pointer arithmetic on references in Java. Only the comparison operators == and != are defined

The relational operators check whether two references points to the same object (or not). They check identity, but do not check on the objects’ contents.

To compare contents an ad-hoc method – e.g. equals() – must be defined, which defines the criteria to compare the contents (values of attributes) of two objects.

For instance we could compare two cars based on their color:

class Car{
  String color;
  ...
  boolean equalsColor(Car other) {
1    return this.color.equals(other.color);
  }
}
1
the method equals() of String checks the content of the strings

Then it it possible to compare two cars based on their contents:

Car a = new Car();
a.paint("red");
Car b = new Car();
b.paint("red");

1sameObject = a == b;
2sameColor = a.equalColor(b);
1
a and b are evidently not the same object
2
but they have the same color

5.2.8 Overloading

Several methods in a class can share the same name. They must have have distinct signature.

In Java, the signature of a method consists of:

  • the method name
  • the ordered list of argument types

The invocation of an overloaded method is potentially ambiguous. Disambiguation is performed by the compiler based on actual parameters. The method definition whose argument types list matches the actual parameters, is selected.

Overloading example:

class Car {
  String color;
  void paint(){
    color = "white";
  }

  void paint(int i){ 
    switch(i){
      case 0: color = "white"; break
      case 1: color = "red"; break
      case 2: color = "green"; break
      case 3: color = "blue"; break
      default: color = "black"; break
    }
  }

  void paint(String newCol){
    color =   newCol  ;
  }
}

The compiler decides which variant of an overload to invoke based on the arguments, starting from the first and going on until a unique method is identified, other arguments are then converted (if possible)

public class Foo{
  public void doIt(int x, long c){ IO.println("a"); }
  public void doIt(long x, int c){ IO.println("b"); }
  public static void main(String args[]){
    Foo f = new Foo();
    f.doIt(5, (long)7); // a
    f.doIt((long)5, 7); // b
  }
}   

Constructors with overloading

class Car {  
  // …
  //    Default constructor, creates a red Ferrari
  public Car(){
        color = "red";
        brand = "Ferrari";
    }       

  // Constructor accepting the brand only
  public Car(String carBrand){
        color = "white";
      brand = carBrand;
    }       

  // Constructor accepting the brand and the color
    public Car(String carBrand, String carColor){
        color = carColor;
        brand = carBrand;
    }
}

5.3 Scope and encapsulation

5.3.1 Visibility modifiers

Visibility modifiers are applicable to members of a class

  • private
    • Member is visible and accessible from instances of the same class only
  • public
    • Member is visible and accessible from everywhere

With public attributes

class Car {
  public String color;
}
Car a = new Car();
a.color = "white"; // ok

With private attributes

class Car {
  private String color;
  public void paint(String color)
  { this.color = color;}
}
Car a = new Car();
a.color = "white";  // error
a.paint( "green");  // ok

The normal approach in terms of visibility is to make all attributes private by default. This is the application of the OO principle called information hiding. All the information managed by an object should be hidden from other objects and classes to avoid possible interference.

Table 5.3: Basic visibility access rules
code in \(\downarrow\) access to \(\rightarrow\) Private
(attribute / method)
Public
(attribute / method)
Method in the same class yes yes
Method in another class no yes

5.3.2 Getters and setters

Getters and setteres are the methods used to respectively read and write private attributes.

They allow to better control in a single point each write access to a private field

public String getColor() {
    return color;
}
public void setColor(String newColor) {
    color = newColor;
}

Example without getter/setter

public class Student {
  public String first;
  public String last;
  public int id;
  public int numExams;
  public Student(){} 
}
public class Exam {
  public int grade;
  public Student student;
  public Exam(){}
}
class StudentExample{
    public static void main(String[]   args  ) {
    // defines a student and her exams
    // lists all student's exams
    Student s=new Student("Alice", "Green", 1234);
    Exam e = new Exam(30);
    e.student = s;
    s.numExams++;
    // print vote  
    IO.println(e.grade);
    // print student  
    IO.println(e.student.last); 
    }
}

With getter and setters and delegation

class StudentExample{
    public static void main(String[]   args  ) {
    // defines a student and her exams
    // lists all student's exams
    Student s=new Student("Alice", "Green", 1234);
    Exam e = new Exam(30);
    e.setStudent(s);
    // prints its values and asks students to
    // print their data
    e.print();
    }
}
public class Student {
    private String first;
    private String last;
    private int id;
  private numExams=0;
    public String toString() {
        return first + " " + 
            last  + " " + 
            id;
    }
}
public class Exam {
    private int grade;
    private Student student;
      public void print() {
          IO.println( "Student " + student.toString() + 
                          " got " + grade);
      }

      public void setStudent(Student s) {
          this.student = s;
      s.numExams++;
    }
}

Getters and setters vs. public fields

  • Getter
    • Allow changing the internal representation without affecting
      • E.g. can perform type conversion
  • Setter
    • Allow performing checks before modifying the attribute
      • E.g. Validity of values, authorization
    • Allow side effects to keep data consistent
      • E.g. Updatinge related information

5.3.3 Modifier / Query methods

In general it is possible to classify methods into two main categories:

  • Modifiers: change the state of the object but do not return a value, e.g. setters
  • Query: return a result and do not change the state of the object, they have no side-effects, e.g. getters

In general it is possible to have a method that both has side effects and return a value.

A principle of good desing is the Modifier / Query Separation:

a method should be either a modifier or a query.

In practice to separate them:

  • Queries return a value
  • Modifiers return void

The consequence of this principle applies to the code using the conforming classes:

  • invocations to
    • queries can be added, removed, and swapped without affecting the overall behavior
    • modifiers cannot be touched without affecting the behavior

The idea si described by Martin Fowler here https://www.martinfowler.com/bliki/CommandQuerySeparation.html. The original concept was proposed by Meyer (1997).

5.4 Package

The basic unit of Object-Oriented langauges, the Class is a better mechanism of modularization than a procedure. Although it is still small, when compared to the size of large applications.

For the purpose of code organization and structuring Java provides the package. A package is a logic set of class definitions. These classes consist in several files, all stored in the same folder Each package defines a new scope (i.e., it puts bounds to visibility of names) It is therefore possible to use same class names in different package without name-conflicts

A package is identified by a name with a hierarchic structure ( fully qualified name ), e.g. java.lang contais basic language classes such as String, System, etc.

The universally applied convention to create unique names for packages is to use internet names in reverse order. For instance packages developed at Politecnico di Torino (polito.it) will be nameed it.polito.mypackage.

Examples of predefined packages

  • java.awt contain classes
    • Button
    • Window
    • Menu
    • etc.
  • java.awt.event is a sub-package containing classes:
    • MouseEvent
    • KeyEvent
    • etc.

5.4.1 Creation and usage

Declaration of a packages is done through the package statement at the beginning of each class file:

package it.polito.packagename;

Classe in other packages are not automatically accessible, they need to be imported.

In the initial part of the file – before the class declaration – import statements declare which classes will be accessible:

import packageName.className;

In addition to importing a single class for each import statement, it is possible to import all the classes in a package:

import packageName.*;

If there is not an import statement, to access a class in a specific package the fully-qualified name of the classe is required, that is the name of the package and the name of the class.

int i = myPackage.Console.readInt()

The possibility to use the fully-qualified name permits using two classes with the same name that are defined in different classes. Two import statements for classes with the same name is an error, because it creates a name clash. Althouht is is possible to import one and directly refer to the other.

import   java.sql.Date  ;
\\...
Date d1; // java.sql.Date
\\...
java.util.Date d2 = new java.util.Date();

The need to import classes residing in a different packages does not apply to the special package java.lang that contains a few fundamental classes, e.g. String and System. The compiler automatically imports all classes in the java.lang package as if there were an initial:

import java.lang.*

5.4.2 Default package

When no package is specified, the class belongs to the so-called default package.

The default package has no name. As a consequence classes in the default package cannot be accessed by classes residing in other packages. For this reason, the usage of default package is considered a bad design practice and it is discouraged.

5.4.3 Package and scope

There are specific scope and visivility rules that apply to packages.

The “interface” of a package is the set of public classes contained in the package. A package can be considered as an entity of modularization. The principle of information hiding can be applied also at package scale. The idea is to minimize the number of classes, attributes, methods visible outside the package.

To take into consideration the package level scope, a third level of visibili package visibility is available in Java. It is represented by the absence of any modifier, i.e. element without either a public or private are considered package only visible. Example:

  • public class A { }
    • Class and public members of A are visible from outside the package
  • class B { }
    • Class and any members of B are not visible from outside the package
  • private class C { }
    • Illegal: a private class is not accessible from any other class, therefore the class and its members would be visible to themselves only

Multiple packages

package it.polito.sample.one;

public class A {
  public int a1;
  private int a2;
  int a3;
  public void m1(){}
}
package it.polito.sample.one;

class B {
  public int b1;
  private int b2;
  int b3;
}
package it.polito.sample.two;

class C {
  public void m2(){}
}

The code in method m1() of class A can access all attributes (a1, a2, a3) in the same class, it can also access b1 in class B, cannot access b2 since it is private, but it can access b3 since it is package visible.

The code in method m2() of class C can access a1 in class A but neither a2 nor a3, it cannot access any member in class B since the class itself is pacakge visible and C resides in a different package.

Table 5.4: Summary of access rules
code in \(\downarrow\) access to \(\rightarrow\) Private
(attribute / method)
Package
(attribute/method)
in public class
Public
(attribute/method)
in package class
Public
(attribute/method)
in public class
same class yes yes yes yes
other class same package no yes yes yes
other class other pacakge no no no yes

5.5 Wrapper Classes

In an ideal OO world, there are only classes and objects, although, for the sake of efficiency, Java – as well as other OO languages – uses primitive types (int, float, etc.).

Wrapper classes are object versions of the primitive types, they encapsulate a single attribute of the corresponding primitive type.

They are defined in the java.lang package.

Table 5.5: Wrapper classes in Java
Primitive type Wrapper Class
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double
void Void

5.5.1 Conversions

The wrapper classes define a few method that carry on conversion operations between different types

An example of conversions for the int, Integer and String type is reported in Table 5.6. Similar conversions are available for all other numeric types.

Table 5.6: Integer conversions
V from to -> int Integer String
int Integer.valueOf(i) String.valueOf(i)
Integer I.intValue() I.toString()
String Integer.parseInt(s) Integer.valueOf(s)

Given an int value, the corresponding Integer object can be build in two different ways:

  • using a constructor new Integer(42), that is discouraged because it is not efficient (see [Pooling][#pooling]),
  • using the valueOf() method, which is the recommended methdo

Example:

Integer obj = Integer.valueOf(88);
String s = obj.toString();
int i = obj.intValue  ();
int j = Integer.parseInt("99");
int k = (obj).intValue();

5.5.2 Autoboxing

Since Java 5, the conversion between primitive types and wrapper classes is performed automatically (autoboxing)

Integer i = Integer.valueOf(2);
int j;
j =   i   + 5;   
    _//instead of: _
j =   i   .intValue ()+5;

i   = j + 2;  
      _//instead of:_
i   =    Integer.valueOf (j+2);

5.5.3 String

There is no primitive type to represent string

String literal is a quoted text

In C:

  • char s[] = "literal"
  • Equivalence between strings and char arrays

In Java

  • char[] != String
  • String class in java.lang package

See Java Characters and Strings

5.6 Arrays

An array is an ordered sequence of variables of the same type which are accessed through an index

In Java an array is an object and it is created with new and stored in the heap.

An array can contain either primitive types or object references (not object values).

Array dimension can be defined at run-time, during object creation, and cannot be change afterwards. That is arrays in Java are not resizable.

5.6.1 Declaration and Creation

An array reference can be declared with one of these equivalent syntaxes

1int[] a;
2int a[];
1
Java style recommended
2
C style, accepted but not recommended

The array declaration allocates memory space for a reference to the array, it does not allocates any memory at all for the array itself, i.e. the elements of the array are not created with the simple declaration.

The array object (i.e. the elements of the array) can be created using either:

  • the new operator, initialize all the elements to 0 or null, or
  • a static initialization , providing the array initial elements values.

Example:

1int[] a;

2a = new int[10];

3String[] s = new String[5];

4int[] primes = {2, 3, 5, 7, 11, 13};

5String[] p = { "John", "Susan" };
1
declaration of an array of int
2
creation of an array of 10 int elements, all the element are initialized to 0
3
declaration and creation of an array of 5 String (references), all the elements is initialized to null
4
declaration and static initialization of an array with 6 int elements whose values are listed,
5
declaration and static inizialization of an array with 2 String references initialized to the given strings.

5.6.2 Operations on arrays

Elements are selected with brackets [ ] (C-like), but Java makes bounds checking at run-time. Array length (number of elements) is given by attribute length

Any access to an invalid inded for an array results in an error. In particular an ArrayIndexOutOfBoundsException exception is produced. The program usually terminates with an error message that indicates the offending index and the length of the array, e.g., Index 8 out of bounds for length 8.

The common idiom for iterating on the elements of an array in Java is:

for(int i=0; i < a.length; i++){
  a[i] = i;
}

It is important to remember that an array reference is not a pointer to the first element of the array. It is a pointer – a reference indeed – to the array object, that contains the elements. It is importanto to remember that arithmetic on pointers does not exist in Java and definitely does not work with arrays.

Since Java 5 a new loop construct, called for-each is available to iterate on arrays and other containers It is a more compact notation with respect to the default idiom:

for( Type var : set_expression)

Where the set_expression can be either an array a class implementing Iterable

When the for-each construct is used, the compiler can generate automatically the loop with correct indexes. In general the usage of this construct is recommended since it is less error prone.

Example:

for(String arg : args ){
  IO.println(arg);
}

is equivalent to

for(int i=0; i < args.length; ++i){
    String arg = args[ i ];
  IO.println(arg);
}

5.6.3 Multidimensional arrays

In Java, multidimensional arrays are implemented as array of arrays:

Person[][] table = new Person[2][3];
table[0][2] = new Person("Mary");

Since rows are not stored in adjacent positions in memory they can be easily changed

double[][] balance = new double[5][6];
\\...
double[] temp = balance[i];
balance[i] = balance[j];
balance[j] = temp;

It is also possible to have rows with different lengths since the type of an array the same independently of its length.

5.6.4 Variable arguments

It is possible to pass a variable number of arguments to a method using the varargs notation

method( type ... args )

The compiler assembles an array that can be used to scan the actual arguments. Type can be primitive or class.

Example

static int min(int ... values){
        int res = Integer.MAX_VALUE;
        for(int v : values){
      res=v<res?v:res;
        }
        return res;
}

public static void main(String[] args) {
  int   m = min(9,3,5,7,2,8);
  IO.println("min =" + m);
}

5.7 static and final modifiers

The static modifier can be applied to both attributes and methods to make them class-level members as opposed to the regular (non static) instance members.

5.7.1 Static attributes

A static attribute represents a property which is common to all instances of a class A single copy of a static attribute is shared by all instances of the class. Sometimes they are called class attributes as opposed to the regular instance attributes.

Static attributes of a class exist before any object of that class is created. A change performed by any object is visible to all instances at once.

Static attributes can be used to keep a shared property, e.g. a count of created instances, or a pool of all instances, or to keep a common constant value used by all objects.

class Car {
  static int countBuiltCars = 0;

  public Car(){
    countBuiltCars++;
  }
}
Note

The use of static attributes should be limited to few very limited a specific cases. In general it is a good programming practice to avoid static attributes because they can make the code untestable and hard to debug.

5.7.2 Static methods

Static methods are not related to any instance, that is they do not define the implicit this reference. Therefore they cannot direcly access a regular (instance) attribute without explicitly dereferencing a reference.

The advantage of static methods is that they do not need an object to be created in order to invoked and executed. This is particularly useful, e.g., for the main method, which is called by the JVM when a program is started.

public class HelloWorld, {
  public static void main (String args[]) {
     IO.println("Hello Java World!");
  }
}

If the main method is not declared as static the JVM need to instantiate an object (if a suitable constructor is available) and then invoke the method.

class Car {
  private static int countBuiltCars = 0;

  public Car(){
    countBuiltCars++;
  }

  public static int howManyCars(){
    return countBuiltCars;
  }
}

To access a static member – both attribute and method – the name of the class is used:

Car.howManyCars(10);

It is possible to import all static items using the import static statement

import static pakage.Utility.*;

Such statment makes all static members of the imported class accessible with their simple name, without the need to prefix them with the class name.

There are two main motivations for using static methods:

  • Implement functions
    • methods whose result depends exclusively on the arguments, avoid creating an object just to invoke the method (see e.g., main() ).
  • Provide ideal factory method
    • method that can be used to create an instance

5.7.3 Function methods

A “function” is a method whose return value depends only on the arguments. Functions are typically defined as static because they do not need any attribute.

Such functions are often collected within a utility class, that is a class containing static function methods only.

Wrapper types include several function methods for conversion purposes, although they are not utility classes.

There are several examples, in the Java standard library, of predefined utility classes:

  • Math
    • Mathematical functions
  • Arrays
    • Functions to operate on arrays
  • IO
    • Functions to perform console I/O
  • System
    • Interact with the operating system
  • Objects
    • Functions to operate on object

5.7.3.1 Class Math

Defines several math-related function methods:

  • Trigonometric functions (sin(), cos(), tan())
  • Min-max (min(), max())
  • Exponential and logarithms ( exp(), log() )
  • Truncations (round(), ceil(), floor() )
  • Random number generation (random())
  • Powers (power(), sqrt())

5.7.3.2 Class Arrays

Providea set of utility functions that work with arrays

  • Binary search ( binarySearch() )
  • Copy ( copyOf() , copyOfRange() )
  • Equality ( equals() , deepEquals() )
  • Fill-in ( fill() )
  • Sorting ( sort() )
  • String representation ( toString() )

5.7.3.3 Class IO

The class IO provides a few functions to perform console-based I/O operations. It defines the following methods:

  • static void print(Object obj) Prints an object value (also primitives)
  • static void println(Object obj) Prints an object value (also primitives) with newline
  • static void println() Prints a new line
  • static String readln(String prompt) Reads a string from the console with a prompt
  • static String readln() Reads a string from the console

5.7.3.4 Class System

Class System is a general purpose utility class. The main functions are:

  • long currentTimeMillis() Current system time in milliseconds
  • void exit(int code) Terminates the execution of the JVM
  • final PrintStream out Standard output stream,
    • Also err for standard error

5.7.4 Factory method

A factory method is a method used to create an object. It Encapsulates an explicit object creation with the new operator.

A factory method can serve several purposes:

  • Perform a check on the parameter, in case the parameter are invalid it can retun, e.g. null, a constructor is not allowed to not return a value.
  • Return objects from a pool to reduce the creation time and minimize memory occupation of immutable objects
  • Maintain a collection of created objects for any purpose
  • Control the allocation of new objectsm, see e.g., Singleton pattern

An example of factory methods that create an Integer object are:

  • valueOf(int)
    • Replaces new Integer(int)
    • Cache values in the range -128 to 127
  • valueOf(String)
    • Returns the integer corresponding to the parsed string
    • Same as: new Integer(Integer.parseInt(s))

5.7.5 Final

When a attribute is declared as final, it cannot be changed after object construction. It can be initialized inline or by the constructor

class Student {
  final int years=3;
  final String id;
  public Student(String id){
      this.id   = id;
  }
}

When a method argument is declared as final it cannot be changed. Non final parameters are treated as local variables (initialized by the caller).

When a local variable is declared as final it cannot be changed after initialization. Note that initialization can occur at declaration or later

5.7.6 Constants

In Java the declaration of constants uses the combined static final modifiers. The final implies not modifiable, while static implies there is only one copy, i.e. it is non redundant.

final static float PI = 3.14;

PI = 16.0;        _// ERROR, no changes_
final static int SIZE;   // missing init

The conding convention in Java states that constants must be named all uppercase.

5.7.7 Static initialization block

A block of code preceded by static is called static initialization block. It can appear within class declaration, outside any method. It is executed at class loading time and can be used to initialize constants

public final static double 2PI;
static {
  2PI =   Math.acos  (-1);
}

5.7.8 Example: Global directory

Code that manages a global name directory

class Directory {
  public final static Directory root;
  static {
    root = new Directory();
  }
  // …
}

What if not always useful and expensive creation?

Manages a global directory

class Directory {
  private static Directory root;
  public static Directory   getInstance  (){
    if(root==null){
      root = new Directory();
    }
    return root;
  }
  // …
}

Created on-demand at first usage

5.7.9 Singleton Pattern

  • Context:
    • A class represents a concept that requires a single instance
  • Problem:
    • Clients could use this class in an inappropriate way

See slide deck on design patterns

PATTER IMAGE

public class Singleton{
  private Singleton() { } // cannot be instantiated using new
  private static Singleton instance;
  public static Singleton getInstance(){
    if(instance==null){
          instance = new Singleton();
    }
    return instance;
  }
}

5.7.10 Fluent Interfaces

Method to design OO API based on extensive use of method chaining

The goal is to improve readability

  • Code looks like prose
  • Often used to build complex objects

Creates a sort of Domain Specific Language (DSL) leveraging the syntax of the host language

See: https://www.martinfowler.com/bliki/FluentInterface.html

Example of usual, non-fluent, API to manage measure with the relative units:

Measure power = new Measure(10.4);
power.addUnit  ("kg", 1);
power.addUnit  ("m", 2);
power.addUnit  ("s", -3);
power.setPrecision  (2);

Fluent

Measure power = Measure.value(10.4).
        is("kg").by("m").squared().by("s").to(-3).
       withPrecision(2).done();

Can be implemented as

public class Measure {
  private double value  ;
  private Unit unit  ;
  private int precision  ;

  publicMeasure(double value) {
      this.value = value  ;
  }

  public void setPrecision(int precision) {
      this.precision = precision;
  }

  public void addUnit(String name, double exp){
      unit = new Unit(name, exp);
  }

  public static Builder value(double v){
      return new Builder(v);
  }
}

Fluent Builder


public static class Builder{
  private Measure object;
  private String unitName;

  public Builder(double v){ object = new Measure(v);}

  public Builder is(String name) {
      unitName = name;    
      return this  ;
  }

  public Builder by(String name) {
    if(unitName != null) {
      object.addUnit( unitName, 1);
    } 
    unitName = name;    
    return this  ;
  }

  public Builder squared() {
      object.addUnit(unitName, 2);  
      unitName = null;
      return this  ;
  }

  public Builder to(double exponent) {
      object.addUnit(unitName, exponent);
      unitName = null;
      return this  ;
  }

  public Measure done() { return object; }

  public Builder withPrecision(int precision) {
      object.setPrecision( precision );
      return this;
  }
}

5.8 Other Types

In addition to classes, that represent the main construct in Java, it is possible to define other types:

  • enumerative types
  • records
  • interfaces

5.8.1 Enum

Defines an enumerative type

public  enum Suits {
  SPADES, HEARTS, DIAMONDS, CLUBS
}

Variables of enum types can assume only one of the enumerated values, e.g. Suits card = Suits.HEARTS; They allow much stricter static checking compared to integer constants (e.g. in C)

Enum can are similar to a class that automatically instantiates the values

class Suits {
   public static final Suits HEARTS=
                            new   Suits   ("HEARTS", 0);
   public static final Suits   DIAMONDS=
                                            new   Suits  ("DIAMONDS", 1);
   public static final Suits   CLUBS= 
                                            new   Suits   ("CLUBS", 2);
   public static final Suits   SPADES= 
                                              new   Suits   ("SPADES", 3);
   private Suits(String enumName, int index) {}  
 }

5.8.2 Record

The record keyword allow defining an immutable class with a compact notation. For instace to declare a 2D point:

record Point(int x, int y){}

The compiler transparenlty provides

  • public final attributes for each parameter of the constructor,
  • getter methods with the same name of the attributes, i.e. x() and y().

It is also possible to define additional (query) methods.

Overall it is equivalent to a class but much more compact, the full class declaration would be:

class Point(){
  public final int x;
  public final int y;
  public Point(int x, int y){
    this.x=x;
      this.y=y;
  } 
}

5.8.3 Interfaces

Interfaces are incomplete types that can define methods without implementation. They are mean to be implemented trough inheritance.

5.9 Nested Classes

It is possible to declare classes nested within other classes.

Java has several types of nested classes:

  • Static nested class, the class is defined within the container name space and scope,
  • Inner class, as the static and in addition the nested class contains a link to the creator container object
  • Local inner class, defined in a method, similar to the inner class and in addition can access local variables
  • Anonymous inner class, similar to the previous but has no explicit name.

5.9.1 (Static) Nested class

A class declared inside another class:

package pkg;
class Outer {
  static class Nested {
  }
}

Similar to regular classes, but is is subject to the member visibility rules. A nested class lies within the scope of the outer class. The fully qualified name includes the outer class: pkg.Outer.Nested

Static nested classes can be used to hide classes that are used only within another class, the advantages are:

  • Reduce namespace pollution
  • Encapsulate internal details

Example:

public class StackOfInt{

1  private static class Element {
    int value;
    Element next;
    Element(int v, Element n){
      value=v; 
      next=n;
    }
  }

2  private Element head;

  public void push(int v){  
    head = new Element(v,head);
  }

  public int pop(){
    int v = head.value;
    head = head.next;
    return v;
  }
}
1
nested class that is visible only to members of the outer class StackOfInt
2
the nested class Element is used as any other class

5.9.2 Inner Class

It is declared like a normal nested class but without the static modifier:

package pkg;
class Outer {
  class Inner{
  }
}

Any inner class instance is associated with the instance of its enclosing class that instantiated it (i.e. it is not static). As a consequence, it cannot be instantiated from a static method or from other classes. It has direct access to the enclosing object’s methods and fields.

public class StackOfInt{

1  private static class Element {
    int value;
    Element next;
    Element(int v){
      value = v; 
2      next = head;
    }
  }

3  private Element head;

  public void push(int v){  
4    head = new Element(v);
  }

  public int pop(){
    int v = head.value;
    head = head.next;
    return v;
  }
}
1
nested class that is visible only to members of the outer class StackOfInt
2
the inner class Element can access the container object attribute head
3
the nested class Element is used as any other class
4
the head argument can be omitted since the inner class has direct access.

5.9.3 Local Inner Class

A local inner class is declared inside a method

public void m(){
  int   j=1;
  class X {
    int plus(){ return j + 1; }
  }
  X x = new X();
  IO.println  (  x.plus  ());
}

References to local variables are allowed, although they are replaced with “current” value. The set of such local variables is called closure

public void m(){
  int   j=1;
  class X {
    int plus(){ return j + 1; }
  }
  j++; // ERROR!
  X x = new X();
  IO.println(  x.plus() );
}

Local variable cannot be changed after being referred to by an inner class. Otherwise there would be ambiguity as to what result should we expect.

Local variables used in local inner classes should be declared final, or be effectively final.

5.9.4 Anonymous Inner Class

Local class without a name. Only possible with inheritance, i.e. implement an interface, or extend a class

See: inheritance in general and inheritance for further details.

5.10 Memory Management

5.10.1 Memory types

Depending on the kind of elements they include, Java manages three different types of memory:

  • Static memory
    • elements living for all the execution of a program (class definitions, static variables)
  • Heap (dynamic memory)
    • elements created at run-time (with ‘new’)
  • Stack
    • elements defined in a code block (local variables and method parameters)

Heap memory is a part of the memory used by a program during execution to store data dynamically created at run-time. In Java class instances (objects) are always stored in the heap. The operator new returns a reference that points to a memory area in the heap.

In summary, Java has the following types of variables:

  • Instance variables, also known as fields or attributes: stored within objects (in the heap)
  • Local Variables: stored in the Stack; method pararameters are treated as local variables
  • Static Variables: stored in static memory

5.10.2 Garbage collector

Memory release, in Java, is no longer a programmer’s concern, Java is a managed memory language.

A component of the JVM called Garbage Collector cleans the heap memory from dead objects. Periodically it analyzes references and objects in memory and then it releases the memory for objects with no active references. There is no predefined timing when such – computationally intensive – procedure is performed. The method System.gc() can be used to suggest the GC to run as soon as possible, but the standard specification of Java does not mandate the suggestion will be obeyed.

5.10.3 Finalization

Before the object is actualy destroyed, i.e. the memory released, the method public void finalize() – if defined – is invoked. The finalize() method is intended to release resources owned by the object.

Warning: there is no guarantee an object will be ever released, therefore there is no guarantee that finalize() will be eventually called before program termination.

Finalization and garbage collection

class   Item {      
  public   void   finalize(){
    IO.println("Finalizing"); 
  }
}
```java

```java
public static void  main(String args[]){
  Item i = new Item();
  i = null;
  System.gc(); //   _probably will finalize object_
}

5.11 Wrap-up

  • Java syntax is very similar to that of C
  • New primitive type: boolean
  • Objects are accessed through references
    • References are disguised pointers!
  • Reference definition and object creation are separate operations
  • Different scopes and visibility levels
  • Arrays are objects
  • Wrapper types encapsulate primitive types