Java programmers must master the four basic!

Probably everyone began to use Java when they were students. We have been learning Java, but there are always some unclear concepts in Java, no matter for junior or senior programmers. Therefore, the purpose of this article is to clarify these concepts.

After reading this article, you will have a deeper understanding of these concepts, and you will find out all the gray things. In this book, we will discuss anonymous inline classes, multithreading, synchronization, and serialization.

1. Anonymous class

Java anonymous classes are very similar to local classes or inline classes, but there is no name. We can use anonymous classes to define and instantiate a class at the same time. This should only be done if the local class is used only once.

Anonymous classes cannot have an explicitly defined constructor. Instead, each anonymous class implicitly defines an anonymous constructor.

There are two ways to create anonymous classes:

  1. Extend existing classes (either abstract or concrete)

  2. Create interface

The best way to understand the code is to read it first, so let's look at the code first.

interface Football
{
    void kick();
}

class AnnonymousClass {

  public static Football football = new Football() {
        @Override
        public void kick() {
            System.out.println("Nested Anonymous Class.");
        }
    };

    public static void main(String\[\] args)
    {
        // anomynous class inside the method
      Football footballObject = new Football()
      {
          @Override
         public void kick()
          {
              System.out.println("Anonymous Class");
          }
      };
      footballObject.kick();

        AnnonymousClass.football.kick();
    }
}

Anonymous classes can be created in class and function code blocks. As you may know, anonymous classes can be created by interfaces, or by extending abstract or concrete classes. In the above example, I first created an interface football, and then implemented an anonymous class within the scope of the class and the main() method. Football can also be an abstract class, or it can be a top-level class juxtaposed with interface.

Football can be an abstract class, see the code below.

public abstract class Football
{
    abstract void kick();
}

Anonymous classes can be not only abstract classes, but also concrete classes.

// normal or concrete class
public class Football
{        public void kick(){}
}// end of class scope.

What if the Football class doesn't have a constructor without parameters? Can we access class variables in anonymous classes? Do we need to overload all methods in anonymous classes?

// normal or concrete class
public class Football {

    protected int score;

    public Football(int score)
    {
        this.score = score;
    }
    public void score(){
        System.out.println("Score "+score);
    };

    public void kick(){}

    public static void main(String\[\] args) {

        Football football = new Football(7)
        {
            @Override
            public void score() {
                System.out.println("Anonymous class inside the method "+score);
            }

        };
        football.score();
    }
}
// end of class scope.
  1. Any constructor can be used when creating anonymous classes. Note that the parameters of the constructor are also used here.

  2. Anonymous classes can extend top-level classes and implement abstract classes or interfaces. Therefore, the rules of access control still apply. We can access the protected variable, but not private.

  3. Because the Football class is extended in the above code, we do not need to overload all methods. However, if it is an interface or an abstract class, then an implementation must be provided for all unimplemented methods.

  4. Static initialization methods or member interfaces cannot be defined in anonymous classes.

  5. Anonymous classes can have static member variables, but they must be constants.

Purpose of anonymous class:

  1. Clearer project structure: usually we use anonymous classes when we need to change the implementation of some methods of a class at any time. This eliminates the need to add a new *. java file to the project to define the top-level classes. This is especially useful when top-level classes are used only once.

  2. UI event listener: in GUI applications, the most common use of anonymous classes is to create various event handlers. For example, the following code:

button.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 // your handler code here
             }
         });

We created an anonymous class and implemented the setOnClickListener interface. The onClick method is triggered when a user clicks a button.

2. Multithreading

Multithreading in Java can execute multiple threads at the same time. Threads are lightweight subprocesses and the smallest unit of processing. The main purpose of using multithreading is to maximize CPU utilization. We use multithreading instead of multiprocessing, because threads are lighter and can also share memory space within the same process. Multithreading is used to implement multitasking.

Thread life cycle

As shown in the figure above, the life cycle of a thread mainly has five states. Let's explain each state in turn.

  1. New: after creating an instance of a thread, it will enter the new state, which is the first state, but the thread is not ready to run.

  2. Runanable: call the start() method of the thread class, and the status will change from new to Runnable, which means that the thread can run, but when it actually starts depends on the Java thread scheduler, because the scheduler may be busy executing other threads. The thread scheduler selects a thread from the thread pool in FIFO (first in, first out) mode.

  3. Blocked: there are many situations that can cause threads to become blocked, such as waiting for I/O operations, waiting for network connections, etc. In addition, a higher priority thread can turn the currently running thread into a blocked state.

  4. Waiting: the thread can call wait() to enter the waiting state. When other threads call notify(), it will return to the runnable state.

  5. When the terminated: start() method exits, the thread enters the terminated state.

Why multithreading?

Using threads allows Java applications to do multiple things at the same time, thus speeding up the process. In technical terms, threads can help you implement parallel operations in Java programs. Because modern CPU s are very fast and may contain multiple cores, there is no way to use all the cores with only one thread.

Key points to remember

Multithreading can make better use of CPU.

Improve responsiveness and user experience

Reduce response time

Serve multiple clients at the same time

There are two main ways to create threads:

Extend Thread class

Implement the Runnable interface

Creating threads by extending the Thread class

Create a class extension Thread class. This class should overload the run () method in the Thread class. The Thread starts its life cycle in the run () method. We create objects of the new class, and then call the start () method to start executing threads. In the Thread object, start() calls run().

public class MultithreadingTest extends Thread
{
    public void run()
    {
        try{
            System.out.println("Thread "+Thread.currentThread().getName()+" is now running");
        }catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    public static void main(String\[\] args)
    {
        for(int i=0;i<10;i++)
        {
            MultithreadingTest multithreadingTest = new MultithreadingTest();
            multithreadingTest.start();
        }
    }
}

You can also create classes through interfaces.

The following code creates a class that implements the java.lang.Runnable interface and overloads the run() method. Then we instantiate a Thread object and call its start() method.

public class MultithreadingTest implements Runnable
{

    @Override
    public void run() {
        System.out.println("Thread "+Thread.currentThread().getName()+" is now running"); //To change body of generated methods, choose Tools | Templates.
    }

    public static void main(String\[\] args)
    {
        for(int i=0;i<10;i++)
        {

            Thread thread = new Thread(new MultithreadingTest());
            thread.start();
        }
    }
}

WeChat official account: Java technology stack, back to back: multi thread, get the latest N thread thread tutorial I have compiled, all dry cargo.

Thread class and Runnable interface

If you extend the Thread class, you cannot extend more classes, because Java does not allow multiple inheritance. Multiple inheritance can be implemented through interfaces. So it's better to use an interface instead of a Thread class.

If we extend the Thread class, it also contains some methods, such as yield(), interrupt(), etc., which our program may not use. There are no useless methods in Runnable interface.

3. Synchronization

Synchronization refers to multi-threaded synchronization. synchronized Can only be executed by one thread at the same time. Synchronization in Java is a very important concept, because Java is a multi-threaded language, multiple threads can be executed in parallel. In a multithreaded environment, the synchronization of Java objects, or Java classes, is very important.

Why sync?

If the code is executed in a multithreaded environment, the objects shared in multiple threads need to be synchronized to avoid damaging the state or causing any unexpected behavior.

Take a look at this before you dive into the concept of synchronization.

class Table {

    void printTable(int n) {//method not synchronized
        for (int i = 1; i <= 5; i++) {
            System.out.print(n * i+" ");
            try {
                Thread.sleep(400);
            } catch (Exception e) {
                System.out.println(e);
            }
        }

    }
}

class MyThread1 extends Thread {

    Table t;

    MyThread1(Table t) {
        this.t = t;
    }

    public void run() {
        t.printTable(5);
    }

}

class MyThread2 extends Thread {

    Table t;

    MyThread2(Table t) {
        this.t = t;
    }

    public void run() {
        t.printTable(100);
    }
}

class TestSynchronization1 {

    public static void main(String args\[\]) {
        Table obj = new Table();//only one object
        MyThread1 t1 = new MyThread1(obj);
        MyThread2 t2 = new MyThread2(obj);
        t1.start();
        t2.start();
    }
}

Running this code will notice that the output is very volatile because there is no synchronization. Let's look at the output of the program.

Output:

100 5 200 10 300 15 20 400 500 25
class Table {

    synchronized void printTable(int n) {//synchronized method
        for (int i = 1; i <= 5; i++) {
            System.out.print(n * i+" ");
            try {
                Thread.sleep(400);
            } catch (Exception e) {
                System.out.println(e);
            }
        }

    }
}

 class TestSynchronization3 {

    public static void main(String args\[\]) {
        final Table obj = new Table();//only one object

        Thread t1 = new Thread() {
            public void run() {
                obj.printTable(5);
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                obj.printTable(100);
            }
        };

        t1.start();
        t2.start();
    }
}

Add the synchronized , then synchronized Method will not allow other threads to enter until the end of execution. The following output is very stable.

Output:

5 10 15 20 25 100 200 300 400 500

Similarly, Java classes and objects can be synchronized.

Note: we do not necessarily need to synchronize the entire method. Sometimes it's best to synchronize only a small part of the method. Java's synchronized Code snippets can do this.

4. Serialization

Serialization in Java is a mechanism that writes the state of an object to a byte stream. The opposite operation is called deserialization, which converts a byte stream into an object.

The process of serialization and deserialization is platform independent, that is, serializing objects on one platform and then deserializing them on another.

When serializing, call the writeObject() method of ObjectOutputStream, and when deserializing, call the readObject() method of ObjectInputStream class.

In the following figure, Java objects are converted into byte streams and stored in various forms of storage, which is called serialization. On the right side of the figure, byte streams in memory are converted to Java objects, a process called deserialization.

Why serialization

Obviously, the created Java class destroys the object after the program is executed or aborted. To avoid this problem, Java provides serialization, through which you can store objects, or persist state for later use, or on other platforms.

The following code demonstrates the process.

public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private String serializeValueName;
    private transient int nonSerializeValueSalary;

    public String getSerializeValueName() {
        return serializeValueName;
    }
    public void setSerializeValueName(String serializeValueName) {
        this.serializeValueName = serializeValueName;
    }
    public int getNonSerializeValueSalary() {
        return nonSerializeValueSalary;
    }
    public void setNonSerializeValueSalary(int nonSerializeValueSalary) {
        this.nonSerializeValueSalary = nonSerializeValueSalary;
    }

    @Override
    public String toString() {
        return "Employee \[serializeValueName=" + serializeValueName + "\]";
    }
}

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializingObject {

    public static void main(String\[\] args) {

        Employee employeeOutput = null;
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;

        employeeOutput = new Employee();
        employeeOutput.setSerializeValueName("Aman");
        employeeOutput.setNonSerializeValueSalary(50000);

        try {
            fos = new FileOutputStream("Employee.ser");
            oos = new ObjectOutputStream(fos);
            oos.writeObject(employeeOutput);

        System.out.println("Serialized data is saved in Employee.ser file");

        oos.close();
        fos.close();

        } catch (IOException e) {

            e.printStackTrace();
        }
    }
}

Output:

Serialized data is saved in Employee.ser file.
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeSerializingObject {

    public static void main(String\[\] args) {

        Employee employeeInput = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;

        try {
            fis = new FileInputStream("Employee.ser");
            ois = new ObjectInputStream(fis);
            employeeInput = (Employee)ois.readObject();

            System.out.println("Serialized data is restored from Employee.ser file");

            ois.close();
            fis.close();

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("Name of employee is : " + employeeInput.getSerializeValueName());
        System.out.println("Salary of employee is : " + employeeInput.getNonSerializeValueSalary());
    }
}

Output:

Serialized data is restored from Employee.ser file
Name of employee is : Aman
Salary of employee is : 0

Key points to remember

  1. If the parent class implements Serializable Interface, then subclasses do not need to be implemented, but the reverse is not necessarily true.

  2. Only non static data members can be saved during serialization.

  3. Static and temporary data members are not saved during serialization. Therefore, if you do not want to save a non static data member, you can set it to transient.

  4. The object's constructor is not called during deserialization.

  5. Associated object must implement Serializable Interface.

summary

1. First of all, we explained the anonymous class, as well as its use and usage.

2. Secondly, we discussed the multithreading in Java, the life cycle of threads, and their uses.

3. Synchronization only allows one thread to enter the synchronized method or code block to access resources, and other threads must wait in the queue.

4. Serialization is the process of storing the state of an object for later use.

By Himanshu Verma
Original text: https://medium.com/swlh/4-things-that-java-developer-thinks-are-most-confusing-complicated-87c2598f33f0
Translator: Wan Yue, editor: Tu min, producer: CSDN (ID: CSDNnews)

Recommend to my blog to read more:

1.Java JVM, collection, multithreading, new features series

2.Spring MVC, Spring Boot, Spring Cloud series tutorials

3.Maven, Git, Eclipse, Intellij IDEA series tools tutorial

4.Latest interview questions of Java, backend, architecture, Alibaba and other large factories

Feel good, don't forget to like + forward!

Tags: Java Spring network jvm

Posted on Thu, 07 May 2020 20:35:18 -0700 by MerMer