Nenhum desses objetos (ResultSet, Connection e Statement) é “fechado sozinho” quando a rotina deixa de usá-los.
É melhor sempre fechá-los imediatamente depois do uso (no caso da conexão, talvez você a mantenha aberta o tempo todo, não sei como você estruturou seu programa). Use um “finally” para forçar que os objetos sejam fechados, mesmo que haja alguma exceção no uso que você não conseguiu tratar.
O “garbage collector” até tenta fechar esses objetos, chamando o método “finalize”, mas você não deve confiar nesse comportamento, porque ele é só chamado muito de vez em quando - na verdade, acredito que na segunda passagem do “garbage collection completo” (“full garbage collection”) é que isso ocorre. Ou seja, é uma coisa que pode ficar várias horas com um ResultSet ou Connection ou Statement aberto, se a aplicação não estiver muito sobrecarregada de memória - não é uma boa coisa.
Cuidado com professores que dizem “o garbage collector vai fechar isso para você, não se preocupe”.
Em uma aplicação no mundo real, você não pode depender desse comportamento, que não é documentado e só existe justamente para “quebrar o galho” de programas que tiveram algum problema no tratamento de erros.