可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have worked with Java for a quite a long time, and I was wondering how the function System.out.print()
works.
Here is my doubt:
Being a function, it has a declaration somewhere in the io package. 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? 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);
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.
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?
回答1:
System.out
is just an instance of PrintStream
. You can check its 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). Unfortunately that is not supported by PrintStream#print
, so you must be mistaking this with something else. However it is very easy to implement these in 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.
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. There is no concatenation instruction in the resulting byte code; i.e. 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
, ...). You can check StringBuilder's source code if you are interested.
UPDATE: I was curious myself and checked (using javap) what my example System.out.println("foo" + 1 + true + myObj)
compiles into. The result:
System.out.println(new StringBuilder("foo1true").append(myObj).toString());
回答2:
Even though it look as if System.put.print...()
take a variable number of arguments it doesn't. 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.
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.
If you want to implement a variable number of arguments in java to mimimc something like C printf
you can declare it like this:
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.
If you want truly a printf then you have to do it like this:
public void t(String s, Object ... args) { String val = args[1].toString(); }
Then would you have to cast or interpret the arguments accordingly.
回答3:
I think you are confused with the printf(String format, Object... args)
method. The first argument is the format string, which is mandatory, rest you can pass an arbitrary number of Object
s.
There is no such overload for both the print()
and println()
methods.
回答4:
Its all about Method Overloading.
There are individual methods for each data type in println() method
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().
If you pass Primitive type:
corresponding primitive type method calls
if you pass String :
corresponding println(String x) method calls
回答5:
You can convert anything to a String as long as you choose what to print. The requirement was quite simple since Objet.toString()
can return a default dumb string: package.classname + @ + object number
.
If your print method should return an XML or JSON serialization, the basic result of toString() wouldn't be acceptable. Even though the method succeed.
Here is a simple example to show that Java can be dumb
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
! Even though you only have two String members and this could be implemented to print
Mocktest@3254487{"field1":"a","field2":"b"}
(or pretty much how it appears in the debbuger)
回答6:
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);
should output 3hello7
instead of 3hello34
回答7:
It is a very sensitive point to understand how to work System.out.print. If the first element is String then plus(+) operator works as Strig concate operator. 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 }