4: Classes and Methods

What We Will Cover


Illuminations

Questions from last class?

Homework Questions?

  • A3-Postal Bar Codes (3/4/09)
  • Given the following code:
    Scanner input = new Scanner(System.in);
    
    Which of the following statements will compile?
    1. int x = input.next();
    2. String x = input.next();
    3. int x = input.nextInt();
    4. String x = input.nextInt();
    For more information see lesson: 2.2.4: Reading Interactive Input

Homework Discussion Questions

  1. What was the hardest part of the assignment?
  2. For C++ programmers new to Java, how similar does Java seem to C++ so far?
  3. Is it hard to make an executable JAR file?

4.1: Classes and Methods

Learner Outcomes

At the end of the lesson the student will be able to:

  • Describe the rationale for object-oriented programming
  • Describe how to declare a class
  • Declare instance variables
  • Define and call simple instance methods

4.1.1: Introduction to Objects and Classes

  • Java programs consist of objects that interact with each other
  • Because of this, Java is known as an object-oriented programming language
  • This means that when you program in Java you focus on objects

Why Objects?

  • If you look around you, how would you summarize what you see in one word?
  • In programming terms, that one word is:

    Object: a person, place or thing that either performs some action in a situation or is acted upon

  • Each object has certain characteristics, like color or number of appendages
  • Also, each object has behaviors, which are the things it can do
  • For example, a car has characteristics such as color and four wheels
  • Also, a car has behaviors such as accelerating and braking
  • As humans we are use to working with objects in the real world
  • We know how to reason about objects and solve problems with objects

Software Objects

  • In object oriented programs, we use our ability to work with objects to solve software problems
  • However, when we develop software we have to make things simpler than in the real world
  • How do we simplify without losing the essence of the object?
  • We can simplify using a concept called abstraction
  • Abstraction: taking away inessential features, until only the relevant parts of the concept remains

Describing Software Objects

  • To describe a software object, we write code for storing data and taking actions
  • We write all the code describing an object in a single software construct called a class

    Class: a software construct that is used as a blueprint to create objects.

  • We use the word class because a class defines a type of (or kind of, or class of) object
  • In a class, data is defined by variables (fields) and the actions are performed in methods
  • Each object can have different data but each object of a class has the same type of data and the same methods
  • A class describes an object but is not the object itself
  • From classes we construct objects for our programs
  • Objects are usually referred to as an object of a class or as an instance of a class
  • In the following sections we look at how to code classes that construct objects

4.1.2: Declaring a Class

  • The general syntax for writing a class is:
    [accessModifier] class ClassName {
        // fields, constructors, and methods of the class
    }
    
  • Where:
    • accessModifier: determines what classes can access this class
    • ClassName: the name you make up for the class
  • For now, use the access modifier: public
  • The public access modifier means all other classes can use this class
  • Later on we will discuss why you might chose another modifier
  • Note that the ClassName is capitalized by convention
  • The curly braces encapsulates all the fields, constructors, and methods of the class
  • You can see the modifiers, names and other features in the following example
  • Note that this example class is overly simplistic but gives us a starting point

Class Declaration -- First Attempt

1
2
3
4
5
6
7
8
public class Product1 {
    public String name;
    public double price;

    public void print() {
        System.out.println(name + " @ " + price);
    }
}

Testing the Class

  • To run the example class we write another class for testing
  • We can start with a simple main() method that instantiates two objects, assigns values to variables and calls a method

Test Class for Class Declaration

1
2
3
4
5
6
7
8
9
10
11
12
public class ProductTest1 {
    public static void main(String[] args) {
        Product1 milk = new Product1();
        milk.name = "Whole milk";
        milk.price = 4.59;
        Product1 bread = new Product1();
        bread.name = "Wheat bread";
        bread.price = 2.99;
        milk.print();
        bread.print();
    }
}

More Information

4.1.3: Instance Variables

  • Recall the following two lines from the class declaration:
    public String name;
    public double price;
    
  • These variables are known as instance variables, member variables or fields:

    Instance variable: a variable defined in a class for which each object (instance) has a separate copy.

  • The word public means that there is no restriction on their use
  • To make use of instance variables, we must first instantiate an object:
    Product1 milk = new Product1();
    
  • The new operator allocates memory space for each instance variable
  • After instantiating an object, we can assign values to the variables:
    milk.name = "Whole milk";
    milk.price = 4.59;
    
  • Each object has its own copy of instance variables:
    Product1 bread = new Product1();
    bread.name = "Wheat bread";
    bread.price = 2.99;
    
  • Thus we can assign different values to the variables in different objects
  • Together, the milk and bread objects have four instance variables

