- 投稿日:2020-07-01T23:18:42+09:00
【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」∠)_
- 投稿日:2020-07-01T20:25:39+09:00
未経験エンジニア奮闘記 JavaSilver合格目指して勉強中詰まったところString型編
はじめに
これは大学中退後8年間フリーターだったアラサーが一念発起してエンジニアに転職できたはいいものの、右も左もわからなすぎるのでJavaSilverを取得しようと頑張ったけどここで詰まったよってお話です。
まず私にはほとんどJavaの知識はありません。それどころかプログラミング全般大した知識が無いと言っていいです。
学生時代に趣味と授業でC言語はいじったなぁくらいで、最近はPython3をWeb教材で勉強したぞ! ってくらいのドがつく素人です。で、何故Javaの資格かと言われると単純に需要が高そうだから。それだけです。
オブジェクト指向ってやつをするのに適してるらしいじゃん? くらいの印象しかありません。Java Silver合格まで勉強中詰まったところをほぼ自分用に書いていこうと思います。
間違い等ありましたら是非私の未来の為に教えてください。String型の不思議
とりあえずJavaの入門書を買って勉強していると、さっそく面白い内容にぶちあたりました。
なんと、String型は参照型であるため例えばStrTest.javapublic class StrTest { public static void main(String args[]) { String str1 = "hoge"; String str2 = "ho"; str2 += "ge"; System.out.println(str1 == str2); } }は
false
になるから文字列比較する時はStrTest.javapublic class StrTest { public static void main(String args[]) { String str1 = "hoge"; String str2 = "ho"; str2 += "ge"; System.out.println(str1.equals(str2)); } }みたいに
equals()
を使用するんだぞとのこと。さっそく大興奮の私は試しにこんなコードを書きました。
StrTest.javapublic 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.javapublic 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であるようです。
さっき書いたコードはどっかのメソッドで新たな文字列が勝手に爆誕しているだけだったってことですね。
- 投稿日:2020-07-01T19:04:32+09:00
【Java】Public static void main(String[] args)ってなんぞ?
javaやっている方は必ず目にするpublic static void main(String[] args)。
実行するには必ず必要なメソッドとなります。
でも、これ、どれか1つでも間違えるとエラーになります。どうしてなんでしょうね?
気になったので、今後の同じ初心者の方向けにご紹介しておきます。
■意味をまず理解しよう
Hello.javaclass 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 修飾子が付いてる必要があるということです。
とりあえず実行するにはこれが必要
- メソッド名が main
- 引数が String 型の配列(可変長引数)
- static 修飾子が付与されている
この条件を満たしてないクラスを java コマンド実行しようとすると以下のように失敗します。
エラー: メイン・メソッドがクラスHelloWorldで見つかりません。次のようにメイン・メソッドを定義してください。 public static void main(String[] args)また、戻り値をvoid以外にした場合も以下のエラーがでます。
エラー: メイン・メソッドはクラスHelloWorldのvoid型の値を返す必要があります。 次のようにメイン・メソッドを定義してください。 public static void main(String[] args)
- 投稿日:2020-07-01T18:37:16+09:00
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.ktval 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.ktdata 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リファレンス
- 投稿日:2020-07-01T17:17:49+09:00
[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 libAPKファイルをデコンパイルする
jadxを使用する。
git clone https://github.com/skylot/jadx.git cd jadx ./gradlew distJDK 8 or higher must be installed:
なので注意すること。
やっとデコンパイル。
# Pathを通してもOK $ build/jadx/bin/jadx --log-level error [APKファイルパス]APKファイル名でフォルダが作成される。
その中に、ソースコード類が入っているので、盗み見参考にすることができる。まとめ
「甘い!! 武術の伝承とはすなわち模倣から始まるのだよ!」
史上最強の弟子ケンイチの登場人物、秋雨の名言である。
「すべての独創は模倣から始まる」
ジャパネットたかたの創業者、髙田明の名言である。
- 投稿日:2020-07-01T15:34:34+09:00
配列から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); }); } }
- 投稿日:2020-07-01T15:24:46+09:00
不変オブジェクトって何?【作り方も解説】
不変オブジェクトは安全~とか、できるだけ不変オブジェクトを作るようにしろ~とか、聞いたことはあるけどそれってなんなの?って人多いと思います
ここでは不変オブジェクトとは何なのか、そして不変オブジェクトはどう作るのかについて説明していこうと思います
不変オブジェクトは言語関係なく作れるのですが、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の場合
単純に出力結果と同じインスタンスしか生成されていないため、最終的に残るインスタンスもAAAのひとつだけですStringの場合
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だったり、値が書き換わってしまいます
これが大問題で、変更を一回加えるとそのオブジェクトを参照しているオブジェクトにも影響が出てしまいます
つまり、可変オブジェクトを使うと変更が加わる度に何かしらの副作用が発生します
Stringの場合はどうなるでしょうか
インスタンスを複数生成するためコストは掛かりますが、ひとつのオブジェクトは常に同じ値を保持し続けます
別の値を持ったインスタンスが欲しくなった場合は、別のインスタンスを生成します
そのため、他のオブジェクトに影響を出さずに済みます
つまり、不変オブジェクトを使うとその値が変更されないことを保証することができます不変オブジェクトはマルチスレッドプログラミングにも役立ちます
不変オブジェクトは値が変わらないため、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を直接呼び出す機会は減っているかもしれませんが、この技法は未だに現役です
まとめ
不変オブジェクトとは何なのか、どうやって作るのか、というのを紹介してみました
どんなシステムでも不変オブジェクトが必要無いシステムは存在しないと言っても過言ではないため、是非覚えて使ってみてください
- 投稿日:2020-07-01T15:21:12+09:00
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)); } }
- 投稿日:2020-07-01T14:18:59+09:00
(学習メモ)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スレッド
スレッドとは
スレッドの作成
- 投稿日:2020-07-01T11:14:41+09:00
SpringBoot で Thymeleafを使って画面を描画する
目的
前回は、SpringBootの@RestControllerを用いて文字列をブラウザ上で表示する事が出来たので、
今回は、@Controllerを用いてHTMLファイルを表示させる方法を学ぼうと思います。事前準備
SpringBootのプロジェクトが既にあるという事を前提として進めていきます。
プロジェクトの作成方法は、Spring Quickstart Guideやこちらの記事を参考にしていただけたら幸いです。1.Controllerを作ろう!
Spring Quickstart Guideが終わった時のソースコードは以下になっていると思います。
DemoApplication.javapackage 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.javapackage 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:run2秒ぐらい待った後、http://localhost:8080/hello にアクセスすると、
hello.htmlが描画され、Controllerで定義したmsgも正しく表示されています。
終わりに
@Controllerを用いてHTMLファイルを表示させる方法を学びました。
Thymeleafの記述方法はまだまだたくさんあるので、別の機会に記事にしたいと思います。参考サイト
Spring Boot で『Thymeleaf』 テンプレートを使う
https://cloudear.jp/blog/?p=799Tutorial: Using Thymeleaf (ja)
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html#thymeleaf%E3%81%AE%E7%B4%B9%E4%BB%8B
- 投稿日:2020-07-01T11:09:22+09:00
[Processing×Java] ループの使いかた
この記事はプログラムの構造をProcessingを通じて理解していくための記事です。
今回はループについて書いていきます。目次
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); }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ループのイメージ
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); }Point : Processingの座標
画面下にいくほど、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)); }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-01T08:11:23+09:00
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
をクリックします。Dependencies
にSpring Web
を追加することを忘れないでください。
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
を選択します。
これで
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 createcreateの後に任意の文字列を渡してお好きなドメインにしても構いません。
これによって作成された
https://xxx.herokuapp.com/
のxxx
の部分をメモしておきます。
また、HerokuのAccountページの真ん中辺りにあるAPI Key
の値もメモしておきます。この2つの値を、CircleCIの
Environment Variables
に設定します。
Pipelines
のページから、Project Settings
→Environment Variables
です。以下の画像のように、ドメインの1部分をHEROKU_APP_NAME
として、API Keyの値をHEROKU_API_KEY
として追加します。
最後に、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
をクリックするとデプロイが走ります。
最終的に
heroku/deploy-via-git
までSuccessになればCDは完了です。こちらに今回作成したコードがありますので良ければ参考にしてみてください。