Classes, Objects and Types

From Wikibooks, open books for an open world
Jump to: navigation, search

API/java.lang.String Java Programming
Classes, Objects and Types
Syntax
Navigate Language Fundamentals topic: v  d  e )


An object is composed of fields and methods. The fields, also called data members, characteristics, attributes, or properties, describe the state of the object. The methods generally describe the actions associated with a particular object. Think of an object as a noun, its fields as adjectives describing that noun, and its methods as the verbs that can be performed by or on that noun.

For example, a sports car is an object. Some of its fields might be its height, weight, acceleration, and speed. An object's fields just hold data about that object. Some of the methods of the sports car could be "drive", "park", "race", etc. The methods really don't mean much unless associated with the sports car, and the same goes for the fields.

The blueprint that lets us build our sports car object is called a class. A class doesn't tell us how fast our sports car goes, or what color it is, but it does tell us that our sports car will have a field representing speed and color, and that they will be say, a number and a word (or hex color code), respectively. The class also lays out the methods for us, telling the car how to park and drive, but these methods can't take any action with just the blueprint — they need an object to have an effect.

In Java, a class is located in a file similar to its own name. If you want to have a class called SportsCar, its source file needs to be SportsCar.java. The class is created by placing the following in the source file:

Computer code Code listing 3.13: SportsCar.java
  1. public class SportsCar {
    
  2.    /* Insert your code here */
    
  3. }
    

The class doesn't do anything yet, as you will need to add methods and field variables first.

The objects are different from the primitive types because:

  1. The primitive types are not instantiated.
  2. In the memory, only their value is stored, directly. No reference to an instance is stored.
  3. In the memory, the allocated space is fixed, whatever their value. The allocated space of an object vary, for instance either the object is intantiated or not.
  4. The primitive types don't have methods callable on them.
  5. A primitive type can't be inherited.

Instantiation and constructors [edit]

In order to get from class to object, we "build" our object by instantiation. Instantiation simply means to create an instance of a class. Instance and object are very similar terms and are sometimes interchangeable, but remember that an instance refers to a specific object, which was created from a class.

This instantiation is brought about by one of the class's methods, called a constructor. As its name implies, a constructor builds the object based on the blueprint. Behind the scenes, this means that computer memory is being allocated for the instance, and values are being assigned to the data members.

In general there are four constructor types: default, non-default, copy, and cloning.

A default constructor will build the most basic instance. Generally, this means assigning all the fields values like null, zero, or an empty string. Nothing would stop you, however, from your default sports car color from being red, but this is generally bad programming style. Another programmer would be confused if your basic car came out red instead of say, colorless.

Example Code section 3.79: A default constructor.
  1. SportsCar car = new SportsCar();
    

A non-default constructor is designed to create an object instance with prescribed values for most, if not all, of the object's fields. The car is red, goes from 0-60 in 12 seconds, tops out at 190mph, etc.

Example Code section 3.80: A non-default constructor.
  1. SportsCar car = new SportsCar("red", 12, 190);
    

A copy constructor is not included in the Java language, however one can easily create a constructor that do the same as a copy constructor. It's important to understand what it is. As the name implies, a copy constructor creates a new instance to be a duplicate of an already existing one. In Java, this can be also accomplished by creating the instance with the default constructor, and then using the assignment operator to equivocate them. This is not possible in all languages though, so just keep the terminology under your belt.

Java has the concepts of cloning object, and the end results are similar to copy constructor. Cloning an object is faster than creation with the new keyword, because all the object memory is copied at once to destination cloned object. This is possible by implementing the Cloneable interface, which allows the method Object.clone() to perform a field-by-field copy.

Example Code section 3.81: Cloning object.
  1. SportsCar car = oldCar.clone();
    

Type [edit]

When an object is created, a reference to the object is also created. The object can not be accessed directly in Java, only through this object reference. This object reference has a type assigned to it. We need this type when passing the object reference to a method as a parameter. Java does strong type checking.

Type is basically a list of features/operations, that can be performed through that object reference. The object reference type is basically a contract that guarantees that those operations will be there at run time.

When a car is created, it comes with a list of features/operations listed in the user manual that guarantees that those will be there when the car is used.

When you create an object from a class by default its type is the same as its class. It means that all the features/operations the class defined are there and available, and can be used. See below:

Example Code section 3.82: Default type.
  1. (new ClassName()).operations();
    

You can assign this to a variable having the same type as the class:

