Java 学习笔记

学习Java入门时做的笔记

📚 目录


基础语法

数据类型

1️⃣ 基本数据类型

数据值存储在自己空间

类型分类占用空间说明
byte整数1字节-128 ~ 127
short整数2字节-32768 ~ 32767
int整数4字节默认整数类型
long整数8字节需加L后缀
float浮点4字节需加F后缀
double浮点8字节默认浮点类型
char字符2字节单个字符
boolean布尔1字节true/false

💡 重点:整数在计算机中以补码形式存在,最高位为符号位

2️⃣ 引用数据类型

数据值存储在堆中,自己空间存储地址值

  • 类(Class)
  • 接口(Interface)
  • 数组(Array)

3️⃣ 类型转换

1
2
3
4
5
6
7
8
9
// 小范围 → 大范围(自动转换)
// 高位补0,符号位扩展
int a = 100;
long b = a;  // 自动转换

// 大范围 → 小范围(强制转换)
// 直接截取低位,可能丢失精度
long c = 100L;
int d = (int) c;  // 强制转换

流程控制

Switch 语句

📌 Case穿透现象

  • 执行case语句时,若未发现break,则顺序执行下一个case
  • 直到发现break为止

使用场景

  • if:适用于范围判断
  • switch:适用于在有限个数据中任选其一
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
switch (day) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
        System.out.println("工作日");
        break;
    case 6:
    case 7:
        System.out.println("周末");
        break;
    default:
        System.out.println("无效日期");
}

循环语句

for vs while

对比项forwhile
运行规则相同相同
使用场景已知循环次数/范围只知道结束条件

continue vs break

关键字作用
continue结束本次循环,进入下一次循环
break结束整个循环

数组

初始化方式

1
2
3
4
5
6
7
8
9
// 静态初始化(指定初始化值)
int[] arr1 = {1, 2, 3, 4, 5};

// 动态初始化(指定数组长度)
int[] arr2 = new int[5];

// 二维数组
int[][] arr3 = new int[3][4];
int[][] arr4 = {{1, 2}, {3, 4}, {5, 6}};

遍历数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 增强for循环
for (int num : arr1) {
    System.out.println(num);
}

// 二维数组遍历
for (int[] row : arr4) {      // 外循环:获取每一行
    for (int num : row) {     // 内循环:获取每个元素
        System.out.print(num + " ");
    }
    System.out.println();
}

方法

方法定义的思路

  1. 我要干什么? → 确定方法体
  2. 我需要什么? → 确定形参

内存原理

  • 方法调用时,栈内存开辟空间存储局部变量和方法调用信息
  • 方法执行完毕,栈内存空间释放

方法重载

  • 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可

方法递归

  • 方法自己调用自己
  • 递归出口:避免死循环

方法参数传递

  • 基本数据类型:值传递
  • 引用数据类型:地址传递

面向对象

类和对象

类是对象的模板,对象是类的具体体现

对象的内存解析

  • :存储局部变量和方法调用
  • :存储对象实例和数组实例,对象和数组的地址值存储在栈中
  • 方法区:存储类信息、常量、静态变量、方法字节码

封装

成员变量和局部变量

  • 成员变量:定义在类中,方法外,可以被类中的所有方法使用,默认初始化值
  • 局部变量:定义在方法中,只能被该方法使用,使用前必须初始化

构造方法

  1. 方法名和类名相同
  2. 没有返回值类型
  3. 用于创建对象并初始化对象成员变量

方法重载

在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可

this关键字

  • 就近原则:在方法中使用变量时,优先使用局部变量,如果没有则使用成员变量
  • this关键字的两种用法
    1. 表示当前对象的引用,指向当前对象本身
    2. 在构造方法中,this(形参列表)表示调用本类中的另一个构造方法

💡 内存原理:this存储在栈中,指向堆中的当前对象,代表方法调用者的地址值

JavaBean类

JavaBean用于描述某类事物,具有以下特点:

  1. 类是公共的(public)
  2. 有一个无参的公共构造方法
  3. 成员变量私有化(private)

static关键字

静态变量

  • 特点:被类的所有对象共享,随着类的加载而加载,优先于对象存在
  • 调用方式:类名.静态变量名 或 对象名.静态变量名

静态方法

  • 特点:多用在测试类和工具类中
  • 调用方式:类名.静态方法名 或 对象名.静态方法名
  • 注意:静态方法中不能使用非静态的成员变量和成员方法,非静态方法二者都可以使用,因为静态方法优先于对象存在,而非静态的成员变量和成员方法依赖于对象存在

工具类

  1. 提供静态方法,方便调用
  2. 构造方法私有化,避免创建对象

继承

继承的基本概念

  1. 子类继承父类,子类拥有父类的属性和方法,可以访问父类的非私有成员
  2. 继承方法:使用extends关键字实现继承
  3. 继承特点:
    • 单继承:Java中一个类只能有一个直接父类
    • 多层继承:子类继承父类,父类继承祖父类
    • Object类:所有类都继承Object类
  4. 继承中成员变量访问特点:
    • 就近原则:子类对象访问成员变量时,优先访问局部变量,再访问子类自己的成员变量,再查找父类的成员变量,直到Object类为止,整个是一个向上的查找过程

super关键字

表示父类对象的引用,指向父类对象本身

  • super(形参列表):在子类构造方法中调用父类的构造方法
  • super.成员变量名:访问父类的成员变量
  • super.成员方法名(实参列表):访问父类的成员方法

方法重写(Override)

  1. 子类继承父类后,可以对父类的方法进行重新定义和实现
  2. 重写方法的方法名、参数列表、返回值类型必须和父类被重写的方法相同
  3. 重写方法的访问权限不能小于父类被重写的方法的访问权限
  4. 父类被重写的方法不能是private、final、static修饰的
  5. 重写方法中可以调用父类被重写的方法,使用super关键字实现

多态

定义

父类引用指向子类对象

使用场景

  • 创建对象时,使用父类创建子类对象
  • 定义方法时,形参使用父类类型,可以传入所有子类对象

特点

  • 多态只能发生在继承关系中
  • 多态只能调用子类重写父类的方法,不能调用子类特有的方法
  • 多态的前提是有继承关系和方法重写

多态的体现

  • 方法调用:编译看左边,运行看右边
  • 变量访问:编译运行都看左边

包的作用

用于对类进行分类管理,避免类名冲突

包的声明和导入

  • 声明package 包名; 声明在类的第一行
  • 导入import 包名.类名;import 包名.*; (* 表示导入包中所有类)

使用其他类的规则

  1. 使用同一个包中的类,不需要导包
  2. 使用java.lang包中的类,不需要导包
  3. 其他情况都需要导包
  4. 使用两个包中的同名类,需要使用类的全限定名(包名.类名)

final关键字

final修饰变量

  1. 基本数据类型:变量值不可改变
  2. 引用数据类型:引用地址不可改变,但对象的内容可以改变

final修饰方法

表示该方法不能被重写

final修饰类

表示该类不能被继承

静态代码块

使用static关键字修饰,随着类的加载而执行,并且只执行一次

作用:用于初始化类的信息

抽象类和抽象方法

抽象方法

父类中的抽象方法,提取子类的共性,没有方法体,用于被子类重写

抽象类

  • 抽象类不能实例化
  • 继承抽象类的子类必须重写父类中的所有抽象方法,除非子类也是抽象类
  • 抽象类可以有构造方法,用于子类创建对象时调用父类构造方法
  • 作用:强制让子类按照某种格式重写

接口

接口的定义

  1. 使用interface关键字定义
  2. 接口中只能定义常量和抽象方法

接口的作用

  1. 规范类的行为
  2. 实现多继承,接口可以被多个子类实现

接口中的成员

  1. 成员变量:只能是常量,默认修饰符public static final
  2. 成员方法:只能是抽象方法,默认修饰符public abstract

接口的实现

  1. 使用implements关键字实现接口
  2. 格式:class 类名 implements 接口名1, 接口名2...{}
  3. 实现接口的类必须重写接口中的所有抽象方法,除非该类是抽象类

接口和类之间的关系

  • 类和类的关系:继承关系,只能单继承,不能多继承
  • 类和接口的关系:实现关系,可以单实现,也可以多实现,可以在继承一个类的同时实现多个接口
  • 接口和接口的关系:继承关系,可以单继承,也可以多继承

接口中新增的方法

默认方法

  • 使用default关键字修饰,可以有方法体,实现类不强制重写
  • 实现类可以重写接口中的默认方法,需要去掉default关键字

静态方法

  • 使用static关键字修饰,可以有方法体,实现类不能重写
  • 只能通过接口名调用接口中的静态方法

私有方法(Java 9及以上版本)

  • 使用private关键字修饰
  • 只能在接口内部使用,不能被实现类使用

内部类

成员内部类

创建内部类对象

  1. 外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
  2. 在外部类的成员方法中创建内部类对象,外界通过外部类对象调用该方法获取内部类对象

内部类方法访问

  1. 访问内部类成员:this.成员变量名成员变量名
  2. 访问外部类成员:外部类名.this.成员变量名

静态内部类

创建静态内部类对象

1
外部类名.内部类名 对象名 = new 外部类名.内部类名();

静态内部类方法访问

  1. 访问静态方法:外部类名.内部类名.静态方法名();
  2. 访问非静态方法:创建对象后,通过对象调用

匿名内部类

  • 定义:没有类名的内部类,必须继承一个类或实现一个接口,继承类或实现接口的同时创建对象
  • 作用:简化代码编写
  • 格式new 父类名或接口名(){重写父类或接口的方法};

集合框架

单列集合

ArrayList

  1. 动态数组,长度可变
  2. 存储有序的、可重复的数据
  3. 底层使用Object[]数组存储数据

HashSet

  • 底层数据结构:哈希表(数组+链表/红黑树)
  • 特点:无序、不可重复、查询效率高
  • 添加元素过程
    • 计算元素的哈希值,确定存储位置
    • 如果位置为空,直接存储
    • 如果位置不为空,使用equals()方法判断是否重复,重复则不添加,不重复则添加到链表或红黑树中
  • 接口实现
    • 重写hashCode()equals()方法(这两个方法属于Object类),确保元素唯一性
    • 不同对象只要属性值相同,hashCode()返回相同的哈希值,equals()返回true

LinkedHashSet

  • 底层数据结构:哈希表+双向链表(维护元素的插入顺序)
  • 特点:有序、不可重复、无索引
  • 接口实现:重写hashCode()equals()方法,确保元素唯一性

TreeSet

  • 底层数据结构:红黑树
  • 特点:有序、不可重复、无索引
  • 添加元素过程
    • 使用compareTo()compare()方法比较元素大小,确定存储位置
    • 按照大小顺序存储元素
  • 接口实现
    • 默认排序:集合中的元素实现Comparable接口,重写compareTo()方法,指定排序规则
    • 比较器排序:集合使用Comparator接口,重写compare()方法,指定排序规则

双列集合

Map接口

基本概念

  1. 双列集合的顶层接口,存储键值对(key-value)
  2. 常用实现类:HashMap、LinkedHashMap、TreeMap

常用方法

  • put(key, value):添加键值对
  • get(key):获取值
  • remove(key):删除键值对
  • containsKey(key):判断是否包含键
  • containsValue(value):判断是否包含值
  • keySet():获取所有键
  • values():获取所有值
  • entrySet():获取所有键值对

遍历方法

  1. keySet():把键存储到Set集合中,遍历Set集合获取键,再通过键获取值
  2. entrySet():把键值对存储到Set集合中,遍历Set集合用getKey()getValue()方法获取每一个键值对
  3. Lambda表达式遍历:map.forEach((key, value) -> { //使用key和value进行操作 });

HashMap

  • 底层数据结构:哈希表(数组+链表/红黑树)
  • 特点:无序、不可重复、无索引
  • 添加键值对过程
    • 计算键的哈希值,确定存储位置
    • 如果位置为空,直接存储键值对
    • 如果位置不为空,使用equals()方法判断键是否重复,重复则覆盖,不重复则添加到链表或红黑树中

LinkedHashMap

  • 底层数据结构:哈希表+双向链表(维护键值对的插入顺序)
  • 特点:有序、不可重复、无索引
  • 添加键值对过程
    • 计算键的哈希值,确定存储位置
    • 如果位置为空,直接存储键值对,并在双向链表中维护插入顺序
    • 如果位置不为空,使用equals()方法判断键是否重复,重复则覆盖,不重复则添加到链表或红黑树中,并在双向链表中维护插入顺序

TreeMap

  • 底层数据结构:红黑树
  • 特点:有序(对键进行排序)、不可重复、无索引
  • 添加键值对过程
    • 使用compareTo()compare()方法比较键的大小,确定存储位置
    • 按照键的大小顺序存储键值对

不可变集合

定义

一旦创建后,集合的内容不能被修改(添加、删除、修改元素)

创建不可变集合的方法

  • List.of(E... elements):创建不可变的List集合
  • Set.of(E... elements):创建不可变的Set集合,元素不能重复
  • Map.of(K k1, V v1, K k2, V v2, ...):创建不可变的Map集合,键值对数量上限为10
  • Map.ofEntries(Map.Entry<? extends K, ? extends V>... entries):创建不可变的Map集合,键值对数量不限

Stream流

获取Stream流

  1. Collection集合
    • 集合对象.stream():获取集合的Stream流
  2. Map集合
    • 需要先使用entrySet()将map转换为单列集合,再转换为Stream流
  3. Arrays工具类
    • Arrays.stream(数组对象):获取数组的Stream流
  4. Stream接口
    • 静态方法:of()iterate()generate()concat()

方法引用

定义:方法引用是Lambda表达式的一种简化形式,用于直接引用已有的方法,当做函数式接口中抽象方法的方法体

Lambda表达式格式参数列表 -> 方法体

方法引用格式类名或对象名::方法名

  • 引用静态方法:类名::静态方法名
  • 引用成员方法:对象名::成员方法名
  • 引用构造方法:类名::new

高级特性

异常处理

异常的分类

  1. 检查异常(编译时异常):在编译阶段被检查的异常,必须处理,否则编译不通过,如IOException、SQLException
  2. 非检查异常(运行时异常):在运行阶段被检查的异常,不强制处理,如NullPointerException、ArrayIndexOutOfBoundsException

异常的处理

  1. try-catch语句
    • 用在方法调用处,能让代码继续运行
    • try块:包含可能抛出异常的代码,当执行到异常时,跳转到对应的catch块处理异常,不再执行try块中后续代码
    • catch块:捕获并处理异常,可以有多个catch块处理不同类型的异常。如果catch没有捕获到异常,异常会继续向上抛出
  2. throws关键字
    • 写在方法定义处,表示该方法可能抛出的异常类型,调用该方法时必须处理异常(编译异常一定要加throws)
    • 格式:返回值类型 方法名(参数列表) throws 异常类型1, 异常类型2…
  3. throw关键字
    • 写在方法体中,表示抛出一个异常对象
    • 格式:throw new 异常类型(“异常信息”);
  4. 自定义异常
    • 手动定义异常类,继承Exception类或RuntimeException类,重写构造方法
    • 用于表示特定的异常情况,让报错信息更加见名知义

File类

创建File对象

  1. new File(String pathname):通过路径创建File对象
  2. new File(String parent, String child):通过父路径和子路径创建File对象
  3. new File(File parent, String child):通过父File对象和子路径创建File对象

常用方法

  1. 创建文件和目录
    • createNewFile():创建文件
    • mkdir():创建单级目录
    • mkdirs():创建多级目录
  2. 删除文件和目录
    • delete():删除文件或目录

IO流

字节流

  1. 输入流:InputStream及其子类,用于读取数据
  2. 输出流:OutputStream及其子类,用于写出数据
  3. 常用子类:
    • FileInputStream:文件字节输入流
      • 方法:read()、read(byte[] b)、read(byte[] b, int off, int len)
    • FileOutputStream:文件字节输出流
      • 方法:write(int b)、write(byte[] b)、write(byte[] b, int off, int len)
  4. 字符集
    • ASCII:美国标准信息交换码,单字节编码,表示128个字符
    • ISO-8859-1:拉丁字母编码,单字节编码,表示256个字符
    • GB2312:简体中文编码,双字节编码,表示6763个汉字和682个其他字符
    • GBK:扩展GB2312,双字节编码,表示21003个汉字和其他字符
    • UTF-8:可变长度编码,1-4字节表示一个字符,兼容ASCII编码,支持全球所有语言字符

字符流

基本概念

  1. 输入流Reader及其子类,用于读取字符数据
  2. 输出流Writer及其子类,用于写出字符数据

常用子类

  • FileReader:文件字符输入流
    • 方法:read()read(char[] cbuf)readLine()
  • FileWriter:文件字符输出流

使用场景

  • 字节流:拷贝任意类型的文件
  • 字符流:处理文本文件,如txt、java、xml等

序列化流

ObjectOutputStream:对象输出流,用于将对象序列化到文件中

  • 方法:writeObject(Object obj)flush()close()

ObjectInputStream:对象输入流,用于从文件中读取对象

  • 方法:readObject()close()

转换流

InputStreamReader:将字节流转换为字符流

  • 构造方法:InputStreamReader(InputStream in)InputStreamReader(InputStream in, String charsetName)

OutputStreamWriter:将字符流转换为字节流

多线程

基本概念

  • 进程:程序在计算机中运行时的一个实例
  • 线程:进程中的一个执行单元,一个进程可以有多个线程,线程之间互相独立,共享进程的资源

网络编程

TCP协议

  1. 面向连接的协议,通信前需要建立连接
  2. 数据传输可靠,数据顺序不会乱
  3. 传输效率相对较低

UDP协议

  1. 面向无连接的协议,通信前不需要建立连接
  2. 数据传输不可靠,数据顺序可能会乱
  3. 传输效率相对较高

Socket编程

  • ServerSocket:服务器套接字,用于监听客户端的连接请求
  • Socket:客户端套接字,用于与服务器建立连接,发送和接收数据
  • 三次握手:建立TCP连接的过程,客户端和服务器之间进行三次数据交换,确保连接的建立
  • 四次挥手:断开TCP连接的过程,客户端和服务器之间进行四次数据交换,确保连接的断开