- 投稿日:2021-08-15T23:53:53+09:00
VSCodeにSpringBootの環境を構築しよう!!
環境 windows JDK11 Maven3.8 step1:必要な拡張機能のインストールをしよう 下記をインストールしよう! step2:VSCodeにJavaとMavenの設定をしよう vscodeの設定を開き、Java.homeとJava.configuration.runtimesとmaven.terminal.favoritesを設定しよう。 下記のサイトが詳しくJava環境を構築するため、詳しく乗っています。 (vscode公式Java-tutorial) https://code.visualstudio.com/docs/java/java-tutorial step3:Spring Bootのプロジェクトを作成しよう 1.ctl + shift + pを押して、Spring Initializr: Create a Maven Projectを選択しよう。 2. spring bootのversionを選択しよう。 3. Javaを選択しよう。 4. MavenのGroupIdを設定しよう。(そのままで大丈夫です。) 5. MavenのArtifactIdの設定をしよう。(そのままで大丈夫です。) 6. MavenのPackageコマンドの際にまとめる拡張子を決めよう。(jarで大丈夫です。) 7. Javaのversionを設定しよう(自分は、11でやりました。) 8. 赤枠の依存関係をMavenに登録しよう。(選択すれば、自動的に登録をしてくれます。) 9. この画面になっていたら大丈夫です。(SPRING BOOT DASHBOARDの表示には時間がかかることもあります。) 10. Spring BOOT DASHBOARDの右矢印を押したら、実行及びデバックができます。 簡単なSampleを実行してみよう src/main/java/example/demoの下に、controllerフォルダーを作成してみましょう。 その下に、HelloController.javaを作成しよう。 下記のようにコードを書きましょう。 下記をクリックしよう。 http://localhost:8080/hello Hello Spring Bootと表示されていればOKです。 参考書 Javaビルドツール入門 Maven/Gradle/SBT/Bazel対応 (掌田津耶乃) https://wings.msn.to/index.php/-/A-03/WGS-JVF-001/ (速習Spring Boot 山田祥寛)
- 投稿日:2021-08-15T22:37:19+09:00
Javaについて、基本だけど意外と知らないこと(Java Silver 「データ型の操作」)
本年度中に、Java Silver及びJava Goldの資格取得を目指しています。 現在エンジニア3年目にも拘らず、今までの経験言語がJavaだったりC#だったりしてふらふらしていたので、ここらで一発、言語について基本的な知識を持っておこうと思ったのがきっかけです。 2年目までは言語知識を深く知らなくても、先輩の作った基盤の上でなんとなくプログラミングできていたように思えます。そのせいか、恥ずかしながら3年目になって(試験勉強を始めて)Javaの理解が進んだ点がいくつかあるので、備忘程度に記載します。 出典 Java SE 8 Silver試験「Javaのデータ型の操作」単元より インスタンスとガベージコレクション Object a = new Object(); //【I】Objectインスタンスを生成し、Object型の変数aの参照先とする Object b = new Object(); //【Ⅱ】Objectインスタンスを生成し、Object型の変数bの参照先とする Object c = a; //【Ⅲ】Object型の変数cの参照先を、変数aの参照先インスタンスとする a = null; //【Ⅳ】変数aが何もインスタンスを参照しないよう設定 b = null; //【Ⅴ】変数bが何もインスタンスを参照しないよう設定 図示してみると 1,2行目でObject型変数a,bに対して、それぞれの参照先となるObjectインスタンスが生成される。↓ 3行目で変数cが生成される。参照先はaが参照しているインスタンスと同じ。↓ 4,5行目で変数a,bがどのインスタンスも参照しないよう設定(=null)されている。もともとaが参照していたインスタンスは引き続き変数cに参照されるのに対し、bが参照していたインスタンスは参照元を失う。bが参照していたインスタンスは今後使用予定がないので、JVMのガベージコレクションの対象となる。↓ ガベージコレクションについては JVM・ガベージコレクションに関してまとめてみた JavaのGCの仕組みを整理する ○○進数を指定したint型の初期化 int a = 111; //よく使うやつ int a = 0x10B; //冒頭に「0x」を付けると12進数 10進数では1*12*12+12*0+1*11 = 155 int b = 0b100001011; //冒頭に「0b」を付けると2進数 10進数では267 int c = 0727; //冒頭に「0」を付けると8進数 10進数では7*8*8+2*8+7*1 471 整数リテラルの書き方 ※リテラル=「代入するときの等号の右側の値」 int a = 123_456_789; //OK 間にアンスコ書いてもOK 値としては123456789 int b = _123_456; //NG アンスコから始めるのはNG コンパイルエラー int c = 123_456_; //NG アンスコで終わるのもNG コンパイルエラー int d = 123_.45; //NG アンスコの横に記号が来るとNG コンパイルエラー long e = 123.45_L; //NGアンスコの横に記号が来るとNG コンパイルエラー float f = 123.4_5F; //OK アンスコの横に記号泣ければOK 値としては123.45(float) 参考 => 【Java】数値リテラル内のアンダースコア char(文字型)に入ってもいい値 char a = 'a'; //OK シングルクウォーテーションで囲むのが習わし char b = '\u30A2'; //OK Unicode値を代入できる char b = 89; //OK Unicode値の10進数値を代入できる char c = null; //NG char(というかプリミティブ型変数)はnullを持てない。初期値は'\u0000' 識別子の命名規則 識別子...変数やメソッド、クラスなどの名前のこと。 int $a = 100; //OK $は変数名に使える int _b_ = 100; //OK _は変数名に使える int ${a} = 100; //NG 変数名に使える記号は$と_のみ コンパイルエラー int g.a = 100; //NG 変数名に使える記号は$と_のみ コンパイルエラー
- 投稿日:2021-08-15T18:00:36+09:00
Velocityで自動HTMLエスケープする
問題 WEBアプリで、velocityを使用してHTMLテンプレートを記述する場合、EscapeToolを使用してエスケープ処理を行うのが普通だが、項目出力の都度エスケープ処理を記載しなければならず、記載忘れでクロスサイトスクリプティングなど重大な脆弱性を埋め込むおそれがある。 解決方針 デフォルトでHTMLエスケープを行うようにして、コーディング時の負担を軽減する 課題 全ての項目をエスケープすれば良いわけではない(出力項目の中にはHTMLタグを埋め込んでやる必要があることもある)。 ココに記載のあるように、VelocityプロジェクトでHTMLエスケープを自動で実施する仕組みを作っていたこともあるが、一律同じエスケープ処理を行うことは「実用的でない」としてdeprecatedになっている。 →一律エスケープするのではなく、デフォルトでエスケープはするが、エスケープしない場所を指定できるようにすれば良いのでは、と思った。 実装案 ココを参考にして、ある変数が定義されている場合のみエスケープを行う拡張を実装する。 エスケープをするかどうか判断する変数の中身を入れ替えるマクロを定義する テンプレート記述時、エスケープをしたくない箇所のみ、マクロを使用してエスケープ対象から除外する。 →デフォルトでエスケープ処理し、セキュリティを担保しつつもエスケープしてはいけない箇所をコーディング時に切り分けることができる コード例 springboot1.2系での使用を想定 実務がこのバージョンだから。。。 エスケープ処理 ReferenceInsertionEventHandler.java package com.example.demo; import org.apache.commons.lang.StringEscapeUtils; import org.apache.velocity.app.event.implement.EscapeReference; import org.apache.velocity.context.Context; import org.apache.velocity.util.ContextAware; public class ReferenceInsertionEventHandler extends EscapeReference implements ContextAware { private final String ATTR_KEY = "__escape_html__"; private final String ATTR_VALUE = "__raw__"; private Context ctx; protected String escape(Object o) { if(ctx.containsKey(ATTR_KEY) && ATTR_VALUE.equals(ctx.get(ATTR_KEY))){ //エスケープ除外用の変数が定義されていたらそのまま出力 return o.toString(); } else { //エスケープ除外用の変数が定義されていなかったらエスケープして出力 return StringEscapeUtils.escapeHtml(o.toString()); } } protected String getMatchAttribute() { return "eventhandler.escape.html.match"; } public void setContext(Context context) { this.ctx = context; } } マクロ escape.vm #macro(raw) #set( $__escape_html__ = "__raw__" ) #end #macro(endraw) #set( $__escape_html__ = "__" ) #end テンプレート #parse("escape.vm") エスケープされる:<br /> ${attributeA}<br /> ${attributeB}<br /> #raw() エスケープされない:<br /> ${attributeA}<br /> ${attributeB}<br /> #endraw() エスケープされる:<br /> ${attributeA}<br /> ${attributeB}<br /> イケてないところ javascriptのエスケープは? 変数名かぶるかも 解決策 ThymeleafやFreeMarkerに移行する プロジェクト開始時からきちんと考えて準備しておく →最初からしっかり決めておけば苦労は少ないけど、後から全部修正するのは大変な苦痛を伴う
- 投稿日:2021-08-15T17:50:14+09:00
Lombok徹底解剖
Javaにおいて、フィールドへのアクセサ等の用意を、アノテーション付与によって肩代わりしてくれる便利なライブラリ、Lombok。 主なアノテーションについてまとめる。 Getter/Setter get○○、set○○メソッドを自動で用意してくれる。 プログラマが書くソース public class User { @Getter private userId; @Setter private userName; } コンパイルするとこうなる public class User { private userId; private userName; public String getUserId() { return this.userId; } public String setUserName(final String userName) { this.userName = userName; } } EqualsAndHashCode クラス変数を利用したequals,hashCodeメソッドを追加 プログラマが書くソース @EqualsAndHashCode public class User { private String userId; private String userName; } コンパイルするとこうなる public class User { //デフォルトコンストラクタは省略 //フィールドの値まで含めて、オブジェクトが一致しているかどうかを判定 public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof User)) { return false; } else { //ダウンキャスト User other = (User)o; if (!other.canEqual(this)) { return false; } else { Object this$userId = this.userId; Object other$userId = other.userId; if (this$userId == null) { if (other$userId != null) { return false; } } else if (!this$userId.equals(other$userId)) { return false; } Object this$userName = this.userName; Object other$userName = other.userName; if (this$userName == null) { if (other$userName != null) { return false; } } else if (!this$userName.equals(other$userName)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof User; } public int hashCode() { int PRIME = true; int result = 1; Object $userId = this.userId; int result = result * 59 + ($userId == null ? 43 : $userId.hashCode()); Object $userName = this.userName; result = result * 59 + ($userName == null ? 43 : $userName.hashCode()); return result; } } ToString そのまま。クラス及びメンバの情報を文字列として出力。 プログラマが書くソース @ToString public class User { private String userId; private String userName; } コンパイルするとこうなる public class User { private userId; private userName; public String toString() { return "User(userId=" + this.userId + ", userName=" + this.userName + ")"; } } AllArgsConstructor メンバすべてを引数とするコンストラクタを生成。 プログラマが書くソース @AllArgsConstructor public class User { private String userId; private String userName; } コンパイルするとこうなる public class User { private userId; private userName; public User(final String userId, final String userName) { this.userId = userId; this.userName = userName; } } なお、内部クラスを持たせると、その内部クラスはコンストラクタに含まれない。 内部クラスそのものに別途AllArgsConstructorを付与の上、インスタンスをセットする必要がある。 プログラマが書くソース(内部クラスあり) @AllArgsConstructor public class User { private String userId; private String userName; private class UserSub { private String subUserId; private String subUserName; } } コンパイルするとこうなる(内部クラスあり) //内部クラスはコンストラクタ変数にはならない public class User { private String userId; private String userName; public User(final String userId, final String userName) { this.userId = userId; this.userName = userName; } private class UserSub { private String subUserId; private String subUserName; private UserSub() { } } } NoArgsConstructor 引数を持たないコンストラクタを生成。 Javaのクラスはコンストラクタを明示的に書かない場合、lombok無しでも引数なしコンストラクタを生成するので、これっていつ必要なのかな... プログラマが書くソース @NoArgsConstructor public class User { private String userId; private String userName; } コンパイルするとこうなる public class User { private userId; private userName; public User() { } } RequiredArgsConstructor コンストラクタでの初期化必須(="final")なメンバを引数としたコンストラクタを生成。 プログラマが書くソース @RequiredArgsConstructor public class User { private String userId; private String userName; } コンパイルするとこうなる public class User { private final String userId; private String userName; public User(final String userId) { this.userId = userId; } } Data getter,setter,equals,hashCode,toStringをまとめて実装してくれる。 プログラマが書くソース @Data public class User { private String userId; private String userName; } コンパイルするとこうなる public class User { private String userId; private String userName; public User() { } public String getUserId() { return this.userId; } public String getUserName() { return this.userName; } public void setUserId(final String userId) { this.userId = userId; } public void setUserName(final String userName) { this.userName = userName; } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof User)) { return false; } else { User other = (User)o; if (!other.canEqual(this)) { return false; } else { Object this$userId = this.getUserId(); Object other$userId = other.getUserId(); if (this$userId == null) { if (other$userId != null) { return false; } } else if (!this$userId.equals(other$userId)) { return false; } Object this$userName = this.getUserName(); Object other$userName = other.getUserName(); if (this$userName == null) { if (other$userName != null) { return false; } } else if (!this$userName.equals(other$userName)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof User; } public int hashCode() { int PRIME = true; int result = 1; Object $userId = this.getUserId(); int result = result * 59 + ($userId == null ? 43 : $userId.hashCode()); Object $userName = this.getUserName(); result = result * 59 + ($userName == null ? 43 : $userName.hashCode()); return result; } public String toString() { return "User(userId=" + this.getUserId() + ", userName=" + this.getUserName() + ")"; } }
- 投稿日:2021-08-15T17:42:04+09:00
[Java] FastDateFormatでParseする
FastDateFormatとは FastDateFormat is a fast and thread-safe version of SimpleDateFormat. FastDateFormatは、 SimpleDateFormatの早くてスレッドセーフ版。 This class can be used as a direct replacement to * {@code SimpleDateFormat} in most formatting and parsing situations. * This class is especially useful in multi-threaded server environments. * {@code SimpleDateFormat} is not thread-safe in any JDK version, * nor will it be as Sun have closed the bug/RFE. 「ほとんどのケースのformatとparseにおいて、SimpleDateFormatの代替となる」と書かれている。 基本的な使い方 Apache Commons Langをhttps://mvnrepository.com/artifact/org.apache.commons/commons-lang3/3.12.0 から追加する (今回は3.12.0を使用) import java.text.ParseException; import java.util.Date; import org.apache.commons.lang3.time.FastDateFormat; public class FastDateFormatBasic { public static void main(String[] args) { FastDateFormat tsDatetimeFormat = FastDateFormat.getInstance("yyyy/MM/dd HH:mm:ss z"); try { Date parsedDate = tsDatetimeFormat.parse("2021/08/15 00:00:00 UTC"); System.out.println(parsedDate); } catch (ParseException e1) { } } } 実行結果: Sun Aug 15 09:00:00 JST 2021 (実行環境のTimezoneで表示される。) 内部実装 FastDateFormatのInstanceは以下のいずれかの方法で取得する。 今回は、1つ目のgetInstanceをとりあげて見る getInstance(String, TimeZone, Locale) getDateInstance(int, TimeZone, Locale) getTimeInstance(int, TimeZone, Locale) getDateTimeInstance(int, int, TimeZone, Locale) getInstance(String pattern, TimeZone timezone, Locale locale)は FormatCacheのgetInstanceをよんでいる。 public static FastDateFormat getInstance(final String pattern) { return cache.getInstance(pattern, null, null); } cacheは、FormatCacheというClassで、 FastDateFormatのCacheを確保している private static final FormatCache<FastDateFormat> cache = new FormatCache<FastDateFormat>() { @Override protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { return new FastDateFormat(pattern, timeZone, locale); } }; cacheのキーには、 pattern,timezone, localeの3つが使われておりこれらが同じであれば同じInstanceが返される。 (FormatCache.getInstance(final String pattern, TimeZone timeZone, Locale locale)) FastDateFormatのConstructorでは、 FastDateParserとFastDatePrinterを初期化する protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) { printer = new FastDatePrinter(pattern, timeZone, locale); parser = new FastDateParser(pattern, timeZone, locale, centuryStart); } FastDateParserは、 FastDateFormat.parse(final String source)内でそのまま parser.parse(source)が呼ばれている。パースのロジックはすべてFastDateParserの中にある FastDatePrinterは、 patternで指定したフォーマットに整形して表示するClassで、 FastDateFormat.format()は printer.formatを読んでいて表示部分のロジックは、 FastDatePrinter にある。 FastDateParser.parse()の中身 まずParsePositionを0として、 parse(final String source, final ParsePosition pos) を呼び返り値のDateを返す。 parse(final String source, final ParsePosition pos)では、 Calendarインスタンスをセットし、 parse(source, pos, cal)を呼び結果のBooleanがTrueの場合は、 cal.getTime()を返し、それ以外の場合は、 nullを返す。 public Date parse(final String source, final ParsePosition pos) { // timing tests indicate getting new instance is 19% faster than cloning final Calendar cal = Calendar.getInstance(timeZone, locale); cal.clear(); return parse(source, pos, cal) ? cal.getTime() : null; } parse(source, pos, cal) では、 イテレータのpatternsに対するループで、 patternに対するStrategyを取得し、対応するStrategyでParseし、パースが失敗した時点でfalseを返す。すべてのParseが成功したときにtrueを返す。 public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { final ListIterator<StrategyAndWidth> lt = patterns.listIterator(); while (lt.hasNext()) { final StrategyAndWidth strategyAndWidth = lt.next(); final int maxWidth = strategyAndWidth.getMaxWidth(lt); if (!strategyAndWidth.strategy.parse(this, calendar, source, pos, maxWidth)) { return false; } } return true; } patterns は constructorで呼ばれているinit()関数でセットされている。中では、 StrategyParserでpattern (e.g. yyyy/MM/dd HH:mm:ss z) の文字一つずつに対してどのStrategyかとwidthをセットにした StrategyAndWidthを決定してセットしている strategyAndWidth.strategy.parseでは各Strategyのparse関数の中で、 setCalendar関数が呼ばれて、渡されているcalendarが更新される cal.getTime() は、以下のようにミリセカンドからDateを初期化して返している public final Date getTime() { return new Date(getTimeInMillis()); } ハマったポイント 以下のように、Stringからタイムゾーンを取得しようとして、 FastDateFormatを使ってみようとしたが、SimpleDateFormatではできるが、FastDateFormatではできなかった。Thread-safeにするために、calendarをFastDateFormatに持たせないようにした影響と思われれる。 例. pattern: yyyy/MM/dd HH:mm:ss z parseする対象: 様々なTimeZoneを持った文字列 2021/07/10 00:00:00 UTC 2021/07/10 00:00:00 PDT やりたいこと: 各インプットで指定されたTimeZoneを取得 以下のように、 package com.example.time; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.lang3.time.FastDateFormat; public class FastDateFormatCheck { public static void main(String[] args) { FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy/MM/dd HH:mm:ss z"); DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z"); String[] timeStamps = new String[] { "2021/07/10 00:00:00 UTC", "2021/07/10 00:00:00 PDT", }; for (String timeStamp : timeStamps) { try { Date fastDate = fastDateFormat.parse(timeStamp); Date normalDate = dateFormat.parse(timeStamp); System.out.println("[FastDateFormat]\tsource: " + timeStamp + "\tparsed: " + fastDate + "\tgetTimeZone: " + fastDateFormat.getTimeZone().getID()); System.out.println("[SimpleDateFormat]\tsource: " + timeStamp + "\tparsed: " + normalDate + "\tgetTimeZone: " + dateFormat.getTimeZone().getID()); } catch (ParseException e1) { } System.out.println("------------------------------------"); } } } 結果: [FastDateFormat] source: 2021/07/10 00:00:00 UTC parsed: Sat Jul 10 09:00:00 JST 2021 getTimeZone: Asia/Tokyo [SimpleDateFormat] source: 2021/07/10 00:00:00 UTC parsed: Sat Jul 10 09:00:00 JST 2021 getTimeZone: UTC ------------------------------------ [FastDateFormat] source: 2021/07/10 00:00:00 PDT parsed: Sat Jul 10 16:00:00 JST 2021 getTimeZone: Asia/Tokyo [SimpleDateFormat] source: 2021/07/10 00:00:00 PDT parsed: Sat Jul 10 16:00:00 JST 2021 getTimeZone: America/Los_Angeles ------------------------------------ 内部実装からも分かる通り、FastDateFormatではTimeZoneの情報はCalendarに更新されるが、Calendarオブジェクトは FastDateParserのparse関数内のローカル変数のため、FastDateFormatから取得することはできない。また、parseの結果も millisecondsからDateオブジェクトを初期化して返しているので、もともと文字列内にあったTimeZone情報は含まれていない。 SimpleDateFormatの場合は (内部ロジックを詳しく見ていないが) CalendarインスタンスがSimpleDateFormatに保持されていてparse時に更新されていて、getTimeZone()では、 return calendar.getTimeZone();と返しているためにTimeZoneをパースした文字列から取得できている。 まとめ FastDateFormatは、SimpleDateFormatの高速版且つThread-safe版として代替に使われる FastDateFormatは、getInstanceでのみインスタンスを取得できる FastDateFormat内には、 FormatCacheに FastDateFormatをキャッシュしていてgetInstanceで pattern, timezone, localeをキーにCacheを作成または既存のインスタンスを返す FastDateFormatには、FastDatePrinterとFastDateParserがあり、それぞれにFormatとParseのロジックが実装されている FastDateParserのparseでは、 Calendarを取得し、patternに対して一文字ずつParseStrategyとwidthを決め、対応するstrategyのparseの中で、calendarを更新し、最終的に、calendar.getTime()でDateオブジェクトを返す calendarの返しかたから、 FastDateFormatでは、 parseしたStringの中にあったTimeZoneがどこだったかの情報は取得できない。 参考 Why is Java's SimpleDateFormat not thread-safe? [duplicate] https://www.joda.org/joda-time/ https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/time/FastDateFormat.html https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/time/FastDateFormat.java
- 投稿日:2021-08-15T16:48:38+09:00
【画像処理解説】ガウシアンフィルタ(Javaソース付き)
はじめに 画像処理を簡単に解説していきます。 下部にはJavaでのソースコードも載せているのでぜひご参照ください。 不定期にはなりますが、随時更新していく予定です。 ガウシアンフィルタとは? ノイズ除去などでよく用いられるフィルタです。 ガウシアンフィルタで使われる関数は以下の式です。 k(x, y) = \frac{1}{2 \pi \sigma^2} \exp{( -\frac{x^2 + y^2}{2\sigma^2})} $sigma=1.3$、カーネルサイズ3×3のガウシアンフィルタは以下のようになります。 なお、このフィルタでは全体の和が1になるように正規化がなされています。 k = \frac{1}{16} \begin{bmatrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{bmatrix} 画像にフィルタをかける際には「畳み込み演算」という処理を行います。 これについては次節で簡単に説明します。 畳み込み演算とは? 簡単に言うと、画像上にフィルタを重ね、平行に移動させながら各画素で計算をします。 フィルタのカーネルサイズが3×3である場合、自身の画素だけでなく周囲8つの画素も計算に使用することになります。 カーネルサイズ$n \times n$の場合、周囲$n^2$の画素情報が用いられます。 つまり、カーネルサイズが大きければ大きいほど広い範囲の情報を畳み込みます。 このサイズは使用する画像や状況に応じて設定する必要があります。 アルゴリズム ガウシアン関数をもとに、カーネル内の各値を計算し、フィルタを生成する。 1で生成したフィルタの合計値をもとに、合計値が1になるようにフィルタを正規化する。 2で生成したガウシアンフィルタをもとに畳み込み演算を行う。 ガウシアンフィルタの例 元画像(カラー画像) 結果画像(ガウシアン画像) ソースコード 畳み込み演算は別メソッドとして実装しています。 今後使用頻度が高いため、切り離しています。 Smoothing.java public static SImage gaussian(SImage oriImg, int ksize, double sigma) { // カーネルサイズが偶数なら奇数にする if (ksize % 2 == 0) ksize++; // 係数の計算 double de = 2 * Math.PI * sigma; double df = -2 * sigma * sigma; // フィルタ正規化用の変数を用意 double sum = 0; // ガウシアンフィルタの生成 SMatrix filter = new SMatrix(3, 3); for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { double v = Math.exp(dx * dx / df + dy * dy / df) / de; filter.set(dx + ksize / 2, dy + ksize / 2, v); sum += v; } // フィルタの正規化 if (sum != 0.0) { for (int dy = 0; dy < ksize; dy++) for (int dx = 0; dx < ksize; dx++) { filter.set(dx, dy, filter.get(dx, dy) / sum); } } // フィルタの畳み込み処理 return convolution(oriImg, filter); } private static SImage convolution(SImage oriImg, SMatrix filter) { // カーネルサイズの取得 int ksize = filter.row(); // グレースケール画像の場合 if (oriImg.channel == 1) { // モザイク画像用を生成 var convImg = new SImage(oriImg.width(), oriImg.height(), 1); // 画像をラスタスキャン for (int y = ksize / 2; y < oriImg.height() - ksize / 2; y++) for (int x = ksize / 2; x < oriImg.width() - ksize / 2; x++) { // 合計用変数の初期化 double sum = 0; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) // 画素値を合計に加算 sum += oriImg.getGray(x + dx, y + dy) * filter.get(dx + ksize / 2, dy + ksize / 2); // 合計値を出力画像にセット convImg.setGray(x, y, (int) Math.rint(sum)); } return convImg; // RGBカラー画像の場合 } else if (oriImg.channel == 3) { // モザイク画像用を生成 var convImg = new SImage(oriImg.width(), oriImg.height(), 3); // 画像をラスタスキャン for (int y = ksize / 2; y < oriImg.height() - ksize / 2; y++) for (int x = ksize / 2; x < oriImg.width() - ksize / 2; x++) { // 合計用変数の初期化 double[] sum = new double[3]; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画素値を合計に加算 int[] rgb = oriImg.getRGB(x + dx, y + dy); for (int i = 0; i < 3; i++) sum[i] += rgb[i] * filter.get(dx + ksize / 2, dy + ksize / 2); } // 合計値のint変換 int[] sum2 = new int[3]; for (int i = 0; i < 3; i++) sum2[i] = (int) Math.rint(sum[i]); // 合計値を出力画像にセット convImg.setRGB(x, y, sum2); } return convImg; // アルファチャネルを持つ場合 } else { // 畳み込み画像用を生成 var convImg = new SImage(oriImg.width(), oriImg.height(), 4); // 画像をラスタスキャン for (int y = ksize / 2; y < oriImg.height() - ksize / 2; y++) for (int x = ksize / 2; x < oriImg.width() - ksize / 2; x++) { // 合計用変数の初期化 double[] sum = new double[4]; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画素値を合計に加算 int[] rgb = oriImg.getARGB(x + dx, y + dy); for (int i = 0; i < 4; i++) sum[i] += rgb[i] * filter.get(dx + ksize / 2, dy + ksize / 2); } // 合計値のint変換 int[] sum2 = new int[4]; for (int i = 0; i < 4; i++) sum2[i] = (int) Math.rint(sum[i]); // 合計値を出力画像にセット convImg.setARGB(x, y, sum2); } return convImg; } }
- 投稿日:2021-08-15T15:48:59+09:00
【6章】Javaを学ぼう
今回の記事はif文、条件分岐について記事を書いていきたいと思います。 私自身のアウトプットの場となりますので、よろしくお願いいたします! 条件分岐 特定の状況のときだけある処理を行う、ということを条件分岐といいます。 if文について 最もよく使う条件分岐であるif文について解説していきたいと思います。 Main.java if(条件式) { 処理; } //条件式、処理を当てはめる if (天気 == 雨) { 傘を持っていく; } ifは英語で「もし〜なら」という意味です。この「〜」に当たる部分が、ifの後の()の部分です。これを条件式と呼びます。条件式がtrueなら{}内の処理を行い、反対に条件式がfalseなら何もしません。 それではもう少し具体的に、if文を使ってみたいと思います。 Main.java //if文① int x = 5; if(x == 5) { //trueになる System.out.println("xは5です"); } //if文② int x = 10; if(x == 5) { //falseになる System.out.println("xは5です"); } 出力結果↓ コンソール //if文① xは5です //trueのため実行される //if文② //falseのため実行されない ブロック if文で{ }という記号を使っていますが、複数の処理をまとめたいときに使います。これをブロックと呼び、条件がtrueのときに行う処理を、ブロックを使ってまとめています。 Main.java int x = 10; if(x > 5) { System.out.println("条件はtrue"); System.out.println("xは5より大きい"); //複数の処理をまとめている } 出力結果↓ コンソール 条件はtrue xは5より大きい elseについて(ifとelseの組み合わせ) ifとelseを組み合わせると、「もし〜なら◯◯、そうでなければ△△」という条件分岐が可能になります。 { }の位置など注意してください! Main.java //if文① int x = 5; if(x < 15) { //trueになる System.out.println("xは15より小さい"); } else { System.out.println("xは15以上"); } //if文② int x = 10; if(x < 5) { //falseになる System.out.println("xは5より小さい"); } else { System.out.println("xは5以上"); } 出力結果↓ コンソール //if文① xは15より小さい //if文② xは5以上 else ifについて if文ではelse ifを用いて、さらなる条件分岐が可能です。 ifとelse if、elseを組み合わせると、「もし〜なら◯◯、そうではなくてもし××なら△△、どちらでもない場合は□□」という条件分岐を可能にできます。 Main.java //if文① int x = 30; if(x >= 35) { //falseになる System.out.println("xは35以上"); } else if(x > 25){ //trueになる System.out.println("xは25より大きく、35より小さい"); } else { System.out.println("xは25以下"); } //if文② int x = 15; if(x >= 35) { //falseになる System.out.println("xは35以上"); } else if(x > 25){ //falseになる System.out.println("xは25より大きく、35より小さい"); } else { System.out.println("xは25以下"); } 出力結果↓ コンソール //if文① xは25より大きく、35より小さい //if文② xは25以下 else ifは必要なだけ並べることができますが、複数の条件に合致しても、実行されるのは最初に合致した条件だけになります。注意してください。 以上がif文についての説明になります。次章では、条件分岐の時に使用する、もう一つの構文であるswitch文について記事を書きたいと思います。よろしければご覧になってください! 最後までご覧いただきまして、ありがとうございました!
- 投稿日:2021-08-15T15:04:35+09:00
Spring Batch で Tasklet を使用してジョブがリスタートする挙動を確認する
概要 Spring Batch で Tasklet を使用してジョブが再実行 (リスタート) する挙動を確認する 1度目は必ず失敗し、2度目以降は必ず成功するステップを用意する 失敗したステップからジョブが再実行 (リスタート) されることを確認する 動作確認環境 Java 11 (AdoptOpenJDK 11.0.11) Spring Batch 4.3.3 Spring Boot 2.5.3 Spring Framework 5.3.9 Gradle 7.1.1 macOS Big Sur 11.4 ソースコード ファイル一覧 ├── build.gradle └── src └── main ├── java │ └── com │ └── example │ └── steps │ ├── MyApplication.java │ ├── MyConfig.java │ ├── MyController.java │ └── MyGate.java └── resources └── application.properties build.gradle plugins { id 'org.springframework.boot' version '2.5.3' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { // Spring Batch implementation 'org.springframework.boot:spring-boot-starter-batch' // Spring Web MVC implementation 'org.springframework.boot:spring-boot-starter-web' // Spring Batch のメタデータを入れるデータベース runtimeOnly 'com.h2database:h2:1.4.200' } MyApplication.java package com.example.steps; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // Spring Boot アプリケーションクラス @SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } MyConfig.java package com.example.steps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.step.tasklet.MethodInvokingTaskletAdapter; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; // バッチ構成クラス @Configuration // Bean 定義クラスであることを示すアノテーション @EnableBatchProcessing // Spring Batch を有効にする public class MyConfig { private static final Logger log = LoggerFactory.getLogger(MyConfig.class); private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; private MyGate firstGate = new MyGate("1番目の門"); private MyGate secondGate = new MyGate("2番目の門"); public MyConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) { this.jobBuilderFactory = jobBuilderFactory; this.stepBuilderFactory = stepBuilderFactory; } // ジョブ @Bean public Job myJob(@Qualifier("firstStep") Step firstStep, @Qualifier("secondStep") Step secondStep) { log.info("myJob メソッドを実行"); return jobBuilderFactory.get("myJob") // 一意となる任意のジョブ名を指定 .start(firstStep) // 最初に実行するステップを指定 .next(secondStep) // 次に実行するステップを指定 .build(); } // ひとつめに実行するステップ @Bean("firstStep") public Step firstStep(@Qualifier("firstTasklet") Tasklet firstTasklet) { log.info("firstStep メソッドを実行"); return stepBuilderFactory.get("firstStep") // 任意のステップ名を指定 .tasklet(firstTasklet) // 実行するタスクレットを指定 .build(); } // ひとつめのステップで実行するタスクレット @Bean("firstTasklet") @StepScope // Step の実行ごとに新たなインスタンスを生成する public Tasklet firstTasklet(@Value("#{jobParameters['challenger']}") String challenger) { log.info("firstTasklet メソッドを実行"); MethodInvokingTaskletAdapter tasklet = new MethodInvokingTaskletAdapter(); tasklet.setTargetObject(firstGate); // 実行対象のオブジェクト tasklet.setTargetMethod("challenge"); // 実行するメソッド名 tasklet.setArguments(new Object[]{challenger}); // 実行するメソッドに渡すパラメータ return tasklet; } // ふたつめに実行するステップ @Bean("secondStep") public Step secondStep(@Qualifier("secondTasklet") Tasklet secondTasklet) { log.info("secondStep メソッドを実行"); return stepBuilderFactory.get("secondStep") // 任意のステップ名を指定 .tasklet(secondTasklet) // 実行するタスクレットを指定 .build(); } // ふたつめのステップで実行するタスクレット @Bean("secondTasklet") @StepScope // Step の実行ごとに新たなインスタンスを生成する public Tasklet secondTasklet(@Value("#{jobParameters['challenger']}") String challenger) { log.info("secondTasklet メソッドを実行"); MethodInvokingTaskletAdapter tasklet = new MethodInvokingTaskletAdapter(); tasklet.setTargetObject(secondGate); // 実行対象のオブジェクト tasklet.setTargetMethod("challenge"); // 実行するメソッド名 tasklet.setArguments(new Object[]{challenger}); // 実行するメソッドに渡すパラメータ return tasklet; } } MyController.java package com.example.steps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecutionException; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * ジョブを実行するための WebAPI を提供する。 */ @RestController public class MyController { private static final Logger log = LoggerFactory.getLogger(MyController.class); private final JobLauncher jobLauncher; private final Job myJob; public MyController(JobLauncher jobLauncher, Job myJob) { this.jobLauncher = jobLauncher; this.myJob = myJob; } @GetMapping("/execute/{challenger}") public Map execute(@PathVariable("challenger") String challenger) { try { log.info("===== ジョブを実行 ====="); jobLauncher.run(myJob, new JobParametersBuilder() .addString("challenger", challenger) .toJobParameters()); return Map.of("execute", "ok"); } catch (JobExecutionException e) { log.info(e.toString()); return Map.of("execute", "error"); } } } MyGate.java package com.example.steps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Set; /** * 挑戦者が通る門。 */ public class MyGate { private static final Logger log = LoggerFactory.getLogger(MyGate.class); private final String gateName; private final Set<String> recordBook = new HashSet<>(); /** * 挑戦者が通る門を生成する。 * @param name 門の名前 */ public MyGate(String name) { this.gateName = name; } /** * 一度目は必ず失敗する。二度目以降は必ず成功する。 * @param challenger 挑戦者 */ public void challenge(String challenger) { if (recordBook.contains(challenger)) { // 挑戦者はすでに来ていたか log.info(String.format("成功: %s は %s を突破した", challenger, gateName)); } else { log.info(String.format("失敗: %s は %s に阻まれた", challenger, gateName)); recordBook.add(challenger); // 挑戦者の名を記録する throw new IllegalStateException("失敗"); } } } application.properties # 起動時にジョブを実行しない設定 spring.batch.job.enabled=false # ログ出力設定 logging.level.root=INFO logging.level.org.springframework.batch.core.step.AbstractStep=OFF logging.pattern.console=%-20.20logger{0} %message%n 挙動を確認する Spring Boot を起動する $ gradle bootrun . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.3) (中略) MyConfig firstStep メソッドを実行 MyConfig secondStep メソッドを実行 MyConfig myJob メソッドを実行 ジョブの実行と出力ログの確認 curl コマンドでジョブ実行のためのエンドポイントをコールし、Spring Boot + Spring Batch のログを確認する。 1度目の実行。 $ curl http://localhost:8080/execute/Alice {"execute":"ok"} ログを確認。ジョブが実行されて、1つめのステップが失敗する。 MyController ===== ジョブを実行 ===== SimpleJobLauncher Job: [SimpleJob: [name=myJob]] launched with the following parameters: [{challenger=Alice}] SimpleStepHandler Executing step: [firstStep] MyConfig firstTasklet メソッドを実行 MyGate 失敗: Alice は 1番目の門 に阻まれた SimpleJobLauncher Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{challenger=Alice}] and the following status: [FAILED] in 80ms 2度目の実行。同じパラメータを指定してジョブを実行することで、失敗した箇所からジョブを再実行 (リスタート) することができる。 $ curl http://localhost:8080/execute/Alice {"execute":"ok"} ログを確認。ジョブが実行されて、1つめのステップが成功し、2つめのステップが失敗する。 MyController ===== ジョブを実行 ===== SimpleJobLauncher Job: [SimpleJob: [name=myJob]] launched with the following parameters: [{challenger=Alice}] SimpleStepHandler Executing step: [firstStep] MyConfig firstTasklet メソッドを実行 MyGate 成功: Alice は 1番目の門 を突破した SimpleStepHandler Executing step: [secondStep] MyConfig secondTasklet メソッドを実行 MyGate 失敗: Alice は 2番目の門 に阻まれた SimpleJobLauncher Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{challenger=Alice}] and the following status: [FAILED] in 34ms 3度目の実行。 $ curl http://localhost:8080/execute/Alice {"execute":"ok"} ログを確認。1つめのステップはすでに成功しているので何もしない。2つめのステップが成功する。ジョブが正常に完了する。 MyController ===== ジョブを実行 ===== SimpleJobLauncher Job: [SimpleJob: [name=myJob]] launched with the following parameters: [{challenger=Alice}] SimpleStepHandler Step already complete or not restartable, so no action to execute: StepExecution: id=2, version=3, name=firstStep, status=COMPLETED, exitStatus=COMPLETED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription= SimpleStepHandler Executing step: [secondStep] MyConfig secondTasklet メソッドを実行 MyGate 成功: Alice は 2番目の門 を突破した SimpleJobLauncher Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{challenger=Alice}] and the following status: [COMPLETED] in 14ms 4度目の実行。 $ curl http://localhost:8080/execute/Alice {"execute":"error"} ログを確認。すでに正常に完了したジョブを再実行しようとして JobInstanceAlreadyCompleteException 例外が発生している。 MyController ===== ジョブを実行 ===== MyController org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={challenger=Alice}. If you want to run this job again, change the parameters. パラメータを変更すれば別のジョブとして実行できる。 $ curl http://localhost:8080/execute/Bob {"execute":"ok"} MyController ===== ジョブを実行 ===== SimpleJobLauncher Job: [SimpleJob: [name=myJob]] launched with the following parameters: [{challenger=Bob}] SimpleStepHandler Executing step: [firstStep] MyConfig firstTasklet メソッドを実行 MyGate 失敗: Bob は 1番目の門 に阻まれた SimpleJobLauncher Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{challenger=Bob}] and the following status: [FAILED] in 13ms 参考資料 Spring Batch 4.3.3 ジョブの構成と実行 - リファレンス Spring Batch 4.3.3 ステップの構成 - リファレンス Spring Batch 4.3.3 API ドキュメント - Javadoc 処理の再実行 - TERASOLUNA Batch Framework for Java (5.x) Development Guideline SpringBatchで簡単なオンデマンドバッチを作成 - Qiita
- 投稿日:2021-08-15T15:04:19+09:00
Visual Studio CodeでのJava開発環境の基本
やりたいこと JavaのVersion管理 チェックスタイル TestをIDE上で実行 環境変数の設定 BreakpointでのDebug Java Runtime java.home in settings.json Command + Shift + P Checkstyle check_style.xml があれば、右クリックして set the Checkstyle Configuration fileをクリックする https://marketplace.visualstudio.com/items?itemName=shengchen.vscode-checkstyle https://checkstyle.sourceforge.io/beginning_development.html <- 残念ながらVSCodeがない settings.json "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml", Test java test runner というExtentionをインストールして上のRuntimeがちゃんと設定できていれば、VSCode上でテストが実行できる 環境変数設定 launch.json { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "java", "name": "Launch Current File", "request": "launch", "mainClass": "${file}" }, { "type": "java", "name": "Launch SampleApplication", "request": "launch", "mainClass": "com.nakamasato.testapp.SampleApplication", "projectName": "testapp", "env":{ "TEST_ENV": "value" } } ] } BreakpointでのDebug Breakpointをつける DebugでRunする 確認 感想 一通り設定はできた intellijの方がJava開発にはいいと聞くのでいつか比較も 参考 https://medium.com/@tariqul.islam.rony/learning-java-and-spring-boot-with-visual-studio-code-vscode-part-1-54073f2fa264 https://code.visualstudio.com/docs/editor/debugging
- 投稿日:2021-08-15T14:32:58+09:00
Spring boot Thymeleafを使ったアレコレ。
Thymeleaf ここでは、事前に用意したデータを取得し、画面に表示する部分まで進めていきます。 構成は、以下の通りとします。 controller から Service へロジックの導入。 Service から Dao を経由して H2 Database からデータを取得します。 取得したデータは、オブジェクト型となるため、テーブル定義に合致するように Entity を作成し、変換した上で返却していきます。 Contoroller では、受け取ったデータを Thymeleaf を利用して HTML を出力するという形です。 必要なもの Contorller からざっと作っていきます。 // src/demo/app/view // ViewContoroller.java package com.example.demo.app.view; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import com.example.demo.app.entity.view.View; import com.example.demo.app.service.view.ViewService; @Controller @RequestMapping("/view") public class ViewController { private ViewService viewService; // Serviceをインスタンス化(DI) @Autowired public ViewController(ViewService viewService) { this.viewService = viewService; } @GetMapping // GetMappingにパスを指定しないことで、RequestMappingで指定したviewにアクセスすると呼び出される // Serviceを経由して取得した、一覧データをHTMLにviewListという名前で受け渡し。→HTMLがレンダリングされる public String init(Model model) { List<View> view = viewService.getAll(); model.addAttribute("title", "View title"); model.addAttribute("viewList", view); return "view/index"; } } // src/demo/app/service/view // ViewServiceImpl.java /** Interfaceのコードはこれだけ package com.example.demo.app.service.view; import java.util.List; import com.example.demo.app.entity.view.View; public interface ViewService { List<View> getAll(); } **/ package com.example.demo.app.service.view; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.example.demo.app.entity.view.View; import com.example.demo.app.repository.view.ViewDao; @Service public class ViewServiceImpl implements ViewService { private ViewDao viewDao; // Daoをインスタンス化(DI) @Autowired public ViewServiceImpl(ViewDao viewDao) { this.viewDao = viewDao; } // Daoを経由して、取得したデータをControllerに返却するのみ @Override public List<View> getAll() { return viewDao.getAll(); } } // Interfaceは、Serviceと同じ感じで記載してください // src/demo/app/repository/view // ViewDaoImpl.java package com.example.demo.app.repository.view; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import com.example.demo.app.entity.view.View; @Repository public class ViewDaoImpl implements ViewDao { private JdbcTemplate jdbcTemplate; // H2よりデータを取得するために、JdbcTemplateを使用しています。 @Autowired public ViewDaoImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public List<View> getAll() { // 作成したSQLを利用してqueryForListでデータObjectを取得します。 String sql = "select * from band;"; List<Map<String , Object>> map = jdbcTemplate.queryForList(sql); List<View> list = new ArrayList<>(); // Object型だと、とにかく使いづらいので、データベース定義に該当するEntityに移管する。 map.stream().forEach( target-> { View instance = new View(); instance.setId((int)target.get("id")); instance.setBandName((String)target.get("bandName")); instance.setBestSong((String)target.get("bestSong")); instance.setCreated(((Timestamp)target.get("created")).toLocalDateTime()); list.add(instance); }); return list; } @Override public void insert(View view) { // TODO Auto-generated method stub } @Override public int update(View view) { // TODO Auto-generated method stub return 0; } } <!-- src/main/resource/template index.html Contollerから"title","viewList"という形でパラメータを受け取る --> <!DOCTYPE html> <!-- ここのオマジナイが無いとThymeleafは使用できないので、注意 --> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" /> <title>View</title> </head> <body> <h1 th:text="${title}">View</h1> <table> <tr> <th>id</th> <th>Bandname</th> <th>BestSong</th> <th>datetime</th> </tr> <tr th:each="view : ${viewList}"> <td th:text="${view.id}"></td> <td th:text="${view.bandName}"></td> <td th:text="${view.bestSong}"></td> <td th:text="${view.created}"></td> </tr> </table> </body> </html> ここまでで、localhost:8080/viewにアクセスすると DB へ登録した値が一覧表示されてると思います。
- 投稿日:2021-08-15T14:28:55+09:00
Spring boot H2 の基礎のアレコレ。
H2 Database 必要な Spring Dependencies H2 Database JDBC API Spring Web Spring Boot Devtools(you shoud install if you need.) application.properties で以下のように書くより、yml で書く方が好きなので、ここでは yml ファイルで設定を書いていきます。 // application.properties(個人的に嫌い。。。) spring.datasource.url=~ spring.datasource.username=~ // mem(メモリ)=Springを起動するたびに初期化される。 spring: datasource: url: jdbc:h2:mem:test driverClassName: org.h2.Driver // 管理者権限 username: sa password: // Webブラウザから確認できるようにする。 h2.console.enabled: true この状態で、localhost:8080/h2-consoleにアクセス出来ると思います。 開発では、事前に仮想 DB としてテーブルを作成して、データを突っ込んだ上で使用するだろうから、その部分をやっていきます。 src/resouce/~ ここに SQL を作って配置するだけ〜。 今回は、2 ファイル用意。 scheme.sql data.sql // scheme.sql create table music( id int not null auto_increment, bandName varchar(100) not null, bestSong varchar(100) not null, created datetime not null, primary key(id) ); // data.sql insert into music (bandName,bestSong,created) values('Queen', 'We Will Rock You', '2019-11-12 08:34:19');
- 投稿日:2021-08-15T14:23:51+09:00
Java入門[継承]
継承とは クラス定義の共通部分を別クラスにまとめることになります。 継承されるクラスを「スーパークラス(親クラス)」 継承した新しいクラスを「サブクラス(子クラス)」 と呼ばれます。 例えば「人(Human)」というスーパークラスがあるとします。 「人(Human)」にも、様々な職業に属したりもします。 「教員(Teacher)」 「エンジニア(Engineer)」 「無職(Unemployed)」 以上の3つはサブクラスとも言えますね。 では、Javaの継承を用いてコードを書いていきます。 Humanクラス Human.java public class Human { private String name; private int money; public String getName(){ return this.name; } public void setName(String name){ this.name = name; } public int getMoney(){ return this.money; } public void setMoney(int money){ this.money = money; } public void call(){ System.out.println("私の名前は、" + getName() + "\n給料は、"+ getMoney() + "なり。"); } } フィールドはprivate メソッドはpublicでカプセル化しています。 Humanクラスを元に、様々な職業を生み出してみます。 3つの職業クラス extends 親クラスで継承ができます。 これで、親クラスにあるフィールドやメソッドを引き継ぐことができます。 Teacher.java public class Teacher extends Human{ public Teacher(String name, int money){ setName(name); setMoney(money); } public void teach(){ System.out.println("優しく教えます。"); } } Engineer.java public class Engineer extends Human{ public Engineer(String name, int money){ setName(name); setMoney(money); } public void assemble(){ System.out.println("コードを書きます。"); } } Unemployed.java public class Unemployed extends Human{ public Unemployed(String name, int money){ setName(name); setMoney(money); } public void troublesome(){ System.out.println("働いたら負けだと思ってます。"); } } Main.javaで呼び出してみよう Main.java public class Main{ public static void main(String[] args){ Teacher t = new Teacher("ほげ先生", 200000); Engineer e = new Engineer("ほげSE", 150000); Unemployed u = new Unemployed("ほげニート", 0); t.call(); t.teach(); e.call(); e.assemble(); u.call(); u.troublesome(); } } // 私の名前は、ほげ先生 // 給料は、200000なり。 // 優しく教えます。 // 私の名前は、ほげSE // 給料は、150000なり。 // コードを書きます。 // 私の名前は、ほげニート // 給料は、0なり。 // 働いたら負けだと思ってます。 Humanクラスを継承して、3つの職業を作ることができました。 これでそれぞれのクラスに記述する量も減って、スッキリ見やすくなりそうですね!
- 投稿日:2021-08-15T12:28:56+09:00
その後のAtCoder(ABC-214終了時点でレート:214)
こんばんは。 ABCに参加するたびに記事を書いていたら大変&記事が大量生産されるので、サマリーして投稿をしていきたいと思います。 直近の戦績 レートの変動はこんな感じでした。 ABC-212:205→201 ABC-213:201→194 ABC-214:194→214 200前後をうろうろする日々となっていますが、214ではようやくC問題を解くことができ、ひさびさのプラスです。 ABC-212 B問題 4桁の暗証番号X1 X2 X3 X4がある。パスワードとして強度は十分か? 以下のいずれかを満たすと弱い。 ・4つとも同じ数字 ・1~3の任意の数字iについて、X(i+1)がX(i)の次の数字である。 2点目の「任意」について、どれか一つでもそういう組み合わせがあるとだめかと理解して組んだところWAになりました。テストケースを見て理解を改め書き直し。 import java.util.Scanner; import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class Main{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); String x = sc.next(); char flg1 = '0'; char flg = '0'; int wk1 = 0; int wk2 = 0; if(x.charAt(0)==x.charAt(1)&&x.charAt(1)==x.charAt(2)&&x.charAt(2)==x.charAt(3)){ flg1 = '1'; } for(int i=0;i<3;i++){ wk1 = Integer.parseInt(String.valueOf(x.charAt(i))); wk2 = Integer.parseInt(String.valueOf(x.charAt(i+1))); if((wk1+1)==(wk2)){ //System.out.println("a" + " " + (Integer.parseInt(x.charAt(i))+1); //System.out.println("a"); flg = '1'; }else{ if(wk1==9&&wk2==0){ //System.out.println("b"); flg = '1'; }else{ flg = '0'; break; } } } if(flg1=='1'||flg=='1'){ System.out.println("Weak"); }else{ System.out.println("Strong"); } } } ABC-213 A問題 A,Bを入力 A xor C = B が成り立つCを求める。 へ?どうやって?分からん・・ とりあえずAとBを2進数に変換して、排他的論理和が成り立つ2進数Cを求め、それを10進数変換。 正解には辿り着いたものの、めっちゃ時間かかる。 解説を読むと簡単に求める方法はあるようですが、ちょっと読んでも理解できず・・やばいな。 import java.util.Scanner; import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class Main{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); int a = sc.nextInt(); int b = sc.nextInt(); int awk = a; int bwk = b; int ah[] = new int[8]; int bh[] = new int[8]; int ch[] = new int[8]; int i = 7; while(awk>0){ if(awk%2==0){ ah[i] = 0; //System.out.println(awk); }else{ ah[i] = 1; } awk = awk/2; i--; } for(int j=i;j>=0;j--){ ah[j] = 0; } //for(int k=0;k<8;k++){ //System.out.println(ah[k]); //} //System.out.println("**b*********"); // b i=7; while(bwk>0){ if(bwk%2==0){ bh[i] = 0; //System.out.println(bwk); }else{ bh[i] = 1; } bwk = bwk/2; i--; } for(int j=i;j>=0;j--){ bh[j] = 0; } //for(int k=0;k<8;k++){ //System.out.println(bh[k]); //} for(int k=0;k<8;k++){ if(bh[k]==1){ if(ah[k]==0){ ch[k]=1; }else{ ch[k]=0; } }else{ if(ah[k]==0){ ch[k]=0; }else{ ch[k]=1; } } } //System.out.println("******c*****"); int crui = 0; int keisu = 1; for(int k=7;k>=0;k--){ //System.out.println("ch[k] " + ch[k]); crui = crui + ch[k]*keisu; //System.out.println(crui); keisu = keisu*2; } System.out.println(crui); } } ABC-214 C問題 複雑なため、問題はサイトをご覧いただくのが良いと思います。 処理を2回回さないといけない点がなかなかやらしい。 サンプルケースではこの点も分からないし・・。 残り6秒で解けました。 import java.util.Scanner; import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class Main{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); long s[] = new long[n]; long t[] = new long[n]; long time = 0; for(int i=0;i<n;i++){ s[i] = sc.nextLong(); } long h[] = new long[n]; for(int i=0;i<n;i++){ t[i] = sc.nextLong(); if(i==0){ h[i] = t[i]; //System.out.println(t[i]); time = s[i]+t[i]; }else{ if(t[i]>=time){ h[i] = time; //System.out.println(time); }else{ h[i] = t[i]; //System.out.println(t[i]); } if(t[i]>=time){ time = time + s[i]; }else{ time = t[i] + s[i]; } } } long time2=time; for(int i=0;i<n;i++){ if(i==0){ if(time2<=h[i]){ System.out.println(time2); time2 = s[i]+time2; }else{ System.out.println(h[i]); time2 = s[i]+h[i]; } }else{ /////////// if(h[i]>=time2){ System.out.println(time2); }else{ System.out.println(h[i]); } /////////// if(h[i]>=time2){ time2 = time2 + s[i]; }else{ time2 = h[i] + s[i]; } //System.out.println(h[i]); } //System.out.println(" time " + time + " t[i] " + t[i]); } //System.out.println(cnt); } } 最後に C問題が久々に解けただけで、また頑張ろうと思い直しています。楽観的。
- 投稿日:2021-08-15T12:04:10+09:00
【画像処理解説】Maxプーリング(Javaソース付き)
はじめに 画像処理を簡単に解説していきます。 下部にはJavaでのソースコードも載せているのでぜひご参照ください。 不定期にはなりますが、随時更新していく予定です。 Maxプーリングとは? 前回説明したモザイク処理の一種です。 具体的には、ブロックごとの代表値にブロックの最大値を採用する方法です。 詳しい説明は以下の記事を参照してください。 アルゴリズム 入力画像をラスタスキャンし、2から5の処理を行う。ただし、移動幅をブロックサイズとする。 平均値計算用の変数を初期化する。 ブロックの中をラスタスキャンし、各画素に4の処理を行う。 3のインデックスが画像からはみ出していない場合、画素値を取得し、2の最大値変数と比較して最大値を更新する。 ブロックの中をラスタスキャンし、各画素に5で取得した値をセットする。 Maxプーリング処理結果の例 元画像(カラー画像) 結果画像(モザイク画像) ソースコード Mosaic.java public static SImage maxPooling(SImage oriImg, int ksize) { // カーネルサイズが偶数なら奇数にする if (ksize % 2 == 0) ksize++; // グレースケール画像の場合 if (oriImg.channel == 1) { // モザイク画像用を生成 var mosaicImg = new SImage(oriImg.width(), oriImg.height(), 1); // 画像をラスタスキャン(カーネルサイズ単位で移動) for (int y = ksize / 2; y < oriImg.height() + ksize / 2; y += ksize) for (int x = ksize / 2; x < oriImg.width() + ksize / 2; x += ksize) { // 最大値用変数の初期化 int max = 0; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ集計 if (x + dx < oriImg.width() && y + dy < oriImg.height()) max = Math.max(max, oriImg.getGray(x + dx, y + dy)); } // カーネル範囲を再スキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ最大値をセット if (x + dx < oriImg.width() && y + dy < oriImg.height()) mosaicImg.setGray(x + dx, y + dy, max); } } return mosaicImg; // RGBカラー画像の場合 } else if (oriImg.channel == 3) { // モザイク画像用を生成 var mosaicImg = new SImage(oriImg.width(), oriImg.height(), 3); // 画像をラスタスキャン(カーネルサイズ単位で移動) for (int y = ksize / 2; y < oriImg.height() + ksize / 2; y += ksize) for (int x = ksize / 2; x < oriImg.width() + ksize / 2; x += ksize) { // 最大値用変数の初期化 int[] max = new int[3]; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ集計 if (x + dx < oriImg.width() && y + dy < oriImg.height()) { int[] rgb = oriImg.getRGB(x + dx, y + dy); for (int i = 0; i < 3; i++) max[i] = Math.max(max[i], rgb[i]); } } // カーネル範囲を再スキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ平均値をセット if (x + dx < oriImg.width() && y + dy < oriImg.height()) mosaicImg.setRGB(x + dx, y + dy, max); } } return mosaicImg; // アルファチャネルを持つ場合 } else { var mosaicImg = new SImage(oriImg.width(), oriImg.height(), 4); for (int y = ksize / 2; y < oriImg.height() + ksize / 2; y += ksize) for (int x = ksize / 2; x < oriImg.width() + ksize / 2; x += ksize) { int[] max = new int[4]; for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { if (x + dx < oriImg.width() && y + dy < oriImg.height()) { int[] rgb = oriImg.getARGB(x + dx, y + dy); for (int i = 0; i < 4; i++) max[i] = Math.max(max[i], rgb[i]); } } for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { if (x + dx < oriImg.width() && y + dy < oriImg.height()) mosaicImg.setARGB(x + dx, y + dy, max); } } return mosaicImg; } }
- 投稿日:2021-08-15T11:38:47+09:00
【画像処理解説】モザイク処理(Javaソース付き)
はじめに 画像処理を簡単に解説していきます。 下部にはJavaでのソースコードも載せているのでぜひご参照ください。 不定期にはなりますが、随時更新していく予定です。 モザイクとは? 現代社会においてモザイクを見たことのない人はいないと思いますが、一応説明します。 モザイクとはブロックごとに平坦化することで、元の画像を分かりにくくする処理です。 ブロックが大きければ大きいほど画像が荒くなります。 一般的なモザイク処理は非可逆変換となっています。 すなわち、一度モザイクをかけると元には戻せないということですね。 そのため、個人情報保護や検閲などで用いられています。 処理方法は? 先述した通り、まず画像をブロック分けします。 そしてブロックごとに何らかの式を用いて代表値を計算し、ブロックすべてを塗りつぶします。 これだけで簡単にモザイクをかけることができます。 言葉で説明するのは非常に簡単なのですが、実際にプログラム化するのは意外と複雑だったりします。 特に初心者の方には少し難しいかもしれませんね。 なお、以下のプログラムでは計算式として平均値を用います。 この処理は平均プーリングとも呼ばれます。 アルゴリズム 入力画像をラスタスキャンし、2から6の処理を行う。ただし、移動幅をブロックサイズとする。 平均値計算用の変数を初期化する。 ブロックの中をラスタスキャンし、各画素に4の処理を行う。 3のインデックスが画像からはみ出していない場合、画素値を取得し2で用意した変数に加算する。 2の変数を用いて平均値を計算する。 ブロックの中をラスタスキャンし、各画素に5で取得した値をセットする。 モザイク処理の結果例 元画像(カラー画像) 結果画像(モザイク画像) ソースコード Mosaic.java public static SImage mosaic(SImage oriImg, int ksize) { // カーネルサイズが偶数なら奇数にする if (ksize % 2 == 0) ksize++; // グレースケール画像の場合 if (oriImg.channel == 1) { // モザイク画像用を生成 var mosaicImg = new SImage(oriImg.width(), oriImg.height(), 1); // 画像をラスタスキャン(カーネルサイズ単位で移動) for (int y = ksize / 2; y < oriImg.height() + ksize / 2; y += ksize) for (int x = ksize / 2; x < oriImg.width() + ksize / 2; x += ksize) { // 平均計算用変数の初期化 double sum = 0; int count = 0; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ集計 if (x + dx < oriImg.width() && y + dy < oriImg.height()) { sum += oriImg.getGray(x + dx, y + dy); count++; } } // 合計値を計算 int mean = (int) Math.rint(sum / count); // カーネル範囲を再スキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ平均値をセット if (x + dx < oriImg.width() && y + dy < oriImg.height()) mosaicImg.setGray(x + dx, y + dy, mean); } } return mosaicImg; // RGBカラー画像の場合 } else if (oriImg.channel == 3) { // モザイク画像用を生成 var mosaicImg = new SImage(oriImg.width(), oriImg.height(), 3); // 画像をラスタスキャン(カーネルサイズ単位で移動) for (int y = ksize / 2; y < oriImg.height() + ksize / 2; y += ksize) for (int x = ksize / 2; x < oriImg.width() + ksize / 2; x += ksize) { // 平均計算用変数の初期化 double[] sum = new double[3]; int count = 0; // カーネル範囲をスキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ集計 if (x + dx < oriImg.width() && y + dy < oriImg.height()) { int[] rgb = oriImg.getRGB(x + dx, y + dy); for (int i = 0; i < 3; i++) sum[i] += rgb[i]; count++; } } // 合計値を計算 int[] mean = new int[3]; for (int i = 0; i < 3; i++) mean[i] = (int) Math.rint(sum[i] / count); // カーネル範囲を再スキャン for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { // 画像内である場合のみ平均値をセット if (x + dx < oriImg.width() && y + dy < oriImg.height()) mosaicImg.setRGB(x + dx, y + dy, mean); } } return mosaicImg; // アルファチャネルを持つ場合 } else { var mosaicImg = new SImage(oriImg.width(), oriImg.height(), 4); for (int y = ksize / 2; y < oriImg.height() + ksize / 2; y += ksize) for (int x = ksize / 2; x < oriImg.width() + ksize / 2; x += ksize) { double[] sum = new double[4]; int count = 0; for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { if (x + dx < oriImg.width() && y + dy < oriImg.height()) { int[] rgb = oriImg.getARGB(x + dx, y + dy); for (int i = 0; i < 4; i++) sum[i] += rgb[i]; count++; } } int[] mean = new int[4]; for (int i = 0; i < 4; i++) mean[i] = (int) Math.rint(sum[i] / count); for (int dy = -ksize / 2; dy <= ksize / 2; dy++) for (int dx = -ksize / 2; dx <= ksize / 2; dx++) { if (x + dx < oriImg.width() && y + dy < oriImg.height()) mosaicImg.setARGB(x + dx, y + dy, mean); } } return mosaicImg; } }
- 投稿日:2021-08-15T00:05:37+09:00
CentOS7にJava11インストール
java11のパッケージを検索 「yum search」 コマンドでパッケージを検索します。 sudo yum search java-11 (中略) ====================================== N/S matched: java-11 =================== java-11-openjdk.i686 : OpenJDK Runtime Environment 11 java-11-openjdk.x86_64 : OpenJDK 11 Runtime Environment java-11-openjdk-demo.i686 : OpenJDK Demos 11 java-11-openjdk-demo.x86_64 : OpenJDK 11 Demos java-11-openjdk-devel.i686 : OpenJDK Development Environment 11 java-11-openjdk-devel.x86_64 : OpenJDK 11 Development Environment java-11-openjdk-headless.i686 : OpenJDK Headless Runtime Environment 11 java-11-openjdk-headless.x86_64 : OpenJDK 11 Headless Runtime Environment java-11-openjdk-javadoc.i686 : OpenJDK 11 API documentation java-11-openjdk-javadoc.x86_64 : OpenJDK 11 API documentation java-11-openjdk-javadoc-zip.i686 : OpenJDK 11 API documentation compressed in a single archive java-11-openjdk-javadoc-zip.x86_64 : OpenJDK 11 API documentation compressed in a single archive java-11-openjdk-jmods.i686 : JMods for OpenJDK 11 java-11-openjdk-jmods.x86_64 : JMods for OpenJDK 11 java-11-openjdk-src.i686 : OpenJDK Source Bundle 11 java-11-openjdk-src.x86_64 : OpenJDK 11 Source Bundle java-11-openjdk-static-libs.i686 : OpenJDK libraries for static linking 11 java-11-openjdk-static-libs.x86_64 : OpenJDK 11 libraries for static linking java11をインストール 実行環境のみが必要な場合はopenjdkをインストールします。 インストール sudo yum install -y java-11-openjdk.x86_64 開発をする場合はopenjdk-develをインストールします。 インストール sudo yum install -y java-11-openjdk-devel.x86_64 インストール後、Javaのバージョンを確認します。 バージョン確認 $ java -version openjdk version "11.0.12" 2021-07-20 LTS OpenJDK Runtime Environment 18.9 (build 11.0.12+7-LTS) OpenJDK 64-Bit Server VM 18.9 (build 11.0.12+7-LTS, mixed mode, sharing)