我在Java工作了很长时间,我想知道函数System.out.print()
是如何工作的。
这是我的疑问:
作为一个功能,它在 io 包中的某个地方有声明。但Java开发人员是如何做到这一点的,因为这个函数可以接受任意数量的参数和任何参数类型,无论它们如何排列? e.g:
System.out.print("Hello World");
System.out.print("My name is" + foo);
System.out.print("Sum of " + a + "and " + b + "is " + c);
System.out.print("Total USD is " + usd);
无论变量a, b, c, usd, foo
的数据类型是什么或它们如何传递,System.out.print()
都不会抛出错误。
对我来说,我从未参与任何需求如此的项目。如果我得到这样的要求,我真的不知道如何解决它。
有人可以向我解释它是如何完成的吗?
答案 0 :(得分:22)
System.out
只是PrintStream
的一个实例。您可以查看其JavaDoc。它的可变性基于method overloading(具有相同名称但具有不同参数的多个方法)。
此打印流将其输出发送到所谓的standard output。
在您的问题中,您提到了一种名为variadic functions(或 varargs )的技术。遗憾的是,PrintStream#print
不支持此功能,因此您必须将其与其他内容混淆。但是,在Java中实现它们非常容易。 Just check the documentation.
如果您好奇Java如何知道如何连接非字符串变量"foo" + 1 + true + myObj
,那么它主要负责Java编译器。
当串联中没有涉及变量时,编译器只是连接字符串。当涉及变量时,连接将转换为StringBuilder#append
链。结果字节代码中没有连接指令;即,+
运算符(在谈论字符串连接时)在编译期间被解析。
Java中的所有类型都可以通过int
类,Integer
中的方法通过boolean
类中的方法,通过自己的{{1}对象转换为字符串Boolean
},...)。如果您有兴趣,可以查看StringBuilder的源代码。
更新:我很好奇并检查了(使用javap)我的示例#toString
编译成的内容。结果:
System.out.println("foo" + 1 + true + myObj)
答案 1 :(得分:3)
即使它看起来好像System.put.print...()
采用了可变数量的参数,但它却没有。如果你仔细观察,字符串只是连接在一起,你可以对任何字符串做同样的事情。唯一发生的事情是,传入的对象被java调用toString()
方法隐式转换为字符串。
如果您尝试这样做,则会失败:
int i = 0;
String s = i;
System.out.println(s);
原因是,因为这里没有进行隐式转换。
但是,如果将其更改为
int i = 0;
String s = "" + i;
System.out.println(s);
它有效,这也是使用System.put.print...()
时的情况。
如果你想在java中实现可变数量的参数来模仿类似C printf
的东西,你可以这样声明它:
public void t(String s, String ... args)
{
String val = args[1];
}
这里发生的是传入一个字符串数组,其中包含所提供参数的长度。这里Java可以为您进行类型检查。
如果你想要真正的printf那么你必须这样做:
public void t(String s, Object ... args)
{
String val = args[1].toString();
}
那么你是否必须相应地演绎或解释这些论点。
答案 2 :(得分:1)
了解如何使用System.out.print是一个非常敏感的问题。 如果第一个元素是String,则plus(+)运算符作为String concate运算符。如果第一个元素是整数加(+),则运算符作为数学运算符。
public static void main(String args[]) {
System.out.println("String" + 8 + 8); //String88
System.out.println(8 + 8+ "String"); //16String
}
答案 3 :(得分:0)
全部关于Method Overloading。
中的每种数据类型都有单独的方法如果传递对象:
打印一个对象,然后终止该行。此方法首先调用String.valueOf(x)来获取打印对象的字符串值,然后表现为调用print(String)然后调用println()。
如果您传递原始类型:
相应的基元类型方法调用
如果你传递String:
对应的println(String x)方法调用
答案 4 :(得分:0)
我认为你对printf(String format, Object... args)
方法感到困惑。第一个参数是格式字符串,这是必需的,其余的可以传递任意数量的Object
s。
print()
和println()
方法都没有这样的重载。
答案 5 :(得分:0)
只要您选择要打印的内容,就可以将任何内容转换为字符串。要求非常简单,因为Objet.toString()
可以返回默认的哑字符串:package.classname + @ + object number
。
如果您的print方法应返回XML或JSON序列化,则toString()的基本结果将不可接受。即使该方法成功了。
这是一个简单的例子,表明Java可能是愚蠢的
public class MockTest{
String field1;
String field2;
public MockTest(String field1,String field2){
this.field1=field1;
this.field2=field2;
}
}
System.out.println(new MockTest("a","b");
会打印package.Mocktest@3254487
的内容!即使您只有两个String成员,也可以实现打印
Mocktest@3254487{"field1":"a","field2":"b"}
(或几乎在debbuger中出现的方式)
答案 6 :(得分:0)
显然,编译器是以令人困惑的方式制作的,尽管编译器开发人员认为它们增加了一些智能性。他们真正应该添加的真正智慧是查看整个论证并一致地解释+运算符。例如,System.out.println(1+2+"hello"+3+4);
应输出3hello7
而不是3hello34
答案 7 :(得分:0)
@ikis,首先,正如@Devolus所说的,这些不是传递给print()
的多个款项。确实,所有这些通过的论点都得到
串联形成单个String。因此print()
不会嘲弄多个论点(也称为var-args)。现在,仍有待讨论的概念是print()
如何打印通过的任何类型的论点
对此。
对此进行解释-toString()
是秘密:
System
是一个类,其静态字段为out
,类型为PrintStream
。因此,您正在调用a的println(Object x)
方法
PrintStream
。
它是这样实现的:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
如我们所见,它正在调用String.valueOf(Object)方法。实现如下:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
在这里您看到toString()
被调用。
因此,该类的toString()
方法返回的所有内容都会被打印出来。
我们知道toString()
在Object
类中,因此从Object继承了默认实现。
ex:请记住,当我们有一个其toString()
被覆盖的类,然后将ref变量传递给print
时,您看到了什么? -这就是我们从toString()
返回的内容。
答案 8 :(得分:0)
您提到的方案不是重载,您只是将不同的变量与字符串连接起来。
System.out.print("Hello World");
System.out.print("My name is" + foo);
System.out.print("Sum of " + a + "and " + b + "is " + c);
System.out.print("Total USD is " + usd);
在所有这些情况下,您只调用print(String s),因为当某些东西与字符串连接时,它会通过调用该对象的toString()转换为String,而原语将被直接串联。 但是,如果您想知道不同的签名,那么可以为各种参数重载print()。