基础语法

注释

  • 单行注释:

    1
    //这是单行注释
  • 多行注释:

    1
    2
    /*这是一个
    多行注释*/
  • 文档注释:

    1
    2
    /**这是
    文档注释*/

命名

  • Java 是大小写敏感的
  • 类名:类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写
  • 变量名、方法名、包名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写
  • 常量:所有字母都大写
  • 所有标识符都应该以:字母、美元符或下划线开始。关键字不能用作标识符

修饰符

  • 访问控制修饰符 : public , protected, default, private
    • public : 对所有类可见。使用对象:类、接口、变量、方法
    • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
    • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
    • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
public Y Y Y Y Y
protected Y Y Y Y N
default Y Y Y N N
private Y N N N N
  • 非访问控制修饰符 : final, abstract, static, synchronized,transient,volatile

运算符

算术运算符

  • +、-、*、/、%、++、–

关系运算符

  • ==、!=、>、<、>=、<=

位运算符

  • &、|、^、~、》、《
    • &:如果相对应位都是1,则结果为1,否则为0
    • |:如果相对应位都是 0,则结果为 0,否则为 1
    • ^:如果相对应位值相同,则结果为0,否则为1
    • ~:按位取反运算符翻转操作数的每一位,即0变成1,1变成0。
    • 》:按位右移运算符。左操作数按位右移右操作数指定的位数
    • 《:按位左移运算符。左操作数按位左移右操作数指定的位数

逻辑运算符

  • &&、||、!

赋值运算符

  • =、+=、-=、*=、/=、(%)=、《=、》=、&=、^=、|=

条件运算符(三元运算符)

  • 格式:条件表达式?值1:值2
  • 执行流程:首先计算关系表达式的值,如果为true,返回值1,如果为false,返回值2

instanceof 运算符

  • 该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。

变量类型

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。无默认值在栈内存,变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。有默认值、在堆内存,这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。

数据类型

内置数据类型(基本数据类型)

  • byte:占1字节、范围是-128到127,默认值是0
  • boolean:占1字节、默认值是false
  • short:占2字节、范围是-32768到32767,默认值是0
  • char:占2字节
  • int:占4字节、范围是-2,147,483,648到2,147,483,647,默认值是0
  • float:占4字节、默认值是0.0
  • double:8字节、默认值是0.0
  • long:8字节、默认值是0
  • 注意:
    • 随便写一个整数字面量默认是int类型的,如果想要当成long类型,需要在其后加l/L
    • 随便写一个小数字面量默认是double类型的,如果想要当成float类型,需要在其后加f/F
    • 字符是char要用单引号’ ‘,字符串是String要用双引号” “

引用数据类型

  • 类,接口,数组,String(默认值是null)

类型转换

  • 自动类型转换(向上转型):类型范围小的变量可以直接赋值给类型范围大的变量
    • 注意:byte,short,char是直接转换成int类型参与运算的。
    • 可以调用父类中可访问的所有成员和方法,不可以调用子类的特有成员和方法。
  • 强制类型转换(向下转型):类型范围大的变量赋值给类型范围小的变量
    • 小数变成整数时会把小数部分丢掉
    • 向下转型后,可以调用子类和父类可访问的所有成员
  • 注意:对象转型之后,属性看编译类型。方法看运行类型。

数组

静态初始化数组

  • 定义数组的时候直接给数组赋值

  • 定义格式第一种:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,…};

  • 定义格式第二种(简化):数据类型[] 数组名 = {元素1,元素2,元素3,…}; 也可以把[]放在数组名后面

  • 列如:

    1
    int arr[] = {18,19,20,21};

动态初始化数组

  • 定义数组的时候只确定元素的类型和数组的长度,之后再存入具体的数据

  • 格式:数据类型[] 数组名 = new 数据类型[长度]

  • 列如:

    1
    int arr [] = new int[10];

二维数组

  • 例如

    1
    int arr[][] = new int[2][4];

内存分配

image-20240408093849901

栈内存

  • 存储局部变量,定义在方法里面的变量
  • 栈内存的数据用完就释放
  • 在栈内存中保存的是堆内存空间的访问地址,栈内存会有个地址指向堆内存的地址
  • 栈内存线程不共享

堆内存

  • 存储new出来的的内容(实体,对象)数组在初始化时,会为存储空间添加默认值

  • 每一个new出来的东西都有一个地址值,使用完毕,会在垃圾站回收器空间时被回收

  • 堆内存是线程共享

方法区

  • 它存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。
  • 方法区是线程共享

泛型

  • JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
  • 格式:<数据类型>
  • Java中的泛型是伪泛型

泛型的好处

  • 统一数据类型
  • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常

细节

  • 泛型只能支持引用数据类型
  • 如果不写泛型,默认是Object类型
    • 此时可以往集合添加任意的数据类型。
    • 带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。

泛型的通配符

  • 泛型不具备继承性,但是数据具备继承性
    • 此时要想泛型可以继承,可以使用泛型的通配符
    • ? extends E:表示可以传递E或者E所有的子类类型
    • ? super E:表示可以传递E或者E所有的父类类型

应用场景

  • 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
  • 如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符

泛型类

1
2
3
4
//泛型通常写成:T、E、K、V。
修饰符 class 类名<类型>{

}

泛型方法

1
2
3
修饰符 <类型> 返回值类型 方法名(类型 变量名){

}

泛型接口

1
2
3
修饰符 interface 接口名<类型>{

}

异常与错误

异常的分类

image-20231125145609247

错误:由java虚拟机生成并抛出的异常,程序无法处理,通常指程序中出现的严重问题。

  • Error(错误)是不可查的,而且也常常在应用程序的控制和处理能力之外,因此当Error(错误)出现时,程序会立即奔溃,Java虚拟机立即停止运行,

异常:是指程序本身可以处理的异常(可以向上抛出或者捕获处理)。

  • 编译时异常:程序在编译过程中发现的异常,受检异常
  • 运行时异常:又称非受检异常

异常的处理

  • Java处理异常的默认方式是中断处理
  • 使用try、catch、finaly捕获异常后程序会继续执行;使用throws抛出的异常类型,出现异常后,程序终止

自己处理(try catch finally)

1
2
3
4
5
6
7
8
9
try{
//可能出现问题的代码
}catch(异常类型 异常名称){
//处理异常的代码
}catch(异常类型 异常名称){
//处理异常的代码
}finally{
//释放资源
}
  • catch中声明的异常类型应该和实际抛出的异常类型要么相同要么有继承关系

  • try块中有多行代码,都有可能出现异常信息时,程序执行的时候是从上往下执行的,当碰到异常情况的时候就会跳出try块,从而try块中剩余的代码就不会执行了,

  • finally修饰的代码一定会执行(前提是异常成功进入到了相应的try catch中)

    • try语句中,在执行return语句时,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,在从中取出返回结果,因此,即使finally中对变量x进行了改变,但是不会影响返回结果。它应该使用栈保存返回值。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      public static void main(String[] args) {
      System.out.println(f());
      }
      public static int f(){
      int x=1;
      try{
      x++;
      return x;
      }finally {
      x++;
      System.out.println(x);
      }
      }
      //结果输出为3和2
  • printStackTrace()方法可以打印出详细的异常信息

