Quiz yourself: Applying access modifiers to Java classes (intermediate)

This Java certification quiz investigates the meaning of default accessibility and how it influences classes in different packages.

July 14, 2020 | Download a PDF of this article
More quiz questions available here

If you have worked on our quiz questions in the past, you know none of them is easy. They model the difficult questions from certification examinations. The “intermediate” and “advanced” designations refer to the exams rather than to the questions, although in almost all cases, “advanced” questions will be harder. We write questions for the certification exams, and we intend that the same rules apply: Take words at their face value and trust that the questions are not intended to deceive you but to straightforwardly test your knowledge of the ins and outs of the language.

This intermediate-level Java SE 11 quiz tests your knowledge of access modifiers for classes.

Given these two classes:


package p1; // line n1
import java.io.PrintStream;
public class Logger {	
	PrintStream ps;	
	public void log(String s) {
		ps.println(s);
	}	
	Logger() {
		ps = System.out;
	}
}

and


package p2;
import java.util.Arrays; 
// line n2
public class LoggingEngine {
	public static void main(String[] args) {
		Logger logger = new Logger();
		Arrays.asList(args).stream().forEach(s -> logger.log(s));
	}
}

What can be done to make the code compile? Choose one.

  1. At line n2, add import p1.Logger;.
  2. At line n2, add import static p1.Logger;.
  3. Change line n1 to package p2; and move that source file to the appropriate directory.
  4. Make the LoggingEngine class extend Logger and add the protected modifier to the Logger constructor.

Answer. This quiz investigates the meaning of default accessibility and how it influences classes in different packages.

In the code, notice first that the Logger class has a constructor that is not prefixed with private, protected, or public. The absence of any explicit modifier means that the class has default accessibility and is accessible only to code that is in the same package.

Notice also that the code in the LoggingEngine class tries to call that Logger class constructor. Closer inspection shows that the Logger class is in package p1 and the LoggingEngine class is in package p2. This presents two problems: First, the code in the second source file does not import the Logger, so it cannot be referred to by its short name. Second, with the code as presented, the constructor will be inaccessible and cannot compile.

Let’s consider the proposed solutions.

Option A suggests adding an import statement. Because the Logger class itself is marked public, this will make the Logger class visible by its short name in the LoggingEngine source file. However, the compiler will still reject the call to the constructor new Logger() because the code is in a different package and the constructor has the default access modifier and is not visible outside the p1 package. From this, you can see that option A is incorrect.

Option B is syntactically invalid; the import static mechanism can be used only to import a named static element of a class, for example, a method or variable, or it can be an asterisk, which is a wildcard form indicating that all static elements should be imported from the target type. Logger is a class, not a static element. Therefore, this import syntax is invalid and option B is incorrect.

Option C takes a different approach. The suggested changes, moving Logger into the package p2 and correcting the directory that contains the source file, serve to place both Logger and LoggingEngine in the same package. Following this change, the constructor of Logger, which has default access, is visible to the LoggingEngine class. Because both classes are in the same package, no imports are needed, and since the constructor is also accessible, the code compiles. Thus, option C is correct.

Option D proposes making the Logger’s constructor protected and making LoggingEngine a subclass of Logger. At first sight, this seems like a workable approach, since the simple view of protected is that it allows a subclass to access parent class elements even though the subclass is in a different package. (Note that protected is actually less restrictive than the default accessibility.)

However, this still will fail to compile for two reasons. Most obviously, there is no import statement in the LoggingEngine class, which means that unqualified access to the Logger is not permitted. The second reason is that protected access doesn’t give blanket access to parental features. Specifically, to access a nonstatic element that is marked as protected, a subclass must do so using a reference that is of the child class type (this reference can be, implicitly or explicitly, either this or super). This is sometimes described as access via inheritance. The bottom line is that with the Logger constructor marked as protected, the following code still won’t compile:


package p2;
import java.util.Arrays;
import p1.Logger;
public class LoggingEngine extends Logger {
	public static void main(String[] args) {
		Logger logger = new Logger(); // fails; constructor not reachable
		Arrays.asList(args).stream().forEach(s -> logger.log(s));
	}
}

The LoggingEngine does not provide the necessary inheritance relationship when calling the Logger’s constructor, so it fails. This problem, and the lack of an import, tell you that option D is incorrect.

By the way, the following code would succeed in compiling:


package p2;
import java.util.Arrays;
import p1.Logger;
public class LoggingEngine extends Logger {	
	LoggingEngine() {
		super(); // can reach superclass’s protected constructor
	}	
	public static void main(String[] args) {
		LoggingEngine logger = new LoggingEngine(); // succeed
		Arrays.asList(args).stream().forEach(s -> logger.log(s));
	}
}

The correct answer is option C.

Simon Roberts

Simon Roberts joined Sun Microsystems in time to teach Sun’s first Java classes in the UK. He created the Sun Certified Java Programmer and Sun Certified Java Developer exams. He wrote several Java certification guides and is currently a freelance educator who publishes recorded and live video training through Pearson InformIT (available direct and through the O’Reilly Safari Books Online service). He remains involved with Oracle’s Java certification projects.

Mikalai Zaikin

Mikalai Zaikin is a lead Java developer at IBA IT Park in Minsk, Belarus. During his career, he has helped Oracle with development of Java certification exams, and he has been a technical reviewer of several Java certification books, including three editions of the famous Sun Certified Programmer for Java study guides by Kathy Sierra and Bert Bates.

Share this Page