Object-Oriented Programming - Language - Java 8 Pocket Guide (2014)

Java 8 Pocket Guide (2014)

Part I. Language

Chapter 5. Object-Oriented Programming

Basic elements of object-oriented programming (OOP) in Java include classes, objects, and interfaces.

Classes and Objects

Classes define entities that usually represent something in the real world. They consist of a set of values that holds data and a set of methods that operates on the data.

An instance of a class is called an object, and it is allocated memory. There can be multiple instances of a class.

Classes can inherit data members and methods from other classes. A class can directly inherit from only one class—the superclass. A class can have only one direct superclass. This is called inheritance.

When implementing a class, the inner details of the class should be private and accessible only through public interfaces. This is called encapsulation. The JavaBean convention is to use accessor and mutator methods (e.g., getFirstName() and setFirstName("Leonardina")) to indirectly access the private members of a class and to ensure that another class cannot unexpectedly modify private members. Returning immutable values (i.e., strings, primitive values, and objects intentionally made immutable) is another way to protect the data members from being altered by other objects.

Class Syntax

A class has a class signature, optional constructors, data members, and methods:

[javaModifiers] class className

[extends someSuperClass]

[implements someInterfaces separated by commas] {

// Data member(s)

// Constructor(s)

// Method(s)

}

Instantiating a Class (Creating an Object)

An object is an instance of a class. Once instantiated, objects have their own set of data members and methods:

// Sample class definitions

public class Candidate {...}

class Stats extends ToolSet {...}

public class Report extends ToolSet

implements Runnable {...}

Separate objects of class Candidate are created (instantiated) using the keyword new:

Candidate candidate1 = new Candidate();

Candidate candidate2 = new Candidate();

Data Members and Methods

Data members, also known as fields, hold data about a class. Data members that are nonstatic are also called instance variables:

[javaModifier] type dataMemberName

Methods operate on class data:

[javaModifiers] type methodName (parameterList)

[throws listOfExceptionsSeparatedByCommas] {

// Method body

}

The following is an example of class Candidate and its data members and methods:

public class Candidate {

// Data members or fields

private String firstName;

private String lastName;

private int year;

// Methods

public void setYear (int y) { year = y; }

public String getLastName() {return lastName;}

} // End class Candidate

Accessing Data Members and Methods in Objects

The dot operator (.) is used to access data members and methods in objects. It is not necessary to use the dot operator when accessing data members or methods from within an object:

candidate1.setYear(2016);

String name = getFirstName() + getLastName();

Overloading

Methods, including constructors, can be overloaded. Overloading means that two or more methods have the same name but different signatures (parameters and return values). Note that overloaded methods must have different parameters, and they may have different return types; but having only different return types is not overloading. The access modifiers of overloaded methods can be different:

public class VotingMachine {

...

public void startUp() {...}

private void startUp(int delay) {...}

}

When a method is overloaded, it is permissible for each of its signatures to throw different checked exceptions:

private String startUp(District d) throws new

IOException {...}

Overriding

A subclass can override the methods it inherits. When overridden, a method contains the same signature (name and parameters) as a method in its superclass, but it has different implementation details.

The method startUp() in superclass Display is overridden in class TouchScreenDisplay:

public class Display {

void startUp(){

System.out.println("Using base display.");

}

}

public class TouchScreenDisplay extends Display {

void startUp() {

System.out.println("Using new display.");

}

}

Rules regarding overriding methods include the following:

§ Methods that are not final, private, or static can be overridden.

§ Protected methods can override methods that do not have access modifiers.

§ The overriding method cannot have a more restrictive access modifier (i.e., package, public, private, protected) than the original method.

§ The overriding method cannot throw any new checked exceptions.

Constructors

Constructors are called upon object creation and are used to initialize data in the newly created object. Constructors are optional, have exactly the same name as the class, and they do not have a return in the body (as methods do).

A class can have multiple constructors. The constructor that is called when a new object is created is the one that has a matching signature:

public class Candidate {

...

Candidate(int id) {

this.identification = id;

}

Candidate(int id, int age) {

this.identification = id;

this.age = age;

}

}

// Create a new Candidate and call its constructor

Candidate candidate = new Candidate(id);

Classes implicitly have a no-argument constructor if no explicit constructor is present. Note that if a constructor with arguments is added, there will be no no-argument constructor unless it is manually added.

Superclasses and Subclasses

In Java, a class (known as the subclass) can inherit directly from one class (known as the superclass). The Java keyword extends indicates that a class inherits data members and methods from another class. Subclasses do not have direct access to private members of its superclass, but do have access to the public and protected members of the superclass. A subclass also has access to members of the superclass where the same package is shared (package-private or protected). As previously mentioned, accessor and mutator methods provide a mechanism to indirectly access the private members of a class, including a superclass:

public class Machine {

boolean state;

void setState(boolean s) {state = s;}

boolean getState() {return state;}

}

public class VotingMachine extends Machine {

...

}

The keyword super in the Curtain class’s default constructor is used to access methods in the superclass overridden by methods in the subclass:

public class PrivacyWall {

public void printSpecs() {...}

}

public class Curtain extends PrivacyWall {

public void printSpecs() {

...

super.printSpecs();

}

}

Another common use of the keyword super is to call the constructor of a superclass and pass it parameters. Note that this call must be the first statement in the constructor calling super:

public PrivacyWall(int l, int w) {

int length = l;

int width = w;

}

public class Curtain extends PrivacyWall {

// Set default length and width

public Curtain() {super(15, 25);}

}

If there is not an explicit call to the constructor of the superclass, an automatic call to the no-argument constructor of the superclass is made.

The this Keyword

The three common uses of the this keyword are to refer to the current object, to call a constructor from within another constructor in the same class, and to pass a reference of the current object to another object.

To assign a parameter variable to an instance variable of the current object:

public class Curtain extends PrivacyWall {

String color;

public void setColor(String color) {

this.color = color;

}

}

To call a constructor from another constructor in the same class:

public class Curtain extends PrivacyWall {

public Curtain(int length, int width) {}

public Curtain() {this(10, 9);}

}

To pass a reference of the current object to another object:

// Print the contents of class curtain

System.out.println(this);

public class Builder {

public void setWallType(Curtain c) {...}

}

Variable-Length Argument Lists

Since Java 5.0, methods can have a variable-length argument list. Called varargs, these methods are declared such that the last (and only the last) argument can be repeated zero or more times when the method is called. The vararg parameter can be either a primitive or an object. An ellipsis (…) is used in the argument list of the method signature to declare the method as a vararg. The syntax of the vararg parameter is as follows:

type... objectOrPrimitiveName

Here is an example of a signature for a vararg method:

public setDisplayButtons(int row,

String... names) {...}

The Java compiler modifies vararg methods to look like regular methods. The previous example would be modified at compile time to:

public setDisplayButtons(int row,

String [] names) {...}

It is permissible for a vararg method to have a vararg parameter as its only parameter:

// Zero or more rows

public void setDisplayButtons (String... names)

{...}

A vararg method is called the same way an ordinary method is called except that it can take a variable number of parameters, repeating only the last argument:

setDisplayButtons("Jim");

setDisplayButtons("John", "Mary", "Pete");

setDisplayButtons("Sue", "Doug", "Terry", "John");

The printf method is often used when formatting a variable set of output, because printf is a vararg method. From the Java API, type the following:

public PrintStream printf(String format,

Object... args)

The printf method is called with a format string and a variable set of objects:

