Java/基礎/変数と代入演算

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

Javaの変数と型システムは、Javaプログラムで使用されるデータの種類と保存方法を定義します。Javaは強い型付け言語であり、変数の型はコンパイル時に決定され、実行時には変更されません。Javaの型システムには、プリミティブ型と参照型の2つの種類があります。プリミティブ型は基本的な型で単一の値を保存するために使用され、参照型はオブジェクトへの参照を保存するために使用されます。

変数は、Javaプログラムでデータを保存するためのメモリ上の場所を表し、変数の型に基づいて宣言されます。変数には名前が付けられ、名前を使用してデータにアクセスできます。プリミティブ型と参照型の両方を使用できますが、プリミティブ型の変数は単一の値を保存するために使用され、参照型の変数はオブジェクトへの参照を保存するために使用されます。プリミティブ型の変数は値渡しであり、関数に引数として渡す場合は、値そのものがコピーされます。一方、参照型の変数は参照渡しであり、関数に引数として渡す場合は、オブジェクトへの参照が渡されます。

Javaの型システムは、コンパイル時にエラーを検出することで安全性を高め、実行時に不正な型変換が発生しないようにすることでプログラムの安定性を高めます。したがって、Javaの型システムは、Javaプログラマーにとって非常に重要な概念です。

変数と代入[編集]

Javaでは、変数を宣言することで、値を格納し、後で参照することができます。変数は、データ型と名前で識別されます。

Javaのデータ型は、プリミティブ型と参照型に分類されます。 プリミティブ型は、int、double、boolean、charなどの基本的な型で、値そのものを格納します。 参照型は、クラス、インタフェース、配列などのオブジェクト型で、オブジェクトへの参照を格納します。

変数を宣言するには、データ型と変数名を指定します。以下は、int型の変数xを宣言する例です。

int x;

変数に値を代入するには、代入演算子=を使用します。以下は、変数xに値10を代入する例です。

x = 10;

変数を宣言と同時に初期化することもできます。以下は、int型の変数xを宣言し、値10で初期化する例です。

int x = 10;

変数には、他の変数の値を代入することもできます。以下は、int型の変数yに、変数xの値を代入する例です。

int y = x;

Javaでは、データ型が異なる変数同士の代入はできません。また、変数に値が代入されていない場合、変数の値は自動的に初期化されません。 もし、未初期化の変数を参照するとerror: variable i might not have been initializedとなります。

変数の値を変更するには、代入演算子を使用します。以下は、変数xの値を20に変更する例です。

x = 20;

データ型[編集]

Javaのデータ型は、プリミティブ型と参照型の2種類に分類されます。以下にそれぞれの型について説明し、表にまとめます。

プリミティブ型[編集]

Wikipedia
Wikipedia
ウィキペディアプリミティブ型の記事があります。

プリミティブ型は、Javaの基本的なデータ型で、値そのものを格納します。Javaのプリミティブ型には、以下の8つがあります。

プリミティブ型
データ型 サイズ (ビット数) 値の範囲 ラッパークラス
boolean 1 true または false Boolean
byte 8 -128 から 127 Byte
short 16 -32768 から 32767 Short
int 32 -2147483648 から 2147483647 Integer
long 64 -9223372036854775808 から 9223372036854775807 Long
float 32 IEEE 754 単精度浮動小数点数 Float
double 64 IEEE 754 倍精度浮動小数点数 Double
char 16 UTF-16の16ビット文。charという名前だがサロゲートペアにより16ビットの範囲を超えたUnicodeについては、「その半分」を表現する Character

Javaでは自動的に型変換が行われる場合があります。例えば、int型とdouble型の演算を行うと、int型の値が自動的にdouble型に変換されます。 ただし、変換元の型よりも変換先の型が大きい場合、精度の低下や情報の欠落が発生する場合があるため、注意が必要です。

また、Javaのプリミティブ型には、各データ型のラッパークラスが用意されています。 ラッパークラスを使用することで、プリミティブ型として扱えるようになります。 例えば、int型の場合はIntegerクラスが対応しています。