将异常抛出(throws和throw)

1
2
3
[修饰符] 返回值类型 方法名(参数列表) [throws 异常1,异常2...]{

}
  • throws关键字声明的方法表示此方法不处理异常而交给方法的调用者进行处理
  • 在重写方法时,它所声明的异常范围不能被扩大
  • throws和throw的区别
    • throws用在方法名后面,跟的是异常类名,throw是用在方法体重,跟的异常对象
    • throws可以跟多个异常类名,用逗号隔开throw只能抛出一个异常对象
    • throws表示抛出异常,由该方法的调用者来处理,throw表示抛出异常,由方法体内的语句处理

自定义异常

​ Java中的异常都是Throwable或者Exception或者RuntimeException的子类,那么我们要创建一个自定义的异常,其实就是创建其对应的子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 编写一个分数必须在0-100之间的异常
*/
public class Main{
public static void main(String[] args) {

int score = 101;
if(score<0||score>100)throw new ScoreException("分数不可分法");
}
}

class ScoreException extends RuntimeException{
public ScoreException(){

}
public ScoreException(String msg){
super(msg);
}
}

程序流程

if

1
2
3
4
5
6
7
8
9
if (布尔表达式1){
...
}
else if (布尔表达式2){
...
}
else {
...
}

switch

1
2
3
4
5
6
7
8
9
10
11
12
13
switch (?){
case value1:
...
break;
case value2:
...
break;
case value3:
...
break;
default:
...
}

三元运算符

1
判断条件?值1:值2

for

普通for循环

1
2
3
for(初始化语句;循环条件;迭代语句){
循环体语句;
}

增强for循环

1
2
3
for(元素数据类型 变量名∶数组或者collection集合){
循环体语句;
}

while

普通while循环

1
2
3
while(循环条件){
循环体语句;
}

do-while循环

1
2
3
do{
循环体语句;
}while(循环条件)

方法

方法的定义

1
2
3
4
修饰符 返回值类型 方法名(参数列表){
方法体;
return 返回值;
}
  • 注意事项:
    • 如果方法的返回值类型为void(无返回值),方法内则不能使用return返回数据
    • 如果方法的返回值类型写了具体类型,方法内部则必须使用return返回
    • java的参数传递都是值传递
    • 基本类型的参数传输存储的数据值
    • 引用类型的参数传输存储的地址值

方法重载(Overload)

  • 重载(overloading) 是在同一个类(或子类)里面,方法名字相同,参数列表不同;返回类型和权限修饰符可以相同也可以不同;可以抛出新的或更广的异常
  • 被重载的方法必须改变参数列表(参数个数或类型不一样)

方法重写(Override)

  • 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变
  • 方法名、参数列表和返回类型都相同;权限修饰符要大于父类方法;不能抛出新的或者更广的异常
  • 返回类型可以是父类返回值类型的子类。

重写规则

  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 构造方法不能被重写。
区别点 方法重载 方法重写
参数列表 必须修改 不能修改
返回类型 可以修改 不能修改(可以是子类)
异常 可以修改 不能抛出新的或者更广的异常
访问 可以修改 可以降低限制

构造方法

  • 当一个对象被创建时候,构造方法用来初始化该对象。
  • 构造方法和它所在类的名字相同。
  • 构造方法没有返回值,但可以有return,return在这里只是表示结束,并不是返回的表示。
  • 默认构造方法的访问修饰符和类的访问修饰符相同
  • 不管你是否自定义构造方法,所有的类都有构造方法,因为 Java 自动提供了一个默认构造方法
  • 一旦你定义了自己的构造方法,默认构造方法就会失效

面向对象

类的五大成员:属性、方法、构造器、代码块、内部类

Java代码执行顺序:父类的静态代码块 –> 子类的静态代码块 –> 父类的普通代码块 –> 父类的构造方法

–> 子类的普通代码块 –> 子类的构造方法

封装

封装的概念

  • 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法
  • 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问
  • 适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性
  • 封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段

封装的好处

  • 良好的封装能够减少耦合。
  • 类内部的结构可以自由修改。
  • 可以对成员变量进行更精确的控制。
  • 隐藏信息,实现细节。

封装的步骤

  • 修改属性的可见性来限制对属性的访问(一般限制为private)
  • 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问

继承

继承的概念

  • 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为
  • 继承需要符合的关系是:is-a,父类更通用,子类更具体

继承的格式

  • 在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的
1
2
3
4
class 父类{
}
class 子类 extends 父类{
}

继承的特性

  • 子类拥有父类非 private 的属性、方法
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
  • 子类可以用自己的方式实现父类的方法
  • Java 的继承是单继承,但是可以多重继承
  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)

extends

  • 在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类

implements

  • 使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口

this

  • this 是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针

super

  • super 可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类

super的用法

  • 每个子类构造方法的第一条语句,都是隐含地调用 super()
  1. 普通的直接引用
    • 与 this 类似,super 相当于是指向当前对象的父类,这样就可以用 super.xxx 来引用父类的成员
  2. 引用构造函数
    • super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)
    • this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)

this和super的特性

  • 均不可以在 static 环境中使用
  • this 和 super 不能同时出现在一个构造函数里面

多态

多态的概念

  • 多态是同一个行为具有多个不同表现形式或形态的能力
  • 多态性是对象多种表现形式的体现

多态的好处

  • 消除类型之间的耦合关系
  • 可替换性
  • 可扩充性
  • 接口性
  • 灵活性
  • 简化性

多态存在的三个必要条件

  • 继承

  • 重写

  • 父类引用指向子类对象

多态的实现方式

  • 方法重写
  • 接口
  • 抽象类和抽象方法

多态中成员访问特点

  • 方法:编译看左边,运行看右边
  • 变量:编译看左边,运行也看左边

Static

  • java中的static关键字主要用于内存管理
  • 实用范围:static可以用在变量、方法、代码块和嵌套类
    • 变量:称为类变量、静态变量
    • 方法:称为类方法、静态方法
    • 代码块:称为静态代码块
    • 嵌套类:称为静态内部类
  • 静态成员变量和方法的访问:类名.静态成员变量/方法(推荐)、对象.静态成员变量/方法(不推荐)
  • 特点
    • 静态方法只能访问静态成员和方法,不可以“直接”访问实例成员和方法
    • 随着类的加载而被加载
    • 优先于对象存在,被所有对象共享
    • 局部变量不能被static修饰

final

  • final是一个关键字,可以用于修饰类,成员变量,成员方法

  • 特点

    • 它修饰的类不能被继承。
    • 它修饰的成员变量是一个常量
    • 它修饰的成员方法是不能被子类重写的
  • final修饰成员变量,必须初始化

  • final修饰的常量定义一般都有书写规范,被final修饰的常量名称,所有字母都大写

  • final修饰的变量只能在显示初始化或者构造函数初始化的时候赋值一次,以后不允许更改

抽象类(abstract)

  • 用abstract关键字

特点

  • 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类
  • 抽象类中的抽象方法只是声明,不包含方法体
  • 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法
  • 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类
  • 抽象方法不能使用private、static、final修饰

接口(interface)

  • 用interface关键字

特点

  • 接口不能用于实例化对象
  • 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法
  • 接口不能包含成员变量,除了 static 和 final 变量
  • 接口不是被类继承了,而是要被类实现
  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)
  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)

抽象类和接口的不同点和相同点

  • 相同点

    • 都不能实例化
    • 子类都必须实现抽象方法
  • 不同点

    • 抽象类可以有构造方法,而接口没有

    • 抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法

    • 抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值

    • 一个类只能继承一个抽象类,但可以实现多个接口

    • 抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范

抽象 接口
组成 构造方法、抽象方法、普通方法、常量、变量 常量、抽象方法(jdk8:默认方法、静态方法)
is like

内部类

一个类嵌套在另一个类里面或者一个方法里面,被嵌套的类称为内部类。

成员内部类

定义

  • 成员内部类是最普通的内部类,它的定义为位于另一个类的内部

内访外

  • 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)

  • 当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问

    1
    2
    外部类.this.成员变量
    外部类.this.成员方法

外访内

  • 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问,创建成员内部类的对象

创建对象格式

1
外部类名.内部类名 对象名=new外部类构造器.new内部类构造器

局部内部类

定义

  • 局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

内范外

  • 直接访问外部类的所有成员(包括私有成员)
  • 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)访问

外访内

  • 创建对象再访问

特点

  • 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的,可以使用final.

匿名内部类

定义

  • 本质上是一个没有名字的局部内部类,定义在方法、代码块中

特点

  • 匿名内部类也是不能有访问修饰符和 static 修饰符
  • 大部分匿名内部类用于接口回调。
  • 匿名内部类是唯一一种没有构造器的类。
  • 匿名内部类可以作为方法的实际参数进行传输

创建对象格式

1
new 类|抽象类名|或接口名(){重写方法}

静态内部类

定义

  • 静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。

特点

  • 静态内部类是不需要依赖于外部类的
  • 它不能使用外部类的非static成员变量或者方法

枚举(enum)

  • 定义枚举类的格式:修饰符 enum 枚举名称{第一行都是罗列枚举类实例的名称}
  • 枚举做信息标志和分类:代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用
  • 枚举类都是继承了枚举类型:java.lang.Enum
  • 枚举都是最终类,不可以被继承
  • 构造器的构造器都是私有的,枚举对外不能创建对象
  • 枚举类的第一行默认都是罗列枚举对象的名称的
  • 枚举类相当于是多例模式

集合

集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象

Collection集合

Collection特点

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的

image-20240311202352087

Collection通用API

1
2
3
4
5
6
7
public boolean add(E e)把给定的对象添加到当前集合中
public void clear()清空集合中所有的元素
public boolean remove(E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public boolean isEmpty()判断当前集合是否为空
public int size()返回集合中元素的个数。
public Object[] toArray()把集合中的元素,存储到数组中
  • remove:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除。
  • cotains
    • contains方法在底层依赖equals方法判断对象是否一致的。
    • 如果存的是自定义对象,没有重写equals方j法,那么默认使用object类中的equals方法进行判断,而object类中equals方法,依赖地址值进行判断。
    • 所以,需要在自定义的Javabean类中,重写equals方法就可以了。

List

特点

  • List系列集合:添加的元素是有序、可重复、有索引

List集合特有方法

1
2
3
4
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
  • add:添加后,原来索引处上的元素会依次往后移

ArrayList

底层原理

  • ArrayList底层是基于数组实现的,根据索引定位元素快,增删需要做元素的移位操作
  • 利用空参创建的集合,在底层创建一个默认长度为0的数组,数组名为elementData
  • 添加第一个元素时,底层会创建一个新的长度为10的数组
  • 存满时,会扩容1.5
  • 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准

LinekdList

底层原理

  • 底层数据结构是双向链表查询慢,增删快,但是如果操作的是首尾元素,速度也是非常快的

image-20240313110643515

-

特有API

image-20240313110333982

Set

  • HashSet:无序、不重复、无索引
  • TreeSet:按照大小默认升序排序、不重复、无索引

Map集合

Map特点

  • Map属于双列集合,双列集合一次需要存一对数据,分别为键和值
  • 键和值是一 一对应的,每一个键只能找到自己对应的值
  • Map集合的键是不重复的,无索引
  • 键+值这个整体我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”
  • Map集合的键值对都可以为null

image-20240313151307847

Map通用API

1
2
3
4
5
6
7
V put(K key,v value)添加元素
v remove(0bject key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(object key)判断集合是否包含指定的键
boolean containsValue(0bject value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数
  • put:添加时,如果键已经存在,就会覆盖原来,并返回原理的值

HashMap

特点

  • 元素按照键是无序,不重复,无索引
  • HashMap跟HashSet底层原理是一模一样的,都是哈希表结构。依赖hashCode方法和equals方法保证键的唯一
  • 如果键存储的是自定义对象,需要重写hashCode和equals方法如果值存储自定义对象,不需要重写hashCode和equals方法

LinkedHashMap

特点

  • 元素按照键是有序,不重复,无索引
    • 这里的有序指的是保证存储和取出的元素顺序一致
  • 底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序

TreeMap

特点

  • TreeMap跟TreeSet底层原理一样,都是红黑树结构的。
  • 元素按照键是不重复、无索引、可排序
    • 可排序是对键进行排序。
      • 方法一:实现Comparable接口,指定比较规则。
      • 方法二:
      • 创建集合时传递Comparator比较器对象,指定比较规则。
    • 默认按照键的从小到大进行排序,也可以自己规定键的排序规则

遍历方式

遍历方式的选择

遍历方式 选择情况
迭代器 在遍历的过程中需要删除元素,请使用迭代器。
列表迭代器 在遍历的过程中需要添加元素,请使用列表迭代器。
增强for循环 仅仅想遍历,那么使用增强for或Lambda表达式。
Lambda表达式 仅仅想遍历,那么使用增强for或Lambda表达式。
普通for 如果遍历的时候想操作索引,可以用普通for。

Collection

迭代器

特点

  • 在Java中的代表是***lterator()***,迭代器是集合的专用遍历方式
  • 迭代器不依赖索引
  • 迭代器就好比是一个箭头,默认指向集合的0索引处

常用方法

1
2
3
boolean hasNext() //询问当前位置是否有元素存在,存在返回true ,不存在返回false
E next() //获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界
void remove() //删除当前获取到的元素

迭代器遍历格式

1
2
3
4
Iterator<集合中元素的类型> iterator = list.iterator();
while(iterator.hasNext()){
集合中元素的类型 变量名 = iterator.next();
}

细节和注意事项

  • 当指到最后一个位置时,如果还继续用next,就会报错NoSuchElementException
  • 迭代器遍历完毕,指针不会复位循环中
  • 在循环中最好不要用两个或以上次数的next方法
  • 迭代器遍历时,不能用集合的方法进行增加或者删除,可以用迭代器提供的remove删除

增强for遍历

特点

  • 增强for的底层就是迭代器,为了简化迭代器的代码书写的。
  • 它是JDK5之后出现的,其内部原理就是一个lterator迭代器
  • 所有的单列集合和数组才能用增强for进行遍历。

增强for遍历格式

1
2
3
4
for(集合中元素的数据类型 变量名:数组或者集合){
......
}

细节和注意事项

  • 修改增强for中的变量,不会改变集合中原本的数据。

Lambda表达式遍历

lambda表达式特点

  • ()->{}

Lambda表达式遍历格式

1
2
3
4
5
6
集合名.forEach((集合中元素的数据类型 变量名)->{
循环体...
});

//如果循环体只有一行代码,则可以简化成以下形式
集合名.forEach(变量名 -> 循环体);

列表迭代器(List特有)

特点

  • listIterator()
  • List集合特有的遍历方式

特有方法

1
void add(E e)//将指定元素插入列表

Map

键找值

1
2
3
4
5
6
7
8
9
//1.通过keySet方法,得到一个包含所有key的单列集合
Set<String> keySet = map.keySet();

//2.遍历包含键的单列集合
for (String key : keySet) {
//3.通过get方法得到键对应的值
Integer value = map.get(key);
System.out.println(key+":"+value);
}

键值对

1
2
3
4
5
6
7
8
9
//1.通过entrySet方法,得到一个包含所有键值对的单列集合
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
//2.遍历包含键值对的单列集合
for (Map.Entry<String, Integer> entry : entrySet) {
//3.通过getKey()、和getValue()得到对应的键和值
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key+":"+value);
}

简化版

1
2
3
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey()+":"+entry.getValue());
}

