为什么以下Java程序会产生奇怪的输出?

时间:2014-07-09 07:38:29

标签: java puzzle

我在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值表示.if我简化第二个print()中的三元运算符表达式

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

然后输出即将来临XX,很奇怪,任何人都可以解决这个问题吗?

理解三元运算符对我有很大的帮助。

提前致谢!

8 个答案:

答案 0 :(得分:6)

这种行为的原因是三元组有一种结果类型,编译器必须提前选择,并在响应中调用print的味道。

true ? x : 0案例中,0被视为char值,并且print(char)被调用。在第二种情况下,由于iintx也会隐式转换为int(扩展转换)并调用print(int),从而产生数字输出。相反,隐含地将i强制转换为char是非法的,因为它可能会失去精确度。

静态解析类型的含义可以通过此示例显示 - 而不是print,因为有print(Object),但请考虑这一点:

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

无论cond是什么,都存在与参数兼容的重载。但是,编译器需要选择一个重载,这在编译时是不可能的。编译器将自动进行同步并将表达式指定为Object *,但没有method(Object)


*实际上,我的编译器说"类型Test中的方法方法(boolean)不适用于参数( Object& Serializable& Comparable<?> )&#34但是,重点是。

答案 1 :(得分:3)

您看到的任何结果都符合JLS 15.25中指定的规则,用于确定条件操作的类型。以下是它提到的观点:

  

如果第二个和第三个操作数具有可转换的类型(第5.1.8节)到数字类型,那么有几种情况:

     
      
  • 如果其中一个操作数的类型为字节或字节,另一个操作数的类型为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节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。 < / p>

  •   

在你的问题中,遵循最后一点。 x类型为char,而i类型为int类型,因此x会提升为int。因此,第二行的输出为88,这是char int的{​​{1}}转换。

答案 2 :(得分:2)

要了解这里发生的事情,我们需要跟踪类型。让我们依次考虑每个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 Language Spec

答案 3 :(得分:1)

在这一行:

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

X被转换为int,这样就打印了char X的十进制值

答案 4 :(得分:1)

使用三元运算符条件时?valTrue:valFalse。这将在两种情况下返回相同的数据类型(true或false)。在行System.out.print(condition? i : x);中,如果为true,则为aspecting int;如果为false,则为Char,但在两种情况下,它将仅返回int。同样在行System.out.print(condition? x : i);中,它将返回Char。

答案 5 :(得分:1)

在第一个表达式中,一侧是char,0 可分配char;因此第一个三元表达式是char。 char zero = 0;

在第二个中有int和char,而char是加宽(int)'X'是88。

常量就像在c ? "" : null中一样,因此不会创建一个Object而是一个String(这里)。

答案 6 :(得分:1)

你的观察是正确的。但是三元运算符处理相同的数据类型。它会考虑真实值的数据类型,并在需要时转换假值的类型。在第一个print语句的情况下,三元运算符将数据类型视为Char作为x的数据类型,在第二个print语句中,三元运算符将数据类型视为int作为i的数据类型,这就是你的原因在char x中得到值的ASCII表示。

    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*/

答案 7 :(得分:1)

在这样输入的三元语句中:(boolean ? 如果输入 : ,则输入 ),只输入如果输入并且 else类型对确定最终类型很重要。编译器处理如下情况:

  1. 如果 if type else type 都相同,那么最终类型也是相同的。
  2. 如果 if type 可以转换为 else type 而不会丢失精度,则最终类型与 else type 相同。
  3. 如果 else type 可以转换为,如果键入而不会丢失精度,则最终类型与 if type 相同。
  4. 如果2和3都可以,则最终类型与 lower 数据范围的类型相同。
  5. 如果以上都不符合条件,则编译器会抛出编译错误。*
  6. 让我们看看这些不同的版本:

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

    这变为X,因为 if type char,而 else type 是可以代表0的任何类型。常量0char数据范围(0到65,535)内有效。可以将x0视为int,但int数据范围更高(-2 31 至2 31 -1)。

    根据第4点,编译器会选择较低范围的类型(char)。

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

    此处, if type 是char,而 else type int。当您将变量声明为非最终 int时,编译器无法确定该值是否永远不会从0更改(即使您的代码未更改它)任何地方!)。

    因此,此处仅适用第2点 if type char可以转换为 else type int而不会有任何精度损失(因为int具有更高的范围),但如果 else类型 int转换为 if type 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