This article is probably to be read together with the one on methods in Java OOP. There is a lot to say about classes, so I’m concentrating on topics which require extra attention when passing Java exams or certification and tricks they use to test your knowledge.
This series of articles are meant for people who already know the basics of Java, that is grammar and vocabulary, and knowledge of OOP. The topics covered correspond to OCA Programmer I level and relate to Java 8 version. If you need to learn the basics, I can recommend the JetBrains Academy Java developer track.
Encapsulation
Encapsulation is one of the core concepts of OOP. It prevents from changing instance variables directly. To achieve it you set your fields private (so it can only be handled from inside the class). You create a getter (access method) to retrieve the current value and a setter (mutator method) to change its value and thanks to which you can create safe guards to control the range of values for example.
Immutability
Immutability prevents from changing the instance variables at all. Values are set at instantiation* and cannot change anymore. You don’t create setters. You create defensive copies for reference types.
Immutable objects are thread-safe, they can be shared between several threads as their value remains constant no matter what.
*variables are final or effectively final, they are initialized in-line, in static blocks or in all constructors.
Inheritance
Another core concept of OOP and every programming beginner’s nightmare, am I right? Here are a few facts to remember about inheritance.
Child classes (aka subclasses) include all members of their parent (base class). Be careful that I wrote child classes, not talking about their instances, and that including does not mean having access to it!
A final class cannot be a parent class.
Fields can be hidden (shadowed) but are not overridden. The access to the fields depends on the reference type.
When methods are overridden, the function called depends on the object type.
All classes (not interfaces) inherit from the super class Object.
Polymorphism
Polymorphism is still a big pain for me, gotta be honest. Especially the way they handle it in exams and tests, because they will operate unnatural manipulations on data just to check if you can follow up (let’s say that when you get those exercises you’re likely to deal easily with real life, usually simpler, cases).
What helped me big time is finding that simplified formula:
- child IS-A parent
- parent = child OK (be careful to read the “=” sign as the assignment sign and not an equality sign, in which case it doesn’t make sense)
- child = (child) parent
It really helps me when solving tricky problems to stick to a concrete implementation of the rule. Here are two real-life, concrete examples:
- a cat IS-A animal
- animal = cat is OK => you can assign a cat to an holding reference of type animal, because a cat goes into the ensemble “animals”
- cat = (cat) animal => explicit casting is mandatory, because an animal might be a cat, but it could also be a dog, thus you require the compiler to check either animal can be casted to cat or not
- a square IS-A rectangle
- rectangle = square
- square = (square) rectangle
The latter example expresses perfectly the rule “a square is a rectangle but a rectangle is not a square”, which means not all rectangles are squares but they can be.
Once an object has been assigned a reference type, only the members of the reference type can be called without explicit cast.
ReferenceType reference = new ObjectType();
I would strongly advise to do tons of exercises on that matter, for examples with extensive test sets like the ones provides by Enthuware (a set covers a whole certification scope and is like 10 bucks) because exams go really deep into nonsensical polymorphism manipulations of all kinds, inventing cases you’re unlikely to encounter is real-life situations.
Class access identifiers
A class can be public or “default” (no explicit identifier), in which case it’s only visible inside the package. This is only true for non-nested classes, which is a OCP topic and will be discussed later.
0 or 1 class per file (including interfaces, enums…) can be public. The public class then has to be named after the file containing it.
Note that it is impossible to access a class in the “unnamed”, also called “default” package, from a class in another package. Therefore, a good practice is to never create a class in that unnamed package.
Overriding instance methods
Subclasses can override methods if they are accessible to them (no private method e.g.). A final method cannot be overridden.
An overridden method has:
- the same signature: name and parameters (same number, same order, same types or covariant)
- the same return type or covariant for reference types
- the same or high accessibility
- the same or lower exception thrown or none (checked exception)
If the signature is different, it is virtually overloading, thus polymorphism cannot happen.
If the method is wrongly implemented, meaning the same name is used but it’s neither overridden or overloaded properly, you’ll get compile time error.
Hiding static methods
Static methods are not overridden but hidden: if a child class defines a method with the same signature, the parent’s is hidden. Though, it can still be accessed just using the parent class name when calling the method. The same rules as in overriding apply and both must be static, otherwise you’ll get compile time error.
Abstract classes
Abstract classes can’t be instantiated. However, they can have a constructor.
They can declare variables, static or not, normal methods and abstract methods.
Abstract methods cannot have an implementation, cannot be final or private.
A concrete class (instantiable class, not abstract) can extend a concrete class or an abstract class. An abstract class can extend an abstract class or a concrete class.
A normal class (concrete) cannot declare abstract methods.
Interfaces
All members of an interface are public by default. Protected in not allowed in an interface. Since introduction of static methods, the modifier “final” is possible, only for the latter. (Static and default interface methods are explained in the post about methods).
You might write the “public” access modifier although it’s not mandatory, as you might write “abstract” in front of non-static, non-default methods and write “abstract” before the interface name.
Fields are implicitly public, static and final, which means you’re not obliged to write those either.
An interface can be implemented by a class, concrete or abstract, and extended by an interface. Java doesn’t support multiple inheritance but you can implement or extend multiple interfaces in one class or interface. Interfaces cannot extend classes. Be careful that multiple implementation may lead to conflicts. If several abstract methods have the same signature but a different return type (non-covariant), it will not compile (overriding). Overloading is ok (still if return type coincides) and doesn’t prevent from overriding all abstract methods in the (last) concrete class.
Interfaces don’t inherit from Object.
Inner-classes will add some subtleties to this, but that will be discussed in OCP series.
Initializers
Initializers are anonymous blocks that are called when referencing or instantiating a class.
It can be static or instance:
static {
// this is a static init block
}
{
// this is an instance init block
}
The static initializers are run once when the class initializes. They can only use static members (as a static method). The instance method are run every time an instance of the class is created.
Initialization order is:
- Superclass constructor is called
- Static fields and initializers are run
- Instance fields and initializers are run
- Current class constructor is called
Final fields
Final fields must be initialized by the time the class constructor completes. They can be initialized inline, in an instance initializer or by the constructor. Uninitialized constant will can compile-time error, as well as trying to change the value of an initialized constant.
Enum
Enumerations are an OCP topic, but it’s such a short and easy topic that I didn’t see a need to write a whole post about them. Enum is a special type of class. They are public (implicit), except if they’re nested but again that very topic will need a whole post on its own. The word “public” can only be used if the enum has its own file!
<public> enum MyEnum {
WHAT,
E,
VER;
}
This is the basic construct of an enum, where each field is public static final (implicit). The modifiers cannot be written in this case.
An explicit constructor can be added and it is private (implicit), so the private modifier can be omitted. Enum cannot be instantiated, even inside the class (but calls to constructors with this() work). There can be several constructors and they can be overloaded. Other fields and methods can be added, either public or private.
Here’s a more complex definition of an enum:
public enum MyEnum {
WHAT(4),
E,
VER(3);
private int len;
MyEnum() {
calcLen();
}
MyEnum(int len) {
this.len = len;
}
public int getLength() {
return this.len;
}
private void calcLen() {
this.len = this.toString().length();
}
}
Have a look at the other posts on Java certification exams tips and subscribe to receive future posts.
0 thoughts on “Class design – OOP | Preparing for Java Oracle Associate Certification (OCA) / Java basic technical tests”