はじめに
現在、Javaの基礎を補うために、私が主導して勉強会を行っております。 勉強会を進めるためにまとめた内容の一部をこのように文章にしてみました。
内容に誤りや不正確な部分があるかもしれませんので、フィードバックをいただけると幸いです。
- 韓国人として、日本語とコンピュータの勉強を同時に行うために、ここに文章を書いています
- 翻訳ツールの助けを借りて書いた文章なので、誤りがあるかもしれません
順序
- プリミティブ型の種類と値の範囲、そしてデフォルト値
- プリミティブ型と参照型
- リテラル
- 変数の宣言および初期化の方法
- 変数のスコープとライフサイクル
- 型変換、キャスティング、そして型プロモーション
- ラッパークラス
- オートボクシング、アンボクシング
- var
プリミティブ型の種類と値の範囲、そしてデフォルト値
Javaプログラミング言語は静的型付け言語です。つまり、すべての変数は使用する前に必ず型を宣言しなければなりません。
int sum = 1; // このように事前に型を宣言する必要があります
Javaは静的型付け言語であるため、上記の例のように型を宣言する必要があり、これはコンパイル時に決定されます。
sum = 1; // Pythonでは変数の型を宣言しません。実行時に型が決定されます。
一方で、Pythonのような動的型付け言語では、実行時に型が決定されます。
Primitive Data(プリミティブデータ) = 基本データ型(原始型)
プリミティブ型
型 | サイズ | 値の範囲 | デフォルト値 |
---|---|---|---|
byte |
8ビット 符号付き整数型 | -128 ~ 127 (-2^7 ~ 2^7 - 1) | 0 |
short |
16ビット 整数型 | -32,768 ~ 32,767 (-2^15 ~ 2^15 - 1) | 0 |
int |
32ビット 整数型 | -2,147,483,648 ~ 2,147,483,647 (-2^31 ~ 2^31 - 1) | 0 |
long |
64ビット 整数型 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 (-2^63 ~ 2^63 - 1) | 0L |
float |
32ビット 浮動小数点型 | -3.4028235 × 10^38 ~ 3.4028235 × 10^38 (小数点6~7桁) | 0.0f |
double |
64ビット 浮動小数点型 | -1.7976931348623157 × 10^308 ~ 1.7976931348623157 × 10^308 (小数点15桁) | 0.0d |
boolean |
true/falseの論理型 | 1ビット以上 (JVMの割り当てによって異なる) | false |
char |
16ビット ユニコード文字 | '\u0000' ~ '\uffff' (0 ~ 65,535) | '\u0000' |
プリミティブ型はnullにはなりません!
各型のビット処理方法
ビットとは?
ビットは、コンピュータが情報を保存・処理する際の最小単位です。ビットは0または1の二つの値を持ち、二進法に基づいてすべてのデータを表現します。複数のビットを組み合わせることで、より大きなデータを表現できます。例えば、8ビットは**1バイト(byte)**を構成し、256種類の値を表現することができます(2^8 = 256)。
int
- S : 1ビットは符号ビット(正数/負数を表す)
- 残りの31ビットは数値の値を表します
| 31 | 30-0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| S | value bits |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
float
- S : 1ビットは符号ビット(正数/負数を表す)
- E : 8ビットは指数
- M : 23ビットは仮数
| 31 | 30-23 | 22-0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| S | EEEEEEEE| MMMMMMMMMMMMMMMMMMMMMMM|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
double
- S : 1ビットは符号ビット(正数/負数を表す)
- E : 11ビットは指数
- M : 52ビットは仮数
| 63 | 62-52 | 51-0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| S | EEEEEEEEEEE | MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
指数? 仮数とは?
仮数 : 実際の値、有効な桁数
指数 : 数の大きさ
- 0.00000000000123 == 1.23 × 10^-12
- 1.23 仮数、12 指数
より大きな数を表現する
Java SE 8以降、unsignedが追加されました。
これは、以前のビットのS(正負を表すビット)を正数に固定し、追加の1ビットに値を入れることができる機能です。
1ビット分、より大きな数を表現できます
Javaの基本型仕様とは?
CやC++のようなコンパイル言語では、intなどの型が、OSの要求に応じて16ビット、32ビット、64ビットになることがあります。 しかし、JavaはWORA(Write Once, Run Anywhere)の原則に従い、どこでも同じようにJVM上で動作するため、JVMの標準ドキュメントで定義された値に従ってビットが割り当てられます。 そのため、どこでコンパイル・実行しても、intは常に32ビットとなります。
プリミティブ型と参照型
プリミティブ型 (Primitive Types)
特徴: 値そのものをメモリに保存し、合計8種類の型があります。
-
整数型:
-
byte
(8ビット) -
short
(16ビット) -
int
(32ビット) -
long
(64ビット)
-
-
浮動小数点型:
-
float
(32ビット) -
double
(64ビット)
-
-
文字型:
-
char
(16ビットのユニコード文字)
-
-
論理型:
-
boolean
(1ビット、true
またはfalse
の値のみ可能)
-
レファレンス型 (Reference Types)
特徴: オブジェクトのメモリアドレスを保存し、オブジェクト自体はヒープメモリに保存されます。
String
、Integer
、List
、その他のObject
、array
、interface
、class
リテラル
リテラルは、ソースコードで固定された値を表現する方法で、計算を必要とせず、コードに直接的に表現されます。
基本データ型(プリミティブ型)を初期化する際、new
キーワードは使用されません。これは、基本型がクラスから生成されたオブジェクトではなく、言語に組み込まれた基本データ型だからです。
boolean result = true;
char capitalC = 'C';
byte b = 100;
short s = 10000;
int i = 100000;
このように、リテラルは変数に値を指定する際に使用され、別途計算やオブジェクト生成を必要としません。
整数リテラル
整数リテラルは、値がL
またはl
で終わる場合、long
型として認識されます。そうでない場合は、基本的にint
型です。
long a = 1L; // long type
int b = 1; // int type
lとLはどちらもlong
として認識されますが、小文字のlは数字の1と混同される可能性があるため、Lを使用することを推奨します。
byte
、short
、int
、long
型の値はint
リテラルで生成できます。long
型の値のうち、int
の範囲を超える値はlong
リテラルで生成する必要があります。
整数リテラルは、次のような数体系で表現できます。
- 10進数 (Decimal)
int a = 26;
- 16進数 (Hexadecimal)
int a = 0x1a; // 16進数で数字26
- 2進数 (Binary)
int a = 0b11010 // 2進数で26
부동소수점 리터럴 (Floating-Point Literals)
부동소수점 리터럴은 F, f로 끝나면 float
타입으로 인식됩니다.
해당 방식대로 끝나지 않는다면 doulbe
타입으로 인식됩니다.
double
의 리터럴은 D또는 d로 끝날 수 있지만, 보통 생략합니다.
double a = 1.1D;
double b = 1.1;
float c = 1.1f;
浮動小数点型は、Eまたはeを使用して科学的記法で表現できます。
// 科学的記法を使用した同じ値
double a = 1.234e2;
double a = 1.1D;
double b = 1.1;
float c = 1.1f;
浮動小数点型は、E
またはe
を使用して科学的記法で表現できます。
// 科学的記法を使用した同じ値
double a = 1.234e2;
Java SE 7以降、より大きな数値を宣言する際に便利なように_
が導入されました。
100,000
のように大きな数を区切る際に,
を使うように、
int a = 100_000;
このように宣言することができます。複数回使用しても問題ありません。
int a = 10____________000;
この_はすべての数値型で使用可能です。
ただし、4つのルールがあります。
数値の開始や終了時には_を使用できません(例:_100)。
小数点の前後では使用できません(例:3._14)。
floatやlong型を表す接尾辞の前には使用できません(例:10_L、3.14_f)。
進数表記で数字が予想される場所には使用できません(例:0_x52、0x_52)。
文字および文字列リテラル (Character and String Literals)
char
とString
型のリテラルは、UTF-16ユニコード文字を含むことができます。
エディタやファイルシステムでこれがサポートされていない場合、ユニコードエスケープ構文を使用できます。
char c = '\u0108'; // ユニコード文字 C with circumflex
String s = "S\u00ED Se\u00F1or"; // ユニコード文字列 "Sí Señor"
- \b : バックスペース
- \t : タブ
- \n : 改行 (new line)
- \f : フォームフィード
- \r : キャリッジリターン
- " : ダブルクォーテーション
- ' : シングルクォーテーション
- \ : バックスラッシュ
変数の宣言および初期化の方法
Javaは強力な静的型付け言語です。
前述の通り、すべての変数は使用される前に必ず宣言されなければなりません。
int a = 1;
このように宣言することで、aという名前のフィールドが存在し、int型のデータを保持し、初期値が1であることを示しています。
ラッパー型は次のように宣言します。
originOne = new Point(23, 94); // Pointオブジェクトを生成し初期化
変数のスコープとライフタイム
- スコープとは、変数が有効なコードの範囲です。
- ライフタイムとは、変数がメモリに存在する期間のことです。
プリミティブ型変数のスコープ
- クラス変数(Static変数)
- スコープ (Scope): クラス変数はクラス全体でアクセスできます。クラスがメモリにロードされた後、クラスがロードされているすべてのメソッド、オブジェクト、または他のクラスからアクセス可能です
- ライフタイム (Lifetime): クラスがメモリにロードされるときに割り当てられ、プログラムが終了するかクラスがアンロードされるまでメモリに保持されます
static int classVariable; // クラスがロードされた瞬間からプログラム終了時まで存在します。
- インスタンス変数 (Non-staticフィールド)
- スコープ (Scope): インスタンス変数は、そのクラスのオブジェクトが生成された後、オブジェクト内のすべてのメソッドでアクセスできます。同じクラスの他のインスタンスとはこの変数を共有しません
- ライフタイム (Lifetime): オブジェクトが生成されたときに割り当てられ、オブジェクトがGCによってメモリから削除されるまで存在します
int instanceVariable; // オブジェクトが存在する間のみメモリに存在
- ローカル変数 (Local Variable)
- スコープ (Scope): ローカル変数は宣言されたブロック内でのみ有効です。たとえば、メソッドやfor、whileブロック内で宣言された変数は、そのブロックが終了するとアクセスできなくなります
- ライフタイム (Lifetime): メソッドやブロックが実行されるときにメモリに割り当てられ、メソッドが終了するとメモリから削除されます
void someMethod() {
int localVariable = 10; // メソッド実行中のみ有効でメモリに存在
}
for(int i = 0; i <= 10; i++){
ここでは、メソッド実行中のみ`int i`がメモリに存在し、有効です。
}
- パラメータ (Parameter)
- スコープ (Scope): パラメータは、そのメソッド内でのみ有効です。メソッドが呼び出される際に渡された値を使用し、メソッドが終了するとパラメータは消えます
- ライフタイム (Lifetime): メソッドが呼び出されるとメモリに割り当てられ、メソッドが終了するとメモリから削除されます
void someMethod(int parameter) {
// parameterはメソッドが実行されている間のみ存在します。
}
まとめ
- クラス変数はクラス全体でアクセス可能で、プログラムが終了するまで存在します
- インスタンス変数はオブジェクト内でのみアクセス可能で、オブジェクトのライフサイクル中存在します
- ローカル変数はそのブロック内でのみ有効で、ブロックが終了すると消えます
- パラメータはメソッド内でのみ有効で、メソッド実行が終了すると消えます
型変換、キャスティング、そして型プロモーション
型変換 (Type Conversion)
型変換とは、ある型のデータを別の型に変換する過程のことです。
- 自動変換 (Widening Conversion): 小さなサイズのデータ型を大きなサイズのデータ型に変換する際に、Javaが自動的に行う変換です
int a = 10; long b = a; // 自動変換 (Widening)
- 強制変換 (Narrowing Conversion): 大きなサイズのデータ型を小さなサイズのデータ型に変換する際に使用され、明示的に変換を行う必要があります
long a = 10; int b = (int) a; // 強制変換 (Narrowing)
強制変換時にデータを明示的に変換する理由
大きなデータ型を小さなデータ型に変換する際、値の損失が発生する可能性があるため、強制的に変換する必要があります。
キャスティング
キャスティングは、型変換の中で明示的な変換を行う際に使用される方法です。
- アップキャスティング (Upcasting): 子クラス型を親クラス型に変換すること。この場合は暗黙的(自動的)に行われます
class Animal { } class Dog extends Animal { } Dog dog = new Dog(); Animal animal = dog; // 自動アップキャスティング
- ダウンキャスティング (Downcasting): 親クラス型を子クラス型に変換すること。明示的にキャスティングを行う必要があります
Animal animal = new Dog(); Dog dog = (Dog) animal; // 明示的ダウンキャスティング
- 型プロモーション (Type Promotion): 型プロモーションは演算過程で自動的に発生する型変換です。小さなサイズの型が大きなサイズの型に自動的に昇格され、演算が行われます
byte a = 10; int b = 20; int result = a + b; // byte型が自動的にintに昇格され演算
まとめ
- 型変換: 自動変換(小さな型から大きな型へ)または明示的変換(大きな型から小さな型へ)
- キャスティング: 型変換を明示的に行う方法
- 型プロモーション: 演算中に小さな型が自動的に大きな型に昇格して演算される過程
ラッパークラス
Java言語は基本的にオブジェクト指向言語であるため、基本型の値をオブジェクトのように扱わなければならない場合が多くあります。そのため、Javaではラッパークラスがサポートされています。
Primitive type | Wrapper class |
---|---|
boolean |
Boolean |
byte |
Byte |
char |
Character |
float |
Float |
int |
Integer |
long |
Long |
short |
Short |
double |
Double |
この他に、4つのNumber
クラスのサブクラスがあります。
-
BigDecimal
: 高精度な浮動小数点演算をサポートするクラス -
BigInteger
: 大きな整数演算を処理するために使用され、int
やlong
の範囲を超える大きな数値を扱うことができます -
AtomicInteger
: マルチスレッド環境で、同期化なしで整数値をアトミックに増減などの演算ができるようにします -
AtomicLong
:AtomicInteger
と同様ですが、long
型の値を扱います
// BigDecimal 宣言
BigDecimal bigDecimal = new BigDecimal("12345.6789101010101");
// BigInteger 宣言
BigInteger bigInteger = new BigInteger("123456789012345678901234567890");
// AtomicInteger 宣言
AtomicInteger atomicInteger = new AtomicInteger(100);
// AtomicLong 宣言
AtomicLong atomicLong = new AtomicLong(100000L);
There are three reasons that you might use a Number object rather than a primitive:
As an argument of a method that expects an object (often used when manipulating collections of numbers).
To use constants defined by the class, such as MIN_VALUE and MAX_VALUE, that provide the upper and lower bounds of the data type.
To use class methods for converting values to and from other primitive types, for converting to and from strings, and for converting between number systems (decimal, octal, hexadecimal, binary).
The following table lists the instance methods that all the subclasses of the Number class
オラクルでは、Number
クラスをプリミティブ(原始型)の代わりに使用する3つの主な理由を説明しています。
-
メソッド引数としてオブジェクトを要求する場合
- Javaは基本的にオブジェクト指向言語であり、多くのAPIやコレクションクラスがプリミティブ型ではなくオブジェクトとして引数を受け取ります
List<Integer> aList = new ArrayList<>();
-
定数の使用
-
Number
関連クラスは定数(MIN_VALUE
、MAX_VALUE
)を定義し、対応するデータ型の最大値と最小値を提供します
-
-
メソッドの使用
-
Number
クラスは、プリミティブ型間の変換、文字列との変換、または数値システム(10進数、8進数、16進数、2進数)間の変換を行うためのメソッドを提供します
-
オートボクシング、アンボクシング
オートボクシングとは、Javaコンパイラが基本型とラッパークラスの間で自動的に変換を行うことを指します。
int a = 100;
Integer b = a; // オートボクシング
Double c = 3.14;
double d = c; // アンボクシング
ラッパークラスのキャッシング
ラッパークラスのうち、 Integer
, Byte
, Short
, Character
, Long
は、特定の範囲をキャッシングしてパフォーマンスを最適化します。
Integer
, Byte
, Short
, Long
: -128~127までキャッシングします。
Character
: 0~127までのユニコード値をキャッシングします。
そのため、次のようなコードが成り立ちます。
次のコードは、
すべてtrue
が返されます。
つまり、キャッシングをしているため、すべて同じメモリアドレスを参照しているということです。
このように異なるメモリアドレスを参照していることが返されます。
これについては、各ラッパークラスのコメントを見るとより理解が深まります。
このように、特定範囲の値をパフォーマンスのためにキャッシングすると明示されています。
このキャッシング範囲はオプションで調整可能です。
var
Javaのボイラープレートコードを減らし、過剰な式の使用を抑えるために追加された構文です。var
はローカル変数内でのみ使用できます。
-
var
はコンパイル時に型を推論します - そのため、実行時にはパフォーマンスに差が生じません
var a; // コンパイルエラー発生
var name = "haroya"; // コンパイラがStringと推論
var age = 24; // コンパイラがintと推論
var price = 19.99; // コンパイラがdoubleと推論
var list = new ArrayList<String>();
もちろん、型が推論できないものは使用できません。
null
などが含まれます。
var name = null; // コンパイルエラー発生