System.out.print()如何工作?

时间:2013-06-15 07:37:22

标签: java system.out

我在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()都不会抛出错误。

对我来说,我从未参与任何需求如此的项目。如果我得到这样的要求,我真的不知道如何解决它。

有人可以向我解释它是如何完成的吗?

9 个答案:

答案 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

println() method

中的每种数据类型都有单独的方法

如果传递对象:

  

打印一个对象,然后终止该行。此方法首先调用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()。