Methods and Encapsulation

Designing methods

Two of the methods parts - method name and parameter list - are called the method signature.

Access Modifiers

  • private. It means the method can be called only from within the same class.
  • default package-private. The method can be called only from classes in the same package.
  • protected. It means the method can be called only from classes in the same package or subclasses.
  • public. Means the method can be called from any class.

(!) The exam creators like to trick you by putting method elements in the wrong order or using incorrect values (!)

public void walk1() {}
default void walk2() {} // DOES NOT COMPILE
void public walk3() {} // DOES NOT COMPILE
void walk4() {}

Optional Specifiers

  • static. It’s used for class methods
  • abstract. It’s used when a method body is not provided
  • final It’s used when a method is not allowed to be overriden by a subclass
  • synchronized It’s used with multithreaded code
  • native it’s used when interacting with code written in another language, such as C++
  • strictfp. It’s used for making float-point calculations portable
public void walk() {}
public final void walk() {}
public static final void walk() {}
public final static void walk() {}
public modifier void walk5() {} // DOES NOT COMPILE
public void final walk() {} // DOES NOT COMPILE
final public void walk() {}

Return Type

Methods with a return type other than void are required to have a return statement inside the method body.

public void walk() {}
public void walk() { return; }
public String walk() { return ""; }
public String walk() {} // DOES NOT COMPILE
public walk() {} // DOES NOT COMPILE
public String int walk() {} // DOES NOT COMPILE
String walk(int a) { if(a==4) return ""; } // DOES NOT COMPILE
int longMethod() {
	return 9L; // DOES NOT COMPILE
}

Method Name

They follow the same rules as variable names.

  • They mey only contain letters, numbers, $ or _
  • The first character is not allowed to be a number.
  • Reserved words are not allowed.
  • A single underscore char is not allowed.
public void walk1() {}
public void 1walk() {} // DOES NOT COMPILE
public walk1 void() {} // DOES NOT COMPILE
public void Walk_$() {}
public _() {} // DOES NOT COMPILE
public void() {} // DOES NOT COMPILE

Parameter List

Altough the list is required, it doesn’t have to contain any parameters. If you have multiple parameters, you separate them with a comma.

public void walk() {}
public void walk {} // DOES NOT COMPILE
public void walk(int a) {}
public void walk(int a; int b) {} // DOES NOT COMPILE
public void walk(int a, int b) {}

Optional Exception List

You can list as many types of exceptions as you want, separated by commas

public void zeroExceptions() {}
public void oneException() throws IllegalArgumentException {}
public void twoExceptions() throws IllegalArgumentException, InterruptedException {}

Method Body

It’s simply a code of block. It’s required.

Working with Varargs

A varargs parameter must be the last element in a method’s parameter list.

public void walk(int)... nums {}
public void walk(int start, int... nums) {}
public void walk(int... nums, int start) {} // DOES NOT COMPILE
public void walk(int... start, int... nums) {} // DOES NOT COMPILE

When calling a method with vararg params, you can either pass in an array, or list the elements of the array and let Java create it for you. You can even omit the varargs values in the method call and it will create an array of 0 length.

public static void walk(int start, int... nums) {
	sout(nums.length());
}

public static void main(String... args) {
	walk(1); // 0
	walk(1, 2); // 1
	walk(1, 2, 3); // 2
	walk(1, new int[] {2, 3}); // 2

	walk(1, null); // NullPointerException
}

Applying Access Modifiers

Private Access

Only code in the same class can call private methods or access private fields.

Default (Package-Private) Access

When there’s no access modifier, Java uses the default, which is package-private. This means that the member is “private” to classes in the same package. In other words, only classes in the package may access it.

Protected Access

It allows everything that default access allows and more. It adds the ability to access members of a parent class.
The definition of protected allows access to subclasses and classes in the same package.

We have the following class with protected fields and methods

package pond.shore;

public class Bird {
	protected String text = "floating";

	protected void floatInWater() {
		sout(text);
	}
}

Now we create a subclass

package pond.goose;

import pond.shore.Bird; // different package

public class Gosling extends Bird {

	public void swim() {
		floatInWater(); // calling protected member
		sout(text); // accessing protected member
	}

}

Class in same package, but not a subclass

package pond.shore; // same package as Bird

public class BirdWatcher {

	public void watchBird() {
		Bird bird = new Bird();
		bird.floatInWater(); // calling protected member
		sout(bird.text); // accessing protected member
	}

}

Class in different package, not a subclass

package pond.inland;

import pond.shore.Bird; // different package

public class BirdWatcherFromAfar {

	public void watchBird() {
		Bird bird = new Bird();
		bird.floatInWater(); // DOES NOT COMPILE
		sout(bird.text); // DOES NOT COMPILE
	}

}

Subclasses and classes in the same package are the only ones allowed to access protected members.

Check the following case

package pond.swan;

import pond.shore.Bird; // different package than Bird

public class Swan extends Bird { // subclass of Bird

	public void swim() {
		floatInWater(); // subclass access to superclass
	}

	public void helpOtherSwanSwim() {
		Swan other = new Swan();
		other.floatInWater(); // subclass access to superclass (is same class)
	}

	public void helpOtherBirdSwim() {
		Bird other = new Bird();
		other.floatInWater(); // DOES NOT COMPILE
	}

}

Altough Bird is the superclass, it’s referenced as a variable rather than inherited. This is why it won’t work.

The protected rules apply under two scenarios:

  • A member is used without referring to a variable. We are taking advantage of inheritance and protected access is allowed.
  • A member is used through a variable. If it’s a subclass, protected access is allowed.

Public Access

public means anyone can access the member from anywhere.

Applying the static keyword

The static keyword applies to the class, rather than a specific instance of the class.

Designing static Methods and Fields

static methods don’t require an instance of the class. They’re shared among all users of the class. It exists independently of any instances of that class.

public class KoalaTester {
	public static void main(String... args) {
		Koala.main(new String[0]);
	}
}

They have two main purposes

  • For utility or helper methods that don’t require any object state, since there’s no need to access instance variables.
  • For state that’s shared by all instances of a class, like a counter.

Accessing a static Variable or Method

You can use an instance of the object to call a static method.
This is legal.

spit(Koala.count); // 0

Koala k = new Koala();
sout(k.count); // 0

k = null;
sout(k.count); //0! This still works

For the last example, Java doesn’t care that k happens to be null, since we are looking for a static it doesn’t matter.

Static vs Instance

A static member cannot call an instance member without referencing an instance of the class.

public class Static {
	private String name = "Static class";

	public static void first() {}
	public static void second() {}
	public void third() {sout(name)}
	public static void main(String... args) {
		first();
		second();
		third(); // DOES NOT COMPILE
		// new Static().third(); // would compile
	}
}

Another example

1: 	public class Gorilla {
2: 		public static int count;
3: 		public static void addGorilla() { count++; }
4: 		
5: 		public void babyGorilla() { count++; }
6: 		
7:	 	public void announceBabies() {
8: 			addGorilla();
9: 			babyGorilla();
10: 	}
11: 	
12: 	public static void announceBabiesToEveryone() {
13: 		addGorilla();
14: 		babyGorilla(); // DOES NOT COMPILE
15: 	}
16: 	
17: 	public int total;
18: 	public static double average = total / count; // DOES NOT COMPILE
19: }

Lines 7-10 are fine because both static and instance methods can refer to a static variable.

Lines 12-15 doesn’t compile because a static method cannot call an instance method.

Line 18 doesn’t compile because a static variable is trying to use an instance variable.

static Variables

Some static variables are meant to change as the programs runs, such as counters.

public class Initializers {
	private static int counter = 0;
}

Others are meant to never change during the program. This type of variable is known as a constant.

public class Initializers {
	private static final int NUM_BUCKETS = 435;

	public static void main(String... args) {
		NUM_BUCKETS = 5; // DOES NOT COMPILE
	}
}

Static Initialization

static initialization specifies they should be run when the class is first loaded.

private static final int NUM_SECONDS_PER_MINUTE;

static {
	NUM_SECONDS_PER_MINUTE = 60;
}

They’re run when the class is first used in the order they’re defined. The statements in them run and assign any static variables as needed.

final variables aren’t allowed to be reassigned.

private static int one;
private static final int two;
private static final int three = 3;
private static final int four; // DOES NOT COMPILE

static {
	one = 1;
	two = 2;
	three = 3; // DOES NOT COMPILE
	two = 20; // DOES NOT COMPILE
}
  • one compiles because it’s not final, so it can be re-assigned as many times as we want
  • two is final but not initialized until later. as final it can only be assigned once.
  • three was already assigned when created.
  • four never gets initialized and as final, static blocks are the only place where it could be initialized.

(!) They’re good for when you need to initialize a static field and the code required to do so requires more than one line. This occurs when you want to initialize a collection like an ArrayList.

Static Imports

import java.utils.List;
import static java.util.Arrays.asList; // static import

public class StaticImports {
	public static void main(String... args) {
		List<String> list = asList("one", "two"); // no Arrays.
	}
}

Regular imports are for importing classes. Static imports are for importing static member of classes.
The idea is that you shouldn’t have to specify where each static method or variable comes from each time you use it.

If we created an asList method in our class, Java would give it preference over the imported one, and our method would be used.

1: import static java.util.Arrays; // DOES NOT COMPILE
2: import static java.util.Arrays.asList;
3: static import java.util.Arrays.*; // DOES NOT COMPILE

4: public class BadStaticImports {
5: 	public static void main(String... args) {
6: 		Arrays.asList("one"); // DOES NOT COMPILE
7: 	}
8: }

Line 1 fails because static imports are only for importing static members.
Line 6 fails, because the asList method is imported on line 2, however the Arrays class is not imported anywhere. This makes it okay to write asList("one"), but not Arrays.asList("one").

You cannot do an static import of two methods with the same name or two static variables with the same name.

import static statics.A.TYPE;
import static statics.B.TYPE; // DOES NOT COMPILE

