Welcome to Lesson 8!


Learning Objectives for Today's Lesson

By the end of today's lesson, you should know...
  • What is a constructor?
  • How do you use method overloading with constructors?
  • What is a default constructor?
  • The differences between primitive and reference types
  • How to use objects as method parameters and return types
  • What is the difference between heap and stack memory?
    • Where are objects stored?
    • Where are reference variables and primitives stored?



Bike b = new Bike();
1. Constructors and Initialization

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:
    Phone iphone = new Phone();
    
  • The expression new Phone() 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 Phone() {
    brand = "Unknown brand";
    model = "Unknown model";
    price = 0.0;
}
  • Constructors must use the same name and capitalization as the class name
  • Constructors can have zero or more parameters, just like other methods
  • Constructors also have no return type (not even void!)

Example of Phone Class with Constructor

/**
 * Phone.java example
 * @author parrishj
 *
 */

public class Phone {
    private String brand;
    private String model;
    private double price;
   
    /**Constructor(s)*/
   
    /**
     * Creates a new Phone object
     * with default values
     */
    public Phone() {
        brand = "Unknown brand";
        model = "Unknown model";
        price = 0.0;
    }

   
    /**Accessors*/

    /**
     * Returns the brand of phone
     * e.g. iPhone or Samsung
     * @return the brand
     */
    public String getBrand() {
        return brand;
    }
   
    /**
     * Returns the phone's model
     * e.g. 7 or Galaxy S5
     * @return the model
     */
    public String getModel() {
        return model;
    }
   
    /**
     * Returns the phone's current
     * market price
     * @return the price
     */
    public double getPrice() {
        return price;
    }
   
    /**Mutators*/

    /**
     * Assigns a brand to the phone
     * e.g. iPhone or Samsung
     * @param newBrand the brand of phone
     */
    public void setBrand(String newBrand) {
        brand = newBrand;
    }
   
    /**
     * Assigns a model to the phone
     * e.g. 7 or Galaxy S5
     * @param newModel the phone's model
     */
    public void setModel(String newModel) {
        model = newModel;
    }

    /**
     * Assigns the phone the current
     * market price for that brand
     * and model
     * @param newPrice the market price
     */
    public void setPrice(double newPrice) {
        price = newPrice;
    }
   
    /**Additional Methods*/

    /**
     * Displays a message alerting the user
     * that the phone is now making a call
     */
    public void makeCall() {
        System.out.println(brand + " is now making a call.");
    }
   
    /**
     * Displays a message alerting the user
     * that the phone is now making a call
     * to a specified person
     * @param name the person who is being called
     */
    public void makeCall(String name) {
        System.out.println(brand + " is now calling " + name);
    }
   
    /**
     * Returns a String of all the important
     * information about the phone, including
     * brand, model and price
     * @return the important phone information
     */
    @Override public String toString() {
        return "Brand: " + brand 
            + "\nModel: " + model
            + "\nPrice: " + price;
    }
}


public class PhoneTest {
    public static void main(String[] args) {
        Phone iphone = new Phone(); //calls our constructor
        System.out.println(iphone); //calls toString()
    }
}

Multiple Constructors

  • We can write a class with more than one constructor
  • In other words, we can overload the constructor by defining multiple versions of it in a single class
  • 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
  • A good constructor will assign values to each member variable of the class.
  • Below are examples of well-written constructors:

Example: Constructor with no Parameters

  • If you do not want to accept arguments for instance variables, write a constructor with no parameters:
  • Note that constructors with no parameters are sometimes called default constructors
  • A default constructor sets instance variables to default values
public Phone() {
    brand = "Unknown brand";
    model = "Unknown model";
    price = 0.0;
}

Example: Constructor with one Parameter

  • If you want to accept only some arguments, write a constructor with at least one parameter.
  • The remaining values can always be set later by calling the setter methods
    public Phone(String theBrand) {
    brand = theBrand;

    model = "Unknown model";
    price = 0.0;
    }
  • Alternately, 
public Phone(String theBrand, String theModel) {
    brand = theBrand;
    model = theModel;
    price = 0.0;
}

Example: Constructor with two Parameters

  • If you want to accept arguments for all instance variables, write a constructor with parameters for each variable:
    public Phone(String theBrand, String theModel, double thePrice) {
    brand = theBrand;
    model = theModel;
    price = thePrice;
    }

Implicit Default Constructors

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

Example Phone Class with Overloaded Constructors

/**
 * Phone.java example
 * @author parrishj
 *
 */

public class Phone {
    public String brand;
    public String model;
    public double price;
   
    /**Constructor(s)*/
   
