私はJavaでかなり長い間働いてきましたが、関数System.out.print()
がどのように機能するのか疑問に思っていました。
私の疑問は次のとおりです。
関数であるため、ioパッケージのどこかに宣言があります。しかし、どのようにJava開発者がそうしましたか?
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()
はエラーをスローしません。
私にとって、要件がこのようなものであったプロジェクトに取り組んだことはありません。ただし、このような要件を取得した場合、それを解決する方法は本当にわかりません。
誰がそれがどのように行われたか説明できますか?
_System.out
_はPrintStream
の単なるインスタンスです。 JavaDoc で確認できます。その可変性は、 method overloading (同じ名前で異なるパラメーターを持つ複数のメソッド)に基づいています。
この印刷ストリームは、出力をいわゆる standard output に送信しています。
あなたの質問では、 variadic functions (またはvarargs)と呼ばれるテクニックに言及しています。残念ながら、これは_PrintStream#print
_でサポートされていないため、他の何かと間違えているに違いありません。ただし、これらをJavaで実装するのは非常に簡単です。 ドキュメントを確認してください。
また、Javaが非文字列変数_"foo" + 1 + true + myObj
_を連結する方法を知っている場合は、主にJavaコンパイラーの責任です。
連結に関係する変数がない場合、コンパイラは単に文字列を連結します。関係する変数がある場合、連結は _StringBuilder#append
_ チェーンに変換されます。結果のバイトコードには連結命令はありません。つまり、_+
_演算子(文字列の連結について話すとき)はコンパイル中に解決されます。
Javaのすべての型は、文字列に変換できます(int
クラスのメソッドを介して、Integer
、boolean
クラスのメソッドを介して、Boolean
、独自の_#toString
_、...を介してオブジェクト)。興味がある場合は、StringBuilderのソースコードを確認してください。
UPDATE:私は自分が興味を持ち、( javap を使用して)私の例System.out.println("foo" + 1 + true + myObj)
が何にコンパイルされるかをチェックしました。結果:
_System.out.println(new StringBuilder("foo1true").append(myObj).toString());
_
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...()
を使用したときにも起こります。
C printf
のようなものをmimimcにJavaで可変数の引数を実装する場合は、次のように宣言できます。
_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();
}
_
次に、それに応じて引数をキャストまたは解釈する必要があります。
System.out.printの動作方法を理解することは非常にデリケートなポイントです。最初の要素が文字列の場合、plus(+)演算子は文字列連結演算子として機能します。最初の要素が整数である場合、プラス(+)演算子は数学演算子として機能します。
public static void main(String args[]) {
System.out.println("String" + 8 + 8); //String88
System.out.println(8 + 8+ "String"); //16String
}
明らかに、コンパイラーは混乱を招くように作成されましたが、コンパイラーの開発者はスマートさを追加したと考えていました。彼らが本当に追加すべき真のスマートさは、引数全体を見て、+演算子を一貫して解釈することです。たとえば、System.out.println(1+2+"hello"+3+4);
は3hello7
ではなく3hello34
を出力する必要があります
あなたが言及したシナリオはオーバーロードではなく、異なる変数を文字列に連結しているだけです。
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);
これらすべての場合において、何かが文字列と連結されると、オブジェクトのtoString()を呼び出すことで文字列に変換され、プリミティブが直接連結されるため、print(String s)を呼び出すだけです。ただし、異なるシグネチャを知りたい場合は、yes print()がさまざまな引数に対してオーバーロードされます。
それはすべて メソッドのオーバーロード 。
println()method には、データ型ごとに個別のメソッドがあります
オブジェクトを渡す場合:
オブジェクトを出力して、行を終了します。このメソッドは、最初にString.valueOf(x)を呼び出して印刷オブジェクトの文字列値を取得し、次にprint(String)を呼び出してからprintln()を呼び出すように動作します。
プリミティブ型を渡す場合:
対応するプリミティブ型のメソッド呼び出し
stringを渡す場合:
対応するprintln(String x)メソッド呼び出し
printf(String format, Object... args)
メソッドと混同していると思います。最初の引数はフォーマット文字列です。これは必須ですが、残りは任意の数のObject
sを渡すことができます。
print()
メソッドとprintln()
メソッドの両方にこのようなオーバーロードはありません。
@ikis、第一に@Devolusが言ったように、これらはprint()
に渡される複数の要素ではありません。実際、渡されたこれらの引数はすべて連結されて単一の文字列を形成します。したがって、print()
は複数の引数(別名k。a。var-args)をティークしません。ここで議論すべき概念は、print()
が渡された引数のタイプをどのように表示するかです。
これを説明するには-toString()
が秘密です:
System
は、out
型の静的フィールドPrintStream
を持つクラスです。したがって、PrintStream
のprintln(Object x)
メソッドを呼び出しています。
次のように実装されます。
_ 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からデフォルトの実装を継承します。
例:toString()
をオーバーライドするクラスがあり、その参照変数をprint
に渡すと、何が表示されるのか覚えていますか? -toString()
から返すものです。
印刷するものを選択する限り、何でも文字列に変換できます。 Objet.toString()
はデフォルトのダム文字列を返すことができるため、要件は非常に単純でした:package.classname + @ + object number
。
印刷メソッドが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
! 2つのStringメンバーのみがあり、これを実装して印刷することもできますが
Mocktest@3254487{"field1":"a","field2":"b"}
(またはそれがデバッガーにどのように表示されるか)