diff --git a/source/ap-java-cheatsheet.ptx b/source/ap-java-cheatsheet.ptx index f272981..7e8f94a 100644 --- a/source/ap-java-cheatsheet.ptx +++ b/source/ap-java-cheatsheet.ptx @@ -10,7 +10,7 @@ The following is intended to be useful in better understanding Java functions coming from a Python background.
-
-Using this operator can make code shorter and more readable in cases where a simple conditional assignment is needed.
+Using this operator can make code shorter and more readable in cases where a simple conditional assignment is needed.
+public class Ternary {
+ public static void main(String[] args) {
+ int a = 4;
+ int x = 2;
+ int outp;
+
+ // ternary:
+ outp = (a % 2 == 0) ? (a * a) : (3 * x - 1);
+ System.out.println("ternary result: " + outp);
+
+ // Equivalent using if/else
+ if (a % 2 == 0) {
+ outp = a * a;
+ } else {
+ outp = 3 * x - 1;
+ }
+
+ System.out.println("if/else result: " + outp);
+ }
+}
+
+
+ In
+ Rearrange the blocks to create a Java method that calculates shipping costs. Orders over $100 get free shipping ($0.0), while members pay $5.0 and non-members pay $10.0. +
+
+ In Python, if you want a program to continue running when an error has occurred, you can use
- class Main {
- public static void main(String[] args) {
- int a = 4;
- int x = 2;
+ number = int(input("Please enter a whole number: ")) # ask user for a number
+ squared = number ** 2 # square the number
+ print("Your number squared is " + str(squared))
+
+
+
+ import java.util.Scanner;
- System.out.println("Result: " + a);
- }
+ public class SquareNumber {
+ public static void main(String[] args) {
+ Scanner user_input = new Scanner(System.in); // create a scanner object
+
+ System.out.print("Please enter a whole number: ");
+ int number = user_input.nextInt(); // ask user for a number
+ int squared = number * number; // square the number
+
+ System.out.println("Your number squared is " + squared);
}
-
+ }
+
+
+ This code works well, but will end with an exception if the user types anything other than a whole number (such as 12.5 or two). If we wanted to ensure the code will continue to run until the user enters the correct format, we could add
+ while True:
+ try: # try to convert the user input to an integer
+ number = int(input("Please enter a whole number: "))
+ squared = number ** 2
+ print("Your number squared is " + str(squared))
+ break
+ except ValueError: # if the user enters a non-integer, print an error message
+ print("That was not a valid number. Please try again: ")
- In this example we are using this ternary operator to assign a value to
+ import java.util.Scanner;
+ import java.util.InputMismatchException;
+ public class SquareNumberWithValidation {
+ public static void main(String[] args) {
+ Scanner scanner = new Scanner(System.in);
+
+ while (true) { // keep asking the user for a number until they enter a valid integer
+ try { // try to convert the user input to an integer
+ System.out.print("Please enter a whole number: ");
+ int number = scanner.nextInt();
+ int squared = number * number;
+ System.out.println("Your number squared is " + squared);
+ break;
+ } catch (InputMismatchException e) { // if the user enters a non-integer, print an error message
+ System.out.println("That was not a valid number. Please try again: ");
+ scanner.nextLine(); // Clear the invalid input from the scanner
+ }
+ }
+ }
+ }
+
+ Firstly, let's talk about the extra import alongside the
+
+ Note that as with other structures in Java,
Java requires parentheses around the condition and curly braces for code blocks in
Java uses
+ Java's
+ Java uses the
Which is a correct Java
if (x > 0) { System.out.println("Positive"); }
+Correct! Java requires parentheses and curly braces.
+if x > 0: print("Positive")
+No, that's Python syntax, not Java.
+if x > 0 { System.out.println("Positive"); }
+No, Java requires parentheses around the condition.
+if (x > 0) print("Positive");
+No,
How do you write Python’s
elif (score > 90)
+No,
else: if (score > 90)
+Incorrect syntax; no colon in Java and not the right structure.
+else if (score > 90)
+Right! Java uses
ifelse (score > 90)
+No,
What is one limitation of Java's
It cannot evaluate relational expressions like greater than or less than.
+No, while
It cannot handle more than five case labels.
+No, there is no such limit. You can have many case labels in a
It always requires a
Incorrect. The
It can only compare a variable to constant values using equality.
+Correct! Java's
-for i in range(10):
+for i in range(10): # range(10) is a list of integers from 0 to 9
print(i)
- In Java, we would write this as:
+
public class DefiniteLoopExample {
public static void main(String[] args) {
- for (Integer i = 0; i < 10; i++ ) {
+ for (Integer i = 0; i < 10; i++ ) { // notice how the initialization, condition, and update are all on the same line.
System.out.println(i);
}
}
}
- Recall that the
++ ++ + range(stop) range(start,stop) range(start,stop,step) -
- The Java
+- + + + ++ + for (start clause; stop clause; step clause) { statement1 statement2 ... } -
- If you want to start at 100, stop at 0 and count backward by 5, the Python loop would be written as:
+ If you want to start at 100, stop at 0 and count backward by 5,
-for i in range(100, -1, -5):
+for i in range(100, -1, -5): # start at 100, stop at 0, decrement by 5
print(i)
- In Java, we would write this as:
+
public class DefiniteLoopBackward {
public static void main(String[] args) {
- for (Integer i = 100; i >= 0; i -= 5) {
+ for (Integer i = 100; i >= 0; i -= 5) { // start at 100, stop at 0, decrement by 5
System.out.println(i);
}
}
}
In Python, the
- In Python, we can iterate over a list as follows:
+
-l = [1, 1, 2, 3, 5, 8, 13, 21]
-for fib in l:
+l = [1, 1, 2, 3, 5, 8, 13, 21] # create a list of integers
+for fib in l: # iterate over the list
print(fib)
- In Java we can iterate over an
import java.util.ArrayList;
public class ForEachArrayListExample {
public static void main(String[] args) {
- ArrayList<Integer> l = new ArrayList<Integer>();
- l.add(1);
- l.add(1);
- l.add(2);
+ ArrayList<Integer> l = new ArrayList< // create an ArrayList of integers
+ l.add(1); // add the first integer to the list
+ l.add(1); // add the second integer to the list
+ l.add(2); // keep going
l.add(3);
l.add(5);
l.add(8);
l.add(13);
- l.add(21);
- for (Integer i : l) {
+ l.add(21); // add the last integer to the list
+ for (Integer i : l) { // iterate over the list
System.out.println(i);
}
}
}
- This example stretches the imagination a bit, and in fact points out one area where Java's primitive arrays are easier to use than an array list.
- In fact all primitive arrays can be used in a for each loop.
+
public class ForEachArrayExample {
public static void main(String[] args) {
- int l[] = {1,1,2,3,5,8,13,21};
- for(int i : l) {
+ int l[] = {1,1,2,3,5,8,13,21}; // create an array of integers using primitive syntax
+ for(int i : l) { // iterate over the array
System.out.println(i);
}
}
}
- To iterate over the characters in a string in Java do the following: -
- +
public class StringIterationExample {
public static void main(String[] args) {
- String t = "Hello World";
- for (char c : t.toCharArray()) {
+ String t = "Hello World"; // create a string
+ for (char c : t.toCharArray()) { // iterate over the characters in the string
System.out.println(c);
}
}
}
+ Rearrange the blocks to create a Java method that accepts an upper bound integer
i = 5
-while i > 0:
+while i > 0: # while i is greater than 0
print(i)
- i = i - 1
+ i = i - 1
- In Java we add parenthesis and curly braces. Here is the same countdown loop in Java:
+ In Java, we add parentheses and curly braces.
public class WhileLoopExample {
public static void main(String[] args) {
- int i = 5;
- while (i > 0) {
+ int i = 5;
+ while (i > 0) { // while i is greater than 0
System.out.println(i);
- i = i - 1;
+ i = i - 1;
}
}
}
public class DoWhileExample {
public static void main(String[] args) {
- int i = 10;
- do {
+ int i = 10;
+ do { // do-while loop, will run at least once no matter the condition
System.out.println("This runs once, i = " + i);
- } while (i < 5);
+ } while (i < 5); // while i is less than 5
}
}
+ Rearrange the blocks to create a Java method that accepts a starting balance and a target amount, then calculates how many years it takes for the balance to reach or exceed the target by doubling each year. +
+
+
+
+
+ The best way to understand classes and objects is to see them in action. Let's define a
+ class Dog:
+ """ A simple Dog class definition. """
+ def __init__(self, name, breed, fur_color):
+ # constructor method to create a Dog object
+ self.name = name
+ self.breed = breed
+ self.fur_color = fur_color
+ self.trained = False # dogs are not trained by default
+ print("Dog named " + self.name + " created!")
+
+ def bark(self):
+ # method to make the dog bark
+ print(self.name + " says woof!")
+
+ def sit(self):
+ # method to make the dog sit
+ if self.trained: # check if the dog has been trained otherwise it will not sit
+ print(self.name + " sits.")
+ else:
+ print(self.name + " has not been trained.")
+
+ def train(self):
+ # method to train the dog, which will set the trained attribute to True
+ self.trained = True
+
+
+ Let's unpack what is going on in
+ The next three blocks of code are the class's methods. These include
+
+ Next, we will use this class to create a new
+ class Dog:
+ """ A simple Dog class definition. """
+ def __init__(self, name, breed, fur_color):
+ # constructor method to create a Dog object
+ self.name = name
+ self.breed = breed
+ self.fur_color = fur_color
+ self.trained = False # dogs are not trained by default
+ print("Dog named " + self.name + " created!")
+
+ def bark(self):
+ # method to make the dog bark
+ print(self.name + " says woof!")
+
+ def sit(self):
+ # method to make the dog sit
+ if self.trained: # check if the dog has been trained otherwise it will not sit
+ print(self.name + " sits.")
+ else:
+ print(self.name + " has not been trained.")
+
+ def train(self):
+ # method to train the dog, which will set the trained attribute to True
+ self.trained = True
+
+ # Create a Dog object called my_dog
+ my_dog = Dog("Rex", "pug", "brown")
+
+
+ In the final line of code in
+ Now that we have created a
+ class Dog:
+ """ A simple Dog class definition. """
+ def __init__(self, name, breed, fur_color):
+ # constructor method to create a Dog object
+ self.name = name
+ self.breed = breed
+ self.fur_color = fur_color
+ self.trained = False # dogs are not trained by default
+ print("Dog named " + self.name + " created!")
+
+ def bark(self):
+ # method to make the dog bark
+ print(self.name + " says woof!")
+
+ def sit(self):
+ # method to make the dog sit
+ if self.trained:
+ print(self.name + " sits.")
+ else:
+ print(self.name + " has not been trained.")
+
+ def train(self):
+ # method to train the dog, which will set the trained attribute to True
+ self.trained = True
+
+
+ my_dog = Dog("Rex", "pug", "brown")
+ my_dog.bark() # call the bark method
+ my_dog.sit() # call the sit method
+
+
+ When running
+ Now, we have a full class definition and have utilized its methods. Class definitions in Java will be covered thoroughly in chapter 6. For now, it is important to know that Python programs can be written without using classes at all. Java, on the other hand, requires all code to reside in a class. This will be discussed later in chapter 6. +
+ +
- Here is a mostly complete implementation of a Fraction class in Python that we will refer to throughout this section:
+
class Fraction:
@@ -76,37 +242,54 @@
"""
self.num = num
self.den = den
- def __repr__(self):
- if self.num > self.den:
- retWhole = int(self.num / self.den)
- retNum = self.num - (retWhole * self.den)
+ def __repr__(self):
+ """
+ :return: a string representation of the fraction
+ """
+ if self.num > self.den:
+ retWhole = int(self.num / self.den) # find the whole number part
+ retNum = self.num - (retWhole * self.den) # find the numerator part
return str(retWhole) + " " + str(retNum) + "/" + str(self.den)
else:
return str(self.num) + "/" + str(self.den)
def show(self):
+ """
+ :return: print the fraction
+ """
print(self.num, "/", self.den)
def __add__(self, other):
+ """
+ :param other: the fraction to add
+ :return: the sum of the two fractions
+ """
# convert to a fraction
other = self.toFract(other)
- newnum = self.num * other.den + self.den * other.num
- newden = self.den * other.den
+ newnum = self.num * other.den + self.den * other.num # find the new numerator
+ newden = self.den * other.den # find the new denominator
common = gcd(newnum, newden)
return Fraction(int(newnum / common), int(newden / common))
- __radd__ = __add__
+ __radd__ = __add__ # allow the fraction to be added to a number
def __lt__(self, other):
+ """
+ :param other: the fraction to compare
+ :return: whether the fraction is less than the other
+ """
num1 = self.num * other.den
num2 = self.den * other.num
return num1 < num2
def toFract(self, n):
+ """
+ :param n: the number to convert to a fraction
+ :return: the fraction representation of the number
+ """
if isinstance(n, int):
other = Fraction(n, 1)
elif isinstance(n, float):
wholePart = int(n)
- fracPart = n - wholePart
- # convert to 100ths???
- fracNum = int(fracPart * 100)
- newNum = wholePart * 100 + fracNum
- other = Fraction(newNum, 100)
+ fracPart = n - wholePart
+ fracNum = int(fracPart * 100) # convert to 100ths
+ newNum = wholePart * 100 + fracNum # combine the whole and fractional parts
+ other = Fraction(newNum, 100)
elif isinstance(n, Fraction):
other = n
else:
@@ -115,9 +298,12 @@
return other
def gcd(m, n):
"""
- A helper function for Fraction
+ A helper function for Fraction.
+ :param m: the first number
+ :param n: the second number
+ :return: the greatest common divisor
"""
- while m % n != 0:
+ while m % n != 0: # keep going until the gcd is found
oldm = m
oldn = n
m = oldn
@@ -126,6 +312,7 @@
print(sorted([Fraction(5, 16), Fraction(3, 16), Fraction(1, 16) + 1]))
- The declarations of instance variables can come at the beginning of the class definition or the end. Cay Horstman, author of
- public class Fraction {
- private Integer numerator;
+ public class Fraction {
+ private Integer numerator; // notice the private modifier
private Integer denominator;
}
+
- Fraction f = new Fraction(1,2);
- Integer y = f.numerator * 10;
+ Fraction f = new Fraction(1,2);
+ Integer y = f.numerator * 10; // trying to access the numerator
-public Integer getNumerator() {
+public Integer getNumerator() { // getter method
return numerator;
}
-public void setNumerator(Integer numerator) {
+public void setNumerator(Integer numerator) { // setter method
this.numerator = numerator;
}
-public Integer getDenominator() {
+public Integer getDenominator() { // getter method
return denominator;
}
-public void setDenominator(Integer denominator) {
+public void setDenominator(Integer denominator) { // setter method
this.denominator = denominator;
}
-
public Fraction(Integer top, Integer bottom) {
- num = top;
+ num = top; // notice the use of num instead of top
den = bottom;
}
public Fraction(Integer num, Integer den) {
- this.num = num;
+ this.num = num; // notice how we use this.num instead of num
this.den = den;
}
- Let’s begin by implementing addition in Java:
+
-public Fraction add(Fraction otherFrac) {
+public Fraction add(Fraction otherFrac) {
Integer newNum = otherFrac.getDenominator() * this.numerator +
- this.denominator * otherFrac.getNumerator();
- Integer newDen = this.denominator * otherFrac.getDenominator();
- Integer common = gcd(newNum, newDen);
+ this.denominator * otherFrac.getNumerator(); // notice the use of this.
+ Integer newDen = this.denominator * otherFrac.getDenominator(); // find the new denominator
+ Integer common = gcd(newNum, newDen); // find the greatest common divisor
return new Fraction(newNum/common, newDen/common);
}
First you will notice that the
Second, you will notice that the method makes use of the
public Fraction add(Fraction otherFrac) {
Integer newNum = otherFrac.getDenominator() * numerator +
- denominator * otherFrac.getNumerator();
+ denominator * otherFrac.getNumerator(); // notice the absence of this.
Integer newDen = denominator * otherFrac.getDenominator();
Integer common = gcd(newNum, newDen);
return new Fraction(newNum/common, newDen/common);
}
The addition takes place by multiplying each numerator by the opposite denominator before adding.
@@ -358,21 +554,22 @@ public Fraction add(Fraction otherFrac) {
To solve the problem of adding an
-public Fraction(Integer num) {
- this.numerator = num;
+public Fraction(Integer num) {
+ this.numerator = num; // set the numerator to the Integer
this.denominator = 1;
}
-public Fraction add(Integer other) {
- return add(new Fraction(other));
+public Fraction add(Integer other) { // overload the add method when the parameter is an Integer
+ return add(new Fraction(other));
}
Notice that the overloading approach can provide us with a certain elegance to our code. @@ -381,21 +578,21 @@ public Fraction add(Integer other) {
- Our full
public class Fraction {
private Integer numerator;
private Integer denominator;
- public Fraction(Integer num, Integer den) {
+ public Fraction(Integer num, Integer den) { // constructor that takes two Integers
this.numerator = num;
this.denominator = den;
}
- public Fraction(Integer num) {
+ public Fraction(Integer num) { // constructor that takes a single Integer, sets the denominator to 1
this.numerator = num;
this.denominator = 1;
}
@@ -411,10 +608,10 @@ public class Fraction {
Integer common = gcd(newNum,newDen);
return new Fraction(newNum/common, newDen/common );
}
- public Fraction add(Integer other) {
+ public Fraction add(Integer other) { // overload the add method when the parameter is an Integer
return add(new Fraction(other));
}
- private static Integer gcd(Integer m, Integer n) {
+ private static Integer gcd(Integer m, Integer n) { // a helper method for the add method
while (m % n != 0) {
Integer oldm = m;
Integer oldn = n;
@@ -423,85 +620,88 @@ public class Fraction {
}
return n;
}
- public static void main(String[] args) {
+ public static void main(String[] args) { // a main method
Fraction f1 = new Fraction(1,2);
System.out.println(f1.add(1));
}
}
- It is worth pointing out that Java has some very handy naming conventions. It is advisable to both use meaningful names and to follow these naming conventions while developing software in Java for good maintenance and readability of code. -
- --
- Class names should be nouns that are written in UpperCamelCase, namely with the first letter of each word capitalized including the first.
- For example,
- Method names use lowerCamelCase which start with a verb that describes the action they perform. This means that method names start with a lower case letter, and use upper case for each internal-word method names. For example,
- Instance variables of a class start with a lower case letter and use lowerCamelCase like method names. For example,
- Constants are in all upper case letters or in upper snake case, which also known as screaming snake case, and which is a naming convention in which each word is written in uppercase letters, separated by underscores.
- For example,
- If you ran the program above you probably noticed that the output is not very satisfying. Chances are your output looked something like this: + +
+ If you ran
Fraction@6ff3c5b5
The reason is that we have not yet provided a friendly string representation for our
+ Rearrange the blocks to create a
-
We are not interested in most of the methods on that list, and many Java programmers live happy and productive lives without knowing much about most of the methods on that list.
- However, to make our output nicer we will implement the
public String toString() {
- return numerator.toString() + "/" + denominator.toString();
+ return numerator.toString() + "/" + denominator.toString(); // convert to a string
}
+
-object1 == object2
+object1 == object2 // this checks to see if the two objects are the same object in memory
- is NOT the same as
+
-object1.equals(object2)
+object1.equals(object2) // this checks to see if the two objects are equal by looking at their instance variables
- Here is an
-public boolean equals(Fraction other) {
+public boolean equals(Fraction other) { // check if this fraction is equal to another fraction
Integer num1 = this.numerator * other.getDenominator();
Integer num2 = this.denominator * other.getNumerator();
if (num1 == num2)
@@ -622,6 +825,7 @@ public boolean equals(Fraction other) {
}
One important thing to remember about
- Here is code that makes the
-public class Fraction extends Number {
+public class Fraction extends Number { // fraction class is a child of Number
...
}
-
- This really isn’t much work for us to implement these methods, as all we have to do is some type conversion and some division:
+ This really isn’t much work for us to implement these methods, as all we have to do is some type conversion and some division as shown in
-public double doubleValue() {
+public double doubleValue() { // convert to a double
return numerator.doubleValue() / denominator.doubleValue();
}
-public float floatValue() {
+public float floatValue() { // convert to a float
return numerator.floatValue() / denominator.floatValue();
}
-public int intValue() {
+public int intValue() { // convert to an int
return numerator.intValue() / denominator.intValue();
}
-public long longValue() {
+public long longValue() { // convert to a long
return numerator.longValue() / denominator.longValue();
}
- However, and this is a big however, it is important to remember that if you specify
- Suppose you try to define a method as follows:
+ Suppose you try to define a method as
public void test(Number a, Number b) {
- a.add(b);
+ a.add(b); // this is a bad idea
}
The Java compiler would give an error because
+ Construct the
-
The
int compareTo(T o)
@@ -796,79 +1034,86 @@ iff y.compareTo(x) throws an exception.)
...
- To make our
-public class Fraction extends Number implements Comparable<Fraction> {
+public class Fraction extends Number implements Comparable<Fraction> { // fraction class is a child of Number and implements the Comparable interface
...
}
The specification
-public int compareTo(Fraction other) {
+public int compareTo(Fraction other) { // compare this fraction with another fraction
Integer num1 = this.numerator * other.getDenominator();
Integer num2 = this.denominator * other.getNumerator();
return num1 - num2;
}
Suppose that you wanted to write a Student class so that the class could keep track of the number of students it had created.
Although you could do this with a global counter variable that is an ugly solution.
- The right way to do it is to use a static variable.
- In Python we could do this as follows:
+ The right way to do it is to use a
class Student:
numStudents = 0
- def __init__(self, id, name):
+ def __init__(self, id, name):
self.id = id
self.name = name
- Student.numStudents = Student.numStudents + 1
+ # this is a static variable, that can be accessed without the self prefix
+ Student.numStudents = Student.numStudents + 1
def main():
for i in range(10):
- s = Student(i,"Student-"+str(i))
+ s = Student(i,"Student-"+str(i)) # create a new Student object
print('Number of students:', Student.numStudents)
main()
- In Java we would write this same example using a static declaration.
+
public class Student {
- public static Integer numStudents = 0;
+ public static Integer numStudents = 0; // static member variable, shared by all instances of the class
private int id;
private String name;
public Student(Integer id, String name) {
this.id = id;
this.name = name;
- numStudents = numStudents + 1;
+ numStudents = numStudents + 1; // a static variable, that can be accessed without the Student prefix
}
public static void main(String[] args) {
for(Integer i = 0; i < 10; i++) {
@@ -879,10 +1124,11 @@ public class Student {
}
+
-private static Integer gcd(Integer m, Integer n) {
+private static Integer gcd(Integer m, Integer n) { // static method to compute the greatest common divisor of two integers
while (m % n != 0) {
Integer oldm = m;
Integer oldn = n;
@@ -907,17 +1157,18 @@ private static Integer gcd(Integer m, Integer n) {
}
- Here is a final version of the
import java.util.ArrayList;
import java.util.Collections;
@@ -1011,5 +1262,132 @@ public class Fraction extends Number implements Comparable<Fraction> {
}
In Java, instance variables (fields) must be declared in the class body before they are used. Unlike Python, you cannot dynamically add new instance variables to an object at runtime.
+Java uses access modifiers like
Java requires a constructor method to initialize objects. A constructor has the same name as the class and defines its parameters explicitly, whereas Python uses the
Every Java class inherits from the
By default, Java’s
Java supports inheritance through abstract classes (like
How are instance variables declared in Java compared to Python?
+They can be created dynamically anywhere in the class like Python.
No, Java does not allow dynamic creation of instance variables at runtime.
Instance variables are declared inside methods only.
No, instance variables are declared in the class body, not in methods.
They must be declared in the class body before use.
Correct! Java requires instance variables (fields) to be declared in the class body.
Java does not use instance variables.
No, instance variables are fundamental in Java classes.
What Java feature encourages encapsulation and controlled access to instance variables?
+Declaring all variables as public.
No, that would expose data and reduce encapsulation.
Using access modifiers like
Right! This is how Java enforces encapsulation.
Using global variables.
No, Java does not support global variables and this reduces encapsulation.
Avoiding the use of classes altogether.
No, encapsulation is a class-based concept in Java.
How does Java initialize objects differently than Python?
+Java uses a constructor method named after the class with explicit parameters.
Correct! Unlike Python's
Java uses the
No, Java does not have
Java initializes objects automatically without constructors.
No, Java requires constructors for explicit initialization.
Java uses global initialization functions instead of constructors.
No, Java uses constructors, not global functions, for object initialization.
What must you do in Java to print objects in a readable way and compare two objects based on their contents rather than their memory references?
+Use
No,
Only override
No, you should override
Java automatically handles content comparison without overrides.
No, default
Override
Yes! This improves output and content-based comparison.
+ In this chapter, we will explore how to translate your recursive logic from Python to Java. While the core concepts of recursion remain the same, the syntax and a bit of the structure of your code will change somewhat. +
+
+ Let's take the familiar factorial function, which calculates
+ You may recall mathematical notation using the symbol
+ Factorial involves multiplication rather than addition, so we use the product symbol
+
+
+def factorial(n):
+ # Check for negative numbers
+ if n < 0:
+ print("Factorials are only defined on non-negative integers.")
+ return
+ # Base Case: 0! or 1! is 1
+ if n <= 1:
+ return 1
+ # Recursive Step: n * (n-1)!
+ return n * factorial(n - 1)
+
+number = 5
+print(str(number) + "! is " + str(factorial(number)))
+
+
+
+ Many Python programs organize related functions into classes. The same factorial function can be placed inside a class as a method instead of as a function. When this is done, you need to create an instance of the class in order to call the method. Below, we create the class
+
+class MTools:
+ def factorial(self, n):
+ # Check for negative numbers
+ if n < 0:
+ print("Factorials are only defined on non-negative integers.")
+ return
+ # Base Case: 0! or 1! is 1
+ if n <= 1:
+ return 1
+ # Recursive Step: n * (n-1)!
+ return n * self.factorial(n - 1)
+
+def main():
+ # Create an instance of the class and call the method
+ mtools_instance = MTools()
+ number = 5
+ print(str(number) + "! is " + str(mtools_instance.factorial(number)))
+
+main()
+
+ + See if you can spot the differences in the Java version below. +
+
+
+public class MTools {
+ public static int factorial(int n) {
+ // Check for negative numbers
+ if (n < 0) {
+ System.out.println("Factorials are only defined on non-negative integers.");
+ return -1; // Return -1 to indicate error
+ }
+ // Base Case: 0! or 1! is 1
+ if (n <= 1) {
+ return 1;
+ }
+ // Recursive Step: n * (n-1)!
+ return n * factorial(n - 1);
+ }
+
+ public static void main(String[] args) {
+ int number = 5;
+ System.out.println(number + "! is " + factorial(number));
+ }
+}
+
+
+ Notice the key differences from Python: instead of
+ In many recursive algorithms, the recursive calls need extra information that the original caller shouldn't have to provide. For example, to recursively process an array, you need to keep track of the index of the current position. This extra information clutters the public-facing signature by forcing users to provide implementation details they shouldn't actually need to know about. +
+
+ First, let's see what happens if we try to write a recursive array sum function without using a helper method. In this approach, the user must provide the starting index, which is awkward and exposes implementation details.
+class ArrayProcessor:
+ def sum_array(self, arr, index):
+ """
+ This version forces users to provide the index parameter.
+ This is inconvenient and exposes implementation details.
+ """
+ # Base case: we've processed all elements
+ if index >= len(arr):
+ return 0
+
+ # Recursive step: current element + sum of remaining elements
+ return arr[index] + self.sum_array(arr, index + 1)
+
+def main():
+ processor = ArrayProcessor()
+ numbers = [1, 2, 3, 4, 5]
+ # Users must remember to start at index 0 - this is confusing!
+ result = processor.sum_array(numbers, 0)
+ print("The sum of " + str(numbers) + " is " + str(result))
+
+main()
+
+
+
+public class ArrayProcessor {
+ public static int sumArray(int[] arr, int index) {
+ // Base case: we've processed all elements
+ if (index >= arr.length) {
+ return 0;
+ }
+
+ // Recursive step: current element + sum of remaining elements
+ return arr[index] + sumArray(arr, index + 1);
+ }
+
+ public static void main(String[] args) {
+ int[] numbers = {1, 2, 3, 4, 5};
+ // Users must remember to start at index 0 - this is confusing!
+ int result = sumArray(numbers, 0);
+ System.out.println("The sum of [1, 2, 3, 4, 5] is " + result);
+ }
+}
+
+ + Both versions force users to understand and provide implementation details they shouldn't need to know about. Now let's see how helper methods solve this problem by providing a clean, user-friendly interface. Notice how the public method only requires the array itself, and the hidden recursive logic tracks the current index position. +
+
+
+class ArrayProcessor:
+ def sum_array(self, arr):
+ """
+ Public method that provides a clean interface for summing array elements.
+ Users only need to provide the array - no implementation details required.
+ """
+ if not arr: # Handle empty array
+ return 0
+ # Start the recursion at index 0
+ return self._sum_helper(arr, 0)
+
+ def _sum_helper(self, arr, index):
+ """
+ Private helper method that does the actual recursive work.
+ Tracks the current index position through the array.
+ """
+ # Base case: we've processed all elements
+ if index >= len(arr):
+ return 0
+
+ # Recursive step: current element + sum of remaining elements
+ return arr[index] + self._sum_helper(arr, index + 1)
+
+def main():
+ processor = ArrayProcessor()
+ numbers = [1, 2, 3, 4, 5]
+ result = processor.sum_array(numbers)
+ print("The sum of " + str(numbers) + " is " + str(result))
+
+main()
+
+
+ The same helper method pattern can be applied in Java, as shown in
+import java.util.Arrays;
+
+public class ArrayProcessor {
+ public static int sumArray(int[] arr) {
+ // Handle empty array
+ if (arr.length == 0) {
+ return 0;
+ }
+ // Start the recursion at index 0
+ return sumHelper(arr, 0);
+ }
+
+ private static int sumHelper(int[] arr, int index) {
+ // Base case: we've processed all elements
+ if (index >= arr.length) {
+ return 0;
+ }
+
+ // Recursive step: current element + sum of remaining elements
+ return arr[index] + sumHelper(arr, index + 1);
+ }
+
+ public static void main(String[] args) {
+ int[] numbers = {1, 2, 3, 4, 5};
+ int result = sumArray(numbers);
+ System.out.println("The sum of " + Arrays.toString(numbers) + " is " + result);
+ }
+}
+
+
+ Compare these improved versions with the earlier problematic ones. Notice how much cleaner the method calls become:
+ This helper method pattern is invaluable when your recursive algorithm needs to track additional state details (like array positions, accumulated values, or depth counters) that the original caller shouldn't need to know about or care about. It's a fundamental pattern and technique you'll likely use frequently in recursive problem solving. +
+
+ The consequence of running out of call stack space, is a concept you may have already encountered in Python. Java handles this in a very similar way to Python, both throwing an error when the call stack depth is exceeded.
+
+ In both languages, if you write a recursive function that doesn't have a base case or that just recurses too deeply, you'll eventually hit this limit. When this happens, Python will raise a
+ The following Python code in
+ def cause_recursion_error():
+ """
+ This function calls itself without a base case, guaranteeing an error.
+ """
+ cause_recursion_error()
+
+ print("Calling the recursive function... this will end in an error!")
+
+ # The line below will start the infinite recursion.
+ # Python will stop it and raise a RecursionError automatically.
+ # Each call adds a new layer to the program's call stack.
+ # Eventually, the call stack runs out of space, causing the error.
+ cause_recursion_error()
+
+
+ The following Java code in
+ public class Crash {
+ public static void causeStackOverflow() {
+ // The line below will start the infinite recursion.
+ // Java will stop it and raise a StackOverflowError automatically.
+ // Each call adds a new layer to the program's call stack.
+ // Eventually, the call stack runs out of space, causing the error.
+ causeStackOverflow();
+ }
+ // A main method is required to run the Java program.
+ public static void main(String[] args) {
+ System.out.println("Calling the recursive method... this will end in an error!");
+
+ causeStackOverflow();
+ }
+ }
+
+ Recursion solves problems by defining a base case and a recursive step; each call reduces the problem size until the base case is reached.
+Java methods must declare visibility, static/instance context, return type, and parameter types; e.g.,
The recursive logic in Java mirrors Python conceptually, but Java uses curly braces
The helper method pattern hides implementation details (like array indices) from callers, providing clean public interfaces while managing recursive state privately.
+Deep or unbounded recursion can exhaust the call stack: Python raises
Neither Java nor Python guarantees tail call optimization, so programmers should use iterative solutions for algorithms that would require very deep recursion.
+Recursive methods in Java must specify return types explicitly, unlike Python's dynamic typing, which affects how you handle error cases and return values.
+Which method signature and behavior best match a typical Java recursive factorial implementation?
+No. While this handles negative numbers, the base case is incorrect - factorial of 0 should be 1, not 0.
No. Printing results is fine for testing, but a proper factorial method should return the computed value.
Correct. This matches the standard recursive factorial definition in Java.
No. While this logic is close, it doesn't handle the case when n = 1, and using long as return type when int parameter is used creates inconsistency.
Why use a private helper method (e.g.,
Because it allows Java to automatically optimize the recursion for faster execution.
No. Java does not automatically optimize recursion just because you use a helper method.
To keep the public API simple while encapsulating extra recursion state (such as the current index) inside a private method.
Correct. This keeps the interface clean while hiding internal details from the caller.
Because public methods cannot take more than one parameter in recursive calls.
No. Public methods can take multiple parameters; this is about interface clarity, not parameter limits.
To eliminate the need for a base case by handling termination in the helper method automatically.
No. The helper method still needs an explicit base case to stop recursion.
Which statement about recursion limits and errors is accurate?
+When the call stack is exhausted, Python raises a
Correct. This difference in exception types and the lack of built-in tail call optimization is a key distinction between the two languages.
+Java automatically applies tail call optimization to recursive methods marked as
No. Java does not perform automatic tail call optimization, regardless of whether methods are marked as final.
+Declaring a recursive method as
No. The
The JVM can detect simple recursive patterns and automatically convert them to iterative loops to prevent stack overflow.
+No. The JVM does not automatically convert recursive methods to iterative ones. This optimization must be done manually by the programmer.
+- File handling is an integral part of programming. Most programming languages have the ability to read from, write to, create, delete, move, and copy files. -
-
- Java has several libraries included for file handling, though, they must be imported. Java includes a class called
- import java.io.File;
+ import math
+ print(math.sqrt(25)) # notice the lower case 'm' in math
- The
- import java.util.Scanner;
-
-
- The
- import java.io.FileWriter;
+ public class SquareRoot {
+ public static void main(String[] args) {
+ System.out.println(Math.sqrt(25)); // notice the upper case 'M' in Math
+ }
+ }
- Finally, these last two classes provide error handling and must be used in tandem with the
- import java.io.IOException;
-
-
- import java.io.FileNotFoundException
-
-
- We will now create a
- File myFile = new File("myfile.txt");
-
+
-
- Now that we have created a new
- First, lets look at the equivalent Python code:
+ The
- import os
-
- filename = "myfile.txt"
-
- if not os.path.exists(filename):
- with open(filename, 'x') as f:
- pass
- print(f"The file {filename} was created successfully.")
- else:
- print(f"The file {filename} already exists.")
-
+
- Now, let's look at Java code that accomplishes the same task:
+ Finally, these last two classes provide error handling and must be used in tandem with the
- import java.io.File;
+
+
import java.io.IOException;
-
- public class CreateFile {
- public static void main(String[] args) {
- if (myFile.createNewFile()) { // If the file was created successfully
- System.out.println("The file " + myFile.getName() + " was created sucessfully.");
- } else { // If a file with the file name chosen already exists
- System.out.println("The file " + myFile.getName() + " already exists.");
- }
- }
- }
-
+ import java.io.FileNotFoundException;
- You may have noticed the use of another method from the File class;
- The code may seem complete at this point, but if you remember from the previous section, error handling using the
- try {
- if (myFile.createNewFile()) { // If the file was created successfully
- System.out.println("The file " + myFile.getName() + " was created sucessfully.");
- } else { // If a file with the file name chosen already exists
- System.out.println("The file " + myFile.getName() + " already exists.");
- }
- } catch (IOException e) {
- System.out.println("An error occurred.");
- e.printStackTrace();
- }
+import java.io.File;
+
+public class CreateFile {
+ public static void main(String[] args) {
+ // First, create a File object that represents "myfile.txt"
+ File myFile = new File("myfile.txt");
+ // Next, print the file path (just the filename.)
+ System.out.println(myFile);
+ }
+}
- The
- An error occurred.
- java.io.IOException: Permission denied
- at java.base/java.io.File.createNewFile(File.java:1040)
- at CreateFile.main(CreateFile.java:7)
-
-
-
- At this point, the program will function correctly. Let's add the try/catch blocks to the foundational code written before to get a complete program.
+ Now let's learn how to make a file in Java. In Python. files can be made using the
- First, the equivalent Python code:
+
- import os
-
- filename = "myfile.txt"
-
+ filename = "newfile.txt"
+ print("Attempting to write to '" + filename + "' using 'w' mode...")
try:
- if not os.path.exists(filename):
- with open(filename, 'x') as f:
- pass # Create the file without writing anything
- print(f"The file {filename} was created successfully.")
- else:
- print(f"The file {filename} already exists.")
- except OSError as e:
- print("An error occurred.")
- import traceback
- traceback.print_exc()
+ with open(filename, 'w') as f:
+ f.write("This file was created using 'w' mode.")
+ print("SUCCESS: The file '" + filename + "' was created or overwritten.")
+ except Exception as e:
+ # This would only catch other unexpected errors
+ print("An unexpected error occurred during write: " + str(e))
+
- Now, the completed Java code:
+
import java.io.File;
@@ -205,191 +149,185 @@
public class CreateFile {
public static void main(String[] args) {
+ File myFile = new File("newfile.txt");
try {
- File myFile = new File("myfile.txt");
- if (myFile.createNewFile()) { // If the file was created successfully
- System.out.println("The file " + myFile.getName() + " was created sucessfully.");
- } else { // If a file with the file name chosen already exists
+ if (myFile.createNewFile()) {
+ System.out.println("The file " + myFile.getName() + " was created successfully.");
+ } else {
System.out.println("The file " + myFile.getName() + " already exists.");
}
} catch (IOException e) {
- System.out.println("An error occurred.");
- e.printStackTrace();
+ // This code runs if an IOException occurs
+ System.out.println("An error occurred while creating the file.");
+ e.printStackTrace(); // This prints the stack trace for more detailed error info
}
-
- }
- }
-
- - You may be wondering: "What if I don't want to create a file in the current working directory?" Good question! In Windows environments, you can specify the file path using two back slashes for each back slash in the file path. For each pair of back slashes, the first back slash acts as an escape character. So, if you want to save a file to this directory: -
- -- C:\Users\UserName\Documents -- -
- The line of code that creates a File object will look like this: -
- -
- File myFile = new File("C:\\Users\\UserName\\Documents\\myfile.txt");
-
- - If you are working in a Linux or Apple environment, you can simply use the file path with single forward slashes: -
- -
- File myFile = new File("/home/UserName/Documents/myfile.txt");
-
- - Let's take a look at how we can use Java to read file contents. We'll start again with library imports and building a class, this time importing the Scanner and FileNotFoundException classes. We will call this class ReadFile: -
- -
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.util.Scanner
-
- public class ReadFile {
- public static void main(String[] args) {
-
}
}
- We will then create a new File object exactly the same as the one from the section on creating files. Additionally, we will create a Scanner object. The Scanner object is the object that does the file reading. We will call this scanner fileReader: -
- -
- File myFile = new File("myfile.txt");
- Scanner fileReader = new Scanner(myFile);
-
- - The next lines consists of a Python code examplethat reads each line of the file passed to the Scanner object.: -
- -
- with open("filename.txt", "r") as file_reader:
- for line in file_reader:
- print(line.strip())
-
- - The equivalent Java code: -
- -
- while (fileReader.hasNextLine()) {
- String data = fileReader.nextLine();
- System.out.println(data);
- }
- fileReader.close();
-
-
- The
- Alternatively, the following code can be used to store the all lines of myfile.txt to one variable: -
- -
- String data = "";
- while (fileReader.hasNextLine()) {
- data = data + fileReader.nextLine() + System.lineSeparator();
- }
- System.out.println(data);
- fileReader.close();
-
-
- Pay close attention to the details of this code.
+ Construct a short Java program that creates a
- Using the second method of storing all file contents to one file, the resulting full code including try/catch blocks (this time using FileNotFoundException instead of IOException) will look something like this. First, the Python code:
+ Let’s take a look at how we can use Python to understand how read file contents in Java. In order to read files generally you iterate through each line in the file and read the line's content. In Java, you read files in a very similar way, however in Java we will use the
+ Consider
+ 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 ++
+ filename = "myfile.txt"
try:
- with open("myfile.txt", "r") as file_reader:
- data = ""
+ # Attempt to open the file in read mode ('r')
+ with open(filename, "r") as file_reader:
+ # Iterate over each line in the file
for line in file_reader:
- data += line # line already includes the newline character
- print(data)
- except FileNotFoundError as e:
- print("An error occurred.")
- import traceback
- traceback.print_exc()
-
+ print(line.strip())
+ except:
+ #catches if the file doesn't exist or can't be written to
+ print("file could not be opened")
- And the Java equivalent:
+
import java.io.File;
- import java.io.FileNotFoundException;
- import java.util.Scanner
-
+ import java.io.FileNotFoundException; // This import is necessary to handle the exception if the file is not found
+ import java.util.Scanner;
public class ReadFile {
- public static void main(String[] args) {
- try {
- File myFile = new File("myfile.txt");
- Scanner fileReader = new Scanner(myFile);
- String data = "";
- while (fileReader.hasNextLine()) {
- data = data + fileReader.nextLine() + System.lineSeparator();
+ public static void main (String[] args) {
+ String filename = "myfile.txt";
+ try (Scanner fileReader = new Scanner(new File(filename))) { // try to open the file and create a Scanner object
+ while (fileReader.hasNextLine()) { // while there is a next line in the file
+ String data = fileReader.nextLine();
+ System.out.println(data);
}
- System.out.println(data);
- fileReader.close();
- } catch (FileNotFoundException e) {
- System.out.println("An error occurred.");
- e.printStackTrace();
+ }
+ catch (FileNotFoundException e) { // and catch the exception if the file is not found
+ System.out.println("Error: The file '" + filename + "' was not found.");
}
}
}
- In this code, we simply print the contents of the file to the console, but it is easy to imagine how the
+ You may have noticed that there are some new methods you haven't seen yet. The
+ Construct the part of a Java program that opens a file with a
@@ -397,70 +335,91 @@
- Let us create the framework for a class that will write to a file. Let's call this class
+
+
+ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
-
+ import java.util.Scanner;
public class WriteFile {
public static void main(String[] args) {
-
+
}
}
-
- Next, we will create a
+
+ shows the Java code to create a FileWriter object. Note that the FileWriter object is created with the name of the file to write to as an argument. If the file does not exist, it will be created. If it does exist, it will be overwritten.:
+
+
+
+
FileWriter myWriter = new FileWriter("myfile.txt");
-
- In this next step, we will use the
+
+ shows the Java equivalent. This is almost completely identical except for the second line, which is very important!
+
+
+
+
myWriter.write("File successfully updated!");
myWriter.close();
-
- You may have noticed the
- Next, we will again add the required try/catch blocks utilizing the
- with open("filename.txt", "r") as file_reader:
- while True:
- line = file_reader.readline()
- if not line: # End of file
- break
- print(line.strip())
-
+
+
- And the equivalent Java code:
+
+
+
try {
FileWriter myWriter = new FileWriter("myfile.txt");
myWriter.write("File successfully updated!");
@@ -470,31 +429,51 @@
System.out.println("An error occurred.");
e.printStackTrace();
}
-
- And that's it! We will add our code to the foundational code for a complete program. First, an example of equivalent Python code:
+ And that's it! We will add our code to the foundational code for a complete program. First,
+ ++
try:
with open("myfile.txt", "w") as my_writer:
my_writer.write("File successfully updated!")
print("File successfully written to.")
- except OSError as e:
+ except OSError:
print("An error occurred.")
import traceback
traceback.print_exc()
- The completed Java code:
+
+ ++
import java.io.FileWriter;
import java.io.IOException;
@@ -512,12 +491,10 @@
}
}
}
-
+ ~
- Files in a specific directory can be written to using the same technique as the last section in which file paths are specified, with two back slashes used in Windows environments. -
+@@ -526,20 +503,30 @@
- Speaking of overwriting data, what if we want to append text to the end of any text already in
+
+
FileWriter myWriter = new FileWriter("myfile.txt", true); // true enables append mode
-
- Now, when we use
+ ++
import java.io.FileWriter;
import java.io.IOException;
@@ -559,78 +546,251 @@
}
- Then if we run the program twice, the contents of
+
+
File successfully updated!File successfully updated!
-
- This doesn't look very good! If we want each additional write to appear on a new line? The first solution may be to use the
+
+
myWriter.write("File successfully updated!\n"); // Added newline character
- myWriter.close();
-
-
- The System.lineseseparator() method is a better solution. This method returns the system's default line separator, which is platform-dependent. For example, on Windows, it returns
- myWriter.write("File successfully updated!" + System.lineseparator()); // Added newline character
- myWriter.close();
-
+ myWriter.close();
- Running it twice will result in the following contents in myfile.txt:
+ Running the code with the newline character twice will result in the following contents in myfile.txt as
+
+
File successfully updated!
File successfully updated!
-
- Finally, we will take a look at using Java to delete files. This one is pretty straight-forward and follows the structure used to create files. This time, however, try/catch blocks are not needed for the program to compile. We will call this class DeleteFile. The completed code should look something like this.
+ Lastly, we will take a look at using Java to delete a file. This is pretty straight-forward and follows the structure used to create files.
- import java.io.File;
-
- public class DeleteFile {
- public static void main(String[] args) {
- File myFile = new File("myfile.txt");
- if (myFile.delete()) {
- System.out.println("Deleted " + myFile.getName());
- } else {
- System.out.println("File could not be deleted.");
- }
- }
+ import java.io.File;
+ import java.io.IOException;
+
+ public class CreateFile {
+ public static void main(String[] args) {
+ File myFile = new File("myfile.txt");
+ try {
+ if (myFile.createNewFile()) {
+ System.out.println("The file " + myFile.getName() + " was created successfully.");
+ } else {
+ System.out.println("The file " + myFile.getName() + " already exists.");
+ }
+ } catch (IOException e) {
+ // This code runs if an IOException occurs
+ System.out.println("An error occurred while creating the file.");
+ e.printStackTrace(); // This prints the stack trace for more detailed error info
+ }
+ }
}
+ And finally,
+import java.io.File;
+import java.io.IOException;
+public class DeleteFile {
+ public static void main(String[] args) {
+ try {
+ File myFile = new File("myfile.txt");
+
+ // Create the file (does nothing if it already exists)
+ myFile.createNewFile();
+ System.out.println("File created: " + myFile.getName());
+
+ // Delete the file
+ if (myFile.delete()) {
+ System.out.println("Deleted " + myFile.getName());
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
+
+
+
- This is almost identical to the code within the try block of the CreateFile class we made earlier. The main difference is the use of the
To work with files in Java, you must import specific classes like
You can create a new file using
Reading from files is done using a
To write to a file, use a
You can delete a file using the
Which import is needed to create and manipulate files in Java?
+import java.util.File;
+No,
import java.io.File;
+Correct!
import java.file.Input;
+No, this is not a valid import for file operations.
+import java.system.io.*;
+No, there is no such package in Java.
+What does
It throws an exception.
+No, it only throws an exception for access errors, not for existing files.
+false
+Correct! It returns
true
+No,
null
+No,
Which method checks if a file has more lines to read using a Scanner?
+nextLine()
+No,
hasMore()
+No, this is not a method of
hasNextLine()
+Correct! This checks if there is another line available to read.
+canReadLine()
+No, this is not a standard method in the Scanner class.
++ Making mistakes is a very natural part of learning Java—or any other programming language. In fact, mistakes are an absolutely essential part of the learning process! So, try not to feel discouraged when you encounter errors in your code. Instead, view each mistake as an opportunity to deepen your understanding. Every programmer, no matter how experienced, encounters errors in their code. The key is to learn how to identify and correct these errors while also learning from them. +
++ The good news is that most errors happen for just a few common reasons, and once you recognize the patterns, they become much easier to fix. This chapter focuses on those typical mistakes and how to understand and correct them. +
++ One of the best ways to correct these errors is to slow down and test your code in small pieces. Write a few lines, compile, and check the output before moving on. If something goes wrong, read the first error message very carefully, and focus on fixing one problem at a time. Often, solving the first error helps fix others that follow. +
+
+ A simple debugging technique is to use
+ Consider the following example where we have a method that multiplies a number by two. We can add print statements to help us debug the code and verify that the method is being called correctly and returning the expected result as shown below in
+ // DebugExample.java
+ public class DebugExample {
+
+ public static void main(String[] args) {
+ int number = 10;
+ int result = multiplyByTwo(number);
+ // Debugging: print the result to verify the method worked
+ System.out.println("Result after multiplying: " + result);
+ }
+
+ public static int multiplyByTwo(int value) {
+ // Debugging: print the input value to check it's being passed correctly
+ System.out.println("multiplyByTwo received: " + value);
+ return value * 2;
+ }
+ }//End of class
+
+
+ In
+ Useful tools in the built-in Java debugger can help you step through your code, inspect variables, and evaluate expressions at runtime. Familiarizing yourself with these tools can greatly enhance your debugging efficiency. +
++ Above all, when you encounter an error, be patient with yourself. Every mistake you make is an opportunity to learn. +
+
+ A common mistake in Java is to forget that every statement must end with a semicolon (
+ // Histo.java
+ import java.util.Scanner; // Imports Scanner
+
+ public class Histo { // Class declaration
+
+ public static void main(String[] args) { // Main method declaration
+ Scanner data = null // The error will point here
+ System.out.println("This line will not compile.");
+ }// End of main method
+ }//End of class
+
+
+ The error "';' expected" on line 7 of
In Python, you can use a variable without declaring it first, but in Java, you must declare all variables before using them.
- If you try to use a variable that has not been declared, the Java compiler will give you an error message like this:
+
import java.util.ArrayList; // Import necessary class
@@ -32,17 +111,69 @@
} // End of class
The 'cannot find symbol' error for the variable
+ Based on
Every variable must be declared with a data type before it is used.
+Correct! Java requires a variable to be declared with its type before it can be assigned or referenced.
+The "cannot find symbol" error happens because
Correct! That error means the compiler reached a variable it has no declaration for.
+Writing
Correct! Declaring
Java lets you use a variable without declaring it first, just like Python.
+Incorrect. Unlike Python, Java requires all variables to be declared before use.
+The error can be fixed by adding an import statement.
+Incorrect. The import is already present; the problem is the missing variable declaration, not a missing import.
+
- In Python, many classes are available by default. However, in Java, you must explicitly import most classes from external packages that you want to use.
+ In Python, many classes are available by default. However, in Java, you must explicitly import most classes from external packages that you want to use
@@ -57,8 +188,9 @@
} // End of class
- You may notice that this error message looks similar to the previous one, however, it has an entirely different cause. In Java, classes like
Unlike Python, where you can create a new object without explicitly using a keyword, Java requires the
// Histo.java // The filename for this example
@@ -84,8 +217,9 @@
} // End of class
- This error message occurs when you forget to use the
- A common mistake in Java is to forget that every statement must end with a semicolon (
- The error "';' expected" on line 7 of
Java is a statically typed language, meaning you must specify the type of objects that can be stored in a container like an
This is a compiler warning, not an error, indicating a potential type safety issue. It occurs because you are calling the
- // Histo.java
- import java.util.Scanner; // Imports Scanner
-
- public class Histo { // Class declaration
-
- public static void main(String[] args) { // Main method declaration
- Scanner data = null // The error will point here
- System.out.println("This line will not compile.");
- }// End of main method
- }//End of class
-
-
// UncheckedWarningDemo.java
@@ -145,6 +256,7 @@
} // End of class
In Java, every variable must be declared with its type before use; undeclared variables cause compilation errors.
+Java requires explicit import statements for classes from external packages (e.g.,
The
Every Java statement must end with a semicolon (
Java uses generics for type safety in containers like
Compiler error messages may sometimes be misleading; understanding common mistakes helps quickly identify the root cause.
+What happens if you use a variable in Java without declaring it first?
+The compiler gives an error indicating the variable cannot be found.
Correct! Java requires all variables to be declared before use.
The variable is automatically declared as type
No. Java does not implicitly declare variables.
The program compiles but throws an error at runtime.
No. This is a compile-time error.
Java ignores the variable and continues compiling.
No. Java will stop compiling with an error.
Why must you include import statements for classes like
Because these classes belong to external packages and are not automatically available.
Correct! Java requires explicit imports for external classes.
Because Java does not support standard input without imports.
No. Standard input is supported but needs the
Because the classes are only available in Python, not Java.
No. This is a Java-specific requirement.
Because the compiler ignores unknown classes without imports.
No. It causes a compile error instead.
What warning occurs when you use an
An "unchecked" warning indicating potential type safety issues.
Correct! Using raw types disables generic type checks.
A syntax error.
No. This is a compiler warning, not an error.
A runtime exception.
No. It only warns about possible runtime errors.
A logical error in the program.
No. The warning points out type safety concerns.
- In this chapter, we will explore how to translate your recursive logic from Python to Java. While the core concepts of recursion remain the same, the syntax and structure of your code will change somewhat. -
-- Let's take the familiar factorial function (which calculates the factorial of a number, namely the product of all positive integers from 1 to n). The logical steps in the code are the same, but the implementation details change. -
-- Here is a simple Python function implementation: -
-
-def factorial(n):
- # Check for negative numbers
- if n < 0:
- print("Factorials are only defined on non-negative integers.")
- return
- # Base Case: 0! or 1! is 1
- if n <= 1:
- return 1
- # Recursive Step: n * (n-1)!
- return n * factorial(n - 1)
-
-def main():
- number = 5
- print(str(number) + "! is " + str(factorial(number)))
-
-main()
-
-
- Many Python programs organize related functions into classes. The same factorial function can be placed inside a class as a method. Then you need to create an instance of the class to call the method. There we create the class
-class MathTools:
- def factorial(self, n):
- # Check for negative numbers
- if n < 0:
- print("Factorials are only defined on non-negative integers.")
- return
- # Base Case: 0! or 1! is 1
- if n <= 1:
- return 1
- # Recursive Step: n * (n-1)!
- return n * self.factorial(n - 1)
-
-def main():
- # Create an instance of the class and call the method
- math_tools = MathTools()
- number = 5
- print(str(number) + "! is " + str(math_tools.factorial(number)))
-
-main()
-
- - See if you can spot the differences in the Java version below. -
-- Here is the equivalent Java code: -
-
-public class MathTools {
- public static int factorial(int n) {
- // Check for negative numbers
- if (n < 0) {
- System.out.println("Factorials are only defined on non-negative integers.");
- return -1; // Return -1 to indicate error
- }
- // Base Case: 0! or 1! is 1
- if (n <= 1) {
- return 1;
- }
- // Recursive Step: n * (n-1)!
- return n * factorial(n - 1);
- }
-
- public static void main(String[] args) {
- int number = 5;
- System.out.println(number + "! is " + factorial(number));
- }
-}
-
-
- Notice the key differences from Python: instead of
- In many recursive algorithms, the recursive calls need extra information that the original caller shouldn't have to provide. For example, to recursively process an array, you need to keep track of the current position (index). To traverse a tree, you need to know the current node. This extra information clutters the public-facing method signature. -
-- A common pattern to solve this is using a private helper method. This pattern lets you create a clean, simple public method that users will call, while the private helper method handles the complex details of the recursion. The public method typically makes the initial call to the private helper, providing the necessary starting values for the extra parameters. -
-
- Let's see this pattern in action with an example that calculates the sum of all elements in an integer array. The public
- You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, - are elegant but inefficient, due to branching recursive calls filling the call stack. A common pattern to solve - this is using a private helper method. -
-
- The following example demonstrates this pattern. The public
- The following Java code demonstrates a similar pattern. -
- -
- public class FibonacciExample {
- public int fib(int n) {
- if (n < 0) {
- throw new IllegalArgumentException("Input cannot be negative.");
- }
- // Initial call to the recursive helper with depth 0.
- return this._fibHelper(n, 0, 1, 0);
- }
- private int _fibHelper(int count, int a, int b, int depth) {
- // Create an indent string based on the recursion depth.
- String indent = " ".repeat(depth);
- // Print when the method is entered (pushed onto the stack).
- System.out.printf("%s[>>] ENTERING _fibHelper(count=%d, a=%d, b=%d)%n", indent, count, a, b);
- // Base Case: When the count reaches 0, 'a' holds the result.
- if (count == 0) {
- System.out.printf("%s[<<] EXITING (Base Case) -> returns %d%n", indent, a);
- return a;
- }
- // Recursive Step.
- int result = this._fibHelper(count - 1, b, a + b, depth + 1);
- // Print when the method exits (popped from the stack).
- System.out.printf("%s[<<] EXITING (Recursive Step) -> passing %d%n", indent, result);
- return result;
- }
- public static void main(String[] args) {
- FibonacciExample calculator = new FibonacciExample();
- int n = 4; // Let's calculate the 4th Fibonacci number.
- System.out.printf("--- Calculating fib(%d) ---%n", n);
- int result = calculator.fib(n);
- System.out.println("--------------------------");
- System.out.printf("The %dth Fibonacci number is: %d%n", n, result);
- }
- }
-
-
- This helper method approach is significantly more efficient in terms of time than the classic branching recursion (where
- However, regarding memory efficiency, the comparison is different. The maximum depth of the call stack for both the naive and the helper method is proportional to n, giving them both a space complexity of O(n). This means that while the helper method is much faster, it is equally vulnerable to a
- The following Python code demonstrates the same pattern, using a public method to initiate the calculation and a private helper method to perform the recursion. -
-
- class FibonacciExample:
- def fib(self, n: int) -> int:
- """
- Public method to start the Fibonacci calculation.
- """
- if n < 0:
- raise ValueError("Input cannot be negative.")
- # Initial call to the recursive helper with depth 0.
- return self._fib_helper(n, 0, 1, 0)
-
- def _fib_helper(self, count: int, a: int, b: int, depth: int) -> int:
- """
- Private helper that performs the tail recursion to find the number.
- """
- # Create an indent string based on the recursion depth.
- indent = " " * depth
- # Print when the method is entered (pushed onto the stack).
- print(f"{indent}[>>] ENTERING _fib_helper(count={count}, a={a}, b={b})")
-
- # Base Case: When the count reaches 0, 'a' holds the result.
- if count == 0:
- print(f"{indent}[<<] EXITING (Base Case) -> returns {a}")
- return a
-
- # Recursive Step.
- result = self._fib_helper(count - 1, b, a + b, depth + 1)
- # Print when the method exits (popped from the stack).
- print(f"{indent}[<<] EXITING (Recursive Step) -> passing {result}")
- return result
-
- # The standard Python entry point, equivalent to Java's `main` method.
- if __name__ == "__main__":
- calculator = FibonacciExample()
- n = 4 # Let's calculate the 4th Fibonacci number.
- print(f"--- Calculating fib({n}) ---")
- result = calculator.fib(n)
- print("--------------------------")
- print(f"The {n}th Fibonacci number is: {result}")
-
- - The consequence of deep recursion, running out of stack space, is a concept you've already encountered in Python. Java handles this in a very similar way, throwing an error when the call stack depth is exceeded. -
-- The key difference is the name of the error: -
-
- Neither language supports
- The following Python code demonstrates a situation where a function calls itself indefinitely without a base case, leading to aRecursionError. -
-
- def cause_recursion_error():
- """
- This function calls itself without a base case, guaranteeing an error.
- """
- cause_recursion_error()
-
- # Standard Python entry point
- if __name__ == "__main__":
- print("Calling the recursive function... this will end in an error!")
-
- # This line starts the infinite recursion.
- # Python will stop it and raise a RecursionError automatically.
- cause_recursion_error()
-
- - The following Java code demonstrates a similar situation, where a method calls itself indefinitely without a base case, leading to a StackOverflowError. -
-
- public class Crash {
- public static void causeStackOverflow() {
- // This method calls itself endlessly without a stopping condition (a base case).
- // Each call adds a new layer to the program's call stack.
- // Eventually, the stack runs out of space, causing the error.
- causeStackOverflow();
- }
- // A main method is required to run the program.
- public static void main(String[] args) {
- System.out.println("Calling the recursive method... this will end in an error!");
- // This line starts the infinite recursion.
- causeStackOverflow();
- }
- }
-
-