Java雜記1—資料型別和變數

冰洋發表於2018-04-25

資料

在說變數之前,我們先來說一下資料是什麼?變數和資料是分不開的。

資料在計算機中實際上就是二進位制,0101010101。這些二進位制實際上是不方便操作的,也不方便管理,哪個人能夠直接識別二進位制串?

沒有吧。所以為了方便管理和操作,在幾乎所有的高階語言中都引入了資料型別和變數的概念。

資料型別

知道了什麼是資料,那麼資料型別就好解釋了,資料型別實際上就是用於對資料歸類的,方便理解和操作。

資料在計算機內部都是二進位制,二進位制程式設計師很難直接理解和操作啊,對於整數使用 int/long,對於小數使用 float/double, 對於字串使用 String,使用這些資料型別和類,程式設計師理解和操作起來就方便多了

通過資料型別定義為資料進行分類之後,計算機就可以規範的為不同的資料型別分配不同大小的記憶體空間。

資料型別的作用

  1. 型別檢查提高安全性
  2. 合理的記憶體分配

例如java中就有八種基本資料型別

  • 整數型別: byte/short/int/long
  • 小數型別 :float/double
  • 字元型別:char
  • 布林型別:boolean

就像世界萬物都是由元素週期表中的108個基本元素組成的,基本資料型別就相當於化學中的基本元素,而物件就相當於世界萬物,String就是char組成的,日期型別就是long。

浮點數
浮點型別介紹

long型別轉換成float型別是不需要強制型別轉換的,也就是說相對於flaot型別,long型別是小型別,儲存的範圍要更小。然而flaot只佔了4個位元組,而long卻佔了8個位元組,long型別的儲存空間要比float型別大。

浮點數使用 IEEE(電氣和電子工程師協會)格式。 浮點數型別使用 符號位、指數、有效位數(尾數)來表示。

在java中,float 和 double 的結構如下:

型別|符號位|指數域|有效位域

-|-|-

float|1位|8位|23位

double|1位|11位|52位

浮點數的精度問題
  1. 儲存的小數數值可能是模糊值

    為什麼這麼說呢?看下面的程式碼

    public static void main(String[] args) {
        double d1 = 0.1;
        double d2 = 0.2;
        System.out.println(d1+d2 == 0.3);
        System.out.println(d1+d2);
    }
    複製程式碼

    上述程式碼的執行結果是false 0.30000000000000004

    也就是double型別的0.1加上0.2 並不是精確的0.3,需要注意的是出現這樣的結果不是因為運算錯誤,而是二進位制無法精確的表示十進位制的0.3。就像十進位制的3/10無法用小數精準表示一樣,很多小數都無法準確的用浮點型表示。

    小數的十進位制轉成二進位制,十進位制的小數要不斷乘2,直到最後的結果為整數才是最終的二進位制值,但有可能最後的結果是一個無限值,浮點型就無法表示了。

    但是對於 整數 來說,在浮點數的有效範圍內,則都是精確的。轉換演算法:十進位制的整數轉成二進位制的演算法是不斷對2求餘數,不會存在無限值的情況。

  2. 浮點數的有效位及精度

    浮點型所能表示的有效位是有限的,所以哪怕是整數只要超出有效位數,也只能存近似值,也就是超出最低有效位的資料將會丟失,從而造精度丟失。 float型別的二進位制有效位是24位,對應十進位制的7 ~ 8位數字;double型別的二進位制53位,對應十進位制的10 ~ 11位數字。

    double、float型別 所能表示的範圍比int、long型別表示的範圍要廣。但是不能完美的表示整型,浮點型別的精度丟失會造成一些問題。

    public static void main(String[] args) {
        int a = 3000000;
        int b = 30000000;
        float f1 = a;
        float f2 = b;
        System.out.println("3000000==3000001 "+(f1==f1+1));
        System.out.println("30000000==30000001 "+(f2==f2+1));
        System.out.println("3000000的有效二進位制位數:"+ Integer.toBinaryString(a).length());
        System.out.println("30000000的有效二進位制位數:"+ Integer.toBinaryString(b).length());
    }
    複製程式碼

    執行結果:

    3000000 == 3000001  false 30000000 == 30000001  true 3000000的有效二進位制位數: 22 30000000的有效二進位制位數: 25

    上面的例子很好體現了精度丟失所帶來的後果:30000000==30000001 的比較居然為true了。而造成這種結果的原因就是 30000000的有效二進位制位數是25位,超出了float所能表示的有效位24位,最後一位就被捨去,所以就造成在剛加的1也被捨去,因此30000000的加一操作前後的浮點型表示是一樣的。

    如果上面的例子中的浮點型用double就不會丟失精度,因為double的精度是52位。

  3. 如何解決浮點數精度丟失問題

    JDK為此提供了兩個高精度的大數操作類給我們:BigInteger、BigDecimal。

Char型別

char用於表示一個字元,這個字元可以是中文字元,也可以是英文字元。賦值時把常量字元用單引號括起來。

我們知道在java內部處理字元時,採用的都是Unicode。char本質上是一個固定佔用兩個位元組的無符號正整數,這個正整數對應於Unicode編號,用於表示那個Unicode編號對應的字元。由於固定佔用兩個位元組,char只能表示Unicode編號在65536以內的字元,而不能表示超出範圍的字元。對於超出這個範圍的會使用使用兩個char。

char的賦值
//Ascii碼
char c = 'A'
//中文字元
char c = '哈'
// 十進位制 Unicode編號
char c = 39532
// 16進位制 Unicode編號
char c = 0x9a6c
//Unicode字元形式
char c = '\u9a6c'
複製程式碼
char的運算

由於char本質上是一個整數,所以可以進行整數可以進行的一些運算,在進行運算時會被看做int,但由於char佔兩個位元組,運算結果不能直接賦值給char型別,需要進行強制型別轉換,這和byte, short參與整數運算是類似的。

char型別的比較就是其Unicode編號的比較。

char的加減運算就是按其Unicode編號進行運算,一般對字元做加減運算沒什麼意義,但Ascii碼字元是有意義的。比如大小寫轉換,大寫A-Z的編號是 65-90,小寫a-z的編號是97-122,正好相差32,所以大寫轉小寫只需加32,而小寫轉大寫只需減32。加減運算的另一個應用是加密和解密,將 字元進行某種可逆的數學運算可以做加解密。

變數

記憶體中儲存資料的一塊區域。

為了運算元據,我們需要把資料放入記憶體中,資料在磁碟中是沒有辦法直接操作的,需要先讀取到記憶體中,才能夠進行直接的操作。而記憶體在計算機中實際上就是一塊兒有地址編號的連續空間,在我們把資料放入記憶體中某個位置之後,為了方便找到和操作這個資料,需要給這個位置起一個名字,這個過程就是通過變數宣告和賦值來表示的。

在現實生活中,矽谷廣場有自己的地址編號,文化路86號(記憶體地址編號),但是同時也有自己的名字(變數名)

換句話說,所謂變數,你可以把它看做一個容器,可以用來承載一些東西,什麼意思呢?你的基本型別的值,物件型別的引用都可以放入變數中,這個過程被稱之為變數的賦值。之所以叫變數,是因為它表示的是記憶體中的位置,這個位置存放的值是可以變化的。

雖然變數的值是可以變化的,但名字是不變的,這個名字應該代表程式設計師心目中這塊記憶體位置的意義,這個意義應該是不變的,比如說這個變數int second表示時鐘秒數,在不同時間可以被賦予不同的值,但它表示的就是時鐘秒數。之所以說應該是因為這不是必須的,如果你非要起一個變數名叫age但賦予它身高的值,計算機也拿你沒辦法。

變數就是給資料起名字,方便找不同的資料,它的值可以變,但含義不應變

變數的操作分為兩個部分,變數的宣告和變數的引用。

變數的宣告,必須有變數的型別和變數名

通過宣告變數,每個變數賦予一個資料型別和一個有意義的名字

宣告變數之後,就在記憶體分配了一塊位置,但這個位置的內容是未知的,賦值就是把這塊位置的內容設為一個確定的值。

變數的賦值,使用賦值運算子=

賦值形式很簡單,直接把熟悉的數字常量形式賦值給變數即可,對應的記憶體空間的值就從未知變成了確定的常量。但常量不能超過對應型別的表示範圍

同時我們可以通過生活中的一些小案例來進一步形象化的說明變數:

變數就像是一個杯子,可以容納一些東西。

而杯子,有不同的大小和型別,對應在java中就是int short等他們的大小不一樣,可以放置的資料也是不一樣的。

變數的分類

變數

需要進行解釋的就是物件型別,在java中實際上是沒有物件變數的,因為資料並沒有放在變數中,放的是引用。也就是在物件型別的變數這個杯子中放置的是物件的引用(地址),引用就像是一個遙控器

整個邏輯是,變數中放引用,引用指向物件資料。


我不能保證每一個地方都是對的,但是可以保證每一句話,每一行程式碼都是經過推敲和斟酌的。希望每一篇文章背後都是自己追求純粹技術人生的態度。

永遠相信美好的事情即將發生。

相關文章