Passing Data among Methods

Java is pass-by-value. A copy of the variable is made and the method receives that copy. Assignments made in the method do not affect the caller.

This is an example for variable assignments.

public static void main(String... args) {
	String name = "Webby";
	speak(name);
	sout(name); // Webby
}

public static void speak(String name) {
	name = "Sparky";
}

We can also call methods on the parametes passed into methods.

public static void main(String... args) {
	StringBuilder name = new StringBuilder();
	speak(name);
	sout(name); // Webby
}

public static void speak(StringBuilder s) {
	s.append("Webby");
}

In the last case, the output is Webby because the method calls a method on the param. It doesn’t reassign name to a different object.

Getting data back from a method is easier. A copy is made of the primitive or reference and returned from the method. Most of the time, this returned value is used or stored in a variable. If the return value is not used, the result is ignored.

public class ReturningValues {
	public static void main(String... args) {
		int number = 1; // number = 1
		String letters = "abc"; // letters = abc
		number(number); // still, number = 1
		letters = letters(letters); // letters = abcd
	}

	public static int number(int number) {
		number++;
		return number;
	}

	public static String letters(String letters) {
		letters += "d";
		return letters;
	}
}

Overloading Methods

Method overloading occurs when methods have the same name but different method signatures, which means they differ by method parameters. Aside from this, they may have different access modifiers, specifiers (static), return types and exception lists.

These are all valid overloaded methods.

public void fly(int numMiles) {}
public void fly(short numFeet) {}
public boolean fly() { return false; }
void fly(int numMiles, short numFeet) {}
public void fly(short numFeet, int numMiles) throws Exception {}

We can overload by changing anything in the parameter list.

public void fly(int numMiles) {}
public fly(int numMiles) {} // DOES NOT COMPILE

This method doesn’t compile because it differs from the original only by return type.

public void fly(int numMiles) {}
public static void fly(int numMiles) {} // DOES NOT COMPILE

Again, the parameter list is the same. They must differ.

varargs

public void fly(int[] lengths) {}
public void fly(int... lengths) {} // DOES NOT COMPILE

Remember that Java treats varargs as if they were an array. This means that the method signature is the same for both methods.

Autoboxing

public void fly(int numMiles) {}
public void fly(Integer numMiles) {}

When the primitive version isn’t present, Java will autobox. However, when the primitive int version is provided, there’s no reason for Java to do the extra work of autoboxing.

Reference Types

public class ReferenceTypes {
	public void fly(String s) {
		sout("string");
	}

	public void fly(Object o) {
		sout("object")
	}

	public void main(String[] args) {
		ReferenceTypes r = new ReferenceTypes();
		r.fly("test");
		sout("-");
		r.fly(56)
	}
}

This prints string-object. When it doesn’t find a primitive int version, it autoboxes into Integer, and then it fits as Object.

Primitives

Primitives work in a way that’s similar to reference variables. Java tries to find the most specific matching overloaded method.

public class Plane {
	public void fly(int i) {
		sout("int");
	}

	public void fly(long l) {
		sout("long");
	}

	public static void main(String[] args) {
		Plane p = new Plane();
		p.fly(123);
		sout("-");
		p.fly(123L);
	}
}

This will print int-long. Java can only accept wider types. It won’t automatically convert to a narrower type.

Generics

You might be surprised that these are not valid overloads.

public void walk(List<String> strings) {}
public void walk(List<Integer> integers) {} // DOES NOT COMPILE

Java hasa concept called type erasure, where generics are used only at compile time. This means the compiled code looks like this

public void walk(List strings) {}
public void walk(List integers) {} // DOES NOT COMPILE

Arrays

Unlike the previous example, this code works just fine

public static void walk(int[] ints) {}
public static void walk(Integer[] integers) {}

Encapsulating Data

Encapsulation means only methods in the class with the variables can refer to the instance variables. Callers are required to use these methods.

public class Swan {
	private int eggs; // private

	public int getNumberEggs() { // getter
		return eggs;
	}

	public void setNumberEggs(int newNumber) { // setter
		if(newNumber >= 0) { // guard condition
			eggs = newNumber;
		}
	}
}

Exam Essentials

Be able to identify correct and incorrect method declarations. A sample method declaration is public static void method(String… args) throws Exception {}.

Identify when a method or field is accessible. Recognize when a method or field is accessed when the access modifier (private, protected, public, or default access) does not allow it.

Recognize valid and invalid uses of static imports. Static imports import static members. They are written as import static, not static import. Make sure they are importing static methods or variables rather than class names.

State the output of code involving methods. Identify when to call static rather than instance methods based on whether the class name or object comes before the method. Recognize that instance methods can call static methods and that static methods need an instance of the object in order to call an instance method.

Recognize the correct overloaded method. Exact matches are used first, followed by wider primitives, followed by autoboxing, followed by varargs. Assigning new values to method parameters does not change the caller, but calling methods on them does.

Identify properly encapsulated classes. Instance variables in encapsulated classes are private. All code that retrieves the value or updates it uses methods. These methods are allowed to be public.