Java の特徴#
- オブジェクト指向。カプセル化、ポリモーフィズム、継承
- 良好な互換性(Java 仮想マシンはクロスプラットフォームを実現し、一度コンパイルすれば多くのプラットフォームで使用可能)
- マルチスレッドをサポート
- ネットワークプログラミングをサポート
- コンパイルと解釈の共存
Write Once, Run Anywhere
JVM#
Java 仮想マシンは、Java バイトコードを実行するための仮想マシンです。JVM は異なるシステムに特化した実装があり、同じバイトコードを使用して同じ実行結果を得ることを目的としています。これは一度コンパイルし、どこでも実行できることを示しています。
JRE と JDK#
JDK(Java Development Kit)は、フル機能の Java SDK です。JRE を含むだけでなく、コンパイラ javac、ツール javadoc、jdb も含まれています。
JRE は Java プログラムの実行時環境であり、コンパイル済みの Java プログラムを実行するために必要な内容の集合です。JVM、Java クラスライブラリ、Java コマンド、およびいくつかの基本コンポーネントが含まれています。通常、Java プログラムを実行するには JRE で十分ですが、Java プログラムを開発またはコンパイルする必要がある場合は JDK を使用する必要があります。
JSP を使用して WEB プログラムをデプロイする場合は、JDK が必要です。アプリケーションサーバーは JSP を Java Servlet に変換し、Servlet をコンパイルするために JDK が必要です。
バイトコード#
JVM が理解できるコードはバイトコードと呼ばれ、拡張子は.class のファイルです。
バイトコードは、従来のインタプリタ型言語の実行効率の低さの問題を解決し、インタプリタ型言語の利点を保持します。
コンパイルと解釈の共存#
コンパイル型:コンパイラを通じてすべてのコードを一度に機械語に翻訳します。実行速度は速いが、開発効率は低い。例:C、C++、Go、Rust。
解釈型:インタプリタを通じてコードを一行ずつ機械語に解釈して実行します。開発は早いが、実行速度は遅い。例:Python、PHP、JS。
Java 言語は、コンパイル型言語の特徴と解釈型言語の特徴の両方を持っています。Java プログラムは、最初にコンパイルされ、その後解釈される 2 つのプロセスを経る必要があります。
コンパイルによってバイトコード.class ファイルが生成され、その後 Java インタプリタによって解釈されて実行されます。
基本構文#
continue、break と return#
continue:現在のループを抜け、次のループを続行します。
break:全体のループ体を抜け、ループ体の下のコードを続行します。
return:現在のメソッドを抜け、そのメソッドの実行を終了します。
メンバー変数とローカル変数#
- 構文形式:メンバー変数はクラスに属し、ローカル変数はメソッドに属します。メンバー変数は public、private、static 修飾子で修飾できますが、ローカル変数はできません。両者とも final で修飾できます。
- ストレージ方法:メンバー変数が static で修飾されている場合、そのメンバー変数はクラスに属します。static で修飾されていない場合、メンバー変数はインスタンスに属します。オブジェクトはヒープに保存され、ローカル変数はスタックに保存されます。
- 生存時間:メンバー変数はオブジェクトの一部であり、オブジェクトの作成とともに作成されます。ローカル変数はメソッド呼び出しに伴って生成され、メソッドの使用が終了すると終了します。
- デフォルト値:メンバー変数が初期値を与えられていない場合、型のデフォルト値が自動的に与えられます(例えば、boolean のデフォルトは false、int のデフォルトは 0、string のデフォルトは null)。ローカル変数は自動的に値が与えられません。
静的変数#
静的変数はクラスのすべてのインスタンスで共有されます。クラスがいくつのオブジェクトを作成しても、それらは同じ静的変数を共有します。
通常、静的変数はfinal
キーワードで修飾されて定数になります。
文字定数と文字列定数#
- 形式 : 文字定数は単一引用符で囲まれた 1 つの文字であり、文字列定数は二重引用符で囲まれた 0 個または複数の文字です。
- 意味 : 文字定数は整数値(ASCII 値)に相当し、式の計算に参加できます。文字列定数はアドレス値(その文字列がメモリに保存されている位置)を表します。
- メモリサイズ : 文字定数は 2 バイトのみを占有します。文字列定数は複数のバイトを占有します。
静的メソッドが非静的メンバーを呼び出せない理由#
静的メソッドはクラスに属し、クラスがロードされるときに作成されます。非静的メンバーはインスタンスに属し、オブジェクトがインスタンス化されるときに作成されます。
静的メソッドは非静的メンバーよりも先に作成されます。
静的メソッドとインスタンスメソッドの違い#
呼び出し方法:静的メソッドはクラス名.メソッド名
で呼び出します。インスタンスメソッドはオブジェクト.メソッド名
で呼び出します。
オーバーライドとオーバーロード#
オーバーロード#
同じクラス内でのみ、メソッド名は同じで、パラメータの型、数、順序が異なり、メソッドの戻り値やアクセス修飾子は異なることができます。
オーバーライド#
実行時に発生します。
- メソッド名とパラメータリストは同じで、戻り値の型も同じでなければなりません。
- 親クラスのアクセス修飾子が
private/final/static
の場合、子クラスはそのメソッドをオーバーライドできません。
基本データ型#
8 種類のデータ型#
- 6 種類の数値型
- 4 種類の整数型:
int\short\byte\long
- 2 種類の浮動小数点型:
float\double
- 4 種類の整数型:
- 1 種類の文字型:
char
- 1 種類のブール型:
boolean
デフォルト値と占有するメモリサイズ#
基本型 | ビット数 | バイト | デフォルト値 | 取値範囲 |
---|---|---|---|---|
byte | 8 | 1 | 0 | -128 ~ 127 |
short | 16 | 2 | 0 | -32768 ~ 32767 |
int | 32 | 4 | 0 | -2147483648 ~ 2147483647 |
long | 64 | 8 | 0L | -9223372036854775808 ~ 9223372036854775807 |
char | 16 | 2 | 'u0000' | 0 ~ 65535 |
float | 32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double | 64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
boolean | 1 | 1 | false | true、false |
long
型は数値の後にL
を付ける必要があります。さもなければ整数型として解析されます。
char a = 'h'
char : 単一引用符、String a = "hello"
: 二重引用符。
基本型とラッパー型の違い#
- 基本型のローカル変数はスタックに保存され、基本型のメンバー変数はヒープに保存されます。ラッパー型はオブジェクトに属し、ヒープに保存されます。
- ラッパー型のデフォルト値は null です。
- ラッパー型はジェネリクスに使用でき、基本型は使用できません。
ラッパー型のキャッシュメカニズム#
Byte
,Short
,Integer
,Long
の 4 つのラッパークラスは、デフォルトで数値 [-128,127] の対応する型のキャッシュデータを作成します。Character
は数値が [0,127] の範囲のキャッシュデータを作成します。Boolean
は直接True
またはFalse
を返します。Float
,Double
にはキャッシュがありません。
範囲を超える新しい変数を定義すると、新しいオブジェクトが作成されます。
ボクシングとアンボクシング#
- ボクシング:基本型を対応する参照型でラップします。
- アンボクシング:ラッパー型を基本データ型に変換します。
なぜ浮動小数点数の計算で精度が失われるのか#
無限小数はコンピュータで保存される際に切り捨てられ、結果として小数の精度が失われます。
浮動小数点数の精度損失をどう解決するか#
BigDecimal
を使用します。
lang
型を超えるデータをどう表現するか#
BigInteger
を使用します。
オブジェクト指向の基礎#
オブジェクト指向と手続き型#
- 手続き型:問題解決のプロセスを一つ一つのメソッドに分解し、それぞれのメソッドを実行して問題を解決します。
- オブジェクト指向:最初にオブジェクトを抽出し、オブジェクトを使ってメソッドを実行することで問題を解決します。
オブジェクト実体とオブジェクト参照#
オブジェクト参照:オブジェクトのインスタンス化
new
を使ってオブジェクトインスタンスを作成します(オブジェクトインスタンスはヒープに保存され、オブジェクト参照はスタックに保存されます)。
1 つのオブジェクト参照は 0 または 1 つのオブジェクトを指すことができます。
1 つのオブジェクトには n 個の参照が指し示すことができます。
コンストラクタ#
オブジェクトの初期化を行います。オブジェクトをインスタンス化する際、後ろの括弧は呼び出されるのが引数なしのコンストラクタであることを示します。
コンストラクタが宣言されていない場合、デフォルトで引数なしのコンストラクタが存在します。引数ありのコンストラクタが宣言されると、デフォルトの引数なしのコンストラクタは上書きされ、再度引数なしのコンストラクタを手動で宣言する必要があります。
コンストラクタはクラス名と同じでなければならず、オーバーライドできませんが、オーバーロードは可能です。
オブジェクト指向の特性、カプセル化、継承、ポリモーフィズム#
カプセル化#
カプセル化とは、オブジェクトの属性をオブジェクト内部に隠し、外部のオブジェクトがオブジェクトの内部情報に直接アクセスできないようにすることです。しかし、外部からアクセス可能なメソッドを通じて属性を操作することはできます。
継承#
- 子クラスは親クラスのすべての属性とメソッドを持っていますが、親クラスのプライベート属性やメソッドにはアクセスできません。
- 子クラスは新しい属性やメソッドを作成し、親クラスを拡張できます。
- 子クラスは独自の方法で親クラスのメソッドを実装できます。
ポリモーフィズム#
- コンパイル時ポリモーフィズム:メソッドのオーバーロードを指します。
- 実行時ポリモーフィズム:プログラム内で定義されたオブジェクト参照が指す具体的な型が実行時に決定されます。
ポリモーフィズムが存在する条件:継承、オーバーライド、親クラスの参照が子クラスのオブジェクトを指すこと(例:Animal a = new Cat()
)
インターフェースと抽象クラス#
共通点:
- インスタンス化できない
- 抽象メソッドを含むことができる
- デフォルトの実装メソッドを持つことができる(default キーワードを使用してインターフェース内で定義)
違い:
- 単一継承、多重実装
- インターフェースのメンバー変数は
public static final
型のみで、初期値を持たなければなりません。抽象クラスのメンバー変数はデフォルトで default であり、子クラスで再定義して値を設定できます。
深いコピー、浅いコピー、参照コピー#
- 浅いコピー:ヒープ上に新しいオブジェクトを作成しますが、元のオブジェクトの内部属性が参照型の場合、浅いコピーは内部オブジェクトの参照アドレスを直接コピーします。つまり、コピーされたオブジェクトと元のオブジェクトは同じ内部オブジェクトを共有します。
- 深いコピー:オブジェクト全体を完全にコピーし、そのオブジェクトが含む内部オブジェクトも含まれます。
- 参照コピー:2 つの異なる参照が同じオブジェクトを指します。
String オブジェクト#
== と equals () の違い#
- == は参照がヒープメモリの同じアドレスを指しているかどうかを判断します。
- equals はヒープ内の内容が同じかどうかを比較し、アドレスは比較しません。
hashCode () をオーバーライドする際は equals () もオーバーライドする必要がある#
異なるオブジェクトの hashCode が同じである可能性がありますが、hashCode が異なるオブジェクトは必ず異なります。
片方だけをオーバーライドすると、equals が同じでも hashCode が異なる場合が発生する可能性があります。
HashSet を使用する際、最初に HashCode が同じかどうかを判断し、同じ場合に equals メソッドを使用して同じかどうかを判断します。
String、StringBuffer、StringBuilder#
StringBuffer
はメソッドに同期ロックを追加しているため、スレッドセーフです。StringBuilder
はメソッドに同期ロックを追加していないため、スレッドセーフではありません。String
は不変(文字列の配列はfinal
で修飾され、プライベートです)であり、String
型を変更するたびに新しいString
オブジェクトが生成され、ポインタが新しいString
オブジェクトを指します。StringBuffer
は毎回StringBuffer
オブジェクト自体を操作し、新しいオブジェクトを生成してオブジェクト参照を変更することはありません。
使用方法:
- 少量のデータを操作する場合:
String
を使用 - 単一スレッドで文字列バッファを操作する場合:
StringBuilder
を使用 - 複数スレッドで文字列バッファを操作する場合:
StringBuffer
を使用
文字列の結合に+
を使用しない#
+
を使用して結合すると、実際にはStringBuilder
のappend()
メソッドを呼び出して実現されます。結合後にtoString()
を呼び出してString
オブジェクトを取得します。ループ内で+
を使用して結合すると、ループが 1 回実行されるごとに新しいStringBuilder
オブジェクトが作成されますが、手動でStringBuilder
を使用すればこの問題は発生しません。
String.equals () と Object.equals ()#
-
String
内のequals
メソッドはオーバーライドされており、String 文字列の値が等しいかどうかを比較します。 -
Object
のequals
メソッドはオブジェクトのメモリアドレスを比較します。
文字列定数プール#
文字列定数プールは、String クラスの重複作成によるメモリ消費を避けるために特別に設けられた領域です。
String 型のオブジェクトを作成すると、その内容がヒープに保存され、オブジェクトの参照が定数プールに保存されます。同じ String オブジェクトを再度作成する場合、ヒープに同じオブジェクトが既に存在する場合は、定数プールに保存された参照を直接返します。
String s1 = new String ("abc") は何個の文字列オブジェクトを作成するか#
文字列 "abc" が以前に作成されていれば、1 つの文字列オブジェクトが作成されます。文字列 "abc" が以前に作成されていなければ、2 つの文字列オブジェクトが作成されます。
intern メソッド#
intern メソッドは、指定された文字列オブジェクトの参照を文字列定数プールに保存します。
- 文字列定数プールに対応する文字列オブジェクトの参照が保存されている場合、その参照を直接返します。
- 文字列定数プールに対応する文字列オブジェクトの参照が保存されていない場合、定数プールにその文字列オブジェクトを指す参照を作成して返します。
String 型の変数と定数で「+」演算を行う#
- コンパイル時に値が確定できる文字列の場合、JVM は結合後の文字列参照を定数プールに置きます。
例えば:String str3 = "str" + "ing"
はString str3 = "string";
に最適化されます。
- コンパイル時に確定できない文字列の場合、
StringBuilder
がappend()
を呼び出し、その後toString()
を呼び出してString
を取得します。
例外#
Exception と Error#
Java には主に 2 種類の例外、Exception と Error があります。
- Exception:プログラム自体が処理できる例外で、catch で捕獲できます。Exception は Checked Exception(受検査例外、必ず処理する必要がある)と Unchecked Exception(非受検査例外、処理しなくてもよい)に分かれます。
- Error:プログラムが処理できない例外で、Error が発生するとスレッドが終了します。例えば、Java 仮想マシンの実行例外、メモリオーバーフロー、スタックオーバーフローなどです。
Checked Exception と Unchecked Exception#
- Checked Exception(受検査例外)は、コンパイル時に、受検査例外がcatchまたはthrowsされていない場合、コンパイルが通過しません。
- RuntimeException およびそのサブクラスを除き、他の Exception はすべて受検査例外に該当します。
- Unchecked Exception(非受検査例外)は、コンパイル時に処理しなくても構いません。
- RuntimeException およびそのサブクラスはすべて非受検査例外で、一般的なものには:NullPointerException、ArrayIndexOutOfBoundsException などがあります。
Throwable クラスの一般的なメソッド#
getMessage()
は例外の簡潔な説明を返します。toString()
は例外の詳細情報を返します。getLocalizedMessage()
は例外オブジェクトのローカライズされた情報を返します。printStackTrace()
はコンソールに Throwable オブジェクトがカプセル化した例外情報を印刷します。
try-catch-finally の使用#
- try:ユーザーが例外を捕獲し、その後には catch または finally のいずれかが必要です。
- catch:try で捕獲した例外を処理します。
- finally:例外を捕獲したかどうかにかかわらず、finally は必ず実行されます。try または catch 内に return がある場合、finally はメソッドが返される前に実行されます。
finally は必ず実行されるか#
必ずしもそうではありません。もし finally の前に JVM が終了した場合、finally は実行されません。また、プログラムが存在するスレッドが死亡した場合や CPU がシャットダウンした場合も実行されません。
ジェネリクス#
ジェネリクスの使用方法#
- ジェネリクスクラス
- ジェネリクスインターフェース
- ジェネリクスメソッド
リフレクション#
リフレクションとは#
プログラムの実行中に、オブジェクトが属するクラスを動的に取得し、クラスのメンバー変数やメソッドを取得し、クラスの属性やメソッドを呼び出します。
リフレクションが使用される場所#
Spring はアノテーション @Component を通じてクラスの情報、属性、パラメータなどを取得します。
リフレクションを使用してクラスを取得する方法#
-
クラス名.class
を使用Class alunbarClass = TargetObject.class;
-
Class.forName("クラス名")
を使用Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
-
メソッド
Object.getClass()
を使用TargetObject o = new TargetObject(); Class alunbarClass2 = o.getClass();
-
クラスローダー
xxxClassLoader.loadClass()
にクラスパスを渡して取得:ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
アノテーション#
アノテーションとは#
クラス、メソッド、変数を修飾でき、プログラムのコンパイルまたは実行時に使用する情報を提供します。
アノテーションの解析方法#
- コンパイル時にスキャン
- 実行時にリフレクションで処理
参照型#
参照型を通じてオブジェクトがガベージコレクタによって回収されるかどうかを判断します♻️
強い参照#
new
キーワードで作成されたオブジェクトは強い参照であり、強い参照のオブジェクトは GC によって回収されません。
ソフト参照#
ソフト参照オブジェクトは、メモリが不足している場合に回収されます。
SoftReference
キーワードを使用してソフト参照オブジェクトを作成できます。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
弱い参照#
弱い参照オブジェクトは直接回収され、作成された弱い参照オブジェクトは次回 GC が発生する前までしか生存できません。
WeakReference
キーワードを使用して弱い参照オブジェクトを作成できます。
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
仮想参照#
仮想参照を通じてオブジェクトを取得することはできず、仮想参照はオブジェクトに対して何の影響も与えません。
仮想参照の役割は、仮想参照のオブジェクトが GC によって回収されるときに通知を受けることです。
PhantomReference
キーワードを使用して仮想参照オブジェクトを作成できます。
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);