HelloWorld
|
Hello World
|
|
コメント
|
コメント
|
|
リテラル
|
文字型のリテラル
|
|
|
数値のリテラル
|
|
|
文字列のリテラル
|
|
|
エスケープシーケンス
|
\t \n
|
|
ブール値(真理値)
|
true, false
|
|
null
|
null
|
変数・データ型
|
変数宣言・代入
|
int a
|
|
定数(final)
|
final int C = 10
|
|
変数の型推論(var)
|
var a = "あ"
|
演算
|
算術演算・論理演算
|
* / + - & ~ ^
|
|
インクリメント・デクリメント
|
a++, b--
|
|
シフト演算
|
a << 16
|
|
代入演算
|
+=, *=
|
|
( )による演算の優先
|
|
|
比較(関係)演算子
|
a <= 10
|
|
文字列の連結演算
|
"AB" + "CD"
|
型変換
|
数値型の暗黙の型変換
|
|
|
文字列を数値に変換
|
Integer.parseInt()
|
|
文字列に変換
|
Integer.toString()
|
配列
|
配列の宣言・配列要素の参照
|
int[] a = new
|
|
配列の初期化
|
int[] a = {}
|
|
配列変数の代入
|
arr1 = arr2
|
|
配列の長さ
|
arr.length
|
|
多次元配列
|
{{..}, {..}}
|
|
要素数が異なる多次元配列
|
{{..}, {....}}
|
制御構文
|
条件判断(if)
|
if(A)
|
|
条件判断(switch-case)
|
switch(A) case B:
|
|
条件式の論理演算
|
A && B, A || B
|
|
条件演算(三項演算)
|
A? B:C
|
|
繰り返し(while)
|
while(A)
|
|
繰り返し(for)
|
for(A; B; C)
|
|
繰り返し(do-while)
|
do while(A)
|
|
ループの脱出・継続(break、continue)
|
break, continue
|
|
イテレーション(拡張for)
|
for(int a : arr)
|
クラス
|
クラスの定義・生成
|
class C
|
|
フィールド定義
|
cls.fld
|
|
メソッド定義
|
void method()
|
|
フィールドのthis参照
|
this.fld
|
|
メソッドから他のメソッド呼び出し
|
|
|
スコープ
|
|
|
フィールド・メソッドの定義位置
|
|
|
フィールドの初期値
|
int a = 10
|
|
初期化ブロック
|
{int a = 10;}
|
|
引数の参照渡し
|
|
|
インスタンス変数は参照の代入
|
|
|
クラス型のフィールドを定義
|
C1 c
|
|
オブジェクト配列
|
C[] a = new C[n]
|
|
メソッドのオーバーロード
|
|
|
コンストラクタ
|
C()
|
|
コンストラクタのオーバーロード
|
|
|
コンストラクタから他のコンストラクタ呼び出し
|
this()
|
|
アクセス制御
|
private, public, protected
|
|
クラスフィールド(静的フィールド)
|
static int a = 10
|
|
クラスメソッド
|
static int method()
|
|
クラスメソッドでインスタンスを生成(ファクトリメソッド)
|
|
|
クラスフィールドによるシングルトン
|
|
|
定数定義だけのクラス
|
static final
|
|
クラスフィールドの初期化ブロック
|
static { }
|
|
インスタンス生成しないクラス
|
final class C
|
|
クラス内のクラス定義
|
|
|
クラス内のstaticクラス
|
|
|
メソッド内のローカルクラス
|
|
|
可変長引数
|
method(int... v)
|
|
mainメソッド
|
public static void main()
|
継承
|
クラスの継承
|
C extends S
|
|
サブクラスのメソッドからsuperとthisの明示的参照
|
super.x, this.x
|
|
スーパークラスのコンストラクタ呼び出し
|
super()
|
|
継承のアクセス制限
|
|
|
メソッドのオーバライド
|
|
|
ポリモフィズム
|
|
|
スーパークラスとサブクラスの型によりthisを決定
|
|
|
finalによるオーバライドの禁止
|
final int ovmethod()
|
|
フィールドのオーバライド
|
|
|
静的フィールド・メソッドのオーバライド
|
|
|
継承を使わずに他のクラスを取り込む
|
|
抽象クラス
|
抽象クラス
|
abstract class V
|
instanceof
|
インスタンスのクラスの確認(instanceof)
|
c instanceof V
|
インタフェース
|
インタフェース定義・実装クラス定義
|
interface I, class C implements I
|
|
複数インタフェースからの実装クラス
|
class C implements I1,I2
|
|
インタフェース間の継承
|
interface Ix extends I
|
|
インタフェースのstaticメソッド
|
static void ifmethod()
|
|
インタフェースのdefaultメソッド
|
default void ifmethod()
|
|
インタフェース型によるポリモーフィズム
|
|
無名(匿名)クラス
|
無名クラスによるサブクラス実装
|
new C(){ }
|
|
インタフェースの無名実装クラス
|
new I(){ }
|
|
関数型インタフェース
|
@FunctionalInterface
|
|
関数型インタフェースによるラムダ式
|
() -> { }
|
ジェネリックス
|
ジェネリッククラスの定義
|
class G<T> new G<T>()
|
|
ジェネリック型のスーパークラスを指定
|
class G<T extends S>
|
|
ジェネリクスメソッド
|
<T> void gmethod()
|
Stringクラス
|
Stringのメソッド
|
String.valueOf()
|
|
文字列フォーマット
|
String.format()
|
ラッパークラス
|
基本型のラッパークラス
|
Boolean, Character
|
|
ラッパークラスの変換メソッド
|
Double.valueOf(), Integer.parseInt()
|
列挙
|
列挙クラス
|
enum E
|
例外処理
|
例外捕捉
|
try catch(XException e)
|
|
複数種類の例外捕捉
|
|
|
finallyブロック
|
try finally
|
|
非チェック例外の伝搬 throws
|
method() throws XException
|
|
例外のスーパークラスで全ての例外を捕捉
|
catch(Exception e)
|
|
例外クラスの作成
|
class Ex extends Exception
|
|
例外の送出
|
throw new XException()
|
|
例外の再送出
|
|
|
例外のトレースログ表示
|
e.printStackTrace()
|
|
try-with-resource構文
|
try(new C) catch()
|
|
条件判定でアサーション例外
|
assert A : "assertion"
|
Hello World
Hello World
class Hello01 {
public static void main(String[] args) {
System.out.println("Hello Java");
}
}
コメント
コメント
class Comment01 {
public static void main(String[] args) {
System.out.println("Hello Java");
// 行末までコメント
/*
改行を含めたコメント区間
*/
/**
javadocでHTMLドキュメント化できるコメント区間
*/
}
}
リテラル
文字型のリテラル
class Liter01 {
public static void main(String[] args) {
System.out.println('$');
System.out.println('あ');
}
}
文字型のリテラルはシングルクォーテーション「'」で1文字を囲みます。
文字型は1文字のUnicodeの数値データです。
数値のリテラル
class Liter02 {
public static void main(String[] args) {
System.out.println(123); // 整数
System.out.println(012); // 8進数
System.out.println(0x2af); // 16進数
System.out.println(0766); // 8進数(ファイルモード等)
System.out.println(0b0110010); // 2進数
System.out.println(.234); // 浮動小数点数(0.は省略可)
System.out.println(1.2589e-2); // 10のマイナス二乗(=0.012589)
System.out.println(1.2589e3); // 10の三乗(=1258.9)
System.out.println(3.25f); // float型を明示
System.out.println(3.25D); // double型を明示
System.out.println(1000000L); // long型を明示
System.out.println(1_000_000); // アンダーバーで区切り
}
}
先頭が0以外の数字の並びは10進数の整数になります。
先頭が0の数値は8進数になります。12は10進数の12ではないことに注意です。
先頭が0xの数値は16進数です。16進数の10〜15はa〜f、またはA〜Fを使います。
0bから続く0と1の数値は2進数です。
小数点を含む数値は浮動小数点です。「0.」の場合の0は省略できます。浮動小数点は、仮数部と乗数を分けるe±nで表現できます。
数値リテラルにサフィックスをつけることで、型を指定できます。
|
サフィックス
|
float
|
f(F)
|
double
|
d(D)
|
long
|
l(L)
|
桁の区切りを見やすくするために数字の間に任意に「_」を入れることができます。
文字列のリテラル
class Liter03 {
public static void main(String[] args) {
System.out.println("ABCD123");
System.out.println("東京都");
}
}
文字列リテラルはダブルクォーテーション「"」で囲みます。文字列はUnicodeで格納されます。
エスケープシーケンス
class Liter04 {
public static void main(String[] args) {
System.out.print("ABC\tDEF");
System.out.print("GHI\rZ\"\n");
System.out.print("\100");
System.out.print("\u30a2");
}
}
改行やタブ文字のような制御文字を、「\(バックスラッシュ)」に続く1文字との組み合わせによるエスケープシーケンスで表します。
Z"C DEFGHI
@ア
エスケープ文字
|
|
\b
|
バックスペース
|
\t
|
タブ
|
\n
|
改行
|
\r
|
復帰(行頭へ移動)
|
\'
|
'(シングルクォーテーション)
|
\"
|
"(ダブルクォーテーション)
|
\\
|
\(エスケープ文字そのもの)
|
\035
|
3桁の0-7の数値からなる8進数が示すコードの文字
|
\u1340
|
u+4桁の16進数からなるUnicode文字
|
ブール値(真理値)
class Liter05 {
public static void main(String[] args) {
boolean t = true;
boolean f = false;
Object a = null;
}
}
ブール値(真理値)の真・偽はそれぞれ「true」「false」です。
null
class Liter06 {
public static void main(String[] args) {
Object a = null;
String s= null;
}
}
「null」はオブジェクトの参照先がないことを示すリテラルです。
オブジェクトの変数にnullを代入することで、参照するインスタンスがないことを明示します。nullを代入した変数のオブジェクトはガベージコレクトされます。
変数・データ型
変数宣言・代入
class Var01 {
public static void main(String[] args) {
int a; // 変数宣言
a = 100; // 変数への代入
System.out.println(a); // 100
int b = 200; // 変数宣言と同時に初期化
System.out.println(b); // 200
int x, y = 300; // 同じ型をカンマで区切って複数指定
x = b; // 変数から変数への代入
System.out.println(x); // 200
System.out.println(y); // 300
//int z = zz; // 変数宣言より前の照前は不可
int zz = 10;
}
}
使用する変数は、データ型と変数名を宣言する必要があります。
データ型 変数名 = 初期値;
変数宣言と同時に「=」の右辺により初期化できます。
メソッド内では、変数を参照する(使う)前に変数が宣言されている必要があります。同じ型の変数をカンマで区切って複数宣言できます。
変数名(を含む全てのシンボル名)の長さに制限はありません。変数名には英字、数字、アンダーバー「_」、ドル「$」が使用できます。大文字小文字は区別されます。「$」は主に中間処理に使用され、通常は使うことはありません。
変数には「=」の右辺より、変数と同じデータ型のリテラル、変数、演算結果、メソッドの戻り値を代入します。
変数名 = リテラル;
変数名 = 変数名;
変数名 = 演算結果;
変数名 = メソッドの戻り値;
数値型同士では、値の精度が落ちない場合に限り、暗黙の型変換により異なる型へ代入できます。
データ型は次の種類があります。
基本データ型
|
char
|
文字型(Unicodeの1文字だが整数型の一種)
|
|
byte
|
8ビット(1バイト)整数型
|
|
short
|
16ビット整数型
|
|
int
|
32ビット整数型(OSなどの環境に依存せず32ビット)
|
|
long
|
64ビット整数型
|
|
float
|
浮動小数点型(32ビット)
|
|
double
|
浮動小数点型(64ビット)
|
|
boolean
|
ブール型(真理値型)
|
オブジェクト型
|
String
|
文字列型(文字列クラス)
|
|
クラス名
|
クラス定義したオブジェクト型
|
32ビット範囲内の整数リテラルはint型、浮動小数点のリテラルはdouble型となります。
class Var02 {
public static void main(String[] args) {
byte bn = 0x44;
short si = 32767;
int i = 100;
long li = 10000000;
float f = 1.2F;
double d = 0.3;
boolean bl = true;
char c = 'あ';
String s = "埼玉県";
System.out.println(bn);
System.out.println(si);
System.out.println(i);
System.out.println(li);
System.out.println(f);
System.out.println(d);
System.out.println(bl);
System.out.println(c);
System.out.println(s);
}
}
文字列型Stringは、変数へ直接文字列リテラルを代入(初期化)できます。
String s = "ABCDEFG";
変数宣言時に初期化しない場合は、以下に示すデータ型のデフォルト値で暗黙に初期化されます。
宣言データ型
|
デフォルト値
|
数値
|
0、0.0
|
文字型
|
\u0000
|
真理値型(boolean)
|
false
|
文字列、配列、オブジェクトの参照
|
null
|
定数(final)
class Var03 {
final static int CNST1 = 100;
public static void main(String[] args) {
final int CNST2 = 200;
System.out.println(CNST1); // 100
// CNST2 = 300; Error
System.out.println(CNST2); // 200
}
}
変数宣言をfinalで修飾することにより、変数を「定数」にします。
finalによる定数の変数宣言では、必ず初期化(初期値を代入)しなければなりません。宣言以降はその変数に再代入できなくなります(変更操作はエラーになる)。
定数の変数名はすべて大文字にするという一般的な傾向があります(規則ではない)。
変数の型推論(var)
class Var04 {
public static void main(String[] args) {
var s = "ABCあいう";
System.out.println(s); // ABCあいう
// s = 100; バリアント型ではない
s = "DEFG"; // これはOK
System.out.println(s); // DEFG
}
}
変数宣言の初期化する右辺のデータ型が明らかな場合に限り、データ型をvarとして宣言し、varが示す具体的データ型のコンパイラの推論により決定させることができます。
var宣言は必ず初期化を伴います。
次のように続けて書くことはできません。
var a = 10, b = 20; // bは不可
varは決してバリアント型ではないので、varで宣言した変数に初期化時以外のデータ型での再代入はできません。
演算
算術演算・論理演算
class Calc01 {
public static void main(String[] args) {
System.out.println(3/2); // 1 --> 整数の割り算は整数の商
System.out.println(3.0/2); // 1.5 --> 浮動小数点として
System.out.println(7%4); // 3 モジュロ(剰余)
System.out.println(5*2); // 10
System.out.println(-3+1); // -2 マイナスの単行演算(+の単行演算も可)
byte si = 0b0011;
System.out.println(si & 0b1010); // 2 = 00000010 AND
System.out.println(si | 0b1010); // 11 = 00001011 OR
System.out.println(si ^ 0b1010); // 9 = 00001001 XOR
System.out.println(~si); // -4 = 11111100 NOT
System.out.println(true | false); // true
}
}
数値型データは演算子により算術演算、論理演算を行います。
演算子
|
演算
|
+ -
|
加算・減算(及び各単項演算)
|
* /
|
乗算・除算(整数の除算は整数の商を求める)
|
%
|
剰余(モジュロ)
|
& |
|
論理積(AND)・論理和(OR)
|
^
|
排他的論理和(XOR)
|
~
|
否定(NOT)
|
インクリメント・デクリメント
class Calc02 {
public static void main(String[] args) {
int a = 5;
a++;
System.out.println(a); // 6
int b = --a;
System.out.println(b); // 5
b = a--;
System.out.println(b); // 5 aをbに代入してからaを-1するのでbは5のまま
System.out.println(a); // 4 aは-1されている
}
}
「x++」は整数変数xを+1加算し「x--」は-1減算します。
「++」と「--」は変数の前後どちらにも付けることができますが、次のように式の評価順番が異なります。
a=b++;
|
⇒
|
a=b;
b=b+1;
|
a=++b;
|
⇒
|
b=b+1;
a=b;
|
シフト演算
class Calc03 {
public static void main(String[] args) {
int a = 0x00000001;
System.out.println(a << 2);
// 4 =0x4 = 0000 0100
a = 0x00000020;
System.out.println(a << 2);
// 128 =0x80 = 1000 0000
a = 0x80000000;
System.out.println(a >> 1);
// -1073741824 =0xc0000000 = 1100 0000 0000 0000 0000 0000 0000 0000
a = 0x80000000;
System.out.println(a >>> 1);
// 1073741824 =0xc0000000 = 0100 0000 0000 0000 0000 0000 0000 0000
}
}
シフト演算子は数値を左右へビットシフトさせます。「<<」は左シフトで「>>」は右シフトです。
byteやshort型をシフト演算するとき、暗黙に32ビットのint型に変換してから行います。
「>>」のときの最上位ビット(MSB)は符号ビットが保存されます。「>>>」では、最上位の符号ビットの有無を無視してMSBに0が入ります。
代入演算
class Calc04 {
public static void main(String[] args) {
int a = 10;
a -= 3;
a *=2;
System.out.println(a); // 14
}
}
変数の演算とその変数自身への代入は代入演算子でまとめることができます。
例えば「a=a+n」は「a+=n」のようにできます。
|
+=
|
-=
|
*=
|
/=
|
%=
|
代入演算子の種類
|
&=
|
^=
|
|=
|
|
|
|
<<=
|
>>=
|
>>>=
|
|
|
( )による演算の優先
class Calc05 {
public static void main(String[] args) {
int a = 10 + 20 * 2;
System.out.println(a); // 50
a = (10 + 20) * 2;
System.out.println(a); // 60
}
}
数学式と同様に、()で囲んだ式の評価を優先します。
式中の演算子の優先順位が同じ場合は、左から順に評価されます。
比較(関係)演算子
class Calc06 {
public static void main(String[] args) {
System.out.println(5 == 2); // false
System.out.println(5 > 2); // true
System.out.println(5 >= 5); // true
System.out.println(5 != 3); // true
String s1 = new String("ABC");
String s2 = new String("ABC");
String s3 = s1;
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
System.out.println(s1.equals(s2)); // true
String sa = "DEF";
String sb = "DEF";
System.out.println(sa == sb); // true
}
}
比較演算(関係演算)は、数値を比較した結果を「ブール値」で返します。オブジェクト型の比較は、両方のインスタンスが同一であれば結果をtrueとします。
比較演算子
|
|
==
|
一致
|
!=
|
不一致
|
< >
|
大小比較
|
<= >=
|
大小比較か一致
|
文字列はStringクラスのオブジェクトなので、文字列の内容が同じでもインスタンスが異なれば一致になりません。そのため、文字列の内容が同じでも不一致になる場合があります。文字列の内容の一致を調べるならば、「==」ではなくStringクラスのequals()メソッドで比較すべきです。
文字列の連結演算
class Calc07 {
public static void main(String[] args) {
System.out.println("ABC" + "DEF"); // ABCDEF
System.out.println("ABC" + 123 + "DEF"); // ABC123DEF
String s = "GHI";
s += "JKL";
System.out.println(s); // GHIJKL
}
}
文字列は加算「+」演算で連結します。文字列のリテラルと文字列型の変数間で連結できます。
文字列と数値の加算では、数値が暗黙に文字列に変換され文字列として連結されます。
型変換
数値の型変換
class Cast01 {
public static void main(String[] args) {
int n;
// n = 0.3; // 精度が下がる方への暗黙の変換はエラーになる
n = (int)2.7; // 明示的な型キャスト
System.out.println(n); // 2 小数点以下が切り捨てられる
double d;
d = 100; // 高い精度への暗黙の型変換
System.out.println(d); // 100.0
System.out.println(d * 3); // 300.0 式の項の中で最も高精度な型に暗黙に変換される
d = 5 / 2; // 整数の除算は浮動小数点への暗黙の型変換はされない
System.out.println(d); // 2.0
d = (double)5/(double)2;
System.out.println(d); // 2.5
}
}
変数の代入やメソッドの引数渡し時などで起こる数値型の型変換は、型の精度がより高い方への変換は暗黙に行われます。精度が下がる側への変換は明示的に「(型)」によりキャストしなければなりません。
<キャスト>
(型名)数値
boolean型は、0や1のような数値型には変換できません。
文字列を数値に変換
class Cast02 {
public static void main(String[] args) {
int i = Integer.parseInt("100");
System.out.println(i);
long l = Long.parseLong("10000000000");
System.out.println(l);
double d = Double.parseDouble("2.4");
System.out.println(d);
int ih = Integer.parseInt("ab", 16);
System.out.println(ih); // 171
int io = Integer.parseInt("064", 8);
System.out.println(io); // 52
}
}
数値型のラッパークラスのparse〜()メソッドにより、数値を表現した文字列を数値に変換できます。
parse〜()メソッドの第二引数にオプションで基数(進数)を指定できます。文字列は指定された基数に従って解釈されます。
数値に変換できない文字列を指定した場合、NumberFormatException例外がスローされます。
class Cast03 {
public static void main(String[] args) {
int i = Integer.valueOf("200").intValue();
System.out.println(i);
int ih = Integer.valueOf("ab", 16).intValue();
System.out.println(ih);
}
}
parse〜()メソッドを使う以外に、数値型のラッパークラスのvalueOf()メソッドにより文字列を数値に変換し、intValue()、doubleValue()メソッド等でint型やdouble型に変換する方法があります。
文字列に変換
class Cast04 {
public static void main(String[] args) {
String si = Integer.toString(100);
System.out.println(si);
si = Integer.toString(171, 16);
System.out.println(si); // ab
String sf = Double.toString(1.23);
System.out.println(sf);
}
}
Objectクラスを継承するサブクラスがオーバライドしているtoString()メソッドにより、そのオブジェクトを適切な文字列に変換できます。
どのような文字列に変換されるかは、各クラスのtoString()メソッドの実装によります。数値型の各ラッパークラスにはそれぞれにtoString()メソッドがあります。
class Cast05 {
public static void main(String[] args) {
String si = String.valueOf(100);
System.out.println(si);
String sf = String.valueOf(1.23);
System.out.println(sf);
}
}
StringクラスのvalueOf()メソッドにより、int、doubleなどの数値型を文字列に変換できます。
配列
配列の宣言・配列要素の参照
class Array01 {
public static void main(String[] args) {
int[] ar;
ar = new int[3];
ar[0] = 10;
ar[1] = 20;
ar[2] = 30;
System.out.println(ar[1]); // 20
int[] b = new int[3];
}
}
変数宣言のデータ型に[]を加えることで、そのデータ型の配列を宣言できます。
配列宣言により配列を参照するための変数を用意します。このとき配列サイズは指定しません。newにより、配列サイズの指定に従ったデータ領域を確保した配列データが生成されます。生成した配列は、配列として宣言した変数に代入できます。
<配列の宣言/生成>
データ型[] 配列変数;
配列変数 = new データ型[サイズ];
データ型[] 配列変数 = new データ型[サイズ];
配列の要素は配列変数を[]の中に整数の添字(インデックス)を指定して参照します。添字には、整数リテラルに限らず整数変数や式を指定できます。先頭の添字は0です。長さ5で生成した配列ならば、添字の末尾は4となります。
<配列要素にアクセス>
配列変数[添字]
配列変数宣言の[]の位置は、C言語的「int ar[] 」のような書式が許容されています。
配列の初期化
class Array02 {
public static void main(String[] args) {
int[] c = new int[]{11, 22, 33, 44};
System.out.println(c[1]); // 22
int[] d = {100, 200, 300};
System.out.println(d[2]); // 300
}
}
newによる配列の生成に続けて{〜}の中にカンマで区切って初期値を並べることで、配列要素の生成と同時に任意の値で初期化できます。その場合は配列要素数は指定しません。配列の長さは初期化データの数で決定します。
初期値を指定しない場合は、各要素はデータ型のデフォルト初期値で初期化されます。
<配列要素の初期化>
new データ型[]{初期値, 初期値, ...};
newを省略して次のように配列を初期化できます。要素は配列変数のデータ型でなければなりません。
配列変数 = {初期値, 初期値, ...};
初期値には、リテラルに限らず変数や式の結果も指定できます。
int a = 22;
int[] c = new int[]{11, a, 33, 44};
配列変数の代入
class Array03 {
public static void main(String[] args) {
int[] ar1 = {10, 20, 30};
int[] ar2;
ar2 = ar1;
ar2[0] = 200;
System.out.println(ar1[0]); // 200
}
}
配列変数は参照型であり、配列のオブジェクトを参照します。配列変数への代入は、配列の参照先の変更です。
上の例は、配列変数同士の代入が配列データのコピーではないことを示しています。
配列の長さ
class Array04 {
public static void main(String[] args) {
int[] ar = {10, 20, 30};
System.out.println(ar.length); // 3 配列の長さ
}
}
配列の長さ(格納可能な要素数)は、lengthフィールドで得ることができます。
配列変数.length
多次元配列
class Array05 {
public static void main(String[] args) {
int[][] arr;
arr = new int[2][3];
arr[0][2] = 10;
arr[1][2] = 100;
System.out.println(arr[0][2]); // 10
System.out.println(arr[1][2]); // 100
String[][] sarr = {{"A", "abc"}, {"D", "def"}, {"G", "ghi"}};
System.out.println(sarr[1][0]); // D
System.out.println(sarr[2][1]); // ghi
}
}
配列は1次元の数列以上の平方、立方のような多次元配列を生成できます。多次元配列の変数は、[]を次元数だけ繰り返して宣言します。
<多次元配列の宣言/生成>
データ型[][] 配列変数;
配列変数 = new データ型[サイズ][サイズ];
多次元配列の初期化は、{〜}の中に下の次元の{〜}を入れ子にします。
<多次元配列要素の初期化>
配列変数 = { {初期値, 初期値, ...}, {初期値, ...}, ...};
class Array06 {
public static void main(String[] args) {
int[][] arr = { {10, 20, 30}, {100, 200, 300}};
System.out.println(arr.length); // 2
System.out.println(arr[0].length); // 3
}
}
配列要素の長さは、それぞれの次元の配列のlengthフィールドにより得られます。
要素数が異なる多次元配列
class Array07 {
public static void main(String[] args) {
int[][] arr = new int[2][];
arr[0] = new int[2];
arr[1] = new int[3];
arr[0][0] = 10;
arr[0][1] = 20;
arr[1][0] = 100;
arr[1][1] = 200;
arr[1][2] = 300;
System.out.println(arr[1][1]); // 200
String[][] sarr = {{"A", "B", "C", "D"}, {"AB", "CD"}, {"EFG", "HIJ", "KLM"}};
System.out.println(sarr[2][0]); // EGF
}
}
多次元配列の各次元の要素数は、そろっていなくてもかまいません。各次元のそれぞれの配列の初期化の長さで確保されます。
同一次元の他の配列要素より短い配列の、存在しない部分の添字で参照することはできません。
制御構文
条件判断(if)
class CtIf01 {
public static void main(String[] args) {
int a = 10;
if (a < 20)
System.out.println(a); // 10
if (a < 20) {
a -= 20;
System.out.println(a); // -10
}
a = 10;
if (a > 10) {
// 実行されない
} else {
System.out.println(a); // 10
}
a = 10;
if (a > 10) {
// 実行されない
} else if (a == 10) {
System.out.println(a); // 10
} else {
// 実行されない
}
}
}
if文は式の評価結果の真(true)と偽(false)の場合によって処理を分岐させます。
式の評価が真の場合ifブロックが実行され、偽の場合はelseのブロックが実行されます。処理が1文の場合はブロック{〜}を省略できます。
elseは省略できます。elseの場合で別の判断をelse if()のように続けることができます。また、else ifは任意に連続できます。
if (式A) {
Aがtrueのとき
} else if (式B) {
Aがfalse、Bがtrueのとき
} else if (式C) {
ABがfalse、Cがtrueのとき
} else {
すべてfalseのとき
}
式の結果は真理値型(boolean)でなければなりません。0やnullはfalseと等価ではありません。
条件判断(switch-case)
class CtSwitch01 {
public static void main(String[] args) {
int a;
a = 20;
switch (a) {
case 10:
System.out.println("case 10");
break;
case 20:
System.out.println("case 20"); // case 20
break;
case 30:
System.out.println("case 30");
break;
default:
System.out.println("default " + a);
break;
}
switch (a) {
case 10:
case 20:
System.out.println("case 10 or 20"); // case 10 or 20
break;
case 30:
System.out.println("case 30");
break;
default:
System.out.println("default " + a);
break;
}
}
}
switch文は、式の評価結果がcase文で指定する値に一致する、それぞれの場合に処理を分岐させます。
swtich文の式の結果は数値、文字、文字列(String)、列挙(enum)の何れかでなければなりません。
caseにはリテラル(定数)、リテラルだけの演算式、finalによる定数の変数が指定できます。変数を含む式、nullは評価できません。switchの結果がnullの場合はNullPointerException例外となります。
case 20 + n:
|
NG
|
case 20 + 10:
|
OK(定数のみの演算なら可能)
|
case n:
|
NG(ただしfinalによる定数変数ならOK)
|
caseの処理の複数の式をブロックで囲む必要はありません。caseの処理はbreak文によりswitchブロックから脱出します。breakを行わない場合は、次のcaseの評価を続けて実行します。caseの処理を省略した場合は、なにも実行せずに次のcaseの評価を行います。複数のcaseをまとめると、それらのOR条件の処理でまとめることができます。
defaultは、全てのcaseに一致しなかった場合に実行します。defaultは必須ではなく省略できます。defaultはcase文のどこでも配置できますが、一般には末尾に置きます。
switch(式) {
case A:
式の結果がA
...
break;
case B:
case C:
式の結果がBかC
...
break;
case D:
式の結果がD
(breakしないで)
case E:
式の結果がDかE
break;
default:
上の何れでもない
}
(各breakはここにジャンプ)
文字列はオブジェクト型でありながら文字列リテラルをcase文に適用できます。この場合の文字列の一致判断には、Stringクラスのequals()メソッドが内部的に使われます。
class CtSwitch02 {
public static void main(String[] args) {
char c = 'あ';
switch (c) {
case 'あ':
System.out.println("case あ"); // case あ
break;
case 'い':
System.out.println("case い");
break;
default:
System.out.println("default " + c);
break;
}
String s = "あいう";
switch (s) {
case "あいう":
System.out.println("case あいう"); // case あいう
break;
case "かきく":
System.out.println("case かきく");
break;
default:
System.out.println("default " + c);
break;
}
}
}
条件式の論理演算
class CtAndOr01 {
public static void main(String[] args) {
int a, b;
a = 10;
if (a >= 10 && a < 20) {
System.out.println(a); // 10
}
if ((a > 20) || (a == 10)) {
System.out.println(a); // 10
}
if (!(a > 10)) {
System.out.println(a); // 10
}
}
}
ifなど制御構文の評価式を、論理演算子により論理積(AND)論理和(OR)で論理演算ができます。
制御構文の評価式は真理値(boolean)の演算なので通常の論理演算子で演算できます。
&, |
|
論理積(AND)・論理和(OR)
|
^
|
排他的論理和(XOR)
|
~
|
否定(NOT)
|
「&&」と「||」は、左辺から評価した時点で真偽が確定した場合は右辺の評価を行いません。それに対し「&」と「|」は左辺と右辺を両方評価します。
条件演算(三項演算)
class CtCond01 {
public static void main(String[] args) {
int a = 10;
System.out.println(a > 10? "TRUE":"FALSE"); // FALSE
}
}
条件演算(三項演算)は、条件の結果により演算する式を選択します。
条件式の評価が真(true)と偽(false)の何れかの場合の式を評価します。
<条件演算>
条件式? 真の場合の式 : 偽の場合の式
繰り返し(while)
class CtWhile01 {
public static void main(String[] args) {
int i = 0;
while (i < 4) {
System.out.println(i); // 0 .. 3
i++;
}
}
}
while文は、式の評価結果が真(true)である間ブロックの処理を繰り返します。
繰り返す処理が1文のみの場合はブロック{〜}を省略できます。
while(式) {
式がtrueの間の処理
...
}
式の結果は真理値型(boolean)でなければなりません。
繰り返し(for)
class CtFor01 {
public static void main(String[] args) {
int i;
for (i = 0; i < 4; i++)
System.out.println(i); // 0 .. 3
System.out.println(i); // 4
for (int j = 0; j < 4; j++) {
System.out.println(j * 10);
System.out.println(j);
}
// System.out.println(j); // エラー(jはforブロック後には存在しない)
int k = 0;
for (; k < 4; ) {
System.out.println(k);
k++;
}
int m, n;
for (m = 0, n = 5; m < 5; m++, n++) {
System.out.println(m);
System.out.println(n);
}
}
}
for文は、whileと同様に式の評価結果が真(true)である間ブロックの処理を繰り返します。
for文には繰り返しの判断式と共に、初期化など初回に実行する式と、次の繰り返しの直前に実行する式を記述できます。
for (初期化式; 条件式; 次の繰り返し前の式) {
繰り返す処理
...
}
for文をwhile文で次のように置き換えることができます。
<forと等価なwhile>
初期化式
while (条件式) {
繰り返す処理
...
次の繰り返し前の式
}
for文の中の初期化、条件、次の繰り返しの部分は任意に省略できます。
for(;;) {
繰り返す処理(永久ループ)
...
}
for文の初期化式と繰り返し前の式は「,」で区切って複数の式を並べることができます。それらは左から順番に式を実行します。
for (初期化式, 初期化式; 条件式; 次の繰り返し前の式) {
初期化式のその場で変数宣言してもかまいません。その変数はforブロックのスコープとなります。
for (int i = 0; i < 10; i++) {
j = i;
...
繰り返し(do-while)
class CtDoWhile01 {
public static void main(String[] args) {
int i = 0;
do {
System.out.println(i); // 0 ... 9
i++;
} while(i < 10);
}
}
do-while文は、while文と同様に式の評価結果が真(true)である間ブロックの処理を繰り返します。
条件式は処理ブロック終わりのwhileで評価します。従ってdoに入る時点で条件が既に偽(false)であっても、最低1回はブロック内を実行することになります。
ループの脱出・継続(break、continue)
class CtBreak01 {
public static void main(String[] args) {
int i = 0;
while (true) {
if (i == 4)
break;
System.out.println(i); // 0 1 2 3
i++;
}
i = 0;
while (true) {
System.out.println(i); // 0 1 2 3
i++;
if (i < 4)
continue;
break;
}
}
}
break文は、for、while、do-while構文で繰り返すループ処理を途中で脱出します。
また、case文の処理からswitchブロックを脱出します。脱出後は繰り返す処理ブロックの次から実行を継続します。
continue文は、繰り返し構文の処理内のループを中断し、繰り返しの最初に戻ります。
class CtBreak02 {
public static void main(String[] args) {
int i = 0;
jumplabel: for (i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
if (i == 3)
break jumplabel;
System.out.println(i + "-" + j);
}
}
jumplabel: for (i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
if (i % 3 == 0)
continue jumplabel;
System.out.println(i + "-" + j);
}
}
}
}
breakとcontinueが脱出または継続するループは、原則として現在繰り返している区間が対象です。ただし、breakとcontinueにラベルをつけることで、多重ループの内側から外側へ飛び越えた脱出または継続が可能です。
0-0
0-1
0-2
1-0
1-1
1-2
2-0
2-1
2-2
1-0
1-1
1-2
2-0
2-1
2-2
4-0
4-1
4-2
ラベルの位置は、forやwhileの真横でなく直前の行でもかまいません。
JumpLabel:
for (int j = 0; j < 3; j++) {
...
イテレーション(拡張for)
class CtForeach01 {
public static void main(String[] args) {
int[] ar = {10, 20, 30};
for (int i : ar) {
System.out.println(i);
}
String[] sarr = {"あ", "い", "ABC"};
for (String s : sarr) {
System.out.println(s);
}
}
}
10
20
30
あ
い
ABC
拡張for文は、配列やコレクションの要素を先頭から順番に取り出して繰り返します。全ての要素について繰り返したらループを終了します。
拡張for文で要素を走査できる対象は、iterableインタフェースを実装したイテレーション可能なクラスのオブジェクトです。
要素を参照する変数は拡張for文の中で宣言します。配列等の要素を先頭から順番に変数に代入しながら処理ブロックを繰り返します。
for (変数宣言 : 配列等) {
繰り返す処理
...
}
クラス
クラスの定義・生成
class C1 {
int a;
double d;
}
class Cls01 {
public static void main(String[] args) {
C1 c;
c = new C1();
System.out.println(c.a); // 0(既定値)
}
}
classにより新たなクラスを定義します。
定義するクラスに任意の名前を付けることができます。クラスは、オブジェクトが保持するデータを「フィールド(メンバ変数)」として、振る舞いを「メソッド」として定義します。全てのデータやメソッドは必ずクラスに属して定義されなければならず、データのみやメソッドのみがグローバルスコープに存在するようなことはできません。
.javaソースファイルをコンパイルすると、ソース中に定義されているクラス名の「クラス名.class」ファイルが作成されます。ひとつのソースファイルに複数クラスが定義されていれば、それぞれのクラス名で.classファイルが作られます。mainメソッドが存在するクラスを実行すると、mainメソッドからプログラムの実行が始まります。
アクセス指定をpublicとするクラスは、ソースファイル内に1つのみとします。publicを指定したクラスを定義した場合、その.javaファイルは「クラス名.java」でなければなりません。
クラス定義は、インスタンスが持つデータをフィールドとして変数宣言の書式で定義し、メソッドを、引数・戻り値と処理内容を定義します。フィールドのみのクラスやメソッドのみのクラスを定義してかまいません。
<クラス定義>
class クラス名 {
フィールド(変数宣言)
フィールド(変数宣言)
...
メソッド定義
メソッド定義
...
}
定義したクラスはユーザ定義型として変数宣言できます。
<クラス型で変数宣言>
クラス名 変数名;
クラスの変数宣言により、そのクラスの実体「インスタンス」を参照する変数が用意されます。変数を宣言した時点では、クラスのインスタンスは存在していません。newによりクラスの実体(オブジェクト)であるインスタンスを生成します。
<インスタンス生成>
new クラス名()
クラスの変数宣言とインスタンスの生成と初期化をまとめることができます。
<クラス変数宣言/インスタンス生成/初期化>
クラス名 変数名 = new クラス名()
フィールド定義
class C1 {
int a;
double d;
}
class Cls02 {
public static void main(String[] args) {
C1 c;
c = new C1();
System.out.println(c.a); // 0(既定値)
c.a = 10;
c.d = 2.34;
System.out.println(c.a); // 10
System.out.println(c.d); // 2.34
C1 cc = new C1();
cc.a = 20;
System.out.println(cc.a); // 20
System.out.println(cc.d); // 0.0 デフォルトで0相当の値で初期化されている
}
}
フィールドは変数宣言の書式で定義します。
<フィールド定義>
class クラス名 {
int a;
String s;
}
変数が参照するクラスのインスタンスから、フィールドを「.」で区切って参照します。
<フィールドの参照>
変数.フィールド名
メソッド定義
class C1 {
int a;
void set(int x) {
a = x;
}
int get() {
return a;
}
void show() {
System.out.println(a);
return;
}
}
class Cls03 {
public static void main(String[] args) {
C1 c = new C1();
c.set(100);
System.out.println(c.get()); // 100
}
}
メソッドは、次のように引数・戻り値と処理を定義します。
<メソッド定義>
戻り値型 メソッド名(引数宣言, 引数宣言, ...) {
変数宣言・処理
...
return 戻り値;
}
引数は変数宣言と同様です。引数が不要な場合は省略できます。戻り値を返さないメソッドは戻り値の型に「void」型を宣言します。voidはメソッド定義のための特殊な型であり、void型のフィールドや変数を宣言することはできません。
メソッドのブロック内には、メソッド内で使用する変数(ローカル変数)の宣言と処理を実装します。メソッドのブロック内では、引数とフィールドを参照できます。
return文で返す戻り値は、値や式の結果が定義で示す戻り値型でなければなりません。
戻り値を返さない(voidを返す)メソッドではreturn文を省略できます。あるいは戻り値を省略して「return」として任意の場所でメソッドから返ることができます。
<戻り値>
return 戻り値;
return;
フィールドのthis参照
class C1 {
int a;
void set(int a) {
this.a = a;
}
}
class Cls04 {
public static void main(String[] args) {
C1 c = new C1();
c.set(10);
System.out.println(c.a);
}
}
クラスのフィールドはメソッドで変数として参照できます。
自身のインスタンスのフィールドを参照する場合、インスタンスを「this」に置き換えて参照できます。
this.フィールド変数名
メソッドから他のメソッド呼び出し
class C1 {
int a;
void set(int x) {
a = x;
}
int get() {
return a;
}
void show() {
System.out.println(get());
System.out.println(this.get());
}
}
class Cls05 {
public static void main(String[] args) {
C1 c = new C1();
c.set(10);
c.show(); // 10
}
}
クラス定義の中のメソッドは、互いに他のメソッドを呼び出すことができます。
thisにより呼び出すメソッドが自身のインスタンスのメソッドであることを明示できます。
this.メソッド名(引数)
スコープ
class C1 {
int a;
void set(int x) {
if (x < 100) {
int b = 100; // ブロックスコープ
x += b;
}
a = x;
}
void show() {
int a = 200; // ローカル変数
System.out.println(this.a); // 105
System.out.println(a); // 200
}
}
class Cls06 {
public static void main(String[] args) {
C1 c = new C1();
c.set(5);
System.out.println(c.a); // 105
c.show();
}
}
メソッドの中で宣言したローカル変数は、メソッドの中のスコープとなります。
クラス内の同名の変数名は現在のブロックのスコープが優先されます。ローカル変数とフィールド名が同じ変数名のとき、メソッド内ではローカル変数の方の参照となります。その場合にフィールドの方を参照するのであれば、thisによりインスタンスのフィールド参照であることを明示する必要があります。
メソッド定義を含めた{〜}で囲むブロックは、それぞれのローカルスコープを持ちます。ブロック内の変数宣言は、そのブロックのスコープになります。
ブロックはメソッドや制御構文等に限らず、任意に設置できます。
メソッド() {
{
ブロック
}
{
ブロック
}
}
クラス定義の外側のグローバルスコープは存在しません。
フィールド・メソッドの定義位置
class C1 {
void show() {
System.out.println(a);
System.out.println(get());
}
int a;
int get() {
return a;
}
}
class Cls07 {
public static void main(String[] args) {
C1 c = new C1();
c.a = 100;
c.show();
}
}
フィールドやメソッドを定義する順序はクラス定義の中で自由です。
メソッドが参照するフィールドがメソッド定義より前に書かれている必要はありません。メソッドも同様に、メソッドの中から呼び出す別のメソッドがそれより前に定義されている必要はありません。一方で、メソッドの処理中などブロック内の変数は、それが参照される前に宣言されていなければなりません。
フィールドの初期値
class C1 {
int a = 100;
final int b = 200;
}
class Cls08 {
public static void main(String[] args) {
C1 c = new C1();
System.out.println(c.a); // 100
//c.b = 20; // エラー
System.out.println(c.b); // 200
}
}
フィールドには、変数宣言と同様に初期値を与えることで、newによるインスタンス生成時の初期値を設定できます。
フィールドを初期化しない場合は、各データ型のデフォルトの初期値で自動的に初期化されます。
フィールドの変数をfinalで宣言し初期値を与えると、その変数は定数となります。finalで定数化したフィールドへの初期化以降の変更操作はエラーとなります。
初期化ブロック
class C1 {
int a;
{
a = 100;
}
}
class Cls09 {
public static void main(String[] args) {
C1 c = new C1();
System.out.println(c.a); // 100
}
}
クラス定義内のブロック{〜}は「初期化ブロック」としてインスタンス生成時に、コンストラクタより先に実行されます。
クラスのインスタンスが生成されるときのフィールドを初期化する順番は次のとおりです。
(1) クラスフィールドの初期値設定
(2) static初期化ブロックによるクラスフィールドの初期化
(3) インスタンスフィールドの初期値設定
(4) 初期化ブロックでインスタンスフィールドの初期化
(5) コンストラクタでフィールドに代入
引数の参照渡し
class C1 {
void chg(String s) {
s = "ABCD";
}
void arrchg(int[] ax) {
ax[1] = 123;
}
int[] arrrep(int[] ax) {
ax = new int[] {11, 22, 33};
return ax;
}
}
class Cls10 {
public static void main(String[] args) {
C1 c = new C1();
String s = "XYZ";
c.chg(s);
System.out.println(s); // XYZ
int[] a = new int[]{10, 20, 30};
c.arrchg(a);
System.out.println(a[1]); // 123 20が123に書き換えられている
int[] b = new int[]{100, 200, 300};
int[] r = c.arrrep(b);
System.out.println(b[0]); // 100
System.out.println(r[0]); // 11
}
}
メソッドの引数が参照型の場合、引数には呼び出し元のオブジェクトの参照が渡ります。
メソッドの処理内で、呼び出し元オブジェクトを参照している引数に変更操作を行うと、呼び出し側が引数に与えたデータが変更されます。
上の例のchg()メソッドとarrrep()メソッドは、何れも参照型である文字列と配列を引数で受け取りますが、メソッドの中で引数へ新しい別のオブジェクトを代入しており、その時点で呼び出し元が与えたオブジェクトへの参照が切れることになるので、その結果呼び出し元は変化しません。
インスタンス変数は参照の代入
class C1 {
int a;
double d;
}
class Cls11 {
public static void main(String[] args) {
C1 ca = new C1();
C1 cb;
cb = ca;
ca.a = 100;
System.out.println(cb.a); // 100
cb.d = 2.34;
System.out.println(ca.d); // 2.34
}
}
クラス型の変数は、そのクラスのインスタンスの参照を代入したものであり、クラスの変数間の代入は同一インスタンスの参照の代入となります。クラスの変数間でフィールドはコピーされません。
クラス型のフィールドを定義
class C1 {
int a;
}
class C2 {
C1 cc;
void set(C1 x) {
cc = x;
}
}
class Cls12 {
public static void main(String[] args) {
C1 c1 = new C1();
c1.a = 100;
C2 c2 = new C2();
c2.set(c1);
System.out.println(c2.cc.a); // 100
c1.a = 200;
System.out.println(c2.cc.a); // 200
}
}
フィールドは基本型に限らず他のクラス型のフィールドを定義できます。
クラスのフィールドは、そのクラスのインスタンスの参照です。上の例では、C2のccはC1のインスタンスのc1を参照しており、c1.aの変更はc2.cc.aを変更することと同じになります。
オブジェクト配列
class C1 {
int a;
double d;
}
class Cls13 {
public static void main(String[] args) {
C1[] carr;
carr = new C1[3];
for (int i = 0; i < carr.length; i++) {
carr[i] = new C1();
carr[i].a = 10 + i;
}
for (int i = 0; i < carr.length; i++) {
System.out.println(carr[i].a); // 10 11 12
}
}
}
クラスは基本型と同様に配列変数を宣言できます。
宣言した配列変数に対しnewで配列を生成しますが、その時点ではまだ配列の生成であり、要素は何のインスタンスも参照していません。
<オブジェクト配列>
クラス名[] 配列変数
配列変数 = new クラス名[n]
配列変数[0] = インスタンス
配列変数[2] = new コンストラクタ()
,,,
メソッドのオーバーロード
class C1 {
int a;
double d;
void set(int x) {
a = x;
}
int set(double x) {
d = x;
return 0;
}
}
class ClsOvl01 {
public static void main(String[] args) {
C1 c = new C1();
c.set(10);
c.set(2.34);
System.out.println(c.a); // 10
System.out.println(c.d); // 2.34
}
}
メソッドのオーバーロードは、引数が異なる同名のメソッド定義します。
戻り値だけが異なるオーバーロードはできません。呼び出すメソッドは引数の差異によりコンパイル時に決定します。
コンストラクタ
class C1 {
int a;
C1(int x) {
a = x;
}
}
class ClsCons01 {
public static void main(String[] args) {
C1 c = new C1(10);
System.out.println(c.a); // 10
}
}
コンストラクタはクラス名と同名とします。戻り値は定義しません。
コンストラクタはメソッドと同様に引数を定義し、フィールドを参照できます。
コンストラクタはnewによるインスタンス生成時に呼び出します。引数のないコンストラクタでも()は省略できません。
new クラス名()
new クラス名(引数)
コンストラクタを定義しないクラスは、引数と処理内容がないデフォルトコンストラクタが暗黙に定義されます。
コンストラクタのオーバーロード
class C1 {
int a;
double d;
C1(int x) {
a = x;
}
C1(double x) {
d = x;
}
}
class ClsCons02 {
public static void main(String[] args) {
C1 ca = new C1(10);
C1 cb = new C1(2.34);
System.out.println(ca.a); // 10
System.out.println(cb.d); // 2.34
}
}
コンストラクタをオーバーロード定義できます。newによる呼び出し時の引数により選択されます。
コンストラクタから他のコンストラクタ呼び出し
class C1 {
int a;
double d;
C1(int x) {
a = x;
}
C1(int x, double y) {
this(x);
d = y;
}
}
class ClsCons03 {
public static void main(String[] args) {
C1 c = new C1(10, 2.34);
System.out.println(c.a); // 10
System.out.println(c.d); // 2.34
}
}
コンストラクタをオーバーロード定義した場合、コンストラクタから別のコンストラクタをthis()として呼び出すことができます。
this(引数)
アクセス制御
class C1 {
private int a;
public int b;
void set(int a) {
this.a = a;
}
private int get() {
return a;
}
}
class ClsAcc01 {
public static void main(String[] args) {
C1 c = new C1();
//c.a = 100; // privateなのでエラーになる
//c.get();
c.set(100);
c.b = 200;
}
}
各アクセス修飾子は、クラス(インスタンス)、フィールド、メソッドのそれぞれが参照(使用)できる範囲を指定します。
アクセス修飾子が指定する範囲外で生成、参照、呼び出し操作を行うとエラーになります。
アクセス修飾子
|
クラス内
|
自パッケージ内
|
継承クラス
|
継承クラス
(他パッケージ)
|
他パッケージ
|
private
|
可
|
不可
|
不可
|
不可
|
不可
|
なし(デフォルト)
|
可
|
可
|
可
|
不可
|
不可
|
protected
|
可
|
可
|
可
|
可
|
不可
|
public
|
可
|
可
|
可
|
可
|
可
|
privateが指定されたものは、クラス定義内のコンストラクタやメソッドの中でのみ使用できます。
アクセス修飾子を指定しないデフォルトでは、パッケージ内ならば全てアクセス可能です。
各アクセス修飾子の適用対象は以下の通りです。
アクセス修飾子
|
クラス
|
フィールド
|
メソッド
|
private
|
☓
|
○
|
○
|
protected
|
☓
|
○
|
○
|
public
|
○
|
○
|
○
|
クラスフィールド(静的フィールド)
class C1 {
static int s = 10;
int a;
C1(int x) {
a = x;
s += x;
}
}
class ClsStatic01 {
public static void main(String[] args) {
C1.s += 5;
System.out.println(C1.s); // 15
C1 ca = new C1(3);
C1 cb = new C1(5);
C1 cc = new C1(2);
System.out.println(C1.s); // 25
System.out.println(ca.s); // 25
System.out.println(cb.s); // 25
}
}
static修飾子を指定するフィールドは「クラスフィールド」となり静的なフィールドになります。
クラスフィールドはインスタンスごとに生成されません。クラスの定義により固定的に存在するので、インスタンスを生成しなくても参照できます。クラスフィールドは、どのインスタンスの変数から参照しても常に唯一のフィールドを参照することになります。
<クラスフィールドの参照>
クラス名.クラスフィールド
インスタンス.クラスフィールド
クラスフィールドはメソッドの中から参照できます。
クラスメソッド
class C1 {
static int s = 0;
int a;
C1(int x) {
a = x;
s += x;
}
static int gets() {
return s;
}
}
class ClsStatic02 {
public static void main(String[] args) {
C1 ca = new C1(3);
C1 cb = new C1(5);
C1 cc = new C1(2);
System.out.println(C1.gets()); // 10
System.out.println(ca.gets()); // 10
System.out.println(cb.gets()); // 10
}
}
static修飾子を指定するメソッドは「クラスメソッド」となり静的なメソッドになります。
クラスの定義により固定的に存在するので、インスタンスを生成しなくても呼び出すことができます。
<クラスメソッドの呼び出し>
クラス名.クラスメソッド(引数)
インスタンス.クラスメソッド(引数)
インスタンスメソッドの中からクラスメソッドを呼び出すことができます。逆に、クラスメソッド内ではインスタンス変数やメソッドを使うことはできません。クラスメソッド内ではクラスフィールドと他のクラスメソッドしか使えません。
クラスメソッドでインスタンスを生成(ファクトリメソッド)
class C1 {
int a;
private C1() {
a = 100;
}
static C1 getins() {
return new C1();
}
}
class ClsStatic03 {
public static void main(String[] args) {
C1 c = C1.getins();
System.out.println(c.a); // 100
}
}
クラスメソッドの中で、そのクラスのインスタンスを生成できます。
自身のクラスのインスタンスを生成して返すクラスメソッドのことをファクトリメソッドと呼びます。
クラスフィールドによるシングルトン
class C1 {
private static C1 singleton = new C1();
int x;
private C1() {
x = 100;
}
static C1 getins() {
return singleton;
}
}
class ClsStatic04 {
public static void main(String[] args) {
C1 ca = C1.getins();
ca.x += 10;
System.out.println(ca.x); // 110
C1 cb = C1.getins();
cb.x += 10;
System.out.println(ca.x); // 120
System.out.println(cb.x); // 120
}
}
クラスのインスタンスを、そのクラス自身のフィールドに生成することで、単一インスタンスのみが存在し続ける「シングルトン」にできます。
クラスフィールドを自身のクラスで定義し、その初期化としてnewで自身のインスタンスを生成します。コンストラクタをprivateに指定することで、インスタンスの生成を禁止します。クラスフィールドに生成した唯一のインスタンスの参照を取得するためのメソッドを用意します。
定数定義だけのクラス
class C1 {
static final int MAX = 100;
static final int MIN = 10;
static final String INFO = "Const max min";
}
class ClsStatic05 {
public static void main(String[] args) {
System.out.println(C1.MAX); // 100
System.out.println(C1.MIN); // 10
System.out.println(C1.INFO); // Const max min
}
}
クラスフィールドにfinalを指定し変更禁止にすることで、クラス名を名前空間とした定数を定義できます。
このようなフィールドはクラスメソッドであっても初期値から変更できません。
クラスフィールドの初期化ブロック
class C1 {
static int x;
static {
x = 100;
}
}
class ClsStatic06 {
public static void main(String[] args) {
System.out.println(C1.x); // 100
}
}
staticの初期化ブロックは、インスタンスフィールドの初期化ブロックとは別に、クラスフィールドの初期化ができます。
static {
クラスフィールドの初期化
}
クラスフィールドの初期化ブロックを含めたクラスの初期化順序は次のとおりです。
(1) クラスフィールドの初期値設定
(2) static初期化ブロックによるクラスフィールドの初期化
(3) インスタンスフィールドの初期値設定
(4) 初期化ブロックでインスタンスフィールドの初期化
(5) コンストラクタでフィールドに代入
インスタンス生成しないクラス
final class C1 {
private C1(){ }
static void show(int a) {
System.out.println(a);
}
static int add(int x, int a) {
return x + a;
}
}
class ClsStatic07 {
public static void main(String[] args) {
System.out.println(C1.add(10, 5));
C1.show(100); // 15
// 100
}
}
クラス定義にfinalを指定すると、そのクラスはインスタンスが生成できなくなります。
通常そのようなクラスは、フィールド、メソッドを全てstaticとして定義します。クラスを名前空間とした変数の参照とメソッドの呼び出しになります。コンストラクタはnewで呼び出せないようにprivateとしなければなりません。
クラス内のクラス定義
class C1 {
int a = 100;
class I1 {
int a = 20;
void show() {
System.out.println(a); // 20
System.out.println(this.a); // 20
System.out.println(C1.this.a); // 100
}
}
void show() {
I1 i = new I1();
i.show();
System.out.println(a); // 100
System.out.println(i.a); // 20
System.out.println(this.a); // 100
}
}
class ClsCls01 {
public static void main(String[] args) {
C1 c = new C1();
C1.I1 i = c.new I1();
//C1.I1 i = (new C1()).new I1(); // 上の2行をまとめた場合
i.show();
c.show();
}
}
クラス定義の中でクラスを定義できます。
内部のクラスから外側のクラスのフィールドやメソッドを使用できます。内部定義するクラスの参照とインスタンスの生成は、外側のクラスに関連付けられます。
外クラス.内クラス
外クラスインスタンス.new 内クラス()
クラス内のstaticクラス
class C1 {
int a = 100;
static class I1 {
static int a = 20;
void show() {
System.out.println(a); // 20
//System.out.println(C1.a); // エラー
}
}
void show() {
I1 i = new I1();
i.show(); // 20
System.out.println(a); // 100
System.out.println(i.a); // 20
System.out.println(this.a); // 100
}
}
class ClsCls02 {
public static void main(String[] args) {
C1 c = new C1();
C1.I1 i = new C1.I1();
i.show();
c.show();
}
}
クラス定義の中でstaticクラスを定義できます。
staticで定義した内部クラスは、外側のクラスのインスタンスに関連しない、外側のクラスの名前空間で定義した独立したクラスになります。
外クラス.内クラス
new 外クラス.内クラス()
外側のクラスのメソッドから、内側のstaticクラスのインスタンスを生成できます。
メソッド内のローカルクラス
class ClsCls03 {
public static void main(String[] args) {
String msg = "Local";
class C1 {
void show() {
System.out.println(msg); // Local
}
}
C1 c = new C1();
c.show();
}
}
クラスの中のメソッドやコンストラクタの中でクラスを定義できます。
メソッド内で定義したクラスは、そのメソッドの処理中に定義からインスタンスの生成までを完結します。ローカルクラスの定義では、メソッドの内部に限らずメソッドを定義しているクラスのフィールドを参照できます。
可変長引数
class C1 {
void set(int... vx) {
System.out.println("Length=" + vx.length);
for (int x: vx)
System.out.println(x);
}
void setadd(int n, int... vx) {
for (int x: vx)
System.out.println(n + x);
}
void setvobj(Object... vo) {
for (Object o: vo)
System.out.println(o);
}
}
class ClsVarg01 {
public static void main(String[] args) {
C1 c = new C1();
c.set(10, 20, 30);
c.set(1, 3, 5, 7, 9);
c.set();
c.setadd(100, 10, 20, 30, 40);
c.setvobj(100, "ABC", 3.5);
}
}
メソッドの引数は可変長で指定可能です。
可変長引数は、引数の変数型の後に「...」をつけて定義します。可変の引数が配列のように格納されます。格納される数は配列と同様にlengthで取得できます。
メソッド(型... 引数名)
引数名.length
Length=3
10
20
30
Length=5
1
3
5
7
9
Length=0
110
120
130
140
100
ABC
3.5
可変長引数の前に固定引数を設定できます。ただし可変長引数の後に固定引数は置けません。可変長引数はメソッドの中に複数配置できません。
可変長引数は引数宣言したデータ型の引数に限定されますが、スーパークラスを指定すれば、その派生するサブクラスの可変長引数が取得できます。
class C1 {
void setbyarr(int[] ax) {
for (int x: ax)
System.out.println(x);
}
}
class ClsVarg02 {
public static void main(String[] args) {
C1 c = new C1();
c.setbyarr(new int[]{0, 5, 10});
}
}
可変長引数と同等なことを配列で実装できます。その場合は呼び出し側で引数に与える配列を生成する必要があります。
mainメソッド
class ClsMain01 {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
mainメソッドは実行するプログラムにひとつだけ定義されている必要があります。Javaは、実行時に指定するクラスからmainメソッドを探し実行のエントリとします。
<mainメソッド定義>
public static void main(String[]) {
引数の文字列配列は実行時のコマンドライン引数が格納されます。
$ java ClsMain01 abc def 123
abc
def
123
継承
クラスの継承
class S1 {
int s;
int gets() {
return s;
}
}
class C1 extends S1 {
int a;
double d;
void shows() {
System.out.println(gets());
}
}
class ClsExt01 {
public static void main(String[] args) {
C1 c = new C1();
c.a = 10;
c.s = 20;
c.d = 2.34;
System.out.println(c.a); // 10
System.out.println(c.s); // 20
System.out.println(c.d); // 2.34
System.out.println(c.gets()); // 20
c.shows(); // 20
}
}
クラス定義をスーパークラスとして継承するサブクラスを定義できます。
<継承の定義>
class サブクラス extends スーパークラス
サブクラスはスーパークラスのフィールドとメソッドを継承します。サブクラスが定義するメソッド定義の中でスーパークラスのフィールドの参照とメソッドを呼び出すことができます。
クラスの継承では、複数のスーパークラスからなる多重継承はできません。
サブクラスのメソッドからsuperとthisの明示的参照
class S1 {
int s;
int a;
int geta() {
return a;
}
}
class C1 extends S1 {
int a;
void seta(int x, int y) {
this.a = x;
super.a = y;
}
void sets(int x) {
this.s = x;
}
void show() {
System.out.println(super.geta());
}
}
class ClsExt02 {
public static void main(String[] args) {
C1 c = new C1();
c.seta(10, 20);
c.sets(30);
System.out.println(c.a); // 10
System.out.println(c.s); // 30
c.show(); // 20
}
}
サブクラスのメソッド内で使うフィールドやメソッドがスーパークラスのものを指していることをsuper.により明示できます。
super.スーパークラスのフィールド
super.スーパークラスのメソッド()
thisは継承元のスーパークラスも含めた自身のインスタンスを示します。
スーパークラスのコンストラクタ呼び出し
class S1 {
int s;
S1() {
s = 20;
}
S1(int x) {
s = x;
}
}
class C1 extends S1 {
int a;
double d;
C1(int x, int y) {
super(x);
a = y;
}
}
class ClsExt03 {
public static void main(String[] args) {
C1 c = new C1(10, 200);
System.out.println(c.a); // 200
System.out.println(c.s); // 10
}
}
サブクラス内のsuper()は、スーパークラスのコンストラクタを呼び出します。
super()の呼び出しは、サブクラスのコンストラクタの先頭で行わなければなりません。
サブクラスのコンストラクタから明示的なsuper()の呼び出しがない場合は、暗黙にスーパークラスのデフォルトコンストラクタが呼び出されます。
継承のアクセス制限
class S1 {
int a;
private int b;
public int c;
private int geta() {
return a;
}
}
class C1 extends S1 {
void set(int x, int y, int z) {
a = x;
//b = y; // privateなのでエラー
c = z;
}
void show() {
System.out.println(a); // 100
//System.out.println(super.geta()); // privateなのでエラー
//System.out.println(b); // privateなのでエラー
System.out.println(c); // 300
}
}
class ClsExt04 {
public static void main(String[] args) {
S1 s = new S1();
s.a = 10;
//s.b = 20; // Error privateなので
s.c = 30;
//System.out.println(s.geta()); // privateなのでエラー
C1 c = new C1();
c.set(10, 20, 30);
c.a = 100;
//c.b = 200; // privateなのでエラー
c.c = 300;
c.show();
}
}
スーパークラスのprivateでアクセス指定されたフィールドとメソッドには、サブクラスでは使用できません。
スーパークラスでアクセス指定のない(デフォルトの)フィールドやメソッドは、同一パッケージに属するサブクラスが継承した場合に限り使用できます。スーパークラスでpublicにアクセス指定したフィールドやメソッドは、パッケージの内外に関わらず継承したサブクラスで使用できます。
メソッドのオーバライド
class S1 {
int a;
void show() {
System.out.println("super " + a);
}
int get() {
return a;
}
}
class C1 extends S1 {
void show() {
System.out.println("override " + a);
super.show();
}
//private int get() { // エラー 弱いアクセス制限でオーバーライド
public int get() {
return a;
}
}
class ClsExt05 {
public static void main(String[] args) {
C1 c = new C1();
c.a = 100;
c.show(); // override 100
// super 100
}
}
スーパークラスと同じメソッドをサブクラスで定義した場合、サブクラスのメソッドのオーバーライドになります。
サブクラスのインスタンスは、オーバーライドしたサブクラス側のメソッドを呼び出します。
オーバライドメソッドのアクセス指定は、スーパークラスよりも制限する方には指定できません。スーパークラスのprivateのメソッドはオーバーライドできません。
public > protected > 指定なし > private
ポリモフィズム
class S1 {
int a;
void show() {
System.out.println("super " + a);
}
}
class C1 extends S1 {
int b;
int get() {
return a;
}
void show() {
System.out.println("override " + a);
}
}
class ClsExt06 {
public static void main(String[] args) {
S1 c = new C1();
c.a = 100;
//c.b = 200; // Error
c.show(); // override 100
//c.get(); // Error
C1 e = (C1)c;
System.out.println(e.get()); // 100
}
}
サブクラスのインスタンスをスーパークラス型で宣言した変数に代入すると、その変数はスーパークラスとしての参照となるため、サブクラスしか定義していないフィールドやメソッドは参照できなくなります。その一方で、スーパークラスのメソッドをサブクラスでオーバーライドしている場合は、サブクラス側が呼び出されます。
スーパークラスをサブクラスでキャストすると、サブクラスの定義で参照できます。
class S1 {
int a;
void show() {
System.out.println("super " + a);
}
}
class C1 extends S1 {
void show() {
System.out.println("override C1 " + a);
}
}
class C2 extends S1 {
void show() {
System.out.println("override C2 " + a);
}
}
class ClsExt07 {
public static void main(String[] args) {
S1 s[] = new S1[2];
s[0] = new C1();
s[1] = new C2();
for (int i = 0; i < s.length; i++)
s[i].a = 10 + i;
for (int i = 0; i < s.length; i++)
s[i].show(); // override C1 10
// override C1 11
}
}
スーパークラスの変数で、それぞれのサブクラスのオーバライドしたメソッドを呼び出すことにより、同じメソッド呼び出し方で、個々のサブクラスの特有の振る舞いで実行させることができます(ポリモーフィズム)。
スーパークラスとサブクラスの型によりthisを決定
class S1 {
int a;
void show() {
System.out.println("super " + a);
}
}
class C1 extends S1 {
int a;
void show() {
super.show(); // super 200
System.out.println("override " + a); // override 100
}
}
class ClsExt08 {
public static void main(String[] args) {
C1 c = new C1();
S1 s = c;
c.a = 100;
s.a = 200;
c.show(); // super 200
// override 100
}
}
サブクラスのインスタンスをスーパークラス型で参照する場合と、サブクラス型で参照する場合でインスタンスが参照する対象が切り替わります。
finalによるオーバライドの禁止
class S1 {
int a;
final void show() {
System.out.println("super " + a);
}
}
class C1 extends S1 {
/* finalによりオーバライドはエラーになる
void show() {
System.out.println("override " + a);
}
*/
}
class ClsExt09 {
public static void main(String[] args) {
C1 c = new C1();
c.show();
}
}
スーパークラスのメソッドにfinalを指定することにより、そのメソッドのオーバーライドを禁止できます。
その場合に、サブクラスがオーバーライドを実装するとエラーになります。
フィールドのオーバライド
class S1 {
int a = 100;
}
class C1 extends S1 {
double a;
void show() {
System.out.println("super " + super.a);
System.out.println("this " + a);
}
}
class ClsExt10 {
public static void main(String[] args) {
C1 c = new C1();
c.a = 1.23;
c.show(); //super 100
//this 1.23
}
}
スーパークラスと同名のフィールドをサブクラスで定義した場合は、スーパークラス側が隠蔽されます。
型が違っていても名前が同じならばサブクラスによるオーバーライドが成立します。
静的フィールド・メソッドのオーバライド
class S1 {
static int a = 100;
static void show() {
System.out.println("show static " + a);
}
}
class C1 extends S1 {
static int a = 200;
static void show() {
System.out.println("show sub " + a);
}
}
class ClsExt11 {
public static void main(String[] args) {
S1.show(); //show static 100
C1.show(); //show sub 200
S1 c = new C1();
c.show(); //show static 100
((C1)c).show(); //show sub 200
}
}
スーパークラスのstaticフィールドとメソッドをサブクラスでオーバライドできます。
どちらが呼び出されるかは、呼び出し時のクラス名かインスタンスのクラス型によって決定します。
継承を使わずに他のクラスを取り込む
class C0 {
void show(String s) {
System.out.println(s);
}
}
class C1 {
C0 c0 = new C0();
void show(String s) {
c0.show(s);
}
}
class ClsExt12 {
public static void main(String[] args) {
C1 c = new C1();
c.show("ABC");
}
}
クラスのインスタンスをフィールドに持つことにより、継承を使わずに他のクラスの機能を取り込むことができます。
抽象クラス
抽象クラスの継承
abstract class V1 {
int a;
abstract int get();
}
class C1 extends V1 {
int get() {
return a;
}
}
class ClsAbs01 {
public static void main(String[] args) {
V1 c = new C1();
c.a = 100;
System.out.println(c.get()); // 100
}
}
メソッドのシグニチャー(戻り値・メソッド名・引数リスト)のみ定義し、実装を記述しない「抽象メソッド」を定義できます。
抽象メソッドにはabstractを指定します。また抽象メソッドを含むクラス定義は「抽象クラス」となり、abstractを指定する必要があります。abstractの指定と同時に暗黙にアクセスがpublicに設定されます。
抽象クラスは直接newでインスタンスを生成できません。必ず継承される必要があり、サブクラスのインスタンスを生成します。抽象クラスを継承するサブクラスは、全ての抽象メソッドをオーバライドして実装しなければなりません。
抽象クラスには、抽象クラスと実装のある通常のメソッド(具象メソッド)を混在させてもかまいません。
abstract class V1 {
int a;
abstract int get();
abstract void set(int x);
}
abstract class C1 extends V1 {
int get() {
return a;
}
void show() {
System.out.println(get());
}
}
class C2 extends C1 {
void set(int x) {
a = x;
}
}
class ClsAbs02 {
public static void main(String[] args) {
C1 c = new C2();
c.set(100);
c.show(); // 100
}
}
抽象クラスを継承したサブクラスが抽象メソッドをオーバライドしない場合は、そのサブクラスも抽象クラスとなり、他のクラスに継承される必要があります。
インスタンスのクラスの確認(instanceof)
abstract class V1 {
int a;
abstract int get();
}
class C1 extends V1 {
int get() {
return a;
}
}
class C2 extends V1 {
int get() {
return a;
}
}
class Insof01 {
public static void main(String[] args) {
V1 c1 = new C1();
V1 c2 = new C2();
System.out.println(c1 instanceof C1); // true
System.out.println(c1 instanceof C2); // false
System.out.println(c2 instanceof C1); // false
System.out.println(c2 instanceof C2); // true
System.out.println(c1 instanceof V1); // true
System.out.println(c2 instanceof V1); // true
}
}
インスタンスがそのクラスから生成されたものかどうかをinstanceofの式で判定できます。
インスタンス instanceof クラス名
インスタンスが指定するクラスから生成されている、式はtrueとなります。クラスが継承元のスーパークラスであってもtrueと判定されます。インスタンスが指定クラスの性質を継承しているかどうかを判断できます。
インタフェース
インタフェース定義・実装クラス定義
interface I1 {
int max = 10;
int get();
void set(int x);
}
class C1 implements I1 {
int a;
public int get() { return a; }
public void set(int x) { a = (x > max)? max : x; }
}
class Iface01 {
public static void main(String[] args) {
C1 c = new C1();
c.set(8);
System.out.println(c.get()); // 8
c.set(12);
System.out.println(c.get()); // 10
}
}
インタフェースは、静的フィールドの定数定義と抽象メソッドの定義のみで定義します。
インタフェースは、実装のない定義のみを記述した特殊な抽象クラスであり、抽象クラスと同様にインタフェースから直接のインスタンスは生成できません。
インタフェース定義
interface {
フィールド = 初期値;
メソッド定義
}
メソッド定義は、
public abstract
が暗黙に指定されます。
インタフェースをimplementsで指定し実装クラスを定義します。実装クラスはインタフェースの抽象メソッド定義に従い実装(オーバライド)します。実装クラスは、インタフェース定義の全てのメソッドを実装しなければインスタンスを生成できません。
インタフェースのメソッドは暗黙にpublicとされます。そのため実装クラスでオーバライドするメソッドもpublicでなければなりません。
実装クラス
class 実装クラス implements インタフェース名, インタフェース名,... {
メソッドの実装
...
}
インタフェースに定義するフィールドは、
public static final
が暗黙に指定されます。インタフェースはインスタンスフィールドを持つことはできません。staticにより静的フィールドとなるので、必ず初期化が伴います。
複数インタフェースからの実装クラス
interface I1 {
int max = 10;
int get();
void set(int x);
}
interface I2 {
void show();
}
class C1 implements I1, I2 {
int a;
public int get() { return a; }
public void set(int x) { a = (x > max)? max : x; }
public void show() { System.out.println(a); };
}
class Iface02 {
public static void main(String[] args) {
C1 c = new C1();
c.set(8);
c.show(); // 8
c.set(12);
c.show(); // 10
}
}
複数のインタフェースから実装クラスを定義する多重継承ができます。
インタフェースの実装クラスに限り、多重継承が許可されています。原則として、implementsで指定する複数のインタフェースのメソッドを全て実装する必要があります。
インタフェースの継承
interface I1 {
int max = 10;
int get();
void set(int x);
}
interface Iex extends I1 {
int get();
void show();
}
class C1 implements Iex {
int a;
public int get() { return a; }
public void set(int x) { a = (x > max)? max : x; }
public void show() { System.out.println(a); };
}
class Iface03 {
public static void main(String[] args) {
C1 c = new C1();
c.set(8);
c.show(); // 8
c.set(12);
c.show(); // 10
}
}
インタフェースを他のインタフェース定義へextendsにより継承できます。
継承先のインタフェースは継承元の定義フィールドと抽象メソッド定義を引き継ぎます。拡張先で同じメソッドを重複して定義する場合は、引数と戻り値は同じでなければなりません。
インタフェースのstaticメソッド
interface I1 {
int max = 10;
int get();
void set(int x);
static void myname() {
System.out.println("Interface I1");
}
}
class C1 implements I1 {
int a;
public int get() { return a; }
public void set(int x) { a = (x > max)? max : x; }
}
class Iface04 {
public static void main(String[] args) {
C1 c = new C1();
c.set(8);
System.out.println(c.get()); // 8
I1.myname(); // Interface I1
}
}
インタフェースでstaticを指定するメソッドは静的メソッドとなります。
インタフェースの静的メソッドは、実装クラスで再定義しません。クラスの静的メソッドと同様に、インスタンスから呼び出すことはできません。
インタフェースの静的メソッドは暗黙にpublicとなりますが、明示的にprivateを指定できます。privateな静的メソッドは、そのインタフェースの定義内でのみ使用できます。
インタフェースのdefaultメソッド
interface I1 {
int max = 10;
int get();
void set(int x);
default void show() {
System.out.println("max " + max);
}
}
class C1 implements I1 {
int a;
public int get() { return a; }
public void set(int x) { a = (x > max)? max : x; }
}
class Iface05 {
public static void main(String[] args) {
C1 c = new C1();
c.set(8);
System.out.println(c.get()); // 8
c.show(); // max 10
}
}
インタフェース定義でdefaultを指定したメソッドには、例外として実装することができます。
インタフェースの実装クラスでは、原則として抽象メソッドを全て実装する必要がありますが、例外としてインタフェースでdefaultで定義したメソッドは実装クラスでの実装を省略できます。また、defaultのメソッドを実装クラスでオーバーライドしてかまいません。オーバーライドされない場合は、インタフェースのdefaultの実装が適用されます。
インタフェースのdefaultメソッドを、実装クラスから呼び出す場合は次のようにします。
インタフェース名.super.デフォルトメソッド()
interface I1 {
default void show() {
System.out.println("a");
}
}
class C1 implements I1 {
public void show() {
I1.super.show();
}
}
多重継承したインタフェースのデフォルトのメソッドを使うときに、複数のインタフェース間で、同じデフォルトメソッドが定義され重複した場合は、コンパイラが関連付けを決定できないのでエラーとします。同名でも引数か戻り値が異なっていれば衝突にはなりません。
インタフェース型によるポリモーフィズム
interface I1 {
void show();
}
class C1 implements I1 {
public void show() {
System.out.println("implements C1");
}
}
class C2 implements I1 {
public void show() {
System.out.println("implements C2");
}
}
class Iface06 {
public static void main(String[] args) {
I1[] c = new I1[2];
c[0] = new C1();
c[1] = new C2();
for (int i = 0; i < c.length; i++)
c[i].show(); // implements C1
// implements C2
}
}
スーパークラス型の変数からサブクラスのインスタンスを呼び出すポリモーフィズムと同様に、インタフェース型の変数に実装クラスのインスタンスを生成して、インタフェース型の変数から異なる実装クラスの同一メソッドを呼び出すことができます。
無名(匿名)クラス
無名クラスによるサブクラス実装
class S1 {
String msg = "";
S1(String m) {
msg = m;
}
void show() {
System.out.println(msg);
}
}
class Anon01 {
public static void main(String[] args) {
S1 c = new S1("Anony") {
void show() {
System.out.println("new-" + msg);
}
};
c.show(); // new-Anony
}
}
スーパークラスを継承するサブクラスを、newの時点で実装してすぐにインスタンスを生成できます。その場合、そのインスタンスのサブクラスは名前がない(付ける必要がない)無名クラスとなります。
<無名クラスの即時定義>
new スーパークラス (引数) {
サブクラスの実装
}:
インタフェースの無名実装クラス
interface I1 {
void show();
}
class Anon02 {
public static void main(String[] args) {
I1 c = new I1() {
public void show() {
System.out.println("Anony Interface");
}
};
c.show(); // Anony Interface
}
}
インタフェースの実装クラスを、newの時点で実装してすぐにインスタンスを生成できます。その場合、そのインスタンスの実装クラスは名前がない(付ける必要がない)無名クラスとなります。
関数型インタフェース
@FunctionalInterface
interface FI {
void show(String s);
}
class C1 {
void msg(String s, FI f) {
f.show(s);
}
}
class FuncIf01 {
static void print1(String msg) {
System.out.printf("[%s]\n", msg);
}
static void print2(String msg) {
System.out.printf("(%s)\n", msg);
}
public static void main(String[] args) {
C1 c = new C1();
c.msg("ABC", FuncIf01::print1);
c.msg("ABC", FuncIf01::print2);
}
}
[ABC]
(ABC)
抽象メソッドが一つだけ定義されているインタフェースは「関数型インタフェース」となります。
関数型インタフェースの実装クラスを定義しなくても、そこに唯一定義しているメソッドと同じ戻り値と引数型で定義した別のメソッドを、インタフェースから実装したクラスのインスタンスのように扱うことができます。
メソッドの戻り値と引数が同一ならば(メソッド名が異なっていても)、暗黙にインタフェースの抽象メソッドをオーバライドしたもの呼び出すことができます。
関数型インタフェースは、抽象メソッドが単一であればdefaultとstaticの定義が含まれていてもかまいません。関数型インタフェースを定義するときに、@FunctionalInterfaceアノテーションを付けることで、コンパイラが関数型インタフェースの要件を満たしているかどうかをチェックしてくれます。
関数型インタフェースによるラムダ式
@FunctionalInterface
interface FI {
void show(String s);
}
class FuncIf02 {
public static void main(String[] args) {
FI f1 = new FI() {
@Override
public void print(String s) {
System.out.printf("[%s]\n", s);
}
};
f1.show("ABC");
FI f2 = (String s) -> { System.out.printf("[%s]\n", s); };
f2.show("DEF");
}
}
[ABC]
[DEF]
関数型インタフェースで定義する抽象メソッドのオーバライドを「ラムダ式」で記述できます。
<ラムダ式の定義>
(メソッド引数) -> { メソッドの実装 }
() -> { メソッドの実装 }
上の例のf2がラムダ式であり、f1はそのラムダ式と同等なことを無名クラスで実装しています。関数型インタフェースで抽象メソッドをオーバライドする方法よりも、ラムダ式ならば実装クラスもメソッド名も省略して、引数と処理だけで実装できます。
ラムダ式は次の省略ができます。
ラムダ式の引数型が関数型インタフェース定義で明らかな場合、型名を省略して引数名のみに省略できます。
引数が一つの場合は()が省略できます。
メソッドの実装が1行の場合はブロック{}が省略できます。
ラムダ式の実装がreturn文の1行のみの場合、ブロックとreturnが省略できます。
<ラムダ式の省略>
FI f2 = (s) -> { System.out.printf("[%s]\n", s); };
FI f2 = (s) -> System.out.printf("[%s]\n", s);
FI f2 = s -> System.out.printf("[%s]\n", s);
() -> { return x; }; ⇒ () -> x;
ジェネリックス
ジェネリッククラスの定義
class G1<T> {
T a;
G1(T x) {
a = x;
}
T get() {
return a;
}
}
class Gene01 {
public static void main(String[] args) {
G1<String> gs = new G1<String>("ABCDE");
String s = gs.get();
System.out.println(s); // ABCDE
G1<Integer> gi = new G1<Integer>(100);
int i = gi.get();
System.out.println(i); // 100
}
}
ジェネリクスにより、クラス定義中に使用する具体的なデータ型を決定せずにクラスを定義できます。
<ジェネリクスな定義>
class クラス名<型パラメータ, ...> {
...
クラス内では型パラメータの名前を仮のデータ型として使用します。ジェネリクスクラスを使用するときに、<〜>の中に具体的なデータ型名を与えることで実際のクラス定義が確定します。型パラメータにはクラス名や数値型名などを任意に設定できます。
<ジェネリクスクラスの具象化>
クラス名<型名>
定義時の型パラメータの名前は任意ですが、データの性質がわかるような大文字の1文字にすることが多く、よく見られるものに次があります。
T
|
一般的な型(Type)
|
E
|
配列やリストの要素の型(Element)
|
K
|
ハッシュ・辞書などのキーとして(Key)
|
V
|
ハッシュ・辞書などの値として(Value)
|
class G1<T, V> {
T a;
V b;
G1(T x, V y) {
a = x;
b = y;
}
V get(T x) {
if (x == a)
return b;
else
return null;
}
}
class Gene02 {
public static void main(String[] args) {
G1<String, Integer> g = new G1<String, Integer>("ABC", 100);
try {
int i = g.get("ABC");
System.out.println(i); // 100
} catch (Exception e) {
e.printStackTrace();
}
}
}
型パラメータはカンマで複数設定できます。
ジェネリック型のスーパークラスを指定
class G1<T extends Number> {
T a;
G1(T x) {
a = x;
}
T get() {
return a;
}
}
class Gene03 {
public static void main(String[] args) {
G1<Integer> gi = new G1<Integer>(100);
int i = gi.get();
System.out.println(i); // 100
//G1<string> gs = new G1<String>("ABC"); // エラー
}
}
ジェネリクスの型パラメータが継承するスーパークラスをextendsで指定することにより、型パラメータに与えるものを、そのスーパークラスのサブクラスに限定することができます。
ジェネリクスメソッド
class G1 {
<T> void show(T x) {
System.out.println(x.getClass());
}
}
class Gene04 {
public static void main(String[] args) {
G1 g = new G1();
g.<String>show("ABCDE");
g.<Double>show(1.23);
}
}
class java.lang.String
class java.lang.Double
メソッド定義に対しジェネリック型を適用できます。
型パラメータはメソッド内のみで使用できます。
<ジェネリクスクラスの具象化>
<型パラメータ, ...> 戻り値 メソッド(
...
Stringクラス
Stringのメソッド
class Str01
{
public static void main(String[] args)
{
String s1 = new String("XYZ東西"); // コンストラクタ
String s2 = "XYZ東西"; // リテラルの代入
System.out.println(String.valueOf(200)); // 数値の文字列変換
System.out.println("ABCDE".length()); // 5 文字数
System.out.println("あいう".length()); // 3
System.out.println("あいう".codePointCount(0, "あいう".length())); // 3
System.out.println("ABC".equals("ABD")); // false 文字列の比較
System.out.println("ABC".compareTo("ABD")); // -1
System.out.println("".isEmpty()); // true
System.out.println(" \t\n".isBlank()); // true 空白文字のみの場合true
System.out.println("ABCDEABCDE".indexOf("CD")); // 2 部分文字列の位置検索
System.out.println("ABCDEABCDE".lastIndexOf("CD")); // 7
System.out.println("ABCDEABCDE".indexOf("CD", 3)); // 7
System.out.println("ABCDEABCDE".indexOf("XY")); // -1 結果なし
System.out.println("ABCDE".contains("CD")); // true 部分文字列が含まれているか
System.out.println("ABCDE".startsWith("ABC")); // true
System.out.println("ABCDE".endsWith("CDE")); // true
System.out.println("ABCDE".substring(2, 4)); // CD 部分文字列
System.out.println("ABCDE".substring(2)); // CDE
System.out.println("ABCDE".charAt(3)); // D 指定位置の文字
System.out.println("[" + " ABC ".strip() + "]"); // [ABC] 前後空白の削除
System.out.println("[" + " ABC ".stripLeading() + "]"); // [ABC ]
System.out.println("[" + " ABC ".stripTrailing() + "]"); // [ ABC]
for (String s : "AB CD EF".split(" "))
System.out.println(s); // AB CD DF
for (String s : "AB CD,EF".split("[ ,]")) // AB CD DF 区切り文字は正規表現
System.out.println(s);
String[] sa = "AB CD EF".split(" ");
System.out.println(String.join("", sa)); // ABCDEF 結合
System.out.println(String.join("-", sa)); // AB-CD-EF 区切り文字を入れて結合
}
}
文字列のStringクラスの主なメソッドに以下があります。
String.valueOf(数値)
|
数値を文字列に変換する
|
文字列.length()
|
文字数を返す
|
文字列.codePointCount(開始位置, 終了位置)
|
Unicodeのサロゲートペアを考慮した文字数を返す
|
文字列.equals(文字列)
文字列.compareTo(文字列)
|
文字列を比較する
|
文字列.isEmpty()
|
空文字列かどうか
|
文字列.isBlank()
|
空白文字のみの文字列かどうか
|
文字列.indexOf(部分文字列 [,開始位置])
|
部分文字列を含む位置を返す
|
文字列.lastIndexOf(部分文字列)
|
部分文字列を含む位置を返す(後方一致)
|
文字列.contains(部分文字列)
|
部分文字列を含むかどうか
|
文字列.startsWith(部分文字列)
|
先頭が部分文字列と一致するか
|
文字列.endsWith(部分文字列)
|
末尾が部分文字列と一致するか
|
文字列.substring(開始位置[,停止位置])
|
開始位置から終了位置の前の区間の部分文字列を返す
停止位置を省略した場合は末尾まで
|
文字列.charAt(位置)
|
指定位置の1文字を返す
|
文字列.strip()
|
文字列の前後の空白文字を削除する
|
文字列.stripLeading()
|
文字列の先頭部分の空白文字を削除する
|
文字列.stripTrailing()
|
文字列の後方部分の空白文字を削除する
|
文字列.split(区切り文字)
|
区切り文字で文字列を分割した文字列配列を返す
区切り文字は正規表現で指定できる
|
String.join(区切り文字, String[])
|
文字列配列を区切り文字で連結して1つの文字列に結合する
|
文字列のリテラルは、Stringクラスのインスタンスとして直接メソッドを呼び出すことができます。
"文字列".メソッド()
文字列を比較するとき「==」でも比較できますが、その場合は文字列の内容が同じかどうかではなく、オブジェクトの一致の比較になります。文字列の内容の一致を調べるならば、equals()メソッドで比較すべきです。
文字数を取得する場合は通常はlength()を使いますが、サロゲートペア文字を考慮する場合はcodePointCount()を使うほうが正確です。
文字列フォーマット
class Str02 {
public static void main(String[] args) {
System.out.println(String.format("%d", 100));
System.out.println(String.format("%4d", 100));
System.out.println(String.format("%04d", 100));
System.out.println(String.format("%04x", 200));
System.out.println(String.format("%.2f", 123.456));
System.out.println(String.format("%4.1f", 123.456));
System.out.println(String.format("%s", "ABC"));
System.out.println(String.format("%5s", "ABC"));
System.out.println(String.format("%b", false));
System.out.println(String.format("%c", 'あ'));
System.out.println(String.format("%%")); // %%で%という文字に置き換わる
System.out.println(String.format("%s-%d", "ABC", 123));
}
}
100
100
0100
00c8
123.46
123.5
ABC
ABC
false
あ
%
ABC-[ 123]
format()メソッドは、文字列をC言語のprintf関数的な書式指定子により数値や文字列を整形します。
String.format(書式指定文字列, 値, 値,...)
書式指定子= %0n.pT
0: ゼロで空白桁をうめる場合
n: 総桁数
p: 桁数うち小数点以下桁数
T: 型指定
型指定
|
|
d
|
整数
|
x、X
|
16進数(Xは英字を大文字に)
|
f
|
浮動小数点数
|
s
|
文字列
|
b, B
|
真理値をtrue/false表記に(Bは大文字に)
|
c
|
文字
|
ラッパークラス
基本型のラッパークラス
class Wrap01 {
public static void main(String[] args) {
Integer iobj1 = 100; // Boxing
int i1 = iobj1; // Unboxing
Integer iobj2 = Integer.valueOf(100);
int i2 = iobj2.intValue();
}
}
基本型には、それぞれのデータを扱うメソッド等の機能を実装したラッパークラスが用意されてます。
ラッパークラス
|
基本型
|
Integer
|
int
|
Short
|
short
|
Long
|
long
|
Float
|
float
|
Double
|
double
|
Character
|
char
|
Byte
|
byte
|
Boolean
|
boolean
|
基本型と、それに対応するラッパークラスは、互いにインスタンス生成やメソッドを呼び出すことなく変換されます。
基本型をラッパークラスの変数に代入するときを「ボクシング(Boxing)」、その逆を「アンボクシング(Unboxing)」と呼びます。
ラッパークラスの変換メソッド
class Wrap02 {
public static void main(String[] args) {
Integer iw = Integer.valueOf(100);
int i = iw.intValue();
Integer is1 = Integer.parseInt("200");
Integer is2 = Integer.parseInt("ab", 16); // =171
String s1 = Integer.toString(i);
String s2 = Integer.toString(i, 8); // ="144"
String s3 = Integer.toHexString(i); // ="64"
String s4 = is.toString();
System.out.println("min=" + Integer.MIN_VALUE); // min=-2147483648
System.out.println("max=" + Integer.MAX_VALUE); // max=2147483647
}
}
各基本型のラッパークラスには、共通して次の変換メソッドが備えられています。
Integer.valueOf(int)
Double.valueOf(double)
|
基本型からラッパークラスのインスタンスを返す
|
intValue()
doubleValue()
|
ラッパークラスのインスタンスの基本型を返す
|
Integer.parseInt(文字列 [,基数])
Double.parseDouble(文字列)
|
文字列から数値に変換してラッパークラスのインスタンスを返す
文字列が表現している基数を指定可
|
toString()
toString(数値 [,基数])
|
ラッパークラスのインスタンス、数値を文字列に変換
文字列表記の基数を指定可
|
toHexString(数値)
|
数値を16進数の文字列に変換
|
上記のIntegerやDoubleは、そのままLong、Short、Float、Booleanに置き換えられます。
列挙
列挙クラス
enum Level {
ERROR,
WARNING,
INFO,
VERBOSE
}
class Enum01 {
static void showlevel(Level lv) {
System.out.println(lv.ordinal());
}
public static void main(String[] args) {
showlevel(Level.INFO); // 2
}
}
enumは、昇順の数値の静的定数を定義する列挙クラスを定義します。
定数の先頭は0から始まる値が割り当てられます。列挙定数の番号はordinal()メソッドで取得できます。
例外処理
例外捕捉
class Excep01 {
public static void main(String[] args) {
try {
int[] ar;
ar = new int[3];
ar[3] = 10;
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Array out of range");
}
}
}
Array out of range
try-catch-finally構文により、実行中の例外の発生時に実行を分岐させて処理できます。
try-catch-finally構文は、実行中に発生する例外を補足する区間と補足後の処理ブロックを記述します。
<例外処理>
try {
例外を補足したい処理ブロック
} catch (例外クラス 変数) {
例外補足時に実行する処理
} catch (...) {
...
} finally {
例外発生に関わらない共通処理
}
tryブロック内で例外が発生時点で指定される例外に該当するcatchブロックへジャンプします。catchブロックには補足したい例外クラスと、例外発生時にそのインスタンスを受け取る変数を宣言します。その場合varによる型推論は使えません。catchブロックは複数設けることができます。
finallyブロックは、例外発生に関わらず必ず最後に実行する処理を記述します。finallyブロックは省略できます。
複数種類の例外捕捉
class Excep02 {
public static void main(String[] args) {
try {
int[] ar;
ar = new int[3];
ar[2] = 10;
ar[2] = 10 / 0;
} catch(ArrayIndexOutOfBoundsException | ArithmeticException e) {
System.out.println(e);
}
}
}
java.lang.ArithmeticException: / by zero
catchブロックで複数の中で何れかの例外を捕捉し、それらに同一の処理を実行させたい場合は、例外クラスを「|(OR)」で区切って複数指定します。
class Excep03 {
public static void main(String[] args) {
try {
int[] ar;
ar = new int[3];
ar[2] = 10;
int a = 10 / 0;
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Array out of range");
} catch(Exception e) {
System.out.println(e);
}
}
}
java.lang.ArithmeticException: / by zero
複数の種類の例外のそれぞれに対応する例外処理を実行させたい場合は、catchブロックをそれらの例外ごとに記述します。
finallyブロック
class Excep04 {
public static void main(String[] args) {
int[] ar;
ar = new int[3];
try {
ar[3] = 10;
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Array out of range");
} finally {
System.out.println("Finally1");
}
try {
ar[2] = 10;
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Array out of range");
} finally {
System.out.println("Finally2");
}
}
}
Array out of range
Finally1
Finally2
finallyブロックは、例外発生に関わらず必ず最後に実行されます。
その必要がなければfinallyブロックは省略できます。
非チェック例外の伝搬 throws
class Excep05 {
static void func() {
int[] ar;
ar = new int[3];
ar[3] = 10;
}
public static void main(String[] args) {
try {
func();
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Array out of range");
}
}
}
Array out of range
tryブロックの中で呼び出したメソッドの中で発生した非チェック例外(unchecked例外)は、そのメソッドの中でtry-catchで捕捉処理しなければ自動的に呼び出し元に伝搬します。
import java.io.*;
class Excep06 {
static void func() throws FileNotFoundException {
BufferedReader br = new BufferedReader(new FileReader("abcdefg.xyz"));
}
public static void main(String[] args) {
try {
func();
} catch(Exception e) {
System.out.println(e);
}
}
}
java.io.FileNotFoundException: abcdefg.xyz (そのようなファイルやディレクトリはありません)
メソッドの中で発生するチェック例外(checked例外)を、メソッド内で捕捉せずに呼び出し元に処理を委ねる場合は、その発生する可能性のある例外をthrowsで宣言する必要があります。throwする例外の種類がメソッドに複数ある場合は、「,」で区切って宣言します。
void func() throws IOException, NullPointerException, ... {
非チェック例外とはRuntimeExceptionクラスを継承する例外クラスです。それ以外のExceptionクラスのサブクラスは、try-catchで処理するか、throwsにより呼び出し元に処理を委ねることを宣言する必要があります。
以下は代表的なチェック例外と非チェック例外です。
チェック例外
|
IOException
|
|
FileNotFoundException
|
|
ClassNotFoundException
|
非チェック例外
|
NullPointerException
|
|
ArithmeticException
|
|
ArrayIndexOutOfBoundsException
|
|
NumberFormatException
|
例外のスーパークラスで全ての例外を捕捉
class Excep07 {
public static void main(String[] args) {
try {
int[] ar;
ar = new int[3];
ar[3] = 10;
} catch(Exception e) {
System.out.println(e);
}
}
}
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
catchの例外クラスにExceptionクラスを指定すると、全ての例外を捕捉できます。
Exceptionは全ての例外クラスのスーパークラスです。例外の変数で具体的なサブクラスを参照できます。
例外クラスの作成
class TheException extends Exception {
}
class Excep08 {
public static void main(String[] args) {
try {
throw new TheException();
} catch(Exception e) {
System.out.println(e);
}
}
}
TheException
Exceptionクラスをスーパークラスとするサブクラスを定義することで、ユーザ例外を定義できます。
作成した例外のインスタンスをnewで生成してthrowで送出します。
例外の送出
class TheException extends Exception {
}
class Excep09 {
static void func() throws Exception {
throw new TheException();
}
public static void main(String[] args) {
try {
func();
} catch(Exception e) {
System.out.println(e);
}
}
}
TheException
throwは、例外クラスのインスタンスを指定し例外を任意に発生させます。
<例外のスロー>
throw 例外クラスのインスタンス
throw new 例外クラス()
例外の再送出
class Excep10 {
static private double divzero(int i) {
try {
return 10 / i;
} catch (ArithmeticException e) {
throw e;
}
}
public static void main(String[] args) {
try {
divzero(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
java.lang.ArithmeticException: / by zero
at Excep10.divzero(Excep10.java:5)
at Excep10.main(Excep10.java:12)
例外が発生し捕捉した例外オブジェクトを、catchブロック内からthrowにより呼び出し元へ伝搬するように再送出できます。
例外のトレースログ表示
class Excep11 {
public static void main(String[] args) {
try {
int[] ar;
ar = new int[3];
ar[3] = 10;
} catch(Exception e) {
e.printStackTrace();
}
}
}
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Excep11.main(Excep11.java:7)
ExceptionクラスのprintStackTrace()メソッドは、発生した例外のトレースログを表示します。
トレースログは例外発生までのメソッドの呼び出し履歴が現れます。
try-with-resource構文
import java.io.FileOutputStream;
class Excep12 {
public static void main(String[] args) {
try (var out = new FileOutputStream("outfile.txt")) {
out.write("ABCDE".getBytes(), 0, 5);
} catch(Exception e) {
e.printStackTrace();
}
}
}
ファイル入出力等で、リソースを閉じる(close)処理をfinallyブロックで行うように実装することが一般的です。その場合、try-with-resource構文により、クローズ操作が必要なリソースオブジェクトを最初に記述することで、finallyで実装すべきクローズ操作が自動的に組み込まれます。
<try-with-resource構文>
try (リソース[;リソース;]) {
...
} catch (...) {
...
}
try(...)の中に、処理終了時に閉じる処理を省略したいリソースのオブジェクトを記述します。リソースの部分は一般に、対象とするインスタンスの変数をその場でnewで生成して代入します。「;」で区切って複数のリソース(文)を記述できます。
try-with-resource構文が使えるリソースのオブジェクトとは、AutoCloseableインターフェースまたはCloseableインタフェースの実装クラスです。ファイルやソケットが代表的です。
条件判定でアサーション例外
class Excep13 {
static private void set(int i) {
assert i < 10 : "invalid value=" + i;
System.out.println(i);
}
public static void main(String[] args) {
try {
set(13);
} catch (AssertionError e) {
e.printStackTrace();
}
}
}
java.lang.AssertionError: invalid value=13
at Excep13.set(Excep13.java:4)
at Excep13.main(Excep13.java:9)
assert文は、条件式がFalseの場合にAssertionExceptionを送出します。
アサーション例外発生時に表示するメッセージ文字列を指定できます。
<アサーション>
assert 条件式 : メッセージ文字列;
Javaの実行時のデフォルトではassert機能は無効で、条件の結果を無視して処理を続行します。assertを有効にするには、実行時に「-enableassertions」または「-ea」オプションを付ける必要があります。
$ java -ea Excep13