Java学习--基础知识

文章目录

Java学习

基础知识

1. 基本数据类型转换

image-20220326205654496

2. 包的命名

image-20220327214244966

3. 访问修饰符

image-20220327214529061

只有默认 和 public 才能修饰类

IDEA小技巧

image-20220327212729670

1. 面向对象

1.1 封装

1.2 继承

image-20220411140053078

查看继承关系

右键 --> 图表 --> 显示图

Java是单继承机制,如果想要A类继承B类和C类,可以A继承B,B继承C

继承的 JVM 内存分配

image-20220327223542546

当子类、父类有同名成员变量时,为就近原则使用该变量。

使用 super时,是满足就近原则,即如果父类没有该变量super则指向的是爷爷类

1.3 方法重写

image-20220328094102842

1.4 多态

1.4.0 类在什么时候加载

image-20220330103223591

1.4.1 向上转型

image-20220328105314197

这里 animal 只能调用Animal里面有的成员函数,而不能调用Cat里特有的成员函数; 最终的运行效果由子类的函数完成

//这里animal的编译类型为Animal,运行类型为Cat

编译类型为什么你就可以调用该类型的什么函数,而运行类型是什么则你在运行期间则跑的就是该运行类型的函数实现

1.4.2 向下转型(接上图)

image-20220328110901819

image-20220328110236410

//这里cat的编译类型 为Cat。运行类型也为Cat

注意向下转型时只能转引用 不能转对象,(引用可以理解为指向对象的指针),向下转型时,原本是父类的引用 强制变为子类引用

image-20220328111012355

image-20220328113321459

//注意:这里animal指向的是一个Cat,向下转型时就不能将 animal转成Dog了

image-20220328151843150

image-20220328145812764

//这里看得是编译类型:base 的编译类型是Base 所以输出为 10
1.4.3 小结
//小结:调用属性(成员变量)看编译类型,调用方法看运行属性

image-20220328153407651

instanceof --> 注意是 运行类型

image-20220328151427015

package study2;

public class ClassTest {
    public static void main(String[] args) {
        Person son = new Son();
        System.out.println(son instanceof Son);
        System.out.println(son instanceof Person);

        Son son1 = (Son) son;
        System.out.println(son1 instanceof Person);
        System.out.println(son1 instanceof Son);
    }
}
class Person{}
class Son extends Person{}

1.4.4 Java动态绑定机制
package study2;

public class ClassTest {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.age);
        System.out.println(a.getAge());
        System.out.println(a.sum());		//注意这里,调用方法时是根据运行类型的所以调用了B中的sum,但是B中Sum被注释掉了,所以调用A中的sum,但A中的sum里有个getAge那这个getAge父类子类都有,该调用谁呢?ans:仍然调用B 即运行类型中的方法,因为就是这个Java动态绑定机制
        System.out.println(a.sum1());		//对比上面,a调用sum1,但是运行类型B中的sum1被注释掉了,所以调用A中的sum1,但是sum1中return age+1; 这里这个age是属性(成员变量),没有动态绑定机制--哪里声明,哪里使用
    }
}

class A{
    public int age = 1;
    public int getAge(){
        return age;
    }
    public int sum(){
        return getAge() + 99;
    }
    public int sum1(){
        return age + 1;
    }
}

class B extends A{
    public int age = 10;

    @Override
    public int getAge() {
        return age;
    }

/*
    @Override
    public int sum() {
        return getAge()+999;
    }
*/
/*
    @Override
    public int sum1() {
        return age + 1;
    }
 */
}
1.4.5 多态的应用
  1. 多态数组:数组定义为父类类型,里面保存的实际元素为子类类型
package test1;

public class PloyArray {
    public static void main(String[] args) {
        Person[] arr = {new Student("小明",1),new Student("小黑",2),new Teacher("老王",40)};
        for (Person temp :arr){
            System.out.println(temp.name + temp.age);
            temp.Say();
            if(temp instanceof Student){
                ((Student) temp).study();
            }else if (temp instanceof Teacher){
                ((Teacher) temp).teach();
            }
        }
    }
}

abstract class Person {
    public String name;
    public int age;
    abstract public void Say();
}

class Student extends Person{
    Student(){}

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

    @Override
    public void Say() {
        System.out.println("I am a Student");
    }

    public void study(){
        System.out.println(this.name +" is studying now");
    }
}

class Teacher extends Person{
    Teacher(){}

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

    @Override
    public void Say() {
        System.out.println("I am a Teacher");
    }

    public void teach(){
        System.out.println(this.name + " is teaching now");
    }
}

  1. 多态参数

    即形参是父类,实际上可以传入子类对象

1.4.6 == 运算符 和 equals 对比 可以根据源码进行比较

==

image-20220328172859087

image-20220328172640011

可以这么想,new 一个对象就是开辟了一块内存地址,然后不管是编译类型为子类还是父类都指向这个地址的话,那他们就是相等的,因为指向的东西是同一个

equals

image-20220328172928138

package test2;

public class Equals {
    public static void main(String[] args) {
        Integer a = new Integer(20000);
        Integer b = new Integer(20000);
        System.out.println(a == b);         //false
        System.out.println(a.equals(b));    //true

        String str1 = new String("nihao");
        String str2 = new String("nihao");
        System.out.println(str1 == str2);           //false
        System.out.println(str1.equals(str2));      //true
    }
}

练习

image-20220328200158470

重写equals

package test2;

import java.util.Objects;

public class Equals {
    public static void main(String[] args) {
        Person person1 = new Person("Jack", 10, '男');
        Person person2 = new Person("Jack", 10, '男');
        System.out.println(person1.equals(person2));    //若没有重写equals则调用的是Object的equals--false

    }
}
class Person{
    private String name;
    private int age;
    private char gender;

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

    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 getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    //重写equals使其可以比较自定义数据类型
    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        }
        if (obj instanceof Person){
            return ((Person)obj).getName().equals(this.getName()) &&
                    ((Person)obj).getAge() == this.getAge() &&
                    ((Person)obj).getGender() == this.getGender();
        }
        return false;
    }
}
1.4.7 HashCode方法

image-20220328201039084

注意在Java中获得的地址不是真正的内存地址,因为Java是运行在JVM虚拟机上的

1.4.8 toString 方法

image-20220328202203073

package test2;

public class test2 {
    public static void main(String[] args) {
        Persons person = new Persons("小王", 19);
        System.out.println("person's toString is "+ person.toString());
        System.out.println("person's Dec = "+person.hashCode());
        System.out.println("person's Dec transform to Hex is "+Integer.toHexString(person.hashCode()));
    }
}

class Persons{
    private String name;
    private int age;

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

image-20220328203135915

//重写toString
package test2;

public class test2 {
    public static void main(String[] args) {
        Persons person = new Persons("小王", 19);
        System.out.println("person's toString is "+ person.toString());
        System.out.println("person's Dec = "+person.hashCode());
        System.out.println("person's Dec transform to Hex is "+Integer.toHexString(person.hashCode()));
    }
}

class Persons{
    private String name;
    private int age;

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

    @Override
    public String toString() {      //一般重写toString是将成员变量输出,但也可以自己定义
        return "Persons{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

image-20220328203453699

1.5 类变量(静态变量/静态属性)

即 static 变量

定义语法:访问修饰符 + static + 数据类型 + 变量名

访问语法:类名.类变量名(推荐) or 对象名.类变量名(会警告)

1.6 类方法(静态方法)

即 static 函数,类方法中无this的参数,而普通方法中隐含着this参数

定义语法:访问修饰符 + static + 数据返回类型 方法名() { }

访问方法:类名.类方法(推荐) or 对象名.类方法(会警告)

package 学习1;

public class Static {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.b);
        A.show();
        a.show2();
    }
}
class A{
    public int a = 10;
    public static int b = 20;
    public static void show(){
//        System.out.println("It is a = "+ a);   //false,静态方法在加载时就生成,而普通成员变量在创建对象时才有,所以静态函数无法调用普通成员变量

        System.out.println("It is b = "+ b);    //true
        System.out.println("It is a static function");
    }
    public void show2(){
        System.out.println("It is a = "+ a);	//true
        System.out.println("It is b = "+ b);    //true,非静态方法则可以调用静态变量 或者是 非静态变量
    }
}

类方法经典的使用场景:当方法中不涉及到任何对象相关的成员,则可以将方法设计成静态方法,提高开发效率,即 一些通用的方法 或者 不希望创建类来调用的方法

2. main方法

image-20220330095121528

//假如有个 hello.java 文件,文件如下
public class hello{
	public static void main(String[] args){
		for(int i = 0;i < args.length;i++){
			System.out.println("第"+(i+1)+"个参数是 "+args[i]);
		}
	}
}

//cmd
javac hello.java				//编译
java hello						//output 空
java hello tom tony smith		//output 第1个参数是 tom,第二个参数是 tony 第三个参数是 smith

image-20220330095820962

3. 代码块

image-20220330100413779

package 学习1;

public class Codeblocks {
    public static void main(String[] args) {
        /*
         * 普通代码块:在方法中创造,并根据编写的顺序进行调用
         */
        {
            System.out.println("This is a normal codeblock_1");
        }
        Movie movie = new Movie();
        movie.show();
        {
            System.out.println("This is a normal codeblock_2");
        }
        Movie movie1 = new Movie("盗梦空间");
        movie1.show();

        Movie movie2 = new Movie("无双", 100);
        movie2.show();
        {
            System.out.println("This is a normal codeblock_3");
        }
    }
}
class Movie{
    private String name;
    private int price;

    /*
     *构造器代码块 :放在类中
     * 每次使用构造器 都会隐式调用构造器代码块
     * 如果只调用类的静态成员时,普通代码块不被执行
     */
    {
        System.out.println("广告...");
        System.out.println("注意事项...");
        System.out.println("电影开始...");
    }

    /*
     * 1.静态代码块,比构造器代码块先执行,哪怕它在构造构造器代码块的下面才编写
     * 2.随着类的加载而执行且只运行 1 次
     *	如:Movie aa = new Movie();
     *	   Movie aa2 = new Movie();
     *	   -->static 代码块也只执行一次
     */
    static {
        System.out.println("This is a static codeblock");
    }

    public Movie(){
        System.out.println("Movie() function");
        this.name = "空";
        System.out.println("Movie is "+ this.name);
    }
    public Movie(String name){
        System.out.println("Movie(String name) function");
        this.name = name;
        System.out.println("Movie is "+this.name);
    }
    public Movie(String name,int price){
        System.out.println("Movie(String name,int price) function");
        this.name = name;
        this.price = price;
        System.out.println("Movie is "+this.name+"  Price is "+ this.price);
    }

    //该方法用于测试会不会调用构造器代码块:不会
    public void show(){
        System.out.println("It will use codeblocks?");
    }

}
//执行结果
E:\JavaJDK\bin\java.exe "-javaagent:E:\JavaIDE\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=49373:E:\JavaIDE\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\WorkSpace\JAVA\Java学习第二阶段\out\production\Java学习第二阶段 学习1.Codeblocks
This is a normal codeblock_1
This is a static codeblock
广告...
注意事项...
电影开始...
Movie() function
Movie is 空
It will use codeblocks?
This is a normal codeblock_2
广告...
注意事项...
电影开始...
Movie(String name) function
Movie is 盗梦空间
It will use codeblocks?
广告...
注意事项...
电影开始...
Movie(String name,int price) function
Movie is 无双  Price is 100
It will use codeblocks?
This is a normal codeblock_3

进程已结束,退出代码0

注意事项

image-20220330104408704

无静态代码块:

有继承:先super(),再普通代码块和普通属性(即非静态成员变量),再自己的构造器

没继承:先普通代码块和普通属性(即非静态成员变量),再自己的构造器

image-20220330104829825
image-20220330105642745

4. 单例设计模式

4.1 饿汉式单例设计模式

为什么要叫饿汉式:因为类只要一加载 该对象就创建好了(又可能这个对象都还没用,就创建好了,显得很着急–饿汉)

//【单例设计模式-饿汉式】步骤
//1.将构造器私有化		--> 防止直接new
//2.在类的内部直接创建对象(该对象是static)
//3.提供一个公共的static方法,返回创建的对象
package 单例设计模式;

public class SingleTon {
    public static void main(String[] args) {
        Father f = Father.getFather();
        System.out.println(f);

        Father f2 = Father.getFather();     
        System.out.println(f2);             //输入的依旧是同一个对象
    }
}

class Father{
    private String name;
    private Father(String name){    //将构造器private化,外部则无法创建多个对象
        this.name = name;
    }
    private static Father father = new Father("杨"); //在类内部创建唯一一个对象static,保证单例(单个实例)设计模式
    public static Father getFather(){      //通过public函数返回,即可得到这个唯一对象
        return father;
    }

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

饿汉式可能创建了对象但是没有使用,资源可能会浪费

4.2 懒汉式单例设计模式

//【单例设计模式-懒汉式】步骤
//1.将构造器私有化
//2.定义一个static静态属性对象 不去new(new了就是饿汉式)
//3.提供一个public的方法去创建对象然后返回该对象
//4.只用当用户使用getFather,才返回Father对象,后面再次调用时,会返回上次创建的father对象
package 单例设计模式;

public class SingleTon2 {
    public static void main(String[] args) {
        System.out.println(Father.n);   //output 1,并没有调用构造器

        Father f = Father.getFather();  //这里才调用了构造器,需要用到对象才会去new
        System.out.println(f);

    }
}

class Father{
    private String name;
    public static int n = 1;
    private Father(String name){
        System.out.println("Construction");
        this.name = name;
    }
    private static Father father;   //和饿汉式不同,先不去new
    public static Father getFather(){
        if (father == null){
            father = new Father("杨");
        }
        return father;
    }

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

对比

image-20220330171448113

5. final关键字

image-20220330171801538

image-20220330172415909

image-20220330172822297

6. abstract

  1. 抽象类不能实例化
package 抽象类;

public class Abstract {
    public static void main(String[] args) {
        Animal animal = new Dog();  //向上转型,只能调用编译类型里有的函数
        animal.eat();
        
        //      Animal animal1 = new Animal();  //false
    }
}
abstract class Animal{  //有抽象函数的类 必须声明为抽象类
    private String name;
    private String food;
    public abstract void eat(); //抽象函数 == 纯虚函数,不能有方法体
}
class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("Dog eating");
    }
    public void drink(){
        System.out.println("Dog drinking");
    }
}
class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("Cat eating");
    }
}
  1. 抽象类不一定要有抽象方法
//1.    抽象类中可以没有抽象方法,且方法还是可以有实现体的
//2.    不带继承关系也是一样,抽象类中可以没有抽象方法,且方法还是可以有实现体的
//3.    普通方法需要创建对象去调用,而抽象类又不能创建对象,所以要使用静态方法才能调用
abstract class Monster extends Animal{
    public static void showInfo(){
        System.out.println("This is a function of abstract class...");
    }
}
  1. 如果一个类继承了抽象类,那么这个类必须实现抽象类中的所有抽象方法,否则这个类也得声明成抽象类

注意:

abstract final class A{}	//false, abstract 是希望别人能继承,而final是不允许别人继承

abstract public static void test2();	//false,static一旦声明则方法无法重载,就违背了abstract的意愿

abstract private void test3();	//false.子类都没有办法看到这个方法,更别说重写了

7. 模板设计模式–抽象类最佳实践

image-20220331100033326

8. 接口

image-20220331101422067

package 接口;

public interface USB {  //接口不需要将成员函数声明成抽象方法 或 类是个抽象类,接口默认方法都是抽象方法,JDK8以后,接口中可以有静态方法、默认方法(需要default关键字) 
    void start();				//省略public
    void end();
    default void show(){		//省略public,用default关键字才可以
        System.out.println("This is a default function of interface");
    }
    static void show2(){		//静态方法
        System.out.println("This is a static function of interface");
    }
}

image-20220331102741095

image-20220331102829426

//USB -- interface file
package 接口;

