Study guide

3.2 Encapsulation and Access Modifiers

Oracle Java SE 21 Developer 1Z0-830

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Java Basics, Primitive Types, and Operators

1.1 Program Structure, Primitives, Literals, Scopes, and var 1.2 Operators, Precedence, and Type Casting 1.3 Strings, Text Blocks, and StringBuilder 1.4 Date and Time APIs (java.time)

2 Chapter 2: Controlling Program Flow & Pattern Matching

2.1 Conditional Logic: if-else and Ternary Operators 2.2 Switch Statements and Switch Expressions 2.3 Pattern Matching for switch and instanceof 2.4 Loops and Branching Statements

3 Chapter 3: Object-Oriented Design & Class Architecture

3.1 Class Declarations, Constructors, and Object Lifecycle 3.2 Encapsulation and Access Modifiers 3.3 Inheritance, Method Overriding, and Polymorphism 3.4 Abstract Classes and Interfaces 3.5 Nested, Inner, Local, and Anonymous Classes 3.6 Method Overloading, Overload Resolution, and Varargs

4 Chapter 4: Modern Java Data Modeling: Records, Enums, and Sealed Types

4.1 Java Records, Canonical/Compact Constructors, and Component Accessors 4.2 Java Enums: Fields, Methods, and Abstract Constant-Specific Behavior 4.3 Sealed Classes and Interfaces: permits, final, non-sealed, and Hierarchy Architecture 4.4 Advanced Record Patterns (JEP 440), Nested Deconstruction, and Sealed Hierarchy Exhaustiveness

5 Chapter 5: Exception Handling and Annotations

5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions 5.2 Exception Handling: try, catch, finally, and Multi-Catch 5.3 Resource Management: try-with-resources and AutoCloseable 5.4 Custom Exceptions and Best Practices 5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types

6 Chapter 6: Generics and the Collections Framework

6.1 Generics Fundamentals and Generic Methods 6.2 Wildcards, Type Bounds, and Type Erasure 6.3 Core Collections: Lists, Sets, Queues, and Maps 6.4 Sequenced Collections in Java 21 6.5 Sorting, Comparable, and Comparator 6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class

7 Chapter 7: Functional Programming & Lambda Expressions

7.1 Built-in Functional Interfaces in java.util.function 7.2 Lambda Expressions, Variable Scopes, and Effectively Final 7.3 Method References: Four Varieties and Edge Cases 7.4 Working with java.util.Optional

8 Chapter 8: The Stream API and Data Pipelines

8.1 Stream Creation and Intermediate Operations 8.2 Terminal Operations and Reductions 8.3 Advanced Collectors, Grouping, Partitioning, and Teeing 8.4 Primitive Streams and Parallel Stream Processing

9 Chapter 9: Concurrency, Multithreading, and Virtual Threads

9.1 Thread Fundamentals, Creation, and Lifecycle 9.2 Thread Safety, Synchronization, and the Java Memory Model 9.3 Concurrency Utilities: ExecutorService, Callable, and Future 9.4 Concurrent Collections, Atomics, and Explicit Locks 9.5 Virtual Threads in Java 21 (Carrier Threads, Pinning, and Best Practices)

10 Chapter 10: The Java Platform Module System (JPMS)

10.1 Module Architecture and module-info.java Declarations 10.2 Module Directives: requires, exports, opens, provides, and uses 10.3 Modular Compilation, Packaging, and CLI Execution 10.4 Module Types, Migration Strategies, ServiceLoader SPI, and JLink

11 Chapter 11: Java I/O, NIO.2, and Object Serialization

11.1 Classic Java I/O Streams and Readers/Writers 11.2 Java NIO.2 File API: Path, Paths, and File System Operations 11.3 Advanced NIO.2 Stream Operations and Directory Traversal 11.4 Java Object Serialization and Record Serialization

12 Chapter 12: Implementing Localization and Internationalization

12.1 Locales and Resource Bundles 12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages

3.2 Encapsulation and Access Modifiers

Key Takeaways Explain This Topic AI

Java provides four access levels: public, protected, package-private (default), and private, with visibility strictly bounded by packages and inheritance hierarchies.

The protected modifier grants access within the same package and to subclasses in different packages, but cross-package subclass access requires reference variables of that subclass type or its subtypes.

Defensive copying is essential on both constructor input and getter output to prevent external callers from mutating private collection or array state.

The volatile modifier guarantees cross-thread memory visibility and happens-before ordering, while transient excludes fields from standard serialization streams.

Last updated: September 2026

Encapsulation and Access Modifiers

Encapsulation is the core object-oriented discipline of bundling internal state with authorized behavior while restricting unauthorized external access to implementation details. In Java SE 21, encapsulation is enforced through access modifiers, defensive copying strategies, robust class invariants, and non-access modifiers.

The Four Java Access Levels and Visibility Boundaries

Java provides four distinct visibility levels governed by three explicit keywords ( public , protected , private ) and one implicit default level (package-private):

Access Level Declaring Class Same Package Subclass (Different Package) World (Different Package & Non-Subclass)

  • public Yes Yes Yes Yes
  • protected Yes Yes Yes (via inheritance only) No
  • (default / package-private) Yes Yes No No
  • private Yes No No No
  • Member vs. Top-Level Visibility Rules

Top-Level Types : Top-level classes, records, and interfaces may only be declared public or package-private (default). Specifying private or protected on top-level type declarations causes a compile-time error.

Class Members : Fields, methods, constructors, and nested types declared within a class body can utilize all four access modifiers ( public , protected , package-private, private ).

Interface Members : In interfaces, constant fields are implicitly public static final , abstract methods and default methods are implicitly public , and private helper methods are explicitly private . Specifying protected inside an interface causes a compile-time error.

JavaBeans Standards and Accessor Conventions

The JavaBeans specification provides standardized patterns for encapsulating class properties: public class UserProfile {

  • private String username;
  • private boolean active; // Primitive boolean
  • private Boolean verified; // Wrapper Boolean
  • private int loginAttempts;
  • // Standard getter and setter for reference / non-boolean types
  • public String getUsername() { return username; }
  • public void setUsername(String username) { this.username = username; }
  • // JavaBeans convention for primitive boolean: isActive()
  • public boolean isActive() { return active; }
  • public void setActive(boolean active) { this.active = active; }
  • // EXAM TRAP: Wrapper Boolean MUST use getVerified(), NOT isVerified()
  • public Boolean getVerified() { return verified; }
  • public void setVerified(Boolean verified) { this.verified = verified; }
  • // Validation-backed mutator protecting invariants
  • public void setLoginAttempts(int loginAttempts) {
  • if (loginAttempts < 0) {
  • throw new IllegalArgumentException("Login attempts cannot be negative");
  • }
  • this.loginAttempts = loginAttempts;
  • }
  • }
  • Defensive Copying: Mutable Objects and Collections

A frequent vulnerability in object-oriented architecture is reference aliasing , where external callers retain references to mutable objects encapsulated inside a class. import java.util.*;

  • public final class SecurityToken {
  • private final String tokenId;
  • private final Date expirationDate;
  • private final List<String> scopes;
  • private final int[] rawKeys;
  • public SecurityToken(String tokenId, Date expirationDate, List<String> scopes, int[] rawKeys) {
  • this.tokenId = Objects.requireNonNull(tokenId, "tokenId required");
  • // 1. Defensive copy of mutable Date object on input
  • this.expirationDate = expirationDate != null ? new Date(expirationDate.getTime()) : null;
  • // 2. Defensive copy of Collection input (protects against caller mutating the list)
  • this.scopes = scopes != null ? new ArrayList<>(scopes) : new ArrayList<>();
  • // 3. Defensive copy of primitive array on input
  • this.rawKeys = rawKeys != null ? rawKeys.clone() : new int[0];
  • }
  • public String getTokenId() {
  • return tokenId; // String is immutable, safe to return directly
  • }
  • // Defensive copy on getter output
  • public Date getExpirationDate() {
  • return expirationDate != null ? new Date(expirationDate.getTime()) : null;
  • }
  • // Return unmodifiable view or defensive snapshot
  • public List<String> getScopes() {
  • return Collections.unmodifiableList(scopes);
  • // Alternatively in Java 10+: return List.copyOf(scopes);
  • }
  • // Defensive copy of internal array on output
  • public int[] getRawKeys() {
  • return rawKeys.clone();
  • }
  • }
  • Shallow vs. Deep Immutability

Collections.unmodifiableList() & List.copyOf() : Provide shallow immutability. The collection container cannot be resized, replaced, or sorted, but if the elements stored inside the collection are mutable objects (e.g., List<Customer> ), callers can still invoke mutating methods on those individual elements!

Deep Immutability : Requires that every nested object and contained element is also strictly immutable or defensively cloned upon access.

Deep Dive: Cross-Package protected Access Rules

The protected modifier is heavily tested on the 1Z0-830 exam. It permits access to:

Any class located within the exact same package .

