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