20200728のJavaに関する記事は14件です。

【java】java初学者がString型と向き合ったメモ

はじめに

javaシルバーの資格取得に向けて学習を進めていく中で、String型の扱いについて理解を深めるために学習した内容のメモです。
解釈に誤りがあれば指摘いただければ幸いです。

String型は参照型であって基本データ型ではないようだ

変数を定義する際に、intとかdoubleとかbooleanとかcharとかデータ型を指定して、変数を定義してました。
同じように文字列を扱う変数を定義する時も、String型を指定して変数を定義する事はわりと当たり前のようにやってました。

こんな感じ↓

変数の定義.java
int i = 100
 // -> 整数を扱えるint型に整数100を代入
char c = 'あ'
 /* -> 文字を扱えるchar型に`あ`を代入
 char型の場合はダブルクォーテーションではなくてシングルクォーテーション*/
double d = 10.0
 // -> 小数点を扱える「浮動小数点数型」に10.0を代入
String s = "文字列です。"
 /* -> 文字列を扱えるString型に"文字列です。"を代入
 Stringの場合はシングルクォーテーションではなくてダブルクォーテーション*/

ただ、ずっと疑問だった事が。
それは「なんでString型だけ大文字で始まるん?」ってこと

基本データ型と参照型があるんや

「なんでString型だけ大文字で始まるん?」
この答えは、ずばりString型は基本データ型ではないからという事にありました。
言い換えると、String型の正体はクラスだったってことです。
確かに、クラスは最初の文字が大文字だし、なるほど!と納得です。

Stringクラスの中身を覗いてみる

Stringはクラス。であれば中身もみれる。
って事で、Stringクラスの中身をみると、インスタンスフィールドの一つにこんな定義がされているんです。

String.java
private final char value[];

これってつまり、基本データ型char型の配列ですね!
なので、Stringの正体は基本データ型char型の配列一まとまりだったみたいです。
さっきの変数の定義.javaで定義したString s = "文字列です。"は言い換えると

private final char value[] = {'文','字','列','で','す','。'}

ってことだったんですね。

String型だけでなく、StringBuilder型もあるらしいぞ

そんなこんなで学習を進めていくと、String以外にもStringBuilderっていうものもあるんですね。

何が違うんだ?

現時点で理解しているのはこんな感じです。

String型 StringBuilder型
変数の宣言 newなしでもありでもいける newを使わないとダメ宣言できない
変数への再代入 参照先が変わる 中身を書き換える

表にする程の内容じゃなかったな・・・

データの扱い方もそうですが、そもそもクラスが異なることもあって、用意されているメソッドも違います。

変数の宣言

StringとStringBuilderとでは変数定義の方法が決まってます。

変数定義.java
String a = "あいうえお"; // -> できる
String b = new String("かきくけこ");  // -> できる
StringBuilder c = "さしすせそ"; // -> できない
StringBuilder d = "たちつてと"; // -> できる

String型の宣言方法による違いは?

文字列の管理の仕方が異なるようです。
newを使わない場合は、Stringクラスが管理している領域に対して、文字列を用意してくれます。
newを使うとStringクラスが管理している領域とは別に個別に文字列が作成されます。

なので、同じString型であっても、newで定義すると、一匹狼のString変数ができあがるようです。
どう違ってくるのか、おそらくStringクラスが持っているメソッドのintern()を使うとイメージつくかもしれません。(後述)

変数への再代入について

「変数への再代入ができない?いやいや。できるじゃん!」そうお思いの方。
ですね。確かにできている気がします

変数の再代入.java
String a = "あいうえお";
a = "かきくけこ";
// -> aの中身は"かきくけこ"になる

けど、これって書き換わっているのではなくて、aの参照先が"あいうえお"から"かきくけこ"に切り替わっているみたいです。

なので、厳密には書き換えているのではなく、切り替えている。みたいです。

String型の比較について

実装をする上で、String型の文字列比較が必要となる事があると思います。
そんな時、比較演算子を使うのが主流ですが、String型の場合は比較演算子ではなく、equals()メソッドを使います。

ナゼなのか

結論から話しますと、比較演算子の場合は参照元を比較してしまうようです
もっとわかりやすく言うと、s1とs2が同じ"あいうえお"だけど、 s1 == s2 とした時、場合によってはfalseになってしまうのです。

比較演算子での文字列比較.java
package sample20200728;

public class Sample02 {
    public static void main(String[] args){
        String s1 = new String("あ");
        String s2 = "あ";
        String s3 = "あ";

        //s1とs2を比較演算子で比較
        if(s1 == s2){
            System.out.println("s1とs2の文字列は一緒だよ");
        }else{
            System.out.println("s1とs2の文字列は違うよ");
        }

        //s1とs3を比較演算子で比較
        if(s1 == s3){
            System.out.println("s1とs3の文字列は一緒だよ");
        }else{
            System.out.println("s1とs3の文字列は違うよ");
        }

        //s2とs3を比較演算子で比較
        if(s2 == s3){
            System.out.println("s2とs3の文字列は一緒だよ");
        }else{
            System.out.println("s2とs3の文字列は違うよ");
        }
    }
}

出力結果
スクリーンショット 2020-07-28 23.21.44.png
文字列が一緒なのに、一部違うよ。と否定されちゃってます。これは比較演算子だと、文字列を比較せずに、参照元を比較してしまうようです。

参照元が一緒かどうか、を判断する時はこれで良いと思いますが、実運用を考えると、文字列が一致しているかどうか。を確認する事が多いのかな?と思います。
そうなると、文字列一緒なのに参照元が違うっていう理由でfalseを返されちゃうとバグの原因になっちゃいますよね?

細かい事は気にしない。文字列さえ一致してればそれでいい!という男前メソッドequals()さんが活躍します。

equals()での比較.java
public class Sample02 {
    public static void main(String[] args){
        String s1 = new String("あ");
        String s2 = "あ";
        String s3 = "あ";

        //s1とs2をequalsメソッドで比較
        if(s1.equals(s2)){
            System.out.println("s1とs2の文字列は一緒だよ");
        }else{
            System.out.println("s1とs2の文字列は違うよ");            
        }

        //s1とs3をequalsメソッドで比較
        if(s1.equals(s3)){
            System.out.println("s1とs3の文字列は一緒だよ");
        }else{
            System.out.println("s1とs3の文字列は違うよ");            
        }

        //s2とs3をequalsメソッドで比較
        if(s2.equals(s3)){
            System.out.println("s2とs3の文字列は一緒だよ");
        }else{
            System.out.println("s2とs3の文字列は違うよ");            
        }
    }
}

実行結果
スクリーンショット 2020-07-28 23.24.06.png

うん。文字列をしっかり比較してくれてますね!

おまけ internメソッド

Stringクラスにはinternメソッドというものが用意されています。
メソッドの説明には「文字列プールの中にある一意の文字列を返す」とありました。
日本語でおk。

でもきっとjavaの試験に出てくるだろうと思い向き合いました。

internメソッドを理解するためにソースコード書いてみた

「文字列プールの中にある一位の文字列を返す」がぜんっぜんピンとこなかったので、実際にソースを書いて挙動を確認してみました。

理解できたことは、こんな感じ

1.intern()メソッドは、対象の文字列がString型の持つ領域内に存在しているかどうかを調べてくれる。
 同一の文字列がある場合 -> それを参照するようにする。 使い回すようなイメージ
 同一の文字列がない場合 -> String型の持つ領域内にその文字列を用意する。
2.String型の変数を定義する時にnewを使って値を定義すると、String型の持つ領域の管轄外となる。
 -> intenr()を使っても参照元を同一にすることができない

