Han

Han Ferik

Home | Projects | AP CSA

Unit 3 Examples

Tools: VSCode | Source Code: GitHub

1. Pitfalls of If Statements

The IfStatementExamples class highlights two key pitfalls in using if statements:

Pitfall #1: Unnecessary If-Else Nesting
In this example, the nested if statements determine grades based on a score. Initially, each condition is embedded within another else block, creating a "pyramid" structure that's both cumbersome and hard to read. This is a common pitfall because each additional level of nesting increases cognitive load and chances for errors. The simplified solution with else if statements removes the unnecessary nesting by sequentially checking conditions. This prevents redundancy and makes it clear that each condition is exclusive.
Tricky Part: Avoid deep nesting in if-else structures whenever possible. Java reads conditions in sequence, so you can keep statements flat and still get accurate results.

Pitfall #2: Repeated Condition Checks
In the example of going outside if it’s raining and you have an umbrella, if (isRaining == true) is an unnecessary check because isRaining is already a Boolean. Using == true or == false with Boolean variables is redundant and can even lead to confusion when reading the code. The solution checks both conditions with if (isRaining && hasUmbrella), making it clearer and more efficient.
Tricky Part: Avoid comparing Boolean variables with == true or == false—this is redundant. Simply use the Boolean variable directly.

IfStatementExamples Java Code

IfStatementExamples Java Code


      // Pitfalls of If Statements:
      public class IfStatementExamples {
      
          public static void main(String[] args) {
              int score = 85;
      
              // Pitfall #1: Unnecessary if-else statements
              if (score >= 90) {
                  System.out.println("Grade: A");
              } else {
                  if (score >= 80) {
                      System.out.println("Grade: B");
                  } else {
                      if (score >= 70) {
                          System.out.println("Grade: C");
                      } else {
                          System.out.println("Grade: F");
                      }
                  }
              }
      
              // Solution: Use else-if to simplify
              if (score >= 90) {
                  System.out.println("Grade: A");
              } else if (score >= 80) {
                  System.out.println("Grade: B");
              } else if (score >= 70) {
                  System.out.println("Grade: C");
              } else {
                  System.out.println("Grade: F");
              }
      
              // Pitfall #2: Repeated condition checks
              boolean isRaining = true;
              boolean hasUmbrella = true;
      
              if (isRaining == true) {  // Avoid checking "== true"
                  if (hasUmbrella == true) {
                      System.out.println("You can go outside.");
                  }
              }
      
              // Solution: Simplify with direct conditions
              if (isRaining && hasUmbrella) {
                  System.out.println("You can go outside.");
              }
          }
      }
          

2. Boolean Operators

In BooleanOperatorsExample, we explore the use of logical operators (&&, ||, and !):

AND (&&): Here, both conditions in if (age >= 18 && hasLicense) must be true for the code inside the block to execute, indicating that both age and a license are required to drive.
Tricky Part: Remember that && is strict. If any condition fails, the entire statement becomes false. Use it only when all conditions must be met.

OR (||): The if (age < 18 || !hasLicense) checks if either condition is true, meaning the person either isn’t old enough or doesn’t have a license. If either is true, they cannot drive.
Tricky Part: || only needs one condition to be true. This can sometimes lead to unintended results if not carefully thought through, so ensure it’s appropriate for your logic.

NOT (!): The if (!isWeekend) inverts the value of isWeekend, checking if it’s not the weekend.
Tricky Part: Inversion is straightforward here, but when combined with other operators, ! can complicate expressions. Use parentheses to clarify complex expressions.

Combining Operators: This final example combines all three operators, showing how conditions can be layered for complex logic checks.
Tricky Part: Combined conditions require understanding of operator precedence. In Java, && binds more tightly than ||, so use parentheses to ensure expressions evaluate as expected.

BooleanOperatorsExample Java Code

BooleanOperatorsExample Java Code


      // Boolean Operators:
      public class BooleanOperatorsExample {
      
          public static void main(String[] args) {
              int age = 20;
              boolean hasLicense = true;
              boolean isWeekend = false;
      
              // Example 1: Using AND (&&) operator
              // Both conditions need to be true for this block to execute
              if (age >= 18 && hasLicense) {
                  System.out.println("You can drive.");
              } else {
                  System.out.println("You cannot drive.");
              }
      
              // Example 2: Using OR (||) operator
              // Only one of these conditions needs to be true for this block to execute
              if (age < 18 || !hasLicense) {
                  System.out.println("You are not allowed to drive.");
              } else {
                  System.out.println("You are allowed to drive.");
              }
      
              // Example 3: Using NOT (!) operator
              // This inverts the boolean value
              if (!isWeekend) {
                  System.out.println("It's a weekday.");
              } else {
                  System.out.println("It's the weekend.");
              }
      
              // Example 4: Combining operators
              // Using &&, ||, and ! together for complex conditions
              if ((age >= 18 && hasLicense) || (isWeekend && !hasLicense)) {
                  System.out.println("Special conditions met for driving or weekend.");
              } else {
                  System.out.println("Regular conditions apply.");
              }
          }
      }
          

3. Truth Table

The TruthTableExample illustrates how to display results of Boolean operations for all combinations of values for two variables, A and B. This is useful for understanding how &&, ||, and ! behave across all possible inputs.
Tricky Part: When constructing a truth table, be careful to account for all combinations. This example is straightforward, but with more variables, manually covering each scenario is challenging. For larger tables, consider using loops and arrays to ensure you don’t miss any combinations.

TruthTableExample Java Code

