Core and Web-based Java

Operators, Branching, Loops and Arrays

PGCP-AC

Operators transform values, control statements choose execution paths, loops repeat work and arrays organize fixed-size indexed data. Correct reasoning requires following operand types, evaluation order, control transfer, array identity and runtime bounds step by step.

1. 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.

2. Arrays and boundaries

An array has a fixed length after creation and uses zero-based indexing. A new primitive array contains default primitive values; a new reference array contains null references until elements are assigned. Java multidimensional arrays are arrays of arrays, so rows can have different lengths. An enhanced for loop reads elements conveniently, but reassigning its loop variable does not replace an array entry.

int[] values = {3, 5, 7};
int sum = 0;
for (int i = 0; i < values.length; i++) sum += values[i];
System.out.println(sum); // 15

Index bounds are checked at runtime. The valid range is zero through length minus one. Array covariance permits a String array reference to be stored in an Object-array variable, but inserting an incompatible object then fails with ArrayStoreException. Prefer types that express intended operations without relying on a runtime failure to enforce them.

3. Promotion and array identity

byte small = 10;
// small = small + 1; // int result cannot be assigned without narrowing
small += 1;          // compound assignment includes its conversion
int[][] rows = { {1, 2}, {3} };
System.out.println(rows.length);    // 2
System.out.println(rows[1].length); // 1

An array object has its own identity. Copying the variable copies its reference, not its elements. Cloning a two-dimensional array duplicates the outer array but keeps references to the existing rows unless those rows are copied separately. Short-circuit expressions can guard both null references and index bounds. Evaluate the guard before the operation it protects; writing the same conditions in reverse order can throw before the guard is reached.

4. 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.

5. 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.

6. 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.

7. 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.

8. 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.

9. 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.

10. if and else

An if condition must be boolean; Java does not implicitly treat integers as conditions.

if (marks < 0 || marks > 100) {
    throw new IllegalArgumentException("marks outside range");
} else if (marks >= 75) {
    grade = 'A';
} else if (marks >= 60) {
    grade = 'B';
} else {
    grade = 'C';
}

Conditions are tested in order, so ranges should be arranged without unreachable or overlapping mistakes. Braces prevent the dangling-else and accidental single-statement problems even when a branch presently has one statement.

11. 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.

12. 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.

13. 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.

14. Array creation and initialization

Arrays are objects with a fixed length and a component type.

int[] first = new int[3];       // {0, 0, 0}
String[] names = new String[2]; // {null, null}
int[] values = {3, 5, 7};

The declaration int[] values is generally preferred over placing brackets after the variable because the type is easier to read. Length is a final array field accessed as values.length, not a method.

Valid indexes run from zero through length minus one. The JVM checks each access and throws ArrayIndexOutOfBoundsException for an invalid index. A zero-length array is valid but has no valid index.

15. Traversal and search

Use an index loop when the position is needed or entries must be replaced:

for (int index = 0; index < values.length; index++) {
    values[index] *= 2;
}

Use enhanced for when reading each value is sufficient. Common off-by-one defects use <= values.length or start at one. State the invariant: before each iteration, indexes below index have been processed and index is within zero through length.

Linear search takes O(n) comparisons in the worst case. A sorted array supports binary search, but only if the sort order and comparator agree with the search operation.

16. Copying and equality

int[] a = {1, 2, 3};
int[] alias = a;
int[] copy = a.clone();

System.out.println(a == alias); // true
System.out.println(a == copy);  // false
System.out.println(Arrays.equals(a, copy)); // true

Assignment copies the array reference. Clone creates a new array with copied component values. For reference components, those copied values are references, so element objects remain shared. System.arraycopy, Arrays.copyOf and copyOfRange support other copy requirements.

Array equals inherits identity behavior from Object. Use Arrays.equals for one-dimensional content and Arrays.deepEquals for nested array content when its recursive semantics match the domain.

17. Multidimensional and jagged arrays

Java represents a multidimensional array as an array whose components are array references.

int[][] matrix = new int[3][];
matrix[0] = new int[2];
matrix[1] = new int[5];
matrix[2] = new int[1];

Rows may have different lengths or be null. Always use matrix[row].length for that row rather than assuming a rectangular shape. Cloning matrix copies only the outer array and shares row arrays; a deep copy must clone each non-null row.

18. Array covariance

Reference arrays are covariant:

String[] strings = new String[2];
Object[] objects = strings;
objects[0] = "valid";
// objects[1] = Integer.valueOf(10); // ArrayStoreException

The variable type permits Object, but the runtime array object still has component type String. The JVM performs the store check and rejects the incompatible value. Generic collections instead reject many analogous type mismatches at compile time and are often a better API choice.

Primitive arrays do not participate in reference-array covariance: int[] is an Object, but it is not an Object[].

19. Command-line arguments

The main parameter is a String array. Arguments need parsing and validation:

public static void main(String[] args) {
    if (args.length != 2) {
        System.err.println("Usage: java Sum <left> <right>");
        return;
    }
    try {
        int left = Integer.parseInt(args[0]);
        int right = Integer.parseInt(args[1]);
        System.out.println(Math.addExact(left, right));
    } catch (NumberFormatException ex) {
        System.err.println("Both arguments must be integers");
    } catch (ArithmeticException ex) {
        System.err.println("Sum is outside the int range");
    }
}

The shell separates arguments before Java receives them. Quoting rules belong to the shell. Parsing success does not establish domain validity, so ranges and relationships still need checks.

20. Practical considerations

  1. Two int operands perform integer division even when assigned to double.
  2. Byte, short and char arithmetic commonly promotes to int.
  3. && and || short-circuit; & and | evaluate both boolean operands.
  4. String concatenation follows left-to-right evaluation after String is reached.
  5. A do-while body executes at least once.
  6. Array length is a field, not length() or size().
  7. Valid indexes end at length minus one.
  8. Enhanced-for variable reassignment does not replace an entry.
  9. Cloning a nested array is shallow at the row-reference level.
  10. Array covariance can fail with ArrayStoreException at runtime.

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.