之前只知道計算機使用反碼來進行計算,可是沒有想到,也沒有想過計算機存儲數字的時候是用什麼格式存儲的,固然它是二進制的,個人意思是它是原碼,反碼,補碼中的哪種。今天由於學習ServerSocketChannel,涉及到了這個問題,才把這個知識點摸透,是以反碼形式存儲的。認識到這一點有什麼做用呢,且聽我說。java
編寫服務器程序的時候,須要啓動server socket監聽某個端口,以及綁定一張網卡(也就是服務器上的網卡,當你的服務器有多張網卡的時候,你能夠選擇將socket綁定到其中一張,此時客戶端到來的請求,只有網卡和端口都相同,纔會被這個socket識別)。我有一張網卡的地址是:192.168.1.78。程序員
try { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 綁定一個地址 serverSocketChannel.bind(socketAddress); } catch (IOException e) { e.printStackTrace(); }
上面代碼中的socketAddress是一個SocketAddress接口的實例,java提供了對該接口的實現類InetSocketAddress,該實現類有構造函數:windows
public InetSocketAddress(InetAddress addr, int port)
InetAddress是一個public類,但它的構造函數是默認權限,也就是包內或子類可訪問,它在java.net包下,但它提供了靜態方法來獲取其實例:數組
public static InetAddress getByAddress(byte[] addr) throws UnknownHostException
固然它還有其餘一些靜態方法來獲取實例,並非必定要用byte數組才行。而我決定使用它,並所以發現了本身知識圖譜中的一個缺陷。服務器
首先咱們知道:app
而後我當時自覺得,java是以原碼存儲數字的。socket
在這樣的知識背景下,我來算一算,我應該傳哪4個byte。我要綁定的網卡的IP地址是192.168.1.78,因此最低位是78,在byte的表達範圍內,不須要特殊處理;第二位是1,也不須要處理;第三位是168,168在java裏用byte已經表達不了了,由於java的byte的最高位用來表達符號,因此它實際上只有7位能表達數值。怎麼回事,難道這個方法建立的InetAddress實例只能表示[0.0.0.0-127.127.127.127]內的IP地址?函數
不對勁,不對勁,我去看一看這個方法的內部是怎麼實現的。學習
public static InetAddress getByAddress(byte[] addr) throws UnknownHostException { return getByAddress(null, addr); }
看來它調用了另外一個方法,咱們再去看看它是怎麼回事。.net
public static InetAddress getByAddress(String host, byte[] addr) throws UnknownHostException { if (host != null && host.length() > 0 && host.charAt(0) == '[') { if (host.charAt(host.length()-1) == ']') { host = host.substring(1, host.length() -1); } } if (addr != null) { if (addr.length == Inet4Address.INADDRSZ) { return new Inet4Address(host, addr); // into here } else if (addr.length == Inet6Address.INADDRSZ) { byte[] newAddr = IPAddressUtil.convertFromIPv4MappedAddress(addr); if (newAddr != null) { return new Inet4Address(host, newAddr); } else { return new Inet6Address(host, addr); } } } throw new UnknownHostException("addr is of illegal length"); }
從第一段代碼咱們能看出來調用getByAddress方法時,傳入的host是null,因此第一個if直接跳過不看,咱們傳入的addr參數,是一個長度爲4的byte數組,因此代碼執行到了into here註釋所在的那一行。繼續:
Inet4Address(String hostName, byte addr[]) { holder().hostName = hostName; holder().family = IPv4; if (addr != null) { if (addr.length == INADDRSZ) { int address = addr[3] & 0xFF; address |= ((addr[2] << 8) & 0xFF00); address |= ((addr[1] << 16) & 0xFF0000); address |= ((addr[0] << 24) & 0xFF000000); holder().address = address; } } holder().originalHostName = hostName; }
原來!在這裏,經過移位運算,byte數組被轉換成了一個int值。左移位是不會影響符號位的,因此在將byte轉換成int的過程當中,符號位實際上被當成了一個數值位(符號位成爲了int中低位上的數值位,但最後一個byte的符號位成爲了int的符號位)。int是4字節=32位=ip地址長,這樣表明IP地址,能夠的。
因此InetAddress是能夠表示[0.0.0.0-255.255.255.255]的,只不過你要將byte中的最高位也考慮上。讓咱們來算一算,無符號的168用有符號的誰來表示。windows自帶程序員計算器,搬出來計算一下,168的二進制表示是10101000。java的byte的最高位是符號位,因此它表示一個負的00101000。而00101000是40,因此我應該用有符號的-40表示無符號的168。同理計算出192用-64表示。
綜上,個人代碼應該是
// 192.168.1.78 InetAddress inetAddress = InetAddress.getByAddress(new byte[]{-64, -40, 1, 78}); System.out.println(inetAddress)
然而,println輸出的倒是:/192.216.1.78
怎麼回事?216是什麼鬼?檢查了一通,發現-40沒有問題,爲何-64就正確的表達了192,-40就叉屁了呢?
debug一下看看吧
11000000_11011000_00000001_01001110,-40的10101000,到這裏怎麼就變成11011000了呢?其餘都是正確的。到這裏我其實沒看出來致使這個問題的根本緣由,可是計算機裏的二進制確實有原碼,反碼,補碼一說,我來套一套看看。
若是數據的存儲不是原碼,而是反碼呢?
10101000 原 11010111 反(符號位不變,其他取反) 11011000 補(反碼+1)
我去,柳暗花明又一村,得來全不費工夫。新的問題是,-64怎麼就對上192了呢
11000000 原 10111111 反 11000000 補
竟然還有這麼巧的事情。我差一點就由於-64和192的對應是正確的而沒往這個方向想。差點由於一個巧合走向了錯誤的思路。也給本身提個醒,有些看似不該該有問題的地方,也有多是存在問題的。
至此我也是才發現一個很重要的知識點,java在存儲數值的時候,使用的是反碼,並非之前我覺得的,用原碼存儲,用反碼計算。這一點平時開發可能基本遇不到,但當你使用 >> << >>> 這種移位運算符,或者用 $ | 運算符的時候就會用到這個知識點。
最後,要想表達168,直接使用它的二進制補碼11011000就能夠了,將他轉換爲有符號的byte是-88。
InetAddress inetAddress = InetAddress.getByAddress(new byte[]{-64, -88, 1, 78}); System.out.println(inetAddress) // /192.168.1.78