[Java]いまさら語るインスタンス化？変数初期化とインスタンス化するとメモリ上で起きること。

Javaで使うメモリ領域は２つです。
Stack（スタック）とHeap（ヒープ）と呼んでいるものです。

## Javaは型によって使うメモリ領域が異なる。
まずJavaの型によって使うメモリを使う領域が異なることはご存知でしょうか？
一般的にJavaの型には
基本型と言われる数値型（byte, short, int, long, float, double）、文字型（char）、論理型（boolean）と
参照型と言われる文字列型（String）やクラス型などがあります。

### 基本型の変数宣言と初期化

基本型の変数を宣言したとします。
（一番多く使われるのがint型だと思いますので、ここで例としてintを使いました。）

```Java
int num;
```

すると、
Stackメモリ領域に　numという器が作られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3915991/d66c076e-0df3-32d3-6c52-5615bc26d85d.png)

変数を宣言しただけで値は入っていないですので、空です。
そのあと、変数を初期化します。

```Java
int num;
num = 10;  //整数10をnumに代入する。
```

そうすると、さきほど作られたStackメモリ領域のnumという器に「10」という値が入ります。

![スクリーンショット 2024-10-23 225959.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3915991/654ce509-1ece-c833-7e7c-4d448bb5a365.png)


### 参照型の変数宣言と初期化

次は参照型の変数宣言と初期化をした場合を見てみましょう。
<font color="gray">
~~(一般的にStringをたくさん使うのでStringを例にします。)~~
※Stringは顕密にいうとまたほかのクラスとメモリの持ち方が異なるため、参照型である配列を例に変更します。
</font>

int型の変数を宣言した時と同じようにint型の配列の変数を宣言します。
```Java
int num;
num = 10;  //整数10をnumに代入する。

int[] arr;
```

そうすると、int型の変数を宣言した時と同じようにStackメモリに器が作られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3915991/15fc4480-79b8-97b6-583f-e2309d76c75e.png)

そのあと、変数を初期化します。
（変数に初めて値を代入することを初期化と言います。）
```Java
int num;
num = 10;  //整数10をnumに代入する。

int[] arr;
arr = new int[2]; //①int型の値を入れられる連続した領域をメモリに作ります。
arr[0] = 1; //②メモリの0番目の部屋に1という値を入れます。
arr[1] = 2; //②メモリの1番目の部屋に2という値を入れます。
```
そうすると、int型の変数を初期化をした時と違う動きになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3915991/4e3d7f6e-a39c-cabe-fcc7-8a02963f996d.png)

さきほどは変数に直接値を入れましたが、参照型は値を直接メモリに入れるのではなく、値が入っているメモリの住所を入れます。メモリの住所を参照するから参照型といいます。
上の例だと、
①Heapメモリ領域に配列のためのメモリ領域を作って、そのメモリ住所をStackメモリのarr変数に代入します。
②Heapメモリ領域の0番目の部屋に1、1番目の部屋に2という値を入れます。

## じゃ、クラスのインスタンス化を語ってみよう

具体的な話をしましょう。
この３つのクラスがあるとします。
```Java:Person.java
package practice;

//人というクラスです。
//人の属性である名前と年齢を持ちます。
public class Person {

	public static String name;
	int age = 1;
	
	String getPerson() {
		return "["+name+"の情報]\r\n年齢:"+age;
	}
	void printPerson() {
		System.out.println(getPerson());
		
	}
}
```
```Java:Student.java
package practice;

//人の属性を利用して学生クラスを作ります。
public class Student {
		Person person;	

		void printStudent() {
			person.printPerson();
		}
}
```
```Java:StudentTest.java
package practice;

//学生クラスをテストするクラスです。
public class StudentTest {

	public static void main(String[] args) {
		
		Student student1 = new Student();
		student1.printStudent();
	}

}
```
これを実行すると、おなじみのNullPoiterExceptionが発生します。
理由を考えてみましょう。

。
。
。
。
。
。
。

今の状態を図で書いてみるとこんなイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3915991/959023cf-d9a3-6ade-d8eb-f39440ac5a50.png)

HeapメモリにあるStudentクラスのpersonオブジェクトに注目してみましょう。
Person型のperson変数と宣言をしたので、メモリ領域は確保されます。
しかしインスタンス化していないため、そのPersonのメモリ住所が与えられておりません。
メモリ住所がないのにPersonクラス（設計図）にあるメソッドを実行しようとしてヌルポが発生します。


Studentクラスを以下のように修正してみてください。
```Java:Student.java
package practice;

public class Student {
	//Person person;	//さきほどのエラーコードはコメントアウトしました。
	Person person = new Person();  //①new 演算子を利用してインスタンス化

		void printStudent() {  //メソッドは変更なし。
			person.printPerson();
		}
}
```

インスタンス化するとperson変数にPersonオブジェクトのメモリ住所が入るので、
NullPointExceptionが発生しないことがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3915991/b133f24f-16d5-c87c-6ab5-2d31a6c79c64.png)

①new 演算子を利用してインスタンス化すると、Person型のメモリ領域が作られます。また、そのメモリ住所をStudentのperson変数に入れます。

そうです。
インスタンス化とはつまりオブジェクトのあるメモリ住所を与えて変数に代入することです。
Stringクラスは例外ですが、すべてのクラスはこのインスタンス化をしてから初めてオブジェクトとしてちゃんと使えるようになります。