20201012のJavaに関する記事は13件です。

Mockitoと仲良くなろうとしたお話

Mokitと仲良くなろうとしたお話

初投稿です。
皆さんは「Mockito」使っていますか?

業務でAndroid Studioを使用してアプリケーションを作成し、UnitTestを行う際にMockitoとケンカしたので、Mockitoと仲良くするため、そして自分の備忘録として投稿します。

使用環境

  • macOS Catalina(10.15.6)
  • Android Studio 4.0.1
  • Android 9.0(sdk:28)
  • Mockito Core 3.5.9
  • Mockito Inline 3.5.9
  • Robolectric 4.3.1

Mockitoとケンカした部分について

作成していたアプリケーションはaidlでインターフェースの提供を受けており、サービスとのbindを行うアプリケーションでした。
初めはRobolectricのShadowApplication.setComponentNameAndServiceForBindService()を使用してMock化したIBinderを渡していましたが、IBinder.Stub.asInterface()でインターフェースを取り出す際に別のオブジェクトになってしまうため、うまくMock化できていませんでした。
(単純に自分のMockの仕方が悪かったのかもしれないです。)

Mockito 3.4.0からstaticなメソッドもMock化できるようになったようなのですが、Webに有益な情報が見つからなかったので、以下に示します。

staticなメソッドのMock化方法

IBinder.Stub.asInterface()など、staticなメソッドのMock化方法です。
以下のような使い方でMock化できます。

final IMyService mockMyService = mock(IMyService.class); // ⭐︎1
try(final MockedStatic<IMyService.Stub> mockedStatic = mockStatic(IMyService.Stub.class)) {
    // ↓IMyService.Stub.asInterface()の戻り値がmock化したIMyService(⭐︎1)になります。
    mockedStatic.when(() -> IMyService.Stub.asInterface(any())).thenReturn(mockMyService);
    // 以下でActivity内などでbindService()してテストしてください。
    final ActivityController<MainActivity> controller = buildActivity(MainActivity.class);
    controller.create;
    ...
}

上記操作を行うことで、staticなメソッドのMock化ができます。
なお、上記ではtry-with-resourcesで記載しているのですが、try-with-resourcesで記載しない場合は、close()を行ってください。

final MockedStatic<IMyService.Stub> mockedStatic = mockStatic(IMyService.Stub.class);
...
mockedStatic.close();

ついでに

メソッド内でnewされるクラスのMock化もMockitoでできます。

final MockedConstruction<SubClass> mockedConstruction = mockConstruction(SubClass.class);
// 以下でnewされたクラスを取得することができます
// 取得したクラスはMock化されているので、when()を使って戻り値などを変化させることができます。
List<SubClass> constructed = mockedConstruction.constructed();
mockedConstruction.close();

最後に

初投稿なのでうまく説明できているか正直自信がありません。
少しでも誰かのためになれれば幸いです。
今回紹介したMock化方法はほんの一部です。
MockedStaticMockedConstructionもコンストラクタが何種類か存在するので、Mockitoの公式ドキュメントを参考にしつつ、Android StudioなどのIDEで色々と試してみてください。

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

mac vscodeでJava構築

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

重複チェックロジックを書くにはどのようにすれば可読性が高くなるのか

重複しているか調べたいデータをList型で所持する。DBのレコードならSQLで取得してくる。
そのうえで例えば文字列の重複チェックなら、

sample.java
String searchStr = '調べたい文字列';
List<String> dataList =  '調べる対象のデータ';

if(dataList.contains(searchStr)){
   // 重複したときの処理

} else {
   // 重複しなかったときの処理

}

このように書くとあとで見たときにわかりやすいのではないだろうか。

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

【Java】Java8から追加されたTime APIを使って10日後の日付を取得・表示

今までのおさらい

新しく追加されたTime APIを使用してみる

新しく追加されたAPI

クラス 時間 ゾーン 用途
ZonedDateTime 厳密な日時情報
LocalDateTime X 日常使う日時情報
LocalDate X X 誕生日
LocalTime X X X X アラーム時刻など
Year X X X X 著作発表年など
YearMonth X X X カード有効期限など
Month X X X X 決算月など
MonthDay X X X 日本の祝日など

このうちLocalDateを使って10日後を計算してみました

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

// Java8から追加されたTime APIを使用
// 10日後の日付を取得する
public class TenDaysCalendarNewAPI {

    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        LocalDate future = now.plusDays(10);

        // フォーマットを指定せずに表示
        System.out.println("■ LocalDate型をそのまま表示");
        System.out.println(now);
        System.out.println(future);
        System.out.println();

        // フォーマットを指定して表示
        System.out.println("■ DateTimeFormatterを使って表示");
        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
        System.out.println(now.format(f));
        System.out.println(future.format(f));
        System.out.println();

        System.out.println("■ SimpleDateFormatを使って表示");
        SimpleDateFormat f2 = new SimpleDateFormat("yyyy年MM月dd日");
        System.out.println(f.format(now));
        System.out.println(f.format(future));
    }

}

実行結果
■ LocalDate型をそのまま表示
2020-10-12
2020-10-22

■ DateTimeFormatterを使って表示
2020年10月12日
2020年10月22日

■ SimpleDateFormatを使って表示
2020年10月12日
2020年10月22日

新しいAPIの方が扱いやすいですね

自分の直感的には扱いやすいと感じました。それはformat指定の際にですね、

SimpleDateFormatは、SimpleDateFormatに日付情報を渡す
DateTimeFormatterは、日付情報にDateTimeFormatterの形式を指定する

という部分ですね。

何を入っているかわからんくなってきますが、

自分的には

System.out.println(f.format(now));

という部分がわかりにくいというか、そういうやり方なんでしかたないですが、なんとなく釈然としない感じがありました。

 System.out.println(now.format(f));

のように、nowの日付情報をformatメソッドの引数に渡した形式の情報に変換するというようなイメージの方がわかりやすいということでした!

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

【自分用】JSPファイルとサーブレットクラスを使用したブラウザ表示

初めてJSPとサーブレットクラスの連結がうまくいったので記録。

【参考リンク】
・Html プルダウンメニューの表示について
(https://murashun.jp/blog/20200128-66.html)
・Html 複数行のテキスト入力フォーム作成
(http://www.htmq.com/html/textarea.shtml)

【JSPファイル】

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>お問い合わせフォーム</title>
</head>
<body>


<%--formを挿入する準備 --%>
<form action="/example/P157_s" method="post">

<%--お名前入力タブ --%>
    名前<input type="text" name="name"><br>

<%--お問合せの種類選択タブ --%>
    <select name="qtype">
      <option value="company">会社について</option>
      <option value="product">製品について</option>
      <option value="support">アフターサポートについて</option>
      </select>

<%--お問い合わせ内容入力タブ --%>
      <p>お問い合わせ内容を入力してください</p>
      <textarea name="body" rows="4" cols="40">
      </textarea><br>

      <%--送信ボタン --%>
    <input type="submit" value="送信">

</form>
</body>
</html>

【サーブレットクラス】

package p157_s;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/P157_s")
public class P157_s extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");

        String name=request.getParameter("name");
        String slags=request.getParameter("qtype");
        String comment=request.getParameter("body");

        //javaコード
        String errorM="";
        if(name==null||name.length()==0) {errorM+="氏名が入力されていません";}
        if(slags==null||slags.length()==0) {errorM+="お問合せの種類が選択されていません";}
        else if(slags.equals("company")){slags="会社について";}
        else if(slags.equals("product")){slags="商品について";}
        else if(slags.equals("support")){slags="アフターサポートについて";}
        if(comment==null||comment.length()==0) {errorM+="お問い合わせ内容が入力されていません";}

        String msg=("氏名["+name+"]様 お問合せの種類["+slags+"] お問合せ内容["+comment+"]");
        if(errorM.length()!=0) {msg=errorM;}

    response.setContentType("text/html; charset=UTF-8");
        PrintWriter out=response.getWriter();

        out.println("<html>");
        out.println("<head>");
        out.println("<title>お問い合わせフォーム</title>");
        out.println("</head>");

        out.println("<meta.charSet=\"UTF-8\">");
        out.println("<body>");
        out.println("<p>"+msg+"</p>");
        out.println("</body>");
        out.println("</html>");
    }
}

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

==とequals

コード作成時にちょっとしたミスを繰り返してしまうので
戒め的に備忘録として記事にします。

String型の変数のif文での確認で下記のミス

<✕>
if(name == "minato"){
・・・処理
}

<〇>
if(name.equals("minato")){
・・・処理
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

java:総称型リストの書き方[メモ書き]

java:総称型のメリットは型の指定をしているところ。
コードの短縮にもなる。

demojava/demo14/Demo14.java
package demojava.demo14;

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

public class Demo14 {
    public static void main(String[] args) {
        List<String> L = new ArrayList<String>();
        String hoge = "Hello List";

        char[] C = hoge.toCharArray();
        for (int i = 0 ;i < C.length; i++){
            L.add(String.valueOf(C[i]));
        }
        for (String val : L) {
            System.out.println(val);
        }

    }
}

実行結果
2020-10-12_15-05-52.png

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

DI,Thymeleafの利点とは

概要

現在のJavaの2大フレームワークの一つ「Spring」。
このフレームワークの大きな特徴であるDI(Dependency Injection)とThymeleafとは何か説明します。
※深くは説明しません。以降学びが深くなった際にコードベースでの説明を記事にします。

DIの利点

DIとは、オブジェクトの成立要件に必要な情報を外部設定することです。
例えば以下のような状況があるとします。

クラスA→クラスB
クラスAはクラスBと依存関係にある(クラスBはクラスAの依存性を持つ)

DIを適用するとクラスAにとって必要なクラスBの情報は外部に切り出されているので、クラスBの状況を無視して、クラスAを実行することができます。

つまり「まだクラスBにエラーがあるから、クラスAのテストができない」ということがなくなります。
これがDIの利点です。

Thymeleafの利点

ThymeleafとはSpring Bootで標準的に使われているテンプレートエンジン。
Thymeleafは変数の部分を属性値で記述しているため、ブラウザで表示しても崩れません。

なので「デザインを修正すると機能しない。しかし機能を正常動作するように修正するとデザインが崩れる」ということがなくなります。
これがThymeleafの利点です。

参考文献

https://www.atmarkit.co.jp/ait/articles/0504/29/news022.html
https://javazuki.com/articles/thymeleaf-introduction.html

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

PHP言語と違うところ。[メモ書き]

PHP言語と違うところ。
クラスの呼び出しやメソッドの呼び出しの考え方が違う。
同じパッケージ内にあるクラスを呼び出す場合、別にimportする必要がない(PHPのinclude等にあたる)。
あと、mainは特別な存在という考え方などもPHPではない考え方だと感じた。

demojava/demo13/Demo13.java
package demojava.demo13;

public class Demo13 {
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        System.out.println(test1.name());
    }
}
demojava/demo13/Test1.java
package demojava.demo13;

public class Test1 {
    public String name() {
        return "テスト1";
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaのRecordに対するcloneメソッドの実装

Java 15で2nd previewとなっているRecordにcloneメソッドを実装し、deep copyできるようにします。
特にこれといって使う場面は浮かびませんが、いつか使うかもしれないので備忘録に。
ポイントはsuper.clone()を使わずにコンストラクタでcloneを作ることくらいです。

環境

Java 15(Oracle Open JDK)
IntelliJ IDEA Community版
Windows

※IntelliJでRecordを使うための設定は以下を参考にしてください(手前味噌ですが)。
https://qiita.com/tora_kouno/items/58eff9f4eb99a941fd41

実装

clone実装前

ExampleRecord.java

public record ExampleRecord(String name, int age, ExampleDto dto) {
}

ExampleDto.java

public class ExampleDto {
  private String name;
  private int age;
  private NodeDto node;
  // setter, getter, toStringは省略
}

NodeDto.java

public class NodeDto {
  private String name;
  // setter, getter, toStringは省略
}

clone実装後

ExampleRecord.java

public record ExampleRecord(String name, int age, ExampleDto dto) {
  @Override
  public ExampleRecord clone() {
    try {
      return new ExampleRecord(this.name, this.age, this.dto.clone());
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return null;
  }
}

ExampleDto.java

public class ExampleDto implements Cloneable {
  private String name;
  private int age;
  private NodeDto node;
  // setter, getter, toStringは省略

  @Override
  public ExampleDto clone() throws CloneNotSupportedException {
    ExampleDto copy = (ExampleDto) super.clone();
    copy.node = this.node.clone();
    return copy;
  }
}

NodeDto.java

public class NodeDto implements Cloneable {
  private String name;
  // setter, getter, toStringは省略

  @Override
  public NodeDto clone() throws CloneNotSupportedException {
    return (NodeDto) super.clone();
  }
}

試す

実装

Main.java

public class Main {
  public static void main(String[] args) {
    ExampleDto dto = new ExampleDto();
    NodeDto nodeDto = new NodeDto();
    nodeDto.setName("node1");
    dto.setNode(nodeDto);
    dto.setName("dto1");
    dto.setAge(1);
    ExampleRecord exampleRecord = new ExampleRecord("record1", 2, dto);
    System.out.println("exampleRecord:"+exampleRecord);// 1
    dto.setName("dto2");
    nodeDto.setName("node2");
    System.out.println("exampleRecord:"+exampleRecord);// 2

    ExampleRecord clone = exampleRecord.clone();
    System.out.println("clone:"+clone);// 3
    dto.setName("dto3");
    nodeDto.setName("node3");
    System.out.println("clone:"+clone);// 4
  }
}

実行結果

1.

exampleRecord:ExampleRecord[name=record1, age=2, dto=ExampleDto{name='dto1', age=1, node=NodeDto{name='node1'}}]

2.

exampleRecord:ExampleRecord[name=record1, age=2, dto=ExampleDto{name='dto2', age=1, node=NodeDto{name='node2'}}]

3.

clone:ExampleRecord[name=record1, age=2, dto=ExampleDto{name='dto2', age=1, node=NodeDto{name='node2'}}]

4.

clone:ExampleRecord[name=record1, age=2, dto=ExampleDto{name='dto2', age=1, node=NodeDto{name='node2'}}]

1->2ではDtoの中身が変わってしまっていますが、3->4では別インスタンスであるためdto.setName("dto3");nodeDto.setName("node3");の影響を受けず、問題なくcloneされていることがわかります。

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

IntelliJでJavaのプレビュー機能(Java 15のRecordなど)の試し方

結論

  1. (IntelliJ IDEAの最新版にする)
  2. プロジェクトで当該のJDKを使うように設定
    1. 使いたいJDKをダウンロード
  3. プレビュー機能をオンにする
    1. File>Project Structure>Project Settings>Modules
    2. Language levelで「15(Preview) - Sealed types, records, patterns, local enums and interfaces」を選択

※上記はWindows環境の場合です。Macの場合、Project Structureの開き方がちょっとだけ違います。
※「Ctrl」+「Shift」+「Alt」+「S」でもProject Structureを開くことができます。

環境

  • Windows
  • IntelliJ IDEA Community版(2020.2.2)
  • Open JDK15

詳細手順

1.IntelliJ IDEAの最新版にする

Java 15の新機能の場合、2020.2より新しいIntelliJが必要です。
2020.2のアップデート情報はこちら
もしバージョンが古い場合は以下の方法でアップデートしてください。

自動アップデート

Help>Check for Updates...から自動アップデートができます。
update and restartボタンを押して待てば完了です。
update and restartボタンが表示されない場合、手動アップデートを行ってください。

手動アップデート

こちらから指定のバージョンのファイルをダウンロードしてアップデートすることもできます。
インストールウィザードで古いバージョンを消して設定を引き継ぐか確認があります。

2.プロジェクトで当該のJDKを使うように設定

IntelliJを開き、新規プロジェクトを作成します。
image.png
プルダウンでDownload JDKを選択すると、選択したディストリビューションとバージョンのJDKが、プロジェクトで使用するJDKに設定されます。
image.png
image.png
ここに表示されないアーリーアクセス版などを利用したい場合は各自でダウンロードした後、「Add JDK...」でダウンロードしたJDKを指定してください。(binやlibがあるディレクトリを指定します。)
image.png

3.プレビュー機能をオンにする

File>Project Structure>Project Settings>Modulesを開きます。
image.png
Language levelのプルダウンから使いたい機能に合わせたものを指定します。
今回はrecordsを試すので「15(Preview) - Sealed types, records, patterns, local enums and interfaces」を指定しました。
image.png

もしJava 15の設定が出ない場合はIntelliJのバージョンやJavaのバージョンを再度確認してみてください。

実行してみる

まずRecord classを作ります。

package records;

public record ExampleRecord(String name, int age) {
}

次に実行するMainクラスを作ります。

package main;

import records.ExampleRecord;

public class Main {
  public static void main(String[] args) {
    ExampleRecord exampleRecord = new ExampleRecord("test", 20);
    System.out.println(exampleRecord);
  }
}

実行してみるとこんな感じになります!

{JDK_path}\bin\java.exe --enable-preview "-javaagent:{IntelliJ IDEA_path}\lib\idea_rt.jar=50507:{IntelliJ IDEA_path}\bin" -Dfile.encoding=UTF-8 -classpath {workspace_path}\{project_name}\out\production\record_refrection main.Main
ExampleRecord[name=test, age=20]

ポイントは--enable-previewがオプションとして付与されているところです。
これによりpreview機能であるRecordが利用できます。
以上になります。

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

【合格体験記】Oracle Certified Java Programmer Silver/Gold SE 11に合格しました.

この度,Java SE 11 認定資格 | Oracle Certified Java Programmer, Silver/Gold SE 11を取得しましたので,試験の概要・対策・感想について簡単にまとめたいと思います.勉強期間としては,SilverとGoldを合わせて1ヶ月半程度でした.

はじめに

Java SE 11 認定資格 | Oracle Certified Java Programmer とは?

Java SE 11 認定資格 | Oracle Certified Java Programmer は,2019年6月下旬より開始された新しいJavaの認定資格試験です.Java 11以前の認定資格としては Oracle Certified Java Programmer, SE 7/8 が有名ですが,Java 11はJava 9以降で初の長期サポート (LTS: Long Term Support) 対象であり,Java SE 8 認定資格の正式な後継資格となります.1

また,認定資格名と試験名が異なるためちょっと混乱するかもしれませんが,Oracle Certified Java Programmer, Silver SE 11 認定資格を取得するためには Java SE 11 Programmer I (1Z0-815-JPN) 試験の合格が,Oracle Certified Java Programmer, Gold SE 11 認定資格を取得するためには Java SE 11 Programmer II (1Z0-816-JPN) 試験の合格が必要です.

試験形式・試験時間・出題数・合格ラインはSilverとGoldで共通です.詳細は Java SE 11 Programmer I (1Z0-815-JPN) 試験Java SE 11 Programmer II (1Z0-816-JPN) 試験 のページを参照してください.

  • 受験料(税抜):¥26,600
  • 出題形式:選択問題
  • 試験時間:180 分
  • 出題数:80 問
  • 合格ライン:63 %

筆者のバックグラウンド

Javaの開発経験は1年半程度あり,Spring Bootを用いたRESTful Web APIや,Spring Batchを用いたバッチプログラムの開発経験があります.ファイルのI/Oが発生するバッチや,RDB/NoSQLに接続するWebサービスの開発等も行っていたため,Silverの試験範囲はおおよそ全て利用経験があり,Goldの試験範囲も一部は業務で使ったこと・見たことがあるレベルでした.とはいえ,並列処理やローカライズ・モジュール・ストリームAPI等,存在は知っていても使ったことも見たこともない試験範囲もありました.

試験範囲

Oracle Certified Java Programmer, Silver SE 11では,Javaの基本的な文法やオブジェクト指向モデル (クラス・カプセル化・継承・ポリモーフィズム) に加え,Java 8では存在しなかったモジュール・システムの知識が問われます. 試験範囲 は以下の通りです.

  • Javaテクノロジと開発環境についての理解
  • 簡単なJavaプログラムの作成
  • Javaの基本データ型と文字列の操作
  • 演算子と制御構造
  • 配列の操作
  • クラスの宣言とインスタンスの使用
  • メソッドの作成と使用
  • カプセル化の適用
  • 継承による実装の再利用
  • インタフェースによる抽象化
  • 例外処理
  • モジュール・システム

Oracle Certified Java Programmer, Gold SE 11では,例外処理・ジェネリクス・コレクション・ラムダ式・ストリーム・並列処理・JDBC等,公式APIの使い方や文法が幅広い分野で問われます.特に関数型インタフェース,ラムダ式,ストリームAPIについては頻出のため,しっかりと対策していく必要があります. 試験範囲 は以下の通りです.

  • Javaの基礎
  • 例外処理とアサーション
  • Javaのインタフェース
  • 汎用とコレクション
  • 関数型インタフェースとラムダ式
  • JavaストリームAPI
  • 組込み関数型インタフェース
  • ストリームに対するラムダ演算
  • モジュール型アプリケーションに移行する
  • モジュール型アプリケーションにおけるサービス
  • 並列処理
  • 並列ストリーム
  • I/O(基本およびNIO2)
  • Java SEアプリケーションにおけるセキュア・コーディング
  • JDBCによるデータベース・アプリケーション
  • ローカライズ
  • アノテーション

資格取得までの道のり (Silver編)

勉強教材

以下を利用して勉強しました.

俗に言う黒本です.その他 紫本 も有名ですが,何かしらJavaの参考書を使ってJavaを勉強した経験のある方なら黒本単体で問題ないと思います.黒本は問題演習メインで,紫本は解説メインの参考書となります.

もしJavaの勉強をしたことがなくてSilverの資格を取得したい場合には,紫本の解説を読み込むか,スッキリわかるJava入門 等の参考書を使って一度基礎を理解してから黒本で問題演習を行ったほうがよいかもしれません.

なお,黒本は出てくる問題を全て覚える勢いでやり込んでください.各所で言われていますが,黒本の問題は実際の試験問題と傾向がよく似ており,黒本の問題が全て解けるなら間違いなく実際の試験も合格できます.

勉強期間

上記教材を利用して,下記スケジュールで資格勉強を進めていきました.およそ1週間程度で模擬試験2回分をさくっと2周し,試験にも無事合格することができました.なお,モジュール・システムについては初見だったため,章の問題・解説も読み込みました.

# 教材名 勉強期間
1 徹底攻略Java SE 11 Silver問題集[1Z0-815]対応 徹底攻略シリーズ 1週間程度

感想・所感

シンプルに行きます.黒本をやり込んでください.それだけでほぼほぼ合格できると思います.

1つだけ注意点を挙げるとすれば,試験におけるソースコードが壊滅的に読みづらく,かつ各所にバグが散りばめられています.(黒本を読めばわかると思いますが...)

コーディング規約が存在しない∧IDEとかじゃなくてメモ帳で開発している∧Java経験者が1人もいない開発チームによって開発されたコードをデバッグするような作業になるので,ちょっと問題を解くのが辛いかもしれません.

資格取得までの道のり (Gold編)

勉強教材

以下を利用して勉強しました.

俗に言う紫本・黒本です.一応注意点ですが,Silverとは異なり,GoldはSE 11の参考書が存在しない (※記事執筆時点) ため,SE 8認定資格の参考書で学習する必要があります.とはいえ,SE 8認定資格の試験範囲 とはほぼほぼ重複しているため,十分対策になります.差分であるモジュール・システムについては この辺の記事(Oracle)この辺の記事(Qiita) で一通り動きを確認しておくとよいかと思います.

学習の流れとしては,まず経験のない分野 (関数型インタフェース・ストリームAPI・並列処理・ローカライズ) は紫本の解説を流し読みしました.問題は解いていません.解説を読了後,黒本の問題演習に入りました.こちらは模擬試験だけではなく,1章から問題演習を2周程度行いました.

そしてやっぱり黒本は問題形式がよく似ているので,こちらも全ての問題を覚える勢いでやり込んであげると良いと思います.もちろん理屈は理解した上で,ですが.

勉強期間

上記教材を利用して,下記スケジュールで資格勉強を進めていきました.期間としては,紫本が1週間程度,黒本が2週間程度だったと思います.時間をかけて勉強したこともあり,実際の試験の正答率はSilverよりもGoldの方が高かったです.

# 教材名 勉強期間
1 オラクル認定資格教科書 Javaプログラマ Gold SE 8 1週間程度
2 徹底攻略Java SE 8 Silver問題集[1Z0-808]対応 徹底攻略シリーズ 2週間程度

感想・所感

こちらも黒本をやり込んでください.分野としては,SE 8での目玉であるストリームやラムダ式はやっぱり大事なので特に重点的に対策をしてください.また,SE 8では存在しなかったモジュール・システムについても,最低限コマンドの種類と構文だけは理解した上で試験に臨んであげると良いと思います.

なお,Silverの問題よりはだいぶソースコードが読みやすいです!Silverのような重箱の隅をつつく問題というよりは,APIの正しい使い方やデータの流れ方を問う問題が多いので,Silverよりも簡単に感じる人がいるかもしれません.

感想 (全体)

実はそろそろJavaではなくPythonを本業にしたいなーと思ったりしていて,だったらJavaを忘れる前に取れるものは取っておこう精神で急ぎ資格を取りました.Java経験者の方であれば割とさくっと取れると思うので,興味のある方は取得を目指してみてはどうでしょうか.

一応これでJavaに未練がなくなったので,身の回りの言語を少しずつPythonに置き換えていけたらいいなーと思っています.


  1. LTSでないJava 9/10には 認定資格 がありません.SE7/8を保持されている方はアップグレード用の移行試験によってSE11の資格にアップグレードすることが可能です. 

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

jarファイルの中身を、解凍せずに変更する方法メモ

はじめに

Javaのプロジェクトの仕事の中で、ソースコードをjarファイルとしてビルドをして、それをサーバーにデプロイしていました。そのデプロイされたjarファイルの中身をテスト用に書き換えたくなった時に、少し嵌ったのでここに解決方法をメモしておきます。

尚、本記事ではSpringフレームワークのプロジェクトを例として扱います。jarファイルの解凍時のディレクトリは、以下のようになっています。

┣ BOOT-INF/
  ┣ classes
    ┗ application.properties <- *このファイルを書き換えたい*
  ┗ lib
    ┗ {使用してるライブラリのjar}
┣ META-INF/
  ┣ maven/
    ┗ {pom.xmlとか}
  ┗ MANIFEST.MF <- マニフェストファイル
┗ org/
  ┗ {classファイルがいっぱい}

具体的には、/BOOT-INF/classes/application.propertiesを書き換えようとして、失敗しました。

失敗例

解凍してから、zipコマンドで固める

jarファイルは実質的にはzipファイルと変わらないので、zipコマンドでjarファイルを解凍して、ファイルを修正し、再度zipコマンドで固め直そうとしました。

この結果できたファイルを基にアプリをjava -jarコマンドで起動しようとすると、以下のエラーが出ました。

Exception in thread "main" java.lang.IllegalStateException: Failed to get nested archive for entry BOOT-INF/lib/bcpkix-jdk15on-1.60.jar
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:108)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:86)
        at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:70)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.io.IOException: Unable to open nested jar file 'BOOT-INF/lib/bcpkix-jdk15on-1.60.jar'
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:256)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:241)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:103)
        ... 4 more
Caused by: java.lang.IllegalStateException: Unable to open nested entry 'BOOT-INF/lib/bcpkix-jdk15on-1.60.jar'. It has been compressed and nested jar files must be stored without compression. Please check the mechanism used to create your executable jar file
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:284)
        at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:264)
        at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:252)
        ... 6 more

エラーメッセージにnested jar files must be stored without compressionとある通り、BOOT-INF/lib以下のライブラリのjarファイル群を圧縮してしまったことで(?)うまくjarファイルとして読み込まれなかったようです。

解凍してから、jarコマンドで再作成

単純なzipコマンドでダメなら、jarコマンドでjarファイルの作成を試みてみます。

jar cvfm xxx.jar ./META-INF/MANIFEST.MF ./

上のコマンドで作成したjarファイルを基に起動しようとすると、以下のエラーが出ました。

no main manifest attribute, in xxx.jar

ちゃんとオプションでマニフェストファイルを指定しているのに、上手く読み込まれていないようです(この現象の直接的な原因は、未解決です)。

成功例

zipコマンドで、解凍せずに中身を入れ替える

jarファイルを解凍せずに中身を書き換えることで、上記の問題をクリアしました。

まず、作業しているローカルの場所で、書き換えたいファイルとそれが置かれているディレクトリを再現します。
私は今回BOOT-INF/classes/application.propertiesというファイルを書き換えたかったので、それを作ります。

mkdir -p BOOT-INF/classes && touch ./BOOT-INF/classes/application.properties

このローカルのapplication.propertiesファイルを書き換えたら、それをzipに取り込みます。
取り込みたいzipファイル(xxx.jar)を同ディレクトリに持ってきて、以下のコマンドで取り込みます。

zip ./xxx.jar ./BOOT-INF/classes/MANIFEST.MF

この結果できたjarファイルは、一度も解凍してないためかエラーが起こらず、問題なく動かすことができました。

ついでに

上に記したのは、jarファイルの中身を書き換える方法でした。
中身を書き換えるのではなく、ただ見るだけであれば、以下のコマンドで見れます。

jar tf xxx.jar

中身を確認するためにわざわざjarファイルを解凍していたら、上司に怒られました。

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