

โปรแกรมภาษาจาวาประกอบด้วยคลาสจำนวนมากมาย และในบางครั้งโปรแกรมของเราก็ประกอบไปด้วยคลาสจำนวนมากเช่นกัน ดังนั้นในบทนี้จะอธิบายวิธีการรวมกลุ่มคลาสที่มีความสัมพันธ์กันเข้าไว้ด้วยกันในแพ็กเกจเดียวกัน และวิธีการนำคลาสที่ถูกเก็บอยู่ในแพ็กเกจ มาใช้ในโปรแกรมที่กำลังจะสร้างขึ้นใหม่ว่ามีวิธีการอย่างไร

กฎการบันทึกชื่อไฟล์มีอยู่เพียงข้อเดียวเท่านั้น คือ หากประกาศคลาสขึ้น โดยระบุหน้าคลาสเป็น public แล้ว จะต้องบันทึกไฟล์ให้มีชื่อเหมือนกับชื่อคลาสที่ประกาศขึ้นเป็น public ด้วย (ใน 1 ไฟล์จะประกอบด้วยกี่คลาสก็ได้ แต่จะมีคลาส public ได้เพียงคลาสเดียวเท่านั้นต่อ 1 ไฟล์)
ยกตัวอย่างเช่น
คลาสที่มีการประกาศไว้ในไฟล์ |
การตั้งชื่อไฟล์ |
Public class Hello { } |
ต้องบันทึกไฟล์โดยใช้ชื่อ Hello.java |
Public class Employee { } |
ต้องบันทึกไฟล์โดยใช้ชื่อ Employee.java |
Class Motorcycle { } |
จะบันทึกไฟล์โดยใช้ชื่อ Motorcycle.java หรือชื่ออื่นๆ ก็ได้เพราะไม่ได้ระบุ public ไว้หน้าคลาส Motorcycle นี้ |
แต่อย่างไรก็ดี แม้ว่าจะสามารถตั้งชื่อไฟล์เป็นอะไรก็ได้สำหรับกรณีที่คลาสนั้นไม่ได้ถูกประกาศเป็น public แต่ก็เป็นที่นิยมว่าควรตั้งชื่อไฟล์ให้เหมือนกับชื่อคลาส เพราะเมื่อคอมไพล์โปรแกรมและได้ไฟล์ .class ออกมาแล้ว จะได้สามารถอ้างอิงได้โดยง่ายกว่าไฟล์ .class นี้คอมไพล์มาจากไฟล์ .java ไฟล์ใด
ตัวอย่างเช่น ถ้ากำหนดคลาส class MobilePhone { } ขึ้นมา แต่สมมุติว่าบันทึกไฟล์ด้วยชื่อ SayHello.java ทำให้เมื่อคอมไพล์โปรแกรมด้วยคำสั่ง javac SayHello.java แล้วเราจะได้ไฟล์ .class ชื่อ SayHello.class ออกมา ซึ่งหากภายหลังมีไฟล์ .class เพิ่มมากขึ้น และเราต้องการแก้ไขการทำงานของคลาส MobilePhone เราก็คงไม่สามารถจำได้ว่าคลาส MobilePhone นี้เขียนไว้ในไฟล์ .java ไฟล์ใด เพราะชื่อไฟล์ SayHello.java ไม่สอดคล้องกับชื่อคลาสนั่นเอง
แต่ถ้าเราบันทึกไฟล์นี้โดยใช้ชื่อ MobilePhone.java จะทำให้เมื่อคอมไพล์โปรแกรมแล้วจะได้ไฟล์ .class ชื่อ MobilePhone.class ออกมา ซึ่งสอดคล้องกับชื่อคลาส MobilePhone ดังนั้นเมื่อต้องการแก้ไขการทำงานของคลาส MobilePhone ในภายหลัง ก็จะทราบได้ทันทีว่าต้องไปแก้ไขที่ไฟล์ MobilePhone.java

แพ็กเกจ (package) ในภาษาจาวา หมายถึง สิ่งที่ใช้ในการรวบรวมคลาสที่มีความเกี่ยวข้องสัมพันธ์กันไว้ในกลุ่มเดียวกัน เพื่อให้ง่ายต่อการจัดการและง่ายต่อการค้นหาคลาสสำหรับการใช้งานในแต่ละครั้ง
รูปแบบของการประกาศแพ็กเกจ คือ
Package ชื่อแพ็กเกจหลัก,ชื่อแพ็กเกจย่อย;
- แพ็กเกจย่อยจะมีหรือไม่มีก็ได้ และสามารถระบุแพ็กเกจย่อยกี่ชั้นก็ได้ตามแต่ต้องการ
- การประกาศชื่อของแพ็กเกจหลักและชื่อของแพ็กเกจย่อยจะนิยมใช้ตัวอักษรตัวเล็กทั้งหมด
ลักษณะสำคัญของแพ็กเกจ
- การประกาศแพ็กเกจจะต้องประกาศไว้ที่บรรทัดบนสุดเท่านั้น แต่สามารถใส่คอมเมนต์
(จะกล่าวถึงรายละเอียดของคอมเมนต์ในบทที่ 5) หรือช่องว่างไว้ก่อนการประกาศแพ็กเกจได้
- สามารถประกาศแพ็กเกจได้เพียง 1 แพ็กเกจต่อ 1 ซอร์สไฟล์ (ไฟล์ .java) เท่านั้น
- ถ้าไม่สามารถประกาศแพ็กเกจขึ้นแล้ว ไฟล์ .class ที่ได้จากการคอมไพล์จะถูกเก็บไว้ที่ไดเร็คทอรี ปัจจุบันที่ทำงานอยู่

