JAVA从入门到精通一文搞定

博主介绍: 大家好,我是想成为Super的Yuperman,互联网宇宙厂经验,17年医疗健康行业的码拉松奔跑者,曾担任技术专家、架构师、研发总监负责和主导多个应用架构。
近期专注: DeepSeek应用,RPA应用研究,主流厂商产品使用,开源RPA 应用等
技术范围: java体系,软件架构,DDD,多年Golang、.Net、Oracle等经验
业务范围: 对传统业务应用技术转型,从数字医院到区域医疗,从院内业务系统到互联网医院及健康服务,从公立医院到私立医院都有一些经历及理解
*** 为大家分享一些思考与积累,欢迎持续关注公众号:【火星求索】 ***

背景知识

Java 相关概念

  1. JavaSE (Java Standard Edition): 基础版,用于开发桌面应用程序。
  2. JavaEE (Java Enterprise Edition): 企业版,用于开发企业级应用程序。
  3. JavaME (Java Micro Edition): 微型版,用于开发嵌入式系统和移动设备应用程序。

编译与运行

  1. 编译阶段:

    • 源文件: .java 文件。
    • 字节码文件: .class 文件。
    • 编译工具: javac.exe,用于将 .java 文件编译为 .class 文件。
      • 命令: javac 文件名.java
      • 编译包: javac -d 编译后存放路径 java源文件路径
  2. 运行阶段:

    • 运行工具: java.exe,用于运行 .class 文件。
      • 命令: java 类名(不带 .class 后缀)
    • JVM (Java Virtual Machine): Java 虚拟机,负责执行字节码文件。

开发环境

  1. JDK (Java Development Kit): Java 开发工具包,包含编译器、调试器等开发工具。
  2. JRE (Java Runtime Environment): Java 运行环境,包含 JVM 和运行 Java 程序所需的库。
  3. JVM (Java Virtual Machine): Java 虚拟机,负责执行字节码文件。

工具与格式

  1. native2ascii: 用于将 Unicode 字符转换为 \u 表示的 ASCII 格式。
  2. UML (Unified Modeling Language): 面向对象设计图,用于表示类、接口、继承、实现等关系。
    • 空心箭头: 指向父类(继承)。
    • 空心虚线箭头: 指向接口(实现)。
    • 实心实线箭头: 表示关联关系。

注释

  1. 单行注释: //
  2. 多行注释: /* */
  3. 文档注释: /** */,用于生成帮助文档。

类与方法结构

类体 {方法体 {java语句;}
}AI写代码java运行

总结

  • JavaSE 是基础版,JavaEE 是企业版,JavaME 是微型版。
  • 编译 使用 javac运行 使用 java
  • JDK 是开发工具包,JRE 是运行环境,JVM 是虚拟机。
  • UML 用于面向对象设计,注释 用于代码说明。
  • 类与方法 的基本结构如上所示。

Java SE API 和文档

一、集成开发环境(IDEA)

以下是用户提供的快捷键和组织方式的总结:


组织方式
  1. Project(工程): 最高层级,包含多个模块。
  2. Module(模块): 工程下的子模块,包含多个包。
  3. Package(包): 模块下的子包,用于组织类和资源。

字体设置
  • 路径: File -> Settings -> Font
    用于调整编辑器的字体样式和大小。

快捷键分类总结
导航与操作
  1. 展开/移动列表:

    • 左右箭头: 展开或折叠列表。
    • 上下箭头: 在列表中移动。
  2. 切换与定位:

    • Alt+左右箭头: 切换 Java 程序。
    • Alt+上下箭头: 在方法间快速移动。
    • Alt+标号: 打开标号窗口。
    • Ctrl+G: 定位到文件的某一行。
    • Ctrl+点击: 切换源码。
    • Ctrl+H: 查看实现类。
  3. 查找与搜索:

    • Ctrl+Shift+N: 查找文件。
    • Ctrl+N: 查找类文件。
    • Ctrl+F12: 在当前类中查找一个方法。

编辑与格式化
  1. 代码编辑:

    • Ctrl+Y: 删除一行。
    • Shift+F6: 重命名。
    • Alt+拖动: 一次编辑多行。
    • Ctrl+Alt+T: 将选中的代码放在 TRY{}IF{}ELSE{} 中。
  2. 代码提示与自动补全:

    • Ctrl+空格: 代码提示。
    • Ctrl+P: 方法参数提示。
    • Ctrl+J: 自动代码。
    • Ctrl+Alt+Space: 类名或接口名提示。
  3. 格式化与优化:

    • Ctrl+Alt+L: 格式化代码。
    • Ctrl+Alt+I: 自动缩进。
    • Ctrl+Alt+O: 优化导入的类和包。

运行与纠错
  1. 运行程序:

    • Ctrl+Shift+F10: 运行当前程序。
  2. 纠错与提示:

    • Alt+回车: 纠错提示。

窗口操作
  1. 全屏模式:
    • Ctrl+Shift+F12: 切换全屏模式。

总结
  • 组织方式: 工程 -> 模块 -> 包,层级清晰,便于管理。
  • 快捷键:
    • 导航与查找:快速定位文件、类、方法。
    • 编辑与格式化:提高代码编写效率。
    • 运行与纠错:快速运行程序并修复错误。
    • 窗口操作:优化开发环境布局。

二、JVM内存划分

局部变量在方法体中声明,运行阶段内存在栈中分配

方法区内存:字节码文件在加载 的时候将其放在方法区之中(最先有数据,调用方法时在栈内分配空间)

堆内存(heap):new对象(成员变量中的实例变量(一个对象一份)在java对象内部存储),只能通过引用调用操作

栈(stack)内存:栈帧永远指向栈顶元素,栈顶元素处于活跃状态,先进后出,后进先出(存储局部变量)

在这里插入图片描述

内存区域与数据存储
  1. 堆内存(Heap):

    • 存储实例变量(对象属性)。
    • 每个 JVM 实例只有一个堆内存,所有线程共享。
    • 垃圾回收器(GC)主要针对堆内存进行回收。
  2. 方法区(Method Area):

    • 存储静态变量(类变量)和类元数据(如类信息、常量池等)。
    • 每个 JVM 实例只有一个方法区,所有线程共享。
    • 方法区是最先有数据的内存区域,因为类加载时静态变量和类信息会初始化。
  3. 栈内存(Stack):

    • 存储局部变量和方法调用栈帧。
    • 每个线程有一个独立的栈内存,线程私有。
    • 栈内存是使用最频繁的内存区域,因为方法调用和局部变量的生命周期较短。

变量存储位置
  1. 局部变量:

    • 存储在栈内存中。
    • 生命周期与方法调用一致,方法结束时局部变量会被销毁。
  2. 实例变量:

    • 存储在堆内存中。
    • 生命周期与对象一致,对象被垃圾回收时实例变量会被销毁。
  3. 静态变量:

    • 存储在方法区中。
    • 生命周期与类一致,类卸载时静态变量会被销毁。

垃圾回收器(GC)
  1. 主要目标:

    • 垃圾回收器主要针对堆内存进行回收,清理不再使用的对象。
    • 栈内存和方法区的垃圾回收机制与堆内存不同。
  2. 特点:

    • 堆内存是垃圾回收的主要区域,因为对象生命周期较长且占用内存较大。
    • 栈内存和方法区的垃圾回收效率较高,因为它们的生命周期较短且数据量相对较小。

三、关键字:

类与关键字
  1. public:

    • 表示公开的类,类名必须与文件名一致,且一个文件中只能有一个 public 类。
  2. class:

    • 用于定义一个类。
  3. static:

    • 表示静态的,修饰的成员变量或方法属于类级别,不依赖于对象。
    • 静态变量在类加载时初始化,存储在方法区内存中。
    • 静态方法不能访问实例变量或实例方法,需要通过对象访问。
  4. break:

    • 用于跳出循环或 switch 语句。
  5. continue:

    • 用于跳过当前循环的剩余部分,直接进入下一次循环。
    • 语法:continue 循环名称;循环名称:
  6. this:

    • 表示当前对象的引用。
    • 用于区分局部变量和实例变量,或在构造方法中调用其他构造方法(this(实参))。
    • 不能用于静态方法中。
  7. native:

    • 用于调用 JVM 本地程序。

输入与输出
  1. System.out.println():

    • 控制台输出,println 表示输出并换行。
  2. 键盘输入:

    • 创建键盘扫描器对象:java.util.Scanner s = new java.util.Scanner(System.in);
    • 字符串输入:String user = s.next();
    • 整数输入:int num = s.nextInt();

final 关键字
  1. 修饰类:

    • 类不能被继承。
  2. 修饰方法:

    • 方法不能被重写。
  3. 修饰变量:

    • 变量不能被修改。
    • 修饰的成员变量必须手动赋值。
    • 修饰的引用一旦指向一个对象,就不能指向其他对象,但所指向的内存可以修改。
  4. 常量:

    • 定义常量:public static final 类型 常量名 = 值;
    • 命名规则:全部大写,用下划线分隔。

super 关键字
  1. 作用:

    • 代表当前对象的父类型特征。
    • 用于访问父类的属性、方法或调用父类的构造方法。
  2. 语法:

    • 访问父类属性或方法:super.
    • 调用父类构造方法:super()
  3. 规则:

    • 不能用于静态方法中。
    • 如果父类和子类有同名属性,访问父类属性时不能省略 super
    • 构造方法的第一行如果没有 this()super(),默认会调用 super()

static 关键字
  1. 静态变量:

    • 属于类级别,不依赖于对象,类加载时初始化。
  2. 静态方法:

    • 类级别的方法,不能访问实例变量或实例方法。
  3. 静态代码块:

    • 在类加载时执行,只执行一次。
    • 语法:static {}
  4. 实例代码块:

    • 在构造方法执行之前执行,用于对象初始化。

包与导入
  1. package:

    • 用于管理类,命名规则:公司域名倒序.项目名.模块名.功能名。
    • 语法:package 包名;
  2. import:

    • 用于导入包中的类。
    • 语法:import 包名.类名;import 包名.*;
    • java.lang.* 是核心语言包,无需导入。
  3. 快捷键:

    • Ctrl+Shift+O:自动导入。

访问控制权限修饰符
  1. private:

    • 私有访问权限,只能在本类中访问。
  2. default:

    • 默认访问权限,可以被本包中的其他类访问。
  3. protected:

    • 受保护的访问权限,可以被本包及不同包的子类访问。
  4. public:

    • 公共访问权限,可以在任何地方访问。
  5. 类的修饰符:

    • 类只能使用 public 或默认修饰符(缺省),内部类除外。

总结
  • 类与关键字publicclassstaticthissuper 等关键字的作用与用法。
  • 输入与输出:控制台输出与键盘输入的基本操作。
  • final:用于修饰类、方法、变量,表示不可修改。
  • static:修饰类级别的成员,与对象无关。
  • 包与导入packageimport 的使用及命名规则。
  • 访问控制权限privatedefaultprotectedpublic 的访问范围。

四、Java基础

以下是用户提供的内容的总结:


标识符
  1. 定义:

    • 用户有权命名的单词,包括类名、方法名、常量名、变量名、接口名等。
  2. 命名规则:

    • 类名、接口名: 首字母大写,后面每个单词首字母大写(大驼峰命名法)。
    • 方法名、变量名: 首字母小写,后面每个单词首字母大写(小驼峰命名法)。
    • 常量名: 全部大写,单词间用下划线分隔。

字面值
  • 定义: 数据本身,如数字、字符串等,通常以紫色显示。

变量
  1. 局部变量:

    • 定义在方法体内,没有默认值,必须手动初始化。
    • 生命周期与方法调用一致。
  2. 成员变量:

    • 定义在类体内,有默认值(数值类型为 0,布尔类型为 false,引用类型为 null)。
    • 分为实例变量和静态变量。
  3. 实例变量:

    • 不带 static 关键字,属于对象级别。
    • 必须通过对象引用访问(引用.变量名)。
    • 存储在堆内存中。
  4. 静态变量:

    • static 关键字,属于类级别。
    • 在类加载时初始化,存储在方法区内存中。
    • 通过类名访问(类名.变量名)。

引用
  • 定义: 是一个变量,可以是实例变量或局部变量。
    • 实例变量: 类名 引用 = new 类名();
    • 局部变量: 引用 变量名 = new 引用();

数据类型
  1. 基本数据类型:

    • 整数型: byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节,后缀 L)。
    • 浮点型: float(4 字节)、double(8 字节)。
    • 布尔型: boolean(1 字节)。
    • 字符型: char(2 字节)。
  2. 引用数据类型:

    • 字符串: String,不可变,存储在方法区字符串池中。
  3. 比较:

    • 基本数据类型使用 == 判断相等。
    • 引用数据类型(包括 String)使用 equals 判断相等。

字符编码
  • 发展顺序: ASCII < ISO-8859-1 < GB2312 < GBK < GB18030 < Big5 < Unicode(统一全球编码)。

位运算符
  1. 逻辑异或(^): 两边不一样为真。
  2. 短路与(&&): 左边为假时直接返回假。
  3. 按位与(&): 将操作数转换为二进制后按位与。
  4. 短路或(||): 左边为真时直接返回真。
  5. 左移(<<): 二进制数据左移,相当于乘以 2 的 N 次方。
  6. 右移:
    • 带符号右移(>>): 正数用 0 填充,负数用 1 填充。
    • 无符号右移(>>>): 无论正负都用 0 填充。
  7. 按位取反(~): 将二进制每一位取反,结果为 -(n+1)

方法(函数)
  1. 定义:

    [修饰符列表] 返回值类型 方法名(形参列表) {方法体;return; // return 后不能跟语句
    }AI写代码java运行
    
  2. 调用: 类名.方法名(实参列表);

  3. 实例方法: 不带 static,需要对象参与。

  4. 静态方法: 带 static,与对象无关。


方法重载(Overload)
  • 定义: 在同一类中,方法名相同但参数列表不同。
  • 特点: 与返回值类型和修饰符列表无关。

方法递归
  • 定义: 方法调用自身,每次递归都会分配新的内存空间(压栈)。

在这里插入图片描述

  • 示例:

    public static int sum(int n) {if (n == 1) {return 1;}return n + sum(n - 1);
    }AI写代码java运行
    

方法覆盖(Override)
  1. 定义: 发生在继承关系中,子类重写父类的方法。
  2. 规则:
    • 方法名、返回值类型、形参列表必须与父类一致。
    • 访问权限不能比父类更低,抛出异常不能更多。
  3. 限制:
    • 私有方法、构造方法不能覆盖。
    • 静态方法不存在覆盖。

总结
  • 标识符: 命名规则与用途。
  • 变量: 局部变量、实例变量、静态变量的定义与存储位置。
  • 数据类型: 基本数据类型与引用数据类型的区别。
  • 位运算符: 各种位运算符的作用与用法。
  • 方法: 定义、调用、重载、递归与覆盖的规则与特点。

五、Java 控制流与 Lambda 表达式

1. 控制流语句
  • If-Else 语句

    if (条件) {// 语句
    } else if (表达式) {// 语句
    } else {// 语句
    }AI写代码java运行
    
  • Switch 语句

    switch (关键词) {case 关键词:// java语句break;default:// 默认语句
    }AI写代码java运行
    
  • For 循环

    for (初始表达式; 布尔表达式; 更新循环体) {// 循环体
    }AI写代码java运行
    
  • 增强 For 循环(For Each)

    for (元素类型 变量名 : 数组或集合) {System.out.println(变量名);
    }AI写代码java运行
    
  • While 循环

    while (表达式) {// 循环体
    }AI写代码java运行
    
  • Do-While 循环

    do {// 循环体
    } while (布尔表达式);AI写代码java运行
    
2. Java 标签
  • 标签用于控制嵌套循环的跳转和中断。
  • 语法label:
  • 用法
    • continue label;:跳过当前循环,继续执行标签处的循环。
    • break label;:结束标签处的循环,执行循环后的代码。
3. Lambda 表达式
  • 实现 Runnable

    // Java 8 之前
    new Thread(new Runnable() {@Overridepublic void run() {System.out.println("Before Java8, too much code for too little to do");}
    }).start();// Java 8 方式
    new Thread(() -> System.out.println("In Java8, Lambda expression rocks !!")).start();AI写代码java运行
    
  • 事件处理

    // Java 8 之前
    JButton show = new JButton("Show");
    show.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("Event handling without lambda expression is boring");}
    });// Java 8 方式
    show.addActionListener((e) -> {System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
    });AI写代码java运行
    
  • 列表迭代

    // Java 8 之前
    List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
    for (String feature : features) {System.out.println(feature);
    }// Java 8 之后
    features.forEach(n -> System.out.println(n));
    // 使用方法引用
    features.forEach(System.out::println);AI写代码java运行
    
  • Map 和 Reduce

    // 不使用 lambda 表达式
    List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
    for (Integer cost : costBeforeTax) {double price = cost + .12 * cost;System.out.println(price);
    }// 使用 lambda 表达式
    costBeforeTax.stream().map((cost) -> cost + .12 * cost).forEach(System.out::println);// 使用 reduce 计算总和
    double bill = costBeforeTax.stream().map((cost) -> cost + .12 * cost).reduce((sum, cost) -> sum + cost).get();
    System.out.println("Total : " + bill);AI写代码java运行
    
  • 对列表的每个元素应用函数

    List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.", "Canada");
    String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
    System.out.println(G7Countries);AI写代码java运行
    
  • 计算集合元素的最大值、最小值、总和以及平均值

    List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
    IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
    System.out.println("Highest prime number in List : " + stats.getMax());
    System.out.println("Lowest prime number in List : " + stats.getMin());
    System.out.println("Sum of all prime numbers : " + stats.getSum());
    System.out.println("Average of all prime numbers : " + stats.getAverage());AI写代码java运行
    
总结
  • 控制流语句:用于控制程序的执行流程,包括条件判断、循环等。
  • Java 标签:用于控制嵌套循环的跳转和中断。
  • Lambda 表达式:简化了匿名类的使用,使代码更简洁,特别是在实现函数式接口(如 RunnableActionListener)时非常有用。
  • Stream API:提供了强大的集合操作功能,如 mapreduceforEach 等,使得对集合的处理更加高效和简洁。

六、面向对象

面向过程与面向对象的对比
  1. 面向过程

    • 因果关系:关注问题的具体步骤和流程。
    • 具体过程:强调如何一步步解决问题。
    • 耦合度高:各个模块之间依赖性强,修改一个模块可能会影响其他模块。
    • 软件拓展性差:由于耦合度高,系统的扩展和维护较为困难。
  2. 面向对象

    • 分类对象:将问题分解为多个对象,每个对象负责特定的功能。
    • 关系层度低:对象之间的依赖关系较弱,耦合度低。
    • 关注对象功能:关注对象能完成哪些功能,而不是具体的实现步骤。
    • 三大特征
      • 封装性:将复杂的事务封装起来,只保留简单的操作入口。封装后形成独立的对象,提高了代码的复用性、适应性和安全性。
      • 继承性:实现代码复用,最重要的是支持多态和方法覆盖。
      • 多态性:父类型的引用可以指向子类型对象,降低程序耦合度,提高扩展力。
面向对象的分析与设计
  • 面向对象的分析(OOA):分析问题域,识别对象及其关系。
  • 面向对象的设计(OOD):设计对象的结构和行为,定义类及其关系。
  • 面向对象的编程(OOP):使用编程语言实现设计,创建对象并实现其功能。
类与对象
  • :高度抽象的对象的集合,是一个模板。
    • 静态代码块:类加载时执行。
    • 实例代码块:实例化时执行。
    • 静态变量:类级别的变量。
    • 实例变量:对象级别的变量,存储在堆内存中。
    • 构造方法:创建对象时调用,用于初始化实例变量。
    • 静态方法:类级别的方法。
    • 实例方法:对象级别的方法。
    • 成员变量:对象的属性,描述对象的状态。
    • 成员方法:对象的行为,描述对象的动作。
  • 对象:类的具体实例。
    • 创建对象类名 对象名称 = new 类名();
    • 使用对象对象名称.属性名对象名称.方法名()
    • 修改对象引用.变量名 = 值
    • 引用与对象:引用保存了对象的地址,指向堆内存中的对象。多个引用可以指向同一个对象,但一个引用只能指向一个对象。
User u=new User();
Address a=new Address();
u.addr=a;
Print(u.addr.city);
A.city=”天津”;
Print(u.addr.city);                                      AI写代码java运行

在这里插入图片描述

封装
  • 私有化属性:使用private关键字将属性私有化。
  • 提供操作入口:通过gettersetter方法提供对属性的访问和修改。
    • 读取属性public 数据类型 get属性名() { return 属性; }
    • 修改属性public void set属性名(数据类型 属性) { this.属性 = 属性; }
  • 业务逻辑控制:在setter方法中添加业务逻辑进行安全控制。
构造方法
  • 作用:创建对象并初始化实例变量。
  • 语法修饰符 构造方法名(形参) { 构造方法体; this.实例变量 = 形参; }
  • 特点:没有返回值类型,方法名与类名一致,不能使用return返回值,但可以使用return结束方法。
  • 调用new 构造方法名(实参)
  • 缺省构造器:如果没有定义构造方法,编译器会自动生成一个无参的缺省构造器。
继承
  • 语法[修饰符列表] class 子类名 extends 父类名 { 类体 = 属性 + 方法 }
  • 单继承:Java中类只能继承一个父类。
  • 继承关系
    • 父类:也称为基类、超类、superclass
    • 子类:也称为派生类、subclass
  • 不可继承:私有的属性和方法、构造方法。
  • 间接继承:通过继承链,子类可以间接继承父类的父类。
  • 默认继承:如果没有显式继承任何类,默认继承java.lang.Object类。
  • super关键字:用于调用父类的属性、方法和构造方法。
多态
  • 向上转型(Upcasting):子类转换为父类型,自动类型转换。
    • 语法父类 引用 = new 子类();
    • 特点:编译通过,运行没有问题。
  • 向下转型(Downcasting):父类转换为子类,强制类型转换。
    • 语法子类 引用 = (子类) 父类引用;
    • 特点:存在隐患,可能导致ClassCastException异常。
  • 动态绑定:父类型引用指向子类型对象,调用方法时实际执行的是子类的方法。
  • instanceof运算符:用于在强制转换前检查对象的类型,避免ClassCastException异常。
    • 语法引用 instanceof 数据类型名
    • 返回值:布尔类型,true表示引用指向的对象是后面的数据类型,false表示不是。

以下是关于 抽象类接口 的总结:


抽象类
  1. 定义:

    • 使用 abstract 关键字修饰的类,是类的进一步抽象。
    • 属于引用数据类型。
  2. 语法:

    [修饰符列表] abstract class 类名 {}AI写代码java运行
    
  3. 特点:

    • 不能使用 privatefinal 修饰。
    • 抽象类可以包含抽象方法和非抽象方法。
    • 抽象类的子类可以是抽象类或非抽象类。
    • 不能实例化(不能创建对象),但可以有构造方法,供子类使用。
  4. 抽象方法:

    • 使用 abstract 关键字修饰,无方法体。
    • 语法:[修饰符列表] abstract 返回值类型 方法名();
    • 包含抽象方法的类一定是抽象类。
  5. 规则:

    • 抽象类不一定有抽象方法,但抽象方法必须出现在抽象类中。
    • 非抽象类继承抽象类时,必须实现所有抽象方法。

接口
  1. 定义:

    • 使用 interface 关键字定义,是完全抽象的(特殊的抽象类)。
    • 属于引用数据类型。
  2. 语法:

    [修饰符列表] interface 接口名 {}AI写代码java运行
    
  3. 特点:

    • 接口中只能包含常量和抽象方法(默认 public static finalpublic abstract,修饰符可省略)。
    • 支持多继承,一个接口可以继承多个接口。
    • 接口不能继承抽象类。
  4. 方法类型:

    • 抽象方法: abstract 修饰(可省略)。
    • 默认方法: default 修饰,提供默认实现。
    • 静态方法: static 修饰,通过接口名调用。
  5. 实现:

    • 类通过 implements 关键字实现接口。
    • 非抽象类实现接口时,必须重写所有抽象方法。
    • 一个类可以实现多个接口。
  6. 多态:

    • 接口支持多态:父类型引用指向子类对象
    • 示例:接口名 引用 = new 实现类();
  7. 作用:

    • 解耦合:调用者面向接口调用,实现者面向接口编写实现。
    • 扩展性强:接口+多态可以降低程序耦合度。

抽象类与接口的区别
特性抽象类接口
抽象程度半抽象(可以包含具体方法)完全抽象(只能包含抽象方法)
构造方法有构造方法,供子类使用无构造方法
继承单继承(一个类只能继承一个抽象类)支持多继承(一个类可以实现多个接口)
内容可以包含抽象方法和非抽象方法只能包含常量和抽象方法
用途抽象行为和数据主要抽象行为
实例化不能实例化不能实例化

开发中的选择
  1. 抽象类:

    • 当多个类有共同的属性和行为,且需要部分具体实现时使用。
    • 适合定义“是什么”(is-a 关系)。
  2. 接口:

    • 当需要定义一组行为规范,且不关心具体实现时使用。
    • 适合定义“能做什么”(like-a 关系)。

示例
  1. 抽象类:

    abstract class Animal {abstract void sound();void sleep() {System.out.println("Sleeping...");}
    }AI写代码java运行
    
  2. 接口:

    interface Flyable {void fly();
    }AI写代码java运行
    
  3. 实现与继承:

    class Bird extends Animal implements Flyable {@Overridevoid sound() {System.out.println("Chirp...");}@Overridepublic void fly() {System.out.println("Flying...");}
    }AI写代码java运行
    

总结
  • 面向对象编程通过封装、继承和多态三大特征,提高了代码的复用性、扩展性和维护性。

  • 类与对象是面向对象编程的基础,类是对对象的抽象,对象是类的实例。

  • 封装通过私有化属性和提供操作入口,增强了代码的安全性和可控性。

  • 继承实现了代码的复用,并支持多态和方法覆盖。

  • 多态通过向上转型和向下转型,降低了程序的耦合度,提高了扩展力。

  • 面向抽象编程,而不是面向具体,可以进一步降低耦合度,提高系统的灵活性和可扩展性。

  • 抽象类 用于定义类的共有特征,支持部分具体实现。

  • 接口 用于定义行为规范,支持多继承和解耦合。

  • 在实际开发中,根据需求选择抽象类或接口,合理使用可以提高代码的扩展性和可维护性。

七、类库

源码、字节码与帮助文档
  1. 源码

    • 理解程序:源码是程序员编写的原始代码,用于理解程序的逻辑和功能。
  2. 字节码

    • 程序开发使用:字节码是源码编译后的中间代码,由JVM执行。它是跨平台的,可以在任何支持JVM的系统上运行。
  3. 帮助文档

    • 对开发提供帮助:帮助文档是开发者的参考指南,通常通过javadoc生成。
    • 注意使用版本同一:确保使用的帮助文档与代码版本一致,避免因版本差异导致的错误。

Object类(根类)

Object是Java中所有类的根类,提供了一些核心方法:

  1. protected Object clone()

    • 负责对象克隆,返回对象的副本。
  2. boolean equals(Object obj)

    • 判断两个对象是否相等。默认比较引用地址,通常需要重写以比较对象内容。
  3. int hashCode()

    • 返回对象的哈希代码值,用于哈希表等数据结构。
  4. String toString()

    • 返回对象的字符串表示形式。默认返回类名@哈希值,通常需要重写以提供更有意义的信息。
  5. protected void finalize() throws Throwable

    • 垃圾回收器负责调用,用于对象销毁前的清理工作。
  6. System.gc()

    • 建议启动垃圾回收器,但不保证立即执行。

System类

System类提供了一些系统级别的操作:

  1. System.gc()

    • 建议启动垃圾回收器。
  2. System.out

    • 静态变量,用于控制台输出。
  3. System.out.print()

    • 输出打印不换行。
  4. System.out.println()

    • 换行输出。
  5. System.currentTimeMillis()

    • 获取自1970年1月1日00:00:00到当前系统时间的总毫秒数。
  6. System.exit(0)

    • 退出JVM。

Arrays类

Arrays是数组工具类,提供了一些常用方法:

  1. Arrays.sort(arr)

    • 对数组进行排序。
  2. Arrays.binarySearch(arr, key)

    • 使用二分法查找元素,不存在时返回-1。

String类

String类用于操作字符串,提供了丰富的构造方法和方法:

  1. 构造方法

    • String(byte[] byte):将字节数组转换为字符串。
    • String(char[] char):将字符数组转换为字符串。
    • String(String string):复制字符串。
  2. 常用方法

    • char charAt(int index):返回指定索引的字符。
    • int compareTo(String string):字典比较大小。
    • boolean contains(String string):判断是否包含指定字符串。
    • boolean endsWith(String string):判断是否以指定字符串结尾。
    • boolean startsWith(String prefix):判断是否以指定前缀开头。
    • boolean equals(Object anObject):比较字符串内容。
    • boolean equalsIgnoreCase(String anotherString):忽略大小写比较。
    • byte[] getBytes():将字符串转换为字节数组。
    • int indexOf(String str):返回子字符串第一次出现的索引。
    • int lastIndexOf(String str):返回子字符串最后一次出现的索引。
    • boolean isEmpty():判断字符串是否为空。
    • String replace(CharSequence target, CharSequence replacement):替换字符串。
    • String substring(int beginIndex):截取字符串。
    • char[] toCharArray():将字符串转换为字符数组。
    • String toLowerCase():将字符串转换为小写。
    • String toUpperCase():将字符串转换为大写。
    • String[] split(String regex):按正则表达式拆分字符串。
    • String trim():去除前后空白。
    • static String valueOf():将其他类型转换为字符串。

StringBuffer与StringBuilder
  1. StringBuffer

    • 线程安全,适用于多线程环境。
    • 常用方法:append()reverse()
  2. StringBuilder

    • 非线程安全,性能优于StringBuffer

包装类

包装类用于将基本数据类型转换为对象:

  1. 常用包装类
    • IntegerCharacter等。
  2. 常用方法
    • int intValue():拆箱,将包装类转换为基本类型。
    • static int parseInt(String s):将字符串转换为整数。

在这里插入图片描述


日期相关类
  1. java.util.Date

    • 表示日期和时间。
  2. SimpleDateFormat

    • 用于格式化日期。
    • 常用方法:format()parse()

数字相关类
  1. DecimalFormat

    • 用于格式化数字。
  2. BigDecimal

    • 用于高精度计算,适用于财务数据。
  3. Random

    • 用于生成随机数。

枚举(Enum)

枚举是一种特殊的类,用于定义一组常量:

enum Season {SPRING, SUMMER, AUTUMN, WINTER
}AI写代码java运行

内部类
  1. 成员内部类

    • 定义在类中,可以访问外部类的所有成员。
  2. 局部内部类

    • 定义在方法中,只能在该方法内访问。
  3. 静态内部类

    • 使用static修饰,只能访问外部类的静态成员。
  4. 匿名内部类

    • 没有名称的内部类,通常用于实现接口或抽象类。

总结
  • 源码是理解程序的基础,字节码是程序运行的关键,帮助文档是开发的指南。
  • Object是Java的根类,提供了对象的基本操作。
  • System类提供了系统级别的操作,如垃圾回收、时间获取等。
  • String类用于操作字符串,提供了丰富的构造方法和方法。
  • StringBufferStringBuilder用于字符串的拼接和修改,前者线程安全,后者性能更优。
  • 包装类用于将基本数据类型转换为对象。
  • 日期相关类用于处理日期和时间。
  • 内部类提供了更灵活的代码组织方式。

八、数组

一维数组
  1. 定义:

    • 数组是引用数据类型,存储在堆内存中。
    • 可以存储各种数据类型,但不能直接存储对象,存储的是对象的引用(内存地址)。
  2. 特点:

    • 数组元素类型统一,最后一个下标为 length - 1
    • 带有 length 属性,用于获取数组长度。
  3. 优点:

    • 查询、查找、检索某个下标元素效率极高(内存连续,类型相同)。
  4. 缺点:

    • 随机增删元素效率较低。
    • 不能存储大数据量。
  5. 定义与初始化:

    • 静态初始化:

      数据类型[] 数组名 = {元素1, 元素2, ...};AI写代码java运行
      
    • 动态初始化:

      数据类型[] 数组名 = new 数据类型[长度];AI写代码java运行
      
  6. 赋值:

    数组名[下标] =;AI写代码java运行
    
  7. 遍历:

    • 使用 for 循环或增强 for 循环:

      for (int i = 0; i < 数组名.length; i++) {System.out.println(数组名[i]);
      }AI写代码java运行
      
  8. 方法参数:

    • 数组可以作为方法的参数:

      void 方法名(数据类型[] 数组名) {}AI写代码java运行
      
  9. main 方法的数组参数:

    • main 方法的参数是一个字符串数组,用于接收命令行参数:

      public static void main(String[] args) {}AI写代码java运行
      
  10. 存储对象:

    • 数组可以存储对象的引用:

      类名[] 数组名 = new 类名[长度];
      数组名[0] = new 类名();AI写代码java运行
      
  11. 数组扩容:

    • 新建一个大数组,然后将原数组拷贝过去:

      int[] newArray = new int[原数组.length * 2];
      System.arraycopy(原数组, 0, newArray, 0, 原数组.length);AI写代码java运行
      
  12. 数组拷贝:

    • 使用 System.arraycopy 方法:

      System.arraycopy(原数组, 原起点, 目标数组, 目标下标, 长度);AI写代码java运行
      

二维数组
  1. 定义:

    • 二维数组是数组的数组,可以看作是一个表格。
  2. 语法:

    数据类型[][] 数组名 = new 数据类型[行数][列数];AI写代码java运行
    
  3. 初始化:

    • 静态初始化:

      数据类型[][] 数组名 = {{元素1, 元素2}, {元素3, 元素4}};AI写代码java运行
      
    • 动态初始化:

      数据类型[][] 数组名 = new 数据类型[行数][列数];AI写代码java运行
      
  4. 遍历:

    • 使用嵌套 for 循环:

      for (int i = 0; i < 数组名.length; i++) {for (int j = 0; j < 数组名[i].length; j++) {System.out.println(数组名[i][j]);}
      }AI写代码java运行
      

总结
  1. 一维数组:

    • 适用于存储一组相同类型的数据。
    • 查询效率高,增删效率低。
    • 可以通过 length 属性获取长度。
    • 支持静态初始化和动态初始化。
  2. 二维数组:

    • 适用于存储表格型数据。
    • 可以看作是一维数组的数组。
    • 支持静态初始化和动态初始化。
  3. 数组的优缺点:

    • 优点:查询效率高,内存连续。
    • 缺点:增删效率低,不能存储大数据量。
  4. 数组的应用场景:

    • 存储一组固定长度的数据。
    • 存储对象引用。
    • 存储表格型数据(二维数组)。

示例
  1. 一维数组:

    int[] arr = {1, 2, 3, 4, 5};
    for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);
    }AI写代码java运行
    
  2. 二维数组:

    int[][] arr = {{1, 2}, {3, 4}};
    for (int i = 0; i < arr.length; i++) {for (int j = 0; j < arr[i].length; j++) {System.out.println(arr[i][j]);}
    }AI写代码java运行
    
  3. 数组存储对象:

    Animal[] animals = new Animal[2];
    animals[0] = new Cat();
    animals[1] = new Dog();AI写代码java运行
    
  4. 数组扩容:

    int[] src = {1, 2, 3};
    int[] dest = new int[src.length * 2];
    System.arraycopy(src, 0, dest, 0, src.length);AI写代码java运行
    

通过合理使用数组,可以高效地存储和操作数据,但需要注意其增删效率较低的缺点。

九、算法

以下是常见 排序算法查找算法 的思想总结,并附带 Java 实例:


排序算法
  1. 冒泡排序(Bubble Sort):

    • 思想:重复遍历数组,每次比较相邻元素,如果顺序错误则交换,直到没有需要交换的元素。

    • 时间复杂度:O(n²)。

    • Java 实现:

      public static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
      }AI写代码java运行
      
  2. 选择排序(Selection Sort):

    • 思想:每次从未排序部分选择最小元素,放到已排序部分的末尾。

    • 时间复杂度:O(n²)。

    • Java 实现:

      public static void selectionSort(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {int minIndex = i;for (int j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}
      }AI写代码java运行
      
  3. 插入排序(Insertion Sort):

    • 思想:将未排序部分的元素逐个插入到已排序部分的正确位置。

    • 时间复杂度:O(n²)。

    • Java 实现:

      public static void insertionSort(int[] arr) {for (int i = 1; i < arr.length; i++) {int key = arr[i];int j = i - 1;while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}
      }AI写代码java运行
      
  4. 快速排序(Quick Sort):

    • 思想:选择一个基准元素,将数组分为两部分,左边小于基准,右边大于基准,递归排序。

    • 时间复杂度:O(n log n)。

    • Java 实现:

      public static void quickSort(int[] arr, int low, int high) {if (low < high) {int pivot = partition(arr, low, high);quickSort(arr, low, pivot - 1);quickSort(arr, pivot + 1, high);}
      }private static int partition(int[] arr, int low, int high) {int pivot = arr[high];int i = low - 1;for (int j = low; j < high; j++) {if (arr[j] < pivot) {i++;int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}int temp = arr[i + 1];arr[i + 1] = arr[high];arr[high] = temp;return i + 1;
      }AI写代码java运行
      
  5. 归并排序(Merge Sort):

    • 思想:将数组分成两半,分别排序,然后合并。

    • 时间复杂度:O(n log n)。

    • Java 实现:

      public static void mergeSort(int[] arr, int left, int right) {if (left < right) {int mid = (left + right) / 2;mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);merge(arr, left, mid, right);}
      }private static void merge(int[] arr, int left, int mid, int right) {int[] temp = new int[right - left + 1];int i = left, j = mid + 1, k = 0;while (i <= mid && j <= right) {if (arr[i] <= arr[j]) {temp[k++] = arr[i++];} else {temp[k++] = arr[j++];}}while (i <= mid) {temp[k++] = arr[i++];}while (j <= right) {temp[k++] = arr[j++];}for (int p = 0; p < temp.length; p++) {arr[left + p] = temp[p];}
      }AI写代码java运行
      

查找算法
  1. 线性查找(Linear Search):

    • 思想:从头到尾遍历数组,逐个比较,找到目标元素。

    • 时间复杂度:O(n)。

    • Java 实现:

      public static int linearSearch(int[] arr, int target) {for (int i = 0; i < arr.length; i++) {if (arr[i] == target) {return i;}}return -1;
      }AI写代码java运行
      
  2. 二分查找(Binary Search):

    • 思想:在有序数组中,每次取中间元素与目标比较,缩小查找范围。

    • 时间复杂度:O(log n)。

    • Java 实现:

      public static int binarySearch(int[] arr, int target) {int left = 0, right = arr.length - 1;while (left <= right) {int mid = (left + right) / 2;if (arr[mid] == target) {return mid;} else if (arr[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return -1;
      }AI写代码java运行
      

总结
  1. 排序算法:

    • 冒泡排序:简单但效率低,适合小规模数据。
    • 选择排序:每次选择最小元素,适合小规模数据。
    • 插入排序:适合部分有序的数据。
    • 快速排序:高效,适合大规模数据。
    • 归并排序:稳定且高效,适合大规模数据。
  2. 查找算法:

    • 线性查找:适合无序数据。
    • 二分查找:适合有序数据,效率高。
  3. 选择依据:

    • 数据规模、是否有序、稳定性要求等。

示例
public class Main {public static void main(String[] args) {int[] arr = {5, 3, 8, 4, 2};bubbleSort(arr);System.out.println("冒泡排序结果: " + Arrays.toString(arr));int[] arr2 = {5, 3, 8, 4, 2};quickSort(arr2, 0, arr2.length - 1);System.out.println("快速排序结果: " + Arrays.toString(arr2));int target = 4;int index = binarySearch(arr2, target);System.out.println("二分查找结果: " + (index != -1 ? "找到,下标为 " + index : "未找到"));}
}AI写代码java运行

输出:

冒泡排序结果: [2, 3, 4, 5, 8]
快速排序结果: [2, 3, 4, 5, 8]
二分查找结果: 找到,下标为 2AI写代码

通过合理选择排序和查找算法,可以高效地处理数据。

十、异常

1. 异常的基本概念
  • 异常在 Java 中以类的方式存在,每个异常类都可以创建异常对象。
  • 方法覆盖规则:子类重写父类方法时,不能抛出比父类方法更高的异常(运行时异常 RuntimeException 除外)。
  • 异常的分类
    • java.lang.Throwable:异常的父类,有两个子类:
      • Error:错误,通常是系统级错误(如 OutOfMemoryError),不可处理,只能退出程序。
      • Exception:异常,所有异常都是在运行阶段发生的。
        • Exception 的直接子类:编译时异常(受检异常 CheckedException),需要在编写程序时预处理。
        • RuntimeException:运行时异常,通常由程序逻辑错误引起,不需要显式处理。

2. 常见运行时异常
  • NullPointerException:空指针异常,尝试访问 null 对象的成员。
  • ArrayIndexOutOfBoundsException:数组下标越界异常。
  • ClassCastException:类型转换异常,尝试将对象强制转换为不兼容的类型。
  • NumberFormatException:数字转换异常,尝试将非数字字符串转换为数字。

3. 异常处理方式
  1. throws 关键字

    • 在方法声明位置使用,将异常抛给调用者处理。

    • 示例:

      public void readFile() throws IOException {// 可能抛出 IOException 的代码
      }AI写代码java运行
      
  2. try-catch-finally 语句

    • 捕获并处理异常。

    • 示例:

      try {// 可能抛出异常的代码
      } catch (NullPointerException e) {System.out.println("空指针异常: " + e.getMessage());
      } catch (ArrayIndexOutOfBoundsException e) {System.out.println("数组下标越界: " + e.getMessage());
      } finally {// 无论是否发生异常,都会执行的代码System.out.println("finally 块执行");
      }AI写代码java运行
      

4. 常用异常方法
  • getMessage():获取异常的简单描述信息(通常是构造方法的参数)。
  • printStackTrace():打印异常的堆栈追踪信息(异步线程中常用)。

5. 自定义异常
  • 步骤

    1. 编写一个类继承 Exception(受检异常)或 RuntimeException(运行时异常)。
    2. 提供两个构造方法:一个无参,一个有参。
    3. 使用 throw 手动抛出异常。
  • 示例

    // 自定义异常类
    public class MyException extends Exception {public MyException() {super();}public MyException(String message) {super(message);}
    }// 使用自定义异常
    public class Test {public static void main(String[] args) {try {throw new MyException("自定义异常发生");} catch (MyException e) {System.out.println(e.getMessage());}}
    }AI写代码java运行
    

6. 异常处理的最佳实践
  • 明确异常类型:捕获具体异常,而不是直接捕获 Exception
  • 合理使用 finally:用于释放资源(如关闭文件、数据库连接等)。
  • 避免空指针异常:在使用对象前进行 null 检查。
  • 日志记录:使用日志框架(如 Log4jSLF4J)记录异常信息,便于排查问题。

总结
  • 异常分类ErrorException,其中 Exception 分为编译时异常和运行时异常。
  • 处理方式throws 抛给调用者,try-catch-finally 捕获并处理。
  • 自定义异常:继承 ExceptionRuntimeException,提供构造方法,使用 throw 抛出。
  • 最佳实践:明确异常类型,合理使用 finally,避免空指针异常,记录日志。

十一、I/O

I/O(输入/输出)概述

I/O(Input/Output)是指应用程序与外部设备(如磁盘、网络、键盘、显示器等)之间的数据交互。Java通过java.io包提供了丰富的I/O类库,支持文件操作、字节流、字符流等功能。


File类

File类是java.io包中唯一代表磁盘文件本身的对象,用于操作文件和目录。

构造方法
  1. File(String path)

    • 根据路径创建File对象。
  2. File(String parent, String child)

    • 根据父路径和子路径(包括文件名)创建File对象。
  3. File(File parent, String child)

    • 根据File对象表示的父路径和子路径创建File对象。

注意:路径分隔符可以使用\\(Windows)或/(Unix/Linux)。

常用方法
  1. boolean exists()

    • 判断文件或目录是否存在。
  2. boolean delete()

    • 删除文件或目录。
  3. boolean createNewFile()

    • 如果文件不存在,则创建一个新文件。
  4. String getName()

    • 返回文件或目录的名称。
  5. String getPath()

    • 返回文件或目录的路径。
  6. String getAbsolutePath()

    • 返回文件或目录的绝对路径。
  7. boolean canRead()

    • 判断文件是否可读。
  8. boolean canWrite()

    • 判断文件是否可写。
  9. boolean isFile()

    • 判断是否为文件。
  10. boolean isDirectory()

    • 判断是否为目录。
  11. long length()

    • 返回文件内容的长度(字节数)。
  12. String[] list()

    • 返回目录内所有文件和子目录的名称。
  13. File[] listFiles()

    • 返回目录内所有文件和子目录的File对象。
  14. createTempFile(String prefix, String suffix)

    • 创建临时文件。
  15. deleteOnExit()

    • JVM退出时自动删除文件。

字节流

字节流用于处理二进制数据(如图片、音频、视频等),以字节为单位进行读写操作。

在这里插入图片描述

字节输入流(InputStream)

InputStream是字节输入流的抽象类,用于从源(如文件、网络等)读取数据。

常用方法

  1. int read()

    • 逐个字节读取,返回读取的字节值(0-255),如果到达流末尾则返回-1。
  2. int read(byte[] b)

    • 将数据读取到字节数组b中,返回实际读取的字节数。
  3. int read(byte[] b, int off, int len)

    • 从偏移量off开始,读取len个字节到数组b中,返回实际读取的字节数。
  4. void close()

    • 关闭流,释放资源。
字节输出流(OutputStream)

OutputStream是字节输出流的抽象类,用于将数据写入目标(如文件、网络等)。

常用方法

  1. void write(int b)

    • 逐个字节写入。
  2. void write(byte[] b)

    • 将字节数组b中的数据写入。
  3. void write(byte[] b, int off, int len)

    • 从偏移量off开始,写入len个字节。
  4. void flush()

    • 强制将缓冲区中的数据写入目标。
  5. void close()

    • 关闭流,释放资源。
具体实现类
  1. FileInputStream

    • 用于从文件中读取字节数据。
  2. FileOutputStream

    • 用于将字节数据写入文件。

拓展总结
  1. 文件操作

    • 使用File类可以创建、删除、重命名文件,判断文件是否存在,查询文件属性等。
  2. 字节流

    • 字节流适用于处理二进制数据,InputStreamOutputStream是字节流的抽象基类。
    • FileInputStreamFileOutputStream是常用的字节流实现类,用于文件的读写操作。
  3. 流的使用注意事项

    • 使用流时,务必在操作完成后调用close()方法关闭流,释放系统资源。
    • 对于输出流,可以调用flush()方法强制将缓冲区中的数据写入目标。
  4. 临时文件

    • 使用createTempFile()方法可以创建临时文件,deleteOnExit()方法可以确保JVM退出时自动删除临时文件。
  5. 路径处理

    • 路径分隔符可以使用\\(Windows)或/(Unix/Linux),Java会自动处理。
  6. 性能优化

    • 对于大文件的读写,建议使用缓冲区(如BufferedInputStreamBufferedOutputStream)来提高性能。

示例代码
文件操作
File file = new File("test.txt");
if (!file.exists()) {file.createNewFile(); // 创建文件
}
System.out.println("文件名称: " + file.getName());
System.out.println("文件路径: " + file.getAbsolutePath());
file.delete(); // 删除文件AI写代码java运行
字节流读写
// 写入文件
try (FileOutputStream fos = new FileOutputStream("output.txt")) {fos.write("Hello, World!".getBytes());fos.flush();
}// 读取文件
try (FileInputStream fis = new FileInputStream("output.txt")) {byte[] buffer = new byte[1024];int len;while ((len = fis.read(buffer)) != -1) {System.out.println(new String(buffer, 0, len));}
}AI写代码java运行

通过掌握这些核心概念和类库,可以高效地处理文件操作和字节流读写。

字符流总结

字符流是Java I/O中用于处理文本数据的流,它以字符为单位进行读写操作。与字节流不同,字符流专门用于处理字符数据(如文本文件),并且支持字符编码(如UTF-8、GBK等),能够正确处理多字节字符。


字符流概述

字符流的核心类是ReaderWriter,它们分别是字符输入流和字符输出流的抽象基类。字符流的主要特点包括:

  1. 以字符为单位
    • 字符流以字符为单位读写数据,适合处理文本文件。
  2. 支持字符编码
    • 字符流可以正确处理字符编码,避免乱码问题。
  3. 高效读写
    • 字符流通常与缓冲区结合使用(如BufferedReaderBufferedWriter),提高读写效率。

在这里插入图片描述


字符输入流(Reader)

Reader是字符输入流的抽象类,用于从源(如文件、字符串等)读取字符数据。

常用方法
  1. int read()

    • 读取单个字符,返回字符的Unicode值(0-65535),如果到达流末尾则返回-1。
  2. int read(char[] cbuf)

    • 将字符数据读取到字符数组cbuf中,返回实际读取的字符数。
  3. int read(char[] cbuf, int off, int len)

    • 从偏移量off开始,读取len个字符到数组cbuf中,返回实际读取的字符数。
  4. void close()

    • 关闭流,释放资源。
具体实现类
  1. FileReader

    • 用于从文件中读取字符数据。
  2. BufferedReader

    • 带有缓冲区的字符输入流,提供readLine()方法逐行读取文本。
  3. InputStreamReader

    • 将字节流转换为字符流,支持指定字符编码。

字符输出流(Writer)

Writer是字符输出流的抽象类,用于将字符数据写入目标(如文件、控制台等)。

常用方法
  1. void write(int c)

    • 写入单个字符。
  2. void write(char[] cbuf)

    • 写入字符数组cbuf中的数据。
  3. void write(char[] cbuf, int off, int len)

    • 从偏移量off开始,写入len个字符。
  4. void write(String str)

    • 写入字符串str
  5. void write(String str, int off, int len)

    • 从偏移量off开始,写入len个字符。
  6. void flush()

    • 强制将缓冲区中的数据写入目标。
  7. void close()

    • 关闭流,释放资源。
具体实现类
  1. FileWriter

    • 用于将字符数据写入文件。
  2. BufferedWriter

    • 带有缓冲区的字符输出流,提供newLine()方法写入换行符。
  3. OutputStreamWriter

    • 将字节流转换为字符流,支持指定字符编码。

字符流与字节流的区别
  1. 单位不同

    • 字节流以字节为单位,适合处理二进制数据。
    • 字符流以字符为单位,适合处理文本数据。
  2. 编码支持

    • 字节流不涉及字符编码,直接处理字节数据。
    • 字符流支持字符编码,能够正确处理多字节字符。
  3. 性能优化

    • 字符流通常与缓冲区结合使用,提高读写效率。

示例代码
字符流读写文件
// 写入文件
try (FileWriter fw = new FileWriter("output.txt");BufferedWriter bw = new BufferedWriter(fw)) {bw.write("Hello, World!");bw.newLine(); // 写入换行符bw.write("This is a test.");
}// 读取文件
try (FileReader fr = new FileReader("output.txt");BufferedReader br = new BufferedReader(fr)) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}
}AI写代码java运行
使用指定编码读写文件
// 写入文件(指定编码为UTF-8)
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8");BufferedWriter bw = new BufferedWriter(osw)) {bw.write("你好,世界!");
}// 读取文件(指定编码为UTF-8)
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("output.txt"), "UTF-8");BufferedReader br = new BufferedReader(isr)) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}
}AI写代码java运行

总结
  1. 字符流适用场景

    • 处理文本文件、字符串等字符数据。
  2. 核心类

    • ReaderWriter是字符流的抽象基类。
    • FileReaderBufferedReaderFileWriterBufferedWriter是常用的实现类。
  3. 字符编码

    • 使用InputStreamReaderOutputStreamWriter可以指定字符编码,避免乱码问题。
  4. 性能优化

    • 使用BufferedReaderBufferedWriter可以提高读写效率。
  5. 流关闭

    • 使用try-with-resources语法确保流被正确关闭,释放资源。

通过掌握字符流的核心概念和类库,可以高效地处理文本数据的读写操作。

十二、集合

集合是Java中用于存储和管理一组对象的容器。它提供了一种更灵活、更高效的方式来操作数据集合。以下是集合的核心概念和总结:


集合的特点
  1. 容器性质

    • 集合是一个容器,可以容纳其他类型的数据。
    • 集合不能直接存储基本数据类型(如intchar等),也不能直接存储对象,存储的是Java对象的内存地址(引用)。
  2. 数据结构

    • 不同的集合对应不同的数据结构(如数组、链表、哈希表、二叉树等)。
    • 使用不同的集合等同于使用了不同的数据结构。
  3. 包位置

    • 所有的集合类都位于java.util包中。

集合的层次结构
  1. 超级父接口:Iterable<T>

    • 所有集合都是可迭代的,即可以通过迭代器遍历集合中的元素。
    • 方法:Iterator<T> iterator():返回集合的迭代器。
  2. 单个元素集合的父接口:Collection<E>

    • 表示存储单个元素的集合的超级接口。
    • 子接口包括:ListSetQueue等。
  3. 键值对集合的父接口:Map<K,V>

    • 表示存储键值对的集合,独立于Collection体系。

集合的实现类总结
1. List接口的实现类
  • ArrayList
    • 底层是数组,查询快,增删慢。
    • 非线程安全。
  • LinkedList
    • 底层是双向链表,增删快,查询慢。
    • 非线程安全。
  • Vector
    • 底层是数组,线程安全,但效率较低,使用较少。
2. Set接口的实现类
  • HashSet
    • 底层是HashMap,元素存储在HashMapkey部分。
    • 无序且不允许重复。
  • TreeSet
    • 底层是TreeMap,元素存储在TreeMapkey部分。
    • 元素自动按大小顺序排序。
3. Map接口的实现类
  • HashMap
    • 底层是哈希表,非线程安全。
    • 允许null键和null值。
  • Hashtable
    • 底层是哈希表,线程安全,但效率较低,使用较少。
    • 不允许null键和null值。
  • Properties
    • 底层是哈希表,线程安全。
    • keyvalue只能存储字符串(String)。
  • TreeMap
    • 底层是二叉树。
    • key自动按照大小顺序排序。

集合的选择
  1. 需要存储单个元素

    • 如果需要有序且允许重复,使用List
      • 查询多,增删少:ArrayList
      • 增删多,查询少:LinkedList
    • 如果不需要重复元素,使用Set
      • 无序:HashSet
      • 有序:TreeSet
  2. 需要存储键值对

    • 非线程安全:HashMap
    • 线程安全:HashtableProperties
    • 需要排序:TreeMap
  3. 线程安全

    • 如果需要线程安全,可以使用VectorHashtableProperties,但效率较低。
    • 推荐使用Collections.synchronizedList()ConcurrentHashMap等并发集合。

总结
  1. 集合的核心

    • 集合是存储和管理一组对象的容器,存储的是对象的内存地址。
    • 不同的集合对应不同的数据结构,选择合适的集合可以提高程序效率。
  2. 常用集合

    • List:有序且允许重复,常用ArrayListLinkedList
    • Set:无序且不允许重复,常用HashSetTreeSet
    • Map:存储键值对,常用HashMapTreeMapProperties
  3. 线程安全

    • 线程安全的集合有VectorHashtableProperties,但效率较低。
    • 推荐使用并发集合(如ConcurrentHashMap)来实现线程安全。

通过掌握集合的核心概念和常用实现类,可以更高效地处理数据集合,并根据需求选择合适的集合类型。

List 集合存储元素的特点:

有序可重复

有序:存进去的顺序和取出的顺序相同,每一个元素都有下标

可重复:存进去1,可以再存储一个1

Set 集合存储元素的特点(Map的Key):

无序不可重复

无序:存进去的顺序和取出的顺序不一定相同,另外 Set 集合中元素没有下标(哈希表的存储)

不可重复:存进去1,不能再存储1了(哈希表的覆盖)

SortedSet( SortedMap )集合存储元素特点:

首先是无序不可重复的,但是 SortedSet 集合中的元素是可排序的

无序:存进去的顺序和取出的顺序不一定相同,另外 Set 集合中元素没有下标

不可重复:存进去1,不能再存储1了

可排序:可以按照大小顺序排列。

Map 集合的 key ,就是一个 Set 集合。

往 Set 集合中放数据,实际上放到了 Map 集合的 key 部分。

Interface Collection

没有使用泛型前可以存储Object的所有子类型

  • Boolean add(E e) 添加元素
  • Object[] toArray() 转化成数组(使用不多)
  • Int size() 返回此集合中元素的数目。
  • Boolean contains(Object o) 如果此集合包含指定的元素(存放在集合中的类型,需要重写equals方法)
  • Void clear() 从此集合中删除所有元素
  • Boolean equals(Object o) 将指定的对象与此集合进行比较以实现相等性(内存地址)
  • Boolean remove(Object o) 从此集合中删除指定元素的单个实例
  • Boolean isEmpty() 如果此集合不包含任何元素(判空)则返回。true

Iterator<E> iterator() ***:**不管存进去什么,拿出来都是Object,取出来还是原类型

返回此集合中元素的迭代器**,Collection通用,Map集合不能用**

只要集合结构发生改变迭代器一定要重新获取

  • default void forEachRemaining(Consumer<? super E> action) 对每个剩余元素执行给定的操作,直到所有元素都已处理完毕或该操作引发异常。
  • Boolean hasNext() 如果迭代具有更多元素,则返回。true
  • Object next() 返回迭代中的下一个元素。(返回object)
  • default void remove() 从基础集合中删除此迭代器返回的最后一个元素(可选操作)。
Interface List 有序可重复,Collection子接口
  • void add(int index, E element) 在此列表中的指定位置插入指定的元素
  • E get(int index) 返回此列表中指定位置处的元素
  • E set(int index, E element) 将此列表中指定位置的元素替换为指定的元素
  • int indexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含该元素,则返回 -1
  • int lastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含该元素,则返回 -1。
  • E remove(int index) 删除此列表中指定位置的元素
Class ArrayList 非线程安全数组,初始化容量10,底层object数组

构造方法:

  • ArrayList() 构造初始容量为 10 的空列表(底层先创建了一个长度为0的数组,添加元素是初始化为10,自动扩容1.5倍)
  • ArrayList(int initialCapacity) 构造具有指定初始容量的空列表(建议提前估计,减少扩容)
  • ArrayList(Collection<? extends E> c) 构造一个列表,其中包含指定集合的元素,并按集合的迭代器返回这些元素的顺序排列。

方法:同List方法

Class LinkedList 双向链表,随机增删效率高,检索效率低
Class Vector 线程安全数组,默认10,扩容翻倍**(不经常使用)**

转换:使用集合工具类:java.util.Collections.synchronizedList(集合)

Interface Set 无序不可重复 存储Map的Key

Class HashSet 哈希表(底层HashMap)

需要重写hashCode和equals方法,其他方法参见HashMap

Interface SortedSet 无序不可重复可排序

Class TreeSet 二叉树(底层TreeMap Key部分)无序不可重复可排序

Key值自定义类需要实现java.long.Comparable接口或者创建比较器对象

class user implements Comparable<user>{      //自定义类需要实现接口int age;public user(int age) {this.age = age;}@Overridepublic String toString() {return "user{" + "age=" + age + '}';}@Override         //重写比较规则public int compareTo(user o) {return this.age-o.age;    //返回==0,value覆盖,返回大于0 到右子树,返回小于0到左子树}
}AI写代码java运行
Interface Map<K,V> Map主接口(和Collection没有继承关系)

以Key和Value存储数据都是引用数据类型,都存储内存地址,Key是主导

  • V put(K key, V value) 添加键值对(Key元素需要重新hashCode和equals方法)(Key可以为空,只有一个)
  • void clear() 清空Map集合
  • V get(Object key) 通过key获取value(key元素需要重新hashCode和equals方法)
  • boolean containsKey(Object key) 判断Map是否包含某个key(底层equals)
  • boolean containsValue(Object value) 判断Map是否包含某个value(底层equals)
  • boolean isEmpty() 判断Map集合元素个数是否为零
  • Set<K> keySet() 获取Map集合所有的Key(是个set集合)
  • V remove(Object key) 通过key删除键值对
  • Collection<V> values() 获取Map集合中键值对所有value(返回Collection)
  • int size() 获取Map集合所有的键值对个数
Set<Map.Entry<Integer,String>>set1=m.entrySet();       //使用方法
Iterator<Map.Entry<Integer,String>> it=set1.iterator(); //获取迭代器
while (it.hasNext()) {Map.Entry<Integer, String> entry = it.next();System.out.println(entry);     //直接遍历Integer key = entry.getKey();    //获取键String value = entry.getValue();    //获取值System.out.println(key + "=" + value);   //分开遍历for(Map.Entry<Integer,String> node:set1)   //效率较高,适合大数据,直接获取System.out.println(node);     //组合遍历AI写代码java运行
Class HashMap<K,V> 哈希表 非线程安全(初始化容量16[必须是2的倍数],默认加载因子0.75)

Key元素类型需要重新hashCode和equals方法

JDK8新特性:当单向链表长度超过8后数据结构会变成红黑树数据结构,当红黑树小于6,会变回链表

构造 函数 描述

  • HashMap() 使用默认初始容量 (16) ,默认负载系数 (0.75)
  • HashMap(int initialCapacity) 指定的初始容量,默认负载系数 初始容量必须是2的倍数:达到散列均匀,提高存取效率
  • HashMap(int initialCapacity, float loadFactor) 指定初始容量和负载系数
  • HashMap(Map<? extends K,? extends V> m)
Class Hashtable<K,V> 哈希表 线程安全(synchronized) Key不可以为空*(不常用)**

初始化容量11,默认加载因子0.75f,扩容:原容量*2+1

Class Properties 属性类 继承Hashtable类 仅支持String

  • Object setProperty(String key, String value) 存
  • String getProperty(String key) 取
  • String getProperty(String key, String defaultValue) 当key值为NULL时,返回def的值;当key值不为NULL时,返回key的值
Interface SortedMap<K,V>

Class TreeMap<K,V> 二叉树 可排序集合(中序遍历)

Key值自定义类需要实现java.long.Comparable接口或者创建比较器对象(类或者匿名内部类)

Class Collections 集合工具类
  • synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
  • synchronizedList(List list) 返回由指定列表支持的同步(线程安全)列表。**
    **synchronizedCollection(Collection c) 返回由指定集合支持的同步(线程安全)集合
  • sort(List list, Comparator<? super T> c) 根据指定比较器引发的顺序对指定列表进行排序。

十三、泛型

1. 泛型概述
  • 引入时间:JDK 5.0 之后的新特性。

  • 作用

    • 统一集合中元素的类型,避免类型转换错误。
    • 只在程序编译阶段起作用,编译后会进行类型擦除(Type Erasure)。
  • 语法

    • 在创建对象时,前后两段添加泛型类型。

    • 示例:

      List<String> list = new ArrayList<String>();AI写代码java运行
      

2. 泛型的优点
  • 类型安全:编译时检查类型,避免运行时类型转换错误。
  • 代码复用:可以编写通用的类和方法,适用于多种类型。
  • 代码简洁:减少强制类型转换的代码。

3. 泛型的缺点
  • 导致集合存储缺少多样性:泛型限制了集合中元素的类型,无法存储多种类型的对象。
  • 类型擦除:泛型信息在编译后会被擦除,运行时无法获取泛型的具体类型。

4. 自动推断机制(钻石表达式)
  • 引入时间:JDK 7 新特性。

  • 作用:自动推断泛型类型,简化代码。

  • 语法:只写前面的泛型类型,后面的泛型类型可以省略。

  • 示例

    List<String> list = new ArrayList<>();AI写代码java运行
    

5. 自定义泛型
  • 泛型类

    • 在定义类时添加 <T>T 是类型参数。

    • 示例:

      public class Box<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}
      }AI写代码java运行
      
    • 使用:

      Box<String> box = new Box<>();
      box.setValue("Hello");
      String value = box.getValue();AI写代码java运行
      
  • 泛型方法

    • 在定义方法时添加 <T>T 是类型参数。

    • 示例:

      public <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}
      }AI写代码java运行
      
    • 使用:

      Integer[] intArray = {1, 2, 3};
      printArray(intArray);AI写代码java运行
      

6. 泛型的通配符
  • <?>:表示任意类型。

  • <? extends T>:表示 T 或其子类型(上界通配符)。

  • <? super T>:表示 T 或其父类型(下界通配符)。

  • 示例:

    public void printList(List<?> list) {for (Object element : list) {System.out.println(element);}
    }AI写代码java运行
    

7. 泛型的限制
  • 不能使用基本类型:泛型类型必须是引用类型(如 Integer 而不是 int)。
  • 不能创建泛型数组:例如 new T[10] 是非法的。
  • 不能实例化泛型类型:例如 new T() 是非法的。

8. 泛型的应用场景
  • 集合框架:如 List<T>Map<K, V> 等。
  • 工具类:如 Comparator<T>Comparable<T> 等。
  • 自定义数据结构:如栈、队列、链表等。

总结与拓展
  • 泛型的作用:统一集合中元素的类型,提高代码的安全性和复用性。
  • 自动推断机制:JDK 7 引入的钻石表达式简化了泛型代码。
  • 自定义泛型:通过泛型类和泛型方法实现通用代码。
  • 通配符<?><? extends T><? super T> 提供了更灵活的类型约束。
  • 限制:泛型不能使用基本类型、不能创建泛型数组、不能实例化泛型类型。

十四、多线程

进程是:一个应用程序(1个进程是一个软件)

独立性:系统分配资源和调度资源的独立单位

动态性:进程实质是程序的一次执行过程,进程是动态产生,动态消亡的

并发性:任何进程都可以同其他进程一起并发执行

线程是:一个进程中的执行场景/执行单元,是进程中单个顺序控制流,是一条执行路径。

并行:同一时刻,多个指令在多个CPU上同时执行

并发:同一时刻,多个指令在单个CPU交替执行

线程状态转换

在这里插入图片描述

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态

当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程构造方法

构造方法名备注
Thread()
Thread(String name)name为线程名字
创建线程第二种方式
Thread(Runnable target)
Thread(Runnable target, String name)name为线程名字
Java 中实现线程的三种方式总结

1. 继承 Thread
  • 实现方式

    • 编写一个类,直接继承 java.lang.Thread
    • 重写 run() 方法,定义线程执行的任务。
  • 创建线程对象

    MyThread thread = new MyThread();AI写代码java运行
    
  • 启动线程

    thread.start();AI写代码java运行
    
  • 特点

    • 简单易用,但 Java 是单继承,继承 Thread 类后无法继承其他类。

2. 实现 Runnable 接口
  • 实现方式

    • 编写一个类,实现 java.lang.Runnable 接口。
    • 实现 run() 方法,定义线程执行的任务。
    • 通常使用匿名内部类创建。
  • 创建线程对象

    Runnable task = new MyRunnable();
    Thread thread = new Thread(task);AI写代码java运行
    
  • 启动线程

    thread.start();AI写代码java运行
    
  • 特点

    • 更灵活,可以避免单继承的限制。
    • 适合多个线程共享同一个任务。

3. 使用 CallableFuture 接口
  • 实现方式

    • 编写一个类,实现 java.util.concurrent.Callable 接口。
    • 实现 call() 方法,定义线程执行的任务,并返回结果。
  • 创建线程对象

    1. 创建 Callable 实现类的实例:

      Callable<Integer> task = new MyCallable();AI写代码java运行
      
    2. 使用 FutureTask 包装 Callable 对象:

      FutureTask<Integer> futureTask = new FutureTask<>(task);AI写代码java运行
      
    3. 使用 FutureTask 对象作为 Threadtarget 创建线程:

      Thread thread = new Thread(futureTask);AI写代码java运行
      
  • 启动线程

    thread.start();AI写代码java运行
    
  • 获取结果

    Integer result = futureTask.get(); // 阻塞直到获取结果AI写代码java运行
    
  • 特点

    • call() 方法可以有返回值和抛出异常。
    • 适合需要获取线程执行结果的场景。

Future 接口的常用方法
  • cancel(boolean mayInterruptIfRunning):尝试取消任务。
  • get():获取任务结果,阻塞直到任务完成。
  • get(long timeout, TimeUnit unit):在指定时间内获取任务结果,超时抛出 TimeoutException
  • isCancelled():判断任务是否被取消。
  • isDone():判断任务是否完成。

三种方式的对比
方式优点缺点
继承 Thread简单易用单继承限制,无法继承其他类
实现 Runnable 接口灵活,避免单继承限制,适合多线程共享任务无法直接获取线程执行结果
使用 CallableFuture可以获取线程执行结果,支持异常处理,功能更强大使用稍复杂,需要 FutureTask 包装

总结
  • 继承 Thread:适合简单的线程任务,但受限于单继承。
  • 实现 Runnable 接口:更灵活,适合多线程共享任务。
  • 使用 CallableFuture:适合需要获取线程执行结果或处理异常的场景。

根据具体需求选择合适的方式实现多线程编程。

获取当前线程对象、获取线程对象名字、修改线程对象名字

方法名作用
static Thread currentThread()获取当前线程对象
String getName()获取线程对象名字
void setName(String name)修改线程对象名字

关于线程的sleep方法

方法名作用
static void sleep(long millis)让当前线程休眠millis秒

关于线程中断sleep()的方法

方法名作用
void interrupt()终止线程的睡眠
Java进程的优先级
常量名备注
static int MAX_PRIORITY最高优先级(10)
static int MIN_PRIORITY最低优先级(1)
static int NORM_PRIORITY默认优先级(5)

方法:

方法名作用
int getPriority()获得线程优先级
void setPriority(int newPriority)设置线程优先级
static void yield()让位,当前线程暂停,回到就绪状态,让给其它线程。
void join()将一个线程合并到当前线程中,当前线程受阻塞,加入的线程执行直到结束
void join(long millis)接上条,等待该线程终止的时间最长为 millis 毫秒
void join(long millis, int nanos)接第一条,等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒

多线程并发环境下,数据的安全问题(重点)

1.为什么这个是重点?

以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。

最重要的是: 你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发的环境下是否是安全的。(重点:★★★★★)

2.什么时候数据在多线程并发的环境下会存在安全问题呢?★★★★★

满足三个条件:

条件1:多线程并发。

条件2:有共享数据。

条件3:共享数据有修改的行为。

满足以上3个条件之后,就会存在线程安全问题。

3.怎么解决线程安全问题呢?

当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在线程安全问题,怎么解决这个问题?

线程排队执行。(不能并发)。用排队执行解决线程安全问题。

这种机制被称为:线程同步机制。专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。

线程同步就是线程排队了,线程排队了就会 牺牲一部分效率 ,数据安全第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。

死锁(DeadLock)

死锁(Deadlock)是多线程编程中的一种常见问题,指的是两个或多个线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,导致这些线程都无法继续执行下去。

死锁代码要会写。一般面试官要求你会写。只有会写的,才会在以后的开发中注意这个事儿。因为死锁很难调试。


死锁的四个必要条件

死锁的发生必须同时满足以下四个条件:

  1. 互斥条件(Mutual Exclusion)

    • 资源一次只能被一个线程占用。
  2. 占有并等待(Hold and Wait)

    • 线程已经占有了至少一个资源,但又申请新的资源,而新的资源被其他线程占用。
  3. 不可抢占(No Preemption)

    • 线程已占有的资源不能被其他线程强行抢占,必须由线程自己释放。
  4. 循环等待(Circular Wait)

    • 存在一个线程的等待循环链,每个线程都在等待下一个线程所占用的资源。

Java 中的死锁示例

以下是一个经典的死锁代码示例,展示了两个线程互相等待对方释放锁的情况:

public class DeadlockExample {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: Holding lock 1...");try {Thread.sleep(100); // 模拟操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: Waiting for lock 2...");synchronized (lock2) {System.out.println("Thread 1: Acquired lock 2!");}}});Thread thread2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: Holding lock 2...");try {Thread.sleep(100); // 模拟操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2: Waiting for lock 1...");synchronized (lock1) {System.out.println("Thread 2: Acquired lock 1!");}}});thread1.start();thread2.start();}
}AI写代码java运行
代码分析
  1. 线程1

    • 先获取lock1,然后尝试获取lock2
    • 在获取lock2之前,线程1会休眠100毫秒。
  2. 线程2

    • 先获取lock2,然后尝试获取lock1
    • 在获取lock1之前,线程2会休眠100毫秒。
  3. 死锁发生

    • 线程1持有lock1并等待lock2
    • 线程2持有lock2并等待lock1
    • 两个线程互相等待,导致死锁。

如何避免死锁
  1. 避免嵌套锁

    • 尽量不要在持有一个锁的同时去申请另一个锁。
  2. 按顺序获取锁

    • 如果多个线程需要获取多个锁,确保它们以相同的顺序获取锁。
  3. 使用超时机制

    • 在获取锁时设置超时时间,如果超时则释放已持有的锁并重试。
  4. 使用工具检测

    • 使用工具(如jstack)检测死锁。

死锁的调试与检测
  1. 使用jstack

    • 运行程序后,使用jstack命令查看线程状态,可以检测到死锁。
  2. 日志输出

    • 在代码中添加日志,记录锁的获取和释放情况。
  3. 使用工具

    • 使用IDE(如IntelliJ IDEA)或第三方工具(如VisualVM)检测死锁。

守护线程

在Java中,线程分为两大类:用户线程守护线程。守护线程(Daemon Thread)是一种特殊的线程,它的生命周期依赖于用户线程。当所有的用户线程结束时,守护线程会自动退出。


守护线程的特点
  1. 依赖用户线程

    • 守护线程是为用户线程提供服务的线程。
    • 当所有的用户线程结束时,守护线程会自动退出。
  2. 典型代表

    • 垃圾回收线程(GC)是Java中最典型的守护线程。
  3. 主线程是用户线程

    • main方法所在的线程是用户线程。
  4. 死循环

    • 守护线程通常是一个死循环,持续执行某些后台任务。

守护线程的应用场景
  1. 定时任务

    • 例如,每天00:00自动备份系统数据。
    • 可以使用定时器(如TimerScheduledExecutorService),并将定时任务设置为守护线程。
  2. 后台监控

    • 例如,监控系统资源使用情况、日志清理等。
  3. 垃圾回收

    • Java的垃圾回收线程就是一个守护线程。

守护线程的设置

在Java中,可以通过setDaemon(boolean on)方法将一个线程设置为守护线程:

方法签名说明
void setDaemon(boolean on)ontrue表示将线程设置为守护线程

注意

  • 必须在调用start()方法之前设置守护线程,否则会抛出IllegalThreadStateException
  • 守护线程中创建的子线程默认也是守护线程。

代码示例

以下是一个守护线程的示例,展示了如何设置守护线程以及它的行为:

public class DaemonThreadExample {public static void main(String[] args) {Thread daemonThread = new Thread(() -> {while (true) {System.out.println("守护线程正在运行...");try {Thread.sleep(1000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}}});// 设置为守护线程daemonThread.setDaemon(true);// 启动守护线程daemonThread.start();// 主线程(用户线程)执行任务System.out.println("主线程开始执行...");try {Thread.sleep(5000); // 模拟主线程执行任务} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程执行完毕,程序退出。");}
}AI写代码java运行
代码分析
  1. 守护线程

    • 守护线程是一个死循环,每隔1秒输出一条消息。
    • 设置为守护线程后,当主线程结束时,守护线程会自动退出。
  2. 主线程

    • 主线程执行5秒后结束。
    • 主线程结束后,守护线程也会自动退出。

守护线程的注意事项
  1. 资源释放

    • 守护线程中不要执行关键任务(如文件写入、数据库操作等),因为它的退出是不可控的。
  2. 线程优先级

    • 守护线程的优先级通常较低,适合执行后台任务。
  3. 生命周期

    • 守护线程的生命周期依赖于用户线程,不能独立存在。

定时器的作用:

间隔特定的时间,执行特定的程序。在实际的开发中,每隔多久执行一段特定的程序,这种需求是很常见的,那么在java中其实可以采用多种方式实现:

可以使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行任务。这种方式是最原始的定时器。(比较low)

在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来用。

不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持定时任务的。

在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架,这个框架只要进行简单的配置,就可以完成定时器的任务。

构造方法名备注
Timer()创建一个定时器
Timer(boolean isDaemon)isDaemon为true为守护线程定时器
Timer(String name)创建一个定时器,其线程名字为name
Timer(String name, boolean isDaemon)结合2、3
方法名作用
void schedule(TimerTask task, Date firstTime, long period)安排指定的任务在指定的时间开始进行重复的固定延迟执行
void cancel()终止定时器
关于Object类的wait()、notify()、notifyAll()方法
方法名作用
void wait()让活动在当前对象的线程无限等待(释放之前占有的锁)
void notify()唤醒当前对象正在等待的线程(只提示唤醒,不会释放锁)
void notifyAll()唤醒当前对象全部正在等待的线程(只提示唤醒,不会释放锁)

wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方法是 Object类中自带 的。

wait方法和notify方法不是通过线程对象调用

调用:

Object o = new Object();

o.wait();

总结 ★★★★★(呼应生产者消费者模式)

1、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。

2、wait方法和notify方法建立在 线程同步 的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。

3、wait方法作用:o.wait() 让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁

4、notify方法作用:o.notify() 让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。

生产者消费者模式(wait()和notify())

什么是“生产者和消费者模式”?

生产线程负责生产,消费线程负责消费。

生产线程和消费线程要达到均衡。

这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。

模拟一个业务需求

仓库我们采用List集合。

List集合中假设只能存储1个元素。

1个元素就表示仓库满了。

如果List集合中元素个数是0,就表示仓库空了。

保证List集合中永远都是最多存储1个元素。

必须做到这种效果:生产1个消费1个。

在这里插入图片描述

十五、反射

1. Class 对象概述
  • Class 对象:在 Java 中,每个类在加载到内存时都会生成一个 Class 对象,该对象存储了类的所有信息(如方法、构造函数、字段等)。
  • 反射:通过 Class 对象,可以在运行时动态获取类的信息并操作类的成员(如调用方法、访问字段等)。

2. Class 对象的生成方式
  1. 类名.class

    • JVM 将类加载到内存中,但不进行初始化。

    • 返回该类的 Class 对象。

    • 示例:

      Class<?> clazz = String.class;AI写代码java运行
      
  2. Class.forName("包名.类名")

    • 加载类并默认进行静态初始化。

    • 返回该类的 Class 对象。

    • 示例:

      Class<?> clazz = Class.forName("java.lang.String");AI写代码java运行
      
  3. Class.forName("包名.类名", false, 类加载器)

    • 第二个参数为 false 时,不进行初始化;为 true 时,进行初始化。

    • 示例:

      Class<?> clazz = Class.forName("java.lang.String", false, ClassLoader.getSystemClassLoader());AI写代码java运行
      
  4. 实例对象.getClass()

    • 对类进行静态初始化和非静态初始化。

    • 返回运行时实际对象所属类的 Class 对象。

    • 示例:

      String str = "Hello";
      Class<?> clazz = str.getClass();AI写代码java运行
      

3. Class 对象的特性
  • 父子类 Class 对象不一致
    • 如果 AB 的子类,则 A.classB.class 返回的 Class 对象不同。
    • 如果 aA 的实例,则 A.classa.getClass() 返回的 Class 对象一致。

4. Class 类的常用方法
  • getName():返回类的全限定名(包名 + 类名)。
  • getSuperclass():返回类的直接父类的 Class 对象。
  • getInterfaces():返回类实现的所有接口的 Class 数组。
  • isArray():判断该类是否是数组类型。
  • isEnum():判断该类是否是枚举类型。
  • isInterface():判断该类是否是接口。
  • isPrimitive():判断该类是否是基本类型(如 intboolean 等)。
  • isAssignableFrom(Class cls):判断该类是否是 cls 的父类或父接口。
  • getComponentType():如果该类是数组类型,返回数组的组件类型。
  • asSubclass(Class clazz):将当前 Class 对象转换为 clazz 的子类类型。

5. asSubclass 方法的使用
  • 作用:将当前 Class 对象转换为指定类的子类类型。

  • 示例

    List<String> strList = new ArrayList<>();
    Class<? extends List> strListCast = strList.getClass().asSubclass(List.class);AI写代码java运行
    
  • 动态加载时的应用

    Class.forName("xxx.xxx.xxx").asSubclass(List.class).newInstance();AI写代码java运行
    
    • 如果 xxx.xxx.xxxList 的子类,则正常执行;否则抛出 ClassCastException

6. 静态加载与动态加载
  • 静态加载:通过 new ClassName() 加载类,编译时必须提供类的定义。
  • 动态加载:通过 Class.forName("ClassName") 加载类,编译时可以缺席,运行时按需提供。

总结
  • Class 对象:存储类的所有信息,是反射机制的核心。
  • 生成方式类名.classClass.forName()实例对象.getClass()
  • 常用方法getName()getSuperclass()getInterfaces()asSubclass() 等。
  • asSubclass:用于将 Class 对象转换为指定类的子类类型。
  • 静态加载与动态加载:静态加载在编译时提供类定义,动态加载在运行时按需提供。

通过掌握 Class 对象和反射机制,可以在运行时动态操作类的成员,实现灵活的编程。

十六、小游戏(进击的小鸟)

public class StartGame {           //游戏开始类public static void main(String[] args) throws InterruptedException {JFrame jFrame = new JFrame("进击の小鸟");  //创建窗口对象jFrame.setSize(400,600);//窗口大小jFrame.setLocationRelativeTo(null); //窗口相对位置jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设定点击关闭结束程序BirdGame birdGame = new BirdGame();  //初始化游戏对象类
​    jFrame.add(birdGame);         //把创建好的对象加进来
​    jFrame.setVisible(true);       //让窗口可视化
​    birdGame.action();          //地面运动方法}
}AI写代码java运行
public class Bird {public BufferedImage images[];public BufferedImage image;   //存放小鸟图片public int x;public int y;public int width;public int height;public int index=0;public double speed=0;    //小鸟初始速度public double upspeed=30;  //初始上抛速度public double s=0;      //经过t,发生的位移public double t=0.2;     //发生位移时间public double g=9.8;     //重力加速度public Bird() throws IOException {x=120;y=120;images=new BufferedImage[8];image= ImageIO.read(getClass().getResource("0.png"));width=image.getWidth();height=image.getHeight();for (int i=0;i<images.length;i++) {images[i] = ImageIO.read(getClass().getResource(i+".png"));}}public void fly(){      //小鸟飞飞index++;image=images[index/2%8];}public void upSpeed(){    //鼠标点击游戏屏幕,给小鸟一个初始上抛速度speed=upspeed;}public void distanceChange(){  //实现小鸟速度,位移,纵坐标变化double v=speed; //初始速度s=v*t-g*t*t/2;  //经过t小鸟的位移speed=v-g*t;   //小鸟经过时间t的末速度y=y-(int)s;      //经过时间t后,小鸟的y}
}AI写代码java运行
public class Column {    //管道类public BufferedImage cImage;public int x;public  int y;public int width;public int height;public  int distance=270;  //两根管道之间的距离public static  int count=0;Random random = new Random();public Column() throws IOException {cImage= ImageIO.read(getClass().getResource("column.png"));x=450+distance*count;width=cImage.getWidth();   //获得管道的宽height=cImage.getHeight();  //高y=-( height/2-random.nextInt(300)-50);count++;}public void step(){x-=5; //让地面往左运动if (x<=-width/2){x=x+distance*2;y=-(height/2-random.nextInt(300)-50) ;//x=400;}}
}AI写代码java运行
public class Ground {  //地面类public BufferedImage image; //存放地面图片public int x;public int y;public Ground() {try {
​      x=0;
​      y=500;
​      image= ImageIO.read(getClass().getResource("ground.png"));} catch (IOException e) {
​      e.printStackTrace();}}public void step(){
​    x-=1; //让地面往左运动if (x==-100){
​      x=0;}}
}AI写代码java运行
public class Music implements Runnable {    //音乐类Player player=null;@Overridepublic void run() {InputStream resourceAsStream = this.getClass().getResourceAsStream("2.mp3");try {player=new Player(resourceAsStream);player.play();} catch (JavaLayerException e) {e.printStackTrace();}}public void stopBGM(){if (player!=null)player.close();}
}AI写代码java运行
public class Score {  //连接对象private String sid;private int score;private String time;public String getSid() {return sid;}public void setSid(String sid) {this.sid = sid;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}public String getTime() {return time;}public void setTime(String time) {this.time = time;}
}AI写代码java运行
public class ScoreManager {   //jdbc连接static{try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}public List<Score> selectAllScore(){     //查询方法List<Score> list = new ArrayList<>();try {String sql="select * from score order by time";Connection conn = DriverManager.getConnection("jdbc:mysql://cdb-kthncrwi.bj.tencentcdb.com:10159/flybird?useUnicode=true", "student", "521qianfeng");PreparedStatement pst = conn.prepareStatement(sql);ResultSet resultSet = pst.executeQuery();while (resultSet.next()){Score score = new Score();
​        score.setSid(resultSet.getString("sid"));
​        score.setScore(resultSet.getInt("score"));
​        score.setTime(resultSet.getString("time"));
​        list.add(score);}} catch (SQLException e) {
​      e.printStackTrace();}return list;}public int insertScore(int score) {     //插入方法int num = 0;String sql = "insert into score(sid,score,time) value(?,?,?)";try {Connection conn = DriverManager.getConnection("jdbc:mysql://cdb-kthncrwi.bj.tencentcdb.com:10159/flybird?useUnicode=true", "student", "521qianfeng");PreparedStatement pst = conn.prepareStatement(sql);String sid= UUID.randomUUID().toString();     //随机生成id
​      pst.setString(1,sid);
​      pst.setInt(2,score);Date date = new Date();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     //创建时间类型对象String time=simpleDateFormat.format(date);
​      pst.setString(3,time);
​      num=pst.executeUpdate();} catch (SQLException e) {
​      e.printStackTrace();}return num;}
}AI写代码java运行
public class BirdGame extends JPanel {   //自定义面板类继承面板类ScoreManager sc=new ScoreManager();public JPanel jp=new JPanel();public BufferedImage bg;         //图片缓冲区(在显示图片前对图片进行操作 eg:.getWidth()宽,.getHeight()高)public BufferedImage startbg;public BufferedImage overbg;public Ground ground;public Bird bird;public Column columns[];public Music music;String file="H:\\Java程序\\小程序\\src\\小鸟\\png\\bg.png";public int state; //表示游戏状态public static final int START=0;   //开始public static final int RUNNING=1;  //运行public static final int GAMEOVER=2; //结束public static int score=0;      //初始积分public BirdGame(){try {state=START;         //游戏初始为游戏开始状态ground=new Ground();     //创建地面类对象,调用地面类构造方法bird = new Bird();columns=new Column[2];music = new Music();for (int i=0;i<columns.length;i++){columns[i]=new Column();}bg= ImageIO.read(getClass().getResource("bg.png")); //读取这张图片并把图片值赋给变量//bg=ImageIO.read(new File(file));//bg=ImageIO.read(new File("src/小鸟/png/bg.png"));startbg=ImageIO.read(getClass().getResource("start.png"));overbg=ImageIO.read(getClass().getResource("gameover.png"));} catch (IOException e) {e.printStackTrace();}}@Overridepublic void paint(Graphics g) {         //绘制一次的画画方法super.paint(g);               //调用画笔g.drawImage(bg,0,0,null);            //绘制背景(最后一个参数为观察者switch (state){case START://绘制游戏开始图片settishi(g);g.drawImage(startbg,0,0,null);break;case RUNNING:for (int i=0;i<columns.length;i++) {
​          g.drawImage(columns[i].cImage, columns[i].x, columns[i].y, null);}break;case GAMEOVER://绘制游戏结束图片settishi2(g);
​        g.drawImage(overbg,0,0,null);break;}
​    g.drawImage(ground.image,ground.x,ground.y,null);  //绘制地面
​    g.drawImage(bird.image,bird.x, bird.y,null);     //绘制小鸟setScore(g);}public boolean isHitGround(){      //撞击地面if (bird.y+bird.height>500){return true;}else {return false;}}public boolean isHitSky(){      //撞击天空if (bird.y<0){return true;}else {return false;}}public boolean isguandao(Column c) {if (bird.x + bird.width >= c.x && c.x + c.width >= bird.x) {   //撞击管道左右if (bird.y <= c.height / 2 + c.y - 72 || bird.y + bird.height >= c.height / 2 + c.y + 72) {return true;} else {return false;}} else {return false;}}public void setScore(Graphics g){               //绘制分数方法Font font = new Font(Font.SERIF, Font.ITALIC, 40);  //罗马字体,斜体,40号g.setFont(font);     //获取字体g.setColor(Color.white);//获取颜色g.drawString(score+"分",40,60);    //画字符串}public void settishi(Graphics g){               //绘制分数方法Font font1 = new Font(Font.SERIF, Font.BOLD, 25);  //罗马字体,斜体,40号g.setFont(font1);     //获取字体g.setColor(Color.black);//获取颜色g.drawString("点击屏幕开始运行",110,400);    //画字符串g.drawString("   制作人---赵嘉盟",120,430);}public void settishi2(Graphics g){               //绘制分数方法Font font2 = new Font(Font.SANS_SERIF, Font.BOLD, 30);  //罗马字体,斜体,40号g.setFont(font2);     //获取字体g.setColor(Color.red);//获取颜色g.drawString("点击屏幕重新开始",100,500);    //画字符串}public void action() throws InterruptedException {    //游戏对象运动方法this.addMouseListener(new BirdMouseListener());   //添加鼠标监听器while (true){switch (state){     //状态不同,对象运动效果不同case START:
​          ground.step();  //调用地面运动方法
​          bird.fly();break;case RUNNING:​          bird.distanceChange();
​          ground.step();  //调用地面运动方法
​          bird.fly();if (isHitGround()||isHitSky()){
​            state=GAMEOVER;break;}for (int i=0;i<columns.length;i++){Column cl=columns[i];
​            cl.step();if (isguandao(cl)){
​              state=GAMEOVER;break;}if (bird.x==cl.x){
​              score++;}}break;case GAMEOVER:
​          music.stopBGM();break;}repaint();  //刷新方法(重新绘制)Thread.sleep(50);  //线程睡眠}}class BirdMouseListener extends MouseAdapter{      //小鸟飞行鼠标控制监听内部类@Overridepublic void mousePressed(MouseEvent e) {super.mousePressed(e);switch (state){case START:
​          state=RUNNING;  //鼠标点击开始运行Thread thread = new Thread(music);
​          thread.start();break;case RUNNING:
​          bird.upSpeed(); //鼠标点击屏幕给小鸟一个初始上抛速度break;case GAMEOVER:
​          sc.insertScore(score);  //向数据库插入分数List<Score> scores = sc.selectAllScore();//查询数据库所有分数String message="";for (Score score1 : scores) {
​            message=message+"时间:"+score1.getTime()+"\n分数:"+score1.getScore()+"\n";}JOptionPane.showConfirmDialog(jp,message,"实时分数",JOptionPane.WARNING_MESSAGE);
​          state=START;   //鼠标点击游戏恢复开始状态​          bird.x=120;
​          bird.y=220;
​          bird.speed=0;Column.count=0;try {
​            columns[0] = new Column();} catch (IOException ex) {
​            ex.printStackTrace();}try {
​            columns[1] = new Column();} catch (IOException ex) {
​            ex.printStackTrace();}
​          score = 0;//给积分初始化for (int i=0;i<columns.length;i++){try {
​              columns[i]=new Column();} catch (IOException ex) {
​              ex.printStackTrace();}}break;}}}
}AI写代码java运行

十七、Stream

Stream简介

Java 8 中的 Stream 是对(Collection)集合对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作

或大批量数据操作。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。

Stream原理

这种编程风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的

结果。

Stream优点

(1)速度更快

(2)代码更少(增加了新的语法Lambda表达式)

(3)强大的Stream API

(4)便于并行

(5)最大化减少了空指针异常Optional

Stream的操作三个步骤:

(1)创建Stream,一个数据源(如:集合、数组),获取一个流;

(2)中间操作,一个中间操作链,对数据源的数据进行处理;

(3)终止操作,一个终止操作,执行中间操作链,并产生结果。

集合有两种方式生成流:

stream() − 为集合创建串行流。

parallelStream() − 为集合创建并行流

-Stream的的中间操作(intermediate)和最终操作(terminal)都包含的方法:


中间操作(intermediate)

1.filter : 通过设置条件来过滤元素。

List<String> list = Arrays.asList("aaa","ddd","bbb","ccc","a2a","d2d","b2b","c2c","a3a","d3d","b3b","c3c");list.stream().filter((s)->s.contains("a")).forEach(s -> System.out.println(s));AI写代码java运行

以上代码使用filter方法过滤出只包含”a”的元素,然后通过forEach将满足条件的元素遍历出来。

map : 就是将对应的元素使用给定方法进行转换。

List<String> list = Arrays.asList("aaa","ddd","bbb","ccc","a2a","d2d","b2b","c2c","a3a","d3d","b3b","c3c");list.stream().filter((s)->s.contains("a")).map((s)-> s + "---map").forEach(s -> System.out.println(s));AI写代码java运行

在filter的基础上,给每个元素后面添加字符串”—map”

flatMap:如果流的元素为数组或者Collection,flatMap就是将每个Object[]元素或Collection元素都转换为Object元素。

List<String[]> setList = new ArrayList<>();setList.add(new String[]{"aa","bb"});setList.add(new String[]{"cc","dd"});setList.add(new String[]{"ee","ff"});//使用map方法setList.stream().map(s->Arrays.stream(s)).forEach(s-> System.out.println("map==" + s));//使用flatMap方法setList.stream().flatMap(s->Arrays.stream(s)).forEach(s-> System.out.println("flatMap==" + s));AI写代码java运行

map就是将数组流直接返回,flatMap是将数组流中的每个元素都返回。

.distinct:将集合中的元素去重。

List<String> disList = Arrays.asList("aaa","ddd","bbb","ddd","aaa");disList.stream().distinct().forEach(s-> System.out.println(s));AI写代码java运行

sorted:将集合中的元素排序。

List<Integer> integerList = Arrays.asList(2,4,1,3);integerList.stream().sorted().forEach(s-> System.out.println(s));AI写代码java运行

可以按照自定义排序:

List<Integer> integerList = Arrays.asList(2,4,1,3);integerList.stream().sorted((s1,s2)->s2.compareTo(s1)).forEach(s-> System.out.println(s));AI写代码java运行

peek:生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数即引用的方法A,当Stream每个元素被消费的时候都会先
执行新Stream给定的方法A。peek是中间操作,如果peek后没有最终操作,则peek不会执行。

List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().peek(s-> System.out.println("peek = "+s)).forEach(s-> System.out.println("forEach = "+s));AI写代码java运行

limit:返回Stream的前n个元素。

List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().limit(2).forEach(s-> System.out.println(s));AI写代码java运行

skip:删除Stream的前n个元素。

List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().skip(2).forEach(s-> System.out.println(s));AI写代码java运行

终端操作(terminal)

1.forEach:遍历Stream中的每个元素,前面每个例子都有使用,此处不再演示。

List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.stream().skip(2).forEach(s-> System.out.println(s));AI写代码java运行

forEachOrdered:遍历Stream中的每个元素。
区别: 在串行流(stream)中没有区别,在并行流(parallelStream)中如果数据源是有序集合,forEachOrdered输出顺序与数据源中顺序
一致,forEach则是乱序。

List<Integer> integerList = Arrays.asList(1,2,3,4);integerList.parallelStream().forEachOrdered(s-> System.out.println(s));AI写代码java运行

toArray:将流转换为Object[]或者指定类型的数组。

List<Integer> integerList = Arrays.asList(1,2,3,4);Object[] array = integerList.stream().toArray();String[] strArr = integerList.stream().toArray(String[]::new);AI写代码java运行

Stream中的toArray普通情况下和集合中的toArray没什么区别,但是Stream中的toArray转换为指定类型的数组。

reduce:将集合中的每个元素聚合成一条数据。有三种情况:

reduce(BinaryOperator accumulator):此处需要一个参数,返回Optional对象:

Optional reduce = integerList.stream().reduce((a, b) -> a + b);

reduce(T identity, BinaryOperator accumulator):此处需要两个参数,第一个参数为起始值,第二个参数为引用的方法

从起始值开始,每个元素执行一次引用的方法(方法引用的中的两个参数:第一个参数为上个元素执行方法引用的结果,第二个参数为当前元素)。

    List<Integer> integerList = Arrays.asList(1,2,3,4);int integer = integerList.stream().reduce(5,(a, b) -> a + b);System.out.println(integer);AI写代码java运行

此例中使用起始值为5,对集合中每个元素求和,可以理解为:5+1+2+3+4=15。

**reduce:**此处需要三个参数。此方法用在并发流(parallelStream)中,启动多个子线程使用accumulator进行并行计算,最终使用combiner对子线程结果进行合并,返回identity类型的数据。

collect:将流转换成集合或聚合元素。有两种情况。接受一个参数和接受三个参数(三个参数在并发流parallelStream中使用),此处介绍一个参数的情况,单个参数接受的参数类型为Collector,Collectors 类实现了很多归约操作

List<Integer> integerList = Arrays.asList(2,4,1,3);List<Integer> integers = integerList.stream().filter(s -> s > 1).collect(Collectors.toList());System.out.println(integers.toString());AI写代码java运行

此处统计集合中大于1的元素并最终返回list。

min:获取集合中最小值。

List<Integer> integerList = Arrays.asList(2,4,1,3);Integer min = integerList.stream().min(Integer::compareTo).get();System.out.println(min);AI写代码java运行

max:获取集合中最大值。

List<Integer> integerList = Arrays.asList(2,4,1,3);Integer max = integerList.stream().max(Integer::compareTo).get();System.out.println(max);AI写代码java运行

count:获取集合中元素个数

List<Integer> integerList = Arrays.asList(2,4,1,3);long count = integerList.stream().count();System.out.println(count);AI写代码java运行

原文地址:https://blog.csdn.net/m0_57376564/article/details/148143797

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

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

相关文章

火山引擎发布大模型生态广场MCP Servers,LAS MCP助力AI数据湖构建

资料来源&#xff1a;火山引擎-开发者社区 近日&#xff0c;火山引擎发布大模型生态广场—— MCP Servers&#xff0c;借助字节跳动生态能力&#xff0c;通过“MCP Market&#xff08;工具广场&#xff09; 火山方舟&#xff08;大模型服务&#xff09;Trae&#xff08;应用开…

NodeJS 对接 Outlook 发信服务器实现发信功能

示例代码&#xff1a; const express require(express); const nodemailer require(nodemailer); const querystring require(querystring); const axios require(axios);const app express(); app.use(express.json());const transporter nodemailer.createTransport({…

【同声传译】RealtimeSTT:超低延迟语音转文字,支持唤醒词与中译英

把你说的话实时变成文字&#xff1a;RealtimeSTT 上手体验 想找一个真正好用的语音转文字工具吗&#xff1f;不用等说完一整段才出结果&#xff0c;也不用反复点击按钮。RealtimeSTT 这个开源项目能做到​​实时​​转录&#xff0c;你说一句&#xff0c;屏幕上几乎同时出现文…

【大模型lora微调】关于推理时如何使用 LoRA Adapter

假设你有两部分&#xff1a; 一个是原始大模型&#xff08;base model&#xff09; 一个是保存的 LoRA Adapter&#xff08;adapter_config.json adapter_model.bin&#xff09; 不合并的情况下推理方法 你可以用 peft 的方式加载 LoRA Adapter&#xff0c;推理时这样写&a…

谷歌时间序列算法:零样本预测如何重塑行业决策?

谷歌时间序列算法&#xff1a;零样本预测如何重塑行业决策&#xff1f; TimesFM 你是否曾面临这样的困境&#xff1f;—— ▸ 需要预测新产品销量&#xff0c;却苦于缺乏历史数据&#xff1b; ▸ 依赖传统模型&#xff08;如ARIMA&#xff09;&#xff0c;但调参耗时且泛化能力…

国产服务器【银河麒麟v10】【CPU鲲鹏920】部署Minio文件服务器

目录 准备工作操作步骤1. 确认挂载点状态2. 创建专用用户和目录3. 下载ARM版Minio到挂在盘4. 环境变量配置5. 更新Systemd服务配置6. 启动、重启7. 防火墙8. 访问验证9. 故障排查&#xff08;如服务未启动&#xff09;​ 结束 准备工作 环境要求&#xff1a;Linux虚拟机 操作…

解决: React Native android webview 空白页

Android react-native-webview 之前是正常的, 升级了 react-native / react-native-webview 等 之后, 就变成了空白页. 通过下面的修改, 可以修复, 回到正常的状态. 来源: https://github.com/react-native-webview/react-native-webview/issues/3697 注意 ts 文件一定要改,…

高中编程教学中教师专业发展的困境与突破:基于实践与理论的双重审视

一、引言 1.1 研究背景 在数字化时代&#xff0c;编程已成为一项基本技能&#xff0c;其重要性日益凸显。编程不仅是计算机科学领域的核心能力&#xff0c;更是培养学生逻辑思维、创新能力和问题解决能力的有效途径。高中阶段作为学生成长和发展的关键时期&#xff0c;开展编…

最小化联邦平均(FedAvg)的算法开销

一、通信开销最小化 FedAvg中服务器与客户端间的频繁参数传输是主要瓶颈&#xff0c;可通过以下方法优化&#xff1a; 1. 模型压缩技术 稀疏化&#xff1a;仅上传重要参数更新&#xff08;如Top-k梯度&#xff09; 实现&#xff1a;客户端本地训练后&#xff0c;保留绝对值最…

准备开始适配高德Flutter的鸿蒙版了

我们的Flutter项目在编译为鸿蒙的过程中&#xff0c; 遇到了各种插件不支持的问题。 大部分都能解决&#xff0c;或者用别的方式代替。 这个高德我真的是无语&#xff0c; 我们只能用高德 &#xff0c; 目前还没看到网上有人适配了鸿蒙。 那就我来干吧&#xff0c; 第一…

webpack到vite的改造之路

前言 随着前端项目的持续迭代与功能扩展&#xff0c;当前基于 Webpack 构建的项目在启动速度、构建速度和首屏加载性能方面逐渐暴露出一些瓶颈。 一方面&#xff0c;Webpack 的打包机制导致本地开发环境的启动时间显著增加&#xff0c;严重影响了开发效率&#xff1b;另一方面…

【重构】如果发现提取的方法不再通用,如何重构

前言 所谓重构&#xff08;refactoring&#xff09;&#xff1a; 在不改变代码外在行为的前提下&#xff0c;对代码做出修改&#xff0c;以改进程序的内部结构。 – Martin Fowler背景 最近在做需求&#xff0c;需要对方法加权限控制&#xff0c;发现旧方法不再适用&#xff0…

REST接口/RPC

REST接口(RESTful API)是一种基于HTTP协议的API设计风格,遵循REST(Representational State Transfer表述性状态转移)架构原则,用于在不同系统之间进行数据交互。它具有简洁、灵活、无状态等特点,广泛应用于Web服务和移动应用开发中。 核心概念 资源导向 将数据或服务抽…

JS入门——事件与事件绑定

JS入门——事件与事件绑定 一、事件的分类 二、事件的绑定方式 实现代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title>JS事件绑定</title></head><body><!-- 修复后的按钮1 -->&…

pyspark 处理字符串函数

pyspark 要处理数据&#xff0c;没有&#xff0c;那就伪造数据 faker 真是个好东西 from faker import Faker import pandas as pd gender ["None","Man","Woman"]fake Faker() names [(fake.first_name(),fake.last_name(),fake.date_of_bi…

五大经典语音芯片型号及应用场景

在语音芯片领域&#xff0c;这五大语音芯片凭借丰富多样的产品和卓越的性能&#xff0c;占据了重要地位。以下为您详细介绍其五款经典语音芯片型号及其对应的应用场景。​ WTN6170-8S​ WTN6170-8S 属于 OTP 一次性语音芯片。它采用 OTP 工艺&#xff0c;成本能够控制在 1 元以…

机器学习管道:构建高效可靠的AI工作流

在当今数据驱动的世界中&#xff0c;机器学习(ML)已成为推动创新和决策的核心技术。然而&#xff0c;将ML模型从实验环境成功部署到生产环境并非易事。机器学习管道(ML Pipelines)作为一种系统化的解决方案&#xff0c;通过自动化工作流程&#xff0c;显著提高了ML项目的可重复…

浏览器调试核心技术指南:从基础到高级的完全掌握

引言​​ 在现代前端开发中,浏览器调试工具已成为开发者最强大的技术伙伴。根据State of JS 2023的统计数据,​​92.7%的专业开发者​​每天使用浏览器DevTools进行问题诊断和性能优化。然而,多数初级开发者仅能使用不到35%的调试功能。本文将系统解析Chrome/Firefox浏览器…

OpenCV 图像翻转

一、知识点 1、void flip(InputArray src, OutputArray dst, int flipCode); (1)、围绕x轴、y轴或两者同时翻转图像。 (2)、参数说明: src: 输入图像。 dst: 输出图像&#xff0c;大小与类型和src相同。 flipCode: 翻转标志。 0表示绕x轴翻转(上下翻转);…

【动手学深度学习】4.2~4.3 多层感知机的实现

目录 4.2. 多层感知机的从零开始实现1&#xff09;初始化模型参数2&#xff09;激活函数3&#xff09;模型4&#xff09;损失函数5&#xff09;训练 4.3. 多层感知机的简洁实现1&#xff09;模型2&#xff09;小结 . 4.2. 多层感知机的从零开始实现 现在让我们实现一个多层感…