6 回答

TA貢獻1833條經驗 獲得超4個贊
在 java 中,每當您嘗試執行算術表達式時,如果表達式包含任何類型的變量,Java 都會將該表達式的所有元素轉換為該表達式中可用的最高數據類型。
所以,
當您執行 10*2 時,兩個操作數都是文字,而不是變量,因此操作數和結果的數據類型也不會自動提升,除非結果超出數據類型的范圍,這里是字節,20 來完全低于字節范圍。
但是當你執行 i*2 時,表達式由變量組成,其中 i 是 int,結果是 20 但是,它的類型將為 int。因為操作數被自動提升為int,也就是說這里的20在表達式求值時會被提升為int,結果將是int,因為兩個操作數都是int。而且 int 不能存儲在字節中,即使它在其范圍內。因為編譯器會認為如果將 int 存儲在字節中將會丟失值。
因此,在這種情況下,您必須強制將其類型轉換為字節。
byte b = (byte)(i*2);
嘗試運行這個你會感到驚訝:
byte b = 10; b = b * 2;
對此的解釋還是和上面說的一樣。

TA貢獻1802條經驗 獲得超4個贊
我對任何特定于 Java 的東西都持否定態度,但任何現代編譯器都會執行常量折疊來“折疊”完全是常量的表達式。即,10 * 2 折疊為 20,因此編譯器將其視為您鍵入的內容byte b = 20;
對于編譯器來說,嘗試優化變量并不實際。盡管在您提供的示例中,查看和了解它相對簡單i
,10
但如果編譯器嘗試優化它并知道它是什么i
,它就必須維護自己的符號表,并且本質上是一個解釋器。由于java是一種預編譯語言,這就達不到目的了。
闡述:
編譯器和解釋器之間是有區別的。編譯器將源代碼作為輸入,并在幕后編寫機器代碼。當機器代碼運行時,就會執行操作/執行/計算。Java是一種編譯語言,因此它的編譯器不做太多計算,它只是編寫可以在Java虛擬機上運行的機器代碼。另一方面,Python 是一種解釋性語言。i * 2
當您運行 python 程序時,在實際計算之前它不會嘗試進行任何類型轉換i * 2
。
現在,有時編譯器會嘗試變得聰明,并內置“優化”。這意味著他們不是編寫執行某些操作的機器代碼,而是用更少的指令編寫機器代碼,因為它知道它將是什么(因此編譯器會進行一些計算來實現這一點)。在您的示例中,編譯器可以將 10 和 2 相乘,然后只編寫一條機器指令來存儲該結果,而不是編寫存儲數字 10、存儲數字 2、將它們相乘、然后存儲結果的機器指令。
當我們引入變量時,編譯器就變得更難優化并找出該變量是什么。實際的編譯程序(Java 編譯器)必須記住 i 現在是一個保存數字 10 的變量。如果我們想優化只是為了知道我們可以將 i * 2 分配給byte
,這意味著編譯器必須記住每個整數變量,以防它在后面的表達式中分配給一個字節 - 此時它并不真正值得優化,因為編譯器花費了額外的計算(額外的編譯工作),而這并沒有真正帶來任何好處。符號表(如上所述)本質上是一個記住變量及其值的表。

TA貢獻1807條經驗 獲得超9個贊
有些語言允許編譯器有很大的自由度,使其盡可能聰明。
但Java不是這樣的語言。Java 的目標之一是您可以使用許多不同的編譯器來編譯您的代碼,并且每次都獲得相同的結果,這樣您就不必擔心您的 IDE 和本地命令行編譯器以及您的生產構建系統是否都兼容以同樣的方式處理你的代碼。
所以你的編譯器拒絕的原因
int i = 10;
byte b = i * 2;
是只有當所有編譯器都被要求接受它時它才能接受它;i這意味著規范必須指定編譯器在編譯時計算出或不計算出 的值的確切條件范圍。這將是一個復雜的混亂,每個編譯器都必須使其完全正確。
因此,規范以相當簡單的方式定義常量表達式(請參閱https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.28),并且只允許當右側是相關類型范圍內的常量表達式時,隱式縮小轉換(請參閱https://docs.oracle.com/javase/specs/jls/se12/html/jls-5.html#jls- 5.2)。
當然,您可以通過編寫強制轉換來解決此問題,以便執行顯式縮小轉換:
int i = 10;
byte b = (byte)(i * 2);
但編譯器不會檢查你是否在;20范圍內 byte你需要自己做。
或者,您可以使i常數:
final int i = 10;
byte b = i * 2;

TA貢獻1805條經驗 獲得超10個贊
這里i
是一個整數, 也是i*2
。您將整數變量分配給字節,這是非法的。您可以通過byte
顯式地將其強制轉換來完成此操作。
byte b = (byte) (i * 2);
Java 不會執行任何值評估來查看它是否適合/不適合目標類型。

TA貢獻1863條經驗 獲得超2個贊
重點是:
int i = 10;
byte b = i * 2
真的很“清楚”,不是嗎?人們知道它的值b 必須是 20,并且 20 非常適合字節范圍。
但假設:
int i = someMethodCall();
byte b = i * 2
現在,當你看到那someMethodCall()具尸體時,也許你也能得出同樣的結論。但您會同意:僅此一點就使決定是否i*2仍在字節范圍內變得更加復雜。
長話短說:編譯器可以(并且確實)對源代碼應用各種分析。例如,盡可能在編譯時計算事物。但具體發生的情況取決于 A) 語言規范和 B) 編譯器實現。
事實是:編寫一個編譯器非常簡單,它只是說:“使用 int 值來初始化字節值是無效的”。編寫一個能夠自行決定“此處有效”的編譯器需要付出相當大的努力。然后,在很多情況下,編譯器不知道,但無論如何都會回來抱怨。
換句話說:創建 java 的人們選擇了一個簡單的編譯器。這使得編譯器更容易實現,但它會導致用戶收到這樣的錯誤消息,(理論上)這是可以避免的(對于很多情況,不是全部)。

TA貢獻1777條經驗 獲得超10個贊
雖然編譯器對這段代碼的分析肯定不會超出可以想象的范圍,以找出在這種特定情況下計算結果確實適合一個字節,但i
仍然被聲明為 an int
,所以我懷疑編譯器將因此應用這些規則對于任何可能的int
和 任何可能的int
,該作業都不起作用。
添加回答
舉報