Module 3: Methods & Arrays

Java for Testers · Module 3

Methods & Arrays: Reusable Code and Collections of Data

As programs grow, two problems appear. First, you find yourself copying the same lines again and again—methods solve that by letting you name a block of code and reuse it. Second, you need to handle many values at once, not just single variables—arrays solve that by storing a fixed-size collection under one name. Together these are the workhorses of real code. In a test framework, a method might be “log in with this username and password”, called from a hundred tests; an array might hold every expected product name you need to verify. This module builds both skills from scratch with testing-flavoured examples.

Learning objectives

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

  • Define methods with parameters and return types, and call them.
  • Use method overloading to give one name several forms.
  • Explain variable scope and the difference between static and instance methods (briefly).
  • Write methods that accept a variable number of arguments with varargs.
  • Declare, initialise, index, and iterate over one-dimensional arrays.
  • Work with two-dimensional arrays for grids and tables.
  • Use the Arrays utility class for common tasks like sorting and printing.

What is a method?

A method is a named block of code that performs a task. You have already used methods—System.out.println is one—and you have written one: main. Now you will write your own. Defining a method means you can run that code any time by calling its name, instead of retyping it. This is the DRY principle: Don’t Repeat Yourself. Here is the anatomy:

public static void greet() {
    System.out.println("Welcome to QALaunch!");
}
  • public — an access modifier (who can call it); more on this in Module 4.
  • static — means the method belongs to the class itself, so you can call it without creating an object (covered below).
  • void — the return type. void means “returns nothing”.
  • greet — the method name, followed by () for its parameters (here, none).

You call it simply by writing its name:

public static void main(String[] args) {
    greet();          // prints: Welcome to QALaunch!
    greet();          // call it as many times as you like
}

Parameters and return types

Methods become powerful when they accept parameters (inputs) and produce a return value (output). Parameters are declared in the parentheses; the return type replaces void and you hand back a value with the return keyword.

// Takes two int inputs, returns their sum as an int
public static int add(int a, int b) {
    return a + b;
}

// Takes a name, returns a greeting String
public static String buildGreeting(String name) {
    return "Hello, " + name + "!";
}

public static void main(String[] args) {
    int sum = add(3, 4);                 // sum is now 7
    String msg = buildGreeting("Priya"); // msg is "Hello, Priya!"
    System.out.println(sum);
    System.out.println(msg);
}

The values you pass in (3, 4, "Priya") are called arguments. They are matched to the parameters by position and must match the declared types. Once a method returns a value, you can store it, print it, or pass it straight into another method—exactly how a test helper like getPageTitle() would feed its result into an assertion.

A method is a kitchen recipe

Think of a method as a recipe card. The name is the dish (“make an omelette”). The parameters are the ingredients you must supply (eggs, salt). The body is the steps. The return value is the finished dish handed back to you. Once the recipe is written, anyone can follow it without knowing the details—they just supply ingredients and receive the result. That is abstraction: the caller of login(user, pass) does not care how login works internally, only that it does the job. Good test frameworks are built from dozens of small, well-named recipes like this, which is what makes them readable.

Method overloading

Java lets you define several methods with the same name as long as their parameter lists differ—different number of parameters, or different types. This is overloading. Java picks the right one based on the arguments you pass.

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

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

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

// Java chooses the matching version automatically:
add(2, 3);        // calls the first  -> 5
add(2, 3, 4);     // calls the second -> 9
add(2.5, 3.5);    // calls the third  -> 6.0

Overloading is why System.out.println happily accepts a String, an int, or a boolean—there are many overloaded versions behind that one name. Note: the return type alone cannot distinguish overloads; the parameters must differ.

Variable scope

Scope is the region of code where a variable exists and can be used. A variable declared inside a method (or a loop, or an if block) is a local variable—it lives only within the braces where it was declared and vanishes when that block ends.

public static void demo() {
    int x = 10;              // x exists only inside demo()
    if (x > 5) {
        int y = 20;          // y exists only inside this if-block
        System.out.println(x + y);
    }
    // System.out.println(y);  // ERROR: y is out of scope here
}
// System.out.println(x);      // ERROR: x is out of scope here too

Keeping variables in the smallest scope that works is good practice: it prevents accidental reuse and makes code easier to reason about. If two methods each declare their own x, they are completely separate—no interference.

Static vs instance methods (a first look)

You have seen static on every method so far. Here is the short version, to be expanded in Module 4. A static method belongs to the class and is called on the class name, with no object needed: Math.max(3, 7). An instance method belongs to an object (an individual thing created from a class) and is called on that object: myString.toUpperCase()—you need an actual String object to call it on.

// static: called on the class, no object required
int bigger = Math.max(3, 7);          // 7

// instance: called on an object
String name = "priya";
String upper = name.toUpperCase();     // "PRIYA" - needs the object 'name'

Rule of thumb for now: utility helpers that just take inputs and return a result are often static; behaviour tied to a specific object’s data is an instance method. Module 4 makes this concrete.

Varargs: a variable number of arguments

Sometimes you want a method to accept any number of arguments. Varargs (variable arguments), written with ..., let you do that. Inside the method, the arguments arrive as an array.

public static int sumAll(int... numbers) {   // accepts zero or more ints
    int total = 0;
    for (int n : numbers) {                  // numbers behaves like an array
        total += n;
    }
    return total;
}

sumAll();              // 0
sumAll(5);             // 5
sumAll(1, 2, 3, 4);    // 10

A varargs parameter must be the last parameter in the list. You will see this pattern in testing utilities—for example a method that takes any number of expected values to check against.

Arrays: storing many values

An array is a fixed-size, ordered collection of values of the same type, stored under one name. Instead of test1, test2, test3, you keep one array scores. Each slot has a numbered position called an index, and—crucially—indexes start at 0. An array of 5 elements has indexes 0, 1, 2, 3, 4.

// Declare and initialise with known values:
int[] scores = {90, 85, 72, 95, 60};

// Access by index:
System.out.println(scores[0]);   // 90  (the first element)
System.out.println(scores[4]);   // 60  (the last element)

// Change an element:
scores[2] = 78;                  // the third slot is now 78

// How many elements? Use .length (a field, not a method)
System.out.println(scores.length);   // 5

You can also create an empty array of a given size and fill it later. Numeric arrays default to 0, booleans to false, and object arrays (like String[]) to null.

String[] browsers = new String[3];   // three empty slots (all null)
browsers[0] = "chrome";
browsers[1] = "firefox";
browsers[2] = "edge";

The off-by-one and out-of-bounds traps

  • Indexes start at 0. The last valid index is length - 1, not length. For a 5-element array the last index is 4.
  • ArrayIndexOutOfBoundsException. Accessing scores[5] on a 5-element array throws this at runtime—a very common crash. It usually means a loop condition used <= instead of <.
  • Arrays are fixed size. Once created, an array cannot grow or shrink. If you need a resizable collection, you will use ArrayList (a later module). Beginners often reach for arrays when a list is the right tool.
  • .length is a field for arrays (no parentheses), but .length() is a method for Strings. Mixing them up is a frequent slip.

Iterating over arrays

To process every element, you loop. There are two idiomatic ways. The classic for loop uses the index—use it when you need the position number:

int[] scores = {90, 85, 72, 95, 60};
for (int i = 0; i < scores.length; i++) {
    System.out.println("Score at index " + i + " is " + scores[i]);
}

The enhanced for loop (also called for-each) reads each element directly and is cleaner when you do not need the index:

for (int score : scores) {          // read as "for each score in scores"
    System.out.println(score);
}

In Selenium you will write for (WebElement row : rows) constantly—this is that exact pattern, looping over every element the browser found.

Common array tasks

A few patterns come up again and again: summing, finding a maximum, and searching.

int[] scores = {90, 85, 72, 95, 60};

int total = 0;
int max = scores[0];
boolean found95 = false;

for (int score : scores) {
    total += score;                 // running sum
    if (score > max) max = score;   // track the largest
    if (score == 95) found95 = true; // search
}

double average = (double) total / scores.length;   // cast to avoid integer division
System.out.println("Total: " + total);     // 402
System.out.println("Max: " + max);         // 95
System.out.println("Average: " + average); // 80.4
System.out.println("Has a 95: " + found95); // true

Note the cast to double when computing the average—without it, 402 / 5 would use integer division and give 80, losing the .4.

Two-dimensional arrays

A 2D array is an array of arrays—a grid with rows and columns. It is perfect for tables, which you meet constantly in web testing. You index with two numbers: grid[row][column].

// A 3-row, 2-column grid of test data (username, password pairs)
String[][] credentials = {
    {"alice", "pass1"},
    {"bob",   "pass2"},
    {"carol", "pass3"}
};

System.out.println(credentials[0][0]);   // "alice"
System.out.println(credentials[2][1]);   // "pass3"

// Iterate over every cell with nested loops:
for (int row = 0; row < credentials.length; row++) {
    for (int col = 0; col < credentials[row].length; col++) {
        System.out.print(credentials[row][col] + " ");
    }
    System.out.println();   // new line after each row
}

That credentials grid is a sneak peek at data-driven testing: one test body, many rows of input—exactly how you would run the same login test against several accounts.

The Arrays utility class

Java ships a helper class, java.util.Arrays, full of static methods for common array work. Import it with import java.util.Arrays; and you get sorting, searching, printing, and comparing for free.

import java.util.Arrays;

int[] scores = {90, 85, 72, 95, 60};

// Print an array readably (plain println on an array prints gibberish):
System.out.println(Arrays.toString(scores));   // [90, 85, 72, 95, 60]

// Sort in place (ascending):
Arrays.sort(scores);
System.out.println(Arrays.toString(scores));   // [60, 72, 85, 90, 95]

// Fill every slot with a value:
int[] zeros = new int[3];
Arrays.fill(zeros, 7);
System.out.println(Arrays.toString(zeros));    // [7, 7, 7]

// Compare two arrays for equal contents:
System.out.println(Arrays.equals(new int[]{1,2}, new int[]{1,2}));  // true

Tutor tip: never println an array directly

If you write System.out.println(scores) on an array, you get something like [I@1b6d3586—not the contents, but a cryptic internal reference. This baffles beginners every time. The fix is Arrays.toString(scores) for a 1D array, or Arrays.deepToString(grid) for a 2D array. Burn this into memory now: it will save you from staring at [Ljava.lang.String;@... wondering what went wrong. When you later debug a Selenium test that collected a list of elements, the same habit—printing contents, not references—helps you see what the browser actually found.

Hands-on exercise

Create a class MethodsAndArrays and complete each task:

  1. Write a static method isEven(int n) that returns a boolean, and call it from a loop over 1–10, printing each number and whether it is even.
  2. Write an overloaded method describe: one version takes an int, another takes a String. Print a sentence appropriate to each type, then call both.
  3. Write a varargs method average(double... values) that returns the mean, and test it with different numbers of arguments.
  4. Create an int[] of at least six numbers. Write a method findMax(int[] numbers) that returns the largest, and a method that returns the sum. Print both results.
  5. Build a String[][] of three username/password pairs. Loop over it with nested loops and print each pair on its own line.
  6. Use Arrays.sort and Arrays.toString to sort and print your number array. Stretch: compute and print the average as a double, remembering the cast.

Knowledge check

  1. What are the parameters and the return type of a method, and what does void mean?
  2. What makes two methods valid overloads of each other? Can return type alone distinguish them?
  3. What is variable scope, and where does a variable declared inside a loop live?
  4. In one line, what is the difference between a static method and an instance method?
  5. What are varargs, and where must a varargs parameter appear in the parameter list?
  6. What is the index of the first element of an array, and of the last element in an array of length 5?
  7. Why does System.out.println(myArray) print gibberish, and what should you use instead?
  8. When would you choose a 2D array, and how do you access the element at row 1, column 0?
Show answers
  1. Parameters are the inputs declared in parentheses; the return type is the kind of value handed back with return. void means the method returns nothing.
  2. Their parameter lists must differ in number or type. No—return type alone cannot distinguish overloads.
  3. Scope is the region where a variable exists; a variable declared inside a loop (or block) exists only within that block’s braces.
  4. A static method is called on the class with no object; an instance method is called on a specific object.
  5. Varargs (type... name) let a method accept any number of arguments, received as an array; the varargs parameter must be last.
  6. The first element is index 0; the last element of a length-5 array is index 4 (i.e. length - 1).
  7. It prints an internal reference, not the contents; use Arrays.toString(myArray) (or Arrays.deepToString for 2D).
  8. Choose a 2D array for grids/tables of data; access row 1, column 0 with grid[1][0].

Key takeaways

  • Methods name and reuse a block of code; they take parameters and may return a value—the core of DRY, readable code.
  • Overloading gives one name multiple parameter forms; the return type alone cannot distinguish them.
  • Variables live only within their scope; static methods belong to the class, instance methods to objects.
  • Varargs accept any number of arguments (as an array) and must be the last parameter.
  • Arrays are fixed-size, same-type collections indexed from 0; iterate with a classic or enhanced for; use 2D arrays for tables.
  • The Arrays class provides sort, fill, equals, and toString—never println an array directly.

Go deeper

A handful of deeper points about methods and arrays separate confident coders from those who get mysterious bugs.

Java is always pass-by-value—but a reference is a value too. When you pass an int to a method, it receives a copy; changing it inside does not touch the original. When you pass an array, the copy is a copy of the reference, so both names point at the same array—changes to its elements are visible to the caller. This surprises many beginners:

static void bump(int[] data) {
    data[0] = 99;                 // affects the caller's array
}

int[] nums = {1, 2, 3};
bump(nums);
System.out.println(nums[0]);      // 99, not 1
  • Jagged arrays. A 2D array’s rows need not be the same length: write int[][] rows = new int[3][]; and give each row its own size later. Always loop with rows[i].length per row rather than assuming a fixed width.
  • More Arrays helpers. Beyond sort and toString, Arrays.copyOf grows or trims an array into a new one, and Arrays.binarySearch finds a value quickly—but only on an array that is already sorted.
  • Recursion. A method may call itself. Every recursive method needs a base case that stops the calls; miss it and you get a StackOverflowError—the method equivalent of an infinite loop.

Save to list

Scroll to Top