انتقل إلى المحتوى
أمين أبوزيد
منشورBEG-010-L06

عرض عملي: تحدّي توقّع نتيجة التعبير ثم تشغيله

  • الدرس 6 من 6
  • 11 دقيقة قراءة
  • مبتدئ
في هذا الدرس 11 موضوعات

هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.

Lesson Overview

This is a guided demo, and its goal is a habit rather than a new operator. You will take a set of expressions that use everything from this module (integer division, remainder, precedence, prefix and postfix, short-circuiting), predict each result on paper first, then run them and compare. Where the program disagrees with you, you will find out why.

Why predict first? When you only run code, a wrong result teaches nothing unless you already know what you expected. Predicting is like a calculator check: estimate the answer before you press the buttons. An answer that is far from the estimate tells you something is wrong, even when no error message appears. In this module almost every mistake produces a plausible number, not an error, so the prediction is the only alarm you have.

The demo prepares the module's lab, BEG-010-LAB1, "Expression predictor", which uses the same method with 20 different expressions.

This lesson builds on Lessons 1 to 5. Use a terminal (javac and java) or an IDE such as IntelliJ IDEA; menus differ between versions and operating systems, so look for the equivalent action. The outputs below come from JDK 21.

Learning Objectives

By the end of this lesson you will be able to:

  • Predict the value of expressions that combine arithmetic, comparison, logical, and increment operators.
  • Record a prediction, a result, and a one-line reason for each mismatch.
  • Explain why 1 + 2 + "3" is "33" and "x=" + 1 + 2 is "x=12".
  • Use a step-by-step method (parentheses, then precedence, then left to right) to evaluate an expression by hand.
  • Diagnose a wrong prediction by finding which rule you misapplied.

Written Explanation

The method

Use the same five steps for every expression:

  1. Write the types. Mark each operand as int, double, String, or boolean. Many wrong predictions come from forgetting a type.
  2. Add parentheses where Java would group things: first ++/--, then * / %, then + -, then comparisons, then &&, then || (Lesson 4).
  3. Evaluate step by step, innermost parentheses first, equal-precedence operators left to right.
  4. Write the prediction with its type, such as 3 versus 3.0. Do this before running anything.
  5. Run, compare, explain. For each mismatch, write one line: which rule did you misapply?

The prediction table you will fill in looks like this:

#ExpressionPredictionResultWhy (if different)

One more rule: + with text

You have used + between text and numbers since BEG-009. Here is the precise rule, which this demo needs. The Java Language Specification says that if either operand of + is a String, the operation is string concatenation, and the other operand is converted to text. It also says + groups left to right. Together:

  • 1 + 2 + "3": first 1 + 2 (two ints) is 3; then 3 + "3" has a String, so it joins as text: "33".
  • "x=" + 1 + 2: first "x=" + 1 is "x=1"; then "x=1" + 2 is "x=12". No addition of 1 and 2 ever happens.
  • "x=" + (1 + 2): parentheses first, so "x=3".

The number is not added until the text stops being involved. One extra detail: 'a' + 1 has no String, so it is numeric addition and gives the int 98 (the character a is stored as a number, as BEG-009 mentioned for char arithmetic). By contrast, "" + 'a' + 1 starts with a String and gives "a1".

Examples

Example 1: the first four predictions (simple)

Create Predict.java with the first four expressions below. Before you run, copy the table above and write your prediction for each into the Prediction column.

java
public class Predict {
    public static void main(String[] args) {
        System.out.println("1: " + 17 / 5);
        System.out.println("2: " + 17 % 5);
        System.out.println("3: " + 17 / 5.0);
        System.out.println("4: " + (2 + 3 * 4 % 5));
        // more lines come in the next examples
    }
}

Here is the reasoning you should have done:

  1. 17 / 5: two ints, so integer division: 3. (Not 3.4.)
  2. 17 % 5: 3 * 5 = 15, remainder 2.
  3. 17 / 5.0: a double operand, so decimals: 3.4.
  4. 2 + 3 * 4 % 5: * and % have equal precedence and group from the left. 3 * 4 is 12, 12 % 5 is 2, 2 + 2 is 4.

(The "1: " + ... part is just a label. Lines 1 to 3 do not need parentheses because the division happens before the +; line 4 needs them so that the + inside is arithmetic and not text.)

Example 2: increments, text, and short-circuiting (practical)

Now add the next expressions in the same method and predict each one before running.

java
        int i = 4;
        int a = i++ + i;
        System.out.println("5: a=" + a + " i=" + i);
        System.out.println("6: " + (1 + 2 + "3"));
        System.out.println("7: " + ("x=" + 1 + 2));
        int d = 0;
        System.out.println("8: " + (d != 0 && 10 / d > 1));
        System.out.println("9: " + (false && true || true));
        System.out.println("10: " + (10 / 4 * 4.0));

