1. ホーム
  2. Java

Java基礎知識 - 知識のポイント

2022-02-24 08:34:46

データ型

パッケージタイプ

基本8種類。

<テーブル タイプ バイト数 ビット数(bit) 最小 最大 デフォルト パッケージクラス 備考 整数型 バイト 1バイト 8ビット、符号付き整数(2進数の補数 -128 (-2^7) 127 (2^7-1) 0 バイト バイト変数はint型の4分の1のスペースしか取らないので、大きな配列のスペースを節約するために、主にint型の代わりに使用されます。 整数型 短い 2バイト 16ビット、符号付き整数(2進数の補数 -32768(-2^15) 32767(2^15-1) 0 短い

Shortデータ型は、byteと同様にスペース効率に優れています。

short変数は、int変数の半分のスペースしか必要としません。

整数型 int 4バイト 32ビット符号付き整数(2進数の補数 -2,147,483,648 (-2^31) 2,147,483,647(2^31-1) 0 整数 一般的な整数型変数のデフォルトはintです。 整数の種類 長い 8バイト 64ビット、符号付き整数(2進数の補数 -9,223,372,036,854,775,808 (-2^63) 9,223,372,036,854,775,807(2^63-1) 0 ロング

long型の場合は、数字の後にlが付きます。

(a) この型は主に大きな整数の比較を必要とするシステムで使用されます。

浮動小数点型 フロート 4バイト 32ビット、単精度、IEEE754準拠の浮動小数点数 1.4E-45 (2^-149) 3.4028235E38(2^128-1) 0.0f フロート

float型は、数値にfを付けて区別します。

大きな浮動小数点数配列を格納する際のメモリスペースを節約するために、float を使用します。

floatは通貨などの正確な値を表すには使用できません。

浮動小数点型 ダバー 8バイト 64ビット、倍精度、IEEE754準拠の浮動小数点数 4.9E-324 (2^-1074) 1.7976931348623157e308(2^1024-1) 0.0d ダブル

浮動小数点数のデフォルトの型は douber です。

また、通貨のような正確な値を表現するために使用することはできません。

文字の種類 チャー 2バイト 16ビット、ユニコード文字 \u0000(0) \⑯(65,535、すなわち2^16 - 1) empty, \u0000, 0 文字 charデータ型は、任意の文字を格納することができる。 ブーリアン型 ブーリアン 1ビット 虚偽 ブーリアン

は、trueとfalseの2つの値しかありません。

この型は、真偽のケースを記録するためのフラグとしてのみ使用されます。

ブーリアンについて。

  1. booleanは0か1かというquot;bit"特性を示しますが、記憶領域の基本単位はbyteであり、bitsではありません。つまり、booleanは少なくとも1バイトを消費します。
  2. JVMの仕様では、ブーリアン変数は4バイトのint型、ブーリアンアレイはバイト配列として扱われます。
  3. boolean型はint型としてコンパイルされます。これは、JVMにおいてint型とまったく同じバイトを占有すると言っているのと同じです。intは4バイトなので、booleanも4バイトになります。
  4. boolean 配列は、Oracle の JVM ではバイト配列としてエンコードされ、各 boolean 要素は 8 ビット = 1 バイトを占有します。
  5. (検証中)Oracle社以外のJVMでは、boolean配列が1byteを占有しない場合があります。
  6. グローバル変数の場合、ラッパークラスのBooleanにはデフォルトでnullが代入され、ベースタイプのbooleanにはデフォルトでfalseが代入されます。この代入処理は、クラスのロード時に行う必要があります。
  7. ローカル変数の場合、値を代入せずに使用するとコンパイラがエラーを報告するので、ローカル変数にデフォルト値を設定しないようにします。

基本型には対応するラッパ型があり、基本型とそれに対応するラッパ型との間の代入は自動的な箱詰めと箱出しを用いて行われます。

Integer x = 2; // packing calls the Integer.valueOf(int i) function
int y = x; // unboxing calls the Integer.intValue() function


キャッシュプール

new Integer(123) と Integer.valueOf(123) の違いです。

  • new Integer(123) は毎回新しいオブジェクトを作成します。
  • Integer.valueOf(123) はキャッシュプールにあるオブジェクトを使用し、複数回の呼び出しで同じオブジェクトへの参照を取得します。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true


valueOf() メソッドの実装は比較的単純で、まず値がキャッシュプールにあるかどうかを判断し、ある場合はプールの内容を直接返します。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}


Java 8 では、Integer キャッシュプールのサイズはデフォルトで -128 から 127 になっています。

static final int low = -128;
static final int high;
static final Integer cache[];

static {
    // high value may be configured by property
    int h = 127;
    String integerCacheHighPropValue =
        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    if (integerCacheHighPropValue ! = null) {
        try {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) - 1);
        } catch( NumberFormatException nfe) {
            // If the property cannot be parsed into an int, ignore it.
        }
    }
    high = h;

    cache = new Integer[(high - low) + 1];
    int j = low;
    for(int k = 0; k < cache.length; k++)
        cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
}



コンパイラはオートボックス時にvalueOf()メソッドを呼び出すため、オートボックスで作成された複数のIntegerインスタンスが同じ値を持つ場合、同じオブジェクトを参照することになります。

Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true


基本型のバッファプールは以下の通りです。

  • ブール値 true および false
  • 全バイト値
  • 128から127の間の短い値
  • int 値 -128 以上 127 未満
  • を入力します。

これらの基本型に対応するラッパータイプを使用すると、バッファプール内のオブジェクトを直接使用することができます。

StackOverflow : new Integer(123)、Integer.valueOf(123)と単なる123の違い

文字列

概要

Stringはfinalと宣言されているため、継承はできません。

内部的には、データはchar配列を使って格納されます。char配列はfinalと宣言されており、値配列を初期化した後は他の配列を参照することができません。

また、Stringは値配列を変更する内部メソッドを持たないので、イミュータブルであることが保証されています。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];