จุดประสงค์ของหัวข้อนี้ต้องการอธิบายให้เข้าใจถึงวิธีการจัดกลุ่มคลาสเป็นหลัก ดังนั้นขอให้ข้ามการทำความเข้าใจใน ส่วนของ การทำงานของโปรแกรมไปก่อน
ตัวอย่างในหัวข้อนี้จะประกอบด้วยซอร์สไฟล์ 4 ไฟล์ คือ Enrollment.java, Withdrawal.java, Points.java, Grade.java โดยแบ่งออกได้เป็น 2 กลุ่มตามลักษณะความสัมพันธ์ของคลาสดังนี้
- กลุ่มของการลงทะเบียน (register) ประกอบด้วย Enrollment.class และ Withdrawal.class ซึ่งเป็นคลาสของการลงทะเบียนเรียนและถอนวิชาเรียน ตามลำดับ
- กลุ่มของการสอบ (exam) ประกอบด้วย Points.class และ Grade.class ซึ่งเป็นคลาสของคะแนนสอบและเกรด ตามลำดับ
ตัวอย่างที่ 2.1 แสดงการจัดกลุ่มคลาสด้วยคำสั่ง package
ให้พิมพ์โค้ดต่อไปนี้ แล้วบันทึกไฟล์เก็บไว้ในไดเร็คทอรี C:\sourcejava
* ไฟล์ Enrollment.java
(เมื่อคอมไพล์จะได้ไฟล์ Enrollment.class เก็บอยู่ในแพ็กเกจ student.register)

* ไฟล์ Withdrawal.java
(เมื่อคอมไพล์จะได้ไฟล์ Withdrawal.class เก็บอยู่ในแพ็กเกจ student.register)

* ไฟล์ Points.java
(เมื่อคอมไพล์จะได้ไฟล์ Points.class เก็บอยู่ในแพ็กเกจ student.exam)

* ไฟล์ Grade.java
(เมื่อคอมไพล์จะได้ไฟล์ Grade.class เก็บอยู่ในแพ็กเกจ student.exam)

จากนั้นให้เปิด command prompt แล้วเข้าไปยังไดเร็คทอรี C:\sourcejava และทำการคอมไพล์ซอร์สไฟล์ทั้ง 4 ไฟล์ข้างต้นพร้อมกันด้วยคำสั่งต่อไปนี้
คำสั่งนี้ หมายถึง การคอมไพล์ไฟล์ทุกไฟล์ที่มีนามสกุลเป็น .java (* หมายถึง ไฟล์ทุกไฟล์) และให้เก็บไฟล์ .class ที่ได้จากการคอมไพล์ไว้ในไดเร็คทอรีปัจจุบัน คือ ไดเร็คทอรี sourcejava (เครื่องหมาย . ที่อยู่หลัง –d หมายถึง ไดเร็คทอรีปัจจุบัน)
Note : |
-d เป็นออปชั่นของ Java compiler (javac.exe) ใช้ระบุเพื่อบอกคอมไพล์เลอร์ว่าเมื่อคอมไพล์ได้ไฟล์ .class ออกมาแล้ว ให้นำไฟล์ .class ไปเก็บไว้ที่ไดเร็คทอรีใด |
เมื่อสั่งคอมไพล์โปรแกรมแล้ว คอมไพเลอร์จะสร้างไดเร็คทอรี register และ exam ขึ้นในไดเร็คทอรีปัจจุบัน (sourcejava) โดยอัตโนมัติ ซึ่งเป็นผลมาจากคำสั่ง package student.register; และ package student.exam; ในซอร์สไฟล์นั่นเอง
จากนั้นคลิกปุ่ม Start > Programs > Accessories > Windows Explorer แล้วเข้าไปที่ C:\sourcejava จะพบลักษณะของไดเร็คทอรี sourcejava เป็นดังนี้

จะเห็นว่าแพ็กเกจมีการทำงานแบบลำดับขั้น (hierarchy) คือ หากสร้างแพ็กเกจ student.exam จะทำให้ไฟล์ .class ที่ได้ถูกเก็บไว้ในไดเร็คทอรี student\exam และหากสร้างแพ็กเกจ student.register จะทำให้ไฟล์ .class ที่ได้ถูกเก็บไว้ในไดเร็คทอรี student\register
Note : |
หากโค้ดในโปรแกรมมีการจัดกลุ่มคลาสด้วยคำสั่ง package แล้ว ในการคอมไพล์โปรแกรมจะต้องระบุออปชั่น –d ให้กับ Java compiler ด้วยเสมอ ไม่เช่นนั้นเมื่อสั่งคอมไพล์โปรแกรมแล้ว คอมไพเลอร์จะไม่สร้างไดเร็คทอรีให้ตามคำสั่งแพ็กเกจที่กำหนด ยกตัวอย่างเช่น หากโค้ดโปรแกรม Enrollment.java กำหนดจัดกลุ่มแพ็คเกจด้วยคำสั่ง package student.register; และโปรแกรมนี้ถูกจัดเก็บอยู่ที่ไดเร็คทอรี C:\sourcejava แล้วสั่งคอมไพล์โปรแกรมดังนี้
- javac Enrollment.java
ผลคือ คอมไพเลอร์จะคอมไพล์โปรแกรม และเก็บไฟล์ Enrollment.class ที่ได้จากการคอมไพล์ไว้ที่ไดเร็คทอรี C:\sourcejava โดยไม่สร้างไดเร็คทอรี student\register ขึ้นภายใต้ไดเร็คทอรี C:\sourcejava
- javac -d.Enrollment.java
ผลคือ คอมไพเลอร์จะคอมไพล์โปรแกรม และเก็บไฟล์ Enrollment.class ที่ได้จากการคอมไพล์ไว้ที่ไดเร็คทอรี C:\sourcejava\student\register |

