CodeGym /Java Blog /ランダム /抽象クラスとインターフェイスの違い
John Squirrels
レベル 41
San Francisco

抽象クラスとインターフェイスの違い

ランダム グループに公開済み
やあ!このレッスンでは、抽象クラスがインターフェイスとどのように異なるかについて説明し、一般的な抽象クラスの例をいくつか検討します。 抽象クラスとインターフェイスの違い - 1このトピックは非常に重要であるため、抽象クラスとインターフェイスの違いについては別のレッスンに充てました。今後の面接の 90% で、これらの概念の違いについて質問されることになります。つまり、何を読んでいるのかを必ず理解する必要があります。何かが完全に理解できない場合は、追加のソースを読んでください。したがって、抽象クラスとは何か、インターフェイスとは何かはわかりました。次に、それらの違いについて説明します。
  1. インターフェースは動作のみを記述します。状態はありません。しかし、抽象クラスには状態が含まれており、両方を記述します。

    たとえば、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;
       }
    }
    

    Bird クラスを作成し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());
       }
    }
    

    ご覧のとおり、抽象クラスの状態 (その状態speciesage変数) に簡単にアクセスできます。

    しかし、同じことをインターフェースで行おうとすると、状況は異なります。それに変数を追加してみることができます。

    
    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();
    }
    

    インターフェイス内でプライベート変数を宣言することもできません。なぜ?private修飾子は実装をユーザーから隠すために作成されたためです。また、インターフェイスの内部には実装がありません。隠すものは何もありません。

    インターフェースは動作のみを記述します。したがって、インターフェイス内にゲッターとセッターを実装することはできません。これはインターフェイスの性質です。インターフェイスは状態ではなく動作を操作するために必要です。

    Java 8 では、実装を持つインターフェイスのデフォルト メソッドが導入されました。それらについてはすでにご存知ですので、繰り返しません。

  2. 抽象クラスは、非常に密接に関連するクラスを接続し、統合します。同時に、共通点がまったくないクラスによって 1 つのインターフェイスを実装することもできます。

    鳥の例に戻りましょう。

    抽象クラスはBird、そのクラスに基づいて鳥を作成するために必要です。鳥だけで他には何もありません!もちろん、いろんな種類の鳥もいますよ。

    抽象クラスとインターフェイスの違い - 2

    インターフェイスを使用するとCanFly、誰もが独自の方法で作業を進めることができます。名前に関連付けられた動作(飛行)のみを説明します。関係のない多くのものが「飛ぶことができる」。

    抽象クラスとインターフェイスの違い - 3

    これら 4 つのエンティティは互いに関連しません。彼らは全員が生きているわけではありません。しかし、それらはすべてCanFly

    抽象クラスを使用してそれらを記述することはできません。これらは同じ状態や同じフィールドを共有しません。航空機を定義するには、おそらくモデル、製造年、最大乗客数のフィールドが必要になります。カールソンの場合は、今日食べたすべてのお菓子のフィールドと、弟とプレイするゲームのリストが必要になります。蚊の場合は、...うーん、私にもわかりません... たぶん、「迷惑レベル」でしょうか?:)

    重要なのは、抽象クラスを使用してそれらを記述することはできないということです。違いすぎます。しかし、彼らは共通の行動を持っています:彼らは飛ぶことができます。インターフェイスは、飛んだり、泳いだり、ジャンプしたり、その他の動作を示す世界のあらゆるものを記述するのに最適です。

  3. クラスは必要な数のインターフェイスを実装できますが、継承できるクラスは 1 つだけです。

    これについてはすでに何度か言及しました。Java にはクラスの多重継承はありませんが、インターフェイスの多重継承はサポートされています。この点は、前の点から部分的に踏襲されています。インターフェースは、多くの場合、他に共通点を持たない多くの異なるクラスを接続しますが、抽象クラスは、非常に密接に関連したクラスのグループに対して作成されます。したがって、そのようなクラスを 1 つだけ継承できるのは当然です。抽象クラスは、「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 バイトをスキップし、無視されたバイト数を返します。
メソッドの完全なリストを学習することをお勧めします。実際には10以上の子供クラスがあります。たとえば、以下にいくつか挙げます。
  1. FileInputStream: の最も一般的なタイプInputStream。ファイルから情報を読み取るために使用されます。
  2. StringBufferInputStream: もう 1 つの役立つタイプのInputStream。文字列をInputStream; に変換します。
  3. BufferedInputStream: バッファリングされた入力ストリーム。パフォーマンスを向上させるために最もよく使用されます。
私たちが行ってBufferedReader、それを使用する必要はないと言ったときのことを覚えていますか? 書くとき:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…使用する必要はありませんBufferedReader: An はInputStreamReaderその仕事をすることができます。ただし、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」にあるファイルからデータを読み取ります。FileInputStream2 つのオブジェクト、 aとBufferedInputStreamそれを「ラップ」する aを作成します。次に、ファイルからバイトを読み取り、文字に変換します。そして、ファイルが終了するまでそれを繰り返します。ご覧のとおり、ここには複雑なことは何もありません。このコードをコピーして、コンピューター上の実際のファイルで実行できます :) この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)オフセット位置から開始して、配列から count バイトの範囲を書き込みます。
以下に、クラスの子孫の一部を示しますOutputStream
  1. 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()など)。


  2. FileOutputStream。このクラスは、ディスク上のファイルにデータを送信するメカニズムを実装します。ちなみに、前の例ですでに使用しました。気づきましたか?それを「ラッパー」として機能する DataOutputStream に渡しました。

  3. 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ので、初めて知る人にとってはこれで十分です。 それでおしまい!インターフェースと抽象クラスの違いを理解し、どんな質問にも、たとえひっかけの質問にも答える準備ができていることを願っています :)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION