Java总结

Java总结

第三章面向对象

面向对象的思想
1、概述

前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式

面向过程的思想
1、概述

我们来回想一下,这几天我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。
在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。
那么什么是面向过程开发呢? 面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。
面向过程的代表语言:C语言

面向对象的思想
1、概述

当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始思索,能不能把这些步骤和功能在进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。

成员变量和局部变量的区别

1.在类中的位置不同
成员变量 类中方法外
局部变量 方法内或者方法声明上
2.在内存中的位置不同
成员变量 堆内存
局部变量 栈内存
3.生命周期不同
成员变量 随着对象的存在而存在,随着对象的消失而消失
局部变量 随着方法的调用而存在,随着方法的调用完毕而消失
4.初始化值不同
成员变量 有默认的初始化值
局部变量 没有默认的初始化值,必须先定义,赋值,才能使用。

一个标准类的定义
1、标准类的格式
java中标准类的定义 2.0版本
    1、成员变量需要使用private关键字修饰
    2、提供两个构造方法(无参构造方法/有参构造方法)
    3、对每一个私有的成员变量提供公共的setXxx()getXxx()
    4、提供一个show方法,展示对象的成员变量值的情况(这个方法后面会被另外一个方法替换)
class Student3 {
    //成员变量
    private String name;
    private int age;

    //构造方法
    Student3() {
    }

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

    //成员方法:公共的setXxx()和getXxx(),以及类其他的方法
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

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

    public int getAge() {
        return this.age;
    }

    //show方法
    public void show() {
        System.out.println("姓名:" + this.name);
        System.out.println("年龄:" + this.age);
    }
}

public class StudentDemo2 {
    public static void main(String[] args) {
        //给对象成员变量赋值的方式:
        //1、在有了对象之后,通过setXxx()进行赋值
//        Student3 s1 = new Student3();
        s1.show();
//        s1.setName("张三");
//        s1.setAge(18);
//        s1.show();

        //2、直接在创建对象的时候,使用有参构造方法进行赋值
        Student3 s2 = new Student3("李四", 16);
        s2.show();
    }
}
1.1、封装

概述

​ 是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

封装的原则

​ 将不需要对外提供的内容都隐藏起来。
​ 把属性隐藏,提供公共方法对其访问。

1.2、private关键字

是一个权限修饰符。
可以修饰成员(成员变量和成员方法)
被private修饰的成员只在本类中才能访问

1.3、this关键字

代表所在类的对象引用

方法被哪个对象调用,this就代表那个对象

1.4、构造方法

**概述:**给对象的数据进行初始化

**构造方法格式:**方法名与类名相同没有返回值类型,连void都没有没有具体的返回值

**构造方法注意事项:**如果你不提供构造方法,系统会给出默认构造方法,如果你提供了构造方法,系统将不再提供,构造方法也是可以重载的

1.5、成员方法
4种成员方法:
    1)无参无返回值的方法
    2)无参有返回值的方法
    3)有参无返回值的方法
    4)有参有返回值的方法
    
public class FunctionDemo {
    //1)无参无返回值的方法
    public void fun1() {
        System.out.println("fun1");
    }

    //2)无参有返回值的方法
    public int fun2() {
        return 100;
    }

    //3)有参无返回值的方法
    public void fun3(String s) {
        System.out.println("这是有参无返回值的 " + s);
    }

    //4)有参有返回值的方法
    public String fun4(String s) {
        return "数加" + s;
    }

}
2、面向对象的练习
2.1、定义一个类Demo,其中定义一个求两个数据和的方法,定义一个测试了Test,进行测试
class Demo1 {
    //成员变量
    private int a;
    private int b;

    //构造方法
    public Demo1() {
    }

    public Demo1(int a, int b) {
        this.a = a;
        this.b = b;
    }

    //成员方法:
    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    public int sum() {
        return a + b;
    }

    public void show() {
        System.out.println("a: " + a);
        System.out.println("b: " + b);
    }
}


public class FengZhuangTest1 {
    public static void main(String[] args) {
//        Demo1 demo1 = new Demo1();
        System.out.println(demo1.fun1());
//        System.out.println(demo1.fun(12,34));

        Demo1 demo1 = new Demo1(12, 13);
        System.out.println(demo1.sum());

    }
}
2.2、定义一个长方形类,定义 求周长和面积的方法,然后定义一个测试了Test2,进行测试
class Rectangle {
    private int length;
    private int width;

    public Rectangle() {
    }

    public Rectangle(int length, int width) {
        this.length = length;
        this.width = width;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getPerimeter() {
        return (length + width) * 2;
    }

    public int getSize() {
        return length * width;
    }

    public void show() {
        System.out.println("长为:" + length);
        System.out.println("宽为:" + width);
        System.out.println("周长为:" + getPerimeter());
        System.out.println("面积为:" + getSize());
    }
}

public class FengZhuangTest2 {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle(10, 5);
        rectangle.show();
    }
}
3、static关键字
java中提供了一个关键字,可以将变量变成类变量(共享变量),这个关键字:static 静态的

被static修饰的成员,会被该类所有的对象共享
class Person {
    private String name;
    private int age;
    private static String nationality;

    public Person() {
    }

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

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

    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 getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public void show() {
        System.out.println("姓名:" + name + ", 年龄:" + age + ", 国籍:" + nationality);
    }
}

public class StaticDemo1 {
    public static void main(String[] args) {
        //创建几个对象,都是中国人
        Person p1 = new Person("胡歌", 40, "中国");
        Person p2 = new Person("邓紫棋", 30);
        Person p3 = new Person("薛之谦", 40);

        p1.show();
        p2.show();
        p3.show();
    }
}
3.1注意事项
static: 静态的,可以修饰成员变量,成员方法,代码块
static修饰成员变量的注意事项:
    1、被static修饰的成员叫做类成员,被该类所有的对象共享、
    2、被static修饰的成员优先对象而存在于方法区中的静态区,并且有个默认值
    3、被static修饰的成员叫做类成员,将来可以直接使用类名进行访问,类名.类成员
class Demo3 {
    String name;
    int age;
    static String nationality;

    public Demo3() {
    }

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

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

    public void show() {
        System.out.println("姓名:" + name + ", 年龄:" + age + ", 国籍:" + nationality);
    }

}


public class StaticDemo2 {
    public static void main(String[] args) {
//        Demo3 d1 = new Demo3("张三", 18, "加拿大");
//        Demo3 d2 = new Demo3("李四", 17);
//        Demo3 d3 = new Demo3("王五", 16);

//        d1.show();
//        d2.show();
//        d3.show();
//        System.out.println(d1.nationality);
        System.out.println(Demo3.nationality);

//        System.out.println(d1.name);



    }
}

3.2、静态成员方法的特点
探究静态成员方法的使用特点:
    1、非静态的成员方法,既可以访问非静态的成员变量,也可以访问静态的成员变量
    2、静态的成员方法,只能访问静态的成员变量/静态的成员方法
class Demo4{
    static int a = 10;
    int b = 20;

    public void fun1(){
        System.out.println(a);
        System.out.println("这是一个非静态的方法");
    }

    public static void fun2(){
        System.out.println(a);
//        System.out.println(b); // 无法从静态上下文中引用非静态 变量 b
        System.out.println("这是一个静态的方法");
//        fun1(); // 无法从静态上下文中引用非静态 方法 fun1()
    }
}

public class StaticDemo3 {
    public static void main(String[] args) {
//        Demo4 demo4 = new Demo4();
        demo4.fun1();
//        demo4.fun2();
//        show();

        Demo4.fun2();
    }

    public static void show(){
        System.out.println("hello");
    }
}
3.3、静态的内存图

静态的内容存在于方法区的静态区

静态区

3.4、静态成员变量和成员变量的区别

所属不同
静态变量属于类,所以也称为为类变量
成员变量属于对象,所以也称为实例变量(对象变量)
内存中位置不同
静态变量存储于方法区的静态区
成员变量存储于堆内存
内存出现时间不同
静态变量随着类的加载而加载,随着类的消失而消失
成员变量随着对象的创建而存在,随着对象的消失而消失
调用不同
静态变量可以通过类名调用,也可以通过对象调用
成员变量只能通过对象名调用

4、main方法
package com.shujia.day06;

/*
    public: 最大的权限
    class: 表示一个类,是java中的基本单位

 */
public class MainDemo {
    /*
        public: 最大的权限,因为main方法将来要被JVM所识别调用,权限要最大
        static: 静态的,将来可以直接类调用
        void: 无返回值的意思,返回给JVM毫无意义
        main: 是JVM所识别入口的名字
        String[]: 字符串一维数组类型
        args: 形参的名字
     */
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}
5、代码块
5.1、概述
代码块:使用大括号将代码括起来的整体,叫做代码块
5.2、分类
局部代码块:将{}括起来的整体,在方法的内部定义,叫做局部代码块,在同一个方法中多个局部代码块的执行顺序是自上而下执行
构造代码块:将{}括起来的整体,在类中方法外定义,叫做构造代码块  构造代码块 --> 构造方法
静态代码块:将{}括起来的整体,在大括号前面加上static关键字,在类中方法外定义,叫做静态代码块 --> 构造代码块 --> 构造方法
同步代码块:(放到多线程的时候讲解
5.3、代码块的作用
1、主要是限定变量的作用域
2、初始化值(放到下节课讲解)
public class CodeDemo1 {

    CodeDemo1(){
        System.out.println("这是构造方法");
    }

    //构造代码块,在每次创建对象的时候调用
    {
        System.out.println("这是构造代码块");
    }

    //静态代码块,在类加载的时候,就会执行一次,且只会在程序运行开始前运行一次,后续不会运行
    static {
        System.out.println("这是静态代码块");
    }



    public static void main(String[] args) {
//        //局部代码块
//        {
//            System.out.println("这是局部代码块1");
//        }
//
//        {
//            System.out.println("这是局部代码块2");
//        }
//
        CodeDemo1 codeDemo1 = new CodeDemo1();
        CodeDemo1 codeDemo2 = new CodeDemo1();

    }
}
6、继承
6.1、概述
java针对多个类中有相同的部分的话,可以将相同的部分提取出来,单独放到一个类中A, 然后其他类与这个A类产生一个关系,这样的话
    其他类中就拥有了A类中的属性或者行为。这个关系叫做继承, java提供了一个关键字 extends来表示继承关系
例如
学生类Student继承自人类Person
Student类在继承关系中称之为子类/派生类,Person类在继承关系中称之为父类/基类/超类
6.2、继承的好处
提高了代码的复用性
    多个类相同的成员可以放到同一个类中
提高了代码的维护性
    如果功能的代码需要修改,修改一处即可
让类与类之间产生了关系,是多态的前提
    其实这也是继承的一个弊端:类的耦合性很强
6.3、继承的注意事项
1、子类无法继承父类中的构造方法
2、java中的类与类之间的继承,不允许出现子类同时继承多个父类的情况,只允许单继承
3、java中的类与类之间的继承,运行多重继承,形成继承体系。
4、java中子类无法继承父类中的私有成员
5、不要为了部分功能而去继承
 什么时候使用继承呢?
            只要类之间满足is a的语法,类与类之间就可以使用继承改进
            学生是人  教师是人   苹果是水果  香蕉是水果
6.4、继承与成员变量的关系
就近原则

用法(thissuper均可如下使用)
    访问成员变量
        this.成员变量      super.成员变量
    访问构造方法(子父类的构造方法问题讲)
        this()    super()
    访问成员方法(子父类的成员方法问题讲)
        this.成员方法() super.成员方法()
class Fu2{
    int a = 10;

    public void show1(){
        int a = 40;
    }
}

class Zi2 extends Fu2{
    int a = 20;
    public void fun1(){ // 无法访问到父类中方法中的变量
        int a = 30;
        System.out.println(a); // 获取的是当前方法中的局部变量a:30
        //需求1:在不创建对象的前提下获取子类中成员变量a:20
//        Zi2 zi2 = new Zi2();
//        System.out.println(zi2.a);
        System.out.println(this.a);
        //需求2:在不创建父类对象的前提下获取父类中成员变量a:10
//        Fu2 fu2 = new Fu2();
//        System.out.println(fu2.a);
        //java提供了一个关键字表示父类的引用:super
        //将来在子类中可以使用super关键字调用父类中的所有非私有的成员
        System.out.println(super.a);

//        show1();
    }
}

public class ExtendsDemo3 {
    public static void main(String[] args) {
        Zi2 zi2 = new Zi2();
        zi2.fun1();
    }
}

6.5、继承与成员方法的关系
继承与成员方法的关系:
     1、子类访问成员方法也遵循就近原则
     2、方法的重写(子类中出现了和父类中一模一样的方法声明【返回值类型,方法名,参数列表都和父类一模一样】)

面试题:重载和重写的区别? OverrideOverload的区别?
     1、重载是发生在同一个类中的,方法名相同,参数列表不同,与返回值无关的现象
     2、重写是发生在继承关系中的,返回值类型,方法名,参数列表都和父类一模一样,只是实现不一样,使用 @Override检测方法能否重写
class Fu4{
    public void fun1(){
        System.out.println("游泳");
    }
}

class Zi4 extends Fu4{
    @Override
    public void fun1(){
        System.out.println("游泳");
        System.out.println("水下踢球");
    }

    public void show1(){
        System.out.println("这是子类中的show1方法");
    }


}

public class ExtendsDemo5 {
    public static void main(String[] args) {
        Zi4 zi4 = new Zi4();
        zi4.fun1();
//        zi4.show1();
    }
}
6.6、方法重写的注意事项
java中有4个权限修饰符,权限从大到小依次为:public	protected  默认	private
1、子类无法重写父类中的私有成员方法
2、子类重写父类方法的时候,权限修饰符不能比父类中的权限还要小,只能大于等于父类方法的权限
    推荐父类用什么修饰符,子类就用什么修饰符
3、子类无法重写父类中的静态方法,只能使用。
4、总的来说,子类可以重写父类中的非私有,非静态的成员方法。
class Fu5{
    public void fun1(){
        System.out.println("这是父类中的非私有的fun1方法");
    }

    private void fun2(){
        System.out.println("这是父类中的私有方法fun2");
    }

    void fun3(){
        System.out.println("这是父类中的非私有的fun3方法,且被默认权限修饰符修饰");
    }

    public static void fun4(){
        System.out.println("这是父类中的静态方法fun4");
    }


}

class Zi5 extends Fu5{
    @Override
    public void fun1(){
        System.out.println("这是子类重写了父类中的非私有的fun1方法");
    }

    @Override
    void fun3(){
        System.out.println("这是子类重写后的fun3方法");
    }

//    @Override
//    private void fun2(){
//        System.out.println("hello world");
//    }

//    @Override
    public static void fun4(){
        System.out.println("这是子类中的静态方法fun4");
    }



}

public class ExtendsDemo6 {
    public static void main(String[] args) {
        Zi5 zi5 = new Zi5();
        zi5.fun1();
        zi5.fun3();
//        zi5.fun4();
    }
}

6.7、如果父类中的方法只想让子类继承使用,并不想让子类重写,java提供了一个关键字给我们使用做限制:final
class OldPhone{
    public void call(){
        System.out.println("打电话");
    }

    public final void fangKuai(){
        System.out.println("俄罗斯方块游戏");
    }
}

class NewPhone extends OldPhone{
    @Override
    public void call(){
        System.out.println("打电话");
        System.out.println("打王者");
    }

//    @Override
//    public void fangKuai(){
//        System.out.println("重写后的俄罗斯方块游戏");
//    }

}

public class ExtendsDemo7 {
    public static void main(String[] args) {
        NewPhone newPhone = new NewPhone();
        newPhone.call();
        newPhone.fangKuai();
    }
}

6.8、继承-猫狗案例
class Animal {
    String name;
    int age;

    public Animal() {
    }

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

    public void eat() {
        System.out.println("吃");
    }

    public void sleep() {
        System.out.println("睡");
    }

    public void show() {
        System.out.println("姓名:" + name);
        System.out.println("年龄:" + age);
    }
}

class Cat extends Animal {
    public Cat() {
    }

    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("🐱吃🐟");
    }

    @Override
    public void sleep() {
        System.out.println("🐱蜷着睡");
    }
}

class Dog extends Animal {
    public Dog() {
    }

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("🐕吃🥩");
    }

    @Override
    public void sleep() {
        System.out.println("🐕趴着睡");
    }
}

public class AnimalDemo1 {
    public static void main(String[] args) {
        //创建一只小猫
        Cat c1 = new Cat("小花", 3);
        c1.eat();
        c1.sleep();
        c1.show();

        System.out.println("========================");
        Dog d1 = new Dog("大黄", 2);
        d1.eat();
        d1.sleep();
        d1.show();
    }
}
7、final关键字
final关键字的使用特点:
    类:被final修饰的类,类不能被继承
    变量:被final修饰的变量,变量变常量(自定义常量)
    方法:被final所修饰的方法,子类只能继承使用,不能进行重写
class Fu6{
    int a = 10;
    final int b = 20;
}

class Zi6 extends Fu6{
    public void fun1(){
        a = 20;
//        b = 30;
        System.out.println(a);
        System.out.println(b);
    }
}


public class FinalDemo {
    public static void main(String[] args) {
        Zi6 zi6 = new Zi6();
        zi6.fun1();

    }
}
final修饰变量,变量变自定义常量使用的注意事项
    1、注意辨别被final修饰的变量,具体是什么值不能改
    2、被final修饰的变量,必须要手动赋值之后才能使用,并且只能赋值一次
    3、必须在构造方法结束之前手动赋值(方案1:使用构造代码块进行赋值  方案2:在构造方法内部进行赋值)
        建议:将来开发的时候,如果你定义了一个final的变量,就在定义的时候就给值
class Demo1{
    int a = 10;

    final int b = 300;

//    Demo1(){
//        b=200;
//    }

//    {
//        b=200;
//    }

}

public class FinalDemo2 {
    public static void main(String[] args) {
        final Demo1 demo1 = new Demo1();

        System.out.println(demo1.a);
        demo1.a = 100;
        System.out.println(demo1.a);
//        demo1 = new Demo1();

//        demo1.b = 200;
        System.out.println(demo1.b);



    }
}
8、多态
8.1、概述
多种状态,某一个事物在不同时刻下的不同状态。
8.2、实现多态的前提
实现多态的三大前提:(同时满足,缺一不可)
    1、要有继承关系
    2、要有方法的重写
    3、要有父类的引用指向子类对象(只有继承关系/实现关系才可以这么写)
class Water{
    public void drink(){
        System.out.println("水可以喝");
    }
}

class SSDWater extends Water{
    @Override
    public void drink(){
        System.out.println("固态的水可以吃,可以等化了再喝");
    }
}
public class DuoTaiDemo1 {
    public static void main(String[] args) {
        //类与类之间的多态
        Water w = new SSDWater();  // 正确读法:从右向左读 固态的水是水
    }
}

8.3、多态访问成员的特点
成员变量: 编译看左,运行看左
成员方法: 编译看左,运行看右
静态的成员方法: 编译看左,运行看左
class Fu7{
    int a = 10;

//    public void fun1(){
//        System.out.println("hello world");
//    }

    public static void fun2(){
        System.out.println("shujia1");
    }
}

class Zi7 extends Fu7{
    int a = 20;


    public void fun1(){
        System.out.println("hello java");
    }

    public static void fun2(){
        System.out.println("shujia2");
    }
}

public class DuoTaiDemo2 {
    public static void main(String[] args) {
        Fu7 f = new Zi7();
    }
}
8.4、多态的好处
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
class Animal{
    String name;
    int age;

    public Animal() {
    }

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

    public void eat(){
        System.out.println("吃");
    }

    public void sleep(){
        System.out.println("睡");
    }
}

class Tiger extends Animal{
    public Tiger() {
    }

    public Tiger(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("🐅吃🥩");
    }

    @Override
    public void sleep() {
        System.out.println("🐅趴着睡");
    }
}

class Bear extends Animal{
    public Bear() {
    }

    public Bear(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("🐻吃🐅");
    }

    @Override
    public void sleep() {
        System.out.println("🐻侧着睡");
    }
}

class Snake extends Animal{
    public Snake() {
    }

    public Snake(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("🐍吃🐀");
    }

    @Override
    public void sleep() {
        System.out.println("🐍蜷着睡");
    }
}

class Monkey extends Animal{
    public Monkey() {
    }

    public Monkey(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("🐒吃🍌");
    }

    @Override
    public void sleep() {
        System.out.println("🐒倒挂睡");
    }
}

class AnimalTool{
    /*
        1、构造方法私有化
        2、成员方法必须是静态的
     */
    public static void useAnimal(Animal animal){ //Animal animal = new Monkey("孙悟空", 10)  类的多态
        //多态访问成员方法的特点 编译看左,运行看右
        animal.eat();
        animal.sleep();
    }

//    public static void useTiger(Tiger tiger){
//        tiger.eat();
//        tiger.sleep();
//    }
//
//    public static void useBear(Bear bear){
//        bear.eat();
//        bear.sleep();
//    }
//
//    public static void useSnake(Snake snake){ // Snake snake = new Snake("小青", 18);
//        snake.eat();
//        snake.sleep();
//    }
}



public class DuoTaiDemo1 {
    public static void main(String[] args) {
        //需求1:我想养一只🐅
        Tiger t1 = new Tiger("跳跳虎1", 3);
//        t1.eat();
//        t1.sleep();
//        useTiger(t1);
//        AnimalTool.useTiger(t1);
        AnimalTool.useAnimal(t1); // new Tiger("跳跳虎1", 3)
        //需求2:我还想养一只🐅
        Tiger t2 = new Tiger("跳跳虎2", 2);
//        t2.eat();
//        t2.sleep();
//        useTiger(t2);
//        AnimalTool.useTiger(t2);
        AnimalTool.useAnimal(t2);
        //如果我创建了n只🐅,eat()方法和sleep()方法都要写一遍
        //需求3:我想养一只🐻
        Bear b1 = new Bear("熊大", 1);
//        b1.eat();
//        b1.sleep();
//        useBear(b1);
//        AnimalTool.useBear(b1);
        AnimalTool.useAnimal(b1);
        Bear b2 = new Bear("熊二", 2);
//        b2.eat();
//        b2.sleep();
//        useBear(b2);
//        AnimalTool.useBear(b2);
        AnimalTool.useAnimal(b2);
        //将来我们要养的动物种类越来越多,意味着class动物类越写越多,这是不可避免
        //但是我们本类中动物的useXxx()方法,这个类就会显得非常臃肿
        //可以将方法使用类再进行封装 专门写一个动物相关的工具类
        //需求4:我现在想养一只🐍
        //1、编写一个Snake类
        //2、修改动物的工具类,加一个使用🐍的方法
        Snake s1 = new Snake("小青", 18);
//        AnimalTool.useSnake(s1); // new Snake("小青", 18);
        //上面的编写需求过程中,虽然也实现了结果,但是我们忽略了实际开发中的一个问题:工具类不会随意的修改
        //但是,我们如果不修改的话,按照原本的方法定义就不能够调用蛇的功能了
        //解决方案:只需要在工具类中写一个通用的方法。利用多态
        AnimalTool.useAnimal(s1); // new Snake("小青", 18)

        //养一只🐒
        Monkey m = new Monkey("孙悟空", 10);
        AnimalTool.useAnimal(m); // new Monkey("孙悟空", 10)


    }

    //将不同的动物的eat方法和sleep方法进行封装
//    public static void useTiger(Tiger tiger){
//        tiger.eat();
//        tiger.sleep();
//    }
//
//    public static void useBear(Bear bear){
//        bear.eat();
//        bear.sleep();
//    }

    //.....
}
8.5、多态的弊端
多态访问成员方法的弊端:无法访问子类中特有的成员方法
向下转型:由多态变成子类引用指向子类本身的对象
    多态向下转型的注意事项:
        ClassCastException: 类型转换异常
        能够转型的两个类之间必须是存在继承关系或者实现关系。
class Fu{
    public void fun1(){
        System.out.println("这是父类中的方法fun1");
    }
}

class Zi extends Fu{
    @Override
    public void fun1(){
        System.out.println("这是重写后的fun1方法");
    }

    public void show1(){
        System.out.println("hello world");
    }

}

public class DuoTaiDemo2 {
    public static void main(String[] args) {
        //父类的引用指向子类对象
        Fu f = new Zi();
        f.fun1();

        Zi z = (Zi)f;
//        f.show1();
        z.show1();
        z.fun1();
    }
}
8.6、多态-老师案例
package com.shujia.day08.ketang;

/*
    老师案例
    具体事物:29期老师,28期老师
    共性:姓名,年龄,讲课。

 */
abstract class Teacher{
    private String name;
    private int age;

    public Teacher() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    //将讲课的方法设置为抽象方法
    public abstract void teach();

}

//创建28期老师类
class Teacher28 extends Teacher{
    public Teacher28() {
    }

    public Teacher28(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("hive教学");
    }

    public void chaoGu(){
        System.out.println("炒股");
    }
}

class Teacher29 extends Teacher{
    public Teacher29() {
    }

    public Teacher29(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("java教学");
    }

    public void playGame(){
        System.out.println("打游戏");
    }
}


public class DuoTaiTest {
    public static void main(String[] args) {
        //创建28期的老师
        Teacher t1 = new Teacher28("童哥",18);  // 抽象多态
        t1.teach();
//        t1.chaoGu();
        //向下转型
        Teacher28 t28 = (Teacher28) t1;
        t28.chaoGu();
        System.out.println("================================");
        Teacher t2 = new Teacher29("xiaohu",19);
        t2.teach();
//        t2.playGame();
        //向下转型
        Teacher29 t29 = (Teacher29) t2;
        t29.playGame();


    }
}
9、抽象类
java为了更好的模拟现实生活,为了能够表示抽象的集合概念这样的思想,提供了一个关键字给我们使用:abstract 抽象的
可以修饰类,成员方法。
修饰类,类为抽象类,类无法new,无法被实例化(创建对象)
修饰方法,方法为抽象方法
abstract class Animal1{
    String name;
    int age;

    public Animal1() {
    }

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

    public void eat(){
        System.out.println("吃");
    }
    public void sleep(){
        System.out.println("睡");
    }
}

//class Animal1{
//    String name;
//    int age;
//
//    public Animal1() {
//    }
//
//    public Animal1(String name, int age) {
//        this.name = name;
//        this.age = age;
//    }
//
//    public void eat(){
//        System.out.println("吃");
//    }
//    public void sleep(){
//        System.out.println("睡");
//    }
//}

class Cat1 extends Animal1{
    public Cat1() {
    }

    public Cat1(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public void eat(){
        System.out.println("🐱吃🐟");
    }

    @Override
    public void sleep(){
        System.out.println("🐱蜷着睡");
    }
}

public class AbstractDemo1 {
    public static void main(String[] args) {
//        Animal1 animal1 = new Animal1();
    }
}
9.1、注意事项
注意事项:
    1、抽象类无法被实例化,却可以在抽象类中编写构造方法,这里的构造方法意义是什么呢?
        为了子类能够成功创建对象,要想初始化子类,必选初始化其直接父类。
    2、被abstract关键字修饰的方法,不允许有方法体的实现,连大括号都不能有。
    3、抽象方法的类必须是抽象class类,不能是具体的class类(如果一个类有抽象方法,这个类必须是抽象类)
    4、抽象class类中既可以存在抽象方法,也可以存在具体的实现方法
    5、当一个具体的class类继承一个抽象类的时候,必须要实现抽象类中的所有抽象方法 !!
    6、抽象类与抽象之间允许存在继承关系, 抽象类继承抽象类的时候,可以选择性地重写方法,也可以都不重写。
abstract class Animal2{
    String name;
    int age;

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

    //抽象方法
    public abstract void eat();

    public abstract void sleep();

    public void fun1(){
        System.out.println("hello world");
    }
}


abstract class Animal2Demo extends Animal2{
//    @Override
//    public void eat(){
//        System.out.println("xxx");
//    }
}

class Cat2 extends Animal2{
    public Cat2() {
    }

    public Cat2(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public void eat(){
        System.out.println("🐱吃🐟");
    }

    @Override
    public void sleep(){
        System.out.println("🐱蜷着睡");
    }
}

public class AbstractDemo2 {
    public static void main(String[] args) {
        Cat2 cat2 = new Cat2();
    }
}
9.2、不能共存的关键字
1、一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义? 表示现实生活中的一种概念集合
2abstract不能和哪些关键字共存
    static 冲突
    final 冲突
    private 冲突
abstract class Demo{
//    public abstract static void fun1(); // 非法的修饰符组合: abstract和static

//    public abstract final void fun2(); // 非法的修饰符组合: abstract和final

//    private abstract void fun3(); // 非法的修饰符组合: abstract和private
}

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

    }
10、接口
10.1、概述
java提供了一个关键字 interface表示一个接口,主要是为了扩展一个类的额外功能
10.2、接口的注意事项(一)
1、接口可以被看作一个特殊的类,JVM也会把接口编译成一个class文件
2、接口中只允许出现抽象方法,默认是抽象方法,默认会在方法的前面加上public abstract修饰符
3、当一个具体的类实现一个接口的时候,必须要重写其中所有抽象方法
abstract class Animal3 {
    public abstract void eat();

    public abstract void sleep();
}

class Dog3 extends Animal3 {
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }

    @Override
    public void sleep() {
        System.out.println("狗趴着睡");
    }
}

class Cat3 extends Animal3 {
    @Override
    public void eat() {
        System.out.println("猫吃🐟");
    }

    @Override
    public void sleep() {
        System.out.println("猫蜷着睡");
    }
}

//定义一个计算接口
interface Compute {
//    public void compute(){ // 接口抽象方法不能带有主体
//        System.out.println("狗会计算");
//    }

    public abstract void compute();
}

//需求:经过训练后的狗会计算
class DogCompute extends Dog3 implements Compute{
    @Override
    public void compute() {
        System.out.println("狗会计算");
    }
}

//需求:经过训练后的猫会计算
class CatCompute extends Cat3 {
    public void compute() {
        System.out.println("猫会计算");
    }
}


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

    }
}
10.3、接口的注意事项(二)
1、抽象类也可以实现接口,但是不是必须重写方法,可以选择性地重写
2、接口中只允许常量出现,不允许出现变量,默认会在定义变量前面加上public static final
3、一个类可以同时实现多个接口,接口之间使用英文逗号分割
4、java允许一个类继承一个类的同时实现接口,可以实现多个接口
    class A extends B implements Inter1,[Inter2,...]{

    }
5、接口与接口之间是继承关系,接口之间继承允许多继承(面试题:java语言中允许多继承吗?从类与类之间继承和接口与接口之间继承回答)
interface Inter {
    public static final int a = 10;


    public abstract void fun1();

    public abstract void fun2();
}

interface Inter2 {

}

interface Inter3 extends Inter, Inter2 {
    void show1();
}

class Demo2 implements Inter, Inter2 {
    @Override
    public void fun1() {

    }

    @Override
    public void fun2() {

    }
}

//编写实现了Inter接口的实现类
//规范接口实现类的写法:接口名Impl
class InterImpl implements Inter {
    @Override
    public void fun1() {
//        a=100; // 无法为最终变量a分配值
        System.out.println(Inter.a);
        System.out.println(a);
        System.out.println("这是重写接口后的fun1方法");
    }

    @Override
    public void fun2() {
        System.out.println("这是重写接口后的fun2方法");
    }
}

abstract class InterImpl2 implements Inter {
    @Override
    public void fun1() {
        System.out.println("这是重写接口后的fun1方法");
    }
}


public class InterfaceDemo2 {
    public static void main(String[] args) {
//        InterImpl inter = new InterImpl();
//        inter.fun1();
        System.out.println(Inter.a);
    }
}
11、形式参数和返回值类型
11.1、概述
形式参数
        基本类型: byte,short,int,long,float,double,boolean,char
        引用类型:
            类:当一个类作为方法参数类型传递的时候,将来调用时应该传入该的类对象
            抽象类:当你看到一个抽象类作为方法形式参数类型的时候,将来调用时,应该传入该抽象类的具体子类的对象
            接口:当你看到一个接口作为方法形式参数类型的时候,将来调用该方法时,应该传入实现该接口的具体子类对象
                
返回值类型
        基本类型: 跳过
        引用类型:
            类:当你看到一个类作为方法的返回值类型的时候,将来方法内部定义时应该返回该类的对象
            抽象类:当你看到一个抽象类作为方法的返回值类型的时候,将来方法内部定义时应该返回该抽象类具体子类对象
            接口:当你看到一个接口作为方法的返回值类型的时候,将来方法内部定义时应该返回实现该接口的具体子类对象
11.2、形式参数案例

class Student{
    public void fun1(){
        System.out.println("好好学习,天天向上");
    }
}

class StudentTest{
    //当一个类作为方法参数类型传递的时候,将来调用时应该传入该的类对象
    public void show(Student student){ //Student student = new Student()
        student.fun1();
    }
}

public class StudentDemo1 {
    public static void main(String[] args) {
        StudentTest studentTest = new StudentTest();
        Student student = new Student();
        studentTest.show(student); // new Student()
    }
}

抽象类

abstract class Student2{
    public void fun1(){
        System.out.println("好好学习,天天向上");
    }
}

class Student2Zi extends Student2{
    //。。
}

class StudentTest2{
    public void show(Student2 student2){  //Student2 student2 = new Student2Zi() // 抽象多态
        student2.fun1();
    }
}

public class StudentDemo2 {
    public static void main(String[] args) {
        StudentTest2 studentTest2 = new StudentTest2();
//        Student2 student2 = new Student2(); //抽象类无法被实例化
        studentTest2.show(new Student2Zi());
    }
}

接口

interface Inter{
    void fun1();
}


class InterImpl implements Inter{
    @Override
    public void fun1() {
        System.out.println("好好学习,天天向上");
    }
}

class Student3{
    public void show(Inter inter){ // Inter inter = new InterImpl(); // 接口多态
        inter.fun1();
    }
}

public class StudentDemo3 {
    public static void main(String[] args) {
        Student3 student3 = new Student3();

//        Inter inter = new Inter(); // 接口无法被实例化
        student3.show(new InterImpl());
    }
}
11.3、返回值类型的案例

class Teacher{
    public void fun1(){
        System.out.println("教书育人");
    }
}

class TeacherTest{
    public Teacher show(){
        return new Teacher();
    }
}


public class TeacherDemo1 {
    public static void main(String[] args) {
        TeacherTest tt = new TeacherTest();
        Teacher t = tt.show(); // Teacher t = new Teacher();
        t.fun1();
    }
}

抽象类

abstract class Teacher2{
    public void fun1(){
        System.out.println("教书育人");
    }
}

class Teacher2Zi extends Teacher2{

}

class TeacherTest2{
    public Teacher2 show(){
        return new Teacher2Zi();
    }
}



public class TeacherDemo2 {
    public static void main(String[] args) {
        TeacherTest2 teacherTest2 = new TeacherTest2();
        Teacher2 t = teacherTest2.show(); //Teacher2 t = new Teacher2Zi() 抽象多态
        t.fun1();
    }
}

接口

interface Inter3{
    void fun1();
}

class Inter3Impl implements Inter3{
    @Override
    public void fun1() {
        System.out.println("教书育人");
    }
}

class Teacher3{
    public Inter3 show(){
        return new Inter3Impl();
    }
}

public class TeacherDemo3 {
    public static void main(String[] args) {
//        Teacher3 teacher3 = new Teacher3();
//        Inter3 inter3 = teacher3.show(); //Inter3 inter3 = new Inter3Impl() 接口多态
//        inter3.fun1();

        Teacher3 teacher3 = new Teacher3();
        teacher3.show().fun1(); //链式编程(链式调用)

//        xxx.xxx()
//                .xxx()
//                .xxxx()
//                .xxx()

    }
}
12、包
12.1、概述
其实就是文件夹
作用:对类进行分类管理
12.2、包的划分
--文件夹--目录
    1、方便管理查找对应的文件
    2、让工程与工程之间的关系更加清晰,将来修改的时候方便一些

一般情况下,包会按照两种类型进行划分:
1、按照角色划分
2、按照功能划分

基于ssm的xxxx管理系统
老师(增删改查)
学生(增删改查)

一般情况下,包的划分是不会频繁更新的
1、按照角色划分
    老师()
        增加.java
        删除.java
        修改.java
        查询.java
    学生()
        增加.java
        删除.java
        修改.java
        查询.java

2、按照功能划分
    增()
        老师增加.java
        学生增加.java
    删()
        老师删除.java
        学生删除.java
    改()
        老师修改.java
        学生修改.java
    查()
        老师查询.java
        学生查询.java

SpringBoot
    --Controller     主要是前端与后端的交互层
    --entity(pojo)   实体类层
    --dao            数据库操作接口层
    --service        实现类接口层
        --serviceImpl
    --utils          工具类层
12.3、包的定义格式和注意事项
定义格式
package 包名;
多级包用.分开即可
注意事项:
package语句必须是程序的第一条可执行的代码
package语句在一个java文件中只能有一个
如果没有package,默认表示无包名

12.4、导包
package com.shujia.day09.bao2;
//import com.shujia.day09.bao1.Demo; // 导入其他包中的类
//import com.shujia.day09.bao1.Demo2;
//..
import com.shujia.day09.bao1.*; // 不要随便使用*,当使用一个包下超过15个类之后,再用*

public class Test {
    public static void main(String[] args) {
        Demo d = new Demo();
        d.fun(10,20);

        Demo2 d2 = new Demo2();
    }
}
13、权限修饰符
13.1、

image-20240311104815459

13.2、类中常见的修饰符
/*
    权限修饰符:public,protected,默认的,private
    常量修饰符:final
    静态修饰符:static
    抽象修饰符:abstract


    类:
        权限修饰符:public,默认的
        常量修饰符:final
        内部类可以使用静态修饰符:static,外部类不行
        抽象修饰符:abstract
    成员变量:
        权限修饰符:public,protected,默认的,private
        常量修饰符:final
        静态修饰符:static
    构造方法:
        权限修饰符:public,protected,默认的,private
    成员方法:
        权限修饰符:public,protected,默认的,private
        常量修饰符:final
        静态修饰符:static
        抽象修饰符:abstract

    常见的修饰符组合:
        public static 工具类中常见
        public static final 在接口中常见
        public abstract  在接口中常见

    无特殊要求,使用public权限居多


 */

14、内部类
14.1、概述
将一个类定义在一个类的内部中
14.2、分类
 成员内部类:在类中方法外定义的类,叫做成员内部类
 局部内部类:在方法内部定义的类,叫做局部内部类
14.3、成员内部类案例
  • 非静态
成员内部类(非静态):
        创建成员内部类对象的语法:外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
    注意:
        1、成员内部类(非静态)既可以访问外部类的中的成员变量,也可以是私有的成员变量
        2、内部类都不会单独编译出一个class文件
 */

class Outer1{
    //成员变量
    private int a = 10;

    //成员内部类
    class Inner1{

        /*
            Inner1(){}
         */
        public void fun1(){
            System.out.println(a);
        }
    }
}

public class InnerClassDemo1 {
    public static void main(String[] args) {
        //创建一个成员内部类的对象(非静态的成员内部类)
        Outer1.Inner1 oi1 = new Outer1().new Inner1();
        oi1.fun1();
    }
}
  • 静态
package com.shujia.day09;

/*
    成员内部类使用static进行修饰

    创建静态的成员内部类的对象语法:
        外部类名.内部类名 对象名 = new 外部类名.内部类名();

 */

class Outer3{
    static class Inner3{
        public void fun1(){
            System.out.println("好好学习,天天向上!");
        }
    }

}

public class InnerClassDemo3 {
    public static void main(String[] args) {
        Outer3.Inner3 oi = new Outer3.Inner3();
        oi.fun1();

    }
}
  • 私有的
/*
    成员内部类可以使用private关键字和static关键字进行修饰
 */
class Outer2{
    //其他类中无法创建内部类的对象了
    private class Inner2{
        public void fun1(){
            System.out.println("好好学习,天天向上!");
        }
    }

    public void show(){
        //创建成员内部类的对象
        Inner2 inner2 = new Inner2();
        inner2.fun1();
    }
}

public class InnerClassDemo2 {
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.show();
    }
}
14.4、局部内部类案例
/*
    局部内部类:将类定义在类中方法的内部

 */

class Outer4{
//    int a = 10;

    public void fun1(){
        //局部变量
        int a = 10;

        //在这里定义的类,叫做局部内部类
        class Inner4{
//            int a = 10;

            public void show(){
//                a=20; // 从内部类引用的本地变量必须是最终变量或实际上的最终变量
                System.out.println(a);
            }
        }

        //创建局部内部类的对象
        Inner4 inner4 = new Inner4();
        inner4.show();

    }
}

public class InnerClassDemo4 {
    public static void main(String[] args) {
        Outer4 outer4 = new Outer4();
        outer4.fun1();
    }
}
14.5、内部类的面试题
补齐程序(注意:内部类和外部类没有继承关系)
	class Outer {
		public int num = 10;
		class Inner {
			public int num = 20;
			public void show() {
				int num = 30;
				System.out.println(?);
				System.out.println(??);
				System.out.println(???);
			}
		}
	}
在控制分别输出:30,20,10
class Outer {
    public int num = 10;
    class Inner {
        public int num = 20;
        public void show() {
            int num = 30;
            System.out.println(num); // 30
            System.out.println(this.num); // 20
//            System.out.println(new Outer().num); // 10
            System.out.println(Outer.this.num); //
        }
    }
}


public class InnerClassTest1 {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}
15、匿名内部类
15.1、概述
就是内部类的简化写法。
15.2、接口匿名内部类案例
/*
    匿名内部类的语法定义格式:
        Xxx xx = new 类名/抽象类名/接口(){
            重写方法;
        }
 */

interface Inter2 {
    void show();
}

//class Inter2Impl implements Inter2{
//    @Override
//    public void show() {
//        System.out.println("好好学习");
//    }
//}

//class Inter2Impl1 implements Inter2{
//    @Override
//    public void show() {
//        System.out.println("天天向上");
//    }
//}

class Demo {
    //当你看到一个接口作为方法的形式参数类型的时候,将来调用时传入的是实现该接口的具体子类对象
    public void fun(Inter2 inter2) { //Inter2 inter2 = new Inter2Impl()
        inter2.show();
    }
}

public class NiMingClassDemo1 {
    public static void main(String[] args) {
        Demo demo = new Demo();
//        //需求1:调用fun方法输出好好学习
//        demo.fun(new Inter2Impl());
//        //需求2:调用fun方法输出天天向上
//        demo.fun(new Inter2Impl1());
        //需求n不同的输出

        //表示由JVM帮我们完成了三件事
        //1. JVM内部帮我们造了一个类,这个实现了Inter2接口
        //2. 重写了方法,我们只是参与了方法的实现
        //3. 帮我们将这个类示例化出来
        demo.fun(new Inter2(){
            @Override
            public void show(){
                System.out.println("好好学习");
            }
        });

//        demo.fun(() -> System.out.println("好好学习")); // Lambda表达式
    }
}
15.3、抽象类匿名内部类案例
abstract class Demo2{
    public abstract void show();
}
//class Demo2Zi extends Demo2{
//    @Override
//    public void show() {
//        System.out.println("好好学习");
//    }
//}
//
//class Demo2Zi2 extends Demo2{
//    @Override
//    public void show() {
//        System.out.println("天天向上");
//    }
//}

class Demo3{
    //当你看到一个抽象类作为方法的形式参数类型的时候,将来调用时传入的是继承该抽象类的具体子类对象
    public void fun(Demo2 demo2){
        demo2.show();
    }
}

public class NiMingClassDemo2 {
    public static void main(String[] args) {
        Demo3 demo3 = new Demo3();
//        //需求1:调用fun方法输出好好学习
//        demo3.fun(new Demo2Zi());
//        //需求2:调用fun方法输出天天向上
//        demo3.fun(new Demo2Zi2());

        demo3.fun(new Demo2(){
            @Override
            public void show() {
                System.out.println("这是匿名内部类");
            }
        });


    }
}

第四章Java中常用的类

Object类
1、概述

类层次结构的根类
所有类都直接或者间接的继承自该类

2、构造方法

public Object()

3、成员方法
3.1、案例1
		public int hashCode() 返回对象的哈希码值。
        public final Class getClass() 获取一个类对应的Class对象
        public String toString() 输出一个对象,实际上输出的是对象调用的toString()方法
         一个标准类的最终写法:
        1、成员变量私有化
        2、构造方法(无参/所有参数)
        3、getXxx()和setXxx()
        4、toString()
class Student {
    //包含了继承自Object类的方法
    String name;
    int age;

    public Student() {
    }

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

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

public class ObjectDemo1 {
    public static void main(String[] args) {
        Student s1 = new Student();
//        System.out.println(s1.hashCode()); // 1163157884
//
//        Student s2 = new Student();
//        System.out.println(s2.hashCode()); // 1956725890

        Class c1 = s1.getClass();
        System.out.println(c1.hashCode()); // 1163157884


        Student s2 = new Student();
        Class c2 = s2.getClass();
        System.out.println(c2.hashCode()); // 1163157884
        System.out.println(c2); // class com.shujia.day10.Student

        System.out.println(s2.toString()); // com.shujia.day10.Student@74a14482
        //getClass().getName() + '@' + Integer.toHexString(hashCode())
        System.out.println(s2.getClass().getName()); // com.shujia.day10.Student+'@'+74a14482
        System.out.println(Integer.toHexString(1956725890)); // 将整数值转成16进制字符串 74a14482

        System.out.println(s1); // com.shujia.day10.Student@1540e19d
        Student s3 = new Student("小虎", 18);
        System.out.println(s3); // com.shujia.day10.Student@677327b6 Student{name='小虎', age=18}
        //我们正常开发中,输出一个对象,实际上我们想要输出对象的成员变量值的情况,而不是一个地址值
        //我们自己的类中重写继承自Object类中的toString()方法就可以了
        //自动生成即可


    }
}
3.2、案例2
   public boolean equals(Object obj)
		protected void finalize() 用于垃圾回收的
		protected Object clone()
package com.shujia.day10;

import java.util.Objects;
import java.util.Scanner;

/*
    public boolean equals(Object obj)
    protected void finalize() 用于垃圾回收的
    protected Object clone()

 */
class Student2 extends Object{
    String name;
    int age;

    public Student2() {
    }

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

    /*
        public boolean equals(Object obj) {
            return (this == obj);
        }
     */

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student2 student2 = (Student2) o;
        return age == student2.age && Objects.equals(name, student2.name);
    }
}

public class ObjectDemo2 {
    public static void main(String[] args) {
        //创建第一个学生对象
        Student2 s1 = new Student2("丁义杰", 17);

        //创建第二个学生对象
        Student2 s2 = new Student2("丁义杰", 17);

        //==比较引用数据类型的时候,比较的是地址值
        System.out.println(s1==s2); // false
        /*
         public boolean equals(Object obj) { //Object obj = new Student2("丁义杰", 17);
            //this -- s1
            //obj -- s2
            return (this == obj);
         }
         */
        //Object类中的equals方法默认比较的是对象的地址值
        System.out.println(s1.equals(s2)); // false // true
        //要想比较两个对象中的成员变量值是否一样
        //只需要自己对象所属类中重写equals方法,改写成比较成员变量值就可以了
        //自动生成即可

        //字符串的比较 String
        String str1 = "hello"; // 存放在内存中的方法区的常量池中
//        System.out.println(str1.hashCode());
        System.out.println(System.identityHashCode(str1)); // 1163157884
        String str2 = new String("hello"); // 存放在堆内存中,实际上指向的依旧是常量池中

        System.out.println(System.identityHashCode(str2)); // 1956725890
        System.out.println(str1==str2); // false
        System.out.println(str1.equals(str2)); // true


    }
}
package com.shujia.day10;

/*
    protected Object clone() 拷贝

    浅拷贝  √
    深拷贝

    首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException 。
    Cloneable中什么成员都没有,这样的接口,在java中称之为标记接口

 */

class Demo {
    int a = 20;
}

class Student3 implements Cloneable {
    String name;
    int age;
    Demo demo;

    public Student3() {
    }

    public Student3(String name, int age, Demo demo) {
        this.name = name;
        this.age = age;
        this.demo = demo;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
//    @Override
//    protected Object clone() throws CloneNotSupportedException {
//        return super.clone();
//    }
}

public class ObjectDemo3 {
    public static void main(String[] args) throws Exception {
        Demo demo = new Demo();

        Student3 s1 = new Student3("小花", 18, demo);
//        System.out.println(s1); // Student3{name='小花', age=18}
        System.out.println(s1.demo); // com.shujia.day10.Demo@4554617c

        Object o = s1.clone();
//        System.out.println(s1 == o); // false
//        System.out.println(o); // Student3{name='小花', age=18}
        Student3 s = (Student3) o;
        System.out.println(s.demo); // com.shujia.day10.Demo@4554617c


    }
}
Scanner类
1、概述

JDK5以后用于获取用户的键盘输入

2、构造方法
Scanner(InputStream source)  构造一个新的 Scanner ,产生从指定输入流扫描的值。
InputStream 字节流
3、成员方法
next() 获取字符串
public String nextLine() 获取字符串 可以接收换行符
nextInt() 获取整数

public boolean hasNextInt() 判断管道中的下一个数据是否是整数
public class ScannerDemo1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个整数:");
        int number = 0;
        Scanner sc1 = null;
        if(sc.hasNextInt()){
            number = sc.nextInt();
        }else {
            sc1 = new Scanner(System.in);
            System.out.println("请输入一个整数:");
            number = sc1.nextInt();
        }

        System.out.println("请输入一个字符串:");
//        String s1 = sc.next();
        String s1 = sc1.next();
        System.out.println("--------------------------");
        System.out.println(s1);
        System.out.println(number);

    }
}
String类
1、概述
String: 字符串 由若干个字符组成的字符序列,整体叫做字符串
特点:
    1、一旦被创建,就不能被改变
    2Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例。
    3、因为String对象是不可变的,它们可以被共享(常量池)
    4、字符串可以被看作一个字符数组
2、构造方法
String类的构造方法:
    public String()
    public String(byte[] bytes)
    public String(byte[] bytes,int offset,int length)
    public String(char[] value)
    public String(char[] value,int offset,int count)
    public String(String original)
2.1、案例
public class StringDemo2 {
    public static void main(String[] args) {
        String s7 = "hello";

        //public String()
        String s1 = new String(); // 相当于创建一个空的字符串
        System.out.println("s1: " + s1); // String类重写了toString()方法

        //public String(byte[] bytes) 将字节数组转化成字符串
        byte[] arr = {97, 98, 99, 100, 101, 102};
        String s2 = new String(arr);
        System.out.println("s2: " + s2); // "abcdef"

        //public String(byte[] bytes,int index,int length) 将字节数组的一部分转成字符串,其中的index表示索引,length表示转成字符串的个数
        String s3 = new String(arr, 2, 3);
        System.out.println("s3: " + s3);

        //public String(char[] value)  将字符数组转成字符串
        char[] arr2 = {'我', '爱', '中', '国'};
        String s4 = new String(arr2);
        System.out.println("s4: " + s4);

        //public String(char[] value,int index,int count) 将字符数组的一部分转成字符串
        String s5 = new String(arr2, 1, 2);
        System.out.println("s5: " + s5);

        //public String(String original)
        String s6 = new String("java");
        System.out.println("s6: " + s6);

    }
}
String类的主要功能
1、判断功能
boolean equals(Object obj)
boolean equalsIgnoreCase(String str)//忽略大小写
boolean contains(String str)//判断是否包含某个字符串
boolean startsWith(String str)//以什么开头
boolean endsWith(String str) //以什么结尾
boolean isEmpty()//是否为空
public class StringDemo3 {
    public static void main(String[] args) {
        String s1 = "hello WoRLd";
        String s2 = "Hello World";

        //boolean equals(Object obj) 判断两个字符串内容是否相同
        System.out.println(s1.equals(s2)); // false

        //boolean equalsIgnoreCase(String str)  判断两个字符串内容是否相同,忽略大小写比较
        System.out.println(s1.equalsIgnoreCase(s2)); // true

        //boolean contains(String str) 判断一个字符串中是否包含另一个字符串
        System.out.println(s1.contains("o World")); // false

        //boolean startsWith(String str) 判断字符串是否以某个字符串开头
        System.out.println(s1.startsWith("hew")); // false

        //boolean endsWith(String str) 判断字符串是否以某个字符串结尾
        System.out.println(s1.endsWith("Rld")); // false

        //boolean isEmpty() 判断字符串是否为空字符串
        s1 = "";

        System.out.println(s1.isEmpty()); // false
    }
}
2、获取功能
int length()
char charAt(int index)
int indexOf(int ch)
int indexOf(String str)
int indexOf(int ch,int fromIndex)
int indexOf(String str,int fromIndex)
String substring(int start)
String substring(int start,int end)
public class StringDemo4 {
    public static void main(String[] args) {
        String s1 = "hello WoeRLd";

        //int length() 获取字符串中的字符个数
        System.out.println(s1.length());

        //char charAt(int index) 根据索引获取索引对应的字符
        System.out.println(s1.charAt(0)); // StringIndexOutOfBoundsException

        //int indexOf(int ch) 获取字符的位置
        //101--ASCII码:e
        //判断'e'第一次出现在字符串中的位置索引
        System.out.println(s1.indexOf(200)); // -1

        //int indexOf(String str) 获取小字符串在大字符串中第一次出现的位置,返回小字符串第一个字符的位置索引
        System.out.println(s1.indexOf("o Wo"));

        //int indexOf(int ch,int fromIndex) //从fromIndex位置开始找,如果找到返回该字符在大字符串中的位置索引
        //"hello WoeRLd"
        System.out.println(s1.indexOf(101)); // 1
        System.out.println(s1.indexOf(101,4)); // 8

        //int indexOf(String str,int fromIndex)

        //String substring(int start) 字符串截取 从start索引位置截取,一直到末尾
        //"hello WoeRLd"
        System.out.println(s1.substring(3));

        //String substring(int start,int end) 子串开始于指定beginIndex并延伸到字符索引endIndex-1
        //[start,end)
        System.out.println(s1.substring(4,8));


    }
}
3、转换共能
byte[] getBytes()
char[] toCharArray()
static String valueOf(char[] chs)
static String valueOf(int i)
String toLowerCase()
String toUpperCase()
String concat(String str)
public class StringDemo5 {
    public static void main(String[] args) {
        String s1 = "我爱中国我";

        //byte[] getBytes() 将字符串转换成字节数组
        byte[] bytes = s1.getBytes();
//        System.out.println(bytes); // [B@4554617c
//        for (int i = 0; i < bytes.length; i++) {
//            System.out.println(bytes[i]);
//        }

        // char[] toCharArray() 将字符串转换成字符数组
        char[] charArray = s1.toCharArray();
//        for (int i = 0; i < charArray.length; i++) {
//            System.out.println(charArray[i]);
//        }

        //static String valueOf(char[] chs) //将字符数组转成字符串
        String s2 = String.valueOf(charArray);
        System.out.println(s1 == s2); // false

        //static String valueOf(int i)  将数值转成字符串  100-->"100"
        String s3 = String.valueOf(100);
        System.out.println(s3);

        //String toLowerCase()  转小写
        String s4 = "Hello WORLD";
        String s5 = s4.toLowerCase();
        System.out.println(s5);

        //String toUpperCase() 转大写
        System.out.println(s5.toUpperCase());

        //String concat(String str) 字符串拼接
        System.out.println("s5: " + s5);
        String s6 = "s5: " + s5;
        String s7 = "s5: ".concat(s5);
        System.out.println(s6);
        System.out.println(s7);


    }
}
4、其他功能
替换功能
    String replace(char old,char new)
    String replace(String old,String new)
去除字符串两空格
    String trim()
按字典顺序比较两个字符串
    int compareTo(String str)
    int compareToIgnoreCase(String str)
public class StringDemo6 {
    public static void main(String[] args) {
        String s1 = "Hello WorLD";

        System.out.println(s1);
        //String replace(char old,char new)  使用新字符替换旧字符
        System.out.println(s1.replace('o', '_'));

        //String replace(String old,String new) 使用字符串替换旧字符串
        System.out.println(s1.replace("ll","数加科技"));

        //String trim() 去除字符串两边的空格
        String s2 = "  Hello WorLD  ";
        System.out.println(s2.trim());
        System.out.println(s2);


        //比较两个字符串内容是否相同,在java中有两种方法可以实现
        //boolean equals(String s)
        String s3 = "hello";
        String s4 = "world";
        System.out.println(s3.equals(s4));
        //int compareTo(String str)
        System.out.println(s3.compareTo(s4)); // -15
        String s5 = "hel";
        System.out.println(s3.compareTo(s5)); // 2
        String s6 = "hello";
        System.out.println(s3.compareTo(s6)); // 0
    }
}
面试题
字符串是常量,它的值在创建之后不能更改
String s = “hello”;
s += “world”; 问s的结果是多少?
面试题
String s = new String(“hello”)String s = “hello”;的区别?
字符串比较之看程序写结果
字符串拼接之看程序写结果
public class StringDemo7 {
    public static void main(String[] args) {
//        String s = "hello";
//        s += "world";
//        System.out.println("s: " + s);
//

        String s1 = new String("hello"); // s1存储的是堆内存中的地址值
        String s2 = "hello"; // 直接指向常量池中字符串对象地址值


    }
}
案例
1、案例1
统计大串中小串出现的次数
举例:在字符串” woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun”中java出现了5次

分析:
    1、可以使用indexOf()方法获取小串第一次出现的位置
    2、使用subString()截取每次java的后续剩余字符串,继续查找
    3、因为不知道次数,使用while循环
public class StringTest3 {
    public static void main(String[] args) {
        String bigStr = "woaijavawozhenaijavawozhendeaijavawozhendehenaijavaxinbuxinwoaijavagun";
        String str = "java";
        int count = 0;

        int i = bigStr.indexOf("java"); // 4 // 8
        while (i != -1) {
            count++;
            bigStr = bigStr.substring(i + str.length());
            i = bigStr.indexOf("java");
        }
        System.out.println("java字符串共出现了 " + count + "次");
    }
}
2、案例2
字符串反转
举例:键盘录入”abc”       输出结果:”cba”
public class StringTest2 {
    public static void main(String[] args) {
        String s = "我爱中国";
        //方案1:字符串拼接
        String res = "";
        for (int i = s.length() - 1; i >= 0 ;i--){
            res = res.concat(String.valueOf(s.charAt(i)));
        }
        System.out.println(res);

        //方案2:字符串转字符数组后,使用之前的方式逆序
        char[] charArray = s.toCharArray();
        //TODO:这里补充逆序的逻辑代码

        //字符数组转字符串
//        String.valueOf(charArray)
//        new String(charArray)

    }
}
3、案例3
把数组中的数据按照指定个格式拼接成一个字符串
举例:int[] arr = {1,2,3}; 输出结果:[1, 2, 3]
public class StringTest1 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};

        String str = "[";
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                str = str.concat(arr[i]+"]");
            }else {
                str = str.concat(arr[i]+",");
            }
        }

        System.out.println(str);
    }
}
StringBuffer类
1、概述
StringBuffer: 可变的字符序列,我们可以理解为是一个可变的字符串,线程安全
2、构造方法
public StringBuffer()
public StringBuffer(int capacity)
public StringBuffer(String str)
public class StringBufferDemo1 {
    public static void main(String[] args) {
        // public StringBuffer()  创建一个空的StringBuffer容器
        StringBuffer sb1 = new StringBuffer();
        // public int capacity() 返回当前容量。
        System.out.println(sb1.capacity()); // 16
        //public int length() 返回长度(字符数)。
        System.out.println(sb1.length()); // 0
        System.out.println("sb1: " + sb1); // StringBuffer重写了toString()

        //public StringBuffer(int capacity) 创建指定大小容量的容器
        StringBuffer sb2 = new StringBuffer(20);
        System.out.println(sb2.capacity());
        System.out.println(sb2.length());

        //public StringBuffer(String str) 创建StringBuffer的同时,内部就包含了一个初始字符串
        StringBuffer sb3 = new StringBuffer("hello");
        System.out.println("sb3: " + sb3);
        System.out.println(sb3.capacity()); // 21
        System.out.println(sb3.length()); // 5


    }
}
3、主要功能
添加功能
    public StringBuffer append(String str)
    public StringBuffer insert(int offset,String str)
删除功能
    public StringBuffer deleteCharAt(int index)
    public StringBuffer delete(int start,int end)
替换功能
    public StringBuffer replace(int start,int end,String str)
反转功能
    public StringBuffer reverse()
public class StringBufferDemo2 {
    public static void main(String[] args) {
        StringBuffer sb1 = new StringBuffer(); // 16
        System.out.println("sb1: " + sb1);

        System.out.println("---------------------------------");
        //public StringBuffer append(String str) 追加
        //无论添加的是什么类型的元素值,只要进入到StringBuffer中,它就是一个一个的普通字符
        sb1.append("java")
                .append(100)
                .append(12.34)
                .append(true);
        System.out.println("sb1: " + sb1);
        //java10hello012.34true

        //public StringBuffer insert(int index,String str) 在StringBuffer容器中指定位置添加字符串
        sb1.insert(6,"hello");
        System.out.println("sb1: " + sb1);
//        System.out.println(sb1.capacity()); // 34
//        System.out.println(sb1.length());

        //public StringBuffer deleteCharAt(int index) 删除指定位置索引上的字符
//        sb1.deleteCharAt(10);
//        System.out.println("sb1: " + sb1);

        //public StringBuffer delete(int start,int end) [start,end)
        //java10hello012.34true
//        sb1.delete(8,14);
//        System.out.println("sb1: " + sb1);

        //public StringBuffer replace(int start,int end,String str) [start,end)
        //java10hello012.34true
        sb1.replace(8,12,"数加科技");
        System.out.println("sb1: " + sb1);

        //public StringBuffer reverse() 反转
        StringBuffer sb2 = new StringBuffer("abcdef");
        System.out.println("sb2: "+sb2);
        sb2.reverse();
        System.out.println("sb2: "+sb2);


    }
}
截取功能
public String substring(int start)
public String substring(int start,int end)
public class StringBufferDemo3 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("hello world");
        System.out.println("sb: " + sb);
        System.out.println("---------------------------");
//        String s1 = sb.substring(4);
//        System.out.println("sb: " + sb);
//        System.out.println("s1: " + s1);

//        public String substring(int start,int end)
        String s2 = sb.substring(4, 8);
        System.out.println("sb: " + sb);
        System.out.println("s2: " + s2);

    }
}

4、案例
4.1、
StringStringBuffer的相互转换

A<-->B
什么场景需要做转换操作:
场景1:可以需要用到另一种类中的方法,来方便处理我们的需求
场景2:方法传参
public class StringBufferDemo1 {
    public static void main(String[] args) {
        //创建一个String对象
        String s1 = "hello";
        System.out.println("s1: " + s1);

        //String-->StringBuffer
        //1、使用StringBuffer构造方法
        StringBuffer sb1 = new StringBuffer(s1);
        System.out.println("sb1: " + sb1);


        //StringBuffer-->String
        //1、toString()方法
        StringBuffer sb2 = new StringBuffer("world");
        String s2 = sb2.toString();
        //2、截取
        String s3 = sb2.substring(0);

        System.out.println("--------------转换调用另一种类中的方法案例-------------------");
        //String对象字符串逆序
        String s4 = "abcd";
        System.out.println("s4: "+s4);
        //先转StringBuffer
        StringBuffer sb3 = new StringBuffer(s4);
        sb3.reverse();
        //再转回字符串String类型
        String s5 = sb3.toString();
        System.out.println("s5: "+s5);

    }
}
4.2、
看程序写结果:
    String作为参数传递
    StringBuffer作为参数传递: 哪一个StringBuffer对象调用方法,哪一个就会改变
public class StringBufferDemo2 {
    public static void main(String[] args) {
//        String s1 = "hello";
//        String s2 = "world";
//        System.out.println("s1: " + s1 + ", s2: " + s2); // s1: hello,s2: world
//        change(s1, s2);
//        System.out.println("s1: " + s1 + ", s2: " + s2); // s1: hello,s2: world

        StringBuffer sb1 = new StringBuffer("hello");
        StringBuffer sb2 = new StringBuffer("world");
        System.out.println("sb1: " + sb1 + ",sb2: " + sb2); // sb1: hello,sb2: world
        change(sb1,sb2);
        System.out.println("sb1: " + sb1 + ",sb2: " + sb2); //sb1: hello,sb2: worldworld
    }

    public static void change(StringBuffer sb1,StringBuffer sb2){
        sb1 = sb2;
        sb2 = sb1.append(sb2); //
        System.out.println("sb1: " + sb1 + ",sb2: " + sb2); // sb1: worldworld,sb2: worldworld
    }


    public static void change(String s1, String s2) {
        s1 = s2; // s1: "world"  s2: "world"
        s2 = s1 + s2; // s1: "world" s2: "worldworld"
        System.out.println("s1: " + s1 + ", s2: " + s2); // s1: world,s2: worldworld
    }
}
4.3、
把数组拼接成一个字符串
public class StringBufferTest1 {
    public static void main(String[] args) {
        int[] arr = {11, 22, 33, 44, 55};
        //创建一个空的StringBuffer
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < arr.length; i++) {
            sb.append(arr[i]);
        }
        String s = sb.toString();

        System.out.println("s: "+s);
    }
}
StringBuilder类

用法和StringBuffer一样但是StringBuilder是线程不安全的

Arrays类
1、概述

​ 针对数组进行操作的工具类。
​ 提供了排序,查找等功能。

2、成员方法
静态成员方法:
    public static String toString(int[] a)
    public static void sort(int[] a)
    public static int binarySearch(int[] a,int key)
public class ArraysDemo1 {
    public static void main(String[] args) {
        //public static String toString(int[] a) 将数组以字符串的形式返回
        int[] arr = {12,32,33,16,27,31};
        String s1 = Arrays.toString(arr);
        System.out.println("排序前:"+s1); // [11, 22, 33, 44, 55, 66]

        //public static void sort(int[] a) 快速排序,结果是升序的
        Arrays.sort(arr);
        System.out.println("排序后:"+Arrays.toString(arr));

        //[12, 16, 27, 31, 32, 33]
        //public static int binarySearch(int[] a,int key) 二分查找 返回查找到元素的索引
        //前提:序列必须是有序的
        System.out.println(Arrays.binarySearch(arr, 100)); // -7

    }
} 
基本类型包装类
1、概述

​ 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数 据

2、作用

用于基本数据类型与字符串之间的转换

3、基本类型对应的包装类
byte: Byte
short: Short
int: Integerlong: Long
float: Float
double: Double
boolean: Boolean
char: Character
4、主要介绍两种Integer和Character
  • Integer构造方法

    • Integer类中的构造方法:
          public Integer(int value)
          public Integer(String s)
          
      public class BaoZhuangDemo1 {
          public static void main(String[] args) {
      //        char c = '中';
      //        int a = 10;
      //        System.out.println(Integer.MAX_VALUE);
      //        System.out.println(Integer.MIN_VALUE);
      
              //public Integer(int value) 将基本数据类型int包装成引用数据类型Integer
      //        Integer i1 = new Integer(100);
      //        System.out.println(i1); // 100
              Integer i1 = 100; // 自动装箱
              System.out.println(i1 + 1); // 自动拆箱
      
              //public Integer(String s)
      //        Integer i2 = new Integer("nihao"); // NumberFormatException
      //        System.out.println(i2);
              Integer i2 = new Integer("200");
              System.out.println(i2 + 3); // 自动拆箱
      
          }
      }
      
      
      
      public class IntegerDemo1 {
          public static void main(String[] args) {
      //        Integer i1 = 200; // 自动装箱
      //        int i = i1.intValue(); // 手动获取被包装的数值
      //        System.out.println(i);
      
              //public static int parseInt(String s)  将字符串转成数字
              //String->int
              int i = Integer.parseInt("100");
              System.out.println(i);
      
              //public static String toString(int i)
              //int->String
              String s1 = Integer.toString(200);
              System.out.println(s1); // "200"
      
              //public static Integer valueOf(int i)
              //int->Integer
              Integer i1 = Integer.valueOf(100);
              System.out.println(i1);
      
              //public static Integer valueOf(String s)
              //String->Integer
              Integer i2 = Integer.valueOf("300");
              System.out.println(i2);
      
      
              //Integer->int
              Integer i3 = 400;
              int i4 = i3.intValue();
              System.out.println(i4);
              // 进制转换
              //public static String toBinaryString(int i)
              //public static String toOctalString(int i)
              //public static String toHexString(int i)
      
              String s = Integer.toBinaryString(20);
              System.out.println(s); // 10100
      
      
          }
      }
      
      
  • Character

    • char - Character
      
      public static boolean isUpperCase(char ch)
      public static boolean isLowerCase(char ch)
      public static boolean isDigit(char ch)//重点
      public static char toUpperCase(char ch)
      public static char toLowerCase(char ch)
          
          
      public class CharacterDemo1 {
          public static void main(String[] args) {
      //        Character c1 = '中';
              boolean b1 = Character.isUpperCase('A');
              System.out.println(b1); // true
      
              boolean b2 = Character.isLowerCase('A');
              System.out.println(b2); // false
      
              //public static boolean isDigit(char ch) 判断一个字符是否是数字
              boolean b3 = Character.isDigit('中');
              System.out.println(b3); // true
      
              //public static char toUpperCase(char ch) 转大写
              char c1 = Character.toUpperCase('a');
              System.out.println(c1);
      
              //public static char toLowerCase(char ch) 转小写
      
          }
      }
      
System类
1、概述

System 类包含一些有用的类字段和方法。它不能被实例化

2、成员方法
 	public static void gc() 垃圾回收
    public static void exit(int status)  强制退出程序
    public static long currentTimeMillis()  重要!!!
        
    public class SystemDemo1 {
    public static void main(String[] args) {
//        for (int i = 0; i < 10; i++) {
//            if(i==5){
                break;
//                System.exit(0);
//            }
//            System.out.println(i);
//        }
//
//        System.out.println("hello world");

        //public static long currentTimeMillis() 获取当前此刻的时间戳
        long l = System.currentTimeMillis();
        System.out.println(l);

    }
}

Date类
1、概述

类 Date 表示特定的瞬间,精确到毫秒。

2、构造方法
public Date()  将程序运行到此行时,此刻的时间
public Date(long date)  将指定的时间戳转成对应的时间
3、成员方法
public long getTime()197011日以来,以此日期为准的00:00:00 GMT的毫秒数
public void setTime(long time)设置此 Date对象以表示19701100:00:00 GMT后的 time毫秒的时间点。
    
    public class DateDemo {
    public static void main(String[] args) {
//        Date date = new Date();
//        System.out.println(date);
//        long time = date.getTime();
//        long l = System.currentTimeMillis();

        //public Date(long date)  将指定的时间戳转成对应的时间
        Date date = new Date(1709535166924L);
        System.out.println(date);
        // yyyy年MM月dd日 HH时mm分ss秒
    }
}
DateFormat类
1、概述

DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。
是抽象类,所以使用其子类SimpleDateFormat

2、SimpleDateFormat类的成员方法和构造方法
package com.shujia.day11;

import com.shujia.day11.utils.DateUtil;

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

/*
    日期格式化类:SimpleDateFormat

    构造方法:
        public SimpleDateFormat(String pattern) yyyy年MM月dd日 HH时mm分ss秒

 */
public class SimpleDateFormatDemo {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");

        Date date = new Date();
        System.out.println(date); // Mon Mar 04 15:06:31 CST 2024
//public final String format(Date date)将Date类型的值格式化我们目标的形式
//public Date parse(String source) 将String类型的格式转换为Date格式

        String res1 = sdf.format(date); // 将Date类型的值格式化我们目标的形式
        System.out.println(res1);

        //需求:将一个时间戳转成指定日期格式 1709535166924
        //时间戳--Date--格式化
//        Date date1 = new Date(1709535166924L);
//        String res2 = sdf.format(date1);
//        System.out.println(res2);
        System.out.println(DateUtil.formatDate(1709535166924L));

    }
}

Random类
1、概述

产生随机数

2、构造方法
package com.shujia.day11;

import java.util.Random;

/*
    Math.random() [0.0,1.0)
构造方法
public Random()
public Random(long seed)
成员方法
public int nextInt()
public int nextInt(int n)




 */
public class RandomDemo1 {
    public static void main(String[] args) {
        Random random = new Random();
//        int i = random.nextInt();
//        System.out.println(i);

        //public int nextInt(int bound)  随机产生一个整数,范围在 [0,bound)
        System.out.println(random.nextInt(100)+1);


    }
}

第五章集合

回顾学习过的容器以及集合的概述
回顾一下到目前为止,我们学习过哪些容器
数组:长度一旦确定就不能更改,对于同一个数组中,既可以是存放基本数据类型元素的数组,也可以是存放引用数据类型数组
StringBuffer:无论存放什么类型的元素,一旦进入到StringBuffer,都是以字符类型的形式存储,长度可以改变
集合:结合了数组和长度可变的优点
集合是一个统称,并不是指某一种类。
属于的集合既然是一个容器,那么这些容器都有一些相同的共性。
我们学习,本质上是学习不同的集合类,这些集合类之间有相同的属性或者行为,集合与集合之间会形成继承体系。
比如有些集合不允许元素重复,有些集合允许元素重复,有些集合可以对元素进行排序,有些集合对查找有更好的支持....
换句话说,就是不同的集合底层的数据结构不同,导致了不同的集合具有不同的特点。
集合的继承体系

集合Collection父接口
1、概述
通过观察发现Collection是一个接口,其中只有抽象方法,接口无法实例化,所以我们要找该接口的实现子类,借助这个子类学习使用Collection中的方法
Collection下面又有两大子接口
    - List接口
        - 实现子类:ArrayList
    - Set接口
2、通过ARRayList实现Collection中的方法
 ArrayList是实现了List接口的具体子类,List接口是继承自Collection接口
   借助创建ArrayList对象,来使用学习Collection接口中的方法

   ArrayList类中有一个无参构造方法,可以让我们创建一个空的集合

   Collection中的抽象方法一定会在具体子类ArrayList中找到实现:
       boolean add(Object e)
       boolean remove(Object o)
       void clear()
       boolean contains(Object o)
       boolean isEmpty()
       int size()

   注意:
       1、java中的集合只能存储引用数据类型
       2、

*/
package com.shujia.day11;

import java.util.Collection;
import java.util.ArrayList;
public class CollectionDemo2 {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();

        //boolean add(Object e) 向集合中添加元素 可以存放任意的引用数据类型
        c1.add(10); // 这里底层涉及到今天说的自动装箱 int:10 --> Integer
        /*
            ArrayList extends AbstractList
            AbstractList extends AbstractCollection
         */
        c1.add(12.34);
        c1.add("hello");
        c1.add(true);
        c1.add(true);
        System.out.println("c1: " + c1); // c1: [10] // toString()底层实际使用的是ArrayList的父类的父类AbstractCollection中的toString()

        System.out.println("-------------------[");
        //boolean remove(Object o)
        c1.remove(true); // 调用一次,只会删一次值
        System.out.println("c1: " + c1);
        System.out.println("-------------------[");
        //void clear() 清空集合
//        c1.clear();
//        System.out.println("c1: " + c1);
        System.out.println("-------------------[");
        //boolean contains(Object o) 判断集合是否包含某个元素
        System.out.println(c1.contains(43));
        System.out.println("-------------------[");
        //boolean isEmpty()
        System.out.println(c1.isEmpty());
        System.out.println("-------------------[");
        //int size() 获取集合中的元素个数
        System.out.println(c1.size());

    }
}

2.1Collection中的方法
package com.shujia.day11;

import java.util.ArrayList;
import java.util.Collection;

/*
    Collection中的方法:
        boolean addAll(Collection c)
        boolean removeAll(Collection c)
        boolean containsAll(Collection c)
        boolean retainAll(Collection c)

 */
public class CollectionDemo3 {
    public static void main(String[] args) {
        Collection c1 = new ArrayList();
        c1.add("java");
        c1.add("maven&git");
        c1.add("redis");
        c1.add("clickhouse");
        c1.add("hadoop");
        System.out.println("c1: " + c1);

        Collection c2 = new ArrayList();
        c2.add("redis");
        c2.add("clickhouse");
        c2.add("hadoop");
        c2.add("hive");
        System.out.println("c2: " + c2);

//        System.out.println("-------------------------------");
//        //boolean addAll(Collection c) 将一个集合中的元素添加到另一个集合中
//        c1.addAll(c2);
//        System.out.println("c1: "+c1);
//        System.out.println("c2: "+c2);
//        System.out.println("-------------------------------");
//        //boolean removeAll(Collection c)
//        c1.removeAll(c2); //在c1中移除与c2相同部分的元素
//        System.out.println("c1: " + c1);
//        System.out.println("c2: " + c2);
//        System.out.println("-------------------------------");
//        // boolean containsAll(Collection c)  判断一个集合中是否包含另一个集合的所有元素
//        System.out.println(c1.containsAll(c2)); // false
        System.out.println("-------------------------------");
        //boolean retainAll(Collection c) 求交集
//        c1.retainAll(c2); // c1与c2做交集 交集的结果存储在c1中,c2不变
        c2.retainAll(c1);
        System.out.println("c1: " + c1);
        System.out.println("c2: " + c2);

    }
}
3、如何遍历集合
package com.shujia.day11;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/*
如何遍历一个集合:
    Object[] toArray()
    把集合转成数组,可以实现集合的遍历

    Iterator iterator()
    迭代器,集合的专用遍历方式

    今后学习集合的4步骤:
        1、创建集合对象
        2、创建元素对象
        3、向集合中添加元素
        4、遍历集合

 */
public class CollectionDemo4 {
    public static void main(String[] args) {
        //创建集合对象
        Collection c1 = new ArrayList(); // 接口多态
        //向集合中添加元素
        c1.add("java");
        c1.add("maven&git");
        c1.add("redis");
        c1.add("clickhouse");
        c1.add("hadoop");
//        c1.add(12);
        System.out.println("c1: " + c1);
        System.out.println("-----------------------");
        // Object[] toArray() 将集合转成数组,遍历数组,得到集合中每个元素
        Object[] array = c1.toArray();
        for (int i = 0; i < array.length; i++) {
            //Object obj = "java"
            //向下转型
            String s = (String) array[i]; // String s = "java"
            System.out.println(s + ", " + s.length());
        }
        System.out.println("===============================");
        //Iterator iterator() 迭代器遍历 是Collection集合特有遍历方式 可遍历的序列
        //获取迭代器对象,其中存储了集合元素
        //因为我这里的c1实际上堆内存是ArrayList的对象,所以调用iterator()底层返回的是一个ArrayList类中的私有成员内部类Itr的对象
        Iterator iterator = c1.iterator(); //Iterator iterator = new Itr()
        /*
            public Iterator<E> iterator() {
                return new Itr();
            }
         */

//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next()); // NoSuchElementException
        //我们应该在取下一个元素之前,先判断一下,下一个位置上有没有元素,如果有,就next(),否则就不获取
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
//        if(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
        //虽然解决了不报错的问题,但是,但是要判断多少次也是不清楚的
        //在不知道次数的情况下,获取数据,优先使用while循环
        while (iterator.hasNext()) {
            String s = (String) iterator.next();
            System.out.println(s + ", " + s.length());
        }


    }
}
3.1、存储自定义对象并遍历
package com.shujia.day12;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.shujia.day12;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/*
    存储自定义对象并遍历
        Student(name,age)

 */
public class CollectionDemo1 {
    public static void main(String[] args) {
        //1、创建集合对象
        Collection c1 = new ArrayList();

        //2、创建元素对象
        Student s1 = new Student("张三", 12);
        Student s2 = new Student("李四", 14);
        Student s3 = new Student("王五", 12);
        Student s4 = new Student("赵六", 13);

        //3、将元素添加到集合中
        c1.add(s1);
        c1.add(s2);
        c1.add(s3);
        c1.add(s4);

        //4、遍历集合
        //4.1先转数组遍历
//        Object[] objects = c1.toArray();
//        for (int i = 0; i < objects.length; i++) {
//            Student student = (Student) objects[i];
//            System.out.println(student.getName() + "--" + student.getAge());
//        }

        //4.2迭代器遍历
        Iterator iterator = c1.iterator(); // new Itr()
        while (iterator.hasNext()) {
            Student student = (Student) iterator.next();
            System.out.println(student.getName() + "--" + student.getAge());
        }


    }
}
List子接口
1、概述
- List 元素有序且允许重复,集合具有索引的概念(接口)
    - ArrayList(具体子类)
    -Vector(具体子类)
    -Linkedlist(具体子类)
2、List子接口的实现
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListDemo1 {
    public static void main(String[] args) {
        //1. 创建集合对象
        List list1 = new ArrayList();

        //2. 创建并添加元素
        list1.add("java");
        list1.add("hadoop");
        list1.add("hive");
        list1.add("hbase");
        list1.add("数据采集");

        //3. 遍历
        Iterator iterator = list1.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}
3、List子接口的成员方法
 因为List相关集合具备了索引的概念,所以List集合它里面就会出现与索引相关的方法
    List集合中的元素具有有序的特点,这里的有序不是指排序,而是指的是存储和取出的顺序一致。


    void add(int index,E element)
    E remove(int index)
    E get(int index)
    E set(int index,E element)
    ListIterator listIterator()
     public class ListDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        List list1 = new ArrayList();

        list1.add("java");
        list1.add("hadoop");
        list1.add("hive");
        list1.add("apple");

        System.out.println("list1: " + list1);
        System.out.println("================================");
        //void add(int index,Object element) 指定索引位置添加元素
        list1.add(1,"mysql");
        System.out.println("list1: " + list1);
        System.out.println("================================");
        //[java, mysql, hadoop, hive, apple]
        //E remove(int index) 指定索引删除元素,返回被删除的元素值
//        Object o = list1.remove(10); //IndexOutOfBoundsException
//        Object o = list1.remove(2);
//        System.out.println(o);
//        System.out.println("list1: " + list1);
        System.out.println("================================");
        //[java, mysql, hadoop, hive, apple]
        //Object get(int index) 指定索引获取索引位置上的元素
        Object o = list1.get(2);
        System.out.println(o);
        System.out.println("list1: " + list1);
        System.out.println("================================");
        //E set(int index,E element)
        Object o1 = list1.set(4, "datax"); //通过索引设置元素值,返回原始的元素
        System.out.println(o1);
        System.out.println("list1: " + list1);
        System.out.println("================================");
        //ListIterator listIterator() 列表迭代器,List集合特有的迭代器
        ListIterator listIterator = list1.listIterator();
        while (listIterator.hasNext()){
            System.out.println(listIterator.next());
        }
        System.out.println("--------------");
        //无论是ListIterator迭代器,还是Iterator迭代器,指针只有一个,而且最开始的时候,在迭代器的最左边
        //所以要想反向遍历,得先正向遍历一遍,将指针移动到最右边,
        while (listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }


    }
}

4、案例
package com.shujia.day12;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/*
    在一个存储字符串的集合中,查找"java"这个元素,如果找到了,就将"flink"添加到集合中,在哪添加无所谓。

    ConcurrentModificationException 并发修改异常
    原因是,在遍历迭代器的时候,集合的长度发生了变化,而迭代器不知道,长度不一致,报错。
    如何避免这样的问题。
    迭代器遍历,迭代器修改,集合遍历,集合修改

 */
public class ListDemo3 {
    public static void main(String[] args) {
        //创建集合对象
        List list1 = new ArrayList();

        list1.add("hadoop");
        list1.add("hive");
        list1.add("java");
        list1.add("apple");

        System.out.println("list1: " + list1);
        System.out.println("=============================");
        //方式1:
//        Object[] array = list1.toArray();
//        for (int i = 0; i < array.length; i++) {
//            String s = (String) array[i];
//            if ("java".equals(s)) {
//                list1.add("flink"); // 将元素添加到末尾
//            }
//        }

        //错误的方式!
//        Iterator iterator = list1.iterator();
//        while (iterator.hasNext()) {
//            Object next = iterator.next();
//            String s = (String) next;
//            if ("java".equals(s)) {
//                list1.add("flink");
//            }
//        }

        //方式2:
//        ListIterator listIterator = list1.listIterator();
//        while (listIterator.hasNext()) {
//            String s = (String) listIterator.next();
//            if ("java".equals(s)) {
//                listIterator.add("flink"); // 将元素添加到指定元素的后一个
//            }
//        }

        System.out.println("list1: " + list1);
    }
}
Arrayslist类
1、概述
 ArrayList()实现类 底层数据结构是数组,查询快,增删慢,线程是不安全的,效率高。
2、演示
public class ArrayListDemo1 {
    public static void main(String[] args) {
        //1、创建集合对象
        ArrayList list = new ArrayList();

        //2、3、创建元素对象并添加集合
        list.add("java");
        list.add("world");
        list.add("hello");
        list.add("java");

        //4、遍历集合
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}
3、案例
3.1、使用ArrayList去除集合中字符串的重复值(字符串的内容相同)
public class ArrayListTest1 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList list1 = new ArrayList();

        //添加一些字符串
        list1.add("java");
        list1.add("hadoop");
        list1.add("hive");
        list1.add("hbase");
        list1.add("数据采集");
        list1.add("hive");
        list1.add("hadoop");
        list1.add("hive");

        //创建一个新空集合
        ArrayList list2 = new ArrayList();

        //遍历旧集合,判断新集合中是否存在该元素,如果有,说明重复,就不添加到新集合中,否则就添加
        Iterator iterator = list1.iterator();
        while (iterator.hasNext()) {
            String s = (String) iterator.next();
            if (!list2.contains(s)) {
                list2.add(s);
            }
        }

        System.out.println("list1: " + list1);
        System.out.println("list2: " + list2);


    }
}
3.2、使用ArrayList去除集合中自定义对象的重复值(对象的成员变量值都相同)
package com.shujia.day12;

import java.util.Objects;

public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Teacher teacher = (Teacher) o;
        return age == teacher.age && Objects.equals(name, teacher.name);
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.shujia.day12;

/*
    使用ArrayList去除集合中自定义对象的重复值(对象的成员变量值都相同)
    按照上一个去重字符串的逻辑对对象进行去重发现,并没有去重
    经过简单的分析后发现,if (!list2.contains(t))永远为true,元素才会不断的向新集合中添加
    !list2.contains(t)整体永远为true, 意味着list2.contains(t)永远为false
    现在应该去看contains()方法的源码

    public boolean contains(Object o) {
        return indexOf(o) >= 0; //  indexOf(o)永远为-1  indexOf(o) >= 0 永远为false
    }

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    通过分析后发现,底层判断元素是否重复,是调用了元素类型中的equals方法来比较的,而我们Teacher类中没有重写equals方法
    调用的是父亲Object类中的equals方法,而Object类中的equals比较的是地址值,我们的每一个Teacher对象都是new出来的,地址值肯定不一样
    所以equals比较的结果永远为false
    要想达到去重效果,就应该比较对象的成员变量值,而不是比较地址值,我们应该是当对象的成员变量值相同的时候,认为是同一个对象
    只需要元素的类型中重写equals方法即可,自动生成即可。

 */


import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListTest2 {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList list = new ArrayList();

        //创建元素对象
        Teacher t1 = new Teacher("小虎", 18);
        Teacher t2 = new Teacher("阿彪", 17);
        Teacher t3 = new Teacher("阿凤", 15);
        Teacher t4 = new Teacher("小宁", 17);
        Teacher t5 = new Teacher("小虎", 18);

        //将元素添加到集合中
        list.add(t1);
        list.add(t2);
        list.add(t3);
        list.add(t4);
        list.add(t5);

        //创建一个新空集合
        ArrayList list2 = new ArrayList();

        //遍历旧集合,判断新集合中是否存在该元素,如果有,说明重复,就不添加到新集合中,否则就添加
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Teacher t = (Teacher) iterator.next();
            if (!list2.contains(t)) {
                list2.add(t);
            }
        }

        System.out.println("list1: " + list);
        System.out.println("list2: " + list2);
    }
}
Vector类
1、概述

底层数据结构是数组,查询快,增删慢
线程安全,效率低

2、成员方法
public void addElement(E obj)
public E elementAt(int index)
public Enumeration elements()
package com.shujia.day12;

import java.util.Enumeration;
import java.util.Vector;

/*
Collection(接口)
    - List(接口)
        - ArrayList(实现类) 底层数据结构是数组,查询快,增删慢,线程是不安全的,效率高。
        - Vector(实现类) 底层数据结构是数组,查询快,增删慢,线程是安全的,效率低。(即使这个是线程安全的,我们今后也不用,后面我们会将不安全的ArrayList变成安全的)
        -
    - Set(接口)


 */
public class VectorDemo1 {
    public static void main(String[] args) {
        //构造方法
        //Vector()
        //构造一个空向量,使其内部数据数组的大小为 10 ,标准容量增量为零。
        //创建一个Vector对象
        Vector vector = new Vector();

//        vector.add("java");
//        vector.add("hello");
//        vector.add("world");
//        vector.add("hadoop");
//        vector.add("hive");

        vector.addElement("java"); // 今后这个方法使用add进行代替
        vector.addElement("hello");
        vector.addElement("world");
        vector.addElement("hadoop");
        vector.addElement("hive");

        System.out.println(vector);
        System.out.println("--------------------------");
        //public Object elementAt(int index)  根据索引获取元素
        System.out.println(vector.elementAt(2)); //今后这个方法使用get进行代替
        System.out.println(vector.get(2));
        System.out.println("--------------------------");
        //public Enumeration elements() 获取所有的元素 // 这个方式可以使用迭代器代替遍历
//        Enumeration elements = vector.elements();
//        while (elements.hasMoreElements()){
//            System.out.println(elements.nextElement());
//        }

    }
}
Linkedlist类
1、概述

底层数据结构是链表,查询慢,增删快
线程不安全,效率高

2、成员方法
public void addFirst(E e)及addLast(E e)
public E getFirst()及getLast()
public E removeFirst()及public E removeLast()
public class LinkedListDemo1 {
    public static void main(String[] args) {
        //构造方法:
        //LinkedList()
        //构造一个空列表。
        //创建一个LinkedList集合对象
        LinkedList list = new LinkedList();

//        list.addLast("java");
//        list.addLast("world");
//        list.addLast("shujia");
//        list.addLast("hello");

        list.addFirst("java");
        list.addFirst("world");
        list.addFirst("shujia");
        list.addFirst("hello");
        System.out.println(list);
        System.out.println("====================================");
        //public E getFirst()及getLast()
//        System.out.println(list.getFirst());
//        System.out.println(list);
//        System.out.println(list.getLast());

        //public E removeFirst()及public E removeLast()  返回的是被删除的元素
//        System.out.println(list.removeFirst());
//        System.out.println(list);
        System.out.println(list.removeLast());
        System.out.println(list);
    }
}
3、案例
请用LinkedList模拟栈数据结构的集合,并测试
栈:先进后出
package com.shujia.day12;

import java.util.LinkedList;

public class MyStack {
    private LinkedList linkedList;

    public MyStack() {
        linkedList = new LinkedList();
    }

    public void myAdd(Object obj){
        linkedList.addFirst(obj);
    }

    public Object myGet(){
        return linkedList.removeFirst();
    }

    public int getLength(){
        return linkedList.size();
    }


}
package com.shujia.day12;

import java.util.LinkedList;

/*
    请用LinkedList模拟栈数据结构的集合,并测试
    栈:先进后出

    如果笔试的时候,直接使用LinkedList对象存储元素的话,0分
    题目的本意是,自己写一个类(Mystack类),类中将LinkedList进行封装,使用自己的类创建对象,底层用的是LinkedList

 */


public class LinkedListTest {
    public static void main(String[] args) {
//        LinkedList list = new LinkedList();
//        list.addFirst("java");
//        list.addFirst("hello");
//        list.addFirst("world");
//        list.addFirst("hadoop");
//        list.addFirst("hive");
//
//        System.out.println(list);

        //创建自己的集合类对象
        MyStack myStack = new MyStack(); // 底层是创建一个LinkedList对象
        myStack.myAdd("java"); //底层是调用了LinkedList对象的addFirst方法
        myStack.myAdd("hello");
        myStack.myAdd("hadoop");
        myStack.myAdd("hive");
        int length = myStack.getLength();
        for (int i = 0; i < length; i++) {
            System.out.println(myStack.myGet());
        }

    }
}
泛型
1、概述
在此之前的集合,我们使用的时候,可以传入不同的数据类型的元素。
但是,实际开发中,一个集合只能够存储一种数据类型,为了就是将来获取元素处理的时候,处理方式能够统一
想一想之前也学习过一种容器,这种容器在定义的时候,就明确了元素的数据类型(数组)
现在集合想要模仿使用定义数组时的特点,可以明确一个集合元素的数据类型。
java为了实现这样的功能,提供了一个技术给我们使用:泛型
2、泛型的好处

1、消除程序中大部分警告

2、获取元素时不需要做向下转型

3、限制集合的存储的数据类型,将来处理的方式统一

3、泛型的种类
3.1、泛型类
 开发代码的时候,写的泛型,将来使用的时候,应该传入一个引用数据类型给到开发时的泛型中接收
   将类当作参数一样进行传递
   <>中应该定义一个变量,用于接收将来使用时传入的引用数据类型,优点类似形式参数的味道
   就要符合标识符的命名规则
   将来开发时,如果泛型中只需要接收一个引用数据类型,定义时,只需要写一个大写的字母

class Demo1<E> { // Demo1<String>
    //E在一个类中是唯一的。
    public void fun1(E a){ // String
        System.out.println(a);
    }
}

/*
    泛型类:在开发代码的时候,将泛型写在类上
 */
public class FanXingDemo1 {
    public static void main(String[] args) {
        Demo1<String> stringDemo1 = new Demo1<>();
        stringDemo1.fun1("12");
    }
}
3.2、泛型方法
泛型方法,将来调用时可以传入任意的数据类型,与类上面的泛型无关
class Demo2{
    //泛型方法,将来调用时可以传入任意的数据类型,与类上面的泛型无关
    public <E> void fun1(E e){
        System.out.println(e);
    }
}

/*
    泛型方法:将泛型定义在方法上
 */
public class FanXingDemo2 {
    public static void main(String[] args) {
        Demo2 demo2 = new Demo2();
        demo2.fun1("da");
        demo2.fun1(12);
        demo2.fun1(12.34);
    }
}
3.3、泛型接口
如果一个类实现一个接口,这个接口有泛型的话,类定义时,也要有泛型,一般情况下与接口的泛型写法一致
interface Inter1<E>{
    void fun1(E e);
}

//如果一个类实现一个接口,这个接口有泛型的话,类定义时,也要有泛型,一般情况下与接口的泛型写法一致
class Demo3<E> implements Inter1<E>{
    @Override
    public void fun1(E e) {
        System.out.println(e);
    }
}

/*
    泛型接口:将泛型定义在接口上
 */
public class FanXingDemo3 {
    public static void main(String[] args) {
        Inter1<String> s = new Demo3<>();
        s.fun1("abc");

        /*
                interface List<E>
                class ArrayList<E> implements List<E>{
                    add(E e){}
                }
         */
        List<String> list = new ArrayList<>();
        list.add("dsa");
//        list.add(12);

    }
}
4、泛型的高级用法
开发程序时,泛型的高级用法:
    泛型通配符<?>
        任意类型,如果没有明确,那么就是Object以及任意的Java类了
    ? extends E
        向下限定,E及其子类
    ? super E
        向上限定,E及其父类
import java.util.ArrayList;
import java.util.Collection;

class Animal {
}

class Dog extends Animal {
}

class Cat extends Animal {
}

class Demo4<E> { // E: Animal
    public void fun1(Collection<? super E> e) {

    }
}

public class FanXingDemo4 {
    public static void main(String[] args) {
//        ArrayList<?> list1 = new ArrayList<Dog>();

        //向下限定
//        ArrayList<? extends Animal> list1 = new ArrayList<Object>();
        ArrayList<Animal> list1 = new ArrayList<>();
        //E: Animal

        //ddAll(Collection<? extends E> c)
        // Collection<? extends E> 表示将来应该传入一个Collection集合对象,并且集合中的元素类型是E本身或者是E的子类类型
        // E: Animal

//        ArrayList<Dog> list2 = new ArrayList<>();
//        ArrayList<Cat> list3 = new ArrayList<>();
//        ArrayList<Object> list4 = new ArrayList<>();
//        list1.addAll(list2);
//        list1.addAll(list3);
//        list1.addAll(list4);

        // ? super E 向上限定
        Demo4<Animal> d = new Demo4<>();
        //fun1(Collection<? super E> e)
        //Collection<? super E> 表示将来应该传入一个Collection集合对象,但是集合中的元素类型只能是E的本身类型或者E的父类类型
        ArrayList<Dog> list2 = new ArrayList<>();
        ArrayList<Cat> list3 = new ArrayList<>();
        ArrayList<Object> list4 = new ArrayList<>();
//        d.fun1(list2);
//        d.fun1(list3);
        d.fun1(list4);
        d.fun1(list1);

    }
}
增强for循环
1、概述

用来遍历数组和Collection集合

语句定义格式:

​ for(元素数据类型 变量名 :Collection集合/数组){

​ System.out.println(变量名)

}

增强for循环存在的目的,主要适用于替代Collection中的迭代器的。
public class ZengForDemo1 {
    public static void main(String[] args) {
//        int[] arr = {12, 34, 1, 234, 42};
//        for (int i : arr) {
//            System.out.println(i);
//        }

        System.out.println("==========================");
        ArrayList<String> list2 = new ArrayList<>(); // 后面的尖括号泛型中的数据类型,根据前面定义时的泛型自动类型推断
        // String[] arr;


        //创建元素对象并添加到集合中
        list2.add("java");
        list2.add("hadoop");
        list2.add("hive");
        list2.add("java");
        list2.add("hbase");
//        for(String s:list2){
//            System.out.println(s);
//        }


    }
}
静态导入
1、概述

格式:import static xx.xx.xx.静态方法

注意:最好在本类中没有重名时使用

2、举例
package com.shujia.day13;

public class Demo5 {
    public static void show1(){
        System.out.println("好好学习,天天向上!");
    }

    public static void show2(){
        System.out.println("莫使金樽空对月");
    }
}
-----------------------------------------------------
    package com.shujia.day13;
import static com.shujia.day13.Demo5.*;

/*
    静态导入:import static xx.xx.xx.静态方法;
    最好在本类中没有重名的时候,使用
 */


public class StaticImportDemo1 {
    public static void main(String[] args) {
//        Demo5.show1();
//        Demo5.show2();
        //..n个静态方法
        //Demo5.静态方法();
        show1();
//        show1();
//        show2();
    }

//    public static void show1(){
//        System.out.println("丁义杰真帅!");
//    }

}
可变参数
1、概述

指的是方法将来调用时,可以传入若干个指定类型的参数

注意:

​ 1、一个方法的定义中只能有一个可变参数

​ 2、可变参数必须在参数列表的最后一个定义

2、举例
public class KeBianCanShuDemo {
    public static void main(String[] args) {
        //需求1:定义一个方法,求两个int类型值的和
        System.out.println(sum(1, 2));
        //需求2:定义一个方法,求三个int类型值的和
        System.out.println(sum(1, 2, 3));
        //需求3:定义一个方法,求四个int类型值的和
        System.out.println(sum(1, 2, 3, 4));
        //需求4:有一个学生,姓名是字符串类型叫做"尚平",考了三门试:98,99,60
        //求该学生的总成绩,姓名:xxx, 总分:xxx
        System.out.println(sumScore("尚平", 98, 99, 60));


        //Arrays工具类中的一个方法
        //public static <T> List<T> asList(T... a)
        List<String> list = Arrays.asList("java", "hadoop", "hive"); // 底层实际上是创建一个ArrayList集合


    }

    public static String sumScore(String name, int... scores) {
        int sumNum = 0;
        for (int i : scores) {
            sumNum += i;
        }
        return "姓名:"+name+", 总分:"+sumNum;
    }


    //将来传入若干个int类型的元素值,JVM将其封装到一个数组中,这个数组名叫做arr
    public static int sum(int... arr) {
        int sumNum = 0;
        for (int i : arr) {
            sumNum += i;
        }
        return sumNum;
    }

//    public static int sum(int a, int b) {
//        return a + b;
//    }
//
//    public static int sum(int a, int b, int c) {
//        return a + b + c;
//    }
}
List集合练习
1、集合的嵌套遍历
package lianxi.day13;

import java.util.ArrayList;

public class ListTest1 {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();
        ArrayList<Student> list3 = new ArrayList<>();
        list3.add(new Student("张三",18));
        list3.add(new Student("李四",19));
        list2.add(15);
        list2.add(33);
        list2.add(55);
        list1.add("java");
        list1.add("hbase");
        list1.add("hadoop");
        list1.add("hive");
        ArrayList<Object> list = new ArrayList<>();
        list.add(list1);
        list.add(list2);
        list.add(list3);
        for (Object o : list) {
            ArrayList o1 = (ArrayList) o;
            for (Object object : o1) {
                System.out.println(object);
            }
        }
    }
}
2、获取10个1-20之间的随机数,要求不能重复
package lianxi.day13;

import java.util.ArrayList;
import java.util.Random;

/*
获取10个1-20之间的随机数,要求不能重复

 */
public class ListTest2 {
    public static void main(String[] args) {
        Random random = new Random();
        ArrayList<Integer> list = new ArrayList<>();
        int size = 0;
        while (size<=10){
            int number = random.nextInt(20)+1;
            if(!list.contains(number)){
                list.add(number);
            }
            size=list.size();
        }

        System.out.println(list);
    }

}
3、键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
package lianxi.day13;
import java.util.ArrayList;
import java.util.Scanner;
/*
键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
 */

public class ListTest3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        ArrayList<Integer> list = new ArrayList<>();
        while (true){
            System.out.println("输入一个数据:");
            int number = sc.nextInt();
            list.add(number);
            if (number==0){
                break;
            }
        }
        Integer maxNumber = list.get(0);

        for (Integer number : list) {
            if (number>maxNumber){
                maxNumber=number;
            }

        }
        System.out.println("最大值为:"+maxNumber);
    }
}
Set接口
1、概述

不含重复且无序元素的集合

Set接口-HashSet
1、概述
底层数据结构是哈希表,线程不安全的,效率高
2、构造方法
构造方法:HashSet() 构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。
无序:存储和取出的顺序不一致
3、举例
public class SetDemo1 {
    public static void main(String[] args) {
        //创建一个HashSet对象
        HashSet<String> set1 = new HashSet<>();

        //向集合中添加元素
        set1.add("pitaya");
        set1.add("apple");
        set1.add("watermelon");
        set1.add("banana");
        set1.add("apple");

//        System.out.println(set1);
        for (String s : set1) {
            System.out.println(s);
        }

    }
}
4、HashSet如何做到去重的
4.1、HashSet中的add方法的源码解释

public class HashSet<E> implements Set<E>{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }

    //set1.add("pitaya");
    public boolean add(E e) {
        //HashSet中的add方法底层是调用了HashMap中的put方法
        // e -- "pitaya"
        // PRESENT -- new Object()
        return map.put(e, PRESENT)==null;
    }
}


public class HashMap<K,V> implements Map<K,V>{
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    public V put(K key, V value) {
        // key -- "pitaya"
        // value -- new Object()
        // 与元素类型中的hashCode方法有关
        return putVal(hash(key), key, value, false, true);
    }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        Node<K,V>[] tab;
        Node<K,V> p;
        int n, i;
        //第一次初始化一个hashTable出来
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //第一个元素,直接放入到哈希表中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e;
            K k;
            //判断待插入的元素与已经在哈希表中的元素
            //1、哈希值是否一样 元素类.hashCode()方法
            //2、内容值是否一样 元素类.equals()方法
            if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

}
4.2、举例
package com.shujia.day13;
/*
    使用HashSet存储学生对象,当学生对象的姓名和年龄一样的时候,表示是同一个学生,应该要进行去重

    结论:
        HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法。

 */

import java.util.HashSet;
import java.util.Objects;

class Student1{
    String name;
    int age;

    public Student1() {
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student1 student1 = (Student1) o;
        return age == student1.age && Objects.equals(name, student1.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

public class HashSetDemo1 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<Student1> set = new HashSet<>();

        //创建元素对象
        Student1 s1 = new Student1("尚平", 18);
        Student1 s2 = new Student1("丁义杰", 16);
        Student1 s3 = new Student1("黄杰", 18);
        Student1 s4 = new Student1("尚平", 18);
        Student1 s5 = new Student1("汪权", 17);

        //将元素添加到集合中
        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        set.add(s5);

        //遍历集合
        for (Student1 s : set) {
            System.out.println(s);
        }

    }
}
Set接口-HashSet类-LinkedHashSet类
1、LinkedHashSet类概述

元素有序唯一
由链表保证元素有序
由哈希表保证元素唯一

2、举例
package com.shujia.day13;

import java.util.LinkedHashSet;

/*
    Collection
        - List
            -- ArrayList
            -- Vector
            -- LinkedList
        - Set(接口) 元素无序且唯一
            -- HashSet 底层数据结构是哈希表,线程不安全的,效率高
                --子类:LinkedHashSet 底层数据结构是哈希表与双向链表
 */
public class LinkedHashSetDemo1 {
    public static void main(String[] args) {
        //创建一个LinkedHashSet对象
        LinkedHashSet<Integer> set = new LinkedHashSet<>();

        //向集合中添加元素
        set.add(12);
        set.add(123);
        set.add(14);
        set.add(21);
        set.add(73);
        set.add(18);
        set.add(12);

        //遍历集合
        for (Integer i : set) {
            System.out.println(i);
        }
    }
}
Set接口-TreeSet类
1、TreeSet类概述

使用元素的自然顺序对元素进行排序
或者根据创建 set 时提供的 Comparator 进行排序
具体取决于使用的构造方法。

2、举例
package com.shujia.day13;

import java.util.TreeSet;

/*
    Collection
        - List
            -- ArrayList
            -- Vector
            -- LinkedList
        - Set(接口) 元素无序且唯一
            -- HashSet 底层数据结构是哈希表,线程不安全的,效率高
                --子类:LinkedHashSet 底层数据结构是哈希表与双向链表
            -- TreeSet 底层数据结构是红黑树(可排序的),线程不安全,效率高

    TreeSet中的排序方式有两种:
        1、自然排序  Comparable
        2、比较器排序 comparator
    需求1:使用TreeSet存储字符串对象,并遍历

    通过观察源码发现,因为我们创建TreeSet集合对象的时候,使用的是无参构造方法
    所以底层创建TreeMap的时候也是无参构造方法,comparator是null,不走比较器排序
    走的是自然排序


 */
public class TreeSetDemo1 {
    public static void main(String[] args) {
        //TreeSet() 构造一个新的,空的树组,根据其元素的自然排序进行排序。
        //创建一个空的TreeSet集合对象
        TreeSet<String> treeSet = new TreeSet<>();

        //创建元素对象并添加到集合中
        treeSet.add("java");
        treeSet.add("hadoop");
        treeSet.add("redis");
        treeSet.add("clickhouse");
        treeSet.add("hbase");
        treeSet.add("java");

        //遍历集合
        for (String s : treeSet) {
            System.out.println(s);
        }

    }
}
3、TreeSet如何进行排序的
3.1、TreeSet中的add方法的源码解释
public class TreeSet<E>{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();

    public TreeSet() {
        this(new TreeMap<E,Object>()); // this(..)
    }

    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    // treeSet.add(s2);
    public boolean add(E e) {
        //TreeSet中add方法底层调用的是TreeMap中的put方法
        //e -- s2
        //PRESENT -- new Object()
        return m.put(e, PRESENT)==null;
    }
}


public class TreeMap{
    private final Comparator<? super K> comparator;
    private transient Entry<K,V> root;

    public TreeMap() {
        comparator = null;
    }

    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

    public V put(K key, V value) {
        // key -- "hadoop"
        // value -- new Object()
        Entry<K,V> t = root; // null
        //如果说是第一个元素插入进来,这个元素就作为红黑树的根保存
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null); // s1
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator; // 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) // s2
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t; // "java"
                cmp = k.compareTo(t.key); // 1
                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;
    }



}
3.2、举例1-自然排序
package com.shujia.day13;

import java.util.TreeSet;

/*
    使用TreeSet集合存储自定义对象Student2(String name,int age)
    要求:最终要按照年龄从小到大排序,并且当学生的姓名和年龄一样的时候要进行去重

    我们按照存储字符串一样的逻辑存储自定义对象,运行的时候报错了
    ClassCastException:
    com.shujia.day13.Student2 cannot be cast to java.lang.Comparable
    原因是因为,我们现在使用的时候自然排序方式,需要元素的类实现Comparable接口,才可以进行转型

 */
class Student2 implements Comparable<Student2> {
    String name;
    int age;

    public Student2() {
    }

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

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

    @Override
    public int compareTo(Student2 s) {
//        return 0;
//        return -1;
//        return 1;
        //显式条件:按照年龄从小到大排序
        //this -- 待插入的元素
        //o -- 已经在树中的根
//        return  this.age-s.age;
        //隐藏条件:当年龄一样的时候,比较姓名
        int i1 = this.age - s.age;
        return (i1 == 0) ? this.name.compareTo(s.name) : i1;
    }
}

public class TreeSetDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student2> treeSet = new TreeSet<>();

        //创建元素对象
        //终要按照年龄从小到大排序
        Student2 s1 = new Student2("张三", 16);
        Student2 s2 = new Student2("李四", 16);
        Student2 s3 = new Student2("王五", 15);
        Student2 s4 = new Student2("赵六", 12);
        Student2 s5 = new Student2("张三", 16);

        //将学生对象添加到集合中
        treeSet.add(s1);
        treeSet.add(s2);
        treeSet.add(s3);
        treeSet.add(s4);
        treeSet.add(s5);

        //遍历集合
        for (Student2 student2 : treeSet) {
            System.out.println(student2);
        }
    }
}
3.3 举例2-比较器排序
package com.shujia.day13;

import java.util.Comparator;
import java.util.TreeSet;

/*
    比较器排序:
        public TreeSet(Comparator<? super E> comparator) {
            this(new TreeMap<>(comparator));
        }

    小总结:
        1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法,
            但是要保证元素的类实现Comparable接口,重写compareTo方法。
        2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法,
            传入一个实现了Comparator接口的子类对象,重写compare方法
 */
class Student3 {
    String name;
    int age;

    public Student3() {
    }

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

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

public class TreeSetDemo3 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student3> treeSet = new TreeSet<>(new Comparator<Student3>() {
            @Override
            public int compare(Student3 o1, Student3 o2) {
                //o1 -- 待插入的元素
                //o2 -- 已经存在树中的根
                int i1 = o1.age - o2.age;
                return (i1 == 0) ? o1.name.compareTo(o2.name) : i1;
            }
        });

        //创建元素对象
        //终要按照年龄从小到大排序
        Student3 s1 = new Student3("张三", 16);
        Student3 s2 = new Student3("李四", 16);
        Student3 s3 = new Student3("王五", 15);
        Student3 s4 = new Student3("赵六", 12);
        Student3 s5 = new Student3("张三", 16);

        //将学生对象添加到集合中
        treeSet.add(s1);
        treeSet.add(s2);
        treeSet.add(s3);
        treeSet.add(s4);
        treeSet.add(s5);

        //遍历集合
        for (Student3 student3 : treeSet) {
            System.out.println(student3);
        }
    }
}
Set集合练习
1、编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
package lianxi.day13;


/*
编写一个程序,获取10个1至20的随机数,要求随机数不能重复。

 */

import java.util.Random;
import java.util.TreeSet;

public class TreeSetTest1 {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet = new TreeSet<>();
        Random random = new Random();
        while (true){
            int number = random.nextInt(20)+1;
            treeSet.add(number);
            if (treeSet.size()==10){
                break;
            }
        }
        System.out.println(treeSet);
    }
}
2、键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
package lianxi.day13;

import java.util.Scanner;

/*
键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台

 */

import java.util.TreeSet;

class Student3 implements Comparable {
    private String name;
    private int chineseScore;
    private int mathScore;
    private int englishScore;
    private int sumScore;

    public Student3() {
    }

    public Student3(String name, int chineseScore, int mathScore, int englishScore, int sumScore) {
        this.name = name;
        this.chineseScore = chineseScore;
        this.mathScore = mathScore;
        this.englishScore = englishScore;
        this.sumScore = sumScore;
    }

    public String getName() {
        return name;
    }

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

    public int getChineseScore() {
        return chineseScore;
    }

    public void setChineseScore(int chineseScore) {
        this.chineseScore = chineseScore;
    }

    public int getMathScore() {
        return mathScore;
    }

    public void setMathScore(int mathScore) {
        this.mathScore = mathScore;
    }

    public int getEnglishScore() {
        return englishScore;
    }

    public void setEnglishScore(int englishScore) {
        this.englishScore = englishScore;
    }
    public void setSumScore(int sumScore){
        this.sumScore=sumScore;
    }
    public int getSumScore(){
        return sumScore;
    }
    public int sumScore(int chineseScore, int mathScore, int englishScore) {
        return chineseScore + mathScore + englishScore;
    }

    @Override
    public int compareTo(Object o) {
        Student3 o1 = (Student3) o;
        int i1 = o1.getSumScore() - this.getSumScore();
        //总分一样,语文成绩不一定一样,
        int i2 = (i1 == 0) ? o1.getChineseScore() - this.getChineseScore() : i1;
        //总分一样,语文成绩一样,数学成绩不一定一样
        int i3 = (i2 == 0) ? o1.getMathScore() - this.getMathScore() : i2;
        //分数都一样,姓名不一定一样
        return (i3 == 0) ? o1.getName().compareTo(this.getName()) : i3;
    }



    @Override
    public String toString() {
        return "Student3{" +
                "name='" + name + '\'' +
                ", chineseScore=" + chineseScore +
                ", mathScore=" + mathScore +
                ", englishScore=" + englishScore +
                ", sumScore=" + sumScore +
                '}';
    }
}

