はじめに
気になっていたJavaを教材を通して初めて触ったのでそのまとめと感想を共有します。
初心者なので間違った説明や一般的ではない手順が含まれている可能性があります。
いくつかのところでRubyとの比較をしていますが、比較できるところを全てを取り上げているわけではありません(今の自分じゃ荷が重いです)。
分からなかった部分は自分で調べていたりします。
初心者が学んだことをノートにまとめているんだなーくらいの認識だとありがたいです。
環境
- Windows11
- AdoptOpenJDK
- vscode
Javaの開発案件はRubyとどう違うか
Ruby on Railsの特徴
Ruby on Railsは、小規模の組織で好まれるフレームワーク。
- 特徴
短期間でプロダクトをリリースできること
言語習得のための学習コストが低いこと
Javaの特徴
Javaは、大手企業の基幹システムなど大規模開発に適した言語。
- 特徴
アプリケーションの仕様変更を行いやすいこと
セキュリティが強固であること
長期間にわたる保守・運用がしやすいこと
実行速度が速いこと
RubyとJavaの比較
-
覚えるルールやコードの記述量が多い
→アプリケーション開発の「自由度」とのトレードオフ -
オブジェクト指向を理解が必須
-
開発に着手する際やエラーが発生した時に必要な記事を見つけにくい
→RubyはRails一択、Javaはフレームワークやビルドツールの種類が数多くある
Javaの開発環境構築
AdoptOpenJDK
①JDK(Java開発環境)のダウンロード&インストール
- javac(コンパイラ)
- jar(Jarファイル関連機能)
- javadoc(仕様書の自動生成)
- JVM(Java実行環境)…JVMとAPI
②環境変数の設定
→JDKはここのフォルダにおいてあるよということをPCに教える
PATH…JDK直下の「bin」フォルダを指定(ここにJavaのプログラム本体が格納されている)
JAVA_HOME…JDKのインストール先を指定
実行方法
- ソースコード作成
- コンパイル
- 処理実行
ふむふむ。
教材に具体的な実行方法が記載されてなかったのでchatGPTに聞いてみた。要約すると、
1.ターミナルを開く
今までRailsでアプリ開発をするときはubuntuをターミナルとして使用していたけど、今回はコマンドプロンプトを使用。
→Windowsでは「コマンドプロンプト」、LinuxやMacでは「ターミナル」。
2.対象ディレクトリに移動
[ドライブ名]:
cd "[ファイルが存在するディレクトリのパス]"
C:
cd "C:\MyJavaProjects"
3.Javaファイルのコンパイル
.java ファイルから .class ファイルを生成。
javac [ファイル名].java
javac MyProgram.java
4.プログラムの実行
コンパイルされた.classファイルをJavaランタイムで実行。
javaコマンドを使用し、ファイル名の拡張子を省略して実行。
java [ファイル名(拡張子なし)]
java MyProgram
…毎回クラスファイル作るの?と疑問。
どうやらJavaのプログラムを実行する際に .class ファイルを明示的に生成しない方法として、いくつかのアプローチがあるらしい。
統合開発環境 (IDE)
Eclipse、IntelliJ IDEA、NetBeansなど
Launch Single-File Source-Code Programs
Java 11以降で導入された機能で、Javaソースファイル(.javaファイル)を直接実行することにより、従来のコンパイルステップを省略できるとのこと。
特徴
- 単一ファイルのソースコード実行
- 開発の迅速化
- コンパイルステップの省略
他にもあったけど、単体ファイルの実行をする程度の学習ならとりあえずこれでいいかな?
Javaファイルのコンパイル作業(javac [ファイル名].java)を省略して実行できることを確認。
java [オプション] ファイル名.java [引数...]
java MyProgram.java
この方法のデメリット
- パフォーマンスの低下
直接実行も結局裏でソースコードをコンパイルしてから実行しているそうなんだけど、これがパフォーマンスを下げる可能性があるらしい。 - 大規模開発には向かない
Rubyとの違い
- Ruby: スクリプト言語。ソースコードを直接実行。
- Java: コンパイル言語。ソースコードをコンパイル後実行。
ソースコードの基本ルール
- ブロック:
中カッコ { } で囲まれた領域を「ブロック」と呼ぶ。
{ } 内にさらに { } を記述することを「ネスト」という。
- クラスブロック:
全てのソースコードは「class」というキーワードから始まるクラスブロックに囲まれる。
クラスブロックはプログラムの基本的な構造を形成する。
- メソッドブロック:
クラスブロック直下にネストされるブロックを「メソッドブロック」と呼ぶ。
実行命令は主にメソッドブロック内に記述される。
- 命令文の最後尾には必ず『 ; (セミコロン)』を記述
// クラスブロック
class MyClass {
// メソッドブロック
public static void main(String[] args) {
// ブロック内の命令文
System.out.println("Hello, world!");
// もう一つのブロック(例えばif文のブロック)
if (args.length > 0) {
System.out.println("引数があります。");
}
}
}
変数
変数
データを一つだけ格納できる名前付きの「箱」。
値
変数で扱われるデータ。
型
変数で扱うデータの種類。
変数宣言
新しい変数を作る行為。
型名 変数名;
代入
変数に値を入れる行為。
初期化
変数宣言と同時に値を代入すること。
型名 変数名 = 初期データ;
参照
変数名を指定して変数内の値を取得すること。
定数
初期化後にデータを変更できない変数。
宣言には final
キーワードを使用する。
final 型名 定数名 = 初期値;
型
-
整数型: データ容量に応じて選択する型。一般的には
int
。 -
小数点数型: データ容量に応じて選択する型。一般的には
double
。 -
文字型:
char
とString
の2種類。char
は1文字、String
は文字列。 -
論理値型:
true
またはfalse
のみを格納するboolean
。
int number = 100; // 整数型
double rate = 3.14; // 小数点数型
char letter = 'A'; // 文字型(char)
String name = "Alice"; // 文字型(String)
boolean isTrue = true; // 論理値型
リテラル
- リテラル: プログラム中に具体的に記述された値。
-
整数リテラル:
int
型として扱われる具体的な整数値。 -
小数点数リテラル:
double
型として扱われる具体的な小数点数値。 -
文字リテラル:
char
またはString
型として扱われる具体的な文字または文字列。 - マジックナンバー: 直接記述された数値リテラル。意味が不明瞭になりやすい。
int age = 30; // 整数リテラル
double height = 1.75; // 小数点数リテラル
char initial = 'A'; // 文字リテラル(char)
String greeting = "Hello"; // 文字リテラル(String)
エスケープシーケンス
-
エスケープシーケンス: 特殊な文字や記号を表現するために、文字の先頭にバックスラッシュ
\\
を付ける記述方法。 - エスケープ文字: エスケープシーケンスによって表現される特殊文字。
String newline = "これは改行を含む\n文字列です。";
String tab = "これは\tタブを含む文字列です。";
String quote = "これは\"ダブルクォート\"を含む文字列です。";
Rubyとの違い
基本的な用語なのでまとめてみました。Rubyと違う点がありますね。
構文と書式
-
Ruby: セミコロン不要。ブロック終了は
end
で明示。 -
Java: セミコロン必須。ブロックは
{}
で囲む。
型システム
- Ruby: 動的型付け。変数宣言時に型指定不要。
- Java: 静的型付け。変数宣言時に型指定必要。
演算子
さらっと基礎的な部分を見るとRubyと同じに見えますが、いくつかの部分でRubyにある演算子がなかったりするようです。深掘りする時間がないので割愛。
// Javaの算術演算子の例
int result = 3 * 4; // resultは12
// Javaの代入演算子の例
int x = 10;
x += 5; // xは15
// Javaの関係演算子の例
boolean isEqual = (5 == 5); // isEqualはtrue
// Javaの論理演算子の例
boolean result = (true && false); // resultはfalse
データの型変換
代入の型一致
代入する値の型と代入先の変数の型は一致している必要がある。型が異なる場合、コンパイルエラーが発生する。
int number = 5; // 正しい。int型の変数にint型の値を代入
double value = 3.14; // 正しい。double型の変数にdouble型の値を代入
int wrong = 3.14; // 誤り。int型の変数にdouble型の値を代入しようとしている
算術演算子の型一致
算術演算は同じ型の変数同士でのみ行うことができる。
int sum = 5 + 10; // 正しい。両方int型
double result = 5.5 + 3.2; // 正しい。両方double型
int wrong = 5 + 3.2; // 誤り。int型とdouble型の混合
自動型変換
代入処理で、代入元と代入先が整数型または小数点数型であり、代入先の型が代入元の型より大きい場合、自動的に型変換が行われる。
int smallNumber = 100; // int型
long bigNumber = smallNumber; // int型からlong型へ自動型変換
オーバーフロー
許容メモリサイズが小さい型に大きな型のデータを代入しようとすると、桁あふれ(オーバーフロー)が発生しエラーになる。
long bigNumber = 2147483648L; // long型
int smallNumber = bigNumber; // エラー。long型からint型への代入はオーバーフローを引き起こす
配列
同じ型の変数を複数まとめた集合体。各変数は要素と呼ばれる。
配列宣言
型名[] 配列名 = new 型名[要素数];
で配列を生成。
インデックス
配列の各要素に付与される番号。0から始まる。
要素の使用
配列名[インデックス] で特定の要素を参照または代入。
// char型の配列singouを作成し、3つの要素を持つように設定
char[] singou = new char[3];
// 配列の各要素に値を代入
singou[0] = '赤'; // 1番目の要素(インデックス0)に '赤' を代入
singou[1] = '青'; // 2番目の要素(インデックス1)に '青' を代入
singou[2] = '黄'; // 3番目の要素(インデックス2)に '黄' を代入
// 配列の要素を参照
char firstColor = singou[0]; // 1番目の要素を参照
char secondColor = singou[1]; // 2番目の要素を参照
char thirdColor = singou[2]; // 3番目の要素を参照
// 参照した要素の値を出力
System.out.println("1番目の信号: " + firstColor);
System.out.println("2番目の信号: " + secondColor);
System.out.println("3番目の信号: " + thirdColor);
プリミティブ型と参照型
変数には「プリミティブ型」と「参照型」の2種類がある。
- プリミティブ型は値そのものを直接保持する型
- 参照型は値のメモリ上の場所情報を保持する型
→プリミティブ型のように値を直接保持するのではなく、実際の値は別のメモリ領域で管理そのメモリ上における場所情報を保持
→配列は参照型に該当
int number = 10; // プリミティブ型
String text = "こんにちは"; // 参照型
多次元配列
2次元以上の配列を指し、配列の要素自体がさらに配列である構造を持つ。
[]を増やすことで多次元配列を宣言。
int[][] twoDimensional = new int[3][3]; // 2次元配列
int[][][] threeDimensional = new int[3][3][3]; // 3次元配列
コマンドライン引数
コマンドラインからプログラムに渡すデータを格納するための特殊なString型配列。
java MyProgram arg1 arg2 arg3
public class MyProgram {
public static void main(String[] args) {
// コマンドライン引数を取得する
// args[0]には "arg1"、args[1]には "arg2"、args[2]には "arg3" が格納されている
String arg1 = args[0];
String arg2 = args[1];
String arg3 = args[2];
// ここで引数を使った処理を行う
}
}
Stringと参照型の扱い
プリミティブ型
値が直接格納されている変数で、メモリ領域が固定されている。
参照型
値の格納場所を指し示す変数で、必要なメモリ領域が変動する。
String型
文字列を格納する参照型で、1文字につき2バイトのメモリが必要。
String example = "プログラミング"; // 7文字で14バイト必要
String anotherExample = "moco"; // 4文字で8バイト必要
疑似プリミティブ型
参照型でありながらプリミティブ型のように扱われる特殊な型。
String data = "データ"; // 簡易的な方法
String formalData = new String("データ"); // 正式な方法
String型の生成
通常は 型名 変数名
で生成されるが、正式な方法は new
キーワードを使う。
String型の内部処理
文字列をchar型の配列として内部管理し、参照時に配列の全要素を結合して返す。
参照型変数の比較
参照型変数間の比較では、変数の場所情報が比較される。
String型の比較
String型変数間の比較には equals
メソッドを使用する。
String example1 = "テスト";
String example2 = "テスト";
boolean isSame = example1.equals(example2); // 文字列内容の比較
参照型の代入
参照型変数の代入では場所情報がコピーされ、両方の変数が同じオブジェクトを参照する。
String original = "元の値";
String copy = "コピーされる値";
copy = original; // originalの場所情報がcopyに代入される
ガベージコレクション
Javaにおいて、参照されなくなったオブジェクトをメモリから自動的に削除する機能。
繰り返しとは
-
順次
ソースコード上の命令文を上から順番に実行する処理方法。 -
繰り返し
特定の条件下で命令文を繰り返し実行する制御構文。 -
条件分岐
条件に応じて異なる命令文を実行する制御構文。
構造化プログラミング
「順次」「繰り返し」「条件分岐」を組み合わせて、分かりやすく効率的なプログラムを作る手法。
for文
初期設定、実行条件、継続処理の3つの部分で構成される制御構文。
-
初期設定
for文の開始時に1度だけ実行される処理、通常はカウンタ変数の宣言と初期化。 -
実行条件
for文の繰り返しを継続するか判断する条件、この条件がtrueの間繰り返し処理が実行される。 -
継続処理
各繰り返し処理の後に実行される処理、通常はカウンタ変数の更新。
for (初期設定; 実行条件; 継続処理) {
// 繰り返し実行される処理
}
for (int i = 0; i < 10; i++) {
// このブロック内の処理が10回繰り返される
System.out.println(i); // 0から9までの数を出力
}
配列のインデックスとfor文
for文で配列のインデックスをカウンタ変数として使用し、配列の各要素に順番にアクセスする。
実行条件と配列の長さ
配列の長さ(length
プロパティ)を実行条件に使用して、配列の全要素を処理する。
String[] words = {"apple", "banana", "cherry"};
for (int i = 0; i < words.length; i++) {
System.out.println(words[i]); // 配列の各要素を出力
}
while文
特定の条件が満たされている間、繰り返し処理を行う制御構文。
実行条件のみを持ち、この条件がtrueの間、繰り返し処理が実行される。
while (実行条件) {
// 繰り返し実行される処理
}
int i = 0;
while (i < 5) {
System.out.println("カウンタ: " + i);
i++; // カウンタ変数を更新
}
for文とwhile文の使い分け
for文は繰り返し回数が決まっている場合に、while文は終了条件が不確定な場合に使用する。
do-while文
少なくとも1回は処理を実行し、その後条件をチェックして繰り返しを決定する制御構文。処理を先に実行し、その後で実行条件をチェックする。
do {
// 最初に実行される処理
} while (実行条件);
int i = 0;
do {
System.out.println("カウンタ: " + i);
i++;
} while (i < 5);
while文とdo-while文の使い分け
while文は繰り返しの前に条件をチェックし(前判定)、do-while文は繰り返しの後に条件をチェックする(後判定)。
条件分岐
if文
条件がtrueの場合に特定の処理を実行する条件分岐構文。
条件分岐に関してはRubyとJavaで大きな違いはないですね。
if (条件①) {
// 条件①がtrueのときに実行する処理
} else if (条件②) {
// 条件①がfalseかつ条件②がtrueのときに実行する処理
} else {
// 条件①も条件②もfalseのときに実行する処理
}
int score = 75;
// else-if文の例
if (score >= 90) {
System.out.println("成績は優秀です");
} else if (score >= 60) {
System.out.println("成績は普通です");
} else {
System.out.println("成績が不十分です");
}
その他の制御構文
break文
ループやswitch文から抜け出すために使用される制御文。
最初はrubyでいうところのreturnかな?と思ったけど、returnはメソッドからの脱出で役割が異なるとのこと。なお、rubyにもbreakはあるとのこと。
break;
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // iが5になった時点でループから抜ける
}
System.out.println(i);
}
continue文
現在のループの残りの処理をスキップし、次の繰り返し処理へ移る制御文。
continue;
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // iが偶数の場合、以降の処理をスキップして次の繰り返しに移る
}
System.out.println(i);
}
ラベル
ラベルはループやswitch文の前に識別子として配置し、break
やcontinue
文と組み合わせて特定のループを制御します。
ラベル名: // ラベルの設定
for (初期化; 実行条件; 継続処理) {
// ループの中身
if (特定の条件) {
break ラベル名; // 指定されたラベルのループから脱出
}
}
outerLoop: // ラベル名「outerLoop」を設定
for (int i = 0; i < 3; i++) {
innerLoop: // ラベル名「innerLoop」を設定
for (int j = 0; j < 3; j++) {
if (i * j > 4) {
System.out.println("i * jが4を超えたので、外側のループを中断");
break outerLoop; // 「outerLoop」ラベルのループから脱出
}
System.out.println(i + " * " + j + " = " + (i * j));
}
}
switch文
複数の条件に基づいて異なる処理を実行する多分岐制御構文。
switch (変数) {
case 値1:
// 値1に合致する場合の処理
break;
case 値2:
// 値2に合致する場合の処理
break;
// 他のcase節
default:
// どのcaseにも合致しない場合の処理
}
String day = "月曜日";
switch (day) {
case "月曜日":
System.out.println("週の始まり");
break;
case "火曜日":
System.out.println("もう1日経過");
break;
default:
System.out.println("週の中盤以降");
}
クラスとメソッド
クラス
プログラムの設計図で、具体的な処理内容を定義する領域。
クラスブロック
クラスの定義を囲むコードブロック。
クラス名の命名規則
- 先頭を大文字、それ以外は小文字
- 言葉の区切りを大文字
class クラス名 {
// クラスの内容
}
class MyFirstClass {
// MyFirstClassの内容
}
メソッド
プログラムの特定の機能を定義するコードブロック。関数とも呼ばれる。
メソッドブロック
メソッドの動作を記述するコードの領域。
メソッド名の命名規則
- 先頭を小文字
- 言葉の区切りを大文字で表す
- 予約語を避け、英単語や略語を使用
[修飾子] 戻り値の型 メソッド名(引数の型 引数名, ...) {
// 命令群
}
-
修飾子
メソッドに関する追加情報を提供。
→メソッドのアクセス範囲(public、privateなど)や特性(static、finalなど)を指定。 -
戻り値の型
メソッドが処理結果として返すデータの型を定義。
→何も返さない場合はvoidを使用。
public class MyClass {
// メソッドの例
public static void printMessage(String message) {
System.out.println(message);
}
}
mainメソッド
Javaプログラムの実行時に最初に呼び出される特別なメソッド。
→プログラムの起動点であり、ここから処理が始まり終わる。戻り値の型はvoid
。
public static void main(String[] args) {
// プログラムの処理
}
public class ExampleClass {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
オーバーロード
同一のクラス内で、引数の型や数が異なる同名のメソッドを複数定義する機能。
オーバーロードの特徴
メソッド名が同じでも、引数の型や数が異なれば別のメソッドとして扱われる。
public class ExampleClass {
// 引数なしのメソッド
public void display() {
System.out.println("引数なしのdisplayメソッド");
}
// 引数が1つのメソッド
public void display(String message) {
System.out.println("引数ありのdisplayメソッド: " + message);
}
// 引数が2つのメソッド
public void display(String message, int number) {
System.out.println("引数2つのdisplayメソッド: " + message + ", 数値: " + number);
}
}
スコープ
変数が有効である範囲。通常は変数が宣言されたブロック({ }で囲まれた範囲)内。
ローカル変数
メソッド内で宣言された変数で、そのメソッド内のみで有効。
スコープの規則
- スコープ内でのみ変数は有効で、外部からアクセス不可。
- ブロック内でのみ有効で、ブロック終了時に消去される。
- 内側のブロックから外側のブロックで宣言された変数へのアクセス可。
- 同じスコープ内で同名の変数宣言不可。
public class ExampleClass {
public void exampleMethod() {
// メソッドレベルのスコープの開始
int number = 10; // この変数はexampleMethod内でのみ有効
if (number > 5) {
// ifブロックのスコープの開始
String message = "Numberは5より大きい"; // この変数はifブロック内でのみ有効
System.out.println(message); // この行は問題なく動作します
// ifブロックのスコープの終了
}
// System.out.println(message); // これはエラーになる。message変数はifブロックの外では使えない
for (int i = 0; i < number; i++) {
// forループのスコープの開始
System.out.println("ループ中のiの値: " + i); // iはforループ内でのみ有効
// forループのスコープの終了
}
// System.out.println(i); // これはエラーになる。i変数はforループの外では使えない
}
// メソッドレベルのスコープの終了
}
API
API (Application Program Interface)
Javaが提供する便利なソースコード群。複雑な機能を実装するために利用可能。
APIの特徴
- 高度なプログラムを自分で書かずに利用できる。
-
println
、parseInt
などのメソッドもAPIの一部。 - Javaの公式ドキュメントでクラスやメソッドの詳細が確認できる。
APIの格納場所
→JRE(Java実行環境)に含まれる。
インポート
Javaのクラスやインターフェースを使用するために、特定のパッケージから取り込む操作。
パッケージ
JavaのAPIやクラスが用途別に分類されているフォルダのような構造。
すべてのAPIはjavaという名前のパッケージの中に入っており、
その下で用途別のパッケージに分けられて管理されている。
パッケージの特徴
- Javaプログラムは必ず何らかのパッケージに属する。
- ソースコードの先頭でパッケージを宣言し、所属するパッケージを定義する。
- 1つのクラスは1つのパッケージにのみ属する。
- パッケージ名はCLASSPATHからの相対パスで指定される。
インポートの方法
使用したいクラスをパッケージから取り込むためには、クラスの前にimport
キーワードを使用する。
java.langパッケージ
利用頻度が高いため、インポートせずに使用できる特別なパッケージ。
import パッケージ名.クラス名;
package com.example.myapp; // パッケージ宣言
import java.util.ArrayList; // ArrayListクラスをインポート
public class ExampleClass {
public void exampleMethod() {
ArrayList<String> list = new ArrayList<>(); // ArrayListの使用
}
}
上記の例では、ExampleClassがcom.example.myappというパッケージに属しています。
APIの中でもjava.langパッケージのクラス(printlnメソッドなど)は利用頻度が高いため、インポート不要で利用できます。
ArrayList
Javaで提供される、要素数が動的に変更可能な配列のようなコレクション。
java.util
パッケージに属するため、使用にはインポートが必要。
無名パッケージ
カレントディレクトリに自動的に適用されるパッケージ。package宣言は不要。
無名パッケージの特徴
- 実行ファイルが存在するフォルダが無名パッケージとして扱われる。
- このパッケージに属するクラスにはpackage宣言が不要。
パッケージとインポート
- 同じパッケージ内のクラスはインポートせずに利用可能。
- 異なるパッケージのクラスを使用する場合、インポートが必要(
java.lang
パッケージを除く)。
おわり
JavaはRubyと比較すると記述量が多いですね。
基礎部分をざっとまとめるだけでも結構大変でした。
比較ももう少し書きたかったですがそれにはJavaとRuby両方に十分な理解が必要ですね。
Javaで初めて見た概念のものが、実はRubyでも同じ記述だったなんてことも多くてRubyもまだまだ知らないことが多いなと思わされました。