インタビュアー:あなたが言うJavaのソースコードには、いくつかの見慣れた顔の質問をバックアップしますか?申し訳ありませんが、我々は、人々は表示されませんソースを募集していません

あなたは、ソースコードが表示されない場合は、時計に忍耐を喜ば

まず、私の本当の経験

インタビュアーがためにも自分の愚かさ、怠惰と傲慢、深センへの私の最初のゲームで、無限の恥だと思い、今でもそれを考える、と言って私に言ったとき、タイトルは、私は深センのインタビューで、フォーチュン500企業2019年6月28日ですインタビューはblunderedます。

私は確かに私の人生はこれが提供する深センでの私の最初のインタビューで、そのホット朝、私はインタビューKさんを受けた最初の日を忘れませんよ。メッセージを受信した後、私はKに移されたが、同社の提案を受けているようだ、私はKの顔に専用のインターネットをチェックし、多くの人々は、彼らが能力を読んで、ソースコードに注意を払うと言うことが判明した、ほぼすべての時間は、いくつかのソースについて質問します古典的な問題、私は文字列など、HashMapの、上のいくつかの記事を見つけるためにオンライン行ってきましたので、Javaソースコードについて多くのことを学びました。私は、それを読んだ後に非常に自信を持って私は、満足な睡眠をそれに答えることができます確信して明日のすべての問題と思いました。

朝、私は階下会社Kが来た九時だったことをインタビューし、フロントデスクは私の妹の名前を呼ばれたときに、私は彼女を追った、午前9時30分については、待合室でのインタビューを待って、私との接触を呼び出すことです、二人を作った小さな部屋の中に一緒に入る明らかに(少しはげなど)技術を行っている、スタートがスムーズに行くし、その後の質問をし、「あなたはJavaソースコードに精通していることをあなたの履歴書に述べている、ということ私は、Stringクラスはもちろん、記事が書き込まれ、継承されていない」、それを継承することができ、あなたに質問をし、文字列を継承することはできません、修正最終的なもので、その後、私は面接の質問の数にしました内容、インタビュアーは、質問を

「サブストリングの実装プロセスについて簡単にしてください」

はい、私が使用した場合、このメソッドのソースを見ていないだろう、私は言い逃れにお答えすることはできません通常、私はホットに私の赤面を感じることができる、この問題を見ていません。彼は「あなたは本当にあなたが本当に読めば、ソースは?サブストリングは、非常に単純な方法は何か読んでください知ることができない」、言うようになった、その後、私の苦痛を見るように見えた私は告白していた、このポイントに私は、ソースコードを読んでいない、そう私は実際にも、私もStringクラスのソースを見つけることができません、どのように簡単な部分文字列を知らない気づいていないのです。

インタビュアーは、タイトルにその文は、私が面接を失敗した、と述べました。

私は新しい世界を開いたので、私は春、そしてSpringBootソースにJDKのソースコード、ソースコードを参照しようと始めた、失敗のこの経験に感謝したい、より多くの私は多くの参照は、この優れたフレームワークを書くために、これらの首長を賞賛します自分のアイデア、コードのロジック、デザインパターンは、とても良いと適切です。私も最初の手のトレーニングフレームワークは、「春のフレームワーク--YzSpringの簡単な手書きのバージョン」で、自分の枠組みを書いてみるようになった、それは夜に仕事をオフにノックして1時間か2時間後に自宅で毎日、週に連れて行ってくれただけでなく、その後、完成しYzSpringは、私は本当に、春を理解し、総感じが最小であるとだけは本当に自分自身の手書き春の作品を理解するために再度行く前にオンライン情報を参照してください感じています。

それでも後に、我々は、インタフェースのフィードバックの低速文句を言ってきた私の手「IPayment」プロジェクトのパートナーは、私はRedisのにコードを最適化し始めたいくつかのデータをキャッシュし、速度が速い再び本当にですが、それぞれがデータ・キャッシュを追加する必要がありますダウンより少ないデータをキャッシュサポートするコードの2つのまたは3行は、問題ではないが、より多くのデータのニーズがキャッシュに書き込まれるよう、コードは非常に肥大化になります。私は注入機能@Autowiredを見たある日、私は突然、なぜ私がキャッシュされたデータでマークされたこれらのノートを必要とする実用的なフレームワークを書くことはできませんし、その後のフレームワークそれで処理された、と思いましたか?先に連続した残業を行ってきました私は「高速データは、Redisのコンポーネントに基づいて、キャッシュ」完了週は、プロジェクトの導入後、唯一の変更、操作だけでなく、オプションのため@BFastCacheを必要とするデータをキャッシュする必要があります。データを操作、選択されたデータソースは、データソースを更新し、セット/大幅に作業効率を向上させること、キーを変更します。とてもよく車輪を書いて、それ以来初めて、ビッグブラザーは、本当に幸せ断言されています。

だから今私はあなたに3つの質問をお願いしたいと思います:

あなたはどのようなソース参照してください?

あなたは、ソースコードを見るのだろうか?

あなたは、ソースコードから、それを収穫しますか?

第二に、あなたがソースコードを得ることができるものを見て

1.クイックトラブルシューティングおよびエラーを削減

符号化する際の文字列サブストリング方法に時々我々がendIndexを渡すことができるように、我々は、一般のRuntimeExceptionを検出することができる文字列の長さよりも大きいので、ランタイムエラーが存在するであろう

String index out of range: 100

時々夢中歩行右のコードを変更するには、この異常が発生した理由を、再び同じ問題を記述するために、次の時間が起こるかわかりません。私たちは、ソースコードを読めば、私たちは、異常発生の原因を知ることができます

public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {//起始坐标小于0
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {//结束坐标大于字符串长度
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {//起始坐标大于结束坐标
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

ソースコードは、例外がスローされた上で、我々は3つのシナリオに基づいて、当社のコードをチェックするために行くことができます、だけでなく、将来の符号化時に、これらの問題に注意を払う三つの可能なシナリオを提供します。

2.学習・プログラミング・プラクティス

それとも、それはあなたであれば、彼のリターンにソース、してください注意を払うのサブストリングの上に、あなたはどのように書くのでしょうか?あなたがソースコードを読んでいない場合は、私は次のように書かれているだろう

        if ((beginIndex == 0) && (endIndex == value.length)) return this;
        return new String(value, beginIndex, subLen);

機能は同じですが、彼らは文を書いていない場合は三項演算子の使用は、単一のコード行で問題を解決する、とすることができ、そして今私は三項演算子に夢中だが、実際にはかなり良いです。

3.(初心者用)デザインパターンを学習します

すべての権利!私は、途中でまともなプログラマとして、私は私が最もSingletonパターンを知っているだけのデザインパターンの23種類があることを知って、デザインパターンを理解していなかったの前に、自習それらのすべては、体系的な教育を受けていない対決。

デザインパターンの主な理由は、実際の戦闘経験がなかったということであることを理解しないで、自分の書き込みのプロジェクトは、ライン上で実行することができ、基本的なイベントではなく、完全に無関係なデザインパターン、です。工場の設計パターンとの私の最初の接触は、log4jのパターンである、完全に工場出荷時のパターンを使用する方法を理解する際に学んだソースコードのlog4jのステップを見バイステップであり、それは意図的にまたは意図せずに使用する設計に開始されますときに、自分のプロジェクトを行いますモードは、ここで取得私のプロジェクトシングルトンクラスコード設定モードであります

import java.util.ResourceBundle;

public class Configration {
	private static Object lock              = new Object();
	private static Configration config     = null;
	private static ResourceBundle rb        = null;
	
	private Configration(String filename) {
		rb = ResourceBundle.getBundle(filename);
	}

	
	public static Configration getInstance(String filename) {
		synchronized(lock) {
			if(null == config) {
				config = new Configration(filename);
			}
		}
		return (config);
	}
	
	public String getValue(String key) {
		String ret = "";
		if(rb.containsKey(key))
		{
			ret = rb.getString(key);
		}
		return ret;
	}
}

3.小型の概要

あなたは非常にシンプルなものの上に多くの人々が、理由は上記の、私に惑わされないでください見つけることがあります理解するために自分で見るだけで、最も簡単な例、非常に勉強ソースコードの価値があります。

第三に、正しい姿勢のソースコードを読みます

クラスHashMapの例にここに非常に高い熱で私たち、そして私は非常に私たちを聞かせて、私たちは迅速かつ容易にソースコードを見ることができ、ショートカットキーの数があり、独自の逆コンパイラは、コードを読み取るためにIDEAを使用することをお勧めします操作するには、飛ぶように冷却します。

ソースを見つけて1

実際には、ポジショニングは、それはまた、さまざまな状況があります

Ctrlキー+左

このように、我々はHashMapのクラスメソッドの一部のみを入力する必要があり、我々は直接、Ctrlキー+を使用すると、元の場所に移動することができます左することができます

Ctrlキー+ Altキー+ B

HashMapのは、この方法は、我々はCtrlキー+左を使用している場合、我々は上にマウスカーソルを置く必要があり、それは私たちが望む結果ではないですが、Mapインタフェースのメソッドを置くために直接ジャンプします、マップアプローチを書き換えることである置きますその後はCtrl + Alt + Bを押して、クラスの多くの書き換えPUTメソッドがありました

私たちは、あなたが置くために方法を見つけることができますビューにクラス、左クリックを見つける必要があります

継承を確認してください。2.

非常に重要なのクラスの継承、方法は、サブクラスで使用することができ、抽象クラスであるため、特に、抽象クラスを継承しています。

我々はソースHashMapを見つける必要があり、最後のステップは、今、私たちが見ることができる、トップを引っ張っクラス定義が継承についてあるときに

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable 

もちろん、あなたがより直感的で、より詳細になりたい場合は、IDEAでショーケースの継承機能にあり、あなたはビューにしたいカテゴリにマウスを置くことができ、その後、Ctrlキー+ Altキー+ Shiftキー+ U、または「図表=」ショー右=図は、次に我々は、継承を見ることができます

 次の使用のための待ち時間があるかもしれないので、その後、一般的に、次のAbstractMapの抽象クラスを表示します。

クラス定数をチェック3.

私たちは、HashMapのコンストラクタに入るとき、私たちは次のコードを見つけました

public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

我々は唯一の私たちは初期容量を通過したInitialCapacityの値を知っているが、DEFAULT_LOAD_FACTORが何であるかを知りませんでした、どのようにはるかに低い読書がソースに等しい、我々はまず、ヘルプなどのような印象を残し、持っているこのクラスの簡潔定数を見ることができますCtrlキー+は、位置の量に見つけ、左、次に良いと注釈の両方のいくつかの定数があることを見出し、上記の定数は、私たちは、私たちはこれらの定数を理解するのに役立ちます、見て

    //序列号
    private static final long serialVersionUID = 362498820763181265L;

    /**
     * 初始容量,必须是2的幂数
     * 1 << 4 = 10000 = 16
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始默认值二进制1左移四位 = 16

    /**
     * 最大容量
     * 必须是2的幂数 <= 1<<30.
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 加载因子,构造函数中没有指定时会被使用
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * 从链表转到树的时机
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 从树转到链表的时机
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * The smallest table capacity for which bins may be treeified.
     * (Otherwise the table is resized if too many nodes in a bin.)
     * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
     * between resizing and treeification thresholds.
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

このように、我々は、HashMapのの定数の役割と重要性を理解しています

コンストラクタを確認してください。4.

私たちは、一般的にクラスを見て、このクラスは、コンストラクタを達成するために、あること、構築する方法を見て最初にあります

    /**
     * 构造一个空的,带有初始值和初始加载因子的HashMap
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

明らかに、別のコンストラクタに上記のコンストラクタポイントは、次に我々はに行くとポイントを参照してください

    /**
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

ここでは、達成するためにコンストラクタところであり、我々は、ライン分析でラインを行かなければなりません。

1.当社の引数は、我々InitialCapacityの値が16に渡され始めていることで、loadFactorは、デフォルトパラメータの0.75Fと最後のステップです

2.初期容量を分析すると、0が例外をスローします未満、0以上で、次のステップは、0以上ではありません

3.最大容量よりも大きいが取る場合、最大容量(1 << 30)よりも初期容量も大きいか否かを決定します

4.負荷率未満又は0に等しい又は数であり、次の例外がスローされている分析、又は

5.初期のHashMapの負荷係数

6.最後の行はサイズに私たちの能力に基づいて実際の容量を決定するためのHashMapの膨張機構である、我々は法のソースコードを見て

    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

このステップでは、我々は、この初期容量ではなく、設定値の真の値として、2のべき乗の最小容量を設定することを求めるよりも、実際に大きくなって、これはその後の操作のための場所です。今、私たちは、上記の計算を説明します。

=演算子符号なしの右|キャップ= 13に、例えば、最初のN = 12、N進数は右のいずれかによって、およびNで一回又は動作第1の実施例と、第その後、00001100であります右、右値00000110、又は場合それは16であり、N + 1に戻された場合の動作を返し、その後、反復計算の最後の工程に、N-00001110で行わ、N = 00001111れます。

これまでのところ、我々は動作する準備ができて、今、HashMapを空のHashMapの初期化を完了しました。

5.チェックロジックのメソッド

当社は通常のHashMapの方法を置くために今の位置に、HashMapの時間を使う方法がより多くの使用に置く、およびコンテンツは、彼はもっと関与することになります

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

何入れて、この方法が行われているハッシュ(キー)で見てみましょう、メソッドputValメソッドを呼び出して、分解、キー、および値のパラメータは何も言うことはありません

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

現在のキーがnullである場合、それがnullでない場合、ハッシュ値の現在のキーはHに割り当てられ、戻りハッシュキー大電流の16ビットと16ビットのローが異なるビットされ得る、0のハッシュ値に直接戻しますまたは値は、この方法は、ハイとローが大きくハッシュ衝突の確率を低減することができる操作に関与している可能にします。

OK!三つのパラメータ、我々はすでに知っているコンテンツのハッシュ値が、3つの値が使用何を知っている、しない心配しないで、我々はputVal方法を入力してくださいエクストラ

    /**
     * Implements Map.put and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 コードの山の頂上を見て、それが頭痛を始めていない、彼を恐れてはいけません、我々は行ずつを打破する、彼は非常に簡単になります。

最初のステップは、十分に翻訳されているコメントは、見ている、お楽しみください

    /**
     * 继承于 Map.put.
     *
     * @param hash key的hash值
     * @param key key
     * @param value 要输入的值
     * @param onlyIfAbsent 如果是 true, 不改变存在的值
     * @param evict if false, the table is in creation mode.
     * @return 返回当前值, 当前值不存在返回null
     */

そして、コンテンツを見て

ノードは、一般的に以下の性質であるHashMapの基礎となるデータ構造であり、いくつかの変数を作成する1

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
}

前記条件が満たされた場合、サイズ変更、実行、および値はタブのサイズを変更するために与えられ、その後、(現在のハッシュマップが空である)、テーブルの現在の長さが空であるかどうか、またはテーブルがゼロであるかどうかを決定する、nおよび割当タブにnは与えられたタブの配列の長さは、より多くのコンテンツは、他の記事に多くのことを言っている詳細な説明リサイズ()メソッド、スペースの不足、今日の焦点はむしろ、HashMapのよりソースコードを、読む方法を説明することです。

3.要素のハッシュ値の現在値に対応する位置に、基礎となる配列を分析すること直接に現在の要素への重要な要素を持っていない、そうでない場合

前記接続ステップ、基礎となる配列の対応する位置は、カラムへの書き込みデータの値は、いくつかの他の動作を有し、OLDVALUEを返す場合。

この工程を経た後、我々はノートの中で、このようなHashMap.put方法として注目を集め、必要ないくつかのポイントを要約し、ツリーとリストの間の変換を端を差し込み、そのサイズ変更です。

スペースの問題、ステップ解析によってステップができ、前のソースコードと類似したを表示するには、特定の方法については、この方法は、私だけで簡単に話の内容のため。

6.小型の概要

ソースを表示いくつかのヒント

1.Ctrl +左またはCtrl + Alt + Bは、ソースの正確な位置を見つけるために

2.一般的な理解がある、いくつかの種類の内部の量をチェックしてください

初期状態へのコンストラクタの例を参照するには3.チェック

4.コードは、コードを分解し、より複雑である場合は、すべてのステップ

読書の他のソースは、分析するために、このルーチンに従うことができます

IVの概要

著者=孟は新しい、間違っている場合は、指摘してください

ソースコードを読むことは最初は読みにくいですが、またゆっくりに慣れていても、絶対に必要なすべてのプログラマのスキルであります

あなたが好きなら、親指、コメント、お気に入り、注意を歓迎

公開された55元の記事 ウォンの賞賛526 ビュー180 000 +

おすすめ

転載: blog.csdn.net/shouchenchuan5253/article/details/105196154