Anomalies and multithreading

The first chapter abnormal

1.1 unusual concept

Refers to the abnormal circumstances in the course of program implementation, there will eventually lead to a non-JVM normal stop.

In object-oriented programming languages ​​such as Java, the class itself is an anomaly, an exception is to create an exception object and throws an exception object. Java is a way to handle exceptions interrupt handling.

1.2 abnormal system

The exception is the root class java.lang.Throwable, there are two subclasses under: java.lang.Errorand java.lang.Exception, said abnormality is usually the latter.

Throwable system:

  • Error: Serious error, the error can not be handled through, it can only be achieved to avoid.
  • Exception: Means the exception, when an exception occurs by way programmers can correct the code, the program continues to run, is the need to properly handle.

Throwable common methods:

  • public void printStackTrace(): For more information about printing the exception.

    Contain the reasons for the type of anomaly, anomalous, also includes locations of the anomalies in the development and debugging phase, had to use printStackTrace

  • public string getMessage(): Gets the reason the exception occurred.

    To the user when it prompts the wrong reasons

  • public String toString(): Get the type of exception and exception descriptions (no).

Abnormal, can put unusual simple class name copied to the API query

1.3 abnormal generation of analytical process

1567066501414

Chapter handle exceptions

2.1 throw an exception throw

In Java, it provides a throw keyword, which is used to throw an exception object specified.

manual:

  1. Create an exception object, package some tips (you can write your own message);
  2. The exception object needs to be informed to the caller, can be completed by keyword throw, throw in the methods used, to throw an exception object, pass the exception object to the caller at, and ends execution of the current method;

Using the format:

throw new 异常类名(参数);

For example:

public class throw关键字 {
    public static void main(String[] args) {
        int[] arr = null;
        /* 传入为null的数组 */
        int element = getElement(arr, 1);
        System.out.println(element);
    }
    public static int getElement(int[] arr,int index){
        if (arr == null){
            /* 手动抛出异常 */
            throw new NullPointerException("这是数组是null");
        }
        return arr[index];
    }
}

2.2 Objects determined non-empty

Class Objects, by some static utility methods composition, these methods are null-save (null pointer safety) or null-tolerant (tolerance null pointer), then its source code, an object is a parabolic null value an abnormal operation.

  • public static<T> T requireNonNull(T obj): Specifies reference object is not null;

Source code for the method described above:

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

The benefits of using: in determining whether the time is empty, only need to call the method can determine whether or not a null pointer, reducing the amount of code of the self-determination

For example:

import java.util.Objects;

public class throw关键字 {
    public static void main(String[] args) {
        int[] arr = null;
        int element = getElement(arr, 1);
        System.out.println(element);
    }
    public static int getElement(int[] arr,int index){
//        if (arr == null){
//            throw new NullPointerException("这是数组是null");
//        }
        /* 将上述抛出异常换成下面的方法,效果相同,使用更简洁 */
        Objects.requireNonNull(arr,"数组为null");
        return arr[index];
    }
}

2.3 Statement throws an exception

The problem of identity out of the report to the caller. Abnormal, and if not caught within the method throw throw a compiler handle exceptions, it must be declared in the throws, let the caller to deal with.

Keyword throws on the method used in the statement, is used to indicate the current method does not handle the exception, but rather to remind the caller of the method to handle exceptions (throw an exception).

Unusual statement format:

修饰符 返回值类型 方法名(参数) throws AAAExcetion,BBBExcetion...{
    throw new AAAExcetion("产生原因");
    throw new BBBExcetion("产生原因");
}

Precautions:

  • throws keyword must be written in the method declaration;
  • keyword throws back exceptions must be declared Exception or a subclass of Exception;
  • If more than one internal method throws an exception object, then throws back must also declare multiple abnormalities. If there are multiple exceptions thrown objects like father and son relationship, less direct statement to the parent anomaly;
  • A statement calling the method throws an exception, we have to deal with abnormal declared either continue to use the throw statement throws, to the processing method of the caller, and ultimately to the JVM, or try ... catch to handle exceptions yourself

For example:

/* 自定义异常 */
class myException extends Exception{
    public myException(){}
    public myException(String s){
        super(s);
    }
}
public class 自定义异常类 {
    /* 定义异常的函数 */
    public static String func(int score) throws myException {
        System.out.println("1");
        if (score >= 0 && score <= 100) {
            return "ok";
        } else {
            throw new myException("错误输入");
        }
    }
    public static void main(String[] args) {
        /* 捕获异常 */
        try{
            String str1 = func(88);
            System.out.println(str1);
            String str2 = func(120);
            System.out.println(str2);

        } catch (myException e){
            System.out.println("异常信息为:"+e.getMessage());
        } finally {
            System.out.println("结尾代码,都会出现这句话");
        }
        System.out.println("不管上面代码出错否,都不会中断程序,都会出现这句话");
    }
}

2.4 catch exceptions try ... catch

If an exception occurs, it will immediately terminate the program, so we have to handle exceptions:

  1. This method does not handle, but dished out a statement by the caller of the method to deal with (throws);
  2. Use try-catch block of statements in the method to handle exceptions

try-catch way is to catch the exception

  • Catch exceptions : Java in the statement of abnormal targeted for capture, processing anomalies can occur in a specified manner;

Catch exceptions syntax is as follows:

try {
    编写可能出现异常的代码
} catch (异常类型 e) {
    处理异常的代码
    // 记录日志/打印异常信息/继续抛出异常
} 

How to get exception information:

Throwable class defines a number of viewing methods:

  • public String getMessage(): Get the description information, the reason (short message) abnormalities

  • public String toString(): Get the type of abnormality and abnormality description information (detail information)

  • public void printStackTrace(): Print the exception stack trace information and output to the console (JVM print exception object, this method is used by default, exception information is the most comprehensive print)

    Contains the type of the exception reason, unusual, abnormal position in the development and debugging phase, we had to use printStackTrace.

2.5 Failure Notes

  • An exception is thrown can not deal with that statement does not capture nor thrown at runtime.
  • If finally there is the return statement, it will never return results finally in, to avoid this situation.
  • If the parent class method throws an exception plurality , the subclass overrides the superclass method, and throw the same exception class or parent is the parent class or subclass exception is not thrown.
  • If the parent class method does not throw an exception , nor can throw subclasses override the parent class method when abnormal, then subclass generate the exception, you can only catch the exception, not throw statement.

  • After try ... catch finally block may be added, in which the code will be executed, is generally used for recycling.

  • Classification of abnormality, in addition to catch exceptions than is uncaught exception , comprising uncaught exception Errorclass and its subclass of direct, indirect and subclasses of RuntimeExceptionclass and subclass of its direct and indirect subclasses.

    Use catch the exception when, try placing a statement can not catch exceptions that can not happen, otherwise it will error

Example I:

class Fu {
    public void method1() throws Exception{}
    public void method2() throws Exception{}
    public void method3() throws Exception{}
    public void method4() {}
}
class Zi extends Fu {
    /* 父类抛出异常的方法重写,子类抛出与父类相同的异常 */
    @Override
    public void method1() throws Exception{}
    /* 父类抛出异常的方法重写,子类抛出父类异常的子类 */
    @Override
    public void method2() throws IndexOutOfBoundsException{}
    /* 父类抛出异常的方法重写,子类不抛出异常 */
    @Override
    public void method3() {}
    /* 如果父类没有抛出异常,那么子类也不能抛出异常,若是存在异常,自行捕获 */
    @Override
    public void method4() {
        try {
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class 子类重写父类抛出异常函数 {
}

Example II:

import java.io.IOException;

public class 不可能出现异常的代码 {
    public static void main(String[] args) {
        try{
            System.out.println("111");
        } catch (IOException E){// 会报错
            System.out.println("捕获异常");
        } catch (IndexOutOfBoundsException e){
            System.out.println("未捕获异常");
        }
    }
}

Chapter III custom exception

In development is always some exceptions are not defined, so we need to define exception classes according to the abnormal situation of their business.

How abnormal class definition:

  1. Customizing a compiler exception inherited java.lang.Exception;

    If the internal compiler method throws an exception, it must handle the exception, or throws, or try ... catch

  2. A runtime exception classes, inheritance in java.lang.RuntimeException;

    Runtime exception, without treatment, to the virtual machine processing (interrupt handling)

/* 自定义一个异常类 */
class myException extends Exception{
    public myException(){}
    public myException(String s){
        super(s);
    }
}
public class 自定义异常类 {
    /* 定义抛出异常的方法 */
    public static String func(int score) throws myException {
        System.out.println("1");
        if (score >= 0 && score <= 100) {
            return "ok";
        } else {
            throw new myException("错误输入");
        }
    }
    public static void main(String[] args) {
        try{
            String str1 = func(88);
            System.out.println(str1);
            String str2 = func(120);
            System.out.println(str2);

        } catch (myException e){
            System.out.println("异常信息为:"+e.getMessage());
        } finally {
            System.out.println("不管上面代码出错否,都会出现这句话");
        }
    }
}

Chapter IV multithreaded

More than 4.1 threads principles

Use a multithreaded fashion: inheritance Thread class, and place the code in the run function performed, and finally start calling

package day13;
class myThread extends Thread {
    private String testName;

    public myThread() {
    }

    public myThread(String testName) {
        this.testName = testName;
    }

    public String getTestName() {
        return testName;
    }

    public void setTestName(String testName) {
        this.testName = testName;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(testName+"-->"+i);
        }
    }
}
public class 多线程 {
    public static void main(String[] args) {
        myThread myThread = new myThread("吖啊");
        /* 以下语句执行后会开启一个不同于main线程的新线程 */
        myThread.start();// 调用start方法,
        for (int i = 0; i < 10; i++) {
            System.out.println("main-->"+i);
        }
    }
}

The output order is not unique

1567695362576

4.2 Thread class

java.lang.Thread class, used as follows

Construction method:

  • public Thread(): Assign a new thread object
  • public Thread(String name): Assign a designated name of the new thread object
  • public Thread(Runnable target)Assign a new thread object with the specified objectives:
  • public Thread(Runnable target,String name)New thread with the specified target object is assigned a name and:

Common methods:

  • public String getName(): Get the name of the current thread

  • public void start(): Causes this thread to begin execution; Java Virtual Machine calls the run method of this thread

  • public void run(): This thread task to be performed, this custom code

  • public static void sleep(long millis): The thread is currently executing a specified number of milliseconds to pause

    Here there may be an exception, the need for exception handling, or try ... catch, or throws

  • public static Thread currentThread(): Return of the currently executing thread object reference

Gets the thread name (in two ways):

class MyThread extends Thread {
    @Override
    public void run() {
        /* 获取进程名方法一 */
        String name_1 = this.getName();
        System.out.println(name_1);
        /* 获取进程名方法二 */
        Thread thread = Thread.currentThread();
        System.out.println(thread);
        String name_2 = thread.getName();
        System.out.println(name_2);
    }
}
public class 获取线程名称 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
        System.out.println(Thread.currentThread().getName());
    }
}

Set the thread name (in two ways):

  1. Use setName Thread class;

    void setName(String name): Change the name of this thread equal to the argument name

  2. Creating a parameterized constructor method, passing the name of the thread of the argument; call argument constructor with the parent class, pass the thread name to the parent, so the parent class (Thread) to set the name of the child thread;

class MyThread extends Thread {
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
public class 设置线程名称 {
    public static void main(String[] args) {
        MyThread myThread_1 = new MyThread();
        /* 方法一 */
        myThread_1.setName("使用setName方法定义的进程名称");
        myThread_1.start();
        /* 方法二 */
        MyThread myThread_2 = new MyThread("使用构造函数定义的进程名称");
        myThread_2.start();
    }
}
// 输出
// 使用setName方法定义的进程名称
// 使用构造函数定义的进程名称

4.3 Second way to create multi-threaded

Implementation steps:

  1. Creating a class that implements the interface Runnable
  2. run method Runnable interface rewritten in the implementation class, set the thread task
  3. Creating a class that implements Runnable interface objects
  4. Create Thread class object constructor implemented passed Runnable interface class object
  5. Start method is called Thread class, open a new thread executes the run method
/* 实现步骤1 */
class RunnableImpl implements Runnable {
    /* 实现步骤2 */
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
public class 多进程创建的方式二 {
    public static void main(String[] args) {
        /* 实现步骤3 */
        RunnableImpl runnable = new RunnableImpl();
        /* 实现步骤4-5 */
        new Thread(runnable).start();
    }
}

The difference between 4.4 Thread and Runable

If a class inherits from the Thread class, not suitable for resource sharing. But if you implement Runnable interface, then it is easy to share resources.

In summary, the implement Runnable than the Thread class inherits the advantages are:

  1. Multiple threads for the same program code to share the same resources;
  2. To avoid the limitations of single inheritance in Java;
  3. Increase the robustness of the program, decoupling operation, code can be shared by multiple threads, and thread independent codes;
  4. Put the thread pool can only achieve Runnable or Callable classes such as threads, not directly into the inheritance Thread class;

Expansion: In Java, every time the program starts to run at least two threads, one is the main thread, a garbage collection thread. Because every time a command is executed using the Java class, in fact, will start a JVM, each JVM is actually initiated a process in the operating system.

4.5 anonymous inner class way to achieve thread creation

public class 匿名类实现多线程 {
    public static void main(String[] args) {
        /* 继承Thread的匿名函数实现多线程 */
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        thread.start();
        /* 接口Runnable使用多态实现多线程 */
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        new Thread(runnable).start();
    }
}

Chapter V thread-safety issues

5.1 synchronized block

synchronizedAn area keyword can be used in the method of representing only enforcing mutually exclusive access to resources of this block.

format:

synchronized(obj){
    // 需要同步操作的代码区域
}

Genlock (obj):

Which, obj called synchronization monitor, which is locked, the principle is: the current thread begins execution synchronized block, you must first obtain a lock on the synchronization code block. And locking any time only one thread can obtain synchronization of the monitor, when the synchronization code block finishes, the thread releases the lock on the synchronization monitor.

Wherein the lock may be in a non-static method is this, as the current class itself in a static method (e.g., single idler embodiment mode: Singleton.class).

  1. Lock object can be any data type;
  2. Multiple threads object, you need to use the same lock;

Note: At any time, at most one thread can own synchronization lock, who got the lock to enter a code block (lock at the same time also was brought in), we found no other thread can only wait for a lock (BLOCKED).

Several processes in order to obtain cpu resources, until he came to a synchronized block, this time to see who grabbed the cpu whoever carries locks, while other processes are blocked to enter the state, after you perform synchronized block, the process still has cpu resources until cycle to the next synchronization code block (or code execution End) will release cpu resources, competition for each post and then wake up again blocking process cpu resources to complete the next round of synchronized block.

For example:

class sailTicket implements Runnable {
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"one");
        while (true) {
            System.out.println(Thread.currentThread().getName()+"two");
            /* 同步代码区,其中obj也可以换成this或者sailTicket.class */
            synchronized(obj){
                System.out.println(Thread.currentThread().getName()+"three");
                if (ticket > 0) {
                    System.out.println("正在售卖第" + ticket + "张票" + Thread.currentThread().getName());
                    ticket--;
                }
            }
        }
    }
}

public class 卖票的同步出售 {
    public static void main(String[] args) {
        sailTicket sailTicket = new sailTicket();
        new Thread(sailTicket).start();
        new Thread(sailTicket).start();
        new Thread(sailTicket).start();
    }
}
/* 部分输出结果如下:
Thread-2one
Thread-1one
Thread-0one
Thread-2two
Thread-1two
Thread-0two
Thread-2three
正在售卖第100张票Thread-2
Thread-2two
Thread-0three
正在售卖第99张票Thread-0
Thread-0two
Thread-1three
正在售卖第98张票Thread-1
Thread-1two
Thread-0three
正在售卖第97张票Thread-0
Thread-0two
Thread-2three
*/

5.2 synchronous method

Using the synchronizedmodified method, called synchronization methods to ensure a thread when the thread of execution, other threads can only wait out the method

format:

public synchronized void method(){
    // 可能会产生线程安全问题的代码
}

Who synchronization lock is?

For non-static method, genlock is this,

For a static method, we use the current bytecode object class methods where (class name .class) as a synchronization lock

For example:

class sailTicket implements Runnable {
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"one");
        while (true) {
            System.out.println(Thread.currentThread().getName()+"two");
            /* 执行同步方法 */
            method();
        }
    }
    public synchronized void method(){
        System.out.println(Thread.currentThread().getName()+"three");
        if (ticket > 0) {
            System.out.println("正在售卖第" + ticket + "张票" + Thread.currentThread().getName());
            ticket--;
        }
    }
}

public class 卖票的同步出售 {
    public static void main(String[] args) {
        sailTicket sailTicket = new sailTicket();
        new Thread(sailTicket).start();
        new Thread(sailTicket).start();
        new Thread(sailTicket).start();
    }
}
/* 部分输出结果如下:
Thread-1one
Thread-0one
Thread-2one
Thread-1two
Thread-0two
Thread-2two
Thread-1three
正在售卖第100张票Thread-1
Thread-1two
Thread-2three
正在售卖第99张票Thread-2
Thread-2two
Thread-0three
正在售卖第98张票Thread-0
Thread-0two
*/

5.3 Lock Lock

java.unit.concurrent.locks.LockProviding mechanisms than synchronized code block and synchronized method broader locking operation, sync block / synchronization method has the function Lock has, in addition to the more powerful is the embodiment of the object-oriented.

Lock Lock Lock also called synchronous, a method of locking and releasing the lock, as follows:

  • public void lock(): Plus genlock
  • public void unlock: Synchronization lock release

For example:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class sailTicket implements Runnable {
    private static int ticket = 100;
    public Lock lock = new ReentrantLock();
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"one");
        while (true) {
            System.out.println(Thread.currentThread().getName()+"two");
            /* 加同步锁 */
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"three");
                if (ticket > 0) {
                    System.out.println("正在售卖第" + ticket + "张票" + Thread.currentThread().getName());
                    ticket--;
                }
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                /* 不管是否有错误,都会释放同步锁 */
                lock.unlock();
            }
        }
    }
    public static synchronized void method(){

    }
}

public class 卖票的同步出售 {
    public static void main(String[] args) {
        sailTicket sailTicket = new sailTicket();
        new Thread(sailTicket).start();
        new Thread(sailTicket).start();
        new Thread(sailTicket).start();
    }
}

Chapter VI thread pool

Thread Pool: JDK1.5 provided after solve the thread to create frequent, time brought destruction, waste of space

Java.util.concurrent.Executors: Thread Pool class factory for generating the thread pool

Executor class static method:

static ExecutorService newFixedThreadPool(int nThreads): A fixed number of threads to create a reusable thread pool

Parameters:

int nThreads: Create a number of threads in the thread pool included

Returns:

ExecutorService interfaces, returns the class object implement ExecutorService interface, we can use the interface to receive ExecutorService (oriented programming interface)

Java.util.concurrent.ExecutorService: thread pool interface to obtain the thread from the thread pool, start method is called, the task execution thread

Common methods:

submit(Runnable task): Submits a Runnable task for execution

void shutdown(): Close / destroy the thread pool

Thread pool using the steps:

  1. Using the thread pool inside the factory class Executor newFixedThreadPool producing a static method specified number of threads of thread pool;
  2. Create a class that implements the Runnable interface override the run method, set the thread task;
  3. submit method calls in ExecutorService, passing threaded tasks (Runnable implementation class), open thread, run method;
  4. ExecutorService shutdown method call in the destruction of the thread pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/* 创建Runnable的实现类,并重写run方法 */
class RunnableImpl implements Runnable {
    private int num = 20;
    @Override
    public void run() {
        while (num > 0){
            System.out.println("this is:" + num + Thread.currentThread().getName());
            num--;
        }
    }
}
public class 使用线程池 {
    public static void main(String[] args) {
        /* 生成线程数为3的线程池 */
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        /* Runnable实现类的对象实例 */
        RunnableImpl runnable = new RunnableImpl();
        /* 将runnable作为参数传入线程池的submit方法中,从而获取一个线程执行run任务
         * 若是任务完成或者分配的时间片使用完毕,则自动将线程归还给线程池
         */
        executorService.submit(runnable);
        executorService.submit(runnable);
        executorService.submit(runnable);
    }
}

Chapter VII Lambda expressions

Simplified way of writing an anonymous inner class

Lambda use the premise:

  • Using a Lambda must have an interface, and the interface required and only an abstract method.

    Whether the built-in JDK Runnable, Comparatorinterfaces or custom interfaces, and only when there is an abstract interface methods and only when, can use Lambda.

  • Using a Lambda must be inferred context .

    The method is a local variable or argument type must be an interface type corresponding to Lambda, Lambda can use the interface as an example.

Lambda expressions of standard format, consists of three parts:

  1. Some arguments: the abstract interface process parameters, no parameters, it is empty, there are parameters to write parameter, separated by commas plurality of parameters;

    Data type parameter list in parentheses, can be omitted, if only one parameter, then the type and () can be omitted

  2. An arrow: transfer means, parameters passed to the method of {01};
  3. Piece of code: rewriting abstract interface method;

{} If only one row in the code, whether or not a return value, can be omitted ({}, return, semicolon), which must be omitted while three

format:

(Parameter list) -> (some code rewriting method)

Example I:

public class 使用Lambda表达式 {
    public static void main(String[] args) {
        /* 使用匿名内部类开启多线程 */
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "这个匿名内部类执行了");
            }
        }).start();
        /* 使用Lambda开启多线程 */
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "Lambda表达式执行了");
        }).start();
        /* 优化Lambda书写方式 */
        new Thread(()-> System.out.println(Thread.currentThread().getName() + "优化版Lambda表达式执行了")).start();
    }
}
/*  输出结果如下:
    Thread-2优化版Lambda表达式执行了
    Thread-0这个匿名内部类执行了
    Thread-1Lambda表达式执行了
*/

Example II:

import java.util.Arrays;
import java.util.Comparator;

class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class 使用Lambda进行排序 {
    public static void main(String[] args) {
        Person[] personArr = {
                new Person("张三",19),
                new Person("李四",17),
                new Person("王五",22)
        };
        /* 方式一:使用匿名内部类排序 */
        Arrays.sort(personArr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        for (Person item : personArr) {
            System.out.println(item);
        }
        /* 方式二:使用Lambda表达式排序,其中参数列表的数据类型Person可以省略 */
        Arrays.sort(personArr, (Person o1, Person o2) -> {
                return o1.getAge() - o2.getAge();
            }
        );
        System.out.println("===========================");
        for (Person item : personArr) {
            System.out.println(item);
        }
    }
}
/*  输出结果如下:
    Person{name='张三', age=19}
    Person{name='李四', age=17}
    Person{name='王五', age=22}
    ===========================
    Person{name='李四', age=17}
    Person{name='张三', age=19}
    Person{name='王五', age=22}
*/

Guess you like

Origin www.cnblogs.com/carle/p/12095376.html