Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 [2] 3 4 5 6 7 ... 50
21
Claro.
El problema es que estamos limitados a movernos mediante números enteros.
Es decir, o nos movemos 5 píxeles, ó 4, ó 3... pero no podemos movernos 2,5 píxeles, ni 3,75.

Por eso hice que una de las desviaciones siempre fuera valor 5. Esto limita los ángulos de movimientos posibles, pero consigue que la velocidad sea constante.
Antes de eso, la velocidad cambiaba radicalmente, si las desviaciones eran por ejemplo 1 y 2 iba muy lento en comparación a cuando resultaban ser 4 y 5 por ejemplo.

22
Citar
¿Si la clase Bola hubiera sido independiente del Tablero habría tenido un método paint? No veo método paint en la clase Bola, pero supongo que podría existir metodo paint si fuera independiente?

Bola es una Ellipse2D y si consultamos su documentación en la API de Java, vemos que no tiene método paint. Es decir, no puede dibujarse en pantalla por sí sola.

Solo tienen representación gráfica(paint) las clases que descienden de Component, que es una clase de la librería AWT.
Esta librería es la primera que usaba Java en sus inicios para mostrar GUI's en pantalla.
Pero enseguida demostró tener muchas limitaciones, por lo que apenas 2 años después apareció la librería Swing, que está construida sobre AWT, por lo que en realidad cuando construimos interfaces estamos usando ambas librerías.

A estos Component(JFrame, JPanel, JLabel, JDialog, etc.....) podemos pasarle otros elementos para que los "pinten" dentro de sus límites, en nuestro caso, una elipse bidimensional.

Citar
¿la g en la clase Tablero representa el interior del elemento grafico Jpanel para poder manipularlo?
La g más bien es el "pincel" que pinta el elemento gráfico.
A g se le indica qué elemento ha de pintar, la posición de la pantalla donde ha de ser pintado, con qué tamaño, con qué colores, con qué fuente de texto....


23
La creación del objeto g, las invocaciones a paint() y otras miles de instrucciones más, se encarga de hacerlo las librerías Swing que son las que dibujan toda la interfaz gráfica.

Esas instrucciones no las escribimos nosotros, de lo contrario, significaría que cada para programa tendríamos que escribir también todas las clases que intervienen en crear una interfaz. Todo eso ya está escrito en las librerías Swing, nosotros solo tenemos que elegir que elementos queremos mostrar, donde se posicionan, que aspecto van a tener y cómo han de comportarse.


Todo eso, empieza a ponerse en marcha cuando invocamos el JFrame principal.

Es decir, en la clase Juego, en su método main() lo "único" que hacemos es instanciar un objeto de Juego

Citar
   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            new Juego();   
         }
      });
   }

Pero esa simple instrucción, pone en marcha todas las otras miles de instrucciones que intervienen en el programa.
Ya que Juego es un JFrame, y en su constructor ya le hemos dicho que muestre dos paneles, uno es el Tablero y otro contiene los botones.
Así que el JFrame ya se encarga de activar las librerías Swing y crea todos los objetos Graphics que necesite, y llama a paint() cuando hace falta
, e invoca todo lo que haya que invocar sin que nosotros lo veamos.

Por ejemplo, este es el código de la clase JPanel, ahí puedes ver que se llaman a métodos, a constantes, a variables y otras clases que nosotros no hemos escrito y que no tenemos ni puñetera idea de qué hacen ni para que sirven.

Código: [Seleccionar]
/*
 * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.swing;

import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.beans.BeanProperty;
import java.beans.JavaBean;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serial;

import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.swing.plaf.PanelUI;

/**
 * <code>JPanel</code> is a generic lightweight container.
 * For examples and task-oriented documentation for JPanel, see
 * <a
 href="https://docs.oracle.com/javase/tutorial/uiswing/components/panel.html">How to Use Panels</a>,
 * a section in <em>The Java Tutorial</em>.
 * <p>
 * <strong>Warning:</strong> Swing is not thread safe. For more
 * information see <a
 * href="package-summary.html#threading">Swing's Threading
 * Policy</a>.
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @author Arnaud Weber
 * @author Steve Wilson
 * @since 1.2
 */
@JavaBean(defaultProperty = "UI", description = "A generic lightweight container.")
@SuppressWarnings("serial") // Same-version serialization only
public class JPanel extends JComponent implements Accessible
{
    /**
     * @see #getUIClassID
     * @see #readObject
     */
    private static final String uiClassID = "PanelUI";

    /**
     * Creates a new JPanel with the specified layout manager and buffering
     * strategy.
     *
     * @param layout  the LayoutManager to use
     * @param isDoubleBuffered  a boolean, true for double-buffering, which
     *        uses additional memory space to achieve fast, flicker-free
     *        updates
     */
    public JPanel(LayoutManager layout, boolean isDoubleBuffered) {
        setLayout(layout);
        setDoubleBuffered(isDoubleBuffered);
        setUIProperty("opaque", Boolean.TRUE);
        updateUI();
    }

    /**
     * Create a new buffered JPanel with the specified layout manager
     *
     * @param layout  the LayoutManager to use
     */
    public JPanel(LayoutManager layout) {
        this(layout, true);
    }

    /**
     * Creates a new <code>JPanel</code> with <code>FlowLayout</code>
     * and the specified buffering strategy.
     * If <code>isDoubleBuffered</code> is true, the <code>JPanel</code>
     * will use a double buffer.
     *
     * @param isDoubleBuffered  a boolean, true for double-buffering, which
     *        uses additional memory space to achieve fast, flicker-free
     *        updates
     */
    public JPanel(boolean isDoubleBuffered) {
        this(new FlowLayout(), isDoubleBuffered);
    }

    /**
     * Creates a new <code>JPanel</code> with a double buffer
     * and a flow layout.
     */
    public JPanel() {
        this(true);
    }

    /**
     * Resets the UI property with a value from the current look and feel.
     *
     * @see JComponent#updateUI
     */
    public void updateUI() {
        setUI((PanelUI)UIManager.getUI(this));
    }

    /**
     * Returns the look and feel (L&amp;F) object that renders this component.
     *
     * @return the PanelUI object that renders this component
     * @since 1.4
     */
    public PanelUI getUI() {
        return (PanelUI)ui;
    }


    /**
     * Sets the look and feel (L&amp;F) object that renders this component.
     *
     * @param ui  the PanelUI L&amp;F object
     * @see UIDefaults#getUI
     * @since 1.4
     */
    @BeanProperty(hidden = true, visualUpdate = true, description
            = "The UI object that implements the Component's LookAndFeel.")
    public void setUI(PanelUI ui) {
        super.setUI(ui);
    }

    /**
     * Returns a string that specifies the name of the L&amp;F class
     * that renders this component.
     *
     * @return the string "PanelUI"
     * @see JComponent#getUIClassID
     * @see UIDefaults#getUI
     */
    @BeanProperty(bound = false, expert = true, description
            = "A string that specifies the name of the L&F class.")
    public String getUIClassID() {
        return uiClassID;
    }


    /**
     * See readObject() and writeObject() in JComponent for more
     * information about serialization in Swing.
     */
    @Serial
    private void writeObject(ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        if (getUIClassID().equals(uiClassID)) {
            byte count = JComponent.getWriteObjCounter(this);
            JComponent.setWriteObjCounter(this, --count);
            if (count == 0 && ui != null) {
                ui.installUI(this);
            }
        }
    }


    /**
     * Returns a string representation of this JPanel. This method
     * is intended to be used only for debugging purposes, and the
     * content and format of the returned string may vary between
     * implementations. The returned string may be empty but may not
     * be <code>null</code>.
     *
     * @return  a string representation of this JPanel.
     */
    protected String paramString() {
        return super.paramString();
    }

/////////////////
// Accessibility support
////////////////

    /**
     * Gets the AccessibleContext associated with this JPanel.
     * For JPanels, the AccessibleContext takes the form of an
     * AccessibleJPanel.
     * A new AccessibleJPanel instance is created if necessary.
     *
     * @return an AccessibleJPanel that serves as the
     *         AccessibleContext of this JPanel
     */
    @BeanProperty(bound = false)
    public AccessibleContext getAccessibleContext() {
        if (accessibleContext == null) {
            accessibleContext = new AccessibleJPanel();
        }
        return accessibleContext;
    }

    /**
     * This class implements accessibility support for the
     * <code>JPanel</code> class.  It provides an implementation of the
     * Java Accessibility API appropriate to panel user-interface
     * elements.
     * <p>
     * <strong>Warning:</strong>
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans
     * has been added to the <code>java.beans</code> package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    @SuppressWarnings("serial") // Same-version serialization only
    protected class AccessibleJPanel extends AccessibleJComponent {

        /**
         * Constructs an {@code AccessibleJPanel}.
         */
        protected AccessibleJPanel() {}

        /**
         * Get the role of this object.
         *
         * @return an instance of AccessibleRole describing the role of the
         * object
         */
        public AccessibleRole getAccessibleRole() {
            return AccessibleRole.PANEL;
        }
    }
}


24
Citar
¿Por que se incluye @SuppressWarnings("serial") en el codigo?
Esto está relacionado con la "serialización" de objetos.
Algunas clases (por ejemplo la clase JPanel) implementan la interfaz Serializable, que es la que permite que un objeto pueda ser transformado en una "serie de bytes" y de ese modo transmitirlo a otro dispositivo o también guardarlo en disco.

Luego ese objeto, al recibirlo el otro dispositivo o al leerlo del disco donde se guardó, esos bytes se transforman de nuevo en un objeto, conservando los valores de sus atributos tal cuál estaban cuando se transmitió/guardó.

Es decir, que si yo tengo una clase Persona que implementa la interfaz serializable, y creo el objeto Persona persona01, pues yo podría transformalo en bytes, y enviártelo a ti a través de una red o un soporte físico y tu luego recuperarlo desde tu programa, siempre que también tengas el código de esa clase Persona

Vale, ¿pero que pasaría si mi clase Persona tiene un código distinto de tu clase Persona? Esto puede ocurrir si por ejemplo ha habido una actualización en el programa y a Persona se le ha añadido un nuevo atributo, o un nuevo método, que no existe en el código de tu clase porque todavía no has actualizado tu programa.

Pues ya tenemos un problema, tu clase no va a saber interpretar bien mi objeto y el programa se puede bloquear, o sufrir pérdida de datos, o quien sabe...

Para poder controlar este problema, las clases que implementan esta capacidad incluyen un atributo llamado serialVersionUID al que se recomienda al programador que le de un valor numérico para indicar un número de versión.
Cada vez que se haga un cambio importante en el código, se ha de modificar este número de versión.
De esta manera, el programa antes de intentar recuperar un objeto que ya ha sido serializado, puede comprobar el número de versión con el que fue serializado. Si no coincide con el número de versión de su propio código, entonces no lo recuperará y se podrá evitar cuelgues o pérdidas de datos.

Entonces, todo esto está muy bien si vamos a serializar objetos. Pero si no lo vamos a hacer, como es el caso de este programa, entonces esto del número de serie nos da igual y no hace falta ponerlo.
Sin embargo, el compilador o IDE que estemos usando puede lanzar un warning si no lo ponemos, ya que considera que es algo importante de lo que conviene avisar al programador.

Una forma de pedirle al compilador de que no nos toque las narices con esta vaina, es con la etiqueta @SuppressWarnings("serial"). Ahí le decimos que NO nos avise por la ausencia de número de versión.

Citar
y el método paint(Graphics g) entiendo que pinta un elemento grafico como un JPanel, ¿la g representa el interior del elemento grafico para poder manipularlo? Y esto que entiendo es una conversión de tipos: Graphics2D G2D = (Graphics2D)g;  ¿Por qué se hace así en lugr de manipular directamente el objeto g?

Esto tiene que ver con la herencia de clases y el polimorfismo.
Y sobre todo de como queríamos gestionar la Bola


El objeto g es de la superclase Graphics, que ya de por sí posee varios métodos y funcionalidades para dibujar elementos gráficos en pantalla.
Para dibujar una bola, podríamos haber usado su método:
Código: [Seleccionar]
drawOval(int x, int y, int width, int height)Pero fíjate que cada vez que lo llamamos, tenemos que darle coordenadas y dimensiones.
Esas coordenadas x e y, dependen de la posición previa, de la "desviación" que calculábamos de forma aleatoria, de la velocidad actual de la "bola"...

Todo esto se podría haber calculado ahí en la clase Tablero. Pero es más cómodo y elegante separar esos cálculos y escribirlos en una clase que represente a una Bola, ya que esa es la gracia de la programación orientada a objetos, que podemos representar cualquier entidad que se nos ocurra.

Nuestra Bola es en realidad un objeto de la clase Ellipse2D y para que se dibuje se la tenemos que pasar al objeto que se encarga de pintar el Tablero, es decir, a Graphics g
Pero la clase Graphics es demasiado genérica y no sabe qué es una Ellipse2D. No podemos hacérsela llegar a ninguno de sus métodos.

Tenemos que subir un nivel en su línea hereditaria y trabajar con Graphics2D. Esta clase hija es menos genérica, esta más especializada en trabajar con figuras 2D y por supuesto sabe lo que es una Ellipse2D y cómo dibujarla en pantalla.

Por eso al objeto g, le pedimos que cambie de tipo y pase a comportarse como un Graphics2D.
Gracias al polimorfismo, el objeto g puede comportarse como un Graphics o como un Graphics2D, según nos convenga.

25
Gracias por el trabajo que conlleva todo este lavado de cara y puesta al día de la web.
 ;) ;) ;)

26
Hola, solo dos detalles de poca importancia:

1- En el código, mejor evitar siempre usar la letra ñ, así como otros caracteres que
sean ajenos al alfabeto inglés.
Código: [Seleccionar]
public int getTamaño () { return listaDeCantantes.size(); }Esto es porque, aunque a nosotros nos funcione, puede haber problemas al compartir nuestro código con otros programadores de otros países cuyos sistemas operativos y programas de edición no contengan una tabla de caracteres que reconozca símbolos latinos como la ñ, las vocales con tilde, vocales con diéresis, las aperturas de interrogación y exclamación(anglosajones solo usan los cierres), etc...

Como digo, es una recomendación, no una obligación. Fastidia un poco, porque entonces hay que escribir "mal" estos nombres.
Se puede usar simplemente la letra n:
Código: [Seleccionar]
public int getTamano () { return listaDeCantantes.size(); }
Pero hay otras formas que se usan habitualmente: tamanio, tamanyo, tamanho,...
Sobre todo vamos a querer usarlas cuando la palabra es "año", porque si solo usamos la n..., emmm.., pues no es elegante  :o




2- Otra recomendación, no obligación.
Si un else va a quedar vacío, pues simplemente no se pone ningún else y ya está.
Si lo creemos necesario, podemos añadir el comentario explicando que en caso de no cumplirse la condición anterior entonces no va a ocurrir nada.
Pero no es necesario incluir un else vacío.
Citar
    public void removeCantante (int posicion) {  //Método

        if (posicion >= 0 && posicion < listaDeCantantes.size() ) {

            listaDeCantantes.remove(posicion); }

        else { } //else vacío. No existe nombre para la posición solicitada, no se ejecuta ninguna instrucción

    }

27
A mí me parece correcto y la ejecución funciona.
Así que, adelante con el resto del curso  ;D

28
Hola.
La solución es correcta, aunque digamos que has desarrollado una solución más compleja de lo necesario.

Pero veo, que en el hilo de otro compañero ya has encontrado una solución más sencilla.

En cualquier caso, si te queda alguna duda solo tienes que preguntar.

Un saludo.

29
Hola.
La solución es correcta.

También podríamos haber comparado directamente un String con otro, sin tener que importar la clase Objects

Citar
        if (subcadena.equals("a")){
            System.out.println ("El texto introducido empieza por la letra "+ subcadena);
        }else{
            System.out.println ("El texto introducido no empieza con la letra a, empieza por la letra "+ subcadena);
        }   


Un saludo.

30
Todo curso que te enseñe a organizar tus ideas en pasos separados y trasladarlos a un pseudocódigo y/o un diagrama de flujos, te será de provecho.

Yo hace ya algunos años cursé el primer año de DAM (estaba en paro y no tenía nada mejor que hacer...) y me sorprendió que este aspecto que he mencionado apenas lo tocásemos.

Lo poco que hicimos sobre esto, nos pedían hacerlo(en el centro donde yo lo cursé, cada DAM puede ser distinto según el centro) con Scratch..., que está muy guay cuando sabes manejarlo, pero aún con su enfoque casi infantil, requiere de cierto aprendizaje.
Y al final la gente pasó más tiempo intentando aprender Scratch, que no intentando aprender cómo organizar las ideas en pasos lógicos.


Y luego este fue el principal obstáculo con el que muchos se encontraron. Tareas y ejercicios que sí sabían resolver mentalmente (la mente hace cosas de forma automática) pero que no sabían cómo trasladar esos procesos a pasos lógicos con los que crear un flujo de instrucciones para programarlo.

Si en vez de Scratch, hubieran usado simplemente papel y lápiz para escribir pseudocódigos y dibujar diagramas de flujo, habrían dedicado más tiempo a lo que realmente importaba.

31
¿Estás seguro de que estás escribiendo la ruta correcta?


Para asegurarte mejor, podemos hacer "trampas" y usar un JFileChooser para seleccionar el fichero que quieres abrir.
Un JFileChooser es una ventana emergente que se usa en programas con interfaz gráfica para explorar los directorios de tu sistema y elegir un fichero.
Cuando eliges un archivo, el JFileChooser te retorna un objeto File con el que ya puedes trabajar.

No es lo ideal, pero también puede usarse en un programa para consola de texto.

Te dejo un programa sencillo de ejemplo, donde con un método abro el JFileChooser y retorno el File escogido.

Aplica ese método en tu programa y a ver si así consigues abrir el fichero deseado:

Código: [Seleccionar]
import java.io.File;
import javax.swing.JFileChooser;

public class SelectorFichero {

public static void main(String[] args) {

System.out.println("A continuacion se abrira una ventana para elegir fichero...");
File elegido = seleccionarFichero();
System.out.println("Ruta del fichero elegido:\n" + elegido.getAbsolutePath());

}

private static File seleccionarFichero() {
//Creamos FileChooser
JFileChooser selector = new JFileChooser();
//Abrimos ventana emergente y recogemos el valor del botón seleccionado por el usuario
int seleccion = selector.showOpenDialog(null);
//Si usuario ha pulsado botón "Aceptar"...
if (seleccion == JFileChooser.APPROVE_OPTION)
return selector.getSelectedFile(); //Retornamos el File seleccionado
//pero, si el usuario ha pulsado botón "Cancelar" o ha habido algún error
else {
System.out.println("Seleccion cancelada");//Informamos
return null; //Y retornamos valor null
}
}

}

32
Hola.

No, no necesitas conocimientos avanzados de matemáticas. Podrías necesitarlos si fueras a estudiar alguna ingeniería avanzada..., pero en absoluto para un DAM.

Te basta y te sobra con saber despejar una ecuación o calcular unos porcentajes.
Por ejemplo, son muy típicos hacer ejercicios del estilo:
"Un comercio vende botellas de vino por 25€.
Si un cliente compra dos botellas se le hace un 20% de descuento en la factura a pagar, pero si compra tres o más productos, el descuento será del 35%.
Haz un programa que calcule el importe final según las cantidades compradas."


Vale, pues para poder escribir un programa que calcule estos porcentajes, primero obviamente tienes que saber tú hacer esos cálculos para luego dar las instrucciones necesarias al ordenador y que también los haga.

Pues prácticamente estas son todas las matemáticas que vas a necesitar.
Así que no te preocupes, eso de que se requiere ser un matemático para aprender a programar, es un poco leyenda urbana.

Sí es cierto que saber manejarse con las matemáticas ayuda mucho, más que nada porque entonces el cerebro está más acostumbrado a organizar las ideas en procesos lógicos y eso ayuda mucho a la hora de diseñar un código, incluso aunque el programa no tenga nada que ver con un proceso aritmético.

Pero no es un requisito imprescindible, en absoluto.

No tengas ningún temor, cualquiera puede aprender programación, sea cuál sea su nivel.

Y además, recuerda que no estás limitado a lo que hubieras aprendido (y quizás ya olvidado) en la ESO.
Puedes aprender, o "reaprender", todo lo que haga falta con unos pocos clicks de ratón.

¿Tú recuerdas como calcular el área de un trapecio?
¡¡Yo tampoco!!
¿Significa eso que ya no podemos escribir un programa que haga eso cálculo?
En absoluto, en menos de 1 minuto podemos encontrar la fórmula y ejemplos de como aplicarla.
Una vez "reaprendida", enseguida sabremos escribir un programa para calcularlo.

¿Recuerdas como encontrar el "Mínimo común múltiplo"?
¿O el "Máximo Común Divisor"?
¿O descomponer en factores primos?

¿No lo recuerdas? Pues no importa, cuando lo necesites, lo buscas por Internet y listo.

Gran parte del trabajo de un programador, consiste en navegar por Internet para aprender o "reaprender" cómo se hacen las cosas ja ja..., para luego poder trasladar ese conocimiento a instrucciones de un programa.

Un saludo.

33
Hola

Lo primero, el coche ha de tener 4 ruedas, así que o ponemos 4 atributos de clase Rueda, o ponemos un array de dicha clase.
Ah, y salvo que haya un motivo de peso, los atributos de chasis y ruedas también deben ser privados.

Citar
public class Coche {
   
      private String matricula;
      private String marca;
      private String modelo;
      private int potencia;
      private int velocidadMaxima;
      private Rueda[] ruedas;
      private Chasis chasis;

Los atributos de las clases Chasis y Rueda también es recomendable que sean privados.


Ahora, cómo construir un coche... aquí efectivamente es donde se presenta el problema.

Las clases Chasis y Rueda están anidadas dentro de Coche, por tanto, por sí solas no pueden existir.
Para que existan y puedan ser invocadas, primero hay que instanciar un objeto Coche.
Por tanto, en el constructor de coche no pondremos que reciban argumentos para chasis y ruedas, porque para disponer de ellos primero hay que construir el propio coche.

Lo que sí haremos será dotar a la clase de unos setters para, después de haber creado el coche, poder crear y asignarle chasis y ruedas.
Código: [Seleccionar]
public class Coche {

private String matricula;
private String marca;
private String modelo;
private int potencia;
private int velocidadMaxima;
private Rueda[] ruedas;
private Chasis chasis;

//Constructor no recibe ni ruedas ni chasis
public Coche(String matricula, String marca, String modelo, int potencia, int velocidadMaxima) {
this.matricula = matricula;
this.marca = marca;
this.modelo = modelo;
this.potencia = potencia;
this.velocidadMaxima = velocidadMaxima;
}

//Chasis y ruedas llegarán por setters
public void setChasis(Chasis chasis) {
this.chasis = chasis;
}

public void setRuedas(Rueda[] ruedas) {
this.ruedas = ruedas;
}


Ahora en el Main ya podemos crear todo lo necesario.
Primero el coche.
Luego, gracias a que tenemos un coche, podemos crear ruedas y chasis
Por último, asignamos ese chasis y ruedas, al coche creado.

Fíjate en el código, que para indicar el tipo del objeto que vamos a crear llamamos a los nombres de clases Coche.Rueda y Coche.Chasis
Pero para construir los objetos, llamamos al nombre del objeto coche instanciado y a través de él podemos instancias los objetos de las clases anidadas.

Citar
public class Main {

   public static void main(String[] args) {
      
      //Primero creamos coche
      Coche coche = new Coche("1998DTM","Jaguar","X-Type",280,250);

      //Ahora, usando la instancia de Coche, podemos crear Ruedas y Chasis
      Coche.Chasis chasis = coche.new Chasis("Carbono", 159.2);
      
      Coche.Rueda[] ruedas = new Coche.Rueda[] {
            //Ruedas delanteras
            coche.new Rueda(145, "Seco", "Dunlop", "Eco Control"),
            coche.new Rueda(145, "Seco", "Dunlop", "Eco Control"),
            //Ruedas traseras
            coche.new Rueda(165, "Seco", "Dunlop", "Sport Max Race"),
            coche.new Rueda(165, "Seco", "Dunlop", "Sport Max Race")
      };
      
      //Tras crearlos, podemos asignarlo a la instancia de Coche
      coche.setChasis(chasis);
      coche.setRuedas(ruedas);
   }

}


Como puedes ver, esto de las clases anónimas es un poco lioso y por lo general vamos a preferir usarlas en casos especiales, sobre todo cuando no se requieran datos externos a la clase que las contiene.
Es decir, todo lo necesario para crear una clase anónima, ya existe dentro de la clase principal contenedora y por lo tanto no hay necesidad de invocarlas fuera de dicha clase.

En este caso, para crear chasis y ruedas, se necesitan datos externos. Se supone que habrá un usuario tecleando los datos de las ruedas y de los diferentes chasis, así que para ir creando objetos con esos datos necesitamos poder invocar a la clase Rueda y Chasis desde la clase "main" donde se están pidiendo datos al usuario.
Es entonces cuando hay que complicarse con los nombres compuestos de dos (o más) clases separadas por puntos y la necesidad de instancia primero las clases "contenedoras" para luego poder llamar a las clases "anidadas".

En casos así, vamos a preferir escribir las distintas clases por separado, en lugar de anidarlas.

34
Aprender a programar desde cero / Re: Acceso a clases anónimas
« en: 13 de Julio 2023, 11:00 »
Necesitaríamos ver cuál es tu código para decirte donde está el problema.


Por cierto, cuando una consulta está relacionada con una consulta anterior, como es este caso, mejor si se hace en el mismo hilo ya abierto en lugar de abrir uno nuevo.
Es mejor porque así toda la información está junta en un mismo hilo y no dispersa ya no solo en distintos hilos, si no también distintas secciones.

35
Aquí tenemos tres entidades.

Dos de esas entidades son:

Citar
Chasis
- String material
- double peso

Citar
Rueda
- double medida
- String tipo
- String marca
- String modelo

Estas dos entidades, estarán incluidas como atributos de la tercera entidad, llamada Coche, que será la clase principal.

Sabemos que un Coche tiene 1 Chasis y 4 Ruedas. Así que respecto a las "ruedas" hay que decidir cómo queremos incluirla en la lista de atributos.
Podemos simplemente introducir 4 atributos de este tipo:
Citar
Coche
- String matricula
- String marca
- String modelo
- int potencia
- int velocidadMaxima
- Chasis chasis
- Rueda ruedaDelIzq
- Rueda ruedaDelDer
- Rueda ruedaTrasIzq
- Rueda ruedaTrasDer

O podemos incluirlas como un array, que luego en el constructor de clase podremos inicializarlo para que tenga 4 elementos

Citar
Coche
- String matricula
- String marca
- String modelo
- int potencia
- int velocidadMaxima
- Chasis chasis
- Rueda[] ruedas

Cualquier opción es buena, tan solo habrá que tener en cuenta que si optamos por usar un array, habrá que usar índices (0,1,2,3) para apuntar a cada rueda en concreto.


Estas tres clases, pueden escribirse cada una en un archivo .java individual por separado.
Pero, teniendo en cuenta que para este caso Chasis y Rueda no tienen mucho sentido como entidades separadas (si por ejemplo fuese un programa para un taller que vende ruedas, entonces esta clase sí tendría sentido como entidad individual), también se puede optar por escribirlo todo en un único archivo .java.

La clase principal sería Coche. Y dentro de su cuerpo, además de atributos, constructores y métodos..., se podrían escribir también las clases Chasis y Rueda, teniendo cada una su propio cuerpo con sus propios atributos, constructores y métodos.

36
Aprender a programar desde cero / Re: Excepciones
« en: 02 de Julio 2023, 12:36 »
Hola.
Que raro, supongo que te ha aparecido esa codificación por alguna diferencia entre el juego de caracteres de nuestros navegadores, ya que no es eso lo que yo escribí.

Te pego una captura de lo que realmente había publicado yo:

37
Creo que la intención del enunciado es que además de una clase "equipo", hagamos también una clase "programador".
Además, de esta forma es más fácil organizar las ideas porque podemos separar las distintas tareas que piden en varias clases.

La clase Programador básicamente solo tiene dos atributos, nombre y apellidos.
Pero sí podemos hacer que sea ella quien valide si los datos que reciba por constructor cumplen los requisitos. Si no los cumple, lanzará excepción.
De esta forma, será imposible construir un Programador si no recibe unos datos válidos.
Código: [Seleccionar]
public class Programador {

private String nombre;
private String apellidos;

public Programador(String nombre, String apellidos) throws Exception {

if (nombre.length() >= 20)
throw new Exception("Campo NOMBRE ha de tener menos de 20 caracteres");
if (apellidos.length() >= 20)
throw new Exception("Campo APELLIDOS ha de tener menos de 20 caracteres");

if (!cadenaEsValida(nombre))
throw new Exception("Campo NOMBRE no admite caracteres numericos, solo texto.");
if (!cadenaEsValida(apellidos))
throw new Exception("Campo APELLIDOS no admite caracteres numericos, solo texto.");

this.nombre = nombre;
this.apellidos = apellidos;
}

private boolean cadenaEsValida(String cadena) {
for (int i = 0; i < cadena.length(); i++) {
char car = cadena.charAt(i);
if (car >= '0' && car <= '9')
return false;
}

return true;
}

public String getNombre() {
return nombre;
}

public String getApellidos() {
return apellidos;
}

}

Luego la clase EquipoProgramadores.
Sería muy parecida a la que estabas escribiendo tú, ya la tenías bien encaminada.
Solo que ahora el array no será de String, si no de la clase Programador
Algo que sí hay que tener en cuenta, es que este array su tamaño dependerá del atributo "tamanoEquipo"

Esta clase se encargará de lanzar excepción si intentamos agregarle un Programador cuando el array ya esté completo.
Código: [Seleccionar]
public class EquipoProgramadores {

private String nombreEquipo;
private String universidad;
private String lenguajeProgramacion;
private int tamanoEquipo;
private Programador[] programadores;

public EquipoProgramadores(String nombreEquipo, String universidad,
String lenguajeProgramacion, int tamanoEquipo) {
this.nombreEquipo = nombreEquipo;
this.universidad = universidad;
this.lenguajeProgramacion = lenguajeProgramacion;
this.tamanoEquipo = tamanoEquipo;
programadores = new Programador[tamanoEquipo];
}

public boolean equipoLleno() {
return programadores[tamanoEquipo-1] != null;
}

public void agregarProgramador(Programador prog) throws Exception {
if (equipoLleno())
throw new Exception("El EQUIPO esta lleno, no admite otro PROGRAMADOR");
else {
//Buscamos hueco libre en el array
for (int i = 0; i < tamanoEquipo; i++)
if (programadores[i] == null) {
programadores[i] = prog;
break;
}
}
}

public String getNombreEquipo() {
return nombreEquipo;
}

public String getUniversidad() {
return universidad;
}

public String getLenguajeProgramacion() {
return lenguajeProgramacion;
}

public int getTamanoEquipo() {
return tamanoEquipo;
}

public Programador[] getProgramadores() {
return programadores;
}

}

Como ves, al repartir las funcionalidades en dos clases, estas quedan más ligeras de código y más fáciles de elaborar y luego leerlas.

Ahora solo faltaría una clase main donde podamos crear un Equipo y añadirle Programadores.

Podemos hacer que primero se pidan los datos para construir el Equipo.
Una vez construido, podemos hacer un menú donde el usuario pueda elegir entre añadir Programadores y mostrar los datos del Equipo.
De esta forma, se puede ir viendo como el array recibe elementos cada vez que añadimos un nuevo miembro al Equipo
Código: [Seleccionar]
public class CrearEquipo {

private static Scanner teclado = new Scanner(System.in);
private static EquipoProgramadores equipo = null;

public static void main(String[] args) {

System.out.println("\t\tCREAR EQUIPO");
System.out.println("\t\t----- ------\n");
//Pedimos datos del EQUIPO
System.out.print("Nombre del equipo: ");
String nombreEquipo = teclado.nextLine();
System.out.print("Universidad que representa: ");
String universidad = teclado.nextLine();
System.out.print("Lenguaje programacion: ");
String lenguaje = teclado.nextLine();
int tamanio = 0;
do {
System.out.print("Miembros del equipo(min:2/max:3): ");
tamanio = Integer.parseInt(teclado.nextLine());
}while (tamanio < 2 || tamanio > 3);

//Construimos EQUIPO
equipo = new EquipoProgramadores(nombreEquipo, universidad, lenguaje, tamanio);

//Menu para añadir PROGRAMADORES y mostrar datos del EQUIPO
int opcion = 0;
do {
System.out.println("\n\t\tGESTION EQUIPO");
System.out.println("\t\t------- ------");
System.out.println("[1] -- Agregar PROGRAMADOR al EQUIPO");
System.out.println("[2] -- Mostrar datos del EQUIPO");
System.out.println("[3] -- TERMINAR PROGRAMA");
System.out.print("Opcion: ");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
nuevoProgramador();
break;
case 2:
mostrarEquipo(equipo);
break;
case 3:
break;
default:
System.out.println("Opcion incorrecta");
}
}while(opcion != 3);
}

private static void nuevoProgramador() {
//Creamos un PROGRAMADOR
Programador nuevo = null;
do {
System.out.println("\n\t\tNUEVO PROGRAMADOR");
System.out.println("\t\t----- -----------\n");
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Apellidos: ");
String apellidos = teclado.nextLine();
try {
nuevo = new Programador(nombre, apellidos);
}
catch(Exception ex) {
System.out.println("ERROR: " + ex.getMessage());
}
}while(nuevo == null);

//Añadimos al EQUIPO
try {
equipo.agregarProgramador(nuevo);
}
catch(Exception ex) {
System.out.println("ERROR: " + ex.getMessage());
}

}

private static void mostrarEquipo(EquipoProgramadores equipo) {
System.out.println("\n\t\tDATOS DEL EQUIPO");
System.out.println("\t\t----- --- ------\n");
System.out.println("Nombre: " + equipo.getNombreEquipo());
System.out.println("Universidad: " + equipo.getUniversidad());
System.out.println("Lenguaje: " + equipo.getLenguajeProgramacion());
System.out.println("Cantidad de miembros: " + equipo.getTamanoEquipo());
System.out.println("\t\tMIEMBROS");
System.out.println("\t\t--------");
for (Programador prog: equipo.getProgramadores()) {
if (prog == null)
System.out.println("--> Miembro no registrado");
else
System.out.println("--> " + prog.getNombre() + " " + prog.getApellidos());
}
}

}

Si hacemos una ejecución del programa, en consola vemos como se producen y capturan excepciones cuando corresponde y como el array va recibiendo elementos.
Citar
      CREAR EQUIPO
      ----- ------

Nombre del equipo: Coding Serious
Universidad que representa: Barataria
Lenguaje programacion: Jaba xD
Miembros del equipo(min:2/max:3): 4
Miembros del equipo(min:2/max:3): 3

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 2

      DATOS DEL EQUIPO
      ----- --- ------

Nombre: Coding Serious
Universidad: Barataria
Lenguaje: Jaba xD
Cantidad de miembros: 3
      MIEMBROS
      --------
--> Miembro no registrado
--> Miembro no registrado
--> Miembro no registrado

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 1

      NUEVO PROGRAMADOR
      ----- -----------

Nombre: Perico
Apellidos: Palotes

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 2

      DATOS DEL EQUIPO
      ----- --- ------

Nombre: Coding Serious
Universidad: Barataria
Lenguaje: Jaba xD
Cantidad de miembros: 3
      MIEMBROS
      --------
--> Perico Palotes
--> Miembro no registrado
--> Miembro no registrado

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 1

      NUEVO PROGRAMADOR
      ----- -----------

Nombre: S4r4
Apellidos: Baras
ERROR: Campo NOMBRE no admite caracteres numericos, solo texto.

      NUEVO PROGRAMADOR
      ----- -----------

Nombre: Sara
Apellidos: Baras

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 1

      NUEVO PROGRAMADOR
      ----- -----------

Nombre: Michael
Apellidos: Indalecio-Galecio-Santana
ERROR: Campo APELLIDOS ha de tener menos de 20 caracteres

      NUEVO PROGRAMADOR
      ----- -----------

Nombre: Michael
Apellidos: Galecio

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 2

      DATOS DEL EQUIPO
      ----- --- ------

Nombre: Coding Serious
Universidad: Barataria
Lenguaje: Jaba xD
Cantidad de miembros: 3
      MIEMBROS
      --------
--> Perico Palotes
--> Sara Baras
--> Michael Galecio

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 1

      NUEVO PROGRAMADOR
      ----- -----------

Nombre: Elon
Apellidos: Musk
ERROR: El EQUIPO esta lleno, no admite otro PROGRAMADOR

      GESTION EQUIPO
      ------- ------
[1] -- Agregar PROGRAMADOR al EQUIPO
[2] -- Mostrar datos del EQUIPO
[3] -- TERMINAR PROGRAMA
Opcion: 3


Espero haberte sido de ayuda. Pregunta lo que necesites.

Un saludo.

38
No estoy muy seguro a que te refieres con "variantes".

Si te refieres a sus distintos métodos: nextInt(), nextDouble(), nextLine(), nextFloat(), etc...
Entonces, a priori, la elección dependerá del tipo de dato que necesites recibir por teclado.

Sin embargo, yo por lo general recomiendo hacer TODAS las lecturas con nextLine(), el cuál nos retorna un String, y ya luego parsear al tipo de dato que podamos necesitar.

Esto es porque nextLine() es el único método que hace una lectura completa de todo lo que haya en el buffer de entrada del Scanner, de manera que para la siguiente lectura el buffer ha quedado "limpio".
Los otros métodos cogen solo lo que necesitan para construir el tipo de dato que han de retornar y dejan "restos" en el buffer, los cuáles pueden entorpecer las siguientes lecturas.

Por ejemplo, en un mensaje anterior te compartí un código para leer un double y controlar como excepción la posibilidad de que fuera negativo.
En el método donde hacemos la lectura de ese double, habrás visto que hago la lectura con nextLine() y al mismo tiempo parseo el String que me proporciona a double:
Citar
   private static double pedirDoublePositivo() throws Exception {
      
      System.out.print("\nIntroduzca un valor double positivo: ");
      double valor = Double.parseDouble(teclado.nextLine());
      
      if (valor < 0)
         throw new Exception("El valor " + valor + " no es positivo.");
      else
         return valor;
   }
Parece que estoy complicando las cosas innecesariamente, pues podría haber hecho la lectura directamente con nextDouble():
Citar
   private static double pedirDoublePositivo() throws Exception {
      
      System.out.print("\nIntroduzca un valor double positivo: ");
      double valor = teclado.nextDouble();
      
      if (valor < 0)
         throw new Exception("El valor " + valor + " no es positivo.");
      else
         return valor;
   }

Y en principio funciona igual de una forma que la otra.
Sin embargo, si forzamos ha cometer un error con la forma en que yo lo hice y tecleamos un texto en lugar de un valor numérico, se produce una excepción, que va a quedar controlada y no pasa nada, el programa continua:
Citar
Introduzca un valor double positivo: catorce
ERROR. Dato invalido:
For input string: "catorce"

Introduzca un valor double positivo: 14

Valor introducido: 14.0

      FIN DE PROGRAMA


Pero, si forzamos el mismo error, esta vez haciendo la lectura con nextDouble(), resulta que el programa queda bloqueado en un bucle infinito debido a que está recibiendo un valor null sin que nos de pie a poder teclear nada distinto.
Y hay que forzar el cierre del programa a las bravas:
Código: [Seleccionar]
Introduzca un valor double positivo: catorce
ERROR. Dato invalido:
For input string: "catorce"

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

Introduzca un valor double positivo: ERROR. Dato invalido:
null

¿Por qué?¿A que se debe esta diferencia de comportamiento?

Recuerda que te mencioné anteriormente un poco sobre los "caracteres especiales". Caracteres que no tienen representación gráfica en pantalla, si no que conllevan una acción.
Por ejemplo el carácter de "nueva línea", el "\n". O también el carácter para tabular texto, el "\t".

Hay otro carácter llamado "retorno de carro", que en lenguaje C y en sus parientes (Java es pariente de C) se representa como "\r".

Es el carácter que pone fin a una línea y hace pasar a la siguiente, como la palanca de las viejas máquinas de escribir.
Este carácter, aunque no lo veamos, se introduce en el Scanner cada vez que pulsamos la tecla intro/enter de nuestro teclado cuando introducimos datos.

Volviendo al ejemplo anterior, cuando yo fuerzo el error tecleando el valor "catorce", en realidad en el buffer del Scanner lo que entra es "catorce\r"

Si le pedimos a nextDouble() que intenta conformar un valor double con esos caracteres, no lo va a conseguir porque el busca caracteres numéricos, del 0 al 9.
Así que se produce una excepción, la cuál controlamos y bueno, no es tan grave.

El problema está en que el buffer del Scanner, no ha quedado limpio.
Los caracteres que componen el String "catorce", han sido recogidos y rechazados por nextDouble(). Pero nextDouble() no captura "caracteres especiales", así que en el buffer del Scanner aún tenemos el carácter "\r"
¿Qué ocurre ahora?
Que al volver a pedir el dato, nextDouble() detecta de nuevo el carácter "\r" y erróneamente piensa que el usuario ha vuelto a pulsar la tecla intro/enter que da por finalizada la lectura de datos.
Así que sin que el usuario haya podido hacer nada realmente, nextDouble() intenta crear de nuevo un dato, pero realmente no hay nada (valor null), así que de nuevo excepción, el buffer sigue teniendo "\r", el bucle se repite, nextDouble() de nuevo encuentra valor null, excepción, el buffer sigue conservando "\r",....
así que el flujo del programa queda atrapado en un bucle sin fin porque nextDouble() reconoce el carácter "\r" pero no lo recoge, así que ese carácter se queda "enquistado" en el buffer del Scanner para siempre.

Esto mismo ocurre con nextInt(), nextShort(), nextByte(), nextFloat().....
Con todos, excepto con nextLine().

nextLine() es el único que se "traga" cualquier cosa que tenga el buffer del Scanner, así que tras cada lectura el buffer va a quedar limpio, sin ningún resto que pueda afectar a las siguientes lecturas.

Por eso conviene leer siempre con nextLine(), y luego hacer el parseo a lo que se necesite.
Puede que ese proceso de parseo luego provoque una excepción o no, pero en cualquier caso, el buffer del Scanner habrá quedado limpio.

Esto ocurre no solo en caso de posibles excepciones. Una lectura que haya resultado correcta con nextDouble() u otro de estos métodos, luego puede entorpecer también futuras lecturas.

Veamos un ejemplo super sencillo:
Pedimos primero el nombre y luego la edad.
El nombre lo leemos con nextLine() porque queremos un String y el edad con nextInt() porque queremos un int.
Código: [Seleccionar]
public class Ejemplo {

public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);

System.out.print("Dime tu nombre: ");
String nombre = teclado.nextLine();

System.out.print("Dime tu edad: ");
int edad = teclado.nextInt();

System.out.println("\nTe llamas " + nombre + " y tu edad es " + edad);
teclado.close();

}

}

Si probamos ese código, funciona perfectamente, como no podría ser de otra forma.
Pero invirtamos la petición de datos, primero pedimos la edad y luego el nombre
Código: [Seleccionar]
public class Ejemplo {

public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);

System.out.print("Dime tu edad: ");
int edad = teclado.nextInt();

System.out.print("Dime tu nombre: ");
String nombre = teclado.nextLine();

System.out.println("\nTe llamas " + nombre + " y tu edad es " + edad);
teclado.close();

}

}

 :o ¡¡Ohh!! Resulta que tras introducir la edad, no nos deja introducir el nombre y el programa termina como si hubiéramos introducido una cadena vacía para el nombre.
¿Qué ha ocurrido?
Lo explicado anteriormente.
nextInt() ha cogido lo necesario para fabricar un int, pero ha dejado el carácter "\r" en el buffer.
Luego, nextLine() ha querido leer un nombre, pero al encontrarse un "\r" en el buffer, da por hecho que el usuario ha pulsado la tecla enter así que da por finalizada la lectura a pesar de que solo obtiene una cadena vacía, porque el usuario en realidad no ha tenido ocasión de teclear nada.

Para evitar estos problemas, lo mejor es leer todo con nextLine().
Y si lo que necesitamos es un int, pues hacemos un parseo mediante la clase Integer
Código: [Seleccionar]
public class Ejemplo {

public static void main(String[] args) {
Scanner teclado = new Scanner(System.in);

System.out.print("Dime tu edad: ");
int edad = Integer.parseInt(teclado.nextLine());

System.out.print("Dime tu nombre: ");
String nombre = teclado.nextLine();

System.out.println("\nTe llamas " + nombre + " y tu edad es " + edad);
teclado.close();

}

}

Espero haber aclarado tus dudas. Si no es así, o bien, todo esto hace que surjan nuevas dudas distintas, solo tienes que preguntar.

Un saludo.

39
Aprender a programar desde cero / Re: Excepciones
« en: 25 de Junio 2023, 19:31 »
Has de usar un bucle y ayudarte de un boolean de manera que finalice el bucle solo si la lectura no ha provocado ninguna excepción.

Podría hacerse así, por ejemplo:
Código: [Seleccionar]
public class PedirDouble {

private static Scanner teclado = new Scanner(System.in);

public static void main(String[] args) {

double dato = 0;
boolean esValido = false;

while(!esValido) {
try {
dato = pedirDoublePositivo();
//Si la línea anterior no ha lanzado excepción, es que es un dato válido
esValido = true; //Ponemos fin al bucle
}
catch(Exception ex) {
System.out.println("ERROR. Dato invalido:\n" + ex.getMessage());
}
}

System.out.println("\nValor introducido: " + dato);
System.out.println("\n\t\tFIN DE PROGRAMA");

}

private static double pedirDoublePositivo() throws Exception {

System.out.print("\nIntroduzca un valor double positivo: ");
double valor = Double.parseDouble(teclado.nextLine());

if (valor < 0)
throw new Exception("El valor " + valor + " no es positivo.");
else
return valor;
}

}

40
Todo OK.

Puedes usar ambos whiles. Aunque en este caso, como queremos garantizar que se va a ejecutar al menos una vez, usar "do while" es un poco más correcto.


Por cierto, para hacer saltos de línea, en lugar de hacerlo así:
Citar
System.out.println(); System.out.println();
quizás te sea más cómodo hacerlo así:
Citar
System.out.println("\n");

\n es un carácter especial, es decir, no tiene representación gráfica en pantalla si no que conlleva una acción, en este caso, hacer un salto de línea.

Incluso imagina que quisieras dejar una separación de por ejemplo 4 líneas, pues más cómodo usando este carácter y resolverlo en una sola instrucción:
Citar
System.out.println("\n\n\n\n");

Aunque sea especial, no deja de ser un carácter, así que puedes combinarlo con otros caracteres "normales" y hacer lo que te salga de la imaginación
Citar
System.out.println("\n\nSaliendo del programa....\n\nAdiós");

Saludos.

Páginas: 1 [2] 3 4 5 6 7 ... 50

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".