Java 11 Silver & Gold 受験対策 & 今後の備忘録
型
Javaのデータ型にはプリミティブ型と参照型がある。
参照型には更にクラス型、配列型、列挙型などの分類がある。
- プリミティブ型
- boolean
- char
- byte
- short
- int
- long
- float
- double
- 参照型
- クラス型
- インターフェース型
- 配列型
- 列挙型
プリミティブ型
型 | サイズ | 説明 | 値 | ラッパー |
---|---|---|---|---|
boolean |
仕様無 | 真偽値 |
true , false
|
Byte |
char |
16bit | Unicode文字 |
¥u0000 ~ ¥uFFFF
|
Character |
byte |
8bit | 整数 |
-128 ~ 127
|
Byte |
short |
16bit | 整数 |
-32768 ~ 32767
|
Short |
int |
32bit | 整数 |
-2137383648 ~ 2147483647
|
Integer |
long |
64bit | 整数 |
-9223372036854775808 ~ 9223372036854775807
|
Long |
float |
32bit | 単精度浮動小数点数 |
±3.40282347E+38 ~ 1.40239846E-45
|
Float |
double |
64bit | 倍精度浮動小数点数 |
±1.79769313486231570E+308 ~ ±4.94065645841246544E-324
|
Double |
String
型はプリミティブ型ではなく参照型。
参照型
クラス型、配列型、列挙型がある。
リテラル
整数リテラル
デフォルトではint
型として扱われる。
整数リテラルをlong
型として扱いたい場合、末尾にl
もしくはL
の接尾辞をつける。
long a = 100L;
また整数リテラルをshort
型やbyte
型として扱うための接尾辞は存在しない。その代わりにint
型とshort
型などは自動変換される。
// 10はint型として扱われる整数リテラルだが、short型に代入できる(short型に自動変換される)
short i = 10;
10進数、2進数、8進数、16進数
デフォルトでは10進数として扱われる。
2進数で表したい場合、0b
を接尾辞としてつける。
byte ichi = 0b0001; // 1
short ni = 0b0010; // 2
int san = 0b00011; // 3
System.out.println(ichi);
System.out.println(ni);
System.out.println(san);
// >> 1
// >> 2
// >> 3
8進数で表したい場合、0
を接尾辞としてつける。
byte nana = 07; // 7
short hachi = 010; // 8
int kyu = 011; // 9
System.out.println(nana);
System.out.println(hachi);
System.out.println(kyu);
// >> 7
// >> 8
// >> 9
16進数で表したい場合、0x
を接尾辞としてつける。
byte jugo = 0xF; // 15
short juroku = 0x10; // 16
int junana = 0x11; // 17
System.out.println(jugo);
System.out.println(juroku);
System.out.println(junana);
// >> 15
// >> 16
// >> 17
浮動小数点数リテラル
デフォルトではdouble
型として扱われる。
float
型として扱いたい場合、末尾にf
もしくはF
の接尾辞をつける。
float a = 100.0F;
真偽リテラル
boolean
型として扱われる。
文字リテラル
char
型として扱われる。
文字リテラルはシングルコーテーション('
)で囲う必要がある。
またJavaは Unicode を文字コードとして採用していて、Unicodeエスケープシーケンスを使って文字を表現する際にはバックスラッシュ(\
)を使用する。(Windows環境では¥
)
Unicode の文字は U+0000
~ U+FFFF
までの文字コードが存在するが、Javaでは\u0000
~ \uFFFF
で表現する。
char komoji = '\u0061';
char omoji = '\u0041';
System.out.println(komoji);
System.out.println(omoji);
// >> a
// >> A
文字リテラルは直接16進数や10進数でも表現できる。(0x0000
~ 0xFFFF
は10進数では 0
~ 65535
であるため、次のような表現も可能。
char komoji = 0x0061;
char komoji2 = 97; // 0x0061 = 97
char omoji = 0x0041;
char omoji2 = 65; // 0x0041 = 65
System.out.println(komoji);
System.out.println(komoji2);
// >> a
// >> a
System.out.println(omoji);
System.out.println(omoji2);
// >> A
// >> A
char
型の変数に文字列リテラルは代入できない。
char ok = 'a'; // 有効
char error = "a"; // コンパイルエラー
Javaはシングルコーテーションとダブルコーテーションに明確な違いがあるため注意。
文字列リテラル
String
型として扱われる。
文字列リテラルはダブルコーテーション("
)で囲う必要がある。
String
型
String
型はプリミティブ型ではなく参照型(java.lang.String
)に分類される。
String
クラスは内部で文字列をchar
型の配列として扱っている。
String
型はインターフェースCharSequence
の実装クラスでもある。
String
型のインスタンス化
参照型のデータ型はnew
演算子によってインスタンス化を行う。
MyClass a = new MyClass();
ただし、String
型だけは特別扱いで、文字列リテラルによってString
型のインスタンスが生成できるようになっている。
String a = "hello world"; // String型はプリミティブ型ではなく参照型
immutable
String
クラスは immutable なクラスであるため、一度生成したインスタンスは中身を変更することができない。
String
型変数への再代入は、インスタンスの中身変更ではなく実際にはインスタンス新規生成を意味する。
// インスタンスに変更を加えているように見えるが、実際にはインスタンスを変更している訳ではない
String a;
a = "hello world";
a = "ABCD";
// 実際は immutable なインスタンスを新規生成している
String a;
a = new String("hello world");
a = new String("ABCD");
このため、subString()
、concat()
、toUpperCase()
などのメソッドが返すインスタンスは、実行されたメソッドのインスタンスではなく、新しいインスタンスということになる。
コンスタントプール
前述の通り、「文字列リテラルの使用」は「java.lang.String
クラスのインスタンス生成」を意味する。
しかし実際の文字列リテラルは、同じものが頻繁に使用されることが多い。同じ文字列であるのにわざわざ別のインスタンスとして扱うメリットはなく、逆に新たなインスタンス生成の度にメモリ領域が必要になってしまい非常に非効率になる。
String a = "hello world";
String b = "hello world";
String c = "hello world";
String d = "hello world";
そのため文字列リテラルにはコンスタントプールという仕組みがある。
コンスタントプールは文字列リテラルから生成されたString
インスタンスを管理するためのメモリ領域のことを指す。
通常、文字列リテラルが使用されるとまず最初に定数用のメモリ空間(コンスタントプール)にその文字列リテラルから生成されたインスタンスが既に存在しないかがチェックされる。存在しない場合に限り、String
型インスタンスが生成され、生成されたインスタンスがコンスタントプールに配置される。
この仕組みにより、同じ文字列リテラルからString
型インスタンスを生成する際は、過去に生成されたString
インスタンスへの参照が再利用される。
// 同じインスタンスへの参照が再利用されていることがわかる
String a = "hello world"; // コンスタントプール生成
String b = "hello world"; // コンスタントプールに存在するインスタンスへの参照を再利用
String c = "hello world"; // コンスタントプールに存在するインスタンスへの参照を再利用
String d = "hello world"; // コンスタントプールに存在するインスタンスへの参照を再利用
if (a == b && b == c && c == d) {
System.out.println("各変数は同じインスタンスを参照している");
}
// >> 各変数は同じインスタンスを参照している
ただし、プログラムの中で動的に生成された文字列はコンスタントプールに配置されない。また明示的にnew
演算子によって生成されたインスタンスが生成された場合、String
インスタンスはコンスタントプールからの再利用は行われない。
String
クラスのintern()
メソッドは、コンスタントプール(を含むメモリ領域)内からString
インスタンスを検索してその参照を返すことができる。
String a = "hello world";
// 明示的にnewしているので、コンスタントプールからのインスタンスの再利用、またはコンスタントプールへの保存がされない
String b = new String("hello world");
if (a == b) {
System.out.println("同じインスタンスを参照している");
}else{
System.out.println("別のインスタンスを参照している");
}
// >> 別のインスタンスを参照している
if (a == b.intern()) {
System.out.println("同じインスタンスを参照している");
}else{
System.out.println("別のインスタンスを参照している");
}
// >> 同じインスタンスを参照している
初期値
クラスのフィールドは、デフォルトで初期化されるため、int
型のメンバ変数は0
、boolean
型はfalse
、参照型のメンバ変数はnull
に初期化される。
一方メソッド内のローカル変数は、明示的に初期化されない限り初期値が設定されない。
class MyClass {
int a; // 0に初期化される
boolean b; // falseに初期化される
String c; // nullに初期化される
void myMethod() {
int x; // 初期化されない
System.out.println(x); // コンパイルエラー
}
}
型推論・ジェネリクス・共変性・反変性
初期化子(Initializer)
クラスの初期化時に特定のコードを実行するための特殊なブロックのこと。
static初期化子(static initializer)とインスタンス初期化子(instance initializer)がある。
static初期化子
クラスがロードされるときに一度だけ実行されるコードブロック。
class MyClass {
static {
System.out.println("hello world");
}
}
静的なフィールドを初期化する際に有効。
class MyClass {
static int a;
static {
a = 10;
}
}
インスタンス初期化子
インスタンス生成の度に実行されるコードブロック。
コンストラクタの前に実行される。
class MyClass {
{
System.out.println("hello world");
}
}
オーバーロードによるコンストラクタが複数あるような場合に、全てのコンストラクタに共通の処理をまとめたい時などに有効。
class MyClass {
{
// コンストラクタの前に実行される
System.out.println("共通処理");
}
MyClass(int a) {
System.out.println("コンストラクタ1");
}
MyClass(int a, int b) {
System.out.println("コンストラクタ2");
}
}
実行順
- スーパークラスのstatic初期化子
- サブクラスのstatic初期化子
- スーパークラスのインスタンス初期化子
- スーパークラスのコンストラクタ
- サブクラスのインスタンス初期化子
- サブクラスのコンストラクタ
class SuperClass {
static {
System.out.println("①static initializer of super class");
}
{
System.out.println("③instance initializer of super class");
}
SuperClass(){
System.out.println("④constructor of super class");
}
}
class SubClass extends SuperClass{
static {
System.out.println("②static initializer of sub class");
}
{
System.out.println("⑤instance initializer of sub class");
}
SubClass(){
System.out.println("⑥constructor of sub class");
}
}
SuperClass a = new SubClass();
// >> ①static initializer of super class
// >> ②static initializer of sub class
// >> ③instance initializer of super class
// >> ④constructor of super class
// >> ⑤instance initializer of sub class
// >> ⑥constructor of sub class
ネストしたクラス
ストリーム
入出力
JBDC
アノテーション
Enum
JavaのEnumはクラス。
定義したEnumはjava.lang.Enum
クラスのサブクラスとして扱われる。
定義した列挙子は、それぞれが列挙型から生成されたインスタンスとして扱われる。
public enum Fruits {
Apple, // Fruits型インスタンス
Banana, // Fruits型インスタンス
Orange, // Fruits型インスタンス
}
コンストラクタやフィールドを定義する事ができる。ただし、コンストラクタはprivate
でなけらばならず、省略した場合には暗黙的にprivate
と解釈される。明示的にpublic
とした場合、コンパイルエラーが発生する。
public enum Fruits {
Apple(1), // Fruits型インスタンス
Banana(2), // Fruits型インスタンス
Orange(3); // Fruits型インスタンス
private int id;
// privateと解釈される
Fruits(int id){
this.id = id;
}
}
ローカライズ
プロパティファイル
例外
アサーション
アサーションの構文は以下の通り。
assert 条件式 : "メッセージ";
アサーションはでフォルドでは無効になっているため、実行時に-ea
オプションをつける必要がある。
$ java -ea com.sample.app.Main
モジュールシステム
試験対策としての早見表
文法的な暗記系知識。
実行
起動パラメータにスペースを含めたい場合、ダブルコーテーション「"
」で囲う
java Main.java "hello world"
起動パラメータにダブルコーテーション「"
」を使いたい場合、エスケープする
LinuxOSの場合はバックスラッシュ「\
」、Windowsの場合は「¥
」を使用する
java Main.java \"
上の例では「"」がパラメータとして渡される。
エスケープされないダブルコーテーション「"
」は無視される
java Main.java a"bc"
上の例では「abc」がパラメータとして渡される。
アクセス修飾子
protected
とデフォルトの違いに注意して覚える
修飾子 | 可視性 | |
---|---|---|
private | - | 同じクラス内からのみ |
なし(デフォルト) | ~ | 同じパッケージ内からのみ |
protected | # | 同じパッケージ内 + 継承関係にあるサブクラス からのみ |
public | + | 全て |
for文
初期化文と更新文はコンマ(,
)で区切って複数記述することができる
for (int i = 0, j = 0; i < 10; i++, j--) {
// 処理
}
ただし初期化では同じ型しか記述できない
// コンパイルエラー
for (int i = 0, long j = 0; i < 10; i++, j--) {
// 処理
}
条件式と更新式は省略できる
for (int i = 0; ; i++) {
// 処理
}
for (int i = 0; i < 10; ) {
// 処理
}
break
で抜けるのは直近のループだけ
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if(条件){
braek;
}
}
// ここに抜ける
}
// ここには抜けない
switch文
switch文の条件式にはboolean
型やlong
型、float
型、double
型を使用することができない。
参照型(String
型と列挙型を除く)についても使用することができない。
fianl
ではない変数はcase
に使用できない
int target = 100;
int a = 10;
final int B = 10;
switch (target) {
case B:
// 処理
break;
case a: // コンパイルエラー
// 処理
break;
}
配列
要素数は必ず右辺に書く
配列宣言時に左辺の型として使用する配列型(int[]
や int[][]
など)は配列オブジェクトへの参照を保持するためのものであり、配列の要素数については関与しない。
各要素については配列オブジェクトが管理を行う。
int[] array = new int[10];
// コンパイルエラー
int[10] array = new int[];
// コンパイルエラー
int[10] array = new int[10];
[]
は変数名の後ろにも置ける
int[] array = new int[10];
int array2[] = new int[10];
int array3[][] = new int[10][20];
多次元配列は[]
をデータ型の後ろと変数名の後ろに分散して置ける
int[] array2[] = new int[10][20]; // 2次元配列
int[][] array3[] = new int[10][20][30]; // 3次元配列
多次元配列の1次元目の要素数は省略できない
int[][] array = new int[10][20];
int[][] array2 = new int[10][];
// コンパイルエラー
int[][] array3 = new int[][20];
多次元配列の2次元目以降は要素数を揃える必要はない
int[][] array = new int[3][];
array[0] = new int[10];
array[1]= new int[20];
array[2] = new int[30];
配列の初期子{}
を使用する場合、要素数は指定できない
int[] array = {1, 2, 3, 4, 5};
int[] array2 = new int[]{1, 2, 3, 4, 5};
// コンパイルエラー
int[] array3 = new int[5]{ 1, 2, 3, 4, 5};
明示的に初期化しなかった配列の各要素は、型のデフォルト値になる
int[] a = new int[1];
System.out.println(a[0]);
// >> 0
double[] b = new double[1];
System.out.println(b[0]);
// >> 0.0
boolean[] c = new boolean[1];
System.out.println(c[0]);
// >> false
char[] d = new char[1];
System.out.println(d[0]); // \u0000
System.out.println((int)d[0]);
// >> �
// >> 0
String[] e = new String[1];
System.out.println(e[0]);
// >> null
Object[] f = new Object[1];
System.out.println(f[0]);
// >> null
コンストラクタ
コンストラクタに見えるメソッド
class MyClass {
// 戻り値を指定しているためコンストラクタ扱いにならず、メソッド扱いになる
void MyClass(){
System.out.println("hello world");
}
}
public class Main {
public static void main(String[] args) {
MyClass a = new MyClass();
a.MyClass();
// >> hello world
}
}
自身のコンストラクタはthis()
で呼び出せる
class MyClass {
MyClass() {
this(10);
}
MyClass(int a) {
}
}
自身のコンストラクタを呼び出す場合、thisより前に処理は実行できない
class MyClass {
MyClass() {
// thisは最初に実行しなければならない
System.out.println("hello world");
// コンパイルエラー
this(10);
}
MyClass(int a) {
}
}
インターフェース
final
かつstatic
なフィールドなら定義できる
interface MyInterface {
public static final int A_MAX = 10;
}
以下のように記述することも可能。
interface MuInterface {
// 内部的には public static final int A_MAX = 10; として扱われる
int A_MAX = 10;
}
static
なメソッドも定義できる
interface MyInterface {
public static void myMethod(){
};
}
private
なメソッドも定義できる
public interface MyInterface {
private void method() {
System.out.println("hello world");
}
}
複数のdefault
メソッドの共通処理をまとめる事ができる。
Object
クラスのメソッドをオーバーライドできない
インターフェースは通常、中身のない抽象メソッドのみが定義可能であり、中身のあるメソッドを定義する場合default
を使用する必要がある。
interface MyInterface {
public void myMethodA();
default public void myMethodB() {
System.out.println("hello world");
}
// コンパイルエラー
public void myMethodC() {
System.out.println("hello world");
}
}
このdefault
を使用してメソッドの定義をするとき、java.lang.Object
のメソッド(equals()
, hashCode()
, toString()
)をオーバーライドすることはできない。
interface MyInterface {
// コンパイルエラー
@Override
default public String toString() {
return "hello world";
}
}
インターフェースのdefault
メソッドを呼び出す
次の構文でインターフェースのdefault
メソッドを実装クラスから呼び出す事ができる。
インターフェース名.super.メソッド名()
interface MyInterface {
default public void a() {
System.out.println("hello world");
}
}
class ConcreteClass implements MyInterface {
@Override
public void a() {
// インターフェース名.super.メソッド名()
MyInterface.super.a();
System.out.println("hello world");
}
}
ただし、直接実装(implements
)していないスーパークラスのdefault
メソッドは呼び出せない(孫から祖父母のメソッドは呼べない)。
interface SuperInterface {
default public void a() {
System.out.println("hello world");
}
}
interface SubInterface extends SuperInterface {
}
class ConcreteClass implements SubInterface {
@Override
public void a() {
// コンパイルエラー
SuperInterface.super.a();
System.out.println("hello world");
}
}
「もしインターフェース名を指定しなくても良い」とすると、多重に実装(implements
)したインターフェースに同じ名前のdefault
メソッドが定義されていたらどちらのメソッドかが判別できなくなってしまう。
interface InterfaceA {
default public void hello() {
System.out.println("hello world");
}
}
interface InterfaceB {
default public void hello() {
System.out.println("hello world");
}
}
class ConcreteClass implements InterfaceA, InterfaceB {
@Override
public void hello() {
// コンパイルエラー(どっちのhello()?)
super.hello();
System.out.println("hello world");
}
}
default
メソッド > 通常メソッド
インタフェースの本来の目的は「型を提供すること」であり、default
メソッドはあくまでもオプション的な位置付けであるため、同じ名前のメソッドがインターフェースとスーパークラス両方で定義されている場合、スーパークラスのメソッドが優先される。
public interface MyInterface {
default void method() {
System.out.println("インターフェース");
}
}
public class SuperClass {
public void method() {
System.out.println("スーパークラス");
}
}
public class MyClass extends SuperClass implements MyInterface {}
MyClass obj1 = new MyClass();
obj1.method();
// >> スーパークラス
SuperClass obj2 = new MyClass();
obj2.method();
// >> スーパークラス
MyInterface obj3 = new MyClass();
obj3.method();
// >> スーパークラス
継承
サブクラスではスーパークラスのフィールド名と同じフィールド名を使える
サブクラスではスーパークラスのフィールド名と同じフィールド名を使える。(オーバーライドではなく、実際には新しいフィールドが追加される)
ただし、実行時どちらにアクセスされるかは変数の宣言型に依存する。スーパークラス型で宣言した変数を通してアクセスを行った場合にはスーパークラスで宣言したフィールドが使用される。
class SuperClass {
String a = "super";
}
class SubClass extends SuperClass {
String a = "sub";
}
SuperClass obj1 = new SubClass();
System.out.println(obj1.a);
// >> super
SubClass obj2 = new SubClass();
System.out.println(obj2.a);
// >> sub
メソッドからのアクセスの場合、以下のようになる。
class SuperClass {
String a = "super";
public void b() {
System.out.println(a);
}
}
class SubClass extends SuperClass {
String a = "sub";
}
SuperClass obj1 = new SubClass();
obj1.b();
// >> super
SubClass obj2 = new SubClass();
obj2.b();
// >> super
class SuperClass {
String a = "super";
public void b() {
System.out.println(a);
}
}
class SubClass extends SuperClass {
String a = "sub";
// オーバーライドしている
@Override
public void b() {
System.out.println(a);
}
}
SuperClass obj1 = new SubClass();
obj1.b(); // オーバーライドしているためSubClassのメソッドが使用される
// >> sub
SubClass obj2 = new SubClass();
obj2.b();
// >> sub
スーパークラスのコンストラクタはコンパイル時に自動で追加される
class SuperClass {
public SuperClass() {
System.out.println("super class constructor");
}
}
class SubClass extends SuperClass {
public SubClass() {
// コンパイル時にここに super(); が自動挿入される
System.out.println("sub class constructor");
}
}
SuperClass obj = new SubClass();
// >> super class constructor
// >> sub class constructor
継承させたくないクラスはfinal
で修飾する
final class myClass {
}
Object
のequals()
Object
のequals()
はオーバーライドして同値性を検証させるものだが、オーバーライドをしなかった場合には同一性(==
と同じ)を検証するようになっている。
static
メソッドはオーバーライドできない
public class SuperClass {
static void method() {
}
}
public class SubClass extends SuperClass {
// コンパイルエラー
@Override
static void method() {
}
}
ただし@Override
アノテーションをつけなかった場合、スーパークラスのstatic
メソッドを隠蔽したことになり、コンパイルエラーが起きない。
public class SubClass extends SuperClass {
// 親クラスのメソッドを隠蔽
static void method() {
}
}
関数型インターフェース・ラムダ式
基本事項はこちら。
ラムダ式内で利用するローカル変数は実質的にfinal
でなければならない
ラムダ式は宣言場所と同じスコープを持つため、ラムダ式内でラムダ式を宣言したスコープからアクセス可能なローカル変数にアクセスすることができる。
一方で、ラムダ式が実行されるのは宣言場所と別のタイミングになるため、ラムダ式内でローカル変数へのアクセスを行うと、そのローカル変数は実質的にfinal
(effectively final)でなければならなくなる。
String a = "hello";
// ラムダ式内で a を参照
Runnable runnable = () -> System.out.println(a);
// aに再代入しようとするとコンパイルエラーが起きる
a = "Goodbye";
runnable.run();
関数型インターフェース 早見表
インターフェース名 | 定義 | 補足 |
---|---|---|
Supplier<T> |
T get(); |
|
Consumer<T> |
void accept(T t); |
|
BiConsumer<T, U> |
void accept(T t, U u); |
|
Predicate<T> |
bolean test(T t); |
|
BiPredicate<T, U> |
boolean test(T t, U u); |
|
Function<T> |
R apply(T t); |
|
BiFunction<T, U> |
R apply(T t, U u); |
|
UnaryOperator<T> |
T apply(T t); |
|
BinaryOperator<T> |
T apply(T t1, T t2); |
BiFunction<T, U> のT とU が同じパターン。BiFunction<T, U> の特殊系と言う位置づけ。 |
Runnabale |
void run(); |
|
Callable<V> |
V call(); |
コレクション
Arrays.asList()
が返すList
は固定サイズ
配列をList
に変換することができるArrays.asList()
が返すList
は、要素数が固定の特殊なList
になっている。
通常のList
と同じだと勘違いして要素を追加しようとすると、実行時例外が発生する。
List<String> list = Arrays.asList(new String[]{"aaa", "bbb", "ccc"});
list.add("ddd"); // 実行時例外が発生する
要素数が固定のList
は明示的に生成することも可能。
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 要素数が固定のListに変換
List<String> list2 = Collections.unmodifiableList(list);
list2.add("ddd"); // 実行時例外が発生する
動的サイズのArrayList
を初期化したい場合、ArrayList
のコンストラクタに固定サイズのList
を渡す。
List<String> list = new ArrayList<>(
Arrays.asList(new String[]{"aaa", "bbb", "ccc"})
);
list.add("ddd");
並列処理
暗記系クラス名とメソッド名
API
累乗はpow
、平方根はsqrt
pow : power(累乗)
// x の y 乗
Math.pow(x, y)
sqrt: square root(平方根)
// x の平方根
Math.sqrt(x)
try-catch / try-with-resources
catch
とfinally
ブロックでどちらも戻り値をreturn
したときはfinally
の戻り値が優先される
String a() {
try {
throw new Exception();
} catch (Exception e) {
return "catch";
} finally {
return "finally";
}
}
System.out.println(a());
// >> finally
try
ブロックで宣言した変数はfinally
ブロックからアクセスできない
try (FileInputStream is = new FileInputStream("sample.txt")) {
} catch (Exception e) {
} finally {
// コンパイルエラー
if (is != null) {
}
}
リソースのクローズ時の実行順
class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("①close()が実行される");
}
}
try (MyResource r = new MyResource()) {
throw new Exception();
} catch (Exception e) {
System.out.println("②catchブロックが実行される");
} finally {
System.out.println("③finallyブロックが実行される");
}
// >> ①close()が実行される
// >> ②catchブロックが実行される
// >> ③finallyブロックが実行される