20201119のJavaに関する記事は10件です。

Google App Engine Java 8 から Java11 移行対策

この記事では、Google App Engine for JavaのJava8からJava11への移行方法について考えてみる

Google App Engineとは

Google App Engine(GAE)は、Google謹製のPaaSサービスであり、Google Cloud Platformの中でも最古参級の古株である。
Go, Python, .NETなど様々な言語に対応しており、もちろんJavaにも対応している(GAE/J)。

GAE Java11 からサーブレットコンテナではなくなった

2019年10月にこれまでのGAE Java 8版に加えて、GAE Java11版が正式リリースされた1
だが、Java 11版のGAE/Jはこれまでのバージョンとは大きな違いがある。
Google App Engine Java 11 ドキュメント - Java 11 ランタイム環境 - フレームワークの互換性にこんなことが書いてある。

App Engine Java 11 ランタイムでは、実行可能な JAR ファイルをデプロイできます。ランタイムには、ウェブサービス フレームワークは含まれません。つまり、サーブレット ベースのフレームワークやライブラリの使用に制限されません。

「サーブレット ベースのフレームワークやライブラリの使用に制限されません」 なんて書いてあるが、要するにGAE/Jはサーブレットコンテナではなくなったということだ。
つまり


GAE Java8
WARファイルをデプロイするサーブレットコンテナ

GAE Java11
JARファイルをデプロイする単なるJavaコンテナ

ということで、同じJavaのPaaSといっても、全く別物になったのである。
Ubar JAR(Fat JAR)であれば、なんでもデプロイできるようになったので、柔軟性が増したのだが、これまで作ってきたWARファイル形式のアプリケーションは一工夫しないとJava11版のGAEでは使えないことになった。
そこで、GAE/JでJava11へバージョンアップするための方法論はどのようなものがあるのか検討してみたい。

GAE Java8 vs Java11

GAEのJava8とJava11には、その他にもいくつか違いがあるので、比較してみる
ポイントは、Java11になって同じインスタンスクラスでもメモリが倍増している点と、すでにJava8の段階でレガシーに位置付けられていた App Engineにバンドルされていた各種API(com.google.appengine.api 以下のパッケージ)が全廃されたことである。

Java8 Java11
コンテナ Servletコンテナ JVMコンテナ
デプロイするファイル WARファイル JAR(Uber Jar)ファイル
構成ファイル XML or YAML YAMLのみ
App Engine バンドル サービス Cloud Datastore, Memcache などのGAE専用API 廃止
(各サービス提供のAPIを使用)
インスタンスサイズ
(Auto Scaling/F1)
メモリ:128MB CPU:600MHz メモリ:256MB CPU:600MHz

移行戦略の検討

前述のようにGAE/Jは、Java8からJava11の間で互換性を失ったわけだが、それでも移行したい場合はどのようにすればいいのか方法を考えてみる。
なお、具体的な方法は、後日別の記事で紹介したい。

A. WARファイルを生かして移行

GAE/Jがサーブレットコンテナではなくなったとしても、それでもサーブレットを使い続けたいといった場合はどうすればよいのか考えてみる。

自力でJettyを立ち上げる

手っ取り早い方法が、GAE/Jの入門ガイドにも書かれているPure JavaのWeb/APサーバーであるJettyを使ってWAR ファイルを JAR ファイルに再パッケージ化する方法だ。
WARファイルを内包したUber Jar(Fat Jar)を作成し、Jettyを起動させWARファイルを読み込ませるという方法だ。
この方法なら、app.yamlを修正するだけで、既存のWARファイルが使用できるようになるはずだ。
(もちろん、廃止になったGAE APIは使用できないので、その部分は修正が必要)

B. Servletを捨てて移行

サーブレットを捨てる覚悟ができれば、よりモダンなWebフレームワークへ移行する道が開ける。
GAE/Jのリファレンスやチュートリアル、サンプルソースを見る限り、デファクトスタンダードとなりつつあるSpring BootをWebフレームワークとして推しているようなので。Spring Boot への全面移行を考えてみる。
方法論としては、大きく分けて二つある。

B-1.Web.xmlのみををSpring Bootに書き換える

基本的にサーブレットクラス自体は、Spring Bootでも使用可能だ。
変更ポイントは今までWeb.xml(もしくはWebServletアノテーション)で記述していたサーブレットマッピングで、これをどこかに記述すればよいことになる。
方法はいくつかあるのだが、Spring Boot Servletマッピングあたりを参照してほしい。

B-2.全面的にSpring Bootに移行

最後の手段は、いっそ全面的にSpring的なAutowiredしまくったソースに書き換えてしまおうという作戦。
当たり前だが、これが一番手間がかかる。

メリット/デメリット

これらの方法論のメリット/デメリットは簡単で、デメリットはA → B-1 → B-2と作業が増え面倒になり、その分、サーブレットというレガシーフレームワークを一掃することができる。
できれば面倒な手は加えたくないが、いつまでもサーブレットを使いつつづけるのも大概なので、どのあたりで折り合いをつけるかという判断である。
しかしながら、どのみち使えなくなったGAEのAPIに対してなんらかの手を入れなくてはならない以上、これを機会に一気にSpring Boot化するというのが王道だと考える。
なお、GAEのAPIの移行は、App Engine 固有の API からの移行 参照。

注意事項

筆者は自分のサイト欧亜大陸鉄道(戦前の世界中の時刻表を利用した、タイムスリップ仮想乗り換え案内サイト)で実際にJava8からJava11への移行を行った。移行した方法は、上記のB-2にあたる、Spring Bootへの全面的な書き換えである。
この作業で一つ大きな問題にぶち当たったので、参考情報として付記しておく。
GAE/Jでは、Java 8からJava 11に移行することで同じインスタンスクラス、すなわち同じ料金で使えるメモリが倍増するのだが、Spring Bootに移行したことで消費メモリは倍以上増加し、結局インスタンスクラスを1段階上げるはめになった。
このあたりの事情は各アプリで異なるため、一概には言えないが、Spring Bootはなかなかメモリを食うフレームワークであることは間違い。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

入力フォームの情報+複数枚の画像をバイト配列に変換する方法【Servlet/JSP】【MySQL】

1.前提知識・条件

本記事では、Java、Servlet/JSP、MySQLなどの環境構築について、構築できている前提とするので省略します。
Java、Servlet,JSP、MySQLに関する基礎知識を前提としています。
必要となる知識
Java : リスト, イテレータ など
Servlet/JSP : GET/POST, データベースとの通信 など
MySQL : Javaのソース上でクエリを実行する方法 など

注意事項:冗長的なコードになっている可能性があるのでご了承ください。

2.投稿のきっかけ

研修でServlet/JSP、MySQLを使ってWebアプリを構築することになり画像の処理が必要でした。
MySQLの方をMediumBLOB型にして、一枚の画像のみを格納・表示させる所までは、それなりに情報が載っており、比較的容易でした。
ですが、入力フォームのテキスト情報と一緒になった場合、画像を複数枚にした場合に情報が少なく行き着くまでにかなり苦労しました。
自分の備忘録としても、他の方の参考になれば良いかと思います(需要があるかは不明)。

3.画像の保存

大まかな処理の流れは、下図のようになります。
flow1.png
①Webブラウザ(JSPファイル)のフォームに情報・画像を登録
②送信ボタンが押されるとServletに登録情報が送信される
③Servletでデータを取得し、画像データをバイト配列に変換する
④DAOクラスを呼び出し、MySQLへ保存する
※本記事では④の処理を省略しています。

4.テーブル構成

今回は簡単に画像名テーブルと画像テーブルの二つをサンプルとします(省略に省略を重ねています)。
画像を複数枚登録する場合は、画像名:画像 = 1:多になることを想定しています。

画像名テーブル

nameId name
varchar(4) varchar(16)
Primary Key

画像テーブル

nameId imageId image
varchar(4) varchar(4) mediumblob
Primary Key

5.実際のコード

form.jsp
<form action="/test/RegisterServlet" enctype="multipart/form-data" method="post" name="reg" id="regform">
 <div>画像名<input type="text" name="imagename"></div>
 <div class="inputarea">画像
  <input type="file" name="file" id="ItemImage" multiple="multiple" accept="image/*" required >
 </div>
 <input type="submit" value="登録">
</form>

formタグ内でenctype="multipart/form-data"を指定することで、複数のデータ型を扱うことが出来る様になります。

RegisterServlet.java
//送信されてきた情報の取得
Collection<Part> pa = request.getParts();
//画像名用の変数を宣言
String name;


ArrayList<Part> regImg = new ArrayList<Part>();
//画像データをバイト配列に変換したとき用のリスト
ArrayList<byte[]> imgList = new ArrayList<byte[]>();

//拡張for文でCollection<Part>の中身をすべて取り出す
for(Part p : pa) {
 //要素のname属性名が"file"であった場合、画像データ用のリストへ格納
 if(p.getName().equals("file")) {
  regImg.add(p);
  } else {
  //画像以外である場合、nameに値を格納
  name = request.getParameter(p.getName()); // ※1
  }
 }

//画像データ格納用のリストの中身をすべて取り出す
for(Part p : regImg) {
 //FileInputStream型にキャストする
 FileInputStream fis = (FileInputStream) p.getInputStream();
 //readAllBytesメソッドでbyte配列に変換する
 byte[] bytes = fis.readAllBytes();
 //byte配列用のリストに格納する
 imgList.add(bytes);
 }
 //DAOメソッドの呼び出し
}

enctype="multipart/form-data"で送られてきたデータはPart型で送られます。
複数のデータを送った場合は、request.getPartsメソッドでCollection型の変数に格納できます。

※1
今回は画像名だけなのでelse文以下は、「name = request.getParameter(p.getName())」だけになっています。
画像名以外のテキストも登録される場合は、HashMapなどを宣言して(p.getName(), request.getParameter(p.getName())で(name属性, パラメータ)の様にセットで格納しておくとよいでしょう。

6.まとめ

いかがだったでしょうか。
本記事では、ブラウザから送られてきたテキストと画像データをServletで受け取ってバイト配列に変換する方法を記述しました。
バイト配列に変換してしまえばデータベースの登録は比較的容易なので省力しています。
需要あるか分かりませんが、私と同じ事考えていて、困る人が少しでも減ったら幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】ループ文

ループ文

ループ文とは

ループ処理の主な機能は、 指定された回数分や特定の条件下にある間、処理を実行し続けること です。

Javaにおいては、for文、while文という繰り返し文の構文が用意されていて、Javaに携わる上では頻繁に使用する機能になります。

for文

ループカウンタなどを使用して、定められた回数だけ同じ処理を繰り返したいときに使用します。
ループカウンタと、添え字による配列要素へのアクセスは非常に相性がいいため、配列の中身を単純に1つ1つ参照する場合などによく使用されます。

for文の書き方

書き方はこちらです。

for (ループカウンタ初期化; 継続条件; ループカウンタ更新) {
    繰り返し行う処理
}

よく使われる内容として、配列の中を全部表示する方法があります。
下記の場合、配列の番号配列の値をそれぞれ出力する方法です。

        String[] type = { "ほのお", "みず", "くさ", "いわ", "でんき", "どく", "エスパー", "じめん" };

        // 配列arrの中身を全て表示する
        for (int i = 0; i < type.length; i++) {
            System.out.println("type[" + i + "]  " + type[i]);
        }

実行結果
image.png

for文の使用するケースについて

このあとのwhile文と使うケースの違いについてですが、
主に繰り返す回数が明確な場合に使用します。例を上げると

  • 九×九のマス目を作成する。
  • fizz buzzでよくある「1〜100の間の数字で」といった条件がある場合

などかと思います。

while文

ほかにwhile文という構文があり、書き方がこちら。

while (継続条件) {
    処理
}

サンプル文であまり見ないパターンであるものの、-5から5までの数を出力するプログラムです。
※コード内にも書きましたが i++を記述しないと無限ループになるため注意!

// 変数の初期化
int i = -5;

// 1から10までの数字を出力
while (i <= 5) {
System.out.println(i);
i++; // この記述をしないと無限ループになる!
}

whileを使った簡単な計算プログラムがこちらになります。

package practice_project;
import java.util.Scanner;

public class Practice1 {
    public static void main(String[] args) {
        int inputNum;       // ユーザが入力する値
        int sum = 0;        // 合計値(最初は0からスタート)

        // ユーザ入力読み込みオブジェクトを生成
        Scanner scanner = new Scanner(System.in);

        // 初回の数値入力
        System.out.print("数値入力(-1で終了):");
        inputNum = scanner.nextInt();

        // ユーザから「-1」が入力されるまで、入力された数値をsum変数に加算
        while (inputNum != -1) {
            // 入力値をsumに加算
            sum += inputNum;

            // 2回目以降の数値入力
            System.out.print("数値入力(-1で終了):");
            inputNum = scanner.nextInt();
        }

        // 合計値を表示
        System.out.println("入力された数値の合計は " + sum);

    }
}

実行結果
image.png

while文の使用するケースについて

for文と使うケースの違いについてですが、
繰り返す回数が定まらない(前もって分からない)場合に使用します。

  • ユーザーのストップなどの入力値を受け取るまでは処理を繰り返す、といったような処理を行う場合
  • 電卓のように、答えが出るまで計算を続ける場合。

などかと思います。

do-while文

do-while文はwhile文と同様、括弧内の継続条件を満たしている間、処理を繰り返します。
書き方はこちら。

do {
    処理
} while (継続条件);

記述を見るとピンとくるかもですが、forやwhileは前判定であるのに対し、do-whileは後判定であるという違いがあります。

この後ろ判定のループでは、まず最初にループ内の処理を1回実行してから、継続条件の判定を行います。
つまり、forやwhileは 0回以上の繰り返し処理 、do-whileは 1回以上の繰り返し処理 で使用します。

while文で書いた内容をdo-while文で書き換えるとこんな感じになります。

package practice_project;
import java.util.Scanner;

public class Practice1 {
    public static void main(String[] args) {
        int inputNum;       // ユーザが入力する値
        int sum = 0;        // 合計値(最初は0からスタート)

        // ユーザ入力読み込みオブジェクトを生成
        Scanner scanner = new Scanner(System.in);

        // 初回の数値入力
        System.out.print("数値入力(-1で終了):");
        inputNum = scanner.nextInt();

        // ユーザから「-1」が入力されるまで、入力された数値をsum変数に加算
        do {
            // 入力値をsumに加算
            sum += inputNum;

            // 加算値を入力
            System.out.print("加算値入力(-1で終了):");
            inputNum = scanner.nextInt();

        } while (inputNum != -1);

        // 合計値を表示
        System.out.println("入力された数値の合計は " + sum + " です。");

    }
}

do-while文の使用するケースについて

do-while文を使用する場面はそんなに多くありません。
「最低一回は処理を行ってから繰り返しの判定を行いたい」
という場面に遭遇したらdo-while文の使用を検討します。

break文と continue文

繰り返し処理のfor文とwhile文では、break文continue文によって、ループの流れを変えることができます。

break文

break文を記述すると、その時点でループ処理から抜けます。
for文にbreak文を組み込んだ場合の構文はこちら。

for (int i = 0; i < 数字; i++) {
    if (何かの条件) {
        break;      // 条件に一致したらループから抜ける
    }
}

例えば先程書いたのプログラムにbreakを追記し、「配列の4番目までいったら処理を止める」という内容にします。

String[] type = { "ほのお", "みず", "くさ", "いわ", "でんき", "どく", "エスパー", "じめん" };

        // 配列arrの中身を全て表示する
        for (int i = 0; i < type.length; i++) {
            System.out.println("type[" + i + "]  " + type[i]);
            if (i > 3) { //配列の4番目になったら..
                break; //処理を終了する
            }
        }

実行結果
image.png

continue文

continue文を書くと、その時点で後の処理をスキップすることが出来ます。

while (条件式) {
    [ある処理A]
    continue;
    [ある処理B]
}

こちらは「1〜10の数字を出力して3の倍数の時は 数字の処理を行わず、代わりに "3の倍数です" と出力する」プログラムです。

// for文で1から20の数字を出力
        for (int i = 1; i <= 10; i++) {
            // 負の値の場合は、以降の処理をスキップ
            if (i % 3 == 0) {
                System.out.println("3の倍数");
                continue;
            }
            System.out.println(i);
        }

image.png

無限ループ

ループ文において、継続条件が常に真である場合、そのループは 無限ループ(永久ループ) となります。

意図的に行う無限ループ

無限ループ は、ミスではなく意図して発生する場合があり、それは
「繰り返す回数が決まっておらず、繰り返し処理を実行するうちに ある条件が成立したときにだけループを終了したい場合」
などに 無限ループ を使用します。

使用する際は、ある条件が成立したときに breakや return を実行してループやメソッド処理の ブロック を抜けるようにします

条件を満たすまで、無限に意識高い系ポエムを、○時〜○時の間の(乱数)分にSNS上に投稿し続けるプログラムも面白いかもしれません。

無限ループさせるには

for文の場合

for文の場合は、for (;;)と記述すると 無限ループ させることができます。
継続条件が省略されているため、判定が行われないので、 無限ループ となります。

// forの無限ループ
for (;;) {
    ループ中の処理
    if (ループ脱出条件) {
        break;
    }
    ループが終わったあとの処理
}

while文の場合

while文の場合は、while (true)として、継続条件に true を指定することで 無限ループ させることができます。

while (true) {
    ループ中の処理
    if (ループ脱出条件) {
        break;
    }
    ループが終わったあとの処理
}

これは、while文の条件式に true(真) が直接指定されているため、
継続条件が false(偽) になることがないので 無限ループ となります。
for文の場合は、for (;;)と記述すると 無限ループ させることができます。
継続条件が省略されているため、判定が行われないので、 無限ループ となります。

do-while文の場合

無限ループのサンプルプログラム

package practice_project;
import java.util.Scanner;

public class Practice1 {
    public static void main(String[] args) {
        int inputNum;       // ユーザが入力する値
        int sum = 0;        // 合計値(最初は0からスタート)

        // ユーザ入力読み込みオブジェクトを生成
        Scanner scanner = new Scanner(System.in);

        // 初回の数値入力
        System.out.print("数値入力(-1で終了):");
        inputNum = scanner.nextInt();

        // ユーザから「-1」が入力されるまで、入力された数値をsum変数に加算
        while (inputNum != -1) {
            // 入力値をsumに加算
            sum += inputNum;

            // 2回目以降の数値入力
            System.out.print("数値入力(-1で終了):");
            inputNum = scanner.nextInt();
        }

        // 合計値を表示
        System.out.println("入力された数値の合計は " + sum);
    }
}

実行結果
image.png

多重ループ

ループ文の中に更にループ文を記述することを多重ループと呼びます。

多重ループを用いて九九を作る

以前作った際にかなり手こずったのでここでもまとめてみます。

コードと実行内容

まずは形からお見せします。for文の多重ループを用いてコードを書いているのがわかるかと思います。

// 九九表を表示
        for (int i = 1; i <= 9; i++) {
            for (int j = 1; j <= 9; j++) {
                System.out.print(i * j + " ");
            }
            System.out.println();

実行結果
image.png

実行結果を見ると分かる通り、外側の i が1つ増加する度に、毎回内側の j が 1 から 9 まで値を変えて九九の計算をしています。
また、内側の j が 9 まで回った後、外側の i を1つ増やす直前に System.out.println();で改行を出力しています。
これにより、一つの段を表示し終わったタイミングで改行が行われることになります。

【補足】 System.out.println();とSystem.out.print();

自分は以前このような形にする際に、
System.out.println();
System.out.print();の使い分けができておらず、ループの内容が正しいのに出力結果が画像のように縦に出力されていました。
image.png

原因は、値の出力をすべて System.out.print lnと入力していたためです。

System.out.print(表示する内容);
System.out.println(表示する内容);

書き方は同じでも決定的な違いは、改行されるかされないかです。
当時違いがわからず、ループの書き方を調べまくっても解決できなかったのですが、原因がこいつだった過去があります。

  • println は改行される
  • printは 改行されない

コードと実行結果がヘラってますが、それぐらいして脳に焼き付けようと思います。

        // println は改行される
        System.out.println("println は改行される");
        System.out.println("println は改行される");

        // print は改行されずにつながっていく
        System.out.print("printは改行されずにつながっていく");
        System.out.print("printは改行されずにつながっていく");
        System.out.print("printは改行されずにつながっていく");

実行結果
image.png

おわりに

ループ分は他の言語を扱っていたときも触れていましたが、色んなパターンで書いてアウトプットしていくことでやっとスキルになるのかなと思いました。
これからも更新していきます。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

悲報 SpringBootは二度はまる。。

Spring Bootで久しぶりにREST APIを書いてみようとして「はまった」話。おそらくまたはまるので備忘

はまりポイントの経緯

Spring Bootをどれぐらい使ったことあるの?

キックオフミーティング用の簡単APIを作成するに1度使って。「お手軽」のイメージをもっていました。
今回OAuthの認証サンプル作ろうと、受け側のダミーサイトとして利用するつもりだったのですが。あっさりとはまりました。

なにをやらかしたの?

Servlet Container が期待通り起動せず。エラーも出ないので途方に暮れた。

まずはプロジェクトの作成

SpringBoot Initializerを使ってサンプルプロジェクトの作成しました。

実行してみましょう。

> gradle bootRun

と。。さて、Tomcat 起動してくれるよね。。。

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.0)

2020-11-19 19:06:10.101  INFO 27916 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_241 on DESKTOP-O85C3T1 with PID 27916 (E:\dev\workspace\springBoot\demo\build\classes\java\main started by shupe in E:\dev\workspace\springBoot\demo)
2020-11-19 19:06:10.103  INFO 27916 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-11-19 19:06:10.375  INFO 27916 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.49 seconds (JVM running for 0.75)

BUILD SUCCESSFUL in 1s
4 actionable tasks: 4 executed

あれ?プロセス終わってexit 0だ。。。

もしかしてバックグラウンドで走っているのかな?**と考え確認してみたもののそんなはずはなく。

>jps
3904 Jps
23660 GradleDaemon

途方に暮れていたのですが、答えは簡単なものでした。

なにが問題だったの?

答えを探し求めること数時間。答えは意外なところに。というか当たり前のところに。。。「以前書いた自分のコード」・・・・・ではなく、プロジェクト定義ファイル。(当時はpom.xml)

使っているStarterが異なりました。

build.gradle
変更前 implementation 'org.springframework.boot:spring-boot-starter'
変更後 implementation 'org.springframework.boot:spring-boot-starter-web'

普通に、Web用のStarterをしていないので期待通りにServerプロセスが立ち上がってこなかったわけです。
Initializerのデフォルトをそのまま信用していたのが失敗の原因でした。

さて、再実行。。。

> Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.0)

