補足(1): 「変数は初期化されていない可能性があります」というエラーについて

Javaでは「値が入っていない変数を使おうとした時」にエラーになる。NetBeansでは次のようなエラーが表示される。

変数aは初期化されていない可能性があります

ちょっとややこしいので、どのような時にOK or NGになるかを解説する。

ソースコード全体

[OK] aに値が入っているのでエラーにならない

変数aに値が代入されているので問題ない。

[NG] aに値が入っていないのでエラーになる

変数aに値が代入されてないので、13行目のreturnがエラーになる。

[NG] aに値が入らない場合があり得るのでエラーになる

変数xが0.5未満の時は変数aに値が代入される(乱数については教科書P.199を参照)が、それ以外の時はaに値は代入されない。xが0.5未満かどうかは実行時に決まることだが、コンパイル時に「代入されない可能性」があればエラーになる。

[OK] aに値が入るのが確実なのでエラーにならない

条件分岐があったとしても、全ての条件で代入していればエラーにならない。ただし、「全ての条件」とはコンパイラが判断できる条件のことなので、次の場合エラーになる。

[NG] aに値が入るのが確実だが、コンパイラが判断できないのでエラーになる

「xが0.5未満」と「xが0.5以上」という条件をチェックしているので、このどちらかにしか当てはまらないが、コンパイラは「どのような条件か」についてはチェックしないので、「この2つの条件に当てはまらない場合」があり得ると判断しエラーになる。else節をつければ全ての条件について記述できるのでエラーにならない。

パッケージ(2): 他のパッケージの利用・アクセス修飾子、クラスの基礎(3): コンストラクタ(メソッドの一種)

前回のまとめ

  • メソッドの定義・利用(呼び出し)について
    • C言語とだいたい同じ
    • メソッドが所属するクラスのフィールドにアクセス(読み・書き)できる。
    • メソッドが所属するクラスのメソッドを呼び出すことができる。
    • 他のインスタンスのメンバーは「インスタンス名.メンバー名」で使える。
  • 課題の解答例: MedicalRecord.java(48〜56行目)

パッケージ(2): 他のパッケージの利用・アクセス修飾子

他のパッケージに所属するクラスの利用方法とアクセス修飾子の機能について確かめよう。

  1. パッケージ「d00000.lecture06」を作成する。
  2. そこにMain1クラスを作成する。
  3. Main1クラスにmainメソッドを作成する。

最終的なソースコード: Main1.java

他のパッケージに所属するクラスを直接使うにはクラス名の前に「パッケージ名.」をつける(9行目)。

d00000.lecture05.MedicalRecord mr1 = new d00000.lecture05.MedicalRecord();

import文を使うと「パッケージ名.」を省略することができる(3行目)。

import d00000.lecture05.MedicalRecord;

この場合、単に「MedicalRecord」と書いた場合は「d00000.lecture05.MedicalRecord」として扱われる。同じ名前のクラスを複数使いたい場合は、パッケージ名を省略できるのは1つだけ。ちなみに、import文なしでパッケージ名を省略した場合は、同じパッケージのものを指す。

注意: 教科書の説明だと、import文を使わないと他のパッケージのクラスが使えないように読めるが、ここで確かめたように、そうではない。import文は単に他のパッケージのクラスを利用するときの「パッケージ名.」を省略できるようにする、という機能である。C言語の#includeとは全然違う。

他のパッケージで使えるクラスは、classの前に「public」というアクセス修飾子がついていなければならない。また、インスタンス化ができたとしても、その中のメンバーにも適切なアクセス修飾子がついていなければならない。「クラスにつくアクセス修飾子」と「メンバーにつくアクセス修飾子」の2種類があることに注意しよう。

クラスの基礎(3): コンストラクタ(メソッドの一種)

メソッドを追加して、MedicalRecordクラスをもっと「使える」クラスにしていこう。

  1. d00000.lecture06にMain2クラスを作成する。
  2. Main2クラスにmainメソッドを作成する。
  3. d00000.lecture05のMedicalRecord.javaをd00000.lecture06にコピーする(前回と同じくリファクタリングコピーを使う)。

最終的なソースコード: Main2.javaMedicalRecord.java

これまでの利用の仕方を確認していこう(Main2.java: 5〜11行目)。8〜10行目をコメントアウト(コメントにすること)して実行してみよう。このようにフィールドをセットし忘れたりするとおかしな動作をすることがある。クラスの設計については色々な考え方があるが、ここでは、「セットしやすいように工夫する」とか「セットし忘れても大丈夫なようにする」といった考え方で改良してみよう。

まずは、フィールドをセットする専用のメソッドを用意してみよう(Main2.java: 15行目、MedicalRecord.java: 58〜68行目)。これを使うことで、3つのフィールドを1行でセットできる。thisについては教科書の110ページを参照すること。メソッドの引数とフィールドの名前が被った場合、メソッドの本体では引数の方が優先して使われる。このような名前が被った状態でフィールドの方を使うためには「this.」をつけてフィールドの方であることを示す必要がある。また、表示する文字列を作るメソッドも用意しよう(Main2.java: 16行目、MedicalRecord.java: 70〜78行目)。これでだいぶシンプルになった。

次は、インスタンス化したら、デフォルトで値を設定するということを考えよう。これにはコンストラクタ(constructor)というちょっと特殊なメソッドを追加することで実現することができる。コンストラクタは、インスタンス化のタイミングで自動的に実行されるメソッドで、次のようにして定義したメソッドがコンストラクタにとして扱われる。

  • メソッド名はクラス名と同じにする。
  • メソッドの戻り値(返り値)の型は記述しない。

まずは引数なしのコンストラクタを追加しよう(MedicalRecord.java: 80〜91行目)。いつものようにインスタンス化する(Main2.java: 19行目)と、このコンストラクタが実行される。最初はMedicalRecord.javaの85行目はコメントを外して、コンストラクタが実行されたことを確認しよう。

次に、引数ありのコンストラクタを追加しよう(MedicalRecord.java: 93〜104行目)。これを使えば、任意の値をインスタンス化の時に設定することができる(Main2.java: 23行目)。

コンストラクタを作ると、配列を作成することも簡単になる(Main2.java: 26〜30行目)。また、任意の値とともインスタンス化することができるので、名前をつけずにインスタンス化し、そのメソッドを実行するなんてことにも利用しやすくなる(Main2.java: 33行目)。

コンストラクタにまつわるハマりどころ

実は、インスタンス化にはコンストラクタが絶対に必要。

今まで、コンストラクタを定義してなかったのにインスタンス化ができていたのはなぜか? コンストラクタを1つも定義しないと、デフォルトコンストラクタが作成される。これがインスタンス化に使われる。

ハマりどころ: コンストラクタを1つでも定義すると、デフォルトコンストラクタは作成されない。引数ありのコンストラクタだけを定義した場合、引数なしの形でインスタンス化することができない。

クラスの基礎(2): メソッドの定義・利用(呼び出し)

前回のまとめ

  • パッケージ(教科書P.164〜)
    • クラスをひとまとめにしたもの
    • パッケージが違えば名前のクラスでも同時に存在できる
  • クラスの基礎(教科書P.66〜)
    • 「フィールド」と「メソッド」で構成され(それ以外のものは無い)、それらを「メンバー」という。
    • クラスは「設計図」。そのままでは使えない。「実体(インスタンス)」を作る必要がある(「クラスは設計図」という比喩だけでは説明できない機能があることに注意)。 
    • 教科書ではインスタンスではなく「オブジェクト」という言葉を使っているが、「オブジェクト」は文脈によって意味が違ったりするので、本講義では「インスタンス」という言葉を使う。
    • インスタンス化(インスタンスを作ること)にはnew演算子を使う。

準備

  • パッケージ「d00000.lecture05」を作成する。
  • 前回作ったMedicalRecord.javaを右クリックしてコピーする。
  • 今回作ったパッケージを右クリックして、貼付け→リファクタリング コピー→リファクタリングを選択する(パッケージ宣言を変更してコピーが行われる)。
  • 今回作ったパッケージにMainクラスを作成し、さらにmainメソッドを作成する。

メソッドの定義・利用(呼び出し)

MedicalRecordクラスにメソッドを追加しよう。

注: クラスやフィールドにアクセス修飾子の「public」が追加されている。解説は次回。

  • メソッドの定義
    • アクセス修飾子(public、private、protected、無し)
    • 戻り値(返り値)の型(戻り値がない場合はvoid)
    • メソッド名
    • 引数(型と引数名のペア)が0個以上
    • 本体
      • return文(本体の実行を終了し呼び出し元に値を返す)
  • メソッドが所属するクラスのフィールドにアクセス(読み・書き)できる。
  • メソッドが所属するクラスのメソッドを呼び出すことができる。
  • 他のインスタンスのメソッドを呼び出すときは次のよう記述する。
    インスタンス名.メソッド名(引数)

パッケージ(1): パッケージの作成、クラスの基礎(1): フィールド

前回のまとめ

  • 配列を作成するにはnew演算子を使い、次のように書く。
    double[] weight = new double[3];
    あとはC言語と大体同じ。
  • 配列の宣言と同時に初期化も行う場合は次のように書く。
    double[] height = {181.2, 167.5, 175.0};
  • メソッドはC言語の関数と大体同じ。
    • 戻り値(返り値)の型
    • メソッド名
    • 引数(引数の型と引数名)
    • return文

パッケージ

  • プログラミングでは、名前をつける作業が沢山ある。クラス名、フィールド名、メソッド名、インスタンス名(変数名)、等々。これらの名前を適切につけることはわかりやすいプログラムを書くことにおいて非常に重要。
  • 名前がかぶると困る。
  • 名前空間(Namespace)
  • Java言語における名前空間の仕組み: パッケージ
  • パッケージ名はユニーク(一意)なものをつける。ドメイン名を逆から記述するのが慣例。
  • 今回は「ユーザ名.lesson04」

NetBeansでパッケージを作る

ソースパッケージを右クリックして新規、Javaパッケージを選択する。

package1.png

パッケージ名を入力。場所がソースパッケージになっていることを確認する。

package2.png

ソース・パッケージの下にできていることを確認する。

package3.png

パッケージに対応してフォルダ(ディレクトリ)ができていることを確認する。

package4.png

クラスとは

  • データと処理をまとめたもの(教科書)
  • 構造体に関数がついたもの(C言語がきちんとわかっている人向けの説明)
  • 型の定義
  • etc..

クラスはフィールドとメソッドで構成される。例えば、下図は、フィールド2つ、メソッド2つを持つクラスを表す。

class.png

クラスの基礎(1): フィールド

カルテ(medical record)を表すクラスを作ろう。今回は、先ほど作ったパッケージに所属させる。パッケージを右クリックして作成する。パッケージが指定されていることを確認する。

medicalrecordclass.png

MedicalRecord.java

ポイント:

  • パッケージ宣言(1行目)
  • フィールドが3つだけのクラス

前回のプログラムでは、体重と身長を別の変数(配列)で管理していたが、同じ人のデータを1つのクラスで表すことができるようになる!

実際に、これを使ってBMIを計算するプログラムを作る。mainメソッドを持つMainクラスを作成しよう。先ほどと同様にパッケージに所属させる。

Main.java

ポイント:

  • パッケージ宣言(1行目)
  • クラスを実際に使う(インスタンスを作る)には、new演算子を使う。
    MedicalRecord mr1 = new MedicalRecord();
  • フィールドにアクセスするには「インスタンス名.フィールド名」とする。
    mr1.name = "たろう";
  • 配列の作成も同様。
    MedicalRecord[] mrArray = new MedicalRecord[3];
    ただし、これは、MedicalRecordが3つ入る配列を作る、というだけなので、それぞれの要素がインスタンス化されているわけではないことに注意!

オブジェクト指向プログラミング的ではないJavaプログラム

前回のまとめ

  • 数値を表す変数の型はC言語と大体同じ。
  • 整数はintを、実数はdoubleを使っておけば大抵は問題ない。
  • boolean型:
    • 真偽を表し、取りうる値は「true」か「false」の2つだけ。
    • C言語にはない型。C言語では0を偽、それ以外を真として扱っている。
    • if文等で使う。
    • 「==」、「>」、「<」等は演算子でboolean型を返す。例えば
      boolean a = (1 == 2);
      のaはfalseになる。
  • 制御構文(if else、for、while等)はC言語と大体同じ。

オブジェクト指向プログラミング

オブジェクト指向プログラミング(object-oriented programming, OOP)というプログラミングにおける考え方がある。これは、プログラムの組み方をオブジェクト(モノ)を中心にしよう(そうすることによって効率よくプログラムを作成することができる)という考え方である。JavaはOOPに基づいてプログラミングするための機能をサポートしたオブジェクト指向プログラミング言語(object-oriented programming language, OOPL)である。

注意: (特に日本においては)オブジェクト指向プログラミングのことを単に「オブジェクト指向」という言葉で扱い「オブジェクト指向分析設計」、「オブジェクト指向モデリング」といったものまでひっくるめてしまっている場合がある。単に「オブジェクト指向」と書かれているときは何を指しているか注意して読むこと。

ここでは(次回のOOPの解説の前提として)OOP的ではないプログラムとして、BMIを計算するプログラムを作成する。

  • ソースコード: BodyMassIndex.java
  • ポイント:
    • Javaにおける配列の使い方(C言語との違い)
    • メソッド(C言語における関数)
    • 「static」について

変数、演算子、制御文

前回のまとめ

  • NetBeansにおけるクラスの作成手順、実行方法
  • ソースコードのフォーマット
  • クラスの名前はアッパーキャメルケースにする

C言語との違いを中心に。詳しくは教科書を参照すること。

注: 「だいたい同じ」とは「(些細な)違いがある」ということなので全く同じと思わないこと。

変数

数値

C言語において、数値を格納する変数の型はint、long、float、double。Java言語でもだいたい同じ。

文字(単独の文字)

C言語で文字はchar型を使う。文字をシングルクォーテーションで囲むことによって文字を表現する。

これで、変数vには文字「a」が代入される(正確には文字「a」の文字コードである97が代入される。つまりcharは整数型)。

Java言語でもだいたい同じ。

文字列(複数の文字)

C言語ではchar型の配列として扱う。

Java言語ではString型を使う。

boolean型(論理型)

C言語では、真偽を表すのにint型を使い、真が0以外、偽が0としてきた。

例えば、

とした場合、aには0が格納される。

Java言語には真偽を表すboolean型がある。boolean型は値としてtrue(真)かfalse(偽)をとる。

論理演算はC言語と同じ。

型の種類

Java言語の型は、大きく2種類に分類される。

  • プリミティブ型
  • 参照型(クラス型)

ここまで紹介した型のうち、String型が参照型で、それ以外はプリミティブ型。とりあえず、ここでは「2つの種類がある(違いがある)」ということを覚えておくこと。

変数の宣言

C言語では変数の宣言は関数の最初で行う(最近のC言語ではそうでもない)。Javaでは関数(メソッド)の最初じゃなくても良い。

演算子

演算子とは「演算を表わす記号」のこと。

C言語とだいたい同じ。

演算子によって、被演算子(演算が作用する対象)として使える型が違うことに注意すること。

  • 算術演算子
  • 代入演算子
  • インクリメント演算子、デクリメント演算子
  • 比較演算子
  • 条件演算子
  • 論理演算子
  • キャスト演算子
  • ビット演算子

どんな演算が行われているか?演算の結果がどうなるか?を理解しよう。

制御文

Java言語の制御文は条件の判定にboolean型を使う(C言語は0かそれ以外かを判定する)。これ以外はC言語とだいたい同じ。

制御文の記述に関する注意: かっこは省略するな!ただ「省略できる」ということは知っていなければならない。

  • if else
  • for
  • while
  • break
  • continue
  • switch

NetBeansで「hello, world」

プロジェクトを作る

新規プロジェクト

新規 Javaアプリケーション

MyFirstProject 1

クラスを作る

MyFirstProject 2

New Javaクラス

メインメソッドを書く

実行する

MyFirstProject NetBeans IDE 8 1 と NetBeansでHello World

Spring Bootによるウェブアプリ開発: [補足01] ユーザ認証(2)

前回の続き。

アカウントの情報がハードコードだと管理が面倒なので、データベースを使おう。その際、保存するパスワードはハッシュ化しよう。

作成・変更したファイル

今回使うページを次に示す。

パス アクセス制限 備考
/suppl01/admin/ 管理人ロールをもつ登録ユーザ 管理人用トップページ
/suppl01/admin/add_user 管理人ロールをもつ登録ユーザ ユーザ登録フォーム

アカウント情報を格納するテーブル

