昨日、プログラマー面接時の技術的な質問事項(アプレッソ版)を書いたところ、「自分ならこう答える」というエントリを書いてくれた人が何人かいて、個別にコメントしようかとも思ったのだが、昨日のエントリだけだと質問の投げっぱなしになってしまうところもあるので、解答編を書くことにした。

なお、「面接の質問項目を公表しちゃっていいの?」という指摘もあったが、ブログに書いたのはあくまでも質問項目の一例だし、解法を検討する過程を見れば普段どんな風に開発しているのかはだいたいわかるので、特に問題ない。

■ プログラミング基礎編
  • for (int i = 0; i < list.getLength(); i++) {}の潜在的パフォーマンスボトルネック
    list.getLength()がlist.getLength()回評価されてしまう。具体例としては、JREに標準で付属するDOMのライブラリのNodeListの実装はlist.getLength()内で非常に重い処理をしているため、これが原因で劇的にパフォーマンスが劣化することがある。
    (まあ「getLength()」で重い処理するなよ、という話もありますが)

    この問題を回避するためのイディオムとしては、
    for (int i = 0, length = list.getLength(); i < length; i++) {}
    といったものがあり、IBM developerWorksなどプログラマー向けのメディアでは、この書き方に統一しているものもある。

  • 参照渡し/値渡し関係の質問、switch fall throughなど
    これはそのままなのでパス。
オブジェクト指向編
    ここは全項目そのままなのでパス。
デザインパターン編
  • Singletonパターンとオブジェクト生成コスト、Singletonパターンのマルチスレッド対応
    Singletonの書き方としては、まず下記のようなものが考えられる。

    パターン1. フィールドの初期化時にインスタンス生成

    public class Singleton {
      private static final Singleton self = new Singleton();
      private Singleton() {}
      public static Singleton getInstance() {
        return self;
      }
    }
    パターン2. 遅延初期化を用いて必要になったときにインスタンス生成
    public class Singleton {
      private static Singleton self;
      private Singleton() {}
      public static synchronized Singleton getInstance() {
        if (self == null) {
          self = new Singleton();
        }
        return self;
      }
    }
    通常はパターン1.で良いとして、インスタンス生成コストの大きいオブジェクトを扱う場合にはパターン2.のような遅延初期化の採用が考えられるが、パターン2.の形にしてしまうとマルチスレッド対応が必要な場合にアクセッサーを同期化する必要があり(この例で言うとsynchronizedの箇所)、アクセッサーが頻繁に呼び出されるようなケースではこの同期化のコストを何とかしたいですね、さてどうしましょう、という話。

    ちなみに、Javaに特化した話としては、下記のようにオンデマンド初期化ホルダークラスイディオム(Effective Java 第二版 p.273)を使うと、遅延初期化の恩恵を受けつつ、なおかつ同期化のコストをかけずに済むイディオムもある。

    パターン3. オンデマンド初期化ホルダークラスイディオム

    public class Singleton {
      private static class Holder {
        static final Singleton self = new Singleton();
      }
      private Singleton() {}
      public static Singleton getInstance() {
        return Holder.self;
      }
    }

  • Observerパターンでイベント発火順序が重要な理由
    ・イベント発火順序を正確に把握せずにObserverパターンを使ってしまうと、一つのSubjectに複数のObserverがついており、かつ一方のObserverがもう一方のObserverの値を参照しているような場合にデータの不整合が生じる危険性があるから etc.

  • Mediatorパターンでメソッドが多くなりすぎてきたときの工夫
    ・機能のまとまりごとにインターフェースを用意し、Mediatorはそれらのインターフェースを実装し、Mediator利用者にはインターフェース渡しする
    Role Objectパターンに切り替える
    ・etc.

  • Visitorパターンを使うべき場合と、再帰的ループで対処する場合との使い分け
    単にComposite型のオブジェクトを再帰的に処理したいだけならむやみにVisitorパターンを使うと単にシンプルさが失われるだけになってしまうことがあるので要注意ですよねー、とか、その場合でも様々なところから利用されるようなクラスについては一応Visitorの口も用意してあると親切な設計になる場合もありますよねー、とか、その辺りの話。

  • Compositeパターンが時として「オブジェクト指向的に気持ち悪く」なる理由
    内容と容器を同一視するために、本来一方にしか含まれないインターフェースをもう一方に持たせるケースがあるから。例えば本来は容器にしか含まれない子要素取得系のメソッドが内容の側にも空実装として含まれるケースとか。
■ Java編
  • static/final、List/Set/Mapの違い
    そのままなのでパス。

  • new Boolean(true)、new Integer(5)などが推奨されない理由
    使い回しの利くImmutableなオブジェクトを複数生成して無駄にヒープを汚すのは望ましくないから。代わりにBoolean.valueOf(true)、Integer.valueOf(5)などを使う。

  • 防御的コピーの話
    防御的コピー = フィールドとして持っているオブジェクトを外部に渡すときに複製をつくって渡す。本来防御的コピーが必要なソースを書いてみて、潜在的問題として防御的コピーが行われていないですねー、とか。

  • Javaでのディープコピーの実装方法としてどのようなものが考えられますか
    ・普通にclone()を実装
    ・直列化を利用
    ・etc.

  • バージョンの異なる同一FQNのクラスを利用するための対応方法
    ・VMをわけて何らかの形で通信(RMI etc.)
    ・クラスローダーを自作
    ・etc.
    ちなみにクラスローダーの作り方については以前Java Programming Tipsに書いた記事を参照のこと。


Effective Java 第2版 (The Java Series)
Joshua Bloch
ピアソンエデュケーション
売り上げランキング: 3085
おすすめ度の平均: 5.0
5 中級以上なら必須のマナー
5 Javaの良書