6 回答

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

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

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

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

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

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