PYnative

Python Programming

  • Learn Python ▼
    • Python Tutorials
    • Python Basics
    • Python Interview Q&As
  • Exercises ▼
    • Python Exercises
    • C++ Exercises
    • C Programming Exercises
    • Java Exercises
    • C# Exercises
  • Quizzes
  • Online Compilers▼
    • Online Python Code Editor
    • Online C Compiler
    • Online C++ Compiler
    • Online Java Compiler
    • Online C# Compiler
Home » Java Exercises » Java Method Exercises: 20+ Coding Problems with Solutions

Java Method Exercises: 20+ Coding Problems with Solutions

Updated on: July 6, 2026 | Leave a Comment

This set of 21 Java method exercises goes well beyond writing a basic function, walking through the patterns that show up constantly in real Java codebases.

You’ll practice method overloading, variable-length arguments (varargs), classic recursion , and Java’s pass-by-value semantics for primitives versus object references. Later exercises cover the fluent interface (method chaining) pattern, encapsulating logic in private helper methods, and passing behavior into a method using functional interfaces and lambda expressions.

Each exercise includes a Problem Statement, Purpose, Hint, and a Solution with Explanation that breaks down exactly how and why the code works.

  • Also, See: Java Exercises with over 20+ topic-wise sets and 575+ coding questions to practice.
  • Practice questions using our Online Java Compiler
+ Table of Contents (21 Exercises)

Table of contents

  • Exercise 1: Circle Area Calculator
  • Exercise 2: The Overloaded Adder
  • Exercise 3: Multi-Shape Area Calculator
  • Exercise 4: Flexible Print Details
  • Exercise 5: Array Searcher
  • Exercise 6: Absolute Value Replication
  • Exercise 7: Dynamic Summation
  • Exercise 8: String Concat with Separator
  • Exercise 9: Safe Minimum Finder
  • Exercise 10: Dynamic Average Calculator
  • Exercise 11: Shopping Invoice Logger
  • Exercise 12: Factorial Finder
  • Exercise 13: Fibonacci Sequence Decoder
  • Exercise 14: Sum of Digits
  • Exercise 15: Power Calculation
  • Exercise 16: Recursive Array Summation
  • Exercise 17: Pass-by-Value Proof
  • Exercise 18: Fluent Interface / Method Chaining
  • Exercise 19: Helper Method Encapsulation
  • Exercise 20: In-Place Array Transformer
  • Exercise 21: Basic Functional Method

Exercise 1: Circle Area Calculator

Problem Statement: Write a method double calculateArea(double radius) that calculates and returns the area of a circle. As a bonus, handle negative radius inputs by returning -1.0.

Purpose: This exercise helps you practice writing a method with a return value and basic input validation, foundational skills for building reusable utility methods.

Given Input: calculateArea(5.0)

Expected Output: Area = 78.53981633974483

▼ Hint
  • Define the method with a double parameter for radius and a double return type.
  • Check if the radius is negative first; if so, return -1.0 immediately.
  • Otherwise, calculate the area using the formula π × radius².
  • Use Math.PI for an accurate value of pi.
▼ Solution & Explanation
public class Main {
    public static double calculateArea(double radius) {
        if (radius < 0) {
            return -1.0;
        }
        return Math.PI * radius * radius;
    }

    public static void main(String[] args) {
        double result = calculateArea(5.0);
        System.out.println("Area = " + result);
    }
}Code language: Java (java)

Explanation:

  • if (radius < 0): Guards against invalid input before doing any calculation, returning -1.0 as a sentinel value for errors.
  • Math.PI * radius * radius: Applies the standard circle area formula using Java's built-in constant for pi.
  • Return type double: Ensures the method can return a precise decimal value rather than being rounded to an integer.
  • Alternative: You could use Math.pow(radius, 2) instead of radius * radius, though direct multiplication is generally faster for a simple square.

Exercise 2: The Overloaded Adder

Problem Statement: Write three overloaded methods named add(): int add(int a, int b), int add(int a, int b, int c), and double add(double a, double b).

Purpose: This exercise helps you practice method overloading, understanding how Java distinguishes methods based on parameter types and count.

Given Input: add(2, 3), add(2, 3, 4), add(2.5, 3.5)

Expected Output:

add(2, 3) = 5
add(2, 3, 4) = 9
add(2.5, 3.5) = 6.0
▼ Hint
  • Define multiple methods with the same name but different parameter lists.
  • Java distinguishes overloaded methods by the number and/or types of parameters, not by return type alone.
  • The two-integer version and three-integer version differ by parameter count.
  • The double version differs by parameter type, even though its parameter count matches the two-integer version.
▼ Solution & Explanation
public class Main {
    public static int add(int a, int b) {
        return a + b;
    }

    public static int add(int a, int b, int c) {
        return a + b + c;
    }

    public static double add(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        System.out.println("add(2, 3) = " + add(2, 3));
        System.out.println("add(2, 3, 4) = " + add(2, 3, 4));
        System.out.println("add(2.5, 3.5) = " + add(2.5, 3.5));
    }
}Code language: Java (java)

Explanation:

  • add(int a, int b) and add(int a, int b, int c): Differ in the number of parameters, so Java treats them as distinct methods.
  • add(double a, double b): Differs in parameter type from the two-integer version, letting Java select the correct overload based on the argument types passed in.
  • Compile-time resolution: Java determines which overload to call at compile time based on the arguments' types, not at runtime.
  • Alternative: You could achieve similar flexibility with a single method using Object... varargs and manual type checking, but overloading is clearer and type-safe.

Exercise 3: Multi-Shape Area Calculator

Problem Statement: Create overloaded methods named computeArea() to handle different shapes: double computeArea(double radius) for a circle, double computeArea(double length, double width) for a rectangle, and double computeArea(double base, double height, boolean isTriangle) for a triangle.

Purpose: This exercise helps you practice designing overloaded methods that share a name but serve different geometric purposes, distinguished by their parameter signatures.

Given Input: computeArea(5.0), computeArea(4.0, 6.0), computeArea(4.0, 6.0, true)

Expected Output:

Circle Area = 78.53981633974483
Rectangle Area = 24.0
Triangle Area = 12.0
▼ Hint
  • Define a version that takes a single double for the circle's radius.
  • Define a version that takes two double values for the rectangle's length and width.
  • Define a version that takes two double values and a boolean flag for the triangle, even though the flag isn't strictly needed for the calculation.
  • Java will select the correct method automatically based on how many arguments are passed and their types.
▼ Solution & Explanation
public class Main {
    public static double computeArea(double radius) {
        return Math.PI * radius * radius;
    }

    public static double computeArea(double length, double width) {
        return length * width;
    }

    public static double computeArea(double base, double height, boolean isTriangle) {
        return 0.5 * base * height;
    }

    public static void main(String[] args) {
        System.out.println("Circle Area = " + computeArea(5.0));
        System.out.println("Rectangle Area = " + computeArea(4.0, 6.0));
        System.out.println("Triangle Area = " + computeArea(4.0, 6.0, true));
    }
}Code language: Java (java)

Explanation:

  • computeArea(double radius): Matches calls with exactly one double argument, calculating a circle's area.
  • computeArea(double length, double width): Matches calls with two double arguments, calculating a rectangle's area.
  • computeArea(double base, double height, boolean isTriangle): Matches calls with two doubles and a boolean, calculating a triangle's area using the base-height formula.
  • Alternative: You could rename each method distinctly, like circleArea() and rectangleArea(), for extra clarity, but overloading keeps a consistent API name for related operations.

Exercise 4: Flexible Print Details

Problem Statement: Create a series of printDetails() methods: one accepting only a name (String), one accepting a name and an age (String, int), and one accepting a name, age, and city (String, int, String). Each should print a cleanly formatted summary.

Purpose: This exercise helps you practice overloading with an increasing number of parameters, a common pattern for building flexible utility or logging methods.

Given Input: printDetails("Alice"), printDetails("Bob", 25), printDetails("Charlie", 30, "New York")

Expected Output:

Name: Alice
Name: Bob, Age: 25
Name: Charlie, Age: 30, City: New York
▼ Hint
  • Write the simplest overload first, accepting only a name and printing it.
  • Write a second overload accepting a name and an age, printing both together.
  • Write a third overload accepting a name, age, and city, printing all three.
  • Each version should build its own formatted output rather than calling the others.
▼ Solution & Explanation
public class Main {
    public static void printDetails(String name) {
        System.out.println("Name: " + name);
    }

    public static void printDetails(String name, int age) {
        System.out.println("Name: " + name + ", Age: " + age);
    }

    public static void printDetails(String name, int age, String city) {
        System.out.println("Name: " + name + ", Age: " + age + ", City: " + city);
    }

    public static void main(String[] args) {
        printDetails("Alice");
        printDetails("Bob", 25);
        printDetails("Charlie", 30, "New York");
    }
}Code language: Java (java)

Explanation:

  • printDetails(String name): Handles calls with only a name, producing the shortest summary.
  • printDetails(String name, int age): Handles calls with a name and age, adding the age to the printed output.
  • printDetails(String name, int age, String city): Handles calls with all three pieces of information, producing the fullest summary.
  • Alternative: You could have the shorter overloads call the longer one with default values, like printDetails(name, 0, "Unknown"), which reduces duplication but requires meaningful defaults.

Exercise 5: Array Searcher

Problem Statement: Write two overloaded search() methods. One searches for an integer target inside an int[] array; the other searches for a String target inside a String[] array. Both should return the index of the found element, or -1 if not found.

Purpose: This exercise helps you practice overloading based on array element type, a pattern used in generic-feeling utility methods before generics are introduced.

Given Input: search(new int[]{10, 20, 30, 40}, 30), search(new String[]{"apple", "banana", "cherry"}, "banana")

Expected Output:

Index of 30 = 2
Index of "banana" = 1
▼ Hint
  • Write one version of search() that accepts an int[] array and an int target.
  • Write a second version that accepts a String[] array and a String target.
  • Loop through the array in each version, comparing each element to the target.
  • Return the index immediately upon a match, or -1 if the loop finishes without finding one.
▼ Solution & Explanation
public class Main {
    public static int search(int[] arr, int target) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == target) {
                return i;
            }
        }
        return -1;
    }

    public static int search(String[] arr, String target) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i].equals(target)) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        System.out.println("Index of 30 = " + search(new int[]{10, 20, 30, 40}, 30));
        System.out.println("Index of \"banana\" = " + search(new String[]{"apple", "banana", "cherry"}, "banana"));
    }
}Code language: Java (java)

Explanation:

  • search(int[] arr, int target): Uses == to compare primitive int values directly, since primitives can be compared with equality operators.
  • search(String[] arr, String target): Uses .equals() instead of == to compare String content rather than object references.
  • return i / return -1: Returns the matching index immediately, or -1 once the loop completes without a match.
  • Alternative: You could use Arrays.asList(arr).indexOf(target) for the String version to leverage a built-in list search, though it doesn't work directly for primitive int[] arrays.

Exercise 6: Absolute Value Replication

Problem Statement: Create your own version of Math.abs(). Overload a method named absoluteValue() to accept and process int, double, and long types.

Purpose: This exercise helps you practice overloading across primitive numeric types, useful for understanding how Java resolves overloads for related but distinct types.

Given Input: absoluteValue(-5), absoluteValue(-3.14), absoluteValue(-100000000000L)

Expected Output:

absoluteValue(-5) = 5
absoluteValue(-3.14) = 3.14
absoluteValue(-100000000000) = 100000000000
▼ Hint
  • Write a version that accepts an int and returns an int.
  • Write a version that accepts a double and returns a double.
  • Write a version that accepts a long and returns a long.
  • In each version, check if the value is negative and negate it if so; otherwise return it unchanged.
▼ Solution & Explanation
public class Main {
    public static int absoluteValue(int value) {
        return value < 0 ? -value : value;
    }

    public static double absoluteValue(double value) {
        return value < 0 ? -value : value;
    }

    public static long absoluteValue(long value) {
        return value < 0 ? -value : value;
    }

    public static void main(String[] args) {
        System.out.println("absoluteValue(-5) = " + absoluteValue(-5));
        System.out.println("absoluteValue(-3.14) = " + absoluteValue(-3.14));
        System.out.println("absoluteValue(-100000000000) = " + absoluteValue(-100000000000L));
    }
}Code language: Java (java)

Explanation:

  • value < 0 ? -value : value: Uses a ternary expression to negate the value only if it's negative, returning it unchanged otherwise.
  • Separate int, double, and long overloads: Ensures each numeric type is handled with its own precision and range, without relying on implicit widening or narrowing conversions.
  • Method resolution: Java automatically picks the overload matching the argument's exact type, or the closest compatible type if no exact match exists.
  • Alternative: You could write a single method using double for all inputs, but that would lose precision for large long values and force unnecessary type conversions.

Exercise 7: Dynamic Summation

Problem Statement: Create a method int sumAll(int... numbers) that accepts any quantity of integers (including zero) and returns their total sum.

Purpose: This exercise helps you practice varargs syntax, useful for writing methods that accept a flexible number of arguments without overloading.

Given Input: sumAll(1, 2, 3, 4, 5)

Expected Output: Sum = 15

▼ Hint
  • Declare the parameter using the int... syntax, which allows zero or more integers to be passed.
  • Inside the method, treat the parameter as a regular int[] array.
  • Loop through the array and accumulate a running total.
  • Return the total after the loop completes, which will be zero if no arguments were passed.
▼ Solution & Explanation
public class Main {
    public static int sumAll(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }

    public static void main(String[] args) {
        System.out.println("Sum = " + sumAll(1, 2, 3, 4, 5));
    }
}Code language: Java (java)

Explanation:

  • int... numbers: Declares a varargs parameter, letting the caller pass any number of int arguments, which Java packages into an array.
  • for (int num : numbers): Iterates over the array just like any other array, since varargs behave as arrays inside the method body.
  • total += num: Accumulates the sum across all provided arguments.
  • Alternative: You could use Arrays.stream(numbers).sum() for a more concise one-liner, though the loop makes the accumulation logic explicit.

Exercise 8: String Concat with Separator

Problem Statement: Write a method String joinStrings(String separator, String... words) that merges all passed words together, separated by the designated separator string.

Purpose: This exercise helps you practice combining a fixed parameter with a varargs parameter, a common pattern for methods needing at least one required argument alongside flexible ones.

Given Input: joinStrings("-", "Java", "is", "fun")

Expected Output: Result = Java-is-fun

▼ Hint
  • Place the fixed separator parameter before the varargs parameter, since varargs must always come last.
  • Use a StringBuilder to build the result incrementally.
  • Append each word followed by the separator, except after the very last word.
  • Handle the case of zero words gracefully by returning an empty string.
▼ Solution & Explanation
public class Main {
    public static String joinStrings(String separator, String... words) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < words.length; i++) {
            result.append(words[i]);
            if (i != words.length - 1) {
                result.append(separator);
            }
        }
        return result.toString();
    }

    public static void main(String[] args) {
        System.out.println("Result = " + joinStrings("-", "Java", "is", "fun"));
    }
}Code language: Java (java)

Explanation:

  • String separator, String... words: Places the required separator argument before the varargs parameter, which Java requires since varargs must be the last parameter.
  • if (i != words.length - 1): Avoids appending a trailing separator after the final word.
  • result.toString(): Converts the accumulated StringBuilder content into the final joined string.
  • Alternative: You could use String.join(separator, words) directly, since it accepts a varargs array too, but writing it manually shows how the built-in method works internally.

Exercise 9: Safe Minimum Finder

Problem Statement: Write a method int findMin(int first, int... rest) that guarantees at least one integer argument is passed to prevent runtime exceptions, returning the smallest value among all inputs.

Purpose: This exercise helps you practice combining a required parameter with varargs to enforce a minimum argument count at compile time.

Given Input: findMin(8, 3, 12, 5, 1)

Expected Output: Minimum = 1

▼ Hint
  • Require the first argument as a normal int parameter, separate from the varargs.
  • This guarantees the caller must supply at least one value, since the compiler enforces the required parameter.
  • Start your running minimum using the required first argument.
  • Loop through the varargs array and update the minimum whenever a smaller value is found.
▼ Solution & Explanation
public class Main {
    public static int findMin(int first, int... rest) {
        int min = first;
        for (int num : rest) {
            if (num < min) {
                min = num;
            }
        }
        return min;
    }

    public static void main(String[] args) {
        System.out.println("Minimum = " + findMin(8, 3, 12, 5, 1));
    }
}Code language: Java (java)

Explanation:

  • int first, int... rest: Splits the arguments into one required value and any number of additional values, guaranteeing at least one argument overall.
  • int min = first: Initializes the running minimum using the guaranteed first argument, avoiding the need to check for an empty array.
  • num < min: Updates the minimum whenever a smaller value is encountered while scanning the remaining arguments.
  • Alternative: You could accept a plain int... values array without a required first parameter, but then you would need extra logic to handle the case of zero arguments.

Exercise 10: Dynamic Average Calculator

Problem Statement: Create a method double calculateAverage(double... values) that computes the average of the provided numbers. Return 0.0 if no arguments are passed.

Purpose: This exercise helps you practice handling the empty-input edge case with varargs, an important defensive programming habit.

Given Input: calculateAverage(4.0, 8.0, 15.0, 16.0)

Expected Output: Average = 10.75

▼ Hint
  • Check if the varargs array has a length of zero first, and return 0.0 immediately in that case to avoid dividing by zero.
  • Otherwise, loop through the array and accumulate a running total.
  • Divide the total by the number of values to compute the average.
  • Return the resulting double value.
▼ Solution & Explanation
public class Main {
    public static double calculateAverage(double... values) {
        if (values.length == 0) {
            return 0.0;
        }

        double total = 0.0;
        for (double value : values) {
            total += value;
        }

        return total / values.length;
    }

    public static void main(String[] args) {
        System.out.println("Average = " + calculateAverage(4.0, 8.0, 15.0, 16.0));
    }
}Code language: Java (java)

Explanation:

  • values.length == 0: Guards against dividing by zero when no arguments are passed, returning 0.0 as a safe default.
  • for (double value : values): Iterates over the varargs array to accumulate the total sum of all provided values.
  • total / values.length: Divides the sum by the count of values to compute the average.
  • Alternative: You could use Arrays.stream(values).average().orElse(0.0), which handles the empty case built in, though the manual loop is clearer for beginners.

Exercise 11: Shopping Invoice Logger

Problem Statement: Write a method void logItems(String storeName, String... items) that prints out a stylized receipt header using the store name, followed by a numbered list of all items passed to the varargs parameter.

Purpose: This exercise helps you practice using varargs for real-world formatted output, useful for building lightweight reporting or logging utilities.

Given Input: logItems("Java Mart", "Bread", "Milk", "Eggs")

Expected Output:

=== Java Mart ===
1. Bread
2. Milk
3. Eggs
▼ Hint
  • Print a formatted header line first, incorporating the store name.
  • Loop through the varargs items array using an index-based loop so you can number each entry.
  • Print each item with its position number, starting from 1 instead of 0.
  • Since the method returns void, print directly instead of building a return value.
▼ Solution & Explanation
public class Main {
    public static void logItems(String storeName, String... items) {
        System.out.println("=== " + storeName + " ===");
        for (int i = 0; i < items.length; i++) {
            System.out.println((i + 1) + ". " + items[i]);
        }
    }

    public static void main(String[] args) {
        logItems("Java Mart", "Bread", "Milk", "Eggs");
    }
}Code language: Java (java)

Explanation:

  • String storeName, String... items: Requires a store name while allowing any number of item names to follow.
  • "=== " + storeName + " ===": Prints a simple stylized header framing the store name.
  • (i + 1) + ". " + items[i]: Numbers each item starting from 1, which reads more naturally than a zero-based index.
  • Alternative: You could use a for-each loop with a separate counter variable instead of an index-based loop, though the index-based version is more concise here.

Exercise 12: Factorial Finder

Problem Statement: Write a recursive method long factorial(int n) that returns the factorial of n.

Purpose: This exercise helps you practice identifying a base case and a recursive case, the two building blocks of any recursive method.

Given Input: factorial(5)

Expected Output: Factorial = 120

▼ Hint
  • Identify the base case: the factorial of 0 or 1 is 1.
  • For the recursive case, multiply n by the factorial of n - 1.
  • Each recursive call should move closer to the base case by decreasing n.
  • Use long as the return type to accommodate larger factorial values that would overflow an int.
▼ Solution & Explanation
public class Main {
    public static long factorial(int n) {
        if (n <= 1) {
            return 1;
        }
        return n * factorial(n - 1);
    }

    public static void main(String[] args) {
        System.out.println("Factorial = " + factorial(5));
    }
}Code language: Java (java)

Explanation:

  • if (n <= 1): Defines the base case, stopping the recursion once n reaches 0 or 1.
  • n * factorial(n - 1): Defines the recursive case, breaking the problem into a smaller version of itself.
  • Return type long: Prevents overflow for larger inputs, since factorials grow very quickly.
  • Alternative: You could compute the factorial iteratively with a for loop, which avoids the overhead of repeated method calls but doesn't demonstrate recursion.

Exercise 13: Fibonacci Sequence Decoder

Problem Statement: Write a recursive method int fibonacci(int n) that calculates the n-th number in the Fibonacci sequence.

Purpose: This exercise helps you practice recursion with two base cases and two recursive branches, a pattern common to many divide-and-conquer problems.

Given Input: fibonacci(7)

Expected Output: Fibonacci(7) = 13

▼ Hint
  • Identify the two base cases: fibonacci(0) returns 0 and fibonacci(1) returns 1.
  • For the recursive case, return the sum of fibonacci(n - 1) and fibonacci(n - 2).
  • Each recursive call branches into two further calls, forming a tree of calls.
  • This straightforward approach recalculates values many times, which is fine for small inputs but inefficient for large ones.
▼ Solution & Explanation
public class Main {
    public static int fibonacci(int n) {
        if (n == 0) {
            return 0;
        }
        if (n == 1) {
            return 1;
        }
        return fibonacci(n - 1) + fibonacci(n - 2);
    }

    public static void main(String[] args) {
        System.out.println("Fibonacci(7) = " + fibonacci(7));
    }
}Code language: Java (java)

Explanation:

  • if (n == 0) / if (n == 1): Defines the two base cases needed to anchor the recursion, since each Fibonacci number depends on the two before it.
  • fibonacci(n - 1) + fibonacci(n - 2): Defines the recursive case, combining the two preceding Fibonacci numbers.
  • Call tree: Each call to fibonacci(n) spawns two more calls, so the number of calls grows exponentially with n.
  • Alternative: You could use memoization with a Map<Integer, Integer> to cache previously computed values, dramatically reducing redundant calculations for larger inputs.

Exercise 14: Sum of Digits

Problem Statement: Write a recursive method int sumDigits(int n) that takes an integer and sums its individual digits (e.g., passing 1234 returns 1 + 2 + 3 + 4 = 10).

Purpose: This exercise helps you practice using modulus and division together to peel digits off a number recursively.

Given Input: sumDigits(1234)

Expected Output: Sum of Digits = 10

▼ Hint
  • Identify the base case: when n is 0, return 0 since there are no more digits to add.
  • For the recursive case, add the last digit of n (using the modulus operator) to the result of the recursive call on the remaining digits.
  • Remove the last digit from n by dividing it by 10 for the recursive call.
  • This approach processes the number one digit at a time, from right to left.
▼ Solution & Explanation
public class Main {
    public static int sumDigits(int n) {
        if (n == 0) {
            return 0;
        }
        return (n % 10) + sumDigits(n / 10);
    }

    public static void main(String[] args) {
        System.out.println("Sum of Digits = " + sumDigits(1234));
    }
}Code language: Java (java)

Explanation:

  • if (n == 0): Defines the base case, stopping the recursion once all digits have been processed.
  • n % 10: Extracts the last digit of the current number using the modulus operator.
  • n / 10: Removes the last digit using integer division, preparing the smaller sub-problem for the next recursive call.
  • Alternative: You could convert the number to a string and iterate over its characters, but the arithmetic approach avoids unnecessary type conversion.

Exercise 15: Power Calculation

Problem Statement: Create a recursive method int power(int base, int exponent) that calculates base raised to exponent, without using Java's built-in Math.pow().

Purpose: This exercise helps you practice recursion where one parameter counts down toward the base case while another stays fixed.

Given Input: power(2, 10)

Expected Output: Result = 1024

▼ Hint
  • Identify the base case: any base raised to the power of 0 equals 1.
  • For the recursive case, multiply the base by the result of raising it to one lower exponent.
  • Each recursive call reduces the exponent by 1, moving toward the base case.
  • Assume a non-negative exponent for this exercise to keep the base case simple.
▼ Solution & Explanation
public class Main {
    public static int power(int base, int exponent) {
        if (exponent == 0) {
            return 1;
        }
        return base * power(base, exponent - 1);
    }

    public static void main(String[] args) {
        System.out.println("Result = " + power(2, 10));
    }
}Code language: Java (java)

Explanation:

  • if (exponent == 0): Defines the base case, since any number raised to the power of 0 is 1.
  • base * power(base, exponent - 1): Defines the recursive case, multiplying the base by the result of a smaller power calculation.
  • exponent - 1: Reduces the exponent with each call, guaranteeing the recursion eventually reaches the base case.
  • Alternative: You could use a divide-and-conquer approach that halves the exponent each call (exponentiation by squaring), which runs in logarithmic time instead of linear time.

Exercise 16: Recursive Array Summation

Problem Statement: Write a recursive method int sumArray(int[] arr, int index) that totals all elements in an integer array by processing elements from the specified index to the end of the array.

Purpose: This exercise helps you practice using an index parameter to track recursive progress through an array without modifying the array itself.

Given Input: sumArray(new int[]{4, 8, 15, 16, 23, 42}, 0)

Expected Output: Sum = 108

▼ Hint
  • Identify the base case: when the index reaches the length of the array, there's nothing left to add, so return 0.
  • For the recursive case, add the element at the current index to the result of summing the rest of the array.
  • Increment the index by 1 with each recursive call to move toward the end of the array.
  • The array itself doesn't need to be modified, only the index parameter changes between calls.
▼ Solution & Explanation
public class Main {
    public static int sumArray(int[] arr, int index) {
        if (index == arr.length) {
            return 0;
        }
        return arr[index] + sumArray(arr, index + 1);
    }

    public static void main(String[] args) {
        System.out.println("Sum = " + sumArray(new int[]{4, 8, 15, 16, 23, 42}, 0));
    }
}Code language: Java (java)

Explanation:

  • if (index == arr.length): Defines the base case, stopping the recursion once every element has been processed.
  • arr[index] + sumArray(arr, index + 1): Defines the recursive case, adding the current element to the sum of the remaining elements.
  • index + 1: Advances the index with each call, ensuring the recursion progresses toward the base case.
  • Alternative: You could sum the array from the end backward instead of the front, though starting at the beginning reads more naturally.

Exercise 17: Pass-by-Value Proof

Problem Statement: Create a method void modifyValues(int primitive, int[] reference). Inside, modify both inputs (reassign the primitive and alter an index of the array). Call this from your main method to observe and explain why the original array changed, but the original primitive did not.

Purpose: This exercise helps you practice understanding Java's pass-by-value semantics, and how they behave differently for primitives versus object references like arrays.

Given Input: int number = 10; int[] arr = {1, 2, 3}; modifyValues(number, arr);

Expected Output:

Before: number = 10, arr[0] = 1
After: number = 10, arr[0] = 99
▼ Hint
  • Inside the method, reassign the primitive parameter to a new value and change one element of the array parameter.
  • Print the primitive and array values in main both before and after calling the method.
  • Remember that Java is always pass-by-value, but for arrays, the value being passed is a reference to the same underlying array object.
  • Reassigning the primitive parameter only changes the local copy inside the method, while modifying an array index changes the shared object itself.
▼ Solution & Explanation
public class Main {
    public static void modifyValues(int primitive, int[] reference) {
        primitive = 999;
        reference[0] = 99;
    }

    public static void main(String[] args) {
        int number = 10;
        int[] arr = {1, 2, 3};
        System.out.println("Before: number = " + number + ", arr[0] = " + arr[0]);
        modifyValues(number, arr);
        System.out.println("After: number = " + number + ", arr[0] = " + arr[0]);
    }
}Code language: Java (java)

Explanation:

  • primitive = 999: Only reassigns the local copy of the primitive inside the method; the original variable in main is untouched, since primitives are passed by value.
  • reference[0] = 99: Modifies the array object itself, which both the caller and the method refer to through their own copies of the same reference.
  • Pass-by-value for references: Java always passes a copy of the reference variable, but that copy still points to the same object in memory, so mutations through it are visible outside the method.
  • Key takeaway: Reassigning a reference parameter to a brand new array, like reference = new int[]{...}, would not affect the caller's array, but mutating the existing array's contents does.

Exercise 18: Fluent Interface / Method Chaining

Problem Statement: Design a simple Hero class with fields like name, health, and weapon. Implement setter methods (setName(), setHealth()) that return this, allowing you to instantiate and configure an object in a single line: Hero player = new Hero().setName("Arthur").setHealth(100);

Purpose: This exercise helps you practice the fluent interface pattern, a common technique for writing readable, chainable configuration code.

Given Input: Hero player = new Hero().setName("Arthur").setHealth(100);

Expected Output: Hero{name='Arthur', health=100, weapon='null'}

▼ Hint
  • Define private fields for name, health, and weapon inside the Hero class.
  • Have each setter method perform its assignment and then return this, referring to the current object instance.
  • Returning this allows the next method call to be chained directly onto the result of the previous one.
  • Add a toString() method so you can easily print the final configured object.
▼ Solution & Explanation
class Hero {
    private String name;
    private int health;
    private String weapon;

    public Hero setName(String name) {
        this.name = name;
        return this;
    }

    public Hero setHealth(int health) {
        this.health = health;
        return this;
    }

    public Hero setWeapon(String weapon) {
        this.weapon = weapon;
        return this;
    }

    @Override
    public String toString() {
        return "Hero{name='" + name + "', health=" + health + ", weapon='" + weapon + "'}";
    }
}

public class Main {
    public static void main(String[] args) {
        Hero player = new Hero().setName("Arthur").setHealth(100);
        System.out.println(player);
    }
}Code language: Java (java)

Explanation:

  • return this: Returns a reference to the current object at the end of each setter, which is what enables chaining.
  • new Hero().setName("Arthur").setHealth(100): Each method call in the chain operates on and returns the same Hero instance, so the configuration accumulates across the chain.
  • Unset weapon field: Remains null since setWeapon() was never called in this example, which the toString() output reflects.
  • Alternative: You could use a separate Builder class instead of chaining setters directly on Hero, which better separates construction logic from the object itself for more complex classes.

Exercise 19: Helper Method Encapsulation

Problem Statement: Write a public method void processUsername(String username). Inside, use a hidden private helper method boolean isValid(String user) to run regex validation checks before printing a success or failure message.

Purpose: This exercise helps you practice encapsulating implementation details in private helper methods, keeping the public interface of a class clean and focused.

Given Input: processUsername("valid_user1"), processUsername("in valid!")

Expected Output:

valid_user1 is a valid username.
in valid! is not a valid username.
▼ Hint
  • Define a private helper method that returns true or false based on a regex pattern, such as only allowing letters, digits, and underscores.
  • Keep the regex validation logic entirely inside the private method, hidden from outside callers.
  • In the public method, call the private helper and branch on its result to print the appropriate message.
  • This separation keeps the public method focused on messaging while the private method focuses on validation.
▼ Solution & Explanation
public class Main {
    public static void processUsername(String username) {
        if (isValid(username)) {
            System.out.println(username + " is a valid username.");
        } else {
            System.out.println(username + " is not a valid username.");
        }
    }

    private static boolean isValid(String user) {
        return user.matches("[a-zA-Z0-9_]+");
    }

    public static void main(String[] args) {
        processUsername("valid_user1");
        processUsername("in valid!");
    }
}Code language: Java (java)

Explanation:

  • private boolean isValid(String user): Restricts this helper method to internal use within the class, hiding the validation details from external callers.
  • user.matches("[a-zA-Z0-9_]+"): Checks that the entire username consists only of letters, digits, and underscores using a regular expression.
  • if (isValid(username)): The public method delegates the validation decision to the helper, keeping its own logic focused on choosing which message to print.
  • Alternative: You could inline the regex check directly inside processUsername(), but extracting it into a private helper keeps the public method more readable and the validation logic reusable.

Exercise 20: In-Place Array Transformer

Problem Statement: Write a method void doubleElements(int[] nums) that doubles every integer inside the array in-place. This exercise tests your understanding of mutable reference pass-by-value mechanics.

Purpose: This exercise helps you practice modifying an array's contents directly through its reference, without needing to return a new array.

Given Input: int[] nums = {1, 2, 3, 4}; doubleElements(nums);

Expected Output:

Before: [1, 2, 3, 4]
After: [2, 4, 6, 8]
▼ Hint
  • Loop through the array using an index-based loop so each element can be reassigned.
  • Multiply each element by 2 and assign the result back to the same index.
  • Since arrays are passed by reference value, modifying elements inside the method changes the original array the caller sees.
  • Use Arrays.toString() to conveniently print the array's contents before and after the change.
▼ Solution & Explanation
import java.util.Arrays;

public class Main {
    public static void doubleElements(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] * 2;
        }
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4};
        System.out.println("Before: " + Arrays.toString(nums));
        doubleElements(nums);
        System.out.println("After: " + Arrays.toString(nums));
    }
}Code language: Java (java)

Explanation:

  • nums[i] = nums[i] * 2: Overwrites each element with double its original value, modifying the array's contents directly.
  • Return type void: The method doesn't need to return anything, since the changes are made directly to the array object the caller already holds a reference to.
  • Arrays.toString(nums): Provides a readable string representation of the array's contents for printing.
  • Alternative: You could return a brand new array with doubled values instead of modifying in place, which avoids side effects but requires the caller to reassign the result.

Exercise 21: Basic Functional Method

Problem Statement: Create a method int operate(int a, int b, java.util.function.BiFunction<Integer, Integer, Integer> operation) that executes an abstract mathematical operation on two integers. Call it from your main class using lambda expressions for addition, subtraction, and multiplication.

Purpose: This exercise helps you practice passing behavior as an argument using functional interfaces and lambda expressions, a core idea in functional-style Java programming.

Given Input: operate(5, 3, (x, y) -> x + y), operate(5, 3, (x, y) -> x - y), operate(5, 3, (x, y) -> x * y)

Expected Output:

Addition = 8
Subtraction = 2
Multiplication = 15
▼ Hint
  • Define the method to accept two integers and a BiFunction<Integer, Integer, Integer> representing the operation to perform.
  • Inside the method, call operation.apply(a, b) to execute whichever logic was passed in.
  • At the call site, pass different lambda expressions to perform different operations without writing separate methods for each.
  • This approach lets the caller decide the behavior while the method handles the structure.
▼ Solution & Explanation
import java.util.function.BiFunction;

public class Main {
    public static int operate(int a, int b, BiFunction<Integer, Integer, Integer> operation) {
        return operation.apply(a, b);
    }

    public static void main(String[] args) {
        System.out.println("Addition = " + operate(5, 3, (x, y) -> x + y));
        System.out.println("Subtraction = " + operate(5, 3, (x, y) -> x - y));
        System.out.println("Multiplication = " + operate(5, 3, (x, y) -> x * y));
    }
}Code language: Java (java)

Explanation:

  • BiFunction<Integer, Integer, Integer> operation: Represents a function that takes two Integer inputs and produces an Integer result, allowing behavior to be passed as an argument.
  • operation.apply(a, b): Executes whichever operation was supplied at the call site, without the method itself knowing the specific logic in advance.
  • (x, y) -> x + y: A lambda expression that implements the BiFunction interface inline, defining addition without a separate named method.
  • Alternative: You could define named methods for each operation and pass method references, like Integer::sum, instead of writing lambda expressions directly.

Filed Under: Java Exercises

Did you find this page helpful? Let others know about it. Sharing helps me continue to create free Python resources.

TweetF  sharein  shareP  Pin

About Vishal

I’m Vishal Hule, the Founder of PYnative.com. As a Python developer, I enjoy assisting students, developers, and learners. Follow me on Twitter.

Related Tutorial Topics:

Java Exercises

All Coding Exercises:

C Exercises
C++ Exercises
Python Exercises
Java Exercises
C# Exercises

Python Exercises and Quizzes

Free coding exercises and quizzes cover Python basics, data structure, data analytics, and more.

  • 15+ Topic-specific Exercises and Quizzes
  • Each Exercise contains 25+ questions
  • Each Quiz contains 25 MCQ
Exercises
Quizzes

Leave a Reply Cancel reply

your email address will NOT be published. all comments are moderated according to our comment policy.

Use <pre> tag for posting code. E.g. <pre> Your entire code </pre>

In: Java Exercises
TweetF  sharein  shareP  Pin

  Java Exercises

  • All Java Exercises
  • Java Exercise for Beginners
  • Java Loops Exercise
  • Java String Exercise
  • Java ArrayList Exercise
  • Java LinkedList Exercise
  • Java HashMap and TreeMap Exercise
  • Java HashSet and TreeSet Exercise
  • Java OOP Exercise
  • Java Methods Exercise
  • Java Enums Exercise
  • Java Exception Handling Exercise
  • Java File Handling Exercise
  • Java Date and Time Exercise
  • Java Data Structures Exercise
  • Java Sorting and Searching Exercise
  • Java Lambda and Functional Interfaces Exercise
  • Java Regex Exercise
  • Java Random Data Generation Exercise
  • Java Generics Exercise
  • Java Reflection Exercise
  • Java JDBC Exercise

All Coding Exercises

Python Exercises C Exercises C++ Exercises Java Exercises C# Exercises

About PYnative

PYnative.com is for Python lovers. Here, You can get Tutorials, Exercises, and Quizzes to practice and improve your Python skills.

Follow Us

To get New Python Tutorials, Exercises, and Quizzes

  • Twitter
  • Facebook
  • Sitemap

Explore Python

  • Learn Python
  • Python Basics
  • Python Databases
  • Python Exercises
  • Python Quizzes
  • Online Python Code Editor
  • Python Tricks

Coding Exercises

  • C Exercises
  • C++ Exercises
  • Python Exercises
  • Java Exercises
  • C# Exercises

Legal Stuff

  • About Us
  • Contact Us

We use cookies to improve your experience. While using PYnative, you agree to have read and accepted our:

  • Terms Of Use
  • Privacy Policy
  • Cookie Policy

Copyright © 2018–2026 pynative.com