40.1 User Schemaにあるようにテーブルを作ると、簡単にアカウント情報として利用することができる(もちろん設定をすれば他の構成でも使えるが)。今回は、これを参考に次のSQL命令でテーブルを作ることにする。

40.1 User Schemaからの変更点は以下の通り。

  • passwordカラムは、ハッシュ化したパスワードを保存する為にサイズを100に変更
  • すでに存在している場合は作成しないようにするために「IF NOT EXISTS」を追加

このSQL命令をH2コンソールで実行してもいいし、JdbcTemplateで実行してもいいのだが、ファイルに記述したSQL命令をアプリの起動時に実行する方法(78.3 Initialize a database using Spring JDBC)がある。
今回はこの方法を使うことにし、src/main/resources/schema.sqlを作成する。

WebSecurityConfigurerAdapterを継承した設定用クラス

参考: 5.6.2 JDBC Authentication

  • 16行目: 設定時にJdbcTemplate(かDataSourcedの)インスタンスが必要なので、引数として「JdbcTemplate jdbcTemplate』を追加し、DIコンテナから取得する。
  • 21行目: パスワードのエンコーダとして使うBCryptPasswordEncoderをインスタンス化する。
  • 22行目: JDBCを使った認証で、どのデータベースを使うか(jdbcTemplate.getDataSource())とどのパスワードエンコーダを使うか(passwordEncoder)を指定する。
  • 29行目: 「/suppl01/admin/**」へのアクセスへは管理者ロールを持つアカウントがアクセスできることを指定する。

コントローラ

  • 86行目: SecurityConfiguration.javaと同様にパスワードエンコーダをインスタンス化する。
  • 87ページ: パスワードエンコーダのencodeメソッドを使ってパスワードをエンコードしたものを保存する。
  • 88ページ: ロールをauthoritiesテーブルに保存する。usersテーブルの情報だけではダメなことに注意する。

テンプレート

add_user.html

  • 14行目: ロールは「ROLE_」をつけることに注意する。

動作確認

ハードコードしたadminでログインし、一般ユーザ、管理者ユーザを作成し、それらのユーザで正しくアクセスできること等を確かめてみよう。

Spring Bootによるウェブアプリ開発: [補足01] ユーザ認証(1)

フォームからユーザ名とパスワードを入力してログインする機能(ユーザ認証)を実現しよう!

作成・変更したファイル

今回使うページを次に示す。

パス アクセス制限 備考
/suppl01/ 誰でも トップページ
/suppl01/login 誰でも ログインフォーム
/suppl01/loginProcess 誰でも ログイン処理を行う(ログインフォームからのPOST先)
/suppl01/logout 誰でも ログアウト処理を行う(ログアウトフォームからのPOST先)
/suppl01/secret/ 登録ユーザ ログイン後に遷移するページ
/suppl01/secret/info 登録ユーザ アカウントの情報を表示するページ

Spring Securityの有効化とBasic認証の無効化

Spring Securityの機能を加えよう。
build.gradleのdependenciesに次の1行を加える。

これでSpring Securityの機能が有効になる。デフォルトでBasic認証の機能が有効になるので、
パスワードとユーザ名を入れないとページが見えなくなる。

Basic認証

今回はBasic認証は使わないので無効にしよう。無効にするにはapplication.propertiesに次の1行を追加する。

WebSecurityConfigurerAdapterを継承した設定用クラス

WebSecurityConfigurerAdapterクラスを継承したクラスを作ることで、ユーザ認証の設定を行うことができる。

  • 9行目: このクラスが設定用のクラスであることを示す。これに加え「@EnableWebSecurity」を記述するという解説が様々なところでなされているが、この設定については82.1 Switch off the Spring Boot security configurationを参照すること。
  • 14〜16行目: ログインできるアカウントを設定する。今回はハードコーディングする(次回、データベースに変更する)。
  • 21〜22行目: ユーザ認証を要求するページとしないページを設定する。
  • 24〜26行目: ログインに関する設定をする。ログインフォームのURLと、ログインフォームの中でどのパラメータ名(input要素のname属性)を使うか、ログインを処理するURL(ログインフォームのactionで指定するパス)、成功した時に遷移するURL、ログイン失敗した時に遷移するURLを指定する。
  • 28〜29行目: ログアウトに関する設定を行う。ログアウトを処理するURL(ログアウトフォームのactionで指定するパス)、ログアウトが成功した時に遷移するURLを指定する。

コントローラ

  • ログインを処理する「/suppl01/loginProcess」とログアウトを処理する「/suppl01/logout」はコントローラで定義しない(Spring Secutiryの機能でアクセスできるようになるため)。
  • 42行目: ユーザ情報を取得するには、@AuthenticationPrincipalアノテーションをつけたUserDetails型を引数にする。

テンプレート(解説が必要なもの)

login.html

  • 10〜12行目: ログインに失敗した場合は「/suppl01/login?error」に遷移する(SecurityConfiguration.javaを確認せよ)。つまり「error」というパラメータが付加される。「param.containsKey(‘error’)」でerrorというパラメータがあるかチェックできる(参考: request/session 属性などに対するウェブコンテキストネームスペース
  • 13行目: methodはpostにする。
  • 13行目: actionはh:action属性で指定する(単なるaction属性じゃダメ)。action属性は、th:actionでリンクURL式を使う。このようにすることによって、自動的にCSRF対策用のパラメータが付加される。
  • 13行目: URLはSecurityConfiguration.javaのloginProcessingUrlで指定したURLにする。
  • 14〜15行目: name属性はSecurityConfiguration.javaで指定した文字列を設定する。

secret/{index,info}.htmlのログアウトフォーム

  • ログインの場合と同様に、methodはpostとし、actionはth:action属性で指定する。URLはSecurityConfiguration.javaのlogoutUrlで指定したURLにする。
  • 単なるリンクにしたい場合は、logoutRequestMatcherメソッドを使った形にする(調べてみよう)。

問題点

基本的なユーザー認証の仕組みを見てきたが、このままでは不便(ユーザ情報がハードコードで管理が面倒)だし問題がある(パスワードが平文で管理されている)。次回では、ユーザ情報をデータベースに保存し、パスワードをハッシュ化して保存する方法を紹介する。

Spring Bootによるウェブアプリ開発: 起動方法(サービスの公開)

これまでアプリの開発における起動方法として、NetBeansのメニューから実行という方法を使ってきた。
この方法は開発時にはいいが、IDEのないPCにコピーしての実行や、サーバでの実行には向かない。

gradlewを使ってbootRunタスクを実行する。

これまでNetBeansからのGradleのタスクを実行してきたが、IDEを使わずにコマンドラインからも実行できる(というかこれが本来の使い方)。

ターミナル(コマンドプロンプト)で、プロジェクトのディレクトリをカレントディレクトリとし、

./gradlew bootRun

と実行する。gradlewに実行権限がなければ事前に「chmod +x gradlew」としておく。Windowsの場合は、

gradlew.bat bootRun

とする(未確認)。

FAT JARを作る。

クラスファイルやリソース等をJAR(Java Archive)という形式にまとめることができる。もともと、JARファイルは自分の作ったプログラムをまとめるもので、ライブラリ等は別に準備して実行する、という使い方が普通であった。最近は、依存するライブラリ(サーブレットコンテナ)等を全て含めて1つのJarファイルで実行ができるようにした「FAT JAR」を利用することも増えている。Spring BootではこのFAT JARも簡単に作ることができる。

./gradlew build

作られたJARファイルは、build/libに置かれる(例えば、webapp-0.0.1-SNAPSHOT.jar)。

これを実行するには、そのファイルを適当なディレクトリにコピーし、そのディレクトリをカレントディレクトリとして、次のように実行する。

java -jar webapp-0.0.1-SNAPSHOT.jar

Windowsならばダブルクリックで起動できる場合もある。しかし、標準出力に出力されるメッセージが読めないので、上記コマンドで実行するのがおすすめである。

FAT JARと同じところにstaticディレクトリをおけば、静的ファイルも扱うことができる。

作ったアプリは公開しよう

残念ながら、Javaで作ったアプリを公開できるサービスは少ない。レンタルサーバでは使えるのはPHPのみというサービスが多い。現実的には、VPSを借りJavaをインストールして実行する、ということになるだろう。

VPSを借りるという以外では、Javaが実行できるPaaSを使う方法がある(これも対応サービスは少ないが)。例えば、HEROKUならばDeploying Spring Boot Applications to Herokuというドキュメントも用意されている。条件によっては無料で使うこともできる。