2020-11-19 19:17:14.164  INFO 1092 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_241 on DESKTOP-O85C3T1 with PID 1092 (E:\dev\workspace\springBoot\demo\build\classes\java\main started by shupe in E:\dev\workspace\springBoot\demo)
2020-11-19 19:17:14.166  INFO 1092 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-11-19 19:17:14.668  INFO 1092 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-11-19 19:17:14.673  INFO 1092 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-11-19 19:17:14.673  INFO 1092 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-19 19:17:14.709  INFO 1092 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-11-19 19:17:14.709  INFO 1092 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 519 ms
2020-11-19 19:17:14.796  INFO 1092 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-19 19:17:14.880  INFO 1092 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-19 19:17:14.886  INFO 1092 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.914 seconds (JVM running for 1.136)
<==========---> 80% EXECUTING [19s]
2020-11-19 19:17:46.236  INFO 1092 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-11-19 19:17:46.236  INFO 1092 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-11-19 19:17:46.237  INFO 1092 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
<==========---> 80% EXECUTING [19m 18s]                                                                                 > IDLE

無事起動しました。「bootRunしたけど、Tomcatが起動せずにとまってしまう」問題に直面したら思い出してください。デフォルトのツールではweb starterになってないよぉ~と

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

空の ArrayList を新規生成するラムダ式

Want

ファクトリメソッドとして空のArrayListを新規作成する Lambda 式を渡したいとき、次のようなラムダを渡すと Sonarqube や IntelliJ 等に "Replace this lambda with a method reference."や"Lambda can be replaced with method reference." というように直接実装を書くんじゃなくてメソッド参照にしろと怒られる。

() -> new ArrayList()

Do

次を渡せばOK。

ArrayList::new

Don't

空のArrayListを用意したあとでそのインスタンスに要素を追加していく場合、次はアウト。

Collections.emptyList()

Why

Collections.emptyList() で生成したリストは、基本的に操作できません。
add メソッドなどを呼び出すと UnsupportedOperationException が発生します。

次も参考にどうぞ。
https://www.baeldung.com/java-collections-emptylist-new-list

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】制御文 ( if, else, else if, switch )

制御分とは

読んで字のごとく「プログラムを制御する文」 ですがプログラムの制御は「分岐」と「繰返し」の2つに分類できます。

  • 「分岐」には、if文とswitch文の2つの構文
  • 「繰返し」には、for文とwhile文、do-while文の3つの構文が存在します。

if文

if文 は、括弧の中の条件によって処理の流れを分岐させます。
条件式の判定がtrueのときに実行する処理を記述します。それ以外の時には処理は行われない。

if (条件式) {
     条件式が true の場合に行う処理
}

else文

else文 「それ以外」の場合、つまりif文の条件式の判定がfalseのときに実行する処理を記述します。

if (条件式) {
     条件式が true の場合に行う処理
} else {
     条件式が false の場合に行う処理
}

else if文

else-if文 は、if文の条件式の判定結果が false だった場合に、別の条件式を付け加えて判定が行われます。
そして、else-if文の条件式の判定結果が true だった場合に、else-if文の { } 内の処理が実行されます。

if (条件式1) {
     条件式1が true の場合に行う処理
} else if (条件式2) {
     条件式1が false で、条件式2が true の場合に行う処理 
}

制御文を用いたサンプルプログラム(数字)

// 年齢を入れるint型の変数oldに34を代入
int old = 34;

// もし、20歳以上の場合は、"成人です。"と表示
if (old >= 20) {
    System.out.println("成人です。");

// でももし、19歳の場合は、"もうすぐ二十歳です。"と表示
} else if (old == 19) {
    System.out.println("もうすぐ二十歳です。");

// それ以外の場合は、"未成年です。"と表示
} else {
    System.out.println("未成年です。");
}

実行結果
image.png

制御文を用いたサンプルプログラム(文字列)

先程行ったように、Stringクラス(文字列を扱うクラス)の比較の場合、== ではなく equals を使用します。
下記は自分がでんきタイプのポケモンだった場合、相手のポケモンに対して攻撃を与えた場合のメッセージを出力するイメージです。(本当はもっと複雑で簡単に書けるとは思いますが、多めにみてください。。)

        // 自分のタイプと敵のタイプをいれるStringクラスを用意。
        String myType = "でんき";
        String enemyType = "敵のタイプを入力する";

        // もし enemyType が"みず"の場合、"こうかは ばつぐんだ!"と表示
        if (enemyType.equals("みず")) {
            System.out.println("こうかは ばつぐんだ!");

            // でももし、enemyType が "じめん","いわ" の場合、"こうかがないようだ"と表示
        } else if (enemyType.equals("じめん") || enemyType.equals("いわ")) {
            System.out.println("こうかがないようだ");
            // でももし、enemyType が同じタイプ( myType )の場合、"こうかはいまひとつのようだ"と表示
        } else if (enemyType.equals(myType)) {
            System.out.println("こうかはいまひとつのようだ");
            // それ以外の場合、"ダメージをあたえた"と表示
        } else {
            System.out.println("ダメージをあたえた");
        }

実行結果

String enemyType = "でんき"; //こうかはいまひとつのようだ
String enemyType = "じめん"; //こうかがないようだ
String enemyType = "みず"; //こうかは ばつぐんだ!
String enemyType = "ノーマル"; //ダメージをあたえた

複数条件の分岐

条件文を複数にしたいときもあります。
例えば、年齢が25歳未満で学生であれば、学割パックが使えます、としましょう。

// 年齢
int age = 24;

// 学生かどうか
boolean is_student = true;

複数条件を指定する場合には 論理演算子 を使用します。

記号 意味 機能
a && b 〜かつ aとbが共にtrueの場合にtrue
a || b 〜または aかbのどちらかがtrueの場合にtrue
! a 〜ではない aがtrueでないときにtrue

ここで、普通の男性の条件を満たしていて結婚していい相手かどうか判定するプログラムを組んでみます。

        int height = 168;//身長
        int annual_income = 3000000;//年収
        boolean college_graduation = false;//大卒
        boolean regular_employee = false;//正社員
        boolean cleanliness = false;//清潔感
        boolean manners = false;//常識やマナー

        if (height < 170 && annual_income < 5000000 && college_graduation && regular_employee && cleanliness && manners) {
            System.out.println("結婚してもいい");
        } else {
            System.out.println("お見送り");
        }

実行結果
image.png

else if

普通の男の条件を満たしていれば女性と結婚する資格を得ることができますが、年収が600万以上であれば、とりあえず一度お会いする権利を得られるとしましょう。

        int height = 168;// 身長
        int annual_income = 3200000;// 年収
        boolean college_graduation = false;// 大卒
        boolean regular_employee = false;// 正社員
        boolean cleanliness = false;// 清潔感
        boolean manners = false;// 常識やマナー

        // 身長170以上, 年収500万以上、大卒、正社員、清潔感がある、マナーがある場合は結婚を検討する
        if (height < 170 && annual_income < 5000000 && college_graduation && regular_employee && cleanliness
                && manners) {
            System.out.println("結婚してもいい");
            // でももし、年収が600万以上であれば、とりあえず一度お会いする権利を得られる
        } else if (annual_income > 6000000) {
            System.out.println("一回会ってみる?");
            // それ以外はお見送りする
        } else {
            System.out.println("お見送り");

        }

実行結果
image.png

switch文

switch文は、括弧内の式によって処理の流れを分岐します。
ひとつの式の結果から多方向に分岐する、多分岐処理で使用します。

主な特徴、記述方法は以下のとおりです。

switch (式) {
    case 値1:
        式が 値1 の場合に行う処理;
        break;
    case 値2:
        式が 値2 の場合に行う処理;
        break;
    default:
        式が上記case選択肢のどれにも当てはまらない場合に行う処理;
        break;
}

記述の手順は

  1. 複数のcaseという選択肢を記述し分岐させる
  2. 各々のcaseの最後にbreak文を記述することで、処理中の
  3. caseを終了してswitch文から抜ける
  4. どのcase選択肢にも当てはまらない場合は、defaultを記述することで、 if文の else と同等の機能を持たせる

switch文を使ったプログラム例

      String myType = "かくとう";
        String enemyPokemon = "ユンゲラー";
        String defenseEnemyType = "エスパー";
        int atack_dmg_point = 50;

        switch (defenseEnemyType) {
            case "いわ":
                System.out.println("こうかはばつぐんだ!");
                System.out.println("てきの" + enemyPokemon + "に" + atack_dmg_point * 2 + "ダメージ!");
                break;
            case "エスパー":
                System.out.println("こうかはいまひとつのようだ");
                System.out.println("てきの" + enemyPokemon + "に" + atack_dmg_point /2 + "ダメージ!");
                break;
            case "ゴースト":
                System.out.println("こうかがないようだ");
                break;
            default:
                System.out.println("てきの" + enemyPokemon + "に" + atack_dmg_point + "ダメージ!");
                break;
        }

実行結果
image.png
少しゲームの内側がわかってきて楽しくなってきましたw

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】拡張for文の使い方

プログラミング勉強日記

2020年11月19日
Javaの拡張for文の使い方をまとめる。

拡張for文とは

 配列やコレクションといった複数の要素を持っているものからすべての要素に含まれる値を順番に取り出して処理するために使われる。
 値を順番に取り出したい配列やコレクションと、取り出した値を格納する変数を:(コロン)で区切って記述する。繰り返される回数は配列やコレクションに含まれている値の数なので条件式と変化式は必要ない。

for文との違い

 for文は指定した条件で繰り返し処理を実行するときに使い、繰り返す処理の内容を指定できる。
 拡張for文はコレクションのすべての要素に対して繰り返し処理をするときに使用できて、for文をより簡単に書ける。