    /**
     * Creates a new Phone object
     * with default values
     */
    public Phone() {
        brand = "Unknown brand";
        model = "Unknown model";
        price = 0.0;
    }
   
    /**
     * Creates a new Phone object
     * with default values, except
     * for the brand
     * @param theBrand the brand of
     * the phone
     * e.g. iPhone or Samsung
     */
    public Phone(String theBrand) {
        brand = theBrand;
        model = "Unknown model";
        price = 0.0;
    }
   
    /**
     * Creates a new Phone object
     * @param theBrand the brand of
     * the phone e.g. iPhone or Samsung
     * @param theModel the phone's
     * model. e.g. 7 or Galaxy S7
     * @param price the phone's price
     * in dollars
     */
    public Phone(String theBrand, String theModel, double thePrice) {
        brand = theBrand;
        model = theModel;
        price = thePrice;
    }
   
    /**Accessors*/

    /**
     * Returns the brand of phone
     * e.g. iPhone or Samsung
     * @return the brand
     */
    public String getBrand() {
        return brand;
    }
   
    /**
     * Returns the phone's model
     * e.g. 7 or Galaxy
     * @return the model
     */
    public String getModel() {
        return model;
    }
   
    /**
     * Returns the phone's current
     * market price
     * @return the price
     */
    public double getPrice() {
        return price;
    }
   
    /**Mutators*/

    /**
     * Assigns a brand to the phone
     * e.g. iPhone or Samsung
     * @param newBrand the brand of phone
     */
    public void setBrand(String newBrand) {
        brand = newBrand;
    }
   
    /**
     * Assigns a model to the phone
     * e.g. 7 or Galaxy
     * @param newModel the phone's model
     */
    public void setModel(String newModel) {
        model = newModel;
    }

    /**
     * Assigns the phone the current
     * market price for that brand
     * and model
     * @param newPrice the market price
     */
    public void setPrice(double newPrice) {
        price = newPrice;
    }
   
    /**Additional Methods*/

    /**
     * Displays a message alerting the user
     * that the phone is now making a call
     */
    public void makeCall() {
        System.out.println(brand + " is now making a call.");
    }
   
    /**
     * Displays a message alerting the user
     * that the phone is now making a call
     * to a specified person
     * @param name the person who is being called
     */
    public void makeCall(String name) {
        System.out.println(brand + " is now calling " + name);
    }
   
    /**
     * Returns a String of all the important
     * information about the phone, including
     * brand, model and price
     * @return the important phone information
     */
    @Override public String toString() {
        return "Brand: " + brand 
            + "\nModel: " + model
            + "\nPrice: " + price;
    }
}

Defining Objects from Overloaded Constructors

  • If a class has more than one constructor, our program must decide which constructor to call
  • The way that Java 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:
public Phone() {...}
public Phone(String theBrand) {...}
public Phone(String theBrand, String theModel, double thePrice) {...}
  • Creating the following objects calls the indicated constructor
Phone phone1 = new Phone();
Phone phone2 = new Phone("Samsung");
Phone phone3 = new Phone("Samsung", "Galaxy S5", 139.99);
  • Note that names play no role in matching, only the number and type in the correct order

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
  • A 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

Activity 8.1: A Person Class with Overloaded Constructor (10 pts)

  • Open up your Person.java class from the last activity.
  • Now, add two constructors to your class as specified below:
  • The first constructor is a default constructor
    • It takes in no parameters
    • It sets the the name field to have a default value of "name unknown"
    • It sets the age field to have a default value of 0
    • It sets the gender field to have a default value of "gender unknown"
  • The second constructor overloads the first
    • It takes in 3 parameters - a String for the name, an int for the age, a String for the gender
    • It assigns name, age and gender the values passed in as parameters
  • Copy and paste the new test file below into PersonTest.java (you can erase the old contents of the file):
/**
* PersonTest.java
* @author
* CIS 36BActivity 8.1
*/

import java.util.Scanner;

public class PersonTest {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String name, gender;
        int age;
        Person person;
     
        System.out.print("Welcome!\n\nEnter your name: ");
        name = input.nextLine();
       
        System.out.print("Enter your age: ");
        age = input.nextInt();
       
        System.out.print("Enter your gender: ");
        gender = input.next();
       
        person = //call 3-argument constructor here
       
        System.out.println("\nYour Summary:\n" + person);
    }
}
  • When you get the correct output (as shown below), upload Person.java and PersonTest.java to Canvas
Sample Output (Note that user input may vary):

Welcome!

Enter your name: Bao Li
Enter your age: 29
Enter your gender: Male

Your Summary:
Name: Bao Li
Age: 29
Gender: Male

 

2. Working with Objects and References

Reference Types

  • Data types in Java are divided into two categories: primitive and reference types
  • All nonprimitive (that is class) types are reference types
data types in Java - 8 primitive types as well as reference types

  • Programs use variables of a reference type to store information about where to find an object in the computer's memory
    • Known as indirect addressing
  • Such reference variables are said to "refer to" objects in the program
  • For example, recall how we create a Scanner in a program:
    Scanner input = new Scanner(System.in);
    
  • In this example, input is a reference to an object of type Scanner
  • The reference variable input contains information on where to find the Scanner object
  • You can see the relationship between a reference variable and an object in the following diagram:
reference variables refer to the object

  • You need the reference to the object in order to call its methods
    int myInt = input.nextInt();
    
  • In this example, we use the reference variable input to refer to the Scanner object

Address Analogy

  • By analogy, a reference variable is like the address of a house
  • The address is a small identifier from which we can find a much larger object
  • One of the advantages of addresses is that they are much easier to work with than actual houses
  • For instance, if we want to locate people on a street by name, we could rearrange all the houses in alphabetical order by name
A plaque that states 1239 Dakota Street The Cheng Family

  • An easier way is to make a list of last names with their addresses
  • The address is the reference to the house object
  • We get the same benefits when programming: it is often easier to work with references to objects rather than actual objects
  • Other examples of references abound in real life: telephone numbers, e-mail addresses, social-security numbers, etc.
  • Each reference type refers to and makes it easier to access a resource

image source


Initial State

  • You can initialize reference types to the value null
    Scanner input = null;
  • null is a keyword that means a "nothing" or "no value"
  • A reference variable can only have one of two possible values: null or a memory address
  • We can see this memory address when we print an Object that does not have a toString method defined
  • If the reference type is a class variable, as opposed to a local variable, then Java initializes it to null by default

References vs. Pointers

  • Pointers are the most primitive and error-prone reference type
  • They simply store a memory address
  • In C/C++, you can actually manipulate pointers with arithmetic or even by assigning an arbitrary value
  • A reference type in Java is like a pointer but without direct manipulation
  • With references, you get the main benefits of a pointer but without many of the errors
  • With fewer errors, you get more bug-free programs
  • Also, you get additional features like automatic de-referencing and garbage collection

Heap and Stack Memory

  • Java uses two types of memory - heap and stack memory
  • Stack memory stores local variables (primitives and reference variables) and information about each method call.
    • Throughout the execution of a program, the amount of stack memory currently in use grows and shrinks as methods are called and methods return (end).
  • Heap memory stores objects.
    • All of the data about the object.
  • Reference variables in the stack memory point to objects on the heap
    • Reference variables simply store the address of the object in the heap
    • An object is shared by all reference variables to this object
  • Once an object no longer has a reference variable referencing it in the stack memory, it is removed by the automatic garbage collection.

Stack vs heap memory
image source

Heap vs stack memory

  • Note: we will talk about the String pool later this quarter


Group Activity - How Many Distinct Objects Are Created In the Below Program?


Student s1 = new Student("Jackie Chan", 3.5);
Student s2 = s1;
s2 = new Student("Sandra Avila", 4.0);
Student s3 = new Student("Basil Awad", 3.9);
s3 = new Student("Pham Van Duc", 3.75);
s2 = s3;
Student s4 = s1;


Objects as Method Parameters

  • Java passes the value stored in a variable the same way for both primitive types and reference types
  • When a method is called, the value of each argument is copied to its corresponding parameter
  • Thus an argument's value can never be changed within a method
  • However, copying a reference value to a parameter has certain implications
  • The method gets access to the exact same object because it has a reference to the object
  • You can see this situation in the following diagram:
Methods calling reference variables receive a copy of the memory address

  • Since the method has been provided with the memory address of the object, the method can directly change the object.
    • Compare to calling a method on a primitive: When calling a method with a primitive parameter, can the method change the value of the original primitive variable?
  • Also, you can see the effects of passing a reference in the following program

Passing an Object to a Method

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
public class PassObject {
    public static void main(String[] args) {
       
        Coffee java = new Coffee("Expresso");
        System.out.println("In main(): "
            + java.getFlavor());
        changeData(java, "Latte");
        System.out.println("In main(): "
            + java.getFlavor());
    }

    public static void changeData(Coffee cup, String flavor) {
        System.out.println("Changing the flavor: "
            + cup.getFlavor());
        cup.setFlavor(flavor);
    }
}

class Coffee {
    private String flavor;

    public Coffee(String newFlavor) {
        flavor = newFlavor;
    }

    public String getFlavor() {
        return flavor;
    }

    public void setFlavor(String newFlavor) {
        flavor = newFlavor;
    }
}

Returning an Object

  • Methods can return a reference to an object
  • You can see how a reference is returned in the following code

Returning an Object from a Method

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
public class ReturnObject {
    public static void main(String[] args) {
        Coffee java = brew("Latte");
            System.out.println("In main() I got: "
            + java.getFlavor());
        System.out.println("Thank you!");
    }

    public static Coffee brew(String flavor) {
        System.out.println("Brewing coffee");
        Coffee cup = new Coffee(flavor);
        return cup;
    }
}

class Coffee {
    private String flavor;

    public Coffee(String newFlavor) {
        flavor = newFlavor;
    }

    public String getFlavor() {
        return flavor;
    }

    public void setFlavor(String newFlavor) {
        flavor = newFlavor;
    }
}


Activity 8.2: Personal Address (10 pts)

  • Open up Person.java class from the last activity.
  • Let's make some changes to this class.
  • First, we are going to add a new class called Address to interact with the Person class.
  • Add a new file to the folder where Person and PersonTest are stored. Name this file Address.java
public class Address {
    private int number;
    private String street;

         /**
     * Default constructor for the Address class
     * Initializes number to 0 and street to
     * "Street unknown"
     */

            public Address() {
        //write default constructor here
    }

    /**
     * Constructor for the Address class
     * @param number the street number
     * @param street the name of the street
     */
    public Address(int theNumber, String theStreet) {
        //write two-argument constructor here
    }

    /**
     * Returns the street number portion
     * of the address
     * @return the street number
     */
    public int getNumber() {
        return number;
    }

    /**
     * Returns the name portion of the
     * address
     * @return the name of the street
     */
    public String getStreet() {
        return street;
    }

            //add setters here

    /**
    * Displays an address in the format number + " " + street;
    * @return a String containing the address
    */
    //Write a toString() method for Address here!
}
  • Next, open your Person.java class.
  • Add an additional member variable:
private Address address;
  • Also alter your multiple argument constructor to pass in an address variable:  

    public Person(... Address newAddress)

  • Update this constructor:
    • Assign a value to the new member variable address
  • Additionally, update your default constructor to assign address a value of new Address(). In other words, you will call the Address default constructor to initialize the address member variable to have default values.
  • Next, add a getter and setter method for the address variable to your Person class.
public Address getAddress() {
    //fill in method body here
}

public void setAddress(Address a) {
    //fill in method body here
}
  • Finally, alter your toString method for Person as follows:
    @Override public String toString() {
        return "Name: " + name +
                "\nAge: " + age +
                "\nGender: " + gender +
                "\nAddress: " + //fill in here!;
    }

  • Copy and paste the new test file below into PersonTest.java (you can erase the old contents of the file), and update the incomplete line.
/**
* PersonTest.java
* @author
* CIS 36B, Activity 8.2
*/

import java.util.Scanner;

public class PersonTest {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String name, gender, street;
        int age, number;
        Person person1;
     
        System.out.print("Welcome!\n\nEnter your name: ");
        name = input.nextLine();
       
        System.out.print("Enter your age: ");
        age = input.nextInt();
       
        System.out.print("Enter your gender: ");
        gender = input.next();
        
        System.out.print("Enter the street number of your address: ");
        number = input.nextInt();
       
        System.out.print("Enter the name of your street: ");
        input.nextLine();
        street = input.nextLine();
        
        Address address = //call Address constructor here
       
        person1 = //call Person constructor here
       
        System.out.println("\nYour Summary:\n" + person1);
    
        input.close();
   
    }
}
  • When you get the correct output (as shown below), upload Address.java, Person.java and PersonTest.java to Canvas
Sample Output (Note that user input may vary):

Welcome!

Enter your name: Wen Li
Enter your age: 23
Enter your gender: Male
Enter the street number of your address: 2141
Enter the name of your street: W. Elm Ave

Your Summary:
Name: Wen Li
Age: 23
Gender: Male
Address: 2141 W. Elm Ave



Wrap Up: 

  • Answer the practice exam questions on Canvas from this lesson.


Upcoming Assignments:

  • Activities 8.1 and 8.2 due Thursday at 11:59pm
  • Lesson 8 Practice Exam Questions due Thursday at 11:59pm
  • Quiz 4 due Friday at 11:59pm
  • Peer Reviews of Lesson 7 and 8 Practice Exam Questions due Saturday at 11:59pm
  • Lab 4 due Monday at 11:59pm

~ Have a Great Weekend! ~