Categories: Java

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インポート宣言は以下のように完全装飾クラス名で記述する。

Java

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


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


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

Java

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

Java

package pake1;
public class p1main2 {
    public static int num2 = 200;
}

Java

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
    }
}

エントリーポイント

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

Java

public static void main(String[] args){
    //処理
}

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

Java

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

第2章 データ型の操作

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

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

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

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

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

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

文字リテラルについて

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

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

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

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

ガーベジコレクション

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

Java

Object = null;
System.gc();

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

第3章

型変換について

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

同一性と同値性

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

コンスタントプール

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

Java

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

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

但し、以下はfalseとなる。

Java

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

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

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

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

Java

public static void main(String[] args) {
   String a = "aaa";
   String b = new String("");
   b = "a"+"a"+"a";

   System.out.println(a == b);//同一性の確認(trueが返る)
   System.out.println(a.equals(b));//同値性の確認(trueが返る)
}

第5章

for文

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

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

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

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

Java

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

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

Java

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

第6章

可変長引数

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

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

到達不可能なコード

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

Java

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

static領域とヒープ領域

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

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

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

Java

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が来るパターン。

Java

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

    Kodomo(int x,int y){
    }
}

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

Java

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

    Kodomo(int x,int y){
    }
}

カプセル化

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

第7章 継承

ポリモーフィズム

第7章-問8

Java

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

Java

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

答え:AAAA

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

MainクラスでA型のオブジェクトとして変数aがAクラスのインスタンス、
変数bがBクラスのインスタンスを持っています。

BクラスにもvalフィールドやAクラスから継承したprintメソッドがありますが
b.valb.printを実行すると、「A」が出力されます。
これはなぜかというとvalフィールドやprintメソッドを親クラスであるAクラスで定義しているためです。
親クラスと子クラスで同名のフィールドが定義されている場合、どちらが使用されるかは宣言時の型によって決まります。
変数bはA型なので、b.valb.printを実行すると「A」が出力されるというわけです。

インターフェース

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

Java

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)

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

Java

public abstract class aaa {
    abstract void methodA(int x);//OK
    abstract void methodB(int x){//コンパイルエラー。
    }
}

第8章

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

try-catch-finally文

第8章-問5

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

Java

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に変わるため。)

Java

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

答え:20

第8章-問7

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

Java

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」が発生する。

Java

public static void main(String[] args) {
    System.out.println(args.length);
    System.out.println(args[0]);
}

staticイニシャライザ

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

Java

static{
    salaryMap.put(SampleConst.PRESIDENT,SampleConst.PRESIDENT_SALARY);
    salaryMap.put(SampleConst.DIRECTOR,SampleConst.DIRECTOR_SALARY);
    salaryMap.put(SampleConst.EMPLOYEE,SampleConst.EMPLOYEE_SALARY);
}

第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に値が挿入(削除)され後ろがずれる。

Java

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文字消したいからといって下のようなコードを記述すると実行時エラーになる。

Java

sb.delete(2,1);

LocalDateクラス

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

  • 月は1から始まる。
  • 不変(immutable)オブジェクトである。

メソッド名 意味
now() 現在の日時でインスタンスを生成する場合
Of() 日付を指定してインスタンスを生成する場合
parse() 文字列形式をLocalDateクラスに変換する場合DateTimeFormatterを指定することもできる。

Java

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メソッドがある。

Java

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メソッドは日付を変更するためのメソッド。


以上で記事の解説はお終い!

もっとJavaやSpringを勉強したい方にはUdemyがオススメ!同僚に差をつけよう!

shiakisudev

View Comments

  • 7章の問8の解説
    クラスBはメソッドは定義されていませんよね…?
    あとメインメソッドの解説のところ
    Stringが小文字だと思います

  • wataru様
    ご指摘ありがとうございます。
    読み返したら問題文と関係ない説明文になってました(´;ω;`)
    解説文を修正いたしました。
    また、stringと小文字になっていた箇所も大文字に修正しました。

  • 頭脳一式さん
    上記でゲストログインしていたwataruです。
    記事の修正ありがとうございます。いつも参考にしております!
    これからも応援しています!