PR

Javaのデータ型の基本:プリミティブ型と参照型の違いを3分で

Java

Javaのデータ型は大きく「プリミティブ型」と「参照型」の2種類に分類することができます。この基本的な分類を理解していないと、メモリ管理やデータ操作の際に予期せぬ挙動・バグを引き起こす可能性が高くなります。特に大規模なアプリケーション開発では、この理解がプログラムの効率性と安定性に非常に重要になってきます。

このページではJavaの基本であるプリミティブ型と参照型の違いを端的にわかりやすく解説します。

スポンサーリンク

プリミティブ型と参照型の違い

プリミティブ型と参照型の違いをわかりやすく表形式で整理しました。ポイントとなる部分を黄色ハイライトで示します。

特徴プリミティブ型 (Primitive Type)参照型 (Reference Type)
定義変数に値を直接保持する変数にオブジェクトの参照を保持する
int, char, boolean などString, 配列, クラスのインスタンス など
メモリ領域スタックメモリに格納されるヒープメモリに格納される
初期値intなら0, booleanならfalse などのデフォルト値null
値の格納直接値を格納するオブジェクトのメモリアドレス(参照)を格納する
値のコピー値そのものがコピーされる参照がコピーされる
影響範囲変更はその変数にのみ影響する変更は同じオブジェクトを参照するすべての変数に影響する

プリミティブ型

プリミティブ型(Primitive Type)は、Javaで最も基本的なデータ型でザックリいえば「箱」のイメージが通用する変数です。(参考 プログラミングの「変数」とは?

Java プリミティブ型
図1:プリミティブ型

「箱」に値を格納して、その「箱」を使って様々な計算や処理を行っていくイメージ。

ポイント1 直接値を保持

プリミティブ型は、変数に直接値を保持する。つまり、変数そのものに数値や文字などの値が格納されます。

ポイント2 メモリ上の配置

プリミティブ型の変数は、メモリ上のスタックという領域に格納されます。スタックメモリは、変数の値を直接保持する高速なメモリ領域です。

ポイント3 値のコピー

プリミティブ型の変数を別の変数に代入すると、値そのものがコピーされます。元の変数と新しい変数は独立して存在し、一方の変更は他方に影響を与えません。

int a = 10;

/*スタックメモリ:
+-------+
|   a   |
+-------+
|  10   |
+-------+*/

int b = a;

/*スタックメモリ:
+-------+  +-------+
|   a   |  |   b   |
+-------+  +-------+
|  10   |  |  10   |
+-------+  +-------+*/


b = 20;

/*スタックメモリ:
+-------+  +-------+
|   a   |  |   b   |
+-------+  +-------+
|  10   |  |  20   |
+-------+  +-------+*/

プリミティブ型の動作原理は非常にシンプル。変数に直接値を保持し、変数間の代入は値そのものをコピーします。これにより、変数同士が独立して動作し、一方の変更が他方に影響を与えることはありません。

プリミティブ型として扱えるデータ型は以下の通りで、Javaでは以下の8種類しか存在しません。

データ型 (Type)サイズ (Size)範囲 (Range)デフォルト値 (Default Value)説明 (Description)
byte8ビット-128 ~ 1270小さな整数を保持する
short16ビット-32,768 ~ 32,7670中程度の整数を保持する
int32ビット-2,147,483,648 ~ 2,147,483,6470標準的な整数を保持する
long64ビット-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,8070L大きな整数を保持する
float32ビット±1.4E-45 ~ ±3.4E+380.0f単精度の浮動小数点数を保持する
double64ビット±4.9E-324 ~ ±1.7E+3080.0d倍精度の浮動小数点数を保持する
char16ビット'\u0000' (0) ~ '\uffff' (65,535)'\u0000'Unicode文字を保持する
boolean1ビットtrue または falsefalse真偽値を保持する

参照型

参照型変数は、実際のデータ(オブジェクト)への「住所」を持つ変数です。データそのものを持っているのではなく、そのデータがメモリのどこにあるかを指しています。

参照型変数を日常生活の例えで説明すると以下のような関係。

  • データそのもの:家
  • 参照型変数:住所

参照型変数は、家そのものを持っているのではなく、家の住所を持っています。住所を知っていることで、その家に行くことができるというイメージです。

Person person1 = new Person("John", 25);

/*スタックメモリ:
+-----------+
|  person1  |
+-----------+
|  (参照)   | --> ヒープメモリの「John」という名前と「25歳」という情報を持つPersonオブジェクト
+-----------+*/

Person person2 = person1;

/*スタックメモリ:
+-----------+     +-----------+
|  person1  | --> |  (参照)   |
+-----------+     +-----------+
|  (参照)   |     |  person2  |
+-----------+     +-----------+
                  |  (参照)   | --> 同じヒープメモリのPersonオブジェクト
                  +-----------+*/

person2.setName("Jane");

/*スタックメモリ:
+-----------+     +-----------+
|  person1  | --> |  (参照)   |
+-----------+     +-----------+
|  (参照)   |     |  person2  |
+-----------+     +-----------+
                  |  (参照)   | --> 同じヒープメモリのPersonオブジェクト(名前が「Jane」に変更)
                  +-----------+

ヒープメモリ:
+-------------------------+
|       Person            |
+-------------------------+
|  name: "Jane"           |
|  age: 25                |
+-------------------------+*/

サンプルコードのポイント1 person1の宣言と初期化

  • Person person1 = new Person("John", 25);
    person1という変数に、「John」「25歳」という情報を持つ「Person」という家の住所が割り当てられます。

サンプルコードのポイント2 person2にperson1の住所をコピー

  • Person person2 = person1;
    person2という変数にも、person1と同じ家の住所が割り当てられます。つまり、person1person2は同じ家(同じデータ)を指しています。

サンプルコードのポイント3 person2を使ってデータを変更

  • person2.setName("Jane");
    person2を使ってその家の名前を「Jane」に変更します。person2person1と同じ家を指しているので、person1を使っても名前は「Jane」に変わっています。

このように、参照型変数は「住所」を使ってオブジェクトを操作するイメージで捉えると理解しやすくなります。

プリミティブ型として扱うデータ型は以下の通り。

データ型 (Type)説明 (Description)例 (Example)
String文字列を保持するクラスです。文字の配列を扱います。"Hello"
配列 (Array)同じ型の複数の値を保持するコンテナです。int[] nums = {1, 2, 3};
クラス (Class)ユーザー定義のデータ型です。フィールドとメソッドを持ちます。Person p = new Person();
インターフェース (Interface)クラスが実装するためのメソッドのセットを定義します。Runnable r = new MyRunnable();
列挙型 (Enum)固定された定数のセットを定義します。Day day = Day.MONDAY;
ラッパークラス (Wrapper Class)プリミティブ型をオブジェクトとして扱うためのクラスです。Integer num = 10;

「型」という概念は、必ずしも「箱」としての物理的なイメージだけではなく、データの性質やそのデータに対してどのような操作ができるかという仕様(契約)を示すものだといえます。

ポイント 型の基本概念

  • データの性質と操作の集合
    型は「どんな値が入るか」と「その値に対してどんな操作が可能か」を定義します。たとえば、intという型は整数が入ること、そして四則演算などの操作ができることを保証します。
  • 箱のアナロジーの限界
    変数を「箱」と考えると、intbooleanは箱の種類(つまり入るデータの性質)を表すと理解できます。しかし、この「箱」の比喩はシンプルなデータに対して有効ですが、クラスの場合はそれ以上の情報―内部に複数の変数やメソッドが定義される―を含むため、少し抽象的な「設計図」「契約」というイメージが適しています。

ポイント クラスが型である理由

  • 設計図としての役割
    クラスはオブジェクト(実体)を生成するための設計図です。クラスが定義するフィールド(変数)とメソッド(操作)は、そのクラスのインスタンスが持つべき性質や振る舞いを規定します。したがって、「このクラス型の変数には、この設計図に従ったオブジェクトしか入らない」という保証が働きます。
  • 型としての意味
    プログラミング言語(Javaなど)において、変数の型はその変数にどのようなデータが入るか、またどのような操作が可能かを決定します。クラスもまた「ユーザー定義型」として、プログラム内でそのクラスに基づくオブジェクトのみを扱えるように型チェックが行われます。たとえば、Personクラスという型の変数には、Personまたはそのサブクラスのインスタンスだけが代入可能です。
  • カプセル化と抽象化
    クラスは内部に複数のフィールドとメソッドを持ちますが、これらは「カプセル化」によって外部から隠蔽され、必要な情報(型としての契約)のみが公開されます。つまり、外部のコードは「このオブジェクトはこのクラスの型である」という情報だけで、内部の実装の詳細を知らなくても、その型に定義された操作を正しく使えます。

クラスは「型」であるというのは、そのクラスが「どのようなデータ(フィールド)を持ち、どのような操作(メソッド)が可能か」という情報の集合、すなわち「設計図」や「契約」を示しているという意味です。

変数の型が箱の種類を決めるという単純なイメージは、プリミティブ型には適用できますが、クラス型の場合は内部の構造や振る舞いも定義しているため、より抽象的な設計図として理解するのが適切です。

プリミティブ型以外はすべて「オブジェクト」

Javaでは、変数には大きく分けて2つのタイプがあることを解説してきました。1つは「プリミティブ型(基本型)」と呼ばれる、intやdouble、booleanといった基本的な値を直接格納するタイプの変数。もう1つは「参照型」と呼ばれ、クラスやインターフェースで定義されたオブジェクトを格納するための変数です。

「箱」のイメージでおさらい

Java プリミティブ型
(再掲):プリミティブ型

よく、変数は「箱」にたとえられます。プリミティブ型の変数は、その「箱」に直接数字やtrue/falseといった値が入っています。たとえば、int a = 10; なら、「a」という名札のついた箱の中に10が入っているイメージ。

一方で、参照型の変数は「オブジェクト」という、もう1つ別の「箱」への住所(場所)を記憶した「アドレス帳」のような役割を果たします。つまり、String s = "Hello"; の場合、「s」という箱の中には、「"Hello"というオブジェクトがある場所のメモ」が入っているイメージです。

「プリミティブ型以外はすべてオブジェクト」とは?

ここで「プリミティブ型以外はすべてオブジェクト」という言葉が登場します。このフレーズは、参照型として扱われるすべてのもの(例:String、List、Map、独自定義したクラスのインスタンスなど)が、結局は何らかのクラスから作られた「オブジェクト」であることを指しています。

言い換えると、プリミティブ型以外は、すべて「オブジェクトを指し示すデータ型(参照型)」なのです。

ここで注意してほしいのは、「変数そのものがオブジェクト」ではなく「変数がオブジェクトを参照している」 という点です。オブジェクトはあくまで「もうひとつの箱(実体)」であり、そのオブジェクトが置いてある場所を参照しているのが変数です。

もう少し丁寧に説明します。


あなたが「変数は箱」だとイメージしているなら、プリミティブ型は箱に値が直接入っている状態でしたね。たとえば、int a = 10;という場合、「a」という箱には10がそのまま入っています。

一方、参照型の場合は少し仕組みが違います。

「オブジェクト」と呼ばれる別の実体があって、それが「大きな箱」をイメージするとわかりやすいかもしれません。この「大きな箱」には、いろいろなデータやメソッド(振る舞い)が詰まっています。たとえば、Stringというクラスから作られた"Hello"というオブジェクトがあったとして、それは「"Hello"オブジェクトをしまっている大きな箱」を想像できます。

String s = "Hello"; と書いたとき、「s」はどんな状態にあるでしょうか?「s」という変数(箱)の中に、直接"Hello"という文字列が入っているのではありません。代わりに「ここに'Hello'オブジェクトがありますよ」という住所メモが入っているイメージです。「s」は実体であるオブジェクトそのものではなく、そのオブジェクトへアクセスするための「参照」なのです。

これが「変数そのものがオブジェクト」ではなく、「変数がオブジェクトを参照している」状態です。変数はあくまで「住所メモ(参照)」を持っていて、その先に「本物の箱(オブジェクト)」が置いてあります。このようにして、Javaではプリミティブ型(基本的な値を直接入れる箱)と参照型(オブジェクトという別の箱へと続く住所メモを入れる箱)を区別しています。そして、プリミティブ型以外(String、List、Map、そして自分が定義したクラスのインスタンスなど)は、すべてこの「住所メモ」を通してオブジェクトにアクセスする仕組みになっているのです。

Javaにおける「参照」をさらに詳しく

Javaにおける「参照」とは、オブジェクトそのものではなく、オブジェクトが格納されているメモリ上の場所を指し示す「目印」のようなものです。初心者向けに以下のポイントで解説します。

ポイント1 変数と実体の違い

  • 基本概念:
    プリミティブ型(例えばintbooleanなど)の変数は、値そのものを保持します。しかし、オブジェクトを扱う変数の場合は、実際のオブジェクトそのものではなく、そのオブジェクトが格納されている場所(アドレス)への「参照」を保持しています。
  • 例:
// オブジェクトの生成
String str = new String("Hello");

この場合、変数strには実際の文字列データ「Hello」が直接入っているのではなく、文字列オブジェクトがメモリのどこかに存在することを示す「参照」が格納されます。

ポイント2 参照の役割

  • オブジェクトへのアクセス:
    参照を通じて、実際のオブジェクトの内容にアクセスしたり、そのメソッドを呼び出したりできます。変数に入っているのは「場所」であり、その場所にアクセスすることでオブジェクトのデータを読み書きできます。
  • 共有と変更:
    もし同じオブジェクトへの参照が複数の変数に渡されると、どの変数を使っても同じオブジェクトにアクセスできます。これにより、ある変数を使ってオブジェクトの状態を変更すると、他の変数からもその変更が見えるという性質があります。
Person personA = new Person("太郎");
Person personB = personA;  // personBはpersonAと同じオブジェクトを参照する
personB.setName("花子");
System.out.println(personA.getName());  // 「花子」と表示される

この例では、personApersonBは同じオブジェクトを参照しているため、どちらかで変更を加えると両方に影響します。

Person personA = new Person("太郎");
Person personB = new Person("花子");
personA = personB;  // ここでpersonAは新たにpersonBと同じオブジェクトを参照するようになる

この場合、personAが元々参照していたオブジェクトとは切り離され、personBが参照するオブジェクトと同じ場所を指すようになります。

「プリミティブ」と「オブジェクト」はどう違う?

もう一度おさらいします。

  • プリミティブ型int a = 10;のように、変数そのものに値(数字やtrue/falseなど)が直接入っている。
  • オブジェクトString s = "Hello";の場合、sは"Hello"オブジェクトを示す「参照」を持っており、そのオブジェクトには様々な情報や処理(メソッド)が詰まっている。

プリミティブ型は直接値を持つ「小さな箱」、オブジェクトは「大きな箱(実体)」を別に持っていて、変数はその「大きな箱の住所」を記憶していると言えます。

「プリミティブ型以外はすべてオブジェクト」とは、値を直接持つのではなく、クラスを元に作られたデータの実体(オブジェクト)をどこかに用意して、それへの参照を変数が保持している状態を指しています。慣れないうちは「箱の中に箱がある」という二段構造は少し抽象的かもしれませんが、これがJavaの言語仕様による基本的な仕組みです。この仕組みを理解すると、Javaの型システムやオブジェクト指向プログラミングがグッとわかりやすくなります。

そして・・・オブジェクトには属性やメソッドが用意されている

「参照型はオブジェクトを参照している」ということは、その参照先のオブジェクトが持つ機能を自由に利用できる、という点が非常に重要です。

プリミティブ型の変数は、たとえば int a = 10; のように、その箱自体に数値が直接入っています。しかし、この「箱」はただの数値を持つだけで、そこにメソッドや属性は存在しません。aに対して「a.何とか()」のようなメソッド呼び出しはできないし、属性を参照することもできません。

一方で、参照型の場合、変数はオブジェクトへの「住所メモ」を持っており、そのオブジェクトはクラス(参考 クラスとオブジェクトの基本)という設計図に基づいてつくられています。クラスには、そのオブジェクトが持つデータ(属性)や機能(メソッド)があらかじめ定義されています。したがって、参照型の変数を通じてオブジェクトにアクセスすれば、そのオブジェクトが備えている豊富な機能を「.(ドット)」演算子で呼び出せます。

たとえば、String s = "Hello"; とした場合、sStringオブジェクトを参照しているため、s.length()で文字数を取得したり、s.toUpperCase()で大文字に変換したりといった、Stringクラスで定義されている便利なメソッドを簡単に呼び出せるわけです。

要するに、「プリミティブ型以外はすべてオブジェクト」だからこそ、そこには属性やメソッドが用意されており、参照型の変数を通してそれらを自在に利用できる、という結論が導けます。参照型であるということは、ただ値を持つだけでなく、オブジェクトが備えている高機能な操作手段(メソッド)やデータ(属性)にアクセスできる、という大きな利点を持っているのです。

タイトルとURLをコピーしました