我只想知道Java中的一个简单的事情。让我们考虑Java中的以下代码段。
int x=10;
String temp="x = "+x;
System.out.println(temp);
在Java中完全有效并产生输出,x = 10作为字符串。
虽然变量x的类型为 int ,但它会自动转换为String类型(包装类型)。 Java如何做到这一点?像C ++和C#一样,运算符重载在某种程度上或某处存在于Java中,尽管它已从Java中删除。 此处使用哪个特定概念将x转换为字符串。我唯一想知道的事情
还有一个问题,在Java中,布尔数据类型(不是布尔值,包装类)不能转换为任何其他类型(根据我所知)。为什么会这样,在某些非常特殊的情况下,将它转换为字符串或其他类型可能会有用。
答案 0 :(得分:9)
编译器在内部将该短语(“x =”+ x)转换为StringBuilder,并使用.append(int)将整数“添加”到字符串中。
为了超越实际的“Java如何做到这一点”,我将接受斯蒂芬的建议并给出理论。从概念上讲,串联中的每个值首先转换为String,然后连接。空值连接为单词“null”。
来自Java Language Specification:
15.18.1.1字符串转换
任何类型都可以通过字符串转换转换为String类型。一个值 原始类型T的x首先被转换为参考值,如同 将它作为适当的类实例创建的参数 表达式:
如果T是布尔值,则使用new Boolean(x)。如果T是char,那么使用new 字符(X)。如果T是byte,short或int,则使用new Integer(x)。如果 T很长,然后使用新的Long(x)。如果T是float,则使用new Float(x)。 如果T为double,则使用new Double(x)。那么这个参考值 通过字符串转换转换为String类型。现在只参考 需要考虑价值观。如果引用为null,则为 转换为字符串“null”(四个ASCII字符n,u,l,l)。 否则,转换就像通过调用一样执行 没有参数的引用对象的toString方法;但如果 调用toString方法的结果为null,则字符串为“null” 而是用来代替。
toString方法由原始类Object定义;许多 类覆盖它,特别是布尔,字符,整数,长,浮点数, Double和String。
15.18.1.2字符串连接的优化
实现可以选择执行转换和连接 在一步中避免创建然后丢弃中间体 字符串对象。增加重复字符串的性能 连接,Java编译器可以使用StringBuffer类或a 类似的技术减少了中间String对象的数量 通过评估表达式创建的。对于原始类型, 实现还可以优化包装器的创建 对象通过直接从基本类型转换为字符串。
优化版本实际上不会首先执行完全包装的String转换。
这是编译器使用的优化版本的一个很好的例证,虽然没有原语的转换,你可以看到编译器在后台将事物更改为StringBuilder:
http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/
这个java代码:
public static void main(String[] args) {
String cip = "cip";
String ciop = "ciop";
String plus = cip + ciop;
String build = new StringBuilder(cip).append(ciop).toString();
}
生成这个 - 看看两个连接样式如何导致相同的字节码:
L0
LINENUMBER 23 L0
LDC "cip"
ASTORE 1
L1
LINENUMBER 24 L1
LDC "ciop"
ASTORE 2
// cip + ciop
L2
LINENUMBER 25 L2
NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
ASTORE 3
// new StringBuilder(cip).append(ciop).toString()
L3
LINENUMBER 26 L3
NEW java/lang/StringBuilder
DUP
ALOAD 1
INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
ASTORE 4
L4
LINENUMBER 27 L4
RETURN
编译器已将“cip + ciop”转换为“new StringBuilder(cip).append(ciop).toString()”。换句话说,“+”实际上是更冗长的StringBuilder习语的简写。
答案 1 :(得分:1)
Java确实支持有限运算符重载...对于具有某些(基本)操作数类型组合的内置运算符。主要案例是:
算术运算符对不同的数字基元类型具有重载。除了字符串连接外,还会重载“+”运算符。
'&amp;','|','^'和'!'运算符因(非短路)逻辑和按位积分运算而过载。
但是,Java 不支持支持任何形式的程序员定义的运算符或运算符重载。
说重载是“编译器魔术”或“编译器执行”的答案都缺少重点。 Java语言规范说它是语言的一部分,它意味着什么,并且(几乎)要求如何实现它。
正如另一个答案所指出的,自动装箱/拆箱和其他事情(严格来说)是转换而不是重载。例如:
int j = ...
Integer i = ...
j = j + i;
这使用'+'运算符的(int, int)
重载,并使用auto-unboxing将第二个操作数转换为int。
byte b = ...
j = j + b;
这使用'+'运算符的(int, int)
重载,并使用强制转换将第二个操作数转换为int。
请注意,JLS指定了根据操作数的(初始)类型确定使用哪个运算符重载的规则。因此,在第二个示例中,JLS要求使用'{'的(int, int)
重载,而不是(byte, byte)
重载。
答案 2 :(得分:0)
这都是编译魔术。你不能用Java做运算符重载。
穿过手指,希望这不是可怕的错误,让我得到20个downvotes