Java Programming
Enums, Boxing, Annotations and Core Utility Packages
PGCP-BDA
enum
A Java enum is a fixed set of typed singleton instances that can contain fields, constructors, methods and per-constant behavior.
autoboxing and unboxing
Autoboxing converts a primitive to its wrapper and unboxing extracts a primitive, with possible allocation, identity surprises and null failures.
java.lang
java.lang contains automatically imported core types such as Object, String, Math, System, wrappers, Thread and fundamental exceptions.
java.util
java.util provides collections, comparators, scanners, random utilities, optional values, functional interfaces and other general-purpose facilities.
java.math
java.math provides BigInteger for arbitrary-precision integers and BigDecimal for controlled decimal arithmetic and rounding.
annotation
Structured metadata attached to a declaration or type use for compilers, tools, frameworks or runtime reflection.
annotation retention
The SOURCE, CLASS or RUNTIME policy that determines how long annotation metadata is preserved.
Optional
A container that explicitly represents either one non-null value or absence and supports conditional transformations.
utility API
A library type of reusable operations, commonly exposed through static methods for algorithms, validation or conversion.
Annotations and retention
Annotations attach structured metadata to program elements. @Override asks the compiler to verify overriding and @Deprecated marks an API that callers should avoid.
A custom annotation may declare where and how long it exists:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audited {
String value() default "";
}
SOURCE retention discards metadata after compilation, CLASS stores it in the class file without requiring runtime visibility and RUNTIME makes it available through reflection. Retention alone does not perform behaviour; a compiler, framework, annotation processor or application must inspect and act on the metadata.
Enums as fixed sets of instances
An enum models a closed set of named constants:
enum OrderStatus {
NEW, PAID, SHIPPED, CANCELLED
}
Each constant is a single instance of the enum type. Enum comparison normally uses ==, which is safe because constants are unique. An enum cannot extend another class because it already extends java.lang.Enum, but it can implement interfaces.
values() returns a new array of constants in declaration order. valueOf("PAID") returns the exact named constant; it is case-sensitive and throws IllegalArgumentException for an unknown name. name() returns the declared identifier, while toString() may be overridden for display.
Imports and name resolution
Code can use a fully qualified name:
java.time.LocalDate today = java.time.LocalDate.now();
An import makes the simple type name available:
import java.time.LocalDate;
LocalDate today = LocalDate.now();
java.lang types and types in the current package need no explicit import. A wildcard such as import java.util.*; imports accessible types from that one package; it does not import java.util.concurrent or any other subpackage. Imports do not load classes, copy code or change access permissions.
If two imported packages contain the same simple name, use at least one fully qualified name. Java has no import alias syntax. Unused imports are harmless to runtime behaviour, though compilers or style tools may report them.
Static imports
A static import allows an accessible static member to be referenced without its class name:
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
double circumference(double radius) {
return 2 * PI * radius;
}
double diagonal(double side) {
return sqrt(2) * side;
}
Use static imports when the shortened expression remains clear, such as assertion methods in tests. Heavy use can obscure which class defines a name. Like an ordinary import, a static import cannot expose a private or otherwise inaccessible member.
Enums with fields and behaviour
Enums can contain constructors, fields and methods:
enum HttpStatus {
OK(200, "Success"),
NOT_FOUND(404, "Not Found"),
SERVER_ERROR(500, "Server Error");
private final int code;
private final String description;
HttpStatus(int code, String description) {
this.code = code;
this.description = description;
}
public int code() { return code; }
public String description() { return description; }
}
Enum constructors are not invoked directly with new; constants invoke them during enum initialization. Constants may also provide class-body-specific implementations of an abstract enum method. This can replace a fragile switch when behaviour naturally belongs to each constant.
ordinal() reports declaration position starting at zero. Never persist it as a business or database identifier: inserting or reordering constants changes ordinals. Persist a stable explicit code or, when appropriate, the constant name.
Packages as namespaces
A package is a named group of related types. It prevents naming conflicts and provides an access boundary. Two projects may both have a class named User, but com.shop.model.User and com.school.model.User are different fully qualified names.
package com.acme.billing;
public class Invoice {
// ...
}
The package declaration, when present, must be the first non-comment source declaration. Convention uses lowercase, reversed domain names followed by project and feature names. Build tools normally expect the directory tree to match the package:
src/
└── com/
└── acme/
└── billing/
└── Invoice.java
The JVM identifies a type by its binary name and defining class loader. Directory placement alone does not declare the package; the source declaration does. Matching them keeps compilation, builds and maintenance predictable.
Design principles in context
Package and type design improve when responsibilities remain focused:
- Single responsibility: a type should have one coherent reason to change.
- Open–closed: add new behaviour through stable abstractions where practical instead of repeatedly modifying central conditionals.
- Substitution: a subtype must preserve its supertype's contract.
- Interface segregation: prefer small, role-specific interfaces over one broad interface that forces unused methods.
- Dependency inversion: high-level policy should depend on abstractions; concrete collaborators can be supplied through constructors.
These are reasoning tools rather than mechanical rules. For example, moving every method into a separate class would reduce cohesion rather than improve it.
Worked reachability trace
Node root = new Node();
Node child = new Node();
root.next = child;
Node alias = child;
child = null;
root = null;
After child = null, the child object remains reachable through root.next and alias. After root = null, the root object may become eligible, but the child remains reachable through alias. Only after alias is cleared or leaves its live scope can the child become eligible, assuming no other root path exists.
Shadowing and enclosing references
Nested scopes may contain same-named fields or variables:
class Outer {
int value = 10;
class Inner {
int value = 20;
void print(int value) {
System.out.println(value); // parameter
System.out.println(this.value); // Inner field
System.out.println(Outer.this.value); // Outer field
}
}
}
Qualifying names removes ambiguity. Excessive shadowing still reduces readability, so distinct names are preferable outside questions designed to test scope.
Nested-type categories
Java supports four common types declared within another context:
| Kind | Declaration location | Implicit enclosing instance | Can capture local variables |
|---|---|---|---|
| Static nested class | Member with static | No | Not applicable |
| Member inner class | Non-static member | Yes | Not applicable |
| Local class | Inside a block | According to context | Yes |
| Anonymous class | Expression at use site | According to context | Yes |
Nested types improve encapsulation when a helper belongs only to one enclosing abstraction. They should not be used merely to avoid creating a separate file when the nested type has an independent responsibility.
References, immutability and copying
A final reference cannot be reassigned, but the object may still be mutable:
final List<String> names = new ArrayList<>();
names.add("Asha"); // legal
// names = new ArrayList<>(); // illegal
An immutable class normally uses private final fields, validates during construction, exposes no mutators and protects mutable components with defensive copies. Immutability is a property of the object's observable state, not merely the presence of final references.
A shallow copy creates a new outer object while sharing referenced nested objects. A deep copy duplicates the intended object graph. “Deep” is a design policy: immutable shared values may not need duplication, while mutable owned state usually does. Copy constructors or factory methods often express this policy more clearly than clone().
Legacy Date, Calendar and SimpleDateFormat
Older code uses java.util.Date, Calendar and SimpleDateFormat. Date primarily wraps a millisecond instant despite its historical methods. Calendar is mutable and has awkward conventions such as zero-based month constants. SimpleDateFormat is mutable and not safe for unsynchronized shared use.
Prefer java.time in new code. Interoperate at boundaries:
Instant instant = legacyDate.toInstant();
Date legacy = Date.from(instant);
Do not rely on object finalization for resources associated with legacy or modern APIs. Finalization is deprecated and has no timely execution guarantee; explicit closure remains the correct model.
Splitting, joining and regular expressions
split accepts a regular expression, not a literal separator:
String[] parts = "api.example.com".split("\\.");
String path = String.join("/", "users", "42", "orders");
A dot means “any character” in regular expressions, so a literal dot must be escaped. In a Java string literal, the backslash itself is escaped, producing "\\.". Other regex metacharacters require the same care. Pattern.quote(separator) safely quotes a variable literal separator.
By default, split discards trailing empty strings. Supplying a negative limit, such as split(",", -1), preserves them. This matters when parsing row-like data where a final empty field is meaningful.
Continue learning
Related notes
Put this topic into timed practice
Open mock tests when you want full-exam pacing, or keep drilling in practice mode.