参照型[編集]

Wikipedia
Wikipedia
ウィキペディア参照型の記事があります。

参照型には、クラス型、インタフェース型、配列型などがあります。 クラス型やインタフェース型は、それぞれ自分で定義して使用することができます。 配列型は、同じデータ型の値を複数格納するためのもので、以下のように宣言します。

注意点

Javaのプリミティブ型は、値そのものを格納するのに対し、参照型はオブジェクトへの参照を格納するため、メモリの使用方法が異なります。 また、プリミティブ型は値渡し、参照型は参照渡しとして扱われます。 このため、値渡しの場合は、値そのものがコピーされ、オリジナルの変数に影響を与えませんが、参照渡しの場合は、オブジェクトそのものへの参照が渡されるため、オリジナルのオブジェクトに影響を与える可能性があります。

ボクシングとアンボクシング[編集]

Javaにおける「ボクシング (Boxing)」と「アンボクシング (Unboxing)」とは、プリミティブ型とそれに対応するラッパークラス(Integer、Double、Booleanなど)の相互変換を指します。

Javaのプリミティブ型は、値そのものを扱うための型であり、一方でオブジェクト指向言語であるJavaは、全てのものをオブジェクトとして扱うことができるため、プリミティブ型に対応するオブジェクトが必要になります。そのため、プリミティブ型から対応するラッパークラスへの変換を行う「ボクシング」と、その逆の変換を行う「アンボクシング」が必要になります。

Java 5からは、自動的にボクシングとアンボクシングが行われる「オートボクシング (Autoboxing)」という機能も追加されました。これにより、プリミティブ型とラッパークラスの変換を明示的に行う必要がなくなり、より簡潔なコードが書けるようになりました。

ただし、ボクシングとアンボクシングは、その処理に時間がかかるため、大量のデータを扱う場合にはパフォーマンス上の問題が発生することがあります。そのため、できる限り明示的な変換を行うことで、パフォーマンスを向上させることが望ましい場合もあります。

では、プリミティブ型とボクシング、オートボクシングの違いがパフォーマンスに与える影響を示すコード例を示します。

まず、プリミティブ型を使用した場合のコード例です。

public class PrimitiveExample {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1; i <= 1000000; i++) {
            sum += i;
        }
        System.out.println("Sum of 1 to 1000000 using primitive type: " + sum);
    }
}

このコードは、1から1000000までの整数の和を計算し、その結果を出力します。

次に、ボクシングを使用した場合のコード例です。

public class BoxingExample {
    public static void main(String[] args) {
        Integer sum = 0;
        for (int i = 1; i <= 1000000; i++) {
            sum += i;
        }
        System.out.println("Sum of 1 to 1000000 using boxing: " + sum);
    }
}

このコードは、同じく1から1000000までの整数の和を計算し、その結果を出力します。ただし、変数 sum の型を Integer にしています。

最後に、オートボクシングを使用した場合のコード例です。

public class AutoBoxingExample {
    public static void main(String[] args) {
        Integer sum = 0;
        for (Integer i = 1; i <= 1000000; i++) {
            sum += i;
        }
        System.out.println("Sum of 1 to 1000000 using autoboxing: " + sum);
    }
}

このコードは、同じく1から1000000までの整数の和を計算し、その結果を出力します。ただし、for ループの制御式で Integer 型の変数を使用しています。

これら3つのコードを実行してみると、プリミティブ型を使用した場合が最も高速であることがわかります。一方、ボクシングやオートボクシングを使用した場合は、パフォーマンスが低下する傾向があります。

したがって、パフォーマンスを重視する場合は、可能な限りプリミティブ型を使用することが推奨されます。ただし、可読性や保守性を重視する場合は、ボクシングやオートボクシングを使用することも検討されます。

文字列[編集]

