Skip to content

VOL 01 / CH 03 / LESSON 04

3.4 Lab: Expressions and Operators ​

Prerequisites: First three pages of this chapter

Estimated time: 40 minutes

Exercise environment: JDK 17+ and Bash; all files written to a temporary directory

You are ready to continue when: You can predict and verify four boundary cases, and record the reasons for any failures

bash
chapter3_lab_root=$(mktemp -d)
readonly chapter3_lab_root
mkdir -p "$chapter3_lab_root/chapter03-lab/out"
cd "$chapter3_lab_root/chapter03-lab"

Required Exercise 1: Division and Remainders ​

Save as DivisionLab.java:

java
public class DivisionLab {
    public static void main(String[] args) {
        System.out.println(10 / 3);
        System.out.println(-10 / 3);
        System.out.println(10 / 3.0);
        System.out.println(-7 % 3);
        System.out.println(Math.floorMod(-7, 3));
    }
}
bash
javac -d out DivisionLab.java
java -cp out DivisionLab

Expected output:

text
3
-3
3.3333333333333335
-1
2

Write one sentence each explaining the rules for negative numbers for / and %.

Required Exercise 2: Assignment, Promotion, and Compound Assignment ​

Save as AssignmentLab.java:

java
public class AssignmentLab {
    public static void main(String[] args) {
        int left;
        int right;
        left = right = 10;

        byte level = 127;
        level += 1;

        System.out.println(left + "," + right);
        System.out.println(level);
    }
}

Compile and run:

bash
javac -d out AssignmentLab.java
java -cp out AssignmentLab

Expected output:

text
10,10
-128

Change level += 1 to level = level + 1, and confirm the compilation fails. Don't just say "syntax differs"; explain the difference between the type of the right-hand expression and implicit narrowing.

Required Exercise 3: Short-Circuiting and Non-Short-Circuiting ​

Save as ShortCircuitLab.java:

java
public class ShortCircuitLab {
    static boolean mark(String label, boolean value) {
        System.out.println(label);
        return value;
    }

    public static void main(String[] args) {
        boolean shortAnd = mark("A", false) && mark("B", true);
        boolean fullAnd = mark("C", false) & mark("D", true);
        boolean shortOr = mark("E", true) || mark("F", false);

        System.out.println(shortAnd);
        System.out.println(fullAnd);
        System.out.println(shortOr);
    }
}
bash
javac -d out ShortCircuitLab.java
java -cp out ShortCircuitLab

The output labels should be A, C, D, and E; B and F should not appear. The three results in sequence are false, false, and true.

Required Exercise 4: String Identity and Content ​

java
import java.util.Objects;

public class EqualityLab {
    public static void main(String[] args) {
        String left = new String("apple");
        String right = new String("apple");
        String missing = null;

        System.out.println(left == right);
        System.out.println(left.equals(right));
        System.out.println(Objects.equals(missing, null));
    }
}
bash
javac -d out EqualityLab.java
java -cp out EqualityLab

Expected to be false, true, and true. In the record, mark each of the three comparisons separately to check identity, content, and null-safe content contract.

Optional Exercise 1: Prefix, Postfix, and Separate Side Effects ​

java
int count = 5;
int before = count++;
int after = ++count;
System.out.println(count + "," + before + "," + after);

Predict first, then verify the output 7,5,7. Then read but do not mimic:

java
int hardToRead = count++ + ++count;

Split it into named intermediate variables and write each step's count.

Optional Exercise 2: Floating-Point Comparison Contract ​

Choose a tolerance for each of the following three sets of values and explain your reasoning:

text
0.1 + 0.2  versus 0.3
1_000_000.0  versus 1_000_000.0001
1e-12  versus 2e-12

A single fixed absolute threshold may not be appropriate for all three scales. Without domain-specific error requirements, there is not enough information to declare "equal."

Optional Exercise 3: Maximum ​

java
int first = 15;
int second = 8;
int third = 22;

int maxOfFirstTwo = first >= second ? first : second;
int max = maxOfFirstTwo >= third ? maxOfFirstTwo : third;
System.out.println(max);

The result should be 22. Here, two named steps are used to avoid nested ternaries that can make the branch relationships unclear. In real engineering, you can also use Math.max.

Fault Experiment ​

Place the three groups of code into main separately, opening only one group at a time:

java
// int divisionByZero = 1 / 0;

// byte left = 10;
// byte right = 20;
// byte invalid = left + right;

// boolean wrong = 1;

The first group compiles; when the program reaches the integer division by zero expression, it throws ArithmeticException. The other two groups are rejected by the compiler: the sum of the two byte variables results in int, and Java does not allow replacing boolean with an integer.

You cannot simplify the second group to byte invalid = (byte) 10 + (byte) 20;. The right side of that line is a value of 30 at compile time, falling within the byte range, so the assignment conversion is allowed to compile. Record both the runtime exception and the two compile-time diagnostics separately, and do not mix the failure stages together.

Safe Cleanup ​

bash
pwd
test ! -e out || rm -r -- out

Confirm that the current directory is this experiment directory and delete only the out related to this experiment.

Acceptance and Cleanup ​

Confirm that you have actually observed integer division with positive and negative numbers, compound assignment narrowing, short-circuiting and non-short-circuiting, and string identity and content comparison. After saving the necessary experimental records, clean up:

bash
cd
ls -ld -- "$chapter3_lab_root"
rm -r -- "$chapter3_lab_root"

The next chapter will make boolean results truly control the path.

→ Next: Conditions and Loops

Built with VitePress | Software Systems Atlas