Lecture 2: Arrays, Classes, Objects, and Strings - Part 3


Part 3: Strings and more Examples

In the second part of this lecture, we learned about the concepts:
Methods (with and without the return statement)
Class
Object
Fields
Instantiation of objects from classes using new
The special constructor method
Accessing fields and methods of an object
The special keyword this
We have seen an example of how classes can be used to extend the data types that Java knows about, and we have seen an example of how a 'main' program can use a newly defined class.

Before we continue with defining our own classes and object, let's take a look at a class that's already defined in the Java language: the String class. Here is the - fairly confusing looking - definition of the String class:

public final class java.lang.String extends java.lang.Object
{
        // Constructors
    public String();
    public String(String  value); 
        // Selected Methods
    public char charAt(int  index); 
    public String concat(String  str);
    public boolean equals(Object  anObject);
    public int indexOf(String  str);    
    public int lastIndexOf(String  str);
    public int length();
    public String replace(char oldChar, char  newChar);
    public String substring(int  beginIndex);
    public String substring(int  beginIndex, int endIndex);
    public String toLowerCase();
    public String toUpperCase();
    public String trim();
    public static String valueOf(boolean  b);
    public static String valueOf(double  d)
    public static String valueOf(int  i);
}
There are many details here but let's ignore them for now and instead focus on the essentials:
the name of the class is String
the String class has no fields
the String class has two constructors, one without input parameters, one with String input parameter
the String class has a many methods
some methods take input parameters, others do not
all methods have a return type different from void
the class String extends Java so that it knows about a String object from now on.
the class String itself already uses the String type
Recall that to access the fields and methods of a class, one uses the 'dot' operator. So, here are some examples using the new String class.

Example: Define two String objects and print them out. Then concatenate the second string to the first one, and print that one out. Finally, define a third string and test if it is equal to the first one. Also, print out the lengths of all strings.
public class Test
{
   public static void main(String arg[])
   {
      // defining two strings and printing them out
      String s1 = new String("Bert");
      String s2 = new String("Wachsmuth");
      System.out.println("First String:  " + s1);
      System.out.println("Second String: " + s2);
      // adding the second string to the first and printing result
      s1 = s1.concat(s2);
      System.out.println("Concatenated: " + s1);
      // defining a third string and testing for equality
      String s3 = new String("Bert Wachsmuth");
      if (s1.equals(s3))
         System.out.println("they are the same");
      else
         System.out.println("they are different");
   }
}
 Let's review what's going on here:
Since a String is a class, each object of type string instantiated from the class will have all methods mentioned above. They can be accessed using the 'dot' operator.
The first two lines are easy: two new objects are instantiated. They are of type String, and they are named s1 and s2. During instantiation, the constructor method of the String class is automatically called and the strings will receive the initial values "Bert" and "Wachsmuth". After the strings are defined, they are printed out.
Then we call upon the object s1 and use it's concatenation method to append the input string to itself. As we can see from the method header of the concat method, that method returns a string, namely the concatenation of the current string together with the input string. The output of the concat method is assigned to s1 and then s1 is printed out.
Then a third string object is instantiated and initialized to "Bert Wachsmuth". To check if its the same as the first string, we call upon the first string's 'equal' method. We could have just as easily used s3.equals(s1). Note: there also is the method equalsIgnoreCase, which checks whether the input string agrees with the current string, not counting upper and lower cases
Actually, since Strings are so frequently used in virtually every Java program, there's a shortcut to creating them:

The 'correct' way to create a String variable would be
String s = new String("whatever");
Instead, you can also use
String s = "whatever";
While there actually is a difference between these two ways, we don't need to worry about it. You can usually use whichever way you like best, at least in small programs.

As another shortcut, you can also try use the usual '==' to test two strings for equality, instead of using the String method 'equals':
String s = "Bert";
String t = "Bert";
if (s == t)
   System.out.println("Same");
Do NOT do that. Every time you need to check two strings for equality, use the 'equals' method instead of the double-equal signs.

To see why, try this example in a small test program:
String s = new String("Bert");
String t = new String("Bert");
if (s == t)
   System.out.println("Same via double-equal");
if (s.equals(t))
   System.out.println("Save via equals method");
