Methods/EncapsulationJ8 Home « Methods/Encapsulation

Lets take a look at the points outlined at Oracle Java SE 8 Programmer I for this part of the certification.

  • Working with Methods and Encapsulation
    1. Create methods with arguments and return values; including overloaded methods.
    2. Apply the static keyword to methods and field.
    3. Create and overload constructors; differentiate between default and user defined constructors.
    4. Apply access modifiers.
    5. Apply encapsulation principles to a class.
    6. Determine the effect upon object references and primitive values when they are passed into methods that change the values.

The table below breaks the above list down into topics with a link to the topic in question.

topic Link
Create methods with arguments and return values; including overloaded methods.Method Creation
Overloading Methods
Apply the static keyword to methods and fieldStatic Methods
Static Variables
Create and overload constructorsConstructors
Overloaded Constructors
Differentiate between default and user defined constructors
Apply access modifiers
Apply encapsulation principles to a class
Determine the effect upon object references and primitive values when they are passed into methods that change the values

Method CreationTop

There are two types of methods available in Java, static and non-static methods. The general mechanics are the same but static methods apply to the class as a whole, whereas non-static methods generally apply to an instance of the class. We will discuss the vagaries of static methods in the Static Methods later in this lesson.Lets start by looking at the components of a method:

method parts diagram

Method declarations can have up to six components but the only required elements are the method's return type, the method name, a pair of parenthesis () and a body between braces {}. Following is the order the components must appear in when used.

  1. Modifiers — such as public as used here are discussed in the Encapsulation lesson along with the other access modifiers and static which we will discuss later in this section in the Static Members lesson. There are also other modifiers which we will learn about later.
  2. The return type — which is the data type of the value returned by the method, or void if the method doesn't return any value.
  3. The method name — which must be a valid identifier as discussed in the Java Variables section of the Primitive Variables lesson. By convention, method names should be a verb in lowercase or multiple words that begin with a verb in lowercase, followed by adjectives, nouns, etc. in camel case.
  4. The parameter list - A comma-delimited list of input parameters, preceded by their data types, enclosed by parentheses. If there are no parameters, you must use empty parentheses. The arguments passed when we call a method are received into the parameter list are within scope as discussed in the Defining A Scope section of the Method Scope lesson.
  5. An exception list — Which will be discussed when we get to look at exceptions.
  6. The method body - The method's code, statements, local variable declaration etc, goes in the body which is enclosed between braces.

Passing Arguments To A Method Top

Before we look at this lets clear up some terminology about arguments and parameters. Arguments are what we pass to a method whereas parameters are what we receive the arguments into. We have already seen with our use of the main() method how to pass an argument to a method and we also coded our own printBits method in the Bitwise Operators lesson. Both these methods accept a single argument into their parameter list, but Java has no restriction on the amount of parameters we can code into a method. Lets code a Tiger class with a new method that accepts two parameters:


package info.java8;
/*
  A Tiger Class
*/ 
public class Tiger {
    String name, colour;
    int age;

    void tigerMannerisms(String action, int times) {  // Our new method with 2 parameters
        System.out.println("Our tiger " + action + " " + times + " times a day.");
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way. Lets write a new test class for our Tiger class.


package info.java8;
/*
  Test Class for Tiger
*/ 
public class TigerTest {

    public static void main (String[] args) {
        Tiger tony = new Tiger();  // Create a Tiger instance
        tony.tigerMannerisms("eats food", 4);  // Pass values
        String tiggerAction = "runs up trees"; 
        int tiggerTimes = 2; 
        tony.tigerMannerisms(tiggerAction , tiggerTimes );  // Pass types
        tony.tigerMannerisms(tony.name, tony.age );  // Pass instance variables
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run tiger

The above screenshot shows the output of running our Tiger class. We created a Tiger instance and passed two arguments to the tigerMannerisms method in various ways.

Returning Values From A Method Top

Up until now all our methods have been declared using the void return type which denotes that the method doesn't return any value. We can declare methods with a return type and if we do we must return a value compatible with that type or covariant thereof, using the return keyword. Lets see this in action :


package info.java8;
/*
  Test class for maths stuff
*/ 
public class MathsStuff {

    public static void main (String[] args) {
        int aSquare = squareNumber(5);   // The return value will go into aSquare
        System.out.println(aSquare);
    }
    
    /*
        A method that squares and returns the passed integer
    */ 
    static int squareNumber(int number) {  
        int square = number * number;  
        return square;  // Here we use the return keyword to pass back a value
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run square

The above screenshot shows the output of running our MathsStuff class. The return value from the squareNumber() method gets assigned to the aSquare primitive.

Overloading MethodsTop

You can use the same method name more than once in a class or subclass and this is known as overloading. When using overloading each method must have different parameter lists so the compiler knows which method to call. Having the same parameter types is fine as long as the order differs.

Overloading Method Rules

When we overload a superclass method or a method in the same class there are rules to adhere to:

  • A method overload must have a different argument list to any like named methods in the same class.
  • A method overload must have a different argument list to any like named methods in a superclass or you end up with a method override and all the rules that apply when using overrides as listed in the section above.
  • A method overload has no constraints on return types.
  • A method overload has no constraints on accessibility.
  • A method overload has no constraints on checked exceptions.


/*
  Some code showing method override rules
*/
public class A { 
    void methodA(int i) { }
    final void methodB() {}
    protected void methodC(String s) {}
    void methodD(String s, int i) {}
    String methodE(String s, int i) { return s; }
    void methodF() { }
}


import java.io.*;  // Import IO exception classes

public class B extends A { 
    void methodA(int i) { }                    // OK, same parameter list as superclass so an override
    void methodA(String s, int i) { }          // OK, this is an overloaded method of superclass
    final void methodB(int i) {}               // OK, parameter list differs from superclass method 
                                               // marked as final, so valid overload  
    void methodC(int i) {}                     // OK, no constraints on subclass overload accessibility
    protected void methodD(int i, String s) {} // OK, this is an overloaded subclass method
    void methodD(String s, int i) { }          // OK, valid override
    int methodE(int i, String s) { return i; } // OK, no constraints on subclass overload return types
    void methodF(int i) throws IOException { } // OK, no constraints on subclass overload
                                               // checked exceptions  
    void methodG() {}                          
    void methodG(String s) {}                  // OK, overloaded in same class
}

Static Methods Top

Static methods run without any instance of a class being involved, as static they belong to the class as a whole, and are accessed using the name of the class in the same way as static variables. Lets think about it, the main() method we have used since the start of these lessons to test our classes, runs before any instances of a class exist. In fact you cannot access the instance variables of a class from a static method within the class or the compiler gets upset. This makes sense as how would a static method know which instance variable to use, there is no reference. Its the same thing when you try to call a non-static method from a static method, the compiler doesn't like it. The static method has no way of knowing how to use any instance variables associated with the non-static method and has no way to reference which instance they belong to. Even if the method has no instance variables the compiler still won't allow it because in the future you may amend the method to include an instance variable. Lets look at these rules with some code:


package info.java8;
/*
  Test class for static calls
*/ 
public class StatCalls {

    public static void main (String[] args) {
        int aSquare = squareNumber(5);   // Calling non-static method
        System.out.println(aSquare);
    }
    
    /*
      A method that squares and returns the passed integer
    */ 
    int squareNumber(int number) {  
        int square = number * number;  
        return square;  // Here we use the return keyword to pass back a value
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run static calls

The above screenshot shows the output of running our StatCalls class. The compiler doesn't like the fact that we are calling a non-static method from main() method. Lets look at using instance variables from a static method.


package info.java8;
/*
  Test class for static calls
*/ 
public class StatCalls2 {
    int number;

    public static void main (String[] args) {
        number = 5;  // Calling instance variable
        int aSquare = StatCalls2.squareNumber(5);   // Calling static method
        System.out.println(aSquare);
    }
    
    /*
      A method that squares and returns the passed integer
    */ 
    static int squareNumber(int number) {  
        int square = number * number;  
        return square;  // Here we use the return keyword to pass back a value
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run static calls2

The above screenshot shows the output of running our StatCalls2 class. The compiler doesn't like the fact that we are calling an instance variable from the main() method.

Of course there is no problem using instantiation from within your static method to access non-static content, or using a local reference from within your static method to access non-static content; otherwise you would never be able to get out of the main() method.

Static Variables Top

Java comes with three kinds of scope and we name variables according to the scope they reside in as detailed in the table below. In this section we focus on static variables and their scope.

Variable Scope Lifetime
staticStatic variables apply to the class as a whole and are declared within the class but outside a method.Exists for as long as the class it belongs to is loaded in the JVM.
instanceInstance variables apply to an instance of the class and are declared within the class but outside a method.Exists for as long as the instance of the class it belongs to.
See the Instance Variables & Scope lesson for more information
localLocal variables apply to the method they appear in.Exists until the method it is declared in finishes executing.
See the Method Scope lesson for more information

Static variables are initialized as soon a class is loaded and before any instances of the class are instantiated.

Like instance variables, static variables are assigned a default value according to their type:

  • object - null
  • boolean - false
  • char - /u0000
  • integer types (byte, short, int and long) - 0
  • floating-point types (float, double) - 0.0

Because static variables are independent of any instance they can be used by all instances of the class. To demonstrate what we mean by this lets write some code that keeps count of the instances we create.


package info.java8;
/*
  A Donkey Class
*/ 
public class Donkey {
    int count;

    public Donkey() {
        count++;  // Increment a counter  
        System.out.println("Our donkey count = " + count);
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way. Lets write a new test class for our Donkey.


package info.java8;
/*
  Test Class for Donkey
*/ 
public class DonkeyTest {

    public static void main (String[] args) {
        Donkey dan = new Donkey(); 
        Donkey sam = new Donkey();  
        Donkey liam = new Donkey(); 
    }
}

Save, compile and run the file in directory   c:\_ObjectsAndClasses in the usual way.

run donkey

The above screenshot shows the output of running our Donkey class. We created three Donkey instances and printed out a total after each. The total is always 1 as each time we create a new instance of Donkey we are creating a new count variable as this is an instance variable. Lets revamp our Donkey class and make the instance variable into a static variable:


package info.java8;
/*
  A Donkey Class
*/ 
public class Donkey {
    static int count;  // We made this static

    public Donkey() {
        count++;  // Increment a counter  
        // Prefix statics with the class name
        System.out.println("Our donkey count = " + Donkey.count); 
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way and then rerun our DonkeyTest class.

run donkey2

The above screenshot shows the output of running our Donkey class. We created three Donkey instances and printed out a total after each. The total now gets gets incremented each time we create a new instance of Donkey just like we want. Static variables apply to the whole class and are initialized when the class is loaded. There is only one copy of each static variable in a class which is shared by all instances of that class. We prefix statics with the class name to avoid ambiguity and to highlight the fact that this is a static.

EncapsulationTop

Encapsulation is a language mechanism used to restrict access to an object's components. In Java parlance this is a mechanism to restrict access to the members (instance variables and methods) of a class, constructors and even to the class itself. So how do we achieve this in Java?. Well Java provides us with the access modifier mechanism for restricting access to our classes, which are the basic unit of encapsulation in Java. We can also restrict access to our instance variables whilst providing access to them via public methods. We can limit construction to the class itself using the private keyword, or to the package the implementation is in using the protected keyword or with no modifier package-private / (the default). The table below lists access modifiers in more detail.

Access ModifiersTop

The table below shows the four types of access available in Java from the most open (public) to the most restrictive (private). We can only explicitly apply the public access modifier to our top-level classes (the classes we compile) but for members and constructors we can explicitly apply the protected and private access modifiers as well. We will talk about packaging in the Packages lesson, but for now we are going to examine how to protect our class members from unwanted access and modification.

Access modifier Class Member Constructor Description
publicYesYesYesA top-level class may be declared with the public access modifier, and if it is the class is accessible to all other classes everywhere.
A member may be declared with the public access modifier, and if it is the member is accessible to all other classes everywhere, assuming the class it resides in is accessible.
A constructor may be declared with the public access modifier, and if it is the constructor is accessible to all other classes everywhere, assuming the class it resides in is accessible.
protectedNoYesYesA member may be declared with the protected access modifier, and if so, is only accessible within its own package and also by a subclass of its class in other packages.
A constructor may be declared with the protected access modifier, and if so, it is only accessible to the package the implementation is in.

See the Packages lesson for more information on packaging.
See the Inheritance Basics lesson for more information on subclassing.
no modifier
package-private /
(the default)
YesYesYesIf a top-level class has no explicit access modifier, which is the default and is also known as package-private, it is accessible only within its own package.
If a member has no explicit access modifier it is only accessible within its own package.
If a constructor has no explicit access modifier, it is only accessible to the package the implementation is in.

See the Packages lesson for more information on packaging.
privateNoYesYesA member may be declared with the private access modifier, and if it is the member is only accessible within its own class.
A constructor may be declared with the private access modifier, and if it is the constructor can only be constructed from within its own class.

See the OO Concepts - Encapsulation lesson for example usage and information on how we use getters and setters to enforce tight encapsulation.

Class Access Modifiers

The table below shows the types of access available in Java for our top-level classes.

Access modifier Description
publicA top-level class may be declared with the public access modifier, and if it is the class is accessible to all other classes everywhere.
no modifier
package-private / (the default)
If a top-level class has no explicit access modifier, which is the default and is also known as package-private, it is accessible only within its own package.
  • The only access modifier that can be used with a top-level class is public.
  • Only one class can be marked as public per source file.
  • If a class within a source file is marked as public then the name of the source file must exactly match the name of the public class (followed by the .java extension).
  • A source file can include multiple non-public classes.
  • If a source file contains no public class then there is no naming constraints on that source file, ie. it doesn't have to match the name of one of the non-public classes.
  • Aside from access modifiers classes can also be marked with the non-access modifiers abstract, final and strictfp. The strictfp is not part of the certification and so we won't go into details. When a class is marked as abstract it has to be subclassed; when a class is marked as final it can't be subclassed, so these two modifers can never be used together.
  • A class can extend a maximum of one other class.


/*
  Some code showing class rules
*/
class A { }                        // OK
private class A { }                // No, can only use public access modifier with top level class
protected class A { }              // No, can only use public access modifier with top level class
public class A { }                 // OK
public class B extends A { }       // OK
public class C extends B, A { }    // No, can only extend one class
abstract class D { }               // OK
final class E { }                  // OK
abstract final class F { }         // No, cannot use both abstract and final


Method Access Modifiers

The table below shows the four types of access available in Java for members (variables, inner classes and methods).

Access modifier Description
publicA member may be declared with the public access modifier, and if it is the member is accessible to all other classes everywhere, assuming the class it resides in is accessible.
protectedA member may be declared with the protected access modifier, and if so, is only accessible within its own package and also by a subclass of its class in other packages..

See the Packages lesson for more information on packaging.
See the Inheritance Basics lesson for more information on subclassing.
no modifier
package-private / (the default)
If a member has no explicit access modifier it is only accessible within its own package.
privateA member may be declared with the private access modifier, and if it is the member is only accessible within its own class.

Non-access Modifiers

The table below shows the types of non-access modifiers available for use with variables.

static variable and instance variable modifiers local variable modifiers
final
transient
volatile
final

ConstructorsTop

Constructors allow us to instantiate our objects via declaration, assignment and creation.

object creation
  1. Declaration - Here. we declare a reference variable named moggy of type Cat and the JVM allocates space for it.
  2. Creation - Tells the JVM to allocate space on The Heap for a new Cat object.
  3. Assignment - Assign the new Cat object to the reference variable moggy.

Access Modifiers

The table below shows the types of access available in Java for constructors.

Access modifier Description
publicA constructor may be declared with the public access modifier, and if it is the constructor is accessible to all other classes everywhere, assuming the class it resides in is accessible.
protectedA constructor may be declared with the protected access modifier, and if so, it is only accessible to the package the implementation is in.

See the Packages lesson for more information on packaging.
no modifier
package-private /
(the default)
If a constructor has no explicit access modifier, it is only accessible to the package the implementation is in.

See the Packages lesson for more information on packaging.
privateA constructor may be declared with the private access modifier, and if it is the constructor can only be constructed from within its own class.

Constructor Checklist

  • A constructor runs when we code the new() operator followed by a class name.
  • Constructors must have the same name as the class and no return type.
  • Constructors are used to initialize the instance variables (object state) of the object instance being constructed.
  • If you don't code a constructor in your class the compiler will put in a default no arguments constructor.
  • If you do code a constructor in your class, the compiler will NOT put in a default no arguments constructor, you will have to code it yourself.
  • We can have more than one constructor in a class and the constructors are then known as overloaded constructors.
  • When using overloaded constructors in a class, each constructor must have different argument lists so the compiler knows which constructor to use to construct our objects. Having the same argument types is fine as long as the order differs.
  • You can refer to any member of the current object from within a non-static method or constructor by using the this() keyword.
  • You can invoke one constructor from another constructor within the same class by calling this() and doing so is known as explicit constructor invocation and is the only way to invoke a constructor.
  • If we decide to use this() it must be the first statement within our constructor or we get a compiler error. This means we can't use this() and super() together.
  • We can use super() to invoke a superclass constructor and if we don't supply this explicitly, then the compiler inserts a no-args super() for us as the first statement in the constructor if we haven't used the this() keyword.
  • When explicitly coding super() we can supply arguments to invoke a constructor in the superclass matching the signature of our call.
  • When explicitly coding super() it must be the first statement within the constructor or we get a compiler error. This means we can't use super() and this() together.
  • When explicitly coding super() only methods and static variables can be used within the call.
  • Interfaces do not have constructors as they are not part of a particular classes inheritance tree.
  • Abstract classes do have constructors, although we can't code them, as they are part of a classes inheritance tree and so are called via super() on concrete subclass instantiation.


/*
  Some code showing constructor usage
*/
public class A { 
    public static void main(String[] args) { 
        A a = new A(); // OK, compiler puts in a default no arguments constructor so we can instantiate
    }
}   

public class B { 
    B() { } // OK, we code our own default no arguments constructor
}                                                

public class C { 
    void C() { } // OK, this is a method with the same name as the class as it has a return type
    public static void main(String[] args) { 
        C c = new C();
    }
}          
                                     
public class D { 
    D() { }
    D(int i) { } // OK, overloaded constructor
    public static void main(String[] args) { 
        D d = new D();
        D d2 = new D(5);
    }
}

public class E { 
    int i;    
    String s;

    E(int i) {
        this(i, "unknown"); // Invoke one constructor from another constructor within the same class
    }
    E(int i, String s) {
        this.i = i;
        this.s = s;
    }
    public static void main(String[] args) { 
        E e = new E(5);
    }
}

/*
  Following code will fail as compiler inserts a no arguments constructor for us which invokes 
  super() and there isn't a no arguments constructor in superclass
*/
public class F extends E { 
}

public class G extends E { 
    G(int i) { // here we invoke existing super constructor so works fine 
        super(i);
    }
    public static void main(String[] args) { 
        G g = new G(5);
    }
}

Overloaded Constructors Top

This isn't the end of the story though, not by a long chalk. Lets enhance our test class above to call a Cat object with no arguments:


package info.java8;
/*
  Test Class5 for Cat
*/ 
public class TestCat5 {

    public static void main (String[] args) {
        Cat moggy = new Cat("Henry", "black", 4);   // Call new Cat constructor with three arguments
        Cat tigger = new Cat("Kitty", "black", 15); // Call new Cat constructor with three arguments
        Cat tabby = new Cat("Felix", "white", 8);   // Call new Cat constructor with three arguments
        Cat tommy = new Cat();  // Call Cat constructor with no arguments
        System.out.println("Our " + moggy.colour + " cat called " + moggy.name + " is " + moggy.age);
        System.out.println("Our " + tigger.colour + " cat called " + tigger.name + " is " 
                                  + tigger.age);
        System.out.println("Our " + tabby.colour + " cat called " + tabby.name + " is " + tabby.age);
        System.out.println("Our " + tommy.colour + " cat called " + tommy.name + " is " + tommy.age);
    }
}

Save and compile the file in directory   c:\_ObjectsAndClasses in the usual way.

run cat class with no args constructor

The above screenshot shows the output of compiling our TestCat5 class. Whoa! What happened here? It has given us an error when we try to create a new Cat object using a no arguments constructor?? It appears from the error that the compiler was looking for a constructor with (String,String,int). But doesn't the compiler put in a default no arguments constructor for us? No it doesn't, as soon as we code our own constructor the compiler doesn't get involved and leaves construction to us. So if we want a default no arguments constructor as well we have to code it. Ok lets do just that:


package info.java8;
/*
  A Cat Class
*/ 
public class Cat {
    String name, colour;
    int age;
    /*
      no-arg constructor for our Cat Class
    */ 
    public Cat() {

    }

    /*
      Below is the new constructor for our Cat Class
    */ 
    public Cat(String a, String b, int c) {
        name = a;
        colour = b;
        age = c;
    }
    
    void talk() {
  System.out.println("meow!");
    }
}

Save and compile our revised Cat class in directory   c:\_ObjectsAndClasses in the usual way.

We now have two constructors in our Cat class. When a class has more than one constructor they are known as overloaded constructors. Each constructor must have different argument lists so the compiler knows which constructor to use to construct our objects. Having the same argument types is fine as long as the order differs. OK, our TestCat5 class should compile fine now, so compile and run it:

run cat class with two constructor

The above screenshot shows the output of running our TestCat5 class. As you can see from the output we now have a fourth line of output for a Cat object, which was instantiated using our no-arg constructor with default values.

Related Java Tutorials

Objects & Classes - Reference Variables - The new Operator
Objects & Classes - Constructors
OO Concepts - Encapsulation
OO Concepts - Polymorphism
OO Concepts - Overriding Methods
Objects & Classes - Overloaded Methods
OO Concepts - Accessing Superclass Members
Inheritance Concepts - Superclass Constructors
OO Concepts - Static Overrides?
OO Concepts - IS-A and HAS-A Relationships