簡體   English   中英

為什么以下Java程序會產生奇怪的輸出?

[英]Why is the following Java program giving strange output?

我在Java Puzzlers中找到了下面的謎題,

public class DosEquis {
    public static void main(String[] args) {
        char x = 'X';
        int i = 0;
        System.out.print(true  ? x : 0);
        System.out.print(false ? i : x); 
    }
}

我嘗試了這個代碼並運行它,但輸出不是按照我的猜測來的,

我的猜測輸出應該是: XX但是在
現實輸出是: X88

我努力了解但我不能,任何人都能給我們解釋一下嗎? 為什么不同的產出即將到來? 因為我能夠理解第一個print()將打印變量char x字符值,但是第二個print()打印88char x的ASCII表示值。如果我簡化第二個print()的三元運算符表達式這個

if(false){
    System.out.print(i);
}else{
    System.out.print(x);
}

然后輸出即將來臨XX ,很奇怪,任何人都可以解決這個問題嗎?

對我來說理解三元運算符會很有幫助。

提前致謝!

這種行為的原因是三元組有一種結果類型,編譯器必須提前選擇,並且響應中調用print的風格。

true ? x : 0 true ? x : 0 case, 0被視為char值,並調用print(char) 在第二種情況下,由於i是一個intx也被隱式轉換為int (擴展轉換)並調用print(int) ,從而產生數字輸出。 相反,隱含地將ichar是非法的,因為它可能會失去精確度。

靜態解析類型的含義可以用這個例子顯示 - 不是用print ,因為有print(Object) ,但考慮一下:

void method(boolean b);
void method(Integer i);
...
method(cond? false:0);

無論cond是什么,都存在與參數兼容的過載。 但是,編譯器需要選擇一個重載,這在編譯時是不可能的。 編譯器將自動進行同步並將表達式指定為Object *,但沒有method(Object)


*實際上,我的編譯器說“類型Test中的方法方法(布爾值)不適用於參數( Object&Serializable&Comparable <?> )”,但重點是。

無論您看到什么結果,都符合JLS 15.25中規定的用於確定條件操作類型的規則。 以下是它提到的觀點:

如果第二個和第三個操作數具有可轉換的類型(第5.1.8節)到數字類型,那么有幾種情況:

  • 如果其中一個操作數的類型為字節或字節,另一個操作數的類型為short或Short,則條件表達式的類型為short。

  • 如果其中一個操作數是T類型,其中T是byte,short或char,另一個操作數是int類型的常量表達式(第15.28節),其值可以在類型T中表示,那么條件表達式的類型是T.

  • 如果其中一個操作數是T類型,其中T是Byte,Short或Character,另一個操作數是int類型的常量表達式(第15.28節),其值可以在類型U中表示,這是應用拆箱的結果轉換為T,則條件表達式的類型為U.

  • 否則,二進制數字提升(第5.6.2節)將應用於操作數類型,條件表達式的類型是第二個和第三個操作數的提升類型。

在你的問題中,遵循最后一點。 xchar類型,而iint類型,因此x被提升為int 因此第二行的輸出是88 ,這是char Xint轉換。

要了解這里發生的事情,我們需要跟蹤類型。 讓我們依次考慮每個print語句,首先:

System.out.print(true ? x : 0);

這里的類型是布爾值? char:const int

編譯器希望ternary語句返回單個類型,它不能返回char或int。 它發現0可以轉換為char常量,因此將其視為'boolean? char:char'。 最后一個char的值為零。 因為布爾值為true,所以第一個char打印為X.

System.out.print(false ? i : x);

這里的類型是布爾值? int:char,int是一個int變量,不能被視為char,因此編譯器的選擇是錯誤,因為類型不同,縮小int或加寬char。 縮小int將會失去精度,因此編譯器不會在我們的后面執行它(它需要顯式轉換)。 加寬不會松散精度,因此char被轉換為int。 'X'的int值是88.因此打印值88。

有關此轉換過程的更多詳細信息,請參閱此處: Java語言規范

在這一行:

System.out.print(false ? i : x);

X被轉換為int,這就是打印char X的十進制值

當您使用三元運算符條件?valTrue:valFalse。 這將在兩種情況下返回相同的數據類型(true或false)。 System.out.print(condition? i : x);中行System.out.print(condition? i : x); 如果為true則為aspecting int,如果為false則為Char,但在這兩種情況下它只返回int。 類似於System.out.print(condition? x : i); 在bothe的情況下,它將返回Char。

在第一個表達式中,一邊是char, 0可以賦給 char ; 因此第一個三元表達式是char。 char zero = 0;

在第二個中有int和char,並且char被加寬(int)'X'是88。

常數c ? "" : null c ? "" : null因此不要創建一個Object而是一個String(這里)。

你的觀察是正確的。 但是三元運算符處理相同的數據類型。 它會考慮真實值的數據類型,並在需要時轉換假值的類型。 在第一個print語句的情況下,三元運算符將數據類型視為Char作為x的數據類型,在第二個print語句中,三元運算符將數據類型視為int作為i的數據類型,這是您獲得ASCII的原因char x中值的表示。

    System.out.print(true  ? x : 0);
 /*ternary operator(compiler) consider the resulting data 
   type as char as the true Value type is char.*/
    System.out.print(false ? i : x); 
/*ternary operator(compiler) consider the resulting data 
   type as int as the true Value type is int.*/

/* but if you replace the print statement with if else condition 
 then there is no point of ternary concept at all*/

在這樣輸入的三元語句中:( (boolean ? if type : else type ) ,只有if類型else類型對於確定最終類型很重要。 編譯器處理如下情況:

  1. 如果if類型else類型都相同,那么最終類型也是相同的。
  2. 如果if類型可以轉換為else類型而不會丟失精度,則最終類型與else類型相同。
  3. 如果else類型可以轉換為if類型而不會丟失精度,則最終類型與if類型相同。
  4. 如果2和3都可以,則最終類型與較低數據范圍的類型相同。
  5. 如果以上都不符合條件,則編譯器會拋出編譯錯誤。*

我們來看看這些不同的版本:

System.out.println(true  ? x : 0); //X

這變為X因為if類型char ,而else類型是可以表示0任何類型。 常量0char數據范圍(0到65,535)內有效。 可以將x0視為int ,但int數據范圍更高(-2 31到2 31 -1)。

根據第4點 ,編譯器選擇較低范圍( char )的類型。

System.out.println(true  ? x : i); //88

這里, if類型是char, else類型int 當您將變量聲明為非final int ,編譯器無法確定該值是否永遠不會從0更改(即使您的代碼在任何地方都沒有更改!)。

因此,這里僅適用第2點 if類型 char可以轉換為else類型 int而不會有任何精度損失(因為int具有更高的范圍),但是如果else類型 int轉換為if類型 char ,則較低范圍的char可能導致精度損失( i值可能超出char的范圍。

因此,編譯器選擇int以避免精度損失,如第2點所示

一些其他測試用例(與上面已經解釋的類似的原因):

System.out.println(false ? i : x); //88 (as per point 3)
System.out.println(false ? 0 : x); //X  (as per point 4)

但是,在此代碼中,結果是不同的:

final int i = 0;
System.out.println(true  ? x : i); //X
System.out.println(false ? i : x); //X

你能說出原因嗎?

* 注意:所有類型最終都可以無損地轉換為Object ,包括可以自動裝箱的原始類型(在最近的Java版本上)。 如果方法被重載以接受Object類型,則可能永遠不會遇到這種情況,因為編譯器將最終類型轉換為Object

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM