Java面向对象编程
什么是面向对象编程(OOP)?
它将数据和相关操作封装在对象中,通过对象之间的交互来解决问题。
简单来说,就是世界上所有的物品,都可以是一个对象,对象是一种特殊的数据结构,里面存储物品的相关属性信息,类似于一张表结构。
类就相当于对象的一个模版,结构相同的对象可以使用同一个类来构造
JVM虚拟机
JVM是什么?
JVM(Java虚拟机)并不是直接安装在操作系统上的一个单独的实体或文件夹,而是一种抽象的计算模型,它通常是由一个程序或者软件包的一部分来实现的。当你在计算机上安装Java运行环境(例如,Java Development Kit, JDK 或者 Java Runtime Environment, JRE)时,实际上就包含了JVM的实现
当你运行一个Java程序时,比如通过命令行输入java YourProgramName,操作系统会调用相应的Java执行文件,这个执行文件负责初始化JVM,并加载、验证、解释执行Java字节码。因此,从操作系统的角度来看,JVM是在程序运行时动态创建的一个进程,它存在于内存中
JVM工作流程
- 编译:Java源代码被编译成字节码(.class文件)。
- 加载:类加载器将字节码加载到内存中。
- 验证:验证字节码是否符合JVM规范。
- 准备与解析:为类变量分配内存并设置初始值,将符号引用转换为直接引用。
- 执行:执行引擎负责执行字节码,通过解释或即时编译的方式。
- 垃圾回收:自动管理内存,释放不再使用的对象占用的空间。
JVM特点
JVM提供了一个安全、可移植的执行环境,使得Java程序能够“一次编写,到处运行”。它通过将Java程序编译成字节码,并在目标平台上通过JVM解释或编译成机器码来执行,从而实现了跨平台的能力
Java程序的运行
Java程序是在内存里的JVM虚拟机上运行的
运行一个java程序时,先把JVM虚拟机运行到内存,再将程序运行到虚拟机中
虚拟机将内存划分为栈内存、堆内存、方法区来配合执行java程序
- 方法区 : 存放类文件
- 栈内存 : 方法执行是放在栈里面的
- 堆内存 : 对象存放在堆中的
类的基本语法
构造器
什么是构造器?
是一种特殊方法,无返回值类型声明,方法名与类名一致
构造器包含无参构造器和有参构造器(重载方法)
构造器的特点
创建对象时会立即调用构造器,即可以创建对象时可以立即为对象赋值
注意
- 类默认自带一个无参构造器
- 如果类定义了有参构造器,类默认的无参构造器就没有了
this
this是什么?
- this是一个变量,可以用在方法中,来拿到当前对象
- 哪个对象调用这个方法,this就拿到哪个对象
this的应用场景
解决变量名冲突问题
封装
面向对象三大特征
封装、继承、多态
封装的设计要求
类就是一种封装(合理隐藏,合理暴露)
- 合理隐藏 :private(私有)关键字修饰成员变量,使得变量只能在本类中直接访问
- 合理暴露 :public(公开)修饰的get和set方法使得成员变量可以取值和赋值
Javabean(实体类)
什么是实体类?
是一种特殊类,类中要满足如下需求:
- 类中的成员变量全部私有,并提供public修饰的getter/setter方法
- 类中需要提供一个无参构造器,有参构造器可选
实体类的作用
创建它的对象,存取数据(封装数据)
实体类的应用场景
实体类的对象只负责数据存取,而对数据的业务处理交给其他类的对象来完成,以实现数据和数据业务处理相分离
static(静态)
定义
概念:静态,可以修饰成员变量、成员方法
分类:成员变量按照有无static修饰,分为静态变量(类变量)和 实例变量(对象的变量)
静态变量的应用场景: 如果某个数据只需要一份,且希望能够被共享(访问修改)。
- 静态变量(类变量):有static修饰,属于类,在计算机中只有一份,会被类的全部对象共享
- 实例变量(对象的变量) : 无static修饰,属于每个对象的
访问静态变量的方法
类名.静态变量(推荐)
对象.静态变量
static修饰方法
- 应用场景:
如果这个方法仅仅是为了实现一个功能而不需要访问对象数据,这个方法就可以直接定 义成静态方法
做工具类:工具类没有创建对象的必要,建议将工具类的构造器私有
- 调用静态方法:
类名.静态方法(推荐)
对象名.静态方法 (不推荐)
注意
- 静态方法中可以直接访问静态成员,不可以直接访问实例成员
- 实例方法中既可以直接访问静态成员,也可以直接访问实例成员
- 实例方法中可以出现this关键字,静态方法中不可以出现this关键字
继承
概念
继承的优点
提高代码的重用性,减少一些代码书写
继承是什么?
Java中提供一个关键字extends,用这个关键字,可以让两个类建立起父子关系
继承的特点
- 单继承 :一个类只能继承一个直接父类
- 多层继承 :Java不支持多继承,但支持多层继承
- 祖宗类 :Object是所有类的祖宗类,应该类要么手动继承Object、要么默认继承、 Object要么间接继承Object
- 就近原则:优先访问自己类中,自己类中没有才会访问父类 super关键字可以指定访问父类的成员:super.父类成员变量/方法
语法
基本语法
public class B extends A{
}
解释:
A为父类(基类 或 超类),B为子类(派生类),子类能得到父类中的所有非私有属性方法 ,子类的对象是由子类和父类共同完成的。
权限修饰符
继承允许一个类(子类)继承另一个类(父类)的属性和方法。
子类可以覆盖或扩展父类的行为。
在Java中,通过关键字extends实现继承关系。
访问控制修饰符public、protected、default(即无修饰符)和private决定了成员的可见性。
- public修饰的成员可以在任何地方被访问,包括不同包中的类。
- protected修饰的成员可以被同一包中的类以及不同包中的子类访问。
- default修饰的成员仅限于同一包内的类访问。
- private修饰的成员只允许在同一类内部访问。
private | 只能本类 |
缺省 | 本类、同一个包中的类 |
protected | 本类,同一个包中的类、子孙类中 |
public | 任意位置 |
范围:private < 缺省 < protected < public
1.四种修饰符修饰的方法在同一个类中都可访问
2.在同一个包下 只能访问public protected default
3.在其他包的子类中,可以访问父类的受保护方法、共有方法
4. 在另一个包中,只能访问public修饰的方法
方法重写
定义
当子类觉得父类中某个方法不好用或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。’
语法(声明不变,重新实现)
- 1. 子类重写父类方法,子类的访问权限必须大于父类
- 2. 重写的方法返回类型,必须与被重写方法的返回值类型一样,或者范围更小
- 3. 私有方法、静态方法不能被重写
方法重写的校验注解(标志):要求方法名称和形参列表与被重写方法一致
@Override // 方法重写的校验注解(标志):要求方法名称和形参列表与被重写方法一致
eg:
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.show();
}
}
// 方法重写 : 方法名称,形参列表必须一样
class People{
private String name;
private int age;
public void show(){
System.out.println("父类");
}
}
class Student extends People{
private int id;
private int score;
@Override // 方法重写的校验注解(标志):要求方法名称和形参列表与被重写方法一致
public void show(){
System.out.println("子类");
}
}
例子:
重写Object的toString方法:
public class Test01 {
public static void main(String[] args) {
Film f = new Film("《野孩子》", 35, 1);
System.out.println(f);
}
}
class Film extends Object{
private String name;
private int price;
private int id;
@Override
public String toString() {
return "\tStudentname:" + name + "\n\tprice:" + price + "\n\tid:" + id + "\n\t9.13不见不散";
}
public Film() {
}
public Film(String name, int price, int id) {
this.name = name;
this.price = price;
this.id = id;
}
public String getName() {
return name;
}
子类构造器
特点
子类的全部构造器,都会先调用父类构造器,再执行自己
语法
子类构造器是如何实现调用父类构造器的:
默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参构造器
public class Test {
public static void main(String[] args) {
// 子类构造器都会先调用父类的构造器,再执行自己的构造器
Son s = new Son();
}
}
class Father{
private Father() {
System.out.println("Father");
}
public Father(int x) {
System.out.println("666");
}
}
class Son extends Father{
public Son() {
super(666); //指定调用父类有参构造器
System.out.println("Son");
}
}
this(...)调用兄弟构造器
语法
注意
this(...)和super(...)都要写在构造器的第一行,而且两种不能同时出现
多态
概念
定义
多态是在继承/实现情况下的一种抽象,表现为:对象多态、行为多态。
多态是对象、行为的多态,Java中属性(成员变量)不谈多态。
多态的条件
1.有继承/实现关系
2.存在父类引用子类对象
3.存在方法重写
多态的优点
提高代码的通用性和可扩展性:
多态允许你编写更加通用的代码,通过父类或接口类型的引用可以操作任何子类的对象。这意味着,如果需要添加新的子类,通常不需要修改已有的代码,只需添加新的子类即可。这种扩展性符合开闭原则(Open/Closed Principle),即对扩展开放,对修改关闭。
减少代码冗余:
由于多态允许你通过一个共同的接口来访问不同类型的对象,因此减少了代码的重复。你可以在一个地方编写代码来处理所有子类的对象,而不需要为每个子类单独编写类似的代码。
增强程序的灵活性:
多态性增强了程序的灵活性,因为你可以更容易地更改或替换系统中的组件。如果一个模块需要被替换或升级,只要新模块实现了相同的接口,那么大多数情况下无需修改其他部分的代码。
提高开发效率:
多态性简化了代码的设计和实现过程,使得开发人员能够更快速地开发出高质量的应用程序。
易于维护:
基于继承关系,只需要维护父类代码,就可以保证所有继承自该父类的子类都会得到更新。这提高了代码的复用性,同时也降低了维护工作的复杂度。
解耦:
多态有助于降低模块间的耦合度,因为它们之间可以通过接口进行通信,而不是直接依赖于具体的实现类。这使得系统的各个部分更加独立,易于理解和修改。
使用多态的好处
1. 在多态形式下,右边对象是解耦合的,便于扩展与维护
2. 定义方法时,使用父类的形参,则该方法可以接收一切子类对象,扩展性更强、更便利。
多态下类型转换
语法
自动类型转换: 父类 变量名 = new 子类();
强制类型转换 : 子类 变量名 = (子类)父类变量;
注意
运行时如果发现强转后类型与真实类型不符,则会报类型转换异常(ClassCastException)的错误来
Java建议
使用instanceof关键字,判断当前对象的真实类型,再进行强转
面向对象高级
final关键字
语法
修饰类 | 该类是最终类,特点是不能被继承了 |
修饰方法 | 该方法被称为最终方法,特点是不能被重写了 |
修饰变量 | 该变量有且仅能被赋值一次 |
变量分类
1.局部变量: 定义在方法内部或者某个代码块内的变量。这些变量只在定义它们的方法或代码块内有效。
2.成员变量: 定义在类内部但方法外部的变量。成员变量对整个类都是可见的,并且每个对象都有其独立的一份副本。
3.静态变量: 通过static关键字声明的成员变量。静态变量被所有对象共享,只有一个副本存在于内存中
注意
- final修饰基本类型的变量,变量存储的数据不能被改变
- final修饰引用类型的变量(如数组),变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的
常量
概念
定义 :使用static final修饰的成员变量就称之为常量
作用: 常用于记录系统的配置信息
单例类(设计模式)
概念
作用 : 确保每个类只能创建一个对象
分类 :饿汉式和懒汉式
语法
1. 饿汉式单例 :拿到对象的时候,对象就已经创建好了
写法 :
1.把类的构造器私有
2.定义一个类变量记住类的一个对象
3.定义一个类方法,返回对象
// 单例类 : 饿汉式单例
public class A {
// 2.定义一个类变量记住类的一个对象
private static A a = new A();
// 1.将构造器私有
private A() {
}
//3. 定义一个类方法返回对象
public static A getObject() {
return a;
}
}
2. 懒汉式单例 :拿到对象的时候,对象才创建
写法:
1.把类的构造器私有
2.定义一个静态变量用于存储对象
3.定义一个静态方法,保证返回的是同一个对象
// 单例类 : 懒汉式单例
public class B {
// 2.定义一个静态变量用于存储对象
private static B b;
// 1.把类的构造器私有
private B() {
}
// 3.定义一个静态方法,保证返回的是同一个对象
public static B getObject() {
if (b == null) {
b = new B();
}
return b;
}
}
使用
枚举类
概念
枚举类 : 枚举是一种特殊的类
特点 :
枚举类的第一行只能写枚举类的对象名称,且要逗号隔开
这些名称本质是常量,每个常量都记住了枚举类的一个对象
枚举类的构造器都是私有的(写不写都只能是私有的,因此,枚举类对外不能创建对象)
应用场景:信息分类和标志
语法
写法:
修饰符 enum 枚举类名 {
名称1,名称2,......
其他成员...
}
public class Test {
/**
枚举类 : 枚举是一种特殊的类
写法:
修饰符 enum 枚举类名 {
名称1,名称2,......
其他成员...
}
特点 :
枚举类的第一行只能写枚举类的对象名称,且要逗号隔开
这些名称本质是常量,每个常量都记住了枚举类的一个对象
枚举类的构造器都是私有的(写不写都只能是私有的,因此,枚举类对外不能创建对象)
应用场景:
1.信息分类和标志
2.
* */
public static void main(String[] args) {
A a1 = A.A1;
System.out.println(a1);
A a2 = A.A2;
System.out.println(a2.toString());
A a3 = A.A3;
System.out.println(a3.name());
// ordinal() : 获取枚举对象的序号,从0开始
System.out.println(a1.ordinal());
System.out.println(a2.ordinal());
System.out.println(a3.ordinal());
}
}
抽象类
概念
抽象类(abstract) : 它就是抽象的意思,可以用它修饰类、成员方法
作用 :被子类继承,实现父类中的抽象方法
核心特点 :有得有失(得到了抽象方法的能力,失去了创建对象的能力)
抽象类不能创建对象
抽象类中可以有抽象方法,也可以有非抽象方法
特点 :
1.抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
2.抽象类中不一定要有抽象方法,有抽象方法的类必须是抽象类
3.一个类继承抽象类,必须重写玩抽象类的全部抽象方法,否则这个类也必须定义成抽象类应用场景
模板方法设计模式
语法
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名称(形参列表)
}
接口
概念
接口 :Java提供了一个关键字interface来定义接口
接口的好处 :
1. 弥补了类单继承的不足,一个类同时可以实现多个接口
2. 让程序可以面向接口编程,更利于实现程序的解耦合接口与类:
- 接口与接口 :多继承,一个接口可以继承多个接口
- 接口与类:多实现,一个接口可以有多个实现方法
- 类与类: 单继承,一个类只能继承一个直接父类
语法
传统接口写法(interface):
1.接口中定义常量,可以省略public static final
2.接口中定义抽象方法,可以省略public abstract
public interface 接口名{
// 成员变量 (常量)
// 成员方法 (抽象方法)
}
类实现接口(implements)
解析:
接口是用来被类实现 (implements)的,实现接口的类是接口类,一个类可以实现多个接口 :
写法:
修饰符 class 实现类类名 implements 接口名1,接口名2,接口名3, ... {
// 实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类要被定义成抽象类
}
接口继承接口(extends)
解析 :
接口可以继承接口,接口可以继承多个接口,接口继承接口,必须使用extends关键字 :
写法:
public interface 接口名 extends 接口名1,接口名2,接口名3, ... {
// 成员变量 (常量)
}
注意 :
1. 接口不能创建对象
接口新增的三种方法(从jdk8开始)
1. 默认方法(实例方法) : 使用default修饰,默认会被加上public修饰
注意 : 只能使用接口的实现类对象调用
2. 私有方法 : 必须用private修饰(jdk9开始支持)注意 : 只能使用接口中其它实例方法来调用
3. 类方法(静态方法) : 使用static修饰,默认会被加上public修饰
注意 :只能用接口名来调用
public interface A {
// 从jdk8开始接口新增的方法
// 1. 默认方法(实例方法) : 使用default修饰,默认会被加上public修饰
// 注意 : 只能使用接口的实现类对象调用
default void show() {
System.out.println("A.show()");
}
// 2. 私有方法 : 必须用private修饰(jdk9开始支持)
private void show1() {
System.out.println("A.show1()");
}
// 3. 类方法(静态方法) : 使用static修饰,默认会被加上public修饰
// 注意 :只能用接口名来调用
static void show2() {
System.out.println("A.show2()");
}
}
抽象类与接口的区别
相同点:
1. 抽象性:它们都可以包含抽象方法,这些方法只有声明而没有具体的实现(body),需要被继承或实现的子类去完成具体的实现。
2. 不可实例化:接口和抽象类本身都不能被实例化,即不能使用new关键字创建它们的对象。如果需要使用,必须通过继承抽象类或实现接口的具体子类来创建实例。
3. 方法实现:无论是继承抽象类还是实现接口的类,都需要提供抽象方法的具体实现。如果一个类继承了一个包含抽象方法的抽象类,或者实现了包含抽象方法的接口,那么这个类也必须是抽象的,除非它提供了所有抽象方法的实现。
4. 引用:接口或抽象类的引用可以指向它们的具体子类或实现类的实例。这意味着你可以使用抽象类或接口类型的变量来引用其实现或继承类的对象,这是多态性的体现。
5. 设计目的:它们都是面向对象设计的一部分,用于定义类之间的关系,提供了一种方式来描述类的公共行为或状态,有助于代码的组织和复用。
不同点 :
1. 实现方式:
接口中的所有方法默认都是抽象的(无实现体),从Java 8开始引入了默认方法(default methods)和静态方法(static methods),可以在接口中提供具体实现。
抽象类可以包含具体实现的方法(即非抽象方法),也可以包含抽象方法。
2. 继承限制:
Java中的类只能继承一个抽象类,即只支持单一继承。
类可以实现多个接口,支持多重继承的效果。
3. 成员变量:
接口只能包含常量(默认为public static final),不允许包含实例变量(非静态变量)。
抽象类可以包含实例变量、常量以及其他类型的成员变量。
4. 构造函数:
接口中不允许包含构造函数。
抽象类可以包含构造函数,这些构造函数可以被子类使用。
5. 方法访问修饰符:
接口中的方法默认是public的,尽管从Java 9开始允许私有方法的存在,但这些方法并不是为了外部实现类使用。
抽象类中的方法可以有不同的访问级别,如public、protected或private。
6. 实现要求:
实现接口的类必须提供接口中所有抽象方法的具体实现,除非该类也被声明为抽象类。
继承抽象类的子类可以选择性地实现抽象类中的抽象方法,也可以直接继承抽象类中的具体方法。
7. 设计意图:
抽象类通常用于提供一组默认的行为和一些需要子类实现的方法,可以看作是一系列相关类的基础模板。
接口主要用于定义行为规范,即定义了一组方法签名,表示类具备某种能力或遵循某种协议。
8. 使用场景:
抽象类适合于那些有很多通用行为的类,这些行为可以被具体化并直接继承。
接口适合于定义类应该具备的行为,而不管这些行为是如何实现的。
以上是chatgpt生成的,在我看来抽象类与接口在语法上的区别最主要是:
抽象类可以定义对象和实例方法,抽象类中abstract修饰的方法(即抽象方法)必须由子类进行重写。
接口中定义的大多数是方法头以及常量,没有具体方法的实现,接口中方法的实现要依据接口的实现类来完成具体方法的实施(方法重写),接口可以看作为框架,而实现类可以看作框架中所填的东西
抽象类只能单继承,接口可以多实现
代码块
概念
定义 : 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类)
分类 : 静态代码块和实例代码块
语法
1. 静态代码块:
格式 : static{}
特点 : 类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次
作用 :完成类的初始化
解析:
静态代码块与类一起加载,所以先执行静态代码块再执行main方法
eg:
public class CodeDemo1 {
// 静态代码块 : 有static修饰, 静态代码块在类加载的时候执行,只执行一次。
public static String[] card = new String[13];
static {
card[0] = "A";
card[1] = "2";
card[2] = "3";
card[3] = "4";
card[4] = "5";
card[5] = "6";
card[6] = "7";
card[7] = "8";
card[8] = "9";
card[9] = "10";
card[10] = "J";
card[11] = "Q";
card[12] = "K";
}
public static void main(String[] args) {
System.out.println(Arrays.toString(card));
}
}
2. 实例代码块:
格式:{}
特点 : 每次创建对象时,执行实例代码块,并在构造器前执行
作用 : 完成对象的初始化
解析:
每次创建对象实例代码块中的内容都会执行
eg:
内部类
概念
定义 : 一个类定义在另一个类的内部。这个类就是内部类
内部类访问外部类:
成员内部类可以直接访问外部类的静态成员,也可以直接访问外部类的实例成员
语法
写法:
使用:
匿名内部类
概念
1. 是一种特殊的局部内部类
2. 匿名:程序员不需要为这个类声明名字,默认有个隐藏的名字
3. 匿名内部类实际上那个是有名字 : 外部类名$编号
4. 匿名内部类本质是一个子类
语法
new 类或者接口(参数值...) {
类体 :一般是方法重写
}
eg:
public class Test {
/**
匿名内部类
概念
1. 是一种特殊的局部内部类
2. 匿名:程序员不需要为这个类声明名字,默认有个隐藏的名字
3. 匿名内部类实际上那个是有名字 : 外部类名$编号
4. 匿名内部类本质是一个子类
应用场景 :
语法
new 类或者接口(参数值...) {
类体 :一般是方法重写
}
**/
public static void main(String[] args) {
Animal a1 = new Dog();
a1.cry();
// 匿名内部类
Animal a2 = new Animal() {
@Override
public void cry() {
System.out.println("匿名内部类");
}
};
}
}
class Dog extends Animal {
@Override
public void cry() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
@Override
public void cry() {
System.out.println("喵喵喵");
}
}
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test3 {
public static void main(String[] args) {
JFrame win = new JFrame("登录窗口");
JPanel panel = new JPanel();
win.add(panel);
JButton btn = new JButton("登录");
panel.add(btn);
win.setSize(300, 200);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("ok");
}
});
}
}
class CLickListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("ok");
}
}
Lambda表达式 :
概念
数学中的函数 : f(x) = 2x + 1
Java中的函数(Lambda表达式): (x) -> 2x + 1
作用 : 用Lambda函数替代某些匿名类对象
注意 : Lambda表达式只能替代函数式接口的匿名内部类
函数式接口 : 有且仅有一个抽象方法的接口
语法
写法 :
(被重写方法的形参列表) -> {
被重写方法的方法体代码
}
Lambda省略规则:
1. 参数类型全部可以省略不写
2. 如果只有一个参数,参数类型省略的同时 "()"也可以省略, 但多个参数不能省略 "()"
3. 如果Lambda表达式方法体只有一行代码,可以省略大括号, 同时要省略分号 ";" 如果这行代码是return语句,也必须去掉return
eg:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
public class Test {
/**
Lambda表达式 :
数学中的函数 : f(x) = 2x + 1
Java中的函数(Lambda表达式): (x) -> 2x + 1
作用 : 用Lambda函数替代某些匿名类对象
注意 : Lambda表达式只能替代函数式接口的匿名内部类
函数式接口 : 有且仅有一个抽象方法的接口
语法
写法 :
(被重写方法的形参列表) -> {
被重写方法的方法体代码
}
Lambda省略规则:
1. 参数类型全部可以省略不写
2. 如果只有一个参数,参数类型省略的同时 "()"也可以省略, 但多个参数不能省略 "()"
3. 如果Lambda表达式方法体只有一行代码,可以省略大括号, 同时要省略分号 ";" 如果这行代码是return语句,也必须去掉return
**/
public static void main(String[] args) {
getWindow();
test1();
test2();
}
public static void getWindow() {
JFrame win = new JFrame("登录窗口");
JPanel panel = new JPanel();
win.add(panel);
JButton btn = new JButton("登录");
panel.add(btn);
win.setSize(300, 200);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
btn.addActionListener(e -> System.out.println("ok"));
}
public static void test1() {
// Lambda 表达式
Swim s11 = () -> {
System.out.println("函数式接口");
};
}
public static void test2() {
Student1[] stu = new Student1[6];
stu[0] = new Student1("张三", 18, 1);
stu[1] = new Student1("李四", 19, 2);
stu[2] = new Student1("王五", 20, 3);
stu[3] = new Student1("赵六", 21, 4);
stu[4] = new Student1("钱七", 22, 5);
stu[5] = new Student1("孙八", 23, 6);
Arrays.sort(stu, (Student1 o1, Student1 o2) -> o2.getAge() - o1.getAge());
for (Student1 s : stu) {
System.out.println(s);
}
}
public static void test3() {
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student1 {
private String name;
private int age;
private int id;
}
@FunctionalInterface //声明函数式接口的注解
interface Swim{
void swimming();
}
API
基础类型操作
Math 类:
Math.abs(int a): 返回 a 的绝对值。
Math.sqrt(double a): 返回 a 的平方根。
Math.pow(double a, double b): 返回 a 的 b 次方。
Math.round(double a): 四舍五入 a 至最接近的整数。
Integer 类:
Integer.parseInt(String s): 将字符串 s 转换为整数。
Integer.toString(int value): 将整数 value 转换为字符串。
字符串处理
String 类:
String.trim(): 删除字符串两端的空白字符。
String.split(String regex): 按照正则表达式 regex 分割字符串。
String.toLowerCase(): 将字符串转换为小写。
String.toUpperCase(): 将字符串转换为大写。
String.replace(char oldChar, char newChar): 替换字符串中的某个字符。
String.format(String format, Object... args): 格式化字符串。
集合操作
List 接口:
List.add(Object o): 向列表添加元素。
List.remove(Object o): 从列表移除元素。
List.contains(Object o): 判断列表是否包含元素。
ArrayList 类:
ArrayList.addAll(Collection c): 添加集合中的所有元素。
ArrayList.subList(int fromIndex, int toIndex): 返回列表的一个视图。
Set 接口:
Set.add(Object o): 向集合添加元素。
Set.remove(Object o): 从集合移除元素。
Map 接口:
Map.put(Object key, Object value): 存储键值对。
Map.get(Object key): 获取键对应的值。
Map.containsKey(Object key): 判断是否存在键。
文件与I/O
Files 类:
Files.readAllLines(Path path): 读取文件的所有行。
Files.write(Path path, byte[] bytes): 将字节数组写入文件。
Files.deleteIfExists(Path path): 删除文件(如果存在)。
File 类:
File.exists(): 检查文件或目录是否存在。
File.mkdir(): 创建单个目录。
File.listFiles(): 获取目录下的文件列表。
输入输出流
InputStream/OutputStream 类:
InputStream.read(): 读取一个字节。
OutputStream.write(byte b): 写入一个字节。
BufferedReader/BufferedWriter 类:
BufferedReader.readLine(): 读取一行。
BufferedWriter.write(String str): 写入字符串。
日期与时间
LocalDate 类:
LocalDate.now(): 获取当前日期。
LocalDate.of(int year, int month, int dayOfMonth): 创建指定日期。
LocalDateTime 类:
LocalDateTime.now(): 获取当前日期时间。
LocalDateTime.parse(String text): 解析日期时间字符串。
异常处理
throw:
throw new IllegalArgumentException("Invalid argument");: 抛出一个异常。
try-catch-finally:
try { ... } catch (Exception e) { ... } finally { ... }: 处理异常。
并发编程
Thread 类:
Thread.sleep(long millis): 使当前线程休眠。
Thread.start(): 启动线程。
Thread.join(): 等待线程结束。
Runnable 接口:
run(): 线程体。
ExecutorService 接口:
submit(Runnable task): 提交一个可运行的任务。
异常
java的异常体系
分类
运行时异常(继承自RuntimeException)
特点:编译不报错(即写代码不报错),运行出错
eg:
// 运行时异常
public static void method1(){
// 运行异常:编译不报错,但是运行出错, 继承自RuntimeException
// 数组越界
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
// 除数为0
System.out.println(10 / 0);
// 空指针异常
String str1 = null;
System.out.println(str1);
System.out.println(str1.length());
}
编译时异常
特点:编译时异常:编译出错,必须处理,继承自Exception
异常处理
抛出异常(throws)
特点:在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理
语法:
方法 throws 异常1, 异常2, 异常3..{
...
}
eg:
捕获异常(try...catch)
特点:直接捕获程序出现的异常
语法:
try{
// 监视可能出现异常的代码
}catch(异常1 变量){
// 处理异常1
}catch(异常2 变量){
// 处理异常2
}...
e.printStackTrace() 可以打印异常原因
eg:
异常作用
1. 抛运行时异常 : 返回一个异常给上层调用者
throw new RuntimeException("除数不能为0");
2.抛编译时异常 : 返回一个异常给上层调用者
throw new Exception("除数不能为0");
自定义异常
自定义运行时异常
写法:
1. 继承自RuntimeException
2. 构造方法:无参,有参
3. 抛出:throw new 异常类("xxx")创建异常对象并抛出
语法:
自定义编译时异常
写法:
1. 继承自Exception
2. 构造方法:无参,有参
3. 抛出:throw new 异常类("xxx")创建异常对象并抛出
语法:
泛型
认识
定义类、接口、方法时,同时声明了一个或多个类型变量(如:<E>)称为泛型类,泛型接口,泛型方法,统称为泛型
语法
public class ArrayList<E>{
}
自定义泛型类
修饰符 class 类名<类型变量, 类型变量, ...> {
}
// 泛型类
public class Demo2{
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
list.remove("hello");
System.out.println(list.toString());
}
}
class MyArrayList<E>{
private ArrayList list = new ArrayList();
public boolean add(E e) {
return list.add(e);
}
public boolean remove(E e) {
return list.remove(e);
}
@Override
public String toString() {
return list.toString();
}
}
自定义泛型接口
修饰符 interface 接口名<类型变量, 类型变量, ...> {
}
import java.util.ArrayList;
public class Demo3 {
public static void main(String[] args) {
MyInterface<String> myInterface = new MyInterfaceClass<>();
myInterface.add("hello");
myInterface.add("world");
System.out.println(myInterface.remove("hello"));
System.out.println(myInterface.toString());
}
}
interface MyInterface<E>{
void add(E e);
boolean remove(E e);
}
class MyInterfaceClass <E> implements MyInterface<E>{
private ArrayList<E> list = new ArrayList<>();
@Override
public void add(E e) {
list.add(e);
}
@Override
public boolean remove(E e){
return list.remove(e);
}
@Override
public String toString() {
return "MyInterfaceClass{" +
"list=" + list +
'}';
}
}
泛型方法
修饰符 <类型变量, 类型变量, ...> 返回值类型 方法名(参数列表) {
}
通配符
语法
定义 :
就是“?”,可以在“使用泛型”的时候代表一切类型
泛型上下限:
上限:
语法 :
?extend A
解释 : 能接收到的是A或者其子类
下限:
?super A
解释: ?能接收到的是A或者其父类
例子
import java.util.ArrayList;
public class Demo5 {
public static void main(String[] args) {
ArrayList<girl> girls = new ArrayList<>();
girls.add(new girl());
girls.add(new girl());
go(girls);
ArrayList<boy> boys = new ArrayList<>();
boys.add(new boy());
boys.add(new boy());
go(boys);
}
// ? : 通配符
public static void go(ArrayList<? extends Student> cars) {
}
}
class Student{
}
class girl extends Student{
}
class boy extends Student{
}
泛型支持的类型
概念
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)
泛型底层是用Object来接数据
包装类
概念
自动包装 : 基本数据类型可以自动转换为包装类型
自动拆箱 :把包装类型的对象直接给基本数据类型的数据
基本数据类型 | 对应包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
// 手工包装
Integer it1 = Integer.valueOf(21);
Integer it2 = Integer.valueOf(21);
System.out.println(it1 == it2);
// 自动包装 : 基本数据类型可以自动转换为包装类型
Integer it3 = 21;
Integer it4 = 21;
System.out.println(it3 == it4);
// 自动拆箱 :把包装类型的对象直接给基本数据类型的数据
int i = it3;
System.out.println(i);
eg:
- ArrayList<Byte> bytes = new ArrayList<>();
- ArrayList<Short> shorts = new ArrayList<>();
- ArrayList<Integer> integers = new ArrayList<>();
- ArrayList<Long> longs = new ArrayList<>();
- ArrayList<Float> floats = new ArrayList<>();
- ArrayList<Double> doubles = new ArrayList<>();
- ArrayList<Character> characters = new ArrayList<>();
- ArrayList<Boolean> booleans = new ArrayList<>();
功能:
Integer.toString() : 基本类型转字符串
Integer.parseInt() : 字符串转基本数据类型
Integer.valueOf() : 字符串转基本类型
// 1.把基本类型的数据转换成字符串
int j = 921;
String str = Integer.toString(j);
System.out.println(str + 1);
Integer it5 = j;
String str1 = it5.toString();
System.out.println(str1 + 1);
// 2.把字符串数值转换成对应的基本数据类型
String str2 = "12345";
int x = Integer.parseInt(str2);
int y = Integer.valueOf(str2);
System.out.println(x + 1);
System.out.println(y + 1);
Byte 类
public static byte parseByte(String s)
public static Byte valueOf(byte b)
public static Byte valueOf(String s)
Short 类
public static short parseShort(String s)
public static Short valueOf(short s)
public static Short valueOf(String s)
Integer 类
public static int parseInt(String s)
public static Integer valueOf(int i)
public static Integer valueOf(String s)
Long 类
public static long parseLong(String s)
public static Long valueOf(long l)
public static Long valueOf(String s)
Float 类
public static float parseFloat(String s)
public static Float valueOf(float f)
public static Float valueOf(String s)
Double 类
public static double parseDouble(String s)
public static Double valueOf(double d)
public static Double valueOf(String s)
Boolean 类
public static boolean parseBoolean(String s)
public static Boolean valueOf(boolean b)
public static Boolean valueOf(String s)
Character 类
public static char toUpperCase(char ch)
public static char toLowerCase(char ch)
public static Character valueOf(char c)
集合
集合体系
语法
Java语法学习总结-CSDN博客文章浏览阅读83次。Java学习语法总结https://blog.csdn.net/m0_73569492/article/details/133365650?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522666F25AF-998A-4B0E-A806-FBC04DE8562E%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=666F25AF-998A-4B0E-A806-FBC04DE8562E&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-133365650-null-null.nonecase&utm_term=java&spm=1018.2226.3001.4450
迭代
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("world");
c.add("java");
// 迭代 : 遍历容器
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
for (Iterator<String> it1 = c.iterator(); it1.hasNext();) {
String s = it1.next();
System.out.println(s);
}
for (String s : c) {
System.out.println(s);
}
Collection
map
Stream流
概念
认识Stream流
在Java中,Stream 是一个来自Java 8的新特性,它提供了一种高效且易于使用的处理数据的方式,尤其是集合数据。Stream API支持并行操作,可以极大地提高处理大量数据时的性能。下面是一些关于Java Stream的基本概念和用法:
源(Source): 数据源可以是集合(Collection)、数组(Array)或是任何提供数据的接口。
管道(Pipeline): Stream API的操作都是通过一系列称为“管道”的方法调用来完成的。这些操作可以分为中间操作(Intermediate operations)和终端操作(Terminal operations)。
中间操作:如filter, map, flatMap等,它们返回一个新的流,并且可以链接起来形成流水线。
终端操作:如forEach, collect, reduce, toArray等,执行这个操作后会从流产生结果,之后流不能再被使用。
内部迭代:与传统的外部迭代(例如使用for-each循环)不同,Stream API采用内部迭代的方式来遍历元素。
优势
Stream流结合Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好
1.简化代码:使用Stream流,可以通过更简洁的代码实现复杂的数据处理逻辑,减少了传统循环和条件语句的使用,使得代码更加清晰易懂。
2.提高可读性:Stream API的设计采用了函数式编程的思想,使得代码的意图更加明确。通过组合多个操作(如filter、map、reduce等),可以创建出易于理解的表达式。
3.提高性能:Stream流内部使用了优化的迭代器,并且支持延迟执行(Lazy Evaluation)。这意味着只有当执行终端操作时,中间操作才会被执行,从而可能减少不必要的计算。
4.内置并行处理:Stream API支持并行流(Parallel Streams),可以在多核处理器上并行处理数据,无需显式管理多线程,即可提高处理大数据集时的性能。
5.不变性:Stream操作通常不会修改源数据结构,而是生成新的结果。这种不可变性有助于避免副作用,并且在多线程环境中更容易实现安全的数据处理。
6.统一的API:Stream API为不同的数据源提供了一致的处理方式,无论是集合、数组还是其他数据源,都可以使用相同的流操作进行处理。
7.延迟加载:对于某些数据源(如无限流或远程数据流),Stream API允许按需处理数据,即只在需要时才加载和处理数据的一部分,这有助于节省内存和提高效率。
初识Stream流
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("马亮");
list.add("周正");
list.add("成朗");
list.add("周末");
list.add("安安");
list.add("小波");
list.add("小明");
// 需求 :将集合中周开头且名字为两个字的名字放在一个集合里面去
// 传统写法
List<String> list2 = new ArrayList<>();
for (String name : list) {
if(name.startsWith("周") && name.length() == 2){
list2.add(name);
}
}
System.out.println(list2);
// Stream流写法
List newList = list.stream().filter(name -> name.startsWith("周")).
filter(name -> name.length() == 2).collect(Collectors.toList());
System.out.println(newList);
// Stream流写法
}
Stream流的使用步骤
1. 创建Stream:
从集合(如List, Set等)、数组或其他数据源创建一个Stream。这通常是通过调用集合的stream()或者parallelStream()方法来实现的,也可以使用Arrays.stream(array)来从数组创建流。
2.中间操作:中间操作是对流进行的一系列操作,它们可以被链接在一起。常见的中间操作包括:
- filter(Predicate<T> predicate): 过滤流中的元素。
- map(Function<T, R> mapper): 将流中的元素映射为另一个流。
- flatMap(Function<T, Stream<R>> mapper): 将流中的每个值都转换为一个流,然后将所有的流连接成一个流。
- sorted(), sorted(Comparator<T> comparator): 对流中的元素进行排序。
- distinct(): 去除重复的元素。
中间操作返回一个新的流,因此可以链式调用。
3. 终端操作:终端操作会消费流并产生结果。常见的终端操作包括:
- forEach(Consumer<T> action): 对流中的每个元素执行一个动作。
- collect(Collector<T,A,R> collector): 将流转换为另一种形式,如转换为集合。
- reduce(BinaryOperator<T>), reduce(T identity, BinaryOperator<T>): 对流中的元素进行归约。
- anyMatch(Predicate<T>), allMatch(Predicate<T>), noneMatch(Predicate<T>): 检查是否至少/全部/没有一个元素满足条件。
- findFirst(), findAny(): 查找第一个/任意一个元素。
- count(): 计算流中的元素数量。
- max(Comparator<T>), min(Comparator<T>): 找到最大/最小元素。
终端操作执行后,流会被消费掉,不能再次使用。
4.可选的管道操作:在中间操作和终端操作之间,还可以插入一些其他操作来改变流的行为,例如peek(Consumer<T> action)用于调试,它会在每个元素传递给下一个操作之前执行一个动作。
Stream流具体操作
1. 获取数据流
- 获取集合的数据流(Collection): default Stream<E> stream();
- 获取Map集合的Stream流
1. 获取键流 : map.keySet().stream()
2.获取值流 : map.values().stream()
3.获取键值对流 : map.entrySet().stream()
// Map集合,获取Stream流 Map<String, Integer> map = new HashMap<>(); // 1. 获取键流 Stream<String> keyStream = map.keySet().stream(); // 2. 获取值流 Stream<Integer> valueStream = map.values().stream(); // 3. 获取键值对流 Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
- 获取数组的数据流:
Arrays类 ( static <T> Stream<T> stream(T[] array)):
Arrays.stream()
Stream类(default Stream<T> of(T... values);):
Stream.of()
// Arrays类 : static <T> Stream<T> stream(T[] array); String[] names = {"张三", "李四", "王五"}; Stream<String> stream = Arrays.stream(names); System.out.println(stream.count()); // Stream类 : default Stream<T> stream(T... values); Stream<String> steam2 = Stream.of(names); System.out.println(steam2.count());
2.中间方法(调用各种方法对数据流进行处理)
- filter(Predicate<? super T> predicate) : 筛选,接收Lambda,从流中排除某些元素
- map(Function<? super T, ? extends R> mapper) : 映射,接收Lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- distinct() : 去重,流中所有元素都是唯一的
- limit(long maxSize) : 截断流,使其元素不超过给定数量
- skip(long n) : 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
- sorted() : 排序,自然排序(Comparable)或定制排序(Comparator)
- skip(long n) : 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
- concat(Stream<? extends T> a, Stream<? extends T> b) : 合并两个流
List<Double> scores = new ArrayList<>();
scores.add(15.2);
scores.add(19.21);
scores.add(9.21);
scores.add(12.21);
scores.add(9.21);
// 降序
scores.stream().sorted((s1, s2)->Double.compare(s2, s1)).forEach(System.out::println);
System.out.println("-------------------");
// 升序
scores.stream().sorted((s1, s2)->Double.compare(s1, s2)).forEach(System.out::println);
System.out.println("-------------------");
// 只要前两名
scores.stream().sorted((s1, s2)->Double.compare(s2, s1)).limit(2).forEach(System.out::println);
System.out.println("-------------------");
// 跳过前两名
scores.stream().sorted((s1, s2)->Double.compare(s2, s1)).skip(2).forEach(System.out::println);
System.out.println("-------------------");
// 去重 : 如果是自定义对象,就需要重写其hashCode和equals方法
scores.stream().distinct().forEach(System.out::println);
System.out.println("-------------------");
// 映射/加工 : 把流上的数据拿出来变成新数据又放到流上去
scores.stream().map(score->score*2).forEach(System.out::println);
System.out.println("-------------------");
// 合并 : a、b两个流合并
Stream<String> a = Stream.of("a","b","c");
Stream<String> b = Stream.of("d","e","f");
Stream.concat(a,b).forEach(System.out::println);
3.终结方法(获取处理的结果)
1. forEach:
void forEach(Consumer action) 用于遍历流中的每个元素,并对每个元素应用一个动作。
scores.stream().forEach(System.out::println);
2. count:
long count() 用于计算流中的元素数量
long count = scores.stream().count();
System.out.println("Count: " + count);
3. reduce
Optional<T> reduce(BinaryOperator) 用于将流中的元素反复结合起来,得到一个单一的结果。
Optional<Double> maxScore = scores.stream().reduce(Double::max);
maxScore.ifPresent(System.out::println);
4. anyMatch, allMatch, noneMatch
这些方法分别用于检查是否至少有一个元素、所有元素、没有元素满足给定的条件
boolean hasScoresAboveTen = scores.stream().anyMatch(score -> score > 10);
System.out.println("Has scores above ten: " + hasScoresAboveTen);
5. findFirst, findAny
Optional<T> findFirst() 和 Optional<T> findAny() 分别用于获取流中的第一个元素和任意一个元素。
Optional<Double> firstScore = scores.stream().findFirst();
firstScore.ifPresent(System.out::println);
6.max
这里 T 是流中的元素类型,comparator 是一个比较器,用于确定元素之间的大小关系。如果你的元素实现了 Comparable 接口,你可以直接传递 (Comparator.naturalOrder())。
Optional<T> max(Comparator<? super T> comparator);
7.min
与 max 方法类似,Stream API 中的 min 方法用于查找流中的最小元素。min 方法同样返回一个 Optional 对象,这样可以在流为空的情况下避免 NullPointerException。
Optional<T> min(Comparator<? super T> comparator);
4.收集Stream流
把Stream流操作后的结果转回到集合或者数组中去返回
Stream流:方便操作集合/数组的手段
集合/数组: 开发的目的
1. collect:
R collect(Collector collector) 用于将流中的元素收集到一个容器中,如 List 或 Set。
- collect(Collectors.toMap(Key, Value))
- collect(Collectors.toList())
- collect(Collectors.toSet())
List<Double> sortedScores = scores.stream()
.sorted()
.collect(Collectors.toList());
List<Person> persons = new ArrayList<>();
persons.add(new Person("张三", 18, "北京"));
persons.add(new Person("李四", 19, "上海"));
persons.add(new Person("王五", 20, "广州"));
persons.add(new Person("赵六", 21, "深圳"));
Map<String, Integer> newmap1 = persons.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
System.out.println(newmap1);
2. toArray
T[] toArray(IntFunction arrayCreator) 用于将流中的元素收集到数组中。
Double[] scoreArray = scores.stream().toArray(Double[]::new);
补充知识:
可变参数
可变参数:
数据类型...参数名称
可变参数可以接收0个至多个参数以及数组
注意:
- 可变参数在方法内部就是一个数组
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
public class Demo1 {
public static void main(String[] args) {
sum(1, 2, 3);
sum();
int[] num = {1, 2, 3};
sum(num);
}
public static void sum(int...num) {
int sum = 0;
for (int i : num) {
sum += i;
}
System.out.println(sum);
System.out.println(num.length);
System.out.println(Arrays.toString(num));
}
}
Collections工具类
用来操作集合的工具类
常用方法
addAll:给集合批量添加元素
shuffle:打乱List集合中的元素顺序
sort:对List集合中的元素进行排序
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class Demo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list, "hello", "world", "java", "hello", "hello");
Collections.shuffle(list);
System.out.println(list);
}
}
方法
1.排序
sort(List<T> list): 对列表进行自然排序。
reverse(List<T> list): 反转列表元素的顺序。
shuffle(List<T> list): 随机打乱列表元素的顺序。
2.查找
binarySearch(List<T> list, T key): 使用二分查找法查找指定元素的位置。
max(Collection<? extends T> coll): 返回集合中的最大元素。
min(Collection<? extends T> coll): 返回集合中的最小元素。
indexOfSubList(List<T> list, List<T> subList): 返回子列表在列表中的索引位置。
3. 比较
disjoint(Collection<?> c1, Collection<?> c2): 判断两个集合是否没有交集。
equalsIgnoreOrder(List<T> list1, List<T> list2): 比较两个列表的内容是否相同(忽略顺序)
4.操作
rotate(List<T> list, int distance): 旋转列表元素。
swap(List<T> list, int i, int j): 交换列表中两个位置的元素。
copy(List<T> dest, List<? extends T> src): 复制列表内容到另一个列表。
unmodifiableCollection(Collection<T> c): 创建不可修改的集合。
unmodifiableList(List<T> list): 创建不可修改的列表。
unmodifiableMap(Map<K,V> m): 创建不可修改的映射。
unmodifiableSet(Set<T> s): 创建不可修改的集合。
frequency(Collection<T> collection, Object element): 计算集合中某个元素出现的次数。
File(文件操作)
概念
File
File是java.io.包下的类,File类的对象,用于代表当前操作系统的文件(文件,文件夹)
File类 : 只能对文件本身进行操作,不能读写文件里面存储的数据
IO流
用来读写数据(可以读写文件或网络中的数据)
File类语法
创建File类的对象
构造器 | 说明 |
---|---|
public File(String pathname) | 路径可以是相对路径,也可以是绝对路径 |
public File(String parent, String child) | 父目录,子目录 |
public File(File parent, String child) | 父目录,子目录 |
定义:
File类的对象可以代表文件/文件夹,并可以调用其提供的方法对象文件进行操作
绝对路径:
绝对路径是从文件系统的根目录开始,到目标文件或目录的完整路径(带盘符)
相对路径:
相对路径是指相对于当前工作目录的位置,它不从根目录开始,而是从某个已知的目录开始(在idea中,默认是到你idea工程下直接寻找文件的)
特殊符号:
- ./ 表示当前目录。
- ../ 表示父目录。
- 在Linux/Unix中,/ 表示根目录。
创建文件
方法名称 | 说明 |
---|---|
createNewFile() | 创建一个新的空的文件 |
mkdir() | 只能创建一级文件夹 |
mkdirs() | 可以创建多级文件夹 |
删除文件或文件夹
方法名称 | 说明 |
---|---|
delete() | 删除文件或空文件夹 |
注意:
delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站
遍历文件目录并获取文件
方法名称 | 说明 |
---|---|
list() | 获取某个目录下的全部一级文件名称 |
listFiles() | 获取某个目录下的全部一级文件对象 |
listFiles方法注意事项:
1.空目录处理:
如果调用 listFiles() 的 File 对象表示的是一个空目录,则会返回一个长度为零的数组(即 File[0])2.文件过滤:
可以通过传递一个 FilenameFilter 或者 FileFilter 实例给 listFiles() 来过滤文件列表。例如,只列出特定类型的文件或满足某些条件的文件。
使用 FilenameFilter 或 FileFilter 时,确保实现了正确的接口,并且过滤逻辑正确无误。3.文件不存在或不是目录:
如果 File 对象表示的不是一个目录或者该文件不存在,listFiles() 方法可能会返回 null。因此,在使用返回值之前应该检查是否为 null。
也可以通过 isDirectory() 方法来确认 File 对象确实代表一个目录。4.权限问题:
当没有足够的权限访问目录或其内容时,listFiles() 可能会返回 null 或抛出 SecurityException。5.异常处理:
在某些情况下,如文件系统错误或安全限制,listFiles() 可能会抛出 NullPointerException 或其他异常。应适当处理这些异常情况。6.递归遍历:
如果需要递归地遍历目录结构,可以考虑使用 FileVisitor 接口与 Files.walkFileTree 方法,或者使用 Apache Commons IO 库中的 FileUtils.listFiles 方法。
案例:查找文件并启动文件
public class Findfile {
public static void main(String[] args) {
try {
File dir = new File("D:/");
findfile(dir, "HBuilderX.exe");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
dir: 搜索的目录
name: 要搜索的文件名
*/
public static void findfile(File dir, String name) throws IOException {
if (dir == null || !dir.exists() || dir.isFile()) {
System.out.println("目录不存在");
return;
}
// 获取目录下的所有一级文件或文件夹
File[] files = dir.listFiles();
if (files != null && files.length > 0) {
//System.out.println("当前目录:" + dir.getAbsolutePath());
for (File file : files) {
if (file != null && file.length() > 0) {
// 如果是文件
if (file.isFile()) {
if (file.getName().contains(name)) {
System.out.println("找到目标文件在:" + file.getAbsolutePath());
// 启动程序
Runtime r = Runtime.getRuntime();
r.exec(file.getAbsolutePath());
}
} else {
findfile(file, name);
}
}
}
}
return;
}
}
字符集
介绍
1.ASCII (American Standard Code for Information Interchange):
ASCII 是最早的字符编码标准之一,它使用7位来表示128个字符,包括英文字母、阿拉伯数字、标点符号及一些控制字符。2.GBK (GB2312-80):
GBK 是GB2312的一个扩展版本,它兼容GB2312,并且增加了对繁体中文的支持,以及更多的符号和字符。3.Unicode (统一码、万国码):
Unicode 是一个旨在覆盖世界上所有字符的字符编码标准,它定义了超过10万个字符的位置,包括几乎所有国家的语言文字。Unicode 最常用的编码形式包括 UTF-8、UTF-16 和 UTF-32。4.UTF-8 (Unicode Transformation Format):
UTF-8 是Unicode的一种变长字符编码,它兼容ASCII,并且可以根据字符的不同,使用1到4个字节来表示一个字符,对于英文等ASCII字符只需要一个字节,而对于其他语言则可能需要更多字节
编码操作
操作 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符编码将此 String 编码为 byte 序列, |
byte[] getBytes(String charsetName) | 使用指定的字符编码将此 String |
解码操作
操作 | 说明 |
---|---|
String(byte[] bytes) | 根据默认平台字符集将指定的 byte 数组解码为 String。 |
String(byte[] bytes, String charsetName) | 根据指定的字符集将指定的 byte 数组解码为 String。 |
IO流
体系
FileInputStream(文件字节输入流) :
作用:
以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去(读字节数据到内存)
构造器
构造器 | 说明 |
---|---|
public FileInputStream(String path) | 创建字节输入流管道与源文件接通 |
public FileInputStream(File file) | 创建字节输入流管道与源文件接通 |
方法
常用方法 | 说明 |
---|---|
public int read() | 从输入流中读取一个字节数据,返回读取的字节,如果发现没有数据可读会返回-1 |
public int read(byte[] b) | 从输入流中读入字节数组, |
package com.ithema.io;
import java.io.FileInputStream;
import java.io.IOException;
public class InputStreamDemo {
/**
FileInputStream(文件字节输入流) :
作用: 以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去(读字节数据到内存)
构造器:
public FileInputStream(String path) : 创建字节输入流管道与源文件接通
public FileInputStream(File file) : 创建字节输入流管道与源文件接通
常用方法:
public int read() : 从输入流中读取一个字节数据,返回读取的字节,如果发现没有数据可读会返回-1
public int read(byte[] b) : 从输入流中读入字节数组,每次最多读b.length个字节,返回实际读取的字节数,如果发现没有数据可读会返回-1
*/
public static void main(String[] args) throws IOException {
// 1. 创建字节输入流管道与源文件接通
FileInputStream fis = new FileInputStream("IO\\src\\test.txt");
// 2.开始读取文件中的字节并输出
// (1).每次读取一个字节,性能较差,且读入汉字输出会乱码
while (true) {
int b = fis.read(); // 读入一个字节,返回读取的字节,如果发现没有数据可读会返回-1
if (b == -1) {
break;
}
System.out.print((char) b);
}
System.out.println();
System.out.println("=======================");
// (2).读取字节数组,每次最多读b.length个字节,返回实际读取的字节数,如果发现没有数据可读会返回-1
// 依然无法避免读取汉字输出乱码的问题:存在截断汉字字节的可能性
FileInputStream fis2 = new FileInputStream("IO\\src\\test01.txt");
byte[] b = new byte[3];
// 定义一个变量记住每次读取多少字节
int len;
while ((len = fis2.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.print(str);
}
System.out.println();
System.out.println("=======================");
/**
读取文本适合字符流,字节流适合做数据的转移
*/
// 使用字节流读取文件时,如何保证不乱码 :
// 1.定义一个与文件一样大的字节数组,一次性读取完文件的全部字节
FileInputStream fis3 = new FileInputStream("IO\\src\\test02.txt");
byte[] b2 = new byte[1024];
int len2;
while ((len2 = fis3.read(b2)) != -1) {
String str2 = new String(b2, 0, len2);
System.out.print(str2);
}
System.out.println();
System.out.println("=======================");
// 2.一次性读完文件的全部字节
FileInputStream fis4 = new FileInputStream("IO\\src\\test03.txt");
byte[] b3 = fis4.readAllBytes();
String str3 = new String(b3);
System.out.println(str3);
}
}
FileOutputStream(文件字节输出流)
作用 :
以内存为基准,把内存中的数据以字节的形式写出到文件中去
构造器
构造器 | 说明 |
---|---|
public FileOutputStream(File file) | 创建输出流管道与源文件对象接通 |
public FileOutputStream(String filepath) | 创建输出流管道与源文件路径接通 |
public FileOutputStream(File file, boolean append) | 创建输出流管道与源文件对象接通, 并且可以指定是否追加数据 |
public FileOutputStream(String filepath, boolean append) | 创建输出流管道与源文件路径接通, 并且可以指定是否追加数据 |
方法
常用方法 | 说明 |
---|---|
public void write(int b) | 写出单个字节数据 |
public void write(byte[] b) | 写出字节数组数据 |
public void write(byte[] b, int off, int len) | 写出字节数组数据, 并且可以指定写出的开始位置和写出的长度 |
public void close() | 关闭输出流管道 |
package com.ithema.io;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamDemo {
/**
FileOutputStream(文件字节输出流) :
作用 : 以内存为基准,把内存中的数据以字节的形式写出到文件中去
1.构造器:
public FileOutputStream(File file) : 创建输出流管道与源文件对象接通
public FileOutputStream(String filepath) : 创建输出流管道与源文件路径接通
public FileOutputStream(File file, boolean append) : 创建输出流管道与源文件对象接通,并且可以指定是否追加数据
public FileOutputStream(String filepath, boolean append) : 创建输出流管道与源文件路径接通,并且可以指定是否追加数据
2.方法:
public void write(int b): 写出单个字节数据
public void write(byte[] b): 写出字节数组数据
public void write(byte[] b, int off, int len): 写出字节数组数据,并且可以指定写出的开始位置和写出的长度
public void close(): 关闭输出流管道
*/
public static void main(String[] args) throws IOException {
// 写单个字节数据
FileOutputStream fos = new FileOutputStream("IO\\src\\test05.txt");
int b1 = 9;
fos.write(b1);
fos.close(); // 关闭输出流管道
// 写字节数组数据
FileOutputStream fos2 = new FileOutputStream("IO\\src\\test06.txt", true);
String str2 = "12345wjk520";
byte[] b2 = str2.getBytes();
fos2.write(b2);
fos2.close(); // 关闭输出流管道
// 写字节数组数据,并且可以指定写出的开始位置和写出的长度
FileOutputStream fos3 = new FileOutputStream("IO\\src\\test07.txt", true);
String str3 = "12345wjk520";
byte[] b3 = str3.getBytes();
fos3.write(b3, 3, 5);
fos3.close(); // 关闭输出流管道
// 如何实现写出去的数据能换行
FileOutputStream fos4 = new FileOutputStream("IO\\src\\test08.txt", true);
fos4.write("5555\r5622\n4554".getBytes());
fos4.close();
System.out.println("写出完毕!");
}
}
FileReader(文件字符输入流)
构造器
构造器 | 说明 |
---|---|
public FileReader(File file) | 创建字符输入流管道与源文件接通 |
public FileReader(String filePath) | 创建字符输入流管道与源文件接通 |
方法
常用方法 | 说明 |
---|---|
public int read() | 从输入流中读取一个字符,返回读取的字符, 如果发现没有数据可读会返回-1 |
public int read(char[] buffer) | 从输入流中读取字符数组,返回读取的字符个数, 如果发现没有数据可读会返回-1 |
package com.ithema.io;
import java.io.FileReader;
import java.io.IOException;
public class ReaderDemo {
/**
FileReader(文件字符输入流) :
作用:
1.构造器 :
public FileReader(File file): 创建字符输入流管道与源文件接通
public FileReader(String filePath): 创建字符输入流管道与源文件接通
2.方法:
public int read(): 从输入流中读取一个字符,返回读取的字符,如果发现没有数据可读会返回-1
public int read(char[] buffer): 从输入流中读取字符数组,返回读取的字符个数,如果发现没有数据可读会返回-1
*/
public static void main(String[] args) throws IOException {
// 1.创建一个文件字符输入流管道与源文件接通
try (FileReader fr1 = new FileReader("IO\\src\\test04.txt")) {
// 读一个字符
while (true) {
int b = fr1.read();
if (b == -1) {
break;
}
System.out.print((char) b);
}
}catch (IOException e) {
e.printStackTrace();
}
System.out.println();
System.out.println("=======================");
// 读多个字符
try (FileReader fr2 = new FileReader("IO\\src\\test10.txt")) {
char[] b = new char[3];
while (true) {
int len = fr2.read(b);
if (len == -1) {
break;
}
System.out.print(new String(b, 0, len));
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
FileWriter(文件字符输出流)
作用 :
以内存为基准,把内存中的数据以字符的形式写到文件中去
构造器
构造器 | 说明 |
---|---|
public FileWriter(File file) | 创建一个字符输出流管道与原文件对象接通 |
public FileWriter(String filepath) | 创建一个字符输出流管道与文件路径接通 |
public FileWriter(File file, boolean append) | 创建一个字符输出流与源文件对象接通, 可以追加数据 |
public FileWriter(String filepath, boolean append) | 创建一个字符输出流与文件路径接通, 可以追加数据 |
方法
常用方法 | 说明 |
---|---|
void wirte(int c) | 写一个字符出去 |
void write(String str) | 写一个字符串出去 |
void write(String str, int off, int len) | 写一个字符串的一部分出去, off为起点下标,len为长度 |
void write(char[] buffer) | 写一个字符数组出去 |
void write(char[] buffer, int off, int len) | 写一个字符数组的一部分出去, off为起点下标,len为长度 |
package com.ithema.io;
import java.io.FileWriter;
import java.io.IOException;
public class WriterDemo {
/**
FileWriter(文件字符输出流) :
作用 : 以内存为基准,把内存中的数据以字符的形式写到文件中去
1. 构造器:
public FileWriter(File file) : 创建一个字符输出流管道与原文件对象接通
public FileWriter(String filepath) : 创建一个字符输出流管道与文件路径接通
public FileWriter(File file, boolean append) : 创建一个字符输出流与源文件对象接通,可以追加数据
public FileWriter(String filepath, boolean append) : 创建一个字符输出流与文件路径接通,可以追加数据
2. 方法:
void wirte(int c) : 写一个字符出去
void write(String str) : 写一个字符串出去
void write(String str, int off, int len) : 写一个字符串的一部分出去, off为起点下标,len为长度
void write(char[] buffer) : 写一个字符数组出去
void write(char[] buffer, int off, int len) : 写一个字符数组的一部分出去, off为起点下标,len为长度
*/
public static void main(String[] args) throws IOException {
// 1. 创建一个文件字符输出流管道与源文件接通
// 写单个字符进去
try (FileWriter fw1 = new FileWriter("IO\\src\\test11.txt")) {
int c = 1;
fw1.write(c);
}catch (IOException e) {
e.printStackTrace();
}
// 写字符串进去
try (FileWriter fw2 = new FileWriter("IO\\src\\test12.txt")) {
String str = "hello world";
fw2.write(str);
}catch (IOException e) {
e.printStackTrace();
}
// 写字符串的一部分进去
try (FileWriter fw3 = new FileWriter("IO\\src\\test13.txt")) {
String str = "hello world";
fw3.write(str, 0, 5);
}catch (IOException e) {
e.printStackTrace();
}
// 写字符数组进去
try (FileWriter fw4 = new FileWriter("IO\\src\\test14.txt")) {
char[] buffer = {'a', 'b', 'c', 'd', 'e'};
fw4.write(buffer);
}catch (IOException e) {
e.printStackTrace();
}
// 写字符数组的一部分进去
try (FileWriter fw5 = new FileWriter("IO\\src\\test15.txt")) {
char[] buffer = {'a', 'b', 'c', 'd', 'e'};
fw5.write(buffer, 1, 3);
fw5.flush(); // 刷新缓冲区,将缓冲区中的数据全部写出去
}catch (IOException e) {
e.printStackTrace();
}
}
}
缓冲字节流
BufferedInputStream(缓冲字节输入流) / BufferedOutputStream(缓冲字节输出流):
作用: 提高字节输入输出的效率
原理:缓冲字节输入流自带了缓冲池,缓冲输出池也自带缓冲池。
构造器
构造器 | 说明 |
---|---|
BufferedInputStream(InputStream is) | 把低级的字节输入流包装成高级的缓冲字节输入流, 提高读数据效率 |
BufferedOutputStream(OutputStream os) | 把低级的字节输出流包装成高级的缓冲字节输出流, 提高写数据效率 |
package com.ithema.io;
import java.io.*;
public class BufferIsOs {
/**
缓冲字节流:
BufferedInputStream(缓冲字节输入流) / BufferedOutputStream(缓冲字节输出流):
作用: 提高字节输入输出的效率
原理:缓冲字节输入流自带了缓冲池,缓冲输出池也自带缓冲池。
1. 构造器
BufferedInputStream(InputStream is) :把低级的字节输入流包装成高级的缓冲字节输入流,提高读数据效率
BufferedOutputStream(OutputStream os) :把低级的字节输出流包装成高级的缓冲字节输出流,提高写数据效率
*/
public static void main(String[] args) throws IOException {
try(
FileInputStream fis = new FileInputStream("IO\\src\\test.txt");
FileOutputStream fos = new FileOutputStream("IO\\src\\test16.txt");
// 把低级的字节输入流包装成高级的缓冲字节输入流
BufferedInputStream bis = new BufferedInputStream(fis);
// 把低级的字节输出流包装成高级的缓冲字节输出流
BufferedOutputStream bos = new BufferedOutputStream(fos);){
// 读数据
byte[] b = new byte[1024];
int len;
while((len = bis.read(b)) != -1){
// 输出数据
System.out.println(new String(b, 0, len));
}
// 写数据
bos.write("hello world".getBytes());
}catch (Exception e) {
e.printStackTrace();
}
}
}
缓冲字符流
BufferedReader(缓冲字符输入流)/BufferedWriter(缓冲字符输出流)
作用:自带字符缓冲池
构造器
构造器 | 说明 |
---|---|
public BufferedReader(Reader r) | 把低级的字符输入流包装成高级的缓冲字符输入流 |
public BufferedWriter(Writer w) | 把低级的字符输出流包装成高级的缓冲字符输出流 |
缓冲输入流新增
方法 | 说明 |
---|---|
public String readLine() | 读取一行数据,并把读取的数据返回, |
缓冲输出流新增
方法 | 说明 |
---|---|
public void newLine() | 换行,输出平台相关换行符 |
package com.ithema.io;
import java.io.*;
public class BufferRW {
/**
缓冲字符流:
BufferedReader(缓冲字符输入流)/BufferedWriter(缓冲字符输出流)
作用:自带字符缓冲池
public BufferedReader(Reader r):把低级的字符输入流包装成高级的缓冲字符输入流
public BufferedWriter(Writer w):把低级的字符输出流包装成高级的缓冲字符输出流
缓冲输入流新增:
public String readLine() throws IOException:读取一行数据,并把读取的数据返回,如果没有数据可读,会返回null
缓冲输出流新增:
public void newLine():换行,输出平台相关换行符
*/
public static void main(String[] args) throws FileNotFoundException {
// 缓冲输入流
try (FileReader fr1 = new FileReader("IO\\src\\test04.txt");
BufferedReader br1 = new BufferedReader(fr1);
) {
char[] c = new char[1024];
int len;
while ((len = br1.read(c)) != -1) {
System.out.println(new String(c, 0, len));
}
}catch (IOException e) {
e.printStackTrace();
}
System.out.println();
System.out.println("============================");
// readLine()
try (FileReader fr2 = new FileReader("IO\\src\\test04.txt");
BufferedReader br2 = new BufferedReader(fr2);
) {
String line;
while ((line = br2.readLine()) != null) {
System.out.println(line);
}
}catch (IOException e) {
e.printStackTrace();
}
// 缓冲输出流
try (FileWriter fw1 = new FileWriter("IO\\src\\test17.txt");
BufferedWriter bw1 = new BufferedWriter(fw1);
) {
char[] c1 = "hello world".toCharArray();
bw1.write(c1);
char[] c2 = "你好,中国".toCharArray();
bw1.newLine();
bw1.write(c2);
}catch (IOException e) {
e.printStackTrace();
}
}
}
其他流
InputStreamReader(字符输入转换流)
作用:解决不同编码时, 字符流读取文本内容乱码问题,将字节输入流转换为字符输入流
解决思路:
先获取原始的字节输入流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了
构造器
构造器 | 说明 |
---|---|
public InputStreamReader(InputStream is) | 把原始的字节输入流,按照代码默认编码转成字符输入流 |
public InputStreamReader(InputStream is, String charsetName) | 把原始的字节输入流,按照指定的字符集编码转成字符输入流 |
PrintStream/PrintWriter(打印流) :
作用:
打印流可以实现更方便、更高效的打印数据出去,能打印啥出去就是啥出去
构造器(PrintSteam)
构造器 | 说明 |
---|---|
public void PrintStream(OutputStream/File/String) | 打印流直接通向输出流/文件/文件路径 |
public void PrintStream(String filepath, Charset charset) | 打印流直接通向文件,指定文件编码 |
public void PrintStream(OutputStream out, boolean autoFlush) | 打印流直接通向输出流,并且自动刷新 |
public void PrintStream(OutputStream out, boolean autoFlush, String encoding) | 打印流直接通向输出流,并且自动刷新,并且指定文件编码 |
方法
常用方法 | 说明 |
---|---|
public void println() | 打印并换行 |
public void write(int/byte/byte[]) | 支持字节数据写入 |
package com.ithema.io;
import java.io.*;
public class InputStreamReaderDemo {
/**
其他流:
InputStreamReader(字符输入转换流):
作用:解决不同编码时, 字符流读取文本内容乱码问题,将字节输入流转换为字符输入流
解决思路:
先获取原始的字节输入流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了
构造器:
public InputStreamReader(InputStream is) : 把原始的字节输入流,按照代码默认编码转成字符输入流
public InputStreamReader(InputStream is, String charsetName) : 把原始的字节输入流,按照指定的字符集编码转成字符输入流
PrintStream/PrintWriter(打印流) :
作用: 打印流可以实现更方便、更高效的打印数据出去,能打印啥出去就是啥出去
PrintStream:
1. 构造器(PrintStream的打印数据方案) :
public void PrintStream(OutputStream/File/String) : 打印流直接通向输出流/文件/文件路径
public void PrintStream(String filepath, Charset charset) : 打印流直接通向文件,指定文件编码
public void PrintStream(OutputStream out, boolean autoFlush) : 打印流直接通向输出流,并且自动刷新
public void PrintStream(OutputStream out, boolean autoFlush, String encoding) : 打印流直接通向输出流,并且自动刷新,并且指定文件编码
2. 方法:
public void println() : 打印并换行
public void write(int/byte/byte[]) : 支持字节数据写入
PrintWriter:
1.构造器(PrintWriter的打印数据方案) :
*/
public static void main(String[] args) {
/**输出乱码问题*/
//f1();
/**InputStreamReader解决输出乱码*/
//f2();
/**PrintStream打印流*/
f3();
}
// 文件和代码编码不一致会导致输出乱码
public static void f1() {
try(
// 读入的字节流编码是默认的UTF-8 而word2文件是GBK,所以会输出乱码
// GBK中中文编码为一个汉字占两个字节
// UTF-8中中文编码为一个汉字占三个字节
FileInputStream fr = new FileInputStream("IO\\src\\word2.txt");
BufferedInputStream br = new BufferedInputStream(fr);
) {
byte[] b = new byte[1024];
int len;
while ((len = br.read(b)) != -1) {
System.out.println(new String(b, 0, len));
}
}catch (Exception e) {
e.printStackTrace();
}
}
// 使用InputStreamReader解决乱码问题
public static void f2() {
try(
// 读入的字节流编码是默认的UTF-8 而word2文件是GBK,所以会输出乱码
// GBK中中文编码为一个汉字占两个字节
// UTF-8中中文编码为一个汉字占三个字节
InputStream is = new FileInputStream("IO\\src\\word2.txt");
InputStreamReader isr = new InputStreamReader(is, "GBK");
BufferedReader br = new BufferedReader(isr);
) {
String str;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
}catch (Exception e) {
e.printStackTrace();
}
}
// PrintStream/PrintWriter(打印流) :
public static void f3() {
try(
PrintStream ps = new PrintStream("IO\\src\\test.txt");
PrintWriter pw = new PrintWriter("IO\\src\\test18.txt", "UTF-8");
) {
ps.println("hello world");
ps.write(97);
ps.println(123456);
pw.println("hello world");
pw.write(97);
pw.println(123456);
}catch (Exception e) {
e.printStackTrace();
}
}
}
原始流和缓冲流性能对比
package com.ithema.io;
import java.io.*;
public class TimeTest {
/**
原始流和缓冲流的性能分析:
*/
public static void main(String[] args) {
String srcPath = "D:\\Users\\File_IO\\image.jpg";
String destPath = "D:\\Users\\File_IO\\copy";
try {
copyFileByByte(srcPath, destPath); // 使用低级字节流按照字节复制文件
copyFileByBytes(srcPath, destPath); // 使用低级字节流按字节数组复制文件
copyFileByBufferedChar(srcPath, destPath); // 使用缓冲流按字节复制文件
copyFileByBufferedChars(srcPath, destPath); // 使用缓冲流按字节数组复制文件
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用低级字节流按照字节复制文件,统计时间差
public static void copyFileByByte(String srcPath, String destPath) throws IOException {
long start = System.currentTimeMillis();
try(FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath + "1.jpg");
) {
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
}catch (FileNotFoundException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("使用低级字节流按照字节复制文件耗时:" + (end - start) + "ms");
}
// 使用低级字节流按字节数组复制文件,统计时间差
public static void copyFileByBytes(String srcPath, String destPath) throws IOException {
long start = System.currentTimeMillis();
try(FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath + "2.jpg");
) {
byte[] b = new byte[1024];
int len;
while ((len = fis.read(b)) != -1) {
fos.write(b, 0, len);
}
}catch (FileNotFoundException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("使用低级字节流按字节数组复制文件耗时:" + (end - start) + "ms");
}
// 使用缓冲流按字节复制文件,统计时间差
public static void copyFileByBufferedChar(String srcPath, String destPath) throws IOException {
long start = System.currentTimeMillis();
try(FileReader fr = new FileReader(srcPath);
FileWriter fw = new FileWriter(destPath + "3.jpg");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
) {
int c;
while ((c = br.read()) != -1) {
bw.write(c);
}
}
catch (FileNotFoundException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("使用缓冲流按字节复制文件耗时:" + (end - start) + "ms");
}
// 使用缓冲流按字节数组复制文件,统计时间差
public static void copyFileByBufferedChars(String srcPath, String destPath) throws IOException {
long start = System.currentTimeMillis();
try(FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath + "4.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
) {
byte[] b = new byte[1024];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}
}catch (FileNotFoundException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("使用缓冲流按字节数组复制文件耗时:" + (end - start) + "ms");
}
}
资源释放问题
1. try-catch-finally :
finally代码区 : 无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止
2. try-with-resource :
该资源使用完毕后,会自动调用close()方法,不用程序员手动释放资源。
语法:
try(定义资源1;定义资源2;...) {
try中的代码;
}catch(Exception e) {
e.printStackTrace();
}
}
package com.ithema.io;
import java.io.*;
public class Copy {
// 文件复制
public static void main(String[] args) throws IOException {
File file1 = new File("D:\\Users\\File_IO\\copy\\test.txt");
File file2 = new File("D:\\Users\\File_IO\\copy1\\test2.txt");
File file3 = new File("D:\\Users\\File_IO\\copy2\\test3.txt");
CopyMethod1(file1.getAbsolutePath(), file2.getAbsolutePath());
CopyMethod2(file1.getAbsolutePath(), file3.getAbsolutePath());
/**
资源释放:
1. try-catch-finally :
finally代码区 : 无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止
2. try-with-resource :
该资源使用完毕后,会自动调用close()方法,不用程序员手动释放资源。
语法:
try(定义资源1;定义资源2;...) {
try中的代码;
}catch(Exception e) {
e.printStackTrace();
}
}
*/
}
// try-catch-finally : 释放资源 (不够简洁)
public static void CopyMethod1(String filename1, String filename2) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(filename1);
byte[] b = fis.readAllBytes();
fos = new FileOutputStream(filename2);
fos.write(b);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 一定会执行:释放资源
try {
if (fis != null) fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// try-with-resource : 释放资源 (简洁)
public static void CopyMethod2(String filename1, String filename2) throws IOException {
try(
// 只能放置资源对象,用完后会自动关闭释放资源
FileInputStream fis = new FileInputStream(filename1);
FileOutputStream fos = new FileOutputStream(filename2);
) {
byte[] b = new byte[1024];
int len;
while ((len = fis.read(b)) != -1) {
String str = new String(b, 0, len);
System.out.print(str);
fos.write(b, 0, len);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
IO框架
what is 框架?
- 框架(Framework): 是预先写好的代码库或一组工具,旨在简化和加速开发过程
- 框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去
what is IO框架?
- IO框架:封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式对文件进行操作,对数据进行读写等
如何导入io框架?
1.commons-io下载链接:Commons IO – Download Apache Commons IO
2. 下载并解压
3.复制jar包导入到项目中
如何查看io框架的说明文档?
常见方法
FileUtils类:
- FileUtils.copyFile(File src, File dest) : 复制文件
- FileUtils.copyDirectory(File src, File dest) : 复制文件夹
- FileUtils.deleteDirectory(File dir) : 删除文件夹
- FileUtils.readFileToString(File file, String encoding) : 读取文件内容
- FileUtils.writeStringToFile(File file, String data, String encoding, boolean append) : 写入文件内容
IOUtils类:
- IOUtils.copy(InputStream input, OutputStream output) : 复制文件
- IOUtils.copy(Reader input, Writer output) : 复制文件
- IOUtils.write(byte[] data, OutputStream output, String charsetName) : 写入文件
多线程
概念
- 线程 : 线程是一个程序内部的一条执行流程
- 单线程 : 如果程序只有一条执行流程,那这个程序就是单线程的程序
- 多线程 : 多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)
- 进程:
1.正在运行的程序(软件)就是一个独立进程
2.线程属于进程
3.进程中的多个线程其实是并发和并行执行的
- 并发(Concurrency)
定义:并发是指多个任务在同一时间段内同时运行,但并不一定是在同一时刻同时运行。在并发模式下,多个任务可能在微观层面上交替执行,即它们共享同一套资源(如CPU时间片),并且在不同的时间片段上执行- 并行(Parallelism)
定义:并行是指多个任务在同一时间点上同时运行,并且能够同时完成。在并行模式下,多个任务可以分配到不同的处理单元(如多核CPU的不同核心、多个处理器或分布式计算环境中的不同节点)上执行。
多线程的创建
<1> 方式一:继承Thread类
step:
1.定义一个子类MyThread类继承线程类java.lang.Thread,重写run()方法
2.创建MyThread类的对象
3.调用线程对象的start方法启动线程(启动后还是执行run方法)
注意:
1.直接调用run方法会当成普通方法执行,此时就相当于是单线程
2.只有调用start方法才是启动一个新的线程执行
3.不要把主线任务放在启动子线程之前
package com.ithema.code;
public class code {
/**
线程(Thread):
概念:
线程 : 线程是一个程序内部的一条执行流程
单线程 : 如果程序只有一条执行流程,那这个程序就是单线程的程序
多线程 : 多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)
多线程的创建:
<1> 方式一:继承Thread类
1.定义一个子类MyThread类继承线程类java.lang.Thread,重写run()方法
2.创建MyThread类的对象
3.调用线程对象的start方法启动线程(启动后还是执行run方法)
注意:
1.直接调用run方法会当成普通方法执行,此时就相当于是单线程
2.只有调用start方法才是启动一个新的线程执行
3.不要把主线任务放在启动子线程之前
<2> 方式二:实现Runnable接口
->匿名内部类写法:
1. 可以创建Runnable接口的匿名内部类对象
2. 再交给Thread线程对象
3.再调用线程对象的start方法启动线程
注意 : 前两种方式存在的问题:假如线程需要返回结果,它们所使用的run方法均不能直接返回结果
如果线程需要返回结果,需要使用Callable接口和FutureTask类来实现
<3> 方式三: 实现Callable接口和FutureTask类
1. 创建任务对象:
定义一个类实现Callable接口,重写call方法,封装要执行的任务代码和要返回的数据
把Callable对象封装成FutureTask(线程任务对象)
2. 把线程任务对象交给Thread对象
3. 调用线程对象的start方法启动线程
4. 调用FutureTask对象的get方法获取线程执行结果
*/
public static void main(String[] args) {
/**创建方式一:继承Thread类*/
// StartThread(); // 多线程
// RunThread(); // 单线程: 直接调用run方法
// MainBefore(); // 单线程 : 主线程先跑
}
//把主线放子线程之前 : 主线程先跑, 还是单线程
public static void MainBefore() {
MyThread mt = new MyThread();
for (int i = 0; i < 5; i++){
System.out.println("主线程任务代码" + i);
}
mt.start();
}
// 直接调用run方法 : 单线程
public static void RunThread(){
// 1. 创建线程类对象代表线程
MyThread mt = new MyThread();
// 2. 调用run方法执行线程任务代码
mt.run();
for (int i = 0; i < 5; i++){
System.out.println("主线程任务代码" + i);
}
}
// 启动线程
public static void StartThread(){
// 4. 创建线程类对象代表线程
Thread mt = new MyThread();
// 5. 调用start方法启动线程 : 调用run方法执行的
mt.start();
for (int i = 0; i < 5; i++){
System.out.println("主线程任务代码" + i);
}
}
}
// 1.定义一个子类继承Thread类
class MyThread extends Thread{
// 2. 重写线程的run方法
@Override
public void run() {
// 3. 在run方法中编写线程的任务代码
for (int i = 0; i < 5; i++){
System.out.println("子线程任务代码" + i);
}
}
}
<2> 方式二:实现Runnable接口
step -> 匿名内部类写法:
1. 可以创建Runnable接口的匿名内部类对象
2. 再交给Thread线程对象
3.再调用线程对象的start方法启动线程注意 :
前两种方式存在的问题:假如线程需要返回结果,它们所使用的run方法均不能直接返回结果
如果线程需要返回结果,需要使用Callable接口和FutureTask类来实现
package com.ithema.code;
public class ThreadDemo2 {
public static void main(String[] args) {
/**创建方式二 :实现Runnable接口 */
//StartThread1();
StartThread2();
}
// 使用匿名内部类启动线程
public static void StartThread1(){
// 3. 创建线程任务对象代表一个线程 : 使用匿名内部类
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++){
System.out.println("子线程任务代码1 : " + i);
}
}
};
// 4. 创建线程对象
Thread t = new Thread(r);
// 5. 启动线程
t.start();
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 5; i++){
System.out.println("子线程任务代码2 : " + i);
}
}
}).start();
for (int i = 0; i < 5; i++){
System.out.println("主线程任务代码 : " + i);
}
}
// 使用接口实现类启动线程
public static void StartThread2(){
// 3. 创建线程任务对象代表一个线程
MyRunnable mr = new MyRunnable();
// 4. 创建线程对象
Thread t = new Thread(mr);
// 5. 启动线程
t.start();
for (int i = 0; i < 5; i++){
System.out.println("主线程任务代码 : " + i);
}
}
}
// 1.定义一个线程任务类来实现Runnable接口
class MyRunnable implements Runnable{
// 2. 重写run方法,设置线程任务
@Override
public void run() {
//线程任务代码
for (int i = 0; i < 5; i++){
System.out.println("子线程任务代码" + i);
}
}
}
<3> 方式三: 实现Callable接口和FutureTask类
step:
1. 创建任务对象:
定义一个类实现Callable接口,重写call方法,封装要执行的任务代码和要返回的数据
把Callable对象封装成FutureTask(线程任务对象)
2. 把线程任务对象交给Thread对象
3. 调用线程对象的start方法启动线程
4. 调用FutureTask对象的get方法获取线程执行结果
package com.ithema.code;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadDemo3 {
public static void main(String[] args) {
/**创建方式三:实现Callable接口和FutureTask类*/
StartThread1();
}
public static void StartThread1(){
// 3. 创建Callable接口实现类的对象
MyCallable callable = new MyCallable(100);
// 4. 把Callable对象封装成真正的线程任务类对象FutureTask对象
// FutureTask继承Runnable接口,所以可以作为Runnable对象传递给Thread对象
FutureTask<String> task = new FutureTask<String>(callable);
// Runnable task = new FutureTask<String>(callable); [多态写法]
// 5. 创建线程对象,将FutureTask对象传递给Thread构造方法
Thread t = new Thread(task);
// 6. 启动线程
t.start();
// 7. 获取线程执行结果(线程执行结束)
try {
// 如果主线程发现第一个线程没有执行完毕,主线程阻塞等待,会让出CPU,让第一个线程执行完毕后,主线程继续往下执行
String result = task.get();
}catch (Exception e) {
e.printStackTrace();
}
}
}
// 1. 定义一个实现类实现Callable接口
class MyCallable implements Callable<String> {
private int num;
public MyCallable(int num) {
this.num = num;
}
// 2. 实现call方法
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <= num; i++) {
sum += i;
}
return "子线程的和为:" + sum;
}
}
Thread类
Thread类提供的常见构造器:
public Thread(String name): 创建一个没有指定名称的线程对象
public Thread(Runnable target): 封装Runnable对象成为线程对象
public Thread(Runnable target, String name): 封装Runnable对象成为线程对象,并指定线程名称
Thread提供的常用方法:
public void run(): 线程的任务方法
public void start(): 启动线程
public String getName(): 获取线程名称,线程名称默认为Thread-0,Thread-1,Thread-2...
public void setName(String name): 设置线程名称
public static Thread currentThread(): 获取当前正在执行的线程对象
public static void sleep (long time): 让当前线程休眠多少毫秒后再继续执行
public final void join(): 让调用当前这个方法的线程先执行完
package com.ithema.code;
public class Demo {
/**
Thread提供的常用方法:
public void run(): 线程的任务方法
public void start(): 启动线程
public String getName(): 获取线程名称,线程名称默认为Thread-0,Thread-1,Thread-2...
public void setName(String name): 设置线程名称
public static Thread currentThread(): 获取当前正在执行的线程对象
public static void sleep (long time): 让当前线程休眠多少毫秒后再继续执行
public final void join(): 让调用当前这个方法的线程先执行完
Thread类提供的常见构造器:
public Thread(String name): 创建一个没有指定名称的线程对象
public Thread(Runnable target): 封装Runnable对象成为线程对象
public Thread(Runnable target, String name): 封装Runnable对象成为线程对象,并指定线程名称
线
*/
public static void main(String[] args) {
Thread t1 = new MyThreadDemo();
t1.start();
System.out.println(t1.getName());
// 休眠1秒
try {
t1.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new MyThreadDemo();
t2.start();
System.out.println(t2.getName());
Thread m = Thread.currentThread(); // 获取当前正在执行的线程对象
System.out.println(m.getName()); // 主线程名字:main
Thread t3 = new MyThreadDemo();
t3.start();
t3.setName("线程3");
System.out.println(t3.getName());
}
}
class MyThreadDemo extends Thread{
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
线程同步的方案(加锁)
方式一 : 同步代码块
作用 : 把访问共享资源的核心代码给上锁,以保证线程安全
格式 :
synchronized(锁对象){
// 访问共享资源的核心代码
}
原理 : 每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
注意 : 锁对象必须是同一个,否则无法同步
锁对象的选择 :
1.建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象
2.对于静态方法建议使用字节码(类名.class)对象作为锁对象
2. 方式二 : 同步方法
作用 : 把访问共享资源的核心方法给上锁,以保证线程安全
格式 :
修饰符 synchronized 返回值类型 方法名(参数列表){
// 访问共享资源的核心代码
}
原理 : 每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
底层原理 : 底层也是隐式锁对象(范围是整个方法)
实例方法的默认锁对象是当前对象 this
静态方法的默认锁对象是 类名.class
3. 方式三 : lock锁
作用 : 可以创建锁对象,进行加锁和解锁
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建锁对象
构造器 :
public ReentrantLock() : 获得Lock锁的实现类对象
方法:
lock() : 获得锁
unlock() : 释放锁
线程池
1.定义:
线程池就是一个可以复用线程的技术:
线程池的接口 :ExecutorService
常用方法:
void execute(Runnable command) : 执行Runnable任务
Future<T> submit(Callable<T> task) : 执行Callable任务,并返回未来任务对象,
用于获取线程返回的结果
void shutdown() :等全部任务完执行成后,关闭线程池
List<Runnable> shutdownNow() : 关闭线程池,不再接受新的任务,并返回尚未执行的任务列表
实现类:ThreadPoolExecutor
构造器 :
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
参数说明:
(1) corePoolSize:指定线程池的核心线程数量.
核心线程池大小,线程池中常驻的线程数量,
即使没有任务,也会一直存活,直到线程池被关闭。
(2) maximumPoolSize:
最大线程池大小,线程池中允许创建的最大线程数量,
如果线程池中线程数量达到这个数量,则后续的任务会被阻塞,等待线程池中线程释放。
(3) keepAliveTime:
指定临时线程的存活时间
(4) unit:
指定临时线程的存活时间单位(秒、分、时、天)
(5) workQueue:
任务队列,用于存放等待被执行的任务,当线程池中的线程数量达到corePoolSize后,后续任务会被存放在workQueue中,等待被执行。
(6) threadFactory:
线程工厂,用于创建线程,默认使用Executors.defaultThreadFactory()。
(7) handler:
拒绝策略,当线程池中的线程数量达到maximumPoolSize后,后续任务会被拒绝,默认使用AbortPolicy策略,直接抛出异常。
2. 创建线程池:
方式一: 使用Executors的实现类ThreadPoolExecutor自创建一个线程池对象
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象(不建议,有弊端)
public static ExecutorService newFixedThreadPool(int nThreads) : 创建固定大小的线程池, 如果某个线程因为执行异常而结束,则线程池会补充一个新线程来替代该线程
public static ExecutorService newCachedThreadPool() : 线程数量随任务增加而增加,如果线程任务执行完毕且空闲了60s,则该线程会被回收掉
public static ExecutorService newSingleThreadExecutor() : 创建一个单线程的线程池,如果这个线程因为执行异常而结束,则线程池会新建一个线程。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) : 创建一个定长线程池,支持定时及周期性任务执行。
3. 临时线程的创建时机:
当线程池中的线程数量达到corePoolSize后,后续的任务会被存放在workQueue中,当有新的任务进来时,会先判断workQueue是否已满,
拒绝新任务:
workQueue已满,且线程池中的线程数量达到maximumPoolSize,则拒绝新任务,抛出异常
4. 任务拒绝策略
ThreadPoolExecutor.AbortPolicy() : 丢弃任务并抛出RejectedExecutionException异常。(默认策略)
ThreadPoolExecutor.DiscardPolicy() : 丢弃任务,但是不抛出异常。(不推荐)
ThreadPoolExecutor.DiscardOldestPolicy() : 丢弃队列中等待最久的任务,然后把当前任务加到队列中。(不推荐)
ThreadPoolExecutor.CallerRunsPolicy() : 由主线程负责调用任务的run()方法从而绕过线程池直接执行
package com.ithema.threadpool;
import java.util.Map;
import java.util.concurrent.*;
public class Test01 {
/**
线程池 :
1.定义:
线程池就是一个可以复用线程的技术:
线程池的接口 :ExecutorService
常用方法:
void execute(Runnable command) : 执行Runnable任务
Future<T> submit(Callable<T> task) : 执行Callable任务,并返回未来任务对象,用于获取线程返回的结果
void shutdown() :等全部任务完执行成后,关闭线程池
List<Runnable> shutdownNow() : 关闭线程池,不再接受新的任务,并返回尚未执行的任务列表
实现类:ThreadPoolExecutor
构造器 :
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
参数说明:
(1) corePoolSize:指定线程池的核心线程数量.
核心线程池大小,线程池中常驻的线程数量,
即使没有任务,也会一直存活,直到线程池被关闭。
(2) maximumPoolSize:
最大线程池大小,线程池中允许创建的最大线程数量,
如果线程池中线程数量达到这个数量,则后续的任务会被阻塞,等待线程池中线程释放。
(3) keepAliveTime:
指定临时线程的存活时间
(4) unit:
指定临时线程的存活时间单位(秒、分、时、天)
(5) workQueue:
任务队列,用于存放等待被执行的任务,当线程池中的线程数量达到corePoolSize后,后续任务会被存放在workQueue中,等待被执行。
(6) threadFactory:
线程工厂,用于创建线程,默认使用Executors.defaultThreadFactory()。
(7) handler:
拒绝策略,当线程池中的线程数量达到maximumPoolSize后,后续任务会被拒绝,默认使用AbortPolicy策略,直接抛出异常。
2. 创建线程池:
方式一: 使用Executors的实现类ThreadPoolExecutor自创建一个线程池对象
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
3. 临时线程的创建时机:
当线程池中的线程数量达到corePoolSize后,后续的任务会被存放在workQueue中,当有新的任务进来时,会先判断workQueue是否已满,
拒绝新任务:
workQueue已满,且线程池中的线程数量达到maximumPoolSize,则拒绝新任务,抛出异常
4. 任务拒绝策略
ThreadPoolExecutor.AbortPolicy() : 丢弃任务并抛出RejectedExecutionException异常。(默认策略)
ThreadPoolExecutor.DiscardPolicy() : 丢弃任务,但是不抛出异常。(不推荐)
ThreadPoolExecutor.DiscardOldestPolicy() : 丢弃队列中等待最久的任务,然后把当前任务加到队列中。(不推荐)
ThreadPoolExecutor.CallerRunsPolicy() : 由主线程负责调用任务的run()方法从而绕过线程池直接执行
*/
public static void main(String[] args) {
// 1.创建线程池对象
// 方式一:使用线程池的实现类ThreadPoolExecutor
ThreadPoolExecutor threadpool = new ThreadPoolExecutor(3, 5, 10,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
// 2.使用线程池执行Callable任务
Future<String>[] future = new Future[5];
for (int i = 1; i < 5; i++) {
MyCallable myCallable = new MyCallable(i);
future[i] = threadpool.submit(myCallable);
}
// 3.获取线程池中的线程执行结果
for (int i = 1; i < 5; i++) {
try {
String result = future[i].get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// 4.关闭线程池 : 等所有任务执行完,再关闭线程池
threadpool.shutdown();
}
}
5.用线程池处理Runnable任务
1.创建线程池对象
2.使用线程池执行任务
3.关闭线程池 : 等所有任务执行完,再关闭线程池
6.用线程池处理Callable任务
1.创建线程池对象
2.使用线程池执行Callable任务
3.获取线程池中的线程执行结果
4.关闭线程池 : 等所有任务执行完,再关闭线程池
注意:如有错误,欢迎指正!!!