Hola Adrián:
Estuve revisando tu ejercicio. Lo veo bien pero creo que habría que hacer algunos cambios.
Respecto de la clase TestHerencia4: no creo que la lista ArrayList<Producto> deba ser un atributo del método main.
Pienso que el método main deberia tener como atributo un objeto tipo EnvioDeProducto que corresponderá a un pedido realizado por un cliente determinado.
Respecto de la clase EnvioDeProducto:
Creo que la lista ArrayList<Producto> debería ser su campo de instancia.
De esta manera (la lista como campo de instancia de la clase EnvioDeProducto), podrás crear en un futuro tantos objetos EnvioDeProducto como pedidos hagan los clientes. Cada objeto EnvioDeProducto corresponderá a un pedido realizado.
Si un cliente solicita un envio a domicilio, crearíamos un objeto tipo EnvioDeProducto el cual contendría todos los productos que solicite.
Si un segundo cliente nos hace un encargo de productos para su restaurante, creamos otro objeto tipo EnvioDeProducto conteniendo todos los productos que le son indispensables para su restaurante.
En la clase EnvioDeProducto tendría un método para agregar productos a la lista y otro método que me muestre la lista completa (a través de un iterador).
Pero es conceptualmente importante pensar que el objeto EnvioDeProducto posea una lista de los productos elegidos, y sus métodos.
De la manera en que lo pensaste tu, la lista es solo un atributo del método main de la clase Herencia4.
Respecto de la organización de las clases: está muy bien, pero creo que podría ser aún más eficiente y poner en práctica los conceptos de herencia.
Fíjate que todos los productos, tienen los campos comunes: fecha de caducidad y nro. de lote, como bien los definiste tu en la clase Producto.
Sin embargo, podemos notar que los campos: fecha de envasado y pais de origen, también son comunes a TODOS los productos, tanto de los productos frescos, como de los productos refrigerados, como de los productos congelados.
Entonces, en vez de definir esos campos en cada clase, sería mejor definirlos en la clase Producto directamente.
Deberíamos por lo tanto agrupar todos los campos comunes en las clases padres y luego que las clases hijas vayan declarando los campos que no sean comunes.
De esta manera el esquema que definiste se mantedría igual, solamente cambiarían los campos que cada clase declara.
Clase Producto: fecha de caducidad, nro. de lote, fecha de envasado, pais de origen.
Clase ProductoRefrigeradoOCongelado: temperatura de mantenimiento recomendada.
Clase ProductoFresco: no declara nuevos campos (son los mismos campos de la clase padre Producto).
Clase ProductoRefrigerado: codigo OSA.
Clase ProductoCongelado no declara nuevos campos (son los mismos campos de la clase padre ProductoRefrigeradoOCongelado).
Clase ProdCongPorAgua: salinidad.
Clase ProdCongPorAire: porcentaje de Ni, porcentaje de O2, porcentaje de CO2, porcentaje de vapor de agua.
Clase ProdCongPorNitrogeno: tiempo de exposicion, y metodo empleado.
Respecto de la clase Clase ProdCongPorAire:
Fíjate que declaras 4 campos de instancia, por tanto deberás tener 4 métodos set y 4 métodos get en la clase. Y los métodos get deben simplemente retornar el valor, no sacarlos por consola.
Para sacar por consola todos los valores de los campos que posee la clase con sus respectivas leyendas está el método mostrarProducto().