Jdk

ソースコードと話す:jdkソースコードの観点からIngterを理解する



Speak With Source Code



この記事ではソースコードの一部のみを紹介しており、Javaソースコードのアイデアは深すぎます。ここでは、いくつかの一般的な機能(純粋に個人的なナンセンス)の基本的な原則のみを学びます。


私の能力は限られており、ソースコードが長すぎるので、以下の点から一般的に使用されるものだけを紹介します。



  • Integerクラスの一般的な説明
  • 整数のコンストラクタとメモリモデル
  • 整数のparseInt(String s)メソッド:文字列を値に変換します
  • 数値反転関数:reverse(int i)
  • 整数自体の実装:toString(int i)

最初にクラス定義を見てください。

public final class Integer extends Number implements Comparable<Integer>

Integerクラスは最終クラスです。つまり、クラスは継承できません。基本的に、データ型に関連するすべてのクラスは継承できません。そしてNumberクラスを継承します



public abstract class Number implements java.io.Serializable

Numberクラスはシリアル化可能なインターフェイスを実装しているため、Integerクラスもシリアル化できます。 Numberのサブクラスは、Byte、Short、Integer、などのデータ型変換メソッドを提供します。
Long、Float、Doubleなど。これは、値をbyte、short、int、long、に変換することを意味します。
フロートとダブル。

Integerクラスは、主に定義に使用されるComparableインターフェイスも実装します。

整数は、intのラッパークラスであるオブジェクトをインスタンス化できます。 jdk1.5でリリースされた新機能:整数を「リテラル」にする自動解凍(共通オブジェクトはすべて新しい:Integer in = new Integer(5)オブジェクトに値を割り当てるには、クラスのメソッドを呼び出す必要があります。リテラルは直接割り当てることができるデータ型:int i = 5)一般的に、直接割り当てることができるリテラルは8つの基本的なデータ型のみです。 Javaで新しく追加されたStringクラスもリテラルです。後でStringソースコードを学ぶときに、これを調べてみましょう。
jdk6の自動アンパックの新機能により、パッケージングクラスはリテラル割り当ての機能も直接持つことができます(整数= 5)自動アンパックは実際にはコードコンパイル中です仮想マシンが自動的に解釈を支援する場合:整数= 5 into:Integer in = new Integer(5)これが解釈の実行になります。自動開梱と梱包について簡単に説明します。興味のある友達は関連するブログをチェックすることができ、それは私のものよりも優れているに違いありません。



* Integer isintPackaging, sointThe size range of is its size * intOccupies four bytes in memory 32The first bit means positive and negative * intThe minimum value is:1-2^31 -> -2147483648 */ public static final int MIN_VALUE = 0x80000000 /** * The maximum value of int is: 2^31-1 -> 2147483648 */ public static final int MAX_VALUE = 0x7fffffff

整数の構築方法から始めましょう:

private final int value//The current int value public Integer(int value) { this.value = value } public Integer(String s) throws NumberFormatException { //Use a string to construct an Integer object, if s cannot be converted into a number, a NumberFormatException will be thrown //If it can be converted, it will be converted in decimal by default. this.value = parseInt(s, 10) }

文字列を10進数に自動的に変換できる上記のparseIntメソッドがあります。すごいですか? parseIntメソッドのソースコードを見てみましょう。

public static int parseInt(String s, int radix)throws NumberFormatException { //s is the string passed in radix is ​​the base number, which is the hexadecimal number (default is decimal) if (s == null) {//No explanation throw new NumberFormatException('null') } //Character.MIN_RADIX int type constant value: 2 Judgment that the hexadecimal number shall not be less than the minimum hexadecimal number as binary if (radix throw new NumberFormatException('radix ' + radix + ' less than Character.MIN_RADIX') } //Character.MAX_RADIX int type constant value: 32 Judging that the hexadecimal number should not be greater than the maximum hexadecimal number is thirty binary if (radix > Character.MAX_RADIX) { throw new NumberFormatException('radix ' + radix + ' greater than Character.MAX_RADIX') } int result = 0 //The boundary value of positive numbers is 1 to 0x7fffffff the boundary value of negative numbers is -1 to 0x80000000 boolean negative = false//Record whether the value obtained is positive or negative false: positive number true: negative number int i = 0, len = s.length()//String offset pointer records the length of the string int limit = -Integer.MAX_VALUE//Maximum boundary value int multmin//The maximum boundary value is shifted one bit to the right int digit if (len > 0) {//When the length is greater than 0, intercept the first character char firstChar = s.charAt(0) if (firstChar <'0') { // may be '+' or '-' if (firstChar == '-') { negative = true//Mark the symbol status as negative limit = Integer.MIN_VALUE } else if (firstChar != '+') throw NumberFormatException.forInputString(s) if (len == 1) //If it is neither '+' or '-', throw an exception throw NumberFormatException.forInputString(s) i++//Shift } multmin = limit / radix//Prevent result*=radix in the loop from out of bounds while (i // Accumulate negative to avoid overflow //Use characters and hexadecimal numbers to calculate the corresponding value (actually take the ascII of the character to calculate) //Convert the character into an integer corresponding to the base digit = Character.digit(s.charAt(i++),radix) if (digit <0) { throw NumberFormatException.forInputString(s) } if (result throw NumberFormatException.forInputString(s) } result *= radix//Each time the multiplying number is carried if (result throw NumberFormatException.forInputString(s) } //The code treats all data as negative numbers (positive numbers), and finally deals with sign issues result -= digit } } else { throw NumberFormatException.forInputString(s) } return negative ? result : -result }

上記のコードでめまいがする場合は、フローチャートを見ると、思考がはるかに明確になります。
画像
整数を学習するときに、次のコードに遭遇することがよくあります

public static void main(String[] args){ Integer i1 = new Integer(5) Integer i2 = new Integer(5) system.out.println(i1.equals(i2)) system.out.println(i1==i2) }

私は、少し基礎を持っている友人が間違いなく同じ答えを得ることができると信じています:真偽記憶モデルはおそらく次のようです:
画像
スタックに格納されているのは、ヒープ内のアドレスへのすべての参照であり、equalsはメモリ内の値を比較しますが、2つのオブジェクトの値が等しい場合、equalsはtrueですが、「==」はメモリのアドレスを比較しますスタックで参照されるモジュール。
上記の質問はほんの小さなケースです。次のことができるかどうか見てみましょう。原理を知っていますか?


public static void main(String[] args){
Integer i1 = 5
Integer i2 = 5
system.out.println(i1.equals(i2))
system.out.println(i1==i2)
}

答えは次のとおりです。本当本当私は多くの人々が夢中になっていると信じています。自動開梱はどうですか?最初にメモリモデルを見てみましょう。

画像

多くの人が理由を尋ねると思いますか?ソースコードを使ってそれについて話しましょう:

private static class IntegerCache { static final int low = -128 static final int high static final Integer cache[]//Static cache Integer array static { // high value may be configured by property int h = 127 String integerCacheHighPropValue = sun.misc.VM.getSavedProperty('java.lang.Integer.IntegerCache.high') if (integerCacheHighPropValue != null) { int i = parseInt(integerCacheHighPropValue) i = Math.max(i, 127) // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1) } high = h cache = new Integer[(high - low) + 1]//Cache -128 to 127 in the cache int j = low for(int k = 0 k new Integer(j++) } private IntegerCache() {} }

明らかに、これは静的内部クラスです。より良い基盤を持つ友人は、静的クラスの静的コードブロックが最初に実行されることを確認できます。したがって、プログラムの実行中、Integerクラスはバイト長の数値(-128〜127)をキャッシュします。整数クラスを直接5にポイントすると、プログラムは最初にキャッシュ内で対応する値を見つけます。したがって、2つの参照は同じメモリブロックを指します。しかし、整数i3 = 128を整数i4 = 128にすると、もう一度比較すると、答えは間違いなく誤りです。興味のある友達はそれを試すことができます。共有クラスデザインのこの内部構造のFlyweightデザインモードへの適用について簡単に説明します:Flyweightデザインのアイデアは、名前としてリソースを使用および共有し、リソースを節約することです。このデザインパターンは、基本的にjdkのパッケージングクラスでいくつかの共有データをキャッシュするために使用され、興味のある友人はそれについて学ぶことができます。

public static int reverse(int i) // HD, Figure 7-1 i = (i & 0x55555555) << 1

この関数は、数値のAND演算によって戻り値を取得します。指定した値の逆順で得られた値の2進補数の2進表現を反転することにより、得られた値が得られます。使用できますこの関数は、データの単純な暗号化と復号化を実行します。

誰もがtoStringメソッドに精通していると思います。 toStringメソッドは、すべてのクラスの基本Objectクラスに実装されます。サブクラスがtoStringを実装して独自の対応するデータを生成する場合は、クラスのメモリアドレスに対応するhashCodeを、このメソッドを書き換える必要があります。整数クラスは独自のtoStringを書き換えます。関数は非常に単純ですが、整数型の数値を文字列型に変換する方法ですが、この下からの実現の原則は学ぶ価値があります。

public String toString() { return toString(value) } public static String toString(int i) { if (i == Integer.MIN_VALUE) return '-2147483648' int size = (i <0) ? stringSize(-i) + 1 : stringSize(i) char[] buf = new char[size] getChars(i, size, buf) return new String(buf, true) }

上記のソースコードから、三項演算を使用してstringSizeメソッドを呼び出し、最後にgetChars()を呼び出すことがわかります。三眼操作はjdkの下部で使用されます。三極演算を使用して、if-elseステートメントを置き換えることができます。対応するメソッドがどのように実装されているかを見てみましょう。

final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE } static int stringSize(int x) {//The main function is to detect whether there is a cross-border or overflow phenomenon caused by exceeding the length for (int i=0 i++) if (x <= sizeTable[i]) return i+1//Return the length, because the subscript starts from 0 so when returning to +1 } static void getChars(int i, int index, char[] buf) { int q, r int charPos = index char sign = 0 if (i <0) { sign = '-' i = -i } // Generate two numbers for each iteration while (i >= 65536) { q = i / 100 // really: r = i - (q * 100) r = i - ((q << 6) + (q << 5) + (q << 2)) i = q buf [--charPos] = DigitOnes[r] buf [--charPos] = DigitTens[r] } // Drop to fast mode for smaller numbers // keep(i <= 65536, i) for () { q = (i * 52429) >>> (16+3) r = i - ((q << 3) + (q << 1)) // r = i-(q*10) ... buf [--charPos] = digits [r] i = q if (i == 0) break } if (sign != 0) { buf [--charPos] = sign } } final static char[] digits = {//Cache 0-9 a-z Use subscripts to match values '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }

上記のソースコードから、IntegerのtoStringメソッドは、数値を大まかに1つの数値に分割し、対応する値を取得して、数字の静的配列内の対応する文字と一致させ、それを文字列配列に連結してから、Stringを呼び出すことがわかります。完了するコンストラクタ。

Javaのデザイン思考は広範で深遠であり、私はこれを自分の能力でしか理解できず、後で追加する機会があります。