จากที่ได้กล่าวไปแล้วว่าแพ็กเกจมีประโยชน์ในการจัดกลุ่มคลาสที่มีความสัมพันธ์กันเข้าไว้ด้วยกัน ซึ่งนอกจากประโยชน์ในข้อนี้แล้วแพ็กเกจยังมีประโยชน์อีกข้อหนึ่ง คือ สามารถหลีกเลี่ยงการชนกันของชื่อคลาสได้
ในการพัฒนาโปรแกรมสำหรับงานหนึ่งๆ เป็นไปได้ว่าโปรแกรมเมอร์แต่ละคนต่างก็พัฒนาโปรแกรมของตัวเอง และอาจตั้งชื่อคลาสซ้ำกัน เช่น หากพัฒนาโปรแกรมเกี่ยวกับการเก็บข้อมูลนักเรียน โปรแกรมเมอร์แต่ละคนก็อาจตั้งชื่อคลาสเป็น student เหมือนกันก็เป็นได้ ซึ่งก็จะทำให้เกิดการชนกันของคลาส student เกิดขึ้น
ปัญหาข้างต้นจะถูกแก้ไขได้โดยการนำแพ็กเกจมาใช้ประโยชน์ กล่าวคือ เมื่อนำแพ็กเกจมาใช้ ก็จะช่วยให้สามารถแยกคลาสที่โปรแกรมเมอร์แต่ละคนพัฒนาออกจากกันได้ แม้ว่าโปรแกรมเมอร์จะตั้งชื่อคลาสซ้ำกันก็ตาม ตัวอย่างเช่น
- อาจารย์สั่งให้นักเรียนทุกคนเขียนโปรแกรม โดยกำหนดให้ตั้งชื่อคลาสในโปรแกรมว่า student เหมือนกันทุกคน ผู้อ่านคิดว่าเราควรจะทำอย่างไร เพื่อป้องกันไม่ให้เกิดการชนกันของคลาส student เกิดขึ้น เพราะเมื่ออาจารย์สั่งคอมไพล์โปรแกรมของนักเรียนแต่ละคนแล้ว คลาส student ของนักเรียนแต่ละคนก็จะถูกบันทึกซ้อนทับกันที่ไดเร็คทอรีหนึ่งๆอย่างแน่นอน เนื่องจากชื่อคลาสซ้ำกันนั่นเอง คำตอบสำหรับกรณีนี้ คือ อาจารย์ควรตั้งโจทย์เพิ่มให้กับนักเรียนด้วยว่า นักเรียนทุกคนจะต้องประกาศแพ็กเกจด้วยชื่อนามสกุลของตัวเองไว้ที่บรรทัดแรกของโปรแกรม เช่น
* นางสาวสมศรี ใจดี : ต้องกำหนด package jaidee.somsri ; ไว้ที่บรรทัดแรกของโปรแกรม เพราะ เมื่อคอมไพล์โปรแกรมแล้วจะทำให้คลาส student ของนางสาวสมศรีถูกเก็บอยู่ที่ไดเร็คทอรี jaidee\somsri
* นางสาวสมศรี รักธรรม : ต้องกำหนด package raktham.somsri ; ไว้ที่บรรทัดแรกของโปรแกรม เพราะ เมื่อคอมไพล์โปรแกรมแล้วจะทำให้คลาส student ของนางสาวสมศรีถูกเก็บอยู่ที่ไดเร็คทอรีraktham\somsri
จากตัวอย่างข้างต้นเมื่อนำแพ็กเกจมาใช้ โดยตั้งชื่อแพ็กเกจด้วยชื่อและนามสกุลของนักเรียนแต่ละคน ซึ่งไม่มีทางซ้ำกันอย่างแน่นอน ก็จะทำให้คลาส student ของนักเรียนแต่ละคนจะถูกเก็บไว้ที่คนละไดเร็คทอรี ดังนั้นแม้ว่าจะตั้งชื่อคลาสซ้ำกันก็จะไม่เป็นปัญหา
Note : |
การตั้งชื่อแพ็กเกจ ควรเลือกใช้ชื่อที่เฉพาะเจาะจง ไม่ซ้ำกับใคร เพื่อที่จะได้สามารถหลีกเลี่ยงปัญหาการชนกันของคลาสไม่ให้เกิดขึ้นได้ |
- เจ้าของกิจการรายหนึ่งจ้างโปรแกรมเมอร์คนหนึ่งให้เขียนโปรแกรมให้กับบริษัทในเครือ ซึ่งมีทั้งหมด 3 บริษัท ได้แก่
* http://www.thaifood.com
* http://www.japanesefood.com
* http://www.italianfood.com
โดยเจ้าของกิจการกำหนดว่าคลาสที่พัฒนาขึ้นสำหรับทั้ง 3 บริษัทนี้ จะต้องตั้งชื่อคลาสว่า Food เท่านั้น ผู้อ่านคิดว่าโปรแกรมเมอร์จะต้องทำอย่างไรเพื่อแยกคลาส Food ของทั้ง 3 บริษัทนี้ออกจากกัน คำตอบคือ โปรแกรมเมอร์จะต้องประกาศแพ็กเกจด้วยชื่อโดเมนเนมไว้ที่บรรทัดแรกของโปรแกรม เนื่องจากโดเมนเนมเป็นชื่อที่เฉพาะเจาะจง ไม่สามารถตั้งซ้ำกันได้ เช่น
* สำหรับ http://www.thaifood.com ให้โปรแกรมเมอร์ประกาศแพ็กเกจเป็น com.thaifood
* สำหรับ http://www.japanesefood.com ให้โปรแกรมเมอร์ประกาศแพ็กเกจเป็น com. Japanesefood
* สำหรับ http://www.italianfood.com ให้โปรแกรมเมอร์ประกาศแพ็กเกจเป็น com. Italianfood

หากต้องการนำคลาสที่มีอยู่แล้วมาใช้ในโปรแกรมที่กำลังจะสร้างขึ้นใหม่ เราจะต้องใช้คำสั่ง import เพื่อบอกให้คอมไพเลอร์ทราบว่าจะสามารถหาคลาสที่เราต้องการใช้งานได้จากในแพ็กเกจใด โดยต้องใช้คำสั่ง import ก่อนการประกาศคลาสเสมอ
รูปแบบของการใช้คำสั่ง import คือ
import ชื่อแพ็กเกจหลัก.ชื่อแพ็กเกจย่อย.ชื่อคลาสที่ต้องการใช้งาน; |
การระบุชื่อคลาสที่ต้องการใช้งานนั้น จะระบุชื่อคลาสลงไปตรงๆหรือระบุเป็น * ซึ่งหมายถึงคลาสทุกคลาสก็ได้
การระบุชื่อคลาสใดคลาสหนึ่งกับการระบุ * ซึ่งหมายถึงทุกคลาสจะไม่มีผลต่างกัน เพราะการระบุ * ไม่ได้หมายความว่าคลาสจะถูกโหลดเข้าสู่หน่วยความจำให้สิ้นเปลือง เพราะความจริงแล้วจะมีแต่เฉพาะคลาสที่ถูกเรียกใช้งานเท่านั้น ที่จะถูกโหลดเข้าสู่หน่วยความจำ

วิธีการเรียกใช้งานคลาสที่เก็บอยู่ในแพ็กเกจสามารถทำได้ 2 วิธี คือ
- ระบุด้วยพาธ (path) เต็มๆว่าคลาสนั้นถูกเก็บอยู่ที่ใด
- ทำการอิมพอร์ตคลาสนั้นเข้ามาในโปรแกรมโดยใช้คำสั่ง import
ตัวอย่างที่ 2.2 แสดงการเรียกใช้งานคลาสที่เก็บอยู่ในแพ็กเกจหนึ่งๆ โดยระบุพาธเต็มของคลาสนั้น
ให้เขียนโค้ดต่อไปนี้ แล้วบันทึกเป็นไฟล์ชื่อ Student1.java โดยเก็บไว้ที่ C:\sourcejava
* ไฟล์ Student1.java

วิธีคอมไพล์โปรแกรม รันโปรแกรม และผลลัพธ์ของโปรแกรม
ก่อนเริ่มต้นคอมไพล์และรันโปรแกรม จำเป็นต้องเซ็ตตัวแปรระบบที่ชื่อว่า classpath ก่อน โดยกำหนดค่าเป็น . (หมายถึงไดเร็คทอรีปัจจุบัน) เพื่อแจ้งให้คอมไพเลอร์ทราบว่าหากคอมไพเลอร์ค้นหาคลาสที่มีการเรียกใช้งานในโปรแกรมไม่พบ (ในที่นี้คือคลาส Grade) ก็ให้คอมไพเลอร์มาค้นหาที่ไดเร็คทอรีปัจจุบันที่ทำงานอยู่ (current working directory) ในที่นี้คือไดเร็คทอรี C:\sourcejava นั่นเอง
จากนั้นสั่งคอมไพล์และสั่งรันโปรแกรม จะมีข้อความ “Grade” แสดงออกทางจอภาพ ซึ่งเป็นผลมาจากการทำงานของเมธอด calGrade ในคลาส Grade นั่นเอง (สามารถดูรายละเอียดการทำงานของคลาส Grade )

อธิบายโปรแกรม
บรรทัดที่ 3 เนื่องจาก Student1.java ถูกเก็บไว้ที่ C:\sourcejava แต่คลาส Grade ที่เราต้องการใช้งานถูก เก็บอยู่ที่ C:\sourcejava\student\exam ดังนั้น การเรียกใช้งานคลาส Grade จึงใช้วิธีระบุพาธ เต็มของคลาส Grade คือ student.exam.Grade ลงไป เพื่อให้คอมไพเลอร์สามารถหาคลาส Grade ที่เราต้องการใช้งานนั้นถูกเก็บอยู่ที่แพ็กเกจ student.exam
บรรทัดที่ 4 เรียกเมธอด calGrade ของคลาส Grade ให้ทำงาน (รูปแบบการเรียกใช้งานเมธอดจะอธิบาย อีกครั้งในบทที่ 7 เรื่อง “คลาสและออบเจ็ค”)
ตัวอย่างที่ 2.3 แสดงการเรียกใช้งานคลาสที่เก็บอยู่ในแพ็กเกจหนึ่งๆ โดยใช้คำสั่ง import.ชื่อแพ็กเกจหลัก.ชื่อแพ็กเกจย่อย.ชื่อคลาสที่ต้องการใช้งาน
ให้เขียนโค้ดต่อไปนี้ แล้วบันทึกเป็นไฟล์ชื่อ Student2.java โดยเก็บไว้ที่ C:\sourcejava
* ไฟล์ Student2.java

วิธีคอมไพล์โปรแกรม รันโปรแกรม และผลลัพธ์ของโปรแกรม

