บทนำ: ที่มาของตัวแปร
จนถึงตอนนี้ เราประกาศตัวแปร (attributes, local variables) แต่ไม่เคยคิดว่า:
- ตัวแปรนี้ เข้าถึงได้จากที่ไหนบ้าง? (Scope)
- ตัวแปรนี้ “มีชีวิต” นานแค่ไหน? (Lifetime)
- ตัวแปรนี้เก็บอยู่ที่ไหนในหน่วยความจำ? (Reference vs Value)
- บทนำ: ที่มาของตัวแปร
- Scope: ขอบเขตการเข้าถึง
- Scope คืออะไร?
- ประเภท Scope
- 1. Class Scope (Member Variables)
- 2. Method Scope (Local Variables)
- 3. Block Scope (Control Structures)
- ตัวอย่าง: Scope ที่ซับซ้อน
- Access Modifiers และ Scope
- Lifetime: ช่วงชีวิตของตัวแปร
- Lifetime คืออะไร?
- ประเภท Lifetime
- 1. Instance Variable Lifetime
- 2. Local Variable Lifetime
- 3. Static Variable Lifetime
- ตัวอย่าง: Lifetime ทั้งหมด
- Reference Memory: การเก็บข้อมูลในหน่วยความจำ
- Primitive vs Reference Types
- Primitive Type: เก็บค่าจริง
- Reference Type: เก็บที่อยู่
- Stack vs Heap
- ตัวอย่าง: Reference Behavior
- ตัวอย่างที่ 1: Scope Demo
- ตัวอย่างที่ 2: Lifetime Demo
- ตัวอย่างที่ 3: Reference Memory
- ตัวอย่างที่ 4: Array Reference
- สรุป: Scope, Lifetime, Reference
- Scope (ขอบเขตการเข้าถึง)
- Lifetime (ช่วงชีวิต)
- Reference Memory
- ตัวอย่างสุดท้าย: รวม Scope, Lifetime, Reference
Scope: ขอบเขตการเข้าถึง
Scope คืออะไร?
Scope (ขอบเขต) คือ ส่วนของโปรแกรมที่สามารถเข้าถึงตัวแปรได้
ตัวแปรที่ประกาศในที่หนึ่ง อาจเข้าถึงไม่ได้ ในที่อื่น
ประเภท Scope
1. Class Scope (Member Variables)
Attributes ที่ประกาศใน class สามารถเข้าถึงได้ทั่วทั้ง class
javapublic class Student {
// Class scope - เข้าถึงได้จากทุก methods ใน class นี้
private String name;
private double gpa;
public void study() {
// เข้าถึงได้: this.name, this.gpa
System.out.println(this.name + " is studying");
}
public void displayInfo() {
// เข้าถึงได้: this.name, this.gpa
System.out.println("Name: " + this.name);
}
}
Visual:
textClass Student
├─ name (private) ─┐
├─ gpa (private) ─┤ Accessible everywhere in class
├─ study() ─┤ Can access name and gpa
├─ displayInfo() ──┘
└─ ...
2. Method Scope (Local Variables)
ตัวแปรที่ประกาศใน method เข้าถึงได้เฉพาะใน method นั้นเท่านั้น
javapublic class Calculator {
public void calculate() {
// Local variable - เข้าถึงได้เฉพาะใน calculate() เท่านั้น
int result = 10 + 5;
System.out.println(result);
}
public void anotherMethod() {
// ❌ ไม่สามารถเข้าถึง result ได้
// System.out.println(result); // ERROR!
}
}
Visual:
textmethod1() {
int x = 5; ← เข้าถึงได้เฉพาะ method1
// x accessible here
}
method2() {
// ❌ x not accessible here
// System.out.println(x); // ERROR!
}
3. Block Scope (Control Structures)
ตัวแปรใน { } scope เข้าถึงได้เฉพาะใน block นั้น
javapublic void demo() {
int x = 10; // Method scope
if (x > 5) {
int y = 20; // Block scope - เข้าถึงได้เฉพาะใน if block
System.out.println(x); // ✓ OK (x is in method scope)
System.out.println(y); // ✓ OK (y is in if block)
}
System.out.println(x); // ✓ OK
System.out.println(y); // ❌ ERROR! (y not in scope)
}
Visual:
textmethod {
int x = 10;
if (...) {
int y = 20; ← เข้าถึงได้เฉพาะที่นี่
println(x); ✓
println(y); ✓
}
println(x); ✓
println(y); ❌ ERROR!
}
ตัวอย่าง: Scope ที่ซับซ้อน
javapublic class ScopeDemo {
// CLASS SCOPE
private String className = "ScopeDemo";
public void method1() {
// METHOD SCOPE
int localVar1 = 10;
for (int i = 0; i < 3; i++) {
// BLOCK SCOPE
int blockVar = i * 2;
System.out.println("className: " + className); // ✓ class scope
System.out.println("localVar1: " + localVar1); // ✓ method scope
System.out.println("i: " + i); // ✓ block scope
System.out.println("blockVar: " + blockVar); // ✓ block scope
}
// ❌ i ไม่เข้าถึงได้ (loop scope)
// System.out.println(i); // ERROR!
// ❌ blockVar ไม่เข้าถึงได้ (for block scope)
// System.out.println(blockVar); // ERROR!
}
public void method2() {
// localVar1 ไม่เข้าถึงได้ (different method scope)
// System.out.println(localVar1); // ERROR!
}
}
Access Modifiers และ Scope
javapublic class AccessDemo {
public String publicAttribute; // เข้าถึงได้ทุกที่
private String privateAttribute; // เข้าถึงได้เฉพาะใน class นี้
protected String protectedAttribute; // เข้าถึงได้ใน subclass
String defaultAttribute; // เข้าถึงได้ในแพ็คเกจเดียวกัน
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
AccessDemo demo = new AccessDemo();
demo.publicAttribute = "OK"; // ✓ OK
demo.privateAttribute = "ERROR"; // ❌ ERROR! (private)
demo.protectedAttribute = "..."; // ✓ OK (in same package)
demo.defaultAttribute = "OK"; // ✓ OK (in same package)
}
}
Lifetime: ช่วงชีวิตของตัวแปร
Lifetime คืออะไร?
Lifetime (ช่วงชีวิต) คือ ช่วงเวลา ที่ตัวแปร มีอยู่ในหน่วยความจำ
ตัวแปรถูกสร้าง (allocated) และถูกลบ (deallocated) ตามขอบเขต
ประเภท Lifetime
1. Instance Variable Lifetime
Instance variables มีชีวิต ตราบเท่า object ยังอยู่ในหน่วยความจำ
javapublic class Student {
private String name; // Instance variable
private double gpa;
public Student(String name, double gpa) {
this.name = name; // allocated
this.gpa = gpa;
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
Student student = new Student("John", 3.75);
// ที่นี่ student object มีชีวิต
// name, gpa มีชีวิต
student = null; // หลังจากนี้ object จะถูกลบ
// Garbage Collector จะลบ student object
// name, gpa ก็ถูกลบไปด้วย
}
}
Timeline:
textmain() เริ่ม
↓
new Student() ← object สร้าง
├─ name allocated
├─ gpa allocated
↓
(ใช้งาน object)
↓
student = null
↓
Garbage Collector
├─ name deallocated
├─ gpa deallocated
├─ object deleted
↓
main() จบ
2. Local Variable Lifetime
Local variables มีชีวิต เฉพาะเมื่อ method/block ทำงาน
javapublic void example() {
int x = 10; // ← allocated เมื่อ method เริ่มทำงาน
// x สามารถใช้ได้
System.out.println(x);
} // ← deallocated เมื่อ method จบ
// หลังจากนี้ x ไม่มีชีวิตแล้ว
Timeline:
textexample() เริ่ม
↓
int x = 10; ← x allocated
↓
(ใช้ x)
↓
} ← method จบ
↓
x deallocated
3. Static Variable Lifetime
Static variables มีชีวิต นานเท่าโปรแกรมทำงาน
javapublic class Counter {
public static int count = 0; // Static variable
public static void increment() {
count++;
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
Counter.increment(); // count = 1
// count ยังมีอยู่
Counter.increment(); // count = 2
// count ยังมีอยู่
} // main จบ แต่ count ยังมีชีวิต
} // โปรแกรมจบ → count deallocated
Timeline:
textProgram starts
↓
Class Counter loaded
↓
count allocated ← allocated ทันที
↓
(ใช้ count ตลอดโปรแกรม)
↓
Program ends
↓
count deallocated ← deallocated ตอนท้ายสุด
ตัวอย่าง: Lifetime ทั้งหมด
javapublic class LifetimeDemo {
// STATIC - มีชีวิตนานเท่าโปรแกรม
public static int globalCounter = 0;
// INSTANCE - มีชีวิตเท่าที่ object มีอยู่
private int instanceCounter = 0;
public void method() {
// LOCAL - มีชีวิตเฉพาะใน method นี้
int localCounter = 0;
for (int i = 0; i < 3; i++) {
// BLOCK - มีชีวิตเฉพาะใน for block
int blockCounter = 0;
}
// blockCounter ไม่มีชีวิตแล้ว
}
// localCounter ไม่มีชีวิตแล้ว
}
// ใช้งาน
LifetimeDemo demo = new LifetimeDemo();
// demo, instanceCounter เกิด
LifetimeDemo demo2 = new LifetimeDemo();
// demo2, instanceCounter (ของ demo2) เกิด
demo = null;
// demo ลบ, instanceCounter (ของ demo) ลบ
// ยังมี: globalCounter, demo2, instanceCounter (ของ demo2)
demo2 = null;
// demo2 ลบ, instanceCounter (ของ demo2) ลบ
// ยังมี: globalCounter
// Program ends
// globalCounter ลบ
Reference Memory: การเก็บข้อมูลในหน่วยความจำ
Primitive vs Reference Types
Java มีสองวิธีในการเก็บข้อมูล:
| ประเภท | สิ่งที่เก็บ | ตัวอย่าง |
|---|---|---|
| Primitive | ค่าจริง | int, double, boolean |
| Reference | ที่อยู่ (pointer) | String, Object, Array |
Primitive Type: เก็บค่าจริง
javaint x = 5; // เก็บค่า 5 ตรงๆ
double y = 3.14; // เก็บค่า 3.14 ตรงๆ
// Memory:
// x: [5]
// y: [3.14]
ทำสำเนา:
javaint a = 10;
int b = a; // b ก็ได้ 10 (สำเนาค่า)
a = 20; // a เปลี่ยน
// b ยังคง 10 (ไม่ได้รับผลกระทบ)
Reference Type: เก็บที่อยู่
javaString name = "John";
// Memory:
// name: [memory address 0x123]
// ↓
// [0x123] = "John"
// name เก็บ "ที่อยู่" ไม่ใช่ค่าตัวจริง
เมื่อทำสำเนา:
javaString name1 = "John";
String name2 = name1; // name2 ชี้ไปที่ที่อยู่เดียวกับ name1
// Memory:
// name1: [address 0x123] ──┐
// name2: [address 0x123] ──┤─→ "John"
// │
// ทั้ง name1 และ name2 ชี้ไปเดียวกัน!
Stack vs Heap
Java จัดเก็บข้อมูลในสองที่:
| Stack | Heap |
|---|---|
| เก็บ Primitive values | เก็บ Objects |
| เก็บ References | (ค่าจริงของ objects) |
| เล็กกว่า | ใหญ่กว่า |
| เร็ว | ช้ากว่า |
Visual:
textStack (เร็ว, เล็ก)
┌─────────────┐
│ int x = 5 │
│ double y = 3.14 │
│ name: [ref] │──────┐
│ car: [ref] │──┐ │
└─────────────┘ │ │
│ │
Heap (ช้า, ใหญ่) │ │
┌─────────────┐ │ │
│ Student obj │←─┤ │
│ String "John"│←─────┘
│ Car obj │
└─────────────┘
ตัวอย่าง: Reference Behavior
javapublic class ReferenceBehavior {
public static void main(String[] args) {
// === PRIMITIVE: Copy Value ===
int a = 10;
int b = a;
a = 20;
System.out.println("a = " + a); // 20
System.out.println("b = " + b); // 10 (ไม่เปลี่ยน)
// === REFERENCE: Copy Address ===
Student student1 = new Student("John", 3.75);
Student student2 = student1; // ชี้ไปที่ object เดียวกัน
student1.setGPA(3.85); // เปลี่ยน student1
System.out.println("student1 GPA: " + student1.getGPA()); // 3.85
System.out.println("student2 GPA: " + student2.getGPA()); // 3.85 (เปลี่ยน!)
// ทั้ง student1 และ student2 ชี้ไปเดียวกัน!
// === NULL REFERENCE ===
Student student3 = null; // ไม่ชี้ไปไหน
// student3.displayInfo(); // ERROR! NullPointerException
}
}
Memory Visualization:
textInitial:
Stack:
┌──────────────────┐
│ a: [10] │
│ b: [10] │
│ student1:[ref] │──┐
│ student2:[ref] │──┼─→ Student object (John, 3.75)
│ student3:[null] │ │
└──────────────────┘ └──→ Heap
After: a = 20, student1.setGPA(3.85)
Stack:
┌──────────────────┐
│ a: [20] │
│ b: [10] │ ← ต่างกัน (primitive)
│ student1:[ref] │──┐
│ student2:[ref] │──┼─→ Student object (John, 3.85)
│ student3:[null] │ │
└──────────────────┘ └──→ Heap
ทั้ง student1 และ student2 ชี้ไปเดียวกัน
ตัวอย่างที่ 1: Scope Demo
javapublic class ScopeExample {
// CLASS SCOPE
private String className = "ScopeExample";
private static int staticCount = 0;
public void exampleMethod() {
// METHOD SCOPE
String methodVar = "Method";
staticCount++;
// เข้าถึงได้ทั้ง class scope และ method scope
System.out.println("Class: " + className);
System.out.println("Method: " + methodVar);
System.out.println("Static: " + staticCount);
// If block
if (staticCount > 0) {
// BLOCK SCOPE
String ifVar = "If Block";
System.out.println("If: " + ifVar);
}
// ❌ ifVar ไม่เข้าถึงได้
// System.out.println(ifVar); // ERROR!
// For loop
for (int i = 0; i < 2; i++) {
// LOOP BLOCK SCOPE
String loopVar = "Loop " + i;
System.out.println(loopVar);
}
// ❌ i ไม่เข้าถึงได้
// System.out.println(i); // ERROR!
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
ScopeExample example = new ScopeExample();
example.exampleMethod();
example.exampleMethod();
// ❌ methodVar ไม่เข้าถึงได้
// System.out.println(example.methodVar); // ERROR!
// ✓ staticCount เข้าถึงได้
System.out.println("Total calls: " + ScopeExample.staticCount); // 2
}
}
ตัวอย่างที่ 2: Lifetime Demo
javapublic class LifetimeExample {
// STATIC - มีชีวิต: program start → program end
private static int staticVar = 0;
// INSTANCE - มีชีวิต: object create → object deleted
private int instanceVar = 0;
public LifetimeExample(int id) {
instanceVar = id;
staticVar++;
System.out.println("Created object " + id +
", total: " + staticVar);
}
public void demonstrateLocal() {
// LOCAL - มีชีวิต: method start → method end
int localVar = 999;
System.out.println("LocalVar: " + localVar);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
System.out.println("=== Create Object 1 ===");
LifetimeExample obj1 = new LifetimeExample(1);
// obj1 alive
// instanceVar (of obj1) alive
// staticVar = 1
System.out.println("\n=== Call Method ===");
obj1.demonstrateLocal();
// localVar created
// localVar used
// localVar deleted (after method ends)
System.out.println("\n=== Create Object 2 ===");
LifetimeExample obj2 = new LifetimeExample(2);
// obj2 alive
// instanceVar (of obj2) alive
// staticVar = 2
System.out.println("\n=== Delete Object 1 ===");
obj1 = null;
// obj1 deleted by Garbage Collector
// instanceVar (of obj1) deleted
// staticVar still = 2
System.out.println("\n=== Delete Object 2 ===");
obj2 = null;
// obj2 deleted
// instanceVar (of obj2) deleted
// staticVar still = 2
System.out.println("\nProgram ends, staticVar deleted");
}
}
ตัวอย่างที่ 3: Reference Memory
javapublic class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
}
public void deposit(double amount) {
balance += amount;
}
public void displayInfo() {
System.out.println("Account: " + accountNumber +
", Balance: " + balance);
}
}
// ใช้งาน
public class ReferenceDemo {
public static void main(String[] args) {
System.out.println("=== Primitive Type ===");
int balance1 = 1000;
int balance2 = balance1;
balance1 = 2000;
System.out.println("balance1: " + balance1); // 2000
System.out.println("balance2: " + balance2); // 1000 (ต่างกัน)
System.out.println("\n=== Reference Type ===");
BankAccount account1 = new BankAccount("ACC001", 1000);
BankAccount account2 = account1; // ชี้ไปเดียวกัน
account1.deposit(1000); // เปลี่ยน account object
System.out.println("Account1:");
account1.displayInfo(); // Balance: 2000
System.out.println("Account2:");
account2.displayInfo(); // Balance: 2000 (เปลี่ยนไปด้วย!)
System.out.println("\n=== Different Objects ===");
BankAccount account3 = new BankAccount("ACC003", 1000);
BankAccount account4 = new BankAccount("ACC004", 1000);
account3.deposit(500);
System.out.println("Account3:");
account3.displayInfo(); // Balance: 1500
System.out.println("Account4:");
account4.displayInfo(); // Balance: 1000 (ต่างกัน)
}
}
Output:
text=== Primitive Type ===
balance1: 2000
balance2: 1000
=== Reference Type ===
Account1:
Account: ACC001, Balance: 2000.0
Account2:
Account: ACC001, Balance: 2000.0
=== Different Objects ===
Account3:
Account: ACC003, Balance: 1500.0
Account4:
Account: ACC004, Balance: 1000.0
ตัวอย่างที่ 4: Array Reference
javapublic class ArrayReferenceDemo {
public static void main(String[] args) {
System.out.println("=== Primitive Array ===");
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // ชี้ไปเดียวกัน
arr1[0] = 99;
System.out.println("arr1[0]: " + arr1[0]); // 99
System.out.println("arr2[0]: " + arr2[0]); // 99 (เปลี่ยน!)
System.out.println("\n=== Object Array ===");
Student[] students1 = new Student[2];
students1[0] = new Student("John", 3.75);
students1[1] = new Student("Jane", 3.50);
Student[] students2 = students1; // ชี้ไปเดียวกัน
students1[0].updateGPA(3.85);
System.out.println("students1[0] GPA: " +
students1[0].getGPA()); // 3.85
System.out.println("students2[0] GPA: " +
students2[0].getGPA()); // 3.85 (เปลี่ยน!)
System.out.println("\n=== New Array vs Same Reference ===");
int[] arr3 = new int[3]; // ใหม่
int[] arr4 = new int[3]; // ใหม่
arr3[0] = 10;
arr4[0] = 20;
System.out.println("arr3[0]: " + arr3[0]); // 10
System.out.println("arr4[0]: " + arr4[0]); // 20 (ต่างกัน)
}
}
สรุป: Scope, Lifetime, Reference
Scope (ขอบเขตการเข้าถึง)
text┌─────────────────────┐
│ Class Scope │
│ ├─ Attributes │
│ ├─ Method Scope │
│ │ ├─ Local Vars │
│ │ ├─ If Block │
│ │ ├─ For Block │
│ │ └─ ... │
│ └─ Method2 │
└─────────────────────┘
เอกสารประกาศ Scope ที่แคบลง = เข้าถึงได้น้อยลง
Lifetime (ช่วงชีวิต)
| ประเภท | จำนวน | ช่วงชีวิต |
|---|---|---|
| Static | 1 | Program start → end |
| Instance | 1 ต่อ object | Object create → delete |
| Local | ใหม่แต่ละรอบ | Method/block start → end |
Reference Memory
| ประเภท | เก็บ | ทำสำเนา |
|---|---|---|
| Primitive | ค่าจริง | สำเนาค่า (ต่างกัน) |
| Reference | ที่อยู่ | ชี้ไปเดียวกัน |
ตัวอย่างสุดท้าย: รวม Scope, Lifetime, Reference
javapublic class ComprehensiveDemo {
// CLASS SCOPE, PROGRAM LIFETIME, STATIC
public static int globalCount = 0;
// CLASS SCOPE, OBJECT LIFETIME, INSTANCE
private String name;
private double[] scores;
public ComprehensiveDemo(String name) {
this.name = name;
this.scores = new double[5];
ComprehensiveDemo.globalCount++;
}
public void addScores(double[] newScores) {
// METHOD SCOPE, METHOD LIFETIME, REFERENCE
// newScores reference points to array
if (newScores != null) {
// BLOCK SCOPE, BLOCK LIFETIME
int length = newScores.length;
for (int i = 0; i < length; i++) {
// LOOP BLOCK SCOPE, LOOP BLOCK LIFETIME
double score = newScores[i];
this.scores[i] = score; // REFERENCE - ชี้ไปที่ array
}
}
}
public void displayInfo() {
System.out.printf("Name: %s, Count: %d\n",
this.name, ComprehensiveDemo.globalCount);
for (int i = 0; i < this.scores.length; i++) {
System.out.printf("Score %d: %.1f\n", i+1, this.scores[i]);
}
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
// globalCount allocated (static lifetime = program duration)
// obj1 allocated (instance lifetime = object duration)
ComprehensiveDemo obj1 = new ComprehensiveDemo("John");
double[] scores1 = {85, 90, 78, 92, 88}; // reference type
obj1.addScores(scores1);
// obj2 allocated
ComprehensiveDemo obj2 = new ComprehensiveDemo("Jane");
double[] scores2 = {75, 80, 82, 78, 85}; // reference type
obj2.addScores(scores2);
obj1.displayInfo();
obj2.displayInfo();
// scores1, scores2 scope ends here (method scope)
// but data still in obj1.scores, obj2.scores (instance lifetime)
obj1 = null; // obj1 deallocated (Garbage Collector)
// but globalCount still exists (static lifetime)
// globalCount deallocated when program ends
}
}
