Java >> Tutorial de Java >  >> Java

Comenzando con Java:construya un Robot Arena

¿Estás comenzando tu carrera como programador? ¿O has incursionado un poco en la programación pero quieres incursionar en Java?

Entonces este articulo es para usted. Pasaremos de cero a construir una arena de robots en Java .

Si te quedas atascado en algún lugar de este tutorial, debes saber que está totalmente bien. En este caso, es posible que desee aprender Java en CodeGym. Lo llevan a través de tutoriales de Java detallados y basados ​​en historias con ejercicios de codificación en el navegador que son ideales para principiantes de Java.

¡Diviértete construyendo robots con Java!

Código de ejemplo

Este artículo va acompañado de un ejemplo de código de trabajo en GitHub.

Prepararse para codificar

Antes de que podamos comenzar a escribir código, debemos configurar nuestro entorno de desarrollo. No te preocupes, esto no va a ser complicado. Lo único que necesitamos por ahora es instalar un IDE o “Entorno de desarrollo integrado”. Un IDE es un programa que usaremos para programar.

Cuando trabajo con Java, IntelliJ es mi IDE preferido. Puede usar cualquier IDE con el que se sienta cómodo, pero para este tutorial, me conformaré con instrucciones sobre cómo trabajar con IntelliJ.

Entonces, si aún no lo ha hecho, descargue e instale la edición comunitaria gratuita de IntelliJ para su sistema operativo aquí. Esperaré mientras lo descargas.

¿IntelliJ está instalado y listo? ¡Empecemos, entonces!

Antes de que nos ensuciemos las manos con el código, creamos un nuevo proyecto Java en IntelliJ. Cuando inicie IntelliJ por primera vez, debería ver un cuadro de diálogo como este:

Haga clic en "Nuevo proyecto" para abrir este cuadro de diálogo:

Si ya tiene abierto un proyecto IntelliJ diferente, puede acceder al cuadro de diálogo "Nuevo proyecto" a través de la opción "Archivo -> Nuevo -> Proyecto".

Si el cuadro desplegable "Proyecto SDK" muestra "Sin JDK", seleccione la opción "Descargar JDK" en el cuadro desplegable para instalar un JDK (Java Development Kit) antes de continuar.

Luego, haga clic en "Siguiente", haga clic en "Siguiente" nuevamente, ingrese "robot-arena" como el nombre del proyecto y finalmente haga clic en "Finalizar".

¡Felicitaciones, acaba de crear un proyecto Java! ¡Ahora es el momento de crear algo de código!

Nivel 1 - Hola Mundo

Comencemos con el programa más simple posible, el infame "Hello World" (en realidad, en Java ya se requieren bastantes conceptos para construir un programa "Hello World" … definitivamente es más simple en otros lenguajes de programación).

El objetivo es crear un programa que simplemente imprima "Hello World" en una consola.

En su nuevo proyecto Java, debería ver la siguiente estructura de carpetas a la izquierda:

Hay carpetas llamadas .idea y out , en el que IntellJ almacena alguna configuración y clases de Java compiladas … no nos molestamos con ellos por ahora.

La carpeta que nos interesa es la src carpeta, que significa "fuente", o más bien "código fuente" o "archivos fuente". Aquí es donde ponemos nuestros archivos Java.

En esta carpeta, cree un nuevo paquete haciendo clic derecho sobre él y seleccionando "Nuevo -> Paquete". Llame al paquete "nivel1".

Paquetes

En Java, los archivos de código fuente se organizan en los llamados "paquetes". Un paquete es solo una carpeta en su sistema de archivos y puede contener archivos y otros paquetes, como una carpeta normal del sistema de archivos.

En este tutorial, crearemos un paquete separado para cada capítulo (o "nivel") con todos los archivos fuente que necesitamos para ese capítulo.

En el paquete level1 , continúe y cree un nuevo archivo Java haciendo clic derecho sobre él y seleccionando "Nuevo -> Clase Java". Llame a esta nueva clase "Aplicación".

Copie el siguiente bloque de código en su nuevo archivo (reemplazando lo que ya está allí):

package level1;

public class Application {
  public static void main(String[] arguments){
    System.out.println("Hello World");
  }
}

Los programas Java están organizados en "clases", donde cada clase suele estar en su propio archivo Java separado con el mismo nombre de la clase (más sobre las clases más adelante). Verá que IntelliJ ha creado un archivo con el nombre Application.java y la clase dentro también se llama Application . Cada clase está en un paquete determinado, que se declara con package level1; en nuestro caso anterior.

Nuestro Application la clase contiene un método llamado main() . Una clase puede declarar muchos métodos como ese con los nombres que elijamos; veremos cómo más adelante en este tutorial. Un método es una unidad de código en una clase que podemos ejecutar . Puede tener entrada en forma de argumentos y la salida en forma de un valor de retorno . Nuestro main() método toma una matriz de String s como entrada y devuelve un void salida, lo que significa que no devuelve ninguna salida (consulte el vocabulario al final de este artículo si desea recapitular lo que significa un determinado término).

Un método llamado main() con el public y static modificadores es un método especial porque se considera el punto de entrada a nuestro programa. Cuando le decimos a Java que ejecute nuestro programa, ejecutará este main() método.

Hagamos esto ahora. Ejecute el programa haciendo clic derecho en Application class en el explorador de proyectos en el lado izquierdo y seleccione "Ejecutar 'Application.main()'" en el menú contextual.

IntelliJ ahora debería abrir una consola y ejecutar el programa por nosotros. Debería ver el resultado "Hello World" en la consola.

¡Felicidades! ¡Acaba de ejecutar su primer programa Java! Ejecutamos el main() método que imprimió algún texto. Siéntase libre de jugar un poco, cambiar el texto y ejecutar la aplicación nuevamente para ver qué sucede.

Exploremos ahora algunos conceptos más del lenguaje Java en el siguiente nivel.

Nivel 2 - Saludo personalizado

Modifiquemos un poco nuestro ejemplo para conocer más conceptos de Java.

El objetivo en este nivel es hacer que el programa sea más flexible, para que pueda recibir a la persona que lo ejecuta.

Primero, crea un nuevo paquete level2 y crea una nueva clase llamada Application en eso. Pegue el siguiente código en esa clase:

package level2;

public class Application {
  public static void main(String[] arguments){
    String name = arguments[0];
    System.out.println("Hello, " + name);
  }
}

Inspeccionemos este código antes de ejecutarlo. Agregamos la línea String name = arguments[0]; , pero ¿qué significa?

Con String name , declaramos una variable de tipo String . Una variable es un marcador de posición que puede contener un cierto valor, como en una ecuación matemática. En este caso, este valor es del tipo String , que es una cadena de caracteres (puede considerarlo como "texto").

Con String name = "Bob" , declararíamos una variable String que contiene el valor "Bob". Puede leer el signo igual como "se le asigna el valor de".

Con String name = arguments[0] , finalmente, declaramos una variable String que contiene el valor de la primera entrada en el arguments variable. El arguments la variable se pasa al main() método como parámetro de entrada. Es de tipo String[] , lo que significa que es una matriz de String variables, por lo que puede contener más de una cadena. Con arguments[0] , le estamos diciendo a Java que queremos tomar el primer String variable de la matriz.

Luego, con System.out.println("Hello, " + name); , imprimimos la cadena "Hola" y agregamos el valor de name variable con el operador "+".

¿Qué crees que pasará cuando ejecutes este código? Pruébelo y vea si tiene razón.

Lo más probable es que reciba un mensaje de error como este:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
	at level2.Application.main(Application.java:5)

El motivo de este error es que en la línea 5, estamos tratando de obtener el primer valor del arguments matriz, pero el arguments matriz está vacía. No hay un primer valor a obtener. A Java no le gusta eso y nos lo dice arrojándonos esta excepción.

Para solucionar esto, necesitamos pasar al menos un argumento a nuestro programa, para que el arguments matriz contendrá al menos un valor.

Para agregar un argumento a la llamada del programa, haga clic derecho en el Application clase de nuevo y seleccione "Modificar configuración de ejecución". En el campo "Argumentos del programa", ingrese su nombre. Luego, ejecute el programa nuevamente. ¡El programa ahora debería saludarte con tu nombre!

Cambie el argumento del programa a un nombre diferente y vuelva a ejecutar la aplicación para ver qué sucede.

Nivel 3 - Juega piedra, papel o tijera con un robot

¡Agreguemos un poco de diversión programando un robot!

En este nivel, vamos a crear un robot virtual que pueda jugar a piedra, papel o tijera.

Primero, crea un nuevo paquete level3 . En este paquete, cree una clase Java llamada Robot y copie el siguiente contenido en él:

package level3;

class Robot {

  String name;
  Random random = new Random();

  Robot(String name) {
    this.name = name;
  }

  String rockPaperScissors() {
    int randomNumber = this.random.nextInt(3);
    if (randomNumber == 0) {
      return "rock";
    } else if (randomNumber == 1) {
      return "paper";
    } else {
      return "scissors";
    }
  }

}

Repasemos este código para entenderlo:

Con class Robot , declaramos una nueva clase con el nombre “Robot”. Como se mencionó antes, una clase es una unidad para organizar nuestro código. Pero es más que eso. Podemos usar una clase como una “plantilla”. En nuestro caso, el Robot class es una plantilla para crear robots . Podemos usar la clase para crear uno o más robots que puedan jugar a piedra, papel o tijera.

Aprender Programación Orientada a Objetos

Si nunca antes ha estado en contacto con la programación orientada a objetos, los conceptos de clases y objetos pueden ser mucho para asimilar. No se preocupe si no comprende todos los conceptos solo con leer este artículo... vendrá con la práctica.

Si desea realizar una introducción más exhaustiva y práctica a la programación orientada a objetos con Java, es posible que desee echar un vistazo a CodeGym.

Una clase puede tener atributos y métodos . Veamos los atributos y métodos de nuestro Robot clase.

Un robot debe tener un nombre, así que con String name; declaramos un atributo con el nombre “name” y el tipo String . Un atributo es solo una variable que está vinculada a una clase.

Veremos el otro atributo con el nombre random más tarde.

El Robot la clase luego declara dos métodos:

  • El Robot() El método es otro método especial. Es un método llamado "constructor". El Robot() se utiliza para construir un nuevo objeto de la clase (o tipo) Robot . Dado que un robot debe tener un nombre, el método constructor espera un nombre como parámetro de entrada. Con this.name = name configuramos el name atributo de la clase al valor que se pasó al método constructor. Más adelante veremos cómo funciona.
  • El rockPaperScissors() El método es el método que permite a un robot jugar a piedra, papel o tijera. No requiere ninguna entrada, pero devuelve un String objeto. La cadena devuelta será una de "piedra", "papel" o "tijeras", según un número aleatorio. Con this.random.nextInt(3) usamos el generador de números aleatorios que hemos inicializado en el random atributo para crear un número aleatorio entre 0 y 2. Luego, con una construcción if/else, devolvemos una de las cadenas dependiendo del número aleatorio.

Entonces, ahora tenemos una clase de robot, pero ¿qué hacemos con ella?

Crea una nueva clase llamada Application en el level3 paquete y copie este código en él:

package level3;

class Application {

  public static void main(String[] args) {
    Robot c3po = new Robot("C3PO");
    System.out.println(c3po.rockPaperScissors());
  }

}

Esta clase tiene un main() método, al igual que en los niveles anteriores. En este método, con Robot c3po = new Robot("C3PO"); creamos un objeto de tipo Robot y almacenarlo en una variable con el nombre c3po . Con el new palabra clave, le decimos a Java que queremos llamar a un método constructor. Al final, esta línea de código llama al Robot() método constructor que hemos declarado anteriormente en el Robot clase. Dado que requiere un nombre de robot como parámetro de entrada, pasamos el nombre "C3PO".

Ahora tenemos un objeto de tipo Robot y puede dejar que juegue piedra, papel o tijera llamando al rockPaperScissors() método, que hacemos en la siguiente línea. Pasamos el resultado de ese método al System.out.println() método para imprimirlo en la consola.

Antes de ejecutar el programa, piense en lo que sucederá. Luego, ¡ejecútalo y comprueba si tenías razón!

El programa debe imprimir "piedra", "papel" o "tijeras". ¡Ejecutalo un par de veces para ver qué sucede!

Nivel 4 - Un Robot Arena

Ahora podemos crear objetos robóticos que juegan a piedra, papel o tijera. Sería divertido dejar que dos robots se batieran en duelo, ¿no?

¡Construyamos una arena en la que podamos enfrentar a dos robots!

Primero, crea un nuevo paquete level4 y copia el Robot clase del nivel anterior en este paquete. Luego, crea una nueva clase en este paquete con el nombre Arena y copie el siguiente código en él:

package level4;

class Arena {

  Robot robot1;
  Robot robot2;

  Arena(Robot robot1, Robot robot2) {
    this.robot1 = robot1;
    this.robot2 = robot2;
  }

  Robot startDuel() {
    String shape1 = robot1.rockPaperScissors();
    String shape2 = robot2.rockPaperScissors();

    System.out.println(robot1.name + ": " + shape1);
    System.out.println(robot2.name + ": " + shape2);

    if (shape1.equals("rock") && shape2.equals("scissors")) {
      return robot1;
    } else if (shape1.equals("paper") && shape2.equals("rock")) {
      return robot1;
    } else if (shape1.equals("scissors") && shape2.equals("paper")) {
      return robot1;
    } else if (shape2.equals("rock") && shape1.equals("scissors")) {
      return robot2;
    } else if (shape2.equals("paper") && shape1.equals("rock")) {
      return robot2;
    } else if (shape2.equals("scissors") && shape1.equals("paper")) {
      return robot2;
    } else {
      // both robots chose the same shape: no winner
      return null;
    }
  }
}

Investiguemos el Arena clase.

Una arena tiene dos atributos de tipo Robot :robot1 y robot2 . Dado que una arena no tiene sentido sin ningún robot, el constructor Arena() espera dos objetos de robot como parámetros de entrada. En el constructor, inicializamos los atributos con los robots pasados ​​al constructor.

La parte divertida ocurre en el startDuel() método. Este método enfrenta a los dos robots entre sí en la batalla. No espera parámetros de entrada, pero devuelve un objeto de tipo Robot . Queremos que el método devuelva el robot que ganó el duelo.

En las dos primeras líneas, llamamos a cada uno de los robots rockPaperScissors() métodos para averiguar qué forma eligió cada uno de los robots y almacenarlos en dos String variables shape1 y shape2 .

En las siguientes dos líneas, simplemente imprimimos las formas en la consola para que luego podamos ver qué robot eligió qué forma.

Luego viene una larga construcción if/else que compara las formas que ambos robots seleccionaron. Si el robot 1 eligió "piedra" y el robot 2 eligió "tijeras", devolvemos al robot 1 como ganador, porque la piedra vence a las tijeras. Esto continúa para los 6 casos diferentes. Finalmente, tenemos un else incondicional bloque que solo se alcanza si ambos robots han elegido la misma forma. En este caso, no hay ganador, por lo que devolvemos null . Nulo es un valor especial que significa "sin valor".

Ahora tenemos una Arena en la que podemos dejar que dos robots luchen entre sí. ¿Cómo empezamos un duelo?

Vamos a crear un nuevo Application clase en el level4 paquete y copie este código en él:

package level4;

class Application {

  public static void main(String[] args) {
    Robot c3po = new Robot("C3PO");
    Robot r2d2 = new Robot("R2D2");

    Arena arena = new Arena(c3po, r2d2);
    
    Robot winner = arena.startDuel();
    if (winner == null) {
      System.out.println("Draw!");
    } else {
      System.out.println(winner.name + " wins!");
    }
  }

}

¿Qué está pasando en este código?

En las dos primeras líneas, creamos dos Robot objetos.

En la siguiente línea, creamos un Arena objeto, usando el constructor discutido previamente Arena() que espera dos robots como entrada. Pasamos los dos objetos de robot que creamos anteriormente.

Luego, llamamos al startDuel() método en el arena objeto. Desde el startDuel() método devuelve el ganador del duelo, almacenamos el valor de retorno del método en el winner variable de tipo Robot .

Si el winner variable no tiene valor (es decir, tiene el valor null ), no tenemos un ganador, por lo que imprimimos "¡Draw!".

Si el winner variable tiene un valor, imprimimos el nombre del ganador.

Revisa el código nuevamente y traza en tu mente lo que sucede en cada línea de código. ¡Luego ejecute la aplicación y vea qué sucede!

Cada vez que ejecutamos el programa, ahora debería imprimir las formas de piedra, papel o tijera que cada uno de los robots ha elegido y luego imprimir el nombre del ganador o "¡Dibujar!" si no hubiera ganador.

¡Hemos construido una arena de robots!

Nivel 5 - Limpieza de la Arena

La arena de robots que hemos construido ya es bastante genial. Pero el código es un poco difícil de manejar en algunos lugares.

¡Limpiemos el código a una calidad de nivel profesional! Presentaremos algunos conceptos más de Java en el camino.

Vamos a solucionar tres problemas principales con el código:

  1. El rockPaperScissors() método en el Robot la clase devuelve un String . Podríamos introducir accidentalmente un error aquí al devolver una cadena no válida como "Duck".
  2. La gran construcción if/else en el Arena la clase es repetitiva y propensa a errores:podríamos introducir fácilmente un error copiando y pegando aquí.
  3. El startDuel() método en el Arena la clase devuelve null si no hubiera ganador. Podríamos esperar que el método siempre devuelva un ganador y olvidarnos de manejar el caso cuando devuelve null .

Antes de comenzar, cree un nuevo paquete level5 y copie todas las clases de level4 en él.

Para hacer el código un poco más seguro, primero presentaremos una nueva clase Shape . Cree esta clase y copie el siguiente código en ella:

package level5;

enum Shape {

  ROCK("rock", "scissors"),

  PAPER("paper", "rock"),

  SCISSORS("scissors", "paper");

  String name;

  String beats;

  Shape(String name, String beats) {
    this.name = name;
    this.beats = beats;
  }

  boolean beats(Shape otherShape) {
    return otherShape.name.equals(this.beats);
  }
}

El Shape class es un tipo especial de clase:una "enumeración". Esto significa que es una enumeración de valores posibles. En nuestro caso, una enumeración de formas válidas en el juego Piedra, Papel o Tijera.

La clase declara tres formas válidas:ROCK , PAPER y SCISSORS . Cada una de las declaraciones pasa dos parámetros al constructor:

  • el nombre de la forma, y
  • el nombre de la forma que late.

El constructor Shape() toma estos parámetros y los almacena en atributos de clase como hemos visto anteriormente en las otras clases.

Además creamos un método beats() se supone que eso decide si la forma supera a otra forma. Espera otra forma como parámetro de entrada y devuelve true si esa forma es la forma que this latidos de forma.

Con el Shape enum en su lugar, ahora podemos cambiar el método rockPaperScissors() en el Robot clase para devolver un Shape en lugar de una cadena:

class Robot {

  ...

  Shape rockPaperScissors() {
    int randomNumber = random.nextInt(3);
    return Shape.values()[randomNumber];
  }

}

El método ahora devuelve Shape objeto. También eliminamos la construcción if/else y la reemplazamos con Shape.values()[randomNumber] al mismo efecto. Shape.values() devuelve una matriz que contiene las tres formas. De esta matriz, simplemente elegimos el elemento con el índice aleatorio.

Con este nuevo Robot class, podemos continuar y limpiar el Arena clase:

class Arena {

   ...

  Optional<Robot> startDuel() {
    Shape shape1 = robot1.rockPaperScissors();
    Shape shape2 = robot2.rockPaperScissors();

    System.out.println(robot1.name + ": " + shape1.name);
    System.out.println(robot2.name + ": " + shape2.name);

    if (shape1.beats(shape2)) {
      return Optional.of(robot1);
    } else if (shape2.beats(shape1)) {
      return Optional.of(robot2);
    } else {
      return Optional.empty();
    }
  }
}

Cambiamos el tipo de las variables de forma de String a Shape , ya que los robots ahora devuelven Shape s.

Luego, hemos simplificado considerablemente la construcción if/else aprovechando el beats() método que hemos introducido en el Shape enumeración Si la forma del robot 1 supera la forma del robot 2, devolvemos al robot 1 como ganador. Si la forma del robot 2 supera la forma del robot 1, devolvemos al robot 2 como ganador. Si ninguna figura gana, tenemos un empate, por lo que no devolvemos ningún ganador.

Puede notar que el startDuel() El método ahora devuelve un objeto de tipo Optional<Robot> . Esto significa que el valor devuelto puede ser un robot o puede estar vacío. Devolver un Opcional es preferible a devolver un null objeto como lo hicimos antes porque deja en claro a la persona que llama al método que el valor devuelto puede estar vacío.

Para acomodar el nuevo tipo de valor devuelto, hemos cambiado el return instrucciones para devolver un robot con Optional.of(robot) o un valor vacío con Optional.empty() .

Finalmente, tenemos que adaptar nuestro Application clase al nuevo Optional valor de retorno:

class Application {

  public static void main(String[] args) {
    Robot c3po = new Robot("C3PO");
    Robot r2d2 = new Robot("R2D2");

    Arena arena = new Arena(c3po, r2d2);
    Optional<Robot> winner = arena.startDuel();
    if (winner.isEmpty()) {
      System.out.println("Draw!");
    } else {
      System.out.println(winner.get().name + " wins!");
    }
  }

}

Cambiamos el tipo del winner variable a Optional<Robot> . El Optional la clase proporciona el isEmpty() método, que usamos para determinar si tenemos un ganador o no.

Si no tenemos un ganador, aún imprimimos "¡Draw!". Si tenemos un ganador, llamamos al get() método en el Optional para obtener el robot ganador y luego imprimir su nombre.

Mira todas las clases que creaste en este nivel y resume lo que sucedería si llamas al programa.

Luego, ejecute este programa y vea qué sucede.

Debería hacer lo mismo que antes, pero hemos aprovechado algunas funciones de Java más avanzadas para que el código sea más claro y menos propenso a errores accidentales.

No se preocupe si no entendió todas las funciones que hemos utilizado en detalle. Si desea ver un tutorial más detallado de todo lo relacionado con Java, querrá consultar los tutoriales de Java de CodeGym.

Vocabulario Java

Uf, había muchos términos en el tutorial anterior. La siguiente tabla los resume para su conveniencia:

Plazo Descripción
Matriz Un tipo de variable que contiene varios elementos. Una matriz se puede declarar agregando corchetes ([] ) al tipo de una variable:String[] myArray; . Se puede acceder a los elementos de una matriz agregando corchetes con el índice del elemento deseado al nombre de la variable, comenzando con 0 para el primer elemento:myArray[0] .
Atributo Una clase puede tener cero o más atributos. Un atributo es una variable de cierto tipo que pertenece a esa clase. Los atributos se pueden usar como variables normales dentro de los métodos de la clase.
Booleano Un tipo de variable que contiene el valor true o el valor false .
Clase Una clase es una unidad para organizar el código y se puede utilizar como plantilla para crear muchos objetos con el mismo conjunto de atributos y métodos.
Constructor Un método especial que se llama cuando usamos el new palabra clave para crear un nuevo objeto de una clase. Puede tener parámetros de entrada como cualquier otro método e implícitamente devuelve un objeto del tipo de la clase en la que se encuentra.
Enumeración Una clase especial que declara una enumeración de uno o más valores válidos.
Parámetro de entrada Una variable de un tipo específico que se puede pasar a un método.
Método Un método es una función que toma algunos parámetros de entrada, hace algo con ellos y luego devuelve un valor de retorno.
Nulo Un valor especial que señala "sin valor".
Objeto Un objeto es una instancia de una clase. Una clase describe el "tipo" de un objeto. Muchos objetos pueden tener el mismo tipo.
Operador Los operadores se utilizan para comparar, concatenar o modificar variables.
Opcional Una clase proporcionada por Java que significa que una variable puede tener un valor opcional, pero el valor también puede estar vacío.
Paquete Unidad de alto nivel para organizar el código. Es solo una carpeta en el sistema de archivos.
Valor devuelto Un método puede devolver un objeto de un tipo específico. Cuando llama al método, puede asignar el valor devuelto a una variable.
Cadena Un tipo de variable que contiene una cadena de caracteres (es decir, un "texto", por así decirlo).
esto Una palabra clave especial que significa "este objeto". Se puede usar para acceder a los atributos de una clase en los métodos de las clases.
Variable Una variable puede contener un valor de cierto tipo/clase. Las variables pueden pasarse a métodos, combinarse con operadores y devolverse desde métodos.
{:.tabla}

¿Adónde ir desde aquí?

Si este artículo te hizo querer aprender más sobre Java, dirígete a CodeGym. Proporcionan una experiencia de aprendizaje muy entretenida y motivadora para Java. ¡Los ejercicios están incrustados en las historias y puede crear y ejecutar código directamente en el navegador!

Y, por supuesto, puede jugar con los ejemplos de código de este artículo en GitHub.


Etiqueta Java