<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    amp@java

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      99 隨筆 :: 0 文章 :: 228 評論 :: 0 Trackbacks
    當讀寫二進制文件,或者要把非標準長度的整數與標準長度的整數互相轉換時,就要用到大量的位操作,雖然看起來很簡單,實際上里面卻有很多細節很容易出錯。

    首先,Java有些標準跟C/C++是不同的:

    1、Java采用高字節在前的方式讀寫數據,例如要把一個4字節的int數值寫入文件時,它是按照從高字節到低字節的順序寫入的,讀取的時候也是這樣讀出來。
    而C/C++則采用平臺相關的方式,在Windows平臺采用低字節在前的方式,在Linux/Unix平臺則采用高字節在前的方式。
    如果Java要讀取C/C++創建的二進制文件,就要注意這個問題,最好先搞清楚原來的文件是采用哪種方式創建的。網絡通信也要注意。

    2、Java沒有無符號數,無論byte,short,int,long都是有符號整數,而C/C++有個unsigned關鍵字可以設置一個數值為無符號數。

    3、Java的整數基本數據類型就是byte,short,int,long這幾個,長度分別為1,2,4,8字節,C/C++可以用typedef定義各種數據類型。

    第二,Java是采用補碼來存放整數的。
    有時候覺得補碼的定義有些奇怪,實際上可以這樣理解:

    把一個整數從0一直往上加1,加到溢出就變成了負數的最小值,然后再繼續加1,最后又能回到0,實際上就是一個輪回。
    例如一個byte類型的整數,一共有8位,能表示256個數值,采用補碼的話數值范圍就是-128~127,表示方法如下:
    0        0000 0000
    1        0000 0001
    .
    .
    126    0111 1110
    127    0111 1111
    -128   1000 0000
    -127   1000 0001
    .
    .
    -1       1111 1111
    0         0000 0000

    第三、不同長度的整數轉換。
    如果是從較短的數轉成較長的數,很簡單,如果是正數就在高字節補0,如果是負數就在高字節補1。
    例如byte的127轉為short的127:
    byte:0111 1111
    short:0000 0000 0111 0111
    byte的-127轉為short的-127
    byte:1000 0001
    short:1111 1111 1000 0001
    如果是從較長的數轉成較短的數,實際上就是把高位都截斷了,所以轉出來的數值可能完全不是一回事了。
    例如short的256轉為byte:
    short:0000 0001 0000 0000
    byte: 0000 0000
    把256變成了0
    short的-255轉成byte:
    short:1111 1111 0000 0001
    byte:0000 0001
    把-255變成了1

    第四、位運算操作符及它們的優先級
    Java的位運算操作符包括:~非,|按位或,&按位與,^按位異或,<<左移,>>右移,>>>右移左側補0
    各種運算符的優先級如下表所示:
    優先級
    運算符
    結合性
    1
    () [] .
    從左到右
    2
    ! +(正) -(負) ~ ++ --
    從右向左
    3
    * / %
    從左向右
    4
    +(加) -(減)
    從左向右
    5
    << >> >>>
    從左向右
    6
    < <= > >= instanceof
    從左向右
    7
    == !=
    從左向右
    8
    &(按位與)
    從左向右
    9
    ^
    從左向右
    10
    |
    從左向右
    11
    &&
    從左向右
    12
    ||
    從左向右
    13
    ?:
    從右向左
    14
    = += -= *= /= %= &= |= ^= ~= <<= >>= >>>=
    從右向左
    根據該表可以看到,位運算操作符的優先級各有不同,分別為:
    1、~
    2、>> << >>>
    3、&
    4、^
    5、|
    另外需要特別注意的是,除了~,其他位運算操作的優先級都低于加減,所以要記得以下語句是返回32而不是7!
    1<<2+3
    還有就是&、^、|的優先級都是低于邏輯操作符的,因此下面的語句會編譯出錯,幸好Java不像C那樣對所有大于1的值都認為是真,否則下面的語句也能編譯通過,但可能與你的意圖不太一樣,可能調試半天才發現。
    if(3&1>0)
    如果記不清楚,還是按照你的意圖加上括號最保險。

    第五、字節數組與整數之間的轉換
    為了把一個整數存入文件,或者從文件中讀取一個整數,需要經常在字節數組和整數之間轉換,這個過程要用到大量的位運算。
    首先需要記住的是,在參與所有運算前,Java都會把byte、short類型的值都轉換成int,然后再對轉換后的int進行操作。例如下面的語句會編譯出錯:
    byte a=10,b=20,c;
    c=a+b;

    因為a和b在相加前都被轉成了int,最后得到的結果是個int類型的值,如果要賦給byte類型的c,必須顯式地進行類型轉換,即把第二句改為:
    c=(byte)(a+b)

    這一點很關鍵,因為對于一個最高位為1的byte類型的整數(負數),在運算之前它會被強制轉換成int類型,根據上面所說的第三點,其實就是往前面的三個高字節補上1,這樣一來,它在參與位運算的過程中,就不僅僅是它本身的8個bit參與了,實際上連前3個字節的24個bit(均為1)也參與了。例如有一個整數i=1082163328,它的二進制表示為:
    01000000 10000000 10000000 10000000
    分為4個字節存儲,除了第一個字節是正數外,其余3個字節均為負數。假如用a代表最高字節的值,用b代表其他三個字節的值,如果按照通常的理解,你可能會這樣得到i的值:
    i=(a<<24)+(b<<16)+(b<<8)+b

    如果a和b都是正數,上面的等式是成立的,但是在這個例子里,卻是錯的,因為上式中的a和b都已經被強制轉換成了int類型再參加運算,實際上
    a=00000000 00000000 00000000 01000000
    b=11111111 11111111 11111111 10000000
    i=01000000 00000000 00000000 00000000+11111111 10000000 00000000 00000000+11111111 11111111 10000000 00000000+11111111 11111111 11111111 10000000
    最后得到的結果是1065320320,不是原來的值了。
    為了不讓byte在強制轉換成int的過程加入了我們不想要的高位1,我們需要把它跟0xff進行與操作,i的值應該這樣運算:
    = ( ( a& 0xff ) << 24 ) +( ( b & 0xff ) << 16 ) + ( ( b & 0xff ) << 8 ) + ( b & 0xff )

    注意,因為&和<<的優先級都低于+,所以上面的括號是不能少的。不過由于跟0xff與操作之后,其余24位都變成了0,因此可以把+改為|操作,因為任何值與0進行或操作都得到本身:
    = ( a & 0xff ) << 24 | ( b & 0xff ) << 16 | ( b & 0xff ) << 8 | ( b & 0xff )

    由于<<的優先級高于|,所以省了一些括號。最高字節可以不與0xff進行與操作,因為它轉換成int后左邊增加的3個字節都在左移24位時被去掉了:
    = a << 24 | ( b & 0xff ) << 16 | ( b & 0xff ) << 8 | ( b & 0xff )


    把int轉為字節數組的時候比較簡單,直接右移截斷即可:
    byte[] b = new byte[4];
    b[0= (byte) (i >> 24);
    b[1= (byte) (i >> 16);
    b[2= (byte) (i >> 8);
    b[3= (byte) i;


    第六、非標準長度整數的存儲和讀取
    假如有兩個變量,他們的值可以用12個bit來表示,如果我們用16bit的short類型來表示一個變量,那么兩個變量就需要4個字節,而實際上它們只需要3個字節就能表示出來,如果存儲空間比較有限,寫入文件時可以把它們存放在3個字節里面,但是讀寫過程就需要進行轉換。
    在內存里,它們都是標準的數據類型:
    short a,b;

    寫入文件時,我們用第一個字節和第二個字節的前半部分來表示a,把第二個字節的后半部分和第三個字節來表示b,即:
    1:xxxx xxxx
    2:xxxx yyyy
    3:yyyy yyyy
    x和y都表示一個bit,分別用來存放a和b。寫入時先把a和b轉為字節數組:
    byte[] out = new byte[3];
    out[
    0= (byte) ( a >> 4 );//把a的高8位放在第一個字節
    out[1= (byte) ( a << 4 );//先把a左移四位,在右邊補上4個0,第二個字節的高4位就是a的低4位了,第二個字節的高4位已經生成,低4位還是0
    out[1|= (byte) ( b >> 8 & 0x0f );//b右移8位,并與0x0f進行與操作,實際上就只保留了b的高4位,并且是在字節的低4位上,跟第二步得到的字節進行或操作,就生成了第二個字節
    out[2= (byte) b;//把b的高4位截斷就得到了低8位
    然后再把這個字節數組寫入文件,就可以用3個字節表示兩個整數了。
    讀取:
    =(short)( (out[0& 0xff<< 4 | ( out[1& 0xf0 )>>4);
    = (short)((out[1& 0x0f<< 8 | ( out[2& 0xff));
    posted on 2012-04-08 16:56 amp@java 閱讀(1840) 評論(2)  編輯  收藏 所屬分類: Java common

    評論

    # re: Java的位操作 2012-04-09 14:53 五月花
    二進制就是二進制,還要弄清是什么語言創建的?怎么創建的?  回復  更多評論
      

    #  內容請不要發表任何與政治相關的內容 2012-04-10 16:36 內容請不要發表任何與政治相關的內容
    內容請不要發表任何與政治相關的內容  回復  更多評論
      

    主站蜘蛛池模板: 亚洲高清国产拍精品青青草原| 3344免费播放观看视频| 国内自产拍自a免费毛片| 亚洲av无码片在线观看| 国产福利视精品永久免费| 亚洲首页在线观看| 免费观看黄色的网站| 亚洲国产片在线观看| 美女视频黄是免费的网址| 亚洲综合色7777情网站777| 99久久综合国产精品免费| 国产成人亚洲综合一区| 思思99re66在线精品免费观看| 亚洲人成人网站18禁| 四虎影永久在线高清免费| 日本黄页网址在线看免费不卡| 国产成人亚洲精品影院| 免费无码AV一区二区| 国产成人A人亚洲精品无码| 日本高清高色视频免费| 亚洲男女一区二区三区| 成年轻人网站色免费看| 看Aⅴ免费毛片手机播放| 77777亚洲午夜久久多人| 国产午夜无码精品免费看动漫| 亚洲视频在线不卡| 女人被男人躁的女爽免费视频 | 国产人成亚洲第一网站在线播放| 成人午夜视频免费| 无码精品人妻一区二区三区免费 | 久久久久亚洲精品美女| 91福利视频免费| 在线综合亚洲中文精品| 亚洲人成电影网站国产精品 | 日韩免费a级在线观看| 一个人看的免费视频www在线高清动漫 | 久久久久国产精品免费网站| 涩涩色中文综合亚洲| 国产亚洲精品无码专区| av大片在线无码免费| 一本久久免费视频|