Free for students · Ad-free · WCAG 2.1 AA Compliant · Accessibility

Anatomy of a Class

Lesson ~11 min read 8 MCQs

In simple terms: In simple terms, class anatomy is about using `public` and `private` labels to control which parts of your code can be seen and used by other parts of your program, just like a TV remote has public buttons but private internal wiring.

Why this matters

Imagine you're driving a car. You have access to the steering wheel, the gas and brake pedals, and the turn signal. These are the public controls, designed for you to use. You can interact with them safely to make the car go, stop, and turn.

Now, imagine if the hood were open and all the engine's complex wiring and moving parts were exposed right next to you. What if you accidentally bumped a critical wire while trying to change the radio station? The whole car could break down.

Car designers are smart. They hide the complex, delicate engine parts under the hood, a practice we call encapsulation. They give you a simple, safe interface to use the car. In programming, we do the exact same thing. This lesson is about how we build that "hood" for our code to make it safer, more reliable, and easier to use.

A car's design mirrors class encapsulation: public controls vs. private engine parts.

Concept overview

classDiagram
    class Car {
        -double currentSpeed
        -boolean engineOn
        +String model
        +startEngine()
        +stopEngine()
        +accelerate(double amount)
        +getSpeed() double
    }
This is a class diagram for a `Car`. The class has two private attributes, `currentSpeed` and `engineOn`, and one public attribute, `model`. It has four public methods: `startEngine()`, `stopEngine()`, `accelerate()`, and `getSpeed()`.

Core explanation

When we build classes, we're creating our own custom data types. Think of a Student, a BankAccount, or a GameCharacter. To make these classes robust and easy to work with, we need to be deliberate about their structure. This is where we get into the anatomy of a class, specifically using the keywords public and private.

Data Encapsulation: The "Black Box" Idea

Data encapsulation is a core principle of object-oriented programming. It means bundling the data (instance variables) and the methods that operate on that data together within a single class, and hiding the implementation details from the outside world.

Think of a microwave. You put your food in, press a few buttons (public methods like setTimer and start), and get hot food out. You don't know—and you don't need to know—exactly how the magnetron generates microwaves to heat your food. The complex details are encapsulated, or hidden. All you need is the simple, public interface.

In Java, we achieve encapsulation with access modifiers: public and private.

  • public: These members are accessible from anywhere. Any other class can see and use them. This is your public interface, like the buttons on the microwave.
  • private: These members are only accessible from inside the class in which they are declared. No other class can see or touch them directly. This is the hidden wiring.

The Anatomy of a public Class

For the AP Computer Science A exam, the classes you write will always be public.

public class Student {
    // ... class members go here
}

This public keyword on the class declaration means that code in other files can create Student objects. Now let's look inside.

Instance Variables: Keep Them private

Instance variables are the data that defines an object's state. For a Student class, this might be their name, ID number, and GPA. Each Student object you create gets its own copy of these variables. Liam's gpa is separate from Priya's gpa.

To properly encapsulate our data, we should almost always declare our instance variables as private.

public class Student {
    private String name;
    private int studentID;
    private double gpa;
    // ... constructors and methods follow
}

Why private? This is the most important takeaway. By making these variables private, we prevent other parts of our code from putting our object into an invalid state.

Imagine if gpa were public. Another piece of code could do this:

The critical difference between public and private instance variables.

someStudent.gpa = 5.0; // A 5.0 GPA isn't possible! anotherStudent.gpa = -100.0; // This is nonsense!

By making gpa private, we force other code to go through a method we provide, where we can add validation. This protects the integrity of our object's data.

Constructors: Make Them public

A constructor's job is to build a new object. To build a Student object from your main method (which is in another class), you need to be able to access the constructor. Therefore, in this course, constructors are always public.

public class Student {
    private String name;
    private int studentID;
    private double gpa;

    // The public constructor
    public Student(String initialName, int id, double initialGpa) {
        name = initialName;
        studentID = id;
        gpa = initialGpa;
    }

    // ... other methods
}

If the constructor were private, you couldn't create a Student from outside the Student class, which would make it pretty useless!

Methods: A Mix of public and private

Methods define an object's behaviors. These can be either public or private.

public Methods: These methods form the object's interface—the services it offers to the outside world. These are things like "getters" that return the value of a private variable, "setters" that modify a private variable (with validation!), or other actions.

public class Student {
    // ... instance variables and constructor

    // A "getter" method
    public double getGPA() {
        return gpa;
    }

    // A "setter" method with validation
    public void setGPA(double newGPA) {
        if (newGPA >= 0.0 && newGPA <= 4.0) {
            gpa = newGPA;
        }
    }

    public void printIDCard() {
        System.out.println("Name: " + name);
        System.out.println("ID: " + studentID);
    }
}

Anyone with a Student object can call getGPA() or printIDCard().

private Methods: Sometimes, a public method is very complex. You can break that complexity down by writing private "helper" methods. These helper methods are not intended to be called by anyone outside the class; they are just an internal implementation detail.

Let's say we want a public method to determine if a student is on the honor roll, which has a complex calculation.

public class Student {
    // ... instance variables, constructor, other methods

    public boolean isOnHonorRoll() {
        // A complex task is made simpler by calling a helper
        boolean hasGoodGrades = checkGrades();
        boolean hasGoodAttendance = checkAttendance();
        return hasGoodGrades && hasGoodAttendance;
    }

    // A private helper method. No one outside this class
    // needs to know how we check the grades.
    private boolean checkGrades() {
        // some complex logic to check grades...
        return gpa > 3.5;
    }

    // Another private helper method
    private boolean checkAttendance() {
        // some complex logic to check attendance records...
        return true; // simplified for example
    }
}

The user of the Student class just calls isOnHonorRoll(). They don't need to see or use the checkGrades() or checkAttendance() methods. Using private helpers keeps our code organized and our public interface clean.

A `Student` class blueprint with private data and public methods.

See it in action

python
Line 1
Output
Click Run to see the output.

        
Try these
    © Shrutam.ai

    Worked examples

    Let's put these concepts into practice. Seeing how public and private work in a complete class is the best way to learn.

    Example 1: Creating a Book Class

    Problem: You're building a library app. Create a Book class that stores a title (String), author (String), and pageCount (int). The data must be encapsulated. The class needs a constructor to initialize a new book and a getSummary() method that returns a string like "To Kill a Mockingbird by Harper Lee".

    Solution Walkthrough:

    1. Define the class and instance variables. We'll start with a public class. The core data—title, author, pageCount—should be hidden from the outside world to prevent invalid data. So, we make them private.

      public class Book {
          private String title;
          private String author;
          private int pageCount;
    2. Create the public constructor. We need a way to create Book objects. The constructor will take the initial values and assign them to our private instance variables.

      // Inside the Book class...
      public Book(String bookTitle, String bookAuthor, int pages) {
          title = bookTitle;
          author = bookAuthor;
          pageCount = pages;
      }

      Why is this public? So that code in other files (like a Library class) can create new Book objects.

    3. Implement the public method. The getSummary() method is a behavior we want to offer to the public. It reads the internal private data and formats it.

      // Inside the Book class...
      public String getSummary() {
          return title + " by " + author;
      }

    Putting it all together:

    public class Book {
        private String title;
        private String author;
        private int pageCount;
    
        public Book(String bookTitle, String bookAuthor, int pages) {
            title = bookTitle;
            author = bookAuthor;
            pageCount = pages;
        }
    
        public String getSummary() {
            return title + " by " + author;
        }
    }

    Where students go wrong: A common mistake is making title or pageCount public. If pageCount were public, another programmer could write myBook.pageCount = -200;, which makes no sense. By making it private, we retain control.

    Preventing invalid object states by keeping instance variables private.

    Example 2: Using a private Helper Method

    Problem: Let's expand our Book class. Add a public method getReadingTimeInMinutes() that estimates how long it will take to read the book. Assume a reading speed of 250 words per page and 1 minute per page. The calculation for words is just an estimate, so we'll hide it in a helper method.

    Solution Walkthrough:

    1. Define the public method. This is the method the outside world will call.

      // Inside the Book class...
      public int getReadingTimeInMinutes() {
          // For simplicity, let's say it's 1 minute per page.
          // A more complex calculation could go here.
          return pageCount;
      }
    2. Let's make it more complex to justify a helper. Let's say we want to add a 5-minute "settling in" period to any book over 100 pages. This logic clutters our public method. Let's move it to a helper.

    3. Create a private helper method. This method will contain the detailed calculation logic. It's private because no one outside the Book class needs to know how we calculate this bonus time.

      // Inside the Book class...
      private int calculateBonusTime() {
          if (pageCount > 100) {
              return 5; // Add 5 bonus minutes for long books
          }
          return 0;
      }
    4. Update the public method to use the helper.

      // Inside the Book class...
      public int getReadingTimeInMinutes() {
          int baseTime = pageCount; // 1 minute per page
          int bonusTime = calculateBonusTime(); // Call the private helper
          return baseTime + bonusTime;
      }

      Why do this? The public method getReadingTimeInMinutes() is now clean and easy to read. The messy details are tucked away in calculateBonusTime(). If we later decide to change the bonus time calculation, we only have to change the private method. The public interface remains the same, and no other part of the program that uses the Book class will break.

    The `Book` class with private data and public methods for interaction.

    Try it yourself

    Ready to build your own class blueprint? Give these a try.

    1. Design a Movie class.

      • It should have private instance variables for title (String), director (String), and rating (a double from 1.0 to 10.0).
      • Write a public constructor that initializes these three variables.
      • Hint: Remember the principle of encapsulation. Why is it important for rating to be private?
    2. Add a helper method to your Movie class.

      • First, add a private instance variable for releaseYear (an int). Don't forget to update your constructor!
      • Create a public method called getMovieDetails() that returns a String.
      • Inside getMovieDetails(), call a new private helper method named isClassic(). This helper should return true if the releaseYear is before 2000, and false otherwise.
      • If isClassic() returns true, the getMovieDetails() string should include the text "(Classic)".
      • Hint: Your public method's job is to assemble the string. Your private method's job is to make a single decision.
    The `Movie` class with private data and public/private methods.