目次
- 変数、クラス、メソッドにつける識別子
- Java7からの新しい整数リテラル表記
- メインメソッドについて
- static変数、staticメソッドを使用する場合
- StringとStringBuilderについて
- replaceとreplaceAllは何が違うのか?
- インクリメント演算子
- 論理演算子
- while、do-while、for文で中カッコを省略した場合
- for文の構文について
- ラベルが記述できるもの
- 配列インスタンスの生成と初期化
- ListとArrayListについて
- 配列のlengthとArrayListのsizeについて
- final修飾子
- staticイニシャライザ、インスタンスイニシャライザ
- デフォルトコンストラクタ
- インタフェースクラス
- オーバライド
- カプセル化
- スーパクラスをサブクラスへ代入できるのか?
- System.out.printlnに参照情報を渡すと
- abstract
- 例外クラスについて
- ラムダ式
- LocalDateTime
変数、クラス、メソッドにつける識別子
- 識別子の1文字目は、英字、ドル記号、アンダースコアのみ
- 識別子の2文字目以降は数字も可
- 予約語は使用不可
- 大文字、小文字は厳密に区別される
- 文字数の長さに制限はない
記号は、$か_のみなので、#なんかは使えません。
Java7からの新しい整数リテラル表記
アンダースコアを使った数値表記ができるようになった。
例)123_456_789
ルールは以下のとおりです。
- リテラルの先頭と末尾には記述できない
- 記号の前後には記述できない(記号とは、「.」「0b」、「f」、「0x」、「L」)
上記以外であれば、場所や回数は自由となっている。下記例もOK。
例)1________5
メインメソッドについて
mainメソッドのことをエントリーポイントとも言います。
通常は、public static void と書くんですが、publicもstaticも識別子なので、順番入れ替えても大丈夫なんですね。
1 2 3 4 5 6 7 8 |
public class Test { public static void main(String[] args) { System.out.println("111"); } static public void main(String[] args) { System.out.println("222"); } } |
もちろん、上2つのメソッドがあれば、コンパイルエラーとなります。
1 2 3 4 5 6 7 8 9 10 11 |
public class Test { public void main(String[] args) { sayHello(); System.out.println("main"); } void sayHello() { System.out.println("hello"); } } // 実行結果 実行時エラー |
上記は、mainメソッドにstaticがついていないため、mainメソッドは通常のインスタンスメソッドとして認識されます。
static変数、staticメソッドを使用する場合
別クラスのstatic変数(クラス変数)を使用する場合、静的インポート又はパッケージ名も含めた指定にする必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package silver1.silver2; public class Access1 { public static int num1 = 55; } ======================================== package silver1; //静的インポートを使用する場合は、以下のどちらでもよい //import static silver1.silver2.Access1.*; import static silver1.silver2.Access1.num1; public class Test { public static void main(String[] args) { int num = num1; //int num = silver1.silver2.num1; // パッケージ名も含めた指定 } } |
staticメソッドからstaticメンバ、staticメソッドは呼び出し可能です。
staticメソッドから、インスタンス変数、インスタンスメソッドは呼び出し不可です。
インスタンスメソッドから、staticメンバ、staticメソッドは呼び出し可能です。
StringとStringBuilderについて
StringとStringBuilderのequalsメソッドは、比較対象が異なります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Test { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); String msg = "Thankyou"; sb.append("Thank").append("you"); if (msg == sb.toString()) { System.out.println("==はオブジェクトの参照情報を比較する"); } if (sb.equals(msg)) { System.out.println("StringBuilderのequalsは、オブジェクトの参照情報を比較する"); } if (msg.equals(sb.toString())) { System.out.println("Stringのequalsは、オブジェクトが保持する値を比較する"); } if (sb.toString().equals(msg)) { System.out.println("StringBuilderのtoStringのequalsは、オブジェクトが保持する値を比較する"); } } } |
上記4つのif文で条件が一致するのは、下2つのif文のみです。
Stringには、「コンスタントプール」という仕組みがあります。
Stringはプログラム中に頻繁に使用されるため、そのたびにインスタンスを生成していては、処理の負荷が高くなり、メモリも大量に消費することになります。そこで、文字列リテラルは、定数値としてインスタンスとは異なる定数用のメモリ空間に作られる。
つまり、同じ文字列リテラルがあれば、「使いまわし」されるということです。
1 2 3 4 5 6 7 8 9 10 11 |
public class Test { public static void main(String[] args) { String a = "sample"; String b = "sample"; System.out.println(a == b); System.out.println(a.equals(b)); } } // 実行結果 true true |
Stringをnewして比較した場合は、以下となります。
1 2 3 4 5 6 7 8 9 10 11 |
public class Test { public static void main(String[] args) { String a = "sample"; String b = new String("sample"); System.out.println(a == b); System.out.println(a.equals(b)); } } // 実行結果 false true |
また、Stringのtrim()は、文字列前後の空白を除去するが、空白だけでなく、\t \r\ \nなども除去します。
StringとStringBuilderの大きな違いは、Stringは不変オブジェクトで、StringBuilderは可変オブジェクトです。
StringBuilderのcapacity()は、16文字のバッファを持っている。
StringBuilder str = new StringBuilder(” a b c d e “);
System.out.println(str.capacity());
この結果は、11+16=27となる。
replaceとreplaceAllは何が違うのか?
1 2 3 4 5 6 7 8 9 10 |
public class Test { public static void main(String[] args) { String str = "abc.def."; System.out.println("Result: " + str.replace(".", "1")); System.out.println("Result: " + str.replaceAll(".", "1")); } } // 実行結果 Result: abc1def1 Result: 11111111 |
replaceでは引数に指定した文字と一致した文字を全て置き換えるが、
replaceAllでは引数に指定した文字を正規表現と見なして置き換える。
Stringは不変なので、どちらも戻り値が置換された文字となる。
インクリメント演算子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Test { public static void main(String[] args) { int a = 10,b = 0; if (10 == a++) { System.out.println("aの値は10:" + a); } else { System.out.println("aの値は10でない:" + a); } if (0 == ++b) { System.out.println("bの値は0:" + b); } else { System.out.println("bの値は0でない:" + b); } } } //実行結果 aの値は10:11 bの値は0でない:1 |
a++の場合は、そのステップ時点ではインクリメントされず、そのステップを越えてからインクリメントされます。逆に、++aだと、そのステップ時点でインクリメントされます。
論理演算子
& と && 何が違うのだろうと調べた結果は、以下のとおり。
- “&”は論理(ビット)積演算子。左辺と右辺は両方評価され、どちらも真なら真。
- “&&”は条件付論理積演算子。左辺が真の時だけ右辺を評価し、右辺も真なら真。
|と||も同様で、”|”は両方評価され、”||”は左辺が偽の時だけ右辺が評価されます。
※インクリメント演算子と絡めた問題がでますよ。
while、do-while、for文で中カッコを省略した場合
下記のように、次の1文だけが繰り返しの対象となります。
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Test { public static void main(String[] args) { int a = 0, b = 0; while(a < 5) System.out.println(a++); for(int i=0; i < 5; i++) System.out.println(i); do System.out.println(b++); while (b < 5); } } |
do-while文で中カッコを省略した場合は、1文のみを記述できます。
下記のように、2文以上記述した場合は、コンパイルエラーとなります。
1 2 3 4 5 6 7 8 9 |
public class Test { public static void main(String[] args) { int b = 0; do System.out.println(b); b++; while (b < 5); } } |
for文の構文について
構文:for ( 初期化文; 条件文; 更新文 ) {
初期化文と更新文は、複数記述できます。
初期化文で複数の変数を宣言する場合は、全て同じ型でなければなりません。
ラベルが記述できるもの
通常は、ループにつけることが多いですが、ループ以外にも使用可能です。
- コードブロック
- 全てのループ文と分岐
- 式
- 代入
- return文
- tryブロック
- throw文
配列インスタンスの生成と初期化
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class Test { public static void main(String[] args) { int[] array1 = {}; int[] array2 = new int[0]; int[] array3 = {1, 2}; int[] array4 = new int[]{1, 2}; int[][] array5 = {}; int[][] array6 = new int[][]{}; int[] a = new int[2]{}; // ①コンパイルエラー int[][] b = new int[]{}; // ②コンパイルエラー } } |
①コンパイルエラーについて
初期化演算子を指定した場合は、要素数は自動計算されるため、要素数を指定することはできません。
②コンパイルエラーについて
多次元配列の生成で、左辺と右辺の次元数が不一致となっているため。
ListとArrayListについて
Listはインターフェースで、ArrayListは、Listを実装したクラスになります。
ArrayListとは、Listインタフェースを実装したコレクションクラスです。
ちなみに、Listを実装したクラスには他にもLinkedListやStackといったクラスが存在しています。
Listはインタフェースなのでインスタンスが生成できません。
抽象クラスもインスタンスが生成できません。
したがって
List<Integer> list = new List<Integer>();
はできません。
つまり、下記のどちらかで宣言することになります。
ArrayList<Integer> list = new ArrayList<Integer>();
List<Integer> list = new ArrayList<Integer>();
- 宣言の型にListを使う方がいい場合
例えばArrayListからLinkedList等、別のコレクションに変更したい場合など。
- 宣言の型にArrayListを使う方がいい場合
例えばArrayListにしかないメソッドを使いたい場合など。
Listのメソッドにはなくて、ArrayListにしかないメソッドは、Listをコピーするcloneメソッドがあります。
つまり、cloneを使うときはArrayListで宣言すればよいということ。
通常は、Listで宣言しておけばOK。
配列のlengthとArrayListのsizeについて
配列のlengthは、値が設定されていなくても、定義した要素数が返ってくる。
ArrayListのsizeは、設定された要素数が返ってくる。
ArrayListのremoveは、要素を削除するメソッド。リスト内に削除する要素が複数ある場合は、最初の要素だけが削除される。要素削除後は、後ろの要素が前詰めされます。
final修飾子
- クラスにつけた場合、そのクラスは継承不可
- メソッドにつけた場合、そのメソッドはオーバーライド不可
- 変数につけた場合、その変数は変更不可
staticイニシャライザ、インスタンスイニシャライザ
どちらもコンストラクタより前に実行されます。
実行順は、staticイニシャライザ→インスタンスイニシャライザ→コンストラクタ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
class SuperClass { SuperClass() { System.out.println("SuperClass:コンストラクタ"); } static { System.out.println("SuperClass:staticイニシャライザ1"); } { System.out.println("SuperClass:インスタンスイニシャライザ1"); } static { System.out.println("SuperClass:staticイニシャライザ2"); } { System.out.println("SuperClass:インスタンスイニシャライザ2"); } } class SubClass extends SuperClass { static { System.out.println("SubClass:staticイニシャライザ1"); } { System.out.println("SubClass:インスタンスイニシャライザ1"); } SubClass() { System.out.println("SubClass:コンストラクタ"); } { System.out.println("SubClass:インスタンスイニシャライザ2"); } static { System.out.println("SubClass:staticイニシャライザ2"); } } public class Test { public static void main(String[] args) { SubClass sub1 = new SubClass(); System.out.println("==========================================="); // SubClass sub2 = new SubClass(); } } // 実行結果 SuperClass:staticイニシャライザ1 SuperClass:staticイニシャライザ2 SubClass:staticイニシャライザ1 SubClass:staticイニシャライザ2 SuperClass:インスタンスイニシャライザ1 SuperClass:インスタンスイニシャライザ2 SuperClass:コンストラクタ SubClass:インスタンスイニシャライザ1 SubClass:インスタンスイニシャライザ2 SubClass:コンストラクタ =========================================== |
staticイニシャライザは、クラスロード時に一度だけ実行される。
では、上記コードのsub2インスタンス生成を有効にして実行するとどうなるか?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class Test { public static void main(String[] args) { SubClass sub1 = new SubClass(); System.out.println("==========================================="); SubClass sub2 = new SubClass(); } } // 実行結果 SuperClass:staticイニシャライザ1 SuperClass:staticイニシャライザ2 SubClass:staticイニシャライザ1 SubClass:staticイニシャライザ2 SuperClass:インスタンスイニシャライザ1 SuperClass:インスタンスイニシャライザ2 SuperClass:コンストラクタ SubClass:インスタンスイニシャライザ1 SubClass:インスタンスイニシャライザ2 SubClass:コンストラクタ =========================================== SuperClass:インスタンスイニシャライザ1 SuperClass:インスタンスイニシャライザ2 SuperClass:コンストラクタ SubClass:インスタンスイニシャライザ1 SubClass:インスタンスイニシャライザ2 SubClass:コンストラクタ |
sub2のインスタンス生成時には、staticイニシャライザが実行されないことが分かります。
デフォルトコンストラクタ
デフォルトコンストラクタとは、引数なしのコンストラクタです。
デフォルトコンストラクタは、クラス内にコンストラクタが定義されていないときだけ、コンパイル時に自動生成されます。
下記のように、クラス内に引数ありのコンストラクタのみが定義されていて、引数なしでインスタンスを生成すると、コンパイルエラーになります。
1 2 3 4 5 6 7 8 |
class Test1 { Test1(int ver){} } public class Test { public static void main(String[] args) { Test1 test = new Test1(); } } |
コンストラクタはアクセス修飾子をつけることができます。
また、戻り値を指定したクラス名と同じメソッドは、コンストラクタではなく通常のメソッドとして解釈されます。
例)void Test1(int ver){}
インタフェースクラス
インタフェースは、抽象メソッドと定数のみを宣言することができます。
インタフェースクラスに変数を宣言すると、暗黙的にpublic static final の3つの修飾子が追加されます。
インタフェース内に宣言できるメソッドは、publicなabstractメソッドのみです。
省略した場合は、暗黙的にpublic abstract修飾子が追加されます。
なので、public以外のアクセス修飾子や、static、finalは指定できません。
つまり、実装クラスでオーバライドするメソッドには、必ずpublicをつけなければならない。
※オーバライドのメソッドでアクセス修飾子なしで問われる問題があります。当然コンパイルエラーが正解となります。
クラスの継承は1つしか継承できない。
インタフェースの継承は複数できる。
ただし、インタフェースクラスでインタフェースの継承は複数できる。
1 2 3 4 5 6 7 |
interface a {} interface b{ } interface c extends a, b{} abstract class aa{} abstract class bb{} abstract class cc extends aa implements a, b{} |
オーバライド
メソッドをオーバライドする際のアクセス修飾子は、スーパクラスと同じもの、または公開範囲が広いものにしなければならない。オーバライドされたメソッドは、サブクラスのメソッドが優先的に実行されます。
修飾子 | 概要 |
public | すべてのクラスからアクセス可能 |
protected | 同一パッケージか、protectedを宣言しているクラスを親にもつサブクラスからアクセス可能 |
なし | 同一パッケージからのみアクセス可能 |
private | クラス内からのみアクセス可能 |
カプセル化
カプセル化は、メンバ変数にはprivate、メソッドにはpublicを指定します。
ただし、インスタンスメソッドで外部に公開したくないものはprivate指定で定義してもカプセル化のルールには反しません。
カプセル化のメリットは、データを保護できることと、不変オブジェクトを設計できること。
スーパクラスをサブクラスへ代入できるのか?
スーパクラスを継承しているサブクラスをスーパクラスへ代入は可能ですが、逆の場合はどうなるのか?
コンパイルエラーになります。
しかし、サブクラスでキャストしてあげれば、コンパイルは通ります。
System.out.printlnに参照情報を渡すと
System.out.println();の引数に参照情報(インスタンス)を渡すと、対象オブジェクト内にあるtoString()メソッドが呼ばれる。
例)System.out.println(new StringBuilder(“Java”);
この例だと、Javaが出力されます。なぜなら、StringBuilderにはtoStringメソッドがあるからです。独自クラスのインスタンスを渡した場合は、そのクラスでtoString()をオーバライドしていなければ、文字列は出力されません。
abstract
abstractは、クラスとメソッドに指定可能。
抽象メソッドの定義は、アクセス修飾子 abstract 型 メソッド名(); となる。
abstract付きのメソッドに、{}の実装部分を定義すると、コンパイルエラーとなります。
例外クラスについて
例外には、コンパイラでチェックされる例外とチェックされない例外がある。
つまり、RuntimeExceptionをthrowしている場合は、catchしていなくてもコンパイルエラーにはなりません。ExceptionやIOExceptionなど、チェック例外をthrowしている場合は必ずthrows もしくは、catchする必要があります。
スーパクラスのメソッドに、throwsが指定されている場合、オーバライドメソッドには指定しなくてもコンパイルエラーにはなりません。指定する場合は、同じクラスかそのサブクラスが指定できます。
スーパクラスのメソッドにthrowsなし、サブクラスにthrowsをつけるとコンパイルエラーとなります。
ラムダ式
ラムダ式内からローカル変数へアクセス可能。ただし、実質的にfinalなローカル変数として扱える変数だけにアクセスができるというルールがある。
つまり、ラムダ式内で、ローカル変数の値を変更することはできない。ということ。
ローカル変数の値を変更するコードを書いていると、コンパイルエラーとなる。
引数が1つの場合は、()を省略できる。()を省略する場合は、型も省略して変数名のみにしないとコンパイルエラーとなる。
LocalDateTime
不変オブジェクトです。
LocalDateTime ld = LocalDateTime.now();
ld.plusDays(10);
System.out.println(ld);
こんな問題がでますが、不変オブジェクトなので、日付は+されません。