【Java】変数、静的・動的メンバ、アクセス修飾子、引数・戻り値について
はじめに
今回は、主にJavaの変数、静的・動的メンバ、アクセス修飾子および引数・戻り値について学習したことをまとめます。
1. ローカル変数、フィールド変数、グローバル変数
/*
* プログラム例1:ローカル変数、フィールド変数、グローバル変数
*/
public class SampleClass() {
private String fieldValue; //フィールド変数
public static String globalValue; //グローバル変数
public void sampleMethod() {
int localValueA; //ローカル変数(メソッド内)
if (条件) {
int localValueB; //ローカル変数(if文内)
}
}
}
例として、上記のコードを用意しました。
(あくまで各変数の説明のためのものなので、プログラムとして特に意味があるものではないことをご了承願います。)
◎ローカル変数
メソッド内の限定された範囲(スコープ)でのみ参照できる変数のことです。
基本的にメソッド、if文、for文の中で定義された変数はそれらの範囲内でしか参照できません。
今回の例では、変数「localValueA」はメソッド「sampleMethod」内、変数「localValueB」はif文内でしか参照できません。それぞれの範囲外で参照しようとするとコンパイルエラーとなります。
◎フィールド変数
クラス内で宣言した変数のことです。クラス内で宣言するので「クラス内変数」とも呼ばれます。
今回の例では「fieldValue」がそれに該当します。
フィールド変数には、アクセス修飾子「private」を付けます。
(アクセス修飾子については3. で詳細を説明しますが、これによりフィールド変数は定義されているクラス内でのみ参照できるようになっています。)
◎グローバル変数
プログラムのどこから(他のクラスから)でも参照できる変数のことを指します。
今回の例では「globalValue」がそれに該当します。
「public(アクセス修飾子、3.で詳細を説明)」と「static(2. で詳細を説明)」をつけることで、他のクラスやそのメソッドからでも参照できる状態になります。
ただし、他のクラスやそのメソッドから情報を操作し値を変更することが可能であるため、思わぬ誤動作の原因に繋がります。
そのためコーディングの作法上、原則として変数にpublicを付けることはありません。
他のクラスの変数を参照したい場合は、publicのゲッター・セッターメソッドを用意し、それらを介して行います。(2.を参照)
2. スタティック(静的)メンバ、インスタンス(動的)メンバ
◎スタティックメンバ(スタティック変数、スタティックメソッド)
/*
* プログラム例2:スタティックメンバのみで構成されたクラス
*/
public class StaticClass {
//スタティック変数
private static String shopName;
/*
* mainメソッド(スタティックメソッド)
*/
public static void main(String[] args) {
shopName = '〇〇商店';
System.out.println("店名:" + shopName);
}
}
// 店名:〇〇商店
プログラム例2のように、staticのついた変数やメソッドをそれぞれ「スタティック(静的)変数」、「スタティック(静的)メソッド」と呼び、それらの総称を「スタティックメンバ」と呼びます。
(「クラス変数」、「クラスメソッド」と呼ぶこともあります。)
スタティックメンバは、プログラム実行前にJavaシステム(JVM)により自動的にメモリにコピーされているため、いつでも参照可能な状態になっています。
プログラム上で最初に実行することとなるmainメソッドは、スタティックメソッドである必要があります。
◎インスタンスメンバ(インスタンス変数、インスタンスメソッド)
/*
* プログラム例3:インスタンスメンバのみで構成されたクラス
*/
public class Shop {
private String product; // 商品(インスタンス変数)
private int taxExcludedPrice; // 税抜価格(インスタンス変数)
private double taxRate; // 税率(インスタンス変数)
private int quantity; // 個数(インスタンス変数)
//コンストラクタ
public Shop(
String product,
int taxExcludedPrice,
double taxRate,
int quantity
) {
this.product = product;
this.taxExcludedPrice = taxExcludedPrice;
this.taxRate = taxRate;
this.quantity = quantity;
}
/*
* ゲッター(商品)
*/
public String getProduct() {
return product;
}
/*
* ゲッター(税抜価格)
*/
public int getTaxExcludedPrice() {
return taxExcludedPrice;
}
/*
* ゲッター(税率)
*/
public double getTaxRate() {
return taxRate;
}
/*
* ゲッター(個数)
*/
public int getQuantity() {
return quantity;
}
/*
* セッター(商品)
* @param product
*/
public void setProduct(String product) {
this.product = product;
}
/*
* セッター(税抜価格)
* @param taxExcludedPrice
*/
public void setTaxExcludedPrice(int taxExcludedPrice) {
this.taxExcludedPrice = taxExcludedPrice;
}
/*
* セッター(税率)
* @param taxRate
*/
public void setTaxRate(double taxRate) {
this.taxRate = taxRate;
}
/*
* セッター(個数)
* @param quantity
*/
public void setQuantity(int quantity) {
this.quantity = quantity;
}
/*
* (商品ごとの)税額
*/
public int productTaxAmount() {
return (int) (taxExcludedPrice * taxRate / 100);
}
/*
* (商品ごとの)小計
*/
public int productSubTotal() {
return taxExcludedPrice * quantity;
}
}
上のプログラム例3のように、staticのつかない変数やメソッドをそれぞれ「インスタンス(動的)変数」、「インスタンス(動的)メソッド」と呼び、それらの総称を「インスタンスメンバ」と呼びます。
インスタンスメンバは、定義した時点ではその情報がまだメモリに存在していないため、プログラムを実行しても参照することができません。
/*
* プログラム例4:インスタンスの生成
*/
public class CashRegister {
/*
* mainメソッド
*/
public static void main(String[] args) {
Shop s1 = new Shop("スナック菓子", 100, 8, 3);
Shop s2 = new Shop("カップラーメン", 120, 8, 5);
Shop s3 = new Shop("お茶(500mL)", 150, 8, 10);
Shop s4 = new Shop("ボールペン", 80, 10, 10);
Shop s5 = new Shop("ノート", 110, 10, 5);
Shop[] productList = {s1, s2, s3, s4, s5}; // 商品リスト
int subTotal = 0; // 小計
int taxAmountTotal = 0; // 税額合計
// 商品リストに対し、繰り返し処理
for(Shop pl : productList) {
System.out.println("商品名:" + pl.getProduct());
System.out.println("単価:" + pl.getTaxExcludedPrice());
System.out.println("数量:" + pl.getQuantity());
System.out.println();
subTotal += pl.productSubTotal();
taxAmountTotal += pl.productTaxAmount();
}
System.out.println("消費税:" + taxAmountTotal + "円");
System.out.println("合計:" + (subTotal + taxAmountTotal) + "円");
}
}
/* 出力
商品名:スナック菓子
単価:100
数量:3
商品名:カップラーメン
単価:120
数量:5
商品名:お茶(500mL)
単価:150
数量:10
商品名:ボールペン
単価:80
数量:10
商品名:ノート
単価:110
数量:5
消費税:327円
合計:4077円
*/
プログラム例4のように、mainメソッドでnew演算子により情報がメモリにコピーされ、「Shop」クラスのインスタンス「s1」〜「s5」が作成されることで初めて参照できるようになります。
そしてコンストラクタによりそれぞれのインスタンス変数が初期化され、「productTaxAmount」メソッド、「productSubTotal」メソッドおよびゲッターメソッドの実行により、「商品名」、「単価」、「数量」、「消費税」、「合計」の値が得られます。
また、今回の例のように、同じ「Shop」クラスのインスタンスを複数作成することもできます。
(スタティックメンバの場合、同じものはメモリー上に1つだけしか作成することができません。)
3. アクセス修飾子
ここまでの説明でも何度か出てきたかと思いますが、アクセス修飾子とは「クラスあるいはクラスメンバを他のクラスにも公開するか(参照できるか)を決める要素」のことです。
主に以下の表2に示した4種類が存在します。
/*
* プログラム例6:アクセス修飾子
*/
package sample;
public class AccessClassA {
private int valueA = 1000; // private
int valueB = 1500; // パッケージアクセス(アクセス修飾子なし)
public int valueC = 2000; // public
protected int valueD = 2500; // protected
}
プログラム例6のように「private」および「public」はクラス名、メソッド名、変数名の先頭に記述しますが、パッケージアクセスの場合は何も記述しません。
/*
* プログラム例7:同じパッケージからのアクセス
*/
package sample;
public class AccessClassB {
public static void main(String[] args) {
AccessClassA accessB = new AccessClassA();
System.out.println("valueA=" + accessB.valueA); //参照不可(コンパイルエラー)
System.out.println("valueB=" + accessB.valueB); //参照可
System.out.println("valueC=" + accessB.valueC); //参照可
System.out.println("valueD=" + accessB.valueD); //参照可
}
}
プログラム例7は、例6と同じパッケージ「sample」の「AccessClassB」クラスから「AccessClassA」の各変数を参照するプログラムとなっています。
アクセス修飾子がpublicである変数「valueC」はもちろん、パッケージアクセスの「valueB」、protectedの「valueD」を参照することができます。
一方、異なるクラスであるため、アクセス修飾子がprivateである「valueA」を参照することはできません。(コンパイルエラーとなります。)
/*
* プログラム例8:異なるパッケージからのアクセス
*/
package sample2;
public class AccessClassC {
public static void main(String[] args) {
AccessClassA accessC = new AccessClassA();
System.out.println("valueA=" + accessC.valueA); //参照不可(コンパイルエラー)
System.out.println("valueB=" + accessC.valueB); //参照不可(コンパイルエラー)
System.out.println("valueC=" + accessC.valueC); //参照可
System.out.println("valueD=" + accessC.valueD); //参照不可(コンパイルエラー)
}
}
プログラム例8は、例6とは異なるパッケージ「sample2」の「AccessClassC」クラスから「AccessClassA」の各変数を参照するプログラムとなっています。
アクセス修飾子がpublicである変数「valueC」を参照することはできますが、privateである「valueA」を参照することはできません。
また、「AccessClassC」は「AccessClassA」とは異なるパッケージに存在しているため、パッケージアクセスである「valueB」およびprotectedの「valueD」を参照することはできません。
上記の通り、パッケージアクセスとprotectedはクラス間でのアクセスの範囲という面では基本的に同じです。
ただ異なるパッケージにサブクラスを作成した際、スーパークラスのパッケージアクセスメンバは継承されませんが、protectedメンバは継承されるといった違いがあります。
4. 引数と戻り値
メソッドが受け取る値のことを「引数」、返す値のことを「戻り値」と呼びます。
上のプログラム例9は、引数として年齢(数値)を渡すと「私は(年齢)歳です。」という文章が返ってくるメソッドです。イメージとしては下の図1のような感じになるかと思います。
まず1行目、メソッド名の直後の括弧内に引数および引数の型を記述します。このメソッドを呼び出す際は、ここで指定したのと同じ型の値を渡す必要があります。
今回の例では、「sampleMethod」メソッドを呼び出す際は、引数としてint型の値を渡します。(異なる型の値を渡そうとするとコンパイルエラーとなります。)
続いて戻り値ですが、まず1行目でメソッド名の直前に戻り値の型を記述します。そしてメソッドの内部で「return文」を用いて返す値を指定します。この際、返す値の型は1行目で指定した型と同じである必要があります。
今回の例では、1行目で戻り値の型を「String」に指定しているため、返す値「message」もString型である必要があります。(異なる型の値を返そうとするとやはりコンパイルエラーとなります。)
ここまでで引数と戻り値を指定するメソッドについて説明しましたが、それらを指定しないメソッドも存在します。
上のプログラム例10は、呼び出された際にただ「改行する」だけのメソッドであり、引数も戻り値も必要ありません。
引数がない場合は、メソッド名直後の括弧を空欄にします。
戻り値がない場合は、メソッド名の直前に「void」と記述し、戻り値がないことを示します。
今回は引数、戻り値両方が存在しないメソッド例を紹介しましたが、片方だけ存在しないメソッドも存在します。
おわりに
今回は、Javaの変数、静的・動的メンバ、アクセス修飾子および引数・戻り値の4項目について説明しました。
初歩的な内容が多めでしたが、役立てていただけると幸いです。
参考文献
・秀和システム 川場隆 著「新わかりやすいJava入門編 第2版」
・TechAcademyマガジン「Javaのローカル変数とグローバル変数の概念とは【初心者向け】」
(https://techacademy.jp/magazine/19485)