20200701のJavaに関する記事は12件です。

【JavaBronze(ブロンズ)】押さえておきたい問題5選

まえがき

昨今、JavaBronze SE7/8 試験を受けました。
勉強をする中で個人的に気になった問題について紹介していきます。

1問目.int型とchar型の互換性

public class Q1 {

    public static void main(String[] args) {
        char chr = 65;
        int num = 'A';
        Q1 q1 = new Q1();
        q1.func1(chr, num);
    }

    void func1(char chr, int num) {
        System.out.println("chr:" + chr);
        System.out.println("num:" + num);
    }
}

このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。

A. chr:A
  num:65
B. コンパイルエラーが発生する
C. 実行時エラーが発生する
D. 何も表示されない

解説

プリミティブ型であるint型とchar型には互換性があり、暗黙的なキャストが可能です。
char型変数にint型を代入すると、ASCIIコード表に対応した文字が代入されます。
逆にint型変数にchar型を代入すると、その文字に対応した10進数が代入されます。

正解:A

2問目.インクリメント演算子とデクリメント演算子

public class Q2 {

    public static void main(String[] args) {
        int num = 1;
        System.out.print(++num + num++ + --num + num--);
    }
}

このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。

A.4
B.6
C.7
D.8

解説

++numは2、num++は2、--numは2、num--は2として計算され、合計して8となります。
注意しなければいけないのは、num++が2として計算された後にインクリメントされ3となりますが、
その後すぐに--numによってデクリメントされ2になるところです。
後置されたインクリメント、デクリメント演算子は、右側に同じ変数がある場合、その変数に対して処理が反映されます。

正解:D

3問目.無限ループと実行時エラー

public class Q3 {

    public static void main(String[] args) {
        char[] chr = { 'A', 'B', 'C' };
        while (true)
            for (int i = 0; i <= chr.length; i++)
                System.out.println(chr[i]);
    }
}

このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。

A. 正常に実行が終了する
B. 無限ループになる
C. コンパイルエラーが発生する
D. 実行時エラーが発生する

解説

while文で無限ループ、for文で実行時エラー(ArrayIndexOutOfBoundsException)が発生します。
しかし、for文で実行時エラーが発生した時に、実行中の処理の途中であったとしても
その処理を途中でストップして実行時エラーとして出力されます。
これらが同時に発生している場合は以下の順番に起きる可能性が判断されていきます。

1.コンパイルエラー
2.実行時エラー
3.無限ループ
4.正常に実行が終了

正解:D

4問目.オーバーライドの関係性

class Super {
    static void func(String str) {
    }
}
class Sub extends Super {
    String str;
    void func(String str) {
        this.str = str;
    }
    void print() {
        System.out.println(str);
    }
}
public class Q4 {
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.func("Hello World");
        sub.print();
    }
}

このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。

A. Hello World
B. コンパイルエラーが発生する
C. 実行時エラーが発生する
D. 何も表示されない

解説

SuperクラスのfuncメソッドはSubクラスのfuncメソッドにオーバーライドされていますが、staticで修飾されているためコンパイルエラーとなります。
注意しなければいけないのは、この問題はmainメソッドのトレースを行ってもエラーに気が付けないところです。
mainメソッドの処理ではなく、スーパークラスとサブクラスのオーバーライドの関係性にあるメソッドに着目する必要があります。

正解:B

5問目.クラス型のキャスト

class Super {
    void print() {
        System.out.println("Hello World");
    }
}

class Sub extends Super {
}

public class Q5 {
    public static void main(String[] args) {
        Sub sub;
        Super spr;
        sub = new Sub();
        spr = sub;
        sub = spr;
        sub.print();
        int num_1;
    }
}

このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。

A. Hello World
B. コンパイルエラーが発生する
C. 実行時エラーが発生する
D. 何も表示されない

解説

サブクラス型 → スーパークラス型 への代入を行うspr = sub;の行ではエラーは起きず、
スーパークラス型 → サブクラス型 への代入を行うsub = spr;の行でコンパイルエラーが起きます。

基本データ型の場合では、小さい型の変数に大きい型の変数を代入する場合、明示的なキャストが必要です。

例)
double double_01 = 100.0;
int int_01 = double_01;      // 暗黙的なキャスト(エラーが起こる)
int int_01 = (int)double_01;     // 明示的なキャスト(エラーが起こらない)

ではクラス型の場合、大きな型(スーパークラス+差分クラス)であるサブクラス型 → 小さい型(スーパークラスのみ)であるスーパークラスに代入してもエラーが起こらず、その逆で起こるのでしょうか?

その理由はコンパイラはコンパイルを型の互換性をチェックしてエラーか判断するためです。
基本データの場合、型の大小で互換性を判断しますが、クラス型の場合はクラスの内容を見て判断されます。

サブクラスはどのスーパークラスを継承しているか書かれていますが、スーパークラスにはどのサブクラスから継承されているか書かれていません。

そのため、サブクラスにスーパークラスを代入する場合は互換性が分かるためエラーになりませんが、スーパークラスにサブクラスを代入する場合は互換性が分からないため、明示的にキャストを行わないとコンパイルエラーになるのです。

正解:B

あとがき

いかがでしたでしょうか。
これで試験対策は万全!!!かも_(:3」∠)_

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

未経験エンジニア奮闘記 JavaSilver合格目指して勉強中詰まったところString型編

はじめに

これは大学中退後8年間フリーターだったアラサーが一念発起してエンジニアに転職できたはいいものの、右も左もわからなすぎるのでJavaSilverを取得しようと頑張ったけどここで詰まったよってお話です。

まず私にはほとんどJavaの知識はありません。それどころかプログラミング全般大した知識が無いと言っていいです。
学生時代に趣味と授業でC言語はいじったなぁくらいで、最近はPython3をWeb教材で勉強したぞ! ってくらいのドがつく素人です。

で、何故Javaの資格かと言われると単純に需要が高そうだから。それだけです。
オブジェクト指向ってやつをするのに適してるらしいじゃん? くらいの印象しかありません。

Java Silver合格まで勉強中詰まったところをほぼ自分用に書いていこうと思います。
間違い等ありましたら是非私の未来の為に教えてください。

String型の不思議

とりあえずJavaの入門書を買って勉強していると、さっそく面白い内容にぶちあたりました。
なんと、String型は参照型であるため例えば

StrTest.java
public class StrTest {
  public static void main(String args[]) {
    String str1 = "hoge";
    String str2 = "ho";
    str2 += "ge";
    System.out.println(str1 == str2);
  }
}

falseになるから文字列比較する時は

StrTest.java
public class StrTest {
  public static void main(String args[]) {
    String str1 = "hoge";
    String str2 = "ho";
    str2 += "ge";
    System.out.println(str1.equals(str2));
  }
}

みたいにequals()を使用するんだぞ

とのこと。さっそく大興奮の私は試しにこんなコードを書きました。

StrTest.java
public class StrTest {
  public static void main(String args[]) {
      String str = "hoge";
      StrChange(str);
      System.out.println(str);
  }

  public static void StrChange(String str){
      str = "fuga";
  }
}

これで出力はfugaになるはず。なんてったって参照型なんだから。
そう思って実行すると出力されるのはhoge。おやぁ~??

まったくクエスチョンになったのでGoogle先生に泣きついてみると、しっかり解説が出てきました。
先輩エンジニアの皆さん、ありがとうございます。

https://qiita.com/chihiro/items/d3d9a028cd5dd8e84649

どうやらString型のインスタンスはimmutableなので値を変更することがそもそもできないようです。
値を変更できない……???

StrTest.java
public class StrTest {
  public static void main(String args[]) {
        String str = "hoge";
        System.out.println(str); // hoge
        str += "hoge";
        System.out.println(str); // hogehoge
        str = "fuga";
        System.out.println(str); // fuga
  }
}

めちゃ変更されてる!!
何故immutableなのに値が変更できる? 何故変更できるのにさっきのコードでは変更されてない?

前後不覚になったのでGoogle先生に泣きついてみると、しっかり解説が出てきました。
先輩エンジニアの皆さん、ありがとうございます。

https://freelance-jak.com/technology/java/1204/

どうやら値を変更しているようにみえて実際は新たな領域に新しい値として作り直しているようです。
というわけで間違いなくString型はimmutableであるようです。
さっき書いたコードはどっかのメソッドで新たな文字列が勝手に爆誕しているだけだったってことですね。

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

【Java】Public static void main(String[] args)ってなんぞ?

javaやっている方は必ず目にするpublic static void main(String[] args)

実行するには必ず必要なメソッドとなります。

でも、これ、どれか1つでも間違えるとエラーになります。どうしてなんでしょうね?

気になったので、今後の同じ初心者の方向けにご紹介しておきます。

■意味をまず理解しよう

Hello.java
class Hello{
    public static void main(String[] args) {
        System.out.println("Hello World");        
    }
}

で、内容はこんな感じ。

意外とインスタンスを理解する上ではstaticも重要なんで覚えておこうね(戒め)

項目 意味
public どこからでも参照可能(アクセス修飾子)
static インスタンス可(new)しなくても外部から使用可能
void 戻り値なし
main メソッド名
String[] 引数をString型の配列で受け取る
args 引数名、argument(和訳:引数)の複数形でargumentsの略省

argsって何者?

String[] argsは、プログラム起動時に指定する値(コマンドライン引数)です。

引数名argsは、慣習的にargsが使われていますが、別の名前でも使用できます。
慣習的にargsが使われている理由は、JavaはC言語の後継であり、C言語の慣習をそのまま受け継いでいるためです。

staticってなんぞ?

main メソッドに static 修飾子が必要です。でもなんでって思いません?

それは、通常、クラスのメソッドを使用する場合はクラスのインスタンスを new で生成する必要があります。(これ初心者覚えてない人多すぎ問題)

SubClass sub = new SubClass();
sub.main(new String[] {"Hello SubClass!"});

しかし static 修飾子を付けることによって new でインスタンス化しなくてもメソッドにアクセスできるようになります。

SubClass.main(new String[] {"Hello SubClass!"});

ということは java コマンドを実行してもメインクラスのインスタンスは生成されないため、実行される main メソッドは static 修飾子が付いてる必要があるということです。

とりあえず実行するにはこれが必要

  1. メソッド名が main
  2. 引数が String 型の配列(可変長引数)
  3. static 修飾子が付与されている

この条件を満たしてないクラスを java コマンド実行しようとすると以下のように失敗します。

エラー: メインメソッドがクラスHelloWorldで見つかりません次のようにメインメソッドを定義してください
   public static void main(String[] args)

また、戻り値をvoid以外にした場合も以下のエラーがでます。

エラー: メイン・メソッドはクラスHelloWorldのvoid型の値を返す必要があります。
次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kotlinのinfix

これは何か

kotlinの予約語、infixってなんやねんって思っちゃったので定義や作り方を調べてみました。

目次

  • なんとかfix
  • 標準ライブラリの例:「to」
  • infixの関数を定義する
  • 定義したinfixの関数をKotlinから呼び出す
  • 定義したinfixの関数をJavaから呼び出す
  • infix関数を逆アセンブルする
  • 参考

なんとかfix

そもそも。infixってなんやねん。英単語を知らない(笑)
Google翻訳にかけると、「中置」って返ってくる。
ここで気づいた、なんとかfixの一個だこれ。他にもよく使ってるなんとかfixがある。
prefix、suffix。
中置きの文字通り、オブジェクトや値の間にinfix関数を置いて、挟まれた2つのオブジェクトや値を引数に処理を行う関数を定義できるKotlinの機能である。

標準ライブラリの例:「to」

標準ライブラリのinfix関数 to は、下記のように呼び出して Pair インスタンスを生成する。

Pair.kt
val pair: Pair<String, String> = "key" to "value"

メソッドは、下記のように拡張関数として定義されている。

Standard.kt
/**

 * Creates a tuple of type [Pair] from this and [that]. 
 * 
 * This can be useful for creating [Map] literals with less noise, for example: 
 * @sample samples.collections.Maps.Instantiation.mapFromPairs 
 */
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that) 

参考:Tuples.kt

それでは、infix関数を自作してみたい。

infixの関数を定義する

よく遭遇するような、「同じようなオブジェクトを持っている、似ているオブジェクトのインスタンスに値を詰め替える」ケースを想定する。
ここでは、Customerクラスの値に履歴シーケンスをくっつけてCustomerHistoryクラスのインスタンスを生成し、それらのインスタンスをジェネリクスに含むPairインスタンスを返す関数を作る。

まずはインプットと合成の結果となるデータクラスを定義。

Data.kt
data class Customer (
        var id: String,
        var name: String
)

data class CustomerHistory (
        var id: String,
        var name: String,
        var seq: Int
)

元ネタであるCustomerから、シーケンス付きでCustomerHistoryインスタンスを生成するinfix関数を、以下のように定義する。

Infix.kt
/**
 * 拡張関数内部でレシーバをthisとして扱っている。シーケンスは関数の引数として受け取る。
 */
infix fun Customer.mapToHistory(seq: Int): Pair<Customer, CustomerHistory> = Pair(
        this,
        CustomerHistory(
                this.id,
                this.name,
                seq
        )
)

これで準備完了。さて、Kotlinコード、およびJavaコードから定義したinfix関数を呼んでみる。

定義したinfixの関数をKotlinから呼び出す

上記で定義した自作infix関数を呼び出す。

Caller.kt
// 元ネタ。
val customer: Customer = Customer("00001", "Eron Musk")

// 元ネタと履歴オブジェクトのPairをシーケンス付きで返す。
val pair: Pair<Customer, CustomerHistory> = customer mapToHistory 2

定義した関数が、例としてよくなかったのでありがたみがわかづらいけども、オブジェクトや値の間に定義した関数を置くことで結果を得られる。

定義したinfixの関数をJavaから呼び出す

Kotlinの相互運用性により、Javaからも呼び出しが可能である。
同じような呼び出しのスニペットを、Javaでもやってみると

Caller.java
/**
 * 戻り値は、Kotlinのライブラリに含まれるkotlin.Pairをimportして定義する。
 */
public static void printHistory(Customer customer) {
        Pair<Customer, CustomerHistory> customerHistory = InfixKt.mapToHistory(customer, 2);
}

やはりなんとなく予想はついてたけど、Javaからみるとinfix関数はstaticメソッドになっている。

infix関数を逆アセンブルする

定義したKotlinファイルはkotlincでコンパイルした。

Javaからの呼び出しを見ることで命令手続きは大方想像がつくんだけども、気になる。
ということで、コンパイルされたKotlinファイル(クラスファイル)を逆アセンブルしてみる。

mac:classes user$ javap InfixKt.class
Compiled from "Infix.kt"
public final class InfixKt {
  public static final kotlin.Pair<Customer, CustomerHistory> mapToHistory(Customer, int);
}

めちゃくちゃ想像どおりだった...クラスのない、もしくは複数クラスをもつKotlinファイルは、コンパイルされると<file name>Ktと命名される。もとのファイル名は Infix.ktだった。
逆アセンブルの結果を見ると、finalなクラスに内包されたstaticメソッドになっている。

2020-07-02:追記
staticメソッドになっているのは、自作した関数が拡張関数であるためでした。

javapにオプションをつけて、もう少し詳しい情報を出力してみる。

mac:classes user$ javap -c -l -p InfixKt.class
Compiled from "Infix.kt"
public final class InfixKt {
  public static final kotlin.Pair<Customer, CustomerHistory> mapToHistory(Customer, int);
    Code:
       0: aload_0
       1: ldc           #10                 // String $this$mapToHistory
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #18                 // class kotlin/Pair
       9: dup
      10: aload_0
      11: new           #20                 // class CustomerHistory
      14: dup
      15: aload_0
      16: invokevirtual #26                 // Method Customer.getId:()Ljava/lang/String;
      19: aload_0
      20: invokevirtual #29                 // Method Customer.getName:()Ljava/lang/String;
      23: iload_1
      24: invokespecial #33                 // Method CustomerHistory."<init>":(Ljava/lang/String;Ljava/lang/String;I)V
      27: invokespecial #36                 // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      30: areturn
    LineNumberTable:
      line 12: 6
      line 13: 10
      line 14: 11
      line 15: 15
      line 16: 19
      line 17: 23
      line 14: 24
      line 12: 27
      line 19: 30
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      31     0 $this$mapToHistory   LCustomer;
          0      31     1   seq   I
}

やはり、コンパイルされたあとも見知ったJavaのオペコードになっているようである。

3行まとめ

  • infixとは、中置きを指す言葉で接頭辞/接尾辞の間に位置するようなもの。
  • infix関数は、値やオブジェクトの間に置いて処理を行う関数である。
  • Javaからみると、staticメソッドのシンタックスシュガーのようになっている。

参考

書籍
Kotlinイン・アクション

ウェブサイト
中置き記法 - Kotlinリファレンス

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

[Android]他の人のアプリを盗み見しよう

業務上、競合他社アプリを盗み見参考にしたく、ソースコードを拝見させていただくことがたまーにあるので、備忘録として残す。

端末にインストール済みのAndroidアプリをダウンロード

# PC、デバイス間の接続ができているか確認
$ adb devices

# 対象のパッケージを検索
$ adb shell pm list packages -f | grep [ダウンロード対象のキーワード]

ダウンロード対象のキーワードは会社名やアプリの名前を入力すれば大抵引っかかる。

# 例
$ adb shell pm list packages -f | grep yamachita0109
package:/data/app/com.yamachita0109.voice-Q6HZbU2pgLu4T3FCQg74kg==/base.apk=com.yamachita0109.voice

標準出力された値を分解する。

APK保存先パス /data/app/com.yamachita0109.voice-Q6HZbU2pgLu4T3FCQg74kg==/
APKファイル名 base.apk
アプリケーションID com.yamachita0109.voice

ダウンロード。

# APK保存先パスを指定
$ adb pull [APK保存先パス]
# 例
$ adb pull /data/app/com.yamachita0109.voice-Q6HZbU2pgLu4T3FCQg74kg==/
/data/app/com.yamachita0109.voice-Q6HZbU2pgLu4T3FCQg74kg==/: 1 file pulled. 31.4 MB/s (9086342 bytes in 0.276s)

# APKファイルがダウンロードされている
$ ls com.yamachita0109.voice-Q6HZbU2pgLu4T3FCQg74kg\=\=/
base.apk    lib

APKファイルをデコンパイルする

jadxを使用する。

git clone https://github.com/skylot/jadx.git
cd jadx
./gradlew dist

JDK 8 or higher must be installed:

なので注意すること。

やっとデコンパイル。

# Pathを通してもOK
$ build/jadx/bin/jadx --log-level error [APKファイルパス]

APKファイル名でフォルダが作成される。
その中に、ソースコード類が入っているので、盗み見参考にすることができる。

まとめ

「甘い!! 武術の伝承とはすなわち模倣から始まるのだよ!」

史上最強の弟子ケンイチの登場人物、秋雨の名言である。

「すべての独創は模倣から始まる」

ジャパネットたかたの創業者、髙田明の名言である。

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

配列からforEachで標準出力する方法

配列からforEachで標準出力する方法。

配列aの中身を変数iで受け取りaddメソッドで配列要素を与えforEachで順番に標準出力する。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        String[] a = {"青", "村", "賢"}; 

        ArrayList<String> numbersList = new ArrayList<>();

        for(String i : a) {
            numbersList.add(i);
        }

        numbersList.stream().forEach((i) -> { System.out.print(i); });

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

不変オブジェクトって何?【作り方も解説】

不変オブジェクトは安全~とか、できるだけ不変オブジェクトを作るようにしろ~とか、聞いたことはあるけどそれってなんなの?って人多いと思います

ここでは不変オブジェクトとは何なのか、そして不変オブジェクトはどう作るのかについて説明していこうと思います
不変オブジェクトは言語関係なく作れるのですが、Javaで説明していこうかなと思います

ここでは初心者向けに解説していますが、より詳細な説明が読みたい方は是非下記の書籍を買ってみてください
Effective Java 第3版

不変オブジェクトとは

ちょーかんたんに言うと、持っているデータが変わらないオブジェクトのことです

意味が分からないと思うので、JavaのStringとStringBuilderを使って説明していきます
まずは下記のコードを見てください

String str = "A";

for(int i = 0; i < 2 ; i++){
  str += "A";
}

System.out.println(str);

出力結果は下記のようになります

AAA

このコードは単純にstrにAを2回足しています
上記のコードではstrをString型で宣言していますが、StringBuilder型でstrを宣言しても出力結果は変わりません

しかし、StringとStringBuilderではインスタンス生成のやり方が違います
最終的にどのようなインスタンスが残るのか、見てみましょう

StringBuilderの場合
StringBuilder.png
単純に出力結果と同じインスタンスしか生成されていないため、最終的に残るインスタンスもAAAのひとつだけです

Stringの場合
String.png
Stringの場合はAが足される度に別のインスタンスを生成しています
そのため、A、AA、AAAのインスタンスが残ります
A、AAのインスタンスは参照が外れているため、GCの対象となっています

StringとStringBuilderの違い

上記の例を見てもらえるとわかると思いますが、StringBuilderはひとつのインスタンスの値が書き換わっているのに対して、Stringは値を書き換えずに別のインスタンスを生成しています

StringBuilderはインスタンスを1回しか生成しないため、負荷が低くて済みます
Stringはインスタンスを何回も生成しているため、その分負荷が高くなります

これだけ見るとStringなんて使う意味ない!と感じるかもしれませんが、それは間違いです

不変オブジェクトと可変オブジェクト

StringBuilderは値を書き換えているため可変オブジェクトと呼ばれます
Stringは値を書き換えていないため不変オブジェクトと呼ばれます

StringBuilderの長所は前述している通り、パフォーマンスの改善に繋がります
今回の例ではAを2回しか結合していないですが、これが10000回とかになるとインスタンスの生成コストを馬鹿にできなくなってきます

しかし、同じインスタンスなのにタイミングによってAだったりAAだったりAAAAAAAだったり、値が書き換わってしまいます
これが大問題で、変更を一回加えるとそのオブジェクトを参照しているオブジェクトにも影響が出てしまいます
可変オブジェクト.png

つまり、可変オブジェクトを使うと変更が加わる度に何かしらの副作用が発生します

Stringの場合はどうなるでしょうか
インスタンスを複数生成するためコストは掛かりますが、ひとつのオブジェクトは常に同じ値を保持し続けます
別の値を持ったインスタンスが欲しくなった場合は、別のインスタンスを生成します
そのため、他のオブジェクトに影響を出さずに済みます
不変オブジェクト.png
つまり、不変オブジェクトを使うとその値が変更されないことを保証することができます

不変オブジェクトはマルチスレッドプログラミングにも役立ちます
不変オブジェクトは値が変わらないため、synchronizedで同期したり…ということをやらなくて済みます
マルチスレッド環境で問題になるのは可変オブジェクトだからです

不変オブジェクトは、本質的にスレッドセーフになります

もし可変オブジェクトを作るにしても、できる限り可変な部分を減らすことによって事故を未然に防ぐことに役立ちます

不変オブジェクトの作り方

ここまでの説明で不変オブジェクトがどれだけ有用か分かって頂けたと思います
ここからは、実際にどのように不変オブジェクトを作るのかを説明していこうと思います

クラスを継承できないようにする

クラスを継承できるようにしてしまうと、継承されて変なメソッドを追加されたりするなど、カプセル化が破られてしまいます
型が同じなのに生成のされ方によって不変オブジェクトだったり可変オブジェクトだったりする…というような問題を起こしたくないため、継承できないようにclassを宣言します

final class TestClass{}

カプセル化されているモジュールのクラスであれば外部から見えないのでfinalを付けなくても実質的に継承は不可能ですが、継承されることを意図していない場合にはfinalを付けて継承を禁止することが一般的です(これは可変オブジェクトにも同じことが言えます)

すべてのフィールドを定数にする

不変オブジェクトは値を変更されたく無いので、すべて定数にします
また、直接変数を参照されないように可視性を設定します

private final int num;
private final StringBuilder str;

基本データ型、参照データ型に関わらず同じように宣言してしまって問題ないです
参照データ型の場合は少し扱いを変える必要があるのですが、後述します

ミューテーターを作らない

ミューテーター(mutator)とは、セッターをはじめとする値を書き換える操作のことです
値は書き換えたくないので、勿論そのような操作をするメソッドは必要ありません

ゲッターに気を付ける

まずは下記コードを見てください

public int getNum(){
  return num;
}

public StringBuilder getStr(){
  return str;
}

getNumメソッドは戻り値が基本データ型になっており、getStrメソッドは戻り値が参照データ型になっています
基本データ型の場合、numの値のコピーが返されますので特に問題はありません
しかし参照データ型の場合、参照をそのまま返してしまうため問題が発生します

getStrメソッドを呼び出した側でインスタンスの参照を取得できてまうため、strインスタンスの中身の値を書き換えることが出来てしまいます

それを防ぐため、先ほどのコードを改良してみました

public String getStr(){
  String result = str.toString;
  return result;
}

StringBuilderクラスにはtoStringメソッドという可変オブジェクトを不変オブジェクトに変えるメソッドがあったので使ってみました
しかし、「ゲッターに気を付ける」の説明でこの型変換の部分はあまり重要ではありません
重要なのは、値をコピーして返しているということです

別のコピーインスタンスを生成しておき、コピーインスタンスの参照を返します
これをすることによってgetStrメソッドを呼び出した側でいかなる手段を使っても、コピーインスタンスをいじることはできますが、元のインスタンスに干渉することはできなくなります

この技法を防御的コピーと呼びます
防御的コピーを使うことによって、StringBuilderのような可変オブジェクトを扱っているクラスでも、クラスを不変オブジェクトとすることができます

防御的コピーを使うときの注意点があります
①パフォーマンスが劣化する可能性があります
 配列やListなどの場合、中身を全てコピーしなければならないためパフォーマンスが劣化する可能性があります
 しかし、不変オブジェクトにはその代償を払うだけの価値があることを忘れないでください
②cloneメソッドを使わないでください
 javaのcloneメソッドはいくつかの問題があるため、防御的コピーをする時利用しないでください

サンプルコード

ここまでで説明した不変オブジェクトのサンプルコードを置いておきます
(コンストラクター部分が追記されています)

final class TestClass{

  private final int num;
  private final StringBuilder str;

  public TestClass(int num,StringBuilder str){
    this.num = num;
    //ここも防御的コピーを行う
    this.str = new StringBuilder(str);
  }

  public int getNum(){
    return num;
  }

  public String getStr(){
    String result = str.toString;
    return result;
  }

}

※2020/7/1追記 (@saka1029 さん、指摘ありがとうございます)
コンストラクターで可変オブジェクトを引数として受け取る時にも防御的コピーを行う必要があります
これを行わなかった場合、呼び出し元とTestClassで同じ参照先を持つことになってしまい、値を書き換えることができてしまいます
可変オブジェクトを受け取る時も、渡す時も、防御的コピーを使ってください

おまけ

上記のサンプルコードではコンストラクターをpublicにしていますが、Effective Javaではstatic factoryが推奨されています
これは現在のAPI開発にも言えます
先ほどのサンプルコードを少し書き換えてみます

final class TestClass{

  private final int num;
  private final StringBuilder str;

  //外部からコンストラクターの使用を禁止している
  //呼び出し側で下記のコードが使えなくなる
  // TestClass tc = new TestClass(10,"AAA");
  private TestClass(int num,StringBuilder str){
    this.num = num;
    this.str = new StringBuilder(str);
  }


  //static factory
  public static final TestClass newInstance(int num,StringBuilder str){
    return new TestClass(num,str);
  }

  public int getNum(){
    return num;
  }

  public String getStr(){
    String result = str.toString;
    return result;
  }

}

呼び出し側のコードは下記のようになります

TestClass tc = TestClass.newInstance(10,"AAA");

上記のコードの場合、static factoryであるnewInstanceメソッドが呼ばれる度に新しいTestClass型のインスタンスが生成されるため、シングルトンにはなりません
が、シングルトンにしたくない場合にはこの技法は使えます

シングルトンにしたい場合にはstatic factoryメソッド内でnewを使わず、予めインスタンスを作っておきreturnするだけにします

DIコンテナの登場でstatic factoryを直接呼び出す機会は減っているかもしれませんが、この技法は未だに現役です

まとめ

不変オブジェクトとは何なのか、どうやって作るのか、というのを紹介してみました

どんなシステムでも不変オブジェクトが必要無いシステムは存在しないと言っても過言ではないため、是非覚えて使ってみてください

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

StringBurrerとArrays.toStringの使い方。

StringBufferとArrays.toStringの使い方。

StringBufferに数字を入れてからカンマ区切りにして,その数字を足して表示し,その過程で得た数字を配列の表示として出力するプログラム。(特に意味はない)

import java.util.*;

public class Main {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < 10; i++) {
            sb.append(i+1).append(",");
        }
        String s = sb.toString();
        String[] a = s.split(",");
        int sum = 0;
        for(String i : a){
            if(Integer.parseInt(i) != a.length) {
                System.out.print(i + " + ");
            }
            else {
                System.out.print(i);
            }
            sum += Integer.parseInt(i);
        }
        System.out.println(" = " + sum);
        System.out.println(Arrays.toString(a));

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

(学習メモ)Java 2級対策:出題範囲

自己学習用メモ
TBE

出題範囲

※3級出題範囲含む
※Java SE8で出題・解答する。

演算子

演算子というのは算数や数学で使う、+(プラス)や-(マイナス)、=(イコール)、などの記号のこと

①ビット演算子(&、^、|、~)
②シフト演算子(<<、>>、>>>)

対象の値の各ビットを右または左へシフト

③代入演算子(&=、^=、|=、<<=、>>=、>>>=)

変数に数字や文字を代入する時に使う演算子

④instanceof演算子

あるオブジェクトの型を動的に判定するための演算子
・オブジェクトが、あるクラスのインスタンスか
・オブジェクトが、あるクラスの子クラスのインスタンスか
・オブジェクトが、特定のインターフェースを実装したインスタンスか

⑤ダイヤモンド演算子
例えば
List a = new ArrayList();を
List a = new ArrayList<>();
のように省略して書ける

制御文

Java 8で追加された
ラムダ式でプログラムを記述することで、コード量を減・読みやすくなる

クラスと継承

クラスの修飾子 (省略時)、public、final、abstract
継承とは extends
型変換(キャスト)
thisとsuper
インタフェース implements

変数とメソッド

変数とメソッドの修飾子 (省略時)、public、protected、private、final、static

オーバーライドとは

親クラスで定義されているメソッドを、子クラスで再度定義すること
オーバーライドは継承関係をもつ2つ以上のクラスで発生

オーバーロードとは

同一クラスの中で同じ名前のメソッドを定義すること
名前は同じですが、引数の数、引数の型、並び順は別のものを定義

総称

Java SE6まで :「総称」
Java SE7以降:「ジェネリクス」

コレクションクラス

  • 「List」「Set」「Map」はインターフェースとして用意されている
  • 実際に使用する際には、これらのインターフェースを実装したクラスを使用 ・よく使われるのはListのArrayList ・List・・・インターフェース ・ArrayList・・・・Listを実装したクラスになります。 ・ArrayListのようにコレクション・フレームワークのインターフェースを実装したクラスのことをコレクションクラスという

パッケージ

パッケージとクラスパス
クラスパス
パッケージの定義 package
パッケージの利用 import

例外処理

例外の定義
例外処理 try~catch?finally
throw
throws

スレッド

スレッドとは
スレッドの作成

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

SpringBoot で Thymeleafを使って画面を描画する

目的

前回は、SpringBootの@RestControllerを用いて文字列をブラウザ上で表示する事が出来たので、
今回は、@Controllerを用いてHTMLファイルを表示させる方法を学ぼうと思います。

事前準備

SpringBootのプロジェクトが既にあるという事を前提として進めていきます。
プロジェクトの作成方法は、Spring Quickstart Guideこちらの記事を参考にしていただけたら幸いです。

1.Controllerを作ろう!

Spring Quickstart Guideが終わった時のソースコードは以下になっていると思います。

DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

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

    @GetMapping("/hello")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
        return String.format("Hello %s!", name);
    }
}

今回の実装に伴い、DemoApplication.javaファイル内の不要になる箇所を削除します。

DemoApplication.javaの完成形
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

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

}

DemoApplication.javaと同じ階層にcontrollerフォルダを作成します。
そしてそのフォルダの中にHelloController.javaを作成して処理を記述します。

HelloController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping("/hello")
    public String hello(Model model) {
        String message = "Hello World from HelloController!!!";
        model.addAttribute("msg", message);
        return "hello";
    }
}

HelloControllerクラスのアノテーションは、@Controllerになっています。
@Controllerとする事で、htmlによって書かれたテンプレートファイルを返してくれます。

return "〇〇"〇〇の部分がhtmlファイルの名前になるので、後でhello.htmlを作成しなければいけないという事になります。

Modelは、テンプレートで利用するためのデータを設定しています。
model.addAttribute("値の名前", 値)という書き方で、文字列が格納されているmessage変数をテンプレートに渡しています。

これでControllerは完成しました!

2.Thymeleafを使えるようにしよう!

MVCのView部分を作成していくのですが、今回はThymeleafを使用します。

Thymeleafとは、springbootで扱う事が出来るテンプレートエンジンです。
Controller側で変数に格納した値をHTMLファイルで表示する事が出来ます。
日本語で書かれたThymeleafチュートリアルもあります!

では、Thymeleafが使えるようにするために、pom.xmlファイルを編集します。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>demo</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>11</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
           <groupId>org.junit.vintage</groupId>
           <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <!-- ↓↓↓↓↓Thymeleafを使用する為に追加↓↓↓↓↓ -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>


  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

3.画面描画するためのHTMLファイルを作成しよう!

src/main/resources/templatesの中にhello.htmlを作成しましょう。

hello.htmlの中身は以下の通りです。
htmlタグにthymeleafの名前空間を定義してあげることを忘れずに!

hello.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">

<head>
  <meta charset="UTF-8">
  <title>Spring Boot Lesson</title>
</head>

<body>
  <h1>ThymeleafでHello World! </h1>

  <h1 th:text="${msg}"></h1>
</body>

</html>

2個目のh1タグで、th:text="${msg}"とすることでControllerから渡ってきた値を表示しています。

4.実行してみよう!

1~3で@ControllerでHTMLファイルを描画する、Thymeleafを用いてControllerで定義した値をHTMLファイルで描画するための準備をしてきました。
実行して確認しましょう。

ターミナルで以下のコマンドを入力してEnter。

ターミナル
$ ./mvnw spring-boot:run

2秒ぐらい待った後、http://localhost:8080/hello にアクセスすると、

スクリーンショット 2020-07-01 10.45.40.png

hello.htmlが描画され、Controllerで定義したmsgも正しく表示されています。

終わりに

@Controllerを用いてHTMLファイルを表示させる方法を学びました。
Thymeleafの記述方法はまだまだたくさんあるので、別の機会に記事にしたいと思います。

参考サイト

Spring Boot で『Thymeleaf』 テンプレートを使う
https://cloudear.jp/blog/?p=799

Tutorial: Using Thymeleaf (ja)
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html#thymeleaf%E3%81%AE%E7%B4%B9%E4%BB%8B

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

[Processing×Java] ループの使いかた

この記事はプログラムの構造をProcessingを通じて理解していくための記事です。
今回はループについて書いていきます。

gradation01.png

目次
0.何のためにループを使うか
1.forループについて
2.ループを使ったプログラムの例

0.何のためにループを使うか

反復的な処理をまとめるために使う。
下のプログラムは、1から10まで数の総和を効率的に求めるためのプログラム。

loop00.java
//変数 sum は求めたい総和
int sum = 0;

//1から10までiを1ずつ足していく。
for(int i = 1;i < 11;i++){
  //総和(新) = 総和(旧) + i
  sum = sum + i;
}

//総和をコンソールに表示する。
println(sum);
55
i sum sum + i
1 1 0 + 1
2 3 1 + 2
3 6 3 + 3
4 10 6 + 4
5 15 10 + 5
6 21 15 + 6
7 28 21 + 7
8 36 28 + 8
9 45 36 + 9
10 55 45 + 10

Point :
ループを使うためには、似たような反復動作の中から共通の部分(性質・法則)を見つけなければいけません!
つまりどの部分を反復させるかを見極める必要があります。

1.forループについて

forループ

forループはループをつくるための手段の1つです。
whileループとやることは変わらないですが、こちらの方がコンパクトでわかりやすいので、こちらを紹介していきます。

◯線を繰り返し描いていくプログラム

loop01.java
//画面の大きさを決定する。
size(500,250);
//線を20pxおきに配置する。
for(int i = 0;i < width;i += 20){
  line(i,0,i,height);
}

loop03.png

Point : i < widthの範囲内で、(i,0)から、(i,height)に引いた直線を20pxおきに描いていくプログラム。

forループの実行順番(このプログラムで例えると...)

iの値は何ですか? : 0です!

iの値(=0)は i < width を満たしていますか? : True!

処理の実行

i += 20 しときますね!

iの値は何ですか? : 20です!
:
:
iの値は何ですか? : 500です!

iの値(=500)は i < width を満たしていますか? : False!(ループ終了)

Point : i += 20 は、i = i + 20 を略して書いたものです。

○forループのイメージ

スクリーンショット 2020-06-30 20.40.02.png

Point : 300円におさまるように遠足のお菓子を選んでいくあの感じに近いかもしれないです...

2.forループを使ったプログラムの例

◯線を少しずつずらしていくプログラム

variable02.java
//画面の大きさを決める
size(500,500);
//背景の色を決める
background(0);

//i < widthを満たす間、iを10ずつ増やしていく。
for(int i = 0;i < width;i += 10){
  //線の色を決める
  stroke(53,183,193);
  //(0,i)から、(i,height)まで線を引く
  line(0,i,i,height);
}

loop03.png

Point : Processingの座標
スクリーンショット 2020-06-30 20.42.39.png
画面下にいくほど、yの値が大きくなるのが特徴。

◯ランダムに線を描いていくプログラム

variable03.java
//画面の大きさを決める
size(500,500);
//背景の色を決める
background(0);

//i < widthを満たす間、iを1ずつ増やしていく。(500回繰り返す)
for(int i = 0;i < width;i += 1){
  //線の色を決める
  stroke(#640915);
  //ランダムな地点からランダムな地点まで線を描く
  line(random(500),random(500),random(500),random(500));
}

loop10.png

Point : random(min,max);
minからmaxまでの範囲のランダムな値を返す(出力する)。
minは省略可能。

Point : iが0,1,2,....498,499になるまでの間、つまり500回ループを繰り返す

Point : 色は(R,G,B)のような選び方以外にも方法がある。
メニューバーの[ツール]→[色選択]→好きな色を選択

◯ループとアニメーション

アニメーションをつくるには、draw()を使うのが効果的です。

variable03.java
//円の大きさを表す変数sを自分で定義する
float s = 0;

//1回のみ繰り返す
void setup(){
  size(600,600);
  //デフォルトは毎秒60フレーム
  frameRate(10);
  background(0);
}

//無限ループ
void draw(){
  //サイズを1ずつ大きくしていく
  s += 1;
  //色の指定
  fill(random(255),0,255,50);
  noStroke();
  //ランダムな場所に円を配置していく
  ellipse(random(width),random(height),s,s);

  //もし、大きさが45より大きくなったら実行される
  if (s > 45) {
    //サイズを0に戻す
    s = 0;
  }

}

スクリーンショット 2020-07-02 8.36.00.png

最後に

読んでいただきありがとうございました。
より良い記事にしていくために御意見、ご指摘のほどよろしくお願いいたします。

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

SpringアプリケーションをCircleCIでCI/CDする(Heroku)

概要

初投稿です。
SpringアプリケーションをCircleCIを使ってCI/CDしてみたいと思います。デプロイ先にはHerokuを使用します。

環境

  • Java 11
  • Spring Boot 2.2.8

前提条件

  • GitHubのアカウントがあり、CLIからpushなどができる
  • Herokuのアカウントがあり、CLIからcreateなどができる

Spring プロジェクトの作成

まずSpring Initializrを使用してSpringアプリケーションを作成します。Springアプリケーションの雛形を作成してくれる便利なサイトです。

以下の画像のように設定をしてGENERATEをクリックします。DependenciesSpring Webを追加することを忘れないでください。
Spring Initializr

demo.zipというようなファイルがダウンロードされるので好きな場所に展開してください。

テスト作成

次にシンプルなControllerの実装と、テストを作成します。

まず、demo/src/main/java/com/example/demo/controller/HelloController.javaを作成します。

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping(path = "/hello")
    public String hello() {
        return "hello";
    }

}

とてもシンプルな、/helloにアクセスすると"hello"と返してくれるだけのControllerです。

次に、demo/src/test/java/com/example/demo/controller/HelloControllerTest.javaを作成します。

package com.example.demo.controller;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class HelloControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    WebApplicationContext webApplicationContext;

    @BeforeEach
    void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    void helloページにアクセスしたらhelloが返ること() throws Exception {
        final var mvcResult = this.mockMvc.perform(get("/hello"))
                .andDo(print()).andExpect(status().isOk()).andReturn();
        String content = mvcResult.getResponse().getContentAsString();
        assertEquals(content, "hello");
    }
}

先ほどのシンプルなControllerをMockMvcというライブラリを使用してテストしています。環境が整っている方はここでテストを実行するとGREENになるはずです。

CIできるようにする

続いて、先ほど作成したテストをGitHubにPushするたびにCircleCIで自動で走るようにしていきます。.circleci/config.ymlを作成します。

version: 2.1

workflows:
  java-ci:
    jobs:
      - test

jobs:
  test:
    docker:
      - image: circleci/openjdk:11-jdk

    working_directory: ~/repo
    environment:
      JVM_OPTS: -Xmx3200m
      TERM: dumb

    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "build.gradle" }}
            - v1-dependencies-
      - run: gradle dependencies
      - save_cache:
          paths:
            - ~/.gradle
          key: v1-dependencies-{{ checksum "build.gradle" }}
      - run: ./gradlew test

circleci/openjdk:11-jdkのimageを使用して./gradlew testを走らせるようにしています。

GitHub上にRepositoryを作成して、ここまでの変更をPushします。

続いて、CircleCIのLoginページからLog In With GitHubを選択してLoginします。GitHubのアカウント情報を入力した後にSelect an organizationの画面が表示されたら、自分のGitHubアカウントを選択します。

Projectsのページが表示されたら、今回作成したRepositoryのSet Up Projectを選択します。以下のような画面が表示されたら、今回はすでに.circleci/config.ymlを作成してあるので、Add Manuallyを選択します。
Add Manually

これでPipelinesのページに遷移し、テストが実行されればCIは完了です。

CircleCIのビルドの始め方の詳しい情報は公式ページも参考にしてみてください。

CDできるようにする

最後に、アプリケーションを継続的にデプロイし続けられるようにしていきます。.circleci/config.ymlを以下のようにします。circleci/herokuというOrbsを使用してHerokuにデプロイできるようにします。

version: 2.1

orbs:
  heroku: circleci/heroku@1.0.1

workflows:
  java-ci-cd:
    jobs:
      - test
      - start_deploy:
          type: approval
          requires:
            - test
      - heroku/deploy-via-git:
          requires:
            - start_deploy

jobs:
  test:
    docker:
      - image: circleci/openjdk:11-jdk

    working_directory: ~/repo
    environment:
      JVM_OPTS: -Xmx3200m
      TERM: dumb

    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "build.gradle" }}
            - v1-dependencies-
      - run: gradle dependencies
      - save_cache:
          paths:
            - ~/.gradle
          key: v1-dependencies-{{ checksum "build.gradle" }}
      - run: ./gradlew test
      - 

次にHeroku側の作成を行います。今回作成しているSpringアプリケーションのルートディレクトリでHerokuのAppsを作成します。

$ heroku create

createの後に任意の文字列を渡してお好きなドメインにしても構いません。

これによって作成されたhttps://xxx.herokuapp.com/xxxの部分をメモしておきます。
また、HerokuのAccountページの真ん中辺りにあるAPI Keyの値もメモしておきます。

この2つの値を、CircleCIのEnvironment Variablesに設定します。
Pipelinesのページから、Project SettingsEnvironment Variablesです。以下の画像のように、ドメインの1部分をHEROKU_APP_NAMEとして、API Keyの値をHEROKU_API_KEYとして追加します。
add env

add env

最後に、Heroku用の設定ファイルを追加します。

ルートディレクトリにProcfileを追加します。

web: java -jar build/libs/demo-0.0.1.jar --server.port=$PORT

さらに、ルートディレクトリにsystem.propertiesを追加します。

java.runtime.version=11

これでここまでの差分をGitHubにPushします。

するとPipelinesのページでtest成功後にJobが止まるので、On_holdを選択してApprove Jobをクリックするとデプロイが走ります。
onhold

最終的にheroku/deploy-via-gitまでSuccessになればCDは完了です。

こちらに今回作成したコードがありますので良ければ参考にしてみてください。

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