Any subclass located in a different package , but strictly through inheritance or through an explicit reference variable typed to that subclass (or its descendants) .

JLS §6.6.2 Subclass Access Rule

When code in a subclass in package pkg.child accesses a protected member declared in a superclass in package pkg.parent , the access is only legal if the object reference expression is of the subclass type or one of its subtypes. Accessing the protected member on a reference of the superclass type or a sibling subclass type fails compilation! // File: pkg/parent/Vehicle.java

package pkg.parent;

  • public class Vehicle {
  • protected int speed = 60;
  • protected void honk() {
  • System.out.println("Beep!");
  • }
  • } // File: pkg/child/Car.java
  • package pkg.child;

import pkg.parent.Vehicle;

  • public class Car extends Vehicle {
  • public void testProtectedAccess() {
  • // CASE 1: Direct access via inherited 'this' -> VALID
  • System.out.println(this.speed);
  • this.honk();
  • // CASE 2: Access via Car reference (Subclass reference) -> VALID
  • Car myCar = new Car();
  • System.out.println(myCar.speed);
  • myCar.honk();
  • // CASE 3: Access via SportsCar reference (Subtype of Car) -> VALID
  • SportsCar sportsCar = new SportsCar();
  • System.out.println(sportsCar.speed);
  • sportsCar.honk();

// CASE 4: Access via Vehicle reference (Superclass reference) -> COMPILE ERROR!

  • Vehicle rawVehicle = new Vehicle();
  • // System.out.println(rawVehicle.speed); // ERROR: speed has protected access in Vehicle
  • // rawVehicle.honk(); // ERROR: honk() has protected access in Vehicle

// CASE 5: Access via Sibling reference (Boat extends Vehicle) -> COMPILE ERROR!

  • // Boat boat = new Boat();
  • // System.out.println(boat.speed); // ERROR: speed has protected access in Vehicle
  • }
  • }
  • class SportsCar extends Car {}
  • Non-Access Modifiers on Fields, Methods, and Classes

Non-access modifiers qualify runtime characteristics, threading behavior, and immutability invariants:

The final Modifier

On a Class : Forbids extension ( public final class SecurityEngine ).

On a Method : Forbids overriding or hiding in subclasses ( public final void validate() ).

On a Variable / Field : Guarantees single assignment. A blank final instance field must be assigned exactly once in every constructor path or instance initializer block; failure to initialize causes a compile-time error.

The static Modifier

Binds the member to the class namespace rather than individual object instances.

Static fields are shared across all instances loaded by the same ClassLoader.

Static methods cannot access this , super , or instance fields without an explicit object reference.

The transient Modifier

Signals to the standard Java Object Serialization mechanism ( java.io.Serializable ) that the field should not be serialized into the byte stream.

Upon deserialization, transient fields are reset to their default primitive values ( 0 , false , 0.0 ) or null for reference types.

The volatile Modifier

Ensures that reads and writes to a field go directly to main memory rather than being cached in thread-local CPU caches.

Establishes a happens-before relationship, guaranteeing memory visibility across threads.

Exam Distinction : volatile guarantees visibility , but does not guarantee atomicity (compound operations like count++ still require explicit synchronization or AtomicInteger ).

Summary Comparison of Non-Access Modifiers

Modifier Permitted On Primary Architectural Effect Common Exam Trap

final Classes, Methods, Variables, Parameters Prevents subclassing, overriding, or variable reassignment. Final reference prevents pointer reassignment, not internal state mutation.

static Methods, Fields, Nested Classes, Blocks Associates member with class type; eliminates instance requirement. Static methods cannot invoke instance methods or access this .

transient Fields only Excludes field from default serialization stream. Deserialization resets transient fields to default zero/null values without running constructors.

volatile Fields only Enforces cross-thread memory visibility and prevents instruction reordering. Does not make compound operations (like x++ ) thread-safe.

Common 1Z0-830 Exam Traps

Cross-Package Protected Access : Remember that a subclass in another package CANNOT access a protected member through a parent reference ( Parent p = new Parent(); p.protectedField; -> ERROR).

Mutable References in Final Fields : Declaring final List<String> list prevents reassigning list = new ArrayList<>() , but does NOT prevent calling list.add("new item") .

Package-Private Class Invisibility : A public method inside a package-private class cannot be called from another package because the enclosing class itself is completely invisible.

Top-Level Modifier Violations : Attempting to declare protected class MyClass or private class MyClass at the top level of a file produces immediate compiler errors.

Loading diagram... Java Access Modifier Boundary Matrix