จะพบว่าผลลัพธ์ที่ได้เหมือนกับตัวอย่างที่ 2.2 ซึ่งระบุพาธเต็มๆของคลาสที่ต้องการใช้งาน
อธิบายโปรแกรม
บรรทัดที่ 1 ทำการอิมพอร์ตคลาส Grade ซึ่งเก็บอยู่ในแพ็กเกจ student.exam เข้ามาในโปรแกรม
บรรทัดที่ 4 บรรทัดนี้เรียกใช้งานคลาส Grade โดยระบุเพียงแค่ชื่อคลาสเท่านั้น ไม่ได้ระบุพาธเต็ม
ของคลาส (แต่ถ้าระบุก็ไม่ผิด แต่ไม่จำเป็น) เพราะบรรทัดที่ 1 บอกให้คอมไพเลอร์ทราบ
แล้วว่าคลาส Grade นี้อยู่ในแพ็กเกจ student.exam
ถ้าหากจะถามว่าระหว่างตัวอย่างที่ 2.2 กับ 2.3 นั้นแบบไหนดีกว่ากัน คำตอบก็คือ ตัวอย่างที่ 2.3 ดีกว่า เพราะทำการอิมพอร์ตคลาสเพียงครั้งเดียวตอนต้นของโปรแกรม แล้วหลังจากนั้นก็สามารถเรียกใช้งานคลาสได้โดยระบุเพียงชื่อคลาส ไม่ต้องระบุพาธเต็มของคลาสให้ยุ่งยากอีก
ตัวอย่างที่ 2.4 แสดงการเรียกใช้งานคลาสที่เก็บอยู่ในแพ็กเกจหนึ่งๆ โดยใช้คำสั่ง import.ชื่อแพ็กเกจหลัก.
ชื่อแพ็กเกจย่อย.*;
จากตัวอย่างที่ 2.3 ให้แก้ไขโค้ดในบรรทัดที่ 1 ของไฟล์ Student2.java จาก
Import student.exam.Grade; |
ไปเป็น
จากนั้นสั่งคอมไพล์โปรแกรม
วิธีคอมไพล์โปรแกรม

จะพบว่าเกิดข้อผิดพลาดขึ้นกับการคอมไพล์โปรแกรม ที่เป็นเช่นนี้เพราะ ณ ตอนนี้คลาส Grade (Grade.class) ถูกเก็บไว้ที่ไดเร็คทอรี C:\sourcejava\student\exam แต่ขณะนี้ไฟล์ Grade.java ถูกเก็บอยู่ที่ไดเร็คทอรี C:\sourcejava
ดังนั้น เมื่อเราคอมไพล์โปรแกรม Student2.java ซึ่งมีการเรียกใช้งานคลาส Grade แทนที่คอมไพเลอร์จะไปดึงคลาส Grade จากไดเร็คทอรี C:\sourcejava\student\exam มันกลับไปดึงคลาส Grade จากไดเร็คทอรีปัจจุบัน คือ C:\sourcejava แทน แต่ขณะนี้คลาส Grade ไม่ได้ถูกเก็บอยู่ที่ไดเร็คทอรีนี้ ดังนั้นจึงทำให้เกิดข้อผิดพลาดดังกล่าวนี้
สำหรับสาเหตุที่คอมไพเลอร์ไปหาไฟล์ Grade.class จากไดเร็คทอรี C:\sourcejava นั้น เป็นเพราะมีไฟล์ Grade.java ถูกเก็บอยู่ที่ไดเร็คทอรี C:\sourcejava ดังนั้นคอมไพเลอร์จึงพยายามหาไฟล์ Grade.class ในไดเร็คทอรีเดียวกันกับไฟล์ Grade.java ถูกเก็บอยู่ ซึ่งก็คือ ไดเร็คทอรี C:\sourcejava นั่นเอง
ลองกลับไปพิจารณาข้อผิดพลาดอีกครั้ง จะพบว่าคอมไพเลอร์ฟ้องว่า “Please remove or make sure it appears in the correct subdirectory of the classpath.” นั่นหมายถึง คอมไพเลอร์แจ้งให้เราย้ายไฟล์ไปไว้ยังไดเร็คทอรีที่เหมาะสม
ดังนั้น คำตอบของปัญหานี้ คือ หากต้องการ import student.exam.*; แทนการ import student.exam. Grade; ก็จะต้องย้ายไฟล์ Grade.java จากไดเร็คทอรี C:\sourcejava ไปไว้ยังไดเร็คทอรีเดียวกันกับที่ไฟล์ Grade.class ถูกเก็บอยู่คือ ย้ายไปไว้ที่ไดเร็คทอรี C:\sourcejava\student\exam นั่นเอง ทั้งนี้ก็เพื่อให้ไฟล์ Grade.java และ Grade.class อยู่ในไดเร็คทอรีเดียวกัน คอมไพเลอร์จะได้สามารถหาไฟล์ Grade.class จากไดเร็คทอรีที่ถูกต้องได้
เมื่อผู้อ่านย้ายไฟล์ Grade.java ไปไว้ยังไดเร็คทอรี C:\sourcejava\student\exam เรียบร้อยแล้ว ให้ทดลองคอมไพล์โปรแกรมใหม่อีกครั้ง

จะพบว่าในครั้งนี้สามารถคอมไพล์และรันโปรแกรมได้ผลลัพธ์ที่ถูกต้อง ดังเช่นตัวอย่างที่ 2.3
ตัวอย่างที่ 2.5 แสดงการเรียกใช้งานคลาสที่เก็บอยู่ในแพ็กเกจอื่นๆ โดยใช้คำสั่ง import ทำการอิมพอร์ต คลาสเหล่านั้นเข้ามาในโปรแกรม
จากตัวอย่างที่ 2.3 ไฟล์ Student2.class ถูกเก็บอยู่ที่ไดเร็คทอรี C:\sourcejava แต่ในตัวอย่างนี้เราจะเก็บไฟล์ .class ไว้ที่ไดเร็คทอรี C:\sourcejava\student\ register แล้วมาดูกันว่าจากไดเร็คทอรีปัจจุบันที่ทำงานอยู่ คือ C:\sourcejava เราจะสามารถเรียกรันโปรแกรมที่เก็บอยู่ในไดเร็คทอรี C:\sourcejava\student\ register ได้อย่างไร
ให้เขียนโค้ดต่อไปนี้ แล้วบันทึกเป็นไฟล์ชื่อ TestPackage.java โดยเก็บไว้ที่ C:\sourcejava
* ไฟล์ TestPackage.java

วิธีคอมไพล์โปรแกรม รันโปรแกรม และผลลัพธ์ของโปรแกรม

โดยปกติหากไฟล์ TestPackage.java ไม่ได้มีการประกาศแพ็กเกจไว้ด้วยคำสั่ง package student. Register; แล้ว เมื่อสั่งคอมไพล์โปรแกรมด้วยคำสั่ง javac –d . TestPackage.java คอมไพเลอร์ก็จะคอมไพล์ TestPackage.java และเก็บไฟล์ .class ที่ได้จากการคอมไพล์ไว้ในไดเร็คทอรีปัจจุบัน คือ ไดเร็คทอรี C:\sourcejava
แต่เนื่องจาก TestPackage.java มีการประกาศแพ็กเกจด้วยคำสั่ง package student.Register; ดังนั้นจึงทำให้ไฟล์ TestPackage.class ที่ได้จากการคอมไพล์ ถูกเก็บอยู่ที่ไดเร็คทอรี C:\sourcejava\student\ register
เมื่อไฟล์ TestPackage.class ถูกเก็บอยู่ในแพ็กเกจ student.Register ซึ่งก็คือไดเร็คทอรี student\ Register และไดเร็คทอรีนี้ก็อยู่ภายใต้ไดเร็คทอรี C:\sourcejava ดังนั้นในการรันโปรแกรมจึงจำเป็นต้องระบุออปชั่นของ Java interpreter คือ –classpath ให้ชี้ไปที่ไดเร็คทอรีปัจจุบัน คือ C:\sourcejava ด้วย จะได้สามารถหาคลาส TestPackage จากไดเร็คทอรี C:\sourcejava\student\register ได้พบ
อธิบายโปรแกรม
บรรทัดที่ 1 กำหนดให้นำ TestPackage.class (ไฟล์ .class ที่ได้จากการคอมไพล์โปรแกรมนี้) ไปเก็บไว้ในแพ็กเกจ student.Register (คือไดเร็คทอรี student\Register) โดยเมื่อทำเช่นนี้แล้วเราจะสามารถเรียกใช้งานคลาสอื่นๆที่เก็บอยู่ในแพ็กเกจ student.Register ได้ทั้งหมด โดยไม่ต้องใช้คำสั่ง import เพื่ออิมพอร์ตแพ็กเกจนี้เข้ามาอีก
บรรทัดที่ 2 ให้ทำการอิมพอร์ตคลาสทุกคลาสที่เก็บอยู่ในแพ็กเกจ student.exam เข้ามาในโปรแกรม
บรรทัดที่ 6-7 แสดงให้เห็นว่า เราสามารถเรียกใช้งานคลาส Enrollment และคลาส Withdrawal ที่อยู่ในแพ็กเกจ student.Register ได้ ซึ่งเป็นผลมาจากบรรทัดที่ 1 (ดูคำอธิบายของบรรทัดที่ 1)
บรรทัดที่ 8-9 แสดงให้เห็นว่า เราสามารถเรียกใช้งานคลาส Grade และคลาส Points ที่อยู่ในแพ็กเกจ student.exam ได้ ซึ่งเป็นผลมาจากบรรทัดที่ 2 ที่ระบุให้ทำการอิมพอร์ตคลาสทุกคลาส ในแพ็กเกจ student.exam เข้ามาในโปรแกรม

จากหัวข้อที่ผ่านมาเราทำการเซ็ตตัวแปร classpath โดยพิมพ์คำสั่ง set classpath=. ลงไปตรงๆที่ command prompt ซึ่งทุกครั้งที่ทำการคอมไพล์โปรแกรม เมื่อเปิด command prompt ใหม่ขึ้นมา เราก็จะต้องใช้คำสั่งนี้ก่อนทุกครั้ง ซึ่งเป็นการเสียเวลา ดังนั้นหัวข้อนี้จะมาศึกษาวิธีการเซ็ตตัวแปรระบบที่ชื่อว่า classpath แบบถาวร
หลักการในการเซ็ตตัวแปร classpath ก็เหมือนกับการเซ็ตตัวแปร path ที่ได้เคยกล่าวไปแล้วในบทที่ 2 ดังนั้น ให้กลับไปดูบทที่ 2 ในหัวข้อ “การเซ็ต environment เพื่อความสะดวกในการเรียกใช้ตัวแปลภาษาจาวา” แล้วทำตามขั้นตอนที่อธิบายไว้ทุกประการ เพียงแต่เปลี่ยนจากตัวแปรชื่อ path มาเป็นตัวแปร classpath และให้กำหนดค่าของตัวแปรเป็น . (จุด) ดังรูป


จาวาเป็นภาษาโปรแกรมเชิงวัตถุ (Object Oriented Language) จึงทำงานด้วยหลักการของ Object Oriented ดังนั้นในบทนี้จะอธิบายหลักการพื้นฐานของ Object Oriented ให้ทราบ ขอให้ผู้อ่านทำความเข้าใจกับเนื้อหาในบทนี้โดยละเอียด เพราะจะเป็นแนวคิดสำคัญที่ช่วยให้ผู้อ่านสามารถศึกษาการเขียนโปรแกรมภาษาจาวาได้โดยง่าย นอกจากนี้ในบทนี้จะกล่าวถึงความโดดเด่นของภาษาจาวาที่มีอยู่เหนือกว่าภาษาอื่นให้ทราบด้วย

จาวาเป็นภาษาที่ทำงานด้วยหลักการพื้นฐานของ Object Oriented ซึ่งแบ่งออกได้เป็น 3 ประเภท ดังนี้
Encapsulation (การหุ้มห่อ)
เป็นกระบวนการซ่อนรายละเอียดการทำงานและข้อมูลไว้ภายใน ไม่ให้ภายนอกสามารถมองเห็นได้เรียกว่า “Information hiding” และเมื่อภายนอกมองไม่เห็นสิ่งที่ถูกซ่อนไว้ภายในแล้ว ก็จะไม่สามารถทำการเปลี่ยนแปลง แก้ไข หรือสร้างความเสียหายให้กับสิ่งต่างๆที่อยู่ภายในได้
หากจะเปรียบเทียบหลักการของ encapsulation แล้ว ก็เหมือนกับการซ่อนกระบวนการทำงานและข้อมูลไว้หลังกำแพง ซึ่งสิ่งที่อยู่ด้านนอกของกำแพงจะไม่สามารถเปลี่ยนแปลงกระบวนการทำงานหรือเข้าถึงข้อมูลที่อยู่ด้านหลังกำแพงได้
ข้อดีของ encapsulation คือ สามารถสร้างความปลอดภัยให้กับข้อมูลได้ เนื่องจากข้อมูลจะถูกเข้าถึงได้จากผู้ที่มีสิทธิ์เท่านั้น


Inheritance (การสืบทอดคุณสมบัติ)
ถ้าหากสิ่งหนึ่งมีลักษณะคล้ายกับอีกสิ่งหนึ่งมากๆ จะมีส่วนที่แตกต่างกันก็เพียงเล็กน้อยเท่านั้น เราก็ไม่จำเป็นต้องสร้างสิ่งนั้นขึ้นมาใหม่ทั้งหมด แต่สามารถนำหลักการของ Object Oriented ที่เรียกว่าInheritance มาใช้ได้
หลักการของ Inheritance คือ ทำการสร้างสิ่งใหม่ขึ้นด้วยการสืบทอดหรือรับเอา (Inherit) คุณสมบัติบางอย่างมาจากสิ่งเดิมที่มีอยู่แล้ว หรือกล่าวคือ ทำการสร้างเพิ่มเติมจากสิ่งที่มีอยู่ได้เลย
ข้อดีของ Inheritance คือ จากการที่สามารถนำสิ่งที่เคยสร้างขึ้นแล้วกลับมาใช้ใหม่ (re-use) ได้ ทำให้ช่วยประหยัดเวลาการทำงานลงไปได้มาก เนื่องจากไม่ต้องเสียเวลาพัฒนาใหม่ทั้งหมด


Polymorphism (การพ้องรูป)
เพื่อให้เข้าใจถึงหลักการ Polymorphism ได้โดยง่าย จึงขอยกตัวอย่างขึ้นมาสักเล็กน้อย ให้ได้เห็นภาพกันสมมุติว่ามีเมธอด ดังนี้
public void calculate(int x) {}
public void calculate(double y) {} |
จะเห็นว่า 2 เมธอดนี้ชื่อ calculate เหมือนกัน แต่มีการรับค่าเข้ามาภายในเมธอดที่แตกต่างกัน โดยเมธอดหนึ่งรับค่าเป็นเลขจำนวนเต็ม (int) ส่วนอีกเมธอดหนึ่งรับค่าเป็นเลขจำนวนจริง ซึ่งสามารถมีส่วนของทศนิยมได้ (double) ซึ่งการที่เมธอดชื่อหนึ่งสามารถรับค่าเข้ามาได้ถึง 2 รูปแบบหรือมากกว่านี่เอง ที่กล่าวได้ส่าเมธอดนำหลักการ Polymorphism มาใช้
การที่เมธอดชื่อเดียวกันสามารถรับอาร์กิวเมนต์ที่แตกต่างกันได้หลายรูปแบบนี้ เรามีศัพท์เรียกเฉพาะว่า เมธอดชื่อนั้นถูกโอเวอร์โหลด (overload)
หากจะถามว่าแล้วเป็นประโยชน์อะไรที่ได้จากการทำแบบนี้ ลองคิดดูง่ายๆ สมมุติว่าเราใช้เมธอด calculate ในการคำนวณอะไรสักอย่าง เราสามารถใช้ประโยชน์จาก Polymorphism ได้ อย่างเช่น หากผู้ใช้งานโปรแกรมส่งข้อมูลเข้ามาเป็นเลขจำนวนเต็ม เนื่องจากไม่ต้องการผลการคำนวณที่ละเอียดมากนัก เราก็สามารถเรียกเมธอด public void calculate(int x) ให้ทำงานได้
แต่หากผู้ใช้งานโปรแกรมส่งข้อมูลเข้ามาเป็นเลขจำนวนจริง เราก็สามารถเรียกเมธอดอีกหนึ่งตัว คือ public void calculate(double y) ที่ทำการคำนวณได้ละเอียดกว่าให้ทำงานได้
ทั้งนี้และทั้งนั้นขึ้นอยู่กับความต้องการในขณะหนึ่งๆว่าต้องการให้เมธอด calculate ทำงานในรูปแบบใด
สรุปแล้วข้อดีของ Polymorphism ก็คือ การทำให้สิ่งหนึ่งสามารถทำงานได้หลากหลายรูปแบบ ตามความต้องการที่เกิดขึ้นในขณะหนึ่งๆ

simple
จาวามีลักษณะคล้ายกับภาษา C++ ค่อนข้างมาก แต่จาวาจะตัดส่วนที่ใช้งานได้ค่อนข้างยากของภาษา C++ ออกไป อย่างเช่น เรื่องของพอยเตอร์,สตรัคเจอร์,ยูเนียน,การใช้ header file, การจองหน่วยความจำ (memoryallocation) เป็นต้น ทำให้จาวาเป็นภาษาที่ง่ายต่อการใช้งาน
Object-Oriented
จาวาถูกออกแบบมาให้เป็น Object Oriented programming (OOP) ทำงานโดยอาศัยหลักการของ Object Oriented ซึ่งมีหลายสิ่งที่โดดเด่นและน่าสนใจ ดังที่ได้กล่าวไปแล้วข้างต้น
robust
จาวามีคุณสมบัติที่ช่วยให้โปรแกรมที่เขียนขึ้นด้วยภาษาจาวามีความคงทน (robust) ไม่เกิดความผิดปกติขึ้นได้โดยง่าย คุณสมบัติที่ว่านี้ หากจะยกตัวอย่างก็คงเป็นในเรื่องของการที่ภาษาจาวามี garbage collection เป็นตัวช่วยจัดการหน่วยความจำให้กับเราโดยอัตโนมัติ จึงช่วยลดข้อผิดพลาดที่อาจเกิดขึ้น จากการที่ผู้เขียนโปรแกรมทำการจองพื้นที่ หน่วยความจำไว้ แล้วลืมคืนหน่วยความจำนั้นให้กับ ระบบจนเกิดสภาวะที่เรียกกันว่า memory leak (หน่วยความจำรั่ว) ขึ้นได้ เป็นต้น
Note : |
garbage collection ของจาวาจะคอยตรวจสอบการใช้งานหน่วยความจำอยู่เป็นระยะๆ โดยหากพบว่าหน่วยความจำส่วนใดถูกจองไว้ แต่ไม่มีการใช้งานแล้ว ก็จะคืนหน่วยความจำส่วนนั้นให้กับระบบ ซึ่งการที่ผู้เขียนโปรแกรมจองหน่วยความจำไว้ แต่ลืมคืนให้กับระบบหลังจากใช้งานเสร็จแล้วนั้น จัดว่าเป็นปัญหาหนึ่งของภาษา C/C++ และภาษาโปรแกรมอีกหลายๆภาษาเลยทีเดียว |
สำหรับเรื่องอื่นๆที่ทำให้โปรแกรมที่เขียนด้วยภาษาจาวามีความคงทน ก็เช่น ทุกครั้งที่คอมไพเลอร์ทำงาน มันจะตรวจสอบในขั้นต้นก่อนว่ามีข้อผิดพลาดใดเกิดขึ้นหรือไม่ ซึ่งตรงนี้เองที่ช่วยเพิ่มความน่าเชื่อถือให้กับโปรแกรมได้มาก เป็นหลักประกันว่าโปรแกรมจะไม่เกิดข้อผิดพลาดขึ้นด้วยเรื่องเล็กๆน้อยๆ
secure
จาวาถูกออกแบบมาให้มีระบบรักษาความปลอดภัยเป็นอย่างดี ยกตัวอย่างเช่น กรณีที่ผู้ใช้งานเรียกรันจาวาแอพเพลต ผู้ใช้สามารถมั่นใจได้ว่าจะไม่มีไวรัสผูกติดมากับแอพเพลตนั้นอย่างแน่นอน เพราะแอพเพลตถูกออกแบบมาให้รันอยู่บน sandbox ซึ่งเป็นเกราะป้องกันเครื่องของเราจากสิ่งแปลกปลอมที่แอบแฝงมาได้เป็นอย่างดี
อีกทั้งในภาษาจาวาก็ไม่มีเรื่องของพอยเตอร์ (pointer) เข้ามาเกี่ยงข้อง ดังนั้นการที่ผู้ไม่ประสงค์ดีจะเข้ามาจองพื้นที่หน่วยความจำในระบบของเราแล้วแอบลักลอบเข้ามาทำร้ายระบบ จึงย่อมไม่สามารถทำได้เพราะพื้นที่หน่วยความจำบนระบบจะถูกเข้าถึงได้จากผู้ที่มีสิทธิ์เท่านั้น
architecturally neutral
ตัวแปลภาษาจาวาถูกออกแบบมาให้เป็นกลาง ไม่ยึดติดกับแพลตฟอร์มหนึ่ง ซึ่งตรงนี้เองที่ช่วยให้ผู้พัฒนาโปรแกรม สามารถพัฒนาโปรแกรมเพียงแค่ครั้งเดียว แต่นำโปรแกรมที่คอมไพล์แล้ว(ซึ่งอยู่ในรูปของ bytecode ในไฟล์ .class) ไปเรียกรันได้ทุกที่ ไม่ว่าที่นั่นจะมีแพลตฟอร์มเป็นชนิดใดก็ตาม
portable
โปรแกรมที่เขียนขึ้นด้วยภาษาจาวาสามารถ นำไปคอมไพล์บนระบบปฏิบัติการที่แตกต่างกัน ได้โดยที่ผู้พัฒนาไม่ต้องปรับปรุง แก้ไขโค้ดโปรแกรมแต่อย่างใด
Multi-thread
จาวาสนับสนุนการทำงานหลายๆ อย่างไปพร้อมกันในเวลาเดียวกัน โดยในโปรแกรมภาษาจาวา 1 โปรแกรมสามารถมรเธรด (thread- หน่วยการทำงานของโปรแกรม) ได้หลายตัว ซึ่งเธรดจะเป็นตัวช่วยให้โปรแกรม สามารถจัดการงานต่างๆไปพร้อมกันได้ในเวลาเดียวกัน ยกตัวอย่างเช่น ในขณะที่เธรดหนึ่งกำลังรับข้อมูลอินพุดเข้ามาในโปรแกรม อีกเธรดหนึ่งก็อาจจะกำลังวาดรูปภาพกราฟฟิกลงบนหน้าจอเพื่อแสดงต่อผู้ใช้งานโปรแกรมก็เป็นได้
|