Static Variable → Shared Resource

บทนำ: ปัญหาของ Instance Variables

จนถึงตอนนี้ attributes ที่เราใช้ เป็นของแต่ละ object

java// ❌ ปัญหา: แต่ละ object มีค่าแยกกัน
public class Student {
    private String name;      // instance variable
    private double gpa;       // instance variable
}

// สร้าง 3 objects
Student john = new Student("John", 3.75);
Student jane = new Student("Jane", 3.50);
Student bob = new Student("Bob", 3.90);

// แต่ละ object มีข้อมูลของตัวเอง
System.out.println(john.name);  // "John"
System.out.println(jane.name);  // "Jane"
System.out.println(bob.name);   // "Bob"

ปัญหา: บางครั้ง ต้องการข้อมูลที่แชร์กัน ทั้ง class

text❌ ทำไมถึงต้องการ shared data?

มหาวิทยาลัย: "Bangkok University"
- john.university = "Bangkok University"
- jane.university = "Bangkok University"
- bob.university = "Bangkok University"

❓ ทำไมต้องเก็บแบบเดียวกัน 3 ครั้ง?

Static Variable คืออะไร?

Static Variable = ข้อมูลที่เป็นของ Class ทั้งหมด (ไม่ใช่ของแต่ละ object)

javapublic class Student {
    // Static variable - เป็นของ Class (shared)
    public static String universityName = "Bangkok University";
    
    // Instance variable - เป็นของแต่ละ object
    private String name;
}

// ใช้งาน
Student.universityName = "Bangkok University";  // ← เข้าถึงจาก Class
System.out.println(Student.universityName);    // "Bangkok University"

Instance vs Static: ความต่างกัน

text┌────────────────┬───────────────────┬──────────────────┐
│                │ Instance Variable │ Static Variable  │
├────────────────┼───────────────────┼──────────────────┤
│ เป็นของ       │ แต่ละ object      │ Class ทั้งหมด    │
│ จำนวน         │ หนึ่งต่อ object   │ หนึ่งต่อ class   │
│ ค่า           │ อาจต่างกัน        │ เดียวกัน         │
│ หน่วยความจำ  │ ใน heap           │ ใน static area   │
│ เข้าถึง       │ object.var        │ ClassName.var    │
│ ช่วงชีวิต     │ object สร้าง→ลบ   │ program start→end│
└────────────────┴───────────────────┴──────────────────┘

ตัวอย่าง Visual: Instance vs Static

text┌─────────────────────────────────────────────┐
│  Heap Memory (Instance Variables)           │
│                                             │
│  john (Student object)                      │
│  ├─ name = "John"                          │
│  └─ gpa = 3.75                             │
│                                             │
│  jane (Student object)                      │
│  ├─ name = "Jane"                          │
│  └─ gpa = 3.50                             │
│                                             │
│  bob (Student object)                       │
│  ├─ name = "Bob"                           │
│  └─ gpa = 3.90                             │
│                                             │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│  Static Area (Class Variables)              │
│                                             │
│  Student.class                              │
│  └─ universityName = "Bangkok University"  │
│     (shared by all objects)                │
│                                             │
└─────────────────────────────────────────────┘

ประกาศ Static Variable

Syntax

java[access modifier] static [type] [variableName] = [initial value];

ตัวอย่าง:
public static String universityName;
private static int totalStudents;
public static final double PI = 3.14159;
protected static int counter = 0;

ตัวอย่าง: การประกาศ

javapublic class Student {
    // STATIC VARIABLES
    public static String universityName = "Bangkok University";      // ← public static
    private static int totalStudents = 0;                           // ← private static
    public static final int MAX_YEAR = 4;                           // ← static final (constant)
    private static double totalGPA = 0;                             // ← private static
    
    // INSTANCE VARIABLES
    private String name;
    private double gpa;
    private int year;
}

Static Shared Counter

ตัวอย่างที่ 1: นับจำนวน Objects

javapublic class Student {
    // Static variable - นับทั้งหมด
    private static int totalStudents = 0;
    
    // Instance variables
    private String name;
    private double gpa;
    
    // Constructor
    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
        totalStudents++;  // ← เพิ่มนับ
    }
    
    // Getter
    public static int getTotalStudents() {
        return totalStudents;
    }
    
    public String getName() {
        return name;
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("Total: " + Student.getTotalStudents());  // 0
        
        Student student1 = new Student("John", 3.75);
        System.out.println("Total: " + Student.getTotalStudents());  // 1
        
        Student student2 = new Student("Jane", 3.50);
        System.out.println("Total: " + Student.getTotalStudents());  // 2
        
        Student student3 = new Student("Bob", 3.90);
        System.out.println("Total: " + Student.getTotalStudents());  // 3
    }
}

Output:

textTotal: 0
Total: 1
Total: 2
Total: 3

ตัวอย่างที่ 2: สกุลเงิน (Currency Conversion)

javapublic class CurrencyConverter {
    // Static variables - อัตราแลกเปลี่ยน (shared)
    private static double thbToUSD = 35.5;    // 1 USD = 35.5 THB
    private static double thbToEUR = 38.0;    // 1 EUR = 38.0 THB
    private static double thbToJPY = 0.28;    // 1 JPY = 0.28 THB
    
    // Static methods - convert
    public static double thbToUsdAmount(double thb) {
        return thb / thbToUSD;
    }
    
    public static double usdToThbAmount(double usd) {
        return usd * thbToUSD;
    }
    
    // Update rates (global change)
    public static void updateUSDRate(double newRate) {
        thbToUSD = newRate;
        System.out.println("✓ USD rate updated to: " + newRate);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Initial Rates ===");
        System.out.println("5000 THB = " + CurrencyConverter.thbToUsdAmount(5000) + " USD");
        System.out.println("100 USD = " + CurrencyConverter.usdToThbAmount(100) + " THB");
        
        System.out.println("\n=== Update Rate ===");
        CurrencyConverter.updateUSDRate(36.0);  // อัตราเปลี่ยน
        
        System.out.println("5000 THB = " + CurrencyConverter.thbToUsdAmount(5000) + " USD");
        System.out.println("100 USD = " + CurrencyConverter.usdToThbAmount(100) + " THB");
    }
}

Static Accumulator

ตัวอย่างที่ 3: บันทึกรวมรายการขาย

javapublic class Store {
    // Static variables - ติดตามรวมทั้ง store
    private static double totalRevenue = 0;      // รายได้รวม
    private static int totalTransactions = 0;    // จำนวน transaction
    private static int totalItemsSold = 0;       // จำนวน items ที่ขาย
    
    // Instance variables - ข้อมูลของแต่ละ store
    private String storeName;
    private String location;
    
    public Store(String name, String location) {
        this.storeName = name;
        this.location = location;
    }
    
    // Instance method - ขายของ
    public void sellItem(String itemName, double price, int quantity) {
        double amount = price * quantity;
        
        // Update static variables (shared)
        totalRevenue += amount;
        totalTransactions++;
        totalItemsSold += quantity;
        
        System.out.printf("[%s] Sold %d x %s = %.2f (Total revenue: %.2f)\n",
                         storeName, quantity, itemName, amount, totalRevenue);
    }
    
    // Static method - ดูสรุปทั้งหมด
    public static void displayGlobalStats() {
        System.out.printf("\n=== Global Store Statistics ===\n");
        System.out.printf("Total Revenue: %.2f THB\n", totalRevenue);
        System.out.printf("Total Transactions: %d\n", totalTransactions);
        System.out.printf("Total Items Sold: %d\n", totalItemsSold);
    }
    
    public void displayStoreInfo() {
        System.out.printf("Store: %s | Location: %s\n", storeName, location);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        // สร้าง 2 stores
        Store store1 = new Store("Store A", "Bangkok");
        Store store2 = new Store("Store B", "Chiang Mai");
        
        System.out.println("=== Store A Operations ===");
        store1.sellItem("Laptop", 25000, 1);
        store1.sellItem("Mouse", 500, 5);
        
        System.out.println("\n=== Store B Operations ===");
        store2.sellItem("Keyboard", 1200, 3);
        store2.sellItem("Monitor", 8000, 2);
        
        // ดูสรุปที่ share กัน
        Store.displayGlobalStats();
    }
}

Output:

text=== Store A Operations ===
[Store A] Sold 1 x Laptop = 25000.00 (Total revenue: 25000.00)
[Store A] Sold 5 x Mouse = 2500.00 (Total revenue: 27500.00)

=== Store B Operations ===
[Store B] Sold 3 x Keyboard = 3600.00 (Total revenue: 31100.00)
[Store B] Sold 2 x Monitor = 16000.00 (Total revenue: 47100.00)

=== Global Store Statistics ===
Total Revenue: 47100.00 THB
Total Transactions: 4
Total Items Sold: 10

Static Constant (Static Final)

ตัวอย่างที่ 4: Configuration Constants

javapublic class Configuration {
    // Static constants - ค่าที่ไม่เปลี่ยน
    public static final String APP_NAME = "Banking System";
    public static final String APP_VERSION = "1.0.0";
    public static final String DATABASE_URL = "jdbc:mysql://localhost:3306/bank";
    public static final int MAX_LOGIN_ATTEMPTS = 3;
    public static final double MIN_BALANCE = 100;
    public static final int TRANSACTION_TIMEOUT = 30;  // seconds
    
    // Static method - display config
    public static void displayConfig() {
        System.out.println("=== Application Configuration ===");
        System.out.println("App: " + APP_NAME);
        System.out.println("Version: " + APP_VERSION);
        System.out.println("Database: " + DATABASE_URL);
        System.out.println("Max Login Attempts: " + MAX_LOGIN_ATTEMPTS);
        System.out.println("Min Balance: " + MIN_BALANCE);
    }
}

public class BankAccount {
    private double balance;
    
    public void withdraw(double amount) {
        // ใช้ static constant
        if (balance - amount < Configuration.MIN_BALANCE) {
            throw new IllegalArgumentException(
                "Balance cannot go below " + Configuration.MIN_BALANCE);
        }
        this.balance -= amount;
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        Configuration.displayConfig();
        
        System.out.println("\n=== Using Constants ===");
        System.out.println("App: " + Configuration.APP_NAME);
        System.out.println("Version: " + Configuration.APP_VERSION);
    }
}

Static Method vs Instance Method

ตัวอย่างที่ 5: Math Utility

javapublic class MathUtil {
    // Static method - ไม่ต้องสร้าง object
    public static int add(int a, int b) {
        return a + b;
    }
    
    public static int multiply(int a, int b) {
        return a * b;
    }
    
    public static double power(double base, int exponent) {
        return Math.pow(base, exponent);
    }
    
    // Instance method - สำหรับ reference
    private int lastResult;
    
    public void calculateAndStore(int a, int b) {
        lastResult = add(a, b);  // เรียก static method
    }
    
    public int getLastResult() {
        return lastResult;
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        // Static method - ไม่ต้องสร้าง object
        int sum = MathUtil.add(5, 3);           // ✓ OK
        int product = MathUtil.multiply(4, 6);  // ✓ OK
        double pow = MathUtil.power(2, 8);      // ✓ OK
        
        System.out.println("5 + 3 = " + sum);
        System.out.println("4 * 6 = " + product);
        System.out.println("2^8 = " + pow);
        
        // Instance method - ต้องสร้าง object
        MathUtil util = new MathUtil();
        util.calculateAndStore(10, 20);
        System.out.println("Stored result: " + util.getLastResult());
    }
}

Static Initialization Block

ตัวอย่างที่ 6: Static Initializer

javapublic class Database {
    // Static variables
    private static String databaseURL;
    private static int connectionPoolSize;
    private static boolean isInitialized = false;
    
    // Static initialization block
    static {
        System.out.println("[Static Init] Initializing database...");
        databaseURL = "jdbc:mysql://localhost:3306/mydb";
        connectionPoolSize = 10;
        isInitialized = true;
        System.out.println("[Static Init] Database initialized!");
    }
    
    // Static method
    public static void connect() {
        if (isInitialized) {
            System.out.println("✓ Connected to: " + databaseURL);
            System.out.println("✓ Pool size: " + connectionPoolSize);
        } else {
            System.out.println("❌ Database not initialized");
        }
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("Program started\n");
        
        Database.connect();
        Database.connect();
    }
}

Output:

text[Static Init] Initializing database...
[Static Init] Database initialized!
Program started

✓ Connected to: jdbc:mysql://localhost:3306/mydb
✓ Pool size: 10
✓ Connected to: jdbc:mysql://localhost:3306/mydb
✓ Pool size: 10

ตัวอย่างที่ 7: University System

javapublic class University {
    // Static variables - ข้อมูล university ทั้งหมด
    public static String universityName = "Bangkok University";
    private static int totalStudents = 0;
    private static int totalFaculty = 0;
    private static double totalTuitionCollected = 0;
    
    // Static method - สถิติ
    public static void displayUniversityStats() {
        System.out.printf("\n=== %s Statistics ===\n", universityName);
        System.out.printf("Total Students: %d\n", totalStudents);
        System.out.printf("Total Faculty: %d\n", totalFaculty);
        System.out.printf("Total Tuition: %.2f\n", totalTuitionCollected);
    }
}

public class Student {
    private String studentID;
    private String name;
    private double gpa;
    private double tuition;
    
    public Student(String id, String name, double gpa, double tuition) {
        this.studentID = id;
        this.name = name;
        this.gpa = gpa;
        this.tuition = tuition;
        
        // Update static counter
        University.totalStudents++;
        University.totalTuitionCollected += tuition;
    }
    
    public void displayInfo() {
        System.out.printf("[%s] %s | GPA: %.2f | University: %s\n",
                         studentID, name, gpa, University.universityName);
    }
}

public class Faculty {
    private String facultyID;
    private String name;
    private String department;
    
    public Faculty(String id, String name, String dept) {
        this.facultyID = id;
        this.name = name;
        this.department = dept;
        
        // Update static counter
        University.totalFaculty++;
    }
    
    public void displayInfo() {
        System.out.printf("[%s] %s | Dept: %s\n", facultyID, name, department);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Creating Students ===");
        Student s1 = new Student("6501001", "John", 3.75, 100000);
        Student s2 = new Student("6501002", "Jane", 3.50, 100000);
        s1.displayInfo();
        s2.displayInfo();
        
        System.out.println("\n=== Creating Faculty ===");
        Faculty f1 = new Faculty("F001", "Dr. Smith", "Computer Science");
        Faculty f2 = new Faculty("F002", "Prof. Jones", "Engineering");
        f1.displayInfo();
        f2.displayInfo();
        
        University.displayUniversityStats();
    }
}

Output:

text=== Creating Students ===
[6501001] John | GPA: 3.75 | University: Bangkok University
[6501002] Jane | GPA: 3.50 | University: Bangkok University

=== Creating Faculty ===
[F001] Dr. Smith | Dept: Computer Science
[F002] Prof. Jones | Dept: Engineering

=== Bangkok University Statistics ===
Total Students: 2
Total Faculty: 2
Total Tuition: 200000.00

Static vs Instance: คำว่า “this”

ความแตกต่าง

javapublic class Account {
    public static String bankName = "National Bank";  // static
    private String accountNumber;                      // instance
    
    public void display() {
        // ✓ ใช้ static ได้
        System.out.println(Account.bankName);
        System.out.println(Account.bankName);  // or: Account.bankName
        
        // ✓ ใช้ instance ต้องมี this/object reference
        System.out.println(this.accountNumber);
    }
}

ตัวอย่างที่ 8: ความเข้าใจถึง Scope

javapublic class Counter {
    public static int globalCount = 0;     // static - shared
    private int instanceCount = 0;         // instance - per object
    
    public void increment() {
        // ✓ เพิ่ม instance counter
        this.instanceCount++;
        
        // ✓ เพิ่ม static counter
        Counter.globalCount++;
    }
    
    public void displayCounts() {
        System.out.printf("Instance: %d | Global: %d\n",
                         this.instanceCount, Counter.globalCount);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        
        c1.increment();
        c1.increment();
        c1.displayCounts();  // Instance: 2 | Global: 2
        
        c2.increment();
        c2.displayCounts();  // Instance: 1 | Global: 3
        
        c1.displayCounts();  // Instance: 2 | Global: 3
        
        System.out.println("Static counter: " + Counter.globalCount);  // 3
    }
}

Output:

textInstance: 2 | Global: 2
Instance: 1 | Global: 3
Instance: 2 | Global: 3
Static counter: 3

Best Practices: Static Variable

1. Static Final สำหรับ Constants

java// ✓ ดี
public class Config {
    public static final String APP_NAME = "MyApp";
    public static final int MAX_USERS = 100;
    public static final double PI = 3.14159;
}

// ❌ ไม่ดี
public class Config {
    public static String APP_NAME = "MyApp";  // should be final
}

2. Private Static สำหรับ Internal Data

java// ✓ ดี
public class Student {
    private static int totalStudents = 0;  // private - ซ่อน
    
    public static int getTotalStudents() {
        return totalStudents;
    }
}

// ❌ ไม่ดี
public class Student {
    public static int totalStudents = 0;  // public - เปิดเผย
}

3. Static Method สำหรับ Utility

java// ✓ ดี: Utility class
public class StringUtils {
    public static String reverse(String text) {
        return new StringBuilder(text).reverse().toString();
    }
    
