Enviar um café pro programador

Pode me ajudar a transformar cafeína em código?

Arrays em métodos: passagem por valor e passagem por referência

Vimos em nosso curso de Java uma seção só para métodos, e estamos em outra seção da apostila só para arrays.
São duas coisas importantes, e a interação entre elas também é importante.
Porém, há alguns detalhes que precisam ser esclarecidos e comentados, que veremos agora neste artigo.

Passando arrays/vetores para methods/métodos

Para chamar os métodos usando arrays como argumentos, não há segredo.
Faça como sempre:

nome_do_metodo(nome_da_variavel);

Não se preocupe com o tipo e com os colchetes, [], pois os métodos já estão preparados e sabem que o que você está mandando é um array e não um tipo comum.

Para declarar, porém, é que há a diferença:
public static tipo_do_retorno nomeDoMetodo(tipo_do_parametro[] nome_da_variavel);

Por exemplo, vamos usar o exemplo do código do artigo passado de nosso curso para criar um método chamado 'exibeMatriz' que exibe uma matriz.
Esse método exibe uma matriz 3x3 de inteiros.

public static void exibeMatriz(int[][] Mat){
//codigo
}

Note que nossa matriz tem duas dimensões, então usamos dois pares de colchetes.

Colocando o código que exibe dentre deste método e fazendo o chamado dentro da main, nosso exemplo ficará assim:

Código Java: Preenche e exibe uma Matriz 3x3

import java.util.Scanner;

public class arrayPraMetodo {
    
    public static void exibeMatriz(int[][] Mat){

        System.out.println("\nA Matriz ficou: \n");
        for(int linha=0 ; linha < 3 ; linha++){
            for(int coluna = 0; coluna < 3 ; coluna ++){
                System.out.printf("\t %d \t",Mat[linha][coluna]);
            }
            System.out.println();
        }
    }
    
    public static void main(String[] args){
            int[][] matriz = new int[3][3];
            
            Scanner entrada = new Scanner(System.in);
            System.out.println("Matriz M[3][3]\n");
            
            for(int linha=0 ; linha < 3 ; linha++){
                for(int coluna = 0; coluna < 3 ; coluna ++){
                    System.out.printf("Insira o elemento M[%d][%d]: ",linha+1,coluna+1);
                    matriz[linha][coluna]=entrada.nextInt();
                }
            }
            
            exibeMatriz(matriz);
            
           
        }


}


Passagem por valor e passagem por referência

Se você já estudou outras linguagens, como C ou C++ sabe o que são esse tipo de passagem e sua importância, assim como sua confusão.

Muitas vezes queremos passar um valor pro método para que este valor seja alterado. Neste caso, estamos passando por referência, ou seja, é como se estivéssemos passando realmente a própria variável pra ser modificada e receber outro valor dentro do método.

Quando passamos por valor, é como se o método tirasse uma 'xerox' ou uma cópia de nossa variável e trabalhasse com essa cópia, e não alterasse nossa variável. Altera e trabalha apenas com a cópia, deixando a nossa original intacta.

Ambos tipos de passagem são úteis e tem suas funcionalidades dependendo do que se queira fazer.
Porém, na linguagem de programação Java, a passagem é sempre, SEMPRE, feita por valor.
Sempre que criamos uma variável de um tipo, passamos uma cópia de seu valor para o método.

Veja este exemplo, onde definimos um numero de valor 2112, dobramos ele no método e imprimimos de novo. Seu valor não se altera:

Código Java: demonstrativo da passagem por valor, o valor não se altera no método


public class passagemPorValor {
    
    public static int dobra(int num){
        return num*2;
    }
   
    public static void main(String[] args){
            int numero=2112;
           
            System.out.println("O valor de numero é: " + numero);
            System.out.println("Dobrando seu valor.");
            dobra(numero);
            System.out.println("Agora o valor de número é: " + numero);
           
        }

}


A variável 'num', do método 'dobra()' é uma cópia da 'numero' do método 'main'.
O valor do 'numero' não é tocado nem visto pelo method 'dobra()'.


Os tipos de referência em Java

Existem tipos em Java que são chamados de 'tipos de referência, que são variáveis que, por natureza, recebem uma referência, ou seja, elas apontam para outro valor (mais especificamente, elas não armazenam o valor na memória, elas armazenam um endereço de outro bloco de memória - ou seja, tem uma refêrencia desse outro endereço de memória).

Como exemplo, sabemos que o Java trata objetos, de uma maneira geral, e os nosso tema atual de estudo, os arrays, por referência. Logo, as classes e nossos arrays são tipo referência.

Quando declaramos um objeto por exemplo, com o nome 'meuObjeto', na verdade esse 'meuObjeto' vai guardar uma referência do objeto real. Ou seja, em Java, os tipos referências não armazenam na memória o valor, e sim um endereço - quem já tiver estudado C pode pensar logo em ponteiro!

Isso tudo se deve ao fato que objetos e arrays não possuírem tamanhos definidos, como os tipos primitivos. Então fica mais 'complicado' pro Java tratar esse tipo de dado, pois eles não tem um tamanho exato.
Então, ao invés de lidar com seus valores exatos (value), ele trata eles por referência (reference).

A confusão reside no fato de que, quando passamos esses tipos de variáveis para os métodos, elas mudam de valor! Mas peraí, por quê?
Como assim? Mas, em Java, a passagem não é sempre por valor?

Na verdade, há dois tipos de chamadas: os que usam cópia dos tipos primitivos e os que usam cópias da referência de objetos. Porém, como o assunto é um pouco avançado e não vimos objetos, darei uma explicação simplificada.

Nos valores do tipo 'primitivo', uma cópia é gerada no método e todas as alterações são feita nessa cópia, ok?

Nos valores do tipo 'referência' o mesmo, pois Java só passa por valor! Ok?
Então, no método, é criada outra cópia do objeto! Ou seja, temos outra referência apontando pro mesmo local na memória. Nada demais até aqui.

Na passagem do tipo por 'referência', essa segunda cópia, dentro do método, altera a referência. Mas a referência aponta pra um local na memória! Então, alteramos o local na memória quando mudamos a referência!

Ora, se as duas variáveis apontam pro mesmo local e uma delas muda esse local, o local fica mudado para as duas, ué. Esse é o truque!


Exemplo de como funciona a passagem dos tipo referência


Se eu eu declaro o meu tipo referência original com o valor 'x', na verdade essa minha variável aponta pra memória onde esse valor realmente está.
Quando mando pro método, o método cria uma cópia dessa variável do tipo referência, porém a danada dessa cópia também aponta pro mesmo local da memória. Logo, temos duas variáveis apontando pra um mesmo local da memória, o local onde está o 'x'.


Mas o método prossegue e altera o valor 'x' dessa cópia. Na verdade ele alterou foi o valor 'x' na memória. Agora esse valor 'x' é 'x+1', e o método acabou.

De volta pra onde o método foi chamado, minha variável do tipo referência original ainda aponta pro mesmíssimo local da memória. Porém, nesse local não existe mais 'x' e sim 'x+1' e temos a falsa impressão que passamos um valor por referência. Mas passamos sim, por valor!

Veja um exemplo, onde criamos um método que calcula o traço de uma matriz 3x3.
Traço é a soma dos elementos da diagonal principal da matriz, ou seja, os elementos cujo número da linha é igual ao número da coluna:


Código Java: Aplicativo que calcula o traço de uma Matriz
import java.util.Scanner;

public class traco{
    
    public static void exibeMatriz(int[][] Mat){

        System.out.println("\nA Matriz ficou: \n");
        for(int linha=0 ; linha < 3 ; linha++){
            for(int coluna = 0; coluna < 3 ; coluna ++){
                System.out.printf("\t %d \t",Mat[linha][coluna]);
            }
            System.out.println();
        }
    }
    
    public static int traco(int[][] Mat){
        int soma=0;
        
        for(int linha=0 ; linha<Mat.length ; linha++){
            soma += Mat[linha][linha];
        }
        
        return soma;
    }
 
    public static void main(String[] args){
            int[][] matriz = new int[3][3];
         
            Scanner entrada = new Scanner(System.in);
            System.out.println("Matriz M[3][3]\n");
         
            for(int linha=0 ; linha < 3 ; linha++){
                for(int coluna = 0; coluna < 3 ; coluna ++){
                    System.out.printf("Insira o elemento M[%d][%d]: ",linha+1,coluna+1);
                    matriz[linha][coluna]=entrada.nextInt();
                }
            }
         
            exibeMatriz(matriz);
         
            System.out.println("\nO traço da Matriz é: "+ traco(matriz));
        }


}


Exemplo de passagem do tipo referência na vida real

Vamos supor que eu anotei um endereço em um papel, o endereço de um prédio.
Em suma: eu tenho uma referência desse prédio.

O papel é minha variável do tipo referência. Ou seja, ela não tem o prédio. Ela tem o endereço dele (referência). Bem claro agora, não?

Agora vamos fazer um 'método'. Ou seja, vamos tirar uma cópia desse papel e dar a um homem-bomba.
Ok, agora o homem bomba tem um papel com a referência também!
Os dois papéis apontam pro mesmo local: o prédio.

É isso que o método faz. Cria uma cópia da referência - por isso é passagem por valor, pois trabalha com a cópia.

Então, agora nosso homem-bomba irá trabalhar com essa cópia (como o método faz).
Ele vai lá e explode o prédio.
Ele mexeu no meu papel? Não, continua intacto, como prediz a passagem por valor.

Porém, ele alterou o lugar para onde meu papel fazia referência.
Ele alterou isso baseado no papel dele. Mas o papel dele tinha o mesmo endereço do meu!
Então, se ele alterou pra onde apontava - pra onde fazia referência - essa mudança vai acontecer pra mim também, pois minha referência também apontava pro prédio!

Em suma, se ficou confuso, aqui vai um resumo do que nos interessa neste ponto do curso:
- se passar tipos primitivos (int, float, double etc), eles não serão alterados pelo métodos
- se passar arrays, eles serão alterados pelos métodos. Bem como todo tipo de Classes

Para ler mais sobre isso, Java in a Nutshell: http://docstore.mik.ua/orelly/java-ent/jnut/ch02_10.htm

2 comentários:

Kevin Martins disse...

Eu só fui entender nas ultimas linhas do artigo kskksks. Mas muito obrigado!

Kevin Martins disse...

só fui entender nas ultimas linhas. kkkkk Mas muito obrigado!

Contribuir com o Java Progressivo

Que tal apoiar e fazer crescer o ensino da programação no Brasil ?

Ajudar nosso país a crescer e se desenvolver cada vez mais, tecnologicamente?

Clica abaixo pra saber mais!

Apoiar o Projeto Progressivo


Tutoriais de Java