Example Code section 3.83: A variable having the same type as the class.
  1. ClassName objRefVariable = new ClassName();
    
  2. objRefVariable.operations();
    

You can assign the created object reference to the class super class, or to an interface the class implements:

Example Code section 3.84: Using the super class.
  1. SuperClass objectRef = new ClassName(); // features/operations list are defined by the SuperClass class
    
  2. ...
    
  3. Interface inter = new ClassName(); // features/operations list are defined by the interface
    

In the car analogy, the created car may have different Type of drivers. We create separate user manuals for them, Average user manual, Power user manual, Child user manual, or Handicapped user manual. Each type of user manual describes only those features/operations appropriate for the type of driver. The Power driver may have additional gears to switch to higher speeds, that are not available to other type of users...

When the car key is passed from an adult to a child we replacing the user manuals, that is called Type Casting.

In Java, casts can occur in three ways:

  • up casting going up in the inheritance tree, until we reach the Object
  • up casting to an interface the class implements
  • down casting until we reach the class the object was created from

Autoboxing/unboxing [edit]

Autoboxing and unboxing, language features since Java 1.5, make the programmer's life much easier when it comes to working with the primitive wrapper types. Consider this code fragment:

Example Code section 3.85: Traditional object creation.
  1. int age = 23;
    
  2. Integer ageObject = new Integer(age);
    

Primitive wrapper objects were Java's way of allowing one to treat primitive data types as though they were objects. Consequently, one was expected to wrap one's primitive data type with the corresponding primitive wrapper object, as shown above.

Since Java 1.5, one may write as below and the compiler will automatically create the wrap object. The extra step of wrapping the primitive is no longer required. It has been automatically boxed up on your behalf:

Example Code section 3.86: Autoboxing.
  1. int age = 23;
    
  2. Integer ageObject = age;
    
Note Keep in mind that the compiler still creates the missing wrapper code, so one doesn't really gain anything performance-wise. Consider this feature a programmer convenience, not a performance booster.

Each primitive type has a class wrapper:

Primitive type Class wrapper
byte java.lang.Byte
char java.lang.Character
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
boolean java.lang.Boolean
void java.lang.Void

Unboxing uses the same process in reverse. Study the following code for a moment. The if statement requires a boolean primitive value, yet it was given a Boolean wrapper object. No problem! Java 1.5 will automatically unbox this.

Example Code section 3.87: Unboxing.
  1. Boolean canMove = new Boolean(true);
    
  2.  
    
  3. if (canMove) {
    
  4.   System.out.println("This code is legal in Java 1.5"); 
    
  5. }
    
Test your knowledge

Question 3.11: Consider the following code:

Example Question 3.11: Autoboxing/unboxing.
  1. Integer a = 10;
    
  2. Integer b = a + 2;
    
  3. System.out.println(b);
    

How many autoboxings and unboxings are there in this code?

Answer
Example Answer 3.11: Autoboxing/unboxing.
  1. Integer a = 10;
    
  2. Integer b = a + 2;
    
  3. System.out.println(b);
    

3

  • 1 autoboxing at line 1 to assign.
  • 1 unboxing at line 2 to do the addition.
  • 1 autoboxing at line 2 to assign.
  • No autoboxing nor unboxing at line 3 as println() supports the Integer class as parameter.




Objects [edit]

Classes [edit]

Java has nested classes that are declared within the body of another class or interface. A class that is not a nested class is called a top level class. An inner class is a non-static nested class.

Classes can be declared with the following modifiers:

  • abstract – cannot be instantiated. Only interfaces and abstract classes may contain abstract methods. A concrete (non-abstract) subclass that extends an abstract class must override any inherited abstract methods with non-abstract methods. Cannot be final.
  • final – cannot be subclassed. All methods in a final class are implicitly final. Cannot be abstract.
  • strictfp – all floating-point operations within the class and any enclosed nested classes use strict floating-point semantics. Strict floating-point semantics guarantee that floating-point operations produce the same results on all platforms.

Note that Java classes do not need to be terminated by a semicolon (";"), which is required in C++ syntax.

Inheritance [edit]

// ChildClass inherits from ParentClass
class ChildClass extends ParentClass { ... }  
  • The default parent of a class is the Object class.
  • A class can only extend a single parent class (no multiple inheritance of implementation).

Scope [edit]

  • this – Reference to the current subclass (assumed by default) (i.e. this.someMethod()).
  • super – Reference to the parent class (i.e. super.someMethod()). Can be used in a subclass to access inherited methods that the subclass has overridden or inherited fields that the subclass has hidden.

Interfaces [edit]

An interface is an abstract class with no implementation details. Its purpose is to define how a set of classes will be used. Classes that implement a common interface can be used interchangeably within the context of the interface type. Interfaces also help to enforce the concept of abstraction—hiding the details of how a class is implemented.

An interface can only contain abstract methods and static final fields. Interface methods are public and abstract by default (unimplemented), and interface fields are public, static and final by default.

Java does not support full orthogonal multiple inheritance. Multiple inheritance in C++ has complicated rules to disambiguate fields and methods inherited from multiple superclasses and types inherited multiple times. By separating interface from implementation, interfaces offer much of the benefit of multiple inheritance with less complexity and ambiguity. The price of no multiple inheritance is some code redundancy; since interfaces only define the signature of a class but cannot contain any implementation, every class inheriting an interface must provide the implementation of the defined methods, unlike in pure multiple inheritance, where the implementation is also inherited.

Java interfaces behave much like the concept of the Objective-C protocol.

Implementing interfaces [edit]

A class can implement one or more interfaces using the implements keyword, in addition to extending another class.

 interface MyInterface {
     void foo();
 }
 interface Interface2 {
     void bar();
 }
 class MyClass implements MyInterface {
     void foo() {...}
     ...
 }
 class ChildClass extends ParentClass implements MyInterface, Interface2 {
     void foo() {...}
     void bar();
     ...
 }

In the following example,

 public interface Deleteable {
     void delete();
 }

any non-abstract class that implements the Deleteable interface must define a non-abstract method named delete that has no parameters and a void return type. The implementation and function of the method are determined by each class. There are many uses for this concept, for example:

 public class Fred implements Deleteable {
     // This method satisfies the Deleteable interface
     public void delete() {
         // Code implementation goes here
     }
     public void someOtherMethod() {
     }
 }
 public void deleteAll(Deleteable[] list) {
     for (int i = 0; i < list.length; i++) {
         list[i].delete();
     }
 }

Because any objects in the above array are guaranteed to have the delete() method, the deleteAll() method needn't differentiate between the Fred objects or any other Deleteable objects.

Extending interfaces [edit]

An interface can extend one or more interfaces using the extends keyword.

 interface ChildInterface extends ParentInterface, AnotherInterface {
     ...
 }

A class that implements the resulting interface must define the combined set of methods.

 public interface MyInterface {
     foo();
 }
 public interface Interface2 extends MyInterface {
     bar();
 }
 public class MyClass implements Interface2 {
     void foo() {...}
     void bar() {...}
     ...
 }

Access modifiers [edit]

Access modifiers determine which code may access classes and class members.

Top level class access [edit]

By default, Java classes are accessible only within their own Java package. This enables a package of classes to provide an API which performs functions behind the scenes. Hidden classes support the work of publicly accessible classes.

  • default – accessible only within the package in which it's defined.
  • public – extends access to classes outside the package

Class member access [edit]

Class members are fields, methods, constructors and nested classes declared within the body of a class. In order of increasing scope of access, the access modifiers for class members are:

  1. private – accessible only within the class
  2. package-private (no modifier) – accessible to other classes in the same package
  3. protected – extends access to subclasses outside the package
  4. public – accessible by any class.

When overriding a method, the method access modifier can't be made more restrictive—to do so would break the interface contract of the parent class. Thus when overridden, a public method must be declared public and a protected method cannot be given default access. However, it is permissible to override a method to make it more accessible. Thus when overriding, a default (package) access method can be declared as protected or public and a protected method can be declared as public.

Fields [edit]

In addition to the access modifiers, data fields may be declared with the following modifiers:

  • final – the value cannot be changed. Must be initialized exactly once. A final field declared without an initializer is a blank final field—a static blank final field must be definitively initialized by a static initializer; a non-static blank final field must be initialized during the execution of each and every constructor. Cannot be volatile.
  • static – belongs to the class, rather than to an instance of the class.
  • transient – not a part of the persistent state of an object. The value should not be saved and later restored.
  • volatile – informs the compiler that it may be accessed by separate threads asynchronously. Cannot be final.

Constants [edit]

Fields that are declared as both static and final are effectively constants; static means there is one occurrence of the field associated with the class, and final means that the field is assigned a value exactly once.

Initializers [edit]

