เราได้อุทิศบทเรียนแยกต่างหากสำหรับความแตกต่างระหว่างคลาสนามธรรมและอินเทอร์เฟซ เนื่องจากหัวข้อนี้สำคัญมาก คุณจะถูกถามเกี่ยวกับความแตกต่างระหว่างแนวคิดเหล่านี้ใน 90% ของการสัมภาษณ์ในอนาคต นั่นหมายความว่าคุณควรแน่ใจว่าคุณเข้าใจสิ่งที่คุณกำลังอ่านอยู่ และถ้าคุณไม่เข้าใจบางอย่าง ให้อ่านแหล่งข้อมูลเพิ่มเติม ดังนั้นเราจึงรู้ว่าคลาสนามธรรมคืออะไรและอินเทอร์เฟซคืออะไร ตอนนี้เราจะพูดถึงความแตกต่างของพวกเขา
-
อินเทอร์เฟซอธิบายพฤติกรรมเท่านั้น มันไม่มีสถานะ แต่คลาสนามธรรมรวมถึงสถานะ: มันอธิบายทั้งสองอย่าง
ตัวอย่างเช่น ใช้
BirdคลาสนามธรรมและCanFlyอินเทอร์เฟซ:public abstract class Bird { private String species; private int age; public abstract void fly(); public String getSpecies() { return species; } public void setSpecies(String species) { this.species = species; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }มาสร้าง
MockingJayคลาสนกและสืบทอดกันBird:public class MockingJay extends Bird { @Override public void fly() { System.out.println("Fly, bird!"); } public static void main(String[] args) { MockingJay someBird = new MockingJay(); someBird.setAge(19); System.out.println(someBird.getAge()); } }อย่างที่คุณเห็น เราสามารถเข้าถึงสถานะของคลาสนามธรรมได้อย่างง่ายดาย — มัน
speciesและageตัวแปรแต่ถ้าเราพยายามทำเช่นเดียวกันกับอินเทอร์เฟซ ภาพจะแตกต่างออกไป เราสามารถลองเพิ่มตัวแปรเข้าไปได้:
public interface CanFly { String species = new String(); int age = 10; public void fly(); } public interface CanFly { private String species = new String(); // Error private int age = 10; // Another error public void fly(); }เราไม่สามารถประกาศ ตัวแปร ส่วนตัวภายในอินเทอร์เฟซได้ ทำไม เนื่องจาก ตัวดัดแปลง ส่วนตัวถูกสร้างขึ้นเพื่อซ่อนการใช้งานจากผู้ใช้ และอินเทอร์เฟซไม่มีการใช้งานอยู่ภายใน: ไม่มีอะไรต้องซ่อน
อินเทอร์เฟซอธิบายพฤติกรรมเท่านั้น ดังนั้นเราจึงไม่สามารถใช้ getters และ setters ภายในอินเทอร์เฟซได้ นี่คือธรรมชาติของอินเทอร์เฟซ: จำเป็นสำหรับการทำงานกับพฤติกรรม ไม่ใช่สถานะ
Java 8 แนะนำวิธีการเริ่มต้นสำหรับอินเทอร์เฟซที่มีการนำไปใช้งาน คุณรู้เกี่ยวกับพวกเขาแล้ว ดังนั้นเราจะไม่พูดซ้ำ
-
คลาสนามธรรมเชื่อมต่อและรวมคลาสที่เกี่ยวข้องอย่างใกล้ชิด ในเวลาเดียวกัน อินเทอร์เฟซเดียวสามารถนำไปใช้กับคลาสที่ไม่มีอะไรเหมือนกันเลย
กลับไปที่ตัวอย่างของเรากับนก
คลาสนามธรรม ของเรา
Birdจำเป็นสำหรับการสร้างนกที่มีพื้นฐานมาจากคลาสนั้น แค่นกและไม่มีอะไรอื่น! แน่นอนว่าจะมีนกชนิดต่างๆ
ด้วย
CanFlyอินเทอร์เฟซ ทุกๆ คนดำเนินไปในแบบของตัวเอง มันอธิบายพฤติกรรม (การบิน) ที่เกี่ยวข้องกับชื่อของมันเท่านั้น สิ่งที่ไม่เกี่ยวข้องมากมาย 'บินได้'
หน่วยงานทั้ง 4 นี้ไม่เกี่ยวข้องกัน พวกเขาไม่ได้มีชีวิตทั้งหมดด้วยซ้ำ อย่างไรก็ตาม พวกเขา
CanFlyทั้งหมดเราไม่สามารถอธิบายโดยใช้คลาสนามธรรมได้ พวกเขาไม่ได้แบ่งปันสถานะเดียวกันหรือฟิลด์ที่เหมือนกัน ในการระบุเครื่องบิน เราอาจต้องการฟิลด์สำหรับรุ่น ปีที่ผลิต และจำนวนผู้โดยสารสูงสุด สำหรับคาร์ลสัน เราต้องการทุ่งสำหรับขนมทั้งหมดที่เขากินในวันนี้ และรายชื่อเกมที่เขาจะเล่นกับน้องชายคนเล็กของเขา สำหรับยุง ...เอ่อ... ฉันไม่รู้ด้วยซ้ำว่า... อาจจะเป็น 'ระดับความรำคาญ'? :)
ประเด็นคือเราไม่สามารถใช้คลาสนามธรรมเพื่ออธิบายได้ พวกเขาแตกต่างกันเกินไป แต่พวกมันมีพฤติกรรมร่วมกัน: พวกมันบินได้ อินเทอร์เฟซเหมาะสำหรับการอธิบายทุกสิ่งในโลกที่สามารถบิน ว่ายน้ำ กระโดด หรือแสดงพฤติกรรมอื่นๆ ได้
-
คลาสสามารถใช้อินเทอร์เฟซได้มากเท่าที่คุณต้องการ แต่สามารถสืบทอดได้เพียงคลาสเดียวเท่านั้น
เราได้กล่าวถึงเรื่องนี้มากกว่าหนึ่งครั้งแล้ว Java ไม่มีการสืบทอดหลายคลาส แต่รองรับการสืบทอดหลายอินเตอร์เฟส ประเด็นนี้เป็นไปตามส่วนก่อนหน้า: อินเทอร์เฟซเชื่อมต่อคลาสต่างๆ มากมายที่มักไม่มีอะไรเหมือนกัน ในขณะที่คลาสนามธรรมถูกสร้างขึ้นสำหรับกลุ่มของคลาสที่เกี่ยวข้องกันอย่างใกล้ชิด ดังนั้นจึงสมเหตุสมผลที่คุณจะสามารถสืบทอดคลาสดังกล่าวได้เพียงหนึ่งคลาสเท่านั้น คลาสนามธรรมอธิบายความสัมพันธ์แบบ 'is-a'
อินเทอร์เฟซมาตรฐาน: InputStream และ OutputStream
เราได้ศึกษาคลาสต่าง ๆ ที่รับผิดชอบอินพุตและเอาต์พุตสตรีมแล้ว ลองพิจารณาInputStreamและOutputStream. โดยทั่วไปแล้วสิ่งเหล่านี้ไม่ใช่อินเทอร์เฟซ แต่เป็นคลาสนามธรรมของแท้ทั้งหมด ตอนนี้คุณรู้แล้วว่ามันหมายถึงอะไร ดังนั้นการทำงานกับพวกมันจะง่ายขึ้นมาก :) InputStreamเป็นคลาสนามธรรมที่รับผิดชอบอินพุตไบต์ Java มีหลายคลาสที่สืบทอดInputStream. แต่ละคนได้รับการออกแบบเพื่อรับข้อมูลจากแหล่งต่างๆ เนื่องจากInputStreamเป็นพาเรนต์ จึงมีวิธีหลายวิธีที่ทำให้ทำงานกับสตรีมข้อมูลได้ง่าย ลูกหลานของแต่ละคนInputStreamมีวิธีการเหล่านี้:
int available()ส่งคืนจำนวนไบต์ที่สามารถอ่านได้close()ปิดสตรีมอินพุตint read()ส่งคืนการแสดงจำนวนเต็มของไบต์ถัดไปที่มีในสตรีม หากถึงจุดสิ้นสุดของสตรีม -1 จะถูกส่งกลับint read(byte[] buffer)พยายามอ่านไบต์ลงในบัฟเฟอร์ และส่งคืนจำนวนไบต์ที่อ่าน เมื่อถึงจุดสิ้นสุดของไฟล์ จะส่งกลับ -1;int read(byte[] buffer, int byteOffset, int byteCount)เขียนส่วนหนึ่งของบล็อกของไบต์ ใช้เมื่ออาร์เรย์ไบต์อาจไม่เต็ม เมื่อถึงจุดสิ้นสุดของไฟล์ จะส่งกลับ -1;long skip(long byteCount)ข้าม byteCount ไบต์ในสตรีมอินพุต และส่งคืนจำนวนไบต์ที่ถูกละเว้น
FileInputStream: ชนิดที่พบบ่อยที่สุดของInputStream. ใช้เพื่ออ่านข้อมูลจากไฟล์StringBufferInputStream: อีกประเภทที่เป็นประโยชน์ของInputStream. มันแปลงสตริงเป็นInputStream;BufferedInputStream: สตรีมอินพุตบัฟเฟอร์ ใช้บ่อยที่สุดเพื่อเพิ่มประสิทธิภาพ
BufferedReaderบอกว่าไม่ต้องใช้ได้ไหม เมื่อเราเขียน:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…คุณไม่จำเป็นต้องใช้BufferedReader: An InputStreamReadercan do the job. แต่BufferedReaderปรับปรุงประสิทธิภาพและยังสามารถอ่านข้อมูลทั้งบรรทัดแทนที่จะเป็นอักขระแต่ละตัว สิ่งเดียวกันนี้ใช้กับBufferedInputStream! คลาสจะรวบรวมข้อมูลอินพุตในบัฟเฟอร์พิเศษโดยไม่ต้องเข้าถึงอุปกรณ์อินพุตตลอดเวลา ลองพิจารณาตัวอย่าง:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class BufferedInputExample {
public static void main(String[] args) throws Exception {
InputStream inputStream = null;
BufferedInputStream buffer = null;
try {
inputStream = new FileInputStream("D:/Users/UserName/someFile.txt");
buffer = new BufferedInputStream(inputStream);
while(buffer.available()>0) {
char c = (char)buffer.read();
System.out.println("Character read: " + c);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
inputStream.close();
buffer.close();
}
}
}
ในตัวอย่างนี้ เราอ่านข้อมูลจากไฟล์ที่อยู่ในคอมพิวเตอร์ที่ ' D:/Users/UserName/someFile.txt ' เราสร้างวัตถุ 2 ชิ้น — a FileInputStreamและ a BufferedInputStreamที่ 'ห่อหุ้ม' จากนั้นเราจะอ่านไบต์จากไฟล์และแปลงเป็นอักขระ และเราทำจนกว่าไฟล์จะสิ้นสุด อย่างที่คุณเห็นไม่มีอะไรซับซ้อนที่นี่ คุณสามารถคัดลอกรหัสนี้และรันบนไฟล์จริงบนคอมพิวเตอร์ของคุณ :) OutputStreamคลาสนี้เป็นคลาสนามธรรมที่แสดงถึงเอาต์พุตสตรีมของไบต์ ดังที่คุณทราบแล้ว สิ่งนี้ตรงกันข้ามกับไฟล์InputStream. ไม่มีหน้าที่ในการอ่านข้อมูลจากที่ใดที่หนึ่ง แต่รับผิดชอบในการส่งข้อมูลที่ใดที่หนึ่ง เช่นInputStreamคลาสนามธรรมนี้ให้ชุดวิธีการที่สะดวกแก่ลูกหลานทั้งหมด:
void close()ปิดสตรีมเอาต์พุตvoid flush()ล้างบัฟเฟอร์เอาต์พุตทั้งหมดabstract void write(int oneByte)เขียน 1 ไบต์ไปยังสตรีมเอาต์พุตvoid write(byte[] buffer)เขียนอาร์เรย์ไบต์ไปยังสตรีมเอาต์พุตvoid write(byte[] buffer, int offset, int count)เขียนช่วงจำนวนไบต์จากอาร์เรย์ เริ่มต้นที่ตำแหน่งออฟเซ็ต
OutputStreamชั้นเรียนบางส่วน:
-
DataOutputStream. เอาต์พุตสตรีมที่มีเมธอดสำหรับการเขียนชนิดข้อมูลมาตรฐาน Javaคลาสที่ง่ายมากสำหรับการเขียนประเภทข้อมูลและสตริงดั้งเดิมของ Java คุณอาจจะเข้าใจรหัสต่อไปนี้แม้ว่าจะไม่มีคำอธิบายก็ตาม:
import java.io.*; public class DataOutputStreamExample { public static void main(String[] args) throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream("testFile.txt")); dos.writeUTF("SomeString"); dos.writeInt(22); dos.writeDouble(1.21323); dos.writeBoolean(true); } }มีวิธีการแยกสำหรับแต่ละประเภท —
writeDouble(),writeLong(),writeShort(), และอื่นๆ FileOutputStream. คลาสนี้ใช้กลไกในการส่งข้อมูลไปยังไฟล์บนดิสก์ อย่างไรก็ตาม เราได้ใช้มันไปแล้วในตัวอย่างที่แล้ว คุณสังเกตเห็นหรือไม่? เราส่งต่อไปยัง DataOutputStream ซึ่งทำหน้าที่เป็น 'wrapper'BufferedOutputStream. กระแสเอาต์พุตบัฟเฟอร์ นอกจากนี้ยังไม่มีอะไรซับซ้อนที่นี่ จุดประสงค์ของมันคล้ายกับBufferedInputStream(หรือBufferedReader) แทนที่จะอ่านข้อมูลตามลำดับตามปกติ จะเขียนข้อมูลโดยใช้บัฟเฟอร์ 'สะสม' แบบพิเศษ บัฟเฟอร์ทำให้สามารถลดจำนวนครั้งที่เข้าถึงอ่างข้อมูล ซึ่งจะเป็นการเพิ่มประสิทธิภาพimport java.io.*; public class DataOutputStreamExample { public static void main(String[] args) throws IOException { FileOutputStream outputStream = new FileOutputStream("D:/Users/Username/someFile.txt"); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); String text = "I love Java!"; // We'll convert this string to a byte array and write it to a file byte[] buffer = text.getBytes(); bufferedStream.write(buffer, 0, buffer.length); } }อีกครั้ง คุณสามารถลองใช้โค้ดนี้ด้วยตัวเองและตรวจสอบว่าโค้ดนี้ใช้ได้กับไฟล์จริงในคอมพิวเตอร์ของคุณ
FileInputStreamและFileOutputStreamดังนั้นBuffreredInputStreamนี่จึงเป็นข้อมูลที่เพียงพอสำหรับการทำความรู้จักกันครั้งแรก แค่นั้นแหละ! เราหวังว่าคุณจะเข้าใจความแตกต่างระหว่างอินเทอร์เฟซและคลาสนามธรรม และพร้อมที่จะตอบคำถามใด ๆ แม้กระทั่งคำถามลวง :)
GO TO FULL VERSION