Java ตัดสินใจตรงข้ามกับ Go เกือบทุกข้อ และเครื่องมือชุดแรก
คอร์สนี้ไม่ได้เริ่มที่ว่าตัวแปรคืออะไร
มันเริ่มที่คำถามที่มีประโยชน์กว่า — Java ตัดสินใจต่างจากภาษาที่คุณใช้อยู่ตรงไหน และมันจ่ายอะไรไปกับการตัดสินใจนั้น
คอร์สนี้สร้าง orderctl เครื่องมือบรรทัดคำสั่งที่ประมวลผลออเดอร์อาหาร
ตัวเดิมกับที่ คอร์ส Go จากศูนย์ สร้างไว้
การเขียนของชิ้นเดียวกันสองภาษา ทำให้คำถาม “ภาษาไหนดีกว่า” กลายเป็นคำถามที่ตอบได้ ด้วยการชี้ไปที่ code สองชุดที่ทำงานเดียวกัน แทนที่จะเถียงกันด้วยความรู้สึก
บทนี้เป็นบทแนวคิดกับการติดตั้ง code ที่รันได้จริงมีแค่ hello world · ของจริงเริ่มบทที่ 2
ห้าข้อที่ Java เลือกทางตรงข้าม
หัวข้อที่มีชื่อว่า “ห้าข้อที่ Java เลือกทางตรงข้าม”คอร์สนี้จะกลับมาที่ทุกแถวทีละบท และทุกแถวมีราคาทั้งสองฝั่ง
| เรื่อง | Go เลือก | Java เลือก | บทที่กลับมา |
|---|---|---|---|
| ความผิดพลาด | เป็นค่าที่ฟังก์ชันคืนกลับมา | เป็น exception ที่กระโดดขึ้นไปเอง | 6 |
| การใช้ซ้ำ | ประกอบอย่างเดียว ไม่มีการสืบทอด | มีการสืบทอดและ abstract class | 5 |
| การเข้ากับ interface | ดูจากรูปร่าง ไม่ต้องประกาศ | ต้องเขียน implements ออกมา | 5 |
| การจัดรูปแบบ | gofmt ที่ไม่มีตัวเลือก | ไม่มีตัวไหนในกล่อง ต้องเลือกเอง | บทนี้ |
| ผลของการ build | โปรแกรมเดียวที่รันบนเครื่องเปล่าได้ | ต้องมี JVM รออยู่ปลายทาง | 3 |
สี่แถวแรกไม่มีฝ่ายชนะ · แถวที่ห้ามีฝ่ายชนะชัดในบางบริบท และคอร์สนี้จะไม่แกล้งทำเป็นไม่เห็น
เลือกรุ่นก่อน
หัวข้อที่มีชื่อว่า “เลือกรุ่นก่อน”Java ออกรุ่นใหม่ทุกหกเดือน แต่มีแค่บางรุ่นที่ได้รับการดูแลยาว
รุ่นที่ได้รับการดูแลยาวเรียกว่า LTS · ตั้งแต่ Java 17 เป็นต้นมา LTS ออกทุกสองปี จึงเป็น 17, 21, 25 แล้วจะเป็น 29
คอร์สนี้ใช้ Java 25 และทุกตัวอย่างรันได้โดยไม่ต้องเปิดโหมดทดลอง
| ของที่คอร์สใช้ | ต้องมีอย่างน้อย |
|---|---|
record · sealed · text block | 17 |
pattern matching ใน switch | 21 |
void main() · IO.println | 25 |
สามอย่างในแถวสุดท้ายคือเหตุผลที่คอร์สนี้ไม่รองรับ 21 · มันทำให้บทที่ 1 กับ 2 เขียนได้โดยไม่ต้องมี build tool เลย ซึ่งเป็นเรื่องที่เปลี่ยนรูปการสอนทั้งคอร์ส
ติดตั้งแล้วดูว่าได้อะไรมา
หัวข้อที่มีชื่อว่า “ติดตั้งแล้วดูว่าได้อะไรมา”เลือกทางไหนก็ได้ แต่ให้ดูแค่ว่าตัวเลขขึ้นต้นด้วย 25
$ java --versionopenjdk 25.0.4.1 2026-08-18ตัวเลขหลังจุดต่างกันตามที่มาที่คุณติดตั้ง · คอร์สนี้จึงผูกกับ 25 เท่านั้น ไม่ผูกกับ string เต็ม
java --version เขียนออก stdout ส่วน java -version เขียนออก stderr
ข้อนี้กัดตอนคุณเขียนสคริปต์แล้วดักผลด้วย $(...) หรือส่งต่อด้วย pipe
· -version จะหายไปเงียบๆ เพราะสองท่านั้นรับเฉพาะ stdout
ผู้เขียนบทนี้ทดสอบข้อนี้ผิดในรอบแรกด้วย · ใช้ 2>&1 >/dev/null ซึ่งเรียงลำดับ
ผิดทาง ทำให้ stdout ยังไหลเข้า pipe อยู่ · ลำดับที่ถูกคือ 2>&1 1>/dev/null
java ไม่ได้มาตัวเดียว มันมาเป็นชุด และแต่ละตัวทำคนละอย่าง
javaรันโปรแกรม และตั้งแต่ 11 มันรับ source โดยตรงได้ด้วยjavacแปล source เป็น.classjarห่อ.classหลายตัวเป็นแฟ้มเดียวjshellลองพิมพ์ทีละบรรทัดแล้วดูผลทันทีjlinkประกอบ runtime ที่มีเฉพาะส่วนที่โปรแกรมใช้
ถ้าคุณมาจาก C# คุณจะมองหา CLI กลางแบบ dotnet ที่คุมทั้ง build, test, run และการ
ดึง package
ไม่มี · ทั้งห้าคำสั่งข้างบนไม่มีตัวไหนรู้จัก dependency เลยสักตัว · ตัวที่ทำหน้าที่นั้นคือ Maven ซึ่งอยู่นอก JDK และเราจะเจอมันในบทที่ 3
ถ้าคุณมาจาก Python จุดที่ต่างคือไม่มี virtual environment ให้ทำ
· dependency ของทุกโปรเจกต์ในเครื่องกองรวมกันที่ ~/.m2 แล้วแยกกันด้วยเลขรุ่น
โปรแกรมแรก
หัวข้อที่มีชื่อว่า “โปรแกรมแรก”สามอย่างที่ Java เคยบังคับให้เขียนรอบ hello world หายไปพร้อมกันทั้งสามอย่าง
สร้าง Hello.java แล้วใส่แค่นี้
void main() { IO.println("orderctl พร้อมทำงาน");}$ java Hello.javaorderctl พร้อมทำงานสังเกตว่าไม่มี class Hello ครอบ ไม่มี public static void main(String[] args)
และไม่มี System.out.println
นี่คือ compact source filecompact source fileรูปย่อของ source ที่ประกาศ class ครอบไม่ต้อง (JEP 512 final ใน Java 25) · ประกาศ `package` ไม่ได้ และสมาชิกทั้งหมดกลายเป็นสมาชิกของ class โดยปริยาย ซึ่งเป็นรูปที่เพิ่งเสร็จสมบูรณ์ที่ Java 25 · สมาชิกทุกตัวที่คุณเขียน กลายเป็นสมาชิกของ class ที่ compiler ตั้งชื่อให้เอง
void main() ที่ไม่ใช่ static และไม่รับ argument คือ instance main methodinstance main method`void main()` ที่ไม่ใช่ static และไม่รับ argument · launcher ของ Java 25 เรียก constructor ไร้ argument ก่อนแล้วจึงเรียก main ตัวนี้
· ตัวรันของ Java 25 สร้าง object ขึ้นมาก่อนด้วย constructor ที่ไม่รับอะไร แล้วจึงเรียกมัน
void main() เป็นของทดลองมาตั้งแต่ Java 21 แล้วค่อยๆ เปลี่ยนรูปจนเสร็จที่ 25
ส่วน class IO ไม่มีเลยใน 21 และ 22 · มันมาครั้งแรกที่ 23 ในชื่อ java.io.IO
โดยตอนนั้นเขียน println(...) เปล่าๆ ได้เลย · พอถึง 25 มันย้ายไป java.lang.IO
และเลิกนำเข้าให้อัตโนมัติ จึงต้องเขียน IO.println(...) เต็มๆ อย่างที่เห็น
ผลคือตัวอย่างจาก 23 หรือ 24 ที่คุณค้นเจอ จะคอมไพล์ไม่ผ่านบน 25 · นี่ไม่ใช่ความผิดของคุณ มันคือฟีเจอร์ที่เพิ่งนิ่ง
เปิดฝากล่องดูว่าเกิดอะไรขึ้น
หัวข้อที่มีชื่อว่า “เปิดฝากล่องดูว่าเกิดอะไรขึ้น”
java Hello.javaไม่ใช่การตีความ มันแปลจริง แค่ไม่เก็บผลไว้
file เดียวกันนั้นเอาไปแปลด้วย javac ก็ได้
$ javac -d out Hello.java$ ls outHello.class$ java -cp out Helloorderctl พร้อมทำงานcompact source file จึงเป็นฟีเจอร์ของภาษา ไม่ใช่โหมดพิเศษของคำสั่ง java
สิ่งที่คำสั่ง java เพิ่มให้คือ source launchersource launcherโหมดที่สั่ง `java Main.java` แล้วแปลในหน่วยความจำ · class ที่มาจาก class path มองไม่เห็น class ที่แปลแบบนี้ จึงรัน JUnit ร่วมกับมันไม่ได้ — มันแปลไว้ในหน่วยความจำแล้วรันทันที
โดยไม่ทิ้ง .class ไว้
flowchart LR A["Hello.java"] -->|"java Hello.java"| B["แปลในหน่วยความจำ<br/>แล้วรันทันที"] A -->|"javac -d out"| C["out/Hello.class"] C -->|"java -cp out Hello"| D["รันจากที่แปลไว้แล้ว"] B -.->|"ไม่เหลืออะไรไว้"| E["ต้องแปลใหม่ทุกครั้ง"]
คำบรรยายภาพ: สองเส้นทางจาก source เดียวกัน เส้นบนแปลใหม่ทุกครั้งแล้วไม่เก็บผล เส้นล่างแปลครั้งเดียวแล้วรันซ้ำได้
ราคาของความสะดวกคือการแปลใหม่ทุกครั้ง วัดบนเครื่องที่เขียนบทนี้ (OpenJDK 25.0.4.1 · macOS arm64) เจ็ดรอบต่อแบบ วัดซ้ำสี่ชุด เส้นบนช้ากว่าเส้นล่างราวสิบเท่า
ตัวเลขวินาทีจริงไม่ถูกยกมาที่นี่ เพราะมันไม่ข้ามเครื่อง · สิ่งที่ข้ามได้คือทิศทาง และทิศทางนี้คือเหตุผลที่บทที่ 3 ต้องเลิกใช้ source launcher
สิ่งที่บทนี้ยังไม่ทำ
หัวข้อที่มีชื่อว่า “สิ่งที่บทนี้ยังไม่ทำ”สี่อย่างที่ยังไม่มี และแต่ละอย่างมาถึงเมื่อมีเหตุผลให้ต้องมี
- ไม่มี
packageจึงยังไม่มีโครงไดเรกทอรีให้จำ - ไม่มี build tool จึงยังไม่มี XML ให้อ่าน
- ไม่มีเทสต์ เพราะ source launcher รัน JUnit ร่วมด้วยไม่ได้
- ไม่มี dependency ซึ่งจะกลายเป็นปัญหาทันทีในบทที่ 3 เพราะ Java ไม่มี JSON มาให้
ลองสั่ง java --list-modules ดูก่อนไปต่อ · standard library ของ Java ถูกแบ่งเป็นก้อน
และบนเครื่องที่เขียนบทนี้มี 69 ก้อน · ไม่มีก้อนไหนชื่อเกี่ยวกับ JSON เลยสักก้อน
ซึ่งเป็นเรื่องที่บทที่ 3 ต้องจัดการ
สรุปก่อนไปต่อ
หัวข้อที่มีชื่อว่า “สรุปก่อนไปต่อ”บทนี้วางกรอบและติดตั้ง ยังไม่มี feature ของ
orderctlเกิดขึ้นจริง
คุณรู้แล้วว่า Java เลือกทางตรงข้ามกับ Go ตรงไหนบ้าง เลือกรุ่นเป็น ติดตั้งแล้ว และรันโปรแกรมแรกได้โดยยังไม่ต้องรู้จัก build tool เลย
บทหน้าเราลงของจริง — record และ sealed interface · Java ไม่มี zero value
ให้ตัวแปรเฉพาะที่ ซึ่งเป็นเรื่องที่คนมาจาก Go ต้องปรับความคิดมากที่สุด
แต่มันแลกมาด้วยกลไกที่ทำให้สถานะที่ผิดสร้างไม่ขึ้นตั้งแต่แรก
บทนี้อิงต้นทางที่ลงวันที่กำกับ อ่านต่อได้โดยตรง:
- JEP 512: Compact Source Files and Instance Main Methods (เข้าถึง 2026-08-27) — รูปที่เสร็จสมบูรณ์ที่ Java 25 และวิธีที่ตัวรันเรียก
void main() - JEP 477: Implicitly Declared Classes and Instance Main Methods (เข้าถึง 2026-08-27) — ที่มาของ class
IOครั้งแรกในชื่อjava.io.IOที่ JDK 23 - JEP 511: Module Import Declarations (เข้าถึง 2026-08-27) — อีกหนึ่งฟีเจอร์ที่เสร็จสมบูรณ์ที่ 25
- Oracle Java SE Support Roadmap (เข้าถึง 2026-08-27) — ตารางอายุการดูแลของแต่ละรุ่น
- java command specification (2026-07-21) —
--versionกับ-versionและโหมด source-file - Adoptium available releases API (เข้าถึง 2026-08-27) — LTS ล่าสุดคือ 25
เช็กความเข้าใจ — บทที่ 1
ข้อ 1 / 3`java Hello.java` ทำงานอย่างไร