独り言

プログラミングの講師をしています。新人研修で扱う技術の解説と個人の技術メモ、技術書の紹介など

【Java】Java入門(導入〜メソッド編)

目次


導入

Javaとは

Javaは、プログラミング言語の一つです。
1996年に(旧)サンマイクロシステムズ(現オラクル)によって開発された言語です。
誕生してから20年以上経過していますが、現在(2019年時点)でも多くのシステムで利用されています。

Javaの用途

プログラミング言語は様々な種類がありますが、言語によって用途がある程度決まっています。
Javaは比較的幅広い用途で使用されています。
Javaを利用することで

  • コンソールアプリケーション(コマンドプロンプトなどから起動するアプリケーション)
  • デスクトップアプリケーション(ダブルクリックで起動するアプリケーション)
  • Webアプリケーション
  • Androidアプリケーション

などの作成が可能です。
また、電化製品などの組み込みプログラムとしても利用されていました。

Javaの利用場面で最も多いのはおそらくWebアプリケーションです。
その中でも特に、規模の大きなWebアプリケーションの作成で利用される場面が多いです。
例えば、金融系でのWebアプリケーションなどで多く利用されます。

規模の小さなWebアプリケーションの場合、PHPRubyなどの言語が使用される場合が多く、Javaはあまり使用されません。

昔は、Javaアプレットという機能で、ブラウザ上で動作するプログラムの作成されていました。
しかし、Flashなどの登場でJavaがブラウザ上で動くプログラムとして利用される場面はなくなりました。
(現在はJavaScriptなどの進化に伴ってFlashもあまり使われなくなりました。)

特徴

以下、Javaの特徴について説明します。

オブジェクト指向型言語

Javaオブジェクト指向型の言語です。
オブジェクト指向が何か、についてはオブジェクト指向の部分で説明します。
現在、オブジェクト指向の機能が備わっている言語は数多くありますが、
その中でもJavaオブジェクト指向で開発することを前提とした言語となっており、
オブジェクト指向を学ぶにはJavaが最も適した言語だと言えるでしょう。

プラットフォームに依存しない

ここでいうプラットフォームとはコンピュータOSのことだと思ってください。
OSとは、PCの場合はWindowsMacスマホの場合はAndroidiOS、といったものです。
C言語など、Javaが登場するよりも前に使用されていたプログラミング言語では、プログラムを作成しても、OSが変わると動きませんでした。
WindowsならWindows用、MacならMac用としてプログラムを作成(ビルド)し直す必要がありました。
しかし、Javaは中間コード方式という仕組みを使うことで、 OSが変わってもプログラムを作り直すことなく動作させることを実現しました。

現在は同じような仕組みを持った言語は珍しくありませんが、Javaが登場したばかりのころは大きな特徴の一つでした。
当時はWrite once, run anywhere(一度(プログラムを)書けば、どこでも実行できる)というスローガンとしても有名になりました。

難易度

Javaは、プログラミング初心者が最初に学ぶ言語としては決して難易度は低くありません。
初学者が独学で学ぶにはハードルが高い言語と言えるでしょう。 しかし、Javaオブジェクト指向前提の言語であることから、オブジェクト指向を学ぶにはJavaが最も適した言語だといえます。
また、難易度が低くないからこそ、Javaを理解しておくことで、 その後、他のプログラミング言語を学ぶときには理解しやすくなります。

Javaを学ぶメリット

Javaをちゃんと理解することができれば、オブジェクト指向を理解したと言えるかと思います。
話が重複しますが、オブジェクト指向が理解できれば、後々他の言語を学ぶときに非常に有利です。
また、今後どうなるかは分かりませんが、今現状(2019年)はJavaは最も多く利用されている言語です。
なのでJavaを習得しておくことは、プログラマー(システムエンジニア)として働きたい場合には有利になります。

導入まとめ


JDKJREJVM

Javaのプログラミングの始める前に、JDK, JRE, JVMについて知っておきましょう。
これらはJavaのプログラムを動かしたり、Javaでプログラムを開発する際に必要になるツールです。
いきなり知らない用語が多く出てくるのではじめは混乱するかもしれませんが、Javaを理解する上で欠かせない用語ですので、きっちり理解しておきましょう。

JVM

JVMは、Java Virtual Machine の略で、日本では「Java仮想マシン」と呼ばれます。
Javaソースコード(Javaの文法に沿って書かれた人間が読めるもの)をコンパイル(変換)すると、クラスファイルと呼ばれるファイルが生成されます。
クラスファイルは中間コードとも呼ばれます。
クラスファイルは機械語(0と1のビット列)にはなっていないので、OS上でそのまま実行することはできません。
このクラスファイルを機械語に変換しながら実行してくれるのがJVMです。
つまり、Javaのプログラムを動かすためにはJVMを用意する必要があります。
このような仕組みは少々面倒に感じるかもしれませんが、Javaの特徴の部分で説明した、「プラットフォームに依存しない」という特徴は、JVMがあるからこそ実現できる仕組みでもあります。

JRE

JREJava Runtime Environment の略です。簡単に言うとJavaの実行環境のことです。
Javaのプログラムを動かすにはJVMが必要だという説明をしましたが、 JVMは単体でダウンロードしてくるものではありません。 通常Javaで作られたプログラムを動かしたい場合、JREをダウンロードしてインストールします。
JREJVMを含む、Javaを実行するのに必要なソフトの集まりだと考えてください。

JDK

JDKJava Development Kit の略です。
一言でいえば、Javaを開発するのに必要なツールの集まりです。
Javaでプログラム開発を行うためには、当然Javaのプログラムを動かせなければいけません。
なので、JDKにはJREも含まれています。
JREに、コンパイラ(コンパイルするためのツール)や標準ライブラリ(Javaの開発で利用できるプログラム群)といった開発するのに必要なツールが加わったものだと考えてください。

まとめると、

  • JVMJavaのプログラムを実行するソフトウェア
  • Javaのプログラムを動かしたければJREが必要
  • Javaでの開発が行いたければJDKが必要

ということです。
包含関係で表すと、
JDK > JRE > JVM
となります。
JREJDKはネット上からダウンロードすることが可能です。

プログラミングの分類からみたJavaの立ち位置

プログラミング言語には様々な種類があり、分類方法にも様々な分類方法があります。
分類方法の中でも、コンパイラ式かインタプリタ式かという分類があります。
これは、プログラミング言語で書かれたソースコードをどのタイミングで変換するか、による分類方法です。

コンパイラ式の言語の場合、ソースコード書いた後にコンパイラというプログラムでコンパイルします。
そうすることで機械語に変換されます。
つまり、プログラムを実行する前にソースコード機械語に変換するのがコンパイラ式の言語です。

一方、インタプリタ式の場合、実行する前に事前に変換する必要はありません。
実行時に機械語に変換しながら実行します。

コンパイラ式の言語の場合、実行前にあらかじめ機械語に変換されているため、実行するときはそのまま実行できます。
そのため、インタプリタ式の言語と比べると、実行速度は速い傾向にあります。
しかし、コンパイラ式の場合、作成したプログラムを異なるプラットフォーム(OS)にコピーした場合、正常に動作しません。
(例えば、Windowsで作成したプログラムをMac OSにコピーした場合など)
一方でインタプリタ式の場合、実行時に機械語に変換されるため、ソースコードをそのままコピーすれば異なる環境でも動作します。
つまり、それぞれにメリットとデメリットがあります。

Javaは、コンパイラ式とインタプリタ式両方の性質を持っています。
事前にコンパイルは必要なものの、コンパイル時には機械語にはならず、中間コード(classファイル)になります。
そして実行時にJVMが中間コードを機械語に変換しながら実行してくれます。
事前にコンパイルすることで実行速度を保ちつつ、JVMという存在のおかげで環境を選ばずに動作してくれます。


環境構築

プログラミングを行う際、まず最初に行うべきことは環境構築です。
Javaでは環境構築の方法としていくつかの方法がありますが、ここではその一つを紹介します。
これ以降の内容はここでの環境を前提としますが、どの環境でも構いません。

ちなみに、プログラミングを学習する上で最初のハードルとなるのが、開発環境の構築です。
開発環境を構築する段階で挫折してしまう人も少なくありません。
最近ではpragateなど、開発環境を作成しなくてもネット上から簡単に学習できる環境も多くなりました。
しかしながら、自分で作りたいものを作る場合にはやはり自分で環境を作る必要があります。
また、プログラマーとして働くのであれば、環境構築は避けては通れない作業です。
様々な環境を作り、環境構築に慣れておきましょう。
一つのサイトを参考にしてうまくいかなかった場合は、他のサイトや書籍などを参考にして色々試行錯誤してみましょう。

テキストエディタの用意

Javaによる開発を行う場合、JDKさえあれば開発をはじめることは可能です。
しかし、開発の効率を上げるためにはその他、便利なツールを使っていく必要があります。
まず必要になるのはテキストエディタです。
ソースコード作成するためにはテキストエディタが優秀でなければ開発の効率は上がりません。
テキストエディタは沢山の種類がありますが、ここでは
Visual Studio Code
というエディタを使用します。
(以下、「VS Code」と呼びます)

以下のサイトから自分のOSに合ったインストーラをダウンロードし、
インストールを行ってください。

https://code.visualstudio.com/download

以下、VS CodeJavaを動作させるまでの手順を簡単に説明していきますが、VS Codeでの環境構築の情報は他のサイトでも色々とアップされています。
このサイトの説明で分からない場合は他のサイトも参考にしてみてください。

拡張機能のインストール

VS Code が起動したら、拡張機能をインストールします。
VS Code拡張機能をインストールしていくことで、便利な機能をどんどん追加していくことが可能です。
VS Codeはインストールした時点では表記が英語になっているため、日本語にしたい場合は「Japanese Language Pack for Visual Studio Code」をインストールします。

次に、Javaの開発が行えるように「Java Extension Pack」をインストールします。

JDKのダウンロードとインストール

Javaによる開発を行うには、JDKが必要です。
JDKには、Oracle社が提供している「OracleJDK」と、Oracle以外の企業や団体が提供している「OpenJDK」の2種類があります。
サポートを受けられるかどうかなどの違いはありますが、学習目的でJavaを利用する場合には差はありませんので、
どちらを使用しても構いません。

VS Codeに「Java Extension Pack」をインストールした時点でJDKがインストールされていない場合、
VS Code経由でRed Hat社のOpenJDKのダウンロードが可能です。

Oracleのサイトからダウンロードする場合は以下のURLからダウンロードします。

https://www.oracle.com/technetwork/java/javase/downloads/index.html

ダウンロードする際、Oracleのアカウントが必要になるため、アカウントがない場合はアカウントを作成します。

JDKはどれを使用しても構いませんが、
ここでは
バージョンは、Java SE 8系を利用します。

ダウンロードが完了したら、インストーラを実行して、適宜インストールを行ってください。

環境変数の設定

JDKのインストールが終わったら、環境変数JAVA_HOMEを設定します。
この設定により、Javaコンパイルや実行の操作が楽に行えるようになります。

コントロールパネル ⇒ システムをセキュリティ ⇒ システム
⇒ システムの詳細設定 ⇒ 環境変数 ⇒ システム環境変数の「新規」ボタン押下
⇒ 変数名:JAVA_HOME、変数値:JDKのインストール場所(例:C:\Program Files\Java\jdk1.8.0_211)
⇒ OKを押してウィンドウを閉じる

動作確認

以上でJavaを動かす環境は整いました。
ここから動作検証していきます。

まずはVisual Studio Codeでコマンドパレットを開きます。
(Ctrl + shift + p または 表示タブ⇒コマンドパレット)
Java: Create Java Project」を選択します。
エクスプローラが起動するので、Javaのプロジェクト(ソースコードなどを保存する場所)を作成したいフォルダを選択します。
プロジェクト名の入力を求められるので、適当なプロジェクト名を入力します。
プロジェクト名は任意です。
JavaLerningなど、適当な名前で作成します。

プロジェクトの作成が完了すると、プロジェクト内の「src\app」フォルダの中に、「App.java」というファイルがあります。

package app;

public class App {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello Java");
    }
}

このファイルをの中身は上記のようになっているはずです。
ここで、3行目と4行目の間に
「Run|Debug」
という文字が出てくるかと思います。
ここで「Run」を押下してください。

デバッグコンソール」と呼ばれる画面下部に
「Hello Java」という文字が出力されていればOKです。

IDEによる開発

ここではVS Codeソースコードを作成する前提で進めていきますが、IDE(統合開発環境)を導入することによる開発も可能です。
IDEとは、特定のプログラミング言語に対して、実行環境、テキストエディタコンパイラ、デバッガ、など、開発を行う上で便利なツール群がまとめられた開発ツールのことです。
言語ごとに色々なIDEがありますが、Javaの場合、「eclipse」や「NetBeans」などが有名です。
ここでは詳しく紹介しませんが、興味があれば他のサイトを参考にして導入してみても良いでしょう。
ある程度開発の規模が大きくなると、IDEによる開発が便利です。


Java入門

最もシンプルなプログラム

まずは「src\app」フォルダの中にMain.javaという名前で以下のプログラムを作成して実行してみましょう。

Main.java

package app;

public class Main{
    public static void main(String[] args) {
        // コンソールに「Hello World」と出力する
        System.out.println("Hello World");
    }
}

結果

Hello World

解説

パッケージ宣言

Javaでは、プログラムの数が多くなった場合でも、役割毎にプログラムを管理しやすくするためにパッケージと呼ばれる機能を使ってプログラムを分けて管理することができます。
PC上でフォルダを分けてファイルを管理するのと同じです。

package app;

先頭に上記のように書くと、「app」というパッケージに入ります。
しばらくは、作成するプログラムはこの「app」というパッケージに作成していきます。
パッケージについての細かな説明は後で行います。

クラス宣言

オブジェクト指向プログラミング言語では「クラス」と呼ばれるものを使用します。
Javaオブジェクト指向を前提としたプログラミング言語です。
そのため、javaは「クラス」という単位でプログラムを作成します。
クラス名はそれが何を表しているかが分かる名前を付けます。
クラス名はアルファベット大文字から始まるのが習慣となっています。
クラスとはいったい何なのか、については、オブジェクト指向の解説で詳しく解説します。

構文

public class クラス名 {

}

public class Main{
    // ...
}

メソッドの宣言

クラスの中には複数のメソッドを書くことができます。
メソッドの中には処理を書くことができます。
(逆に、処理はメソッドにしか書くことができません)
メソッドの詳細についてはメソッドの単元で解説するのでここでは割愛。
今は、クラスの中に以下のメソッドを定義し、その中に書けば実行できるということを知っておきましょう。
Javaのプログラムを実行したときには、mainという名前のメソッドが実行される決まりになっています。
しばらくはこのmainメソッドの中に処理を書いて、Javaの基礎を学んでいきます。

public static void main(String[] args) {
    // ここに処理を書く
}

コンソールへの出力

コンソールに対して何かを出力したい場合は、以下のように書きます。

構文

System.out.println(出力したい値);

正確に表現すると、
「System」というクラスの「out」というフィールドの「println」メソッドを呼び出しています。
今の時点では、そういうものか、程度の認識で問題ありません。
ちなみに、「println」を「print」にすると、出力後に改行が行われません。

Main.java

package app;

public class Main {
    public static void main(String[] args) {
        // 数値を出力する場合は直接数値を書く
        System.out.println(10);    
        // 文字列を出力する場合は"(ダブルクォート)でくくる
        System.out.println("abc");

        // lnを書かないと、改行をしない出力になる
        System.out.print(10);
        System.out.print("abc");
    }
}

結果

10
abc
10abc

ブロック

クラス宣言の後や、メソッド宣言の後に中かっこ({})が書かれています。
この中かっこに囲われた部分のことを「ブロック」と呼びます。
クラスやメソッドの開始と終了がどこなのかが、中かっこによって分かるようになっています。

Main.java

package app;

public class Main { // クラスの開始
    public static void main(String[] args) { // メソッドの開始
        
    } // メソッドの終了
} // クラスの終了

メソッドのブロックの中に書いている一つ一つの処理を文と呼びます。
Javaでは文の最後はセミコロン(;)を付ける決まりになっています。

Main.java

package app;

public class Main {
    public static void main(String[] args) {
        // 文 // 最後に必ずセミコロンを付ける
        System.out.println(10);    
    }
}

コメント

プログラムに影響を与えないものです。
プログラムの説明文などを書く場合に使用します。
Javaではコメントは3種類ある。

  • 1行コメント
  • 複数行コメント
  • javadocコメント

の3種類です。

// 1行コメント

/* 
    複数行コメント
*/

/** 
 *  javadocコメント
 *  javadocと呼ばれるリファレンスを出力するためのコメント
 *  
 */

コメントの使い方

コメントは処理に影響を与えないため、何でも自由に書くことができます。 初心者の段階では、自分が作成したソースコードに処理の説明を詳しく書くことで、プログラミングの理解が深まることがあります。
なので、初心者でプログラミング学習中のうちは積極的にコメントを書いていきましょう。

仕事でプログラミングをするときは、何の処理をしているのかをいちいちコメントに書くことはしません。
何の処理をしているのかは、ソースコードの中身をみれば分かるため、
「なぜそのような処理を書いたのか」など、ソースコードからは読み取れない情報をコメントに残すようにします。

また、ソースコードの一部をコメントにして処理を向こうにすることを「コメントアウト」と言います。 不要なコードだけど後で元に戻す可能性がある、テストのために一時的に処理を無効にしたい、などの場合、 ソースコードは消さずにコメントアウトしてテキスト上には残す場合があります。
開発時にはコメントアウトを有効に活用しましょう。

VS Codeの場合、コメントアウトしたい行を選択して、「Ctrl + /」でコメントアウトすることが可能です。

javadocコメント

javadocコメントは、ソースコードからJavaAPIリファレンスを作成するためのコメントです。

APIリファレンスとは
https://docs.oracle.com/javase/jp/8/docs/api/
のサイトのような、Javaのプログラムの説明が書かれた文書のことです。

ソースコードjavadocのコメントを書いておくことで、
javadocコマンドにより、APIリファレンス用のHTMLファイルを出力することができるようになります。

コンパイルエラー

Javaでプログラムを作成した場合、実行する前に一度コンパイルする必要があります。
コンパイルする際、ソースコードに文法上の間違いあると、エラーが出てコンパイルに失敗します。
この時のエラーのことをコンパイルエラーと呼びます。
例えば以下のようなものがあります。

package app;

public class Main {
    public static void main(String[] args) {
        // 文の最後にセミコロンがない場合はコンパイルエラー
        System.out.println(10)  
    }
}
package app;

public class Main {
    public static void main(String[] args) {
        System.out.println(10);
    }
 // 最後の閉じ中かっこがないのでコンパイルエラー

他にも様々な原因でコンパイルエラーは発生します。
自分でたくさんのプログラムを作成していく中で、
どんな場合にエラーになるかを理解しながら覚えつつ慣れていきましょう。
エラーになってもプログラムが壊れたり何かのファイルが壊れたりすることはありません。
エラーをよく読んで冷静に対処しましょう。

VS Codeの場合、Java拡張機能が入っていれば、コンパイルエラーは分かりやすく表示してくれます。
エラー個所のコードに波線が表示されます。

インデント

インデントとは、日本語では字下げと言います。
以下のプログラムを見てください。

Main.java

package app;
public class Main {
public static void main(String[] args) {
// コンソールに「Hello World」と出力する
System.out.println("Hello World");
}
}

結果

Hello World

最初に実行したプログラムと結果は全く同じなのですが、
全ての行が左端から始まっているため、ソースコードがなんとなく読みにくくなっています。
Javaでプログラムを書く場合は、ブロックの中に入った段階で
スペース4つ分(またはタブ2つ分)のスペースを空けることでソースを読みやすくするのが一般的です。
このようにブロックの中かくときに書くスペースのことをインデントといいます。

VS Codeを利用ソースコードを書いている場合、改行した際に自動で適切なインデントがされるようになっています。
仮にインデントが崩れてしまった場合、Shift + Alt + F のショートカットキーにより自動でソースコードの整形が行われます。

Java入門まとめ

  • Javaプログラムは拡張子「.java」のファイルに書く。
  • ファイル名はクラス名と一致する必要がある。
  • Javaではクラスという単位でプログラムを作成する。
  • 処理はメソッドの中に書く。
  • Javaのプログラムを実行したときはmainメソッドが呼ばれる。
  • コンソールへ出力を行いたい時は「System.out.println」の中に出力したいものを書く。
  • Javaはコメントが3種類ある。
  • ソースコードの文法が間違えているとコンパイルエラーが発生する
  • ソースコードを読みやすくするためにインデントを書く

変数

変数とはデータを保存しておく入れ物のことです。
値にラベルをつけておくものと説明されることもあります。
どちらの認識でも構いません。
自分がイメージしやすい方で理解してください。

構文

型 変数名;  // 宣言
変数 = 値;  // 代入
型 変数 = 値; // 初期化

型とは、データの種類を表すものです。
Java以外のプログラミング言語では、変数を使用する際に型を指定する必要のないものもありますが、Javaの場合、変数を利用する際は必ず型を指定してあげる必要があります。
Javaは型に厳格なプログラミング言語であるという認識を持っておきましょう。

Main.java

package app;

public class Main {
    public static void main(String[] args) {
        // この場合はintが型でnが変数名
        int n;  // 宣言
        n = 10; // 代入
        // 変数を出力
        System.out.println(n);

        // 初期化
        int m = 10;
        System.out.println(m);

        // 再代入
        n = 20;
        System.out.println(n);

        // 以下はコンパイルエラー
        // 同じ名前の変数を2度宣言することはできない
        // int n;

        // 以下はコンパイルエラー
        // 値を代入せずに使用することはできない
        int num;
        // System.out.println(num); 
    }
}

結果

10
10
20

変数名の付け方

変数を付ける時は変数名を決める必要があります。
変数名の付け方については、明確な答えがあるわけでありませんが、
何を表すかが分かる名前を付けることが求められます。

それとは別で、守らなければいけないルールと、
Javaの世界で慣習となっている名前の付け方があります。

ルール

  • 変数名に使えるのは数字とアルファベット。記号は_(アンダースコア)と$のみ
  • 数字で始めてはいけない
  • 予約語は使用できない。予約語とは、Javaのキーワードとして使われてるもの

慣習

  • 変数名は小文字のアルファベットから始まる
  • 単語が複数続く場合は、単語の先頭が大文字になる

Main.java

int abc_123 = 10;  // OK
// int abc! = 10;     // NG !は使えない

int a1 = 20;  // OK
// int 1a = 20;  // NG 数字で始めることはできない

// int class = 30;  // NG classは予約語なので使えない
int Class = 30;  // OK Javaは大文字と小文字は区別されるため、先頭が大文字なら使用可能。ただし推奨はされない。

String userName = "alice";   // 推奨
String user_name = "alice";  // 推奨されない
String UserName = "alice";   // 推奨されない

名前を付ける時に、単語の区切りで先頭が大文字になる書き方をキャメルケースといいます。
単語の区切りをアンダースコアで表す書き方をスネークケースといいます。
キャメルケースの中でも、一番先頭の文字が小文字になる書き方をローワーキャメルケース
先頭が大文字になる書き方をアッパーキャメルケース、といいます。
プログラミング言語の種類によって、どの書き方が推奨されるかは変わってきます。
Javaの場合、変数名とメソッド名はローワーキャメルケース。
クラス名はアッパーキャメルケース、が推奨されています。

ちなみに、キャメルとは日本語ではラクダのことです。
キャメルケースとは、単語の先頭が大文字になることがラクダのコブに似ていることからそういった名前が付けられたようです。
スネークケースは、アンダースコアで区切る表現がヘビのように見えるのでそのような名前がついたようです。

型の種類

型の種類は大きく分けて2種類あります。
基本型(プリミティブ型とも呼ばれます。どちらで呼んでもかまいません。)と参照型の2種類です。

基本型は全部で8種類あります。
8種類ある基本型は大きく4つに分けることができ、整数、小数、文字、真偽値の4つです。
整数には4つ、小数は2つの種類があり、トータルで8種類です。

  • 基本型
    • 整数型
      • byte:8bit(-127 ~ 128)
      • short:16bit(-32768 ~ 32767)
      • int:32bit(-2147483648 ~ 2147483647)
      • long:64bit(-9223372036854775808 ~ 9223372036854775807)
    • 小数(浮動小数点)
      • float:32bit
      • double:64bit
    • 文字
      • char
    • 真偽値
      • boolean(true, false)

変数を使用する場合、コンピュータのメモリ上に変数用の領域が確保されます。
型の隣にあるxxbitというのは、その型の変数を使用する際に確保される領域のサイズです。
整数の型の括弧の中の数値は、扱える値の範囲です。

8種類の中で使用頻度が高いものはintとdoubleの2つです。

整数の扱う場合、ほとんどの場合int型を使用します。
扱う数値の値がint型の範囲に納まらない場合、longを使用します。
それぞれの型の数値の範囲は覚えておく必要はありませんが、
byte, short, int, long の順で扱える値の範囲が大きくなることは覚えておきましょう。

小数の場合、ほとんどdouble型を使用します。
ただし、基本型で小数を扱う場合、丸め誤差が出る可能性があるので注意が必要です。
お金の計算など、誤差が許されない数値の計算については、基本型は使用せず、BigDecimalなどの参照型を使用して計算を行います。

基本型に該当しない型は全て参照型となります。
例えば、文字列を表すString型は参照型です。
参照型は無数にあり、自分で作成することも可能です。
(クラスが型になる。自分でクラスを作成すればそれが型になる)

参照型の場合、型の名前が大文字から始まります。(Stringなど)
基本型の場合は小文字から始まり、参照型の場合は大文字から始まると理解しておいても良いでしょう。

Main.java

package app;

public class Main {
    public static void main (String[] args) {

        // 整数
        byte b = 10;   // 8bit
        short s = 2000;  // 16bit
        int n = 300000;    // 32bit // 整数のデフォルトの型
        long l = 5000000000L;  // 64bit // longにするにはLを付ける
        // long l = 5l;  // 小文字のlでも良い
        System.out.println(b);
        System.out.println(s);
        System.out.println(n);
        System.out.println(l);

        // 小数
        float f = 1.3F;  // float型にするにはFを付ける
        // float f = 1.3f;  // 小文字のfでも良い
        double d = 2.5;  // 小数のデフォルトの型
        System.out.println(f);
        System.out.println(d);

        // 文字
        // シングルクォートでくくる。
        // 1文字のみ。空文字や2文字以上を代入することはできない。
        // Javaでは「文字」と「文字列」は明確に異なるのでしっかりと区別する。
        char c1 = 'a';
        System.out.println(c1);
        // 内部的には数値(文字コード:Unicode)で管理されているため
        // 整数を入れることもできる
        char c2 = 97;
        System.out.println(c2); // 文字コード表で97に該当する「a」が出力される

        // 真理値(boolean)
        // 取りうる値はtrueとfalseの2種類のみ
        boolean b1 = false; // 偽を表す値
        boolean b2 = true;  // 真を表す値
        System.out.println(b1);
        System.out.println(b2);

        // 参照型
        // 基本型以外全て
        // 以下はString(文字列)の例
        String str = "abc";
        System.out.println(str);
    }
}

結果

10
2000
300000
5000000000
1.3
2.5
a
a
false
true
abc

リテラル

ソースコードの中に直接書かれた値のことをリテラルと呼びます。
ソースコードに書かれた「100」「10.0」「true」「"abc"」などがリテラルです。
リテラルで書かれた整数はデフォルトではint型になります。
リテラルで書かれた整数はデフォルトではfloat型になります。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 10は整数リテラル
        System.out.println(100);

        // 整数のリテラルはアンダースコアを挟むことも可能
        int num = 1000_000;
        // 表示は1000000となる
        System.out.println(num); 

        // 以下はコンパイルエラー
        // intの範囲を超えたためエラー
        // System.out.println(10_000_000_000); 

        // 以下はコンパイルエラーにならない
        // intの範囲を超える場合はLを付けてlong型にする
        // System.out.println(10_000_000_000L); 


        // abcは文字列リテラル
        System.out.println("abc");
    }
}

結果

100
1000000
abc

進数

int型の値を扱う場合、2進数、8進数、16進数で表現することも可能です。
2進数の場合、先頭に0bを付けます。
8進数の場合、先頭に0を付けます。
16進数の場合、先頭に0xを付けます。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int num2 = 0b00001111;  // 2進数
        int num8 = 010; // 8進数
        int num16 = 0x10; // 16進数

        System.out.println(num2);
        System.out.println(num8);
        System.out.println(num16);
    }
}

結果

15
8
16

型変換

条件にもよりますが、異なる型に変換することが可能です。
互換性のある型同士であれば、「小さい型」⇒「大きい型」の変換は可能です。
「大きい方」⇒「小さい型」は、後に説明するキャスト演算子を使用すれば可能です。
互換性のない型同士は基本的に変換できなません。
ただしメソッドにより変換可能な型もあります。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 整数型
        byte b = 100; 
        short s = b; // OK
        int n = s;   // OK
        long l = n;  // OK
        // byte b2 = s;  // NG 後述のキャスト演算子の使用で無理やり変換は可能
        // short s2 = n; // NG 後述のキャスト演算子の使用で無理やり変換は可能

        // 小数型
        float f = 2.5F;
        double d = f;   // OK
        // float f2 = d;   // NG 後述のキャスト演算子の使用で無理やり変換は可能

        String str = "100";
        // int num = str;      // NG メソッドを使うことで変換可能
    }
}

変数まとめ

  • 変数とはデータを格納する箱。またはデータにラベル付けして名前を付けたもの
  • 変数名の付け方はJavaのルールで決められているものと、Javaの世界で観衆になっているものがある
  • 変数を使用する際には型が必要
  • 型とはデータの種類のこと
  • 型は大きく分けて基本型と参照型に分かれる
  • 基本型は7種類。よく使用するのは、intとdouble型
  • 文字列を表すStringは参照型

演算

計算を行う処理のことを演算といいます。
コンピュータにおいては、全ての処理は数値の計算で成り立っています。
そのため、プログラミングでは数値の計算以外でも様々な処理を「演算」という言葉で表現します。
演算の対象となるものを「オペランド」、演算を行うものを「演算子」といいます。
例えば、「1 + 2」という式があった場合、「1」と「2」がオペランドで、「+」が演算子です。
オペランド演算子が合わさったものは「式」と呼ばれます。
また、プログラミングの世界では演算子がなくても、変数単体の場合でも「式」と呼ばれます。
式は、評価されることによって一つの値となります。

以下、様々な演算子について説明していきます。

算術演算子

数値の計算や文字列の結合を行うための演算子です。

5種類あります。

  • +:加算
  • -:減算
  • *:乗算(×に該当)
  • /:徐算(÷に該当)
  • %:剰余(割り算の余り)

剰余は、偶数か奇数かの判断、ある数の倍数かどうかを判断する場合などに使用します。
複数の算術演算子を組み合わせた場合、優先度が高いものから処理されます。
優先度の高さは数学の場合と同じです。
乗算、除算、剰余の3つが加算、減算よりも優先度が高いです。 同じ優先度の演算が並ぶ場合は左から順に処理されます。 優先度を変更したい場合、()を使用することで優先順位を変えることが可能です。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 数値の演算
        System.out.println(1 + 2);   // 加算
        System.out.println(10 - 5);  // 減算
        System.out.println(10 * 10); // 乗算
        System.out.println(10 / 2);  // 除算
        System.out.println(10 % 3);  // 剰余(割り算の余り)

        int i = 5;
        System.out.println(i + 5); // 変数も使える

        // 整数同士の計算は結果も整数になる
        System.out.println(10 / 3);

        // 結果を小数にしたい場合は両方、あるいは片方を小数にする
        System.out.println(10 / 3.0);

        System.out.pritnln("-----------");

        // 0での割り算はできないので実行時にエラー
        // System.out.println(10 / 0);

        // 優先順位
        // 加算、減算よりも、乗算、除算、剰余が優先される
        System.out.println(1 + 2 * 3); 
        // 優先順位を変えたいしたい場合は括弧を付ける
        System.out.println((1 + 2) * 3);

        System.out.println("-----------");

        // 文字列の結合
        System.out.println("Hello" + " World");
        // 文字と数値を足すと文字列になる
        System.out.println("Hello" + 1 + 2);
        // 計算を先に行いたい場合は括弧を使用する
        System.out.println("Hello" + (1 + 2));

        String str = "Hello";
        // 変数も使える
        System.out.println(str + " World"); 
    }
}

結果

3
5
100
5
1
10
3
3.3333333333333335
-----------
7
9
-----------
Hello World
Hello12
Hello3
Hello World

オーバーフロー

数値を入れる型には値の上限があり、 計算内容によっては上限の値を超えて、意図しない結果になることがあります。
これをオーバーフロー(桁あふれ)といいます。

例えば、int型の場合、32bitの範囲しか扱うことができません。
演算を行ったときに、32bit目で繰り上がりが発生して、33bit以上でないと表すことができない数値になった場合、
32bit目以降は切り捨てられます。
そのため、意図しない結果となってしまいます。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // int型 + int型 = int型
        // 計算結果がint型に収まらないため、意図しない結果になる
        System.out.println(2000_000_000 + 2000_000_000);

        // Lを付けることでlong型になるので、意図した結果になる
        System.out.println(2000_000_000L + 2000_000_000L);

        // Lをつけるのは片方だけでも良い。
        // 異なる型同士の計算は大きい方に合わせる
        System.out.println(2000_000_000L + 2000_000_000);
    }
}

結果

-294967296
4000000000
4000000000

浮動小数点の丸め誤差

TODO

代入演算子

変数に値を代入するときの演算子のことです。
数学では「=」は左辺と右辺が等しいことを表す演算子ですが、プログラミングの世界では代入を表す演算子になり、
左辺に右辺を代入することを表します。
=と算術演算子を組み合わせることで変数を使用した計算結果をそのまま変数に上書きすることができます。

  • =:通常の代入
  • +=:右辺を加算して代入
  • -=:右辺を減算して代入
  • *=:右辺を乗算して代入
  • /=:右辺で割り算して代入
  • %=:右辺で割った余りを代入

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n;
        n = 10;  // 代入
        n += 10; // nに10を加算した値を代入
        System.out.println(n);
        n = 10;
        n -= 5;  // nから10を減算した値を代入
        System.out.println(n);
        n = 10;
        n *= 3;  // nに3を掛けた値を代入
        System.out.println(n);
        n = 10;
        n /= 2;  // nを2で割った値を代入
        System.out.println(n);
        n = 10;
        n %= 3;  // nを3で割った余りを代入
        System.out.println(n);

        // 文字列に対しては文字列結合ができる
        String str = "abc";
        str += "def";
        System.out.println(str);
    }
}

結果

20
5
30
5
1
abcdef

単項演算子

数値に対して1を加算、あるいは減算する演算子です。
加算をインクリメント、減算をデクリメントといいます。
評価されるタイミングによって前置、後置の二つがあります。
それぞれの組み合わせで計4種類あります。

  • 後置インクリメント:処理されたあとに1加算する
  • 後置デクリメント:処理された後に1減算する
  • 前置インクリメント:処理される前に1加算する
  • 前置デクリメント:処理される前に1減算する

実際の開発では、後置インクリメント(デクリメント)が使用されることがほとんどで、前置インクリメント(デクリメント)はあまり使用されません。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 10;   
        // 後置インクリメント
        System.out.println(n++);
        System.out.println(n);
        System.out.println("---");
        // 後置デクリメント
        System.out.println(n--);
        System.out.println(n);
        System.out.println("---");
        // 前置インクリメント
        System.out.println(++n);
        System.out.println(n);
        System.out.println("---");
        // 前置デクリメント
        System.out.println(--n);
        System.out.println(n);
    }
}

結果

10
11
---
11
10
---
11
11
---
10
10

比較演算子

左辺と右辺を比較し、結果をboolean型の値(trueかfalse)として返す演算子です。

  • ==:右辺と左辺が等しい場合true
  • >=:左辺が右辺以上の場合true
  • <=:右辺が左辺以上の場合true
  • >:左辺が右辺より大きい場合true
  • <:左辺が右辺より大きい場合true
  • !=:右辺と左辺が異なる場合true

左辺と右辺が等しいことを表すとき、数学では「=」を使用しますが、Javaでは「=」は代入を表す演算子のため、区別するために「==」として=記号2つで表現します。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 10;
        int m = 8;
        System.out.println(n == m);  // 同じ場合true
        System.out.println(n >= m);  // nがm以上の場合true
        System.out.println(n <= m);  // nがm以下の場合true
        System.out.println(n > m);   // nがmより大きい場合true
        System.out.println(n < m);   // nがmより小さい場合true
        System.out.println(n != m);  // nとmが異なる場合true
    }
}

結果

false
true
false
true
false
true

論理演算子

結果がbooleanの値となる式を複数組み合わせたい場合に使用する演算子です。
比較演算子を使用した式を複数組み合わせたい場合などに使用します。

  • && :「かつ(and)」を表す演算子。左辺と右辺が両方trueの場合にtrue。左辺がfalseだった場合、右辺は評価しない。
  • & :「かつ(and)」を表す演算子。左辺がfalseの場合でも右辺も評価する。
  • || :「または(or)」を表す演算子。左辺か右辺の片方でもtrueだった場合にtrue。左辺がtrueの場合、右辺は評価しない。
  • | :「または(or)」を表す演算子。左辺がtrueの場合でも右辺も評価する。
  • ! :「否定」を表す演算子。trueの場合false、falseの場合trueとなる。

「&&」と「||」は短絡演算子と呼ばれることもあります。
(左辺を評価した事前で全体の評価が決まる場合、右辺を評価しないため)
実際の開発では「&」と「|」はあまり使用されません。
「&&」と「||」が使用されることがほとんどです。

「&&」と「||」では「&&」の方が評価の優先順位が高いです。
算術演算子と同じく()で優先順位を変えることができます。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        boolean b1 = (10 > 5);
        boolean b2 = (10 < 5);
        // &&(論理積)
        // b1とb2が両方trueの場合にtrue 
        // b1がfalseの場合、b2は評価しない
        System.out.println(b1 && b2);  // false
        // b1とb2の片方でもtrueの場合にtrue
        // b1がtrueの場合、b2は評価しない
        System.out.println(b1 || b2);  // true
        // 否定
        // b1がtrueの場合false、b1がfalseの場合true
        System.out.println(!b1);  // false
        // b1とb2が両方trueの場合にtrue 
        // b1がfalseの場合でもb2も評価する
        System.out.println(b1 & b2); // false
        // b1がtrueの場合でもb2も評価する
        System.out.println(b1 | b2); // true

        boolean b3 = true;
        // 連続して書くことも可能
        System.out.println(b1 && b2 && b3); // false

        // 以下は、左から順に評価した場合falseになりそうだが、実際はtrueになる
        // || と && は、&&の方が先に評価される
        System.out.println(true || false && false); // true

        // 括弧で評価の優先順位を変えられる
        System.out.println((true || false) && false);  // false

    }
}

キャスト演算子

型の変換に使用する演算子です。
使用できるのは互換性のある型同士だけです。
基本型の場合、数値同士の場合。
参照型の場合は継承関係がある型同士の場合です。
参照型の場合は実行時エラーの可能性があるので使用時には注意が必要です。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 10000;
        // (型)がキャスト演算子 
        // 値が変わる可能性があるので使用には注意する
        byte b = (byte)n; 
        System.out.println(b);

        // 以下はコンパイルエラー
        // 整数と文字列は互換性がないため変換できない。
        // String s = "123";
        // int n = (int)s;
    }
}

結果

16

ビット演算子

数値を2進数で表現したときのビット演算が行える演算子です。

  • &:AND 両方のビットが1の場合に1
  • |:OR どちから片方でもビットが1なら1
  • ^:XOR 片方のビットが1なら1
  • ~:NOT ビットを反転させる
  • <<:左へビットシフト
  • >>:右へビットシフト(符号あり)
  • >>>:右へビットシフト(符号なし)

使用するかどうかは作成するプログラムによるかと思いますが、
使用頻度はそれほど多くないでしょう。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int b1 = 0b11110000; // 10進数だと240
        int b2 = 0b00001111; // 10進数だと15

        System.out.println(b1);
        System.out.println(b2);

        int result = b1 | b2;
        System.out.println(result); // 11111111 // 10進数だと255

        result = b1 & b2;
        System.out.println(result); // 00000000 // 10進数だと0

        result = b1 ^ result; // 11110000 ^ 00000000
        System.out.println(result); // 11110000 // 10進数だと240

        result = ~b1;
        System.out.println(result); // 00001111
        System.out.printf("%x\n", result); // 16進数で確認

        System.out.println(b2<<1); 
        System.out.println(b2>>1); 
        System.out.println(b2>>>1); 

    }
}

結果

240
15
255
0
240
-241
ffffff0f
30
7
7

演算子まとめ

  • 数値の計算を行う場合は算術演算子を使用する
  • 算術演算子は四則演算(+, -, *, -)と剰余(%)がある
  • 文字列の結合には「+」を使用する
  • 変数に値を代入する場合は代入演算子を使用する
  • 代入のときに算術演算子と組み合わせて計算結果を代入できる
  • 単項演算子で1加算、1減算ができる
  • 比較演算子と論理演算子は制御構文と組み合わせて使用する
  • ビット演算が可能

制御構文

プログラムの処理は上から下に順番に処理されていきます。

条件分岐

大きく分けて、if文とswitch文の2種類があります。

if文

if文

条件を満たす場合のみ特定の処理を行いたいという場合はif文を使用します。

構文

if (条件式) {
    処理
}

Main.java

package app;

public class Main {
    public static void main (String[] args) {

        int n = 10;
        if (n >= 5) {
            System.out.println("5以上");
        }

        // 処理が1行だけの場合は{}を省いても可
        if (n >= 5)
            System.out.println("5以上");
    }
}

結果

5以上
5以上

いろいろな書き方 以下の例では4つのif文が書かれていますが、
書き方が違うだけで実質全て同じです。
※nが小数の型の場合は同じ条件ではなくなります。
自分が後で見た時に分かりやすいと思えるような書き方をするように心がけましょう。

Main.java

package app;

public class Main {
    public static void main (String[] args) {

        int n = 10;
        // 1
        if (n >= 5) {
            System.out.println("5以上");
        }

        // 2
        if (n > 4) {
            System.out.println("5以上");
        }

        // 3
        if (5 <= n) {
            System.out.println("5以上");
        }

        // 4
        if (4 < n) {
            System.out.println("5以上");
        }
    }
}

結果

5以上
5以上
5以上
5以上

中かっこ省略時の注意点
中かっこを省略した場合は、if文の直後の1行目にのみif文が適用されます。
以下の例の場合、Aを出力する処理のにみif文が適用されるので注意しましょう。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 1;
        if (n >= 5)
            System.out.println("A");
            System.out.println("B");
            System.out.println("C");
    }
}

結果

B
C

if~else

条件を満たす場合と満たさない場合で処理を分けたい場合はif~else文を使用します。

構文

if(条件式) {
    処理
} else {
    処理
}

Main.java

package app;

public class Main {
    public static void main (String[] args) {
    
        int n = 10;
        if (n >= 5) {
            System.out.println("5以上");
        } else {
            System.out.println("5未満");
        }
    }
}

結果

5以上

if else if文

条件をさらに細かく分けたい場合はif~else if文を使用します。

構文

if(条件式) {
    処理
} else if (条件式) {
    処理
} else {
    処理
}

最後のelseは省略可

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 8;
        if (n >= 10) {
            System.out.println("10以上");
        } else if (n >= 5 ) {
            System.out.println("5以上");
        } else {
            System.out.println("5未満");
        }
    }
}

結果

5以上

if~else if文を使用する場合は条件を書く順番に注意が必要です。
以下のプログラムを確認してください。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int a = 100;

        if (a >= 10) {
            System.out.println("10以上です");
        } else if (a >= 50) {
            System.out.println("50以上です");
        } else if (a >= 100) {
            System.out.println("100以上です");
        }

        if (a >= 100) {
            System.out.println("100以上です");
        } else if (a >= 50) {
            System.out.println("50以上です");
        } else if (a >= 10) {
            System.out.println("10以上です");
        }
    }
}
10以上です
100以上です

2つのif文の条件式は同じですが、処理が異なっています。
if文は、条件式を上から順に評価していき、当てはまったブロックの処理を実行します。
そしてそれ以降の処理は実行されません。
if~else ifを使用して条件を複数書くときは、順番に注意しましょう。

文字列比較

文字列(というか、参照型)の場合、同じかどうかを比較するときはequalsメソッドを使用します。
(メソッドが何かについては後述)
参照型の場合、変数の値に入っているのはインスタンス(Stringの場合は文字列インスタンス)のアドレス。
そのため、==を使用した場合はアドレス同士の比較になるため、文字列の値が同じかどうかは関係なくなってしまいます。
そのため、==を使用してもコンパイルエラーにはなりませんが、意図しない結果になってしまいます。
equalsメソッドを使用すると、アドレスではなく、文字列の値が等しいかどうかを比較してくれます。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        if (s1.equals(s2)) {
            System.out.println("同じ");
        } else {
            System.out.println("異なる");
        }
    }
}

結果

同じ

上で参照型の値の場合、比較はequalsメソッドを使用すると書きましたが、
String型の場合、「==」で比較しても同じ結果になる場合があります。 例えば、先の例ではequalsメソッドを「==」に変更 しても同じ結果になります。
このような現象が起きるのは、String型が参照型の中でも特殊な扱いをされている型だからです。
詳しくは後述しますが、基本的に参照型の比較はequalsメソッドを使用すると理解しておいてください。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        if (s1 == s2) {
            System.out.println("同じ");
        } else {
            System.out.println("異なる");
        }
    }
}

結果

同じ

論理演算子の活用

if文を入れ子にして書く場合、論理演算子を使用して条件式をまとめられる場合があります。
条件が複数ある場合は、論理演算子を有効に活用してソースコードを読み見やすくするように心がけましょう。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int a = 10;
        int b = 20;

        if (a >= 10) {
            if (b >= 20) {
               System.out.println(a + b); 
            }
        }

        // 上記と同じ処理
        if (a >= 10 && b >= 20) {
            System.out.println(a + b);
        }
    }
}

結果

30
30

ドモルガン

以下の例は2つのif文がありますが、 結果はどちらも同じ条件式になります。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 70;
        if (n >= 0 && n <= 100) {
            System.out.println("0~100の間");
        }

        if (!(n < 0 || n > 100)) {
            System.out.println("0~100の間");
        }
    }
}

結果

0~100の間
0~100の間

!(否定)を使用した場合、

|| ⇒ &&
&& ⇒ ||
<= ⇒ >
>= ⇒ <
> ⇒ <=
< ⇒ =>

と変換されます。 (図を描くとイメージしやすくなります。)

三項演算子(条件演算子

?の左側の条件がtrueの場合、?の右の値が代入される、falseの場合:の右の値が代入される演算子です。
if~elseを使って変数に値を代入するような処理を簡単に書くことができる演算子です。
初心者には分かりにくいため、積極的には使用しないことが多いです。
条件によって文字列を結合させるような処理をする場合には三項演算子を使用したほうが分かりやすくなる場面もあります。

構文

変数 = 条件式 ? trueの場合の値 : falseの場合の値;

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 10;
        String str = n >= 5 ? "5以上":"5未満";
        System.out.println(str);

        // if~elseで同じことができる
        n = 3;
        if (n >= 5) {
            str = "5以上";
        } else {
            str = "5未満";
        }
        System.out.println(str);
    }
}

結果

5以上
5未満

switch

breakは省略可。書き忘れて思わぬ動作になりやすいので注意が必要です。
defaultは省略可能です。
変数の取りうる値があらかじめ決まっている場合に有効になります。

構文

switch (式) {
    case ラベル:
        処理
        break;
    case ラベル:
        処理
        break;
    ...
    default:
        処理
        break;
}

switchの式で扱える型はあらかじめ決められています。

  • byte
  • short
  • int
  • char
  • String
  • Enum

です。
Enumは一言でいうと定数の集合体のようなものです。
詳しくは後述します。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int n = 5;
        switch (n) {
            case 1:
                System.out.println("nは1");
                break;
            case 2:
                System.out.println("nは2");
                break;
            case 3:
                System.out.println("nは3");
                break;
            default:
                System.out.println("それ以外");
                break;
        }

        System.out.println("----------");

        // 文字も可
        char c = 'b';
        switch (c) {
            case 'a':
                System.out.println("nは1");
                break;
            case 'b':
                System.out.println("nは2");
                break;
            case 'c':
                System.out.println("nは3");
                break;
            default:
                System.out.println("それ以外");
                break;
        }

        System.out.println("----------");

        // 文字列も可
        String s = "a";
        switch (s) {
            case "a":
                System.out.println("A");
                break;
            case "b":
                System.out.println("B");
                break;
            case "c":
                System.out.println("C");
                break;
            default:
                System.out.println("それ以外");
                break;
        }

        System.out.println("----------");

        // breakは省略することも可
        switch (s) {
            case "a":
                System.out.println("A");
                // break;
            case "b":
                System.out.println("B");
                // break;
            case "c":
                System.out.println("C");
                break;
            default:
                System.out.println("それ以外");
                break;
        }
        // この場合 A, B, C が表示される
        // つまり、breakがない場合は、次のcaseラベルの処理も実行される。
    }
}

結果

それ以外
----------
nは2
----------
A
----------
A
B
C

ifとswitch

switch文でできることは、if文でも実現可能です。 以下のプログラムはif文とswitch文で同じことをしています。

Main.java

package app;

public class Main {
    public static void main (String[] args) {

        String str = "a";

        if(str.equals("a")) {
            System.out.println("A");
        } else if (str.equals("b")) {
            System.out.println("B");
        } else if (str.equals("c")) {
            System.out.println("C");
        } else {
            System.out.println("Z");
        }

        switch(str) {
            case "a":
                System.out.println("A");
                break;
            case "b":
                System.out.println("B");
                break;
            case "c":
                System.out.println("C");
                break;
            default:
                System.out.println("Z");
                break;
        }

        System.out.println("---");

        int num = 1;
        if (num == 1) {
            System.out.println("1");
        } else if (num == 2) {
            System.out.println("2");
        } else if (num == 3) {
            System.out.println("3");
        } else {
            System.out.println("0");
        }

        switch(num) {
            case 1:
                System.out.println("1");
                break;
            case 2:
                System.out.println("2");
                break;
            case 3:
                System.out.println("3");
                break;
            default:
                System.out.println("0");
                break;
        }
    }
}

結果

A
A
---
1
1

つまり、値が等しいとき、という条件の場合は、ifでもswitchでも書けるということです。
好みにもよりますが、このような場合、switch文を使用することをお勧めします。
一般的には値が等しい場合の処理を書くならswitchの方が読みやすいです。
また、内部構造的にswitchの方が処理が速いと言われています。
なので、switchが使用できる場合は積極的にswitch文を使用していきましょう。

Java12からのswitch

TODO

繰り返し

for文とwhile文の2種類があります。
ループ文とも呼びます。
for文は、あらかじめループの回数が決まっている場合に使用します。
while文は、ループの回数を判断することができない場合に使用します。
ループの回数が判断できない場合とは、例えばDBの読み込みやファイルの読み込みなど、Javaのプログラム外にあるリソースを読み込んで処理する場合などです。
Javaのプログラム外のリソースを利用しない場合は、ほとんどの場合for文で対応可能です。

for

構文

for(初期化;条件式;後処理) {
    処理
}

初期化、条件式、後処理はそれぞれ省略可能です。
ただし、省略することで無限ループになる可能性もあり、省略する場面は少ないです。
無限ループとは、ループが終了せずに中の処理が永遠に繰り返される状態です。
開発者が意図的にプログラムを停止して処理を強制的に終了させる必要があります。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 条件式がtrueになっている間、処理を繰り返す
        for (int i = 0; i < 10; i++) {
            System.out.print(i);
        }

        System.out.println("");

        // if文同様、処理が1行の場合は中かっこ省略可
        for (int i = 0; i < 10; i++) 
            System.out.print(i);

        System.out.println("");
        
        // 2重for
        // for文の中にfor文が書ける。
        // for文に限らず、if文など、処理は何でも書ける
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                System.out.print("i * j = " + (i * j) + " ");
            }
            System.out.println("");  // 改行
        }

        // 以下は初期化と条件と後処理を省略した形
        // コンパイルエラーにはならないが無限ループになる
        // for(;;){
        //     System.out.println("for文");
        // }
    }
}

結果

0123456789
0123456789
i * j = 0 i * j = 0 i * j = 0 i * j = 0 i * j = 0 i * j = 0 i * j = 0 i * j = 0 i * j = 0 i * j = 0
i * j = 0 i * j = 1 i * j = 2 i * j = 3 i * j = 4 i * j = 5 i * j = 6 i * j = 7 i * j = 8 i * j = 9
i * j = 0 i * j = 2 i * j = 4 i * j = 6 i * j = 8 i * j = 10 i * j = 12 i * j = 14 i * j = 16 i * j = 18
i * j = 0 i * j = 3 i * j = 6 i * j = 9 i * j = 12 i * j = 15 i * j = 18 i * j = 21 i * j = 24 i * j = 27
i * j = 0 i * j = 4 i * j = 8 i * j = 12 i * j = 16 i * j = 20 i * j = 24 i * j = 28 i * j = 32 i * j = 36
i * j = 0 i * j = 5 i * j = 10 i * j = 15 i * j = 20 i * j = 25 i * j = 30 i * j = 35 i * j = 40 i * j = 45
i * j = 0 i * j = 6 i * j = 12 i * j = 18 i * j = 24 i * j = 30 i * j = 36 i * j = 42 i * j = 48 i * j = 54
i * j = 0 i * j = 7 i * j = 14 i * j = 21 i * j = 28 i * j = 35 i * j = 42 i * j = 49 i * j = 56 i * j = 63
i * j = 0 i * j = 8 i * j = 16 i * j = 24 i * j = 32 i * j = 40 i * j = 48 i * j = 56 i * j = 64 i * j = 72
i * j = 0 i * j = 9 i * j = 18 i * j = 27 i * j = 36 i * j = 45 i * j = 54 i * j = 63 i * j = 72 i * j = 81

while

条件式がtrueになっている間、処理を繰り返す文です。

構文

while(条件式) {
    処理
}

do~while

条件式がtrueになっている間、処理を繰り返す文です。 while文との違いは、処理が必ず1回は実行されるかどうかの違いです。
do~whileは、条件がどうあろうと最初の1回は必ず実行されます。

do {
    処理
} while (条件式);

whileとdo~whileを比較した場合、do~whileでなければできない処理はそれほど多くないため、whileの方が使用頻度としては高いです。

Main.java

package app;

public class Main {
    public static void main (String[] args) {

        int i = 0;
        while(i < 10) {
            System.out.print(i);
            i++; // これがないと無限ループになって処理が終わらなくなるので注意。
        }

        System.out.println("");

        int j = 10;
        do {
            System.out.print(j);
            i++; 
        } while(i < 10);
    }
}

結果

0123456789
10

ループ制御

ループを制御する構文としてbreakとcontinueがあります。

  • break:直近のループを終了させる
  • continue:その周の処理を中断して次の周に入る

break

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // break
        // ループの処理を終わらせることができる
        for (int i = 0; i < 10; i++) {
            System.out.print(i);
            // iが5以上ならループ終了
            if(i >= 5) {
                break;
            }
        }

        System.out.println("");

        int i = 0;
        while(i < 10) {
            System.out.print(i);
            i++; 
            // iが5以上ならループ終了
            if(i >= 5) {
                break;
            }
        }
    }
}

結果

012345
01234

continue

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // continue
        // その周のループを飛ばして次のループに処理が移る
        for (int i = 0; i < 10; i++) {
            // 偶数なら処理スキップ
            if (i % 2 == 0) {
                continue;
            }
            System.out.print(i);
        }

        System.out.println("");

        int i = 0;
        while(i < 10) {
            i++; 
            // 偶数なら処理スキップ
            if (i % 2 == 0) {
                continue;
            }
            System.out.print(i);
        }
    }
}

結果

13579
13579

breakやcontinueの注意点

ループ処理が入れ子になっているとき、breakやcontinueが適用されるのは一番直近のループ処理のみです。
外側のループには適用されないので、入れ子のループ構造の中で使用する際は注意が必要です。

package app;

public class Main {
    public static void main (String[] args) {
        
        int i = 0;
        int j = 0;
        while(i < 3) {
            j = 0;
            while(j < 10) {
                if (j == 5) {
                    break; // 直近のwhileを抜ける
                }
                j++;
                System.out.print(j);
            }
            i++;
            System.out.println("");
        }

        System.out.println("-----");

        i = 0;
        j = 0;
        while(i < 3) {
            j = 0;
            while(j < 10) {
                j++;
                if (j % 2 == 0) {
                    continue; // 直近のwhileを抜ける
                }
                System.out.print(j);
            }
            i++;
            System.out.println("");
        }
    }
}

結果

12345
12345
12345
-----
13579
13579
13579

変数のスコープ

if文やfor文などのブロックの中で宣言した変数は、
そのブロックの中でしか使用できません。
変数が使用できる範囲のことを変数のスコープといいます。
スコープ外の変数を使用することはできません。
仮にスコープ外の変数を使用しようとした場合はコンパイルエラーになります。

Main.java

package app;

public class Main {
    public static void main(String[] args) {
        if(true) {
            int num = 10;
            System.out.println(num);
        }

        // 以下はコンパイルエラー
        // if文の中で宣言された変数はif文の外で使用できない
        // System.out.println(num);

        // ブロックが異なれば同じ名前の変数も使用可能
        if (true) {
            int num = 10;
            System.out.println(num);
        } else {
            int num = 20;
            System.out.println(num);
        }


        for (int i = 0; i < 10; i++) {
            System.out.print(i);
        }

        // 以下もコンパイルエラー
        // iはfor文の外では使えない
        // System.out.println(i);

        if (true) {

            if (true) {
                int num = 30;
            }
            // 以下もコンパイルエラー
            // System.out.println(num);
        }

    }
}

結果

10
10
0123456789

制御構文まとめ

  • 条件によって処理を分けたい時はif文を使用する
  • 変数の取りうる値があらかじめ決まっている場合はswitch文を使用する
  • 同じ処理を繰り返し行いたい場合はループ処理を使用する
  • あらかじめループ回数が決まっている場合はfor文を使用する
  • ループ回数があらかじめ決まっていない場合はwhile文を使用する
  • ループ処理を途中で終了させたい場合にはbreakを使用する
  • ループでその周の処理をスキップさせたい場合はcontinueを使用する

配列

配列はまとまったデータを扱う仕組みです。
プログラムでは複数のデータをまとめて扱った方が効率よく処理できる場合があります。
変数では基本的に一つのデータしか扱えないため、複数のデータを扱うには別の仕組みが必要です。
そこで役に立つのが配列です。

配列の基本

まずは以下のソースコードから配列の基本的な使い方を確認してください。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // int型の配列の宣言
        int[] array;

        // 以下のように宣言だけの状態で使用するとコンパイルエラー
        // System.out.println(array);

        // 初期化(要素数の確保)
        // 配列の中の一つ一つの箱のことを要素と呼ぶ
        // 5つの要素が確保された
        array = new int[5]; 

        // まとめて書いても可
        // int[] array = new int[5];

        // 要素へのアクセスは、[]の中に何番目かの数字を書く
        // このとき0のことを添え字という
        // 添え字は0から始まる
        System.out.println(array[0]);  // new で初期化し場合、intの初期値(0)がセットされている
        array[0] = 1; // それぞれの要素に値をセットできる
        array[1] = 2;
        array[2] = 3;
        array[3] = 4;
        array[4] = 5;
        System.out.println(array[1]);
        System.out.println(array[2] + array[3]); // 変数のように計算も可
        int n = array[4];       // 変数へのセット可能
        System.out.println(n); 

        int i = 3;
        System.out.println(array[i]); // 添え字に変数を使用することも可

        // 以下は実行時エラー
        // 5つの要素を確保した場合、使用できる添え字は0~4
        // array[5] = 10;
        // System.out.println(array[5]);   

        // 配列そのものを出力すると、ハッシュ値と呼ばれる配列の値が格納されたアドレスが表示される。
        // 詳しくはtoStringメソッドで。ここでは割愛。
        System.out.println(array); 

        // 最初から値をセットする初期化方法もある
        int[] array2 = {1, 2, 3, 4, 5};
        System.out.println(array2[0] + array2[1]); 

        // []は変数名に付けることもできる
        // 以下の2つは全く同じ意味
        int[] array3 = new int[5];
        int array4[] = new int[5];
    }
}

結果

0
2
7
5
4
[I@6d06d69c
3

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // Stringの配列
        // 配列はint型だけでなく、他のどんな型でも使用可
        // もちろんStringでも可能
        String[] sArray1 = new String[3];
        System.out.println(sArray1[0]); // Stringの場合、初期値はnullという特殊な値になる

        // もちろんこっちの初期化も可能。
        String[] sArray2 = {"abc", "def", "hig"};
        System.out.println(sArray2[0]);

        // length
        // lengthを使用すると、配列の要素数を取得できる
        int[] numArray = {1, 2, 3, 4, 5};
        String[] strArray = {"a", "b", "c"};
        System.out.println(numArray.length);
        System.out.println(strArray.length);
    }
}

結果

null
abc
5
3

配列のループ処理

配列はループ処理を相性が良く、組み合わせて処理を行うことが多いです。
配列をループを使って処理する場合、主にfor文を使って処理します。
lengthという配列の要素数を取得する機能を用いてループしたり、拡張for文と呼ばれるfor文を用いて処理を行うことも多いです。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 配列のfor文
        // lengthを使用すると、配列の要素数分のループができるので、相性が良い
        // 合計出力
        int sum = 0;
        int[] numArray = {1, 2, 2, 4, 5};
        for (int i = 0; i < numArray.length; i++) {
            sum += numArray[i];
        }
        System.out.println(sum);

        // 毎回出力
        String[] strArray = {"A", "B", "C"};
        for (int i = 0; i < strArray.length; i++) {
            System.out.print(strArray[i]);
        }
    }
}

結果

14
ABC

拡張for文

構文

for (要素の型 変数名 : 配列名) {
    処理
}

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 拡張for文
        // 配列に対する便利なfor文がある
        // 自動的に要素数分繰り返し処理がされる。
        // 定義した変数に要素が自動的にセットされる

        int[] nums = {1, 2, 3, 4, 5};
        for(int num : nums) {
            System.out.print(num);
        }

        String[] strArray = {"a", "b", "c", "d", "e" };
        for(String str : strArray) {
            System.out.print(str);
        }
    }
}

2次元配列

2次元配列は配列の中に配列を入れる仕組みです。
列と行で構成される表をイメージすると良いかもしれません。
3次元、4次元なども作成可能ですが、使用される場面は限られるため ここでは2次元のみを紹介します。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // 2次元配列
        // 配列の中に配列を入れることができる
        int[][] array;
        // 要素は 2 × 3 で6つの要素を確保。
        array = new int[2][3];
        array[0][0] = 10;
        array[0][1] = 20;
        array[1][0] = 30;
        array[1][1] = 40;
        System.out.println(array[0][0]);
        System.out.println(array[0][1]);
        System.out.println(array[1][0]);
        System.out.println(array[1][1]);
        System.out.println(array[1][2]);

        // 値を指定して初期化する場合は、中かっこを入れ子にする
        // 2 × 3 の要素
        int[][] array2 = {{1, 2, 3}, {4, 5, 6}};
        System.out.println(array2[0][0]); // 1が出力
        System.out.println(array2[0][1]); // 2が出力
        System.out.println(array2[1][0]); // 4が出力
        System.out.println(array2[1][1]); // 5が出力

        System.out.println(array2.length);     // 2が出力される。1次元配列の要素が2つ
        System.out.println(array2[0].length);  // 3が出力される。要素は{1,2,3}なので3つ
    }
}

結果

10
20
30
40
0
1
2
4
5
2
3
package app;

public class Main {
    public static void main (String[] args) {
        // 要素数が違う1次元配列を格納することも可
        int[][] array = {{1}, {2,3}, {4,5,6}};
        System.out.println(array[0][0]);
        System.out.println(array[1][1]);
        System.out.println(array[2][2]);

        // 2次元配列の要素数だけ確保して
        int[][] array2 = new int[2][];
        // 1次元配列を後から初期化も可能
        array2[0] = new int[] {1,2,3};
        array2[1] = new int[] {4,5,6};

        // 2重ループで要素を出力
        for (int i = 0; i < array2.length; i++) {
            for(int j = 0; j < array2[i].length; j++) {
                System.out.print(array2[i][j]);
            }
        }

        System.out.println("");

        // 拡張for文
        for(int[] a : array2) {
            for(int b : a) {
                System.out.print(b);
            }
        }
    }
}

結果

1
3
6
123456
123456

配列の弱点

配列はまとまったデータを扱いたい時に非常に便利な仕組みです。
しかし、配列は、一度要素数を決めてしまうと、その後に要素数を変更することができません。
もしも動的に要素を増やしたい場合は、リストと呼ばれる仕組みを使用します。
リストについてはコレクションフレームワークの中で扱います。


メソッド

メソッドは一言でいえば処理をまとめたものです。
処理を書く場合はメソッドにしか書けません。
1つのクラスにはメソッドはいくつでも書くことができます。

メソッドの定義

構文

public static 戻り値の型 メソッド名(引数の型 引数名, ...) {
    処理
    return 戻り値;
}

解説

public

アクセス修飾子と呼ばれるものです。 詳しくは「アクセス修飾子」で扱います。 今はとりあえずpublicをつけておくとどこからでも使用できるメソッドになると覚えておきましょう。

static

staticが付くと「クラスメソッド」になります。 付けない場合は「インスタンスメソッド」になります。 インスタンスメソッドとクラスメソッドの違いについてはオブジェクト指向で扱います。 今の段階ではメソッドは全てクラスメソッドとして作成します。

戻り値の型

メソッドは、メソッドを呼び出した側に値を返すことができます。 その時に返す値の型を書きます。 もし、値を返す必要がない場合は「void」と書きます。

メソッド名

メソッドには名前を付ける必要があります。 メソッド名のルールや慣習は変数と同じです。 何の処理をしているのかが名前だけで分かるように付けるのが理想です。

引数

メソッドは処理に必要なデータを受け取ることができます。 これを引数と言います。 引数は何個でも書くことができます。 引数には型と変数名が必要です。 引数で定義された変数は、そのメソッドの中だけで使用されるローカル変数となります。 引数が不要な場合は省略することもできます。

戻り値

メソッドの宣言で戻り値の型を指定している場合は、returnによって戻り値を返す必要があります。 書かない場合はコンパイルエラーになります。 戻り値の型が「void」(戻り値なし)の場合は、returnは不要です。 ただし、return; のみ書くことも可能です。 returnを書いた場合、その時点でメソッドの処理が終了します。

メソッドの定義と呼び出し

Main.java

package app;

public class Main {
    // 引数にint型の値を受け取り、1加算した値を返すメソッド
    public static int addOne(int num) {
        num++;
        return num;
    }

    // 定義したメソッドを呼び出す
    public static void main(String[] args) {
        int a;
        // 同じクラス内のメソッドはメソッド名を指定することで呼び出せる。
        a = addOne(5); // aという変数で処理結果を受け取る。5 + 1 の値がnumに代入れる。
        System.out.println(a); 

        // 戻り値は受けらなくてもエラーにはならない
        addOne(5);

        // 戻り値があるメソッドの呼び出しの場合は代入せずにそのまま出力することもできる。
        System.out.println(addOne(5)); 

        int b = 10;
        a = addOne(b); // 引数は変数で渡すこともできる
        System.out.println(a);

        int n = 10;
        addOne(n); // 引数に渡した値は、メソッドの中にコピーされ、そこだけが反映されるので
        System.out.println(n);  // メソッドを呼び出しても値は変わらない

        // 引数に渡す値が定義と合わない場合はエラー
        // a = addNum();
        // a = addNum(10, 20);

        // 定義された場所は関係ない
        // ソースコード上呼び出すメソッド呼びも下に書いても呼び出せる
        System.out.println(addTen(1));

    }

    // mainメソッド以外のメソッドからでも呼び出し可能
    // addOneメソッドを10回呼び出す
    public static int addTen(int num) {
        for (int i=0; i < 10; i++) {
            num = addOne(num);
        }
        return num;
    }
}

結果

6
6
11
10
11

様々なメソッドの定義

MethodSample.java

package app;

public class MethodSample {

    // int型を受け取って1加算した値を返す
    // 戻り値の型を指定した場合、retuenは必ず書く
    public static int addOne(int num) {
        return num + 1;
    }

    // 引数は何個でも受け取れる
    public static int add(int a, int b) {
        return a + b;
    }

    // 引数は型が異なってもOK
    public static String add(String a, int b) {
        return a + b;
    }

    // 引数はなくても良い
    // その場合は()だけを書く
    public static String getHello() {
        return "Hello";
    }

    // 戻り値がない場合は戻り値の型は「void」と書く
    // 戻り値がない場合は「return」は不要
    public static void dipsAdd1(int a, int b) {
        Sustem.out.println(a + b);
    }

    // voidでもreturnは書くことは可能
    // その場合は戻り値を書かない
    public static void dipsAdd2(int a, int b) {
        Sustem.out.println(a + b);
        return;
    }

    // returnを通ると処理が終了する
    // そのためreturnの後に処理を書くとコンパイルエラー
    public static int addOne2(int num) {
        return num + 1;

        // 以下はコンパイルエラー
        // System.out.println(num); 
    }

    // 戻り値の型を指定している場合、必ずreturnを通る必要がある
    // 特定の条件の場合しか通らない処理の場合はコンパイルエラー
    // 以下はaが1以外の場合はreturnを通らない
    public static String isOne1(int a) {
        if (a == 1) {
            return "OK";
        }
    }

    // 度の場合でもreturnを通るようにしておけばエラーにならない
    public static String isOne2(int a) {
        if (a == 1) {
            return "OK";
        } else {
            return "NG";
        }
    }
}

オーバーロード

Javaでは、引数の数、型、並び順が異なれば、同じメソッド名を定義することができます。
それをオーバーロードと呼びます。

package app;

public class Main {

    public static void main(String[] args) {
        // 対応した引数のメソッドが自動的に呼ばれる
        System.out.println(add(1, 2));
        System.out.println(add(1, 2, 3));
        System.out.println(add("1", 2));
    }

    public static int add(int a, int b) {
        return a + b;
    }

    // 引数の数が違う
    public static int add(int a, int b, int c) {
        return a + b + c;
    }

    // 引数の型が違う
    public static String add(String a, int b) {
        return a + b;
    }

    // 戻り値が違うだけはオーバーロードにはならない
    // 以下はコンパイルエラー
    // public static String add(int a, int b) {
    //    return "" + (a + b);
    // }
}

結果

3
6
12

メソッドの再帰呼び出し

メソッドの中で自分自身のメソッドを呼び出すことを再帰呼び出しといいます。
書き方に注意しないと、returnを通らず、Stackoverflowという例外が発生します。
使いこなせると便利ですが、使用数際は十分注意しましょう。

package app;

public class Main {
    public static void main(String[] args) {
        int n = addRecursion(5);
        System.out.println(n);
    }

    
    public static int addRecursion(int num) {
        num += 10;
        if(num <= 100) {
            num = addRecursion(num);
        }
        return num;
    }
}

結果

105

値渡し

引数に値を渡したときは、値がコピーされて引数に渡されます。
そのため、呼び出し元の変数の値には影響を及ぼしません。
これは引数の型が基本型の場合はイメージしやすいかと思いますが、参照型の場合は初学者は混乱する人が多いです。
参照型の場合、変数にはインスタンスのアドレスが入っているため、引数に渡した時には、アドレスが渡されます。
その仕組みにより、配列などを引数で扱う場合には注意が必要です。
サンプルを見ながら違いを理解できるようにしてください。

Main.java

package app;

public class Main {

    public static void main(String[] args) {
        int num = 5;
        int[] nums = {5, 6, 7};

        changeNum(num);
        changeNum(nums);

        System.out.println(num);      // 値は変わらない
        System.out.println(nums[0]);  // 要素の値は変わる
    }

    // 引数で渡された値を10に変更
    public static void changeNum(int num) {
        num = 10;
    }
    // 引数で渡された配列の最初の要素を10に変更
    public static void changeNum(int[] nums) {
        nums[0] = 10;
    }
}

結果

5
10

別クラスのメソッドの呼び出し

別のクラスに定義されたメソッドを呼び出すこともできます。
別のクラスのメソッドを呼び出す場合は、「クラス名.メソッド名」で呼び出します。
ただし、これはクラスメソッド(メソッド定義にstaticが付いたメソッド)に限ります。
クラスメソッド以外のメソッドの呼び出しに関してはオブジェクト指向で扱います。

Tools.java

package app;

// mainメソッドのないクラスを定義
public class Tools {
    public static int add(int a, int b) {
        return a + b;
    }
}

Main.java

package app;

// 別クラスで定義したメソッドの呼び出し
public class Main {
    public static void main(String[] args) {
        int sum = Tools.add(10, 20);  // クラス名.メソッド名() で呼び出すことができる
        System.out.println(sum);
    }
}

実行する際はmainメソッドがあるクラス(ここではMainクラス)を実行します。
mainメソッドが定義されていないクラスは実行することはできません。

mainメソッド

今まで、作成したプログラムを実行して確認する場合、 最初に動く処理は全てmainというメソッドに書いていました。
このmainというメソッドは特殊なメソッドで、 Javaのプログラムを実行したときに最初に呼ばれるメソッドとなっています。
「エントリポイント」とも呼ばれます。
エントリポイントの特徴は

  • メソッド名は「main」
  • 戻り値はなし(void)
  • 引数はStringの配列(変数名は関係ないが、argsとすることが多い)

ちなみにargsは、arguments(引数)の略だと思われます。

Javaのプログラムを実行するとき、mainメソッドの引数に値を渡すこともできます。
コマンドからの実行の場合、クラス名のあとにスペース区切りで値を書くと、それがmainメソッドの引数になります。
スペース区切りで配列の要素になります。

> java クラス名 引数1 引数2

可変長引数のメソッド

メソッドの引数を何個にすればよいか決まらない場合があります。 そのような場合に、可変長の引数を利用することができます。

構文

メソッド名(型 ... 変数名)

可変長で受け取った値は配列として格納されます。
つまり、実質配列の型で受け取って処理するのと同じになります。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        int sum = add(1, 2, 3);
        System.out.println(sum);
        sum = add(1, 2, 3, 4, 5);
        System.out.println(sum);
    }

    public static int add(int ... nums) {
        int sum = 0;
        // numsを配列として扱う
        for (int i = 0; i < nums.length; i++){
            sum += nums[i];
        }
        return sum;
    }
}

結果

6
15

可変長の引数を使用する場合、条件があります。

  • 引数が複数ある場合、最後の引数にしか書けない
  • 可変長の引数は一つしか書けない

このルールについては、メソッドを使用する際に引数の区切りがつくかどうかを基準に考えると理解しやすくなります。

package app;

public class Main {
    public static void main (String[] args) {
        int sum = add(1, 2, 3);
        System.out.println(sum);
        sum = add(1, 2, 3, 4, 5);
        System.out.println(sum);
    }

    // 引数が複数ある場合、可変長の引数は最後に書く
    public static int add(int sum, int ... nums) {
        for (int i = 0; i < nums.length; i++){
            sum += nums[i];
        }
        return sum;
    }

    // 以下はコンパイルエラー
    // 可変長の引数は最初には書けない
    // public static int add(int ... num, int sum) {
    //     for (int i = 0; i < nums.length; i++){
    //         sum += nums[i];
    //     }
    //     return sum;
    // }

    // 以下もコンパイルエラー
    // 可変長の引数は複数書けない
    // public static int add(int ... sum, int ... nums) {
    //     for (int i = 0; i < nums.length; i++){
    //         sum[0] += nums[i];
    //     }
    //     return sum;
    // }
}

結果

6
15

クラスライブラリの利用

Javaではあらかじめ多数のクラスが用意されています。
クラスライブラリ、標準ライブラリ、APIなどと呼ばれます。
それらのクラスに用意されているメソッドを使用することで、様々な処理を行うことができます。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // IntegerクラスのparseIntメソッド
        // 文字列をint型に変換する。
        String str = "123";
        int num = Integer.parseInt(str);
        System.out.println(num);

        // str = "abc";
        // num = Integer.parseInt(str); // 文字列が数値でない場合は実行時エラーになる。
        // System.out.println(num);

        // Mathクラスのmaxメソッド
        // 引数の数値の最大のものを返す
        int a = 80;
        int b = 85;
        int maxNum = Math.max(a, b);
        System.out.println(maxNum);

        // 0以上1未満のランダムな数を返す
        double r = Math.random();
        System.out.println(r);
    }
}

結果

123
85
0.022441511802850034  // 実行の度に値は変わる

文字列操作

文字列を扱うクラスをStringクラスといいます。
今までのサンプルでも多く使用してきました。 Stringクラスには様々なメソッドが用意されている。
Stringクラスのメソッドを使用することで、様々な文字列の操作が行えます。

クラスの型で作成された変数は、「オブジェクト」や「インスタンス」と呼びます。
(クラスについての詳細は別途説明)
Stringも文字列を表すクラスのため、 文字列はインスタンス(オブジェクト)です。

インスタンスのメソッドを使用する場合、
インスタンス名.メソッド名
により呼び出すことができます。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        String str = "";

        // length
        // 文字列の文字数をint型で返す
        str = "abcde";
        int length = str.length();
        System.out.println(length);

        // toUpperCase
        // 元の文字列を大文字に変換した新しい文字列を作成して返す
        str = "abc";
        String result = str.toUpperCase();
        System.out.println(result);

        // toLowerCase
        // 元の文字列を小文字に変換した新しい文字れを作成して返す
        str = "ABC";
        result = str.toLowerCase();
        System.out.println(result);

        // indexOf
        // 引数で指定された文字が文字列の中の何番目にあるかを返します
        str = "abcde";
        int index = str.indexOf("c"); // 0から始まるので、2が返ってくる
        System.out.println(index);
        index = str.indexOf("z");     // 指定した文字列がない場合は-1が返る
        System.out.println(index);

        // isEmpty
        // 文字列が空文字かどうかをboolean型で返します
        str = "a";
        boolean b = str.isEmpty();
        System.out.println(b);
        str = "";
        boolean b = str.isEmpty();
        System.out.println(b);

        // equals
        // 文字列が引数で指定された文字列と一致するかをboolean型で返します
        str = "abc";
        String str2 = "abd";
        b = str.equals(str2);
        System.out.println(b);

        // substring
        // 部分文字列を作成して返します
        str = "abcdefg";
        result = str.substring(1, 3); // 1番目から3番目まで(0から始まる)
        System.out.println(result);   // bcd

        // replaceAll
        // 指定された文字列を置換する
        str = "abcdefg";
        result = str.replaceAll("abc", "123");  // abcを123に置換
        System.out.println(result);

        // trim
        // 前後の空白を取り除いた新しい文字列を作成して返す
        str = "   abc   ";
        result = str.trim();
        System.out.println(result);
    }
}

ここで紹介した以外にも、Stringクラスには様々なメソッドが用意されています。
詳しくはJavaAPIリファレンスを参照して色々なメソッドを使用してみてください。

メソッドを組み合わせる

メソッドを使用する場合、メソッドの使用結果に対してそのまま更にメソッドを使用することができます。
これはメソッドチェーンとも呼ばれます。
このような書き方もできるようにしておきましょう。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // ある文字列から部分文字列を取得して文字列の長さを取得する
        String str = "abcdefg";
        String result = str.substring(1, 3);
        int length = result.length();
        System.out.println(length);

        // 上記の処理はまとめて書くことが可能
        System.out.println("abscdfg".substring(1, 3).length);
        // 1. オブジェクトは変数に代入しなくてもメソッドが使用できる
        // 2. メソッドの戻り値がオブジェクト(今回の場合String型)の場合は、メソッドの結果に対してさらにメソッドを使用できる。

        // "a"と"c"の位置を取得し、その部分文字列を作成
        str = "abcdefg";
        int index1 = str.indexOf("a");
        int index2 = str.indexOf("c");
        result = str.substring(index1, index2);
        System.out.println(result);

        // これもまとめて書ける
        str = "abcdefg";
        System.out.println(str.substring(str.indexOf("a"), str.indexOf("c")));
        // 引数でもメソッドを使用することができる。
        // int型の引数を受け取る場合、int型が戻り値になっているメソッドを使用することが可能。

        /*
        つまり、メソッドを使用したり、引数に値を渡す場合、必ずしも変数に入れる必要はなく
        メソッドを使用した結果の値が何になるかが重要。
        */
    }
}

エスケープシーケンス

エスケープシーケンスとは、特殊な文字を表すための記法。
Javaでは文字列は"(ダブルクォーテーション)でくくって表現したいが、 そのままでは"(ダブルクォーテーション)を文字列に含めたい場合にうまくいかない。
そんな場合は、\(バックスラッシュ、または円マーク)を付けてあげることで表現できる。
ダブルクォーテーション以外でも、特殊な文字を表す場合には\記号を前に付ける。

Main.java

package app;

public class Main {
    public static void main (String[] args) {
        // System.out.println(""こんにちは""); // エラーになる
        System.out.println("\"こんにちは\"");  // "こんにちは"と表示される
        System.out.println("\\1000");  // \を表示したい場合は、\\と書く
    }
}

標準出力と標準入力

標準出力

今まで「System.out」オブジェクトのprintlnメソッドやprintメソッドを使用して、
文字列や数値の出力結果を見てきました。
printlnやprintによる出力は、「標準出力」と呼ばれます。
一般的に標準出力とは、簡単に言うと画面に対する出力のことです。
Javaでは標準出力の機能はSystemクラスのoutオブジェクト(PrintStreamクラスのオブジェクト)が持っています。
outオブジェクトのメソッドを使用することで、画面に対して文字列や数値などを出力することができます。

標準入力

標準出力とは逆に標準入力というものもあります。
一般的にはキーボードからの入力のことを指します。
今までJavaで標準出力で結果を確認する形で様々なプログラムを見てきましたが、
キーボードからの入力を受け付けて、それによって処理を行いたい場合もあります。
そんな時にJavaでも標準入力の機能が使用できます。
Javaで「System.in」を使用します。
また、Scannerというクラスを使用します。

Main.java

package app;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        String s1 = sc.nextLine();
        System.out.println(s1);
        sc.close();
    }
}

入力

Hello

結果

Hello

VS Code での設定

VS Codeを使用して標準入力のプログラムを動かす場合、
デフォルトの設定ではうまく動きません。
拡張機能の設定の変更が必要になります。

拡張機能
Java Debuggerの設定
consolse
internalConsole ⇒ integratedTerminal
に変更する。

もともとは標準出力の内容はデバッグコンソールに出力される設定だが、
そのままでは、標準入力を受け付けてくれない。
ターミナルに出力されるように設定しておくと、標準入力を受け付けるようになります。


デバッグ機能について

作成するプログラムの規模がある程度大きくなってくると、不具合が発生したときに調査が難しくなります。
そのような場合、デバッグの機能を活用することで調査がしやすくなります。

そもそもデバッグとは何でしょう。
まず、コンピュータの世界では不具合のことをバグといいます。
そしてそのバグを取り除くことをデバッグと言います。

バグとは虫という意味です。
昔は、コンピュータの中に小さな虫が入り込むことで、コンピュータに不具合が起こっていたようです。
そのため、不具合を解消するために、リアルにバグ(虫)を取り除いていたそうです。
その名残でソフトウェアの世界でも、不具合を解消することをデバッグと呼びます。

大抵のプログラミング言語の開発環境ではデバッグの機能が用意されています。
VS CodeJavaソースコードを書いている場合、mainメソッドの上の部分に
Run | Debug
と表示されるかと思います。
ここで「Debug」を実行することでデバッグの機能が利用できます。

VS Codeデバッグを利用する際には、ソースコードの左側の行数の左隣をクリックし丸印を付けます。
この丸印は、「ブレークポイント」と呼ばれます。
ブレークポイントがある状態で「Debug」を実行すると、処理がブレークポイントの行で停止します。
処理が停止したら、その時点で宣言されている変数の値を確認したり、評価された式の値を確認したりすることができます。
処理が停止した場所からは、ステップオーバー(F10)やステップイン(F11)で処理を1行ずつ進めることができます。
ステップオーバーの場合、現在処理中のメソッドの中を1行ずつ処理を進めていくことができます。
ステップインの場合、別のメソッドを呼び出している行では、呼び出したメソッドの中まで処理を辿ることができます。
ブレークポイントは何か所でも付けることができます。
処理が複雑になり、変数の値がどのように変化していくのかソースコードから読み取れなくなってきた場合、
デバッグの機能を活用すると良いでしょう。


コマンドによるコンパイルと実行

TODO