国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

表達式中的陷阱

2019-11-14 21:54:21
字體:
來源:轉載
供稿:網友
表達式中的陷阱

作者:禪樓望月(http://m.survivalescaperooms.com/yaoyinglong/)

字符串的陷阱

java程序創建對象常見的方式有:

  • new;
  • 通過Class對象的newInstance()方法調用構造函數創建Java對象;
  • 反序列化;
  • clone()方法。

此外,基本類型以及基本類的包裝類、字符串還可以以直接量的方式來創建Java對象。如:

String str="hello world";Integer in=5;

對于Java程序中的字符直接量,JVM會使用一個字符串池來保護他們:當第一次使用某個字符串直接時,JVM會將它們放入字符串池進行緩存。在一般情況下,字符串緩沖池中字符串對象不會被垃圾回收,當程序再次需要使用該字符串時,無需重新創建一個新的字符串,而是直接讓引用變量指向字符串池中已有的字符串:

String str1="hello";String str2="hello";System.out.PRintln(str1==str2);//-->true

雖然String是引用類型。但是由于str1和str2兩個字符串的值都是直接量,它們都指向JVM的字符串池里的“hello”字符串,因此判斷str1==str2時輸出true。

注意:

也可以通過字符串連接表達式來創建字符串對象。如果這個字符串連接表達式的值可以在編譯時確定下來,那么JVM會在編譯時計算該字符串變量的值,并讓它指向字符串池中對應的字符串。但是,如果字符串連接表達式中使用了變量或者調用了方法,那就只能到運行時才可確定該字符串連接表達式的值,也就無法在編譯時確定該字符串變量的值,因此無法利用JVM的字符串池。

image

輸出的結果為:

image

image

但是也有例外:字符串連接運算中的所有變量都可執行“宏替換”

如果字符串連接運算中的所有變量都可以執行“宏替換”,那么JVM一樣可以在編譯時就確定字符串連接表達式的值,一樣會使字符串變量指向JVM字符串池中的對應字符串。

String str="I love Java";final String str1="I love ";String str2=str1+"Java";System.out.println(str==str2);//-->true

我們用反編譯工具看看:

String str = "I love Java";String str1 = "I love ";String str2 = "I love Java";     //這里執行了宏替換。編譯時將str1用相應的值代替了。這是因為str1是final的而final變量的值在初始化之后不會在變化了。所以可以執行宏替換。System.out.println(str == str2);

不可變的字符串

String在創建之后是不可變的。只能改變字符串變量的引用,不能改變字符串對象。

String str="I love Java";str="I love C#";

第二行代碼并沒有將“I love Java”字符串對象變為“I love C#”字符串對象,只是改變str的指向,使其指向“I love C#”字符串對象,但是“I love Java”字符串對象仍然存在內存中。但是該“I love Java”字符串對象以后也許永遠也不會被使用了,但是它并不會被垃圾回收器所回收,它將一直存在于字符串池中------>這也是Java內存泄漏的一個原因。字符串比較

使用“==”比較的是兩個字符串所引用的是否是同一個字符串對象。但是如果要比較兩個字符串序列是否相同則須用String的 equals 方法,如下是String類的equals方法源碼

public boolean equals(Object anObject) {        if (this == anObject) {            return true;        }        if (anObject instanceof String) {            String anotherString = (String) anObject;            int n = value.length;            if (n == anotherString.value.length) {                char v1[] = value;                char v2[] = anotherString.value;                int i = 0;                while (n-- != 0) {                    if (v1[i] != v2[i])                            return false;                    i++;                }                return true;            }        }        return false;    }

還有String的compareTo系列方法。

表達式類型的陷阱

表達式類型的自動提升。

當表達式中出現不同的數據類型是,表達式的結果以表達中最高等級操作數的類型為準。

復合賦值運算符的陷阱

諸如:+=、-=、*=……等復合賦值運算符都隱式包含了類型轉換。

image

如上圖所示,a=a+5出現了編譯錯誤,是因為,5為int類型,所以會將整個表達式的值提升為int,而a又是short不能進行類型的自動轉換,所以出現了編譯錯誤。而a+=5沒有出現編譯錯誤的原因為,我們看看Java底層怎么處理的:

image

是不是很清楚了,底層默默的進行了隱式的類型轉換。怎么轉的呢?是以等號左邊的類型作為等號后邊表達式計算結果的類型。這下可危險了:萬一等號右邊表達式的計算結果的范圍比等號左邊的大怎么辦,那只能悲催了,如:

image

所以我們在使用復合賦值運算符是要特別小心,特別是:

    1. 將復合賦值運算符運用于byte、short或char等類型的變量;
    2. 將復合賦值運算符運用于int類型的變量,而表達式右邊是long、float、double類型的值;
    3. 將復合賦值運算符運用于float類型的變量,而表達式右邊是double類型的值。
多線程的陷阱

不要使用Thread類來創建線程。

不要調用run方法。

當同步一段代碼塊時,程序必須顯式的為它指定同步監視器;對于非靜態方法同步方法而言,該方法的同步監視器是this—即調用該方法的Java對象;對于靜態同步方法而言,該方法的同步監視器不是this而是當前類本身

public class Test implements Runnable{    static boolean staticFLag=true;    public static synchronized void test0(){        for(int i=0; i<100; i++){            System.out.println("test0:"+Thread.currentThread().getName()+" "+i);        }    }    public void test1(){        synchronized (this) {            for(int i=0; i<100; i++){                System.out.println("test1:"+Thread.currentThread().getName()+" "+i);            }        }    }    @Override    public void run() {        if(staticFLag){            staticFLag=false;            test0();        }else {            staticFLag=true;            test1();        }    }    public static void main(String[] args) throws InterruptedException {        Runnable runnable=new Test();        new Thread(runnable).start();        new Thread(runnable).start();    }}

打印的結果為:

test0:Thread-0 0

test1:Thread-1 0

test0:Thread-0 1

test1:Thread-1 1

test0:Thread-0 2

test1:Thread-1 2

test0:Thread-0 3

test1:Thread-1 3

……

我們可以看到,由于兩個方法 沒有使用相同的同步監視器,所以它們可以同時并發執行,相互之間不會有影響。但是如果我們這樣變一下:

public class Test implements Runnable{    static boolean staticFLag=true;    public static synchronized void test0(){        for(int i=0; i<100; i++){            System.out.println("test0:"+Thread.currentThread().getName()+" "+i);        }    }    public void test1(){        synchronized (Test.class) {            for(int i=0; i<100; i++){                System.out.println("test1:"+Thread.currentThread().getName()+" "+i);            }        }    }    @Override    public void run() {        if(staticFLag){            staticFLag=false;            test0();        }else {            staticFLag=true;            test1();        }    }    public static void main(String[] args) throws InterruptedException {        Runnable runnable=new Test();        new Thread(runnable).start();        new Thread(runnable).start();    }}

打印:

test0:Thread-0 0

test0:Thread-0 1

test0:Thread-0 2

……

test0:Thread-0 98

test0:Thread-0 99

test1:Thread-1 0

test1:Thread-1 1

……

test1:Thread-1 99

我們看到,當我們顯示的指定test1方法內的代碼塊的同步監視器為Test.class時,由于test0方法和test1方法是用了相同的同步監視器,所以必須在test0方法釋放了該同步監視器的鎖定,test1才能使用。

本文鏈接:http://m.survivalescaperooms.com/yaoyinglong/p/java%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%AD%E7%9A%84%E9%99%B7%E9%98%B1.html


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 德清县| 鹿泉市| 泾川县| 镇康县| 南郑县| 贵阳市| 神池县| 兴隆县| 黔南| 天气| 温州市| 堆龙德庆县| 谷城县| 深圳市| 德兴市| 五峰| 东山县| 天津市| 三明市| 瑞丽市| 光泽县| 阜宁县| 陆丰市| 玉门市| 巧家县| 军事| 抚宁县| 延长县| 宕昌县| 泰安市| 谷城县| 通州区| 长丰县| 高碑店市| 综艺| 黄骅市| 太保市| 彭水| 泾源县| 清水县| 呼和浩特市|