java进阶学习

(一)常用类

参考博客:https://www.cnblogs.com/coderzjz/p/13611161.html

参考视频:https://www.bilibili.com/video/BV1vt4y197nY?p=31&vd_source=73cf57eb7e9ae1ddd81e6b44cf95dbeb

Object类

getClass()

1673874240623

返回的是对象的类型。

这里的Student是实体类

1673873647404

hashCode()

1673873735395

可以用hashcode来判断两个对象是不是同一个。

(这里:s1和s2不是同一个对象,在内存里开辟的地址是不一样的,所以当然不一样。而s3没有开辟的新的地址,而是把s1的地址也给了s3,所以s3和s1是同一个对象)

1673874013669

toString()

1673874371223

1673874554797

默认的源码

1673874646982

我们可以去实体类重写

1673874688286

再调用

1673874717294

equals()

1673874812977

源码

1673875105517

实例(因为比较的是地址所以..都不相等)

1673875285517

1673875237656

回到实体类重写方法

1673875578104

1673875625574

这个时候就比较内容了

1673875665565

finalize()

1673876114532

源码

1673876335749

实体类重写

1673876376149

实例

1673876464205

这样就被回收了

1673876496307

包装类

概述

1673876681957

对应

1673876722462

类型转换与装箱拆箱

  • 类型转换:

    指的是,基本类型与包装类之间的类型转换

  • 装箱(基本数据类型-->引用数据类型)

    存放在栈里边的基本类型转化为存在堆里边的对象(引用数据类型)

  • 拆箱(引用数据类型-->基本数据类型)

    存在堆里边的对象(引用数据类型)转化为存放在栈里边的基本类型

1673878174107

1673878222864

基本类型和字符串转换

1673879106612

1673879376482

1673879511834

Integer缓冲区

1673881635614

String类

1673881898636

1673881907534

1673881953148

1673881963012

1673882084293

1673882390726

字符串之间的内容的值比较---->s1.equals(s2)

1673882584639

String常用方法

1673940731002

实例

package com.lovi.base.commonClass.String;

import java.util.Arrays;

public class StringTest {
    public static void main(String[] args) {
// 1. length(); 返回字符串长度
// 2. charAt(int index); 返回某个位置的字符
// 3. contains(String str); 判断是否包含某个字符串
        System.out.println("-------------------String方法01-------------------------");

        String content = "java是最好的语言,java哈哈哈哈,java呀!";
        System.out.println(content.length()); // 26
        System.out.println(content.charAt(content.length() - 1)); // !
        System.out.println(content.contains("java")); // true

// 4. toCharArray(); 返回字符串对应数组
// 5. indexOf(); 返回子字符串首次出现的位置
// 6. lastIndexOf(); 返回字符串最后一次出现的位置
        System.out.println("-------------------String方法02-------------------------");
        System.out.println(Arrays.toString(content.toCharArray()));//[j, a, v, a, 是, 最, 好, 的, 语, 言, ,, j, a, v, a, 哈, 哈, 哈, 哈, ,, j, a, v, a, 呀, !]
        System.out.println(content.indexOf("java")); // 0
        System.out.println(content.indexOf("java", 4)); // 从索引4开始找 返回11
        System.out.println(content.lastIndexOf("java")); // 20

// 7. trim(); //去掉字符串前后空格
// 8. toUpperCase(); toLowerCase(); 转换大小写
// 9. endsWith(str); startsWith(str);  判断是否以str 结尾、开头
        System.out.println("-------------------String方法03-------------------------");

        String ct = " hello world ";
        System.out.println(ct.trim()); // "hello world"
        System.out.println(ct.toUpperCase()); // HELLO WORLD
        System.out.println(ct.toLowerCase()); // hello world
        System.out.println(ct.endsWith("world")); // true
        System.out.println(ct.startsWith("hello")); // true

// 10. replace(char old, char new); 用新的字符或字符串替换旧的字符或字符串
// 11. split(); 对字符串拆分
        System.out.println("-------------------String方法04-------------------------");

        System.out.println(content.replace("java", "php")); //php是最好的语言,php哈哈哈哈,php呀!
        System.out.println(content);//java是最好的语言,java哈哈哈哈,java呀!
        String say = "java is the best language";
        String[] arr = say.split(" "); // "[ ,]+" 表示空格 逗号切分 +号表示切分可以多个 比如多个空格
        System.out.println(arr.length); // 5
        for(String string : arr){
            System.out.println(string);
        }
// 打印出
//java
//is
//the
//best
//language
        System.out.println("-------------------String方法05-------------------------");

// 补充两个equals/compareTo();比较大小
        String s1 = "hello";
        String s2 = "HELLO";
        System.out.println(s1.equalsIgnoreCase(s2));// 忽略大小写比较true

// compareTo(); 两字符不同时比较字符字典序的ascii码
// 字符相同时比较长度 返回差值
        String a1 = "xbc";
        String a2 = "xbcefg";
        String a3 = "xbgds";
        String a4 = "xbc";
        String a5 = "abc";

        System.out.println(a1.compareTo(a2));//-3(比长度)
        System.out.println(a1.compareTo(a3));//-4(c和g的差值)
        System.out.println(a1.compareTo(a4));//0(比长度)
        System.out.println(a1.compareTo(a5));//23(x和a的差值)
        System.out.println(a2.compareTo(a3));//-4(c和g的差值)
    }
}

案例

package com.lovi.base.commonClass.String;
/*
String案例
已知String str = "this is a text";
1.将str中的单词单独获取
2.将str中的text替换成practice
3.在text前面插入一个easy
4.将每个单词的首字母改为大写
 */

import java.util.Arrays;

public class StringTest2 {
    public static void main(String[] args) {
        String str = "this is a text";
        // 1.
        String[] arr = str.split(" ");
        for (String s : arr) {
            System.out.println(s);
        }
        // 2.
        String str2 = str.replace("text", "practice");
        System.out.println(str2);
        // 3.
        String str3 = str.replace("text", "easy text");
        System.out.println(str3);
        // 4.
        String[] arr2=new String[arr.length];
        String f1="";//空字符串定义
        for (int i = 0; i < arr.length; i++) {
            char first = arr[i].charAt(0);
            char upperfirst = Character.toUpperCase(first);
            String news = upperfirst + arr[i].substring(1);//字符串截取
            f1=f1+news+' ';//字符串拼接(需要先给f1初始化)
            System.out.println(news);
            arr2[i]=news;
        }
        System.out.println("f1:"+f1);//f1:This Is A Text
        //字符串合并(拼接)
        System.out.println(Arrays.toString(arr2));
        //join() 将数组或者多个内容合并为字符串 (String 类的静态方法)
        String res = String.join(" ",arr2);//[数组转化为字符串,并且用空格分开
        System.out.println(res);
    }
}

StringBuffer和StringBuilder(可变字符串)

1673940759919

实例

package com.lovi.base.commonClass.String;

public class StringTest3 {
    public static void main(String[] args) {
        // StringBuffer 和 StringBuilder 用法一致
        StringBuffer sb = new StringBuffer();
        System.out.println(sb);
        // 1. append(); 追加
        sb.append("java no1");
        System.out.println(sb);

        // 2. insert(); 添加、插入
        sb.insert(0, "在第一个位置插入");
        System.out.println(sb);

        // 3.replace(); 替换
        sb.replace(0, 2, "你好呀"); // 左闭右开
        System.out.println(sb);

        // 4. delete(); 删除
        sb.delete(0, 5); // 左闭右开
        System.out.println(sb);

        // 5. 清空
        sb.delete(0, sb.length());
        System.out.println(sb.length());

    }
}

验证string和StringBuilder的效率

1673942019770

Math类

参考:https://blog.csdn.net/m0_58262663/article/details/124910469

package com.lovi.base.commonClass.math;

public class mathTest {
    public static void main(String[] args){
        //返回a的绝对值
        double a = -5.0;
        double abs = Math.abs(a);
        System.out.println("a的绝对值:"+abs);

        //返回两个数中的最大值、最小值
        double b =10.0;
        double max = Math.max(a,b);
        double min = Math.min(a,b);
        System.out.println("a,b的最大值为:"+max);
        System.out.println("a,b的最小值为:"+min);

        //产生一个0-1之间的随机数(包括0,不包括1)
        double random = Math.random();
        System.out.println("产生一个0-1之间的随机数:"+random);

        //返回a的3次幂
        double pow = Math.pow(a,3);
        System.out.println("a的3次幂为:"+pow);

        //返回b的平方根
        double sqrt = Math.sqrt(b);
        System.out.println("b的平方根为:"+sqrt);

        //返回c的对数
        double c = 8.0;
        double log = Math.log(c);
        System.out.println("c的对数为:"+log);

        //返回d的正弦值
        double d = 0.5;
        double sin = Math.sin(d);
        System.out.println("d的正弦值为:"+sin);

        //返回d的反正弦值
        double asin = Math.asin(d);
        System.out.println("d的反正弦值为:"+asin);

        //返回大于d的最小整数,并将该整数转化为double数据
        double ceil = Math.ceil(d);
        System.out.println("大于d的最小整数为:"+ceil);

        //返回小于d的最大整数,并将该整数转化为double数据
        double floor = Math.floor(d);
        System.out.println("小于d的最大整数为:"+floor);

        //返回某个数的四舍五入的值
        System.out.println(Math.round(15.6));
        System.out.println(Math.round(15.5));
        System.out.println(Math.round(15.4));
        System.out.println(Math.round(-15.5));
        System.out.println(Math.round(-15.6));
        /*
        如果该数为非负数,小数大于或等于0.5入,小于0.5舍
        如果该数为负数,小数大于0.5入,小于或等于0.5舍
         */
    }
}

Random类

package com.lovi.base.commonClass.random;

import java.util.Random;

public class testRandom {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextInt(5)); //0 ~ 5间随机整数,不包括5
    }
}

File类

参考链接:https://blog.csdn.net/I_r_o_n_M_a_n/article/details/110103112

System类

1673945078819

package com.lovi.base.commonClass.system;

import java.util.Arrays;

public class testSystem {
    public static void main(String[] args) {
        //arraycopy:数组的复制
        //src:原数组
        //srcPos:从哪个位置开始复制
        //dest:目标数组
        //destPos:目标数组的位置
        //length :复制的长度
        //arraycopy(src,srcPos,dest,destPos,length);
        int [] arr ={20,18,15,8,35,26,45,90};
        int [] dest=new int [8];
        System.arraycopy(arr,0,dest,0,arr.length);
        System.out.println(Arrays.toString(dest));

        //currentTimeMillis()  返回从1970年1月1日 到现在的毫秒数
        //用于计时
        long start = System.currentTimeMillis();
        String string="";
        for(int i=0;i<999;i++){
            string+=i;
        }
        long end = System.currentTimeMillis();
        System.out.println("用时:"+(end - start));

        //System.gc() 告诉垃圾回收器回收垃圾
        new Student(14561, "小明");
        new Student(68717, "小红");
        new Student(46466, "小绿");
        System.gc();
        //System.exit(0) 退出jvm  后面代码不会执行
        System.exit(0);
        System.out.println("本代码不会执行");
    }
}

1673946264048

BigDecimal类

1673942978312

1673943005810

1673942239055

大浮点数精确计算(加减乘除)

1673942802409

其中

1673942958334

Date类

1673943089955

实例

1673943640993

1673943795879

Calendar类

1673943832173

实例

1673944180958

1673944514932

1673944488164

SimpleDateFormat类

格式化date:(把日期转成字符串)
解析:(把字符串转成日期)

1673944552843

1673944674101

1673944910503

常用类小结

内部类:
在一个类的内部再定义一个完整的类。
成员内部类、静态内部类、局部内部类、匿名内部类。
Object类:
所有类的直接或间接父类,可存储任何对象。
包装类:
基本数据类型所对应的引用数据类型,可以使Object统一所有数据。
String类:
字符串是常量,创建之后不可改变,字面值保存在字符串池中,可以共享。
BigDecimal:
可精确计算浮点数。
Date
特定时间
Calendar
日历
SimpleDateFormat
格式化时间
System
系统类

(二)集合框架

(三)IO流(IO框架)

记录\n\r

\r是把光标定位到当前行的首位
\n是换行

字符和字节的关系

  • 字符:
    你可以将字符简单地理解为你可以用输入法单独打出的任何单个的内容。对于中文和英文而言,以下内容分别都是1个字符: (1)1个中文字,例如“是” (2)1个英文字母,例如“T” (3)1个中文标点符号,例如“。” (4)1个英文标点符号,例如“.” 所谓的“字符串”就是由以上说的一个个字符组成的,字符串就是由一个个字符拼接而成的列表
  • 字节
    UTF-8 编码中,一个英文字为一个字节,一个中文为三个字节。
    Unicode 编码中,一个英文为一个字节,一个中文为两个字节。

(四)GUI

直接看我的小博客:GUI基础学习

GUI游戏实战:贪吃蛇

(五)多线程

视频教程:https://www.bilibili.com/video/BV1V4411p7EF

1.概述

普通方法调用和多线程

1674410902223

程序.进程.线程

  • 程序是指令和数据的有序集合,其本身没有任何运
    行的含义,是一个静态的概念

  • 而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位

  • 线程是CPU调度和执行的的单位。通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没
    有存在的意义。

注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务
器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能
执行一个代码,因为切换的很快,所以就有同时执行的错局

核心概念

  • 线程就是独立的执行路径;
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
  • main()称之为主线程,为系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制.
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2.线程创建

1674411527428

继承Thread类

  • 子类继承Thread类具备多线程能力
  • 启动线程:子类对象. start()
  • 不建议使用:避免OOP单继承局