TruthTableExample Java Code


      // Truth Table:
      public class TruthTableExample {
      
          public static void main(String[] args) {
              boolean[] values = { true, false };
      
              System.out.println("A\tB\tA && B\tA || B\t!A");
              System.out.println("-----------------------------");
      
              // Loop through all combinations of A and B
              for (boolean A : values) {
                  for (boolean B : values) {
                      boolean andResult = A && B;
                      boolean orResult = A || B;
                      boolean notAResult = !A;
      
                      System.out.println(A + "\t" + B + "\t" + andResult + "\t" + orResult + "\t" + notAResult);
                  }
              }
          }
      }
          

4. Short-Circuit Operations

In the ShortCircuitExample, we see how short-circuiting prevents unnecessary evaluation in && and || conditions:

AND Short-Circuit (&&): In the line if (x < 5 && checkCondition()), Java doesn’t call checkCondition() because x < 5 is false. With &&, if the first condition fails, Java skips the second, saving processing time.
Tricky Part: While this improves efficiency, remember that short-circuiting can skip function calls or side effects you might expect to run, so avoid placing essential logic in conditions that may be skipped.

OR Short-Circuit (||): In if (x > 5 || checkCondition()), checkCondition() isn’t evaluated because x > 5 is true. For ||, if the first condition is true, Java skips the second.
Tricky Part: Short-circuiting with || can similarly bypass code unexpectedly, so be cautious with functions in these conditions. If functions have side effects, use separate statements.

ShortCircuitExample Java Code

ShortCircuitExample Java Code


      // Short Circuit Operations:
      public class ShortCircuitExample {
      
          public static void main(String[] args) {
              int x = 10;
      
              // Example of short-circuit AND (&&)
              // The second condition (x > 5) will not be evaluated since x < 5 is false
              if (x < 5 && checkCondition()) {
                  System.out.println("This won't print, as short-circuit AND stops at x < 5.");
              } else {
                  System.out.println("AND short-circuit: Second condition skipped.");
              }
      
              // Example of short-circuit OR (||)
              // The second condition (x < 5) will not be evaluated since x > 5 is true
              if (x > 5 || checkCondition()) {
                  System.out.println("OR short-circuit: Second condition skipped.");
              } else {
                  System.out.println("This won't print, as short-circuit OR stops at x > 5.");
              }
          }
      
          // Method to simulate a condition check with a print statement
          public static boolean checkCondition() {
              System.out.println("checkCondition() was called!");
              return true;
          }
      }
          

5. Comparing Primitives vs. Comparing Objects

In ComparisonExample, we explore the difference between comparing primitives and objects:

Comparing Primitives: if (a == b) compares values directly. Since a and b are both 5, this evaluates to true. Primitives always compare their actual values, making comparison straightforward.
Tricky Part: Primitive comparisons are simple, but when switching between objects and primitives, remember that they behave differently.

Comparing Objects: The line if (obj1 == obj2) returns false because it checks reference equality, not the actual value within each object. Since obj1 and obj2 refer to different memory locations, they aren’t considered equal by ==. Using obj1.equals(obj2) checks the values within, returning true since both hold 5.
Tricky Part: == checks if two objects point to the same memory location, not their content. To check for value equality, always use .equals() with objects.

ComparisonExample Java Code

ComparisonExample Java Code


      // Comparing Primitives vs. Comparing Objects:
      public class ComparisonExample {
      
          public static void main(String[] args) {
              // Comparing primitives
              int a = 5;
              int b = 5;
      
              if (a == b) {
                  System.out.println("Primitives are equal (a == b)");
              } else {
                  System.out.println("Primitives are not equal (a != b)");
              }
      
              // Comparing objects
              Integer obj1 = new Integer(5);
              Integer obj2 = new Integer(5);
      
              if (obj1 == obj2) {
                  System.out.println("Objects are equal (obj1 == obj2)");
              } else {
                  System.out.println("Objects are not equal (obj1 != obj2)");
              }
      
              if (obj1.equals(obj2)) {
                  System.out.println("Objects are equal in value (obj1.equals(obj2))");
              } else {
                  System.out.println("Objects are not equal in value (!obj1.equals(obj2))");
              }
          }
      }
          

6. De Morgan’s Law

In DeMorgansLawExample, we see how De Morgan’s Laws transform Boolean expressions to simplify conditions, particularly when negating them:

Example 1: !(A && B): Here, !(A && B) is equivalent to !A || !B. Both expressions yield the same result, demonstrating De Morgan’s Law. This conversion is useful because it simplifies the negation of combined conditions.
Tricky Part: When negating complex expressions, remember that !(A && B) doesn’t equal !A && !B—that’s incorrect logic. De Morgan’s Laws are essential to keep negations accurate.

Example 2: !(A || B): In this case, !(A || B) is equivalent to !A && !B. Both produce the same result, showing that De Morgan’s Law holds for OR conditions as well.
Tricky Part: Be mindful of which operations you’re negating. Using De Morgan’s Law correctly is especially helpful when refactoring code, as it can reduce the need for nested negations and make the logic clearer.

DeMorgansLawExample Java Code

DeMorgansLawExample Java Code


      // De Morgan's Law:
      public class DeMorgansLawExample {
      
          public static void main(String[] args) {
              boolean A = true;
              boolean B = false;
      
              // Example 1: !(A && B) is equivalent to !A || !B
              boolean result1 = !(A && B);
              boolean result2 = !A || !B;
      
              System.out.println("!(A && B) = " + result1);
              System.out.println("!A || !B = " + result2);
              System.out.println("Are they equal? " + (result1 == result2));  // should be true
      
              // Example 2: !(A || B) is equivalent to !A && !B
              boolean result3 = !(A || B);
              boolean result4 = !A && !B;
      
              System.out.println("\n!(A || B) = " + result3);
              System.out.println("!A && !B = " + result4);
              System.out.println("Are they equal? " + (result3 == result4));  // should be true
          }
      }
          

Find me on the interwebs!

Github LinkedIn Instagram Facebook