Initializers are blocks of code that are executed at the same time as initializers for fields.

Static initializers [edit]

Static initializers are blocks of code that are executed at the same time as initializers for static fields. Static field initializers and static initializers are executed in the order declared. The static initialization is executed after the class is loaded.

 static int count = 20;
 static int[] squares;
 static {  // a static initializer
     squares = new int[count];
     for (int i = 0; i < count; i++)
         squares[i] = i * i;
 }
 static int x = squares[5];  // x is assigned the value 25

Instance initializers [edit]

Instance initializers are blocks of code that are executed at the same time as initializers for instance (non-static) fields. Instance field initializers and instance initializers are executed in the order declared.

Both instance initializers and instance field initializers are executed during the invocation of a constructor. The initializers are executed immediately after the superclass constructor and before the body of the constructor.

Methods [edit]

In addition to the access modifiers, methods may be declared with the following modifiers:

  • abstract – the method is undefined in the class, and must be defined by any concrete (non-abstract) subclass. Cannot be static, final or native.
  • final – the method cannot be redefined in a subclass. For instance (non-static) methods, this allows the compiler to expand the method (similar to an inline function) if the method is small enough. Cannot be abstract.
  • native – the method links to native machine-dependent code. Declared without a body. Cannot be abstract.
  • static – belongs to the class, rather than to an instance of the class. Cannot be abstract.
  • strictfp – all floating-point operations in the method and enclosed inner classes use strict floating-point semantics. Strict floating-point semantics guarantee that floating-point operations produce the same results on all platforms.
  • synchronized – causes the current thread to acquire the lock for the associated object before executing the body of the method. If the lock is currently held by another thread, the current thread will block until the lock is released and the thread is able to obtain the lock. The associated object is the Template:Javadoc:SE object for static methods and the object instance for non-static methods. While it is allowed to declare an abstract method as synchronized, it is meaningless to do so since synchronization is an aspect of the implementation, not the declaration, and abstract methods do not have an implementation.

Note that a private method can't be abstract and is implicitly final.

Varargs [edit]

Java SE 5.0 added syntactic support for methods with a variable number of arguments (varargs) [1], which simplifies the typesafe usage of methods requiring a variable number of arguments. The last parameter can be followed with ..., and Java will box all the arguments into an array:

public void drawPolygon (Template:Javadoc:SE... points) {…}

When calling the method, a programmer can simply separate the points by commas, without having to explicitly create an array of Point objects. Within the method, the points can be referenced as points[0], points[1], etc. If no points are passed, the array has a length of zero. To require the programmer to use a minimum number of parameters, those parameters can be specified before the variable argument:

// A polygon needs at least 3 points.
public void drawPolygon (Point p1, Point p2, Point p3, Point... otherPoints) {…}

Constructors [edit]

A constructor is called to initialize an object immediately after the object has been allocated. Typically, a constructor is invoked using the new keyword, although constructors can also be invoked using reflection provided by the java.lang.reflect package.

  • The access modifiers are the only modifiers that may be used for declaring constructors.
  • When possible, the object should be a valid, meaningful object once it is constructed, as opposed to relying on a separate initialization method.
  • By convention, a copy constructor is a constructor that accepts an object of its own type as a parameter and copies the data members.
  • If no explicit constructor is defined, then the compiler provides an implicit empty default constructor that takes no parameters.
  • Constructors can be overloaded.
  • The first statement in a constructor may invoke a superclass constructor: super(...); or another constructor in the same class: this(...);
  • If there is no explicit call to super(...) or this(...), then the default superclass constructor super(); is called before the body of the constructor is executed.

Methods in the Object class [edit]

Methods in the Template:Javadoc:SE class are inherited, and thus shared in common by all classes.

The clone method [edit]

Main page: Clone (Java method)

The Template:Javadoc:SE method returns a new object that is a copy of the current object. Classes must implement the marker interface Template:Javadoc:SE to indicate that they can be cloned.

The equals method [edit]

The Template:Javadoc:SE method compares the object to another object and returns a boolean result indicating if the two objects are equal. Semantically, this method compares the contents of the objects whereas the equality comparison operator "==" compares the object references. The equals method is used by many of the data structure classes in the Template:Javadoc:SE package. Some of these data structure classes also rely on the Object.hashCode method—see the hashCode method for details on the contract between equals and hashCode. Implementing equals() isn't always as easy as it seems, see 'Secrets of equals()' for more information.

The finalize method [edit]

Main page: Finalizer

The Template:Javadoc:SE method is called exactly once before the garbage collector frees the memory for object. A class overrides finalize to perform any clean up that must be performed before an object is reclaimed. Most objects do not need to override finalize.

There is no guarantee when the finalize method will be called, or the order in which the finalize method will be called for multiple objects. If the JVM exits without performing garbage collection, the OS may free the objects, in which case the finalize method doesn't get called.

The finalize method should always be declared protected to prevent other classes from calling the finalize method.

protected void finalize() throws Throwable { ... }

The getClass method [edit]

The Template:Javadoc:SE method returns the Template:Javadoc:SE object for the class that was used to instantiate the object. The class object is the base class of reflection in Java. Additional reflection support is provided in the java.lang.reflect package.

The hashCode method [edit]

The Template:Javadoc:SE method returns an integer (int) that is used as a hash code for storing the object in an associative array. Classes that implement the Template:Javadoc:SE interface provide associative arrays and rely on the hashCode method. A good hashCode implementation will return a hash code that is stable (does not change) and evenly distributed (the hash codes of unequal objects tend to be unequal and the hash codes are evenly distributed across integer values).

Because associative arrays depend on both the equals and hashCode methods, there is an important contract between these two methods that must be maintained if the objects are to be inserted into a Map:

For two objects a and b
  • a.equals(b) == b.equals(a)
  • if a.equals(b) then a.hashCode() == b.hashCode()

In order to maintain this contract, a class that overrides the equals method must also override the hashCode method, and vice versa, so that hashCode is based on the same properties (or a subset of the properties) as equals.

A further contract that the map has with the object is that the results of the hashCode and equals methods will not change once the object has been inserted into the map. For this reason, it is generally a good practice to base the hash function on immutable properties of the object.

Two equal objects must have the same hashcode. However, 2 different objects are NOT required to have different hashcodes.

The toString method [edit]

The Template:Javadoc:SE method returns a Template:Javadoc:SE that contains a text representation of the object. The toString method is implicitly called by the compiler when an object operand is used with the string concatenation operators (+ and +=).

The wait and notify thread signaling methods [edit]

Every object has two wait lists for threads associated with it. One wait list is used by the synchronized keyword to acquire the mutex lock associated with the object. If the mutex lock is currently held by another thread, the current thread is added to the list of blocked threads waiting on the mutex lock. The other wait list is used for signaling between threads accomplished through the wait and notify and notifyAll methods.

Use of wait/notify allows efficient coordination of tasks between threads. When one thread needs to wait for another thread to complete an operation, or needs to wait until an event occurs, the thread can suspend its execution and wait to be notified when the event occurs. This is in contrast to polling, where the thread repeatedly sleeps for a short period of time and then checks a flag or other condition indicator. Polling is both more computationally expensive, as the thread has to continue checking, and less responsive since the thread won't notice the condition has changed until the next time to check.

The wait methods [edit]

There are three overloaded versions of the wait method to support different ways to specify the timeout value: Template:Javadoc:SE, Template:Javadoc:SE and Template:Javadoc:SE. The first method uses a timeout value of zero (0), which means that the wait does not timeout; the second method takes the number of milliseconds as a timeout; the third method takes the number of nanoseconds as a timeout, calculated as 1000000 * timeout + nanos.

The thread calling wait is blocked (removed from the set of executable threads) and added to the object's wait list. The thread remains in the object's wait list until one of three events occurs:

  1. another thread calls the object's notify or notifyAll method (see the notify methods below for details);
  2. another thread calls the thread's Template:Javadoc:SE method; or
  3. a non-zero timeout that was specified in the call to wait expires.

The wait method must be called inside of a block or method synchronized on the object. This insures that there are no race conditions between wait and notify. When the thread is placed in the wait list, the thread releases the object's mutex lock. After the thread is removed from the wait list and added to the set of executable threads, it must acquire the object's mutex lock before continuing execution.

The notify and notifyAll methods [edit]

The Template:Javadoc:SE and Template:Javadoc:SE methods remove one or more threads from an object's wait list and add them to the set of executable threads. notify removes a single thread from the wait list, while notifyAll removes all threads from the wait list. Which thread is removed by notify is unspecified and dependent on the JVM implementation.

The notify methods must be called inside of a block or method synchronized on the object. This insures that there are no race conditions between wait and notify.



API/java.lang.String Java Programming
Classes, Objects and Types
Syntax