■Java
Java言語と実行環境であるJVM(Java Virtual Machine)を中心としたプログラミング技術
JVMがプログラムを読みこんで実行する仕組みで、JVMは環境ごとに用意されている
これにより、CPUやOSが変わっても同じプログラムをそのまま動かすことができる
Javaのプログラムを開発するには、JDK(Java Development Kit)というソフトウェアパッケージを使用する
JDKには、JavaコンパイラやJVMが含まれている
現在Javaの開発は、OpenJDKというプロジェクトで、オープンソースで進められている
OpenJDKプロジェクトで提供されるのはソースコードなので、
OpenJDKを使用したい場合は自分でビルドを行なってバイナリデータを作成する必要がある
ただしこれは敷居が高いので、オラクルをはじめとした複数のベンダーやグループからバイナリデータが提供されている
Oracleからは「Oracle OpenJDK」が提供されており、サポート期間が6ヶ月だが機能は有償版と同じ
OracleJDKは本番環境での利用が原則有償とされた時期があるが、
2021年に再び用途を問わず無償で利用できるようになった
■Javaのバージョン
バージョン番号の形式は、長い歴史の中で何度も変わっていて解りづらい
Javaのバージョン番号の形式を理解する - Magnolia Tech
https://blog.magnolia.tech/entry/2021/11/14/213822
Javaバージョンメモ(Hishidama's Java version Memo)
https://www.ne.jp/asahi/hishidama/home/tech/java/version.html
■Javaのインストール
2022年12月時点でJava19が最新だが、
Java 7, 8, 11, 17 がLTSなので、Java17を使用する
Java Downloads | Oracle
https://www.oracle.com/java/technologies/downloads/
ページ内の「Java 17 → Windows」から「x64 Installer」をダウンロードする
ダウンロードしたファイルを実行してインストールを行うと、以下にインストールされた
C:\Program Files\Java\jdk-17.0.5
コマンドプロンプトでバージョンを確認すると、以下のように表示された
また、この時点でJShellを使用できるようになる(JShellについては、後述の「JShellでのプログラム実行」を参照)
>java -version
java version "17.0.5" 2022-10-18 LTS
Java(TM) SE Runtime Environment (build 17.0.5+9-LTS-191)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.5+9-LTS-191, mixed mode, sharing)
■IntelliJ IDEAのインストール
IntelliJ IDEAのインストールにはToolbox Appを使用するといい
単体のインストーラーもあるが、自動アップデートが行えないなどがあるため、Toolbox Appの使用が推奨されているらしい
JetBrains Toolbox App: Manage Your Tools with Ease
https://www.jetbrains.com/toolbox-app/
「.exe」からインストーラーをダウンロードする
ダウンロードしたファイルを実行してインストールを行うと、以下にインストールされた
また、タスクトレイにToolboxのアイコンが表示された
タスクトレイからToolboxを開くと、初回は利用規約の同意画面が表示される。同意して進む
JetBrains製のIDEが一覧表示されるので、「IntelliJ IDEA Community Edition」をインストールする
(Android Studioもここからインストールできるみたい)
インストール完了後、Toolboxから「IntelliJ IDEA Community Edition」をクリックすると起動できる
はじめから日本語化されていた
■IntelliJ IDEAの日本語化
Toolbox Appからインストールした場合、IntelliJ IDEAは始めから日本語化されている
…が、IntelliJ IDEAのアップデートを行った際に英語になってしまうことがあった
(アップデート完了時に「IntelliJ IDEA 2023.1 の新機能」が表示されたので、メジャーバージョンアップ時に発生するのかもしれない)
この場合、日本語言語パックのプラグインをインストールすることで、改めて日本語化できる
IntelliJ IDEA 日本語化 - JetBrains 公式言語パックのインストール
https://pleiades.io/pages/pleiades_jetbrains_manual.html
以下は2023年4月に日本語化を試した際のメモ(アップデートすると英語表示になってしまったため)
1. 最初のWelcome画面を開く(プロジェクトを開いている場合は閉じる)
2. 「Plugins」をクリック
3. Marketplaceの検索で「japan」と入力
4. 「Japanese Language Pack / 日本語言語パック」が見つかるので「Install」をクリック
5. 「Restart IDE」ボタンをクリックして、アプリケーションを再起動
■メモ
JavaのJREとは?JDKとの違いやインストール方法まで解説 | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
https://www.sejuku.net/blog/63551
2019年にJavaを利用している人は全員理解すべきことを説明してみる - orangeitems’s diary
https://www.orangeitems.com/entry/2018/02/08/152022
Javaのインストール2023年版 - きしだのHatena
https://nowokay.hatenablog.com/entry/2023/01/12/211150
Javaコーディング規約 | Future Enterprise Coding Standards
https://future-architect.github.io/coding-standards/documents/forJava/
■IntelliJ IDEAでのプログラム作成と実行
Welcome画面で「新規プロジェクト」ボタンをクリックし、以下の内容で作成する
名前: Hello
言語: Java
ビルドシステム: Maven
JDK: 17
作成すると、同期や依存関係のダウンロードが行われる
2〜3分ほど待つ
右下に「ビルド済みの共有インデックスをダウンロード」というポップアップが表示された場合、「常にダウンロード」をクリックする
なお、これはメニューバーの「ファイル → 設定 → ツール → 共有インデックス → JDK」からも設定できる
C:\Users\refirio\IdeaProjects\Hello\src\main\java\org\example\Main.java
に以下のファイルが作成されていることを確認できる
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
ツールバーの「実行」ボタンを押すと、以下のようにプログラムが実行される
(ここに表示されたコマンドをコマンドプロンプトに張り付けても、同様の実行結果を得ることができる)
"C:\Program Files\Java\jdk-17.0.5\bin\java.exe" -javaagent:C:\Users\refirio\AppData\Local\JetBrains\Toolbox\apps\IDEA-C\ch-0\223.8214.52\lib\idea_rt.jar=51303:C:\Users\refirio\AppData\Local\JetBrains\Toolbox\apps\IDEA-C\ch-0\223.8214.52\bin -Dfile.encoding=UTF-8 -classpath C:\Users\refirio\IdeaProjects\Hello\target\classes org.example.Main
Hello world!
プロセスは終了コード 0 で終了しました
■ビルドシステム
プログラムのビルド作業を自動化するためのもの
それぞれの違いは、以下などを参考にする
ビルドツール make / ant / maven / gradle ざっくり理解メモ - Qiita
https://qiita.com/MahoTakara/items/ff73338e218b656bedfa
大まかに、以下のような特徴がある
現在新規に作る場合は、MavenもしくはGradleを使うといい
Make ... C/C++のビルドで利用されることが多いが、Javaのビルドに利用することもできる
Ant ... Javaで書かれたプログラムで、Makeの考え方が解っていれば取っつきやすい
自由度が高いためプロジェクトごとに構成が異なり、設定の読解が難しくなるという難点がある
Maven ... Antの反省を生かして、あえて自由度を減らしてデフォルトの構成とプラグインの組み合わせで事足りるように設計された
シンプルな反面、デフォルトの設定から外れたことをしようとすると、途端に複雑・冗長になってしまう
Gradle ... Mavenの反省を生かして、Mavenで培われたデフォルト構成を引き継ぎつつ、必要に応じてGroovyやKotlinといったプログラミング言語で記述できる柔軟性を兼ね備えている
Mavenを理解していれば、Gradleを使うのは比較的容易
■コマンドプロンプトでのプログラム実行
以下のようにして、IntelliJ IDEAを使わずにプログラムを実行できる
「-Dfile.encoding=UTF-8」は文字化け対策
cd C:\Users\Yamano\IdeaProjects\Hello\src\main\java\org\example
java -Dfile.encoding=UTF-8 Main.java
■JShellでのプログラム実行
コマンドラインツールで簡単な処理を実行できる
以下はコマンドプロンプトから実行した例
C:\Users\refirio>jshell
| JShellへようこそ -- バージョン17.0.5
| 概要については、次を入力してください: /help intro
jshell> 5 + 2
$1 ==> 7
jshell> /exit
| 終了します
■基本の文法
■値と計算
package org.example;
public class Main {
public static void main(String[] args) {
// 四則計算
System.out.println(5 + 2); // 7
System.out.println(5 - 2); // 3
System.out.println(5 * 2); // 10
System.out.println(5 / 2); // 2
System.out.println(5 % 2); // 1
// 計算の優先順位
System.out.println(2 + 3 * 4); // 14
System.out.println((2 + 3) * 4); // 20
// 計算に実数を使用すると結果も実数になる
System.out.println(5.0 / 2); // 2
// 文字列の表示
System.out.println("Hello world!");
// 文字列のフォーマット
System.out.println("%sは%,d円です。".formatted("書籍", 1200));
}
}
■変数と型
package org.example;
public class Main {
public static void main(String[] args) {
// 変数(varによる型推論)
var a1 = "TEST1";
var a2 = 'A';
var a3 = 500;
var a4 = 2.5;
System.out.println("a1 = " + a1 + ", a2 = " + a2 + ", a3 = " + a3 + ", a4 = " + a4);
// 変数(型指定)
String b1 = "TEST2"; // String型は文字列を扱うクラス
char b2 = 'B';
int b3 = 600;
double b4 = 3.2;
System.out.println("b1 = " + b1 + ", b2 = " + b2 + ", b3 = " + b3 + ", b4 = " + b4);
// 型変換
int c1 = Integer.parseInt("1234");
double c2 = Double.parseDouble("5.6");
int c3 = (int)b4;
double c4 = (double)b3;
System.out.println("c1 = " + c1 + ", c2 = " + c2 + ", c3 = " + c3 + ", c4 = " + c4);
// 変数(ラッパークラスでの型指定)
String d1 = "TEST2"; // String型は文字列を扱うクラス
Character d2 = 'B';
Integer d3 = 600;
Double d4 = 3.2;
System.out.println("d1 = " + d1 + ", d2 = " + d2 + ", d3 = " + d3 + ", d4 = " + d4);
}
}
■標準API
package org.example;
import java.time.*;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
// 現在の日付
System.out.println(LocalDate.now());
//System.out.println(java.time.LocalDate.now());
// 現在の時間
System.out.println(LocalTime.now());
//System.out.println(java.time.LocalTime.now());
// 現在の日時
System.out.println(LocalDateTime.now());
//System.out.println(java.time.LocalDateTime.now());
// 日時の操作
var date = LocalDate.of(2022, 12, 28);
System.out.println(date);
var time = LocalTime.of(17, 30);
System.out.println(time);
var datetime = LocalDateTime.of(date, time);
System.out.println(datetime);
// 日時のフォーマット1
System.out.printf("%tY年%<tm月%<td日%n", datetime);
//System.out.println("%tY年%<tm月%<td日".formatted(datetime));
// 日時のフォーマット2
var formatter = DateTimeFormatter.ofPattern("yyyy年M月d日");
System.out.println(formatter.format(date));
}
}
■SwingによるGUI
package org.example;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
// ウインドウを定義
var frame = new JFrame("テスト");
frame.setSize(600, 400);
frame.setLocation(200, 200);
frame.setVisible(true);
// ラベルを定義
JLabel label = new JLabel("ハロー!");
frame.add(label);
// パネルを定義
JPanel panel = new JPanel();
panel.add(label);
frame.add(panel);
/*
// ラベルを定義
JLabel label = new JLabel("ハロー!");
label.setBounds(10, 10, 100, 20);
// パネルを定義
JPanel panel = new JPanel();
panel.setLayout(null);
panel.add(label);
frame.add(panel);
*/
}
}
■条件分岐
package org.example;
public class Main {
public static void main(String[] args) {
// 条件分岐
var str = "test";
if (str.equals("test")) {
System.out.println("等しい");
} else {
System.out.println("等しくない");
}
// switch(Java14から導入された書き方)
var num = 2;
switch (num) {
case 1 -> System.out.println("数字は1");
case 2, 3 -> System.out.println("数字は2もしくは3");
default -> System.out.println("その他の数字");
}
// switch(Java14より前の書き方)
switch (num) {
case 1:
System.out.println("数字は1");
break;
case 2:
case 3:
System.out.println("数字は2もしくは3");
break;
default:
System.out.println("その他の数字");
break;
}
}
}
■配列
package org.example;
public class Main {
public static void main(String[] args) {
// 配列
var oddNumbers = new int[5];
oddNumbers[0] = 1;
oddNumbers[1] = 3;
oddNumbers[2] = 5;
oddNumbers[3] = 7;
oddNumbers[4] = 10;
System.out.println(oddNumbers[1]); // 3
System.out.println(oddNumbers.length); // 5
// 配列(初期化と同時に値を設定)
var evenNumbers = new int[]{2, 4, 6, 8, 10, 12};
System.out.println(evenNumbers[1]); // 4
System.out.println(evenNumbers.length); // 6
// 型のラッパークラスでも宣言できる
//var evenNumbers = new Integer[]{2, 4, 6, 8, 10, 12};
// 変数に型を指定すると、初期化時の宣言を簡略ができる
//int[] evenNumbers = {2, 4, 6, 8, 10, 12};
//Integer[] evenNumbers = {2, 4, 6, 8, 10, 12};
// 文字列クラスの配列
var names = new String[]{"山田", "佐藤", "鈴木"};
System.out.println(names[0]); // 山田
System.out.println(names.length); // 3
// 多次元配列
var numbers = new int[][]{{1, 2, 3}, {4, 5, 6}};
System.out.println(numbers[0][0]); // 1
System.out.println(numbers.length); // 2
}
}
■リスト
package org.example;
import java.util.*;
public class Main {
public static void main(String[] args) {
// 変更できないリスト
var names1 = List.of("山田", "佐藤", "鈴木");
System.out.println(names1.get(0)); // 山田
System.out.println(names1.size()); // 3
// 変更できるリスト(型はジェネリクスで指定)
var names2 = new ArrayList<String>();
names2.add("山田");
names2.add("佐藤");
names2.add("鈴木");
System.out.println(names2.get(1)); // 佐藤
System.out.println(names2.size()); // 3
// 変更できるリストの初期値を指定(型はジェネリクスで指定)
List<String> names3 = new ArrayList<String>(names1);
System.out.println(names3.get(2)); // 鈴木
System.out.println(names3.size()); // 3
// 変更できるリストの初期値を指定&ジェネリクスの型推論
var names4 = new ArrayList<>(names1);
System.out.println(names4.get(2)); // 鈴木
System.out.println(names4.size()); // 3
// 基本型のラッパークラス(ジェネリクスには参照型を指定する必要があるため、int, double, boolean, charではなくInteger, Double, Boolean, Characterとして扱う)
List<Integer> numbers = new ArrayList<Integer>(List.of(2, 4, 6));
System.out.println(numbers.get(1)); // 4
System.out.println(numbers.size()); // 3
}
}
■マップ
package org.example;
import java.util.*;
public class Main {
public static void main(String[] args) {
// 変更できないマップ
var fruits1 = Map.of("apple", "リンゴ", "orange", "ミカン", "grape", "ブドウ");
System.out.println(fruits1.get("orange")); // ミカン
System.out.println(fruits1.size()); // 3
// 変更できるマップ(型はジェネリクスで指定)
var fruits2 = new HashMap<String, String>();
fruits2.put("apple", "リンゴ");
fruits2.put("orange", "ミカン");
fruits2.put("grape", "ブドウ");
System.out.println(fruits2.get("orange")); // ミカン
System.out.println(fruits2.size()); // 3
}
}
■繰り返し
package org.example;
public class Main {
public static void main(String[] args) {
// forによる繰り返し
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// whileによる繰り返し
int j = 0;
while (j < 5) {
System.out.println(j);
j++;
}
// Listを定義
var fruits = List.of("Apple", "Orange", "Grape");
// 基本for文でListを処理
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
// 拡張for文でListを処理
for (var fruit: fruits) {
System.out.println(fruit);
}
}
}
■Stream
以下はループで処理したもの
package org.example;
import java.util.*;
public class Main {
public static void main(String[] args) {
var names = List.of("Yamamoto", "Sato", "Suzuki");
// 5文字以上の名前を抽出(ループで処理)
var result = new ArrayList<String>();
for (var name: names) {
if (name.length() >= 5) {
result.add(name);
}
}
System.out.println(result);
// 5文字以上の名前をカウント(ループで処理)
var count = 0;
for (var name: names) {
if (name.length() >= 5) {
count++;
}
}
System.out.println(count);
}
}
以下はStreamで処理したもの(結果は同じ)
package org.example;
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
var names = List.of("Yamamoto", "Sato", "Suzuki");
// 5文字以上の名前を抽出(Streamで処理)
var result = names.stream()
.filter(s -> s.length() >= 5)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(result);
// 5文字以上の名前をカウント(ループで処理)
var count = names.stream()
.filter(s -> s.length() >= 5)
.count();
System.out.println(count);
}
}
以下のような処理ができる
package org.example;
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
var names = List.of("Yamamoto", "Sato", "Suzuki");
// 配列をそのまま返す
var result1 = names.stream()
.toList();
System.out.println(result1); // [Yamamoto, Sato, Suzuki]
// 個数を数える
var result2 = names.stream()
.count();
System.out.println(result2); // 3
// すべての要素が「Y」を含むか確認
var result3 = names.stream()
.allMatch(s -> s.contains("Y"));
System.out.println(result3); // false
// いずれか1つの要素が「Y」を含むか確認
var result4 = names.stream()
.anyMatch(s -> s.contains("Y"));
System.out.println(result4); // true
// すべての要素が「X」を含まないか確認
var result5 = names.stream()
.noneMatch(s -> s.contains("X"));
System.out.println(result5); // true
// 要素を1つずつ出力
names.stream()
.forEach(s -> System.out.println(s));
// 中間処理が不要なら、Streamを介さずに処理できる
names.forEach(s -> System.out.println(s));
// 要素を1つずつ出力
names.stream()
.limit(2) // 処理する要素は2つまで
.sorted() // ソート
.distinct() // 重複があれば省く
.forEach(s -> System.out.println(s));
var integers = new int[]{2, 4, 6, 8, 10};
var doubles = new double[]{2.0, 4.0, 6.0, 8.0, 10.0};
// 明示的に整数を扱う
var result6 = IntStream.of(integers).sum();
System.out.println(result6); // 30
// 明示的に少数を扱う
var result7 = DoubleStream.of(doubles).sum();
System.out.println(result7); // 30.0
}
}
Optionalで値の有無を管理できる
package org.example;
import java.util.*;
public class Main {
public static void main(String[] args) {
var names = List.of("Yamamoto", "Sato", "Suzuki");
// オプショナル(値があるとき)
var result1 = names.stream()
.filter(s -> s.length() >= 3)
.findAny();
System.out.println(result1); // Optional[Yamamoto]
// オプショナル(値がないとき)
var result2 = names.stream()
.filter(s -> s.length() >= 10)
.findAny();
System.out.println(result2); // Optional.empty
// オプショナルに値があるかどうかを判定
if (result1.isPresent()) {
System.out.println("result1に値があります"); // こちらが出力される
} else {
System.out.println("result1に値がありません");
}
if (result2.isEmpty()) {
System.out.println("result2に値がありません"); // こちらが出力される
} else {
System.out.println("result2に値があります");
}
}
}
■正規表現
package org.example;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("0\\d+-\\d+-\\d+");
if (pattern.matcher("090-1234-5678").find()) {
System.out.println("電話番号です。");
} else {
System.out.println("電話番号ではありません。");
}
if (pattern.matcher("12345").find()) {
System.out.println("電話番号です。");
} else {
System.out.println("電話番号ではありません。");
}
}
}
■メソッド
staticとして宣言すると、「クラス名.メソッド名()」でメソッドを呼び出すことができる
さらにクラス名を省略することで、メソッドを関数のように扱うことができる
package org.example;
public class Main {
public static void main(String[] args) {
// 基本的なメソッド
message();
//Main.message();
// 引数のあるメソッド
greeting("Java");
// 戻り値のあるメソッド
System.out.println("計算結果は" + twice(3) + "です。");
}
/*
* 基本的なメソッド
*/
static void message() {
System.out.println("Hello world!");
}
/*
* 引数のあるメソッド
*/
static void greeting(String name) {
System.out.println("Hello " + name + "!");
}
/*
* 戻り値のあるメソッド
*/
static int twice(int x) {
return x * 2;
}
}
■ファイル入出力
ファイルの書き込み
package org.example;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
var message = """
TEST1
TEST2
TEST3
""";
try {
var p = Path.of("test.txt");
Files.writeString(p, message);
} catch (IOException e) {
System.out.println("ファイルに書き込めません: " + e.getMessage());
}
System.out.println("Complete!");
}
}
ファイルの読み込み
package org.example;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
try {
var p = Path.of("test.txt");
String s = Files.readString(p);
System.out.println(s);
} catch (IOException e) {
System.out.println("ファイルを読み込めません: " + e.getMessage());
}
System.out.println("Complete!");
}
}
■HTTP
クライアント
package org.example;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
URI uri = URI.create("https://example.com/");
HttpRequest req = HttpRequest.newBuilder(uri).build();
try {
HttpResponse<String> response = client.send(req, HttpResponse.BodyHandlers.ofString());
String body = response.body();
body.lines().limit(5).forEach(System.out::println);
} catch (IOException e) {
System.out.println("IOException: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("InterruptedException: " + e.getMessage());
}
System.out.println("Complete!");
}
}
サーバ(プログラム実行後、ブラウザで
http://localhost:8880/ にアクセスできる)
package org.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Main {
public static void main(String[] args) throws IOException {
var server = new ServerSocket(8880);
while (true) {
try (Socket soc = server.accept();
var isr = new InputStreamReader(soc.getInputStream());
var bur = new BufferedReader(isr);
var w = new PrintWriter(soc.getOutputStream()))
{
System.out.println("connect from " + soc.getInetAddress());
bur.lines()
.takeWhile(line -> !line.isEmpty())
.forEach(System.out::println);
w.println("""
HTTP/1.1 200 OK
Content-Type: text/html
<html>
<head><title>Hello</title></head>
<body><h1>Hello</h1><p>It works!</p></body>
</html>
""");
}
}
}
}
■列挙型
package org.example;
public class Main {
enum Gender {
MALE, FEMALE, NONE
}
public static void main(String[] args) {
var gender = Gender.NONE;
if (gender == Gender.MALE) {
System.out.println("男性");
} else if (gender == Gender.FEMALE) {
System.out.println("女性");
} else if (gender == Gender.NONE) {
System.out.println("不明");
}
}
}
■クラス
package org.example;
public class Main {
public static void main(String[] args) {
// クラスの利用
var user = new User("Yamashita", 19);
System.out.println(user.name() + "さんの年齢は" + user.age() + "歳です。");
if (user.isAdult()) {
System.out.println(user.name() + "さんは成年です。");
} else {
System.out.println(user.name() + "さんは未成年です。");
}
}
}
/*
* クラス
*/
class User {
public String name;
public int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() {
return name;
}
public int age() {
return age;
}
public boolean isAdult() {
if (age >= 20) {
return true;
} else {
return false;
}
}
}
以下のようにレコードクラスを使うことで、クラスをシンプルに記述できる(Java16から利用可能)
インスタンス変数と同じ名前でゲッターが実行されるが、セッターは実装されない
getNmae()/setName() のような実装も選べるといいかと思ったが、インスタンス変数は原則普遍にするべき…ということだと思われる
package org.example;
public class Main {
public static void main(String[] args) {
// レコードクラスの利用
var user = new User("Yamashita", 19);
System.out.println(user.name() + "さんの年齢は" + user.age() + "歳です。");
if (user.isAdult()) {
System.out.println(user.name() + "さんは成年です。");
} else {
System.out.println(user.name() + "さんは未成年です。");
}
}
/*
* レコードクラス
*/
record User(String name, int age) {
public boolean isAdult() {
if (age >= 20) {
return true;
} else {
return false;
}
}
}
}
Main.java と同じ場所に例えば User.java を作成すると、以下のようにメインのプログラムから呼び出すことができる
package org.example;
public class User {
public String name;
public int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() {
return name;
}
public int age() {
return age;
}
public boolean isAdult() {
if (age >= 20) {
return true;
} else {
return false;
}
}
}
package org.example;
public class Main {
public static void main(String[] args) {
// クラスの利用
var user = new User("Yamashita", 19);
System.out.println(user.name() + "さんの年齢は" + user.age() + "歳です。");
if (user.isAdult()) {
System.out.println(user.name() + "さんは成年です。");
} else {
System.out.println(user.name() + "さんは未成年です。");
}
}
}
■Mavenの利用
デフォルトの pom.xml は以下のとおり
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Hello</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
今回は「businessCalendar4j」の利用を試す
これは、日本やアメリカの休祝日、または営業日を求めることができるライブラリ
dependencies のブロックを追加し、以下のように指定する
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Hello</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>one.cafebabe</groupId>
<artifactId>businessCalendar4j</artifactId>
<version>1.21</version>
</dependency>
</dependencies>
</project>
新しいライブラリを追加すると、画面右上に「Mavenの変更を読み込む Ctrl+Shift+O」ボタンが表示される
クリックすると指定したライブラリが読み込まれ、依存解決が行われる
※dependencyの内容入力には保管機能が働く
これはメニューバーの「ファイル → 設定 → ビルド、実行、デプロイ → ビルドツール → Maven → リポジトリ」で
「
https://repo.maven.apache.org/maven2/」を選択して「アップデート」を押すことで、最新の状態に更新できる
(この処理は数分から数十分かかる)
以下のプログラムで、休日を一覧表示できる
package org.example;
import one.cafebabe.bc4j.BusinessCalendar;
import static one.cafebabe.bc4j.BusinessCalendar.JAPAN;
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
BusinessCalendar.newBuilder().holiday(JAPAN.PUBLIC_HOLIDAYS).build().getHolidaysBetween(
LocalDate.of(2022, 1, 1),
LocalDate.of(2022, 12, 31)
).forEach(System.out::println);
}
}
正常に実行できれば、以下のように2022年の祝日一覧が表示される
Holiday{date=2022-01-01, name='元日'}
Holiday{date=2022-01-10, name='成人の日'}
Holiday{date=2022-02-11, name='建国記念の日'}
Holiday{date=2022-02-23, name='天皇誕生日'}
Holiday{date=2022-03-21, name='春分の日'}
Holiday{date=2022-04-29, name='昭和の日'}
Holiday{date=2022-05-03, name='憲法記念日'}
Holiday{date=2022-05-04, name='みどりの日'}
Holiday{date=2022-05-05, name='こどもの日'}
Holiday{date=2022-07-18, name='海の日'}
Holiday{date=2022-08-11, name='山の日'}
Holiday{date=2022-09-19, name='敬老の日'}
Holiday{date=2022-09-23, name='秋分の日'}
Holiday{date=2022-10-10, name='スポーツの日'}
Holiday{date=2022-11-03, name='文化の日'}
Holiday{date=2022-11-23, name='勤労感謝の日'}
■保守しやすい設計
以下のように、金額を表すクラスがあるとする
package org.example;
import java.util.Currency;
public class Money {
int amount; // 金額
Currency currency; // 通貨単位
}
このクラスは、以下のようにして使用できる
package org.example;
import java.util.Currency;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// クラスの利用
var money = new Money();
// 所持金
money.amount = 1000;
money.currency = Currency.getInstance(Locale.JAPAN);
// バリデート
if (money.amount < 0) {
System.out.println("金額には0以上を指定してください。");
System.exit(0);
}
if (money.currency == null) {
System.out.println("通貨単位を指定してください。");
System.exit(0);
}
// 商品金額
var goods = 300;
// 金額の計算
if (goods > money.amount) {
System.out.println("金額が足りません。");
}
money.amount -= goods;
// 結果の表示
System.out.println("残金は" + money.amount + "(" + money.currency + ")です。");
}
}
ただしこのMoneyクラスは、クラス単体で正常に動作するように設計できていない
具体的には
・金額や通貨単位がセットされないまま使われる可能性がある
・金額が単純な数値型なので、金額以外の意図しない数値を渡される可能性がある
・バリデートや金額の計算など、ロジックがあちこちに散らばる可能性がある。また重複して同じ処理が何度も書かれる可能性がある
・クラス内の値が意図せず変更されてしまう可能性がある
といった問題があり、プログラムの規模が大きくなると仕様変更の際に変更し忘れが発生しやすくなる
今回の場合、以下のようにすることで変更に強いクラスになる
package org.example;
import java.util.Currency;
public class Money {
private final int amount; // 金額
private final Currency currency; // 通貨単位
public Money(final int amount, final Currency currency) {
if (amount < 0) {
throw new IllegalArgumentException("金額には0以上を指定してください。");
}
if (currency == null) {
throw new IllegalArgumentException("通貨単位を指定してください。");
}
this.amount = amount;
this.currency = currency;
}
public int amount() {
return amount;
}
public Currency currency() {
return currency;
}
public Money pay(final Money other) {
if (!currency.equals(other.currency)) {
throw new IllegalArgumentException("異なる通貨単位では計算できません。");
}
if (other.amount > amount) {
throw new IllegalArgumentException("支払う金額が足りません。");
}
final int payed = amount - other.amount;
return new Money(payed, currency);
}
}
package org.example;
import java.util.Currency;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// 所持金
var money = new Money(1000, Currency.getInstance(Locale.JAPAN));
// 商品金額
var goods = new Money(300, Currency.getInstance(Locale.JAPAN));
// 金額の計算
var payed = money.pay(goods);
// 結果の表示
System.out.println("残金は" + payed.amount() + "(" + payed.currency() + ")です。");
}
}
■Tomcat&サーブレット
サーブレットは、Webサーバ上で動作するJavaプログラム
サーブレットを動かすために必要なのがTomcat
TomcatはWebサーバの機能を持っているが、簡易的なものなのでnginxやApacheと組み合わせて使うといい
Apache Tomcatとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
https://wa3.i-3-i.info/word12843.html
Java Servletとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
https://wa3.i-3-i.info/word12835.html
以下に昔検証した際のメモがある(JSP+Servletなので、もはや古い構成。今ならSpringFrameworkを使うのが良さそう)
Dropbox\技術\Tomcat\Tomcat.txt
■SpringBoot
以下を参照
Dropbox\技術\SpringBoot.txt
■コマンドでjarファイルとwarファイルを作成
環境の構築は、以下にある「Vagrantでアプリケーション(WAR)を起動」「EC2でアプリケーション(WAR)を起動」を参照
Dropbox\技術\SpringFramework.txt
■コマンドでjarファイルを作成
jarコマンドを使ってjarファイル、warファイルを作る方法 - Qiita
https://qiita.com/Qui/items/14961678ef939673f744
C:\Users\refirio\Vagrant\java\code\sample_jar\META-INF\MANIFEST.MF
Main-Class: sample.App
C:\Users\refirio\Vagrant\java\code\sample_jar\src\sample\App.java
package sample;
public class App {
public static void main(String[] arg) {
System.out.println("Hello World!");
}
}
C:\Users\refirio\Vagrant\java\code\sample_jar\classes
(カラのフォルダを作成しておく)
$ cd /var/www/sample_jar/
$ javac -sourcepath src -d classes src/sample/App.java
以下にコンパイル済みファイルが作成される
C:\Users\refirio\Vagrant\java\code\sample_jar\classes\sample\App.class
$ jar cvfm sample.jar META-INF/MANIFEST.MF -C classes .
以下にjarファイルが作成される
C:\Users\refirio\Vagrant\java\code\sample_jar\sample.jar
jarファイルの内容を確認
$ jar tf sample.jar
META-INF/
META-INF/MANIFEST.MF
sample/
sample/App.class
jarファイルを実行
$ java -jar sample.jar
Hello World!
■コマンドでwarファイルを作成
jarコマンドを使ってjarファイル、warファイルを作る方法 - Qiita
https://qiita.com/Qui/items/14961678ef939673f744
Maven Repository: javax.servlet > javax.servlet-api > 4.0.1
https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/4.0.1
C:\Users\refirio\Vagrant\java\code\sample_war\lib\javax.servlet-api-4.0.1.jar
(上のURLから入手したファイル)
C:\Users\refirio\Vagrant\java\code\sample_war\src\sample\SampleServlet.java
package sample;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/sample")
public class SampleServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
ServletOutputStream out = resp.getOutputStream();
out.println("Hello World!");
out.flush();
}
}
C:\Users\refirio\Vagrant\java\code\sample_war\WebContent\WEB-INF\classes
(カラのフォルダを作成しておく)
$ cd /var/www/sample_war/
$ javac -sourcepath src -classpath lib/* -d WebContent/WEB-INF/classes src/sample/SampleServlet.java
以下にコンパイル済みファイルが作成される
C:\Users\refirio\Vagrant\java\code\sample_war\WebContent\WEB-INF\classes\sample\SampleServlet.class
$ jar cvf sample.war -C WebContent .
以下にjarファイルが作成される
C:\Users\refirio\Vagrant\java\code\sample_war\sample.war
warファイルの内容を確認
$ jar tf sample.war
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/classes/
WEB-INF/classes/sample/
WEB-INF/classes/sample/SampleServlet.class
Tomcatにデプロイする
Tomcatの環境は、「SpringFramework.txt」の「Vagrantでアプリケーション(WAR)を起動」で作成できるはず
$ sudo su -
# mv /var/www/sample_war/sample.war /opt/apache-tomcat-8.5.54/webapps/sample.war
# cd /opt/apache-tomcat-8.5.54/webapps/
# chown tomcat. sample.war
以下にアクセスすると「Hello World!」と表示される
http://192.168.33.10:8080/sample/sample
引き続き以下などを参考に、データベースへの接続を試したい
JavaによるWebアプリケーション開発
https://www.bigbang.mydns.jp/java-kaihatsu-x.htm