When you run this program, only the second test will be successful, the first one will not notice that the strings are the same. The reason for that is that '==' test whether two strings are stored at the same memory location, whereas 'equals' checks whether the strings acutally match, character for character. So, to avoid any problems, always use the 'equals' or 'equalsIgnoreCase' methods to test two strings for equality unless you know exactly what you are doing.

Let's do some more examples, for practise. We won't need any specific examples using Strings in any major way. Many programs simply create strings, and test them for equality (using the equals) method - and we already know how that works. Instead, let's create a more involved examples on how classes are used. We will expand and modify this example later, so perhaps you want to create these classes on your account as well (remember, you can cut-and-paste the source code below).

Example: Suppose we are writing on a program dealing with geometric shapes. Every such shape has a name, an area, and a perimeter (or cicumference). Create two classes: a Rectangle class and a Circle class. Then create a small test program to test the new classes.
A Rectangle and a Circle are to have a name, and area, and a perimeter. Therefore, we shall need three fields. Moreover, we need methods to compute the area and perimeter, a method to display the information, and a constructor to initialize everything properly. Moreoever, a Rectangle has a width and a height, while a circle has a radius, and both classes will need a method to set that information properly. So, here we go, defining the two classes Rectangle and Circle.
class Rectangle
{
   // fields
   String name;
   double area;
   double perimeter;
   double width;
   double height;
   // constructor
   Rectangle()
   {
      name = "Rectangle";
      area = perimeter = 0;
   }
   void setDimensions(double _height, double _width)
   {
      height = _height;
      width  = _width;
   }
   void computeArea()
   {
      area = height * width;
   }
   void computePerimeter()
   {
      perimeter = 2 * (height + width);
   }
   void display()
   {
      System.out.println("Type: " + name);
      System.out.println("Area: " + area);
      System.out.println("Perimeter: " + perimeter);
   }
}
class Circle
{
   // fields
   String name;
   double area;
   double perimeter;
   double radius;
   // constructor
   Circle()
   {
      name = "Circle";
      area = perimeter = 0;
   }
   void setDimensions(double _radius)
   {
      radius= _radius;
   }
   void computeArea()
   {
      area = Math.PI * radius * radius;
   }
   void computePerimeter()
   {
      perimeter = 2 * Math.PI * radius;
   }
   void display()
   {
      System.out.println("Type: " + name);
      System.out.println("Area: " + area);
      System.out.println("Perimeter: " + perimeter);
   }
}
Remember, each of the above classes should be saved in its own file, one named 'Rectangle.java' and another named 'Circle.java'. Now, let's go for a small program to test our two new classes:
public class ShapeTest
{
   public static void main(String args[])
   {
      // instantiating two shapes
      Rectangle r = new Rectangle();
      Circle c = new Circle();
      // setting their dimensions
      r.setDimensions(2, 3);
      c.setDimensions(4);
      // computing area and perimeter
      r.computeArea();
      r.computePerimeter();
      c.computeArea();
      c.computePerimeter();
      // displaying the information
      r.display();
      c.display();
   }
}
Recall that the above file must be saved as 'ShapeTest.java' !

So, these three classes (Rectangle, Circle, ShapeTest) illustrate how to program in Java: instead of creating one large program to do all the work, we create many small, smart objects. These objects contains all the information they need, and they are smart, having methods to do certain operations. Then, in a 'main' program, we simply instantiate the right objects, and "tell them" what we need them to do by calling on their methods. The objects in turn will do what they are told, and the entire program works.

A Word for the Wise
When dealing with objects, you should always pretent that a particular object really exists. Objects are like the employees of your main program. Like any good boss, you should empower your objects as much as possible: anything they can handle on their own they should, and they should only consult with you - the main program - when absolutely necessary.


Assignment: Suppose we are creating an payroll system for the Acme Widget company. They have two types of employees: trainee workers who make $12 per hour, and experienced workers making $20 per hour. For each of the two types of workers, create a seperate class. Each class should have fields for the type of worker, the name of the worker, the number of hours worked, and the total salary owed to the worker. They should have methods to set the name, to set the number of hours, to compute the salary, and to print out the info. Finally, create a small Payroll class to test everything.

(bgw)