Javaにおいて文字列はリテラルとして表現されます。 つまり、ダブルクオートで囲まれた文字列はJavaにおいて文字列リテラルとして扱われ、プログラム内で文字列を表すために利用されます。

また、Javaの文字列は値型(プリミティブ型)として振る舞います。 つまり、文字列はオブジェクトではなく、値そのものを扱うための型となります。 ただし、Stringクラスはオブジェクトとして実装されていますが、文字列自体はイミュータブルであるため、値そのものは変更できません。

このように、Javaの文字列は値型として扱われるため、文字列を比較する場合は、==演算子ではなく、equalsメソッドを使用する必要があります。 また、文字列を結合する場合は、+演算子を使用することができます。

String str1 = "Hello";
String str2 = "world";
String result = str1 + " " + str2;
System.out.println(result);  // 出力結果: "Hello world"

定数[編集]

Javaでは、値を変更できない定数を定義することができます。定数は、final キーワードを使用して宣言され、一度値を代入されると、それ以降変更することができません。例えば、以下のように宣言します。

final int MAX_COUNT = 100;

キャスト[編集]

Javaでは、異なるデータ型の変数間で値を変換することができます。この変換をキャストと呼びます。キャストには、明示的なキャストと暗黙的なキャストの2つがあります。明示的なキャストは、(データ型)の形式で表現され、暗黙的なキャストは、Javaが自動的に行います。例えば、以下のように宣言します。

int a = 5;
double b = (double) a;

null[編集]

Javaでは、オブジェクト型の変数にnullという特別な値を代入することができます。nullは、オブジェクトが存在しないことを表します。例えば、以下のように宣言します。

String str = null;

型推論[編集]

Java 10から、変数の宣言時にvarキーワードを使用して、型推論を行うことができます。varを使用すると、右辺の式から型を自動的に推論して、変数の型を決定します。例えば、以下のように宣言します。

ローカル変数宣言での型推論では、varキーワードを使用して、変数の型の宣言を省略することができます。一方、型推論される変数は初期化が必要です。以下の例では、varによりint型推論されています。

var i = 1;

forループ変数でも同様に、varキーワードを使用することで型推論が可能です。以下の例では、List[removed]型からint型への変換によって、変数jがint型で推論されています。

List<Integer> list = Arrays.asList(1, 2, 3);
for (var j : list) {
    // do something
}

その他にも、例えば、ラムダ式についても型推論が可能です。以下の例では、Functional InterfaceのPredicate[removed]を省略しています。

var f = (Predicate<Integer>) i -> i > 0;

また、複雑な型を返すメソッドを受け取る場合にも、型推論は有用です。以下の例では、flatMap()メソッドを使用して、文字列のリストを1つのStringに変換しています。

List<String> list = Arrays.asList("hello", "world");
var result = list.stream()
        .flatMap(s -> Arrays.stream(s.split("")))
        .collect(Collectors.joining());

最後に、ループ実例を示します。以下の例では、List[removed]からString[]に変換しています。

List<String> list = Arrays.asList("hello", "world");
var array = new String[list.size()];
var index = 0;
for (var element : list) {
    array[index++] = element;
}

多次元配列[編集]

Javaでは、1次元以上の多次元配列を宣言することができます。多次元配列は、配列の配列として表現されます。例えば、以下のように宣言します。

int[][] arr = {{1, 2}, {3, 4}};

enum型[編集]

Javaでは、enum型を使用して、列挙型を定義することができます。列挙型は、あらかじめ定義された一連の定数を表します。列挙型を宣言するには、enumキーワードを使用します。例えば、以下のように宣言します。

enum Color {
    RED, GREEN, BLUE
}

Optional[編集]

JavaのOptionalは、null値を扱う場合に便利な機能です。null値は、Javaプログラムで頻繁に使用されるため、それを適切に処理することは重要です。

例えば、あるメソッドがnullを返す可能性がある場合、その返り値を処理する際にnullチェックを行う必要があります。しかし、nullチェックを行うことが煩わしい場合や、忘れてしまう場合もあります。このような場合、Optionalを使用することで、null値を簡単に扱うことができます。

