Hola edioswaldo:
Estuve revisando tus respuestas y veo todas muy bien salvo en aquellas dos que marcas dudas ("a" y "e"). Vayamos a ellas.
Respecto de la pregunta a)...
En la jerarquía de herencias que se plantea: todo objeto Taxi es siempre un objeto Vehiculo. Sin embargo su recíproca no se cumple: no todo Vehiculo es siempre un Taxi.
Este parece ser el problema en este punto.
Veamos, en la línea:
taxi2= (Taxi)carro2;
en tiempo de compilación no se marca ningún error ya que el compilador "confía" que llegado este momento de tener que hacer esta asignación, carro2 que es un Vehiculo, pueda llegar a contener efectivamente un Taxi como el casting lo pide.
Sin embargo, en tiempo de ejecución eso no sucede en este ejercicio.
carro2 no es un Taxi y estamos queriendo forzar a que lo sea. Estamos queriendo forzar que carro2 que tiene los atributos y métodos de Vehiculo tenga además "de la nada" dos atributos más y 4 métodos más (que corresponden a los de Taxi). Eso sería forzar a que se convierta en algo que no es, y nos da (logicamente) ese error en tiempo de ejecución.
Veamos ahora qué sucedería con este otro ejemplo:
Vehiculo carro4=new Taxi("ABC-999",4,"Edi7",4); ;
Taxi taxi4= new Taxi("ABC-222",4,"Edi2",4);
taxi4= (Taxi) carro4;
System.out.println("Carro 4= "+carro4.toString());
Aquí carro4 es tipo Vehiculo. Es decir: ese objeto tipo Vehiculo ahora EFECTIVAMENTE contiene un objeto tipo Taxi, por lo tanto en la asignación en tiempo de ejecución:
taxi4= (Taxi) carro4;
no habrá ningún problema.
Insisto en lo siguiente: "Todo objeto Taxi es siempre un objeto Vehiculo. Sin embargo su recíproca no se cumple: no todo Vehiculo es siempre un Taxi."
Te planteo lo siguiente:
Supongamos que la clase Vehiculo hubiera tenido además de la clase hija Taxi, la clase hija AvionDeCarga.
Vehiculo carro4=new new AvionDeCarga("ABC-222",4,"piloto 34","fuselejae 2K4", "container");
Taxi taxi4= new Taxi("ABC-222",4,"Edi2",4);
taxi4= (Taxi) carro4;
System.out.println("Carro 4= "+taxi4.toString());
aquí es más avidente y lógico que en tiempo de ejecución se marque con un error esta incompatibilidad, ya que por mas casting que haga no puedo transformar un avion de carga (que es lo que realmente contiene el objeto tipo Vehiculo llamado carro4) en un objeto tipo Taxi que es lo que me pide el casting.
Respecto de la pregunta e)...
super() se utiliza en un constructor de una subclase para llamar al constructor de su super clase.
En este método sobreescrito de una subclase, si quieres hacer una llamada al método de su superclase, debes invocarlo de esa manera: super.toString()