Q8 of 40 · Core Java
Explain Java's access modifiers — public, protected, package-private, and private.
Short answer
Short answer: private restricts access to the declaring class only. Package-private (no modifier) allows access within the same package. protected allows same-package access plus subclasses in other packages. public allows access from anywhere. Choose the most restrictive modifier that still satisfies the design.
Detail
Access modifiers control visibility — who can read or call a member. Java has four levels, from most to least restrictive:
| Modifier | Same class | Same package | Subclass (other pkg) | Everywhere |
|---|---|---|---|---|
private |
✅ | ❌ | ❌ | ❌ |
| (none) | ✅ | ✅ | ❌ | ❌ |
protected |
✅ | ✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ | ✅ |
Package-private (no modifier) is the default for class members and is easily overlooked. It's useful for package-internal helpers that shouldn't be part of a public API. Test code in the same package can access package-private members directly — this is the intended mechanism for testing internals without reflection.
Principle of least privilege: always start with private and widen only when forced. Overly permissive access is a common source of tight coupling — if everything is public, refactoring becomes painful because anything might be depending on those internals.
In test automation, this appears in two contexts: (1) Page Object locators should be private — tests interact through public methods, not raw selectors. (2) If you need to test a package-private method, put the test class in the same package as the production class (src/test/java/com/example/ mirrors src/main/java/com/example/) and access it directly without @VisibleForTesting hacks.
// EXAMPLE
AccessModifiers.java
public class LoginPage {
// private — only this class accesses the locator strings
private static final String EMAIL_INPUT = "[data-testid='email']";
private static final String PASSWORD_INPUT = "[data-testid='password']";
private static final String SUBMIT_BUTTON = "[data-testid='login-btn']";
private final Page page; // page reference — nobody else needs this
// protected — subclasses (e.g. AdminLoginPage) can reuse this setup
protected LoginPage(Page page) {
this.page = page;
}
// public — the API that tests use
public void login(String email, String password) {
page.fill(EMAIL_INPUT, email);
page.fill(PASSWORD_INPUT, password);
page.click(SUBMIT_BUTTON);
}
// package-private — TestUtils in the same package can call this
// without exposing it to the whole test suite
String currentUrl() {
return page.url();
}
}