JazzEE(2)

news2025/4/7 16:19:44

JazzEE(2)

  • 8、异常
    • 引入try-catch
    • catch中如何处理异常
    • try-catch-finally
    • 多重catch
    • 异常的分类
    • throw和throws区别
      • 小案例
    • 重载和重写的异常处理
    • 自定义异常
  • 9、常用类
    • 包装类
      • 引入
      • Integer
    • String类
      • String字符串内存
    • StringBuilder类
      • 可变和不可变
      • 常见方法
      • StringBuffer和StringBuilder区别
    • 时间处理的类
      • Date
      • 日期转换
      • Calendar
    • Math
    • 枚举类
  • 10、集合
    • 数据结构
    • 集合引入
    • Collection接口
      • 第一个子接口List
        • 实现类:ArrayList
          • 源码
          • 自己模拟一个ArrayList
          • 将自定义的引用数据类型存入ArrayList集合
        • 实现类:Vector
        • 泛型
          • 基本应用
          • 扩展:
            • 泛型类:
            • 泛型方法:
            • 泛型接口:
            • 泛型受限:
        • 实现类:LinkedList
          • 基本代码
          • 扩展
          • 模拟LinkedList
        • 面试:iterator(),Iterator,Iterable关系
        • ListIterator
      • 第二个子接口Set
          • HashSet
            • 深入理解HashSet
            • 复习
          • TreeSet
      • 完整结构图
    • Map接口
      • HashMap
      • TreeMap
      • Map结构图
      • 源码
        • HashMap,HashSet
        • TreeMap,TreeSet
          • 验证红黑树
      • Collections工具类
  • 11、IO流
    • File类
    • IO的引入
    • 最终:文件的复制
      • 分解:文件----》程序
      • FileInputStream
      • 分解:程序----》文件
      • FileOutputStream
      • 将上面两步骤合为一起:完成复制
      • 编码总结
    • 节点流,处理流:BufferedInputStream,BufferedOutputStream
    • 字符流:FileReader,FileWriter:文件的复制
    • System类对IO流的支持
    • 转换流
    • 数据流:专门操纵基本数据类型的
    • 对象流:专门操纵引用数据类型的
    • 文件夹的复制
  • 12、多线程
    • 程序,进程,线程
    • 线程创建的三种方式
      • 第一种:继承Thread类
        • 设置线程名字
        • 龟兔赛跑
        • 买火车票
      • 第二种:实现Runnable接口
        • 扩展:静态代理模式
        • 龟兔赛跑
        • 买火车票
      • 第三种:实现Callable接口
      • 总结
    • 线程的生命周期
    • 控制线程常用方法
      • 优先级
      • join
      • sleep
      • yield()
      • setDaemon
      • stop
    • 线程安全、通信,线程池
      • 线程安全问题
        • 习题
        • 线程同步的缺点
        • Lock锁
      • 线程组
      • 线程之间的通信问题
        • 分解1:
        • 分解2:
        • 分解3:
      • 线程池
        • 队列
        • 线程池
        • 内置线程池

8、异常

引入try-catch

【1】完成求商功能:

public class Test {
    public static void main(String[] args) {
        //功能:键盘录入两个数,求商。
        //拿来一个扫描器:
        Scanner sc=new Scanner(System.in);
        System.out.print("请从键盘录入第一个数:");
        int num1=sc.nextInt();
        System.out.print("请从键盘录入第二个数:");
        int num2=sc.nextInt();
        //求商:
        System.out.println("商为:"+num1/num2);
    }
}

【2】测试:
在这里插入图片描述

在测试过程中,发现程序很可能出现问题。
在正常的程序中出现不正常的现象—》异常。
【3】将程序的漏洞都堵上:处理异常:if_else形式

public class Test {
    public static void main(String[] args) {
        //功能:键盘录入两个数,求商。
        //拿来一个扫描器:
        Scanner sc=new Scanner(System.in);
        System.out.print("请从键盘录入第一个数:");
        if(sc.hasNextInt()){//判断第一个数是否是int类型数据
            int num1=sc.nextInt();
            System.out.print("请从键盘录入第二个数:");
            if(sc.hasNextInt()){//判断第二个数是否是int类型数据
                int num2=sc.nextInt();
                if(num2==0){//如果第二个数为0,那么进行提示:
                    System.out.println("除数不能为0!");
                }else{
                    //求商:
                    System.out.println("商为:"+num1/num2);
                }
            }else{
                System.out.println("请录入一个整数!");
            }
        }else{
            System.out.println("请录入一个整数!");
        }
    }
}

上面用if-else堵漏洞,有什么缺点吗?
(1)代码臃肿。
(2)即使我加了很多if-else进去,很可能出现一种情况就是有的漏洞没堵上。

【4】基于上面的缺点,java给我们提供了一种处理异常的机制:
"异常三连"try-catch-finally
【5】try-catch:
在上面的异常中,控制台上的异常怎么看:
(1)看第一行:异常的类型。
(2)看最后一行:异常出现的位置

上面的代码出现了几种异常:
(1)Exception in thread “main” java.lang.ArithmeticException: / by zero
(2)Exception in thread “main” java.util.InputMismatchException

捕获:处理:try-catch:

public class Test2 {
    public static void main(String[] args) {
        System.out.println("------开始使用计算器:-------");
        try{
            //功能:键盘录入两个数,求商。
            //拿来一个扫描器:
            Scanner sc=new Scanner(System.in);
            System.out.print("请从键盘录入第一个数:");
            int num1=sc.nextInt();
            System.out.print("请从键盘录入第二个数:");
            int num2=sc.nextInt();
            //求商:
            System.out.println("商为:"+num1/num2);
        }catch(Exception ex){
            System.out.println("你的程序有错啊!");
        }
        System.out.println("---谢谢你使用计算器---");
    }
}

可能出现情况:
(1)我的代码没有异常:那么只走try中代码,然后不走catch,程序继续往下走。
(2)我的代码有异常:
a.假如你的异常对象 catch后面()中的类型可以匹配,异常进行捕获,执行catch后面{}中的代码
b.假如你的异常对象 catch后面()中的类型不匹配,异常不可以进行捕获,直接抛出异常,程序中断,之后的代码也不会再执行了

catch中如何处理异常

public class Test2 {
    public static void main(String[] args) {
        System.out.println("------开始使用计算器:-------");
        try{
            Scanner sc=new Scanner(System.in);
            System.out.print("请从键盘录入第一个数:");
            int num1=sc.nextInt();
            System.out.print("请从键盘录入第二个数:");
            int num2=sc.nextInt();
            //求商:
            System.out.println("商为:"+num1/num2);
        }catch(Exception ex){
            //第一种:什么都不写。
            //第二种:自定义异常信息:
            //System.out.println("你的代码有问题!");
            //第三种:打印
            //System.out.println(ex);//java.util.InputMismatchException 证明Exception中重写了toString方法。
            //System.out.println(ex.toString());
            //System.out.println(ex.getMessage());打印:后面的异常信息
            //ex.printStackTrace();//异常的堆栈信息全部进行打印
            //第四种:
            //throw ex;将信息抛给调用者,在当前这个题里,调用者为虚拟机,那么就将处理异常的方式抛给
            //虚拟机了,虚拟机做了:(1)将异常堆栈信息进行打印 (2)中断程序
        }
        System.out.println("---谢谢你使用计算器---");
    }
}

try-catch-finally

/*
1.System.out.println("---谢谢你使用计算器---");在什么情况下不执行:
(1)throw ex;
(2)catch没有进行正常的异常捕获
(3)遇到return
2.怎么才能让System.out.println("---谢谢你使用计算器---");无论如何都执行:
finally
3.一般什么代码放入finally中:
关闭数据库资源,关闭IO流资源,关闭socket资源。
4.只有一种情况会让finally中的代码不执行:
System.exit(1);// 终止当前正在运行的 Java 虚拟机。
5.return和finally是否冲突?不冲突:
执行顺序先finally再走return
 */
public class Test3 {
    public static void main(String[] args) {
        System.out.println("------开始使用计算器:-------");
        try{
            Scanner sc=new Scanner(System.in);
            System.out.print("请从键盘录入第一个数:");
            int num1=sc.nextInt();
            System.out.print("请从键盘录入第二个数:");
            int num2=sc.nextInt();
            //求商:
            System.out.println("商为:"+num1/num2);
            //加上return
            return;
            //System.exit(0);// 终止当前正在运行的 Java 虚拟机。
        }catch(ArithmeticException ex){
            System.out.println("你的代码有问题!");
        }finally {
            System.out.println("---谢谢你使用计算器---");
        }
    }
}

多重catch

/*
多重catch:多个catch依次匹配执行,Exception接收放在最后,一般都是先子类,再父类。
 */
public class Test3 {
    public static void main(String[] args) {
        System.out.println("------开始使用计算器:-------");
        try{
            Scanner sc=new Scanner(System.in);
            System.out.print("请从键盘录入第一个数:");
            int num1=sc.nextInt();
            System.out.print("请从键盘录入第二个数:");
            int num2=sc.nextInt();
            //求商:
            System.out.println("商为:"+num1/num2);
        }catch(ArithmeticException ex){
            System.out.println("除数不能为0!");
        }catch(InputMismatchException ex){
            System.out.println("输入的类型不是整数类型!");
        }catch(Exception ex){
            System.out.println("代码出现问题!");
        }finally {
            System.out.println("---谢谢你使用计算器---");
        }
    }
}

在源码中看到,还有这种写法:
在这里插入图片描述

异常的分类

【1】层次:
在这里插入图片描述
【2】代码:

package com.bjsxt.test02;
public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
       //运行时异常:
        //int[] arr={1,2,3};
        //System.out.println(arr[3]);//运行时异常:ArrayIndexOutOfBoundsException
        //int num=10/0;
        //String s=null;
        //String newStr=s.toUpperCase();
        //System.out.println(newStr);
        //检查时异常:防患于未然,在我编写代码的时候直接就让我处理异常。
        //第一种处理方式:利用try-catch进行捕获。
        /*try {
            Class.forName("com.bjsxt.test01.Test").newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }*/
        //第二种处理方式:在方法的声明处将异常类型添加上,意味着别人在调用这个方法的时候:
        //告诉别人这个方法中可能会出现哪种错误。
        Class.forName("com.bjsxt.test01.Test").newInstance();
        //快捷键:alt+enter
    }
}

throw和throws区别


package com.bjsxt.test02;
import java.util.Scanner;
/*
throw跟throws区别:
(1)位置不同:
throw:方法内部
throws:方法的声明之后
(2)内容不同:
throw+异常对象
throws+异常的类型
(3)作用不同
throw:异常出现的源头
throws:在方法的声明处,告诉方法的调用者 这个方法中可能出现某种异常,那么谁调用谁处理。
 */
public class Test2 {
    //功能:两个数相除,当除数为0的时候 让程序出现异常。
    public static void main(String[] args)  {
        try {
            a();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void a() throws Exception {
            devide();
    }
    public static void devide() throws Exception {
        Scanner sc=new Scanner(System.in);
        System.out.print("请从键盘录入第一个数:");
        int num1=sc.nextInt();
        System.out.print("请从键盘录入第二个数:");
        int num2=sc.nextInt();
        if(num2==0){//除数为0
            //制造异常:
            //制造运行时异常:
            //throw new RuntimeException("除数不能为0");
            //制造检查时异常:
            throw new Exception("除数不能为0");
        }else{//不等于0 就输出商:
            System.out.println("商为:"+num1/num2);
        }
    }
}

小案例


package com.bjsxt.test03;
public class Student {
    private String name;
    private int age;
    private String sex;
    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 String getSex() {
        return sex;
    }
    public void setSex(String sex) throws Exception {
        if("男".equals(sex)||"女".equals(sex)){
            this.sex = sex;
        }else{
            //不正常,制造异常:
            //throw new RuntimeException("你的性别录入错误!");
            throw new Exception("你的性别录入错误!");
        }
    }
    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        //this.sex = sex;
        try {
            this.setSex(sex);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Student() {
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    public static void main(String[] args) {
        Student s=new Student();
        s.setAge(19);
        s.setName("lili");
        try {
            s.setSex("lkjhlkjhlkj");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(s);
        Student s2=new Student("feifei",20,"asdfasdf");
        System.out.println(s2);
    }
}

重载和重写的异常处理

【1】重载跟异常无关:

package com.bjsxt.test04;
//重载:跟异常无关
public class Test {
    public void a(int age) throws Exception{
    }
    public void a(String name) throws Exception{
    }
}

【2】重写跟异常:

package com.bjsxt.test04;
//重写:子类异常类型<=父类异常类型
public class Person {
    public void eat() throws Exception{
        System.out.println("----1");
    }
}
class Student extends  Person{
    public void eat() throws RuntimeException{
        System.out.println("------2");
    }
}

自定义异常

【1】自定义异常继承自Exception:

public class MyException extends Exception {
    public MyException() {
    }
    public MyException(String message) {
        super(message);
    }
}

public class Student {
    private String name;
    private int age;
    private String sex;
    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 String getSex() {
        return sex;
    }
    public void setSex(String sex) throws MyException {
        if("男".equals(sex)||"女".equals(sex)){
            this.sex = sex;
        }else{
            throw new MyException("性别错误!!!");
        }
    }
    public Student() {
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    public static void main(String[] args) {
        Student s=new Student();
        s.setAge(19);
        s.setName("lili");
        try {
            s.setSex("lkjhlkjhlkj");
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

【2】自定义异常继承RunTimeException:

public class MyException extends RuntimeException{
    public MyException() {
    }
    public MyException(String message) {
        super(message);
        System.out.println("出现异常");
    }
}
public class Student {
    private String name;
    private int age;
    private String sex;
    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 String getSex() {
        return sex;
    }
    public void setSex(String sex){
        if("男".equals(sex)||"女".equals(sex)){
            this.sex = sex;
        }else{
            throw new MyException("性别错误!!!");
        }
    }
    public Student() {
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    public static void main(String[] args) {
        Student s=new Student();
        s.setAge(19);
        s.setName("lili");
       s.setSex("lkjhlkjhlkj");
    }
}

9、常用类

包装类

引入

【1】引入:
以前定义变量,经常使用基本数据类型:
基本数据类型就是一个数,加点属性,加点方法,加点构造器
就是将基本数据类型这个数进行了一个封装,产生了一个类,----包装类

【2】
基本数据类型 对应的包装类 继承关系
byte Byte —>Number----> Object
short Short
int Integer
long Long
float Float
double Double
char Character Object
boolean Boolean Object

【3】都已经有基本数据类型,为啥还要包装类?

(1)面向对象的思维,最擅长的就是操纵各种类,假如把基本数据类型编程包装类,以后可以操纵类。

(2)之前我们学习的数组,装东西的,装基本数据类型和引用数据类型都可以。
但是以后我们要学习 集合,装东西的,但是只能装引用数据类型。

【4】现在学习了包装类,是不是以后就不用基本数据类型了?
不是。

Integer

【1】在使用Integer包装类的时候,不用导包直接使用:
在这里插入图片描述
【2】层次关系:
在这里插入图片描述
继承关系:Integer—>Number---->Object
实现接口:直接实现Comparable:当中一定实现了一个方法:compareTo方法–》内部比较器
间接实现自Serializable:
【3】常用属性:

//常用属性:
        System.out.println(Integer.MAX_VALUE);
        System.out.println(Integer.MIN_VALUE);
        //"物极必反"原则:
        System.out.println(Integer.MAX_VALUE+1);
        System.out.println(Integer.MIN_VALUE-1);

【4】构造器:

 //构造器:
        Integer i1=new Integer(12);//int--->Integer
        Integer i2=new Integer("你好");//String--->Integer

(1)int类型做参数的构造器:
在这里插入图片描述
谁调用了这个构造器,已经告知你这个构造器中很可能出现异常:
只要我们传的实参不是全数字 就会抛出异常。
【5】自动装箱和自动拆箱:
在这里插入图片描述

//类型转换:
        //自动装箱
        Integer i1=12;//int--->Integer
        System.out.println(i1);
        //自动拆箱
        Integer i2=new Integer(12);
        int num=i2;//Integer--->int
        System.out.println(i2);

【6】常用方法:
(1)compareTo:

Integer i1=new Integer(22);
        Integer i2=new Integer(12);
        int num=i1.compareTo(i2);//源码:return (x < y) ? -1 : ((x == y) ? 0 : 1);
        System.out.println(num);

(2)equals:
a.比较的是new关键字创建的对象 :

Integer i1=new Integer(12);
        Integer i2=new Integer(22);
        System.out.println(i1==i2);//比较左右两侧地址值  :false
        System.out.println(i1.equals(i2));//true
        //equals重写Object中的equals方法,里面比较的是:具体的封装的那个value值是否相等

在这里插入图片描述
b.自动装箱的包装类:

 Integer i1=129;
        Integer i2=129;
        System.out.println(i1==i2);
        /*
        ==比较的左右两侧的数值:
        数:-128~127之间:  比较的是就是数 (在cache数组中的数)----》true
        数不在-128~127之间:底层就创建对象  ==比较的就是左右的地址值。--->false
         */

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cdaec7f9ef784aeaa91975fda2828769.png
在这里插入图片描述
(3)intvalue():

 Integer i1=new Integer(12);
        int num= i1.intValue();//Integer--->int
        System.out.println(num);

(4)parseInt:

int num=Integer.parseInt("1asdfasdf");//String--->int

(5)toString:

Integer i=new Integer(12);
        System.out.println(i);
        System.out.println(i.toString());//Integer---->String

String类

【1】属于java.lang包下,可以直接使用不需要导包
【2】
在这里插入图片描述
String str=“abc”;
(1)"abc"是靠三个字符组成:‘a’ ‘b’ ‘c’—>“abc”
(2)“abc”—>String类下的具体的一个对象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.bjsxt.test02;
import java.util.Arrays;
public class Test {
    public static void main(String[] args) {
        //字符串---》引用数据类型
       // String str="你好java,\n2019!";
        /*String s1=new String();
        String s2=new String("abc");
        System.out.println(s2);
        String s3=new String(new char[]{'a','b','c'});
        System.out.println(s3);*/
        /*String s4="abcdef";
        System.out.println(s4.length());//6
        System.out.println(s4.isEmpty());
        String s5="";
        System.out.println(s5.isEmpty());
        String s6=null;
        System.out.println(s6.isEmpty());*///报错:NullPointerException
        /*String s="asdfgj";
        System.out.println(s.charAt(3));;//f*/
      /*  String s1=new String("aec");
        String s2=new String("abc");
        System.out.println(s1==s2);//false
        System.out.println(s1.equals(s2));//比较内容的值是否相等
        int num=s1.compareTo(s2);
        System.out.println(num);*/
       /* String s="abcdefghijk";
        String newStr=s.substring(3);//字符串的截取:
        System.out.println(newStr);
        String ss=s.substring(3,7);//[3,7)的截取:
        System.out.println(ss);*/
       /*String a="abc";
       String b="def";
       String s=a.concat(b);//字符串的拼接
        System.out.println(s);*/
       /*String a="abcdaf";
       String b=a.replace('a','j');
        System.out.println(b);*/
        /*String a="basdg";
        System.out.println(a.contains("bas"));;*/
        /*String a="a-b-c-d-e-f";
        String[] str=a.split("-");
        System.out.println(Arrays.toString(str));
        String b="iahajaka";
        String[] str2=b.split("a");
        System.out.println(Arrays.toString(str2));*/
       /* String a="abc";
        System.out.println(a.toUpperCase().toLowerCase());;*/
      /* String a="aaaa";
        System.out.println(a.toString());*/
      String a="   asds dsg df dg   ";//去除首尾空格
      System.out.println(a.trim());;
      String s=String.valueOf(12);
        System.out.println(s);
    }
}

String字符串内存

在这里插入图片描述

在这里插入图片描述
总结:
(1)
“a”+“b”+“c”---->“abc”
“abc”+“”—>“abc”
这种给定的字符串,会进行编译器优化,直接拼成完整的字符串 。直接在方法区的常量池中开辟空间,然后地址给了栈中的变量,不会在堆中开辟空间。

(2)用new关键字创建对象:
就会在堆中开辟空间。

(3)有变量参与运算进行字符串拼接的时候,会在堆中开辟空间。

面试题:

public class Test {
    public static void main(String[] args) {
       String s4=new String("abc");
       String s1="abc";
       String s2="abc";
        System.out.println(s1==s4);
        System.out.println(s1==s2);
    }
}

StringBuilder类

在这里插入图片描述

【2】
StringBuilder源码自行查看,然后自行画图:

public class Test {
    public static void main(String[] args) {
        String a="abc";//不可变字符串
        StringBuilder sb2=new StringBuilder();//底层char类型数组长度为16
        StringBuilder sb=new StringBuilder("abc");//可变字符串
        sb.append("ttttttt");
        sb.append("uuuuuuuuuuuu");
        sb.append("aaaaaaaaaaaaaaaaaaaaaaaaaaa");
    }
}

在这里插入图片描述

可变和不可变

在这里插入图片描述
在这里插入图片描述

常见方法

public class Test {
        public static void main(String[] args) {
                StringBuilder sb=new StringBuilder("nihaojavawodeshijie");
                //增
                sb.append("这是梦想");
                System.out.println(sb);//nihaojavawodeshijie这是梦想
                //删
                sb.delete(3, 6);//删除位置在[3,6)上的字符
                System.out.println(sb);//nihavawodeshijie这是梦想
                
                sb.deleteCharAt(16);//删除位置在16上的字符
                System.out.println(sb);//nihavawodeshijie是梦想
                
                //改
                StringBuilder sb1=new StringBuilder("$23445980947");
                sb1.insert(3, ",");//在下标为3的位置上插入 , 
                System.out.println(sb1);
                StringBuilder sb2=new StringBuilder("$2你好吗5980947");
                sb2.replace(3, 5, "我好累");//在下标[3,5)位置上插入字符串
                System.out.println(sb2);
                sb.setCharAt(3, '!');
                System.out.println(sb);
                //查
                StringBuilder sb3=new StringBuilder("asdfa");
                for (int i = 0; i < sb3.length(); i++) {
                        System.out.print(sb3.charAt(i)+"\t");
                }
                System.out.println();
                String str=sb3.substring(2,4);//截取[2,4)返回的是一个新的String,对StringBuilder没有影响
                System.out.println(str);
                System.out.println(sb3);
        }
}

StringBuffer和StringBuilder区别

【1】StringBuiler:JDK1.5开始 效率高 线程不安全
【2】StringBuffer:JDK1.0开始 效率低 线程安全
联系:查看StringBuffer源码:底层依旧是数组的扩容。
在这里插入图片描述

时间处理的类

Date


package com.bjsxt.test01;
import java.util.Date;
public class Test {
    public static void main(String[] args) {
        //创建一个java.util.Date对象:
        Date d=new Date();
        System.out.println(d);
        System.out.println(d.toString());
        System.out.println(d.toGMTString());
        //这个方法可以使用。但是这个方法过期了,过时了,废弃了。
        System.out.println(d.toLocaleString());
        System.out.println(d.getYear());//2019-1900=119
        System.out.println(d.getMonth());//返回的值在 0 和 11 之间,值 0 表示 1 月。
        System.out.println(d.getTime());//1556586936921
        long time01=System.currentTimeMillis();
        System.out.println(time01);
        //问题:以后让你获取这个毫秒数,你优先用哪个方法?下面的。因为下面的方法被static修饰了,可以直接用类名.方法名直接调用,不用创建对象。
        //创建java.sql.Date对象:
        java.sql.Date date=new java.sql.Date(1556586936921L);
        System.out.println(date);//2019-04-30
        /*
        (1)java.util.Date和java.sql.Date区别:
        java.util.Date  :年月日 时分秒
        java.sql.Date  :年月日
        (2)java.util.Date和java.sql.Date联系:
         Date 继承自java.util.Date
         */
        //sql--->util:
        Date d2=date;
        //util--->sql:
        java.sql.Date d3=(java.sql.Date)d2;
        java.sql.Date d4=new java.sql.Date(new Date().getTime());
        //sql.Date常用方法:String--->Date
        java.sql.Date d5=java.sql.Date.valueOf("2019-3-8");
    }
}

日期转换

【1】引入:

//String--->java.util.Date:
        //1.String-->java.sql.Date
        java.sql.Date d1=java.sql.Date.valueOf("2017/3/8");
        //2.java.sql.Date--->java.util.Date
        java.util.Date d2=d1;
        System.out.println(d2);
        //上面的valueOf方法,传入的字符串必须格式为:年-月-日 格式,这样就有限制了。

在这里插入图片描述
【3】两种最常用的方法:

public class Demo {
    public static void main(String[] args) {
        //日期格式化对象:
        //DateFormat df=new DateFormat();抽象类不可以直接创建对象:
        DateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //String--->Date
        try {
            Date date=df.parse("2018-3-6 12:36:29");
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //Date--->String
        String str=df.format(new Date());
        System.out.println(str);
        Date d2=new Date();
        System.out.println(d2.toString());
        System.out.println(d2.toLocaleString());
        System.out.println(d2.toGMTString());
    }
}

Calendar

Calendar这个类非常强大,关于日期的方法他都可以处理,但是
因为我们操作日期 基本就操作到年月日 时分秒,之前学的Date就已经够用了。
Calendar实际开发用的相对较少。

【1】基础代码:

package com.bjsxt.test03;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class Test {
    public static void main(String[] args) {
        //Calendar是抽象类,不可以直接创建对象,可以用下面两种方法:
        Calendar cal=new GregorianCalendar();
        Calendar cal2=Calendar.getInstance();
        System.out.println(cal.toString());
        //日历类的常用属性读取:
        System.out.println(cal.get(Calendar.YEAR));//2019
        System.out.println(cal.get(Calendar.MONTH));//3---四月
        System.out.println(cal.get(Calendar.DATE));//30
        System.out.println(cal.get(Calendar.DAY_OF_WEEK));//3
        System.out.println(cal.getActualMaximum(Calendar.DATE));
        System.out.println(cal.getActualMinimum(Calendar.DATE));
        //属性设置:
        cal.set(Calendar.YEAR,2001);
        cal.set(Calendar.MONTH,4);
        cal.set(Calendar.DATE,10);
        System.out.println(cal);
        //String--->Calendar:
        //String--->Date:
        java.sql.Date d=java.sql.Date.valueOf("2019-3-5");
        //Date--->Calendar:
        cal.setTime(d);
        System.out.println(cal);
    }
}

【2】练习:
在这里插入图片描述

package com.bjsxt.test01;
import java.util.Calendar;
import java.util.Scanner;
public class Test {
    public static void main(String[] args) {
        //1.键盘录入提示信息:
        /*Scanner sc=new Scanner(System.in);
        System.out.print("请输入你想要查看的日期:(提示:请按照例如2019-3-7的格式)");
        String strDate=sc.next();
        System.out.println(strDate);*/
        //2.将上面接收的String---》Calendar:
        //2.1String--->Date
        java.sql.Date d=java.sql.Date.valueOf("2019-1-24");
        //2.2 Date---》Calendar:
        Calendar cal= Calendar.getInstance();
        cal.setTime(d);
        //System.out.println(cal);
        //3.输出日历的星期:
        System.out.println("日\t一\t二\t三\t四\t五\t六\t");
        //4.遍历   1-本月最大天   的数字:
        //5.先求出本月最大天数:
        int maxDay=cal.getActualMaximum(Calendar.DATE);
        //System.out.println(maxDay);
        //9.求出当天日期:
        int nowDay=cal.get(Calendar.DATE);
        //10.求出本月的第一天 是 一周中的第几天:
        //10.1:将日期置成本月的第一天:
        cal.set(Calendar.DATE,1);
        int dayOfWeek= cal.get(Calendar.DAY_OF_WEEK);
        //System.out.println("一号是本周第"+dayOfWeek+"天");
        //11.求出一号前面的空格数:
        int space=dayOfWeek-1;
        //7.引入一个计数器,用来计算换行:(每7个一换行)
        int count=space;
        //12.遍历space:
        for(int i=1;i<=space;i++){
            System.out.print("\t");
        }
        //6.遍历:
        for(int i=1;i<=maxDay;i++){
            //8.在当天日期上加上*
            if(i==nowDay){
                System.out.print(i+"*\t");
            }else{
                System.out.print(i+"\t");
            }
            count++;
            if(count%7==0){
                System.out.println();
            }
        }
    }
}

Math

【1】java.lang包下 不需要导包直接使用
【2】类被final修饰,不能被继承
【3】意味着在外面不能创建Math类的对象:
Math m=new Math();—不能
在这里插入图片描述

public class Demo {
    public static void main(String[] args) {
        System.out.println("随机数:"+Math.random());
        System.out.println("绝对值:"+Math.abs(-30));
        System.out.println("向上取值:"+Math.ceil(9.1));
        System.out.println("向下取值:"+Math.floor(9.9));
        System.out.println("四舍五入:"+Math.round(8.5));
        System.out.println("最大值:"+Math.max(3,6));
        System.out.println("最小值:"+Math.min(3,6));
        System.out.println("次幂:"+Math.pow(3,6));
    }
}

【6】静态导入:
可以将类名省略,然后import +static +那个包下那个类下的所有内容 (*) 。
假如在静态导入后,本类有同名方法,优先调用本类的方法。

package com.bjsxt.test01;
import static java.lang.Math.*;
public class Demo {
    public static void main(String[] args) {
        System.out.println("随机数:"+random());
        System.out.println("绝对值:"+abs(-30));
        System.out.println("向上取值:"+ceil(9.1));
        System.out.println("向下取值:"+floor(9.9));
        System.out.println("四舍五入:"+round(8.5));
        System.out.println("最大值:"+max(3,6));
        System.out.println("最小值:"+min(3,6));
        System.out.println("次幂:"+pow(3,6));
    }
    public static int random(){
        return 10;
    }
}

【7】Random随机数类
Math.random()----->[0.0,1.0)

(1)利用有参构造器创建对象:
从结果发现,每次传入的参数(seed种子)只要是一样的,那么产生的结果也是一样的。
要想产生的随机数不一样,必须传入的种子就不一样。

public class TestRandom {
    public static void main(String[] args) {
        Random r=new Random(System.currentTimeMillis());
        for (int i=1;i<=10;i++){
            System.out.println(r.nextInt());
        }
    }
}

(2)利用空构造器创建对象:
表面上我们在用空构造器创建对象,实际上底层还是调用了有参构造器,
并且传入的参数每次创建对象都不一样:
在这里插入图片描述

public class TestRandom {
    public static void main(String[] args) {
        Random r=new Random();
        for(int i=1;i<=10;i++){
            //System.out.println(r.nextInt(10));
            System.out.println(r.nextDouble());;
        }
    }
}

在这里插入图片描述

枚举类

【1】class—>类
interface—>接口
enum---->枚举 -----》全部都是引用数据类型

【2】引入:

public enum Gender {,;
}
package com.bjsxt.test02;
public class Student {
    //属性
    private int age;
    private String name;
    private Gender sex;
    //提供相应的setter,getter方法:
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Gender getSex() {
        return sex;
    }
    public void setSex(Gender sex) {
        this.sex = sex;
    }
    //构造器
    public Student() {
    }
    public Student(int age, String name, Gender sex) {
        this.age = age;
        this.name = name;
        this.sex = sex;
    }
    //toString:
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
    public static void main(String[] args) {
        Student s=new Student();
        s.setAge(19);
        s.setName("lili");
        s.setSex(Gender.);
        System.out.println(s);
        Student s2=new Student(22,"feifei",Gender.);
        System.out.println(s2);
    }
}

【3】总结:

只能够取特定值中的一个
使用enum关键字
所有的枚举类型隐性地继承自 java.lang.Enum。(枚举实质上还是类!而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是public static final的。可以直接通过枚举类型名直接使用它们。)
public enum Gender extends java.lang.Enum 隐性地继承自 java.lang.Enum
Gender继承自Enum类,Enum类继承自Object类,所以枚举类Gender也是一个类。

强烈建议当你需要定义一组常量时,使用枚举类型
尽量不要使用枚举的高级特性,事实上高级特性都可以使用普通类来实现,没有必要引入复杂性!

public enum Gender {,;
    /*public void show(){
        System.out.println("assadf");
    }*/
}
class Test{
    public static void main(String[] args) {
        //创建枚举类对象:
        Gender a=Gender.;
        //a.show();
        System.out.println(a);//Enum类中重写了toString
    }
}

【4】枚举结合switch

package com.bjsxt.test02;
public class Demo {
    public static void main(String[] args) {
        /*
        可以:int,byte,short,char,String,枚举类
        不可以:long,float,double,boolean
         */
        Gender a=Gender.;
        switch (a){
            case:
                System.out.println("是女");
                break;
            case:
                System.out.println("是男");
                break;
        }
    }
}

10、集合

数据结构

集合引入

【1】为什么使用集合?
举例:存储学生的成绩
以前:用数组 缺点:长度一旦确定就没有办法再修改了,所以如果删除或者增加元素,大量移动元素的位置。
特点:数组只能放一种数据类型,可以是基本数据类型也可以是引用数据类型

引入一个方法东西的-------》容器(集合)
优点:增加删除元素效率高
特点:一个集合可以方法多种数据类型,(但是一般我们都使用泛型,让他只放一种类型。)
但是它只可能放引用数据类型。
所以:集合能不能放int类型数据?不能。int类型可以进行自动装箱,转成包装类的Integer类型方放入集合。

【2】我们学习的集合,是各种各样的集合,为啥要学习不同的集合?
数据结构不一样,导致集合不一样,特点不一样。

【3】集合有哪些?简要结构图:
在这里插入图片描述
【4】集合的应用场景:
当需要将相同结构的个体整合到一起的时候,就考虑使用集合。
在这里插入图片描述

Collection接口

package com.bjsxt.test01;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
    public static void main(String[] args) {
        /*
        接口常用方法:
        增加:add(E e)   addAll(Collection<? extends E> c)
        删除:clear()  remove(Object o)
        修改:
        查看:iterator()  size()
        判断:contains(Object o)   equals(Object o)  isEmpty()
         */
        //Collection:接口不能创建对象:
        Collection col=new ArrayList();
        col.add(11);
        col.add("aaaa");//表面上是int类型数据,实际12进行了自动装箱  然后我放入的是Integer数据
        //Integer i=12;  col.add(i);
        col.add(33);//Double
        Collection col2=new ArrayList();
        col2.add(11);
        col2.add(22);
        col2.add(33);
        //col.addAll(col2);
        System.out.println(col.equals(col2));//true:证明底层一定重写了equals,比较的是具体的元素的值。
        System.out.println(col==col2);//false:一定返回的是false,因为比较的是地址的值。
        System.out.println("------------------");
        col.remove("anc");
        System.out.println(col/*.toString()*/);
        System.out.println("集合中放的元素的个数:"+col.size());
        System.out.println( col.contains(11));
        /*col.clear();
        System.out.println(col*//*.toString()*//*);
        System.out.println("集合中放的元素的个数:"+col.size());
        System.out.println(col.isEmpty());*/
        System.out.println("-----------集合的遍历:-----------------");
        //方法1:普通for循环:---不行:因为没有提供  根据索引获取元素的 方法
        /*for(int i=0;i<=col.size()-1;i++){
            System.out.println(col);
        }*/
        //方法2:增强for循环:
        for(Object o:col){
            System.out.print(o+"\t");
        }
        System.out.println("\n---------------------------------");
        //方法3:iterator()  迭代器:
        Iterator it= col.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

在这里插入图片描述

第一个子接口List

package com.bjsxt.test01;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Demo {
    public static void main(String[] args) {
        List list=new ArrayList();
        /*
        常用方法:
        增加:add(int index, E element)
        删除:remove(int index)
        修改:set(int index, E element)
        查看:get(int index)
        判断:
         */
        list.add(12);
        list.add(82);
        list.add(20);
        list.add(38);
        list.add(1);
        list.add(1,666);
        list.remove(1);
        list.set(2,999);
        System.out.println(list);
        System.out.println(list.size());
        System.out.println(list.get(3));
        //对List集合的遍历:
        //方式1:普通for循环:
        System.out.print("[");
        for(int i=0;i<=list.size()-1;i++){
            if(i!=list.size()-1){
                System.out.print(list.get(i)+", ");
            }else{
                System.out.print(list.get(i)+"]");
            }
        }
        System.out.println("\n----------------------");
        //方法2:增强for循环:
        for(Object o:list){
            System.out.println(o);
        }
        System.out.println("\n----------------------");
        //方式3:迭代器:
        Iterator it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

实现类:ArrayList
源码

总结:ArrayList底层就是数组,那些方法其实就是对数组进行增删改查操作。

【1】

public class Test2 {
    public static void main(String[] args) {
        //创建一个ArrayList对象:
        ArrayList al=new ArrayList();//调用构造器,底层数组是{},长度为0
        第一次调用add方法:
        al.add("abc");
    }
}

总结:调用空构造器和第一次add做了什么事?
首先将底层的数组扩容为长度为10 ,然后在长度为10的新数组中添加元素:

public class ArrayList{
        //ArrayList底层是一个Object类型的数组
        transient Object[] elementData; 
        //这个Object类型的数组被使用的数量。
        private int size;
        //就是一个空的Object的数组
         private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        
        
        //空构造器:底层数组 长度0
        public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
                //赋予了一个{}数组---->this.elementData = {}
    }
        
        
        public boolean add(E e) {//实际参数:"abc"
        ensureCapacityInternal(size + 1);  // 经历了这个方法,底层已经将数组扩容为10了
        elementData[size++] = e;//elementData[0]="abc"   然后size+1操作
        return true;
    }
        
        private void ensureCapacityInternal(int minCapacity) {//1
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
                //ensureExplicitCapacity(10)
    }
        
        private static int calculateCapacity(Object[] elementData, int minCapacity) {//参数:{},1
        //  现在elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA满足,返回的是true,走这个if了
                if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                        //return Math.max(10,1);---->10
                        
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
         private void ensureExplicitCapacity(int minCapacity) {//参数:10
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//10-0》0--》if是执行的
            grow(minCapacity);//grow(10)
    }
        
        
        private void grow(int minCapacity) {//minCapacity:10
       
        int oldCapacity = elementData.length;//oldCapacity:0
        int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity:0
        if (newCapacity - minCapacity < 0)//if(0-10<0)-->这个分支走:
            newCapacity = minCapacity;//newCapacity:10
        if (newCapacity - MAX_ARRAY_SIZE > 0)//不走
            newCapacity = hugeCapacity(minCapacity);//不走
        //完成了数组的扩容,将原来的{}数组复制到长度为10的新数组中
                //并且将ArrayList底层的老数组变成了长度为10的新数组。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
        
        
        
        
        
        
        
        
}

【2】

public class Test2 {
    public static void main(String[] args) {
        //创建一个ArrayList对象:
        ArrayList al=new ArrayList();//调用构造器,底层数组是{},长度为0
        第一次调用add方法:
        al.add("abc");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("ggg");
        al.add("asdfasdfasdf");//放入第11个字符串:
    }
}
public class ArrayList{
        //ArrayList底层是一个Object类型的数组
        transient Object[] elementData; 
        //这个Object类型的数组被使用的数量。
        private int size;
        //就是一个空的Object的数组
         private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
         
         
         
         public boolean add(E e) {//参数:"asdfasdfasdf"
        ensureCapacityInternal(size + 1);  // 经历完这个方法,底层数组已经扩容为新的长度为15的了
        elementData[size++] = e;//elementData[10]="asdfasdfasdf"  然后进行size+1操作 size:11
        return true;
    }
        
        
        private void ensureCapacityInternal(int minCapacity) {//11
                //calculateCapacity(长度为10的老数组,11)
                //ensureExplicitCapacity(11)
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//
    }
        
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//分支不走
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;//走这个返回11
    }
         
         
         private void ensureExplicitCapacity(int minCapacity) {//11
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//11-10>0--->走
            grow(minCapacity);//grow(11)
    }
         
         
         private void grow(int minCapacity) {//minCapacity:11
        
        int oldCapacity = elementData.length;//oldCapacity:10
        int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity:15 :扩容长度是原来的1.5倍
        if (newCapacity - minCapacity < 0)//不走
            newCapacity = minCapacity;//不走
        if (newCapacity - MAX_ARRAY_SIZE > 0)//不走
            newCapacity = hugeCapacity(minCapacity);//不走
        //底层老的数组 从长度为10变成了长度为15
                //并且ArrayList底层数组的指向  指向了新的长度为15的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }       
         
}

【3】al.isEmpty();

【4】al.clear();

 public void clear() {
        modCount++;
        
        for (int i = 0; i < size; i++)//对数组进行遍历
            elementData[i] = null;//数组中的每个位置都置为null
        size = 0;//数组中被占用的数量size置为0
    }

【5】al.remove(“abc”);

public boolean remove(Object o) {//参数:“abc”
        if (o == null) {//不走
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {//走这个
                
            for (int index = 0; index < size; index++)//对数组进行遍历
                if (o.equals(elementData[index])) {//找数组中有没有元素"abc"
                                   //假如找到了才走下面的方法
                                   //fastRemove:删除这个元素了
                                   //fastRemove(这个元素对应的索引)
                    fastRemove(index);//fastRemove(0)
                    return true;
                }
        }
        return false;
    }
        
        
         private void fastRemove(int index) {//0
        modCount++;
        int numMoved = size - index - 1;//numMoved:5
        if (numMoved > 0)//走这个分支
                //System.arraycopy(elementData,1,elementData,0,5)---》底层通过数组的复制完成了删除操作
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; //将数组中有效位置最后一个置为null
    }

【6】al.get(3);

public E get(int index) {//3
        rangeCheck(index);
        return elementData(index);//elementData(3)
    }
        
         E elementData(int index) {
        return (E) elementData[index];//elementData[3]
    }
自己模拟一个ArrayList
package com.bjsxt.test02;
import java.util.Arrays;
public class MyArrayList {
    //MyArrayList底层也是一个Object类型的数组:
    Object[] value;
    //数组中被使用的数量:
    int count;
    //空构造器:
    public MyArrayList(){
        //value=new Object[10];
        this(10);
    }
    //有参构造器:
    public MyArrayList(int length){
        value=new Object[length];
    }
    //添加add方法:
    public void add(Object o){
        value[count]=o;
        count++;
        //进行数组的扩容:
        if(count>value.length-1){//长度不够
            //扩容方式1:Object[] newObj= Arrays.copyOf(value,20);
            //扩容方式2:有新数组:
            Object[] newObj=new Object[value.length*2+1];//10*2+1==21
            //将老数组中的东西 复制 到新数组中:
            for(int i=0;i<=value.length-1;i++){
                newObj[i]=value[i];
            }
            
            //将value的指向:指向新的数组:
            value=newObj;
        }
    }
    //重写toString
    @Override
    public String toString() {
        StringBuilder sb=new StringBuilder();
        sb.append("[");//拼接左面的[
        for(int i=0;i<=count-1;i++){
            sb.append(value[i]+",");//将数组遍历拼接
        }
        sb.setCharAt(sb.length()-1,']');//将sb字符串最后的,替换为]
        return sb.toString();
    }
    //增加一个计算数组中元素数量的方法:
    public int length(){
        return count;
    }
    //判断是否为空:
    public boolean isEmpty(){
        return count==0;
    }
    public static void main(String[] args) {
        //创建一个我自定义的数组的对象:
        MyArrayList list=new MyArrayList();//底层数组长度为10
        list.add("abc");
        list.add("dddd");
        list.add("fdffff");
        list.add("fdffff");
        list.add("fdffff");
        list.add("fdffff");
        list.add("fdffff");
        list.add("fdffff");
        list.add("fdffff");
        list.add("fdffff");
        list.add("dddddddddddddddd");
        list.add("33333");
        System.out.println(list);
        System.out.println("集合中元素的数量:"+list.length());
        System.out.println(list.isEmpty());
    }
}

练习:
list.remove(“abc”);
思路:
(1)将后一位移到前一位
(2)
在这里插入图片描述

将自定义的引用数据类型存入ArrayList集合

【1】代码:

package com.bjsxt.test03;
import java.util.ArrayList;
import java.util.Iterator;
public class Student {
    //属性
    private int age;
    private String name;
    //方法
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //构造器
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
    //main方法:
    public static void main(String[] args) {
        //创建一个ArrayList集合对象:
        ArrayList al=new ArrayList();
        Student s=new Student(19,"feifei");
        al.add(s);
        al.add(new Student(21,"lulu"));//匿名对象存入集合
        al.add(new Student(22,"lili"));
        //遍历:
        //普通for
        //增强for
        //迭代器:
        Iterator it = al.iterator();
        while(it.hasNext()){
            Student o=(Student)(it.next());
            System.out.println(o.getName()+"----"+o.getAge());
        }
    }
}

【2】代码尝试改动:
在这里插入图片描述

实现类:Vector

在这里插入图片描述

泛型
基本应用
package com.bjsxt.test03;
import java.util.ArrayList;
import java.util.Iterator;
public class Student {
    //属性
    private int age;
    private String name;
    //方法
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    //构造器
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
    //main方法:
    public static void main(String[] args) {
        //创建一个ArrayList集合对象:
        //加入泛型:作用:限制放入集合中元素的类型的,将类型限制为同一种类型
        //加入泛型,其实就是对编译期间生效,在编译期间就不允许我添加其他的类型了!泛型在JDK1.5之后出的

        ArrayList<Student> al=new ArrayList<Student>();
        al.add(new Student(21,"lulu"));
        al.add(new Student(22,"lulu2"));
        al.add(new Student(23,"lulu3"));
        /*al.add("abc");
        al.add(12);
        al.add(8.3);*/
        //增强for遍历:
        /*for(Object o:al){//Object o=学生对象
            System.out.println(o);
        }*/
        //加了泛型之后,遍历更加简单了,直接用泛型类型接收即可
        for(Student s:al){
            System.out.println(s);
        }
    }
}

扩展:

就是为了让你能看懂API:
不是为了让你自己写代码用的。

泛型类:
package com.bjsxt.test04;
public class FanXing<AA> {
    //FanXing其实就是一个普通的类,然后我加了<A>之后就变成了一个泛型类
    //<>中的字母可以是什么?随意,但是在API中一般叫E
    //<AA>就好像是一个占位符,就是告诉别人这里加了泛型,但是这个类型是什么还不确定
    //什么时候确定呢?创建对象的时候才确定
}
class A{
    public static void main(String[] args) {
        //创建一个普通类的对象:
        FanXing fx=new FanXing();
        //加上泛型:
        FanXing<Integer> fx2=new FanXing<Integer>();
        FanXing<String> fx3=new FanXing<String>();
    }
}

泛型方法:
package com.bjsxt.test04;
public class FanXing<AA> {
    //普通方法:可以
    public void a(){}
    //方法的参数是AA,AA的类型确定吗?确定,AA在创建对象的时候就已经确定了
    public void b(AA a){}
    //方法的参数是BB,BB的类型确定吗?BB类型不确定
    //帮我们解决了 不同数据类型做参数的重载问题
    public <BB> void c(BB b){}
    //static修饰的方法,先于对象存在
    //先于对象存在,不知道AA是啥,因为AA的类型要在对象创建后才确定的
    //public static void d(AA a){}
    //BB无论有没有对象,BB的类型都不确定,所以加static无所谓
    public static <BB> void e(BB b){}
    public void f(AA[] a){}
    public <QQ> QQ[] g(QQ...q){
        //参数为可变参数,内部当做数组处理:
        for(QQ a:q){
            System.out.println(a);
        }
        return q;
    }
}
class A{
    public static void main(String[] args) {
        //创建一个对象:
        FanXing<Integer> fx=new FanXing<Integer>();
        //AA的类型确定为Integer
        fx.b(12);
        fx.c(12);
        fx.c("asdf");
        fx.c(8.9);
        fx.f(new Integer[]{12,34,43});
        fx.g();
        fx.g(12,23,45,45);
        fx.g("asfd","asf","asfasdf");
    }
}

泛型接口:
public interface MyInterface<AA> {
}
class A implements MyInterface {}
class B implements MyInterface<String> {
}
class C<BB> implements MyInterface<BB>{
  public void a(BB b){}
}
泛型受限:

泛型的上限: ? extends A :只要泛型为A或者A的子类都可以传入
泛型的下限: ? super A :只要泛型为A或者A的父类都可以传入

package com.bjsxt.test06;
import java.util.ArrayList;
public class Person {
    int age;
    public Person(int age) {
        this.age = age;
    }
    public Person() {
    }
    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
class Student extends Person{
    String name;
    public Student(int age, String name) {
        super(age);
        this.name = name;
    }
    public Student(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}
class Test{
    public static void main(String[] args) {
        //创建多个Person对象:将对象放入集合中:
        ArrayList<Person> al=new ArrayList<Person>();
        al.add(new Person(18));
        al.add(new Person(20));
        al.add(new Person(14));
        show(al);
        ArrayList<Student> al2=new ArrayList<Student>();
        al2.add(new Student("nana"));
        al2.add(new Student("feifei"));
        al2.add(new Student("lili"));
        show(al2);
    }
//泛型受限:泛型的上限:只要是Person或者Person的子类 都可以传入
    public static void show(ArrayList<? extends Person> al){
        //对集合进行遍历:
        for(Person p:al){
            System.out.println(p);
        }
        //System.out.println("aaaa");
    }
    /*public static void show(ArrayList<Student> al2){
        for(Student s:al2){
            System.out.println(s);
        }
    }*/
}

实现类:LinkedList
基本代码

【1】基本代码:

package com.bjsxt.test07;
import java.util.LinkedList;
public class Test {
    /*
    LinkedList常用方法:
    增加:addFirst(E e)    addLast(E e)
          offerFirst(E e)   offerLast(E e)
    删除:pollFirst()   pollLast()--->JDK1.6  处理异常的方式更加健壮。
        removeFirst()  removeLast()--->JDK1.2
    修改:
    查看:getFirst()  getLast()
         peekFirst()   peekLast()
    判断:
     */
    public static void main(String[] args) {
        LinkedList<String> list=new LinkedList<String>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("eee");
        list.add("ffff");
        System.out.println(list);
        list.clear();
        list.pollFirst();
        list.pollLast();
        /* System.out.println(list);*/
        /*list.removeFirst();
        list.removeLast();*/
      /*  System.out.println(list);*/
    }
}

【2】底层结构:


public class Test {
    
    public static void main(String[] args) {
        LinkedList<String> list=new LinkedList<String>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        
    }
}

在这里插入图片描述
【3】遍历:

public class Test {
    public static void main(String[] args) {
        LinkedList<String> list=new LinkedList<String>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("eee");
        list.add("ffff");
        //遍历:
        //普通for循环:
        for(int i=0;i<=list.size()-1;i++){
            System.out.println(list.get(i));
        }
        //增强for:
        for(String s:list){
            System.out.println(s);
        }
        
        //迭代器:
        //理解简单
        /*Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }*/
        System.out.println("-----------");
        //虽然难理解,但是这个好:
        for(Iterator<String> it = list.iterator();it.hasNext();){
            System.out.println(it.next());
        }
    }
}
扩展

添加链接描述

模拟LinkedList

【1】add方法模拟:

package com.bjsxt.test08;
public class Node {//结点类
    //定义属性:
    private Object pre;//结点的前一个元素的指向
    private Object obj;//结点当前的元素
    private Object next;//结点的后一个元素的指向
    //提供setget方法:
    public Object getPre() {
        return pre;
    }
    public void setPre(Object pre) {
        this.pre = pre;
    }
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }
    public Object getNext() {
        return next;
    }
    public void setNext(Object next) {
        this.next = next;
    }
    @Override
    public String toString() {
        return ""+obj;
    }
}
package com.bjsxt.test08;
public class MyLinkedList {
    //这个链表中一定有一个头结点
    Node first;
    //这个链表中一定有一个尾结点
    Node last;
    //计数器:
    int count=0;
    public MyLinkedList(){}
    //添加元素:
    public void add(Object o){
        //如果你放入的是第一个元素:
        if(first==null){
            //创建一个结点的对象:
            Node n=new Node();
            n.setPre(null);
            n.setObj(o);
            n.setNext(null);
            //将这个链表的first和last都指向这个新的结点:
            first=n;
            last=n;
        }else{//第二个结点以后都走这个分支:
                      //创建一个结点的对象:
            Node n=new Node();
            n.setPre(last);//放入当前链表中的last结点
            n.setObj(o);
            n.setNext(null);
            //在将链表中的最后一个结点的next指向  新创建的结点
            last.setNext(n);
            //将链表的最后一个结点变成我新创建的结点
            last=n;
        }
        count++;
    }
    public Object get(int index){
        Node n=first;
        for(int i=0;i<index;i++){
            n=(Node)(n.getNext());
        }
        return n.getObj();
    }
    public static void main(String[] args) {
        //创建我自定义的链表集合的对象:
        MyLinkedList ml=new MyLinkedList();
        ml.add("aaa");
        ml.add("bbb");
        ml.add("ccc");
        System.out.println(ml.count);
        System.out.println(ml.get(2));;//通过索引得到元素
    }
}

在这里插入图片描述

【2】源码简单查看:

a.构造器:

public LinkedList() {
    }

b.add第一个元素:

 //假如放入的是第一个结点“aaa”-->first=null  last=null
         void linkLast(E e) {//"aaa"
        final Node<E> l = last;//l:null
                //将我放入的元素封装成为一个Node对象:利用构造器进行赋值:(null,"aaa",null)
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;//将链中last指向新的结点newNode
        if (l == null)//走
            first = newNode;//将链中first指向新的结点newNode
        else
            l.next = newNode;
        size++;//size计数器加1
        modCount++;
    }

c.add第2个元素:

//假如放入的是第2个结点“bbb”-->first="aaa"  last="aaa"
         void linkLast(E e) {//"bbb"
        final Node<E> l = last;//l:"aaa"
                //将我放入的元素封装成为一个Node对象:利用构造器进行赋值:("aaa","bbb",null)
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;//将链中last指向新的结点newNode
        if (l == null)//不走
            first = newNode;
        else//走
            l.next = newNode;//将“aaa”的下一个指向newNode
        size++;//size计数器再加1
        modCount++;
    }

d.clear()

 public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

e.get(index)
size是链表的计数器,size/2将索引分为左右两部分,
如果是左面的部分:正序查找
如果是右面的部分:倒叙查找

 */
    Node<E> node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
面试:iterator(),Iterator,Iterable关系

在这里插入图片描述
在这里插入图片描述

ListIterator

【1】在指定的字符串之后添加一个新的字符串:

public class Test {
    public static void main(String[] args) {
        ArrayList al=new ArrayList();
        al.add("aaa");
        al.add("bbb");
        al.add("ccc");
        al.add("ddd");
        //在“ccc”之后添加一个字符串“eeeeee”:
        Iterator it = al.iterator();
        while(it.hasNext()){
            if("ccc".equals(it.next())){
                al.add("eeeeee");
            }
        }
    }
}

在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        ArrayList al=new ArrayList();
        al.add("aaa");
        al.add("bbb");
        al.add("ccc");
        al.add("ddd");
        //在“ccc”之后添加一个字符串“eeeeee”:
        ListIterator li = al.listIterator();
        while(li.hasNext()){
            if("ccc".equals(li.next())){
                li.add("eeeee");
            }
        }
        System.out.println(al);
    }
}

可以进行倒序遍历:

public class Test {
    public static void main(String[] args) {
        ArrayList al=new ArrayList();
        al.add("aaa");
        al.add("bbb");
        al.add("ccc");
        al.add("ddd");
        //在“ccc”之后添加一个字符串“eeeeee”:
        ListIterator li = al.listIterator();
        while(li.hasNext()){
            if("ccc".equals(li.next())){
                li.add("eeeee");
            }
        }
        //System.out.println(li.hasNext());
        //System.out.println(li.hasPrevious());
        //倒着遍历:
        while(li.hasPrevious()){
            System.out.println(li.previous());
        }
        System.out.println(li.hasPrevious());
        System.out.println(li.hasNext());
        //System.out.println(al);
    }
}

第二个子接口Set

HashSet

【1】放入Integer类型数据:

public class Test {
    public static void main(String[] args) {
        //创建一个Set集合:
        Set set=new HashSet();
        set.add(12);
        System.out.println(set.add(26));//true--->放进去了返回true
        set.add(6);
        set.add(7);
        System.out.println(set.add(26));//false--->没放进去返回的是false
        set.add(1);
        System.out.println(set);//特点:唯一,无序
        System.out.println(set.size());//5
    }
}

【2】放入String类型数据:

public class Test {
    public static void main(String[] args) {
        //创建一个Set集合:
        Set set=new HashSet();
        set.add("java");
        System.out.println(set.add("html"));//true--->放进去了返回true
        set.add("css");
        set.add("jQuery");
        System.out.println(set.add("html"));//false--->没放进去返回的是false
        set.add("Mybatis");
        System.out.println(set);//特点:唯一,无序
        System.out.println(set.size());//5
    }
}

【3】放入自定义的引用数据类型:

public class Test {
    public static void main(String[] args) {
        Set set=new HashSet();
        set.add(new Student(18,"nana",180.4));
        System.out.println(set.add(new Student(21,"feifei",160.4)));
        set.add(new Student(27,"lili",150.4));
        System.out.println( set.add(new Student(21,"feifei",160.4)));
        set.add(new Student(16,"lulu",170.4));
        System.out.println(set);
        System.out.println(set.size());
    }
}
public class Student {
    private int age;
    private String name;
    private double height;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(int age, String name, double height) {
        this.age = age;
        this.name = name;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", height=" + height +
                '}';
    }
}

在这里插入图片描述

深入理解HashSet

在这里插入图片描述
2.哈希表是如何查找数据的,如何添加数据的
1.计算哈希 码(调用hashCode(),结果是一个int值,整数的哈希码取自身即可)
2.计算在哈希表中的存储位置 y=k(x)=x%11 x:哈希码 k(x) 函数 y:在哈希表中的存储位置
3.存入哈希表
情况1:一次添加成功
情况2:多次添加成功(出现了冲突,调用equals()和对应链表的元素进行比较,
比较到最后,结果都是false,创建新节点,存储数据,并加入链表末尾)
情况3:不添加(出现了冲突,调用equals()和对应链表的元素进行比较,
经过一次或者多次比较后,结果是true,表明重复,不添加)

             结论1:哈希表添加数据快(3步即可,不考虑冲突)
             结论2:唯一
             结论2:无序3.哈希表是如何查询数据的
             和添加数据的过程是相同的
             情况1:一次找到   23  86  76
             情况2:多次找到   67  56  78
             情况3:找不到   100 200
             结论1:哈希表查询数据快

4.hashCode和equals到底有什么神奇的作用
hashCode():计算哈希码,是一个整数,根据哈希码可以计算出数据在哈希表中的存储位置
equals():添加时出现了冲突,需要通过equals进行比较,判断是否相同
查询时也需要使用equals进行比较,判断是否相同

5.并且执行了几次?
hashCode是放入几个元素,就执行几次
equals:在相同位置放入元素的时候才会去比较。

6.各种类型数据的hashCode()返回的值是什么?
Integer:返回的就是value值
在这里插入图片描述
在这里插入图片描述
7.equals方法又是怎么比较的呢?

8.如何减少冲突
1)哈希表的长度和表中的记录数的比例–装填因子:
如果Hash表的空间远远大于最后实际存储的记录个数,则造成了很大的空间浪费,
如果选取小了的话,则容易造成冲突。
在实际情况中,一般需要根据最终记录存储个数和关键字的分布特点来确定Hash表的大小。
还有一种情况时可能事先不知道最终需要存储的记录个数,则需要动态维护Hash表的容量,此时可能需要重新计算Hash地址。
装填因子=表中的记录数/哈希表的长度, 4/ 16 =0.25 8/ 16 =0.5 12/16=0.75
如果装填因子越小,表明表中还有很多的空单元,则添加发生冲突的可能性越小;
而装填因子越大,则发生冲突的可能性就越大,在查找时所耗费的时间就越多。
有相关文献证明当装填因子在0.5左右的时候,Hash的性能能够达到最优。
因此,一般情况下,装填因子取经验值0.5。
2)哈希函数的选择
直接定址法 平方取中法 折叠法 除留取余法(y = x%11)
查询相关资料
3)链地址法(数组里面是链表)
当然还有其他的方法:开放地址法,再散列法,建立一个公共溢出区

复习

【1】hashCode()是做什么事的?
放入的类型要重写这个方法,然后返回一个int类型的数,–》哈希码
得到哈希码,根据一个表达式,算出在主数组中的位置。
【2】equals()是做什么事的?
当在相同位置放元素的时候,要先进行比较(只比较是否相等即可,不用比谁大谁小)

【3】底层的计算位置的表达式 真的是取余数那个吗?不一定
y=x%5–没有验证

【4】底层主数组长度是多少??–没有验证

【5】数组长度为16,但是我放了20个元素,现在冲突的概率是不是就大了
有没有处理办法???—》扩容 --没有验证

【6】主数组—》是什么类型的?–没有验证

【7】数组中的那个链表啥样的?结点啥样?–没有验证

【8】链表追加元素 请问在哪加?–没有验证

TreeSet

【1】放入Integer类型数据:

public class Test {
    public static void main(String[] args) {
        Set set=new TreeSet();
        set.add(12);
        System.out.println(set.add(3));;
        set.add(7);
        set.add(9);
        System.out.println(set.add(3));;
        set.add(16);
        System.out.println(set);//有序:按照升序进行排列   无序:没有按照输入顺序进行输出
        System.out.println(set.size());//5:   唯一
    }
}

在这里插入图片描述
【3】验证Integer底层真的实现了内部比较器:
在这里插入图片描述

【4】放入String类型数据:

public class Test {
    public static void main(String[] args) {
        Set set=new TreeSet();
        set.add("cjava");
        System.out.println(set.add("ejava"));;
        set.add("ajava");
        set.add("djava");
        System.out.println(set.add("ejava"));;
        set.add("bjava");
        System.out.println(set);//有序:按照升序进行排列   无序:没有按照输入顺序进行输出
        System.out.println(set.size());//5:   唯一
        
    }
}

在这里插入图片描述
【5】放入自定义的引用数据类型的数据:
必须实现内部比较器:

package com.bjsxt.test03;
public class Student implements Comparable {
    @Override
    public int compareTo(Object o) {
        //比较规则:
        //按照年龄排序:
        Student other=(Student)o;
        //return -(this.getAge()-other.getAge());
        //按照身高比较:
        //return ((Double)(this.getHeight())).compareTo((Double)(other.getHeight())) ;
        //按照姓名比较:
        //return this.getName().compareTo(other.getName());
        //按照姓名和年龄比较。
        if(this.getName().compareTo(other.getName())==0){//姓名相等了才比较年龄,
            return this.getAge()-other.getAge();
        }else{//姓名不等
            return this.getName().compareTo(other.getName());
        }
    }
    private String name;
    private int age;
    private double height;
    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 double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

package com.bjsxt.test03;
import java.util.Set;
import java.util.TreeSet;
public class Test {
    public static void main(String[] args) {
        Set set=new TreeSet();
        set.add(new Student("alili",17,170.4));
        set.add(new Student("blili",14,160.4));
        set.add(new Student("alili",12,180.4));
        set.add(new Student("clili",21,140.4));
        set.add(new Student("alili",14,160.4));
        set.add(new Student("elili",10,150.4));
        System.out.println(set);
        System.out.println(set.size());
    }
}

利用外部比较器:

package com.bjsxt.test03;
import java.util.Comparator;
public class Student  {
    private String name;
    private int age;
    private double height;
    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 double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}
class BiJiao01 implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        //规则:按照年龄比较:
        Student s1=(Student)o1;
        Student s2=(Student)o2;
        return s1.getAge()-s2.getAge();
    }
}
class BiJiao02 implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        //规则:按照姓名比较:
        Student s1=(Student)o1;
        Student s2=(Student)o2;
        return s1.getName().compareTo(s2.getName());
    }
}

package com.bjsxt.test03;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class Test {
    public static void main(String[] args) {
        // BiJiao02 bj=new BiJiao02();
        // Comparator bj=new BiJiao02();//多态
        //将外部比较器跟TreeSet集合利用构造器进行关联。
        //匿名内部类:如果这个比较规则  在项目中只用一次,就用匿名内部类写即可。
        Set set=new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //规则:按照姓名比较:
                Student s1=(Student)o1;
                Student s2=(Student)o2;
                return s1.getName().compareTo(s2.getName());
            }
        });
        set.add(new Student("alili",17,170.4));
        set.add(new Student("blili",14,160.4));
        set.add(new Student("alili",12,180.4));
        set.add(new Student("clili",21,140.4));
        set.add(new Student("alili",14,160.4));
        set.add(new Student("elili",10,150.4));
        System.out.println(set);
        System.out.println(set.size());
    }
}

总结:
底层:二叉树
只要是放进去的自定义的引用数据类型,比较实现内部比较器或者外部比较器

完整结构图

在这里插入图片描述

Map接口

HashMap

【1】总结:
HashMap的特点是按照key来说的,无序,唯一
HashMap中提供了各种方法,对于查找的方法很全面:
对key遍历,对value遍历,对key-value的映射键值对 一起遍历的

package com.bjsxt.test01;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
    /*
    接口 Map<K,V>---:key,value  “键值对”
    增加:put(K key, V value)
    删除:clear()  remove(Object key)
    修改:
    查看:entrySet()  get(Object key)  keySet() values()
         size()
    判断:containsKey(Object key)
          containsValue(Object value)
          isEmpty()
     */
    public static void main(String[] args) {
        //创建一个Map集合的对象:
        Map<Integer,String> map=new HashMap<Integer,String>();
        map.put(16,"zhaoss");//存入的成对的信息,存入的键值对,都是有映射关系。
        System.out.println( map.put(8,"lili"));//null---这个value把谁替换了
        map.put(4,"nana");
        System.out.println(map.put(6,"lulu"));//lili
        map.put(2,"feifei");
        System.out.println(map.put(8,"ganggang"));//lulu
       // map.clear();
        //map.remove(4);
        /*System.out.println(map);
        System.out.println(map.size());//特点:唯一,无序(按照key来总结的特点)
        System.out.println(map.containsKey(4555));;
        System.out.println(map.containsValue("lili"));
        map.clear();
        System.out.println(map.isEmpty());*/
        //遍历:查看: get(Object key)
        //keySet();对集合的key部分做遍历
        System.out.println("------------");
        Set<Integer> set = map.keySet();
        for(Integer i:set){
            System.out.println(i);
        }
        System.out.println("------------");
        //values()对集合的value部分做遍历
        Collection<String> col = map.values();
        for(String s:col){
            System.out.println(s);
        }
        System.out.println("------------");
        for(Integer i:set){
            System.out.println(map.get(i));
        }
        System.out.println("------------");
        //entrySet()
        //entrySet方法返回值为Set类型,有泛型,泛型为:Entry类型---》这个东西是Map接口的内部接口
        //这个接口又有两个泛型<Integer, String>
        Set<Map.Entry<Integer, String>> set1 = map.entrySet();
        for(Map.Entry<Integer, String> m:set1){
            System.out.println(m.getKey()+"-----"+m.getValue());
        }
    }
}

【2】将代码中的HashMap全部替换为Hashtable,发现结果一模一样

TreeMap

【1】内部比较器:

package com.bjsxt.test02;
import java.util.TreeMap;
public class Test {
    public static void main(String[] args) {
        TreeMap<Student,String> tm=new TreeMap<Student,String>();
        Student s1=new Student("lili",18,160.4);
        tm.put(s1,s1.getName());
        Student s2=new Student("glili",16,160.4);
        tm.put(s2,s2.getName());
        Student s3=new Student("lasdf",18,160.4);
        tm.put(s3,s3.getName());
        Student s4=new Student("ttti",12,160.4);
        tm.put(s4,s4.getName());
        System.out.println(tm);
        System.out.println(tm.size());
    }
}
package com.bjsxt.test02;
public class Student implements Comparable {
    private String name;
    private int age;
    private double height;
    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 double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
    @Override
    public int compareTo(Object o) {
        Student other=(Student)o;
        return this.getAge()-other.getAge();
    }
}

【2】外部比较器:

package com.bjsxt.test02;
import java.util.Comparator;
import java.util.TreeMap;
public class Test {
    public static void main(String[] args) {
        TreeMap<Student,String> tm=new TreeMap<Student,String>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                Student s1=(Student)o1;
                Student s2=(Student)o2;
                return s1.getAge()-s2.getAge();
            }
        });
        Student s1=new Student("lili",18,160.4);
        tm.put(s1,s1.getName());
        Student s2=new Student("glili",16,160.4);
        tm.put(s2,s2.getName());
        Student s3=new Student("lasdf",18,160.4);
        tm.put(s3,s3.getName());
        Student s4=new Student("ttti",12,160.4);
        tm.put(s4,s4.getName());
        System.out.println(tm);
        System.out.println(tm.size());
    }
}

package com.bjsxt.test02;
public class Student  {
    private String name;
    private int age;
    private double height;
    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 double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

Map结构图

在这里插入图片描述

源码

HashMap,HashSet

【1】看HashMap的空构造器,put方法,get方法:

HashMap<Integer,String> hm=new HashMap<Integer,String>();
public class HashMap<K,V>{//K:--->Integer   V:--->String
        //就是Hash表原理底层最重要的那个主数组:
        //这个主数组是什么类型的:Entry
        transient Entry<K,V>[] table;
        static final int DEFAULT_INITIAL_CAPACITY = 16;//要赋给主数组:长度是16
        static final float DEFAULT_LOAD_FACTOR = 0.75f;//装填因子
        transient int size;//计数器:用来计数放入的元素的数量的
        final float loadFactor;//一会用来接收装填因子的
        int threshold;//用来接收 数组的最大承重  16*0.75=12
        
        //发现Entry这个类是HashMap的一个内部类
        static class Entry<K,V> implements Map.Entry<K,V> {
                //4个属性:
        final K key;//Integer key;---:键值对的“key”
        V value;//String value---->键值对的“value”
        Entry<K,V> next;//指向下一个结点
        int hash;//哈希码
        /**
         * 构造器:创建Entry对象初始化 
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        }
        
        //空构造器:
        public HashMap() {
                //调用了本类的带参构造器  this(16,0.75f);
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);//
    }
        
        public HashMap(int initialCapacity, float loadFactor) {//(16,0.75f)
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;//loadFactor=0.75
        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
                //threshold:12  
                
        table = new Entry[capacity];
                //table = new Entry[16]--->主数组长度为16,创建了一个长度为16的主数组
        useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        init();
    }
        
        
        //hm.put(12,"aaa");
        public V put(K key, V value) {K:--->Integer   V:--->String
            //对key是否为空的判断
        if (key == null)
            return putForNullKey(value);
                
                //计算key的哈希码:底层进行了二次散列 :h ^= k.hashCode();为了避免哈希码重复
        int hash = hash(key);
                
                //计算key最在主数组的位置
                //底层计算:h & (length-1)  其实他就相当于:h%(length-1)取余数
        int i = indexFor(hash, table.length);//  假如人家算出来是:i=3
                
                
                //1.假如放入的是第一个元素:table[3]是null  将table[3]给了e
                //2.e=null --》不满足e!=null--->for循环不走
                
                
                //9.再次往下标为3的位置上放东西:
                //10table[3]-->0x99-->e  :  e--->0x99
                //11. e!=null-->满足-->走入for循环中
        for (Entry<K,V> e = table[i]; 
                     e != null;
                     e = e.next) {
                                Object k;
                                //12.底层调用equals进行比较。判断是否一样。
                                //如果一样,走入if
                                //如果不一样
                                
                                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                                        
                                        V oldValue = e.value;//将老的value给oldValue变量
                                        e.value = value;//将老的value替换为新的value
                                        e.recordAccess(this);
                                        return oldValue;//将老的value返回,然后可以在控制台输出
                                }
        }
                
                //3.上面循环不走直接走下面的代码:
        modCount++;
                //4.addEntry(key的哈希码,key,value,元素的位置3)
                //13.开始添加结点:
        addEntry(hash, key, value, i);
        return null;
    }
        
        //5.参数:(key的哈希码,key,value,元素的位置3)
        void addEntry(int hash, K key, V value, int bucketIndex) {
                //如果size的值大于12这个极限数字了,那么底层的主数组要扩容为原来的二倍
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        createEntry(hash, key, value, bucketIndex);
    }
        //6.参数:(key的哈希码,key,value,元素的位置3)
        void createEntry(int hash, K key, V value, int bucketIndex) {
                
                //14.e:table[3]--->0x99
        Entry<K,V> e = table[bucketIndex];//7.e:null 
                //8.table[3]=new Entry(key的哈希码,key,value,null)
                //15.new Entry(哈希码,key,value,0x99)
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
        //16   new Entry(哈希码,key,value,0x99)
             Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;//value=value
            next = n;//next=null
            key = k;//key=key
            hash = h;//hash=哈希码
        }
                
                
                
        //通过key获取value	
        public V get(Object key) {
                //判断key是否为null
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);
        return null == entry ? null : entry.getValue();
    }
}

在JDK1.7以前,插入元素是在链表的头插入
在JDK1.8以后,插入元素是在链表的尾部插入
在JDK。18以后,当链表的长度大于8的时候,底层变成了红黑树
【2】HashSet源码:

public class HashSet{       
        
        
        //底层:是一个Map:
        private transient HashMap<E,Object> map;
        
        //空构造器:构建Map
        public HashSet() {
        map = new HashMap<>();
    }
        
        public boolean add(E e) {
                //给set添加元素其实底层就是给map put元素呢
        //put将元素放入map的key位置 ,value位置放入的就是Object对象
        return map.put(e, PRESENT)==null;
    }
        
        //就是一个Object对象
        private static final Object PRESENT = new Object();
        
        public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
        
}
TreeMap,TreeSet
public class TreeMap{
        
        
        private final Comparator<? super K> comparator;//comparator用来接收:外部比较器:
        
        
        //空构造器:
        public TreeMap() {
        comparator = null;//没有指定外部比较器
    }
        //带参构造器
        public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;//指定外部比较器
    }
        
        private transient Entry<K,V> root = null;//初始化root为null
        private transient int size = 0;//计算
        public V put(K key, V value) {//添加元素
        
        Entry<K,V> t = root;
                
                
                //t=null意味着这个树中还没有结点呢
        if (t == null) {
                        //比较:自己跟自己比
            compare(key, key); // type (and possibly null) check
//
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        //将外部比较器给了cpr
        Comparator<? super K> cpr = comparator;
                //判断cpr是不是null
                //不是空---》指定了外部比较器
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
                //是空---》指定了内部比较器
        else {
            if (key == null)
                throw new NullPointerException();
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
        
        
        
}
验证红黑树
package com.bjsxt.test04;
import java.util.TreeMap;
public class Test {
    public static void main(String[] args) {
        TreeMap<Student,Integer> tm=new TreeMap<Student,Integer>();
        tm.put(new Student(1),1001);
        tm.put(new Student(2),1001);
        tm.put(new Student(3),1001);
        tm.put(new Student(4),1001);
        tm.put(new Student(-5),1001);
    }
}
package com.bjsxt.test04;
public class Student implements  Comparable{
    int age;
    public Student(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return age+"";
    }
    @Override
    public int compareTo(Object o) {
        Student s=(Student)o;
        System.out.println(this+"和"+s+"正在进行比较");
        return this.age-s.age;
    }
}

Collections工具类

package com.bjsxt.test007;
import java.util.ArrayList;
import java.util.Collections;
 
public class TestCollections {
        public static void main(String[] args) {
                
                ArrayList<String> al=new ArrayList<String>();
                al.add("apple");
                al.add("banana");
                al.add("java");
                System.out.println(al);
                //addAll(Collection<? super T> c, T... elements)
                Collections.addAll(al, "merry","lili","abc");
                System.out.println(al);
                Collections.addAll(al, new String[]{"nihao","jsp"});//可变参数相当于数组
                System.out.println(al);
                
                //binarySearch(List<? extends Comparable<? super T>> list, T key) 
                System.out.println(Collections.binarySearch(al, "lili"));//发现不准确  为什么?因为要对排序后的查找
                Collections.sort(al);
                System.out.println(al);
                System.out.println(Collections.binarySearch(al, "abc"));//发现不准确  为什么?因为要对排序后的查找---返回索引
                System.out.println(Collections.binarySearch(al, "aaaa"));//找不到  返回负数
                
                //copy(List<? super T> dest, List<? extends T> src) 
                ArrayList<String> sl=new ArrayList<String>();
                Collections.addAll(sl, "a","b","c","d","e","a","b","c","d","e");
                System.out.println("==");
                System.out.println(al);
                System.out.println(sl);
                
                Collections.copy(sl, al);//sl的长度必须大于al的长度的时候在可以进行赋值替换
                System.out.println(al);
                System.out.println(sl);//其实相当于替换
                
                
                //fill(List<? super T> list, T obj) 
                System.out.println(al);
//		Collections.fill(al, "java");
                System.out.println(al);
                
                //
                System.out.println(Collections.max(al));;
        }
}

11、IO流

File类

【1】盘符上:什么是文件,什么是文件夹?
有后缀的是:文件
没有后最的:文件夹(目录)

【2】文件,文件夹都可以存放在什么位置上?
C盘第一硬盘

【3】查看文件或者文件夹的属性
在这里插入图片描述
在这里插入图片描述

public class Test {
    public static void main(String[] args) throws IOException {
        //将程序和文件进行关联:创建File类的对象:
        //File f=new File("d:\\a.txt");
        File f=new File("d:/a.txt");
        //File f=new File("d:"+File.separator+"a.txt");//与系统有关的默认名称分隔符
        //常用方法:
        System.out.println("文件是否可读:"+f.canRead());
        System.out.println("文件是否可写:"+f.canWrite());
        File f2=new File("d:/b.txt");
        System.out.println("比较结果:"+f.compareTo(f2));//按字母顺序比较两个抽象路径名。
        /*if(f.exists()){
            System.out.println("是否删除成功:"+f.delete());
        }else{
            System.out.println("是否创建成功:"+f.createNewFile());//如果不存在,就创建,如果存在,不会重复创建
        }*/
        File f3=new File("d:/a.txt");
        File f4=new File("d:/b.txt");
        System.out.println("地址是否相等:"+(f3==f4));//false
        System.out.println("路径是否相等:"+f3.equals(f4));
        System.out.println("--------------------------------");
        File f5=new File("a.txt");
        if(f5.exists()){
            System.out.println("是否删除成功:"+f5.delete());
        }else{
            System.out.println("是否创建成功:"+f5.createNewFile());//如果不存在,就创建,如果存在,不会重复创建
        }
        System.out.println("绝对路径:"+f5.getAbsolutePath());//在盘符上真正存放的位置
        System.out.println("相对路径:"+f5.getPath());//相对谁而言?相对项目
        System.out.println("toString得到的结果跟相对路径是一样的:"+f5);//toString
        System.out.println("文件的名称:"+f.getName());
        System.out.println("上级目录:"+f.getParent());
        System.out.println("是否是一个目录:"+f.isDirectory());
        System.out.println("是否是一个文件:"+f.isFile());
        System.out.println("是否隐藏:"+f.isHidden());
        System.out.println("文件中的字节数:"+f.length());
    }
}

(2)File类对文件夹进行的操作:

public class Test2 {
    public static void main(String[] args) throws IOException {
        //关联:目录(文件夹)
        File f=new File("d:/bjsxt");
        //常用方法:
        //f.mkdir();//创建单层目录
        //f.mkdirs();//创建多层目录
        //f.delete();//只能删除空文件夹
       /*String[] str=f.list();//对文件夹下的文件或者目录进行遍历查看,得到的就是名字
       for(String s:str){
           System.out.println(s);
       }*/
//用的多:
       File[] files=f.listFiles();
       for(File file:files){
           System.out.println(file.length()+"--"+file.isDirectory()+"----"+file.getPath()+"---"+file.getName());
       }
       /*
 System.out.println("文件是否可读:"+f.canRead());
        System.out.println("文件是否可写:"+f.canWrite());
        File f2=new File("d:/b.txt");
        System.out.println("比较结果:"+f.compareTo(f2));//按字母顺序比较两个抽象路径名。*/
        /*if(f.exists()){
            System.out.println("是否删除成功:"+f.delete());
        }else{
            System.out.println("是否创建成功:"+f.createNewFile());//如果不存在,就创建,如果存在,不会重复创建
        }*/
       /* File f3=new File("d:/a.txt");
        File f4=new File("d:/b.txt");
        System.out.println("地址是否相等:"+(f3==f4));//false
        System.out.println("路径是否相等:"+f3.equals(f4));
        System.out.println("--------------------------------");
        File f5=new File("a.txt");
        if(f5.exists()){
            System.out.println("是否删除成功:"+f5.delete());
        }else{
            System.out.println("是否创建成功:"+f5.createNewFile());//如果不存在,就创建,如果存在,不会重复创建
        }
        System.out.println("绝对路径:"+f5.getAbsolutePath());//在盘符上真正存放的位置
        System.out.println("相对路径:"+f5.getPath());//相对谁而言?相对项目
        System.out.println("toString得到的结果跟相对路径是一样的:"+f5);//toString
        System.out.println("文件的名称:"+f.getName());
        System.out.println("上级目录:"+f.getParent());
        System.out.println("是否是一个目录:"+f.isDirectory());
        System.out.println("是否是一个文件:"+f.isFile());
        System.out.println("是否隐藏:"+f.isHidden());
        System.out.println("文件中的字节数:"+f.length());*/
    }
}

(3)习题:
遍历你某一个文件夹下的所有内容:

public class Test3 {
    public static void main(String[] args) {
        //遍历:d:/bjsxt目录下的所有内容:
        showFile(new File("d:/bjsxt"),1);
    }
    //抽取:对目录进行遍历:
    public static void showFile(File f,int level){
        File[] files=f.listFiles();
        for(File file:files){
            //通过这个level在前面加点"-"
            for(int i=0;i<level;i++){
                System.out.print("- ");
            }
            if(file.isDirectory()){//是一个文件夹
                System.out.println(file.getName());
                //向下进行遍历查看:
                showFile(file,level+1);
            }else{//是文件:
                System.out.println(file.getName());
            }
        }
    }
}
public class Test3 {
    public static void main(String[] args) {
        //遍历:d:/bjsxt目录下的所有内容:
        showFile(new File("d:/bjsxt"),1);
    }
    //抽取:对目录进行遍历:
    public static void showFile(File f,int level){
        File[] files=f.listFiles();
        for(File file:files){
            //通过这个level在前面加点"-"
            for(int i=0;i<level;i++){
                System.out.print("- ");
            }
            System.out.println(file.getName());
            if(file.isDirectory()){//是一个文件夹
                //向下进行遍历查看:
                showFile(file,level+1);
            }
        }
    }
}

IO的引入

【1】File类:关联盘符上面的文件或者文件夹:
做什么事:获取一些属性等信息,还可以对文件(目录)创建删除等操作。
但是:咱们有没有获取文件里面的东西?没有,因为File不能帮我们完成这个事

【2】IO能做的事:程序和数据源之间的桥梁:
分类:
在这里插入图片描述
【3】有哪些流:
在这里插入图片描述

最终:文件的复制

分解:文件----》程序

FileInputStream

【1】利用字节流 :一个字节一个字节的读取数据:

public class Test {
    public static void main(String[] args) throws IOException {
        //1.有一个源文件:
        File file=new File("d:\\bjsxt\\a.txt");
        //2.将一根"管"搭在源文件和程序之间:---》IO流和文件进行关联:
        FileInputStream fis=new FileInputStream(file);
        //3.开始动作:"吸"---》读取:
        /*int n=fis.read();
        System.out.println(n);
        int n2=fis.read();
        System.out.println(n2);
        int n3=fis.read();
        System.out.println(n3);
        int n4=fis.read();
        System.out.println(n4);*/
        int n=fis.read();//每次读一个字节进来
        while(n!=-1){
            System.out.println((char)n);
            n=fis.read();
        }
        //4.关闭流:
        fis.close();
    }
}

在这里插入图片描述
【2】提升效率的方式:利用缓冲数组:
在我这个题里面,我的缓冲数组长度定义为6,
实际在开发中一般定义为:1024的整数倍
在这里插入图片描述
public class Test {
public static void main(String[] args) throws IOException {
//1.有一个源文件:
File file=new File(“d:\bjsxt\a.txt”);
//2.将一根"管"搭在源文件和程序之间:—》IO流和文件进行关联:
FileInputStream fis=new FileInputStream(file);
//3.开始动作:“吸”—》读取:
//增加一个数组:
byte[] b=new byte[6];
/int len=fis.read(b);
int len2=fis.read(b);
int len3=fis.read(b);
System.out.println(len);//6
System.out.println(len2);//4
System.out.println(len3);//-1
/
int len=fis.read(b);//每次数组中读取元素的数量;
while(len!=-1){
//System.out.println(len);
for(int i=0;i<=len-1;i++){
System.out.println((char)b[i]);
}
len=fis.read(b);
}
//4.关闭流:
fis.close();
}
}
在这里插入图片描述

分解:程序----》文件

FileOutputStream

【1】一个字节一个字节的向外传送数据:

public class Test {
    public static void main(String[] args) throws IOException {
        //1.确定目标文件:
        File file=new File("d:/demo.txt");
        //2.在文件和程序中间搭一根”管“
        FileOutputStream fos=new FileOutputStream(file);
       // FileOutputStream fos=new FileOutputStream(file,true);
        //3.将内容写出去:
        String str="abc123你好";
        byte[] bytes=str.getBytes();
        System.out.println("转换为数组的长度为:"+bytes.length);
        for(byte b:bytes){
            System.out.println(b);
            fos.write(b);
        }
        //4.关闭流:
        fos.close();
    }
}

【2】利用数组向外传送数据:

public class Test2 {
    public static void main(String[] args) throws IOException {
        //1.确定目标文件:
        File file=new File("d:/demo.txt");
        //2.在文件和程序中间搭一根”管“
        FileOutputStream fos=new FileOutputStream(file);
        //3.将内容写出去:
        String str="abc123你好asfdasfdasfdasdf";
        byte[] bytes=str.getBytes();
        fos.write(bytes);
        //4.关闭流:
        fos.close();
    }
}

将上面两步骤合为一起:完成复制

在这里插入图片描述

public class Test {
    public static void main(String[] args) throws IOException {
        //1.有源文件:
        File f1=new File("d:/a.txt");
        //2.有目标文件:
        File f2=new File("d:/b.txt");
        //3.将一根管怼到源文件上去:
        FileInputStream fis=new FileInputStream(f1);
        //4.将一根管怼到目标文件上去:
        FileOutputStream fos=new FileOutputStream(f2);
        //5.开始动作:
        int n=fis.read();
        while(n!=-1){
            fos.write(n);
            n=fis.read();
        }
        //6.关闭流:
        fos.close();
        fis.close();
    }
}

在这里插入图片描述

public class Test {
    public static void main(String[] args) throws IOException {
        //1.有源文件:
        File f1=new File("d:/a.txt");
        //2.有目标文件:
        File f2=new File("d:/b.txt");
        //3.将一根管怼到源文件上去:
        FileInputStream fis=new FileInputStream(f1);
        //4.将一根管怼到目标文件上去:
        FileOutputStream fos=new FileOutputStream(f2);
        //5.开始动作:
        byte[] b=new byte[8];
        int len=fis.read(b);
        while(len!=-1){
            fos.write(b,0,len);//将这个b数组中草[0,len)的位置弄出去--》其实就是将数组中的有效内容输出。
            len=fis.read(b);
        }
        //6.关闭流:
        fos.close();
        fis.close();
    }
}

编码总结

【1】在编辑器中:
utf-8:占用三个字节
bgk(ANSI):占用两个字节

【2】在java中:
无论是字母,数字,汉字等 全部内存都是占用一个字符。

节点流,处理流:BufferedInputStream,BufferedOutputStream

【1】利用FileInputStream,FileOutputStream 一个字节一个字节的读取写出数据:

public class Test {
    public static void main(String[] args) throws IOException {
        //1.有源文件:
        File f1=new File("D:/JAVA_API_1.6_zh_CH.CHM");
        //2.有目标文件:
        File f2=new File("d:/b.CHM");
        //3.将一根管怼到源文件上去:
        FileInputStream fis=new FileInputStream(f1);
        //4.将一根管怼到目标文件上去:
        FileOutputStream fos=new FileOutputStream(f2);
        long startTime=System.currentTimeMillis();
        //5.开始动作:
        int n=fis.read();
        while(n!=-1){
            fos.write(n);
            n=fis.read();
        }
        //6.关闭流:
        fos.close();
        fis.close();
        long endTime=System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
}

结果:需要的时间很长:
【2】利用FileInputStream,FileOutputStream 利用数组读取写出数据:

public class Test2 {
    public static void main(String[] args) throws IOException {
        //1.有源文件:
        File f1=new File("D:/JAVA_API_1.6_zh_CH.CHM");
        //2.有目标文件:
        File f2=new File("d:/b.CHM");
        //3.将一根管怼到源文件上去:
        FileInputStream fis=new FileInputStream(f1);
        //4.将一根管怼到目标文件上去:
        FileOutputStream fos=new FileOutputStream(f2);
        //5.开始动作:
        long startTime=System.currentTimeMillis();
        byte[] b=new byte[1024];
        int len=fis.read(b);
        while(len!=-1){
            fos.write(b,0,len);//将这个b数组中草[0,len)的位置弄出去--》其实就是将数组中的有效内容输出。
            len=fis.read(b);
        }
        //6.关闭流:
        fos.close();
        fis.close();
        long endTime=System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
}

【3】用一种效率更高的方式:
在这里插入图片描述

package com.bjsxt.test03;
import java.io.*;
public class Test2 {
    public static void main(String[] args) throws IOException {
        //1.有源文件:
        File f1=new File("D:/JAVA_API_1.6_zh_CH.CHM");
        //2.有目标文件:
        File f2=new File("d:/b.CHM");
        //3.将一根管怼到源文件上去:
        FileInputStream fis=new FileInputStream(f1);
        //4.将一根管怼到目标文件上去:
        FileOutputStream fos=new FileOutputStream(f2);
        //5.在FileInputStream外再套一个管--》提升功能
        BufferedInputStream bis=new BufferedInputStream(fis);//底层缓冲数组默认的大小为8192
        //6.在FileOutputStream外再套一个管--》提升功能
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        //7.开始动作:
        long startTime=System.currentTimeMillis();
        byte[] b=new byte[1024];
        int len=bis.read(b);
        while(len!=-1){
            bos.write(b,0,len);
            len=bis.read(b);
        }
        //6.关闭流://我们关闭流,倒着关闭:
        /*bos.close();
        bis.close();
        fos.close();
        fis.close();*/
        //closeAll(bos,bis,fos,fis);
        closeAll(bos,bis);//直接关闭最高级的流就可以了,可以省略里面那些流
        long endTime=System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
    public static void closeAll(Closeable...clos){
        for(Closeable cl:clos){
            try {
                cl.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

字符流:FileReader,FileWriter:文件的复制

【1】利用FileReader,FileWriter 一个字符一个字符的 复制 数据:

public class Test {
    public static void main(String[] args) throws IOException {
        //1.源文件:
        File f1=new File("d:/a.txt");
        //2.目标文件:
        File f2=new File("d:/b.txt");
        //3.套管:
        FileReader fr=new FileReader(f1);
        FileWriter fw=new FileWriter(f2);
        //4.开始动作:
        int n=fr.read();
        while(n!=-1){
            System.out.println(n);
            fw.write(n);
            n=fr.read();
        }
        //5.关闭流:字符流有缓存机制,
        fw.close();
        fr.close();
        //fw.flush();//冲刷
    }
}

备注:以后不记得哪些流需要关闭,所以我们以后遇到流就管就行了。

【2】利用FileReader,FileWriter 利用字符数组:

public class Test {
    public static void main(String[] args) throws IOException {
        //1.源文件:
        File f1=new File("d:/a.txt");
        //2.目标文件:
        File f2=new File("d:/b.txt");
        //3.套管:
        FileReader fr=new FileReader(f1);
        FileWriter fw=new FileWriter(f2);
        //4.开始动作:
        char[] ch=new char[5];
        int len=fr.read(ch);
        while(len!=-1){
            fw.write(ch,0,len);
            len=fr.read(ch);
        }
        //5.关闭流:字符流有缓存机制,
        fw.close();
        fr.close();
        //fw.flush();//冲刷
    }
}

【3】利用缓冲流::

public class Test {
    public static void main(String[] args) throws IOException {
        //1.源文件:
        File f1=new File("d:/a.txt");
        //2.目标文件:
        File f2=new File("d:/b.txt");
        //3.套管:
        FileReader fr=new FileReader(f1);
        FileWriter fw=new FileWriter(f2);
        BufferedReader br=new BufferedReader(fr);
        BufferedWriter bw=new BufferedWriter(fw);
        //4.开始动作:
        char[] ch=new char[5];
        int len=br.read(ch);
        while(len!=-1){
            bw.write(ch,0,len);
            len=br.read(ch);
        }
        //5.关闭流:字符流有缓存机制,
        bw.close();
        br.close();
    }
}

【4】缓冲流另一种方式:

public class Test {
    public static void main(String[] args) throws IOException {
        //1.源文件:
        File f1=new File("d:/a.txt");
        //2.目标文件:
        File f2=new File("d:/b.txt");
        //3.套管:
        FileReader fr=new FileReader(f1);
        FileWriter fw=new FileWriter(f2);
        BufferedReader br=new BufferedReader(fr);
        BufferedWriter bw=new BufferedWriter(fw);
        //4.开始动作:
        String str=br.readLine();
        while(str!=null){
            bw.write(str);
            bw.newLine();
            str=br.readLine();
        }
        //5.关闭流:字符流有缓存机制,
        bw.close();
        br.close();
    }
}

【5】我想自己处理异常:

public class Test {
    public static void main(String[] args)  {
        //1.源文件:
        File f1=new File("d:/a.txt");
        //2.目标文件:
        File f2=new File("d:/b.txt");
        //3.套管:
        FileReader fr= null;
        BufferedReader br=null;
        BufferedWriter bw=null;
        try {
            fr = new FileReader(f1);
            FileWriter fw=new FileWriter(f2);
            br=new BufferedReader(fr);
            bw=new BufferedWriter(fw);
            //4.开始动作:
            String str=br.readLine();
            while(str!=null){
                bw.write(str);
                bw.newLine();
                str=br.readLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5.关闭流:字符流有缓存机制,
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

System类对IO流的支持

【1】代码:

package com.bjsxt.test05;
import java.io.*;
import java.util.Scanner;
public class Test {
    public static void main(String[] args) throws IOException {
        //键盘录入一个数字:
       /* Scanner sc=new Scanner(System.in);
        System.out.print("请录入:");
        int num=sc.nextInt();
        System.out.println(num);*/
        //疑问:键盘录入这个事到底是谁实现的?Scanner sc=new Scanner(System.in);
        //答案:不是Scanner    是: System.in
        /*InputStream is=System.in;//其实就是:将一根管怼到键盘上
        int n=is.read();
        System.out.println(n);*/
        //Scanner sc=new Scanner(System.in);相当于:将一根管怼到键盘上,将键盘录入的数据进行扫描:
        //Scanner sc=new Scanner(new FileInputStream(new File("d:/a.txt")));
        //FileInputStream:一根管怼到硬盘上的文件上去了,然后Scanner扫描器扫这个管里面的内容。
        /*Scanner sc=new Scanner(new FileInputStream(new File("d:/a.txt")));
        while(sc.hasNext()){
            System.out.println(sc.next());
        }*/
        //这根管怼到:显示器上去了---》控制台
        PrintStream ps=System.out;
        ps.print("a");//将内容原样输出在控制台+不换行
        ps.print("b");
        ps.println("d");//将内容原样输出在控制台+换行
        ps.print("e");
        ps.print("o");
        System.out.print("aaaa");
        System.out.println("0000000");
    }
}

转换流

package com.bjsxt.test05;
import java.io.*;
public class Test2 {
    public static void main(String[] args) throws IOException {
        //键盘录入数据,将数据保存到硬盘的文件中去,用一种效率高的方式。
        //键盘:
        InputStream is=System.in;
        //将字节流转换为---->字符流:单向转换:
        InputStreamReader isr=new InputStreamReader(is);
        //需要两个管:
        BufferedReader br=new BufferedReader(isr);
        BufferedWriter bw=new BufferedWriter(new FileWriter(new File("d:/q.txt")));
        //开始动作:
        String str=br.readLine();
        while(!str.equals("byebye")){
            bw.write(str);
            bw.newLine();
            str=br.readLine();
        }
        //关闭流:
        bw.close();
        br.close();
        isr.close();
        is.close();
    }
}

数据流:专门操纵基本数据类型的

【1】利用数据流向外写数据:

public class Test01 {
    public static void main(String[] args) throws IOException {
        DataOutputStream dos=new DataOutputStream(new FileOutputStream(new File("d:/a.txt")));
        dos.writeInt(12);
        dos.writeBoolean(false);
        dos.writeDouble(9.8);
        dos.writeUTF("abcnihao");
        dos.close();
    }
}


public class Test02 {
    public static void main(String[] args) throws IOException {
        DataInputStream dis=new DataInputStream(new FileInputStream(new File("d:/a.txt")));
        System.out.println(dis.readInt());
        System.out.println(dis.readBoolean());
        System.out.println(dis.readDouble());
        dis.close();
    }
}

在这里插入图片描述

注意上面的问题:
(1)我们是先写出去,再读进来,否则不知道该读什么类型。
(2)读写的时候,顺序不能乱。

在这里插入代码片

【3】比如我想将一个对象的信息写出去:
对象:
拼接字符串:“年龄:18 性别:男 名字:丽丽 ”

现在想到一个问题:能不能将对象直接写出去???可以:

对象流:专门操纵引用数据类型的

【4】引入:对象流:
写出一个对象:

public class Test {
    public static void main(String[] args) throws IOException {
        //创建一个Student的对象,将对象写到文件中去:
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(new File("d:/a.txt")));
        Student s=new Student("lili",18,160.7);
        oos.writeObject(s);
        oos.close();
    }
}

在这里插入图片描述
------》序列化
在这里插入图片描述
------》反序列化:

public class Test02 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //将文件中的对象读进来:
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(new File("d:/a.txt")));
        Object o=ois.readObject();
        System.out.println(o);
        ois.close();
    }
}

在这里插入图片描述

public class Student implements Serializable{
    //加入一个序列化的版本号
    private static final long serialVersionUID = -6237412967763134270L;
    private String name;
    private int age;
    private double height;
    private String sex;
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    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 double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

PS:
(1)变量加上transient修饰,就不可以序列化
(2)变量被static修饰,也不可以被序列化

文件夹的复制

package com.bjsxt.test08;
import java.io.*;
public class Test {
    public static void main(String[] args) throws IOException {
        //复制文件夹:
        copyDir(new File("d:/bjsxt"),new File("d:/bjsxt2"));
    }
    public static void copyDir(File srcFile,File targetFile) throws IOException {
        //判断是否有目标文件夹:
        if(!targetFile.exists()){//如果不存在我就创建
            targetFile.mkdirs();
        }
        //开始复制:
        File[] files=srcFile.listFiles();
        for(File f:files){
            if(f.isDirectory()){//如果f是文件夹,复制文件夹:
                copyDir(new File(srcFile+"/"+f.getName()),new File(targetFile+"/"+f.getName()));
            }else{ //如果f是文件,直接复制文件就可以:
                copyFile(new File(srcFile+"/"+f.getName()),new File(targetFile+"/"+f.getName()));
            }
        }
    }
    //提取方法:文件的复制:
    public static void copyFile(File srcFile,File targetFile) throws IOException {
        //选取字节流:
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(targetFile));
        //开始动作:
        byte[] b=new byte[1024];
        int len=bis.read(b);
        while(len!=-1){
            bos.write(b,0,len);
            len=bis.read(b);
        }
        //关闭流:
        bos.close();
        bis.close();
    }
}

12、多线程

程序,进程,线程

在这里插入图片描述
在这里插入图片描述

线程创建的三种方式

第一种:继承Thread类

在这里插入图片描述

package com.bjsxt.test01;
//TestThread类,继承了Thread,它就具备了多线程的能力
//有个多线程的能力,就可以跟其它的线程争抢CPU资源
//TestThread线程类下的具体的对象就是线程对象。
public class TestThread extends Thread{
    //一会要去争抢资源了,这个线程要做的任务是什么呢?将任务写到run方法中:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println("TestThread--------:"+i);
        }
    }
}

package com.bjsxt.test01;
public class Test {
    //在我的程序中现在有三个线程:1.主线程  2.子线程tt  3.垃圾回收机制(忽略不计)
    public static void main(String[] args) {//主线程
        for(int i=1;i<=10;i++){
            System.out.println("main方法(1)----------:"+i);
        }
        //创建一个子线程:TestThread的对象:
        TestThread tt=new TestThread();//子线程
        //tt.run();//想要执行子线程的任务
        tt.start();//启动这个线程
        //start方法是父类Thread中的方法:
        //使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
        for(int i=1;i<=10;i++){
            System.out.println("main方法(2)----------:"+i);
        }
    }
}

设置线程名字

【1】利用setName,getName方法:

package com.bjsxt.test01;
//TestThread类,继承了Thread,它就具备了多线程的能力
//有个多线程的能力,就可以跟其它的线程争抢CPU资源
//TestThread线程类下的具体的对象就是线程对象。
public class TestThread extends Thread{
    //一会要去争抢资源了,这个线程要做的任务是什么呢?将任务写到run方法中:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(this.getName()+i);
        }
    }
}

package com.bjsxt.test01;
public class Test {
    //在我的程序中现在有三个线程:1.主线程  2.子线程tt  3.垃圾回收机制(忽略不计)
    public static void main(String[] args) {//主线程
        Thread.currentThread().setName("主线程");
        for(int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
        //创建一个子线程:TestThread的对象:
        TestThread tt=new TestThread();//子线程
        tt.setName("这是第一个子线程:");
        //tt.run();//想要执行子线程的任务
        tt.start();//启动这个线程
        //start方法是父类Thread中的方法:
        //使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
        for(int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

【2】利用有参构造器:

package com.bjsxt.test01;
//TestThread类,继承了Thread,它就具备了多线程的能力
//有个多线程的能力,就可以跟其它的线程争抢CPU资源
//TestThread线程类下的具体的对象就是线程对象。
public class TestThread extends Thread{
    public TestThread(String name) {
        super(name);
    }
    //一会要去争抢资源了,这个线程要做的任务是什么呢?将任务写到run方法中:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(this.getName()+i);
        }
    }
}

package com.bjsxt.test01;
public class Test {
    //在我的程序中现在有三个线程:1.主线程  2.子线程tt  3.垃圾回收机制(忽略不计)
    public static void main(String[] args) {//主线程
        Thread.currentThread().setName("主线程");
        for(int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
        //创建一个子线程:TestThread的对象:
        TestThread tt=new TestThread("子线程01111111:");//子线程
        //tt.run();//想要执行子线程的任务
        tt.start();//启动这个线程
        //start方法是父类Thread中的方法:
        //使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
        for(int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
龟兔赛跑
package com.bjsxt.test02;
public class Tortoise extends Thread{//乌龟
    //加一个有参构造器,一会用来设置线程的名字:
    public Tortoise(String name) {
        super(name);
    }
    //重写run方法:
    @Override
    public void run() {
        while(true){
            System.out.println("我是"+this.getName()+",我在跑,我现在领先了。。");
        }
    }
}

package com.bjsxt.test02;
public class Rabbit extends Thread {//兔子:
    //用来设置线程名字的
    public Rabbit(String name) {
        super(name);
    }
    //重写run方法:
    @Override
    public void run() {
        while(true){
            System.out.println("我是"+this.getName()+",我没有偷懒,我也在跑。。。");
        }
    }
}
package com.bjsxt.test02;
public class Test {
    public static void main(String[] args) {
        //乌龟线程:
        Tortoise tt=new Tortoise("乌龟线程:");
        tt.start();
        //兔子线程:
        Rabbit rb=new Rabbit("兔子线程");
        rb.start();
    }
}

买火车票

package com.bjsxt.test03;
public class BuyTrainTicket extends Thread {
    //属性:(不同窗口共享10张票)
    private static int ticketNum=10;
    //构造器:设置线程名字:设置窗口的名字:
    public BuyTrainTicket(String name) {
        super(name);
    }
    //每个窗口做什么事:写入run方法:
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            if(ticketNum>0){
                System.out.println("我在"+this.getName()+"买到了从北京到哈尔滨的票,现在票剩余:"+(--ticketNum)+"张");
            }
        }
    }
}
package com.bjsxt.test03;
public class Test {
    public static void main(String[] args) {
        //有几个线程?三个线程。,每个线程就是一个窗口:
        BuyTrainTicket b1=new BuyTrainTicket("窗口1");
        b1.start();
        BuyTrainTicket b2=new BuyTrainTicket("窗口2");
        b2.start();
        BuyTrainTicket b3=new BuyTrainTicket("窗口3");
        b3.start();
    }
}

在这里插入图片描述

第二种:实现Runnable接口

在这里插入图片描述

package com.bjsxt.test04;
public class TestThread implements Runnable{
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+"------:"+i);
        }
    }
}
package com.bjsxt.test04;
public class Test {
    public static void main(String[] args) {//主线程
        //创建子线程:
        TestThread tt=new TestThread();
        Thread t=new Thread(tt,"子线程");//通过构造器将Thread和TestThread进行了关联
        t.start();
        for(int i=1;i<=10;i++){
            System.out.println("main----"+i);
        }
    }
}
扩展:静态代理模式

【1】案例:
吕鹏昌------》追求一个女生---------》找一个人帮他(赵珊珊)–》家聪聪
在这里插入图片描述

public interface Beirenzhui {//被人追
    void maiHua();//买花
    void maizuanjie();//买钻戒
}
public class JiaCongCong implements Beirenzhui {
    @Override
    public void maiHua() {
        System.out.println("小雏菊。。。。");
    }
    @Override
    public void maizuanjie() {
        System.out.println("Darry ring。。。。。");
    }
}
public class LiuFang implements Beirenzhui{
    @Override
    public void maiHua() {
        System.out.println("玫瑰花");
    }
    @Override
    public void maizuanjie() {
        System.out.println("I do ..");
    }
}
public class zhaoshanshan implements Beirenzhui{
   // private JiaCongCong jcc=new JiaCongCong();
    private Beirenzhui brz;
    public zhaoshanshan(Beirenzhui brz){//Beirenzhui brz=new LiuFang();--->多态
        this.brz=brz;//属性Beirenzhui brz=new LiuFang();
    }
    @Override
    public void maizuanjie() {
        brz.maizuanjie();
    }
    //代理中的方法比被代理的类中多!
    public void findFlower(){
        System.out.println("寻求卖花的合作厂商");
    }
    @Override
    public void maiHua() {
        findFlower();
        brz.maiHua();
        jiesuanMoney();
    }
    public void jiesuanMoney(){
        System.out.println("结算钱。。。。");
    }
}

public class LvPengchang {
    public static void main(String[] args) {
        //让代理赵珊珊给家聪聪买花,买钻戒:
        zhaoshanshan zss=new zhaoshanshan(new LiuFang());
        zss.maiHua();
        zss.maizuanjie();
    }
}

总结:代理:zhaoshanshan 被代理:家聪聪,刘芳

关联:利用代理的有参构造器

代理中方法要比被代理的方法 多!

龟兔赛跑
package com.bjsxt.test06;
public class Tortoise implements  Runnable{//乌龟
    @Override
    public void run() {
        while(true){
            System.out.println(Thread.currentThread().getName()+"一直跑");
        }
    }
}
package com.bjsxt.test06;
public class Test {
    public static void main(String[] args) {
        //乌龟:
        Tortoise tt=new Tortoise();
        Thread t=new Thread(tt);
        t.setName("乌龟");
        t.start();
        //兔子:参数为:匿名内部类
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("兔子一直在跑");
                }
            }
        });
        t2.start();
    }
}
买火车票
package com.bjsxt.test07;
public class BuyTrainTicket implements Runnable {
    private int ticketNum=10;
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            if(ticketNum>0){
                System.out.println("我在"+Thread.currentThread().getName()+"买到了火车票,剩余"+(--ticketNum)+"张");
            }
        }
    }
}

package com.bjsxt.test07;
public class Test {
    public static void main(String[] args) {
        BuyTrainTicket b=new BuyTrainTicket();
        //窗口1:
        new Thread(b,"窗口1").start();
        //窗口2:
        BuyTrainTicket b2=new BuyTrainTicket();
        new Thread(b,"窗口2").start();
        //窗口3:
        BuyTrainTicket b3=new BuyTrainTicket();
        new Thread(b,"窗口3").start();
    }
}

第三种:实现Callable接口

【1】第一种和第二种的run方法的缺点:

public class Test implements  Runnable{
    @Override
    public void run() {
        
    }
}

缺点1:这个方法没有返回值。
缺点2:不能抛出异常
所以基于上面两个缺点,引入了方式3:

【3】方式3:JDK1.5以后为了解决上述缺点:

好处:call方法可以有返回值,还可以处理异常
缺点:线程创建比较麻烦
get方法是一个阻塞方法,什么时候线程代码执行完毕才会将返回值做一个获取。

package com.bjsxt.test08;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class RundomNum implements Callable<Integer>{//产生随机数的线程
    //对于这个call方法来说,有返回值,返回值的类型跟泛型是一致的,并且对于这个方法是可以抛出异常的
    @Override
    public Integer call() /*throws Exception*/ {
        System.out.println("--------");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new Random().nextInt(10);//生成10以内的随机数
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建线程对象:
        RundomNum rn=new RundomNum();
        FutureTask ft=new FutureTask(rn);
        Thread t=new Thread(ft);
        t.start();
        //获取线程执行的结果:
        //是一个阻塞的方法
        System.out.println("当前线程是否在完成:"+ft.isDone());
        Integer i=(Integer)(ft.get());//接收call方法的返回值的。
        System.out.println(i);
        System.out.println("当前线程是否在完成:"+ft.isDone());
    }
}

总结

第一种和第二种哪个好?
java中只有单继承,所以用第二种实现接口的方式,还可以继承其他的类,

第三种好处:
(1)call方法有返回值
(2)可以抛出异常

线程的生命周期

在这里插入图片描述
在这里插入图片描述

控制线程常用方法

优先级

在这里插入图片描述

public class Tortoise extends Thread{//乌龟
    //加一个有参构造器,一会用来设置线程的名字:
    public Tortoise(String name) {
        super(name);
    }
    //重写run方法:
    @Override
    public void run() {
        while(true){
            System.out.println("我是"+this.getName()+",我在跑,我现在领先了。。乌龟的优先级级别:"+this.getPriority());
        }
    }
}

package com.bjsxt.test02;
public class Rabbit extends Thread {//兔子:
    //用来设置线程名字的
    public Rabbit(String name) {
        super(name);
    }
    //重写run方法:
    @Override
    public void run() {
        while(true){
            System.out.println("我是"+this.getName()+",我没有偷懒,我也在跑。。。兔子的优先级别:"+this.getPriority());
        }
    }
}

package com.bjsxt.test02;
public class Test {
    public static void main(String[] args) {
        //乌龟线程:
        Tortoise tt=new Tortoise("乌龟线程:");
        tt.setPriority(9);
        tt.start();
        //兔子线程:
        Rabbit rb=new Rabbit("兔子线程");
        rb.setPriority(1);
        rb.start();
    }
}

join

join方法:当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程。----“半路杀出个程咬金”
注意:必须先start,再join才有效。

package com.bjsxt.test09;
public class TestThread extends Thread {
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            System.out.println(this.getName()+"---------"+i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for(int i=1;i<=10;i++){
            if(i==6){
                TestThread tt=new TestThread();
                tt.start();
                tt.join();//"半路杀出个程咬金"
            }
            System.out.println(Thread.currentThread().getName()+"-----"+i);
        }
    }
}

sleep

睡眠:
Thread.sleep(3000);//遇到这个方法,程序就被阻塞
//什么时候“睡完了”,这个阻塞就结束了

【1】龟兔赛跑加上裁判倒计时:

public class Test {
    public static void main(String[] args) {
        //裁判:喊: 3.。2  。。。1
        for(int i=3;i>=1;i--){
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //乌龟线程:
        Tortoise tt=new Tortoise("乌龟线程:");
        tt.setPriority(9);
        tt.start();
        //兔子线程:
        Rabbit rb=new Rabbit("兔子线程");
        rb.setPriority(1);
        rb.start();
    }
}

【2】完成一个秒表的功能:

package com.bjsxt.test09;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
    public static void main(String[] args) {
        //5.格式化提到while循环之外:
        DateFormat df=new SimpleDateFormat("HH:mm:ss");
        //3.加入死循环,让时间一直显示:
        while(true){
            //实现一个秒表:
            //1.获取当前时间:
            Date d=new Date();
            //2.将时间按照我想要展现的格式展现:进行日期的格式化:
            String strDate=df.format(d);
            System.out.println(strDate);
            //4.停顿一秒:
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

yield()

package com.bjsxt.test10;
public class TestThread extends Thread {
    public TestThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            System.out.println(this.getName()+"---"+i);
            if(i==6){
                Thread.yield();
                //yield:暂停当前线程,使线程进入到就绪状态(不是阻塞,是就绪),进入到就绪状态以后等着CPU的调度,
可能立马被调用,也可能等着被调用。
            }
        }
    }
    public static void main(String[] args) {
        //创建一个线程
        TestThread t1=new TestThread("线程1");
        t1.start();
        //创建一个线程
        TestThread t2=new TestThread("线程2");
        t2.start();
    }
}

setDaemon

设置伴随线程;
皇上 —》陪葬----》妃子
main主线程—》伴随线程----》DemoThread
但是在主线程结束以后,DemoThread “垂死挣扎”了一下,趁这个时间赶紧再执行一点代码:
在这里插入图片描述

package com.bjsxt.test10;
public class DemoThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=1000;i++){
            System.out.println(this.getName()+"----"+i);
        }
    }
    public static void main(String[] args) {
        //子线程创建+启动:
        DemoThread dt=new DemoThread();
        dt.setName("子线程");
        //将子线程设置为伴随线程:
        dt.setDaemon(true);
        dt.start();
        for(int i=1;i<=10;i++){
            System.out.println("main-----"+i);
        }
    }
}

stop

public class Test {
    public static void main(String[] args) {
        for(int i=1;i<=100;i++){
            if(i==6){
                Thread.currentThread().stop();
            }
            System.out.println(i);
        }
    }
}

线程安全、通信,线程池

线程安全问题

在这里插入图片描述
在这里插入图片描述
原因:
就是因为多个线程在争抢资源:

解决:
在我的程序中加入–“锁”---->加同步----》同步监视器
加锁方式1:利用同步代码块:

package com.bjsxt.test07;
public class BuyTrainTicket implements Runnable {
    private int ticketNum=10;
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            synchronized (this){//锁:()中的内容:this
                if(ticketNum>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("我在"+Thread.currentThread().getName()+"买到了火车票,剩余"+(--ticketNum)+"张");
                    //System.out.println("我在"+Thread.currentThread().getName()+"买到了火车票,买到了第"+(ticketNum--)+"张车票");
                }
            }
        }
    }
}

加锁方式2:利用同步方法:

package com.bjsxt.test07;
public class BuyTrainTicket implements Runnable {
    private int ticketNum=10;
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            buyTicket();
        }
    }
//同步方法谁是锁?  锁的是this,谁调用方法就把谁锁了。
    public synchronized void buyTicket(){
        if(ticketNum>0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我在"+Thread.currentThread().getName()+"买到了火车票,剩余"+(--ticketNum)+"张");
            //System.out.println("我在"+Thread.currentThread().getName()+"买到了火车票,买到了第"+(ticketNum--)+"张车票");
        }
    }
}

加了同步,代码的执行过程是什么样的?
线程1:进入到run方法中,看到程序中有锁,但是没有锁住,然后它将这个锁锁住。
其他的线程可以进来,但是看到锁被锁住了,只能等着,
等到什么时候啊?等到线程1完事了,将锁进行释放,释放完了之后其余的线程就可以将这个锁抢过去了,然后谁抢到谁再加上锁。

--------》
另一个火车票的代码:

package com.bjsxt.test03;
public class BuyTrainTicket extends Thread {
    //属性:(不同窗口共享10张票)
    private static int ticketNum=10;
    //构造器:设置线程名字:设置窗口的名字:
    public BuyTrainTicket(String name) {
        super(name);
    }
    //每个窗口做什么事:写入run方法:
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            synchronized (BuyTrainTicket.class){//"abc"---只能将引用数据类型作为锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(ticketNum>0){
                    System.out.println("我在"+this.getName()+"买到了从北京到哈尔滨的票,现在票剩余:"+(--ticketNum)+"张");
                }
            }
        }
    }
}

总结:
多线程在争抢资源,就要实现线程的同步(就要进行加锁),
并且这个锁必须是共享的,必须是唯一的。
咱们的锁一般都是引用数据类型的。

目的:解决了线程安全问题。

习题

银行卡:主卡 和 副卡
男主人 女主人—》主卡 和 副卡

package com.bjsxt.test11;
public class AccoutThread implements Runnable{
    //共享账号:
    private Account ac=new Account();
    @Override
    public void run() {
       /*synchronized (this){
           if(ac.getBalance()>=400){
               ac.getMoney(300);
               System.out.println(Thread.currentThread().getName()+"在取款,取款后余额为:"+ac.getBalance());
           }else{
               System.out.println(Thread.currentThread().getName()+"在取款,但是余额不足400,取款失败");
           }
       }*/
        getM();
    }
    public synchronized  void getM(){
        if(ac.getBalance()>=400){
            ac.getMoney(300);
            System.out.println(Thread.currentThread().getName()+"在取款,取款后余额为:"+ac.getBalance());
        }else{
            System.out.println(Thread.currentThread().getName()+"在取款,但是余额不足400,取款失败");
        }
    }
}

package com.bjsxt.test11;
public class Account {
    //共享钱:
    int money=600;
    //取钱
    public void getMoney(int money){
        this.money-=money;//this.money=this.money-money
    }
    //得到余额
    public int getBalance(){
        return money;
    }
}

package com.bjsxt.test11;
public class Test {
    public static void main(String[] args) {
        AccoutThread ac=new AccoutThread();
        new Thread(ac,"男主人").start();
        new Thread(ac,"女主人").start();
    }
}

线程同步的缺点

【1】
线程安全,效率低
线程不安全,效率高

【2】可能造成死锁:

public class TestDeadLock implements Runnable {
    public int flag = 1;
    static Object o1 = new Object(),o2 = new Object();
        
        
    public void run(){
        System.out.println("flag=" + flag);
        // 当flag==1锁住o1
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // 只要锁住o2就完成
                synchronized (o2) {
                    System.out.println("2");
                }
            }
        }
        // 如果flag==0锁住o2
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // 只要锁住o1就完成
                synchronized (o1) {
                    System.out.println("3");
                }
            }
        }
    }
        
        
    public static void main(String[] args) {
        // 实例2个线程类
        TestDeadLock td1 = new TestDeadLock();
        TestDeadLock td2 = new TestDeadLock();
        td1.flag = 1;
        td2.flag = 0;
        // 开启2个线程
        Thread t1 = new Thread(td1);
        Thread t2 = new Thread(td2);
        t1.start();
        t2.start();
    }
}

疑问1:
(1)在这里插入图片描述

static去掉,还会死锁吗?不会
(2)如何解决死锁?
在这里插入图片描述

Lock锁

Lock锁:
JDK1.5后新增功能,
与采用synchronized相比,lock可提供多种锁方案,更灵活

    注意:如果同步代码有异常,要将unlock()写入finally语句块

                                    ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,
 但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。
  此外,它还提供了在激烈争用情况下更佳的性能。


    Lock和synchronized的区别
    1.Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁
    2.Lock只有代码块锁,synchronized有代码块锁和方法锁
    3.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

    优先使用顺序:
    Lock----同步代码块(已经进入了方法体,分配了相应资源)----同步方法(在方法体之外)
package com.bjsxt.test07;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BuyTrainTicket implements Runnable {
    private int ticketNum=10;
    //"买来一把锁":
    Lock lock=new ReentrantLock();//多态
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            //打开锁
            lock.lock();
            try{
                if(ticketNum>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("我在"+Thread.currentThread().getName()+"买到了火车票,剩余"+(--ticketNum)+"张");
                }
            }catch(Exception ex){
                ex.printStackTrace();
            }finally {
                //关闭锁
                lock.unlock();
            }
        }
    }
}

线程组

package com.bjsxt.test12;
public class TestThread extends Thread{
    public TestThread() {
    }
    //两个参数:第一个参数设置线程所在的线程组,第二个参数:设置线程的名字的
    public TestThread(ThreadGroup group, String name) {
        super(group, name);
    }
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(i);
        }
    }
//设置线程名字
    public TestThread(String name) {
        super(name);
    }
    public static void main(String[] args) {
        TestThread tt=new TestThread();
        tt.setName("这是一个线程名字");
        System.out.println(tt.toString());
        //Thread[Thread-0,5,main]-->线程名字,优先级别,线程组名字:
        System.out.println(tt.getThreadGroup().getName());//main
        System.out.println(tt.getThreadGroup().getParent().getName());//上层组:system
        System.out.println(tt.getThreadGroup().getParent().getParent());//null
        //创建一个线程组:
        ThreadGroup tg=new ThreadGroup("我的线程组哈哈哈");
        System.out.println(tg.getName());
        TestThread t1=new TestThread(tg,"线程1");
        TestThread t2=new TestThread(tg,"线程2");
        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t1.getThreadGroup().getParent().getName());
        t1.start();
        t2.start();
        System.out.println("------------");
        tg.list();
    }
}

线程之间的通信问题

应用场景:生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
在这里插入图片描述

最终代码结果:
在这里插入图片描述
在这里插入图片描述

代码分析:
商品:被生产者和消费者共享:商品属性:品牌,名字
线程1:生产者
线程2:消费者

分解1:
package com.bjsxt.test13;
public class Product {//商品
    //属性:
    //品牌
    private String brand;
    //名字
    private String name;
    //提供setget方法:
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

package com.bjsxt.test13;
public class ProducerThread extends Thread {//生产者线程
    //共享商品:
    private Product p;
    public ProducerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            if(i%2==0){
                //费列罗巧克力:
                p.setBrand("费列罗");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                p.setName("巧克力");
            }else{
                p.setBrand("哈尔滨");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                p.setName("啤酒");
            }
            //将信息打印:
            System.out.println("生产者生产了:"+p.getBrand()+"----"+p.getName());
        }
    }
}

package com.bjsxt.test13;
public class CustomerThread extends Thread {//消费者线程
    private Product p;
    public CustomerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println("消费者消费,消费了:"+p.getBrand()+"----"+p.getName());
        }
    }
}
package com.bjsxt.test13;
public class Test {
    public static void main(String[] args) {
        //创建一个共享商品:
        Product p=new Product();
        //创建生产者和消费者线程:
        CustomerThread ct=new CustomerThread(p);
        ProducerThread pt=new ProducerThread(p);
        pt.start();
        ct.start();
    }
}

代码出现的问题:
(1)没有交替生产和消费
正常效果生产一个消费一个,但是现在生产好几个,消费好几个。
(2)打印数据错乱:
哈尔滨 null
哈尔滨 巧克力
费列罗啤酒

分解2:

【1】加锁:利用同步代码块:

package com.bjsxt.test14.test13;
public class Product {//商品
    //属性:
    //品牌
    private String brand;
    //名字
    private String name;
    //提供setget方法:
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
package com.bjsxt.test14.test13;
public class ProducerThread extends Thread {//生产者线程
    //共享商品:
    private Product p;
    public ProducerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            synchronized (p){
                if(i%2==0){
                    //费列罗巧克力:
                    p.setBrand("费列罗");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    p.setName("巧克力");
                }else{
                    p.setBrand("哈尔滨");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    p.setName("啤酒");
                }
                //将信息打印:
                System.out.println("生产者生产了:"+p.getBrand()+"----"+p.getName());
            }
        }
    }
}

package com.bjsxt.test14.test13;
public class CustomerThread extends Thread {//消费者线程
    private Product p;
    public CustomerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            synchronized (p){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者消费,消费了:"+p.getBrand()+"----"+p.getName());
            }
        }
    }
}
package com.bjsxt.test14.test13;
public class Test {
    public static void main(String[] args) {
        //创建一个共享商品:
        Product p=new Product();
        //创建生产者和消费者线程:
        CustomerThread ct=new CustomerThread(p);
        ProducerThread pt=new ProducerThread(p);
        pt.start();
        ct.start();
    }
}

【2】加锁:同步方法:

package com.bjsxt.test15.test13;
public class Product {//商品
    //属性:
    //品牌
    private String brand;
    //名字
    private String name;
    //提供setget方法:
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //生产产品:
    public synchronized void setProduct(String brand,String name){
        this.setBrand(brand);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setName(name);
        //将信息打印:
        System.out.println("生产者生产了:"+this.getBrand()+"----"+this.getName());
    }
    //消费商品
    public synchronized void getProduct(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("消费者消费,消费了:"+this.getBrand()+"----"+this.getName());
    }
}

package com.bjsxt.test15.test13;
public class CustomerThread extends Thread {//消费者线程
    private Product p;
    public CustomerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            p.getProduct();
        }
    }
}
package com.bjsxt.test15.test13;
public class ProducerThread extends Thread {//生产者线程
    //共享商品:
    private Product p;
    public ProducerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            if(i%2==0){
                //费列罗巧克力:
                p.setProduct("费列罗","巧克力");
            }else{
               p.setProduct("哈尔滨","啤酒");
            }
        }
    }
}
package com.bjsxt.test15.test13;
public class Test {
    public static void main(String[] args) {
        //创建一个共享商品:
        Product p=new Product();
        //创建生产者和消费者线程:
        CustomerThread ct=new CustomerThread(p);
        ProducerThread pt=new ProducerThread(p);
        pt.start();
        ct.start();
    }
}

现在代码中还有的问题:生产和消费并没有交替进行;

分解3:

在这里插入图片描述

package com.bjsxt.test15.test13;
public class Product {//商品
    //属性:
    //品牌
    private String brand;
    //名字
    private String name;
    //加入一个“灯”:
    boolean flag=false;//最开始这个灯应该是绿色的:false--》没有商品
    //提供setget方法:
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //生产产品:
    public synchronized void setProduct(String brand,String name){
        if(flag==true){//灯是红色:生产者不生产的,等着
            try {
                wait();//Object的方法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果灯是绿色的呢?开始生产商品:
        this.setBrand(brand);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setName(name);
        //将信息打印:
        System.out.println("生产者生产了:"+this.getBrand()+"----"+this.getName());
        //截止到上面,商品已经生产完了:
        //将灯变成:红色
        flag=true;
        //告诉消费者赶紧来消费:
        notify();//Object类方法
    }
    //消费商品
    public synchronized void getProduct(){
        if(!flag){//flag==false--->如果灯是绿色的:
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果灯是红色的:开始消费:
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("消费者消费,消费了:"+this.getBrand()+"----"+this.getName());
        //截止到这里,消费结束
        //灯变成:绿色:
        flag=false;
        //通知生产者赶紧生产:
        notify();
    }
}
package com.bjsxt.test15.test13;
public class ProducerThread extends Thread {//生产者线程
    //共享商品:
    private Product p;
    public ProducerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            if(i%2==0){
                //费列罗巧克力:
                p.setProduct("费列罗","巧克力");
            }else{
                p.setProduct("哈尔滨","啤酒");
            }
        }
    }
}
package com.bjsxt.test15.test13;
public class CustomerThread extends Thread {//消费者线程
    private Product p;
    public CustomerThread(Product p){
        this.p=p;
    }
    //重写run方法:
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            p.getProduct();
        }
    }
}

package com.bjsxt.test15.test13;
public class Test {
    public static void main(String[] args) {
        //创建一个共享商品:
        Product p=new Product();
        //创建生产者和消费者线程:
        CustomerThread ct=new CustomerThread(p);
        ProducerThread pt=new ProducerThread(p);
        pt.start();
        ct.start();
    }
}

在这里插入图片描述

线程池

队列

在这里插入图片描述

线程池

在这里插入图片描述

内置线程池

JDK1.5之后提供了内置线程池:
【1】可缓存线程池:

public class Test {
    public static void main(String[] args) {
        //可缓存线程:
        ExecutorService es = Executors.newCachedThreadPool();
        //执行任务:
        for(int i=1;i<=100;i++){
            es.execute(new TestThread());
        }
        //关闭:
        es.shutdown();
    }
}

最开始没有核心线程,来一个任务,新建一个线程来执行这个任务,当这个任务执行完以后,这个线程继续执行其他的任务,所以在结果中我们发现,线程可以是重复的:
在这里插入图片描述
【2】定长线程池:

public class Test {
    public static void main(String[] args) {
        //定长线程:
        ExecutorService es = Executors.newFixedThreadPool(3);
        //执行任务:
        for(int i=1;i<=100;i++){
            es.execute(new TestThread());
        }
        //关闭:
        es.shutdown();
    }
}

在这里插入图片描述
【3】定时线程池:

public class Test {
    public static void main(String[] args) {
        //定时线程:
        ScheduledExecutorService ses=Executors.newScheduledThreadPool(3);
        //执行任务:
        for(int i=1;i<=100;i++){
            ses.schedule(new TestThread(),3, TimeUnit.SECONDS);
        }
        //关闭:
        ses.shutdown();
    }
}

进行延时:延时3秒执行线程
【4】单例线程池:

public class Test {
    public static void main(String[] args) {
        //单例线程:
        ExecutorService es= Executors.newSingleThreadExecutor();
        //执行任务:
        for(int i=1;i<=100;i++){
           es.execute(new TestThread());
        }
        //关闭:
        es.shutdown();
    }
}

就是由一个线程来执行任务

public class Test {
    public static void main(String[] args) {
        //单例线程:
        ExecutorService es= Executors.newSingleThreadExecutor();
        //执行任务:
        for(int i=1;i<=100;i++){
            //利用匿名内部类的对象传入execute方法也是可以的:
           es.execute(new Runnable() {
               @Override
               public void run() {
                   System.out.println("当前线程为:"+Thread.currentThread().getName());
               }
           });
        }
        //关闭:
        es.shutdown();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1968457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot整合Juint,ssm框架

目录 SpringBoot整合Juint 1.导入相关的依赖 2.创建测试类&#xff0c;使用注解SpringBootTest SpringBoot整合ssm框架 1.使用脚手架创建Spring项目 2.修改pom.xml 我先修改了SpringBoot的版本&#xff0c;修改为2.3.10.RELEASE&#xff0c;因为SpringBoot版本太高会出现…

数据集——鸢尾花介绍和使用

文章目录 一、鸢尾花数据集内容二、使用中常转换DataFrame 一、鸢尾花数据集内容 from sklearn import svm, datasets # 鸢尾花数据 iris datasets.load_iris() print(iris.data) X iris.data[:, :2] # 为便于绘图仅选择2个特征 y iris.target它包含了150个样本&#xff0c…

3.8.语义分割

语义分割 ​ 语义分割将图片中的每个像素分类到对应的类别(有监督学习) 1.图像分割和实例分割 图像分割将图像划分为若干组成区域&#xff0c;这类问题的方法通常利用图像中像素之间的相关性。它在训练时不需要有关图像像素的标签信息&#xff0c;在预测时也无法保证分割出的区…

单细胞数据整合-去除批次效应harmony和CCA (学习)

目录 单细胞批次效应学习 定义 理解 常用的去批次方法-基于Seurat 1&#xff09; Seurat-integration&#xff08;CCA&#xff09; 2&#xff09; Seurat-harmony 去批次代码 ①Seurat-integration&#xff08;CCA&#xff09; ②Seurat-harmony 单细胞批次效应学习 …

【C++进阶学习】第十一弹——C++11(上)——右值引用和移动语义

前言&#xff1a; 前面我们已经将C的重点语法讲的大差不差了&#xff0c;但是在C11版本之后&#xff0c;又出来了很多新的语法&#xff0c;其中有一些作用还是非常大的&#xff0c;今天我们就先来学习其中一个很重要的点——右值引用以及它所扩展的移动定义 目录 一、左值引用和…

AI智驾时代降临,端到端奏响“三重奏”

“追上未来&#xff0c;抓住它的本质&#xff0c;把未来转变为现在”&#xff0c;俄国哲学家车尔尼雪夫斯曾这样描述未来。而走到今天的新能源汽车&#xff0c;其通向未来的本质就是做好智能化。 呐喊智能化的口号&#xff0c;从2023年延续到2024年。如今&#xff0c;智能化的…

如何清理电脑浏览器缓存和内存 macbookpro浏览器怎么清理

浏览器已经成为我们日常生活中不可或缺的工具。然而&#xff0c;随着时间的推移&#xff0c;浏览器缓存的积累可能会逐渐影响我们的上网体验&#xff0c;导致网页加载速度变慢、浏览器运行卡顿等问题。因此&#xff0c;定期清理浏览器缓存变得尤为重要。那么Mac怎么清除浏览器缓…

单元测试之打桩-stub

首先&#xff0c;了解两个单元测试和集成测试的基本概念&#xff1a; 驱动程序/驱动模块&#xff08;driver&#xff09;&#xff0c;用以模拟被测模块的上级模块。驱动模块在集成测试中接受测试数据&#xff0c;把相关的数据传送给被测模块&#xff0c;启动被测模块。 桩程序…

Kubernetes Prometheus 系列 | AlertManager实现企业微信报警

helm部署prometheusgrafana直通车&#xff08;与本文章关联&#xff09; 首先注册企业微信&#xff1a;https://work.weixin.qq.com/ 目录 一、第一种根据企业id&#xff0c;应用secret等绑定二、第二种方式-添加群聊天机器人webhook&#xff08;推荐&#xff09; 前言&#x…

滑动窗口专题

前言&#xff1a; ①单调队列模板&#xff08;左边是队头hh&#xff0c;右边是队尾tt&#xff09; 首先维护两个指针hh和tt的位置 常见模型&#xff1a;找出滑动窗口中的最大值/最小值 int hh 0, tt -1; for (int i 0; i < n; i ) {while (hh < tt && check_…

区间DP---多边形 与金字塔

多边形&#xff1a;https://www.acwing.com/problem/content/285/ 其实就是环形的石子合并&#xff0c;只不过由于乘法的存在还要记录一下最大值与最小值。 AC代码&#xff1a; #include <bits/stdc.h> using namespace std; int a[105]; char b[105]; int dpmax[105]…

U盘跨机使用难题:打不开的困境与数据恢复之道

在数字时代&#xff0c;U盘作为我们日常数据交换和存储的重要工具&#xff0c;扮演着不可或缺的角色。然而&#xff0c;当您满怀期待地将U盘插入另一台电脑&#xff0c;却遭遇“无法打开”的尴尬时&#xff0c;那份焦急与无助可想而知。本文将深入探讨“U盘在别的电脑用了后打不…

隐写工具steghide linux编译安装

1、git clone https://github.com/StefanoDeVuono/steghide.git 2、autoreconf -i 3、./configure 4、make 编译完成后再src目录下又steghide执行下程序 报错&#xff1a;configure: error: cannot find required auxiliary files: compile时需要执行autoreconf 如果往j…

外卖项目day11---微信支付(跳过微信接口进行伪支付,超超超细节哦!!!)/cpolar内网穿透工具使用

这里一般个人是没有的&#xff0c;我们用不到 内网穿透工具临时获取到临时域名(可用可不用&#xff0c;拓展一下也好&#xff09; 安装客户端&#xff0c;在相应目录打开cmd窗口&#xff0c;输入以下代码 可以看到这里的请求地址变了&#xff0c;这就是内网穿透工具的作用 微信…

Windows Server 2025 Preview 部署 Ⅱ—— 在ESXi 8上安装 Windows Server 2025

目录 3. 在ESXi 8上安装 Windows Server 20253.1 创建Win Server 虚拟机&#xff08;1&#xff09;创建 VM&#xff08;2&#xff09;选择创建类型&#xff08;3&#xff09;设置VM名和选择guest OS&#xff08;4&#xff09;选择存储&#xff08;5&#xff09;自定义虚拟机组件…

leafletjs-gis中如何判断图层或元素是点,线,面的类型

查找地图上的图层&#xff0c;要素&#xff1a; window.MapLeaflet.eachLayer(function (layer) {if (layer ! window.MapLeaflet) {console.log("layer instanceof L.Polyline", layer instanceof L.Polyline)// L.Marker,L.Polyline, L.Polygon||L.Rectangle,L.C…

spring boot(学习笔记第十六课)

spring boot(学习笔记第十六课) Spring boot的websocket(点对点) 学习内容&#xff1a; Spring boot的websocket&#xff08;点对点&#xff09;spring boot(学习笔记第十六课) 1. Spring boot的websocket&#xff08;点对点&#xff09; 前面练习了websocket的广播模式。 接…

1.GPIO

理论说明 输入 上拉输入&#xff1a;拉高电平 下拉输入&#xff1a;拉低电平 浮空输入&#xff1a;不拉高也不拉低电平 输出 开漏输出&#xff1a;不能输出高电平&#xff08;P-MOS不可用&#xff0c;则只能低电平&#xff09; 推挽输出&#xff1a;可输出高低电平 输出速率…

informer中DeltaFIFO机制的实现分析与源码解读

informer中的DeltaFIFO机制的实现分析与源码解读 DeltaFIFO作为informer中重要组件&#xff0c;本文从源码层面了解是如何DelatFIFO是实现的。 DeltaFIFO的定义 找到delta_fifo.go的源码&#xff0c;位于client-go/tools/cache/delta_fifo.go 代码结构大致如下: store定义…

【priority_queue的模拟实现】

priority_queue的模拟实现 小杨 容器适配器&#xff1a;priority_queue priority_queue即优先级队列是一种容器适配器&#xff0c;根据严格的弱排序标准(即排序规则可以更改),他的第一个元素总是它所包含的元素中最大的。优先队列将特定容器类封装作为其底层容器类。标准容器类…