- 投稿日:2019-05-07T23:20:03+09:00
テスト自動化の範囲 どこまで担保? カバレッジ100%は必要?
junit等のテスト自動化で、どこまで担保を取るかはどの現場でも議論になると思います。
とても良いルール付けに巡り合えたので紹介したいと思います。
この投稿の目的
無駄なテストコード作成を削減し生産性を上げ、毎日定時で帰宅するためです。
前提
・コントローラ層 :サービス層からの値を画面表示値に編集したりする責務です。
・サービス層 :各機能処理の入り口です。
・ドメイン層 :ビジネスロジックの責務はこの層です。
DTO、VOの詰め替えもここで行います。
・データアクセス層:DBアクセスが責務です。ルール付け、担保範囲
コントローラ層
基本的に不要です。
なぜならコントローラは画面に関わるところですので、画面に関わるテストで担保が取れるからです。Seleniumや結合テストで担保を取るものを、わざわざjunitなどのサーバサイドで担保を取るのは無駄です。複雑な処理がある場合は、その処理のみ切り出してそこだけ担保取るのが良いです。私の場合は別メソッドに切り出してその箇所のカバレッジのみ100%にしています。
サービス層
ここのテストを一番厚くすべきです。サービス層以下の結合テストをする勢いで担保を取ると良いです。但し、以下の担保は不要です。
・業務に起こりえない箇所の網羅。
・戻り値のデータの全比較(assert)。
例えば2件リストがある場合は1件目のみ全ての値を比較して2件目は不要です。
よくある反論として「1件目の値を2件目の値に入る可能性がある」だと思いますが、
1件目と2件目に入る値を別の値にしておいて片方だけ比較すれば担保が取れます。
・メソッドの呼び出し回数。
担保理由が不明だからです。戻ってきたアウトプットの検証で十分です。ドメイン層
基本的に不要です。
サービス層で担保を取っておけば十分です。複雑なビジネスロジックがある場合は、その部分だけ担保を取ります。
データアクセス層
ここも厚くする箇所です。指定した条件で期待した登録、更新、参照できることを細かく担保しましょう。SQLの外部結合、内部結合条件も細かく網羅しましょう。
サービス層のテストで作ってもよいのですが、サービスのテストコードが大量になるのを防ぐためデータアクセス層のテストに任せられるところはお任せしましょう。全体的
業務で起きないことは担保不要です。システム開発は業務改善が目的です。業務で起きえないことを色々考慮し増えた分岐は処理されないコードです。さらにカバレッジ100%という品質向上に貢献しない悪習ルールと組み合わさるとそれだけで2,3日無駄な作業を行います。
苦い経験①
私はカバレッジ100%を目指し過剰な品質担保で、1週間ぐらいは工数を無駄に使ってしまったことがあります…
苦い経験②
この方法を考えた同じチームメンバーの方は、過去にカバレッジ100%が必須だった現場で追加開発の度に多数のjunit修正が必要となりました。その結果、保守性が悪くお金の折り合いがつかなくなりプロジェクトが無くなったそうです…
まとめ
今回はサーバサイドを例に挙げていますが、seleniumなどのその他テスト自動化も同じだと思います。
一番良いのは「ちょうどいい担保」と「メンテのしやすさ」です。
- 投稿日:2019-05-07T23:13:49+09:00
JavaFX beanとコントロールの双方向バインディング
メモ
すでにどなたかがお書きかとはおもいますが・・。
あるデータを格納したbeanとコントロールを双方向バインディングし、バインディング直後はbeanの持っている値をコントロールに反映、以降コントロールの値が(ユーザーのマニュアル操作で)変更された時にはbeanの値に反映したい。
バインディングのところだけメモしておく。
textfield.textProperty().bindBidirectional(javafxbean.stringProperty());javafxbeanとtextfieldを逆にすると、うまく行かない。
コード
FXMLDocument.fxml<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.AnchorPane?> <AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="bindtest.FXMLDocumentController"> <children> <Button fx:id="button" layoutX="126" layoutY="90" onAction="#handleButtonAction" text="Click Me!" /> <TextField fx:id="textfield" layoutX="83.0" layoutY="162.0" /> <Label fx:id="label" layoutX="147.0" layoutY="114.0" text="Label" /> </children> </AnchorPane>FXMLDocumentController.javapackage bindtest; import java.net.URL; import java.util.ResourceBundle; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; public class FXMLDocumentController implements Initializable { @FXML private Button button; @FXML private TextField textfield; @FXML private Label label; private javafxbean[] beans; int idx = 0; javafxbean prev = null; @Override public void initialize(URL url, ResourceBundle rb) { beans = new javafxbean[3]; for (int i = 0; i < 3; i++) { beans[i] = new javafxbean(i); } setBean(beans[idx]); } @FXML private void handleButtonAction(ActionEvent event) { idx++; idx %= 3; setBean(beans[idx]); } private void setBean(javafxbean b) { label.setText("id = " + idx); if (prev != null) { textfield.textProperty().unbindBidirectional(prev.stringProperty()); } textfield.textProperty().bindBidirectional(b.stringProperty()); prev = b; print(); } private void print() { System.out.println(idx); for (javafxbean b : beans) { System.out.println(b); } System.out.println(); } }javafxbean.javapackage bindtest; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class javafxbean { int id; StringProperty data = new SimpleStringProperty(""); public javafxbean(int id) { this.id = id; } public StringProperty stringProperty(){ return data; } }BindTest.javapackage bindtest; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class BindTest extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml")); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
- 投稿日:2019-05-07T22:58:25+09:00
Angular framework features for web development
Why Angular is Better For Web Application Development? In this computerized period, the market is seeing the regularly expanding interest for web engineers. There are a great many sites on the web, and the numbers are rising each day. Numerous stages are accessible in the market for web improvement. Picking the correct one with the capacity to show a utilitarian and an easy to understand is extreme. AngularJS has picked up a great deal of footing since it was dispatch by Google. It is an open source web application which spins around HyperText Markup language (HTML), Cascading Style Sheet (CSS) and JS (JavaScript).
Why Angular is Better For Web Development?What is AngularJS?
Distributed by Google in 2009 AngularJS has turned out to be a standout amongst the most prominent JavaScript structures till now. To be valid, it doesn't bring any principal new and progressive answers for designers and developers. In any case, regardless it stays top best framework. Things being what they are, the reason? It is straightforward in light of the fact that it is anything but difficult to utilize, it gives high improvement to the entire advancement procedure and structure of JavaScript code. As it were, it contains everything engineers require while building dynamic single page application.
The basic role of AngularJS is to rearrange front-end advancement. Named as the following enormous thing, AngularJS accompanies a few systems and modules for planners just as designers. Here are a couple of motivations to pick AngularJS:Created by Google
Precise is created and kept up by specialists at Google. In this way, engineers can rest guaranteed that they would deal with a steady code base. It likewise gives them a phenomenal chance to gain from confirmed open source specialists. On the off chance that any issues emerge, experts and network individuals give the arrangement.
Utilizations MVC Architecture
AngularJS utilizes Model View Controller (MVC) engineering for web application advancement. Actualizing MVC is straightforward. The engineer needs to part the application and AngularJS deals with everything else. In the MVC, Model is in charge of looking after information, View is in charge of showing the information, and Controller overcomes any issues among View and Model.
Taking care of Dependencies
Dynamic stacking and conditions become simple with Angular, and their use according to prerequisite without agonizing over examples and namespaces turns out to be simple too. Rakish deals with the total article life cycle accordingly taking care of conditions especially.Highlight Rich
Precise has highlights like information authoritative, scope the executives, structure approval, API customer, orders, and information official. These highlights make Angular vigorous and make the web applications progressively direct, simple to distinguish and investigate if any issues emerge.
Two-way information official
Precise utilizations two-way information authoritative. It has various advantages, for example, a programme update of the hidden information store. UI refreshes itself when information store refreshes. These updates expel a ton of rationale from the front-end show code particularly utilizing Angular's decisive tendency towards UI introduction. Viable packaging of information toward the front diminishes the need to do perplexing and ruinous control of the DOM.Standard JavaScript Functionalities
Angular uses Plain Old JavaScript Object (POJO) in each item. This implies the requirement for additional getter and setter capacities are repetitive. POJO gives all the standard JavaScript functionalities and encourages in expelling and including properties from items and can circle over articles freely.Source: Java training in chennai
- 投稿日:2019-05-07T22:45:26+09:00
mybatis-spring-boot-starter 2.0の変更点
(だいぶ遅くなりましたが・・・)2019年1月22日のリリースされた2.0の変更点をまとめました
Note:
2019/4に2.0.1がリリース済みなので、内容的には2.0.1の変更点も含みます。この変更では、目玉になるような大きな機能追加・改善はないため、Spring Boot 2.x(Spring Framework 5.x)以上を必須とした最初のバージョンであるという点がこの変更の目玉となります(地味な改善はいくつか行っていますが・・)。
なお、
- mybatis-spring-boot-starterの使い方については、こちら(バージョン2.0対応済)
- mybatis-spring-boot-starter 1.2から1.3の変更点については、こちら
- mybatis-spring-boot-starter 1.1から1.2の変更点については、こちら
- mybatis-spring-boot-starter 1.0から1.1の変更点については、こちら
も必要に応じて合わせてご覧下さい。
Note:
ちなみに、Spring Boot 1.5.x(Spring Framework 4.3.x)向けのメンテナンスはバージョン1.3.xで継続していますが、2019/8を目処にSpring Boot 1.5.xがEOLになることがアナウンスされているので、バージョン1.3.xのメンテナンスも同じ時期に終了する予定です(=最後に1.3.5をリリースしてメンテナンスを終了する予定)。依存ライブラリの必須バージョン
バージョン2.0を使う場合は、以下のバージョンが必須になります。
- MyBatis 3.5+
- Mybatis-Spring 2.0+
- Spring Boot 2.0+
Javaの必須バージョン
バージョン2.0を使う場合は、Java 8以上が必要になります。
依存ライブラリのバージョン更新
バージョン2.0では、以下のライブラリのバージョンが更新されています。
ライブラリ名 1.3.2のバージョン 2.0.0のバージョン 2.0.1のバージョン MyBatis 3.4.6 3.5.0 3.5.1 MyBatis Spring 1.3.2 2.0.0 2.0.1 Spring Boot 1.5.10.RELEASE 2.0.8.RELEASE 2.0.9.RELEASE Note:
バージョン2.0.0と同時にリリースされたバージョン1.3.3ではSpring Boot 1.5.19.RELEASE、バージョン2.0.1と同時にリリースされたバージョン1.3.4ではSpring Boot 1.5.20へ更新されています。コンフギュレーションプロパティで
typeAliasesSuperTypeの指定をサポート
typeAliasesSuperTypeはSpring MyBatis提供のSqlSessionFactoryBeanでサポートしているオプションで、タイプエイリアスとして登録するクラスを指定パッケージ配下からスキャンする際に、指定したクラス・インタフェースに割り当て可能なクラス(=継承または実装しているクラス)のみをスキャン対象にしたい場合に利用します。例えば・・・指定したパッケージ配下から
com.example.data.Entityインタフェースを実装しているクラスだけをタイプエイリアスに登録する場合は、src/main/resources/application.propertiesmybatis.type-aliases-package=com.example mybatis.type-aliases-super-type=com.example.data.Entityという指定にすることで実現することができます。
インジェクション可能な
DataSourceが1つの時のみMyBatisのAuto-Configureが有効になるように変更バージョン1.3.xまでは、プライマリ指定がない状態で複数の
DataSourceがDIコンテナに登録されていると(= by typeでのインジェクション候補のDataSourceが複数存在する状態になっていると)、MyBatisのAuto-Configureでエラーになっていましたが、2.0からはMyBatisのAuto-Configureが無効になるように変更が行われています。この変更は、Spring Boot提供のJdbcTemplateのAuto-Configureなどの動作をフィードバックしました。なお、複数データソースに対するAuto-Configureのサポートについては、機能追加要望としてIssueを作成して頂ければ幸いです(PRも大歓迎です!)。
spring-boot-autoconfigure-processorの適用
spring-boot-autoconfigure-processorは、Auto-Configureクラスのアノテーション情報(Auto-Configureに必要なメタ情報)をコンパイル時に事前に読み取ってプロパティファイルへ出力しておくことで、Spring Boot実行時にメタ情報をクラスから読み込む処理を省いてアプリケーション起動時間を早めることを目的としてアーティファクトです。
バージョン1.3.xではこの仕組みを利用していませんでしたが、バージョン2.0よりこの仕組みを利用してメタ情報をプロパティファイルへ出力するように改善しています。ベースパッケージ指定時のワイルドカードをサポート
バージョン2.0.1より、コンフィギュレーションプロパティ(
mybatis.type-aliases-packageとmybatis.type-handlers-package)にて、スキャン対象のベースパッケージを指定する際にワイルドカードを指定することができるようになります。具体的には・・・
# パッケージの深さを意識したワイルドカード指定 mybatis.type-handlers-package=com.example.*.typehandler # パッケージの深さを意識しないワイルドカード指定 mybatis.type-handlers-package=com.example.**.typehandlerといった感じの指定が可能になります。
Note:
MyBatis本体はワイルドカードの指定をサポートしていないため、MyBatis本体の設定ファイル(mybatis-config.xml)などに指定するベースパッケージにはワイルドカードを指定することはできません。Important:
バージョン2.0.1を適用すると、ワイルドカード指定の有無にかかわらず「タイプエイリアスをスキャンする際に重複エラーが発生する不具合」が報告されているので、アップデートする際に注意が必要になります。 なお、無条件でエラーが発生するわけではなく、スキャン対象のパッケージ配下に以下の条件をみたすクラス・インタフェースが存在すると、重複エラーになることがわかっています。
- 無名オブジェクトを複数利用している
- 同じ名前のインタフェース・クラス・内部クラスが複数ある
この事象は、MyBatis Spring 2.0.2で修正される予定(2019/7上旬リリース予定)で、MyBatis Spring 2.0.2を取り込んだバージョン2.1.0を2019/7上旬にリリースする予定になっています。なお、 (2019/3でSpring Boot 2.0.xがEOLになることがアナウンスされているため+既にGA対象からも外れているため)バージョン2.0.xにバックポートする予定はありません。本事象を解決するためには、基本的にはバージョン2.1.xへアップデートすることを推奨します。ただし・・・どうしてもバージョン2.1.xへアップデートできない場合は、MyBatis Spring 2.0.2を個別に適応することを検討していただければと思います。
@MybatisTest指定時に@ExtendWith(SpringExtension.class)の指定が不要になるバージョン2.0.1より、
@MybatisTestを付与したJUnit 5のテストケースクラスにて、@ExtendWith(SpringExtension.class)の指定が不要になります。これは、@MybatisTest側に@ExtendWith(SpringExtension.class)を付与することで、JUnit 5のフレームワークエンジンが合成アノテーションである@MybatisTestに指定しているアノテーションから情報を読み取ってくれるためです。
この変更は、Spring Boot提供の@SpringBootTestなどに対して同様の対応が行われていたものをフィードバックしたものです。バージョン2.0.1以降でのJUnit5テストケースクラスの作成例@MybatisTest class MyMapperTest { // ... }参考:バージョン2.0.0以前でのJUnit5テストケースクラスの作成例@ExtendWith(SpringExtension.class) // バージョン2.0.1以降ではこの指定は不要 @MybatisTest class MyMapperTest { // ... }
- 投稿日:2019-05-07T22:09:21+09:00
JavaCCのセットアップ手順
とっても易しいセットアップ手順
職場でどうしても構文解析をしたくなったので、JavaCCというパーサジェネレータを使う機会がありました。
この記事は、JavaCCを使うにあたって、公式ページのGetting Startedなど色々読んでまとめた自分向け手順です。
環境
以下の環境で動作確認しました。
OS JDK JavaCC Redhat Enterprise Linux(RHEL) 6 1.6 6.0 macOS Mojave 1.8 6.0 Windows 10 1.8 6.0 ダウンロード
公式ダウンロードサイトからzipファイルをダウンロードします。
私の環境では、「Download JavaCC 6.0」というリンクボタンを押すとダウンロードが始まりました。解凍
ダウンロードしたzipファイルを解凍します
解凍先は書き込み権限のあるところならどこでも大丈夫です。
(私はサーバ側の環境を壊さないよう、以下のようにホームディレクトリ配下に解凍しました。)(Linx/Mac)${HOME}/usr/local/tools/javacc-6.0(Windows)%systemdrive%%homepath%¥usr¥local¥tools¥javacc-6.0実行ファイルの作成
解凍したディレクトリ(フォルダ)内に"bin"というディレクトリがあります。
ここに以下のような実行ファイルを起きます。${HOME}/usr/local/tools/javacc-6.0/bin/javacc(Linux/Mac)#!/bin/bash java -cp ${HOME}/usr/local/tools/javacc-6.0/bin/lib/javacc.jar javacc "$@"%systemdrive%%homepath%¥usr¥local¥tools¥javacc-6.0¥bin¥javacc.bat(Windows)@ECHO OFF java -cp %systemdrive%%homepath%¥usr¥local¥tools¥javacc-6.0¥bin¥lib¥javacc.jar javacc %%*パスを通します。
ユーザ環境変数PATHに実行ファイルを置いたディレクトリを追加します。Linux/Macexport PATH=${PATH}:${HOME}/usr/local/tools/javacc-6.0/binWindowsset PATH=%PATH%;%systemdrive%%homepath%¥usr¥local¥tools¥javacc-6.0¥binテストしてみます
上記でセットアップは完了です。
ここで、動作するかテストしておきましょう。(Windowsについては割愛)Linux/Mac(ホームディレクトリを/home/hogehogeとした場合)> # まずはPATHが通ったことを確認 > which javacc /home/hogehoge/usr/local/tools/javacc-6.0/bin/javacc > # javaccが実行できることを確認 > javacc Java Compiler Compiler Version 6.0_1 (Parser Generator) Usage: javacc option-settings inputfile "option-settings" is a sequence of settings separated by spaces. Each option setting must be of one of the following forms: ...(実際はもっと長いメッセージが出ます。)
- 投稿日:2019-05-07T21:28:41+09:00
ABC - 026 - A&B&C
AtCoder ABC 026 A&B&C
- ABC027すごい悩んだのに、、、難易度差が激しい
A問題
- $(x,y)$の組み合わせを全部試す
private void solveA() { final int a = nextInt(); int res = IntStream.range(1, a).reduce(0, (sum, i) -> Integer.max(i * (a - i), sum)); out.println(res); }B問題
- 外から赤→白に塗っていく
- 最後、
Math.PIを使わずに3.1415って手入力したらNGでしたprivate void solveB() { int numN = nextInt(); int[] wk = IntStream.range(0, numN).map(i -> nextInt()).toArray(); Arrays.sort(wk); long res = 0; boolean isRed = true; for (int i = 0; i < wk.length; i++) { res += Math.pow(wk[numN - 1 - i], 2) * (isRed ? 1 : -1); isRed = !isRed; } double mul = res * Math.PI; out.println(mul); }C問題:class利用
上司が入力されている配列を渡される
社員番号が小さい上司がただ一人存在するとの記述から後ろから見ていけば、一番最後の人がわかる?- 前から見ていくと自分の部下を探すことになるけど、後ろから見ていけば自分の上司を探すことになる
- 上司の給料は部下によって決まるので後ろからみていったほうがよさそう
冗長だけどclassを作成して自分の給料を表現した
- 給料表現しているのに
BOSSなのは。。。自分の給料を計算し、計算結果をもとにBOSSの給料を計算する
private void solveC() { int numN = nextInt(); /* * Index=0 -> B2 * Index=-1 -> B1 */ int[] wk = IntStream.range(0, numN - 1).map(i -> nextInt()).toArray(); /* * 部下リスト */ Boss[] wkList = new Boss[numN - 1]; /* * 高橋専用 */ Boss takahashi = new Boss(); /* * 最後から実行すれば必ず上司がいる。よね? * (高橋が上司を除く) */ for (int i = wk.length - 1; i >= 0; i--) { /* * 自分の給与を取得 */ int salary = getSelfSalary(wkList, i); /* * bossの番号とindexを合わせる * 高橋がbossの場合は-1になる */ int bossNum = wk[i] - 2; /* * 上司の部下リスト取得 * 上司のリストに自分の給与を登録(合算?) */ if (bossNum == -1) { /* * BOSSが高橋なのでサラリーを+ * 高橋であってもmaxとmin+1の制約が掛かるので */ takahashi.setMax(salary); takahashi.setMin(salary); takahashi.addMembers(1); } else { Boss boss = wkList[bossNum]; if (boss == null) { /* * 今まで部下がいなかった上司なので登録 */ boss = new Boss(); wkList[bossNum] = boss; } boss.setMax(salary); boss.setMin(salary); boss.addMembers(1); } } out.println(takahashi.getMax() + takahashi.getMin() + 1); } /** * 自分の部下リスト取得して、 * 自分の給与計算 * @param wkList * @param i * @return */ private int getSelfSalary(Boss[] wkList, int i) { int salary; Boss self = wkList[i]; if (self != null) { int min = self.getMin(); int max = self.getMax(); salary = max + min + 1; } else { salary = 1; } return salary; } /** * 自分の部下の数と * 自分の給与の最大と最小を保持する * @author works * */ private static class Boss { int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; int members; public int getMembers() { return members; } public void addMembers(int member) { this.members += member; } public int getMin() { return min; } public int getMax() { return max; } public void setMin(int min) { this.min = Integer.min(this.min, min); } public void setMax(int max) { this.max = Integer.max(this.max, max); } }C問題:List
classを利用して計算したけど、コレListでもできるよね???
と思って、Listで表現してみました。classより大分速かったです
private void solveC2() { int numN = nextInt(); /* * Index=0 -> B2 * Index=-1 -> B1 */ int[] wk = new int[numN + 1]; List<List<Integer>> mems = new ArrayList<List<Integer>>(); wk[0] = 0; wk[1] = 0; mems.add(new ArrayList<Integer>()); mems.add(new ArrayList<Integer>()); for (int i = 0; i < numN - 1; i++) { wk[i + 2] = nextInt(); mems.add(new ArrayList<Integer>()); } for (int i = wk.length - 1; i >= 0; i--) { List<Integer> tmp = mems.get(i); int salary = 0; if (tmp.size() != 0) { int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (int j = 0; j < tmp.size(); j++) { min = Integer.min(min, tmp.get(j)); max = Integer.max(max, tmp.get(j)); } salary = min + max + 1; } else { salary = 1; } tmp = mems.get(wk[i]); tmp.add(salary); } long salary = 0; { List<Integer> tmp = mems.get(1); int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (int j = 0; j < tmp.size(); j++) { min = Integer.min(min, tmp.get(j)); max = Integer.max(max, tmp.get(j)); } salary = min + max + 1; } out.println(salary); }
- 投稿日:2019-05-07T20:23:12+09:00
Java Bronze資格試験の申し込み手順がややこしすぎるぅぅぅぅ泣
■ なぜこの記事を書いたのか
Java Bronze試験の申し込みに手こずりまくった。
皆さんにはあんな思いをさせたくないので、ここに申し込み手順を記していきたいと思います。
※試験会場で受験する場合を記述しています。
※2019年5月に執筆
■ 手順(お品書き)
- ピアソンVUEのアカウント作成
- Oracleアカウントを作成
- ピアソンVUEから試験会場の予約
- 受験
- オラクルHPにて合否結果確認
- 最後に。。。
■ ①ピアソンVUEのアカウント作成
まずは試験を申し込む為(試験会場の予約をする為)、『ピアソンVUE』のアカウントを作成します。
上記リンクにアクセスしたら、画面中央のキャンペーンコードをメモしておいて下さい。
試験の申し込み時にこのコードを入力すれば再受験料が無料になります!
キャンペーンコードをメモしたら、画面右の『アカウント作成』ボタンをクリックし必要事項を入力してアカウントを作って下さい。
■ ② Oracleアカウントを作成
なぜ『Oracleアカウント』を作成しなければならないのか?それは『CertView』を利用する為だ!
『CertView』とはオラクル社が提供している認定システムの事。この『CertView』で試験結果の確認や資格証明書をダウンロードできます。なのでこの『CertView』を利用する為、『Oracleアカウント』を作成しなければいけません。
上記リンクよりアクセスし、画面下部の『アカウント作成』ボタンをクリックし必要事項を入力してアカウントを作って下さい。
※Oracleアカウント登録後、上記の画面からOracleへサインインする際『ユーザー名』を求められます。この『ユーザー名』はOracleアカウント作成時に登録したメールアドレスになります。
■ ③ピアソンVUEから試験会場の予約
※試験会場で受験する場合を記述しています。
いよいよ試験会場を予約していきます。(やっとか。。。)
先ほど、手順①で作成したアカウントを用いて『ピアソンVUE』にサインインして下さい。![]()
画面右側にサインインボタンがあります。
サインインしたら、『試験の表示』をクリック。
![]()
『試験の検索』に「Java SE 7/8 Bronze」と入力し検索。
![]()
「ONLINE」ではない方を選択。
![]()
その後は『試験を予約する』ボタンを押し、『受験予約』に進んで下さい。お好きな試験会場と日時を選び、個人情報やクレジットカード情報を入力します。先に進むとキャンペーンコードの入力欄がありますので、メモした方は入力を忘れずに。
⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎
以上でJava Bronze試験の申し込みは終了です!パチパチパチ!!
⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎⭐︎
■ ④受験
受験に際しての注意事項を記述します。
- 最低でも試験開始15分前に到着する。
- 本人確認書類を2点提示します。ちなみに自分の場合は『運転免許証』と『保険証』を提示しました。下記リンクに詳細が載っています。
本人確認書類について- 試験前に顔写真を撮るので、お顔を整えるのを忘れずに。
■ ⑤オラクルHPにて合否結果確認
現在執筆中。。。
■ ⑥最後に。。。
私事ですがブログ記事初投稿になります。
こんなちっぽけな体験談ですが、申し込みで挫折してしまうのは勿体無いと思い、受験される方のお役に立てればと思いこの記事を書かせて頂きました。
拙い文章も多かったと思いますが最後まで読んで頂きありがとうございました。
(最後に「いいね!」ボタン押して頂けたら嬉しいですボソッ)
受験頑張ってね!
- 投稿日:2019-05-07T16:27:52+09:00
java プロパティファイル
ファイル名は
.propertiesで終わらせる。src/hoge/foo/sample.properties# キー = 値 url = jdbc:postgresql:mydatabase user = student password = himituResourceBundleを使って中身を読み込む。
import java.util.ResourceBundle; ResourceBundle rb = ResourceBundle.getBundle("hoge/foo/sample"); // ファイル名 System.out.println(rb.getString("password")); // 結果:himitsu引数のファイル名は
src/sample.propertiesなら getBundle(sample)。src/hoge/foo/sample.propertiesなら getBundle(hoge/foo/sample)。
- 投稿日:2019-05-07T12:11:50+09:00
JavaのWebアプリケーションでサーブレットが読み込めなかった話
ちょっとしたデータ編集作業の用が生じ、簡易的なWebアプリケーションを作成してGUIで進めることにした。
個人的に使うので、保守性も何も気にする必要がない。とにかくさくっと作れればよい。
端末にEclipseが入っていたので、EclipseでJavaで動的Webアプリを作るぞ。ポチポチポチっとプロジェクトやServletをテンプレートから作成して、あとはにょろにょろっとコーディングじゃい。しかしjspからajaxでJavaサーブレットにアクセスしようにも応答しない。これはおおいに困った。
web.xml<web-app> <servlet> <servlet-name>test</servlet-name> <servlet-class>TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping> </web-app>test.js$ajax({ url:'/testapp/test', type:'POST', data:{ 'param1':'aaa', 'param2':'bbb' } }) .done((data)=> {console.log("done!");}) .fail((data)=> {console.log("fail!");}) .always((data)=> {console.log("always!");})TestServlet.java@WebServlet("/TestServlet") public class TestServlet extends HttpServlet { public TestServlet() {...} protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {...} protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {...} }console.logfail! always!どうやってもTestServletに届かず、何が原因かさっぱりわからない。
で、よくよく見たところ、TestServlet.javaを自動生成した際に入っていた@WebServlet("/TestServlet")が気になった。
やれやれ。僕は検索した。げろげろ。web.xmlってわざわざ書く必要ないのね……。
web.xmlの記述を削除して、ajaxのurlを'/testapp/TestServlet'にしたら難なく動いた。普段フレームワークやテンプレートに頼りっきりだと化石エンジニアになってしまいますね。日頃の研鑽はいざという瞬間の開発速度に如実に現れますな。
- 投稿日:2019-05-07T02:22:18+09:00
(Java)継承により値要素を追加したクラスのequals()実装方法
はじめに
下記のような継承関係にあるクラスのequals()メソッドを普通に実装しただけでは、equals()が満たすべき性質が満たせない(補足1)ということが知られている。*1参考
同参考文献にはその回避策も記載されている(補足2)が、別の文献*2参考からの解決策を記載する。
結論
下記のような2引数のequals()静的メソッドを作成し、それを通してObject.equals()を呼び出す。重要なのは両クラスのequals()を呼び出す部分。これはこのクラス構成以外でも使用可能。全文
ちなみに標準APIに Objects.equals(Object, Object) があるが、左辺のequals()しか呼び出していない。ObjectUtil.javafinal class ObjectUtil { private ObjectUtil() { throw new AssertionError("Not intended to be instanciated"); } public static boolean equals(final Object lhs, final Object rhs) { // 左辺・右辺の参照先が同じなら同一 if (lhs == rhs) return true; // 左辺・右辺の参照先が共にnullなら同一 if (lhs == null && rhs == null) return true; // 左辺・右辺の参照先のどちらかがnullなら不同 if (lhs == null || rhs == null) return false; // 左辺・右辺の参照先が同一クラスならそのクラスのequals()を返却する if (lhs.getClass() == rhs.getClass()) return lhs.equals(rhs); // 左辺・右辺の参照先が別のクラスなら両クラスのequals()のandを返却する return lhs.equals(rhs) && rhs.equals(lhs); } }補足1
// 1. 対称性が満たせない // -> 右辺・左辺を入れ替えると結果が異なる // 上図のクラス構成を用いた例 Point1D p1 = new Point1D(0); Point2D p2 = new Point2D(0, 0); assert(p1.equals(p2) == true); // x値しか比較しないためtrue assert(p2.equals(p1) == false); // Point2Dのinstanceであることを判定するためfalse // 2. 推移性が満たせない // -> p1 == p2 && p2 == p3 の場合でも、p1 == p3 とならない // 上図のクラス構成を用いた例 // 前提: Point2D.equals() 仕様に下記を追加しておく // 引数が、Point1Dまたはそのサブクラス、かつx値が同じ場合でも、true Point2D p1 = new Point2D(0, 0); Point1D p2 = new Point1D(0); Point2D p3 = new Point2D(0, 1); assert(p1.equals(p2) == true); // 新仕様のためtrue assert(p2.equals(p3) == true); // x値の比較しないためtrue assert(p1.equals(p3) == false); // x,y値を比較するためfalse // 3. リスコフの置換原則が満たせない // -> サブクラスでも同様の動きが想定されるが、そうならない // 上図のクラス構成を用いた例 // 前提1: Point1D.equals() 仕様を下記のように変更する // 引数が、Point1D、かつx値が同じ場合のみ、true // 前提2: CountingPoint1D.equals() 仕様を下記のように変更する // 引数が、CountingPoint1D、かつx値が同じ場合のみ、true Point1D p1 = new Point(0); CountingPoint1D cp1 = new CountingPoint(0); CountingPoint1D cp2 = new CountingPoint(0); List<CountingPoint1D> list = Arrays.asList(cp1); assert(list.contains(cp2) == true); // cp1 == cp2 のためtrue assert(list.contains(p1) == false); // 前提1のためfalse補足2
// 継承ではなくコンポジションを用いる // 例 class Point2D { private final Point1D p1; private final int y; ... @Override public boolean equals(Object o) { if (!(o instanceof Point2D)) return false; Point2D rhs = (Point2D)o; return p1.equals(rhs.p1) && y.equals(rhs.y); } }*1
ジョシュア・ブロック(平成26年)「Effective Java 第2版」丸善. 項目8
*2
アンドレイ・アレキサンドレスク(2013)「プログラミング言語D」翔泳社. 6.8.3
- 投稿日:2019-05-07T02:18:26+09:00
東京公共交通オープンデータから鉄道網のForce-Graphを作ってみる
東京公共交通オープンデータチャレンジ
東京公共交通オープンデータチャレンジでは、首都圏の主要な公共交通機関データがオープンデータとして公開されており、「第3回東京公共交通オープンデータチャレンジ」にエントリーすれば、アクセストークンが発行されAPIを使用することができます。
GW中にサイトを知り、とりあえずエントリーしてみました。概要
地方在住のため、首都圏の鉄道網をあまり知りません。
このため、情報整理と東京オープンデータチャレンジのAPI、D3.jsの勉強を兼ねて首都圏鉄道網のForce-Graphを作ってみました。
内容・手順
1.ODPT Train APIでデータ整理
「ODPT Train API」で路線と駅の情報を収集し、javaでデータを整理しました。
ソースコードを以下に示します。
なお、httpクライアントはOkHttp、JSONの処理はGsonを使用しています。
「ODPT Train API」のレスポンス(JSON)から路線の「路線名」、「固有識別子」、「路線の駅」を抽出してRaillineインスタンスを生成し、路線内の駅の「駅名」、「固有識別子」、「関連する路線の識別子」からStationインスタンスを生成しています。駅は路線毎に固有識別子が割り当てられているようなので、「駅名(和名)」で情報を集約しています。import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class Test { private static final String URL_TOKYO_CH="https://api-tokyochallenge.odpt.org/api/v4/"; private static final String KEY_TOKYO_CH="アクセストークン"; @SuppressWarnings({ "unused", "rawtypes", "unchecked" }) public static void main(String[] args){ OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder(); okHttpBuilder.connectTimeout(20, TimeUnit.SECONDS); okHttpBuilder.readTimeout(20, TimeUnit.SECONDS); okHttpBuilder.writeTimeout(20, TimeUnit.SECONDS); OkHttpClient client=okHttpBuilder.build(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); List<Map> list=trainAPI(client,gson,"odpt:Railway"); Map<String,Station> stations=new HashMap<String,Station>(); List<RailLine> raillines=new ArrayList<RailLine>(); for(Map map : list){ RailLine line=new RailLine(); line.name_ja=((Map)map.get("odpt:railwayTitle")).get("ja").toString(); line.name_en=((Map)map.get("odpt:railwayTitle")).get("en").toString(); line.sameAs=map.get("owl:sameAs").toString(); line.operator=map.get("odpt:operator").toString(); List<Map> ll=(List<Map>)map.get("odpt:stationOrder"); for(Map o : ll){ String st=((Map)o.get("odpt:stationTitle")).get("ja").toString(); line.stations.add(st); if(stations.containsKey(st)){ Station s=stations.get(st); s.lines.add(line.name_ja); }else{ Station s=new Station(); s.sameAs=o.get("owl:sameAs").toString(); s.name_ja=((Map)o.get("odpt:stationTitle")).get("ja").toString(); s.name_en=((Map)o.get("odpt:stationTitle")).get("en").toString(); s.lines.add(line.sameAs); stations.put(s.name_ja, s); } } raillines.add(line); } Map<String,Object> ret=new HashMap<String,Object>(); ret.put("stations", stations); ret.put("raillines", raillines); File f=new File("railway.json"); BufferedWriter bw=null; try{ bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f),"UTF-8")); bw.write(gson.toJson(ret)); bw.flush(); bw.close(); bw=null; }catch(Exception e){ e.printStackTrace(); }finally{ if(bw!=null){ try{bw.close();}catch(Exception e){} } } } @SuppressWarnings("unchecked") private static List<Map> trainAPI(OkHttpClient client,Gson gson,String odpc){ String url=URL_TOKYO_CH+odpc+"?acl:consumerKey="+KEY_TOKYO_CH; System.out.println(url); try{ Request request = new Request.Builder() .url(url) .get() .build(); Response response = client.newCall(request).execute(); return gson.fromJson(response.body().string(), List.class); }catch(Exception e){ e.printStackTrace(); return null; } } static class Station{ public String name_ja; public String name_en; public String sameAs; public List<String> lines=new ArrayList<String>(); } static class RailLine{ public String name_ja; public String name_en; public String sameAs; public String operator; public List<String> stations=new ArrayList<String>(); } }2.出力したJSON
上記のコードを実行すると、以下のJSONファイルが出力されます。
これを眺めていると、「こういう路線があって、こういう駅があるんだー」となんとなく首都圏の鉄道網について理解が増した気がします。{ "raillines": [ { "name_ja": "東京さくらトラム(都電荒川線)", "name_en": "Tokyo Sakura Tram (Arakawa Line)", "sameAs": "odpt.Railway:Toei.Arakawa", "operator": "odpt.Operator:Toei", "stations": [ "三ノ輪橋", "荒川一中前", "荒川区役所前", "荒川二丁目", "荒川七丁目", "町屋駅前", "町屋二丁目", "東尾久三丁目", "熊野前", "宮ノ前", "小台", "荒川遊園地前", "荒川車庫前", "梶原", "栄町", "王子駅前", "飛鳥山", "滝野川一丁目", "西ヶ原四丁目", "新庚申塚", "庚申塚", "巣鴨新田", "大塚駅前", "向原", "東池袋四丁目", "都電雑司ヶ谷", "鬼子母神前", "学習院下", "面影橋", "早稲田" ] }, /***** 省略 *******/ "stations": { "世良田": { "name_ja": "世良田", "name_en": "Serada", "sameAs": "世良田", "lines": [ "odpt.Railway:Tobu.Isesaki" ] }, "東所沢": { "name_ja": "東所沢", "name_en": "Higashi-Tokorozawa", "sameAs": "東所沢", "lines": [ "odpt.Railway:JR-East.Musashino" ] }, /***** 省略 *******/3.D3.jsでForce-Graphを表示
路線・駅の情報ををD3.jsに読み込み、Force-Graphを生成してみました。
初めてD3.jsでForce-Graphを作りましたが、javaでGraphLayoutを書く時と比べて、すごく簡単に作れるのに驚きました。
D3.jsすごい。<!DOCTYPE html> <html> <head> <title>tokyo-challenge-test</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.2/d3.min.js"></script> </head> <body> <svg></svg> <script type="text/javascript"> let width = 1200; let height = 800; const loadData = () => { d3.json("railway.json").then(function(json) { createMap(json); }); }; const createMap=(json)=>{ const rail=json.raillines; const station=json.stations; let nodes=[]; let links=[]; let check={}; let idv=0; for(let i=0;i<rail.length;i++){ let sts=rail[i].stations; let tmp=[]; for(let j=0;j<sts.length;j++){ if(!check[sts[j]]){ let p={id:idv++,label:station[sts[j]].name_ja,val:1}; tmp.push(p); nodes.push(p); check[sts[j]]=p; }else{ check[sts[j]].val=check[sts[j]].val+1; tmp.push(check[sts[j]]); } } for(let i=1;i<tmp.length;i++){ let l={source:tmp[i-1].id,target:tmp[i].id}; links.push(l); } } const svg = d3.select("svg").attr("width",width).attr("height",height); const link = d3.select("svg") .selectAll("line") .data(links) .enter() .append("line") .attr("stroke-width", 1) .attr("stroke", "#ccc"); const node = d3.select("svg") .selectAll("g") .data(nodes) .enter() .append("circle") .attr("r",function(d){return d.val*5;}) .attr("fill", "orange") .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); const label = d3.select("svg") .selectAll("g") .data(nodes) .enter() .append("text") .attr("text-anchor", "middle") .attr("dominant-baseline", "middle") .style("fill", "steelblue") .style("font-size", "9px") .text(function(d){return d.label;}); const simulation = d3.forceSimulation() .force("link", d3.forceLink()) .force("center", d3.forceCenter(600, 450)) .force("charge", d3.forceManyBody().strength(-8)) .force("x", d3.forceX().strength(0.05).x(width / 2)) .force("y", d3.forceY().strength(0.05).y(height / 2)); simulation.nodes(nodes).on("tick", ticked); simulation.force("link").links(links); function ticked() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }); label.attr("x", function(d) { return d.x; }) .attr("y", function(d) { return d.y; }); } function dragstarted(d) { if(!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if(!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } const zoom = d3.zoom() .scaleExtent([1/4,4]) .on('zoom', function(){ node.attr("transform", d3.event.transform); link.attr("transform", d3.event.transform); label.attr("transform", d3.event.transform); }); svg.call(zoom); } loadData(); </script> </body> </html>最後に
駅・路線が多すぎて、よくわからないグラフになりましたが、上野駅よりも新宿駅、渋谷駅の方が乗り入れている路線が多いのは意外でした。
もう少し表示を工夫したり、各駅間の距離や運賃等のデータを追加して遊んでみたいと思っています。
- 投稿日:2019-05-07T02:12:10+09:00
Javaのマルチスレッドを一からやって苦労したので整理する
きっかけ
技術アップのため、ジャストシステムさんが作られた「Java100本ノック」をやった時のこと。
https://github.com/JustSystems/java-100practices/blob/masterマルチスレッド関連の問題で詰まりに詰まり、1問こなすのに2時間かかるとかいうひどい状態だった。
しかも色々ググってクリアしても色々よくわからない部分があったので、備忘録を兼ねて整理する。昔授業でもやったけど、どう使えばいいのかすらよくわからなかったので記憶に残らなかった…
この記事では上記の100本ノックのマルチスレッドに関する問題を取り上げる。
おことわり
勉強中なので誤りがある可能性があります
なるべく誤ったことを書かないよう努力はしていますが、ここ違うよ!っていうコメントいただけるととても喜びます。また、先述のJava100本ノックを見たことがある前提の記述が一部あります。ご了承ください。
Javaにおけるマルチスレッド基本
説明だとここがわかりやすかった。
https://eng-entrance.com/java-threadThreadクラスを継承、もしくはRunnableインターフェースを実装したクラスの中で、run()メソッドを
オーバーライドしてマルチスレッドで行いたい処理を記述するというのが基本形。
(クラス定義しなくてもThread作れたりする…後述)サンプルを作ってみる。
それぞれ違う文字列を標準出力に出すスレッドを2つ作って、並列で動かす。ThreadSample.java/** * 基本的なスレッド * */ public class ThreadSample extends Thread{ // run()をオーバーライドして、その中に処理を書く @Override public void run() { for(int i = 0; i < 300; i++) { System.out.println(" マルチ-"+i); } } }ThreadSampleMain.java/** * 基本的なスレッドの実行用クラス * ThreadSampleと並列実行する * */ public class ThreadSampleMain { public static void main(String[] args) { // Threadを継承したクラスをnewする ThreadSample thread = new ThreadSample(); // .start()メソッドを実行すると、run()に書かれた処理が動く thread.start(); for(int i=0; i < 300; i++) { System.out.println("メイン-"+i); } } }動かしてみるとこんな感じ。
(略) メイン-297 マルチ-0 # ここでMainが終わっていないのにもう片方のスレッドの内容が出ている マルチ-1 マルチ-2 マルチ-3 マルチ-4 マルチ-5 マルチ-6 マルチ-7 マルチ-8 マルチ-9 マルチ-10 マルチ-11 マルチ-12 マルチ-13 (略)しかし、この一番単純なスレッドは、実行するごとにどこでThreadSampleの方のスレッドが実行されるかとか、そういうことが保証されていない。
これを保証できる仕組みが色々ある。名前の宣言しなくてもスレッド使えるよ
さっきのコードでthreadと言う名前でThreadSampleのインスタンスを作ったけど、名前宣言しなくても使えるよパターン
Mainを書き換えるとこんな感じ。ThreadSampleMain2public class ThreadSampleMain2 { public static void main(String[] args) { // Threadを継承したクラスをnewしてそのまま開始 new ThreadSample().start(); for(int i=0; i < 300; i++) { System.out.println("メイン-"+i); } } }100本ノックの回答例を見ているとこういう記法が多い。
恥ずかしながらJavaウン年やっててnewしたオブジェクトにそのままメソッドの実行処理を書けるの知らなかった…そもそもクラスにしなくても使えるよ
Java100本ノック問041の回答例などに見られるパターン
ThreadSampleMain3public class ThreadSampleMain3 { public static void main(String[] args) { // newした無名のスレッドをそのまま実行する new Thread() { @Override public void run() { for(int i = 0; i < 300; i++) { System.out.println(" マルチ-"+i); } } }.start(); for(int i=0; i < 300; i++) { System.out.println("メイン-"+i); } } }実際にエンタープライズ系のシステムで使われているのは今のところ見たことがないけど、こういう書き方もできるらしい。
Threadインスタンスの中にRunnableを入れる?
Java100本ノックの回答例を見ていると、Runnableを実装したクラスを直接start()せずに、Threadクラスのインスタンスの引数として代入して、そのインスタンスにstart()しているパターンがすごく多い。
例えば、以下、問040の回答例より引用
public class Answer040 implements Runnable { /** * 040の解答です. * キャッチされない例外のスタックトレースを * 現在時刻とともに標準エラー出力する. * * @param arguments 使用しません. */ public static void main(final String[] args) { Thread thread = new Thread(new Answer040()); // UncaughtExceptionHandlerを実装したハンドラをsetUncaughtExceptionメソッドで登録. thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); // メインスレッドの実行. thread.start(); } /** * スレッドを実行. */ public void run() { // スリープ. try { Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } Thread subThread = new Thread(new SubThread()); // UncaughtExceptionHandlerを実装したハンドラをサブスレッドに紐付ける. subThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); // サブスレッドの実行. subThread.start(); } }この回答例を読んで疑問が浮かんだ。
- なぜrun()をオーバーライドしているのか? subThread()を直接newして、それにstart()してはダメなのか?
- run()の前にあるスリープはなんなのか?
例えば、こんな感じに書き換えても同じことができるはずでは?
public class Answer040-2 implements Runnable { public static void main(final String[] args) { // 注:SubThreadはRunnableをimplementsしたクラス SubThread sub = new SubThread(); sb.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); sub.start(); }結論から言うとできません。(コンパイルエラーになる)
ThreadにRunnable実装インスタンスを登録する理由
先ほどのコードでいうとここ
public class Answer040 implements Runnable { /** * 040の解答です. * キャッチされない例外のスタックトレースを * 現在時刻とともに標準エラー出力する. * * @param arguments 使用しません. */ public static void main(final String[] args) { Thread thread = new Thread(new Answer040()); // ←ここ (略)そもそもThreadインスタンスの引数にRunnableを継承したインスタンスを入れるというのはどういう処理なのか、Javadocを見てみる。
public Thread(Runnable target)
新しいThreadオブジェクトを割り当てます。このコンストラクタは、Thread (null, target, gname) (gnameは新たに生成される名前)と同じ効果を持ちます。自動的に作成される名前は、nを整数とすると"Thread-"+nの形式を取ります。
パラメータ:
target - このスレッドの起動時に呼び出されるrunメソッドを含むオブジェクト。nullの場合、このクラスのrunメソッドは何も行わない。なるほど。同じ作用をするコンストラクタがあると。そちらも見てみる。
public Thread(ThreadGroup group,
Runnable target,
String name)
その実行オブジェクトとしてtarget、名前として指定されたnameを持つ、groupによって参照されるスレッド・グループに属するような、新しいThreadオブジェクトを割り当てます。
セキュリティ・マネージャが存在する場合は、checkAccessメソッドがThreadGroupをその引数に指定して呼び出されます。
さらに、getContextClassLoaderまたはsetContextClassLoaderメソッドをオーバーライドしたサブクラスのコンストラクタから直接的または間接的に呼び出された場合、そのcheckPermissionメソッドがRuntimePermission("enableContextClassLoaderOverride")アクセス権で呼び出されます。つまりThreadコンストラクタには、スレッドグループ(ThreadGroup)や、スレッド名(String)を省略できるコンストラクタがあり、先ほどのコードで触れたRunnableを引数として持つコンストラクタは、スレッドグループとスレッド名を自動割り当てにしている。
それで、このコンストラクタは何をしているかと言うと、
新しいThreadオブジェクトを割り当てます。
ということらしい。
Threadオブジェクトを割り当てますとはどういうことなのか調べてみる。
https://www.task-notes.com/entry/20151031/1446260400上記サイトによるとThreadクラスは引数として渡されたRunnableオブジェクトのrun()を動作させるような実装になっている。
すなわち引数のRunnableに自作のスレッドを渡すというのは、これをrun()で実行してねと言うこと。
また、extends Threadよりも(run()以外をオーバーライドしない限り)implements Runnableの方が良いことがわかった。
恐らく、多重継承できないためextendsは控えた方がいいのもあるだろう。これらから、今の時点で得られた結論としては直接newせずThreadに対して渡すのはjava.lang.Threadのスーパークラスのrun()メソッドを利用するためだということと、後述するスレッドグループやExceptionHandlerのメソッドの都合だと推測している。
サブスレッド生成前にスリープする理由
先ほどの回答例のここ
(略) /** * スレッドを実行. */ public void run() { // スリープ. try { Thread.sleep(500L); // ←ここ } catch (InterruptedException e) { e.printStackTrace(); } Thread subThread = new Thread(new SubThread()); // UncaughtExceptionHandlerを実装したハンドラをサブスレッドに紐付ける. subThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); // サブスレッドの実行. subThread.start(); }この処理はたびたび出てきて、職場の先輩も「なぜかわからんけどこれ入れないと上手く動かない」と言っていた。
色々ググってみたが出てこず。mainメソッドに直接書かれたThreadがstart()された後、一瞬待たないとsetUncaughtExceptionHandler()が成功しないことがあるからでは? と推測している。
ひとまず、今のところはおまじないとして覚えておくことに。余談:setUncaughtExceptionHandler()について
これは先ほどのJava100本ノック問040で使用する必要のあるメソッド。
UncaughtExceptionHandlerというインターフェースを実装したクラスを作っておき、その中でuncaughtException(Thread,Trowable)をオーバーライドしておくと、catchできなかった例外をスレッドが吐いたとき、スレッドが異常終了する直前にこのメソッドが呼ばれる。
例えば例外のログ出力などに役立つ
参考:https://javazuki.com/articles/uncaught-exception-handler-usage.htmlそしてこれは、Java.lang.Threadのメソッドである。Runnableには無いので、Threadに対してRunnable実装のインスタンスを入れて使うのがいいようだ。
先ほどの章でいきなりsubThreadクラスをnewして出来ないのは、ないメソッドを呼ぼうとしていたのでコンパイルエラーになっていた。スレッドグループへの登録
スレッドグループ:スレッドをグループ化して、アクティブ数の監視や複数スレッドの一時停止などができる仕組み
スレッドグループを使うと、登録時にスレッドに名前を設定できて、スレッドグループの中の名前というスコープで管理できるので、普通の変数と混ざらず良い感じらしい。100本ノック問35の答えなどで出てくる記法
https://github.com/JustSystems/java-100practices/blob/master/contents/035/README.md以下、問35の答えより引用
public final class Answer035 implements Runnable { /* スレッドグループA. */ private static ThreadGroup groupA = new ThreadGroup("GroupA"); /* スレッドグループB. */ private static ThreadGroup groupB = new ThreadGroup("GroupB"); /** * グループA,Bのスレッドを各100スレッド実行する. */ @Override public void run() { for (int i = 0; i < 100; i++) { new Thread(groupA, new ThreadRun(), "thread" + i).start(); } for (int i = 0; i < 100; i++) { new Thread(groupB, new ThreadRun(), "thread" + i).start(); } } /** * 各スレッドグループにおけるアクティブスレッド数を出力する. * * @param point カウント回数 */ public static void printActiveCount(int point) { System.out.println("Active Threads in Thread Group " + groupA.getName() + " at point(" + point + "):" + " " + groupA.activeCount()); System.out.println("Active Threads in Thread Group " + groupB.getName() + " at point(" + point + "):" + " " + groupB.activeCount()); } /** * 035の解答です. * スレッドグループごとにスレッドを実行し、 * 各スレッドにてアクティブであるスレッド数を標準出力する. * * @param arguments 使用しません. */ public static void main(String[] args) throws InterruptedException { /* 新しいスレッドを割り当てる. */ Thread thread = new Thread(new Answer035()); /* スレッドを実行する. */ thread.start(); // アクティブスレッド数を出力する. for (int i = 1 ;; i++) { printActiveCount(i); thread.sleep(1000L); // アクティブスレッドが0になったときにループを抜ける. if (groupA.activeCount() == 0 && groupB.activeCount() == 0) { break; } } } }これのrun()のオーバーライドの部分。
これは、スレッドグループに、自作のThread継承クラスThreadRunを登録しようとしている。
Threadグループで管理するときは、Threadクラスのコンストラクタのうち、第二引数にThreadを取れるものを使って、そこにnewすることで複数個のスレッドをいちいち名前宣言しなくても使える。スレッド同士での排他処理
複数のスレッドで、この処理はこの処理より前にやったら困るって時とかに使う。
色々やり方があって、この辺から一気に複雑になるので、やり方別にまとめていく用語整理
スレッドセーフ
ある処理と、ある処理は並列実行しても問題を起こさないよという意味
「スレッドセーフなメソッド」みたいな感じ。さっきのように全然関係のない変数を出力するだけならいいけど、static変数を参照するメソッドと書き換えるメソッドが一緒にあって、書き換える前に参照したりしてNullPointerException吐いたりとかするのはスレッドセーフじゃない。
スレッドセーフにさせるための処理方法が色々ある。1つ実行してる間はもう片方を止めるとか。
参考元:https://wa3.i-3-i.info/word12456.htmlスレッドプール
スレッドを新しく作るより、使いまわした方が早いため、生成済みのスレッドを使いまわす手法のこと。
スレッドプールの生成はExecutor(ExecutorService)で行う。synchronized使うパターン
javaのメソッド宣言に"synchronized"というものをつけて書いた処理。
これが付いたメソッド同士は、同時に実行されなくなりnotify()やwait()などでメソッドで実行を制御することができる。
自分語りだけど、いい加減な勉強していた学生時代でもこの2つだけは覚えていたので、「マルチスレッドとかsynchronizedすればええやん」とかいう頭の悪いことを言っていた苦い記憶がある。Java100本ノックでもこれは出てくるが、やってみるとはまったのでメモ。
やってはいけないパターン
Java100本ノック問041より
wait()とnotify()を用いて、「1から10000までの整数を加算して結果をグローバル変数に格納する」スレッドAと、「スレッドAの動作終了後グローバル変数の値を標準出力に出力する」スレッドBと、スレッドA,Bをほぼ同時に開始するプログラムとを実装せよ。
スレッドA:Q41_ThreadA.java
スレッドB:Q41_ThreadB.java
処理2つとグローバル変数のクラス:Q41_number.java
実行用:Q41.java
として以下のように実装した。Q41_numberpublic class Q41_number { public static long number = 0; public synchronized void addNumber() { System.out.println("add number..."); for(long i = 1; i <= 10000; i++) { number += i; } System.out.println("end"); // 終了したことを通知する notify(); } public synchronized void showNumber() { try { System.out.println("waiting..."); wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(number); } }Q41_ThreadApublic class Q41_ThreadA implements Runnable{ Q41_number q41 = new Q41_number(); @Override public void run() { // wait()する前にnotify()すると無限ループするので、少し待つ try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } q41.addNumber(); } }Q41_ThreadBpublic class Q41_ThreadB implements Runnable{ Q41_number q41 = new Q41_number(); @Override public void run() { // ThreadAより僅かに先に動くようにする try { Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } q41.showNumber(); } }Q41public class Q41 { public static void main(String[] args) { Thread threadA = new Thread(new Q41_ThreadA()); Thread threadB = new Thread(new Q41_ThreadB()); // 実行する threadA.start(); threadB.start(); } }実行するとQ41_number#showNumber()のwait()のところで止まって動かなくなる
理由
http://www.ne.jp/asahi/hishidama/home/tech/java/thread.html
このサイトのsynchronizedの章のコメントに書いていた。//↑同一インスタンスのfunc1()とfunc2()を別スレッドから同時に呼び出しても、片方ずつしか実行されない。
// 同一インスタンスのfunc1()とfunc1()を別スレッドから呼び出すのも同様。
// 別インスタンスであれば、ロックオブジェクトが異なるので排他されず、同時に実行される。さっきのコードだと、ThreadAとThreadBが持つQ41_numberのインスタンス自体が違う。
synchronizedなメソッドを持つインスタンスは複数のスレッドで共用するようにすること。volatileについて
フィールドの定義時に設定できる。
これをつけると、そのフィールドはコンパイラの最適化の時にもキャッシュされなくなる。
ふわふわした言い方だけど、「このフィールド別スレッドが書き換えてるはずなのにデバッグしてみたら書き換わってないんだけど…」と言う時にキャッシュが使用されていることを疑ってみる。キャッシュやそれによっておこる現象は以下のサイトが詳しい。
https://www.sejuku.net/blog/65848Java100本ノックだと問017がこれを使っている。
スレッド単位での実行順定義や終了待機
複数個のスレッドがあって、それらにスレッド単位で実行順をつけたりする時に役立つ
java.util.concurrent.CountDownLatch を使う。
これは定義した時に設定する数値がゼロになるまで待機するawait()と、数値を1減らすcountDownLatch()を利用して、すべてのスレッドが終わるまで待機などを実現する。









