Skip to main content

6.2 Variables and Methods

In the object-oriented paradigm, variables and methods are the core building blocks of a class. The type of variable changes depending on where it is declared, and methods are code blocks responsible for specific functionality.

1. Types of Variables by Declaration Location

Variables are divided into three types based on where they are declared: class variables, instance variables, and local variables.

1.1 Class Variable (Static Variable)

  • Declaration: Declared in the class body with the static keyword
  • Characteristic: All instances share a single storage space
  • Access: Can be accessed without an instance using ClassName.variableName
  • Lifetime: Created when the program starts → destroyed when the program ends
  • Use case: Values that all objects must share (e.g., interest rate, card size)

1.2 Instance Variable

  • Declaration: Declared in the class body without static
  • Characteristic: Created independently for each instance
  • Access: Accessed via referenceVariable.variableName
  • Lifetime: Created when instance is created → destroyed when instance is garbage-collected
  • Use case: Values unique to each object (e.g., card suit, card number)

1.3 Local Variable

  • Declaration: Declared inside a method, constructor, or block ({})
  • Characteristic: Only valid within that block, automatically destroyed when the block ends
  • Default value: No automatic initialization — must be initialized manually before use
  • Storage: Stack memory
public class Card {
// === Instance variables (different for each card) ===
String kind; // suit (Spades, Hearts, Diamonds, Clubs)
int number; // number (1~13)

// === Class variables (shared by all cards) ===
static int width = 100; // width (mm)
static int height = 250; // height (mm)

void printCard() {
// === Local variable (valid only inside this method) ===
String display = kind + number;
System.out.println("Card: " + display);
// display disappears when the method ends
}
}

public class CardTest {
public static void main(String[] args) {
// Class variable: accessible via class name without an instance
System.out.println("Card width: " + Card.width); // 100
System.out.println("Card height: " + Card.height); // 250

Card c1 = new Card();
Card c2 = new Card();

c1.kind = "Spades"; c1.number = 1;
c2.kind = "Hearts"; c2.number = 13;

// Instance variable: independent for each object
System.out.println(c1.kind + c1.number); // Spades1
System.out.println(c2.kind + c2.number); // Hearts13

// Changing a class variable is reflected across all instances
Card.width = 120;
System.out.println(c1.width); // 120 (shared!)
System.out.println(c2.width); // 120 (shared!)
}
}

2. What Is a Method?

A method is a block of code that performs a specific task, given a name so it can be reused at any time.

Why Use Methods?

  1. Reusability: Write code once and reuse it in multiple places
  2. Eliminate duplication: No need to write the same logic multiple times
  3. Organization: Keeps main clean and separates logic
  4. Maintenance: Updating one method updates the entire feature

3. Method Declaration Syntax

accessModifier  returnType  methodName(parameter1, parameter2, ...) {
// method body (code to execute)
return returnValue; // can be omitted if returnType is void
}
// void method: no return value
public void printGreeting(String name) {
System.out.println("Hello, " + name + "!");
// return; // void can omit return (or use empty return for early exit)
}

// Method with a return value
public int add(int a, int b) {
return a + b; // must return an int value
}

// Conditional return (every code path must have a return)
public String grade(int score) {
if (score >= 90) return "A";
else if (score >= 80) return "B";
else if (score >= 70) return "C";
else return "F";
}

4. Parameter vs Argument

The two terms are often used interchangeably, but they are technically different.

TermDescriptionExample
ParameterDefined in method declaration parenthesesint add(int a, int b)a, b are parameters
ArgumentActual values passed when calling the methodadd(3, 5)3, 5 are arguments
// Parameters: x, y
public double calculateDistance(double x, double y) {
return Math.sqrt(x * x + y * y);
}

// Arguments: 3.0, 4.0
double distance = calculateDistance(3.0, 4.0); // 5.0

5. Call by Value

Java always uses Call by Value only. However, the behavior differs between primitive and reference types.

Primitive Types: Copy of Value Is Passed

public class CallByValueExample {
static void doubleValue(int x) {
x = x * 2; // modifies the copy -> no effect on original
System.out.println("x inside method: " + x); // 20
}

public static void main(String[] args) {
int num = 10;
doubleValue(num);
System.out.println("num after method call: " + num); // 10 (unchanged!)
}
}

Reference Types: Copy of Address Is Passed

public class CallByReferenceExample {
static void changeBrand(Car car) {
car.brand = "Kia"; // modifies the field of the same object via address -> affects original!
}

static void replaceCar(Car car) {
car = new Car(); // local variable car now points to a new object -> no effect on original variable
car.brand = "BMW";
}

public static void main(String[] args) {
Car myCar = new Car();
myCar.brand = "Hyundai";

changeBrand(myCar);
System.out.println(myCar.brand); // Kia (field value was changed)

replaceCar(myCar);
System.out.println(myCar.brand); // Kia (the reference itself was not changed)
}
}
Reference Type Parameter Caution

When a reference type argument is passed to a method, modifying the object's fields inside the method does affect the original. However, reassigning the reference variable to a different object does not change the original reference.

6. Recursive Methods

When a method calls itself, it is called recursion.

Factorial

public class RecursionExample {

// n! = n x (n-1) x ... x 2 x 1
static long factorial(int n) {
if (n <= 1) return 1; // Base Case — absolutely required!
return n * factorial(n - 1); // recursive call
}

// Fibonacci sequence (naive recursion — slow performance, for concept learning)
static int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}

public static void main(String[] args) {
System.out.println("5! = " + factorial(5)); // 120
System.out.println("10! = " + factorial(10)); // 3628800

System.out.print("Fibonacci sequence: ");
for (int i = 0; i < 10; i++) {
System.out.print(fibonacci(i) + " ");
}
// 0 1 1 2 3 5 8 13 21 34
}
}
StackOverflowError

Every recursive call adds a stack frame. Without a Base Case, or with a wrong one, infinite recursion occurs and throws a StackOverflowError. Always set a clear termination condition.

Recursion vs Iteration

// Recursive style (intuitive but uses more memory)
static long factorialRecursive(int n) {
if (n <= 1) return 1;
return n * factorialRecursive(n - 1);
}

// Iterative style (better performance, suitable for large data)
static long factorialIterative(int n) {
long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}

7. static Methods vs Instance Methods

Aspectstatic MethodInstance Method
How to callClassName.methodName()referenceVariable.methodName()
Instance requiredNoYes
Member variable accessClass variables onlyAll member variables
this usageNot allowedAllowed
Use caseUtility, conversion, calculationMethods that use/modify object state
public class MathUtils {
// static method: can be called without an instance
public static int max(int a, int b) {
return a > b ? a : b;
}

public static double circleArea(double radius) {
return Math.PI * radius * radius;
}
}

public class Counter {
private int count = 0; // instance variable

// Instance method: uses the instance variable
public void increment() {
count++;
}

public int getCount() {
return count;
}
}

public class MethodTypeExample {
public static void main(String[] args) {
// static method: call directly without creating an instance
System.out.println(MathUtils.max(10, 20)); // 20
System.out.println(MathUtils.circleArea(5.0)); // 78.53...

// Instance method: must create an object first
Counter counter = new Counter();
counter.increment();
counter.increment();
counter.increment();
System.out.println(counter.getCount()); // 3
}
}

8. Practical Example: Calculator Class

Implementing a complete calculator class.

public class Calculator {
// Remembers the last calculation result (instance variable)
private double lastResult;
// Operation count (class variable — shared by all Calculator instances)
private static int operationCount = 0;

// Addition
public double add(double a, double b) {
lastResult = a + b;
operationCount++;
return lastResult;
}

// Subtraction
public double subtract(double a, double b) {
lastResult = a - b;
operationCount++;
return lastResult;
}

// Multiplication
public double multiply(double a, double b) {
lastResult = a * b;
operationCount++;
return lastResult;
}

// Division
public double divide(double a, double b) {
if (b == 0) {
System.out.println("Error: Cannot divide by zero!");
return Double.NaN;
}
lastResult = a / b;
operationCount++;
return lastResult;
}

// Modulo
public double modulo(double a, double b) {
if (b == 0) {
System.out.println("Error: Cannot divide by zero!");
return Double.NaN;
}
lastResult = a % b;
operationCount++;
return lastResult;
}

// Power
public double power(double base, double exponent) {
lastResult = Math.pow(base, exponent);
operationCount++;
return lastResult;
}

// Square root
public double sqrt(double a) {
if (a < 0) {
System.out.println("Error: Cannot compute square root of a negative number!");
return Double.NaN;
}
lastResult = Math.sqrt(a);
operationCount++;
return lastResult;
}

// Return last result
public double getLastResult() {
return lastResult;
}

// Return total operation count (static method)
public static int getOperationCount() {
return operationCount;
}

// Reset calculator
public void reset() {
lastResult = 0;
System.out.println("Calculator has been reset.");
}
}

public class CalculatorTest {
public static void main(String[] args) {
Calculator calc = new Calculator();

System.out.println("=== Calculator Test ===");
System.out.printf("10 + 5 = %.1f%n", calc.add(10, 5)); // 15.0
System.out.printf("10 - 3 = %.1f%n", calc.subtract(10, 3)); // 7.0
System.out.printf("4 x 6 = %.1f%n", calc.multiply(4, 6)); // 24.0
System.out.printf("15 / 4 = %.2f%n", calc.divide(15, 4)); // 3.75
System.out.printf("17 %% 5 = %.1f%n", calc.modulo(17, 5)); // 2.0
System.out.printf("2^10 = %.0f%n", calc.power(2, 10)); // 1024
System.out.printf("sqrt(144) = %.1f%n", calc.sqrt(144)); // 12.0

System.out.println("Last result: " + calc.getLastResult()); // 12.0
System.out.println("Total operations: " + Calculator.getOperationCount()); // 7

// Error case test
calc.divide(10, 0); // prints error message
}
}

Output:

=== Calculator Test ===
10 + 5 = 15.0
10 - 3 = 7.0
4 x 6 = 24.0
15 / 4 = 3.75
17 % 5 = 2.0
2^10 = 1024
sqrt(144) = 12.0
Last result: 12.0
Total operations: 7
Error: Cannot divide by zero!

9. Method Writing Guidelines

Good Method Writing Guide
  1. Single responsibility: One method does one thing only.
  2. Keep it short: Aim for 20 lines or fewer.
  3. Clear name: Start with a verb and make the function obvious (calculateTax(), getUserById()).
  4. Minimize parameters: If there are too many parameters, consider bundling them into an object.
  5. Handle exceptions: Validate input values for safe handling.

Summary

ConceptKey Content
Class variableDeclared with static, shared by all instances
Instance variableIndependent for each object, stored on heap
Local variableDeclared inside method/block, no automatic initialization
void methodNo return value
Call by ValuePrimitives pass a copy of value; reference types pass a copy of address
RecursionBase case is required; watch for StackOverflowError
static methodCan be called without an instance; can only access class variables
Instance methodCalled after creating an object; can access all member variables