How do you keep a class's internal state valid when other code can read and write it directly?
You cannot fully, which is the point worth making first. If a class exposes a setter for every field, any caller can put the object into a state the class never intended, so the class has to validate on every write. That means validation logic, error handling and defensive copies scattered across a lifecycle that should be simple.
The stronger design is to remove the write path entirely. Make the field private, expose no mutator, and change state only through operations that establish the invariant as a side effect. A balance cannot be set to a negative number because there is no setBalance method; there is a withdraw method that either succeeds or refuses. The invalid state has no path into the object.
Where you genuinely need to accept values, validate in one place rather than at each use. A constructor that rejects a negative amount, a check in the mutator that throws with a clear message, and a private copy of any mutable input so the caller cannot mutate the object afterwards. That last part, defensive copying, is the detail interviewers often probe: returning your internal list directly lets a caller change it without going through any of your logic.
Finally, prefer immutable objects where you can. If nothing can change after construction, there is no window in which the object is briefly invalid, no thread-safety question to answer, and the compiler can share references freely.