Java変数のスコープ(有効範囲)について

この記事は、[シアトルコンサルティング株式会社 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/seattleconsulting)の7日目の記事です。
こんにちは、シアトルコンサルティングの小泉です。
##はじめに
今回は変数のスコープ範囲についてまとめました。
プログラミングを学び始めた際に、変数の有効範囲がどこまでかわからずエラーが出ることがよくありました。
そんなプログラミング初学者に向けて作成いたしましたので、変数のスコープについて知りたい方はぜひ活用してみてください。

##ゴール
変数のスコープについて理解した上でコードを書けるようになる。

##Java変数のスコープ(有効範囲)とは
文字通り定義した変数の有効な範囲のことです。
（変数の宣言した場所によって、その変数を使用できる範囲が制限されます）

・ローカル変数  
・インスタンス変数  
・static変数

宣言する場所によって、呼び名も複数種類に分かれています。
  
## ローカル変数
{ } ブロック単位で有効範囲が変わり、ブロック内で扱える変数のことをローカル変数と言います。
ブロックの中で、変数を宣言した位置より後ろで使用することが出来ます。

```java
class Main {
  public static void main(String args[]) {
    String block1 = "ブロック1";
    System.out.println(block1); // ブロック1

    {
      String block2 = "ブロック2";
      // block2を宣言したブロック内なのでアクセスできます
      System.out.println(block2); // ブロック2

      // block1はアクセスできます
      System.out.println(block1); // ブロック1
    }

    // block2を宣言したブロックの外側なので、コンパイルエラーになります
    System.out.println(block2); // block2 cannot be resolved to a variable
  }
}
```

## インスタンス変数
クラス定義の直下で宣言する変数のことを言います。
newされる度にメモリ領域にインスタンスが作成されます。
オブジェクト.インスタンス変数名でアクセスできます。

```java
class Person {
  // クラスの下で変数を定義（インスタンス変数）
  String name;
  int age;
  int height;

  // クラス内のメソッドからもアクセスできます（staticメソッドからはアクセスできません。下記参照。）
  public void checkInfo() {
    System.out.print("名前は"+ name + "、");
    System.out.print("年齢は"+ age + "、");

    if(age < 20) {
      System.out.print("未成年。");
    }

    System.out.println("身長は"+ height + " です。");
  }
}
```

```java
class Main {
  public static void main(String args[]) {
    Person tanaka = new Person(); // newキーワードでオブジェクトを生成

    tanaka.name = "田中"; // オブジェクトを代入した変数名tanakaで、インスタンス変数nameに値を代入しています
    tanaka.age = 19; // オブジェクトを代入した変数名tanakaで、インスタンス変数ageに値を代入しています

    System.out.println(tanaka.name); // 田中
    System.out.println(tanaka.age); // 19

    // ↓ローカル変数は宣言・初期化を行わなければならなかったが、
    // インスタンス変数は自動で初期化されます。
    System.out.println(tanaka.height); // 0

    tanaka.checkInfo(); // 名前は田中、年齢は19、未成年。身長は0 です。


    Person suzuki = new Person(); // newキーワードでオブジェクトを生成

    suzuki.name = "鈴木"; // オブジェクトを代入した変数名suzukiで、インスタンス変数nameに値を代入しています
    suzuki.age = 51; // オブジェクトを代入した変数名suzukiで、インスタンス変数ageに値を代入しています
    suzuki.height = 146; // オブジェクトを代入した変数名suzukiで、インスタンス変数heightに値を代入しています

    System.out.println(suzuki.name); // 鈴木
    System.out.println(suzuki.age); // 51
    System.out.println(suzuki.height); // 146

    suzuki.checkInfo(); // 名前は鈴木、年齢は51、身長は146 です。
  }
}
```
Personクラスを元にnewキーワードでオブジェクトを複数生成（インスタンス化）した時、
インスタンス変数の値はインスタンス毎に異なり、オブジェクト毎に異なる値を扱うことができます。
オブジェクトを代入した変数名.インスタンス変数名でアクセスします。

## static変数（クラス変数）
クラス定義の直下で宣言する変数。
変数を宣言する時、static修飾子を指定することでstatic変数として扱われます。
インスタンス変数は、各オブジェクト毎に値を保持していましたが、
static変数は、1箇所にまとめられて値を保持します。

```java
class Sample {
  int number = 99; // インスタンス変数
  static String color = "黄"; // static変数（クラス変数）

  // static変数（クラス変数）に値をセットするメソッド
  public void setColor(String value) {
    color = value;
  }
}
```

故にクラスをインスタンス化することなく、アクセスができます。

```java  
class Main {
  public static void main(String args[]) {
    Sample sample1 = new Sample();

    sample1.number = 10;
    System.out.println(sample1.number); // 10
    sample1.setColor("赤");
    System.out.println(Sample.color); // 赤

    Sample sample2 = new Sample();
    // インスタンス変数であるnumberは、オブジェクト毎に保持しているものなので、初期値の99が入っています。
    System.out.println(sample2.number); // 99

    // static変数（クラス変数)のcolorは共通の値を共有するため、
    // デフォルトの「黄」はsample1.setColorのタイミングで赤がセットされました。
    // だからここでstatic変数（クラス変数）にアクセスすると赤と出力されます。
    System.out.println(Sample.color); // 赤
  }
}
```
static変数（クラス変数）はクラスの共通変数のため、変更を加えると使用している箇所全てに影響が出てしまいます。

##さいごに
変数の種類やスコープについて理解いただけましたでしょうか？
今後も初学者に向けて役立つ記事を載せていくのでよろしくお願いします。