国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Java 與無符號那些事兒

2019-11-14 22:16:49
字體:
供稿:網(wǎng)友
java 與無符號那些事兒

最近在使用 Java 作為 WebSocket 客戶端連接 Node.js 的 WebSocket 服務(wù)器的時(shí)候,由于使用的客戶端庫比較老,所以遇到了字節(jié)符號的問題,上網(wǎng)查了一下,看到這篇文章寫的很有意思,就翻譯一下。

原文地址:http://www.darksleep.com/player/JavaAndUnsignedTypes.html

原文作者:Sean R. Owens

以下是正文


Java 中的無符號類型是怎么回事兒?

在 C 和 C++ 這樣的語言中,都提供了不同長度的整數(shù)類型:char, short, int, long (實(shí)際上,char 并不是真正的整數(shù),但是你可以把它當(dāng)成整數(shù)來用。在實(shí)際應(yīng)用場景中,很多人在 C 語言中用 char 來存儲較小的整數(shù))。在大部分的 32 位操作系統(tǒng)上,這些類型分別對應(yīng) 1 字節(jié),2 字節(jié),4 字節(jié)和 8 字節(jié)。但是需要注意的是,這些整數(shù)類型所對應(yīng)的字節(jié)長度在不同的平臺上是不一樣的。相對而言,由于 Java 是針對跨平臺來設(shè)計(jì)的,所以無論運(yùn)行在什么平臺上,Java 中的 byte 永遠(yuǎn)是 1 字節(jié),short 是 2 字節(jié),int 是 4 字節(jié),long 是 8 字節(jié)。

C 語言中的整數(shù)類型都提供了對應(yīng)的“無符號”版本,但是 Java 中就沒有這個(gè)特性了。我覺得 Java 不支持無符號類型這個(gè)事兒實(shí)在是太不爽了,你想想,大量的硬件接口、網(wǎng)絡(luò)協(xié)議以及文件格式都會用到無符號類型?。↗ava 中提供的 char 類型和 C 中的 char 有所不同,在 Java 中,chat 是用 2 個(gè)字節(jié)來表示 Unicode 值,在 C 中,char 是用 1 個(gè)字節(jié)來表示 ASCII 值。雖然可以在 Java 中把 char 當(dāng)做無符號短整型來使用,用來表示 0 到 2^16 的整數(shù)。但是這樣來用可能產(chǎn)生各種詭異的事情,比如當(dāng)你要打印這個(gè)數(shù)值的時(shí)候?qū)嶋H上打印出來的是這個(gè)數(shù)值對應(yīng)的字符而不是這個(gè)數(shù)值本身的字符串表示)。

那么,如何應(yīng)對 Java 中無符號類型的缺失?

好吧,對于我給出的這種方案,你可能會不喜歡……

答案就是:使用比要用的無符號類型更大的有符號類型。

例如:使用 short 來處理無符號的字節(jié),使用 long 來處理無符號整數(shù)等(甚至可以使用 char 來處理無符號短整型)。確實(shí),這樣看起來很浪費(fèi),因?yàn)槟闶褂昧?2 倍的存儲空間,但是也沒有更好的辦法了。另外,需要提醒的是,對于 long 類型變量的訪問不是原子性操作,所以,如果在多線程場景中,你得自己去處理同步的問題。

如何以無符號的形式存儲和讀取數(shù)據(jù)?

如果有人從網(wǎng)絡(luò)上給你發(fā)送了一堆包含無符號數(shù)值的字節(jié)(或者從文件中讀取的字節(jié)),那么你需要進(jìn)行一些額外的處理才能把他們轉(zhuǎn)換到 Java 中的更大的數(shù)值類型。

還有一個(gè)就是字節(jié)序問題。但是現(xiàn)在我們先不管它,就當(dāng)它是“網(wǎng)絡(luò)字節(jié)序”,也就是“高位優(yōu)先”,這也是 Java 中的標(biāo)準(zhǔn)字節(jié)序。

從網(wǎng)絡(luò)字節(jié)序中讀取

假設(shè)我們開始處理一個(gè)字節(jié)數(shù)組,我們希望從中讀取一個(gè)無符號的字節(jié),一個(gè)無符號短整型和一個(gè)無符號整數(shù)。

short anUnsignedByte = 0; char anUnsignedShort = 0; long anUnsignedInt = 0; int firstByte = 0; int secondByte = 0; int thirdByte = 0; int fourthByte = 0; byte buf[] = getMeSomeData(); // Check to make sure we have enough bytes if(buf.length < (1 + 2 + 4)) doSomeErrorHandling(); int index = 0; firstByte = (0x000000FF & ((int)buf[index])); index++; anUnsignedByte = (short)firstByte; firstByte = (0x000000FF & ((int)buf[index])); secondByte = (0x000000FF & ((int)buf[index+1])); index = index+2; anUnsignedShort = (char) (firstByte << 8 | secondByte); firstByte = (0x000000FF & ((int)buf[index])); secondByte = (0x000000FF & ((int)buf[index+1])); thirdByte = (0x000000FF & ((int)buf[index+2])); fourthByte = (0x000000FF & ((int)buf[index+3])); index = index+4; anUnsignedInt = ((long) (firstByte << 24                         | secondByte << 16                         | thirdByte << 8                         | fourthByte))                         & 0xFFFFFFFFL;

好吧,現(xiàn)在看起來有一點(diǎn)兒復(fù)雜。但是實(shí)際上很直觀。首先,你看到很多這樣的東東:

0x000000FF & (int)buf[index]

首先,把有符號的 byte 提升成 int 類型,然后對這個(gè) int 進(jìn)行按位與操作,僅保留最后 8 個(gè)比特位。因?yàn)?Java 中的 byte 是有符號的,所以當(dāng)一個(gè) byte 的無符號值大于 127 的時(shí)候,表示符號的二進(jìn)制位將被設(shè)置為 1(嚴(yán)格來說,這個(gè)不能算是符號位,因?yàn)樵谟?jì)算機(jī)中數(shù)字是按照補(bǔ)碼方式編碼的),對于 Java 來說,這個(gè)就是負(fù)數(shù)。當(dāng)將負(fù)數(shù)數(shù)值對應(yīng)的 byte 提升為 int 類型的時(shí)候,0 到 7 比特位將會被保留,8 到 31 比特位會被設(shè)置為 1。然后將其與 0x000000FF 進(jìn)行按位與操作來擦除 8 到 31 比特位的 1。上面這句代碼可以簡短的寫作:

0xFF & (int)buf[index]

Java 自動(dòng)填充 0xFF 的前導(dǎo)的 0 ,并且在 Java 中,位操作符 & 會導(dǎo)致 byte 自動(dòng)提升為 int。

接下來你看到的是很多的按位左移運(yùn)算符 <<。 這個(gè)操作符會對左操作數(shù)按位左移右操作數(shù)指定的比特位。所以,如果你有一個(gè) int foo = 0x000000FF,那么 foo << 8 會得到 0x0000FF00,foo << 16 會得到 0x00FF0000。

最后是按位或操作符 |。假設(shè)你現(xiàn)在把一個(gè)無符號短整型的 2 個(gè)字節(jié)加載到了對應(yīng)的整數(shù)中,你會得到 0x000000120x00000034 兩個(gè)整數(shù)?,F(xiàn)在你把第一個(gè)字節(jié)左移 8 位得到 0x000012000x00000034,然后你需要把他們再拼合回去。所以需要進(jìn)行按位或操作。0x00001200 | 0x00000034 會得到 0x00001234,這樣就可以存儲到 Java 中的 char 類型。

這些都是基礎(chǔ)操作。但是對于無符號 int,你需要把它存儲到 long 類型中。其他操作和前面類似,只是你需要把 int 提升為 long 然后和 0xFFFFFFFFL 進(jìn)行按位與操作。最后的 L 用來告訴 Java 請把這個(gè)常量視為 long 來處理。

向網(wǎng)絡(luò)寫入字節(jié)序

假設(shè)現(xiàn)在我們要把上面步驟中我們讀取到的數(shù)值寫入到緩沖區(qū)。我們當(dāng)時(shí)是按照無符號 byte,無符號 short 和無符號 int 的順序讀取的,現(xiàn)在,甭管什么原因吧,我們打算按照無符號 int,無符號 short 和無符號 byte 的順序來寫出。

buf[0] = (anUnsignedInt & 0xFF000000L) >> 24; buf[1] = (anUnsignedInt & 0x00FF0000L) >> 16; buf[2] = (anUnsignedInt & 0x0000FF00L) >> 8; buf[3] = (anUnsignedInt & 0x000000FFL); buf[4] = (anUnsignedShort & 0xFF00) >> 8; buf[5] = (anUnsignedShort & 0x00FF); buf[6] = (anUnsignedByte & 0xFF);
字節(jié)序到底是怎么回事兒?這是什么意思?我需要關(guān)注嗎?以及,網(wǎng)絡(luò)字節(jié)序什么樣的?

Java 中所使用的“高位優(yōu)先”字節(jié)序又被稱為“網(wǎng)絡(luò)字節(jié)序”。Intel x86 處理器是“低位優(yōu)先”字節(jié)序(除非你在上面運(yùn)行 Java 程序)。x86 系統(tǒng)創(chuàng)建的數(shù)據(jù)文件通常是(但不是必須的)低位優(yōu)先的,而 Java 程序創(chuàng)建的數(shù)據(jù)文件通常是(但不是必須的)高位優(yōu)先的。任何系統(tǒng)都可以按照自己需要的字節(jié)序來輸出數(shù)據(jù)。

字節(jié)序是什么意思?

“字節(jié)序”是指計(jì)算機(jī)是按照何種順序在內(nèi)存中存儲數(shù)值的。常見的無非是高位優(yōu)先和低位優(yōu)先兩種模式。你當(dāng)然需要關(guān)注字節(jié)序的問題了,否則,如果你按照高位優(yōu)先的字節(jié)序去讀取一個(gè)低位優(yōu)先字節(jié)序存儲的數(shù)據(jù)文件,很可能就只能得到亂七八糟的數(shù)據(jù)了,反之亦然。

任何數(shù)值,無論是何種表達(dá)方式,比如 5000,000,007 或者它的 16 進(jìn)制格式 0x1DCD6507,都可以看做是數(shù)字字符串。對于一個(gè)數(shù)字字符串,我們可以認(rèn)為它有開始(最左),有結(jié)束(最右)。在英語中,第一個(gè)數(shù)字就是最高位數(shù)字,例如 5000,000,007 中的 5 實(shí)際上表示的是 500,000,000。最后一位數(shù)字是最低位數(shù)字,例如 500,000,007 中的 7 對應(yīng)的值是 7。

當(dāng)我們說到字節(jié)序的時(shí)候,我們是參照我們寫數(shù)字時(shí)候的順序。我們總是從高位開始寫,然后是次高位,直到最低位,是不是這樣???

在上面的例子中,數(shù)值 500,000,007,對應(yīng) 16 進(jìn)制表示方式是 0x1DCD6507,我們把它分成 4 個(gè)獨(dú)立的字節(jié):0x1D, 0xDC, 0x650x07,對應(yīng) 10 進(jìn)制的值 29, 205, 101 和 7。最高位字節(jié) 29 表示 29 *256 * 256 * 256 = 486539264,接下來是 205,表示 205 * 256 * 256 = 13434880,然后是 101,表示 101 * 256 = 25856,最后一個(gè) 7 就是 7 * 1 = 7。它們的值:

486539264 + 13434880 + 25856 + 7 = 500,000,007

當(dāng)計(jì)算機(jī)在它的內(nèi)存中存儲這 4 個(gè)字節(jié)的時(shí)候,假設(shè)存儲到內(nèi)存的地址是 2056, 2057, 2058 和 2059。那么問題來了:到底在哪個(gè)內(nèi)存地址上存儲哪個(gè)字節(jié)呢?它可能是在地址 2056 存儲 29, 2057 存儲 205,2058 存儲 101,2059 存儲 7,就像你寫下這個(gè)數(shù)字的順序一樣,我們稱之為高位優(yōu)先。但是,其他的計(jì)算機(jī)架構(gòu)可能是在 2056 存儲 7,2057 存儲 101, 2058 存儲 205, 2059 存儲 29,這樣的順序我們稱之為低位優(yōu)先。

針對 2 個(gè)字節(jié)的以及 8 個(gè)字節(jié)的存儲方式,也是同樣的。最高位字節(jié)稱為 MSB,最低位字節(jié)稱為 LSB。

好吧,那么我為什么要關(guān)心字節(jié)序的問題?

這個(gè)視情況而定了。通常情況下你不需要關(guān)心這個(gè)問題。無論你在什么平臺運(yùn)行 Java 程序,它的字節(jié)序都是一樣的,所以你就無需關(guān)心字節(jié)序的問題。

但是,當(dāng)你要處理其他語言產(chǎn)生的數(shù)據(jù)呢?那么,字節(jié)序就是一個(gè)大問題了。你必須得保證你按照數(shù)據(jù)被編碼的順序來進(jìn)行解碼,反之亦然。如果你足夠幸運(yùn),通常在 API 或者協(xié)議規(guī)范、文件格式說明中找到關(guān)于字節(jié)序的說明。如果不巧……祝你好運(yùn)吧!

最重要的是,你需要清晰的了解你所使用的字節(jié)序是什么樣的以及你需要處理的數(shù)據(jù)的字節(jié)序是什么樣的。如果二者不同,你需要進(jìn)行額外的處理來保證正確性。還有就是,如果你需要處理無符號數(shù)值,你需要確保將正確的字節(jié)放到對應(yīng) integer/short/long 類型的正確位置。

網(wǎng)絡(luò)字節(jié)序又是什么?

當(dāng)設(shè)計(jì) ip 協(xié)議的時(shí)候,高位優(yōu)先字節(jié)序被設(shè)計(jì)為網(wǎng)絡(luò)字節(jié)序。在 IP 報(bào)文中德數(shù)值類型都是按照網(wǎng)絡(luò)字節(jié)序存儲的。產(chǎn)生報(bào)文的計(jì)算機(jī)所使用的字節(jié)序稱為“宿主機(jī)字節(jié)序”,可能和網(wǎng)絡(luò)字節(jié)序一樣,也可能不一樣。和網(wǎng)絡(luò)字節(jié)序一樣,Java 中的字節(jié)序是高位優(yōu)先的。

為什么沒有無符號類型?

為什么 Java 不提供無符號類型呢?好問題!我也常常覺得這個(gè)事情非常詭異,尤其是當(dāng)時(shí)已經(jīng)有很多網(wǎng)絡(luò)協(xié)議都使用無符號類型了。在 1999 年,我在 Web 上也找了很久(那個(gè)時(shí)候 google 還沒有這么棒),因?yàn)槲铱偸怯X得這事兒不應(yīng)該是這樣。直到有一天我采訪 Java 發(fā)明者中的一位(是 Gosling 嗎?不太記得了,要是我保存了當(dāng)時(shí)的網(wǎng)頁就好了),這位設(shè)計(jì)者說了一段話,大意是:“嘿!無符號類型把事情搞復(fù)雜了,沒有人真正需要無符號類型,所以我們把它趕出去了”。

這里有一個(gè)頁面,是記錄了一次對 James Gosling 的采訪,看看能否收到一些啟發(fā):

http://www.gotw.ca/publications/c_family_interview.htm

問:程序員經(jīng)常討論使用“簡單語言”編程的優(yōu)點(diǎn)和缺點(diǎn)。你怎么看待這個(gè)問題?你覺得 C/C++/Java 算是簡單語言嗎?

Ritchie: 略

Stroustrup:略

Gosling:作為一個(gè)語言設(shè)計(jì)者,我不太理解所謂的“簡單”結(jié)束了是什么意思,我希望 Java 開發(fā)者把這個(gè)概念留在他自己腦海里就好啦。舉例來說,按照那個(gè)定義,Java 不算是簡單語言。實(shí)際上很多語言都會在極端案例下完蛋,那些極端案例是人們都不會理解的。你去問 C 語言開發(fā)人員關(guān)于無符號的問題,你很快就會發(fā)現(xiàn)沒有幾個(gè) C 語言開發(fā)人員真正理解無符號類型到底發(fā)生了些什么,什么是無符號運(yùn)算。這些事情讓 C 語言變得復(fù)雜。我覺得 Java 語言是非常簡單的。

另外,參考:

http://www.artima.com/weblogs/viewpost.jsp?thread=7555

Oak 往事……

by Heinz Kabutz

2003 年 7 月 15

為了豐富我對 Java 歷史的了解,我開始研究 Sun 的網(wǎng)站,無意間發(fā)現(xiàn)了 Oak 0.2 的語言規(guī)范書。Oak 是 Java 語言最早使用的名稱,這份文檔算是現(xiàn)存的最古老的關(guān)于 Oak 的文檔了。

……

無符號整數(shù)(3.1 節(jié))

規(guī)范書說:“8 比特,16 比特,32 比特,64 比特的,這 4 種不同寬度的整數(shù)類型都是有符號的,除非在前面加上 unsigned 修飾符”。

在側(cè)欄中又說:“無符號類型尚未實(shí)現(xiàn);可能永遠(yuǎn)也不會實(shí)現(xiàn)了。” 好吧,就是這樣了。

Oak 語言規(guī)范可以從 https://duke.dev.java.net/green/OakSpec0.2.ps 下載 PostScript 版本,或者從 http://www.me.umn.edu/~shivane/blogs/cafefeed/resources/14-jun-2007/OakSpec0.2.zip 下載壓縮的 PDF 版本。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 樟树市| 宿迁市| 江源县| 玉龙| 沿河| 宁安市| 怀集县| 正阳县| 东阿县| 青州市| 嘉义市| 太保市| 砚山县| 上林县| 浮梁县| 沁水县| 涟源市| 苗栗县| 莎车县| 商洛市| 凤翔县| 舞钢市| 伊宁市| 丰顺县| 凤山市| 金山区| 锦屏县| 伽师县| 横山县| 苗栗市| 浑源县| 襄垣县| 陵川县| 宽甸| 玉山县| 雅安市| 营口市| 孙吴县| 洛隆县| 达日县| 那坡县|