Instance Variables

Things To Do First

Things To Do Later

Modifiers: public and private

In Java, there are a set of access modifiers. You can find a complete list of these modifiers and their meanings and use in the Java Reference material on the Access Modifiers page. An access modifier defines the accessibility of a class, method, or data member. In other words, the access modifier will indicate where a programmer can reference or refer to a particular class, method, or data member.

For example, the main() method in a Java program is defined as public. This means that the main method may be accessed (called, invoked) anywhere in the program's code, and also anywhere in another program's code.

public Classes, Methods, and Instance Variables

A class that is defined as "public" may be instantiated by any other class. Applets must be defined as public classes because they have to be instantiated by a user's browser. A class can be defined with no modifier, such as this one:

class MyMessage
{
	// ... class code goes here ...
}

This class is not available to all other classes; it is only available to classes in the same package. We refer to this as default, friendly, or package access. For our use, this means that this class is available only to classes in the same folder. This is technically slightly incorrect, but we'll talk about this when we learn to create our own packages later on.

A method that is defined as public can be invoked or called by any other code in the class or program where the method is defined, or even from other classes and programs. For example, a class MyMessage has a public method called "displayMessage()":

public class MyMessage
{
	public String message;
	public void displayMessage()
	{
		System.out.println(message);
	}
}

This displayMessage() method can be accessed by another Java class or program:

public class TestMyMessage
{
	public static void main(String[] args)
	{
		MyMessage hello = new MyMessage();
		hello.message = "Hello, World!";
		displayMessage();
	}
}

An instance variable defined as public can be read or written to by any other class or program. This is usually an undesirable situation, as we'll learn in the next couple of sessions, however the previous example demonstrates the use of a public variable. The MyMessage class has a public variable called "message". We can assign a value to this variable (as we did in the statement hello message = "Hello, World!";) and we can also read the value in the variable and use it (as we could do in a statement such as String newMessage = "kaluha! " + hello.message;).

Note
For those of you with a programming background, a public instance variable bears no resemblance to a "global" variable that is accessible and retains its value throughout the program in which it is defined. In a Java program, you can create "global" variables by declaring them at the top of the program, under the class header, but you would declare them as "private" as we'll see later.

private Methods and Instance Variables

Notice that we did not include classes in this sub-heading! That's because classes are generally not defined as private! In fact, a class can only use the "public" access modifier unless it's an inner class. You'll learn about inner classes in the second course.

A method that is defined as private in a class or program is only available inside that class or program. For example, create the following class and program:

public class Book {
	
    public String bookTitle;
    public double bookPrice;

    private void displayBook() {
        System.out.println(bookTitle + ": $" + bookPrice);
    }
}
public class TestBook {
	
    public static void main(String[] args) {

        Book javaText = new Book();
        javaText.bookTitle = "OOP Develpment Using Java";
        javaText.bookPrice = 122.95;
        javaText.displayBook();
    }
}

When you compile both programs, you'll get the following error:

TestBook.java:17: displayBook() has private access in Book
                javaText.displayBook();
                        ^

This error is telling you that you can't invoke the javaText.displayBook() method because it's defined as private in the Book class. You can't access a private method from outside the class where it is defined.

Why would you want to define a private method in a class? Later we'll talk about encapsulation, which is the main reason behind private members of classes and programs. Simply, a private method is usually a "helper" method that performs a task to help out the tasks performed in other methods in the same class or program. The following scenario should help understand this:

Say you are asked to write a program that makes use of an Employee class that another programmer has already written. Your program prints the pay checks for the employees in the company. You read the documentation for the Employee class and see that there are some data members and some methods that are going to be useful to you. One of them is a method that prints a pay check.

This printPayCheck() method accesses the employee database and gathers the necessary information, then calculates all the fields necessary for a pay check, such as federal tax deductions, union dues, pension plan deductions, gross pay, and net pay. This printPayCheck() method is probably made up of many code statements that call other methods, such as the method to calculate the federal tax deduction. As a programmer using the Employee class, you don't need to know about or even care about those methods; you are only interested in the public method that prints a pay check. The little methods that "help" the printPayCheck() method are of no use to other programmers, only to the printPayCheck() method inside the Employee class. Therefore, those "helper" methods will be defined as private, so they can't be accessed outside the Employee class.

As we'll see later with instance variables, members of a class are often defined as private in order to protect instances of the class from being maliciously or accidentally altered in some way.

Private instance variables work the same way as private methods: they are only accessible inside the class and are not available to code in other classes or programs. You might wonder, why on earth would we add a data member to a class if it's not to be accessed by another program or class? In fact, this is a standard practice! In order to protect a piece of data from being given an incorrect value, we often make the data member private, and then we provide an "accessor method" for that data member. The accessor method will accept the value you want to put into the data member, make sure it's valid, and place the valid value in the data member itself. We'll learn about accessor methods in a later session.

Instance Variables

When we define the data members (also called properties or attributes) for a class, we define instance variables. These are not the same as the variables we have been defining in a program's main() method; those variables were local variables. A few of the significant differences between local variables and instance variables are:

Instance VariablesLocal Variables
  • Represent data members for a class.
  • Are used to temporarily hold values (such as user inputs, or results of calculations) inside a method.
  • Can be defined with modifiers such as public and private.
  • Are not defined with modifiers because they are only accessible inside the the method in which they are defined.
  • Do not need to be initialized.
  • Must be initialized or assigned a value before they are used in an expression.
  • Should be defined all together at the top of the class, under the class header.
  • Can technically be declared anywhere in the method, although sometimes we prefer to declare them all together at the top of the method under the method header.

Local variables can be thought of as "working" variables; we use them to hold data values temporarily until we are ready to use them later in our code. Instance variables are the pieces of data that contribute to the definition of a particular object; they are part of our class "recipe" or template.

Protecting Instance Variables

Instance variables should always be defined as private. This allows you to protect the instance variables from being given invalid data, and allows you to add code for exception and error handling in the event that a variable is given bad data by another programmer.

One problem you might realize can be demonstrated with the following modification of the Message class:

public class Message {

    private String message;

    public void display() {
        System.out.println(message);
    }
}

We've made the message instance variable a private variable. Now try to compile the test class:

public class TestMessage {

    public static void main(String[] args) {

        Message greeting = new Message();
        greeting.message = "Hello, World!";
        greeting.display();
	}
}

When you compile the test class, you get the following error:

TestMessage.java:13: message has private access in Message
                greeting.message = "Hello, World!";
                        ^
1 error

You can't access a private data member (or method, even) from outside its class. Here, we are trying to access the message instance variable, but it's defined as private in the Message class. This means it's not available outside the Message class.

Ideally, you would add code to your class to allow limited access to private instance variables. For example, if you had an Account class, you wouldn't want just anyone being allowed to change the balance instance variable, and you certainly wouldn't want someone to give negative balances to account objects! In addition, you wouldn't want just any object having access to certain data. For example, the Customer object shouldn't be able to read other Customer object's balances, only their own. So how can we protect our instance variables from receiving invalid values, and how can we protect them from being read by other objects? In object-oriented programming, we use accessors and mutators.

Accessor and Mutator Methods

As previously discussed, in a well-designed class, the programmer should keep the instance variables private, so that access to them is limited. Recall that private instance variables can't be accessed outside the class in which they are defined; in other words, no other class or program can read or change the value of a private instance variable. We need to make instance variables private in order to protect them from receiving invalid data. One problem with this of course is that other classes and programs can't access the variables even for legitimate purposes. For this reason, we provide accessor and mutator methods as gateways or filters to the private instance variables.

Code the following test program for your Room class, then compile them both, and run the program:

public class TestRoom {
	
    public static void main(String[] args) {

        Room office = new Room();
        office.length = 16.5;
        office.width = 20;
        System.out.println("Area: " + office.calcArea());
	}
}

When we run the program, we get the following error messages:

TestRoom.java:15: length has private access in Room
                classroom.length = 16.5;
                         ^
TestRoom.java:16: width has private access in Room
                classroom.width = 20;
                         ^
2 errors

Press any key to continue...

We've discussed this kind of error before: You can't access the private instance variables length and width ouside of the Room class. So how do we give them values? How do we retrieve those values later if we want to display them? This is where accessor and mutator methods come in.

Mutator Methods

Mutator methods are special methods that put values into private instance variables. Many mutator methods will also validate the data going into a variable, for example when we made the Circle class, we decided that if a radius value was 0 or less, we would use a default value of 1 instead. If you were writing an employee class, you might want to make sure that an employee object's rate of pay was always greater than 0 and never greater than 100.00. In our Room class, we might decide that 0 or negative values are invalid for the length and width instance variables. We can program our mutator methods to check the values first, then either assign them if they're valid, or use a default if they're not.

There are rules you must follow when defining a mutator method for a class. Some of these are stylistic rules but a few are syntax rules:

  • There must be one mutator method for each private instance variable in your class if you want to provide write-access to that variable by other classes/programs.
    • To create a read-only instance variable, simply don't define a mutator method! Without a mutator method, no other class or program can change the value of an instance variable.
  • A mutator method always accepts one argument, for which there must be a parameter variable in the method's header.
    • The parameter variable's data type must match the data type of the instance variable for this mutator.
  • A mutator method always returns type void.
  • A mutator method's name always starts with "set" (lower-case) and then the name of the instance variable, starting with a capital letter.
    • This is why mutators are sometimes called "set methods".
    • Examples:
      • instance variable: price -- mutator method: setPrice()
      • instance variable: radius -- mutator method: setRadius()
      • instance variable: firstName -- mutator method: setFirstName()
  • Mutators should be public. They must be accessible to other classes and programs.
Note
Some programmers use the terms "accessor methods" or "accessors" refer to both mutator methods and accessor methods! This is because both kinds of methods allow access to private instance variables, regardless of whether that's write access or read access. Be careful if you hear the term "accessor methods" - in many cases, this is referring to both kinds of methods. Make sure you understand the context in which this term is used, so you know whether the reference is to both kinds of methods, or only the "set" methods.

Let's create to very basic mutator methods for the Room class that set the values of length and width, but only if the programmer-specified values are positive. If the parameter is 0 or negative, use a default value of 1:

public void setLength(double length)
{
	if (length > 0)
		this.length = length;
	else
		this.length = 1;
}

public void setWidth(double width)
{
	if (width > 0)
		this.width = width;
	else
		this.width = 1;
}

To use these methods, you simply call them as you would any other method:

Room classroom = new Room();
classroom.setLength(16.5);
classroom.setWidth(20);
classroom.displayDimensions();

Try it with invalid values for length and/or width and see what output you get.

Accessor Methods

Accessor methods are similar to mutator methods, except that instead of setting the values of instance variables, accessor methods allow another class or program to read the values of instance variables. An accessor method returns the value of a particular instance variable, so as with mutators, there are a set of rules that need to be followed:

  • There must be one accessor method for each private instance variable in your class if you want to provide read access to that variable by other classes/programs.
  • An accessor method accepts no arguments, so it does not require any parameter variables.
  • An accessor method always returns a data type, never void.
    • The return data type must match the data type of the instance variable for this accessor.
  • An accessor method must always return the value of its instance variable using the return statement.
  • An accessor method's name always starts with "get" (lower-case) and then the name of the instance variable, starting with a captial letter.
    • This is why accessors are sometimes called "get methods".
    • Examples:
      • instance variable: price -- accessor method: getPrice()
      • instance variable: radius -- accessor method: getRadius()
      • instance variable: firstName -- accessor method: getFirstName()
  • Accessors should be public. They must be accessible to other classes and programs.

The accessor methods for our Room class would be defined as:

public double getLength()
{
	return length;
}

public double getWidth()
{
	return width;
}

Exercises

[solutions]

1. Add code to your TestRoom program that uses the class's accessor methods: calculate and display what the volume of the room would be if the height of the room was 15.

2. Modify the Circle.java class from the previous session: Ensure that the radius data member is private. Define acessor and mutator methods for the radius variable. A radius of a circle should never be 0 or less. Your new class should have the following structure:

Class: Circle
- radius : double
+ Circle()
+ Circle(radius: double)
+ getRadius() : double
+ setRadius(radius : double) : void
+ calcArea() : double
+ calcCircumference() : double
+ toString() : String

Write a test program that tests out your Circle class:

3. Modify the Time.java class from the previous session. Ensure that all three instance variables are private. Define accessor and mutator methods for each instance variable. The hours should be between 0 and 23 inclusive, and the minutes and seconds should be between 0 and 59, inclusive.

Your class should now have the following structure:

Class: Time
Data Members:
- hours : int
- minutes : int
- seconds : int
Methods:
+ Time()
+ Time(hours : int, minutes : int, seconds : int)
+ getHours() : int
+ getMinutes() : int
+ getSeconds() : int
+ setHours(hours : int) : void
+ setMinutes(minutes : int) : void
+ setSeconds(seconds : int) : void
+ calcTotalSeconds() : int
+ toString() : String

Write a program that uses the Time class:

4. Modify the Dice class from the previous session. Add accessor methods only to the class.

Your class should now have the following structure:

Class: Dice
Data Members:
- firstDie : int
- secondDie : int
Methods:
+ Dice()
+ getFirstDie() : int
+ getSecondDie() : int
+ tossDice() : void
+ sum() : int
+ toString() : String

Why are there no mutator methods in the Dice class? Because the dice get their values from tossing, not by allowing a programmer to choose and set the dice values. The tossDice() method generates two random numbers from 1 to 6 inclusive and assigns them to the two instance variables, so we won't be needing methods to set the dice values.

Write a program that tests your Dice class: