บทนำ: ปัญหาของการ Copy Objects
บางครั้งต้องการ copy object เพื่อสร้าง object ใหม่ที่มี ข้อมูลเดียวกัน
java// ❌ ปัญหา: ไม่มีวิธีที่ดีในการ copy
public class Student {
private String name;
private double gpa;
private int year;
public Student(String name, double gpa, int year) {
this.name = name;
this.gpa = gpa;
this.year = year;
}
}
// ต้องการสร้าง copy
Student original = new Student("John", 3.75, 2);
// ❌ ต้องจำข้อมูล และ set ใหม่
Student copy = new Student(original.getName(), original.getGPA(), original.getYear());
// ❌ ยาว + ลืมได้
// ❌ ถ้า Student มี 10 attributes ต้อง set ทั้งหมด!
ปัญหา:
- ❌ Copy ยุ่งเหยิง
- ❌ ต้องรู้ attributes ทั้งหมด
- ❌ ลืมได้
ยาวไปอยากเลือกอ่าน
- บทนำ: ปัญหาของการ Copy Objects
- Copy Constructor คืออะไร?
- ตัวอย่างที่ 1: Copy Constructor พื้นฐาน
- Shallow Copy vs Deep Copy
- ปัญหา: Shallow Copy
- ✓ Deep Copy (ถูกต้อง)
- ตัวอย่างที่ 2: Copy Constructor ด้วย Validation
- Default Constructor คืออะไร?
- ตัวอย่างที่ 3: Default Constructor
- Java's Implicit Default Constructor
- ❓ ถ้าไม่เขียน Constructor เลย?
- ⚠️ ถ้าเขียน Constructor ใด Constructor หนึ่ง
- ✓ ถ้าต้องใช้ทั้ง Default + Regular
- ตัวอย่างที่ 4: Multiple Constructors Together
- ตัวอย่างที่ 5: Copy Constructor ด้วย Collections
- Best Practices: Copy Constructor
- 1. ใช้ Deep Copy สำหรับ Reference Types
- 2. Null Check
- 3. Default Constructor ใช้ Chaining
- ตัวอย่างสุดท้าย: Complete Class ด้วยทุก Constructors
- สรุป: Copy Constructor & Default Constructor
Copy Constructor คืออะไร?
Copy Constructor = Constructor ที่รับ object เดียวกัน และ copy ข้อมูล
textSyntax:
public ClassName(ClassName original) {
this.field1 = original.field1;
this.field2 = original.field2;
// ...
}
ตัวอย่าง:
public Student(Student original) {
this.name = original.name;
this.gpa = original.gpa;
this.year = original.year;
}
ตัวอย่างที่ 1: Copy Constructor พื้นฐาน
javapublic class Student {
private String name;
private double gpa;
private int year;
// Regular Constructor
public Student(String name, double gpa, int year) {
this.name = name;
this.gpa = gpa;
this.year = year;
}
// Copy Constructor
public Student(Student original) {
this.name = original.name;
this.gpa = original.gpa;
this.year = original.year;
}
public void displayInfo() {
System.out.printf("Name: %s | GPA: %.2f | Year: %d\n", name, gpa, year);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
// สร้าง original
Student original = new Student("John", 3.75, 2);
// ✓ Copy ง่ายๆ
Student copy = new Student(original);
original.displayInfo(); // Name: John | GPA: 3.75 | Year: 2
copy.displayInfo(); // Name: John | GPA: 3.75 | Year: 2
// ✓ Copy มี ข้อมูลเดียวกัน แต่ต่างกัน object
System.out.println("Same object? " + (original == copy)); // false
}
}
Shallow Copy vs Deep Copy
ปัญหา: Shallow Copy
เมื่อ copy object ที่มี reference types (เช่น List, Array)
javaimport java.util.ArrayList;
import java.util.List;
public class Course {
private String courseID;
private List<String> students;
// Regular Constructor
public Course(String courseID) {
this.courseID = courseID;
this.students = new ArrayList<>();
}
// ❌ Shallow Copy (ผิด!)
public Course(Course original) {
this.courseID = original.courseID;
this.students = original.students; // ← reference ตัวเดียวกัน!
}
public void addStudent(String name) {
students.add(name);
}
public void displayInfo() {
System.out.printf("Course: %s | Students: %s\n", courseID, students);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
Course original = new Course("CS101");
original.addStudent("John");
// Shallow copy
Course copy = new Course(original);
// ❌ ปัญหา: เพิ่มที่ copy → original เปลี่ยนด้วย!
copy.addStudent("Jane");
original.displayInfo(); // Course: CS101 | Students: [John, Jane] ❌ เปลี่ยน!
copy.displayInfo(); // Course: CS101 | Students: [John, Jane]
}
}
ปัญหา: students เป็น reference type
original.studentsและcopy.studentsชี้ไปยัง list เดียวกัน- เปลี่ยน
copy.students→original.studentsเปลี่ยนด้วย
✓ Deep Copy (ถูกต้อง)
javaimport java.util.ArrayList;
import java.util.List;
public class Course {
private String courseID;
private List<String> students;
// Regular Constructor
public Course(String courseID) {
this.courseID = courseID;
this.students = new ArrayList<>();
}
// ✓ Deep Copy (ถูก!)
public Course(Course original) {
this.courseID = original.courseID;
// ✓ สร้าง list ใหม่ แล้ว copy ข้อมูล
this.students = new ArrayList<>(original.students);
}
public void addStudent(String name) {
students.add(name);
}
public void displayInfo() {
System.out.printf("Course: %s | Students: %s\n", courseID, students);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
Course original = new Course("CS101");
original.addStudent("John");
// Deep copy
Course copy = new Course(original);
// ✓ เพิ่มที่ copy → original ไม่เปลี่ยน!
copy.addStudent("Jane");
original.displayInfo(); // Course: CS101 | Students: [John] ✓ ไม่เปลี่ยน
copy.displayInfo(); // Course: CS101 | Students: [John, Jane]
}
}
แตกต่าง:
original.students=[John](ไม่เปลี่ยน)copy.students=[John, Jane](เปลี่ยน)- แต่ละอันมี list ต่างกัน (independent)
ตัวอย่างที่ 2: Copy Constructor ด้วย Validation
javapublic class BankAccount {
private String accountNumber;
private String accountHolder;
private double balance;
// Regular Constructor
public BankAccount(String accountNumber, String accountHolder, double balance) {
if (accountNumber == null || accountNumber.isEmpty()) {
throw new IllegalArgumentException("Account number required");
}
if (balance < 0) {
throw new IllegalArgumentException("Balance cannot be negative");
}
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.balance = balance;
}
// Copy Constructor
public BankAccount(BankAccount original) {
if (original == null) {
throw new IllegalArgumentException("Original account cannot be null");
}
// ✓ Copy ข้อมูล (ไม่ต้อง validate เพราะ original ผ่านแล้ว)
this.accountNumber = original.accountNumber;
this.accountHolder = original.accountHolder;
this.balance = original.balance;
}
public void withdraw(double amount) {
if (amount > this.balance) {
throw new IllegalArgumentException("Insufficient balance");
}
this.balance -= amount;
}
public void displayInfo() {
System.out.printf("Account: %s | Holder: %s | Balance: %.2f\n",
accountNumber, accountHolder, balance);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
BankAccount original = new BankAccount("ACC001", "John Doe", 50000);
// ✓ Copy เสร็จเลย
BankAccount copy = new BankAccount(original);
System.out.println("=== Original ===");
original.displayInfo();
System.out.println("\n=== Copy ===");
copy.displayInfo();
// ✓ ใช้งาน copy ได้เลย
System.out.println("\n=== Copy withdraws 10000 ===");
copy.withdraw(10000);
copy.displayInfo();
System.out.println("Original unchanged:");
original.displayInfo();
}
}
Output:
text=== Original ===
Account: ACC001 | Holder: John Doe | Balance: 50000.00
=== Copy ===
Account: ACC001 | Holder: John Doe | Balance: 50000.00
=== Copy withdraws 10000 ===
Account: ACC001 | Holder: John Doe | Balance: 40000.00
Original unchanged:
Account: ACC001 | Holder: John Doe | Balance: 50000.00
Default Constructor คืออะไร?
Default Constructor = Constructor ที่ไม่มี parameter และ initialize ด้วย default values
textSyntax:
public ClassName() {
// initialize ด้วย default values
}
ตัวอย่างที่ 3: Default Constructor
javapublic class Student {
private String name;
private double gpa;
private int year;
// Default Constructor
public Student() {
this.name = "Unknown";
this.gpa = 0.0;
this.year = 1;
}
// Regular Constructor
public Student(String name, double gpa, int year) {
this.name = name;
this.gpa = gpa;
this.year = year;
}
// Copy Constructor
public Student(Student original) {
this.name = original.name;
this.gpa = original.gpa;
this.year = original.year;
}
public void displayInfo() {
System.out.printf("Name: %s | GPA: %.2f | Year: %d\n", name, gpa, year);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
// ✓ Default Constructor - สร้างแบบ placeholder
Student s1 = new Student();
s1.displayInfo(); // Name: Unknown | GPA: 0.00 | Year: 1
// ✓ Regular Constructor
Student s2 = new Student("John", 3.75, 2);
s2.displayInfo(); // Name: John | GPA: 3.75 | Year: 2
// ✓ Copy Constructor
Student s3 = new Student(s2);
s3.displayInfo(); // Name: John | GPA: 3.75 | Year: 2
}
}
Java’s Implicit Default Constructor
❓ ถ้าไม่เขียน Constructor เลย?
java// ❌ ไม่เขียน Constructor
public class Student {
private String name;
private double gpa;
}
// Java สร้าง implicit default constructor อัตโนมัติ
// เหมือน:
public class Student {
public Student() {
// (empty - attributes มี default values)
}
}
// สามารถ new ได้
Student student = new Student();
// name = null (default for String)
// gpa = 0.0 (default for double)
⚠️ ถ้าเขียน Constructor ใด Constructor หนึ่ง
java// ⚠️ ถ้าเขียน Constructor ที่มี parameter
public class Student {
private String name;
private double gpa;
// Regular Constructor
public Student(String name, double gpa) {
this.name = name;
this.gpa = gpa;
}
}
// Java จะ NOT สร้าง implicit default constructor!
// ❌ ต้องเขียน default constructor เอง
Student s1 = new Student("John", 3.75); // ✓ OK
Student s2 = new Student(); // ❌ ERROR! No default constructor
✓ ถ้าต้องใช้ทั้ง Default + Regular
java// ✓ เขียน default constructor เอง
public class Student {
private String name;
private double gpa;
// Default Constructor
public Student() {
this.name = "Unknown";
this.gpa = 0.0;
}
// Regular Constructor
public Student(String name, double gpa) {
this.name = name;
this.gpa = gpa;
}
}
// ✓ ทั้งคู่ใช้ได้
Student s1 = new Student(); // ✓ OK
Student s2 = new Student("John", 3.75); // ✓ OK
ตัวอย่างที่ 4: Multiple Constructors Together
javapublic class Product {
private String productID;
private String productName;
private double price;
private int quantity;
// Constructor 1: Default Constructor
public Product() {
this("", "", 0, 0); // ← เรียก Constructor 3 (master)
}
// Constructor 2: Basic info
public Product(String id, String name) {
this(id, name, 0, 0); // ← เรียก Constructor 3
}
// Constructor 3: All info (Master)
public Product(String id, String name, double price, int qty) {
if (id == null || id.isEmpty()) {
throw new IllegalArgumentException("Product ID required");
}
this.productID = id;
this.productName = name;
this.price = price;
this.quantity = qty;
}
// Constructor 4: Copy Constructor
public Product(Product original) {
this(original.productID, original.productName,
original.price, original.quantity);
}
public void displayInfo() {
System.out.printf("ID: %s | Name: %s | Price: %.2f | Qty: %d\n",
productID, productName, price, quantity);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
System.out.println("=== Default Constructor ===");
Product p1 = new Product();
p1.displayInfo(); // ID: | Name: | Price: 0.00 | Qty: 0
System.out.println("\n=== Basic Info Constructor ===");
Product p2 = new Product("P001", "Laptop");
p2.displayInfo(); // ID: P001 | Name: Laptop | Price: 0.00 | Qty: 0
System.out.println("\n=== Full Info Constructor ===");
Product p3 = new Product("P002", "Mouse", 500, 50);
p3.displayInfo(); // ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50
System.out.println("\n=== Copy Constructor ===");
Product p4 = new Product(p3);
p4.displayInfo(); // ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50
}
}
Output:
text=== Default Constructor ===
ID: | Name: | Price: 0.00 | Qty: 0
=== Basic Info Constructor ===
ID: P001 | Name: Laptop | Price: 0.00 | Qty: 0
=== Full Info Constructor ===
ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50
=== Copy Constructor ===
ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50
ตัวอย่างที่ 5: Copy Constructor ด้วย Collections
javaimport java.util.ArrayList;
import java.util.List;
public class Student {
private String studentID;
private String name;
private List<String> enrolledCourses;
// Default Constructor
public Student() {
this("", "", new ArrayList<>());
}
// Regular Constructor
public Student(String id, String name) {
this(id, name, new ArrayList<>());
}
// Master Constructor
public Student(String id, String name, List<String> courses) {
this.studentID = id;
this.name = name;
// ✓ Deep copy: สร้าง list ใหม่
this.enrolledCourses = new ArrayList<>(courses);
}
// Copy Constructor
public Student(Student original) {
this(original.studentID, original.name, original.enrolledCourses);
// ✓ Master constructor ทำ deep copy ให้
}
public void enrollCourse(String course) {
enrolledCourses.add(course);
System.out.println("✓ Enrolled: " + course);
}
public void displayInfo() {
System.out.printf("ID: %s | Name: %s | Courses: %s\n",
studentID, name, enrolledCourses);
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
// สร้าง original
Student original = new Student("S001", "John");
original.enrollCourse("CS101");
original.enrollCourse("MATH101");
System.out.println("=== Original ===");
original.displayInfo();
// Copy
System.out.println("\n=== Copy ===");
Student copy = new Student(original);
copy.displayInfo();
// ✓ เพิ่ม course ที่ copy
System.out.println("\n=== Copy enrolls more courses ===");
copy.enrollCourse("ENG101");
copy.displayInfo();
// ✓ Original ไม่เปลี่ยน
System.out.println("\n=== Original unchanged ===");
original.displayInfo();
}
}
Output:
text✓ Enrolled: CS101
✓ Enrolled: MATH101
=== Original ===
ID: S001 | Name: John | Courses: [CS101, MATH101]
=== Copy ===
ID: S001 | Name: John | Courses: [CS101, MATH101]
=== Copy enrolls more courses ===
✓ Enrolled: ENG101
ID: S001 | Name: John | Courses: [CS101, MATH101, ENG101]
=== Original unchanged ===
ID: S001 | Name: John | Courses: [CS101, MATH101]
Best Practices: Copy Constructor
1. ใช้ Deep Copy สำหรับ Reference Types
java// ✓ ดี: Deep copy
public class Order {
private List<Item> items;
public Order(Order original) {
// ✓ สร้าง list ใหม่
this.items = new ArrayList<>(original.items);
}
}
// ❌ ไม่ดี: Shallow copy
public class Order {
private List<Item> items;
public Order(Order original) {
// ❌ ใช้ reference เดียวกัน
this.items = original.items;
}
}
2. Null Check
java// ✓ ดี: Check null
public class Account {
private String accountNumber;
public Account(Account original) {
if (original == null) {
throw new IllegalArgumentException("Original cannot be null");
}
this.accountNumber = original.accountNumber;
}
}
// ⚠️ ไม่ดี: ไม่ check
public class Account {
public Account(Account original) {
this.accountNumber = original.accountNumber; // NPE ถ้า original = null
}
}
3. Default Constructor ใช้ Chaining
java// ✓ ดี: Default chaining
public class Employee {
public Employee() {
this("", "", 0); // ← เรียก master
}
public Employee(String id, String name, double salary) {
// master initialization
}
}
// ❌ ไม่ดี: Duplicate code
public class Employee {
public Employee() {
this.id = "";
this.name = "";
this.salary = 0;
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
}
ตัวอย่างสุดท้าย: Complete Class ด้วยทุก Constructors
javaimport java.util.ArrayList;
import java.util.List;
public class Course {
private String courseID;
private String courseName;
private int credits;
private List<String> enrolledStudents;
// Constructor 1: Default
public Course() {
this("", "", 0);
}
// Constructor 2: Basic info
public Course(String courseID, String courseName) {
this(courseID, courseName, 3);
}
// Constructor 3: Master
public Course(String courseID, String courseName, int credits) {
if (courseID == null || courseID.isEmpty()) {
throw new IllegalArgumentException("Course ID required");
}
if (credits < 1 || credits > 4) {
throw new IllegalArgumentException("Credits must be 1-4");
}
this.courseID = courseID;
this.courseName = courseName;
this.credits = credits;
this.enrolledStudents = new ArrayList<>();
}
// Constructor 4: Copy Constructor
public Course(Course original) {
if (original == null) {
throw new IllegalArgumentException("Original cannot be null");
}
this.courseID = original.courseID;
this.courseName = original.courseName;
this.credits = original.credits;
// ✓ Deep copy
this.enrolledStudents = new ArrayList<>(original.enrolledStudents);
System.out.println("✓ Course copied: " + courseName);
}
public void enrollStudent(String studentName) {
enrolledStudents.add(studentName);
}
public int getEnrollmentCount() {
return enrolledStudents.size();
}
public void displayInfo() {
System.out.printf("ID: %s | Name: %s | Credits: %d | Students: %d\n",
courseID, courseName, credits, enrolledStudents.size());
}
}
// ใช้งาน
public class Main {
public static void main(String[] args) {
System.out.println("=== 1. Default Constructor ===");
Course c1 = new Course();
c1.displayInfo();
System.out.println("\n=== 2. Basic Info Constructor ===");
Course c2 = new Course("CS101", "OOP");
c2.displayInfo();
System.out.println("\n=== 3. Full Info Constructor ===");
Course c3 = new Course("CS102", "Data Structures", 4);
c3.enrollStudent("John");
c3.enrollStudent("Jane");
c3.enrollStudent("Bob");
c3.displayInfo();
System.out.println("\n=== 4. Copy Constructor ===");
Course c4 = new Course(c3);
c4.displayInfo();
System.out.println("\n=== Add student to copy ===");
c4.enrollStudent("Alice");
c4.displayInfo();
System.out.println("\n=== Original unchanged ===");
c3.displayInfo();
}
}
Output:
text=== 1. Default Constructor ===
ID: | Name: | Credits: 0 | Students: 0
=== 2. Basic Info Constructor ===
ID: CS101 | Name: OOP | Credits: 3 | Students: 0
=== 3. Full Info Constructor ===
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 3
=== 4. Copy Constructor ===
✓ Course copied: Data Structures
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 3
=== Add student to copy ===
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 4
=== Original unchanged ===
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 3
สรุป: Copy Constructor & Default Constructor
text┌───────────────────────────────────────────────────┐
│ Copy Constructor & Default Constructor │
├───────────────────────────────────────────────────┤
│ │
│ DEFAULT CONSTRUCTOR │
│ ├─ ไม่มี parameter │
│ ├─ Initialize ด้วย default values │
│ └─ ใช้กำหนดค่า placeholder/ค่าเริ่มต้น │
│ │
│ COPY CONSTRUCTOR │
│ ├─ รับ object เดียวกัน เป็น parameter │
│ ├─ Copy ข้อมูลทั้งหมด │
│ ├─ ✓ Deep copy สำหรับ reference types │
│ └─ สร้าง independent copy │
│ │
│ SHALLOW vs DEEP COPY │
│ ├─ Shallow: reference type ชี้เดียวกัน │
│ │ (ปัญหา: เปลี่ยนกระทบกัน) │
│ ├─ Deep: reference type ต่างกัน │
│ │ (ถูก: independent copies) │
│ └─ ใช้ new ArrayList(original) เป็นต้น │
│ │
│ IMPLICIT vs EXPLICIT │
│ ├─ ถ้าไม่เขียน constructor │
│ │ Java สร้าง implicit default │
│ ├─ ถ้าเขียน constructor หนึ่งตัว │
│ │ Java ไม่สร้าง implicit default │
│ └─ ต้องเขียน default เอง │
│ │
│ BEST PRACTICES │
│ ├─ ใช้ chaining เพื่อ reuse code │
│ ├─ Master constructor เหนือสุด │
│ ├─ Null check ใน copy constructor │
│ └─ Deep copy สำหรับ collections │
│ │
└───────────────────────────────────────────────────┘