Lambda表达式

1
2
3
map.forEach((key,value)->{
System.out.println(key+":"+value);
});

Stream流

  • 作用:结合了Lambda表达式,简化集合、数组的操作

不可变集合

  • 不可变是指不能增删改,只能查

不可变list集合

  • List.of(E….elements);

不可变Set集合

  • Set.of(E….elements)
  • 注意,Set是无重复的。

不可变Map集合

  • Map.of( K k1,V v1,K k2, V v2…)
  • 注解,key是不能重复的
  • 最多只能传20个参数,也就是10个键值对。
  • 如果要传超过10个以上的键值对可以用Map.copyOf(Map map)方法,注意,此方法在jdk9之后才有

Stream的使用步骤

  1. 先得到一条Stream流,并把数据放上去

    获取方式 方法名 说明
    单列集合 default Stream stream() Collection中的默认方法
    双列集合 无(可以用keySet和entrySet) 无法直接使用stream流
    数组 public static Stream stream(T[]array) Arrays工具类中的静态方法
    一堆零散数据 public static Stream of(T…values) Stream接口中的静态方法
    • 单列集合:list.stream()
    • 双列集合:map.keySet().stream()
    • 数组:Arrays.stream(arr)
    • 零散数据:Stream.of(T…values)
  2. Stream流常见的中间方法

    名称 说明
    Stream filter(Predicate<? super T> predicate) 过滤
    Stream limit(long maxSize) 获取前几个元素
    Stream skip(long n) 跳过前几个元素
    Stream distinct() 元素去重,依赖(hashCode和equals方法)
    static Stream concat(Stream a, Stream b) 合并a和b两个流
    Stream map(Function<T ,R> mapper) 转换流中的数据类型
    • 注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
    • 注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据
  3. Stream流的终结方法

    名称 说明
    void forEach(Consumer action) 遍历
    long count() 统计
    toArray() 收集流中的数据,放到数组中
    **collect(**Collector collector) 收集流中的数据,放到集合中

文件

