1. Object Oriented Programming in Java
Introduction to Object Oriented Programming in Java1.1. What is Object Oriented Programming (OOP)?
- A software design method that models the characteristics of real or abstract objects using software classes and objects.
·
Characteristics of objects:
- State (what the objects have)
- Behavior (what the objects do)
- Identity (what makes them unique)
- Definition: an object is a software bundle of related fields (variables) and methods.
- In OOP, a program is a collection of objects that act on one another (vs. procedures).
- Speed
- RPM
- Gear
- Direction
- Fuel level
- Engine temperature
- Change Gear
- Go faster/slower
- Go in reverse
- Stop
- Shut-off
- VIN
- License Plate
1.2. Why OOP?
- Modularity — Separating entities into separate logical units makes them easier to code, understand, analyze, test, and maintain.
- Data hiding (encapsulation) — The implementation of an object’s private data and actions can change without affecting other objects that depend on it.
·
Code reuse through:
- Composition — Objects can contain other objects
- Inheritance — Objects can inherit state and behavior of other objects
- Easier design due to natural modeling
Although a lot of great software is implemented in procedural languages like C, OO languages typically scale better for taking on medium to large software projects.
1.3. Class vs. Object
·
A class is a template or
blueprint for how to build an object.
- A class is a prototype that defines state placeholders and behavior common to all objects of its kind.
- Each object is a member of a single class — there is no multiple inheritance in Java.
·
An object is an instance
of a particular class.
- There are typically many object instances for any one given class (or type).
- Each object of a given class has the same built-in behavior but possibly a different state (data).
- Objects are instantiated (created).
It is the design that is used to build a car of a particular type or class.
When the physical cars roll off the assembly line, those cars are instances (concrete objects) of that class.
Many people can have a 2007 BMW 335i, but there is typically only one design for that particular class of cars.
As we will see later, classification of objects is a powerful idea, especially when it comes to inheritance — or classification hierarchy.
1.4. Classes in Java
- Everything in Java is defined in a class.
- In its simplest form, a class
just defines a collection of data (like a record or a C
struct
). For example:
class Employee {
String name;
String ssn;
String emailAddress;
int yearOfBirth;
}
- The order of data fields and methods in a class is not significant.
Employee.java
There are a few exceptions to this rule (for non-
public
classes), but the accepted
convention is to have one class defined per source file.Note that in Java,
Strings
are also classes rather than being implemented as primitive types.Unlike local variables, the state variables (known as fields) of objects do not have to be explicitly initialized. Primitive fields (such as
yearOfBirth
) are automatically set to
primitive defaults (0
in
this case), whereas objects (name
,
ssn
, emailAddress
) are automatically set to null
— meaning that they do not point to
any object.1.5. Objects in Java
·
To create an object (instance) of
a particular class, use the
new
operator, followed by an invocation of a constructor for that class,
such as: new MyClass()
- The constructor method initializes the state of the new object.
- The
new
operator returns a reference to the newly created object.
·
As with primitives, the variable
type must be compatible with the value type when using object references, as
in:
Employee e = new Employee();
- To access member data or
methods of an object, use the dot (
.
) notation:variable.field
orvariable.method
()
Consider this simple example of creating and using instances of the
Employee
class:public class EmployeeDemo {
public static void main(String[] args) {
Employee e1 = new Employee();
e1.name = "John";
e1.ssn = "555-12-345";
e1.emailAddress = "john@company.com";
Employee e2 = new Employee();
e2.name = "Tom";
e2.ssn = "456-78-901";
e2.yearOfBirth = 1974;
System.out.println("Name: " + e1.name);
System.out.println("SSN: " + e1.ssn);
System.out.println("Email Address: " + e1.emailAddress);
System.out.println("Year Of Birth: " + e1.yearOfBirth);
System.out.println("Name: " + e2.name);
System.out.println("SSN: " + e2.ssn);
System.out.println("Email Address: " + e2.emailAddress);
System.out.println("Year Of Birth: " + e2.yearOfBirth);
}
}Running this code produces:
Name: John
SSN: 555-12-345
Email Address: john@company.com
Year Of Birth: 0
Name: Tom
SSN: 456-78-901
Email Address: null
Year Of Birth: 1974
1.6. Java Memory Model
·
Java variables do not contain the
actual objects, they contain references to the objects.
- The actual objects are stored in an area of memory known as the heap.
- Local variables referencing those objects are stored on the stack.
- More than one variable can hold a reference to the same object.
Figure 4. Java Memory Model
As previously mentioned, the stack is the area of memory where local variables (including method parameters) are stored. When it comes to object variables, these are merely references (pointers) to the actual objects on the heap.
Every time an object is instantiated, a chunk of heap memory is set aside to hold the data (state) of that object. Since objects can contain other objects, some of this data can in fact hold references to those nested objects.
In Java:
- Object references can
either point to an actual object of a compatible type, or be set to
null
(0
is not the same asnull
). - It is not possible to instantiate objects on the stack. Only local variables (primitives and object references) can live on the stack, and everything else is stored on the heap, including classes and static data.
1.7. Accessing Objects through References
Employee e1 = new Employee();
Employee e2 = new Employee();
// e1 and e2 refer to two independent Employee objects on the heap
Employee e3 = e1;
// e1 and e3 refer to the *same* Employee object
e3 = e2;
// Now e2 and e3 refer to the same Employee object
e1 = null;
// e1 no longer refers to any object. Additionally, there are no references
// left to the Employee object previously referred to by e1. That "orphaned"
// object is now eligible for garbage collection.
Note
|
|
The statement Employee e3 = e2;
sets e3 to point to the
same physical object as e2 .
It does not duplicate the object. Changes to e3 are reflected in e2 and vice-versa. |
1.8. Garbage Collection
·
Unlike some OO languages, Java
does not support an explicit destructor method to delete an object
from memory.
- Instead, unused objects are deleted by a process known as garbage collection.
·
The JVM automatically runs garbage
collection periodically. Garbage collection:
- Identifies objects no longer in use (no references)
- Finalizes those objects (deconstructs them)
- Frees up memory used by destroyed objects
- Defragments memory
·
Garbage collection introduces
overhead, and can have a major affect on Java application performance.
- The goal is to avoid how often and how long GC runs.
- Programmatically, try to avoid unnecessary object creation and deletion.
- Most JVMs have tuning parameters that affect GC performance.
- Frees up programmers from having to manage memory. Manually identifying unused objects (as in a language such as C++) is not a trivial task, especially when programs get so complex that the responsibility of object destruction and memory deallocation becomes vague.
·
Ensures integrity of programs:
- Prevents memory leaks — each object is tracked down and disposed off as soon as it is no longer used.
- Prevents reallocation of objects that are still in use or have already been released. In Java it is impossible to explicitly reallocate an object or use one that has already been reallocated. In a language such as C++ dereferencing null pointers or double-freeing objects typically crashes the program.
java
-X
), you can:- Set minimum amount of
memory (e.g.
-Xmn
) - Set maximum amount of
memory (e.g.
-Xmx
,-Xss
) - Tune GC and memory
integrity (e.g.
-XX:+UseParallelGC
)
1.9. Methods in Java
·
A method is a set of
instructions that defines a particular behavior.
- A method is also known as a function or a procedure in procedural languages.
·
Java allows procedural programming
through its static methods (e.g. the
main()
method). - A static method belongs to a class independent of any of the class’s instances.
- It would be possible to implement an entire program through static methods, which call each other procedurally, but that is not OOP.
public class EmployeeDemo {
public static void main(String[] args) {
Employee e1 = new Employee();
e1.name = "John";
e1.ssn = "555-12-345";
e1.emailAddress = "john@company.com";
Employee e2 = new Employee();
e2.name = "Tom";
e2.ssn = "456-78-901";
e2.yearOfBirth = 1974;
printEmployee(e1);
printEmployee(e2);
}
static void printEmployee(Employee e) {
System.out.println("Name: " + e.name);
System.out.println("SSN: " + e.ssn);
System.out.println("Email Address: " + e.emailAddress);
System.out.println("Year Of Birth: " + e.yearOfBirth);
}
}Running this code produces the same output as before:
Name: John
SSN: 555-12-345
Email Address: john@company.com
Year Of Birth: 0
Name: Tom
SSN: 456-78-901
Email Address: null
Year Of Birth: 1974
1.10. Methods in Java (cont.)
·
In true OOP, we combine an
object’s state and behavior together.
- For example, rather than having external code access the individual fields of an Employee object and print the values, an Employee object could know how to print itself:
class Employee {
String name;
String ssn;
String emailAddress;
int yearOfBirth;
void print() {
System.out.println("Name: " + name);
System.out.println("SSN: " + ssn);
System.out.println("Email Address: " + emailAddress);
System.out.println("Year Of Birth: " + yearOfBirth);
}
}
public class EmployeeDemo {
public static void main(String[] args) {
Employee e1 = new Employee();
e1.name = "John";
e1.ssn = "555-12-345";
e1.emailAddress = "john@company.com";
Employee e2 = new Employee();
e2.name = "Tom";
e2.ssn = "456-78-901";
e2.yearOfBirth = 1974;
e1.print();
e2.print();
}
}Running this code produces the same output as before:
Name: John
SSN: 555-12-345
Email Address: john@company.com
Year Of Birth: 0
Name: Tom
SSN: 456-78-901
Email Address: null
Year Of Birth: 1974
1.11. Method Declarations
Each method has a declaration of the following format:modifiers returnType name(params) throws-clause { body }
modifiers
public
,
private
, protected
, static
, final
,
abstract
, native
, synchronized
returnType
A primitive type, object type, or
void
(no return value)
name
The name of the method
params
paramType paramName
, …
throws-clause
throws
ExceptionType
, …
body
The method’s code, including the
declaration of local variables, enclosed in braces
Note that abstract methods do not have a body (more on this later).Here are some examples of method declarations:
public static void print(Employee e) { ... }
public void print() { ... }
public double sqrt(double n) { ... }
public int max(int x, int y) { ... }
public synchronized add(Employee e) throws DuplicateEntryException { ... }
public int read() throws IOException { ... }
public void println(Object o) { ... }
protected void finalize() throws Throwable { ... }
public native void write(byte[] buffer, int offset, int length) throws IOException { ... }
public boolean equals(Object o) { ... }
private void process(MyObject o) { ... }
void run() { ... }
1.12. Method Signatures
·
The signature of a method
consists of:
- The method name
- The parameter list (that is, the parameter types and their order)
·
The signature does not
include:
- The parameter names
- The return type
- Each method defined in a class must have a unique signature.
- Methods with the same name but different signatures are said to be overloaded.
1.13. Invoking Methods
- Use the dot (
.
) notation to invoke a method on an object:objectRef.method
(params)
·
Parameters passed into methods are
always copied (“pass-by-value”).
- Changes made to parameter variables within the methods do no affect the caller.
- Object references are also copied, but they still point to the same object.
void setYearOfBirth(int year) {
yearOfBirth = year;
year = -1; // modify local variable copy
}We invoke it from
EmployeeDemo.main()
as:int y = 1974;
e2.setYearOfBirth(y);
System.out.println(e2.yearOfBirth); // prints 1974
System.out.println(y); // prints 1974
On the other hand, we add this method to our EmployeeDemo class:static void printYearOfBirth(Employee e) {
System.out.println(e.yearOfBirth);
e.yearOfBirth = -1; // modify object's copy
}We invoke it from
EmployeeDemo.main()
as:printYearOfBirth(e2); // prints 1974
System.out.println(e2.yearOfBirth); // prints -1
1.14. Static vs. Instance Data Fields
·
Static (or class) data fields
- Unique to the entire class
- Shared by all instances (objects) of that class
- Accessible using
ClassName.fieldName
- The class name is optional within static and instance methods of the class, unless a local variable of the same name exists in that scope
- Subject to the declared access mode, accessible from outside the class using the same syntax
·
Instance (object) data fields
- Unique to each instance (object) of that class (that is, each object has its own set of instance fields)
- Accessible within
instance methods and constructors using
this.fieldName
- The
this.
qualifier is optional, unless a local variable of the same name exists in that scope - Subject to the
declared access mode, accessible from outside the class from an object
reference using
objectRef.fieldName
static int vacationDays = 10;and we print this in the Employee’s
print()
method:System.out.println("Vacation Days: " + vacationDays);In the EmployeeDemo’s
main()
method, we change vacationDays
to 15:Employee.vacationDays = 15;Now,
e1.print()
and e2.print()
will both show the vacation
days set to 15. This is because both e1
and e2
(and any other
Employee object) share the static vacationDays
integer field.The field
vacationDays
is
part of the Employee class, and this is also stored on the heap, where it is
shared by all objects of that class.Static fields that are not protected (which we will soon learn how to do) are almost like global variables — accessible to anyone.
Note that it is possible to access static fields through instance variables (e.g.,
e1.vacationDays = 15;
will have the same effect), however this is discouraged. You should always
access static fields by ClassName.staticFieldName
,
unless you are within the same class, in which case you can just say staticFieldName
.1.15. Static vs. Instance Methods
·
Static methods can access only
static data and invoke other static methods.
- Often serve as helper procedures/functions
- Use when the desire is to provide a utility or access to class data only
·
Instance methods can access both
instance and static data and methods.
- Implement behavior for individual objects
- Use when access to instance data/methods is required
·
An example of static method use is
Java’s Math class.
- All of its
functionality is provided as static methods implementing mathematical
functions (e.g.,
Math.sin()
). - The Math class is designed so that you don’t (and can’t) create actual Math instances.
- Static methods also are used to implement factory methods for creating objects, a technique discussed later in this class.
class Employee {
String name;
String ssn;
String emailAddress;
int yearOfBirth;
int extraVacationDays = 0;
static int baseVacationDays = 10;
Employee(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
static void setBaseVacationDays(int days) {
baseVacationDays = days < 10? 10 : days;
}
static int getBaseVacationDays() {
return baseVacationDays;
}
void setExtraVacationDays(int days) {
extraVacationDays = days < 0? 0 : days;
}
int getExtraVacationDays() {
return extraVacationDays;
}
void setYearOfBirth(int year) {
yearOfBirth = year;
}
int getVacationDays() {
return baseVacationDays + extraVacationDays;
}
void print() {
System.out.println("Name: " + name);
System.out.println("SSN: " + ssn);
System.out.println("Email Address: " + emailAddress);
System.out.println("Year Of Birth: " + yearOfBirth);
System.out.println("Vacation Days: " + getVacationDays());
}
}To change the company vacation policy, do
Employee.setBaseVacationDays(15);
To give one employee extra vacation, do
e2.setExtraVacationDays(5);
1.16. Method Overloading
- A class can provide multiple definitions of the same method. This is known as overloading.
·
Overloaded methods must
have distinct signatures:
- The parameter type list must be different, either different number or different order.
- Only parameter types determine the signature, not parameter names.
- The return type is not considered part of the signature.
public class Employee {
…
public void print(String header, String footer) {
if (header != null) {
System.out.println(header);
}
System.out.println("Name: " + name);
System.out.println("SSN: " + ssn);
System.out.println("Email Address: " + emailAddress);
System.out.println("Year Of Birth: " + yearOfBirth);
System.out.println("Vacation Days: " + getVacationDays());
if (footer != null) {
System.out.println(footer);
}
}
public void print(String header) {
print(header, null);
}
public void print() {
print(null);
}
}In our
EmployeeDemo.main()
we can then do:e1.print("COOL EMPLOYEE");
e2.print("START OF EMPLOYEE", "END OF EMPLOYEE");
1.17. Variable Argument Length Methods
·
Java 5 introduced syntax supporting
methods with a variable number of argument (also known as varargs).
- The last parameter
in the method declaration must have the format
Type
... varName
(literally three periods following the type). - The arguments corresponding to the parameter are presented as an array of that type.
- You can also invoke the method with an explicit array of that type as the argument.
- If no arguments are provided corresponding to the parameter, the result is an array of length 0.
- There can be at most one varargs parameter in a method declaration.
- The varargs parameter must be the last parameter in the method declaration.
max()
method:public int max(int... values) {
int max = Integer.MIN_VALUE;
for (int i: values) {
if (i > max) max = i;
}
return max;
}You can then invoke the
max()
method with any of the following:max(1, -2, 3, -4);
max(1);
max();
int[] myValues = {5, -7, 26, -13, 42, 361};
max(myValues);
1.18. Constructors
·
Constructors are like special
methods that are called implicitly as soon as an object is instantiated (i.e.
on
new ClassName()
). - Constructors have no
return type (not even
void
). - The constructor name must match the class name.
·
If you don’t define an explicit
constructor, Java assumes a default constructor
- The default constructor accepts no arguments.
- The default constructor automatically invokes its base class constructor with no arguments, as discussed later in this module.
·
You can provide one or more
explicit constructors to:
- Simplify object initialization (one line of code to create and initialize the object)
- Enforce the state of objects (require parameters in the constructor)
- Invoke the base class constructor with arguments, as discussed later in this module.
- Adding any explicit constructor disables the implicit (no argument) constructor.
class Employee {
String name;
String ssn;
…
Employee(String name, String ssn) {
this.name = name; // "this." helps distinguish between
this.ssn = ssn; // instance and parameter variables
}
…
}Then we can modify
EmployeeDemo.main()
to call the specified constructor:public class EmployeeDemo {
public static void main(String[] args) {
Employee e1 = new Employee("John", "555-12-345");
e1.emailAddress = "john@company.com";
Employee e2 = new Employee("Tom", "456-78-901");
e2.setYearOfBirth(1974);
…
}
}
1.19. Constructors (cont.)
- As with methods, constructors can be overloaded.
·
Each constructor must have a
unique signature.
- The parameter type list must be different, either different number or different order.
- Only parameter types determine the signature, not parameter names.
- One constructor can invoke
another by invoking
this(param1, param2, …)
as the first line of its implementation.
Employee
e = new Employee();
because there is no constructor that takes no
parameters.We could add additional constructors to our class Employee:
Employee(String ssn) { // employees must have at least a SSN
this.ssn = ssn;
}
Employee(String name, String ssn) {
this(ssn);
this.name = name;
}
Employee(String name, String ssn, String emailAddress) {
this(name, ssn);
this.emailAddress = emailAddress;
}
Employee(String ssn, int yearOfBirth) {
this(ssn);
this.yearOfBirth = yearOfBirth;
}Now we can construct Employee objects in different ways:
Employee e1 = new Employee("John", "555-12-345", "john@company.com");
Employee e2 = new Employee("456-78-901", 1974);
e2.name = "Tom";
1.20. Constants
·
“Constant” fields are defined
using the
final
keyword, indicating their values can be assigned only once. - Final instance fields must be initialized by the end of object construction.
- Final static fields must be initialized by the end of class initialization.
- Final local variables must be initialized only once before they are used.
- Final method parameters are initialized on the method call.
Important
|
|
Declaring a reference variable as final
means only that once initialized to refer to an object, it can’t be changed
to refer to another object. It does not imply that the state
of the object referenced cannot be changed. |
·
Final static field can be
initialized through direct assignment or by using a static initializer.
o
A static initializer consists of the
keyword
static
followed by a block, for example: o private static int[] values = new int[10];
o static {
o for (int i = 0; i < values.length; i++) {
o values[i] = (int) (100.0 * Math.random());
o }
}If we declare
ssn
as
final, we then must either assign it right away or initialize it in all
constructors. This can also be done indirectly via constructor-to-constructor
calls. Once initialized, final instance fields (e.g. ssn
) can no longer be changed.class Employee {
final String ssn;
…
Employee(String ssn) {
this.ssn = ssn;
}
…
}Local variables can also be set as final, to indicate that they should not be changed once set:
public class EmployeeDemo {
public static void main(String[] args) {
final Employee e1 = new Employee(…);
final Employee e2 = new Employee("456-78-901", 1974);
final Employee e3;
e3 = e2;
…
}
}
1.21. Encapsulation
·
The principle of encapsulation
is that all of an object’s data is contained and hidden in the object and
access to it restricted to methods of that class.
- Code outside of the object cannot (or at least should not) directly access object fields.
- All access to an object’s fields takes place through its methods.
·
Encapsulation allows you to:
- Change the way in which the data is actually stored without affecting the code that interacts with the object
- Validate requested changes to data
- Ensure the consistency of data — for example preventing related fields from being changed independently, which could leave the object in an inconsistent or invalid state
- Protect the data from unauthorized access
- Perform actions such as notifications in response to data access and modification
1.22. Access Modifiers: Enforcing Encapsulation
- Access modifiers are Java keywords you include in a declaration to control access.
·
You can apply access modifiers to:
- Instance and static fields
- Instance and static methods
- Constructors
- Classes
- Interfaces (discussed later in this module)
·
Two access modifiers provided by
Java are:
private
visible only within the same class
public
visible everywhere
Note
|
|
There are two additional access levels that we’ll discuss in the next module. |
1.23. Accessors (Getters) and Mutators (Setters)
- A common model for designing data access is the use of accessor and mutator methods.
·
A mutator — also known as a setter — changes
some property of an object.
- By convention,
mutators are usually named
setPropertyName
.
·
An accessor — also known as a getter — returns
some property of an object.
- By convention,
accessors are usually named
getPropertyName
. - One exception is
that accessors that return a
boolean
value are commonly namedisPropertyName
.
·
Accessors and mutators often are
declared
public
and
used to access the property outside the object. - Using accessors and mutators from code within the object also can be beneficial for side-effects such as validation, notification, etc.
- You can omit
implementing a mutator — or mark it
private
— to implement immutable (unchangeable) object properties.
public class Employee {
private String name;
private final String ssn;
…
public void setName(String name) {
if (name != null && name.length() > 0) {
this.name = name;
}
}
public String getName() {
return this.name;
}
public String getSsn() {
return this.ssn;
}
…
}Now, to set the name on an employee in
EmployeeDemo.main()
(i.e., from the outside), you must call:e2.setName("Tom");as opposed to:
e2.name = "Tom"; // won't compile, name is hidden externally
1.24. Inheritance
·
Inheritance allows you to
define a class based on the definition of another class.
- The class it inherits from is called a base class or a parent class.
- The derived class is called a subclass or child class.
·
Subject to any access modifiers,
which we’ll discuss later, the subclass gets access to the fields and methods
defined by the base class.
- The subclass can add its own set of fields and methods to the set it inherits from its parent.
·
Inheritance simplifies modeling of
real-world hierarchies through generalization of common features.
- Common features and functionality is implemented in the base classes, facilitating code reuse.
- Subclasses can extended, specialize, and override base class functionality.
For example, a manager is also an employee but it has a responsibility over a department, whereas a generic employee does not.
1.25. Inheritance, Composition, and Aggregation
- Complex class structures can be built through inheritance, composition, and aggregation.
·
Inheritance establishes an “is-a”
relationship between classes.
- The subclass has the same features and functionality as its base class, with some extensions.
- A Car is-a Vehicle. An Apple is-a Food.
·
Composition and aggregation are
the construction of complex classes that incorporate other objects.
- They establish a “has-a” relationship between classes.
- A Car has-a Engine. A Customer has-a CreditCard.
·
In composition, the
component object exists solely for the use of its composite object.
- If the composite object is destroyed, the component object is destroyed as well.
- For example, an Employee has-a name, implemented as a String object. There is no need to retain the String object once the Employee object has been destroyed.
·
In aggregation, the
component object can (but isn’t required to) have an existence independent of
its use in the aggregate object.
- Depending on the structure of the aggregate, destroying the aggregate may or may not destroy the component.
- For example, let’s say a Customer has-a BankAccount object. However, the BankAccount might represent a joint account owned by multiple Customers. In that case, it might not be appropriate to delete the BankAccount object just because one Customer object is deleted from the system.
Additionally when designing a class structure with composition or aggregation, we should keep in mind the principle of encapsulation when deciding where to implement functionality. Consider implementing a method on Customer that would return the customer’s age. Obviously, this depends on the birth date of the customer stored in the Date object. But should we have code in Customer that calculates the elapsed time since the birth date? It would require the Customer class to know some very Date-specific manipulation. In this case, the code would be tightly coupled –- a change to Date is more likely to require a change to Customer as well. It seems more appropriate for Date objects to know how to calculate the elapsed time between two instances. The Customer object could then delegate the age request to the Date object in an appropriate manner. This makes the classes loosely coupled –- so that a change to Date is unlikely to require a change to Customer.
1.26. Inheritance in Java
·
You define a subclass in Java
using the
extends
keyword followed by the base class name. For example: class Car extends Vehicle { // ... }
- In Java, a class can extend at most one base class. That is, multiple inheritance is not supported.
- If you don’t explicitly extend a base class, the class inherits from Java’s Object class, discussed later in this module.
·
Java supports multiple levels
of inheritance.
- For example, Child can extend Parent, which in turn extends GrandParent, and so on.
class A {
String a = null;
void doA() {
System.out.println("A says " + a);
}
}
class B extends A {
String b = null;
void doB() {
System.out.println("B says " + b);
}
}
class C extends B {
String c = null;
void doA() {
System.out.println("Who cares what A says");
}
void doB() {
System.out.println("Who cares what B says");
}
void doC() {
System.out.println("C says " + a + " " + b + " " + c);
}
}
public class ABCDemo {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
a.a = "AAA";
b.a = "B's A";
b.b = "BBB";
c.a = "Who cares";
c.b = "Whatever";
c.c = "CCC";
a.doA();
b.doB();
c.doA();
c.doB();
c.doC();
}
}The output of running ABCDemo is:
A says AAA
B says BBB
Who cares what A says
Who cares what B says
C says Who cares Whatever CCC
1.27. Invoking Base Class Constructors
·
By default, Java automatically
invokes the base class’s constructor with no arguments before
invoking the subclass’s constructor.
- This might not be desirable, especially if the base class doesn’t have a no-argument constructor. (You get a compilation error in that case.)
·
You can explicitly invoke a base
class constructor with any arguments you want with the syntax
super(arg1, arg2, ...)
. For example: · class Subclass extends ParentClass {
· public Subclass(String name, int age) {
· super(name);
· // Additional Subclass initialization...
· }
}
- The call to
super()
must be the first statement in a subclass constructor.
Note
|
|
A single constructor cannot invoke both super()
and this() . However, a
constructor can use this()
to invoke an overloaded constructor, which in turn invokes super() . |
1.28. Overriding vs. Overloading
·
The subclass can override
its parent class definition of fields and methods, replacing them with its own
definitions and implementations.
- To successfully override the base class method definition, the subclass method must have the same signature.
- If the subclass defines a method with the same name as one in the base class but a different signature, the method is overloaded not overridden.
·
A subclass can explicitly invoked
an ancestor class’s implementation of a method by prefixing
super.
to the method call. For example: · class Subclass extends ParentClass {
· public String getDescription() {
· String parentDesc = super.getDescription();
· return "My description\n" + parentDesc;
· }
}Consider defining a Manager class as a subclass of Employee:
public class Manager extends Employee {
private String responsibility;
public Manager(String name, String ssn, String responsibility) {
super(name, ssn);
this.responsibility = responsibility;
}
public void setResponsibility(String responsibility) {
this.responsibility = responsibility;
}
public String getResponsibility() {
return this.responsibility;
}
public void print(String header, String footer) {
super.print(header, null);
System.out.println("Responsibility: " + responsibility);
if (footer != null) {
System.out.println(footer);
}
}
}Now with code like this:
public class EmployeeDemo {
public static void main(String[] args) {
…
Manager m1 = new Manager("Bob", "345-11-987", "Development");
Employee.setBaseVacationDays(15);
m1.setExtraVacationDays(10);
…
m1.print("BIG BOSS");
}
}The output is:
BIG BOSS
Name: Bob
SSN: 345-11-987
Email Address: null
Year Of Birth: 0
Vacation Days: 25
Responsibility: DevelopmentNote that the Manager class must invoke one of super’s constructors in order to be a valid Employee. Also observe that we can invoke a method like
setExtraVacationDays()
that is defined
in Employee on our Manager instance m1
.1.29. Polymorphism
- Polymorphism is the ability for an object of one type to be treated as though it were another type.
·
In Java, inheritance provides us
one kind of polymorphism.
- An object of a subclass can be treated as though it were an object of its parent class, or any of its ancestor classes. This is also known as upcasting.
o
For example, if Manager is a
subclass of Employee:
Employee e = new Manager(...);
o
Or if a method accepts a reference
to an Employee object:
o public void giveRaise(Employee e) { // ... }
o // ...
o Manager m = new Manager(...);
giveRaise(m);Why is polymorphism useful? It allows us to create more generalized programs that can be extended more easily.
Consider an online shopping application. You might need to accept multiple payment methods, such as credit cards, debit card, direct bank debit through ACH, etc. Each payment method might be implemented as a separate class because of differences in the way you need to process credits, debits, etc.
If you were to handle each object type explicitly, the application would be very complex to write. It would require
if-else
statements everywhere to test for the different types of payment methods, and
overloaded methods to pass different payment type objects as arguments.On the other hand, if you define a base class like PaymentMethod and then derive subclasses for each type, then it doesn’t matter if you’ve instantiated a CreditCard object or a DebitCard object, you can treat it as a PaymentMethod object.
1.30. More on Upcasting
·
Once you have upcast an object
reference, you can access only the fields and methods declared by the base
class.
o
For example, if Manager is a
subclass of Employee:
Employee e = new Manager(...);
- Now using
e
you can access only the fields and methods declared by the Employee class.
·
However, if you invoke a method on
e
that is defined in
Employee but overridden in Manager, the Manager version is executed.
o
For example:
o public class A {
o public void print() {
o System.out.println("Hello from class A");
o }
o }
o public class B extends A {
o public void print() {
o System.out.println("Hello from class B");
o }
o }
o // ...
o A obj = new B();
obj.print();
In the case, the output is
"Hello from class B".
From within a subclass, you can explicitly invoke a base class’s version of
a method by using the super.
prefix on the method call.1.31. Downcasting
·
An upcast reference can be downcast
to a subclass through explicit casting. For example:
· Employee e = new Manager(...);
· // ...
Manager m = (Manager) e;
- The object
referenced must actually be a member of the downcast type, or else a
ClassCastException
run-time exception occurs.
·
You can test if an object is a
member of a specific type using the
instanceof
operator, for example: if (obj instanceof Manager) { // We've got a Manager object }
public class EmployeeDemo {
public static void main(String[] args) {
final Employee e1 = new Employee("John", "555-12-345", "john@company.com");
final Employee e2 = new Employee("456-78-901", 1974);
e2.setName("Tom");
Employee em = new Manager("Bob", "345-11-987", "Development");
Employee.setBaseVacationDays(15);
e2.setExtraVacationDays(5);
em.setExtraVacationDays(10);
if (em instanceof Manager) {
Manager m = (Manager) em;
m.setResponsibility("Operations");
}
e1.print("COOL EMPLOYEE");
e2.print("START OF EMPLOYEE", "END OF EMPLOYEE");
em.print("BIG BOSS");
}
}This would print:
BIG BOSS
Name: Bob
SSN: 345-11-987
Email Address: null
Year Of Birth: 0
Vacation Days: 25
Responsibility: Operations
1.32. Abstract Classes and Methods
·
An abstract class is a
class designed solely for subclassing.
- You can’t create actual instances of the abstract class. You get a compilation error if you attempt to do so.
- You design abstract classes to implement common sets of behavior, which are then shared by the concrete (instantiable) classes you derive from them.
o
You declare a class as abstract
with the
abstract
modifier: public abstract class PaymentMethod { // ... }
·
An abstract method is a
method with no body.
- It declares a method signature and return type that a concrete subclass must implement.
o
You declare a method as abstract
with the
abstract
modifier and a semicolon terminator: public abstract boolean approveCharge(float amount);
- If a class has any abstract methods declared, the class itself must also be declared as abstract.
public abstract class Triangle implements Shape {
public abstract double getA();
public abstract double getB();
public abstract double getC();
public double getPerimeter() {
return getA() + getB() + getC();
}
// getArea() is also abstract since it is not implemented
}Now we can create concrete triangle classes based on their geometric properties. For example:
public class RightAngledTriangle extends Triangle {
private double a, b, c;
public RightAngledTriangle(double a, double b) {
this.a = a;
this.b = b;
this.c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
}
public double getA() { return a; }
public double getB() { return b; }
public double getC() { return c; }
public double getArea() { return (a * b) / 2; }
}
1.33. Interfaces
·
An interface defines a
set of methods, without actually defining their implementation.
- A class can then implement the interface, providing actual definitions for the interface methods.
·
In essence, an interface serves as
a “contract” defining a set of capabilities through method signatures and
return types.
- By implementing the interface, a class “advertises” that it provides the functionality required by the interface, and agrees to follow that contract for interaction.
For example, one must have a driver’s license to drive a car, regardless for what kind of a car that is (i.e., make, model, year, engine size, color, style, features etc.).
However, the car must be able to perform certain operations:
- Go forward
- Slowdown/stop (break light)
- Go in reverse
- Turn left (signal light)
- Turn right (signal light)
- Etc.
1.34. Defining a Java Interface
·
Use the
interface
keyword to define an interface
in Java. - The naming convention for Java interfaces is the same as for classes: CamelCase with an initial capital letter.
o
The interface definition consists
of public abstract method declarations. For example:
o public interface Shape {
o double getArea();
o double getPerimeter();
}
·
All methods declared by an
interface are implicitly
public
abstract
methods. - You can omit either
or both of the
public
andstatic
keywords. - You must include the semicolon terminator after the method declaration.
public static final
fields for use by
subclasses that implement the interface.- Any such field must be declared and initialized by the interface.
- You can omit any or all of
the
public
,static
, andfinal
keywords in the declaration.
1.35. Implementing a Java Interface
·
You define a class that implements
a Java interface using the
implements
keyword followed by the interface name. For example: class Circle implements Shape { // ... }
·
A concrete class must then provide
implementations for all methods declared by the interface.
- Omitting any method declared by the interface, or not following the same method signatures and return types, results in a compilation error.
·
An abstract class can omit
implementing some or all of the methods required by an interface.
- In that case concrete subclasses of that base class must implement the methods.
·
A Java class can implement as many
interfaces as needed.
o
Simply provide a comma-separated
list of interface names following the
implements
keyword. For example: class ColorCircle implements Shape, Color { // ... }
·
A Java class can extend a base
class and implement one or more interfaces.
o
In the declaration, provide the
extends
keyword and the base class name,
followed by the implements
keywords and the interface name(s). For example: class Car extends Vehicle implements Possession { // ... }
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public double getArea() {
return Math.PI * Math.pow(this.radius, 2);
}
public double getPerimeter() {
return Math.PI * this.radius * 2;
}
}
/**
* Rectange shape with a width and a height.
* @author sasa
* @version 1.0
*/
public class Rectangle implements Shape {
private double width;
private double height;
/**
* Constructor.
* @param width the width of this rectangle.
* @param height the height of this rectangle.
*/
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
/**
* Gets the width.
* @return the width.
*/
public double getWidth() {
return width;
}
/**
* Gets the height.
* @return the height.
*/
public double getHeight() {
return height;
}
public double getArea() {
return this.width * this.height;
}
public double getPerimeter() {
return 2 * (this.width + this.height);
}
}
public class Square extends Rectangle {
public Square(double side) {
super(side, side);
}
}
1.36. Polymorphism through Interfaces
·
Interfaces provide another kind of
polymorphism in Java.
- An object implementing an interface can be assigned to a reference variable typed to the interface.
o
For example, if Circle implements
the Shape interface:
Shape s = new Circle(2);
o
Or you could define a method with
an interface type for a parameter:
o public class ShapePrinter {
o public void print(Shape shape) {
o System.out.println("AREA: " + shape.getArea());
o System.out.println("PERIMETER: " + shape.getPerimeter());
o }
}
- When an object reference is upcast to an interface, you can invoke only those methods declared by the interface.
public class ShapeDemo {
public static void main(String[] args) {
Circle c = new Circle(5.0);
Rectangle r = new Rectangle(3, 4);
Square s = new Square(6);
ShapePrinter printer = new ShapePrinter();
printer.print(c);
printer.print(r);
printer.print(s);
}
}This would print:
AREA: 78.53981633974483
CIRCUMFERENCE: 31.41592653589793
AREA: 12.0
CIRCUMFERENCE: 14.0
AREA: 36.0
CIRCUMFERENCE: 24.0
1.37. Object
:
Java’s Ultimate Superclass
·
Every class in Java ultimately has
the Object class as an ancestor.
- The Object class implements basic functionality required by all classes.
- Often you’ll want to override some of these methods to better support your custom classes.
·
Behaviors implemented by Object
include:
- Equality testing and hash code calculation
- String conversion
- Cloning
- Class introspection
- Thread synchronization
- Finalization (deconstruction)
1.38. Overriding Object.toString()
- The
Object.toString()
method returns a String representation of the object.
·
The
toString()
method is invoked automatically:
- When you pass an
object as an argument to
System.out.println(obj)
and some other Java utility methods - When you provide an object as a String concatenation operand
·
The default implementation returns
the object’s class name followed by its hash code.
- You’ll usually want to override this method to return a more meaningful value.
public class Circle implements Shape {
// ...
public String toString() {
return "Circle with radius of " + this.radius;
}
}
1.39. Object Equality
- When applied to object
references, the equality operator (
==
) returnstrue
only if the references are to the same object. For example:
Circle a = new Circle(2);
Circle b = new Circle(2);
Circle c = a;
if { a == b } { // false }
if { a == c } { // true }One exception to this equality principle is that Java supports string interning to save memory (and speed up testing for equality). When the
intern()
method is invoked on a String,
a lookup is performed on a table of interned Strings. If a String object with
the same content is already in the table, a reference to the String in the
table is returned. Otherwise, the String is added to the table and a reference
to it is returned. The result is that after interning, all Strings with the
same content will point to the same object.String interning is performed on String literals automatically during compilation. At run-time you can invoke
intern()
on any String object that you want to add to the intern pool.1.40. Object Equivalence
·
The Object class provides an
equals()
method that you can override to
determine if two objects are equivalent. - The default
implementation by Object is a simple
==
test. - You should include
all
`significant'' fields for your class when overriding `equals()
.
o
We could define a simple version
of
Circle.equals
as
follows: o public booleans equals(Object obj) {
o if (obj == this) {
o // We're being compared to ourself
o return true;
o }
o else if (obj == null || obj.getClass() != this.getClass()) {
o // We can only compare to another Circle object
o return false;
o }
o else {
o // Compare the only significant field
o Circle c = (Circle) obj;
o return c.radius == this.radius;
o }
}A somewhat more complex example of an equality test is shown in this class:
public class Person {
private String name;
private final int yearOfBirth;
private String emailAddress;
public Person(String name, int yearOfBirth) {
this.name = name;
this.yearOfBirth = yearOfBirth;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getYearOfBirth() {
return this.yearOfBirth;
}
public String getEmailAddress() {
return this.emailAddress;
}
public void setEmailAddress() {
this.emailAddress = emailAddress;
}
public boolean equals(Object o) {
if (o == this) {
// we are being compared to ourself
return true;
} else if (o == null || o.getClass() != this.getClass()) {
// can only compare to another person
return false;
} else {
Person p = (Person) o; // cast to our type
// compare significant fields
return p.name.equals(this.name) &&
p.yearOfBirth == this.yearOfBirth;
}
}
public int hashCode() {
// compute based on significant fields
return 3 * this.name.hashCode() + 5 * this.yearOfBirth;
}
}
1.41. Object Equivalence (cont.)
·
Overriding
equals()
requires care. Your equality
test must exhibit the following properties: - Symmetry: For two
references,
a
andb
,a.equals(b)
if and only ifb.equals(a)
- Reflexivity: For all
non-
null
references,a.equals(a)
- Transitivity: If
a.equals(b)
andb.equals(c)
, thena.equals(c)
- Consistency with
hashCode()
: Two equal objects must have the samehashCode()
value
Note
|
||
The hashcode() method
is used by many Java Collections Framework classes like HashMap to identify
objects in the collection. If you override the equals() method in your class, you must
override hashcode() as
well. |
||
Tip
|
||
The Apache Commons Lang library (http://commons.apache.org/lang) includes two helper
classes, EqualsBuilder and HashCodeBuilder, that can greatly simplify creating proper equals() and hashcode() implementations. |
||
- This Stack Overflow article: http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java
- The article "Java theory and practice: Hashing it out" on IBM developerWorks: http://www.ibm.com/developerworks/java/library/j-jtp05273.html
- The book Effective Java, 2nd ed., by Joshua Bloch
This comment has been removed by the author.
ReplyDelete/*Demonstrate Binary Sort example
ReplyDelete* ID: 201210269
*
*
*/
package binary.sort1;
/**
*
* @author Administrator
*/
public class BinarySort1 {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
int[] lisa={10,15,20,25,30,35,40,45,50,55};
int i,m,c=0,L = 0,u,mid;
m=40;
for(i=0;i<10;i++)
L=0;
u=10-1;
while(L<=u)
{
mid=(L+u)/2;
if(m==lisa[mid])
{
c=1;
break;
}
else if(m<lisa[mid])
{
u=mid-1;
}
else
L=mid+1;
}
if(c==0)
System.out.print("The number is not found.");
else
System.out.print("The number is found.");
}
}