イミュータビリティのメリット

1. ハッシュ値をキャッシュすることができる

HashMapのキーとしてStringを使う場合など、Stringのハッシュ値を使うことが多いので、イミュータビリティ機能によりハッシュ値もイミュータブルになり、一度だけ計算すればよいことになります。

2. 文字列プールの必要性

Stringオブジェクトがすでに作成されている場合、Stringプールから参照を取得します。Stringが不変である場合のみ、String Poolを使用することができます。

3. セキュリティ

Stringはパラメータとして使われることが多く、String immutabilityはそのパラメータが不変であることを保証する。例えば、ネットワーク接続の場合にStringがMutableであると、ネットワーク接続中にStringが変更され、Stringオブジェクトを変更した側は、別のホストに接続されたと勘違いしますが、必ずしもそうとは限りません。

4. スレッドセーフ

文字列不変性は本質的にスレッドセーフであり、複数のスレッドで安全に使用することができる。

Program Creek : なぜJavaではStringがimmutableなのか?

文字列、StringBuffer、StringBuilder

1. 可変性

  • 文字列不変
  • StringBufferとStringBuilderはMutableです。

2. スレッドセーフ

  • 文字列は不変であるため、スレッドセーフである
  • StringBuilderはスレッドセーフではありません。
  • StringBuffer はスレッドセーフで、synchronized を使って内部で同期化されています。

スタックオーバーフロー 文字列、StringBuffer、およびStringBuilder

文字列.内部()

String.internal()を使用すると、同じ内容の文字列変数が同じメモリーオブジェクトを参照していることを確認できます。

次の例では、s1 と s2 は new String() を使って2つの異なるオブジェクトを作成し、s3 は s1.internal() メソッドでオブジェクトの参照を取得しています。

intern() は、まず s1 が参照するオブジェクトを String Pool に入れ、その後オブジェクトの参照を返します。

つまり、s3とs1は、文字列定数プールで同じオブジェクトを参照しているのです。

String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2); // false
String s3 = s1.internal();
System.out.println(s1.intern() == s3); // true


二重引用符を使用する "bbb" を使用して文字列インスタンスを作成すると、新しいオブジェクトは自動的に String Pool に入れられます。

String s4 = "bbb";
String s5 = "bbb";
System.out.println(s4 == s5); // true


Java 7以前は、文字列定数プールはランタイム定数プールに置かれ、これは永久に世代交代します。

そして、Java 7では、文字列定数プールはNative Methodに移されました。

これは、永久世代は容量が限られており、文字列を多用するシナリオではOutOfMemoryErrorエラーが発生する可能性があるためです。

事業内容

パラメータ受け渡し

Javaの引数は、参照渡しではなく値渡しでメソッドに渡されます。

以下のコードのDog dogのdogは、オブジェクトのアドレスを格納するポインタです。

メソッドにパラメータを渡す場合、基本的にオブジェクトのアドレスを値としてフォームパラメータに渡すことになります。

そのため、メソッド内でポインタが参照するオブジェクトを変更しても、その時点で2つのポインタはまったく別のオブジェクトを指しており、一方が指すオブジェクトの内容を変更しても、もう一方には何の影響もありません。

public class Dog {
    String name;

    Dog(String name) {
        this.name = name;
    }