System.out.printf("Hello voter %s%n

This is machine %d%n", "Sally", 1);

For detailed information on formatting a string passed into the printf method, see java.util.Formatter.

The enhanced for loop (for each) is often used to iterate through the variable argument:

printRows() {

for (String name: names)

System.out.println(name);

}

Abstract Classes and Abstract Methods

Abstract classes and methods are declared with the keyword abstract.

Abstract Classes

An abstract class is typically used as a base class and cannot be instantiated. It can contain abstract and nonabstract methods, and it can be a subclass of an abstract or a nonabstract class. All of its abstract methods must be defined by the classes that inherit (extend) it unless the subclass is also abstract:

public abstract class Alarm {

public void reset() {...}

public abstract void renderAlarm();

}

Abstract Methods

An abstract method contains only the method declaration, which must be defined by any nonabstract class that inherits it:

public class DisplayAlarm extends Alarm {

public void renderAlarm() {

System.out.println("Active alarm.");

}

}

Static Data Members, Static Methods, Static Constants, and Static Initializers

Static data members, methods, constants, and initializers reside with a class and not instances of classes. Static data members, methods, and constants can be accessed in the class in which they are defined or in another class using the dot operator.

Static Data Members

Static data members have the same features as static methods and are stored in a single location in memory.

They are used when only one copy of a data member is needed across all instances of a class (e.g., a counter):

// Declaring a static data member

public class Voter {

static int voterCount = 0;

public Voter() { voterCount++;}

public static int getVoterCount() {

return voterCount;

}

}

...

int numVoters = Voter.voterCount;

Static Methods

Static methods have the keyword static in the method declaration:

// Declaring a static method

class Analyzer {

public static int getVotesByAge() {...}

}

// Using the static method

Analyzer.getVotesByAge();

Static methods cannot access nonstatic methods or variables because static methods are associated with a class, not an object.

Static Constants

Static constants are static members declared constant. They have the keywords static and final, and a program cannot change them:

// Declaring a static constant

static final int AGE_LIMIT = 18;

// Using a static constant

if (age == AGE_LIMIT)

newVoter = "yes";

Static Initializers

Static initializers include a block of code prefaced by the keyword static. A class can have any number of static initializer blocks, and it is guaranteed that they will run in the order in which they appear. Static initializer blocks are executed only once per class initialization. A block is ran when the JVM class loader loads StaticClass, which is upon the initial reference to the code.

// Static Initializer

static {

numberOfCandidates = getNumberOfCandidates();

}

Interfaces

Interfaces provide a set of declared public methods that do not have method bodies. A class that implements an interface must provide concrete implementations of all the methods defined by the interface, or it must be declared abstract.

An interface is declared using the keyword interface, followed by the name of the interface and a set of method declarations.

Interface names are usually adjectives and end with “able” or “ible,” as the interface provides a capability:

interface Reportable {

void genReport(String repType);

void printReport(String repType);

}

A class that implements an interface must indicate so in its class signature with the keyword implements:

class VotingMachine implements Reportable {

public void genReport (String repType) {

Report report = new Report(repType);

}

public void printReport(String repType) {

System.out.println(repType);

}

}

TIP

Classes can implement multiple interfaces, and interfaces can extend multiple interfaces.

Enumerations

In simplest terms, enumerations are a set of objects that represent a related set of choices:

enum DisplayButton {ROUND, SQUARE}

DisplayButton round = DisplayButton.ROUND;

Looking beyond simplest terms, an enumeration is a class of type enum and it is a singleton. Enum classes can have methods, constructors, and data members:

enum DisplayButton {

// Size in inches

ROUND (.50f),

SQUARE (.40f);

private final float size;

DisplayButton(float size) {this.size = size;}

private float size() { return size; }

}

The method values() returns an array of the ordered list of objects defined for the enum:

for (DisplayButton b : DisplayButton.values())

System.out.println("Button: " + b.size());

Annotation Types

Annotations provide a way to associate metadata (data about data) with program elements at compile time and runtime. Packages, classes, methods, fields, parameters, variables, and constructors can be annotated.

Built-in Annotations

Java annotations provide a way to obtain metadata about a class. Java has three built-in annotation types, as depicted in Table 5-1. These annotation types are contained in the java.lang package.

Annotations must be placed directly before the item being annotated. They do not have any parameters and do not throw exceptions. Annotations return primitive types, enumerations, class String, class Class, annotations, and arrays (of these types).

Table 5-1. Built-in annotations

Annotation type

Description

@Override

Indicates that the method is intended to override a method in a superclass.

@Deprecated

Indicates that a deprecated API is being used or overridden.

@SuppressWarnings

Used to selectively suppress warnings.

The following is an example of their use:

@Override

public String toString() {

return super.toString() + " more";

}

Because @Override is a marker annotation, a compile warning will be returned if the method to be overridden cannot be found.

Developer-Defined Annotations

Developers can define their own annotations using three annotation types. A marker annotation has no parameters, a single value annotation has a single parameter, and a multivalue annotation has multiple parameters.

The definition of an annotation is the symbol @, followed by the word interface, followed by the name of the annotation.

Repeated annotations are permitted.

The meta-annotation Retention indicates that an annotation should be retained by the VM so that it can be read at runtime. Retention is in the package java.lang.annotation:

@Retention(RetentionPolicy.RUNTIME)

public @interface Feedback {} // Marker

public @interface Feedback {

String reportName();

} // Single value

public @interface Feedback {

String reportName();

String comment() default "None";

} // Multi value

Place the user-defined annotation directly before the item being annotated:

@Feedback(reportName="Report 1")

public void myMethod() {...}

Programs can check the existence of annotations and obtain annotation values by calling getAnnotation() on a method:

Feedback fb =

myMethod.getAnnotation(Feedback.class);

The Type Annotations Specification (also known as “JSR 308”) allows for annotations to be written in array positions and generic type arguments. Annotations may also be written with superclasses, implemented interfaces, casts, instanceof checks, exception specifications, wildcards, method references, and constructor references. See Java SE 8 for the Really Impatient by Cay S. Horstmann (Addison-Wesley) for detailed information on Annotations in these contexts.

Functional Interfaces

A functional interface, a.k.a. Single Abstract Method (SAM) interface, is an inteface that defines one and only one abstract method. The annotation @FunctionalInterface may be placed in front of an interface to declare its intention as a functional interface. An interface can have any number of default methods.

@FunctionalInterface

public interface InterfaceName {

// Only one abstract method allowed

public void doAbstractTask();

// Multiple default methods allowed

default public void performTask1(){

System.out.println("Msg from task 1.");

}

default public void performTask2(){

System.out.println("Msg from task 2.");

}

}

Instances of functional interfaces can be created with lambda expressions, method references, or constructor references.