基本的な書き方
for (データ型 変数名: コレクション){
  実行する文1;
  実行する文2;
}

配列でのfor文と拡張for文

for文
public class Main {
    public static void main (String[] args){
        int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
        for (int index = 0; index < numbers.length; index ++) {
            System.out.println(numbers[index]);
        }
    }
}
拡張for文
public class Main {
    public static void main (String[] args){
        int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        for (int number : numbers) {
            System.out.println(number);
        }
    }
}
実行結果
1
2
3
4
5
6
7
8
9
10

コレクションの拡張for文

拡張for文
import java.util.ArrayList;
import java.util.List;
public class EnhancedForCollection {
    public static void main (String[] args){
        List<Integer> numbers = new ArrayList<Integer>();
        for (int number = 1; number <= 10; number++) {
            numbers.add(number);
        }
        for (int number : numbers) {
            System.out.println(number);
        }
   }
}
実行結果
1
2
3
4
5
6
7
8
9
10

参考文献

【Java入門】なるほど納得!for文と拡張for文の違い
拡張for文(for-each文)
【初心者でもすぐわかる】Javaの拡張for文の使い方まとめ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】変更に強いコード - 変数名・メソッド化

変更に強いコードを書くための考え方を学びます

目的ごとに変数を用意しよう

  • 計算ロジックに意識が向き過ぎると変数名のことを忘れる
    • 入力、処理、出力の役割ごとに変数名を使う (説明用の変数を導入)
  • 一個の変数を使いまわさない
    • ロジックを変更しにくい。長い目で見ると副作用が伴う
  • Immutableに書くことができる
    • オブジェクトを更新せずに別の目的のオブジェクトが必要な時に新しいオブジェクトをつくること(Immutableなオブジェクト)
//目的ごとにローカル変数を使う
class Main {
        public static void main(String args[]) {
        int unitPrice = 1000;
        int quantity = 3;
        float tax  = 10;
        int basePrice = unitPrice * quantity;
        int shippingCost = 0;
        if (basePrice < 3000){
            shippingCost = 500;
        }

        float basePriceWithTax = basePrice * (1+tax/100);

        //NG例1.0になってしまう
        //System.out.println((float)(1+10/100)); 

        System.out.println(basePriceWithTax);  //3300.0

       //NG例 一個の変数を使いまわさない
       //basePrice  = basePrice *  (1+tax/100);

        }
}

メソッドとして独立させよう

  • 関連性のあるデータとロジックをメソッドに抽出
  • 再利用/修正されることがあるかどうか考えよう
    • ex: ShipppingCostの計算などは再利用されることがある機能
    • 同じ機能のコードを何回もかくことがないようにしよう
      • 複数箇所に渡って書いてしまうと変更時に回収できない
  • 数値の直書きもよくない
    • ロジックの中には固定値が入らないようにしよう
/*
point1:目的ごとにローカル変数を使う
point2:同じ機能のコードをメソッド化
point3:数値を固定値に(staticでいつでも参照できてfinalで変更できないようにする)
*/

public class Main {
        //クラス変数は、インスタンス化された各オブジェクトから共通してアクセス可能
        public static final int MIN_BASEPRICE = 500;
        public static final int SHIPPING_COST = 500;
        public static final int unitPrice = 1000;
        public static final int quantity = 3;
        public static final float tax  = 10;


        public static void main(String args[]) throws Exception {

        /*非static変数(ンスタンス変数)はインスタンス固有の変数
        int MIN_BASEPRICE = 500;
        int SHIPPING_COST = 500;
        int unitPrice = 1000;
        int quantity = 3;
        float tax  = 10;
        */

        var mainInstance  = new Main();
        var basePrice = mainInstance.run();
        var shippingCost = mainInstance.getShippingCost(basePrice);
        System.out.println("shippingCost " + shippingCost );  //shippingCost 0

        /* 上記はこのコードと同じ
        int basePrice = new Main().run();
        int shippinigCost = new Main().getShippingCost(basePrice);
        */   

        /*非staticメソッドはインスタンスから呼び出さなくてはならない
        error: non-static method getShippingCost(int) cannot be referenced from a static context
        int shippinigCost = getShippingCost(basePrice); 
        */

        }

        private int run(){
            int basePrice = unitPrice * quantity;
            float basePriceWithTax = basePrice * (1+tax/100);
            System.out.println("basePrice "+ basePrice);                //basePrice 3000
            System.out.println("basePriceWithTax "+ basePriceWithTax);  //basePriceWithTax 3300.0
            return basePrice;
        }

        public int getShippingCost(int basePrice){
            int shippingCost = 0; 
            if (basePrice<MIN_BASEPRICE){
                shippingCost = SHIPPING_COST;
            }
            return shippingCost;
        }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】演算子まとめ

演算子

演算子の種類

プログラミングで使用される演算子は以下のように種類があります。

  • 算術演算子(計算式に使われる)
  • 代入演算子(計算式に使われる)
  • 比較演算子(条件式に使われる)
  • 論理演算子(論理式に使われる)

使う演算子によって以上のように様々な「式」をつくることが出来ます。

算術演算子

算術演算子は足し算引き算などの計算を作る演算子です。

記号 機能
+ 加算
- 減算
* 乗算
/ 除算
% 余剰

算術演算子を用いた計算

算術演算子を用いて作った計算式がこちらです。

 System.out.println(3 + 2);

実行結果
image.png

文字列の連結

文字列を組み合わせることも可能です。
先ほど作成したハローカツラプログラムを出力してみます。

