はじめに
私事だがJava SE11 Silverの試験をもうすぐ受ける。
個人的につまづきやすい部分や、「どういう挙動だっけ?」と
分からなくなるときがあるため備忘録として残す。
細かい説明は割愛
第1章:簡単なJavaプログラムの作成
クラスの実行コマンド(-cp)
カレントディレクトリ以外にあるクラスファイルを実行
java -cp build ex.Main
import
test.b.Sample.class
のクラスパスをbin/test/b/Sample.class
としたとき、
test.b.Sample.class
のimport文は…
- import test.b.Sample;
- import test.b.*;
binはクラスファイルの配置場所であり、
配置場所はコンパイル時や実行時にクラスパスで指定するものであり、
インポート宣言で指定するものではない
第2章:Javaの基本データ型と文字列操作
a = a * a;
public class Sample {
public static void main(String[] args) {
int a = 3;
a = a * a;
System.out.println(a); //9
}
}
varの型推論
varの型推論はローカル変数にのみ使える
フィールドの宣言やメソッドの引数・戻り値に使うのはNG
public class Sample1{
private static var num1 = 10; //コンパイルエラー
public static void main(String[] args) {
var num2 = 10; //エラーなし
}
private static void method(var num3) { //コンパイルエラー
var num4 = 10; //エラーなし
}
}
varの型推論が使えない変数への代入
- var a;
- 初期化する際に型推論を行うため
- var a = null;
- var a = 1, b = 2;
- var a = {1, 2, 3};
- int[] a = {1, 2, 3}; <- 変数の型からint配列型のインスタンスを生成している
- ラムダ式: var a = ()->{};
varの型推論が使える意外な例
- var a = new ArrayList<>();
- a.forEach(b->System.out.println(b)); <-bはObject型となる
final修飾子は変数宣言と同時に
final修飾子は定数を表すものだが、
変数宣言と同時に値を代入する必要はなさそう
以下のような記述でもエラーは起こらない
class A{
final int num;
A(){
num=10;
}
}
ただし、一度設定した値は変更できないため、
以下のようなコードはダメ
class A{
final int num;
A(){
num=10;
}
A(int num2){
this();
num = num2; //The final field num may already have been assigned
}
}
第3章:演算子と判定構造
charと数値の互換性
public class Sample{
public static void main(String[] args) {
char c1 = 65;
System.out.println(c1); //A
short c2 = (short)'A';
System.out.println(c2); //65
int n = 100;//(byte, short, longも同様)
// char c3 = n; //コンパイルエラー
char c4 = (char)n;
System.out.println(c4); //d
double d = 100;
char c5 = (char)h;
System.out.println(c5); //d
}
}
整数型・実数型の互換性
public class Sample{
public static void main(String[] args) {
byte a1 = 100;
short a2 = 100;
int a3 = 100;
long a4 = 100;
double b1 = a1;
double b2 = a2;
double b3 = a3;
double b4 = a4;
float c1 = a1;
float c2 = a2;
float c3 = a3;
float c4 = a4;
//int a = b1; //コンパイルエラー
//long b = c1; //コンパイルエラー
}
}
後置インクリメント
public class Sample {
public static void main(String[] args) {
System.out.println(n()); //0
}
private static int n() {
int x=0;
return x++;
}
}
他にも…
public class Sample{
public static void main(String[] args) {
int num = 10;
System.out.println(num++ + "," + num); //10,11
}
}
第4章:制御構造
到達不可なコードはコンパイルエラー
public class Sample{
public static void main(String[] args) {
for(;;) {
if(true) {
continue;
System.out.println("a"); //Unreachable code
}
if(true) {
break;
System.out.println("b"); //Unreachable code
}
}
}
}
第5章:配列の操作
配列の初期化
OKなもの
- int[] array = new int[]{};
- int[] array; array = new int[]{};
NGなもの
- int[] array = new int[]; array = {1, 2, 3};
- 初期化子は変数宣言とセットでのみ使える
- 以下のような記述はOK(インスタンスを再生成するため、最初に確保した要素数の縛りはない)
- int[] array = new int[3];
- array = new int[]{1, 2, 3, 4};
配列は変数宣言時(インスタンス生成時)に初期化される
public class Sample1{
public static void main(String[] args) {
int[] array1 = new int[3];
double[] array2 = new double[3];
char[] array3 = new char[3];
boolean[] array4 = new boolean[3];
String[] array5 = new String[3];
Integer[] array6 = new Integer[3];
Double[] array7 = new Double[3];
for(int i=0;i<3;i++) {
System.out.print(array1[i]+","+array2[i]+","+array3[i]+","+array4[i]+","+array5[i]+","+array6[i]+","+array7[i]+",");
System.out.println();
}
}
}
//0,0.0, ,false,null,null,null,
//0,0.0, ,false,null,null,null,
//0,0.0, ,false,null,null,null,
配列のコピー(同一性)
System.arraycopy()
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
- src:コピー元の配列、
- srcPos:コピー元のどの位置からコピーを開始するか
- dest:コピー先の配列
- destPos:コピー先のどの位置からコピーを開始するか
- length:コピーする要素数
public class Sample1 {
public static void main(String[] args) {
char[] array = "Hello".toCharArray();
char[] array2 = new char[array.length];
System.arraycopy(array, 0, array2, 0, array.length);
System.out.println(array2); //Hello
System.out.println(array == array2); //false
}
}
clone():1
配列のコピーをするには、clone()
や変数の代入を用いる
public class Sample1{
public static void main(String[] args) {
int[] arrayA = new int[] {1, 2, 3};
int[] arrayB = arrayA.clone();
int[] arrayC = arrayA;
System.out.println(arrayA == arrayB); //false
System.out.println(arrayA == arrayC); //true
System.out.println(arrayB == arrayC); //false
}
}
clone()
はインスタンスの複製であり、そのインスタンスへの参照値がarrayBに格納されるため、
arrayAとarrayBは異なったインスタンスである。
arrayC = arrayA
については、arrayCにarrayAインスタンスへの参照値が入るため、
同じインスタンスを共有することになる(同じ参照値である)。よって同一。
clone():2
public class Sample1{
public static void main(String[] args) {
int[][] arrayA = new int[][] {{1, 2}, {1, 2}, {1, 2, 3}};
int[][] arrayB = arrayA.clone();
int[][] arrayC = arrayA;
System.out.println(arrayA == arrayB); //false
System.out.println(arrayA == arrayC); //true
System.out.println(arrayB == arrayC); //false
}
}
第6章:インスタンスとメソッド
一見コンストラクタに見えても、戻り値がついていれば普通のメソッド
public class Sample{
Sample(){ //コンストラクタ
}
void Sample(){ //メソッド
}
public static void main(String[] args) {
Sample1 s = new Sample1(); //Sampleコンストラクタが呼び出される
s.Sample1(); //void Sample()が呼び出される
}
}
コンストラクタと初期化子
初期化子とは、インスタンスが生成されたタイミングで行われる処理。
処理順はコンストラクタよりも早い。
public class Sample{
{
System.out.println("A");
}
Sample(){
System.out.println("B");
}
Sample(int num){
System.out.println("C");
}
{
System.out.println("D");
}
public static void main(String[] args) {
new Sample();
new Sample(1);
}
//A
//D
//B
//A
//D
//C
}
↓ほかの例
public class Sample1{
Sample1(){
this(1);
System.out.println("A");
}
Sample1(int num){
System.out.println("B");
}
{
System.out.println("C");
}
public static void main(String[] args) {
new Sample1();
}
//C
//B
//A
}
staticについて
static初期化子
クラスのロード時に行われる処理
static {
int num = 10;
}
staticな変数の初期値
public class Sample{
static int num1;
static String str1;
int num2;
String str2;
public static void main(String[] args) {
System.out.println(num1); //0
System.out.println(str1); //null
System.out.println(num2); //コンパイルエラー
System.out.println(str2); //コンパイルエラー
}
}
staticなメソッドからインスタンス変数へはアクセスできない
public class Sample1
int n; //インスタンス生成タイミングで0で初期化される
void a() {
n++;
}
public static void main(String[] args) {
System.out.println(n); //コンパイルエラー
}
}
第7章:クラスの継承・インタフェース・抽象クラス
スーパークラスのメソッド呼び出し注意点
スーパークラスのメソッドをサブクラスで呼び出す際
super.メソッド名
で呼び出すが、1つ上のものに対してのみ有効。
例えば、
super.super.メソッド名
のような記述はできず、2つ上のクラスのメソッドは呼び出せない。
共変戻り値
オーバーライドする際、戻り値はスーパークラスと同じものでなければならないが、
サブクラスのものでも可能
class A{
Object method() {
return null;
}
}
class B extends A{
Number method() {
byte n = 10;
return n;
}
}
class C extends B{
Integer method() {
int n = 10;
return n;
}
}
スーパークラスのコンストラクタ呼び出しの重複
class SampleA{
public SampleA(int num){
System.out.println("A");
}
}
class SampleB extends SampleA{
public SampleB(){ //コンパイルエラー
System.out.println("B");
}
public SampleB(int num){
super(num);
this(); //コンパイルエラー
}
}
public class SampleC{
public static void main(String[] args) {
new SampleB();
new SampleB(10);
}
}
new SampleB();のとき
スーパークラスと継承関係にあるため、public SampleB()で暗黙的にsuper()が呼び出されるが、
該当コンストラクタがないためエラー
new SampleB(10);のとき
public SampleB(int num)でsuper(int num)が呼び出した後、
this()でpublic SampleB()・ないしsuper()を呼び出すため、
スーパークラスのコンストラクタを2回呼び出すことになる。
Constructor call must be the first statement in a constructor
というエラーが示すように、
スーパークラスのコンストラクタは重複した呼び出しはできない。
なお、以下のようにすればエラーは起こらない
class SampleA{
public SampleA(){ //追加
System.out.println("A");
}
public SampleA(int num){
System.out.println("A");
}
}
class SampleB extends SampleA{
public SampleB(){
System.out.println("B");
}
public SampleB(int num){
this(); //super()削除 *このとき、super()は記述されていないがもちろん呼び出されない
}
}
抽象クラスには具象メソッドのみでもOK
abstract class A{
void method() {}
void method2() {}
}
抽象クラスにおけるアクセス修飾子
public abstract class Sample3 {
public abstract void method1();
protected abstract void method2();
abstract void method3();
private abstract void method(); //この行のみコンパイルエラー
}
抽象クラスはオーバーライド前提のクラスであるため、
同一クラス内からのみアクセス可能であるprivate修飾子を付けることはできない
(オーバーライドは異なるクラスを継承するものである)
オーバーライドとオーバーロード
class SampleA {
public void method() {
System.out.println("A");
}
}
class SampleB extends SampleA {
//SampleAのmethod()をオーバーライドしている
public void method1() {
System.out.println("B");
}
//シグニチャが異なるため、オーバーロード
public void method1(String s) {
System.out.println("C");
}
}
public class SampleC extends SampleB {
public static void main(String[] args) {
SampleA a = new SampleB();
a.method(); //B
a.method("hello"); //コンパイルエラー
}
}
コレクションの修飾子
コレクションに対しても普通に修飾子をつけることができる
private int[] array = new int[3];
public List<String> list = new ArrayList<>();
アクセス修飾子とオーバーライド
class SampleA {
public void method() {
System.out.println("A");
}
public void a() {
method();
}
}
class SampleB extends SampleA {
public void method() {
System.out.println("B");
}
public void b() {
method();
}
}
public class SampleC {
public static void main(String[] args) {
SampleB s = new SampleB();
s.method(); //B
s.a(); //B
s.b(); //B
}
}
SampleAクラスのmethod()のアクセス修飾子をprivateに変えると…
class SampleA {
private void method() {
System.out.println("A");
}
public void a() {
method();
}
}
class SampleB extends SampleA {
public void method() {
System.out.println("B");
}
public void b() {
method();
}
}
public class SampleC {
public static void main(String[] args) {
SampleB s = new SampleB();
s.method(); //B
s.a(); //A
s.b(); //B
}
}
継承において、privateメンバ・コンストラクタは引き継がれないため、
SampleBクラスのmethod()はオーバーライドされず、別のメソッドとして定義されることになる。
インターフェースの多重継承
interface Sample1 {
void a();
}
interface Sample2 {
void b();
}
interface Sample3 extends Sample1, Sample2 {
void c();
}
public class Sample4 implements Sample3{
@Override
public void a() {
}
@Override
public void b() {
}
@Override
public void c() {
}
}
Javaでは原則として多重継承は禁止だが、
インターフェースからインターフェースへの継承に限っては多重継承OK
スーパークラスのデフォルトメソッドを呼び出す
スーパーインターフェース.super.メソッド
interface A{
default void method() {
System.out.println("A");
}
}
interface B extends A{
default void method() {
A.super.method();
System.out.println("B");
}
}
public class Sample1 implements B{
public static void main(String[] args) {
A a = new Sample1();
a.method();
//A
//B
}
}
ただし、呼び出せるのは1つ上のデフォルトメソッドのみで、
2つ以上うえのデフォルトメソッドは呼び出せない↓↓
interface A{
default void method() {
System.out.println("A");
}
}
interface B extends A{
default void method() {
A.super.method();
System.out.println("B");
}
}
class C implements B{
public void method() {
A.super.method(); //コンパイルエラー
System.out.println("B");
}
}
デフォルトメソッドのオーバーライドの注意点
java.lang.Objectクラスに定義されているメソッドは
デフォルトメソッドでオーバーライドできない
interface A{
default String toString() {
return "A";
}
}
A default method cannot override a method from java.lang.Object
第8章:関数型インターフェース・ラムダ式
メソッド参照
import java.util.List;
class Sample{
public static void print(Integer num) {
System.out.println(num);
}
}
public class Sample1{
public static void main(String[] args) {
List<Integer> list = List.of(1, 2, 3);
list.forEach(Sample::print);
}
}
//1
//2
//3
再利用しない簡単な処理はラムダ式を使うが、
関数型インターフェースの実装に、ラムダ式でなく既存のメソッドを使う場合はメソッド参照を用いる
第9章:API
Math.round()
int Math.round(float)
long Math.round(double)
:小数点第一位を四捨五入し、小数点以下を丸める(例:2.5 -> 3, 250.3 -> 250)
System.arraycopy();
日付関係
DateTimeFormatter
【DateTimeFormatterクラスの定数の例】
| フォーマッター | 例 |
|:-:|:-:|
| BASIC_ISO_DATE | 20210515 |
| ISO_DATE | 2021-05-15 |
| ISO_DATE_TIME | 2021-05-15T00:00:00 |
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Sample1 {
public static void main(String[] args) {
LocalDateTime time = LocalDateTime.of(2021, 5, 15, 0, 0);
String str = time.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(str); //2021-05-15T00:00:00
}
}
Duration
Duration d = Duration.between(start, end)
時刻の差を扱う
Period
Period p = Period.between(start, end)
Period p = localDate.until(target)
日付の差を扱う
List<>
add():注意点
add(int index, String element)
で指定した位置に要素を挿入することができるが、
範囲外に挿入しようとすると例外が発生する。
public class Sample1{
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add(2, "B");
list.add("c");
list.add("d");
System.out.println(list);
}
//例外:Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 2, Size: 1
}
contains()
boolean contains(Object o)
import java.util.List;
public class Sample1 {
public static void main(String[] args) {
List<String> list = List.of("a", "b", "c");
System.out.println(list.contains("b")); //true
System.out.println(list.contains("x")); //false
}
}
ラムダ式関連
forEach()
void forEach(Consumer<? super T> action)
import java.util.List;
public class Sample1 {
public static void main(String[] args) {
List<String> list = List.of("a", "b", "c");
list.forEach(s -> System.out.println(s));
}
}
//a
//b
//c
removeIf()
public boolean removeIf(Predicate<? super E> filter)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Sample1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(
Arrays.asList(new String[] { "a", "b", "c" })
);
if(list.removeIf(s -> s.equals("b"))) {
System.out.println(list);
}
}
}
sort()
void sort(Comparator<? super E> c)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Sample1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(
Arrays.asList(new String[] { "c", "a", "b" }));
list.sort((s1, s2) -> s1.compareTo(s2));
System.out.println(list); //[a, b, c]
list.sort((s1, s2) -> -s1.compareTo(s2));
System.out.println(list); //[c, b, a]
}
}
第10章:例外処理
エラー・例外の簡単な分類
throws宣言について
throws宣言はあくまで、
「この例外・エラー(またはそのサブクラス)を投げる可能性がありますよ~」
という意味なので、非検査例外であれば、呼び出し元のメソッドで対処する必要はない
public class Sample1
public static void main(String[] args) {
try {
method(0);
}catch(IOException e) {
}
}
private static void method(int num) throws IOException, IndexOutOfBoundsException {
if(num==10)throw new FileNotFoundException();
else throw new IndexOutOfBoundsException();
}
}
- IOExceptin()->検査例外
- IndexOutOfBoundsException()->非検査例外
なお、実際にはFileNotFoundException()
をthrowしているが、
前述したようにthrows IOException
と宣言しているため、IOExceptionをcatchする必要あり
ClassCastException()
- 非検査例外
- インスタンスの型変換ができなかったときに発生
- プリミティブ型(int, char, doubleなど)の型キャストでは発生しない
UnsupportedOperationException()
- 非検査例外
- 要求されたオペレーションがサポートされていないことを示す
- List.of()などで生成した固定のリストを変更(追加や削除)しようとしたら発生する
ExceptionInInitializerError()
- エラー
- staticイニシャライザ内で問題が発生したときに起こるエラー
第11章:モジュールシステム
コマンドラインからの操作
java --list-modules
現在の実行環境で参照可能なモジュールの取得
コンパイル
javac -d <クラスの生成位置> <コンパイルするファイル>
javac -d out foo\XTest.java
-d クラスの生成位置の指定
--module-path <モジュールのルートディレクトリ>
javac -d out --module-path out src\module-info.java src\app\Main.java
--module-path(または-p)アプリケーション・モジュールを検索する位置の指定
実行
java --module-path out\foo;out\client --module client/app.Main
--module(または-m)モジュール名とエントリポイントとなるクラスの指定
モジュールの設定情報を調べるためのコマンド
java --module-path out --describe-module foo
--describe-module
jmod describe
クラス・メソッド・モジュールの依存関係を調べるコマンド
jdeps --list-deps out\foo
java --show-module-resolution
おわりに
黒本のコードや解説を参考にまとめています
【結果】
無事合格できました。
受けた感想としては、黒本やってて良かったなーという感じです笑
黒本の末章には総まとめ問題が2つありますが、
似たような問題が多かったです。
何なら全く同じ問題もいくつかあった気がするので、
黒本の総まとめ問題をやっていれば2,3割ぐらいはサービス問題ですね。
とりあえず1週間ぐらいはゆっくり過ごして、
その後はまたJava関連の勉強をしようかな~という感じにしたいと思います。