The Oracle Certified Professional: Java SE 17 Developer exam objective 13.1 focuses on applying secure coding practices — this is about protecting your software from attacks and data breaches. For a complete beginner, understanding security is like learning to lock your front door before leaving home: it seems obvious, but without it, everything you build is vulnerable. If you skip these practices, your code can be exploited by hackers, crashed by bad input, or corrupted by accidental changes — and the 1Z0-829 exam will test whether you know how to prevent that.
Jump to a section
A simple way to picture Security and Best Practices
A dinner service goes badly when a chef uses unwashed utensils from the floor, causing customers to get food poisoning. This leads to the restaurant being shut down by the health inspector.
Now think of writing Java code like running a restaurant kitchen. The kitchen has many chefs (different parts of your code), ingredients (data), and recipes (methods). In a secure kitchen, every chef follows strict rules: they wash their hands before handling food, they label and date every ingredient, and they never use the same knife for raw chicken and salad. These rules are like secure coding practices. Encapsulation is like keeping the ingredients in sealed, labelled containers so only the right chef can access them. Immutability is like using a pre-prepared, sealed salad pack that cannot be changed once it arrives — no one can add or remove ingredients after it is sealed. Input validation is like the head chef tasting every sauce before it goes out to a customer, checking that it is not spoiled or contaminated. If a chef ignores these rules, the kitchen becomes dangerous — data gets corrupted (like spoiled ingredients), security holes appear (like unlocked back doors), and the whole system crashes (like the restaurant being shut down). The health inspector’s final report is like a security audit: it catches every violation. Secure coding practices are your kitchen’s health code — follow them, and your software stays safe, stable, and trustworthy.
Security in Java is not just about fancy encryption or firewalls. At its heart, secure coding means writing code that behaves predictably and cannot be hijacked by unexpected inputs or malicious actors. For the 1Z0-829 exam, you need to understand three core pillars: encapsulation, immutability, and input validation. Let us break each one down from the ground up.
Encapsulation is the practice of hiding the internal state of an object and only allowing access through controlled methods. Imagine you have a bank account object. The balance should not be a public field that anyone can change directly. Instead, you make the field private and provide public methods like getBalance() and deposit(). This way, you can add checks — for example, preventing a deposit of a negative amount. In Java, encapsulation is achieved using access modifiers: private, protected, and public. Private means the field or method is only accessible within the same class. Protected allows access by subclasses and classes in the same package. Public means accessible from anywhere. The key idea is: always make fields private unless you have a very good reason not to. Then provide public getter and setter methods if needed, and add validation inside those methods. This prevents other parts of your program from putting your data into an invalid state.
Immutability is the concept that once an object is created, its state cannot be changed. This might seem restrictive, but it is incredibly powerful for security. If an object is immutable, no code can modify it after creation. This eliminates entire categories of bugs and security flaws. In Java, you create an immutable class by following a few rules:
Make the class final so it cannot be subclassed (a subclass could introduce mutable behaviour).
Make all fields final so they can only be assigned once (in the constructor).
Do not provide any setter methods.
If the class contains mutable objects (like arrays or lists), ensure that those objects are not directly returned or modified. Instead, return defensive copies or unmodifiable versions.
Set all fields in the constructor and do not change them afterwards.
A classic example is the String class in Java. Strings are immutable. When you call toUpperCase() on a String, you get a new String object back — the original remains unchanged. This is why Strings are safe to use as keys in HashMaps or as parameters in multithreaded code. Immutable objects are inherently thread-safe and cannot be tampered with by malicious code.
Input validation is the process of checking data that comes into your program from external sources — user input, files, network connections, database queries, etc. If you do not validate input, attackers can inject malicious data that causes your program to behave in unexpected ways. For example, SQL injection occurs when user input is concatenated directly into a SQL query. If a user types ' OR 1=1; -- into a login form, and you build a query like "SELECT * FROM users WHERE username = '" + input + "';", the attacker can bypass authentication entirely. The solution is to use parameterised queries (PreparedStatement in Java) which treat user input as data, not executable code.
Similarly, if your program expects a number and receives a string, you must validate that the input is indeed a number before processing it. Java provides built-in mechanisms for this: using Integer.parseInt() inside a try-catch block, or using regular expressions to check the format. Input validation should happen as early as possible, ideally at the boundary of your system (when the data first arrives). Never assume that data is safe just because it comes from a trusted source — always validate.
Why do these practices matter? Because they prevent common vulnerabilities that are tested in the 1Z0-829 exam:
Malicious code injection (SQL, command injection, cross-site scripting)
Unauthorised access to or modification of data (broken encapsulation)
Data corruption or race conditions (mutable shared state)
Denial of service (unvalidated input causing crashes or infinite loops)
In earlier days of software development, many programs trusted all input and exposed internal state publicly. This led to massive security breaches. Modern secure coding practices like encapsulation, immutability, and input validation are the industry standard. The exam expects you to recognise when code is vulnerable, and to know the correct Java syntax to apply these protections.
Identify the Data
First, determine what data in your class needs protection. For example, a bank account has a balance, account number, and transaction history. This step is crucial because you cannot secure what you do not know about. Insecure applications often leak or expose data that the developer did not even realise was sensitive.
Apply Encapsulation with Access Modifiers
Mark all fields as private. Provide public getters for read-only access if needed. For fields that require modification, provide a setter but include validation logic inside it — check that the value is not null, within expected range, or matches a pattern. This step ensures that data cannot be placed into an invalid state from outside the class.
Design for Immutability Where Possible
If the data should never change after creation (like a transaction record or a configuration object), follow the immutable class rules: make the class final, all fields final, initialise everything in the constructor, and return defensive copies of any mutable objects. This step eliminates side effects and makes the code thread-safe.
Validate All Inputs at the Boundary
When data enters your system — whether from a user, a file, a database, or a network call — validate it immediately. Check for null, empty, length, format (e.g., regex), and range. Use try-catch blocks for numeric parsing. Reject invalid data early rather than letting it propagate through your program. This step prevents injection attacks and logical errors.
Use PreparedStatement for Database Queries
When your Java code interacts with a SQL database, never concat user input into a query string. Instead, use PreparedStatement with question mark placeholders. Set the parameters using setString(), setInt(), etc. This step automatically escapes malicious input and completely prevents SQL injection, one of the most common and dangerous vulnerabilities.
Return Defensive Copies of Mutable Objects
When a getter method returns a mutable object such as an array, List, or Date, create a copy of that object and return the copy. For example, return new ArrayList<>(internalList) or return internalArray.clone(). This prevents the caller from modifying the internal state of your object. This step preserves both encapsulation and immutability.
Test the Security Boundaries
Write unit tests that try to break your security: attempt to access private fields via reflection (the exam tests that you know not to rely on that in production), try to modify returned mutable objects, pass invalid data to setters, and attempt SQL injection through the user input path. These tests confirm that your protections work and help you catch regressions when code changes.
An IT professional working on a banking application at a fintech company uses secure coding practices every single day. Let us walk through a concrete scenario: building a customer account management system.
First, the developer defines a Customer class. They use encapsulation to protect the customer's sensitive data. The fields such as accountNumber, balance, and socialSecurityNumber are marked private. No other class can directly set the balance or read the social security number without going through controlled methods. The developer provides a public getBalance() method, but this method does not expose the balance as a mutable object — it returns a copy of a BigDecimal (an immutable type) or a defensive copy of a mutable object like a Date. This prevents other parts of the system from modifying the balance after it is retrieved.
Second, the developer creates an immutable Transaction class. Once a transaction is recorded (with an amount, timestamp, and type), that record must never change. Audit logs, regulatory compliance, and legal requirements all depend on immutability. To ensure this, the developer makes the class final, all fields final, and ensures that the constructor initialises everything. If the transaction involves a list of items, the constructor makes a defensive copy of that list before storing it. This way, even if the caller later modifies their list, the stored transaction is unaffected.
Third, the developer implements input validation for every method that accepts user input. For example, a method to transfer funds accepts an account number and an amount. The developer immediately validates:
The account number is not null or empty, and matches a valid format (e.g., 10 digits using a regular expression).
The amount is positive and does not exceed the account's current balance.
The transfer method uses a PreparedStatement to avoid SQL injection when updating the database.
These validations happen before any business logic runs. If validation fails, the method throws a custom exception (e.g., InvalidTransferException) with a clear message, and the calling code handles it gracefully without crashing the application.
Additionally, the professional uses the principle of least privilege. They ensure that methods and classes do not expose more functionality than necessary. For example, getter methods for internal IDs are omitted if not needed. They also use dependency injection to avoid hardcoding database credentials — those are stored in encrypted configuration files, not in the code.
Finally, the professional writes unit tests for each validation rule and each encapsulation boundary. They test that private fields are inaccessible from outside the class, that immutable objects throw exceptions if someone tries to modify them (which they should not because there are no setter methods), and that invalid inputs are caught early with meaningful error messages. This is not theoretical — it is the daily reality of writing secure, maintainable code in any production environment.
The 1Z0-829 exam tests Security and Best Practices primarily through multiple-choice questions that require you to identify secure coding patterns and recognise vulnerable code. You will not be asked to write a full program, but you will need to read code snippets and decide if they are secure or not. The exam loves to test your understanding of access modifiers, immutable classes, and input validation techniques.
Here are the exact concepts you must master:
Encapsulation with access modifiers: You must know what private, protected, public, and default (package-private) mean. The exam will show you a class with fields that have inappropriate access modifiers and ask you to identify which modifier makes it encapsulated. The correct answer is to make fields private and provide public getters/setters if necessary. They also test that encapsulating classes should not expose mutable objects directly — you must return defensive copies.
Immutable class design: You must be able to list the rules for creating an immutable class: declare the class final, make all fields final, do not provide setter methods, return defensive copies of mutable objects, and set everything in the constructor. The exam might show a class that breaks one rule — for example, a setter method for a final field (which would cause a compilation error), or a class that is not final (allowing a subclass to add mutable behaviour). You need to spot the flaw.
Input validation and SQL injection: The exam will test how to safely handle user input in SQL queries. They will show code that concatenates strings into a query (vulnerable) versus code that uses PreparedStatement with placeholders (safe). You must recognise that PreparedStatement prevents SQL injection because it escapes input automatically. They also test that you should validate numeric ranges, null checks, and string patterns.
Common trap patterns include:
The exam might present a class with public fields and ask if it is encapsulated. Many beginners think "yes, because the fields have getters" but forget that the fields themselves are public. Encapsulation requires private fields.
They might show an immutable class that returns a reference to a mutable object directly from a getter. A beginner might think it is fine, but the correct answer is that it breaks immutability because the caller can modify the object. The safe way is to return a copy (e.g., new ArrayList<>(internalList)).
They might ask which access modifier should be used for a class that should only be accessible within its own package. The answer is default (package-private) — not protected, not public, not private (classes cannot be private at top level).
They might test that an immutable class with final fields can still have mutable nested objects that are modifiable if exposed incorrectly — this is a fine distinction.
Key definitions to memorise:
Encapsulation: hiding internal state and requiring all interaction through methods.
Immutability: state cannot change after object creation.
Input validation: ensuring data meets expected format and constraints before processing.
Defensive copy: a new copy of a mutable object created to prevent external modification.
PreparedStatement: a Java interface that precompiles SQL statements and safely separates SQL logic from user input.
You will also see questions about the principles of least privilege (generally, in conjunction with access modifiers) and about using final to enforce immutability. The exam expects you to know that default constructors are public if no access modifier is specified, and that making a constructor private is a way to prevent instantiation (singleton pattern), but that is less relevant here. Focus on the three pillars above.
Encapsulation in Java is achieved by marking all fields private and providing controlled public getter and setter methods with validation inside.
An immutable class must be declared final, all fields must be final, no setter methods should exist, and mutable objects must be defensively copied on return.
Input validation should be performed at the system boundary on all external data sources, not just user input from a web form.
SQL injection is prevented by using PreparedStatement with parameter placeholders, not by escaping strings or using string concatenation.
Returning a defensive copy of a mutable object from a getter is mandatory to preserve immutability and encapsulation.
The default (package-private) access modifier restricts access to classes within the same package only — it is a specific type of encapsulation.
Making a class final prevents subclassing, which can introduce mutable behaviour and break immutability.
Validation logic for numeric input should check both nullity and range, not just type conversion.
These come up on the exam all the time. Here's how to tell them apart.
Encapsulation
Focuses on hiding internal data to prevent unauthorised access.
Achieved using access modifiers like private and protected.
Uses getters and setters to control how data is accessed or modified.
Abstraction
Focuses on hiding implementation details to reduce complexity.
Achieved using interfaces and abstract classes.
Provides a simplified interface (API) without revealing underlying code.
Immutability
Refers to the object's state: the object cannot be modified after creation.
Achieved through class design (final class, final fields, no setters).
Example: the String class is immutable.
Final Keyword on Variables
Refers to the variable reference: the variable cannot be reassigned to point to a different object.
Achieved by adding the final keyword to a variable declaration.
Example: final List<String> list = new ArrayList<>(); the list reference cannot change, but the list object can still be modified.
PreparedStatement
Precompiles SQL and sends it separately from parameter values.
Automatically escapes user input, preventing SQL injection.
Uses placeholders (?) and setXxx() methods to bind parameters.
Statement
Sends a complete SQL string to the database as a single command.
Vulnerable to SQL injection if user input is concatenated directly.
Used for simple, non-repeated queries where parameter binding is not needed.
Defensive Copy
Returns a new copy of a mutable object, protecting internal state.
Safer for immutable classes or encapsulated objects.
Increases memory usage slightly but is the secure practice.
Direct Reference Return
Returns the exact same object reference, allowing external modification.
Breaks encapsulation and immutability.
Improves performance slightly because no copy is made, but is insecure.
Private
Accessible only within the same class.
Used for fields and methods that should not be visible to any external code.
The most restrictive access modifier for class members.
Protected
Accessible within the same package and by subclasses (even in other packages).
Used for fields or methods that subclasses need to access or override.
Less restrictive than private but more restrictive than public.
Mistake
Making all fields private is enough for full security.
Correct
Private fields are a necessary but not sufficient condition. You must also control what getter and setter methods expose, validate inputs, and return defensive copies of mutable objects. An attacker can still modify internal state if a getter returns a mutable reference.
Beginners hear 'make fields private' and assume that is the complete solution, but they miss the subtleties of exposing objects through methods. This is reinforced by oversimplified tutorials.
Mistake
Immutability means the object cannot change at all, so the reference variable must be final.
Correct
Immutability refers to the object's state, not the reference. A final reference variable means you cannot reassign that variable to point to a different object, but the object itself could still be mutable. Immutability is about the class design, not the reference keyword.
The word 'final' is used in both contexts (final class, final field) but learners confuse the reference final with object immutability. Many online examples mix the two concepts without clarification.
Mistake
Input validation is only needed for user input from a web form.
Correct
Input validation is needed for all external data sources, including files, network streams, database results, environment variables, and system properties. Malicious data can come from anywhere, not just a browser.
Most beginner examples focus on HTTP requests, so learners assume that is the only vector. They forget that an attacker could tamper with a file or intercept a network call.
Mistake
Encapsulation and abstraction are the same thing.
Correct
Encapsulation hides internal data (the 'how' of an object), while abstraction hides implementation details (the 'what' of a class). Encapsulation is about bundling data with methods and restricting access; abstraction is about providing a simplified interface. They are related but distinct.
Both concepts involve hiding complexity, and beginners often see them used interchangeably in general conversations. The exam tests the precise definitions, and confusion here is a common trip-up.
Mistake
Using prepared statements makes input validation unnecessary.
Correct
Prepared statements prevent SQL injection, but they do not validate the semantic correctness of the data. For example, they will not stop a user from entering a negative age or a string that is too long for a database column. You still need separate validation logic.
Beginners think that escaping input is the same as validating it. They do not realise that validation includes range checks, format checks, and business rule enforcement.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
A private field or method is accessible only within the same class. A protected field or method is accessible within the same package and by subclasses (even if they are in different packages). The exam tests you on these definitions frequently.
Technically yes, but the exam expects a final class for immutability. If the class is not final, a subclass could add mutable fields or override methods in a way that breaks immutability. So the safe, exam-correct answer is: an immutable class must be declared final.
The String class is designed to be immutable for security and performance reasons. Immutability makes Strings safe for multithreading, allows them to be used as keys in HashMaps without risk of hashcode change, and prevents tampering when used in network connections or file paths.
Use PreparedStatement with bind parameters (placeholders like ?) instead of concatenating user input into a query string. PreparedStatement automatically escapes the input, so even if a user types SQL commands, they are treated as literal text, not executable code.
A defensive copy is a duplicate of a mutable object created to prevent external modification. For example, if you have a private ArrayList field and your getter returns the same ArrayList reference, the caller can add or remove elements directly. By returning new ArrayList<>(internalList), you protect your internal data.
In Java, when you do not specify an access modifier for a class, it is package-private (also called default). This means the class is accessible only to other classes in the same package. It is a way to encapsulate classes that are implementation details of a package.
You've finished Security and Best Practices. Continue through the 1Z0-829 study guide to build a complete picture of the exam.
Done with this chapter?