javaSE知识

1.static

叫静态,可以修饰成员变量、成员方法

1. 类变量

成员变量按照有无static修饰,分为两种:

类变量:有static修饰,属于类,在计算机里只有一份,会被类的所有对象共享

实列变量:没有static修饰的,属于每个对象的 

public class Test {
    public static void main(String[] args) {
        // 目标:掌握有无static修饰成员变量的用法,特点。
        // 1、类变量的用法。
        // 类名.类变量(推荐)
        Student.name = "袁华";

        // 对象.类变量(不推荐)
        Student s1 = new Student();
        s1.name = "马冬梅";;

        Student s2 = new Student();
        s2.name = "秋雅";

        System.out.println(s1.name); // 秋雅
        System.out.println(Student.name); // 秋雅

        // 2、实例变量的用法:属于每个对象的变量
        // 对象.实例变量
        s1.age = 23;
        s2.age = 18;
        System.out.println(s1.age); // 23

        // System.out.println(Student.age); // 报错
    }
}

public class Test2 {
    public static void main(String[] args) {
        // 目标:通过案例理解类变量的应用场景。
        User u1 = new User();
        User u2 = new User();
        User u3 = new User();
        User u4 = new User();

        System.out.println(User.number);
    }
}
public class Student {
    // 类变量
    static String name;
    // 实例变量(对象的变量)
    int age;
}

public class User {
    // 类变量
    public static int number;

    public User(){
        // User.number++;
        // 注意:在同一个类中,访问自己类的类变量,才可以省略类名不写。
        number++;
    }
}

2. static修饰成员方法

成员方法根据有无static也分为两类:类方法、实例方法

public class Test {
    public static void main(String[] args) {
        // 目标:掌握有无static修饰方法的用法。
        // 1、类方法的用法
        // 类名.类方法(推荐)
        Student.printHelloWorld();

        // 对象.类方法(不推荐)
        Student s = new Student();
        s.printHelloWorld();

        // 2、实例方法的用法
        // 对象.实例方法
        s.printPass();
        // Student.printPass(); // 报错
    }


}
public class Test {
    public static void main(String[] args) {
        // 目标:掌握有无static修饰方法的用法。
        // 1、类方法的用法
        // 类名.类方法(推荐)
        Student.printHelloWorld();

        // 对象.类方法(不推荐)
        Student s = new Student();
        s.printHelloWorld();

        // 2、实例方法的用法
        // 对象.实例方法
        s.printPass();
        // Student.printPass(); // 报错
    }


}

static修饰成员方法的内存原理

1.类方法:static修饰的方法,可以被类名调用,是因为它是随着类的加载而加载的;
         所以类名直接就可以找到static修饰的方法
         
2.实例方法:非static修饰的方法,需要创建对象后才能调用,是因为实例方法中可能会访问实例变量,而实例变量需要创建对象后才存在。
          所以实例方法,必须创建对象后才能调用。

3.工具类 

         类方法的应用知识,叫做工具类。

        如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一下,所以把这样的类就叫做工具类。

生成验证码的工具类 

public class MyUtil {

    private MyUtil(){
    }

    public static String createCode(int n){
        String code = "";
        String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        Random r = new Random();
        // 2、开始定义一个循环产生每位随机字符
        for (int i = 0; i < n; i++) {
            // 3、随机一个字符范围内的索引。
            int index = r.nextInt(data.length());
            // 4、根据索引去全部字符中提取该字符
            code += data.charAt(index); // code = code + 字符
        }
        return code;
    }
}
/**
   登录界面
 */
public class LoginDemo {
    public static void main(String[] args) {
        // 1、定义2个变量 一个是记住最终产生的随机验证码 一个是记住可能用到的全部字符
        System.out.println(MyUtil.createCode(4));


    }
}
/**
  注册界面
 */
public class RegisterDemo {
    public static void main(String[] args) {
        System.out.println(MyUtil.createCode(6));
    }
}

 工具类的使用就是这样子的

 工具类里的方法全都是静态的,推荐用类名调用为了防止使用者用对象调用。我们可以把工具类的构造方法私有化。

public class MyUtils{
    //私有化构造方法:这样别人就不能使用构造方法new对象了
    private MyUtils(){
        
    }
    
    //类方法
    public static String createCode(int n){
       ...
    }
}

4.static的注意事项

public class Student {
    static String schoolName; // 类变量
    double score; // 实例变量

    // 1、类方法中可以直接访问类的成员,不可以直接访问实例成员。
    public static void printHelloWorld(){
        // 注意:同一个类中,访问类成员,可以省略类名不写。
        schoolName = "huang";
        printHelloWorld2();

        // System.out.println(score); // 报错的
        // printPass(); // 报错的

        // System.out.println(this); // 报错的
    }

    // 类方法
    public static void printHelloWorld2(){

    }

    // 2、实例方法中既可以直接访问类成员,也可以直接访问实例成员。
    // 实例方法
    // 3、实例方法中可以出现this关键字,类方法中不可以出现this关键字的
    public void printPass(){
        schoolName = "huang2";
        printHelloWorld2();

        System.out.println(score);
        printPass2();

        System.out.println(this);
    }

    // 实例方法
    public void printPass2(){

    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:掌握使用类方法、实例方法时的几点注意事项。
        // 1、类方法中可以直接访问类的成员,不可以直接访问实例成员。
        // 2、实例方法中既可以直接访问类成员,也可以直接访问实例成员。
        // 3、实例方法中可以出现this关键字,类方法中不可以出现this关键字的
    }
}

5.static的应用知识:代码块

代码块根据有无static修饰分为两种:静态代码块、实例代码块

public class Student {
    static int number = 80;
    static String schoolName = "huang";
    // 静态代码块
    static {
        System.out.println("静态代码块执行了~~");
        // schoolName = "黑马";
    }
    int age;
    // 实例代码块
    {
        System.out.println("实例代码块执行了~~");
        // age = 18;
        System.out.println("有人创建了对象:" + this);
    }

    public Student(){
        System.out.println("无参数构造器执行了~~");
    }

    public Student(String name){
        System.out.println("有参数构造器执行了~~");
    }
}


静态代码块不需要创建对象就能够执行

public class Test {
    public static void main(String[] args) {
        // 目标:认识两种代码块,了解他们的特点和基本作用。
        System.out.println(Student.number);
        System.out.println(Student.number);
        System.out.println(Student.number);

        System.out.println(Student.schoolName); // 黑马

        System.out.println("---------------------------------------");
        Student s1 = new Student();
        Student s2 = new Student("张三");
        System.out.println(s1.age);
        System.out.println(s2.age);
    }
}

 关于静态代码块重点注意:静态代码块,随着类的加载而执行,而且只执行一次。

实例代码块的作用和构造器的作用是一样的,用来给对象初始化值; 而且每次创建对象之前都会先执行实例代码块。

实例代码块每次创建对象之前都会执行一次

6.static的应用知识:单例设计模式

所谓设计模式指的是,一类问题可能会有多种解决方案,而设计模式是在编程实践中,多种方案中的一种最优方案

public class A {
    // 2、定义一个类变量记住类的一个对象
    private static A a = new A();

    // 1、必须私有类的构造器
    private A(){

    }

    // 3、定义一个类方法返回类的对象
    public static A getObject(){
        return a;
    }
}
public class B {
    // 2、定义一个类变量,用于存储这个类的一个对象。
    private static B b;

    // 1、把类的构造器私有
    private B(){

    }

    // 3、定义一个类方法,这个方法要保证第一次调用时才创建一个对象,后面调用时都会用这同一个对象返回。
    public static B getInstance(){
        if(b == null){
            System.out.println("第一次创建对象~");
            b = new B();
        }
        return b;
    }
}

public class Test1 {
    public static void main(String[] args) {
        // 目标:掌握单例设计模式的写法。
        A a1 = A.getObject();
        A a2 = A.getObject();
        System.out.println(a1);
        System.out.println(a2);
    }
}

/**
 * 目标:掌握懒汉式单例的写法。
 */
public class Test2 {
    public static void main(String[] args) {
        B b1 = B.getInstance(); // 第一次拿对象
        B b2 = B.getInstance();
        System.out.println(b1 == b2);
    }
}

2.继承

1.继承的认识

继承就是用extends关键字,让一个类和另一个类建立起一种父子关系

子类可以继承父类非私有的成员

带继承关系的类,java会用类和其父类,这多张设计图来一起创建类的对象

对象能直接访问什么成员,是由子父类这多张设计图共同决定的,这多张设计图对外暴露了什么成员,对象就可以访问什么成员

子类对象实际上是由子、父类两张设计图共同创建出来的。

所以,在子类对象的空间中,既有本类的成员,也有父类的成员。但是子类只能调用父类公有的成员。

// 父类
public class A {
    // 公开成员
    public int i;
    public void print1(){
        System.out.println("===print1===");
    }

    // 私有成员
    private int j;
    private void print2(){
        System.out.println("===print2===");
    }
}
// 子类
public class B extends A{
    private int k;
    // 子类是可以继承父类的非私有成员
    public void print3(){
        System.out.println(i);
        print1();

        // System.out.println(j);
        // print2();
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:认识继承、掌握继承的特点。
        B b = new B();
        System.out.println(b.i);
        // System.out.println(b.j);
        // System.out.println(b.k);

        b.print1();
        //b.print2();
        b.print3();
    }
}

2.继承的好处

减少了重复代码的编写,提高了代码的复用性

public class People {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Teacher extends People{
    private String skill;

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    public void printInfo(){
        System.out.println(getName() + "具备的技能:" + skill);
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚继承的好处。
        Teacher t = new Teacher();
        t.setName("播仔");
        t.setSkill("Java、Spring");
        System.out.println(t.getName());
        System.out.println(t.getSkill());
        t.printInfo();
    }
}

3.权限修饰符

就是用来限制类中的 成员(成员变量、成员方法、构造器、代码块、内部类)能够被访问的范围

public 都可以访问

protected 任意包下的任意类不可以

缺省 任意包下的子类和任意包下的任意类不可以

private 只可以本类中使用

public class Fu {
    // 1、私有:只能在本类中访问
    private void privateMethod(){
        System.out.println("==private==");
    }

    // 2、缺省:本类,同一个包下的类
    void method(){
        System.out.println("==缺省==");
    }

    // 3、protected: 本类,同一个包下的类,任意包下的子类
    protected void protectedMethod(){
        System.out.println("==protected==");
    }

    // 4、public: 本类,同一个包下的类,任意包下的子类,任意包下的任意类
    public void publicMethod(){
        System.out.println("==public==");
    }

    public void test(){
        privateMethod();
        method();
        protectedMethod();
        publicMethod();
    }

}
public class Demo {
    public static void main(String[] args) {
        // 目标:掌握不同权限修饰符的作用。
        Fu f = new Fu();
        // f.privateMethod();
        f.method();
        f.protectedMethod();
        f.publicMethod();
    }
}

import com.huang.d9_modifer.Fu;

public class Zi extends Fu {
    public void test(){
        // privateMethod(); // 报错
        // method(); // 报错
        protectedMethod();
        publicMethod();
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Fu f = new Fu();
        // f.privateMethod(); // 报错
        // f.method();
        // f.protecedMethod();
        f.publicMethod();

        Zi zi = new Zi();
        // zi.protectedMethod();
    }
}

4.继承的两个注意事项

public class Test {
    public static void main(String[] args) {
        // 目标:掌握继承的两个注意事项事项。
        // 1、Java是单继承的:一个类只能继承一个直接父类;Java中的类不支持多继承,但是支持多层继承。
        // 2、Object类是Java中所有类的祖宗。
        A a = new A();

        B b = new B();

        ArrayList list = new ArrayList();
        list.add("java");
        System.out.println(list.toString());
    }
}

class A {} //extends Object{}
class B extends A{}
// class C extends B , A{} // 报错
class D extends B{}

5.继承中的方法重写

1.当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法

2.重写后,方法的访问,java会遵循就近原则

3.使用Override注解,可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好,

4.子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限

5.重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小

6.私有方法、静态方法不能被重写,如果重写会报错

public class A {
    public void print1(){
        System.out.println("111");
    }

    public void print2(int a, int b){
        System.out.println("111111");
    }
}


public class B extends A{
    // 方法重写
    @Override // 安全,可读性好
    public void print1(){
        System.out.println("666");
    }


    // 方法重写
    @Override
    public void print2(int a, int b){
        System.out.println("666666");
    }
}
public class Student extends Object{
    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 +
                '}';
    }
}
public class Student extends Object{
    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 +
                '}';
    }
}

6.子类中访问其他成员的特点

就近原则:现在子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果父类范围还没有找到则报错

重名的成员,可以通过super关键字,指定访问父类的成员:super.父类成员变量/父类成员方法

public class F {
    String name = "父类名字";

    public void print1(){
        System.out.println("==父类的print1方法执行==");
    }
}
public class Z extends F {
    String name = "子类名称";

    public void showName(){
        String name = "局部名称";
        System.out.println(name); // 局部名称
        System.out.println(this.name); // 子类成员变量
        System.out.println(super.name); // 父类的成员变量
    }

    @Override
    public void print1(){
        System.out.println("==子类的print1方法执行了=");
    }

    public void showMethod(){
        print1(); // 子类的
        super.print1(); // 父类的
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:掌握子类中访问其他成员的特点:就近原则。
        Z z = new Z();
        z.showName();
        z.showMethod();
    }
}

7.构造器

1.子类的全部构造器,都会先调用父类的构造器,再执行自己。

2.默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),他会调用父类的无参数构造器。

3.如果父类没有无参数构造器,则我们必须在子类构造器的第一行手动super(...),指定去调用父类的有参构造器

4.任意类的构造器中,是可以通过this(...)去调用该类的其他构造器的。

5.this(...)、super(...)都只能放在构造器的第一行,因此,有了this(...)就不能写super(...),反之亦然

class F{
//    public F(){
//        System.out.println("===父类F的 无参数构造器 执行了===");
//    }

    public F(String name, int age){

    }
}

class Z extends F{
    public Z(){
        // super(); // 默认存在的
        super("播妞", 17);
        System.out.println("===子类Z的 无参数构造器 执行了===");
    }

    public Z(String name){
        // super(); // 默认存在的
        super("播妞", 17);
        System.out.println("===子类Z的 有参数构造器 执行了===");
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:先认识子类构造器的特点,再掌握这个特点的常见应用场景。
        Z z = new Z();
        Z z2 = new Z("播妞");
    }
}

 

public class Test2 {
    public static void main(String[] args) {
        // 目标:搞清楚子类构造器为什么要调用父类构造器,有啥应用场景。
        Teacher t = new Teacher("李四", 36, "Java");
        System.out.println(t.getName());
        System.out.println(t.getAge());
        System.out.println(t.getSkill());
    }
}

class Teacher extends People{
    private String skill;

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

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }
}

class People{
    private String name;
    private int age;

    public People() {
    }

    public People(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 class Test3 {
    public static void main(String[] args) {
        // 目标:掌握在类的构造器中,通过this(...)调用兄弟构造器的作用。
        Student s1 = new Student("李四", 26, "家里蹲大学");

        // 需求:如果学生没有填写学校,那么学校默认就荒
        Student s2 = new Student("张三", 28);
        System.out.println(s2.getName());
        System.out.println(s2.getAge());
        System.out.println(s2.getSchoolName());
    }
}

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

    public Student() {
    }

    public Student(String name, int age){
       this(name, age, "荒");
    }

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

    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 getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }
}

3.多态

1.多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态,Java的属性(成员变量)不谈多态

2.有继承/实现关系;存在父类引用子类对象;存在方法重写

1.认识多态

public class People {
    String name = "父类";
    public void run(){
        System.out.println("人会跑");
    }
}
public class Student extends People{
    String name = "学生";
    @Override
    public void run() {
        System.out.println("学生跑的飞快~~~");
    }

    public void study(){
        System.out.println("学生好好学习~~~");
    }
}
public class Teacher extends People{
    String name = "老师";
    @Override
    public void run() {
        System.out.println("老师跑的比较慢~~~");
    }

    public void teach(){
        System.out.println("老师教授Java~~~");
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:认识多态
        People p1 = new Teacher();
        System.out.println(p1.name);
        p1.run();

        People p2 = new Student();
        System.out.println(p2.name);
        p2.run();
    }
}

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握使用多态的好处,以及多态下的类型转换问题。
//        People p1 = new Student();
//        p1.run();

//        Teacher t = new Teacher();
//        go(t);

        Student s = new Student();
        go(s);
    }

    public static void go(People p){
        System.out.println("开始------------------------");
        p.run();
        if(p instanceof Student){
            Student s = (Student) p;
            s.study();
        }else if(p instanceof Teacher){
            Teacher t = (Teacher) p;
            t.teach();
        }
        System.out.println("结束------------------------");
    }
}

2.使用多态的好处

1.右边对象是解耦合的,更便于扩展和维护

2.定义方法时,使用父类类型的形参,可以接受一切子类对象,扩展性更强、更便利

3.不能使用子类的独有方法,

3. 强制类型转换

1.可以把对象转换成其真正的类型,从而解决了多态下不能调用子类独有方法的问题

2.存在继承/实现,就可以进行强制类型转换,编译阶段不会报错,但是,运行时,如果发现对象的真实类型与强转后的类型不同会报错(ClassCastException)

3.使用instanceof判断当前对象的真实类型:对象 instanceof 类型

 4.final

1.final关键字是最终的意思,可以修饰(类、方法、变量)

2.修饰类:该类被称为最终类,特点是不能被继承了。

3.修饰方法:该方法被称为最终方法,特点是不能被重写了。

4.修饰变量:该变量只能被赋值一次。

final修饰变量的注意

5.final修饰基本类型的变量,变量存储的数据不能被改变。

6.final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。

 1.认识常量

常量
●使用了static final修饰的成员变量就被称为常量;
●作用:通常用于记录系统的配置信息。
public class Constant {
public static final String SCHOOL NANE =“完美世界";
}
注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来。
使用常量记录系统配置信息的优势、执行原理
●代码可读性更好,可维护性也更好。
●程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,
这样可以保证使用常量和直接用字面量的性能是一样的。
 

public class Constant {
    // 常量
    public static final String SCHOOL_NAME = "完美世界";
}

2.基本使用

public class FinalDemo1 {
    public static void main(String[] args) {
        // 目标:掌握final关键字的基本作用。

        // 变量有几种? 局部变量  成员变量(类变量,实例变量)
        final double a = 3.14;
        // a = 3.15; // 第二次赋值

        pay(0.8);

        E e = new E();
        // e.name = "李四";
        // E.schoolName = "修仙"; // 第二次赋值

        // 注意事项:
        final int a2 = 10;
        // a2 = 23;

        final int[] arr1 = {10, 20, 30};
        // arr1 = null; // 第二次赋值
        arr1[1] = 222;
        System.out.println(arr1[1]);
    }

    public static void pay(final double z){
        // z = 0.9; // 第二次赋值
    }
}

class E{
    final String name = "张三";
    final static String schoolName = "荒"; // 常量
}

// final的作用:修饰类,类不能被继承了
//final class A{}
//class B extends A{}

class C{
    // final修饰方法,方法不能被重写了
    public final void test(){

    }
}

class D extends C{
//    @Override
//    public void test() {
//
//    }
}

5.抽象类

1、抽象类、抽象方法是什么样的?
●都是用abstract修饰的; 抽象方法只有方法签名,不能写方法体。
2、抽象类有哪些注意事项和特点?
●抽象类中可以不写抽象方法, 但有抽象方法的类一定是抽象类
●类有的成员 (成员变量、方法、构造器)抽象类都具备。
●抽象类不能创建对象, 仅作为一种特殊的父类,让子类继承并实现。
● 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
 

 1.认识抽象类

// 抽象类
public abstract class A {
    private String name;
    static String schoolName;

    public A(){

    }

    // 抽象方法 必须有abstract修饰,只有方法签名,没有方法体
    public abstract void test();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public abstract class Animal {
    private String name;

    public abstract void cry();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class B extends A {
    @Override
    public void test() {

    }
}

public class Cat extends Animal{
    public void cry(){
        System.out.println(getName() + "喵喵喵的叫~~");
    }
}

public class Dog extends Animal{
    public void cry(){
        System.out.println(getName() + "汪汪汪的叫~~");
    }
}
public class Pig extends Animal{
    @Override
    public void cry() {
        System.out.println(getName() + "嚯嚯嚯~~~");
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:认识抽象类,掌握抽象类的基本作用。

        // 抽象类不能创建对象
        // A a = new A();

        B b = new B();
        b.setName("B");
        b.test();
    }
}

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握抽象类的使用场景和好处.
        Animal a = new Pig();
        a.cry();
    }
}

2.抽象类的好处

1、抽象类的应用场景和好处是什么?
●父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定
义成抽象方法,交给子类去重写实现,我们抽出这样的抽象类,就是为了更好
的支持多态。
 

3.模板方法 

1、模板方法设计模式解决了什么问题?
●解决方法中存在重复代码的问题。
2、模板方法设计模式应该怎么写?
●定义一个抽象类。
●在里面定义2个方法, 一个是模板方法:放相同的代码里,一个是抽象方法:具体实现交给子类完成。
3、模板方法建议使用什么关键字修饰?为什么
●建议使用final关键字修饰模板方法。
 

public class A extends C{
    @Override
    public void doSing() {
        System.out.println("我是一只小小小小鸟,想要飞就能飞的高~~~");
    }
}

public class B extends C{
    @Override
    public void doSing() {
        System.out.println("我们一起学猫叫,喵喵喵喵喵喵喵~~");
    }
}
public abstract class C {
    // 模板方法
    public final void sing(){
        System.out.println("唱一首你喜欢的歌:");

        doSing();

        System.out.println("唱完了!");
    }

    public abstract void doSing();
}

public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚模板方法设计模式能解决什么问题,以及怎么写。
        B b = new B();
        b.sing();

    }
}

6.接口

1、使用接口有啥好处,第一个好处是什么?

可以解决类单继承的问题,通过接口,我们可以让一个类有一个亲爹的同时,还可以找多个干
爹去扩展自己的功能。
2、 为什么我们要通过接口,也就是去找干爹,来扩展自己的功能呢?
因为通过接口去找干爹,别人通过你implements的接口,就可以显性的知道你是谁,从而也
就可以放心的把你当作谁来用了。
3、 使用接口的第二个好处是什么?
●一个类我们说可以实现多个接口,同样,一个接口也可以被多个类实现的。这样做的好处是我们的程序就可以面向接口编程了,这样我们程序员就可以很方便的灵活切换各种业务实现了。

1.认识

public interface A {
    // 成员变量(常量)
    String SCHOOL_NAME = "完美世界";

    // 成员方法(抽象方法)
    void test();

}
public interface B {
    void testb1();
    void testb2();
}
public interface C {
    void testc1();
    void testc2();
}
// 实现类
public class D implements B, C{
    @Override
    public void testb1() {

    }

    @Override
    public void testb2() {

    }

    @Override
    public void testc1() {

    }

    @Override
    public void testc2() {

    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:认识接口。
        System.out.println(A.SCHOOL_NAME);

        // A a = new A();
        D d = new D();
    }
}

 

2.好处

public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚使用接口的好处。
        Driver s = new A();
        s.drive();

        Driver d = new A();
        d.drive();

        Animal a = new Cat();
        a.cry();
    }
}

class B implements Driver{
    @Override
    public void drive() {

    }
}

class A extends Student implements Driver, Singer{
    @Override
    public void drive() {

    }

    @Override
    public void sing() {

    }
}

class Student{

}

interface Driver{
    void drive();
}

interface Singer{
    void sing();
}

3.案例

public class ClassManager {
    private ArrayList<Student> students = new ArrayList<>();
    private StudentOperator studentOperator = new StudentOperatorImpl2();

    public ClassManager(){
        students.add(new Student("迪丽热巴", '女', 99));
        students.add(new Student("古力娜扎", '女', 100));
        students.add(new Student("马尔扎哈", '男', 80));
        students.add(new Student("卡尔扎巴", '男', 60));
    }

    // 打印全班全部学生的信息
    public void printInfo(){
        studentOperator.printAllInfo(students);
    }

    // 打印全班全部学生的平均分
    public void printScore(){
        studentOperator.printAverageScore(students);
    }
}
public class Student {
    private String name;
    private char sex;
    private double score;

    public Student() {
    }

    public Student(String name, char sex, double score) {
        this.name = name;
        this.sex = sex;
        this.score = score;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}
public interface StudentOperator {
    void printAllInfo(ArrayList<Student> students);
    void printAverageScore(ArrayList<Student> students);
}
public class StudentOperatorImpl1 implements StudentOperator{
    @Override
    public void printAllInfo(ArrayList<Student> students) {
        System.out.println("----------全班全部学生信息如下--------------");
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println("姓名:" + s.getName() + ", 性别:" + s.getSex() + ", 成绩:" + s.getScore());
        }
        System.out.println("-----------------------------------------");
    }

    @Override
    public void printAverageScore(ArrayList<Student> students) {
        double allScore = 0.0;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            allScore += s.getScore();
        }
        System.out.println("平均分:" + (allScore) / students.size());
    }
}
public class StudentOperatorImpl2 implements StudentOperator{
    @Override
    public void printAllInfo(ArrayList<Student> students) {
        System.out.println("----------全班全部学生信息如下--------------");
        int count1 = 0;
        int count2 = 0;
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println("姓名:" + s.getName() + ", 性别:" + s.getSex() + ", 成绩:" + s.getScore());
            if(s.getSex() == '男'){
                count1++;
            }else {
                count2 ++;
            }
        }
        System.out.println("男生人数是:" + count1  + ", 女士人数是:" + count2);
        System.out.println("班级总人数是:" + students.size());
        System.out.println("-----------------------------------------");
    }

    @Override
    public void printAverageScore(ArrayList<Student> students) {
        double allScore = 0.0;
        double max = students.get(0).getScore();
        double min = students.get(0).getScore();
        for (int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            if(s.getScore() > max) max = s.getScore();
            if(s.getScore() < min) min = s.getScore();
            allScore += s.getScore();
        }
        System.out.println("学生的最高分是:" + max);
        System.out.println("学生的最低分是:" + min);
        System.out.println("平均分:" + (allScore - max - min) / (students.size() - 2));
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:完成班级学生信息管理的案例。
        ClassManager clazz = new ClassManager();
        clazz.printInfo();
        clazz.printScore();
    }
}

 4.接口新增方法

1、JDK8开始,接口中新增了哪些方法?
●默认方法:使用default修饰,使用实现类的对象调用。
●静态方法: static修饰, 必须用当前接口名调用
●私有方法: private修饰,jdk9开始才有的,只能在接口内部被调用。
●他们都会默认被public修饰。
2、JDK8开始,接口中为啥要新增这些方法?
●增强了接口的能力, 更便于项目的扩展和维护。
 

public interface A {
    /**
     * 1、默认方法:必须使用default修饰,默认会被public修饰
     * 实例方法:对象的方法,必须使用实现类的对象来访问。
     */
    default void test1(){
        System.out.println("===默认方法==");
        test2();
    }

    /**
     * 2、私有方法:必须使用private修饰。(JDK 9开始才支持的)
     *   实例方法:对象的方法。
     */
    private void test2(){
        System.out.println("===私有方法==");
    }

    /**
     * 3、静态方法:必须使用static修饰,默认会被public修饰
     */
     static void test3(){
        System.out.println("==静态方法==");
     }

     void test4();
     void test5();
     default void test6(){

     }
}

public class B implements A{
    @Override
    public void test4() {

    }

    @Override
    public void test5() {

    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:掌握接口新增的三种方法形式。
        B b = new B();
        b.test1();
        // b.test2();
        A.test3();
    }
}

5.注意事项

接口其他注意事项(了解)
1、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
2、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
3、一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

public class Test {
    public static void main(String[] args) {
        // 目标:理解接口的多继承。
    }
}

interface A{
    void test1();
}
interface B{
    void test2();
}
interface C{}

// 接口是多继承的
interface D extends C, B, A{

}
public class Test2 {
    public static void main(String[] args) {
        // 目标:了解使用接口的几点注意事项。

//        Zi zi = new Zi();
//        zi.run();

    }
}

 1、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
//interface I{
//    void test1();
//}
//interface J{
//    String test1();
//}
interface K extends I, J{

}
//
 2、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
class E implements I, J{

}
//
 3、一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
//class Fu{
//    public void run(){
//        System.out.println("===父类的run方法执行了===");
//    }
//}
//interface IT{
//    default void run(){
//        System.out.println("===接口IT中的run方法执行了===");
//    }
//}
//class Zi extends Fu implements IT {
//
//}
//
 4、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
//interface It1{
//    default void test(){
//        System.out.println("IT1");
//    }
//}
//
//interface IT2{
//    default void test(){
//        System.out.println("IT2");
//    }
//}
//
//class N implements It1, IT2{
//    @Override
//    public void test() {
//        System.out.println("自己的");
//    }
//}

7.内部类

1.成员内部类

1.成员内部类是什么?如何创建其对象?
●就是类中的一个普通成员,类似前面我们学过的普通成员变量、成员方法
●外部类名.内部类名 对象名= new外部类(..).new 内部类(..);
2.成员内部类的实例方法中,访问其他成员有啥特点?
●可以直接访问外部类的实例成员、 静态成员
●可以拿到当前外部类对象,格式是:外部类名.this。
 

public class Outer {
    private int age = 99;
    public static String a;
    // 成员内部类
    public class Inner{
        private String name;
        public static String schoolName; // JDK 16开始才支持定义静态成员的
        private int age = 88;

        public void test(){
            System.out.println(age);
            System.out.println(a);

            int age = 66;
            System.out.println(age);// 66
            System.out.println(this.age);// 88
            System.out.println(Outer.this.age);// 99
        }

        public String getName() {
            return name;
        }

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

    public void test2(){
        System.out.println(age);
        System.out.println(a);
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:了解成员内部类和其特点。
        Outer.Inner in = new Outer().new Inner();
        in.test();
    }
}

 

2.静态内部类

1.什么是静态内部类?如何创建对象?有啥特点?
●有static修饰的内部类。
●外部类名.内部类名 对象名= new外部类.内部类(..);
●可以直接访问外部类的静态成员,不能直接访问外部类的实例成员。
 

public class Outer {
    private int age = 99;
    public static String schoolName;
    // 静态内部类
    public static class Inner{
        private String name;
        public static int a;
        private int age = 88;

        public void test(){
            System.out.println(schoolName);
            // System.out.println(age);
        }

        public String getName() {
            return name;
        }

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

    public static void test2(){
        System.out.println(schoolName);
        // System.out.println(age);
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:了解静态内部类。
        Outer.Inner in = new Outer.Inner();
        in.test();
    }
}

3.局部内部类

局部内部类
●局部内部类是定义在在方法中、代码块中、构造器等执行体中。
public class Test 
public static void main(String[] args) {
}
public static void go(){
class A{}
abstract class B{}
interface C{
}
}
 

4.匿名内部类

1、匿名内部类的书写格式是什么样的?
new类或接口(参数值..){

类体(一般是方法重写);

};
new Animal(){
@Override
public void cry() {
};
};
2、匿名内部类有啥特点?
●匿名内部类本质就是一个子类,并会立即创建出一个子类对象
3、匿名内部类有啥作用、应用场景?
●可以更方便的创建出一个子类对象。
●匿名内部类通常作为一个参数传输给方法。

public class Test {
    public static void main(String[] args) {
        // 目标:认识匿名内部类,并掌握其作用。
//        Animal a = new Cat();
//        a.cry();

        // 1、把这个匿名内部类编译成一个子类,然后会立即创建一个子类对象出来。
        Animal a = new Animal(){
            @Override
            public void cry() {
                System.out.println("猫喵喵喵的叫~~~");
            }
        };
        a.cry();
    }
}

//class Cat extends Animal{
//    @Override
//    public void cry() {
//        System.out.println("猫喵喵喵的叫~~~");
//    }
//}

abstract class Animal{
    public abstract void cry();
}

 

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握匿名的常见使用场景。
//        Swimming s1 = new Swimming(){
//            @Override
//            public void swim() {
//                System.out.println("狗🏊‍飞快~~~~");
//            }
//        };
//        go(s1);

        go(new Swimming(){
            @Override
            public void swim() {
                System.out.println("狗🏊‍飞快~~~~");
            }
        });

    }

    // 设计一个方法,可以接收swimming接口的一切实现类对象进来参加游泳比赛。
    public static void go(Swimming s){
        System.out.println("开始-----------------------");
        s.swim();
    }
}



// 猫和狗都要参加游泳比赛
interface Swimming{
    void swim();
}

 

8.枚举

枚举类的特点:
Compiled from "A. java"
public final class A extends java. lang. Enum<A> {
public enum A{
X,Y, z;
public static final A X = new A();
public static final A Y = new A();
public static final A z = new A();
public static A[] values();
public static A value0f(java.1ang. String);
|}
●枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
●枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
●枚举都是最终类,不可以被继承。
●枚举类中,从第二行开始,可以定义类的其他各种成员。
编译器为枚举类新增了几个方法,并且枚举类都是继承: java.lang.Enum类的, 从enum类也会继承到一些方法。

public enum Constant {
//    public static final int BOY = 0;
//    public static final int GIRL = 1;
    BOY, GIRL;
}
// 枚举类
public enum A {
    // 常量,每个常量都是记住枚举类的一个对象的。
    X, Y, Z;

    A(){
    }

    A(String name){
    }
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args) {
        // 目标:认识枚举类。
        // A.X = null;
        A a1 = A.X;
        A a2 = A.Y;
        A a3 = A.Z;
        //Enum类提供了一个ordinal()方法,用来返回枚举对象的序数,比如本例中SPRING, SUMMER, AUTUMN, WINTER的序数就分别是0,1,2,3。
        System.out.println(a2.ordinal());
    }
}

 

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握枚举类的使用场景。
        provideInfo(Constant.BOY);
    }

    public static void provideInfo(Constant sex){
        switch (sex) {
            case BOY:
                // 男生
                System.out.println("展示了一些信息给男生观看~~~");
                break;
            case GIRL:
                // 女生
                System.out.println("展示了一些信息给女生观看~~~");
                break;
        }
    }
}

 

 9.泛型

泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如: <E>), 称为泛型类、泛型接口,泛型方法、它们统称为泛型。
public class ArrayList<E>{

...

}
●作用: 泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。
●泛 型的本质:把具体的数据类型作为参数传给类型变量。
 

public class Test1 {
    public static void main(String[] args) {
        // 目标:认识泛型
        ArrayList list = new ArrayList();
        list.add("java1");
        list.add("java2");
        list.add("java3");
        // list.add(new Cat());

        for (int i = 0; i < list.size(); i++) {
            String e = (String) list.get(i);
            System.out.println(e);
        }

        System.out.println("-----------------------------------");
//        ArrayList<String> list1 = new ArrayList<String>();
        ArrayList<String> list1 = new ArrayList<>(); // JDK 1.7开始,后面的数据类型可以不声明
        list1.add("java1");
        list1.add("java2");
        list1.add("java3");
        // list1.add(new Cat());

        for (int i = 0; i < list1.size(); i++) {
            String e = list1.get(i);
            System.out.println(e);
        }

    }
}

class Cat{}

1.泛型类

public class Animal {
}
public class Cat extends Animal{
}
public class Dog extends Animal{
}
// 泛型类
public class MyArrayList<E> {
    private Object[] arr = new Object[10];
    private int size; // 记录当前位置的

    public boolean add(E e){
        arr[size++] = e;
        return true;
    }

    public E get(int index){
        return (E) arr[index];
    }
}
public class MyClass2<E, T> {
    public void put(E e, T t){

    }
}

public class MyClass3<E extends Animal> {

}

public class Test {
    public static void main(String[] args) {
        // 目标:掌握泛型类的定义和使用。
        MyArrayList<String> list = new MyArrayList<>();
        list.add("java1");
        list.add("java2");
        String ele = list.get(1);
        System.out.println(ele);

        MyClass2<Cat, String> c2 = new MyClass2<>();

        // MyClass3<String> c3 = new MyClass3<>(); // 报错
        MyClass3<Animal> c4 = new MyClass3<>();
        MyClass3<Dog> c5 = new MyClass3<>();
    }
}

 2.泛型接口

// 泛型接口
public interface Data<T> {
//public interface Data<T extends Animal> {
    void add(T t);
    ArrayList<T> getByName(String name);
}
public class Student {
}
public class StudentData implements Data<Student>{
    @Override
    public void add(Student student) {

    }

    @Override
    public ArrayList<Student> getByName(String name) {
        return null;
    }
}
public class Teacher {
}
public class TeacherData implements Data<Teacher>{
    @Override
    public void add(Teacher teacher) {

    }

    @Override
    public ArrayList<Teacher> getByName(String name) {
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        // 目标:掌握泛型接口的定义和使用。
        // 场景:系统需要处理学生和老师的数据,需要提供2个功能:保存对象数据。根据名称查询数据。
    }
}

3.泛型方法

泛型方法
修饰符<类型变量,类型变量,..>. 返回值类型方法名(形参列表) {
public static <T> void test(T t){}

错误:
public E get(int index){
return (E) arr[index];
}
通配符
●就是“?”,可以在“使用泛型”的时候代表一切类型; ETKV是在定义泛型的时候使用。.
泛型的上下限:
●泛型上限: ? extends Car: ?能接收的必须是Car或者其子类。
●泛型下限: ? superCar : ?能接收的必须是Car或者其父类。、
 

public class BENZ extends Car{
}

public class BMW extends Car{
}

public class Car {
}

public class Dog {
}

public class Test {
    public static void main(String[] args) {
        // 目标:掌握泛型方法的定义和使用。
        String rs = test("java");
        System.out.println(rs);

        Dog d = test(new Dog());
        System.out.println(d);

        // 需求:所有的汽车可以一起参加比赛。
        ArrayList<Car> cars = new ArrayList<>();
        cars.add(new BMW());
        cars.add(new BENZ());
        go(cars);

        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        bmws.add(new BMW());
        go(bmws);

        ArrayList<BENZ> benzs = new ArrayList<>();
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        go(benzs);

//        ArrayList<Dog> dogs = new ArrayList<>();
//        dogs.add(new Dog());
//        dogs.add(new Dog());
//        go(dogs);
    }

    // ? 通配符,在使用泛型的时候可以代表一切类型   ? extends Car(上限)   ? super Car(下限)
    public static void go(ArrayList<? extends Car> cars){

    }

//    public static <T extends Car> void go(ArrayList<T> cars){
//
//    }

    // 泛型方法
    public static <T> T test(T t){
        return t;
    }
}

 4.泛型注意事项.

泛型的擦除问题和注意事项
●泛型是工作在编译阶段的,一旦程序编译成class文件, class文件中就不存在泛型了,这就是泛型擦除。
●泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
 

public class Test {
    public static void main(String[] args) {
        // 目标:理解泛型的注意事项。
        // 1、泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
        ArrayList<String> list = new ArrayList<>();
        list.add("java1");
        list.add("java2");
        list.add("java3");
        String rs = list.get(2);
        System.out.println(rs);

        // 2、泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
//        ArrayList<int> list1 = new ArrayList<>();
//        ArrayList<double> list2 = new ArrayList<>();
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(12);

        ArrayList<Double> list2 = new ArrayList<>();
        list2.add(23.3);

        double a = 0.1;
        double b = 0.2;
        double c = a + b;
        System.out.println(a);
        BigDecimal a1 = new BigDecimal(0.1);
    }
}

10.常用api

1.object类

1、Object中toString方法的作用是什么?存在的意义是什么?
●基本作用:返回对象的字符串形式。
●存在的意义:让子类重写,以便返回子类对象的内容。
2、Object中equals方 法的作用是什么?存在的意义是什么?
●基本作用:默认是比较两个对象的地址是否相等。
●存在的意义:让子类重写,以便用于比较对象的内容是否相同。
 

public class Student { // extends Object{
    private String name;
    private int age;

    public Student() {
    }

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

    // 重写equals方法,比较两个对象的内容一样就返回true.
    // 比较者:s2 == this
    // 被比较者:s1 == o
    @Override
    public boolean equals(Object o) {
        // 1、判断两个对象是否地址一样,一样直接返回true.
        if (this == o) return true;
        // 2、判断o是null直接返回false, 或者比较者的类型与被比较者的类型不一样,返回false
        //               Student.class !=  Pig.class
        if (o == null || this.getClass() != o.getClass()) return false;
        // 3、o不是null,且o一定是学生类型的对象。开始比较内容了!
        Student student = (Student) o;
        return this.age == student.age && Objects.equals(this.name, student.name);
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

public class Test {
    public static void main(String[] args) {
        // 目标:掌握Object类提供的常用方法。
        Student s1 = new Student("赵敏", 23);
        // System.out.println(s1.toString());
        System.out.println(s1);

        Student s2 = new Student("赵敏", 23);
        System.out.println(s2.equals(s1));
        System.out.println(s2 == s1);
    }
}

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 目标:掌握Object类提供的对象克隆的方法。
        // 1、protected Object clone():对象克隆。

        User u1 = new User(1, "zhangsan",
                "wo666",new double[]{99.0, 99.5});
        System.out.println(u1.getId());
        System.out.println(u1.getUsername());
        System.out.println(u1.getPassword());
        System.out.println(u1.getScores());

        User u2 = (User) u1.clone();
        System.out.println(u2.getId());
        System.out.println(u2.getUsername());
        System.out.println(u2.getPassword());
        System.out.println(u2.getScores());
    }
}
/ Cloneable是一个标记接口。
// 规则。
public class User implements Cloneable{
    private int id; // 编号
    private String username; // 用户名
    private String password; // 密码
    private double[] scores; // 分数

    public User(){}

    public User(int id, String username,
                String password, double[] scores) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.scores = scores;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // super去调用父类Object中的clone方法。
        User u2 = (User) super.clone();
        u2.scores = u2.scores.clone();
        return u2;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public double[] getScores() {
        return scores;
    }

    public void setScores(double[] scores) {
        this.scores = scores;
    }
}

 2.objects

0bjects
●Objects是一 个工具类, 提供了很多操作对象的静态方法给我们使用。
Objects类的常见方法
public static boolean equals (object a, object b)先做非空判断, 再比较两个对象
public static boolean isNull(Object obj)
判断对象是否为nu1l,为null返回true ,反之
public static boolean nonNull(Object obj)
判断对象是否不为null,不为nul1则返回true,反之
源码分析
public static boolean equals(Object a, Object b) {
return (a == b) | | (a != null && a.equals(b));
}
 

public class Test {
    public static void main(String[] args) {
        // 目标:掌握Objects类提供的常用方法。
        String s1 = null;
        String s2 = "itheima";

        // System.out.println(s1.equals(s2));
        System.out.println(Objects.equals(s1, s2)); // 更安全,更好的!

        System.out.println(Objects.isNull(s1)); // true
        System.out.println(s1 == null); // true
        System.out.println(Objects.isNull(s2)); // false
        System.out.println(s2 == null );// false

        System.out.println(Objects.nonNull(s2)); // true
        System.out.println(Objects.nonNull(s1)); // false
    }
}

3.包装类

包装类
●包装类就是把基本类型的数据包装成对象。
基本数据类型
对应的包装类(引用数据类型)
byte         Byte

short        Short
int        Integer

long        Long
char        Character
float       Float
double        Double
boolean        Boolean 

基本类型的数据包装成对象的方案
public Integer(int value):已过时
public static Integer valueOf(int i)

自动装箱:基本数据类型可以自动转换为包装类型。自 动拆箱:包装类型可以自动转换为基本数据类型。

包装类的其他常见操作
●可以把基本类型的数据转换成字符串类型。
public static String toString(double d)
public String toString()
●可以把字符 串类型的数值转换成数值本身对应的数据类型。
public static int parselnt(String s)
public static Integer valueOf(String s)
 

public class Test {
    public static void main(String[] args) {
        // 目标:掌握包装类的使用。
        // Integer a1 = new Integer(12);
        Integer a2 = Integer.valueOf(12);
        System.out.println(a2);

        // 自动装箱:可以自动把基本类型的数据转换成对象。
        Integer a3 = 12;

        // 自动拆箱:可以自动把包装类型的对象转换成对应的基本数据类型。
        int a4 = a3;

        // 泛型和集合不支持基本数据类型,只能支持引用数据类型。
        // ArrayList<int> list = new ArrayList<>();
        ArrayList<Integer> list = new ArrayList<>();
        list.add(12); // 自动装箱
        list.add(13); // 自动装箱

        int rs = list.get(1); // 自动拆箱
        System.out.println("--------------------------------------------------");

        // 1、把基本类型的数据转换成字符串
        Integer a = 23;
        String rs1 = Integer.toString(a); // "23"
        System.out.println(rs1 + 1); // 231

        String rs2 =  a.toString(); // "23"
        System.out.println(rs2 + 1);

        String rs3 = a + "";
        System.out.println(rs3 + 1);

        // 2、把字符串类型的数值转换成对应的基本类型。
        String ageStr = "29";
        // int ageI = Integer.parseInt(ageStr); // 29
        int ageI = Integer.valueOf(ageStr); // 29
        System.out.println(ageI + 1); // 30

        String scoreStr = "99.5";
        // double score = Double.parseDouble(scoreStr); // 99.5
        double score = Double.valueOf(scoreStr); // 99.5
        System.out.println(score + 0.5);

    }
}

4.StringBuilder

StringBuilder
●StringBuilder代表 可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的。
●好处: StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁。
构造器public StringBuilder()
创建一个空白的可变的字符串对象,不包含任何内容
public StringBuilder(String str)
创建一个指定字符串内容的可变字符串对象
public StringBuilder append(任意类型)添加数据并返 回StringBuilder对象本身
public StringBuilder reverse( )
将对象的内容反转
public int length()
返回对象内容长度
public String toString()
通过toString()就可以实现把StringBuilder转换为String
 

public class Test1 {
    public static void main(String[] args) {
        // 目标:搞清楚StringBuilder的用法和作用。
        // StringBuilder s = new StringBuilder(); // s ""
        StringBuilder s = new StringBuilder("huang3"); // s "itheima"

        // 1、拼接内容
        s.append(12);
        s.append("huang");
        s.append(true);

        // 支持链式编程
        s.append(666).append("huang2").append(666);
        System.out.println(s);

        // 2、反转操作
        s.reverse();
        System.out.println(s);

        // 3、返回字符串的长度
        System.out.println(s.length());

        // 4、把StringBuilder对象又转换成String类型。
        String rs = s.toString();
        System.out.println(rs);
    }
}

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握StringBuilder的好处。
        // 需求:要拼接100万次abc
        // 先用String测试看看性能
//        String rs = "";
//        for (int i = 1; i <= 1000000 ; i++) {
//            rs = rs + "abc";
//        }
//        System.out.println(rs);

        // 使用StringBuilder演示
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 1000000; i++) {
            sb.append("abc");
        }
        System.out.println(sb);
    }
}
public class Test3 {
    public static void main(String[] args) {
        // 目标:完成遍历数组内容,并拼接成指定格式的案例。
        System.out.println(getArrayData(new int[]{11, 22, 33}));
    }

    public static String getArrayData(int[] arr){
        // 1、判断arr是否为null
        if(arr == null){
            return null;
        }

        // 2、arr数组对象存在。 arr = [11, 22, 33]
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < arr.length; i++) {
            if(i == arr.length - 1){
                sb.append(arr[i]);
            }else {
                sb.append(arr[i]).append(", ");
            }
        }
        sb.append("]");

        return sb.toString();
    }
}

5.StringJoiner

StringJoiner
●JDK8开始才有的,跟StringBuilder- -样,也是用来操作字符串的,也可以看成是一一个容器,创建之后里面的内容是可变的。
●好处:不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
构造器public StringJoiner (间隔符号)
创建一个stringJoiner对象, 指定拼接时的间隔符号
public stringJoiner (间隔符号,开始符号,结束符号)创建一 个StringJoiner对象, 指定拼接时的间隔符号、开始符号、结束符号
public StringJoiner add (添加的内容)
添加数据,并返回对象本身
public int length()
返回长度(字符出现的个数)
public String toString()
返回一个字符串(该字符串就是拼接之后的结果)
 

public class Test {
    public static void main(String[] args) {
        // 目标:掌握StringJoiner的使用。
        // StringJoiner s = new StringJoiner(", "); // 间隔符!
        StringJoiner s = new StringJoiner(", ", "[", "]"); // 间隔符!
        s.add("java1");
        s.add("java2");
        s.add("java3");
        System.out.println(s); // [java1, java2, java3]

        System.out.println(getArrayData(new int[]{11, 22, 33}));
    }

    public static String getArrayData(int[] arr){
        // 1、判断arr是否为null
        if(arr == null){
            return null;
        }
        // 2、arr数组对象存在。 arr = [11, 22, 33]
        StringJoiner s = new StringJoiner(", ", "[", "]");
        for (int i = 0; i < arr.length; i++) {
            s.add(arr[i] + "");
        }
        return s.toString();
    }
}

6.math

Math
●代表数学,是一个工具类,里面提供的都是对数据进行操作的一-些静态方法。
Math类提供的常见方法
方法名
说明
public static int abs(int a)
获取参数绝对值
public static double ceil(double a)
向上取整
public static double floor(double a)
向下取整
public static int round(float a)
四舍五入
public static int max(int a,int b)
获取两个int值中的较大值
public static double pow(double a, double b)
返回a的b次幂的值
public static double random()
返回值为double的随机值,范围[0.0,1.0)
 

public class MathTest {
    public static void main(String[] args) {
        // 目标:了解下Math类提供的常见方法。
        // 1、public static int abs(int a):取绝对值(拿到的结果一定是正数)
        //    public static double abs(double a)
        System.out.println(Math.abs(-12)); // 12
        System.out.println(Math.abs(123)); // 123
        System.out.println(Math.abs(-3.14)); // 3.14

        // 2、public static double ceil(double a): 向上取整
        System.out.println(Math.ceil(4.0000001)); // 5.0
        System.out.println(Math.ceil(4.0)); // 4.0

        // 3、public static double floor(double a): 向下取整
        System.out.println(Math.floor(4.999999)); // 4.0
        System.out.println(Math.floor(4.0)); // 4.0

        // 4、public static long round(double a):四舍五入
        System.out.println(Math.round(3.4999)); // 3
        System.out.println(Math.round(3.50001)); // 4

        // 5、public static int max(int a, int b):取较大值
        //   public static int min(int a, int b):取较小值
        System.out.println(Math.max(10, 20)); // 20
        System.out.println(Math.min(10, 20)); // 10

        // 6、 public static double pow(double a, double b):取次方
        System.out.println(Math.pow(2, 3)); // 2的3次方   8.0
        System.out.println(Math.pow(3, 2)); // 3的2次方   9.0

        // 7、public static double random(): 取随机数 [0.0 , 1.0) (包前不包后)
        System.out.println(Math.random());
    }
}

 7.System

System
●System代表程序所在的系统, 也是一一个工具类。
System类提供的常见方法
方法名
说明
public static void exit(int status)
终止当前运行的Java虚拟机。
public static long currentTimeMillis()
返回当前系统的时间毫秒值形式
 

/**
 * 目标:了解下System类的常见方法。
 */
public class SystemTest {
    public static void main(String[] args) {

        // 1、public static void exit(int status):
        //   终止当前运行的Java虚拟机。
        //   该参数用作状态代码; 按照惯例,非零状态代码表示异常终止。
         System.exit(0); // 人为的终止虚拟机。(不要使用)

        // 2、public static long currentTimeMillis():
        //    获取当前系统的时间
        //    返回的是long类型的时间毫秒值:指的是从1970-1-1 0:0:0开始走到此刻的总的毫秒值,1s = 1000ms
        long time = System.currentTimeMillis();
        System.out.println(time);

        for (int i = 0; i < 1000000; i++) {
            System.out.println("输出了:" + i);
        }

        long time2 = System.currentTimeMillis();
        System.out.println((time2 - time) / 1000.0 + "s");
    }
}

8.Runtime

 Runtime
●代表程序所在的运行环境。
●Runtime是一 一个单例类。

// 1. public static Runtime getRuntime() 返回与当前Java应用程序关联的运行时对象。| I
// 2. public void exit(int status) 终止当前运行的虚拟机,该多数用作状态代码;按照惯例,非零状态代码表示异常终止。
// 3、public int availableProcessors()
// 4. public long totalMemory() 返回Java虚拟机中的内存总量。
// 5、public long freeMemory() 返回Java 虚拟机中的可用内存量
// 6、public Process exec(String command) 启动某个程序,并返回代表该程序的对象。
 

/**
 *  目标:了解下Runtime的几个常见方法。
 */
public class RuntimeTest {
    public static void main(String[] args) throws IOException, InterruptedException {

        // 1、public static Runtime getRuntime() 返回与当前Java应用程序关联的运行时对象。
        Runtime r = Runtime.getRuntime();

        // 2、public void exit(int status) 终止当前运行的虚拟机,该参数用作状态代码; 按照惯例,非零状态代码表示异常终止。
        // r.exit(0);

        // 3、public int availableProcessors(): 获取虚拟机能够使用的处理器数。
        System.out.println(r.availableProcessors());

        // 4、public long totalMemory() 返回Java虚拟机中的内存总量。
        System.out.println(r.totalMemory()/1024.0/1024.0 + "MB"); // 1024 = 1K     1024 * 1024 = 1M

        // 5、public long freeMemory() 返回Java虚拟机中的可用内存量
        System.out.println(r.freeMemory()/1024.0/1024.0 + "MB");

        // 6、public Process exec(String command) 启动某个程序,并返回代表该程序的对象。
        // r.exec("D:\\soft\\XMind\\XMind.exe");
        Process p = r.exec("QQ");
        Thread.sleep(5000); // 让程序在这里暂停5s后继续往下走!!
        p.destroy(); // 销毁!关闭程序!
    }
}

9.BigDecimal

BigDecimal的常见构造器、常用方法
构造器
说明
public BigDecimal(double val)注意: 不推荐使用这个
将double转 换为BigDecimal
public BigDecimal(String va1)
把string转成BigDecimal
方法名
说明
public static BigDecimal value0f(double val)
转换一个double成BigDecimal
public BigDecimal add(BigDecimal b)
加法
public BigDecimal subtract(BigDecimal b)
减法
public BigDecimal multiply(BigDecimal b)
乘法
public BigDecimal divide(BigDecimal b)
除法
public BigDecimal divide (另一个BigDecimal对象, 精确几位,舍入模式)
除法、可以控制精确到小数几位
public double doubleValue()
将BigDecimal转换为double
 

public class Test {
    public static void main(String[] args) {
        // 目标:掌握BigDecimal的使用。
        System.out.println(0.1 + 0.2);
        System.out.println(1.0 - 0.32);
        System.out.println(1.015 * 100);
        System.out.println(1.301 / 100);
    }
}

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握BigDecimal进行精确运算的方案。
        double a = 0.1;
        double b = 0.2;

        // 1、把浮点型数据封装成BigDecimal对象,再来参与运算。
        // a、public BigDecimal(double val) 得到的BigDecimal对象是无法精确计算浮点型数据的。 注意:不推荐使用这个,
        // b、public BigDecimal(String val)  得到的BigDecimal对象是可以精确计算浮点型数据的。 可以使用。
        // c、public static BigDecimal valueOf(double val): 通过这个静态方法得到的BigDecimal对象是可以精确运算的。是最好的方案。
        BigDecimal a1 = BigDecimal.valueOf(a);
        BigDecimal b1 = BigDecimal.valueOf(b);

        // 2、public BigDecimal add(BigDecimal augend): 加法
        BigDecimal c1 = a1.add(b1);
        System.out.println(c1);

        // 3、public BigDecimal subtract(BigDecimal augend): 减法
        BigDecimal c2 = a1.subtract(b1);
        System.out.println(c2);

        // 4、public BigDecimal multiply(BigDecimal augend): 乘法
        BigDecimal c3 = a1.multiply(b1);
        System.out.println(c3);

        // 5、public BigDecimal divide(BigDecimal b): 除法
        BigDecimal c4 = a1.divide(b1);
        System.out.println(c4);

//        BigDecimal d1 = BigDecimal.valueOf(0.1);
//        BigDecimal d2 = BigDecimal.valueOf(0.3);
//        BigDecimal d3 = d1.divide(d2);
//        System.out.println(d3);

        // 6、public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式) : 除法,可以设置精确几位。
        BigDecimal d1 = BigDecimal.valueOf(0.1);
        BigDecimal d2 = BigDecimal.valueOf(0.3);
        BigDecimal d3 = d1.divide(d2,  2, RoundingMode.HALF_UP); // 0.33
        System.out.println(d3);

        // 7、public double doubleValue() : 把BigDecimal对象又转换成double类型的数据。
        //print(d3);
        //print(c1);
        double db1 = d3.doubleValue();
        double db2 = c1.doubleValue();
        print(db1);
        print(db2);
    }

    public static void print(double a){
        System.out.println(a);
    }
}

10.Date

Date
●代表的是日期和时间。
构造器
说明
public Date()
创建一个Date对象,代表的是系统当前此刻日期时间。
public Date(long time)
把时间毫秒值转换成Date日期对象。
常见方法
说明
public long getTime()
返回从1970年1月1日00:00:00走到 此刻的总的毫秒数
public void setTime(long time)设置日期对象的时间为当前时间毫秒值对应的时间
 

public class Test1Date {
    public static void main(String[] args) {
        // 目标:掌握Date日期类的使用。
        // 1、创建一个Date的对象:代表系统当前时间信息的。
        Date d = new Date();
        System.out.println(d);

        // 2、拿到时间毫秒值。
        long time = d.getTime();
        System.out.println(time);

        // 3、把时间毫秒值转换成日期对象: 2s之后的时间是多少。
        time += 2 * 1000;
        Date d2 = new Date(time);
        System.out.println(d2);

        // 4、直接把日期对象的时间通过setTime方法进行修改
        Date d3 = new Date();
        d3.setTime(time);
        System.out.println(d3);
    }
}

11.SimpleDateFormat

SimpleDateFormat
●代表简单日期格式化,可以用来把日期对象、时间毫秒值格式化成我们想要的形式。
常见构造器
说明
public SimpleDateFormat (String pattern)
创建简单日期格式化对象,并封装时间的格式
格式化时间的方法
说明
public final String format(Date date)
将日期格式化成日期/时间字符串
public final String format(0bject time)
将时间毫秒值式化成日期/时间字符串
 

public class Test2SimpleDateFormat {
    public static void main(String[] args) throws ParseException {
        // 目标:掌握SimpleDateFormat的使用。
        // 1、准备一些时间
        Date d = new Date();
        System.out.println(d);

        long time = d.getTime();
        System.out.println(time);

        // 2、格式化日期对象,和时间 毫秒值。
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");

        String rs = sdf.format(d);
        String rs2 = sdf.format(time);
        System.out.println(rs);
        System.out.println(rs2);
        System.out.println("-------------------------------------------------------------------");

        // 目标:掌握SimpleDateFormat解析字符串时间 成为日期对象。
        String dateStr = "2022-12-12 12:12:11";
        // 1、创建简单日期格式化对象 , 指定的时间格式必须与被解析的时间格式一模一样,否则程序会出bug.
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d2 = sdf2.parse(dateStr);
        System.out.println(d2);
    }
}
public class Test3 {
    public static void main(String[] args) throws ParseException {
        // 目标:完成秒杀案例。
        // 1、把开始时间、结束时间、小贾下单时间、小皮下单时间拿到程序中来。
        String start = "2023年11月11日 0:0:0";
        String end = "2023年11月11日 0:10:0";
        String xj = "2023年11月11日 0:01:18";
        String xp = "2023年11月11日 0:10:57";

        // 2、把字符串的时间解析成日期对象。
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        Date startDt = sdf.parse(start);
        Date endDt = sdf.parse(end);
        Date xjDt = sdf.parse(xj);
        Date xpDt = sdf.parse(xp);

        // 3、开始判断小皮和小贾是否秒杀成功了。
        // 把日期对象转换成时间毫秒值来判断
        long startTime = startDt.getTime();
        long endTime = endDt.getTime();
        long xjTime = xjDt.getTime();
        long xpTime = xpDt.getTime();

        if(xjTime >= startTime && xjTime <= endTime){
            System.out.println("小贾您秒杀成功了~~");
        }else {
            System.out.println("小贾您秒杀失败了~~");
        }

        if(xpTime >= startTime && xpTime <= endTime){
            System.out.println("小皮您秒杀成功了~~");
        }else {
            System.out.println("小皮您秒杀失败了~~");
        }
    }
}

12.Calendar

Calendar
●代表的是系统此刻时间对应的日历,通过它可以单独获取、修改时间中的年、月、日、时、分、秒
Calendar日历类的常见方法
方法名
说明
public static Calendar getInstance()
获取当前日历对象
public int get(int field)
获取日历中的某个信息。
public final Date getTime()
获取日期对象。
public long getTimeInMillis()
获取时间毫秒值
public void set(int field,int value)
修改日历的某个信息。
public void add(int field, int amount )
为某个信息增加/减少指定的值
 

public class Test4Calendar {
    public static void main(String[] args) {
        // 目标:掌握Calendar的使用和特点。
        // 1、得到系统此刻时间对应的日历对象。
        Calendar now = Calendar.getInstance();
        System.out.println(now);

        // 2、获取日历中的某个信息
        int year = now.get(Calendar.YEAR);
        System.out.println(year);

        int days = now.get(Calendar.DAY_OF_YEAR);
        System.out.println(days);

        // 3、拿到日历中记录的日期对象。
        Date d = now.getTime();
        System.out.println(d);

        // 4、拿到时间毫秒值
        long time = now.getTimeInMillis();
        System.out.println(time);

        // 5、修改日历中的某个信息
        now.set(Calendar.MONTH, 9); // 修改月份成为10月份。
        now.set(Calendar.DAY_OF_YEAR, 125); // 修改成一年中的第125天。
        System.out.println(now);

        // 6、为某个信息增加或者减少多少
        now.add(Calendar.DAY_OF_YEAR, 100);
        now.add(Calendar.DAY_OF_YEAR, -10);
        now.add(Calendar.DAY_OF_MONTH, 6);
        now.add(Calendar.HOUR, 12);
        now.set(2026, 11, 22);
        System.out.println(now);
    }
}

/**
 *  目标:搞清楚为什么要用JDK 8开始新增的时间类。
 */
public class Test {
    public static void main(String[] args) {
        // 传统的时间类(Date、SimpleDateFormat、Calendar)存在如下问题:
        // 1、设计不合理,使用不方便,很多都被淘汰了。
        Date d = new Date();
        //System.out.println(d.getYear() + 1900);

        Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        System.out.println(year);

        // 2、都是可变对象,修改后会丢失最开始的时间信息。

        // 3、线程不安全。

        // 4、不能精确到纳秒,只能精确到毫秒。
        // 1秒 = 1000毫秒
        // 1毫秒 = 1000微妙
        // 1微妙 = 1000纳秒
    }
}

13.LocalDate

LocalDate:代表本地日期(年、月、日、星期)
LocalTime:代表本地时间(时、分、秒、纳秒)
LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
它们获取对象的方案
方法名
示例
LocaDate ld = LocalDate.now();
public static Xxxx now():获取系统当前时间对应的该对象
LocalTime lt = LocalTime.now();
LocalDateTime ldt = LocalDateTime.now();


 

public class Test1_LocalDate {
    public static void main(String[] args) {
        // 0、获取本地日期对象(不可变对象)
        LocalDate ld = LocalDate.now(); // 年 月 日
        System.out.println(ld);

        // 1、获取日期对象中的信息
        int year = ld.getYear(); // 年
        int month = ld.getMonthValue(); // 月(1-12)
        int day = ld.getDayOfMonth(); // 日
        int dayOfYear = ld.getDayOfYear();  // 一年中的第几天
        int dayOfWeek = ld.getDayOfWeek().getValue(); // 星期几
        System.out.println(year);
        System.out.println(day);
        System.out.println(dayOfWeek);

        // 2、直接修改某个信息: withYear、withMonth、withDayOfMonth、withDayOfYear
        LocalDate ld2 = ld.withYear(2099);
        LocalDate ld3 = ld.withMonth(12);
        System.out.println(ld2);
        System.out.println(ld3);
        System.out.println(ld);

        // 3、把某个信息加多少: plusYears、plusMonths、plusDays、plusWeeks
        LocalDate ld4 = ld.plusYears(2);
        LocalDate ld5 = ld.plusMonths(2);

        // 4、把某个信息减多少:minusYears、minusMonths、minusDays、minusWeeks
        LocalDate ld6 = ld.minusYears(2);
        LocalDate ld7 = ld.minusMonths(2);

        // 5、获取指定日期的LocalDate对象: public static LocalDate of(int year, int month, int dayOfMonth)
        LocalDate ld8 = LocalDate.of(2099, 12, 12);
        LocalDate ld9 = LocalDate.of(2099, 12, 12);

        // 6、判断2个日期对象,是否相等,在前还是在后: equals isBefore isAfter
        System.out.println(ld8.equals(ld9));// true
        System.out.println(ld8.isAfter(ld)); // true
        System.out.println(ld8.isBefore(ld)); // false
    }
}

14.LocalTime

LocalTime的常用API (都是处理时、分、秒、纳秒相关的)。
方法名
说明
public int getHour() 
获取小时
public int getMinute()
获取分
public int getSecond()
获取秒
public int getNano()
获取纳秒
方法名
说明
withHour、withMinute、 withSecond、 wi thNano
修改时间,返回新时间对象
plusHours、plusMinutes、plusSeconds、plusNanos
把某个信息加多少,返回新时间对象
minusHours、minusMinutes、minusSeconds、minusNanos把某 个信息减多少,返回新时间对象
equals isBefore isAfter
判断2个时间对象,是否相等,在前还是在后
 

package com.huang.d4_jdk8_time;
import java.time.LocalTime;

public class Test2_LocalTime {
    public static void main(String[] args) {
        // 0、获取本地时间对象
        LocalTime lt = LocalTime.now(); // 时 分 秒 纳秒 不可变的
        System.out.println(lt);

        // 1、获取时间中的信息
        int hour = lt.getHour(); //时
        int minute = lt.getMinute(); //分
        int second = lt.getSecond(); //秒
        int nano = lt.getNano(); //纳秒

        // 2、修改时间:withHour、withMinute、withSecond、withNano
        LocalTime lt3 = lt.withHour(10);
        LocalTime lt4 = lt.withMinute(10);
        LocalTime lt5 = lt.withSecond(10);
        LocalTime lt6 = lt.withNano(10);

        // 3、加多少:plusHours、plusMinutes、plusSeconds、plusNanos
        LocalTime lt7 = lt.plusHours(10);
        LocalTime lt8 = lt.plusMinutes(10);
        LocalTime lt9 = lt.plusSeconds(10);
        LocalTime lt10 = lt.plusNanos(10);

        // 4、减多少:minusHours、minusMinutes、minusSeconds、minusNanos
        LocalTime lt11 = lt.minusHours(10);
        LocalTime lt12 = lt.minusMinutes(10);
        LocalTime lt13 = lt.minusSeconds(10);
        LocalTime lt14 = lt.minusNanos(10);

        // 5、获取指定时间的LocalTime对象:
        // public static LocalTime of(int hour, int minute, int second)
        LocalTime lt15 = LocalTime.of(12, 12, 12);
        LocalTime lt16 = LocalTime.of(12, 12, 12);

        // 6、判断2个时间对象,是否相等,在前还是在后: equals isBefore isAfter
        System.out.println(lt15.equals(lt16)); // true
        System.out.println(lt15.isAfter(lt)); // false
        System.out.println(lt15.isBefore(lt)); // true

    }
}

15. LocalDateTime

LocalDateTime的常用API (可以处理年、月、日、星期、时、分、秒、纳秒等信息)
方法名
说明
getYear. getMonthValue、 getDayOfMonth、getDayOfYear
获取年月日、时分秒、纳秒等
getDayOfWeek、getHour、 getMinute、 getSecond、 getNano
withYear、wi thMonth、withDayOfMonth、 wi thDayOfYear
修改某个信息,返回新日期时间对象
withHour、withMinute、withSecond、withNano
plusYears、plusMonths、 plusDays、 plusWeeks
把某个信息加多少,返回新日期时间对象
plusHours、plusMinutes、 plusSeconds、 plusNanos
minusYears、minusMonths、minusDays、minusWeeks
把某个信息减多少,返回新日期时间对象
minusHours、minusMinutes、minusSeconds、minusNanos
equals isBefore isAfter
判断2个时间对象,是否相等,在前还是在后
 

public class Test3_LocalDateTime {
    public static void main(String[] args) {
        // 0、获取本地日期和时间对象。
        LocalDateTime ldt = LocalDateTime.now(); // 年 月 日 时 分 秒 纳秒
        System.out.println(ldt);

        // 1、可以获取日期和时间的全部信息
        int year = ldt.getYear(); // 年
        int month = ldt.getMonthValue(); // 月
        int day = ldt.getDayOfMonth(); // 日
        int dayOfYear = ldt.getDayOfYear();  // 一年中的第几天
        int dayOfWeek = ldt.getDayOfWeek().getValue();  // 获取是周几
        int hour = ldt.getHour(); //时
        int minute = ldt.getMinute(); //分
        int second = ldt.getSecond(); //秒
        int nano = ldt.getNano(); //纳秒

        // 2、修改时间信息:
        // withYear withMonth withDayOfMonth withDayOfYear withHour
        // withMinute withSecond withNano
        LocalDateTime ldt2 = ldt.withYear(2029);
        LocalDateTime ldt3 = ldt.withMinute(59);

        // 3、加多少:
        // plusYears  plusMonths plusDays plusWeeks plusHours plusMinutes plusSeconds plusNanos
        LocalDateTime ldt4 = ldt.plusYears(2);
        LocalDateTime ldt5 = ldt.plusMinutes(3);

        // 4、减多少:
        // minusDays minusYears minusMonths minusWeeks minusHours minusMinutes minusSeconds minusNanos
        LocalDateTime ldt6 = ldt.minusYears(2);
        LocalDateTime ldt7 = ldt.minusMinutes(3);


        // 5、获取指定日期和时间的LocalDateTime对象:
        // public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour,
        //                                  int minute, int second, int nanoOfSecond)
        LocalDateTime ldt8 = LocalDateTime.of(2029, 12, 12, 12, 12, 12, 1222);
        LocalDateTime ldt9 = LocalDateTime.of(2029, 12, 12, 12, 12, 12, 1222);

        // 6、 判断2个日期、时间对象,是否相等,在前还是在后: equals、isBefore、isAfter
        System.out.println(ldt9.equals(ldt8));
        System.out.println(ldt9.isAfter(ldt));
        System.out.println(ldt9.isBefore(ldt));

        // 7、可以把LocalDateTime转换成LocalDate和LocalTime
        // public LocalDate toLocalDate()
        // public LocalTime toLocalTime()
        // public static LocalDateTime of(LocalDate date, LocalTime time)
        LocalDate ld = ldt.toLocalDate();
        LocalTime lt = ldt.toLocalTime();
        LocalDateTime ldt10 = LocalDateTime.of(ld, lt);

    }
}

16.ZoneId

 //目标:了解时区 和带时区的时间。
// 1、ZoneId的常见方法:
// public static ZoneId systemDefoultl; 获取系统默认的时区
// public static Set<String> getAvailableZoneIds(): 获取Java 支持的全部时区Id
// public static ZoneId of(String zoneId) :把某个时区id封装成ZoneId对象。
// 2、ZonedDateTime:带时区的时间。
// public static ZonedDateTime now(ZoneId zone): 获取菜个时区的ZonedDateTime对象。
// public static ZonedDateTime now(): 获取系统默认时区的ZonedDateTime对象

public class Test4_ZoneId_ZonedDateTime {
    public static void main(String[] args) {
        // 目标:了解时区和带时区的时间。
        // 1、ZoneId的常见方法:
        // public static ZoneId systemDefault(): 获取系统默认的时区
        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId.getId());
        System.out.println(zoneId);

        // public static Set<String> getAvailableZoneIds(): 获取Java支持的全部时区Id
        System.out.println(ZoneId.getAvailableZoneIds());

        // public static ZoneId of(String zoneId) : 把某个时区id封装成ZoneId对象。
        ZoneId zoneId1 = ZoneId.of("America/New_York");

        // 2、ZonedDateTime:带时区的时间。
        // public static ZonedDateTime now(ZoneId zone): 获取某个时区的ZonedDateTime对象。
        ZonedDateTime now = ZonedDateTime.now(zoneId1);
        System.out.println(now);

        // 世界标准时间了
        ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC());
        System.out.println(now1);

        // public static ZonedDateTime now():获取系统默认时区的ZonedDateTime对象
        ZonedDateTime now2 = ZonedDateTime.now();
        System.out.println(now2);

        // Calendar instance = Calendar.getInstance(TimeZone.getTimeZone(zoneId1));
    }
}

 17.Instant

Instant时间线上的某个时刻/时间戳
●通过获取Instant的对 象可以拿到此刻的时间,该时间由两部分组成:从1970-01-01 00:00:00开始走到此刻的总秒数+不够1秒的纳秒娄
1970-01-01 00:00:00

1663253222、总秒数
63411500 纳秒数
1秒=1000毫秒;
1毫秒= 1000微秒;
1微秒=1000纳秒;
方法名
说明
1秒= 1000 000 000纳秒
public static Instant now() 公
获取当前时间的Instant对象(标准时间)
public 1ong getEpochSecond()
获取从1970-01-01T00: 00: 00开始记录的秒数。
public int getNano()
从时间线开始,获取从第二个开始的纳秒数
plusMillis plusSeconds plusNanos
判断系列的方法
minusMillis minusSeconds minusNanos
减少时间系列的方法
equals、isBefore、 isAfter
增加时间系列的方法

public class Test5_Instant {
    public static void main(String[] args) {
       // 1、创建Instant的对象,获取此刻时间信息
        Instant now = Instant.now(); // 不可变对象

        // 2、获取总秒数
        long second = now.getEpochSecond();
        System.out.println(second);

        // 3、不够1秒的纳秒数
        int nano = now.getNano();
        System.out.println(nano);

        System.out.println(now);

        Instant instant = now.plusNanos(111);

        // Instant对象的作用:做代码的性能分析,或者记录用户的操作时间点
        Instant now1 = Instant.now();
        // 代码执行。。。。
        Instant now2 = Instant.now();

        LocalDateTime l = LocalDateTime.now();
    }
}

 18.DateTimeFormatter

DateTimeFormatter
方法名
说明
pub1ic static DateTimeFormatter ofPattern( 时间格式)
获取格式化器对象
public String format (时间对象)
格式化时间
 

/**
 *  目标:掌握JDK 8新增的DateTimeFormatter格式化器的用法。
 */
public class Test6_DateTimeFormatter {
    public static void main(String[] args) {
        // 1、创建一个日期时间格式化器对象出来。
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

        // 2、对时间进行格式化
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        String rs = formatter.format(now); // 正向格式化
        System.out.println(rs);

        // 3、格式化时间,其实还有一种方案。
        String rs2 = now.format(formatter); // 反向格式化
        System.out.println(rs2);

        // 4、解析时间:解析时间一般使用LocalDateTime提供的解析方法来解析。
        String dateStr = "2029年12月12日 12:12:11";
        LocalDateTime ldt = LocalDateTime.parse(dateStr, formatter);
        System.out.println(ldt);
    }
}

 19.Period

Period (- -段时期)
●可以用于计算两个LocalDate对象相差的年数、月数、天数。
方法名
说明
public static Period between(LocalDate start, LocalDate end)
传入2个日期对象,得到Period对象
public int getYears()
计算隔几年,并返回
public int getMonths()
计算隔几个月,年返回
public int getDays()
计算隔多少天,并返回
 

/**
 * 目标:掌握Period的作用:计算机两个日期相差的年数,月数、天数。
 */
public class Test7_Period {
    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2029, 8, 10);
        LocalDate end = LocalDate.of(2029, 12, 15);

        // 1、创建Period对象,封装两个日期对象。
        Period period = Period.between(start, end);

        // 2、通过period对象获取两个日期对象相差的信息。
        System.out.println(period.getYears());
        System.out.println(period.getMonths());
        System.out.println(period.getDays());
    }
}

 20.Duration

Duration ( 持续时间)
●可以用于计算两个时间对象相差的天数、小时数、分数、秒数、纳秒数;支持LocalTime、LocalDateTime、 Instant等时间。
方法名
说明
public static Duration between(开始时间对象1,截止时间对象2)
传入2个时间对象,得到Duration对象
public long toDays()
计算隔多少天,并返回
public long toHours()
计算隔多少小时,并返回
public long toMinutes()
计算隔多少分,并返回
public long toSeconds()
计算隔多少秒,并返回
public long toMillis()
计算隔多少毫秒,并返回
public long toNanos ()
计算隔多少纳秒,并返回
 

public class Test8_Duration {
    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2025, 11, 11, 11, 10, 10);
        LocalDateTime end = LocalDateTime.of(2025, 11, 11, 11, 11, 11);
        // 1、得到Duration对象
        Duration duration = Duration.between(start, end);

        // 2、获取两个时间对象间隔的信息
        System.out.println(duration.toDays());// 间隔多少天
        System.out.println(duration.toHours());// 间隔多少小时
        System.out.println(duration.toMinutes());// 间隔多少分
        System.out.println(duration.toSeconds());// 间隔多少秒
        System.out.println(duration.toMillis());// 间隔多少毫秒
        System.out.println(duration.toNanos());// 间隔多少纳秒

    }
}

21.Arrays

// 1、public static String tostring(类型[] arr):返回数组的内容
// 2. public static int[] copyOfRange(类型[] arr, 起始索引,结束索引):拷贝数组(指定范围, 包前不包后)
// 3、public static copy0f(类型[] arr, int newLength): 拷贝数组,可以指定新数组的长度。
// 4、public static setAll(double[] array, IntToDoubleFunction generator): 把数组中的原数据改为新数据又存进去。
// 5. public static void sort(类型[] arr): 对数组进行排序(默认是升序排序)
 

/**
 * 目标:掌握Arrays类的常用方法。
 */
public class ArraysTest1 {
    public static void main(String[] args) {
        // 1、public static String toString(类型[] arr): 返回数组的内容
        int[] arr = {10, 20, 30, 40, 50, 60};
        System.out.println(Arrays.toString(arr));

        // 2、public static 类型[] copyOfRange(类型[] arr, 起始索引, 结束索引) :拷贝数组(指定范围,包前不包后)
        int[] arr2 = Arrays.copyOfRange(arr, 1, 4);
        System.out.println(Arrays.toString(arr2));

        // 3、public static copyOf(类型[] arr, int newLength):拷贝数组,可以指定新数组的长度。
        int[] arr3 = Arrays.copyOf(arr, 10);
        System.out.println(Arrays.toString(arr3));

        // 4、public static setAll(double[] array, IntToDoubleFunction generator):把数组中的原数据改为新数据又存进去。
        double[] prices = {99.8, 128, 100};
        //                  0     1    2
        // 把所有的价格都打八折,然后又存进去。
        Arrays.setAll(prices, new IntToDoubleFunction() {
            @Override
            public double applyAsDouble(int value) {
                // value = 0  1  2
                return prices[value] * 0.8;
            }
        });
        System.out.println(Arrays.toString(prices));

        // 5、public static void sort(类型[] arr):对数组进行排序(默认是升序排序)
        Arrays.sort(prices);
        System.out.println(Arrays.toString(prices));
    }
}

 方式一: 让该对象的类 实现Comparable(比较规则)接口,然后重写compareTo方法,自己来制定比较规则。
方式二:使用下面这个sort方法,创建Comparator比较器接口的匿名内部类对象,然后自己制定比较规则。
public static <T> void sort(T[] arr, Comparator<? super T> c)对数组进行排序(支持自定义排序规则)

public class ArraysTest2 {
    public static void main(String[] args) {
        // 目标:掌握如何对数组中的对象进行排序。
        Student[] students = new Student[4];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("紫霞", 163.8, 26);
        students[3] = new Student("至尊宝", 167.5, 24);

        // 1、public static void sort(类型[] arr):对数组进行排序。
//        Arrays.sort(students);
//        System.out.println(Arrays.toString(students));

        // 2、public static <T> void sort(T[] arr, Comparator<? super T> c)
        // 参数一:需要排序的数组
        // 参数二:Comparator比较器对象(用来制定对象的比较规则)
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // 制定比较规则了:左边对象 o1   右边对象 o2
                // 约定1:认为左边对象 大于 右边对象 请您返回正整数
                // 约定2:认为左边对象 小于 右边对象 请您返回负整数
                // 约定3:认为左边对象 等于 右边对象 请您一定返回0
//                if(o1.getHeight() > o2.getHeight()){
//                    return 1;
//                }else if(o1.getHeight() < o2.getHeight()){
//                    return -1;
//                }
//                return 0; // 升序
                 return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
                // return Double.compare(o2.getHeight(), o1.getHeight()); // 降序
            }
        });
        System.out.println(Arrays.toString(students));


    }
}
public class ArraysTest2 {
    public static void main(String[] args) {
        // 目标:掌握如何对数组中的对象进行排序。
        Student[] students = new Student[4];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("紫霞", 163.8, 26);
        students[3] = new Student("至尊宝", 167.5, 24);

        // 1、public static void sort(类型[] arr):对数组进行排序。
//        Arrays.sort(students);
//        System.out.println(Arrays.toString(students));

        // 2、public static <T> void sort(T[] arr, Comparator<? super T> c)
        // 参数一:需要排序的数组
        // 参数二:Comparator比较器对象(用来制定对象的比较规则)
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // 制定比较规则了:左边对象 o1   右边对象 o2
                // 约定1:认为左边对象 大于 右边对象 请您返回正整数
                // 约定2:认为左边对象 小于 右边对象 请您返回负整数
                // 约定3:认为左边对象 等于 右边对象 请您一定返回0
//                if(o1.getHeight() > o2.getHeight()){
//                    return 1;
//                }else if(o1.getHeight() < o2.getHeight()){
//                    return -1;
//                }
//                return 0; // 升序
                 return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
                // return Double.compare(o2.getHeight(), o1.getHeight()); // 降序
            }
        });
        System.out.println(Arrays.toString(students));


    }
}

11.lambda 

public class LambdaTest1 {
    public static void main(String[] args) {
        // 目标:认识Lambda表达式.
//        Animal a = new Animal(){
//            @Override
//            public void run() {
//                System.out.println("狗跑的贼快~~");
//            }
//        };
//        a.run();

          // 注意:Lambda表达式并不是说能简化全部匿名内部类的写法,只能简化函数式接口的匿名内部类。
         //  错误的代码!
//        Animal a = () -> {
//            System.out.println("狗跑的贼快~~");
//        };
//        a.run();

//         Swimming s = new Swimming(){
//             @Override
//             public void swim() {
//                 System.out.println("学生快乐的游泳~~~~");
//             }
//         };
//         s.swim();

        Swimming s = () -> {
              System.out.println("学生快乐的游泳~~~~");
        };
        s.swim();
    }
}

interface Swimming{
    void swim();
}

abstract class Animal{
    public abstract void run();
}
public class LambdaTest2 {
    public static void main(String[] args) {
        // 目标:使用Lambda简化函数式接口。
        double[] prices = {99.8, 128, 100};

//        Arrays.setAll(prices, new IntToDoubleFunction() {
//            @Override
//            public double applyAsDouble(int value) {
//                // value = 0  1  2
//                return prices[value] * 0.8;
//            }
//        });

//        Arrays.setAll(prices, (int value) -> {
//                return prices[value] * 0.8;
//        });

//        Arrays.setAll(prices, (value) -> {
//            return prices[value] * 0.8;
//        });

//        Arrays.setAll(prices, value -> {
//            return prices[value] * 0.8;
//        });

        Arrays.setAll(prices, value -> prices[value] * 0.8 );

        System.out.println(Arrays.toString(prices));
        System.out.println("----------------------------------------------------------");

        Student[] students = new Student[4];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("紫霞", 163.8, 26);
        students[3] = new Student("至尊宝", 167.5, 24);

//        Arrays.sort(students, new Comparator<Student>() {
//            @Override
//            public int compare(Student o1, Student o2) {
//                return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
//            }
//        });

//        Arrays.sort(students, (Student o1, Student o2) -> {
//                return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
//        });

//        Arrays.sort(students, ( o1,  o2) -> {
//            return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
//        });

        Arrays.sort(students, ( o1,  o2) -> Double.compare(o1.getHeight(), o2.getHeight()));


        System.out.println(Arrays.toString(students));
    }
}

方法引用

public class Car {
    private String name;
    private double price;

    public Car() {

    }

    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
public class CompareByData {
    public static int compareByAge(Student o1, Student o2){
        return o1.getAge() - o2.getAge(); // 升序排序的规则
    }

    public int compareByAgeDesc(Student o1, Student o2){
        return o2.getAge() - o1.getAge(); // 降序排序的规则
    }
}
/**
 *  目标:了解静态方法引用、实例方法引用,简化Lambda表达式。
 */
public class Test1 {
    public static void main(String[] args) {
        Student[] students = new Student[4];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("紫霞", 163.8, 26);
        students[3] = new Student("至尊宝", 167.5, 24);

        // 原始写法:对数组中的学生对象,按照年龄升序排序
//        Arrays.sort(students, new Comparator<Student>() {
//            @Override
//            public int compare(Student o1, Student o2) {
//                return o1.getAge() - o2.getAge(); // 按照年龄升序排序
//            }
//        });

        // 使用Lambda简化后的形式
        // Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());

        // Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2));

        // 静态方法引用
        // Arrays.sort(students,  CompareByData::compareByAge);
        System.out.println("--------------------------------------------------------------");

        // Arrays.sort(students, (o1, o2) -> o2.getAge() - o1.getAge()); // 降序

        CompareByData compare = new CompareByData();
        // Arrays.sort(students, (o1, o2) -> compare.compareByAgeDesc(o1, o2)); // 降序
        // 实例方法引用
        Arrays.sort(students, compare::compareByAgeDesc); // 降序

        System.out.println(Arrays.toString(students));
    }
}

/**
 * 目标:掌握特定类型的方法引用。
 */
public class Test2 {
    public static void main(String[] args) {
        String[] names = {"boby", "angela", "Andy" ,"dlei", "caocao", "Babo", "jack", "Cici"};

        // 进行排序(默认是按照字符串的首字符编号进行升序排序的)
        // Arrays.sort(names);

        // 要求忽略首字符大小写进行排序。
//        Arrays.sort(names, new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                // 制定比较规则。o1 = "Andy"  o2 = "angela"
//                return o1.compareToIgnoreCase(o2);
//            }
//        });

        // Arrays.sort(names, ( o1,  o2) -> o1.compareToIgnoreCase(o2) );
        // 特定类型的方法引用!
        Arrays.sort(names, String::compareToIgnoreCase);

        System.out.println(Arrays.toString(names));
    }
}

/**
 *  目标:构造器引用(理解语法)
 */
public class Test3 {
    public static void main(String[] args) {
        // 1、创建这个接口的匿名内部类对象。
//        CreateCar cc = new CreateCar(){
//            @Override
//            public Car create(String name, double price) {
//                return new Car(name, price);
//            }
//        };

        // CreateCar cc = ( name,  price) -> new Car(name, price);

        // 构造器引用
        CreateCar cc = Car::new;
        Car c = cc.create("奔驰", 49.9);
        System.out.println(c);
    }
}

interface CreateCar{
    Car create(String name, double price);
}

排序算法

/**
 * 目标:掌握冒泡排序的编写。
 */
public class Test1 {
    public static void main(String[] args) {
        // 1、准备一个数组
        int[] arr = {5, 2, 3, 1};

        // 2、定义一个循环控制排几轮
        for (int i = 0; i < arr.length - 1; i++) {
            // i = 0  1  2           【5, 2, 3, 1】    次数
            // i = 0 第一轮            0   1   2         3
            // i = 1 第二轮            0   1             2
            // i = 2 第三轮            0                 1

            // 3、定义一个循环控制每轮比较几次。
            for (int j = 0; j < arr.length - i - 1; j++) {
                // 判断当前位置的元素值,是否大于后一个位置处的元素值,如果大则交换。
                if(arr[j] > arr[j+1]){
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

/**
 *  目标:掌握选择排序。
 */
public class Test2 {
    public static void main(String[] args) {
        // 1、准备好一个数组
        int[] arr = {5, 1, 3, 2};
        //           0  1  2  3

        // 2、控制选择几轮
        for (int i = 0; i < arr.length - 1; i++) {
            // i = 0 第一轮    j = 1 2 3
            // i = 1 第二轮    j = 2 3
            // i = 2 第三轮    j = 3
            // 3、控制每轮选择几次。
            for (int j = i + 1; j < arr.length; j++) {
                // 判断当前位置是否大于后面位置处的元素值,若大于则交换。
                if(arr[i] > arr[j]){
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}
/**
 * 目标:掌握二分查找算法。
 */
public class Test3 {
    public static void main(String[] args) {
        // 1、准备好一个数组。
        int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};

        System.out.println(binarySearch(arr, 150));

        System.out.println(Arrays.binarySearch(arr, 81));
    }

    public static int binarySearch(int[] arr, int data){
        // 1、定义两个变量,一个站在左边位置,一个站在右边位置
        int left = 0;
        int right = arr.length - 1;

        // 2、定义一个循环控制折半。
        while (left <= right){
            // 3、每次折半,都算出中间位置处的索引
            int middle = (left + right) / 2;
            // 4、判断当前要找的元素值,与中间位置处的元素值的大小情况。
            if(data < arr[middle]){
                // 往左边找,截止位置(右边位置) = 中间位置 - 1
                right = middle - 1;
            }else if(data > arr[middle]){
                // 往右边找,起始位置(左边位置) = 中间位置 + 1
                left = middle + 1;
            }else {
                // 中间位置处的元素值,正好等于我们要找的元素值
                return middle;
            }
        }
        return -1; // -1特殊结果,就代表没有找到数据!数组中不存在该数据!
    }
}

正则表达式

/**
 * 目标:体验一下使用正则表达式来校验数据格式的合法性。
 * 需求:校验QQ号码是否正确,要求全部是数字,长度是(6-20)之间,不能以0开头。
 */
public class RegexTest1 {
    public static void main(String[] args) {
        System.out.println(checkQQ(null));
        System.out.println(checkQQ("251425876"));
        System.out.println(checkQQ("2514a8d76"));
        System.out.println("--------------------------------------------------");

        System.out.println(checkQQ1(null));
        System.out.println(checkQQ1("251425876"));
        System.out.println(checkQQ1("2514a8d76"));

    }

    public static boolean checkQQ1(String qq){
        return qq != null && qq.matches("[1-9]\\d{5,19}");
    }

    public static boolean checkQQ(String qq){
        // 1、判断qq号码是否为null
        if(qq == null || qq.startsWith("0") || qq.length() < 6 || qq.length() > 20){
            return false;
        }

        // 2、qq至少是不是null,不是以0开头的,满足6-20之间的长度。
        // 判断qq号码中是否都是数字。
        // qq = 2514ghd234
        for (int i = 0; i < qq.length(); i++) {
            // 根据索引提取当前位置处的字符。
            char ch = qq.charAt(i);
            // 判断ch记住的字符,如果不是数字,qq号码不合法。
            if(ch < '0' || ch > '9'){
                return false;
            }
        }
        // 3、说明qq号码肯定是合法
        return true;
    }
}
/**
 * 目标:掌握正则表达式的书写规则
 */
public class RegexTest2 {
    public static void main(String[] args) {
        // 1、字符类(只能匹配单个字符)
        System.out.println("a".matches("[abc]"));    // [abc]只能匹配a、b、c
        System.out.println("e".matches("[abcd]")); // false

        System.out.println("d".matches("[^abc]"));   // [^abc] 不能是abc
        System.out.println("a".matches("[^abc]"));  // false

        System.out.println("b".matches("[a-zA-Z]")); // [a-zA-Z] 只能是a-z A-Z的字符
        System.out.println("2".matches("[a-zA-Z]")); // false

        System.out.println("k".matches("[a-z&&[^bc]]")); // : a到z,除了b和c
        System.out.println("b".matches("[a-z&&[^bc]]")); // false

        System.out.println("ab".matches("[a-zA-Z0-9]")); // false 注意:以上带 [内容] 的规则都只能用于匹配单个字符

        // 2、预定义字符(只能匹配单个字符)  .  \d  \D   \s  \S  \w  \W
        System.out.println("徐".matches(".")); // .可以匹配任意字符
        System.out.println("徐徐".matches(".")); // false

        // \转义
        System.out.println("\"");
        // \n \t
        System.out.println("3".matches("\\d"));  // \d: 0-9
        System.out.println("a".matches("\\d"));  //false

        System.out.println(" ".matches("\\s"));   // \s: 代表一个空白字符
        System.out.println("a".matches("\s")); // false

        System.out.println("a".matches("\\S"));  // \S: 代表一个非空白字符
        System.out.println(" ".matches("\\S")); // false

        System.out.println("a".matches("\\w"));  // \w: [a-zA-Z_0-9]
        System.out.println("_".matches("\\w")); // true
        System.out.println("徐".matches("\\w")); // false

        System.out.println("徐".matches("\\W"));  // [^\w]不能是a-zA-Z_0-9
        System.out.println("a".matches("\\W"));  // false

        System.out.println("23232".matches("\\d")); // false 注意:以上预定义字符都只能匹配单个字符。

        // 3、数量词: ?   *   +   {n}   {n, }  {n, m}
        System.out.println("a".matches("\\w?"));   // ? 代表0次或1次
        System.out.println("".matches("\\w?"));    // true
        System.out.println("abc".matches("\\w?")); // false

        System.out.println("abc12".matches("\\w*"));   // * 代表0次或多次
        System.out.println("".matches("\\w*"));        // true
        System.out.println("abc12张".matches("\\w*")); // false

        System.out.println("abc12".matches("\\w+"));   // + 代表1次或多次
        System.out.println("".matches("\\w+"));       // false
        System.out.println("abc12张".matches("\\w+")); // false

        System.out.println("a3c".matches("\\w{3}"));   // {3} 代表要正好是n次
        System.out.println("abcd".matches("\\w{3}"));  // false
        System.out.println("abcd".matches("\\w{3,}"));     // {3,} 代表是>=3次
        System.out.println("ab".matches("\\w{3,}"));     // false
        System.out.println("abcde徐".matches("\\w{3,}"));     // false
        System.out.println("abc232d".matches("\\w{3,9}"));     // {3, 9} 代表是  大于等于3次,小于等于9次

        // 4、其他几个常用的符号:(?i)忽略大小写 、 或:| 、  分组:()
        System.out.println("abc".matches("(?i)abc")); // true
        System.out.println("ABC".matches("(?i)abc")); // true
        System.out.println("aBc".matches("a((?i)b)c")); // true
        System.out.println("ABc".matches("a((?i)b)c")); // false

        // 需求1:要求要么是3个小写字母,要么是3个数字。
        System.out.println("abc".matches("[a-z]{3}|\\d{3}")); // true
        System.out.println("ABC".matches("[a-z]{3}|\\d{3}")); // false
        System.out.println("123".matches("[a-z]{3}|\\d{3}")); // true
        System.out.println("A12".matches("[a-z]{3}|\\d{3}")); // false

        // 需求2:必须是”我爱“开头,中间可以是至少一个”编程“,最后至少是1个”666“
        System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));
        System.out.println("我爱编程编程66666".matches("我爱(编程)+(666)+"));
    }
}

/**
 * 目标:校验用户输入的电话、邮箱、时间是否合法。
 */
public class RegexTest3 {
    public static void main(String[] args) {
        // checkPhone();
        checkEmail();
    }

    public static void checkPhone(){
        while (true) {
            System.out.println("请您输入您的电话号码(手机|座机): ");
            Scanner sc = new Scanner(System.in);
            String phone = sc.nextLine();
            // 18676769999  010-3424242424 0104644535
            if(phone.matches("(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})")){
                System.out.println("您输入的号码格式正确~~~");
                break;
            }else {
                System.out.println("您输入的号码格式不正确~~~");
            }
        }
    }

    public static void checkEmail(){
        while (true) {
            System.out.println("请您输入您的邮箱: ");
            Scanner sc = new Scanner(System.in);
            String email = sc.nextLine();
            /**
             * dlei0009@163.com
             * 25143242@qq.com
             * itheima@itcast.com.cn
             */
            if(email.matches("\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2}")){
                System.out.println("您输入的邮箱格式正确~~~");
                break;
            }else {
                System.out.println("您输入的邮箱格式不正确~~~");
            }
        }
    }
}

/**
 * 目标:掌握使用正则表达式查找内容。
 */
public class RegexTest4 {
    public static void main(String[] args) {
        method1();
    }

    // 需求1:从以下内容中爬取出,手机,邮箱,座机、400电话等信息。
    public static void method1(){
        String data = " 来黑马程序员学习Java,\n" +
                "        电话:1866668888,18699997777\n" +
                "        或者联系邮箱:boniu@itcast.cn,\n" +
                "        座机电话:01036517895,010-98951256\n" +
                "        邮箱:bozai@itcast.cn,\n" +
                "        邮箱:dlei0009@163.com,\n" +
                "        热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";
        // 1、定义爬取规则
        String regex = "(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})|(\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2})"
                + "|(400-?\\d{3,7}-?\\d{3,7})";
        // 2、把正则表达式封装成一个Pattern对象
        Pattern pattern = Pattern.compile(regex);
        // 3、通过pattern对象去获取查找内容的匹配器对象。
        Matcher matcher = pattern.matcher(data);
        // 4、定义一个循环开始爬取信息
        while (matcher.find()){
            String rs = matcher.group(); // 获取到了找到的内容了。
            System.out.println(rs);
        }
    }

}
public class RegexTest5 {
    public static void main(String[] args) {
        method2();
    }

    // 需求2:把全部邮箱的账户名找出来。
    public static void method1(){
        String data = " 来黑马程序员学习Java,\n" +
                "        电话:1866668888,18699997777\n" +
                "        或者联系邮箱:boniu@itcast.cn,\n" +
                "        座机电话:01036517895,010-98951256\n" +
                "        邮箱:bozai@itcast.cn,\n" +
                "        邮箱:dlei0009@163.com,\n" +
                "        热线电话:400-618-9090 ,heima, 400-618-4000,4006184000,4006189090";
        // 1、定义爬取规则
        String regex = "(\\w{2,})@(\\w{2,20})(\\.\\w{2,10}){1,2}";
        // 2、把正则表达式封装成一个Pattern对象
        Pattern pattern = Pattern.compile(regex);
        // 3、通过pattern对象去获取查找内容的匹配器对象。
        Matcher matcher = pattern.matcher(data);
        // 4、定义一个循环开始爬取信息
        while (matcher.find()){
            System.out.println(matcher.group());
            System.out.println(matcher.group(1)); // 指定获取正则表达式匹配后的第一组内容
            System.out.println(matcher.group(2)); // 指定获取正则表达式匹配后的第二组内容
        }
    }


    // 需求3:某系统的日志文件记录了当天进入系统的全部用户信息,需要把这些用户的名字爬取出来另作他用。
    public static void method2(){
        String data = "欢迎张全蛋光临本系统!他删库并跑路,欢迎李二狗子光临本系统!" +
                "欢迎马六子光临本系统!它浏览了很多好看的照片!欢迎夏洛光临本系统!他在六点钟购买了一台拖拉机!";
        // 1、定义爬取规则
        // String regex = "欢迎(.+)光临"; // 贪婪匹配
        String regex = "欢迎(.+?)光临"; // +? 非贪婪匹配
        // 2、把正则表达式封装成一个Pattern对象
        Pattern pattern = Pattern.compile(regex);
        // 3、通过pattern对象去获取查找内容的匹配器对象。
        Matcher matcher = pattern.matcher(data);
        // 4、定义一个循环开始爬取信息
        while (matcher.find()){
            System.out.println(matcher.group());
            System.out.println(matcher.group(1));
        }
    }
}

/**
 * 目标:掌握使用正则表达式做搜索替换,内容分割。
 */
public class RegexTest6 {
    public static void main(String[] args) {
        // 1、public String replaceAll(String regex , String newStr):按照正则表达式匹配的内容进行替换
        // 需求1:请把 古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴,中间的非中文字符替换成 “-”
        String s1 = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";
        System.out.println(s1.replaceAll("\\w+", "-"));

        // 需求2(拓展):某语音系统,收到一个口吃的人说的“我我我喜欢编编编编编编编编编编编编程程程!”,需要优化成“我喜欢编程!”。
        String s2 = "我我我喜欢编编编编编编编编编编编编程程程";
        /**
         * (.)一组:.匹配任意字符的。
         * \\1 :为这个组声明一个组号:1号
         * +:声明必须是重复的字
         * $1可以去取到第1组代表的那个重复的字
         */
        System.out.println(s2.replaceAll("(.)\\1+", "$1"));

        // 2、public String[] split(String regex):按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。
        // 需求1:请把 古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴,中的人名获取出来。
        String s3 = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";
        String[] names = s3.split("\\w+");
        System.out.println(Arrays.toString(names));
    }
}

异常

// 1、必须让这个类继承自Exception,才能成为一个编译时异常类。
public class AgeIllegalException extends Exception{
    public AgeIllegalException() {
    }

    public AgeIllegalException(String message) {
        super(message);
    }
}
// 1、必须让这个类继承自RuntimeException,才能成为一个运行时异常类。
public class AgeIllegalRuntimeException extends RuntimeException{
    public AgeIllegalRuntimeException() {
    }

    public AgeIllegalRuntimeException(String message) {
        super(message);
    }
}

/**
 * 目标:认识异常。
 */
public class ExceptionTest1 {
    public static void main(String[] args) throws ParseException{
//         Integer.valueOf("abc");

//         int[] arr = {11, 22, 33};
//         System.out.println(arr[5]);

//        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date d = sdf.parse("2028-11-11 10:24");
            System.out.println(d);
//        } catch (ParseException e) {
//            e.printStackTrace();
//        }
    }
}

/**
 * 目标:掌握自定义异常,以及异常的作用。
 */
public class ExceptionTest2 {
    public static void main(String[] args) {
        // 需求:保存一个合法的年龄
//        try {
//            saveAge(223);
//            System.out.println("底层执行成功的!");
//        } catch (Exception e) {
//            e.printStackTrace();
//            System.out.println("底层出现了bug!");
//        }

        try {
            saveAge2(225);
            System.out.println("saveAge2底层执行是成功的!");
        } catch (AgeIllegalException e) {
            e.printStackTrace();
            System.out.println("saveAge2底层执行是出现bug的!");
        }
    }

    public static void saveAge2(int age) throws AgeIllegalException{
        if(age > 0 && age < 150){
            System.out.println("年龄被成功保存: " + age);
        }else {
            // 用一个异常对象封装这个问题
            // throw 抛出去这个异常对象
            // throws 用在方法上,抛出方法内部的异常
            throw new AgeIllegalException("/age is illegal, your age is " + age);
        }
    }

    public static void saveAge(int age){
        if(age > 0 && age < 150){
            System.out.println("年龄被成功保存: " + age);
        }else {
            // 用一个异常对象封装这个问题
            // throw 抛出去这个异常对象
            throw new AgeIllegalRuntimeException("/age is illegal, your age is " + age);
        }
    }
}

/**
 * 目标:异常的处理
 */
public class ExceptionTest3 {
    public static void main(String[] args)  {
        try {
            test1();
        } catch (FileNotFoundException e) {
            System.out.println("您要找的文件不存在!!");
            e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。
        } catch (ParseException e) {
            System.out.println("您要解析的时间有问题了!");
            e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。
        }
    }

    public static void test1() throws FileNotFoundException, ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse("2028-11-11 10:24:11");
        System.out.println(d);
        test2();
    }

    public static void test2() throws FileNotFoundException {
        // 读取文件的。
        InputStream is = new FileInputStream("D:/meinv.png");
    }
}

/**
 * 目标:异常的处理
 */
public class ExceptionTest3_2 {
    public static void main(String[] args)  {
        try {
            test1();
        } catch (Exception e) {
            System.out.println("您当前操作有问题");
            e.printStackTrace(); // 打印出这个异常对象的信息。记录下来。
        }
    }

    public static void test1() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse("2028-11-11 10:24:11");
        System.out.println(d);
        test2();
    }

    public static void test2() throws Exception {
        // 读取文件的。
        InputStream is = new FileInputStream("D:/meinv.png");
    }
}
/**
 * 目标:掌握异常的处理方式:捕获异常,尝试修复。
 */
public class ExceptionTest4 {
    public static void main(String[] args) {
        // 需求:调用一个方法,让用户输入一个合适的价格返回为止。
        // 尝试修复
        while (true) {
            try {
                System.out.println(getMoney());
                break;
            } catch (Exception e) {
                System.out.println("请您输入合法的数字!!");
            }
        }
    }

    public static double getMoney(){
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请您输入合适的价格:");
            double money = sc.nextDouble();
            if(money >= 0){
                return money;
            }else {
                System.out.println("您输入的价格是不合适的!");
            }
        }
    }
}

集合

/**
 * 目标:认识Collection体系的特点。
 */
public class CollectionTest1 {
    public static void main(String[] args) {
        // 简单确认一下Collection集合的特点。
        ArrayList<String> list = new ArrayList<>(); // 有序 可重复 有索引
        list.add("java1");
        list.add("java2");
        list.add("java1");
        list.add("java2");
        System.out.println(list);

        HashSet<String> set = new HashSet<>(); // 无序,不重复,无索引
        set.add("java1");
        set.add("java2");
        set.add("java1");
        set.add("java2");
        set.add("java3");
        System.out.println(set);

    }
}
/**
    目标:掌握Collection集合的常用API.
    Collection是集合的祖宗类,它的功能是全部集合都可以继承使用的,所以要学习它。
    Collection API如下:
         - public boolean add(E e):  把给定的对象添加到当前集合中 。
         - public void clear() :清空集合中所有的元素。
         - public boolean remove(E e): 把给定的对象在当前集合中删除。
         - public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
         - public boolean isEmpty(): 判断当前集合是否为空。
         - public int size(): 返回集合中元素的个数。
         - public Object[] toArray(): 把集合中的元素,存储到数组中。
 */
public class CollectionTest2API {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>(); // 多态写法
        // 1.public boolean add(E e):添加元素, 添加成功返回true。
        c.add("java1");
        c.add("java1");
        c.add("java2");
        c.add("java2");
        c.add("java3");
        System.out.println(c);

        // 2.public void clear():清空集合的元素。
        //c.clear();
        //System.out.println(c);

        // 3.public boolean isEmpty():判断集合是否为空 是空返回true,反之。
        System.out.println(c.isEmpty()); // false

        // 4.public int size():获取集合的大小。
        System.out.println(c.size());

        // 5.public boolean contains(Object obj):判断集合中是否包含某个元素。
        System.out.println(c.contains("java1")); // true
        System.out.println(c.contains("Java1")); // false

        // 6.public boolean remove(E e):删除某个元素:如果有多个重复元素默认删除前面的第一个!
        System.out.println(c.remove("java1"));
        System.out.println(c);

        // 7.public Object[] toArray():把集合转换成数组
        Object[] arr = c.toArray();
        System.out.println(Arrays.toString(arr));

        String[] arr2 = c.toArray(new String[c.size()]);
        System.out.println(Arrays.toString(arr2));

        System.out.println("--------------------------------------------");
        // 把一个集合的全部数据倒入到另一个集合中去。
        Collection<String> c1 = new ArrayList<>();
        c1.add("java1");
        c1.add("java2");
        Collection<String> c2 = new ArrayList<>();
        c2.add("java3");
        c2.add("java4");
        c1.addAll(c2); // 就是把c2集合的全部数据倒入到c1集合中去。
        System.out.println(c1);
        System.out.println(c2);
    }
}

/**
    目标:Collection集合的遍历方式一:使迭代器Iterator遍历
 */
public class CollectionDemo01 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("赵敏");
        c.add("小昭");
        c.add("素素");
        // c.add("灭绝");
        System.out.println(c);
        // c = [赵敏, 小昭, 素素]
        //                       it

        // 使用迭代器遍历集合
        // 1、从集合对象中获取迭代器对象。
        Iterator<String> it = c.iterator();
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
        // System.out.println(it.next()); // 出现异常的

        // 2、我们应该使用循环结合迭代器遍历集合。
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);

        }

    }
}

/**
     目标:Collection集合的遍历方式二:增强for
 */
public class CollectionDemo02 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("赵敏");
        c.add("小昭");
        c.add("素素");
        c.add("灭绝");
        System.out.println(c);
        // c = [赵敏, 小昭, 素素, 灭绝]
        //                      ele

        // 使用增强for遍历集合或者数组。
        for (String ele : c) {
            System.out.println(ele);
        }

        String[] names = {"迪丽热巴", "古力娜扎", "稀奇哈哈"};
        for (String name : names) {
            System.out.println(name);
        }

    }
}

/**
     目标:Collection集合的遍历方式三:JDK8开始新增的Lambda表达式。
 */
public class CollectionDemo03 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("赵敏");
        c.add("小昭");
        c.add("殷素素");
        c.add("周芷若");
        System.out.println(c);
        // [赵敏, 小昭, 殷素素, 周芷若]
        //                     s

        // default void forEach(Consumer<? super T> action):  结合Lambda表达式遍历集合:
//        c.forEach(new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        });
//
//        c.forEach((String s) -> {
//                System.out.println(s);
//        });
//
//        c.forEach(s  -> {
//            System.out.println(s);
//        });
//
//        c.forEach(s  -> System.out.println(s) );

        c.forEach(System.out::println );
    }
}


/**
 *       目标:完成电影信息的展示。
 *       new Movie("《肖生克的救赎》" , 9.7 ,  "罗宾斯")
 *       new Movie("《霸王别姬》" , 9.6 ,  "张国荣、张丰毅")
 *       new Movie("《阿甘正传》" , 9.5 ,  "汤姆.汉克斯")
 */
public class CollectionTest04 {
    public static void main(String[] args) {
        // 1、创建一个集合容器负责存储多部电影对象。
        Collection<Movie> movies = new ArrayList<>();
        movies.add( new Movie("《肖生克的救赎》" , 9.7 ,  "罗宾斯"));
        movies.add( new Movie("《霸王别姬》" , 9.6 ,  "张国荣、张丰毅"));
        movies.add( new Movie("《阿甘正传》" , 9.5 ,  "汤姆.汉克斯"));
        System.out.println(movies);

        for (Movie movie : movies) {
            System.out.println("电影名:" + movie.getName());
            System.out.println("评分:" + movie.getScore());
            System.out.println("主演:" + movie.getActor());
            System.out.println("---------------------------------------------");
        }
    }
}

public class Movie {
    private String name;
    private double score;
    private String actor;

    public Movie() {
    }

    public Movie(String name, double score, String actor) {
        this.name = name;
        this.score = score;
        this.actor = actor;
    }

    public String getName() {
        return name;
    }

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

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "name='" + name + '\'' +
                ", score=" + score +
                ", actor='" + actor + '\'' +
                '}';
    }
}

/**
      目标:掌握List系列集合的特点,以及其提供的特有方法。
 */
public class ListTest1 {
    public static void main(String[] args) {
        // 1.创建一个ArrayList集合对象(有序、可重复、有索引)
        List<String> list = new ArrayList<>();  // 一行经典代码
        list.add("蜘蛛精");
        list.add("至尊宝");
        list.add("至尊宝");
        list.add("牛夫人");
        System.out.println(list); // [蜘蛛精, 至尊宝, 至尊宝, 牛夫人]

        // 2.public void add(int index, E element): 在某个索引位置插入元素。
        list.add(2, "紫霞仙子");
        System.out.println(list);

        // 3.public E remove(int index): 根据索引删除元素,返回被删除元素
        System.out.println(list.remove(2));
        System.out.println(list);

        // 4.public E get(int index): 返回集合中指定位置的元素。
        System.out.println(list.get(3));

        // 5.public E set(int index, E element): 修改索引位置处的元素,修改成功后,会返回原来的数据
        System.out.println(list.set(3, "牛魔王"));
        System.out.println(list);
    }
}

/**
    拓展:List系列集合的遍历方式.

    List遍历方式:
        (1)for循环。(独有的,因为List有索引)。
        (2)迭代器。
        (3)foreach。
        (4)JDK 1.8新技术。

 */
public class ListTest2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("糖宝宝");
        list.add("蜘蛛精");
        list.add("至尊宝");

        //(1)for循环
        for (int i = 0; i < list.size(); i++) {
            // i = 0 1 2
            String s = list.get(i);
            System.out.println(s);
        }

        //(2)迭代器。
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        //(3)增强for循环(foreach遍历)
        for (String s : list) {
            System.out.println(s);
        }

        //(4)JDK 1.8开始之后的Lambda表达式
        list.forEach(s -> {
            System.out.println(s);
        });
    }
}

/**
 * 目标:掌握LinkedList集合的使用。
 */
public class ListTest3 {
    public static void main(String[] args) {
        // 1、创建一个队列。
        LinkedList<String> queue = new LinkedList<>();
        // 入队
        queue.addLast("第1号人");
        queue.addLast("第2号人");
        queue.addLast("第3号人");
        queue.addLast("第4号人");
        System.out.println(queue);
        // 出队
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);
        System.out.println("--------------------------------------------------");
        // 2、创建一个栈对象。
        LinkedList<String> stack = new LinkedList<>();
        // 压栈(push)
        stack.push("第1颗子弹");
        stack.push("第2颗子弹");
        stack.push("第3颗子弹");
        stack.push("第4颗子弹");
        System.out.println(stack);
        // 出栈(pop)
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println(stack);
    }
}


/**
 * 目标:整体了解一下Set系列集合的特点。
 */
public class SetTest1 {
    public static void main(String[] args) {
        // 1、创建一个Set集合的对象
        //Set<Integer> set = new HashSet<>(); // 创建了一个HashSet的集合对象 一行经典代码  HashSet: 无序 不重复 无索引
        Set<Integer> set = new LinkedHashSet<>(); //  有序 不重复 无索引
//         Set<Integer> set = new TreeSet<>(); //  可排序(升序) 不重复 无索引
        set.add(666);
        set.add(555);
        set.add(555);
        set.add(888);
        set.add(888);
        set.add(777);
        set.add(777);
        System.out.println(set);
    }
}

/**
 * 目标:了解一下哈希值。
 * Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
 *      public int hashCode(): 返回对象的哈希值。
 * 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
 * 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
 */
public class SetTest2 {
    public static void main(String[] args) {
        Student s1 = new Student("蜘蛛精",25, 169.5);
        Student s2 = new Student("紫霞",22, 166.5);
        System.out.println(s1.hashCode());
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        String str1 = new String("abc");
        String str2 = new String("acD");
        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode());
    }
}

/**
 * 目标:自定义的类型的对象,比如两个内容一样的学生对象,如果让HashSet集合能够去重复!
 */
public class SetTest3 {
    public static void main(String[] args) {
        Set<Student> students = new HashSet<>();
        Student s1 = new Student("至尊宝", 28, 169.6);
        Student s2 = new Student("蜘蛛精", 23, 169.6);
        Student s3 = new Student("蜘蛛精", 23, 169.6);
        System.out.println(s2.hashCode());
        System.out.println(s3.hashCode());
        Student s4 = new Student("牛魔王", 48, 169.6);
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        System.out.println(students);
    }
}

/**
 * 目标:掌握TreeSet集合的使用。
 */
public class SetTest4 {
    public static void main(String[] args) {
        Set<Integer> set1 = new TreeSet<>();
        set1.add(6);
        set1.add(5);
        set1.add(5);
        set1.add(7);
        System.out.println(set1);

        // TreeSet就近选择自己自带的比较器对象进行排序
//        Set<Student> students = new TreeSet<>(new Comparator<Student>() {
//            @Override
//            public int compare(Student o1, Student o2) {
//                // 需求:按照身高升序排序
//                return Double.compare(o1.getHeight() , o2.getHeight());
//            }
//        });
        Set<Student> students = new TreeSet<>(( o1,  o2) ->  Double.compare(o1.getHeight() , o2.getHeight()));
        students.add(new Student("蜘蛛精",23, 169.7));
        students.add(new Student("紫霞",22, 169.8));
        students.add(new Student("至尊宝",26, 165.5));
        students.add(new Student("牛魔王",22, 183.5));
        System.out.println(students);
    }
}


public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private double height;

    // this  o
    @Override
    public int compareTo(Student o) {
        // 如果认为左边对象大于右边对象返回正整数
        // 如果认为左边对象小于右边对象返回负整数
        // 如果认为左边对象等于右边对象返回0
        // 需求:按照年龄升序排序、
        return this.age - o.age;
    }

    public Student() {
    }

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

    // 只要两个对象内容一样就返回true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);
    }

    // 只要两个对象内容一样,返回的哈希值就是一样的。
    @Override
    public int hashCode() {
        // 姓名 年龄 身高计算哈希值的
        return Objects.hash(name, age, height);
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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


/**
 * 目标:理解集合的并发修改异常问题,并解决。
 */
public class CollectionTest1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("王麻子");
        list.add("小李子");
        list.add("李爱花");
        list.add("张全蛋");
        list.add("晓李");
        list.add("李玉刚");
        System.out.println(list);
        //  [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]

        // 需求:找出集合中全部带“李”的名字,并从集合中删除。
//        Iterator<String> it = list.iterator();
//        while (it.hasNext()){
//            String name = it.next();
//            if(name.contains("李")){
//                list.remove(name);
//            }
//        }
//        System.out.println(list);

        // 使用for循环遍历集合并删除集合中带李字的名字
        //  [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]
        //  [王麻子, 李爱花, 张全蛋, 李玉刚]
        //                                i
//        for (int i = 0; i < list.size(); i++) {
//            String name = list.get(i);
//            if(name.contains("李")){
//                list.remove(name);
//            }
//        }
//        System.out.println(list);

        System.out.println("---------------------------------------------------------");
        // 怎么解决呢?
        // 使用for循环遍历集合并删除集合中带李字的名字
        //  [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]
        //  [王麻子, 张全蛋]
        //                  i
//        for (int i = 0; i < list.size(); i++) {
//            String name = list.get(i);
//            if(name.contains("李")){
//                list.remove(name);
//                i--;
//            }
//        }
//        System.out.println(list);
        // 倒着去删除也是可以的。

        // 需求:找出集合中全部带“李”的名字,并从集合中删除。
//        Iterator<String> it = list.iterator();
//        while (it.hasNext()){
//            String name = it.next();
//            if(name.contains("李")){
//                // list.remove(name); // 并发修改异常的错误。
//                it.remove(); // 删除迭代器当前遍历到的数据,每删除一个数据后,相当于也在底层做了i--
//            }
//        }
//        System.out.println(list);

        // 使用增强for循环遍历集合并删除数据,没有办法解决bug.
//        for (String name : list) {
//            if(name.contains("李")){
//                list.remove(name);
//            }
//        }
//        System.out.println(list);

//        list.forEach(name -> {
//            if(name.contains("李")){
//                list.remove(name);
//            }
//        });
//        System.out.println(list);
    }
}


/**
 * 目标:掌握Collections集合工具类的使用。
 */
public class CollectionsTest1 {
    public static void main(String[] args) {
        // 1、public static <T> boolean addAll(Collection<? super T> c, T...elements):为集合批量添加数据
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三", "王五", "李四", "张麻子");
        System.out.println(names);

        // 2、public static void shuffle(List<?> list):打乱List集合中的元素顺序。
        Collections.shuffle(names);
        System.out.println(names);

        // 3、 public static <T> void sort(List<T> list):对List集合中的元素进行升序排序。
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(5);
        list.add(2);
        Collections.sort(list);
        System.out.println(list);

        List<Student> students = new ArrayList<>();
        students.add(new Student("蜘蛛精",23, 169.7));
        students.add(new Student("紫霞",22, 169.8));
        students.add(new Student("紫霞",22, 169.8));
        students.add(new Student("至尊宝",26, 165.5));
        // Collections.sort(students);
        // System.out.println(students);

        // 4、public static <T> void sort(List<T> list, Comparator<? super T> c): 对List集合中元素,按照比较器对象指定的规则进行排序
        Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Double.compare(o1.getHeight(), o2.getHeight());
            }
        });
        System.out.println(students);
    }
}


public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private double height;

    // this  o
    @Override
    public int compareTo(Student o) {
        // 如果认为左边对象大于右边对象返回正整数
        // 如果认为左边对象小于右边对象返回负整数
        // 如果认为左边对象等于右边对象返回0
        // 需求:按照年龄升序排序、
        return this.age - o.age;
    }

    public Student() {
    }

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

    // 只要两个对象内容一样就返回true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);
    }

    // 只要两个对象内容一样,返回的哈希值就是一样的。
    @Override
    public int hashCode() {
        // 姓名 年龄 身高计算哈希值的
        return Objects.hash(name, age, height);
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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


public class Card {
    private String number;
    private String color;
    // 每张牌是存在大小的。
    private int size; // 0 1 2 ....

    public Card() {
    }

    public Card(String number, String color, int size) {
        this.number = number;
        this.color = color;
        this.size = size;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    @Override
    public String toString() {
        return color + number ;
    }
}

/**
    目标:斗地主游戏的案例开发。
    业务需求分析:
        业务: 总共有54张牌。
        点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
        花色: "♠", "♥", "♣", "♦"
        大小王: "👲" , "🃏"
        点数分别要组合4种花色,大小王各一张。
        斗地主:发出51张牌,剩下3张作为底牌。
 */
public class GameDemo {
    public static void main(String[] args) {
        //  1、牌类。
        //  2、房间
        Room m = new Room();
        //  3、启动游戏
        m.start();
    }
}

public class Room {
    // 必须有一副牌。
    private List<Card> allCards = new ArrayList<>();

    public Room(){
        // 1、做出54张牌,存入到集合allCards
        // a、点数:个数确定了,类型确定。
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        // b、花色:个数确定了,类型确定。
        String[] colors = {"♠", "♥", "♣", "♦"};
        int size = 0; // 表示每张牌的大小
        // c、遍历点数,再遍历花色,组织牌
        for (String number : numbers) {
            // number = "3"
            size++; // 1 2 ....
            for (String color : colors) {
                // 得到一张牌
                Card c = new Card(number, color, size);
                allCards.add(c); // 存入了牌
            }
        }
        // 单独存入小大王的。
        Card c1 = new Card("",  "🃏" , ++size);
        Card c2 = new Card("", "👲" , ++size);
        Collections.addAll(allCards, c1, c2);
        System.out.println("新牌:" + allCards);
    }

    /**
     * 游戏启动
     */
    public void start() {
        // 1、洗牌: allCards
        Collections.shuffle(allCards);
        System.out.println("洗牌后:" + allCards);

        // 2、发牌,首先肯定要定义 三个玩家。 List(ArrayList)  Set(TreeSet)
        List<Card> linHuChong = new ArrayList<>();
        List<Card> jiuMoZhi = new ArrayList<>();
        List<Card> renYingYing = new ArrayList<>();
        // 正式发牌给这三个玩家,依次发出51张牌,剩余3张做为底牌。
        // allCards = [♥3, ♣10, ♣4, ♥K, ♦Q, ♣2, 🃏, ♣8, ....
        //             0     1   2   3   4   5   6 ...   % 3
        for (int i = 0; i < allCards.size() - 3; i++) {
            Card c = allCards.get(i);
            // 判断牌发给谁
            if(i % 3 == 0){
                // 请啊冲接牌
                linHuChong.add(c);
            }else if(i % 3 == 1){
                // 请啊鸠来接牌
                jiuMoZhi.add(c);
            }else if(i % 3 == 2){
                // 请盈盈接牌
                renYingYing.add(c);
            }
        }

        // 3、对3个玩家的牌进行排序
        sortCards(linHuChong);
        sortCards(jiuMoZhi);
        sortCards(renYingYing);
        // 4、看牌
        System.out.println("啊冲:" + linHuChong);
        System.out.println("啊鸠:" + jiuMoZhi);
        System.out.println("盈盈:" + renYingYing);
        List<Card> lastThreeCards = allCards.subList(allCards.size() - 3, allCards.size()); // 51 52 53
        System.out.println("底牌:" + lastThreeCards);
        jiuMoZhi.addAll(lastThreeCards);
        sortCards(jiuMoZhi);
        System.out.println("啊鸠抢到地主后:" + jiuMoZhi);
    }

    /**
     * 集中进行排序
     * @param cards
     */
    private void sortCards(List<Card> cards) {
        Collections.sort(cards, new Comparator<Card>() {
            @Override
            public int compare(Card o1, Card o2) {
                // return o1.getSize() - o2.getSize(); // 升序排序
                return o2.getSize() - o1.getSize(); // 降序排序
            }
        });
    }
}



/**
 * 目标:掌握Map集合的特点。
 */
public class MapTest1 {
    public static void main(String[] args) {
        // Map<String, Integer> map = new HashMap<>(); // 一行经典代码。 按照键 无序,不重复,无索引。
        Map<String, Integer> map = new LinkedHashMap<>(); // 有序,不重复,无索引。
        map.put("手表", 100);
        map.put("手表", 220); // 后面重复的数据会覆盖前面的数据(键)
        map.put("手机", 2);
        map.put("Java", 2);
        map.put(null, null);
        System.out.println(map);

        Map<Integer, String> map1 = new TreeMap<>(); // 可排序,不重复,无索引
        map1.put(23, "Java");
        map1.put(23, "MySQL");
        map1.put(19, "李四");
        map1.put(20, "王五");
        System.out.println(map1);
    }
}


/**
 * 目标:掌握Map集合的常用方法(重点)
 */
public class MapTest2 {
    public static void main(String[] args) {
        // 1.添加元素: 无序,不重复,无索引。
        Map<String, Integer> map = new HashMap<>();
        map.put("手表", 100);
        map.put("手表", 220);
        map.put("手机", 2);
        map.put("Java", 2);
        map.put(null, null);
        System.out.println(map);
        // map = {null=null, 手表=220, Java=2, 手机=2}

        // 2.public int size():获取集合的大小
        System.out.println(map.size());

        // 3、public void clear():清空集合
        //map.clear();
        //System.out.println(map);

        // 4.public boolean isEmpty(): 判断集合是否为空,为空返回true ,反之!
        System.out.println(map.isEmpty());

        // 5.public V get(Object key):根据键获取对应值
        int v1 = map.get("手表");
        System.out.println(v1);
        System.out.println(map.get("手机")); // 2
        System.out.println(map.get("张三")); // null

        // 6. public V remove(Object key):根据键删除整个元素(删除键会返回键的值)
        System.out.println(map.remove("手表"));
        System.out.println(map);

        // 7.public  boolean containsKey(Object key): 判断是否包含某个键 ,包含返回true ,反之
        System.out.println(map.containsKey("手表")); // false
        System.out.println(map.containsKey("手机")); // true
        System.out.println(map.containsKey("java")); // false
        System.out.println(map.containsKey("Java")); // true

        // 8.public boolean containsValue(Object value): 判断是否包含某个值。
        System.out.println(map.containsValue(2)); // true
        System.out.println(map.containsValue("2")); // false

        // 9.public Set<K> keySet(): 获取Map集合的全部键。
        Set<String> keys = map.keySet();
        System.out.println(keys);

        // 10.public Collection<V> values(); 获取Map集合的全部值。
        Collection<Integer> values = map.values();
        System.out.println(values);

        // 11.把其他Map集合的数据倒入到自己集合中来。(拓展)
        Map<String, Integer> map1 = new HashMap<>();
        map1.put("java1",  10);
        map1.put("java2",  20);
        Map<String, Integer> map2 = new HashMap<>();
        map2.put("java3",  10);
        map2.put("java2",  222);
        map1.putAll(map2); // putAll:把map2集合中的元素全部倒入一份到map1集合中去。
        System.out.println(map1);
        System.out.println(map2);
    }
}

/**
 * 目标:完成Map集合的案例:统计投票人数。
 */
public class MapDemo4 {
    public static void main(String[] args) {
        // 1、把80个学生选择的景点数据拿到程序中来。
        List<String> data = new ArrayList<>();
        String[] selects = {"A", "B", "C", "D"};
        Random r = new Random();
        for (int i = 1; i <= 80; i++) {
            // 每次模拟一个学生选择一个景点,存入到集合中去。
            int index = r.nextInt(4); // 0 1 2 3
            data.add(selects[index]);
        }
        System.out.println(data);

        // 2、开始统计每个景点的投票人数
        // 准备一个Map集合用于统计最终的结果
        Map<String, Integer> result = new HashMap<>();

        // 3、开始遍历80个景点数据
        for (String s : data) {
            // 问问Map集合中是否存在该景点
            if(result.containsKey(s)){
                // 说明这个景点之前统计过。其值+1. 存入到Map集合中去
                result.put(s, result.get(s) + 1);
            }else {
                // 说明这个景点是第一次统计,存入"景点=1"
                result.put(s, 1);
            }
        }
        System.out.println(result);
    }
}

/**
 *  目标:掌握Map集合的遍历方式1:键找值
 */
public class MapTest1 {
    public static void main(String[] args) {
        // 准备一个Map集合。
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 162.5);
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8}

        // 1、获取Map集合的全部键
        Set<String> keys = map.keySet();
        // System.out.println(keys);
        // [蜘蛛精, 牛魔王, 至尊宝, 紫霞]
        //         key
        // 2、遍历全部的键,根据键获取其对应的值
        for (String key : keys) {
            // 根据键获取对应的值
            double value = map.get(key);
            System.out.println(key + "=====>" + value);
        }
    }
}
/**
 * 目标:掌握Map集合的第二种遍历方式:键值对。
 */
public class MapTest2 {
    public static void main(String[] args) {
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8}
        // entries = [(蜘蛛精=169.8), (牛魔王=183.6), (至尊宝=169.5), (紫霞=165.8)]
        //                                                         entry

        // 1、调用Map集合提供entrySet方法,把Map集合转换成键值对类型的Set集合
        Set<Map.Entry<String, Double>> entries = map.entrySet();
        for (Map.Entry<String, Double> entry : entries) {
            String key = entry.getKey();
            double value = entry.getValue();
            System.out.println(key + "---->" + value);
        }
    }
}

/**
 * 目标:掌握Map集合的第二种遍历方式:键值对。
 */
public class MapTest3 {
    public static void main(String[] args) {
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8}

//        map.forEach((k, v) -> {
//            System.out.println(k + "--->" + v);
//        });

//        map.forEach(new BiConsumer<String, Double>() {
//            @Override
//            public void accept(String k, Double v) {
//                System.out.println(k + "---->" + v);
//            }
//        });

        map.forEach(( k,  v) -> {
              System.out.println(k + "---->" + v);
        });
    }
}


public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private double height;

    // this  o
    @Override
    public int compareTo(Student o) {
        return this.age - o.age; // 年龄升序排序
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);
    }

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

    public Student() {
    }

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


    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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

/**
 * 目标:掌握Map集合下的实现类:HashMap集合的底层原理。
 */
public class Test1HashMap {
    public static void main(String[] args) {
        Map<Student, String> map = new HashMap<>();
        map.put(new Student("蜘蛛精", 25, 168.5), "盘丝洞");
        map.put(new Student("蜘蛛精", 25, 168.5), "水帘洞");
        map.put(new Student("至尊宝", 23, 163.5), "水帘洞");
        map.put(new Student("牛魔王", 28, 183.5), "牛头山");
        System.out.println(map);
    }
}

/**
 * 目标:掌握LinkedHashMap的底层原理。
 */
public class Test2LinkedHashMap {
    public static void main(String[] args) {
        // Map<String, Integer> map = new HashMap<>();    //  按照键 无序,不重复,无索引。
        LinkedHashMap<String, Integer> map = new LinkedHashMap<>(); //  按照键 有序,不重复,无索引。
        map.put("手表", 100);
        map.put("手表", 220);
        map.put("手机", 2);
        map.put("Java", 2);
        map.put(null, null);
        System.out.println(map);
    }
}

/**
 * 目标:掌握TreeMap集合的使用。
 */
public class Test3TreeMap {
    public static void main(String[] args) {
        Map<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Double.compare(o2.getHeight(), o1.getHeight());
            }
        });
//        Map<Student, String> map = new TreeMap<>(( o1,  o2) ->   Double.compare(o2.getHeight(), o1.getHeight()));
        map.put(new Student("蜘蛛精", 25, 168.5), "盘丝洞");
        map.put(new Student("蜘蛛精", 25, 168.5), "水帘洞");
        map.put(new Student("至尊宝", 23, 163.5), "水帘洞");
        map.put(new Student("牛魔王", 28, 183.5), "牛头山");
        System.out.println(map);
    }
}


/**
 * 目标:理解集合的嵌套。
 * 江苏省 = "南京市","扬州市","苏州市“,"无锡市","常州市"
 * 湖北省 = "武汉市","孝感市","十堰市","宜昌市","鄂州市"
 * 河北省 = "石家庄市","唐山市", "邢台市", "保定市", "张家口市"
 */
public class Test {
    public static void main(String[] args) {
        // 1、定义一个Map集合存储全部的省份信息,和其对应的城市信息。
        Map<String, List<String>> map = new HashMap<>();

        List<String> cities1 = new ArrayList<>();
        Collections.addAll(cities1, "南京市","扬州市","苏州市" ,"无锡市","常州市");
        map.put("江苏省", cities1);

        List<String> cities2 = new ArrayList<>();
        Collections.addAll(cities2, "武汉市","孝感市","十堰市","宜昌市","鄂州市");
        map.put("湖北省", cities2);

        List<String> cities3 = new ArrayList<>();
        Collections.addAll(cities3, "石家庄市","唐山市", "邢台市", "保定市", "张家口市");
        map.put("河北省", cities3);
        System.out.println(map);

        List<String> cities = map.get("湖北省");
        for (String city : cities) {
            System.out.println(city);
        }

        map.forEach((p, c) -> {
            System.out.println(p + "----->" + c);
        });
    }
}

可变参数

/**
 * 目标:认识可变参数,掌握其作用。
 */
public class ParamTest {
    public static void main(String[] args) {
        // 特点:
        test(); // 不传数据
        test(10); // 传输一个数据给它
        test(10, 20, 30); // 传输多个数据给它
        test(new int[]{10, 20, 30, 40}); // 传输一个数组给可变参数
    }

    // 注意事项1:一个形参列表中,只能有一个可变参数。
    // 注意事项2:可变参数必须放在形参列表的最后面
    public static void test(int...nums){
        // 可变参数在方法内部,本质就是一个数组。
        System.out.println(nums.length);
        System.out.println(Arrays.toString(nums));
        System.out.println("-----------------------------------------");
    }
}

stream流


/**
   目标:初步体验Stream流的方便与快捷
 */
public class StreamTest1 {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
        System.out.println(names);
        // names = [张三丰, 张无忌, 周芷若, 赵敏, 张强]
        //          name

        // 找出姓张,且是3个字的名字,存入到一个新集合中去。
        List<String> list = new ArrayList<>();
        for (String name : names) {
            if(name.startsWith("张") && name.length() == 3){
                list.add(name);
            }
        }
        System.out.println(list);

        // 开始使用Stream流来解决这个需求。
        List<String> list2 = names.stream().filter(s -> s.startsWith("张"))
                .filter(a -> a.length()==3).collect(Collectors.toList());
        System.out.println(list2);
    }
}

/**
 * 目标:掌握Stream流的创建。
 */
public class StreamTest2 {
    public static void main(String[] args) {
        // 1、如何获取List集合的Stream流?
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
        Stream<String> stream = names.stream();

        // 2、如何获取Set集合的Stream流?
        Set<String> set = new HashSet<>();
        Collections.addAll(set, "刘德华","张曼玉","蜘蛛精","马德","德玛西亚");
        Stream<String> stream1 = set.stream();
        stream1.filter(s -> s.contains("德")).forEach(s -> System.out.println(s));

        // 3、如何获取Map集合的Stream流?
        Map<String, Double> map = new HashMap<>();
        map.put("古力娜扎", 172.3);
        map.put("迪丽热巴", 168.3);
        map.put("马尔扎哈", 166.3);
        map.put("卡尔扎巴", 168.3);

        Set<String> keys = map.keySet();
        Stream<String> ks = keys.stream();

        Collection<Double> values = map.values();
        Stream<Double> vs = values.stream();

        Set<Map.Entry<String, Double>> entries = map.entrySet();
        Stream<Map.Entry<String, Double>> kvs = entries.stream();
        kvs.filter(e -> e.getKey().contains("巴"))
                .forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));

        // 4、如何获取数组的Stream流?
        String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
        Stream<String> s1 = Arrays.stream(names2);
        Stream<String> s2 = Stream.of(names2);
    }
}


/**
 * 目标:掌握Stream流提供的常见中间方法。
 */
public class StreamTest3 {
    public static void main(String[] args) {
        List<Double> scores = new ArrayList<>();
        Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
        // 需求1:找出成绩大于等于60分的数据,并升序后,再输出。
        scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s));

        List<Student> students = new ArrayList<>();
        Student s1 = new Student("蜘蛛精", 26, 172.5);
        Student s2 = new Student("蜘蛛精", 26, 172.5);
        Student s3 = new Student("紫霞", 23, 167.6);
        Student s4 = new Student("白晶晶", 25, 169.0);
        Student s5 = new Student("牛魔王", 35, 183.3);
        Student s6 = new Student("牛夫人", 34, 168.5);
        Collections.addAll(students, s1, s2, s3, s4, s5, s6);
        // 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
        students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .forEach(s -> System.out.println(s));

        // 需求3:取出身高最高的前3名学生,并输出。
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .limit(3).forEach(System.out::println);
        System.out.println("----------------------------------------------------------------");

        // 需求4:取出身高倒数的2名学生,并输出。   s1 s2 s3 s4 s5 s6
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .skip(students.size() - 2).forEach(System.out::println);

        // 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
        students.stream().filter(s -> s.getHeight() > 168).map(Student::getName)
               .distinct().forEach(System.out::println);

        // distinct去重复,自定义类型的对象(希望内容一样就认为重复,重写hashCode,equals)
        students.stream().filter(s -> s.getHeight() > 168)
                .distinct().forEach(System.out::println);

        Stream<String> st1 = Stream.of("张三", "李四");
        Stream<String> st2 = Stream.of("张三2", "李四2", "王五");
        Stream<String> allSt = Stream.concat(st1, st2);
        allSt.forEach(System.out::println);
    }
}

/**
 * 目标:Stream流的终结方法
 */
public class StreamTest4 {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        Student s1 = new Student("蜘蛛精", 26, 172.5);
        Student s2 = new Student("蜘蛛精", 26, 172.5);
        Student s3 = new Student("紫霞", 23, 167.6);
        Student s4 = new Student("白晶晶", 25, 169.0);
        Student s5 = new Student("牛魔王", 35, 183.3);
        Student s6 = new Student("牛夫人", 34, 168.5);
        Collections.addAll(students, s1, s2, s3, s4, s5, s6);
        // 需求1:请计算出身高超过168的学生有几人。
        long size = students.stream().filter(s -> s.getHeight() > 168).count();
        System.out.println(size);

        // 需求2:请找出身高最高的学生对象,并输出。
        Student s = students.stream().max((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get();
        System.out.println(s);

        // 需求3:请找出身高最矮的学生对象,并输出。
        Student ss = students.stream().min((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get();
        System.out.println(ss);

        // 需求4:请找出身高超过170的学生对象,并放到一个新集合中去返回。
        // 流只能收集一次。
        List<Student> students1 = students.stream().filter(a -> a.getHeight() > 170).collect(Collectors.toList());
        System.out.println(students1);

        Set<Student> students2 = students.stream().filter(a -> a.getHeight() > 170).collect(Collectors.toSet());
        System.out.println(students2);

        // 需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高,存入到一个Map集合返回。
        Map<String, Double> map =
                students.stream().filter(a -> a.getHeight() > 170)
                        .distinct().collect(Collectors.toMap(a -> a.getName(), a -> a.getHeight()));
        System.out.println(map);

        // Object[] arr = students.stream().filter(a -> a.getHeight() > 170).toArray();
        Student[] arr = students.stream().filter(a -> a.getHeight() > 170).toArray(len -> new Student[len]);
        System.out.println(Arrays.toString(arr));
    }
}


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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);
    }

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

    public Student() {
    }

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


    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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

io流

/**
 * 目标:掌握File创建对象,代表具体文件的方案。
 */
public class FileTest1 {
    public static void main(String[] args) {
        // 1、创建一个File对象,指代某个具体的文件。
        // 路径分隔符
        // File f1 = new File("D:/resource/ab.txt");
        // File f1 = new File("D:\\resource\\ab.txt");
        File f1 = new File("D:" + File.separator +"resource" + File.separator + "ab.txt");
        System.out.println(f1.length()); // 文件大小

        File f2 = new File("D:/resource");
        System.out.println(f2.length());

        // 注意:File对象可以指代一个不存在的文件路径
        File f3 = new File("D:/resource/aaaa.txt");
        System.out.println(f3.length());
        System.out.println(f3.exists()); // false

        // 我现在要定位的文件是在模块中,应该怎么定位呢?
        // 绝对路径:带盘符的
        // File f4 = new File("D:\\code\\javasepromax\\file-io-app\\src\\itheima.txt");
        // 相对路径(重点):不带盘符,默认是直接去工程下寻找文件的。
        File f4 = new File("file-io-app\\src\\huang.txt");
        System.out.println(f4.length());
    }
}
/**
     目标:掌握File提供的判断文件类型、获取文件信息功能
 */
public class FileTest2 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        // 1.创建文件对象,指代某个文件
        File f1 = new File("D:/resource/ab.txt");
        //File f1 = new File("D:/resource/");

        // 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
        System.out.println(f1.exists());

        // 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之。
        System.out.println(f1.isFile());

        // 4、public boolean isDirectory()  : 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
        System.out.println(f1.isDirectory());

        // 5.public String getName():获取文件的名称(包含后缀)
        System.out.println(f1.getName());

        // 6.public long length():获取文件的大小,返回字节个数
        System.out.println(f1.length());

        // 7.public long lastModified():获取文件的最后修改时间。
        long time = f1.lastModified();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        System.out.println(sdf.format(time));

        // 8.public String getPath():获取创建文件对象时,使用的路径
        File f2 = new File("D:\\resource\\ab.txt");
        File f3 = new File("file-io-app\\src\\huang.txt");
        System.out.println(f2.getPath());
        System.out.println(f3.getPath());

        // 9.public String getAbsolutePath():获取绝对路径
        System.out.println(f2.getAbsolutePath());
        System.out.println(f3.getAbsolutePath());
    }
}

/**
 * 目标:掌握File创建和删除文件相关的方法。
 */
public class FileTest3 {
    public static void main(String[] args) throws Exception {
        // 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
        File f1 = new File("D:/resource/huang2.txt");
        System.out.println(f1.createNewFile());

        // 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
        File f2 = new File("D:/resource/aaa");
        System.out.println(f2.mkdir());

        // 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
        File f3 = new File("D:/resource/bbb/ccc/ddd/eee/fff/ggg");
        System.out.println(f3.mkdirs());

        // 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
        System.out.println(f1.delete());
        System.out.println(f2.delete());
        File f4 = new File("D:/resource");
        System.out.println(f4.delete());
    }
}

/**
 * 目标:掌握File提供的遍历文件夹的方法。
 */
public class FileTest4 {
    public static void main(String[] args) {
        // 1、public String[] list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
        File f1 = new File("D:\\course\\待研发内容");
        String[] names = f1.list();
        for (String name : names) {
            System.out.println(name);
        }

        // 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
        File[] files = f1.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }

        File f = new File("D:/resource/aaa");
        File[] files1 = f.listFiles();
        System.out.println(Arrays.toString(files1));
    }
}


/**
 * 目标:认识一下递归的形式。
 */
public class RecursionTest1 {
    public static void main(String[] args) {
        test1();
    }

    // 直接方法递归
    public static void test1(){
        System.out.println("----test1---");
        test1(); // 直接方法递归
    }

    // 间接方法递归
    public static void test2(){
        System.out.println("---test2---");
        test3();
    }

    public static void test3(){
        test2(); // 间接递归
    }
}

/**
 * 目标:掌握递归的应用,执行流程和算法思想。
 */
public class RecursionTest2 {
    public static void main(String[] args) {
        System.out.println("5的阶乘是:" + f(5));
    }

    public static int f(int n){
        // 终结点
        if(n == 1){
            return 1;
        }else {
            return f(n - 1) * n;
        }
    }
}
/**
 * 目标:掌握文件搜索的实现。
 */
public class RecursionTest3 {
    public static void main(String[] args) throws Exception {
          searchFile(new File("D:/") , "QQ.exe");
    }

    /**
     * 去目录下搜索某个文件
     * @param dir  目录
     * @param fileName 要搜索的文件名称
     */
    public static void searchFile(File dir, String fileName) throws Exception {
        // 1、把非法的情况都拦截住
        if(dir == null || !dir.exists() || dir.isFile()){
            return; // 代表无法搜索
        }

        // 2、dir不是null,存在,一定是目录对象。
        // 获取当前目录下的全部一级文件对象。
        File[] files = dir.listFiles();

        // 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象。
        if(files != null && files.length > 0){
            // 4、遍历全部一级文件对象。
            for (File f : files) {
                // 5、判断文件是否是文件,还是文件夹
                if(f.isFile()){
                    // 是文件,判断这个文件名是否是我们要找的
                    if(f.getName().contains(fileName)){
                        System.out.println("找到了:" + f.getAbsolutePath());
                        Runtime runtime = Runtime.getRuntime();
                        runtime.exec(f.getAbsolutePath());
                    }
                }else {
                    // 是文件夹,继续重复这个过程(递归)
                    searchFile(f, fileName);
                }
            }
        }
    }
}


/**
 * 目标:掌握如何使用Java代码完成对字符的编码和解码。
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、编码
        String data = "a我b";
        byte[] bytes = data.getBytes(); // 默认是按照平台字符集(UTF-8)进行编码的。
        System.out.println(Arrays.toString(bytes));

        // 按照指定字符集进行编码。
        byte[] bytes1 = data.getBytes("GBK");
        System.out.println(Arrays.toString(bytes1));

        // 2、解码
        String s1 = new String(bytes); // 按照平台默认编码(UTF-8)解码
        System.out.println(s1);

        String s2 = new String(bytes1, "GBK");
        System.out.println(s2);
    }
}

/**
 * 目标:使用字节流完成对文件的复制操作。
 */
public class CopyTest5 {
    public static void main(String[] args) throws Exception {
        // 需求:复制照片。
        // 1、创建一个字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");
        // 2、创建一个字节输出流管道与目标文件接通。
        OutputStream os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt");

        System.out.println(10 / 0);
        // 3、创建一个字节数组,负责转移字节数据。
        byte[] buffer = new byte[1024]; // 1KB.
        // 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
        int len; // 记住每次读取了多少个字节。
        while ((len = is.read(buffer)) != -1){
            os.write(buffer, 0, len);
        }

        os.close();
        is.close();
        System.out.println("复制完成!!");
    }
}

/**
 * 目标:掌握文件字节输入流,每次读取一个字节。
 */
public class FileInputStreamTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建文件字节输入流管道,与源文件接通。
        // InputStream is = new FileInputStream(new File("file-io-app\\src\\itheima01.txt"));
        // 简化写法:推荐使用。
        InputStream is = new FileInputStream(("file-io-app\\src\\huang01.txt"));

        // 2、开始读取文件的字节数据。
        // public int read():每次读取一个字节返回,如果没有数据了,返回-1.
//        int b1 = is.read();
//        System.out.println((char)b1);
//
//        int b2 = is.read();
//        System.out.println((char) b2);
//
//        int b3 = is.read();
//        System.out.println(b3);

        // 3、使用循环改造上述代码
        int b; // 用于记住读取的字节。
        while ((b = is.read()) != -1){
            System.out.print((char) b);
        }

        // 读取数据的性能很差!
        // 读取汉字输出会乱码!!无法避免的!!
        // 流使用完毕之后,必须关闭!释放系统资源!
        is.close();
    }
}

/**
 * 目标:掌握使用FileInputStream每次读取多个字节。
 */
public class FileInputStreamTest2 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字节输入流对象代表字节输入流管道与源文件接通。
        InputStream is = new FileInputStream("file-io-app\\src\\itheima02.txt");

        // 2、开始读取文件中的字节数据:每次读取多个字节。
        //  public int read(byte b[]) throws IOException
        //  每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1.
//        byte[] buffer = new byte[3];
//        int len = is.read(buffer);
//        String rs = new String(buffer);
//        System.out.println(rs);
//        System.out.println("当次读取的字节数量:" + len);
//
//        // buffer = [abc]
//        // buffer = [66c]
//        int len2 = is.read(buffer);
//        // 注意:读取多少,倒出多少。
//        String rs2 = new String(buffer, 0, len2);
//        System.out.println(rs2);
//        System.out.println("当次读取的字节数量:" + len2);
//
//        int len3 = is.read(buffer);
//        System.out.println(len3); // -1

        // 3、使用循环改造。
        byte[] buffer = new byte[3];
        int len; // 记住每次读取了多少个字节。  abc 66
        while ((len = is.read(buffer)) != -1){
            // 注意:读取多少,倒出多少。
            String rs = new String(buffer, 0 , len);
            System.out.print(rs);
        }
        // 性能得到了明显的提升!!
        // 这种方案也不能避免读取汉字输出乱码的问题!!

        is.close(); // 关闭流
    }
}


/**
 * 目标:使用文件字节输入流一次读取完文件的全部字节。
 */
public class FileInputStreamTest3 {
    public static void main(String[] args) throws Exception {
        // 1、一次性读取完文件的全部字节到一个字节数组中去。
        // 创建一个字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");

        // 2、准备一个字节数组,大小与文件的大小正好一样大。
//        File f = new File("file-io-app\\src\\itheima03.txt");
//        long size = f.length();
//        byte[] buffer = new byte[(int) size];
//
//        int len = is.read(buffer);
//        System.out.println(new String(buffer));
//
//        System.out.println(size);
//        System.out.println(len);

        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));

        is.close(); // 关闭流
    }
}


/**
 * 目标:掌握文件字节输出流FileOutputStream的使用。
 */
public class FileOutputStreamTest4 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字节输出流管道与目标文件接通。
        // 覆盖管道:覆盖之前的数据
//        OutputStream os =
//                new FileOutputStream("file-io-app/src/itheima04out.txt");

        // 追加数据的管道
        OutputStream os =
                new FileOutputStream("file-io-app/src/huang04out.txt", true);

        // 2、开始写字节数据出去了
        os.write(97); // 97就是一个字节,代表a
        os.write('b'); // 'b'也是一个字节
        // os.write('磊'); // [ooo] 默认只能写出去一个字节

        byte[] bytes = "我爱你中国abc".getBytes();
        os.write(bytes);

        os.write(bytes, 0, 15);

        // 换行符
        os.write("\r\n".getBytes());

        os.close(); // 关闭流
    }
}

public class MyConnection implements AutoCloseable{
    @Override
    public void close() throws Exception {
        System.out.println("释放了与某个硬件的链接资源~~~~");
    }
}


/**
 * 目标:认识try-catch-finally。
 */
public class Test1 {
    public static void main(String[] args) {
        try {
            System.out.println(10 / 2);
            // return; // 跳出方法的执行
            // System.exit(0); // 虚拟机
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            System.out.println("===finally执行了一次===");
        }

        System.out.println(chu(10, 2));
    }

    public static int chu(int a, int b){
        try {
            return a / b;
        }catch (Exception e){
            e.printStackTrace();
            return -1; // 代表的是出现异常
        }finally {
            // 千万不要在finally中返回数据!
            return 111;
        }
    }
}

/**
 * 目标:掌握释放资源的方式。
 */
public class Test2 {
    public static void main(String[] args)  {
        InputStream is = null;
        OutputStream os = null;
        try {
            System.out.println(10 / 0);
            // 1、创建一个字节输入流管道与源文件接通
            is = new FileInputStream("file-io-app\\src\\itheima03.txt");
            // 2、创建一个字节输出流管道与目标文件接通。
            os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt");

            System.out.println(10 / 0);

            // 3、创建一个字节数组,负责转移字节数据。
            byte[] buffer = new byte[1024]; // 1KB.
            // 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
            int len; // 记住每次读取了多少个字节。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放资源的操作
            try {
                if(os != null) os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


/**
 * 目标:掌握释放资源的方式:try-with-resource
 */
public class Test3 {
    public static void main(String[] args)  {
        try (
                // 1、创建一个字节输入流管道与源文件接通
                InputStream is = new FileInputStream("file-io-app\\src\\itheima03.txt");
                // 2、创建一个字节输出流管道与目标文件接通。
                OutputStream os = new FileOutputStream("file-io-app\\src\\itheima03copy.txt");

                // 注意:这里只能放置资源对象。(流对象)
                // int age = 21;
                // 什么是资源呢?资源都是会实现AutoCloseable接口。资源都会有一个close方法,并且资源放到这里后
                // 用完之后,会被自动调用其close方法完成资源的释放操作。
                MyConnection conn = new MyConnection();
                ){

            // 3、创建一个字节数组,负责转移字节数据。
            byte[] buffer = new byte[1024]; // 1KB.
            // 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
            int len; // 记住每次读取了多少个字节。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0, len);
            }
            System.out.println(conn);
            System.out.println("复制完成!!");

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

public class FileReaderTest1 {
    public static void main(String[] args)  {
        try (
                // 1、创建一个文件字符输入流管道与源文件接通
                Reader fr = new FileReader("io-app2\\src\\huang01.txt");
                ){
            // 2、读取文本文件的内容了。
//            int c; // 记住每次读取的字符编号。
//            while ((c = fr.read()) != -1){
//                System.out.print((char) c);
//            }
            // 每次读取一个字符的形式,性能肯定是比较差的。

            // 3、每次读取多个字符。
            char[] buffer = new char[3];
            int len; // 记住每次读取了多少个字符。
            while ((len = fr.read(buffer)) != -1){
                // 读取多少倒出多少
                System.out.print(new String(buffer, 0, len));
            }
            // 性能是比较不错的!
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 * 目标:掌握文件字符输出流:写字符数据出去
 */
public class FileWriterTest2 {
    public static void main(String[] args) {
        try (
                // 0、创建一个文件字符输出流管道与目标文件接通。
                // 覆盖管道
                // Writer fw = new FileWriter("io-app2/src/huang02out.txt");
                // 追加数据的管道
                Writer fw = new FileWriter("io-app2/src/huang02out.txt", true);
                ){
            // 1、public void write(int c):写一个字符出去
            fw.write('a');
            fw.write(97);
            //fw.write('磊'); // 写一个字符出去
            fw.write("\r\n"); // 换行

            // 2、public void write(String c)写一个字符串出去
            fw.write("我爱你中国abc");
            fw.write("\r\n");

            // 3、public void write(String c ,int pos ,int len):写字符串的一部分出去
            fw.write("我爱你中国abc", 0, 5);
            fw.write("\r\n");

            // 4、public void write(char[] buffer):写一个字符数组出去
            char[] buffer = {'黑', '马', 'a', 'b', 'c'};
            fw.write(buffer);
            fw.write("\r\n");

            // 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
            fw.write(buffer, 0, 2);
            fw.write("\r\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 目标:搞清楚字符输出流使用时的注意事项。
 */
public class FileWriterTest3 {
    public static void main(String[] args) throws Exception {
        Writer fw = new FileWriter("io-app2/src/huang03out.txt");

        // 写字符数据出去
        fw.write('a');
        fw.write('b');
        fw.write('c');
        fw.write('d');
        fw.write("\r\n");

        fw.write("我爱你中国");
        fw.write("\r\n");
        fw.write("我爱你中国");

//        fw.flush(); // 刷新流。
//        fw.write("张三");
//        fw.flush();

        fw.close(); // 关闭流,关闭流包含刷新操作!
        // fw.write("张三");
    }
}
import java.io.*;
/**
 * 目标:掌握字节缓冲流的作用。
 */
public class BufferedInputStreamTest1 {
    public static void main(String[] args) {
        try (
                InputStream is = new FileInputStream("io-app2/src/huang01.txt");
                // 1、定义一个字节缓冲输入流包装原始的字节输入流
                InputStream bis = new BufferedInputStream(is);

                OutputStream os = new FileOutputStream("io-app2/src/huang01_bak.txt");
                // 2、定义一个字节缓冲输出流包装原始的字节输出流
                OutputStream bos = new BufferedOutputStream(os);
        ){

            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");

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


/**
 * 目标:掌握字符缓冲输入流的用法。
 */
public class BufferedReaderTest2 {
    public static void main(String[] args)  {
        try (
                Reader fr = new FileReader("io-app2\\src\\huang04.txt");
                // 创建一个字符缓冲输入流包装原始的字符输入流
                BufferedReader br = new BufferedReader(fr);
        ){
//            char[] buffer = new char[3];
//            int len;
//            while ((len = br.read(buffer)) != -1){
//                System.out.print(new String(buffer, 0, len));
//            }
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());
//            System.out.println(br.readLine());

            String line; // 记住每次读取的一行数据
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 * 目标:掌握字符缓冲输出流的用法。
 */
public class BufferedWriterTest3 {
    public static void main(String[] args) {
        try (
                Writer fw = new FileWriter("io-app2/src/huang05out.txt", true);
                // 创建一个字符缓冲输出流管道包装原始的字符输出流
                BufferedWriter bw = new BufferedWriter(fw);
        ){

            bw.write('a');
            bw.write(97);
            bw.write('磊');
            bw.newLine();

            bw.write("我爱你中国abc");
            bw.newLine();

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

/**
    目标:观察原始流和缓冲流的性能。
 */
public class TimeTest4 {
    // 复制的视频路径
    private static final String SRC_FILE = "D:\\resource\\线程池.avi";
    // 复制到哪个目的地
    private static final String DEST_FILE = "D:\\";

    public static void main(String[] args) {
        // copy01(); // 低级字节流一个一个字节的赋值,慢的简直让人无法忍受,直接淘汰!
        copy02();// 低级的字节流流按照一个一个字节数组的形式复制,速度较慢!
        // copy03(); // 缓冲流按照一个一个字节的形式复制,速度较慢,直接淘汰!
        copy04(); // 缓冲流按照一个一个字节数组的形式复制,速度极快,推荐使用!
    }

    private static void copy01() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                OutputStream os = new FileOutputStream(DEST_FILE + "1.avi");
        ){

            int b;
            while ((b = is.read()) != -1){
                os.write(b);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("低级字节流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy02() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                OutputStream os = new FileOutputStream(DEST_FILE + "2.avi");
        ){
            byte[] buffer = new byte[1024*64];
            int len;
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("低级字节流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy03() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                BufferedInputStream bis = new BufferedInputStream(is);
                OutputStream os = new FileOutputStream(DEST_FILE + "3.avi");
                BufferedOutputStream bos = new BufferedOutputStream(os);
        ){

            int b;
            while ((b = bis.read()) != -1){
                bos.write(b);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy04() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                BufferedInputStream bis = new BufferedInputStream(is, 64 * 1024);
                OutputStream os = new FileOutputStream(DEST_FILE + "4.avi");
                BufferedOutputStream bos = new BufferedOutputStream(os, 64 * 1024);
        ){
            byte[] buffer = new byte[1024 * 64]; // 32KB
            int len;
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }
}


/**
 * 目标:掌握字符输入转换流的作用。
 */
public class InputStreamReaderTest2 {
    public static void main(String[] args) {
        try (
                // 1、得到文件的原始字节流(GBK的字节流形式)
                InputStream is = new FileInputStream("io-app2/src/huang06.txt");
                // 2、把原始的字节输入流按照指定的字符集编码转换成字符输入流
                Reader isr = new InputStreamReader(is, "GBK");
                // 3、把字符输入流包装成缓冲字符输入流
                BufferedReader br = new BufferedReader(isr);
                ){
            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }


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

/**
 * 目标:掌握字符输出转换流的使用。
 */
public class OutputStreamWriterTest3 {
    public static void main(String[] args) {
        // 指定写出去的字符编码。
        try (
                // 1、创建一个文件字节输出流
                OutputStream os = new FileOutputStream("io-app2/src/huang07out.txt");
                // 2、把原始的字节输出流,按照指定的字符集编码转换成字符输出转换流。
                Writer osw = new OutputStreamWriter(os, "GBK");
                // 3、把字符输出流包装成缓冲字符输出流
                BufferedWriter bw = new BufferedWriter(osw);
                ){
            bw.write("我是中国人abc");
            bw.write("我爱你中国123");

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

/**
 * 目标:掌握不同编码读取乱码的问题。
 */
public class Test1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个文件字符输入流与源文件接通
                // 代码编码:UTF-8  文件的编码:UTF-8
                // Reader fr = new FileReader("io-app2/src/huang04.txt");

                //         1 床    前     明  月光c
                //  GBK    o [oo] [oo]  [oo] ...
                //  UTF-8  1 ?????
                // 代码编码:UTF-8 文件的编码:GBK
                Reader fr = new FileReader("io-app2/src/huang06.txt");
                // 2、把文件字符输入流包装成缓冲字符输入流
                BufferedReader br = new BufferedReader(fr);
                ){

            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 * 目标:掌握打印流:PrintStream/PrintWriter的用法。
 */
public class PrintTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个打印流管道
//                PrintStream ps =
//                        new PrintStream("io-app2/src/huang08.txt", Charset.forName("GBK"));
//                PrintStream ps =
//                        new PrintStream("io-app2/src/huang08.txt");
                PrintWriter ps =
                        new PrintWriter(new FileOutputStream("io-app2/src/huang08.txt", true));
                ){
                ps.println(97);
                ps.println('a');
                ps.println("我爱你中国abc");
                ps.println(true);
                ps.println(99.5);

                // ps.write(97); // 'a'

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


/**
 * 目标:了解下输出语句的重定向。
 */
public class PrintTest2 {
    public static void main(String[] args) {
        System.out.println("老骥伏枥");
        System.out.println("志在千里");

        try ( PrintStream ps = new PrintStream("io-app2/src/huang09.txt"); ){

            // 把系统默认的打印流对象改成自己设置的打印流
            System.setOut(ps);

            System.out.println("烈士暮年");
            System.out.println("壮心不已");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 目标:使用数据输入流读取特定类型的数据。
 */
public class DataInputStreamTest2 {
    public static void main(String[] args) {
        try (
                DataInputStream dis =
                        new DataInputStream(new FileInputStream("io-app2/src/huang10out.txt"));
                ){
            int i = dis.readInt();
            System.out.println(i);

            double d = dis.readDouble();
            System.out.println(d);

            boolean b = dis.readBoolean();
            System.out.println(b);

            String rs = dis.readUTF();
            System.out.println(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 * 目标:数据输出流。
 */
public class DataOutputStreamTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个数据输出流包装低级的字节输出流
                DataOutputStream dos =
                        new DataOutputStream(new FileOutputStream("io-app2/src/huang10out.txt"));
                ){
            dos.writeInt(97);
            dos.writeDouble(99.5);
            dos.writeBoolean(true);
            dos.writeUTF("黑马程序员666!");

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

/**
 * 目标:掌握对象字节输出流的使用:序列化对象。
 */
public class Test1ObjectOutputStream {
    public static void main(String[] args) {
        try (
                // 2、创建一个对象字节输出流包装原始的字节 输出流。
                ObjectOutputStream oos =
                        new ObjectOutputStream(new FileOutputStream("io-app2/src/huang11out.txt"));
                ){
            // 1、创建一个Java对象。
            User u = new User("admin", "张三", 32, "666888xyz");

            // 3、序列化对象到文件中去
            oos.writeObject(u);
            System.out.println("序列化对象成功!!");

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

/**
 * 目标:掌握对象字节输入流的使用:反序列化对象。
 */
public class Test2ObjectInputStream {
    public static void main(String[] args) {
        try (
                // 1、创建一个对象字节输入流管道,包装 低级的字节输入流与源文件接通
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("io-app2/src/huang11out.txt"));
                ){
            User u = (User) ois.readObject();
            System.out.println(u);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


// 注意:对象如果需要序列化,必须实现序列化接口。
public class User implements Serializable {
    private String loginName;
    private String userName;
    private int age;
    // transient 这个成员变量将不参与序列化。
    private transient String passWord;

    public User() {
    }

    public User(String loginName, String userName, int age, String passWord) {
        this.loginName = loginName;
        this.userName = userName;
        this.age = age;
        this.passWord = passWord;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

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

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "User{" +
                "loginName='" + loginName + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                ", passWord='" + passWord + '\'' +
                '}';
    }
}


/**
 * 目标:使用CommonsIO框架进行IO相关的操作。
 */
public class CommonsIOTest1 {
    public static void main(String[] args) throws Exception {
//        FileUtils.copyFile(new File("io-app2\\src\\huang01.txt"), new File("io-app2/src/a.txt"));
//        FileUtils.copyDirectory(new File("D:\\resource\\私人珍藏"), new File("D:\\resource\\私人珍藏3"));
//        FileUtils.deleteDirectory(new File("D:\\resource\\私人珍藏3"));

        // Java提供的原生的一行代码搞定很多事情
        // Files.copy(Path.of("io-app2\\src\\huang01.txt"), Path.of("io-app2\\src\\b.txt"));
        System.out.println(Files.readString(Path.of("io-app2\\src\\huang01.txt")));
    }
}

特殊文件


/**
 * 目标:掌握使用Properties类读取属性文件中的键值对信息。
 */
public class PropertiesTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个Properties的对象出来(键值对集合,空容器)
        Properties properties = new Properties();
        System.out.println(properties);

        // 2、开始加载属性文件中的键值对数据到properties对象中去
        properties.load(new FileReader("properties-xml-log-app\\src\\users.properties"));
        System.out.println(properties);

        // 3、根据键取值
        System.out.println(properties.getProperty("赵敏"));
        System.out.println(properties.getProperty("张无忌"));

        // 4、遍历全部的键和值。
        Set<String> keys = properties.stringPropertyNames();
        for (String key : keys) {
            String value = properties.getProperty(key);
            System.out.println(key + "---->" + value);
        }

        properties.forEach((k, v) -> {
            System.out.println(k + "---->" + v);
        });
    }
}

/**
 * 目标:掌握把键值对数据存入到属性文件中去
 */
public class PropertiesTest2 {
    public static void main(String[] args) throws Exception {
        // 1、创建Properties对象出来,先用它存储一些键值对数据
        Properties properties = new Properties();
        properties.setProperty("张无忌", "minmin");
        properties.setProperty("殷素素", "cuishan");
        properties.setProperty("张翠山", "susu");

        // 2、把properties对象中的键值对数据存入到属性文件中去
        properties.store(new FileWriter("properties-xml-log-app/src/users2.properties")
                , "i saved many users!");

    }
}


/**
 * 目标:掌握使用Dom4j框架解析XML文件。
 */
public class Dom4JTest1 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个Dom4J框架提供的解析器对象
        SAXReader saxReader = new SAXReader();

        // 2、使用saxReader对象把需要解析的XML文件读成一个Document对象。
        Document document =
                saxReader.read("properties-xml-log-app\\src\\helloworld.xml");

        // 3、从文档对象中解析XML文件的全部数据了
        Element root = document.getRootElement();
        System.out.println(root.getName());

        // 4、获取根元素下的全部一级子元素。
        // List<Element> elements = root.elements();
        List<Element> elements = root.elements("user");
        for (Element element : elements) {
            System.out.println(element.getName());
        }

        // 5、获取当前元素下的某个子元素。
        Element people = root.element("people");
        System.out.println(people.getText());

        // 如果下面有很多子元素user,默认获取第一个。
        Element user = root.element("user");
        System.out.println(user.elementText("name"));

        // 6、获取元素的属性信息呢?
        System.out.println(user.attributeValue("id"));
        Attribute id = user.attribute("id");
        System.out.println(id.getName());
        System.out.println(id.getValue());

        List<Attribute> attributes = user.attributes();
        for (Attribute attribute : attributes) {
            System.out.println(attribute.getName() + "=" + attribute.getValue());
        }

        // 7、如何获取全部的文本内容:获取当前元素下的子元素文本值
        System.out.println(user.elementText("name"));
        System.out.println(user.elementText("地址"));
        System.out.println(user.elementTextTrim("地址")); // 取出文本去除前后空格
        System.out.println(user.elementText("password"));

        Element data = user.element("data");
        System.out.println(data.getText());
        System.out.println(data.getTextTrim()); // 取出文本去除前后空格
    }
}

/**
 * 目标:如何使用程序把数据写出到 XML文件中去。
 * <?xml version="1.0" encoding="UTF-8" ?>
 * <book>
 *     <name>从入门到跑路</name>
 *     <author>dlei</author>
 *     <price>999.9</price>
 * </book>
 */
public class Dom4JTest2 {
    public static void main(String[] args) {
        // 1、使用一个StringBuilder对象来拼接XML格式的数据。
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n");
        sb.append("<book>\r\n");
        sb.append("\t<name>").append("从入门到跑路").append("</name>\r\n");
        sb.append("\t<author>").append("dlei").append("</author>\r\n");
        sb.append("\t<price>").append(999.99).append("</price>\r\n");
        sb.append("</book>");

        try (
                BufferedWriter bw = new BufferedWriter(new FileWriter("properties-xml-log-app/src/book.xml"));
                ){
            bw.write(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


/**
 * 目标:掌握LogBack日志框架的使用。
 */
public class LogBackTest {
    // 创建一个Logger日志对象
    public static final Logger LOGGER = LoggerFactory.getLogger("LogBackTest");

    public static void main(String[] args) {
        //while (true) {
            try {
                LOGGER.info("chu法方法开始执行~~~");
                chu(10, 0);
                LOGGER.info("chu法方法执行成功~~~");
            } catch (Exception e) {
                LOGGER.error("chu法方法执行失败了,出现了bug~~~");
            }
        //}
    }

    public static void chu(int a, int b){
        LOGGER.debug("参数a:" + a);
        LOGGER.debug("参数b:" + b);
        int c = a / b;
        LOGGER.info("结果是:" + c);
    }
}

日志

多线程


/**
 * 1、让这个类实现Callable接口
 */
public class MyCallable implements Callable<String> {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    // 2、重写call方法
    @Override
    public String call() throws Exception {
        // 描述线程的任务,返回线程执行返回后的结果。
        // 需求:求1-n的和返回。
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "线程求出了1-" + n + "的和是:" + sum;
    }
}

/**
 * 1、定义一个任务类,实现Runnable接口
 */
public class MyRunnable implements Runnable{
    // 2、重写runnable的run方法
    @Override
    public void run() {
        // 线程要执行的任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程输出 ===》" + i);
        }
    }
}


/**
 * 1、让子类继承Thread线程类。
 */
public class MyThread extends Thread{
    // 2、必须重写Thread类的run方法
    @Override
    public void run() {
        // 描述线程的执行任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程MyThread输出:" + i);
        }
    }
}

/**
 * 目标:掌握线程的创建方式一:继承Thread类
 */
public class ThreadTest1 {
    // main方法是由一条默认的主线程负责执行。
    public static void main(String[] args) {

        // 3、创建MyThread线程类的对象代表一个线程
        Thread t = new MyThread();
        // 4、启动线程(自动执行run方法的)
        t.start();  // main线程 t线程

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }
}


/**
 * 目标:掌握多线程的创建方式二:实现Runnable接口。
 */
public class ThreadTest2 {
    public static void main(String[] args) {
        // 3、创建任务对象。
        Runnable target = new MyRunnable();
        // 4、把任务对象交给一个线程对象处理。
        //  public Thread(Runnable target)
        new Thread(target).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出 ===》" + i);
        }
    }
}

/**
 * 目标:掌握多线程创建方式二的匿名内部类写法。
 */
public class ThreadTest2_2 {
    public static void main(String[] args) {
        // 1、直接创建Runnable接口的匿名内部类形式(任务对象)
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程1输出:" + i);
                }
            }
        };
        new Thread(target).start();

        // 简化形式1:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程2输出:" + i);
                }
            }
        }).start();

        // 简化形式2:
        new Thread(() -> {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程3输出:" + i);
                }
        }).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }
}


/**
 * 目标:掌握线程的创建方式三:实现Callable接口。
 */
public class ThreadTest3 {
    public static void main(String[] args) throws Exception {
        // 3、创建一个Callable的对象
        Callable<String> call = new MyCallable(100);
        // 4、把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务对象的作用?
        // 1、是一个任务对象,实现了Runnable对象.
        // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。
        FutureTask<String> f1  = new FutureTask<>(call);
        // 5、把任务对象交给一个Thread对象
        new Thread(f1).start();


        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> f2  = new FutureTask<>(call2);
        new Thread(f2).start();


        // 6、获取线程执行完毕后返回的结果。
        // 注意:如果执行到这儿,假如上面的线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。
        String rs = f1.get();
        System.out.println(rs);

        String rs2 = f2.get();
        System.out.println(rs2);
    }
}

public class MyThread extends Thread{
    public MyThread(String name){
        super(name); // 为当前线程设置名字了
    }
    @Override
    public void run() {
        // 哪个线程执行它,它就会得到哪个线程对象。
        Thread t = Thread.currentThread();
        for (int i = 1; i <= 3; i++) {
            System.out.println(t.getName() + "输出:" + i);
        }
    }
}


/**
 * 目标:掌握Thread的常用方法。
 */
public class ThreadTest1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread("1号线程");
        // t1.setName("1号线程");
        t1.start();
        System.out.println(t1.getName()); // Thread-0

        Thread t2 = new MyThread("2号线程");
        // t2.setName("2号线程");
        t2.start();
        System.out.println(t2.getName()); // Thread-1

        // 主线程对象的名字
        // 哪个线程执行它,它就会得到哪个线程对象。
        Thread m = Thread.currentThread();
        m.setName("最牛的线程");
        System.out.println(m.getName()); // main

        for (int i = 1; i <= 5; i++) {
            System.out.println(m.getName() + "线程输出:" + i);
        }
    }
}

/**
 * 目标:掌握sleep方法,join方法的作用。
 */
public class ThreadTest2 {
    public static void main(String[] args) throws Exception {
        System.out.println(Runtime.getRuntime().availableProcessors());
        for (int i = 1; i <= 5; i++) {
            System.out.println(i);
            // 休眠5s
            if(i == 3){
                // 会让当前执行的线程暂停5秒,再继续执行
                // 项目经理让我加上这行代码,如果用户交钱了,我就注释掉!
                 Thread.sleep(5000);
            }
        }

        // join方法作用:让当前调用这个方法的线程先执行完。
        Thread t1 = new MyThread("1号线程");
        t1.start();
        t1.join();

        Thread t2 = new MyThread("2号线程");
        t2.start();
        t2.join();

        Thread t3 = new MyThread("3号线程");
        t3.start();
        t3.join();
    }
}


public class Account {
    private String cardId; // 卡号
    private double money; // 余额。

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    // 小明 小红同时过来的
    public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}

public class DrawThread extends Thread{
    private Account acc;
    public DrawThread(Account acc, String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 取钱(小明,小红)
        acc.drawMoney(100000);
    }
}


/**
 * 目标:模拟线程安全问题。
 */
public class ThreadTest {
    public static void main(String[] args) {
         // 1、创建一个账户对象,代表两个人的共享账户。
        Account acc = new Account("ICBC-110", 100000);
        // 2、创建两个线程,分别代表小明 小红,再去同一个账户对象中取钱10万。
        new DrawThread(acc, "小明").start(); // 小明
        new DrawThread(acc, "小红").start(); // 小红
    }
}

public class Account {
    private String cardId; // 卡号
    private double money; // 余额。

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public static void test(){
        synchronized (Account.class){

        }
    }

    // 小明 小红线程同时过来的
    public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        // this正好代表共享资源!
        synchronized (this) {
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱后,余额剩余:" + this.money);
            }else {
                System.out.println(name + "来取钱:余额不足~");
            }
        }
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}

public class Account {
    private String cardId; // 卡号
    private double money; // 余额。

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public static void test(){
        synchronized (Account.class){

        }
    }

    // 小明 小红线程同时过来的
    public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        // this正好代表共享资源!
        synchronized (this) {
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱后,余额剩余:" + this.money);
            }else {
                System.out.println(name + "来取钱:余额不足~");
            }
        }
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}


public class DrawThread extends Thread{
    private Account acc;
    public DrawThread(Account acc, String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 取钱(小明,小红)
        acc.drawMoney(100000);
    }
}


/**
 * 目标:模拟线程安全问题。
 */
public class ThreadTest {
    public static void main(String[] args) {
        Account acc = new Account("ICBC-110", 100000);
        new DrawThread(acc, "小明").start(); // 小明
        new DrawThread(acc, "小红").start(); // 小红

        Account acc1 = new Account("ICBC-112", 100000);
        new DrawThread(acc1, "小黑").start(); // 小黑
        new DrawThread(acc1, "小白").start(); // 小白
    }
}

public class Account {
    private String cardId; // 卡号
    private double money; // 余额。

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public static void test(){
        synchronized (Account.class){

        }
    }

    // 小明 小红线程同时过来的
    // 同步方法
    public synchronized void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}


public class DrawThread extends Thread{
    private Account acc;
    public DrawThread(Account acc, String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 取钱(小明,小红)
        acc.drawMoney(100000);
    }
}

/**
 * 目标:模拟线程安全问题。
 */
public class ThreadTest {
    public static void main(String[] args) {
        Account acc = new Account("ICBC-110", 100000);
        new DrawThread(acc, "小明").start(); // 小明
        new DrawThread(acc, "小红").start(); // 小红
    }
}


public class Account {
    private String cardId; // 卡号
    private double money; // 余额。
    // 创建了一个锁对象
    private final Lock lk = new ReentrantLock();

    public Account() {
    }

    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    // 小明 小红线程同时过来的
    public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        try {
            lk.lock(); // 加锁
            // 1、判断余额是否足够
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱后,余额剩余:" + this.money);
            }else {
                System.out.println(name + "来取钱:余额不足~");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lk.unlock(); // 解锁
        }
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }


}

public class DrawThread extends Thread{
    private Account acc;
    public DrawThread(Account acc, String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 取钱(小明,小红)
        acc.drawMoney(100000);
    }
}


/**
 * 目标:模拟线程安全问题。
 */
public class ThreadTest {
    public static void main(String[] args) {
        Account acc = new Account("ICBC-110", 100000);
        new DrawThread(acc, "小明").start(); // 小明
        new DrawThread(acc, "小红").start(); // 小红
    }
}

public class Desk {
    private List<String> list = new ArrayList<>();

    // 放1个包子的方法
    // 厨师1 厨师2 厨师3
    public synchronized void put() {
        try {
            String name = Thread.currentThread().getName();
            // 判断是否有包子。
            if(list.size() == 0){
                list.add(name + "做的肉包子");
                System.out.println(name + "做了一个肉包子~~");
                Thread.sleep(2000);

                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }else {
                // 有包子了,不做了。
                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 吃货1 吃货2
    public synchronized void get() {
        try {
            String name = Thread.currentThread().getName();
            if(list.size() == 1){
                // 有包子,吃了
                System.out.println(name  + "吃了:" + list.get(0));
                list.clear();
                Thread.sleep(1000);
                this.notifyAll();
                this.wait();
            }else {
                // 没有包子
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 * 目标:了解一下线程通信。
 */
public class ThreadTest {
    public static void main(String[] args) {
        //   需求:3个生产者线程,负责生产包子,每个线程每次只能生产1个包子放在桌子上
        //      2个消费者线程负责吃包子,每人每次只能从桌子上拿1个包子吃。
        Desk desk  = new Desk();

        // 创建3个生产者线程(3个厨师)
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师1").start();

        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师2").start();

        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师3").start();

        // 创建2个消费者线程(2个吃货)
        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货1").start();

        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货2").start();
    }
}

/**
 * 1、让这个类实现Callable接口
 */
public class MyCallable implements Callable<String> {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    // 2、重写call方法
    @Override
    public String call() throws Exception {
        // 描述线程的任务,返回线程执行返回后的结果。
        // 需求:求1-n的和返回。
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "求出了1-" + n + "的和是:" + sum;
    }
}


public class MyRunnable implements Runnable{
    @Override
    public void run() {
        // 任务是干啥的?
        System.out.println(Thread.currentThread().getName() + " ==> 输出666~~");
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 目标:掌握线程池的创建。
 */
public class ThreadPoolTest1 {
    public static void main(String[] args) {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        Runnable target = new MyRunnable();
        pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        // 到了临时线程的创建时机了
        pool.execute(target);
        pool.execute(target);
        // 到了新任务的拒绝时机了!
        pool.execute(target);

        // pool.shutdown(); // 等着线程池的任务全部执行完毕后,再关闭线程池
        // pool.shutdownNow(); // 立即关闭线程池!不管任务是否执行完毕!
    }
}


/**
 * 目标:掌握线程池的创建。
 */
public class ThreadPoolTest2 {
    public static void main(String[] args) throws Exception {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 2、使用线程处理Callable任务。
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

/**
 * 目标:掌握线程池的创建。
 */
public class ThreadPoolTest3 {
    public static void main(String[] args) throws Exception {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
//        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
//                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.CallerRunsPolicy());

        // 1-2 通过Executors创建一个线程池对象。
        ExecutorService pool = Executors.newFixedThreadPool(17);
        // 老师:核心线程数量到底配置多少呢???
        // 计算密集型的任务:核心线程数量 = CPU的核数 + 1
        // IO密集型的任务:核心线程数量 = CPU核数 * 2

        // 2、使用线程处理Callable任务。
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

网络通信


/**
 * 目标:掌握InetAddress类的使用。
 */
public class InetAddressTest {
    public static void main(String[] args) throws Exception {
        // 1、获取本机IP地址对象的
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());

        // 2、获取指定IP或者域名的IP地址对象。
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());

        // ping www.baidu.com
        System.out.println(ip2.isReachable(6000));
    }
}

/**
 * 目标:完成UDP通信快速入门:实现1发1收。
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象(发韭菜出去的人)
        DatagramSocket socket = new DatagramSocket(7777);

        // 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                , InetAddress.getLocalHost(),  6666);

        // 3、开始正式发送这个数据包的数据出去了
        socket.send(packet);

        System.out.println("客户端数据发送完毕~~~");
        socket.close(); // 释放资源!
    }
}


/**
 * 目标:完成UDP通信快速入门-服务端开发
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 3、开始正式使用数据包来接收客户端发来的数据
        socket.receive(packet);

        // 4、从字节数组中,把接收到的数据直接打印出来
        // 接收多少就倒出多少
        // 获取本次数据包接收了多少数据。
        int len = packet.getLength();

        String rs = new String(buffer, 0 , len);
        System.out.println(rs);

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(packet.getPort());

        socket.close(); // 释放资源
    }
}

/**
 * 目标:完成UDP通信快速入门:实现客户端反复的发。
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建客户端对象(发韭菜出去的人)
        DatagramSocket socket = new DatagramSocket();

        // 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)
       /* public DatagramPacket(byte buf[], int length,
             InetAddress address, int port)
               参数一:封装要发出去的数据。
               参数二:发送出去的数据大小(字节个数)
               参数三:服务端的IP地址(找到服务端主机)
               参数四:服务端程序的端口。
             */
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦发现用户输入的exit命令,就退出客户端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临!退出成功!");
                socket.close(); // 释放资源
                break; // 跳出死循环
            }

            byte[] bytes = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length
                    , InetAddress.getLocalHost(),  6666);

            // 3、开始正式发送这个数据包的数据出去了
            socket.send(packet);
        }
    }
}


/**
 * 目标:完成UDP通信快速入门-服务端反复的收
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("----服务端启动----");
        // 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口
        DatagramSocket socket = new DatagramSocket(6666);

        // 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)
        byte[] buffer = new byte[1024 * 64]; // 64KB.
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        while (true) {
            // 3、开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);

            // 4、从字节数组中,把接收到的数据直接打印出来
            // 接收多少就倒出多少
            // 获取本次数据包接收了多少数据。
            int len = packet.getLength();

            String rs = new String(buffer, 0 , len);
            System.out.println(rs);

            System.out.println(packet.getAddress().getHostAddress());
            System.out.println(packet.getPort());
            System.out.println("--------------------------------------");
        }
    }
}


/**
 *  目标:完成TCP通信快速入门-客户端开发:实现1发1收。
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();

        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        // 4、开始写数据出去了
        dos.writeUTF("在一起,好吗?");
        dos.close();

        socket.close(); // 释放连接资源
    }
}

/**
 *  目标:完成TCP通信快速入门-服务端开发:实现1发1收。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        // 5、使用数据输入流读取客户端发送过来的消息
        String rs = dis.readUTF();
        System.out.println(rs);
        // 其实我们也可以获取客户端的IP地址
        System.out.println(socket.getRemoteSocketAddress());

        dis.close();
        socket.close();
    }
}


/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();

        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }

            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

/**
 *  目标:完成TCP通信快速入门-服务端开发:实现服务端反复发消息
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serverSocket.accept();

        // 3、从socket通信管道中得到一个字节输入流。
        InputStream is = socket.getInputStream();

        // 4、把原始的字节输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            try {
                // 5、使用数据输入流读取客户端发送过来的消息
                String rs = dis.readUTF();
                System.out.println(rs);
            } catch (Exception e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线了!");
                dis.close();
                socket.close();
                break;
            }
        }
    }
}


/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);
        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();
        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }
            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}


public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);

                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ClentReaderThread extends Thread{
    private Socket socket;
    public ClentReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    System.out.println("自己下线了:" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 *  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去
 */
public class Client {
    public static void main(String[] args) throws Exception {
        // 1、创建Socket对象,并同时请求与服务端程序的连接。
        Socket socket = new Socket("127.0.0.1", 8888);

        // 创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息。
        new ClentReaderThread(socket).start();

        // 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。
        OutputStream os = socket.getOutputStream();
        // 3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            // 一旦用户输入了exit,就退出客户端程序
            if("exit".equals(msg)){
                System.out.println("欢迎您下次光临!退出成功!");
                dos.close();
                socket.close();
                break;
            }
            // 4、开始写数据出去了
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static List<Socket> onLineSockets = new ArrayList<>();
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();
            onLineSockets.add(socket);
            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());
            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}


public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    // 把这个消息分发给全部客户端进行接收。
                    sendMsgToAll(msg);
                } catch (Exception e) {
                    System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
                    Server.onLineSockets.remove(socket);
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendMsgToAll(String msg) throws IOException {
        // 发送给全部在线的socket管道接收。
        for (Socket onLineSocket : Server.onLineSockets) {
            OutputStream os = onLineSocket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            System.out.println("有人上线了:" + socket.getRemoteSocketAddress());

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();
        }
    }
}



public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        //  立即响应一个网页内容:“黑马程序员”给浏览器展示。
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须换行
            ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666<div>");
            ps.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 *  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。
 */
public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("-----服务端启动成功-------");
        // 1、创建ServerSocket的对象,同时为服务端注册端口。
        ServerSocket serverSocket = new ServerSocket(8080);

        // 创建出一个线程池,负责处理通信管道的任务。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(8) , Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            // 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serverSocket.accept();

            // 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            pool.execute(new ServerReaderRunnable(socket));
        }
    }
}


public class ServerReaderRunnable implements Runnable{
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        //  立即响应一个网页内容:“黑马程序员”给浏览器展示。
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println(); // 必须换行
            ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666<div>");
            ps.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

单元测试

public class Calculator {
    public String name;
    public void printNumber(String name){
        System.out.println("名字长度是:" + name.length());
    }

    public double divide(int a, int b){
        return a / b;
    }
}
/**
 * 字符串工具类
 */
public class StringUtil {
    public static void printNumber(String name){
        if(name == null){
            System.out.println(0);
            return; // 停掉方法
        }
        System.out.println("名字长度是:" + name.length());
    }

    /**
     * 求字符串的最大索引
     */
    public static int getMaxIndex(String data){
        if(data == null) {
            return -1;
        }
        return data.length() - 1;
    }


}

 


/**
 * 测试类
 */
public class StringUtilTest {
    @Before
    public void test1(){
        System.out.println("---> test1 Before 执行了---------");
    }

    @BeforeClass
    public static void test11(){
        System.out.println("---> test11 BeforeClass 执行了---------");
    }

    @After
    public void test2(){
        System.out.println("---> test2 After 执行了---------");
    }

    @AfterClass
    public static void test22(){
        System.out.println("---> test22 AfterClass 执行了---------");
    }

    @Test // 测试方法
    public void testPrintNumber(){
        StringUtil.printNumber("admin");
        StringUtil.printNumber(null);
    }

    @Test // 测试方法
    public void testGetMaxIndex(){
        int index1 = StringUtil.getMaxIndex(null);
        System.out.println(index1);

        int index2 = StringUtil.getMaxIndex("admin");
        System.out.println(index2);

        // 断言机制:程序员可以通过预测业务方法的结果。
        Assert.assertEquals("方法内部有bug!", 4, index2);
    }
}

public class Test {
    public static void main(String[] args) {
        Calculator c = new Calculator();
    }
}

 

反射


public class Cat {
    public static int a;
    public static final String COUNTRY = "中国";
    private String name;
    private int age;

    public Cat(){
        System.out.println("无参数构造器执行了~~");
    }

    private Cat(String name, int age) {
        System.out.println("有参数构造器执行了~~");
        this.name = name;
        this.age = age;
    }

    private void run(){
        System.out.println("🐱跑的贼快~~");
    }

    public void eat(){
        System.out.println("🐱爱吃猫粮~");
    }

    private String eat(String name){
        return "🐱最爱吃:" + name;
    }

    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 "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class ObjectFrame {
    // 目标:保存任意对象的字段和其数据到文件中去
    public static void saveObject(Object obj) throws Exception {
        PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy-app\\src\\data.txt", true));
        // obj是任意对象,到底有多少个字段要保存。
        Class c = obj.getClass();
        String cName = c.getSimpleName();
        ps.println("---------------" + cName + "------------------------");
        // 2、从这个类中提取它的全部成员变量
        Field[] fields = c.getDeclaredFields();
        // 3、遍历每个成员变量。
        for (Field field : fields) {
            // 4、拿到成员变量的名字
            String name = field.getName();
            // 5、拿到这个成员变量在对象中的数据。
            field.setAccessible(true); // 禁止检查访问控制
            String value = field.get(obj) + "";
            ps.println(name + "=" + value);
        }
        ps.close();
    }
}

 


public class Student {
    private String name;
    private int age;
    private char sex;
    private double height;
    private String hobby;

    public Student() {
    }

    public Student(String name, int age, char sex, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.height = height;
        this.hobby = hobby;
    }

    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 char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
}

public class Teacher {
    private String name;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

 

/**
 * 目标:获取Class对象。
 */
public class Test1Class {
    public static void main(String[] args) throws Exception {
        Class c1 = Student.class;
        System.out.println(c1.getName()); // 全类名
        System.out.println(c1.getSimpleName()); // 简名:Student

        Class c2 = Class.forName("com.huang.d2_reflect.Student");
        System.out.println(c1 == c2);

        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c3 == c2);
    }
}

/**
 * 目标:掌握获取类的构造器,并对其进行操作。
 */
public class Test2Constructor {
    @Test
    public void testGetConstructors(){
        // 1、反射第一步:必须先得到这个类的Class对象
        Class c = Cat.class;
        // 2、获取类的全部构造器
        // Constructor[] constructors = c.getConstructors();
        Constructor[] constructors = c.getDeclaredConstructors();
        // 3、遍历数组中的每个构造器对象
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName() + "--->"
            + constructor.getParameterCount());
        }
    }

    @Test
    public void testGetConstructor() throws Exception {
        // 1、反射第一步:必须先得到这个类的Class对象
        Class c = Cat.class;
        // 2、获取类的某个构造器:无参数构造器
        Constructor constructor1 = c.getDeclaredConstructor();
        System.out.println(constructor1.getName() + "--->"
                + constructor1.getParameterCount());
        constructor1.setAccessible(true); // 禁止检查访问权限
        Cat cat = (Cat) constructor1.newInstance();
        System.out.println(cat);

        AtomicInteger a;


        // 3、获取有参数构造器
        Constructor constructor2 =
                c.getDeclaredConstructor(String.class, int.class);
        System.out.println(constructor2.getName() + "--->"
                + constructor2.getParameterCount());
        constructor2.setAccessible(true); // 禁止检查访问权限
        Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 3);
        System.out.println(cat2);

    }
}


/**
 * 目标:掌握获取类的成员变量,并对其进行操作。
 */
public class Test3Field {
    @Test
    public void testGetFields() throws Exception {
        // 1、反射第一步:必须是先得到类的Class对象
        Class c = Cat.class;
        // 2、获取类的全部成员变量。
        Field[] fields = c.getDeclaredFields();
        // 3、遍历这个成员变量数组
        for (Field field : fields) {
            System.out.println(field.getName() +  "---> "+ field.getType());
        }
        // 4、定位某个成员变量
        Field fName = c.getDeclaredField("name");
        System.out.println(fName.getName() + "--->" + fName.getType());

        Field fAge = c.getDeclaredField("age");
        System.out.println(fAge.getName() + "--->" + fAge.getType());

        // 赋值
        Cat cat = new Cat();
        fName.setAccessible(true); // 禁止访问控制权限
        fName.set(cat, "卡菲猫");
        System.out.println(cat);

        // 取值
        String name = (String) fName.get(cat);
        System.out.println(name);
    }
}

 


/**
 * 目标:掌握获取类的成员方法,并对其进行操作。
 */
public class Test4Method {
    @Test
    public void testGetMethods() throws Exception {
        //  1、反射第一步:先得到Class对象。
        Class c = Cat.class;
        // 2、获取类的全部成员方法。
        Method[] methods = c.getDeclaredMethods();
        // 3、遍历这个数组中的每个方法对象
        for (Method method : methods) {
            System.out.println(method.getName() + "--->"
                    + method.getParameterCount() + "---->"
                    + method.getReturnType());
        }
        //  4、获取某个方法对象
        Method run = c.getDeclaredMethod("run"); // 拿run方法,无参数的
        System.out.println(run.getName() + "--->"
                + run.getParameterCount() + "---->"
                + run.getReturnType());

        Method eat = c.getDeclaredMethod("eat", String.class);
        System.out.println(eat.getName() + "--->"
                + eat.getParameterCount() + "---->"
                + eat.getReturnType());

        Cat cat = new Cat();
        run.setAccessible(true); // 禁止检查访问权限
        Object rs = run.invoke(cat); // 调用无参数的run方法,用cat对象触发调用的。
        System.out.println(rs);

        eat.setAccessible(true); // 禁止检查访问权限
        String rs2 = (String) eat.invoke(cat, "鱼儿");
        System.out.println(rs2);
    }
}
/**
 * 目标:使用反射技术:设计一个保存对象的简易版框架。
 */
public class Test5Frame {
    @Test
    public void save() throws Exception {
        Student s1 = new Student("黑马吴彦祖", 45, '男', 185.3, "蓝球,冰球,阅读");
        Teacher t1 = new Teacher("播妞", 999.9);

        // 需求:把任意对象的字段名和其对应的值等信息,保存到文件中去。
        ObjectFrame.saveObject(s1);
        ObjectFrame.saveObject(t1);
    }
}

注解


@MyTest1(aaa="牛魔王", ccc={"HTML", "Java"})
// @MyTest2(value = "孙悟空")
//@MyTest2("孙悟空")
//@MyTest2(value = "孙悟空", age = 1000)
@MyTest2("孙悟空")
public class AnnotationTest1 {
    @MyTest1(aaa="铁扇公主", bbb=false, ccc={"Python", "前端", "Java"})
    public void test1(){

    }

    public static void main(String[] args) {

    }
}

 

/**
 * 目标:认识元注解,搞清楚元注解的作用。
 */
@MyTest3
public class AnnotationTest2 {

    // @MyTest3
    private String name;

    @MyTest3
    public void test(){

    }
}

/**
 * 目标:掌握注解的解析。
 */
public class AnnotationTest3 {
    @Test
    public void parseClass(){
        // 1、先得到Class对象
        Class c = Demo.class;
        // 2、解析类上的注解
        // 判断类上是否包含了某个注解
        if(c.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 =
                    (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }

    @Test
    public void parseMethod() throws Exception {
        // 1、先得到Class对象
        Class c = Demo.class;
        Method m = c.getDeclaredMethod("test1");
        // 2、解析方法上的注解
        // 判断方法对象上是否包含了某个注解
        if(m.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 =
                    (MyTest4) m.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }
}

 

**
 * 目标:模拟Junit框架的设计。
 */
public class AnnotationTest4 {
    // @MyTest
    public void test1(){
        System.out.println("===test1====");
    }

    @MyTest
    public void test2(){
        System.out.println("===test2====");
    }

    @MyTest
    public void test3(){
        System.out.println("===test3====");
    }

    @MyTest
    public void test4(){
        System.out.println("===test4====");
    }

    public static void main(String[] args) throws Exception {
        AnnotationTest4 a = new AnnotationTest4();
        // 启动程序!
        // 1、得到Class对象
        Class c = AnnotationTest4.class;
        // 2、提取这个类中的全部成员方法
        Method[] methods = c.getDeclaredMethods();
        // 3、遍历这个数组中的每个方法,看方法上是否存在@MyTest注解,存在
        // 触发该方法执行。
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                // 说明当前方法上是存在@MyTest,触发当前方法执行。
                method.invoke(a);
            }
        }
    }
}
@MyTest4(value = "蜘蛛精", aaa=99.5, bbb = {"至尊宝", "黑马"})
@MyTest3
public class Demo {
    @MyTest4(value = "孙悟空", aaa=199.9, bbb = {"紫霞", "牛夫人"})
    public void test1(){
    }
}

 


@Target(ElementType.METHOD) // 注解只能注解方法。
@Retention(RetentionPolicy.RUNTIME) // 让当前注解可以一直存活着。
public @interface MyTest {
}
/**
 * 自定义注解
 */
public @interface MyTest1 {
    String aaa();
    boolean bbb() default true;
    String[] ccc();
}

 

public @interface MyTest2 {
    String value(); // 特殊属性
    int age() default 23;
}
@Target({ElementType.TYPE, ElementType.METHOD}) // 当前被修饰的注解只能用在类上,方法上。
@Retention(RetentionPolicy.RUNTIME) // 控制下面的注解一直保留到运行时
public @interface MyTest3 {
}

 


@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
    String value();
    double aaa() default 100;
    String[] bbb();
}

动态代理

public class BigStar implements Star{
    private String name;

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

    public String sing(String name){
        System.out.println(this.name + "正在唱:" + name);
        return "谢谢!谢谢!";
    }

    public void dance(){
        System.out.println(this.name  + "正在优美的跳舞~~");
    }
}

public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){
       /* newProxyInstance(ClassLoader loader,
                Class<?>[] interfaces,
                InvocationHandler h)
                参数1:用于指定一个类加载器
                参数2:指定生成的代理长什么样子,也就是有哪些方法
                参数3:用来指定生成的代理对象要干什么事情
                */
        // Star starProxy = ProxyUtil.createProxy(s);
        // starProxy.sing("好日子") starProxy.dance()
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class}, new InvocationHandler() {
                    @Override // 回调方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 代理对象要做的事情,会在这里写代码
                        if(method.getName().equals("sing")){
                            System.out.println("准备话筒,收钱20万");
                        }else if(method.getName().equals("dance")){
                            System.out.println("准备场地,收钱1000万");
                        }
                        return method.invoke(bigStar, args);
                    }
                });
        return starProxy;
    }
}
public interface Star {
    String sing(String name);
    void dance();
}

 

public class Test {
    public static void main(String[] args) {
        BigStar s = new BigStar("杨超越");
        Star starProxy = ProxyUtil.createProxy(s);

        String rs = starProxy.sing("好日子");
        System.out.println(rs);

        starProxy.dance();
    }
}

public class ProxyUtil {
    public static UserService createProxy(UserService userService){
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{UserService.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        if(method.getName().equals("login") || method.getName().equals("deleteUsers")||
                               method.getName().equals("selectUsers")){
                            long startTime = System.currentTimeMillis();

                            Object rs = method.invoke(userService, args);

                            long endTime = System.currentTimeMillis();
                            System.out.println(method.getName() + "方法执行耗时:" + (endTime - startTime)/ 1000.0 + "s");
                            return rs;
                        }else {
                            Object rs = method.invoke(userService, args);
                            return rs;
                        }
                    }
                });
        return userServiceProxy;
    }
}

 


 * 目标:使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 1、创建用户业务对象。
        UserService userService = ProxyUtil.createProxy(new UserServiceImpl());

        // 2、调用用户业务的功能。
        userService.login("admin", "123456");
        System.out.println("----------------------------------------------------");

        userService.deleteUsers();
        System.out.println("----------------------------------------------------");

        String[] names = userService.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));
        System.out.println("----------------------------------------------------");

    }
}
/**
 *  用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName,String passWord) throws Exception;
    // 删除用户
    void deleteUsers() throws Exception;
    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
}

/**
 * 用户业务实现类(面向接口编程)
 */
public class UserServiceImpl implements UserService{
    @Override
    public void login(String loginName, String passWord) throws Exception {
        if("admin".equals(loginName) && "123456".equals(passWord)){
            System.out.println("您登录成功,欢迎光临本系统~");
        }else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
    }

    @Override
    public void deleteUsers() throws Exception{
        System.out.println("成功删除了1万个用户~");
        Thread.sleep(1500);
    }

    @Override
    public String[] selectUsers() throws Exception{

        System.out.println("查询出了3个用户");
        String[] names = {"张全蛋", "李二狗", "牛爱花"};
        Thread.sleep(500);

        return names;
    }
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值