    String getName() {
        return this.name;
    }

    void setName(String name) {
        this.name = name;
    }

    String getObjectAddress() {
        return super.toString();
    }
}

public class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        func(dog);
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        System.out.println(dog.getName()); // A
    }

    private static void func(Dog dog) {
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        dog = new Dog("B");
        System.out.println(dog.getObjectAddress()); // Dog@74a14482
        System.out.println(dog.getName()); // B
    }
}


class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        func(dog);
        System.out.println(dog.getName()); // B
    }

    private static void func(Dog dog) {
        dog.setName("B");
    }
}



しかし、メソッドでオブジェクトのフィールド値を変更すると、元のオブジェクトのそのフィールドの値も変更されます。同じアドレスが指すものを変更することになるからです。

// float f = 1.1;


StackOverflowです。Javaは"pass-by-reference"と"pass-by-value"のどちらでしょうか?

フロートとダブルの比較

1.1リテラルはdouble型であり、float変数に1.1を直接代入することはできません。javaは暗黙的に下方遷移を行うことができません。それは精度を低くしてしまうからです。

float f = 1.1f;


1.1fリテラルはfloat型です。

short s1 = 1;
// s1 = s1 + 1;


暗黙の型変換

リテラル1はint型であり、short型よりも正確であるため、int型からshort型に暗黙のうちにダウンコンバートすることはできない。

s1 += 1;


しかし、+=演算子を使って暗黙のうちに型変換を行うことができます。

s1 = (short) (s1 + 1);

上の文は、s1 + 1 の結果を下に変換することと同じである。

String s = "a";
switch (s) {
    case "a":
        System.out.println("aaa");
        break;
    case "b":
        System.out.println("bbb");
        break;
}


StackOverflow : Java の +=, -=, *=, /= 複合代入演算子にはなぜキャスティングが必要ないのですか?

スイッチ

Java 7から、switch条件文にStringオブジェクトを使用できるようになりました。

// long x = 111;
// switch (x) { // Incompatible types. found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'
// case 111:
// System.out.println(111);
// break;
// case 222:
// System.out.println(222);
// break;
// break; // }


switchがlongをサポートしないのは、少数の値に対してのみ等しい値を取るように設計されているからで、値が複雑すぎる場合はifを使った方が良い。

public class AccessExample {
    public String id;
}


StackOverflow : なぜswitch文のデータ型は長くないのですか、Java?

継承

アクセス許可

Javaにはprivate、protected、publicの3つのアクセス修飾子があり、アクセス修飾子がなければ、パッケージレベルで見ることができます。

アクセス修飾子は、クラスまたはクラスのメンバー(フィールドとメソッド)に追加することができます。

  • クラスの可視化とは、他のクラスがそのクラスを使用してインスタンスオブジェクトを作成できることを意味します。
  • member visible は、他のクラスがこのクラスのインスタンスオブジェクトでメンバーにアクセスできることを意味します。

protected は、メンバが継承システムにおいてサブクラスから見えることを示すために使用されますが、このアクセス修飾子はクラスにとって何の意味も持ちません。

よく設計されたモジュールは、すべての実装の詳細を隠し、そのAPIと実装を明確に分離しています。モジュール同士はAPIを通じてのみ通信し、あるモジュールは他のモジュールの内部動作について知る必要はありません。これは、情報隠蔽またはカプセル化として知られる概念です。そのため、アクセス権は各クラスやメンバが外部から可能な限りアクセスできないようにする必要があります。

子クラスのメソッドが親クラスのメソッドをオーバーライドする場合、子クラスのそのメソッドのアクセス・レベルは親クラスのアクセス・レベルより低くすることは許されません。これは、親クラスのインスタンスが使えるところならどこでも子クラスのインスタンスが使えるようにするため、つまりリヒター置換の原則を満たすようにするためです。

なぜなら、そうすると、フィールドがどのように変更されるかの制御ができなくなり、クライアントが自由に変更できるようになるからです。例えば、以下の例では、AccessExampleはidという共通フィールドを持っていますが、ある時点でidフィールドを格納するためにintを使いたくなった場合、すべてのクライアントコードを修正しに行く必要があります。

public class AccessExample {

    private int id;

    public String getId() {
        return id + "";
    }

    public void setId(String id) {
        this.id = Integer.valueOf(id);
    }
}


publicフィールドをpublicゲッターメソッドやセッターメソッドに置き換えることで、フィールドを変更する際の挙動を制御することができます。

public class AccessWithInnerClassExample {
    private class InnerClass {
        int x;
    }

    private InnerClass innerClass;

