Webシステムは基本的に複数人で同時利用されるのが前提のため、マルチスレッドアクセスを考慮した設計・実装を行う必要があります。業務でJavaの各変数とスレッドセーフについて考える機会があったので、「インスタンス変数とローカル変数の違いとスレッドセーフとの関係」について今回は整理してみようと思います。
Javaでの変数毎のメモリ管理イメージ
上記は変数毎のメモリ管理イメージとなります。Javaではクラス変数やインスタンス変数はヒープ領域と呼ばれる共有メモリ領域へ保存されます。複数のスレッドで共有され別のスレッドに書き換えられる可能性があり,スレッドセーフではありません。対してローカル変数はJavaスタックと呼ばれるスレッド固有メモリ領域へ保存されます。スレッド固有領域なので別のスレッドに書き換えられる可能性はないのでスレッドセーフとなります。
インスタンス変数とは
- サンプルコード
|
public class ClassSample{ /** インスタンス変数 */ private String hoge; } |
- メソッドの外に記述します。staticは付けません(staticが付くとクラス変数になります)。
- ヒープ領域と呼ばれる共有メモリ領域へ保存される為、宣言しただけではスレッドセーフにはなりません。
- スレッドセーフを保つ為には初期化する必要があります。
- コンスラクタで初期化
- インスタンス宣言時に初期化
- 最初のget時に初期化
- 他のスレッドからの更新を防ぐ為、修飾子はprivateにする必要があります。
- 当該クラス内の任意のメソッドから参照可能となります。
ローカル変数とは
- サンプルコード
|
public class ClassSample{ public static void methodSample(){ // ローカル変数 int num = 1; } } |
- メソッド内に記述します。
- そのメソッドが実行中の間だけ有効となります。
- Javaスタックと呼ばれるスレッド固有メモリ領域へ保存される為、スレッドセーフとなります。
様々な業務でシステム開発をしているとDTOやVOというクラスが出てきて違いが曖昧になってくる事があるので両者の違いについて整理しておきます。
DTO(Data Transfer Object)
- 基本的には値をまとめて受け渡す事のみを目的とするクラス。
- DTO(Data Transfer Object)はデザインパターンの一つ。
- 過去、J2EEではVO(Value Objects)と呼ばれていた。
- VOとは違い、値は変更可能なのでsetterメソッドを持つ。
VO(Value Objects)
- VO(Value Objects)もデザインパターンの一つ。
- インスタンスコンストラクタでプロパティを初期化してプロパティの変更(set)は外から出来ないようにするクラス。
- VOは不変である事が前提のため、MVC間で使用する場合にはDTOを用いた方が相性が良い。
- VOクラス例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
/** * <p>[概 要] SampleVO。</p> * <p>[詳 細] </p> * <p>[備 考] </p> * <p>[環 境] </p> */ public class SampleVO implements Serializable{ /** * 番号 */ private Integer no; /** * データ */ private String data; /** * <p>[概 要] コンストラクタ。</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @param no * @param data */ public SampleVO(Integer no, String data) { this.no = no; this.data = data; } /** * <p>[概 要] 番号を取得する。</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @return 番号 */ public Integer getNo() { return no; } /** * <p>[概 要] データを取得する。</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @return データ */ public String getData() { return data; } } |
javaでプログラミングしていると必ずと言っていいほど「オーバーロード」や「オーバーライド」という言葉が出てきます。復習も兼ねて今回は両者の違いについて整理してみます。
オーバーロードとは
- 同一クラス内の同一メソッド名で、「引数の型」、「引数の数」、「引数の並び順」のいずれかが異なるメソッドを複数定義すること。
- 同一クラス内の同一メソッド名でも「戻り型」、「アクセスレベル」、「引数名」、「throws節」が不一致している場合はオーバーロードとは見做されないのでコンパイルエラーになります。
Javaでのオーバーロード実装例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public class OverLoadSample{ /** * <p>[概 要] オーバーロードサンプルメソッド1</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @param lang プログラミング言語 */ public static void dispMessage(String lang){ System.out.println("得意なプログラミング言語は「" + lang + "」です。"); } /** * <p>[概 要] オーバーロードサンプルメソッド2</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @param country 国名 * @param lang プログラミング言語 */ public static void dispMessage(String country, String lang){ System.out.println("私の母国は「" + country + "」です。"); System.out.println("得意なプログラミング言語は「" + lang + "」です。"); } } |
JUnit
|
import org.junit.Test; public class OverLoadSampleTest { @Test public void dispMessageTest() { // 「オーバーロードサンプルメソッド1」の実行 OverLoadSample.dispMessage("JavaScript"); // 「オーバーロードサンプルメソッド2」の実行 OverLoadSample.dispMessage("日本", "JavaScript"); } } |
実行結果(コンソール)
得意なプログラミング言語は「JavaScript」です。
私の母国は「日本」です。
得意なプログラミング言語は「JavaScript」です。
オーバーライドとは
- 親クラス(スーパークラス)で定義されているインスタンスメソッドを、子クラス(サブクラス)で再定義すること。
- 親クラス(スーパークラス)のメソッドを変更することは出来ませんが、子クラス(サブクラス)に特化した機能を付与する事は可能です。
- アクセス修飾子が「private」以外でオーバーライドは適用対象となります。(例:「public」・「protected」)
- オーバーライドする側はオーバーライドされる側と「戻り型」、「メソッド名」、「引数型」、「引数の数」が全て一致する必要があります。どれか一つでも異なる場合はオーバーライドとは見做されません。
Javaでのオーバーライド実装例
スーパークラス(親クラス)
|
public class OverRideParent{ /** * <p>[概 要] オーバーライドサンプル スーパークラス用メソッド</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @param lang プログラミング言語 */ public static void dispMessage(){ System.out.println("得意なプログラミング言語はCです。"); } } |
サブクラス(子クラス)
|
public class OverRideChild1 extends OverRideParent{ /** * <p>[概 要] オーバーライドサンプル サブクラス用メソッド1</p> * <p>[詳 細] </p> * <p>[備 考] </p> * @param lang プログラミング言語 */ public static void dispMessage(){ System.out.println("得意なプログラミング言語はJavaです。"); } } |
|
public class OverRideChild2 extends OverRideParent{ } |
JUnit
|
import org.junit.Test; public class OverRideSampleTest { @Test public void dispMessageTest() { // 「オーバーロードライドサブクラス用メソッド1」の実行 OverRideChild1.dispMessage(); // 「オーバーロードライドサブクラス用メソッド2」の実行 OverRideChild2.dispMessage(); } } |
実行結果(コンソール)
得意なプログラミング言語はJavaです。
得意なプログラミング言語はCです。
Webアプリケーションを作成する上で重要になってくるのがMVCと呼ばれるソフトウェア設計モデルです。
MVCとは
- ソフトウェアを「Model」・「View」・「Controller」という3要素の組み合わせでシステムを実装する方式です。
MVCモデルのイメージ
-
Model
- Modelは、システムの中で処理の中核を担うビジネスロジック(アプリケーションが扱う領域のデータと手続き)を担当するシステムの本体部分に当たります。
- データベースなどに格納された生のデータを隠蔽し、抽象化された形のAPIを通じて他のモジュールからのアクセスをコントロールするモジュールです。
- Modelは入出力や表示といった処理は行わず、業務モデルの実装といった部分を担当します。
- アプリケーション特有のルールやロジック(ビジネスロジック)を持ち、データの整合性に絶対の責任を持ちます。
- データの変更をViewに通知するのもModelの責任となります。(Modelの変更を通知するのにObserverパターンが用いられることもあります)。
- 多くのアプリケーションではデータの格納に永続的な記憶の仕組み(データベースなど)が使われています。
- MVCの概念では、データの(UI以外の)入出力は取り扱わないので、データアクセスも本来MVCの概念の範疇を超えるものではありますが、敢えて言えばModelの中に隠蔽されると考えられます。
- J2EEでは JavaBeans、EJBがモデルを担当します。
- サーバサイドでいうserviceに関する処理を行なっています。
- View
- Viewは、表示、入出力といった部分を担当します。
- Modelのデータを取り出してユーザが見るのに適した形で表示する要素です。
- UIへの出力を担当します。
- Modelが提供する抽象化されたデータを、どのような形式で人へ見せるのかを記述したモジュールです。
- GUIアプリケーションの場合、使用するGUI部品や画面上のレイアウトを指定します。
- 必要な情報をModelから取り出して画面へ表示させる為の処理を行います。
- J2EEではServletやJSPで実装します。
- 一般的な HTML表示に関してはJSP、そのほかの特殊なデータ(バイナリデータ)を出力する場合は Servletを用いるのが標準的な使用方法となります。
- Controller
- コントローラは、ViewとModelを制御します。
- 自分自身では表示を行ったりロジックの実行は行わず、Viewからの入力に応じて、必要なロジックの実行をModelに依頼し、その結果表示をViewに依頼します。
- ユーザの入力(通常イベントとして通知されます)に対して応答し、それを処理する要素です。
- UIからの入力を担当します。
- ViewとModelの間に位置して、ユーザーにどんな順序でデータを見せて行くとか、Viewを通したユーザーからの入力をModelへのAPIコールへとマッピングするのがこのモジュールの役割となります。
- ModelとViewに変更を引き起こす場合もありますが、直接描画を行ったり、Modelの内部データを直接操作したりはしません。
- J2EEではServletでControllerを実装します。
- イベントはコントローラで受け取り、画面上構成するボタン(イベント)の数だけ処理があります。
MVCモデルのメリット
- 並行開発
機能毎の分離が明確になることにより独立性が確保出来る。
・開発の分業が可能になる
・専門家を配置しクオリティーを上げる事が可能になる
- 再利用性
コンポーネント間の依存性が最小限に抑えられる為、他の部分の影響を受けにくい。
・コンポーネントの再利用がしやすくなる
・他のコンポーネントの変更が原因で、複数の担当者が同一のソースに対してメンテナンスを行う、というような事態を避けることもでき、保守性も確保される
どうもJavaを学習していると色々なサイトで「インスタンス」と「オブジェクト」が混同して使用されている為、私も含めオブジェクト指向の初心者の方には混乱する一因にもなっています。一度、自分の頭の中の整理も含めておさらいしてみようと思います。
基本的にオブジェクト指向では、すべてのモノや事柄を「オブジェクト」として捉えています。この時点ではクラスという概念がなくても、オブジェクトは存在します。一方、インスタンスとはクラスという概念を具象化したものです。Java言語においては、すべてのオブジェクトはクラスから作られますので、すべてのオブジェクト=インスタンスとなります。このことから開発現場では「インスタンス」と「オブジェクト」を使い分けずに使用することが多い為、初心者の方が混乱する一因になっているようですね。
- ここで少し身近な具体例として「パソコン構成」をクラスに置き換えて考えてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
|
/** * パソコン構成を表示するクラス */ public class Pc { /** * マザーボード */ private String motherBoard; /** * CPU */ private String cpu; /** * グラフィックボード */ private String graphicBoard; /** * メモリ */ private String memory; /** * パソコン・クラスのコンストラクタ */ public Pc() { // マザーボードに「ASUS F2A85-V PRO」をセット motherBoard = "ASUS F2A85-V PRO"; // CPUに「Intel Core i7 Extreme Processor i7-3970X」をセット cpu = "Intel Core i7 Extreme Processor i7-3970X"; // グラフィックボードに「NVIDIA GeForce GTX 690」をセット graphicBoard = "NVIDIA GeForce GTX 690"; // メモリに「Kingston ValueRAM 12GBKit (3x4GB)-DDR3 1066MHz」をセット memory = "Kingston ValueRAM 12GB Kit(3x4GB)-DDR3 1066MHz"; } /** * パソコン構成を表示 */ public void showPc() { System.out.println("パソコン構成を表示"); System.out.println(" マザーボード : " + getMotherBoard()); System.out.println(" CPU : " + getCpu()); System.out.println(" グラフィックボード : " + getGraphicBoard()); System.out.println(" メモリ : " + getMemory()); } /** * パソコンのマザーボードを取得 * * @return マザーボード */ public String getMotherBoard() { return motherBoard; } /** * パソコンのCPUを取得 * * @return CPU */ public String getCpu() { return cpu; } /** * パソコンのグラフィックボードを取得 * * @return グラフィックボード */ public String getGraphicBoard() { return graphicBoard; } /** * パソコンのメモリを取得 * * @return メモリ */ public String getMemory() { return memory; } } |
- パソコンの構成がイメージ出来たでしょうか?
このままではただの設計図でしかないので、次にパソコンを生成するクラスを作成してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
/** * パソコン製造業者クラス */ public class PcManufacture { /** * パソコンの受注依頼を受けるクラス * * @return パソコン・インスタンス */ public Pc acceptPcOrder() { // パソコン・クラスからパソコン・クラスのインスタンスを生成 final Pc pcIns = new Pc(); // 生成されたパソコン・インスタンスをreturnします return pcIns; } } |
- 12行目でパソコン・クラスのインスタンスを生成しています。この作業を「パソコン・オブジェクトの生成」とか「パソコン・クラスのインスタンス化」という表現をしています。
さらにパソコンを注文するお客様のクラスを用意します。
|
/** * パソコンを購入するお客様クラス */ public class Customer { public static void main(final String[] args) { // パソコン製造業者を生成する final PcManufacture pcManIns = new PcManufacture(); // パソコン製造業者にパソコンを注文する final Pc pcIns = pcManIns.acceptPcOrder(); // 注文されたパソコンを表示する pcIns.showPc(); } } |
- パソコン製造業者がいないと、お客様が注文する相手がいませんので、7行目でパソコン製造業者・クラスのインスタンスを生成しています。
- 10行目でパソコン製造業者のacceptPcOrderメソッドを呼び出す事により、パソコン・クラスのインスタンスを生成し、お客様クラスで使用出来るようにしています。
これらのソースコードから「クラス」・「オブジェクト」・「インスタンス」を一覧化してみるとこのようなイメージとなります。
項目 | サンプルソースの例だと | 備考 |
クラス | Pc、PcManufacture、Customer | 設計図 |
オブジェクト | クラスオブジェクト:Pc、PcManufacture、Customer
インスタンスオブジェクト:pcIns、pcManIns | 設計工程で用いる俗語のようなもの。製造工程ではインスタンスと同義語と思ってもさほど差し支えない。 |
インスタンス | pcIns、pcManIns | クラスを具象化した実体 |
どうでしょうか?
この解説である程度ご理解が進めば幸いです。それにしても日本語って難しいですね・・・。
「駑馬十駕」 IT系情報を中心に調べた事をコツコツ綴っています。