Vamos a dividir el código en partes para que se vea bien:
// Declaración:
Animal a;
// Instanciación:
a = new Gato();
Le asignes lo que le asignes a la variable a (sea un Gato, un Perro o un Conejo), la variable la has declarado como de tipo Animal. Así que siempre se considera como de tipo Animal. El asunto es que un Gato _es_ un Animal, pero un Animal no tiene por qué ser un Gato.
Incluso si en un momento dado contiene un objeto de tipo Gato, si quieres usarlo como un Gato, tienes que decirlo explícitamente o convertirla explícitamente al tipo apropiado:
Supongamos que Gato tiene un método maullar() que Animal, lógicamente, no tiene.
Aunque yo sé que ahora a contiene un objeto de tipo Gato, no puedo hacer a.maullar() porque a es de tipo Animal. Lo único que podría hacer es algo como...
if (a instanceof Gato) {
// Puedo convertirla y asignarla a una variable de tipo Gato que luego uso como quiera:
Gato b = (Gato) a;
b.maullar();
// Puedo usarla como Gato convirtiéndola sobre la marcha pero explícitamente:
((Gato) a).maullar();
}
De todos modos, creo que te vendría bien mirar un buen libro o al menos un tutorial sobre programación orientada a objetos, porque es mejor que te leas una buena explicación en lugar de ir probando cosas al azar.