當前位置:首頁 » 編程軟體 » java編譯常量折疊

java編譯常量折疊

發布時間: 2023-05-05 06:14:21

① String a="a"; String b="b"; a=a+b; 這里共創建了幾個對象

這里共創建了3個對象。

"a"+"b"+"c"在編譯期已經常量折疊為"abc",變數a獲得是"abc"。

甲骨文jdk(1.7),javac會進行常量折疊,全字面量字元串相加是可以折疊為一個字面常量,而且是進入常量池的。這個問題涉及到了字元串常量池和字元串拼接。

只創建了一個對象,在字元串池只會有一個對象。因為它是一行定義的對象,編譯時只會初始化一次字元串緩沖池的數據。如果是 String a="a";String b="b";String c="c";String d=a+b+c;這里就創建了4個對象。

(1)java編譯常量折疊擴展閱讀

String 對象的實現:

String 對象的創建方式

1、通過字元串常量的方式

String str= "pingtouge"的形式,使用這種形式創建字元串時, JVM 會在字元串常量池中先檢查是否存在該對象,如果存在,返回該對象的引用地址,如果不存在,則在字元串常量池中創建該字元串對象並且返回引用。

使用這種方式創建的好處是:避免了相同值的字元串重復創建,節約了內存

2、String()構造函數的方式

String str = new String("pingtouge")的形式,使用這種方式創建字元串對象過程就比較復雜,分成兩個階段,首先在編譯時,字元串pingtouge會被加入到常量結構中,類載入時候就會在常量池中創建該字元串。

然後就是在調用new()時,JVM 將會調用String的構造函數,同時引用常量池中的pingtouge字元串,在堆內存中創建一個String對象並且返回堆中的引用地址。

② JAVA代碼編譯


publicclassTest{
publicstaticvoidmain(Stringargs[]){
帶畝Squaresquare=newSquare(4);
doublearea=square.area();
System.out.println("面積為州行蠢:"+area);
}
}

interfaceIShape{
publicdoublearea();
}

classSquareimplementsIShape{
privatedoublea;

publicSquare(doublea){
this.a=a;
}

@Override
publicdoublearea()冊陪{
returna*a;
}
}

③ 為什麼我的這個程序的字元串「+拼接」比StringBuilder的「append拼接」快呢

這種結果是正常的,因為你的循環數量特別大,大部分時間分配給了循環,當使用+的時候sbbb.append("1" + "aaaaaa" + "2");,括弧中的靜態字元串會相加(底層是使沖唯用StringBuilder的append()方法實現的),相加之後,在字元串的靜態緩存區就會存在一個新的靜態字元串「1aaaaaa2」,當下次循環再調用這個語句:sbbb.append("1" + "aaaaaa" + "2")時,String的優陵鋒化機制已經記住了這個表達式("1" + "aaaaaa" + "2"),會把這個表達式直接轉換為靜態緩存區中的"1aaaaaa2",
也就是說從第二次循環開始就做了這樣的操作:sbbb.append("1aaaaaa2");,而不是重新去計算("1" + "aaaaaa" + "2")的結果。下面的語句sbb.append("1"); sbb.append("aaaaaa"); sbb.append("2");從第二次循環開始,每次還是調用三次append()方散汪培法,而上面的剛才說了,從第二次開始只是調用了一次append()方法而已。
另外,在使用StringBuilder的時候最好指定初始容量,一般有個默認初始容量,如果不指定,比如你要操作的長度為256,而初始長度只是16,它就會擴大緩存區,影響效率,所以可以預知字元串大小時,最好指定初始容量,以便提升效率,比如StringBuilder sb = new StringBuilder(256);

④ java如何優化編譯呢

#java編譯器對`String常量表達式`的優化:
- 1.String+String 可以被編譯器識別為常量表達
String a="ab" ;
String b="a"+"b";//編譯後:b="ab"
System.out.println(a==b);//true
分析:
編譯器將"a"+"b"當做常量表達式,在編譯時期進行優化,直接取"ab". 在運行時期
並沒有創建新的對象,而是從jvm字元串常量池中獲取之前已經存在的"ab"對象.

- 2.String+基本類型 可以被編譯器識別為常量表達式

String a="a1";
String b="a"+1; //"a1"
String c="a"+true;//"atrue"
String d="a"+3.14;//"a3.14"

#java編譯器對`常量`優化:
* 它是編譯時的一項優化技術,將代碼的常量計算在編譯期完成,節約了運行時的計算量.

1.常量替換
//編譯前:
final int x=10;
int y=x;

//編譯後
int x=10;
int y=10;//編譯時,常量替換了

2.數學恆等式的模式匹配替換

//編譯前:
int x=10+10;

//編譯後
int x=20;//編譯時,模式匹配替換了

3.常量折疊

//編譯前:
boolean flag=true||(a || b && c);

//編譯後
boolean flag=true;//編譯時,常量折疊了

⑤ javac編譯後文件內容變化

當使用javac編譯器編譯Java源代碼時,會產生一個位元組碼文件,它包含了Java虛擬機(JVM)可以理解的指令。位元組碼文件的內容是由Java源代碼編譯而來的,它們是由一系列指令組成的,這些指令描述了Java虛擬機如何執行Java程序。位元組碼文件的內容可以被用來描述Java程序的行為,這些指令可以用來控制Java程序的執行,以及它們如何處理數據。位元組碼文件還可以包含元數據,這些元數據可以用來描述Java程序的結構,以及它們如何與其他程序交互。位元組碼文件的內容可以被用來描述Java程序的行為,這些指令可以用來控制Java程序的執行,以及它們如何處理數據。位元組碼文件還可以包含元數據,這些元數據可以用來描述Java程序的結陸棚則構,以及它們如和皮何與其他程序交互。此外,位元組碼文件還可以包含一些額外的信息,例如類型信息,變數名稱和方法名稱等。總之,位元組碼文件的內容可以用來描述Java程序的行為,以及它們如何處理數據和與其他早棚程序交互。

⑥ javashort怎麼-1

註:如未特別說明,Java語言規范 jls 均基於JDK8,使用環境是 eclipse4.5 + win10 + JDK 8
本篇的知識點,主要是涉及到 Java 中一些比較答橡雹常見的默認窄化處理(Java編譯器自動添加的),這里將從一個問題開始,據說這也是一道常見的筆試題/面試題:

為什麼 short i = 1; i += 1; 可以正確編譯運行而 short i = 1; i = i + 1; 會出現編譯錯誤?

其他說法:都放在一起編譯會出現有什麼結果,哪一行報錯?為什麼?

筆者註:其實這其中會涉及到一些編譯優化和底層的知識,限於知識面,本篇不涉及,如有需要,可自行搜索。

本文的目錄結構如下:

1、結論

關於開篇提出的問題,這里先直接給出結論:

Java語言規范規定基礎數據類型運算默認使用32位精度的int類型

只要是對基本類型做窄化處理的,例如 long -> int -> short -> char,都需要做強制轉換,有些是Java編譯器默認添加的,有的則是代碼中顯式做強制轉換的。

short i = 1; i += 1;可以正確編譯運行是因為Java編譯器自己添加了強制窄化處理,即對於任何的T a; X b; a += b;等價於T a; X b; a = (T) (a + b);Java編譯器會默認做這個顯式強制轉換(盡管有時候會出現精度問題,例如 b 是 float 、 double 類型,強烈建議不要有這樣的操作)。前面的i += 1其實就等價於i = (int) (i + 1),即便將數字1換成是double類型的1.0D也是如此。

short i = 1; i = i + 1;編譯不通過的原因就很明顯了:無論是代碼中,還是Java編譯器,都沒有做強制轉換,int 類型直接賦給 short ,因此編譯出錯。

對於常量(數字常量、常量表達式、final常量等),Java編譯器同樣也可以做默認的強制類型轉換,只要常量在對應清帆的數據范圍內即可。

2、詳如殲解

接下來講詳細分析為什麼 short i = 1; i += 1; 可以正確編譯而 short i = 1; i = i + 1; 則會編譯失敗。先列一下搜出來的一些令人眼前一亮(or 困惑)的代碼

public static voidmain(String[] args) {//注:short ∈ [-32768, 32767]

{/** 1、對於 +=, -=, *=, /=, Java編譯器默認會添加強制類型轉換,

* 即 T a; X b; a += b; 等價於 T a; X b; a = (T) (a + b);*/

//0是int類型的常量,且在short范圍內,被Java編譯器默認強制轉換的

short i = 0;

i+= 1; //等價於 i = (short) (i + 1);

System.out.println("[xin01] i=" + i); //輸出結果: 1

i = (short) (i + 1);

System.out.println("[xin02] i=" + i); //輸出結果: 2

/** 下面這2行都會有編譯報錯提示:

* Exception in thread "main" java.lang.Error: Unresolved compilation problem:

* Type mismatch: cannot convert from int to short

* [注]錯誤: 不兼容的類型: 從int轉換到short可能會有損失

* Eclipse 也會有提示: Type mismatch: cannot convert from int to short*/

//i = i + 1;//i = 32768;

i= 0;

i+= 32768; //等價於 i = (short) (i + 32768); 下同

System.out.println("[xin03] i=" + i); //輸出結果: -32768

i += -32768;

System.out.println("[xin04] i=" + i); //輸出結果: 0

i= 0;long j = 32768;

i+=j;

System.out.println("[xin05] i=" + i); //輸出結果: -32768

i= 0;float f = 1.23F;

i+=f;

System.out.println("[xin06] i=" + i); //(小數位截斷)輸出結果: 1

i= 0;double d = 4.56D;

i+=d;

System.out.println("[xin07] i=" + i); //(小數位截斷)輸出結果: 4

i= 10;

i*= 3.14D;

System.out.println("[xin08] i=" + i); //輸出結果: 31

i= 100;

i/= 2.5D;

System.out.println("[xin09] i=" + i); //輸出結果: 40

}

{/** 2、常量表達式和編譯器優化: 常量折疊*/

//2 * 16383 = 32766//(-2) * 16384 = -32768//都在 short 范圍內,常量表達式在編譯優化後直接用對應的常量結果,然後編譯器做強制轉換

short i = 2 * 16383; //等價於 short i = (short) (2 * 16383);

short j = (-2) * 16384;//2 * 16384 = 32768,超過 short 范圍,編譯器不會做轉換//Type mismatch: cannot convert from int to short//short k = 2 * 16384;//常量表達式在編譯優化後直接用對應的常量結果,然後編譯器做強制轉換

short cThirty = 3 * 10;short three = 3;short ten = 10;//Type mismatch: cannot convert from int to short//short thirty = three * ten;

final short fTthree = 3;final short fTen = 10;//常量表達式在編譯優化後直接用對應的常量結果,然後編譯器做強制轉換

short fThirty = fTthree *fTen;final short a = 16384;final short b = 16383;//常量表達式在編譯優化後直接用對應的常量結果,然後編譯器做強制轉換

short c = a +b;

}

}

接下來根據代碼羅列的兩部分分別進行說明:

2.1、對於 +=, -=, *=, /=, Java編譯器默認會添加強制類型轉換,即 T a; X b; a += b; 等價於 T a; X b; a = (T) (a + b);

A compound assignment expression of the formE1 op= E2is equivalent toE1 = (T) ((E1) op (E2)), whereTis the type ofE1, except thatE1is evaluated only once.

For example, the following code is correct:

short x = 3;

x+= 4.6;

and results in x having the value 7 because it is equivalent to:

short x = 3;

x= (short)(x + 4.6);

筆者註:

實際上,直接加上強制類型轉換的寫法,也是大家都熟悉且理解起來最清晰的方式,可以避免可能潛在的類型不匹配時出現的精度損失問題,使用的時候需要注意。當然,筆者認為這些方式都沒有好壞之分,正確地使用即可。

Java從語言規范層面對此做了限制。有興趣的還可以通過 class文件和 javap -c 反匯編對所使用的位元組碼作進一步的研究。

知道了Java語言相關的規范約定,我們就可以看出,與之對應的是以下這種出現編譯錯誤的寫法(報錯提示:Type mismatch: cannot convert from int to short):

short i = 1;//i + 1 是 int 類型,需要強制向下類型轉換

i = i + 1;

2.2、常量表達式和編譯器優化: 常量折疊

需要注意的是,前面的示例short x = 3;中的3其實默認是 int 類型,但是卻可以賦值給short類型的x。這里涉及到到的其實是 常量表達式。

In addition, if the expression is a constant expression (

A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.

A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:

Byte and the value of the constant expression is representable in the type byte.

Short and the value of the constant expression is representable in the type short.

Character and the value of the constant expression is representable in the type char.

對於常量表達式,其結果是可以自動做窄化處理的,只要是在對應的數據類型範圍內,Java編譯器就進行做默認強制類型轉換。

Some expressions have a value that can be determined at compile time. These are constant expressions (

true(short)(1*2*3*4*5*6)

Integer.MAX_VALUE/ 2

2.0 *Math.PI"The integer " + Long.MAX_VALUE + " is mighty big."

A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (

通過常量表達式賦值的final變數都是常量,這種也是編譯期可以確認最終值,會通過編譯優化直接賦予最終值,而且可以直接依靠編譯器做窄化處理。

對於這一塊,其實對應的是一個比較基礎的編譯器優化:常量折疊(Constant Folding),有興趣的可以自行搜索。stackoverflow 上由相關的討論,參考[9]、[10]、[11]

筆者註:

盡管常量表達式最終都被編譯器優化為直接值,但是為了清晰,提高可讀性、可維護性,代碼中沒必要對常量直接換算,例如一天 24 * 60 * 60 秒,其實可以分別用可讀性更強的final常量來表示。

Bloch大神的 Java Puzzlers 中也有相關的一些說明,有興趣的可以去看看

⑦ byte a=1;byte b=2;byte c=a+b;byte d=1+2; 請問第三句和第四句的計算機底層計算分別是怎麼樣的

分析:

d = 1 + 2; 1和2是常量,為固定不變的數據,在編譯的時候(編譯器javac),已經確定了1+2的結果並沒有超過byte類型的取值范圍,可以賦值給變數d,因此d=1+2 是正確的。

常量優化機制

有一些計算,非常簡單,例如常量和常量的計算就非常簡單,在編譯階段就可以把這些簡單的運算計算完。

反之, c = a + b ,a和b是變數,變數的值是可能變化的,在編譯的時候,編譯器javac不確定 a + b 的結果是什麼,因此會將結果以int類型進行處理,所以int類型不能賦值給byte類型,因此編譯失敗。

⑧ 如何在編譯java的時候,取消編譯器對編譯常量的優化

遇到的問題是想重新編譯某個java文件(比如A.java),裡面有個常量(比如finalinta)和上次編譯時不一樣,但是另一個使用A.class的a的文件(比如B.java)由於在javac在上次編譯的時候將當時的A.class裡面的常量直接給內聯了,所以就達不到想要的效果。
如果是這樣的話,對於String可以使用.intern()來防止編譯器進行優化,對於其他類型,可以要麼不定義為常量,要麼將常量定義為private,然後使用一個static方法來返回這個常量。

⑨ 簡述JAVA程序的編輯編譯和運行過程

第一步(編譯): 創建完源文件之後,程序會先被編譯為.class文件。Java編譯一個類時,如果這個類所依賴的類還沒有被編譯,編譯器就會先編譯這個被依賴的類,然後引用,否則直接引用,這個有點象make。

如果java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話報「cant find symbol」的錯誤。

第二步(運行):java類運行的過程大概可分為兩個過程:1、類的載入 2、類的執行。需要說明的是:JVM主要在程序第一次主動使用類的時候,才會去載入該類。也就是說,JVM並不是在一開始就把一個程序就所有的類都載入到內存中,而是到不得不用的時候才把它載入進來,而且只載入一次。

特別說明:java類中所有public和protected的實例方法都採用動態綁定機制,所有私有方法、靜態方法、構造器及初始化方法<clinit>都是採用靜態綁定機制。而使用動態綁定機制的時候會用到方法表,靜態綁定時並不會用到。

(9)java編譯常量折疊擴展閱讀:

Java整個編譯以及運行的過程相當繁瑣,本文通過一個簡單的程序來簡單的說明整個流程。

Java代碼編譯:是由Java源碼編譯器來完成;

Java位元組碼的執行:是由JVM執行引擎來完成

Java程序從源文件創建到程序運行要經過兩大步驟:

1、源文件由編譯器編譯成位元組碼(ByteCode)

2、位元組碼由java虛擬機解釋運行。因為java程序既要編譯同時也要經過JVM的解釋運行,所以說Java被稱為半解釋語言( "semi-interpreted" language)。

熱點內容
雲購源碼系統 發布:2024-05-02 06:12:52 瀏覽:105
電腦如何進行安卓升級 發布:2024-05-02 06:10:08 瀏覽:37
元龍第5集免費看完整版緩存 發布:2024-05-02 06:03:47 瀏覽:668
腳本宣傳片 發布:2024-05-02 05:56:26 瀏覽:570
有線投屏安卓手機如何設置 發布:2024-05-02 05:43:26 瀏覽:896
搶誠信紅包用什麼伺服器好 發布:2024-05-02 05:37:44 瀏覽:104
淘寶客源碼程序 發布:2024-05-02 05:34:46 瀏覽:814
大淘客cms源碼 發布:2024-05-02 05:33:12 瀏覽:447
matlab新建文件夾 發布:2024-05-02 05:14:19 瀏覽:719
看加密相冊 發布:2024-05-02 04:45:53 瀏覽:664