Introduction to Classes


Defining a Class

Classes are the work horses of object oriented programming. Classes define the general structure of an object, what types of data it holds, and what actions can be performed on that data.

In some ways, a Java class is similar to a C struct. A class, however, defines functions applicable only to its internal data. Furthermore, where a C struct allows access to any data field within a function that references it, Java allows restrictions to be placed on what can and can't be accessed.

The following template provides a somewhat simplified class declaration:

class classname{
      // Variable declarations
type variable_name;
type variable_name2;
...
// Constructors
classname(parameter_set1){
..
}

classname(parameter_set2){
..
}
...

// Methods
return_type method_name(parameters){
..
}
...
}


Back to top


Variable and Method Modifiers

The functionality of a class and its instances can be customized with the use of modifying keywords. Both variables and methods can include these keywords when declared within a class.

Access Modifiers - The use of these statements determines what code can access the variable or method in question.

public

The variable or method can be accessed or invoked by any other class.
protected The variable/method can only be accessed/invoked by code within the same class, within a subclass, or the same package.
private The variable or method can only be accessed/invoked by code within the same class.

If none of these modifiers are used, then the variable or method can only be accessed or invoked by code within the same package (i.e., the default is protected). Frequently, data is declared as private or protected and methods are public. This prevents code within another class from accidentally changing variables. If the variable does need to be changed or accessed by code outside the class or package, an accessor method is made available to perform that task.

Other Modifiers - Aside from access modifiers, there are other key words that impact the use and functionality of variables and methods.

final When used with variables, the final modifier creates a named constant. Much like a #define statement in C, the value cannot be changed. With regard to methods, this modifier prevents the named method from being overridden (see Inheritance below).

static The static modifier associates the named variable or method with the class in general rather than with individual instances of a class. The presence or absence of this keyword results in two kinds of variables and two kinds of methods.


Back to top


Classes and Variables

Instance Fields
Instance fields are declared without the static keyword. These are the variables that are associated with a particular instantiation of a class. When an instance of a class is created, it has its own individual instance fields. These variables can be primitive types or other objects. It is the different variable values held by a class instance that makes each individual instance unique. Instance fields are accessed with the following syntax:

reference_name.variable_name

Here the reference_name is the name of the variable that holds a reference to the object and variable_name is the name of the instance variable of that object. This is a great deal like the manner used in C to access individual fields of a struct. As an example, consider the following code

class Person{
  int age;
}
class InstanceVarDemo{
  public static void main(String args[]){
    Person p1 = new Person();
    p1.age = 27;
    Person p2 = new Person();
    p2.age = 19;
    System.out.println("Person 1 is " + p1.age + " yrs old.");
    System.out.println("Person 2 is " + p2.age + " yrs old.");
  }
}

UNIX>java InstanceVarDemo
Person 1 is 27 yrs old.
Person 2 is 19 yrs old.
UNIX>
I admit, it's not the most complex code in the world, but it does illustrate the point. The variables p1 and p2 refer to two different instances of the Person class and each has a different value for the age variable. Note, this code is not really good object oriented programming because it allows any code to mess with the age variable for any given instance.

Static Fields
Also known as Class Fields, these are variables that are relevant to the class itself. That is, if the class has instances, any static fields that the class defines will be the same for all instances. The Java Math class has two static fields, namely PI and E (these fields are also declared as final so they cannot be changed). Since static fields are associated with the class, you do not have to have an instance of the class to use them. Static fields can be used the same way as instance fields, or in the following manner:

Class_name.variable_name

Here Class_name is the name of the class itself, not the name of the reference variable. The following code illustrates the use of static variables within the Math class.

class StaticVarDemo{
  public static void main(String args[]){
    System.out.println("pi is " + Math.PI);
    System.out.println("e is " + Math.E);
  }
}

UNIX>java StaticVarDemo
pi is 3.141592653589793
e is 2.718281828459045
UNIX>
Note, since each instance of a class has a reference to the static fields of the class, if any instance alters the variable, then it is changed for every instance. Put another way, static fields are global variables within a class.
class Person{
  static int income;
}
class AnotherDemo{
  public static void main(String args[]){
    Person p1 = new Person();
    p1.income = 40000;
    Person p2 = new Person();
    System.out.println("Person 1 has income " + p1.income);
    System.out.println("Person 2 has income " + p2.income);

    p2.income = 100000;
    System.out.println("Person 2 got a raise to " + p2.income);
    System.out.println("Person 1 now makes " + p1.income + " as well");
  }
}

