Java Programming
Strings, Constructors, Methods, Encapsulation and Static Members
PGCP-BDA
String immutability
A Java String cannot change after construction; apparent modifications return new strings, enabling sharing, safe hashing and predictable value behavior.
StringBuilder
StringBuilder maintains a mutable character buffer for efficient single-threaded construction through append, insert, replace and related operations.
constructor
A constructor initializes a new object, has no return type and must delegate to another constructor or superclass constructor before ordinary statements.
method overloading
Overloading declares methods with the same name but different parameter lists and is resolved from compile-time types.
Overloading declares methods with the same name but different parameter lists. Selection occurs at compile time from the declared argument types and applicable conversions.
void print(int value) { }
void print(long value) { }
void print(String value) { }
Return type alone cannot distinguish overloads. Boxing, varargs and null can create surprising or ambiguous calls. Prefer names that express different semantics when overloads no longer represent one conceptual operation.
Overloading differs from overriding: overloading is compile-time selection among signatures; overriding is runtime selection of an instance method implementation in an inheritance hierarchy.
passing data
Java passes arguments by value
access modifiers
public, protected, package access and private control which code can name a member or type; they do not make mutable state intrinsically safe.
encapsulation
Encapsulation keeps representation behind a controlled interface so invariants can be enforced without exposing every field.
immutable class
An immutable class prevents observable state changes after construction by validating inputs, controlling references and avoiding mutating methods.
static initialization
Initialization of class variables and static blocks when the JVM initializes a class, in their textual order.
Constructors
A constructor has the class name and no return type. Writing void Account() declares a method, not a constructor. Constructors can have access modifiers and can be overloaded by parameter list.
public BankAccount(String number) {
this(number, 0);
}
If a class declares no constructor, the compiler supplies a default no-argument constructor whose accessibility follows the class. That generated constructor implicitly invokes the superclass no-argument constructor. Once any constructor is declared, the automatic default is not supplied.
A constructor can be private to control creation through factories. Abstract classes can have constructors because subclass construction must initialize the inherited portion.
String objects and immutability
String represents a sequence of UTF-16 code units and is immutable. Once constructed, its contents never change. Methods that appear to modify a string return another string:
String original = "core java";
String upper = original.toUpperCase();
System.out.println(original); // core java
System.out.println(upper); // CORE JAVA
Immutability makes strings safe to share, suitable as map keys and naturally thread-safe as values. It also lets the JVM reuse literal objects. Reassignment changes which object a variable refers to; it does not modify the earlier String.
Unicode characters outside the Basic Multilingual Plane use a surrogate pair, so length() counts UTF-16 code units rather than always counting visible characters. Use code-point APIs when a task requires Unicode code points rather than indexes compatible with ordinary String methods.
Immutability
An immutable object exposes no observable state change after construction. A practical immutable class is commonly final, keeps fields private and final, validates construction, offers no mutators and controls mutable inputs and outputs.
public final class Course {
private final String title;
private final List<String> topics;
public Course(String title, List<String> topics) {
this.title = Objects.requireNonNull(title);
this.topics = List.copyOf(topics);
}
public List<String> topics() { return topics; }
}
Final fields alone are insufficient if they reference mutable objects that callers can change. List.copyOf creates an unmodifiable snapshot under its element-reference semantics. Elements must also be immutable or defensively copied if their mutation would change the Course's observable state.
Initialization and association example
Suppose Employee contains a Department reference. Constructing an Employee array creates slots for employee references, not an Employee in every slot. Each intended employee must be created and assigned. Creating an Employee that points to an existing Department does not clone that Department; several employees can share it deliberately.
Instance fields are initialized before the constructor completes and superclass construction precedes the subclass constructor's main body under the Java 17 model. Static initialization belongs to class initialization rather than to every object creation. This distinction explains why an object counter can be static while a particular employee's salary must normally be an instance field. Constructor validation should establish a usable state before the reference is published to other code, especially to other threads.
Constructor chaining
this(...) delegates to another constructor in the same class. super(...) selects a superclass constructor. Under Java 17, an explicit constructor invocation must be the first statement and a constructor cannot contain both.
If neither appears, the compiler inserts an implicit super() when legal. If the superclass lacks an accessible no-argument constructor, the subclass constructor must explicitly select an accessible alternative.
Chaining should centralize validation and initialization rather than duplicate it. A recursive chain is rejected because it could never reach actual object initialization.
Class members and object state
A class declaration introduces a reference type. Its instance fields describe per-object state, instance methods describe operations on that state and constructors establish new instances.
public final class BankAccount {
private final String number;
private long balanceInPaise;
public BankAccount(String number, long openingBalance) {
if (number == null || number.isBlank()) {
throw new IllegalArgumentException("Account number is required");
}
if (openingBalance < 0) {
throw new IllegalArgumentException("Opening balance cannot be negative");
}
this.number = number;
this.balanceInPaise = openingBalance;
}
public long balanceInPaise() { return balanceInPaise; }
}
Every account has its own number and balance. The private representation is measured in the smallest currency unit, so floating-point rounding does not enter ordinary balance arithmetic. The constructor prevents an invalid account from being created.
Instance initialization order
For ordinary class construction, reason in this order:
- Memory for the object is allocated and fields receive default values.
- The selected constructor begins by invoking another constructor.
- Superclass construction completes before subclass instance initialization.
- Instance field initializers and instance initializer blocks for the current class run in textual order.
- The current constructor body runs.
class Sample {
private int value = initializeValue();
{ value += 2; }
Sample() { value += 3; }
private int initializeValue() { return 10; }
}
After field initialization, the block and the constructor body, value is 15. Do not call overridable instance methods from constructors; dynamic dispatch can reach subclass state before the subclass has initialized it.
References and shared data
Java always passes arguments by value. A copied object reference still denotes the same object, so a method can mutate shared state; assigning a new reference to the parameter cannot replace the caller's variable. Static fields belong to the class rather than a particular object, while instance fields belong to individual objects. A static method has no instance this reference.
class Account {
private int balance;
Account(int initial) {
if (initial < 0) throw new IllegalArgumentException();
balance = initial;
}
int balance() { return balance; }
}
Association is a general relationship between objects. Aggregation describes a whole that refers to independently existing parts; composition describes a stronger ownership relationship. These are modeling distinctions rather than separate Java keywords. An employee belonging to a department need not inherit from Department. Choose inheritance for a valid substitutable is-a relationship and fields for has-a relationships.
Objects and invariants
A class defines fields and behavior for its instances. Encapsulation keeps representation details behind a controlled interface so that operations can preserve invariants. A bank account, for example, should reject an invalid withdrawal through a method rather than expose its balance for arbitrary modification. Private fields help establish this boundary, but meaningful validation is still required.
A constructor initializes a new instance. It has the class name and no return type. Constructors may be overloaded; when no constructor is declared, the compiler supplies a default constructor subject to superclass requirements. Under Java 17 rules, a constructor invocation using this or super must be the first statement. Instance field initialization and constructor execution follow a defined order through the inheritance chain.
this
Inside an instance context, this refers to the current receiver. It disambiguates fields from parameters and can pass the current object to another operation.
public void rename(String number) {
// this.number = number; // impossible here because number field is final
}
There is no this in a static method because no receiver object is required to call it. A lambda captures the surrounding this; an anonymous class introduces its own receiver, a distinction relevant in callback code.
Avoid publishing this from a constructor to a global collection, callback or another thread. Other code could observe a partially initialized object.
Complete model example
public final class Order {
private final String id;
private final List<LineItem> items;
public Order(String id, List<LineItem> items) {
if (id == null || id.isBlank()) throw new IllegalArgumentException("id required");
if (items == null || items.isEmpty()) throw new IllegalArgumentException("items required");
this.id = id;
this.items = List.copyOf(items);
}
public long totalInPaise() {
return items.stream().mapToLong(LineItem::amountInPaise).sum();
}
}
The constructor establishes identity and nonempty composition, the collection cannot be structurally modified through the returned reference and the total is derived rather than separately stored. Whether LineItem needs copying depends on its own immutability.
Factory methods
A static factory is a named creation method:
public static BankAccount empty(String number) {
return new BankAccount(number, 0);
}
Factories can communicate purpose, return cached instances, choose a subtype or validate before construction. Constructors remain appropriate when direct creation is clear. A factory does not automatically guarantee a new object; its contract should state identity and caching behavior where relevant.
StringBuilder and StringBuffer
StringBuilder stores a mutable character sequence:
StringBuilder builder = new StringBuilder();
for (int i = 1; i <= 3; i++) {
if (builder.length() > 0) builder.append(", ");
builder.append(i);
}
String result = builder.toString(); // 1, 2, 3
Methods such as append, insert, delete, replace and reverse modify the same builder and often return it for chaining. capacity() reports allocated storage, while length() reports used characters.
StringBuffer provides a similar API with synchronized individual methods. That synchronization adds overhead and does not automatically make a multi-call algorithm atomic. Use StringBuilder for normal local construction. Use explicit coordination or confinement when mutable text truly crosses threads.
Neither builder overrides equals for content, so compare the resulting strings or use an appropriate character-sequence comparison.
Composition versus inheritance
Inheritance models a substitutable “is-a” relationship. Composition models “has-a” behavior or data. An Employee has a Department and must not extend Department merely for access reuse.
Composition reduces coupling when a class delegates to replaceable collaborators:
public final class PayrollService {
private final TaxCalculator taxCalculator;
public PayrollService(TaxCalculator taxCalculator) {
this.taxCalculator = Objects.requireNonNull(taxCalculator);
}
}
The collaborator can be supplied and tested independently. Use inheritance only when every subclass instance satisfies the parent contract and polymorphic substitution is meaningful.
Encapsulation and invariants
Encapsulation is control over representation and state transitions, not merely marking fields private and generating getters and setters. An invariant is a condition that must remain true whenever an object is observably usable.
public void withdraw(long amount) {
if (amount <= 0) throw new IllegalArgumentException("Amount must be positive");
if (amount > balanceInPaise) throw new IllegalStateException("Insufficient balance");
balanceInPaise -= amount;
}
A public setBalance would allow callers to bypass these rules. Expose domain operations such as deposit and withdraw. Validation belongs at every public boundary that could violate the invariant; constructor-only validation is insufficient when later mutators exist.
Concatenation and text blocks
The + operator concatenates when either operand is a String. Evaluation proceeds left to right:
System.out.println("Total: " + 2 + 3); // Total: 23
System.out.println("Total: " + (2 + 3)); // Total: 5
The compiler can combine constant expressions and may translate simple concatenation efficiently. Repeated concatenation inside a loop can create many intermediate strings, so a mutable builder is preferable.
Java text blocks represent multiline text while applying defined indentation rules:
String json = """
{
"active": true
}
""";
They improve source readability but still create ordinary immutable String values.
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.