Object-oriented programming basic details

Objectives

  • Differentiate types of constructors
  • Create and use constructors
  • Use the "this" keyword
  • Throw exceptions
  • Create and use accessor methods
  • Create and use mutator methods
  • Create and use predicate methods
  • Create a class using inheritance
  • Create a class using composition
  • Describe the difference between overloading and overriding a method
  • Describe the differences between stack and heap memory allocation

Constructors

  • Constructors are like methods, but they must have the same name as the class and they have no return data type.
  • A default constructor has no parameters. A default constructor is automatically provided for every class by Java if (and only if) no constructors have been explicitly provided.
  • A copy constructor is used to make a copy of an object. Its parameter is an object of its own type. You will see an example of this in the overloading section.
  • The main purpose of a constructor is to initialize instance variables for the object being created. Constructors may also need to update static fields, set up network connections, etc.
  • Constructors are called when code uses the new keyword.
  • The first statement in any constructor is a call to another constructor (either another constructor in the same class or a call to a superclass constructor). If you do not explicitly write that call, Java will automatically call the superclass default constructor (which can be a problem if it does not exist).

Constructor examples

Accessors, mutators, and predicate methods

  • Private access is often used on instance fields to make sure other classes can not modify them.
  • Public accessor methods are created to allow the values of the private fields to be read.
  • Public mutator methods are created to allow the values of the private fields to be modified.
  • Mutator methods are where error checking can be placed to make sure instance fields do not get set to invalid values.
  • You can effectively make fields non-readable or non-writable by making them private and deciding to not include private accessor or mutator methods.
  • If it does not matter whether instance fields are seen and modified by other classes and don't need error checking, then you can go ahead and make them public access and just skip creating accessor and mutator methods.
  • Once you have a mutator method for a field, you should use that mutator method everywhere you want to set that field's value.
  • Predicate methods return true or false. They often have names such as isEmpty(), isVisible(), etc.

The "this" keyword

The this keyoword refers to the current object. It was used in the example above to call one constructor from another one in the same class. It is also used to differentiate instance variables from local variables when they have the same name. For example, here are the mutators from the previous example rewritten to work even when the local variable names are the same as the instance variable names.

Overloading

Multiple methods in a class can have the same name as long as the compiler can differentiate them by their signature, which means having a parameter list that differs by more than the parameter names (which are irrelevant in this case). The Date example above had overloaded constructors. Here's the same example with additional constructor overloading:

Overriding

Classes inherit the fields and methods (but not constructors) of the classes they extend (subclass). Sometimes the inherited methods do not do what you want them to. That is what overriding a method is for. You can write a new method that replaces the inherited method. One common example is the toString() method. It is a member of Object, so every class in Java has an inherited toString() instance method. It prints out something like this for the Date object: Date@1db9742. To make that more sensible, we can replace (override) the inherited method like this:

Composition

Composition simply means using objects as part of another object. Composition represents a has-a relationship in contrast to the is-a relationship that inheritance represents. The example below shows two four objects being used to compose a new object:

Destructors

  • The idea behind destructors is to clean up resources no longer needed when an object goes away.
  • The intent of destructors is to be called when an object is destroyed.
  • One big problem with destructors in Java is you have no guarantee if or when they will be called after an object is no longer being used. The program may end without the destructor being called.
  • If destructors worked as you might expect, you could use a class-level static integer to hold a count of how many objects were created out of a class. The constructor(s) would increment the count whenever a new object is created. The destructor would decrement the count - but that doesn't work since you can't count on the destructor being called when the object goes out of use.
  • The signature for a destructor in Java is: public void finalize()

Static class members

  • There is a single copy of static class fields that the class and all objects share. This is different than instance fields since the class does not have access to instance fields and each object gets their own version of each instance variable.
  • Static fields exist as soon as a class is loaded. Instance fields do not exist until an object is created from a class.
  • Static members can be referenced using either an object reference or the class name. Instance members require an object reference.
  • The difference between static methods and instance methods is that instance methods get a reference to the object they have been called on. This means that instance methods can access both instance and static members of an object, but static methods can only access static members.
  • An example of accessing a static int field of the Color class: Color.BLACK

Types of access

  • public: all other classes have access
  • private: only the class itself has access
  • protected: only the class itself and subclasses have access
  • default (package): only the class itself, subclasses, and other classes in the same package have access

Stack vs. heap

  • Local method variables are allocated storage on the stack. They come into existence when the method starts to run and are destroyed when the method is finished running.
  • If a method calls itself (recursion), then there will be more stack space allocated for that call and new versions of the local variables will be created on the stack.
  • Objects are created on the heap using the keyword new. They exist as long as there is a reference to them. After that it is up to the JVM garbage collector to decide when to dispose of them.

Throwing an exception

Throwing an exception is easy. Use the keyword throw followed by the exception object you want to throw. If the exception is a checked exception, then the method the throw is contained in must either try and catch the exception or declare that it throws that type of exception in the method signature. For example: