Dichiarazione di variabili all'interno o all'esterno di un ciclo


236

Perché il seguente funziona bene?

String str;
while (condition) {
    str = calculateStr();
    .....
}

Ma questo si dice che sia pericoloso / errato:

while (condition) {
    String str = calculateStr();
    .....
}

È necessario dichiarare le variabili al di fuori del ciclo?

Risposte:


289

L'ambito delle variabili locali dovrebbe essere sempre il più piccolo possibile.

Nel tuo esempio presumo strè non utilizzato al di fuori del whileciclo, altrimenti non sarebbe chiedere la domanda, perché dichiarando all'interno delwhile ciclo non sarebbe un'opzione, in quanto non sarebbe la compilazione.

Pertanto, poiché nonstr viene utilizzato al di fuori del ciclo, l'ambito più piccolo possibile è nel ciclo while.str

Quindi, la risposta è decisamente che strdovrebbe assolutamente essere dichiarata nel ciclo while. Nessun se, nessun e, nessun ma.

L'unico caso in cui questa regola potrebbe essere violata è se per qualche motivo è di vitale importanza che ogni ciclo di clock debba essere eliminato dal codice, nel qual caso potresti voler considerare l'istanza di qualcosa in un ambito esterno e riutilizzarla invece di ri-istanziandolo su ogni iterazione di un ambito interno. Tuttavia, questo non si applica al tuo esempio, a causa dell'immutabilità delle stringhe in Java: una nuova istanza di str verrà sempre creata all'inizio del tuo loop e dovrà essere gettata via alla fine di esso, quindi lì non c'è possibilità di ottimizzare lì.

EDIT: (iniettando il mio commento qui sotto nella risposta)

In ogni caso, il modo giusto di fare le cose è scrivere tutto il tuo codice in modo corretto, stabilire un requisito prestazionale per il tuo prodotto, misurare il tuo prodotto finale rispetto a questo requisito e, se non lo soddisfa, quindi andare a ottimizzare le cose. E ciò che di solito accade è che trovi modi per fornire alcune ottimizzazioni algoritmiche belle e formali in un paio di posti che fanno sì che il nostro programma soddisfi i suoi requisiti di prestazioni invece di dover andare su tutta la tua base di codice e modificare e hackerare le cose in per spremere i cicli di clock qua e là.


2
Interrogazione sull'ultimo paragrafo: se era un altro, allora String che non è immutabile, influenza?
Harry Joy,

1
@HarryJoy Sì, certo, ad esempio StringBuilder, che è modificabile. Se si utilizza StringBuilder per creare una nuova stringa in ogni iterazione del ciclo, è possibile ottimizzare le cose allocando StringBuilder all'esterno del ciclo. Tuttavia, questa non è una pratica consigliabile. Se lo fai senza un'ottima ragione, si tratta di un'ottimizzazione prematura.
Mike Nakis,

7
@HarryJoy Il modo giusto di fare le cose è scrivere tutto il tuo codice in modo corretto , stabilire un requisito prestazionale per il tuo prodotto, misurare il tuo prodotto finale rispetto a questo requisito, e se non lo soddisfa, vai a ottimizzare le cose. E tu sai cosa? Di solito sarai in grado di fornire alcune ottimizzazioni algoritmiche gradevoli e formali in un paio di posti che faranno il trucco invece di dover andare su tutta la tua base di codice e modificare e hackerare le cose per comprimere i cicli di clock qua e là.
Mike Nakis,

2
@MikeNakis, cosa che stai pensando in un ambito molto ristretto.
Siten,

5
Vedete, le moderne CPU multi-gigahertz, multi-core, pipeline, multi-livello-cache di memoria ci consentono di concentrarci sulle seguenti migliori pratiche senza doversi preoccupare dei cicli di clock. Inoltre, l'ottimizzazione è consigliabile solo se e solo se è stato stabilito che è necessario, e quando è necessario, un paio di modifiche altamente localizzate di solito raggiungeranno le prestazioni desiderate, quindi non è necessario sporcare tutto il nostro codice con piccoli hack in nome della performance.
Mike Nakis,

293

Ho confrontato il codice byte di quei due (simili) esempi:

Diamo un'occhiata a 1. esempio :

package inside;

public class Test {
    public static void main(String[] args) {
        while(true){
            String str = String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

dopo javac Test.java, javap -c Testotterrai:

public class inside.Test extends java.lang.Object{
public inside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

Diamo un'occhiata a 2. esempio :

package outside;

public class Test {
    public static void main(String[] args) {
        String str;
        while(true){
            str =  String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

dopo javac Test.java, javap -c Testotterrai:

public class outside.Test extends java.lang.Object{
public outside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

Le osservazioni mostrano che non vi è alcuna differenza tra questi due esempi. È il risultato delle specifiche JVM ...

Ma nel nome della migliore pratica di codifica si consiglia di dichiarare la variabile nel più piccolo ambito possibile (in questo esempio è all'interno del ciclo, poiché questo è l'unico posto in cui viene utilizzata la variabile).


3
È il risultato della Soecification di JVM, non dell'ottimizzazione del compilatore. Gli slot di stack richiesti da un metodo sono tutti allocati all'ingresso del metodo. Ecco come viene specificato il bytecode.
Marchese di Lorne,

2
@Arhimed c'è un motivo in più per inserirlo nel ciclo (o solo nel blocco '{}'): il compilatore riutilizzerà la memoria allocata nel frame dello stack per la variabile in un altro ambito se si dichiara in quell'altro ambito più che variabile .
Serge,

1
Se scorre in un elenco di oggetti di dati, farà differenza per la maggior parte dei dati? Probabilmente 40 mila.
Mithun Khatri il

7
Per qualcuno di voi finalamanti: dichiarando strcome finalnel insidecaso del pacchetto anche non fa differenza =)
skia.heliou

27

La dichiarazione di oggetti nel più piccolo ambito migliora la leggibilità .

Le prestazioni non contano per i compilatori di oggi. (In questo scenario)
Dal punto di vista della manutenzione, la seconda opzione è migliore.
Dichiarare e inizializzare le variabili nello stesso posto, nell'ambito più ristretto possibile.

Come diceva Donald Ervin Knuth :

"Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutti i mali"

cioè) situazione in cui un programmatore lascia che considerazioni sulle prestazioni influenzino la progettazione di un pezzo di codice. Questo può portare ad un disegno che è non pulite come avrebbe potuto essere o codice che non è corretto, perché il codice è complicata dalla ottimizzazione e il programmatore è distratto da ottimizzare .


1
"La seconda opzione ha prestazioni leggermente più veloci" => l'hai misurata? Secondo una delle risposte, il bytecode è lo stesso, quindi non vedo come le prestazioni potrebbero essere diverse.
Assylias,

Mi dispiace ma non è proprio il modo giusto per testare le prestazioni di un programma java (e come puoi comunque testare le prestazioni di un ciclo infinito?)
assylias,

Sono d'accordo con gli altri tuoi punti - è solo che credo che non ci siano differenze di prestazioni.
Assylias,

11

se si desidera utilizzare stranche looop esterno; dichiaralo fuori. in caso contrario, la seconda versione va bene.


11

Passa alla risposta aggiornata ...

Per coloro che si preoccupano delle prestazioni, estrarre System.out e limitare il loop a 1 byte. Utilizzando double (test 1/2) e String (3/4) i tempi trascorsi in millisecondi sono indicati di seguito con Windows 7 Professional 64 bit e JDK-1.7.0_21. I bytecode (indicati anche di seguito per test1 e test2) non sono gli stessi. Ero troppo pigro per provare con oggetti mutabili e relativamente complessi.

Doppio

Test1 ha impiegato: 2710 msec

Test2 ha impiegato: 2790 msec

String (basta sostituire double con string nei test)

Test3 ha richiesto: 1200 msecs

Test4 ha richiesto: 3000 msec

Compilazione e ottenere bytecode

javac.exe LocalTest1.java

javap.exe -c LocalTest1 > LocalTest1.bc


public class LocalTest1 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        double test;
        for (double i = 0; i < 1000000000; i++) {
            test = i;
        }
        long finish = System.currentTimeMillis();
        System.out.println("Test1 Took: " + (finish - start) + " msecs");
    }

}

public class LocalTest2 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        for (double i = 0; i < 1000000000; i++) {
            double test = i;
        }
        long finish = System.currentTimeMillis();
        System.out.println("Test1 Took: " + (finish - start) + " msecs");
    }
}


Compiled from "LocalTest1.java"
public class LocalTest1 {
  public LocalTest1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: lstore_1
       4: dconst_0
       5: dstore        5
       7: dload         5
       9: ldc2_w        #3                  // double 1.0E9d
      12: dcmpg
      13: ifge          28
      16: dload         5
      18: dstore_3
      19: dload         5
      21: dconst_1
      22: dadd
      23: dstore        5
      25: goto          7
      28: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
      31: lstore        5
      33: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      36: new           #6                  // class java/lang/StringBuilder
      39: dup
      40: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      43: ldc           #8                  // String Test1 Took:
      45: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      48: lload         5
      50: lload_1
      51: lsub
      52: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      55: ldc           #11                 // String  msecs
      57: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      60: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      63: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      66: return
}


Compiled from "LocalTest2.java"
public class LocalTest2 {
  public LocalTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: lstore_1
       4: dconst_0
       5: dstore_3
       6: dload_3
       7: ldc2_w        #3                  // double 1.0E9d
      10: dcmpg
      11: ifge          24
      14: dload_3
      15: dstore        5
      17: dload_3
      18: dconst_1
      19: dadd
      20: dstore_3
      21: goto          6
      24: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
      27: lstore_3
      28: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: new           #6                  // class java/lang/StringBuilder
      34: dup
      35: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      38: ldc           #8                  // String Test1 Took:
      40: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: lload_3
      44: lload_1
      45: lsub
      46: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      49: ldc           #11                 // String  msecs
      51: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      54: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      57: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      60: return
}

RISPOSTA AGGIORNATA

Non è davvero facile confrontare le prestazioni con tutte le ottimizzazioni JVM. Tuttavia, è in qualche modo possibile. Test migliori e risultati dettagliati in Google Caliper

  1. Alcuni dettagli sul blog: dovresti dichiarare una variabile all'interno di un ciclo o prima del ciclo?
  2. Repository GitHub: https://github.com/gunduru/jvdt
  3. Risultati del test per doppio caso e loop 100M (e sì tutti i dettagli JVM): https://microbenchmarks.appspot.com/runs/b1cef8d1-0e2c-4120-be61-a99faff625b4

Dichiarato prima di 1.759.209 Dichiarato all'interno di 2.242.308

  • Dichiarato prima di 1.759.209 ns
  • Dichiarato all'interno 2.242.308 n

Codice di prova parziale per doppia dichiarazione

Questo non è identico al codice sopra. Se semplicemente codifichi un loop fittizio, JVM lo salta, quindi almeno devi assegnare e restituire qualcosa. Questo è consigliato anche nella documentazione di Caliper.

@Param int size; // Set automatically by framework, provided in the Main
/**
* Variable is declared inside the loop.
*
* @param reps
* @return
*/
public double timeDeclaredInside(int reps) {
    /* Dummy variable needed to workaround smart JVM */
    double dummy = 0;

    /* Test loop */
    for (double i = 0; i <= size; i++) {

        /* Declaration and assignment */
        double test = i;

        /* Dummy assignment to fake JVM */
        if(i == size) {
            dummy = test;
        }
    }
    return dummy;
}

/**
* Variable is declared before the loop.
*
* @param reps
* @return
*/
public double timeDeclaredBefore(int reps) {

    /* Dummy variable needed to workaround smart JVM */
    double dummy = 0;

    /* Actual test variable */
    double test = 0;

    /* Test loop */
    for (double i = 0; i <= size; i++) {

        /* Assignment */
        test = i;

        /* Not actually needed here, but we need consistent performance results */
        if(i == size) {
            dummy = test;
        }
    }
    return dummy;
}

Riepilogo: dichiarato Prima indica prestazioni migliori - davvero minuscole - ed è contro il più piccolo principio di portata. JVM dovrebbe effettivamente farlo per te


Metodologia di test non valida e non fornisci alcuna spiegazione dei risultati.
Marchese di Lorne,

1
@EJP Questo dovrebbe essere abbastanza chiaro per coloro che sono interessati all'argomento. La metodologia è presa dalla risposta di PrimosK per fornire informazioni più utili. Ad essere sincero, non ho idea di come migliorare questa risposta, forse puoi fare clic su Modifica e mostrarci come farlo correttamente?
Onur Günduru,

2
1) Java Bytecode viene ottimizzato (riordinato, compresso, ecc.) In fase di esecuzione, quindi non preoccuparti troppo di ciò che è scritto nei file .class. 2) ci sono 1.000.000.000 di tiri per ottenere una vittoria delle prestazioni di 2,8 secondi, quindi circa 2,8 ns per corsa rispetto a uno stile di programmazione sicuro e corretto. Un chiaro vincitore per me. 3) Poiché non fornisci informazioni sul riscaldamento, i tuoi tempi sono abbastanza inutili.
Hardcoded

@Hardcoded test migliori / micro benchmarking con pinza solo per loop doppi e 100M. Risultati online, se desideri che altri casi siano liberi di modificare.
Onur Günduru,

Grazie, questo elimina i punti 1) e 3). Ma anche se il tempo è salito a ~ 5 ns per ciclo, questo è ancora un momento da ignorare. In teoria c'è un piccolo potenziale di ottimizzazione, in realtà le cose che stai facendo per ciclo di solito sono molto più costose. Quindi il potenziale sarebbe di pochi secondi al massimo in una corsa di alcuni minuti o addirittura ore. Esistono altre opzioni con un potenziale più elevato disponibile (ad esempio Fork / Join, Flussi paralleli) che verificherei prima di dedicare tempo a questo tipo di ottimizzazioni di basso livello.
Hardcoded

7

Una soluzione a questo problema potrebbe essere quella di fornire un ambito variabile che incapsuli il ciclo while:

{
  // all tmp loop variables here ....
  // ....
  String str;
  while(condition){
      str = calculateStr();
      .....
  }
}

Saranno automaticamente de-referenziati al termine dell'oscilloscopio esterno.


6

All'interno, minore è la portata della variabile, meglio è.


5

Se non è necessario utilizzare il strciclo after the while (relativo all'ambito), quindi la seconda condizione, ad es

  while(condition){
        String str = calculateStr();
        .....
    }

è meglio se si definisce un oggetto nello stack solo se conditionè vero. Lo uso se ne hai bisogno


2
Si noti che anche nella prima variante, nessun oggetto viene costruito se la condizione è falsa.
Philipp Wendler,

@ Phillip: Sì, hai ragione. Colpa mia. Stavo pensando com'è adesso. Cosa ne pensi?
Cratylus,

1
Bene, "definire un oggetto nello stack" è un termine alquanto strano nel mondo Java. Inoltre, allocare una variabile nello stack è di solito un noop in fase di esecuzione, quindi perché preoccuparsi? Scoping per aiutare il programmatore è il vero problema.
Philipp Wendler,

3

Penso che la migliore risorsa per rispondere alla tua domanda sia il seguente post:

Differenza tra la dichiarazione di variabili prima o in loop?

Secondo la mia comprensione questa cosa sarebbe dipendente dalla lingua. IIRC Java lo ottimizza, quindi non c'è alcuna differenza, ma JavaScript (ad esempio) eseguirà l'intera allocazione di memoria ogni volta nel ciclo. In Java, in particolare, penso che il secondo sarebbe più veloce quando si eseguiva il profiling.


3

Come molte persone hanno sottolineato,

String str;
while(condition){
    str = calculateStr();
    .....
}

NON è meglio di questo:

while(condition){
    String str = calculateStr();
    .....
}

Quindi non dichiarare le variabili al di fuori dei loro ambiti se non le stai riutilizzando ...


1
tranne probabilmente in questo modo: link
Dainius Kreivys

2

La dichiarazione di String str al di fuori del ciclo wile consente di fare riferimento all'interno e all'esterno del ciclo while. Dichiarare String str all'interno del ciclo while consente di fare riferimento solo al ciclo while.


1

Le variabili devono essere dichiarate il più vicino possibile al luogo di utilizzo.

Rende più semplice RAII (Resource Acquisition Is Initialization) .

Mantiene stretto l'ambito della variabile. Ciò consente all'ottimizzatore di funzionare meglio.



1

La strvariabile sarà disponibile e riserverà un po 'di spazio in memoria anche dopo essere stata eseguita sotto il codice.

 String str;
    while(condition){
        str = calculateStr();
        .....
    }

La strvariabile non sarà disponibile e anche la memoria verrà rilasciata che è stata allocata per la strvariabile nel codice seguente.

while(condition){
    String str = calculateStr();
    .....
}

Se seguissimo sicuramente il secondo, ciò ridurrebbe la memoria del nostro sistema e aumenterà le prestazioni.


0

La dichiarazione all'interno del loop limita l'ambito della rispettiva variabile. Tutto dipende dalle esigenze del progetto dall'ambito della variabile.


0

In verità, la domanda sopra esposta è un problema di programmazione. Come vorresti programmare il tuo codice? Dove è necessario accedere a "STR"? Non è possibile dichiarare una variabile utilizzata localmente come variabile globale. Nozioni di base di programmazione credo.


-1

Questi due esempi danno come risultato la stessa cosa. Tuttavia, il primo ti fornisce l'uso della strvariabile al di fuori del ciclo while; il secondo no.


-1

Avviso per quasi tutti in questa domanda: ecco un codice di esempio in cui all'interno del ciclo può essere facilmente 200 volte più lento sul mio computer con Java 7 (e anche il consumo di memoria è leggermente diverso). Ma si tratta di allocazione e non solo portata.

public class Test
{
    private final static int STUFF_SIZE = 512;
    private final static long LOOP = 10000000l;

    private static class Foo
    {
        private long[] bigStuff = new long[STUFF_SIZE];

        public Foo(long value)
        {
            setValue(value);
        }

        public void setValue(long value)
        {
            // Putting value in a random place.
            bigStuff[(int) (value % STUFF_SIZE)] = value;
        }

        public long getValue()
        {
            // Retrieving whatever value.
            return bigStuff[STUFF_SIZE / 2];
        }
    }

    public static long test1()
    {
        long total = 0;

        for (long i = 0; i < LOOP; i++)
        {
            Foo foo = new Foo(i);
            total += foo.getValue();
        }

        return total;
    }

    public static long test2()
    {
        long total = 0;

        Foo foo = new Foo(0);
        for (long i = 0; i < LOOP; i++)
        {
            foo.setValue(i);
            total += foo.getValue();
        }

        return total;
    }

    public static void main(String[] args)
    {
        long start;

        start = System.currentTimeMillis();
        test1();
        System.out.println(System.currentTimeMillis() - start);

        start = System.currentTimeMillis();
        test2();
        System.out.println(System.currentTimeMillis() - start);
    }
}

Conclusione: a seconda della dimensione della variabile locale, la differenza può essere enorme, anche con variabili non così grandi.

Solo per dire che a volte, al di fuori o all'interno del ciclo è importante.


1
Certo, il secondo è più veloce, ma stai facendo cose diverse: test1 sta creando molti Foo-Objects con grandi array, test2 no. test2 sta riutilizzando ripetutamente lo stesso oggetto Foo, il che potrebbe essere pericoloso in ambienti multithread.
Hardcoded

Pericoloso in ambiente multithread ??? Per favore, spiega perché. Stiamo parlando di una variabile locale. Viene creato ad ogni chiamata del metodo.
RT15,

Se si passa l'oggetto Foo a un'operazione che elabora i dati in modo asincrono, l'operazione potrebbe continuare a funzionare sull'istanza Foo mentre si stanno modificando i dati al suo interno. Non è nemmeno necessario essere multithread per avere effetti collaterali. Quindi il riutilizzo delle istanze è abbastanza pericoloso, quando non sai chi sta ancora usando l'istanza
Hardcoded

Ps: il tuo metodo setValue dovrebbe essere bigStuff[(int) (value % STUFF_SIZE)] = value;(prova un valore di 2147483649L)
Hardcoded

Parlando di effetti collaterali: hai confrontato i risultati dei tuoi metodi?
Hardcoded

-1

Penso che contino anche le dimensioni dell'oggetto. In uno dei miei progetti, avevamo dichiarato e inizializzato un grande array bidimensionale che stava facendo sì che l'applicazione generasse un'eccezione di memoria insufficiente. Abbiamo invece spostato la dichiarazione fuori dal ciclo e cancellato l'array all'inizio di ogni iterazione.


-2

Si rischia NullPointerExceptionse il calculateStr()metodo restituisce null e quindi si tenta di chiamare un metodo su str.

Più in generale, evitare di avere variabili con un valore nullo . A proposito, è più forte per gli attributi di classe.


2
Questo non è in alcun modo correlato alla domanda. La probabilità di NullPointerException (su future chiamate di funzione) non dipende da come viene dichiarata una variabile.
Desert Ice,

1
Non credo, perché la domanda è "Qual è il modo migliore per farlo?". IMHO Preferirei un codice più sicuro.
Rémi Doolaeghe,

1
Non vi è alcun rischio di a NullPointerException.Se questo codice tentasse di return str;riscontrare un errore di compilazione.
Marchese di Lorne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.