public interface USB {  //接口不需要将成员函数声明成抽象方法 或 类是个抽象类,接口默认方法都是抽象方法
    String name = "USB";    //实际上是 public static final String name, 所以必须进行初始化
    int testNum = 1;        //实际上是 public static int testNum, 要初始化


    void start();
    void end();
    default void show(){
        System.out.println("This is "+  name + "'s default function");
    }
    static void show2(){
        System.out.println("This is "+  name + "'s static function");
    }
}

//USB2 -- interface file
package 接口;

public interface USB2 {
    void start();
    String name = "USB2";
    default void show(){
        System.out.println("This is " + name + "'s default function of USB2");
    }
}

/*
*	USB 和 USB2 中有同名默认函数 show();
*	那么同时实现两个接口的类需要怎么调用呢? 如下
*	需要在类内重写该方法,然后使用 接口名.super.方法名
*
*	USB 和 USB2 中也有同名的接口方法
*	由于是同名的故最终都是实现同一个接口,不用按接口名进行区分
*/
package 接口;

public class Computer implements USB,USB2 {
    @Override
    public void start() {
        System.out.println("This is a computer is starting");
    }

    @Override
    public void end() {
        System.out.println("This is a computer is closing");
    }

    @Override
    public void show() {
        USB.super.show();       //调用USB的show()
        USB2.super.show();      //调用USB2 的show()
    }
}


//Main file
package 接口;

public class Main {
    public static void main(String[] args) {
        Computer a = new Computer();
        a.start();
        a.end();
        a.show();

        Phone p = new Phone();
        p.start();
        p.end();
        p.show();

        USB.show2();

//        USB.name = "123";         //无法赋值给final
        System.out.println(name);   //证明name是static ,可以通过接口名 直接访问
        System.out.println(Computer.testNum);   //可以通过实现接口的类去访问接口中的(静态)属性
        //但是如果某类实现了多个接口,且这几个接口中有同名属性,建议直接使用--> 接口名.属性名
//        System.out.println(Computer.name);  //false,指向不明确
        System.out.println(USB2.name);  //true

    }
}

运行结果:
E:\JavaJDK\bin\java.exe "-javaagent:E:\JavaIDE\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=60455:E:\JavaIDE\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\WorkSpace\JAVA\Java学习第二阶段\out\production\Java学习第二阶段 接口.Main
    
This is a computer is starting
This is a computer is closing
This is USB's default function
This is USB2's default function of USB2
This is a phone is starting
This is a phone is closing
This is USB2's default function of USB2
This is USB's static function
USB
1
USB2

进程已结束,退出代码0

实现接口是对Java单继承机制的补充

8.1 接口的多态

//iMac.java
package 接口多态;

public class iMac implements USB3 {
    @Override
    public void start() {
        System.out.println("iMac is starting...");
    }

    @Override
    public void end() {
        System.out.println("iMac is ending...");
    }
}

//iPhone.java
package 接口多态;

public class iPhone implements USB3{
    @Override
    public void start() {
        System.out.println("iPhone is starting...");
    }

    @Override
    public void end() {
        System.out.println("iPhone is ending...");
    }
}

//USB3 --> interface
package 接口多态;

public interface USB3 {
    void start();
    void end();
}

//Sys.java
package 接口多态;

public class Sys {
    public void work(USB3 interfaces){		//只要实现该接口的对象就可以被调用,看下面的main
        interfaces.start();
        System.out.println("working...");
        interfaces.end();
    }
}

//main
package 接口多态;

public class Main2 {
    public static void main(String[] args) {
        iMac iMac = new iMac();		//iMac 已经实现了相关接口
        iPhone iPhone = new iPhone();	//iPhone 也实现了相关接口

        Sys system = new Sys();
        system.work(iMac);
        system.work(iPhone);
    }
}

//output

iMac is starting...
working...
iMac is ending...
iPhone is starting...
working...
iPhone is ending...

进程已结束,退出代码0

8.2 接口多态数组

//main
package 接口多态;

public class Main2 {
    public static void main(String[] args) {
        iMac iMac1 = new iMac(11);
        iPhone iPhone1 = new iPhone(12);
        iMac iMac2 = new iMac(13);

        USB3[] usb3Arr = new USB3[3];
        usb3Arr[0] = iMac1;
        usb3Arr[1] = iPhone1;
        usb3Arr[2] = iMac2;

        Sys system = new Sys();
        system.travel(usb3Arr);
    }
}

//Sys.java
package 接口多态;

public class Sys {
    public void work(USB3 interfaces){
        interfaces.start();
        System.out.println("working...");
        interfaces.end();
    }

    public void travel(USB3[] usbArr){
        for (USB3 temp : usbArr){
            if(temp instanceof iPhone){
                ((iPhone) temp).call();
            }
            work(temp);
        }
    }
}

//iPhone.java
package 接口多态;

public class iPhone implements USB3{
    private int id;

    public iPhone(int id) {
        this.id = id;
    }

    @Override
    public void start() {
        System.out.println(this.id + " iPhone is starting...");
    }

    @Override
    public void end() {
        System.out.println(this.id + " iPhone is ending...");
    }

    public void call(){
        System.out.println(this.id+" This is iphone calling...");
    }

}


//iMac.java
package 接口多态;

public class iMac implements USB3 {
    private int id;

    public iMac(int id) {
        this.id = id;
    }

    @Override
    public void start() {
        System.out.println(this.id + " iMac is starting...");
    }

    @Override
    public void end() {
        System.out.println(this.id + " iMac is ending...");
    }

}

8.3 接口的多态传递

package 接口的多态传递;

public class interfacePass {
    public static void main(String[] args) {
        No_2 point = new Man();     //true
        No_2 point2 = new Man();    //true,经过了传递性
        
        No_3 point3 = new Man2();   //true,既然继承了,就得把父类接口的函数实现了
        
    }
}

interface No_1{}
interface No_2 extends No_1{}
class Man implements No_2{}

//----------------------------------------------------------------------------

interface No_3{
    public void show();
}
interface No_4 extends No_3{
    
}
class Man2 implements No_4{
    @Override
    public void show() {
        System.out.println("This is a function of interface:No_3");
    }
}

注意

package 接口的多态传递;

public class home_test {
    public static void main(String[] args) {
        C c = new C();
        c.show();
    }
}

interface A{
    int x = 1;
}
class B{
    public int x = 0;
}
class C extends B implements A{
    public void show(){
        System.out.println(super.x);        //若继承和接口实现中有属性名重复,则要明确指明使用的是哪个
        System.out.println(A.x);
    }
}

9. 内部类

9.0 外部类、外部其他类、内部类

class Out0{		-------外部其他类
    
}

class Out1{		-------外部其他类
    
}

class Out2{		-------外部类
    {
        class inner0{	-------局部内部类,方法块中(不能加访问修饰符)
            
        }
    }
    
    public void func(){
        class inner1{	-------	局部内部类:方法中(不能加访问修饰符)
            
        }
    }
    
    private(public / protected) class inner2{	------成员内部类
        
    }
    
    private(public / protected) static class inner3{	------静态内部类
        
    }
}

image-20220331144132546

image-20220331144402070

9.1 局部内部类

方法内 代码块内

package 内部类;

public class OuterMain2 {
    public static void main(String[] args) {
        //这里也检测了代码执行顺序:
        //1、static codeblock (静态的东西)
        //2、普通代码块
        //3、普通成员函数
        Outer outer = new Outer(9);
        outer.func();
    }
}

class Outer{
    private final int num;

    public Outer(int x){
        num = x;            //final 的成员变量可以在 构造器中初始化
        System.out.println("This is constructor !");
    }

    private void Prints(){
        System.out.println("This is a function of Outer");
    }
    static {
        System.out.println("This is a static codeblock");
    }
    {
        class Inner2{
            private final int num =  999;        //与外部类成员重名,访问按照就近原则
            public void func2(){
                System.out.println("This is a inner function of Inner in the codeblock");
                System.out.println("num = "+ num);                      //output 999,就近的是 999
                System.out.println("Outer's num = "+Outer.this.num);    //output 0, 要通过类名.this.属性名 去访问,但由代码块比构造函数先执行,所以外部类的num还没有被初始化
                Prints();
            }
        }
        new Inner2().func2();
    }
    public void func(){
        //1、局部内部类是定义在外部类的局部位置,通常在方法
        //2、可以直接访问外部类的所有成员(包括private)
        //3、不能添加访问修饰符,但是可以使用final修饰
        //4、作用域:仅仅在定义它的方法或代码块中
        //5、外部类可以通过创建对象访问内部类及其属性和方法
        //6、内部类有属性和外部类属性重名,则按照就近原则访问,若要访问外部同名属性--->外部类名.this.属性
        class Inner{            //可以访问外部类的私有成员
            public void func1(){
                System.out.println("Outer's num is "+ num);
            }
        }
        Inner inner = new Inner();
        inner.func1();
    }
}

9.2 匿名内部类

image-20220401094850874

image-20220401110434616

image-20220401151148213

9.2.1 基于接口的匿名内部类
package 内部类;

public class AnonymousClass {
    public static void main(String[] args) {
        /*
         *传统方式:创建一个对象,然后调用方法
         *有时候只是想调用一次对象 or 接口 内的函数,创建一个类去调用太麻烦,故选择匿名内部类
         */

        //1、匿名内部类 我们看不见名字,但系统会自动分配名字
        //2、编译类型:A    运行类型:getClass()--> class 内部类.AnonymousClass$1
        //3、new 一个匿名内部类:JDK 底层创建AnonymousClass$1 并立即创建了一个对象实例,并把地址返回给了man
        
        A man = new A() {
            @Override
            public void call() {
                System.out.println("This is a anonymous class's function");
            }
        };
        man.call();
        System.out.println(man.getClass()); //output class 内部类.AnonymousClass$1

    }
}

interface A {
    public void call();
}
9.2.2 基于类的匿名内部类

普通类 和 抽象类

package 内部类;

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

        int a = 99;

        Father son = new Father("仁"){      //匿名内部类 继承父类,这里 “仁 ”会传给Father的构造器
            public final int anony = 999;         //外部其他类 无法访问
            @Override
            public String getName() {
                System.out.println("a is "+a);          //可以直接访问类外成员
                System.out.println("anony is "+anony);
                return super.getName(); //从这里super 就可以发现是继承关系
            }
        };
        System.out.println(son.getName());

        class Test{
            public void func(){
                String test = son.getName();
//                System.out.println(anony);  //外部其他类无法访问 匿名内部类的属性
            }
        }


        //基于抽象类的匿名内部类
        //匿名内部类的创建又像定义,又像创建 ,所以可以用另一种调用方法
        new Father2() {
            @Override
            public void Print() {
                System.out.println("This is a anonymous class");
            }

            @Override
            public void show() {
                System.out.println("This is anonymous");
            }
        }.show();       //这里也有动态绑定,调用的是匿名内部类中的show()

    }
}
class Father{
    public Father(String name){
        this.name = name;
    }
    private String name;
    public String getName(){
        return name;
    }
}

abstract class Father2{
    public abstract void Print();
    public void show(){
        System.out.println("This is Father2");
    }
}
9.2.3 匿名内部类的实践
  1. 匿名内部类当实参 传入

    package 内部类;
    
    public class AnonymousClass3 {
        public static void main(String[] args) {
            Show(new Usb() {            //用匿名内部类 当作函数实参 传入进去
                @Override
                public void Print() {
                    System.out.println("This is a anonymousClass function");
                }
            });
        }
    
        public static void Show(Usb usb){
            usb.Print();
        }
    }
    
    interface Usb{
        void Print();
    }
    

9.3 成员内部类

  • 与局部内部类 不同的是,成员内部类 是定义在外部类的成员位置上
  • 成员内部类可以加访问修饰符,而局部内部类不可以
  • 成员内部类可以随便访问外部类的任意属性(包括private)
  • 外部类也可以随意访问成员内部类的任意属性(包括private)
package 内部类;

public class MemberClass {
    public static void main(String[] args) {
        Big big = new Big();
        big.func();
    }
}

class Big{
    private int a = 99;

    private class Small{        //成员内部类,可以加访问修饰符,而局部内部类不可以
        private int a;
        public void show(){
            System.out.println("MemberClass->Small is "+ a);   //就近原则
            System.out.println("OuterClass-->Big is "+Big.this.a);  //当然如果不重名,则可以直接使用a
        }
    }

    //通过成员方法去使用成员内部类
    public void func(){
        Small small = new Small();
        small.show();
    }

}

    //通过成员方法去使用成员内部类
    public void func(){
        Small small = new Small();
        small.show();
    }

}

外部其他类 访问成员内部类的方法

package 内部类;

public class MemberClass2 {
    public static void main(String[] args) {
        A1 a1 = new A1();

        //法一:外部其他类调用成员内部类:通过外部类A1对象再new一个成员内部类
        A1.A1Inner smalla1 = a1.new A1Inner();
        smalla1.func();

        //也可以合在一起写
        A1.A1Inner smalla111 = new A1().new A1Inner();

        //法二:通过外部类定义一个方法去返回成员内部类实例
        A1.A1Inner smalla11 = a1.getA1Inner();
        smalla1.func();


    }
}
class A1{
    private final int a = 999;
    public class A1Inner{
        public void func(){
            System.out.println("A1's a = "+ a);
        }
    }
    public A1Inner getA1Inner(){
        return new A1Inner();
    }
}

9.4 静态内部类

定义在外部类的成员位置,但是有static修饰

package 内部类;

public class StaticClass {
    public static void main(String[] args) {
        //可以直接创建静态成员内部类,而不用通过外部类再new成员内部类--Out.Inner inner = new Out().new Inner()-->false
        //这里输出 This is Inner Constructor ! 并没有调用Out的构造器
        //这里也算是外部其他类访问静态内部类
        Out.Inner inner = new Out.Inner();	
    }
}

class Out {
    Out(){
        System.out.println("This is Out Constructor !");
    }
    private int no_static_num = 9;
    private static int static_num = 99;
    public static class Inner{      //静态成员内部类中的方法均为 静态方法
        private int static_num = 9999;
        Inner(){
            System.out.println("This is Inner Constructor !");
        }
        public void func(){
//            System.out.println(no_static_num);      //静态成员内部类不能访问非静态成员属性
            System.out.println(static_num);            //就近原则 output 9999
            System.out.println(Out.static_num);         //外部类的同名属性 output 99
        }
    }
    
    //外部类属性、成员函数访问静态内部类-->则通过对象访问
    public void func2(){
        Inner in = new Inner();
        in.func();
    }
}

外部其他类访问静态内部类

//    外部其他类访问静态内部类
//    方法一:
      Out.Inner 对象名 = new Out.Inner();
      对象名.属性

//    方法二:
//    通过外部类方法返回一个静态内部类
      public Inner getInner(){
          return new Inner();
      }

10. 枚举

自定义枚举类–引出真正的枚举

package 枚举;

public class BasicEnum {
    public static void main(String[] args) {
        WEEKEND day = WEEKEND.MONDAY;
        System.out.println(day.getName());
    }
}

//自定义枚举类
class WEEKEND{
    //初始化对象,使用public 为了供外部使用
    //enum 的本质其实就是 这样
    public final static WEEKEND MONDAY = new WEEKEND("周六");
    public final static WEEKEND TUESDAY = new WEEKEND("周日");


    private String name;

    //使用private 防止任意new
    private WEEKEND(String name) {
        this.name = name;
    }

    //可以提供get方法,但是不用set方法,因为枚举 一般只读
    public String getName() {
        return name;
    }
}

真正的枚举enum

package 枚举;

public class Enum {
    public static void main(String[] args) {
        System.out.println(Season.SPRING);

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


        Season[] arr = Season.values();        //values() 返回该枚举的所有枚举对象
        for(Season it : arr){
            System.out.println(it);
            System.out.println(it.name());     //name()  返回当前枚举的名字
            System.out.println(it.ordinal());   //ordinal() 返回当前枚举 在整个枚举类内的编号
        }

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

        Season temp = Season.valueOf("SUMMER");    //valueOf() 把字符串转换成枚举,前提是字符串必须是已有的枚举对象,否则会报错
        System.out.println(temp);

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

        Season it = Season.SPRING;
        Season it2 = Season.WINTER;
        System.out.println(it.compareTo(it2));  //compareTo()  返回 it的编号 - it2 的编号,这里是 0 - 3 = -3
    }
}
enum Season{
    //使用enum来实现枚举,要求将定义常量对象写在第一行
    SPRING("春天","温暖"),  //编号 0
    SUMMER("夏天","炎热"),  //编号 1
    AUTUMN("秋天","凉爽"),  //编号 2
    WINTER("冬天","寒冷");  //编号 3

    private String name;
    private String desc;

    private Season(String name,String desc){
        this.name = name;
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Season {" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}
// enum 本质是继承了 java.lang.Enum 所以enum不能继承其他类了(Java是单继承机制),但是可以去实现接口

11. 注解

@interface -----> 注解 @Target 是注解的注解,称 元注解

这些注解 可以看源码, 从@Target中看 这些注解的适用范围:类、方法、属性…

  1. Override

    重写方法

  2. Deprecated

    表示某个方法 、 类 已经过时了,不推荐使用,但是还是能用

  3. SuppressWarning

​ 抑制警告,使某些警告不被提示出来

元注解(了解)

Retention:指定注解的作用范围,三种:SOURCE、CLASS、RUNTIME

image-20220402095040353

image-20220402095158083

Target:指定注解可以在哪些地方使用

Documented:指定该注解是否会在javadoc体现

Inherited:子类会继承父类注解

12. 异常

//如果认为可能会出现异常/问题,可以使用try-catch异常处理机制来解决,从而保证程序的健壮性
//IDEA 快捷键:选中代码块--> ctrl + alt + t --> 选择try-catch
//如果程序进行了异常处理,那么即使出现了异常,程序也可以继续执行
package 异常;

public class Exception01 {
    public static void main(String[] args) {
        int a = 0;
        int b = 1;
        int res;
        try {
            res = b / a;
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("程序继续...");
    }
}

//运行结果:
java.lang.ArithmeticException: / by zero
	at 异常.Exception01.main(Exception01.java:9)
程序继续...

12.1 异常体系

image-20220402103617353

image-20220402104349149

//虚线:实现接口
//实线:继承

image-20220402104446498

12.1.1 编译异常和运行异常

image-20220403165440373

12.2 常见的运行时异常

image-20220402104700453

// ClassCastException 异常
package 异常;

public class Exception02 {
    public static void main(String[] args) {
        A b = new B();
        B castB = (B)b; //true  (向下转型)
        C castC = (C)b; //false (类转换异常)
    }
}
class A{}
class B extends A{}
class C extends A{}


// NumberFormatException 异常
// 欲将字符串转换成某数值类型,但是该字符串不能转成相应格式->抛出该异常
package 异常;

public class Exception02 {
    public static void main(String[] args) {
        String str = "1234";
        int num = Integer.parseInt(str);
        System.out.println(num);    //true,output 1234

        String str2 = "你好吗";
        int num2 = Integer.parseInt(str2);
        System.out.println(num2);   //NumberFormatException
    }
}

12.3 常见的编译异常

image-20220402110128100

12.4 异常处理的方式

image-20220402110313568

若没有显式使用try-catch-finally 就默认throws

//try-catch-finally
//catch 和 finally 二者至少有一个
try{
    代码(可能有异常)
}catch(Exception e){
    //捕获到异常
    //1.当异常发生时,系统将异常封装成Exception对象e,传递给catch
    //2.得到异常对象后,程序员自己处理
    //3.注意:如果没有发生异常,catch代码块不执行
}finally{
    //不管try代码块是否有异常发生,始终要执行finally
    //重要资源的释放 或者其他。如:关闭文件
}

// throws

image-20220402111419705

//try-catch-finally 和 throws 只能选择一种,throws 就是往上层抛异常,让上层解决,上层可以继续往上抛,也可以自己try-catch-finally解决,JVM是最高层-->JVM处理异常:输出异常信息,中断程序
12.4.1 try-catch 处理异常
package 异常;

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

        //1、如果try中可能有多个可能的异常,则可以使用多个catch去捕获异常,对应处理,但是捕捉到第一个异常后就不会继续再捕捉了
        //2、子类异常 要写在父类异常之前
        try {
            Person man = null;
            int n1 = 0;
            int n2 = 1;
            int res = n2 / n1;
            System.out.println(man.getName());
        } catch (ArithmeticException e) {
            System.out.println("数学计算异常" + e.getMessage());
        } catch (NullPointerException e) {
            System.out.println("空指针异常" + e.getMessage());
        } catch(Exception e){
            System.out.println("这是父类异常Exception :"+ e.getMessage());
        }

        try {
            int a1 = 0;
            int a2 = 2;
            int res2 = a2 / a1;
        } finally {
            System.out.println("没有catch捕获异常的话,遇到异常直接崩掉");
        }
        System.out.println("try-finally 遇到异常不会执行这步");

    }
}

class Person {
    private String name;

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

    public String getName() {
        return name;
    }
}
12.4.2 注意区分数组越界异常 和 空指针异常

空指针异常:你这个是指向为null的指针,你还要操作它就是空指针异常;

数组越界异常:你要访问的数组下标已经超过数组长度了

package 异常;

public class ExceptionWork02 {
    public static void main(String[] args) {
        System.out.println(method());   //output 4
    }

    public static int method(){
        int i = 1;
        try {
            i++;    //i = 2
            String[] names = new String[3];
            if(names[1].equals("Tom")){
                System.out.println(names[1]);
            }else {
                names[3] = "Test";
            }
            return 1;
        }
        catch(ArrayIndexOutOfBoundsException e){
            return 2;
        }
        catch (NullPointerException e) {
            return ++i;     //i = 3
        } finally {
            return ++i;     //i = 4
        }
    }
}
12.4.3 实际应用(检测输入)
package 异常;

import java.util.Scanner;

public class ExceptionWork03 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入选项:");
        while (true){
            String str = scanner.next();
            int op;
            try {
                op = Integer.parseInt(str);
            } catch (NumberFormatException e) {
                System.out.print("输入错误,请重新输入:");
                continue;
            }
            switch (op){
                case 1:
                    System.out.println("Option 1");
                    break;
                case 2:
                    System.out.println("Option 2");
                    break;
                default:
                    System.out.println("Option selected is null");
                    break;
            }
            return;
        }
    }
}

//执行,这里连小数也可以检测出来

请输入选项:n
输入错误,请重新输入:b
输入错误,请重新输入:2
Option 2

进程已结束,退出代码0

12.4.4 throws 处理异常

image-20220403112256726

package 异常;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

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

    }


    //1、使用throws 抛出异常让调用func()的调用者(方法)接收并处理该异常
    //2、throws后面的异常类型可以是方法中产生异常的类型,也可以是它的父类
    //3、throws 关键字后也可以是异常列表,即可以出现多个异常
    public void func1() throws FileNotFoundException{
        System.out.println("抛出异常");
    }
    
    public void func2() throws Exception{
        System.out.println("该异常是FileNotFoundException的父类");
    }

    public void func3() throws FileNotFoundException,NullPointerException,ArrayIndexOutOfBoundsException{
        System.out.println("可以抛出多个异常");
    }
}
package 异常;

public class Throws02 {
    public static void main(String[] args) {
        Father father = new Father();
        Son son = new Son();
        father.method();
        son.method();
    }
}
class Father{
    public void method() throws NumberFormatException{
        String str = "111";
        System.out.println(Integer.parseInt(str));
    }
}
class Son extends Father{
    //子类重写父类的方法是,抛出的异常要么和父类相同,要么是父类抛出的类型的子类
    @Override
    public void method() throws Exception{  //这里Exception是NumberFormatException 的父类
        
    }
}
package 异常;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

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

    }
    public static void func1(){
        //这里 func2()抛出一个 编译异常,而func1()没有处理机制,故编译会报错
        // 解决方法--> try-catch or throws
        func2();
    }
    public static void func2() throws FileNotFoundException{
        FileInputStream fis = new FileInputStream("D://test.txt");
    }
    //-----------------------------------------------------------------------

    public static void func3(){
        //这里 func4()抛出的异常为运行异常,故在编译时不会像上面一样报错
        //程序员不需要显示处理运行异常,运行异常有默认处理机制
        func4();
    }
    public static void func4() throws ArithmeticException{

    }
}
12.4.5 自定义异常

image-20220403170106838

//一般地,我们将自定义异常 继承 运行异常 RuntimeException,因为可以使用默认的处理机制,如果继承 Exception 则有可能要处理编译异常,较为麻烦

package 异常;

import java.util.Scanner;

public class customException_ {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入年龄:");
        int age = scanner.nextInt();
        if (!(age >= 0 && age <= 120)) {
            throw new CustomException("年龄不在0-120范围内");
        }
        System.out.println("age is " + age);
    }
}

class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}
12.4.5.1 throw 和 throws 的对比
image-20220403171401808

测试:

finally 要先执行 不要忘记

image-20220403171531515

作业:

package 异常;

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

        try {
            if (args.length != 2) {
                throw new ArrayIndexOutOfBoundsException("输入参数个数不对");
            }
            int a = Integer.parseInt(args[0]);
            int b = Integer.parseInt(args[1]);
            int res = cal(a, b);
            System.out.println("result is " + res);

        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());
        } catch (NumberFormatException e) {
            System.out.println("格式参数不对");
        } catch (ArithmeticException e) {
            System.out.println("出现除以 0 ");
        }

    }

    public static int cal(int a, int b) {
        return a / b;
    }
}

13. 常用类

13.0 装箱、拆箱

装箱:基本类型–> 包装类型

拆箱:包装类型–>基本类型

JDK 5 以后实现了自动装箱和拆箱的方式

package 常用类;

public class Wrapper01 {
    public static void main(String[] args) {
        //演示int --> Integer 装箱和拆箱
        //JDK 5 以前:手动装箱 和 拆箱

        //手动装箱
        int n1 = 100;
        Integer integer = new Integer(n1);
        Integer integer1 = Integer.valueOf(n1);

        //JDK5以后,自动装箱 和 拆箱
        //Integer.valueOf(int i) 返回一个值为i 的Integer类型
        //同理,如果是Double.valueOf(double i) 则返回一个值为i的 Double类型
        int n2 = 200;
        Integer integer2 = n2;   //装箱,调用的仍然是valueOf()
        int n3 = integer;       //拆箱
    }
}

13.1 包装类 wrapper

image-20220403175032285

操作方法:

选中一个类名,右键

如:

image-20220403190726392

在继承图中 点击空格键,即可进行类的搜索和添加

[外链图片转存中…(img-B63og9Cx-1649684466510)]

小测试:

package 常用类;

public class Homework01 {
    public static void main(String[] args) {
        //三目运算符要看做一个整体
        //里面有Double 精度更高,所以先转换成高精度
        //所以输出了1.0
        Object obj1 = true?new Integer(1):new Double(2.0);
        System.out.println(obj1);   //output 1.0

        Object obj2;
        if (true){
            obj2 = new Integer(1);
        }else {
            obj2 = new Double(2.0);
        }
        System.out.println(obj2);   //output 1
    }
}
13.1.1 包装类型和String类型的转换
package 常用类;

public class wrapperToString {
    public static void main(String[] args) {
        //包装类-->String
        Integer i = 100;    //自动装箱
        //方式1
        String str1 = i + "";
        //方式2
        String str2 = i.toString();
        //方式3
        String str3 = String.valueOf(i);

        //String --> 包装类(Integer)
        String str4 = "1234";
        //方式1
        Integer i2 = Integer.parseInt(str4);    //观察parseInt发现返回的是int类型,所以这里也用了自动装箱
        //方式2
        Integer i3 = new Integer(str4);     //Deprecated
         //方式3
        Integer i4 = Integer.valueOf(str4);
    }
}
13.1.2 常用方法

[外链图片转存中…(img-a7OfCzfb-1649684466510)]

其余方法可以通过继承的图看

Integer面试题

[外链图片转存中…(img-xH1XZI3o-1649684466510)]

注意对源码的阅读

[外链图片转存中…(img-dxtUoGSJ-1649684466510)]

[外链图片转存中…(img-MzMaqQeA-1649684466511)]

//只要有基本数据类型,那么和包装类比较就是值比较

13.2 String 类

[外链图片转存中…(img-buc5XVXt-1649684466511)]

//查看String的源码 和 继承关系图可以知道 关于String的构造器 和 本质等等
//自己不熟悉的地方
//1.String 是类,真正存储数据时用 private final byte【】 value存储字符(源码),而final代表的是地址不能修改,而不是值不能修改

package 常用类;

import java.util.Arrays;

public class String01 {
    public static void main(String[] args) {
        final byte[] arr = {'a','b','c'};
        byte[] temp = {'1','2','3'};
//        arr = temp;   	//false
        arr[0] = temp[0];	//值可以改变
        System.out.println(Arrays.toString(arr));
    }
}

//输出结果
[49, 98, 99]  //这是ASCII码 代表:1 b c
13.2.1 String对象的创建方式
//方式1:直接
String s = "hello";
//方式2:构造器
String s = new String("hello"); 

二者有区别!!!

[外链图片转存中…(img-4LMBrfgI-1649684466511)]

[外链图片转存中…(img-23uP6DZK-1649684466512)]

//根据上面的图就可以清晰的明白下面的TF
package 常用类.String类;

import javax.sound.midi.Soundbank;

public class String02 {
    public static void main(String[] args) {
        String str = "abc";
        String str2 = "abc";
        System.out.println(str == str2);    //T

        String str3 = new String("abc");
        System.out.println(str == str3);    //F

        String str4 = new String("abc");
        System.out.println(str4 == str3);   //F

        System.out.println(str == str4);    //F

        System.out.println(str == str3.intern());               //T

        System.out.println(str4.intern() == str3.intern());     //T
    }
}

面试题:

[外链图片转存中…(img-UsABjmnI-1649684466512)]

[外链图片转存中…(img-XsaRsgl2-1649684466512)]

[外链图片转存中…(img-bvkfPSqF-1649684466513)]

[外链图片转存中…(img-i0xiDNuG-1649684466513)]

String类的说明

[外链图片转存中…(img-FIwBqeHZ-1649684466514)]

13.2.2 常用方法

[外链图片转存中…(img-WYPIusVn-1649684466514)]

[外链图片转存中…(img-GiDTLf1c-1649684466514)]

package 常用类.String类;

public class String03 {
    public static void main(String[] args) {
        //如果有特殊字符 需要转义符‘/’
        String s1 = new String("E:\\WorkSpace\\JAVA\\Java学习第三阶段");
        String[] split = s1.split("\\\\");      //必须有转义字符才能区分\\
        for (String str:split){
            System.out.println(str);
        }

        //String 转成 char[]
        char[] arr = s1.toCharArray();
        for (char c : arr) {
            System.out.print(c + " ");
        }

        //格式化输出
        Person p = new Person();
        p.name = "小王线性在";
        p.age = 22;
        p.weight = 80.357;
        p.height = 190.262;

        //- 左对齐
        //%.2f 会进行四舍五入
        String tem = String.format("\n学生姓名:%-12s年龄:%-4d体重:%.2f身高:%.1f",p.name,p.age,p.weight,p.height);
        System.out.println(tem);
        //也可以
        String formatStr = "\n学生姓名:%-12s年龄:%-4d体重:%.2f身高:%.1f";
        tem = String.format(formatStr,p.name,p.age,p.weight,p.height);
        System.out.println(tem);
    }
}

class Person{
    public String name;
    public int age;
    public double weight;
    public double height;
}

13.3 StringBuffer 类

[外链图片转存中…(img-JkoxSFrk-1649684466515)]

