Wednesday, August 01, 2007

3.3-Alguns erros a evitar.[repost7-Continuação]

A chamada a bolha(a); faz executar um algoritmo de ordenação(do menor para o maior) conhecido como BubbleSort que ordenará o array a. Esse método é ótimo para demonstração, mas péssimo em eficiência (Quando precisar de eficiência, procure usar um algoritmo como MergeSort, por exemplo).
Repare que esse método faz várias passagens pelo array comparando pares sucessivos em cada passagem. Se um par estiver em uma ordem crescente, ou os valores forem iguais, nada será mudado. Se um par estiver na ordem decrescente, seus valores são trocados no array.
No trecho: mudaValor(a,j,j+1); haverá uma chamada para trocar a posição de dois elementos para ficarem em ordem crescente. Deve ser notado que uma referência ref é passada junto a dois números inteiros que representam os índices do array, e temp será a variável que armazenará o conteúdo temporariamente de um dos valores para evitar que qualquer conteúdo seja perdido durante as atribuições.
É necessário reparar bem nessa última assinatura do método mudaValor(int[] ref,int x,int y). É a popular operação de swap (troca) de variáveis que veremos mais a fundo no próximo tópico.
Uma provável solução para a classe Confusao pode ser :

public class Solucao{ //classe com as devidas correcoes
public static void mudaArray(int a[]){
for(int i=0;i"menor"a.length;i++)
a[i]+=17;

}

public static int mudaElemento(int a){
a*=10;

return a;
}
public static void troca(int[] ref,int x,int y){
int temp;

temp=ref[x];

ref[x]=ref[y];
ref[y]=temp;

}
public static void bolha(int []a){
for(int i=0;i"menor"a.length;i++)

for(int j=0;j"menor"a.length-1;j++)

if(a[j]"maior"a[j+1])

troca(a,j,j+1);
}
public static void main(String []GUJ){
int a[]={123,6111,52,13,2};

String s="GUJ";

mudaArray(a);
for(int i=0;i"menor"a.length;i++)
//para exibir os elementos modificados
System.out.println(a[i]+"\n");

a[2]=mudaElemento(a[2]); //passagem por valor
System.out.println("Valor do elemento na posicao 3 do array:"+a[2]+"\n");
s=s.concat(" O Maior Forum De Usuarios Java Do Brasil");
System.out.println(s+"\n");
bolha(a);
//ordena os elementos
for(int i=0;i"menor"a.length;i++)
//para exibir os elementos ordenados
System.out.println(a[i]);
}
} //fim da classe



Acabamos com esse overloading desnecessário, o programador deve escrever os
métodos mais intuitivos (de fácil compreensão) possíveis, logo foi eliminada aquela
população de mudaValor em série por mudaArray, mudaIndice e troca(lembrando que
o que manipulava String foi eliminado!). Para mudaArray e bolha(que já funcionavam no
exemplo anterior) apenas incluímos o laço for para correta exibição dos elementos na tela.
Em :
a[2]=mudaElemento(a[2]);
Fez-se necessária essa atribuição porque é o único modo de se receber o resultado da
cópia passada como parâmetro para mudaElemento(), devido a passagem ser por valor.
Já para a String eliminamos o gasto de ter aquele método inútil na memória e fizemos a
concatenação (via concat()) que adiciona uma
String ao final da String s (de valor "GUJ").
É importante perceber que o objeto
String é imutável(na verdade, nem tanto, veja em http://blog.caelum.com.br/)
mas sua variável de referência na stack não!
Nesse exemplo, existem 3 objetos String em heap
("GUJ"," O Maior Forum De Usuarios Java Do Brasil" e "GUJ O Maior Forum De Usuarios Java Do Brasil"),
mas apenas um está sendo referenciado("GUJ O Maior Forum De Usuarios Java Do Brasil").
Os demais são considerados perdidos! Já pensou num programa ter zilhões de concatenações e
Strings novas sendo criadas á todo momento? Um OutOfMemoryError na sua aplicação
poderia ser catastrófico! Pense bem antes de precisar concatenar muitas Strings
(A classe
StringBuffer e StringBuilder são para isso!).E a heap pode ser relativamente
vasta. Mas não é infinita.
Essa classe de “Solução” é perfeita?Não. Longe disso. Ela apenas serve para ilustrar que é
melhor que a anterior, mas várias otimizações podem (e devem!) ser feitas. O uso do
modificador
static em excesso é uma forma pobre de programação (e proceduralização
do Java!) e o único intuito no exemplo em uso é evitar a criação de instâncias da classe
para chamar os métodos. E o método mudaElemento poderia ser eliminado apenas
colocando um if em mudaArray verificando se é dado elemento do array e efetuar a
operação desejada quando este for encontrado. Na vida real de programador é necessário
ser mais atencioso (e menos preguiçoso!) para observar as melhorias que sempre podem
ser feitas em um programa.

Nota: O tamanho da heap é calculado baseado na memória física (RAM) da máquina e é
feito pelo algoritmo que faz alocação de espaço para tenta usar o máximo possível.
Na verdade, ele tenta usar metade da memória disponível, mas se a memória for inferior a
160MegaBytes, ele (o algoritmo alocador de espaço) tentará usar o máximo possível.

Nota2: Deve-se salientar que em Java o programador não é responsável pela liberação
de memória, não possuindo qualquer capacidade de manipulação direta dos objetos
residentes em memória heap. Em C, por exemplo, o programador é responsável pela
liberação da memória em uso.Isso é perigoso!Em Java, o coletor de lixo(Garbage
Collector
) se encarrega de
limpá-la pelo programador, evitando erros como acesso a
dados que foram desalocados e estouro pela não liberação de objetos.O programador não
tem o menor controle sobre o coletor de lixo, que é uma thread de baixa prioridade da
Máquina Virtual, cuida de todo o processo.O máximo que é possível fazer é chamar

System
.gc(); para solicitar a execução do coletor, mas não é garantida a sua execução.
Cuide para que o objeto que tenha que ser coletado não possua nenhuma referência a ele
(ou uma atribuição null depois
de seu uso),pois só assim o coletor poderá entrar em ação.

3.3-Alguns erros a evitar.[repost6]

Veja a seguinte classe:

public class Confusao{ //classe com muitos erros propositais!!! ;)
    
     private static String s;
     
     public static void mudaValor(int a[]){
         for(int i=0;i"menor"a.length;i++)
           a[i]+=17;   
        }
     
     public static int mudaValor(int a){ 
        a*=10;
       return a;                       
                                       } 
     public static String mudaValor(String s){ 
        s=s.concat(" O Maior Forum De Usuarios Java Do Brasil");
            
          return s;                          }       
     public static void mudaValor(int[] ref,int x,int y){ //troca os    //elementos em um array
        int temp;
              temp=ref[x];
              ref[x]=ref[y];
              ref[y]=temp;
                                                       }
     public static void bolha(int []a){
          for(int i=0;i"menor"a.length;i++) //controla o número de passagens
           for(int j=0;j"menor"a.length-1;j++) //controla as comparações e      //trocas
              if(a[j]"maior"a[j+1]) //compara o elemento com o seguinte,se for //maior
                mudaValor(a,j,j+1); //haverá a troca.                
                                      }                                                      
     public static void main(String []GUJ){
        int a[]={123,6111,52,13,2};
     
             s=new String("GUJ");
             //super exemplo de Overloading-sobrecarga de métodos
             mudaValor(a); //muda o valor do array
             System.out.println(a); //array eh um objeto sera exibido seu                //endereço Heap
        
             mudaValor(a[2]); //muda o valor da variavel
             System.out.println(a[2]);
        
             mudaValor(s); //tenta mudar o valor da String
             System.out.println(s);
        
             bolha(a);
             System.out.println(a);
        
        }   
 }


Compile (javac Confusao.java )e rode (java Confusao).Uma classe perfeita não?
Ela é perfeita para se jogar no lixo!Os piores erros são o que passam na compilação e na
execução, pois estes são difíceis de serem depurados!Nesse exemplo será fácil de ver os
erros, mas nem sempre será assim!
Temos 5 métodos de classe além do main(), sendo
que 4 deles com mesmo nome(mudaValor), mas assinaturas distintas(a assinatura de um
método é composta de nome+lista de parâmetros).
A essa possibilidade de métodos possuírem mesmo nome, mas lista de argumentos
diferente é chamada de overloading (sobrecarga), cabendo ao Interpretador selecionar
qual método deve ser executado combinando a lista de argumentos com o parâmetro
passado a chamada do método.
A essa associação de atributos feita em tempo de compilação, é chamada de early binding (ligação prematura)
ou static binding (ligação estática); que é executada pelo Carregador de Classe
(ClassLoader), procurando os membros estáticos na sua classe. Se existirem, eles serão
carregados!

NOTA
:Quando executamos métodos não estáticos, ou algum método sofre overriding
(é subscrito) numa subclasse, o compilador não sabe qual método será chamado e isso
só é resolvido em tempo de execução, processo chamado de late binding (ligação tardia),
dinamic binding(ligação dinâmica) ou a melhor definição, virtual method invocation (invocação de método virtual)
pois os métodos "existem" no momento da execução(nenhum código extra é gerado).

Em teoria essa classe deveria adicionar 17 á todos os membros do array a, através da
chamada a primeira função mudaValor() e mostrá-lo na tela. Deveria através da chamada
a mudaValor(a[2]); mudar o valor da variável (o que não ocorre). A terceira chamada
era para retornar a String modificada "GUJ O Maior Forum De Usuarios Java Do Brasil",
mas só GUJ é retornado. E a chamada ao método bolha, era para permitir exibir no
método println() o array a ordenado do menor para o maior. Nota-se que é exibido uns
caracteres estranhos (do tipo [I@10b62c9) assim como na primeira chamada a println().
Porque isso ocorre?Porque é passado uma referência a um array em ambos os casos!
Todos os objetos tem associados a eles uma referência a própria classe, quando uma
referência a um array é passada ao método println() há uma chamada ao método ToString
que retornará o nome da classe a qual esse objeto é uma instância(no caso [I-de Inteiro),
um separador "@", e uma representação hexadecimal representando o código hash desse
objeto.
Veja o formato:
getClass().getName() + '@' + Integer.toHexString(hashCode())
Nota-se que nas duas chamadas a println(a) serão retornadas a mesma String!Isso se
deve porque o objeto está num único local na tabela
hash (esse endereço de memória de
mentirinha que é @numero_hexadecimal).Não devemos passar um objeto a println() a
menos que se queira mostrá-lo dessa forma, senão poderá ser necessário subscrever o
método println()(para retornar um objeto).
Prosseguindo com a análise do código, a chamada ao primeiro mudaValor(a) e ao
método bolha(a) tem êxito, o problema foi passar uma referência ao método println()
para exibir na tela! Deve-se usar um laço for para poder exibir os valores corretos na tela:
for(int i=0;i"menor"a.length;i++)
System.out.println(a[i]);


Dessa forma, todos os índices serão percorridos, pois ocorre uma passagem por valor
(índice após índice) a cada interação do laço.(Nota: a.length retorna o tamanho total do
vetor).

Prosseguindo, temos a segunda chamada a mudaValor() na qual é passado a[2].Exibirá
69 na tela, logo a operação teve êxito, certo?ERRADO!Deveria exibir 690, correspondente
ao mudaValor() que recebe um tipo inteiro e multiplica por 10, mas o valor da posição 3
do array (assim como o array todo) foi modificado pela chamada ao primeiro
mudaValor()na qual foi passado o array a.Ele não pode ser exibido(o array a modificado
com os novos valores) porque foi passado erroneamente a println() também! Além do
mais, quando se passa um índice (um elemento) individual de um array de tipo de dados
primitivos essa passagem é feita por valor como uma simples variável! Elementos
individuais de um array de tipo referência(
Object, String...) são passados como uma
referência a esse objeto!O valor passado (69) não foi modificado, pois a cópia de a[2]
ficou limitada a execução no escopo do método!


A terceira chamada a mudaValor() é tudo o que você não pode fazer em Java! Não há
como mudar o valor de uma determinada String numa passagem para um método porque
Strings são imutáveis!E nesse caso, o gasto de memória foi um desperdício! (Em relação
ao que pode ser feito!). Jamais tente modificar Strings via método. Quando precisar de
Strings modificáveis, use java.lang.
StringBuffer(com sincronização) ou StringBuilder
(sem sincronização, portanto mais rápida!).