    public static boolean isPalindrome(String text) {
        return text.equals(reverse(text));
    }
}

// ใช้ - ไม่ต้องสร้าง object
String result = StringUtils.reverse("hello");

4. ระวัง: Static Mutable

java// ⚠️ ระวัง: Static mutable - ทำให้เกิดปัญหา
public class BadExample {
    public static List<String> sharedList = new ArrayList<>();
}

// ใครก็แก้ได้
BadExample.sharedList.add("item");
BadExample.sharedList.clear();  // ← ใครก็ลบได้!

// ✓ ดี: Static immutable
public class GoodExample {
    public static final List<String> sharedList = 
        Collections.unmodifiableList(new ArrayList<>());
}

สรุป: Static Variable

text┌─────────────────────────────────────────┐
│  STATIC VARIABLE (Shared Resource)     │
├─────────────────────────────────────────┤
│                                         │
│  ✓ เป็นของ Class (ไม่ใช่ object)      │
│  ✓ มีค่าเดียว ทั้ง program            │
│  ✓ เข้าถึงจาก: ClassName.variable     │
│  ✓ ใช้สำหรับ:                         │
│    - Counters (นับ)                   │
│    - Accumulator (รวม)                │
│    - Constants (ค่าคงที่)             │
│    - Shared configuration             │
│                                         │
│  ⚠️ ระวัง:                             │
│    - เพิ่มความซับซ้อน                │
│    - ทำให้ test ยาก                   │
│    - Mutable static = ปัญหา          │
│                                         │
└─────────────────────────────────────────┘

ตัวอย่างสุดท้าย: Real-World Scenario

javapublic class OnlineShoppingSystem {
    // Static variables - ระบบ-wide data
    private static double totalSaleAmount = 0;
    private static int totalOrdersProcessed = 0;
    private static int activeUsersCount = 0;
    public static final String PLATFORM_NAME = "ShopHub";
    public static final double TAX_RATE = 0.07;  // 7% tax
    
    // Static method
    public static void displaySystemStats() {
        System.out.printf("\n=== %s System Statistics ===\n", PLATFORM_NAME);
        System.out.printf("Total Sales: %.2f\n", totalSaleAmount);
        System.out.printf("Total Orders: %d\n", totalOrdersProcessed);
        System.out.printf("Active Users: %d\n", activeUsersCount);
    }
}

public class Customer {
    private String customerID;
    private String email;
    private double totalSpent = 0;
    
    public Customer(String id, String email) {
        this.customerID = id;
        this.email = email;
        OnlineShoppingSystem.activeUsersCount++;
    }
    
    public void placeOrder(double amount) {
        double totalWithTax = amount * (1 + OnlineShoppingSystem.TAX_RATE);
        this.totalSpent += totalWithTax;
        
        OnlineShoppingSystem.totalSaleAmount += totalWithTax;
        OnlineShoppingSystem.totalOrdersProcessed++;
        
        System.out.printf("[%s] Order placed: %.2f (with tax: %.2f)\n",
                         email, amount, totalWithTax);
    }
    
    public void displayInfo() {
        System.out.printf("Customer: %s | Total Spent: %.2f\n", email, totalSpent);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Creating Customers ===");
        Customer c1 = new Customer("C001", "[email protected]");
        Customer c2 = new Customer("C002", "[email protected]");
        
        System.out.println("\n=== Placing Orders ===");
        c1.placeOrder(1000);
        c1.placeOrder(500);
        c2.placeOrder(2000);
        
        c1.displayInfo();
        c2.displayInfo();
        
        OnlineShoppingSystem.displaySystemStats();
    }
}

Output:

text=== Creating Customers ===

=== Placing Orders ===
[[email protected]] Order placed: 1000.00 (with tax: 1070.00)
[[email protected]] Order placed: 500.00 (with tax: 535.00)
[[email protected]] Order placed: 2000.00 (with tax: 2140.00)

Customer: [email protected] | Total Spent: 1605.00
Customer: [email protected] | Total Spent: 2140.00

=== ShopHub System Statistics ===
Total Sales: 3745.00
Total Orders: 3
Active Users: 2

บทความนี้เป็นส่วนหนึ่งของรายวิชา Object-Oriented Programming with Java สำหรับนักศึกษาวิศวกรรมคอมพิวเตอร์และวิศวกรรมซอฟต์แวร์