intern().java
public class Sample01 {
    public static void main(String[] args){

        String s1 = new String("テスト"); //String型クラスの持つ領域"外"でs1変数"テスト"を作成
        String s2 = s1.intern();  /* s1.intern()の返り値の"テスト"という文字列をs2に代入する。
        ただ、s1はnewで作成しているため、String型クラスの領域外で作った文字列のため、String型クラス内に"テスト"という文字列は存在していない。
        この場合は、intern()を使ったタイミングで、String型クラスの領域内に"テスト"という文字列を用意する*/
        String s3 = "むむむ"; //String型クラスの持つ領域"内"でs3変数"むむむ"を作成
        String s4 = s3.intern(); /*s3.intern()の返り値の"むむむ"という文字列をs3に代入する。
        s1と違いs3はString型クラスの領域内に"むむむ"を用意しているので、既に作っている"むむむ"を参照する(つまり文字列だけじゃなくて参照する場所も同じ)*/

        System.out.println("s1はnewを使用した変数" + s1);
        System.out.println("s2はs1.intern()の返り値を代入した変数" + s2);
        System.out.println("s3は\"むむむ\"を代入した変数" + s3);
        System.out.println("s4はs3.intenr()の返り値を代入した変数" + s4);   

        //newで作った"テスト"とs1.intern()で作った"テスト"を比較
        if(s1 == s2){
            System.out.println("true: s1とs2の参照先は同じです");
        }else{
            // s1とs2の参照先が違うので、falseになる
            System.out.println("false: s1とs2の参照先は異なります");
        }

        //String型が管理している領域を使って用意したs3"むむむ"と、s3.intern()で拾ってきた"むむむ"を代入したs4を比較
        if(s3 == s4){
            //s3,s4ともにString型の領域から生成されている、s4についてはs3.intern()でs3の文字列を参照して代入している
            System.out.println("true: s3とs4の参照先は同じです");
        }else{
            System.out.println("false: s3とs4の参照先は異なります");
        }

        /*s2は"テスト"という文字列をString型クラスが持つ領域内に作っている
        そのため、s5 = s2.intern()とした時、s5にはs2と同じものを参照することになる*/
        String s5 = s2.intern();

        //s2とs5の参照元が同じかどうかを確認する
        if(s2 == s5){
            //s2.intern()を使ってs5に代入しているので、参照元は同一のものとなる。
            System.out.println("true: s2とs5の参照先は同じです");
        }else{
            System.out.println("false: s2とs5の参照先は異なります");
        }
        System.out.println("============");
    }
}

実行結果
スクリーンショット 2020-07-28 22.55.05.png

どう足掻いてもnewで定義したs1は仲間外れになります。

おわり

以上が、String型と向き合った内容でした。
まだまだ理解がおいついていない事も多いので、きっと誤った解釈もあると思います。
その場合はお手数をおかけしますが、ご指摘いただけますと幸いです。

それでは、ありがとうございましたm(_ _)m

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

【java】java初学者の僕は感じた。「なんでString型だけ最初の文字が大文字なん?」String型と向き合ってみた。

はじめに

javaシルバーの資格取得に向けて学習を進めていく中で、String型の扱いについて理解を深めるために学習した内容のメモです。
解釈に誤りがあれば指摘いただければ幸いです。

String型は参照型であって基本データ型ではないようだ

変数を定義する際に、intとかdoubleとかbooleanとかcharとかデータ型を指定して、変数を定義してました。
同じように文字列を扱う変数を定義する時も、String型を指定して変数を定義する事はわりと当たり前のようにやってました。

こんな感じ↓

変数の定義.java
int i = 100
 // -> 整数を扱えるint型に整数100を代入
char c = 'あ'
 /* -> 文字を扱えるchar型に`あ`を代入
 char型の場合はダブルクォーテーションではなくてシングルクォーテーション*/
double d = 10.0
 // -> 小数点を扱える「浮動小数点数型」に10.0を代入
String s = "文字列です。"
 /* -> 文字列を扱えるString型に"文字列です。"を代入
 Stringの場合はシングルクォーテーションではなくてダブルクォーテーション*/

ただ、ずっと疑問だった事が。
それは「なんでString型だけ大文字で始まるん?」ってこと

基本データ型と参照型があるんや

「なんでString型だけ大文字で始まるん?」
この答えは、ずばりString型は基本データ型ではないからという事にありました。
言い換えると、String型の正体はクラスだったってことです。
確かに、クラスは最初の文字が大文字だし、なるほど!と納得です。

Stringクラスの中身を覗いてみる

Stringはクラス。であれば中身もみれる。
って事で、Stringクラスの中身をみると、インスタンスフィールドの一つにこんな定義がされているんです。

String.java
private final char value[];

これってつまり、基本データ型char型の配列ですね!
なので、Stringの正体は基本データ型char型の配列一まとまりだったみたいです。
さっきの変数の定義.javaで定義したString s = "文字列です。"は言い換えると

private final char value[] = {'文','字','列','で','す','。'}

ってことだったんですね。

String型だけでなく、StringBuilder型もあるらしいぞ

そんなこんなで学習を進めていくと、String以外にもStringBuilderっていうものもあるんですね。

何が違うんだ?

現時点で理解しているのはこんな感じです。

String型 StringBuilder型
変数の宣言 newなしでもありでもいける newを使わないとダメ宣言できない
変数への再代入 参照先が変わる 中身を書き換える

表にする程の内容じゃなかったな・・・

データの扱い方もそうですが、そもそもクラスが異なることもあって、用意されているメソッドも違います。

変数の宣言

StringとStringBuilderとでは変数定義の方法が決まってます。

変数定義.java
String a = "あいうえお"; // -> できる
String b = new String("かきくけこ");  // -> できる
StringBuilder c = "さしすせそ"; // -> できない
StringBuilder d = "たちつてと"; // -> できる

String型の宣言方法による違いは?

文字列の管理の仕方が異なるようです。
newを使わない場合は、Stringクラスが管理している領域に対して、文字列を用意してくれます。
newを使うとStringクラスが管理している領域とは別に個別に文字列が作成されます。

なので、同じString型であっても、newで定義すると、一匹狼のString変数ができあがるようです。
どう違ってくるのか、おそらくStringクラスが持っているメソッドのintern()を使うとイメージつくかもしれません。(後述)

変数への再代入について

「変数への再代入ができない?いやいや。できるじゃん!」そうお思いの方。
ですね。確かにできている気がします

変数の再代入.java
String a = "あいうえお";
a = "かきくけこ";
// -> aの中身は"かきくけこ"になる

けど、これって書き換わっているのではなくて、aの参照先が"あいうえお"から"かきくけこ"に切り替わっているみたいです。

なので、厳密には書き換えているのではなく、切り替えている。みたいです。

String型の比較について

実装をする上で、String型の文字列比較が必要となる事があると思います。
そんな時、比較演算子を使うのが主流ですが、String型の場合は比較演算子ではなく、equals()メソッドを使います。

ナゼなのか

結論から話しますと、比較演算子の場合は参照元を比較してしまうようです
もっとわかりやすく言うと、s1とs2が同じ"あいうえお"だけど、 s1 == s2 とした時、場合によってはfalseになってしまうのです。

比較演算子での文字列比較.java
package sample20200728;

public class Sample02 {
    public static void main(String[] args){
        String s1 = new String("あ");
        String s2 = "あ";
        String s3 = "あ";

        //s1とs2を比較演算子で比較
        if(s1 == s2){
            System.out.println("s1とs2の文字列は一緒だよ");
        }else{
            System.out.println("s1とs2の文字列は違うよ");
        }

        //s1とs3を比較演算子で比較
        if(s1 == s3){
            System.out.println("s1とs3の文字列は一緒だよ");
        }else{
            System.out.println("s1とs3の文字列は違うよ");
        }

        //s2とs3を比較演算子で比較
        if(s2 == s3){
            System.out.println("s2とs3の文字列は一緒だよ");
        }else{
            System.out.println("s2とs3の文字列は違うよ");
        }
    }
}

出力結果
スクリーンショット 2020-07-28 23.21.44.png
文字列が一緒なのに、一部違うよ。と否定されちゃってます。これは比較演算子だと、文字列を比較せずに、参照元を比較してしまうようです。

参照元が一緒かどうか、を判断する時はこれで良いと思いますが、実運用を考えると、文字列が一致しているかどうか。を確認する事が多いのかな?と思います。
そうなると、文字列一緒なのに参照元が違うっていう理由でfalseを返されちゃうとバグの原因になっちゃいますよね?

細かい事は気にしない。文字列さえ一致してればそれでいい!という男前メソッドequals()さんが活躍します。

equals()での比較.java
public class Sample02 {
    public static void main(String[] args){
        String s1 = new String("あ");
        String s2 = "あ";
        String s3 = "あ";

        //s1とs2をequalsメソッドで比較
        if(s1.equals(s2)){
            System.out.println("s1とs2の文字列は一緒だよ");
        }else{
            System.out.println("s1とs2の文字列は違うよ");            
        }

        //s1とs3をequalsメソッドで比較
        if(s1.equals(s3)){
            System.out.println("s1とs3の文字列は一緒だよ");
        }else{
            System.out.println("s1とs3の文字列は違うよ");            
        }

        //s2とs3をequalsメソッドで比較
        if(s2.equals(s3)){
            System.out.println("s2とs3の文字列は一緒だよ");
        }else{
            System.out.println("s2とs3の文字列は違うよ");            
        }
    }
}

実行結果
スクリーンショット 2020-07-28 23.24.06.png

うん。文字列をしっかり比較してくれてますね!

おまけ internメソッド

Stringクラスにはinternメソッドというものが用意されています。
メソッドの説明には「文字列プールの中にある一意の文字列を返す」とありました。
日本語でおk。

でもきっとjavaの試験に出てくるだろうと思い向き合いました。

internメソッドを理解するためにソースコード書いてみた

「文字列プールの中にある一位の文字列を返す」がぜんっぜんピンとこなかったので、実際にソースを書いて挙動を確認してみました。

理解できたことは、こんな感じ

1.intern()メソッドは、対象の文字列がString型の持つ領域内に存在しているかどうかを調べてくれる。
 同一の文字列がある場合 -> それを参照するようにする。 使い回すようなイメージ
 同一の文字列がない場合 -> String型の持つ領域内にその文字列を用意する。
2.String型の変数を定義する時にnewを使って値を定義すると、String型の持つ領域の管轄外となる。
 -> intenr()を使っても参照元を同一にすることができない

intern().java
public class Sample01 {
    public static void main(String[] args){

        String s1 = new String("テスト"); //String型クラスの持つ領域"外"でs1変数"テスト"を作成
        String s2 = s1.intern();  /* s1.intern()の返り値の"テスト"という文字列をs2に代入する。
        ただ、s1はnewで作成しているため、String型クラスの領域外で作った文字列のため、String型クラス内に"テスト"という文字列は存在していない。
        この場合は、intern()を使ったタイミングで、String型クラスの領域内に"テスト"という文字列を用意する*/
        String s3 = "むむむ"; //String型クラスの持つ領域"内"でs3変数"むむむ"を作成
        String s4 = s3.intern(); /*s3.intern()の返り値の"むむむ"という文字列をs3に代入する。
        s1と違いs3はString型クラスの領域内に"むむむ"を用意しているので、既に作っている"むむむ"を参照する(つまり文字列だけじゃなくて参照する場所も同じ)*/

        System.out.println("s1はnewを使用した変数" + s1);
        System.out.println("s2はs1.intern()の返り値を代入した変数" + s2);
        System.out.println("s3は\"むむむ\"を代入した変数" + s3);
        System.out.println("s4はs3.intenr()の返り値を代入した変数" + s4);   

        //newで作った"テスト"とs1.intern()で作った"テスト"を比較
        if(s1 == s2){
            System.out.println("true: s1とs2の参照先は同じです");
        }else{
            // s1とs2の参照先が違うので、falseになる
            System.out.println("false: s1とs2の参照先は異なります");
        }

        //String型が管理している領域を使って用意したs3"むむむ"と、s3.intern()で拾ってきた"むむむ"を代入したs4を比較
        if(s3 == s4){
            //s3,s4ともにString型の領域から生成されている、s4についてはs3.intern()でs3の文字列を参照して代入している
            System.out.println("true: s3とs4の参照先は同じです");
        }else{
            System.out.println("false: s3とs4の参照先は異なります");
        }

        /*s2は"テスト"という文字列をString型クラスが持つ領域内に作っている
        そのため、s5 = s2.intern()とした時、s5にはs2と同じものを参照することになる*/
        String s5 = s2.intern();

        //s2とs5の参照元が同じかどうかを確認する
        if(s2 == s5){
            //s2.intern()を使ってs5に代入しているので、参照元は同一のものとなる。
            System.out.println("true: s2とs5の参照先は同じです");
        }else{
            System.out.println("false: s2とs5の参照先は異なります");
        }
        System.out.println("============");
    }
}

実行結果
スクリーンショット 2020-07-28 22.55.05.png

どう足掻いてもnewで定義したs1は仲間外れになります。

おわり

以上が、String型と向き合った内容でした。
まだまだ理解がおいついていない事も多いので、きっと誤った解釈もあると思います。
その場合はお手数をおかけしますが、ご指摘いただけますと幸いです。

それでは、ありがとうございましたm(_ _)m

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

h:selectOneRadioのラジをボタンでいろいろやってみる

  • 環境
    • CentOS Linux release 7.8.2003 (Core)
    • Payara Server 5.194
    • Eclipse IDE for Enterprise Java Developers.Version: 2020-03 (4.15.0)
    • JSF 2.3.9

選択肢を設定してみる

  • 選択肢を格納する便利クラスとしてSelectItemがある
選択肢の数 タグ SelectItem
1つ f:selectItem 使わない
1つ f:selectItem 使う
複数 f:selectItems 使う

スクリーンショット 2020-07-28 22.18.03.png

さんぷる
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core">
<head>
  <title>selectOneRadioをいろいろ使ってみる</title>
</head>
<body>
  <h4>選択肢を設定する</h4>
  <h:selectOneRadio value="0">
    <f:selectItem itemValue="0" itemLabel="直書きした選択肢" />
    <f:selectItem value="#{sampleBean.radioSelectItem}" />
    <f:selectItems value="#{sampleBean.radioItems}" itemValue="0" />
  </h:selectOneRadio>
</body>
</html>
SampleBean.java
package brans;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.faces.model.SelectItem;
import javax.inject.Named;

import lombok.Data;

@Named
@RequestScoped
@Data
public class SampleBean {
    /** 単体用の選択肢. */
    private SelectItem radioSelectItem = new SelectItem(1, "selectItemとSelectItemで作った選択肢");
    /** 複数用の選択肢  */
    private List<SelectItem> radioItems = new ArrayList<SelectItem>();

    /** Beanの初期化処理. */
    @PostConstruct
    public void init() {
        setRadioItems();
    }

    /** 複数用の選択肢を設定する. */
    private void setRadioItems() {
        radioItems.add(new SelectItem(2, "selectItemsとSelectItemで作った選択肢-1つ目"));
        radioItems.add(new SelectItem(3, "selectItemsとSelectItemで作った選択肢-2つ目"));
    }
}

配置してみる

  • 選択肢の並びはlayout属性を使う。無駄にCSSで頑張って失敗した。
layout 並び
lineDirection 横並び(デフォルト)
pageDirection 縦並び
  • ラジオボタンと他の要素(チェックボックスとか)を一緒に横並びにしたい場合
    1. ラジオボタンと他の要素をh:panelGroupで囲う
    2. 囲ったものにCSSでFlexbox(display: flex;)を設定する
    3. Flexbox内の子要素の配置方向がデフォルトで左からの横並びになる(flex-direction: row;)
    4. チェックボックスのlayoutは指定しない(デフォルトで横並びになる) スクリーンショット 2020-07-28 22.43.16.png
さんぷる
<!-- 最初のと同じなので省略 -->
  <h4>縦並び配置してみる</h4>
  <h:selectOneRadio value="0" layout="pageDirection">
<!-- 最初のと同じなので省略 -->
  <h4>チェックボックスと合わせて1行で表示してみる</h4>
  <h:panelGroup style="display: flex;">
    <h:selectBooleanCheckbox id="check" />
    <h:outputLabel for="check" value="チェックボックス"/>
    (
    <h:selectOneRadio value="0">
      <f:selectItem itemValue="0" itemLabel="選択肢1" />
      <f:selectItem itemValue="1" itemLabel="選択肢2" />
    </h:selectOneRadio>
    )
  </h:panelGroup>
</body>
</html>

参考

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

Burp Suiteの拡張機能を作る際のチートシート

(ほぼ)日課のQiita記事の投稿です。
毎日書くの結構難しいから、今後平日は興味のある情報のURLまとめるとかの方がいいかも・・・

とりあえず今回は、Burp Suiteの拡張機能を作成し始めた頃に調べた情報をまとめました。
Burpのインストールや使用方法については割愛します。

今回は以下を参考にさせていただきました。

Burpの拡張機能の作り方

  1. Burpからインターフェイス用のソースを取得し、作成する拡張機能に組み込む。 (“Extender” -> “APIs” -> “Save interface files”)
  2. burp.BurpExtender という名のクラスを、以下のように作る。

    • パッケージが burp に属している
    • クラス名が BurpExtender
    • インターフェース IBurpExtender を実装
  3. 実装した IBurpExtenderインターフェイスの registerExtenderCallbacks()メソッドの中に実際に実行したいコードを書いていく

BurpExtender.java
package burp;

public class BurpExtender implements IBurpExtender {

    public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
        // ここに実際に実行されるコードを書いていく
    }
}

作成できる機能の一部

  • I(Proxy|Http|Scanner)Listener API
    ProxyなどのHTTP通信やScannerで検出した問題を取得

  • IContextMenu~, IMenuItemHandler API
    右クリックメニューを拡張

  • IMessageEditor~ API
    HTTPの編集画面をカスタマイズ

  • IIntruderPayload~ API
    Intruderで使うペイロード生成

他の機能など、詳しくは以下を参考にしてください。

拡張機能の読み込み

  • Extender > Extensions > Add
  • Select file... から、追加したい拡張機能のjarファイルを選択する
  • Errorタブに何も表示されていなければ、エラーなく読み込み成功
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Burp Suiteの拡張機能を作り方メモ

(ほぼ)日課のQiita記事の投稿です。
毎日書くの結構難しいから、今後平日は興味のある情報のURLまとめるとかの方がいいかも・・・

とりあえず今回は、Burp Suiteの拡張機能を作成し始めた頃に調べた情報をまとめました。
Burpのインストールや使用方法については割愛します。

今回は以下を参考にさせていただきました。

Burpの拡張機能の作り方

  1. Burpからインターフェイス用のソースを取得し、作成する拡張機能に組み込む。 (“Extender” -> “APIs” -> “Save interface files”)
  2. burp.BurpExtender という名のクラスを、以下のように作る。

    • パッケージが burp に属している
    • クラス名が BurpExtender
    • インターフェース IBurpExtender を実装
  3. 実装した IBurpExtenderインターフェイスの registerExtenderCallbacks()メソッドの中に実際に実行したいコードを書いていく

BurpExtender.java
package burp;

public class BurpExtender implements IBurpExtender {

    public void registerExtenderCallbacks(IBurpExtenderCallbacks iBurpExtenderCallbacks) {
        // ここに実際に実行されるコードを書いていく
    }
}

作成できる機能の一部

  • I(Proxy|Http|Scanner)Listener API
    ProxyなどのHTTP通信やScannerで検出した問題を取得

  • IContextMenu~, IMenuItemHandler API
    右クリックメニューを拡張

  • IMessageEditor~ API
    HTTPの編集画面をカスタマイズ

  • IIntruderPayload~ API
    Intruderで使うペイロード生成

他の機能など、詳しくは以下を参考にしてください。

拡張機能の読み込み

  • Extender > Extensions > Add
  • Select file... から、追加したい拡張機能のjarファイルを選択する
  • Errorタブに何も表示されていなければ、エラーなく読み込み成功
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot + Springfox springfox-boot-starter 3.0.0つかう

https://github.com/springfox/springfox をつかう。

build.gradle
plugins {
  id 'org.springframework.boot' version '2.3.2.RELEASE'
  id 'io.spring.dependency-management' version '1.0.9.RELEASE'
  id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
  mavenCentral()
}
dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  developmentOnly 'org.springframework.boot:spring-boot-devtools'
  testImplementation('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
  }
  // https://mvnrepository.com/artifact/io.springfox/springfox-boot-starter
  implementation 'io.springfox:springfox-boot-starter:3.0.0'
}
test {
  useJUnitPlatform()
}

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class MainApp {
    @GetMapping("/hoge")
    public String hoge() {
        return "hoge";
    }

    public static void main(String[] args) {
        SpringApplication.run(MainApp.class, args);
    }
}

これで起動して http://localhost:8080/swagger-ui/index.html にアクセスすると以下の画面が表示される。2.0以前とは異なりauto-configがあるのでswagger用のjava-configを作る必要がない。

swagger333.jpg

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

20代後半で5年間培ったCOBOLから、Web系言語にスキルチェンジした話

こんにちは!Qiita初投稿の(@k_eng_m)と申します。
以下、簡単な自己紹介となります。

  • 東京都生まれの28歳
  • 文系出身でエンジニア歴約6年
    • SIer企業でCOBOL 5年(要件定義から全工程を経験)
    • SES企業でJava 1年

キャリアのほとんどはCOBOLを使用した、いわゆるホスト系、企業の基幹システムの開発・保守に携わってきました。
現在はSES企業にてJavaを中心に、JavaScript(vue.js)、AWSなんかを触らせていただいています。

スキルチェンジを経て約1年が経過しましたので、
今日はこのCOBOLの経験を経て、未経験でWeb系言語に転向した話を、記事として残しておくこととします。

全くの未経験というよりは、エンジニア経験はあるものの
『VBやCOBOL等のレガシー言語をやってたけど、スキルチェンジは可能なのか』
『これまでの経験は、スキルチェンジ後に役に立つのか』
『○○歳からじゃもう遅いのではないか』
といったことを考えていらっしゃる方、
またこれから先、スキルチェンジを伴う転職を考えている方が、自身の持論・経験談が少しでも何かの足しになれば、とても幸いです。

経緯・スキルチェンジの理由

私は新卒で某SIer企業に就職しました。
金融系案件の開発・保守が主な企業で、社会的影響力が高い分
入社当初は「バリバリ働いて成果を出してやるぞ!」とメラメラしておりました。

しかし、2〜3年経過したところで以下のような不満が生まれました。

  • コーディングは委託さん任せでほぼ皆無。顧客との案件調整・管理業務が中心となっていること。
  • エクセルで作成されたドキュメント管理(設計書、エビデンス)の作業時間がとても長いこと。
  • バッチ処理中心の開発は、何かを作っている感覚に乏しいこと。

きっとSIerあるあるかと思います。笑
コーディングの奇麗さより、エクセル資料の見栄えや、文章の読みやすさを重視されるぐらいです。

学生の頃、思い描いていたエンジニアのイメージとは、程遠い現実と直面とすることなりました。

そこで、私は会社の有志が取り組んでいたJavaの勉強会へ参加し、
併せてJSPとサーブレットを学ぶことで、Javaを使った開発の汎用性の高さに魅了され
COBOLでは実現できない、Webアプリを作成する楽しさを知りました。

この言語を学べば、業種を問わない多様なプロジェクトでたくさんの経験を積めるはず、、

そう思い、転職を決断しました。

転職活動へ

入社して5年目の春ごろ、転職活動を開始しました。
最初はとにかく実務経験を積むと割り切って、Java + α(フロント系言語等)が経験出来れば、なんでも良しでした。
エンジニアの開発業務といえば、「SIer」「自社開発(Web系)」「SES」に分類されることが多いと思います。
「SIer」に関しては、今回の転職で目指すところではなかったため、「自社開発(Web系)」「SES」を複数社、面接させていただきました。
以下、選考結果とその所感を記載させていただきます。

自社開発(Web系)

スキル不足で、全て見送りでした。
(ポートフォリオとしてJavaで簡単な在庫管理アプリを作成しましたが、ありきたり過ぎましたね…)
ましてや、何のポートフォリオも無しに、自社開発企業に入社することは、ほぼ不可能です。
技術について「こんな本を読んで学習している」「こんな勉強会に参加して日々、自己研鑽に努めている」程度では、
とても面接を突破することはできません。

自社でサービスを行なっている企業様へ入社するためには、
少なくとも、以下の条件を満たす必要があります。

  1. 実務経験
    その会社の自社サービスに使用されているプログラミング言語、開発環境の経験が強く求められます。
    Javaの経験を求める会社は少なく、Ruby on railsやPHPあたりの経験を求められる会社が圧倒的に多いです。
    COBOLの経験ニーズは全くありません。

  2. ポートフォリオの作成
    その会社で使用されているプログラミング言語で、何か一つ(可能であれば二つ三つぐらい)サービスをローンチして、
    GitHubにコミットすることで、初めて書類選考が通るかと思います。
    面接では、自分の作ったサービスのこだわりを、いかに語れるかが鍵となります。
    自分の作ったポートフォリオについて、なぜこのサービスを作り、どう工夫し、その会社のサービスのどういった点に貢献できるのか(できそうか)を伝えることが重要です。

SES

複数社、内定をいただくことができました。
COBOLの経験を一定数、評価していただける企業様と出会うことができました。
SES企業を選ぶ上で重要なのは、その会社が得意としているプログラミング言語と、自分のやりたい分野のスキルセットがマッチするかどうかです。
面接では、以下のようなことを聞かれます。

  1. 実務経験
    これまでにどんなプログラミング言語を使って、どんなプロジェクトで
    どのような成果を挙げたかは必ず聞かれます。
    何か一つ、自分の働きが会社として数字やデータに、大きく反映されたエピソードがあると良いです。
    数値を用いるなど、第三者が想像しやすい材料を混ぜてアピールすると、話の具体性が向上します。

  2. 資格の有無
    SES企業は、プロジェクトへのアサイン率向上や単価交渉を有利に進めるため、資格の有無を確認する企業が多いです。
    (私は関連IT資格を何も持っていませんでした…)
    持っていると学習意欲とポテンシャルをアピールすることができます。
    経験の浅いエンジニアほど、IPAの資格(基本情報技術者、応用情報技術者等)や
    ベンダー資格(Oracle認定試験(Oracle MasterやJava)、LPIC等)の所持は武器となります。

入社難易度は、自社開発企業に比べてかなり低くなる印象です。

SES企業へ入社

複数社、内定をいただけた中からサーバー系のJavaを中心に、フロント系、インフラ系案件を
バランスよく扱っていらっしゃるSES企業に入社を決めました。
入社前にOracle認定のJava Silverを取得の上、学習意欲をアピールし、
COBOL一択だった人生から脱却して、現在も客先常駐にて、Javaに触れながら日々研鑽の毎日を過ごしています。

面接でCOBOLの経験は評価されるのか

はっきり言って、ほぼ評価されませんでした。
数十社ほど自社開発、SES企業様と面接させていただきましたが
おそらく人事担当者様からすると
「システム開発の流れ(ウォーターフォールモデル)は知っている」
「全くのプログラムど素人ではない」

ぐらいの認識で、プログラミング経験者としてのアドバンテージは、さほど得られないのが実情でした。

同じエンジニアという職種でも、COBOLというプログラミング言語と
Java、PHP、RubyといったWeb系プログラミング言語は、
経験値の質として、かなり異なってくるということですね。

反省

スキルチェンジにあたり、もっとこうしておけば良かったな、と感じたことを綴ってみます。

  • 面接でやる気と姿勢をアピールする材料が少なすぎた

とにかく早く実務経験を積みたかったため、COBOLの経験以外にアピールする材料が少なすぎました、、
20代での転職とはいえ、ポテンシャルを見ていただけるのも限界があります。
せめて資格の取得や、ポートフォリオの充実等で、誠意を見せるべきでした。
実務経験に乏しいエンジニアほど、目に見える形の武器を持って、積極的にやる気をアピールするべきだと思います。

  • IT業界やプログラミング言語に対する知識が不足していた

恥ずかしながら、COBOLというプログラミング特性を知らなさ過ぎました。
世の中にはどんなプログラミング言語があって、それを使用するとどのようなことが実現できて、
どんなキャリアを築いていけるのか、ぐらいは学生のうちに、よく考えておくべきだったと思いました。
働いていくうちに、理想と現実の乖離に気がついて、やりたいことが明確になっていくケースは
業種問わずあると思いますので、難しいところですね、、

  • 前職に5年は長く居すぎた

このままCOBOLで戦っていくのか、他の言語にも挑戦していくのかの決断が遅すぎました。
プロジェクトへの参画条件として『Javaの経験〇〇年以上~』といった募集要項があるように
エンジニアのキャリアにおいて、各言語やフレームワーク、ツールの経験年数は非常に重要です。

当たり前ですが、IT業界は資格が無くても働ける業界なので、どんなに難易度の高い資格を取得しても
どんなに素晴らしいポートフォリオを作り上げても、実務経験に勝るものはありません。
自分のキャリアを今後どうすべきか悩んでいる方は、一度、それと徹底的に向き合う時間を作るべきだと考えています。
そして、調べる、考えるはほどほどにして、実際に行動に移してみること。
例えば、今すぐ転職の決断を下す、とまではいかなくとも、とりあえず転職サイトに登録して
興味のある企業を探してみる、エージェントの方に自分の経歴を評価していただく等、何でも良いかと思います。
動いてみた結果、違う、と感じたのならば、また、調べる、考える、に戻るでも、いったん行動をやめてしまっても、それで良いと思います。

後回しにすると、返ってくるツケも大きくなります。年齢は待ってくれません。

結論

20代後半で、COBOLからWeb系言語へのスキルチェンジは可能です。
ただし、これまでのCOBOLエンジニアとしての経験値を評価していただける企業は稀かと思います。
※私の経歴がしょぼいだけかもしれません…

また、SES企業での話になってしまいますが
各企業との面接を通じて感じたこと、また入社後の30代40代の先輩方を見て感じたことは
20代までなら、あまり大きな実績を残していなくとも、スキルチェンジが可能です。
30代以降は、これまでの経歴での実績やリーダー経験、自己研鑽の度合いをより重視される印象でした。

いずれにしろ、一からのスタートとなりますのでこのスキルチェンジにおいて、年収や待遇アップを図ることは難しいと思います。
将来への自己投資と考え、経験を買ったと思って、努力あるのみです。

まとめ

このような経験を経て、今に至っております。
やりたい技術を学ぶことができて、自己成長を感じられる今の環境に対して、とても満足しています。
スキルチェンジ後1年は、学びたい分野の資格を一通り取得して基礎を固めようと思い、以下のベンダー資格を取得しました。

  • 2019年 7月 Oracle Java Silver SE8

  • 2020年 1月 Oracle MASTER Bronze 12c

  • 2020年 4月 Oracle Java Gold SE8

  • 2020年 6月 AWS Solution Architect Associate

これからはフレームワークやフロント言語、Dockerなど、身に着けたい知識をコツコツと勉強していく予定です。

持論まみれの長文となってしまいましたが、この読みづらい文章をここまで読んでいただいた皆様に感謝致します。。
『それは違うんじゃない?』『自分の時はこうだったよ!!』などのコメントがあれば、
ぜひ教えていただけると幸いでございます。

最後に申し上げたいことは『人間行動しなかった後悔の方が、行動した後悔より遥かに苦しい』ということです!
お互いに強いエンジニアとなれるよう、頑張っていきましょう!

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

【Java入門】繰り返し処理について(while、do-while、for、拡張for、break、continue)

目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は繰り返し処理、繰り返し制御文について学ぶために書いています。

【Java入門目次】
変数と型
型変換
変数のスコープ
文字列の操作
配列の操作
演算子
条件分岐
・繰り返し処理 ←今ここ
・クラスについて(準備中)
・抽象クラス(準備中)
・インターフェース(準備中)
・カプセル化(準備中)
・モジュールについて(準備中)
例外処理について
ラムダ式について
Stream APIについて

繰り返し処理とは

とある処理を条件が成立している間回数を指定して、繰り返し処理をさせる事です。

int a = 0;
System.out.println("a => " + a);
a += 1;
System.out.println("a => " + a);
a += 1;
System.out.println("a => " + a);

上記の様なaを出力、+1して再度出力といった一連の処理も繰り返し処理によってスッキリ記述する事が可能です。

繰り返し処理はループとも呼ばれ、本記事ではwhile文、do-while文、for文、拡張for文を取り上げます。

while文

指定された条件が成立する(true)間、繰り返し処理を行います。

構文
while( 条件式 ) {
  処理文; // 条件式がtrueの場合、実行される
}

whileの後の条件式は、boolean値(true,false)を返す式ではないといけません。

while文の例
int num = 0;
while(num < 5) {
  System.out.println("num => " + num);
  num++;
}
実行結果
num => 0
num => 1
num => 2
num => 3
num => 4

冒頭でも出てきた変数を出力、+1して再度出力といった一連の処理をスッキリ記述する事が出来ています。
numが5未満の間この処理は繰り返され、ループ毎にインクリメントされたnumが5になった時
条件判定時にfalseが返されるためwhile文は終了します。

while文の注意点

無限ループ

繰り返し処理が止まる事なく実行され続けてしまう事があります。
以下の点に注意しましょう。

無限ループの例1
int num = 0;
while(num < 5) {
  System.out.println("num => " + num);
  num++;
}

このコードでnum++の記述がなかった場合、num変数は0のままなので
条件式num < 5はtrueを返し続けるため、無限ループとなってしまいます。

無限ループの例2
int num = 0;
while(true) {
  System.out.println("num => " + num);
  num++;
}

次に、条件式のところでwhile(true)をしてしまった場合。
これも常にtrueと返されるので無限ループとなってしまいます。

無限ループになってしまわない為(ループから抜ける為)に使われる繰り返し制御文は別記事にて説明します。そちらも一緒に覚えておきましょう。

ループが始まらない

ダメな例
while(false) {
  // 処理
}

条件式をwhile(false)とすると、処理に入る事が出来ない為コンパイルエラーとなります。

何も出力されない

エラーにもならないのに何も出力されない時もあります。

何も出力されない例
int num = 0;
while(num > 0) {
  System.out.println("num => " + num);
  num--;
}

上記の実行結果は何も出力されません。
whileの条件が0より大きいかを比較している為、いきなりfalseが返されループの中に入る事なく処理が終わってしまうからです。

num変数を1以上にするとループ内の処理が実行されます。

改善案
int num = 3;
while(num > 0) {
  System.out.println("num => " + num);
  num--;
}
実行結果
num => 3
num => 2
num => 1

do-while文

while文と同様に指定された条件が成立する(true)間、繰り返し処理を行います。

構文
do {
  処理文;
}while( 条件式 );

while文と同様で条件式は、boolean値(true,false)を返す式ではないといけません。

do-while文の例
int num = 0;
do {
  System.out.println("num => " + num);
  num++;
}while(num < 5);
実行結果
num => 0
num => 1
num => 2
num => 3
num => 4

numが5未満の間この処理は繰り返され、ループ毎にインクリメントされたnumが5になった時
条件判定時にfalseが返されるためdo-while文は終了します。

while文とdo-while文の違い

条件判定が行われるタイミングが違います。

while文では先に条件判定が行われるので、前述の通り処理が実行されない場合もあります。
しかし、do-while文では条件判定よりも先にdoブロックがあり後で条件判定が行われます。よって条件に関係なく1度は処理が実行されます。

条件判定の例_while
int num = 0;
while(num > 0) {
  System.out.println("while_num => " + num);
  num--;
}

上記のwhile文では何も出力されません。

条件判定の例_do-while
int num = 0;
do {
  System.out.println("do-while_num => " + num);
  num--;
}while(num > 0);
実行結果
do-while_num => 0

do-while文では1度だけ処理が実行され、条件式がfalseである為do-while文を終了しています。

このような違いがある事に注意しましょう。

while文とdo-while文の共通点

while文もdo-while文も処理する文が1つしかない場合は{}を省略して記述する事が可能です。

whileの場合
int num = 0;
while (num < 5) System.out.println("num => " + num++);
実行結果
while_num => 0
while_num => 1
while_num => 2
while_num => 3
while_num => 4
do-whileの場合
int num = 5;
do
  System.out.println("do-while_num => " + num--);
while(num > 0);
実行結果
do-while_num => 5
do-while_num => 4
do-while_num => 3
do-while_num => 2
do-while_num => 1

for文

for文も条件判定がtrueである間、繰り返し処理が実行されます。

while文、do-while文では、()には条件式のみ記述していましたが、
for文の()内では、カウント変数の宣言、初期化条件式カウント変数の更新を行います。

構文
for(式1; 式2; 式3;) {
  処理文;
}

for文の流れは以下の通りになります。

・式1で繰り返し回数を示す変数(カウンタ変数)を宣言、初期化。
・式2で条件判定を行う。
・条件判定がtrueの場合、処理文が実行される。
・処理が実行された後、式3でカウンタ変数の更新が行われる。
・再び式2で条件判定を行う。
・式2での判定がfalseの場合、for文を終了する。

実際にコードと実行結果を見てみましょう。

for文の例
for(int i = 0; i < 5; i++) {
  System.out.println("i => " + i);
}
実行結果
i => 0
i => 1
i => 2
i => 3
i => 4

このようにfor文でもwhile文、do-while文と同様の処理を記述する事が出来ます。

for文の省略した記述方法

以下の様な記述方法でもfor文は実行されます。

カウンタ変数の宣言、初期化の省略

for文の省略した記述方法_例1
int num = 0;
for(; num < 5; num++) {
  System.out.println("num => " + num);
}
実行結果
num => 0
num => 1
num => 2
num => 3
num => 4

for文でカウンタ変数の宣言、初期化を行うのではなく、事前に行っています。
この様な記述方法でも動作します。

カウンタ変数の更新の省略

for文の省略した記述方法_例2
for(int num = 0; num < 5; ) {
  System.out.println("num => " + num++);
}
実行結果
num => 0
num => 1
num => 2
num => 3
num => 4

処理文内にてnum変数をインクリメントして更新しています。
この様な記述方法でも動作します。

インクリメントを忘れてしまうと無限ループになってしまうので注意しましょう。

for文の様々な記述方法と注意点

for文の()内に記述する方法はいくつか規則があるのでみていきましょう。

式1は文である必要がある

以下は式1に変数を記述しているだけなのでエラーになります。

コンパイルエラー
int num = 1;
for(num; num < 5; num++) {
  System.out.println("num => " + num);
}

以下の様に式1は文である必要があります。

改善案
int num = 1;
for(num += 1; num < 5; num++) {
  System.out.println("num => " + num);
}
実行結果
num => 2
num => 3
num => 4

式1の宣言式は1つのみ

以下は式1で複数宣言式を記述しているためエラーになります。

コンパイルエラー
for(int i = 0, int = j = 0; i < 5; i++) {
  System.out.println("i => " + i);
  System.out.println("j => " + (j+=2));
  System.out.println("===============");
}

以下の様に式1での宣言式は1つではないといけません。

改善案
for(int i = 0, j = 0; i < 5; i++) {
  System.out.println("i => " + i);
  System.out.println("j => " + (j+=2));
  System.out.println("===============");
}
実行結果
i => 0
j => 2
===============
i => 1
j => 4
===============
i => 2
j => 6
===============
i => 3
j => 8
===============
i => 4
j => 10
===============

式3も複数入れる事が可能

カンマで式を区切る事で、カウンタ変数の更新処理も複数入れる事が出来ます。
上記のコードを書き換えてみます。

式3に複数入れる例
for(int i = 0, j = 2; i < 5; i++, j+=2) {
  System.out.println("i => " + i);
  System.out.println("j => " + j);
  System.out.println("===============");
}
実行結果
i => 0
j => 2
===============
i => 1
j => 4
===============
i => 2
j => 6
===============
i => 3
j => 8
===============
i => 4
j => 10
===============

同様の結果を出力する事が出来ます。

拡張for文

for文をさらに便利にした記述方法として、配列やコレクションの全要素を順番に取り出して処理をする場合に使用します。

構文
for(変数宣言 : 参照変数名) {
  処理文;
}

実際にコードと実行結果を見てみましょう。

拡張for文の例
int[] numbers = {1, 2, 3, 4, 5};
for(int val : numbers) {
  System.out.println(val);
}
実行結果
val => 1
val => 2
val => 3
val => 4
val => 5

numbersの要素を1つずつval変数へ代入し出力という流れを繰り返し行っています。

同じ処理をfor文で記述して比較してみましょう。

for文の場合
int[] numbers = {1, 2, 3, 4, 5};
for(int i = 0; i < numbers.length; i++) {
  System.out.println("numbers[" + i + "] => " + numbers[i]);
}
実行結果
numbers[0] => 1
numbers[1] => 2
numbers[2] => 3
numbers[3] => 4
numbers[4] => 5

拡張for文で記述する事により、カウンタ変数の宣言、初期化や条件式、カウンタ変数の更新処理の記述が不要になります。
for文に比べてシンプルに記述する事が可能です。

拡張for文の注意点

変数宣言する型は、参照する変数の型と一致していなければいけません。
以下はエラーになります。

コンパイルエラー
String[] names = {"田中", "佐藤", "鈴木"};
for(int val : names) {
  System.out.println("name => " + val);
}

参照変数がString型の配列であるのに対して、変数宣言がint型である為です。

参照変数と各要素を受け取る変数の型は一致合わせるよう注意しましょう。

改善案
String[] names = {"田中", "佐藤", "鈴木"};
for(String val : names) {
  System.out.println("name => " + val);
}
実行結果
name => 田中
name => 佐藤
name => 鈴木

for文と拡張for文の共通点

for文も拡張for文も処理する文が1つしかない場合は{}を省略して記述する事が可能です。

for文の場合
for(int i = 0; i < 3; i++)
  System.out.println("i => " + i);
実行結果
i => 0
i => 1
i => 2
拡張for文の場合
int[] numbers = {10, 20, 30};
for(int val : numbers)
  System.out.println("val => " + val);
実行結果
val => 10
val => 20
val => 30

ローカル変数型推論(var)の利用が可能です。
(ローカル変数型推論については別記事にまとめようと思います。)

for文の場合
for(var i = 0; i < 3; i++) {
  // 省略
}
拡張for文の場合
for(var val : numbers) {
  // 省略
}

繰り返し制御文とは

上述の繰り返し処理は、条件式がtrueの間実行され続けるというものでした。

特定の条件の時に繰り返し処理から抜けたいという時、繰り返し制御文であるbreak文continue文を使用します。

break文

実行中の繰り返し処理を中断して抜け出す時、繰り返し文の無限ループから抜け出す時に使用します。
switch文のcase内でも使用します。

ループから抜けた後は次の処理を実行します。

break文の例
for(int i = 0; ; i++) {
  if(i == 5) {
    break;
  }
  System.out.println("i => " + i);
}
System.out.println("for文の後の処理");
実行結果
i => 0
i => 1
i => 2
i => 3
i => 4
for文の後の処理

式2を書き忘れているため、条件式がありません。よって無限ループになってしまいます。
しかし、if(i == 5)のブロック内にbreak;がある事で、iが5になった時ループを抜ける事になります。

そしてfor文の後の処理を実行しています。

continue文

実行中の繰り返し処理を中断するのではなくその処理だけスキップして、条件式の判定を行わせ更に繰り返し処理を続ける時に使用します。

continue文の例
for(int i = 0; i < 10; i++) {
  if((i % 3) == 0) {
    continue;
  }
  System.out.println("i => " + i);
}
System.out.println("for文の後の処理");
実行結果
i => 1
i => 2
i => 4
i => 5
i => 7
i => 8
for文の後の処理

if((i % 3) == 0)のブロック内にcontinue;がある事で、
iを3で割って余りが0の時に処理をスキップしています。(式2の条件判定がtrueである限りループは続く)

そして、for文が終了した後はその後の処理を実行しています。

break文とcontinue文の共通点 ラベル

繰り返し文がネストしている場合(複数ある場合)、内側の繰り返し文に記述されたbreak文やcontinue文は、内側の繰り返し処理のみ適用されます。
外側のループからも抜けたい、スキップしたい場合、ラベルを使用する事で可能です。

構文
ラベル名:
for(式1; 式2; 式3;) {
  for(式1; 式2; 式3;) {
    break 又は continue ラベル名;
  }
}

break文とcontinue文でそれぞれラベルの有無で処理がどう違うかみてみましょう。

break文の場合

ラベルを使用しない場合は以下の様になります。
iとjが1の時にループを終えたい。という想定で実装をしています。

ラベル無し
for(int i = 0; i < 3; i++) {
  for(int j = 0; j < 3; j++) {
    System.out.println("i: " + i + ", j: " + j);
    if(i == 1 && j == 1) {
      break;
    }
  }
}
実行結果
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 1
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2

ループは終わらず、iがインクリメントされて次のループにいっています。

ラベルを使用した場合は以下の様になります。

ラベル有り
loop:
for(int i = 0; i < 3; i++) {
  for(int j = 0; j < 3; j++) {
    System.out.println("i: " + i + ", j: " + j);
    if(i == 1 && j == 1) {
      break loop;
    }
  }
}
実行結果
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 1

iとjが1の時に外側のループも抜ける事が出来ています。

continue文の場合

ラベルを使用しない場合は以下の様になります。
iとjが1の時に、iをインクリメントした次のループへスキップしたい。という想定で実装をしています。

ラベル無し
for(int i = 0; i < 3; i++) {
  for(int j = 0; j < 3; j++) {
    if(i == 1 && j == 1) {
      continue;
    }
    System.out.println("i: " + i + ", j: " + j);
  }
}
実行結果
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 1, j: 2
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2

iとjが1の時だけしかスキップされていません。

ラベルを使用した場合は以下の様になります。

ラベル有り
loop2:
for(int i = 0; i < 3; i++) {
  for(int j = 0; j < 3; j++) {
    if(i == 1 && j == 1) {
      continue loop2;
    }
    System.out.println("i: " + i + ", j: " + j);
  }
}
実行結果
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0
i: 2, j: 0
i: 2, j: 1
i: 2, j: 2

iとjが1の時に、iをインクリメントした次のループへスキップ出来ています。

この様にラベルを用いる事で、多重ループから抜け出したり、スキップしたりする事が可能になります。

終わりに

同じ処理を繰り返し行うための構文、条件に応じて処理を中断、スキップさせるための繰り返し制御文を学びました。
使い分ける事によってコードをスッキリ記述する事ができ、同じ処理を何度も書かなくて済むので使いこなしていきたいですね。

参考サイト

【Java入門】繰り返し処理をマスターしよう!(forとwhile)
拡張for文(for-each文)

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

WSLで、JVM言語の開発環境を整える

SDKMAN公式インストールガイド

sudo apt-get install unzip zip

curl -s "https://get.sdkman.io" | bash

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

【Effective Javaを読む】 第2章 項目4 『privateのコンストラクタでインスタンス化不可能を強制する』

【Effective Javaを読む】 第2章 項目4 『privateのコンストラクタでインスタンス化不可能を強制する』

ユーティリティクラス(staticのメソッドとstaticのフィールドからなるクラス)はインスタンス化されるようには設計されない。にもかかわらずインスタン化できてしまうものをよく見るので、privateのコンストラクタを使って絶対インスタンス化できないようにしましょう、ってお話。

用語集

デフォルトコンストラクタ

コンストラクタを1つも定義していない場合、自動的にデフォルトコンストラクタと呼ばれるコンストラクタが作成される。

サンプルコード

明示的にprivateなコンストラクタを記述することで、外部からのアクセスを不可能にし、デフォルトコンストラクタの生成も抑止される。
AssertionErrorは厳密には必要ではないけれど、保険として入れている。

//インスタンス化できないユーティリティクラス
public class UtilityClass {
    //インスタンス化できないようにするためにデフォルトコンストラクタを抑制する
    private UtilityClass() {
        throw new AssertionError();
    }
     //残りは省略
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】Spring Boot 2.3.1でバリデーションをつけていく記事

はじめに

こんにちは!ゼロからJavaであそぶシリーズ第4弾です。いま名前つけました。

前回までの記事はこちら↓

  1. macでSTSの環境構築
  2. Spring BootでHelloWorld
  3. Spring Bootでエコーアプリケーション作成

今回は、前回作ったエコーアプリケーションに、バリデーションをつけていこうと思います。

もょもと.gif

現状、文字列も数字も環境依存文字も、何文字でも入れられてしまうガバ状態なので、怪文書を送ると以下のように表示崩れが発生してしまいます。

スクリーンショット 2020-07-21 0.26.28.png

怖い。
名前にハテナが並んでると不穏な雰囲気になっちゃいますね。文字化けかな?
ゼロが画面幅を突き破ってしまっているのもなんか嫌ですね。

そこで今回は、何かのサービスのユーザーネームっぽく、以下の要件でバリデートするようにしちゃおうと思います。エラーの場合同じ画面内でエラーメッセージを出すイメージです。

  • 使用可能文字種:半角英数のみ
  • 文字数制限:4文字以上

完成品は以下のような形です。
(背景色を目に優しい色に変えました)

画面収録 2020-07-21 23.39.15.mov.gif

使用環境とバージョン

  • macOS Catalina
  • jdk14.0.1
  • JUnit5
  • Maven 3.6.3_1
  • STS 4.6.1
  • Spring Boot 2.3.1
  • spring-boot-starter-thymeleaf-2.3.1

フォームクラスにアノテーションでバリデーションを実装

フォームクラスに以下のアノテーションを追加しました。

  • @NotBlank:nullまたは空文字でないこと1をチェックできます
  • @Size:属性minまたはmaxを使用して最小値・最大値を指定できます
  • @Pattern:属性値regexpで正規表現のパターン文字列を指定できます。今回は半角英数のみを許容する正規表現を使用しました
EchoForm.java
package com.example.form;

import java.io.Serializable;

import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotBlank;

public class EchoForm implements Serializable {
    private static final long serialVersionUID = 1L;
    @NotBlank
    @Size(min = 4)
    @Pattern(regexp = "[a-zA-Z0-9]*") //英数字であること
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

コントローラークラスにエラー時の分岐追加

変更を加えたechoメソッドのみ記述します。
変更点は以下2点です。

  • 引数にBindingResultクラスを追加しました。BindingResultには入力チェック結果が入っています。入力チェックの実行とBindingResultの生成はフレームワークが担ってくれる部分ですので、実装の必要はありません。(Spring徹底入門より)
  • エラー時の分岐を追加しました。 アノテーションによるチェックでエラーが発生した場合、「Hello ${ユーザーネーム}」の画面に行ってほしくないため、同じページをリターンするように変更しています。
EchoController.java
@RequestMapping(value = "echo", method = RequestMethod.POST)
    public String echo(@Validated EchoForm echoForm, BindingResult result, Model model) {
        if (result.hasErrors() ) {
            model.addAttribute("name", echoForm.getName());
            return "index";
        }
        model.addAttribute("name", echoForm.getName());
        return "echo";
    }

index.htmlにエラー時のテキスト追加

エラーが出た際だけ、以下のように赤字のエラーメッセージを表示するようにしたいんですよね…
これ、Springならできちゃいます!

スクリーンショット 2020-07-21 23.26.59.png

前回既にフォームオブジェクトをバインディングしているので、echoFormのエラー情報を取得してThymeleafにぶち込みHTMLを作ることができてしまうのです。
Springすごい。Thymeleafすごい。

form部分のみ記載します。
spanで囲っている場所で、オブジェクトがエラーである場合のみエラーメッセージを表示するようにしてあります2。具体的に言うと、th:errors属性にフォームオブジェクトのプロパティを設定しているため、指定したプロパティに対するエラーメッセージのみ表示されるようになっています。
エラーメッセージが複数ある場合は自動的に<br />が入る仕様です。ありがたい。

index.html
<form th:object="${echoForm}" th:action="@{/echo}" method="POST">
        <div>ユーザーネームを入れてください</div>
        <div>(半角英数4文字以上)</div>
        <div>
            <input type="text" th:field="*{name}" /> 
            <button>送信します</button>
        </div>
        <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" style="color: red"></span>
</form>

エラー文言の日本語化

実は、ここまでの実装で実行すると、エラーメッセージがアノテーションのデフォルトメッセージ(英語)になってしまいますので、エラーが以下のようになってしまいます。

スクリーンショット 2020-07-21 23.52.09.png

これはちょっと嫌ですね…上はまだオシャレなエラーメッセージ❤️で通せるかもですけど、sizeの数字に関してはユーザーから見たら意味不明ですものね…
ここは日本語のエラーメッセージに直しましょう!

Spring MVCによるBean Validationのエラーメッセージは、propertiesファイルに定義するのが望ましいようです。いくつか方法があるようですが、クラスパス直下にValidationMessages.propertiesを作成する方法が分かりやすかったので、そちらを採用しました。

resources配下にValidationMessages.propertiesを新規作成します。

スクリーンショット 2020-07-22 0.29.58.png

中身は、使用したアノテーションのメッセージを書き換えるだけのシンプルな実装です。

ValidationMessages.properties
org.hibernate.validator.constraints.NotBlank.message=入力必須です。
javax.validation.constraints.Size.message=4文字以上でなくてはいけません。
javax.validation.constraints.Pattern.message=半角英数のみ有効です。

ちなみに、上記をただValidationMessages.propertiesに貼り付けると、日本語入力してエンターを押すだけで自動的に以下のようになってしまいました…Spring Bootの標準BeanがUTF-8でないためマルチバイト文字で文字化けしてしまうようです

ValidationMessages.properties
org.hibernate.validator.constraints.NotBlank.message=\u5165\u529B\u5FC5\u9808\u3067\u3059\u3002
javax.validation.constraints.Size.message=4\u6587\u5B57\u4EE5\u4E0A\u3067\u306A\u304F\u3066\u306F\u3044\u3051\u307E\u305B\u3093\u3002
javax.validation.constraints.Pattern.message=\u534A\u89D2\u82F1\u6570\u306E\u307F\u6709\u52B9\u3067\u3059\u3002

こちらのQiitaのコメント欄でも言及があったのですが、自分もSpring 2系でJava11以上だからか、ファイルのプロパティでUTF-8にすればアプリケーションでも問題なく作動しました。
ファイルを右クリック > プロパティ > ResourceのText file encodingをデフォルトからUTF-8に変更するだけです。

スクリーンショット 2020-07-22 0.58.50.png

これでファイルを保存しようとすると以下警告が出ますが、Save as UTF-8で問題なく作動しました。

スクリーンショット 2020-07-22 0.50.06.png

これで、以下のように日本語のメッセージになりました!

スクリーンショット 2020-07-22 0.41.48.png

おわりに

今回はエラー時のテキスト追加部分のThymeleaf記法と、ValidationMessages.propertiesの日本語化でちょっと苦労しました!今回紹介した方法はSpring 1系だと怪しいので、適宜調べてみてください。

お読みいただき、ありがとうございました!


  1. nullのみチェックするには@NotNullを使用します 

  2. こちらのページの実装を参考にしました。 

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

【#3 Java】Javaを勉強するならこれを読め!~厳選~

書籍

スッキリわかるJava入門 第三版
Javaの基礎からオブジェクト指向まで、Javaの「なぜ?」が分かる一冊。詳しい解説と豊富なイラストで、しっかり学べる内容になっています。注意点は、最初から最後までストーリー仕立てになっているため、項目ごとに調べるのには不向き。


2週間でJava SE Bronzeの基礎が学べる本 徹底攻略シリーズ
未経験SEとして働いている友達に教えてもらった本。そもそもプログラミング言語とは?Javaとは?という所でつまづいていた私を助けてくれました:sob:

サイト

Java SE API & ドキュメント
調べる際、初めはQiitaなどのサイトでもいいと思うのですが、投稿主の主観が入ってきてしまう恐れがあるため最終的にはやっぱり公式を見るのが一番だと思っています。
使用するバージョンに合ったドキュメントを見なければいけない点に注意!


java入門 javaのプログラムが走る喜び・快感を感じよう!
「Javaで簡単なプログラミングが出来るようになる」ことを目的とした、初心者向けのサイト。
Javaの基本となるプログラム例と説明文が丁寧に記載されており、とても分かりやすいです。


Java道 Java基本
基本的な知識の解説の他、疑問に思ったことや実現したいこと、エラーなどから解決策を探せるFAQもあり便利です。


Java コード入門
説明、ソースコードの例がとても見やすく分かりやすいです。




以上、Javaを勉強する際に読むべき個人的おすすめ書籍、サイトでした!
他にもこれおすすめだよ、というものがあったら是非教えてください!

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

Javaのテストで環境変数がいるとき

やりたいこと

環境変数で値を上げてるところも含めてテストしたいときに、テスト時に環境変数がなくて死ぬのをなんとかしたい

解決策

pomに書く

pom.xml
                        <plugin>
                                <artifactId>maven-surefire-plugin</artifactId>
                                <version>{your version}</version>
+                               <configuration>
+                               <environmentVariables>
+                                       <TEST_ENV>100</TEST_ENV>
+                               </environmentVariables>
+                               </configuration>
                        </plugin>

これでコード内でSystem.getenv("TEST_ENV")としてるところで100がとれるようになる

参照

https://maven.apache.org/components/surefire-archives/surefire-3.0.0-M1/maven-surefire-plugin/test-mojo.html#environmentVariables

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

JavaとGradleでHello world

はじめに

何番煎じかわかりませんが、学習のためにJavaとGradleを使って「Hello world.」と表示するプログラムを作成してみました。

前提

  • Java version
    • 11
  • Gradle version
    • 6.5.1
  • OS
    • Windows 10

上記の環境で動作確認しています。ただし、ごく簡単なことしかしないため、多少のバージョンの違いは適宜読み替えれば問題ないはずです。

Gradleプロジェクトの作成

Gradleプロジェクトを作るためにまず適当なフォルダを作成します。

コマンドならば次のようになります。

mkdir hello-gradle
cd hello-gradle

ここではフォルダをhello-gradleという名前にしました。

次に、gradleコマンドでプロジェクトを作成します。
プロジェクトを作成したいフォルダをカレントディレクトリにして、(つまり、cd hello-gradleしてフォルダの中にいる状態で)次のコマンドを実行します。

gradle init --type java-application

するといくつか質問をされます。

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2]

ビルドの設定を行うスクリプトをGroovyかKotlinのどちらで記述するか選択します。Javaプロジェクトの場合は普通Groovyを使うと思われるので、「1」を入力してEnterします。

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter

次にテストフレームワークを選択します。今回はHello worldするだけなので、テストは何でもよいのですが、とりあえず「4」を入力してEnterします。

Project name (default: hello-gradle):
Source package (default: hello.gradle):

最後に「Project name」と「Source package」をそれぞれ聞かれます。ここではデフォルトで進めることとし、何も入力せずEnterします。

これでhello-gradleフォルダの中に各種ファイルが生成されるはずです。

「Hello world.」するために必要なソースコードはすでにすべて出来上がっています。

今回もっとも重要な「Hello world.」を標準出力するメインメソッドはsrc/main/java/hello/gradle/App.javaにあります。

なお、最初にgradle init --type java-applicationしましたが、--typeの指定無しでgradle initだけでもいくつか質問事項が増えるだけで同じ結果になるはずです。

ただ、Javaのコマンドラインアプリケーションを作成するなら--type java-applicationを指定したほうが速いです。

--typeに指定できるタイプはSupported Gradle build typesに記載されています。たとえば、kotlin-applicationを指定するとKotlinのプロジェクトができるようです。

動作確認

hello-gradleフォルダにおいて、コマンドプロンプトなら、

gradlew.bat run

のコマンドを実行します。するとHello world.の文字列が出力されているはずです。これでJava + GradleによるHello worldは完了です。

テストを実行

Hello worldを標準出力するだけのプログラムなのでテストをしても意味がないですが、せっかくテストコードが用意されているのでテストも実行してみます。

動作確認時と同じように、コマンドプロンプトなら、

gradlew.bat test

を実行します。

すると、テストは成功し、BUILD SUCCESSFULと表示されます。

なお、テストコードはsrc/test/java/hello/gradle/AppTest.javaにあります。

このクラスのappHasAGreetingメソッドがテストメソッドです。

このテストはメインメソッドのあるAppクラスのgetGreetingメソッドがnullを返さない、ということを検証しているだけです。このメソッドはHello world.という文字列を返しているので当然テストはパスします。

このgetGreetingメソッドをたとえば、

package hello.gradle;

public class App {
    public String getGreeting() {
        // return "Hello world.";
        return null;
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}

のようにnullを返すように変更してテストを実行すると、今度はテストが失敗し、> Task :test FAILEDという文言と失敗したテストメソッドが表示されます。

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