Controlling Access to Class Members
- Access level modifiers determine whether other classes can use a particular field or invoke a particular method.
- There are two levels of access control:
- At the top level – public, or package-private (no explicit modifier).
- At the member level – public, private, protected, or package-private (no explicit modifier).
- A class may be declared with the modifier public, in which case that class is visible to all classes everywhere.
- If a class has no modifier (the default, also known as package-private), it is visible only within its own package.
- At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning.
- For members, there are two additional access modifiers: private and protected.
- The private modifier specifies that the member can only be accessed in its own class.
- The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package.
| Modifier | Class | Package | Subclass | World |
| public | Y | Y | Y | Y |
| protected | Y | Y | Y | N |
| no modifier | Y | Y | N | N |
| private | Y | N | N | N |
Tips on Choosing an Access Level
- If other programmers use your class, you want to ensure that errors from misuse cannot happen.
- Access levels can help you do this.
- Use the most restrictive access level that makes sense for a particular member. Use private unless you have a good reason not to.
- Avoid public fields except for constants.
Encapsulation
- Encapsulation in Java is a mechanism of wrapping the data (variables) and code acting on the data (methods) together as a single unit.
- In encapsulation, the variables of a class will be hidden from other classes and can be accessed only through the methods of their current class.
- Therefore, it is also known as data hiding.
Properly encapsulated
To achieve encapsulation in Java
- Declare the variables of a class as private.
- Provide public setter and getter methods to modify and view the variables values.
public class Box {
private double width;
private double height;
private double depth;
public Box(double width, double height, double depth) {
this.width = width;
this.height = height;
this.depth = depth;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getDepth() {
return depth;
}
public void setDepth(double depth) {
this.depth = depth;
}
// compute and return volume
double volume() {
return width * height * depth;
}
// sets dimensions of box
void setDim(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
}
- The public setXXX() and getXXX() methods are the access points of the instance variables of the EncapTest class.
- Normally, these methods are referred as getters and setters.
- Therefore, any class that wants to access the variables should access them through these getters and setters.
Pass Objects to Methods
- Java is strictly pass-by-value.
- Object references can be parameters.
- Call by value is used, but now the value is an object reference.
- This reference can be used to access the object and possibly change it.
public class BoxTest {
public static void main(String[] args) {
Box theBox = new Box(2, 4, 6);
System.out.println("before: " + theBox);
update(theBox);
System.out.println("after: " + theBox);
}
private static void update(Box box) {
// modify box here
box.setWidth(1);
box.setHeight(3);
box.setDepth(5);
}
/*
* before: width: 2.0, height: 4.0, depth: 6.0
* after: width: 1.0, height: 3.0, depth: 5.0
*/
}
Returning Objects
- A method can return any type of data, including class types that you create.
- For example, in the following program, the incrByTen( ) method returns an object in which the value of a is ten greater than it is in the invoking object.
public class BoxTest {
public static void main(String[] args) {
Box theBox = new Box(2, 4, 6);
System.out.println("before: " + theBox);
theBox = getBox();
System.out.println("after: " + theBox);
}
private static Box getBox() {
return new Box(12, 12, 12);
}
/*
* before: width: 2.0, height: 4.0, depth: 6.0
* after: width: 12.0, height: 12.0, depth: 12.0
*/
}
Method Overloading
- In Java, it is possible to define two or more methods within the same class that share the same name, as long as their parameter declarations are different.
- When this is the case, the methods are said to be overloaded, and the process is referred to as method overloading.
- Method overloading is one of the ways that Java supports polymorphism.
- When an overloaded method is invoked, Java uses the type and/or number of arguments as its guide to determine which version of the overloaded method to actually call.
- Thus, overloaded methods must differ in the type and/or number of their parameters.
public class OverloadingExample {
public static void main(String[] args) {
// call the overloaded methods
addNumbers(1, 2);
addNumbers(1, 2, 3);
addNumbers(1, 2, 3, 4);
addNumbers(1, 2, 3, 4, 5);
}
// overloaded methods
private static void addNumbers(int a, int b) {
System.out.println("total: " + (a + b));
}
private static void addNumbers(int a, int b, int c) {
System.out.println("total: " + (a + b + c));
}
private static void addNumbers(int a, int b, int c, int d) {
System.out.println("total: " + (a + b + c + d));
}
private static void addNumbers(int a, int b, int c, int d, int e) {
System.out.println("total: " + (a + b + c + d + e));
}
/*
* total: 3
* total: 6
* total: 10
* total: 15
*/
}
Overloading Constructors
- In addition to overloading normal methods, you can also overload constructor methods.
- In fact, for most real-world classes that you create, overloaded constructors will be the norm, not the exception.
public class Box {
private double width;
private double height;
private double depth;
public Box() {
width = 10;
height = 10;
depth = 10;
}
public Box(double width, double height, double depth) {
this.width = width;
this.height = height;
this.depth = depth;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getDepth() {
return depth;
}
public void setDepth(double depth) {
this.depth = depth;
}
// compute and return volume
double volume() {
return width * height * depth;
}
// sets dimensions of box
void setDim(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
@Override
public String toString() {
return String.format("width: %s, height: %s, depth: %s", getWidth(), getHeight(), getDepth());
}
}
The static Keyword
- There will be times when you will want to define a class member that will be used independently of any object of that class.
- Normally, a class member must be accessed only in conjunction with an object of its class.
- However, it is possible to create a member that can be used by itself, without reference to a specific instance.
- To create such a member, precede its declaration with the keyword static.
- When a member is declared static, it can be accessed before any objects of its class are created, and without reference to any object.
- You can declare both methods and variables to be static.
- The most common example of a static member is main( ). main( ) is declared as static because it must be called before any objects exist.
- Instance variables declared as static are, essentially, global variables.
- When objects of its class are declared, no copy of a static variable is made.
- Instead, all instances of the class share the same static variable.
Methods declared as static have several restrictions.
- They can only directly call other static methods.
- They can only directly access static data.
- They cannot refer to this or super in any way.
public class BoxTest {
public static void main(String[] args) {
Box theBox = new Box(2, 4, 6);
System.out.println("before: " + theBox);
theBox = getBox();
System.out.println("after: " + theBox);
}
private static Box getBox() {
return new Box(12, 12, 12);
}
private static void update(Box box) {
// modify box here
box.setWidth(1);
box.setHeight(3);
box.setDepth(5);
}
}
Nested and Inner Classes
- It is possible to define a class within another class; such classes are known as nested classes.
- The scope of a nested class is bounded by the scope of its enclosing class. Thus, if class B is defined within class A, then B does not exist independently of A.
- A nested class has access to the members, including private members, of the class in which it is nested. However, the enclosing class does not have access to the members of the nested class.
- A nested class that is declared directly within its enclosing class scope is a member of its enclosing class.
- It is also possible to declare a nested class that is local to a block.
There are two types of nested classes: static and non-static.
- A static nested class is one that has the static modifier applied.
- Because it is static, it must access the non-static members of its enclosing class through an object.
- That is, it cannot refer to non-static members of its enclosing class directly.
- Because of this restriction, static nested classes are seldom used.
The most important type of nested class is the inner class. An inner class is a non-static nested class. It has access to all of the variables and methods of its outer class and may refer to them directly in the same way that other non-static members of the outer class do.
public class NestedInnerExample {
public static void main(String[] args) {
MyOuter out = new MyOuter();
out.display();
MyOuter.MyInner in = out.getInner();
in.display();
}
}
class MyOuter {
private int number = 10;
public void display() {
System.out.println("number: " + number);
}
public MyInner getInner() {
return new MyInner();
}
public class MyInner {
private String msg = "Hello";
public void display() {
System.out.println("msg: " + msg);
// update number
number++;
System.out.println("number in MyInner: " + number);
}
}
/*
* number: 10
* msg: Hello
* number in MyInner: 11
*/
}
Varargs: Variable-Length Arguments
- Beginning with JDK 5, Java has included a feature that simplifies the creation of methods that need to take a variable number of arguments.
- This feature is called varargs and it is short for variable-length arguments.
- A method that takes a variable number of arguments is called a variable-arity method, or simply a varargs method.
public class VarargsExample {
public static void main(String[] args) {
// all these will call the same methods
addNumbers(1, 2);
addNumbers(1, 2, 3);
addNumbers(1, 2, 3, 4);
addNumbers(1, 2, 3, 4, 5);
}
private static void addNumbers(int... nums) {
int total = 0;
// nums is an array
// use loop to access values in nums
for (int n : nums) {
total += n;
}
System.out.println("total: " + total);
}
/*
* total: 3
* total: 6
* total: 10
* total: 15
*/
}