The main focus of the creative pattern is “How do you create objects?” , its main feature is “the separation of object creation and use”. This reduces the coupling of the system and eliminates the need for users to focus on object creation details. The creation mode is divided into:

  • The singleton pattern
  • Factory method pattern
  • Abstract engineering model
  • The prototype pattern
  • Builder model

4.1 Singleton design pattern

The Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern is the creation pattern, which provides the best way to create objects.

This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique objects directly, without instantiating the objects of the class.

4.1.1 Singleton pattern structure

The singleton pattern has the following roles:

  • Singleton class: a class that can create only one instance
  • Access classes: Use singleton classes

4.1.2 Implementation of singleton pattern

Singleton patterns fall into two categories:

  • Hungry: Class loading causes the instance object to be created
  • Lazy: Class loading does not cause the instance object to be created, but only when the object is first used

Hungry-mode 1 (static variable mode)

public class Singleton {
    // Private constructor
    private Singleton(a) {}
    
    // Create an object for the class at the member location
    private static Singleton instance = new Singleton();
    
    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

This method declares static variables of the Singleton type at member locations and creates an object of the Singleton class, instance, which is created as the class is loaded. If the object is large enough to remain unused, memory is wasted.

Hungry-mode 2 (Static code block mode)

public class Singleton { 
    // Private constructor
    private Singleton(a) {} 
    
    // Create an object for the class at the member location
    private static Singleton instance;
    
    static {
        instance = new Singleton();
    }
    
    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

Static variables of the Singleton type are declared at member locations, and object creation is done in static code blocks, which are also created for class loading. So it’s basically the same as lazy approach 1, but there’s also a memory waste problem.

Lazy style – Approach 1 (Thread unsafe)

public class Singleton {
    // Private constructor
    private Singleton(a) {}

    // Create an object for the class at the member location
    private static Singleton instance;

    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Static variables of type Singleton are declared at member positions without assigning to the object. When was the assignment done? The Singleton class object is created when the getInstance() method is called to get the Singleton class object, thus implementing the lazy loading effect. However, if you are in a multi-threaded environment, there are thread safety issues.

Lazy style – Approach 2 (Thread-safe)

public class Singleton {
    // Private constructor
    private Singleton(a) {}

    // Create an object for the class at the member location
    private static Singleton instance;

    // Provide static methods to get the object
    public static synchronized Singleton getInstance(a) {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

This method realizes lazy loading effect and solves the thread safety problem. But the addition of the synchronized keyword to the getInstance() method results in particularly poor performance. As you can see from the code above, thread-safety issues only occur when instance is initialized and disappear once the initialization is complete.

Lazy – Mode 3 (Double-checked lock)

public class Singleton {
    // Private constructor
    private Singleton(a) {}

    // Create an object for the class at the member location
    private static Singleton instance;

    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        if (instance == null) {
            synchronized (Singleton.class) {
                // Check again if the lock is null
                if (instance == null) {
                    instance = newSingleton(); }}}returninstance; }}Copy the code

Double-checked locking mode is a kind of very good singleton implementation pattern, solve the singleton, performance, safety, thread the double-checked locking mode above looks perfect, in fact is there is a problem, in the case of multi-threaded, there might be null pointer, the cause of the problem is the JVM at the time of instantiation objects will reorder operation optimization and instructions.

To solve the null-pointer exception problem caused by the double-checking pattern, use only the volatile keyword, which guarantees visibility and order.

public class Singleton {
    // Private constructor
    private Singleton(a) {}

    // Create an object for the class at the member location
    private static volatile Singleton instance;

    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        if (instance == null) {
            synchronized (Singleton.class) {
                // Check again if the lock is null
                if (instance == null) {
                    instance = newSingleton(); }}}returninstance; }}Copy the code

The double-checked locking pattern with the volatile keyword is a good singleton implementation that ensures thread-safety without performance issues in multithreaded situations.

Lazy – Mode 4 (Static inner class mode)

In the static inner class singleton, instances are created by the inner class. Since the JVM does not load a static inner class during the loading of an external class, the inner class is loaded only when its properties/methods are called and its static properties are initialized. Static attributes, because they are modified static, are guaranteed to be instantiated only once, and in a strict order.

public class Singleton {
    // Private constructor
    private Singleton(a) {}

    private static class SingletonHolder {
        public static final Singleton INSTANCE = new Singleton();
    }

    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        returnSingletonHolder.INSTANCE; }}Copy the code

INSTANCE is not initialized when the Singleton class is loaded for the first time. Only getInstance() is called for the first time. The virtual machine loads SingletonHolder and initializes INSTANCE. The Singleton class is also guaranteed to be unique.

The static inner class singleton pattern is an excellent singleton pattern, which is commonly used in open source projects. In the case of no lock, to ensure the safety of multi-threading, and no performance impact and space waste.

Enumeration methods

public enum Singleton {
    INSTANCE;
}
Copy the code

Enumeration class implements the singleton pattern is highly recommend the singleton implementation pattern, because an enumerated type is thread-safe, and will only be loaded once, designers fully utilizes enumeration of this feature to implement the singleton pattern, the enumeration method is very simple, and enumerated types is used only one will not be damaged in the singleton implementation of singleton pattern.

4.1.3 Existing problems

4.1.3.1 Problem demonstration

Failure singleton mode:

Enable the Singleton class defined above to create multiple objects, excluding enumerations. There are two ways, serialization and reflection.

Serialization deserialization:

The Test class:

public class Test {
    public static void main(String[] args) throws Exception {
        // Write objects to a file
        //writeObject2File(); 
        // Read objects from files
        Singleton s1 = readObjectFromFile();
        Singleton s2 = readObjectFromFile();
        
        // Determine if two deserialized objects are the same object
        System.out.println(s1 == s2);
    }
    
    private static Singleton readObjectFromFile(a) throws Exception { 
        // Create an object input stream object
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt")); 
        // Read the Singleton object first
        Singleton instance = (Singleton) ois.readObject();
        return instance;
    }
    
    public static void writeObject2File(a) throws Exception { 
        // Get the Singleton class object
        Singleton instance = Singleton.getInstance(); 
        // Create an object output stream
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));
        // Write the instance object into the fileoos.writeObject(instance); }}Copy the code

The above code results in false, indicating that serialization and deserialization have broken the singleton design pattern.

Reflection:

The Test class:

public class Test {
    public static void main(String[] args) throws Exception {
        // Get the bytecode object of the Singleton class
        Class clazz = Singleton.class; 
        // Get the private, no-argument constructor object of the Singleton class
        Constructor constructor = clazz.getDeclaredConstructor(); 
        // Cancel the access check
        constructor.setAccessible(true);
        
        // Create object s1 for the Singleton class
        Singleton s1 = (Singleton) constructor.newInstance(); 
        // Create object s2 of the Singleton class
        Singleton s2 = (Singleton) constructor.newInstance();
        
        // Determine if the two Singleton objects created by reflection are the same objectSystem.out.println(s1 == s2); }}Copy the code

The above code results in false, indicating that serialization and deserialization have broken the singleton design pattern

4.1.3.2 Problem solving

Serialization and deserialization approaches break the singleton pattern

Add the readResolve() method to the Singleton class, which is called by reflection during deserialization and returns the value of the method if defined, or the newly generated object if not.

The Singleton class:

public class Singleton implements Serializable { 
    // Private constructor
    private Singleton(a) {}
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        return SingletonHolder.INSTANCE;
    }
    
    /** * the following is to solve the serialization deserialization crack singleton pattern ** /
    private Object readResolve(a) {
        returnSingletonHolder.INSTANCE; }}Copy the code

Reflection mode crack singleton solution

public class Singleton {
    // Private constructor
    private Singleton(a) {
        /* * Reflection to break the singleton pattern need to add code */
        if(instance ! =null) {
            throw newRuntimeException(); }}private static volatile Singleton instance; 
    
    // Provide static methods to get the object
    public static Singleton getInstance(a) {
        if(instance ! =null) {
            return instance;
        }
        synchronized (Singleton.class) {
            if(instance ! =null) {
                return instance;
            }
            instance = new Singleton();
            returninstance; }}}Copy the code

4.1.4 JDK source code parsing -Runtime class

The Runtime class uses the singleton design pattern, using methods from the Runtime class.

public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
        // Get the Runtime object
        Runtime runtime = Runtime.getRuntime();
        
        // Returns the total amount of memory in the Java virtual machine.
        System.out.println(runtime.totalMemory()); 
        
        // Returns the maximum amount of memory that the Java virtual machine is trying to use.
        System.out.println(runtime.maxMemory());
        
        // Create a new process to execute the specified string command and return the process object
        Process process = runtime.exec("ipconfig"); 
        
        // Obtain the result of executing the command from the input stream
        InputStream inputStream = process.getInputStream(); byte[] arr = new byte[1024 * 1024* 100];
        int b = inputStream.read(arr);
        System.out.println(new String(arr,0,b,"gbk")); }}Copy the code

4.2 Factory Mode

2 an overview

Requirements: Design a coffee shop ordering system

Design a Coffee class and define its two subclasses (AmericanCoffee and latte); Design a coffee shop (CoffeeStore), coffee shop has the function of ordering coffee.

The concrete class is designed as follows:

In Java, everything is connected with objects, and these objects need to be created. If we create the object directly, it will seriously coupling the object. If we want to change the object, all the new objects need to be modified again, which obviously violates the open and close principle of software design. If we use the factory model to produce objects, we can only deal with the factory, and completely decouple the object. If we want to change the object, we can directly replace the object in the factory, and achieve the purpose of decoupling from the object. So the biggest advantage of the factory model is decoupling.

There are three types of factories that are used in this tutorial:

  • Simple Factory model
  • Factory method pattern
  • Abstract Factory pattern

4.2.2 Simple Factory mode

Simple factories are more like programming habits than design patterns.

4.2.2.1 structure

A simple factory contains the following roles:

  • Abstract product: Defines the specification of the product, describing the main features and functions of the product
  • Concrete product: A subclass that implements or inherits an abstract product
  • Concrete factory: provides a method to create a product by which the caller retrieves the product
4.2.2.2 realizing

Now use a simple factory to improve on the above example. The class diagram looks like this:

The factory class code is as follows:

public class SimpleCoffeeFactory {
    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if ("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        
        returncoffee; }}Copy the code

The Factory handles the details of creating the object. Once the SimpleCoffeeFactory is in place, orderCoffee() in the CoffeeStore class becomes the customer of the object. Later, if the Coffee object is needed, it can be retrieved directly from the factory. This decouples the Coffee implementation class and creates new coupling between the CoffeeStore object and the SimpleCoffeeFactory factory object, and the factory object and the goods object.

If we added a new variety of coffee later, we would have to modify the SimpleCoffeeFactory code to violate the on/off principle. The factory class may have many clients, such as creating Meituan takeout and so on, so only need to modify the factory class code, save other modification operations.

4.2.2.3 pros and cons

Advantages:

Encapsulates the process of creating objects, and objects can be obtained directly through parameters. Separating the creation of objects from the business logic layer eliminates the need to modify the customer code later, and makes it less likely to modify the customer code and easier to extend if the factory class is modified directly to create a new product rather than in the original code.

Disadvantages:

Adding a new product still requires modifying the factory class code, violating the “open close principle.”

4.2.2.4 extension

In the development process, some people define the factory class to create objects as static methods. This is the static factory pattern, and the code is as follows:

public class SimpleCoffeeFactory {
    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if ("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        
        returncoffee; }}Copy the code

4.2.3 Factory method mode

In view of the shortcomings in the above example, the factory method pattern can be perfectly solved, completely following the open and closed principle.

4.2.3.1 concept

Define an interface for creating objects and let subclasses decide which product class objects to instantiate. Factory method is a product class whose instantiation is deferred to its factory subclass.

4.2.3.2 structure

Key roles of the factory method pattern:

  • AbstractFactory: Provides an interface for creating products through which callers access factory methods of concrete factories to create products.
  • Concrete Factory (ConcreateFactory) : It is mainly used to implement abstract methods in abstract factories to complete the creation of concrete products.
  • Abstract Product: Defines the specification of the Product, describing the main features and functions of the Product.
  • Concrete product (ConcreateProduct) : Implements an interface defined by an abstract product role. It is created by a concrete factory and corresponds to a concrete factory.
4.2.3.3 implementation

Using the factory method pattern to improve the above example, the class diagram is as follows:

The code is as follows:

Abstract factory:

public interface CoffeeFactory {
    Coffee createCoffee(a);
}
Copy the code

Specific factory:

public class LatteCoffeeFactory implements CoffeeFactory {
    public Coffee createCoffee(a) {
        return newLatteCoffee(); }}public class AmericanCoffeeFactory implements CoffeeFactory {
    public Coffee createCoffee(a) {
        return newAmericanCoffee(); }}Copy the code

Coffee shop:

public class CoffeeStore {
    private CoffeeFactory factory;
    
    public CoffeeStore(CoffeeFactory factory) {
        this.factory = factory;
    }
    
    public Coffee orderCoffee(String type) {
        Coffee coffee = factory.createCoffee();
        coffee.addMilk();
        coffee.addsugar();
        returncoffee; }}Copy the code

As you can see from the code you wrote above, when you add product classes, you add factory classes accordingly, and you don’t need to modify the factory class code, which addresses the weakness of the simple factory pattern.

The factory method pattern is a further abstraction of the simple factory pattern, which retains the advantages of the simple factory pattern and overcomes its disadvantages due to the use of polymorphism.

Advantages and disadvantages 4.2.3.4

Advantages:

  • Users only need to know the name of the specific factory can get the product they need, without knowing the specific creation process of the product;
  • When new products are added to the system, only specific product classes and corresponding specific factory classes need to be added without any modification to the original factory to meet the open and close principle.

Disadvantages:

  • Each additional product adds a concrete product class and a corresponding concrete factory class, which increases the complexity of the system;

4.2.4 Abstract factory pattern

The factory method mode introduced in front considers the production of a kind of products, such as livestock farms only raise animals, TV factories only produce television sets, etc..

The factory only produce the same kind product, called with the same kind products, that is to say: the factory method pattern only consider the production with the grade of products, but in real life, many factory is a comprehensive factory, can produce many grade of products, such as electrical appliance factory is producing the TV production of washing machine or air conditioning, there are both software professional and biological professional college.

Abstract factory model will consider the production of multi-grade products. A group of products at different grades produced by a specific factory is called product family. The horizontal axis in the following figure is product grade, that is, the same type of products. The vertical axis is product family, that is, products of the same brand, products of the same brand from the same factory.

4.2.4.1 concept

The abstract factory pattern is a product pattern structure that provides access classes with an interface to create a set of related or interdependent objects, and that access classes can obtain different levels of the same family without specifying the concrete class that requires the product.

The abstract factory pattern is an updated version of the factory method pattern, which produces only one level of products, whereas the abstract factory pattern can produce multiple levels of products.

4.2.4.2 structure

The main roles of the abstract factory pattern are as follows:

  • AbstractFactory: Provides an interface for creating products. It contains multiple methods for creating products with different levels of products
  • ConcreteFactory: ConcreteFactory implements abstract methods in abstract factories to create concrete products
  • AbstractProduct: a specification that defines a product and describes its main features and functions. Abstract factory patterns have multiple abstract products
  • Concreteproducts: Interfaces that implement abstract product roles and are created by concrete factories in a many-to-one relationship
4.2.4.3 implementation

Nowadays, the business of coffee shops has changed, and they not only need to produce coffee but also desserts, such as Tiramisu and matcha mousse. If we follow the factory method mode, we need to define Tiramisu, matcha mousse, Tiramisu factory and matcha mousse factory, which is prone to class explosion. Latte and Americano are both coffee products. Tiramisu, matcha mousse is also a product grade; Latte drinking Tiramisu is the same product family (Italian), americano and matcha mousse are the same product family (American). So this example can be implemented using the abstract factory pattern, with the following class diagram:

Abstract factory:

public interface DessertFactory {
    Coffee createCoffee(a);
    
    Dessert createDessert(a);
}
Copy the code

Specific factory:

// American dessert factory
public class AmericanDessertFactory implements DessertFactory {
    public Coffee createCoffee(a) {
        return new AmericanCoffee();
    }
    
    public Dessert createDessert(a) {
        return newMatchaMousse(); }}// Italian flavor dessert factory
public class ItalyDessertFactory implements DessertFactory {
    public Coffee createCoffee(a) {
        return new LatteCoffee();
    }
    
    public Dessert createDessert(a) {
        return newTiramisu(); }}Copy the code

If you want to add the same product family, you only need to add a corresponding factory class and do not need to modify the other classes.

Advantages and disadvantages 4.2.4.4

Advantages:

  • When multiple objects in a product family are designed to work together, it ensures that clients always use only objects in the same product family.

Disadvantages:

  • All factory classes need to be modified when a new product needs to be added to the product family.
4.2.4.5 Application Scenarios
  • When the objects to be created are a series of interrelated or interdependent product families, such as TV sets, washing machines, air conditioners, etc
  • There are multiple product families in the system, but only one group of products is used at a time, for example, someone only likes to wear a certain brand of clothes and shoes
  • The system provides the product class library, and all products have the same interface, the client does not depend on the creation details and internal structure of the product instance

4.2.5 Mode Expansion

Simple factory + configuration file decoupling

Factory objects and product objects can be decoupled through the factory pattern + configuration file. The factory class is loaded with the name of the entire class in the configuration file, and the object is created for storage. If the client needs the object, it can directly obtain the object:

Step 1: Define the configuration file. For demonstration purposes, we use the properties file named bean.properties as the configuration file

american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee
Copy the code

Step 2: Improve the factory class

public class CoffeeFactory {
    private static Map<String,Coffee> map = new HashMap();
    
    static {
        Properties p = new Properties();
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            p.load(is);

            // Iterate over the Properties collection object
            Set<Object> keys = p.keySet(); for (Object key : keys) {
                // Get value by key (full class name)
                String className = p.getProperty((String) key); // Get the bytecode objectClass clazz = Class.forName(className); Coffee obj = (Coffee) clazz.newInstance(); map.put((String)key,obj); }}catch(Exception e) { e.printStackTrace(); }}public static Coffee createCoffee(String name) {
        returnmap.get(name); }}Copy the code

Static member variables are used to store created objects (keys store names and values store corresponding objects), while reading configuration files and creating objects are written in static blocks of code that are intended to be executed only once.

4.2.6 JDK source code parsing – collection. iterator method

public class Demo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add(Linghu Chong);
        list.add("The wind is clear");
        list.add("Whatever I do.");
        
        // Get the iterator object
        Iterator<String> it = list.iterator(); // use iterators to traverse
        while(it.hasNext()) { String ele = it.next(); System.out.println(ele); }}}Copy the code

You should be familiar with the code above, which uses an iterator to walk through a collection and get the elements in the collection. The method of obtaining iterators for single-column collections uses the factory method pattern. Let’s look at the structure through the class diagram:

The Collection interface is an abstract factory class, and ArrayList is a concrete factory class. The Iterator interface is the abstract commodity class, and the Iter inner class in the ArrayList class is the concrete commodity class. In the concrete factory class, the iterator() method creates the concrete goods class object.

1. The getInstance() method in the DateForamt class uses the factory schema;

2. The getInstance() method in the Calendar class uses factory mode;

4.3 Prototype Mode

This overview

Using an already created instance as a prototype, create a new object that is identical to the prototype object by copying the prototype object.

4.3.2 structure

The archetypal pattern contains the following characters:

  • Abstract stereotype classes: Specify the concrete stereotype objects that must be implementedclone()methods
  • Concrete stereotype class: Implements abstract stereotype classesclone()Method, an object that can be copied
  • Access classes: Use the concrete stereotype classesclone()Method to copy the new object

The interface class diagram is as follows:

4.3.3 implementation

The clone of prototype mode is divided into shallow clone and deep clone.

Shallow clone: Create a new object with the same attributes as the original object. For non-basic attributes, the new object still points to the memory address of the object pointed to by the original attribute.

Clone: When a new object is created, other objects referenced in the property will also be cloned, instead of pointing to the original object address

The Clone () method is provided in the Object class in Java to implement shallow cloning. The Cloneable interface is the abstract prototype class in the class diagram above, and the child implementation class that implements the Cloneable interface is the concrete prototype class. The code is as follows:

Realizetype(concrete prototype class):

public class Realizetype implements Cloneable {
    public Realizetype(a) { System.out.println("Concrete prototype object created!");
    }
    
    @Override
    protected Realizetype clone(a) throws CloneNotSupportedException { 
        System.out.println("Concrete prototype copied successfully!");
        return (Realizetype) super.clone(); }}Copy the code

PrototypeTest(test access class):

public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype r1 = new Realizetype();
        Realizetype r2 = r1.clone();
        System.out.println("Are objects R1 and R2 the same object?"+ (r1 == r2)); }}Copy the code

4.3.4 case

The “three good student” certificates of the same school wear different names of the winners, otherwise all the same, you can use the prototype mode to copy multiple “three good student” certificates, and then modify the name on the certificate.

The class diagram is as follows:

The code is as follows:

/ / diploma classes
public class Citation implements Cloneable {
    private String name;
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getName(a) {
        return (this.name);
    }
    
    public void show(a) {
        System.out.println(name + Classmate: I performed well in the first semester of the 2020 academic year and was awarded "Merit Student". Especially so!");
    }
    
    @Override
    public Citation clone(a) throws CloneNotSupportedException {
        return (Citation) super.clone(); }}// Test access classes
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation c1 = new Citation(); c1.setName("Zhang");
        
        // Copy the certificate
        Citation c2 = c1.clone(); // Change the name of the certificate to lis2 c2.setName(" lis3 ");c1.show(); c2.show(); }}Copy the code

4.3.5 Application Scenarios

  • Object creation is very complex, and you can use prototype mode to create objects quickly
  • High performance and safety requirements

4.3.6 Extending (Deep Clone)

Change the name attribute of the Citation class to a Student attribute as follows:

/ / diploma classes
public class Citation implements Cloneable {
    private Student stu;

    public Student getStu(a) {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

    void show(a) {
        System.out.println(stu.getName() + Classmate: I performed well in the first semester of the 2020 academic year and was awarded "Merit Student". Especially so!");
    }

    @Override
    public Citation clone(a) throws CloneNotSupportedException {
        return (Citation) super.clone(); }}/ / class students
public class Student {
    private String name;

    private String address;

    public Student(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Student(a) {}public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress(a) {
        return address;
    }

    public void setAddress(String address) {
        this.address = address; }}/ / test class
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation c1 = new Citation();
        Student stu = new Student("Zhang"."Xi 'an"); c1.setStu(stu);

        // Copy the certificate
        Citation c2 = c1.clone();

        // Get the c2 certificate for the student
        Student stu1 = c2.getStu(); stu1.setName("Bill");

        // Check whether stu and STU1 are the same object
        System.out.println("Stu and STU1 are the same object?"+ (stu == stu1)); c1.show(); c2.show(); }}Copy the code

The running results are as follows:

Description:

If the stU object is the same as stu1, the name attribute of the STU1 object is changed to “Li4”. This is the effect of shallow cloning, copying the attributes of a reference type in a concrete prototype class (Citation). This situation requires the use of deep cloning, which requires the use of object streams. The code is as follows:

public class CitationTest1 {
    public static void main(String[] args) throws Exception {
        Citation c1 = new Citation();
        Student stu = new Student("Zhang"."Xi 'an");
        c1.setStu(stu);

        // Create an object output stream object
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        // Write the c1 object into the file
        oos.writeObject(c1);
        oos.close();
        
        // Create a stream object
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        // Read the object
        Citation c2 = (Citation) ois.readObject(); 
        // Get the c2 certificate for the student
        Student stu1 = c2.getStu(); 
        stu1.setName("Bill");
        
        // Check whether stu and STU1 are the same object
        System.out.println("Stu and STU1 are the same object?"+ (stu == stu1)); c1.show(); c2.show(); }}Copy the code

The running results are as follows:

The Citation and Student classes must implement the Serializable interface, otherwise NotSerializableException will be thrown.