OOP Classes: Static vs Instance Methods, Attributes & Relationships
This article is a comprehensive overview of OOP class components – including static vs. instance elements, relationships and generics with practical examples.
In a Nutshell
Classes consist of attributes, methods, visibility levels and contracts. Static elements concern the class as a whole, instance elements individual objects. Generics increase type safety and reusability.
Compact Technical Description
A class defines structure and behavior through attributes, methods, visibility levels, constructors, invariants and contracts.
Static vs. Instance Elements:
Static Elements (Class-level)
- Static Attributes: Belong to the class, not to objects
- Static Methods: Can be called without an object
- Usage: Counters, caches, factories, constants
- Access: Via class name, not via object
Instance Elements (Object-level)
- Instance Attributes: Each object has its own copy
- Instance Methods: Operate on object state
- Usage: Object-specific data and behavior
- Access: Only via object instance
Class Relationships (UML):
- Association: Simple relationship between classes
- Aggregation: “has-a” relationship with independent parts
- Composition: “is-part-of” relationship with dependent parts
- Specialization: Subclass inherits from base class
Generics:
- Type Safety: Compile-time type checking
- Reusability: One code for different types
- Containers:
List<T>,Map<K,V>,std::vector<T>
Exam-Relevant Key Points
- Static Attributes: Belong to the class, shared value for all objects
- Instance Attributes: Each object has its own copy
- Static Methods: Callable without object, no this pointer
- Instance Methods: Operate on object state with this pointer
- Class Relationships: Association, aggregation, composition, inheritance
- UML Notation: Different symbols for relationship types
- Generics: Type templates for type-safe containers
- IHK-relevant: Fundamental understanding for OOP development
Core Components
- Static Attributes: Class variables for shared state
- Instance Attributes: Object variables for individual state
- Static Methods: Class methods without object reference
- Instance Methods: Object methods with state access
- Constructors: Object initialization
- Relationships: Structural connections between classes
- Generics: Type-parameterized classes and methods
- UML: Graphical notation for class design
Practical Examples
1. Static vs Instance Elements in Java
public class Mitarbeiter {
// Statische Attribute (gehören zur Klasse)
private static int anzahlMitarbeiter = 0;
private static final String FIRMA = "TechCorp";
private static double mindestgehalt = 2000.0;
// Instanzattribute (gehören zum Objekt)
private int mitarbeiterId;
private String name;
private double gehalt;
// Statischer Initialisierungsblock
static {
System.out.println("Mitarbeiter-Klasse wird geladen");
anzahlMitarbeiter = 0;
}
// Konstruktor (Instanz-Initialisierung)
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 + ")");
}
// Statische Methode (kann ohne Objekt aufgerufen werden)
public static int getAnzahlMitarbeiter() {
return anzahlMitarbeiter;
}
public static String getFirma() {
return FIRMA;
}
public static void setMindestgehalt(double mindestgehalt) {
if (mindestgehalt > 0) {
Mitarbeiter.mindestgehalt = mindestgehalt;
}
}
// Instanzmethode (benötigt Objekt)
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);
}
// Getter/Setter für Instanzattribute
public String getName() {
return name;
}
public double getGehalt() {
return gehalt;
}
}
// Verwendung der Klasse
public class MitarbeiterDemo {
public static void main(String[] args) {
// Statische Methoden aufrufen (ohne Objekt)
System.out.println("Anzahl Mitarbeiter: " + Mitarbeiter.getAnzahlMitarbeiter());
System.out.println("Firma: " + Mitarbeiter.getFirma());
Mitarbeiter.setMindestgehalt(2500.0);
// Objekte erstellen (Instanzen)
Mitarbeiter alice = new Mitarbeiter("Alice", 3000.0);
Mitarbeiter bob = new Mitarbeiter("Bob", 2800.0);
// Instanzmethoden aufrufen
alice.erhoeheGehalt(5.0);
bob.anzeigen();
// Statische Methode nach Objekterstellung
System.out.println("Anzahl Mitarbeiter: " + Mitarbeiter.getAnzahlMitarbeiter());
// Fehler: this in statischer Methode nicht verfügbar
// public static void fehlerhafteMethode() {
// System.out.println(this.name); // Fehler: kann nicht auf this zugreifen
// }
}
}
2. Class Relationships with UML Examples
// Association: Instructor teaches courses
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); // Back reference
}
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; // Back reference
public Kurs(String titel) {
this.titel = titel;
}
public void setDozent(Dozent dozent) {
this.dozent = dozent;
}
public String getTitel() {
return titel;
}
}
// Aggregation: Department has employees (can exist without department)
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);
// Employee continues to exist
}
}
// Composition: Order has order items (only exist with order)
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();
}
// Inner class for composition
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. Generics with Static Elements
// Generische Klasse mit statischen Elementen
public class Container<T> {
// Statische Attribute (sind nicht generisch!)
private static int anzahlContainer = 0;
private static final String VERSION = "1.0";
// Instanzattribute (sind generisch)
private T inhalt;
private int id;
public Container(T inhalt) {
this.inhalt = inhalt;
this.id = ++anzahlContainer;
}
// Statische Methode (kann nicht auf T zugreifen)
public static int getAnzahlContainer() {
return anzahlContainer;
}
public static String getVersion() {
return VERSION;
}
// Instanzmethode (kann auf T zugreifen)
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"));
}
// Generische Methode (statisch)
public static <U> Container<U> create(U inhalt) {
return new Container<>(inhalt);
}
}
// Verwendung
public class ContainerDemo {
public static void main(String[] args) {
// Statische Methoden aufrufen
System.out.println("Container-Version: " + Container.getVersion());
// Verschiedene Container-Typen
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());
// Fehler: Statische Attribute sind nicht generisch
// Container<String>.getAnzahlContainer(); // Syntax-Fehler
}
}
4. Factory Pattern with Static Methods
public class FahrzeugFactory {
// Statische Factory-Methoden
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);
}
// Statische Methode mit Validierung
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);
}
}
}
// Abstrakte Basisklasse
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();
}
// Konkrete Klassen
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");
}
}
// Verwendung der Factory
public class FactoryDemo {
public static void main(String[] args) {
// Statische Factory-Methoden verwenden
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();
// Dynamische Erstellung
Fahrzeug bmw = FahrzeugFactory.erstelleFahrzeug("pkw", "BMW", 150);
bmw.anzeigen();
}
}
UML Notation for Class Components
Class with Static and Instance Elements
+---------------------------+
| Employee |
+---------------------------+
| - numberOfEmployees: int | <<static>>
| - COMPANY: String | <<static>>
| - employeeId: int |
| - name: String |
| - salary: double |
+---------------------------+
| + getNumberOfEmployees(): int | <<static>>
| + setMinimumSalary(double): void | <<static>>
| + increaseSalary(double): void |
| + display(): void |
+---------------------------+
Relationships in UML
Instructor 1..* --* Course (Association)
Department 1 --o* Employee (Aggregation)
Order 1 --* OrderPosition (Composition)
Vehicle <|-- Car (Inheritance)
Static vs. Instance - Decision Aid
When to Use Static Elements?
Static Attributes:
- Counters for all instances
- Constants for the entire class
- Shared resources (database connection)
- Class-level caches
Static Methods:
- Factory methods for object creation
- Utility methods without state
- Conversion methods
- Validation methods
When to Use Instance Elements?
Instance Attributes:
- Object-specific data
- State that changes
- Configuration per object
Instance Methods:
- Methods that access object state
- Behavior that depends on instance data
- Methods that require the this pointer
Advantages and Disadvantages
Advantages of Static Elements
- Memory Efficiency: Only one copy for all objects
- Simple Access: Callable without object instance
- Shared State: Equal for all objects
- Factory Pattern: Easy object creation
Disadvantages
- Global State: Complicates testability
- Thread Safety: With concurrent access
- Flexibility: No polymorphism possible
- Initialization: Complex dependencies
Common Exam Questions
-
What is the difference between static and instance attributes? Static attributes belong to the class (one copy), instance attributes belong to the object (one copy per object).
-
Can static methods access instance attributes? No, because they have no this pointer and don’t know which object they belong to.
-
Explain aggregation vs composition! Aggregation: Parts can exist without the whole, composition: Parts exist only with the whole.
-
Why are static attributes not generic? They belong to the class, not the instance, so there is only one version per class.
Most Important Sources
- https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
- https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
- https://www.uml-diagrams.org/class-diagrams.html