ArrayList de Objetos en Java

En esta entrada vamos a escribir un programa Java que crea un ArrayList de Objetos de tipo Coche. El programa pide por teclado los datos de los coches y los guarda en el array. A continuación utilizará el ArrayList para mostrar por pantalla lo siguiente:
  • Todos los coches introducidos.
  • Todos los coches de una marca determinada.
  • Todos los coches con menos de un número determinado de Kilómetros.
  • El coche con mayor número de Kilómetros.
  • Todos los coches ordenados por número de kilómetros de menor a mayor.
Primero creamos la clase Coche:
//Clase Coche
public class Coche {
    private String matricula;
    private String marca;
    private String modelo;
    private int Km;

    public int getKm() {
        return Km;
    }

    public void setKm(int Km) {                                                                                   
        this.Km = Km;
    }
 
    public String getMarca() {
        return marca;
    }

    public void setMarca(String marca) {
        this.marca = marca;
    }

    public String getMatricula() {
        return matricula;
    }

    public void setMatricula(String matricula) {                                                                  
        this.matricula = matricula;
    }

    public String getModelo() {
        return modelo;
    }

    public void setModelo(String modelo) {
        this.modelo = modelo;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();                                                                   
        sb.append("\nMatrícula: ");
        sb.append(matricula);
        sb.append("\nMarca: ");
        sb.append(marca);
        sb.append("\nModelo: ");
        sb.append(modelo);
        sb.append("\nKm: ");
        sb.append(Km);    
        return sb.toString();
    }   
}

A continuación creamos la clase principal del proyecto:
//Clase principal
public class Basico1 {

    static Scanner sc = new Scanner(System.in);
 
 //Se crea un ArrayList para guardar objetos de tipo Coche.
    static ArrayList<Coche> coches = new ArrayList<>();
    
    //método main
    public static void main(String[] args) {
        leerCoches();
        System.out.println("\nCoches introducidos:");
        mostrarCoches();
        mostrarPorMarca();
        mostrarPorKm();
        System.out.println("\nCoche con mayor número de Km: " + mostrarMayorKm());                                
        System.out.println("\nCoches ordenados de menor a mayor número de Km");
        mostrarOrdenadosPorKm();
    } //fin método main
   
    //Método para leer coches e introducirlos en el array
    public static void leerCoches(){
 
        //Declaración de variables para leer los datos de los coches
        String matricula;
        String marca;
        String modelo;
        int Km;
  
        //Variable auxiliar que contendrá la referencia a cada coche nuevo.
        Coche aux;
        int i, N;
  
        //se pide por teclado el número de coches a leer
        do {
            System.out.print("Número de coches? ");
            N = sc.nextInt();
        } while (N < 0);
        sc.nextLine(); //limpiar el intro
  
        //lectura de N coches
        for (i = 1; i <= N; i++) {
            //leer datos de cada coche
            System.out.println("Coche " + i);
            System.out.print("Matrícula: ");
            matricula = sc.nextLine();          
            System.out.print("Marca: ");
            marca = sc.nextLine();
            System.out.print("Modelo: ");
            modelo = sc.nextLine();
            System.out.print("Número de Kilómetros: ");
            Km = sc.nextInt();
            sc.nextLine(); //limpiar el intro
   
            aux = new Coche(); //Se crea un objeto Coche y se asigna su referencia a aux                          
   
            //se asignan valores a los atributos del nuevo objeto
            aux.setMatricula(matricula);
            aux.setMarca(marca);
            aux.setModelo(modelo);
            aux.setKm(Km);
                      
            //se añade el objeto al final del array
            coches.add(aux);
        }
    } //fin método leerCoches()

Podemos representar de forma gráfica el contenido del ArrayList según se van introduciendo los objetos:





    //Método para mostrar todos los coches   
    public static void mostrarCoches(){      
        for(int i = 0; i< coches.size(); i++)
            System.out.println(coches.get(i));  //se invoca el método toString de la clase Coche                  
    }
    //Método para mostrar todos los coches de una marca que se pide por teclado                                   
    public static void mostrarPorMarca(){
        String marca;
        System.out.print("Introduce marca: ");
        marca = sc.nextLine();
        System.out.println("Coches de la marca " + marca);
        for(int i = 0; i < coches.size(); i++){
            if(coches.get(i).getMarca().equalsIgnoreCase(marca))
                System.out.println(coches.get(i));
        }
    }
    //Método para mostrar todos los coches con un número de Km inferior                                           
    //al número de Km que se pide por teclado
    public static void mostrarPorKm(){
        int Km;
        System.out.print("Introduce número de kilómetros: ");
        Km = sc.nextInt();
        System.out.println("Coches con menos de " + Km + " Km");
        for(int i = 0; i < coches.size(); i++){
            if(coches.get(i).getKm() < Km)
                System.out.println(coches.get(i));
        }
    }
    //Método que devuelve el Coche con mayor número de Km                                                         
    public static Coche mostrarMayorKm(){
        Coche mayor = coches.get(0);
        for(int i = 0; i < coches.size(); i++){
            if(coches.get(i).getKm() > mayor.getKm())
                mayor = coches.get(i);
        }
        return mayor;
    }
    //Método que muestra los coches ordenados por número de Km de menor a mayor                                   
    public static void mostrarOrdenadosPorKm(){
        int i, j;
        Coche aux;
        for(i = 0; i < coches.size()-1; i++)
            for(j=0; j < coches.size()-i-1; j++)
                if(coches.get(j+1).getKm() < coches.get(j).getKm()){
                    aux = coches.get(j+1);
                    coches.set(j+1, coches.get(j));
                    coches.set(j, aux);                
                }
        mostrarCoches();
    }
} //fin de la clase principal

Java static. Atributos y métodos estáticos o de clase

Los atributos y métodos estáticos también se llaman atributos de clase y métodos de clase.
Se declaran como static.
Supongamos una clase Coche sencilla que se utiliza en un programa de compra-venta de coches usados. De esta clase se han creado tres objetos de tipo Coche:

   

La clase contiene 6 atributos: marca, modelo, color, matrícula, precio y descuento. Supongamos que el descuento es una cantidad que se aplica a todos los coches sobre el precio de venta. Como este dato es el mismo para todos los coches y es un valor que se puede modificar en cualquier momento no debe formar parte de cada coche sino que es un dato que deben compartir todos. Esto se consigue declarándolo como atributo static.

Un Atributo static:

  • No es específico de cada objeto. Solo hay una copia del mismo y su valor es compartido por todos los objetos de la clase.
  • Podemos considerarlo como una variable global a la que tienen acceso todos los objetos de la clase. 
  • Existe y puede utilizarse aunque no existan objetos de la clase. 

Para acceder a un atributo de clase se escibe:
NombreClase.atributo
Un Método static:
  • Tiene acceso solo a los atributos estáticos de la clase.
  • No es necesario instanciar un objeto para poder utilizarlo.
Para acceder a un método de clase se escribe:
NombreClase.método()
Ejemplo:
Vamos a escribir una clase Persona que contendrá un atributo contadorPersonas que indique cuantos objetos de la clase se han creado.
contadorPersonas debe ser un atributo de clase ya que no es un valor que se deba guardar en cada objeto Persona que se crea, por lo tanto se debe declarar static:
public class Persona {

    private String nombre;                                                                                        
    private int edad;
    static int contadorPersonas;                                                                                  
    .......

}
Un ejemplo de uso desde fuera de la clase Persona podría ser mostrar su valor en la clase principal:
System.out.println(Persona.contadorPersonas);     
También podríamos modificar su valor en la clase principal de esta forma:
Persona.contadorPersonas++;     
Si contadorPersonas lo declaramos como atributo private solo podremos acceder al atributo desde fuera de la clase a través de métodos static. En ese caso podemos tener métodos get y set para acceder a contadorPersonas que sean static además de un método incrementarContador también static:

public class Persona {


    private String nombre;
    private int edad;
    private static int contadorPersonas;                                                                          
 
    .......

    public static int getContadorPersonas() {
            return contadorPersonas;
    }

    public static void setContadorPersonas(int contadorPersonas) {                                                
            Persona.contadorPersonas = contadorPersonas;
    }

    public static void incrementarContador(){
            contadorPersonas++;
    }
 
    . . . . . 
 
}

En este caso, para acceder desde fuera de la clase al contador de personas debemos usar:
Persona.metodoStatic
Por ejemplo:
System.out.println(Persona.getContadorPersonas());    

La clase persona completa sería esta:
//Clase Persona
public class Persona {

    private String nombre;
    private int edad;
    private static int contadorPersonas;

    public Persona() {
    }

    public Persona(String nombre, int edad) {                                                                     
        this.nombre = nombre;
        this.edad = edad;
    }

    public void setNombre(String nom) {
        nombre = nom;
    }

    public String getNombre() {
        return nombre;
    }

    public void setEdad(int ed) {
        edad = ed;
    }

    public int getEdad() {
        return edad;
    }

    public static int getContadorPersonas() {
            return contadorPersonas;
    }

    public static void setContadorPersonas(int contadorPersonas) {                                                
            Persona.contadorPersonas = contadorPersonas;
    }

    public static void incrementarContador() {
        contadorPersonas++;
    }
}

Ejemplo de uso de la clase Persona:

//Clase Principal
public class Estatico1 {
    public static void main(String[] args) {
        Persona p1 = new Persona("Tomás Navarra", 22);                                                            
        Persona.incrementarContador();
        Persona p3 = new Persona("Jonás Estacio", 23);
        Persona.incrementarContador();
        System.out.println("Se han creado: " + Persona.getContadorPersonas() + " personas");                      
    }
}
Vemos en el ejemplo que cada vez que se crea un nuevo objeto se incrementa el contador invocando al método incrementarContador().
En lugar de invocar al método incrementarContador() cada vez que se crea un objeto, es más correcto hacer el incremento de la variable estática directamente en el constructor.
El código de la clase Persona y de la clase principal quedaría ahora así:
//Clase Persona
public class Persona {

    private String nombre;
    private int edad;
    private static int contadorPersonas;

    public Persona() {
        contadorPersonas++;
    }

    public Persona(String nombre, int edad) {                                                                     
        this.nombre = nombre;
        this.edad = edad;
        contadorPersonas++;
    }

    public void setNombre(String nom) {
        nombre = nom;
    }

    public String getNombre() {
        return nombre;
    }

    public void setEdad(int ed) {
        edad = ed;
    }

    public int getEdad() {
        return edad;
    }

    public static int getContadorPersonas() {
            return contadorPersonas;
    }

    public static void setContadorPersonas(int contadorPersonas) {                                                
            Persona.contadorPersonas = contadorPersonas;
    }

}


//Clase Principal
public class Estatico1 {
    public static void main(String[] args) {
        Persona p1 = new Persona("Tomás Navarra", 22);
        Persona p3 = new Persona("Jonás Estacio", 23);
        System.out.println("Se han creado: " + Persona.getContadorPersonas() + " personas");                      
    }
}

Ejercicios Arrays Java. Leer nombre y sueldo de 20 empleados y mostrar el que más gana

Programa Java que lea el nombre y el sueldo de 20 empleados y muestre el nombre y el sueldo del empleado que más gana.

Para hacerlo utilizaremos dos arrays:
Un array de String para los nombres de los empleados
Un array de tipo double para los sueldos de cada empleado.

Al mismo tiempo que leemos los datos de los empleados iremos comprobando cuál es el que tiene el mayor sueldo. Para ello tomamos el sueldo del primer empleado que se lee como mayor sueldo y después vamos comprobando el resto de sueldos. Cuando encontramos alguno mayor que el mayor actual este sueldo se convierte en el nuevo mayor.

En general para calcular el mayor de una serie de números tomamos el primero como mayor y después comparamos el resto de números.

//programa que muestra el nombre y el sueldo del empleado que más gana
import java.util.*;

public class Main {

    public static void main(String[] args) {
 
        Scanner sc = new Scanner(System.in);
  
        //creamos los arrays
        String[] empleados = new String[20];
        double[] sueldos = new double[20];

        //variables donde guardar el nombre y sueldo del empleado que más gana
        String nombreMayor;
        double mayorSueldo;

        int i = 0;

        //se lee el primer empleado
        System.out.println("Lectura de nombres y sueldos de empleados: ");                                        
        System.out.print("Empleado " + (i + 1) + ": ");
        empleados[i] = sc.nextLine();
        System.out.print("Sueldo: ");
        sueldos[i] = sc.nextDouble();

        //se toma el primero leído como mayor
        mayorSueldo = sueldos[i];
        nombreMayor = empleados[i];

        //se leen el resto de empleados
        for (i = 1; i < empleados.length; i++) {
            sc.nextLine(); //limpiar el buffer
            System.out.print("Empleado " + (i + 1) + ": ");
            empleados[i] = sc.nextLine();
            System.out.print("Sueldo: ");
            sueldos[i] = sc.nextDouble();
            //se compara el sueldo leído con el mayor actual
            if (sueldos[i] > mayorSueldo) {
                mayorSueldo = sueldos[i];
                nombreMayor = empleados[i];
            }
        }

        //mostrar resultados
        System.out.println("Empleado con mayor sueldo: " + nombreMayor );                                         
        System.out.println("Sueldo: " + mayorSueldo);
    }
}

Clases Envolventes

Java es un lenguaje de programación orientado a objetos. Un programa Java debe contener objetos y las operaciones entre ellos. La única excepción a esto en un programa Java son los tipos de datos primitivos (int, double, char, etc.)
Los tipos de datos primitivos no son objetos pero en ocasiones es necesario tratarlos como tales. Por ejemplo, hay determinadas clases que manipulan objetos (ArrayList, HashMap, …). Para poder utilizar tipos primitivos con estas clases Java provee las llamadas clases envolventes también llamadas clases contenedoras o wrappers.
Cada tipo primitivo tiene su correspondiente clase envolvente:

Tipo primitivo
Clase Envolvente
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
char
Character
boolean
Boolean

Estas clases proporcionan métodos que permiten manipular el tipo de dato primitivo como si fuese un objeto.

Las conversiones entre los tipos primitivos y sus clases envolventes son automáticas. No es necesario hacer un casting. Para realizarlas se utiliza el Boxing/Unboxing.
  • Boxing: Convertir un tipo primitivo en su clase Wrapper.
  • Unboxing: Convertir un objeto de una clase  Wrapper en su tipo primitivo.

Ejemplo de Boxing:

double x = 29.95;
Double y;
y = x; // boxing      
Ejemplo de Unboxing:

double x;
Double y = 29.95;
x = y; // unboxing     

En la siguiente tabla aparecen algunos métodos de la clase Integer. El resto de clases envolventes correspondientes a tipos primitivos numéricos tienen métodos similares.

Integer(int valor)


Integer(String valor)

Constructor a partir de un int
Integer n = new Integer(20);

Constructor a partir de un String
String s = "123456";
Integer a = new Integer(s);

int intValue()
float floatValue()
double doubleValue()

Devuelve el valor equivalente
Integer n = new Integer(30);
int x = n.intValue();
double y = n.doubleValue();
int parseInt(String s)
Método estático que devuelve un int a partir de un String.
String s = "123456";
int z = Integer.parseInt(s);
       
String toBinaryString(int i)
String toOctalString(int i)
String toHexString(int i)



Métodos estáticos que devuelven un String con la representación binaria, octal o hexadecimal del número.
int numero = 12;
String binario = Integer.toBinaryString(numero);

Integer valueOf(String s)
Método Estático. Devuelve un Integer a partir de un String.
Integer m = Integer.valueOf("123");


Clase Character

Provee una serie de métodos para manipular los datos de tipo char. En la siguiente tabla aparecen algunos de estos métodos. 

Character(char c)



Constructor a partir de un char
char car = 'x';
Character a = new Character(car);
char charValue()

Devuelve el char equivalente
Character n = new Character('q');
char c = n.charValue();

boolean isLowerCase(char ch) boolean isUpperCase(char ch)
boolean isDigit(char ch)
boolean isLetter(char ch)
Comprueba si es un carácter en minúsculas.
Comprueba si es un carácter en mayúsculas.
Comprueba si es un dígito (carácter del 0 al 9).
Comprueba si es una letra.
Todos son estáticos.
if(Character.isUpperCase(c)){
  .....           
}
       
char toLowerCase(char ch)
char toUpperCase(char ch)




Devuelve el char en mayúsculas.
Devuelve el char en minúsculas.
Métodos estáticos.
char car = 'u';
System.out.println(Character.toUpperCase(car));
Character valueOf(char c)
Método Estático. Devuelve un Character a partir de un char.
Character m = Character.valueOf('a');
Ejemplo de uso de la clase Character: programa que lee un texto por teclado y muestra cuántos dígitos y letras contiene.
import java.util.Scanner;

public class MainJavaApplication {

    public static void main(String[] args) {
 
        Scanner sc = new Scanner(System.in);
        String texto;
        int cuentaCifras = 0, cuentaLetras = 0;                                                                   
  
        System.out.println("Introduce texto ");
        texto = sc.nextLine();
  
        for (int i = 0; i < texto.length(); i++) {
            if (Character.isDigit(texto.charAt(i))) {
                cuentaCifras++;
            } else if (Character.isLetter(texto.charAt(i))) {
                cuentaLetras++;
            }
        }
  
        System.out.println("El texto contiene " + cuentaCifras + " dígitos");                                     
        System.out.println("El texto contiene " + cuentaLetras + " letras");
    }
}

Clase String

Un String en Java representa una cadena de caracteres no modificable.
Todos los literales de la forma "cualquier texto", es decir, literales entre comillas dobles, que aparecen en un programa java se implementan como objetos de la clase String.
CREAR UN STRING
Se puede crear un String de varias formas, entre ellas:
-         Utilizando una cadena de caracteres entre comillas:
String s1 = "abcdef";   
-         Utilizando operador de concatenación +con dos o más objetos String:
String s2 = s1 + "ghij";      //s2 contiene "abcdefghij"
String s3 = s1 + s2 + "klm";  //s3 contiene " abcdefabcdefghijklm"               
Además la clase String proporciona varios constructores, entre ellos:

CONSTRUCTOR
DESCRIPCIÓN
String()
Constructor por defecto. El nuevo String toma el valor ""
String s = new String();   //crea el string s vacío.  
Equivale a:   String s = "";
String(String s )
Crea un nuevo String, copiando el que recibe como parámetro.
String s = "hola";
String s1 = new String(s);  
//crea el String s1 y le copia el contenido de s
String( char[] v )  
Crea un String y le asigna como valor los caracteres contenidos en el array recibido como parámetro.
char [] a = {'a', 'b', 'c', 'd', 'e'};
String s = new String(a);  
//crea String s con valor "abcde"
String( char[] v , int pos, int n)
Crea un String y le asigna como valor los n caracteres contenidos en el array recibido como parámetro, a partir de la posición pos.
char [] a = {'a', 'b', 'c', 'd', 'e'};
String s = new String(a, 1, 3);
//crea String s con valor "bcd";


MÉTODOS DE LA CLASE STRING
La clase String proporciona métodos para el tratamiento de las cadenas de caracteres: acceso a caracteres individuales, buscar y extraer una subcadena, copiar cadenas, convertir cadenas a mayúsculas o minúsculas, etc.


MÉTODO
DESCRIPCIÓN
length()
Devuelve la longitud de la cadena
indexOf(‘caracter’)
Devuelve la posición de la primera aparición de carácter dentro del String. Devuelve -1 si no lo encuentra.
lastIndexOf(‘caracter’)
Devuelve la posición de la última aparición de carácter dentro del String. Devuelve -1 si no lo encuentra.
charAt(n)
Devuelve el carácter que está en la posición n
substring(n1,n2)
Devuelve la subcadena desde la posición n1 hasta n2 - 1
toUpperCase()
Devuelve la cadena convertida a mayúsculas
toLowerCase()
Devuelve la cadena convertida a minúsculas
equals(otroString)
Compara dos cadenas y devuelve true si son iguales
equalsIgnoreCase(otroString)
Igual que equals pero sin considerar mayúsculas y minúsculas
compareTo(OtroString)
Devuelve 0 si las dos cadenas son iguales. <0 si la primera es alfabéticamente menor que la segunda ó >0 si la primera es alfabéticamente mayor que la segunda.
compareToIgnoreCase(OtroString)
Igual que compareTo pero sin considerar mayúsculas y minúsculas.
valueOf(N)
Convierte el valor N a String. N puede ser de cualquier tipo.


Los puedes consultar todos en la API de Java:

Debemos recordar que:
Los objetos String no son modificables.
Por lo tanto, los métodos que actúan sobre un String con la intención de modificarlo lo que hacen es crear un nuevo String a partir del original y devolverlo modificado.
Por ejemplo: Una operación como convertir a mayúsculas o minúsculas un String no lo modificará sino que creará y devolverá un nuevo String con el resultado de la operación.
String s = "abc";            


s = s.toUpperCase(); //convertir a mayúsculas el contenido del String s      

El recolector de basura es el encargado de eliminar de forma automática los objetos a los que ya no hace referencia ninguna variable.



EL OPERADOR DE CONCATENACIÓN +
La clase proporciona el operador + (concatenación) para unir dos o más String.
El resultado de aplicar este operador es un nuevo String concatenación de los otros.
Por ejemplo, si tenemos dos String b y c:
String b = "Ordenador";
String c = " Portátil";               
La operación:  
b = b + c;               
crea un nuevo String (b + c) y le asigna su dirección a b: