A class is a named, reusable definition that specifies the data (state) and the operations (behavior) shared by a family of objects. In object-oriented programming, a class acts as a blueprint and a type at the same time: code creates concrete objects, called instances, from it, and the compiler or runtime uses it to decide which operations those instances support.
Classes are the main unit of design in languages such as Java, C#, C++, Python, Kotlin and Swift. They bundle fields and methods into one cohesive module, control what outside code may touch through encapsulation, and provide the anchor points for inheritance and polymorphism, the neighboring concepts of this topic in programming.
How a class turns into objects
A class becomes useful only when code instantiates it, usually with a new expression or by calling the class like a function, as in Python. The runtime allocates memory for the instance fields, runs a constructor that establishes a valid initial state, and returns a reference to the new object. The class itself is stored once; each object holds only its own field values.
Methods are not copied into every object. An instance method is compiled once and receives the target object as a hidden parameter, named this in Java, C# and C++ and self in Python. When a method is virtual, the call is resolved at run time through a method table (often called a vtable), which is the mechanism behind dynamic dispatch.
The following table lists the members a typical class declaration contains and the role each one plays.
| Member | What it holds or does | Example in Java |
|---|---|---|
| Instance field | State stored separately in every object | private double balance; |
| Static field | One value shared by the whole class | static int count; |
| Constructor | Initializes a new object and enforces invariants | Account(String owner) |
| Instance method | Behavior that reads or changes the object state | void deposit(double amount) |
| Static method | Behavior tied to the class, not to an instance | static Account open() |
| Nested type | A helper class scoped inside the outer class | static class Builder |
A well-designed class protects its invariants, the conditions that must always hold for its objects, such as a balance that never drops below zero. The constructor establishes the invariant, and every public method must preserve it.
Class, object, interface and related concepts
A class differs from an object in the same way a definition differs from a value: there is one Invoice class, but an application may hold thousands of Invoice objects at once. Confusing the two leads to errors such as storing per-object data in a static field, which silently shares it across all instances.
The table below separates the class from the constructs it is most often confused with.
| Construct | Holds state | Provides implementation | Can be instantiated |
|---|---|---|---|
| Class | Yes | Yes | Yes |
| Abstract class | Yes | Partly | No, only its subclasses |
| Interface | No instance state | Usually no, except default methods | No |
| Object | Yes, its own values | Uses the methods of its class | It is already an instance |
| Struct (C#, Swift) | Yes | Yes | Yes, with value semantics |
Not every object-oriented language needs classes. JavaScript is prototype-based: objects inherit directly from other objects, and the class keyword introduced in ECMAScript 2015 is largely syntax over prototypes. Python treats classes themselves as objects whose type is a metaclass, which allows classes to be created and modified at run time.
Kinds of classes and member visibility
Languages offer several class variants, each fitting a different design need. Choosing the right kind communicates design intent to other developers and lets the compiler enforce it.
| Variant | Defining trait | Typical use |
|---|---|---|
| Concrete class | Fully implemented and instantiable | Domain entities, services |
| Abstract class | Declares abstract methods for subclasses | Shared base with partial logic |
| Final or sealed class | Cannot be subclassed, or only by listed types | Security-sensitive or closed hierarchies |
| Record or data class | Compiler generates equality, hashing and accessors | Immutable value carriers, DTOs |
| Generic class | Parameterized by one or more types | Typed collections and containers |
| Inner or anonymous class | Defined inside another class or expression | Callbacks, iterators, small adapters |
Access modifiers decide which code may use each member and are the primary tool of encapsulation. The exact keywords vary: Java and C# use public, protected and private, Java adds package-private as its default, and Python relies on a naming convention with a leading underscore rather than enforced rules. A sound default is to make fields private and expose only the methods that callers genuinely need.
Designing a cohesive class
A good class has one clear responsibility and a name that states it, usually a noun such as PaymentGateway or ShoppingCart. This idea is captured by the single responsibility principle, the first of the SOLID principles: a class should have only one reason to change.
- Prefer composition over inheritance: hold a reference to a helper object instead of extending a base class only to reuse its code.
- Keep the public surface small, because every public method becomes a commitment that later changes can break.
- Make value-like classes immutable, so instances can be shared across threads without locks.
- Validate in the constructor, so an invalid object can never exist.
- Depend on interfaces, not on concrete classes, to keep units testable and replaceable.
Size is a useful warning signal. Opinions differ, but a class that grows beyond a few hundred lines or dozens of methods usually mixes responsibilities and benefits from being split.
Common class design mistakes
Most problems with classes come from poor boundaries rather than from syntax. The table maps frequent symptoms to their causes and the usual remedy.
| Symptom | Likely cause | Remedy |
|---|---|---|
| God class with thousands of lines | Too many responsibilities in one place | Extract cohesive classes |
| Anemic model with only getters and setters | Behavior lives in separate service classes | Move logic next to its data |
| Fragile subclasses after a base change | Deep inheritance used for code reuse | Replace with composition |
| Objects in an invalid state | Public setters bypass validation | Validate in constructors and methods |
| Data leaking between instances | Per-object state kept in static fields | Use instance fields |
Another frequent error is overriding equality without hashing. In Java and C#, a class that overrides equals but not hashCode (or Equals without GetHashCode) breaks hash-based collections, because equal objects may land in different buckets.
In everyday language
A class is like the architectural plan for a house. The plan states how many rooms there are, where the doors go and what each switch does, but nobody can live in a plan. Builders use it to construct real houses, and every house built from the same plan has the same layout while holding different furniture and different residents.
In software, the class is the plan and the objects are the houses. One class can produce many objects, each with its own data but all following the same rules.
Seen in the real world
A development team building an online bookstore models its checkout with a ShoppingCart class. The class holds a private list of line items and exposes methods such as addItem, removeItem and total. The constructor creates an empty cart, and addItem rejects a quantity of zero or less, so no cart can ever contain an invalid line.
On a busy afternoon the server holds about 4,000 carts in memory at once. All of them are instances of the same class: the method code exists once, while each object stores only its own items. When the team later adds a 10% discount rule for orders above 50 dollars, they change a single method in one class, and every cart in the system follows the new rule after deployment.