2
2

More than 3 years have passed since last update.

JavaBronzeに出てくる「変なJava」と忘れがちなJava知識の復習

Last updated at Posted at 2020-05-02

入門書に無く、しかも調べにくい、見慣れないJava知識について、初心者なりに調べたのでまとめます。

はじめに

昨年6月にスイッチが入り、知識ほぼ0から昨年10月の基本情報処理技術者に合格しました。

その際にJavaの入門書も触れていて、Javaも比較的得点していたので

「オブジェクト指向もばっちり!春に会社からJavaBronze受けさせられるらしいけど、余裕でしょ!」

と調子乗ったら、模擬試験で50%しか取れなくて反省してます。どう考えても自分の復習不足。

(でも仕方ないよね、全然知らないJava知識まで出たし...)

ということで、Javaの復習も兼ねて、入門書の簡単なおさらいと、模擬試験で知らなかったJavaについてまとめます。

参考

特に記載のない情報については、以下を参考にしました。

また初心者なので、間違っている箇所があれば、ぜひご指摘ください。


1. String args[]とString[] args

模試の1問目からこれはハード過ぎません?

正しいmain()メソッドを2つ選択する問題。これは調べるのに苦労したことの1つ。

結論から言えば、下2つはどちらも同じ意味。

public void main(String[] args){}
public void main(String args[]){}

参考に載せたIBMの記事によれば、

Java 言語での配列の宣言は、以下に示す C 言語の場合とほとんど同じです。
ただし、Java 言語では配列の括弧を変数名の後に続けるのではなく、以下に示すように型の後に続ける構文を使用することもできます。

として、下コードに示す2つの事例を出している。

int k[];
double temperature[];
String names[];
int[] k;
double[] temperatures;
String[] names;

C言語は前者が主流で、Javaでも前者のようにできるものの、後者が多いとのこと。知らなかった...

2. コンパイルエラーと実行時エラーの違いは?

これは基本情報処理技術者では(確か)あまり問われなかった内容で、

自分の基本情報ノートにも書き留めてなかった内容なので、今回改めて確認。

コンパイルエラー

コンパイルに失敗したエラー。文法上のミスに起因する。ありがちな例は下記。

//---------------------------------------------
// 1. ifやelseの条件式ミス
if{
    System.out.println("条件式の記述欠落");
} else(i>3){
    System.out.println("elseに条件式はつけられない");
}
//---------------------------------------------
// 2. あるべきものの欠落
// doの内容は省略できない
do while(i>=0);
//---------------------------------------------
// 3. 全く継承関係のないオリジナル型のキャスト
Ramen r = new Ramen();
Book b = (book)r;
//---------------------------------------------
// 4. 抽象クラスのabstract指定欠落
class NewRamen{
    // abstractメソッドがあれば、必ずclassもabstract
    // また、インスタンス作成が禁止される
    public abstract void MakeSoup(Dashi dashi);
}
//---------------------------------------------

実行時エラー

文法ミスはないが、思い通りに実行できないエラー。ありがちな例を列挙。

  • 0で除算する
  • 配列の範囲外の要素へアクセスする
  • 存在しないファイルを開く
  • アクセスレベルがおかしい
  • 親クラスの実体を子クラスへキャストする
    • 継承関係があればコンパイルは通ってしまう

まとめ

  • 文法エラーがあればコンパイルに失敗するので、コンパイルエラーが起きる。
  • 文法ミスはないが、動作に失敗する場合は実行時エラーが発生する。

3. 識別子として使用可能なものは?

軽く読み流してしまっていたせいで得点出来なかった。ここで改めて勉強。

識別子とは

変数やクラス名などの命名時に使用可能な文字や数字の並びのこと

使用可能なもの

  • 半角英字(小文字・大文字)
    • 大文字小文字の違いは区別される
  • 数字
    • 最初の1文字目に数字はNG
  • アンダースコア「_」
    • 別名:アンダーバー
  • ドル記号「$」
    • ほぼ使われないらしい...?情報不足。
  • 全角英数字・全角ひらがな・漢字その他
    • 使えないこともないらしい
    • 非推奨

その他ルール・慣習

試験とは関係ないものもあるものの、念の為確認。

  • 予約語は使えない
  • 変数名は「myAge」など小文字で
  • 定数名は「MAX_COUNT」など大文字で
    • どちらも慣習的なもの
  • 公式リファレンスの影響で、命名規則にcamelCaseが多い
    • snake_caseも定数などで見かける