public class TreeSetTest2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        TreeSet<Student3> treeSet = new TreeSet<>();
        Student3 s1 = new Student3();
        for (int i = 1; i <= 5; i++) {
            System.out.println("第" + i + "学生");
            System.out.print("姓名:");
            String name = sc.next();
            System.out.print("语文成绩:");
            int chineseScore = sc.nextInt();
            System.out.print("数学成绩:");
            int mathScore = sc.nextInt();
            System.out.print("英语成绩:");
            int englishScore = sc.nextInt();
            Student3 s = new Student3(name, chineseScore, mathScore, englishScore,s1.sumScore(chineseScore,mathScore,englishScore));
            treeSet.add(s);
        }
        for (Student3 student3 : treeSet) {
            System.out.println(student3);
        }
    }
}
Collection集合的总结
Map接口
1、概述
1Map集合中的元素是一个键值对
2、一个键对应一个值,键不允许重复,键是唯一的
3、值可以发生重复
2、成员方法(实现的方式:通过具体的子类HashMap)
V put(K key,V value)//添加元素
V remove(Object key//返回删除键所对应的值
void clear()//清空元素
boolean containsKey(Object key)//是否包含键
boolean containsValue(Object value)//是否包含值
boolean isEmpty()//是否为空
int size()//元素的个数
------------------------------------
V get(Object key)//获取键所对应的值
Set<K> keySet()//获取所有的键并封装成一个Set集合
Collection<V> values()//获取所有的值并封装成Collection集合
Set<Map.Entry<K,V>> entrySet()//获取所有的键值对并封装成Set集合

2.1、举例
public class MapDemo1 {
    public static void main(String[] args) {
        //创建HashMap的对象
        //HashMap()
        //构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
        HashMap<Integer, String> map1 = new HashMap<>();

        //V put(K key,V value) 向集合中添加一个元素键值对,如果键已经存在集合中,值会进行覆盖处理,返回被覆盖的值
        //向map集合中添加一个键值对元素
        System.out.println(map1.put(1001, "尚平"));
        System.out.println("map1: " + map1);
        System.out.println("--------------------------------");
        System.out.println(map1.put(1001, "丁义杰"));
        System.out.println("map1: " + map1);
        System.out.println("--------------------------------");
        map1.put(1003, "乔丹");
        map1.put(1002, "黄杰");
        map1.put(1004, "卢鹏");
        map1.put(1005, "张跃");
        System.out.println("map1: " + map1);
        System.out.println("--------------------------------");
        //V remove(Object key) 根据键删除整个键值对,因为键是唯一的,返回键对应的值
        System.out.println(map1.remove(1100));
        System.out.println("map1: " + map1);
//        System.out.println("--------------------------------");
//        //void clear()
//        map1.clear();
//        System.out.println("map1: " + map1);
        System.out.println("--------------------------------");
        //boolean containsKey(Object key)
        System.out.println(map1.containsKey(1001));
        //boolean containsValue(Object value)
        System.out.println(map1.containsValue("黄杰1"));
        System.out.println("--------------------------------");
        //boolean isEmpty()
        System.out.println(map1.isEmpty());
        System.out.println("--------------------------------");
        //int size() 获取元素的个数,键值对的个数
        System.out.println(map1.size());




    }
}
2.2、举例2
package com.shujia.day14;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
成员方法:
    V get(Object key)
    Set<K> keySet()
    Collection<V> values()
    Set<Map.Entry<K,V>> entrySet()


    Map集合遍历的方式:
        1、先获取所有的键,遍历键获取对应的值
        2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
 */
public class MapDemo2 {
    public static void main(String[] args) {
        HashMap<Integer, String> map1 = new HashMap<>();
        map1.put(1001, "尚平");
        map1.put(1003, "乔丹");
        map1.put(1002, "黄杰");
        map1.put(1004, "卢鹏");
        map1.put(1005, "张跃");
        System.out.println("map1: " + map1);
        System.out.println("-------------------------");
        //V get(Object key) 根据键获取值
        System.out.println(map1.get(1010));
        System.out.println("-------------------------");
        //Set<K> keySet() 获取所有的键组成一个Set集合返回
        Set<Integer> keySet = map1.keySet();
        System.out.println(keySet);
        System.out.println("-------------------------");
        //Collection<V> values()
        Collection<String> values = map1.values();
        System.out.println(values);
        System.out.println("-------------------------");
        //Set<Map.Entry<K,V>> entrySet()
        Set<Map.Entry<Integer, String>> entries = map1.entrySet();
        System.out.println(entries);

    }
}
3、遍历的方法
3.1、先获取所有的键,遍历键获取对应的值
public class MapDemo3 {
    public static void main(String[] args) {
        HashMap<Integer, String> map1 = new HashMap<>();
        map1.put(1001, "尚平");
        map1.put(1003, "乔丹");
        map1.put(1002, "黄杰");
        map1.put(1004, "卢鹏");
        map1.put(1005, "张跃");
        System.out.println("map1: " + map1);
        System.out.println("-------------------------");
        //获取所有的键
        Set<Integer> keySet = map1.keySet();
        for (Integer key : keySet) {
            //根据键获取对应的值
            String value = map1.get(key);
            System.out.println(key + "--" + value);
        }
    }
}
3.2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
public class MapDemo4 {
    public static void main(String[] args) {
        HashMap<Integer, String> map1 = new HashMap<>();
        map1.put(1001, "尚平");
        map1.put(1003, "乔丹");
        map1.put(1002, "黄杰");
        map1.put(1004, "卢鹏");
        map1.put(1005, "张跃");
        System.out.println("map1: " + map1);
        System.out.println("-------------------------");
        //先获取所有的键值对
        Set<Map.Entry<Integer, String>> keyValues = map1.entrySet();
        for (Map.Entry<Integer, String> keyValue : keyValues) {
            Integer key = keyValue.getKey();
            String value = keyValue.getValue();
            System.out.println(key + "--" + value);
        }
    }
}
Map接口-HashMap类
1、概述

键是哈希表结构,可以保证键的唯一性,线程不安全,效率高

2、案例

创建学生类

package com.shujia.day14;

import java.util.Objects;

public class Student1{
    private String name;
    private int age;

    public Student1() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student1 student1 = (Student1) o;
        return age == student1.age && Objects.equals(name, student1.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

//    @Override
//    public int compareTo(Student1 o) {
//        //按照年龄从小到大排序
//        int i1 = this.getAge() - o.getAge();
//        //隐藏条件:年龄一样的时候,姓名不一定一样
//        return (i1 == 0) ? this.getName().compareTo(o.getName()) : i1;
//    }
}

实现

package com.shujia.day14;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
    根据昨天HashSet中add方法实际上就是调用今天要讲的HashMap中的put方法,要想保证HashMap中的键唯一
    根据分析得出,键的类型要重写hashCode()方法和equals方法

    HashMap<Student,String> 当学生的姓名和年龄一样的时候,认为是同一个学生

 */
public class HashMapDemo1 {
    public static void main(String[] args) {
        //创建集合对象
        HashMap<Student1, String> map = new HashMap<>();

        //创建元素对象
        Student1 s1 = new Student1("小红", 16);
        Student1 s2 = new Student1("小红2", 15);
        Student1 s3 = new Student1("小红3", 18);
        Student1 s4 = new Student1("小红4", 19);
        Student1 s5 = new Student1("小红", 16);

        //向集合中添加元素
        map.put(s1, "打篮球");
        map.put(s2, "踢足球");
        map.put(s3, "打排球");
        map.put(s4, "打台球");
        map.put(s5, "打羽毛球");

        //遍历集合
        Set<Map.Entry<Student1, String>> entries = map.entrySet();
        for (Map.Entry<Student1, String> entry : entries) {
            Student1 key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ": " + value);
        }
    }
}
Map接口-HashMap类-LinkedHashMap类
1、概述
底层数据结构是哈希表和双链表
取出的顺序与添加的顺序一致
2、案例
public class LinkedHashMapDemo {
    public static void main(String[] args) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();

        map.put("1001","shujia1");
        map.put("1002","shujia2");
        map.put("1003","shujia3");
        map.put("1002","shujia4");
        map.put("1007","shujia5");
        map.put("1005","shujia6");
        map.put("1004","shujia7");

        System.out.println(map);
    }
}
Map接口-TreeMap类
1、概述
底层数据结构是红黑树,可以保证键的排序和唯一性
根据创建时调用的构造方法不同,map中的键排序的规则不同
创建TreeMap是无参构造方法的话,将来map中的键是以自然排序
创建TreeMap是有参构造方法,传入Comparator接口的实现类对象(匿名内部类的方式)
2、案例
2.1、案例1
public class TreeMapDemo1 {
    public static void main(String[] args) {
        // 创建TreeMap由无参构造方法
        TreeMap<Integer, String> map = new TreeMap<>();

        map.put(1004,"张三");
        map.put(1005,"李四");
        map.put(1002,"王五");
        map.put(1001,"赵六");
        map.put(1004,"王二麻");

        System.out.println(map);
    }
}
2.2、案例2

创建学生类

package com.shujia.day14;

import java.util.Objects;

public class Student1{
    private String name;
    private int age;

    public Student1() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student1 student1 = (Student1) o;
        return age == student1.age && Objects.equals(name, student1.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

//    @Override
//    public int compareTo(Student1 o) {
//        //按照年龄从小到大排序
//        int i1 = this.getAge() - o.getAge();
//        //隐藏条件:年龄一样的时候,姓名不一定一样
//        return (i1 == 0) ? this.getName().compareTo(o.getName()) : i1;
//    }
}

实现

package com.shujia.day14;

import java.util.Comparator;
import java.util.TreeMap;

/*
    将学生类型作为键的类型,按照年龄从小到大排序,当学生的姓名和年龄一样的
    时候,要进行去重
 */
public class TreeMapDemo2 {
    public static void main(String[] args) {
        TreeMap<Student1, String> map = new TreeMap<>(new Comparator<Student1>() {
            @Override
            public int compare(Student1 o1, Student1 o2) {
                //按照年龄从小到大排序
                int i1 = o1.getAge() - o2.getAge();
                //隐藏条件:年龄一样的时候,姓名不一定一样
                return (i1 == 0) ? o1.getName().compareTo(o2.getName()) : i1;
            }
        });

        map.put(new Student1("张三",18),"打游戏");
        map.put(new Student1("张三2",17),"打游戏2");
        map.put(new Student1("张三3",13),"打游戏3");
        map.put(new Student1("张三4",17),"打游戏4");
        map.put(new Student1("张三5",19),"打游戏5");
        map.put(new Student1("张三",18),"打游戏6");


        System.out.println(map);


    }
}
Map集合的练习
1、“aababcabcdabcde”,获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest1 {
    public static void main(String[] args) {
        TreeMap<Character, Integer> map = new TreeMap<>();
        String s="aababcabcdabcde";
        for (int i=0;i<s.length();i++){
            char c = s.charAt(i);
            if (!map.containsKey(c)){
                map.put(c,1);
            }else {
                Integer i1 = map.get(c)+1;
                map.put(c,i1);
            }
        }
//        System.out.println(map);
        Set<Character> keySet = map.keySet();
        for (Character c1 : keySet) {
            System.out.print(c1+"("+map.get(c1)+")");
        }
        System.out.println();
    }
}
2、HashMap嵌套ArrayList
package com.shujia.day14;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
    HashMap嵌套ArrayList
    每一个人都有一些爱好
    HashMap<Person,ArrayList<String>>
 */
class Person {
    String name;

    public Person() {
    }

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

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

public class TreeMapTest2 {
    public static void main(String[] args) {
        HashMap<Person, ArrayList<String>> map = new HashMap<>();

        ArrayList<String> zhangSanList = new ArrayList<>();
        zhangSanList.add("王者荣耀");
        zhangSanList.add("英雄联盟");
        map.put(new Person("张三"), zhangSanList);

        ArrayList<String> liSiList = new ArrayList<>();
        liSiList.add("饥荒");
        liSiList.add("文明6");
        map.put(new Person("李四"), liSiList);

        ArrayList<String> shangPingList = new ArrayList<>();
        shangPingList.add("完蛋,我被美女包围了");
        shangPingList.add("GTA5");
        map.put(new Person("尚平"), shangPingList);

        //第一种方式遍历:
//        Set<Person> people = map.keySet();
//        for (Person person : people) {
//            ArrayList<String> list = map.get(person);
//            System.out.println(person.name + "喜欢的游戏是:" + list);
//        }

        //第二种遍历方式:
        Set<Map.Entry<Person, ArrayList<String>>> entries = map.entrySet();
        for (Map.Entry<Person, ArrayList<String>> entry : entries) {
            Person person = entry.getKey();
            ArrayList<String> list = entry.getValue();
            System.out.println(person.name + "喜欢的游戏是:" + list);
        }


    }
}
面试题
package com.shujia.day14;

import java.util.HashMap;
import java.util.Hashtable;

/*
    集合常见的面试题:
        1、你在开发种常用的集合有哪些?
            如果没有特殊要求,且元素是单列元素的话,使用ArrayList居多,如果是键值对元素的话,默认使用HashMap居多
            其他的集合根据查询或者增删的需求,或者线程安全或不安全来选择。
        2、HashMap和Hashtable的区别
            共同点:都是实现了Map接口的实现子类,元素都是以键值对的方式存储
            不同点:
                1、HashMap键和值都允许为null值,Hashtable的键和值都不允许为null值
                2、HashMap线程不安全,Hashtable是线程安全的
        3、List,Set,Map等接口是否都继承子Map接口


    Collection(接口)
        - List(接口) 元素有序且允许重复
            - ArrayList 底层数据结构是数组,查询快,增删慢,线程不安全,效率高
            - Vector 底层数据结构是数组,查询快,增删慢,线程安全的,效率低。即便是线程安全的,后面也不用。
            - LinkedList 底层数据结构是双链表,增删快,查询慢,线程不安全,效率高
        - Set(接口)  元素无序且唯一
            - HashSet 底层数据结构是哈希表,元素类中要重写hashCode()和equals方法,线程不安全,效率高
                - LinkedHashSet 底层数据结构是哈希表和双链表,哈希表保证了元素的唯一性,双链表保证了元素的有序,线程不安全,效率高
            - TreeSet 底层数据结构是红黑树,可以进行自定义排序
                自然排序:要求创建集合对象的时候是无参构造方法,且元素类要实现Comparable接口,并重写compareTo方法
                比较器排序:要求在创建集合对象的时候,构造方法中需要传入Comparator接口的实现子类对象,重写compare方法

    Map(接口)
        - HashMap(子类)
            - LinkedHashMap(子类)
        - TreeMap(子类)


 */
public class MianShiDemo {
    public static void main(String[] args) {
        HashMap<String, String> map1 = new HashMap<>();
        /*
            public V put(K key, V value) {
                return putVal(hash(key), key, value, false, true);
            }
         */
        map1.put("a", "s");
        map1.put(null, "s");
        map1.put("a", null);
        map1.put(null, null);
        System.out.println(map1);

        /*
        public synchronized V put(K key, V value) {
            // Make sure the value is not null
            if (value == null) {
                throw new NullPointerException();
            }

            // Makes sure the key is not already in the hashtable.
            Entry<?,?> tab[] = table;
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;
            @SuppressWarnings("unchecked")
            Entry<K,V> entry = (Entry<K,V>)tab[index];
            for(; entry != null ; entry = entry.next) {
                if ((entry.hash == hash) && entry.key.equals(key)) {
                    V old = entry.value;
                    entry.value = value;
                    return old;
                }
        }
         */
        Hashtable<String, String> map2 = new Hashtable<>();
        map2.put("a", "s");
//        map2.put(null, "s");
//        map2.put("a", null);
//        map2.put(null, null);
        System.out.println(map2);

    }
}
Collections类(工具类)
1、概述

针对集合操作的工具类

2、成员方法
	public static <T> void sort(List<T> list)//只对List集合进行排序
    public static <T> int binarySearch(List<?> list,T key)//二分查找,返回值所在的索引
    public static <T> T max(Collection<?> coll)//求最大值
    public static void reverse(List<?> list)//反转
    public static void shuffle(List<?> list)//打乱
3、案例
public class CollectionsDemo {
    public static void main(String[] args) {
        //public static <T> void sort(List<T> list)  只能对List集合排序
        ArrayList<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("apple");
        list.add("hbase");

        System.out.println("list: " + list);
        Collections.sort(list);
        System.out.println("list: " + list);
        System.out.println("-----------------------------------");
        //[apple, hbase, hello, world]
        //public static <T> int binarySearch(List<?> list,T key) 二分查找
        System.out.println(Collections.binarySearch(list, "hello"));
        System.out.println("-----------------------------------");
        //public static <T> T max(Collection<?> coll)
        System.out.println(Collections.max(list)); //获取最大值
        System.out.println("-----------------------------------");
        //public static void reverse(List<?> list)
        Collections.reverse(list);
        System.out.println("list: " + list);
        System.out.println("-----------------------------------");
        //public static void shuffle(List<?> list) 这里的shuffle和后面hadoop中的shuffle不是一个概念
        //随机打乱
        Collections.shuffle(list);//[hbase, apple, world, hello]  [hello, apple, hbase, world]
        System.out.println("list: " + list);


    }
}
4、Collections工具类可以将线程不安全的类变成线程安全的类,使用方式不变
public class CollectionsDemo2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        List<String> list1 = Collections.synchronizedList(list);
        list1.add("java");
        list1.add("java");
        list1.add("java");
        list1.add("java");

        System.out.println(list1);
       

    }
}
集合总结

第六章IO流

异常
1、概述

异常就是Java程序在运行过程中出现的错误

2、异常分类图解

image-20240308193719294

3、异常处理的方案
3.1 try…catch方案
1、语法定义格式:
    try{
        可能会出错的代码;
    }catch(异常类名 e){
        如果try中的代码出现了问题,就会匹配catch,执行catch中的代码
    }

顶层父类Throwable中的方法:
    getMessage() 获取异常的信息
    toString() 获取异常类和异常信息
    e.printStackTrace();JVM初始默认处理方案输出
public class ExceptionDemo2 {
    public static void main(String[] args) {
        int[] arr = {11,22,33,44};

        System.out.println(arr[5]);
        try {
            System.out.println(arr[5]); //new ArrayIndexOutOfBoundsException()
            //当try中出现异常的时候,JVM内部会创建一个对应的异常类的对象
            //将该异常类对象分发给catch进行匹配
            //与catch中的类型匹配,如果一样,就捕获到该异常,在catch大括号中进行处理
        }catch (Exception e){ //Exception e = new ArrayIndexOutOfBoundsException()
//            System.out.println("出错啦!!!");
//            System.out.println(e.getMessage());
//            System.out.println(e.toString());
            e.printStackTrace();
        }


        System.out.println("hello world");
    }
}
  • try…catch注意事项

    1try中的代码一旦遇到了异常,try中的后续代码不会执行,直接匹配catch
        2、如果多个catch中的异常类之间具有继承关系,应该将父类的catch放在下面
        3、如果catch异常类之间是平级关系,不存在父子关系的话,可以使用jdk1.7新特性 使用|在一个catch中表达
        4finally中的代码,无论try中是否报错,都会执行,finally在今后开发中,一般放释放关闭资源的代码
    
    今后在程序中,抛开Error不谈,运行时期异常一般情况是由于代码逻辑不够严谨导致,检查并修改代码就能解决
    但是,编译时期异常,无法通过修改逻辑代码解决,必须进行异常处理。
    所以结论:
        1、编译时期异常必须要进行处理,程序才能运行。
        2、运行时期异常,可以通过修改代码逻辑避免报错,也可以像处理编译时期异常一样进行处理。
    
public class ExceptionDemo3 {
    public static void main(String[] args) {
       try {
           int[] arr = {11,22,33,44,55};
           System.out.println(arr[2]); // ArrayIndexOutOfBoundsException

           int a = 3;
           int b = 1;
           System.out.println(a/b); // ArithmeticException
       }catch (ArrayIndexOutOfBoundsException | ArithmeticException e1){
            e1.printStackTrace();
       }finally {
           System.out.println("这是finally中代码。。。。。");
       }

        System.out.println("hello world");

    }
}
3.2、throws方案
在出错代码的方法上写, 不建议在main方法上写,因为main方法中出现问题的话,就会将错误对象抛给JVM,而JVM默认处理方案就是程序停止,后续代码不会执行
方法抛出的异常如果是编译时期异常,调用者必须要处理,否则编译不通过,运行不了
如果抛出的是运行时期异常,可以不处理,但是可能会因为逻辑不够严谨导致出错。
public class ExceptionDemo4 {
    public static void main(String[] args) {
//        Demo demo = new Demo();
//        Object o = demo.clone();
//
//        System.out.println("helloworld");



//        try {
//            fun1();
//        }catch (Exception e){
//            e.printStackTrace();
//        }
        fun1();


        System.out.println("helloworld");


    }

    public static void fun1() throws ArrayIndexOutOfBoundsException,ArithmeticException{
        int[] arr = {11,22,33,44};
        System.out.println(arr[2]);

        int a = 3;
        int b = 1;
        System.out.println(a/b);

    }
}
4、throw
4.1概述
throws: 抛出异常类名,在方法的定义上写,表示一种可能性
throw: 抛出异常对象,在方法内部写,表示一定会发生该异常

以下故事,纯属虚构,如有雷同,纯属巧合。
丁义杰是一个快乐的小矿工,每天的任务就在矿洞中挖矿。有一天,他挖到一个远古炸弹(throw),于是将炸弹向外运输(throws,
运输给洞外的负责人,由负责人处理这颗炸弹(try..catch..)
只要没有try..catch..异常依旧存在,程序停止。

今后遇到异常,直接try..catch...
遇到红色下划线,就alt+回车键选择处理的方式
public class ThrowDemo {
    public static void main(String[] args) {

//        try {
//            fun1();
//        }catch (Exception e){
//            e.printStackTrace();
//        }

        Demo demo = new Demo();
        try {
            Object o = demo.clone();
            System.out.println(o);
            System.out.println(demo);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }



        System.out.println("hello world");
    }

    public static void fun1() {
        int a = 3;
        int b = 0;
        if (b != 0) {
            System.out.println(a / b);
        } else {
            throw new ArithmeticException();
        }
    }
}
File类
1、概述
是用来描述一个文件或者文件夹的。
2、构造方法
public File(String pathname)
public File(String parent,String child)
public File(File parent,String child)
2.1、案例
public class FileDemo1 {
    public static void main(String[] args) {
        //public File(String pathname)
        //根据文件或者文件夹的路径名创建对应File对象
        //相对路径:src/com/shujia/day14/a.txt
        //绝对路径(完整路径):E:\projects\IDEAProjects\bigdata29-java\src\com\shujia\day14\a.txt

//        File file = new File("E:\\projects\\IDEAProjects\\bigdata29-java\\src\\com\\shujia\\day14\\a.txt");
//        System.out.println(file);

        //public File(String parent,String child)
//        File file = new File("E:\\projects\\IDEAProjects\\bigdata29-java\\src\\com\\shujia\\day14", "a.txt");
//        System.out.println(file);

        //public File(File parent,String child)
        File file = new File("E:\\projects\\IDEAProjects\\bigdata29-java\\src\\com\\shujia\\day14");
        File file1 = new File(file, "a.txt");
        System.out.println(file1);
    }
}
3、成员方法
创建功能
    public boolean createNewFile()
    public boolean mkdir()
    public boolean mkdirs()
删除功能
    public boolean delete()
重命名功能
    public boolean renameTo(File dest)
3.1、案例
public class FileDemo2 {
    public static void main(String[] args) throws Exception{
//        File file = new File("E:\\projects\\IDEAProjects\\bigdata29-java\\src\\com\\shujia\\day14\\a.txt");

        //public boolean createNewFile() 创建该目标,创建文件
//        System.out.println(file.createNewFile()); // 创建成功 true,目标已经存在,返回false

        //public boolean mkdir() 创建单极文件夹
//        System.out.println(file.mkdir());

//        File file1 = new File("E:\\projects\\IDEAProjects\\bigdata29-java\\src\\com\\shujia\\day14\\aaa\\bbb\\ccc");
//        //public boolean mkdirs() 创建多级文件夹
//        System.out.println(file1.mkdirs());

        //public boolean delete()
        //如果删除的是文件夹的话,要保证该文件夹必须要是空的才能删除
        //删除文件,直接就能删除
//        File file = new File("E:\\projects\\IDEAProjects\\bigdata29-java\\src\\com\\shujia\\day14\\aaa");
//        System.out.println(file.delete());
//        File file = new File("src/com/shujia/day14/a.txt");
//        System.out.println(file.delete());

        //public boolean renameTo(File dest)
        File file = new File("src/com/shujia/day14/a.txt");
        File file1 = new File("src/com/shujia/day14/尚平.txt");
        System.out.println(file.renameTo(file1));

    }
}
4、判断功能
4.1、概述
public boolean isDirectory()
public boolean isFile()
public boolean exists()
public boolean canRead()
public boolean canWrite()
public boolean isHidden()
4.2、案例
public class FileDemo3 {
    public static void main(String[] args) {
        File file = new File("src/com/shujia/day14/尚平.txt");

        //public boolean isDirectory() 判断该File对象指向是否是一个文件夹
        System.out.println(file.isDirectory());

        //public boolean isFile() 判断该File对象指向是否是一个文件
        System.out.println(file.isFile());

        //public boolean exists() 判断该File对象指向目标是否存在
        System.out.println(file.exists());

        //public boolean canRead() 判断该File对象指向目标是否可读
        System.out.println(file.canRead());

        //public boolean canWrite() 判断该File对象指向目标是否可写
        System.out.println(file.canWrite());

        // public boolean isHidden() 判断该File对象指向目标是否被隐藏
        System.out.println(file.isHidden());


    }
}
5、基本获取功能
5.1、概述
public String getAbsolutePath()
public String getPath()
public String getName()
public long length()
public long lastModified()
5.2、案例
public class FileDemo4 {
    public static void main(String[] args) {
        File file = new File("src/com/shujia/day14/尚平.txt");

        //public String getAbsolutePath() 获取目标的绝对路径
//        System.out.println(file.getAbsolutePath()); // E:\projects\IDEAProjects\bigdata29-java\src\com\shujia\day14\尚平.txt

        //public String getPath() 获取相对路径
//        System.out.println(file.getPath()); // src\com\shujia\day14\尚平.txt

        // public String getName() 获取目标的名字
//        System.out.println(file.getName()); // 尚平.txt

        //public long length() 获取目标存储的字节数
//        System.out.println(file.length()); // 57

        //public long lastModified() 获取最后的修改时间 返回的是时间戳
//        System.out.println(file.lastModified()); //1709885930741

        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(file.lastModified())));


    }
}
6、高级获取功能
6.1概述
public String[] list() 返回当前目录下所有文件和文件夹名字的数组
public File[] listFiles() 返回当前目录下所有文件和文件夹对应的File对象数组
6.2、案例
    需求:获取E盘下所有java后缀的文件的名字
public class FileDemo5 {
    public static void main(String[] args) {
        //先将E盘路径封装成File对象
        File file = new File("E:\\");

//        String[] list1 = file.list();
//        System.out.println(Arrays.toString(list1));

//        File[] files = file.listFiles();
//        System.out.println(Arrays.toString(files));
        String[] list1 = file.list();
        if (list1 != null) {
            for (String s : list1) {
                if (s.endsWith(".java")) {
                    System.out.println(s);
                }
            }
        }

    }
}
7、文件名称过滤器
7.1、概述
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)
7.2、案例
public class FileDemo6 {
    public static void main(String[] args) {
        File file = new File("E:\\");

        String[] list1 = file.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
//                return false;
//                return true;
                //这里返回的true还是false,根据条件来处理
                // dir -- E:\\
                // name -- 遍历到每一个文件或者文件夹的名字
                //public File(File parent,String child)
                //构建当前文件或者文件夹的File对象
                File file1 = new File(dir, name);
//                if(file1.isFile() && file1.getName().endsWith(".java")){
//                    return true;
//                }else {
//                    return false;
//                }
                return file1.isFile() && file1.getName().endsWith(".java");
            }
        });

//        String[] list1 = file.list((dir, name) -> {
//            File file1 = new File(dir, name);
//            return file1.isFile() && file1.getName().endsWith(".java");
//        });

        System.out.println(Arrays.toString(list1));
    }
}
递归的思想
1、概述
方法定义中调用方法本身的现象
2、递归的前提
递归必备前提:
    1、进入递归的入口
    2、方法要有返回值
    3、结束递归的出口(如果没有出口就是死递归)
提示:debug可以程序运行的每一步
3、案例
public class DiGuiDemo1 {
    public static void main(String[] args) {
        System.out.println(jieCheng(5));

        for (int i = 1; i < 10; i++) {
            System.out.println(i);
        }
    }

//    public static void fun1(){
//        fun1();
//    }

    // 5! = 5*4*3*2*1;
    public static int jieCheng(int number) {
        //结束条件
        if (number == 1) {
            return 1;
        } else {
            return number * jieCheng(--number);
        }

//        return number * jieCheng(--number);// OOM (out of memory)
    }
}
4、递归的练习
4.1、递归遍历目录下指定后缀名结尾的文件名称
package com.shujia.day15;

import java.io.File;

/*
    递归遍历E:\\目录下指定后缀名结尾的文件名称
    递归方法的前提:
        1、要有入口
        2、方法要有返回值(看情况)
        3、要有出口

   分析:
    1、将E盘封装成File对象
    2、获取当前目录下所有的文件或者文件夹组成的File对象数组
    3、挨个遍历每一个File对象
        1)如果是一个文件,且是以.java后缀的,那就输出
        2)如果是一个文件夹,回到第2步

 */
public class DiGuiTest1 {
    public static void main(String[] args) {
        File file = new File("E:\\");

        show(file);

    }

    public static void show(File file){
        //获取当前目录下所有的文件或者文件夹组成的File对象数组
        File[] files = file.listFiles();

        //挨个遍历每一个File对象
        if(files!=null){
            for (File file1 : files) {
                if(file1.isFile() && file1.getName().endsWith(".java")){
                    System.out.println(file1.getName());
                }else {
                    show(file1);
                }
            }
        }


    }

}
4.2、递归删除带内容的目录
package com.shujia.day15;

import java.io.File;

/*
    递归删除带内容的目录 E:\\data

    分析:
        1、将E:\\data封装成File对象
        2、获取File对象下面所有文件和文件夹组成的File对象数组
        3、遍历File对象数组,判断是否是文件或者文件夹
            1)如果是文件,就直接delete
            2)如果是文件夹,继续回到第2步

 */
public class DiGuiTest2 {
    public static void main(String[] args) {
        File file = new File("E:\\data");
        deleteFile(file);
    }

    public static void deleteFile(File file) {
        File[] files = file.listFiles();
        if (files != null) {
            for (File file1 : files) {
                if(file1.isFile()){
                    file1.delete();
                }else {
                    deleteFile(file1);
                }
            }
        }
        file.delete();
    }
}
IO流
1.概述
IO流用来处理设备之间的数据传输(上传文件和下载文件)
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
2、分类
根据流向划分:以java程序为中心
    输入流:外界数据 ---> java程序
    输出流:java程序 ---> 外界存储中
按照种类:
    字节流(万能流):计算机中存储使用的都是字节
        字节输入流:InputStream
        字节输出流:OutputStream
    字符流(转换流)= 字节流 + 编码表
        字符输入流:Reader
        字符输出流:Writer
3、构造方法
字节输出流:
    OutputStream(抽象父类)-- FileOutputStream(实现子类)

构造方法:
    FileOutputStream(File file)
    FileOutputStream(String name)
需求:使用FileOutputStream向a.txt中写一句话"hello world"
成员方法:
    public void write(int b)
    public void write(byte[] b)
    public void write(byte[] b,int off,int len)
字节输入流:InputStream(抽象父类)--FileInputStream(实现子类)
    FileInputStream(File file)
    FileInputStream(String name)
 成员方法:
        public int read()
        public int read(byte[] b)
3.1、字节输出流
public class IODemo1 {
    public static void main(String[] args) throws Exception {
        //FileOutputStream(File file)
        //src/com/shujia/day15/a.txt
//        File file = new File("src/com/shujia/day15/a.txt");
//
//        //创建字节输出流对象, 目标文件如果不存在,将会自动创建
//        FileOutputStream fos = new FileOutputStream(file);
//        System.out.println(fos);

        //FileOutputStream(String name), 目标文件如果不存在,将会自动创建
        FileOutputStream fos = new FileOutputStream("src/com/shujia/day15/a.txt");

        //public void write(int b)
//        fos.write(97);

        //public void write(byte[] b) 写一个字节数组
        byte[] arr = {97, 98, 99, 100, 101};
//        fos.write(arr);

        //public void write(byte[] b,int index,int length) 写字节数组的一部分
        fos.write(arr,1,3);
        //释放资源
        fos.close();
//        fos.write(arr); // java.io.IOException: Stream Closed

    }

    
    /*
    字节输出流写数据
    1、实现追加写
    2、实现换行写
 */
public class FileOutputStreamDemo1 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            //创建字节输出流对象
            fos = new FileOutputStream("src/com/shujia/day15/a1.txt", true);
            //向文件中写一些数据
            fos.write("\r\n".getBytes()); //手动写一个换行符
            fos.write("poiuyt".getBytes());


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
3.2、字节输入流
public class FileInputStreamDemo1 {
    public static void main(String[] args) throws Exception{
        //创建字节输入流对象 读数据必须要求目标文件要提前存在
        FileInputStream fis = new FileInputStream("src/com/shujia/day15/a1.txt");

        //public int read() 每调用一次该方法,就会读取一个字节出来
//        System.out.println((char) fis.read());
//        System.out.println((char) fis.read());
//        System.out.println((char) fis.read());
//        System.out.println((char) fis.read());
//        System.out.println((char) fis.read());
//        System.out.println(fis.read());
//        System.out.println(fis.read());
        //使用while循环进行读取
        //每read()一次,光标就会向后移动一个字节单位
//        int i = 0;
//        while ((i=fis.read())!=-1){
//            System.out.println((char) i);
//        }


        //public int read(byte[] b)
        //先创建一个米勺为了舀米粒(空的字节数组为了读取字节)
//        byte[] bytes = new byte[2];
//        int length = fis.read(bytes); //返回值表示读到了几个字节
//        System.out.println(length);
//        //真正读取到的字节存储在字节数组中
//        //将字节数组转字符串
//        String s = new String(bytes,0,length);
//        System.out.println(s);
//
//
//        int length2 = fis.read(bytes); //返回值表示读到了几个字节
//        System.out.println(length2);
//        //真正读取到的字节存储在字节数组中
//        //将字节数组转字符串
//        String s2 = new String(bytes,0,length2);
//        System.out.println(s2);
//
//        int length3 = fis.read(bytes); //返回值表示读到了几个字节
//        System.out.println(length3);
//        //真正读取到的字节存储在字节数组中
//        //将字节数组转字符串
//        String s3 = new String(bytes,0,length3);
//        System.out.println(s3);
        byte[] bytes = new byte[2];
        int length = 0;
        while ((length=fis.read(bytes))!=-1){
            String s2 = new String(bytes,0,length);
            System.out.print(s2);
        }


        //释放资源
        fis.close();
    }
}
4、字节缓冲流
OutputStream
    - FileOutputStream 普通字节输出流
    - BufferedOutputStream 字节缓冲输出流

InputStream
    - FileInputStream 普通字节输入流
    - BufferedInputStream 字节缓冲输入流
4.1、BufferedInputStream 字节缓冲输入流
字节缓冲流:BufferedInputStream
    构造方法:
        BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
public class BufferedInputStreamDemo1 {
    public static void main(String[] args) throws Exception{
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src/com/shujia/day15/a2.txt"));

        //一次读取一个字节
//        int i = 0;
//        while ((i=bis.read())!=-1){
//            System.out.print((char) i);
//        }

        //一次读取一个字节数组
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length= bis.read(bytes))!=-1){
            String s2 = new String(bytes, 0, length);
//            String s = new String(bytes, 0, length);
            System.out.println(s2);
        }

        bis.close();

    }
}
4.2、BufferedOutputStream字节缓冲输出流
 字节缓冲流:BufferedOutputStream
    构造方法:
        BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
 */
public class BufferedOutputStreamDemo1 {
    public static void main(String[] args) throws Exception{
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/com/shujia/day15/a2.txt",true));

        //建议,每写一次数据,刷新一次
        bos.write("abcdef".getBytes());
        bos.flush();

        bos.close(); // 包含了flush()

    }
}
5、字节流复制数据
5.1、案例1-图片复制
/*
    复制文件,将E:\哈希表.png 复制到D:\others
    数据源:
        输入流 - 字节输入流 - InputStream - FileInputStream

    目的地:
        输出流 - 字节输出流 - OutputStream - FileOutputStream

 */
public class CopyFileDemo1 {
    public static void main(String[] args) throws Exception {
        //创建字节输入流对象
        FileInputStream fis = new FileInputStream("E:\\哈希表.png");
        //创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("D:\\others\\哈希表3.png");

        //方式1:一次读写一个字节
//        int i = 0;
//        while ((i=fis.read())!=-1){
//            fos.write(i);
//        }


        //方式2:一次读写一个字节数组
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, length);
        }


        //释放资源
        fos.close();
        fis.close();
    }
}
5.2、案例2-音频复制
/*
    将E:\video29\29期day36 递归 5.mp4 复制到D:\递归.mp4


 */
public class CopyFileDemo2 {
    public static void main(String[] args) throws Exception {
        //创建字节输入流对象
        FileInputStream fis = new FileInputStream("E:\\video29\\29期day36 递归 5.mp4");
        //创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("D:\\递归.mp4");

        //方式1:一次读写一个字节
//        int i = 0;
//        while ((i=fis.read())!=-1){
//            fos.write(i);
//        }


        //方式2:一次读写一个字节数组
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, length);
        }


        //释放资源
        fos.close();
        fis.close();
    }
}
5.3、普通字节(输入、输出)流和字节缓冲(输入、输出)流的比较
/*
    比较普通字节流和字节缓冲流复制视频的速度E:\video29\29期day36 递归 5.mp4

    方式1:普通字节流一次读写一个字节
    方式2:字节缓冲流一次读写一个字节

    方式3:普通字节流一次读写一个字节数组
    方式4:字节缓冲流一次读写一个字节数组

 */
public class CopyFileDemo3 {
    public static void main(String[] args) throws Exception {
        //方式1:普通字节流一次读写一个字节
//        fun1(); // >1min
        //方式2:字节缓冲流一次读写一个字节
//        fun2(); // 3963 毫秒

        //方式3:普通字节流一次读写一个字节数组
//        fun3(); //总耗时:508 毫秒
        //方式4:字节缓冲流一次读写一个字节数组
        fun4(); // 总耗时:381 毫秒

    }

    public static void fun4() throws Exception{
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\video29\\29期day36 递归 5.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\递归4.mp4"));

        byte[] bytes = new byte[1024];
        int length = 0;
        long start = System.currentTimeMillis();
        while ((length = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, length);
            bos.flush();
        }

        long end = System.currentTimeMillis();
        System.out.println("总耗时:" + (end - start) + " 毫秒");


        bos.close();
        bis.close();
    }

    public static void fun3() throws Exception {
        FileInputStream fis = new FileInputStream("E:\\video29\\29期day36 递归 5.mp4");
        FileOutputStream fos = new FileOutputStream("D:\\递归3.mp4");


        byte[] bytes = new byte[1024];
        int length = 0;
        long start = System.currentTimeMillis();
        while ((length = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, length);
        }

        long end = System.currentTimeMillis();
        System.out.println("总耗时:" + (end - start) + " 毫秒");


        fos.close();
        fis.close();
    }


    public static void fun2() throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\video29\\29期day36 递归 5.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\递归2.mp4"));

        int i = 0;

        long start = System.currentTimeMillis();
        while ((i = bis.read()) != -1) {
            bos.write(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("总耗时:" + (end - start) + " 毫秒");


        bos.close();
        bis.close();
    }


    public static void fun1() throws Exception {
        FileInputStream fis = new FileInputStream("E:\\video29\\29期day36 递归 5.mp4");
        FileOutputStream fos = new FileOutputStream("D:\\递归1.mp4");

        int i = 0;

        long start = System.currentTimeMillis();
        while ((i = fis.read()) != -1) {
            fos.write(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("总耗时:" + (end - start) + " 毫秒");


        fos.close();
        fis.close();
    }
}
6、字符流
6.1、概述
字符流:= 字节流+编码表
    字符输出流
        Writer(抽象类) - OutputStreamWriter(子类)
    字符输入流
        Reader(抽象类) - InputStreamReader(子类)
6.2、构造方法和成员方法
 OutputStreamWriter 字符输出流
        构造方法:
            public OutputStreamWriter(OutputStream out)
            public OutputStreamWriter(OutputStream out,String charsetName)
        成员方法:
            public void write(int c)
            public void write(char[] cbuf)
            public void write(char[] cbuf,int off,int len)
            public void write(String str)
            public void write(String str,int off,int len)


    InputStreamReader 字符输入流
        构造方法:
            public InputStreamReader(InputStream in)
            public InputStreamReader(InputStream in,String charsetName)
        成员方法:
            public int read()
            public int read(char[] cbuf)

什么情况使用字节流或者字符流:
    一个文件如果你使用自带记事本能打开且能看懂的话,无脑用字符流。否则用字节流。
    优先使用字节流(万能流)
public class InputStreamReaderDemo2 {
    public static void main(String[] args) throws Exception {
        //创建字符输入流对象
//        InputStreamReader isr = new InputStreamReader(new FileInputStream("src/com/shujia/day15/b2.txt"));
        //public InputStreamReader(InputStream in,String charsetName)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("src/com/shujia/day15/b2.txt"),"UTF-16BE");

        //public int read() 一次读取一个字符
//        System.out.println((char) isr.read());
//        int i = 0;
//        while ((i=isr.read())!=-1){
//            System.out.print((char) i);
//        }

        //public int read(char[] cbuf)
        char[] chars = new char[1024];
        int length = 0;
        while ((length=isr.read(chars))!=-1){
            String s = new String(chars, 0, length);
            System.out.println(s);
        }




        //释放资源
        isr.close();

    }
}
public class OutputStreamWriterDemo1 {
    public static void main(String[] args) throws Exception{
        // public OutputStreamWriter(OutputStream out)
//        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src/com/shujia/day15/b2.txt",true));
        //public OutputStreamWriter(OutputStream out,String charsetName)
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src/com/shujia/day15/b2.txt"),"UTF-16BE");

//        public void write(int c) 一次写一个字符
//        osw.write(97);
//        osw.flush();

//        public void write(char[] cbuf) 一次写一个字符数组
//        char[] chars = {'我','爱','中','华'};
//        osw.write(chars);

//        public void write(char[] cbuf,int off,int len)一次写一个字符数组一部分
//        osw.write(chars,2,2);


//        public void write(String str) 一次写一个字符串
        osw.write("尚平这次真的很帅");


//        public void write(String str,int off,int len) 一次写一个字符串的一部分
//        osw.write("尚平这次真的很帅",2,2);



        osw.flush();
        osw.close();

    }
}
6.3、字符流的简写
 OutputStreamWriter 字符输出流
        构造方法:
            public OutputStreamWriter(OutputStream out)
    简化写法:
        OutputStreamWriter - FileWriter(子类)

    InputStreamReader 字符输入流
        构造方法:
            public InputStreamReader(InputStream in)
    简化写法:
        InputStreamReader - FileReader(子类)
public class FileReaderDemo1 {
    public static void main(String[] args) throws Exception {
//        OutputStreamWriter osw = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream("src/com/shujia/day15/b2.txt")));
        FileWriter fw = new FileWriter("src/com/shujia/day15/b2.txt");
        //等同于 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src/com/shujia/day15/b2.txt"))
        fw.write("你好世界");
        fw.flush();


//        InputStreamReader isr = new InputStreamReader(new FileInputStream("src/com/shujia/day15/b2.txt"));
        FileReader fr = new FileReader("src/com/shujia/day15/b2.txt");
//        int i = 0;
//        while ((i = fr.read()) != -1) {
//            System.out.print((char) i);
//        }

        char[] chars = new char[1024];
        int length = 0;
        while ((length=fr.read(chars))!=-1){
            String s = new String(chars, 0, length);
            System.out.println(s);
        }


        fw.close();

    }
}
7、编码的加密和解密
package com.shujia.day15;

import java.util.Arrays;

public class BianMaDemo {
    public static void main(String[] args) throws Exception{
        String s = "今天晚上8点行动";

        //加密 String-->字节数组
//        byte[] bytes = s.getBytes();
        byte[] bytes = s.getBytes("Unicode");
        System.out.println(Arrays.toString(bytes)+"--"+bytes.length);

        //解密 字节数组-->字符串
        String s1 = new String(bytes,"Unicode");
        System.out.println(s1);

    }
}
8、字符缓冲流
8.1、概述
 字符缓冲输出流
            Writer
                - OutputStreamWriter
                       - FileWriter
                - BufferedWriter
                    特有方法:newLine()//自动换行
        字符缓冲输入流
            Reader
                - InputStreamReader
                    - FileReader
                - BufferedReader
                    特有方法:readLine()//一次只读取一行


    字符缓冲输入流:
        BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。

 */
8.2、字符缓冲输入流案例
public class BufferedReaderDemo1 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src/com/shujia/day16/a1.txt")));

        //一次读取一个字符
//        int i = 0;
//        while ((i = br.read()) != -1) {
//            System.out.print((char) i);
//        }

        //一次读取一个字符数组
//        char[] chars = new char[1024];
//        int length = 0;
//        while ((length = br.read(chars)) != -1) {
//            String s = new String(chars, 0, length);
//            System.out.print(s);
//        }

        //一次读取一整行
        //public String readLine() throws IOException读一行文字。
//        String s = br.readLine();
//        System.out.println(s);
//
//        String s2 = br.readLine();
//        System.out.println(s2);
        //使用while循环改进 如果已达到流的末尾,则为null
        String s = null;
        while ((s = br.readLine())!=null){
            System.out.println(s);
        }


        //释放资源
        br.close();
    }
}
8.3、字符缓冲输出流
public class BufferedWriterDemo1 {
    public static void main(String[] args) throws Exception{
        //BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
//        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("src/com/shujia/day16/a1.txt")));
        BufferedWriter bw = new BufferedWriter(new FileWriter("src/com/shujia/day16/a1.txt"));
        bw.write("今天的天气还可以");
        bw.flush();
//        bw.write("\r\n"); //根据系统的换行符是什么,就应该写什么换行符
        //public void newLine() throws IOException写一行行分隔符。 自动根据运行的系统生成一个换行符
        bw.newLine();

        bw.write("我们去春游吧");
        bw.flush();


        //释放资源
        bw.close();
    }
}
8.4、使用字符缓冲流实现文件的复制
public class CopyFileDemo {
    public static void main(String[] args) throws Exception {
        //创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("src/com/shujia/day16/a1.txt"));
        //创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("src/com/shujia/day16/a4.txt"));

        //1、一次读写一个字符
//        int i = 0;
//        while ((i=br.read())!=-1){
//            bw.write(i);
//            bw.flush();
//        }

        //2、一次读写一个字符数组
//        char[] chars = new char[1024];
//        int length = 0;
//        while ((length = br.read(chars)) != -1) {
//            bw.write(chars, 0, length);
//            bw.flush();
//        }

        //3、一次读写一行
        //readLine()不会读取换行符
        String line = null;
        while ((line = br.readLine())!=null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }


        //释放资源
        bw.close();
        br.close();
    }
}
9、IO流练习
9.1、把ArrayList集合中的字符串数据存储到文本文件
public class IOTest1 {
    public static void main(String[] args) throws Exception{
        //字符缓冲输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("src/com/shujia/day16/b1.txt"));

        ArrayList<Integer> arrayList = new ArrayList<>();

        arrayList.add(12);
        arrayList.add(32);
        arrayList.add(41);
        arrayList.add(41);
        arrayList.add(1);

        for (Integer i : arrayList) {
            bw.write(""+i);
            bw.newLine();
            bw.flush();
        }

        //释放资源
        bw.close();
    }
}
9.2、键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件

学生类

package com.shujia.day16;

public class Student {
    //姓名,语文成绩,数学成绩,英语成绩
    private String name;
    private int chinese;
    private int math;
    private int english;

    public Student() {
    }

    public Student(String name, int chinese, int math, int english) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }

    public String getName() {
        return name;
    }

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

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getEnglish() {
        return english;
    }

    public void setEnglish(int english) {
        this.english = english;
    }

    public int getSumScore() {
        return chinese + math + english;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", chinese=" + chinese +
                ", math=" + math +
                ", english=" + english +
                '}';
    }
}

创建IOTest2

public class IOTest2 {
    public static void main(String[] args) {
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);


