資料
在說變數之前,我們先來說一下資料是什麼?變數和資料是分不開的。
資料在計算機中實際上就是二進位制,0101010101。這些二進位制實際上是不方便操作的,也不方便管理,哪個人能夠直接識別二進位制串?
沒有吧。所以為了方便管理和操作,在幾乎所有的高階語言中都引入了資料型別和變數的概念。
資料型別
知道了什麼是資料,那麼資料型別就好解釋了,資料型別實際上就是用於對資料歸類的,方便理解和操作。
資料在計算機內部都是二進位制,二進位制程式設計師很難直接理解和操作啊,對於整數使用 int/long,對於小數使用 float/double, 對於字串使用 String,使用這些資料型別和類,程式設計師理解和操作起來就方便多了
通過資料型別定義為資料進行分類之後,計算機就可以規範的為不同的資料型別分配不同大小的記憶體空間。
資料型別的作用
- 型別檢查提高安全性
- 合理的記憶體分配
例如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位
浮點數的精度問題
-
儲存的小數數值可能是模糊值
為什麼這麼說呢?看下面的程式碼
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求餘數,不會存在無限值的情況。
-
浮點數的有效位及精度
浮點型所能表示的有效位是有限的,所以哪怕是整數只要超出有效位數,也只能存近似值,也就是超出最低有效位的資料將會丟失,從而造精度丟失。 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位。
-
如何解決浮點數精度丟失問題
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中實際上是沒有物件變數的,因為資料並沒有放在變數中,放的是引用。也就是在物件型別的變數這個杯子中放置的是物件的引用(地址),引用就像是一個遙控器。
整個邏輯是,變數中放引用,引用指向物件資料。
我不能保證每一個地方都是對的,但是可以保證每一句話,每一行程式碼都是經過推敲和斟酌的。希望每一篇文章背後都是自己追求純粹技術人生的態度。
永遠相信美好的事情即將發生。