    public AccessWithInnerClassExample() {
        innerClass = new InnerClass();
    }

    public int getValue() {
        return innerClass.x; // direct access
    }
}


ただし、例外として、パッケージレベルでプライベートなクラスや、ネストされたプライベートなクラスの場合は、メンバーを直接公開しても特に大きな影響はないでしょう。

public abstract class AbstractClassExample {

    protected int x;
    private int y;

    public abstract void func1();

    public void func2() {
        System.out.println("func2");
    }
}

public class AbstractExtendClassExample extends AbstractClassExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}

// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated
AbstractClassExample ac2 = new AbstractExtendClassExample();
ac2.func1();


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

1. 抽象クラス

抽象クラスも抽象メソッドも、abstractキーワードを使って宣言します。抽象クラスは一般に抽象メソッドを含んでおり、それらは抽象クラス内に配置されなければなりません。

抽象クラスと通常のクラスの最大の違いは、抽象クラスはインスタンス化できず、サブクラスをインスタンス化するために抽象クラスを継承する必要があることです。

public interface InterfaceExample {
    void func1();

    default void func2(){
        System.out.println("func2");
    }

    int x = 123;
    // int y; // Variable 'y' might not have been initialized
    public int z = 0; // Modifier 'public' is redundant for interface fields
    // private int k = 0; // Modifier 'private' not allowed here
    // protected int l = 0; // Modifier 'protected' not allowed here
    // private void fun3(); // Modifier 'private' not allowed here
}


public class InterfaceImplementExample implements InterfaceExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}

// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);


public class SuperExample {
    protected int x;
    protected int y;

    public SuperExample(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void func() {
        System.out.println("SuperExample.func()");
    }
}

public class SuperExtendExample extends SuperExample {
    private int z;

    public SuperExtendExample(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public void func() {
        super.func();
        System.out.println("SuperExtendExample.func()");
    }
}

SuperExample e = new SuperExtendExample(1, 2, 3);
e.func();

SuperExample.func()
SuperExtendExample.func()


public final native Class<? > getClass()

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

protected void finalize() throws Throwable {}


2.インターフェース

インターフェースは、抽象クラスを拡張したもので、Java 8以前は、完全に抽象化されたクラスと見なすことができ、つまり、いかなるメソッドの実装も持つことができなかった。

Java 8以降では、デフォルト・メソッドをサポートしないインターフェースは保守コストがかかりすぎるという理由から、インターフェースもデフォルト・メソッドの実装を持つことができるようになりました。

Java 8以前は、インターフェイスが新しいメソッドを追加したい場合、それを修正するのはインターフェイスを実装するすべてのクラスに任されていました。

インターフェースのメンバー(フィールド+メソッド)は、デフォルトですべてpublicであり、privateやprotectedとして定義することはできません。

インターフェイスのフィールドは、デフォルトではすべて static および final です。

x.equals(x); // true


x.equals(y) == y.equals(x); // true


if (x.equals(y) && y.equals(z))
    x.equals(z); // true;


3. 比較する

  • 設計レベルでは、抽象クラスはIS-A関係を提供し、LIKE-A置換の原則、すなわち子オブジェクトがすべての親オブジェクトを置換できるようにしなければならない。インターフェースは、メソッドの実装契約を提供し、インターフェースとそれを実装するクラスとの間にIS-A関係を必要としないという点で、よりLIKE-A関係に近いと言えます。
  • 使い方としては、クラスは複数のインターフェースを実装することができますが、複数の抽象クラスを継承することはできません。
  • インターフェイスのフィールドはstatic型とfinal型しか使用できませんが、抽象クラスのフィールドにはそのような制限はありません。
  • インターフェイスのメンバはpublicでなければならないが、抽象クラスのメンバは複数のアクセス権を持つことができる。

4. 選択項目の使用

インターフェイスを使用する :

  • 例えば、無関係のクラスが Compareable インターフェースの compareTo() メソッドをすべて実装することができます。
  • 多重継承を行う必要がある。

抽象クラスを使用する。

  • 複数の関連するクラスでコードを共有する必要がある。
  • 継承されたメンバーへのアクセスを制御できるようにする必要がある(すべてがpublicであるとは限らない)。
  • 非静的・非定常なフィールドを継承する必要がある。

多くの場合、インターフェースは抽象クラスのような厳密なクラス階層の要件がなく、クラスに振る舞いを追加する柔軟性があるため、抽象クラスよりも優先されます。また、Java 8から、インターフェースはデフォルトのメソッド実装を持つことができるようになり、同様に変更するコストが低くなりました。

スーパー

