頭脳一式

人の記憶なんて曖昧なもの。すべての情報を頭に記憶するなんてナンセンス。困ったらここに来ればいいじゃん?というスタンスで最強のナレッジベースを目指すブログ

【OCJ-P】Java SE8 Silverに向けた対策-忘れやすい挙動まとめ

覚えきれない部分をまとめてみる。

第1章

パッケージ

パッケージの目的

  • 名前空間を提供し、名前の衝突を避ける。
  • アクセス修飾子と組み合わせてアクセス制御機能を提供する。
  • クラスの分類を可能にする。
アクセス修飾子 意味
public すべてのクラスからアクセスできる。
protected 同一パッケージに属する場合は継承関係に係わらずアクセス可能。
異なるパッケージに属する2つのクラスが継承関係にあるとき、publicと同じ働きをする。
異なるパッケージに属する2つのクラスが継承関係にないとき、privateと同じ働きをする。
default 同一パッケージ内のみアクセスできる。
private 同一クラス内のみアクセスできる。

クラス名が重複するのを避けるため、コンパイラJVMは「パッケージ名.クラス名」の完全修飾クラス名で扱っている。
パッケージはディレクトリ構造とマッピングしていて、
例えば「jp.co.xxx.sample」という完全修飾クラスは[jp]フォルダ内の[co]フォルダ内の[xxx]フォルダに存在するsampleクラスということになる。

  • パッケージ宣言ではクラス名を含まない。
  • パッケージに属さないクラスは存在しない。パッケージ宣言を省略すると無名パッケージとなる。
  • 無名パッケージに属するクラスは、同じ無名パッケージに属するクラスからしかアクセスできない。
    (明示的にパッケージ宣言したクラスからは、無名パッケージに属するクラスにアクセスできない。)

staticインポート

本来、staticなメソッドやフィールドは「クラス.フィールド名」や「クラス.メソッド名」
の書式で、どのクラスに定義されているものなのかを明示しなければならない。
これをフィールドやメソッド名だけで省略表記できるようにしたのがstaticインポート。
staticインポート宣言は以下のように完全装飾クラス名で記述する。

import static jp.co.xxx.sample.num;//sampleクラスのstaticなnum変数をインポート。
import static jp.co.xxx.sample.print;//sampleクラスのstaticなprintメソッドをインポート。

メソッドをstaticインポートするときはメソッド名だけ記述する。(()や引数は不要。)

  • 同名のフィールドメソッドを重複してstaticインポートすると、コンパイルエラーになる。
  • インポートしたクラスでインポートされたメソッドやフィールドと同名のものがあった場合には、そのインポートは無視される。

パッケージとstaticインポートのまとめ。

package pake1;

public class p1main {
    public static int num1 = 100;
}
package pake1;

public class p1main2 {
    public static int num2 = 200;
}
package pake2;
import static pake1.p1main2.*;
import pake1.p1main;
public class p2main {

    public static void main(String[] args) {
        System.out.println(p1main.num1);//100
        System.out.println(num2);//200
    }
}

エントリーポイント

クラス内に複数定義されたメソッドのうち、どのメソッドから処理を始めるのかを決めなくてはならない。
処理を始めるためのメソッドのことをエントリーポイントという。

public static void main(string[] args){

}

このうちプログラマが自由に決められるのは引数名の「args」の部分のみ。
エントリーポイントの引数にはString配列型だけでなく、可変長引数のString型を受け取ることもできる。

// 引数名を変えた例
public static void main(string[] hogehoge){ //処理  }  

// 可変長引数のString型に変えた例
public static void main(string... args){  //処理  }

第2章 データ型の操作

プリミティブ型と参照型の違いについては以下の記事にまとめた。

acompletesetofbrain.hatenablog.com

整数リテラルの記述について

8進数を表す場合は、「067」のように「0」を接頭辞として付ける。
16進数であれば「0x9F」のように「0x」を接頭辞として付ける。
2進数であれば「0b01010101」のように「0b」を接頭辞として付ける。

整数リテラルの表記について

桁数の多い数値リテラルの見やすさを向上するためにアンダースコア「_」を使った数値表記がJava SE7から導入されている。
表記方法については以下の制約がある。

  • リテラルの先頭と末尾には記述することができない。
  • 記号の前後には記述することができない。
  • 「.」「f」「L」「0b」「0x」など。

文字リテラルについて

説明
char型 文字リテラルは'c'のようにシングルクォーテーションで囲むこと。
String型 文字列リテラルは”str”のようにダブルクォーテーションで囲むこと。

JavaUnicodeを標準の文字コードとしている。
javaでは'\u30A2'のように「\u」の接頭辞の後ろに16進数で4桁を付け、シングルクォーテーションで囲む。
char型の変数に0~65535までの数値を代入することができる。

変数、メソッド、クラスなどの命名規約について

  • 予約語は使えない。(if for abstract等)
  • 記号のうち使えるのは「_」と通貨記号のみ。
  • 先頭に数字は使うことはできない。

ガーベジコレクション

使わなくなったオブジェクトを自動的に破棄してくれるJavaの便利な機能。
不要になったゴミ(ガーベジ)と回収(コレクション)するのでガーベジコレクションと呼ばれる。
強制的にガーベジコレクションを呼ぶためには以下の記述をする。

Object = null;
System.gc();

ガーベジコレクションの対象は、参照されなくなったインスタンス
コンパクション…インスタンスの破棄を繰り返すことで細切れになったメモリをまとめ、大きな空間を確保する機能。

第3章

型変換について

int型の数値をshort型やbyte型に代入する場合、その値が型の範囲内であれば、コンパイルエラーにはならない。
数値を演算するとき、左右のオペランドは同じ型でなければならない。
もしオペランドの型が異なる場合は、小さいほうの型は大きい方の型に自動的に変換される。
オペランドとは演算対象を指す。

同一性と同値性

同一性とは、複数のオブジェクトが同じインスタンスを参照していること。(同値性を兼ねる。)
同一性であるかどうかは「==」で演算子で判定する。
同値性とは、インスタンスは異なるが、同じ値を持っていること。
同値性であるかどうかはequalsメソッドを使って確認する。
但し、Objectクラスに定義されているequalsメソッドは同一性を確認するものになっている。
何を以って同値と判定するかはプログラマに委ねられているので、オーバーライドして確認する。

コンスタントプール

Stringのインスタンスは、文字列リテラルを記述するだけで作られる。

String a = 'sample';
String b = 'sample';
System.out.println(a == b); //trueが返る。

これはコンスタントプールという仕組みがあるため。 同じ文字リテラルは定数値としてインスタンスとは異なる定数用のメモリ空間に作られ、そこへの参照が String型変数に代入される。

但し、以下はfalseとなる。

Stirng a = new Stirng("sample");
String b = 'sample';
System.out.println(a == b); //falseが返る。

new演算子を使ってプログラマが明示的に「新しいインスタンスを作る」場合、その都度インスタンスが作られ
それぞれが異なる参照を持つ。
そのためオブジェクトaはインスタンス用のメモリ空間に作られたStringインスタンスへの参照が渡され、
オブジェクトbはコンスタントプールによって定数用のメモリ空間に作られたStringインスタンスへの参照が渡される。

新しいインスタンスでもfalseにならない場合。

例えnewしたとしても、後から値を代入して結果的に文字列が同じになる場合はコンスタントプールを使用する。

1: public static void main(String[] args) {
2: String a = "aaa";
3: String b = new String("");
4: b = "a"+"a"+"a";
5:
6: System.out.println(a == b);//同一性の確認(trueが返る)
7: System.out.println(a.equals(b));//同値性の確認(trueが返る)
8: }

第5章

for文

for文は、初期化文、条件文、更新文と3つの繰り返し処理で構成されている。

  • 複数記述できるのは初期化文と更新分の2つのみ。
  • 初期化文は同じ型であればカンマで区切って複数記述することができる。
     型宣言は最初の1つ目のみ記載する。
     (例えばint型とlong型の2つの変数を定義するとコンパイルエラーとなる。)
  • 条件文に複数の条件を記述したい場合は、論理演算子を使って複合条件の文にしなければならない。
  • 条件文と更新文は省略することができるが、省略するときでもセミコロンは必要。

while文で中カッコを省略した記述

中カッコを省略して記述した場合、繰り返しの対象となるのは、構文が開始された次の1行のみとなる。
次のコードは「A」を5回出力した後、「B」を1回出力する。

int cnt = 0;
while (cnt++  <5)
  System.out.println("A");
  System.out.println("B");

但し、do-while文で中カッコを省略した場合は、文を複数記述するとコンパイルエラーとなる。

int cnt = 0;
do
  System.out.println("A");//ここでコンパイルエラー。
  System.out.println("B");
while (cnt++  <5);

第6章

可変長引数

Java SE5から可変長引数が導入された。

  • 同じ型の可変な引数をまとめることができるだけで、異なる型はまとめられない。
  • 可変長引数以外の引数を受け取る場合は、可変長引数は最後の引数に記述しなければならない。
  • 可変長引数の宣言はデータ型の後ろに「...」を記述しなければならない、
    変数名の後ろに「...」をつけてもコンパイルエラーになる。
    可変長引数はJVMによって配列に置き換えられる。
    そのためその値を使うときは大カッコを使用する。

到達不可能なコード

return文の後ろの処理は実行できない。 return文を実行した後に何らかの処理をするようなコードを記述するとコンパイルエラーとなる。

void sample(int num){
  return;
  System.out.println(num);//ここでコンパイルエラー。
}

static領域とヒープ領域

staticで修飾されたフィールドやメソッドは「static領域」と呼ばれる領域に配置される。
それ以外のものは「ヒープ領域」と呼ばれる領域に配置される。
インスタンスが生成されるときは、ヒープ領域ににあるクラス定義に従ってインスタンスが生成される。

コンパイルエラー:あいまいなメソッド呼び出し。

次のコードはオーバーロードの条件を満たしている。
2つのint型を渡した呼び出しは、両方適用出来てしまう。
このような場合、JVMはどちらを呼び出していいかを判断できなくなってしまう。
そのため、JVMは「あいまいなメソッド呼び出し」としてコンパイルエラーを発生させる。

public class test {

    public static void main(String[] args) {
        test ts = new test();
        System.out.println(ts.calc(2, 3));//ここでコンパイルエラー。
    }

    double calc(double a,int b ){
        return (a+b)/2;
    }

    double calc(int a,double b ){
        return (a+b)/2;
    }
}

コンストラクタと初期化ブロック

すべてのコンストラクタで一部だけ共通の処理がしたい!そんなときは初期化ブロックを用いる。

  • 初期化ブロックはすべてのコンストラクタで共通する前処理を実現するために使用する。
  • コンストラクタのthisは一行目に記述しなければならない。thisより上に処理するコードがあるとコンパイルエラー。
  • コンストラクのsuper()は一行目に記述しなければならない。super()よりも上に処理するコードがあるとコンパイルエラー。

つまり、両方を同時に記述することはできないので、以下のコードはどちらもコンパイルエラーになる。

★1行目にsuper()で2行目にthisが来るパターン。

public class Kodomo extends Oya{
    Kodomo(int x){
        super();
        this(100,200);//コンパイルエラー。
    }

    Kodomo(int x,int y){

    }
}

★1行目にthisで2行目にsuper()が来るパターン。

public class Kodomo extends Oya{
    Kodomo(int x){
        this(100,200);
        super();//コンパイルエラー。
    }

    Kodomo(int x,int y){

    }
}

カプセル化

フィールドはprivateにする。 フィールドを扱うメソッドは必要なものをpublicにする。(必ずしもすべてをpublicにする必要はない。)

第7章 継承

問10~13は見直した方が良い。

ポリモーフィズム

第7章-問8

class A{
  String val = "A";
  void print(){
    System.out.println(val);
  }
}

class B extends A{
  String val = "B";
}
public class Main{
  public static void main(String[] args){
   A a = new A();
   A b = new B();
   System.out.println(a.val);
   System.out.println(b.val);
   a.print();
   b.print();
  }
}


答え:AAAA
フィールドを参照した場合は、変数の型で宣言された方を使う。
メソッドを呼び出した場合は、メソッド内の指示に従う。

インスタンスの中には、Aクラスから引き継いだメソッドとBクラスで定義したメソッドがある。
aはA型である。Aクラスの中にはBクラスで定義したメソッドがないのでこのままでは使えない。
使うにはアップキャストする必要がある。

A a = new B();
B b = (B)a;

インターフェース

インターフェース内のメソッド宣言ではヘッダ部分のみしか宣言できない。
処理本体を書くとコンパイルエラー。(中括弧{}含む。)
但し、Java SE8から追加されたstaticメソッド及びdefaultメソッドは処理本体を記述できる。

public interface A {
    void methodA(int x);//OK

    void methodB(int y){//この宣言はコンパイルエラー。

    }

    static int methodC(int z){//staticメソッドはOK
        return z;
    }

     default int methodD(int o){//defaultメソッドはOK
         return o;
    }
}

抽象クラス

抽象クラスとは、抽象メソッドと具象メソッドを保持できるクラスのこと。
抽象メソッドには以下の制約がある。

  • abstract宣言すること。
  • 実装を持つことが出来ないこと(中カッコもかけず、セミコロンで終わること。)

具象メソッド * 実装を持つことを強制される。(中カッコのなしのセミコロン閉じはNG)

抽象クラスもインターフェースと同じくメソッドの宣言ではヘッダ部分のみしか宣言できない。
処理本体を書くとコンパイルエラー。(中括弧{}含む。)

public abstract class aaa {
    abstract void methodA(int x);//OK

    abstract void methodB(int x){//コンパイルエラー。

    }
}

第8章

例外処理は以下にまとめた。

acompletesetofbrain.hatenablog.com

try-catch-finally文

第8章-問5

catchブロック内にreturnがあってもfinallyブロックが先に処理される。
処理される順番はcatch→finallyだけど、 returnによって呼び出し元に制御が戻る前にfinallyブロックが先に処理されるということ。

try{
    throw new NullPointerException();
} catch (NullPointerException e){
    return "A";
} finally{
    System.out.println("B");
}
return "C";


答え:BA

第8章-問6

catchブロックとfinallyブロックの両方がreturnをするときはfinallyの値で上書きされる。
(returnするときに戻り値を格納する専用の変数があり、catchブロックの処理を実行すると戻り値の値が10、 finallyブロックの戻り値の値が20に変わるため。)

try{
    throw new RuntimeException();
} catch (RuntimeException e){
    return 10;
} finally{
    return 20;
}


答え:20

第8章-問7

finallyブロックで値を変更しても変数valの値が変わるだけ(20になるだけ)で、戻り値用の変数の値は変わらない。 但し、これはプリミティブ型の場合の話で、参照型の場合は、戻り値用の変数も同じインスタンスを持つため、finallyブロックで値を書き変えることが可能。

int val = 0;
try{
  String[] array = {"A","B","C"};
  System.out.println(array[3]);
}catch (RuntimeException e){
    val = 10;
    return val;
}fially{
    val += 10 ;
}


答え:10
finally文が実行されないのは以下のどれかのパターンに該当するとき

  • JVM又はOSがクラッシュしたとき
  • tryブロックやcatchブロックでsystem.exitメソッドを呼び出してアプリケーションを強制終了させたとき。

起動パラメータなしでargsを使用した場合の挙動

3行目:要素が0なので0が出力される。
4行目:存在しない要素1にアクセスしたので「java.lang.ArrayIndexOutOfBoundsException」が発生する。

1: public static void main(String[] args) {
2:
3:   System.out.println(args.length);
4:   System.out.println(args[0]);
5:
6: }

staticイニシャライザ

インスタンスが生成されるタイミングではなく、クラスがロードされたタイミングに一度だけ呼び出される初期化ブロック。
staticイニシャライザを使用することでstatic変数の初期化が可能になる。

第9章

Stringクラス

可変なオブジェクトのことを「mutable(可変)」という。
不変なオブジェクトのことを「immutable(不変)」という。
Stringオブジェクトは不変なオブジェクトである。

メソッド名 意味
indexOf() 引数で指定した文字が文字列のどの位置に存在するかを調べるメソッド。
substring() 指定した引数を添え字として任意の文字列を抽出するメソッド。
replace()  文字列の先頭から末尾まで、指定された文字でヒットすればそれに置き換える。
replaceAll() 置換した結果の文字列を持った新しいStringインスタンスを生成し、そのインスタンスへの参照を返す。
trim() 引数で指定した文字列の前後にある空白を除去するメソッド。空白としてspace、tab、改行などが対象となる。
startWith() 指定した引数で始まっているかを真偽値で返すメソッド。
endWith() 指定した引数で終わっているかを真偽値で返すメソッド。

StringBuilderのappnedメソッドはオーバーロードさまざまな種類の引数を受け取ることができる。

受け取れる型 要点
プリミティブ型 プリミティブ型の値は全て文字列型に変換される。
真偽値のtrueなら"true"、整数値の10は"10"になる。
String型 StringクラスのスーパクラスであるCharSequence
char配列 -
オブジェクト toStringメソッドはObjectクラスに定義されたものが呼び出されるので
オーラーライドする必要がある。

insertメソッドとdeleteメソッドの例※指定したindexに値が挿入(削除)され後ろがずれる。

StringBuilder sb = new StringBuilder("ABCDE");
sb.insert(2,"o");
System.out.println(sb);//ABoCDE
sb.delete(2,3);
System.out.println(sb);//ABCDE

deleteメソッドはindexを渡して値を削除するところに気をつける。indexの2番目から1文字消したいからといって下のようなコードを記述すると実行時エラーになる。

sb.delete(2,1);

LocalDateクラス

LocalDateクラスは日付を扱うためのクラス。

  • 月は1から始まる。
  • 不変(immutable)オブジェクトである。
メソッド名 意味
now() 現在の日時でインスタンスを生成する場合
Of() 日付を指定してインスタンスを生成する場合
parse() 文字列形式をLocalDateクラスに変換する場合DateTimeFormatterを指定することもできる。
LocalDate a = LocalDate.now();//「2017-07-17」
LocalDate b = LocalDate.of(2017,7,17);//「2017-07-17」
LocalDate c = LocalDate.parse("2017-01-17");//「2017-07-17」
LocalDate d = LocalDate.parse("2017-01-17",DateTimeFormatter.ISO_LOCAL_DATE);//「2017-07-17」

LocalTimeクラス

LocalTimeクラスは時間を扱うクラス。

  • 不変(immutable)オブジェクトである。
  • 24時間で扱い、午前/午後は区別しない。
  • LocalDateクラスと同じく、ofメソッド/nowメソッド/parseメソッドがある。
LocalTime t1 = LocalTime.now();//00:52:15.417
LocalTime t2 = LocalTime.of(0,1,2);//00:01:02
LocalTime t3 = LocalTime.parse("00:10:20");//00:10:20
LocalTime t4 = t3.plusHours(12);//t3の値は「00:10:20」のまま。
//t4の値は「12:10:20」になる。

Durationクラスは時刻の差を扱うクラス。 Durationは”期間”という意味。 betweenメソッドは2つの日時の差を計算し、Durationのインスタンスとして戻すメソッド。

LocalDateTimeクラス

  • LocalDateとLocalTimeの両方の特徴を持ち、日付だけ/時刻だけ/日時を扱えるクラス。

periodクラス

periodクラスは日付の差を扱うクラス。

  • untilメソッドは日付の差を計算するメソッド
  • minusメソッドは日付を変更するためのメソッド。