Este
material cobre diversos aspectos relacionados com o conceito de variável, p.ex.
declaração, atribuição, inicialização, tipo. A linguagem Java possui sete
categorias de variáveis, a maior parte do material abaixo focaliza nas
variáveis da categoria variável local - local
variable.
Em
diversas linguagens de programação existe o conceito de variável. Uma
variável corresponde a uma posição de memória. Uma posição de memória pode
guardar um valor. Uma declaração de variável define seu tipo e o identificador
(nome) e também outros atributos. Uma das coisas que podemos fazer com
uma variável é usa-la como alvo de uma iniciação/inicialização ou ainda como
alvo de uma operação de atribuição (assignment).
A sintaxe
simplificada para a declaração de uma variável é:
<tipo>
<nomeDeVariavel>;
Esta forma apenas declara a variável; outra sintaxe:
<tipo>
<nomeDeVariavel>=<expressao>;
Esta segunda forma corresponde a declarar e iniciar/inicializar o valor da
posição de memória correspondente à variável.
A sintaxe
simplificada de um comando ou operação de atribuição é:
<identificador da
variável> = <expressão>;
Neste comando o valor correspondente à expressão é copiado para a posição de
memória correspondente à variável. Note que o antigo valor é perdido. Só
podemos atribuir um valor para uma variável que tenha sido declarada. A
sintaxe e semântica de uma <expressão> serão definidas
mais tarde; os programas abaixo apresentam expressões simples e bem intuitivas.
Um exemplo de tipo em Java é o tipo int correspondente a um subconjunto
finito do conjunto dos inteiros da Matemática e outro exemplo é o tipo double
correspondente a um subconjunto finito do conjunto dos números reais da
Matemática.
Quando o
fluxo de controle atinge um “comando de declaração” de varável local a variável
passa a ficar disponível para iniciação e posteriormente para atribuições e
acesso. Quando o fluxo de controle atinge uma atribuição a “expressão” à
direita do operador de atribuição (=) é avaliada e o resultado da avaliação da
expressão passa a ser o valor da variável que está à esquerda o operador de
atribuição. Há uma certa similaridade sintática e semântica entre o comando de
atribuição e a iniciação ou inicialização de uma variável.
O trecho
de programa a seguir ilustra algumas declarações de variáveis e comandos de
atribuição:
int v1;
double v2;
v1=123;
v2=3.1415;
No trecho acima são declaradas duas variáveis, a variável v1 é do tipo int e a
variável v2 é do tipo double. Vemos ainda dois comandos de atribuição usando
expressões simples.
O tipo
restringe os valores que podem ser atribuidos a uma variável. O tipo int permite
a representação de valores de -2.15 bilhões até +2.15 bilhões, o tipo long
permite a representação de valores de -9.22 quintilhões ate' +9.22 quintilhões.
A restrição de tipo para uma variável acarreta maior disciplina por parte do
programador e permite programas mais eficientes e com menor probabilidade de
erros. O trecho a seguir não é permitido na linguagem Java:
int v1;
v1=3.1415; /*erro! */
O compilador emite uma mensagem de erro dizendo que a constante 3.1415 é do
tipo double e não pode ser utilizada na atribuição para uma variável do
tipo int (mais à frente será visto como que valores de tipos diferentes do tipo
da variável podem ser utilizados em expressões de atribuição).
A
associação de um tipo a uma variável na Computação é similar à amarração de uma
variável a um domínio como ocorre na Matemática. No entanto deve ser ressaltado
que o conceito de variável em Computação e o conceito de variável em Matemática
são conceitos que apresentam aspectos distintos. Uma das distinções mais notáveis
é que uma variável na Matemática, usualmente, não tem um valor específico. Considere a expressão da
Matemática
y=x+3
A variável x não tem um valor associado a ela, esta expressão pode ser interpretada
como representando o lugar geométrico dos pontos de um plano correspondentes a
uma reta. A variável (da Matemática) x pode tomar qualquer valor real. As vezes
os autores cuidadosos explicitam que a variável x "pode tomar valores em"
ou "pertence ao" domínio dos números Reais, outros autores assumem
que o leitor irá deduzir isso. Na Computação, uma variável, no escopo
onde ela é válida, sempre tem um determinado valor associado a ela, este valor
possivelmente muda ao longo do tempo, mas a cada momento o valor de uma
variável é único. Na Matemática, uma expressão envolvendo uma variável, deve
sempre considerar que aquela variável, em todo ponto que ela aparece, tem
sempre um mesmo valor. Na Computação, em particular na linguagem Java, isto não
é verdade, considere o trecho de programa abaixo:
int x;
x=(x=((x=1)+1)+1);
Na expressão acima a variável x aparece três vezes. Em cada uma das vezes x tem
um valor distinto associado a ela. (A expressão acima
não tem muito sentido (melhor escrever x=3;) mas, mais a
frente na disciplina, veremos o conceito de função ou método e teremos
oportunidade de construir expressões onde este aspecto faça mais sentido)
A
linguagem Java tem oito tipos primitivos:
É interessante
que não exista o mnemônico integer ao invés int, ou real
ao invés de float, afinal os computadores representam apenas um
subconjunto finito dos domínios infinitos correspondentes encontrados na
Matemática. O tipo byte permite representar valores de -128 a +127. O tipo
short permite representar valores de -32768 a 32767. O tipo byte é muito usado
em programas voltados para recursos computacionais, pois as variáveis deste
tipo correspondem à menor posição endereçável da maioria dos computadores.
A existência
de tipos além de disciplinar o programador é justificada por questões de
eficiência. Com a tecnologia atual ainda não temos sistemas gerais que
conseguem descobrir dinamicamente e de forma eficiente como representar os
valores com um número variável de posições de memória para os diferentes
domínios de problemas. O programador fica encarregado de definir e operar com
tipos apropriados para a solução que está sendo implementada.
Além dos
oito tipos primitivos todos os demais tipos disponíveis em Java são
genericamente conhecidos como tipo
referência. As cadeias de caracteres são relacionadas ao tipo String que
corresponde a um dos tipos referência. Nesta altura da disciplina podemos
portanto exibir a seguinte taxonomia: um tipo pode ser primitivo ou referência;
um tipo primitivo corresponde a byte, short, int, long, char, float, double e
boolean; o tipo referência corresponde a milhares de tipos entre eles o tipo
String. Além do tipo String veremos em breve o tipo arranjo(array) que também
corresponde a um tipo referência.
Cada
tipo tem literais que são as cadeias de caracteres que representam os valores
constantes disponíveis. O tipo boolean tem somente dois literais: (1) true e (2) false. Cada valor
inteiro (ou mais apropriadamente cada valor do tipo int) pode ter vários
literais, um literal decimal, um literal hexadecimal ou um literal octal
(base oito!).
O trecho
de programa Java correspondente a System.out.println(<argumento>)
ou System.out.print(<argumento>)
corresponde a invocar a impressão do argumento no video do sistema (mais à
frente veremos que corresponde na verdade a escrever o argumento na saída padrão do sistema). O argumento
deve ser do tipo String (o tipo String será visto mais à frente na disciplina).
A linguagem Java permite que o programador use como argumento uma variável ou
expressão de um tipo que não seja String, e neste caso é feita uma conversão
do argumento para o tipo String. Os programas abaixo ilustram declarações de
variáveis e comandos de atribuição conforme discutido acima. Observe o uso de
"comentários", eles podem ser introduzidos através do uso de duas
barras: //
ou ainda podem ser delimitados através do "inicio de
comentário": /* (barra seguido de
asterisco) e "fim de comentário": */ (asterisco seguido
de barra).
class VariavelTipoInt{
public static void
main(String args[]){
int i; //
declaração
i=3;
// comando de atribuição
System.out.println(i); /*Aqui ocorre uma conversão do valor 3
de tipo int para um valor do tipo String */
}
}
class VariavelTipoDouble{
public static void
main(String args[]){
double d;
//declaração
d=3.1415;
//comando de atribuição
System.out.println(d); //Aqui ocorre uma conversão do valor 3.1415
//de tipo double para um valor do tipo String
}
}
class VariavelTipoBoolean{
public static void
main(String args[]){
boolean b; // declaração
b=true; //comando de atribuição
System.out.println(b);
}
}
class DuasVariaveisTipoInt{
public static void
main(String args[]){
int i; //
declaração
int j; //
declaração
i=5;
// comando de atribuição: expressão simples
j=i/2; //
comando de atribuição: expressão envolvendo divisão
System.out.println(j);
}
}
Literais
Um literal
é a forma de representar no texto de um programa um valor de um certo tipo. A
linguagem Java apresenta 6 tipos de literais:
IntegerLiteral
FloatingPointLiteral
BooleanLiteral
CharacterLiteral
StringLiteral
NullLiteral
Destacamos
o tipo referência String em função de ser o único tipo referência que possui
literais. Os literais String (cadeias de caracteres) consistem de
sequências de caracteres delimitadas por aspas (p.ex. "A",
"AB", "ABC", "ABCD"). Existem também expressões
do tipo String. O operador + (adição) além de representar a soma de inteiros ou
soma de ponto flutuante pode representar a concatenação de cadeias de
caracteres. A cadeia "ABCD" é equivalente à expressão "AB"
+ "CD" ou à expressão "A" + "BCD". O operador + é
dito ser um operador polimórfico.
Os literais
inteiros podem ser expressos por seqüências de caracteres em decimal (p.ex. 1,
23, 456), seqüências de caracteres em hexadecimal precedidas por 0x ou 0X (0X1,
0xA, 0xFF02) ou seqüências de caracteres em octal precedidas por 0 (sim! um
zero à esquerda! p.ex. 01, 023, 0456). Os literais inteiros podem apresentar
como sufixo a letra L maiúscula ou minúscula, neste caso o literal inteiro
passa a ser do tipo long (p. ex. 23L, OXAl, 026L). Os literais inteiros podem
designar valores para variáveis do tipo int, long, short, byte e char.
A
linguagem Java tem regras sobre quais literais, com quais valores, podem ser
atribuidos a variáveis inteiras. O compilador faz as conversões necessárias e,
internamente, todos valores são representados através de zeros e uns. O trecho
de programa abaixo mostra uma inicialização ou iniciação e a impressão deste
valor em diferentes bases:
int x=14; /* int
x=0xE; ou int x=016; */
System.out.println(x);
/* conversão padrão: base 10 */
System.out.println("base
3: "+x/(3*3)%3+x/3%3+x%3);
System.out.println("base
2: "+x/(2*2*2*2)%2+x/(2*2*2)%2+x/(2*2)%2+x/2%2+x%2);
O trecho acima imprime:
14
base 3: 112
base 2: 1110
É um exercício interessante imprimir os valores em diferentes bases. Não é
preciso iterar para saber a quantidade de dígitos necessários uma vez que, para
cada base, o maior número possível de dígitos é fixo. No caso da base 10 uma
variável int pode conter um máximo de 10 dígitos decimais. No caso de uma
variável long, ela pode guardar valores com 64 digitos binários.
Os
literais de ponto flutuante tem as seguintes partes: parte inteira, ponto
decimal, parte fracionária, expoente e sufixo. O expoente, se presente, é
indicado pela letra e ou E seguido de um
inteiro. Os literais de ponto flutuante podem designar valores para variáveis
do tipo float e double. O maior literal do tipo float é 3.40282347E+38F, o
menor positivo finito diferente de zero é 1.40239846E-45F. O maior literal
double positivo é 1.79769313486231570E+308, o menor positivo diferente de zero é
4.94065645841246544E-324
Os tipos
float e double, segundo a recomendação do padrão IEEE 754 podem representar
infinito. Não existem literais para infinito devem ser usadas expressões do
tipo 1f/0f ou -1d/0d, outra opção é o uso de constantes prédefinidas:
Double.POSITIVE_INFINITY
Double.NEGATIVE_INFINITY
Os
literais float e double somente podem ser expressos na base dez.
Os
literais de caracteres são sequências de caracteres entre apóstrofos. O literal
correspondente à letra a minúscula é 'a', este é um exemplo de literal de um só caractere mas
existe também a Seqüência de Escape. A representação da barra invertida:
'\\', a representação de aspas '\"' e a representação do
apostrofo: '\''. Alguns literais
de caracteres representam caracteres que podem ser tratados de forma especial
por alguns dispositivos de entrada e saída: '\n' (newline), '\t' (tabulation),
'\b' (backspace), '\r' (return), '\f' (form feed). É importante entender que um
caractere em um vídeo ou impressora, em geral, corresponde a um valor do tipo int,
a linguagem Java possui o tipo char para poder explicitar que estamos
tratando de um valor int que deve ser considerado como um caractere. Estes dois
tipos têm similaridades mas são distintos! Considere o programa abaixo:
class prog{
public static void
main(String[] args){
int i;
char c;
i=65;
c=65;
System.out.println(i);
System.out.println(c);
}
}
O
programa acima imprime uma linha com:
65
correspondente à conversão do valor de i em um valor do tipo String e uma outra
linha com:
A
correspondente à conversão do valor de c em um valor do tipo String. Na
convenção seguida por Java o caractere A ('A') é codificado
usando o valor do tipo int 65. Modifique o programa acima para poder obter
diferentes caracteres quando da impressão de um valor de uma variável do tipo char.
Complete a
tabela de compatibilidade de atribuição de literais e variáveis (faça os
programas e verifique!) As colunas são os tipos de literais e as linhas
representam os tipos de variáveis.
|
literal |
Integer |
Floating |
Boolean |
Character |
String |
Null |
|
byte |
ok |
|
|
|
x |
x |
|
short |
ok |
|
|
|
x |
x |
|
Int |
ok |
|
|
|
x |
x |
|
long |
ok |
|
|
|
x |
x |
|
char |
|
|
|
ok |
x |
x |
|
float |
|
ok |
|
|
x |
x |
|
double |
|
ok |
|
|
x |
x |
|
boolean |
|
|
ok |
|
x |
x |
|
referência |
x |
x |
x |
x |
(*) |
ok |
(*) O compilador
só permite um literal String ser atribuído a uma variável do tipo String
.
A
linguagem Java permite algumas atribuições entre variáveis de tipos diferentes.
Considere o trecho de programa abaixo:
int i=3;
long j=2L;
j=i; /* permitido:
valor de um tipo mais restrito para um menos restrito */
Mas neste
outro trecho há um problema:
int i=3;
long j=2L;
i=j; /* erro!! */
A
linguagem Java exige que, na atribuição de um valor de tipo menos restrito para
uma variável de tipo mais restrito, seja usado, de forma explícita, um
mecanismo denominado "elencamento" (cast). O
"elenco" de valores de uma variável do tipo long é maior que o
"elenco" de valores do tipo int. A linguagem Java exige que o
programador explicite o elencamento do valor long para um valor int.
Grosseiramente é como se a linguagem obrigasse o programador a declarar que ele
sabe que aquela atribuição pode resultar em problemas, ou seja é mais uma
medida de disciplinamento do programador.
int i=3;
long j=2L;
i= (int) j; /* ok
*/
j=9999999999L;
i= (int) j; /*
permitido mas o valor em j não e' preservado */
A
conversão em um elencamento envolve, em geral, abandonar alguns bits de mais
alta ordem. No trecho abaixo a variável b é iniciada com um valor que no
contexto do tipo short é negativo, mas que ao ser elencado perde alguns bits e
passa a ser, no contexto do tipo byte, um valor positivo! No trecho abaixo o
conteúdo de s é -134 e o conteúdo de b é 122.
short s= -134;
byte b = (byte) s;
System.out.println(s);
System.out.println(b);
Uma outra
tabela trata da questão da atribuição entre variáveis de tipos primitivos
diferentes. Considere a atribuição A=B; O tipo de A corresponde às linhas e o
tipo de B corresponde às colunas. Complete a tabela abaixo, onde inválido
significa que a linguagem não permite a atribuiçao nem mesmo com o uso de
elencamento (cast), auto significa que a conversão é feito de forma
implícita e elencar significa que a atribuição só é possível através do
uso de elencamento.
|
A
B- |
long |
int |
short |
char |
byte |
double |
float |
boolean |
|
long |
- |
auto |
auto |
auto |
auto |
elencar |
elencar |
inválido |
|
Int |
elencar |
- |
|
|
|
|
|
inválido |
|
short |
elencar |
elencar |
- |
|
|
|
|
inválido |
|
char |
elencar |
elencar |
|
- |
|
|
|
inválido |
|
byte |
elencar |
elencar |
|
|
- |
|
|
inválido |
|
double |
auto |
auto |
|
|
|
- |
|
inválido |
|
float |
auto |
auto |
|
|
|
|
- |
inválido |
|
boolean |
inválido |
inválido |
inválido |
inválido |
inválido |
inválido |
inválido |
- |
Observe
que a linguagem Java não permite a mistura do tipo boolean com nenhum outro
tipo. Em algumas linguagens, como por exemplo C e C++ é permitido tratar
valores booleanos como sendo inteiros e vice-versa. Nestas linguagens o valor
false corresponde ao valor zero (zero inteiro) e o valor true a valores
inteiros diferentes de zero.
Exercícios:
Um dos problemas enfrentados por quem aprende novos conceitos são as colisões
de representação. Na linguagem Java um símbolo tal como como o 1 pode representar um valor do tipo int, um
valor do tipo char ou um valor do tipo String (que será visto a frente)
ou um valor do tipo short. Não é uma questão trivial entender que o valor
int representado por 1 não é a mesma coisa que
o valor char representado por 1. O
problema é que estamos falando de diferentes "uns" representados pelo
mesmo símbolo: 1. O valor char 1 corresponde ao
valor int 49.
Escreva um
programa em Java que ilustra o que foi dito acima.
Uma
variável (local) só fica disponível no ambiente de execução Java quando o fluxo
de controle atinge a declaração da variável. O compilador Java não deixa o
programador utilizar uma variável (local) cuja declaração ainda não foi
atingida pelo fluxo de execução. Tente compilar o programa abaixo. A mensagem
de erro do compilador é “cannot find symbol”.
class erroDeCompilacao{
public static void main(String[] arg){
a=8;
int a;
a=7;
}
}
Uma variável pode ser declarada sem
ser iniciada/inicializada. O compilador Java não deixa o programador consultar
(ler) o valor de uma variável que não foi iniciada ou que não teve alguma
atribuição. Tente compilar o programa abaixo. A mensagem de erro do compilador
é “variable v might not have been initialized”.
class
erroDeCompilacao{
public static void main(String[] arg){
int v;
int a=v;
}
}
d