【Java入門】static変数(クラス変数)の使い方

Javaの「static」を使うと、それが含まれるクラスをインスタンス化せずにその変数にアクセスすることができます。

これに対して「static」を使わない変数はそのクラスをインスタンス化しなければ、その変数にはアクセスできません。

この2つの違いによって、それぞれの変数の役割に違いがあるって知ってましたか?この違いを知ってそれぞれを使い分けることができると、コーディングの幅が広がります。

この記事では、static変数について以下の内容で解説していきます。

  • static変数とは
  • static変数の使い方
  • static変数の注意点

static変数について、初心者でもわかるようにやさしく解説します!

>>> いますぐ「static変数の使い方」を知りたい方はこちら

なお、Javaの記事については、こちらにまとめています。

目次

static変数(クラス変数)とは

冒頭で述べたように、「static」句を使うとそれが含まれるクラスをインスタンス化せずにその変数にアクセスすることができます。この変数のことを「static変数」と呼びます。

これに対して「static」句を使わない変数はそのクラスをインスタンス化しなければ、その変数にはアクセスできません。この変数のことを「非static変数」と呼びます。

「static変数」は「クラス名.」の後に記述しますので「クラス変数」、「非static変数」は「インスタンス名.」の後に記述しますので「インスタンス変数」とも呼ばれます。

「クラス変数」はクラス内の変数、「インスタンス変数」はインスタンス固有の変数となります。

つまり、「クラス変数」はそのクラスから作られたインスタンスの全てで共有される変数、「インスタンス変数」はそのインスタンス1個内だけで使われる変数となります。

ですので、「クラス変数」はグローバル変数、「インスタンス変数」はローカル変数のような使い分けをすることができます。

Javaのグローバル変数、ローカル変数とは?

「クラス変数」はグローバル変数、「インスタンス変数」はローカル変数のような使い分けをすることができると述べましたが、Javaでは厳密にいうとグローバル変数はありません。

グローバル変数といえば、あらゆる場所からアクセスできる変数という意味になるため、あるクラスから作られたインスタンスで共有できる「クラス変数」とは少々意味合いが違いますよね。

あくまで、「クラス変数」はクラスから作られたインスタンスの全てで共有される意味で、グローバル変数に近い使い方ができるということです。

ちなみに、ローカル変数は1つのメソッド内だけで使われる変数としてJavaにもあります。

static変数(クラス変数)の使い方

それではstatic変数(クラス変数)の使い方をみていきましょう。

宣言について

static変数(クラス変数)は以下のように宣言します。

アクセス修飾子(publicなど) static 型名 変数名

初期化について

クラス変数の場合、データ型を指定して宣言すると初期値が与えられます。

その場合の初期値についてまとめました。

  • 整数値系のデータ型:0(ゼロ)
  • float型:0.0f
  • double型:0.0d
  • 文字型:’u0000′
  • boolean型:false
  • 参照型(String):null

なお、インスタンス変数の場合でも同様に初期値が与えられます。

呼び出し方について

static変数(クラス変数)の呼び出し方は以下の通りです。

クラス名.変数名

クラスのインスタンスを生成せずに、クラス名を使って呼び出します。

値の取得と受け渡しについて

static変数(クラス変数)の値の取得や受け渡しは前述のように呼び出すことで可能です。

サンプルコードで確認しましょう。

class ClassSample {
    private int val1 = 0; // 非static変数(インスタンス変数)
    private static int val2 = 0; // static変数(クラス変数)
 
    // コンストラクタ クラスがインスタンス化される度に変数に1加算する
    public ClassSample(int val1, int val2) {
        this.val1 += val1;
        ClassSample.val2 += val2; // 「クラス名.変数名」でstatic変数を宣言
    }
 
    // 変数の値を表示
    public void print(){
        System.out.println("val1 = " + val1 + ", val2 = " + val2);
    }
}
 
public class Main {
 
    public static void main(String[] args) { 
        for (int i = 0; i < 3; i++){
            ClassSample cs = new ClassSample(1, 1);
            cs.print();
        }
    }
 
}

実行結果:

val1 = 1, val2 = 1
val1 = 1, val2 = 2
val1 = 1, val2 = 3

この例では、Mainクラスのmainメソッド内でClassSampleクラスから3個インスタンスを生成しています。コンストラクタを使ってインスタンス生成の度にメンバ変数のval1とval2の値に1を加算しています。

val1は「非static変数」ですのでインスタンスが生成される度に0(ゼロ)で初期化されます。コンストラクタを使って1が加算され、3個のどのインスタンスでも値は1となります。

これに対して、val2は「static変数」ですので、3回インスタンスが生成される度に1ずつ加算されています。

val1はローカル変数、val2はグローバル変数として役割が違うことをおわかり頂けたかと思います。

static変数(クラス変数)の注意点

static変数はグローバル変数として派生生成された全てのインスタンスからアクセスできます。したがって、参照する場合には値が変更されているのが前提として必要に応じて更新取得する必要があります。

またインターフェース内でstatic変数を用いる場合は、finalと宣言しなくても暗黙的にfinalとして扱われ定数扱いとなるので注意しましょう。

こちらもサンプルコードで確認しておきます。

// インターフェース
interface InterfaceSample {
    static int Val1 = 0; // 暗黙的にfinal 定数扱い
    default void print(){};
}
 
// 親クラス
abstract class ClassExtends {
    protected static int val2 = 0; // static変数(クラス変数)
    abstract void setVal2(int val2);
}
 
// 継承クラス
class ClassSample extends ClassExtends implements InterfaceSample{
    // コンストラクタ クラスがインスタンス化される度に変数に1加算する
    public ClassSample(int val2) {
        ClassExtends.val2 += val2;
    }
 
    // 変数の値を表示
    public void print(){
        System.out.println("Val1 = " + Val1 + ", val2 = " + val2);
    }
 
    // val2のsetterメソッド
    public void setVal2(int val2){
        ClassSample.val2 = val2;
    }
}
 
public class Main {

    public static void main(String[] args) {
 
        ClassSample cs1 = new ClassSample(1);
        cs1.print();
        ClassSample cs2 = new ClassSample(1);
        cs2.print();
        ClassSample cs3 = new ClassSample(1);
        cs3.print();
 
        ClassSample cs4 = new ClassSample(1);
        cs4.setVal2(0); // 代入初期化
        cs3.print();
    }
 
}

実行結果:

Val1 = 0, val2 = 1
Val1 = 0, val2 = 2
Val1 = 0, val2 = 3
Val1 = 0, val2 = 0

このサンプルコードでは、InterfaceSampleインターフェース内でstatic変数Val1を定義し「final」句を付けていませんが、暗黙的finalで定数扱いとなり値を変更することはできません。

InterfaceSampleインターフェースを実装し、ClassExtendsクラスを継承したClassSampleクラスをMainクラスのmainメソッド内で複数インスタンス化しています。

ClassSampleクラスのインスタンスからsetVal2メソッドを使用してstatic変数val2の値を代入初期化しています。代入前のcs3インスタンスではval2の値は「3」でしたが、代入後は代入値の「0」(ゼロ)に更新されています。

static変数val2はClassSampleクラス内では定義せず親クラスのClassExtendsクラスで定義していますので、どこで値が変更されているかは注意する必要があります。

static変数(クラス変数)の使い方まとめ

ここでは、紹介したstatic変数の使い方を簡単にまとめています。

static変数の宣言:

アクセス修飾子(publicなど) static 型名 変数名

static変数の呼び出し方:

クラス名.変数名

サンプルプログラム(非static変数)の場合:

class ClassSample {
    private int val = 0;
 
    public ClassSample(int val) {
        this.val += val;
    }
 
    // 変数の値を表示
    public void print(){
        System.out.println("val = " + val);
    }
}
 
public class Main {
 
    public static void main(String[] args) { 
        for (int i = 0; i < 3; i++){
            ClassSample cs = new ClassSample(1);
            cs.print();
        }
    }
 
}
val = 1
val = 1
val = 1

このように「非static変数」の場合はインスタンスが生成される度に0(ゼロ)で初期化されてしまうため、変数の値は変わりません。

続いて「static変数」のプログラムを見てみましょう。サンプルプログラム(static変数)の場合:

class ClassSample {
    private static int val = 0;
 
    public ClassSample(int val) {
        ClassSample.val += val;
    }
 
    // 変数の値を表示
    public void print(){
        System.out.println("val = " + val);
    }
}
 
public class Main {
 
    public static void main(String[] args) { 
        for (int i = 0; i < 3; i++){
            ClassSample cs = new ClassSample(1);
            cs.print();
        }
    }
 
}
val = 1
val = 2
val = 3

「static変数」の場合は3回インスタンスが生成される度に1ずつ加算されています。

まとめ

ここでは、static変数について使い方や使う上での注意点について説明しました。

「static」句を使い分けることで、ローカル変数、グローバル変数、グローバル定数などを使い分けることができて便利です。

変数の使い分けを理解していなければ、思わぬ値が出力されるということにもなりかねません。慣れて使いこなすことができるように、この記事を何度も参考にして下さいね!

この記事を書いた人

熊本在住のフリープログラマ兼ライターです。C/C++/C#、Java、Python、HTML/CSS、PHPを使ってプログラミングをしています。専門は画像処理で最近は機械学習、ディープラーニングにはまっています。幅広くやってきた経験を活かしてポイントをわかりやすくお伝えしようと思います。
お問合せはこちらでも受け付けています。
info@sss-lab.com

目次