Session 3: 1 April: Chapter 6: Creating Classes and Methods

To some extent this will cover concepts you have already seen, but now we will focus more on the actual syntax and usage.

Class statement

All code is contained within a Class definition. It looks like this:

<3.1>

class Who {

//body of the class

}

But we usually qualify it as a public class, which means it can be used by other classes:

<3.2>

public class Who {

}

If it extends another class it looks like this:

<3.3>

public class Who extends What {

}

And if it implements an interface it might look like this:

<3.4>

public class Who extends What implements IDontKnow {

}

You can implement as many interfaces as you want, but you can only extend one superclass (single inheritance):

<3.5>

public class Who extends What
implements IDontKnow, ThirdBase {
}

Remember, write a method body for every method signature in the interface, otherwise it won't compile.

Instance Variables

In the first session, we talked about object attributes otherwise known as fields. In code, these are instance variables. Book example:

<3.6>

public class VolcanoRobot6 {
   String status;
   int speed;
   float temperature;
}

Remember, any variable has the scope of the block where it is declared. In this case, they have the scope of the whole object, so they can be used in any method.

(with the exception of static methods, as we will explain).

Normally, we like to encapsulate all instance variables so other classes cannot directly access them, so the declaration normally would look like this:

<3.7>

public class VolcanoRobot6 {
   private String status;
   private int speed;
   private float temperature;
}

Remember, hiding the variables from the outside allows you to change the way you implement a class without breaking the social contract with other developers.

Instance variables are initialized upon the creation of the class. You can choose to give them initial (default) values, like this:

<3.8>

public class VolcanoRobot6 {
   private String status = "idle";
   private int speed = 0;
   private float temperature = 98.6;
}

If you do not provide initial values, the JVM will do so:

numbers default to 0,

booleans default to false,

objects (including Strings and Arrays) default to null.

Static (Class) Variables

These look like instance variables, except they have the word static in their declaration:

<3.9>

public class VolcanoRobot6 {
   private static String status = "idle";
   private static int speed = 0;
   private static float temperature = 98.6;
}

This has two effects:

There is only one value shared among all instances of the class.

If any instance changes the value, all other instances will immediately see the new value.

It can be accessed from a class method (static method) which does not require that any object be created.

We will talk about that in a minute

The most common type of static variable is a constant. These are usually public:

<3.10>

public static final double PI = 3.14159

Instance Methods

In the first session we talked about object behaviors also known as methods. Now we will talk about how to write them.

"Normal" methods are instance methods, that is, they require an object instance to execute.

We will talk about static or class methods in a minute.

A method signature looks like this:

<3.11>

returnType methodName(type1 arg1, type2 arg2, ...) {
   // body of the method
}

You return values through the return keyword, e.g. "return 0;"

If a return type was declared you must return a value.

If the return is void, just "return;" or let the method bottom out with no return statement.

The returnType, type1 etc. can be any primitive or object type.

Strings and Arrays are common object types, example:

<3.12>

double[] parseNumbersFromText(String inputText) {}

Primitives are passed by value whereas objects are passed by reference.

Changing a primitive will only change that local copy of the primitive:

<3.13>

int multiplyByTwo(int i) {
   i *= 2; //only changes local copy of i
   return i;
}

But with an object, there is no local copy of the object, only a local copy of the reference:

<3.14>

void multiplyByTwo(int[] intarray) {
   for (int i=0; i< intarray.length; i++) {
      intarray[i] *= 2; //changes array in caller!
      //don't need to return any value
   }
}

void is common return type, which means "no return value".

this and super Keywords

In any instance method, the word this refers to the current object instance. Usually is implied, e.g. in this example we don't need to say this.height:

<3.15>

void setHeight(float theHeight) {
   height = theHeight;
}

But when an instance variable has been hidden by a local variable, you might need this:

<3.16>

void setHeight(float height) {
   this.height = height;
}

In any object, super refers to the superclass and can be used just like an object reference

When overriding a method, you can use super to call the parent version of the method.

<3.17>

void doSomethingReallyCool() {
   // first call the parent version
   super.doSomethingReallyCool();
   // now add additional functionality
   System.out.println( this.toString() );
}

 

Private vs. Public Methods

One of our encapsulation rules is: minimize the number of public methods. That way, you have less to support and the social contract is easier to maintain.

Therefore, most methods should be declared private. They are only visible within the class and can only be used by that class.

<3.18>

private int multiplyByTwo(int i) {
   i *= 2; //only changes local copy of i
   return i;
}

If something needs to be visible outside, it is declared public:

<3.19>

public void setHeight(float height) {
   this.height = height;
}

If there is neither public nor private, the method has package access aka friend access and can only be used by classes within its package.

we will talk about packages in week 7.

Finally, there is protected access which makes something visible only to itself and subclasses.

Static or Class Methods

Remember, static methods can be called without needing to create an object instance:

<3.20>

int i = Integer.parseInt("123");

But they also can be called with an instance:

<3.21>

Integer I = new Integer();
int i = I.parseInt("123");

To declare a static method, simply put the static keyword before the return type:

<3.22>

public static int parseInt(String input)

Static methods do not have access to any instance variables, but they do have access to static variables.

Static methods do not have access to the this keyword.

The main Method

Any Java class can have an optional main method declared exactly like this:

<3.23>

public static void main(String[] args)

The JVM knows about the main method, allowing you to invoke the method from a command line.

The args are command line arguments. You (the programmer) are responsible for validating them.

<3.24>

public class SayMyName {
   public static void main(String[] args) {
      if (args.length == 0
      || args[0].trim().length() == 0) {
         System.out.println("You must enter a name");
      } else {
        System.out.println(args[0]);
      }
   }
}
usage:
prompt>java SayMyName Yoda
prompt>java SayMyName "Darth Fluffy"

 

This is how all Java applications are started.

An application consists of one or more classes, invoked through some class' main method

Different from an applet which runs within a web browser or a servlet which runs in a web server.

The same class can be used for both an application and an applet.

Constructors

There is a special kind of method called a constructor which is only used when creating an object.

no return type and the name must be the same as the class name (including capitalization).

A constructor with no arguments:

<3.25>

public class Foo {
   String bar;
   public Foo() {
      //set bar to default value
      bar = "fubar";
   }
}
usage: Foo f = new Foo();

A constructor with an argument:

<3.26>

public Foo(String bar) {
   this.bar = bar;
}
usage: Foo f = new Foo("beyond all recognition");

In some cases you need to call super() to call the superclass' constructor, especially if there are arguments:

<3.27>

public class R2Unit extends Droid {
   public R2Unit(String name) {
   super(name); //calls the Droid(String name) ctor
   }
}

If you don't do an explicit super() it will automatically call super() with no arguments at the beginning of the constructor.

The super() call must be at the very beginning of the constructor.

Every method will always have a default constructor with no arguments. If you don't write one yourself, the compiler will automatically create one that looks like this:

<3.28>

public class R2Unit extends Droid {
   public R2Unit() {
   super();
   }
}

This ensures that the implicit call to super() will always execute.

Method Overloading

You can define multiple methods with the same name, same return type, but different arguments. This is called method overloading.

Common in constructors. Example from Java API:

<3.29>

FileInputStream(File file)
FileInputStream(FileDescriptor fdObj)
FileInputStream(String name)

Should be done if the method is doing fundamentally the same thing but taking different input types.

<3.30>

//adds 2 ints
int addTwoNumbers(int i1, int i2) {
   return i1 + i2;
}
//adds the first two elements of an array
int addTwoNumbers(int[] iarray) {
   addTwoNumbers(iarray[0], iarray[1]);
}

For constructors, it is common to use this() to pass part of the work along to another constructor

<3.31>

public class R2Unit extends Droid {
   public R2Unit(String name) {
   this(name, null);
}
public R2Unit(String name, Color color) {
   super(name, color);
   }
}

Overloading not to be confused with overriding a method

when you redefine a method from a superclass to give it more specialized functionality.

Finalizers

A special method signature which you can define in any class

<3.32>

protected void finalize() throws Throwable {
   super.finalize(); //doesn't do it implicitly!
   //add your own release code
}

The finalize() is called automatically by the JVM just before the object is garbage collected.

You don't call it yourself.

You don't know when it will be called.

An object is GCd sometime after all the references to it have been removed.

Finalizers are used for freeing up external resources such as files. Usually you don't use them at all.

Ignore what the book says about finalize(), it is wrong. You shouldn't use finalize() to remove references to other objects.

Inner Classes

You can create a class which is only visible to one other class. This is useful for creating data structures specific to that class. These are called inner classes.

<3.33>

class Salesperson {
   class ContactInfo {
      String address,
      city,
      state,
      zip,
      phone;
   }

private ContactInfo ci;

private void setContactInfo(String address,
String city,
String state,
String zip,
String phone) {
   ci = new ContactInfo();
   ci.address = address;
   ci.city = city;
   ci.state = state;
   ci.zip = zip;
   ci.phone = phone;
   }
}

In this simple example, we use an inner class called ContactInfo to store all the contact information, instead of using separate variables in the Salesperson class.

The ContactInfo object (ci) is declared private so it is not visible outside of the Salesperson object.

Inner classes are used to better encapsulate data structures and methods that the outside world does not need to know about.