JAVA基础相关知识点(一)

文章目录

  • static关键字
  • 代码块
    • 静态代码块
    • 实例代码块
  • 单例模式设计
  • 继承
  • 权限修饰符
  • 方法重写
  • 子类构造器的特点
  • this调用兄弟构造器
  • 多态
  • final
  • 抽象类(特殊的父类)
  • 接口
  • 内部类
    • 成员内部类
    • 静态内部类
    • 局部内部类
    • 匿名内部类(重点,用处较多)
  • 枚举类
  • 包装类
  • 泛型
    • 泛型类
    • 泛型接口
    • 泛型方法
  • 常用API
    • object
    • objects
    • StringBuilder
    • StringBuffer
    • Stringjoiner
    • Math
    • System
    • Runtime
    • BigDecimal
    • 日期时间
      • JDK8之前传统的日期、时间
        • Date和SimpleDateFormat
        • Calendar
      • JDK8开始新增的日期、时间
    • 数组排序
    • JDK8新特性:Lambda表达式
  • 可变参数
  • 集合进阶
    • Collection
      • Collection集合的常用方法
      • Collection的遍历方式
        • 迭代器
        • 增强for
        • lambda表达式
      • Collection工具类
      • list
        • ArrayList和LinkedList区别
      • Set
        • 怎样选择适合的集合??
        • 集合的并发修改异常
    • Map集合
      • Map集合相关方法
      • Map集合的遍历方式
      • 集合的嵌套
  • Stream
    • stream流初体验
    • Stream常见的中间方法
    • Stream常见的终结方法

这里的java基础笔记是从面向对象开始做笔记的,像前面的数据类型这些东西各位自行百度吧,没啥难的

JDK11文档

static关键字

  1. 相当于vue的vuex,被static修饰的属性和方法可以在程序内通过类名.属性/方法访问到(也可以被对象.属性/方法访问到【不推荐】)
  2. static修饰的方法只能访问到static修饰的属性或别的方法,但是非static修饰的方法可以访问到被static修饰的属性和方法
  3. 同一个类中访问static修饰的属性和方法可以不带类名直接访问

代码块

静态代码块

package org.example.demo01;
public class Student {static int number = 80;// 静态代码块执行了static {System.out.println("静态代码块执行了");number += 10;}
}

在test.java里面如下

package org.example.demo01;public class Test {public static void main(String[] args) {// 通过下面的打印也可以触发静态代码块的执行System.out.println(Student.number); // 90}
}
  1. 静态代码块格式为static{}
  2. 静态代码块在类加载时被执行,且只执行一次(即使上述代码多次打印叶志华会执行一次静态代码块里面的代码,适合做类的初始化操作,例如给类变量进行初始化赋值)

实例代码块

  1. 格式为 {}
  2. 每次创建对象时,执行示例代码块,并在构造器前执行
  3. 和构造器一样,都是用来完成对象的初始化赋值的,例如:对实例变量进行初始化赋值

单例模式设计

饿汉式单例

pubilc class A {// 2. 定义一个类变量记住类的一个对象private static A a = new A();// 1. 私有构造器,防止创建对象,只允许通过类名调用返回这个对象,保证一个类只能有一个对象private A(){}// 3. 定义一个类方法返回对象pubilc static A getObject(){return a;}}

单例模式确保了一个类只能有一个对象

好处:例如电脑的任务管理器,无论启动多少次都只有一个窗口,它就是被设计为了单例模式的,避免浪费内存

继承

父类代码如下

package org.example.entends;public class A {public static int num;public void getNum(){System.out.println("访问到了父类的非私有方法");}
}

子类代码如下

package org.example.entends;
public class B extends A{public void getANum(){// 打印父类的非私有成员System.out.println(num);// 调用父类的非私有方法getNum();}
}
  1. 子类不能调用父类私有的成员方法和私有成员变量
  2. 子类创建对象是由子类和父类共同完成的(即子类创建的对象可以调用子类和父类的非私有化的成员方法和成员变量)
  3. java是单继承的,java的类不支持多继承,但是支持多层继承

好处:将多个类可以复用的成员方法和成员变量提取出来作为父组件可以大大减少代码量

权限修饰符

在下面范围内能访问到的权限修饰符

  1. 在本类中:private,缺省,protected,public
  2. 同一个包下其他类里:缺省,protected,pubilc
  3. 任意包下的子类里(父子继承关系):protected,pubilc
  4. 任意包下的任意类里:public

方法重写

什么是方法重写?

  1. 当子类觉得父类的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称,参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写
  2. 注意:重写后,方法的访问,Java会遵循就近原则
  3. 子类重写的父类的方法名要加@Override注解,如下所示:
父类有print()子类继承父类重写print如下所示@Overridepubilc void print(){System.out.println("6666")}
  1. 子类重写的方法权限修饰符必须大于或者等于父类的权限

  2. 静态方法和私有方法不能被重写,否则报错

在子类方法访问其他成员(成员变量,成员方法),是依照就近原则的

  • 先在子类局部范围找
  • 然后子类成员范围找
  • 然后父类成员范围找,如果还没找到就报错

子类构造器的特点

  • 子类的全部构造器都会先调用父类的构造器再调执行自己
  • 默认情况下,子类全部构造器的第一行代码都是super()写不写都有,它会调用父类的无参构造器
  • 如果父类没有无参数构造器,则我们在子类构造器第一行手写super(…),制定调用父类有参数构造器
package org.example.entends;
//  父类
class F{public F(){System.out.println("==父类无参构造器执行了==");}public F(String name){}
}
// 子类
class Z extends F{public Z(){// 在super里面加个参数就会去调用有参构造器就不会对无参构造器发起调用了super("靳");System.out.println("==子类的无参构造器执行了==");}public Z(String name){super("靳");System.out.println("子类的有参数构造器执行了");}
}public class Test {public static void main(String[] args) {Z z = new Z();Z z2 = new Z("萧寂");}
}

this调用兄弟构造器

// 例如有个需求,默认如果学生不填写学校的话,默认为萧寂大学,但是目前有参构造器需要传入三个参数,其中需要传学校字段,那这个怎么做呢???就需要用到现在的调用兄弟构造器了,如下所示public Student(String name, int age){this(name,age,"萧寂大学")
}public Student(String name, int age,String schoolName){this.name = name;this.age = age;this.shoolName = schoolName;
}// 这样的话,创建学生如果只传入两个字段的话,由于方法重载的缘故,会执行第一个有参构造,第一个有参构造通过this关键字再去执行第二个,也就是调用了兄弟构造器

多态

什么是多态

  • 多态是在继承/实现情况下的一种现象,表现为:对象多态,行为多态

具体代码体现

People p1 = new Student();
p1.run()People p2 = new Teacher();
p2.run()如上所示,老师和学生都是属于人,都有跑的这个行为,这就是多态(本质上来说就是相当于继承,子类对父类的方法进行了继承,然后创建子类对象,那么所有的子类对象都会有父类的方法,也可以在每个子类中对方法进行重写)

示例代码

package org.example.duotai;// 父类
class People {public  String name = "父类People的名称";public void run(){System.out.println("人可以跑");}
}// 学生类
class Student extends People{public  String name = "子类Student的名称";// 方法重写@Overridepublic void run() {System.out.println("学生跑得贼快");}
}// 老师类
class Teacher extends People{public  String name = "子类Teacher的名称";// 方法重写@Overridepublic void run() {System.out.println("老师跑得贼快");}
}public class Test {public static void main(String[] args) {// people这个就是多态的象征// 在这里Student和Teacher都是属于People的// 但是p1还是指向Teacher对象,p2还是指向Student对象的People p1 = new Teacher();p1.run();  // Student的rungo(p1); // 将子类对象传递过去 System.out.println(p1.name); // 这里打印的还是父类的namePeople p2 = new Student();p2.run();  // Teacher的rungo(p2); // 将子类对象传递过去System.out.println(p2.name); // 这里打印的还是父类的name// 以上可以总结// 对于子类的方法,编译看左边(父类类型),运行看右边(运行的是子类方法)// 对于子类的变量,编译看左边(父类类型),运行看左边(运行打印的是父类变量)}// 这里可以接收父类类型的参数,接受一切子类对象public static void go(People p){}
}

多态的前提

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

多态注意事项

  • 多态是对象、行为的多态,java中的属性(成员变量)不谈多态

多态的好处

  • 在多态形势下,右边对象是解耦合的,更便于扩展和维护。
  • 定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强,更便利

多态的弊端

  • 因为编译看左边的父类类型,因此子类能调用的方法只能来源于父类或者是子类重写的方法,不能调用子类独有的方法(例如非重写的方法都是子类独有的,如果想使用多态,又想调用自己独有的方法,可以看下面的类型转换的代码)

多态下的类型转换问题

  • 自动类型转换:父类 变量名 = new 子类(); 例如:People p = new Teacher();
  • 强制类型转换: 子类 变量名 = (子类)父类变量 例如:Teacher t = (Teacher)p;

强制类型转换的一个注意事项

  • 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错
  • 运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)错误出来

如下示例代码

package org.example.duotai;// 父类
class People {public  String name = "父类People的名称";public void run(){System.out.println("人可以跑");}
}// 学生类
class Student extends People{public  String name = "子类Student的名称";// 方法重写@Overridepublic void run() {System.out.println("学生跑得贼快");}public String getName(){return name;}
}// 老师类
class Teacher extends People{public  String name = "子类Teacher的名称";// 方法重写@Overridepublic void run() {System.out.println("老师跑得贼快");}
}public class Test {public static void main(String[] args) {// 自动类型转换// 父类 变量名 = new 子类();People p1 = new Teacher();// 强制类型转换// 子类 变量名 = (子类)父类变量People p2 = new Student();Student s1 = (Student) p2;// 这时候转换成学生对象了类型了,就可以调用学生类的独有方法了System.out.println(s1.getName());// 强制类型转换存在的问题,如下Teacher t1 = (Teacher) p2; // 可以看出编译时没报错,但是运行起来就报错了(ClassCastException),因为p2是学生类型,不能转成老师类型}
}

为了解决编译时这种异常,JAVA建议

  • 使用instanceof关键字,判断当前对象的真实类型,再进行强转
p instanceof Student// 如下判断完类型再去强转if(p2 instanceof Student){Student s1 = (Student) p2;}

final

  • final关键字是最终的意思,可以修饰(类,方法,变量)
  • 修饰类:该类为最终类,特点是不能被继承了
  • 修饰方法:该方法被称为最终方法,特点是不能被重写了
  • 修饰变量:该变量只能被赋值一次

final修饰变量的注意

  • final修饰基本类型的变量,变量存储的数据不能被改变
  • final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的

抽象类(特殊的父类)

抽象类注意事项、特点

  • 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
  • 类该有的成员(成员变量,方法,构造器)抽象类都可以有
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
  • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

抽象类的场景和好处

  • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现我们设计这样的抽象类,就是为了
    更好的支持多态。

接口

package org.example.接口;// interface定义接口
interface A {String SCHOOL_NAME = "萧寂大学";// 上面的代码等同于下面这个,可以发现 public static final 是灰色的// 也就是说在接口内部定义的变量就默认是常量,不可被改变//  public static final String SCHOOL_NAME = "萧寂大学";// 成员方法(默认是抽象方法,可以被重写,不能加方法体)void test();// 等同于下面代码 public abstract在接口内部默认添加的// public abstract void test();// 接口内不能有构造器或者静态代码块// 接口不能被创建对象
}class B implements A{@Overridepublic void test() {// 重写A接口的方法}
}class Test {public static void main(String[] args) {// 可以发现这个接口中的变量能被类名.调用到System.out.println(A.SCHOOL_NAME);}
}
  • 接口是通过implements实现的,不是继承来的,实现接口的类称为实现类,也就是接口的子类
  • 修饰符 class 实现类名 implements 接口1,接口2,接口3…
  • 通过上面的格式可以看出一个类可以实现多个接口,实现类实现多个接口必须重写完全部接口的全部抽象方法,否则实现类需要定义为抽象类

案例

package org.example.接口;interface C {void testc1();void testc2();
}interface D {void testd1();void testd2();
}class E implements C, D {@Overridepublic void testc1() {}@Overridepublic void testc2() {}@Overridepublic void testd1() {}@Overridepublic void testd2() {}
}class test {public static void main(String[] args) {E e = new E();e.testd1();}
}

接口的好处

  • 弥补了类单继承的不足,一个类同时可以实现多个接口
  • 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现,代码如下所示
package org.example.接口;interface Driver {void drive();
}interface Singer {void sing();
}class Student {public void getStudent(){System.out.println("我是学生");}
}class E extends Student implements Driver, Singer {@Overridepublic void drive() {// 司机开车System.out.println("司机开车");}@Overridepublic void sing() {// 歌手唱歌System.out.println("歌手唱歌");}
}class test {public static void main(String[] args) {// 由于extends只能单继承,所以一个类只能有一个父类,但是有了接口后,子类可以任意去切换,实现了自由// 希望E是学生类型(不能调用司机和歌手的方法)Student student = new E();student.getStudent();// 希望E是司机类型(不能调用歌手的方法,否则报错)Driver driver = new E();driver.drive();// 希望E是歌手(不能调用司机方法,否则报错)Singer singer = new E();singer.sing();}
}

同理,当多个类实现同一个接口时,创建任意类对象时都会重写这个接口的方法,从而实现可以灵活的更换某个对象,例如AB俩个实现类都实现了C接口,如果new A();调用A.getXXX(),但是某天不需要是A了,需要改成B,那这里直接把new的对象换成B就好了

jdk8之后新增的三种接口的方法

package org.example.接口;interface F {// 1.默认方法,必须使用default修饰,默认会被public修饰,可以省略public// jdk8开始支持的// 默认方法就是实例方法,对象的方法,必须使用实现类对象可以访问到// 好处:例如很多实现类实现了这个接口,某天后需要对接口新增功能,则所有实现类都必须重写接口,维护成本较大,通过定义默认方法,则所有的实现类都会默认可以调用这个方法,不需要一个一个重写了,维护成本低public default void test1(){System.out.println("默认方法");test2(); // 这里可以调用私有方法};// 2.私有方法,必须使用private进行修饰// jdk9开始支持的// 私有方法也是实例方法 - 对象的方法// 由于是私有的,所以应该在当前接口内被调用private void test2(){System.out.println("私有方法");}// 3.静态方法// jdk8之后支持// 必须使用static修饰,默认会被public修饰// 可以通过接口名.调用,如下 F.test3()public static void test3(){System.out.println("静态方法");}
}class G implements F{}class TestE {public static void main(String[] args) {G g = new G();g.test1(); // 调用成员方法F.test3(); // 调用静态方法}
}

接口的多继承

  • 一个接口可以继承多个接口
package org.example.接口;interface H {void test1();
}
interface I {void test2();
}
interface L {
}// 接口的多继承interface K extends H,I,L {
}// 好处
// 如下代码:当项目中接口比较多,就需要一个一个实现比较麻烦
// 当一个接口继承了其它接口的话,直接就可以只实现这一个接口就好
// 实现K接口后,必须重写K继承的所有接口的方法(也是验证了实现K接口就实现了其它接口的目的)
//class M implements H,I,L{};
class M implements K{@Overridepublic void test1() {}@Overridepublic void test2() {}
};

接口其他注意事项(了解)

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

内部类

成员内部类

package org.example;class ChengYuanNeiBuLei {private int age = 99;// 成员内部类public class Inner{private String name;private int age = 88;// jdk16才开始定义静态成员的// public static String schoolname;// 内部类同样有构造器和成员方法public Inner() {}public Inner(String name) {this.name = name;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}public String toString() {return "Inner{name = " + name + "}";}// 了解内部类的访问特点// 在方法内定义age = 77  内部类定义age = 88 外部类定义age = 99// 依次打出77 88 99public void test (){int age = 77;System.out.println(age); // 77// 访问内部类自身的属性System.out.println(this.age); // 88// 访问外部类的属性(外部类名.this.属性)System.out.println(ChengYuanNeiBuLei.this.age); // 99}}
}class Test{public static void main(String[] args) {// 了解成员内部类和其特点// 语法如下// 外部类.内部类 变量名 = 外部类对象.内部类对象ChengYuanNeiBuLei.Inner in = new ChengYuanNeiBuLei().new Inner();in.setName("萧寂");System.out.println(in.getName());// 调用test方法in.test();}
}

静态内部类

package org.example.JingTaiNeiBuLei;class JingTaiNeiBuLei {private int age;public static String schoolName = "萧";// 静态内部类(特点和外部类没啥区别)public static class Inner {private String name;public static int o;public Inner() {}public Inner(String name) {this.name = name;}/*** 获取** @return name*/public String getName() {return name;}/*** 设置** @param name*/public void setName(String name) {this.name = name;}public String toString() {return "Inner{name = " + name + "}";}// 静态内部类访问外部成员方法// 静态内部类可以直接访问外部类的静态成员,不能访问外部类的实例成员public static void test(){System.out.println(schoolName); // 外部schoolName是可以直接访问// System.out.println(age); // 报错,不能被访问,因为age是对象的变量,不是静态变量}}
}class Test {public static void main(String[] args) {// 了解静态内部类// 静态内部类和之前的静态变量和静态方法一样可以被类名.调用JingTaiNeiBuLei.Inner in = new JingTaiNeiBuLei.Inner();in.setName("萧寂");System.out.println(in.getName());// 调用方法in.test();}
}

局部内部类

  • 定义在方法中,代码块中,构造器等执行体中
  • 语法如下(鸡肋语法,看看就好,不做过多描述,开发不常用)
public class Test {public static void main (String[] args) {}public static void  go(){class A{}abstract class B{}interface C{}}
}

匿名内部类(重点,用处较多)

  • 就是一种特殊的局部内部类;所谓匿名;指的就是不需要为这个类声明名字
new 类或接口(参数值...){类体(一般是方法重写);
};

案例代码

package 匿名内部类;abstract class Animail {public void cry(){System.out.println("动物的叫声");}
}class Cat extends Animail{@Overridepublic void cry(){System.out.println("猫的叫声:喵喵喵~~");}
}class Tset{public static void main(String[] args) {// 在开发中,我们使用猫这个对象会使用一下方法Animail cat = new Cat();cat.cry();/*** 但是当我们开发功能比较多的时候,* 我们不可能需要某一模块就专门创建一个类* 因此就有了匿名内部类* 格式如下:*/Animail cat2 = new Animail() {@Overridepublic void cry() {System.out.println("猫的叫声:喵喵喵~~");}};cat2.cry();// 上面代码new Animail() {}相当于创建了Animail的子类对象并继承了Animail,会去重写Animail里面的抽象方法,并去执行}
}

匿名内部类大多数是作为参数传递到方法,在方法内部完成一系列操作的

匿名内部类可以方便快速的创建出子类对象

枚举类

  • 一种特殊的类

枚举类的格式:

修饰符 enum 枚举类名{名称1,名称2,...;其他成员...;
}
  • 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
  • 枚举类的构造器都是私有的(写不写都只能是私有的),因此,举类对外不能创建对象。
  • 枚举都是最终类,不可以被继承。
  • 枚举类中,从第二行开始,可以定义类的其他各种成员。编译器为枚举类新增了几个方法,并且枚举类都是继承:iava.lang.Enum类的,从enum类也会继承到一些方法
package org.example.枚举类;enum A {// 注意:枚举类的第一行必须罗列的是枚举对象的名字X,Y,Z;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}class test{public static void main(String[] args) {A a1 = A.X;System.out.println(a1);// 1.枚举类的构造器都是私有的,不能对外创建对象//  A a = new A();// 2.枚举类的第一行都是常量,记住的是枚举类的对象A a2 = A.Y;// 3.枚举类提供了一些额外的APIA[] as = A.values(); // 拿到全部对象A a3 = A.valueOf("X"); // 根据常量名得到对象System.out.println(a3.name()); // 拿到名字XSystem.out.println(a3.ordinal()); // 拿到索引}
}

抽象枚举

package org.example.枚举类;public enum B {// 这里的属于匿名内部类类似的写法// 本质调用的是无参构造器// 传入参数的话就是调用有参构造器// 内部重写的还是抽象方法(快捷键打个 方法名 回车)X(){@Overridepublic void go(){System.out.println("X的内容");}},Y("萧寂"){@Overridepublic void go() {System.out.println(getName()+"在跑~~");}};private String name;// 以下的构造器,成员方法都是正常的// 提功有参构造器必须同时声明无参构造器,否则上面的X,Y声明报错B(){}B(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}// 定义抽象方法public abstract void go();
}class Test{public static void main(String[] args) {B y = B.Y;// 调用y对象的go方法y.go();B x = B.X;// 调用X对象的go方法x.go();}
}

包装类

基本数据类型对应的包装类(引用数据类型)
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

示例代码

package org.example.包装类;import java.util.ArrayList;public class A {
}class Test{public static void main(String[] args) {// 掌握包装类的使用// 将12转换成对象,打印出了对象的值Integer a = Integer.valueOf(12);System.out.println(a);// 自动装箱(自动把基本数据类型转换成对象)Integer a1 = 24;System.out.println(a1);// 自动拆箱(自动把包装类型的对象转换成基本数据类型)int a2 = a1;System.out.println(a2);// 泛型和集合只支持引用数据类型,不支持基本数据类型// ArrayList<int> list = new ArrayList<>(); // 报错ArrayList<Integer> list = new ArrayList<>();list.add(12); // 自动装箱list.add(13); // 自动装箱int rs = list.get(1); // 自动拆箱System.out.println(rs);}
}

包装类的其他常见操作

  • 可以把基本类型的数据转换成字符串类型.
  • 可以把字符串类型的数值转换成数值本身对应的数据类型

示例代码

        // 1. 把基本类型的数据转换成字符串类型.Integer i = 23;String rs1 = Integer.toString(i);System.out.println(rs1 + "1"); // 231,23转成了字符串再做拼接String rs2 = i.toString();System.out.println(rs2 + "1"); // 打印结果同上String rs3 = i + "";System.out.println(rs3 + "1"); // 打印结果同上System.out.println("=====================");// 2. 把字符串类型的数值转换成数值本身对应的数据类型String ageStr = "29";// int age =  Integer.parseInt(ageStr); // 29// 下面这个和上面这个有相同效果并且支持int和double类型int age =  Integer.valueOf(ageStr); // 29System.out.println(age+1); // 30String scoreStr = "99.5";// double score =  Double.parseDouble(scoreStr); // 29double score =  Double.valueOf(scoreStr); // 29System.out.println(score+0.5); // 30

泛型

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

格式:

ArrayList<String> arrayList = new ArrayList<>();
String是约束类型,否则集合内任意类型都可以被加入,造成数据混乱

作用:

  • 泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常

泛型本质就是把具体的数据类型作为参数传给类型变量

泛型类

泛型类定义格式

修饰符 class 类名<类型变量,类型变量,...> {}public class Student<E> {}

自定义封装一个泛型类

package org.example.泛型;// 建议都使用大写的EDKV这种定义
// 1.简单定义泛型
class MyArrayList<E>{public boolean add(E e){return true;}public E get(int index){return null;}}
// 2.定义多个泛型的类型
class MyArrayList2<E,T>{public boolean add(E e,T t){return true;}
}// 3.使用继承关系的泛型
// 定义父类
class Animail{}
// 子类继承
class Dog extends Animail{}
class Cat extends Animail{}
// 使用继承关系的泛型类型
class MyArrayList3<E extends Animail>{public boolean add(E e){return true;}
}class Test {public static void main(String[] args) {// 掌握泛型类的定义和使用// 1.自定义泛型的定义和使用MyArrayList<String> list = new MyArrayList<>();list.add("aaa");// list.add(123); // 报错(参数类型错误)// 2.使用多个类型约束的泛型MyArrayList2<String,Number> list2 = new MyArrayList2<>();list2.add("aaa",111);// list2.add("aaa"); // 报错(参数不够)// list2.add(123); // 报错(参数不够,且第一个类型为string)// list2.add(111,"aaa") // 报错,参数类型错误// 3.使用继承关系的泛型// 则下面的泛型类型必须是Animail本身或者是继承了Animail的子类类型,否则报错MyArrayList3<Cat> list3 = new MyArrayList3<>();Cat cat = new Cat();list3.add(cat); }
}

泛型接口

泛型接口的定义格式

修饰符 interface 接口名<类型变量,类型变量,...> {}public interface A<E,D,K,L,...> {}

示例代码

package org.example.泛型;import java.util.ArrayList;interface Data<T> {void add(T t);ArrayList<T> getByName(String name);
}// 定义学生和老师类
class Student{}
class Teacher{}// 老师实现类
class TeacherData implements Data<Teacher>{@Overridepublic void add(Teacher teacher) {}@Overridepublic ArrayList<Teacher> getByName(String name) {return null;}
}
// 学生实现类
class StudentData implements Data<Student>{@Overridepublic void add(Student student) {}@Overridepublic ArrayList<Student> getByName(String name) {return null;}
}class Test1{public static void main(String[] args) {// 泛型接口的定义和使用// 场景:系统需要处理学生和老师的数据,需要提供两个功能,保存对象数据,根据名称查询数据}
}

泛型方法

简单的泛型方法使用

package org.example.泛型;class C {
}class Test3{public static void main(String[] args) {// 掌握泛型方法的定义和使用System.out.println(test("java"));System.out.println(test(123));System.out.println(test(true));}public static <T> T test(T t){return t;}
}

示例代码

package org.example.泛型;import java.util.ArrayList;// 定义汽车父类
class Car {
}// 定义宝马车
class BMW extends Car{}
// 定义奔驰车
class BENZ extends Car{}class Test3{public static void main(String[] args) {// 需求:所有的汽车可以一起参加比赛// 创建arrayListArrayList<Car> cars = new ArrayList<>();cars.add(new BENZ());cars.add(new BMW());go(cars);ArrayList<BMW> bmws = new ArrayList<>();bmws.add(new BMW());bmws.add(new BMW());go(bmws);ArrayList<BENZ> benzs = new ArrayList<>();benzs.add(new BENZ());benzs.add(new BENZ());go(benzs);}// 限定了所有的类型都是继承或者直接为Car的,否则就报错进不来// public static <T extends Car> void go(ArrayList<T> cars){}// 下面这个也不报错,在ArrayList里面?类型代表一切类型,这样相当于不加类型,任何类型都可以进来了// public static void go(ArrayList<?> cars){}// 下面这个也可以实现同等效果,将ArrayList的?类型的范围缩小了,只限制于Car及其子类public static void go(ArrayList<? extends Car> cars){}
}

注意事项:

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件就不存在泛型了,这就是泛型擦除
  • 泛型是不支持基本数据类型的,只能支持对象类型(引用数据类型)

常用API

object

  • object类是java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用object类中提供的一些方法

常见的:toString() 、equals()

toString()和equals()方法需要在类里面写完成员变量后再重写一下,equals重写会有弹出框一直无脑下一步就好,否则打印或者比较的都是地址值

由于Object的APi太多了,各位可以查询api文档或者询问GPT怎么使用

这里贴一下JDK11的在线文档,打开就是object的所有方法

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

objects

  • equals接收两个参数为两个对象,与object自带的equals相比,当相比较的数据其中一个为null时,传统的euqals会直接抛出异常,而objects的equals会返回false代表不相等
  • isNull 比较对象是否为null
  • nonNull 比较对象不为null

StringBuilder

  • StringBuilder代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的,。
  • 好处:StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁。

示例代码

package org.example.StringBuilder;
class Test{public static void main(String[] args) {// 搞清楚StringBuilder的用法和作用StringBuilder s = new StringBuilder("Ji");// 1.拼接内容(向后追加)s.append(12);s.append("萧寂");s.append(true);System.out.println(s);// 支持链式编程s.append("哈哈哈").append("我").append("爱你");// 打印System.out.println(s);// 2.翻转s.reverse();System.out.println(s);// 3. 返回字符串长度System.out.println(s.length());// 4. StringBuilder对象又转成String类型String rs =  s.toString();System.out.println(rs);}
}

为什么操作字符串建议使用StringBuilder,而不是原来学过的String

package org.example.StringBuilder;public class Test1 {public static void main(String[] args) {// 掌握StringBuilder的好处.// 需求: 要拼接100万次abc.// String rs = "";// for (int i = 0; i < 1000000; i++) {//     rs= rs +"abc";// }// // 可以发现等待时间会很长,底层要不断地去计算// System.out.println(rs);// 使用StringBuilder演示StringBuilder sb = new StringBuilder();for (int i = 0; i < 1000000; i++) {sb.append("abc");}// 可以发现几乎1-2秒就出来了结果,非常快System.out.println(sb);}
}
  • 对于字符串的操作,如频繁的拼接、修改等,建议用StringBuilder,效率更高!
  • 注意:如果操作字符串较少,或者不需要操作,以及定义字符串变量,还是建议用String

StringBuffer

StringBuffer与StringBuilder

  • StringBuffer的用法与StringBuilder是一模一样的
  • 但 StringBuilder是线程不安全的 StringBuffer是线程安全的

Stringjoiner

  • JDK8开始才有的,跟StringBuilder一样,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的。
  • 好处:不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
package org.example.StringJoiner;import java.util.StringJoiner;public class Test {public static void main(String[] args) {// 掌握Stringjoiner的使用// 默认携带三个参数// 参数一:间隔符号// 参数二:开始符号// 参数三:结尾符号// 参数二和参数三可以省略(因为是起始符号因此会一起省略或者一起出现)StringJoiner s = new StringJoiner(", ","[","]");s.add("java1");s.add("java2");s.add("java3");s.add("java4");System.out.println(s); // [java1, java2, java3, java4]}
}

Math

  • 代表数学,是一个工具类,里面提供的都是对数据进行操作的一些静态方法
package org.example.Math;public class Test {public static void main(String[] args) {// 绝对值System.out.println(Math.abs(-12)); // 12System.out.println(Math.abs(123)); // 123System.out.println(Math.abs(-3.14)); // 3.14// 向上取整System.out.println(Math.ceil(4.12345667)); // 5.0System.out.println(Math.ceil(4.0));  // 4.0// 向下取整System.out.println(Math.floor(4.999999)); // 4.0System.out.println(Math.floor(4.0)); // 4.0// 四舍五入System.out.println(Math.round(3.4999)); // 3System.out.println(Math.round(3.50001)); // 4// 取最大值System.out.println(Math.max(10,20)); // 20// 取最小值System.out.println(Math.min(10,20)); //10// 取次方System.out.println(Math.pow(2,3)); // 2的3次方 8.0System.out.println(Math.pow(3,2)); // 3的2次方 9.0// 取随机数System.out.println(Math.random()); // [0.0,1.0)范围内任意随机数}
}

System

  • System代表程序所在的系统,也是一个工具类。
package org.example.System;public class Test {public static void main(String[] args) {// System常见两个方法// 终止当前运行的Java虚拟机// 该参数用作状态代码;按照惯例,非零状态代码表示异常终止// System.exit(0); // 人为的终止虚拟机// System.out.println("123"); // 无打印,虚拟机直接终止了// 获取当前系统的时间(毫秒值)long time = System.currentTimeMillis();System.out.println(time);}
}

Runtime

  • 代表程序所在的运行环境。
  • Runtime是一个单例类。
package org.example.Runtime;import java.io.IOException;public class Test {public static void main(String[] args) throws IOException, InterruptedException {// 了解Runtime常见方法// 获取与当前Java应用程序关联的运行时对象Runtime r = Runtime.getRuntime();// 终止当前运行的Java虚拟机// 该参数用作状态代码;按照惯例,非零状态代码表示异常终止// r.exit(0);// System.out.println(11); // 关闭虚拟机后打印不出来//获取虚拟机能够使用的处理器数System.out.println(r.availableProcessors()); // 本人电脑是20// 获取Java虚拟机中的内存总量System.out.println(r.totalMemory()/1024.0/1024.0+"MB"); // 1024 = 1K 1024*1024 = 1M// 返回Java虚拟机中的可用内存量System.out.println(r.freeMemory()/1024.0/1024.0+"MB");// 启动某个应用程序,并返回代表该程序的对象Process p = r.exec("D:\\ruanjian\\FSCapture\\FSCapture.exe");Thread.sleep(5000); // 让程序在这里停止5秒继续往下走p.destroy(); // 销毁,关闭程序}
}

BigDecimal

  • 用于解决浮点型运算时,出现结果失真的问题。
  • 浮点型运算时,直接加减乘除可能会出现运算结果失真
package org.example.BigDecimal;import java.beans.beancontext.BeanContext;
import java.math.BigDecimal;public class Test {public static void main(String[] args) {// 解决小数运算失真的问题double a = 0.1;double b = 0.2;System.out.println(a+b); // 0.30000000000000004// BigDecimal有俩类型,Double和String类型// 开发常用String类型的,不推荐使用Double// 1.把他们变成字符串封装成BigDecimal对象来运算// BigDecimal a1 = new BigDecimal(Double.toString(a));// BigDecimal b1 = new BigDecimal(Double.toString(b));// 下面这两行代码等同于上面这两行(推荐用以下方式:把小数转换成字符串再得到BigDecimal对象来使用(更简洁))BigDecimal a1 = BigDecimal.valueOf(a);BigDecimal b1 = BigDecimal.valueOf(b);// a1 + b1 加BigDecimal c1 = a1.add(b1);System.out.println(c1); // 0.3// a1 - b1 减BigDecimal d1 = a1.subtract(b1);System.out.println(d1); // 0.1// a1 * b1 乘BigDecimal e1 = a1.multiply(b1);System.out.println(e1); // 0.02// a1 / b1 除// 案例一这个除法有问题,如 1 / 3 是一个无限小数,程序不知道保留几位就报错了BigDecimal G1 = BigDecimal.valueOf(1);BigDecimal J1 = BigDecimal.valueOf(3);// BigDecimal f1 = G1.divide(J1);// System.out.println(f1); // 报错// 除法建议使用以下方法,在参数2这里,明确要保留几位,这样就不会有问题// 当然还有参数三,可以指定是否使用四舍五入等方式(这里不细分,各位可以去百度)BigDecimal f1 = G1.divide(J1,2);System.out.println(f1);// 将上面的结果BigDecimal类型的转成double类型,这里以除法结果为例double rs = f1.doubleValue();System.out.println(rs);}
}

日期时间

JDK8之前传统的日期、时间

Date和SimpleDateFormat
  • 代表日期和时间以及时间的格式化
package org.example.Date;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class Test {public static void main(String[] args) throws ParseException {// 创建一个Data的对象,代表系统当前时间信息的Date d = new Date();System.out.println(d); // Sun Apr 07 21:30:53 CST 2024// 得到时间毫秒值long time = d.getTime();System.out.println(time); // 1712496653452// 把时间毫秒值转换为日期对象:2s之后的时间是多少time+=2*1000;Date d2 = new Date(time);System.out.println(d2); // Sun Apr 07 21:30:55 CST 2024// 把日期对象的时间通过setTime的方法进行修改Date d3 = new Date();d3.setTime(time);System.out.println(d3); // Sun Apr 07 21:30:55 CST 2024// 将时间日期格式化SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a");System.out.println(sdf.format(time)); // 2024-04-07 21:30:55 周日 下午// 将字符串解析成日期对象String dataStr = "2022-12-12 12:12:11";// 创建简单日期格式化对象,指定的时间格式化格式必须与被解析的时间格式一模一样,否则程序会出bugSimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date da2 = sdf2.parse(dataStr);System.out.println(da2); // Mon Dec 12 12:12:11 CST 2022}
}
Calendar
  • 代表的是系统此刻时间对应的日历。
  • 通过它可以单独获取、修改时间中的年、月、日、时、分、秒等
package org.example.Calendar;import java.util.Calendar;
import java.util.Date;public class Test {public static void main(String[] args) {// 1. 得到系统此刻时间对应的日历对象Calendar now = Calendar.getInstance();System.out.println(now); // java.util.GregorianCalendar[time=1712497323373,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2024,MONTH=3,WEEK_OF_YEAR=15,WEEK_OF_MONTH=2,DAY_OF_MONTH=7,DAY_OF_YEAR=98,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=42,SECOND=3,MILLISECOND=373,ZONE_OFFSET=28800000,DST_OFFSET=0]// 2.获取日历中的某个信息System.out.println(now.get(Calendar.YEAR)); // 2024System.out.println(now.get(Calendar.DAY_OF_YEAR)); // 一年中的第几天// 3.拿到日历中记录的日期对象Date d = now.getTime();System.out.println(d);// 4.拿到时间毫秒值System.out.println(now.getTimeInMillis());// 5.修改日历中的某个信息now.set(Calendar.MONTH,9); // 修改月份为10月份now.set(Calendar.DAY_OF_YEAR,125); // 修改一年中的第几天System.out.println(now);// 6.为某个信息增加或者减少多少now.add(Calendar.DAY_OF_YEAR,100);now.add(Calendar.DAY_OF_YEAR,-10); // 天数增加或者减少now.add(Calendar.DAY_OF_MONTH,-10);System.out.println(now);}
}

JDK8开始新增的日期、时间

  • LocalDate:年,月,日,星期
package org.example.JDK8之后新增的时间日期;
import java.time.LocalDate;
public class Test {public static void main(String[] args) {// 获取本地日期对象LocalDate ld = LocalDate.now(); // 年月日System.out.println(ld); // 2024-04-09// 获取日期对象中的信息System.out.println(ld.getYear()); // 年System.out.println(ld.getMonthValue()); // 月(1-12)System.out.println(ld.getDayOfMonth()); // 日System.out.println(ld.getDayOfYear()); // 一年中第几天System.out.println(ld.getDayOfWeek().getValue()); // 星期几// 直接修改某个信息LocalDate ld2 = ld.withYear(2099); // 返回值为新日期对象,并不会修改原来日期对象System.out.println(ld2); // 2099-04-09System.out.println(ld); // 2024-04-09System.out.println(ld.withMonth(2)); // 修改月份 2024-02-09// 把某个信息加多少System.out.println(ld.plusYears(2)); // 当前基础加2年System.out.println(ld.plusMonths(2)); // 当前基础加两个月// 把某个信息减多少System.out.println(ld.minusYears(2)); // 当前基础减2年System.out.println(ld.minusMonths(2)); // 当前基础减两个月// 获取指定日期的LocalDate对象LocalDate ld3 = LocalDate.of(2099,12,12);LocalDate ld4 = LocalDate.of(2099,12,12);System.out.println(ld3);// 判断2个日期对象是否相等,在前还是后System.out.println(ld3.equals(ld4)); // true 相等System.out.println(ld3.isAfter(ld4)); // false ld3不在ld4后面System.out.println(ld3.isBefore(ld4)); // false ld3也不在ld4前面}
}
  • LocalTime:时,分,秒,纳秒
package org.example.JDK8之后新增的时间日期;import java.time.LocalTime;public class Time {public static void main(String[] args) {// 获取本地时间对象LocalTime lt = LocalTime.now(); // 时分秒纳秒 不可变的System.out.println(lt); // 21:58:58.652642900// 获取时间中的信息System.out.println(lt.getHour()); // 时System.out.println(lt.getMinute()); // 分System.out.println(lt.getSecond()); // 秒System.out.println(lt.getNano()); // 纳秒// 修改时间System.out.println(lt.withHour(10));System.out.println(lt.withMinute(10));System.out.println(lt.withSecond(10));System.out.println(lt.withNano(10));// 加多少System.out.println(lt.plusHours(10));System.out.println(lt.plusMinutes(10));System.out.println(lt.plusSeconds(10));System.out.println(lt.plusNanos(10));// 减多少System.out.println(lt.minusHours(10));System.out.println(lt.minusMinutes(10));System.out.println(lt.minusSeconds(10));System.out.println(lt.minusNanos(10));// 获取指定时间的对象LocalTime lt2 = LocalTime.of(12,12,12);LocalTime lt3 = LocalTime.of(12,12,12);// 判断两个时间对象 是否相等 在前还在在后System.out.println(lt2.equals(lt3)); // true 相等System.out.println(lt2.isAfter(lt3));// false lt2不在lt3后面System.out.println(lt2.isBefore(lt3));// false lt2不在lt3前面}
}
  • LocalDateTime:年,月,日,星期,时,分,秒,纳秒(本地日期)
package org.example.JDK8之后新增的时间日期;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;public class LocalDateTimeTest {public static void main(String[] args) {LocalDateTime ldt = LocalDateTime.now(); // 年月日时分秒 纳秒System.out.println(ldt);// 可以获取日期和时间的全部信息System.out.println(ldt.getYear()); // 年System.out.println((ldt.getMonthValue())); // 月System.out.println(ldt.getDayOfMonth());// 日System.out.println(ldt.getDayOfYear()); // 一年中的第几天System.out.println(ldt.getDayOfWeek().getValue()); // 周几System.out.println(ldt.getHour());// 时System.out.println(ldt.getMinute()); // 分System.out.println(ldt.getSecond()); // 秒System.out.println(ldt.getNano()); // 纳秒// 修改时间信息System.out.println(ldt.withYear(2029));System.out.println(ldt.withMinute(59));// 加多少System.out.println(ldt.plusYears(2));System.out.println(ldt.plusMinutes(3));// 减多少System.out.println(ldt.minusYears(2));System.out.println(ldt.minusMinutes(3));// 获取指定日期和时间LocalDataTime对象LocalDateTime ldt2 = LocalDateTime.of(2029,12,12,12,12,12,2001);LocalDateTime ldt3 = LocalDateTime.of(2029,12,12,12,12,12,2001);// 判断2个日期,时间对象,是否相等,在前还是后System.out.println(ldt2.equals(ldt3)); // trueSystem.out.println(ldt2.isAfter(ldt3)); // false ld3不在ld2后面System.out.println(ldt2.isBefore(ldt3)); // false ld3不在ld2前面// 把LocalDataTime转换成LocalDate和LocalTimeLocalDate ld = ldt.toLocalDate();LocalTime lt = ldt.toLocalTime();System.out.println(ld);System.out.println(lt);}
}
  • ZoneId:时区
  • ZonedDateTime:带时区的时间
package org.example.时区;import java.time.Clock;
import java.time.ZoneId;
import java.time.ZonedDateTime;public class Test {public static void main(String[] args) {// 获取系统默认时区ZoneId zoneid = ZoneId.systemDefault();System.out.println(zoneid.getId()); // Asia/ShanghaiSystem.out.println(zoneid); // Asia/Shanghai// 获取Java支持的全部时区IDSystem.out.println(ZoneId.getAvailableZoneIds());// 把某个时区Id封装成ZoneId对象ZoneId zoneId1 = ZoneId.of("America/New_York");// 带时区的时间(当前时区的时间)ZonedDateTime now = ZonedDateTime.now(zoneId1);System.out.println(now); // 2024-04-09T11:13:39.566356-04:00[America/New_York]// 获取世界标准时间(北京时间 = 世界标准时间 + 8小时)System.out.println(ZonedDateTime.now(Clock.systemUTC()));// 获取系统默认时区的ZoneDateTime对象ZonedDateTime now2 = ZonedDateTime.now();System.out.println(now2);// 可以通过上面的now2.各种调用时间的方法,与上面的几个时间方法使用一致,这里不再演示}
}
  • Instant:时间戳/时间线
package org.example.Instant;import java.time.Instant;public class Test {public static void main(String[] args) {// 创建Instant的对象,获取此刻时间信息Instant now = Instant.now();System.out.println(now);// 获取总秒数(从1970年到现在)long second = now.getEpochSecond();System.out.println(second);// 不够一秒的纳秒数int nano = now.getNano();System.out.println(nano);// 下面的不演示了,也是跟上面一样的操作时间的方法,通过now.调用,也是关于时间的增减判断之类的// 使用场景// 用于记录用户某个操作的时间点Instant now1 = Instant.now();// 代码执行....Instant now2 = Instant.now();}
}
  • DateTimeFormatter:用于时间的格式化和解析
package org.example.DateTimeFormatter;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;public class DateTimeFormatterTest {public static void main(String[] args) {// SimpleDateFormat 线程不安全// 代替SimpleDateFormat 线程安全// 1. 创建一个日期时间格式化器对象出来DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");// 2. 对时间进行格式化LocalDateTime now = LocalDateTime.now();System.out.println(now); // 获取到当前时间对象String rs = formatter.format(now);System.out.println(rs); // 得到格式化后的时间// 第二种方案System.out.println(now.format(formatter)); // 结果和上面一样(反向格式化)// 解析时间(一般使用LocalDateTime提供的解析方法来解析)String dateStr = "2029年12月12日 12:12:11";System.out.println(LocalDateTime.parse(dateStr, formatter)); // 解析到的日期对象}
}
  • Period:时间间隔(年,月,日)
package org.example.Period;import java.time.LocalDate;
import java.time.Period;public class PeriodTest {public static void main(String[] args) {// 创建Period对象,再封装两个日期对象// 开始时间LocalDate start = LocalDate.of(2029,8,10);// 结束时间LocalDate end = LocalDate.of(2029,8,15);Period period = Period.between(start,end);// 间隔多少年System.out.println(period.getYears());// 间隔多少月System.out.println(period.getMonths());// 间隔多少天System.out.println(period.getDays());}
}
  • Duration:时间间隔(时,分,秒,纳秒) 可以用于计算两个时间对象相差的天数、小时数、分数、秒数、纳秒数;支持LocalTime、LocalDateTime、Instant等时间。
package org.example.Duration;import java.time.Duration;
import java.time.LocalDateTime;public class DurationTest {public static void main(String[] args) {// 开始时间LocalDateTime start = LocalDateTime.of(2025,11,11,11,10,10);// 结束时间LocalDateTime end = LocalDateTime.of(2025,11,11,11,11,11);// 1. 得Duration对象Duration duration = Duration.between(start,end);// 获取两个时间对象间隔的信息// 间隔多少天System.out.println(duration.toDays());// 间隔多少小时System.out.println(duration.toHours());// 间隔多少分System.out.println(duration.toMinutes());// 间隔多少秒System.out.println(duration.toSeconds());// 间隔多少毫秒System.out.println(duration.toMillis());// 间隔多少纳秒System.out.println(duration.toNanos());}
}

数组排序

package org.example;import java.util.Arrays;
import java.util.Comparator;class Student {private int age;private double height;public Student(int age, double height) {this.age = age;this.height = height;}public Student() {}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic String toString() {return "Student{" +"age=" + age +", height=" + height +'}';}
}class Test{public static void main(String[] args) {// 定义数组Student[] student = new Student[4];student[0] = new Student(19,172.5);student[1] = new Student(17,173.6);student[2] = new Student(22,165.3);student[3] = new Student(13,155.8);// 根据年龄进行排序// 参数一:需要排序的数组// 参数二:比较器对象(用来指定比较器的比较规则)Arrays.sort(student, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {// 制定比较规则了:左边对象o1 右边对象 o2// 约定1:认为左边对象大于右边对象请您返回正整数// 约定2:认为左边对象小于右边对象请您返回负整数// 约定3:认为左边对象等于右边对象请您一定返回0// 根据年龄升序排序// return o1.getAge()-o2.getAge();// 根据年龄降序排序// return o2.getAge()-o1.getAge();// 小数类型排序处理(因为返回的是int类型,小数不能直接强转成int进行排序,不然数据不准确了)// 传统的就是通过if判断// o1.getHeight()>o2.getHeight() 返回1 等这种方法去返回1 -1 0 但是比较麻烦// double类型提供一种方法可以直接返回0 1 -1// 如下所示// return Double.compare(o1.getHeight(),o2.getHeight()); // 根据身高升序排序return Double.compare(o2.getHeight(),o1.getHeight()); // 根据身高降序排序}});System.out.println(Arrays.toString(student));// 直接数字(小数)的排序double[] prices = {99.8,128,100};Arrays.sort(prices); // 默认升序排序System.out.println(Arrays.toString(prices)); // [99.8, 100.0, 128.0]// 手动反转数组顺序实现降序for (int i = 0; i < prices.length / 2; i++) {double temp = prices[i];prices[i] = prices[prices.length - 1 - i];prices[prices.length - 1 - i] = temp;}System.out.println(Arrays.toString(prices)); // 输出:[128.0, 100.0, 99.8]}
}

JDK8新特性:Lambda表达式

  • Lambda表达式是JDK8开始新增的一种语法形式;作用:用于简化名内部类的代码写法
(被重写方法的形参列表)->{被重写方法的方法体代码。
}

Lambda表达式的省略写法(进一步简化Lambda表达式的写法)

  • 参数类型可以省略不写,
  • 如果只有一个参数,参数类型可以省略,同时()也可以省略。
  • 如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写,
package org.example.LambdaTest1;class LambdaTest1 {public static void main(String[] args) {// 认识lambda表达式(下面这个是传统写法)// Animal a = new Animal(){//     @Override//     public void run() {//         System.out.println("狗跑得贼快");//     }// };// a.run();// lambda表达式是匿名内部类简化版// 注意:Lambda表达式并不是说能简化全部匿名内部类的写法,只能简化函数式接口的匿名内部类。// 函数式接口: 是接口并且接口里面只能有一个抽象方法// 注意:将来我们见到的大部分函数式接口,上面都可能会有一个@Functionalnterface的注解,有该注解的接口就必定是函数式接口。// 下面这个是错误示范:非接口// Animal a = ()->{//// }// 这个下面是正确写法// 有参数的需要留下参数,和前端的箭头函数差不多Swimming s = ()->{System.out.println("我今天去游泳了");};s.swim();}
}interface Swimming{void swim();
}abstract class Animal{public abstract void run();
}

可变参数

package org.example.可变参数;import java.util.Arrays;public class Test {public static void main(String[] args) {// 可变参数test(); // 不报错test(10); // 传入一个数据test(10,20,30); // 传入多个数据test(new int[]{10,20,30,40}); // 传入数组}// 下面参数格式为int...nums 或者int... nums这样多个空格也可以// 可变参数本质就是数组且一个形参列表中,只能有一个可变参数// 可变参数必须放在形参列表的最后面public static void test(int...nums){System.out.println(nums.length); // 这里打印的是参数长度System.out.println(Arrays.toString(nums)); // 这里打印的是数据}
}

集合进阶

  • Collection(单列集合,每个元素(数据)只会包含一个值)

Collection

Collection集合的常用方法

  • Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。因此可以理解为会使用Collection的方法则其他单列集合也会有这些方法,也是同样的使用方法的

方法如下

  • 1.public boolean add(Ee):添加元素,添加成功返回true.
  • 2.public void clear():清空集合的元素
  • 3.public boolean isEmpty():判断集合是否为空 是空返回true,反之
  • 4.public int size():获取集合的大小。
  • 5.public boolean contains(object obj):判断集合中是否包含某个元素。
  • 6.public boolean remove(Ee):删除某个元素:如果有多个重复元素默认删除前面的第一个!
  • 7.public 0bject[] toArray():把集合转换成数组
package org.example.Collection集合;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;public class Test2 {public static void main(String[] args) {Collection<String> c = new ArrayList<>(); // 多态写法// 1.public boolean add(Ee):添加元素,添加成功返回true.c.add("java1");c.add("java1");c.add("java2");c.add("java3");c.add("java3");System.out.println(c); // [java1, java1, java2, java3, java3]// 2.public void clear():清空集合的元素// c.clear();// System.out.println(c); // []// 3.public boolean isEmpty():判断集合是否为空 是空返回true,反之。System.out.println(c.isEmpty()); // false// 4.public int size():获取集合的大小。System.out.println(c.size()); // 5// 5.public boolean contains(object obj):判断集合中是否包含某个元素。System.out.println(c.contains("java1")); // trueSystem.out.println(c.contains("Java1")); // false// 6.public boolean remove(Ee):删除某个元素:如果有多个重复元素默认删除前面的第一个c.remove("java1");System.out.println(c); // [java1, java2, java3, java3]// 7.public 0bject[] toArray():把集合转换成数组Object[] arr = c.toArray();System.out.println(Arrays.toString(arr)); // [java1, java2, java3, java3]// 上边默认的是object类型数组,如果我们只想转换成String类型数组怎么操作呢??如下String[] arr2 = c.toArray(new String[c.size()]);System.out.println(Arrays.toString(arr2)); // [java1, java2, java3, java3]// 把一个集合的全部数据倒入另一个集合中去Collection<String> c1 = new ArrayList<>();c1.add("java1");c1.add("java2");c1.add("java3");c1.add("java4");Collection<String> c2 = new ArrayList<>();c2.add("java5");c2.add("java6");c2.add("java7");c2.add("java8");c1.addAll(c2);System.out.println(c1); // [java1, java2, java3, java4, java5, java6, java7, java8]System.out.println(c2); // [java5, java6, java7, java8]}
}

Collection的遍历方式

迭代器
  • 迭代器是用来遍历集合的专用方式(数组没有迭代器),在java中迭代器的代表是Iterator。
package org.example.Collection集合;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;public class Test3 {public static void main(String[] args) {// 迭代器// 创建集合Collection<String> c= new ArrayList<>();c.add("aaa");c.add("bbb");c.add("ccc");c.add("ddd");c.add("eee");// 获取到迭代器对象Iterator<String> it = c.iterator();// System.out.println(it.next()); // aaa// System.out.println(it.next()); // bbb// System.out.println(it.next()); // ccc// System.out.println(it.next()); // ddd// 使用循环结合迭代器遍历集合// it.hasNext()如果有下一个返回true,否则为falsewhile (it.hasNext()){String ele = it.next();System.out.println(ele);}}
}
增强for

for(元素的数据类型 变量名:数组或者集合){}

package org.example.Collection集合;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;public class Test4 {public static void main(String[] args) {// 迭代器// 创建集合Collection<String> c= new ArrayList<>();c.add("aaa");c.add("bbb");c.add("ccc");c.add("ddd");c.add("eee");// 使用增强for循环for(String ele:c){System.out.println(ele);}System.out.println("----------------------");String[] names = {"java","js","node"};for (String name : names) {System.out.println(name);}}
}
lambda表达式
  • 得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合
package org.example.Collection集合;import java.util.ArrayList;
import java.util.Collection;public class Test5 {public static void main(String[] args) {// lambda表达式// 创建集合Collection<String> c= new ArrayList<>();c.add("aaa");c.add("bbb");c.add("ccc");c.add("ddd");c.add("eee");c.forEach(s->{System.out.println(s);});}
}

Collection不支持for循环,因为无索引

Collection工具类

  • Collections是一个用来操作集合的工具类
package org.example.Collection集合;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;class Student{private String name;private int age;private double height;public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}public Student() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", height=" + height +'}';}
}public class Test6 {public static void main(String[] args) {// 工具类Collections// 创建集合List<String> list= new ArrayList<>();// 为集合批量添加数据Collections.addAll(list,"张三","李四","王五","麻子");System.out.println(list); // [张三, 李四, 王五, 麻子]// 打乱list集合中的元素顺序(因为只有list集合是有序地,所以这个方法只支持list集合)Collections.shuffle(list);System.out.println(list); // 每次运行随机打乱顺序// 对list集合中的元素进行升序排序// list为int类型时默认升序排序List<Integer> list1 = new ArrayList<>();Collections.addAll(list1,2,3,10,1);Collections.sort(list1);System.out.println(list1);// 学生对象排序规则制定List<Student> students = new ArrayList<>();students.add(new Student("蜘蛛精",23,169.7));students.add(new Student("紫霞",22,169.8));students.add(new Student("至尊宝",26,165.5));students.add(new Student("牛魔王",22,183.5));// Collections.sort(students); // 报错,因为不是int类型的,程序不知道排序方法Collections.sort(students, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {// 按照身高排序(升序)// return Double.compare(o1.getHeight(),o2.getHeight());// 按照年龄排序(升序)return o1.getAge()-o2.getAge();}});System.out.println(students);}
}

list

  • List系列集合:添加的元素是有序、可重复、有索引。
  • ArrayList、LinekdList:有序、可重复、有索引。
  • List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了,因此list也能用Collection的所有方法
package org.example.list集合;import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {List<String> list = new ArrayList<>(); // 是一行经典代码,ArrayList用的比较多,又因为是list实现类,因此后续想更换集合只需要换个名,不需要更改逻辑和方法list.add("java");list.add("java1");list.add("jav2");list.add("java3");System.out.println(list); // [java, java1, jav2, java3]// 在某个索引位置插入元素list.add(2,"萧寂");System.out.println(list); // [java, java1, 萧寂, jav2, java3]// 根据索引删除元素,返回被删除元素System.out.println(list.remove(2)); // 萧寂System.out.println(list); // [java, java1, jav2, java3]// 返回集合中指定位置的元素System.out.println(list.get(3)); // java3// 修改索引位置处的元素,修改成功后,会返回原来的数据System.out.println(list.set(3, "萧")); // java3System.out.println(list); // [java, java1, jav2, 萧]}
}

List支持的遍历方式

  • for循环(因为有索引)
  • 迭代器
  • 增强for循环
  • Lambda表达式
package org.example.list集合;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class Test1 {public static void main(String[] args) {List<String> list = new ArrayList<>(); // 是一行经典代码,ArrayList用的比较多,又因为是list实现类,因此后续想更换集合只需要换个名,不需要更改逻辑和方法list.add("java");list.add("java1");list.add("jav2");list.add("java3");System.out.println(list); // [java, java1, jav2, java3]// for循环for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}// 迭代器Iterator<String> it = list.iterator();while (it.hasNext()){System.out.println(it.next());}// 增强for循环for (String s : list) {System.out.println(s);}// lambda表达式list.forEach(s->{System.out.println(s);});}
}
ArrayList和LinkedList区别

ArrayList:

  • ArrayList:有序,可重复,有索引。
  • 基于数组实现的
  • 查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同,
  • 删除效率低:可能需要把后面很多的数据进行前移。
  • 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。

LinkedList:

  • LinkedList:有序,可重复,有索引。
  • 基于双链表实现的。
  • 查询慢, 无论查询哪个数据都要从头开始找,
  • 链表增删相对快(但对首尾元素进行增删改查的速度是极快的。LinkedList新增了:很多首尾操作的特有方法。)
  • 只是在首尾增删元素,用LinkedList来实现很合适!
addFirst 在该列表开头插入指定的元素
addLast 将指定的元素追加到此列表的末尾
getFirst 返回此列表中的第一个元素
getLast 返回此列表中的最后一个元素
removeFirst 从此列表中删除并返回第一个元素
removeLast 从此列表中删除并返回最后一个元素

使用LinkenList设计队列(先进先出,后进后出)和栈(先进后出,后进先出)

package org.example.list集合;import java.util.LinkedList;public class Test3 {public static void main(String[] args) {// LinkedList 双链表 先进先出 有序// 设计队列(先进先出,后进后出)// 创建一个队列LinkedList<String> queue = new LinkedList<>();// 入队queue.addLast("第1"); // 从尾部添加元素queue.addLast("第2");queue.addLast("第3");queue.addLast("第4");System.out.println(queue); // [第1, 第2, 第3, 第4]// 出队System.out.println(queue.removeFirst()); // 第1System.out.println(queue.removeFirst()); // 第2System.out.println(queue); // [第3, 第4]System.out.println("-------------");// 设计栈(先进后出,后进先出),可以理解为弹匣LinkedList<String> stack = new LinkedList<>();// 入栈stack.addFirst("第一颗子弹");stack.addFirst("第二颗子弹");stack.addFirst("第三颗子弹");stack.addFirst("第四颗子弹");System.out.println(stack); // [第四颗子弹, 第三颗子弹, 第二颗子弹, 第一颗子弹]// 出栈System.out.println(stack.removeFirst()); // 第四颗子弹System.out.println(stack.removeFirst()); // 第三颗子弹System.out.println(stack); // [第二颗子弹, 第一颗子弹]}
}

ArrayList和LinkedList底层采用的数据结构不同,应用场景不同

Set

  • Map(双列集合,每个元素包含两个值(键值对))
  • 无序:添加数据的顺序和获取出的数据顺序不一致;不重复;无索引;
  • Hashset: 无序、不重复、无索引,增删改查比较快;
  • LinkedHashset:有序、不重复、无索引,增删改查比较快。
  • Treeset:按照大小默认升序排序、不重复、无索引,增删改查比较快。
package org.example.list集合;import java.util.*;public class Test4 {public static void main(String[] args) {// 创建一个set集合的对象//  Set<Integer> set = new HashSet<>(); // 无序不重复无索引 无序是运行一次后这个顺序就定下来了,不是每次运行都是随机的//  Set<Integer> set = new LinkedHashSet<>(); // 有序,不重复,无索引Set<Integer> set = new TreeSet<>(); // 可排序(默认升序),不重复,无索引,括号内重写new Comparator<String>可以自定义排序规则set.add(111);set.add(222);set.add(222);set.add(222);set.add(333);set.add(444);set.add(333);System.out.println(set); // [444, 333, 222, 111]}
}

两种集合的特点

package org.example.Collection集合;import java.util.ArrayList;
import java.util.HashSet;public class Test {public static void main(String[] args) {// 确认ArrayList集合特点ArrayList<String> list = new ArrayList<>();list.add("java1");list.add("java2");list.add("java3");list.add("java3");System.out.println(list); // [java1, java2, java3, java3] 有序有索引,可重复// HashSet集合特点HashSet<String> set = new HashSet<>();set.add("java1");set.add("java2");set.add("java3");set.add("java3");System.out.println(set); // [java3, java2, java1] 无序无索引不可重复}
}
怎样选择适合的集合??
1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?LinkedList集合(有序、可重复、有索引),底层基于双链表实现的3.如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。(常用)4.如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?LinkedHashSet集合(有序,不重复,无索引),底层基于哈希表和双链表。5.如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?Treeset集合,基于红黑树实现。
集合的并发修改异常
  • 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
  • 解决方法参考这个视频吧,笔记写不清楚

Map集合

  • Map集合称为双列集合,格式:{key1=value1,key2=value2,key3=value3,…,一次需要存一对数据做为一个元素
  • Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合’
  • Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值
  • 需要存储-一对应的数据时,就可以考虑使用Map集合来做

集合体系

  • TreeMap<k,v>
  • hashMap<k,v>
  • LinkedHashMap<k,v>

Map集合体系的特点
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求

  • HashMap(由键决定特点):无序、不重复、无索引;(用的最多)
  • LinkedHashMap (由键决定特点):由键决定的特点:有序、不重复、无索引。
  • TreeMap (由键决定特点):按照大小默认升序排序、不重复、无索引。
package org.example.Map;import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;public class Test {public static void main(String[] args) {// Map<String,Integer> map = new HashMap<>(); // 经典代码,按照键 无序 不重复 无索引Map<String,Integer> map = new LinkedHashMap<>(); //按照键 有序 不重复 无索引map.put("手表",100);map.put("手表",220); // 这个重复的键后面的会覆盖前面的map.put("手机",2000);map.put("java",80);System.out.println(map); // {手表=220, 手机=2000, java=80}Map<Integer,String> map1 = new TreeMap<>(); // 有序,默认升序排序map1.put(23,"java");map1.put(25,"mysql");map1.put(10,"js");map1.put(18,"html");System.out.println(map1); // {10=js, 18=html, 23=java, 25=mysql}}
}

Map集合相关方法

Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的

package org.example.Map;import java.util.*;public class Test1 {public static void main(String[] args) {// map集合常用方法Map<String,Integer> map = new LinkedHashMap<>();map.put("手表",100);map.put("手机",2000);map.put("java",80);map.put(null,null);System.out.println(map); // {手表=100, 手机=2000, java=80, null=null}// 1. 获取集合大小System.out.println(map.size()); // 4// 2. 清空集合// map.clear();// System.out.println(map); // {}// 3. 判断集合是否为空,为空返回true,反之false;System.out.println(map.isEmpty()); // false// 4. 根据键获取值System.out.println(map.get("手表")); // 100// 5. 根据键删除某个数据System.out.println(map.remove("手表")); // 100System.out.println(map); // {手机=2000, java=80, null=null}// 6. 判断是否包含某个键System.out.println(map.containsKey("手表")); // false// 7. 判断是否包含某个值System.out.println(map.containsValue(100)); // false// 8. 获取map集合的全部键Set<String> keys = map.keySet();System.out.println(keys); // [手机, java, null]// 9. 获取map集合的全部值Collection<Integer> values = map.values();System.out.println(values); // [2000, 80, null]// 10. 把其他map集合的数据倒入到自己集合中来Map<String,Integer> map2 = new LinkedHashMap<>(); //按照键 有序 不重复 无索引map2.put("手表2",100);map2.put("手机2",2000);Map<String,Integer> map3 = new LinkedHashMap<>(); //按照键 有序 不重复 无索引map3.put("手表3",100);map3.put("手机3",2000);map2.putAll(map3); // 如果两个集合有重复的键还是会被覆盖的System.out.println(map2); // {手表2=100, 手机2=2000, 手表3=100, 手机3=2000}System.out.println(map3); // {手表3=100, 手机3=2000}}
}

Map集合的遍历方式

  • 键找值

    • 先获取Map集合全部的键,再通过遍历键来找值

    • package org.example.Map;import java.util.Collection;
      import java.util.LinkedHashMap;
      import java.util.Map;
      import java.util.Set;public class Test2 {public static void main(String[] args) {// map集合常用方法Map<String,Double> map = new LinkedHashMap<>();map.put("手表",100.03);map.put("手机",2000.52);map.put("java",80.79);System.out.println(map); // {手表=100.03, 手机=2000.52, java=80.79}// 键找值方式遍历Set<String> keys = map.keySet();System.out.println(keys); // [手表, 手机, java]// 遍历全部的键,根据键获取对应的值for (String key : keys) {double value = map.get(key);System.out.println(key+"---"+value);}}
      }
      
  • 键值对

    • 把“键值对“看成一个整体进行遍历(难度较大)

    • package org.example.Map;import java.util.LinkedHashMap;
      import java.util.Map;
      import java.util.Set;// 键值对方式遍历
      public class Test3 {public static void main(String[] args) {// map集合常用方法Map<String,Double> map = new LinkedHashMap<>();map.put("手表",100.03);map.put("手机",2000.52);map.put("java",80.79);System.out.println(map); // {手表=100.03, 手机=2000.52, java=80.79}// 使用增强for循环的方法// 1、调用Map集合提供entrySet方法,把Map集合转换成键值对类型的Set集合Set<Map.Entry<String, Double>> entries = map.entrySet();System.out.println(entries); // [手表=100.03, 手机=2000.52, java=80.79]for (Map.Entry<String, Double> entry : entries) {String key = entry.getKey(); // 获取当前循环的值的键double value = entry.getValue();System.out.println(key+"----"+value); // 数据}}
      }
      
  • Lambda

    • JDK1.8开始之后的新技术(非常的简单)

    • package org.example.Map;import java.util.LinkedHashMap;
      import java.util.Map;
      import java.util.Set;// Lambda表达式方式遍历
      public class Test4 {public static void main(String[] args) {// map集合常用方法Map<String,Double> map = new LinkedHashMap<>();map.put("手表",100.03);map.put("手机",2000.52);map.put("java",80.79);System.out.println(map); // {手表=100.03, 手机=2000.52, java=80.79}map.forEach((k,v)->{System.out.println(k+"----"+v);});}
      }
      

集合的嵌套

需求
要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息

  • 江苏省=南京市,扬州市,苏州市,无锡市,常州市
  • 湖北省=武汉市,孝感市,十堰市,宜昌市,鄂州市
  • 河北省=石家庄市,唐山市,邢台市,保定市,张家口市
    分析

定义一个Map集合,键用表示省份名称,值表示城市名称,注意:城市会有多个。

package org.example.Map;import java.util.*;public class Test5 {public static void main(String[] args) {// - 江苏省=南京市,扬州市,苏州市,无锡市,常州市// - 湖北省=武汉市,孝感市,十堰市,宜昌市,鄂州市// - 河北省=石家庄市,唐山市,邢台市,保定市,张家口市// 省份可以无序,但是省份下面的城市是有序的Map<String, List<String>> map = new HashMap<>();// 创建list集合代表城市信息List<String> city1 = new ArrayList<>();Collections.addAll(city1,"南京市","扬州市","苏州市","无锡市","常州市");map.put("江苏省",city1);List<String> city2 = new ArrayList<>();Collections.addAll(city2,"武汉市","孝感市","十堰市","宜昌市","鄂州市");map.put("湖北省",city2);List<String> city3 = new ArrayList<>();Collections.addAll(city3,"石家庄市","唐山市","邢台市","保定市","张家口市");map.put("河北省",city3);System.out.println(map);List<String> cities = map.get("湖北省");for (String city : cities) {System.out.println(city);}map.forEach((a,b)->{System.out.println(a+"-------"+b);});}
}

Stream

  • 也叫Stream流,是Jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据。
  • 也叫Stream流,是Jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据。

stream流初体验

package org.example.Stream;import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;public class Test {public static void main(String[] args) {List<String> names = new ArrayList<>();Collections.addAll(names,"张三丰","张无忌","周芷若","赵敏","张强");System.out.println(names);// 找出姓张,且是三个字的名字,存入到一个新的集合中去// 使用原来方法实现List<String> list = new ArrayList<>();for (String name : names) {if(name.startsWith("张")&&name.length()==3){list.add(name);}}System.out.println(list);// 使用stream流实现List<String> list2 = names.stream().filter(s -> {return s.startsWith("张");}).filter(a -> {return a.length() == 3;}).collect(Collectors.toList());System.out.println(list2);}
}

集合和数组获取Stream的方式

package org.example.Stream;import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class Test1 {public static void main(String[] args) {// 获取list集合的stream流List<String> names = new ArrayList<>();Collections.addAll(names,"张三丰","张无忌","周芷若","赵敏","张强");// 流的获取Stream<String> stream = names.stream();// 获取set集合的stream流Set<String> set = new HashSet<>();Collections.addAll(set,"刘德华","张曼玉","蜘蛛精","玛德","德玛西亚");// 流的获取Stream<String> stream1 = set.stream();stream1.filter(s->s.contains("德")).forEach(s->{System.out.println(s);});// 获取Map集合的stream流Map<String,Double> map = new HashMap<>();map.put("古力娜扎",172.3);map.put("迪丽热巴",168.3);map.put("玛尔扎科",166.3);map.put("卡尔扎巴",168.3);Set<String> strings = map.keySet();// 流的获取Stream<String> stream2 = strings.stream();// 获取到map所有的值Collection<Double> values = map.values();Stream<Double> stream3 = values.stream(); // map值流,可以对map集合的值做处理Set<Map.Entry<String, Double>> entries = map.entrySet();Stream<Map.Entry<String, Double>> stream4 = entries.stream(); // map的每一项的数据流,对每一项数据整体做处理(键值都包含了)// 返回键中包含"巴"的数据stream4.filter(e->{return e.getKey().contains("巴");}).forEach(s->{System.out.println(s);});// 获取数组的stream流(两种方式)String[] names2 = {"张翠山","东方不败","唐大山","孤独求败"};// 流的获取Stream<String> stream5 = Arrays.stream(names2);Stream<String> stream6 = Stream.of(names2);}
}

Stream常见的中间方法

package org.example.Stream;import java.util.*;
import java.util.stream.Stream;class Student{private String name;private int age;private double height;public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}public Student() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", height=" + height +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && Double.compare(height, student.height) == 0 && Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age, height);}
}public class Test2 {public static void main(String[] args) {List<Double> scores = new ArrayList<>();Collections.addAll(scores,88.5,100.0,60.0,99.0,9.5,99.6,25.0);// 需求1,找出成绩大于等于60分的数据,并升序后,再输出\// sorted默认升序scores.stream().filter(s->{return s>=60;}).sorted().forEach(s->{System.out.println(s);});System.out.println("-------------------------");List<Student> students = new ArrayList<>();students.add(new Student("蜘蛛精", 23, 169.7));students.add(new Student("蜘蛛精", 23, 169.7));students.add(new Student("紫霞", 22, 169.8));students.add(new Student("至尊宝", 26, 165.5));students.add(new Student("牛魔王", 22, 183.5));// 需求2:找出年龄大于等于23,且年龄小于等于20岁的学生,并按照年龄降序输出students.stream().filter(s->{return s.getAge()>=23&&s.getAge()<=30;}).sorted((s1,s2)->{return s1.getAge()-s2.getAge();}).forEach(s->{System.out.println(s);});System.out.println("-------------------------");// 需求3:取出身高最高的前三名学生,并输出// limit得到排序后的前几位students.stream().sorted((s1,s2)->{return Double.compare(s2.getHeight(),s1.getHeight());}).limit(3).forEach(s->{System.out.println(s);});System.out.println("-------------------------");// 需求4:取出身高倒数的2名学生,并输出// skip 跳过前几个students.stream().sorted((s1,s2)->{return Double.compare(s2.getHeight(),s1.getHeight());}).skip(students.size()-2).forEach(s->{System.out.println(s);});System.out.println("-------------------------");// 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出// distinct去重students.stream().filter(s->{return s.getHeight()>168;}).map(s->{return s.getName();}).distinct().forEach(s->{System.out.println(s);});System.out.println("------");// 希望内容一样就去除重复,重写(hashCode,equals)// 如下代码: 先不重写student的hashCode和equals可以发现并没有被真的去重,当重写完后会发现才是真的去重了students.stream().filter(s->{return s.getHeight()>168;}).distinct().forEach(s->{System.out.println(s);});System.out.println("---------------------------");// 将两个流合并为一个流Stream<String> st1 = Stream.of("张三", "李四");Stream<String> st2 = Stream.of("王五", "麻子","哈哈");Stream<String> concat = Stream.concat(st1, st2);concat.forEach(s->{System.out.println(s);});}
}

Stream常见的终结方法

  • 终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
  • 收集Stream流 : 就是把Stream流操作后的结果转回到集合或者数组中去返回
  • Stream流:方便操作集合/数组的手段;集合/数组:才是开发中的目的
package org.example.Stream;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;class Student{private String name;private int age;private double height;public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}public Student() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", height=" + height +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && Double.compare(height, student.height) == 0 && Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age, height);}
}public class Test3 {public static void main(String[] args) {List<Student> students = new ArrayList<>();students.add(new Student("蜘蛛精", 23, 169.7));students.add(new Student("蜘蛛精", 23, 169.7));students.add(new Student("紫霞", 22, 169.8));students.add(new Student("至尊宝", 26, 165.5));students.add(new Student("牛魔王", 22, 183.5));students.add(new Student("铁扇公主", 20, 173.5));// 需求1:请计算出身高超过168的学生有几人// count统计数量System.out.println(students.stream().filter(s -> {return s.getHeight() > 168;}).count());// 需求2:请找出身高最高的学生对象,并输出// get:获取到中间方法返回的数据并直接返回// max:获取到身高最高的学生对象并返回System.out.println(students.stream().max((o1, o2) -> {return Double.compare(o1.getHeight(), o2.getHeight());}).get());// 需求3:请找出身高最矮的学生对象,并输出// min:获取到身高最矮的学生对象并返回System.out.println(students.stream().min((o1, o2) -> {return Double.compare(o1.getHeight(), o2.getHeight());}).get());// 需求4:请找出身高超过170的学生对象,并放到一个新集合中去并返回// collect:收集流,注意流只能被收集一次System.out.println(students.stream().filter(s -> {return s.getHeight() > 170;}).collect(Collectors.toList())); // 放到list集合中System.out.println(students.stream().filter(s -> {return s.getHeight() > 170;}).collect(Collectors.toSet())); // 放到Set集合中(如果碰到重复的会去重)// 需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高存入到一个map集合返回// 去下重再收集// toMap里面参数1是键,参数2是值System.out.println(students.stream().filter(a -> {return a.getHeight() > 170;}).distinct().collect(Collectors.toMap(a -> a.getName(), a -> a.getHeight())));}
}

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

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

相关文章

【Maven】Maven模块划分:为什么和如何进行模块化设计

在现代软件开发过程中&#xff0c;模块化是一个核心概念&#xff0c;它帮助开发者将复杂的系统分解成更小、更易管理的部分。Maven&#xff0c;作为一个项目管理和构建自动化工具&#xff0c;提供了强大的支持来实现项目的模块化。本文将深入探讨为什么要在Maven中划分模块以及…

自定义类似微信效果Preference

1. 为自定义Preference 添加背景&#xff1a;custom_preference_background.xml <?xml version"1.0" encoding"utf-8"?> <selector xmlns:android"http://schemas.android.com/apk/res/android"><item><shape android:s…

LRTimelapse for Mac:专业延时摄影视频制作利器

LRTimelapse for Mac是一款专为Mac用户设计的延时摄影视频制作软件&#xff0c;它以其出色的性能和丰富的功能&#xff0c;成为摄影爱好者和专业摄影师的得力助手。 LRTimelapse for Mac v6.5.4中文激活版下载 这款软件提供了直观易用的界面&#xff0c;用户可以轻松上手&#…

【从浅学到熟知Linux】进程控制下篇=>进程程序替换与简易Shell实现(含替换原理、execve、execvp等接口详解)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 进程程序替换什么是程序替换及其原理替换函数execlexeclpexecleexecvexecvpexecvpeexecve 替换函数总结实现…

spring - tx 事务的使用(事务的传播行为是啥)

补充&#xff1a;事务的传播行为是啥 事务的传播行为&#xff1a;指的是&#xff0c;当一个事务方法被另一个事务方法调用时&#xff0c;这个被调用的事务方法应该如何进行&#xff0c; 简单来说&#xff0c;它决定了事务方法是在调用者的事务中运行&#xff0c;还是为自己开启…

宝剑锋从磨砺出,透视雀巢咖啡品牌焕新与产品升级的想象力

自1989年进入中国市场以来&#xff0c;陪伴着国内咖啡行业由启蒙期走向兴盛期的雀巢咖啡&#xff0c;始终坚持以消费者高品质、个性化需求为本位&#xff0c;在保有独特性的基础上持续创新&#xff0c;实现了从无到有的攻克与突破。 近日&#xff0c;深耕中国三十六载的雀巢咖…

康耐视visionpro-CogCreateLinePerpendicularTool操作操作工具详细说明

CogCreateLinePerpendicularTool]功能说明&#xff1a; 创建点到线的垂线 CogCreateLinePerpendicularTool操作说明&#xff1a; ①.打开工具栏&#xff0c;双击或点击扇标拖拽添加CogCreateLinePerpendicularTool ②.添加输入源&#xff1a;右键“链接到”或以连线拖 拽的方式…

Java代码基础算法练习-圆的面积-2024.04.17

任务描述&#xff1a; 已知半径r&#xff0c;求一个圆的面积(保留两位小数)&#xff0c;其中 0 < r < 5&#xff0c;PI 3.14&#xff0c;圆面积公式: PI * r * r 任务要求&#xff1a; 代码示例&#xff1a; package April_2024;import java.util.Scanner;// 已知半径…

【前端】1. HTML【万字长文】

HTML 基础 HTML 结构 认识 HTML 标签 HTML 代码是由 “标签” 构成的. 形如: <body>hello</body>标签名 (body) 放到 < > 中大部分标签成对出现. <body> 为开始标签, </body> 为结束标签.少数标签只有开始标签, 称为 “单标签”.开始标签和…

Linux-时间同步服务器

1. (问答题) 一.配置server主机要求如下&#xff1a; 1.server主机的主机名称为 ntp_server.example.com 编写脚本文件 #!/bin/bash hostnamectl hostname ntp_server.example.com cd /etc/NetworkManager/system-connections/ rm -fr * cat > eth0.nmconnection <&…

每日三个JAVA经典面试题(三十六)

1.垃圾回收器如何工作&#xff1f;它是如何影响应用性能的&#xff1f; 垃圾回收器&#xff08;Garbage Collector&#xff0c;GC&#xff09;是Java虚拟机&#xff08;JVM&#xff09;中的一部分&#xff0c;负责自动管理堆内存中的对象生命周期&#xff0c;回收不再被引用的…

【编译原理】02词法分析(1)

接上篇 &#xff1a;【编译原理】01引论 词法分析是编译过程中将字符流转换成为符号流的一个工作阶段&#xff0c;是编译的第一步工作&#xff0c;其后续工作是语法分析。 词法分析的输入是源代码&#xff1b; 词法分析的输出是符号流&#xff1b; 词法分析需要识别词法错误&am…

STM32 软件I2C方式读取MT6701磁编码器获取角度例程

STM32 软件I2C方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》&#x1f33f;《Arduino通过I2C驱动MT6701磁编码器并读取角度数据》&#x1f530;MT6701芯片和AS5600从软件读取对比&#xff0c;只是读取的寄存器和…

Swift错误处理

错误与异常的概念&#xff1a; 在Swift中&#xff0c;错误&#xff08;error&#xff09;表示程序可能遇到的意外情况&#xff0c;这些情况可能导致程序无法按预期的方式继续执行。错误通常表示由于外部因素或不正确的代码导致的问题。异常是一种特殊类型的错误&#xff0c;表…

js在当前浏览器中获取当前操作系统平台代码 js函数

在浏览器中获取当前操作系统平台 windows , mac , Unix, linux, other 代码 //获取当前浏览器操作系统平台 function getCurrentSystem() {var operatingInfo navigator.userAgent;var isWin (navigator.platform "Win32") || (navigator.platform "Windo…

代码随想录算法训练营第56天| 583. 两个字符串的删除操作|72. 编辑距离|编辑距离总结篇

代码随想录算法训练营第56天| 583. 两个字符串的删除操作|72. 编辑距离|编辑距离总结篇 详细布置 583. 两个字符串的删除操作 本题和动态规划&#xff1a;115.不同的子序列 相比&#xff0c;其实就是两个字符串都可以删除了&#xff0c;情况虽说复杂一些&#xff0c;但整体思…

【Redis 神秘大陆】009 案例实践进阶

九、案例实践&进阶方案 9.1 本地缓存组件选型 使用缓存组件时需要重点关注集群方式、集群、缓存命中率。 需要关注集群组建方式、缓存统计&#xff1b;还需要考虑缓存开发语言对缓存的影响&#xff0c;如对于JAVA开发的缓存需要考虑GC的影响&#xff1b;最后还要特别关注…

SQL优化——核心概念

文章目录 1、基数(数据分布)2、选择性3、直方图&#xff08;HISTOGRAM&#xff09;4、回表&#xff08;TABLE ACCESS BY INDEX ROWID&#xff09;5、集群因子&#xff08;CLUSTERING FACTOR&#xff09;6、表与表之间关系 1、基数(数据分布) 某个列唯一键&#xff08;Distinct…

springboot整合dubbo实现RPC服务远程调用

一、dubbo简介 1.什么是dubbo Apache Dubbo是一款微服务开发框架&#xff0c;他提供了RPC通信与微服务治理两大关键能力。有着远程发现与通信的能力&#xff0c;可以实现服务注册、负载均衡、流量调度等服务治理诉求。 2.dubbo基本工作原理 Contaniner:容器Provider&#xf…

[AI]-(第0期):认知深度学习

深度学习是一种人工智能&#xff08;AI&#xff09;方法&#xff0c;用于教计算机以受人脑启发的方式处理数据。 深度学习模型可以识别图片、文本、声音和其他数据中的复杂模式&#xff0c;从而生成准确的见解和预测。 您可以使用深度学习方法自动执行通常需要人工智能完成的…