Reasoning for each:

  1. i++ + i: operands are read left to right. i++ gives 4 and i becomes 5; then the second i is 5. So a is 9 and i is 5.
  2. 1 + 2 + "3": this is the one expected to catch you. Many people predict 6. Following the rule above, it is "33".
  3. "x=" + 1 + 2: "x=12".
  4. The guard from Lesson 5: d != 0 is false, so the division is skipped. The result is false, with no crash.
  5. && groups before ||: (false && true) || true is false || true, which is true.
  6. 10 / 4 * 4.0: left to right, 10 / 4 is integer division (2), then 2 * 4.0 is 8.0. The double appears too late to save the fraction.

Run the program with javac Predict.java followed by java Predict. The output, in JDK 21, is:

text
1: 3
2: 2
3: 3.4
4: 4
5: a=9 i=5
6: 33
7: x=12
8: false
9: true
10: 8.0

If your table has a mismatch, do not just correct the prediction. Write the reason, as in the next example.

Example 3: the mistake log (realistic)

Imagine a developer who predicted line 6 as 6 and line 10 as 10.0. Their completed table might look like this:

#ExpressionPredictionResultWhy (if different)
61 + 2 + "3"6331 + 2 is 3 first, then a String is involved, so + joins text
1010 / 4 * 4.010.08.010 / 4 is integer division (2), done before the double is used

Each "Why" is one sentence naming a rule, not "I was wrong". The sentences matter more than the numbers: they are a list of the rules you tend to forget. A second program shows more text and char cases:

java
public class Predict2 {
    public static void main(String[] args) {
        int x = 9;
        x -= 2 * 3;
        System.out.println(x);
        System.out.println("n=" + 1 + 2);
        System.out.println("n=" + (1 + 2));
        System.out.println(1 + 2 + "n");
        System.out.println('a' + 1);
        System.out.println("" + 'a' + 1);
    }
}
text
3
n=12
n=3
3n
98
a1

First line: x -= 2 * 3 is x = x - (2 * 3), so 9 - 6 is 3. The "n=" + (1 + 2) line shows parentheses repairing the text mix-up that "n=" + 1 + 2 causes. This is a real bug source: a message such as "Total: " + price + tax prints two numbers side by side instead of adding them.

Backend Insight

Backend developers do this kind of predicting all day, usually without the table. A wrong total in an invoice, a skipped record in a report, or an access check that passes when it should not: each is an expression that gave a plausible but wrong answer. The prediction habit is what turns a vague "it gives the wrong number" into "I expected 8.0, got 10.0, so the division happens as integers", which is a bug you can fix.

The habit also shapes testing. A test is a recorded prediction: "for these inputs, this expression should give this output". The tests you will write in the testing modules are exactly this table, saved in code, and the values worth testing are the boundaries you met here: zero divisors, > versus >=, the first and last number of a range. Log messages that glue values with + are another place where the text rule from this lesson bites.

Common Mistakes

Mistake 1: Running before predicting

If you run first, you will rationalize whatever you see ("yes, that is what I thought"). Write the prediction down before running, in a file or on paper.

Mistake 2: Predicting 1 + 2 + "3" as 6

Once one side of + is text, the operation joins. Evaluate left to right, one operator at a time.

Mistake 3: Forgetting the type of each step

10 / 4 * 4.0 is 8.0, not 10.0, because the type of the first division is int. Predict the type as well as the value (3 is not 3.0).

Mistake 4: Fixing the prediction instead of explaining it

A silently corrected prediction teaches nothing. Always write the rule you got wrong in one line.

Mistake 5: Assuming "no error" means "correct"

All ten expressions above run without an error message; one of them is wrong in most people's heads. Compare the output with your prediction, not with "did it crash".

Exercises

Do each exercise on paper before running anything, and fill in a prediction table with a reason for every mismatch.

Level A: Foundation

A1. Predict, with the type of each result: 9 / 2, 9 % 2, 9 / 2.0, 9 > 2.

أظهر الحل A1

A1. 4 (int), 1 (int), 4.5 (double), true (boolean).

A2. Predict: "a" + 1 + 1, 1 + 1 + "a", "a" + (1 + 1).

أظهر الحل A2

A2. "a11", "2a", "a2".

A3. int n = 5; n *= 3 + 1; What is n?

أظهر الحل A3

A3. 20. The right side 3 + 1 is 4, and 5 * 4 is 20.

Level B: Application

B1. Predict p, q, and the printed value: int p = 2; int q = p++ * 3 + p; System.out.println(q > p && q % 2 == 1);

أظهر الحل B1

B1. p++ gives 2 and p becomes 3; 2 * 3 is 6; the second p is 3, so q is 9. Then q > p is 9 > 3, which is true, and q % 2 == 1 is 1 == 1, which is true. It prints true; p is 3 and q is 9.

B2. Predict the output (and say whether any line could crash):

java
int d = 3;
System.out.println(d > 0 && 12 / d > 3);
d = 0;
System.out.println(d > 0 && 12 / d > 3);
System.out.println(d == 0 || 12 / d > 3);
أظهر الحل B2

B2. true (12 / 3 is 4, and 4 > 3), then false (the guard d > 0 fails and the division is skipped), then true (d == 0 is true, so the division is skipped). No line crashes.

B3. Predict 8 - 4 / 3 * 3 + 1 % 2 and show each step.

أظهر الحل B3

B3. 4 / 3 is 1; 1 * 3 is 3; 1 % 2 is 1. The expression becomes 8 - 3 + 1, evaluated from the left: 8 - 3 is 5, then 5 + 1 is 6. The answer is 6.

Level C: Challenge

C1. Without running anything, write six expressions of your own that each test a different rule from the module (one for integer division, one for %, one for prefix versus postfix, one for precedence, one for short-circuiting, one for the text rule). Give your prediction for each. Then run them and log any mismatch.

أظهر الحل C1

C1. There is no single right answer. A good set: 13 / 4 (3), 13 % 4 (1), int k = 2; k++ + k (5, with k equal to 3), 1 + 2 * 3 (7), n != 0 && 6 / n > 1 with n = 0 (false, no crash), and "v" + 1 + 1 ("v11"). Check that each of yours tests a different rule and that your log has a reason for every mismatch.

C2. A teammate wrote System.out.println("Sum: " + a + b); with a = 3 and b = 4 and is surprised by the output. Predict the output, explain it, and give two different fixes.

أظهر الحل C2

C2. The output is Sum: 34. The expression groups as ("Sum: " + a) + b, so b is appended as text. Fixes: add parentheses, "Sum: " + (a + b), which prints Sum: 7; or compute first: int sum = a + b; and print "Sum: " + sum.

Mini Challenge

Prepare for the lab. Write five new expressions of your own (not copied from this lesson) that are likely to trip you up, for example one that mixes int and double division, one with % and a negative number, one with ++ inside an expression, one that mixes && and ||, and one with + and a String.

  1. Predict all five on paper with their types.
  2. Put them in a program, run it, and fill a table with the result and a one-line reason for every mismatch.
  3. Add a short comment at the top of the file summarizing which rule you most often got wrong.

Check your work: every prediction was written before running, and the "Why" column has a rule name (such as "integer division", "postfix", "left to right") in each mismatch row, not just a corrected number.

Summary & Next Lesson

Key takeaways

  • Predict first, run second, explain every mismatch with a rule. It is the cheapest debugging skill there is.
  • Method: write the types, add parentheses by precedence, evaluate left to right, and record the type of the result.
  • Once a String is involved, + joins text. 1 + 2 + "3" is "33" and "x=" + 1 + 2 is "x=12".
  • Integer division, %, prefix versus postfix, precedence, and short-circuiting each change a result without any error message.
  • The mismatch log is a personal list of rules to watch.

Next lesson

This was the last lesson of the module. Next comes the module's lab, BEG-010-LAB1, "Expression predictor": predict 20 expressions on paper, then run them, and explain each mismatch.

Self-check

  1. Why should you write the prediction before running the code?
  2. What is 1 + 2 + "3", and why?
  3. What is 10 / 4 * 4.0?
  4. In int a = i++ + i; with i = 4, what are a and i?
  5. What should the "Why" column contain when a prediction was wrong?
أظهر الإجابات
  1. Otherwise you will tend to accept whatever the program prints, and you cannot tell a surprise from an expected result.
  2. "33". 1 + 2 is 3 first; then adding a String makes + join text.
  3. 8.0: integer division gives 2 first, then 2 * 4.0.
  4. a is 9 and i is 5.
  5. The specific rule that was misapplied, in one sentence, such as "integer division happens first".

References

  • Oracle, The Java Language Specification, Java SE 21, Chapter 15: Expressions. Section 15.18 says that if either operand of + is a String the operation is string concatenation, and that additive operators group left to right; section 15.18.1 gives 1 + 2 + " fiddlers" as "3 fiddlers" and "fiddlers " + 1 + 2 as "fiddlers 12". Section 15.7 gives left-to-right evaluation of operands.
  • Oracle, The Java Tutorials: Operators. It supports the precedence order used in the method.
  • All outputs above came from compiling and running the code with JDK 21. That 'a' + 1 is the int 98 follows from the promotion of char to int in arithmetic, which BEG-009 introduced, and the printed 98 was confirmed by running the code. The claim that this method is the "cheapest debugging skill" is the author's opinion, not a sourced fact.

التطبيق

طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.

المختبر والتمارين الاختبار

تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.

اكتب للبحث في المستويات والمراحل والوحدات والدروس.