20210109のJavaに関する記事は8件です。

WSWSL2 Docker VSCodeでJava開発環境を構築

WSL2 Docker VSCodeでJava開発環境を構築

WSL2とDockerとVSCodeで開発環境を作るのがホットな話題だったので試してみたのですが、
作ったものの開発環境の動作が重すぎてドはまりしました。
オチとしては開発ソースを軽量Linuxのフォルダ内でなくWindowsフォルダにおいていたのが原因でした。
自分への戒めのため手順を残しておきます。

WSL2のインストール

Linux 用 Windows サブシステムを有効にする

管理者として PowerShell を開き、以下を実行します。

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

仮想マシンの機能を有効にする

管理者として PowerShell を開き、以下を実行します。

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

マシンを再起動します。

Linux カーネル更新プログラム パッケージをダウンロードする

  1. 最新のパッケージをダウンロードし、インストールします。
    x64 マシン用 WSL2 Linux カーネル更新プログラム パッケージ

WSL 2 を既定のバージョンとして設定する

PowerShell を開いて次のコマンドを実行し、新しい Linux ディストリビューションをインストールする際の既定のバージョンとして WSL 2 を設定します。

wsl --set-default-version 2

選択した Linux ディストリビューションをインストールする

  1. Microsoft Storeを開き、希望する Linux ディストリビューションを検索し、選択します。
  2. ディストリビューションのページで、[入手] を選択します。ここではUbuntu 20.04 LTSを入手します。
  3. Ubuntu 20.04LTSを起動し、ユーザー名・パスワードを入力します。
  4. コマンドsudo apt-get updateでUbuntu を更新しておきます。

WSLのフォルダを確認

WindowsからWSLのLinuxファイルシステムにはエクスプローラーに以下のパスを入力することでアクセスできます。

\\wsl$

Ubuntu20.04LTSの場合は以下になります。

\\wsl$\Ubuntu-20.04

WSL+Dockerを利用して開発を行うときは、ソース等をこの配下に配置します。

またWSLからWindowsのフォルダを参照するとWSLのターミナルからは
下記とおりになります。

/mnt/c/

※こちらですが、この配下にソースを置いておくとLinuxシステムからWindowsシステムへの参照が行われるため、ファイル I/O が異常に遅くなりますの気を付けてください。

WSLのリソースを制限

WSLはデフォルトだとホストマシンの物理メモリの80%を確保するようで、
ホストのメモリ不足が発生する可能性があるので、設定を変更します。
以下のファイル(なければ新規作成)に設定を記述します。
%USERPROFILE%\.wslconfig

記述内容は以下になります。

[wsl2]
memory=4GB
swap=0
processors=2

くわしくはWSL コマンドと起動構成を参照

設定を反映させるにはWSLを再起動します。

PowerShellから以下のコマンドを実行し、WSLのターミナルを立ち上げなおすことで再起動されます。

wsl --shutdown

起動中のWSLを確認するにはPowerShellで以下のコマンドを実行します。

wsl -l -v

Dockerのインストール・設定

インストール

Docker Desktopをインストール
インストール時にEnable WSL 2 Windows Featuresのチェックをいれること

設定

Docker Desktopを起動し、タスクバーのDockerのアイコンを右クリックしsetteingsを起動

  • GeneralにてUse the WSL 2 based engineにチェックがはいっていること
  • ResourcesにてEnable integration with my default WSL distroにチェックがはいっていること。
    ※PowerShellでwsl -lコマンドで表示したリストで規定になっているDistroからDockerDesktopを使用できる。

VSCodeの設定

環境変数の設定

Windowsの環境変数のPATHに以下のようにVScodeのbinフォルダを設定しておくと
WSLからコマンドでVSCodeを起動できるようなります。
C:\app\VSCode\bin

拡張機能のインストール

Remote Developmentをインストールします。

Remote Development は複数の拡張機能が含まれますが、
今回は特にRemote - Containerを使用します。
他には以下のような拡張機能もインストールされます。

  • Remote - WSL
  • Remote - SSH

VSCodeの起動

  • WindowsTerminal or Ubuntuターミナルを起動
  • コマンドcd ~を実行してホームフォルダに移動
  • コマンドpwdでホームフォルダのパスを確認
    WSL 上では /home/ユーザー名 になります。
    Windows上 では\\wsl$\Ubuntu-20.04\home\ユーザー名になります。
  • 任意のフォルダを作成し移動
mkdir dev_root
cd dev_root
  • VsCodeの起動
code .

Remote コンテナの作成

ctrl + shift + p を押し、
remote-containers: Open Folder in Containerを入力

下記通りプロンプトが表示されるのでそれぞれ選択を行う

  1. フォルダを選択
  2. Dockerイメージを選択→ javaを選択
  3. Javaバージョンを選択→11を選択
  4. ビルドツールを選択→maven gradle nodeはお好みで選択

Javaプロジェクトの作成

VsCodeのターミナルから
spring boot initializrでプロジェクトのひな型を作成

curl https://start.spring.io/starter.zip \
       -d dependencies=web \
       -d javaVersion=11 \
       -o demo.zip

プロジェクトのひな型を解凍

unzip demo.zip

DemoApplication.javaを下記通り編集

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class DemoApplication {

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

    @GetMapping("/")
    public String index(){
        return "hello World";
    }

}

F5キーを押し、デバッグ実行を行いhttp://localhost:8080/にアクセスする

Remote-Containerについての補足

Remote-Containerを利用すると
.devcontainerフォルダが作成され、そこに以下の二つのファイルが作成されます。

  • devcontainer.json
    こちらにはビルドするDockerファイルやDocker Composeファイルを指定したり、
    コンテナのVsCodeの設定(java.homeとか)やインストールしたい拡張機能(spring-boot-extention-pack等)を記述することができます。

  • Dockerfile
    コンテナをビルドするためのDockerファイルになります。今回は拡張機能(MS)が用意しているDockerイメージを使用しましたが、
    Docker Hub等にアップされている自分の好きなDockerイメージに変更することが可能です。

この記事は以下の情報を参考にして執筆しました。

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

クリリンでもわかるJava入門(後編)

前回のドラゴンボール(前編)

オブジェクト指向の魅力的な機能から始めていく

カプセル化(オブジェクト指向3大機能その①)

オブジェクト指向の3大機能(カプセル化・継承・多様性)の1つ。
アクセス制御を行うことでミスを防ぐ

4種のアクセス修飾子
制限度 名前 コードで指定方法 許可範囲
厳しい private private 自分自身のクラスのみ
public private 何も書かない 自分と同じパッケージのクラスのみ
protected protected 自分と同じパッケージor自分を継承したクラス
緩い public public 全てのクラス

一般的にフィールドは private、メソッドは publicにする。
※クラスはpublic か public privateしか使えない。

フィールドがprivateだと他クラスから変更ができない。変更するときは getterメソッドと setterメソッドというものを用意する。
getterで取得、setterで上書き。
基本的なメソッドの命名は
「get/set + フィールドの一文字目を大文字()」となる。
getter()のみを使えばRead only(読み込みのみ、上書き不可)のフィールドを実現できたりメリットがある。

sample.java
public class Hello{
    private String name;

    public String getName(){
        return this.name;
    }

    public void setName(String name){
        this.name = name;
    }
}

↓setterでフィールドへの上書き検査もできる。

sample.java
private String name;

public void setName(String name){
    //名前がnullなら中断
    if(name == null){
        throw new IllegalArgumentException("名前がnullの為、処理中断")
        this.name = name;
    }

継承(オブジェクト指向3大機能その②)

extends で他クラスを継承し、内容を継ぐことができる。

public class Angel extends Human {
}

つまり、Angelクラスをインスタンス化したとき Humanクラスのメソッド(&フィールド)が使える。

Angel Ag = new Angel();
Ag.run(); //Humanクラスで宣言したrunメソッド

このクラスの継承関係では元となった親クラスHumanをスーパークラスという。
継承済みAngelを他クラスに継承させることもできる。
※ただし、1クラスで複数同時継承はできない(1回1個まで)。

オーバーライド

↑に記述したAngelクラスで継承したHumanクラスの一部を変更したいときは、
再定義することで上書きできる。
※Humanクラスに影響はない。

Angel.java
public void run(){
    System.out.println("runメソッドの内容が上書きされた")
}

子クラスでオーバーライド(上書き)されたくないときは
「public final class」のようにfinalを付ける。

上書き前のrunメソッドも使いたいという場合は、
superを利用しスーパークラス(親)を呼び出せる。

super.run(); //メソッド
super.name; //フィールド

※「親-子-孫」 の継承関係の場合、孫から子へはsuperで呼べるが、孫から親へはsuperでは呼べない。

継承とコンストラクタ

インスタンス化(new)すればコンストラクタが自動で呼ばれるが、
継承しているとスーパークラスのコンストラクタから処理される。
例えば、Humanクラスを継承したAngelクラスをインスタンス化したとき、
Angelのコンストラクタ等の処理の前に、Humanにコンストラクタがあればそちらが優先処理される。

実はAngelクラスでHeroクラスのコンストラクタを「super()」で呼んでいる。
書かなくても自動で暗黙のsuper()が呼ばれる。

Angel.java
public class Angel extends Human {
    //省略できるが暗黙的に呼ばれる
    public Angel(){ //Angelのコンストラクタ
        super(); //Humanのコンストラクタを呼ぶ
    }
}

※super()はthis()同様、コンストラクタの先頭行にしか記述できない。

正しい継承

正しく継承をするには「is-a」(子は親の一種である)を守る。

例:3つ

  • スーパークラス:Human(人型) サブクラス:Angel

 〇 Angelは人型の一種である。

  • スーパークラス:Food サブクラス:Sushi

 〇 SushiはFoodの一種である。

  • スーパークラス:Car サブクラス:Engine

 × EngineはCarの一種である。
  より具体的な車(スーパーカー、ベンツ等)なら〇

親から子へは具体的に特化していき、
子から親へは抽象的に汎化していく。

間違った継承をするとクラスに矛盾が生じたり、オブジェクト指向の1つ「多様性」を利用できなくなる。

高度な継承

他の開発者向けにスーパークラスを作るとなれば、様々な不都合が出てくる。
3つの不都合に対する解決法を挙げていく。

①詳細未定(抽象)メソッドの宣言

他に開発者2人が 悟空クラスと ベジータクラスを作ろうとしている。
人口や面積、似たフィールドがある為、継承用にスーパーサイヤ人クラスを作ってあげる。
かめはめ波メソッドを作ろうとしたが、それぞれで威力が違う為定義できない。

SuperSaiyajin.java
public class SuperSaiyajin {
    public void kamehameha (Saibaiman s){
        s.hp -= ??;
    System.out.println("サイバイマンのHPが??減った!")
    }
}

とりあえず、オーバーライドして上書きしてもらう事にして、
かめはめ波メソッドの処理を削除すれば良いと考えるが、
何も行わないメソッドだと思われてしまうのを避ける為に「abstract(アブストラクト)」を使う。
日本語で「抽象的・あいまい」という意味。
「{}」を付けずに「;」を付ける。
abstractの付いたメソッドを抽象メソッドという。

public abstract void kamehameha (Saibaiman s);

②抽象メソッドを含むクラスの宣言

1つでも abstract を含むメソッドがるときクラスにも abstract を付ける必要がある。(無いとエラー)

SuperSaiyajin.java
public abstract class SuperSaiyajin {
    public abstract void kamehameha (Saibaiman s);
}

abstract の付いたクラスは抽象クラスと呼ばれ、new(インスタンス化)できない。(するとエラー)
継承(extends)用に作成したスーパーサイヤ人クラスが間違ってインスタンス化されないようになる。

③オーバーライドの強制

悟空クラスでかめはめ波メソッドのオーバーライド(上書き)を忘れる可能性がある。
しかし、上の①②を実践していればエラーが起こるので忘れることがなくなる。
なぜなら、スーパーサイヤ人を継承しているということは「抽象メソッドのかめはめ波メソッド」を含む。
抽象メソッドを含むクラスは abstract が必要。
オーバーライドに気づき、かめはめ波メソッドを抽象化せずオーバーライドすればエラーは消える。

アクセス修飾子 protected について

protected の許可範囲は自分と同パッケージか、自分を継承したクラス。
自分のパッケージだけなら public で良いわけで、違うパッケージかつ継承クラスに protected を
使うことになるので利用局面は少ないといえる。

インタフェース

特に抽象度が高いクラスをインタフェースという。
インタフェースにできる2条件
①すべて抽象メソッドである。
②基本的にフィールドを持たない。
↑のSuperSaiyajin.javaはインタフェースにできる。
インタフェースのメソッドは自動で「public abstract」になる為、省略可能。

SuperSaiyajin.java
public interface class SuperSaiyajin {
    //public abstract は省略可能
    void kamehameha (Saibaiman s);
}

基本的にフィールドを持たないと書いたが、「public static final(定数)」だけ宣言が許可されている。
これもメソッド同様、自動で保管されるので省略可能。
例:int power = 1000000;

※もちろんインタフェースも抽象的に変わりない為インスタンス化不可。

インタフェースの実装

インタフェースの継承は extends ではなく、implements を使う。
implementsは日本語で実装する。
インタフェース(抽象的で未確定)をオーバーライドで実装確定するイメージ。

Gokuu.java
public class Gokuu implements SuperSaiyajin {
}

インタフェースの必要性

継承は複数クラス同時にできないと言いました。
理由は例えば、
かめはめ波メソッドを持つ悟空クラスと、チチクラスを
継承時点で悟飯クラスは威力が違うどっちのかめはめ波メソッドを使えばいいのか衝突し問題になる。

そこで両親がインタフェース(未確定)なら衝突せず、悟飯自身でオーバーライドしてかめはめ波の威力を決定できる。
よってインタフェースは多重継承が可能。

Gohan.java
public class Gohan implements Gokuu, Chichi{
}

インタフェースとextendsの使い分け

悟空クラスをインタフェースとして次を見てください。

Gohan.java
public interface class Gohan extends Gokuu {
}

悟空はインタフェースだから implements で継承じゃないの?となる。
実は implements を使うのはインタフェースを継承したクラスでオーバーライド(実装)するときだけ。
この悟飯クラスはよく見たらインタフェースだ。
悟空インタフェース(未確定)⇒悟飯インタフェース(未確定)なので確定(実装)にはなっていない。
よってただの継承(extends)である。

implements を使うのは未確定から確定になるときのみ。
言い換えると、クラスがインタフェースを継承するときのみ。
インタフェース⇒クラス はimplements

extendsとimplementsは同時に使える

例:public class Gohan extends SuperSaiyajin implements Gokuu, Chichi{

多様性(オブジェクト指向3大機能③)

多様性(polymorphism)はポリフォーフィズムとも呼ばれる。
カプセル化は private、継承は extends と、文法があるが多様性にはない。
ここでの多様性とは曖昧に捉えることでメリットを得られる方法のこと。

スーパーサイヤ人クラスを親とする子クラス「悟空クラス」をインスタンス化すると(継承関係)

Gokuu g = new Gokuu();

↑のようになるが、スーパーサイヤ人型にもできる。

SuperSaiyajin g = new Gokuu();

悟空はスーパーサイヤ人クラスを継承している為、これが可能になる。
つまり、親の型でインスタンス化ができる。
インタフェースのような抽象クラスはインスタンス化できないと言ったが、
ここでは型として利用しているだけで問題はない。

悟空はスーパーサイヤ人の一種である。
これが逆だと矛盾が生じる為、正しく継承する必要があった。(項目:正しい継承)

型を曖昧にしたときの変化

悟空クラスを親クラス(スーパーサイヤ人)にしたときどう変化するのか。

//1
public abstract class SuperSaiyajin {
    public abstract void kamehameha(); //スーパーサイヤ人はかめはめ波を使える。
}
//2
public class Gokuu extends SuperSaiyajin {
    public void kamehameha(){ //かめはめ波のオーバーライド
        System.out.println("100ダメージ");
    }

    public void kaiouken(){  //悟空は界王拳を使える。
    }
}
//3
public class Main{
    public static void main (String[] args){
        Gokuu g = new Gokuu();
        SuperSaiyajin s = g; //悟空インスタンスをスーパーサイヤ人型へ代入

        s.kamehameha();
        s.kaiouken();
    }
}

↑のs.kamehameha()は成功するが、s.kaiouken()はエラーになる。
スーパーサイヤ人クラスにかめはめ波はあるが、界王拳はない為使えない。
実は以下のようなルールがある。

型とインスタンス

  • 使えるメソッドは型で決まる。

  • メソッドの処理は中身(インスタンス)で決まる。

つまり、スーパーサイヤ人クラスで適当に界王拳メソッドを作れば、
呼び出すことが可能になり、処理は悟空クラスでオーバーライドした内容で動作しエラーは起こらない。

型を途中で変える

↑のソースで、スーパーサイヤ人は誰でも界王拳が使えるわけじゃないし
やっぱり悟空クラスにだけ界王拳を使えるようにしたい!となる場合がある。
そんなときはキャストを使う。

SuperSaiyajin s = new Gokuu();
Gokuu g = (Gokuu) s; //スーパーサイヤ人型を悟空型へ変換し代入

今回のような「曖昧な型を厳密な型に代入」することをダウンキャストとって失敗が危惧される。
例えば、悟空インスタンスのスーパーサイヤ人型をチチ型へ変換し代入したときもコンパイル時にエラーが出ない。
しかし、動作させた瞬間にClassCastExceptionというエラーが発生する。
キャストによる強制代入の内容が間違っているという意味のエラー。

このエラーを確実に回避するには、ある演算子を使用する。

instanceof演算子
変数 instanceof 型名 (変数を型名に代入可能ならばtrue)

SuperSaiyajin s = new Gokuu();
if (s instanceof Gokuu){
    Gokuu g = (Gokuu) s;
}

多様性のメリット

多様性を用いてインスタンスを曖昧に捉えることで
呼び出せるメソッドが減ったりとメリットがないようだが、
具体的なコードで説明していく。
スーパーサイヤ人1名、ナメック星人2名のPT(パーティー)が仙豆で100HP回復するプログラム。

public class Main {
    public static void main (String[] args){
        SuperSaiyajin s1 = new SuperSaiyajin();
        Namekkuseijin n1 = new Namekkuseijin();
        Namekkuseijin n2 = new Namekkuseijin();

        //仙豆で回復
        s1.setHp( s1.getHp() + 100);
        n1.setHp( n1.getHp() + 100);
        n2.setHp( n2.getHp() + 100);
    }
}
//前提:スーパーサイヤ人もナメック星人も抽象クラス「Character」を継承している。
//Characterクラスはhpフィールドとそのgetter,setterを持つとする。

このプログラムの回復処理は2つの問題がある。

  • コードに重複が多い ⇒ 記述が面倒。変数名を間違えそう。

  • 将来的に多くの修正が必要 ⇒ PT人数の増加による行追加。インスタンス名が変更による変数名修正。

これは多様性と配列の組み合わせで解決可能。

public class Main {
    public static void main (String[] args){
        Character[] c = new Character[3]; 
        c s1 = new SuperSaiyajin();
        c n1 = new Namekkuseijin();
        c n2 = new Namekkuseijin();

        //仙豆で回復
        for (Character ch : c){ //拡張for文
            ch.setHp( ch.getHp() + 100);
        }
    }
}

次は、引数を曖昧にした活用例を紹介
悟空がサイバイマンと魔人ブウに攻撃するプログラム。
以下のように処理を分けて書くはず。

Gokuu.java
public clss Gokuu extends SuperSaiyajin {
    public void kamehameha (Saibanman s){}; //サイバイマンにかめはめ波を撃つ処理

    public void kamehameha (Majinbuu m){}; //魔人ブウにかめはめ波を撃つ処理
}

しかし、敵が増えるごとにコードの追加が必要になる。
これも多様性で敵(Enemy)を曖昧にすれば、以下で済む。

Gokuu.java
public clss Gokuu extends SuperSaiyajin {
    public void kamehameha (Enemy e){}; //敵にかめはめ波を撃つ処理
}

これでEnemyクラスを継承している敵なら誰にでもかめはめ波を撃てる。
引数の渡し方は↓のような感じで良い。

Gokuu g = Gokuu();
Saibaiman s = Saibaiman();
Majinbuu m = Majinbuu();

g.kamehameha(s); //サイバイマンにかめはめ波
g.kamehameha(m); //魔人ブウにかめはめ波

多様性の最大のメリット

多様性の真価は、
異なるインスタンスを曖昧に捉えることでまとめて使えることと
動作は型でなくインスタンスのメソッドで決まることを組み合わせたときに発揮される。

例.java
public class Main {
    public static void main(String[] args){
        SuperSaiyajin [] superSaiyajin  = new SuperSaiyajin[3];
        superSaiyajin  [0] = new Gokuu(); //悟空は奥義メソッドは身勝手の極意
        superSaiyajin  [1] = new Beji-ta(); //ベジータの奥義メソッドはファイナルフラッシュ
        superSaiyajin  [2] = new Bejitto(); //ベジットの奥義メソッドはベジットソード

        for (SuperSaiyajin su : superSaiyajin ){
            su.ougi(); //それぞれ所有する奥義を使う。
        }
    }
}
実行結果.
悟空は奥義:身勝手の極意を使用した。
ベジータは奥義:ファイナルフラッシュを使用した。
ベジットは奥義:ベジットソードを使用した。

全体から多様性の構図と本質を見る。
指示側は「サイヤ人達、とりま奥義使え」といい加減なのに対し、
一方、キャラクター(サイヤ人)側は「奥義使え」と言われただけで独自の奥義を使えている。
このように、呼び出し側は相手を同一視し、同じように呼ぶのに、
呼び出される側は、決められた動きをするという特性から「多様性」と名付けられた。

Javaの標準クラス

日時を扱う基本形式2つ

  • 形式1:long型の数値

  基準日時である1970年1月1日0時0分0秒(エポックと呼ばれる)から経過したミリ秒数で表現する方法。
  long値:1316622225935 は2011年9月22日・・・を意味する。
  しかし、人間では日付が読めないことや、longは単に数字の格納にも使われ中身が日時情報と判断がつかない。
  System.currentTimeMiilis()で現在時刻取得。

  • 形式2:Date型のインスタンス

  long型の課題克服で広く用いられているのが java.util.Dateクラス。
  内部でlong値を保持しているだけだが、Date型の変数は日時情報であると判断がつく為、Javaで最も利用される

Date例.java
import java.util.Date;
public class Main {
    public static void main(String[] args){
        Date now = new Date(); //現在時刻取得
        System.out.println(now);
        System.out.println(now.getTime());
        Date p = new Date(1316622225935L); //long型なので「L」が必要
        System.out.println(p);
    }
}
実行結果.
Thu Jan 14 07:25:22 JST 2021 ?この行はリアルタイム次第
1610576722675
Thu Sep 22 01:23:45 JST 2011

人間が扱いやすい2つの形式

  • 形式3:String型のインスタンス

  SimpleDateFormatクラスで書式の指定ができる。(例:yyyy年MM月dd日も可能)

SimpleDateFormat例.java
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
    public static void main(String[] args){
        Date now = new Date(); //現在時刻取得
        SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); //書式指定
        String s = f.format(now); //フォーマットをあてる
        System.out.println(s);
        //逆に指定日時(String型)からDate型にもできる。
        Date d = f.parse("2020/01/14 01:01:01");
    }
}
  • 形式4:6つのint形式

  月や秒、それぞれを指定できる。
  変数.set(年,月,日,時,分,秒)or変数.set(Calendar.〇〇, 値)で指定。
  ※Calendarで月の指定には注意。0始まりなので1月ならset(Calendar.MONTH, 2)となる。

Calendar例.java
import java.util.Calendar;
import java.util.Date;
//public ~割愛
Date now = new Date(); //現在時刻取得
Calendar c = Calendar.getInstance();
c.setTime(now); //setTime()でDate型をリアルタイムセット
c.set(2030,8); //set()で年・月を上書き
c.set(Calendar.YEAR, 2020) //set()で年を上書き
int s = c.get(Calendar.SECOND) //get()で秒を取得

暗黙の継承

メソッドもフィールドも一切定義していないクラスでtoString()を呼び出せるのは、
全てのクラスはjava.lang.Objectを継承しているからだ。
実質:public class 〇〇 extends Object

Objectクラスの存在価値は
最低限必要なメソッド(toString()、.equals())が使える他に、
多様性が利用できる利点がある。

public class Main {
    public static void main(String[] args){
        Object o1 = new Gokuu(); //全インスタンスはオブジェクトの一種であると言える。
    }
}

ラッパークラス

Javaの型には基本型と参照型がある。基本型(String以外)では数値や文字の格納、値を使った演算処理が行える。
しかし、値に対する操作を行う手段(メソッド)は用意されていない。また、複雑な処理をより簡易に行えるAPIなどを利用しようとした場合、基本型は引数の対象外となるため使用できない。

そこで基本データ型の値をラップ(=包み込む)してオブジェクトとして利用できるようにするクラスが用意されている。
それらをラッパークラスと呼ぶ。

例えば「数値を文字列に変換したい」といった場合、ラッパークラスを利用して変換、といったことも行える。

基本データ型:int の ラッパークラス:java.lang.Integer

//ラッパークラスを利用するパターン
int i = 84;
Integer o = new Integer(i);
String str = o.toString(); //toStringはint(基本型)には使用できないが変数oはオブジェクトだ
//利用しないパターン
int i = 84;
String str = String.valueOf(i);

2つの違いは、valueOfは値がnullならnullを返すが、toStringはエラーが起こる為注意!
(※ラッパークラスはByte,Shot,Integer,long,Float,Double,Boolean等がある。)

エラーの種類と対応策

とりあえず動作するプログラムを作るのは簡単だが、エラーを起こさないプログラムを作るのは難しい。
いかなる場合も不具合のないプログラムを目指す必要がある。

Javaでの不具合は主に3種

  • ①文法エラー(syntax error)

  文字・文法間違え、セミコロン抜け等。
  ⇒エラー文の箇所を修正。

  • ②実行時エラー(runtime error)

  文法に問題がない為、コンパイル時は成功するが、実行中にエラーが出る。
  ⇒対策処理を記述しておく。

  • ③論理エラー(logic error)

  文法に問題がなく、強制終了することもないが内容がおかしい。(例:電卓ソフトの計算結果がおかしい)
  ⇒自力で探しコードを修正。

例外的状況

①と③はテスト時のコード修正で予防できるが、②実行時エラーはそうはいかない。
そもそも実行時エラーは想定外の事態の発生で起こる。
この「想定外の事態」を例外的状況(exceptional situation)又は例外(exception)という。

例外パターン例
・パソコンのメモリ不足
  開発環境では問題なく、本番環境の動作中にメモリ不足。
・存在すべきファイルがない
  誤って消されていた。
・nullが入っている変数のメソッドを呼び出した。
  ユーザーの想定外の操作により、本来入る予定のないnullが変数に入り、その変数を使用するメソッドを呼び出した。
  (if文でnullチェックはできるが、全ての処理に対しチェックするのは現実的ではない。)

これら全て開発時では例外的状況の発生を予防できない。
しかし、例外的状況発生後の対策は可能。

例外処理

例外的状況に陥ったときに備えた対策を例外処理という。

従来型の例外処理
処理①に対してif文でチェック
処理②に対してif文でチェック
と、1つの処理毎にチェックが必要で面倒。本来の処理の行もわかりづらい。

try-catch文

try-catch文でまとめてチェックできる。
tryは通常通り流れ、例外が発生したところでcatchブロックに移る。

try{
    //処理①
    //処理②
}catch(・・・ 変数){
    //・・・という例外処理が発生すれば動作する。
}

↑のコード「・・・」には例外的状況を表すクラス「例外クラス」が入る。

例外の種類

  • ①Error 系例外

  catchする必要なし。
  回復の見込みがない致命的な状況。
  例:OutOfMemoryError(メモリ不足)、ClassFormatError(クラスファイル破損)

  • ②Exception 系例外

  catchすべき。
  java.lang.Excepton(RunTimeException以外)を親に持つ。
  想定し対処を考える必要がある例外的状況。
  例:IOException(ファイル等が読み書き不可)、ConnectException(ネットに接続できない)

  • ③RunTimeException 系例外

  catchしなくてもよい。
  java.lang.RunTimeExceptionを親に持つ。
  必須とは言えない例外的状況。
  例:NullPointerException(変数等がNull)、ArrayIndexOutBoundsException(配列の添え字不正)

②のException 系例外の発生可能性がある箇所はtry-catch文で囲んでいないと、
コンパイル時に「例外 java.io.IOException は報告されません。~」と怒られる。
これをチェック例外という

発生する例外の調べ方

APIに含まれるクラスは例外が発生する可能性があるメソッドが多くある。
発生する例外はAPIリファレンスに掲載されている。
例:
APIが FileWriter なら「throws IOException」と書かれている為、
try-catch文でcatch(IOException e)としてやる。

↑1行上の「変数e」にはエラー情報が詰まっている(文字はeである必要はない)。
例外インスタンスと呼ばれ、e.getMessage()のようにも使える。

例外インスタンスが必ず備えているメソッド

メソッド 意味
getMessage() エラーメッセージの取得
printStackTrace() スタックトレースを画面に出力

※スタックトレース:プログラムの起動内容や例外内容等が記録された情報

様々なcatch構文

try-catch-catchのように複数の例外をキャッチすることも可能。
また、抽象的な例外クラスを指定することもできる。
Exception 系例外なら親に java.lang.Exceptonを持つので
catch(Exception e)でException 系例外全てをキャッチできる。

後付け処理の対応

次に、ファイルを開いて⇒文字を書いて⇒ファイルを閉じるプログラムがある。

try {
    FileWriter fw = new FileWriter("data.txt");
    fw.write("hello!");
    fw.close();
} catch (Exception e) {
    System.out.println("エラー発生");
}

これには問題点がある。
ファイルは開いたら閉じるのが決まり。
write()で上書きした後に、もしディスク容量が不足した場合に例外が発生し
catchへ移動するのでclose()が飛ばされる。

その対策でclose()をtry-catch構文の外へ移動し、
try-catch後に閉じれば問題ないかと思うが、NullPointerException等のcatchしていない例外が発生すれば
その時点で処理が止まり、close()が流れない。

必ず最後に実行しなければならない処理には
try-catch-finallyを使う。

} finally {
    fw.close(); //例外が起こっても行う処理
}

finallyが必須の状況は、今回のようなファイルやデータベース接続、ネットワーク接続等

例外の伝搬

例外はcatchしないと強制終了することがわかる。
では、mianメソッドでaメソッドを呼び出し、aメソッドはbメソッドを呼び出したが
bメソッドで例外が発生した場合どういう流れで処理されるか。

main() ⇒ a() ⇒ b() ✖例外発生

b()でcatchできなければa()へ、a()でも無理ならmain()へ、それでも無理なら強制終了となる。
この現象を例外の伝播と呼ぶ。
(例えば、a()でb()を呼び出すとき例外が発生する想定をしcatch構文を記述していれば防げた。)

Exception 系例外はキャッチが必須の為、例外の伝播は基本発生しない。
しかし、スロー宣言を行えば呼び出し元へ例外を伝播できる。
throws 例外クラス1, 例外クラス2...(throw:意味 投げる)

public static void b() throws IOException{
    //本当はtry-catch構文でIOExceptionを拾う必要があるが
    //例外発生した場合は、俺を呼び出したやつに投げるぜ!(責任丸投げ)
    FileWriter fw = new FileWriter("data.txt");
}

これで、コンパイル時にtry-catchがないと怒られることはないが、
呼び出し側が(try-catchする)責任を取るので注意が必要。

例外を発生させる

例外を投げるとも言う。
throw new 例外クラス名("エラーメッセージ");
※スロー宣言は throw s で「s」があるから間違わないように。

if ( name == null) {
    throw new NullPointerException("null発生!");
}

オリジナル例外クラスの定義

チェック例外(Exception 系例外)を継承すればオリジナルエラーメッセージの例外を作れる。
(非チェック例外で作成する状況はほとんどない。)

OrignalException.java
public class UnRecitedSpellException extends Exception {
    //エラーメッセージを受け取るコンストラクタ
    public UnRecitedSpellException(String msg) {
        super(msg); //作成したエラーメッセージを親に渡している。
    }
}
Main.java
public class Main {
    public static void main(String[] args){
        try {
            throw new UnRecitedSpellException("呪文が詠唱が間違っているぜ!")
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ThymeleafでTimestamp型をフォーマットする

  @GetMapping(value = "")
  public String init(Model m) {
    LocalDateTime ldt = LocalDateTime.now();
    Timestamp ts = Timestamp.valueOf(ldt);
    m.addAttribute("ts", ts); // ts = 2021-01-09 17:38:27.918333
    return "index";
  }
<p th:text="${#dates.format(new java.util.Date(ts), 'yyyy-MM-dd hh:mm')}"></p>
<!-- 出力: 2021-01-09 17:38 -->
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】全角⇔半角の変換

・参考
TERASOLUNA Server Framework for Java 7.6文字列処理

ライブラリの追加

https://mvnrepository.com/artifact/org.terasoluna.gfw/terasoluna-gfw-string

maven
<!-- https://mvnrepository.com/artifact/org.terasoluna.gfw/terasoluna-gfw-string -->
<dependency>
    <groupId>org.terasoluna.gfw</groupId>
    <artifactId>terasoluna-gfw-string</artifactId>
    <version>5.6.1.RELEASE</version>
</dependency>
gradle
dependencies {
// https://mvnrepository.com/artifact/org.terasoluna.gfw/terasoluna-gfw-string
compile group: 'org.terasoluna.gfw', name: 'terasoluna-gfw-string', version: '5.6.1.RELEASE'
}

Gradle→Gradleプロジェクトのリフレッシュを忘れずに。

全角から半角へ変換

import org.terasoluna.gfw.common.fullhalf.DefaultFullHalf;

// 中略
        String halfwidth = DefaultFullHalf.INSTANCE.toHalfwidth(aタチヅデプ);// 濁点・半濁点もOK
        System.out.println(halfwidth); //aタチヅデプ

半角から全角へ変換

import org.terasoluna.gfw.common.fullhalf.DefaultFullHalf;

// 中略
        String fullwidth = DefaultFullHalf.INSTANCE.toFullwidth("ア゙!A8ガザ");
        System.out.println(fullwidth);// ア゛!A8ガザ

その他の方法

Javaの文字変換ライブラリICU4Jを使ってみよう
Javaにおける文字列の全角⇔半角変換について
他にもICU4Jやjava.text.normalizerで変換する方法があります。
normalizerだと全角カタカナ→半角カタカナ変換ができませんでした

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

リダイレクト先に値を渡す(メッセージ表示)

リダイレクト先でメッセージを表示させたかったが少し手こずったので学習記録用記事

リダイレクトで遷移先に値を渡す

値を渡す側のコントローラー?

Controller.java
@RequestMapping(value="/create", method=RequestMethod.POST)
public String create(RedirectAttributes redirectAttributes){

    redirectAttributes.addFlashAttribute("message","リダイレクト先に表示したいメッセージ")

    return "redirect:/index"
}

値を受け取る側のコントローラー?

Controller.java
@GetMapping("/index")
public String getIndex(@ModelAttribute("message") String message,Model model){
    model.addAttribute("message",message);
    return "/index";
}

インターフェース RedirectAttributes

addFlashAttribute()は、
実際には属性をフラッシュマップに格納
addFlashAttribute()のの利点は、フラッシュ属性にほとんどすべてのオブジェクトを格納できること

addAttribute()
基本的に、からリクエストパラメータを構築
属性とリクエストで目的のページにリダイレクト
addAttribute()を使用すると、追加したオブジェクトが取得される。通常のリクエストパラメータに変換すると、Stringやプリミティブなどのオブジェクトタイプにかなり制限される。

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

【.net、Java連携】C#.netで.jarを実行するンジャー

どうしてもJavaを使わないとできない処理をC#.netから起動する必要が出た場合、
その実装に困りますよね…
そこで参上!C#.netで.jarを実行するンジャー!!(ここまで茶番)

Java側のプログラム

java側のプログラムはmainメソッド(関数)を入れます。
main関数のコマンドライン引数"args"でC#から値を受け取ります。
下記サンプルプログラムでは、1つの引数でメッセージ:msgを受け取り、それを表示しています。

Sample.java
    public static void main(String[] args) {
        // TODO 自動生成されたメソッド・スタブ
        if (args.length <= 0) {
            System.out.println("出力メッセージなし");
        } else {
            System.out.println("出力メッセージ:" + args[0]);
        }
        // Enterキー入力待ち
        // 参考:https://stackoverflow.com/questions/26184409/java-console-prompt-for-enter-input-before-moving-on
        System.out.println("Press \"ENTER\" to exit...");
        Scanner scanner = new Scanner(System.in);
        scanner.nextLine();
    }

プログラムができたら、実行可能な.jarファイルを生成しましょう。

C#(.net)側のプログラム

C#側では、Processクラスのオブジェクトで.jarファイルを起動します。
.jarファイルは、C#(.net)の実行ファイルが生成されるDebug/Releaseフォルダに配置するとファイル指定が楽です。

// コマンドプロンプトを表示して実行する場合
Process.Start("java", "-jar (.jarファイル名orパス) (引数) (引数)…"))
// コマンドプロンプトを表せずして実行する場合
Process.Start("javaw", "-jar (.jarファイル名orパス) (引数) (引数)…"))

.jarファイルの実行中にC#のプログラムを止めたい場合、WaitForExitメソッドを使いましょう。

// 終了待ち
jar.WaitForExit();

.jarファイルの実行結果は、ExitCodeメソッドで受け取れます。
※javaのmainメソッドが void main() なら、戻り値0で正常終了

// 結果取得(0:正常終了)
jar.ExitCode();

オブジェクトを作成しますので、usingやclose,disposeなどによるオブジェクト解放をお忘れなく。
参考:確保したリソースを忘れずに解放するには?[C#/VB]
実装例↓

ConnectJar.cs
        /// <summary>
        /// .jar実行
        /// </summary>
        /// <param name="msg">メッセージ</param>
        /// <returns>True:成功/False:失敗</returns>
        public static bool Excecute(string msg)
        {
            bool result = false;
            Process jar = null;
            try
            {
                // .jarをプロセスとして起動
                using (jar = Process.Start("java", "-jar Sample.jar " + msg))
                {
                    // 終了待ち
                    jar.WaitForExit();
                    // 結果取得(0:正常終了)
                    if (jar.ExitCode == 0) result = true;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("例外発生\n" + e.Message);
            }
            return result;
        }

あとはC#.netを実行するだけ…!
サンプルコード全体は下記リポジトリにあります。
CSJarソースコード

補足

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

[.net、Java連携]C#.netで.jarを実行するンジャー

どうしてもJavaを使わないとできない処理をC#.netから起動する必要が出た場合、
その実装に困りますよね…
そこで参上!C#.netで.jarを実行するンジャー!!(ここまで茶番)

Java側のプログラム

java側のプログラムはmainメソッド(関数)を入れます。
main関数のコマンドライン引数"args"でC#から値を受け取ります。
下記サンプルプログラムでは、1つの引数でメッセージ:msgを受け取り、それを表示しています。

Sample.java
    public static void main(String[] args) {
        // TODO 自動生成されたメソッド・スタブ
        if (args.length <= 0) {
            System.out.println("出力メッセージなし");
        } else {
            System.out.println("出力メッセージ:" + args[0]);
        }
        // Enterキー入力待ち
        // 参考:https://stackoverflow.com/questions/26184409/java-console-prompt-for-enter-input-before-moving-on
        System.out.println("Press \"ENTER\" to exit...");
        Scanner scanner = new Scanner(System.in);
        scanner.nextLine();
    }

プログラムができたら、実行可能な.jarファイルを生成しましょう。

C#(.net)側のプログラム

C#側では、Processクラスのオブジェクトで.jarファイルを起動します。
.jarファイルは、C#(.net)の実行ファイルが生成されるDebug/Releaseフォルダに配置するとファイル指定が楽です。

// コマンドプロンプトを表示して実行する場合
Process.Start("java", "-jar (.jarファイル名orパス) (引数) (引数)…"))
// コマンドプロンプトを表示せずに実行する場合
Process.Start("javaw", "-jar (.jarファイル名orパス) (引数) (引数)…"))

.jarファイルの実行中にC#のプログラムを止めたい場合、WaitForExitメソッドを使いましょう。

// 終了待ち
jar.WaitForExit();

.jarファイルの実行結果は、ExitCodeメソッドで受け取れます。
※javaのmainメソッドが void main() なら、戻り値0で正常終了

// 結果取得(0:正常終了)
jar.ExitCode();

オブジェクトを作成しますので、usingやclose,disposeなどによるオブジェクト解放をお忘れなく。
参考:確保したリソースを忘れずに解放するには?[C#/VB]
実装例↓

ConnectJar.cs
        /// <summary>
        /// .jar実行
        /// </summary>
        /// <param name="msg">メッセージ</param>
        /// <returns>True:成功/False:失敗</returns>
        public static bool Excecute(string msg)
        {
            bool result = false;
            Process jar = null;
            try
            {
                // .jarをプロセスとして起動
                using (jar = Process.Start("java", "-jar Sample.jar " + msg))
                {
                    // 終了待ち
                    jar.WaitForExit();
                    // 結果取得(0:正常終了)
                    if (jar.ExitCode == 0) result = true;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("例外発生\n" + e.Message);
            }
            return result;
        }

あとはC#.netを実行するだけ…!
サンプルコード全体は下記リポジトリにあります。
CSJarソースコード

補足

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

パスワード変更機能

Springでパスワード変更機能の実装
学習の記録用として記載

DBのパスワードと入力されたパスワードの照合

・パスワードはハッシュ化されたものが登録されている状態

※DBにハッシュ化(暗号化)してパスワードを保存⬇️

Service.java
@Autowired
PasswordEncoder;

public Entity save(Entity user, String rawPassword){

    String encodedPassword = passwordEncoder.encode(password);
    user.setPassword(encodedPassword);
    return Repository.save(user);
}

PasswordEncoderとは

Spring Securityが提供するクラスで、DBなどにログインパスワードを保存するときの暗号化に関わるクラス
・パスワードを暗号化(ハッシュ化)をする
・認証時に入力のパスワードと、保存した暗号化パスワードが一致するかを確認する
Spring Security 公式リファレンス

・入力したパスワードとDBに保存されているハッシュかされたパスワードが一致するかを照合する

Controller.java
//入力したパスワードとDBのハッシュ化されているパスワードの照合
if (passwordEncoder.matches(rawPass,dbPass)){
   //一致すれば画面遷移
   return "/index";
}

Image from Gyazo
Spring Security 公式リファレンス

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