Skip to content
IRC-CodingIRC-Coding
Métodos estáticosAtributos de instanciaRelaciones de clasesGenericsUML

Clases OOP: métodos Static vs Instance

Aprende las diferencias entre métodos estáticos e de instancia, relaciones de clases, Generics y UML con ejemplos prácticos.

S

schutzgeist

9 min read
Clases OOP: métodos Static vs Instance

Clases OOP: Métodos estáticos vs de instancia, atributos y relaciones

Este artículo es una guía exhaustiva de los componentes de clases OOP, incluyendo elementos estáticos e de instancia, relaciones y genéricos con ejemplos prácticos.

Resumen rápido

Las clases se componen de atributos, métodos, visibilidades y contratos. Los elementos estáticos pertenecen a la clase en su conjunto, mientras que los elementos de instancia pertenecen a objetos individuales. Los genéricos aumentan la seguridad de tipos y la reutilización de código.

Descripción técnica compacta

Una clase define estructura y comportamiento a través de atributos, métodos, visibilidades, constructores, invariantes y contratos.

Elementos estáticos vs de instancia:

Elementos estáticos (a nivel de clase)

  • Atributos estáticos: Pertenecen a la clase, no a los objetos
  • Métodos estáticos: Se pueden invocar sin crear un objeto
  • Uso: Contadores, cachés, fábricas, constantes
  • Acceso: A través del nombre de la clase, no del objeto

Elementos de instancia (a nivel de objeto)

  • Atributos de instancia: Cada objeto tiene su propia copia
  • Métodos de instancia: Operan sobre el estado del objeto
  • Uso: Datos y comportamiento específicos del objeto
  • Acceso: Solo a través de la instancia del objeto

Relaciones entre clases (UML):

  • Asociación: Relación simple entre clases
  • Agregación: Relación “tiene-un” con partes independientes
  • Composición: Relación “es-parte-de” con partes dependientes
  • Especialización: Una subclase hereda de una clase base

Genéricos:

  • Seguridad de tipos: Verificación de tipos en tiempo de compilación
  • Reutilización: Un código para diferentes tipos
  • Contenedores: List<T>, Map<K,V>, std::vector<T>

Puntos clave para el examen

  • Atributos estáticos: Pertenecen a la clase, valor compartido por todos los objetos
  • Atributos de instancia: Cada objeto tiene su propia copia
  • Métodos estáticos: Se invocan sin objeto, no tienen puntero this
  • Métodos de instancia: Operan sobre el estado del objeto con puntero this
  • Relaciones entre clases: Asociación, agregación, composición, herencia
  • Notación UML: Símbolos diferentes para tipos de relaciones
  • Genéricos: Plantillas de tipo para contenedores con seguridad de tipos
  • Relevancia en exámenes: Comprensión fundamental para desarrollo OOP

Componentes principales

  1. Atributos estáticos: Variables de clase para estado compartido
  2. Atributos de instancia: Variables de objeto para estado individual
  3. Métodos estáticos: Métodos de clase sin referencia a objetos
  4. Métodos de instancia: Métodos de objeto con acceso al estado
  5. Constructores: Inicialización de objetos
  6. Relaciones: Conexiones estructurales entre clases
  7. Genéricos: Clases y métodos con parámetros de tipo
  8. UML: Notación gráfica para diseño de clases

Ejemplos prácticos

1. Elementos estáticos vs de instancia en Java

public class Mitarbeiter {
    // Atributos estáticos (pertenecen a la clase)
    private static int anzahlMitarbeiter = 0;
    private static final String FIRMA = "TechCorp";
    private static double mindestgehalt = 2000.0;
    
    // Atributos de instancia (pertenecen al objeto)
    private int mitarbeiterId;
    private String name;
    private double gehalt;
    
    // Bloque de inicialización estático
    static {
        System.out.println("Mitarbeiter-Klasse wird geladen");
        anzahlMitarbeiter = 0;
    }
    
    // Constructor (inicialización de instancia)
    public Mitarbeiter(String name, double gehalt) {
        this.mitarbeiterId = ++anzahlMitarbeiter;
        this.name = name;
        this.gehalt = Math.max(gehalt, mindestgehalt);
        
        System.out.println("Mitarbeiter " + name + " erstellt (ID: " + mitarbeiterId + ")");
    }
    
    // Método estático (se puede invocar sin objeto)
    public static int getAnzahlMitarbeiter() {
        return anzahlMitarbeiter;
    }
    
    public static String getFirma() {
        return FIRMA;
    }
    
    public static void setMindestgehalt(double mindestgehalt) {
        if (mindestgehalt > 0) {
            Mitarbeiter.mindestgehalt = mindestgehalt;
        }
    }
    
    // Método de instancia (requiere objeto)
    public void erhoeheGehalt(double prozent) {
        this.gehalt *= (1 + prozent / 100);
        System.out.println(name + "s Gehalt erhöht auf " + gehalt);
    }
    
    public void anzeigen() {
        System.out.println("ID: " + mitarbeiterId + ", Name: " + name + 
                          ", Gehalt: " + gehalt + ", Firma: " + FIRMA);
    }
    
    // Getters/Setters para atributos de instancia
    public String getName() {
        return name;
    }
    
    public double getGehalt() {
        return gehalt;
    }
}

// Uso de la clase
public class MitarbeiterDemo {
    public static void main(String[] args) {
        // Invocar métodos estáticos (sin objeto)
        System.out.println("Anzahl Mitarbeiter: " + Mitarbeiter.getAnzahlMitarbeiter());
        System.out.println("Firma: " + Mitarbeiter.getFirma());
        
        Mitarbeiter.setMindestgehalt(2500.0);
        
        // Crear objetos (instancias)
        Mitarbeiter alice = new Mitarbeiter("Alice", 3000.0);
        Mitarbeiter bob = new Mitarbeiter("Bob", 2800.0);
        
        // Invocar métodos de instancia
        alice.erhoeheGehalt(5.0);
        bob.anzeigen();
        
        // Método estático después de crear objetos
        System.out.println("Anzahl Mitarbeiter: " + Mitarbeiter.getAnzahlMitarbeiter());
        
        // Error: this no está disponible en método estático
        // public static void fehlerhafteMethode() {
        //     System.out.println(this.name); // Fehler: kann nicht auf this zugreifen
        // }
    }
}

2. Relaciones entre clases con ejemplos UML

// Asociación: Docente imparte Cursos
public class Dozent {
    private String name;
    private List<Kurs> unterrichteteKurse = new ArrayList<>();
    
    public Dozent(String name) {
        this.name = name;
    }
    
    public void addKurs(Kurs kurs) {
        unterrichteteKurse.add(kurs);
        kurs.setDozent(this); // Referencia inversa
    }
    
    public void zeigeKurse() {
        System.out.println(name + " unterrichtet:");
        for (Kurs kurs : unterrichteteKurse) {
            System.out.println("  - " + kurs.getTitel());
        }
    }
}

public class Kurs {
    private String titel;
    private Dozent dozent; // Referencia inversa
    
    public Kurs(String titel) {
        this.titel = titel;
    }
    
    public void setDozent(Dozent dozent) {
        this.dozent = dozent;
    }
    
    public String getTitel() {
        return titel;
    }
}

// Agregación: Departamento tiene Empleados (pueden existir sin el departamento)
public class Abteilung {
    private String name;
    private List<Mitarbeiter> mitarbeiter = new ArrayList<>();
    
    public Abteilung(String name) {
        this.name = name;
    }
    
    public void addMitarbeiter(Mitarbeiter mitarbeiter) {
        this.mitarbeiter.add(mitarbeiter);
    }
    
    public void removeMitarbeiter(Mitarbeiter mitarbeiter) {
        this.mitarbeiter.remove(mitarbeiter);
        // Mitarbeiter sigue existiendo
    }
}

// Composición: Pedido tiene Líneas de Pedido (existen solo con el pedido)
public class Bestellung {
    private String bestellId;
    private List<Bestellposition> positionen = new ArrayList<>();
    
    public Bestellung(String bestellId) {
        this.bestellId = bestellId;
    }
    
    public void addPosition(String produkt, int menge, double preis) {
        Bestellposition position = new Bestellposition(produkt, menge, preis);
        positionen.add(position);
    }
    
    public double berechneGesamtbetrag() {
        return positionen.stream()
            .mapToDouble(Bestellposition::getGesamtpreis)
            .sum();
    }
    
    // Clase interna para composición
    private class Bestellposition {
        private String produkt;
        private int menge;
        private double einzelpreis;
        
        public Bestellposition(String produkt, int menge, double einzelpreis) {
            this.produkt = produkt;
            this.menge = menge;
            this.einzelpreis = einzelpreis;
        }
        
        public double getGesamtpreis() {
            return menge * einzelpreis;
        }
    }
}

3. Genéricos con elementos estáticos

// Clase genérica con elementos estáticos
public class Container<T> {
    // Atributos estáticos (¡no son genéricos!)
    private static int anzahlContainer = 0;
    private static final String VERSION = "1.0";
    
    // Atributos de instancia (son genéricos)
    private T inhalt;
    private int id;
    
    public Container(T inhalt) {
        this.inhalt = inhalt;
        this.id = ++anzahlContainer;
    }
    
    // Método estático (no puede acceder a T)
    public static int getAnzahlContainer() {
        return anzahlContainer;
    }
    
    public static String getVersion() {
        return VERSION;
    }
    
    // Método de instancia (puede acceder a T)
    public T getInhalt() {
        return inhalt;
    }
    
    public void setInhalt(T inhalt) {
        this.inhalt = inhalt;
    }
    
    public void anzeigen() {
        System.out.println("Container #" + id + ": " + 
                          (inhalt != null ? inhalt.toString() : "leer"));
    }
    
    // Método genérico (estático)
    public static <U> Container<U> create(U inhalt) {
        return new Container<>(inhalt);
    }
}

// Uso
public class ContainerDemo {
    public static void main(String[] args) {
        // Llamar métodos estáticos
        System.out.println("Container-Version: " + Container.getVersion());
        
        // Diferentes tipos de contenedor
        Container<String> stringContainer = new Container<>("Hallo");
        Container<Integer> intContainer = new Container<>(42);
        Container<Double> doubleContainer = Container.create(3.14);
        
        stringContainer.anzeigen();
        intContainer.anzeigen();
        doubleContainer.anzeigen();
        
        System.out.println("Anzahl Container: " + Container.getAnzahlContainer());
        
        // Error: Los atributos estáticos no son genéricos
        // Container<String>.getAnzahlContainer(); // Error de sintaxis
    }
}

4. Factory Pattern con métodos estáticos

public class FahrzeugFactory {
    // Métodos factory estáticos
    public static Fahrzeug erstellePKW(String marke, int leistung) {
        return new PKW(marke, leistung, 4);
    }
    
    public static Fahrzeug erstelleMotorrad(String marke, int leistung) {
        return new Motorrad(marke, leistung, false);
    }
    
    public static Fahrzeug erstelleLKW(String marke, int leistung, double ladung) {
        return new LKW(marke, leistung, ladung);
    }
    
    // Método estático con validación
    public static Fahrzeug erstelleFahrzeug(String typ, String marke, int leistung) {
        switch (typ.toLowerCase()) {
            case "pkw":
                return erstellePKW(marke, leistung);
            case "motorrad":
                return erstelleMotorrad(marke, leistung);
            case "lkw":
                return erstelleLKW(marke, leistung, 1000.0);
            default:
                throw new IllegalArgumentException("Unbekannter Fahrzeugtyp: " + typ);
        }
    }
}

// Clase base abstracta
abstract class Fahrzeug {
    protected String marke;
    protected int leistung;
    
    public Fahrzeug(String marke, int leistung) {
        this.marke = marke;
        this.leistung = leistung;
    }
    
    public abstract void anzeigen();
}

// Clases concretas
class PKW extends Fahrzeug {
    private int anzahlTueren;
    
    public PKW(String marke, int leistung, int anzahlTueren) {
        super(marke, leistung);
        this.anzahlTueren = anzahlTueren;
    }
    
    @Override
    public void anzeigen() {
        System.out.println("PKW: " + marke + ", " + leistung + " PS, " + anzahlTueren + " Türen");
    }
}

class Motorrad extends Fahrzeug {
    private boolean hatSeitenwagen;
    
    public Motorrad(String marke, int leistung, boolean hatSeitenwagen) {
        super(marke, leistung);
        this.hatSeitenwagen = hatSeitenwagen;
    }
    
    @Override
    public void anzeigen() {
        System.out.println("Motorrad: " + marke + ", " + leistung + " PS, " + 
                          (hatSeitenwagen ? "mit" : "ohne") + " Seitenwagen");
    }
}

class LKW extends Fahrzeug {
    private double ladung;
    
    public LKW(String marke, int leistung, double ladung) {
        super(marke, leistung);
        this.ladung = ladung;
    }
    
    @Override
    public void anzeigen() {
        System.out.println("LKW: " + marke + ", " + leistung + " PS, " + ladung + " kg Ladung");
    }
}

// Uso de la factory
public class FactoryDemo {
    public static void main(String[] args) {
        // Usar métodos factory estáticos
        Fahrzeug golf = FahrzeugFactory.erstellePKW("Volkswagen", 110);
        Fahrzeug harley = FahrzeugFactory.erstelleMotorrad("Harley", 80);
        Fahrzeug scania = FahrzeugFactory.erstelleLKW("Scania", 500, 20000.0);
        
        golf.anzeigen();
        harley.anzeigen();
        scania.anzeigen();
        
        // Creación dinámica
        Fahrzeug bmw = FahrzeugFactory.erstelleFahrzeug("pkw", "BMW", 150);
        bmw.anzeigen();
    }
}

Notación UML para componentes de clase

Clase con elementos estáticos e instancia

+---------------------------+
|        Mitarbeiter         |
+---------------------------+
| - anzahlMitarbeiter: int  |  <<static>>
| - FIRMA: String          |  <<static>>
| - mitarbeiterId: int     |
| - name: String           |
| - gehalt: double         |
+---------------------------+
| + getAnzahlMitarbeiter(): int | <<static>>
| + setMindestgehalt(double): void | <<static>>
| + erhoeheGehalt(double): void    |
| + anzeigen(): void              |
+---------------------------+

Relaciones en UML

Dozent 1..* --* Kurs           (Asociación)
Abteilung 1 --o* Mitarbeiter    (Agregación)
Bestellung 1 --* Bestellposition (Composición)
Fahrzeug <|-- PKW               (Herencia)

Estático vs. Instancia: Guía de decisión

Cuándo usar elementos estáticos

Atributos estáticos:

  • Contadores para todas las instancias
  • Constantes para toda la clase
  • Recursos compartidos, como conexiones a bases de datos
  • Cachés para la clase

Métodos estáticos:

  • Métodos factory para crear objetos
  • Métodos de utilidad sin estado
  • Métodos de conversión
  • Métodos de validación

Cuándo usar elementos de instancia

Atributos de instancia:

  • Datos específicos del objeto
  • Estado que cambia
  • Configuración por objeto

Métodos de instancia:

  • Métodos que acceden al estado del objeto
  • Comportamiento que depende de datos de instancia
  • Métodos que necesitan el puntero this

Ventajas y desventajas

Ventajas de elementos estáticos

  • Eficiencia de memoria: Solo una copia para todos los objetos
  • Acceso simple: Llamable sin crear instancia
  • Estado compartido: Igual para todos los objetos
  • Factory Pattern: Creación sencilla de objetos

Desventajas

  • Estado global: Dificulta las pruebas
  • Thread-Safety: Riesgos con acceso concurrente
  • Flexibilidad: Sin polimorfismo
  • Inicialización: Dependencias complejas

Preguntas frecuentes de examen

  1. ¿Cuál es la diferencia entre atributos estáticos e instancia? Los atributos estáticos pertenecen a la clase (una sola copia), los atributos de instancia pertenecen al objeto (una copia por objeto).

  2. ¿Pueden los métodos estáticos acceder a atributos de instancia? No, porque no tienen un puntero this y no saben a qué objeto pertenecen.

  3. ¡Explica agregación vs composición! Agregación: Las partes pueden existir sin el todo. Composición: Las partes solo existen con el todo.

  4. ¿Por qué los atributos estáticos no son genéricos? Pertenecen a la clase, no a la instancia, por lo que existe solo una versión por clase.

Fuentes principales

  1. https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
  2. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
  3. https://www.uml-diagrams.org/class-diagrams.html

Continúa en la ruta de aprendizaje OOP

Todos los artículos de OOP están completos. Regresa al primer artículo: Objektorientierte Programmierung OOP Grundlagen.

Volver al blog
Share:

Entradas relacionadas