实现Runnable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

继承Thread类

package com.lovi.thread;
/*
自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
就会发现这些都是交替执行的!(start方法,交给cpu调度)
 */
public class Thead01 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("执行了线程的run方法"+i);
        }
    }

    public static void main(String[] args) {
        Thead01 t1 = new Thead01();
        t1.start();//自己去调度!
        for (int i = 0; i < 200; i++) {
            System.out.println("执行了主线程的main方法==>"+i);
        }
    }
}

1674287092361


网图下载

百度上找到这个包并且下载:https://nowjava.com/jar/detail/m02261718/commons-io-2.6.jar.html

1674287212619

1674287680653

1674287713044

package com.lovi.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class Thread02 extends Thread{
    private String url;
    private String name;

    public Thread02(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WebDownloader wd = new WebDownloader();
        wd.downloader(url,name);
        System.out.println("下载了==>"+name);
    }

    public static void main(String[] args) {
        //这里我发现就是图片路径是百度搜索出来的就可以,但是是bing搜索出来的就都不行,就很奇怪。
        Thread02 t1 = new Thread02("https://pic.616pic.com/ys_bnew_img/00/26/86/lxRx8VAXKN.jpg","a.jpg");
        Thread02 t2 = new Thread02("https://img1.baidu.com/it/u=2163627747,3625401606&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500","b.jpg");
        Thread02 t3 = new Thread02("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","c.jpg");
        Thread02 t4 = new Thread02("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","d.jpg");
        Thread02 t5 = new Thread02("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","e.jpg");
        Thread02 t6 = new Thread02("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","f.jpg");
        Thread02 t7 = new Thread02("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","g.jpg");
        Thread02 t8 = new Thread02("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","h.jpg");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        t7.start();
        t8.start();
    }
}
//文件下载工具类!
class WebDownloader {
    //url==>网络路径,name==>存储的名字!
    public void downloader(String url , String name){
        try {
            //FileUtiIs文件工具,复制url到文件
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件下载失败!");
        }

    }

}


1674289778410


Runable接口

1674290118594

1674290139130

关键点

Thread03 t1 = new Thread03();
new Thread(t1).start();//(需要线程类实现Runnable方法)
//t1.start();//(需要线程类继承Thread)

package com.lovi.thread;

public class Thread03 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("执行了线程的run方法"+i);
        }
    }

    public static void main(String[] args) {
        Thread03 t1 = new Thread03();
        new Thread(t1).start();//(需要线程类实现Runnable方法)
        //t1.start();//(需要线程类继承Thread)
        for (int i = 0; i < 200; i++) {
            System.out.println("执行了主线程的main方法==>"+i);
        }
    }
}

1674290510410

抢火车票
package com.lovi.thread;
//多个线程操作同一个资源(Runable的好处)
//抢火车票的问题
public class Thread04 implements Runnable{
    private int ticketNums = 10;
    @Override
    public void run() {
        while (true){
            //获取当前线程的名字==>Thread.currentThread().getName()
            System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums--+"张票!");
            //模拟延时,让线程睡一下(1秒)!
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("线程睡觉出了问题");
            }
            //设置跳出循环的条件
            if(ticketNums <= 0){
                break;
            }

        }

    }

    public static void main(String[] args) {
        Thread04 t1 = new Thread04();
        new Thread(t1,"小白").start();
        new Thread(t1,"小红").start();
        new Thread(t1,"小黄").start();
    }
}

1674291414809

发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。


龟兔赛跑
package com.lovi.thread;
/*
1.先让乌龟和兔子都正常两个线程跑
2.再给兔子每几步就睡一下!
3.设定好,跑100步就赢
 */
//龟兔赛跑(控制台版)
public class Thread05 implements Runnable {
    private String winner=null;
    boolean flag=false;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
            //判断比赛是否结束
            flag = gameOver(i);
            //当产生胜利者的时候跳出循环!
            if(flag){
                break;
            }

            //让兔子睡觉(每走50步睡一下)
            if (Thread.currentThread().getName().equals("兔子")&&i%50==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
    //判断游戏结束
    public boolean gameOver(int steps){
        //判断是否有胜利者
        if(winner!=null){
            return true;
        }
        if(steps >= 100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is  " + winner);
            return true;
        }
        return false;

    }

    public static void main(String[] args) {
        Thread05 thread05 = new Thread05();
        new Thread(thread05,"兔子").start();
        new Thread(thread05,"乌龟").start();

    }
}

1674293429271


Callable接口与线程池(初步)

关键点

/*
Callable的好处
1. 可以定义返回值
2. 可以抛出异常
使用关键点
        0.创建一个线程
        Thread06 t1 = new Thread06("https://pic.616pic.com/ys_bnew_img/00/26/86/lxRx8VAXKN.jpg","a.jpg");
        1.创建执行任务(线程池)
        ExecutorService service = Executors.newFixedThreadPool(8);//执行8个子线程
        //2.提交执行
        Future<Boolean> future1 =service.submit(t1);
        3.获取结果
        boolean rs1 = future1.get();
        //4.关闭服务
        service.shutdownNow();
        
 */
package com.lovi.thread;

import java.util.concurrent.*;

public class Thread06 implements Callable<Boolean> {
    private String url;
    private String name;

