Java Programming

Java Platform, JVM, JDK, Types, Operators and Control Flow

PGCP-BDA

JVM architecture

The JVM loads and verifies classes, manages runtime memory, executes bytecode through interpretation and compilation and provides services such as garbage.

JDK and JRE

A JDK contains development tools plus a runtime, while the runtime supplies the JVM and libraries required to execute Java applications.

bytecode

Java bytecode is a platform-neutral instruction format stored in class files and executed or compiled by a JVM.

class loading

Class loading locates class bytes, verifies and links them, then initializes static state when required by the language rules.

Java class structure

A Java source unit can declare a package and imports followed by types containing fields, constructors, methods and nested types under access rules.

primitive and reference types

Primitive variables contain language-level values, while reference variables contain references to objects or arrays and may contain null.

wrapper classes

Wrapper classes such as Integer and Double represent primitive values as objects and supply parsing, constants, comparison and utility methods.

Java operators

Java operators perform arithmetic, comparison, logical, bitwise, assignment.

if and switch

if selects by a Boolean condition, while switch selects among compatible constants, enum values, strings or modern pattern cases depending on Java version.

for loop

A Java for loop combines initialization, continuation test and update around a repeated statement

while and do-while

while tests before each iteration, whereas do-while executes the body once before its first test.

Runtime areas and primitive ranges

The JVM's logical runtime areas include per-thread stacks and program counters, shared heap storage and class-related runtime structures. Stack frames contain local-variable slots and an operand stack used while executing bytecode. Class loading, verification, preparation, resolution and initialization are related but distinct stages; declaring a reference is not the same operation as constructing an instance.

PrimitiveWidth or role
byteSigned 8-bit integer
shortSigned 16-bit integer
intSigned 32-bit integer
longSigned 64-bit integer
charUnsigned 16-bit UTF-16 code unit
float32-bit floating-point value
double64-bit floating-point value
booleanLogical true or false; no language-level sizeof operator

For a signed b-bit integer, the range is minus 2 raised to b-1 through 2 raised to b-1 minus one. A narrowing cast can discard information. Integer arithmetic overflow wraps according to Java's fixed-width rules rather than behaving like C++ signed overflow.

JDK, runtime and JVM

The Java Development Kit (JDK) supplies tools for developing and running Java software. These include the compiler javac, launcher java, documentation and archive tools, the standard libraries and a runtime implementation.

The JVM is the abstract execution environment defined for class files. A concrete JVM implementation loads and verifies classes, manages runtime data, executes bytecode, compiles frequently used paths when profitable, manages threads and performs garbage collection.

Historically, the term JRE described a separately distributed runtime containing a JVM and libraries without development tools. Modern JDK releases and application runtime images have changed packaging practices, but the conceptual distinction between development tools and runtime facilities remains useful.

“Write once, run anywhere” means compiled class files target a defined runtime contract. It does not mean one native executable runs directly on every processor. Each platform needs a compatible JVM and platform-dependent libraries or assumptions can still reduce portability.

Loop forms

LoopAppropriate use
whileRepeat while a condition remains true
do-whileBody must run before first condition check
basic forInitialization, condition and update form one loop
enhanced forRead each array or iterable element conveniently
int sum = 0;
for (int value : values) {
    sum += value;
}

An enhanced-for variable receives each element value. Assigning a new primitive value to that local variable does not replace the array entry. If the element is an object reference, mutating the referenced object is visible, while assigning a new reference to the loop variable still does not replace the entry.

From source to execution

Java source is compiled into class files containing bytecode. A JVM loads classes, verifies their structure, links references, initializes classes when required and executes instructions. Execution may combine interpretation and just-in-time compilation. The JDK supplies development tools such as javac as well as runtime facilities. Portability comes from a defined bytecode and runtime contract; a suitable JVM is still required for each platform.

A conventional Java 17 application starts with public static void main(String[] args). Packages organize names; imports make names convenient to use without copying code into a file. A public top-level class normally shares its name with the source file. The classpath tells tools where to locate classes and libraries. Compilation errors prevent valid class generation; runtime exceptions occur during execution of otherwise compiled code.

Primitive types

Java defines exactly eight primitive types:

TypeMeaningImportant property
byteSigned integer8 bits
shortSigned integer16 bits
intSigned integer32 bits
longSigned integer64 bits
charUTF-16 code unitUnsigned 16 bits
floatBinary floating point32 bits
doubleBinary floating point64 bits
booleanLogical valuetrue or false

For a signed integer width b, the range is -2^(b-1) through 2^(b-1)-1. Java does not define boolean as an integer and there is no language-level sizeof operator for asking primitive storage size.

A char stores one UTF-16 code unit. Many characters fit in one code unit, while supplementary Unicode characters require a surrogate pair. Code that processes user-perceived characters must not assume String.length() always counts complete characters.

Switch statements and expressions

Traditional switch statements can fall through when a case does not transfer control. This supports grouped cases but can create defects.

String name = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    default -> "Unknown";
};

Arrow-style switch rules do not fall through. A switch expression must produce a value for every possible path, using an expression or yield from a block. Supported selector and label forms depend on the Java language version; use the configured version rather than mixing newer features into an older course toolchain.

Null handling is separate from an ordinary default in the classic forms commonly used in Java 17; validate a nullable selector before switching unless the selected language feature explicitly handles it.

Practical considerations

  1. javac compiles source; java launches a class.

  2. Class files contain bytecode, not universally final native code.

  3. JDK includes development tools; JVM is the execution environment.

  4. Imports shorten names and do not copy or instantiate classes.

  5. String and arrays are reference types.

  6. Fields receive defaults; local variables require definite assignment.

  7. Char is one UTF-16 code unit, not necessarily a complete character.

  8. Integer division occurs before widening the result.

  9. Narrowing casts can lose information and integer overflow wraps.

  10. Garbage collection does not replace external-resource cleanup.

  11. Two int operands perform integer division even when assigned to double.

  12. Byte, short and char arithmetic commonly promotes to int.

  13. && and || short-circuit; & and | evaluate both boolean operands.

  14. String concatenation follows left-to-right evaluation after String is reached.

  15. A do-while body executes at least once.

  16. Array length is a field, not length() or size().

  17. Valid indexes end at length minus one.

  18. Enhanced-for variable reassignment does not replace an entry.

  19. Cloning a nested array is shallow at the row-reference level.

  20. Array covariance can fail with ArrayStoreException at runtime.

Reference types and null

Classes, interfaces, arrays, enums, records and strings are reference types. A reference variable can identify an object compatible with its declared type or contain null.

String first = new String("Java");
String alias = first;
String absent = null;

first and alias hold references to the same object. Assigning alias = null changes that variable only; it does not modify the object or the first variable. Dereferencing null produces NullPointerException at runtime.

Java passes argument values by value. Passing an object reference copies the reference value, so a method can mutate the shared object but cannot reassign the caller's variable by assigning its parameter.

Operator categories and evaluation

Java operators include arithmetic, relational, equality, logical, bitwise, shift, assignment, conditional, object-creation, type-test and access operators. Operands are generally evaluated left to right, while precedence determines grouping.

int result = 2 + 3 * 4;     // 14: multiplication groups first
int grouped = (2 + 3) * 4;  // 20

Parentheses document intended grouping and are preferable when a reader might misinterpret a mixed expression. Precedence does not mean the higher-precedence operand is evaluated first; it describes the expression tree. Side effects inside complex expressions make evaluation harder and should be separated into statements.

Numeric operations and decisions

Java applies numeric promotion before many arithmetic operations. Two int operands produce integer division, so 7 / 2 is 3; assigning that result to double afterwards does not recover the fraction. A narrowing conversion can lose information. Byte and short operands are ordinarily promoted to int in arithmetic. String concatenation uses + and evaluation order can make "sum=" + 2 + 3 different from "sum=" + (2 + 3).

Boolean short-circuit operators evaluate the right operand only when needed. This makes value != null && value.length() > 0 safe for null input. Integer division by zero throws ArithmeticException, whereas floating-point division follows floating-point rules. Selection uses if, switch and the conditional operator. Loop choice should express the stopping condition clearly; a do-while loop checks its condition after the body.

Comparison and equality

Relational operators compare compatible primitive numeric values and produce boolean results. Primitive equality compares primitive values. Reference equality compares whether two references identify the same object or are both null.

String first = new String("Java");
String second = new String("Java");
System.out.println(first == second);      // false
System.out.println(first.equals(second)); // true

Use content equality methods for domain objects according to their contract. Calling first.equals(second) fails if first is null; Objects.equals(first, second) is null-safe.

Floating-point NaN is unequal to every value including itself. Compare floating-point calculations with a tolerance only when the domain supports that interpretation.

Conditional operator and string concatenation

The conditional operator selects one of two expressions:

String category = score >= 40 ? "pass" : "fail";

Its result type is determined from both branches under language rules, not simply from the branch taken at runtime.

String concatenation is left-associative:

System.out.println("sum=" + 2 + 3);       // sum=23
System.out.println("sum=" + (2 + 3));     // sum=5

Once the left side becomes a String, later plus operations concatenate. Parentheses force arithmetic before concatenation.

Logical, bitwise and shift operators

&& and || operate on booleans and short-circuit. &, | and ^ can operate bitwise on integers; with boolean operands they evaluate both sides.

if (text != null && !text.isEmpty()) {
    System.out.println(text);
}

The null test must run first. Reversing the operands dereferences null before reaching the guard. Short-circuiting is useful for bounds checks as well.

Shift operators move integer bit patterns. << shifts left, >> sign-extends a signed right shift and >>> zero-fills a right shift. Shift distances are masked according to the promoted left operand's width, an important output-question detail.

Break, continue, return and labels

break exits the nearest loop or switch. continue skips to the next iteration of the nearest loop. return exits the current method. Labels can target an enclosing loop:

outer:
for (int row = 0; row < matrix.length; row++) {
    for (int column = 0; column < matrix[row].length; column++) {
        if (matrix[row][column] == target) break outer;
    }
}

Labels can simplify a rare nested-loop exit, but extracting a search method that returns a result is often clearer. Every loop needs a demonstrable progress or termination condition.

Packages, imports and file naming

A package organizes types into a namespace and supports access control. Its declaration should correspond to the source organization expected by the build. A public top-level class is normally stored in a source file with the same case-sensitive name.

An import makes a type or static member available by a shorter source name. It does not copy source code, construct objects or necessarily load the class at that moment. Types in java.lang are implicitly available by simple name. Types in the same package need no import.

Wildcard imports do not recursively import subpackages and two imported types with the same simple name require disambiguation through a fully qualified name.

Strings and equality

String is a final reference type whose instances are immutable. Concatenation produces a result without changing existing strings. String literals may refer to interned instances, but program logic should compare string content using equals rather than relying on reference identity with ==.

String input = new String("java");
System.out.println(input == "java");      // false: identity
System.out.println(input.equals("java")); // true: content

Immutability supports sharing and safe use as map keys when equality and hash behavior are consistent. For repeated text construction, use a mutable builder rather than creating excessive intermediate results.

Widening, narrowing and overflow

A widening primitive conversion generally moves to a type able to represent the source range, although moving a large integer to floating point can lose precision. A narrowing conversion requires an explicit cast and may discard high bits, range or precision.

int value = 130;
byte narrowed = (byte) value; // -126 after low-bit preservation

Fixed-width integer overflow wraps using two's-complement arithmetic. For example, Integer.MAX_VALUE + 1 produces Integer.MIN_VALUE. This is defined Java behavior, but usually indicates a domain defect. Exact arithmetic helpers such as Math.addExact can throw on overflow when detection is required.

Floating-point values include infinities, signed zero and NaN. Floating-point division by zero behaves differently from integer division by zero.

Default values and definite assignment

Instance fields, static fields and array components receive default values. Numeric fields become zero, boolean becomes false, char becomes the zero code unit and reference fields become null.

Local variables do not receive automatic default values for source-language use. The compiler performs definite-assignment analysis:

int result;
if (args.length > 0) {
    result = 10;
}
// System.out.println(result); // compile-time error: not definitely assigned

Initializing every local to an arbitrary zero merely to silence the compiler can hide missing logic. Ensure every control-flow path assigns a meaningful value before use.

Arithmetic, promotion and division

Binary numeric promotion applies before many arithmetic operations. Byte, short and char operands are promoted to int. Wider operands can promote the other side to long, float or double.

byte left = 10;
byte right = 20;
int total = left + right;

double a = 7 / 2;      // 3.0
double b = 7 / 2.0;    // 3.5
double c = (double) 7 / 2; // 3.5

In a, integer division completes before its result widens to double. Integer division by zero throws ArithmeticException. Floating-point division by zero can produce infinity or NaN according to floating-point rules.

The remainder operator % supplies the remainder after division. Its result follows Java's signed arithmetic rules, so confirm behavior for negative operands rather than assuming a mathematical modulo in every domain.

Increment and compound assignment

Prefix increment changes a variable and yields the new value; postfix increment changes it but yields the old value for the surrounding expression.

int x = 5;
int a = x++; // a=5, x=6
int b = ++x; // x=7, b=7

Avoid placing several increments of the same variable into one expression even when Java defines the evaluation; separate statements communicate intent.

Compound assignment includes an implicit conversion to the left-hand type:

byte small = 10;
small += 1;             // equivalent in effect to a narrowing conversion
// small = small + 1;   // int result cannot be assigned directly

That convenience can conceal truncation. It does not guarantee that the mathematical result fits.

Numeric promotion and arithmetic

Arithmetic on byte, short and char operands commonly promotes them to int. Thus:

byte a = 10;
byte b = 20;
int sum = a + b;
// byte wrong = a + b; // compile-time error without a narrowing conversion

If either operand has a wider numeric type, binary numeric promotion chooses an appropriate common type. Integer division discards the fractional remainder:

System.out.println(15 / 4);   // 3
System.out.println(15.0 / 4); // 3.75

The operand types decide the operation before assignment. double answer = 15 / 4; calculates integer 3 and then widens it to 3.0.

Literals

Integer literals are ordinarily int when representable. Use an L suffix for a long literal when necessary and prefer uppercase L because lowercase l resembles the digit one.

long population = 8_100_000_000L;
int mask = 0b1111_0000;
int color = 0xFF_AA_00;
double rate = 1.8e1;
float ratio = 0.75F;
char newline = '\n';

Floating-point literals are double by default; assigning one to float requires an F suffix or an explicit conversion. Underscores can improve readability between digits but cannot appear in arbitrary positions.

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.