NGな変数の例

試験に以下のような変数が出たら、それはコンパイルできない

  • 3coins
  • Takanawa-Gateway
  • #成人式
  • IdolM@ster
  • :thinking_face:
    • ちなみにthinking_face:はラベルとしてなら可能

OKな変数の例

次のような変数名は正しい。

  • $tmp
  • _socialDistance
  • Madoka$Magica
  • JAVAたのしい

4. インクリメントの前置と後置の違いは?

このあたりも出がち。違いは評価と計算の順序。

前置

値を返す前に、操作を行う。

int a = 10;
int b = ++a;
System.out.println(a); /* 11 */
System.out.println(b); /* 11 */

後置

値を返した後に、操作を行う。

int a = 10;
int b = a++;
System.out.println(a); /* 11 */
System.out.println(b); /* 10 */

問題例

下のようなもの、模試に出ました。

int i = 1;
if(i<i++){
    System.out.println("i<i++は必ずfalseを返す");
} else{
    System.out.println("でも条件判定後にi++はされている")
}
System.out.println(i); /* 2 */

5. 「|」と「||」,「&」と「&&」の違いは?

||と&&

  • または、かつ
  • 短絡評価を行う
    • 例えば、 true || (false && false) なら「true||」の時点で答えが確定するので、評価を止めるイメージ
  • なお、OR,ANDとは表記不可

|と&

  • または、かつ
  • 短絡評価を行わず、最後まで評価を続ける

まとめ

短絡評価するか否かの差。基本は同じ。

おまけの「^」

  • a^b
  • aとbが等価ならtrue、そうでなければfalse

(追記)ビット演算子

  • &や|,^はビット演算子として主に使われる
    • それぞれ、ビット演算のAND,OR,XORに対応
  • 論理演算子は特に理由がなければ、「&&」「||」「!」(NOT)などを使うことを推奨

6. char型をint型にキャストできる理由は?

内部的にchar型がUnicodeの数値だから。

詳しい記事はこちら

7. publicのクラスとソースファイルの関係は?

入門書の復習。

  • 1ソースファイルには、publicクラスは最大1つまで
    • 全て非publicクラスでも行ける(一般的ではない)
  • ソースファイル名はpublicクラスと同じにする
    • 全て非publicクラスの場合はソースファイル名自由

8. コンストラクタに指定できるものは?

入門書の復習と、細かな仕様の確認。

コンストラクタとは

  • そのクラスが作成された直後に、自動的に呼び出される、特別なメソッド
    • プログラマが直接呼び出すものではない
    • コンストラクタが他コンストラクタを呼び出すのは可能
    • メソッド名がクラス名と完全に等しい必要がある
  • 特別なので、他メソッドと扱いが異なる
    • 静的(static)、final、戻り値、abstractを指定できない
    • 継承できない
  • オーバーロードは可能
  • 全てのアクセス修飾子を付与できる(privateなど)
    • が、大半は省略する(package private)
  • 1つもコンストラクタが定義されていなければ、コンパイラが自動付与する
    • 「デフォルトコンストラクタ」
    • 引数なし、処理内容なし

NGな例

abstract class Ramen{
    // 抽象も静的もNG
    static abstract Ramen()
}
class Ramen{
    // finalや戻り値はNG
    final Tsukemen Ramen(){}
}

OKな例

class Ramen{
    // 一般的なコンストラクタ
    Ramen(){}
}
class Ramen{
    // 引数違いでオーバーロードOK
    // アクセス修飾子もOK
    private Ramen(Karakarauo karakarauo){}
    public Ramen(Gyukotsu gyukotsu){}
}

9. 親クラスから継承できるもの、できないものの基準は?

ほぼ入門書の復習。孫継承などは他サイトに詳しい解説も。

継承できる

  • protectedやpublic、package private指定のメンバ
    • メンバ = メソッド(操作・関数) + フィールド(属性)

継承できない

  • final指定済みメンバ
    • 定数
  • private指定メンバ
    • 継承先から読めないし、継承もされない
  • コンストラクタ
    • コンパイル時に補完されることもある

10. 箱としての型と中身としての型の違いは?

入門書の復習。イメージとして、下記のようなサンプルコードを想定する。

// Characterは「歩く」「話す」「寝る」ができる
// Heroはそれに加え「戦う」ができる
// ここでは、Hero型はCharacter型を親に持つとする
Hero h = new Hero("アンパンマン");
// 中身はHero型だが、Characterの箱に入れてみる
Character c = (Character)h;

箱としての型

つまりCharacterのこと。(正確には、インスタンス作成後のHero型も、だけど)

  • どのメソッドを呼べるか、決定する
    • サンプルコードだと、Characterとして「歩く」、「話す」、「寝る」などは出来そう
    • でも「戦う」という動作は、CharacterではなくHeroにしか出来ない
    • 箱も中身もHero型なら、「戦う」もできる
    • でも箱がCharacter型なら、中身がHeroでも、c.fight()とは記述できない
  • Interfaceも、中身ではなく箱としてなら使用できる
    • Interfaceは、例えるなら、ただの「レストランのメニュー表」だから ## 中身としての型 つまりHeroのこと。
  • メソッドがどう動くか、どういうフィールド(属性)があるか決定する

ダウンキャストとは

あいまいな型から厳密な型へキャストすること。

継承関係があれば文法上は問題ないが、中身によっては実行時エラーとなる。

Hero h = new Hero("アンパンマン");
Character c = (Character)h;
// 日常を過ごしていた
// 突然バイキンマンが現れた!
// ヒーローはヒーローとして行動させたい!
if(c instanceof Hero){
    // 中身がHero型のもののみ、Heroの箱に入れ直す
    Hero hero = (Hero)c;
    hero.attack();
}

Hero型でない中身をHero型の箱に入れないよう、エラー回避でinstanceofを用いる。

まとめ

箱の型は何のメソッドを使えるか、中身の型は呼ばれたメソッドがどう動くかを決定する。

ダウンキャストによるエラー回避でinstanceofを用いることがある。

親だ子だと覚えると混乱しがちなので、入門書では以下のようにまとめられていた。

絵に描いてみて嘘にならない代入は許されます

(例)平凡キャラはどう頑張っても完全なヒーローになれないので、ヒーローを名乗れない。

Character c = new Character("かば");
Hero h = c; /* 実行時エラー */
// Hero型で決められた「戦う」をCharacterが持ってない

11. オーバーライドできる場合、できない場合の基準は?

復習だが重要なので再確認。

  • アクセス修飾子の変更は緩めることのみOK
    • protectedをpublicにするのはOK
    • publicを無指定にするのはNG
  • フィールドは、子クラスで同じものを定義すると隠蔽になる
    • 親クラスの同名フィールドにどうやってもアクセスできなくなる ## OKな例
  • 継承した際に、無指定だったアクセス修飾子をpublicにした ## NGな例
  • メソッドがfinal指定されている
  • そもそもprivateで継承できなかった
  • 動的なメソッドを静的(static指定)にオーバーライドする
    • 逆も不可
  • 継承した際にアクセス修飾子をprivateにした

12. アクセス修飾子無指定の場合は?

入門書の復習。基本中の基本だけど焦って模試で間違えたので。

  • package privateの扱い
    • 縛りのゆるさ : private < package private < protected < public
  • 自分のクラスと同じパッケージ内からのアクセスを許可

13. インターフェースクラスって?

入門書の復習。

  • 全て抽象メソッドの特殊な抽象クラス、多重継承OK
  • 「extends」ではなく、「implements」で実装する
    • インターフェース同士の継承はextends
    • 異種継承はimplements
  • public abstructを省略可能、省略するのが主流
  • default指定で実装内容付き抽象メソッドを定義できる
  • フィールドを原則持たない

    • public static finalのみ可能
    • 省略された場合、コンパイラが自動補完 # 14. finalの挙動の違いは? こちらのQiita記事記事が詳しいです。
  • final クラス

    • 継承禁止
  • final メソッド

    • オーバーライド禁止
  • final フィールド

    • 定数。値の再代入禁止

終わりに

完全に復習不足でした(再びの猛省)。曖昧な理解のまま放置しないよう、気をつけないと。

他にも復習した箇所があれば、記事を追加していきます!

編集メモ

[1.1]<2020/05/02>

  • ビット演算子について付記

[1.0]<2020/05/02>

  • 公開
2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2