        String a = "うおおーす!";
        String b = a + "やけどなおしの よういは いいか!"; // 変数bには、連結結果の文字列("ABCDEF")が保持される。
        System.out.println(a);
        System.out.println(b);

image.png
成功です。

代入演算子

代入演算子は、変数に代入する値に何かしらの計算を行い、その結果を再び同じ変数に代入する際に便利です。

記号 機能 内容
a += b a と bを足してaに代入 a = a + b
a -= b aからbを引いてaに代入 a = a- b
a *= b AとBを掛け合わせてAに代入 a = a* b
a /= b AをBで割りAに代入 a = a / b
a %= b AをBで割りその余りをAに代入 a = a % b
a ++ (インクリメント) Aから値を1増やす a + 1
a -- (デクリメント) Aから値を1減らす a - 1

代入演算子を用いた式を作ります。

        int a = 10;
        int b = 10;
        int c = 10;
        int d = 10;
        int e = 10;

        System.out.println(a += 10);// 10を足す
        System.out.println(b -= 10);// 10を引く
        System.out.println(c *= 10);// 10をかける
        System.out.println(d /= 10);// 10で割る
        System.out.println(e %= 10);// 10で割った余り

出力結果
image.png

比較演算子

比較演算子は、左辺と右辺を比較し、数字の大小などを評価します。そして結果として真(true)か偽(false)の値を取ります。

記号 機能 別名
a == b aとbが等しいときにtrue eq
a != b aとbが等しくない時にtrue ne, neq
a > b aがbより大きい時にtrue gt
a < b aがbより小さい時にtrue lt
a >= b aがbより大きいか等しい時にtrue gte, ge
a <= b aがbより小さいか等しい時にtrue lte, le

文字列の比較

文字列の比較は特別です。 文字列は、比較演算子で比較することはできません。
文字列を比較する際は、Stringクラスにある「 equals 」という名前のメソッド(関数)を利用します。

i  == 1;

// iが比較対象と合っていたらtrue
System.out.println(i == 1);
System.out.println(i == 2);

// iが比較対象と違っていたらtrue
System.out.println(i != 1);
System.out.println(i != 2);

実行結果
image.png

論理演算子

論理演算子は複数の比較演算子の結果を組み合わせる場合に使用されます。
「~か〜だったら、、」や、「〜」
そのため、左辺や右辺には比較演算式が入ります。
そしてその左辺と右辺を比べた結果として、真(true)か偽(false)の値を取ります。

論理演算子

記号 意味 機能
a && b 〜かつ aとbが共にtrueの場合にtrue
a || b 〜または aかbのどちらかがtrueの場合にtrue
! a 〜ではない aがtrueでないときにtrue

サンプルコード

        // 「a >= 5」と「a <= 20」のどちらも正しければ 真(true)、それ以外の場合は 偽(false) となる。
        int a = 10;
        System.out.println(a >= 5 && a <= 20);

        // 「b > 20」と「c < 30」のどちらかが正しければ 真(true)、どちらも正しくない場合は 偽(false) となる。
        int b = 10;
        int c = 20;
        System.out.println(b > 20 || c < 30);

        // 「d < 20」の結果が「!」によって否定される。
        int d = 10;
        System.out.println(!(d < 20));

実行結果
image.png

式の評価の優先順位

式の評価は、演算子の優先順位に従いながら、左から行われます。
複数の演算子が含まれる式を記述する場合、どの順番で計算が行われるのか注意。

a > 10 || b < 20

こういった式があったの順序はこのようになります。

  1. > と <
  2. ||

おわりに

演算子はたくさん種類があるようなのですが主な使い方はどの言語もかわらないのでしっかり把握しておく。

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

eclipseでwhere we looked for ~~~とエラーが出る

eclipseでのエラーは沢山あるが今回はタイトルに書かれているエラーについて解決策を紹介させていただく

確認すべきことがいくつかあるが一つ目がeclipseのヘルプ→Eclipse IDEについて→インストール詳細→構成
vm-
hogehoge(25行目あたり)
に書かれているのが

-vm
C:\・・・JREのパス・・・\bin\server\jvm.dll

だと、タイトル通りのエラーになることがわかっている。 要するに、JDKを探しているのに、上記の例ではvmがJREに設定されているため、JDKを見つけられないというエラーが起きるわけだ。


次に、windowsのコマンドプロンプトにjava -versionと打ちこむ。エラーメッセージが表示されるようだと、JDKがインストールされていないことが確認できる。
その場合はこちらの優秀なサイトを一読した後、こちらに載せられているリンクからOracleの公式ダウンロードページへ飛んでインストールしてほしい。インストール方法については割愛させていただく。


インストールできたならば次に、フォルダからC:\・・・pleiadesのパス・・・\pleiades\eclipse\eclipse.iniを開き、

openFile
--;
-vim←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←これを挿入してほしい
C:\・・・JDKのパス・・・\bin\server\jvm.dll←←←これを挿入してほしい
-vmargs
-;

そしてeclipseを再起動してみる。エラーが出ないようであればそれでよい。(一応、構成のvm-の行を確認してみてほしい。JDKのパスに変わっているはずだ)


エラーが出るようであれば、それはシステム環境変数の設定が必要だ。windowsのverやOSによって設定の仕方があるのでお勧めのサイトをのせておく。
このサイトを読んでJDKに関する変数を設定すれば冒頭で悩まされた原因が解消されると思う。


eclipseに関するエラーはほかにも沢山あると思うが、今回はこちらのエラーについてわかりやすく説明した。
javaはおもしろい!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む