黑马程序员Java基础学习,涉及精细知识点复习【持续更新】

文章目录

01java基础

java基础

1.变量:

概念:内存中的存储空间,空间中存储的数据是可以发送改变的。

定义: 数据类型 变量名 = 变量值;

使用:输出、修改

2.变量的注意事项:

  • 变量名不能重复
  • (局部)变量未赋值,不能使用
  • 定义long类型数量,数据后面加L
  • 定义float类型变量,数据后面加F

3.关键字:

含义:被java语言赋予了特定含义的单词

特点:全部小写;常用的代码编辑器中有特殊颜色标记

4.标识符:

含义:给类、方法、变量等起名字的符合

组成规则:由数字、字母、下划线和$组成

注意事项:不能以数字开头、不能是关键字、区分大小写

常见命名规范:

  • 见名之意
  • 类:大驼峰命名法
  • 方法、变量:小驼峰命名法

5.运算符:

含义:对字面量或者变量进行操作的符号

表达式:用运算符把字面量或者变量连接起来符合java语法的式子就可以成为表达式。不同运算符连接的表达式体现的是不同类型的表达式。

6.char

  • 单独打印,展示的是字符本身
  • 参与运算,展示的是字符在ASCII码表中对应的整数相加的结果
System.out.println("A");  //A
System.out.println("A"+1);  //66

7.扩展的赋值运算符底层隐含了强调类型转换

short s = 1; //1是int(大->小,应该是强转,在这也没有强转,是因为java中有常量优化机制 )
 // s+=2;  扩展的赋值运算符,隐含了强转
//s = s+2报错
s = (short)(s+2);


/*常量优化机制:在编译时,如果常量在左边类型的范围内,编译通过,反之编译失败(报错)
byte b = 1; 
byte b2 = 128;报错,因为 byte [-128,127]
*/

8.Debug:

供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以用于追踪程序执行过程来调试程序。

9.跳转控制语句:

  • continue:用在循环中,基于条件控制,跳过某次循环体内容的执行,继续下一次的执行。
  • break:用在循环中,基于条件控制,终止循环体内容的执行,也就是说结束当前的整个循环。

10.方法:

  • 定义:完成特定功能的代码块
  • 好处:提高代码的复用性
  • 注意事项:方法不能嵌套定义; void表示无返回值,可以省略return,也可以单独的书写return,后面不加数据。
  • return的作用:返回结果;结束方法

11.方法重载:

  • 定义同一个类中,方法名相同,参数列表不同(顺序、类型、数量)
  • 好处:让调用者不用记忆太多的方法名;调用方法时,JVM会根据参数列表匹配对应的方法。

面向对象

1.类:
  • 类是对现实生活中一类具有共同属性和行为的事物的抽象。
  • 类是对象的抽象,对象是类的实体。
  • 对象的属性:对象具有的各种特征,每个对象的每个属性拥有特定的值。
  • 对象的行为:对象能够执行的操作。
2.成员变量:类中方法外的变量,不能赋值

成员变量有默认初始值,局部变量没有默认初始值

3.成员方法:

不加static关键字

4.java内存分配:

在这里插入图片描述

  • java程序在运行时,需要在内存中分配空间。为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
  • 栈:所有局部变量都会在栈内存中创建;方法执行都会加载到栈中进行。
    • 方法进栈,局部变量跟随方法进栈
  • 堆:所有对象及其对应的实例变量和数组都将存储在此处(new 出来的东西)。
    • new出来的东西进堆
5.成员变量有初始值,局部变量没有初始值。
6.this关键字:
  • this限定的变量用于指代成员变量
    • 方法的形参如果与变量同名,不带this修饰的变量指的是形参,而不是成员变量
    • 解决局部变量和成员变量重名问题
    • 方法被哪个对象调用,this就代表哪个对象
    • this代表本来对象,可以调用本类中的成员
7.封装:
  • 概述:
    • 是面向对象三大特征之一
    • 是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部,外界是无法直接操作的
  • 原则:
    • 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
    • 成员变量 private ,提供对应的getXxx()/setXxx() 方法
  • 好处:
    • 通过方法来控制成员变量的操作,提高了代码的安全性
    • 把代码用方法进行封装,提高了代码的复用性
8.构造方法:
  • 概述:
    • 一种特殊的方法;用于创建对象;完成对象数据的初始化
  • 注意事项:
    • 方法名与类名保持一致
    • 每new一次对象,自动执行一次空参构造
    • 带参构造的本质,就是在创建对象的同时,完成赋值
    • 两种构造都提供,java允许方法重载
    • 如果没有定义构造方法,系统将给出一个默认的无参数构造方法;如果定义了构造方法,系统将不再提供默认的构造方法;如果自定义了带参构造方法,还要使用无参数构造方法,就必须在写一个无参数构造方法。
9.标准的JavaBean组成
  • 成员变量:必须私有
  • 构造方法:提供两种(空参、有参)
  • 成员方法:必须提供get和set方法,其他方法根据题目提供
10.JDK帮助文档使用流程:
  • 打开帮助文档
  • 找到索引项,输入要学习的类,然后回车
  • 看类所在的包:java.lang 包下的类在使用的时候不需要导包
  • 看类的描述:类是干什么的
  • 看类的构造方法:创建对象使用
  • 看类的成员方法:完成功能使用

GUI

1.java.awt包:

Abstract Window Toolkit(抽象窗口工具包),需要调用本地系统方法实现功能,属重量级控件。

2.javax.swing包:
  • 在awt的基础上,建立的一套图形界面系统,提供了更多的组件,而且完全由java实现。增强了移植性,属轻量级控件。
  • 组件 :是具有图形表示的对象,该图形表示可以显示在屏幕上并且可以与用户交互。
3.GUI组件:

在这里插入图片描述

4.事件监听机制
  • 事件源:事件发生的地方。可以是按钮、窗体、图片等
  • 事件:发生了什么事情。例如:鼠标点击事件,键盘按下事件等
  • 事件绑定:把事件绑定到事件源上,当发生了某个事件,则触发对应的处理逻辑
    • 事件源对象.addXXXListener(事件);
5.“”创建的字符串对象,在字符串常量池存储,如果内容一样的字符串,本质上是同一个对象。
6.基本类型包装类
  • 将基本数据类型封装成对象的好处就是可以通过对象调用方法操作数据
  • 常用的操作之一:用于基本数据类型与字符串之间的转换
7.比较字符串相等

str == null 、 str.isEmpty()、str ==“”、str.length()、str.equals(“”)的区别

null:该对象是否存在

String str = null;
System.out.println(str.length());
//会报错:NullPointerException
//null表示这个字符串不指向任何的东西

isEmpty():该对象存在,值是否为空

“”:一个长度为0的字符串,它可以调用字符串方法

02java_进阶

一、继承

1.定义:

​ 继承是将多个类的相同属性和行为抽取到单独一个类中,那么多个类无需再定义这些共性属性和行为,只要继承这个单独类即可继承这些属性和行为了。

2.优点(解决的问题):
  • 提高了代码的维护性、复用性
  • 建立了类与类之间的关系
3.局部变量,本类成员变量,父类成员变量重名如何区分?
  • 局部变量直接访问
  • 本类成员变量,使用this访问
  • 父类成员变量,使用super访问
4.如果子父类中出现同名同参数的方法,先使用谁的?如何指定访问父类的?
  • 优先使用子类的,要访问父类相同的方法可以使用super关键字,如:super.父类成员方法。
5.重载与重写
  • 重载
    • 在本类中发生;
    • 方法名相同;
    • 方法的参数列表不同(类型不同、数量不同、顺序不同);
    • 和返回值没有任何关系。
  • 重写
    • 发生在父子类(继承)中;
    • 子类的方法和父类的方法一模一样(非私有);
    • 应用场景:当子类需要父类的功能,但父类的功能不完全满足自己的需求时,子类可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容;
    • 父类方法中不能私有;
    • 子类中重写方法的访问权限必须>=父类中方法的权限。
6.继承中构造方法的访问特点:
  • 子类中所有的构造方法默认都会访问父类中无参的构造方法;
  • 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据;(子类初始化之前,一定要先完成父类的初始化)
  • 子类中所有的构造方法,默认会通过super()访问父类中无参的构造方法,super(参数)访问父类中有参的构造方法,每一个子类构造方法的第一句默认都是:super();
  • this(…)和super(…)必须放在构造方法的第一行有效句,并且二者不能共存。

二、抽象类

1.定义:

​ 使用abstract关键字修饰的类称为抽象类。

  • 抽象方法:使用abstract关键字修饰且没有具体实现的方法就是一个抽象方法。
2.使用场景:
  • 当父类定义一个方法时,每个子类对该方法的具体实现逻辑都不一样,那么父类定义该方法时就可以定义成抽象方法,这个类就必须是抽象类;
3.注意细节:
  • 抽象类也是类,也能定义类中的5大成分(成员变量、成员方法、构造方法、代码块、内部类);
  • 抽象类和抽象方法需要用abstract关键字修饰;
  • 有抽象方法的类一定是抽象类;
  • 抽象类是不能实例化的(不能创建对象);
    • 疑问:既然不能创建对象,那为什么还要有构造方法?
      • 抽象类通常是用于父类,创建子类对象时,需要先初始化父类(抽象方法中构造方法的作用就是用于父类初始化使用)
  • 抽象类的子类要么重写抽象类中所有的抽象方法,要么子类是一个抽象类;

三、匿名对象

1.定义:

​ 没有对象名接收的对象;

2.使用方式:
  • 匿名对象直接调用成员方法;
//当只使用对象中的某个方法一次时:使用匿名对象
new Student().study();
  • 匿名对象直接当做方法参数传递;
public static void show(Student stu){
  ..........
}

public static void main(String []args){
  show(new Student( ));
}
  • 匿名对象直接当做返回值。
public static Student creatStudent(String name,int age){
  
  return new Student(name,age);
}

四、static关键字

1.定义:

​ static关键字是静态的意思,是Java中的一个修饰符关键字,可以修饰成员方法、成员变量

  • 被static修饰的成员变量,一般叫做静态变量
  • 被static修饰的成员方法,一般叫做静态方法
2.static修饰的特点:
  • 被所在类的所有对象共享
    • 是我们判断是否使用静态关键字的条件
  • 随着类的加载而加载,优先于对象存在
    • 对象需要类被加载后,才能创建
  • 可以通过类名调用
    • 也可以通过对象名调用
    • 推荐使用类名调用
3.注意细节:
  • 静态方法只能访 问静态的成员(静态方法、静态变量);
  • 非静态方法可以访问静态的成员,也可以访问非静态的成员;
  • 静态方法中不能使用this关键字;
4.使用场景
  • 工具类

五、final关键字

1.定义:

​ final是Java语言中的修饰符关键字,用来修饰:类、方法、变量;

2.解决的问题:
  • 当某个变量不允许修改数据值时,可以使用final修饰变量;
  • 当某个方法 不允许被子类重写时,可以使用final修饰方法;
  • 当某个类不允许被继承时,可以使用final修饰类。
3.使用final:
public final class 类名{
  
  private final int COUNT = 100;
  
  public final void method(){
    
  }
}
4.使用细节:
  • final和abstract不能共同使用

六、权限修饰符

1.定义:
  • 权限修饰符是Java语言中的关键字,用于修饰:类、变量、方法;
  • private、public、protected、默认(什么都不写)
2.解决的问题:
  • 限制程序 问权限)
3.使用方法:
权限修饰符使用范围
private仅限本类中使用
默认(什么都不写)同一个包下的任意类
protected同一个包下的任意类、不同包下的子类
public没有限制
4.使用场景:
  • 工具类(特点):
    • 不能被继承
    • 不能让其他类创建对象
    • 提供静态方法

七、代码块

1.代码块划分:
  • 构造代码块
    • 书写位置:类中方法外(和成员变量、成员方法属于同一级)
    • 执行特点:会在每一个构造方法执行前,执行一次
    • 作用:当类中多个构造方法有共性内容时,可以抽取到构造代码块中
  • 静态代码块(开发中使用最多)
    • 书写位置:类中方法外(和成员变量、成员方法属于同一级)
      • 直接在构造代码块前 atic
    • 执行特点:随着类加载到内存,会执行一次。(类加载到方法区初始化的时候就会执行静态代码块)
    • 作用:可以对静态数据进行初始化(数据必须在创建对象之前就初始化完成)
  • 局部代码块(了解)
    • 书写位置:写在方法体中
    • 特点:在方法执行时,才会调用
    • 作用:限定作用域

八、接口

1.定义:

​ 当一个类中的所有方法都是抽象方法的时候,此类就是定义规则的类,我们就可以将其定义为接口;接口也是一种引用数据类型,它比抽象类还要抽象。

2.特点:
  • 接口不能实例化(不能创建对象)
  • 接口中没有构造方法(接口只能通过子类来实现对象创建)
  • 类与接口是实现关系,通过implements关键字表示
  • 接口的子类(实现类)
    • 要么重写接口中的所有抽象方法
    • 要么是抽象类
  • 接口可以多实现(一个类可以实现多个接口【接口类只能单一继承问题】)
3.作用:

​ 制定规则

4.在接口中可以书写的内容:
public interface 接口名{
  //抽象方法
  
  //静态常量
  public static final int NUMBER = 10;
}

//调用常量:
//接口名.常量名
5.接口中方法的升级:
  • 在JDK1.8中新增:默认方法(必须有default关键字,仅限在接口中书写)、静态方法

    • 默认方法:

    在这里插入图片描述

    • 静态方法:

    在这里插入图片描述

  • 在JDK1.9中新增:私有方法 (有方法体代码)

在这里插入图片描述

6.接口的使用思路:
  • 如果发现一个类中的所有方法都是抽象方法,那么就可以将该类,改进为一个接口
  • 涉及到了接口大面积更新方法,而不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法
  • 希望默认方法调用的更加简洁,可以考虑涉及为static静态方法。(需要去掉default关键字)
  • 接口中的方法出现了重复的代码,还不想被其他类访问,可以考虑抽取出一个私有方法。(需要去掉default关键字)
7.类和接口的关系
  • 类和类的关系
    • 继承关系、只能单继承,但是可以多层继承
  • 类和接口的关系
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
  • 接口和接口的关系
    • 继承关系,可以单继承,也可以多继承(接口中仅仅只是定义功能,没有功能的实现,子类要自己实现功能)
8.接口和抽象类:

​ 相同点:

  • 接口和抽象类,都不能实例化(不能创建对象)

  • 接口和抽象类都具有抽象方法

  • 接口和抽象类都是作为父类型存在

    不同点:

  • 抽象类:除了抽象方法外,还具有成员变量、构造方法、非抽象方法

  • 接口:除了抽象方法外,只有常量(JDK8:默认方法、静态方法;JDK9:私有方法)

  • 事物的共性内容(属性、行为),抽取到父类中(可以是抽象类);特有的行为,可以抽取到接口中

九、枚举

1.定义:

​ 在Java语言中,枚举是一种数据类型。枚举用来表示:固定且仅有几种取值范围的数据。

权限修饰符 enum 枚举名称{
  枚举项1,枚举项2...}

pu blic enum Sex{
  BOY,GIRL;
}

//通过枚举名直接引用枚举项即可,
Sex.BOY
2.枚举解决程序中的问题:

​ 当程序中有数据是固定且只有几种取值范围时,使用枚举类型强制设置值的范围(赋值数据有保障)

3.枚举的本质:

​ 枚举其实本质上是一个类,每一个枚举项是本枚举类类型的一个对象。我们可以使用JDK提供的反编译命令,将枚举的字节码进行反编译查看。

  • 枚举本质是一个final类,继承了Enum类
  • 在枚举可以定义:固定选项值、成员变量、构造方法(私有的,外界无法调用,只能定义枚举项的时候使用)、成员方法等
java -p 字节码文件名

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

十、工具类

1.书写规则:
  • 工具类,修饰为final(不让继承)
  • 工具类的构造方法,设置为private(不让其他类创建对象)
  • 工具类中提供:静态方法(通过类名访问)

十一、多态

1.定义:

同一个对象,在不同时刻表现出来的不同形态。

2.解决的问题:
  • 提供代码的复用性
  • 提供代码的扩展性
Cat c2 = new Cat();
Animal c1 = new Cat(); 
3.使用:
  • 父类型 对象 = new 子类();
    • Animal c1 = new Cat();
4.注意细节:
  • 多态中:父类和子类拥有一模一样的成员变量时:
    • 编译时:以父类型中的成员变量为主
    • 运行时:使用父类中的成员变量
  • 多态中:父类和子类拥有一模一样的成员方法时:
    • 编译时:以父类中的成员方法为主(检查父类中有没有这个成员方法,没有就报错)
    • 运行时:使用子类中的成员方法为主(调用子类对象中重写后的成员方法)
  • 父引用不能调用子类对象中特有的方法
  • 容易发生:java.lang.ClassCastException(异常转换异常) 解决方法:instanceof
if(父引用对象 instanceof 子类型 ){
  子类型 对象 = (子类型)父引用对象;
  对象.特有成员方法
}

if(animal instanceof Cat){
  Cat cat = (Cat) animal;
  cat.catchMouse();
}

十二、内部类

1.概念:

一个类A内部定义了一个类B,那么B就是A的内部类。A可以成为外部类。

class  Outer{

  class Inner{
    
  }
}
2.成员内部类对象实例化
  • 内部类的类型表示:外部类名.内部类名

  • 成员内部类创建对象,需要借助外部类的对象,如下:

    外部类名.内部类名 变量 = 外部类对象.new 内部类构造方法(参数)

class Person{
  class Heart{
    private int rate;
    
    public void beats(){
      System.out.println("咚咚咚~~~")
    }
  }
}

创建内部类Heart对象,并调用beats()方法

Person.Heart heart = new Person().new Heart();
heart.beats();
3.内部类访问外部类成分
  • 成员内部类的方法中默认存在一个外部类对象: 外部类名.this 。可用此格式来调用外部类成员,访问时如果没有冲突,格式可省略,如果有冲突就不能省略了。
  • 对于外部类来讲,没有默认的内部类对象的,如果要访问内部类的成员,需要创建内部类对象
class Person {
  private String name = "张三";
  int num = 30;//外部类成员变量
  class Heart {
    private int rate;//60~75
    int num = 20;//内部类成员变量
    public void beats() {
      int num = 10;//内部类方法中的局部变量
      System.out.println(num);//10
      System.out.println(this.num);//20
      System.out.println(Person.this.num);//30
    }
  }
}
4.匿名内部类:
  • 解决程序中的问题:简化程序中代码的书写
  • 介绍:匿名内部类是一个非常特殊的内部类,从命名可以看出,匿名内部类是没有类名的,因此这个内部类只能使用一次。
  • 定义格式:
new 类名/接口(){ //本质是一个子类对象
  //如果父类型有抽象方法,需要全部重写
}
  • 使用:
    • 使用父类型变量多态接收该匿名子类对象
    • 以匿名对象的方式使用
      • 直接调用方法使用
      • 当做方法的参数传递
      • 当做方法的返回值使用

在这里插入图片描述

  • 前提条件:有一个父类或者父接口
5.两个字符串比较:
  • a.equals(b) :如果a是null值,肯定会空指针
  • Objects.equals(a,b);:如果a是null,不会导致空指针异常

十三、包装类

1.作用:
  • 实现String类型和7种基本类型(没有char)之间的转换。
    • 字符串转为基本类型:包装类调用parseXxxx(字符串)方法
  • Java中的基本数据类型没有方法和属性,而包装类就是为了让基本数据类型拥有方法和属性,实现对象化交互。
2.基本数据类 型转为字符串
  • 直接在数值后加一个空字符串
  • 通过String类静态方法valueOf()
3.使用细节:
  • 在把String类型数据转换为基本类型数据时,容易发生:NumberFormatExcepion异常
    • 原因:String类型数据,不符合要转换的基本类型数据格式
  • 包装类在使用时存在:自动装箱、自动拆箱
    • 自动装箱:Integer.valueOf(数值)
    • 自动拆箱:Integer对象.intValue()
  • 包装类中有常量池(数据值没有超出常量池范围,就直接从常量池中获取对象)

十四、正则表达式

1.定义:由一些特定的字符组成的字符串校验规则。
2.解决的问题:
  • 正则表达式只能针对字符串进行格式校验;
  • 应用场景:对用户输入的字符串数据进行校验(数据合法性。例:手机号码格式)
3.使用方法:(基本上是复制粘贴)

在这里插入图片描述

在这里插入图片描述

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

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

十五、集合

1.好处:
  • 大小可变(随意扩容)
  • 可以存储多种不同类型的数据
  • 底层使用数据结构(存取效率高)
2.集合只能存储引用数据类型(如果要存储基本数据类型需要进行装箱)。
3.分类:

Collection(List、Set)、Map

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

//Collections工具类:是针对集合提供了一些功能(排序、二分查询、乱序、添加另一个集合)

//把给定的元素添加到指定的集合中
Collections.addAll(ListSet集合 , 元素1,元素2......)//对集合中的元素进行排序(自然排序、比较器)
Collections.sort( 集合 )//对集合中的元素随机打乱顺序
Collections.shuffle( 集合 )

十六、迭代器

1.概述:

迭代器是对Iterator的称呼,专门用来对Collection集合进行遍历使用的。学习迭代器的目的就是为了遍历集合。

2.迭代器相关的API介绍:

迭代器的使用有两个重要的接口:Iterable、Iterator。

3.迭代器的使用步骤:
  • 通过集合对象,获取到迭代器对象;
  • 使用迭代器对象调用hasNext(),判断是否存在下一个元素;
  • 如果有调用next()方法,获取下一个元素;
  • 循环2,3步骤,直到hasNext()方法返回false为止。
//使用迭代器:遍历集合
//1.通过集合对象,获取到迭代器对象
Iterator<String> it = collection.iterator();

//2.while循环,判断迭代器对象中是否有下一个元素
while(it.hasNext()){
  //3.使用迭代器对象,取出下一个元素
  String str = it.next();
  System.out.println(str);
}
4.注意事项
  • 在迭代器完成集合的遍历后,不要在使用:next()方法;NoSuchElementException
  • 在迭代器遍历集合的过程中,不能使用集合对象来增删元素;ConcurrentModificationException
    • 删除元素:使用迭代器对象中的remove方法
    • 增加元素:不用考虑(解决方案:使用其他的集合对象。例:List集合)
5.增强for循环
  • 基于普通的for循环,进行强化(底层使用:迭代器)
  • 应用:针对数组或集合,进行遍历
for( 元素类型 变量 : 容器){
  //从容器中获取出一个元素,赋给变量
  
  //下次:继续从容器中获取一个元素,赋给变量
  
  //......
  
  //停止条件 :容器没有元素了
}

十七、泛型

1.什么是泛型?

在Java语言中,是一种类型参数,可以设置存储数据的类型;

2.解决的问题:

在创建集合对象时,明确了集合中所存储元素的类型(限定类型)。

  • 泛型是使用在代码编写时期的技术方式(编译期技术);
  • 泛型在程序运行后,就擦除。
3.泛型的好处:

在编译时期会做类型的检查,可以有效避免在运行时类型强转的异常,对于程序员来将不用额外的类型强转操作,简化代码。

4.泛型的使用:
  • 泛型类
public class 类名<T>{
  
}
//当不确定类中成员变量具体使用类型时,可以用泛型表示
public class 泛型类<T>{
  
  private T 变量;
}
//在创建泛型类对象时,明确类型

//泛型类中成员变量的类型为:Integer
泛型类<Integer> 对象 = new 泛型类<Integer>();

//泛型类中成员变量的类型为:String
泛型类<String> 对象 = new 泛型类<String>();
  • 泛型接口
//当不确定接口中的某个方法参数使用什么类型、或方法的返回值使用什么类型:
//可以用泛型表示
public interface 泛型接口<T>{
  public void method(T a);
}

//情况1:在子类编写时,指定接口上泛型的具体类型
public class 子类 implements 泛型接口 String>{
  //方法重写
  public void method(String a){
    
  }
}
//情况2:在子类编写时,没有指定接口上的泛型。意味着:子类也使用和接口相同的泛型
//(子类:泛型类)

public class 子类<T> implements 泛型接口<T>{
  //方法重写
  public void method(T a){
    
  }
}
//创建子类对象时,明确了泛型的类型
子类<Integer> 对象 = new 子类<>();
  • 泛型方法
//语法格式
修饰符 <泛型> 返回值类型 方法名(<泛型> 参数1...{
  //方法体
}

//当前类没有声明为泛型类,但该类中的方法参数或方法返回值不确定类型时;
//使用泛型方法

public <T> void method(T param){
  
}

//当调用方法时, 向方法中传递参数的类型,就是泛型的类型


5.开发时选择哪个泛型定义:
  • 先明确要定义的是接口还是类
    • 在类中的成员变量,在不确定具体使用哪种类型时,可以使用:泛型 表示类型
    • 当接口中方法,在不确定方法参数类型或方法返回值类型时,可以使用:泛型
  • 在编写工具类时,工具类中的方法参数或返回值,不确定具体类型,可以使用:泛型
6.泛型的通配符
  • 在语法定义层面,可以使用:泛型的通配符 ?(任意类型)
ArrayList<?> list = new ArrayList<String>();
  • 当我们队泛型的类型确定不了,而想要表达的可以是任意类型,可以使用泛型通配符给定。符号就是一个问号: ? 表示任意类型,用来给泛型指定的一种通配值。
public class Test{
  public static void main(String[] args){
    ArrayList<Student> students = new ArrayList<Student>();
    method(students);
    
    
    ArrayList<Worker> workers = new ArrayList<Worker>();
    method(workers);
  }
  public static void method(ArrayList<?> list){
    
  }
}
  • 泛型通配符搭配集合使用一般在方法的参数中比较常见;在集合中泛型是不支持多态的,如果为了匹配任意类型,我们就会使用泛型通配符了。
7.受限泛型

受限泛型是指,在使用通配符的过程中,对泛型做了约束,给泛型指定类型时,只能是某个类型福类型或者子类型。

  • 泛型的下限:
    • <? super 类型> 只能是某一类型,及其父类型,其他类型不支持
  • 泛型的上限:
    • <? extends 类型> 只能是某一个类型,及其子类型,其他类型不支持

十八、数据结构

1.数组结构:
  • 数组在内存中的体现是一块连续存储数据的空间;
  • 查询快;
  • 增删元素效率慢;
2.List集合:
  • 特点:
    • 带有索引
    • 存储元素的顺序和获取元素的顺序一致
    • 可以存储重复元素
  • 常用子类:
    • LinkedList
      • 实现List接口,底层使用双向链表
    • ArrayList
      • ArrayList集合,实现List接口(List接口中的所有功能都有),底层就是使用:数组结构;
      • 特点:查询快、增删慢;
    • Vector

因为List集合可以使用索引,故围绕着索引,设计很多API方法:

//添加元素
List集合.add(索引,元素值)//向List集合中指定索引位置上,添加元素
  										//如指定索引位置上已有索引,会自动向后移动
//修改元素
List集合.set(索引,元素值)//修改List集合中指定索引位置上的元素值
//删除元素
List集合.remove(索引) //删除List集合中指定索引位置上的元素值
//获取元素
List集合.get(索引);
3.链表结构:
  • 增删快、查询慢
  • 特有方法都围绕着链表头和尾设计

在这里插入图片描述

  • 链表结构:
    • 在内存中是使用节点来存储数据
      • 节点 = 数据 + 地址
    • 链表:有头、有尾
    • 分类:
      • 单向链表:只能从头到尾
      • 双向链表:可以从头到尾,也可以是从尾到头(提高查询效率)

在这里插入图片描述

在这里插入图片描述

4.set集合
  • 特点:
    • 没有索引;(所以不能使用普通for循环遍历,使用迭代器遍历->增强for)
    • 存取元素不保证顺序;
    • 不允许存储重复元素
  • 常用子类:
    • HashSet集合
      • 特点:没有索引;存取元素不保证顺序、不能存储重复元素
      • 底层使用:哈希表结构(数组+链表)
      • 当使用HashSet存储自定义对象时,可能会出现存储重复内容的对象;
        • 解决方法:在自定义类中重写hashCode()和equals()方法。
    • LinkedHashSet集合
      • 特点:没有索引;不能存储重复元素;存取元素顺序一致
      • 底层:哈希表(为了去重) + 链表(保证存储元素的顺序)
      • 存储元素时,先过哈希表,如果哈希表能够接受数据,进一步存到链表结构实现
    • TreeSet集合
      • 特点:不能存储重复元素;没有索引;存储的元素会按照规则进行排序;
      • 底层:使用树结构(红黑树);
        • 去重、排序(拿树中已存在的元素,和要存储的元素进行比较 [比较大小:0、正数、负数])
      • 在TreeSet集合中存储:String、Integer、Double、Character (JDK提供的类型)
        • 都默认已实现了 java.lang.Comparable接口(自带自然排序规则)
      • 在TreeSet集合中存储:自定义类型 (程序员自己定义的)
        • 就必须保证自定义类型有实现java.lang.Comparable接口,并重写compareTo方法
        • 如果自定义类型,没有实现Comparable接口,在存储到TreeSet集合中时,会引发异常
//compareTo方法:
public int compareTo (E e){
  
  
  //返回值有三种:正数、负数、0 (底层红黑树需要)
}


public int compareTo (Student stu){
  //比较年龄
  int result = this.age - stu.age;
  if(result == 0){
    result = this.name.compareTo(stu.name);
  } 
  return result;
}

  • 需求:在TreeSet中存储的String类型数据,按照字符串长度从大到小排序
    • 结论:使用自然排序做不到(String类是final修饰,不能继承)
    • 解决方法:不使用自然排序,使用其他排序方式(比较器)
  • 排序的方式:
    • 自然排序:元素必须实现Comparable接口
    • 比较器排序:元素 不需要实现Comparable接口,需要在创建TreeSet对象时,指定排序规则。
      • 重写Comparator接口中的compare(T o1,T 02)方法;
//TreeSet构造方法
public TreeSet()  //默认使用自然排序

public TreeSet(Comparator c) //指定比较器对象
  
  
//Comparator接口:比较器,obj1表示要存储的元素,obj2表示已存在的元素
int compare(Object obj1, Object obj2) //比较两个元素的大小,0、正数、负数
  
//例子:
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>(){
	//重写方法
  public int compare(Student stu1,Student stu2){
  		//stu1:要存储的元素
  		//stu2: 已存在的元素
  }
})
5.Map
  • 概述:java.util.Map<K,V>集合,里面保存的数据是成对存在的,称之为双列集合。存储的数据,我们称为键值对。之前所学的Collection集合中元素是单个存在的,称为单列集合。
  • 特点:
    • 可以存储两个元素(键值对元素);
    • key元素不能重复,value元素允许重复;
    • 一个key元素只能对应一个value 元素(一一对应)[通过key找到value];
    • 存取元素不保 证顺序;
    • 没有索引;
  • 常用方法:

在这里插入图片描述

  • 遍历:

    • 键找值
      • 获取Map集合中所有的key元素,遍历所有的key元素,通过key元素找到对应的value元素
    Set<> 存储所有key的Set集合对象 = map集合.keySet();
    
    //例子:
    public class Demo1 {
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<>();
            map.put("周瑜","小乔");
            map.put("孙策","大乔");
            map.put("刘备","孙尚香");
            map.put("诸葛亮","黄月英");
            Set<String> keys = map.keySet();
            System.out.println(map);
            for(String key : keys){
                System.out.println(key+","+map.get(key));
            }
        }
    }
    
    • 键值对
      • 获取Map集合中所有的Map.Entry,遍历所有的Map.Entry,通过Entry中的API方法获取到key、value
    Set<Map.Entry<> > 存储所有键值对对象的Set集合对象 = map集合.entrySet();
    Map.Entry:
    	Object getKey();
    	Object getValue();
    
    
    //例子:
    public class Demo2 {
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<>();
            map.put("周瑜","小乔");
            map.put("孙策","大乔");
            map.put("刘备","孙尚香");
            map.put("诸葛亮","黄月英");
            Set<Map.Entry<String, String>> entries = map.entrySet();
            System.out.println(map);
            for(Map.Entry<String, String> s : entries){
                System.out.println(s.getKey()+","+s.getValue());
            }
        }
    }
    

在Map集合中当key存储的是自定义对象时,要保证对象存储数据的唯一性,需要:自定义对象中重写hashCode、equals方法。

  • 子类:
    • HashMap 底层使用哈希表
      • 此前的HashSet底层实现就是HashMap完成的,HashSet保存的元素其实就是HashMap集合中保存的键,底层的结果是哈希表结构,具有键唯一,无序的特点。
    • LinkedHashMap 底层使用哈希表+链表,去重,有序
      • 特点:元素唯一、元素有序
    • TreeMap 底层使用红黑树 ,键去重,通过键排序(按照指定规则排序)
      • 排序方法:
        • 自然排序(键所在的类要实现Comparable)
        • 若自定义类没有自然排序功能,或自然排序功能不满足要求时。可以自定义比较器排序(Comparator),在创建集合对象时,指定比较器
        • 两种排序方式对应了TreeMap的两个构造方法:
public TreeMap()   //使用自然排序
public TreeMap(Comparator<? super K> comparator)  //比较器排序
6.哈希表结构:
  • 底层是使用大小为16的数组 + 链表组成的存储方式 ;
    • JDK8之前:底层采用数组+链表实现
    • JDK8之后:底层采用数组 + 链表/红黑树实现
      • 从链表->红黑树时机: 当链表的长度>8,自动把链表转换为红黑树
  • 哈希表存储数据的方式(借助 哈希值 [内存中的存储位置]):
拿要存储的元素,结合哈希算法,计算出哈希值(存储位置)//调用:元素.hashCode()
  
判断:计算出的存储位置上是否有元素存在
			情况1:  没有元素存在   ->   直接存储
			情况2:  已有元素存在
							拿要存储的元素  和  已经存在的元素 进行比较(比较内容是否相同) 元素.equals()
  								相同:(重复元素) -> 不存储
  								不同:(不重复元素)
  										再次拿当前存储空间作为算法因子,再次进行哈希算法计算,计算新的存储空间
  										
7.树结构
  • 二叉树
    • 以跟节点为坐标,小于根节点的数据,存储在左边,大于根节点的数据,存储在右边;

在这里插入图片描述

  • 二叉查找树结构
    • 又称二叉排序树或者二叉搜索树
    • 特点:
      • 每一个节点上最多有两个子节点;
      • 每个节点的左子节点比当前节点小,右子节点比当前节点大;

在这里插入图片描述

  • 平衡二叉树结构
    • 特点:
      • 二叉树左右两个子树的高度差不超过1
      • 任意节点的左右两个子树都是一颗平衡二叉树
      • 通过左旋或右旋将不是平衡的二叉树变成平衡二叉树

在这里插入图片描述

  • 红黑树结构
    • 特点:
      • 平衡二叉B树;
      • 每一个节点可以是红或者黑;
      • 红黑树不是高度平衡的,它的平衡是通过“自己的红黑规则”进行实现的;
    • 红黑规则:
      • 每一个节点是红色或者黑色;
      • 根节点必须是黑色;
      • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的;
      • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
      • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
    • 添加节点:
      • 添加的节点的颜色,可以是红色的,也可以是黑色的。
      • 红色效率高 (默认是红色 )

在这里插入图片描述

十九、可变参数

1.定义:

在Java语言提供了一种特殊的参数:可变参数(可以改变的参数)

  • 在调用方法时,传递的参数可以是任意个(底层:使用了数组)
2.语法格式:
public 返回值类型 方法名(参数类型... 参数名){
  // ...就是可变参数的语法表示形式
}
//例子:
public static int getSum(int... args){
  int sum = 0;
  
  //把可变参数当做数组使用
  for(int i = 0;i < args.length;i++){
    sum += args[i];
  }
  return sum;
}
3.注意:
  • 可变参数只能作为方法的最后一个参数,但其前面可以有货没有任何其他参数;
  • 可变参数本质上是数组,不能作为方法的重载。如果同时出现相同类型的数组和可变参数方法,是不能编译通过的。
4.可变参数方法使用:

调用可变参数方法,可以给出零到任意多个参数,编译器会将可变参数转化为一个数组,也可以直接传递一个数组。方法内部使用时直接当做数组使用即可。

二十、算法

1.冒泡排序:
  • 定义:将一组数据按照升序规则进行排列;
  • 原理:相邻 的数据两两比较,小的放前面,大的放后面。
  • 注意:
    • 如果有n个数据进行排序,总共需要比较n-1次轮次
    • 每一次比较完毕,下一次的比较就会少一个数据参与
2.选择排序:
  • 它的工作原理是每一次从待排序的数据元素中选出最小的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。
3.二分查找:
  • 前提:数组中的元素需要有序;
  • 步骤:
    • 定义两个变量,表示要查找的范围,默认min=0,max=最大索引;
    • 循环查找,当min<=max;
    • 计算出mid的值
    • 判断mid位置的元素是否为要查找的元素,如果是直接返回对应索引;
    • 如果要查找的值在mid的左半边,那么min值不变,max=mid-1,继续下次循环查找;
    • 如果要查找的值在mid的右半边,那么max值不变,min=mid+1,继续下次循环查找;
    • 当min>max时,表示要查找的元素在数组中不存在,返回 -1。

二一、日志

1.日志技术的优势:
  • 可以将系统执行的信息选择性的记录到指定的位置(控制台、文件中、数据库中);
  • 可以随时以开关的形式控制是否记录日志,无需修改源代码;

在这里插入图片描述

2.体系结构:
  • 日志规范:一些接口,提供给日志的实现框架设计的标准;
    • 常见的规范:Commons Logging、Simple Logging Facade for Java
  • 日志框架:牛人或者第三方公司已经做好的日志记录实现代码,后来者直接可以拿去使用;
    • 常见的框架:Log4J、Logback
3.Logback
  • 概述:

    • Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好;
    • 官网:https://logback.qos.ch/index.html
    • Logback是基于slf4j的日志规范实现的框架;
  • 三个技术模块:

    • logback-core:为其他两个模块奠定了基础,相当于入口,必须有;
    • logback-classic:它是log4j的一个改良版本,核心功能模块,同时它完整实现了slf4j API;
    • logback-access:模块与Tomcat和jetty等servlet容器集成,以提供HTTP访问日志功能;
  • Logback快速入门

    • 需求:导入Logback日志技术到项目中,用于记录系统日志信息;
    • 步骤:
      • 导入Logback所需的相关jar文件,并添加到项目资源库中
        • 在项目工程下新建lib文件夹,把logback需要的jar文件放到该文件夹下
        • lib目录下存储的jar文件,添加到当前项目资源库中
      • 把logback核心配置文件,拷贝到当前项目的src目录下(必须是src下)
      • 在类中获取到Logger日志对象,使用日志对象中的API方法记录需要的操作信息
    private static Logger logger = LoggerFactory.getLogger("类名") 
    
  • Logback日志框架的核心:logback.xml

  • 日志级别:

    • TRACE(追踪)<DEBUG(调试,程序有bug,测试bug使用的)<INFO(关键信息)<WARN(警告)<ERROR(错误);默认级别是debug(忽略大小写),对应其方法;
    • 作用:用于控制系统中哪些日志级别是可以输出的,只输出级别不低于设定级别的日志信息;
    • ALL和OFF分别是打开全部日志信息,关闭全部日志信息;

二二、异常

1.异常概述

​ 异常就是程序出现了不正常的情况。程序在执行过程中,数据导致程序不正常,最终会导致JVM的非 正常停止。注意:语句错误不算在异常体系中。

2.异常的存在形式

​ 异常有类型之分,比如我们之前有接触过的比较熟悉的数组越界异常(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException),类型转换异常(ClassCastException)。当程序中产生异常时,其实就是在异常的位置创建了一个该异常的对象,该对象携带了相关的异常信息。因此,异常就是Java中提供的类的对象。

3.程序中异常产生后,是如何处理的?

​ 程序中一旦产生异常,首先会 中断向下执行。异常的传递要根据处理方式而定,如果没有处理,默认是将异常传递给本方法的调用者。不断往回传递,知道JVM收到该异常,此时程序终止执行。

4.异常体系

在这里插入图片描述

在这里插入图片描述

5.虚拟机处理异常方式
  • 打印异常的信息,异常的类型、异常发生的位置、异常的原因
  • JVM停止
6.异常处理的方式
  • 声明:throws
    • 遇到异常发生了,自己不处理,交给别人处理
      • 最终还是需要有一个位置使用try…catch来处理异常
      • 结论:在main方法中只能使用try…catch

使用声明的方式处理异常:

//声明是书写在方法定义上
修饰符号 返回值类型 方法名(参数类型 参数,...throws 异常类1,异常类2,...
{

}
//在定义方法时,使用声明的方式处理异常
public void methods ()throws NullPointerException
{

}

/*
作用:
表示告知调用者当前的方法可能会出现某些异常,使用时需要注意
如果当前方法没有出现任何异常,那么代码会正常执行
如果当前方法中出现了异常,会把异常交给本方法调用者处理
*/

/*
编译时异常因为在编译时就会检查,所以必须要写在方法后面进行显示声明
运行时异常因为在运行时才会发生,所以在方法后面可以不写
如果声明多个异常有子父类关系,那么只要声明一个父类即可(多态)
*/
  

throws 和 throw 的区别:

  • throws:声明
  • throw:抛出(手动引发异常)
    • 程序员手动创建一个异常对象,并抛出给调用者
    • 注意:抛出异常的格式必须在方法的内部完成;如果手动抛出一个异常,在throw后面不能书写任意代码
    • 在方法中,当传递的参数有误,没有继续运行下去的意义了,则采取抛出处理,表示让该方法结束运行;告诉调用者方法中出现了问题;
throwsthrow
用在方法声明后面,跟的是异常类名用在方法体内,跟的是异常对象
表示声明异常,调用该方法有可能出现这样的异常表示手动抛出异常对象,告知调用者数据传入有误
异常对象由JVM创建异常对象我们自己创建
//格式: 
修饰符 返回值类型 方法名(参数列表){
  throw new 异常类名();
}

//把创建的异常类对象抛出给调用者
throw new RuntimeException("参数不能为空")

  
  • 捕获:try…catch
    • 遇到异常发生,自己处理
      在这里插入图片描述

在这里插入图片描述

异常处理有两种:声明、捕获、在程序开发中到底怎样选择,使用哪个呢?

  • 自定义方法(程序员自己写的方法),通常都可以使用:声明
    • 方法体内代码比较清爽(阅读性好)
    • 把异常统一抛出到main方法中,进行统一的处理
  • 捕获的使用场景:
    • main方法中只能使用捕获
    • 父类型中的方法不支持throws,在子类重写方法时,重写的方法只能使用捕获
public class Demo extends Thread{
  //重写方法
  public void run(){
    
    try{
      Thread.sleep(100);
    }catch(InterruptedException e){
      e.printStackTrace();
    }
  }
}
7.Throwable的成员方法
方法名说明
public String getMessage()返回此throwable的详细消息字符串
public String toString()返回此可抛出的简短描述
public void printStackTrace()把异常的错误信息输出在控制台
8.自定义异常
  • 程序员自己编写的异常类
  • 解决问题:JDK提供的异常类在命名上做不到见名之意,通常在开发中程序员会自定义自己的异常类。
  • 使用:
public 自定义异常类 extends Exception{//当前自定义异常类为:编译时异常
  public 自定义异常类(){
    //super();   //调用父类中的无参构造方法
  }
  public 自定义异常类(String message){
    super(message);
  }
}

public 自定义异常类 extends RuntimeException{//当前自定义异常类为:运行时异常
  
}

二三 、Lambda

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

1.lambda表达式:
  • 作用:简化程序中的匿名内部类代码书写;
  • 前置要求:仅针对函数式接口进行代码编写;
2.函数式接口
  • 只有一个抽象方法需要重写的接口,函数式接口。函数式接口是允许有其他的非抽象方法的存在,例如:静态方法、默认方法、私有方法。
  • 为了标识接口是一个函数式接口,可以在接口之上加上一个注解:@FunctionalInterface,以示区别。
  • 在JDK中 java.util.function包中的所有接口都是函数式接口。我们学习线程时学习的Runnable也是函数式接口。
@FunctionlInterface
public interface Runnable{
  public abstract void run();
}

自己定义函数式接口,比如定义一个游泳的函数式接口:

@FunctionlInterface
public interface Swim{
  public abstract void swimming();
}
3.格式书写
  • 标准语法
    • ():代表的是一个方法;
    • ->:指向要做的事情
    • {}:功能代码(具体要做事情的代码)
(参数 ,... ) ->{
  //方法体代码(要做什么事情)
}
  • 省略规则:
    • 可以省略参数类型:要么全部省略,要么全部保留;
    • 如果参数仅有一个时,可以省略小括号;
    • 如果代码块中仅有一行代码,可以省略:大括号、分号、return ;
4.Lambda表达式和匿名内部类区别

所需类型不同

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类;
  • Lambda表达式:只能是函数式接口;

使用限制不同:

  • 如果接口中有且仅有一个抽象方法需要重写,可以使用Lambda表达式,也可以使用匿名内部类;
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式;

实现原理不同:

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件;
  • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成;

二四、Stream流

1.作用:
  • 针对集合进行功能简化开发
2.Stream流的三类方法
  • 获取Stream流
    • 创建一条流水线,并把数据放到流水线上准备进行操作
  • 中间方法:过滤、截取
    • 流水线上的操作;一次操作完毕之后,还可以继续进行其他操作;
  • 终结方法
    • 一个Stream流只能有一个终结方法;是流水线上的最后一个操作;
3.获取Stream流对象:
//单列集合:Collection[ List, Set ]
Stream 流对象 = 单列集合对象.stream();

//双列集合:Map (不能直接获取流对象)
1.先通过keySet()entrySet(),获取到Set集合
2.Stream 流对象 = Set集合对象.stream();

//数组
Stream 流对象 = Arrays.stream( 数组 )//特殊的操作: Stream流中的静态方法:  static<T> Stream<T> of(T... args)
Stream 流对象 = Stream.of(同一种类型元素,同一种类型元素,同一种类型元素,...);
4.Stream流中间操作方法:

在这里插入图片描述

  • 过滤

    • Stream filter(Predicate predicate):用于对流中的数据进行过滤

    Predicate接口中的方法:boolean test(T t):对给定的参数进行判断,返回一个布尔值;

  • 截取

  • 跳过

  • 合并

  • 转换

  • 排序

5.Stream流:终结方法

在这里插入图片描述

6.Stream流:收集方法

在这里插入图片描述

  • tolist
public class StreamDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        //保留集合中的偶数
        list = list.stream()
                .filter(num -> num%2==0)//过滤流对象中的元素
                .collect(Collectors.toList());//把流对象中的元素收集到List集合

        System.out.println(list);
    }
}
  • toArray
public class StreamDemo2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        //保留集合中的偶数
        Integer[] array = list.stream()
                .filter(num -> num % 2 == 0)//过滤流对象中的元素
                .toArray(value -> new Integer[value]); //把流中的元素收集到数组中

        System.out.println(Arrays.toString(array));
    }
}
  • toMap
public class StreamDemo {
    public static void main(String[] args) {
        //创建List集合
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "zhangsan,23", "lisi,24", "wangwu,25");

        //使用Stream流解决需求:保留年龄>=24岁的人,并将结果收集到Map集合(key:姓名 、 value:年龄)
        Map<String, String> map = list.stream()
                .filter(str -> Integer.parseInt(str.split(",")[1]) >= 24)
                .collect(Collectors.toMap(s -> s.split(",")[0],
                        s -> s.split(",")[1]));

        System.out.println(map);
    }
}
7.Optional类
  • 介绍:可能包含或不包含非空值的容器对象。如果一个值存在,isPresent()将返回true和get()返回值;简单理解:使用Optional可以简化非空判断操作。
  • Optional类常用功能:

在这里插入图片描述

public class OptionalDemo1 {
    public static void main(String[] args) {
        //获取学生对象
        Student student = getStudent();
        //没有使用Optional之前,进行非空验证
        //A公司
        if (A_Util.checkValidData(student)) {
            System.out.println("有效数据");
        }
        //B公司
        if (B_Util.isNotNull(student) == false) {
            System.out.println("有效数据");
        }

        //C程序员:开始使用的A公司的工具类,  
      //后面技术总监要求更换为B公司的工具类 (意味着:C程序员要修改代码逻辑)

        //使用Optional进行非空验证    ofNullable可以避免:空指针异常的问题
        boolean result = Optional.ofNullable(student).isPresent();
        if (result) {
            System.out.println("Optional : 有效数据");
        }

        //获取Optional中的元素值
        Student stu = Optional.ofNullable(student).get();
        System.out.println(stu);

//Optional<Student> optionalStudent = Optional.ofNullable(student);
//        if(optionalStudent.isPresent()){
//            //stuent对象不为空
              //........
//        }else{
//            //........
//        }
    }

    public static Student getStudent() {
        return  new Student("张三");
       // return null;
    }
}

二五、线程

1.相关概念
  • 并行:在同一时刻,有多个任务在多个CPU上同时执行;
  • 并发:在同一时刻,有多个任务在单个CPU上交替执行;
  • 进程:简单地说就是在多任务操作系统中,每个独立执行的程序; 所以进程也就是“正在进行的程序”。
  • 线程:程序运行的基本单元。当操作系统执行一个程序时,会在系统中建立一个进程,该进程必须至少建立一个线程(这个线程被称为主线程)作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个线程。
2.多线程:
  • 是指从软件或者硬件上实现多个线程并发执行的技术;
  • 具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能;
3.多线程开发的好处:提高程序的效率
4.多线程实现方法
  • 方式一:Thread类(java语言提供的线程类)
    • 创建一个子类,继承Thread类(创建的子类,也是线程类)
    • 在子类中,编写让线程帮助完成的任务(任务代码)
      • 重写Thread类中的run方法(线程任务)
    • 启动线程 start()方法
  • 方式二:实现Runnable接口(接口可以多实现,而且允许子类在继承其他父类)【推荐:灵活度高】
    • 创建一个子类, 实现Runnable接口
    • 在子类中,重写Runnable接口中的方法:run(线程任务方法)
    • 创建Thread类对象,并把实现了Runnable接口的子类对象,作为参数传递给Thread类对象
    • 启动线程
优点缺点
实现Runnable扩展性强,实现接口的同时还可以继承其他类编程相对复杂,不能直接使用Thread类中的方法
继承Thread编程比较简单,可以直接使用Thread类中的方法可扩展性较差,不能在继承其他的类
5.并发编程
  • 定义:

在一个程序中开启多个线程,让多个线程执行相同的任务,从而提高程序执行效率。

  • 解决的问题:

提高程序的运行效率; 提高使用者体验;

  • 实现:

Thread类、Runnable接口;

6.线程中常用方法:
String getName() //获取线程的名字
  
void setName(String name)  //给线程设置一个名字
  
static Thread currentThread()  //获取当前正在运行的线程对象
  
static void sleep(long 毫秒)  //让当前运行的线程休息(单位:毫秒)
  
void join()   //阻塞调用此方法的线程,直到线程thread完成,此线程再继续

7.线程安全
  • 问题原因:多个线程对同一个数据,进行读写操作,造成数据错乱。
  • 解决思想:让共享数据存在安全的环境中,当某一个线程访问共享数据时,其他线程都是无法操作的。
  • 实现:把多条线程操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可;Java语言基于线程安全问题,提供了同步机制:同步代码块;同步方法;锁机制(Lock锁);
  • 线程的同步:Java允许多线程并发执行,当多个线程同时操作一个可共享的资源变量时(如数据的增删改 查),这会导致数据不准确,相互之间产生冲突,因此加入同步锁,以避免在该线程没有完成操作之前,被其他线程的调用,从而保证该变量的唯一性和准确性;
8.同步代码块

锁住多条语句操作共享数据,可以使用同步代码块实现

  • 格式:
synchronized(任意对象){
  多条语句操作共享数据的代码
}

  • 默认情况锁是打开的,只要有一个线程进去执行代码,锁就会关闭;
  • 当线程执行完出来,锁才会自动打开;
  • 锁对象可以是任意对象,但是多个线程必须使用同一把锁;
  • 好处:
    • 解决了多线程的数据安全问题;
  • 弊端:
    • 当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率;
  • 锁对象的使用规范:
    • 建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象;
    • 对于静态方法建议使用==字节码(类名.class)==对象作为锁对象
9.同步方法

就是把synchronized关键字加到方法上,保证线程执行该方法的时候,其他线程只能在方法外等着;

  • 格式
修饰符 synchronized 返回值类型 方法名(方法参数){}
  • 同步代码块和同步方法的区别:
    • 同步代码块可以锁住指定代码;同步方法是锁住方法中所有代码;
    • 同步代码块可以指定锁对象;同步方法不能指定锁对象;
  • 同步方法也有对象锁:
    • this锁(当前对象锁)
    • 类名.class(静态方法上的对象锁)
10.Lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock;

  • Lock中提供了获得锁和释放锁的方法
    • void lock():获得锁
    • void unlock():释放锁
  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法;
    • ReentrantLock():创建一个ReentrantLock的实例

注意:多个线程使用相同的Lock对象,需要多线程操作数据的代码放在lock()和unLock()方法之间。一定要确保unlock最后能够调用。

11.线程死锁
  • 定义:

同步代码块的锁进行嵌套使用,就会大概率产生死锁;

  • 如何避免死锁:

不使用锁的嵌套;

12.线程的状态
  • 新建状态(NEW)

  • 就绪状态(RUNNABLE)

  • 阻塞状态(BLOCKED)

  • 等待状态(WAITING)

  • 计时等待(TIMED_WAITING)

  • 结束状态(TERMINATED)

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

13.线程通讯
  • 定义:

    线程间的通讯技术就是通过等待和唤醒机制,来实现多个线程协同操作完成某一项任务,例如经典的生产者和消费者案例。

等待唤醒机制其实就是让线程进入等待状态或者让线程从等待状态中唤醒,需要用到两种方法:

  • 等待方法:
    • 特殊之处:会释放掉对象锁
wait()  //无限等待(只能其他线程唤醒)
wait(long 毫秒)   //计时等待(时间到了自动唤醒)
  
//以上两个方法调用会导致当前线程释放掉锁资源
  • 唤醒方法
    • 特殊之处:不会释放掉对象锁
notify()//唤醒处于和notify使用同一个对象锁上的,且处于"等待状态"的任意一个线程

notifyAll()      唤醒处于"等待状态"的所有线程
  
//以上两个方法调用不会导致当前线程释放掉锁资源

使用细节:wait() 方法和notify() 方法,都必须绑定在对象锁上

Object lock = new Object();//对象锁

lock.wait();

lock.notify();
14.等待唤醒的使用
  • 等待和唤醒方法的调用有什么要求:
    • 需要使用锁对象调用,需要在同步代码块中完成
  • 线程进入等待使用方法:
    • wait进入无限等待,wait(时间) 计时等待
  • 线程唤醒其他线程的方法是什么:
    • notify()、notifyAll()
15.线程池
  • 线程使用存在的问题:

    • 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间;
    • 如果大量线程在执行,会涉及到线程间上下文的切换,会极大的消耗CPU运算资源;
  • 线程池定义:

    • 就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源;
  • 线程池使用流程:

    • 创建线程池指定线程开启的数量;
    • 提交任务给线程池,线程池中的线程就会获取任务,进行处理任务;
    • 线程处理完任务,不会销毁,而是返回到线程池中,等待下一个任务执行;
    • 如果线程池中的所有线程都被占用,提交的任务,只能等待线程池中的线程处理完当前任务;
  • 线程池使用的好处:

    • 降低资源消费:为了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务;
    • 提高响应速度:当任务到达时,任务可以不需要等待线程创建,就能立即执行;
    • 提高线程的可管理性:可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,服务器死机(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
  • 如何得到线程池对象

    • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象;
    //构造方法
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,
                             long keepAliveTime,TimeUnit unit,
                             BlockingQueue<Runnable> workQueue,
                             ThreadFactory threadFactory,
                             RejectedExecutionHandler handler)
    
    参数解释
    corePoolSize指定线程池的核心线程数量
    maximumPoolSize指定线程池的最大线程数量
    keepAliveTime指定临时线程的存活时间
    unit指定临时线程存活的时间单位(秒、分、时、天)
    workQueue指定线程池的任务队列
    threadFactory指定线程池的线程工厂(创建线程的地方)
    handler指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
    • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象;
Executors.newFixedThreadPool()
  • 线程池的注意事项:
    • 临时线程的创建:
      • 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程;
    • 什么时候会开始拒绝新任务:
      • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务;
  • 线程池处理Runnable任务
    • ExecutorService的常用方法
    • 使用步骤:创建线程池;定义Runnable任务;创建Runnable任务;将任务添加到线程池中;
方法名称说明
void execute(Runnable command)执行 Runnable 任务
Future submit(Callable task)执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果
void shutdown()等全部任务执行完毕后,再关闭线程池
List shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务

在这里插入图片描述

  • 线程池处理Callable任务
    • 使用步骤:创建线程池;定义Callable任务;创建Callable任务,提交任务给线程池;获取执行结果
    • ExecutorService的常用方法
方法名称说明
Future submit(Callable task)执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果
void shutdown()等全部任务执行完毕后,再关闭线程池
List shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务

Callable与Runnable不同点:

  • Callable支持结果返回,Runnable不行
  • Callable可以抛出异常,Runnable不行

二六、File类

1.File类介绍
  • java.io.File类是文件和目录路径名的抽象表示形式,主要用于文件和目录的创建、查找和删除等操作;
  • 用来描述计算机磁盘上的文件或目录的
  • 使用方法
public File(String path){
  //文件对象
  File file = new File("路径\文件名")
    
  //目录对象
  File dir = new File("路径")
}

public File(String parent,String child ){
  //基于父路径 和 子路径 合并后,生成File对象
}

在这里插入图片描述

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

在这里插入图片描述

二七、IO流

1.IO流解决的问题:
  • 把程序中存储在内存中的数据,写入到文件中(持久存储);
  • 把磁盘文件中存储的数据,读取到内存中;
2.IO流概述:
  • I流:把磁盘文件中存储的数据,读取到内存中(读数据);
  • O流:把内存中的数据,写入到磁盘文件(写数据);
3.IO流分类:

在这里插入图片描述

字节流(万能流):所有类型的文件都可以读写;

字符流:通常用于针对纯文本文件进行读写操作;

  • 纯文本文件: 使用计算机中的记事本软件打开并看得懂的文件;
4.字节输出流

​ OutputStream(父类、抽象类) 子类:FileOutputStream

  • 以字节为单位,把内存中数据写入到磁盘文件中;
    在这里插入图片描述

  • 字节流写数据的3种方式:

方法名说明
void write(int b )一次写一个字节数据
void write(byte[] b)一次写一个字节数组数据
void write(byte[] b,int off,int len)一次写一个字节数组的部分数据
  • 向文件中追加写入数据:(创建字节输出流对象时,不会清空文件中的原有内容)
    • 写完数据后,加换行符
      • windows:\r\n
      • linux:\n
      • mac:\r
 FileOutputStream fos = new FileOutputStream("关联文件",true);
//第二个参数:true,表示追加写入数据


//向文件中写入:换行符号
fos.write("\r\n".getBytes());
5.字节输入流

InputStream(父类、抽象类) 子类:FileInputStream

  • 字节流读数据(一次读一个字节)

在这里插入图片描述

  • 读数据的方法:
//一次读取1个字节数据,返回实际读取到的字节数据;读取到文件末尾时:返回-1
int read()
  
//一次最多读取buf.length个 字节数据,把读取到的字节数据存储到buf数组中,
// 并返回实际读取字节数据的个数;读取到文件末尾时:返回-1 
int read(byte[] buf)
6.文件复制
  • 前提:

    • 有源文件(读源文件中的数据)
    • 有目标文件(向目标文件中写入数据)

    1.创建字节输入流,关联源文件

    2.创建字节输出流,关联目标文件

    3.使用字节输入流,读取源文件中的字节数据

    4.把读取到的字节数据,写入到目标文件中

    5.循环重复:3、4

    6.当源文件读取结束(结束标记 :-1)

    7.释放资源

7.IO资源的处理
try{
  //监视可能会发生异常的代码
}catch(Exception e){
  //处理异常
}finally{
  //不论是否有异常,都会执行
  //应用场景:释放资源
}

JDK1.7版本中IO资源处理代码(try-with-resource):

  • 好处:自动释放IO资源(不需要在书写close()方法)
  • 注意:资源对象想要实现自动释放,必须保证资源对象由实现AutoCloseable接口;
try(创建IO流对象语句1;创建IO流对象语句12...){
  //监视可能会发生异常的代码
}catch(Exception e){
  //处理 异常
}
8.字节缓冲流

在IO体系下,java提供了高效流(提高读写文件的效率)

  • 读文件:BufferedInputStream
  • 写文件:BuffereOutputStream

在IO流中基础流只有两个:字节流、字符流

BufferedInputStream流的创建:读数据

  • 自己没有读数据的能力,需要依赖字节输入流实现读数据
//构造方法:public BufferedInputStream( InputStream is )

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("关联文件"))

BufferedOutputStream流的创建:写数据

  • 自己没有读数据的能力,需要依赖字节输入流实现读数据
//构造方法:public BufferedOutputStream(OutputStream os )

//覆盖写入
BufferedOutputStream bos = new BufferedOutputStream(new FileInputStream("关联文件"))

//追加写入
FileOutputStream fos = new FileOutputStream("关联文件",true);
BufferedOutputStream bos = new BufferedOutputStream(fos)
  
  • 原理

在这里插入图片描述

9.使用Properties
  • Properties和IO流结合的方法:
方法名说明
setProperty(String key,String value)向Properties集合中存储:key、value
String getProperty(String key)根据指定的key,获取properties集合中对应的value
Set stringPropertyNames();获取Properties集合中所有的key元素
void load(InputStream inStream)以字节流形式,把文件中的键值对,读取到集合中
void load(Re ader reader)以字符流形式,把文件中的键值对,读取到集合中
void store(OutputStream out,String comments)把集合中的键值对,以字节流形式写入文件中,参数二为注释
void store(Writer writer,String comments)把集合中的键值对,以字符流形式写入文件中,参数二为注释
  • Properties类的作用:
    • 读取开发中使用的.properties配置文件;
      • properties配置文件中的数据是以key/value形式出现
      • 把properties配置文件中的key,存储到Properties类中的key元素下
      • 把properties配置文件中的value,存储到Properties类中的value元素下
10.ResourceBundle工具类
  • API介绍:

    • java.util.ResourceBundle它是一个抽象类

    • 我们可以使用它的子类PropertyResourceBundle来读取以.properties结尾的配置文件 通过静态方法直接获取对象

    • static ResourceBundle 直接获取默认语言环境下的属性资源。

    • 参数注意:baseName

      • 1.属性集名称不含扩展名
      • 2.属性集文件是在src目录中的

      比如:src中存在一个文件user.properties

      ResourceBundle bundle = ResourceBundle.getBundle(“user”);

  • ResourceBundle中常用方法:

    • String getString(String key):通过键,获取对应的值
  • 作用:读取项目工程下src目录中的.properties配置文件

//获取ResourceBundle对象(必须保证properties配置文件存放在src目录下)
ResourceBundle rb = ResourceBundle.getBundle("properties配置文件名跑[不含后缀名]");

//根据给定的key,获取value
String value = rb.getString("key");

11.字符流

字符流 = 字节流+ 编码表

​ 底层还是使用字节流读写数据,但是由于指定编码表,那么就可以一次读写2或3个字节数据;

​ 为什么使用字符流?针对纯文本文件中的中文数据,进行读写操作

​ 为什么字节流读取中文数据时有乱码?答案:编码表

​ 利用了转换流实现:字节流和字符流之间的转换;

  • 字符输出流 : java.io.Writer类

    • Writer类是一个抽象类(不能实例化)
    • 写数据的5中方式
    方法名说明
    void write(char c)写一个字符
    void write(char[] cbuf)写入一个字符数组
    void write(char[] cbuf,int off,int len)写入字符数组的一部分
    void write(String str)写一个字符串
    void write(String str,int off,int len)写一个字符串的一部分
    flush()刷新流,还可以继续写数据
    close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
    • 常用子类:FileWriter
//创建字符输出流
writer w = new FileWriter("关联文件")//覆盖写入
writer w = new FileWriter("关联文件"true)//追加写入
  • 字符输入流: java.io.Reader类

    • Reader类是一个抽象类,不能实例化,常用子类FileReader
    • FileReader类:用来读取字符文件的便捷类
    方法名说明
    int read()一次读一个字符数据
    int read(char[] cbuf)一次读一个字符数组数据
    FileReader类构造方法
    pulic FileReader(File file)从指定的File路径中读取数据
    pulic FileReader(String fileName)从指定的String路径中读取数据
Reader r = new FileReader("关联文件");

//一次读取1个字符数据
int data = r.read();//data中存储的是字符数据的编码值

//一次读取多个字符数据
int len = r.read(char[] cbuf)
  //一次最多读取cbuf.length个字符数据,并把读取的字符数据存储到cbuf数组中,
  //返回实际读取到的字符数据个数,读到文件末尾返回-1
12.字符缓冲流
  • BufferedWriter:可以将数据高效的写出 BufferedWriter(Writer out)
  • BufferedReader:可以将数据高效的读入到内存 BufferedReader(Reader in)

==注意:==字符缓冲流不具备读写功能,只提供缓冲区,真正读写还是需要依赖于构造接受的基本的字符流;

//字符输入缓冲流
BufferedReader br = new BufferedReader(new FileReader("关联文件"));

//字符输出缓冲流,覆盖写入
BufferedWriter bw = new BufferedWriter(new FileWriter("关联文件"));


//字符输出缓冲流,追加写入
BufferedWriter bw = new BufferedWriter(new FileWriter("关联文件",true));
  • 字符缓冲流的特有方法:
    • BufferedWriter:void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
    • BufferedReader:public String readLine():读取文件一行数据,不包含换行符号,读到文件的末尾返回null;
13.编码表

把生活中的数据和计算机中的数据整合在一起,存放在一张表中;

  • 计算机中存储的信息都是用二进制数据表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果;

  • 按照编码表规则,将字符存储到计算机中,称为编码;

  • 按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码

  • 编码和解码使用的编码表必须一致,否则会导致乱码;

    简单理解:存储一个字符a,首先需在编码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储,称为编码;读取的时候,先把二进制解出来,在转成97,通过97查找编码表中对应的字符是a,称为解码。

14.转换流
  • 作用:读写特定编码表的文件;FileReader类默认是UTF-8编码表(无法读GBK编码表的文件)
  • 输入流:InputStreamReader
    • 自身没有读数据的能力,需要依赖字节输入流
  • 输出流:OutputStreamWriter
    • 自身没有写数据的能力,需要依赖字节输出流
//InputStreamReader(InputStream 字节输入流,String 编码表名字)
//创建输入流,并制指定关联文件使用的编码表
InputStreamReader isr = new InputStreamReader(new FileInputStream("关联文件"),"GBK");
  
//OutputStreamWriter(OutputStream 字节输出流,String 编码表名字)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutStream("关联文件"),"GBK");
15.对象操作流
  • 对象操作流的应用:

    • 把程序中创建的对象,先写入到文件中(持久化存储)
    • 即使重启计算机后
    • 通过程序读取文件中存储的对象,可以重新把对象加载到内存中
  • 对象操作输出流(对象序列化流):把内存中创建的对象,写入到本地文件中,或者在网络中传输对象

    • IO流类:ObjectOutputStream
    Object readObject()
    
  • 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输对象;

    • IO流类:ObjectInputStream
    void writeObject(Object obj)
    
  • 序列化流时,需要对象实现序列化接口,Serializable,但是它是一个空接口(接口中什么也没)

    • 在Java语言中存在一种特殊的接口:标记接口(空接口)

      • 仅有接口的定义,接口内没有任何内容
      • 作用:给类的实例添加一个标记
    • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会出问题,会抛出InvalidClassException异常

      • 解决方法:给对象所属的类加一个serialVersionUID

        private static final long serialVersionUID 42L;

    • 如果一个对象中的某个成员变量的值不想被序列化?(成员变量以及数据不写入到文件中)

      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程;
16.打印流
  • 作用:
    • 在写入数据后可以实现自动换行
    • 通常用于日志记录
  • 类:PrintStream
public PrintStream(String filePath):构建一个打印流对象,传入接收数据的文件路径
  • 常用方法:
方法名说明
public void println(数据)打印后换行
public void print(数据)打印后不换行
17.Commons-io工具类
  • IOUtils类:针对 IO流进行读写操作(单文件vs单文件)
public static int copy(InputStream in ,OutputStream)
  //把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数
  //(适合文件大小为2GB以下)
  
public static long copyLarge(InputStream in ,OutputStream out)
  //把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数
  //(适合文件大小为2GB以上)
  • FileUtils类:针对File对象进行读写操作(单文件vs单文件、目录vs目录)
public static void copyFileToDirectory(final File srcFile,final File destFile)
  //复制文件到另外一个目录下
public static void copyDirectoryToDirectory(File src,File dest)
  //复制src目录到dest位置

二八、递归、装饰者模式

1.递归

递归是应用于方法上的解决方案:方法自己调用自己。

注意:弊端:递归存在一个隐患,每次递归时方法都会入栈,从而占用栈内存资源,当递归次数太多,栈内存用完时会引发错误:内存溢出;

解决方案:队列结构

2.装饰者模型

不改变原类,不使用继承的基础上,动态地扩展一个对象的功能(功能增强)

  • 装饰者模式的套路:
    • 装饰类和被装饰类需要有共同的父类型/接口;
    • 装饰类的构造要接收被装饰类的对象;
    • 在装饰类中要把增强扩展的功能进行扩展;
    • 对于不要增强的功能之间调用;
//字符输入缓冲流:BufferedReader   特点:提高读的效率

//情况:在使用FileReader类时,发现读的效率低,希望提高效率

//不能修改FileReader类、不能继承FileReader类,要对read(char[] cbuf)方法进行增强

//使用装饰者模式解决:
/*
	装饰类:BufferedReader
	被装饰类:FileReader
*/
//装饰类    具有相同的父类型
public class BufferedReader extends Reader{
  //被装饰类
  private FileReader fileReader;
  //成员变量
  private int size = 1024*8
  //构造方法
  public BufferedReader(FileReader fileReader,int size){
    this.fileReader = fileReader;
    this.size = size;
  }
  public BufferedReader(FileReader fileReader){
    this.fileReader = fileReader;
  }
   public BufferedReader(){}
  
  //缓冲区对象
  char[] cbuf = new char[size];
  
