Java-Key-Concept-banner
Automated Testing Key Java Concept

Key Java Concepts to Enhance Your Automation Testing Skills

Java is one of the most widely used programming languages, known for its versatility, platform independence, and robustness, it remains a favorite among all the developers globally.  Whether you are new to programming or wishing to enhance your Java skills, then, it is imperative to understand the basic principles of Java in order to write code that is not only effective and efficient but also easy to maintain.

This blog post is intended to be your guide to learning Java. We will start with the basics of the language, and discuss data types, variables, methods, and constructors. We will then progress to the control flow constructs such as if-else statements, loops and switch cases to enable you develop the logic and the ability to repeat certain blocks of code. But we are not done yet! We will also look at some of the more advanced topics like OOP, collections, and exceptions, so you will have a full understanding of the Java development process.

By the time you finish this guide, you’ll possess a solid Java foundation and be prepared to confidently confront real-world programming obstacles. Let’s get started!

Understanding Datatypes and Variables

In Java, Data types are very important because they determine what kind of information can be stored and used. Primitive data types, such as integers, floating-point numbers, characters, and booleans, provide the foundation for representing simple values.
On the other hand, non-primitive data types, like arrays, classes, and interfaces, enable the creation of more complex structures and allow for the organisation and manipulation of larger sets of data.

Data typesCategorySizeRange
bytePrimitive8-bit-128 to 127
shortPrimitive16-bit-32,768 to 32,767
intPrimitive32-bit-2^31 to 2^31-1
longPrimitive64-bit-2^63 to 2^63-1
floatPrimitive32-bit±3.4e−038 to ±3.4e+038
doublePrimitive64-bit±1.7e−308 to ±1.7e+308
charPrimitive16-bit0 to 65,535
booleanPrimitive1-bitTrue or False
StringNon-PrimitiveN/AN/A
ArrayNon-PrimitiveN/AN/A
ClassNon-PrimitiveN/AN/A

A Variable is a container that holds data that can be changed during program execution.You will often define variables for WebDriver instances, element locators, or test data. 

Let’s understand this with the Fibonacci series. It is a sequence of numbers where each term is the sum of the two preceding ones, beginning with 0 and 1. The pattern follows: 0, 1, 1, 2, 3, 5, 8, 13, 21, …

import java.util.ArrayList;

public class FibonacciSeries {

    public static void main(String[] args) {
        int n = 10; // You can change this value to generate more or fewer terms

        // Variables to store the first two terms
        int firstTerm = 0, secondTerm = 1; // Primitive data types

        // ArrayList to store the Fibonacci series (non-primitive data type)
        ArrayList<Integer> fibonacciList = new ArrayList<>();

        System.out.println("Fibonacci Series of " + n + " terms:");
        
        for (int i = 0; i < n; ++i) { // Using a for loop
            fibonacciList.add(firstTerm); // Add current term to the list
            System.out.print(firstTerm + " "); // Print current term
            
            // Compute the next term
            int nextTerm = firstTerm + secondTerm;
            // Update terms for next iteration
            firstTerm = secondTerm;
            secondTerm = nextTerm;
        }
        System.out.println("\nFibonacci List: " + fibonacciList);
    }
}

The provided Java program generates the Fibonacci series for a predefined number of terms, the variable n of int data type is set to 10 in this example.  It utilizes primitive data types, specifically int, to store the first two terms of the series (0 and 1), while an ArrayList<Integer> serves as a non-primitive data type to dynamically store the generated Fibonacci numbers.

An Overview of Methods and Constructors

Methods are essentially blocks of code that are designed to perform a specific task. A method is grouping of instructions that execute some operation and return the result. It allows us to reuse the code without writing it again. 

Let’s consider the example of factorials of the number.

class Factorial {
    // Recursive method to calculate factorial
    int factorial(int n) {
        if (n == 0) return 1;
        return n * factorial(n - 1);
    }

    public static void main(String[] args) {
        Factorial fact = new Factorial();
        System.out.println("Factorial of 5: " + fact.factorial(5)); // Output: 120
    }
}

Here, a factorial method is created which returns the factorial of the given number. 

This method is called from the main method of the class. Factorial method accepts integer value n and finds the factorial of n. 

Constructor used to initialise the objects. Every class has at least one constructor, and if none is defined. Java compiler automatically provides a default constructor. Constructors share the same name as the class and do not have a return type. 

Let’s consider an example of palindrome numbers. A palindrome number is a number that remains the same when reversed (e.g., 121, 1331).

class PalindromeChecker {
    int number;

    // Constructor to initialize number
    PalindromeChecker(int num) {
        this.number = num;
    }

    // Method to check if the number is a palindrome
    boolean isPalindrome() {
        int original = number, reversed = 0, remainder;
        
        while (original != 0) {
            remainder = original % 10;
            reversed = reversed * 10 + remainder;
            original /= 10;
        }
        return number == reversed;
    }

    public static void main(String[] args) {
        PalindromeChecker obj1 = new PalindromeChecker(121);
        PalindromeChecker obj2 = new PalindromeChecker(123);

        System.out.println("121 is palindrome? " + obj1.isPalindrome()); // true
        System.out.println("123 is palindrome? " + obj2.isPalindrome()); // false
    }
}

In this example, isPalindrome method is created which checks if a given number is palindrome or not. Here, obj1 and obj2 are created under the main method and  the constructor takes num parameter as an integer and assigns it to the number instance.

Exploring Control Statements in Java

Control statements allow our program to choose different paths of execution based on certain conditions. The primary control flow statements in Java are if , else , switch , while , do while , and for.

How to Use If and If-Else Statements Effectively

You can perform specific actions depending on whether a certain condition is true or false. It checks a condition and runs a set of instructions if the condition is true; otherwise, it skips those instructions. If you have two sets of instructions and need to run one of them, you should use an if-else statement.

Let’s understand this by checking if the given number is odd or even

public class EvenOddCheck {

    public static void main(String[] args) {
        int number = 7; 

        // Check if the number is even or odd using a single if-else statement
        if (number % 2 == 0) {
            System.out.println("The number " + number + " is even.");
        } else {
            System.out.println("The number " + number + " is odd.");
        }
    }
}

The provided Java program checks whether a number is even or odd using an if-else statement. The condition within the if statement evaluates whether the number is divisible by 2, which indicates that it is even; if this condition is true, it prints a message stating that the number is even. Conversely, if the condition is false (meaning the number is not divisible by 2), the program executes the else block, which outputs that the number is odd. 

Working with Nested If-Else Statements 

The nested if else statement means using if else statement inside if body or else body. It allows for multi-level decision-making based on multiple conditions.
Let’s consider the AgeCategory program as an example. If the age is greater than 18, the user is classified as an adult. If the age exceeds 65, the user is categorized as a senior citizen; otherwise, they are considered a Teenager and if age is less than 13 then child.

public class AgeCategory {
    public static void main(String[] args) {
        int age = 20; 

        if (age >= 18) {
            System.out.println("Adult");

            if (age >= 65) {
                System.out.println("Senior Citizen");
            } else {
                System.out.println("Not a Senior Citizen");
            }

        } else {
            System.out.println("Minor");
            
            if (age < 13) {
                System.out.println("Child");
            } else {
                System.out.println("Teenager");
            }
        }
    }
}

This Java program classifies a person’s age into different categories using nested if statements. The age variable is initially set to 20. It first checks if the person is an adult (age >= 18). If true, it further checks if the person is a senior citizen (age >= 65) and prints “Senior Citizen” or “Not a Senior Citizen” accordingly. If the age is less than 18, the program classifies the person as a minor, then checks whether the person is a child (age < 13) or a teenager (age between 13 and 17).

Mastering the If-Else-If Ladder Statement for Multiple Conditions

The if else ladder statement is a set of conditions which is based on “Nested if condition”. The if-else ladder condition performs the expressions from top to down. Once the condition is true, the statement for the same is executed. 

To dive in detail, let’s understand theResponse code program.

int responseCode = driver.getResponseCode(); // Assume this method retrieves the HTTP response code after login

if (responseCode == 200) {
    System.out.println("Login successful.");
} else if (responseCode == 401) {
    System.out.println("Unauthorized access: Invalid credentials.");
} else if (responseCode == 403) {
    System.out.println("Forbidden: You do not have permission to access this resource.");
} else if (responseCode == 500) {
    System.out.println("Server error: Please try again later.");
} else {
    System.out.println("Unexpected response code: " + responseCode);
}

The if-else ladder evaluates the response code to determine the outcome of the login attempt. If the response code is 200, it indicates that the login was successful, and a corresponding message is printed. If the response code is 401, it signifies unauthorised access due to invalid credentials, prompting a specific error message. Similarly, a 403 response indicates that the user does not have permission to access the resource, while a 500 response suggests a server error. If none of these conditions are met, it defaults to printing an unexpected response code message. This structured approach allows for clear handling of various outcomes from a login operation.

Simplifying Code with the Switch Statement

The switch statement lets you pick which set of instructions to run, depending on the value of a specific variable. It’s similar to choosing an item from a restaurant menu.

Let’s consider the browser selection program.

String browser = "Chrome";

switch (browser) {
    case "Chrome":
        System.out.println("Launching Chrome browser");
        WebDriver driver = new ChromeDriver();
        break;
    case "Firefox":
        System.out.println("Launching Firefox browser");
        WebDriver driver = new FirefoxDriver();
        break;
    case "Edge":
        System.out.println("Launching Edge browser");
        WebDriver driver = new EdgeDriver();
        break;
    default:
        System.out.println("Unsupported browser");
}

Here, the variable browser is set to “Chrome”. The switch statement evaluates this variable and matches it against several cases. If the value is “Chrome”, it prints “Launching Chrome browser” and creates a new instance of ChromeDriver. If the value were “Firefox” or “Edge”, it would print the respective messages and instantiate FirefoxDriver or EdgeDriver. If none of the specified cases match, the default case executes, printing “Unsupported browser.”

Diving into Loop Statements in Java

How to Iterate Using the For Loop

It is a feature used to execute a particular part of the program repeatedly if a given condition evaluates to be true.  A loop statement allows us to execute a statement or group of statements multiple times.

Let’s take an example of Prime numbers. A prime number is a natural number greater than 1 that is only divisible by 1 and itself. In other words, it has exactly two factors: 1 and the number itself.

Example of prime numbers : 2, 3, 5, 7, 11, 13, 17, 19, 23, …

import java.util.Scanner;

public class PrimeNumberCheck {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter a number: ");
        int number = scanner.nextInt();
        boolean isPrime = true;

        if (number <= 1) {
            isPrime = false; // 0 and 1 are not prime numbers
        } else {
            for (int i = 2; i <= number / 2; i++) {
                if (number % i == 0) {
                    isPrime = false; // Number is divisible by another number
                    break;
                }
            }
        }

        if (isPrime) {
            System.out.println(number + " is a prime number.");
        } else {
            System.out.println(number + " is not a prime number.");
        }

        scanner.close();
    }
}

A Scanner object is created to read an integer input from the user, which is stored in the number variable. A boolean variable isPrime is initialized to true to track whether the number is prime. If the number is less or equal to 1, setting isPrime to false since numbers 0 and 1 are not prime . If the number is greater than 1, a for loop iterates from 2 to number/2, If a divisor is found, isPrime is set to false. Finally, based on the isPrime flag, the program prints whether the number is prime or not. 

Understanding the While Loop for Conditional Iteration

The Java while loop runs a group of commands as long as a condition is true.

Let’s consider an example of ATM pin validation, if a user enters incorrect otp for more than 3 times, account should be blocked else access should be granted.

import java.util.Scanner;

public class ATMPinValidation {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int correctPin = 1234; // Set the correct PIN
        int attempts = 0;
        int maxAttempts = 3;

        while (attempts < maxAttempts) {
            System.out.print("Enter your PIN: ");
            int enteredPin = scanner.nextInt();
            
            if (enteredPin == correctPin) {
                System.out.println("Access Granted. Welcome!");
                break;
            } else {
                System.out.println("Incorrect PIN. Try again.");
                attempts++;
            }
        }

        if (attempts == maxAttempts) {
            System.out.println("Too many failed attempts. Account locked.");
        }

        scanner.close();
    }
}

This example uses a while loop to continuously check for the maximum attempts of the pin validation. The program repeatedly prompts the user for input, checking if the entered PIN matches the correct one. If the user enters the correct PIN, access is granted, and the loop exits using the break statement.Once the user reaches the maximum attempts (3 failed attempts), the account is locked, and a warning message is displayed.

Using the Do-While Loop for Post-Test Execution

The do-while loop runs a group of commands at least once, even if the condition isn’t true. After the first run, it keeps repeating the commands until the condition becomes true  such as displaying an ATM menu before taking user input.

import java.util.Scanner;

public class ATMMenu {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int choice;

        do {
            // Display menu options
            System.out.println("\nATM Menu:");
            System.out.println("1. Check Balance");
            System.out.println("2. Deposit Money");
            System.out.println("3. Withdraw Money");
            System.out.println("4. Exit");
            System.out.print("Enter your choice: ");
            choice = scanner.nextInt();

            // Process user choice
            switch (choice) {
                case 1:
                    System.out.println("Your balance is $1000.");
                    break;
                case 2:
                    System.out.println("Enter amount to deposit:");
                    int deposit = scanner.nextInt();
                    System.out.println("You deposited $" + deposit);
                    break;
                case 3:
                    System.out.println("Enter amount to withdraw:");
                    int withdraw = scanner.nextInt();
                    System.out.println("You withdrew $" + withdraw);
                    break;
                case 4:
                    System.out.println("Exiting. Thank you!");
                    break;
                default:
                    System.out.println("Invalid choice. Please try again.");
            }
        } while (choice != 4); // Loop continues until the user chooses to exit

        scanner.close();
    }
}

In the given example, the do-while loop ensures that the menu is displayed at least once before processing user input. If the user enters 1, the bank balance is displayed. If they enter 2, they are prompted to enter an amount to deposit. If the user selects 3, they are asked to enter the withdrawal amount. Finally, if the user enters 4, the program terminates and exits.

Using Jump Statements in Java

Jump statements transfer the execution control to the other part of the program. There are two types of jump statements in Java, i.e., break and continue.

Breaking Out of Loops with the Java Break Statement

It is used to break the current flow of the program and transfer the control to the next statement outside a loop or switch statement. However, it breaks only the inner loop in the case of the nested loop.

Skipping Iterations with the Continue Statement

It skips the specific part of the loop and jumps to the next iteration of the loop immediately.

public class JumpStatementExample {
    public static void main(String[] args) {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        WebDriver driver = new ChromeDriver();
        
        driver.get("http://example.com");
        List<WebElement> elements = driver.findElements(By.className("item"));

        for (WebElement element : elements) {
            if (element.getText().equals("Skip This")) {
                continue; // Skip this iteration if the text matches
            }
            if (element.getText().equals("Desired Element")) {
                System.out.println("Found the desired element!");
                break; // Exit the loop once the desired element is found
            }
        }
        
        driver.quit();
    }
}

In this example, we use a for loop to iterate through a list of web elements identified by their class name. The continue statement is employed to skip any iterations where the element’s text matches “Skip This,” allowing the loop to proceed to the next element. Conversely, when the “Desired Element” is found, the break statement terminates the loop immediately.

Explaining Data Objects and Their Role in Java

Data objects are instances of classes that encapsulate data and behaviours related to that data, that is useful in organising code, reusability and encapsulation. 

It is implemented as POJO (Plain old java objects). It contains private fields and getter setters as public to modify those data.
To dive in more into getters-setters using Lombok you can refer our existing blog Lombok Unleashed: Elevating Java Efficiency with Getters, Setters, Constructors, Builders, and More

// Data Object class
public class LoginData {
    private String username;
    private String password;

    // Constructor
    public LoginData(String username, String password) {
        this.username = username;
        this.password = password;
    }

    // Getters
    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

// Selenium Test Class
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;

public class LoginTest {
    public static void main(String[] args) {
        // Instantiate WebDriver
        WebDriver driver = new ChromeDriver();
        driver.get("https://example.com/login");

        // Create a data object
        LoginData loginData = new LoginData("testuser", "password123");

        // Use the data object in the test
        WebElement usernameField = driver.findElement(By.id("username"));
        WebElement passwordField = driver.findElement(By.id("password"));
        WebElement loginButton = driver.findElement(By.id("loginButton"));

        usernameField.sendKeys(loginData.getUsername());
        passwordField.sendKeys(loginData.getPassword());
        loginButton.click();

        driver.quit();
    }
}

In the example, a data object (LoginData) is used to encapsulate the username and password required for the login process. The LoginData class contains private fields for username and password, with a constructor and public getter methods to provide controlled access. In the LoginTest class, the Selenium WebDriver navigates to a login page, locates the input fields and login button using their element IDs, and fills in the credentials from the LoginData object. 

An Introduction to Collections

Collections are containers that group multiple items into a single unit, making it easier to manage and manipulate data. In Java, the Collection framework provides a comprehensive architecture for storing and handling groups of objects. It includes interfaces like List, Set, and Map, as well as concrete classes like ArrayList, HashSet, and HashMap. These collections offer versatile methods for performing operations such as searching, sorting, insertion, manipulation, and deletion efficiently.

One of the most powerful tools for working with collections in Java is the Streams API. Streams enable functional-style operations, allowing developers to process data declaratively. They support operations like filtering, mapping, and reducing, which help write clean, concise, and maintainable code. Streams are particularly useful for bulk operations on collections, making data processing more efficient.

Example: Using Streams in Java

Let’s look at a simple example of using Streams to filter and transform a list of numbers:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        // Create a list of integers from 1 to 10
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // Process the list using a stream
        List<Integer> doubledEvenNumbers = numbers.stream() // Convert list to stream
                                                  .filter(n -> n % 2 == 0) // Keep only even numbers
                                                  .map(n -> n * 2) // Double each even number
                                                  .collect(Collectors.toList()); // Collect results into a list

        // Print the final list
        System.out.println("Doubled Even Numbers: " + doubledEvenNumbers);
    }
}

Output:

Doubled Even Numbers: [4, 8, 12, 16, 20]

In this example, we:

  • Used filter() to select only even numbers.
  • Applied map() to double each of the filtered numbers.
  • Collected the transformed numbers into a new list using collect().

This demonstrates how Streams make data processing more readable and maintainable.

To explore more about how Java Streams can revolutionize your approach to data processing, check out our detailed guide here: Java Streams: Unleashing the Power of Functional Data Processing.

Handling Exceptions in Java Applications

Well-handled exceptions can stop tests from failing and give useful information when mistakes happen.

An exception is something that interrupts the usual process of a program running.

There are two types of exception

  1. Checked exceptions must be either caught or declared in the method signature (e.g., IOException).
  2. Un-checked exceptions are the  runtime exceptions that do not need to be explicitly handled (e.g., NullPointerException, ArrayIndexOutOfBoundsException).

There are several keywords for exception handling:

  • Try :This is a section of code that might cause an error.
  • catch: A block that handles the exception thrown by the try block.
  • finally: This section runs after the try and catch parts, no matter if an error happened or not.
  • throw: Used to explicitly throw an exception.
  • throws: Declares that a method may throw specific exceptions.

Managing Errors Using Try-Catch Blocks

Imagine a scenario where a user attempts to withdraw money from an ATM. If the requested amount exceeds the available balance, an exception is triggered. However, if the withdrawal amount is within the limit, the transaction is successfully processed.

import java.util.Scanner;

public class ATMWithdrawal {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double accountBalance = 5000.00; // Available balance

        System.out.print("Enter withdrawal amount: ");
        double withdrawalAmount = scanner.nextDouble();

        try {
            if (withdrawalAmount > accountBalance) {
                throw new Exception("Insufficient Balance! Transaction failed.");
            }
            accountBalance -= withdrawalAmount; // Deduct amount
            System.out.println("Withdrawal successful! Remaining balance: " + accountBalance);
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage()); // Handle insufficient balance
        } finally {
            System.out.println("Thank you for using the ATM.");
            scanner.close();
        }
    }
}

In this code, the account balance is 5000.00 and prompts the user to enter the amount they wish to withdraw. Inside the try block, the program checks if the requested amount exceeds the available balance. If it does, an exception is thrown, displaying an error message in the catch block to indicate an insufficient balance. Otherwise, the transaction proceeds,  The finally block ensures that a message thanking the user is always displayed and the scanner is closed, regardless of the transaction outcome.

A Comprehensive Guide to Object-Oriented Programming (OOPs)

OOPS stands for Object-Oriented Programming System. It is a programming paradigm based on the concept of “objects” that contain data and methods to manipulate that data. 

For more details, please visit Object-Oriented Programming in Less than 10 Minutes💡 – JigNect Technologies Pvt Ltd Object-Oriented Programming in Less than 10 Minutes💡

Conclusion 

In this blog, we’ve covered a comprehensive range of foundational concepts in Java, from understanding datatypes and variables to diving deep into control statements, loops, and jump statements. We also explored key features like methods, constructors, data objects, and collections, which are essential building blocks of any Java program. Additionally, we examined the importance of exception handling to ensure robust and error-free applications, and concluded with an introduction to Object-Oriented Programming, the core paradigm of Java.

This will not only bolster your skills for automation but also prepare you for advanced testing scenarios. As automation continues to evolve, having a strong foundation in Java will empower you to tackle complex challenges with confidence.

By focusing on these key areas, you can significantly enhance your effectiveness as a Selenium tester and contribute more meaningfully to your team’s automation efforts. 

Witness how our meticulous approach and cutting-edge solutions elevated quality and performance to new heights. Begin your journey into the world of software testing excellence. To know more refer to Tools & Technologies & QA Services.

If you would like to learn more about the awesome services we provide,  be sure to reach out.

Happy Testing 🙂