Hytale Modding
Conceptos básicos de Java

09 - Trabajando con cadenas

Domina la manipulación de texto y las operaciones con strings en Java.

Los Strings son uno de los tipos de clase más usados en Java. Son fundamentales para gestionar nombres de jugador, mensajes de chat, descripciones de objetos y, prácticamente, todo lo que ves.

Conceptos Básicos del String

La clase String se utiliza para almacenar texto:

String name = "Simon";
String message = "¡Bienvenido a Hytale!";
String empty = "";
Los Strings son Inmutables

Una vez declarado un String NO se puede alterar Una vez declarado un String NO se puede alterar ¡Los métodos que aparentan modificar un String realmente crean una nueva! Una vez declarado un String NO se puede alterar ¡Los métodos que aparentan modificar un String realmente crean una nueva!

String text = "Hola";
text.toUpperCase();           // Crea "HOLA" pero no se guarda
System.out.println(text);     // Sigue teniendo el valor "Hola"

String upper = text.toUpperCase();  //Guarda el resultado
System.out.println(upper);          // "HOLA"

Métodos de String

Longitud

String nombre = "Alice";
int longitud = nombre.length();
System.out.println(longitud);  // 5

Conversión a Mayúsculas / Minúsculas

String texto = "Hola Mundo";
String mayuscula = texto.toUpperCase();  // "HOLA MUNDO"
String minuscula = texto.toLowerCase();  // "hola mundo"

Comprobación de contenido

String mensaje = "Bienvenido a Hytale";

boolean tieneHytale = mensaje.contains("Hytale");  // verdadero (true)
boolean tieneMinecraft = mensaje.contains("Minecraft");  // falso (false)

boolean empiezaEn = mensaje.startsWith("Bienvenido");  // verdadero (true)
boolean acabaEn = mensaje.endsWith("Hytale");  // verdadero (true)

Extraer partes del String

String texto = "Hola Mundo";

char primer = texto.charAt(0);      // 'H'
char ultimo = texto.charAt(10);      // 'o'

String sub1 = texto.substring(0, 5);   // "Hola"
String sub2 = texto.substring(6);      // "Mundo"
Índice de Substring

substring(start, end) incluye start pero excluye end:

String texto = "Hytale";
//             012345 (índices)

texto.substring(0, 2);  // "Hy" (índices 0 y 1)
texto.substring(2, 6);  // "tale" (índices 2, 3, 4, 5)
texto.substring(2);     // "tale" (de 2 hasta el final)

Substituir texto

String texto = "Yo amo Minecraft";
String substituido = texto.replace("Minecraft", "Hytale");
System.out.println(substituido);  // "Yo amo Hytale"

String sinEspacios = "Hola Mundo".replace(" ", "");
System.out.println(sinEspacios);  // "HolaMundo"

Recortando espacios vacíos

String sucio = "  Hola Mundo  ";
String limpio = sucio.trim();
System.out.println(limpio);  // "Hola Mundo" (sin espacios al final)

Dividiendo Strings

String comando = "give player sword 5";
String[] partes = comando.split(" ");

System.out.println(partes[0]);  // "give"
System.out.println(partes[1]);  // "player"
System.out.println(partes[2]);  // "sword"
System.out.println(partes[3]);  // "5"

Comparación de Strings

Nunca use == para Strings

Siempre use .equals() para comparar los contenidos de las String!

String nombre1 = "Steve";
String nombre2 = "Steve";

// Incorrecto - Compara la referencia de los objetos
if (nombre1 == nombre2) {
    System.out.println("Igual");
}

// Correcto - Compara el texto
if (nombre1.equals(nombre2)) {
    System.out.println("Igual");
}

// Ignora la capitalización al comparar
if (nombre1.equalsIgnoreCase("steve")) {
    System.out.println("Igual (ignorando capitalización)");
}

Orden de comparación

String a = "Manzana";
String b = "Banana";

int resultado = a.compareTo(b);
// resultado < 0 if a viene antes de la b
// resultado == 0 if a es igual a b
// resultado > 0 if a viene despues de la b

if (a.compareTo(b) < 0) {
    System.out.println(a + " viene antes que  " + b);
}

Concatenación de Strings

Usando el operador +

String primera = "Hola";
String segunda = "Mundo";
String combinadas = primera + " " + segunda;  // "Hola Mundo"

int puntuacion = 100;
String message = "Puntuación: " + puntuacion;  // "Puntuación: 100"

Usando concat()

String resultado = "Hola".concat(" Mundo");  // "Hola Mundo"

Construyendo Strings largas

Para muchas concatenaciones, use StringBuilder:

StringBuilder builder = new StringBuilder();
builder.append("Jugador: ");
builder.append("Alice");
builder.append(", Nivel: ");
builder.append(10);
builder.append(", Vida: ");
builder.append(100);

String resultado = builder.toString();
System.out.println(resultado);
// "Jugador: Alice, Nivel: 10, Vida: 100"
¿Por qué StringBuilder?

La concatenación regular crea muchas String temporales:

// Ineficiente - Crea muchas String temporales
String resultado = "";
for (int i = 0; i < 1000; i++) {
    resultado = resultado + i;  // ¡Crea 1000 String temporales!
}

// Eficiente - StringBuilder es mutable
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);  // Modifica un objeto
}
String resultado = sb.toString();

¡Use StringBuilder cuando construya Strings en bucles!

Ejemplos prácticos

Parsear (o analizar) un comando de un jugador

public class ParseadordeComandos {
    public static void parsearComando(String command) {
        // "/give Steve diamond_sword 1"
        String[] parts = command.split(" ");
        
        String accion = parts[0].replace("/", "");  // "give"
        String jugador = parts[1];                   // "Steve"
        String item = parts[2];                     // "diamond_sword"
        int cantidad = Integer.parseInt(parts[3]);    // 1
        
        System.out.println("Acción: " + accion);
        System.out.println("Jugador: " + jugador);
        System.out.println("Ítem: " + item);
        System.out.println("Cantidad: " + cantidad);
    }
    
    public static void main(String[] args) {
        parsearComando("/give Steve diamond_sword 1");
    }
}

Formatear el nombre visible del jugador

public class FormateadordeJugador{
    public static String NombredeFormato(String nombre, int nivel, String rango) {
        StringBuilder formateado = new StringBuilder();
        
        if (rango != null && !rango.isEmpty()) {
            formateado.append("[").append(rango).append("] ");
        }
        
        formateado.append(name);
        formateado.append(" (Nv. ").append(nivel).append(")");
        
        return formateado.toString();
    }
    
    public static void main(String[] args) {
        String visible1 = NombredeFormato("Alice", 10, "VIP");
        System.out.println(visible1);  // "[VIP] Alice (Nv. 10)"
        
        String visible2 = NombredeFormato("Bob", 5, null);
        System.out.println(visible2);  // "Bob (Nv. 5)"
    }
}

Validar nombre de usuario

public class ValidadordeNombredeUsuario{
    public static boolean esValido(String nombredeusuario) {
        // Reglas: 3-16 carácteres, sólo letras y números
        
        if (nombredeusuario == null || nombredeusuario.isEmpty()) {
            return false;
        }
        
        nombredeusuario = nombredeusuario.trim();
        
        if (nombredeusuario.length() < 3 || nombredeusuario.length() > 16) {
            return false;
        }
        
        for (int i = 0; i < nombredeusuario.length(); i++) {
            char c = nombredeusuario.charAt(i);
            if (!Character.isLetterOrDigit(c)) {
                return false;
            }
        }
        
        return true;
    }
    
    public static void main(String[] args) {
        System.out.println(esValido("Steve"));      // verdadero (true) 
        System.out.println(esValido("Player_123")); // falso (false, lleva guión bajo)
        System.out.println(esValido("ab"));         // falso (false, demasiado corto)
        System.out.println(esValido(""));           // falso (false, vacío)
    }
}

Creador de descripción de objetos

public class DescripciondeItem{
    public static String descripciondeCreacion(String nombre, String rareza, 
                                         int dano, int durabilidad) {
        StringBuilder desc = new StringBuilder();
        
        // Título con código de color de rareza
        desc.append(consigueColorRareza(rareza));
        desc.append(nombre);
        desc.append("\n");
        
        // Estadísticas
        desc.append("Daño: ").append(dano).append("\n");
        desc.append("Durabilidad: ").append(durabilidad).append("/");
        desc.append(durabilidad).append("\n");
        desc.append("Rareza: ").append(rareza);
        
        return desc.toString();
    }
    
    private static String consigueColorRareza(String rareza) {
        switch (rareza.toLowerCase()) {
            case "legendaria": return "§6";  // Dorado
            case "epica": return "§5";       // Morado
            case "rara": return "§9";       // Azul
            case "comun": return "§f";     // Blanco
            default: return "§7";           // Gris
        }
    }
    
    public static void main(String[] args) {
        String desc = descripciondeCreacion("Excalibur", "Legendaria", 50, 1000);
        System.out.println(desc);
    }
}

Filtro de mensajes del chat

public class FiltrodelChat {
    private static String[] palabrasProhibidas = {"palabrota1", "palabrota2"};
    
    public static String filtrarMensaje(String mensaje) {
        String filtrado = mensaje.toLowerCase();
        
        for (String palabra : palabrasProhibidas) {
            if (filtrado.contains(word)) {
                String reemplazamiento = "*".repeat(word.length());
                filtrado = filtered.replace(palabra, reemplazamiento);
            }
        }
        
        return filtrado;
    }
    
    public static void main(String[] args) {
        String mensaje = "Este es un test de una palabrota1";
        String limpio = filterMessage(mensaje);
        System.out.println(limpio );  // "Este es un test de una ********"
    }
}

Formateo de Strings

Usando String.format()

String nombre = "Alice";
int nivel = 10;
double vida = 85.5;

String formateado = String.format("%s es nivel %d con %.1f%% de vida", 
                                 nombre, nivel, vida);
System.out.println(formateado);
// "Alice es nivel 10 con 85.5% de vida"
Especificadores de Formato

Códigos de Formato típicos

  • %s - String
  • %d - Integro
  • %f - Valor de punto flotante
  • %.2f - Float con 2 posiciones decimales
  • %n - Nueva línea (independiente de la plataforma)
String.format("Nombre: %s", "Steve");          // "Nombre: Steve"
String.format("Nivel: %d", 10);              // "Nivel: 10"
String.format("Vida: %.1f", 85.5);         // "Vida: 85.5"
String.format("Posición: (%d, %d, %d)", 
              10, 64, -5);                    // "Posición: (10, 64, -5)"

Operadores de String típicos

Consultar si un String está vacío

String texto = "";

// Consulta si está vacío o es nulo
if (texto == null || texto.isEmpty()) {
    System.out.println("¡Vacío!");
}

// Consulta si es vacio, nulo, o que sólo tenga espacios
if (texto == null || texto.trim().isEmpty()) {
    System.out.println("¡Vacío o con sólo espacios!");
}

Cuenta de ocurrencias

public static int contadordeOcurrencias(String texto, String objetivo) {
    int cuenta = 0;
    int indice = 0;
    
    while ((indice = texto.indexOf(objetivo, indice)) != -1) {
        cuenta++;
        indice += objetivo.length();
    }
    
    return cuenta;
}

// Uso
int cuenta = contadordeOcurrencias("hola hola mundo", "hola");
System.out.println(cuenta);  // 2

Invertir un String

public static String invertir(String texto) {
    StringBuilder sb = new StringBuilder(texto);
    return sb.reverse().toString();
}

// Usage
String invertido = invertir("Hola");
System.out.println(invertido);  // "aloH"

Comprobar si es palíndromo (se lee igual del derecho que del revés)

public static boolean esPalindromo(String texto) {
    String limpiado= texto.toLowerCase().replaceAll("[^a-z0-9]", "");
    String invertido = new StringBuilder(limpiado).reverse().toString();
    return limpiado.equals(invertido);
}

// Uso
System.out.println(isPalindrome("ama"));     // verdadero (true)
System.out.println(isPalindrome("hola"));       // falso (false)

Ejercicios de práctica

  1. Verificador de nombre de usuario: Escriba un método que compruebe si un nombre de usuario:

    • Tiene entre 3 y 16 caracteres
    • Contiene únicamente letras, números y guiones bajos
    • No comienza en un número
    • Devuelve verdadero (true) si es válido, falso (false) de lo contrario
  2. Parseador (Analizador) de Comandos: Parsee el formato de este comando: /teleport x y z

    • Extraiga las coordenadas
    • Conviértalas en enteros
    • Devuelva una array con tres valores
  3. Formateador de Chat: Cree un método que formatee mensajes de chat:

    • Entrada: nombre de usuario, rango, mensaje
    • Salida: "[RANGO] NombredeUsuario: mensaje"
    • Si no tiene rango, muestre únicamente "NombredeUsuario: mensaje"
  4. Contador de palabras: Cuente cuantas palabras hay en una frase (las palabras están separadas por espacios)

  5. Conversión de título a mayúsculas: Convierta el siguiente String a un título en mayúsculas:

    • Entrada: «hola mundo»
    • Salida: «Hola Mundo»