UNIX>java AnotherDemo
Person 1 has income 40000
Person 2 has income 40000
Person 2 got a raise to 100000
Person 1 now makes 100000 as well
UNIX>

Back to top

Methods

Methods are similar to C functions. Like functions, they may take arguments and they may return a value. A method can change data within the class or instance, call other accessible methods, or call itself recursively. As with variables, the static keyword in method declaration results in two different kinds of methods.

Instance Methods
Instance methods operate on an instance of a class. They are called in the following manner:

reference_name.method_name(parameters);

As in C, the parameter set is a comma-separated list. Frequently, parameters aren't even needed with instance methods. The object making the method call is completely accessible within the body of the method. This means that all the instance fields of the object can be changed or accessed within the body of the method. The static variables and methods can be used as well. The following code illustrates the use of instance methods in a commonly used way, as accessor methods.

class Person{
  int age = 21;
  int getAge(){
    return age;
  }
}
class InstanceMethDemo{
  public static void main(String args[]){
    person p1 = new Person();
    System.out.println("p1 is " + p1.getAge());
  }
}

UNIX>java InstanceMethDemo
p1 is 21
UNIX>
Notice that any instance of the Person class will have an age of 21 in this example. Keep in mind this is just for the purpose of illustration, we will see a better way to do this when we get to constructors.

Static Methods
Static methods are associated with a class instead of a particular object. For that reason, they are also referred to as Class methods. Just as static variables can act as global variables, static methods can act as global functions (a la C). Frequently, they are useful operations that don't require a specific instance to operate on. The Math class, for example, defines a number of static methods. A static method can be called just like an instance method (using the instance name), or using the following template:

Class_name.method_name(parameters);

A static method has access to all static variables within a class, but cannot access any instance variables or instance methods. Take a look at the following code for an example of some of the static methods within the Math class.

class MathMethDemo{
  public static void main(String args[]){
    int x = 25;
    int y = 48;
    int z;

    z = Math.min(x, y);
    System.out.println("Min is " + z);
    z = Math.max(x, y);
    System.out.println("Max is " + z);
    System.out.println("The natural log of 25 is " + Math.log(x));
    System.out.println("A random double is " + Math.random());
  }
}

UNIX>java MathMethDemo
Min is 25
Max is 48
The natural log of 25 is 3.2188758248682006
A random double is 0.8233360808119343
UNIX>

Based on this we can now examine what the following statements mean:

	public static void main(String args[]) ...
public means this method is accessible to any code, static says this method is associated with the class it is declared in, and void is the return value. The name of the method is main (obviously) and it takes an array of String objects as its parameters.
	System.out.println("Hello!");
Here System is the name of the class, out is a static variable that refers to a PrintStream object, and println is an instance method within the PrintStream class that takes a String object as an argument.

Method Overloading - Unlike C, Java allows multiple methods to have the same name, as long as each one has a different set of parameters. This distinct parameter signature lets the compiler know which of the methods is actually being invoked. The signature has to vary in either the type and/or order of its parameters. Each overloaded method requires a separate implementation within the class declaration.


Back to top


Constructors

When you create an instance of an object, you generally want it to initialize some or all of the variables with certain values. In our InstanceMethDemo above, we automatically assigned a value of 21 to the age instance field to illustrate the use of the accessor method, getAge(). It would be much more useful (and much more in line with the object-oriented paradigm) to specify an age when an instance is created. Java does this with a special kind of method called a constructor.

A constructor is a method that has the same name as the class it is defined in. Unlike other methods, you cannot call a constructor explicitly. Instead, when you create an instance, the new operator calls the constructor for you and returns a reference to the created object. As such, you don't define a return value for a constructor. Constructors can be public, protected, or private, though public is perhaps the most common. In many ways, they are just like other methods. Consider the code below that revisits the Person class.

class Person{
  int age;

  Person(int x){      // Constructor - same name as the class
    age = x;
  }

  int getAge(){
    return age;
  }
}
class ConstructorDemo1{
  public static void main(String args[]){
    Person p1 = new Person(27);
    System.out.println("p1 is " + p1.getAge());
  }
}

UNIX>java ConstructorDemo1
p1 is 27
UNIX>
Like other methods, constructors can also be overloaded. Suppose we have an object that can be created in a couple of ways, Java allows us to define a constructor for each circumstance. As with other overloaded methods, the parameter signature has to be distinct for each constructor.
class Person{
  int age;
  String name;

  Person(int x){                  // Constructor 1
    age = x;
    name = "J. Doe";
  }
  Person(int x, String s){        // Constructor 2
    age = x;
    name = new String(s);
  }
  int getAge() {return age;}
  String getName() {return name;}
}
class ConstructorDemo2{
  public static void main(String args[]){
    p1 = new Person(25);
    p2 = new Person(27, "Ben Pack");
    System.out.println(p1.getName() + " is " + p1.getAge());
    System.out.println(p2.getName() + " is " + p2.getAge());
  }
}
UNIX>java ConstructorDemo2
J. Doe is 25
Ben Pack is 27
UNIX>
For constructors and other methods, it is often useful to use the this keyword. When used, it refers to the object being executed, or in the case of constructors, created. It also allows one constructor to explicitly call another constructor with a different signature. For example, we can replace the constructors in the ConstructorDemo2 as follows
  Person(int age){
    this(age, "J. Doe");     // Call the other constructor
  }
  Person(int age, String name){
    this.age = age;          // this.age (instance variable) to distinguish
    this.name = name;        // from the parameter of the same name
  }
The output from the program after the constructors have been changed is the same as before.
Back to top

Inheritance

There are times when you need to use the functionality of a class, but it needs to be more specialized, perhaps with other methods and variables. Our Person class for example is (marginally) useful, but what if we want to make it more specialized. Admittedly, this example class is fairly trivial, but if it were larger and more robust, it would make sense to use the functionality of an established class instead of coding up an entirely new, more specialized class that is closely related to the original one. This is the reason that object-oriented programming uses inheritance.

In Java, one class can be declared as a subclass by using the extends keyword when the class is declared. We can, for example, create a subclass of our Person class as follows

class Employee extends Person{
  float salary;
  .
  .
  .
}
Now the Employee class inherits all the variables and methods of its superclass. Even though we haven't declared an age or name within the Employee class, it has inherited those variables from its superclass, Person. The getAge() and getName() methods from Person can also be used with an Employee object. Take a look at the following code and its output
class Person{
  private int age;
  private String name;

  Person(int age){
    this(age, "J. Doe");     // Call the other constructor
  }
  Person(int age, String name){
    this.age = age;          // this.age (instance variable) to distinguish
    this.name = name;        // from the parameter of the same name
  }

  int getAge() {return age;}
  String getName() {return name;}
}

class Employee extends Person{
  private float salary;

  Employee(int age, String name, float salary){
    super(age, name);        // Call the superclass constructor
    this.salary = salary;
  }

  float getSalary() {return salary;}
}

class EmployeeDemo{
  public static void main(String args[]){
    Employee e = new Employee(27, "Ben Pack", 99999.50f);
    System.out.println(e.getName() + " is " + e.getAge());
    System.out.println("and he makes $" + e.getSalary() + " a year.");
    System.out.println("It's a nice dream anyway.");
  }
}


UNIX>java EmployeeDemo
Ben Pack is 27
and he makes $99999.5 a year.
It's a nice dream anyway.
UNIX>
As you can see, the Employee object makes use of the fields and methods of its superclass without having to implement anything except what it has that the superclass doesn't. Note that the Employee constructor calls the constructor of its superclass. If no constructor was specified for Employee, the interpreter would have inserted one implicitly that would have called the superclass constructor. Since every instance of a subclass is also an instance of the superclass, you can use a superclass object to refer to an instance of a subclass. To actually access the methods and variables of the subclass, however, requires a subclass object.

In Java, every class is a subclass of the Object class, or has that class as an ancestor. Even if a class does not explicitly extend another class, it is implicitly assumed to extend object. This means that an Object object can hold a reference to any instance of any class (kind of like the way a void* can be abused in C). Assigning a class instance to an Object object is a widening conversion, so no cast is required. Going the opposite way does require a cast, however. Take a look at the following code

class ObjectCastDemo{
  public static void main(String args[]){
    String s = "There is no spoon.";
    Object o = s;                      //No cast required, widening conversion

    String s2 = (String)o;             //Cast required, narrowing conversion
    System.out.println(s2);
  }
}

UNIX>java ObjectCastDemo
There is no spoon.
UNIX>

If a subclass and its superclass have variables of the same name, then the subclass variable is said to shadow that of the superclass. Simply using the variable name within a subclass method or using the this keyword will be accessing the variable for the subclass. To access the superclass variable, you would have to use the super keyword. For example, if a class and subclass both have a variable x, within the subclass the terms x and this.x refer to the x variable of the subclass. To access the superclass x, you would say super.x. When an instance method is defined with the same name, parameter signature, and return type, it is said to override the superclass method (note this is very different from overloading, don't confuse the two). When a class instance calls a method that has been overriden, it is the instance, not the object refering to the instance, that determines which method will be invoked. This means tha t if a superclass variable is actually refering to the instance of one of its subclasses, the method that is invoked will be the one defined by the subclass.

If a class declares a method as final that means that no subclasses can override the method. If an entire class is declared as final, that class cannot be extended (i.e., no subclasses allowed). Naturally, if a class is declared as final, all of its methods are final as well.
Back to top


Examples of Java classes

The number of classes that Java has available is staggering, fortunately, not all of them are really needed to create the programs we're going to be working with. Below is a listing of some of the classes you might find useful and some links to more information on their methods and variables. Some of the method names might seem longer than necessary, but they describe the action that the method performs.

Wrapper Classes

The Java wrapper classes encapsulate the Java primitive types. Each primitive type has a corresponding class with static and instance methods. The value a wrapper class instance holds is immutable, that is it cannot be changed after it has been created. If a change is required, a new instance has to be created. The wrapper classes are
Byte, Short, Integer, Long, Float, Double, Character, and Boolean. Each contains methods to turn Strings to simple types, as well as accessor methods and methods for converting between the numeric types.

String

The String class represents a string of characters. The string of characters that is referenced by a String object is immutable, that is there is no way that the characters can be changed once the object is created. Note that any time a string of characters is enclosed within double-quotes, the compiler creates a String object to hold a reference to it. In some cases this is simpler than using a constructor.

StringBuffer

The StringBuffer class also represents a string of characters, but unlike the String class, a StringBuffer class is mutable. A StringBuffer object also provides dynamic resizing, so it grows and shrinks as necessary.

Math

The Math class has no instance methods and no constructors. Instead, it has static variables and methods that are useful for trigonometry, exponentiation, etc. It is Java's answer to C's math.h library. Note, Java also has a StrictMath class that implements the same operations but to a greater degree of precision regardless of the operating platform. The performance of the Math class in program execution is much better than that of StrictMath.

Random

The Random class provides useful pseudo-random number generation for use with simulations and other applications. Although the Math class offers a Random static method, the Random class actually allows you to create different instances of objects, all with different seeds. The Random class also allows you to specify whether you want an int, long, double, float, a byte array, or gaussian distribution. Note - to use the Random class, you will have to put the statement import java.util.*; at the top of the source file. This is similar to a #include in C.
Back to top

Exercises

When is this lab due?

The problem below is due at midnight Feb 2 using a submit script (information on how to use this script will be mailed to you).

  1. Total Points: 100
  2. Incorrect output: -10 to -50 pts.
  3. Class inheritance: -20pts
  4. No constructors defined: -20
  5. Wrong class structure: -30

Assumptions for this lab:

  1. Constructors ARE required!

Programming problems to be submitted:

Simple student database

In this exercise, you will implement a very simple student database. You have to write 4 classes: Person, Student, Course and Main. The Main class should print out all student information (illustrated below).
  • The Person class contains name and age, and it is the super class of the Student class.
  • The Student class contains student ID, major, and courses taken by the student. This Student class must inherit name and age from the Person class.
  • The Course class contains the course ID, course name, room number and instructor name.
  • Lastly, you can use Main class to print out all the students' information.

    In this exercise, you are required to use constructors to add student information and course information. There are total of 4 courses and 2 students as specified below:

  • Course1: "cs365", "Programming concepts", "c206", "Dr. Berry"
  • Course2: "cs360", "System programming", "c205", "Dr. Straight"
  • Course3: "cs380", "Theory of computation", "c206", "Dr. Thomason"
  • Course4: "cs140", "Data Structures", "M311", "Dr. Vander-Zanden"

  • Student1: 19, "Ben Pack", "CS", "999-99-9999" and he is taking cs365, cs360 and cs380
  • Student2: 20, "David Moore", "CE", "000-00-0000", and he is taking cs260 and cs140

    Your output should look similar to this:

    UNIX> javac *.java                                             
    UNIX> java ManageStudent                               
    Ben Pack is 19
    Major is CS
    CourseID         CourseName     Room    Instructor
    1.cs365   Programming concepts  c206    Dr. Berry
    2.cs360   System programming    c205    Dr. Straight
    3.cs380   Theory of computation c206    Dr. Thomason
    
    
    David Moore is 20
    Major is CE
    CourseID         CourseName     Room    Instructor
    1.cs360   System programming    c205    Dr. Straight
    2.cs140   Data Structures       M311    Dr. Vander-Zanden
    
    


    Created by Ben Pack & Justin Giles 2001 modified by Yuan Li 2005