不可忽視的基礎——java位運算

相信你們和我同樣,接觸java這門語言的時候就聽過java位運算的鼎鼎大名,固然也僅限於據說過。平常開發過程當中使用過麼?使用位運算的好處是什麼?java

想要真正理解java位運算,首先要搞清楚,這個「位」表明的含義。bash

一切的起源:二進制

位:二進制位,簡稱「位」。是二進制記數系統中表示小於2的整數的符號,通常用1或 0表示,是具備相等機率的兩種狀態中的一種。二進制位的位數可表示一個機器字的字長,一個二進制位包含的信息量稱爲一比特(bit)。ui

舉個栗子:
int佔4個字節(byte)
1byte = 8bit
換算下來,一個int類型即佔32bit
int i = 88; 這裏的88爲十進制,轉換爲二進制爲:1011000,使用完整的32位表示即爲:00000000 00000000 00000000 01011000this

上文中的00000000 00000000 00000000 01011000即爲十進制88轉爲二進制的 原碼 ,與其相關的定義還有 反碼補碼spa

關於原碼、反碼和補碼

在計算機內,有符號數有三種表示法:原碼、反碼以及補碼。
原碼:就是二進制定點表示法,即最高位爲符號位,「0」正負「1」,其他位表示數值的大小。
反碼:正數的反碼與其原碼相同;負數的反碼是對正數逐位取反,符號位保持爲1。
補碼:正數的補碼與其原碼相同;負數的補碼是在其反碼的末位加1。code

以上內容摘自百度百科
一樣的,咱們使用 「88」 舉例說明原碼、反碼以及補碼。
「88」的原碼:00000000 00000000 00000000 01011000
「88」的反碼:00000000 00000000 00000000 01011000
「88」的補碼:00000000 00000000 00000000 01011000
對於負數 「-88」,其原碼、反碼以及補碼以下:
「-88」的原碼:10000000 00000000 00000000 01011000
「-88」的反碼:11111111 11111111 11111111 10100111
「-88」的補碼:11111111 11111111 11111111 10101000cdn

爲何要使用補碼?

參考知乎熱門回答:原碼、反碼、補碼的產生、應用以及優缺點有哪些?
簡單來講,就是計算機計算減法時有各類不方便,因而發明了反碼,結果發現反碼也有缺陷(有兩個零存在:「+0」和「-0」),進而發明了補碼解決這個問題。
在計算機系統中,數值一概用補碼來表示和存儲。緣由在於,使用補碼,能夠將符號位和數值域統一處理;同時,加法和減法也能夠統一處理。此外,補碼與原碼相互轉換,其運算過程是相同的,不須要額外的硬件電路。
有關補碼的意義及做用在上面的連接裏討論的很是詳盡,我這裏就不班門弄斧了,理解就好~
對原碼、反碼以及補碼有一個初步的認知後,咱們接下來再看位運算就會清晰不少。對象

關於位運算

關於位運算,這裏運用哲學上三個究極問題試圖講解清楚位運算到底是何方神聖:什麼是位運算?位運算的做用?位運算有什麼優點?blog

什麼是位運算

百度百科是這麼解釋的
程序中的全部數在計算機內存中都是以二進制的形式儲存的。位運算就是直接對整數在內存中的二進制位進行操做。好比,and運算原本是一個邏輯運算符,但整數與整數之間也能夠進行and運算。舉個例子,6的二進制是110,11的二進制是1011,那麼6 and 11的結果就是2,它是二進制對應位進行邏輯運算的結果(0表示False,1表示True,空位都當0處理)。內存

下表列出了位運算符的基本運算(A = 8, B = 9)

操做符 描述 例子
按位與& 若是相對應位都是1,則結果爲1,不然爲0 A&B=8,即1000
按位或| 若是相對應位都是0,則結果爲0,不然爲1 A|B=9,即1001
按位異或^ 若是相對應位值相同,則結果爲0,不然爲1 A^B=1,即0001
按位取反~ 按位取反運算符翻轉操做數的每一位,即0變成1,1變成0 ~A=7,即0111
左移 << 按位左移運算符。左操做數按位左移右操做數指定的位數 A << 2 = 32,即1000 00
右移 >> 按位右移運算符。左操做數按位右移右操做數指定的位數 A >> 2 = 2,即0010

位運算的做用及優點

莫慌
我嘗試脫離實際應用場景描述清楚位運算的做用及優點,而後發現脫離實際講應用是件很是困難的事情,其難度不亞於買彩票。因此這裏結合Android原碼中的MeasureSpec類來描述位運算的做用和優點。
熟悉Android View體系的小夥伴應該都對 MeasureSpec不陌生。不熟悉的請自行Google,否則下面你看起來可能就會有些雲裏霧裏。咱們來看它的代碼:

public static class MeasureSpec {
  
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        
        public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

       
        public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }

       
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
    }
複製代碼

代碼不難理解,上面就運用了不少位運算。咱們都知道MeasureSpec是用來操做View的測量模式以及測量大小的。這個測量模式和測量大小在系統中使用一個32位的int類型的參數表示。若是讓咱們本身去實現這樣一個操做測量模式和測量大小的類,咱們大概會這麼寫:

public class MeasureSpec{

    public static final int UNSPECIFIED = 0;
    public static final int EXACTLY     = 1;
    public static final int AT_MOST     = 2;

    /**
     * 測量模式
     */
    private int mode;

    /**
     * 測量大小
     */
    private int size;

    public int getMode() {
        return mode;
    }

    public void setMode(int mode) {
        this.mode = mode;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}
複製代碼

而後每次對View進行操做的時候都會 new 一個MeasureSpec對象,對其的mode和size參數進行相應的操做。

這裏原碼就很巧妙的運用了位運算簡化了相應的操做,使用32位的二進制來操做mode和size:高兩位表示mode,低30位表示size,避免了頻繁的建立對象,更省內存,讓我等對位運算不了解的拍手稱秒哇。

總結

不一樣於其餘文章講解位運算的概念,本文更側重於運用位運算的做用及優點。前人篳路藍縷,以啓山林,以聰明才智發明了位運算這種簡潔高效的運算符,但願你能理解並正確發揮其做用,走上人生的巔峯~

相關文章
相關標籤/搜索