sábado, 5 de setembro de 2009

Métodos,Frames, Parametros, Argumentos



Sumário:
Básico sobre Processos, Stack,Operand Stack Local Variable, Referência a Runtime Constant Pool,Declarando métodos, Parametros, Argumentos, Retorno de Frames

Grande parte do seu código, deve ser separado por tarefas fazendo com que crie-se um programa melhor Orientado a Objetos.

Antes de tudo, Terei que introduzir alguns conceitos:



Oque é um processo?
Um programa em execução.

Oque temos em um processo?
Todo processo tem ao menos uma thread.


Oque temos em uma thread?


Basicamente uma Stack, que é um empilhador de frames.Métodos quando são invocados, sobe -se uma FRAME NA STACK.Então em um processo temos ao menos uma thread, cada thread tem uma Stack.

Oque temos em uma Stack?

Basicamente Frames(métodos quando são invocados).


Oque eu preciso para que um programa seja um processo?


De um método que será executado, que ficará no fim da STACK -Normalmente main - É assim que a JVM sabe quando executar um método, é sempre oque estiver no TOP da STACK, Só temos 2 métodos capazes de fazer isso, e um deles é o main.

Oque são Frames?

Métodos em execução.


Oque temos em Frames?

Toda Frame tem:

- Local Variable - São as variaveis locais ao Frame(Ou seja variáveis locais ao método em execução)
- Operand Stack - Todas as operações são feitas aqui, é onde trabalhamos em java normalmente também é usada para receber retornos de métodos e para preparar parametros para passar para argumentos.
- Uma referência a Runtime Constant Pool -> É A posição 0 da Local Variable.

Somente importante lembrar é que existe alguns modelos de implementação da JVM, onde pode ser visto no tutorial de threads, nesse caso estou assumindo que a JVM esta usando threads nativas com modelo ROUND-ROBIN ou seja do S.O. Nativo. Normalmente as threads são encapsuladas pela JVM, ou seja não ficam espostas ao KERNEL...Para quem esta estudando agora não é interessante saber isso, para quem ja tem um carga em cima de java isso é bem importante.VEJA O TUTORIAL threads.

- Uma stack também armazena Parametros quando estão preparados para contrabater com os argumentos do Frame.

Como Declarar um método?
Tudo entre [] é opcional:


[modificador_de_acesso] tipo_retorno identificador([argumentos]){

bloco_de_codigo

}
*modificador_de_acesso -> São varias palavras reservadas(keywords) que indicam como o método sera acessado, é opcional

*tipo_retorno
-> É o retorno que seu método terá a quem invocou(chamou) ele, se não quiser nenhum tipo de retorno deve-se usar a keyword void.So podemos ter um tipo_de_retorno e se for diferente de void, deve-se usar a keyword return dentro do bloco_de_codigo do método.


*identificador
-> O nome que você atribui ao método, não deve ser nenhuma keyword.


*([argumentos])
-> È uma lista de variáveis que o método aceita como parametro, se nenhum argumento for necessario, apenas não especifique nada, Perceba que os Parenteses ( ) não são OPCIONAIS apenas os Argumentos são.


*bloco_de_codigo
-> São o conjunto de instruções feitas pelo método.




Temos 2 tipos de métodos:
- O chamador - Um método que invoca outra
- O trabalhador - O método que trabalha a quem invocou ele




Invocando métodos: Invocar método é o ato de chamar o método, dizer olhar ta na hora de trabalhar.Nesse exemplo usaremos uma classe com main(ou seja um processo), no main() instanciaremos a classe e invocaremos um método do objeto, Vejamos primeiro em código java:



public class MinhaClasse{


public static void main(String array[]){


}


}

Supondo que executemos assim, acabamos de criar um programa, que será um processo pois tem o main() no fim da Stack. Todo processo tem uma thread, toda thread tem uma Stack, e toda Stack tem um método que é executado, vejamos:




Clique na imagem para ampliar






Agora vamos instanciar um objeto:

public class MinhaClasse{


public void metodo(){


}

public static void main(String array[]){

MinhaClasse d = new MinhaClasse();

d.metodo(); //Invocação do método- (main é Chamador e, metodo() é trabalhador)

}


}


Vejamos oque acontece no ponto de invocação do método(ou seja d.metodo();):





Clique na imagem para ampliar, Lembre-se que o método que é executado é sempre aquele que estiver no TOP da STACK, ou seja nesse caso é o metodo(). Vejamos oque lemos em cima nesse tutorial: Todo processo tem uma thread, toda thread tem uma STACK, toda Stack tem frames. Um frame é um método em execução, UM frame tem Local Variable, Operand Stack e uma referência a Runtime Constant Pool.




Invocando método da mesma classe:
Apenas inclua o nome do método e os parametros. Nesse caso abaixo, metodo() invoca metodo2(); - EM questão de Frames é a mesma coisa que invocação de métodos de classes Externas

Vejamos
:

public class
MinhaClasse{

public void
metodo(){
metodo2(); //metodo() é trabalhador de main e, chamador de metodo2() //bloco_de_codigo }

public void metodo2(){
//é trabalhador de metodo()
//bloco_de_codigo

}


public static void main(String array[]){


MinhaClasse d =
new MinhaClasse();
d.
metodo(); //Invocação do método (main é Chamador e, metodo() é trabalhador)

//bloco_de_codigo


}
}

Quando uma Instancia da JVM e
stiver executando metodo2(), oque teremos ? UM processo que tem uma thread que tem uma STACK, que terá 3 frames vejamos:




Clique na imagem para ampliar. Cada método quando esta em execução vira uma FRAME na STACK. Quando termina-se a execução do método, a Frame deixa de existir.



Supondo que metodo2() terminou o bloco_de_código dele
, então a instancia da JVM volta a executar o - frame metodo - no //bloco_de_codigo de metodo logo após a invocação de metodo2();


Vejamos:


Clique na imagem para ampliar,Veja que o frame que esta em runtime agora é o metodo, main esta aguardando e metodo2 ja terminou o bloco_de_codigo dele.
Supondo agora que o frame metodo terminou o bloco_de_codigo dele, então a Instancia da JVM vai para o bloco_de_codigo de main logo abaixo da invocação de metodo() ou seja aonde esta escrito em
main
//bloco_de_codigo . Vejamos:






Clique na imagem para ampliar, Veja a diferença, Quando um método esta em execução e quanto termina o bloco_de_codigo dele.
Vejamos agora oque acontece quando a Instancia da JVM acaba o bloco_de_codigo de main:








Clique na imagem para ampliar,
Uma Stack sem frames. Como a jvm executará algo se não tem nada? Logo termina a STACK, que termina a thread, que termina o processo, chegamos então a:






Clique na imagem para ampliar,Ou seja terminou o processo. Por isso que a Stack vai empilhando os Frames(que são métodos quando são invocados). E vai tirando da STACK os frames conforme o bloco_de_codigo do frame(ou seja do método) termina, e ela volta para o frame que é chamador logo abaixo da linha de execução da invocação do método trabalhador.



OU seja:



public class MinhaClasse{

public void
metodo(){

//bloco_de_codigo


}



public static void main(String array[]){


MinhaClasse d =
new MinhaClasse();
d.
metodo(); //Invocação do método (main é Chamador e, metodo() é trabalhador)
int x = 0; // <- É executado quando a frame metodo terminar

//bloco_de_codigo


}


}


Quando o frame metodo terminar o bloco_de_codigo dele, a instrução que será executada é
int x = 0;, pois a Instancia da JVM volta para o Frame main na instrução logo abaixo da INVOCAÇÃO do Frame trabalhador, main aqui é chamador.



Passagem de Parâmetros aos Argumentos dos métodos e Retorno de Valores:



Argumentos
são valores recebidos na invocação de um método, que são apenas declarações de variáveis local:


Declarando variavel local:

tipo identificador;

Parâmetros
são os valores que são passados aos Argumentos do Método, você pode passar valores literais(Primitivos), variáveis de referência ou varáveis Primitivas, Igual ao feito com valores literais, Se for passar uma String como parâmetro use aspas duplas: "String", se for passar um float, não se esqueça do f ou F: 20F ou cast etc.
Os Paramâmetros passados aos argumentos do método devem ter mesma ORDEM,TIPO e QUANTIDADE doque os Argumentos.
Retorno de um método
é feito especificando na Declaração do método ou seja o tipo de retorno, UM MÉTODO so pode retornar um VALOR.(Quando você especifica que um método vai retornar um valor você deve usar a keyword - return - para dizer oque será retornado(use return Dentro do bloco_de_codigo do método)) - Se um método não retorna nada, você é obrigado a usar void, usando void não se usa a keyword return, ja que nada é retornado.
O Método Chamador passa Parametros ao Método trabalhador.




Vejamos exemplos:


public class Test{


public void go(int x){



}

public static void main(String arra[]){


Test d = new Test();
d.go(10); //Invocação com passagem de Parametros ao argumento de go(int x)
}

}

Como fica isso em bytecode?


Resumindo oque interessa ver agora, assim:
Isso é oque ocorre na frame de main(),

Code:

0: new #2; //class Test

3:dup
4: invokespecial #3; //Method "":()V

7: astore_1

8: aload_1

9: bipush 10
11: invokevirtual #4; //Method go:(I)V

14: return



E como fica isso desenhando?

Essa Operand Stack e Local Variable são do Frame de main, Lembre-se TODO FRAME TEM UMA OPERAND STACK E UMA LOCAL VARIABLE alem do 0 da Local variable que é a referência para a Constant pool da classe. Vejamos Opcode por Opcode desse bytecode:

0: new #2; - Cria um espaço em memória do tamanho que será o Objeto do tipo Test e joga uma referência na Operand Stack(POREM NO new ele SÒ CRIA O ESPAÇO DE MEMÒRIA), Lembre-se o Objeto não existe ainda só existirá em RUNTIME com dynamic Linking na Runtime Constant Pool. Por isso é uma referência simbólica, o #2 aponta para o #2 da Constant Pool do Class File(Ou seja referência simbólica).





Clique na imagem para ampliar



3: dup;
- Duplica oque estiver no Top da Operand Stack





Clique na imagem para ampliar



4: invokespecial #3;
- Invoca o construtor da Classe(Veremos isso depois) que constroi agora o objeto. new só cria o espaço de memória, dup duplica e invokespecial #3 chama o construtor da classe (//Method "":()V)


Clique na imagem para ampliar



Depois que o invokespecial o seu trabalho:






Clique na imagem para ampliar
SOMENTE EM RUNTIME, depois do dynamic linking teriamos agora sim o objeto, deses jeito(isso ocorre depois que o invokespecial #3; faz seu trabalho e em Runtime claro):




Clique na imagem para ampliar


7: astore_1
-> Mandou armazenar o valor que estiver no TOP da Operand Stack como uma variavel local no indice 1 da Local variable O OBJETO SO ESTARÁ la em RUNTIME(Tempo de execução, ou seja quando você mandar executar seu Class File, EM tempo de compilação so temos referências simbólicas a Constant Pool do Class file exemplo: new #2; )






Clique na imagem para ampliar
8: aload_1 -> carrega o indice 1 da Local Variable para o TOP da operand Stack O OBJETO SO ESTARÁ la em RUNTIME(Tempo de execução, ou seja quando você mandar executar seu Class File, EM tempo de compilação so temos referências simbólicas a Constant Pool do Class file exemplo: new #2; )






Clique na imagem para ampliar,Veja que oque estiver na Local Variable é COPIADO na Operand Stack, e tudo que estiver na Operand Stack é arrancado. Portanto java trabalha com o conceito de COPIA DE VALORES.




9:bipush 10-> Empura 10 na Operand Stack O OBJETO SO ESTARÁ la em RUNTIME(Tempo de execução, ou seja quando você mandar executar seu Class File, EM tempo de compilação so temos referências simbólicas a Constant Pool do Class file exemplo: new #2; )







Clique na imagem para ampliar, Lembre-se que tipos primitivos usamos em Binário, so que aqui estou colocando em DECIMAL para ficar mais facil de entender 11:invokevirtual #4; //Method go:(I)V -> Invoca-se o método go e passa para ele o inteiro que estiver no TOP DA OPERAND STACK, a invocação do método requer o endereço do objeto e os PARAMETROS ao ARGUMENTO vejamos:

Antes:








Clique na imagem para ampliar






Depois:



Clique na imagem para ampliar







Veja como é feita a verificação de parametros e argumentos:


É assim que é feita a passagem de parâmetros aos argumentos.



Veja como ficou o frame de public void go(int x):

Veja que o Parametro fica sendo a variavel local, lembra-se que todo Argumento é uma variavel local! pois bem são as primeiras da sua local variable do Frame. OU seja Parametros que são avaliados nos Argumentos do Frame acabam se tornando variáveis locais ao FRAME após tudo.








Oque acontece se eu colocar mais de um argumento?
Os Parâmetros devem ser iguais em ORDEM,TIPO e QUANTIDADE vejamos o exemplo:



public class Test{

public void go(int x, byte c, String d){


}

public static void main(String arra[]){
Test d = new Test();
d.go(10,(byte)2, "String" );//MESMA ORDEM TIPO E QUANTIDADE DE PARAMETROS E ARGUMENTOS

}

}

Veja que á necessidade de especificar corretamento o tipo de dados, exemplo:
(byte) 2, leva-se em conta que todo inteiro é um int, portanto necessário cast.

E como fica isso visualmente?


Clique na imagem para ampliar, Verifique a ORDEM o tipo e A QUANTIDADE DE PARAMETROS E ARGUMENTOS, vejam que são iguais.Os parametros são trabalhados na Operand Stack do Frame antes de serem passados como parametros realmente, e em um único fluxo vão todos em mesma ORDEM,TIPO e Quantidade.




Retorno:

Preciso Realmente fazer algo com o retorno do método?

Não se você não quiser, so invoque o método e não faça nada com o retorno

Vejamos como isso ocorre:

public class Test{

public int go(){
return 10; //Veja que é obrigatório o uso da keyword return e o valor tem q
//ser o mesmo do declarado no método ou seja, 10 é int

}

public static void main(String arra[]){
Test d = new Test();
int x = d.go();//agora a minha variavel local int x, vale 10.

}

}

Em bytecode temos:
ISSO É A FRAME MAIN, REPITO A MAIN FAZ ISSO

0: new #2; //class Test
3: dup
4: invokespecial #3; //Method "":()V
7: astore_1
8: aload_1
9: invokevirtual #4; //Method go:()I
12: istore_2
13: return




Como ja vimos oque cada Opcode faz, então pularei para a linha 8 em diante:

8: aload_1->


Clique na imagem para ampliar


9:invokevirtual #4; //Method go:()I->veja que esse método tem RETORNO, I

Antes


Depois(Aqui esta o retorno do método trabalhador):



Lembre-se tipos primitivos são escritos em binário porem usarei em decimal por ser mais facil de visualizar. Perceba que o retorno do frame Trabalhador(OU seja go) agora esta na OPERAND STACK DO frame Chamador. Frame main = Chamador, go = Trabalhador


12: istore_2


Agora temos uma variavel local com o valor que retornou.




A relação que se faz em opcode e java é a seguinte:


Test d = new Test();
1 - new = new e dup
2 - Test() = invokespecial #3; //Method "":()V -> o #3 pode mudar, depende em qual indice da constant Pool será colocado o construtor
3 - Test d = astore_1, Quando ele manda armazenar ja tem o espaço certo que uma instancia daquele tipo pode ocupar, por isso é necessário colocar
Test d -> tipo identificador, para o compilador ja calcular o espaço necessário.



Resumo:

- Boas práticas a uma especificação(Classe) é que ela contenha entre 5 ~ 8 métodos, cada com linhas entre 20 ~ 25
- Todo método com modificador de acesso public é considerado uma interface pois define como sera a interação dos objetos
- O método que é executado é aquele que estiver no Top da Stack, ou seja aquele que foi o ultimo a ser invocado
- Você pode invocar métodos da mesma Classe ou de outras Classes
- Você pode chamar métodos em qualquer ordem, Supondo que em uma classe eu tenha a declaração de MetodoA antes da declaração de MetodoB...Se eu invocar MetodoB e depois MetodoA quer dizer que MetodoB termina primeiro, A ordem de declaração na classe Não IMPORTA!!
- Para invocar métodos da mesma classe apenas inclua o nome dele e os argumentos para ele
- Em argumentos:
*Temos promoção de primitivos
*É necessário cast, principalmente em literais onde todo numero é considerado int
public void go(byte x)
x++;

go(10); -> Erro de compilação


- Argumentos são variáveis Locais que podem ser usadas no bloco_de_codigo do método, e quando passamos parametros aos argumentos, ele são armazenados automaticamente com o valor do parametro(Na variavel local).
- Sempre passe os Parâmetros na mesma ORDEM,TIPO e Quantidade que estiver nos Argumentos.
- A stack , representa Frames e parametros para contrabater com os argumentos.
- Temos os métodos chamadores e os trabalhadores
- Os métodos Chamadores quando invocam um método trabalhador os parametros que o chamador passa para o trabalhador, o trabalhor verifica se tem mesma ORDEM,TIPO e Quantidade dos argumentos e se tiver agora serão variaveis locais ao frame do método trabalhador.
- O tipo de retorno de um método trabalhador será jogado na Operand Stack do método Chamador.
- Argumentos são as primeiras variáveis locais de um método

Cuidado:
*Com Parametros a argumento - veja:

public void go(byte x){

}

go((byte) 2); -> É necessário cast, ja que default de inteiros é int

Outro exemplo:
public void go(float x){

}

go(3F); -> É necessário cast, ja que default de pontos flutuantes é double

Nenhum comentário:

Postar um comentário