//根据 StringBuffer 源码可以知道,它继承了AbstractStringBuilder类--该类使用byte【】 存储数据,使用StringBuffer是用的byte【】存储数据

//String vs. StringBuffer
//String 保存的是字节常量final byte[],里面的值不能修改,每次String类的更新,实际上是更改地址,效率较低
 
//StringBuffer 保存的是字节变量 byte[],里面的值可以修改,不用更新地址,效率高,只有在容量不够时才会更新

基础使用

package 常用类.StringBuffer类;

public class StringBuffer01 {
    public static void main(String[] args) {
        //StringBuffer 构造器
        StringBuffer sb1 = new StringBuffer();
        StringBuffer sb2 = new StringBuffer(100);
        //....

        //String 转 StringBuffer
        //1
        String str = "hello";
        StringBuffer sb3 = new StringBuffer(str);
        System.out.println(sb2);
        //2
        StringBuffer sb4 = new StringBuffer();
        sb4 = sb4.append(str);
        System.out.println(sb4);

        //------------------------------

        //StringBuffer 转 String
        //1.StringBuffer的toString()
        String To_str = new StringBuffer("hello").toString();

        //2.用构造器
        String To_str2 = new String(new StringBuffer("hello"));

    }
}
13.3.1 常用方法

[外链图片转存中…(img-P3nn5uPI-1649684466515)]

package 常用类.StringBuffer类;

public class StringBuffer02 {
    public static void main(String[] args) {
        StringBuffer b = new StringBuffer("你好");
        b.append(",李焕英").append(" 这部电影咋呀").append("?");
        System.out.println(b);//你好,李焕英 这部电影咋呀?


        /*
         *delete 和 replace 的区间都是 左闭右开
         */
        b.delete(2,4);
        System.out.println(b);//你好焕英 这部电影咋呀?

        b.replace(0,4,"无双");
        System.out.println(b);//无双 这部电影咋呀?


        int index = b.indexOf("这部");
        System.out.println(index);  //3

        //在索引为2的位置, 插入“三国 ”
        b.insert(2,"三国");
        System.out.println(b);//无双三国 这部电影咋呀?

        System.out.println(b.length());//12
    }
}

13.4 StringBuilder 类

image-20220404145544282

//StringBuilder 的用法和 StringBuffer 差不多,只是在单线程中用StringBuilder更好,多线程中用StringBuffer
//StringBuilder 的方法没有做互斥的处理,即没有synchronized关键字

13.5 String\StringBuffer\StringBuilder 比较

image-20220404145544282

效率:StringBuilder > StringBuffer > String

image-20220404150616729

13.6 Math 类

13.6.1 常用方法

image-20220404151142075

package 常用类.Math类;

public class Math01 {
    public static void main(String[] args) {
        System.out.printf("%d\n%n",(int)Math.pow(2,2)); //4

        //向上去整
        System.out.println(Math.ceil(3.6)); //4.0
        System.out.println(Math.ceil(3.4)); //4.0
        System.out.println(Math.ceil(-3.4)); //-3.0

        //向下取整
        System.out.println(Math.floor(3.6));    //3.0
        System.out.println(Math.floor(3.4));    //3.0
        System.out.println(Math.floor(-3.4));    //-4.0

        //四舍五入
        System.out.println(Math.round(3.6));    //4
        System.out.println(Math.round(3.4));    //3
        System.out.println(Math.round(-3.4));    //-3

        //开方
        System.out.println(Math.sqrt(64));  //8.0

        //Math.random()返回 【0,1)的随机小数
        System.out.println(Math.random());

        //返回一个 【2,7】的随机整数 ---->[a,b]
        //(int)a <= x <= (int)(a + (b-a+1) * Math.random() )
        System.out.println((int)(2 + (7-2+1)*Math.random()));

        System.out.println(Math.min(1,2));  //1
        System.out.println(Math.max(1,2));  //2
    }
}

13.7 Arrays 类

13.7.1 常用方法

Arrays.toString()

Arrays.sort()

Arrays.binarySearch()

Arrays.copyOf()

Arrays.fill()

Arrays.equals()

Arrays.asList()

package 常用类.Array类;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;

public class Array01 {
    public static void main(String[] args) {
        //用 Arrays.toString() 能更方便输出
        Integer[] arr = {1,2,4,3,0};
        System.out.println(arr);                     //[Ljava.lang.Integer;@3ac3fd8b
        System.out.println(Arrays.toString(arr));    //[1, 2, 4, 3, 0]

        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));    //[0, 1, 2, 3, 4]

        //匿名内部类 + 泛型
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(Arrays.toString(arr));    //[4, 3, 2, 1, 0]

        //也可以不用泛型
        Arrays.sort(arr,new Comparator() {
            @Override
            public int compare(Object o1,Object o2){
                Integer i1 = (Integer) o1;
                Integer i2 = (Integer) o2;
                return i2 - i1;
            }
        });
        System.out.println(Arrays.toString(arr));    //[4, 3, 2, 1, 0]
    }

}

自定义排序方式的原理:

package 常用类.Array类;

import java.util.Arrays;
import java.util.Comparator;

public class Arrays02 {
    public static void main(String[] args) {
        int[] arr ={9,3,2,4,1};

// myBubble()等于如下
//      Arrays.sort(arr,new Comparator<Integer>() {
//          @Override
//          public int compare(Integer o1,Integer o2){
//              return o2 - o1;
//          }
//      }
        myBubble(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                int i1 = (Integer) o1;
                int i2 = (Integer) o2;
                return i1 - i2;
            }
        });
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 该函数模拟了Arrays.sort(T[] ,Comparator<T> </T>)的实质, 这里使用了冒泡排序
     * @param arr 传入的数组
     * @param c 比较器
     */
    public static void myBubble(int[] arr, Comparator c){
        int temp;
        for (int i = 0;i<arr.length - 1;i++){
            for (int j = 0; j <arr.length - i - 1; j++){
                if(c.compare(arr[j],arr[j+1])>0){
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

binarySearch()

//必须有序才能排列
//存在返回下标,不存在返回 -(low +1),low为所查找的数应该在的位置

package 常用类.Array类;

import java.util.Arrays;

public class Arrays03 {
    public static void main(String[] args) {
        int[] arr = {2,4,3,1,0};
        Integer[] arrs = new Integer[arr.length];
        int i = 0;
        for (int it :arr){
            arrs[i++] = it; //装箱
        }
        System.out.println(Arrays.toString(arrs));//[2, 4, 3, 1, 0]

        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));//[0, 1, 2, 3, 4]

        //Arrays.binarySearch()查找的数据如果不在,就返回 return -(low + 1);
        int index = Arrays.binarySearch(arr,3);
        System.out.println(index);

        index = Arrays.binarySearch(arr, 5);    //5应该在 4的后面,4的下标为4,所以low = 5--> 故 index = -(5 + 1) 即 -6
        System.out.println(index);// -6
    }
}

Arrays.copyOf()

package 常用类.Array类;

import java.util.Arrays;

public class Arrays04 {
    public static void main(String[] args) {
        Integer[] arr = {3,4,7,5,1};
        Integer[] array = Arrays.copyOf(arr,arr.length - 1);
        System.out.println(Arrays.toString(array));//[3, 4, 7, 5]
        System.out.println(Arrays.toString(arr));//[3, 4, 7, 5, 1]
    }
}

Arrays.fill()

Arrays.equals()

package 常用类.Array类;

import java.util.Arrays;
import java.util.List;

public class Arrays04 {
    public static void main(String[] args) {
        Integer[] arr = {3,4,7,5,1};
        Integer[] array = Arrays.copyOf(arr,arr.length - 1);
        System.out.println(Arrays.toString(array));//[3, 4, 7, 5]
        System.out.println(Arrays.toString(arr));//[3, 4, 7, 5, 1]

        Integer[] arr2 = new Integer[]{9,3,2};
        Integer[] arrTest = {9,3,2};
        Integer[] arr3 = new Integer[]{9,2,3};

        System.out.println(Arrays.hashCode(arr2));//38535
        System.out.println(Arrays.hashCode(arrTest));//38535
        System.out.println(Arrays.hashCode(arr3));//38505
        System.out.println(arr2 == arrTest);//false

        Arrays.fill(arr2,999);
        System.out.println(Arrays.toString(arr2));//[999, 999, 999]


        Integer[] arr4 = new Integer[]{999,999,999};
        System.out.println(Arrays.equals(arr2,arr4));  //true

        List list = Arrays.asList(1,3,4,6,2,0);
        System.out.println(list);   //[1, 3, 4, 6, 2, 0]
        System.out.println(list.getClass());//运行类型:class java.util.Arrays$ArrayList 是 Arrays 的一个内部类

    }
}

自定义数据类型,按照自己希望的方式排序

//Book为自定义数据类型,按照价格排序

package 常用类.Array类;

import java.util.Comparator;

public class Arrays05 {
    public static void main(String[] args) {
        Book[] books = new Book[4];
        books[0] = new Book("红楼梦~",100);
        books[1] = new Book("金瓶梅~",90);
        books[2] = new Book("青年文摘~",5);
        books[3] = new Book("java从入门到放弃~",300);

        MySort(books,new Comparator<Book>() {
            @Override
            public int compare(Book o1, Book o2) {
                return o1.getPrice() - o2.getPrice();
            }
        });


        for (Book it:books) {
            System.out.println(it);
        }

    }
    public static void MySort(Book[] books, Comparator<Book> comparator){
        Book temp;
        for (int i = 0; i < books.length - 1; i++) {
            for (int j = 0; j < books.length - i- 1; j++) {
                if(comparator.compare(books[j],books[j+1])>0){
                    temp = books[j];
                    books[j] = books[j+1];
                    books[j+1] = temp;
                }
            }
        }
    }

}
class Book{
    private String name;
    private int price;

    public Book() {
    }

    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

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

13.8 System 系统类

13.8.1 常用方法

image-20220404203558262

arraycopy() 相对 Arrays.copyOf()更底层

13.9 BigInteger 和 BigDecimal 类

//BigInteger 适合保存较大的整型
//BigDecimal 适合保存精度更高的浮点型
package 常用类.BigNumber;

import java.math.BigInteger;

public class BigNum01 {
    public static void main(String[] args) {
//        long num = 9999999999999999999999999999999; //false 整数过大
        BigInteger num = new BigInteger("9999999999999999999999999999999"); //要用引号

        //实现加减乘除
        //该类型不能直接使用运算符进行计算
        //加
        BigInteger a = num.add(new BigInteger("1"));
        System.out.println(a);//10000000000000000000000000000000

        //减
        BigInteger b = num.subtract(new BigInteger("1"));
        System.out.println(b);

        //乘
        BigInteger c = num.multiply(new BigInteger("2"));
        System.out.println(c);//19999999999999999999999999999998

        //除
        BigInteger d = num.divide(new BigInteger("2"));
        System.out.println(d);//4999999999999999999999999999999

    }
}
package 常用类.BigNumber;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;

import static java.math.BigDecimal.ROUND_DOWN;
import static java.math.BigDecimal.ROUND_UP;

public class BigNum01 {
    public static void main(String[] args) {
        BigDecimal num = new BigDecimal("9999999999999999999999999999999.123456789"); //要用引号

        //实现加减乘除
        //该类型不能直接使用运算符进行计算
        //加
        BigDecimal a = num.add(new BigDecimal("1"));
        System.out.println(a);//10000000000000000000000000000000.123456789

        //减
        BigDecimal b = num.subtract(new BigDecimal("1"));
        System.out.println(b);//9999999999999999999999999999998.123456789

        //乘
        BigDecimal c = num.multiply(new BigDecimal("2"));
        System.out.println(c);//19999999999999999999999999999998.246913578

        //除
        //除可能会出现异常,即除完的数是个无限小数
        //解决:在divide后加一个精度
        //CEILING 保留的精度 是 分子的精度,分子 = 9999999999999999999999999999999.123456789
        BigDecimal d = num.divide(new BigDecimal("2"),RoundingMode.CEILING);//后面的参数是舍入模式
        System.out.println(d);//4999999999999999999999999999999.561728395

    }
}

13.10 日期类

第一代:

image-20220406084456494

SimpleDateFormat() 内的格式

image-20220406090753800

package 常用类.日期类;

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

public class Data01 {
    public static void main(String[] args) throws ParseException {
        Date time = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年 MM月 dd日 E HH:mm:ss");
        System.out.println(simpleDateFormat.format(time));  //2022年 04月 06日 周三 09:15:16

        //把 字符串转换成 Date,但格式仍然是国外的形式,如果要格式化输出,则仍要用format()
        String time2 = "1999年 2月 26日 周四 10:09:28";//该字符串要按照 simpleDateFormat 的格式去写
        Date time3 = simpleDateFormat.parse(time2);
        System.out.println(time3);  //Fri Feb 26 10:09:28 CST 1999,即使你的星期数写错了,系统也会自动纠正,这一天应该是 周四
        System.out.println(simpleDateFormat.format(time3));//1999年 02月 26日 周五 10:09:28

        //获取当前时间到1970-01-01 00:00:00的毫秒数,注意时区不同也有影响,如CN 1970-01-01 08:00:00为参考时间
        System.out.println(time.getTime());//1649208503025
    }
}
第二代:

[外链图片转存中…(img-Cq4fBrqR-1649684466520)]

package 常用类.日期类;

import java.util.Calendar;

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

        Calendar calendar = Calendar.getInstance(); //通过getInstance() 创建对象
        System.out.println(calendar);
        System.out.println("年 = "+ calendar.get(Calendar.YEAR));
        System.out.println("月 = "+ (calendar.get(Calendar.MONTH)+1));       //注意这里必须+1才是正确的月份
        System.out.println("日 = "+ calendar.get(Calendar.DAY_OF_MONTH));
        //Calendar 没有专门的格式化方法,需要程序员自己格式化输出
        System.out.println(calendar.get(Calendar.YEAR)+"."+(calendar.get(Calendar.MONTH)+1)+"."+calendar.get(Calendar.DAY_OF_MONTH));
    }
}

//= 2022= 4= 6
2022.4.6

进程已结束,退出代码0

第三代:

image-20220406094321413

image-20220406094423066

image-20220406100205173

package 常用类.日期类;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class Data03 {
    public static void main(String[] args) {
        LocalDate ld = LocalDate.now();
        System.out.println(ld);//2022-04-06
        System.out.println(ld.getYear());//2022

        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);//2022-04-06T09:57:21.927410300
        System.out.println(ldt.getMonth());//APRIL

        //DateTimeFormatter 进行格式化
        DateTimeFormatter dateTimeFormatter =DateTimeFormatter.ofPattern("yyyy.MM.dd E HH:mm:ss");
        System.out.println(dateTimeFormatter.format(ldt));//2022.04.06 周三 09:57:21
    }
}

plusDays(), minusDays()

package 常用类.日期类;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class Data04 {
    public static void main(String[] args) {
        Instant it = Instant.now();
        System.out.println(it);//2022-04-06T02:10:20.270559400Z
        Date date = Date.from(it);
        System.out.println(date);//Wed Apr 06 10:10:20 CST 2022


        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd E HH:mm:ss");
               //用plusDays 可以表示多少天后,minusDays 可以表示多少天前,还有其他 加年,月之类的 可以自己查
        System.out.println("现在时间 "+dateFormatter.format(now));//现在时间 2022.04.06 周三 10:10:20
        LocalDateTime ldt = now.plusDays(20);
        System.out.println("20天后 "+dateFormatter.format(ldt));//20天后 2022.04.26 周二 10:10:20
        LocalDateTime ldt2 = now.minusDays(30);
        System.out.println("30天前 "+dateFormatter.format(ldt2));//30天前 2022.03.07 周一 10:10:20



    }
}

14. 集合

14.1 集合框架体系图

image-20220406103402425

image-20220406104701193

image-20220406104636278

image-20220406105000556

14.2 迭代器

image-20220406105619489

  • 用迭代器遍历集合:
package 集合.Collections;

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

public class CollectionSys {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("小明");
        list.add(1);
        list.add(true);
        list.add(20.5);
        System.out.println(list);//[小明, 1, true, 20.5]
        list.remove(1);
        System.out.println(list);//[小明, true, 20.5]
        list.add(1);
        System.out.println(list);//[小明, true, 20.5, 1]
        list.remove((Integer)1);        //注意这里 删除 1 的时候,不能直接 remove(1) 这会表示删除下标为1的元素,remove((int)1)也是,必须用remove(Integer(1))才行
        System.out.println(list);//[小明, true, 20.5]
        System.out.println("---------------------");

        List movies = new ArrayList();  //ArrayList() 实现了 List接口
        movies.add(new Movie("战狼2",LocalDateTime.now().minusYears(2)));
        movies.add(new Movie("沙丘",LocalDateTime.now().minusYears(1).plusMonths(3)));
        movies.add(new Movie("杀破狼3",LocalDateTime.now().minusYears(3).minusDays(2)));

        System.out.println(movies);//[电影名:战狼2    上映日期 2020.04.06, 电影名:沙丘     上映日期 2021.07.06, 电影名:杀破狼3   上映日期 2019.04.04]


        System.out.println("-----------------------");
        Iterator it = movies.iterator();
        
        //itit 可以快速 生成下面的代码
        while (it.hasNext()){
            Object temp = it.next();
            System.out.println(temp);
        }
        
        //-------------------------------------------
        it = movies.iterator(); //表示重置迭代器,即指向开头元素的前一个位置

    }
}

class Movie{
    private String name;
    private LocalDateTime time;

    public Movie(String name, LocalDateTime time) {
        this.name = name;
        this.time = time;
    }

    @Override
    public String toString() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
        return String.format("电影名:%-5s  上映日期 %-10s",name,dateTimeFormatter.format(time));
    }
}
  • 用增强for循环也可以,增强for就是简化版的iterator 本质一样,但只能用于遍历集合 或 数组
for (Object it : movies){
    System.out.println(it);
}

14.3 List 接口

image-20220406142055993

List 和 Set 都实现了 Collection接口,但是List实现的 和 Set 实现的是不一样的,要分开

image-20220406141723656

//1.List集合的添加顺序和取出顺序一致、且可以重复
//2.List支持索引,从0开始
//JDK 中实现List的子类有很多
package 集合.MyList;

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

@SuppressWarnings("all")
public class list01 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("Tom");
        list.add("Jerry");
        list.add("Jack");
        list.add("Lucy");
        System.out.println(list);//[Tom, Jerry, Jack, Lucy]

        System.out.println(list.get(2));//Jack

        list.add(2,"杨皓翔");
        System.out.println(list);//[Tom, Jerry, 杨皓翔, Jack, Lucy]

        List list2 = new ArrayList();
        list2.add("关羽");
        list2.add("张飞");
        list2.add("刘备");
        list2.add("刘备");

        list.addAll(2,list2);
        System.out.println(list);//[Tom, Jerry, 关羽, 张飞, 刘备, 刘备, 杨皓翔, Jack, Lucy]

        //indexOf() 返回数组下标
        int index = list2.indexOf("张飞") + 1;
        System.out.println("张飞在list2集合中的第" + index +"个位置");//张飞在list2集合中的第2个位置

        index = list2.lastIndexOf("刘备") + 1;
        System.out.println("最后一个刘备在list2中的第"+index+"个位置");//最后一个刘备在list2中的第4个位置

        list2.remove(3);
        System.out.println(list2);//[关羽, 张飞, 刘备]

        list2.set(0,"马超");  //将下标为0的数据更换为马超
        System.out.println(list2);//[马超, 张飞, 刘备]

        list2.add("关羽");
        list2.add("赵云");
        list2.add("吕布");
        //subList 截取的是前闭后开区间
        List listFive = list2.subList(0,5);
        listFive.set(2,"黄忠");
        System.out.println(listFive);//[马超, 张飞, 黄忠, 关羽, 赵云]

    }
}

//三种遍历 方式

[外链图片转存中…(img-2qEsjpf5-1649684466523)]

package 集合.MyList;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

public class list02 {
    public static void main(String[] args) {
        //以下三种均可以
//        List books = new ArrayList();
//        List books = new LinkedList();
        List books = new Vector();

        books.add(new Book("三国演义",105,"罗贯中"));
        books.add(new Book("西游记",109,"吴承恩"));
        books.add(new Book("水浒传",100,"施耐庵"));
        books.add(new Book("红楼梦",97,"曹雪芹"));

        MySort(books);
        for(Object it :books){
            System.out.println((Book)it);
        }
    }
    public static void MySort(List books){
        for (int i = 0; i < books.size() - 1 ; i++) {
            for (int j = 0; j < books.size() - i - 1; j++) {
                Book b1 = (Book)books.get(j);
                Book b2 = (Book)books.get(j + 1);
                if(b1.getPrice() > b2.getPrice()){
                    books.set(j,b2);
                    books.set(j+1,b1);
                }
            }
        }
    }
}
class Book{
    private String name;
    private double price;
    private String writer;

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

    public String getWriter() {
        return writer;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return String.format("书名:%-5s作者:%-6s价格:%.2f",name,writer,price);
    }
}
14.3.1 ArrayList
//ArrayList 底层是由数组实现数据存储的,基本等同于Vector
//除ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList

image-20220406153325414

在IDEA设置时,可以设置成如下,以显示更多调试数据

image-20220406161412162

14.3.2 Vector

image-20220406162513584

image-20220406162735940

14.3.3 LinkedList

image-20220406170721214

image-20220406193525127

image-20220406194335817

14.4 Set 接口

//1.	无序(添加和取出的顺序不一样),没有索引
//2.	不允许重复元素,所以最多包含一个null

image-20220406194755948

package 集合.MySet;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

@SuppressWarnings({"all"})
public class Set01 {
    public static void main(String[] args) {
        // Set 接口的实现类的对象(Set接口对象)不能存放重复元素,可以加null
        Set set = new HashSet();
        set.add("Tom");
        set.add("Jerry");
        set.add("Lucy");
        set.add("Lucy");//添加重复数据
        set.add(null);
        set.add("Jack");
        System.out.println(set);//[null, Tom, Lucy, Jerry, Jack]

        //遍历
        //法一:迭代器
        Iterator it = set.iterator();
        while (it.hasNext()) {
            System.out.print(it.next()+" ");
        }

        // 快捷键——>大写 I
        //法二:增强for
        for (Object sets:set) {
            System.out.println(sets);
        }
    }
}
14.4.1 HashSet
// HashSet 实现了Set接口
// HashSet 实际上是HashMap 
// 可以存放 null 但只能有一个
// HashSet 不保证元素是有序的(存放元素的顺序和取出元素的顺序),取决于hash后,再确定索引的结果
// 不能有重复元素
//注意 不能有重复元素这一点
/*
        看是不是重复元素,得看源码怎么定义的
*/
package 集合.MySet;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class Set02 {
    public static void main(String[] args) {
        Set hashSet = new HashSet();
        hashSet.add("Lucy");
        /*
        看是不是重复元素,得看源码怎么定义的
         */
        hashSet.add("Jerry");   //OK
        hashSet.add("Jerry");   //false ,插入重复数据

        hashSet.add(new Dog("Tom"));    //OK
        hashSet.add(new Dog("Tom"));    //OK,在没有重写equals()时,不算重复数据

        hashSet.add(new String("str")); //OK
        hashSet.add(new String("str")); //false,重复元素

        System.out.println(hashSet);//[集合.MySet.Dog@5594a1b5, 集合.MySet.Dog@6a5fc7f7, Lucy, Jerry]
    }
}
class Dog{
    public String name;

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

//运行结果
[str, 集合.MySet.Dog@5594a1b5, 集合.MySet.Dog@6a5fc7f7, Lucy, Jerry]
//HashSet底层机制说明:数组+链表+红黑树

image-20220407095014142

image-20220407092251817

//如果 做测试想要hashcode相等,以加到同一个结点后形成链表
//则可以重写hashCode(),HashSet.add()会调用hashCode()来计算hash值,底层计算的hash值与hashCode()返回的值不同,但有一个明确的计算公式
class Person{
    private int a;
    public Person(int a){
        this.a = a;
    }
    @Override
    public int hashCode(){
        return 100;		//返回同一个hashCode值
    }
}
// 自定义数据类型如何不让出现重复元素:
// 重写 equals() 和 hashCode() -- 两个都要重写!!!
// 使用快捷键--> Alt + Insert 即可

package 集合.MySet;

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

public class Set03 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("张三",12));
        hashSet.add(new Employee("张三",12));
        hashSet.add(new Employee("李四",12));
        hashSet.add(new Employee("李四",11));

        for (Object it: hashSet) {
            System.out.println((Employee)it);
        }
    }
}
class Employee{
    private String name;
    private int age;

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

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

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

    @Override
    public String toString() {
        return String.format("姓名:%-5s年龄:%-2d",this.name,this.age);
    }
}
// 如果类中包含类,想要不能添加重复的元素,则两个类均要重写 equals() 和 hashCode()

package 集合.MySet;

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

public class Set04 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("张三",10000,1999,1,2));
        hashSet.add(new Employee("张三",10000,1999,1,2));
        hashSet.add(new Employee("张三",20000,1999,1,2));
        hashSet.add(new Employee("李四",10000,1999,1,2));


        for(Object it : hashSet){
            System.out.println((Employee)it);
        }

    }
}

class MyDate{
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year && month == myDate.month && day == myDate.day;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }
}

class Employee{
    private String name;
    private double sal;
    private MyDate birthday;

    public Employee(String name, double sal, int year,int month,int day) {
        birthday = new MyDate(year, month, day);
        this.name = name;
        this.sal = sal;
    }

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

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

    @Override
    public String toString() {
        return String.format("姓名:%4s薪资:%.2f生日:%d.%d.%d", name, sal, birthday.getYear(), birthday.getMonth(), birthday.getDay());
    }
}
14.4.2 LinkedHashSet

image-20220407141133486

image-20220407141200021

//1. LinkedHashSet 遍历数据的顺序是和插入数据时的顺序相同
//2. LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
//3. LinkedHashSet 底层结构(数组+双向链表)
//4. 添加第一次时,直接将数组table 扩容到 16,存放的节点类型是LinkedHashMap$Entry
//	Entry是内部类,继承了HashMap.Node

image-20220407150712646

14.4.3 TreeSet
//不允许重复,但是加入进去的数据会被排序,当然也可以自定义Comparator 从而用TreeSet(Comparator) 构造器进行自定义排序,数据结构用的是树

//TreeSet的底层是 TreeMap

//这只是其中一个构造器,但是可以发现 它存储数据是用TreeMap存的

image-20220408145617258

package 集合.MySet;

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

public class TreeSet_ {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();

        treeSet.add("tom");
        treeSet.add("jack");
        treeSet.add("sp");
        treeSet.add("a");
        treeSet.add("Boy");
        treeSet.add("Tom");

        //默认是按字母升序排列,且如果有大写字母 大写在前,小写在后
        // 排序原理可以通过查看源码 下面的自定义排序顺序其实是按源码原理进行改变,是排序的一部分 即很多集合都实现了Comparator接口,来进行比较
        System.out.println(treeSet);//[Boy, Tom, a, jack, sp, tom]

        treeSet.clear();
        treeSet.add(2);
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(4);
        treeSet.add(0);
        treeSet.add(5);
        //默认从小到大
        System.out.println(treeSet);//[0, 1, 2, 3, 4, 5]

        //自定义元素排序顺序
        //使用TreeSet 提供的一个构造器,可以传入一个比较器
        /*
        !!!!! 注意这里compare 返回的是int 类型,实际上在比较时,compare只是其中的一个过程,最终是否将结点添加进去还有另一个过程,看源码就可以
        */
        TreeSet treeSet2 = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return (Integer)o2 - (Integer)o1 ;//从大到小
            }
        });

        treeSet2.add(2);
        treeSet2.add(3);
        treeSet2.add(1);
        treeSet2.add(4);
        treeSet2.add(0);
        treeSet2.add(5);
        System.out.println(treeSet2);//[5, 4, 3, 2, 1, 0]


    }
}

14.5 Map 接口

// Set 其实也是按照 k-v 键值对存放,只是Set的 v由系统默认存入一个常量,而Map则是自己定义存入

image-20220407155121949

package 集合.MyMap;

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

public class Map01 {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("1","张三");
        map.put("2","李四");
        System.out.println(map);//{1=张三, 2=李四}
        //不允许key有重复,但是不同的是,出现重复则进行替换 而不是舍去
        map.put("1","王五");
        System.out.println(map);//{1=王五, 2=李四}

        map.put(1, "赵六");   //key 值不一定必须是字符串
        map.put(new Object(),"哈哈");
        System.out.println(map);//{1=王五, 1=赵六, 2=李四, java.lang.Object@3ac3fd8b=哈哈}

        //通过 key访问 value
        System.out.println(map.get("1"));//王五
        System.out.println(map.get((Integer)1));//赵六
    }
}

常用方法

image-20220407212216402

image-20220407212725108

14.5.1 HashMap

image-20220407212706976

//HashMap 的底层是以实现Map接口 加上一些属性和方法的(看HashMap 和 Map 的源码即可)
//其中k-v数据保存在 HashMap$Node 即HashMap的内部类Node中
//而Node实现了Map的Entry接口

image-20220407205243813

//实际上,EntrySet是个HashMap的内部类-- final class EntrySet extends AbstractSet<Map.Entry<K,V>> ,而在调试时进入源码发现 entrySet是EntrySet类,但是右键进入其源码发现是一个 Set<Map.Entry<K,V>> entryset变量, 我不理解该过程是怎么处理的呢?
// 我觉得是 调用EntrySet的构造函数返回了一个Entry,而接收者是entrySet

//注意:存k-v的是Node,但是entrySet是个Entry的Set集(EntrySet<Map.Entry>)  --> 因为Node实现了Entry接口故可以存放,进而利用Entry的方法方便管理k

Map的遍历

package 集合;

import java.util.*;

@SuppressWarnings({"all"})
public class Set06 {
    public static void main(String[] args) {

        Map map = new HashMap();
        map.put("01","张三");
        map.put("02","李四");
        map.put("03","王五");
        map.put("04","赵六");

        //先取出所有的Key 再通过Key 取出对应的 Value
        Set keyset = map.keySet();
        System.out.println(keyset);//[01, 02, 03, 04]
        //组一:取key再遍历
        //(1)
        for(Object key : keyset){
            System.out.println(key + "-" + map.get(key));
        }
        //(2)
        Iterator it = keyset.iterator();
        while (it.hasNext()){
            Object key = it.next();
            System.out.println(key + "-" + map.get(key));
        }

        //组二:取value再遍历
        Collection values = map.values();
        //这里可以使用所有的Collections使用的遍历方法
        //(1)
        for (Object value :values) {
            System.out.println(value);
        }
        //(2)
        Iterator it2 = values.iterator();
        while (it2.hasNext()) {
            Object value = it2.next();
            System.out.println(value);
        }

        //组三 通过EntrySet 获取 k-v
        Set entrySet = map.entrySet(); //EntrySet<Map.Entry<k,>
        //(1)
        for(Object e:entrySet){
            //将e转成 Map.Entry // e 的类型为 HashMap$Node
            Map.Entry m = (Map.Entry) e;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        //(2)
        Iterator it3 = entrySet.iterator();
        while (it3.hasNext()){
            //it3 的类型为 HashMap$Node 而 Node 实现了 Entry接口,实际上如果可以转成 HashMap.Node也可以使用getKey() 和 getValue() 但是 他们不是public 故只能转成 Entry
//            HashMap.Node m = (HashMap.Node)it3.next();
            Map.Entry m = (Map.Entry)it3.next();
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}

image-20220407220557740

小结:

image-20220408092337829

HashMap底层机制

image-20220408093305399

扩容机制

image-20220408093347766

14.5.2 Hashtable

image-20220408110604496

// 简单说明 Hashtable 底层
//1.底层有数组 Hashtable$Entry[] 初始化大小为 11
//2.临界值 threshold 8 (11 * loadFactor --- 11 * 0.75)
//3.扩容机制:(oldCapaity << 1) + 1  ----> 新容量 =  原容量*2 + 1

image-20220408120718606

14.5.3 Properties

image-20220408121431553

//继承了Hashtable 通过 k-v存放数据,键值对 均不能为null
//有相同的key值,也是会被替换  
//增删改查相关操作 和HashMap的差不多,忘记了可以去自己查一下
14.5.4 TreeMap
//对比TreeSet TreeSet底层是用TreeMap<k,v>存数据, 但v 是由系统自己定义的,所以还是单列的,而TreeMap 存数据时就可以k-v键值对存储,是双列的
//TreeMap 也有可以传入comparartor的构造器用于 put后 就可以排好序

14.6 开发中如何选择集合实现类

image-20220408135011181

14.7 Collections 工具类

image-20220408155616975

image-20220408161116193

Collections

14.8 本章小题(都是好题)

题目1:

image-20220408162234981
题目2:

image-20220408164710690

package 集合.Collections;

import java.util.TreeSet;

public class Collection02 {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        //底层源码分析:add方法会调用 TreeSet的put方法,
        // 其中里面的compare函数会先将传入的对象转成Comparable接口类型,再用compareTo去比较 是否为重复数据
        //但是如果你这个自定义类没有实现Comparable接口,就无法转换成Comparable接口类,就会抛出 类型转换异常
        // Comparable接口内 就只有一个 compareTo 方法,所以只有让自定义类实现这个方法,这个异常才不会被抛出
        treeSet.add(new Person());  //ClassCastException
    }
}
class Person{}
package 集合.Collections;

import java.util.TreeSet;

public class Collection02 {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        //底层源码分析:add方法会调用 TreeSet的put方法,
        // 其中里面的compare函数会先将传入的对象转成Comparable接口类型,再用compareTo去比较 是否为重复数据
        //但是如果你这个自定义类没有实现Comparable接口,就无法转换成Comparable接口类,就会抛出 类型转换异常
        // Comparable接口内 就只有一个 compareTo 方法,所以只有让自定义类实现这个方法,这个异常才不会被抛出
        treeSet.add(new Person("张三"));  //OK
        
        // 不抛异常了,但是如此简单的compareTo使得只能添加一个new Person() 因为只返回0 ,使得在put方法中利用compareTo返回值决定新元素插入位置, 若 为0:该元素key值已存在->丢弃数据 (这是Set,要是Map则会更新数据)
        //所以要将compareTo 写好
        
        //!!!! 看本章小题的第一题 对比 tree 和 hash 的比较方法!!!!!
        
        treeSet.add(new Person("李四"));  //false
        System.out.println(treeSet);//[Person {name='张三'}]
    }
}
class Person implements Comparable{
    private String name;

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

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

    @Override
    public String toString() {
        return "Person {" +
                "name='" + name + '\'' +
                '}';
    }
}
package 集合.Collections;

import java.util.TreeSet;

public class Collection02 {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        //底层源码分析:add方法会调用 TreeSet的put方法,
        // 其中里面的compare函数会先将传入的对象转成Comparable接口类型,再用compareTo去比较 是否为重复数据
        //但是如果你这个自定义类没有实现Comparable接口,就无法转换成Comparable接口类,就会抛出 类型转换异常
        // Comparable接口内 就只有一个 compareTo 方法,所以只有让自定义类实现这个方法,这个异常才不会被抛出
        treeSet.add(new Person("Tom"));  //OK
        treeSet.add(new Person("Jerry"));  //OK
        treeSet.add(new Person("A"));  //OK
        System.out.println(treeSet);// 看下面的输出结果
    }
}
class Person implements Comparable{
    private String name;

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

    public String getName() {
        return name;
    }

    @Override
    public int compareTo(Object o) {
        Person p = (Person) o;

        //compareTo的返回值是用于put 里插入数据,对于treeSet 是决定插入左子树还是右子树的
        //对于如下代码  则输出---[Person {name='A'}, Person {name='Tom'}, Person {name='Jerry'}]
        //若将-1 和 1 交换位置 ---[Person {name='Jerry'}, Person {name='Tom'}, Person {name='A'}]
        if(p.getName().equals(name)){
            return 0;
        }

        if(p.getName().length() > name.length()){
            return 1;
        }else {
            return -1;
        }

    }

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

题目3:

image-20220408180043670

package 集合.Collections;

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

public class Colections03 {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        Person p1 = new Person(1001,"AA");
        Person p2 = new Person(1002,"BB");
        set.add(p1);        //将 p1指向的对象 根据hash值放入table的某一地址 (记住是 对象!!!)
        set.add(p2);        //将 p2指向的对象 根据hash值放入table的某一地址

        //将 p1指向的对象的name更改后,p1所代表的对象的hash值就会发生变化,虽然p1仍然指向的是原来那个对象,且原来那个对象的name确实变化了
        // 但是 在remove时会根据hash值进行移除,这时p1的hash值已经和上面添加时的hash值不同了,所以remove没有移除原来添加的对象
        p1.name = "CC";
        set.remove(p1);
        System.out.println(set);//[Person {id=1002, name='BB'}, Person {id=1001, name='CC'}]

        //这时,想要add(new Person(1001,"CC")) 此时这个new 的对象的hash值,才是刚才想要remove的hash,由于之前remove的位置为空,所以可以添加
        set.add(new Person(1001,"CC"));
        System.out.println(set);//[Person {id=1002, name='BB'}, Person {id=1001, name='CC'}, Person {id=1001, name='CC'}]

        //这时又add(new Person(1001,"AA")) 此时这个hash值和第一次添加对象时的hash值相同,但是!,第一次添加的对象的name已经该变了,所以这里会挂到第一次添加的对象的后面 
        set.add(new Person(1001,"AA"));
        System.out.println(set);//[Person {id=1002, name='BB'}, Person {id=1001, name='CC'}, Person {id=1001, name='CC'}, Person {id=1001, name='AA'}]

    }
}
class Person{
    int id;
    String name;

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

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

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

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

15. 泛型

image-20220408203708658

image-20220408203844602

给泛型指向的数据类型要求是引用类型,不能是基本数据类型

//List<Integer> list = new ArrayList<>();// OK
//List<int> list = new ArrayList<>(); //false
代码实例

实例1:确定泛型类型后,编译器可以检测添加是否正确

package 泛型;

import java.util.ArrayList;

public class Generic01 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("小狗", 1);
        Dog dog2 = new Dog("大狗", 2);
        Cat cat1 = new Cat("小猫",1);

        ArrayList<Dog> arraylist = new ArrayList<>();
        arraylist.add(dog1);
        arraylist.add(dog2);
        //如果没有使用泛型确定ArrayList的类型,则加入Cat类也不会有问题
        //但如果使用泛型确定要添加的类型后,如果加入的不是该类型编译器会报错
//        arraylist.add(cat1);//false
        System.out.println(arraylist);//[Dog{name='小狗', age=1}, Dog{name='大狗', age=2}]

        //同理这里也不用像以前一样 从Object 向下转型,很麻烦了
        for(Dog it:arraylist){
            System.out.println(it);
        }

        //使用泛型确定的类型为父类时,可以添加子类
        ArrayList<Animal> arraylist2 = new ArrayList<>();
        arraylist2.add(cat1);
        arraylist2.add(dog2);
        //输出时会使用动态绑定机制,就近原则,使用子类的函数
        System.out.println(arraylist2);//[Cat{name='小猫', age=1}, Dog{name='大狗', age=2}]

        //同理这里也不用像以前一样 从Object 向下转型 很麻烦了
        for (Animal it:arraylist2) {
            System.out.println(it);
        }
    }
}

class Animal{
    protected String name;
    protected int age;

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

class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

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

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

实例2:类使用泛型

package 泛型;

import java.util.ArrayList;

public class Generic02 {
    public static void main(String[] args) {
        Person<String> person1 = new Person<>("小王");
        Person<Integer> person2 = new Person<>(1);
//        Person<String> person3 = new Person<>(2);//false

        ArrayList<Person> arrayList = new ArrayList<>();
        arrayList.add(person1);
        arrayList.add(person2);
        for (Person p : arrayList) {
            System.out.println(p.getName());
        }
        //output:
        //小王
		//1

        //---------------------------------------------------------
        ArrayList<Person<String>> arrayList1 = new ArrayList<>();
        arrayList1.add(person1);
//        arrayList.add(person2);//false
        for (Person p : arrayList1) {
            System.out.println(p.getName());
        }
        //output:
        //小王

    }
}
class Person<T>{
    private T name;

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

    public T getName() {
        return name;
    }
}

实例3:遍历

package 泛型;

import java.util.*;

public class Generic03 {
    public static void main(String[] args) {
        Student tom = new Student("Tom", 12);
        Student tony = new Student("Tony", 11);
        /*
         * 用HashSet泛型
         * 使用两种方法遍历
         */
        HashSet<Student> studentHashSet = new HashSet<>();
        studentHashSet.add(tom);
        studentHashSet.add(tony);
        //法一
        for (Student student : studentHashSet) {
            System.out.println(student);
        }
        //法二
        Iterator<Student> it = studentHashSet.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        /*
         * 用HashMap泛型
         * 使用三种方法遍历
         */
        HashMap<String,Student> studentHashMap = new HashMap<>();
        studentHashMap.put(tom.getName(),tom);
        studentHashMap.put(tony.getName(),tony);
        //法一
        /*
         *entrySet() 源码
         *  public Set<Map.Entry<K,V>> entrySet() {
         *         Set<Map.Entry<K,V>> es;
         *         return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
         *     }
         */
        Set<Map.Entry<String, Student>> studentSet = studentHashMap.entrySet();
        for (Map.Entry<String, Student> entry : studentSet) {
            System.out.println(entry.getKey()+"-"+entry.getValue());
        }

        //法二
        /*
         *  public final Iterator<Map.Entry<K,V>> iterator() {
         *   return new EntryIterator();
         *  }
         */
        Set<Map.Entry<String, Student>> studentSet2 = studentHashMap.entrySet();
        Iterator<Map.Entry<String, Student>> iterator = studentSet2.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next().getKey() + "-" + iterator.next().getValue());
        }

        //法三
        Set<String> ks = studentHashMap.keySet();
        for(String it2:ks){
            System.out.println(it2 + "-"+ studentHashMap.get(it2));
        }
    }
}
class Student{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

}
题目:(学到了代码复用的知识)

创建Employee类,其中包含name,sal 和 birthday,其中birthday 是类,包含 year、month、day,后使用ArrayList 的sort进行排序,名字相同就按出生日期排

自己写的:

解答+回顾成员内部类知识

package 泛型;

import java.util.ArrayList;
import java.util.Comparator;

public class Generic04 {
    public static void main(String[] args) {
        Employee tom = new Employee("tom",10000,1999,3,1);
        Employee tom1 = new Employee("tom",15000,1995,1,12);
        Employee tom2 = new Employee("tom",15001,1995,2,12);
        Employee tom3 = new Employee("tom",15002,1995,2,1);
        Employee jerry = new Employee("jerry",12000,1999,3,19);
        Employee alice = new Employee("alice",11000,1999,3,19);

        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(tom);
        employees.add(tom1);
        employees.add(tom2);
        employees.add(tom3);
        employees.add(jerry);
        employees.add(alice);
        employees.sort(new Comparator<>() {
            /*
             * 按姓名排序,若姓名相同 则按出生日期排序,谁出生早 谁排前 
             * @param o1
             * @param o2
             * @return
             */
            @Override
            public int compare(Employee o1, Employee o2) {
                if(o1.getName().equals(o2.getName())){
                    if(o1.getYear() == o2.getYear()){
                        if(o1.getMonth() == o2.getMonth()){
                            return o1.getDay() - o2.getDay();
                        }
                        return o1.getMonth() - o2.getMonth();
                    }
                    return o1.getYear() - o2.getYear();
                }
                return o1.getName().compareTo(o2.getName());
            }
        });

        for(Employee it:employees){
            System.out.println(it);
        }
    }
}
class Employee{
    private String name;
    private double sal;
    private Birthday birthday;

    public int getYear() {
        return birthday.year;
    }

    public int getMonth() {
        return birthday.month;
    }

    public int getDay() {
        return birthday.day;
    }
    private class Birthday{
        private int year;
        private int month;
        private int day;

        @Override
        public String toString() {
            return "Birthday = "+year+"."+month+"."+day;
        }

        public Birthday(int year, int month, int day){
            this.year = year;
            this.month = month;
            this.day = day;

        }
    }
    public Employee(String name,double sal,int year,int month,int day){
        this.name = name;
        this.sal = sal;
        birthday = new Birthday(year, month, day);
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}

感觉更好的答案:

package 泛型;

import java.util.ArrayList;
import java.util.Comparator;

public class Generic04_1 {
    public static void main(String[] args) {
        Employee tom = new Employee("tom",10000,1999,3,1);
        Employee tom1 = new Employee("tom",15000,1995,1,12);
        Employee tom2 = new Employee("tom",15001,1995,2,12);
        Employee tom3 = new Employee("tom",15002,1995,2,1);
        Employee jerry = new Employee("jerry",12000,1999,3,19);
        Employee alice = new Employee("alice",11000,1999,3,19);

        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(tom);
        employees.add(tom1);
        employees.add(tom2);
        employees.add(tom3);
        employees.add(jerry);
        employees.add(alice);
        employees.sort(new Comparator<>() {
            /*
             * 按姓名排序,若姓名相同 则按出生日期排序,谁出生早 谁排前
             * 这里将 出生日期的比较放在了 Birthday 成员内部类去比较,使得代码 复用性更高,也更简洁
             * 但是要注意 如果要这样写的话,成员内部类Birthday的访问权限 得是 public
             */
            @Override
            public int compare(Employee o1, Employee o2) {
                if(o1.getName().equals(o2.getName())) {
                   return o1.getBirthday().compareTo(o2.getBirthday());
                }
                return o1.getName().compareTo(o2.getName());
            }
        });

        for(Employee it:employees){
            System.out.println(it);
        }
    }
}
class Employee{
    private String name;
    private double sal;
    private Birthday birthday;

    public Birthday getBirthday() {
        return birthday;
    }

    public class Birthday implements Comparable<Birthday>{  //实现Comparable接口 才能重写 compareTo函数
        private int year;
        private int month;
        private int day;

        @Override
        public String toString() {
            return "Birthday = "+year+"."+month+"."+day;
        }

        @Override
        public int compareTo(Birthday o) {
             int temp = year - o.year;
             if(temp!=0){
                 return temp;
             }
             temp = month - o.month;
             if(temp != 0){
                 return temp;
             }
             temp = day - o.day;
             return temp;
        }

        public Birthday(int year, int month, int day){
            this.year = year;
            this.month = month;
            this.day = day;

        }
    }
    public Employee(String name,double sal,int year,int month,int day){
        this.name = name;
        this.sal = sal;
        birthday = new Birthday(year, month, day);
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}
自定义泛型类

image-20220409110853376

static静态属性也不能用泛型

package 泛型;

public class Genertic05 {
    public static void main(String[] args) {
        Tiger<Double,String,Integer> tiger = new Tiger<>(12.1,"john",12);
        System.out.println(tiger);
        Tiger tiger1  = new Tiger("hello"); //等同于 Tiger<String,Object,Object> tiger1 = new Tiger<>("hello"); 
        System.out.println(tiger1);

    }
}
class Tiger<T,K,V>{
    private T str1;
    private K str2;
    private V str3;

    public Tiger(T str){
        str1 = str;
    }
    public Tiger(T str1,K str2,V str3) {
        this.str1 = str1;
        this.str2 = str2;
        this.str3 = str3;
    }

    @Override
    public String toString() {
        return "Tiger{" +
                "str1=" + str1 +
                ", str2=" + str2 +
                ", str3=" + str3 +
                '}';
    }
}
自定义泛型接口

image-20220409123139047

package 泛型;

public class GenericInterface01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.start();
        phone.end();
        String s = phone.IsUsb("hello");
        System.out.println(s);

        Calculator calc = new Calculator();
        calc.start();
        calc.end();

        USB<String> device = new Phone();   //因为 Phone类实现的就是 USB<String> 接口
        String str1 = device.start();
//        Double str2 = device.end(); //false
        USB<Integer> device2 = new Calculator(); //OK
        USB devices = new Phone();//OK
//        USB<Double> device3 = new Phone();//false Phone 没有实现 这个接口

        USB_2<Boolean> device3 = new Computer(); //OK
//        USB_2<Integer> device4 = new Computer(); //false
        device3.start();
        device3.end();
        device3.func();
        device3.IsUsb(1.1);

    }
}
interface USB<T>{
    T start();
    T end();
    //static 中泛型就不能使用
//    static void Is(T temp){
//        System.out.println("This is Is func()"+ temp);
//    }
    //default中泛型则可以使用
    default T IsUsb(T temp){
        System.out.println("This is USB "+ temp );
        return temp;
    }
//    T temp; //false ,接口中属性都是静态性质,所以这样不行
}

interface USB_2<K> extends USB<Double> {    //继承接口时确定泛型类型
    K func();
}
class Phone implements USB<String>{     //实现接口时确定泛型类型
    @Override
    public String start() {
        return "This is phone start";
    }

    @Override
    public String end() {
        return "This is phone end";
    }
}
class Calculator implements USB<Integer> {  //实现接口时确定泛型类型
    @Override
    public Integer start() {
        System.out.println("This is calculator start");
        return 1+1;
    }

    @Override
    public Integer end() {
        System.out.println("This is calculator end");
        return 2+2;
    }
}

class Computer implements USB_2<Boolean> {      //实现 继承了USB<Double> 的接口
    @Override
    public Boolean func() {
        System.out.println("This is a computer");
        return true;
    }

    @Override
    public Double start() {
        System.out.println("This is computer start");
        return 1.1;
    }

    @Override
    public Double end() {
        System.out.println("This is computer end");
        return 2.2;
    }
}
自定义泛型方法
package 泛型;

public class GenerticMethod01 {
    public static void main(String[] args) {
        String a = func("hello",12);
        /* output
         * static func'T Class is class java.lang.String
         * static func'K Class is class java.lang.Integer
         */

        Test<String> test = new Test<>("Is test");
//        test.func2(12.2);//false 泛型要求是String
        test.func2("12.2");//true
        /* output
         * Class's func2 K's Class is class java.lang.String is 12.2
         */


        test.func(12.2);//true,泛型函数里有别的泛型
        /* output
         * func K's Class is class java.lang.String
         * func T's Class isclass java.lang.Double
         */

        test.func("hello");//true
        /* output
         * func K's Class is class java.lang.String
         * func T's Class isclass java.lang.String
         */

    }
    public static <T,K> T func(T str,K str2){
        System.out.println("static func'T Class is " + str.getClass());
        System.out.println("static func'K Class is " + str2.getClass());
        return str;
    }
}
class Test<K>{
    public Test(K testIs) {
        this.testIs = testIs;
    }

    K testIs;
    //泛型方法
    public <T> void func(T temp){
        System.out.println("func K's Class is "+testIs.getClass());
        System.out.println("func T's Class is"+temp.getClass());
    }

    //使用了泛型的方法,不是泛型方法
    public void func2(K temp){
        System.out.println("Class's func2 K's Class is "+temp.getClass() + " is "+ temp);
    }

    /*
     *     这个并不能与上面的泛型方法做区分
     */
//    public void func(K temp){
//        System.out.println(temp.getClass());
//    }
}
泛型的继承和通配符

image-20220409153628907

package 泛型;

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

public class Generic06 {
    public static void main(String[] args) {
//        List<Object> list = new ArrayList<String>();    //false,泛型没有自动去判断继承关系的机制,要自己去说明
        List<?> list = new ArrayList<>();   //默认Object ,OK
        List<?> list1 = new ArrayList<King>();//OK
        List<?> list2 = new ArrayList<Father>();//OK
        List<?> list3 = new ArrayList<Son>();//OK
        //-----------------------------------------------
        
        List<? extends Father> list4 = new ArrayList<Father>();//OK
        List<? extends Father> list5 = new ArrayList<Son>();//OK,因为Son继承了Father
//        List<? extends Father> list6 = new ArrayList<King>();//false,因为King不是Father或其子类
//        List<? extends Father> list6 = new ArrayList<Object>();//false,因为Object不是Father或其子类
        List<? extends Father> list6 = new ArrayList<>();//ok,这没有说明泛型确定的类型
        //------------------------------------------------
        
        List<? super Father> list7 = new ArrayList<>(); //ok,这没有说明泛型确定的类型
//        List<? super Father> list8 = new ArrayList<Son>();//false,Son 不是Father 类或其父类
        List<? super Father> loose8 = new ArrayList<King>();//OK
        List<? super Father> loose9 = new ArrayList<Object>();//OK
        
    }
}

class King{}
class Father extends King{}
class Son extends Father{}

本章作业:

image-20220409171840479

//没有使用JUnit

package 泛型;

import org.junit.jupiter.api.Test;

import java.util.*;

public class HomeWork {
    public static void main(String[] args) {
        User tom = new User(1, 12, "Tom");
        User jack = new User(2, 22, "Jack");
        User lucy = new User(3, 2, "Lucy");
        DAO<User> userDAO = new DAO<>();
        userDAO.save("1",tom);
        userDAO.save("2",jack);
        userDAO.save("3",lucy);
        List<User> list = userDAO.list();
        for (User user : list) {
            System.out.println(user);
        }
        userDAO.delete("3");
        list = userDAO.list();
        for (User user : list) {
            System.out.println(user);
        }
        userDAO.update("2",lucy);
        list = userDAO.list();
        for (User user : list) {
            System.out.println(user);
        }
    }
}

class DAO<T> {
    Map<String,T> map;

    public DAO() {
        this.map = new HashMap<>();
    }
    
    public void save(String id, T entity){
        map.put(id, entity);
    }

    public T get(String id){
        Set<String> keys = map.keySet();
        for (String key : keys) {
            if(key.equals(id)){
                return map.get(key);
            }
        }
        return null;
    }
    public void update(String id,T entity){;
        if( map.get(id) != null){
            map.replace(id, entity);
            System.out.println("更新成功");
        }else {
            System.out.println("该id不存在");
        }
    }
    public List<T> list() {
        return new ArrayList<T>(map.values());
    }
    public void delete(String id){
        map.remove(id);
    }
}
class User{
    private int id;
    private int age;
    private String name;

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

    public int getId() {
        return id;
    }

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

16. JUnit使用

以前调试是,写好函数放入main中,通过对象进行调用调试,有时候还得把同类型函数注释掉,现在使用JUnit 则更加方便

image-20220409163946367

在需要调试的函数前加 @Test 并导入 JUnit5包进去,然后就可以在想调试的函数处,右键进行选择

17. 文件

常用操作

1. 创建文件:

image-20220409202900499

package 文件;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;

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

    }

    /**
     * new File() 只是在java中创建了一个对象
     * 只有执行了createNewFile 方法,才会真正的,在磁盘创建该文件
     */

    @Test
    //方式1:new File(String pathname)
    public void create01(){
        String filePath = "E:\\news.txt";
        File file = new File(filePath);

        try {
            file.createNewFile();
            System.out.println("create file successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    //方式2:new File(FIle parent,String child) -- 根据父目录+子文件名构建
    //E:news2.txt
    public void create02() {
        File parentPath = new File("E:\\");
        String fileName = "news2.txt";
        File file = new File(parentPath,fileName);

        try {
            file.createNewFile();
            System.out.println("create file successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    //方式3:new File(String parent,String child) -- 根据父目录+子路径
    public void create03() {
        String parentPath = "E:\\";
        String chilePath = "news3.txt";
        File file = new File(parentPath, chilePath);

        try {
            file.createNewFile();
            System.out.println("create file successfully");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
2. 获取文件信息

image-20220409203044425

package 文件;

import org.junit.jupiter.api.Test;

import java.io.File;

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

    }

    @Test
    public void info(){
        File file = new File("E:\\news.txt");

        //获取文件名
        System.out.println("文件名:"+file.getName());
        //获取文件绝对路径
        System.out.println("文件绝对路径:"+file.getAbsolutePath());
        //获取文件父目录
        System.out.println("文件父目录:"+file.getParent());
        //获取文件大小(字节) 一个汉字3个字节
        System.out.println("文件大小(字节):"+file.length());
        //文件是否存在
        System.out.println("文件是否存在:"+ file.exists());
        //是不是一个文件
        System.out.println("是不是文件:"+ file.isFile());
        //是不是一个目录
        System.out.println("文件是不是目录:"+ file.isDirectory());

    }
}

//output

文件名:news.txt
文件绝对路径:E:\news.txt
文件父目录:E:\
文件大小(字节):11
文件是否存在:true
是不是文件:true
文件是不是目录:false

进程已结束,退出代码0

image-20220409203946229

3. 目录操作和文件删除

image-20220409204121686

package 文件;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;

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

    }

    @Test
    //删除文件
    public void makeDir(){
        String path = "E:\\news.txt";
        File file = new File(path);
        if (file.exists()){
            if(file.delete()){
                System.out.println("删除成功");
            }else
                System.out.println("删除失败");
        }else
            System.out.println("文件不存在");
    }


    @Test
    //这里可以体会到在java中,目录也是文件
    //删除目录(文件夹)
    public void makeDir2(){
        String path = "E:\\Yang";
        File file = new File(path);
        if (file.exists()){
            if(file.delete()){
                System.out.println("删除成功");
            }else
                System.out.println("删除失败");
        }else
            System.out.println("目录不存在");
    }

    @Test
   //创建目录 一级 和 多级
    public void makeDir3(){
        //创建多级目录 mkdirs
        String directoryPath = "E:\\Yang\\a\\b\\c";
        File file = new File(directoryPath);
        if (file.exists()){
            System.out.println("该目录1已存在");
        }else {
            if(file.mkdirs()){
                System.out.println("多级目录1创建成功");
            }else {
                System.out.println("多级目录1创建失败");
            }
        }
        //创建一级目录 mkdir
        String directoryPath2 = "E:\\Wang";
        File file2 = new File(directoryPath2);
        if (file2.exists()){
            System.out.println("该目录2已存在");
        }else {
            if(file2.mkdir()){
                System.out.println("一级目录2创建成功");
            }else {
                System.out.println("一级目录2创建失败");
            }
        }
    }
}

IO流原理及流的分类

image-20220409210100778

//InputStream, OutputStream, Reader, Writer 都是抽象类,使用时要用实现他们的对象
FileInputStream:字节输入流

image-20220409212021762

package File;

import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.IOException;

public class File_1 {
    public static void main(String[] args) throws IOException {

    }

    @Test
    public void readFile() throws IOException {
        FileInputStream file = new FileInputStream("E:\\hello.txt");
        int readDate;

        /**
         * 用 FileInputStream 为了能读取中文,得使用 read(byte【】 b),该方法能将b.length个字节读入b中
         *         且返回的是实际读取到的字节数,而最多能读取b.length个字节数
         *         普通的read()读出来的是乱码
         *         read() 每次读取一个字节,返回int类型,即字节的ASCII码
         */


        byte[] bytes = new byte[20];
        readDate = file.read(bytes);
        System.out.println("实际读到的字节数:"+ readDate);//实际读到的字节数:16
        if (readDate > 0 ) {
            System.out.println(new String(bytes));
        }
        /**
         * output
         * hello world 小王
         */

        //------------------------------------------------------------------------------
        byte[] bytes2 = new byte[3];
        while (file.read(bytes2) > 0){
            System.out.println(new String(bytes2));
        }
        /**
         * 由于每次最多读到3个字节,所以仍然有可能会出现 没有正好读到汉字的那三个字节,导致出现乱码
         * output
         * hel
         * lo
         * wor
         * ld
         * 小?
         * 酢?
         */

        //------------------------------------------------------------------------------
        while ((readDate = file.read())!=-1){
            System.out.println(((char)readDate));
        }
        /**
         * output
         * hello world ????
         */

        file.close();
    }
}
FileOutputStream:字节输出流
package File;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

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

    }

    @Test
    /**
     * 若文件不存在,则会自动创建文件
     */
    public void writeFile01(){
        String filePath = "E:\\news2.txt";
        FileOutputStream file = null;
        String text = "hello world";
        try {

            /**
             * new FileOutputStream(filePath)       -- 该方式创建的文件流,写入文件时会覆盖原先文件
             * public FileOutputStream(String name)
             *
             * new FileOutputStream(filePath,true); -- 该方式则是追加写入
             * public FileOutputStream(String name, boolean append)
             */

            file = new FileOutputStream(filePath);
            //String.getBytes() 可以将String类型转换成byte【】
            //write 里面不能直接传String
            file.write(text.getBytes());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                file.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
实践1(文件拷贝)
package File;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
    /**
     * 程序功能:将E:\view.jpg图片,拷贝到D:\
     * @param args
     */
    public static void main(String[] args) {
        String srcFilePath = "E:\\view.jpg";
        String desFilePath = "D:\\view.jpg";

        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;

        try {
            inputStream = new FileInputStream(srcFilePath);
            outputStream = new FileOutputStream(desFilePath);
            byte[] bytes = new byte[1024];
            int len;
            int count = 0;
            //这里要循环读文件到程序,如果不循环,可能所要的文件大于1024个字节
            while((len = inputStream.read(bytes))!= -1 ){
                //读入程序后要立即写出到目的地址,否则可能无法正确拷贝文件
                //这里也必须要使用write(byte[] b,int off,int len),若用write(byte[] b) 则有可能文件1020个字节 但是将1024个字节写回文件,使得文件出错,这里是图片,则可能出现文件显示不全
                System.out.println(count++);        //通过count 我可以计数循环发生了多少次
                outputStream.write(bytes, 0, len);
            }
            System.out.println("Copy successfully!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
                outputStream.close();
            }catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}
FileReader:字符输入流

image-20220410141602071

package File;

import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

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

    }

    @Test
    public void reader01() {
        String filePath = "E:\\hello.txt";
        FileReader fileReader = null;
        int date = 0;
        try {
            fileReader = new FileReader(filePath);
            //单个字符读取
            while ((date = fileReader.read()) != -1) {
                System.out.print((char) date);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void reader02(){
        String filePath = "E:\\hello.txt";
        FileReader fileReader = null;
        int len = 0;
        char[] bytes = new char[3];
        try {
            fileReader = new FileReader(filePath);
            //一下读多个字符,使用read(buf)
            // read(),read(buf)都是如果返回-1则为读完
            //而 read(buf)每次返回实际读了多少个字符
            while((len = fileReader.read(bytes)) >0){
                System.out.print(new String(bytes,0,len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
FileWriter:字符输出流

image-20220410141751251

package File;

import org.junit.jupiter.api.Test;

import java.io.FileWriter;
import java.io.IOException;

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

    }

    @Test
    public void FileWriter(){
        String filePath = "E:\\hello.txt";
        FileWriter fileWriter = null;



        try {
            fileWriter = new FileWriter(filePath,true);
            //写入单个字符 write(int)
            fileWriter.write('八');

            //写入指定数组 write(char【】)
            //使用toCharArray() 将String 转成 char【】
            String text = "\nhello 小子";
            fileWriter.write(text.toCharArray());

            //写入整个字符串 write(String)
            String text2 = "\nhello 卡布奇诺";
            fileWriter.write(text2);

            //写入字符串只能部分 write(String,off,len)
            String text3 = "\nabcdefg";
            fileWriter.write(text3,0,2); //注意这里只能将 a 这一个字符写入 转义字符\n算一个字符

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileWriter != null) {
                try {
                    //只有close()或flush()后才能写入进去,看源码是它们执行过后才写入完成
                    fileWriter.flush();
//                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

节点流 和 处理流

image-20220410151020704

image-20220410151001448

/* 节点流 直接涉及处理数据源 较为底层,但是功能不丰富、效率也可能较低,所以产生了处理流在节点流的基础上完善节点流 */

image-20220410152152818

举例:用 BufferedReader / BufferedWriter 这样的处理流 (也叫包装流)操作有更方便的功能

而它们的源码里其实就是有 节点流的 相关接口

image-20220410152507578

处理流:

image-20220410160412714

//用字节流处理二进制文件【pdf、avi、doc、jpg....】
//用字符流既可以处理二进制文件,又可以处理文本文件

// 处理流 使用了装饰模式 和 适配器模式
// 相当于 处理流 将 直接参与底层文件操作的字节流 封装起来 并添加 更灵活的功能,方便人们使用

自己的简单总结看标题 ” [自己的理解](# 自己的理解)“

image-20220410200140015

1. 字符处理流
BufferedReader(用于字符流)
/*
* 用 BufferedReader 调用 FileReader 举例
*/
package Buffered;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class BufferedReader01 {
    public static void main(String[] args) throws Exception {
        String filePath = "E:\\hello.txt";
        String line;
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //按行读取 ,当返回null时停止读取
        while((line = bufferedReader.readLine()) != null){
            System.out.println(line);
        }
        //关闭流,只需要关闭 bufferedReader即可,因为底层会自动去关闭节点流
        // 这里的底层是FileReader
        bufferedReader.close();
    }
}
BufferedWriter(用于字符流)
package Buffered;

import java.io.BufferedWriter;
import java.io.FileWriter;

public class BufferedWriter01 {
    public static void main(String[] args) throws Exception{
        String filePath = "E:\\hello2.txt";
        //默认是覆盖的方式
//        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
       //若要追加写入的方式,在 节点流上说明是追加方式写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));


        //下面的写入没有换行
        bufferedWriter.write("hello 小黑");
        bufferedWriter.write("hello 小白");
        bufferedWriter.write("hello 小黄");
        //若要换行
        bufferedWriter.newLine();   //插入一个和系统相关的换行
        bufferedWriter.write("hello 换行");

        bufferedWriter.close();
    }
}
实践2(文件拷贝)
package Buffered;

import org.junit.jupiter.api.Test;

import java.io.*;

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

    }

    /**
     * BufferedReader 和 BufferedWriter 是字符流文件
     * 不要去操作二进制文件【jpg、doc、pdf、avi...】可能会造成文件损坏
     */

    @Test
    public void Copy01() throws Exception{
        BufferedReader bf = null;
        BufferedWriter bw = null;

        String srcPath = "E:\\hello.txt";
        String desPath = "E:\\hello_copy.txt";

        //这里bf 底层仍然调用的是FileReader进行读
        //bw 底层调用 FileWriter进行写
        char[] chars = new char[1024];
        bf = new BufferedReader(new FileReader(srcPath));
        bw = new BufferedWriter(new FileWriter(desPath));
        int len = 0;
        int count = 0;
        while((len = bf.read(chars)) > 0){
            System.out.println(count++);
            bw.write(chars, 0, len);
        }
        bw.close();
        bf.close();
    }

    @Test
    public void Copy02() throws Exception {
        BufferedReader bf = null;
        BufferedWriter bw = null;

        String srcPath = "E:\\hello.txt";
        String desPath = "E:\\hello_copy.txt";

        bf = new BufferedReader(new FileReader(srcPath));
        bw = new BufferedWriter(new FileWriter(desPath));
        String line;
        int count = 0;

        //readLine()读取一行数据,但没有将换行符读入,所以要自己手动添加换行
        while((line = bf.readLine()) != null){
            System.out.println(count++);
            bw.write(line);
            bw.newLine();
        }
        bw.close();
        bf.close();
    }
}
2. 字节处理流
BufferedInputStream(用于字节流)
BufferedOutputStream(用于字节流)

看 标题”[自己的理解](# 自己的理解)“

实践3(文件拷贝)
package Buffered;

import org.junit.jupiter.api.Test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

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

    }

    @Test
    public void Copy1() throws Exception{
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        String srcPath = "E:\\view.jpg";
        String descPath = "E:\\view_copy.jpg";

        byte[] bytes = new byte[1024];
        bis = new BufferedInputStream(new FileInputStream(srcPath));
        bos = new BufferedOutputStream(new FileOutputStream(descPath));
        int len = 0;
        //read(byte【】 b) 返回的是实际读到的字节个数
        while ((len = bis.read(bytes)) > 0){
            bos.write(bytes, 0, len);
        }
        System.out.println("copy completed");
        bos.close();
        bis.close();
    }

    @Test
    public void Copy2() throws Exception{
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        String srcPath = "E:\\view.jpg";
        String descPath = "E:\\view_copy.jpg";

        bis = new BufferedInputStream(new FileInputStream(srcPath));
        bos = new BufferedOutputStream(new FileOutputStream(descPath));
        int readDate;
        //read() 返回的是 ASCII码
        while (( readDate = bis.read()) != -1){
            bos.write(readDate);
        }
        System.out.println("copy completed");
        bos.close();
        bis.close();
    }
}
自己的理解

这两个与上面用于字符流的思路是一样的,处理流将底层节点流封装到它们里面

BufferedInputStream 构造器里有 InputStream 这是一个抽象类,所以只要是继承了InputStream 的 IO 类传入构造器,再加上动态绑定机制,就可以使用对应的方法去IO操作

同理,BufferedOutputStream构造器里面有 OutputStream 这也是一个抽象类,所以只要是继承了OutputStream 的 IO 类传入构造器,再加上动态绑定机制,就可以使用对应的方法去IO操作

包括上面所有的处理流底层,都是将类似于FileWriter、FileReader … 这种参与底层IO的类对应封装实现更好的功能

3. 对象处理流:

image-20220411100911822

// 必须实现相应接口(Serializable 或 Externalizable)但一般实现 Serializable 接口

image-20220411101258920

※ 序列化 与 反序列化

image-20220411101030136

ObjectInputStream (对象处理流)
package ObjectStream;

import org.junit.jupiter.api.Test;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

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

    }

    @Test
    public void Save() throws Exception{
        //序列化后,保存的文件格式不是文本文件,而是按照他的格式来保存
        String filePath = "E:\\data.dat";
        ObjectOutputStream oos = null;

        Dog dog1 = new Dog("小黑", 1);
        oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(dog1);
        //如果只写了oos.write(100); 则它只会保存100 不会把类型保存上
        oos.writeInt(100);             //int  --> Integer (实现了 Serializable)
        oos.writeChar('a');         //char --> Character (实现了 Serializable)
        oos.writeBoolean(true);     //boolean --> Boolean (实现了 Serializable)
        oos.writeChars("hello");    //String --> String (实现了 Serializable)
        oos.writeDouble(2.2);       //double --> Double (实现了Serializable)

        oos.close();
        System.out.println("数据保存完毕 ");
    }
}
class Dog implements Serializable {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
ObjectOutputStream(对象处理流)
/*
* 序列化时添加的顺序是怎么样的,反序列化就要按照这个顺序进行
*/

package ObjectStream;

import org.junit.jupiter.api.Test;

import java.io.*;

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

    }

    @Test
    public void Read() throws Exception{
        String filePath = "E:\\data.dat";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

        //这里要注意,反序列化后对象里面所有的东西都要一致才行
        //如:这里我在新文件里创建了一个新的Dog类,但是这个类的内部要和序列化时的Dog类一模一样,不能多实现一个方法或字段,也不能少一个方法或字段
        //但是方法的实现体内部可以做改变
        Dog dog = (Dog)ois.readObject();
        System.out.println(dog.getName());
        System.out.println(dog);
        ois.close();
    }
}
class Dog implements Serializable{
    private String name;
    private int age;
//    private int sal; //序列化时没有该字段

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

    public String getName() {
        return name + " Read ";
    }

        @Override
    public String toString() {
        return name + " " + age;
    }
}

对象处理流的注意事项

image-20220411112037435

// static 和 transient 不会被序列化, 序列化时不会将它们存入文件,反序列化时读取时它们也是null的
4. 标准输入输出流

image-20220411112901951

package StandardIOStream;

public class IOStream01 {
    public static void main(String[] args) {
        //System.in 标准输入:键盘
        //System.in 编译类型:InputStream
        //          运行类型:BufferedInputStream
        System.out.println(System.in.getClass());


        //System.out 标准输出:显示器
        //System.out 编译类型:PrintStream
        //           运行类型:PrintStream
        System.out.println(System.out.getClass());
    }
}
5. 转换流
//如果文件是utf-8 但是读取时是GBK 则可能出现乱码,总结一下就是:文件编码和读取时的程序编码 不一致导致可能会发生的乱码错误,故使用转换流来确定读取时的编码格式

image-20220411145955938

InputStreamReader (用于字符流)

[外链图片转存中…(img-YEN0PNWj-1649684466543)]

package TransformStream;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Transform01{
    /**
     * 本程序实验打开与该程序编码格式不同的文件时的情形
     * 程序编码格式:GBK
     * 欲打开文件编码格式:utf-8
     */
    public static void main(String[] args) throws Exception{
        /**
         * 第一部分是以字符流 和 字节流打开该文件,并使用字符流和字节流的处理流进行操作,并没有进行转换
         */
        String filePath = "E:\\hello.txt";
        BufferedReader br = new BufferedReader(new FileReader(filePath));
        System.out.println(br.readLine());//hello world 灏忕帇 -- 这里出现乱码
        br.close();

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
        byte[] b = new byte[1024];
        int len = bis.read(b);
        //将字节数组 转换成String
        System.out.println(new String(b,0,len));//hello world 灏忕帇  -- 这里出现乱码
        bis.close();

        /**
         * 第二部分 使用转换流打开文件,并使用了字节流和字符流的处理流进行操作
         */
        //1.使用转换流:将字节流转换为字符流,并确定使用哪种编码打开,这里使用utf-8,因为hello.txt 是utf-8的
        //2.再用处理流 将字符流包装,以便使用更方便的方法
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
        BufferedReader bufferedReader = new BufferedReader(isr);
        //也可以合并写
        BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8));
        System.out.println(bufferedReader.readLine());//hello world 小王
        System.out.println(bufferedReader2.readLine());//hello world 小王
        //关闭时,只用关闭最外层
        bufferedReader.close();
    }
}
OutputStreamWriter(用于字符流)

[外链图片转存中…(img-LAJNagqx-1649684466543)]

package TransformStream;

import org.junit.jupiter.api.Test;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class Transform02 {
    /**
     * 使用 OutputStream转换流将FileOutputStream字节流转换成字符流,
     * 并以GBK编码格式写到文件中
     * @param args
     */
    public static void main(String[] args) throws Exception {


    }

    @Test
    public void Save_GBK() throws Exception{
        String filePath = "E:\\hello3.txt";
        String charSet = "GBK";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath),charSet);
        osw.write("小明");
        osw.close();
        System.out.println("按照"+charSet+"保存成功");
    }

    @Test
    public void CopyTo_GBK() throws Exception{
        String srcPath = "E:\\hello.txt";
        String destPath = "E:\\hello2.txt";
        String charSet = "GBK";
        //注意这里源文件是什么编码格式,你就用什么编码格式打开
        //如 hello.txt 是 utf-8,则打开时就用utf-8打开,这样再保存成其他编码时,编码格式才会按照你的charSet去实现
        InputStreamReader isr = new InputStreamReader(new FileInputStream(srcPath), StandardCharsets.UTF_8);
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destPath),charSet);

        BufferedReader bufferedReader = new BufferedReader(isr);
        BufferedWriter bufferedWriter = new BufferedWriter(osw);
        String date;
        while ((date = bufferedReader.readLine()) != null){
            bufferedWriter.write(date);
            bufferedWriter.newLine();
        }
        bufferedReader.close();
        bufferedWriter.close();
    }
}
6. 打印流

没有输出流,只有输入流

PrintStream 字节打印流
package PrintStream;

import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.PrintStream;

public class PrintStream01 {
    public static void main(String[] args) throws Exception {
        
    }
    
    @Test
    public void Print() throws Exception {
        PrintStream out = new PrintStream("E:\\a.txt");
        out.println("hello");
        out.write("hello".getBytes());
        out.close();

        /**
         * E:\\a.txt 若不存在则自动创建
         * a.txt 内容:
         * hello
         * hello
         */
    }
    
    @Test
    public void Print2()throws Exception{
        //在默认情况下,PrintStream输出数据的位置为标准输出:即显示器
        PrintStream out = System.out;
        out.println("hello");
        //因为print的底层使用的是write,所以我们可以直接调用write进行打印/输出
        out.write("你好".getBytes());
        out.close();

        //我们可以去修改打印输出的位置/设备
        //setOut 重新分配输出流
        //输出修改到 E:\\f1.txt
        System.setOut(new PrintStream("E:\\f1.txt"));
        System.out.println("hello 小子");
        out.close();

        /**
         * E:\\f1.txt 内容:
         * hello 小子
         */
    }
}

PrintWrite 字符打印流
package PrintStream;

import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

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

    }

    @Test
    public void Print01() throws Exception{
        PrintWriter pw = new PrintWriter(System.out);
        pw.print("hello 小子");     //标准输出,即显示器输出
        pw.close();
    } 

    @Test
    public void Print02() throws FileNotFoundException {
        //追加打印到a.txt
        PrintWriter pw = new PrintWriter(new FileOutputStream("E:\\a.txt",true));
        pw.print("hello 小子");
        pw.close(); //相当于 flush + close 只有close()才能将其打印进去
    }
}
7. Properties 类
//是 Hashtable下的子类,专门用于读写文件配置的集合类

image-20220411210107935

image-20220411210156602

package MyProperties;

import java.io.*;
import java.util.Properties;

public class Properties01 {
    public static void main(String[] args) throws IOException {
        //创建properties对象
        Properties properties = new Properties();
        //加载指定properties文件
        properties.load(new FileReader("E:\\WorkSpace\\JAVA\\Java学习新\\src_Learning\\MyProperties\\MySQL.properties"));
        //把 k-v显示到控制台
        properties.list(System.out);
        /**
         * output:
         *
         * -- listing properties --
         * ip=192.168.100.100
         * pwd=12345
         * user=root
         */

        //当然也可以定向输出到指定文件内,这里我使用了相对路径 输出到文件下的test.properties
        //如果使用list(new PrintWrite(...))  --  字符流 则无法输入到文件
        //而使用字节流则可以
        properties.list(new PrintStream(new FileOutputStream("test.properties")));


        //获取指定key的value
        System.out.println(properties.getProperty("user")); //root

    }
}
package MyProperties;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class Properties02 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();

        //创建键值对
        properties.setProperty("charSet","GBK");
        properties.setProperty("user","小明"); //中文是存储的Unicode码

        //如果key存在则替换value,否则新增k-v
        //因为Properties类 底层是 Hashtable 所以有上面对k-v 新增 或 替换的方式
        properties.setProperty("charSet","utf-8");


        //存入文件,且为追加模式
        //store(Writer,String) 第二个参数为写入文件时的注释,没有则写null
        properties.store(new FileWriter("test.properties",true),null);
        /**
         * #Mon Apr 11 21:25:51 CST 2022
         * charSet=GBK
         * user=СÃ÷       //中文存储 Unicode码
         */

        //注释:This is a comment
        properties.store(new FileWriter("test.properties",true),"This is a comment");
        /**
         * #This is a comment  //多了一行 comment
         * #Mon Apr 11 21:25:51 CST 2022
         * charSet=GBK
         * user=СÃ÷        //中文存储 Unicode码
         */

    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值