  //重写read()方法
  public int read(){
    ....
      read(cbuf);
  }
  
  //重写read(char[] cbuf)方法
  public int read(char[] cbuf){
    .....
      
  }
  //重写相关方法 (不需要增强)
  public void close(){
    fileReader.close();//调用原有功能
  }
}

二九、网络编程

三要素:

  • IP地址:设备在网络中的地址,是唯一的标识;
  • 端口:应用程序在设备中唯一的标识;(每一个程序都有自己唯一的端口号)
  • 通信 协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议;
1.IP地址

​ 全称:互联网协议地址,是分配给上网设备的数字标签;常见的IP分类为:ipv4和ipv6;

​ 网络编程:会使用到java.net包下的API

  • java.net.InetAddress类:,此类表示Internet协议(IP)地址;
方法名说明
static InetAddress getByName(String)在给定主机名的情况下获取InetAddress类的对象
String getHostName()获取此IP地址的主机名
String getHostAddress()返回IP地址字符串(以文本表现形式)
//获取InetAddress对象
InetAddress ipObj = InetAddress.getByName("计算机名/IP地址");

//调用方法
String hostname = ipObj.getHostName();
String ip = ipObj.getHostAddress();
  • 常用命令:
    • ipconfig : 查看本机IP地址
    • ping IP地址 :检查网络是否连通
  • 特殊IP地址:
    • 127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用;
2.端口

​ 端口:应用程序在设备中唯一的标识;

​ 端口号:应用程序的唯一标识方式,用两个字节表示的整数值,它的取值范围是0 ~ 65535。其中0 ~ 1023之间的端口号用于一些知名的网络服务或者应用。我们自己使用1024以上的端口号就可以了。

​ ==注意:==一个端口号只能被一个应用程序使用;

3.协议

计算机网络中,连接和通信的规则被称为网络通信协议

  • UDP协议 :用户数据报协议(User Datagram Protocol)
    • 面向无连接协议
    • 数据大小限制在64K以内
    • 数据不安全,容易丢失
    • 传输速度快
  • TCP协议:传输控制协议(Transmission Control Protocol)
    • 面向有连接协议
    • 数据大小没有限制
    • 数据相对安全(相对UDP协议来将)
    • 传输速度慢
4.TCP通信程序

TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socker对象;

TCP通信程序,要求必须有两端程序:

  • 客户端/浏览器
  • 服务端

在Java语言中,想要实现客户端、服务端两个程序之间进行交互,需要依赖Socket对象(利用TCP协议);

  • 客户端程序:Socket
    • 先找到计算机
    • 再找到计算机上的服务端程序
    • 按照TCP协议进行数据发送、接收
      • 底层是基于IO流实现数据的发送、接收
  • 服务端程序:ServerSocket
    • 绑定服务端程序的端口号

java.net.Socket类:客户端

public Socket(String address,int port){
  //参数1:ip地址或主机名
  //参数2:端口号(服务端程序的端口号)
}
  
  OutputStream getOutputStream() //基于Socket对象获取网络输出流(发送数据)
   
  InputStream getInputStream() //基于socket对象获取网络输入流(接收数据)
 //1.创建客户端Socket对象,并连接服务端程序
Socket socket = new Socket("对方计算机IP地址","服务端程序端口号");
//2.基于Socket对象,获取网络输出流(发送数据)
OutputStream netOut = socket.getOutputStream();
//3.发送数据
neOut.write("数据".getBytes());
//4.关闭资源
neOut.close();
socket.close();

java.net.ServerSocket类:服务端

public ServerSocket(int port){
  //构造方法需要绑定一个端口号,port就是端口号
}

//监听客户端连接,并接受连接,返回一个Socket对象
Socket accept() //该方法会一直阻塞直到建立连接
//1.创建服务端ServerSocket对象
ServerSocket ss = new ServerSocket(端口)
//2.监听客户端连接,返回Socket对象
Socket server = ss.accept();
//3.基于Socket对象,获取网络输入流(接收数据)
InputStream netIn = server.getInputStream();
byte[] buf = new byte[1024];
//4.读取数据
int len = netIn.read(buf);
//5.关闭资源
netIn.close();
server.close();
ss.close();

在这里插入图片描述

三十、设计模式

1.单例模式:
  • 解决的问题:保证程序中运行的某个类只能创建一个对象(节省空间、共享数据)

  • 实现步骤:

    • 类中的构造方法私有化
    • 类中提供一个静态的方法(让外部获取到当前类的实例对象)
    • 在当前类中创建一个实例对象(唯一)
  • 分类

    • 饿汉式:随着类的加载就把唯一对象创建完成;
    public class King{
      //静态成员变量:共享数据(内存中独有一份)
      //创建本类的对象(唯一对象)
      private static King king = new King();//静态的内容是随着类的加载而存在
      
      //私有构造方法
      private King(){
        
      }
      //静态方法:返回当前类对象的实例
      public static King getInstance(){
        return king;
      }
    }
    
    • 懒汉式:当需要使用对象时,才会创建;
    public class King{
      //静态成员变量:共享数据(内存中独有一份)
      //创建本类的对象(唯一对象)
      private static King king;//默认值是 null
      
      //私有构造方法
      private King(){
        
      }
      //静态方法:返回当前类对象的实例,保证多线程情况下,对象也是唯一的
      public static synchronized  King getInstance(){
        if(king == null){
          king = new  King();
        }
        return king;
      }
    }
    
2.多例设计模式
  • 多例模式,是一种常用的设计模式之一。通过多例模式可以保证项目中,应用该模式的类有固定数量的实例

    多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。

  • 使用场景:线程池

线程池 = Executors.newFixedThreadPool(3);
  • 实现步骤:
    • 创建一个类,将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象;
    • 在类中定义该类被创建对象的总数量;
    • 在类中定义存放类实例的list集合;
    • 在类中提供静态代码块,在静态代码块中创建类的实例,并添加到list中;
    • 提供获取类实例的静态方法;
package com.itheima.prototype;

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

public class Teacher {
    private String name;

    //私有化构造方法
    private Teacher() {
    }

    //容器:存储固定数量的Teacher对象
    private static List<Teacher> teachers = new ArrayList<>();

    //固定数量
    private static int max = 3;

    //随着类加载时初始化数据
    static {
        for (int i = 0; i < max; i++) {
            teachers.add(new Teacher());
        }
    }

    //静态方法:获取一个Teacher对象
    public static Teacher getInstance() {
        //随机从集合中获取一个对象
        Teacher teacher = teachers.get(new Random().nextInt(teachers.size()));
        return teacher;
    }



    //静态方法
//    public static Teacher getInstance(int count) {
//        int size = teachers.size();//集合大小
//        int result = count - size;
//        if (result > 0) {
//            for (int i = 0; i < result; i++) {
//                teachers.add(new Teacher());
//            }
//        }
//
//        Teacher t = getInstance();
//        return t;
//    }


}

  • 作用:
    • 可以保证项目中有个类有固定个数的实例,在实现需求的基础上,能够提高实例的复用性。
3.工厂方法模式
  • 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用 new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.
  • 需求:定义汽车工厂类,生产各种品牌的车
  • 实现步骤
    • 编写一个Car接口, 提供run方法
    • 编写一个Falali类实现Car接口,重写run方法
    • 编写一个Benchi类实现Car接口,重写run方法
    • 提供一个CarFactory(汽车工厂),用于生产汽车对象
    • 定义CarFactoryTest测试汽车工厂
package com.itheima.factorydesign_demo;

/*
  - 需求:定义汽车工厂类,生产各种品牌的车

  - 实现步骤
      - 编写一个Car接口, 提供run方法
      - 编写一个Falali类实现Car接口,重写run方法
      - 编写一个Benchi类实现Car接口
      =============================================
      - 提供一个CarFactory(汽车工厂),用于生产汽车对象
      - 定义CarFactoryTest测试汽车工厂
 */
public class CarTest {
    public static void main(String[] args) {
        Car benchi = CarFactory.getInstance(Brand.BENCHI);
        System.out.println(benchi);
    }
}

// 汽车接口
interface Car {
    public abstract void run();
}

// 编写一个Falali类实现Car接口,重写run方法
class Falali implements Car {
    public Falali() {
    }

    @Override
    public void run() {
        System.out.println("法拉利破百需要3秒...");
    }
}

// 编写一个Benchi类实现Car接口
class Benchi implements Car {
    @Override
    public void run() {
        System.out.println("奔驰破百需要5秒...");
    }
}

// 汽车品牌枚举
enum Brand {
    BENCHI, FALALI, BAOMA, BINLI, AODI;
}

// 提供一个CarFactory(汽车工厂),用于生产汽车对象
class CarFactory {
    private CarFactory() {
    }

    public static Car getInstance(Brand brand) {
        switch (brand) {
            case FALALI:
                return new Falali();
            case BENCHI:
                return new Benchi();
            default:
                return null;
        }
    }
}

 提供一个CarFactory(汽车工厂),用于生产汽车对象
//class CarFactory{
//    private CarFactory(){}
//
//    public static Car getInstance(String brand) {
//        if(brand.equals("Falali")){
//            return new Falali(10);
//        }else if(brand.equals("Benchi")) {
//            return new Benchi();
//        }else {
//            return null;
//        }
//    }
//}

三一、Junit单元测试

  • 单元测试就是编写测试代码,可以准确、快速地保证程序的正确性,Junit是Java单元测试的框架;
1.常见注解:

JUnit4可以通过注解的方式来标记方法

  • @BeforeClass 全局只会执行一次,而且是第一个运行(标记的方法需要是一个静态无参无返回值方法)
  • @Before 在测试方法运行之前运行(非静态无参无返回值方法)
  • @Test 测试方法(此方法必须是非静态无参无返回值方法),主要用于测试的方法
  • @After 在测试方法运行之后运行(非静态无参无返回值方法)
  • @AfterClass 全局只会执行一次,而且是最后一个运行(标记的方法需要是一个静态无参无返回值方法)
  • @Ignore 忽略此方法

习题

1.求某一年中2月的天数

//利用日历求,很easy
Calendar c = Calendar.getInstance();
c.set(2024,2,1);//其实是3月1日,求这天的前一天即可
c.add(Calendar.DATE,-1);
System.out.println(c.get(Calendar.DATE));

  • 28
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值