More Information

4.1.4: Introduction to Methods

Method -- a named block of code defined inside a class.

  • In other languages, a method is known as a function, procedure or subroutine
  • Our Product1 class has only one method definition:
    public void print() {
        System.out.println(name + " @ " + price);
    }
    
  • All method definitions belong to some class and are defined in the class to which they belong
  • A method definition has two parts:
    • a heading: public void print()
    • a body:
      {
          System.out.println(name + " @ " + price);
      }
      
  • You call a method you write the same way you call a method for a predefined class
  • When we call a method, we pass the flow of control to the method
  • In this case, the method executes the single statement:
    System.out.println(name + " @ " + price);
    
  • When the method finishes executing all its statements, the flow of control returns to the calling point
  • In this case, the caller was one of these lines in the main() method of ProductTest1:
    milk.print();
    bread.print();
    
  • Both of these method calls transferred control to the print() method of the Product1 class
  • Both times the method returned after executing the last command of the method

4.1.5: Summary

  • Java programs primarily consist of objects that interact with each other
  • An object is a person, place or thing that either performs some action in a situation or is acted upon
  • Software objects are often simplifications or real world objects
  • The process of simplifying real world objects without losing their essential features is known as abstraction
  • To describe a software object, we write code for both data and actions
  • We write all the code describing an object in a single software construct called a class
  • The class is like a blueprint and is the set of rules for creating an object
  • All Java code is written using classes
  • A class uses instance variables to store the data for an object
  • The actions that an object can perform are defined in methods
  • A method is a set of statements that performs a specific task
  • When you want the statements executed, you call the method

Check Yourself

  1. What is a class?
  2. What is an object?
  3. What is the difference between a class and an object?
  4. What is the syntax for declaring a class?
  5. What is a method?
  6. Where are methods defined?

Exercise 4.1

Take one minute to prepare an answer the following questions.

  1. Add a method to the Product1 class named initialize() that sets the instance variable name to "none" and the instance variable price to 0.
  2. Update the ProductTest1 class to call the initialize() method for each object

4.2: More About Methods

Learner Outcomes

At the end of the lesson the student will be able to:

  • Write methods for classes
  • Pass values to methods
  • Return values from methods
  • Code void return types for methods

4.2.1: Enhancing Our Class

  • Recall that methods are a named block of code defined inside a class
  • We looked at an example method:
    public void print() {
        System.out.println(name + " @ " + price);
    }
    
  • The name is specified in the method heading, in this case print
  • The method body contains the statements to execute, which are defined inside the curly braces
  • Many methods have input values (a.k.a. arguments or parameters) that are transferred or passed into the method
  • We have used methods that take arguments such as "Hello, World!" in:
    System.out.println("Hello, World!");
    
  • Many methods have output or return values
  • For example, the name in: name = input.nextLine();
  • In general, we categorize methods into two kinds:
    • Methods that return a value
    • Methods that perform an action without returning a value
  • Methods that do not return a value are often called void methods

Improving Our Class

  • In the following example we enhance our product class by adding more methods
  • In addition, we change the access modifier of the instance variables from public to private
  • The word private means that only members of this class can access the variable
  • As a rule of thumb, you should declare all instance variables private
  • We will discuss the rationale for declaring instance variables private in a later section

Example Class -- Second Attempt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Scanner;

public class Product2 {
    private String name;
    private double price;

    public void print() {
        System.out.println(name + " @ " + price);
    }

    public void read() {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the product name: ");
        name = input.nextLine();
        System.out.print("Enter the product price: ");
        price = input.nextDouble();
        input.nextLine(); // clear whitespace
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double newPrice) {
        price = newPrice;
    }
}

Test Class for Example Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Scanner;

public class ProductTest2 {
    public static void main(String[] args) {
        Product2 milk = new Product2();
        Product2 bread = new Product2();
        milk.read();
        System.out.println();
        bread.read();
        milk.print();
        bread.print();

        System.out.print("\nEnter price increase: ");
        Scanner input = new Scanner(System.in);
        double increase = input.nextDouble();
        double milkPrice = milk.getPrice();
        milk.setPrice(milkPrice + increase);
        double breadPrice = bread.getPrice();
        bread.setPrice(breadPrice + increase);
        milk.print();
        bread.print();
    }
}

4.2.2: Defining Methods

  • The general syntax for defining a method is:
    [accessModifier] returnType methodName(parameterList) {
        statements
    }
    
  • Where:
    • accessModifier: determines which classes can access this method
    • returnType: the type of value returned by the method, or void for no return value
    • methodName: the name you make up for the method
    • parameterList: the types and names of the parameters
    • statements: the commands to execute when the method is called

Access Modifier

  • The access modifier specifies which classes can use your method
  • The word public means the method can be accessed by any class
  • If we used private, then the method can only be accessed from inside the class
  • You can think of this as a privacy feature
  • If you keep a journal on the Web (a blog) then it is public and anyone can read it
  • However, if you keep a journal on your personal computer, then it is private and only you can read it
  • Use a public access modifier for methods for now
  • Later we will discuss when to use private

Return Type

  • Sometimes you will want a method to return a value
  • If you do, then you need to specify the type of data it will return
  • If you do not want a method to return a value, then you use the keyword void

Method Name

  • Technically, you can use any valid identifier for a method name
  • However, professional programmers follow naming conventions, which include:
    1. Use a name that suggests its meaning
    2. Use verbs to name methods, since they perform an action
    3. Start method names with a lower case letter
    4. Separate multiple-word names by starting each word (after the first) with a capital letter
  • Since you and I both want your code to look professional, you must follow these conventions
  • More information: The Java Language Specification: 6.8.3 Method Names

Parameter List

  • You must have parenthesis after the method name
  • Inside the parenthesis, you define a list of parameters, if any
  • Parameters are like variables except they are declared inside the method parenthesis
  • Each parameter must have both a type and a name, like a variable
  • If you have more than one parameter, you separate each one with commas
  • A parameter type can be any primitive type or class type, like a variable
  • Any parameter that you declare must be given a value when you call the method

Code Block

  • After the parenthesis, you define the block of code that you want to execute
  • The block starts with an opening curly brace: {
  • The block ends with a closing curly brace: }
  • Between the curly braces, you list all the statements you want the method to execute

More Information

4.2.3: Returning a Value

  • Recall that methods can return values and that every method has a return type
    [accessModifier] returnType methodName(parameterList) {
        statements
    }
    
  • Until now we have used void for all our method return types:
    public void print() {
        System.out.println(name + " @ " + price);
    }
    
  • However, we can declare a method that gives back a specific type to the caller, like:
    public double getPrice() {
        return price;
    }
    
  • If you declare that a method will return a value, you must return a value of the correct type
  • The way you return a value is to use a return statement
  • For example:
    return price;
  • When you call a method that returns a value, then you must save the return value or you will lose it:
    double milkPrice = milk.getPrice();
    
  • Sometimes you may just want to immediately use the returned value, like:
    System.out.println("Price: " + milk.getPrice());
    
  • If you do not need to use the returned value again, there is no need to assign it to a variable

More Information

4.2.4: Local Variables and Blocks

  • Look at the definition of the method read():
    public void read() {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the product name: ");
        name = input.nextLine();
        System.out.print("Enter the product price: ");
        price = input.nextDouble();
        input.nextLine(); // clear whitespace
    }
    
  • This method includes the declaration of the variable input
  • A variable declared inside a method is called a local variable

    Local variable: a variable that is only accessible within the block or method within which it is declared.

  • It is called a local variable because it can only be used inside the method in which it is declared
  • On the other hand, an instance variable can be used in any method of the class
  • The difference is a matter of scope:

    Scope: the region of a program that a variable or other program item is accessible.

  • Scope is delimited by curly braces { }
  • The set of curly braces is known as a block
  • If you declare a variable inside a block, that variable is local to that block
  • This means that when the block ends, all variables declared in the block disappear
  • If you declare a variable outside a block, you can use the variable either inside or outside the block
  • In the following example, the first commented variable declaration causes a compiler error (if uncommented)
    • You cannot have more than one variable declaration in a method
  • The second commented statement causes a compiler error (if uncommented) because the int i is declared in the scope of the for loop

Block Testing

1
2
3
4
5
6
7
8
9
public class BlockTester {
    public static void main(String[] args) {
        // int i;
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
        //System out.println("At end, i=" + i);
    }
}

4.2.5: Method Parameters

  • Like other programming languages, you can pass arguments into your methods
  • Here is a method with a parameter from our example class:
    public void setPrice(double newPrice) {
        price = newPrice;
    }
    
  • The parameter is double newPrice in the parenthesis of the method heading
  • Note that parameters look like variable declarations
  • That is because parameters are local variables except for one key difference
  • The difference between parameters and local variables is that parameters are initialized by arguments made in the method call:
    milk.setPrice(milkPrice + increase);
    
  • In this example, the program computes (milkPrice + increase) and passes the computed value to newPrice
  • In the setPrice() method, the newPrice parameter is initialized to the value passed from the method call
  • For example, if the value of (milkPrice + increase) is 4.79 then newPrice is initialized to the value 4.79
  • The parameter is initialized by copying the value 4.79 from the method call to newPrice
  • After being initialized, the parameter works like any other local variable
  • You can access parameter values, like:
    price = newPrice;
    
  • Also, you can assign parameter variables new values, like:
    newPrice = newPrice + 0.25;
    
  • When the method finishes executing, the newPrice value disappears

Arguments and Parameters

  • Depending on your programming background, you might use the term arguments or parameters for the values passed into methods
  • The terminology is not that important
  • However, the way I will use the terms are:
    • A method has parameters
      public void setPrice(double newPrice) {
          price = newPrice;
      }
      
    • A caller passes arguments
      milk.setPrice(milkPrice + increase);
  • Arguments are values you pass into methods
  • When the argument drops into a method, it lands into a parameter
  • The parameter is just like a local variable in the method
    • Except that a parameter gets initialized by an argument
  • The important part is:

    If a method takes a parameter, you must pass it something.

  • The something must be a value with the same type as the parameter

Coercion of Arguments

  • Note that the type of arguments must be compatible with the type of parameters
  • However, Java will automatically convert narrower argument types to broader types:
    byte => short => int => long => float => double
                    /
             char =/
    
  • For example, we could call the setPrice() method with an int argument:
    milk.setPrice(5);
    
  • Java would convert the int to a double when making the method call
  • Note that this is the same automatic type casting we discussed in lesson 2.1.6

Call-By-Value

  • In Java, all arguments are passed to parameters by value (a.k.a. call-by-value)
  • A method only gets a copy of an argument's value
  • Changes to the parameter variable inside a method do not affect the value of a calling argument
  • You can see the effects of call-by-value in the following example

Testing Call-By-Value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PassByValue {
    public static void main(String[] args) {
        int x = 2;
        PassByValue pbv = new PassByValue();
        pbv.passMethod(x);
        // print x to see if its value has changed
        System.out.println(
            "After calling passMethod, x = " + x);
    }

    // Change parameter in passMethod()
    public void passMethod(int p) {
        p = 100;
    }
}

More Information

4.2.6: Overloading Methods

  • You can have several methods with the same name in a class
  • However, each method must have a different parameter list for each method:
    • Number of parameters
    • Parameter types
    • Order of parameters
  • For example we could have all of these methods in a class
    void print(String s) {
        System.out.println(s);
    }
    
    void print(String s, int i) {
        System.out.println(s + ", " + i);
    }
    
    void print(int i, String s) {
        System.out.println(s + ", " + i);
    }
    
  • The method name and parameter list together are known as the method signature
  • Note that the return type is not part of the signature
  • The compiler cannot distinguish between two methods with the same signature but different return types
  • The following example demonstrates method overloading

Example Demonstrating Method Overloading

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class OverloadingDemo {

    public static void main(String[] args) {
        OverloadingDemo od = new OverloadingDemo();
        od.print("String only");
        od.print("String first", 7);
        od.print(7, "int first");
    }

    void print(String s) {
        System.out.println(s);
    }

    void print(String s, int i) {
        System.out.println(s + ", " + i);
    }

    void print(int i, String s) {
        System.out.println(s + ", " + i);
    }
}

4.2.7: Summary

  • Methods are a named block of code defined inside a class
  • The general syntax for defining a method is:
    [accessModifier] returnType methodName(parameterList) {
        statements
    }
    
  • Where:
    • accessModifier: determines which classes can access this method
    • returnType: the type of value returned by the method, or void for no return value
    • methodName: the name you make up for the method
    • parameterList: the types and names of the parameters
    • statements: the commands to execute when the method is called
  • The access modifier specifies which classes can use your method
    • private: can only be accessed by member methods of this class
    • public: can be accessed by member methods of any class
  • Use a public access modifier for methods for now
  • Sometimes you will want a method to return a value
  • If you do, then you need to specify the type of data it will return
  • If you do not want a method to return a value, then you use the keyword void
  • We covered the naming convention for methods that professional programmers follow
  • A variable declared inside a method is called a local variable
  • It is called a local variable because it can only be used inside the method in which it is declared
  • A parameter acts like a local variable with one important exception
  • A parameter is initialized in a method call to the value of the calling argument
  • On the other hand, a local variable is not initialized when declared
  • Instead, you must assign a local variable a value with an assignment statement
  • Another feature of Java is that you can have two methods with the same name
    • However, the parameter list must be different
    • This is known as method overloading

Check Yourself

  1. Which of the following is a method definition and which is a method call?
    1. public double getPrice() { }
    2. milk.getPrice();
  2. What statement is used to return values from methods?
  3. Can methods be declared in other methods?
  4. Why do methods sometimes need parameters?
  5. If you declare three parameters, how many arguments must you include in a method call?
  6. What are the differences between local variables and instance variables?
  7. What is meant by the term argument? parameter?
  8. What do local variables and parameter variables have in common?
  9. In which essential aspect do local variables and parameter variables differ?
  10. How many methods with the same name can you define in a class?
  11. If two methods have the same name, what must be different in each definition?

Exercise 4.2

Take one minute to prepare an answer the following questions.

  1. Which of the following are valid method declarations?
    1. methodA() { /*...*/ }
    2. void methodB { /*...*/ }
    3. void methodC() { /*...*/ }
    4. void methodD(void) { /*...*/ }

4.3: Encapsulation and Data Hiding

Learner Outcomes

At the end of the lesson the student will be able to:

  • Discuss the concept of encapsulation and information hiding
  • Design and implement accessor and mutator member methods
  • Design and implement toString() methods

4.3.1: Encapsulation and Information Hiding

  • As programs become larger, we need to change the way we work on them
  • Large programs usually have teams of people who work on them like:
    • One or two software engineers may work on the user interface
    • Another two or three may work on the logic of the application and various features
    • Another may work on data storage and retrieval
  • When many programmers work together, they need to take special care with the data
  • The data type of an application may need to change as the program develops
  • For example, we may start a program by keeping money in a variable of type double
  • Later we may decide that two int variables are a better choice (dollars and cents)
  • Any part of the program that used the double variable would have to change

The Prescription for Larger Programs

  • To make larger programs more manageable, we use classes
  • Two important features of classes are:
    • Encapsulation
    • Information hiding
  • These two concepts are closely intertwined
  • Sometimes you will see a reference to one term when the writer means both terms

Encapsulation

Encapsulation: inclusion of a number of items into a single unit

  • We saw an example of encapsulation when we looked at our first product class
  • Encapsulation allowed us to group related variables in one container (capsule)
  • If the variables in the grouping are logically related, we can more easily keep track of what is happening with the data
  • For example, we can keep all the data about a product inside one container
  • In addition to data, classes extend encapsulation to include member methods
  • Member methods are methods that work with the member variables
  • Thus, methods like print() are grouped into the class that describes a product

Information Hiding

Information hiding: hiding the parts of the design that are most likely to change (a.k.a. data hiding)

  • As programs develop, developers change them frequently
  • Even after a program is first completed and released, successful programs are updated with new features
  • With large programs, making a change in one area can cause problems in other areas
  • By hiding the parts of a program most likely to change, a programmer can make implementing changes much easier
  • The ability to change parts of a program without affecting other parts becomes more important as programs grow larger

4.3.2: Private Data

  • Recall that the instance variables of our example classes
  • In the first example we declared the variables with the modifier public:
    public String name;
    public double price;
    
  • Yet in the second example we declared the variables private:
    private String name;
    private double price;
    
  • The modifier public means there are no restrictions on accessing the variables
  • The modifier private means the variable accessed outside of the class
  • We could test this feature by writing a test like the following:
    Product2 milk = new Product2();
    milk.name = "Whole milk";
    milk.price = 4.59;
    
  • If we try to compile this code, then we get an error message
  • However, if we remove the keyword private, then the test code compiles without error
  • So why bother with private data?
  • Good software engineering practice says you should almost always declare class variables private
    • One of the exceptions is that constants may be declared public
    • There are other rare cases, but you will not run across them in this course
    • Thus, for this course, always declare non-constant instance variables private
  • The most important reason is to hide the internal implementation details of the class
  • You want to prevent programmers from relying on those details
  • This lets you safely modify your class implementation at any time without worrying that you will break code that uses the class

4.3.3: Accessor and Mutator Methods

  • You should always make all instance variables of a class private
  • However, you still may need to do something with the data of a class object

Mutator Methods

  • Recall the read() method:
    public void read() {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the product name: ");
        name = input.nextLine();
        System.out.print("Enter the product price: ");
        price = input.nextDouble();
        input.nextLine(); // clear whitespace
    }
    
  • The read() method is a mutator method - an operation that modifies the object
  • After a mutator method runs, the state of the class changes (mutates)
  • In this case, the read() method changes the values of all the member variables

Accessor Methods

  • Recall the print() method:
    public void print() {
        System.out.println(name + " @ " + price);
    }
    
  • The print() method is an accessor method
  • An accessor method queries the object for some information without changing the state of the object
  • The print() method displays information about a product without changing the state of the object

Get and Set Methods

  • Changing and retrieving a single value is a common operation
  • In these cases, the standard practice is to use the word get or set in front of the variable name
  • Thus, the name for a mutator method to change the value of price is setPrice():
    public void setPrice(double newPrice) {
        price = newPrice;
    }
    
  • The name for an accessor method to ret rive the value of price is getPrice():
    public double getPrice() {
        return price;
    }
    

More Information

4.3.4: Another Product Class

  • Let us update our product class with some new methods
  • We will add a setProduct() method to set both a name and price together
  • Also we add a toString() method, which is expected in all Java classes

Example Class -- Third Attempt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.Scanner;

public class Product3 {
    private String name;
    private double price;

    public void setProduct(String newName,
            double newPrice) {
        name = newName;
        price = newPrice;
    }

    public void print() {
        System.out.println(toString());
    }

    public void read() {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the product name: ");
        name = input.nextLine();
        System.out.print("Enter the product price: ");
        price = input.nextDouble();
        input.nextLine(); // clear whitespace
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double newPrice) {
        price = newPrice;
    }

    public String toString() {
        return "Product " + name + " @ " + price;
    }
}

Test Class for the Example Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Scanner;

public class ProductTest3 {
    public static void main(String[] args) {
        Product3 milk = new Product3();
        Product3 bread = new Product3();
        milk.setProduct("Whole milk", 4.59);
        bread.read();
        milk.print();
        bread.print();

        System.out.print("\nEnter price increase: ");
        Scanner input = new Scanner(System.in);
        double increase = input.nextDouble();
        double milkPrice = milk.getPrice();
        milk.setPrice(milkPrice + increase);
        double breadPrice = bread.getPrice();
        bread.setPrice(breadPrice + increase);
        System.out.println(milk);
        System.out.println(bread);
    }
}

4.3.5: Method toString()

  • Java expects certain methods in all classes
  • The reason is the Java standard libraries have code that assumes such methods are defined
  • One of these methods is toString()
  • If you do not supply the method then Java will supply one for you
  • Java accomplishes this through inheritance, which we will cover later
  • The default toString() method usually does not provide the behavior you want
  • Thus you should normally provide your own toString() method for a class
  • When practical, method toString() should return all the interesting information contained in the object
  • In the case of a product, we return the name and price:
    public String toString() {
        return "Product " + name + " @ " + price;
    }
    
  • Ideally the string should be self-explanatory
  • Note that when you print a class, the toString() method gets called automatically
  • Thus, the following are equivalent:
    System.out.println(milk);
    System.out.println(milk.toString());
    

4.3.6: Summary

  • OOP (object-oriented programming) provides encapsulation and information hiding for your code
  • Encapsulation and information hiding allow you to change the inner workings of a class without affecting other parts of a program
  • The ability to change portions of a program without affecting other parts becomes more important as programs grow larger
  • All code is encapsulated in one place:
    • Variables are stored in an object
    • Methods are included that work with those variables
  • In addition, you can hide the information of your program that is likely to change using access modifiers:
    • private: can only be accessed by member methods of this class
    • public: can be accessed any class and method
  • Since variables may change over time, you declare variables private
  • For this course, use private for all variables and public for most methods
  • When you need to access private data, you use accessor and mutator methods
  • An accessor method queries the object for some information without changing the state (variables) of the object
  • A mutator method modifies the state (variables) of an object
  • Java expects certain methods in all classes
  • The reason is the Java standard libraries have code that assumes such methods are defined
  • One of these methods is toString()
  • When practical, method toString() should return all the interesting information contained in the object

Check Yourself

  1. What is meant by the terms "encapsulation" and "information hiding"?
  2. What is a member method?
  3. What is the difference between an accessor and a mutator method?
  4. Should you declare get and set methods for all instance variables? Why or why not?
  5. Why is the toString() method included in most classes?
  6. What will happen if you try to compile and execute code with the following statements?
    Product milk = new Product();
    System.out.println(milk);
    

Exercise 4.3

Take one minute to find as many problems as you can in the following class:

1
2
3
4
5
6
7
8
public class Product1 {
    public String name;
    public double price;

