Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
25: Classes and Objects
I want to start today with a snippet of code that I've seen almost every single junior developer write in their first week of Java. It looks perfectly logical at first glance, but it will make the compiler scream at you.
public class GameCharacter {
String name = "Hero";
int health = 100;
public static void main(String[] args) {
System.out.println("Character " + name + " has " + health + " HP.");
}
}
The "Non-Static" Wall
If you try to run this, you'll get a compiler error: non-static variable name cannot be referenced from a static context. It's one of the most frustrating errors when you're starting out because you're thinking, "The variable is right there! Why can't the program see it?"
Here is the deal: the main method is static. In Java, static means "this belongs to the class itself," not to any specific instance of the class. However, name and health are instance variables. They belong to a specific character.
Imagine if you had a blueprint for a house. The blueprint says "every house has a front door color." But you can't walk up to the blueprint and try to open the door. You can't interact with the door until you actually build a house from that blueprint. Right now, you're trying to open the door on the blueprint.
Creating an Instance to Fix the Crash
To fix this, we need to instantiate the class. We use the new keyword to tell Java, "Take this blueprint and actually allocate some memory for a real object."
public class GameCharacter {
String name = "Hero";
int health = 100;
public static void main(String[] args) {
// We create an 'instance' of GameCharacter called player1
GameCharacter player1 = new GameCharacter();
// Now we ask player1 specifically for their name and health
System.out.println("Character " + player1.name + " has " + player1.health + " HP.");
}
}
Now it works. We aren't talking to the GameCharacter class anymore; we're talking to player1, a concrete object that exists in your computer's memory.
Blueprints versus Objects
This is the core of Object-Oriented Programming (OOP). The class is your blueprint. It defines what data (fields) a thing has and what it can do (methods). The object is the actual thing you create using that blueprint.
The real power here is that you can create as many objects as you want from a single class, and they each maintain their own separate state. I don't want every character in my game to have the same name and health—that would be a very boring game.
GameCharacter player1 = new GameCharacter();
player1.name = "Arthur";
player1.health = 100;
GameCharacter player2 = new GameCharacter();
player2.name = "Morgana";
player2.health = 150;
System.out.println(player1.name); // Prints Arthur
System.out.println(player2.name); // Prints Morgana
Packaging Logic into Methods
Adding variables is great, but classes are meant to encapsulate behavior too. Instead of manually changing health values in the main method, we should give the GameCharacter the ability to take damage itself. This keeps the logic tied to the data.
public class GameCharacter {
String name;
int health = 100;
public void takeDamage(int amount) {
this.health -= amount;
System.out.println(name + " took " + amount + " damage! Health is now " + health);
}
public static void main(String[] args) {
GameCharacter hero = new GameCharacter();
hero.name = "Arthur";
hero.takeDamage(20); // Much cleaner than doing hero.health = hero.health - 20;
}
}
Notice the this keyword. I use this.health to be explicit: "I am talking about the health variable that belongs to this specific instance of the character." It's not strictly required here, but as you start writing more complex code, it'll save you from a lot of confusion.
📋 Practical Task
Building a Persistent BankAccount Tracker
Your task is to create a system that manages multiple bank accounts. You need to ensure that each account tracks its own balance independently.
- Create a class named
BankAccount. - Give it two fields: a
String accountHolderand adouble balance. - Implement a method called
deposit(double amount)that adds money to the balance and prints the new total. - Implement a method called
withdraw(double amount). This method should check if the account has enough money. If it does, subtract the amount; if not, print "Insufficient funds!". - In your
mainmethod, instantiate two different accounts (e.g., one for "Alice" and one for "Bob"). - Perform a few deposits and withdrawals on both accounts to prove that Alice's spending doesn't affect Bob's balance.
There are no comments for now.