day06--Java advanced programming: multi-threading, enumeration classes, annotations, reflection, network communication

1 Day16 – Multithreading 01

1.1 Program Concept

Program: It is a set of instructions written in a certain language to complete a specific task. It refers to a piece of static code, a static object.

1.2 Process

1.2.1 Concept

Process: It is an execution process of a program, or a program that is running. It is a dynamic
process: it has its own process of emergence, existence and demise. --life cycle

  1. Such as: running QQ, running MP3 player
  2. Programs are static, processes are dynamic
  3. Process is the unit of resource allocation. The system will allocate different memory areas to each process during operation.

Note: A program execution can have multiple processes. The operation of a software depends on process execution. It may rely on one process (IDEA) or multiple processes (chrome). The corresponding process is loaded when the software is running, and the corresponding process will disappear when the software is closed, which is dynamic. ..

1.3 Threads

1.3.1 Features

  1. Independence : A process is an independent entity in the system. It can have its own independent resources. Each process has its own private address space. A user process cannot directly access the address space of other processes without the permission of the process itself.
  2. Dynamic : The difference between a process and a program is that a program is just a static set of instructions, while a process is a set of instructions that is active in the system. The concept of time is added to the process. The process has its own life cycle and various states. These concepts are not available in the program.
  3. Concurrency : Multiple processes can execute concurrently on a single processor without affecting each other.

1.3.2 Concept

Thread: It is the smallest unit that the operating system can perform calculation scheduling. It is an execution path within a program. It is included in the process and is the actual operating unit in the process. A process can start multiple threads.

  1. If a process executes multiple threads in parallel at the same time, it supports multi-threading.
  2. If a process has only one thread, the program is called single-threaded.
  3. If there are multiple execution paths in a process, it is called a multi-threaded program (Such as 360: can clean garbage, anti-virus, computer physical examination and other operations at the same time。)。
  4. Threads are the unit of scheduling and execution. Each thread has an independent running stack and program counter (pc), and the overhead of thread switching is small.
  5. Multiple threads in a process share the same memory unit/memory address space
  6. They allocate objects from the same heap and have access to the same variables and objects. This makes communication between threads easier and more efficient.However, multiple threads operating shared system resources may bring security risks.
  7. In short, after a program runs, there is at least one process, and a process contains multiple threads.

Insert image description here

1.4 The relationship between processes and threads

Insert image description here
figure 1

As can be seen from Figure 1: there can be multiple processes in an operating system, and there can be multiple threads in a process. Each process has its own independent memory. Each thread shares the memory in a process, and each thread Has its own independent memory. (It is very important to remember this relationship clearly!)
So if you want to use thread technology, you must first have a process. The process is created by the OS. Can you achieve it? No, it is usually done in C or C++ language.

relation:

  1. The scope of a process is larger and can include multiple threads. The scope of a thread is smaller and is the smallest unit that can be scheduled when each software is running.
  2. A process has its own storage space, can store its own data, and can contain multiple threads.
  3. Threads also have their own storage space, and each thread is independent.

Insert image description here
figure 2

It can be seen from Figure 2:

  1. The virtual machine stack and program counter have one copy for each thread.
  2. Method area and heap, each process has one copy of them.
  3. That is: if a program has two processes, each process needs a method area and heap. Each process has 2 threads, and each thread has a virtual machine stack and program counter. Therefore, each thread shares the method area and heap of the same process, so that multiple threads operating shared system resources may cause security risks.

1.5 Single-core/multi-core CPU, the concept of parallelism/concurrency

Insert image description here

1.5.1 CPU time-sharing scheduling

Insert image description here

Time slice : The time allocated by the CPU to each program. Each process is assigned a time period, called its time slice. That is, the time that the process is allowed to run so that each program appears to be running at the same time.
If the process is still running when the time slice ends, the CPU will be deprived of it and assigned to another process, suspending the current process. If the process blocks or ends before the end of the time slice, the CPU will switch immediately without wasting CPU resources. When switching to the previously executed process, restore the scene and continue execution.

On a macro level : we can have multiple applications open at the same time, each running in parallel and at the same time. 1/3000ns

On a micro level : due toonly one CPU, only part of the program requirements can be processed at a time. How to deal with fairness, one way is to introduce time slices, and each program executes in turn. Multi-core improves concurrency.

1.6 Multithreading

1.6.1 Advantages of multithreading

A single-core CPU executes multiple tasks in sequence: because multi-threading requires time to switch back and forth, single-threading is faster than multi-threading.
Multi-core CPUs perform multiple tasks: single threads can only execute one by one no matter how many CPUs there are. Multi-threads can be switched because there are multiple CPUs. One CPU controls a part of the threads, and multiple CPUs control the speed of multiple threads executing tasks at the same time. Much faster than single thread.
Insert image description here

1.6.2 When to use multithreading

Insert image description here

1.6.3 How to distinguish between single thread and multi-thread

Insert image description here

1.7 Multi-threading features

1.7.1 Randomness

Insert image description here

1.7.2 Thread status

Insert image description here

There are five states in total in the thread life cycle:

  1. New state (New) : When the thread object pair is created, it enters the new state, such as: Thread t = new MyThread();
  2. Ready state (Runnable) : When the start() method (t.start();) of the thread object is called, the thread enters the ready state. A thread in the ready state only means that the thread is ready and waiting for CPU scheduling execution at any time. It does not mean that the thread will be executed immediately after executing t.start();
  3. Running state (Running) : When the CPU starts to schedule the thread in the ready state, the thread can actually be executed at this time, that is, it enters the running state. Note: The ready state is the only entrance to the running state. That is to say, if a thread wants to enter the running state for execution, it must first be in the ready state;
  4. Blocked state (Blocked) : For some reason, the thread in the running state temporarily gives up the right to use the CPU and stops execution. At this time, it enters the blocked state. It will not have the opportunity to be called by the CPU again until it enters the ready state. to the running state;
    depending on the reasons for the blocking, the blocking state can be divided into three types :
    a) Waiting blocking : The thread in the running state executes the wait() method, causing this thread to enter the waiting blocking state;
    b) Synchronous blocking : When a thread fails to acquire a synchronized synchronization lock (because the lock is occupied by other threads), it will enter a synchronized blocking state; c
    ) Other blocking : by calling the thread's sleep() or join() or issuing an I/O request, the thread will enter a blocking state. When the sleep() state times out, join() waits for the thread to terminate or times out, or the I/O processing is completed, the thread returns to the ready state.
  5. Dead state (Dead) : The thread has finished executing or exited the run() method due to an exception, and the thread ends its life cycle.

1.8 Multi-thread creation 1: Inherit the Thread class

1.8.1 Overview

The Thread class (lang package) is essentially an instance that implements the Runnable interface and represents an instance of a thread. The only way to start a thread is through the start() instance method of the Thread class. The Start() method is a native method that will notify the underlying operating system. Eventually, the operating system will start a new thread, and the operating system will execute the run() method. It is very simple to implement multi-threading in this way. By directly extending Thread through your own class and overriding the run() method, you can start a new thread and execute your own defined run() method.

Simulate opening multiple threads and calling the run() method on each thread

A thread is a thread of execution in a program. The Java Virtual Machine allows applications to run multiple threads of execution concurrently.

1.8.2 Creating objects

      //自己定义一个子类继承Thread,可以是多态的的形式创建
        Thread() 
		          分配新的 Thread 对象。 
		Thread(Runnable target) 
		          分配新的 Thread 对象。 
		Thread(Runnable target, String name) 
		          分配新的 Thread 对象。 
		Thread(String name)   //创建对象时指定的线程名称。
		          分配新的 Thread 对象。 
		......

1.8.3 Thread common methods and thread priority

package atguigu.java;

/**
 * 测试Thread中的常用方法:
 * 1. void start():启动当前线程;调用当前线程的run()
 * 2. void run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
 * 3. static Thread currentThread():静态方法,返回执行当前代码的线程 通过类名调用
 * 4. String getName():获取当前线程的名字
 * 5. void setName():设置当前线程的名字
 * 6. static void yield():释放当前cpu的执行权
 * 7. void join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才
 *           结束阻塞状态。
 * 8. void stop():已过时。当执行此方法时,强制结束当前线程。
 * 9. static void sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前
 *                          线程是阻塞状态。
 * 10. boolean isAlive():判断当前线程是否存活
 * 11.long getId() 返回该线程的标识符。
 *
 *
 * 线程的优先级:
 * 1.
 * MAX_PRIORITY:10   最大
 * MIN _PRIORITY:1   最小
 * NORM_PRIORITY:5  -->默认优先级
 * 2.如何获取和设置当前线程的优先级:
 *  int getPriority():获取线程的优先级
 *  void setPriority(int p):设置线程的优先级
 *
 *   说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下
 *   被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
 *
 *
 * @author shkstart
 * @create 2019-02-13 下午 2:26
 */
class HelloThread extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 == 0){
    
    

//                try {
    
    
					//sleep方法会抛异常,因为这是重写后的方法,重写之前的方法没有抛异常所以不能用throws只能是try-catch
//                    sleep(10);//单位:毫秒  1000毫秒=1秒
//                } catch (InterruptedException e) {
    
    
//                    e.printStackTrace();
//                }

                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
            }

//            if(i % 20 == 0){
    
    
//                yield();
//            }

        }

    }

    public HelloThread(String name){
    
    
        super(name);
    }
}


public class ThreadMethodTest {
    
    
    public static void main(String[] args) {
    
    

        HelloThread h1 = new HelloThread("Thread:1");//通过构造器给线程命名

//        h1.setName("线程一"); 写在start()方法之前 通过setName方法给线程命名
        //设置分线程的优先级 可以写数字 1 2 3...,也可以写这几个值 MAX_PRIORITY MIN _PRIORITY 但只能代表10 1
        h1.setPriority(Thread.MAX_PRIORITY);

        h1.start();

        //给主线程命名
        Thread.currentThread().setName("主线程");
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
            }

//            if(i == 20){
    
    
//                try {
    
    
//                    h1.join();
//                } catch (InterruptedException e) {
    
    
//                    e.printStackTrace();
//                }
//            }

        }

//        System.out.println(h1.isAlive());

    }
}

1.8.4 Case test 1: 2 threads (t1, t2) do the same thing

Just define a subclass to inherit Thread.

package atguigu.java;

/**
 * 多线程的创建,方式一:继承于Thread类
 * 1. 创建一个继承于Thread类的子类
 * 2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中
 * 3. 创建Thread类的子类的对象
 * 4. 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()
 * <p>
 * 例子:遍历100以内的所有的偶数
 *
 * @author shkstart
 * @create 2019-02-13 上午 11:46
 */

//1. 创建一个继承于Thread类的子类
class MyThread extends Thread {
    
    
    //2. 重写Thread类的run()
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}


public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        //3. 创建Thread类的子类的对象
        MyThread t1 = new MyThread();

        //4.通过此对象调用start():①启动当前线程 ② 调用当前线程的run()
        t1.start();
        //问题一:我们不能通过直接调用run()的方式启动线程。
//        t1.run();

        //问题二:再启动一个线程,遍历100以内的偶数。不可以还让已经start()的线程去执行。会报IllegalThreadStateException
//        t1.start();
        //我们需要重新创建一个线程的对象
        MyThread t2 = new MyThread();
        t2.start();


        //如下操作仍然是在main线程中执行的。
        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ":" + i + "***********main()************");
            }
        }
    }

}

Insert image description here
According to the thread name, it can be seen that the execution result is random.
Insert image description here
Note: It can be confirmed from the above results that the start() method only notifies the operating system that the thread is ready. The operating system decides when to execute it, and our JVM has no control over it.

1.8.5 Case test 2: 2 threads (t1, t2) do different things

Two subclasses need to be defined to inherit Thread.

package atguigu.exer;

/**
 * 练习:创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数
 *
 *
 * @author shkstart
 * @create 2019-02-13 下午 2:16
 */
public class ThreadDemo {
    
    
    public static void main(String[] args) {
    
    
//        MyThread1 m1 = new MyThread1();
//        MyThread2 m2 = new MyThread2();
//
//        m1.start();
//        m2.start();

        //如果一个线程只用一次,可以简写为创建Thread类的匿名子类的方式
        new Thread(){
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 100; i++) {
    
    
                    if(i % 2 == 0){
    
    
                        System.out.println(Thread.currentThread().getName() + ":" + i);

                    }
                }
            }
        }.start();


        new Thread(){
    
    
            @Override
            public void run() {
    
    
                for (int i = 0; i < 100; i++) {
    
    
                    if(i % 2 != 0){
    
    
                        System.out.println(Thread.currentThread().getName() + ":" + i);

                    }
                }
            }
        }.start();

    }
}

class MyThread1 extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ":" + i);

            }
        }

    }
}


class MyThread2 extends Thread{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 != 0){
    
    
                System.out.println(Thread.currentThread().getName() + ":" + i);

            }
        }

    }
}

Insert image description here

1.9 Multi-thread creation 2: Implement Runnable interface

1.9.1 Overview

If your class has extended another class, you cannot inherit more. In this case, you can implement a Runnable interface. (The interface can be implemented in multiple ways and is more flexible)

The Runnable interface should be implemented by classes that intend to execute their instances through a thread. The class must define a parameterless method called run. (Because there is an abstract method run() in the interface, the subclass must override the abstract method of the parent class)

1.9.2 Common methods

//只有一个方法
void run() 
          使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。 

1.9.3 Testing

package atguigu.java;

/**
 * 创建多线程的方式二:实现Runnable接口
 * 1. 创建一个实现了Runnable接口的类
 * 2. 实现类去实现Runnable中的抽象方法:run()
 * 3. 创建实现类的对象
 * 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
 * 5. 通过Thread类的对象调用start()
 *
 *
 * 比较创建线程的两种方式。
 * 开发中:优先选择:实现Runnable接口的方式
 * 原因:1. 实现的方式没有类的单继承性的局限性
 *      2. 实现的方式更适合来处理多个线程有共享数据的情况。
 *
 * 联系:public class Thread implements Runnable
 * 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
 *
 * @author shkstart
 * @create 2019-02-13 下午 4:34
 */
//1. 创建一个实现了Runnable接口的类
class MThread implements Runnable{
    
    

    //2. 实现类去实现Runnable中的抽象方法:run()
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }

        }
    }
}


public class ThreadTest1 {
    
    
    public static void main(String[] args) {
    
    
        //3. 创建实现类的对象
        MThread mThread = new MThread();
        //4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(mThread);
        t1.setName("线程1");
        //5. 通过Thread类的对象调用start():① 启动线程 ②调用当前线程的run()-->调用了Runnable类型的target的run()
        t1.start();

        //再启动一个线程,遍历100以内的偶数
        Thread t2 = new Thread(mThread);
        t2.setName("线程2");
        t2.start();
    }

}

Note: You can see that the order of execution is chaotic. We already know that the start() method only notifies the operating system that the thread is ready. The operating system decides when to execute it, and our JVM has no control over it. This is the reason for the disorder, and it is normal.

1.10 Thread safety issues in ticket sales cases

Design 4 ticket windows to sell a total of 100 tickets.
Design and write code using multi-threaded programming.

1.10.1 Option 1: Inherit Thread

package cn.teud.threaddemo;

		//测试  卖票:设计4个售票窗口,总计售票100张。
		public class Test4_Tickets {
    
    
		    public static void main(String[] args) {
    
    
		        //4,测试
		        //问题1: 现在4个线程,卖了400张票 ?? --
		        //原因:tickets是成员变量也叫实例变量,只要创建了实例,每个实例就都会拥有tickets资源,创建了4个实例,就会拥有了4个tickets
		        //解决:如果能够把tickets作为一个全局唯一的全局共享的资源,从头到尾只会存在一次的资源,那么需要被static修饰.
		        MyTickets t = new MyTickets();
		        MyTickets t2 = new MyTickets();
		        MyTickets t3 = new MyTickets();
		        MyTickets t4 = new MyTickets();
		        t.setName("1号窗口");
		        t2.setName("2号窗口");
		        t3.setName("3号窗口");
		        t4.setName("4号窗口");
		        //5,开启线程
		        t.start();
		        t2.start();
		        t3.start();
		        t4.start();
		    }
		}
		//模拟多线程售票:extends Thread
		class MyTickets extends Thread {
    
    
		    //1,定义变量,记录票数
		    static int tickets = 100;//因为tickets资源会被每个对象拥有一次,不符合需求.想要在多个对象间共享一个tickets资源,所以static
		    //2,开始卖票 -- 业务放在重写的run()里
		    @Override
		    public void run() {
    
    
		        //super.run();
		        //3,一直卖票
		        while (true) {
    
    
//1.假设tickets=1 t1 t2 t3 t4四个人都瞒住了判断条件,进去卖票。(谁睡醒没法控制,产生原因及一个人干活后数据还没来得及恢复就被另一个让人拿到了。)
		            if (tickets > 0) {
    
    //有票就可以卖票
		                //6,让程序休息一下,如果数据仍然是正确的,那么这个多线程程序才是完美的!!88888888888888(即:静态方法 sleep())
		                try {
    
    
		                    //问题2: 超卖: 程序卖出了  -1  0  -2

		                    //问题3: 重卖: 程序把一张票卖给了多个人
		                    //2.原因??? t1  t2  t3  t4四个人都睡着了。
		                    Thread.sleep(10);//让程序休眠10ms
		                } catch (InterruptedException e) {
    
    
		                    e.printStackTrace();
		                }
// 问题2超卖产生的原因?
//假t1醒了,现在tickets=1,开始执行tickets--,输出1,tickets自减变为0
 //t1刚变完,t3醒了,现在tickets=0,开始执行tickets--,输出0,tickets自减变为-1.
// t3刚变完,t4醒了,现在tickets=-1,开始执行tickets--,输出-1,tickets自减变为-2.
// t4刚变完,t2醒了,现在tickets=2,开始执行tickets--,输出-2,tickets自减变为-3.
//问题三重买产生的原因? (与超卖的原因几乎一样,因为过程不可控,不知道谁先醒,回去抢占资源)
//假t1醒了,现在tickets=73,开始执行tickets--,输出73,tickets自减变为72,还没来得及改变。
 // t3醒了,现在tickets=73,开始执行tickets--,输出73,tickets自减变为-72. 还没来得及改变。
//t4醒了,现在tickets=-73,开始执行tickets--,输出-73,tickets自减变为72. 还没来得及改变。
// t4刚变完,t2醒了,现在tickets=72,开始执行tickets--,输出72,tickets自减变为71.


		                System.out.println( getName() + "=" + tickets--);
		            } else {
    
     //没票就结束
		                break; //死循环的出口!!
		            }
		        }
		    }
		}

1.10.2 Option 2: Implement Runnable

package cn.teud.threaddemo;
		//测试  卖票:设计4个售票窗口,总计售票100张。
		public class Test5_Tickets2 {
    
    
		    public static void main(String[] args) {
    
    
		        MyTickets2  target = new MyTickets2();
		        //怎么把目标对象和Thread对象绑定关系
		        Thread t = new Thread(target) ;
		        Thread t2 = new Thread(target) ;
		        Thread t3 = new Thread(target) ;
		        Thread t4 = new Thread(target) ;
		        //启动线程
		        t.start();
		        t2.start();
		        t3.start();
		        t4.start();
		    }
		}
		//模拟多线程售票:implements Runnable
		class MyTickets2 implements Runnable {
    
    
		    //1,定义变量,记录票数
		    /*这个地方没有用static为啥卖的还是100张票而不是400张票。因为实现接口,虽然是开了四个窗口,
		    但是执行的任务都是同一个target对象,只new了一次所以从始至终卖的是100张票。不用修饰static
		    修饰也是一个对象.*/
		    int tickets = 100; 
		    

		    //2, 把卖票业务放入重写的run()
		    @Override
		    public void run() {
    
    
		        while (true) {
    
    //一直卖票
		            if (tickets>0) {
    
    
		                //一定要让程序睡一会儿,来检查数据是否安全 !!!
		                try {
    
    
		                    //问题1:超卖:程序卖出了0  -1  -2号票
		                    //问题2:重卖:程序把一张票卖给了多个人
		                    //原因???  解决方案????
		                    Thread.sleep(10);
		                } catch (InterruptedException e) {
    
    
		                    e.printStackTrace();
		                }
		                //3,获取线程名Thread.currentThread().getName()
		                System.out.println(Thread.currentThread().getName()+"="+tickets--);
		            }else{
    
    
		                break ;//死循环的出口!!!
		            }
		        }
		    }
		}

1.10.3 Thread safety issues

  1. Every time a thread object is created, a tickets variable with a value of 100 will be generated. If the object is created 4 times, 400 tickets will be generated. If it doesn’t meet the requirements, how to solve it? Can the tickets variable be shared between each object to ensure that as many objects as possible can sell these 100 tickets?
    Solution: Use static modification or create a thread in the second way.
  2. Oversold occurs, -1, -2. - -Multi-thread security issues
  3. Resale occurs, and the same ticket is sold to multiple people. - -Multi-thread security issues
  4. How do multi-thread safety issues arise ?
    When a thread is operating a ticket and the operation is not completed, other threads participate and also operate the ticket.
    A common situation is due to randomness of threads + access latency (Coupled with delays, the probability of errors becomes higher.)。
  5. How to determine whether a program has thread safety issues in the future ? - - In a multi-threaded program + there is shared data + multiple statements operate on the shared data.
  6. How to solve thread safety problem? - - Synchronization lock, Lock lock (new feature of jdk1.5)
    Explanation: When a thread a is operating a ticket, other threads cannot participate. Until thread a finishes operating the ticket, other threads can start operating the ticket. In this case, even if thread a is blocked, it cannot be changed.

1.10.4 Extensions

1 Is JVM startup single-threaded or multi-threaded?
For multi-threading, at least the main thread and GC thread must be started.

2. Daemon
thread.

2 Day17 – Multi-threading 02 + synchronization lock

2.1 Synchronization lock solves thread safety issues: 01

Wrap potentially problematic code so that only one thread can execute it at a time. Synchronization is achieved through the sychronized keyword.
When multiple objects operate on shared data, synchronization locks can be used to solve thread safety issues.

  1. In current multi-threaded programming, resource preemption occurs, causing data security risks.
  2. It would be nice if you could lock the resource. Whoever has the key can use the key to open the lock and use the resources.
  3. Synchronization locks are used to lock shared resources. It is guaranteed that shared resources are exclusive and no one can grab them at the same time.
  4. Synchronization locks essentially sacrifice efficiency to ensure the security of shared resources.
  5. The difference between synchronous and asynchronous:
    Synchronous: means that only one person can operate data at the same time, and others can only wait in line.Sacrifice efficiency and improve safety. (StringBuffer is locked, so the efficiency is reduced and the security is improved.)
    Asynchronous: It means that no one is queuing up at the same time, and everyone rushes to grab it together.Increased efficiency at the expense of safety
  6. Use the synchronized keyword to achieve the effect of synchronization lock.
  7. The difference between concurrency and parallelism:
    Concurrency: A CPU executes multiple programs at the same time.
    Parallelism: There are multiple CPUs (computers now are multi-core: multiple CPUs), and one CPU executes one program.

Insert image description here

2.1.1 Synchronized syntax

Advantages and disadvantages of synchronization:

  1. Benefits: Solve the thread safety problem.
  2. When operating synchronous code, only one thread can participate, and other threads wait. It is equivalent to a single-threaded process and has low efficiency.
//方式一:同步代码块
synchronized(同步监视器/锁对象){
    
    //锁的代码块,需要指定锁对象,可以是任意对象,但是必须是同一个对象。
    *      //需要被同步的代码
   
}
说明:1.操作共享数据的代码,即为需要被同步的代码。  -->不能包含代码多了,也不能包含代码少了。
      2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
      3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
        要求1:多个线程必须要共用同一把锁。
        要求2:同步需要两个或者两个以上的线程。

//方式二:同步方法
synchronized public void eat(){
    
     
 //需要被同步的代码
}
说明:1.如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
      2.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
      3.要求和方式一几乎相同,锁对象只有一个。

2.1.2 How to determine whether there are security risks associated with multi-thread concurrency during development?

– In a multi-threaded scenario + shared resources, operated by multiple statements >=2

2.1.3 Modification of ticketing case 01 (synchronous code block - ->Thread)

package com.thread;

/**
 * 使用同步代码块解决继承Thread类的方式的线程安全问题
 *
 * 说明:在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
 *
 */
public class Test1 {
    
    
    public static void main(String[] args) {
    
    
        //4,测试
        //问题1: 现在4个线程,卖了400张票 ?? --
        //原因:tickets是成员变量也叫实例变量,只要创建了实例,每个实例就都会拥有tickets资源,创建了4个实例,就会拥有了4个tickets
        //解决:如果能够把tickets作为一个全局唯一的全局共享的资源,从头到尾只会存在一次的资源,那么需要被static修饰.
        MyTickets t = new MyTickets();
        MyTickets t2 = new MyTickets();
        MyTickets t3 = new MyTickets();
        MyTickets t4 = new MyTickets();
        t.setName("1号窗口");
        t2.setName("2号窗口");
        t3.setName("3号窗口");
        t4.setName("4号窗口");
        //5,开启线程
        t.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//模拟多线程售票:extends Thread
class MyTickets extends Thread {
    
    
    //1,定义变量,记录票数
    static int tickets = 100;//因为tickets资源会被每个对象拥有一次,不符合需求.想要在多个对象间共享一个tickets资源,所以static

    //private static Object obj = new Object();//方式一的锁对象:加上static是因为,Thread方式造了多个对象,所以加上static保证锁对象只有一个

    //2,开始卖票 -- 业务放在重写的run()里
    @Override
    public void run() {
    
    
        //super.run();
        //3,一直卖票
        while (true) {
    
    

            /*锁位置找对合理的位置 + 锁对象是同一个对象 !!
            * 位置合适:如果while (true) {放在同步锁里面,则程序执行相当于每次执行一个线程,把票卖完后,值执行别的线程,
            * 即:一个人把票买完之后其它人才能买票,显然不合适。
            * */

           /*同步代码块--->extends Thread方式的锁对象:同步代码块锁对象任意
           * 方式一:private static Object obj = new Object();
           * 随便创建一个对象当做锁对象,因为以继承Thread的方式创建多个线程对象,所以把锁对象设置为静态的保证多个线程之间的锁对象是同一个。
           * 方式二(容易错误):因为每次自己new一个对象作为锁对象太麻烦,直接用this代表本类对象作为锁对象更简洁,但是因为继承Thread的方式是创建了多个
           *       线程对象,此时this的对象有:t,t2,t3,t4.不满足多个线程之间锁对象是同一个,所以这种方式错误。当然如果只有一个线程对象可以用。
           * 方式三:Class clazz = MyTickets.class,MyTickets.class只会加载一次,使用类对象的方式,类对象只会加载一次。
           * */
           // synchronized (obj){ 方式一的锁对象
           // synchronized (this){方式二的锁对象
            synchronized (MyTickets.class){
    
    

                if (tickets > 0) {
    
    //有票就可以卖票
                    //6,让程序休息一下,如果数据仍然是正确的,那么这个多线程程序才是完美的!!
                    try {
    
    
                        Thread.sleep(10);//让程序休眠10ms
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println( getName() + "=" + tickets--);
                } else {
    
     //没票就结束
                    break; //死循环的出口!!
                }
            }

        }
    }
}

2.1.4 Modification of ticketing case 02 (synchronous code block - -> Runnable)

package com.thread;


/**
 * 使用同步代码块解决实现 Runnable接口的方式的线程安全问题
 *
 *在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
 *
 */
public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        MyTickets2  target = new MyTickets2();
        //怎么把目标对象和Thread对象绑定关系
        Thread t1 = new Thread(target) ;
        Thread t2 = new Thread(target) ;
        Thread t3 = new Thread(target) ;
        Thread t4 = new Thread(target) ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//模拟多线程售票:implements Runnable
//3,目前程序中有安全隐患 -- 加锁来解决 -- 锁的位置(发生点到问题结束) -- 锁的对象(同一个)
class MyTickets2 implements Runnable {
    
    
    //1,定义变量,记录票数
    int tickets = 100;  //不加static 是因为Runnable天然只造了一个对象
    //Object obj = new Object();//方式一的锁对象:不加static 是因为Runnable天然只造了一个对象,注意这个锁对象要放在方法外面。
    //2, 把卖票业务放入重写的run()
    @Override
    public void run() {
    
    
       // Object obj = new Object();方式一的锁对象的错误方式:不能写在里面,使用的每次都是new出来的新对象,可没统一!!在里面创建对象相当于new了4次对象。
        while (true) {
    
    //一直卖票

            //4,锁位置:从问题起点开始--同步代码块,需要同时指定锁对象,可以是任意对象,但是必须是同一个。
            //5,加锁后,多个线程来访问这段资源,都得排队访问不再抢着干了。提高了安全,牺牲了效率。

            /*
            * 同步代码块--->implements Runnable方式的锁对象:同步代码块锁对象任意
            * 方式一:随便创建一个对象当做锁对象,因为以implements Runnable的方式创建天然只有一个线程对象,四个线程来了,使用的都是new出来的Object同一个对象
            * 所以锁对象不用设置为static静态的,但是要注意不能写在run()方法里面,在里面写相当于每次调用方法使用的每次都是new出来的新对象,不符合多个线程锁对象共享。
            * 方式二:因为每次自己new一个对象作为锁对象太麻烦,直接用this代表本类对象作为锁对象更简洁,因为implements Runnable的方式是创建了一个线程对象,所以
            * this只代表一个本类对象的引用target,符合规范。
            * */
            //synchronized (obj){方式一的锁对象  synchronized (new Object())直接new也是方式一的锁对象的错误方式
            synchronized (this){
    
    //方式二的锁对象:四个线程来了,使用的都本类的对象this

                if (tickets>0) {
    
    
                    try {
    
    
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"="+tickets--);
                }else{
    
    
                    break ;//死循环的出口!!!
                }
            }

        }
    }
}

2.1.5 Transformation of Ticketing Case 03 (Synchronization Method - ->Thread)

package com.thread;


/**
 * 使用同步方法解决继承Thread类的方式的线程安全问题
 *
 *关于同步方法的总结:
 * 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
 * 2. 非静态的同步方法,同步监视器是:this
 *    静态的同步方法,同步监视器是:当前类本身
 */
public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        //4,测试
        //问题1: 现在4个线程,卖了400张票 ?? --
        //原因:tickets是成员变量也叫实例变量,只要创建了实例,每个实例就都会拥有tickets资源,创建了4个实例,就会拥有了4个tickets
        //解决:如果能够把tickets作为一个全局唯一的全局共享的资源,从头到尾只会存在一次的资源,那么需要被static修饰.
        MyTickets3 t = new MyTickets3();
        MyTickets3 t2 = new MyTickets3();
        MyTickets3 t3 = new MyTickets3();
        MyTickets3 t4 = new MyTickets3();
        t.setName("1号窗口");
        t2.setName("2号窗口");
        t3.setName("3号窗口");
        t4.setName("4号窗口");
        //5,开启线程
        t.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//模拟多线程售票:extends Thread
class MyTickets3 extends Thread {
    
    
    //1,定义变量,记录票数
    static int tickets = 100;//因为tickets资源会被每个对象拥有一次,不符合需求.想要在多个对象间共享一个tickets资源,所以static



    //2,开始卖票 -- 业务放在重写的run()里
    @Override
    public void run() {
    
    
        //super.run();
        //3,一直卖票
        while (true) {
    
    
            show();


        }
    }
    //private synchronized void show(){ //同步监视器:t,t2,t3,t4。此种解决方式是错误的,有线程安全问题,锁对象不共享。
    private static synchronized void show(){
    
    同步监视器:MyTickets3.class       改为静态的方法,锁对象共享。
        if (tickets > 0) {
    
    //有票就可以卖票
            //6,让程序休息一下,如果数据仍然是正确的,那么这个多线程程序才是完美的!!
            try {
    
    
                Thread.sleep(10);//让程序休眠10ms
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println( Thread.currentThread().getName() + "=" + tickets--);
        }
    }
}

2.1.6 Transformation of Ticketing Case 04 (Synchronization Method - -> Runnable)

package com.thread;

/**
 * 使用同步方法解决实现 Runnable接口的方式的线程安全问题
 *
 *说明:如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
 *    此案例需要修改代码。
 *
 */


public class Test4 {
    
    
    public static void main(String[] args) {
    
    
        MyTickets4  target = new MyTickets4();
        //怎么把目标对象和Thread对象绑定关系
        Thread t1 = new Thread(target) ;
        Thread t2 = new Thread(target) ;
        Thread t3 = new Thread(target) ;
        Thread t4 = new Thread(target) ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//模拟多线程售票:implements Runnable
//3,目前程序中有安全隐患 -- 加锁来解决 -- 锁的位置(发生点到问题结束) -- 锁的对象(同一个)
class MyTickets4 implements Runnable {
    
    
    //1,定义变量,记录票数
    int tickets = 100;  //不加static 是因为Runnable天然只造了一个对象

    //2, 把卖票业务放入重写的run()
    @Override
    public void run() {
    
    

        while (true) {
    
    //一直卖票

            show();

        }
    }

    public  synchronized void show(){
    
    //同步监视器:this
        //synchronized (this){还是同步代码块的方式

        if (tickets>0) {
    
    
            try {
    
    
                Thread.sleep(10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"="+tickets--);
        }
        //}
    }
}

2.2 Singleton design pattern

2.2.1 Solving the problem of lazy thread safety

package com.thread;

class Single{
    
    
    //	1、私有化构造方法,不让外界直接new
    private Single() {
    
    }

    //	2、在类的内部,创建好对象
    //static :静态只能调用静态
    static private  Single s = null;

    //	3、对外界提供一个公共的get(),返回一个已经准备好的对象
//static是为了外界不通过对象访问而是通过类名直接方法
    static public Single get(){
    
    
   // static synchronized public Single get(){//1.同步方法

       /* 方式一:效率低,因为要求只创建一个对象,现在执行顺序是:第一次第一个线程进来,判断对象为null后创建好对象再返回对象。
        *之后别的线程也要一个个的进来判断不为空后再返回,每次线程来都要进行判断,效率低。正确做法应该是第一个线程首次创建好对象后
        * 挂个牌子已经创建好一个对象了,别的线程不用在进行判断是否有对象而是直接返回对象即可。
        *
        * */
//        synchronized (Single.class) {//2.同步代码块
//            if(s==null){
    
    
//                /*会有线程安全问题:假如现在多个线程,每个线程调用run方法,各个run方法有调用这个get方法。
//                当第一个线程进入到这个get方法后,第一次进入对象为空,此时可能发生阻塞第二个线程进来判断为空
//                也需要创建对象。这样就创建了2个对象不合理。s相当于共享数据。
//                */
//                s = new Single();
//            }
//            return s;//是null说明还没有创建对象,不是null说明创建好了对象直接返回,可以保证只创建一次对象。
//        }
        //方式二:效率稍高
        if(s == null){
    
    
            synchronized (Single.class) {
    
    
                if(s==null){
    
    
                    s = new Single();
                }
            }
        }
        return s;

    }
}

2.3 Thread deadlock problem

Insert image description here

2.3.1 Case test

package com.atguigu.java1;

/**
 * 演示线程的死锁问题
 *
 * 1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
 * 都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
 *
 * 2.说明:
 * 1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
 * 2)我们使用同步时,要避免出现死锁。
 *
 * @author shkstart
 * @create 2019-02-15 下午 3:20
 */
public class ThreadTest {
    
    

    public static void main(String[] args) {
    
    

        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();


        new Thread(){
    
    
            @Override
            public void run() {
    
    

                synchronized (s1){
    
    

                    s1.append("a");
                    s2.append("1");

                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }

                   //线程安全问题:拿着s1等着执行s2
                    synchronized (s2){
    
    
                        s1.append("b");
                        s2.append("2");

                        System.out.println(s1);
                        System.out.println(s2);
                    }


                }

            }
        }.start();


        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                synchronized (s2){
    
    

                    s1.append("c");
                    s2.append("3");

                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
					 //线程安全问题:拿着s2等着执行s1
                    synchronized (s1){
    
    
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }


                }



            }
        }).start();


    }


}

2.4 Lock solves thread safety issues: 02

JDK5.0 new

2.4.1 Lock lock modification ticket sales case

package com.atguigu.java1;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 解决线程安全问题的方式三:Lock锁  --- JDK5.0新增
 *
 * 1. 面试题:synchronized 与 Lock的异同?
 *   相同:二者都可以解决线程安全问题
 *   不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
 *        Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
 *
 * 2.优先使用顺序:
 * Lock  同步代码块(已经进入了方法体,分配了相应资源)  同步方法(在方法体之外)
 *
 *
 *  面试题:如何解决线程安全问题?有几种方式
 * @author shkstart
 * @create 2019-02-15 下午 3:38
 */
class Window implements Runnable{
    
    

    private int ticket = 100;
    //1.实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        while(true){
    
    
            try{
    
    

                //2.调用锁定方法lock(),把代码放在try-catch-finally中,每页异常所以不用写catch.
                lock.lock();

                if(ticket > 0){
    
    

                    try {
    
    
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
                    ticket--;
                }else{
    
    
                    break;
                }
            }finally {
    
    
                //3.调用解锁方法:unlock()
                lock.unlock();
            }

        }
    }
}

public class LockTest {
    
    
    public static void main(String[] args) {
    
    
        Window w = new Window();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

2.5 Thread communication issues

2.5.1 Getting started: wait(), notify(), notifyAll

package com.atguigu.java2;

/**
 * 线程通信的例子:使用两个线程打印 1-100。线程1, 线程2 交替打印
 *
 * 涉及到的三个方法:
 * wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
 * notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
 * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
 *
 * 说明:
 * 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
 * 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
 *    否则,会出现IllegalMonitorStateException异常
 * 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
 *
 * 面试题:sleep() 和 wait()的异同?
 * 1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
 * 2.不同点:1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
 *          2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
 *          3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
 *
 * @author shkstart
 * @create 2019-02-15 下午 4:21
 */
class Number implements Runnable{
    
    
    private int number = 1;
    private Object obj = new Object();
    @Override
    public void run() {
    
    

        while(true){
    
    

            synchronized (obj) {
    
    
	   /*
        * 程序执行过程:
        * 假如线程1先执行进入方法中,因为没有wait阻塞的方法,此时obj.notify();只是执行下而已,之后打印线程1并进入到阻塞。
        * 线程2进来首先唤醒线程1,因为此时同步锁对象是线程2拿着的即便唤醒线程1,线程1也进来,之后打印线程2,线程2进入到阻塞。
        * 之后线程1在进来唤醒线程2,此时线程1拿着锁对象线程2唤醒也进不来,打印线程1,线程1进入到阻塞状态。
        * 之后线程2进来唤醒线程1.......直到打印完成。
        *
        * */
                obj.notify();

                if(number <= 100){
    
    

                    try {
    
    
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;

                    try {
    
    
                        //使得调用如下wait()方法的线程进入阻塞状态
                        obj.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }

                }else{
    
    
                    break;
                }
            }

        }

    }
}


public class CommunicationTest {
    
    
    public static void main(String[] args) {
    
    
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("线程1");
        t2.setName("线程2");

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

2.5.2 Classic example: producer/consumer problem

package com.atguigu.java2;

/**
 * 线程通信的应用:经典例题:生产者/消费者问题
 *
 * 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
 * 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员
 * 会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品
 * 了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
 *
 * 分析:
 * 1. 是否是多线程问题?是,生产者线程,消费者线程
 * 2. 是否有共享数据?是,店员(或产品)
 * 3. 如何解决线程的安全问题?同步机制,有三种方法
 * 4. 是否涉及线程的通信?是
 *
 * @author shkstart
 * @create 2019-02-15 下午 4:48
 */
class Clerk{
    
    

    private int productCount = 0;
    //生产产品
    public synchronized void produceProduct() {
    
    

        if(productCount < 20){
    
    
            productCount++;
            System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");

            notify();

        }else{
    
    
            //等待
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }

    }
    //消费产品
    public synchronized void consumeProduct() {
    
    
        if(productCount > 0){
    
    
            System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品");
            productCount--;

            notify();
        }else{
    
    
            //等待
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }

    }
}

class Producer extends Thread{
    
    //生产者

    private Clerk clerk;

    public Producer(Clerk clerk) {
    
    
        this.clerk = clerk;
    }

    @Override
    public void run() {
    
    
        System.out.println(getName() + ":开始生产产品.....");

        while(true){
    
    

            try {
    
    
                Thread.sleep(10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            clerk.produceProduct();
        }

    }
}

class Consumer extends Thread{
    
    //消费者
    private Clerk clerk;

    public Consumer(Clerk clerk) {
    
    
        this.clerk = clerk;
    }

    @Override
    public void run() {
    
    
        System.out.println(getName() + ":开始消费产品.....");

        while(true){
    
    

            try {
    
    
                Thread.sleep(20);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            clerk.consumeProduct();
        }
    }
}

public class ProductTest {
    
    

    public static void main(String[] args) {
    
    
        Clerk clerk = new Clerk();

        Producer p1 = new Producer(clerk);
        p1.setName("生产者1");

        Consumer c1 = new Consumer(clerk);
        c1.setName("消费者1");
        Consumer c2 = new Consumer(clerk);
        c2.setName("消费者2");

        p1.start();
        c1.start();
        c2.start();

    }
}

2.6 Multi-thread creation 3: Implementing the Callable interface

JDK5.0 new
Insert image description here
Insert image description here

package com.atguigu.java2;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 创建线程的方式三:实现Callable接口。 --- JDK 5.0新增
 *
 *
 * 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
 * 1. call()可以有返回值的。
 * 2. call()可以抛出异常,被外面的操作捕获,获取异常的信息
 * 3. Callable是支持泛型的
 *
 * @author shkstart
 * @create 2019-02-15 下午 6:01
 */
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
    
    
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
    
    
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    
    
    public static void main(String[] args) {
    
    
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();
        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);
        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
    
    
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            //get方法只是获取call方法的返回值,如果你不感兴趣或者下面执行的代码中也用不到,则不用写这一步。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }
    }

}

2.7 Multi-thread creation 4: Use thread pool (most commonly used in development)

JDK5.0 new
Most writing thread pools in development are implemented through frameworks. Generally, there is no need to write a thread pool yourself.
Insert image description here
Insert image description here

package com.atguigu.java2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 创建线程的方式四:使用线程池
 *
 * 好处:
 * 1.提高响应速度(减少了创建新线程的时间)
 * 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 * 3.便于线程管理
 *      corePoolSize:核心池的大小
 *      maximumPoolSize:最大线程数
 *      keepAliveTime:线程没有任务时最多保持多长时间后会终止
 *
 *
 * 面试题:创建多线程有几种方式?四种!
 * @author shkstart
 * @create 2019-02-15 下午 6:30
 */

class NumberThread implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for(int i = 0;i <= 100;i++){
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for(int i = 0;i <= 100;i++){
    
    
            if(i % 2 != 0){
    
    
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {
    
    

    public static void main(String[] args) {
    
    
        //1. 提供指定线程数量的线程池 ExecutorService是接口,Executors.newFixedThreadPool(10)这个工具类是
        //为了创建对象,因为接口不能实例化,所以这个工具类的返回值一定是接口的子类对象,多态形式。
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性:
        /*直接通过service无法调用:
         *因为接口中的属性比较少,而且变量都是常量不允许修改,所以先要设置属性一定是ExecutorService接口的子类中设置。
         *而Executors.newFixedThreadPool(10);的返回值恰好是子类对象,因为接收直接用的是多态形式看不到这个子类
         *对象是谁,那么这个实现类对象如何看到呢????
         * service.getClass():获取这个对象到底是那个类造的。此工具类返回的对象是ThreadPoolExecutor类造的,
         * 因为是通过多态父类接收的,想调用子类的属性,可以通过强转实现。即service1调用。
         */
//        System.out.println(service.getClass()); //ThreadPoolExecutor
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();
//...... 查Api即可

        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }

}

2.8 Comparison of four thread creation methods

Way advantage shortcoming
Thread It is simple to write. If you need to access the current thread, you do not need to use the Thread.currentThread() method. You can get the current thread directly by using this. The thread class has inherited the Thread class, so it cannot inherit other parent classes.
Runnable The thread class only implements the Runnable interface or the Callable interface, and can also inherit other classes. In this way, multiple threads can share the same target object, so it is very suitable for multiple identical threads to process the same resource, so that the CPU, code and data can be separated to form a clear model and better reflect Object-oriented thinking. Programming is slightly more complicated, if you want to access the current thread, you must use the Thread.currentThread() method.
Callable Runnable stipulates (the overridden method is run(), and Callable stipulates (overrides) the method call(). A Callable task can return a value after execution, but a Runnable task cannot return a value. The Call method can throw an exception , the run method is not allowed. Running the Callable task can get a Future object, which represents the result of asynchronous calculation. Slow access to other items
Pool The thread pool can be created with a fixed size, so there is no need to repeatedly create thread objects. Threads are relatively resource-consuming and threads will not be created unbounded all the time, slowing down the system. Programming is cumbersome and difficult to understand

3 enumeration class + annotation

3.1 Concept of enumeration class

Insert image description here
Insert image description here

3.2 Two ways to create enumeration classes

3.2.1 Custom enumeration class

package com.atguigu.java;

/**
 * 一、枚举类的使用
 * 1.枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类
 * 2.当需要定义一组常量时,强烈建议使用枚举类(枚举类里面是有限个对象,对象是常量)
 * 3.如果枚举类中只有一个对象,则可以作为单例模式的实现方式。
 *
 * 二、如何定义枚举类
 * 方式一:jdk5.0之前,自定义枚举类
 * 方式二:jdk5.0,可以使用enum关键字定义枚举类
 * @author shkstart
 * @create 2019 上午 10:17
 */
public class SeasonTest {
    
    

    public static void main(String[] args) {
    
    
        Season spring = Season.SPRING;//通过类名调用
        System.out.println(spring);//Season{seasonName='春天', seasonDesc='春暖花开'},默认继承Object 不重写toString()输出的是地址值

    }

}
//方式一:自定义枚举类
class Season{
    
    
    //1.声明Season对象的属性:private final修饰
    private final String seasonName;
    private final String seasonDesc;

    //2.私有化类的构造器,并给对象属性赋值
    private Season(String seasonName,String seasonDesc){
    
    //常量在构造器中赋值,是可以改变的。在直接量和构造代码块中不能修改。
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //3.提供当前枚举类的多个对象:public static final的
    public static final Season SPRING = new Season("春天","春暖花开");
    public static final Season SUMMER = new Season("夏天","夏日炎炎");
    public static final Season AUTUMN = new Season("秋天","秋高气爽");
    public static final Season WINTER = new Season("冬天","冰天雪地");

    //4.其他诉求1:获取枚举类对象的属性    get,set快捷键:Alt+insert
    public String getSeasonName() {
    
    
        return seasonName;
    }

    public String getSeasonDesc() {
    
    
        return seasonDesc;
    }
    //5.其他诉求2:提供toString()
    @Override
    public String toString() {
    
    
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

3.2.2 Use the enum keyword to define an enumeration class

package com.atguigu.java;

/**
 * 方式二:使用enum关键字定义枚举类
 * 说明:定义的枚举类默认继承于java.lang.Enum类,如果不重写toString()打印的是对象名而不是地址值。
 *     继承Object里才是地址值。
 *
 * Enum类中的常用方法:
 *    values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。
 *    valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异  *                                                                     常:IllegalArgumentException。
 *    toString():返回当前枚举类对象常量的名称
 * 
 * @author shkstart
 * @create 2019 上午 10:35
 */
public class SeasonTest1 {
    
    
    public static void main(String[] args) {
    
    
        Season1 summer = Season1.SUMMER;
        //toString():返回枚举类对象的名称
        System.out.println(summer.toString());//SUMMER,因为继承的是父类是Enum,如果没重写toString输出的是对象名,如果父类是Object没重写则是地址值
        System.out.println(Season1.class.getSuperclass());//查看当前类的父类,class java.lang.Enum

        System.out.println("****************");

        //values():返回所有的枚举类对象构成的数组
        Season1[] values = Season1.values();
        for(int i = 0;i < values.length;i++){
    
    
            System.out.println(values[i]);//SPRING,SUMMER,AUTUMNWINTER

        }
        System.out.println("****************");
        Thread.State[] values1 = Thread.State.values();
        for (int i = 0; i < values1.length; i++) {
    
    
            System.out.println(values1[i]);//查看当前线程的状态
        }

        System.out.println("****************");

        //valueOf(String objName):返回枚举类中对象名是objName的对象。
        Season1 winter = Season1.valueOf("WINTER");
        //如果没有objName的枚举类对象,则抛异常:IllegalArgumentException
        // Season1 winter = Season1.valueOf("WINTER1");
        System.out.println(winter);
    }
}


//使用enum关键字枚举类
enum Season1{
    
    



    /*1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
    * 把前面的修饰符,对象名,等号,new对象 如果一样需要删除,否则报错:
    *                    public static final Season1 SPRING = new Season1("春天","春暖花开");---》SPRING ("春天","春暖花开"),
    *                    public static final Season SUMMER = new Season("夏天","夏日炎炎");---> SUMMER ("夏天","夏日炎炎"),
    *                    public static final Season AUTUMN = new Season("秋天","秋高气爽");--> AUTUMN ("秋天","秋高气爽"),
    *                    public static final Season WINTER = new Season("冬天","冰天雪地");--->WINTER ("冬天","冰天雪地");
    *
    */


     SPRING ("春天","春暖花开"),
     SUMMER ("夏天","夏日炎炎"),
     AUTUMN ("秋天","秋高气爽"),
     WINTER ("冬天","冰天雪地");


    //2.声明Season对象的属性:private final修饰
    private final String seasonName;
    private final String seasonDesc;

    //3.私有化类的构造器,并给对象属性赋值
    private Season1(String seasonName,String seasonDesc){
    
    //常量在构造器中赋值,是可以改变的。在直接量和构造代码块中不能修改。
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }



    //4.其他诉求1:获取枚举类对象的属性    get,set快捷键:Alt+insert
    public String getSeasonName() {
    
    
        return seasonName;
    }

    public String getSeasonDesc() {
    
    
        return seasonDesc;
    }
    //5.其他诉求2:提供toString()
   /* @Override
    public String toString() {
        return "Season1{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }*/
}


3.3 Situation when custom enumeration class implements interface

3.3.1 Custom enumeration class implementation interface

Oddly, if there is an abstract method to implement the interface, you can just rewrite it.

3.3.2 Use the enum keyword to define the enumeration class implementation interface

package com.atguigu.java;

/**
 * 使用enum关键字定义的枚举类实现接口的情况:
 *     情况一:实现接口,在enum类中实现抽象方法,每个对象调这个方法时输出的内容一样。
 *     情况二:让枚举类的对象分别实现接口中的抽象方法,每个对象调这个方法时输出的内容不一样。
 * @author shkstart
 * @create 2019 上午 10:35
 */
public class SeasonTest1 {
    
    
    public static void main(String[] args) {
    
    
        Season1 summer = Season1.SUMMER;
        //toString():返回枚举类对象的名称
        System.out.println(summer.toString());//SUMMER,因为继承的是父类是Enum,如果没重写toString输出的是对象名,如果父类是Object没重写则是地址值
        System.out.println(Season1.class.getSuperclass());//查看当前类的父类,class java.lang.Enum

        System.out.println("****************");

        //values():返回所有的枚举类对象构成的数组
        Season1[] values = Season1.values();
        for(int i = 0;i < values.length;i++){
    
    
            System.out.println(values[i]);//SPRING,SUMMER,AUTUMNWINTER
            values[i].show();
        }
        System.out.println("****************");
        Thread.State[] values1 = Thread.State.values();
        for (int i = 0; i < values1.length; i++) {
    
    
            System.out.println(values1[i]);//查看当前线程的状态
        }

        System.out.println("****************");

        //valueOf(String objName):返回枚举类中对象名是objName的对象。
        Season1 winter = Season1.valueOf("WINTER");
        //如果没有objName的枚举类对象,则抛异常:IllegalArgumentException
        // Season1 winter = Season1.valueOf("WINTER1");
        System.out.println(winter);
        winter.show();
    }
}

//定义接口
interface Info{
    
    
    void show();
}


//使用enum关键字枚举类
enum Season1 implements Info{
    
    



    /*1.提供当前枚举类的对象,多个对象之间用","隔开,末尾对象";"结束
    * 把前面的修饰符,对象名,等号,new对象 如果一样需要删除,否则报错:
    *                    public static final Season1 SPRING = new Season1("春天","春暖花开");---》SPRING ("春天","春暖花开"),
    *                    public static final Season SUMMER = new Season("夏天","夏日炎炎");---> SUMMER ("夏天","夏日炎炎"),
    *                    public static final Season AUTUMN = new Season("秋天","秋高气爽");--> AUTUMN ("秋天","秋高气爽"),
    *                    public static final Season WINTER = new Season("冬天","冰天雪地");--->WINTER ("冬天","冰天雪地");
    *
    */


     /*情况一:
     SPRING ("春天","春暖花开"),
     SUMMER ("夏天","夏日炎炎"),
     AUTUMN ("秋天","秋高气爽"),
     WINTER ("冬天","冰天雪地");*/
    //情况二:每个对象都重写show方法
     SPRING ("春天","春暖花开"){
    
    
         @Override
         public void show() {
    
    
             System.out.println("春天在哪里?");
         }
     },
     SUMMER ("夏天","夏日炎炎"){
    
    
         @Override
         public void show() {
    
    
             System.out.println("夏天在哪里?");
         }
     },
     AUTUMN ("秋天","秋高气爽"){
    
    
         @Override
         public void show() {
    
    
             System.out.println("秋天在哪里?");
         }
     },
     WINTER ("冬天","冰天雪地"){
    
    
         @Override
         public void show() {
    
    
             System.out.println("冬天在哪里?");
         }
     };


    //2.声明Season对象的属性:private final修饰
    private final String seasonName;
    private final String seasonDesc;

    //3.私有化类的构造器,并给对象属性赋值
    private Season1(String seasonName,String seasonDesc){
    
    //常量在构造器中赋值,是可以改变的。在直接量和构造代码块中不能修改。
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }



    //4.其他诉求1:获取枚举类对象的属性    get,set快捷键:Alt+insert
    public String getSeasonName() {
    
    
        return seasonName;
    }

    public String getSeasonDesc() {
    
    
        return seasonDesc;
    }


    //5.其他诉求2:提供toString()
   /* @Override
    public String toString() {
        return "Season1{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }*/

//    @Override//情况一:实现接口,在enum类中实现抽象方法,每个对象调这个方法时输出的内容一样
//    public void show() {
    
    
//
//    }
}


3.4 Annotations

Summary: In order to optimize the previous development model, annotation development is proposed. The amount of code is reduced and the reuse of annotations is improved. The flag is @Annotation

Insert image description here
Insert image description here

3.5 Examples of using annotations

3.5.1 Generate document-related annotations

Insert image description here

3.5.2 JDK built-in annotations

package com.atguigu.java1;

import java.util.ArrayList;
import java.util.Date;

/**
 * 注解的使用
 *
 * 1. 理解Annotation:
 * ① jdk 5.0 新增的功能
 *
 * ② Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation,
 * 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。
 *
 * ③在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android
 * 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗
 * 代码和XML配置等。
 *
 * 2. Annocation的使用示例
 * 示例一:生成文档相关的注解
 * 示例二:在编译时进行格式检查(JDK内置的三个基本注解)
 *      @Override: 限定重写父类方法, 该注解只能用于方法
 *      @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
 *      @SuppressWarnings: 抑制编译器警告
 *      @SafeVarargs jdk1.7出现,堆污染,不常用
 *      @FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
 *
 * 示例三:跟踪代码依赖性,实现替代配置文件功能
 * @author shkstart
 * @create 2019 上午 11:37
 */
public class AnnotationTest {
    
    

    public static void main(String[] args) {
    
    
        Person p = new Student();
        p.walk();

        Date date = new Date(2020, 10, 11);//已过时,但仍然能用,源码中用@Deprecated注解修饰了
        System.out.println(date);

        @SuppressWarnings("unused")//加上注解警告会消失,unused属性:没有用。  里面的值可以看成是成员变量
        int num = 10;//定义了一个变量没有用,在eclipse中会有一个警告:定义了变量没有用。在idea中变量名变为灰色。

        @SuppressWarnings({
    
     "unused", "rawtypes" })  //变量没有用,没有添加泛型
        ArrayList list = new ArrayList();



    }



}



class Person{
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

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

    public void walk(){
    
    
        System.out.println("人走路");
    }
    public void eat(){
    
    
        System.out.println("人吃饭");
    }
}

interface Info{
    
    
    void show();
}

class Student extends Person implements Info{
    
    

    @Override//不加上注解不一定不是重写,加上注解会在编译期检查是否为重写父类的方法 如果不是则报错。
    public void walk() {
    
    
        System.out.println("学生走路");
    }

    public void show() {
    
    

    }
}




3.5.3 Track code dependencies and implement alternative configuration file functions

Insert image description here
Insert image description here

3.6 Meta-annotations

3.6.1 Concept

Insert image description here

3.6.2 Testing

Add meta-annotations to custom annotations:

package com.atguigu.java1;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

/**
 * 1.如何自定义注解:参照@SuppressWarnings定义
 * ① 注解声明为:@interface
 * ② 内部定义成员,通常使用value表示
 * ③ 可以指定成员的默认值,使用default定义
 * ④ 如果自定义注解没有成员,表明是一个标识作用。public @interface MyAnnotation {},使用时也不需要写属性值
 *
 * 注意:
 * 如果注解有成员,在使用注解时,需要指明成员的值。
 * 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
 * 自定义注解通过都会指明两个元注解:Retention、Target
 *
 *  2.jdk 提供的4种元注解
 *        元注解:对现有的注解进行解释说明的注解
 *      Retention:指定所修饰的 Annotation 的生命周期:SOURCE(在源文件中有效)\CLASS(默认行为, 在class文件中有效)\RUNTIME(在运行时有效)
 *                                               只有声明为RUNTIME生命周期的注解,才能通过反射获取。
 *      Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素:如果不指定则代表那都可以用。
 *            ElementType.ANNOTATION_TYPE 		应用于注释类型
 *            ElementType.CONSTRUCTOR 			应用于构造函数
 *            ElementType.FIELD 				应用于字段或属性
 *            ElementType.LOCAL_VARIABLE 		应用于局部变量
 *            ElementType.METHOD 				应用于方法级
 *            ElementType.PACKAGE 				应用于包声明
 *            ElementType.PARAMETER 			应用于方法的参数
 *            ElementType.TYPE 					应用于类的元素
 *      *******出现的频率较低*******
 *      Documented:表示所修饰的注解在被javadoc解析时,保留下来。默认情况下,javadoc是不包括注解的。定义为Documented的注解必须设置Retention值为RUNTIME。
 *
 *      Inherited:被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。
 *                如何证明子类继承了注解--->通过反射获取注解信息 ---到反射内容时系统讲解
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER,TYPE_USE})
@Inherited
public @interface MyAnnotation {
    
    
    //如果只有一个参数成员,建议使用参数名为value。注意这是一个属性不是方法,虽然有()。
    //String value() ;//形式一:可以是任意类型的,如果value值可以指定多个可以定义为数组如:String[] value() ;
    String value() default "hello";//形式二:通过default关键字指定一个默认值
}



Usage notes:

package com.atguigu.java1;

public class AnnotationTest {
    
    

    public static void main(String[] args) {
    
    
        Person p = new Student();
        p.walk();
    }
}


//@MyAnnotation(value="hello")//形式一:使用注解,因为自定义注解@MyAnnotation有变量value,所以这里需要指定一个值 否则会报错。暂时随便指定个值,没有特殊含义.只有一个值可以简写为:@MyAnnotation("hello")
@MyAnnotation()//形式二:因为使用的value属性有默认值,此时不用指定,也不会报错。不想要默认值,可以进行修改如:@MyAnnotation(value="hi")
class Person{
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

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

    public void walk(){
    
    
        System.out.println("人走路");
    }
    public void eat(){
    
    
        System.out.println("人吃饭");
    }
}

interface Info{
    
    
    void show();
}

//student继承了person类,person添加了自定义注解@MyAnnotation(),又因为自定义注解设置了元注解@Inherited具有可继承性,所以
//student也有@MyAnnotation()注解。如何证明student继承了注解??---->通过反射
class Student extends Person implements Info{
    
    

    @Override
    public void walk() {
    
    
        System.out.println("学生走路");
    }

    public void show() {
    
    

    }
}




3.7 Custom annotations

3,7,1 Overview

Insert image description here

3.7.2 Testing

Definition annotation:

package com.atguigu.java1;

/**
 * 如何自定义注解:参照@SuppressWarnings定义
 * ① 注解声明为:@interface
 * ② 内部定义成员,通常使用value表示
 * ③ 可以指定成员的默认值,使用default定义
 * ④ 如果自定义注解没有成员,表明是一个标识作用。public @interface MyAnnotation {},使用时也不需要写属性值
 *
 * 注意:
 *1. 如果注解有成员,在使用注解时,需要指明成员的值。
 *2. 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
 *3. 自定义注解通过都会指明两个元注解:Retention、Target---详情查看3.6元注解
 * 
 */


public @interface MyAnnotation {
    
    
    //如果只有一个参数成员,建议使用参数名为value。注意这是一个属性不是方法,虽然有()。
    //String value() ;//形式一:可以是任意类型的,如果value值可以指定多个可以定义为数组如:String[] value() ;
    String value() default "hello";//形式二:通过default关键字指定一个默认值
}



Usage notes:

package com.atguigu.java1;

public class AnnotationTest {
    
    

    public static void main(String[] args) {
    
    
        Person p = new Student();
        p.walk();
    }
}


//@MyAnnotation(value="hello")//形式一:使用注解,因为自定义注解@MyAnnotation有变量value,所以这里需要指定一个值 否则会报错。暂时随便指定个值,没有特殊含义。只有一个值可以简写为:@MyAnnotation("hello")
@MyAnnotation()//形式二:因为使用的value属性有默认值,此时不用指定,也不会报错。不想要默认值,可以进行修改如:@MyAnnotation(value="hi")
class Person{
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

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

    public void walk(){
    
    
        System.out.println("人走路");
    }
    public void eat(){
    
    
        System.out.println("人吃饭");
    }
}

interface Info{
    
    
    void show();
}

class Student extends Person implements Info{
    
    

    @Override
    public void walk() {
    
    
        System.out.println("学生走路");
    }

    public void show() {
    
    

    }
}




3.8 New features of jdk1.8 annotations

3.8.1 Repeatable annotations and type annotations

Class AnnotationTest:

package com.atguigu.java1;

import java.util.ArrayList;

public class AnnotationTest {
    
    

    public static void main(String[] args) {
    
    
        Person p = new Student();
        p.walk();
    }
}


//@MyAnnotation(value="hello")//形式一:使用注解,因为自定义注解@MyAnnotation有变量value,所以这里需要指定一个值 否则会报错。暂时随便指定个值,没有特殊含义.只有一个值可以简写为:@MyAnnotation("hello")
//@MyAnnotation()//形式二:因为使用的value属性有默认值,此时不用指定,也不会报错。不想要默认值,可以进行修改如:@MyAnnotation(value="hi")

//jdk 8之前的写法想要使用重复的注解:想要写多个注解,在自定义注解MyAnnotations中声明为MyAnnotation类型的数组:MyAnnotation[] value();
//@MyAnnotations({@MyAnnotation(value="hi"),@MyAnnotation(value="hi")})
//jdk 8开始想要这样使用重复的注解:
@MyAnnotation(value="hi")
@MyAnnotation(value="abc")
class Person{
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

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

    public void walk(){
    
    
        System.out.println("人走路");
    }
    public void eat(){
    
    
        System.out.println("人吃饭");
    }
}

interface Info{
    
    
    void show();
}

//student继承了person类,person添加了自定义注解@MyAnnotation(),又因为自定义注解设置了元注解@Inherited具有可继承性,所以
//student也有@MyAnnotation()注解。如何证明student继承了注解??---->通过反射
class Student extends Person implements Info{
    
    

    @Override
    public void walk() {
    
    
        System.out.println("学生走路");
    }

    public void show() {
    
    

    }
}
//类型注解的使用:
class Generic<@MyAnnotation T>{
    
    

    public void show() throws @MyAnnotation RuntimeException{
    
    

        ArrayList<@MyAnnotation String> list = new ArrayList<>();

        int num = (@MyAnnotation int) 10L;
    }

}

AnnotationMyAnnotation:

package com.atguigu.java1;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;

/**
 * 1.如何自定义注解:参照@SuppressWarnings定义
 * ① 注解声明为:@interface
 * ② 内部定义成员,通常使用value表示
 * ③ 可以指定成员的默认值,使用default定义
 * ④ 如果自定义注解没有成员,表明是一个标识作用。public @interface MyAnnotation {},使用时也不需要写属性值
 *
 * 注意:
 * 如果注解有成员,在使用注解时,需要指明成员的值。
 * 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
 * 自定义注解通过都会指明两个元注解:Retention、Target
 *
 *  2.jdk 提供的4种元注解
 *        元注解:对现有的注解进行解释说明的注解
 *      Retention:指定所修饰的 Annotation 的生命周期:SOURCE(在源文件中有效)\CLASS(默认行为, 在class文件中有效)\RUNTIME(在运行时有效)
 *                                               只有声明为RUNTIME生命周期的注解,才能通过反射获取。
 *      Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素:如果不指定则代表那都可以用。
 *            ElementType.ANNOTATION_TYPE 		应用于注释类型
 *            ElementType.CONSTRUCTOR 			应用于构造函数
 *            ElementType.FIELD 				应用于字段或属性
 *            ElementType.LOCAL_VARIABLE 		应用于局部变量
 *            ElementType.METHOD 				应用于方法级
 *            ElementType.PACKAGE 				应用于包声明
 *            ElementType.PARAMETER 			应用于方法的参数
 *            ElementType.TYPE 					应用于类的元素
 *      *******出现的频率较低*******
 *      Documented:表示所修饰的注解在被javadoc解析时,保留下来。默认情况下,javadoc是不包括注解的。定义为Documented的注解必须设置Retention值为RUNTIME。
 *
 *      Inherited:被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。
 *                如何证明子类继承了注解--->通过反射获取注解信息 ---到反射内容时系统讲解
 * 3.可重复注解:① 在MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
 *             ② MyAnnotation的Target和Retention等元注解(如:@Inherited)与MyAnnotations相同。
 * 4. 类型注解写在@Target注解中:
 *      ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)。
 *      ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中。
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER,TYPE_USE})
@Inherited
@Repeatable(MyAnnotations.class)//jdk 8开始想要这样使用重复的注解
public @interface MyAnnotation {
    
    
    //如果只有一个参数成员,建议使用参数名为value。注意这是一个属性不是方法,虽然有()。
    //String value() ;//形式一:可以是任意类型的,如果value值可以指定多个可以定义为数组如:String[] value() ;
    String value() default "hello";//形式二:通过default关键字指定一个默认值
}

AnnotationsMyAnnotations:

package com.atguigu.java1;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Inherited
public @interface MyAnnotations {
    
    
    MyAnnotation[] value();// jdk 8之前的写法:想要写多个注解声明为数组
}

4 Day18 – Reflection

4.1 Concept

Insert image description here

Insert image description here

4.2 Application of reflection

Insert image description here

4.3 Main APIs related to reflection

Note: The first letter of the keyword class in the previously defined class is lowercase. The first letter of Class is capitalized, which represents a class and is used to represent a general class.
Insert image description here

4.4 Comprehensive test: (obtain reflection object)

This case test:

  1. Comparison between creating objects before using reflection and creating objects after using reflection to obtain public and private resources
  2. Public structures can be called through direct new or reflection. How to choose during development
  3. Are the reflection mechanism and object-oriented encapsulation contradictory? What do you think of the two technologies?
  4. Regarding the understanding of java.lang.Class class,4 ways to get an instance of Class
  5. Description of what structures Class instances can be

ReflectionTest class:

package com.atguigu.java;

import org.junit.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author shkstart
 * @create 2019 上午 10:38
 */
public class ReflectionTest {
    
    


    //反射之前,对于Person的操作
    @Test
    public void test1() {
    
    

        //1.创建Person类的对象
        Person p1 = new Person("Tom", 12);

        //2.通过对象,调用其内部的属性、方法  (一般属性私有化,通过public公共的方法间接调用私有属性。public方法可以直接调用)
        p1.age = 10;
        System.out.println(p1.toString());

        p1.show();

        //在Person类外部,不可以通过Person类的对象调用其内部私有结构。
        //比如:name私有属性、showNation()私有方法 以及私有的构造器
    }

    //反射之后,对于Person的操作
    @Test
    public void test2() throws Exception{
    
    
        Class clazz = Person.class;
        //1.通过反射,创建Person类的对象
        Constructor cons = clazz.getConstructor(String.class,int.class);//获取指定的构造器
        Object obj = cons.newInstance("Tom", 12);//通过构造器创建对象
        Person p = (Person) obj;//本质上obj是Person类型
        System.out.println(p.toString());//Person{name='Tom', age=12}
        //2.通过反射,调用对象指定的属性、方法
        //调用属性 age此时为public
        Field age = clazz.getDeclaredField("age");//获取属性age,里面是属性的名字
        age.set(p,10);//修改name属性的值
        System.out.println(p.toString());//Person{name='Tom', age=10}

        //调用方法 show此时为public
        Method show = clazz.getDeclaredMethod("show");//获取方法show,里面是方法的名字。注意同名的方法使用重载的方式区别。
        show.invoke(p);//你好,我是一个人

        System.out.println("*******************************");

        //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
        //调用私有的构造器
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person) cons1.newInstance("Jerry");
        System.out.println(p1);//Person{name='Jerry', age=0}

        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1,"HanMeimei");
        System.out.println(p1);//Person{name='HanMeimei', age=0}

        //调用私有的方法
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);

        //相当于String nation = p1.showNation("中国")
        String nation = (String) showNation.invoke(p1,"中国");//我的国籍是:中国
        System.out.println(nation);//中国


    }
    /*疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
    建议:直接new的方式。
    那么什么时候会使用:反射的方式。 反射的特征:动态性   解释动态性:一开始在编译器不能够确定要new的对象是谁,举例:在后面的
                     开发中项目部署到服务器上,先启动服务器才能通过前端访问后台,通过浏览器访问后台 有可能是登录或者注册,url后面
                     分别有参数/login登录或者rgist注册,发送到服务器端解析这个参数 发现是登录,此时服务器已经运行了,之后造login对应的对象
                     调对应的方法。  总结:在运行期间不知道改造那个类的对象,因为不知道你是想注册还是想登录,你发过来是什么就造相关类
                     的对象 进而调用相关的方法,这就叫做动态性需要用到反射来做。之前是可以确定造什么对象,比如创建Person类的对象,直接new Person()即可。*/

    //疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
    //不矛盾。封装是指的是私有的资源不能直接调用,反射是私有的也能直接调用 那不是白封装了吗,看似矛盾其实不冲突。解释:封装性指的是私有的资源调用
    //可能有风险,可以通过公有的方法调用私有的资源这种方式更好。而反射调用指的是你能不能调用的问题,你通过公有的方法调用私有的可能比直接调用私有的
    // 写法更好 建议使用这个,但是非要调用私有的那就调只不过通过公用的方法可能更好。总结:封装性解决的是建议用什么方式调的问题,反射解决的是你能不能调的问题。

    /*
    关于java.lang.Class类的理解
    1.类的加载过程:
        编译器:程序(.java源文件)经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
        运行期:接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件
              加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此
              运行时类,就作为Class的一个实例。

    2.换句话说,Class的实例就对应着加载到内存中的一个运行时类。(所以Class用new的方式赋值不对,new的是编译期对应的类)
    3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式
    来获取此运行时类。
     */
    //获取Class的实例的方式(前三种方式需要掌握)。方式三使用频率最多。
    @Test
    public void test3() throws ClassNotFoundException {
    
    
        //方式一:调用运行时类的属性:.class
        // Class本身带了泛型,可以写为:Class<Person> clazz1 = Person.class;指定泛型类型,如果不加泛型像之前学的一样如:用到的时候可能会需要强转。
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1);//class com.atguigu.java.Person
        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();//Person()
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);//class com.atguigu.java.Person

        //方式三:调用Class的静态方法:forName(String classPath)
        //这种方式需要处理异常,因为担心你找不到指定路径的文件 类似于io流要抛出异常
        Class clazz3 = Class.forName("com.atguigu.java.Person");//类的全类名:包名+类名 (因为同一个Module的不同的包中类名可能相同)
        System.out.println(clazz3);//class com.atguigu.java.Person
        //clazz3 = Class.forName("java.lang.String");//可以进行修改,说明不论是系统Api提供的类还是自定义的类都可以作为Class的一个实例
        //System.out.println(clazz3);//class java.lang.String

        System.out.println(clazz1 == clazz2);//true 说明地址值相同,前三种方式获取的类是同一个
        System.out.println(clazz1 == clazz3);//true

        //方式四:使用类的加载器:ClassLoader  (了解)
        //   ReflectionTest是当前类
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();//获取当前自定义类的类加载器是谁
        Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");//通过类加载器调用方法获取Class实例。
        System.out.println(clazz4);//class com.atguigu.java.Person

        System.out.println(clazz1 == clazz4);//true,也是同一个类

    }


    //万事万物皆对象?对象.xxx,File,URL,反射,前端、数据库操作


    //Class实例可以是哪些结构的说明:不止是运行期的类,也可以是如下结构:
    /*(1)class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
      (2)interface:接口
      (3)[]:数组
      (4)enum:枚举
      (5)annotation:注解@interface
      (6)primitive type:基本数据类型
      (7)void*/
    @Test
    public void test4(){
    
    
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        // 只要数组的元素类型与维度(都是一维数组)一样,就是同一个Class
        System.out.println(c10 == c11);//true

    }
}


Person class:

package com.atguigu.java;

/**
 * @author shkstart
 * @create 2019 上午 10:38
 */
public class Person {
    
    

    private String name;
    //为了测试反射把属性改为public
    public int age;

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }

    public Person(String name, int age) {
    
    

        this.name = name;
        this.age = age;
    }
    //为了测试反射把构造方法改为private
    private Person(String name) {
    
    
        this.name = name;
    }

    public Person() {
    
    
        System.out.println("Person()");
    }

    public void show(){
    
    
        System.out.println("你好,我是一个人");
    }
    //为了测试反射把普通方法改为private
    private String showNation(String nation){
    
    
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
}

4.5 Detailed explanation of class loading process

Insert image description here
Insert image description here
Insert image description here

4.6 Understanding of class loader ClassLoader

Insert image description here
Insert image description here

4.6.1 Common methods of class loader

Insert image description here

Insert image description here

4.6.2 ClassLoaderTest test

ClassLoaderTest class:

package com.atguigu.java;

import org.junit.Test;

import java.io.InputStream;
import java.util.Properties;

/**
 * 了解类的加载器
 * @author shkstart
 * @create 2019 下午 2:16
 */
public class ClassLoaderTest {
    
    
     //测试1:获取各种类加载器
    @Test
    public void test1(){
    
    
        //对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();//获取当前自定义类的类加载器是谁
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2  (系统类加载器)

        //调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@279f2327(扩展类加载器)

        //调用扩展类加载器的getParent():无法获取引导类加载器
        //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//null

        //String类的加载器是引导类加载器,不能直接拿到 所以输出位null
        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3);//null

    }


    //测试2:Properties(集合):用来读取配置文件。它是Map接口集合的子类Hashtable的子类Properties集合
    @Test
    public void test2() throws Exception {
    
    

        Properties pros =  new Properties();//集合对象
        //在idea中,使用相对路径 此时的文件默认在当前的module下。
        //读取配置文件的方式一:使用Map接口集合的子类Hashtable的子类Properties集合读取配置文件  详情见day05 -- 7.6
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");//这种方式如何读取到当前Module src目录下的properties文件
                                                                //一般配置文件都是放在src目录下,如果是Module下部署到Tomact服务器中会缺失
//        pros.load(fis);

        //读取配置文件的方式二:使用ClassLoader
        //在idea中,使用相对路径时,配置文件默认识别为:当前module的src下
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();//获取当前自定义类的类加载器是谁
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");//通过类加载器获取类路径下的指定文件的输入流
        pros.load(is);


        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user = " + user + ",password = " + password);//user = 吴飞,password = abc123



    }

}

jdbc1.properties configuration file:

user=吴飞
password=abc123

Insert image description here

4.7 Create objects of runtime classes through reflection

Insert image description here

NewInstanceTest:

package com.atguigu.java;

import org.junit.Test;

import java.util.Random;

/**
 * 通过发射创建对应的运行时类的对象
 *
 * @author shkstart
 * @create 2019 下午 2:32
 */
public class NewInstanceTest {
    
    
	//另一种方式查看 4.11
    @Test
    public void test1() throws IllegalAccessException, InstantiationException {
    
    

        Class<Person> clazz = Person.class;//获取Class的实例
        /*
        newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。

        要想此方法正常的创建运行时类的对象,要求:
        1.运行时类必须提供空参的构造器
        2.空参的构造器的访问权限得够。通常,设置为public。(>=private)


        在javabean中要求提供一个public的空参构造器。原因:
        1.便于通过反射,创建运行时类的对象
        2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

         */
                           //需要处理异常
        Person obj = clazz.newInstance();// 创建此 Class 对象所表示的类的一个新实例。
        System.out.println(obj);//Person{name='null', age=0}

    }

   
}

Person:

package com.atguigu.java;

/**
 * @author shkstart
 * @create 2019 上午 10:38
 */
public class Person {
    
    

    private String name;
    //为了测试反射把属性改为public
    public int age;

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }

    public Person(String name, int age) {
    
    

        this.name = name;
        this.age = age;
    }
    //为了测试反射把构造方法改为private
    private Person(String name) {
    
    
        this.name = name;
    }

    public Person() {
    
    
        System.out.println("Person()");
    }

    public void show(){
    
    
        System.out.println("你好,我是一个人");
    }
    //为了测试反射把普通方法改为private
    private String showNation(String nation){
    
    
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
}

4.8 Understand the dynamics of reflection

package com.atguigu.java;

import org.junit.Test;

import java.util.Random;


public class NewInstanceTest {
    
    

   

    //体会反射的动态性:编译时不确定创建什么对象,在运行期才能确定造的是什么对象
    //举例:使用随机数获取不同的对象 编译期不确定,只能是通过反射来做。
    @Test
    public void test2(){
    
    

        for(int i = 0;i < 100;i++){
    
    
            int num = new Random().nextInt(3);//0,1,2  Random:Api中的一个类 随机数
            String classPath = "";
            switch(num){
    
    
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.atguigu.java.Person";
                    break;
            }

            try {
    
    
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }



    }

    /*
    创建一个指定类的对象。
    classPath:指定类的全类名
     */
    public Object getInstance(String classPath) throws Exception {
    
    
        Class clazz =  Class.forName(classPath);
        return clazz.newInstance();
    }

}

4.10 Get the complete structure of the runtime class

4.10.1 Provide a Person class with rich structure

Person subclass:

package com.atguigu.java1;

/**
 * @author shkstart
 * @create 2019 下午 3:12
 */
@MyAnnotation(value="hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{
    
    


    private static final long serialVersionUID = 3974356206692991220L;
    private String name;
    int age;
    public int id;

    public Person(){
    
    }

    @MyAnnotation(value="abc")
    private Person(String name){
    
    
        this.name = name;
    }

    Person(String name,int age){
    
    
        this.name = name;
        this.age = age;
    }
    @MyAnnotation
    private String show(String nation){
    
    
        System.out.println("我的国籍是:" + nation);
        return nation;
    }

    public String display(String interests,int age) throws NullPointerException,ClassCastException{
    
    
        return interests + age;
    }


    @Override
    public void info() {
    
    
        System.out.println("我是一个人");
    }

    @Override
    public int compareTo(String o) {
    
    
        return 0;
    }

    private static void showDesc(){
    
    
        System.out.println("我是一个可爱的人");
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

Creature parent class:

package com.atguigu.java1;

import java.io.Serializable;

/**
 * @author shkstart
 * @create 2019 下午 3:12
 */
public class Creature<T> implements Serializable {
    
    

    private static final long serialVersionUID = 3539774587585249102L;
    private char gender;
    public double weight;

    private void breath(){
    
    
        System.out.println("生物呼吸");
    }

    public void eat(){
    
    
        System.out.println("生物吃东西");
    }

}

MyInterface interface:

package com.atguigu.java1;

/**
 * @author shkstart
 * @create 2019 下午 3:15
 */

public interface MyInterface {
    
    
    void info();
}

MyAnnotation annotation:

package com.atguigu.java1;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

/**
 * @author shkstart
 * @create 2019 下午 3:19
 */
@Target({
    
    TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)//只有声明为RUNTIME生命周期的注解,才能通过反射获取。
public @interface MyAnnotation {
    
    
    String value() default "hello";

}

4.10.2 Get the attribute structure (member variables) of the current runtime class

package com.atguigu.java2;

import com.atguigu.java1.Person;
import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * 获取当前运行时类的属性结构
 *
 * @author shkstart
 * @create 2019 下午 3:23
 */
public class FieldTest {
    
    


    //获取类里面的所有属性:
    @Test
    public void test1(){
    
    

        Class clazz = Person.class;

        //获取属性结构
        //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for(Field f : fields){
    
    
            /*输出结果:
            public int com.atguigu.java1.Person.id
            public double com.atguigu.java1.Creature.weight*/
            System.out.println(f);
        }

        System.out.println();

        //getDeclaredFields():获取当前运行时类中声明的所有属性,包括私有的。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
    
    
            /*输出结果:
            private static final long com.atguigu.java1.Person.serialVersionUID
            private java.lang.String com.atguigu.java1.Person.name
            int com.atguigu.java1.Person.age
            public int com.atguigu.java1.Person.id*/
            System.out.println(f);
        }
    }

    //获取类里面属性的具体结构:权限修饰符  数据类型 变量名 = 变量值
    //非静态:只能拿到:权限修饰符  数据类型 变量名  至于变量值暂时拿不到,想要拿到需要有对象,这里只测试非静态
    //静态:权限修饰符  数据类型 变量名  变量值都可以拿到
    @Test
    public void test2(){
    
    
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
    
    
            //1.权限修饰符
            int modifier = f.getModifiers();
            //输出结果:直接输出返回的是一个一个的数字,反射下的Modifier类中 每个数字对应的有权限修饰符
            //注意,如果输出的数字是0代表默认的权限,所以输出啥也没有。
            System.out.print(modifier+"\t" );//输出数字
            System.out.print(Modifier.toString(modifier)+"\t" );//输出数字对应的权限修饰符


            //2.数据类型
            Class type = f.getType();
            System.out.print(type.getName()+"\t" );

            //3.变量名
            String fName = f.getName();
            System.out.print(fName);

            System.out.println();//打印一个变量的所有信息后再换行
        }

        /*输出结果:
          26	private static final	       long	                 serialVersionUID
          2	    private	                       java.lang.String	     name
          0		                               int	                 age
          1	    public	                       int	                 id  */

        //注意:String权限修饰符会保留包名为private static final,因为自己也可以定义一个类叫作String,为了和lang包下的String进行区分
        //     所以会以全类名的方式显示。
    }


}

4.10.3 Obtain the method structure of the runtime class

package com.atguigu.java2;

import com.atguigu.java1.Person;
import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 获取运行时类的方法结构
 *
 * @author shkstart
 * @create 2019 下午 3:37
 */
public class MethodTest {
    
    


    //获取类里面的所有方法:
    @Test
    public void test1(){
    
    

        Class clazz = Person.class;

        //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for(Method m : methods){
    
    
            System.out.println(m);
        }
        System.out.println();
        //还有很多方法,具体查看Api
        //getDeclaredMethod(String name, 类<?>... parameterTypes) :获取一个指定的方法对象
        //                参数1:方法的名称       参数1:方法的名称参数数组
        //getDeclaredMethods():获取当前运行时类中声明的所有方法,包括私有的。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
    
    
            System.out.println(m);
        }
    }


    //获取类里面方法的具体结构:注解,权限修饰符,返回值类型,方法名,参数,方法上抛出的异常。
    /*
    @Xxxx
    权限修饰符  返回值类型  方法名(参数类型1 形参名1,...) throws XxxException{}
     */
    @Test
    public void test2(){
    
    
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();//获取当前运行时类中声明的所有方法,包括私有的。
        for(Method m : declaredMethods){
    
    
            //1.获取方法声明的注解 只有声明为RUNTIME生命周期的注解,才能通过反射获取。
            Annotation[] annos = m.getAnnotations();//方法上可以写多个注解,所以获取的是注解数组 需要进一步的遍历
            for(Annotation a : annos){
    
    
                System.out.println(a);
            }

            //2.权限修饰符  类似于获取属性的权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

            //3.返回值类型
            System.out.print(m.getReturnType().getName() + "\t");

            //4.方法名
            System.out.print(m.getName());
            System.out.print("(");
            //5.形参列表
            Class[] parameterTypes = m.getParameterTypes();//获取参数类型数组,如果没有参数那么返回的类型数组长度为0
            //parameterTypes == null && parameterTypes.length == 0代表数组里面没有参数。
            if(!(parameterTypes == null && parameterTypes.length == 0)){
    
    
                for(int i = 0;i < parameterTypes.length;i++){
    
    //有可能是多个形参 所以需要遍历

                    if(i == parameterTypes.length - 1){
    
    //考虑最后一个形参没有逗号文体
                                            //形参类型                    形参名
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }

                    System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
                }
            }

            System.out.print(")");

            //6.抛出的异常
            Class[] exceptionTypes = m.getExceptionTypes();
            if(exceptionTypes.length > 0){
    
    
                System.out.print("throws ");
                for(int i = 0;i < exceptionTypes.length;i++){
    
    
                    if(i == exceptionTypes.length - 1){
    
    
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }

                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }


            System.out.println();
        }



    }
}

4.10.4 Obtain other structures of runtime classes

The structures obtained are:

  1. Get the constructor structure
  2. Get the parent class of the runtime class
  3. Get the generic parent class of the runtime class
  4. Get the generics of the runtime class's parent class with generics
  5. Get the interface implemented by the runtime class
  6. Get the package where the runtime class is located
  7. Get annotations for runtime class declarations
package com.atguigu.java2;

import com.atguigu.java1.Person;
import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @author shkstart
 * @create 2019 下午 4:19
 */
public class OtherTest {
    
    

    /*
    获取构造器结构

     */
    @Test
    public void test1(){
    
    

        Class clazz = Person.class;
        //getConstructors():获取当前运行时类中声明为public的构造器,不包括父类的
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
    
    
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器,包括私有的
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
    
    
            System.out.println(c);
        }

        //至于构造器方法内部的结构和方法类似也能做。

    }

    /*
    获取运行时类的父类

     */
    @Test
    public void test2(){
    
    
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);//class com.atguigu.java1.Creature  输出的类不带泛型
    }

    /*
    获取运行时类的带泛型的父类

     */
    @Test
    public void test3(){
    
    
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);//com.atguigu.java1.Creature<java.lang.String> 输出的类带泛型
    }

    /*
    获取运行时类的带泛型的父类的泛型


    代码:逻辑性代码  vs 功能性代码
     */
    @Test
    public void test4(){
    
    
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //获取泛型类型  为什么是数组:比如Map的泛型类上就有2个,所以为数组。因为这里测试只有一个就不在遍历了。
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        //不加getTypeName()或者getName()输出为:class java.lang.String,这个方法可以去掉class
//        System.out.println(actualTypeArguments[0].getTypeName()); 方式一   输出:java.lang.String
        System.out.println(((Class)actualTypeArguments[0]).getName()); //方式二   输出:java.lang.String
    }

    /*
    获取运行时类实现的接口  (即Person的父接口)
     */
    @Test
    public void test5(){
    
    
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
    
    
            System.out.println(c);
        }

        System.out.println();
        //获取运行时类的父类实现的接口  (Person父接口的父接口)
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
    
    
            System.out.println(c);
        }

    }
    /*
        获取运行时类所在的包

     */
    @Test
    public void test6(){
    
    
        Class clazz = Person.class;

        Package pack = clazz.getPackage();
        System.out.println(pack);
    }

    /*
        获取运行时类声明的注解

     */
    @Test
    public void test7(){
    
    
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation annos : annotations){
    
    
            System.out.println(annos);
        }
    }

}

4.11 Call the specified structure of the runtime class

Call the specified structures in the runtime class: properties, methods, constructors

ReflectionTest class:

package com.atguigu.java2;

import com.atguigu.java1.Person;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 调用运行时类中指定的结构:属性、方法、构造器
 *
 * @author shkstart
 * @create 2019 下午 4:46
 */
public class ReflectionTest {
    
    

    /*

        不需要掌握,因为开发中变量都是私有的
     */
    //测试:获取指定的属性,要求运行时类中属性声明为public
    @Test
    public void testField() throws Exception {
    
    
        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();


        //获取指定的属性:要求运行时类中属性声明为public
        //通常不采用此方法    抛异常
        Field id = clazz.getField("id");

        /*
        设置当前属性的值  因为这里的属性的非静态的 所以需要创建对象

        set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
         */

        id.set(p,1001);

        /*
        获取当前属性的值
        get():参数1:获取哪个对象的当前属性值
         */
        int pId = (int) id.get(p);
        System.out.println(pId);


    }
    /*
    如何操作运行时类中的指定的属性 -- 需要掌握
     */
    //测试:获取运行时类中指定变量名的属性,包括私有的
    @Test
    public void testField1() throws Exception {
    
    
        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
        Field name = clazz.getDeclaredField("name");

        //2.保证当前属性是可访问的,如果不进行设置 ,私有的 默认的 protected的属性虽然可以拿到但是不能够赋值和取值。不设置想要使用只能是public的属性。
        name.setAccessible(true);
        //3.获取、设置指定对象的此属性值
        name.set(p,"Tom");

        System.out.println(name.get(p));

        //获取静态属性和获取静态方法方式差不多 set时只写一个参数 当前类.class 或者 只写一个Null,get时写一个当前类.class
    }

    /*
    如何操作运行时类中的指定的方法 -- 需要掌握
     */
    @Test
    public void testMethod() throws Exception {
    
    

        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表(因为方法有可能是重名重载的)
         */
        Method show = clazz.getDeclaredMethod("show", String.class);
        //2.保证当前方法是可访问的
        show.setAccessible(true);

        /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
        System.out.println(returnValue);

        System.out.println("*************如何调用静态方法*****************");

        // private static void showDesc(){
    
    
        //        System.out.println("我是一个可爱的人");
        //    }

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //如果调用的运行时类中的方法没有返回值 void,则此invoke()返回null
//        Object returnVal = showDesc.invoke(null); 方式一  2种方式:只写一个参数 当前类.class 或者 只写一个Null
        Object returnVal = showDesc.invoke(Person.class);//方式二
        System.out.println(returnVal);//null

    }

    /*
    如何调用运行时类中的指定的构造器
     */
    @Test
    public void testConstructor() throws Exception {
    
    
        Class clazz = Person.class;

        //private Person(String name)
        /*
        1.获取指定的构造器
        getDeclaredConstructor():参数:指明构造器的参数列表
         */

        Constructor constructor = clazz.getDeclaredConstructor(String.class);

        //2.保证此构造器是可访问的
        constructor.setAccessible(true);

        //3.调用此构造器创建运行时类的对象
        Person per = (Person) constructor.newInstance("Tom");//形参的值
        System.out.println(per);//Person{name='Tom', age=0, id=0}

    }

}

Person class:

package com.atguigu.java1;

/**
 * @author shkstart
 * @create 2019 下午 3:12
 */
@MyAnnotation(value="hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{
    
    


    private static final long serialVersionUID = 3974356206692991220L;
    private String name;
    int age;
    public int id;

    public Person(){
    
    }

    @MyAnnotation(value="abc")
    private Person(String name){
    
    
        this.name = name;
    }

    Person(String name,int age){
    
    
        this.name = name;
        this.age = age;
    }
    @MyAnnotation
    private String show(String nation){
    
    
        System.out.println("我的国籍是:" + nation);
        return nation;
    }

    public String display(String interests,int age) throws NullPointerException,ClassCastException{
    
    
        return interests + age;
    }


    @Override
    public void info() {
    
    
        System.out.println("我是一个人");
    }

    @Override
    public int compareTo(String o) {
    
    
        return 0;
    }

    private static void showDesc(){
    
    
        System.out.println("我是一个可爱的人");
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

4.12 Summarize the ways to create Java objects (3 ways)

Description: How to create objects of a class?

  1. Method 1: new + constructor
  2. Method 2: To create an object of class Xxx, you can consider: Check whether there are static methods in Xxx, Xxxs, XxxFactory, and XxxBuilder classes. Its static methods can be called to create Xxx objects. (Essentially, it is still new + constructor, but it is encapsulated for you. It is not recommended that you create it directly with new, which involves the embodiment of encapsulation.)
  3. Method three: through reflection

4.13 Application of Reflection - Dynamic Proxy (Episode 655)

4.13.1 Overview

Insert image description here
Insert image description here

4.13.2 Testing static proxies

package com.atguigu.java;

/**
 * 静态代理举例
 *
 * 特点:代理类和被代理类在编译期间,就确定下来了。
 *
 * @author shkstart
 * @create 2019 上午 10:11
 */
interface ClothFactory{
    
    

    void produceCloth();

}


//被代理类
class NikeClothFactory implements ClothFactory{
    
    

    @Override
    public void produceCloth() {
    
    
        System.out.println("Nike工厂生产一批运动服");
    }
}

//代理类
class ProxyClothFactory implements ClothFactory{
    
    

    private ClothFactory factory;//用被代理类对象进行实例化

    public ProxyClothFactory(ClothFactory factory){
    
    
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
    
    
        System.out.println("代理工厂做一些准备工作");

        factory.produceCloth();

        System.out.println("代理工厂做一些后续的收尾工作");

    }
}



public class StaticProxyTest {
    
    
    public static void main(String[] args) {
    
    
        //创建被代理类的对象
        ClothFactory nike = new NikeClothFactory();
        //创建代理类的对象
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        proxyClothFactory.produceCloth();
        /* 输出:
           代理工厂做一些准备工作
           Nike工厂生产一批运动服
           代理工厂做一些后续的收尾工作 */


    }
}

4.13.3 Testing dynamic proxies

package com.atguigu.java;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * 动态代理的举例
 *
 * @author shkstart
 * @create 2019 上午 10:18
 */

interface Human{
    
    

    String getBelief();

    void eat(String food);

}
//被代理类
class SuperMan implements Human{
    
    


    @Override
    public String getBelief() {
    
    
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
    
    
        System.out.println("我喜欢吃" + food);
    }
}

class HumanUtil{
    
    

    public void method1(){
    
    
        System.out.println("====================通用方法一====================");

    }

    public void method2(){
    
    
        System.out.println("====================通用方法二====================");
    }

}

/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。


 */

class ProxyFactory{
    
    
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){
    
    //obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);
        //Proxy :反射下的一个类
        // Proxy.newProxyInstance(xx xx,xx xx ,xx xx):创建代理类的对象
        //参数1:对象的类加载器  参数2:被代理类实现的接口  参数3:
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }

}

class MyInvocationHandler implements InvocationHandler{
    
    

    private Object obj;//需要使用被代理类的对象进行赋值

    public void bind(Object obj){
    
    
        this.obj = obj;
    }

    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    

        HumanUtil util = new HumanUtil();
        util.method1();

        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);

        util.method2();

        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;

    }
}

public class ProxyTest {
    
    

    public static void main(String[] args) {
    
    
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("四川麻辣烫");

        System.out.println("*****************************");

        NikeClothFactory nikeClothFactory = new NikeClothFactory();

        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);

        proxyClothFactory.produceCloth();

    }
}

4.13.4 Dynamic proxy and AOP

Insert image description here
Insert image description here
Insert image description here
Insert image description here

5 Day19 – Network communication (Socket communication)

Note: io flow is data transmission performed locally, which is data transmission performed in the network. Here we will briefly explain it, focusing on the web stage.

5.1 Overview of network communication

Insert image description here

Insert image description here
Overview: Network communication is actually communication between Sockets, and data is transmitted between two Sockets through IO. In essence, network programming is to abstract data into the form of IO streams and transmit them over the network.
Insert image description here
Insert image description here

5.2 Overview of network communication elements

Note: The two problems of network programming communication are solved respectively through: ip port number and protocol.

Insert image description here
Insert image description here

Insert image description here

5.3 Detailed explanation of two communication elements

5.3.1 IP and port number

1. IP address introduction:

1.1 Description: Similar to the File class, to perform reading and writing operations on local files, an endpoint File class is required, and File corresponds to a file in the local hard disk. The IP address is a node in network transmission. In Java, everything is an object, represented by the InetAddress class. Finally, ip + port number form a node Socket in the network and consider the network communication protocol, so that different hosts can realize data transmission.

1.2 Overview:
Insert image description here

1.3 Inheritance structure:

Insert image description here
1.4 Create object: There is no constructor method provided, and this object instance can be returned according to the static method:

1.5 Two representation methods of host address:
Insert image description here
Insert image description here
1.6 Test:

package com.java;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 一、网络编程中有两个主要的问题:
 * 1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
 * 2.找到主机后如何可靠高效地进行数据传输
 *
 * 二、网络编程中的两个要素:
 * 1.对应问题一:IP和端口号
 * 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)
 *
 *
 * 三、通信要素一:IP和端口号
 *
 * 1. IP:唯一的标识 Internet 上的计算机(通信实体)
 * 2. 在Java中使用InetAddress类代表IP
 * 3. IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
 * 4. 域名:   www.baidu.com   www.mi.com  www.sina.com  www.jd.com
 *            www.vip.com   域名更加形象方便记忆
 * 5. 本地回路地址:127.0.0.1 对应着:localhost(可以看成是域名)
 *
 * 6. 如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost()
 *        两个常用方法:getHostName() / getHostAddress()
 *
 * 7. 端口号:正在计算机上运行的进程。
 * 要求:不同的进程有不同的端口号
 * 范围:被规定为一个 16 位的整数 0~65535。
 *
 * 8. 端口号与IP地址的组合得出一个网络套接字:Socket
 * @author shkstart
 * @create 2019 下午 2:30
 */
public class InetAddressTest {
    
    

    public static void main(String[] args) {
    
    

        try {
    
    
            //类似于:File file = new File("hello.txt");   参数:域名或ip地址
            InetAddress inet1 = InetAddress.getByName("192.168.10.14");//ip地址

            System.out.println(inet1);//192.168.10.14

            InetAddress inet2 = InetAddress.getByName("www.atguigu.com");//域名
            System.out.println(inet2);//www.atguigu.com/111.7.163.158

            InetAddress inet3 = InetAddress.getByName("127.0.0.1");
            System.out.println(inet3);///127.0.0.1

            //获取本地ip
            InetAddress inet4 = InetAddress.getLocalHost();
            System.out.println(inet4);//DESKTOP-4323HLC/169.254.173.57(这个获取的是局域网内的本机ip地址,实际上就是127.0.0.1)

            //getHostName() 获取域名
            System.out.println(inet2.getHostName());//www.atguigu.com
            //getHostAddress() 获取ip地址
            System.out.println(inet2.getHostAddress());//111.7.163.158

        } catch (UnknownHostException e) {
    
    
            e.printStackTrace();
        }


    }


}


2. Port number introduction:
2.1 Overview:
Insert image description here
Insert image description here

5.3.2 Agreement

Overview:
Insert image description here
TCP/IP protocol cluster:
Insert image description here
Comparison between TCP (such as transferring files, sending emails, browsing the web) and UDP (such as domain name query, voice calls, live video, tunnel networks, etc.): Tunnel network: such as VPN
.
Insert image description here
Three-way handshake, four-way wave:
Explanation: TCP requires a three-way handshake when establishing a connection, and four waves when disconnecting.
Real life example: Client: Xiao Ming Server: Jack Ma
Note:Only the client can initiate the handshake.
The first handshake: Xiao Ming said to Jack Ma: Hello, I am Xiao Ming.
The second handshake: Ma Yun said to Xiao Ming: Hello, I know you are Xiao Ming, my name is Ma Yun. The
third handshake: Xiao Ming said to Ma Yun: I know you know that I am Xiao Ming and you are Ma Yun.
A three-way handshake can basically guarantee that both parties are present. For example, a one-time handshake is 50%, a two-time handshake is 80%, and a three-way handshake is 99%. If the probability of both parties being present exceeds the three-way handshake, the probability of both parties being present is relatively small. Too many handshakes will waste time. .
Insert image description here
Real life examples: client, server.
Note: Both the client and the server can actively release the connection, but generally the client releases the connection and the server does not close the connection. For example: when you go to Baidu to read news and don’t want to read it, just turn off the computer. , look at it at any time and turn on the computer at any time. If the server is also closed, it means that Baidu cannot be played.
The first wave: the client said to the server: I want to disconnect.
The second wave: the server said to the client: I know you want to disconnect.
The third wave: After the server disconnects, it says to the client: I have disconnected. The
fourth wave: The client sends data to the server to verify whether the information can still be received. If no information is received, it means disconnecting.
Insert image description here

5.4 TCP network programming case test

5.4.1 The client sends information to the server, and the server displays the data on the console

package com.java;


import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 实现TCP的网络编程
 * 例子1:客户端发送信息给服务端,服务端将数据显示在控制台上
 * 注意:需要先启动服务器,在启动客户端。如果先启动客户端建立连接 服务器没有启动,则不会连接成功。
 * @author shkstart
 * @create 2019 下午 3:30
 */
public class TCPTest1 {
    
    

    //客户端
    @Test
    public void client()  {
    
    
        Socket socket = null;
        OutputStream os = null;
        try {
    
    
            //1.创建Socket对象,指明服务器端的ip和端口号
            InetAddress inet = InetAddress.getByName("127.0.0.1");//这里是自己发给自己
            socket = new Socket(inet,8899);
            //2.获取一个输出流,用于输出数据
            os = socket.getOutputStream();//返回此套接字的输出流。
            //3.写出数据的操作
            os.write("你好,我是客户端mm".getBytes());
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //4.资源的关闭
            if(os != null){
    
    
                try {
    
    
                    os.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
            if(socket != null){
    
    
                try {
    
    
                    socket.close();//socket类似于流 也需要自己关闭。
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }

            }
        }



    }
    //服务端
    @Test
    public void server()  {
    
    

        ServerSocket ss = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
    
    
            //1.创建服务器端的ServerSocket,指明自己的端口号
            //为什么不指定服务器的ip,在哪个服务器跑的当然知道自己这个服务器的ip。
            ss = new ServerSocket(8899);
            //2.调用accept()表示接收来自于客户端的socket
            socket = ss.accept();
            //3.获取输入流
            is = socket.getInputStream();//返回此套接字的输入流。

            //不建议这样写,可能会有乱码。因为用的是字节流读取汉字如果存不下会有乱码,UTF-8一个汉字占3个字节,new byte[5],第二个汉字就存不下了。
            //  解决:     1.可以扩大字节数组new byte[1000],但是如果写出的数据多了同样不行。2.使用ByteArrayOutputStream
//        byte[] buffer = new byte[1024];
//        int len;
//        while((len = is.read(buffer)) != -1){
    
    
//            String str = new String(buffer,0,len);//还原为字符串可能会出现乱码。
//            System.out.print(str);
//        }
            //4.读取输入流中的数据
            //这个对象有没有指定文件路径,那么数据写到哪了???这个类本身提供了一个数组,不够的话会自动扩容。
            //来几个数据依次拼接到数组中,最后统一还原为字符串
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[5];
            int len;
            while((len = is.read(buffer)) != -1){
    
    
                baos.write(buffer,0,len);
            }

            System.out.println(baos.toString());//把里面的所有字节数组转化为字符串
            //Socket提供的方法: InetAddress  getInetAddress() 返回套接字连接的地址。
            //InetAddress(ip类): String getHostAddress() 返回IP地址字符串(以文本表现形式)。
            System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress() + "的数据");

        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            if(baos != null){
    
    
                //5.关闭资源   先关闭外层,ByteArrayOutputStream ,InputStream, socket ,ServerSocket
                try {
    
    
                    baos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(is != null){
    
    
                try {
    
    
                    is.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(socket != null){
    
    
                try {
    
    
                    socket.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(ss != null){
    
    
                try {
    
    
                    ss.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }

        }





    }

}


Insert image description here

5.4.2 The client sends the file to the server, and the server saves the file locally.

package com.java;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *
 * 实现TCP的网络编程
 * 例题2:客户端发送文件给服务端,服务端将文件保存在本地。
 *
 * @author shkstart
 * @create 2019 下午 3:53
 */
public class TCPTest2 {
    
    

    /*
    这里涉及到的异常,应该使用try-catch-finally处理
     */
    @Test
    public void client() throws IOException {
    
    
        //1.
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
        //2.
        OutputStream os = socket.getOutputStream();
        //3.       这里的数据来自于文件所以先把数据读进来。
        FileInputStream fis = new FileInputStream(new File("beauty.jpg"));
        //4.
        byte[] buffer = new byte[1024];
        int len;
        while((len = fis.read(buffer)) != -1){
    
    
            os.write(buffer,0,len);//把读进来的数据写出去
        }
        //5.
        fis.close();
        os.close();
        socket.close();
    }

    /*
    这里涉及到的异常,应该使用try-catch-finally处理
     */
    @Test
    public void server() throws IOException {
    
    
        //1.
        ServerSocket ss = new ServerSocket(9090);
        //2.
        Socket socket = ss.accept();
        //3.
        InputStream is = socket.getInputStream();
        //4.
        FileOutputStream fos = new FileOutputStream(new File("beauty1.jpg"));//保存到本地
        //5.
        byte[] buffer = new byte[1024];
        int len;
        while((len = is.read(buffer)) != -1){
    
    
            fos.write(buffer,0,len);
        }
        //6.
        fos.close();
        is.close();
        socket.close();
        ss.close();

    }
}


5.4.3 The client sends the file to the server, and the server saves it locally. and returns "send successfully" to the client

package com.java;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 实现TCP的网络编程
 * 例题3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。
 * 并关闭相应的连接。
 * @author shkstart
 * @create 2019 下午 4:13
 */
public class TCPTest3 {
    
    

    /*
        这里涉及到的异常,应该使用try-catch-finally处理
         */
    @Test
    public void client() throws IOException {
    
    
        //1.
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
        //2.
        OutputStream os = socket.getOutputStream();
        //3.
        FileInputStream fis = new FileInputStream(new File("beauty.jpg"));
        //4.
        byte[] buffer = new byte[1024];
        int len;
        while((len = fis.read(buffer)) != -1){
    
    
            os.write(buffer,0,len);
        }
        //关闭数据的输出
        socket.shutdownOutput();

        //5.接收来自于服务器端的数据,并显示到控制台上
        InputStream is = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bufferra = new byte[20];
        int len1;
        while((len1 = is.read(bufferra)) != -1){
    
    
            baos.write(bufferra,0,len1);
        }

        System.out.println(baos.toString());

        //6.
        fis.close();
        os.close();
        socket.close();
        baos.close();
    }

    /*
    这里涉及到的异常,应该使用try-catch-finally处理
     */
    @Test
    public void server() throws IOException {
    
    
        //1.
        ServerSocket ss = new ServerSocket(9090);
        //2.
        Socket socket = ss.accept();
        //3.
        InputStream is = socket.getInputStream();
        //4.
        FileOutputStream fos = new FileOutputStream(new File("beauty4.jpg"));
        //5.
        byte[] buffer = new byte[1024];
        int len;
        while((len = is.read(buffer)) != -1){
    
    
            fos.write(buffer,0,len);
        }
        /*如果客户端不调用socket.shutdownOutput()这个方法,"图片传输完成"在控制台输出不来 图片也没有复制不成功,
        说明是客户端发送数据 和服务端接收数据之间出现的问题。为什么会产生这个原因呢????
        因为read方法是个阻塞式的方法,在服务端读取客户端发送的数据时 没有明确告诉的话这个方法不会退出循环,
        写在while中的方法是 判断文件没有的话返回为-1,在客户端传输据没有给个明确的指示什么时候传完数据,所以在服务端
        接收数据时一直等着客户端发送数据 没有给个明确的指示 客户端数据何时传完。它会一直停留在while循环中出不去,也就不会执行下面的代码了。*/
        System.out.println("图片传输完成");

        //6.服务器端给予客户端反馈
        OutputStream os = socket.getOutputStream();
        os.write("你好,美女,照片我已收到,非常漂亮!".getBytes());

        //7.
        fos.close();
        is.close();
        socket.close();
        ss.close();
        os.close();

    }
}


5.4.4 Client and server understanding

Insert image description here

5.5 UDP network programming

5.5.1 Overview

Insert image description here
Insert image description here
Insert image description here
Insert image description here

5.5.2 Testing

package com.java;

import org.junit.Test;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * UDPd协议的网络编程
 *
 * 先启动接收端,在启动发送端
 * @author shkstart
 * @create 2019 下午 4:34
 */
public class UDPTest {
    
    

    //发送端
    @Test
    public void sender() throws IOException {
    
    

        DatagramSocket socket = new DatagramSocket();



        String str = "我是UDP方式发送的导弹";
        byte[] data = str.getBytes();
        InetAddress inet = InetAddress.getLocalHost();
        //指定服务端的ip和端口号 封装为数据报
        DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);

        socket.send(packet);//发送数据报

        socket.close();

    }
    //接收端
    @Test
    public void receiver() throws IOException {
    
    

        DatagramSocket socket = new DatagramSocket(9090);//指定自己的端口号

        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

        socket.receive(packet);//把数据接收到数据报packet中,本质上是在buffer中
        //packet.getData()获取packet中的字节数组,从0开始看你写进去几个
        System.out.println(new String(packet.getData(),0,packet.getLength()));

        socket.close();
    }
}


5.6 URL Programming

Note: No longer write the client and server, for example: directly enter the address in the browser to access the resources in the server.
Insert image description here
Insert image description here
Insert image description here
Insert image description here

5.6.1 Test 1: URL common method test

package com.java;

import java.net.MalformedURLException;
import java.net.URL;

/**
 * URL网络编程
 * 1.URL:统一资源定位符,对应着互联网的某一资源地址
 * 2.格式:
 *  http://localhost:8080/examples/beauty.jpg?username=Tom
 *  协议   主机名    端口号  资源地址           参数列表
 *
 * @author shkstart
 * @create 2019 下午 4:47
 */
public class URLTest {
    
    

    public static void main(String[] args) {
    
    

        try {
    
    

            URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");

//            public String getProtocol(  )     获取该URL的协议名
            System.out.println(url.getProtocol());
//            public String getHost(  )           获取该URL的主机名
            System.out.println(url.getHost());
//            public String getPort(  )            获取该URL的端口号
            System.out.println(url.getPort());
//            public String getPath(  )           获取该URL的文件路径
            System.out.println(url.getPath());
//            public String getFile(  )             获取该URL的文件名
            System.out.println(url.getFile());
//            public String getQuery(   )        获取该URL的查询名
            System.out.println(url.getQuery());




        } catch (MalformedURLException e) {
    
    
            e.printStackTrace();
        }

    }


}

5.6.2 Test 2: Download files from tomact server

package com.java;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @author shkstart
 * @create 2019 下午 4:54
 */
public class URLTest1 {
    
    
    //说明:beauty.jpg 为本机tomact服务器下的一个资源
    //需求:把服务器的资源文件 beauty.jpg下载下来。
    public static void main(String[] args) {
    
    

        HttpURLConnection urlConnection = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
    
    
            URL url = new URL("http://localhost:8080/examples/beauty.jpg");
            //URLConnection urlConnection = url.openConnection():获取服务器的一个连接对象
            //http协议获取的是 HttpURLConnection的一个连接对象,又是URLConnection的子类,所以可以强转。
            urlConnection = (HttpURLConnection) url.openConnection();

            urlConnection.connect();//通过对象获取连接

            is = urlConnection.getInputStream();//获取到流下面步骤就一样了
            fos = new FileOutputStream("day10\\beauty3.jpg");

            byte[] buffer = new byte[1024];
            int len;
            while((len = is.read(buffer)) != -1){
    
    
                fos.write(buffer,0,len);
            }

            System.out.println("下载完成");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //关闭资源
            if(is != null){
    
    
                try {
    
    
                    is.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(fos != null){
    
    
                try {
    
    
                    fos.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if(urlConnection != null){
    
    
                urlConnection.disconnect();
            }
        }






    }
}


Guess you like

Origin blog.csdn.net/aa35434/article/details/131211582