    public void print() {
        System.out.println(name + " @ " + price);
    }
}

4.4: Constructors and Initialization

Learner Outcomes

At the end of the lesson the student will be able to:

  • Write constructors with and without parameters
  • Overload constructors
  • Construct objects from classes with overloaded constructors
  • Describe how to prevent "shadowing"

4.4.1: Introducing Constructors

  • We often want to initialize the instance variables for an object when we create the object
  • A constructor is a special type of method designed to perform such initialization
  • Although it may not be obvious, we have been using constructors
  • Constructors are called whenever we use the new operator to create an object
  • For example:
    Product milk = new Product();
    
  • The expression new Product() calls a constructor
  • If you do not write your own constructor, then Java supplies one for you
  • We have been using this automatically provided constructor up until now
  • The automatically provided constructor constructs an object but does little else
  • It is usually better to define your own constructor so you can correctly initialize variables

Defining a Constructor

  • The general syntax for defining a constructor is:
    [accessModifier] ClassName(parameterList) {
        statements
    }
    
  • Where:
    • accessModifier: determines which classes can access this method
    • ClassName: the same name as the class
    • parameterList: the types and names of the parameters
    • statements: the commands to execute when the method is called
  • For now, use the public access modifier for all class constructors
    • Other objects call the constructor when creating new objects
  • For example:
    public Product() {
        name = "Unknown";
        price = 0;
    }
    
  • Constructors must use the same name and capitalization as the class name
  • Constructors can have zero or more parameters, just like methods

4.4.2: Multiple Constructors

  • We can write a class with more than one constructor
  • All constructors have the same name as the class, but have different parameter lists
  • The technique of defining multiple constructors is known as constructor overloading
  • Having multiple constructors allows us to create an object in different ways
  • By allowing different ways of creating an object, we make our classes more flexible and easier to reuse

Example: Constructor with no Parameters

  • If you do not want to accept arguments for instance variables, code a constructor with no parameters:
    public Product() {
        name = "Unknown";
        price = 0.0;
    }
    
  • Note that constructors with no parameters are called no-arg constructors

Example: Constructor with one Parameter

  • If you want to accept only some arguments, code a constructor appropriately:
    public Product(String newName) {
        name = newName;
        price = 0;
    }
    

Example: Constructor with two Parameters

  • If you want to accept arguments for all instance variables, code a constructor with parameters for each variable:
    public Product(String newName, double newPrice) {
        name = newName;
        price = newPrice;
    }
    

Implicit Default Constructors

  • If the programmer does not define a constructor, then the compiler supplies an empty one like the following:
    public Product() {}
  • However, if the programmer defines any constructor, the compiler does not provide one
  • Best practice is to always define your own no-parameter constructor

4.4.3: Yet Another Product Class

  • Let us update our product class with some new constructors
  • We add three constructors as shown below

Example Class -- Fourth Attempt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import java.util.Scanner;

public class Product {
    private String name;
    private double price;

    public Product() {
        name = "Unknown";
        price = 0;
    }

    public Product(String newName) {
        name = newName;
        price = 0;
    }

    public Product(String newName, double newPrice) {
        name = newName;
        price = newPrice;
    }

    public void print() {
        System.out.println(toString());
    }

    public void read() {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the product name: ");
        name = input.nextLine();
        System.out.print("Enter the product price: ");
        price = input.nextDouble();
        input.nextLine(); // clear whitespace
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double newPrice) {
        price = newPrice;
    }

    public String toString() {
        return "Product " + name + " @ " + price;
    }
}

Test Class for the Example Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.Scanner;

public class ProductTest {
    public static void main(String[] args) {
        Product milk = new Product("Whole milk", 4.59);
        Product bread = new Product("Wheat bread");
        bread.setPrice(2.99);
        Product cheese = new Product();
        System.out.print("For cheese, ");
        cheese.read();
        System.out.println("\nCurrent prices:");
        milk.print();
        bread.print();
        cheese.print();

        System.out.print("\nEnter price increase: ");
        Scanner input = new Scanner(System.in);
        double increase = input.nextDouble();
        double milkPrice = milk.getPrice();
        milk.setPrice(milkPrice + increase);
        double breadPrice = bread.getPrice();
        bread.setPrice(breadPrice + increase);
        double cheesePrice = cheese.getPrice();
        cheese.setPrice(cheesePrice + increase);
        System.out.println(milk);
        System.out.println(bread);
        System.out.println(cheese);
    }
}

4.4.4: Creating Objects from Overloaded Constructors

  • If a class has more than one constructor, our program must decide which constructor to call
  • The way that C++ resolves which constructor to call is by matching the arguments to the parameter list
  • Constructor matching is done based on the number and types of the parameters
  • Also, the matching only works if the order is correct
  • For example, assume our class has the following three constructors:
    Product() { }
    Product(string newName) { }
    Product(string newName, double newPrice) { }
    
  • Creating the following objects calls the indicated constructor:
    Product milk;                     // first
    Product bread("Rye Bread");       // second
    Product cheese("Cheddar", 6.75);  // third
    
  • Note that names play no role in matching, only the number and type in the correct order

4.4.5: Default Values

  • Local variables are not initialized automatically
  • However, the compiler will initialize all instance variables to default values as follows:
    • Integer (byte, short, int, long): 0
    • Floating-point (float, double): 0.0
    • Character (char): '\u0000'
    • Boolean: false
    • Objects: null
  • Programmer can explicitly declare an initial value for instance variables
  • For example:
    private String name = "Unknown";
    private double price = 1.0;
    
  • In some cases you may be able to not code any constructors with this type of initialization
  • However, if you code any constructors you should code a no-parameter constructor

4.4.6: Local Variables, Parameters and Shadowing

  • Shadowing is when a local variable or parameter has the same name as a member variable of a class
  • Local variables and parameters with same name as a member variable hide the member variable
  • Any use of the variable's name will refer to the local variable or parameter
  • This is the source of many an elusive bug
  • You should not use the same name for a local variable or parameter and a class member variable
  • Remember that a parameter is like a local variable but initialized in a special way
  • Thus you should use a different name for a parameter and a member variable
  • Which lines contain local shadowing in the following program and how do you correct the problem?

Example of Shadowing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Shadowing {
    private double length, width;

    public Shadowing(double length, double width) {
        length = length;
        width = width;
    }

    public String toString() {
        return "length: " + length + ", width: " + width;
    }

    public static void main(String[] args) {
        Shadowing sh = new Shadowing(3, 5);
        System.out.println(sh.toString());
    }
}

What the Shadow Knows

  • We correct shadowing by making sure a parameter name is different than a member variable name
  • For example:
    public Shadowing(double newLength, double newWidth) {
        length = newLength;
        width = newWidth;
    }
    
  • In the above code, we changed the name of the parameters
  • The parameter that was length is now newLength
  • Also the parameter that was width is now newWidth

CheckStyle to the Rescue!

  • One nice feature of CheckStyle is that it catches shadowed variables:
    Shadowing.java:4:29: 'length' hides a field.
  • Be sure to run CheckStyle and pay attention to the error messages

4.4.7: Summary

  • Constructors are used to initialize all the member variables of a class
  • We can code a constructor with or without parameters
  • A constructor without parameters is known as a default constructor
  • We can use constructors with parameters to assign values to the member variables using the parameters
  • We can write a class with more than one constructor
  • If the class has more than one constructor, the constructor matching the argument list is used
  • For example, assume our class has the following three constructors:
    Product() { }
    Product(string newName) { }
    Product(string newName, double newPrice) { }
    
  • Creating the following objects calls the indicated constructor:
    Product milk;                     // first
    Product bread("Rye Bread");       // second
    Product cheese("Cheddar", 6.75);  // third
    
  • Note that names play no role in matching, only the number and type of parameters in the correct order
  • Local variables are not initialized automatically
  • However, instance variables are initialized automatically to 0 or null
  • In addition, programmers can declare initial values explicitly
  • Local variables and parameters with same name as a class variable hide the class variable
    • Known as "shadowing"
  • You should not use the same name for a local variable or parameter and a class variable

Check Yourself

  1. What is the purpose of a constructor?
  2. How many constructors can you define for each class?
  3. When does a compiler automatically include a constructor?
  4. To what values does Java initialize instance variables?
  5. What is meant by the term "shadowing"?
  6. How do you avoid "shadowing"?

Exercise 4.4

Take one minute to prepare an answer the following question:

  1. Given a class named Timer, which of the following is a valid declaration of a constructor:
    1. void Timer() {}
    2. Timer Timer() {}
    3. Timer(Timer t) {}
    4. public static void Timer(String args[]) {}

Wrap Up

Due Next:
A3-Postal Bar Codes (3/4/09)
A4-Shape Classes (3/11/09)

Home | Blackboard | Announcements | Schedule | Room Policies | Course Info
Help | FAQ's | HowTo's | Links
Last Updated: March 02 2009 @11:26:04