  • 親のコンストラクタにアクセスする。super() 関数を使用して親のコンストラクタにアクセスし、 初期化作業の一部を親に委譲することができます。
  • 親クラスのメンバにアクセスする。子クラスが親クラスのメソッド実装をオーバーライドしている場合、super キーワードを使用して親クラスのメソッド実装を参照することができます。
x.equals(y) == x.equals(y); // true


x.equals(null); // false;


Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y); // false


public class EqualExample {
    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() ! = o.getClass()) return false;

        EqualExample that = (EqualExample) o;

        if (x ! = that.x) return false;
        if (y ! = that.y) return false;
        return z == that.z;
    }
}



キーワードスーパーを利用する

上書きとオーバーロード

1. オーバーライド

継承とは、親クラスとメソッドの宣言が同じものを子クラスが実装することです。

置換の原則を満たすために、オーバーライドには次の2つの制約があります。

  • 子クラスのメソッドのアクセス権は、親クラスのメソッドと同等以上でなければなりません。
  • 子クラスメソッドの戻り値の型は、親クラスメソッドの戻り値の型であるか、そのサブタイプである必要があります。

Overrideアノテーションを使用することで、コンパイラーは上記の2つの制約が満たされているかどうかをチェックするのに役立ちます。

2. オーバーロード

同じクラスに存在するとは、既存のメソッドと名前は同じだが、少なくとも1つのパラメータの型、数、順序が異なるということである。

なお、戻り値が異なり、その他が同じであれば、オーバーロードとは言いません。

オブジェクト汎用メソッド

概要

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size()); // 2


equals()

1. イコールの関係

(一)自己反省

31*x == (x<<5)-x

(ii) 対称性

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}


(iii) 譲渡性

public class ToStringExample {
    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}

ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());

ToStringExample@4554617c


(iv) 一貫性

equals()メソッドを複数回呼び出しても結果は同じである

public class CloneExample {
    private int a;
    private int b;
}

CloneExample e1 = new CloneExample();
// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object'


(v) ヌルとの比較

NULLでないオブジェクトxに対してx.equals(NULL)を呼び出すと、falseが返されます。

public class CloneExample {
    private int a;
    private int b;

    @Override
    protected CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}

CloneExample e1 = new CloneExample();
try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

java.lang.CloneNotSupportedException: CloneExample


2. equals() で == を指定する。

  • 基本型では、2つの値が等しいかどうかを == で判断します。基本型には equals() メソッドはありません。 基本型には equals() メソッドはありません。
  • 参照型の場合、2つの変数が同じオブジェクトを参照しているかどうかを == で判断し、参照されているオブジェクトが等価かどうかを equals() で判断します。
public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


3. 実装

  • 同じオブジェクトへの参照であるかどうかを調べ、参照であれば直接trueを返す。
  • は同じ型かどうかを調べ、違う場合は直接falseを返します。
  • Objectオブジェクトを変換します。
  • 各キーフィールドが等しいかどうかを判断する。
public class ShallowCloneExample implements Cloneable {
    private int[] arr;

    public ShallowCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ShallowCloneExample clone() throws CloneNotSupportedException {
        return (ShallowCloneExample) super.clone();
    }
}

ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222


ハッシュコード()

hashCode()はハッシュ値を返し、equals()は2つのオブジェクトが等価かどうかを判断するために使用されます。等価な2つのオブジェクトは同じハッシュ値を持たなければなりませんが、同じハッシュ値を持つ2つのオブジェクトは必ずしも等価ではありません。

equals() メソッドは、常に hashCode() メソッドでオーバーライドして、ハッシュが等しい2つのオブジェクトが等しいことを確認する必要があります。

次のコードでは、2つの等価なオブジェクトが新たに作成され、HashSetに追加されています。2 つのオブジェクトを同じものとして扱い、1 つのオブジェクトだけをコレクションに追加したいのですが、EqualExample は hasCode() メソッドを実装していないため、2 つのオブジェクトのハッシュ値が異なり、結局 2 つの等価オブジェクトがコレクションに追加されています。

public class DeepCloneExample implements Cloneable {
    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}


DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2


理想的なハッシュ関数は一様であるべきで、すなわち、不等間隔のオブジェクトはすべての可能なハッシュ値にわたって均等に分散されるべきである。このためには、ハッシュ関数がすべてのドメインの値を考慮し、各ドメインをRのビットとして扱い、R-indexの整数に形成する必要がある。rは奇数素数であり、偶数であれば2倍することは1ビット左にシフトすることと等しいため、乗算オーバーフローの際に情報が失われることから一般には31とされる。

31の掛け算は、シフトと引き算に変換できます。 public class CloneConstructorExample { private int[] arr; public CloneConstructorExample() { arr = new int[10]; for (int i = 0; i < arr.length; i++) { arr[i] = i; } } public CloneConstructorExample(CloneConstructorExample original) { arr = new int[original.arr.length]; for (int i = 0; i < original.arr.length; i++) { arr[i] = original.arr[i]; } } public void set(int index, int value) { arr[index] = value; } public int get(int index) { return arr[index]; } } CloneConstructorExample e1 = new CloneConstructorExample(); CloneConstructorExample e2 = new CloneConstructorExample(e1); e1.set(2, 222); System.out.println(e2.get(2)); // 2 この最適化は、コンパイラが自動的に行います。

final int x = 1;
// x = 2; // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;


toString()

デフォルトはこの形式のToStringExample@4554617cを返し、@の後の値はハッシュコードの符号なし16進数表現である。

public class A {
    private int x; // Instance variable
    private static int y; // Static variables

    public static void main(String[] args) {
        // int x = A.x; // Non-static field 'x' cannot be referenced from a static context
        A a = new A();
        int x = a.x;
        int y = A.y;
    }
}


public abstract class A {
    public static void func1(){
    }
    // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static'
}


public class A {
    private static int x;
    private int y;

    public static void func1(){
        int a = x;
        // int b = y; // Non-static field 'y' cannot be referenced from a static context
        // int b = this.y; // 'A.this' cannot be referenced from a static context
    }
}


clone()

1. 複製可能

clone() は、Object の protected メソッドです。public ではないので、クラスが明示的に clone() をオーバーライドしていない場合、他のクラスはそのクラスインスタンスの clone() メソッドを直接呼び出すことはできません。

public class A {
    static {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
    }
}

123


public class OuterClass {
    class InnerClass {
    }

    static class StaticInnerClass {
    }

    public static void main(String[] args) {
        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}


clone()を書き換えて、以下のような実装にします。

import static com.xxx.ClassName.*


public static String staticField = "staticVariable";

static {
    System.out.println("static statement block");
}

public String field = "instance variable";

{
    System.out.println("Common statement block");
}



public InitialOrderTest() {
    System.out.println("constructor");
}


CloneExample が Cloneable インターフェースを実装していないため、上記は CloneNotSupportedException をスローします。

clone() メソッドは Cloneable インターフェースのメソッドではなく、Object の protected メソッドであることに注意が必要です。Cloneable インターフェースは、単に、クラスが Cloneable インターフェースを実装していない場合に clone() メソッドを呼び出すと、CloneNotSupportedException が発生すると述べています。

Class.forName("com.mysql.jdbc.Driver")

2.浅いコピー

コピーされたオブジェクトと元のオブジェクトの参照型が同じオブジェクトを参照している。

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}


ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222


3.ディープコピー

コピーされたオブジェクトと元のオブジェクトの参照型が、異なるオブジェクトを参照している。

public class DeepCloneExample implements Cloneable {
    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}



DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2


4. clone() の代替となるもの

Effective Java bookによると、clone()は避け、コピーコンストラクタやコピーファクトリを使用してオブジェクトをコピーするのが良いとのことです。

public class CloneConstructorExample {
    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}


CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2


キーワード

最終

1.データ

データを定数として宣言します。コンパイル時の定数、または実行時に初期化された後に変更できない定数です。

  • 基本型の場合、finalは値を変更しない。
  • 参照型の場合、finalは参照を変更しない。つまり、他のオブジェクトを参照することはできないが、参照されたオブジェクト自体を変更することは可能である。
final int x = 1;
// x = 2; // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;


2. メソッド

メソッドをサブクラスでオーバーライドできないことを宣言します。

プライベートメソッドは暗黙のうちにfinalと指定されます。サブクラスで定義されたメソッドがベースクラスのプライベートメソッドと同じシグネチャを持つ場合、そのサブクラスのメソッドはベースメソッドをオーバーライドするのではなく、サブクラスで新しいメソッドを定義していることになります。

3.クラス

クラスの継承を認めないことを宣言する。

スタティック

1. 静的変数

  • 静的変数。クラス変数とも呼ばれ、その変数がクラスに属し、クラスのすべてのインスタンスで共有され、クラス名で直接アクセスできることを意味します。静的変数は、メモリ内に1つのコピーしか存在しません。
  • インスタンス変数。インスタンス変数は、インスタンスが生成されるごとに生成され、インスタンスとともに生死する。
public class A {
    private int x; // Instance variable
    private static int y; // Static variables

    public static void main(String[] args) {
        // int x = A.x; // Non-static field 'x' cannot be referenced from a static context
        A a = new A();
        int x = a.x;
        int y = A.y;
    }
}


2. 静的メソッド

静的メソッドは、クラスがロードされたときに存在し、どのインスタンスにも依存しないメソッドです。つまり、静的メソッドは実装を持たなければならず、抽象的なメソッドにはなりえません。

public abstract class A {
    public static void func1(){
    }
    // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static'
}


所属するクラスの静的フィールドと静的メソッドのみアクセス可能で、メソッドにthisキーワードとsuperキーワードを含めることはできません。

public class A {
    private static int x;
    private int y;

    public static void func1(){
        int a = x;
        // int b = y; // Non-static field 'y' cannot be referenced from a static context
        // int b = this.y; // 'A.this' cannot be referenced from a static context
    }
}


3. 静的ステートメントブロック

静的ステートメントブロックは、クラスの初期化時に一度だけ実行されます。

public class A {
    static {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
    }
}


123


4. 静的内部クラス

非静的内部クラスは外部クラスのインスタンスに依存しますが、静的内部クラスはそうではありません。

public class OuterClass {
    class InnerClass {
    }

    static class StaticInnerClass {
    }

    public static void main(String[] args) {
        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}


静的な内部クラスは、外部クラスの非静的な変数やメソッドにアクセスすることはできません。

5. 静的ラッパー

静的変数やメソッドを使用する際にClassNameを指定する必要がなくなるため、コードがシンプルになりますが、可読性はかなり下がります。

import static com.xxx.ClassName.*


6. 初期化順序

静的変数と静的ステートメントブロックは、インスタンス変数と通常のステートメントブロックより優先され、静的変数と静的ステートメントブロックの初期化の順序は、コード内の配置順序に依存します。

public static String staticField = "staticVariable";


static {
    System.out.println("static statement block");
}


public String field = "instance variable";


{
    System.out.println("Common statement block");
}



コンストラクタの初期化は最後になります。

public InitialOrderTest() {
    System.out.println("constructor");
}


継承がある場合、初期化順序は :

  • 親クラス(静的変数、静的ステートメントブロック)
  • サブクラス(スタティック変数、スタティックブロック)
  • 親クラス(インスタンス変数、通常文ブロック)
  • 親クラス(コンストラクタ)
  • サブクラス (インスタンス変数、共通ステートメントブロック)
  • サブクラス(コンストラクタ)

リフレクション

各クラスには  クラス  オブジェクトがあり、このオブジェクトにはクラスに関する情報が含まれています。新しいクラスがコンパイルされると、同じ名前の .class ファイルが生成され、その中にクラスオブジェクトが格納されます。

クラスの読み込みは、クラスオブジェクトの読み込みと同じです。クラスがJVMに動的にロードされるのは、最初に使用されるときだけで、ロードには Class.forName("com.mysql.jdbc.Driver") この方法では、クラスの読み込みを制御し、メソッドはクラス・オブジェクトを返します。

Reflectionは実行時のクラス情報を提供し、コンパイル時にクラスの.classが存在しなくても、実行時にクラスを読み込むことができる。

クラスとjava.lang.reflectが一緒になってリフレクションのサポートを提供します。java.lang.reflectクラスライブラリは、3つの主要なクラスを含んでいます。

著作権について https://www.pdai.tech所有. リンク Java基礎知識 - 知識のポイント|Javaフルスタック知識体系

  • フィールド : Field オブジェクトに関連付けられたフィールドは、get() および set() メソッドを使用して読み取ったり変更したりすることができます。
  • メソッド : メソッドオブジェクトに関連付けられたメソッドは、invoke()メソッドを使用して呼び出すことができます。
  • コンストラクタ : コンストラクタは、新しいオブジェクトを作成するために使用することができます。

Reflectionを使用する利点。

  • 拡張性機能 : アプリケーションは、拡張性オブジェクトの完全修飾名を使用してインスタンスを作成することにより、外部のユーザー定義クラスを使用することができます。
  • クラスブラウザとビジュアル開発環境 クラスブラウザは、クラスのメンバーを列挙できる必要がある。視覚的な開発環境は正しいコードを書くために開発者を助けるためにリフレクションで利用可能な型情報を利用することから利益を得ることができます。
  • デバッガとテストツール テストハーネスはリフレクションを利用して、発見可能なAPIのセットを系統的に呼び出すことができます テストハーネスはリフレクションを利用して、クラスで定義された発見可能なAPIのセットを系統的に呼び出し、テストスイートの高いレベルのコードカバレッジを保証することができます。

リフレクションの欠点

Reflectionは強力ですが、無差別に使用するべきではありません。もし、リフレクションを使わずに操作を行うことが可能であれば、リフレクションを使わない方が望ましいでしょう。リフレクションを使ってコードにアクセスするときは、次のような懸念に注意する必要があります。

