オブジェクト指向オブジェクト指向

公開日: 2026-07-29 08:35 更新日: 2026-07-29 08:35 4732文字 24 min read ... ページビュー

この記事では、クラスとオブジェクトの定義、メンバ変数とメソッドの機能、コンストラクタとthisキーワードの使用、カプセル化、継承、多相、superとthisの呼び出し、JARパッケージ管理、JVMランタイムデータ領域構造など、オブジェクト指向プログラミングのコア概念と実装の詳細を体系的に紹介します。オブジェクト指向の“データと振る舞いのカプセル化”の考え方を強調し、クラス抽象化とオブジェクトインスタンス化を通じてコードの再利用性と保守性を実現し、メソッド呼び出し、パラメータ渡し、アクセス制御、書き換えルール、メモリ管理メカニズムを詳細に説明し、Javaプログラミングの完全な理論と実践の基礎を提供します。

オブジェクト指向オブジェクト指向

オブジェクト指向のアイデア

プロセス指向は問題解決のステップと実行順序を強調し、オブジェクト指向はデータとデータを操作する振る舞いをオブジェクトに組織化し、オブジェクト間でタスクを達成することを強調する。

クラスは、クラスの具体的なインスタンスであるオブジェクトのクラスに共通する特性と振る舞いを抽象化したものです。

オブジェクト指向プログラミングには、通常次の手順が含まれる。

  1. ビジネスにおけるオブジェクトとその関係を分析する。

  2. オブジェクトの共通の特性に基づいてクラスを定義します。

  3. オブジェクトを作成し、初期状態を設定します。

  4. オブジェクトのメソッドを呼び出して、ビジネス機能を完了します。

クラスの定義

クラスはclassキーワードを使用して定義され、メンバ変数、コンストラクタ、メソッドを含めることができます。

public class Person {
    private String name;
    private int age;

    public void introduce() {
        System.out.println("我叫 " + name);
    }
}

メンバー変数

クラス内やメソッド外で定義された変数はメンバ変数と呼ばれ、しばしばフィールドやプロパティと呼ばれます。メソッド、コンストラクタ、またはコードブロックで定義された変数はローカル変数と呼ばれます。

public class Person {
    String name;
    int age;
    char sex;
    double height;
}

メンバー変数には基本型と参照型を使用できます。

public class School {
    String name;
    String address;
}
public class Person {
    String name;
    School school;
}

Personオブジェクトが作成されると、参照タイプフィールドschoolのデフォルト値はnullになります。school.nameにアクセスする前に、Schoolオブジェクトを指す必要があります。

Person person = new Person();
person.school = new School();
person.school.name = "哈工大";
person.school.address = "南岗区";

次のコードでは、person.schoolnullのままであるため、NullPointerExceptionをスローします。

Person person = new Person();
person.school.name = "哈工大";
image-001
image-001

オブジェクトが有効な参照によってアクセスされなくなると、ガベージコレクタのリサイクルオブジェクトになります。ガベージコレクションの具体的なタイミングはJVMによって決定され、プログラムはオブジェクトがすぐに回収されると仮定することはできない。

メンバー変数のデフォルト値#メンバー変数ノデフォルト#

オブジェクトを作成すると、メンバー変数はデフォルト値を取得します。

タイプデフォルト
integer 型0
浮動小数点型0.0 0です。
char\u0000
boolean`false
参照型の種類null

ローカル変数には自動的に使用できるデフォルト値はなく、読み込む前に代入を完了する必要があります。

オブジェクトのメモリー構造

概念的には、オブジェクトは通常、オブジェクトヘッダ、インスタンスデータ、アライメントパディングを含みます。配列オブジェクトには長さの記録も必要です。

image-002
image-002

オブジェクトヘッダ、参照サイズ、アライメントはJVM 実装の詳細であり、JVM、圧縮ポインタ、および実行プラットフォームの影響を受け、ビジネスコードでは固定バイト数に依存しないでください。

方法論は

メソッドは繰り返し呼び出し可能な振る舞いをカプセル化します。

访问修饰符 返回类型 方法名(参数列表) {
    方法体
    return 返回值;
}

戻り値は

戻り値の型がvoid以外の場合、通常終了するすべての実行パスは宣言された型と互換性のある値を返さなければなりません。returnを実行すると、現在のメソッドは直ちに終了します。

public int add(int a, int b) {
    return a + b;
}

戻り値の型がvoidの場合、結果を返す必要はなく、値なしのreturn早期終了メソッドを使用することもできます。

public void printPositive(int value) {
    if (value <= 0) {
        return;
    }
    System.out.println(value);
}

参加型の構成

メソッド宣言のパラメータは形式パラメータと呼ばれ、メソッドが呼び出されたときに渡される値は実パラメータと呼ばれます。

public boolean eat(String food) {
    return "肉".equals(food);
}
boolean full = person.eat("大米饭");

文字列の内容を比較するときは、equals()を使用します。==は使用しません。

メソッド内で別のメソッドを呼び出す

同じオブジェクトのメソッド間で直接呼び出すことも、thisを明示的に使用することもできます。

public void introduce() {
    System.out.println("我叫 " + name);
}

public void play() {
    introduce();
    // 等价于 this.introduce();
}

他のクラスのインスタンスメソッドを呼び出す場合は、対応するオブジェクトを取得する必要があります。

School school = new School();
school.showInfo();

メソッドのオーバーロード

同じクラス内で、同じメソッド名と異なるパラメータリストを持つメソッドはオーバーロードを構成します。パラメータリストの違いは、パラメータの数、パラメータのタイプ、またはパラメータの順序の違いとして表現することができる。

public double add(double a, double b) {
    return a + b;
}

public int add(int a, int b) {
    return a + b;
}

public double add(int a, double b) {
    return a + b;
}

戻り値の型だけではオーバーロードできません。

コンパイラーは引数の型に基づいて最もマッチするメソッドを選択します。プリミティブ型昇格、ボックス化、可変パラメータなどの候補が複数ある場合、呼び出しの曖昧さが生じる可能性があるため、オーバーロード設計は明確にする必要があります。

public class Calculator {
    double add(double a, double b) {
        return a + b;
    }

    int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        calculator.add(10, 20);   // 调用 int 版本
        calculator.add('a', 'b'); // char 提升为 int
        calculator.add(10L, 20L); // long 提升为 double
    }
}

オブジェクトを作成する

new式を使用して、オブジェクトを作成し、コンストラクタを呼び出すことができます。

Person person1 = new Person();
person1.name = "Tom";
person1.age = 20;

Person person2 = new Person();
person2.name = "Jack";
person2.age = 21;

変数person1person2は、2つの異なるオブジェクトへの参照を保持します。

コンストラクター

コンストラクタは、オブジェクトの作成時に初期化を完了するために使用されます。コンストラクタ名はクラス名と同じで、戻り値の型は持たない必要があります。

public class Person {
    String name;
    int age;
    double height;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
}

クラス内でコンストラクタが宣言されていない場合、コンパイラーはクラスアクセスレベルに関連する引数なしコンストラクタを提供します。コンストラクタが明示的に宣言されると、コンパイラーは引数なしコンストラクタを自動的に補完しなくなります。

クラスは異なるパラメータリストでコンストラクタをオーバーロードできます。

このキーワードです

thisは、現在のオブジェクトを表します。

現在のオブジェクトのメンバーにアクセスするには

メンバー変数がローカル変数と同じ名前の場合、thisを使用してメンバー変数を区別します。

public Person(String name, int age, double height) {
    this.name = name;
    this.age = age;
    this.height = height;
}

thisは、現在のオブジェクトのメソッドを呼び出すときに省略できます。

public void introduce() {
    System.out.println("我叫 " + name);
}

public void sing() {
    this.introduce();
}

他のコンストラクタの呼び出し

this()は、このクラスの他のコンストラクタを呼び出すために使用され、現在のコンストラクタの最初の文でなければなりません。

public Person() {
    System.out.println("创建 Person 对象");
}

public Person(String name, int age) {
    this();
    this.name = name;
    this.age = age;
}

public Person(String name, int age, double height) {
    this(name, age);
    this.height = height;
}

コンストラクタ間のループ呼び出しはありません。

カプセル化する

カプセル化は、データとデータを操作する振る舞いをクラスに整理し、外部から直接制御されるべきではない実装の詳細を隠すことです。

プロパティカプセル化

通常、フィールドはprivateとして宣言され、メソッドを介して制御されたアクセスが提供されます。

public class Person {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄不合法");
        }
        this.age = age;
    }
}

動作カプセル化

方法論は、明確かつ単一の責任を負うべきです。高い凝集はコードの可読性、再利用性、テスト性を向上させる。

パッケージPackage

パッケージはクラスを整理し、クラス名の競合を回避するために使用され、そのディレクトリ構造は通常パッケージ名と一致します。

package com.hyxy.system;

import com.hyxy.model.Student;

public class Test {
    public static void main(String[] args) {
        Student student = new Student();
    }
}

パッケージ名は通常小文字で使用され、一般的な名前は会社のドメイン名とプロジェクト名とモジュール名の逆書きです。java.langパッケージは、StringObjectSystemなどの自動的にインポートされます。

デフォルトパッケージ内のクラスは、名前付きパッケージ内のコード仕様による参照や管理が困難なため、正式なプロジェクトでは推奨されません。

継承する

サブクラスは、extendsを使用して親クラスを継承します。継承は、“サブクラスが親クラスである”という関係を表現し、親クラスが継承できる状態や振る舞いを再利用するために使用されます。

public class Animal {
    protected String name;

    public void eat() {
        System.out.println("动物正在进食");
    }
}
public class Person extends Animal {
    private String id;

    public void study() {
        System.out.println("学习");
    }
}

Javaクラスは単一継承のみをサポートしているが、複数階層継承を形成できる。他のクラスを明示的に継承していないクラス。デフォルトではObjectを直接継承します。

コンストラクタは継承されず、親クラスのprivateメンバはサブクラスから直接アクセスできませんが、親クラスのオブジェクト状態の一部になります。

アクセス権変更子

修飾子現在のクラス同じクラス異なる包子クラスその他の場所
privateできるかもしれないいいえ、いいえ。いいえ、いいえ。いいえ、いいえ。
デフォルトの権限できるかもしれないできるかもしれないいいえ、いいえ。いいえ、いいえ。
protectedできるかもしれないできるかもしれないできるかもしれないいいえ、いいえ。
publicできるかもしれないできるかもしれないできるかもしれないできるかもしれない

クロスパッケージサブクラスでのprotectedのアクセスも参照式型によって制限され、実際には保護されたメソッドで動作をカプセル化することを優先する必要があります。

メソッドの書き換え方法

親クラスから継承可能なインスタンスメソッドを再実装するサブクラスはオーバーライドと呼ばれます。

public class Person extends Animal {
    @Override
    public void eat() {
        System.out.println("这个人正在吃饭");
    }
}

上書きには、次の主なルールを満たす必要があります。

  • メソッド名とパラメータリストは同じです。
  • 戻り値の型が同じか、親クラスの戻り値の型のサブタイプです。
  • サブクラスのメソッドは、親クラスのメソッドよりも厳密にアクセスできません。
  • サブクラスによって宣言されたチェック例外は親メソッドよりも広範ではありません。
  • finalメソッドはオーバーライドできません。privateメソッドは見えません。静的メソッドはオーバーライドではなく隠されています。

コンパイラが書き換えが正しいかどうかをチェックできるように、@Overrideを追加することをお勧めします。

書き換えと動的結合

public class Shape {
    public double area() {
        return 0;
    }

    public static double sumArea(Shape[] shapes) {
        double sum = 0;
        for (Shape shape : shapes) {
            sum += shape.area();
        }
        return sum;
    }
}
public class Rectangle extends Shape {
    private final int length;
    private final int width;

    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }

    @Override
    public double area() {
        return length * width;
    }
}
Shape[] shapes = {
    new Rectangle(10, 20),
    new Rectangle(15, 10)
};
System.out.println(Shape.sumArea(shapes));

配列要素の宣言型がShapeであっても、ランタイムは実際のオブジェクトのオーバーライド後にarea()メソッドを呼び出します。

superキーワード

superは、親クラスメンバにアクセスしたり、親クラスコンストラクタを呼び出したりするために使用されます。

親メソッドを呼び出す

@Override
public void eat() {
    super.eat();
    System.out.println("这个人正在吃饭");
}

親クラスコンストラクタの呼び出し

サブクラスコンストラクタは直接または間接的に親クラスコンストラクタを呼び出す必要があります。明示的に記述しない場合、コンパイラーは最初の行にsuper()を追加しようとします。

public class Person extends Animal {
    private final String id;

    public Person(String name, String id) {
        super(name);
        this.id = id;
    }
}

親クラスがアクセス可能な引数なしコンストラクタを持たない場合、サブクラスは他の親クラスコンストラクタを明示的に呼び出す必要があります。super()this()は両方ともコンストラクタの最初の行に配置する必要があります。したがって、同じコンストラクタ内で両方を直接表示することはできません。

多形性

親クラス型への参照はサブクラスオブジェクトを指すことができ、これはポリモーフィズムを反映する。

Animal animal = new Person();
Object object = new Person();

Animal[] animals = {
    new Person(),
    new Dog()
};

多相は、メソッドパラメータとコレクションが複数の具体的なサブクラスを受け取ることを可能にする。

public void feed(Animal animal) {
    animal.eat();
}

オーバーライドされたインスタンスメソッドを呼び出すと、実行結果は実際のオブジェクト型によって決まります。アクセスできるメンバは参照の宣言型によって決まります。

ポリモーフィックリファレンスのアクセス範囲

親クラス参照は、サブクラスが追加したメソッドを直接呼び出すことはできません。

image-003
image-003
image-004
image-004
image-005
image-005
Animal animal = new Person();
animal.eat();
// animal.study(); // 编译错误

フィールドアクセスはインスタンスメソッドと同じ動的バインディング効果を持たないため、親クラスとサブクラスで同じ名前のフィールドを定義することは推奨されません。

アップシフトとダウンシフト

サブクラスオブジェクトの親クラス型への変換はアップキャストと呼ばれ、通常は自動的に行われます。

Animal animal = new Person();

親クラス参照からサブクラス型への変換はダウンキャストと呼ばれ、明示的な変換が必要で、実際のオブジェクトはターゲット型でなければなりません。

Person person = (Person) animal;
person.study();

間違ったダウンキャストはClassCastExceptionをスローします。

instanceof 演算子

instanceofは、オブジェクトが指定された型に安全に変換できるかどうかを判断するために使用されます。対象がnullの,判定はfalseとなる.

public void friend(Animal animal) {
    if (animal instanceof Person) {
        Person person = (Person) animal;
        person.study();
    } else if (animal instanceof Dog) {
        Dog dog = (Dog) animal;
        dog.watchDoor();
    }
}

JARパッケージのエクスポートおよびインポート

JARはZIP 形式ベースのアーカイブで、バイトコード、リソース、メタデータを格納できます。ライブラリは通常、コンパイル結果をJARとしてパッケージ化し、必要に応じてソースコードやドキュメントを添付することもできます。

IntelliJ IDEAでのエクスポート

  1. FileProject StructureArtifactsの電源を入れます。

  2. JAR Artifactを新規作成し、出力の内容と場所を設定します。

  3. BuildBuild ArtifactsによりJARを構築する.

サードパーティ製 JARのインポート

  1. プロジェクトにlibディレクトリを作成し、JARファイルをコピーします。

  2. FileProject StructureLibrariesの電源を入れます。

  3. libのJARをエンジニアリングクラスパスに追加します。

MavenやGradleを使用するプロジェクトは、依存関係構成でサードパーティ製ライブラリを管理し、大量のJARを手動でコピーすることは推奨されません。

Object 一般的なメソッド

toStringメソッド#toStringメソッド#

toString()オブジェクトの文字列表現を返します。System.out.println(object)は、オブジェクトのtoString()を間接的に呼び出します。

Object.toString()のデフォルトの結果は、通常、クラス名とハッシュコードの16 進数形式を含み、信頼できるメモリアドレスと解釈すべきではありません。

public class StudentCard {
    private final String id;
    private final String name;

    public StudentCard(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "学号:" + id + ",姓名:" + name;
    }
}

equalsメソッドとhashCodeメソッド

Object.equals()デフォルトの比較参照が同じオブジェクトを指しているかどうか。ビジネスオブジェクトは通常、キーフィールドに基づいてequals()を上書きする。

public class StudentCard {
    private final String id;
    private final String name;

    public StudentCard(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (!(object instanceof StudentCard)) {
            return false;
        }
        StudentCard other = (StudentCard) object;
        return Objects.equals(id, other.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

equals()を書き直す際には、反射性、対称性、推移性、一貫性を維持し、nullを正しく扱う必要がある。hashCode()を書き換えて、等しいオブジェクトが同じハッシュコードを持つようにしてください。

文字列コンテンツ比較には、equals()または他の明示的なコンテンツ比較方法を使用する必要があります。

JVMランタイム·データ領域の概要

JVMランタイム·データ·ゾーンは、通常、次の部分で構成されます。

  • プログラムカウンタ:現在のスレッドが実行したバイトコード位置を記録し、各スレッドが独立して保持します。
  • Java 仮想マシンスタック:メソッド呼び出しごとにスタックフレームが作成され、ローカル変数、オペランドスタック、および戻り値情報などを保持します。
  • ローカルメソッドスタック:ローカルメソッド呼び出しをサポートし、仮想マシンスタックと統合することができます。
  • ヒープ:主にオブジェクトと配列を格納し、ガベージコレクタによって管理され、通常はすべてのスレッドで共有されます。
  • メソッド領域:クラス構造、実行時定数プール、静的フィールドなどの論理情報を保存します。具体的な実装はメタ空間などのメカニズムを使用できます。

論理ランタイム領域をJVMバージョンの物理メモリ実装と完全に同一視しないでください。

Javaのパラメータ転送

Javaは値を渡す。メソッドが呼び出されると、引数の値が形式引数にコピーされます。

プリミティブ型パラメータ

public void modifyValue(int value) {
    value++;
    System.out.println("方法内部:" + value);
}
int value = 3;
modifyValue(value);
System.out.println("方法外部:" + value);

メソッド内ではパラメータのコピーを変更し、外部変数は変更しません。

image-006
image-006

参照型のパラメータ

オブジェクトを渡すと、コピーされるのは参照値です。形式引数と実体引数は最初は同じオブジェクトを指しているため、形式引数でオブジェクトプロパティを変更すると、呼び出し元は変更を見ることができます。

public void rename(Person person) {
    person.name = "李四";
}
Person person = new Person();
person.name = "张三";
rename(person);
System.out.println(person.name); // 李四

しかし、形式パラメータをnullまたは別のオブジェクトに再割り当てすると、形式パラメータが保持する参照コピーのみが変更されます。

public void clearReference(Person person) {
    person = null;
}

呼び出し後、外部変数は元のオブジェクトを参照します。

配列の内容の変更

メソッドの目的が元の配列を空にすることである場合、パラメータを新しい配列に指定するのではなく、配列要素を変更する必要があります。

public void clear(int[] array) {
    Arrays.fill(array, 0);
}

メソッドが新しい配列を作成して返す場合は、呼び出し元が戻り値を受け取る必要があります。

public int[] createEmptyCopy(int[] array) {
    return new int[array.length];
}

気に入ったならばコメントを残してくださいね~

... ページビュー
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka