class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
มันคือคลาสเหล่านี้ที่เรียกว่าซ้อนกัน แบ่งออกเป็น 2 ประเภท:
- คลาสที่ซ้อนกันแบบไม่คงที่ - คลาสที่ซ้อนกันแบบไม่คงที่ เรียกอีกอย่างว่าคลาสภายในในอีกทางหนึ่ง
- คลาสที่ซ้อนกันแบบคงที่ - คลาสที่ซ้อนกันแบบคงที่
- ชั้นเรียนท้องถิ่น
- ชั้นเรียนที่ไม่ระบุชื่อ

public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Go!");
}
public class HandleBar {
public void right() {
System.out.println("Steering wheel to the right!");
}
public void left() {
System.out.println("Steering wheel to the left!");
}
}
public class Seat {
public void up() {
System.out.println("The seat is up!");
}
public void down() {
System.out.println("The seat is down!");
}
}
}
ที่นี่เรามีคลาสBicycle
-จักรยาน มี 2 ฟิลด์และ 1 วิธี - start()
. 
Bicycle
ได้แก่ คลาสHandleBar
(พวงมาลัย) และSeat
(ที่นั่ง) เหล่านี้เป็นคลาสที่ครบถ้วน: อย่างที่คุณเห็นแต่ละคลาสมีวิธีการของตัวเอง ณ จุดนี้ คุณอาจมีคำถาม: ทำไมเราถึงจัดคลาสหนึ่งไว้ข้างในอีกคลาสหนึ่ง? ทำไมต้องทำให้พวกเขาเป็นภายใน? โอเค สมมติว่าเราต้องการคลาสแยกสำหรับพวงมาลัยและที่นั่งในโปรแกรม แต่คุณไม่จำเป็นต้องทำรัง! คุณสามารถเรียนภาคปกติได้ ตัวอย่างเช่นเช่นนี้:
public class HandleBar {
public void right() {
System.out.println("Steering wheel to the right!");
}
public void left() {
System.out.println("Steering wheel left");
}
}
public class Seat {
public void up() {
System.out.println("The seat is up!");
}
public void down() {
System.out.println("The seat is down!");
}
}
คำถามที่ดีมาก! แน่นอนว่าเราไม่มีข้อจำกัดทางเทคนิค - เราสามารถทำได้ด้วยวิธีนี้ เป็นเรื่องเกี่ยวกับการออกแบบคลาสอย่างถูกต้องจากมุมมองของโปรแกรมเฉพาะและในความหมายของโปรแกรมนั้น คลาสภายในเป็นคลาสสำหรับการเน้นเอนทิตีบางอย่างในโปรแกรมที่เชื่อมโยงกับเอนทิตีอื่นอย่างแยกไม่ออก พวงมาลัย ที่นั่ง แป้นเหยียบเป็นส่วนประกอบของจักรยาน แยกจากจักรยานก็ไม่สมเหตุสมผล ถ้าเราแยกคลาสเหล่านี้ออกจากคลาสสาธารณะ โปรแกรมของเราอาจมีโค้ดต่อไปนี้:
public class Main {
public static void main(String[] args) {
HandleBar handleBar = new HandleBar();
handleBar.right();
}
}
อืม... ความหมายของโค้ดนี้ยากที่จะอธิบายด้วยซ้ำ เรามีแฮนด์จักรยานแปลกๆ อยู่ (ทำไมต้องใช้ด้วย พูดตามตรงก็ไม่รู้) แล้วพวงมาลัยนี้ก็หมุนไปทางขวา... ด้วยตัวเอง โดยไม่มีจักรยาน... ด้วยเหตุผลบางอย่าง การแยกสาระสำคัญของพวงมาลัยออกจากแก่นแท้ของจักรยานทำให้เราสูญเสียตรรกะของโปรแกรมของเราไป เมื่อใช้คลาสภายใน โค้ดจะดูแตกต่างไปจากเดิมอย่างสิ้นเชิง:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.HandleBar handleBar = peugeot.new HandleBar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handleBar.left();
handleBar.right();
}
}
เอาต์พุตคอนโซล:
Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
สิ่งที่เกิดขึ้นอย่างกะทันหันก็สมเหตุสมผล! :) เราได้สร้างวัตถุจักรยาน เราสร้าง "วัตถุย่อย" สองอย่างขึ้นมา - พวงมาลัยและเบาะนั่ง เรายกเบาะให้สูงขึ้นเพื่อความสะดวก - แล้วเราก็ไป: เรากลิ้งและบังคับทิศทางในที่ที่เราต้องไป! :) วิธีการที่เราต้องการถูกเรียกบนวัตถุที่จำเป็น ทุกอย่างเรียบง่ายและสะดวกสบาย ในตัวอย่างนี้ การเน้นแฮนด์และเบาะนั่งจะช่วยเพิ่มการห่อหุ้ม (เรากำลังซ่อนข้อมูลเกี่ยวกับชิ้นส่วนของจักรยานภายในคลาสที่เกี่ยวข้อง) และช่วยให้เราสร้างนามธรรมที่มีรายละเอียดมากขึ้น ตอนนี้เรามาดูสถานการณ์อื่นกัน สมมติว่าเราต้องการสร้างโปรแกรมที่สร้างโมเดลร้านขายจักรยานและอะไหล่ 
-
วัตถุของคลาสภายในไม่สามารถดำรงอยู่ได้หากไม่มีวัตถุของคลาส "ภายนอก"
นี่เป็นเหตุผล: นั่นคือเหตุผลที่เราสร้าง คลาส
Seat
ภายในขึ้นมาHandleBar
เพื่อไม่ให้พวงมาลัยและที่นั่งแบบไม่มีเจ้าของปรากฏที่นี่และที่นั่นในโปรแกรมของเรารหัสนี้จะไม่รวบรวม:
public static void main(String[] args) { HandleBar handleBar = new HandleBar(); }
คุณสมบัติที่สำคัญดังต่อไปนี้ต่อจากนี้:
-
วัตถุของคลาสภายในสามารถเข้าถึงตัวแปรของคลาส "ภายนอก"
ตัวอย่างเช่น เรามาเพิ่ม
Bicycle
ตัวแปร ให้กับคลาสของเราint seatPostDiameter
- เส้นผ่านศูนย์กลางของหลักอานจากนั้นในคลาสภายใน
Seat
เราสามารถสร้างวิธีการgetSeatParam()
ที่จะบอกพารามิเตอร์ที่นั่งให้เราทราบ:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Go!"); } public class Seat { public void up() { System.out.println("The seat is up!"); } public void down() { System.out.println("The seat is down!"); } public void getSeatParam() { System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
และตอนนี้เราสามารถรับข้อมูลนี้ในโปรแกรมของเรา:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.getSeatParam(); } }
เอาต์พุตคอนโซล:
Параметр сиденья: диаметр подседельного штыря = 40
ใส่ใจ:ตัวแปรใหม่ถูกประกาศด้วยตัวแก้ไขที่เข้มงวดที่สุด
private
- แล้วคนชั้นในยังเข้าได้! -
ไม่สามารถสร้างวัตถุคลาสภายในในวิธีการคงที่ของคลาส "ภายนอก"
สิ่งนี้อธิบายได้จากคุณสมบัติการออกแบบของคลาสภายใน คลาสภายในสามารถมีคอนสตรัคเตอร์พร้อมพารามิเตอร์หรือเพียงคอนสตรัคเตอร์เริ่มต้น แต่โดยไม่คำนึงถึงสิ่งนี้ เมื่อเราสร้างอ็อบเจ็กต์ของคลาสภายใน การอ้างอิงถึงอ็อบเจ็กต์ของคลาส "ภายนอก" จะถูกส่งผ่านไปอย่างเงียบ ๆ ท้ายที่สุดแล้ว การมีอยู่ของวัตถุดังกล่าวถือเป็นข้อกำหนดเบื้องต้น มิฉะนั้นเราจะไม่สามารถสร้างวัตถุของชนชั้นภายในได้
แต่ถ้าเมธอดคลาสภายนอกเป็นแบบสแตติก แสดงว่าออบเจ็กต์คลาสภายนอกอาจไม่มีอยู่เลย! ซึ่งหมายความว่าตรรกะของชนชั้นภายในจะถูกทำลาย ในสถานการณ์เช่นนี้ คอมไพลเลอร์จะเกิดข้อผิดพลาด:
public static Seat createSeat() { //Bicycle.this cannot be referenced from a static context return new Seat(); }
-
คลาสภายในไม่สามารถมีตัวแปรและวิธีการคงที่
ตรรกะที่นี่เหมือนกัน: วิธีการและตัวแปรแบบคงที่สามารถมีอยู่และถูกเรียกได้แม้ว่าจะไม่มีวัตถุก็ตาม
แต่หากไม่มีวัตถุของคลาส "ภายนอก" เราก็จะไม่สามารถเข้าถึงคลาสภายในได้
ขัดแย้งกันชัดๆ! ดังนั้นจึงห้ามไม่ให้มีตัวแปรคงที่และวิธีการในคลาสภายใน
คอมไพเลอร์จะแสดงข้อผิดพลาดเมื่อพยายามสร้าง:
public class Bicycle { private int weight; public class Seat { //inner class cannot have static declarations public static void getSeatParam() { System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
เมื่อสร้างอ็อบเจ็กต์ของคลาสภายใน ตัวแก้ไขการเข้าถึงจะมีบทบาทสำคัญ
คลาส ภายในสามารถแสดงโดยตัวดัดแปลงการเข้าถึงมาตรฐาน-
public
,private
และprotected
package private
ทำไมมันถึงสำคัญ?
สิ่งนี้ส่งผลต่อตำแหน่งในโปรแกรมของเราที่เราสามารถยกตัวอย่างคลาสภายในได้
หากคลาสของเรา
Seat
ถูกประกาศเป็นpublic
เราสามารถสร้างอ็อบเจ็กต์ในคลาสอื่นได้ ข้อกำหนดเพียงอย่างเดียวคือต้องมีอ็อบเจ็กต์ของคลาส "ภายนอก" ด้วยอย่างไรก็ตาม เราได้ทำสิ่งนี้ไปแล้วที่นี่:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.HandleBar handleBar = peugeot.new HandleBar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handleBar.left(); handleBar.right(); } }
เราเข้าถึงคลาสภายในได้อย่างง่ายดาย
HandleBar
จากไฟล์Main
.หากเราประกาศคลาสภายในเป็น
private
เราจะมีสิทธิ์เข้าถึงเฉพาะการสร้างวัตถุภายในคลาส "ภายนอก" เท่านั้นSeat
เราจะไม่สามารถสร้างวัตถุ จากภายนอกได้อีกต่อไป:private class Seat { //methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); //Bicycle.Seat has a private access in 'Bicycle' Bicycle.Seat seat = bicycle.new Seat(); } }
คุณคงเข้าใจตรรกะอยู่แล้ว :)
-
ตัวดัดแปลงการเข้าถึงสำหรับคลาสภายในทำงานเหมือนกับตัวแปรทั่วไป
ตัวแก้ไข
protected
ให้การเข้าถึงตัวแปรคลาสในคลาสที่สืบทอดและในคลาสที่อยู่ในแพ็คเกจเดียวกันงาน เดียวกัน
protected
สำหรับคลาสภายใน สามารถสร้าง ออบเจ็กต์protected
คลาสภายในได้ :- ภายในคลาส "ภายนอก"
- ในชั้นเรียนที่สืบทอด;
- ในคลาสที่อยู่ในแพ็คเกจเดียวกัน
หากคลาสภายในไม่มีตัวแก้ไขการเข้าถึง (
package private
) ก็สามารถสร้างอ็อบเจ็กต์ของคลาสภายในได้- ภายในคลาส "ภายนอก"
- ในคลาสที่อยู่ในแพ็คเกจเดียวกัน
คุณคุ้นเคยกับตัวปรับแต่งมาเป็นเวลานาน ดังนั้นจึงไม่มีปัญหาใดๆ ที่นี่
GO TO FULL VERSION