在Java中,两个char类型的数值相加的结果是int类型还是char类型?

94

在添加'a' + 'b'时,它会产生195。输出的数据类型是int还是char


3
如果类型为 char,那么 'a' + 'b' 的值将不是 195 而是 'Ã' - Joren
1
这里是 http://ideone.com 上是 195,但在 Eclipse 中不是。 - orange
2
你从算法书上读到了那个,哈哈!我来这里就是因为这个练习的。;) - Jack Twain
8个回答

144

将Java中的char、short或byte相加的结果是一个int

Java语言规范关于二进制数值提升的说明

  • 如果任何一个操作数是引用类型,则执行自动拆箱转换(§5.1.8)。然后:
  • 如果任一操作数是double类型,则将另一个操作数转换为double类型。
  • 否则,如果任一操作数是float类型,则将另一个操作数转换为float类型。
  • 否则,如果任一操作数是long类型,则将另一个操作数转换为long类型。
  • 否则,两个操作数都将转换为int类型。

但请注意它对复合赋值运算符(如+=)的说明

二进制操作的结果将被转换为左侧变量的类型...并将转换的结果存储到该变量中。

例如:

char x = 1, y = 2;
x = x + y; // compile error: "possible loss of precision (found int, required char)"
x = (char)(x + y); // explicit cast back to char; OK
x += y; // compound operation-assignment; also OK

一种通常的方式是将结果转换为Object,然后询问它属于哪个类别,以确定结果的类型:

System.out.println(((Object)('a' + 'b')).getClass());
// outputs: class java.lang.Integer

如果你对性能感兴趣,注意Java bytecode甚至没有专门的指令来处理较小的数据类型。例如,对于加法,有指令iadd(用于int)、ladd(用于long)、fadd(用于float)、dadd(用于double),就是这样。为了模拟使用较小类型的x += y,编译器将使用iadd,然后使用像i2c(“int转char”)这样的指令将int的上半字节清零。如果本地CPU具有用于1字节或2字节数据的专用指令,则由Java虚拟机在运行时进行优化。
如果你想将字符连接为字符串而不是将它们解释为数字类型,有很多方法可以做到这一点。最简单的方法是将空字符串添加到表达式中,因为将char和String相加会产生一个String。所有这些表达式的结果都是字符串"ab"
  • 'a' + "" + 'b'
  • "" + 'a' + 'b' (这个可以工作是因为"" + 'a'先被评估;如果""在最后,您将得到"195"
  • new String(new char[] { 'a', 'b' })
  • new StringBuilder().append('a').append('b').toString()
  • String.format("%c%c", 'a', 'b')

23

charbyte(还有short)的二进制算术运算会自动提升为int类型 -- 参见JLS 5.6.2。


2
一元算术运算。 - Tom Hawtin - tackline

7

您可能希望了解以下关于char的表达式。

char c='A';
int i=c+1;

System.out.println("i = "+i);

这在Java中是完全有效的,会返回66,即字符(Unicode)c+1的相应值。
String temp="";
temp+=c;
System.out.println("temp = "+temp);

这个在Java中是合法的,字符串类型变量temp自动接受char类型的c并在控制台上产生temp=A


以下所有语句在Java中也是有效的!

Integer intType=new Integer(c);
System.out.println("intType = "+intType);

Double  doubleType=new Double(c);
System.out.println("doubleType = "+doubleType);

Float floatType=new Float(c);
System.out.println("floatType = "+floatType);

BigDecimal decimalType=new BigDecimal(c);
System.out.println("decimalType = "+decimalType);

Long longType=new Long(c);
System.out.println("longType = "+longType);

尽管 c 是 char 的一种类型,但在相应的构造函数中可以无误地提供它,并且上述所有语句都被视为有效语句。 它们分别产生以下输出。
intType = 65
doubleType = 65.0
floatType = 65.0
decimalType = 65
longType =65

char是一种原始数字整数类型,因此受到这些类型的所有规则的限制,包括转换和提升。您需要阅读相关知识,而JLS是其中最好的资源之一:Conversions and Promotions。特别地,请阅读有关“5.1.2扩展原始转换”的简短介绍。


3

Java编译器可以将其解释为任何一个。

通过编写程序并查找编译器错误来检查它:

public static void main(String[] args) {
    int result1 = 'a' + 'b';
    char result2 = 'a' + 'b';
}

如果是字符,第一行会出现错误,而第二行不会。如果是整数,则相反。
我编译了它,没有任何错误。所以Java接受两者。
然而,当我打印它们时,我得到了:
int: 195 char: Ã
发生的情况是当你执行以下操作:
char result2 = 'a' + 'b'

执行了隐式转换(从intchar的“原始缩小转换”)。

@eboix:你写道“编译器会自动转换为char或int”[sic]...这是不正确的,因为int并没有被“转换”为int,从intchar的过程也不叫做“转换”,而是称为“缩小原始类型转换”;) - TacticalCoder
是的,我也预料到了。在编写程序之前,我已经写了一部分关于这个问题的答案,但是当我看到没有警告或错误时,我就把它删除了。 - eboix
如果你想的话,@user988052,我可以将它改回原来的样子,然后你可以编辑它从而获取积分。如果你的声望值低于2000分,你仍然可以编辑它。不过需要注意的是,发帖人必须接受这个修改。我现在就将它改回去,这样你就能拿到属于你的两个积分了。 - eboix
为什么对于这一行 char result2 = 'a' + 'b' 进行了隐式转换,但是对于类似 char x, y = 2, z = 3; x = z + y; 的情况却没有进行隐式转换呢? - matt
@matt - 因为z + y不是常量表达式。请查看我的答案,其中包含更详细的解释...以及JLS链接。 - Stephen C
显示剩余2条评论

1
根据二进制提升规则,如果两个操作数都不是double、float或long类型,则它们都会被提升为int类型。然而,我强烈建议不要将char类型视为数字类型,这样做有点违背了它的初衷。

1
在程序中使用char作为数字值是完全符合其目的的,这也是JLS为此类用法付出大量言辞的原因。 - Lew Bloch

1

虽然您已经有了正确的答案(在JLS中引用),但这里有一些代码来验证当添加两个char时,您会得到一个int

public class CharAdditionTest
{
    public static void main(String args[])
    {
        char a = 'a';
        char b = 'b';

        Object obj = a + b;
        System.out.println(obj.getClass().getName());
    }
}

输出结果是

java.lang.Integer


这不是一个令人信服的验证,因为你混合了装箱转换。类型intInteger并不是同一种东西。更有说服力的验证是尝试将a + b分配给一个int变量或一个char变量。后者会产生编译错误,大致意思是“你不能将int分配给char”。 - Stephen C

0

尽管 Boann的答案是正确的,但在赋值上下文中出现常数表达式的情况会产生复杂性。

考虑以下示例:

  char x = 'a' + 'b';   // OK

  char y = 'a';
  char z = y + 'b';     // Compilation error

发生了什么?它们不是意思相同吗?为什么在第一个例子中将int赋值给char是合法的?

当常量表达式出现在赋值上下文中时,Java编译器计算表达式的值并查看它是否在您要分配的类型范围内。如果是,则应用隐式的缩小原始转换

在第一个例子中,'a' + 'b' 是一个常量表达式,它的值将适合于一个 char,所以编译器允许将 int 表达式结果的隐式缩小为 char
在第二个例子中,y 是一个变量,因此 y + 'b' 不是一个常量表达式。即使Blind Freddy也能看出值将适合,编译器也不允许任何隐式缩小,并且您会得到一个编译错误,指出无法将 int 赋值给 char

在这种情况下,有一些其他注意事项关于何时允许隐式的缩小原始转换;请参阅JLS 5.2JLS 15.28获取完整详情。

后者详细解释了常量表达式的要求。 它可能不是您想象中的那样。(例如,仅将y声明为final是没有帮助的。)


0

char 被表示为 Unicode 值,其中 Unicode 值由 \u 后跟 十六进制 值表示。

由于任何应用于 char 值并提升为 int 的算术运算,因此 'a' + 'b' 的结果计算如下:

1.) 使用 Unicode 表 将相应的 char 应用于 Unicode

2.) 应用 十六进制转十进制 并在 Decimal 值上执行操作。

    char        Unicode      Decimal
     a           0061          97
     b           0062          98  +
                              195

Unicode 十进制转换器

例子

0061

(0*16^3) + (0*16^2) + (6*16^1) + (1*16^0) (0*4096) + (0*256) + (6*16) + (1*1) 0 + 0 + 96 + 1 = 97
0062

(0*163)+(0*162)+(6*161)+(2*160
(0*4096)+(0*256)+(6*16)+(2*1)
0 + 0 + 96 + 2 = 98
因此 97 + 98 = 195

例子2

char        Unicode      Decimal
 Ջ           054b         1355
 À           00c0          192 
                      --------
                          1547    +
                          1163    -
                             7    /
                        260160    *
                            11    %

这不是问题所询问的内容。 - Stephen C
我的详细回复已被管理员删除,他们解释了将我的答案从已删除的帖子中迁移的原因。如果我的回复被删除是为了掩盖SO管理的缺陷,那么你能否至少删除上面的评论或以某种方式指示受众,这样我就不必再写一条评论了?问题提交给SO管理员。 - Pavneet_Singh
我坚持我的评论。正如我回复你的时候……但是后来删除了……你在这里发布这个问题的原因并不改变它与问题95%无关,而且5%重复了早期的答案。如果你想保存你浪费的努力的结果,最好将其保存在你的硬盘上。或者找到其他相关的问题。 - Stephen C
如果您对版主有任何意见,请在meta.stackoverflow.com上提出,并提供证据。我在这里所做的就是整理这个问答。 - Stephen C
离开一个制作水果蛋糕的配方也可能会帮助读者...如果他正在尝试烘烤水果蛋糕。如果用户想要了解 Unicode 转换的详细信息,他们很可能已经使用不同的搜索词,并最终找到了完全不同的问题。为了使这个答案有用,它需要在那个问题上。这个问题是关于结果的类型而不是值。 - Stephen C
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接