java.lang.Object

java.lang.Object

Talvez uma das classes mais esquecidas do JDK, mas também umas das mais importantes a classe java.lang.Object é a classe-pai de todas as classes em Java. Essa obrigatoriedade torna a classe Object o ponto de encontro para uma série de funcionalidades oferecidas em Java.

Igualdade

Para quem já usou uma linguagem de programação antes do Java, quando começa a usar Java tem uma certa dificuldade em fazer comparações de objetos. Principalmente testar a igualdade.

Java é fortemente orientado a objetos e isso tem profundas implicações. Uma delas passa pela diferenciação entre variável primitiva e váriável de referência. Basicamente, uma variável primitiva expressa o próprio valor da variável enquanto uma variável de referência é apenas um apontador para um objeto. Como existem váriáveis de referência para objetos e os objetos em si mesmos, a linguagem tem que oferecer formas de testar a igualdade das váriaveis, por um lado, e dos objetos, por outro. O teste de igualdade de variáveis é feito pelo operador == que compara o valor da variável. O teste de igualdade de objetos é feito pelo uso do método equals() presente em todos os objetos.

equals()

Este método presente em Object, e portanto em qualquer classe Java, permite testar se um dado objeto é igual a outro. Mesmo quando as referencias são diferentes (ou seja, existem dois objetos colocados em locais diferentes da memória) os objetos apontados podem ser iguais.

01
02 Integer a = new Integer (1);
03 Integer b = new Integer(1);
04
05 // a referencia ao objeto é diferente
06 assertFalse(a==b);
07
08//mas o valor é o mesmo
09 assertTrue(a.equals(b));
10

Código 1:

O fato de equals() ser um método presente em qualquer objeto permite que a lógica que compara o objeto seja modificada conforme o tipo de objeto. Este fato é importantíssimo porque podemos ter controle sobre o tipo de comparação que estamos fazendo mesmo quando o objeto não foi codigficado por nós. Esta forma de comparação de objetos é principalmente importante quando agrupamos os objetos em coleções.

hashCode()

Este método é uma forma de tornar todas as classes java flexíveis o suficiente para poderem ser usadas no Java Collections Framework através do padrão Template Method. A importância deste método prende-se com a importância do uso de mapas como coleções e a necessidade de operações de busca rápida.

A ideia é simples. Este método deve retornar um inteiro que identifique o objeto de forma que se o número retornado por dois objetos é diferente, então os objetos são diferentes. Contudo, se o número é igual, não significa que os objetos são iguais.

Mais detalhes do uso e implementação de equals() e hashCode() no artigo sobre coleções.

clone() e Clonable

Já que a igualdade é algo definido pelo estado interno de um objecto para ter dois objetos iguais é necessário criar um código que copie esse estado para uma outra instancia do mesmo tipo de objeto. É necessário criar uma réplica do objeto cujo estados queremos duplicar. É necessário criar um clone.

Para poupar trabalho ao programador, o clone() implementa um código de cópia superficial (shalow copy). Isto significa que todos os atributos do novo objeto tem o mesmo valor que os atributos do objeto original.
Se esses objetos forem objetos imutáveis, isso é o suficiente para que a cópia seja independente do original e portanto, seu estado ser modificado sem danos ao estado do objeto original. Contudo, na maiorias das vezes estamos interessados em clonar os objetos dos atributos eles próprios. Uma clonagem em cadeia como esta é o que se chama de cópia em profundidade (deep copy).
Esta cópia em profundidade pode ainda ser seletiva, copiando alguns objetos diretamente e clonando outros.

Mas, nem todos os objetos podem ser clonados. Alguns objetos são criados para ser únicos, ou para o estado de cada um seja diferente a todo o momento. Por isso, embora todos os objetos tenham a capacidade de acessar o método clone() nem todos podem deixar que ele seja usado. Por esse motivo, o método clone() é protegido, ou seja, só acessivel por objetos que pertençam à hierarquia de Object (que são todos os objetos em Java, mas isso é um detalhe). A idéia é que o objeco use as capacidade já implementadas em clone() internamente , mas não seja obrigado a ser clonável. Para aqueles objetos que são realmente clonáveis, eles devem implementar a interface Cloneable. Esta é na realidade uma interface marcadora (ou seja, não obriga a implementar nenhum método específico) para indicar que o objeto desta classe pode ser replicado.

Redução para String

Em todas as linguagens de programação que contenham o conceito de String é muitas vezes necessário converter a informação num texto, uma String. Isso é útil, por exemplo, para debug. Espera-se que ao converter para String a informação seja útil. Para informações como número e datas a conversão para String é óbvia. Queremos uma String que um humano intreprete com o mesmo valor que o int ou a o Date original.

Mas e para um objeto que contém várias informações? A conversão para String tem que ser tal que um humano consiga entender o estado interno do objeto. Isto, claro, depende do estado do objeto e do objeto em si, mas a capacidade de converter o estado em String tem que ser comum a todos os objetos. Por isso, todos os objetos implementam toString() através de Object.

Reflexão

Se o objeto é a instância de uma classe, como saber a que classe pertence um dado objeto? A resposta é simples: invoque getClass(). Este método irá retornar um objeto Class que indica a classe a que o objeto pertence.

Embora possamos pensar em getClass() como uma forma de saber a que classe o objeto pertence, é também a porta de entrada para uma das capacidades mais supreendentes e poderosas do Java: a Relexão. A capacidadede trabalhar com os metadados dos objetos, é uma poderosa capacidade de habilita a linguagem java a ser o palco da implementação de frameworks de infraestrutrura que lêem, alteram ou escrevem a definição de objetos em runtime.

Threads

Java contém, “de fábrica”, a capacidade de trabahar com várias linhas de trabalho. Todos os programas correm em uma linha de trabalho (thread) mas elas podem-se multiplar conforme o programa avança.

O uso de threads baseia-se na idéia de sincronismo. As threads funcionam em paralelo e sem nenhuma ligação com as outras. Contudo existe pontos onde elas têm que trocar dados, ou usam objetos cuja lógica interna só comporta o uso por uma thread por vez.
Este motivo simples implica que devem existir forma de demarcar as regiões de sincronismo e de controlar quando é seguro avançar uma thread que depende do trabalho de outra(s).

Java embute então, através de Object essas capacidades em todos objetos, e por conseqüência em quaisquer threads (já que elas são também controladas por meio de objetos: Thread)

Como todos os programas java já começam funcionando numa linha própria de execução, não há como fugir da necessidade da impementação dos mecanismos relativos a threads. Embora pouco usados em programa com uma única linha de execução, os métodos notify(), notifyAll() e as várias versões de wait() são a base para a implementação de todos os mecanismo de sincronização.

Recolha de lixo

Uma das capacidade do Java que o destingue de tantas linguagens usadas antes dele é o conceito de coletor de lixo (Garbage Collector).
A idéia é que à medida que os objetos não vão sendo usados mais pelo programa, eles são colocados no lixo. Mas alguém tem que recolher e destruir esse lixo. Essa é a missão do Colector de Lixo; um algoritmo implementado na própria JVM encarregado de encontrar o lixo, recolher e destrui-lo.

O algortimo de recolha de lixo é extremamente simples, mas a sua implementação é extremamente complexa já que tudo tem que ser feito sem interferir com a performace do programa principal. Esta é uma área de pesquisa que tem dado grandes passos depois que o Java mostrou que o conceito funciona e produz uma programação mais limpa para todos os programadores.

O estado de um objeto e seus algoritmos internos podem ter uma complexidade aleatória já que não há nenhuma restrição ao que um objeto pode fazer. Por este motivo, é importante informar o objeto quando ele está sendo recolhido para destruição. O objeto tem que finalizar o que está fazendo e libertar quaisquer recursos que esteja usando. Este é o papel do método finalize(). Uma forma da JVM comunicar com o objeto e informá-lo que é hora de acabar tudo o que está fazendo. Este método é importante em framework de intraestrutura que precisa responder a ações especiais como a recolha de lixo. Os objetos comuns, do dia-a-dia não têm necessidade de implementar nenhum código neste método, e por isso, ele é noralmente pouco conhecido e usado. Mesmo assim ele é de extrema importância para o Java.

Object é um objeto que parece não ser muito útil na programação do dia-a-dia, mas é de extrema importância para a definição e funcionamento da plataforma Java. Cada grupo de métodos está ligado a assuntos, que só por si, dariam muito que falar. Fica aqui apenas uma explicação superficial para se entender a necessidade destes métodos serem definidos, tal como o são, na classe Object.

Licença


Creative Commons License
Sérgio Taborda
Este trabalho é licenciado sob a

Licença Creative Commons Atribuição-Uso Não-Comercial-Não a obras derivadas 3.0 Genérica
.

2 opiniões sobre “java.lang.Object”

  1. Muito bom seu artigo!
    Não só o artigo mas o blog como um todo!
    To acompanhando suas atualizações!
    Abraços!

  2. po legal blz tirei algumas duvidas se vc puder manda pro meu email um mini curso de java.lang.Object eu agradeço.

Deixe um comentário