classDiagram
class Employee {
color: String
brand: String
turnedOn: boolean
turnOn(): void
paint(): void
printState(): void
}
5 Basic Features
Learning objectives
- Learn the syntax of the Java language
- Understand the primitive types
- Understand how classes are defined and objects used
- Understand how modularization and scoping work
- Understand how arrays work
- Learn about wrapper types
5.1 Fundamental syntax
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-elseswitchwhiledo-whileforbreakcontinue
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 ){ ... } //NOIn 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.
| 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 syntax123256789L0xff34123.750.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
| 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:
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
- 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 referencea1 - 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:
0or0Lor0.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 . attributeA 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!");Systemis a class in packagejava.langoutis a (static) attribute ofSystemreferencing an object of typePrintStream(representing the standard output)println()is a method ofPrintStreamwhich 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()ofStringchecks the content of the strings
Then it it possible to compare two cars based on their contents:
- 1
-
aandbare 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"; // okWith 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"); // okThe 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.
| 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
- Allow changing the internal representation without affecting
- 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
- Allow performing checks before modifying the attribute
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.awtcontain classesButtonWindowMenu- etc.
java.awt.eventis a sub-package containing classes:MouseEventKeyEvent- 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.
| 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.
| 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.
| 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[]!=StringStringclass injava.langpackage
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
- 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
newoperator, initialize all the elements to0ornull, or - a static initialization , providing the array initial elements values.
Example:
- 1
-
declaration of an array of
int - 2
-
creation of an array of 10
intelements, all the element are initialized to0 - 3
-
declaration and creation of an array of 5
String(references), all the elements is initialized tonull - 4
-
declaration and static initialization of an array with 6
intelements whose values are listed, - 5
-
declaration and static inizialization of an array with 2
Stringreferences 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++;
}
}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()).
- methods whose result depends exclusively on the arguments, avoid creating an object just to invoke the method (see e.g.,
- 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 newlinestatic void println()Prints a new linestatic String readln(String prompt)Reads a string from the console with a promptstatic 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 millisecondsvoid exit(int code)Terminates the execution of the JVMfinal PrintStream outStandard output stream,- Also
errfor standard error
- Also
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
- Replaces new
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 initThe 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
publicfinalattributes for each parameter of the constructor,- getter methods with the same name of the attributes, i.e.
x()andy().
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:
- 1
-
nested class that is visible only to members of the outer class
StackOfInt - 2
-
the nested class
Elementis 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.
- 1
-
nested class that is visible only to members of the outer class
StackOfInt - 2
-
the inner class
Elementcan access the container object attributehead - 3
-
the nested class
Elementis used as any other class - 4
-
the
headargument 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
5.1.1 Comments
Java allows both C-style comments (multi-lines):
and comments on a single line: