简体   繁体   English

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

[英]How does System.out.print() work?

I have worked with Java for a quite a long time, and I was wondering how the function System.out.print() works. 我已经使用Java很长一段时间了,我想知道函数System.out.print()是如何工作的。

Here is my doubt: 这是我的疑问:

Being a function, it has a declaration somewhere in the io package. 作为一个函数,它在io包中的某个地方有一个声明。 But how did Java developers do that, since this function can take in any number of arguments and any argument types no matter how they are arranged? 但Java开发人员是如何做到这一点的,因为这个函数可以接受任意数量的参数和任何参数类型,无论它们如何排列? eg: 例如:

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);

No matter what is the datatype of variables a, b, c, usd, foo or how they are passed, System.out.print() never throws an error. 无论变量a, b, c, usd, foo或它们如何传递的数据类型是什么, System.out.print()不会抛出错误。

For me, I have never worked on any project where the requirement was like this. 对我来说,我从未参与任何需求如此的项目。 Provided, if I get a requirement like this, I really don't know how to solve it. 如果我得到这样的要求,我真的不知道如何解决它。

Can anyone explain to me how it's done? 任何人都可以向我解释它是如何完成的吗?

System.out is just an instance of PrintStream . System.out只是PrintStream一个实例。 You can check its JavaDoc . 您可以检查其JavaDoc Its variability is based on method overloading (multiple methods with the same name, but with different parameters). 它的可变性基于方法重载 (具有相同名称但具有不同参数的多个方法)。

This print stream is sending its output to so called standard output . 此打印流将其输出发送到所谓的标准输出


In your question you mention a technique called variadic functions (or varargs ). 在你的问题中,你提到了一种称为可变函数 (或varargs )的技术。 Unfortunately that is not supported by PrintStream#print , so you must be mistaking this with something else. 不幸的是, PrintStream#print不支持,因此您必须将其与其他内容混淆。 However it is very easy to implement these in Java. 但是,在Java中实现它们非常容易。 Just check the documentation. 只需查看文档。


And if you are curious how Java knows how to concatenate non-string variables "foo" + 1 + true + myObj , it is mainly responsibility of a Java compiler. 如果你很好奇Java如何连接非字符串变量"foo" + 1 + true + myObj ,它主要负责Java编译器。

When there is no variable involved in the concatenation, the compiler simply concatenates the string. 当串联中没有涉及变量时,编译器只是连接字符串。 When there is a variable involved, the concatenation is translated into StringBuilder#append chain. 当涉及变量时,连接将转换为StringBuilder#append chain。 There is no concatenation instruction in the resulting byte code; 结果字节代码中没有连接指令; ie the + operator (when talking about string concatenation) is resolved during the compilation. 即在编译期间解析+运算符(当谈论字符串连接时)。

All types in Java can be converted to string ( int via methods in Integer class, boolean via methods in Boolean class, objects via their own #toString , ...). Java中的所有类型都可以转换为字符串( int通过Integer类中的方法, boolean通过Boolean类中的方法,对象通过自己的#toString ,...)。 You can check StringBuilder's source code if you are interested. 如果您有兴趣,可以查看StringBuilder的源代码。


UPDATE: I was curious myself and checked (using javap ) what my example System.out.println("foo" + 1 + true + myObj) compiles into. 更新:我很好奇自己并检查(使用javap )我的示例System.out.println("foo" + 1 + true + myObj)编译成。 The result: 结果:

System.out.println(new StringBuilder("foo1true").append(myObj).toString());

Even though it look as if System.put.print...() take a variable number of arguments it doesn't. 即使看起来好像System.put.print...()采用可变数量的参数,它也不会。 If you look closely, the string is simply concatenated and you can do the same with any string. 如果你仔细观察,字符串只是连接在一起,你可以对任何字符串做同样的事情。 The only thing that happens is, that the objects you are passing in, are implicitily converted to a string by java calling the toString() method. 唯一发生的事情是,传入的对象被java调用toString()方法隐式转换为字符串。

If you try to do this it will fail: 如果您尝试这样做,它将失败:

int i = 0;
String s = i;
System.out.println(s);

Reason is, because here the implicit conversion is not done. 原因是,因为这里隐式转换没有完成。

However if you change it to 但是,如果您将其更改为

int i = 0;
String s = "" + i;
System.out.println(s);

It works and this is what happens when using System.put.print...() as well. 它工作正常,这也是使用System.put.print...()时发生的情况。

If you want to implement a variable number of arguments in java to mimimc something like C printf you can declare it like this: 如果你想在java中实现可变数量的参数来mimimc像C printf这样的东西,你可以这样声明它:

public void t(String s, String ... args)
{
    String val = args[1];
}

What happens here is that an array of Strings is passed in, with the length of the provided arguments. 这里发生的是传入一个字符串数组,其中包含所提供参数的长度。 Here Java can do the type checking for you. 这里Java可以为您进行类型检查。

If you want truly a printf then you have to do it like this: 如果你想真正的printf那么你必须这样做:

public void t(String s, Object ... args)
{
    String val = args[1].toString();
}

Then would you have to cast or interpret the arguments accordingly. 那么你是否必须相应地演绎或解释这些论点。

It is a very sensitive point to understand how to work System.out.print. 了解如何使用System.out.print是一个非常敏感的问题。 If the first element is String then plus(+) operator works as String concate operator. 如果第一个元素是String,则plus(+)运算符作为String concate运算符。 If the first element is integer plus(+) operator works as mathematical operator. 如果第一个元素是整数加号(+),则运算符作为数学运算符。

public static void main(String args[]) {
    System.out.println("String" + 8 + 8); //String88
    System.out.println(8 + 8+ "String"); //16String
}

Its all about Method Overloading . 它的全部是关于方法重载

There are individual methods for each data type in println() method println()方法中的每种数据类型都有单独的方法

If you pass object : 如果传递对象:

Prints an Object and then terminate the line. 打印一个对象,然后终止该行。 This method calls at first String.valueOf(x) to get the printed object's string value, then behaves as though it invokes print(String) and then println(). 此方法首先调用String.valueOf(x)来获取打印对象的字符串值,然后表现为调用print(String)然后调用println()。

If you pass Primitive type: 如果您传递原始类型:

corresponding primitive type method calls 相应的原始类型方法调用

if you pass String : 如果你传递字符串:

corresponding println(String x) method calls 对应的println(String x)方法调用

I think you are confused with the printf(String format, Object... args) method. 我认为你对printf(String format, Object... args)方法感到困惑。 The first argument is the format string, which is mandatory, rest you can pass an arbitrary number of Object s. 第一个参数是格式字符串,这是必需的,其余的可以传递任意数量的Object

There is no such overload for both the print() and println() methods. print()println()方法都没有这样的重载。

You can convert anything to a String as long as you choose what to print. 只要选择要打印的内容,就可以将任何内容转换为String。 The requirement was quite simple since Objet.toString() can return a default dumb string: package.classname + @ + object number . 由于Objet.toString()可以返回默认的哑字符串: package.classname + @ + object number因此要求非常简单。

If your print method should return an XML or JSON serialization, the basic result of toString() wouldn't be acceptable. 如果print方法应返回XML或JSON序列化,则toString()的基本结果将不可接受。 Even though the method succeed. 即使该方法成功。

Here is a simple example to show that Java can be dumb 这是一个简单的例子,表明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");

will print something package.Mocktest@3254487 ! 会打印一些东西package.Mocktest@3254487 Even though you only have two String members and this could be implemented to print 即使您只有两个String成员,也可以实现打印

Mocktest@3254487{"field1":"a","field2":"b"}

(or pretty much how it appears in the debbuger) (或几乎如何出现在debbuger中)

Evidently, the compiler was made in a confusing way although the compiler developers thought they added some smartness. 显然,尽管编译器开发人员认为编译器增加了一些智能,但编译器仍然是一种令人困惑的方式。 The true smartness they should really add is to look entire argument and interpret + operator consistently. 他们真正应该添加的真正智慧是查看整个论证并一致地解释+运算符。 For example, System.out.println(1+2+"hello"+3+4); 例如, System.out.println(1+2+"hello"+3+4); should output 3hello7 instead of 3hello34 应该输出3hello7而不是3hello34

@ikis, firstly as @Devolus said these are not multiple aruements passed to print() . @ikis,首先是@Devolus说这些不是传递给print()多个aruements。 Indeed all these arguments passed get concatenated to form a single String. 实际上,传递的所有这些参数都会连接起来形成一个String。 So print() does not teakes multiple arguements (aka var-args). 因此print()不会产生多个争论(又名var-args)。 Now the concept that remains to discuss is how print() prints any type of the arguement passed to it. 现在要讨论的概念是print()如何print()传递给它的任何类型的争论。

To explain this - toString() is the secret: 解释一下 - toString()是秘密:

System is a class, with a static field out , of type PrintStream . System是一个类,具有类型为PrintStream的静态字段out So you're calling the println(Object x) method of a PrintStream . 所以你要调用PrintStreamprintln(Object x)方法。

It is implemented like this: 它实现如下:

 public void println(Object x) {
   String s = String.valueOf(x);
   synchronized (this) {
       print(s);
       newLine();
   }
}

As wee see, it's calling the String.valueOf(Object) method. 如我们所见,它正在调用String.valueOf(Object)方法。 This is implemented as follows: 这实现如下:

 public static String valueOf(Object obj) {
   return (obj == null) ? "null" : obj.toString();
}

And here you see, that toString() is called. 在这里你看, toString()被调用。

So whatever is returned from the toString() method of that class, same gets printed. 因此无论从该类的toString()方法返回什么,都会打印出相同的内容。

And as we know the toString() is in Object class and thus inherits a default iplementation from Object. 而且我们知道toString()Object类中,因此从Object继承了默认的iplementation。

ex: Remember when we have a class whose toString() we override and then we pass that ref variable to print , what do you see printed? 例:请记住,当我们有一个类的toString()我们覆盖,然后我们将该ref变量传递给print ,你看到什么打印? - It's what we return from the toString() . - 这是我们从toString()返回的内容。

The scenarios that you have mentioned are not of overloading, you are just concatenating different variables with a String. 您提到的方案不是重载,您只是使用String连接不同的变量。

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);

in all of these cases, you are only calling print(String s) because when something is concatenated with a string it gets converted to a String by calling the toString() of that object, and primitives are directly concatenated. 在所有这些情况下,您只调用print(String s),因为当某些东西与字符串连接时,它会通过调用该对象的toString()转换为String,并且原语直接连接。 However if you want to know of different signatures then yes print() is overloaded for various arguments. 但是,如果您想知道不同的签名,那么对于各种参数,都会重载print()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM