Je travaille avec Java depuis assez longtemps et je me demandais comment fonctionne la fonction System.out.print()
.
Voici mon doute:
En tant que fonction, il a une déclaration quelque part dans le paquet io . Mais comment les développeurs Java ont-ils fait cela, puisque cette fonction peut prendre n'importe quel nombre d'arguments et tous types d'arguments, peu importe la façon dont ils sont organisés par exemple:
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);
Quel que soit le type de données des variables a, b, c, usd, foo
ou la manière dont elles sont transmises, System.out.print()
ne génère jamais d'erreur.
Pour moi, je n'ai jamais travaillé sur un projet où l'exigence était comme ça. À condition que, si j'ai une telle exigence, je ne sache vraiment pas comment la résoudre.
Quelqu'un peut-il m'expliquer comment c'est fait?
System.out
est simplement une instance de PrintStream
. Vous pouvez vérifier son JavaDoc . Sa variabilité est basée sur surcharge de méthode (plusieurs méthodes portant le même nom, mais avec des paramètres différents).
Ce flux d'impression envoie sa sortie à ce qu'on appelle sortie standard .
Dans votre question, vous mentionnez une technique appelée fonctions variadiques (ou varargs). Malheureusement, ce n'est pas supporté par PrintStream#print
, vous devez donc le confondre avec autre chose. Cependant, il est très facile de les implémenter en Java. Il suffit de consulter la documentation.
Et si vous êtes curieux de savoir comment Java sait comment concaténer des variables non-chaînes "foo" + 1 + true + myObj
, il est principalement de la responsabilité d'un compilateur Java.
Lorsqu'il n'y a pas de variable impliquée dans la concaténation, le compilateur concatène simplement la chaîne. Quand une variable est impliquée, la concaténation est traduite en chaîne StringBuilder#append
. Il n'y a pas d'instruction de concaténation dans le code d'octet résultant; c'est-à-dire que l'opérateur +
(lorsqu'il est question de concaténation de chaînes) est résolu lors de la compilation.
Tous les types en Java peuvent être convertis en chaîne (int
via des méthodes dans Integer
classe, boolean
via des méthodes dans Boolean
classe, des objets via leur propre #toString
, ...). Vous pouvez vérifier le code source de StringBuilder si cela vous intéresse.
UPDATE: J'étais moi-même curieux et j'ai vérifié (en utilisant javap ) ce que mon exemple System.out.println("foo" + 1 + true + myObj)
compile. Le résultat:
System.out.println(new StringBuilder("foo1true").append(myObj).toString());
Même s'il semble que System.put.print...()
prenne un nombre variable d'arguments, ce n'est pas le cas. Si vous regardez bien, la chaîne est simplement concaténée et vous pouvez faire de même avec n'importe quelle chaîne. La seule chose qui se produit est que les objets que vous transmettez sont convertis implicitement en chaîne par Java en appelant la méthode toString()
.
Si vous essayez de faire cela, cela échouera:
int i = 0;
String s = i;
System.out.println(s);
La raison est parce qu'ici la conversion implicite n'est pas faite.
Cependant, si vous le changez en
int i = 0;
String s = ""+i;
System.out.println(s);
cela fonctionne et c’est ce qui se produit lors de l’utilisation de System.put.print...()
.
Si vous voulez implémenter un nombre variable d'arguments en Java pour imiter quelque chose comme C printf
, vous pouvez le déclarer comme ceci:
public void t(String s, String ... args)
{
String val = args[1];
}
Ce qui se passe ici, c'est qu'un tableau de chaînes est passé, avec la longueur des arguments fournis. Ici, Java peut effectuer la vérification de type pour vous.
Si vous voulez vraiment une printf, vous devez le faire comme ceci:
public void t(String s, Object ... args)
{
String val = args[1].toString();
}
Dans ce cas, devrez-vous exprimer ou interpréter les arguments en conséquence?.
C'est un point très sensible de comprendre comment travailler avec System.out.print . Si le premier élément est String, l'opérateur plus (+) fonctionne comme un opérateur String concate. Si le premier élément est un entier plus, l'opérateur (+) fonctionne comme un opérateur mathématique.
public static void main(String args[]) {
System.out.println("String" + 8 + 8); //String88
System.out.println(8 + 8+ "String"); //16String
}
Évidemment, le compilateur a été conçu de manière à créer de la confusion, bien que les développeurs du compilateur aient pensé ajouter une certaine intelligence. Le véritable atout qu'ils devraient vraiment ajouter est de rechercher l'argument entier et d'interpréter l'opérateur + de manière cohérente. Par exemple, System.out.println(1+2+"hello"+3+4);
doit générer 3hello7
au lieu de 3hello34
Les scénarios que vous avez mentionnés ne concernent pas la surcharge, vous concaténez différentes variables avec une chaîne.
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);
dans tous ces cas, vous appelez seulement print (String s), car lorsque quelque chose est concaténé avec une chaîne, il est converti en String en appelant le toString () de cet objet et les primitives sont directement concaténées . Cependant, si vous voulez connaître différentes signatures, alors oui print () est surchargé pour différents arguments.
Tout sur Surcharge de méthode .
Il existe des méthodes individuelles pour chaque type de données dans méthode println ()
Si vous passez objet:
Imprime un objet, puis termine la ligne. Cette méthode appelle d'abord String.valueOf (x) pour obtenir la valeur de chaîne de l'objet imprimé, puis se comporte comme si elle invoquait print (String) puis println ().
Si vous passez le type primitif:
appels de méthode de type primitif correspondants
si vous passez String:
appels de méthode println (String x) correspondants
Je pense que vous êtes confus avec la méthode printf(String format, Object... args)
. Le premier argument est la chaîne de format, qui est obligatoire, mais vous pouvez passer un nombre arbitraire de Object
s.
Il n'y a pas de surcharge de ce type pour les méthodes print()
et println()
.
@ikis, premièrement comme @Devolus a dit que ce ne sont pas de multiples aruements passés à print()
. En effet, tous ces arguments passés sont concaténés Pour former une seule chaîne. Donc, print()
ne contient pas plusieurs arguments (a. K. A. Var-args). Maintenant, le concept qui reste à discuter est la façon dont print()
imprime n'importe quel type de l'argument transmis.
Pour expliquer cela - toString()
est le secret:
System
est une classe, avec un champ statique out
, de type PrintStream
. Donc, vous appelez la méthode println(Object x)
de PrintStream
.
Il est implémenté comme ceci:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
Comme nous le voyons, elle appelle la méthode String.valueOf (Object). Ceci est implémenté comme suit:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Et ici vous voyez, cette toString()
est appelée.
Ainsi, tout ce qui est renvoyé par la méthode toString()
de cette classe est imprimé.
Et comme nous le savons, la toString()
est dans la classe Object
et hérite donc d’une implémentation par défaut de Object.
ex: Rappelez-vous que lorsque nous avons une classe dont toString()
est substituée, puis que nous passons cette variable de référence à print
, que voyez-vous imprimer? - C'est ce que nous revenons de la toString()
.
Vous pouvez convertir n'importe quoi en chaîne tant que vous choisissez ce que vous souhaitez imprimer. L'exigence était assez simple puisque Objet.toString()
peut renvoyer une chaîne muette par défaut: package.classname + @ + object number
.
Si votre méthode d'impression doit renvoyer une sérialisation XML ou JSON, le résultat de base de toString () ne serait pas acceptable. Même si la méthode réussit.
Voici un exemple simple pour montrer que Java peut être muet
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");
va imprimer quelque chose package.Mocktest@3254487
! Même si vous n’avez que deux membres String et que cela pourrait être implémenté pour imprimer
Mocktest@3254487{"field1":"a","field2":"b"}
(ou à peu près comment il apparaît dans le débogueur)