Return to Course Content Home

More Object-Oriented Programming in Java

Required Reading

None

Intro

Now that you've read the tutorial, there are a few areas we should still discuss.

Constructors

You've already seen the examples of Object construction in the Java Tutorial. What I'd like to do is cover constructors in a little more detail. Constructors are used sole to create instances of a Class. These instances are referred to as Objects.

When you make an Object, your code only creates a single Object by calling a single constructor. Even if you don't define a constructor in an Object, Java will make one for you. This will always be the "default" constructor that has no arguments, and executes no methods during the construction. All it does is populate the instance attributes.

Consider the code shown below:


public class Bar {

 private String name="Bob";

  

  public Bar() {

  }

  

  public Bar(String name) {

    this.name = name;

  }



  public String getName() {

    return name;

  }    

}



public class Foo extends Bar{

    

    final int x;

    final int y;

  

  public Foo() {

      super("Adele");

      x = 5;

      y = 6;

  }

  

  public Foo(int x, int y) {

      this.x = x;

      this.y = y;

  }

  

  public static void main(String[] args) {

      Foo f1 = new Foo();

      Foo f2 = new Foo(0, 0);

      System.out.println("f1 data: name = " + f1.getName() + ", x=" + f1.x + ", y=" + f1.y);

      System.out.println("f2 data: name = " + f2.getName() + ", x=" + f2.x + ", y=" + f2.y);

  }

}

We can create a Foo class by either calling new Foo() or new Foo(x, y). If we use the Foo() constructor, notice the super("Adele") statement. This explicitly calls the superclass constructor Bar(String name). Notice in the Foo(int x, int y) constructor, there is not super() call. Does this mean the superclass constructor is not called?

No, it doesn't. If you don't specify a constructor with the super(...) statement, the default constructor is called.

If you run the above code, you get:


f1 data: name = Adele, x=5, y=6

f2 data: name = Bob, x=0, y=0

There is one drawback to this pattern of constructors, often you want a base constructor to do most of the initialization, and have the constructors with arguments just do a little bit more. A common pattern is shown below:


public class Foo extends Bar{

    

    final int x;

    final int y;

  

  public Foo() {

      this("Adele");

      x = 5;

      y = 6;

  }

  

  public Foo(String name) {

      super(name);

      x = 5;

      y = 6;

  }

  

  public Foo(int x, int y) {

      this("Adele");

      this.x = x;

      this.y = y;

  }

  

  public static void main(String[] args) {

      Foo f1 = new Foo();

      Foo f2 = new Foo(0, 0);

      System.out.println("f1 data: name = " + f1.getName() + ", x=" + f1.x + ", y=" + f1.y);

      System.out.println("f2 data: name = " + f2.getName() + ", x=" + f2.x + ", y=" + f2.y);

  }

}

Note that for this example, the output will be:


f1 data: name = Adele, x=5, y=6

f2 data: name = Adele, x=0, y=0

In this case, there is only one constructor that calls the superclass constructor, and it is the Foo(String name) variant. The other constructors call this(String), which chains the constructors together to get the same effect.

There is one other way to initialize values besides the "typical" constructor, and that is called an object initializer. This can be looked at a chunk of code that acts as a global constructor. Look at the code below:

public class Bar {

 private String name="Bob";

  

  public Bar() {

  }

  

  public Bar(String name) {

    this.name = name;

  }



  public String getName() {

    return name;

  }    



  public void setName(String name) {

    this.name = name;

  }

}



public class Foo extends Bar{

    

    final int x;

    final int y;



    { 

       setName("Adele");

    }

  

  public Foo() {

      super();

      x = 5;

      y = 6;

  }

  

  public Foo(int x, int y) {

      super();

      this.x = x;

      this.y = y;

  }

  

  public static void main(String[] args) {

      Foo f1 = new Foo();

      Foo f2 = new Foo(0, 0);

      System.out.println("f1 data: name = " + f1.getName() + ", x=" + f1.x + ", y=" + f1.y);

      System.out.println("f2 data: name = " + f2.getName() + ", x=" + f2.x + ", y=" + f2.y);

  }

}

The code above produces the same output as the second example.


f1 data: name = Adele, x=5, y=6

f2 data: name = Adele, x=0, y=0

The code within the {}'s after the final int y; statement is the object initializer, and is run before any of the specific constructors for the Foo class. This overrides the "Bob" value that is assigned to the name attribute.

It is possible to have a static initializer, which initializes static attributes in the same way.


  static {

      // some code to initialize static attributes

  }

Overloading

As you may have noticed in the Java Tutorial, Overloading is when you have several named methods of a single name, but with different number of arguments or type of arguments. This allows you to reuse a method name for the same type of behavior, but realize that with different arguments, the method might have to do something different internally. For example, in a class hypothetical class MyMathUtilities you could have something like:


   public static int add(int x, int y {

      return x + y;

   }

   

   public static int add(String x, String y) {

      return Integer.parseInt(x).intValue() + Integer.parseInt(y).intValue();

   }

Both methods are designed to add two numbers, hence the "add" method name. In this case one adds two numbers that are initially integers, the other adds two numbers that start out as strings, but end up as an integer.

Comments and Javadoc

Commenting code has always been a good idea. Sun created a tool to turn you comments in your code into a stand-alone html document. This process is what is used to build the Java API. The comments you see in the Javadoc are actually generated from comments found in the source code.

There is a specific style and format that you must use so that the Javadoc tool will recognize your comments and and turn them into readable HTML. Read the reference document on How to Write Doc Comments for the Javadoc Tool for a tutorial.

Abstract classes vs interfaces

When should you use an Abstract class versus an interface? Both seem to have close to the same functionality?

An interface allows somebody to provide any type of class to satisfy the interface requirements. Even though there isn't multiple inheritance in Java, implementing multiple interfaces is allowed. This allows a class that at first glance might have a very different purpose from still satisfying the requirements in an interface.

Many Java programmers believe that every class used by others should actually be an interface. This allows you the developer to make significant changes in a class that implements the interface, but it is inconsequential to the user. In addition, the selection of an interface allows the user to always provide their own class if they need it.

In contrast, an Abstract class tends to provide a bit more functionality than an interface. Abstract classes can even have constructors, they just need to be called by a subclass with the super() method. Abstract classes provide more than just a method signature, they typically provide a lot of functionality already built in to many, if not all of the methods.

Visibility Modifiers

Viability Modifiers are keywords that signify who can see the classes, methods, or attributes that you create.

public
This modifier indicates that the variable or method can be accessed anywhere an instance of the class is accessible. A class may also be designated public, which means that any other class can use the class definition. The name of a public class must match the filename, thus a file can have only one public class private

"public" modifiers should be used for class definitions and method definitions. The only time "public" should be used with an attribute if it is a constant (final static public ....).
private
A private variable or method is only accessible from methods within the same class. Declaring a class variable private "hides" the data within the class, making the data available outside the class only through method calls.

Private methods are variables are designed to let you, the developer, manage attributes and methods as you wish. These methods are to never be used by the "general public" because how they are used and interact may change.
protected
Protected variables or methods can only be accessed by methods within the class, within classes in the same package, and within subclasses. Protected variables or methods are inherited by subclasses of the same or different package.

Protected variables and methods are typically used in a superclass, letting a person who subclasses the object have access to some of the internals of the superclass. Some people do not use protected methods because they break the encapsulation of the superclass, and permit a developer to change the internals of a superclass and thus break subclasses.
(default - non provided)
This is also referred to as "package protected". A variable or method has default visibility if a modifier is omitted. Default visibility indicates that the variable or method can be accessed by methods within the class, and within classes in the same package. Default variables are inherited only by subclasses in the same package.

It is recommended that you do not use this type of access in your design of code, it turns out it promotes bad design. If you need to access attributes or methods of another class, good design requires use of public or private access.

How to tell if a class is an instance of another class

One final tip is to show you how to test if an object is an instance of another class. There is actually an "instanceof" test that can be used within an "if" statement

if (foo instanceof Integer) {
  Integer myInt = (Integer)foo;
  // do something with myInt...
}

The above test checks to see if "foo" is an instance of the Integer class, if so, you can safely cast foo to be of type Integer.

Summary