Possível erro no livro da Kathy Sierra (SCJP)

Boa tarde, pessoal!

Eu estava resolvendo os exercícios do capítulo 3 (Assignments) e, logo no exercício 2, me deparei com a seguinte questão:

[quote]Given:

class CardBoard {
    Short story = 5;
    CardBoard go(CardBoard cb) {
        cb = null;
        return cb;
    }
    public static void main(String[] args) {
        CardBoard c1 = new CardBoard();
        CardBoard c2 = new CardBoard();
        CardBoard c3 = c1.go(c2);
        c1 = null;
        // do Stuff
    }
}

When // doStuff is reached, how many objects are eligible for GC?

A. 0
B. 1
C. 2
D. Compilation fails.
E. It is not possible to know.
F. An exception is thrown at runtime.[/quote]

Seguindo o meu raciocínio (segundo a seqüência do código):

  1. Um objeto CardBoard é criado e referenciado por c1. A variável de referência story aponta para um objeto Short de valor 5;
  2. Outro objeto CardBoard é criado e referenciado por c2. A variável de referência story desse novo objeto aponta para o mesmo objeto Short, de acordo com a regra citada na página 236, que diz que o código abaixo é válido:
Short s1 = 5;
Short s2 = 5;

System.out.println(s1 == s2); // Imprime true, o que significa que s1 e s2 apontam para o mesmo objeto
  1. O método go(CardBoard) é chamado, com uma cópia da variável de referência c2;
  2. c2, portanto, continua apontando para o mesmo CardBoard;
  3. c3 não aponta para nenhum objeto;
  4. c1 não aponta para nenhum objeto, portanto, como não é referenciado por mais nenhuma variável, o objeto apontado por c1 fica elegível para o Garbage Collector;
  5. Mesmo se c1 for coletado, o Short para o qual c1.story apontava não fica elegível, pois c2.story ainda aponta para esse objeto!
  6. Portanto, a resposta correta, seguindo esse raciocínio, é a resposta B (1)

Entretanto, a resposta correta segundo o livro é a resposta C (2), pois, segundo o livro, o objeto Short também seria elegível.

Colegas de comunidade, vocês concordam comigo?

Você está certo - é a pegadinha em que o revisor do código caiu. Se o código original lesse:

Short story = 12345;

aí teríamos 2 objetos disponíveis para garbage collection.

Jé fazem tres anos que me certifiquei, de forma que estou um pouco enferrujado.

Os objetos C2 e C3 não seriam igual a null ?

[editado] Pensando melhor, C2 não é null não [/editado]

Márcio

o livro esta certo…

existem 2 objetos…

vc acertou q o C1 está sujeito ao coletor, SÓ QUE, dentro do seu objeto C1, vc tem uma variavel de instancia q utiliza de um wrapper…entao, assim como vc está deixando o C1 para o GC, vc tb está deixando a referencia do wrapper, SHORT

ok!?

[quote=pardal_nb]o livro esta certo…

existem 2 objetos…

vc acertou q o C1 está sujeito ao coletor, SÓ QUE, dentro do seu objeto C1, vc tem uma variavel de instancia q utiliza de um wrapper…entao, assim como vc está deixando o C1 para o GC, vc tb está deixando a referencia do wrapper, SHORT

ok!?[/quote]

???

Qual o nome desta referência do wrapper ? O que é referÊncia do wapper ? Quem é o wapper ? :oops:

???
Márcio

[quote=pardal_nb]o livro esta certo…

existem 2 objetos…

vc acertou q o C1 está sujeito ao coletor, SÓ QUE, dentro do seu objeto C1, vc tem uma variavel de instancia q utiliza de um wrapper…entao, assim como vc está deixando o C1 para o GC, vc tb está deixando a referencia do wrapper, SHORT

ok!?[/quote]

Ué, e C3 não está null tbém?

Engraçado que eu colocaria a C(2) tbém de cara, mas por achar que C1 e C3 estão null.

[quote=marcioa1][quote=pardal_nb]o livro esta certo…

existem 2 objetos…

vc acertou q o C1 está sujeito ao coletor, SÓ QUE, dentro do seu objeto C1, vc tem uma variavel de instancia q utiliza de um wrapper…entao, assim como vc está deixando o C1 para o GC, vc tb está deixando a referencia do wrapper, SHORT

ok!?[/quote]

???

Qual o nome desta referência do wrapper ? O que é referÊncia do wapper ? Quem é o wapper ? :oops:

???
Márcio[/quote]

hehehehe, tambem “viajei” :stuck_out_tongue:

Vamos lá…

C3 não é NULO… C3 faz referecia a C2…reparem que o metodo retorno uma referencia para o objeto q eh passado, q neste caso é :

[code]
CardBoard go(CardBoard cb) {
cb = null;
return cb;
}

CardBoard c3 = c1.go(c2); [/code]

C2… Por isso C3 nao eh nulo, ok?!

Seguindo…

Wrapper é um empacotador de tipos primitivos…faz com q tipos primitivos (int, flot, double …) se tornem OBJETOS(Float, Integer …)…

Na linha 2:

Temos uma variável de instância, ou seja do objeto instanciado…qnd nós instanciamos C1 (CardBoard c1 = new CardBoard(); ) ele terá essa variável de instancia com ele…isto é, “story” É uma var. de instancia de C1…e variaveis de instancia estão sujeitas ao GC…ok?!

seguindo…

qnd nós atribuimos um valor NULL para C1, ou seja, qnd tiramos o “controle remoto”, o ponteiro, para o objeto no HEAP, nós estamos deixando para trás tb o nosso objeto story…(story é um objeto, e nao um tipo primitivo, notem, Short…se fosse short, ai a resposta seria 1)

certo pessoal?! :smiley:

[quote=pardal_nb]Vamos lá…

C3 não é NULO… C3 faz referecia a C2…reparem que o metodo retorno uma referencia para o objeto q eh passado, q neste caso é :

[code]
CardBoard go(CardBoard cb) {
cb = null;
return cb;
}

CardBoard c3 = c1.go(c2); [/code]

C2… Por isso C3 nao eh nulo, ok?!

[/quote]

E como se explica isso?

[code]class CardBoard {
Short story = 5;
CardBoard go(CardBoard cb) {
cb = null;
return cb;
}
public static void main(String[] args) {
CardBoard c1 = new CardBoard();
CardBoard c2 = new CardBoard();
CardBoard c3 = c1.go(c2);
c1 = null;

       System.out.println(c1==null); // true
       System.out.println(c2==null); // false
       System.out.println(c3==null); // TRUE -> C3 É NULO
       // do Stuff  
   }  

} [/code]

Pardal, tente entender essa lógica:

  1. CardBoard.story é uma variável de instância, isso é correto. Mas repare que a a pergunta refere-se à quantidade de objetos que são elegíveis para o GC;
  2. CardBoard.story não é um objeto, é uma variável de referência, portanto, no caso, aponta para um objeto do tipo Short;
  3. Duas variáveis de referência dos tipos abaixo sempre apontarão para o mesmo objeto (para economia de memória):
    3.1 Byte
    3.2 Boolean
    3.3 Character (de \u0000 a \u007F)
    3.4 Short e Integer (de -128 a 127)
  4. Temos dois objetos CardBoard criados. Os dois têm variáveis story que apontam para o mesmo objeto Short com valor 5;
  5. Quando o CardBoard referenciado por c1 for coletado, a variável desse objeto story perderá sua referência com o Short, mas a variável story do objeto CardBoard referenciado por c2 ainda não perdeu sua referência, portanto esse Short não é elegível para o GC!!!
  6. Se o código fosse como o descrito abaixo, realmente seriam dois objetos elegíveis para o GC:
class CardBoard {

    /*
     * story é maior que 127, portanto cada story de cada objeto CardBoard 
     * criado referenciará um objeto diferente!!!
     */
    Short story = 1680;
    CardBoard go(CardBoard cb) {
        cb = null;
        return cb;
    }
    public static void main(String[] args) {
        CardBoard c1 = new CardBoard();
        CardBoard c2 = new CardBoard();
        CardBoard c3 = c1.go(c2);
        c1 = null;
        // do Stuff
    }
}

[quote=dreamspeaker][quote=pardal_nb]Vamos lá…

C3 não é NULO… C3 faz referecia a C2…reparem que o metodo retorno uma referencia para o objeto q eh passado, q neste caso é :

[code]
CardBoard go(CardBoard cb) {
cb = null;
return cb;
}

CardBoard c3 = c1.go(c2); [/code]

C2… Por isso C3 nao eh nulo, ok?!

[/quote]

E como se explica isso?

[code]class CardBoard {
Short story = 5;
CardBoard go(CardBoard cb) {
cb = null;
return cb;
}
public static void main(String[] args) {
CardBoard c1 = new CardBoard();
CardBoard c2 = new CardBoard();
CardBoard c3 = c1.go(c2);
c1 = null;

       System.out.println(c1==null); // true
       System.out.println(c2==null); // false
       System.out.println(c3==null); // TRUE -> C3 É NULO
       // do Stuff  
   }  

} [/code][/quote]

c3 é nulo! O método CardBoard.go(CardBoard) retorna uma variável de referência que não aponta para nada (nula)!
c2 não é nulo porque o que é passado para o método é uma cópia de como chegar ao objeto referenciado por ele, portanto o valor de c2 (como chegar no objeto) continua inalterado, independente do que acontecer no método! O método, todavia, pode modificar o objeto.

[quote=felipealbuquerque]
2. CardBoard.story não é um objeto, é uma variável de referência, portanto, no caso, aponta para um objeto do tipo Short;
6. Se o código fosse como o descrito abaixo, realmente seriam dois objetos elegíveis para o GC:

[code]
class CardBoard {

/*
 * story é maior que 127, portanto cada story de cada objeto CardBoard 
 * criado referenciará um objeto diferente!!!
 */
Short story = 1680;
CardBoard go(CardBoard cb) {
    cb = null;
    return cb;
}
public static void main(String[] args) {
    CardBoard c1 = new CardBoard();
    CardBoard c2 = new CardBoard();
    CardBoard c3 = c1.go(c2);
    c1 = null;
    // do Stuff
}

}
[/code][/quote]

entao me fala, quais?

se for como estão falando seriam os mesmos objetos :?

[quote=pardal_nb][quote=felipealbuquerque]
2. CardBoard.story não é um objeto, é uma variável de referência, portanto, no caso, aponta para um objeto do tipo Short;
6. Se o código fosse como o descrito abaixo, realmente seriam dois objetos elegíveis para o GC:

[code]
class CardBoard {

/*
 * story é maior que 127, portanto cada story de cada objeto CardBoard 
 * criado referenciará um objeto diferente!!!
 */
Short story = 1680;
CardBoard go(CardBoard cb) {
    cb = null;
    return cb;
}
public static void main(String[] args) {
    CardBoard c1 = new CardBoard();
    CardBoard c2 = new CardBoard();
    CardBoard c3 = c1.go(c2);
    c1 = null;
    // do Stuff
}

}
[/code][/quote]

entao me fala, quais?

se for como estão falando seriam os mesmos objetos :?

[/quote]

O CardBoard referenciado por c1 e o Short (de valor 1680) referenciado por c1.story.

Novamente: quando o Short está entre -128 e 127, duas variáveis de referência apontam para o mesmo objeto. Fora desse intervalo, vários objetos são criados.

e o C3?!

ele continua a msm coisa, entao seriam 3 objetos, nao 2, certo?!

[quote=pardal_nb]e o C3?!

ele continua a msm coisa, entao seriam 3 objetos, nao 2, certo?![/quote]

Em momento algum foi criado um objeto para c3 referenciar. Note que, desde sua inicialização, c3 == null.
Um erro que costumam-se cometer é confundir as variáveis de referência com os objetos em si (e isso é explorado no exame). Quando declaramos uma variável de referência, não necessariamente criamos um objeto junto.

agora q nao estou entendo mais nada entao…

falaram q C3 e C1 sao nulos e q estao a disposicao do GC…

qual a resposta da pergunta no livro??
2, story e C1 ??

e qual a resposta para o exemplo q vc deu?

c1 e c3 não são objetos! São apenas variáveis de referência.

Não é porque c1 e c3 são nulos que haverão necessariamente dois objetos disponíveis para o GC.

Siga a lógica do meu primeiro post que você conseguirá visualizar o porquê de a resposta correta ser somente um objeto (o CardBoard que era referenciado por c1), no exercício do livro.
No livro, erroneamente, a resposta é a © - 2 objetos (o referenciado por c1 e o Short referenciado pela variável story desse objeto)

No exemplo que eu dei com o Short maior que 127, seriam dois objetos: o CardBoard que era referenciado por c1 e o Short referenciado pela variável story desse CardBoard.

entendi o q vc quis dizer.

Se a linha 4 fosse comentada:

class CardBoard {
    Short story = 5;
    CardBoard go(CardBoard cb) {
        cb = null;
        return cb;
    }
    public static void main(String[] args) {
        CardBoard c1 = new CardBoard();
        CardBoard c2 = new CardBoard();
        CardBoard c3 = c1.go(c2);
        c1 = null;
        c2 = null;
        // do Stuff
    }
}

teríamos 2 objetos (C1 e C3), certo?

Li tudo que escreveram nesse tópico, e entendi bem.

Isso que o felipe falou fez bastante sentido,

Só não quero que pare por aqui, quero saber se realemente está errado no livro ou não ^^

Obrigado,

[quote=pardal_nb]entendi o q vc quis dizer.

Se a linha 4 fosse comentada:

class CardBoard {
    Short story = 5;
    CardBoard go(CardBoard cb) {
        cb = null;
        return cb;
    }
    public static void main(String[] args) {
        CardBoard c1 = new CardBoard();
        CardBoard c2 = new CardBoard();
        CardBoard c3 = c1.go(c2);
        c1 = null;
        c2 = null;
        // do Stuff
    }
}

teríamos 2 objetos (C1 e C3), certo?[/quote]

  • Até a linha 9, teremos c1 referenciando um CardBoard, c2 referenciando outro e suas respectivas variáveis story apontando para o mesmo Short;
  • Na linha 10, teremos c3 referenciando o mesmo CardBoard que c2;
  • Na linha 11, o objeto que era referenciado por c1 não tem mais nenhuma referência, então fica elegível para o GC;
  • O código do livro vai até aí, então, mesmo com a linha 4 comentada, teríamos apenas um objeto elegível para o GC;
  • Com a adição da linha 12, teremos… apenas um objeto elegível para o GC! Note que c2 não referencia mais nada, mais c3, que referenciava o mesmo objeto que c2, ainda o referencia.
  • Se a linha 4 não fosse comentada, c3 não referenciaria nada desde o início, então teríamos 3 objetos elegíveis para o GC: Os CardBoards para os quais c1 e c2 apontavam e, finalmente, o Short com valor 5.