    public Thread06(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() {
        WebDownloader wd = new WebDownloader();
        wd.downloader(url,name);
        System.out.println("下载了==>"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread06 t1 = new Thread06("https://pic.616pic.com/ys_bnew_img/00/26/86/lxRx8VAXKN.jpg","a.jpg");
        Thread06 t2 = new Thread06("https://img1.baidu.com/it/u=2163627747,3625401606&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500","b.jpg");
        Thread06 t3 = new Thread06("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","c.jpg");
        Thread06 t4 = new Thread06("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","d.jpg");
        Thread06 t5 = new Thread06("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","e.jpg");
        Thread06 t6 = new Thread06("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","f.jpg");
        Thread06 t7 = new Thread06("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","g.jpg");
        Thread06 t8 = new Thread06("https://img2.baidu.com/it/u=3502644715,3610019172&fm=253&fmt=auto&app=120&f=JPEG?w=1000&h=615","h.jpg");


        //1.创建执行任务(线程池)
        ExecutorService service = Executors.newFixedThreadPool(8);//执行8个子线程
        //2.提交执行
        Future<Boolean> future1 =service.submit(t1);
        Future<Boolean> future2 =service.submit(t2);
        Future<Boolean> future3 =service.submit(t3);
        Future<Boolean> future4 =service.submit(t4);
        Future<Boolean> future5 =service.submit(t5);
        Future<Boolean> future6 =service.submit(t6);
        Future<Boolean> future7 =service.submit(t7);
        Future<Boolean> future8 =service.submit(t8);
        //3.获取结果
        boolean rs1 = future1.get();
        boolean rs2 = future2.get();
        boolean rs3 = future3.get();
        boolean rs4 = future4.get();
        boolean rs5 = future5.get();
        boolean rs6 = future6.get();
        boolean rs7 = future7.get();
        boolean rs8 = future8.get();
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        System.out.println(rs4);
        System.out.println(rs5);
        System.out.println(rs6);
        System.out.println(rs7);
        System.out.println(rs8);

        //关闭服务
        service.shutdownNow();

    }
}
//文件下载工具类!
class WebDownloader {
    //url==>网络路径,name==>存储的名字!
    public void downloader(String url , String name){
        try {
            //FileUtiIs文件工具,复制url到文件
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件下载失败!");
        }

    }

}

1674303128836


package com.lovi.thread;
/*
穿插代理模式是为了理解,Thread这个类用了代理模式的,实现了Runnable接口.

而代理模式
1. 真实对象和代理对象都要实现同一个接口!

代理模式的好处
1. 代理对象可以帮真实对象做很多哦真实对象做不到的事情

本例子为小明结婚
婚庆公司代理他结婚的事情,小明只要人在就行

 */
public class TestProxy {
    public static void main(String[] args) {
        //婚庆公司
        new WeddingCompany(new XiaoMing()).startMarry();
        //联系到线程,其实Thread也用了代理模式.
        /*
        Thread实现了Runable接口,而真实的线程,也可以实现Runable接口
        最终的调用就是new Thread(thread05,"兔子").start();
        和婚庆公司这个new WeddingCompany(new XiaoMing()).startMarry();
        就是异曲同工之妙!就是用的代理模式嗷
         */
        
    }
}
//结婚接口
interface Marry{
    //开始结婚
    void startMarry();
}
//真实对象==>小明
class XiaoMing implements Marry{

    @Override
    public void startMarry() {
        System.out.println("小明开始结婚!");
    }
}
//代理对象==>婚庆公司
class WeddingCompany implements Marry{
    //要结婚的人为target
    private Marry target;
    //初始化,要结婚的人
    public WeddingCompany(Marry target){
        this.target=target;
    }
    @Override
    public void startMarry() {
        //结婚之前
        before();
        //结婚
        target.startMarry();
        //结婚之后
        after();
    }
    public void before(){
        System.out.println("结婚之前,婚庆公司帮忙布置场景等");
    }
    public void after(){
        System.out.println("结婚之后,婚庆公司帮忙收拾一地的垃圾");
    }
}

1674305219232

lambda表达式

1674411783541

推导lambda表达式

package com.lovi.thread;
/*
lambda表达式
这里主要是学习简化的思路,一步一步的推导lambda表达式
推导
    1.传统方法
    2.静态内部类
    3.局部内部类
    4.匿名内部类
    5.lambda表达式
 */
public class TestLambda {
    //4.静态内部类
    static class Lambda02 implements ILike{

        @Override
        public void likeLambda() {
            System.out.println("I like lambda2");
        }
    }

    public static void main(String[] args) {
        //3.传统方法实现
        ILike like = new Lambda01();
        like.likeLambda();
        //静态内部类
        like = new Lambda02();
        like.likeLambda();

        //5. 局部内部类
        class Lambda03 implements ILike{

            @Override
            public void likeLambda() {
                System.out.println("I like lambda3");
            }
        }
        like = new Lambda03();
        like.likeLambda();
        //6. 匿名内部类,没有类的名称,必须借助接口或者父类
        like = new ILike() {
            @Override
            public void likeLambda() {
                System.out.println("I like lambda4");
            }
        };
        like.likeLambda();
        //7. lambda表达式(简化),没有接口的名字也没有方法的名字,直接整
        like = () -> {
            System.out.println("I like lambda5");
        };
        like.likeLambda();



    }
}
//1.定义函数式接口(接口只有一个方法)
interface ILike{
    void likeLambda();
}
//2.实现接口
class Lambda01 implements ILike{

    @Override
    public void likeLambda() {
        System.out.println("I like lambda1");
    }
}

1674307752644

简化

package com.lovi.thread;
//lambda表达式的再次理解
public class TestLambda2 {

    public static void main(String[] args) {
        ILove love = null;

        //lambda表达式
        love = (int i)->{
            System.out.println("I love u ==>"+i);
        };
        love.love(5);
        // 简化1==>去掉参数类型.
        love = (i)->{
            System.out.println("I love u ==>"+i);
        };
        love.love(6);
        // 简化2==>去掉括号,(只有单参数才可以,多参数不可以去掉)
        love = i->{
            System.out.println("I love u ==>"+i);
        };
        love.love(7);
        // 简化3==>去掉花括号,(只有一行代码语句才可以,多行不可以)
        love = i->System.out.println("I love u ==>"+i);
        love.love(8);
        
    }
}
//函数式接口
interface ILove{
    void love(int i);
}

1674315677693

3.线程状态

1674411802373

1674411828126

线程方法

1674411871740

线程停止

package com.lovi.thread;
/*
线程停止
不推荐使用JDK提供的stop()、destroy()方法。【已废弃】
推荐线程自己停止下来
建议使用一个标志位进行终止变量
当flag=false,则终止线程运行。
 */
public class Thread07 implements Runnable{
    //1.设置一个标志位
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        //2.线程体中使用标志
        while (flag){
            System.out.println("线程正在跑..."+i++);
        }

    }
    //3.设置一个方法来停止线程,改变标志位
    public void stop(){
        this.flag=false;
    }
    public static void main(String[] args) throws InterruptedException {
        Thread07 thead07 = new Thread07();
        new Thread(thead07).start();
        //4.让线程正常停止!
        for (int i = 0; i < 70001; i++) {
            if(i==70000){
                thead07.stop();
                System.out.println("线程停止!");
            }
        }

    }
}

结果

线程正在跑...0
线程正在跑...1
线程正在跑...2
线程正在跑...3
线程正在跑...4
线程正在跑...5
线程正在跑...6
线程正在跑...7
线程正在跑...8
线程正在跑...9
线程正在跑...10
线程正在跑...11
线程正在跑...12
线程正在跑...13
线程正在跑...14
线程正在跑...15
线程正在跑...16
线程停止!

Process finished with exit code 0


线程休眠

  • sleep (时间)指定当前线程阻塞的毫秒数;
  • sleep存在异常InterruptedException;
  • sleep时间达到后线程进入就绪状态;
  • sleep可以模拟网络延时,倒计时等。
  • 每一个对象都有一个锁,sleep不会释放锁;

模拟倒计时

package com.lovi.thread;
//模拟倒计时!sleep方法
public class Thread08 {
    public static void main(String[] args) throws InterruptedException {
        countDown();//设为静态方法就可以在main方法直接调用!(因为main方法就是静态方法)
    }
    //倒计时方法
    public static void countDown() throws InterruptedException {
        //一共10秒,线程每秒睡一下!
        int nums=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(nums);
            nums--;
            if(nums<=0){
                break;
            }
        }
    }
}

结果

10
9
8
7
6
5
4
3
2
1

打印系统每秒时间

package com.lovi.thread;


import java.text.SimpleDateFormat;
import java.util.Date;

//每秒打印一下系统当前时间,sleep方法
public class Thread09 {
    public static void main(String[] args) throws InterruptedException {
        Date nowTime = new Date(System.currentTimeMillis());//系统当前时间
        while (true){
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("hh:mm:ss").format(nowTime));//给时间格式化
            nowTime = new Date(System.currentTimeMillis());
        }

    }

}

1674364437943

线程礼让

package com.lovi.thread;
/*
礼让不一定成功,看cpu心情
 */

//线程礼让yield
public class Thread10 implements Runnable{
    public static void main(String[] args){
        Thread10 thread10 = new Thread10();
        new Thread(thread10,"a").start();
        new Thread(thread10,"b").start();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//线程礼让.
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

这是礼让成功的结果(不一定成功)看cpu调度

1674364873984

如果取消礼让(并且是单核的情况下)

那么就是aabb或者bbaa,不会是ab交替

线程join(插队!并且运行到停止)

package com.lovi.thread;
/*
线程插队!
这里是一般都是先执行主线程.然后
让主线程执行到一半让子线程插队!
(插队之前:主线程和子线程交替执行)
插队之后就是,得插队的运行完才可以跑其他线程
 */

//线程插队join
public class Thread11 implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread11 thread11 = new Thread11();
        Thread thread = new Thread(thread11);
        thread.start();
       //主线程准备跑
        for (int i = 0; i < 200; i++) {
            System.out.println("主线程在运行"+i);
            if(i==150){
                //子线程插队
                thread.join();
            }
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程vip插队来了!"+i);
        }
    }
}

1674365929727

这里主线程和子线程在150之前交替运行.150之后就执行完子线程的方法再运行主线程的剩下的


线程状态

package com.lovi.thread;
/*
线程状态
新生
就绪
运行
阻塞
死亡
 */

//线程插队join
public class Thread12{
    public static void main(String[] args) throws InterruptedException {
        //让线程每秒睡一下,循环结束后打印////
        Thread thread = new Thread(
                ()->{
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("////");
                }
        );
        //观察线程状态
        Thread.State state = thread.getState();
        System.out.println(state);//NEW

        //线程启动后
        thread.start();
        state = thread.getState();
        System.out.println(state);//Run

        //只要线程没终止,就一直输出状态!
        while (state!=Thread.State.TERMINATED){
            Thread.sleep(2000);
            state = thread.getState();//更新线程状态
            System.out.println(state);//输出
        }
    }


}

1674366993646

注意:同一个线程停止之后就不能在运行了(死亡的线程不能复生)

线程优先级

package com.lovi.thread;
//线程优先级,优先级高的获得cpu先调度的概率就高,线程优先级只有1-10
public class Thread13{
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"==>"+Thread.currentThread().getPriority());

        //创建子线程
        TestPriority testPriority = new TestPriority();
        Thread t1 = new Thread(testPriority);
        Thread t2 = new Thread(testPriority);
        Thread t3 = new Thread(testPriority);
        Thread t4 = new Thread(testPriority);
        Thread t5 = new Thread(testPriority);

        //先设置优先级再执行!
        t1.setPriority(Thread.MIN_PRIORITY);
        t1.start();

        t2.setPriority(4);
        t2.start();

        t3.setPriority(9);
        t3.start();

        t4.setPriority(3);
        t4.start();

        t5.setPriority(Thread.MAX_PRIORITY);
        t5.start();


    }


}
class TestPriority implements Runnable{

    @Override
    public void run() {
        //打印出线程的名字和优先级
        System.out.println(Thread.currentThread().getName()+"==>"+Thread.currentThread().getPriority());
    }
}

1674369869330

守护线程

package com.lovi.thread;
/*
上帝守护你
上帝类
你类
上帝守护你这个线程安稳结束
守护线程守护用户线程正常执行
setDaemon
 */
//守护线程
public class Thread14 {
    public static void main(String[] args) {
        //创建线程
        God god = new God();
        You you = new You();
        Thread thread1 = new Thread(god);
        Thread thread2 = new Thread(you);
        //设置守护线程
        thread1.setDaemon(true);
        thread1.start();
        thread2.start();
    }
}
//god
class God implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("上帝守护你");
        }
    }
}
//you
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我每天都开开心心的活着!");
        }
        System.out.println("============Good bye ! World!============");
    }
}

1674371018080

4.线程同步

  • 并发 : 同一个对象被多个线程同时操作
    • 上万人同时抢100张票
    • 两个银行同时取钱
  • 线程同步
    • 处理多线程问题时 , 多个线程访问同一个对象 , 并且某些线程还想修改这个对象 . 这时候我们就需要线程同步 . 线程同步其实就是一种等待机制 , 多个需要同时访问
      此对象的线程进入这个对象的等待池 形成队列, 等待前面线程使用完毕 , 下一个线程再使用
    • 形成条件:队列+锁
    • 同步方法
      • public synchronized void method(int args) {}
    • 同步块
      • synchronized (Obj )

三大线程不安全案例(没有同步)

抢票

package com.lovi.thread.unsafe;
/*
线程不安全案例一
抢票
 */
public class Test001{
    public static void main(String[] args) {
        BuyTickets test001 = new BuyTickets();
        new Thread(test001,"我").start();
        new Thread(test001,"你").start();
        new Thread(test001,"她").start();

    }


}
class BuyTickets implements Runnable{
    //票!
    private int ticketNums=10;
    private boolean flag = true;//标志位
    @Override
    public void run() {
        //执行买票
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
    //买票
    private void buy() throws InterruptedException {

        //判断是否有票
        if (ticketNums<=0){
            flag=false;
            return;
        }
        //抢票
        System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums+"张票");
        ticketNums--;

    }

}

1674391576425

比较乱而且有重复!


银行取钱

package com.lovi.thread.unsafe;

/*
场景!你和女朋友去银行取钱,账户一共有100万,你取50万,女友取100万,看看情况!
 */
//银行取钱
public class Test002 {
    public static void main(String[] args) {
        Account account = new Account(100,"恋爱小金库");
        Bank bank1 = new Bank(account,50,"小明");
        Bank bank2 = new Bank(account,100,"小红");
        new Thread(bank1).start();
        new Thread(bank2).start();
    }
}
//账户
class Account{
    int money;
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行
class Bank implements Runnable{
    Account account;
    int drawMoney;//要取的钱
    int restMoney;//银行卡里剩下的余额
    String name;//取钱的人

    public Bank(Account account, int drawMoney, String name) {
        this.account = account;
        this.drawMoney = drawMoney;
        this.restMoney = this.account.money-drawMoney;
        this.name = name;

    }

    @Override
    public void run() {
        //先判断有没有钱!
        if(account.money-drawMoney<0){
            System.out.println("钱不够了!取不了钱了!");
            return;
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //取钱
        System.out.println(this.name+"取了"+drawMoney);
        //取钱之后要及时更新
        account.money=account.money-drawMoney;
        System.out.println("目前的卡内余额为:"+account.money);
    }
}

1674388809222


集合

package com.lovi.thread.unsafe;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class Test003{
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());//每执行一次for循环就把线程加到集合里一次
            }).start();
        }
        
        //打印最终集合的大小
        System.out.println(list.size());
    }
}

1674389526987

线程不安全体现在,到达不了10000,因为,太快了,多个线程操控同一个位置的时候,把两个线程加到同一个集合位置了.


线程同步--修改不安全案例

对于不安全案例一这样改即可

1674391839396

同步默认锁的是this,也就是当前类


对于不安全案例2

1674392224519

得到

1674392262538

哪个类的属性会发生变化,就锁哪个类的对象!


对于不安全案例三

关键点

1674393254893

1674393266195

package com.lovi.thread.unsafe;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class Test003{
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());//每执行一次for循环就把线程加到集合里一次
                }
            }).start();
        }
        //Thread.sleep(1);//打印之前拖延一下主线程的结束时间,避免主线程在还没接收到最终的size的时候就结束了
        //打印最终集合的大小
        System.out.println("=====打印最终的集合大小咯!=========");//也可以用打印这个拖延,一样可以
        System.out.println(list.size());

    }
}

1674393230620

扩展JUC

package com.lovi.thread;

import java.util.concurrent.CopyOnWriteArrayList;

//测试JUC安全类型的集合
public class Thread15 {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                    list.add(Thread.currentThread().getName());//每执行一次for循环就把线程加到集合里一次
            }).start();
        }
        Thread.sleep(1);//打印之前拖延一下主线程的结束时间,避免主线程在还没接收到最终的size的时候就结束了
        System.out.println(list.size());

    }
}

1674393588854

死锁

死锁:多个线程各自占有一些共享资源 , 并且互相等待其他线程占有的资源才能运行 , 而导致两个或者多个线程都在等待对方释放资源 , 都停止执行的情形 . 某一个同步块同时拥有 “ 两个以上对象的锁 ” 时 , 就可能会发生 “ 死锁 ” 的问题 。

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件 : 进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件 : 若干进程之间形成一种头尾相接的循环等待资源关系。

避免死锁:破环四个条件的一个或多个。


死锁案例

package com.lovi.thread;

/*
案例,化妆
 */
//测试死锁==>多个对象互相锁着一个资源的同时又锁着拿着另一个对象拿到的资源
public class TestDeadLock {
    public static void main(String[] args) {
        MakeUp makeUp1 = new MakeUp(0,"小白");
        MakeUp makeUp2 = new MakeUp(1,"小黑");
        new Thread(makeUp1).start();
        new Thread(makeUp2).start();
    }

}
//口红
class Lipstick{

}
//镜子
class Mirror{

}
//化妆
class MakeUp implements Runnable{
    //static 由于只初始化一次,因此可以保证只拿到一个资源
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String name;//使用化妆品的人

    public MakeUp(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆!
    public void makeup() throws InterruptedException {
        if(choice==0){
            synchronized (lipstick){
                System.out.println(this.name+"获得口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){//在有口红锁的前提下锁镜子
                    System.out.println(this.name+"获得镜子的锁");
                }
            }


        }else {
            synchronized (mirror){
                System.out.println(this.name+"获得镜子的锁");
                Thread.sleep(2000);
                synchronized (lipstick){
                    System.out.println(this.name+"获得口红的锁");
                }

            }

        }
    }
}

1674396751295

死锁关键点(拿着一个还锁另一个)

1674396896048

解决(把另一个拿出来)

1674396932357

Lock

关键点

//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
try里上锁
lock.lock();//上锁
把不安全代码放进去
finally解锁
lock.unlock();//解锁

package com.lovi.thread;


import java.util.concurrent.locks.ReentrantLock;

public class TestLock implements Runnable{
    private int ticketNums = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();//上锁
                
                //不安全代码放进去
                
                //设置跳出循环的条件
                if(ticketNums <= 0){
                    break;
                }
                //获取当前线程的名字==>Thread.currentThread().getName()
                System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums--+"张票!");

            }finally {
                lock.unlock();//解锁
            }
            //模拟延时,让线程睡一下(1秒)!
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("线程睡觉出了问题");
            }

        }

    }

    public static void main(String[] args) {
        TestLock t1 = new TestLock();
        new Thread(t1,"小白").start();
        new Thread(t1,"小红").start();
        new Thread(t1,"小黄").start();
    }
}

synchronized 与 Lock 的对比

1674412777453

4.线程通信

Java提供了几个方法解决线程之间的通信问题

1674412994270

注意 : 均是Object类的方法 , 都只能在同步方法或者同步代码块中
使用,否则会抛出异常IllegalMonitorStateException


管程法:
package com.lovi.thread.advanced;
//生产者消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class Test1 {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Producer(container).start();
        new Consumer(container).start();
    }
}
//生产者--->只需要管生产鸡
class Producer extends Thread{
    //需要缓冲区
    SynContainer container = new SynContainer();
    public Producer(SynContainer synContainer){
        this.container = synContainer;
    }
    //生产

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                container.push(new Chicken(i));
                System.out.println("生产了id为"+i+"的鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//消费者--->只需要管消费鸡
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消费了id为"+container.pop().id+"的鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//产品
class Chicken{
    int id;//产品编号
    public Chicken(int id){
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //需要一个容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数
    int count = 0;
    //生产者放入产品(放入具体的鸡)
    public synchronized void push(Chicken chicken) throws InterruptedException {
        //如果缓冲区满了,就等待生产者消费
        if(count == chickens.length){
            this.wait();
        }
        //如果没有满就继续放入产品(把具体的鸡给具体的缓冲区的位置)
        chickens[count]=chicken;
        count++;
        //有鸡了之后就通知消费者来消费!
        this.notifyAll();
    }
    //消费者消费产品
    public synchronized Chicken pop() throws InterruptedException {
        //判断能否消费,不能就等待生产
        if(count==0){
            this.wait();//等待生产者生产!
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];//消费掉的具体的鸡,并且返回

        //吃完一个之后又通知生产者生产!
        this.notifyAll();

        return chicken;
    }
}

1674404612435

信号灯法
package com.lovi.thread.advanced;
/*
关键点是,flag

演员表演节目
观众观看节目
(这个节目不是直播)
需要电视机来实现这个过程

演员表演,观众等待 //flag=T 的时候表演
观众观看,演员等待 //flag=F 的时候观看
 */
//生产者消费者模式-->信号灯法
public class Test2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Actor(tv).start();
        new Audience(tv).start();
    }

}
//演员只管表演
class Actor extends Thread{
    TV tv;
    public Actor(TV tv){
        this.tv=tv;
    }
    //表演


    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(i%2==0){
                try {
                    this.tv.play("小品");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                try {
                    this.tv.play("广告");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//观众只管观看
class Audience extends Thread{
    TV tv;
    public Audience(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                tv.watch();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
//电视机
class TV{
    boolean flag = true;//定义标志信号
    String program;//表演的节目
    //演员表演节目!
    public synchronized void play(String program) throws InterruptedException {
        //判断是否需要表演
        if(!flag){
            this.wait();
        }
        System.out.println("============================");
        //表演节目
        System.out.println("演员表演了"+program);
        //通知观众观看
        this.notifyAll();
        //并且把节目放到电视机里播放
        this.program = program;
        this.flag=!this.flag;
    }
    //观众观看节目
    public synchronized void watch() throws InterruptedException {
        //判断是否需要观看
        if(flag){
            this.wait();
        }
        //观看节目
        System.out.println("观众观看了"+program);
        //通知演员表演
        this.notifyAll();
        this.flag=!this.flag;
    }
}

1674408111866

这里注意演员的run和观众的run的fori次数要一致!

5.线程池

1674413105319

1674413126046

案例

package com.lovi.thread.advanced;

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

//测试线程池
public class Test3 {
    public static void main(String[] args) {
        //1.创建服务,线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //2.执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //3.关闭连接
        service.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

结果

pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-5
pool-1-thread-4

6.小结

package com.lovi.thread;

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

//回顾线程创建的几种方式
public class End {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //线程启动
        new MyThread01().start();
        new Thread(new MyThread02()).start();

        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread03());
        new Thread(futureTask).start();

        //获取Callable返回值
        Integer i = futureTask.get();
        System.out.println(i);
    }
}
//1.继承Thread类
class MyThread01 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread01");
    }
}
//2.实现Runnable接口(推荐)
class MyThread02 implements Runnable{

    @Override
    public void run() {
        System.out.println("MyThread02");
    }
}
//2.实现Callable接口
class MyThread03 implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread03");
        return 520;
    }
}

结果

MyThread01
MyThread02
MyThread03
520

(六)网络编程

网络编程也称为套接字编程 socket编程

参考视频:https://www.bilibili.com/video/BV1LJ411z7vY

参考博客:https://www.kuangstudy.com/bbs/1448246328915161090

1.1、概述

信件:

img

计算机网络:

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接到一起,在网络操作系统和网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络编程的目的:

无线电台…传播交流信息,数据交换,通信。

想要达到这个效果需要什么:

  1. 如何准确的定位一台计算机主机 192.168.16.124:端口,定位到这台计算机的某个资源上
  2. 找到了这个主机,如何传输数据?

javaWeb: 网页编程 B/S架构

网络编程: TCP/IP C/S架构

1.2、网络通信的要素

如何实现网络的通信?

通信双方的地址:

  • IP
  • 端口号
  • 192.168.16.124:5900

规则:网络通信的协议

TCP/IP参考模型:

img

  • 应用层

    HTTP:超文本传输协议

    FTP:文件上传协议

    SMTP:邮件发送协议

    Telnet:远程登录协议

    DNS:域名解析

  • 传输层:TCP、UDP

  • 网络层:封包

  • 数据链路层:无线/有线

小结:

  1. 网络编程中的两个主要问题
    • 如何准确的定位到一台或者多台计算机主机
    • 找到主机之后如何进行通信
  2. 网络编程中的要素
    • IP和端口号 IP类
    • 网络通信协议 udp,tcp
  3. 万物皆对象

1.3 代码实践

Tcp实现发送消息(客户端发送,服务端接收)

  • TcpClient
package com.lovi.base.net;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

//tcp客户端
public class TcpClient {
    public static void main(String[] args) throws Exception{
        //1.设置地址和端口,连接上tcp
        InetAddress serverIp = InetAddress.getByName("127.0.0.1");
        int port = 666;
        while (true){
            Socket socket = new Socket(serverIp, port);//连接靠这一条!
            //2.输出内容!给服务端读取!(获得一个输出流)
            OutputStream os = socket.getOutputStream();
            Scanner scanner = new Scanner(System.in);
            os.write(scanner.nextLine().getBytes());

            //3.关闭!
            os.close();
            socket.close();
        }

    }
}
  • TcpServer
package com.lovi.base.net;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

//tcp服务端
public class TcpServer {
    public static void main(String[] args) throws Exception{
        //1.创造一个连接(有地址)
        ServerSocket serverSocket = new ServerSocket(666);
        while (true){
            //2.接收连接(等待客户端连接过来)
            Socket socket = serverSocket.accept();

            //3.读取内容!(一个输入流!)
            InputStream is = socket.getInputStream();
            //3.1,用管道流来读取!
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.println(baos);//管道流打印输出

            //4.关闭(先开后关原则)
            baos.close();
            is.close();
            socket.close();
        }



    }
}

1674202327403

tcp实现文件上传(客户端发送文件,服务端接收并输出文件)

TcpClient02

package com.lovi.base.net;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

//客户端发送文件
public class TcpClient02 {
    public static void main(String[] args) throws Exception{
        //连接上!(对应服务端的端口号和ip地址)
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 800);
        //发送文件分为,1.读取要发送的文件(输入流),2.把要发送的文件写出去!(输出流)
        FileInputStream fis = new FileInputStream("1.jpg");
        OutputStream os = socket.getOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer))!= -1){
            //把读到的东西通过字节流写出去!!
            os.write(buffer,0,len);
        }
        //2.1 通知服务端,已经发送完成(停止输出)
        socket.shutdownOutput();
        //2.2 接受服务端已经接收完毕的通知
        InputStream is = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer2 = new byte[1024];
        int len2;
        while ((len2 = is.read(buffer2))!=-1){
            baos.write(buffer2,0,len2);
        }
        System.out.println(baos);

        //关闭
        os.close();
        fis.close();
        socket.close();

    }
}

TcpServer02

package com.lovi.base.net;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

//服务端接收并输出文件
public class TcpServer02 {
    public static void main(String[] args) throws Exception{
        //设置地址端口
        ServerSocket serverSocket = new ServerSocket(800);
        //等待客户端的连接
        Socket socket = serverSocket.accept();
        //读取文件(读取到的东西输出成文件)
        InputStream is = socket.getInputStream();
        FileOutputStream fos = new FileOutputStream("1-copy.jpg");
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }
        //告诉服务端,已经接受完成,可以关闭服务了
        OutputStream os = socket.getOutputStream();
        os.write("服务端:已经接受完成,可以关闭服务了".getBytes());

        //关闭
        os.close();
        fos.close();
        is.close();
        socket.close();

    }
}

根目录下

1674202481697

udp实现发送消息与接收消息

UdpClient01

package com.lovi.base.net;

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

//udp发送消息,客户端
public class UdpClient01 {
    public static void main(String[] args) throws Exception{
        //1.建立socket
        DatagramSocket socket = new DatagramSocket();
        //2.建包!+发送数据(数据,数据起始,数据长度,要发送的ip地址和端口)
        String msg = "客户端发送了一条消息!(消息内容为:你好呀)";
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, InetAddress.getByName("localhost"), 9000);

        //3.发送数据
        socket.send(packet);
        //4.关闭!
        socket.close();
    }
}

UdpServer01

package com.lovi.base.net;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

//udp发送消息,服务端
public class UdpServer01 {
    public static void main(String[] args) throws Exception{
        //开放端口,给用户发送
        DatagramSocket socket = new DatagramSocket(9000);
        //接收数据包!
        byte[] buffer = new byte[1024];//这里需要保证,buffer足够大,不然会接收不全!
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        socket.receive(packet);//阻塞式接受

        // 把接收到的数据包的信息在控制台打印出来
        System.out.println(packet.getAddress().getHostAddress());//查看发送的主机地址
        System.out.println(new String(packet.getData()));//查看发送的具体内容

        //关闭
        socket.close();

    }
}

1674202599620

udp实现发送消息与接收消息(控制台循环版)

UdpSender

package com.lovi.base.net;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UdpSender {
    public static void main(String[] args) throws Exception{
        //1.建立包裹
        DatagramSocket socket = new DatagramSocket();
        //2. 准备数据,控制台读取 System.in(建立缓冲输入流)
        /*
        输入流读取控制台输入的信息!然后放到缓冲区里读取出来
         */
        while (true){
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            String data = reader.readLine();//读取一行一行的数据
            byte[] buffer = data.getBytes();
            DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, InetAddress.getByName("localhost"), 700);

            //3.发送消息
            socket.send(packet);
            if(data.equals("bye")){
                break;
            }

        }
        //4.关闭
        socket.close();



    }
}

UdpReceiver

package com.lovi.base.net;


import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiver {
    public static void main(String[] args) throws Exception{

        //1.接收包裹,的端口号设置好
        DatagramSocket socket = new DatagramSocket(700);
        //2.准备接收包裹
        while (true){
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
            socket.receive(packet);//阻塞式接收包裹

            //3. 断开连接 bye
            byte[] data = packet.getData();
            String receiveData = new String(data, 0, data.length);
            System.out.println(receiveData);
            if (receiveData.equals("bye")) {
                break;
            }
        }

        //4.关闭
        socket.close();


    }

}

1674202734385

注意:

如果控制台格式丑(如下图,就把那个红色框框那个按掉就可以了)

1674464055866

udp实现聊天+结合多线程!

发送端

package com.lovi.base.net.chat;

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

public class UdpSender01 implements Runnable{
    DatagramSocket socket = null;
    BufferedReader reader = null;

    //ip地址
    String toIP;
    //端口
    int toPort;
    //发送方的名字
    String fromName;

    public UdpSender01(String toIP, int toPort, String fromName) {
        this.toIP = toIP;
        this.toPort = toPort;
        this.fromName = fromName;
    }

    @Override
    public void run() {

        while (true){

            try {
                //1.建立包裹
                socket = new DatagramSocket();
                //2. 准备数据,控制台读取 System.in(建立缓冲输入流)
                reader = new BufferedReader(new InputStreamReader(System.in));
                String data = null;//读取一行一行的数据
                data = fromName+":"+reader.readLine();
                byte[] buffer = data.getBytes();
                DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, InetAddress.getByName(toIP), toPort);

                //3.发送消息
                socket.send(packet);
                if(data.equals("bye")){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //4.关闭
        socket.close();

    }
}
package com.lovi.base.net.chat;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiver01 implements Runnable{
    DatagramSocket socket=null;
    //发送方的端口!
    int fromPort;

    public UdpReceiver01(int fromPort) {
        this.fromPort = fromPort;
    }

    @Override
    public void run() {
        try {
            //1.接收包裹,的端口号设置好
            socket = new DatagramSocket(fromPort);
            //2.准备接收包裹
            while (true){
                byte[] buffer = new byte[1024];
                DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
                socket.receive(packet);//阻塞式接收包裹

                //3. 断开连接 bye
                byte[] data = packet.getData();
                String receiveData = new String(data, 0, data.length);
                System.out.println(receiveData);
                if (receiveData.equals("bye")) {
                    break;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        //4.关闭
        socket.close();

    }
}

测试

package com.lovi.base.net.chat;
//教师端!
public class TalkTeacher {
    public static void main(String[] args) {
        //发送
        new Thread(new UdpSender01("localhost",666,"老师1号")).start();
        //接收
        new Thread(new UdpReceiver01(999)).start();
    }
}
package com.lovi.base.net.chat;
//学生端!
public class TalkStudent {
    public static void main(String[] args) {
        //发送
        new Thread(new UdpSender01("localhost",999,"学生1号")).start();
        //接收
        new Thread(new UdpReceiver01(666)).start();
    }
}

1674465637453

URL

url的基本结构

传输协议://主机名:端口号/文件名 #片段名?参数列表

基础方法

package com.lovi.base.net;

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

public class URL01 {
    public static void main(String[] args) throws MalformedURLException {
        //伪造一个url
        URL url = new URL("https://localhost:8089/helloworld/hello.jsp?username=Lovi&password=123");
        System.out.println(url.getProtocol());//协议
        System.out.println(url.getHost());//主机ip
        System.out.println(url.getPort());//端口
        System.out.println(url.getPath());//文件路径
        System.out.println(url.getFile());//全路径(不包括端口ip和协议)
        System.out.println(url.getQuery());//参数
    }
}

1674466899700

下载网络资源

package com.lovi.base.net;

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

//通过网络路径下载东西(类似爬虫)
public class URL02 {
    public static void main(String[] args) throws IOException {
        //1.下载地址
        URL url = new URL("https://m801.music.126.net/20230123181030/0d7095373fabde8b176a4af34d774237/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/19868466829/fef8/4413/b867/80540d0529f8ac21da31840b6aea5707.m4a");
        //2.连接到这个资源
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        //3.读取资源
        InputStream is = urlConnection.getInputStream();
        //4.写出到本机
        FileOutputStream fos = new FileOutputStream("D:\\AAA\\Red Eye.m4a");
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer))!=-1){
            fos.write(buffer,0,len);//写出到本地
        }
        //5.关闭
        fos.close();
        is.close();
        urlConnection.disconnect();//断开连接
        System.out.println("下载成功");
    }
}

1674467572107

1674467628802

(七)注解和反射

参考视频:https://www.bilibili.com/video/BV1p4411P7V3/

参考笔记:https://www.cnblogs.com/gh110/p/15153665.html

(八)JVM

参考视频:https://www.bilibili.com/video/BV1iJ411d7jS/

参考博客:https://www.cnblogs.com/gh110/p/14917326.html

posted @ 2023-01-29 15:11  Lovi*  阅读(491)  评论(0)    收藏  举报