  • <強い パフォーマンスオーバーヘッド : その結果、反射的操作は非反射的操作よりも遅いパフォーマンスを持ち、パフォーマンスに敏感なアプリケーションで頻繁に呼び出されるコードのセクションで避けるべきです。

  • セキュリティに関する制限 : Reflection は実行時のパーミッションが必要ですが、 セキュリティマネージャの下で実行されている場合は存在しない可能性があります。これは、アプレットのような制限されたセキュリティコンテキストで実行する必要があるコードにとって重要な考慮点です。

  • <強い 内部構造の公開 リフレクションは、プライベートなフィールドやメソッドにアクセスするような、 リフレクションを使わないコードでは違法となる操作を実行することを可能にするので、 リフレクションの使用は予期せぬ副作用をもたらし、コードを機能不全に陥らせ、 移植性を破壊する可能性があります。

例外事項

Throwableは例外として投げることができる任意のクラスを表現するために使用することができ、2つのタイプがあります。  エラー  と  例外 . Errorは、JVMが処理できないエラーを示すために使用されるのに対し、Exceptionには2つのタイプがあります。

  • チェックされた例外 : try... .catch... 文を使用して、例外を捕捉して処理し、例外から回復できるようにする必要があります。
  • チェックされていない例外 : 0 の除算で発生する算術例外のような、プログラムがクラッシュして復旧できない実行時エラーです。

一般化

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}


アノテート

Javaアノテーションは、いくつかのツールがコンパイル時や実行時に解析して利用したり、指示や設定として機能するためにコードに付加されるメタ情報です。アノテーションは、コードの実際のロジックには影響を与えず、また与えることができず、補助的な目的にのみ使用されます。

アノテーション アノテーションの実装原理とカスタムアノテーションの例

機能紹介

Java の各バージョンの新機能

著作権についてhttps://www.pdai.tech所有。リンク Java基礎知識 - 知識ベース|Javaフルスタック知識体系

Java SE 8の新しいハイライト

  1. ラムダ式
  2. パイプラインとストリーム
  3. 日付と時刻のAPI
  4. デフォルトのメソッド
  5. タイプアノテーション
  6. Nashhorn JavaScript エンジン
  7. 並列アキュムレータ
  8. 並列演算
  9. PermGen エラーの除去

Java SE 7 の新しいハイライト

  1. Switchステートメントでの文字列
  2. 一般的なインスタンス生成のための型推論
  3. 多重例外処理
  4. 動的言語への対応
  5. リソースで試す
  6. Java nioパッケージ
  7. バイナリ・リテラル、リテラル内のアンダースコア
  8. ダイヤモンド構文

JavaとC++の違い

  • Javaは純粋なオブジェクト指向言語であり、すべてのオブジェクトはjava.lang.Objectを継承します。一方、C++はCとの互換性のためにオブジェクト指向とプロシージャ指向の両方をサポートします。
  • Javaは仮想マシンによってクロスプラットフォーム機能を実現するが、C++はプラットフォームに依存する。
  • Javaにはポインタがなく、その参照は安全なポインタとして理解されるのに対し、C++にはCと同じポインタがあります。
  • Javaは自動ガベージコレクションをサポートしていますが、C++は手動での収集が必要です。
  • Javaは多重継承をサポートしておらず、複数のインタフェースを実装することでしか同じことを実現できませんが、C++は多重継承をサポートしています。
  • Javaは演算子のオーバーロードをサポートしておらず、2つのStringオブジェクトの加算をサポートしていますが、これは言語サポートの組み込み操作であり、演算子のオーバーロードではありません、一方C++はサポートしています。
  • Java の goto は予約語ですが使えません。C++ は goto を使うことができます。
  • Javaは条件付きコンパイルをサポートしていません。C++は#ifdef #ifndefのような前処理コマンドを使用して条件付きコンパイルをサポートしています。

JavaとC++の主な違いは何ですか?

JREまたはJDK

  • JREはJVMプログラムであり、JavaアプリケーションはJRE上で動作する必要があります。
  • JDKはJREのスーパーセットで、JREにJavaプログラムを開発するためのツールを追加したものです。

参考文献

  • Eckel B. Java プログラミングのアイデア [M]. 機械工業出版社, 2002.
  • Bloch J. Effective java [M]. アディソンウェスリープロフェッショナル, 2017.