例えば、次のコードは、Optionalを使用してnull値を扱う例です。

Optional<String> optionalName = Optional.ofNullable(name);
if (optionalName.isPresent()) {
    System.out.println("Name is " + optionalName.get());
} else {
    System.out.println("Name is not present");
}

この例では、Optional.ofNullable()メソッドを使用して、null値であるかどうかをチェックします。その後、isPresent()メソッドを使用して、Optionalが値を持っているかどうかをチェックし、get()メソッドを使用して、値を取得しています。また、値が存在しない場合には、代替のメッセージを表示するように設定されています。

また、次のように、Optionalを使用して値が存在しない場合のデフォルト値を設定することもできます。

String name = null;
String defaultName = "Guest";
String result = Optional.ofNullable(name).orElse(defaultName);
System.out.println(result);

この例では、Optional.ofNullable()メソッドを使用して、null値であるかどうかをチェックし、orElse()メソッドを使用して、値が存在しない場合にデフォルト値を設定しています。

以上のように、Optionalを使用することで、null値を扱いやすくなり、コードが煩雑になることを防ぐことができます。

附録[編集]

チートシート[編集]

public class Main {
    public static void main(String[] args) {
        // 変数の宣言と初期化
        int num1 = 10;
        int num2 = 20;
        String name = "John";
        boolean flag = true;
        
        // 変数の使用と演算
        System.out.println("num1 + num2 = " + (num1 + num2)); // 30
        System.out.println("My name is " + name); // My name is John
        System.out.println("flag is " + flag); // flag is true
        
        // プリミティブ型と参照型
        int[] array = {1, 2, 3}; // 参照型の例
        char ch = 'a'; // プリミティブ型の例
        
        // プリミティブ型と参照型の違い
        int num3 = num1; // num1の値をコピーしてnum3に代入(値渡し)
        int[] array2 = array; // arrayの参照をコピーしてarray2に代入(参照渡し)
        array[0] = 100; // arrayの値を変更
        
        System.out.println("num3 = " + num3); // num3 = 10
        System.out.println("array[0] = " + array[0]); // array[0] = 100
        System.out.println("array2[0] = " + array2[0]); // array2[0] = 100
        
        // プリミティブ型と参照型の比較
        boolean isEqual = num1 == num3; // プリミティブ型同士の比較
        boolean isSameArray = array == array2; // 参照型同士の比較
        
        System.out.println("num1 == num3 : " + isEqual); // num1 == num3 : true
        System.out.println("array == array2 : " + isSameArray); // array == array2 : true
    }
}

用語集[編集]

  • 変数(Variable):データを保存するためのメモリ上の場所を表す識別子
  • データ型(Data type):データの種類を定義するために使用されるクラスまたはプリミティブ型
  • プリミティブ型(Primitive type):単一の値を保存するための基本的な型(int, double, boolean など)
  • 参照型(Reference type):オブジェクトへの参照を保存するための型(String, Integer, List など)
  • 宣言(Declaration):変数の型と名前を指定して、変数を作成すること
  • 初期化(Initialization):変数に最初の値を割り当てること
  • スコープ(Scope):変数が有効である範囲(ブロックスコープ、クラススコープなど)
  • 型キャスト(Type casting):データ型を変換すること(プリミティブ型、参照型ともに可能)
  • 自動型変換(Automatic type conversion):異なるデータ型間での暗黙の型変換(プリミティブ型、参照型ともに可能)
  • パッケージ(Package):クラスの階層構造を整理するために使用される名前空間
  • import文(import statement):他のパッケージに定義されたクラスを使用するための文
  • staticインポート(Static import):静的メンバーをインポートするための文(staticメソッドや定数など)
  • final変数(Final variable):初期化後に値を変更できない変数
  • static変数(Static variable):クラス変数。クラス内で共有される変数
  • インスタンス変数(Instance variable):オブジェクトごとに異なる値を持つ変数