File类

  • 作用:获取文件本身的信息,删除文件,创建文件等功能

  • File类创建对象:

    • File f = new File(pathname);
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45

      - pathname支持绝对路径(从盘符开始),也支持相对路径(不带盘符)
      - 相对路径默认直接到当前工程下的目录寻找文件
      - 路径用双反斜杠或斜杠/

      - 常用API:

      - getAbsolutePath():获取文件的绝对路径
      - getPath():获取文件定义时所用的路径
      - getName():获取文件的名称(带后缀)
      - length():获取文件大小(**字节**个数)
      - lastModified():获取文件的最后修改时间
      - isFile()和isDirectory():判断文件是文件还是文件夹
      - createNewFile():创建新文件
      - delete():删除文件
      - mkdir():创建一级目录
      - mkdirs():创建多级目录
      - 常用来遍历的API:
      - list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
      - listFiles():获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回

      ## **IO流**

      - 作用:读写文件数据
      - 分为两大类:字节流和字符流

      ### 编码和解码

      - 英文,数字,符号一般占1个字节
      - GBK中,中文占2个字节
      - UTF-8中,中文占3个字节

      - 编码
      - str.getBytes(编码格式):编码格式默认是utf-8,返回类型是一个byte类型的数组
      - 解码
      - new String(bytes,解码格式):解码格式默认是utf-8,返回类型是字符串

      ### 字节流

      - 字节输入流(InputStream)

      - InputStream是一个抽象类,其实现类常用FileInputStream

      ```java
      InputStream inputStream = new FileInputStream(path);
    • 常用API

      • read():每次读取一个字节返回,返回的是该字节的编码,如果字节已无可读内容则返回-1

      • **readAllBytes()**:读取全部字节,返回的是一个byte类型的数组。

        1
        System.out.println(new String(inputStream.readAllBytes()));
  • 字节输出流(OutputStream)

    • OutputStream是一个抽象类,其实现类常用FileOutputStream

      1
      OutputStream os =new FileOutputStream(path,append);
      • 默认会清空之前的数据,如果想追加的话就在括号里加个true
    • 常用API

      • write(int a):写入一个字节

      • **write(byte[] bytes)**:写一个字节数组

        1
        outputStream.write("卢家业呀".getBytes());
      • flush():刷新数据

      • close():关闭流,关闭包含刷新,关闭后流不可以继续使用了

字符流

  • 字符输入流(Reader)

    • Reader是一个抽象类,其实现类常用FileReader

      1
      Reader reader = new FileReader(path);
    • 常用API

      • read():每次读取一个字节返回,返回的是该字节的编码,如果字节已无可读内容则返回-1
  • 字符输出流(Writer)

    • Writer是一个抽象类,其实现类常用FileWriter

      1
      Writer writer = new FileWriter(path,append);
    • 常用API

      • write()

        1
        writer.write(str);
      • flush():刷新数据

      • close():关闭流,关闭包含刷新,关闭后流不可以继续使用了

多线程

基础概念

  • 进程:进程是程序的基本执行实体
  • 线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 多线程的作用:可以让程序同时做多件事情,提高运行效率
  • 并发:在同一时刻,有多个指令在单个CPU上交替执行
  • 并行:在同一时刻,有多个指令在多个CPU上同时执行

线程的创建方式

主线程应该放在子线程之后

  • 方式一:继承Thread类

    1. 定义一个类继承Thread
    2. 重写run方法,里面是定义线程以后要干啥
    3. 创建一个该类的对象
    4. 调用start方法启动线程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //创建线程方式一:
    public class ThreadLearn extends Thread{//1.继承Thread类
    public void run(){//2.重写run方法
    for (int i = 0; i < 5; i++) {
    System.out.println("子线程正在输出"+i);
    }
    }

    public static void main(String[] args) {
    Thread thread = new ThreadLearn();//3.创建一个该类的对象
    thread.start();//4.调用start方法启动线程
    for (int i = 0; i < 5; i++) {
    System.out.println("主线程正在输出"+i);
    }
    }
    }
  • 方式二: 实现Runnable接口

    1. 实现Runnable接口
    2. 重写run方法
    3. 创建一个该类对象
    4. 把该对象交给Thread线程对象处理
    5. 调用start方法,启动线程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //创建线程方式二:实现Runnable接口
    public class Thread2 implements Runnable{//1.实现Runnable接口
    @Override
    public void run() {//2.重写run方法
    for (int i = 0; i < 5; i++) {
    System.out.println("子线程1正在输出"+i);
    }
    }
    public static void main(String[] args) {
    Runnable runnable = new Thread2();//3.创建一个该类的对象
    Thread thread1 = new Thread(runnable);//4.把该对象交给Thread线程对象处理
    thread1.start();//5.调用start方法,启动线程
    }
    }
    //也可以用匿名内部类或lambda表达式的方法来简化代码
  • 方法三:实现Callable接口,结合FutureTask完成(改方法可以获取多线程运行的结果)

    1. 实现Callable接口,应申明线程任务执行完毕后的结果的数据类型
    2. 重写call方法
    3. 创建该类对象
    4. 把该对象交给FutureTask对象
    5. 再把FutureTask对象交给Thread对象处理
    6. 调用start方法
    7. 可通过调用FutureTask对象的get方法获取线程执行完毕后的结果

三种实习方式对比

image-20231120210301967

多线程的书写套路

  1. 循环
  2. 同步代码块(同步方法)
  3. 判断共享数据是否到了末尾(到了末尾就break)
  4. 判断共享数据是否到了末尾(没到末尾就执行核心逻辑)

Thread常用方法

  • setName(String name):给线程命名
    • 如果没有给线程命名,它也有默认的名字:Thread-X。
    • Thread的构造方法也可以给线程命名
  • getName():获取线程的名字
  • currentThread():获取当前的线程对象
  • sleep(long time): 让线程休眠指定的时间,单位为毫秒
  • setPriority(int newPriority): 设置线程的优先级
    • 默认是5,最小是1,最大是10
    • 线程的优先级越高只是会让它执行的概率变高。
  • getPriority(): 获取线程的优先级
  • setDaemon(boolean on): 设置为守护线程
    • 当其他的非守护线程执行完毕之后,守护线程会陆续结束
  • yield(): 出让/礼让线程
    • 尽可能的让线程交替执行
  • join(): 插入/插队线程
    • 让某个线程执行完后再执行其他线程

线程的生命周期和安全问题

生命周期

image-20231118180413483

安全问题

  • 线程在执行代码的时候,cpu的执行权,随时有可能被其他线程抢走(随机性)
    • 解决方式:把操作共享数据的代码锁起来
  • 死锁问题: 一种线程间互相等待的状态,导致程序无法正常运行

线程的六种状态

  • 新建:至今尚未启动的线程处于这种状态。
  • 就绪start:正在Java虚拟机中执行的线程处于这种状态。
  • 阻塞:受阻塞并等待某个监视器锁的线程处于这种状态。
  • 无限期等待wait:无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
  • 计时等待sleep:等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
  • 结束:已退出的线程处于这种状态。

synchronized锁

  • 同步代码块

    • 格式如下

      1
      synchronized(锁对象){操作共享数据的代码}
    • 特点

      • 锁默认打开,有一个线程进去了,锁自动关闭
      • 里面的代码全部执行完毕,线程出来,锁自动打开
    • 注意

      • 锁对象是任意的一个对象,它一定要是唯一的
  • 同步方法

    • 格式如下

      1
      修饰符 synchronized 返回值类型 方法名 (方法参数){...}
    • 特点

      • 同步方法是锁住方法里面所有的代码
      • 锁对象不能自己指定
    • 注意

      • 非静态的方法的锁对象是this,静态的方法的锁对象是当前的字节码文件对象
      • StringBuilder的方法是线程不安全的,StringBuffer的方法是线程安全的

lock锁

  • 注意
    • Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
    • Lock中提供了获得锁lock() 和 释放锁unlock()的方法
    • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
    • ReentrantLock的构造方法ReentrantLock():创建一个ReentrantLock的实例

等待唤醒机制

常见方法

  • wait():当前线程等待,直到被其他线程唤醒
  • notify():随机唤醒单个线程
  • notifyAll():唤醒所有线程

阻塞队列

  • ArrayBlockingQueue:底层是数组,有界的
  • LinkBlockingQueue:底层是链表,石界但不是真正的干界,最大为int的最七值。
  • put数据时:放不进去,会等着,也叫做阻塞。
  • take数据时:取出第一个数据,取不到会等着,也叫做阻塞。

线程池

原理

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程任务就会排队等待

代码实现

  1. 创建线程池
    • 方法一(创建一个无上限的线程池):ExecutorService pool = Executors.newCachedThreadPool();
    • 方法二(创建一个有上限的线程池):ExecutorService pool = Executors.newFixedThreadPool(int i);
  2. 提交任务
    • pool.submit(new MyThread());
  3. 销毁线程池
    • pool.shutdown();

自定义线程池

  • ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
    • 参数一:核心线程数量,不能小于0
    • 参数二:最大线程数,不能小于e,最大数量>=核心线程数量
    • 参数三:空闲线程最大存活时间,不能小于0
    • 参数四:时间单位,用TimeUnit指定
    • 参数五:任务队列,不能为null
    • 参数六:创建线程工厂,不能为null
    • 参数七:任务的拒绝策略,不能为null

ThreadLocal

介绍

  • ThreadLocal并不是一个Thread,而是Thread的局部变量
  • 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
  • ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

常用方法

  • set:设置当前线程的线程局部变量的值
  • get:返回当前线程所对应的线程局部变量的值

基础知识

  • Java的线程默认是采用抢占式调度(随机)的。可以通过设置优先级的方式,使线程抢占的概率更高。
  • 堆内存是唯一的,而每个线程都有自己的栈内存

Java高级

反射

认识反射

  • 反射就是∶加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)
  • 简单来说就是:获取类的信息、操作它们。

反射的引用场景

  • 反射的作用
    • 可以得到一个类的全部成分然后操作。
    • 可以破坏封装性。
  • 主要用途:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能。

获取类

获取类的三种方式

  1. Class c1=类名.class
  2. 调用Class提供方法:public static Class forName(String package);
    • package是全类名(文件在项目中的位置)
  3. object提供的方法:public Class getClass();
    • 如:Class c3=对象.getClass();
  • 获取类名之后就可以获取类中的成员变量、方法、构造器了

获取构造器

获取构造器的方法

  1. Constructor<?>[] getconstructors():获取全部由public修饰的构造器
  2. Constructor<?>[] getDeclaredConstructors:获取全部构造器
  3. Constructor<T> getconstructor(class<?>… parameterTypes):获取某个由public修饰的构造器
  4. Constructor<T> getDeclaredConstructor(class<?>… parameterTypes):获取某个构造器

获取构造器的作用:依然是初始化对象返回

  • T newInstance(Object… initargs):调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回,只能调用非私有的构造器
  • public void setAccessible(boolean flag):设置为true,表示禁止检查访问控制(暴力反射),使得私有的构造器也能调用

获取类的成员变量

获取成员变量的方法

  1. public Field[] getFields():获取类的全部成员变量(只能获取public修饰的)
  2. public Field[] getDeclaredFields():获取类的全部成员变量(只要存在就能拿到)
  3. public Field getField(string name):获取类的某个成员变量(只能获取public修饰的)
  4. public Field getDeclaredField(string name):获取类的某个成员变量(只要存在就能拿到)

获取成员变量的作用:依然是赋值和取值

  • void set(object obj, object value) :赋值
  • object get(object obj):取值
  • public void setAccessible(boolean flag):设置为true,表示禁止检查访问控制(暴力反射)

获取类的成员方法

获取类的成员方法的方式

  1. Method[ ] getMethods():获取类的全部成员方法(只能获取public修饰的)
  2. Method[] getDeclaredMethods():获取类的全部成员方法(只要存在就能拿到)
  3. Method getMethod(String name,class<?>… parameterTypes):获取类的某个成员方法(只能获取public修饰的)
  4. Method getDeclaredMethod(String name,class<?>… parameterTypes):获取类的某个成员方法(只要存在就能拿到)

成员方法的作用:依然是执行

  • public object invoke(object obj, object. . . args):触发某个对象的该方法执行。
  • public void setAccessible(boolean flag):设置为true,表示禁止检查访问控制(暴力反射)

注解

认识注解

注解(Annotation)

  • 就是Java代码里的特殊标记,比如: @Override、@Test等
  • 作用是让其他程序根据注解信息来决定怎么执行该程序。
  • 注解可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处。
  • 注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。
  • @注解(…):其实就是一个实现类对象,实现了该注解以及Annotation接口。

自定义注解

格式

1
2
3
publiuc @interface 注解名称{
public 属性类型 属性名() default 默认值;
}
  • 如果注解中只有一个value属性,使用注解时,value名称可以不写。

元注解

  • 元注解的作用:用来修饰注解的注解。

@Target(ElementType.XXX)

  • 作用:声明被修饰的注解只能在哪些位置使用
  • TYPE:类接口
  • FIELD:成员变量
  • METHOD:成员方法
  • PARAMETER:方法参数
  • CONSTRUCTOR:构造器
  • LOCAL_VARIABLE:局部变量

@Retention(RetentionPolicy.XXX)

  • 作用:声明注解的保留周期
  • SOURCE:只作用在源码阶段,字节码文件中不存在。
  • CLASS(默认值):保留到字节码文件阶段,运行阶段不存在。
  • RUNTIME(开发常用):一直保留到运行阶段。

注解的解析

  • 判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。

  • 要解析谁上面的注解,就应该先拿到谁(通过反射)

  • AnnotatedElement接口提供了解析注解的方法

    • public Annotation[] getDeclaredAnnotations():获取当前对象上面的注解。
    • public T getDeclaredAnnotation(class annotationclass):获取指定的注解对象
    • public boolean isAnnotationPresent(class annotationclass):判断当前对象上是否存在某个注解

动态代理

认识动态代理

  • 对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责。
  • 对象有什么方法想被代理,代理就一定要有对应的方法

Net

基础知识

  • 网络通信三要素:IP地址、端口、协议
  • IP地址:设备在网络中的地址,是唯一的标识
    • 常见的IP分类为:IPv4(4字节(32位))和IPv6(16字节(128位))
    • DNS服务器:也称域名解析器,可将域名解析成IP地址
    • IP地址的形式:公网地址、和私有地址(局域网使用)
    • 局域网(以太网):专门为组织机构内部使用,常以192.168.开头,范围是192.168.0.0—192.168.255.255
    • IP常用命令:ipconfig(查看本机IP地址),ping IP地址(检查网络是否连通)
    • 本机IP地址:127.0.0.1或localhost
  • 端口:应用程序在设备中唯一的标识
    • 端口号:标识正在计算机设备上运行的进程(程序),被规定为一个16位的二进制,范围是0~65535。
    • 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用271
    • 注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占用8080,MySQL占用3306)
    • 动态端口:49152到65535。之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。
    • 注意:一个设备中不能出现两个程序的端口号一样
  • 协议:数据在网络中传输的规则
    • OSI参考模型:世界互联协议标准,全球通信规范,由于此模型过于理想化,未能在因特网上进行广泛推广。
    • TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
      • 应用层:HTTP、FTP、DNS、SMTP…
      • 传输层:TCP、UDP…
      • 网络层:IP、ICMP…
      • 数据链路层+物理层:物理寻址、比特流…
    • 传输层的两个常见协议
      • TCP:传输控制协议(连接,可靠)
      • UDP:用户数据报协议(不连接,不可靠)

InetAddress

  • 此类表示Internet协议(IP)地址
  • 常用API
    • InetAddress getLocalHost():返回本主机的地址对象
    • InetAddress getByName(String host):得到指定主机的IP地址对象,参数是域名或者IP地址
    • String getHostName():获取此IP地址的主机名(域名)
    • String getHostAddress():返回IP地址字符串
    • boolean isReachable(int timeout):在指定毫秒内连通该IP地址对应的主机,连通放回true

UDP通信

  • DatagramSocket:UDP的发送端和接收段对象

    • void send(DatagramPacket dp):发送数据包
    • void receive(DatagramPacket dp):接收数据包
  • DatagramPacket:数据包对象

    • 发送端对象:DatagramPacket(byte buf[], int length,InetAddress address, int port)
    • 接收段对象:DatagramPacket(byte buf[], int length)
  • 列子

    • 发送端
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Client1 {
    public static void main(String[] args) throws Exception{
    System.out.println("客户发送端========================");
    //1.创建发送端对象(发送端自带默认端口号)
    DatagramSocket client = new DatagramSocket();

    //2创建一个数据包对象封装数据
    byte[] sData = "你好,我是JY".getBytes();//要发送的数据
    /*
    参数一:要发送的数据
    参数二:发送数据的大小
    参数三:接收端的IP地址
    参数四:接收端的端口
    * */
    DatagramPacket packet = new DatagramPacket(sData, sData.length, InetAddress.getLocalHost(),8888);

    //3.发送数据出去
    client.send(packet);
    }
    }
    • 接收端
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Serverg1 {
    public static void main(String[] args) throws Exception{
    System.out.println("服务接受端=======================");
    //1.创建接收端对象,注册端口号
    DatagramSocket server = new DatagramSocket(8888);

    //2.创建一个数据包对象接收数据
    byte [] rData= new byte[1024*64];
    DatagramPacket packet = new DatagramPacket(rData,rData.length);

    //3.等待接收数据
    server.receive(packet);

    //4.获取接收数据的大小,并输出
    int dataLenth = packet.getLength();
    String strData = new String(rData,0,dataLenth);
    System.out.println("收到了:"+strData);
    }
    }

TCP通信

JDBC

JDBC简介

  • 概念:JDBC就是使用Java语言操作关系型数据库的一套API,全称:( Java DataBase Connectivity ) Java数据库连接。
  • 本质:官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。

快速入门步骤

  1. 创建工程,导入驱动jar包

  2. 注册驱动

    1
    Class.forName("com.mysql.jdbc.Driver");//在mysql 5之后的驱动jar包可以不做这一步
  3. 获取连接

    1
    Connection con = DriverManager.getConnection(url,username,password);
  4. 定义sql语句

    1
    String sql = "要执行的sql语句"
  5. 获取执行sql对象

    1
    Statement stmt = con.createStatement();
  6. 执行sql

    1
    stmt.executeQuery(sql);
  7. 处理返回结果

  8. 释放资源

JDBC API详解

DriverManager

  • 作用:注册驱动、获取数据库连接
  1. 注册驱动:

    1
    Class.forName("com.mysql.jdbc.Driver");//在mysql 5之后的驱动jar包可以不做这一步
  2. 获取连接:

    • Connection getConnection(Strng url,String user,String password)

    • 参数url语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…

    • 可配置useSSL=false参数,禁用安全连接方式,解决警告提示。

Connection

  • 作用:获取执行sql的对象、管理事务
  1. 获取执行sql的对象
    • 普通执行sql对象:Statement createStatement()
    • 预编译SQL的执行SQL对象:防止SQL注入:PrepareStatement prepareStatement(sql)
    • 执行存储过程的对象:CallableStatement prepareCall(sql)
  2. 管理事务
    1. 开启事务:setAutoCommit(boolean autoCommit):true为自动提交事务;false为手动提交事务
    2. 提交事务:commit()
    3. 回滚事务:rollback()

Statement

  • 作用:执行sql语句

  • 执行DML、DDL语句:int executeUpdate(sql)

    • DML语句返回值:DML语句影响的行数(DML)
    • DDL语句返回值:DDL语句执行后,执行成功也可能返回0
  • 执行DQL语句:ResultSet executeQuery(sql)

    • 返回值:ResultSet结果集对象

ResultSet

  • 作用:封装DQL查询语句的结果
  • boolean next()
    • 常跟着循环一起使用
    • 改方法会将光标从当前位置向下移动一行,并判断当前行是否为有效行
  • xxx getXxx(参数)
    • xxx指的是数据类型,根据需求写。
    • 如:int getInt(参数)和String getString(参数)

PreparedStatement

  • 作用:预编译SQL并执行SQL语句
  1. 获取PreparedStatement对象

    1
    PrepareStatement prst = con.prepareStatemnt(sql); //sql语句中的参数值用?代替
  2. 设置参数值

    1
    2
    prst.setXxx(参数1,参数2);
    //Xxx指的是数据类型,参数1是sql语句中?的位置编号,参数2是要设给?的值
  3. 执行sql

    1
    prst.executeUpdate()或executeQuery();

数据结构

介绍

  • 数据结构是计算机底层存储、组织数据的方式。
  • 是指数据相互之间是以什么方式排列在一起的。
  • 数据结构是为了更加方便的管理和使用数据,需要结合具体的业务场景来进行选择。
  • 一般情况下,精心选择的数据结构可以带来更高的运行或者存储效率。

image-20240312204540277

特点

  • 先进后出,后进先出
  • 数据进入栈模型的过程称为:压/进栈
  • 数据离开栈模型的过程称为:弹/出栈

队列

image-20240312204620758

特点

  • 先进先出,后进后出
  • 数据从后端进入队列模型的过程称为:入队列
  • 数据从前端离开队列模型的过程称为:出队列

数组

特点

  • 查询快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
  • 删除慢:要将原始数据删除,同时后面每个数据前移。
  • 添加慢:添加位置后的每个数据后移,再添加元素。

链表

单向链表

image-20240312211111795

双向链表

image-20240312211143209

特点

  • 链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
  • 链表查询慢,无论查询哪个数据都要从头开始找。
  • 链表增删相对快

二叉树

二叉查找树

平衡二叉树

算法篇

回溯

理论基础

  • 回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。
  • 因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
  • 回溯法解决的问题都可以抽象为树形结构集合的大小就构成了树的宽度,递归的深度,都构成的树的深度。递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯三部曲

  1. 回溯函数模板返回值以及参数
  2. 回溯函数终止条件
  3. 回溯搜索的遍历过程

常见问题

  • 组合问题
  • 排列问题
  • 切割问题
  • 子集问题
  • 棋盘问题(N皇后、解数独)

模板

1
2
3
4
5
6
7
8
9
10
11
12
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}

排列问题

image-20240405221811922

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.ArrayList;
import java.util.List;

/**
* 输入: [1,2,3]
* 输出: [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
*/
public class paiLie1 {

static List<List<Integer>> res = new ArrayList<>();
static List<Integer> listpath = new ArrayList<>();
static boolean used[];

public static void main(String[] args) {
int[] arrs = {1,2,3};
used=new boolean[arrs.length];
backTracking(arrs);
System.out.println(res);
}

private static void backTracking(int[] arrs) {
if(arrs.length==listpath.size()){
res.add(new ArrayList<>(listpath));
return;
}

for (int i = 0; i < arrs.length; i++) {
if(used[i]){
continue;
}
used[i]=true;
listpath.add(arrs[i]);
backTracking(arrs);
listpath.remove(listpath.size()-1);
used[i]=false;
}
}


}

组合问题

image-20240405223050408

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
* 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
*
* 示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
*/
public class zuHe1 {
static List<List<Integer>> result= new ArrayList<>();
static List<Integer> path= new ArrayList<>();

public static void main(String[] args) {
result.clear();
path.clear();

Scanner sc = new Scanner(System.in);
int n =sc.nextInt();
int k = sc.nextInt();
backtracking(n,k,1);
System.out.println(result);
}

public static void backtracking(int n,int k,int startIndex){
if (path.size() == k){
result.add(new ArrayList<>(path));
return;
}
for (int i =startIndex;i<=n;i++){
path.add(i);
backtracking(n,k,i+1);
path.remove(path.size()-1);
}
}

}




贪心

理论基础

  • 贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
  • 贪心没有套路,说白了就是常识性推导加上举反例。

贪心四部曲

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

动态规划

理论基础

  • 动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
  • 动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的

动态规划五部曲

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

常见问题

  • 背包问题
  • 路径问题
  • 爬楼梯问题
  • 股票问题
  • 子序列问题

字符串

重复子串

问题:判断一个字符串,能否由它的一个子串重复多次构成,如:

  • 输入”abab“,输出true
  • 输入”aba“,输出false

解法一(移动匹配法)

  • 将原字符串*2,再去头去尾得到一个新的字符串,如果新字符串还包含原字符串则为true,否则为false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader bu = new BufferedReader(new InputStreamReader(System.in));
String str = bu.readLine();
String str2= str+str;
str2=str2.substring(1,str2.length()-1);

if(str2.contains(str)){
System.out.println(true);
}else{
System.out.println(false);
}
}
}

解法二(KMP算法)

字符串替换

字符串替换可以用正则表达式来做如:

1
str=str.replaceAll("a{10,}", "b"); //用b来替换str中全部超过9个连续的a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
String str1 = "jybzzz";
//1.反转字符串:用StringBuilder类中的reverse()
StringBuilder str2 = new StringBuilder(str1).reverse();

//2.1字符串的比较:compareTo(str)
//先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值。
String str3="help";
String str4="hell";
System.out.println(str3.compareTo(str4));//打印4,p与l的字符大小差值
//如果前k个字符都相等,只是长度不同,那么返回两个字符串长度差值。
String str5="help me";
System.out.println(str3.compareTo(str5));

//3.字符串的查找
//charAt(int index):此方法返回index位置上的字符
System.out.println(str1.charAt(3));
//indexOf(String str):此方法为返回str第一次出现的位置
System.out.println(str1.indexOf("zzz"));

//4.字符串的转换
//转数字:Integer.parseInt(str)
String str6="12345";
System.out.println("字符串转数字:"+Integer.parseInt(str6));
//转数组:toCharArray()
char[] chars = str1.toCharArray();
System.out.println("字符串转数组:"+Arrays.toString(chars));

//5.字符串的替换
String str7="hello";
//replaceAll(str1,str2):将字符串中的全部str1替换成str2
System.out.println(str7.replaceAll("l","o"));
//replaceAll(str1,str2):将字符串中的第一个str1替换成str2
System.out.println(str7.replaceFirst("l","o"));

//6.字符串拆分:split(str)
String str8="hello,world";
String[] os = str8.split(",");
System.out.println(Arrays.toString(os));


//7.字符串截取:substring(int start,int end) 注:左闭右开
System.out.println(str1.substring(1,str1.length()));

//8.大小写转换
String str9="JyBzZz";
//将字符串全部变为小写:toLowerCase()
System.out.println(str9.toLowerCase());
//将字符串全部变为大写:toUpperCase()
System.out.println(str9.toUpperCase());

//9.字符串去空格
//去掉字符串中的左右空格,保留中间空格:trim()
String str10 = " j y b z z z ";
System.out.println(str10.trim());
//去掉全部空格:replaceAll(" ","")
System.out.println(str10.replaceAll(" ",""));

类型转换

  • 字符串转数字
    • Interger.parseInt(str)
  • 数组转List集合
    • Arrays.asList(arr)
  • 集合转数组
    • arr.toArray()

排序

  • 数组

    • 升序:Arrays.sort(array);
  • 集合

    • 升序:Collections.sort(list);
    • 降序(先升序再reverse):Collections.reverse(list);
  • 自定义对象按某个属性排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student("Alice", 22));
    studentList.add(new Student("Bob", 20));
    studentList.add(new Student("Charlie", 25));
    studentList.add(new Student("David", 18));

    Collections.sort(studentList, new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
    return s1.getAge()-s2.getAge();
    }
    });

数组集合

数组

  • 数组截取

    1
    System.arraycopy(原始数组,截取的开始位置,截取后的新数组,0,截取的长度);
  • 数组求和

    1
    Arrays.stream(数组名).sum()
  • 数组求最大值最小值平均值

    1
    2
    3
    最大值:Arrays.stream(arr).max().getAsInt()
    最小值:Arrays.stream(arr).min().getAsInt()
    平均值:Arrays.stream(arr).average().getAsDouble()

Math

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 * 取整运算
*/
// 1、四舍五入round
int num1=Math.round(3.4f);
System.out.println(num1);
// 2.String.format()
String.format("%.要保留的小数位数f",num);

// 2.向上取整ceil

double num2 = Math.ceil(3.5f);
System.out.println((int)num2);

// 3.向下取整floor
double num3 = Math.floor(3.5);
System.out.println((int)num3);
System.out.println("--------------------------");

/**
* 指数运算
*/
// 1.返回以a为底数,以b为指数的幂值pow
double num4 = Math.pow(16,0.5);
System.out.println(num4);
/**
* 随机数
*/

// 1.返回大于等于 0.0、小于 1.0 的 double 类型随机数random
double numran=Math.random();
System.out.println(numran);

double num = 3.5;
System.out.printf("%.2f",num);

BigDecimal

 BigDecimal类是Java中提供的一个用于精确计算和表示任意精度的十进制数的类
1
2
3
4
5
6
7
8
9
10
11
public static BigDecimal valueOf(x)

public BigDecimal add(BigDecimal b)加法

public BigDecimal subtract(BigDecimal b)减法

public BigDecimal multiply(BigDecimal b)乘法

public BigDecimal divide(BigDecimal b)除法

public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式)除法

知识点

  • equals和==

    • “==”是运算符,如果是基本数据类型,则比较存储的值;如果是引用数据类型,则比较所指向对象的地址值。
    • quals是Object的方法,默认比较的是对象的地址值。可以通过重写从而比较的对象的值。如String
  • classpathpath 环境变量的查看与配置的方式完全相同。

  • synchronized 关键字有同步代码块和同步方法2 种

  • 默认的 equals() 方法在比较两个对象时,使用的是引用比较,而String中的equals方法使用的是值比较。

  • 对于基本数据类型==是值比较,对于引用数据类型==是引用比较

  • 对于String s1=”abc”,”abc“是放在方法区中的字符串常量池里面,它是可以共享和重用的。而对于String s1=new String(“abc”),”abc“是放在堆内存中的。

  • StringBuilder的equals用的是默认的比较方式。

  • StringBuilde是非线程安全的,StringBuffer是线程安全的

  • Arraylist是非线程安全的,Vector是线程安全的

  • HashMap是非线程安全的,HashTables是线程安全的

  • 堆内存是唯一的,而每个线程都有自己的栈内存

  • File 对象既可以表示文件,也可以表示目录,它可以查出与文件相关的信息,不包括文件内容

  • super(参数):调用父类中的某一个构造器

  • 局部变量在使用前必须被初始化,成员变量不用。

  • switch

    • 后面的小括号只能是基本数据类型和String、enum
    • 匹配哪一个case就从哪一个位置向下执行,直到遇到了break或者整体结束为止
    • case后面的数值不可以重复
  • Java中的自动装箱是指:自动将基本数据类型转换为对应的包装类对象。

  • 静态方法

    • 静态方法只能访问静态成员和方法,不可以“直接”访问实例成员和方法
    • 静态方法属于类而不属于实例,是不能被实例化的方法。
  • 断言是一种用于验证程序中的条件是否为真的机制,通常用于调试和测试阶段。

  • 线程是一种用于并发执行的单元,可以同时运行多个线程实现多任务。

  • 布尔类型不能被强制类型转换为其他数据类型

Idea快捷键

  • alt+insert:构造器、GetSet方法、ToString、equals() and hashCode()快速生成