Fundamentals 13 min read

Understanding Java Marker Interfaces: Serializable, Cloneable, and RandomAccess

The article explains Java marker interfaces—Serializable, Cloneable, and RandomAccess—showing how their empty definitions signal capabilities such as object serialization, shallow or deep cloning, and fast list indexing, and demonstrates the practical effects on code behavior and performance.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Understanding Java Marker Interfaces: Serializable, Cloneable, and RandomAccess

In Java, a marker interface is an interface without any methods or fields. It does not impose any semantic requirements on the implementing class; it merely indicates that the class belongs to a particular type.

The article introduces three classic marker interfaces: Serializable , Cloneable , and RandomAccess .

Serializable

Serializable enables an object to be converted into a byte stream and written to a file. The interface itself is empty:

public interface Serializable {
}

Example class without implementing Serializable:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private String name;
private Integer age;
}

Attempting to serialize this class throws java.io.NotSerializableException because the class does not implement the marker interface.

Fix by adding the interface:

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User implements Serializable {
private String name;
private Integer age;
}

Serialization code:

public static void writeObject() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\Desktop\\test.txt"));
User user = new User("张三", 20);
oos.writeObject(user);
oos.close();
}

Deserialization restores the object:

public static void readObject() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\Desktop\\test.txt"));
User user = (User) ois.readObject();
System.out.println(user);
ois.close();
}

Cloneable

Cloneable marks a class as supporting the clone() method. The interface is also empty:

public interface Cloneable {
}

Simple clone example (shallow copy):

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User implements Cloneable {
private String name;
private Integer age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

When the class contains a reference field, shallow cloning copies only the reference, leading to shared mutable state (shallow copy problem).

To achieve a deep copy, the referenced class must also implement Cloneable and be cloned manually:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address implements Cloneable {
private String province;
private String city;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class User implements Cloneable {
private String name;
private Integer age;
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
Address addressCopy = (Address) user.address.clone();
user.setAddress(addressCopy);
return user;
}
}

After deep cloning, modifications to the original object's fields do not affect the cloned object.

RandomAccess

RandomAccess is a marker interface indicating that a list supports fast random access (e.g., ArrayList ). The empty interface:

public interface RandomAccess {
}

Performance test for random access on an ArrayList (which implements RandomAccess):

public static void main(String[] args) throws Exception {
List
list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
long start = System.currentTimeMillis();
for (int i = 0; i < list.size(); i++) {
Integer num = list.get(i);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}

Result: ~8 ms.

Sequential access using an iterator on the same list:

public static void main(String[] args) throws Exception {
List
list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
Iterator
iterator = list.iterator();
long start = System.currentTimeMillis();
while (iterator.hasNext()) {
Integer num = iterator.next();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}

Result: ~17 ms.

For a LinkedList (which does not implement RandomAccess), random access is much slower:

public static void main(String[] args) throws Exception {
List
list = new LinkedList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
long start = System.currentTimeMillis();
for (int i = 0; i < list.size(); i++) {
Integer num = list.get(i);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}

Result: ~11076 ms.

Sequential access on a LinkedList is still faster than its random access but slower than ArrayList sequential access:

public static void main(String[] args) throws Exception {
List
list = new LinkedList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
Iterator
iterator = list.iterator();
long start = System.currentTimeMillis();
while (iterator.hasNext()) {
Integer num = iterator.next();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}

Result: ~36 ms.

These examples demonstrate how marker interfaces convey important semantic information to the JVM and developers, influencing object behavior (serialization, cloning) and algorithmic optimizations (random access).

JavaSerializationCloningdeep copyMarker InterfaceRandomAccess
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.