茫茫網海中的冷日
         
茫茫網海中的冷日
發生過的事,不可能遺忘,只是想不起來而已!
 恭喜您是本站第 1671764 位訪客!  登入  | 註冊
主選單

Google 自訂搜尋

Goole 廣告

隨機相片
IMG_60D_00021.jpg

授權條款

使用者登入
使用者名稱:

密碼:


忘了密碼?

現在就註冊!

爪哇咖啡屋 : [轉貼]解決 java 數值範圍以及 float 與 double 精度丟失的問題

發表者 討論內容
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]解決 java 數值範圍以及 float 與 double 精度丟失的問題

解決java數值範圍以及float與double精度丟失的問題

下面小編就為大家帶來一篇解決java數值範圍以及float與double精度丟失的問題。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

1.java中int,float,long,double取值範圍


public class TestOutOfBound {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE-(-Integer.MAX_VALUE)); //記憶體溢位
System.out.println(Integer.MAX_VALUE); //2的31次方-1,10個數位,正的20億左右,用在錢上面不一定夠
System.out.println(Integer.MIN_VALUE); //負的2的31次方
System.out.println(Long.MAX_VALUE); //2的64次方-1,19個數位,很大了,可放心用在錢上面
System.out.println(Long.MIN_VALUE); //負的2的64次方
System.out.println(Float.MAX_VALUE); //2的128次方-1,38個數位,比long多了一倍,這個主要用來做簡單數學精確運算使用
System.out.println(Float.MIN_VALUE); //2的-149次方
System.out.println(Double.MAX_VALUE); //2的1024次方-1,308個數位,是float數位的10倍,主要用來做複雜運算和天文運算
System.out.println(Double.MIN_VALUE); //2的-1074次方
}
}

2.float與double精度丟失問題

例子:


舉例:double result = 1.0 - 0.9;
這個結果不用說了吧,都知道了,0.09999999999999998

為什麼會出現這個問題呢,就這是java和其它計算機語言都會出現的問題,下面我們分析一下為什麼會出現這個問題:

float和double型別主要是為了科學計算和工程計算而設計的。他們執行二進位制浮點運算,這是為了在廣泛的數字範圍上提供較為精確的快速近似計算而精心設計的。然而,它們並沒有提供完全精確的結果,所以我們不應該用於精確計算的場合。float和double型別尤其不適合用於貨幣運算,因為要讓一個float或double精確的表示0.1或者10的任何其他負數次方值是不可能的(其實道理很簡單,十進位制系統中能不能準確表示出1/3呢?同樣二進位制系統也無法準確表示1/10)。

浮點運算很少是精確的,只要是超過精度能表示的範圍就會產生誤差。往往產生誤差不是因為數的大小,而是因為數的精度。因此,產生的結果接近但不等於想要的結果。尤其在使用 float 和 double 作精確運算的時候要特別小心。

現在我們就詳細剖析一下浮點型運算為什麼會造成精度丟失?


首先我們要搞清楚下面兩個問題:
(1) 十進位制整數如何轉化為二進位制數
演算法很簡單。舉個例子,11表示成二進位制數:
11/2=5 餘 1
5/2=2 餘 1
2/2=1 餘 0
1/2=0 餘 1
0結束 11二進位制表示為(從下往上):1011
這裡提一點:只要遇到除以後的結果為0了就結束了,大家想一想,所有的整數除以2是不是一定能夠最終得到0。
換句話說,所有的整數轉變為二進位制數的演算法會不會無限迴圈下去呢?絕對不會,整數永遠可以用二進位制精確表示 ,但小數就不一定了。
(2) 十進位制小數如何轉化為二進位制數
演算法是乘以2直到沒有了小數為止。舉個例子,0.9表示成二進位制數
0.9*2=1.8 取整數部分 1
0.8(1.8的小數部分)*2=1.6 取整數部分 1
0.6*2=1.2 取整數部分 1
0.2*2=0.4 取整數部分 0
0.4*2=0.8 取整數部分 0
0.8*2=1.6 取整數部分 1
0.6*2=1.2 取整數部分 0
......... 0.9二進位制表示為(從上往下): 1100100100100......
注意:上面的計算過程迴圈了,也就是說*2永遠不可能消滅小數部分,這樣演算法將無限下去。
很顯然,小數的二進位制表示有時是不可能精確的 。其實道理很簡單,十進位制系統中能不能準確表示出1/3呢?同樣二進位制系統也無法準確表示1/10。
這也就解釋了為什麼浮點型減法出現了"減不盡"的精度丟失問題。

3.解決方法一:

如果不介意自己記錄十進位制的小數點,而且數值不大,那麼可以使用long ,int等基本型別,具體用int還是long要看涉及的數值範圍大小,缺點是要自己處理十進位制小數點,最明顯的做法就是處理貨幣使用分來計算,而不用元(只涉及加減)。

如:


int resultInt = 10 - 9;
double result = (double) resultInt / 100;//最終時候自己控制小數點

4.解決方法二:

使用BigDecmal,而且需要在構造引數使用String型別。

在《Effective Java》這本書中就給出了一個解決方法。該書中也指出,float和double只能用來做科學計算或者是工程計算,在商業計算等精確計算中,我們要用java.math.BigDecimal。

BigDecimal類一個有4個方法,我們只關心對我們解決浮點型資料進行精確計算有用的方法,即

BigDecimal(double value) // 將double型資料轉換成BigDecimal型資料

思路很簡單,我們先通過BigDecimal(double value)方法,將double型資料轉換成BigDecimal資料,然後就可以正常進行精確計算了。等計算完畢後,我們可以對結果做一些處理,比如 對除不盡的結果可以進行四捨五入。最後,再把結果由BigDecimal型資料轉換回double型資料。

這個思路很正確,但是如果你仔細看看API裡關於BigDecimal的詳細說明,你就會知道,如果需要精確計算,我們不能直接用double,而非要用 String來構造BigDecimal不可!所以,我們又開始關心BigDecimal類的另一個方法,即能夠幫助我們正確完成精確計算的 BigDecimal(String value)方法。
// BigDecimal(String value)能夠將String型資料轉換成BigDecimal型資料


那麼問題來了,想像一下吧,如果我們要做一個浮點型資料的加法運算,需要先將兩個浮點數轉為String型資料,然後用 BigDecimal(String value)構造成BigDecimal,之後要在其中一個上呼叫add方法,傳入另一個作為引數,然後把運算的結果(BigDecimal)再轉換為浮 點數。如果每次做浮點型資料的計算都要如此,你能夠忍受這麼煩瑣的過程嗎?至少我不能。所以最好的辦法,就是寫一個類,在類中完成這些繁瑣的轉換過程。這 樣,在我們需要進行浮點型資料計算的時候,只要呼叫這個類就可以了。網上已經有高手為我們提供了一個工具類Arith來完成這些轉換操作。它提供以下靜態 方法,可以完成浮點型資料的加減乘除運算和對其結果進行四捨五入的操作:

public static double add(double v1,double v2)

public static double sub(double v1,double v2)

public static double mul(double v1,double v2)

public static double div(double v1,double v2)

public static double div(double v1,double v2,int scale)

public static double round(double v,int scale)

下面會附上Arith的原始碼,大家只要把它編譯儲存好,要進行浮點數計算的時候,在你的源程式中匯入Arith類就可以使用以上靜態方法來進行浮點數的精確計算了。

附錄:Arith原始碼


import java.math.BigDecimal;
/**
* 由於Java的簡單型別不能夠精確的對浮點數進行運算,這個工具類提供精
* 確的浮點數運算,包括加減乘除和四捨五入。
*/
public class Arith{
//預設除法運算精度
private static final int DEF_DIV_SCALE = 10;
//這個類不能例項化
private Arith(){
}
/**
* 提供精確的加法運算。
* @param v1 被加數
* @param v2 加數
* @return 兩個引數的和
*/
public static double add(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精確的減法運算。
* @param v1 被減數
* @param v2 減數
* @return 兩個引數的差
*/
public static double sub(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精確的乘法運算。
* @param v1 被乘數
* @param v2 乘數
* @return 兩個引數的積
*/
public static double mul(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到
* 小數點以後10位,以後的數字四捨五入。
* @param v1 被除數
* @param v2 除數
* @return 兩個引數的商
*/
public static double div(double v1,double v2){
return div(v1,v2,DEF_DIV_SCALE);
}
/**
* 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale引數指
* 定精度,以後的數字四捨五入。
* @param v1 被除數
* @param v2 除數
* @param scale 表示表示需要精確到小數點以後幾位。
* @return 兩個引數的商
*/
public static double div(double v1,double v2,int scale){
if(scale<0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供精確的小數位四捨五入處理。
* @param v 需要四捨五入的數字
* @param scale 小數點後保留幾位
* @return 四捨五入後的結果
*/
public static double round(double v,int scale){
if(scale<0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
};

以上這篇解決java數值範圍以及float與double精度丟失的問題就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援itread01.com。


原文出處:解決java數值範圍以及float與double精度丟失的問題 - IT閱讀
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Java 程式碼界 3% 的王者?看我是如何解錯這 5 道題的

Java 程式碼界 3% 的王者?看我是如何解錯這 5 道題的

前些日子,阿里妹(妹子出題也這麼難)發表了一篇文章《懸賞徵集!5 道題徵集程式碼界前 3% 的超級王者》——看到這個標題,我內心非常非常激動,因為終於可以證明自己技術很牛逼了。

但遺憾的是,憑藉 8 年的 Java 開發經驗,我發現這五道題自己全解錯了!慘痛的教訓再次證明,我是那被秒殺的 97% 的工程師之一。

不過,好歹我這人臉皮特別厚,雖然全都做錯了,但還是敢於坦然地面對自己。

01、原始型別的 float

第一題是這樣的,程式碼如下:

        }
    }
}

乍一看,這道題也太簡單了吧?

1.0f - 0.9f 的結果為 0.1f, 0.9f - 0.8f 的結果為 0.1f,那自然 a == b 啊。

但實際的結果竟然不是這樣的,太傷自尊了。

float a = 1.0f - 0.9f;
System.out.println(a); // 0.100000024
float b = 0.9f - 0.8f;
System.out.println(b); // 0.099999964

加上兩條列印語句後,我明白了,原來發生了精度問題。

Java 語言支援兩種基本的浮點型別: float 和 double ,以及與它們對應的包裝類 Float 和 Double 。它們都依據 IEEE 754 標準,該標準用科學記數法以底數為 2 的小數來表示浮點數。


但浮點運算很少是精確的。雖然一些數字可以精確地表示為二進位制小數,比如說 0.5,它等於 2-1;但有些數字則不能精確的表示,比如說 0.1。因此,浮點運算可能會導致舍入誤差,產生的結果接近但並不等於我們希望的結果。

所以,我們看到了 0.1 的兩個相近的浮點值,一個是比 0.1 略微大了一點點的 0.100000024,一個是比 0.1 略微小了一點點的 0.099999964。

Java 對於任意一個浮點字面量,最終都舍入到所能表示的最靠近的那個浮點值,遇到該值離左右兩個能表示的浮點值距離相等時,預設採用偶數優先的原則——這就是為什麼我們會看到兩個都以 4 結尾的浮點值的原因。

02、包裝器型別 Float

再來看第二題,程式碼如下:

        }
    }
}

乍一看,這道題也不難,對吧?無非是把原始型別的 float 轉成了包裝器型別 Float,並且使用 equals 替代 == 進行判斷。

這一次,我以為包裝器會解決掉精度的問題,所以我猜想輸出結果為 true。但結果再次打臉——雖然我臉皮厚,但仍然能感覺到臉有些微微的紅了起來。

Float a = Float.valueOf(1.0f - 0.9f);
System.out.println(a); // 0.100000024
Float b = Float.valueOf(0.9f - 0.8f);
System.out.println(b); // 0.099999964

加上兩條列印語句後,我明白了,原來包裝器並不會解決精度的問題。

private final float value;
public Float(float value) {
    this.value = value;
}
public static Float valueOf(float f) {
    return new Float(f);
}
public boolean equals(Object obj) {
    return (obj instanceof Float)
           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}

從原始碼可以看得出來,包裝器 Float 的確沒有對精度做任何處理,況且 equals 方法的內部仍然使用了 == 進行判斷。

03、switch 判斷 null 值的字串

來看第三題,程式碼如下:

        }
    }
}

這道題就有點令我霧裡看花了。

我們都知道,switch 是一種高效的判斷語句,比起 if/else
真的是爽快多了。尤其是 JDK 1.7 之後,switch 的 case 條件可以是 char, byte, short, int, Character, Byte, Short, Integer, String, 或者 enum 型別。

本題中,param 型別為 String,那麼我認為是可以作為 switch 的 case 條件的,但 param 的值為 null,null 和 "null" 肯定是不匹配的,我認為程式應該進入到 default 語句輸出 default。

但結果再次打臉!程式丟擲了異常:

    at com.cmower.java_demo.Test.main(Test.java:7)

也就是說, switch () 的括號中不允許傳入 null。為什麼呢?

我翻了翻 JDK 的官方文件,看到其中有這樣一句描述,我直接搬過來大家看一眼就明白了。

When the switch statement is executed, first the Expression is evaluated. If the Expression evaluates to null, a NullPointerException is thrown and the entire switch statement completes abruptly for that reason. Otherwise, if the result is of a reference type, it is subject to unboxing conversion.

大致的意思就是說,switch 語句執行的時候,會先執行 switch () 表示式,如果表示式的值為 null,就會丟擲 NullPointerException 異常。

那到底是為什麼呢?


        break;
    }
}

藉助 jad,我們來反編譯一下 switch 的位元組碼,結果如上所示。原來 switch () 表示式內部執行的竟然是 (s = param).hashCode(),當 param 為 null 的時候,s 也為 null,呼叫 hashCode() 方法的時候自然會丟擲 NullPointerException 了。

04、BigDecimal 的賦值方式

來看第四題,程式碼如下:

        System.out.println(b);
    }
}

這道題真不難,a 和 b 的唯一區別就在於 a 在呼叫 BigDecimal 構造方法賦值的時候傳入了浮點數,而 b 傳入了字串,a 和 b 的結果應該都為 0.1,所以我認為這兩種賦值方式是一樣的。

但實際上,輸出結果完全出乎我的意料:

System.out.println(b); // 0.1

這究竟又是怎麼回事呢?

這就必須看官方文件了,是時候搬出 BigDecimal(double val) 的 JavaDoc 鎮樓了。


  1. The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

解釋:使用 double 傳參的時候會產生不可預期的結果,比如說 0.1 實際的值是 0.1000000000000000055511151231257827021181583404541015625,說白了,這還是精度的問題。(既然如此,為什麼不廢棄呢?)

  1. The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

解釋:使用字串傳參的時候會產生預期的結果,比如說 new BigDecimal("0.1") 的實際結果就是 0.1。

  1. When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

解釋:如果必須將一個 double 作為引數傳遞給 BigDecimal 的話,建議傳遞該 double 值匹配的字串值。方式有兩種:

double a = 0.1;
System.out.println(new BigDecimal(String.valueOf(a))); // 0.1
System.out.println(BigDecimal.valueOf(a)); // 0.1

第一種,使用 String.valueOf() 把 double 轉為字串。

第二種,使用 valueOf() 方法,該方法內部會呼叫 Double.toString() 將 double 轉為字串,原始碼如下:


public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

05、ReentrantLock

最後一題,也就是第五題,程式碼如下:


public class LockTest {
    private final static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        try {
            lock.tryLock();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

問題如下:

A: lock 是非公平鎖
B: finally 程式碼塊不會丟擲異常
C: tryLock 獲取鎖失敗則直接往下執行


很慚愧,我不知道 ReentrantLock 是不是公平鎖;也不知道 finally 程式碼塊會不會丟擲異常;更不知道 tryLock 獲取鎖失敗的時候會不會直接往下執行。沒法作答了。

連續五道題解不出來,雖然我臉皮非常厚,但也覺得臉上火辣辣的,就像被人狠狠地抽了一個耳光。

容我研究研究吧。

1)lock 是非公平鎖

ReentrantLock 是一個使用頻率非常高的鎖,支援重入性,能夠對共享資源重複加鎖,即當前執行緒獲取該鎖後再次獲取時不會被阻塞。

ReentrantLock 既是公平鎖又是非公平鎖。呼叫無參構造方法時是非公平鎖,原始碼如下:

public ReentrantLock() {
    sync = new NonfairSync();
}

所以本題中的 lock 是非公平鎖,A 選項是正確的。

ReentrantLock 還提供了另外一種構造方法,原始碼如下:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

當傳入 true 的時候為公平鎖,false 的時候為非公平鎖。

那公平鎖和非公平鎖到底有什麼區別呢?

公平鎖可以保證請求資源在時間上的絕對順序,而非公平鎖有可能導致其他執行緒永遠無法獲取到鎖,造成“飢餓”的現象。

公平鎖為了保證時間上的絕對順序,需要頻繁的上下文切換,而非公平鎖會減少一些上下文切換,效能開銷相對較小,可以保證系統更大的吞吐量。

2)finally 程式碼塊不會丟擲異常

Lock 物件在呼叫 unlock 方法時,會呼叫
AbstractQueuedSynchronizertryRelease 方法,如果當前執行緒不持有鎖的話,則丟擲 IllegalMonitorStateException 異常。

所以建議本題的示例程式碼優化為以下形式(進入業務程式碼塊之前,先判斷當前執行緒是否持有鎖):

boolean isLocked = lock.tryLock();
if (isLocked) {
    try {
        // doSomething();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

3)tryLock 獲取鎖失敗則直接往下執行

tryLock() 方法的 Javadoc 如下:

Acquires the lock if it is available and returns immediately with the value true. If the lock is not available then this method will return immediately with the value false.


中文意思是如果鎖可以用,則獲取該鎖,並立即返回 true,如果鎖不可用,則立即返回 false。

針對本題的話, 在 tryLock 獲取鎖失敗的時候,程式會執行 finally 塊的程式碼。


原文出處: Java 程式碼界 3% 的王者?看我是如何解錯這 5 道題的 - IT閱讀
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Java 基礎之數據存儲

我的全棧之路-Java基礎之數據存儲

我的全棧之路-Java基礎之數據存儲

我的全棧之路

2.1 標識符

標識符是在程序開發時給包,類、接口、註解,變量,方法命名的字符序列。
標識符在命名時需要遵守相關的規則和規範:

標識符的命名規則:

  1. 標識符不能是Java的關鍵字和保留字,關鍵字是Java中被賦予賦予特殊含義的單詞,例如public static void等等。保留字是現有Java版本尚未使用,但是未來的Java版本可能會做關鍵字使用的單詞,例如goto,const。但是可以包含關鍵字,保留字。
  2. 標識符可以由字母、數字、下劃線組成,但是不能以數字開頭

日常開發中應該嚴格遵守標識符的命名規則,否則程序會出現編譯錯誤。

標識符的命名規範:

  1. 包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使 用單數形式,但是類名如果有複數含義,類名可以使用複數形式。,例如com.taobao
  2. 類名單詞首字母大寫,多個單詞首字母也要大寫,例如HelloWorld
  3. 變量名、方法名首字母小寫,後面單詞首字母大寫,例如myName,testVarriable()
  4. 常量名單詞字母全部大寫,多個單詞使用下劃線隔開,例如ORDER_TYPE
  5. 雖然Java支持Unicode字符集,但是標識符不能是中文,同時禁止使用中英文混合命名,更不允許直接使用中文的命名方式,建議命名時見名知意。

由於軟件開發是團隊協作,標識符的命名規範不遵守不會出現錯誤,但是遵守標識符的命名規範可以提高程序的可讀性,便於後期維護,因此日常開發中也應該嚴格遵守。


//包的命名
/**
* Java標識符
*
* [email protected]
* @create 2019-07-22 16:21
* @website www.ittimeline.net
* @since JDK11
*/

IdentifierTest
{
(String[] args) {
CustomerInfo customerInfo = new CustomerInfo();
customerInfo.setCustomerName();
System.out.println( + customerInfo.getCustomerName());
System.out.println( + CustomerInfo.CUSTOMER_ID_CARD);
}
}
/**
* 類的命名
*/

CustomerInfo
{
/**
* 成員變量的命名
*/

private String customerName;
/**
* 常量的命名
*/

;
/**
* 方法的命名
*
* @return
*/

()
{
this.customerName;
}
(String customerName)
{
this.customerName = customerName;
}
}

2.2 變量

2.2.1 變量的概述

變量代表內存的某個存儲區域,該區域的數據可以在同一類型之內隨着業務邏輯不斷髮生變化,例如銀行的利率,遊戲中人物的位置等等。
變量作爲最基本的存儲數據的單元,由變量名、變量類型和變量值組成。通過操作變量,就可以方便的操作內存中的數據。

2.2.2 變量的定義和使用

Java是強類型的語言,變量在定義時必須要明確變量的數據類型。變量定義的語法格式爲 數據類型 變量名=變量值;
,數據類型可以是Java的基本數據類型和引用數據類型兩種。 變量名必須遵守標識符的命名規則和規範,=表示賦值,即將"="右邊的值賦值給左邊的變量,Java中的相等性使用"=="表示,變量的值可以是對應變量數據類型取值範圍內的值。例如 int age=28;

變量的定義也可以是先聲明,再進行賦值


//先聲明一個整數變量
int number;
//將整數變量number賦值爲10
number=10;

變量定義後,Java會根據變量的數據類型在內存中開闢對應的空間存儲數據。

變量在使用前必須聲明並賦初始值,如果使用了未聲明的變量,程序會發生編譯錯誤,如果使用了未賦值的變量,程序會發生運行時錯誤。


package net.ittimeline.java.core.foundational.syntax;
/**
* Java變量的使用
* <p>
* 變量的聲明格式
* <p>
* 變量類型 變量名=變量值;
* 變量必須初始化賦值後才能使用
*
* [email protected]
* @create 2019-07-22 16:55
* @website www.ittimeline.net
* @since JDK11
*/

VarriableTest
{
(String[] args) {
//變量的聲明賦值
28;
System.out.println( + age);
//修改變量的值
//同一時刻,變量只有一個值,新值會覆蓋舊值
age = 29;
System.out.println( + age);
//如果使用了未聲明的變量,會發生編譯錯誤
//編譯時找不到變量name的聲明

String name;
//局部變量聲明之後還要初始化賦值才能使用

}
}

在使用變量時,只能在定義時的作用域內使用。在同一個作用域下不能定義重複的變量名。


package net.ittimeline.java.core.foundational.syntax;
/**
* 變量作用域測試
*
* [email protected]
* @version 2019/9/2 16:38
* @website ittimeline.net
* @since JDK11
*/

VarriableScopeTest
{
(String[] args) {
//這裏會發生編譯錯誤,因爲整數變量age在test方法中定義,只能在test方法中使用

}
()
{
28;
//這裏會發生編譯錯誤,在同一個作用域下,不能定義重複的變量
// int age = 29;
}
}

2.2.4 變量的分類

變量按照其作用域可以分爲局部變量、成員變量以及類變量。
在方法中聲明的變量叫做局部變量,作用域和生命週期從屬於方法


package net.ittimeline.java.core.foundational.syntax.variable;
/**
* 局部變量的使用
*
* [email protected]
* @create 2019-07-26 10:39
* @website www.ittimeline.net
* @since JDK11
*/

LocalVariableTest
{
(String[] args) {
{
29;
}
//這裏無法使用代碼塊中的aqe變量,因爲age只能在{}作用域內訪問

100;
System.out.println( + score);
}
}

成員變量是在方法外部、類中定義,並未使用static修飾的變量,成員變量的作用域和生命週期從屬於對象,
訪問成員變量時需要實例化對象,然後通過對象.成員變量名訪問。


package net.ittimeline.java.core.foundational.syntax.variable;
/**
* 成員變量的使用
*
* [email protected]
* @create 2019-07-26 10:40
* @website www.ittimeline.net
* @since JDK11
*/

MemberVariableTest
{
/**
* 方法外部,類中聲明兩個成員變量
*/

String name;
int age;
(String[] args) {
//實例化MemberVariableTest對象
MemberVariableTest memberVariableTest = new MemberVariableTest();
//通過對象的引用memberVariableTest.name訪問成員變量
memberVariableTest.name = ;
memberVariableTest.age = 29;
System.out.println( + memberVariableTest.age);
}
}

類變量是在方法外部、類中定義,使用static修飾的變量,作用域和生命週期從屬於類。訪問類變量通過類名.成員變量名。


package net.ittimeline.java.core.foundational.syntax.variable;
/**
* 靜態變量的使用
*
* [email protected]
* @create 2019-07-26 10:40
* @website www.ittimeline.net
* @since JDK11
*/

StaticVarriableTest
{
(String[] args) {
System.out.println(User.ID_CARD);
//靜態變量可以直接由類名.變量名訪問
System.out.println(User.remark);
}
}
User {
;
/**
* 靜態變量由Java負責初始化
*/

static String remark;
}

成員變量和類變量由Java根據變量的負責初始化,局部變量由程序員手動初始化。


package net.ittimeline.java.core.foundational.object;
/**
* Java成員變量的初始化機制
*
* [email protected]
* @create 2019-07-22 10:15
* @website www.ittimeline.net
* @since JDK11
*/

DefaultInitialization
{
char c;
int i;
DefaultInitialization() {
System.out.println( + i);
System.out.println(']');
}
(String[] args)
{
DefaultInitialization defaultInitialization = new DefaultInitialization();
}
}

2.2.5 變量的交換

在Java中,變量的交換有三種方式實現,分別是使用中間變量、算術運算以及異或運算三種方式實現。


package net.ittimeline.java.core.foundational.syntax.variable;
/**
* 變量交換
*
* [email protected]
* @create 2019-07-26 10:17
* @website www.ittimeline.net
* @since JDK11
*/

VariableSwapTest
{
(String[] args) {
5;
10;
//通過引入中間變量tmp實現變量的交換
System.out.println();
System.out.println();
println(left, right);
// tmp=5 left=5
int tmp = left;
//left=10,right=10
left = right;
right = tmp;
System.out.println();
println(left, right);
//通過算術運算+ -實現交換
System.out.println();
left = 5;
right = 10;
System.out.println();
println(left, right);
//left=15 right=10
left = left + right;
//right=5 left=15
right = left - right;
//left=10 right=5
left = left - right;
System.out.println();
println(left, right);
//通過異或運算實現變量交換
System.out.println();
left = 5;
right = 10;
System.out.println();
println(left, right);
left = left ^ right;
right = left ^ right;
left = left ^ right;
System.out.println();
println(left, right);
}
/**
* 輸出left和變量的值
*
* @param left
* @param right
*/

int right)
{
System.out.println( + right);
}
}

2.3 數據類型

在計算機底層數據都是使用二進制表示的,使用起來不太方便,因此引入數據類型的概念,數據類型用於對不同的數據進行分類,其原因在於

  • 不同的類型所能存儲的數據類型不同,
  • 不同表示的數據範圍(極限)也不同
  • 不同的數據類型 在內存中佔據的空間(字節數量)也不同
  • 不同的數據類型參與不同的運算。

Java的數據類型分爲基本數據類型(Primitive Type)和引用數據類型(Reference Type)兩大類,如下列表所示,目前只討論基本數據類型
Java數據類型

其中基本數據類型都有數組類型,數組就是一片連續的存儲空間。
而基本數據類型不具有面向對象的編程機制,也沒有對象的特性:沒有成員變量、方法可以被調用,因此Java八種基本數據類型對應的引用類型,也被稱爲包裝類,如下表格所示。


基本數據類型byteshortintfloatdoublebooleanchar
包裝類ByteShortIntegerFloatDoubleBooleanCharacter

在日常開發中常用的基本類型有byte,int,long和boolean。

2.4 JDK1.5新特性 自動拆箱和裝箱

JDK1.5以後提供了自動拆箱和自動裝箱的特性實現了基本數據類型和包裝類的轉換。

自動拆箱:將引用數據類型(例如Long)轉換爲基本數據類型(例如long)
自動裝箱:將基本數據類型(例如byte)轉換爲引用數據類型(例如Byte)


package net.ittimeline.java.core.foundational.object;
/**
* JDK5新特性:自動拆箱和自動裝箱
* 自動拆箱:引用數據類型轉換爲基本數據類型
* 自動裝箱:基本數據類型轉換爲引用數據類型
* Java八種基本數據類型以及包裝類想換轉換
* [email protected]
* @create 2019-07-22 10:40
* @website www.ittimeline.net
* @since JDK11
*/

AutoboxingTest
{
(String[] args) {
//自動裝箱
Byte by = 1;
//自動拆箱
byte bt = by;
System.out.println( + bt);
Short sh = 1;
short s = sh;
System.out.println( + s);
Integer in = 1;
int i = in;
System.out.println( + i);
Long lo = 1L;
long l = lo;
System.out.println( + l);
Boolean bo = true;
boolean b = bo;
System.out.println( + b);
Character ch = 'x';
char c = ch;
System.out.println( + c);
Float fl = 1.0f;
float f = fl;
System.out.println( + f);
Double db = 1.0d;
double d = db;
System.out.println( + d);
}
}

2.5 整型

2.5.1 Java整型的極限

整型是日常生活中常用的類型,例如人的年齡,大樓的層數等等。
Java中的整型按照佔據不同內存大小,可以分爲byte,short,int,long四種類型。
其中整數默認是int類型,long類型的變量值需要加L後綴。
可以通過查看其包裝類Byte的源碼獲取其表示數據的範圍
Byte的表示範圍
以及Byte佔據的位數和字節數

Byte佔據的位數和字節數

其他類型可以使用同樣的方式通過查看包裝類的源碼獲取存儲數據的表示範圍以及佔據的字節帶小。

使用四種整數類型的包裝類獲取其對應的數據存儲範圍以及佔據的字節數量。


package net.ittimeline.java.core.foundational.syntax.type.integer;
/**
* 整數的極限以及內存大小
*
* [email protected]
* @create 2019-07-26 12:59
* @website www.ittimeline.net
* @since JDK11
*/

IntegerLimitsTest
{
(String[] args) {
//這裏的%d表示整數佔位符,會被對應的數值替換
System.out.printf(,Byte.MAX_VALUE,Byte.MIN_VALUE,Byte.BYTES);
System.out.printf(,Short.MAX_VALUE,Short.MIN_VALUE,Short.BYTES);
System.out.printf(,Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.BYTES);
System.out.printf(,Long.MAX_VALUE,Long.MIN_VALUE,Long.BYTES);
}
}

在使用對應的數據類型時,需要注意不要超過其存儲的極限(即最大值和最小值),否則程序會發生錯誤。

2.5.2 大整數BigInteger

日常開發中使用的整數是byte,int,long以及對應的包裝類,整數默認是int,而long類型的常量值後面需要添加L後綴,如果想要使用比Long更大範圍的數據,可以使用java.math包中的BigInteger類。


package net.ittimeline.java.core.foundational.syntax.type.integer;
import java.math.BigInteger;
/**
* Java
* Java整型有byte short int long 四種類型,常用的有byte,int和long
* 整數默認是int
*
* [email protected]
* @create 2019-07-22 18:54
* @website www.ittimeline.net
* @since JDK11
*/

IntegerTest
{
(String[] args) {
128;
System.out.println(+bt);
//
int INT_MAX_VALUE=Integer.MAX_VALUE;
System.out.println(+INT_MAX_VALUE);
//long類型的變量值如果超過了int的表示範圍,需要使用L後綴
//全球人口數量
76_0000_0000L;
System.out.println(+totalPopulation);
//如果想要表示宇宙的星球數量,假如是1萬億億 long類型也不能夠存儲
//long starQuantity=1_0000_0000_0000_0000_0000L;
//此時可以使用JDK提供的BigInteger來存儲大整數
String starQuantityStr=;
BigInteger starQuantity=new BigInteger(starQuantityStr);
System.out.println(+starQuantity);
}
}

2.5.3 整數的四種進制類型

在Java中,整數支持四種進制類型,分別是二進制、八進制、十進制和十六進制。其中JDK1.7以後支持二進制,以0b開頭,而八進制是以0開頭,十六進制是以0x開頭


package net.ittimeline.java.core.foundational.syntax.type.integer;
/**
* 整數的四種進制類型
*
* [email protected]
* @create 2019-07-26 13:10
* @website www.ittimeline.net
* @since JDK11
*/

IntegerRadix
{
(String[] args) {
0b1100;
System.out.println(+binary);
012;
System.out.println(+oct);
12;
System.out.println(+value);
0x12;
System.out.println(+hex);
}
}

在日常企業開發中基本使用的都是十進制的整數,但是在JDK源碼以及其他場景中中會使用到其他進制,
Integer類就使用了十六進制的常量來表示int的取值範圍。
int取值範圍表示

2.5.4 數值字面量使用下劃線

如果Java源文件中存在着很長的數字字面量(即變量值),不易閱讀,此時可以使用JDK7以後新增的特性:數值字面量使用下劃線來分割數值,使其更加容易閱讀。
我們可以在整數和浮點數的數值之間使用下劃線,編譯器在編譯時會刪除這些下劃線。


package net.ittimeline.java.core.foundational.syntax.type.integer;
/**
* 數值字面量使用下劃線
*
* [email protected]
* @create 2019-07-26 14:51
* @website www.ittimeline.net
* @since JDK11
*/

IntegerFloatUnderScoreTest
{
(String[] args) {
//很長的變量值,不容易閱讀
12000000000000L;
//數值使用下劃線分割,更加容易閱讀,編譯器在編譯時會刪除下劃線
System.out.println(+ number);
number=12_0000_0000_0000L;
//因此在輸出number變量的值時不會看到下劃線
System.out.println(+ number);
3.1415_926;
System.out.println(+value);
}
}

2.6 浮點型

2.6.1 浮點型的兩種表示方式

浮點數就是生活中常用的小數,例如銀行的利率,股市的漲跌等等都是浮點數。
Java中的浮點數類型分爲單精度浮點型float和雙精度浮點型double兩種,默認是double類型,如果想要使用float類型,變量值後面加上f後綴,浮點數可以使用十進制和科學計數法兩種表示方式。


package net.ittimeline.java.core.foundational.syntax.type.floats;
/**
* Java浮點型
* 數學意義上的小數就是浮點型
* float和double是Java的兩種浮點類型
* 浮點數默認是double,如果想使用float,變量值後面跟f後綴
* <p>
* 浮點數在進行運算時存在着誤差
*
* [email protected]
* @create 2019-07-22 19:21
* @website www.ittimeline.net
* @since JDK11
*/

FloatTest
{
(String[] args) {
//float類型的變量值後面要加上f後綴,否則會出現編譯錯誤
12.0f;
//浮點類型默認是double
12.0;
//使用科學計數法表示浮點數
1.2e1;
System.out.println( + flt);
System.out.println( + dbl);
System.out.println( + value);
3.1415926;
//3.1415926 等價於 31415926e-7
31415926e-7;
System.out.println( + source);
System.out.println( + target);
}
}

2.6.2 浮點型的極限以及存儲方式

float佔據四個字節(Byte),也就是32位(bit),double佔據八個字節(Byte),也就是64位,而由於浮點數的存儲機制和整數的不一樣,Java的浮點數準守IEEE754標準,採用二進制的科學計數法表示浮點數。
對於float,第一位是符號位,接下來八位表示指數,接下來23位表示尾數。
對於double,第一位是符號位,接下來11尾數表示指數,接下來52表示尾數。

float所能表示的最大值會比佔據8個字節的long都要大。


package net.ittimeline.java.core.foundational.syntax.type.floats;;
/**
* 浮點類型的極限
*
* [email protected]
* @create 2019-07-26 15:09
* @website www.ittimeline.net
* @since JDK11
*/

FloatLimitsTest
{
(String[] args) {
//這裏的%e表示整數佔位符,會被對應的科學計數法替換
System.out.printf(,Float.MAX_VALUE,Float.MIN_VALUE,Float.BYTES);
//使用科學計數法表示float最大值
3.402823e+38f;
//使用十進制小數表示float最大值
Float floatMax=340282300000000000000000000000000000000f;
System.out.println(+floatMaxExponent);
System.out.println(+floatMax);
//由於浮點數的存儲機制不同佔據四個字節的float表示的數據極限比佔據八個字節的Long還要大
System.out.printf(,Long.MAX_VALUE,Long.MAX_VALUE,Long.BYTES);
System.out.printf(,Double.MAX_VALUE,Double.MIN_VALUE,Double.BYTES);
}
}

2.6.3 浮點數精度問題

單精度的float有效位數是7位,即小數點後能保留7位,第八位會進行四捨五入處理。
雙精度的double有效位數是16位,即小數點後保留16位,第十七位會進行四捨五入處理。

如果在使用float或者double的小數時,超過了有效位數進行相關的運算時會得到錯誤的結果。


package net.ittimeline.java.core.foundational.syntax.type.floats;;
/**
* 浮點數運算的精度問題
*
* [email protected]
* @create 2019-07-26 15:13
* @website www.ittimeline.net
* @since JDK11
*/

FloatPrecisionTest
{
(String[] args) {
1.123_456_789f;
//輸出結果是1.1234568 1.123456789,小數點後截取7位,第8位會進行四捨五入
System.out.println(+floatValue);
1.111_222_333_444_555_666_777;
//輸出結果是1.1112223334445557 1.111222333444555666777,小數點後截取16位,第17位會進行四捨五入
System.out.println(+doubleValue);
//浮點類型的數據在運算時運算結果會有誤差
2.0f;
//float類型的浮點數小數有效位數是7位
1.2_222_222f;
//0.7777778
float floatResult=floatSource-floatTarget;
System.out.println(+floatSource);
System.out.println(+floatTarget);
//運算結果爲 0.7777778 因爲float的有效位數是7位
System.out.println(+floatResult);
//double類型的 2.0-1.1獲取的計算結果是錯誤的
2.0;
1.1;
double doubleResult=doubleSource-doubleTarget;
System.out.println(+doubleSource);
System.out.println(+doubleTarget);
//運算結果爲 0.8_999_999_999_999_999 因爲double的有效位數是16位
System.out.println(+doubleResult);
//如果數據超過了有效位數,在進行相關運算時(例如這裏的關係運算:相等)也會得到錯誤的結果
1.000_000_000_000_000_001;
1.000_000_000_000_000_002;
System.out.println(+(source==target));
}
}

如果想要獲取更高的精度,例如金融計算的業務場景中,會使用到更高精度,此時可以使用java.math包中的BigDecimal來完成。


package net.ittimeline.java.core.foundational.syntax.type;
import java.math.BigDecimal;
/**
* 無損精度計算
*
* [email protected]
* @create 2019-07-26 16:37
* @website www.ittimeline.net
* @since JDK11
*/

BigDecimalTest
{
(String[] args) {
//使用字符串構建BigDecimal對象
BigDecimal sourceBigDecimal = );
BigDecimal targetTargetDecimal = );
//調用BigDecimal提供的subtract方法實現減法運算 解決了浮點數double計算的精度問題
BigDecimal resultBigDecimal = sourceBigDecimal.subtract(targetTargetDecimal);
System.out.println( + resultBigDecimal);
1.000_000_000_000_000_001;
1.000_000_000_000_000_002;
BigDecimal doubleSourceBigDecimal=new BigDecimal(source);
BigDecimal doubleTargetBigDecimal=new BigDecimal(target);
/**
* 使用BigDecimal對象提供的equals方法判斷是否相等 這裏的結果是true
* 在使用BigDecimal時不能使用double構造BigDecimal對象,否則會得到錯誤的結果
*/

System.out.println(+doubleSourceBigDecimal.equals(doubleTargetBigDecimal));
//將上面的數字轉換爲字符串之後就能獲取精確的運算結果
String sourceStr=;
String targetStr=;
//只能使用String構造BigDecimal才能獲取精確的運算結果
BigDecimal strSourceBigDecimal=new BigDecimal(sourceStr);
BigDecimal strTargetBigDecimal=new BigDecimal(targetStr);
System.out.println(+strSourceBigDecimal.equals(strTargetBigDecimal));
}
}

2.7 布爾型

布爾類型只有boolean一個類型,而且其取值只能是true或則false。在日常開中boolean常常和關係、邏輯運算以及條件判斷語句一起使用。


package net.ittimeline.java.core.foundational.syntax.type;
/**
* 布爾型
*
* [email protected]
* @create 2019-07-26 10:15
* @website www.ittimeline.net
* @since JDK11
*/

BooleanTest
{
(String[] args) {
//產生2個0-1之間的隨機小數
double source=Math.random();
double target=Math.random();
System.out.println(+source);
System.out.println(+target);
// >就是關係運算
boolean result=source>target;
System.out.println(+result);
//這裏等價於result==true
if(result){
System.out.println();
}else{
System.out.println();
}
}
}

2.8 字符型

Java中的字符使用char表示,用於存儲單個字符,字符型的變量值使用 ''包含。
由於Java使用16位的Unicode作爲編碼方式,而Unicode是國際組織制定的可以容納世界上所有文字和符號的字符編碼方案,因此Java程序支持各種語言的字符。

除此以外char還能使用Unicode表示字符,由於其佔據2個字節空間大小,其表示範圍是\u0000 - \uFFFF。在日常開發中還會使用到一些常用的轉義字符,例如實現換行的\n以及實現路徑轉義的\等等。

字符在計算機底層都是以數字的方式存儲的,例如字符'a'底層的數字是97,'A'底層的數字是65,字符'0'的底層數字是48,然後依次遞增。


package net.ittimeline.java.core.foundational.syntax.type;
/**
* 字符型
*
* [email protected]
* @create 2019-07-26 10:15
* @website www.ittimeline.net
* @since JDK11
*/

CharacterTest
{
(String[] args) {
//Java支持Unicode標準,可以存儲世界上所有國家的語言文字
//英文
'L';
//中文
'劉';
//日文
'お';
//韓文
'안';
System.out.println(+english);
System.out.println(+chinese);
System.out.println(+japan);
System.out.println(+korean);
'\u0041';
//大寫字母A對應的數字是65
System.out.println(+A);
'\u0061';
//小寫的字母a對應的數字是97
System.out.println(+a);
//字符0對應的數字是48
'\u0030';
System.out.println(+zero);
'\n';
System.out.print();
'\\';
System.out.println(+path);
}
}

2.9 數據類型轉換

2.9.1 數據類型自動轉換

Java中的八種基本數據類型除了boolean之外,在進行算術運算時,會按照如下的規則進行自動類型轉換。

double

當byte,short,char三種類型的變量運算時結果類型爲int

  • byte和byte運算的結果類型是int
  • byte和short運算的結果類型是int
  • byte和char運算的結果類型是int
  • char和char運算的結果類型是int
  • char和short運算的結果類型是int
  • int和int運算的結果類型是int
  • byte,short,int,long和float運算結果類型是float
  • byte,short,int,long,float和double運算結果類型是double

即表示範圍小的變量會自動提升爲表示範圍大的變量,這裏的表示範圍指的是數據類型對應的取值範圍,而不是數據類型佔用的內存空間,例如float取值範圍要大於long的取值範圍。

自動類型轉換可以在一定程度上避免進行運算的時候結果超過數據類型的極限而造成程序錯誤。


package net.ittimeline.java.core.foundational.syntax.convert;
/**
* 自動類型轉換
* 八種基本數據類型除了boolean之外在進行運算時會進行自動類型轉換
*
* 在進行運算時,容量小的數據類型的變量與容量大的大數據類型的變量做運算時,結果自動提升爲容量大的數據類型,防止運算時超過極限值。
* 此時容量大小指的是,數據類型表示數的範圍大小,而不是佔用內存大小,比如float容量大於long的容量
* byte -> short-> int-> long-> float ->double
* byte,short,char->int->long->float->double
*
* byte、short、char運算後的結果類型必須是int
*
* [email protected]
* @create 2019-07-27 08:53
* @website www.ittimeline.net
* @since JDK11
*/

AutoConvert
{
(String[] args) {
//byte和int運算的結果是int
12;
12;
//如果用byte存儲運算結果,會編譯失敗
int intResult=byteVal+intVal;
System.out.println(+intResult);
float floatResult=byteVal+intVal;
System.out.println(+floatResult);
//容量小的變量值自動轉換爲容量大的變量
12;
double dblValue=shortVal;
'a';
123;
int intNewResult=ch+intNewValue;
System.out.println(+intNewResult);
12;
12;
int resultInt=sourceByte+targetChar;
//short和char運算結果類型是int
29;
'b';
int result=shortNewVal+newChar;
// byte和byte運算也是int
12;
12;
int sourceTarget=source+target;
System.out.println(+sourceTarget);
//int
int maxTarget=Integer.MAX_VALUE;
int maxSource=Integer.MAX_VALUE;
//這裏已經超出了int表示的最大值,因此輸出的結果是錯誤的
int maxResult=maxSource+maxTarget;
System.out.println(+maxResult);
}
}

2.9.2 數據類型強制類型轉換

而在某些應用場景下我們還可能會使用到強制類型轉換,例如數據庫查詢返回的結果是long類型,但是需要強制轉換爲int類型。
強制類型轉換是自動類型轉換的逆運算,使用強轉符()實現,如果強轉的變量超過目標類型的最大值會損失精度。


package net.ittimeline.java.core.foundational.syntax.convert;
/**
* 強制類型轉換
* 強制類型轉換是自動類型轉換的逆運算
* 需要使用強轉符:()
* 強制類型轉換可能會丟失精度
* [email protected]
* @create 2019-07-27 09:08
* @website www.ittimeline.net
* @since JDK11
*/

NarrowConvert
{
(String[] args) {
12.8;
System.out.println(+dblVal);
//強制類型轉換 (強轉類型)
int)dblVal;
//強制轉換可能會損失精度
System.out.println(+intVal);
//如果轉換的變量值(例如這裏的12)在強制轉換類型的表示範圍之內(例如這裏的byte)則不會損失精度
12;
byte)longVal;
System.out.println(+byteVal);
128;
byte)sourceVal;
//這裏會損失精度,因爲128已經超過了byte存儲的最大值
System.out.println(+targetVal);
}
}

在給變量賦值時,如果變量的值沒有超過變量類型的極限,賦值時也會發送自動類型轉換,但是如果賦值時變量的值超過了變量類型的極限,那麼此時就需要使用到強制類型轉換實現。


package net.ittimeline.java.core.foundational.syntax.convert;
/**
* 類型轉換的特殊情況
*
* [email protected]
* @create 2019-07-27 09:18
* @website www.ittimeline.net
* @since JDK11
*/

ConvertFeature
{
(String[] args) {
//整型常量默認爲int,浮點常量默認爲double
//雖然long類型的變量值必須加上L後綴,但是這裏的123就是int,這裏實際上就是發生了自動類型轉換
123;
System.out.println(+Integer.MAX_VALUE);
// 因爲2147483648超過了int表示的最大值,所以這裏必須加上L後綴
2147483648L;
System.out.println(+bigSource);
//3.14默認是double類型,在賦值是必須使用強制類型轉換
//因爲將表示範圍大的值賦值給表示範圍小的變量必須強制類型轉換
3.14;
//整型默認的是int類型
12;
1;
//浮點型默認是double
12.3+byteVal;
System.out.println(+dblVal);
}
}

2.10 String

2.10.1 字符串變量

日常開發中最常用的數據類型是String,它用於存儲字符串,使用 " "包含起來,String位於java.lang包下,屬於引用類型的一種,底層是字符數組。

String類型變量的定義以及與char類型的比較


package net.ittimeline.java.core.foundational.syntax.type;
/**
* 字符串變量
* <p>
* String用於存儲字符串(多個字符),屬於引用數據類型
*
* [email protected]
* @create 2019-07-27 09:22
* @website www.ittimeline.net
* @since JDK11
*/

StringTest
{
(String[] args) {

String str = ;
String charConent = ;
System.out.println( + charConent);
//空字符
' ';
System.out.println();
//空字符串
String emptyStr = ;
System.out.println();
}
}

2.10.2 字符串連接

String可以和八種基本數據類型使用加號(+)做連接運算,運算的結果類型是String。
String類型與基本數據類型的連接運算,如果加號前後有String類型的變量,那麼此時的加號就是表示連接,如果+前後沒有String,此時的加號表示算術運算的加法。


package net.ittimeline.java.core.foundational.syntax.type;
/**
* 字符串與八種基本數據類型的運算
* 字符串可以和八種基本數據類型進行連接運算,使用+表示連接,運算的結果類型是String類型
* +符號前後有String表示連接,沒有String表示算術運算的加法
*
* [email protected]
* @create 2019-07-27 09:29
* @website www.ittimeline.net
* @since JDK11
*/

StringOperatorTest
{
(String[] args) {
String info=;
18;
String name=;
176.0;
false;
362352355643454356L;
info=+isMarry;
System.out.println();
System.out.println(info);
//判斷+表示字符串連接還是算術運算的加法
'a';
10;
String str=;
//第一個加法前後都不是String,因此表示加法,而不是字符串連接,而a表示97,第二個加法後面是String,因此表示字符串連接
//最後的運算結果是107hello
System.out.println(charVal+number+str);
//第一個加法後面是String,運算結果是ahello10
System.out.println(charVal+str+number);
//第一個加法後面是String,運算結果是ahello10
System.out.println(charVal+(str+number));
//第一個加法前後都不是String,因此加號表示算術運算符的加法運算,最後的運算結果是107hello
System.out.println((charVal+number)+str);
String star=;
'\t';
String content=star+tab+star;
System.out.println(+content);
}
}

2.11 數據在內存中的存儲方式

數據在內存中有三種表現方式:原碼、反碼和補碼
數據在內存中都是以二進制的補碼存儲,在理解補碼之前首先得理解原碼以及反碼。

原碼就是一個數據本身的二進制表現形式,而數值可以分爲有符號和無符號兩種。
有符號數其二進制表示最高位(最左邊)是符號位,1表示負數,0表示正數。

  • 例如無符號數15的原碼使用一個字節表示爲0000 1111
  • 例如有符號15的原碼使用一個字節表示爲0000 1111
  • 例如有符號-15的原碼使用一個字節表示爲1000 1111

反碼
無符號數的反碼等於原碼
例如無符號數15的反碼等於原碼,即無符號數15的反碼用一個字節表示爲0000 1111

有符號數正數的反碼也等於原碼
例如有符號正數15的反碼等於原碼,即有符號數15的反碼用一個字節表示爲0000 1111

有符號負數的反碼是原碼最高位不變,其他位取反
例如有符號負數-15的反碼是原碼最高位不變,其他位取反用一個字節表示爲1111 0000

補碼
無符號數的補碼等於反碼
例如無符號數15的補碼等於反碼,即無符號數15的補碼使用一個字節表示爲0000 1111

有符號正數的補碼等於反碼

例如有符號正數15的補碼等於反碼,即有符號正數15的補碼使用一個字節表示爲0000 1111

有符號負數的補碼等於反碼加1
例如有符號負數-15的補碼等於反碼1111 0000加1,即有符號負數15的補碼使用一個字節表示爲11111 0001

數值是否有符號原碼反碼補碼
150000 11110000 11110000 1111
150000 11110000 11110000 1111
-151000 11111111 00001111 0001

無符號數以及有符號正數,計算機存儲的是原碼,因爲原碼等於反碼
有符號負數計算機存儲的是補碼,補碼等於原碼取反加1,原碼等於補碼取反加1。


原文出處:我的全棧之路-Java基礎之數據存儲 - 台部落
前一個主題 | 下一個主題 | 頁首 | | |



Powered by XOOPS 2.0 © 2001-2008 The XOOPS Project|