MindView Inc.

Pensando em Java, 3ª ed. Revisão 4.0


[ Viewing Hints ] [ Book Home Page ] [ Free Newsletter ]
[ Seminars ] [ Seminars on CD ROM ] [ Consulting ]

Anterior Próximo Página Inicial Índice Conteúdo

3: Controlando o Fluxo do Programa



Tal como uma criatura consciente, um programa deve manipular seu mundo e fazer escolhas durante a execução.



Em Java você manipula dados utilizando operadores, fazendo escolhas com comandos de controle de execução. Java foi herdado do C++, então a maioria destes comandos e operadores irão ser familiar para os programadores C e C++. Java também adicionou algumas melhorias e simplificações. Comentários (em inglês)



Se você se encontrar patinando um pouco neste capítulo, certifique-se de explorar o CD ROM multimedia contido neste livro: Foundations for Java. Contem palestras em audio, slides, exercícios, e soluções especificamente projetadas para torná-lo fluente nos fundamentos necessários para aprender Java.Comentários (em inglês)

Usando operadores Java



Um operador pega um ou mais argumentos e produz um novo valor. Os argumentos são de diferente forma do que uma chamada ordinaria a um método, mas efetivamente são os mesmos. Adição (+), subtração e menos unário (-), multiplicação (*), divisão (/), e atribuição (=) todos trabalham da mesma forma em que qualquer linguagem. Comentários (em inglês)



Todos os operadores produzem um valor de seus operandos. No entanto, um operador pode modificar o valor de um operando. Isto é chamado de efeito colateral. O uso mais comun para operadores que modificam seus operandos é para geração de efeito colateral, mas você deve manter em mente que o valor produzido é disponível para o seu uso, como nos operadores sem efeitos colaterais. Comentários (em inglês)



Na maioria das vezes os operadores somente trabalham com primitivas. As exceções são para ‘=’, ‘==’ e ‘!=’, que trabalham com todos os objetos (e são um ponto de confusão para objetos). Também, a classe String suporta ‘+’ e ‘+=’. Comentários (em inglês)

Precedência



A precedência de operador define como uma expressão é avaliada quando existem diversos operadores. Java tem as regras específicas que determinam a ordem da avaliação. O mais fácil de lembrar é que a multiplicação e a divisão acontecem antes da adição e da subtração. Os programadores esquecem-se freqüentemente das outras regras de precedência, assim você deve usar parênteses para tornar a ordem da avaliação explícita. Por examplo: Comentários (em inglês)



a = x + y - 2/2 + z;




tem um significado muito diferente da mesma expressão com um agrupamento específico de parênteses: Comentários (em inglês)



a = x + (y - 2)/(2 + z);



Atribuição



A atribuição é executada com o operador =. Significa "pegue o valor do lado direito (chamado frequentemente de rvalue) e copie para dentro do lado esquerdo (também chamado delvalue).” Um rvalue é qualquer constante, variável, ou expressão que pode produzir um valor, mas um lvalue deve ser uma variável nomeada única. (Isto é, deve haver um espaço físico para armazenar o valor.) Por exemplo, você pode atribuir um valor constante a uma variável:



a = 4;




mas você não pode associar nada à uma constante - ela não pode ser um lvalue. (Você não pode usar a atribuição 4 = a;.) Comentários (em inglês)



A atribuição de primitivas é bem direta. Como a primitiva contém o valor real e não uma referência a um objeto, quando você atribui ou associa primitivas, você copia o conteúdo de um lugar para outro. Por exemplo, se você diz que a = b para as primitivas, então o conteúdo de b é copiado para a. Se depois você modifica a, b naturalmente não é afetado por esta modificação. Como programador, isso é o que você espera na maioria das situações. Comentários (em inglês)



Entretanto, quando você atribui objetos, as coisas mudam. Sempre que você manipula um objeto, o que você está manipulando é a referência. Por isso, quando você atribui "de um objeto para outro," você está na verdade copiando uma referência de um lugar para outro. Isto significa que quando você diz que c = d para objetos, você ficará com ambos c e d apontando para o objeto que, originalmente, apenas d apontava. Segue um exemplo que demostra este comportamento: Comentários (em inglês)



//: c03:Assignment.java
// Atribuição entre objetos é um pequeno truque.
import com.bruceeckel.simpletest.*;

class Number {
  int i;
}

public class Assignment {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    n1.i = 9;
    n2.i = 47;
    System.out.println("1: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
    n1 = n2;
    System.out.println("2: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
    n1.i = 27;
    System.out.println("3: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
    monitor.expect(new String[] {
      "1: n1.i: 9, n2.i: 47",
      "2: n1.i: 47, n2.i: 47",
      "3: n1.i: 27, n2.i: 27"
    });
  }
} ///:~




Primeiro, note que alguma coisa nova tem sido adicionada. A linha:



import com.bruceeckel.simpletest.*;




importa a biblioteca “simpletest” que foi criada para testar o código neste livro e que é explicada no Capítulo 15. No início da classe de Atribuição você vê a linha:



  static Test monitor = new Test();




Isso cria uma instância da classe Test do pacote simpletest, chamada monitor.Finalmente, ao final do main( ), vê-se o comando:. Finally, at the end of main( ), you see the statement:



    monitor.expect(new String[] {
      "1: n1.i: 9, n2.i: 47",
      "2: n1.i: 47, n2.i: 47",
      "3: n1.i: 27, n2.i: 27"
    });




Este é o resultado esperado do programa, expressado como um array de objetos String. Quando o programa é executado, ele não apenas imprime o resultado, mas o compara com esse array para verificar se o array está correto. Logo, quando você vir um programa neste livro que use o pacote simpletest, você verá também uma chamada expect( ) que lhe mostrará qual é a saída do programa. Assim, você vê resultados validados pelo programa.



A classe Number é simples, e duas instâncias suas (n1 e n2) são criadas no main( ). À variável membro i de cada instância de Number é dado um valor diferente, e em seguida n2 é atribuída a n1, e n1 é alterada. Em muitas linguagens de programação esperar-se-ia que n1 e n2 fossem independentes a todo tempo, mas pelo fato de você ter atribuído uma referência, verá o resultado dado pelo comando expect( ). Alterar o objeto n1 parece alterar n2 também! Isto acontece porque n1 e n2 contêm a mesma referência, que aponta para o mesmo objeto (A referência original contida em n1, que apontava para o objeto encapsulando o valor 9, foi substituída durante a atribuição e efetivamente perdida; o objeto ao qual apontava será coletado pelo garbage collector).Comentários (em inglês)



Esse fenômeno é frequentemente chamado de aliasing, e é uma maneira fundamental com a qual Java trabalha com objetos. Mas e se você não quiser o aliasing neste caso? Você poderia reescrever a atribuição da seguinte forma: Comentários (em inglês)



n1.i = n2.i;




This retains the two separate objects instead of tossing one and tying n1 and n2 to the same object, but you’ll soon realize that manipulating the fields within objects is messy and goes against good object-oriented design principles. This is a nontrivial topic, so it is left for Appendix A, which is devoted to aliasing. In the meantime, you should keep in mind that assignment for objects can add surprises. Comentários (em inglês)

Aliasing during method calls



Aliasing will also occur when you pass an object into a method:



//: c03:PassObject.java
// Passing objects to methods may not be what
// you're used to.
import com.bruceeckel.simpletest.*;

class Letter {
  char c;
}

public class PassObject {
  static Test monitor = new Test();
  static void f(Letter y) {
    y.c = 'z';
  }
  public static void main(String[] args) {
    Letter x = new Letter();
    x.c = 'a';
    System.out.println("1: x.c: " + x.c);
    f(x);
    System.out.println("2: x.c: " + x.c);
    monitor.expect(new String[] {
      "1: x.c: a",
      "2: x.c: z"
    });
  }
} ///:~




Em muitas linguagens de programação, o método f( ) pode parecer estar fazendo uma cópia do argumento Letter y dentro do escopo do método. Mas, mais uma vez, uma referência está sendo passada, assim a linha Comentários (em inglês)



y.c = 'z';




está na verdade alterando o objeto também para o escopo externo a f( ). O resultado no comando expect( ) mostra isso.Comentários (em inglês)



Aliasing and its solution is a complex issue and, although you must wait until Appendix A for all the answers, you should be aware of it at this point so you can watch for pitfalls. Comentários (em inglês)

Mathematical operators



Os operadores matemáticos básicos são os mesmos que os disponíveis na maioria das linguagens de programação: adição (+), subtração (-), divisão (/), multiplicação (*) e módulo (%, o qual produz o resto da divisão por inteiro). A divisão por inteiro trunca o resultado ao invés de arredondá-lo.Comentários (em inglês)



Java também usa uma notação abreviada para realizar uma operação e uma associação ao mesmo tempo. Isto é simbolizado por um operador seguido por um sinal de igual e é consistente com todos os operadores na linguagem (sempre que fizer sentido). Por exemplo, para adicionar o valor 4 à variável x e guardar o resultado em x, use: x += 4. Comentários (em inglês)



Este exemplo mostra o uso de operadores matemáticos:



//: c03:MathOps.java
// Demonstrates the mathematical operators.
import com.bruceeckel.simpletest.*;
import java.util.*;

public class MathOps {
  static Test monitor = new Test();
  // Shorthand to print a string and an int:
  static void printInt(String s, int i) {
    System.out.println(s + " = " + i);
  }
  // Shorthand to print a string and a float:
  static void printFloat(String s, float f) {
    System.out.println(s + " = " + f);
  }
  public static void main(String[] args) {
    // Create a random number generator,
    // seeds with current time by default:
    Random rand = new Random();
    int i, j, k;
    // Choose value from 1 to 100:
    j = rand.nextInt(100) + 1;
    k = rand.nextInt(100) + 1;
    printInt("j", j);  printInt("k", k);
    i = j + k; printInt("j + k", i);
    i = j - k; printInt("j - k", i);
    i = k / j; printInt("k / j", i);
    i = k * j; printInt("k * j", i);
    i = k % j; printInt("k % j", i);
    j %= k; printInt("j %= k", j);
    // Floating-point number tests:
    float u,v,w;  // applies to doubles, too
    v = rand.nextFloat();
    w = rand.nextFloat();
    printFloat("v", v); printFloat("w", w);
    u = v + w; printFloat("v + w", u);
    u = v - w; printFloat("v - w", u);
    u = v * w; printFloat("v * w", u);
    u = v / w; printFloat("v / w", u);
    // the following also works for
    // char, byte, short, int, long,
    // and double:
    u += v; printFloat("u += v", u);
    u -= v; printFloat("u -= v", u);
    u *= v; printFloat("u *= v", u);
    u /= v; printFloat("u /= v", u);
    monitor.expect(new String[] {
      "%% j = -?\\d+",
      "%% k = -?\\d+",
      "%% j \\+ k = -?\\d+",
      "%% j - k = -?\\d+",
      "%% k / j = -?\\d+",
      "%% k \\* j = -?\\d+",
      "%% k % j = -?\\d+",
      "%% j %= k = -?\\d+",
      "%% v = -?\\d+\\.\\d+(E-?\\d)?",
      "%% w = -?\\d+\\.\\d+(E-?\\d)?",
      "%% v \\+ w = -?\\d+\\.\\d+(E-?\\d)??",
      "%% v - w = -?\\d+\\.\\d+(E-?\\d)??",
      "%% v \\* w = -?\\d+\\.\\d+(E-?\\d)??",
      "%% v / w = -?\\d+\\.\\d+(E-?\\d)??",
      "%% u \\+= v = -?\\d+\\.\\d+(E-?\\d)??",
      "%% u -= v = -?\\d+\\.\\d+(E-?\\d)??",
      "%% u \\*= v = -?\\d+\\.\\d+(E-?\\d)??",
      "%% u /= v = -?\\d+\\.\\d+(E-?\\d)??"
    });
  }
} ///:~




A primeira coisa que você verá são alguns métodos abreviados para impressão: o método printInt( ) imprime uma String seguida por um int e o método printFloat( ) imprime uma String seguida por um float. Comentários (em inglês)



Para gerar números, o programa primeiramente cria um objeto Random. Considerando que não há argumentos passados durante a criação, Java usa a hora corrente como entrada para o gerador de número randômico. O programa gera vários tipos diferentes de números randômicos com o objeto Random simplesmente chamando os métodos: nextInt( ) e nextFloat( ) (você também pode usar nextLong( ) ou nextDouble( )). Comentários (em inglês)



O operador “resto da divisão“, quando usado com o resultado do gerador de números randômicos, limita o resultado a um limite superior ao operando menos 1 (99 neste caso). Comentários (em inglês)

Expressões Regulares



Considerando que números randômicos são usados para gerar a saída para este programa, o comando expect( ) não pode mostrar somante saídas de literais como o fez anteriormente, levando em consideração que a saída variará de uma execução à outra. Para resolver este problema, expressões regulares, uma nova característica introduzida em Java JDK 1.4 (porém uma característica antiga em linguagens como Perl e Python) será utilizada dentro do comando expect( ). Embora a cobertura desta poderosa ferramenta não ocorra até o capítulo 12, para entender estes comandos você precisará de uma introdução à expressões regulares. Aqui você aprenderá o necessário para ler o comando expect( ), mas se você desejar uma descrição completa pesquise por java.util.regex.Pattern na documentação JDK, disponível para download. Comentários (em inglês)



Uma expressão regular é uma forma de descrever strings em termos gerais de modo que você possa dizer: “Se uma string tem estas coisas, então ela atende às minhas necessidades.” Por exemplo, para dizer que um número pode ou não ser precedido por um sinal de menos, você coloca o sinal de menos seguido por um ponto de interrogação, desta forma:Comentários (em inglês)



-?




To describe an integer, you say that it’s one or more digits. In regular expressions, a digit is ‘\d’, but in a Java String you have to “escape” the backslash by putting in a second backslash: ‘\\d’. To indicate “one or more of the preceding expression” in regular expressions, you use the ‘+’. So to say “possibly a minus sign, followed by one or more digits,” you write: Comentários (em inglês)



-?\\d+




O qual você pode ver nas primeiras linhas do comando expect( ) no código precedente.



Algo que não faz parte da sintaxe de expressões regulares é a notação ‘%%’ (perceba o espaço incluído para fins de legibilidade) no começo das linhas do comando expect( ) Esta é uma notação usada por simpletest para indicar que o resto da linha é uma expressão regular. Desta forma você não a verá em expressões regulares normais, apenas nos comandos simpletest expect( ) Comentários (em inglês)



Quaisquer outros caracteres que não forem considerados caracteres especiais para buscas a expressões regulares são utilizados como argumento de pesquisa exata. Desta forma na primeira linha:



%% j = -?\\d+




A seqüência ‘j = ’ é perfeitamente localizada. Entretanto, na terceira linha o sinal ‘+’ em ‘j + k’ deve ser tratado para ser reconhecido, porque ele é um caracter de expressão regular, assim como o ‘*’. O restante das linhas deveriam ser compreensíveis em função desta introdução. Mais adiante neste livro, quando características adicionais de expressões regulares forem utilizadas nos comandos expect( ), elas serão explicadas.Comentários (em inglês)

Operadores Unários Menos e Mais



O unário menos (-) e o unário mais(+) são os mesmos que os operadores binários menos e mais. O compilador descobre qual a utilização de acordo com a forma como você escreve a expressão. Por exemplo, o comando Comentários (em inglês)



x = -a;




tem um significado óbvio. O compilador é capaz de descobrir: Comentários (em inglês)



x = a * -b;




but the reader might get confused, so it is clearer to say: Comentários (em inglês)



x = a * (-b);




Unary minus inverts the sign on the data. Unary plus provides symmetry with unary minus, although it doesn’t have any effect. Comentários (em inglês)

Auto increment and decrement



Java, assim como C, é cheio de atalhos. Atalhos podem facilitar a digitação do código, e facilitar ou dificultar a leitura. Comentários (em inglês)



Two of the nicer shortcuts are the increment and decrement operators (often referred to as the auto-increment and auto-decrement operators). The decrement operator is -- and means “decrease by one unit.” The increment operator is ++ and means “increase by one unit.” If a is an int, for example, the expression ++a is equivalent to (a = a + 1). Increment and decrement operators not only modify the variable, but also produce the value of the variable as a result. Comentários (em inglês)



There are two versions of each type of operator, often called the prefix and postfix versions. Pre-increment means the ++ operator appears before the variable or expression, and post-increment means the ++ operator appears after the variable or expression. Similarly, pre-decrement means the -- operator appears before the variable or expression, and post-decrement means the -- operator appears after the variable or expression. For pre-increment and pre-decrement, (i.e., ++a or --a), the operation is performed and the value is produced. For post-increment and post-decrement (i.e. a++ or a--), the value is produced, then the operation is performed. As an example: Comentários (em inglês)



//: c03:AutoInc.java
// Demonstrates the ++ and -- operators.
import com.bruceeckel.simpletest.*;

public class AutoInc {
  static Test monitor = new Test();
  public static void main(String[] args) {
    int i = 1;
    System.out.println("i : " + i);
    System.out.println("++i : " + ++i); // Pre-increment
    System.out.println("i++ : " + i++); // Post-increment
    System.out.println("i : " + i);
    System.out.println("--i : " + --i); // Pre-decrement
    System.out.println("i-- : " + i--); // Post-decrement
    System.out.println("i : " + i);
    monitor.expect(new String[] {
      "i : 1",
      "++i : 2",
      "i++ : 2",
      "i : 3",
      "--i : 2",
      "i-- : 2",
      "i : 1"
    });
  }
} ///:~




You can see that for the prefix form, you get the value after the operation has been performed, but with the postfix form, you get the value before the operation is performed. These are the only operators (other than those involving assignment) that have side effects. (That is, they change the operand rather than using just its value.) Comentários (em inglês)



The increment operator is one explanation for the name C++, implying “one step beyond C.” In an early Java speech, Bill Joy (one of the Java creators), said that “Java=C++--” (C plus plus minus minus), suggesting that Java is C++ with the unnecessary hard parts removed, and therefore a much simpler language. As you progress in this book, you’ll see that many parts are simpler, and yet Java isn’t that much easier than C++. Comentários (em inglês)

Relational operators



Relational operators generate a boolean result. They evaluate the relationship between the values of the operands. A relational expression produces true if the relationship is true, and false if the relationship is untrue. The relational operators are less than (<), greater than (>), less than or equal to (<=), greater than or equal to (>=), equivalent (==) and not equivalent (!=). Equivalence and nonequivalence work with all built-in data types, but the other comparisons won’t work with type boolean. Comentários (em inglês)

Testing object equivalence



The relational operators == and != also work with all objects, but their meaning often confuses the first-time Java programmer. Here’s an example: Comentários (em inglês)



//: c03:Equivalence.java
import com.bruceeckel.simpletest.*;

public class Equivalence {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1 == n2);
    System.out.println(n1 != n2);
    monitor.expect(new String[] {
      "false",
      "true"
    });
  }
} ///:~




The expression System.out.println(n1 == n2) will print the result of the boolean comparison within it. Surely the output should be true and then false, since both Integer objects are the same. But while the contents of the objects are the same, the references are not the same and the operators == and != compare object references. So the output is actually false and then true. Naturally, this surprises people at first. Comentários (em inglês)



What if you want to compare the actual contents of an object for equivalence? You must use the special method equals( ) that exists for all objects (not primitives, which work fine with == and !=). Here’s how it’s used: Comentários (em inglês)



//: c03:EqualsMethod.java
import com.bruceeckel.simpletest.*;

public class EqualsMethod {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1.equals(n2));
    monitor.expect(new String[] {
      "true"
    });
  }
} ///:~




The result will be true, as you would expect. Ah, but it’s not as simple as that. If you create your own class, like this: Comentários (em inglês)



//: c03:EqualsMethod2.java
import com.bruceeckel.simpletest.*;

class Value {
  int i;
}

public class EqualsMethod2 {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Value v1 = new Value();
    Value v2 = new Value();
    v1.i = v2.i = 100;
    System.out.println(v1.equals(v2));
    monitor.expect(new String[] {
      "false"
    });
  }
} ///:~




you’re back to square one: the result is false. This is because the default behavior of equals( ) is to compare references. So unless you override equals( ) in your new class you won’t get the desired behavior. Unfortunately, you won’t learn about overriding until Chapter 7 and about the proper way to define equals( ) until Chapter 11, but being aware of the way equals( ) behaves might save you some grief in the meantime. Comentários (em inglês)



Most of the Java library classes implement equals( ) so that it compares the contents of objects instead of their references. Comentários (em inglês)

Logical operators



Each of the logical operators AND (&&), OR (||) and NOT (!) produces a boolean value of true or false based on the logical relationship of its arguments. This example uses the relational and logical operators: Comentários (em inglês)



//: c03:Bool.java
// Relational and logical operators.
import com.bruceeckel.simpletest.*;
import java.util.*;

public class Bool {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Random rand = new Random();
    int i = rand.nextInt(100);
    int j = rand.nextInt(100);
    System.out.println("i = " + i);
    System.out.println("j = " + j);
    System.out.println("i > j is " + (i > j));
    System.out.println("i < j is " + (i < j));
    System.out.println("i >= j is " + (i >= j));
    System.out.println("i <= j is " + (i <= j));
    System.out.println("i == j is " + (i == j));
    System.out.println("i != j is " + (i != j));
    // Treating an int as a boolean is not legal Java:
//! System.out.println("i && j is " + (i && j));
//! System.out.println("i || j is " + (i || j));
//! System.out.println("!i is " + !i);
    System.out.println("(i < 10) && (j < 10) is "
       + ((i < 10) && (j < 10)) );
    System.out.println("(i < 10) || (j < 10) is "
       + ((i < 10) || (j < 10)) );
    monitor.expect(new String[] {
      "%% i = -?\\d+",
      "%% j = -?\\d+",
      "%% i > j is (true|false)",
      "%% i < j is (true|false)",
      "%% i >= j is (true|false)",
      "%% i <= j is (true|false)",
      "%% i == j is (true|false)",
      "%% i != j is (true|false)",
      "%% \\(i < 10\\) && \\(j < 10\\) is (true|false)",
      "%% \\(i < 10\\) \\|\\| \\(j < 10\\) is (true|false)"
    });
  }
} ///:~




In the regular expressions in the expect( ) statement, parentheses have the effect of grouping an expression, and the vertical bar ‘|’ means OR. So:



(true|false)




Means that this part of the string may be either ‘true’ or ‘false’. Because these characters are special in regular expressions, they must be escaped with a ‘\\’ if you want them to appear as ordinary characters in the expression. Comentários (em inglês)



You can apply AND, OR, or NOT to boolean values only. You can’t use a non-boolean as if it were a boolean in a logical expression as you can in C and C++. You can see the failed attempts at doing this commented out with a //! comment marker. The subsequent expressions, however, produce boolean values using relational comparisons, then use logical operations on the results. Comentários (em inglês)



Note that a boolean value is automatically converted to an appropriate text form if it’s used where a String is expected. Comentários (em inglês)



You can replace the definition for int in the preceding program with any other primitive data type except boolean. Be aware, however, that the comparison of floating-point numbers is very strict. A number that is the tiniest fraction different from another number is still “not equal.” A number that is the tiniest bit above zero is still nonzero. Comentários (em inglês)

Short-circuiting



When dealing with logical operators, you run into a phenomenon called “short circuiting.” This means that the expression will be evaluated only until the truth or falsehood of the entire expression can be unambiguously determined. As a result, the latter parts of a logical expression might not be evaluated. Here’s an example that demonstrates short-circuiting:



//: c03:ShortCircuit.java
// Demonstrates short-circuiting behavior.
// with logical operators.
import com.bruceeckel.simpletest.*;

public class ShortCircuit {
  static Test monitor = new Test();
  static boolean test1(int val) {
    System.out.println("test1(" + val + ")");
    System.out.println("result: " + (val < 1));
    return val < 1;
  }
  static boolean test2(int val) {
    System.out.println("test2(" + val + ")");
    System.out.println("result: " + (val < 2));
    return val < 2;
  }
  static boolean test3(int val) {
    System.out.println("test3(" + val + ")");
    System.out.println("result: " + (val < 3));
    return val < 3;
  }
  public static void main(String[] args) {
    if(test1(0) && test2(2) && test3(2))
      System.out.println("expression is true");
    else
      System.out.println("expression is false");
    monitor.expect(new String[] {
      "test1(0)",
      "result: true",
      "test2(2)",
      "result: false",
      "expression is false"
    });
  }
} ///:~




Each test performs a comparison against the argument and returns true or false. It also prints information to show you that it’s being called. The tests are used in the expression: Comentários (em inglês)



if(test1(0) && test2(2) && test3(2))




You might naturally think that all three tests would be executed, but the output shows otherwise. The first test produced a true result, so the expression evaluation continues. However, the second test produced a false result. Since this means that the whole expression must be false, why continue evaluating the rest of the expression? It could be expensive. The reason for short-circuiting, in fact, is that you can get a potential performance increase if all the parts of a logical expression do not need to be evaluated. Comentários (em inglês)

Bitwise operators



The bitwise operators allow you to manipulate individual bits in an integral primitive data type. Bitwise operators perform Boolean algebra on the corresponding bits in the two arguments to produce the result. Comentários (em inglês)



The bitwise operators come from C’s low-level orientation, where you often manipulate hardware directly and must set the bits in hardware registers. Java was originally designed to be embedded in TV set-top boxes, so this low-level orientation still made sense. However, you probably won’t use the bitwise operators much. Comentários (em inglês)



The bitwise AND operator (&) produces a one in the output bit if both input bits are one, otherwise it produces a zero. The bitwise OR operator (|) produces a one in the output bit if either input bit is a one and produces a zero only if both input bits are zero. The bitwise EXCLUSIVE OR, or XOR (^), produces a one in the output bit if one or the other input bit is a one, but not both. The bitwise NOT (~, also called the ones complement operator) is a unary operator; it takes only one argument. (All other bitwise operators are binary operators.) Bitwise NOT produces the opposite of the input bit—a one if the input bit is zero, a zero if the input bit is one. Comentários (em inglês)



The bitwise operators and logical operators use the same characters, so it is helpful to have a mnemonic device to help you remember the meanings: because bits are “small,” there is only one character in the bitwise operators. Comentários (em inglês)



Bitwise operators can be combined with the = sign to unite the operation and assignment: &=, |= and ^= are all legitimate. (Since ~ is a unary operator, it cannot be combined with the = sign.) Comentários (em inglês)



The boolean type is treated as a one-bit value, so it is somewhat different. You can perform a bitwise AND, OR, and XOR, but you can’t perform a bitwise NOT (presumably to prevent confusion with the logical NOT). For booleans, the bitwise operators have the same effect as the logical operators except that they do not short circuit. Also, bitwise operations on booleans include an XOR logical operator that is not included under the list of “logical” operators. You’re prevented from using booleans in shift expressions, which are described next. Comentários (em inglês)

Shift operators



The shift operators also manipulate bits. They can be used solely with primitive, integral types. The left-shift operator (<<) produces the operand to the left of the operator shifted to the left by the number of bits specified after the operator (inserting zeroes at the lower-order bits). The signed right-shift operator (>>) produces the operand to the left of the operator shifted to the right by the number of bits specified after the operator. The signed right shift >> uses sign extension: if the value is positive, zeroes are inserted at the higher-order bits; if the value is negative, ones are inserted at the higher-order bits. Java has also added the unsigned right shift >>>, which uses zero extension: regardless of the sign, zeroes are inserted at the higher-order bits. This operator does not exist in C or C++. Comentários (em inglês)



If you shift a char, byte, or short, it will be promoted to int before the shift takes place, and the result will be an int. Only the five low-order bits of the right-hand side will be used. This prevents you from shifting more than the number of bits in an int. If you’re operating on a long, you’ll get a long result. Only the six low-order bits of the right-hand side will be used, so you can’t shift more than the number of bits in a long. Comentários (em inglês)



Shifts can be combined with the equal sign (<<= or >>= or >>>=). The lvalue is replaced by the lvalue shifted by the rvalue. There is a problem, however, with the unsigned right shift combined with assignment. If you use it with byte or short, you don’t get the correct results. Instead, these are promoted to int and right shifted, but then truncated as they are assigned back into their variables, so you get -1 in those cases. The following example demonstrates this: Comentários (em inglês)



//: c03:URShift.java
// Test of unsigned right shift.
import com.bruceeckel.simpletest.*;

public class URShift {
  static Test monitor = new Test();
  public static void main(String[] args) {
    int i = -1;
    System.out.println(i >>>= 10);
    long l = -1;
    System.out.println(l >>>= 10);
    short s = -1;
    System.out.println(s >>>= 10);
    byte b = -1;
    System.out.println(b >>>= 10);
    b = -1;
    System.out.println(b>>>10);
    monitor.expect(new String[] {
      "4194303",
      "18014398509481983",
      "-1",
      "-1",
      "4194303"
    });
  }
} ///:~




In the last shift, the resulting value is not assigned back into b, but is printed directly, so the correct behavior occurs. Comentários (em inglês)



Here’s an example that demonstrates the use of all the operators involving bits:



//: c03:BitManipulation.java
// Using the bitwise operators.
import com.bruceeckel.simpletest.*;
import java.util.*;

public class BitManipulation {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Random rand = new Random();
    int i = rand.nextInt();
    int j = rand.nextInt();
    printBinaryInt("-1", -1);
    printBinaryInt("+1", +1);
    int maxpos = 2147483647;
    printBinaryInt("maxpos", maxpos);
    int maxneg = -2147483648;
    printBinaryInt("maxneg", maxneg);
    printBinaryInt("i", i);
    printBinaryInt("~i", ~i);
    printBinaryInt("-i", -i);
    printBinaryInt("j", j);
    printBinaryInt("i & j", i & j);
    printBinaryInt("i | j", i | j);
    printBinaryInt("i ^ j", i ^ j);
    printBinaryInt("i << 5", i << 5);
    printBinaryInt("i >> 5", i >> 5);
    printBinaryInt("(~i) >> 5", (~i) >> 5);
    printBinaryInt("i >>> 5", i >>> 5);
    printBinaryInt("(~i) >>> 5", (~i) >>> 5);

    long l = rand.nextLong();
    long m = rand.nextLong();
    printBinaryLong("-1L", -1L);
    printBinaryLong("+1L", +1L);
    long ll = 9223372036854775807L;
    printBinaryLong("maxpos", ll);
    long lln = -9223372036854775808L;
    printBinaryLong("maxneg", lln);
    printBinaryLong("l", l);
    printBinaryLong("~l", ~l);
    printBinaryLong("-l", -l);
    printBinaryLong("m", m);
    printBinaryLong("l & m", l & m);
    printBinaryLong("l | m", l | m);
    printBinaryLong("l ^ m", l ^ m);
    printBinaryLong("l << 5", l << 5);
    printBinaryLong("l >> 5", l >> 5);
    printBinaryLong("(~l) >> 5", (~l) >> 5);
    printBinaryLong("l >>> 5", l >>> 5);
    printBinaryLong("(~l) >>> 5", (~l) >>> 5);
    monitor.expect("BitManipulation.out");
  }
  static void printBinaryInt(String s, int i) {
    System.out.println(
      s + ", int: " + i + ", binary: ");
    System.out.print("   ");
    for(int j = 31; j >= 0; j--)
      if(((1 << j) &  i) != 0)
        System.out.print("1");
      else
        System.out.print("0");
    System.out.println();
  }
  static void printBinaryLong(String s, long l) {
    System.out.println(
      s + ", long: " + l + ", binary: ");
    System.out.print("   ");
    for(int i = 63; i >= 0; i--)
      if(((1L << i) & l) != 0)
        System.out.print("1");
      else
        System.out.print("0");
    System.out.println();
  }
} ///:~




The two methods at the end, printBinaryInt( ) and printBinaryLong( ), take an int or a long, respectively, and print it out in binary format along with a descriptive string. You can ignore the implementation of these for now. Comentários (em inglês)



You’ll note the use of System.out.print( ) instead of System.out.println( ). The print( ) method does not emit a newline, so it allows you to output a line in pieces. Comentários (em inglês)



In this case, the expect( ) statement takes a file name, from which it reads the expected lines (which may or may not include regular expressions). This is useful in situations where the output is too long or inappropriate to include in the book. The files ending with “.out” are part of the code distribution, available for download from www.BruceEckel.com, so you can open the file and look at it to see what the output should be (or simply run the program yourself). Comentários (em inglês)



As well as demonstrating the effect of all the bitwise operators for int and long, this example also shows the minimum, maximum, +1, and -1 values for int and long so you can see what they look like. Note that the high bit represents the sign: 0 means positive and 1 means negative. The output for the int portion looks like this:



-1, int: -1, binary: 
   11111111111111111111111111111111
+1, int: 1, binary: 
   00000000000000000000000000000001
maxpos, int: 2147483647, binary: 
   01111111111111111111111111111111
maxneg, int: -2147483648, binary: 
   10000000000000000000000000000000
i, int: 59081716, binary: 
   00000011100001011000001111110100
~i, int: -59081717, binary: 
   11111100011110100111110000001011
-i, int: -59081716, binary: 
   11111100011110100111110000001100
j, int: 198850956, binary: 
   00001011110110100011100110001100
i & j, int: 58720644, binary: 
   00000011100000000000000110000100
i | j, int: 199212028, binary: 
   00001011110111111011101111111100
i ^ j, int: 140491384, binary: 
   00001000010111111011101001111000
i << 5, int: 1890614912, binary: 
   01110000101100000111111010000000
i >> 5, int: 1846303, binary: 
   00000000000111000010110000011111
(~i) >> 5, int: -1846304, binary: 
   11111111111000111101001111100000
i >>> 5, int: 1846303, binary: 
   00000000000111000010110000011111
(~i) >>> 5, int: 132371424, binary: 
   00000111111000111101001111100000




The binary representation of the numbers is referred to as signed two’s complement. Comentários (em inglês)

Ternary if-else operator



This operator is unusual because it has three operands. It is truly an operator because it produces a value, unlike the ordinary if-else statement that you’ll see in the next section of this chapter. The expression is of the form: Comentários (em inglês)



boolean-exp ? value0 : value1




If boolean-exp evaluates to true, value0 is evaluated, and its result becomes the value produced by the operator. If boolean-exp is false, value1 is evaluated and its result becomes the value produced by the operator. Comentários (em inglês)



Of course, you could use an ordinary if-else statement (described later), but the ternary operator is much terser. Although C (where this operator originated) prides itself on being a terse language, and the ternary operator might have been introduced partly for efficiency, you should be somewhat wary of using it on an everyday basis—it’s easy to produce unreadable code. Comentários (em inglês)



The conditional operator can be used for its side effects or for the value it produces, but in general you want the value, since that’s what makes the operator distinct from the if-else. Here’s an example: Comentários (em inglês)



static int ternary(int i) {
  return i < 10 ? i * 100 : i * 10;
} 




You can see that this code is more compact than what you’d need to write without the ternary operator: Comentários (em inglês)



static int alternative(int i) {
  if (i < 10)
    return i * 100;
  else
    return i * 10;
}




The second form is easier to understand, and doesn’t require a lot more typing. So be sure to ponder your reasons when choosing the ternary operator—it’s generally warranted when you’re setting a variable to one of two values. Comentários (em inglês)

The comma operator



The comma is used in C and C++ not only as a separator in function argument lists, but also as an operator for sequential evaluation. The sole place that the comma operator is used in Java is in for loops, which will be described later in this chapter. Comentários (em inglês)

String operator +



There’s one special usage of an operator in Java: the + operator can be used to concatenate strings, as you’ve already seen. It seems a natural use of the + even though it doesn’t fit with the traditional way that + is used. This capability seemed like a good idea in C++, so operator overloading was added to C++ to allow the C++ programmer to add meanings to almost any operator. Unfortunately, operator overloading combined with some of the other restrictions in C++ turns out to be a fairly complicated feature for programmers to design into their classes. Although operator overloading would have been much simpler to implement in Java than it was in C++, this feature was still considered too complex, so Java programmers cannot implement their own overloaded operators like C++ programmers can. Comentários (em inglês)



The use of the String + has some interesting behavior. If an expression begins with a String, then all operands that follow must be Strings (remember that the compiler will turn a quoted sequence of characters into a String): Comentários (em inglês)



int x = 0, y = 1, z = 2;
String sString = "x, y, z ";
System.out.println(sString + x + y + z);




Here, the Java compiler will convert x, y, and z into their String representations instead of adding them together first. And if you say:



System.out.println(x + sString);




Java will turn x into a String. Comentários (em inglês)

Common pitfalls when using operators



One of the pitfalls when using operators is attempting to leave out the parentheses when you are even the least bit uncertain about how an expression will evaluate. This is still true in Java. Comentários (em inglês)



An extremely common error in C and C++ looks like this:



while(x = y) {
  // ....
}




The programmer was clearly trying to test for equivalence (==) rather than do an assignment. In C and C++ the result of this assignment will always be true if y is nonzero, and you’ll probably get an infinite loop. In Java, the result of this expression is not a boolean, but the compiler expects a boolean and won’t convert from an int, so it will conveniently give you a compile-time error and catch the problem before you ever try to run the program. So the pitfall never happens in Java. (The only time you won’t get a compile-time error is when x and y are boolean, in which case x = y is a legal expression, and in the preceding example, probably an error.) Comentários (em inglês)



A similar problem in C and C++ is using bitwise AND and OR instead of the logical versions. Bitwise AND and OR use one of the characters (& or |) while logical AND and OR use two (&& and ||). Just as with = and ==, it’s easy to type just one character instead of two. In Java, the compiler again prevents this, because it won’t let you cavalierly use one type where it doesn’t belong. Comentários (em inglês)

Casting operators



The word cast is used in the sense of “casting into a mold.” Java will automatically change one type of data into another when appropriate. For instance, if you assign an integral value to a floating-point variable, the compiler will automatically convert the int to a float. Casting allows you to make this type conversion explicit, or to force it when it wouldn’t normally happen. Comentários (em inglês)



To perform a cast, put the desired data type (including all modifiers) inside parentheses to the left of any value. Here’s an example:



void casts() {
  int i = 200;
  long l = (long)i;
  long l2 = (long)200;
}




As you can see, it’s possible to perform a cast on a numeric value as well as on a variable. In both casts shown here, however, the cast is superfluous, since the compiler will automatically promote an int value to a long when necessary. However, you are allowed to use superfluous casts to make a point or to make your code more clear. In other situations, a cast may be essential just to get the code to compile. Comentários (em inglês)



In C and C++, casting can cause some headaches. In Java, casting is safe, with the exception that when you perform a so-called narrowing conversion (that is, when you go from a data type that can hold more information to one that doesn’t hold as much), you run the risk of losing information. Here the compiler forces you to do a cast, in effect saying “this can be a dangerous thing to do—if you want me to do it anyway you must make the cast explicit.” With a widening conversion an explicit cast is not needed, because the new type will more than hold the information from the old type so that no information is ever lost. Comentários (em inglês)



Java allows you to cast any primitive type to any other primitive type, except for boolean, which doesn’t allow any casting at all. Class types do not allow casting. To convert one to the other, there must be special methods. (String is a special case, and you’ll find out later in this book that objects can be cast within a family of types; an Oak can be cast to a Tree and vice-versa, but not to a foreign type such as a Rock.) Comentários (em inglês)

Literals