        //创建TreeSet集合对象
        TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //按照总分从高到低输出到控制台
//        return 0;
                int i1 = o2.getSumScore() - o1.getSumScore();
                //总分一样,语文成绩不一定一样,
                int i2 = (i1 == 0) ? o2.getChinese() - o1.getChinese() : i1;
                //总分一样,语文成绩一样,数学成绩不一定一样
                int i3 = (i2 == 0) ? o2.getMath() - o1.getMath() : i2;
                //分数都一样,姓名不一定一样
                return (i3 == 0) ? o2.getName().compareTo(o1.getName()) : i3;
            }
        });

        Student s;
        //创建元素对象
        for (int i = 1; i <= 5; i++) {
            System.out.println("---------请输入第 " + i + " 个学生的信息---------");
            System.out.print("姓名: ");
            String name = sc.next();
            System.out.print("语文成绩: ");
            int chinese = sc.nextInt();
            System.out.print("数学成绩: ");
            int math = sc.nextInt();
            System.out.print("英语成绩: ");
            int english = sc.nextInt();
            //创建一个学生对象
            s = new Student(name, chinese, math, english);
            //添加到集合中
            treeSet.add(s);
        }

        //遍历集合,将数据写入到文件中
        //创建字符缓冲输出流对象
        BufferedWriter bw = null;
        try{
            bw = new BufferedWriter(new FileWriter("src/com/shujia/day16/stu.csv"));
            bw.write("排名,姓名,语文成绩,数学成绩,英语成绩,总分");
            bw.newLine();
            bw.flush();
            int i = 1;
            for (Student student : treeSet) {
                bw.write(i+","+student.getName()+","+student.getChinese()+","+student.getMath()+","+student.getEnglish()+","+student.getSumScore());
                bw.newLine();
                bw.flush();
                i++;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(bw!=null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
10、序列化流
10.1、概述
    对象流(序列化流)
        序列化(对象输出流):将java数据对象像流一样在网络进行传输
        ObjectOutputStream
            - public final void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。

        反序列化(对象输入流):将网络中传输的流转化一个java数据对象
        ObjectInputStream
            - public final Object readObject() 读取一个对象。
10.2、注意事项
NotSerializableException: com.shujia.day16.Student2 不能进行序列化
    需要元素的类实现Serializable标记接口

    我们正常按照序列化写,反序列化读的时候是没问题的,但是呢,我们修改了类中的东西,再读一次之后就报错
    InvalidClassException:
        com.shujia.day16.Student2;
        local class incompatible:
            stream class desc serialVersionUID = -7932280709648663762,
            local class serialVersionUID = -6203786557949116328
    解决方案:serialVersionUID永远不变
    如果不想让某一个成员做序列化的话,可以再成员的前面加上transient关键字修饰
10.3、序列化案例

创建学生2类

package com.shujia.day16;

import java.io.Serializable;

// alt+回车
public class Student2 implements Serializable {
    private static final long serialVersionUID = -7932280709648663762L;
    private String name;
    transient int age;

    public Student2() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

创建ObjectOutputStreamDemo1类

public class ObjectOutputStreamDemo1 {
    public static void main(String[] args) throws Exception{
//        write();
        read();




    }
    public static void read() throws Exception{
        //创建反序列化对象
        //ObjectInputStream(InputStream in)
        //创建从指定的InputStream读取的ObjectInputStream。
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/com/shujia/day16/obj.txt"));

        //public final Object readObject() 读取一个对象
        Object o = ois.readObject(); //Object o = new Student2("尚平", 18);
        System.out.println(o);


        //释放资源
        ois.close();
    }

    public static void write() throws Exception{
        //创建一个学生对象
        Student2 s1 = new Student2("尚平", 18);


        //创建序列化流对象
        //ObjectOutputStream(OutputStream out)
        //创建一个写入指定的OutputStream的ObjectOutputStream。
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/com/shujia/day16/obj.txt"));

        //public final void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
        oos.writeObject(s1);
        oos.flush();


        //释放资源
        oos.close();
    }
}

第七章 多线程

多线程
1、概述
进程和线程的区别:
    进程:是操作系统给该应该程序一个独立计算资源
    线程:是包含在进程中的单位。若一个进程中有多个线程,这个程序叫做多线程程序。
2、多线程的创建
java程序启动的时候,是多线程方式启动的,会有两个线程:主线程(main),垃圾回收线程

一个java应用程序,被看作是一个进程,进程中可以包含多个线程。
java提供了一个类专门描述线程的,Thread

创建线程的方式1:
    创建自定义类继承Thread类,重写其中的run方法
    步骤:
        1、创建线程类(Thread类子类),重写run方法
        2、创建该线程类的对象(可以创建若干个)
        3start()启动线程(启动若干个)【a.先创建一个线程 b.由着个线程调用该线程对象中的run方法】
3、注意事项
1Thread类线程对象启动的时候,必须使用通过start方法启动的,如果直接调用run方法执行的话,可以普通的对象调用方法没区别,依旧是单线程程序
2、线程对象调用了start()方法并不是立刻执行,只是为了让这个线程对象具备执行CPU资格
4、多线程的功能
4.1、如何获取和设置多线程的名称
  • 概述

    • 如何获取一个线程的名字,可以观察到当前是哪一个线程在执行
          public final String getName() 返回此线程的名称。
      如何给一个线程设置名字
          方式1public final void setName(String name)将此线程的名称更改为等于参数name 。
          方式2:使用Thread类的有参构造方法Thread(String name) 分配一个新的 Thread对象。
      
  • 举例

    • //1、创建线程类(Thread类子类),重写run方法
      class MyThread1 extends Thread {
          /*
              MyThread1(){
                  super();
              }
           */
      
          MyThread1(String name){
              super(name);
          }
      
          @Override
          public void run() {
              //在run方法中编写线程要做的事情。
              for (int i = 1; i <= 200; i++) {
                  System.out.println(getName()+"--"+i);
              }
          }
      }
      
      public class ThreadDemo1 {
          public static void main(String[] args) {
              //线程1
              //线程2
              //。。。
              //2、创建该线程类的对象(可以创建若干个)
              MyThread1 t1 = new MyThread1("尚平"); // Thread-0
      //        t1.setName("尚平");
      //        thread1.run();
              t1.setPriority(10);
              System.out.println(t1.getPriority());
              t1.start();
              MyThread1 t2 = new MyThread1("丁义杰"); // Thread-1
      //        t2.setName("丁义杰");
      //        thread2.run();
              t2.setPriority(1);
              System.out.println(t2.getPriority());
              t2.start();
          }
      }
      
4.2、线程的调度
  • 概述

    • Java线程是抢占式调度的,每一个线程都有自己的优先级,如何查看线程的优先级以及设置优先级?
          public final int getPriority() 返回此线程的优先级。默认的优先级都是5
          public final void setPriority(int newPriority)更改此线程的优先级。 [1-10]
      
  • 举例

    • //1、创建线程类(Thread类子类),重写run方法
      class MyThread1 extends Thread {
          /*
              MyThread1(){
                  super();
              }
           */
      
          MyThread1(String name){
              super(name);
          }
      
          @Override
          public void run() {
              //在run方法中编写线程要做的事情。
              for (int i = 1; i <= 200; i++) {
                  System.out.println(getName()+"--"+i);
              }
          }
      }
      
      public class ThreadDemo1 {
          public static void main(String[] args) {
              //线程1
              //线程2
              //。。。
              //2、创建该线程类的对象(可以创建若干个)
              MyThread1 t1 = new MyThread1("尚平"); // Thread-0
      //        t1.setName("尚平");
      //        thread1.run();
              t1.setPriority(10);
              System.out.println(t1.getPriority());
              t1.start();
              MyThread1 t2 = new MyThread1("丁义杰"); // Thread-1
      //        t2.setName("丁义杰");
      //        thread2.run();
              t2.setPriority(1);
              System.out.println(t2.getPriority());
              t2.start();
          }
      }
      
4.3、线程的控制
  • 概述

    • 线程休眠
      public static void sleep(long millis)
      线程加入
      public final void join()
      线程礼让
      public static void yield()
      后台线程
      public final void setDaemon(boolean on)
      中断线程
      public final void stop()
      public void interrupt()

  • 举例

    • 线程的休眠

      public class MySleepThread extends Thread {
          @Override
          public void run() {
              for (int i = 1; i <= 200; i++) {
                  //休眠线程
                  //public static void sleep(long millis)
                  //当一个线程执行过程中遇到了Thread类调用sleep方法的话,该线程就会发生阻塞
                  //当倒计时结束后,继续抢CPU执行权,抢到后,继续执行run方法的后续代码
                  try {
                      Thread.sleep(500);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
      
                  System.out.println(getName()+"--"+i);
              }
          }
      }
      class Test1{
          public static void main(String[] args) {
              //创建3个线程对象
              MySleepThread t1 = new MySleepThread();
              MySleepThread t2 = new MySleepThread();
              MySleepThread t3 = new MySleepThread();
      
              t1.setName("张三");
              t2.setName("李四");
              t3.setName("王五");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      
    • 线程的加入

      public class MyJoinThread extends Thread{
      @Override
      public void run() {
        for (int i = 1; i <= 200; i++) {
            System.out.println(getName()+"--"+i);
        }
      }
      }
      class Test2{
      public static void main(String[] args) throws Exception{
        MyJoinThread t1 = new MyJoinThread();
        t1.setName("张三");
      
        MyJoinThread t2 = new MyJoinThread();
        t2.setName("李四");
        MyJoinThread t3 = new MyJoinThread();
        t3.setName("王五");
      
        t1.start();
        /*
            加入线程,在start()方法之后调用,表示其他的线程要等待该线程执行完,才能执行
            如果没有放在该对象的start方法之后,仍然会发生交替执行
         */
        t1.join();
        t2.start();
        t3.start();
      }
      }
      

  • 线程礼让

    public class MyYieldThread extends Thread{
     @Override
     public void run() {
         for (int i = 1; i <= 200; i++) {
             System.out.println(getName()+"--"+i);
             /*
                 礼让线程
                 public static void yield()
              */
             Thread.yield(); //只是单纯的让程序看起来好像和谐了一点,并不是线程之间交替执行
         }
     }
    }
    
    class Test3{
     public static void main(String[] args) {
         MyYieldThread t1 = new MyYieldThread();
         MyYieldThread t2 = new MyYieldThread();
         MyYieldThread t3 = new MyYieldThread();
         t1.setName("张三");
         t2.setName("李四");
         t3.setName("王五");
    
         t1.start();
         t2.start();
         t3.start();
     }
    }
    

  • 后台线程

    public class MyDaemonThread extends Thread{
     @Override
     public void run() {
         for (int i = 1; i <= 200; i++) {
             System.out.println(getName()+"--"+i);
         }
     }
    }
    
    class Test4{
     public static void main(String[] args) {
         MyDaemonThread t1 = new MyDaemonThread();
         MyDaemonThread t2 = new MyDaemonThread();
         MyDaemonThread t3 = new MyDaemonThread();
    
         t1.setName("刘备");
         t2.setName("张飞");
         t3.setName("关羽");
         /*
             后台线程,又称之为守护线程
             java中所有的线程分为两类:
                 1、用户线程
                 2、后台线程(守护线程)
             守护线程是依赖于用户线程存在的,当一个进程中没有用户线程的话,就不会存在后台线程了。
             将一个普通的用户线程设置为后台线程
             public final void setDaemon(boolean on)将此线程标记为daemon线程或用户线程。
          */
         t2.setDaemon(true);
         t3.setDaemon(true);
    
         t1.start();
         t2.start();
         t3.start();
     }
    }
    

  • 中断线程

    public class MyStopThread extends Thread {
        @Override
        public void run() {
            System.out.println("我睡觉啦...要睡10秒种");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("我醒了...继续工作");
            for (int i = 1; i < 11; i++) {
                System.out.println("hello world");
            }
    
        }
    }
    
    class Test5{
        public static void main(String[] args) {
            MyStopThread t1 = new MyStopThread();
            t1.start(); // 10s
    
            try {
                Thread.sleep(5000); // 5s
    //            t1.stop();会使线程直接中断,后面不能再运行
                t1.interrupt();//仅仅是通知线程,线程有机会执行一些后续操作,同时也可以无视这个通知
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
    
        }
    }
    
5、实现多线程的方式2
5.1、概述
创建线程对象的第二种方式:实现Runnable接口,重写run方法
    步骤
        1:自定义一个类A实现Runnable接口,重写run方法
        2:创建类A的对象
        3:借助Thread类,创建线程对象
        4:调用start()方法
5.2、举例
public class MyRunnable1 implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 200; i++) {
            //间接地使用Thread类中的public static Thread currentThread()
            //获取当前的线程对象
            //调用getName()
            System.out.println(Thread.currentThread().getName()+": "+i);
        }
    }
}

class Test6{
    public static void main(String[] args) {
        MyRunnable1 myRunnable1 = new MyRunnable1();

        //Thread(Runnable target) 分配一个新的 Thread对象。
//        Thread t1 = new Thread(myRunnable1);
//        Thread t2 = new Thread(myRunnable1);


        //Thread(Runnable target, String name)
        //分配一个新的 Thread对象。
        Thread t1 = new Thread(myRunnable1,"张三");
        Thread t2 = new Thread(myRunnable1,"李四");

        //启动线程
        t1.start();
        t2.start();
    }
}
6、多线程的生命周期

image-20240313112918088

7、关于电影卖票程序的思考
7.1、问题1
问题1:我们正常按照实现线程第二种方式写这题的时候,运行时发现,每一个窗口的票都是从100开始的
    解决方案:将原先写在run方法中的100张票,写在成员变量的位置上
  • 程序代码:

    • public class Window implements Runnable {
          int tickets = 100; // 1
          @Override
          public void run() {
              while (true) {
                  //t1 t2 t3
                  if (tickets > 0) {
                      try {
                          //t1 t2 t3
                          Thread.sleep(50);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
                      //当前窗口1正在出售第 22张票
                      //当前窗口2正在出售第 22张票
                      //...
                      //当前窗口1正在出售第 1张票
                      //当前窗口2正在出售第 0张票
                      //当前窗口3正在出售第 -1张票
      
                  }
              }
          }
      }
      
      class SellTicketsDemo1{
          public static void main(String[] args) {
              Window window = new Window();
      
              //创建3个线程窗口对象
              Thread t1 = new Thread(window, "窗口1");
              Thread t2 = new Thread(window, "窗口2");
              Thread t3 = new Thread(window, "窗口3");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      
7.2、问题2(synchronized同步代码块解决线程不安全)
  • 概述1

    • 问题2:为了模拟现实生活中的卖票,我们适当加入一些延迟,但是遇到了两个很严重的现象
          现象1:出现了不同的窗口售卖同一张票的情况
              当前窗口1正在出售第 22张票
              当前窗口2正在出售第 22张票
      
          现象2:出现了不合理的票数
      
  • 概述2

    • 如何判断一个程序中是否存在线程安全的问题?(三要素缺一不可)
          1、是否存在多线程环境? 是  三个线程对象
          2、是否存在共享数据? 是 100张票
          3、是否有多条语句操作着共享数据? 是 tickets做了判断,还做了减一操作
      
  • 解决线程不安全的方案:

    • 方案1synchronized同步代码块
          synchronized(对象){
             操作共享数据的代码;
          }
      
    • 注意事项:

      • 锁对象必须是唯一且共享的

  • 实现代码:

    • class Demo{
      
      }
      
      class Window2 implements Runnable {
          int tickets = 100; // 1
      //    Object obj = new Object();
          Demo d = new Demo();
          @Override
          public void run() {
      //        Demo d = new Demo();
              while (true) {
                  //t1 t2 t3
                  synchronized (d){
                      if (tickets > 0) {
                          try {
                              //t1 t2 t3
                              Thread.sleep(50);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
                          //当前窗口1正在出售第 22张票
                          //当前窗口2正在出售第 22张票
                          //...
                          //当前窗口1正在出售第 1张票
                          //当前窗口2正在出售第 0张票
                          //当前窗口3正在出售第 -1张票
      
                      }
                  }
              }
          }
      }
      
      public class SellTicketsDemo2{
          public static void main(String[] args) {
              Window2 window = new Window2();
      
              //创建3个线程窗口对象
              Thread t1 = new Thread(window, "窗口1");
              Thread t2 = new Thread(window, "窗口2");
              Thread t3 = new Thread(window, "窗口3");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }
      
  • 程序代码的改进

    • 概述

      synchronized写在方法定义上,这个方法就是同步方法,锁对象默认是this
      //    public synchronized void sellTickets() {
      //        while (true) {
      //            if (tickets > 0) {
      //                try {
      //                    Thread.sleep(50);
      //                } catch (InterruptedException e) {
      //                    e.printStackTrace();
      //                }
      //                System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
      //            }
      //        }
      //    }
      
          //将synchronized写在静态方法定义上,这个方法就是同步静态方法,锁对象默认是当前类的Class对象
          public synchronized static void sellTickets() {
              if (tickets > 0) {
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
              }
          }
      }
      
    • 改进后的代码

      package com.shujia.day17;
      
      import java.util.Vector;
      
      class Window3 implements Runnable {
          static int tickets = 100;
          //    Demo d = new Demo();
          int i = 1;
      
          @Override
          public void run() {
              while (true) {
                  if (i % 2 == 0) {
                      synchronized (Window3.class) {
                          if (tickets > 0) {
                              try {
                                  Thread.sleep(50);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
      
                          }
                      }
                  } else {
                      sellTickets();
                  }
                  i++;
              }
          }
      
      
          //将synchronized写在方法定义上,这个方法就是同步方法,锁对象默认是this
      //    public synchronized void sellTickets() {
      //        while (true) {
      //            if (tickets > 0) {
      //                try {
      //                    Thread.sleep(50);
      //                } catch (InterruptedException e) {
      //                    e.printStackTrace();
      //                }
      //                System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
      //            }
      //        }
      //    }
      
          //将synchronized写在静态方法定义上,这个方法就是同步静态方法,锁对象默认是当前类的Class对象
          public synchronized static void sellTickets() {
              if (tickets > 0) {
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
              }
          }
      }
      
      public class SellTicketsDemo3 {
          public static void main(String[] args) {
              Window3 window = new Window3();
      
              //创建3个线程窗口对象
              Thread t1 = new Thread(window, "窗口1");
              Thread t2 = new Thread(window, "窗口2");
              Thread t3 = new Thread(window, "窗口3");
      
              t1.start();
              t2.start();
              t3.start();
      
      
      //        Vector
      
          }
      }
      
7.3、解决线程不安全方案2(lock锁)
  • 概述

    ReentrantLock lock = new ReentrantLock();//获得锁对象
    lock.lock()//获得锁
    lock.unlock()//释放锁
    
  • 程序代码

    import java.util.concurrent.locks.ReentrantLock;
    
    class Window4 implements Runnable {
        int tickets = 100;
        //创建多个线程对象共享的一把锁
        ReentrantLock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while (true) {
                lock.lock(); //获得锁
                if (tickets > 0) {
                    try {
                        //t1 t2 t3
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前" + Thread.currentThread().getName() + "正在出售第 " + (tickets--) + "张票");
                }
                lock.unlock(); //释放锁
            }
        }
    }
    
    public class SellTicketsDemo4 {
        public static void main(String[] args) {
            Window4 window = new Window4();
    
            //创建3个线程窗口对象
            Thread t1 = new Thread(window, "窗口1");
            Thread t2 = new Thread(window, "窗口2");
            Thread t3 = new Thread(window, "窗口3");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
8、死锁
8.1、概述

死锁:线程之间出现了相互等待的现象。

8.2、案例
  • 描述:

    中国人和外国人吃饭的问题
        前提:中国人吃饭必须有两支筷子,外国人吃饭必须要有一把刀和一把叉
        现在的情况:
            中国人 一支筷子,一把刀
            外国人 一支筷子,一把叉
    
  • 程序代码

    class Locks{
        //创建两把锁
        public static final ReentrantLock LOCK_1 = new ReentrantLock();
        public static final ReentrantLock LOCK_2 = new ReentrantLock();
    }
    
    class Person extends Thread{
        private boolean flag;
    
        Person(boolean flag){
            this.flag = flag;
        }
    
        @Override
        public void run() {
            if(flag){
                synchronized (Locks.LOCK_1){
                    System.out.println("if lock1");
                    //t1-1
                    synchronized (Locks.LOCK_2){
                        System.out.println("if lock2");
                    }
                }
            }else {
                synchronized (Locks.LOCK_2){
                    System.out.println("else lock2");
                    //t2-2
                    synchronized (Locks.LOCK_1){
                        System.out.println("else lock1");
                    }
                }
            }
        }
    }
    
    
    public class DieLockDemo {
        public static void main(String[] args) {
            Person p1 = new Person(true);
            Person p2 = new Person(false);
            p1.start();
            p2.start();
        }
    }
    
9、等待唤醒机制
  • 描述

    问题1:我们按照创建线程第二种方式,创建生产者和消费者之后,运行发现,结果是null-0,这个结果是必然且唯一的
        解决方案:在外面创建一个学生对象,以参数的形式传递给生产者和消费者
    问题2:当我们加入循环,让生产者每一次赋值不同的值,让我们观察更加清楚
        但是运行的结果出现了问题。
        我们理想的结果,生产者赋值一次,消费者取值一次。
        现在现象:
            1、同一个数据,重复消费了很多次
            2、出现了错误的数据,姓名和年龄对不上
        出现了线程安全的问题:
            a. 是否存在多线程环境?存在
            b. 是否有共享数据? 有
            c. 是否有多条语句操作着共享数据? 有
        我们加入了线程安全的同步代码块之后,数据错误的现象消失了,但是重复消费依旧还在
        解决方案:需要在线程安全的前提下,加入等待唤醒机制!
        锁对象等待,或者唤醒
    
  • 程序代码

    • 生产者

      public class Producer implements Runnable{
          private Student student;
          int i =0;
          Producer(Student student){
              this.student = student;
          }
      
          @Override
          public void run() {
      //        Student student = new Student();
              while (true){
                  synchronized (student){
                      //生产者生产数据之前,应该先看一看数据有没有被消费,如果没有被消费,就等待
                      if(student.getFlag()){
                          try {
                              student.wait();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
      
                      //student中flag是false,生产者生产数据
                      if(i%2==0){
                          student.setName("张三");
                          student.setAge(18);
                      }else {
                          student.setName("李四");
                          student.setAge(16);
                      }
      
                      student.setFlag(true);
                      //通知消费者消费数据
                      student.notify();
                  }
      
                  i++;
              }
      
          }
      }
      
    • 消费者

      package com.shujia.day17.waitdemo;
      
      public class Consumer implements Runnable{
          private Student student;
      
          Consumer(Student student){
              this.student = student;
          }
      
          @Override
          public void run() {
      //        Student student = new Student();
              while (true){
                  synchronized (student){
                      //消费者消费数据之前,应该先看一看有没有数据产生
                      if(!student.getFlag()){
                          //等待生产者生产数据
                          try {
                              student.wait();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
      
                      //如果Student中flag的值是true,说明数据已经产生,无需等待,直接消费
                      System.out.println(student.getName()+"-"+student.getAge());
                      //消费完数据之后,将flag改成false
                      student.setFlag(false);
                      //通知生产者继续生产
                      student.notify();
                  }
              }
          }
      }
      
    • 学生对象(作为中间的参数数据)

      public class Student {
          private String name;
          private int age;
          private boolean flag;
      
          public Student() {
          }
      
          public Student(String name, int age,boolean flag) {
              this.name = name;
              this.age = age;
              this.flag = flag;
          }
      
          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 boolean getFlag() {
              return flag;
          }
      
          public void setFlag(boolean flag) {
              this.flag = flag;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
      public class Test {
          public static void main(String[] args) {
              Student student = new Student();
              //name-null
              //age-0
              //flag-false
      
      
              //创建生产者线程对象
              Producer producer = new Producer(student);
              Thread t1 = new Thread(producer);
      
              //创建消费者线程对象
              Consumer consumer = new Consumer(student);
              Thread t2 = new Thread(consumer);
      
              t1.start();
              t2.start();
      
          }
      }
      
10、线程状态转换图image-20240314192525203
11、线程组
11.1、概述

Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制

11.2、构造方法

ThreadGroup(String name)
构造一个新的线程组。
ThreadGroup(ThreadGroup parent, String name)
创建一个名为name的子线程组

11.3、成员方法
public final String getName()//返回此线程组的名称
public final ThreadGroup getThreadGroup()//返回此线程所属的线程组
setDaemon()//可以将线程组变成守护线程组其中的线程也变成守护线程
11.4、案例
package com.shujia.day18;

/*
    线程组:java可以将多个线程分装一个线程组中
    ThreadGroup

 */

class Demo1 extends Thread{
    private String name;
    private ThreadGroup threadGroup;

    Demo1(ThreadGroup threadGroup,String name){
        super(threadGroup,name);
    }

    @Override
    public void run() {
        for(int i=1;i<200;i++){
            System.out.println(i);
        }
    }
}

public class ThreadGroupDemo1 {
    public static void main(String[] args) {
        //构造方法
        //ThreadGroup(String name) 构造一个新的线程组。
        ThreadGroup tg1 = new ThreadGroup("帅哥组");

        //创建两个线程对象
        //创建线程对象时候可以指定该线程是属于哪个线程组的
        Demo1 d1 = new Demo1(tg1,"尚平");
        Demo1 d2 = new Demo1(tg1, "丁义杰");

        //ThreadGroup类中有一个方法public final String getName()返回此线程组的名称。
        //可以获取线程组的名字
        //1、获取线程所在的线程组
        //public final ThreadGroup getThreadGroup()返回此线程所属的线程组
        ThreadGroup threadGroup = d1.getThreadGroup();
        //2、调用线程组ThreadGroup中getName()
        String name = threadGroup.getName();
        System.out.println(d1.getName()+"所在的线程组名为:"+name);


        ThreadGroup tg2 = new ThreadGroup("美女组");
        Demo1 d3 = new Demo1(tg2, "刘亦菲");
        Demo1 d4 = new Demo1(tg2, "林志玲");

        //可以直接对线程组进行设置,线程组中的线程也一并进行设置
        tg1.setDaemon(true);





    }
}
12、线程池
12.1、概述

线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来

12.2、构造方法

Executors已经为我们封装好了 4 种常见的功能线程池:

定长线程池(FixedThreadPool)

定时线程池(ScheduledThreadPool )
可缓存线程池(CachedThreadPool)
单线程化线程池(SingleThreadExecutor)

newSingleThreadScheduledExecutor() 只有一个线程,用来调度任务在指定时间执行

12.3、四种线程池的特点
  • 定长线程池(FixedThreadPool)、

只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。

  • 定时线程池(ScheduledThreadPool )

核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列

  • 可缓存线程池(CachedThreadPool)

无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列

  • 单线程化线程池(SingleThreadExecutor)

只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列

  • 单线程定时线程池

只有一个线程,用来调度任务在指定时间执行

12.4、主要介绍定时线程池
  • 成员方法

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)
Future submit(Callable task)

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

/*
    线程池:java提供了一个工具类Executors帮助我们创建不同种类的线程池
    public static ExecutorService newFixedThreadPool(int nThreads)  固定大小的线程池'

    线程的创建第三种方式:实现Callable接口,结合线程池创建。

    面试题:
        1、多线程的创建方式有几种,分别是哪几种?
        2、线程池有哪些?分别什么特点。

 */

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 200; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
        }
    }
}

//class MyCallable implements Callable{
//
//    @Override
//    public Object call() throws Exception {
//        for (int i = 1; i <= 200; i++) {
//            System.out.println(Thread.currentThread().getName() + "--" + i);
//        }
//        return null;
//    }
//}

public class ThreadPoolDemo {
    public static void main(String[] args) {
        //创建一个线程池
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //将线程放入到线程组中运行呢?
        //Future<?> submit(Runnable task);
//        pool.submit(new MyRunnable());
//        pool.submit(new MyRunnable());
//        pool.submit(new MyRunnable());
        //pool-1-thread-1
        //pool-1: 第一个线程池
        //thread-1: 线程池中第一个线程

        //<T> Future<T> submit(Callable<T> task);

//        pool.submit(new MyCallable());
//        pool.submit(new MyCallable());
//        pool.submit(new MyCallable());

        pool.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                for(int i=1;i<=200;i++){
                    System.out.println(Thread.currentThread().getName() + "--" + i);
                }
                return null;
            }
        });

        pool.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                for(int i=1;i<=200;i++){
                    System.out.println(Thread.currentThread().getName() + "--" + i);
                }
                return null;
            }
        });


        //关闭线程池
        pool.shutdown();


    }
}
13、定时器
13.1、概述

定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能

13.2、构造方法

Timer()
创建一个新的计时器。

public abstract class TimerTask//抽象类

13.3、成员方法
  • Timer

public void schedule(TimerTask task, long delay)//定时一个延时任务 只会执行一次
public void schedule(TimerTask task,long delay,long period)//延迟执行任务之后,以后的每间隔一段时间重复执行

  • TimerTask

public abstract void run()//该定时器任务要执行的操作
public boolean cancel()//取消此计时器任务

13.4、案例
import java.util.Timer;
import java.util.TimerTask;

/*
    在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
 */
public class TimerDemo {
    public static void main(String[] args) {
        //创建一个定时器对象
        //Timer() 创建一个新的计时器。
        Timer timer = new Timer();

        //设置定时任务
        //public void schedule(TimerTask task, long delay) 定时一个延时任务 只会执行一次
//        timer.schedule(new MyTask(timer),3000);
        //public void schedule(TimerTask task,long delay,long period) 延迟执行任务之后,以后的每间隔一段时间重复执行
        timer.schedule(new MyTask(timer),5000,2000);


//        timer.cancel(); //关闭定时器


    }
}

class MyTask extends TimerTask{
    private Timer timer;

    MyTask(Timer timer){
        this.timer = timer;
    }

    @Override
    public void run() {
        System.out.println("砰!爆炸了....");
        //关闭定时器
//        timer.cancel();
    }
}
14、多线程面试题

多线程有几种实现方案,分别是哪几种?
同步有几种方式,分别是什么?
启动一个线程是run()还是start()?它们的区别?
sleep()和wait()方法的区别
为什么wait(),notify(),notifyAll()等方法都定义在Object类中
线程的生命周期图

15、设计模式
15.1、分类

创建型模式
行为型模式
结构型模式

15.2、创建模式

1、简单工厂模式
2、工厂方法模式
3、单例模式
a. 懒汉式
b. 饿汉式

15.3、案例
  • 简单工厂模式

    • 动物类:
    public abstract class Animal {
        public abstract void eat();
    
        public abstract void sleep();
    }
    
    • 狗类:
    public class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃肉");
        }
    
        @Override
        public void sleep() {
            System.out.println("狗趴着睡");
        }
    }
    
    • 猫类:
    public class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("猫吃🐟");
        }
    
        @Override
        public void sleep() {
            System.out.println("猫蜷着睡");
        }
    }
    
    • 动物工厂:
    public class AnimalFactory {
        private AnimalFactory(){
    
        }
    
        //创建一只狗
        public static Dog createDog(){
            return new Dog();
        }
    
        //创建一只猫
        public static Cat createCat(){
            return new Cat();
        }
    
    }
    
    • 测试类:
    /*
        简单工厂模式:
            有一个类专门负责创建各种对象的,不需要在main方法中自己new
     */
    public class Test {
        public static void main(String[] args) {
    //        Cat cat = new Cat();
    //        Dog dog = new Dog();
    
            Cat cat = AnimalFactory.createCat();
            Dog dog = AnimalFactory.createDog();
        }
    }
    
  • 工厂方法模式

    • 工厂:
    public abstract class Factory {
        public abstract Animal createAnimal();
    }
    
    • 猫工厂:
    public class CatFactory extends Factory{
        @Override
        public Animal createAnimal() {
            return new Cat();
        }
    }
    
    • 狗工厂:
    public class DogFactory extends Factory{
        @Override
        public Animal createAnimal() {
            return new Dog();
        }
    }
    
    • 测试类:
    public class Test {
        public static void main(String[] args) {
            DogFactory dogFactory = new DogFactory();
            CatFactory catFactory = new CatFactory();
    
            Animal dog = dogFactory.createAnimal();
            Animal cat = catFactory.createAnimal();
        }
    }
    
  • 单例模式:

    • 懒汉式:

      • /*
            单(单一)例(实例)模式:整个java程序运行过程中,一个类的对象在内存中有且仅有一个
            1. 构造方法私有化
            2. 将自己类作为静态成员变量类型存在
            3. 提供静态公共的成员方法获取这个对象
        
        
            懒汉式:当调用获取对象的方法的时候,内存才会创建该类的对象
            饿汉式:无论是否调用获取对象的方法,内存中始终都会有且仅有一个该类对象
        
            面试的时候,两个都要说,重点说懒汉式,开发的时候写饿汉式。
            懒汉式涉及线程安全的问题,饿汉式不涉及。
        
         */
        
        //饿汉式:
        class Student2 {
            private static Student2 student2 = new Student2();
        
            private Student2() {
            }
        
            public static Student2 getStudent() {
                //t1,t2,t3
                return student2;
            }
        }
        
        //
        
        public class DanLiDemo2 {
            public static void main(String[] args) {
                Student2 s1 = Student2.getStudent();
                Student2 s2 = Student2.getStudent();
                System.out.println(s1==s2);
            }
        }
        
    • 饿汉式

      • /*
            单(单一)例(实例)模式:整个java程序运行过程中,一个类的对象在内存中有且仅有一个
            1. 构造方法私有化
            2. 将自己类作为静态成员变量类型存在
            3. 提供静态公共的成员方法获取这个对象
        
        
            懒汉式:当调用获取对象的方法的时候,内存才会创建该类的对象
            饿汉式:无论是否调用获取对象的方法,内存中始终都会有且仅有一个该类对象
        
         */
        
        //懒汉式:
        class Student {
            private static Student student;
        
            private Student() {
            }
        
            //t1,t2,t3
            public synchronized static Student getStudent() {
                if (student == null) {
                    //t1,t2,t3
                    student = new Student();
                    return student;
                } else {
                    return student;
                }
            }
        }
        
        //
        
        public class DanLiDemo1 {
            public static void main(String[] args) {
        //        Student s1 = Student.getStudent();
        //        Student s2 = Student.getStudent();
        //        System.out.println(s1==s2);
        
        //        new Student();
        
            }
        }
        
第八章网络编程
1、网络编程的概述

计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。极域
网络编程
就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换

2、网络模型

OSI(Open System Interconnection开放系统互连)参考模型
TCP/IP参考模型

3、网络参考模型图

image-20240315191547134

4、网络通信三要素

IP地址:InetAddress
网络中设备的标识,不易记忆,可用主机名
端口号
用于标识进程的逻辑地址,不同进程的标识
传输协议
通讯的规则
常见协议:TCP,UDP

5、IP地址

要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,在TCP/IP协议中,这个标识号就是IP地址

6、InetAddress类

没有构造方法
要掌握的功能
获取任意主机:getByName
主机名:getHostName
主机Ip地址:getHostAddress

7、端口号

物理端口 网卡口
逻辑端口 我们指的就是逻辑端口
A:每个网络程序都会至少有一个逻辑端口
B:用于标识进程的逻辑地址,不同进程的标识
C:有效端口:065535,其中01024系统使用或保留端口。
通过netstat -ano可以查看端口号

8、Socket

Socket套接字:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket原理机制:
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。

9、UDP协议

将数据源和目的封装成数据包中,不需要建立连接;每个数据报包的大小在限制在64k;因无连接,是不可靠协议;不需要建立连接,速度快

9.1、案例1
  • 发送端

1:建立udp的socket服务
2:将要发送的数据封装成数据包
3:通过udp的socket服务,将数据包发送出
4:关闭资源

public class SendDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立udp的socket服务
        //public DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。
        DatagramSocket ds = new DatagramSocket();

        //2:将要发送的数据封装成数据包 DatagramPacket
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        //构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
        //byte[] buf  要发送的数据的字节数组表现形式
        byte[] bytes = "大家好,我是帅哥丁义杰".getBytes();
        //int length  要发送数据字节数组的长度
        int length = bytes.length;
        //InetAddress address  目标发送的ip地址的InetAddress形式对象
        InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
        //int port 目标机器上应用程序的端口号
        int port = 12345;
        //创建数据包
        DatagramPacket datagramPacket = new DatagramPacket(bytes, length, inetAddress, port);

        //3:通过udp的socket服务,将数据包发送出
        //public void send(DatagramPacket p) 从此套接字发送数据报包。
        //将上面创建好的数据包进行发送
        ds.send(datagramPacket);

        //4:关闭资源
        ds.close();
    }
}
  • 接收端

1:建立udp的socket服务.
2:通过receive方法接收数据
3:将收到的数据存储到数据包对象中
4:通过数据包对象的功能来完成对接收到数据进行解析.
5:可以对资源进行关闭

public class ReceiveDemo1 {
    public static void main(String[] args) throws Exception {
        //public DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。
        //创建Socket对象并绑定一个端口号
        DatagramSocket ds = new DatagramSocket(12345);

        //接收端需要创建一个空的数据包,接收过来的数据
        //DatagramPacket(byte[] buf, int length)
        //构造一个 DatagramPacket用于接收长度的数据包 length 。
        //byte[] buf 将来用于接收数据的字节数组
        byte[] bytes = new byte[1024];
        //int length  所准备的字节数组的长度
        int length = bytes.length;
        DatagramPacket dp = new DatagramPacket(bytes, length);


        //2:通过receive方法接收数据
        //receive(DatagramPacket p)
        //程序走到这一步,会发生阻塞等待数据过来
        ds.receive(dp); //3:将收到的数据存储到数据包对象中

        //4:通过数据包对象的功能来完成对接收到数据进行解析.
        byte[] data = dp.getData(); // 用于解析数据包中接收到的数据
        int dataLength = dp.getLength(); // 获取真正接收到字节数组长度
        //将接收到的字节数组转字符串
        String info = new String(data, 0, dataLength);
        InetAddress inetAddress = dp.getAddress();
        String ip = inetAddress.getHostAddress(); //获取发送段的ip地址
        String hostName = inetAddress.getHostName(); //获取发送端的主机名
        System.out.println("===================================");
        System.out.println(hostName + " ip地址为:" + ip + ", 发来数据:" + info);
        System.out.println("===================================");

        //5:可以对资源进行关闭
        ds.close();

    }
}
9.2、案例2
  • 发送端

1:建立udp的socket服务
2:将要发送的数据封装成数据包
3:通过udp的socket服务,将数据包发送出
4:关闭资源

public class ReceiveDemo1 {
    public static void main(String[] args) throws Exception {
        System.out.println("=================欢迎来到29期大家庭 聊天群==========================\r\n");

        //public DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。
        //创建Socket对象并绑定一个端口号
        DatagramSocket ds = new DatagramSocket(12345);

        //接收端需要创建一个空的数据包,接收过来的数据
        //DatagramPacket(byte[] buf, int length)
        //构造一个 DatagramPacket用于接收长度的数据包 length 。
        //byte[] buf 将来用于接收数据的字节数组
        byte[] bytes = new byte[1024];
        //int length  所准备的字节数组的长度
        int length = bytes.length;
        DatagramPacket dp = new DatagramPacket(bytes, length);


        while (true){
            //2:通过receive方法接收数据
            //receive(DatagramPacket p)
            //程序走到这一步,会发生阻塞等待数据过来
            ds.receive(dp); //3:将收到的数据存储到数据包对象中

            //4:通过数据包对象的功能来完成对接收到数据进行解析.
            byte[] data = dp.getData(); // 用于解析数据包中接收到的数据
            int dataLength = dp.getLength(); // 获取真正接收到字节数组长度
            //将接收到的字节数组转字符串
            String info = new String(data, 0, dataLength);

            InetAddress inetAddress = dp.getAddress();
            String ip = inetAddress.getHostAddress(); //获取发送段的ip地址
            String hostName = inetAddress.getHostName(); //获取发送端的主机名

            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("================="+time+"=================");
            if("886".equals(info)){
                System.out.println("******"+hostName + " ip地址为:" + ip + "发送端停止发送了!!!!!!!******");
//                break;
            }else {
                System.out.println(hostName + " ip地址为:" + ip + ", 发来数据:" + info);
            }

        }

        //5:可以对资源进行关闭
//        ds.close();

    }
}
  • 接收端

1:建立udp的socket服务.
2:通过receive方法接收数据
3:将收到的数据存储到数据包对象中
4:通过数据包对象的功能来完成对接收到数据进行解析.
5:可以对资源进行关闭

public class SendDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立udp的socket服务
        //public DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。
        DatagramSocket ds = new DatagramSocket();
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);

        while (true){
            System.out.print("请输入要发送的数据: ");
            String info = sc.next();


            //2:将要发送的数据封装成数据包 DatagramPacket
            //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
            //构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
            //byte[] buf  要发送的数据的字节数组表现形式
            byte[] bytes = info.getBytes();
            //int length  要发送数据字节数组的长度
            int length = bytes.length;
            //InetAddress address  目标发送的ip地址的InetAddress形式对象
            InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
            //int port 目标机器上应用程序的端口号
            int port = 12345;
            //创建数据包
            DatagramPacket datagramPacket = new DatagramPacket(bytes, length, inetAddress, port);

            //3:通过udp的socket服务,将数据包发送出
            //public void send(DatagramPacket p) 从此套接字发送数据报包。
            //将上面创建好的数据包进行发送
            ds.send(datagramPacket);

            if("886".equals(info)){
                break;
            }
        }

        //4:关闭资源
        ds.close();
    }
}
10、TCP协议

建立连接,形成传输数据的通道;在连接中进行大数据量传输;通过三次握手完成连接,是可靠协议;必须建立连接,效率会稍低

10.1、案例1
  • 客户端

1:建立客户端的Socket服务,并明确要连接的服务器。
2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
3:通过Socket对象的方法,可以获取这两个流
4:通过流的对象可以对数据进行传输
5:如果传输数据完毕,关闭资源

public class ClientDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        //构造方法:要传输服务器目标的ip地址和端口号
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        //InetAddress address 服务器的ip地址的InetAddress表现形式
//        InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
//        //int port 服务器程序所占用的端口号
//        int port = 10086;
//        Socket socket = new Socket(inetAddress, port);
        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        //该Socket对象如果成功创建,就说明已经与服务器建立连接成功!
        Socket socket = new Socket("192.168.1.43", 10086);
        System.out.println("与服务器段连接成功!" + socket);

        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        //4:通过流的对象可以对数据进行传输
        outputStream.write("包旭真的很帅,比尚平都帅!".getBytes());

        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}
  • 服务端

1:建立服务器端的socket服务(ServerSocket),需要一个端口
2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
3:通过客户端的获取流对象的方法,读取数据或者写入数据
4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

public class ServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1:创建服务器段的Socket对象,
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);

        //2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
        //程序运行到这一步的时候,发生阻塞,会监听客户端的连接
        //返回的Socket对象,实际上可以理解为是与某一个客户端的连接通道
        //将来可以使用Socket对象中的输入流获取客户端发送的信息,或者使用输出流向客户端发送反馈
        Socket socket = ss.accept();

        //获取通道中的输入流对象
        InputStream is = socket.getInputStream();
        InetAddress inetAddress = socket.getInetAddress();
        String hostName = inetAddress.getHostName();
        String hostAddress = inetAddress.getHostAddress();

        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = is.read(bytes))!=-1){
            String info = new String(bytes, 0, length);
            System.out.println(hostName + " ip地址为:" + hostAddress + ", 发来数据:" + info);
        }

        socket.close();
        ss.close();
    }
}
10.2、案例2
  • 客户端

1:建立客户端的Socket服务,并明确要连接的服务器。
2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
3:通过Socket对象的方法,可以获取这两个流
4:通过流的对象可以对数据进行传输
5:如果传输数据完毕,关闭资源

public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        //构造方法:要传输服务器目标的ip地址和端口号
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        //InetAddress address 服务器的ip地址的InetAddress表现形式
//        InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
//        //int port 服务器程序所占用的端口号
//        int port = 10086;
//        Socket socket = new Socket(inetAddress, port);
        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        //该Socket对象如果成功创建,就说明已经与服务器建立连接成功!
        Socket socket = new Socket("192.168.1.43", 10086);
        System.out.println("与服务器段连接成功!" + socket);

        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        //4:通过流的对象可以对数据进行传输
        outputStream.write("包旭真的很帅,比尚平都帅!".getBytes());
        //告诉服务器没有数据
        socket.shutdownOutput();


        //获取通道中的输入流对象
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int length = inputStream.read(bytes);
        String info = new String(bytes, 0, length);
        System.out.println("服务器给我的反馈是:" + info);

        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}
  • 服务端

1:建立服务器端的socket服务(ServerSocket),需要一个端口
2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
3:通过客户端的获取流对象的方法,读取数据或者写入数据
4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

public class ServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1:创建服务器段的Socket对象,
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);

        //2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
        //程序运行到这一步的时候,发生阻塞,会监听客户端的连接
        //返回的Socket对象,实际上可以理解为是与某一个客户端的连接通道
        //将来可以使用Socket对象中的输入流获取客户端发送的信息,或者使用输出流向客户端发送反馈
        Socket socket = ss.accept();

        //获取通道中的输入流对象
        InputStream is = socket.getInputStream();
        InetAddress inetAddress = socket.getInetAddress();
        String hostName = inetAddress.getHostName();
        String hostAddress = inetAddress.getHostAddress();
        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = is.read(bytes))!=-1){
            String info = new String(bytes, 0, length);
            System.out.println(hostName + " ip地址为:" + hostAddress + ", 发来数据:" + info);
        }

        //获取通道中的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("服务器已收到! ".getBytes());

        socket.close();
        ss.close();
    }
}
10.3、案例3
  • 客户端

1:建立客户端的Socket服务,并明确要连接的服务器。
2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
3:通过Socket对象的方法,可以获取这两个流
4:通过流的对象可以对数据进行传输
5:如果传输数据完毕,关闭资源

public class ClientDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        //构造方法:要传输服务器目标的ip地址和端口号
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        //InetAddress address 服务器的ip地址的InetAddress表现形式
//        InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
//        //int port 服务器程序所占用的端口号
//        int port = 10086;
//        Socket socket = new Socket(inetAddress, port);
        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        //该Socket对象如果成功创建,就说明已经与服务器建立连接成功!
        Socket socket = new Socket("192.168.1.43", 10086);
        System.out.println("与服务器段连接成功!" + socket);
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);
        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        while (true){
            System.out.println("请输入要发送的数据: ");
            String info = sc.next();

            if("886".equals(info)){
                break;
            }

            //4:通过流的对象可以对数据进行传输
            outputStream.write(info.getBytes());
        }


        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}
  • 服务端

1:建立服务器端的socket服务(ServerSocket),需要一个端口
2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
3:通过客户端的获取流对象的方法,读取数据或者写入数据
4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

public class ServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1:创建服务器段的Socket对象,
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);

        //2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
        //程序运行到这一步的时候,发生阻塞,会监听客户端的连接
        //返回的Socket对象,实际上可以理解为是与某一个客户端的连接通道
        //将来可以使用Socket对象中的输入流获取客户端发送的信息,或者使用输出流向客户端发送反馈
        Socket socket = ss.accept();

        //获取通道中的输入流对象
        InputStream is = socket.getInputStream();
        InetAddress inetAddress = socket.getInetAddress();
        String hostName = inetAddress.getHostName();
        String hostAddress = inetAddress.getHostAddress();

        byte[] bytes = new byte[1024];
        int length = 0;
        while ((length = is.read(bytes))!=-1){
            String info = new String(bytes, 0, length);
            System.out.println(hostName + " ip地址为:" + hostAddress + ", 发来数据:" + info);
        }

//        socket.close();
//        ss.close();
    }
}
10.4、案例4
  • 客户端

1:建立客户端的Socket服务,并明确要连接的服务器。
2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
3:通过Socket对象的方法,可以获取这两个流
4:通过流的对象可以对数据进行传输
5:如果传输数据完毕,关闭资源

public class ClientDemo1 {
    public static void main(String[] args) throws Exception{
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        //构造方法:要传输服务器目标的ip地址和端口号
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        //InetAddress address 服务器的ip地址的InetAddress表现形式
//        InetAddress inetAddress = InetAddress.getByName("192.168.1.43");
//        //int port 服务器程序所占用的端口号
//        int port = 10086;
//        Socket socket = new Socket(inetAddress, port);
        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        //该Socket对象如果成功创建,就说明已经与服务器建立连接成功!
        Socket socket = new Socket("192.168.1.43", 10086);
        System.out.println("与服务器段连接成功!" + socket);
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);
        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        while (true){
            System.out.println("请输入要发送的数据: ");
            String info = sc.next();



            //4:通过流的对象可以对数据进行传输
            outputStream.write(info.getBytes());
            if("886".equals(info)){
                break;
            }
        }


        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}
  • 服务端

1:建立服务器端的socket服务(ServerSocket),需要一个端口
2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
3:通过客户端的获取流对象的方法,读取数据或者写入数据
4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

public class ServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1:创建服务器段的Socket对象,
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);
        System.out.println("===============欢迎来到29期大家庭!😘=========================");

        //死循环监听客户端的连接,将每个客户端封装成一个线程运行
        while (true){
            Socket socket = ss.accept();
            new OneClient(socket).start();
        }


    }
}

class OneClient extends Thread{
    private Socket socket;
    private String hostName;
    private String hostAddress;
    OneClient(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //获取通道中的输入流对象
            InputStream is = socket.getInputStream();
            InetAddress inetAddress = socket.getInetAddress();
            hostName = inetAddress.getHostName();
            hostAddress = inetAddress.getHostAddress();
            System.out.println("=====😊用户: "+hostName+"已上线!====");
            byte[] bytes = new byte[1024];
            int length = 0;
            while ((length = is.read(bytes))!=-1){
                String info = new String(bytes, 0, length);
                if("886".equals(info)){
                    System.out.println("=====😢用户: "+hostName+"已离线!====");
                }else {
                    String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                    System.out.println(time);
                    System.out.println(hostName + " ip地址为:" + hostAddress + ", 发来数据:" + info);
                }

            }
        }catch (Exception e){
            System.out.println("--------------------------------------------");
            System.out.println(hostName + " ip地址为:" + hostAddress + "异常下线............");
            System.out.println("--------------------------------------------");
        }
    }
}
第九章类的加载器和反射
1、类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化 就是我们以前讲过的初始化步骤

2、类的加载器
2.1、概述

负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

2.2、类加载器的组成及其作用

Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

3、反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

4、案例
  • 学生类
class Student {
    public String name;
    private int age;
    String address;

    public Student() {
    }

    Student(int age) {
        this.age = age;
    }

    private Student(String name) {
        this.name = name;
    }

    public void fun1() {
        System.out.println("这是公共的成员方法");
    }

    private void fun1(String name) {
        System.out.println("这是私有的成员方法" + name);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 反射的实现
public class StudentDemo1 {
    public static void main(String[] args) throws Exception {
        Student student = new Student();

        //如何获取一个类对用的Class对象
        //1、在有对象的前提下,使用getClass()方法获取
//        Class<? extends Student> aClass = student.getClass();

        //2、没有对象,只有类,通过类的属性进行获取
//        Class<Student> aClass = Student.class;

        //3、最常用的方式,Class类中有一个静态方法 JDBC
        //public static Class<?> forName(String className) 返回与给定字符串名称的类或接口相关联的类对象。
        Class<?> studentClass = Class.forName("com.shujia.day19.Student");

        System.out.println("===============================================================================");
        /*
            一个类中有三种成员:
                1、成员变量 Field
                2、构造方法 Constructor
                3、成员方法 Method
         */
        //TODO:通过反射获取构造方法并使用
        //public Constructor<T> getConstructor(Class<?>... parameterTypes)
        //返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数。
//        Constructor<?> c1 = studentClass.getConstructor(); //表示获取Student类中的无参构造方法
//        System.out.println(c1);
//        //获取Student类中的有参构造方法
//        //获取不是public修饰的构造方法
//        //getDeclaredConstructor() 可以获取所有权限构造方法
        Constructor<?> c2 = studentClass.getDeclaredConstructor(int.class);
        System.out.println(c2);
//        Constructor<?> c3 = studentClass.getDeclaredConstructor(String.class);
//        System.out.println(c3);
//        //如何使用将获取到的构造方法来创建对象呢?
//        //Constructor@newInstance(Object... initargs)
//        //使用由此Constructor对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。
        Object o = c1.newInstance();
        System.out.println(o);
//        c3.setAccessible(true); // 暴力访问,绕过检测机制
//        Object o = c3.newInstance("张三");
//        System.out.println(o);

        //TODO:通过反射获取成员变量并使用
        //Class@getField 根据属性的名字获取对应的被public修饰的成员变量的对象
//        Field name = studentClass.getField("name");
//        System.out.println(name);
//        //getDeclaredField 获取所有权限的成员变量
//        Field age = studentClass.getDeclaredField("age");
//        System.out.println(age);
//
//        //getFields获取类中所有的被public修饰的成员变量
        Field[] fields = studentClass.getFields();
        System.out.println(Arrays.toString(fields));
        Field[] declaredFields = studentClass.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));
//        //使用获取到的成员变量
//        Constructor<?> c1 = studentClass.getConstructor(); //表示获取Student类中的无参构造方法
//        Object o = c1.newInstance();
//        System.out.println(o);
//        //需求:给对象o中的成员变量name进行赋值
//        //public void set(Object obj, Object value)
//        //将指定对象参数上的此Field对象表示的字段设置为指定的新值。
//        name.set(o,"李四");
//        System.out.println(o);
//        age.setAccessible(true);
//        age.set(o,18);
//        System.out.println(o);

        //TODO:通过反射获取成员方法并使用
        //public Method getMethod(String name, Class<?>... parameterTypes)
        //获取类中被public所修饰的成员方法
        //根据方法的名字和参数类型获取
        Method fun1 = studentClass.getMethod("fun1");
        System.out.println(fun1);
//        //getDeclaredMethod获取任意一个成员方法,包括私有的
        Method fun11 = studentClass.getDeclaredMethod("fun1", String.class);
        Method fun11 = studentClass.getMethod("fun1", String.class);
        System.out.println(fun11);

        //getMethods可以获取类中及其直接父类中的所有被public修饰的成员方法
//        Method[] methods = studentClass.getMethods();
//        for (Method method : methods) {
//            System.out.println(method);
//        }
        //getDeclaredMethods可以获取本类中的所有的成员方法,包括私有的
//        Method[] declaredMethods = studentClass.getDeclaredMethods();
//        for (Method declaredMethod : declaredMethods) {
//            System.out.println(declaredMethod);
//        }

        //如何调用获取到的成员方法
        Constructor<?> c1 = studentClass.getConstructor(); //表示获取Student类中的无参构造方法
        Object o = c1.newInstance();
        //public Object invoke(Object obj, Object... args)
        //在具有指定参数的方法对象上调用此方法对象表示的基础方法。
        fun1.invoke(o);
        fun11.setAccessible(true);
        fun11.invoke(o,"数加");


    }
}
第十章lambda表达式
1、概述
  • 从JDK1.8开始为了简化使用者进行代码开发,专门提供有Lambda表达式的支持,利用此操作形式可以实现函数式的编程,对于函数式编程比较著名的语言:Scala,利用函数式的编程可以避免掉面向对象编程之中的一些繁琐的问题。 面向对象在其长期发展的过程中一直有一部分的反对者认为面向对象过于繁琐。
2、写lambda的场景
  • 能够使用 Lambda 表达式的一个重要依据是必须有相应的函数接口。所谓函数接口,是指内部有且仅有一个抽象方法的接口。
  • Lambda 表达式的另一个依据是类型推断机制。在上下文信息足够的情况下,编译器可以推断出参数列表的类型,而不需要显式指名。
3、lambda的优点
  • Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升
4、lambda的基本语法
4.1、概述
  • Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为->,该操作符被称为Lambda 操作符或箭头操作符。

    它将Lambda 分为两个部分:
    左侧:指定了Lambda 表达式需要的所有参数(对应接口中形参)
    右侧:指定了Lambda 体,即Lambda 表达式要执行的功能。(方法体,可以推断返回值类型)

4.2 、分类
  • Lambda表达式根据使用方式分为6类:
    1. 无参数,无返回值
    2. 有一个参数,无返回值
    3. 若只有一个参数,小括号可以省略不写
    4. 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
    5. 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
    6. Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
4.3、案例
  • 无参数,无返回值

    • 基本语句格式
      () -> {
      执行语句
      }

    • package com.shujia.day20.lambdademo5;
      
      /*
          1. 无参数,无返回值
       */
      public interface Inter {
          void fun1();
      }
      public class Demo {
          public static void main(String[] args) {
      //        show(new Inter() {
      //            @Override
      //            public void fun1() {
      //                System.out.println("hello world");
      //            }
      //        });
      
              show(() -> System.out.println("hello world"));
          }
      
          public static void show(Inter inter) {
              inter.fun1();
          }
      }
      
  • 有一个参数,无返回值

    • 基本语句格式
      (变量名) -> {
      执行语句
      }

    • /*
          有一个参数,无返回值
       */
      public interface Inter {
          void fun1(String s);
      }
      public class Demo {
          public static void main(String[] args) {
              show("数加", new Inter() {
                  @Override
                  public void fun1(String s) {
                      System.out.println("好好学习,天天向上" + s);
                  }
              });
      
      //        show("数加", e -> System.out.println("好好学习,天天向上" + e));
          }
      
          public static void show(String s, Inter inter) {
              inter.fun1(s);
          }
      }
      
  • 若只有一个参数,小括号可以省略不写

    • 基本语句格式
      变量名 -> {
      执行语句
      }

    • /*
          若只有一个参数,小括号可以省略不写
       */
      @FunctionalInterface
      public interface Inter1 {
          void fun1(int n);
      }
      public class Demo1 {
          public static void main(String[] args) {
              //需求:接口中的逻辑是直接输出n的值
      //        show1(10, (e) -> System.out.println(e));
              //TODO:结论1:当接口的参数个数只有一个的适合,改写Lambda表达式的时候,左边小括号可以省略
              show1(10, e -> System.out.println(e));
      
  • 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句

    • 基本语句格式
      (变量名1, 变量名2) -> {
      执行语句;
      return xxx;
      }

    • public interface Inter2 {
          void fun1(int a,int b);
      }
      public class Demo1 {
          public static void main(String[] args) {
      		//需求:接口中的逻辑求两个数相乘的输出
              //TODO:结论2:当接口的参数大于等于2个的时候,改写Lambda表达式的时候,左边小括号不可以省略
             	 show2(3, 4, (e1, e2) -> System.out.println(e1 * e2));
      //        show2(3, 4, (e1, e2) -> System.out.println(e1 + e2));
      //        show2(3, 4, (e1, e2) -> System.out.println(e1 - e2));
      //        show2(3, 4, (e1, e2) -> System.out.println(e1 / e2));
      
  • 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写

    • 基本语句格式
      (变量名1, 变量名2) -> 要返回的结果;

    • public interface Inter4 {
          String fun1(String s1, String s2);
      }        
      

    InetAddress inetAddress = socket.getInetAddress();
    hostName = inetAddress.getHostName();
    hostAddress = inetAddress.getHostAddress();
    System.out.println(“=😊用户: "+hostName+"已上线!”);
    byte[] bytes = new byte[1024];
    int length = 0;
    while ((length = is.read(bytes))!=-1){
    String info = new String(bytes, 0, length);
    if(“886”.equals(info)){
    System.out.println(“=😢用户: "+hostName+"已离线!”);
    }else {
    String time = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(new Date());
    System.out.println(time);
    System.out.println(hostName + " ip地址为:" + hostAddress + “, 发来数据:” + info);
    }

          }
      }catch (Exception e){
          System.out.println("--------------------------------------------");
          System.out.println(hostName + " ip地址为:" + hostAddress + "异常下线............");
          System.out.println("--------------------------------------------");
      }
    

    }
    }


#### 第九章类的加载器和反射

##### 1、类的加载

> 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
> 加载 
> 就是指将class文件读入内存,并为之创建一个Class对象。
> 任何类被使用时系统都会建立一个Class对象。
> 连接
> 验证 是否有正确的内部结构,并和其他类协调一致
> 准备 负责为类的静态成员分配内存,并设置默认初始化值
> 解析 将类的二进制数据中的符号引用替换为直接引用
> 初始化 就是我们以前讲过的初始化步骤

##### 2、类的加载器

###### 2.1、概述

> 负责将.class文件加载到内存中,并为之生成对应的Class对象。
> 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

###### 2.2、类加载器的组成及其作用

> Bootstrap ClassLoader 根类加载器
> 	也被称为引导类加载器,负责Java核心类的加载
> 	比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
> Extension ClassLoader 扩展类加载器
> 	负责JRE的扩展目录中jar包的加载。
> 	在JDK中JRE的lib目录下ext目录
> Sysetm ClassLoader 系统类加载器
> 	负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

##### 3、反射

> JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
> 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

##### 4、案例

- 学生类

~~~Java
class Student {
    public String name;
    private int age;
    String address;

    public Student() {
    }

    Student(int age) {
        this.age = age;
    }

    private Student(String name) {
        this.name = name;
    }

    public void fun1() {
        System.out.println("这是公共的成员方法");
    }

    private void fun1(String name) {
        System.out.println("这是私有的成员方法" + name);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 反射的实现
public class StudentDemo1 {
    public static void main(String[] args) throws Exception {
        Student student = new Student();

        //如何获取一个类对用的Class对象
        //1、在有对象的前提下,使用getClass()方法获取
//        Class<? extends Student> aClass = student.getClass();

        //2、没有对象,只有类,通过类的属性进行获取
//        Class<Student> aClass = Student.class;

        //3、最常用的方式,Class类中有一个静态方法 JDBC
        //public static Class<?> forName(String className) 返回与给定字符串名称的类或接口相关联的类对象。
        Class<?> studentClass = Class.forName("com.shujia.day19.Student");

        System.out.println("===============================================================================");
        /*
            一个类中有三种成员:
                1、成员变量 Field
                2、构造方法 Constructor
                3、成员方法 Method
         */
        //TODO:通过反射获取构造方法并使用
        //public Constructor<T> getConstructor(Class<?>... parameterTypes)
        //返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数。
//        Constructor<?> c1 = studentClass.getConstructor(); //表示获取Student类中的无参构造方法
//        System.out.println(c1);
//        //获取Student类中的有参构造方法
//        //获取不是public修饰的构造方法
//        //getDeclaredConstructor() 可以获取所有权限构造方法
        Constructor<?> c2 = studentClass.getDeclaredConstructor(int.class);
        System.out.println(c2);
//        Constructor<?> c3 = studentClass.getDeclaredConstructor(String.class);
//        System.out.println(c3);
//        //如何使用将获取到的构造方法来创建对象呢?
//        //Constructor@newInstance(Object... initargs)
//        //使用由此Constructor对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。
        Object o = c1.newInstance();
        System.out.println(o);
//        c3.setAccessible(true); // 暴力访问,绕过检测机制
//        Object o = c3.newInstance("张三");
//        System.out.println(o);

        //TODO:通过反射获取成员变量并使用
        //Class@getField 根据属性的名字获取对应的被public修饰的成员变量的对象
//        Field name = studentClass.getField("name");
//        System.out.println(name);
//        //getDeclaredField 获取所有权限的成员变量
//        Field age = studentClass.getDeclaredField("age");
//        System.out.println(age);
//
//        //getFields获取类中所有的被public修饰的成员变量
        Field[] fields = studentClass.getFields();
        System.out.println(Arrays.toString(fields));
        Field[] declaredFields = studentClass.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));
//        //使用获取到的成员变量
//        Constructor<?> c1 = studentClass.getConstructor(); //表示获取Student类中的无参构造方法
//        Object o = c1.newInstance();
//        System.out.println(o);
//        //需求:给对象o中的成员变量name进行赋值
//        //public void set(Object obj, Object value)
//        //将指定对象参数上的此Field对象表示的字段设置为指定的新值。
//        name.set(o,"李四");
//        System.out.println(o);
//        age.setAccessible(true);
//        age.set(o,18);
//        System.out.println(o);

        //TODO:通过反射获取成员方法并使用
        //public Method getMethod(String name, Class<?>... parameterTypes)
        //获取类中被public所修饰的成员方法
        //根据方法的名字和参数类型获取
        Method fun1 = studentClass.getMethod("fun1");
        System.out.println(fun1);
//        //getDeclaredMethod获取任意一个成员方法,包括私有的
        Method fun11 = studentClass.getDeclaredMethod("fun1", String.class);
        Method fun11 = studentClass.getMethod("fun1", String.class);
        System.out.println(fun11);

        //getMethods可以获取类中及其直接父类中的所有被public修饰的成员方法
//        Method[] methods = studentClass.getMethods();
//        for (Method method : methods) {
//            System.out.println(method);
//        }
        //getDeclaredMethods可以获取本类中的所有的成员方法,包括私有的
//        Method[] declaredMethods = studentClass.getDeclaredMethods();
//        for (Method declaredMethod : declaredMethods) {
//            System.out.println(declaredMethod);
//        }

        //如何调用获取到的成员方法
        Constructor<?> c1 = studentClass.getConstructor(); //表示获取Student类中的无参构造方法
        Object o = c1.newInstance();
        //public Object invoke(Object obj, Object... args)
        //在具有指定参数的方法对象上调用此方法对象表示的基础方法。
        fun1.invoke(o);
        fun11.setAccessible(true);
        fun11.invoke(o,"数加");


    }
}
第十章lambda表达式
1、概述
  • 从JDK1.8开始为了简化使用者进行代码开发,专门提供有Lambda表达式的支持,利用此操作形式可以实现函数式的编程,对于函数式编程比较著名的语言:Scala,利用函数式的编程可以避免掉面向对象编程之中的一些繁琐的问题。 面向对象在其长期发展的过程中一直有一部分的反对者认为面向对象过于繁琐。
2、写lambda的场景
  • 能够使用 Lambda 表达式的一个重要依据是必须有相应的函数接口。所谓函数接口,是指内部有且仅有一个抽象方法的接口。
  • Lambda 表达式的另一个依据是类型推断机制。在上下文信息足够的情况下,编译器可以推断出参数列表的类型,而不需要显式指名。
3、lambda的优点
  • Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升
4、lambda的基本语法
4.1、概述
  • Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为->,该操作符被称为Lambda 操作符或箭头操作符。

    它将Lambda 分为两个部分:
    左侧:指定了Lambda 表达式需要的所有参数(对应接口中形参)
    右侧:指定了Lambda 体,即Lambda 表达式要执行的功能。(方法体,可以推断返回值类型)

4.2 、分类
  • Lambda表达式根据使用方式分为6类:
    1. 无参数,无返回值
    2. 有一个参数,无返回值
    3. 若只有一个参数,小括号可以省略不写
    4. 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
    5. 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
    6. Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
4.3、案例
  • 无参数,无返回值

    • 基本语句格式
      () -> {
      执行语句
      }

    • package com.shujia.day20.lambdademo5;
      
      /*
          1. 无参数,无返回值
       */
      public interface Inter {
          void fun1();
      }
      public class Demo {
          public static void main(String[] args) {
      //        show(new Inter() {
      //            @Override
      //            public void fun1() {
      //                System.out.println("hello world");
      //            }
      //        });
      
              show(() -> System.out.println("hello world"));
          }
      
          public static void show(Inter inter) {
              inter.fun1();
          }
      }
      
  • 有一个参数,无返回值

    • 基本语句格式
      (变量名) -> {
      执行语句
      }

    • /*
          有一个参数,无返回值
       */
      public interface Inter {
          void fun1(String s);
      }
      public class Demo {
          public static void main(String[] args) {
              show("数加", new Inter() {
                  @Override
                  public void fun1(String s) {
                      System.out.println("好好学习,天天向上" + s);
                  }
              });
      
      //        show("数加", e -> System.out.println("好好学习,天天向上" + e));
          }
      
          public static void show(String s, Inter inter) {
              inter.fun1(s);
          }
      }
      
  • 若只有一个参数,小括号可以省略不写

    • 基本语句格式
      变量名 -> {
      执行语句
      }

    • /*
          若只有一个参数,小括号可以省略不写
       */
      @FunctionalInterface
      public interface Inter1 {
          void fun1(int n);
      }
      public class Demo1 {
          public static void main(String[] args) {
              //需求:接口中的逻辑是直接输出n的值
      //        show1(10, (e) -> System.out.println(e));
              //TODO:结论1:当接口的参数个数只有一个的适合,改写Lambda表达式的时候,左边小括号可以省略
              show1(10, e -> System.out.println(e));
      
  • 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句

    • 基本语句格式
      (变量名1, 变量名2) -> {
      执行语句;
      return xxx;
      }

    • public interface Inter2 {
          void fun1(int a,int b);
      }
      public class Demo1 {
          public static void main(String[] args) {
      		//需求:接口中的逻辑求两个数相乘的输出
              //TODO:结论2:当接口的参数大于等于2个的时候,改写Lambda表达式的时候,左边小括号不可以省略
             	 show2(3, 4, (e1, e2) -> System.out.println(e1 * e2));
      //        show2(3, 4, (e1, e2) -> System.out.println(e1 + e2));
      //        show2(3, 4, (e1, e2) -> System.out.println(e1 - e2));
      //        show2(3, 4, (e1, e2) -> System.out.println(e1 / e2));
      
  • 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写

    • 基本语句格式
      (变量名1, 变量名2) -> 要返回的结果;

    • public interface Inter4 {
          String fun1(String s1, String s2);
      }        
      //需求:将两个字符串进行拼接操
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尔等都不懂涐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值