The 30 second answer
A record's fields are always final and set through its constructor, and equals and hashCode always use all the components. A record can't extend another class, because it already extends Record, and it can't add extra instance fields. It can implement interfaces and have methods. Also, it is only shallowly immutable: a list inside can still change unless I copy it in the constructor.
| Record | Normal class | |
|---|---|---|
| Fields | private final, one per component | anything |
| Extra instance fields | not allowed | allowed |
| Superclass | implicitly java.lang.Record, can't extend | any non-final class |
| Subclassing | implicitly final | allowed |
| equals / hashCode / toString | generated from all components | inherited from Object unless written |
| Accessors | name() | whatever you write |
Shallow immutability is the common follow-up:
record Cart(List<Item> items) {
Cart { items = List.copyOf(items); } // defensive, unmodifiable copy
}[Opinion] Use a normal class when you need mutable state, inheritance, hidden internal state or lazy fields. JPA entities should stay normal classes, because JPA needs a no-arg constructor and mutable, proxyable entities; records work well for DTOs and query projections.
Sources: JEP 395: Records, JLS 8.10 Record Classes