Ordinarily, when you insert a literal value into a program, the compiler knows exactly what type to make it. Sometimes, however, the type is ambiguous. When this happens, you must guide the compiler by adding some extra information in the form of characters associated with the literal value. The following code shows these characters: Comentários (em inglês)



//: c03:Literals.java

public class Literals {
  char c = 0xffff; // max char hex value
  byte b = 0x7f; // max byte hex value
  short s = 0x7fff; // max short hex value
  int i1 = 0x2f; // Hexadecimal (lowercase)
  int i2 = 0X2F; // Hexadecimal (uppercase)
  int i3 = 0177; // Octal (leading zero)
  // Hex and Oct also work with long.
  long n1 = 200L; // long suffix
  long n2 = 200l; // long suffix (but can be confusing)
  long n3 = 200;
  //! long l6(200); // not allowed
  float f1 = 1;
  float f2 = 1F; // float suffix
  float f3 = 1f; // float suffix
  float f4 = 1e-45f; // 10 to the power
  float f5 = 1e+9f; // float suffix
  double d1 = 1d; // double suffix
  double d2 = 1D; // double suffix
  double d3 = 47e47d; // 10 to the power
} ///:~




Hexadecimal (base 16), which works with all the integral data types, is denoted by a leading 0x or 0X followed by 0-9 or a-f either in uppercase or lowercase. If you try to initialize a variable with a value bigger than it can hold (regardless of the numerical form of the value), the compiler will give you an error message. Notice in the preceding code the maximum possible hexadecimal values for char, byte, and short. If you exceed these, the compiler will automatically make the value an int and tell you that you need a narrowing cast for the assignment. You’ll know you’ve stepped over the line. Comentários (em inglês)



Octal (base 8) is denoted by a leading zero in the number and digits from 0-7. There is no literal representation for binary numbers in C, C++, or Java. Comentários (em inglês)



A trailing character after a literal value establishes its type. Uppercase or lowercase L means long, upper or lowercase F means float and uppercase or lowercase D means double. Comentários (em inglês)



Exponents use a notation that I’ve always found rather dismaying: 1.39 e-47f. In science and engineering, ‘e’ refers to the base of natural logarithms, approximately 2.718. (A more precise double value is available in Java as Math.E.) This is used in exponentiation expressions such as 1.39 x e-47, which means 1.39 x 2.718-47. However, when FORTRAN was invented, they decided that e would naturally mean “ten to the power,” which is an odd decision because FORTRAN was designed for science and engineering, and one would think its designers would be sensitive about introducing such an ambiguity.[17] At any rate, this custom was followed in C, C++ and now Java. So if you’re used to thinking in terms of e as the base of natural logarithms, you must do a mental translation when you see an expression such as 1.39 e-47f in Java; it means 1.39 x 10-47. Comentários (em inglês)



Note that you don’t need to use the trailing character when the compiler can figure out the appropriate type. With Comentários (em inglês)



long n3 = 200;




there’s no ambiguity, so an L after the 200 would be superfluous. However, with Comentários (em inglês)



float f4 = 1e-47f; // 10 to the power




the compiler normally takes exponential numbers as doubles, so without the trailing f, it will give you an error telling you that you must use a cast to convert double to float. Comentários (em inglês)

Promotion



You’ll discover that if you perform any mathematical or bitwise operations on primitive data types that are smaller than an int (that is, char, byte, or short), those values will be promoted to int before performing the operations, and the resulting value will be of type int. So if you want to assign back into the smaller type, you must use a cast. (And, since you’re assigning back into a smaller type, you might be losing information.) In general, the largest data type in an expression is the one that determines the size of the result of that expression; if you multiply a float and a double, the result will be double; if you add an int and a long, the result will be long. Comentários (em inglês)

Java has no “sizeof”



In C and C++, the sizeof( ) operator satisfies a specific need: it tells you the number of bytes allocated for data items. The most compelling need for sizeof( ) in C and C++ is portability. Different data types might be different sizes on different machines, so the programmer must find out how big those types are when performing operations that are sensitive to size. For example, one computer might store integers in 32 bits, whereas another might store integers as 16 bits. Programs could store larger values in integers on the first machine. As you might imagine, portability is a huge headache for C and C++ programmers. Comentários (em inglês)



Java does not need a sizeof( ) operator for this purpose, because all the data types are the same size on all machines. You do not need to think about portability on this level—it is designed into the language. Comentários (em inglês)

Precedence revisited



Upon hearing me complain about the complexity of remembering operator precedence during one of my seminars, a student suggested a mnemonic that is simultaneously a commentary: “Ulcer Addicts Really Like C A lot.”

Mnemonic

Operator type

Operators

Ulcer

Unary

+ - ++--

Addicts

Arithmetic (and shift)

* / % + - << >>

Really

Relational

> < >= <= == !=

Like

Logical (and bitwise)

&& || & | ^

C

Conditional (ternary)

A > B ? X : Y

A Lot

Assignment

= (and compound assignment like *=)



Of course, with the shift and bitwise operators distributed around the table it is not a perfect mnemonic, but for non-bit operations it works.

A compendium of operators



The following example shows which primitive data types can be used with particular operators. Basically, it is the same example repeated over and over, but using different primitive data types. The file will compile without error because the lines that would cause errors are commented out with a //!. Comentários (em inglês)



//: c03:AllOps.java
// Tests all the operators on all the primitive data types
// to show which ones are accepted by the Java compiler.

public class AllOps {
  // To accept the results of a boolean test:
  void f(boolean b) {}
  void boolTest(boolean x, boolean y) {
    // Arithmetic operators:
    //! x = x * y;
    //! x = x / y;
    //! x = x % y;
    //! x = x + y;
    //! x = x - y;
    //! x++;
    //! x--;
    //! x = +y;
    //! x = -y;
    // Relational and logical:
    //! f(x > y);
    //! f(x >= y);
    //! f(x < y);
    //! f(x <= y);
    f(x == y);
    f(x != y);
    f(!y);
    x = x && y;
    x = x || y;
    // Bitwise operators:
    //! x = ~y;
    x = x & y;
    x = x | y;
    x = x ^ y;
    //! x = x << 1;
    //! x = x >> 1;
    //! x = x >>> 1;
    // Compound assignment:
    //! x += y;
    //! x -= y;
    //! x *= y;
    //! x /= y;
    //! x %= y;
    //! x <<= 1;
    //! x >>= 1;
    //! x >>>= 1;
    x &= y;
    x ^= y;
    x |= y;
    // Casting:
    //! char c = (char)x;
    //! byte B = (byte)x;
    //! short s = (short)x;
    //! int i = (int)x;
    //! long l = (long)x;
    //! float f = (float)x;
    //! double d = (double)x;
  }
  void charTest(char x, char y) {
    // Arithmetic operators:
    x = (char)(x * y);
    x = (char)(x / y);
    x = (char)(x % y);
    x = (char)(x + y);
    x = (char)(x - y);
    x++;
    x--;
    x = (char)+y;
    x = (char)-y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    x= (char)~y;
    x = (char)(x & y);
    x  = (char)(x | y);
    x = (char)(x ^ y);
    x = (char)(x << 1);
    x = (char)(x >> 1);
    x = (char)(x >>> 1);
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x <<= 1;
    x >>= 1;
    x >>>= 1;
    x &= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void byteTest(byte x, byte y) {
    // Arithmetic operators:
    x = (byte)(x* y);
    x = (byte)(x / y);
    x = (byte)(x % y);
    x = (byte)(x + y);
    x = (byte)(x - y);
    x++;
    x--;
    x = (byte)+ y;
    x = (byte)- y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    x = (byte)~y;
    x = (byte)(x & y);
    x = (byte)(x | y);
    x = (byte)(x ^ y);
    x = (byte)(x << 1);
    x = (byte)(x >> 1);
    x = (byte)(x >>> 1);
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x <<= 1;
    x >>= 1;
    x >>>= 1;
    x &= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void shortTest(short x, short y) {
    // Arithmetic operators:
    x = (short)(x * y);
    x = (short)(x / y);
    x = (short)(x % y);
    x = (short)(x + y);
    x = (short)(x - y);
    x++;
    x--;
    x = (short)+y;
    x = (short)-y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    x = (short)~y;
    x = (short)(x & y);
    x = (short)(x | y);
    x = (short)(x ^ y);
    x = (short)(x << 1);
    x = (short)(x >> 1);
    x = (short)(x >>> 1);
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x <<= 1;
    x >>= 1;
    x >>>= 1;
    x &= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void intTest(int x, int y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    x = ~y;
    x = x & y;
    x = x | y;
    x = x ^ y;
    x = x << 1;
    x = x >> 1;
    x = x >>> 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x <<= 1;
    x >>= 1;
    x >>>= 1;
    x &= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void longTest(long x, long y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    x = ~y;
    x = x & y;
    x = x | y;
    x = x ^ y;
    x = x << 1;
    x = x >> 1;
    x = x >>> 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x <<= 1;
    x >>= 1;
    x >>>= 1;
    x &= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    float f = (float)x;
    double d = (double)x;
  }
  void floatTest(float x, float y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    //! x = ~y;
    //! x = x & y;
    //! x = x | y;
    //! x = x ^ y;
    //! x = x << 1;
    //! x = x >> 1;
    //! x = x >>> 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    //! x <<= 1;
    //! x >>= 1;
    //! x >>>= 1;
    //! x &= y;
    //! x ^= y;
    //! x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    double d = (double)x;
  }
  void doubleTest(double x, double y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x > y);
    f(x >= y);
    f(x < y);
    f(x <= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x && y);
    //! f(x || y);
    // Bitwise operators:
    //! x = ~y;
    //! x = x & y;
    //! x = x | y;
    //! x = x ^ y;
    //! x = x << 1;
    //! x = x >> 1;
    //! x = x >>> 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    //! x <<= 1;
    //! x >>= 1;
    //! x >>>= 1;
    //! x &= y;
    //! x ^= y;
    //! x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
  }
} ///:~




Note that boolean is quite limited. You can assign to it the values true and false, and you can test it for truth or falsehood, but you cannot add booleans or perform any other type of operation on them. Comentários (em inglês)



In char, byte, and short, you can see the effect of promotion with the arithmetic operators. Each arithmetic operation on any of those types produces an int result, which must be explicitly cast back to the original type (a narrowing conversion that might lose information) to assign back to that type. With int values, however, you do not need to cast, because everything is already an int. Don’t be lulled into thinking everything is safe, though. If you multiply two ints that are big enough, you’ll overflow the result. The following example demonstrates this: Comentários (em inglês)



//: c03:Overflow.java
// Surprise! Java lets you overflow.
import com.bruceeckel.simpletest.*;

public class Overflow {
  static Test monitor = new Test();
  public static void main(String[] args) {
    int big = 0x7fffffff; // max int value
    System.out.println("big = " + big);
    int bigger = big * 4;
    System.out.println("bigger = " + bigger);
    monitor.expect(new String[] {
      "big = 2147483647",
      "bigger = -4"
    });
  }
} ///:~




You get no errors or warnings from the compiler, and no exceptions at run time. Java is good, but it’s not that good. Comentários (em inglês)



Compound assignments do not require casts for char, byte, or short, even though they are performing promotions that have the same results as the direct arithmetic operations. On the other hand, the lack of the cast certainly simplifies the code. Comentários (em inglês)



You can see that, with the exception of boolean, any primitive type can be cast to any other primitive type. Again, you must be aware of the effect of a narrowing conversion when casting to a smaller type, otherwise you might unknowingly lose information during the cast. Comentários (em inglês)

Execution control



Java uses all of C’s execution control statements, so if you’ve programmed with C or C++, then most of what you see will be familiar. Most procedural programming languages have some kind of control statements, and there is often overlap among languages. In Java, the keywords include if-else, while, do-while, for, and a selection statement called switch. Java does not, however, support the much-maligned goto (which can still be the most expedient way to solve certain types of problems). You can still do a goto-like jump, but it is much more constrained than a typical goto. Comentários (em inglês)

true and false



All conditional statements use the truth or falsehood of a conditional expression to determine the execution path. An example of a conditional expression is A == B. This uses the conditional operator == to see if the value of A is equivalent to the value of B. The expression returns true or false. Any of the relational operators you’ve seen earlier in this chapter can be used to produce a conditional statement. Note that Java doesn’t allow you to use a number as a boolean, even though it’s allowed in C and C++ (where truth is nonzero and falsehood is zero). If you want to use a non-boolean in a boolean test, such as if(a), you must first convert it to a boolean value by using a conditional expression, such as if(a != 0). Comentários (em inglês)

if-else



The if-else statement is probably the most basic way to control program flow. The else is optional, so you can use if in two forms:



if(Boolean-expression)
  statement



or


if(Boolean-expression)
  statement
else
  statement




The conditional must produce a boolean result. The statement is either a simple statement terminated by a semicolon, or a compound statement, which is a group of simple statements enclosed in braces. Any time the word “statement” is used, it always implies that the statement can be simple or compound. Comentários (em inglês)



As an example of if-else, here is a test( ) method that will tell you whether a guess is above, below, or equivalent to a target number:



//: c03:IfElse.java
import com.bruceeckel.simpletest.*;

public class IfElse {
  static Test monitor = new Test();
  static int test(int testval, int target) {
    int result = 0;
    if(testval > target)
      result = +1;
    else if(testval < target)
      result = -1;
    else
      result = 0; // Match
    return result;
  }
  public static void main(String[] args) {
    System.out.println(test(10, 5));
    System.out.println(test(5, 10));
    System.out.println(test(5, 5));
    monitor.expect(new String[] {
      "1",
      "-1",
      "0"
    });
  }
} ///:~




It is conventional to indent the body of a control flow statement so the reader can easily determine where it begins and ends.

return



The return keyword has two purposes: It specifies what value a method will return (if it doesn’t have a void return value) and it causes that value to be returned immediately. The preceding test( ) method can be rewritten to take advantage of this: Comentários (em inglês)



//: c03:IfElse2.java
import com.bruceeckel.simpletest.*;

public class IfElse2 {
  static Test monitor = new Test();
  static int test(int testval, int target) {
    if(testval > target)
      return +1;
    else if(testval < target)
      return -1;
    else
      return 0; // Match
  }
  public static void main(String[] args) {
    System.out.println(test(10, 5));
    System.out.println(test(5, 10));
    System.out.println(test(5, 5));
    monitor.expect(new String[] {
      "1",
      "-1",
      "0"
    });
  }
} ///:~




There’s no need for else, because the method will not continue after executing a return. Comentários (em inglês)

Iteration



Looping is controlled by while, do-while and for, which are sometimes classified as iteration statements. A statement repeats until the controlling Boolean-expression evaluates to false. The form for a while loop is



while(Boolean-expression)
  statement




The Boolean-expression is evaluated once at the beginning of the loop and again before each further iteration of the statement. Comentários (em inglês)



Here’s a simple example that generates random numbers until a particular condition is met:



//: c03:WhileTest.java
// Demonstrates the while loop.
import com.bruceeckel.simpletest.*;

public class WhileTest {
  static Test monitor = new Test();
  public static void main(String[] args) {
    double r = 0;
    while(r < 0.99d) {
      r = Math.random();
      System.out.println(r);
      monitor.expect(new String[] {
        "%% \\d\\.\\d+E?-?\\d*"
      }, Test.AT_LEAST);
    }
  }
} ///:~




This uses the static method random( ) in the Math library, which generates a double value between 0 and 1. (It includes 0, but not 1.) The conditional expression for the while says “keep doing this loop until the number is 0.99 or greater.” Each time you run this program, you’ll get a different-sized list of numbers. Comentários (em inglês)



Na instrução expect( ) você pode ver a flag Test.AT_LEAST seguindo a lista de strings esperada. A instrução expect( ) pode incluir diferentes flags para alterar seu comportamento; esta flag diz que expect( ) deve olhar pelo menos as linhas que foram mostradas, mas outras também podem aparecer (e serão ignoradas). Aqui, ela diz “você deve olhar no mínimo um valor double.” Comentários (em inglês)

do-while



The form for do-while is



do
  statement
while(Boolean-expression);




The sole difference between while and do-while is that the statement of the do-while always executes at least once, even if the expression evaluates to false the first time. In a while, if the conditional is false the first time the statement never executes. In practice, do-while is less common than while. Comentários (em inglês)

for



A for loop performs initialization before the first iteration. Then it performs conditional testing and, at the end of each iteration, some form of “stepping.” The form of the for loop is:



for(initialization; Boolean-expression; step)
  statement




Any of the expressions initialization, Boolean-expression or step can be empty. The expression is tested before each iteration, and as soon as it evaluates to false, execution will continue at the line following the for statement. At the end of each loop, the step executes. Comentários (em inglês)



for loops are usually used for “counting” tasks:



//: c03:ListCharacters.java
// Demonstrates "for" loop by listing
// all the lowercase ASCII letters.
import com.bruceeckel.simpletest.*;

public class ListCharacters {
  static Test monitor = new Test();
  public static void main(String[] args) {
    for(int i = 0; i < 128; i++)
      if(Character.isLowerCase((char)i))
        System.out.println("value: " + i +
          " character: " + (char)i);
    monitor.expect(new String[] {
      "value: 97 character: a",
      "value: 98 character: b",
      "value: 99 character: c",
      "value: 100 character: d",
      "value: 101 character: e",
      "value: 102 character: f",
      "value: 103 character: g",
      "value: 104 character: h",
      "value: 105 character: i",
      "value: 106 character: j",
      "value: 107 character: k",
      "value: 108 character: l",
      "value: 109 character: m",
      "value: 110 character: n",
      "value: 111 character: o",
      "value: 112 character: p",
      "value: 113 character: q",
      "value: 114 character: r",
      "value: 115 character: s",
      "value: 116 character: t",
      "value: 117 character: u",
      "value: 118 character: v",
      "value: 119 character: w",
      "value: 120 character: x",
      "value: 121 character: y",
      "value: 122 character: z"
    });
  }
} ///:~




Note that the variable i is defined at the point where it is used, inside the control expression of the for loop, rather than at the beginning of the block denoted by the open curly brace. The scope of i is the expression controlled by the for. Comentários (em inglês)



This program also uses the java.lang.Character “wrapper” class, which not only wraps the primitive char type in an object, but also provides other utilities. Here, the static isLowerCase( ) method is used to detect whether the character in question is a lower-case letter. Comentários (em inglês)



Traditional procedural languages like C require that all variables be defined at the beginning of a block so that when the compiler creates a block, it can allocate space for those variables. In Java and C++, you can spread your variable declarations throughout the block, defining them at the point that you need them. This allows a more natural coding style and makes code easier to understand. Comentários (em inglês)



You can define multiple variables within a for statement, but they must be of the same type:



for(int i = 0, j = 1; i < 10 && j != 11; i++, j++)
  // body of for loop




The int definition in the for statement covers both i and j. The ability to define variables in the control expression is limited to the for loop. You cannot use this approach with any of the other selection or iteration statements. Comentários (em inglês)

The comma operator



Earlier in this chapter I stated that the comma operator (not the comma separator, which is used to separate definitions and method arguments) has only one use in Java: in the control expression of a for loop. In both the initialization and step portions of the control expression, you can have a number of statements separated by commas, and those statements will be evaluated sequentially. The previous bit of code uses this ability. Here’s another example:



//: c03:CommaOperator.java
import com.bruceeckel.simpletest.*;

public class CommaOperator {
  static Test monitor = new Test();
  public static void main(String[] args) {
    for(int i = 1, j = i + 10; i < 5;
        i++, j = i * 2) {
      System.out.println("i= " + i + " j= " + j);
    }
    monitor.expect(new String[] {
      "i= 1 j= 11",
      "i= 2 j= 4",
      "i= 3 j= 6",
      "i= 4 j= 8"
    });
  }
} ///:~




You can see that in both the initialization and step portions, the statements are evaluated in sequential order. Also, the initialization portion can have any number of definitions of one type. Comentários (em inglês)

break and continue



You can also control the flow of the loop inside the body of any of the iteration statements by using break and continue. break quits the loop without executing the rest of the statements in the loop. continue stops the execution of the current iteration and goes back to the beginning of the loop to begin the next iteration. Comentários (em inglês)



Este programa mostra exemplos de break e continue no interior de loops for e while:



//: c03:BreakAndContinue.java
// Demonstrates break and continue keywords.
import com.bruceeckel.simpletest.*;

public class BreakAndContinue {
  static Test monitor = new Test();
  public static void main(String[] args) {
    for(int i = 0; i < 100; i++) {
      if(i == 74) break; // Out of for loop
      if(i % 9 != 0) continue; // Next iteration
      System.out.println(i);
    }
    int i = 0;
    // An "infinite loop":
    while(true) {
      i++;
      int j = i * 27;
      if(j == 1269) break; // Out of loop
      if(i % 10 != 0) continue; // Top of loop
      System.out.println(i);
    }
    monitor.expect(new String[] {
      "0",
      "9",
      "18",
      "27",
      "36",
      "45",
      "54",
      "63",
      "72",
      "10",
      "20",
      "30",
      "40"
    });
  }
} ///:~




In the for loop, the value of i never gets to 100 because the break statement breaks out of the loop when i is 74. Normally, you’d use a break like this only if you didn’t know when the terminating condition was going to occur. The continue statement causes execution to go back to the top of the iteration loop (thus incrementing i) whenever i is not evenly divisible by 9. When it is, the value is printed. Comentários (em inglês)



The second portion shows an “infinite loop” that would, in theory, continue forever. However, inside the loop there is a break statement that will break out of the loop. In addition, you’ll see that the continue moves back to the top of the loop without completing the remainder. (Thus printing happens in the second loop only when the value of i is divisible by 10.) In the output, The value 0 is printed, because 0 % 9 produces 0. Comentários (em inglês)



A second form of the infinite loop is for(;;). The compiler treats both while(true) and for(;;) in the same way, so whichever one you use is a matter of programming taste. Comentários (em inglês)

The infamous “goto”



The goto keyword has been present in programming languages from the beginning. Indeed, goto was the genesis of program control in assembly language: “If condition A, then jump here, otherwise jump there.” If you read the assembly code that is ultimately generated by virtually any compiler, you’ll see that program control contains many jumps (the Java compiler produces its own “assembly code,” but this code is run by the Java Virtual Machine rather than directly on a hardware CPU).



A goto is a jump at the source-code level, and that’s what brought it into disrepute. If a program will always jump from one point to another, isn’t there some way to reorganize the code so the flow of control is not so jumpy? goto fell into true disfavor with the publication of the famous “Goto considered harmful” paper by Edsger Dijkstra, and since then goto-bashing has been a popular sport, with advocates of the cast-out keyword scurrying for cover. Comentários (em inglês)



As is typical in situations like this, the middle ground is the most fruitful. The problem is not the use of goto, but the overuse of goto; in rare situations goto is actually the best way to structure control flow. Comentários (em inglês)



Although goto is a reserved word in Java, it is not used in the language; Java has no goto. However, it does have something that looks a bit like a jump tied in with the break and continue keywords. It’s not a jump but rather a way to break out of an iteration statement. The reason it’s often thrown in with discussions of goto is because it uses the same mechanism: a label. Comentários (em inglês)



A label is an identifier followed by a colon, like this:



label1:




The only place a label is useful in Java is right before an iteration statement. And that means right before—it does no good to put any other statement between the label and the iteration. And the sole reason to put a label before an iteration is if you’re going to nest another iteration or a switch inside it. That’s because the break and continue keywords will normally interrupt only the current loop, but when used with a label, they’ll interrupt the loops up to where the label exists: Comentários (em inglês)



label1: 
outer-iteration {
  inner-iteration {
    //...
    break; // 1
    //...
    continue;  // 2
    //...
    continue label1; // 3
    //...
    break label1;  // 4
  }
}




In case 1, the break breaks out of the inner iteration and you end up in the outer iteration. In case 2, the continue moves back to the beginning of the inner iteration. But in case 3, the continue label1 breaks out of the inner iteration and the outer iteration, all the way back to label1. Then it does in fact continue the iteration, but starting at the outer iteration. In case 4, the break label1 also breaks all the way out to label1, but it does not reenter the iteration. It actually does break out of both iterations. Comentários (em inglês)



Aqui está um exemplo usando loops for:



//: c03:LabeledFor.java
// Java's "labeled for" loop.
import com.bruceeckel.simpletest.*;

public class LabeledFor {
  static Test monitor = new Test();
  public static void main(String[] args) {
    int i = 0;
    outer: // Can't have statements here
    for(; true ;) { // infinite loop
      inner: // Can't have statements here
      for(; i < 10; i++) {
        System.out.println("i = " + i);
        if(i == 2) {
          System.out.println("continue");
          continue;
        }
        if(i == 3) {
          System.out.println("break");
          i++; // Otherwise i never
               // gets incremented.
          break;
        }
        if(i == 7) {
          System.out.println("continue outer");
          i++; // Otherwise i never
               // gets incremented.
          continue outer;
        }
        if(i == 8) {
          System.out.println("break outer");
          break outer;
        }
        for(int k = 0; k < 5; k++) {
          if(k == 3) {
            System.out.println("continue inner");
            continue inner;
          }
        }
      }
    }
    // Can't break or continue to labels here
    monitor.expect(new String[] {
      "i = 0",
      "continue inner",
      "i = 1",
      "continue inner",
      "i = 2",
      "continue",
      "i = 3",
      "break",
      "i = 4",
      "continue inner",
      "i = 5",
      "continue inner",
      "i = 6",
      "continue inner",
      "i = 7",
      "continue outer",
      "i = 8",
      "break outer"
    });
  }
} ///:~




Note that break breaks out of the for loop, and that the increment-expression doesn’t occur until the end of the pass through the for loop. Since break skips the increment expression, the increment is performed directly in the case of i == 3. The continue outer statement in the case of i == 7 also goes to the top of the loop and also skips the increment, so it too is incremented directly. Comentários (em inglês)



If not for the break outer statement, there would be no way to get out of the outer loop from within an inner loop, since break by itself can break out of only the innermost loop. (The same is true for continue.) Comentários (em inglês)



Of course, in the cases where breaking out of a loop will also exit the method, you can simply use a return. Comentários (em inglês)



Here is a demonstration of labeled break and continue statements with while loops:



//: c03:LabeledWhile.java
// Java's "labeled while" loop.
import com.bruceeckel.simpletest.*;

public class LabeledWhile {
  static Test monitor = new Test();
  public static void main(String[] args) {
    int i = 0;
    outer:
    while(true) {
      System.out.println("Outer while loop");
      while(true) {
        i++;
        System.out.println("i = " + i);
        if(i == 1) {
          System.out.println("continue");
          continue;
        }
        if(i == 3) {
          System.out.println("continue outer");
          continue outer;
        }
        if(i == 5) {
          System.out.println("break");
          break;
        }
        if(i == 7) {
          System.out.println("break outer");
          break outer;
        }
      }
    }
    monitor.expect(new String[] {
      "Outer while loop",
      "i = 1",
      "continue",
      "i = 2",
      "i = 3",
      "continue outer",
      "Outer while loop",
      "i = 4",
      "i = 5",
      "break",
      "Outer while loop",
      "i = 6",
      "i = 7",
      "break outer"
    });
  }
} ///:~




The same rules hold true for while: Comentários (em inglês)

  1. A plain continue goes to the top of the innermost loop and continues.
  2. A labeled continue goes to the label and reenters the loop right after that label.
  3. A break “drops out of the bottom” of the loop.
  4. A labeled break drops out of the bottom of the end of the loop denoted by the label.


It’s important to remember that the only reason to use labels in Java is when you have nested loops and you want to break or continue through more than one nested level. Comentários (em inglês)



In Dijkstra’s “goto considered harmful” paper, what he specifically objected to was the labels, not the goto. He observed that the number of bugs seems to increase with the number of labels in a program. Labels and gotos make programs difficult to analyze statically, since it introduces cycles in the program execution graph. Note that Java labels don’t suffer from this problem, since they are constrained in their placement and can’t be used to transfer control in an ad hoc manner. It’s also interesting to note that this is a case where a language feature is made more useful by restricting the power of the statement. Comentários (em inglês)

switch



O switch é algumas vezes classificado como uma expressão de seleção. A expressão switch seleciona dentre algumas porções de código, aquela que vai ser executada baseado no valor de uma expressão integral. Sua sintaxe é:Comentários (em inglês)



switch(integral-seletor) {
  case integral-valor1 : declaração; break; 
  case integral-valor2 : declaração; break;
  case integral-valor3 : declaração; break;
  case integral-valor4 : declaração; break;
  case integral-valor5 : declaração; break;
  // ...
  default: declaração;
}




O parâmetro Integral-selector é uma expressão que produz um valor integral. Oswitch compara o resultado do integral-selector com cada integral-value. Se encontra um valor igual, o statement correspondende (simples ou composto) é executado. Se nem uma igualdade ocorrer o valor do statement default irá executar. Comentários (em inglês)



Você pode perceber na definição acima que cada case termina com um break, o qual determina a execução saltar para o final do statement switch. Essa é a forma convencional de construir um statement switch , mas o break é opcional. Se estiver faltando, o código para o case seguinte irá ser executado até que encontre um break. Embora você geralmente não deseje esse tipo de comportamento, pode ser que seja útil para um programador mais experiênte. Note que o último statement, logo após o default, não possui um break por que a execução iria cair exatamente onde o break estaria de qualquer maneira. Você poderia adicionar um break no final do statement default sem prejuízo se você considerar importante para a formatação do seu estilo de programar.Comentários (em inglês)



The switch statement is a clean way to implement multiway selection (i.e., selecting from among a number of different execution paths), but it requires a selector that evaluates to an integral value, such as int or char. If you want to use, for example, a string or a floating-point number as a selector, it won’t work in a switch statement. For non-integral types, you must use a series of if statements. Comentários (em inglês)



Here’s an example that creates letters randomly and determines whether they’re vowels or consonants: Comentários (em inglês)



//: c03:VowelsAndConsonants.java
// Demonstrates the switch statement.
import com.bruceeckel.simpletest.*;

public class VowelsAndConsonants {
  static Test monitor = new Test();
  public static void main(String[] args) {
    for(int i = 0; i < 100; i++) {
      char c = (char)(Math.random() * 26 + 'a');
      System.out.print(c + ": ");
      switch(c) {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u': System.out.println("vowel");
                  break;
        case 'y':
        case 'w': System.out.println("Sometimes a vowel");
                  break;
        default:  System.out.println("consonant");
      }
      monitor.expect(new String[] {
        "%% [aeiou]: vowel|[yw]: Sometimes a vowel|" +
          "[^aeiouyw]: consonant"
      }, Test.AT_LEAST);
    }
  }
} ///:~




Since Math.random( ) generates a value between 0 and 1, you need only multiply it by the upper bound of the range of numbers you want to produce (26 for the letters in the alphabet) and add an offset to establish the lower bound. Comentários (em inglês)



Although it appears you’re switching on a character here, the switch statement is actually using the integral value of the character. The single-quoted characters in the case statements also produce integral values that are used for comparison. Comentários (em inglês)



Notice how the cases can be “stacked” on top of each other to provide multiple matches for a particular piece of code. You should also be aware that it’s essential to put the break statement at the end of a particular case; otherwise, control will simply drop through and continue processing on the next case. Comentários (em inglês)



In the regular expression in this expect( ) statement, the ‘|’ is used to indicate three different possibilities. The ‘[]’ encloses a “set” of characters in a regular expression, so the first part says “one of a, e, i, o, or u, followed by a colon and the word ‘vowel’.” The second possibility indicates either y or w and: “Sometimes a vowel.” The set in the third possibility begins with a ‘^’, which means “not any of the characters in this set,” so it indicates anything other than a vowel will match. Comentários (em inglês)

Calculation details



O comando:
char c = (char)(Math.random() * 26 + 'a');




merece uma olhada mais de perto. Math.random( ) produz um double, assim o valor 26 é convertido para um double para executar a multiplicação, a qual também produz um double. Isso significa que ‘a’ deve ser convertido para um double para executar a soma. O resultado double é transformado de volta num char com um cast. Comentários (em inglês)



O que o cast para char faz? Ou seja, se você tem o valor 29.7 e faz um cast deste para um char, o valor resultante será 30 ou 29? A resposta para isso pode ser vista neste exemplo: Comentários (em inglês)



//: c03:CastingNumbers.java
// O que acontece quando você faz um cast de um float
// ou doublo para um valor integral?
import com.bruceeckel.simpletest.*;

public class CastingNumbers {
  static Test monitor = new Test();
  public static void main(String[] args) {
    double
      above = 0.7,
      below = 0.4;
    System.out.println("acima: " + above);
    System.out.println("abaixo: " + below);
    System.out.println("(int)acima: " + (int)above);
    System.out.println("(int)abaixo: " + (int)below);
    System.out.println("(char)('a' + acima): " +
      (char)('a' + above));
    System.out.println("(char)('a' + abaixo): " +
      (char)('a' + below));
    monitor.expect(new String[] {
      "acima: 0.7",
      "abaixo: 0.4",
      "(int)acima: 0",
      "(int)abaixo: 0",
      "(char)('a' + acima): a",
      "(char)('a' + abaixo): a"
    });
  }
} ///:~




Assim o retorno de uma conversão de um float ou double para um valor integral sempre trunca o número. Comentários (em inglês)



A segunda questão diz respeito a Math.random( ). Produz um valor de zero até 1, inclusive ou exclusive do valor ‘1’? Na giria matemática, é (0,1), ou [0,1], ou (0,1] ou [0,1)? (O colchete significa “inclui,” enquanto o parentese significa “não inclui.”) Novamente, um programa de teste pode prover a resposta: Comentários (em inglês)



//: c03:RandomBounds.java
// Faz Math.random() produzir 0.0 e 1.0?
// {Executar manualmente}

public class RandomBounds {
  static void usage() {
    System.out.println("Uso: \\n\\t" +
      "RandomBounds menor\\n\\tRandomBounds maior");
    System.exit(1);
  }
  public static void main(String[] args) {
    if(args.length != 1) usage();
    if(args[0].equals("menor")) {
      while(Math.random() != 0.0)
        ; // Manter tentando
      System.out.println("Produz 0.0!");
    }
    else if(args[0].equals("maior")) {
      while(Math.random() != 1.0)
        ; // Manter tentando
      System.out.println("Produz 1.0!");
    }
    else
      usage();
  }
} ///:~




Para rodar o programa , você deve digitar na linha de comando: Comentários (em inglês)



java RandomBounds menor




ou



java RandomBounds maior




Em ambos os caso você será forçado a fechar o programa manualmente, isto poderia aparentar que Math.random( ) nunca produz 0.0 ou 1.0. Mas isto mostra como uma experiência pode ser enganosa. Se você considerar [18] que há cerca de 262 frações double diferentes entre 0 e 1, a probabilidade de encontrar qualquer um dos valores experimentalmente pode exceder a vida útil de um computador, ou de seu operador. Concentre-se que 0.0 está incluso no retorno de Math.random( ). Ou, em giria matemática, está [0,1). Comentários (em inglês)

Sumário



Este capítulo conclui o estudo das características fundamentais que aparecem em muitas linguagens de programação: cálculo, precedência de operadores, conversão de tipos, e seleção e iteração. Agora você está pronto para começar os passos que o movem diretamente para o mundo da programação orientada-a-objeto. O próximo capítulo irá cobrir os importantes assuntos da inicialização e limpeza de objetos, seguido no capítulo subsequente pelo conceito essencial de implementação oculta.Comentários (em inglês)

Exercícios



Respostas para os exercícios selecionados podem ser achadas no documento eletrônico The Thinking in Java Annotated Solution Guide, disponível para uma pequena taxa em www.BruceEckel.com.

  1. Há duas expressões na seção entitulada “precedência” antes neste capítulo. Coloque estas expressões em um programa e demonstre que elas produzem resultados diferentes.Feedback
  2. Coloque os métodos ternary( ) e alternative( ) em um programa funcional.Feedback
  3. Das seções entituladas “if-else” e “return,” modifique os dois métodos test( ) para que testval seja testado para ver se está dentro do limite entre (e inclusive) os argumentos begin e end. Feedback
  4. Escreva um programa que imprima valores de 1 a 100. Feedback
  5. Modifique o exercício 4 para que o programa saia pelo uso do comando break ao valor 47. Tente usar no lugar do return . Feedback
  6. Escreva um método que tome dois argumentos String e use todas as comparações booleanas para comparar as duas Strings e imprimir os resultados. Para os == e !=, também faça o teste equals( ) . Em main( ), chame seu método com alguns objetos String diferentes. Feedback
  7. Escreva um programa que gere 25 valores randomicos int . Para cada valor, use uma declaração if-else para classificá-la como maior que, menor que, ou igual ao segundo valor gerado randomicamente. Feedback
  8. Modifique o exercício 7 assim seu código será cercado por laço “while” infinito . Ele irá rodar até que você o interrompa através do teclado (normalmente pelo pressionamento de Control+C). Feedback
  9. Escreva um programa que usa dois laços for aninhados e o operador mod (%) para detectar e imprimir números primos (número integrais que não são divisíveis por qualquer outro número exceto ele mesmo e 1).Feedback
  10. Criar uma declaração switch que imprima uma mensagem para cada case, e coloque um switch dentro de um laço for que tente cada case. Coloque um break depois de cada case e teste-o, então remova os breaks e veja o que acontece. Feedback



[17] John Kirkham escreveu, “Eu iniciei na computação em 1962 usando FORTRAN II em um IBM 1620. Naquele tempo, e durante os anos 60 e até os anos 70, FORTRAN era uma linguagem maiúscula. Isto provavelmente começou porque muitos dos equipamentos de entrada presentes eram velhas unidades de teletipo que usavam código Baudot de 5 bits, a qual não tinha capacidade de usar letras minúsculas. O ‘E’ na notação exponencial era também sempre maiúsculo e nunca seria confundido com a base dos logaritmos naturais ‘e’, a qual é sempre minúscula. O ‘E’ simplesmente sustentou-se para exponenciais, os quais eram base do sistema numérico usado - usualmente 10. Na ocasião, o sistema octal também foi usado amplamente por programadores. Embora eu nunca vi usá-lo, se eu tivesse visto um número octal em anotação exponencial eu o teria considerado como se fosse base 8. A primeira vez em que eu me lembro de ver um ‘e' minúsculo sendo usado como foi nos recentes anos 70 e eu também achei isto confuso. O problema surgiu quando as minúsculas começaram a aparecer no FORTRAN, não em seu começo. Nós na verdade tivemos funções para usar se você realmente quisesse usar a base de logaritmo natural, mas elas eram todas maiúsculas.”



[18] Chuck Allison escreveu: O número total de números de um sistema de números de ponto flutuante é
2(M-m+1)b^(p-1) + 1
onde b é a base (normalmente 2), p é a precisão (dígitos na mantissa), M é o maior expoente, e m é o menor expoente. IEEE 754 usa:
M = 1023, m = -1022, p = 53, b = 2
assim o número total de números é
2(1023+1022+1)2^52
= 2((2^10-1) + (2^10-1))2^52
= (2^10-1)2^54
= 2^64 - 2^54
Metade destes números (correspondentes aos expoentes no limite [-1022, 0]) são menos que 1 na magnitude (ambos positivo e negativo), assim 1/4 desta expressão, ou 2^62 - 2^52 + 1 (aproximadamente 2^62) está dentro do limite [0,1). Veja meu texto em http://www.freshsources.com/1995006a.htm (último texto).


Anterior Próximo Página Inicial Índice Conteúdo