Respostas:
No seu exemplo, presumo que nãostr
seja usado fora do loop, caso contrário você não faria a pergunta, porque declará-lo dentro do loop não seria uma opção, pois não seria compilado.while
while
Portanto, como nãostr
é usado fora do loop, o menor escopo possível é dentro do loop while.str
Portanto, a resposta é enfaticamente que str
absolutamente deve ser declarada dentro do loop while. Sem ifs, nem ands, sem mas.
O único caso em que essa regra pode ser violada é se, por algum motivo, é de vital importância que todo ciclo de clock seja retirado do código; nesse caso, convém instanciar algo em um escopo externo e reutilizá-lo em vez de re-instanciando-o em todas as iterações de um escopo interno. No entanto, isso não se aplica ao seu exemplo, devido à imutabilidade de strings em java: uma nova instância de str sempre será criada no início do seu loop e terá que ser descartada no final dele. não há possibilidade de otimizar lá.
EDIT: (injetando meu comentário abaixo na resposta)
De qualquer forma, a maneira certa de fazer as coisas é escrever todo o seu código corretamente, estabelecer um requisito de desempenho para o seu produto, medir o seu produto final em relação a esse requisito e, se ele não o atender, otimizar as coisas. E o que geralmente acaba acontecendo é que você encontra maneiras de fornecer algumas otimizações algorítmicas agradáveis e formais em apenas alguns lugares que fazem com que nosso programa atenda aos requisitos de desempenho em vez de precisar percorrer toda a sua base de códigos e ajustar e hackear as coisas. para apertar os ciclos do relógio aqui e ali.
Comparei o código de bytes desses dois exemplos (semelhantes):
Vejamos o 1. exemplo :
package inside;
public class Test {
public static void main(String[] args) {
while(true){
String str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
depois javac Test.java
, javap -c Test
você receberá:
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
}
Vejamos 2. exemplo :
package outside;
public class Test {
public static void main(String[] args) {
String str;
while(true){
str = String.valueOf(System.currentTimeMillis());
System.out.println(str);
}
}
}
depois javac Test.java
, javap -c Test
você receberá:
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
}
As observações mostram que não há diferença entre esses dois exemplos. É o resultado das especificações da JVM ...
Porém, em nome da melhor prática de codificação, é recomendável declarar a variável no menor escopo possível (neste exemplo, ela está dentro do loop, pois esse é o único local em que a variável é usada).
final
amantes: declarando str
como final
no inside
caso do pacote também faz diferença =)
Declarar objetos no menor escopo melhora a legibilidade .
O desempenho não importa para os compiladores de hoje. (Neste cenário)
Da perspectiva da manutenção, a segunda opção é melhor.
Declare e inicialize variáveis no mesmo local, no escopo mais restrito possível.
Como Donald Ervin Knuth disse:
"Devemos esquecer pequenas eficiências, digamos, 97% das vezes: a otimização prematura é a raiz de todo mal"
ie) situação em que um programador permite que considerações de desempenho afetem o design de um pedaço de código. Isso pode resultar em um design que não é tão limpo quanto poderia ter sido ou um código incorreto, porque o código é complicado pela otimização e o programador é distraído pela otimização .
Pule para a resposta atualizada ...
Para quem se preocupa com o desempenho, retire o System.out e limite o loop a 1 byte. Usando double (teste 1/2) e usando String (3/4), os tempos decorridos em milissegundos são fornecidos abaixo no Windows 7 Professional de 64 bits e no JDK-1.7.0_21. Os bytecodes (também fornecidos abaixo para test1 e test2) não são os mesmos. Eu estava com preguiça de testar com objetos mutáveis e relativamente complexos.
Duplo
Teste1 realizado: 2710 ms
Teste2 realizado: 2790 ms
String (basta substituir double por string nos testes)
Teste 3 realizado: 1200 ms
Teste4 realizado: 3000 ms
Compilando e obtendo 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
}
Realmente não é fácil comparar o desempenho com todas as otimizações da JVM. No entanto, é um pouco possível. Melhores testes e resultados detalhados no Google Caliper
Isso não é idêntico ao código acima. Se você apenas codificar um loop fictício, a JVM o ignora, pelo menos você precisa atribuir e retornar algo. Isso também é recomendado na documentação do 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;
}
Resumo: declaradoAnterior indica melhor desempenho - muito pequeno - e é contra o menor princípio de escopo. A JVM realmente deve fazer isso por você
Uma solução para esse problema pode ser fornecer um escopo variável que encapsule o loop while:
{
// all tmp loop variables here ....
// ....
String str;
while(condition){
str = calculateStr();
.....
}
}
Eles seriam automaticamente referenciados quando o escopo externo terminar.
Se você não precisar usar o str
loop after the while (relacionado ao escopo), a segunda condição, ou seja,
while(condition){
String str = calculateStr();
.....
}
é melhor, pois se você definir um objeto na pilha somente se condition
for verdadeiro. Ou seja, use-o se você precisar
Penso que o melhor recurso para responder à sua pergunta seria o seguinte post:
Diferença entre declarar variáveis antes ou em loop?
De acordo com meu entendimento, isso dependeria da linguagem. O Java IIRC otimiza isso, para que não haja diferença, mas o JavaScript (por exemplo) fará toda a alocação de memória a cada vez no loop.
Como muitas pessoas apontaram,
String str;
while(condition){
str = calculateStr();
.....
}
NÃO é melhor que isso:
while(condition){
String str = calculateStr();
.....
}
Portanto, não declare variáveis fora de seu escopo se você não estiver reutilizando ...
A declaração String str fora do loop wile permite que ela seja referenciada dentro e fora do loop while. A declaração String str dentro do loop while permite que ela seja referenciada apenas dentro do loop while.
As variáveis devem ser declaradas o mais próximo possível de onde são usadas.
Isso facilita o RAII (Aquisição de recursos é inicializada) .
Mantém o escopo da variável restrito. Isso permite que o otimizador funcione melhor.
De acordo com o guia de desenvolvimento do Google Android, o escopo da variável deve ser limitado. Por favor, verifique este link:
A str
variável estará disponível e reservará algum espaço na memória, mesmo depois de executada abaixo do código.
String str;
while(condition){
str = calculateStr();
.....
}
A str
variável não estará disponível e também será liberada a memória que foi alocada para a str
variável no código abaixo.
while(condition){
String str = calculateStr();
.....
}
Se seguimos o segundo, certamente isso reduzirá a memória do sistema e aumentará o desempenho.
Na verdade, a pergunta acima é uma questão de programação. Como você gostaria de programar seu código? Onde você precisa que o 'STR' seja acessado? Não há como declarar uma variável que é usada localmente como uma variável global. Noções básicas de programação, acredito.
Aviso para quase todo mundo nesta pergunta: Aqui está um código de exemplo onde, dentro do loop, pode ser facilmente 200 vezes mais lento no meu computador com Java 7 (e o consumo de memória também é um pouco diferente). Mas é sobre alocação e não apenas escopo.
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);
}
}
Conclusão: Dependendo do tamanho da variável local, a diferença pode ser enorme, mesmo com variáveis não tão grandes.
Só para dizer que, às vezes, fora ou dentro do loop importa.
bigStuff[(int) (value % STUFF_SIZE)] = value;
(tente um valor de 2147483649L)
Eu acho que o tamanho do objeto também importa. Em um dos meus projetos, declaramos e inicializamos uma grande matriz bidimensional que estava fazendo com que o aplicativo lançasse uma exceção de falta de memória. Em vez disso, removemos a declaração do loop e limpamos a matriz no início de cada iteração.
Você corre o risco de NullPointerException
se seu calculateStr()
método retornar nulo e tentar chamar um método em str.
De maneira mais geral, evite ter variáveis com um valor nulo . A propósito, é mais forte para atributos de classe.
NullPointerException.
Se este código tentou return str;
seria encontrar um erro de compilação.