目录
一:IO流(输入输出流)
1:File类(位于java.io包下)
1.1:File的创建
常用构造方法
文件绝对路径在idea的位置:右键文件,选择Copy Path,获取绝对路径
这些构造方法均未声明异常
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 |
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 |
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 |
import java.io.File;
public class FileTest {
public static void main(String[] args) {
//方式1 绝对路径创建
File file1 = new File("D:\\IDEA\\hello.txt");
//方式二 相对路径创建
File file2 = new File("src\\hello.txt");
//方式三 根据父类路径下创建
File file3 = new File("D:\\IDEA", "hello.txt");
}
}
1.2:获取File类信息的方法
使用:对象名. 方法
方法摘要 | |
---|---|
boolean |
createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 |
boolean |
delete() 删除此抽象路径名表示的文件或目录。 |
void |
deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。 |
boolean |
equals(Object obj) 测试此抽象路径名与给定对象是否相等。 |
boolean |
exists() 测试此抽象路径名表示的文件或目录是否存在。 |
File |
getAbsoluteFile() 返回此抽象路径名的绝对路径名形式。 |
String |
getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。 |
String |
getName() 返回由此抽象路径名表示的文件或目录的名称。 |
String |
getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null 。 |
File |
getParentFile() 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null 。 |
String |
getPath() 将此抽象路径名转换为一个路径名字符串。 |
boolean |
isAbsolute() 测试此抽象路径名是否为绝对路径名。 |
boolean |
isDirectory() 测试此抽象路径名表示的文件是否是一个目录。 |
boolean |
isFile() 测试此抽象路径名表示的文件是否是一个标准文件。 |
long |
length() 返回由此抽象路径名表示的文件的长度。 |
String[] |
list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 |
File[] |
listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 |
import java.io.File;
import java.io.IOException;
public class FileExample {
public static void main(String[] args) {
// 创建一个 File 对象
File file = new File("example.txt");
// 创建新文件
try {
if (file.createNewFile()) { //如果为true 则在相对路径下创建
System.out.println("文件已创建: " + file.getName()); //获取文件名
} else {
System.out.println("文件已存在。");
}
} catch (IOException e) {
System.out.println("发生了一个错误。");
e.printStackTrace();
}
// 获取文件信息
System.out.println("绝对路径: " + file.getAbsolutePath());
System.out.println("文件大小: " + file.length() + " 字节");
// 删除文件
if (file.delete()) {
System.out.println("文件已删除。");
} else {
System.out.println("删除文件失败。");
}
}
}
1.3:文件的 递归 删除操作(不走回收站)
为什么不能直接delete(),因为如果是目录下还存在文件,无法执行此方法。
下面示例包含了对文件数组的增强for遍历(listFiles()方法),与集合不同的是,输入输出流可以在增强for循环内进行删除操作。
import java.io.File;
public class FileDeletion {
public static void main(String[] args) {
// 指定要删除的目录路径
File directoryToDelete = new File("path/to/directory");
// 调用递归删除方法
if (deleteDirectory(directoryToDelete)) {
System.out.println("目录及其内容已成功删除: " + directoryToDelete.getAbsolutePath());
} else {
System.out.println("删除操作失败。");
}
}
// 递归删除文件和目录
public static boolean deleteDirectory(File directory) {
// 确保传入的是一个目录
if (!directory.exists()) {
System.out.println("目录不存在: " + directory.getAbsolutePath());
return false;
}
if (directory.isDirectory()) {//如果是目录
// 获取目录中的所有文件和子目录
File[] files = directory.listFiles(); //返回文件数组
if (files != null) {
for (File file : files) {
// 递归删除子文件和子目录
if (file.isDirectory()) {
deleteDirectory(file); // 递归调用
} else {
// 删除文件
if (file.delete()) {
System.out.println("删除文件: " + file.getAbsolutePath());
} else {
System.out.println("删除文件失败: " + file.getAbsolutePath());
}
}
}
}
}
// 删除目录本身
return directory.delete();
}
}
2:字节流
2.1:字节流概述
以字节为单位,适用于处理二进制数据(如图像、音频和所有文件类型)。
主要接口 输入流 InputStream 输出流 OutputStream。
他们的主要实现类 :FileInputStream 和 FileOutputStream
2.2:字节输入流 FileInputStream
构造方法摘要 | |
---|---|
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的 File 对象 file 指定。 ***会抛出异常FileNotFoundException*** |
|
FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的路径名 name 指定。 |
方法摘要 | |
---|---|
void |
close() 关闭此文件输入流并释放与此流有关的所有系统资源。 |
int |
read() 从此输入流中读取一个数据字节。返回读取的字节(范围:0-255)或 -1 表示到达流的末尾。 |
int |
read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 |
int |
read(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 |
示例,现目录下有一个hello.txt文件,内容是:helloworld,使用字节输入流依次读此文件。
import java.io.*;
public class FileTest {
public static void main(String[] args) {
FileInputStream fis=null;
//创建字节输入流
try {
fis= new FileInputStream("csdn/src/hello.txt");
int len1 = fis.read();
System.out.println(len1+" "+(char)(len1));
int len2 = fis.read();
System.out.println((char)(len2));
} catch (IOException e) {
e.printStackTrace();
} finally {//用于关闭流资源
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 结果是
104 h
e
使用字节输入流循环读取文件。
import java.io.*;
public class FileTest {
public static void main(String[] args) {
FileInputStream fis=null;
//创建字节输入流
try {
fis= new FileInputStream("csdn/src/hello.txt");
int len;
while((len=fis.read())!=-1){
//使用循环挨个读取 并打印
System.out.print((char)len+" ");
}
} catch (IOException e) {
e.printStackTrace();
} finally {//用于关闭流资源
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//结果为:h e l l o w o r l d
使用字节输入流一次读取一个字节数组。
import java.io.*;
public class FileTest {
public static void main(String[] args) {
FileInputStream fis=null;
byte[] bytes = new byte[10];
//创建字节输入流
try {
fis= new FileInputStream("csdn/src/hello.txt");
fis.read(bytes);//把helloworld 共十个字节读入bytes数组中
for (byte aByte : bytes) {
//循环遍历输出 元素
System.out.print((char) aByte+" ");
}
} catch (IOException e) {
e.printStackTrace();
} finally {//用于关闭流资源
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//结果 h e l l o w o r l d
2.3:字节输出流FileOutputStream
构造方法摘要 | |
---|---|
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
|
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
|
构造器后面的布尔类型代表此文件可以追加输出 | |
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。 |
|
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。 |
方法摘要 | |
---|---|
void |
close() 关闭此文件输出流并释放与此流有关的所有系统资源。 |
void |
write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。 |
void |
write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 |
void |
write(int b) 将指定字节写入此文件输出流。 |
往文件中添加单个字符。
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
//1 创建输出流对象,并指定在hello.txt文件中写出
FileOutputStream fos = new FileOutputStream("csdn/src/hello.txt");
//往文件中写一个字符a
fos.write(97);//a的ASCII值
//释放流资源
fos.close();
}
}
//结果文件为a
往文件中写出字符串。
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {//往外抛异常
//1 创建输出流对象,并指定在hello.txt文件中写出
FileOutputStream fos = new FileOutputStream("csdn/src/hello.txt");
//往文件中写一个字符串
String s="helloworld";
//转化为字节数组
byte[] bytes = s.getBytes();
fos.write(bytes);//写一个字节数组
//释放流资源
fos.close();
}
}
//结果文件为 helloworld
在上述结果下,继续在后面添加“你好世界!”。
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
//1 创建输出流对象,并指定在hello.txt文件中写出
FileOutputStream fos = new FileOutputStream("csdn/src/hello.txt",true);
//往文件中写一个字符串
String s="你好世界!";
//转化为字节数组
byte[] bytes = s.getBytes();
fos.write(bytes);//写一个字节数组
//释放流资源
fos.close();
}
}
//结果文件为 helloworld你好世界!
从字节数组的特定位置写特定个字节
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
//1 创建输出流对象,并指定在hello.txt文件中写出
FileOutputStream fos = new FileOutputStream("csdn/src/hello.txt");
//往文件中写一个字符串
String s="hello!";
//转化为字节数组
byte[] bytes = s.getBytes();
fos.write(bytes,0,2);//从字节数组的0索引开始写2个字节
//释放流资源
fos.close();
}
}
//结果文件为 he
2.4:使用字节流进行文件的复制操作
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
String sourceFile = "source.txt"; // 源文件路径
String destinationFile = "destination.txt"; // 目标文件路径
FileInputStream fis = new FileInputStream(sourceFile);//创建输入流读取源文件
FileOutputStream fos = new FileOutputStream(destinationFile);//创建输出流写出文件
byte[] buffer = new byte[1024]; // 缓冲区 ,一次读一个字节数组
int len;
// 从源文件读取数据并写入目标文件
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len); //读到多少个,往文件写出len个字节
}
}
}
3:字符流
3.1:字符流概述
字符流是处理字符(文本)数据的流,旨在支持字符数据的高效读写。Java中的字符流主要包含两种类型:Reader
类和Writer
类
3.2:Reader类和Writer类的核心方法
(1) Reader
构造方法摘要 | |
---|---|
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 |
|
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。 |
read()
: 读取单个字符。read(char[] cbuf)
: 将字符读入字符数组。readLine()
: 从输入流读取一行字符(在BufferedReader
中使用)。
(2) Writer
构造方法摘要 | |
---|---|
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 |
|
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 |
|
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 |
|
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 |
write(int c)
: 写入单个字符。write(char[] cbuf)
: 写入字符数组。write(String str)
: 写入字符串。flush()
: 刷新输出流,确保所有数据都被写入。close()
: 关闭流,释放资源。
示例代码:使用 FileReader
和 FileWriter进行文件复制
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) {
String sourceFile = "source.txt"; // 源文件路径
String destinationFile = "destination.txt"; // 目标文件路径
// 使用 FileReader 读取字符文件
try {
FileReader fr = new FileReader(sourceFile);
FileWriter fw = new FileWriter(destinationFile);
int character; // 用于存放每次读取的字符
// 逐个字符读取源文件并写入目标文件
while ((character = fr.read()) != -1) {
fw.write(character); // 写入目标文件
}
System.out.println("文件复制成功!");
} catch (IOException e) {
System.err.println("文件复制失败: " + e.getMessage());
}
}
}
4:缓存流
4.1:缓冲流概念
缓存流在输入输出流的基础上增加了一层缓存,数据先被读取到内存中,然后再由缓存批量传输到目标位置。这样可以显著减少对磁盘或网络的直接访问次数。
4.2:主要实现类:
- BufferedInputStream:为字节输入流提供缓存。
- BufferedOutputStream:为字节输出流提供缓存。
- BufferedReader:为字符输入流提供缓存,同时还可以通过
readLine()
方法逐行读取字符串。 - BufferedWriter:为字符输出流提供缓存,能够以字符为单位写入数据,有助于提高写入效率。
字节缓冲流复制文件示例代码
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
// 源文件路径
String sourceFilePath = "path/to/source/file.txt";
// 目标文件路径
String destinationFilePath = "path/to/destination/file.txt";
// 调用复制文件的方法
copyFile(sourceFilePath, destinationFilePath);
}
public static void copyFile(String source, String destination) {
// 创建流对象
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destination))) {
// 设置一个缓冲区
byte[] buffer = new byte[8192]; // 8KB的缓冲区
int bytesRead;
//循环读取和写入
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
System.out.println("文件复制成功!");
} catch (IOException e) {
System.err.println("文件复制失败: " + e.getMessage());
}
}
}
字符缓存流文件复制示例代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharFileCopy {
public static void main(String[] args) {
// 源文件路径
String sourceFilePath = "path/to/source/file.txt";
// 目标文件路径
String destinationFilePath = "path/to/destination/file.txt";
// 调用复制文件的方法
copyFile(sourceFilePath, destinationFilePath);
}
public static void copyFile(String source, String destination) {
// 创建流对象
try (BufferedReader br = new BufferedReader(new FileReader(source));
BufferedWriter bw = new BufferedWriter(new FileWriter(destination))) {
String line;
// 循环读取每一行并写入目标文件
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine(); // 添加换行符
}
System.out.println("文件复制成功!");
} catch (IOException e) {
System.err.println("文件复制失败: " + e.getMessage());
}
}
}
二:多线程
1:认识线程
⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按照自己的代码. 多个线程之间 “同时” 执行着多份代码,main()⼀般被称为主线程(Main Thread)。
- 进程是包含线程的. 每个进程至少有⼀个线程存在,即主线程。
- 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.
- 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
2:线程的创建
2.1:继承Thread类
public class Test {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();//开启线程方式 对象.start() 方法
}
}
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("这⾥是线程运⾏的代码");
}
}
2.2:实现Runnable接口
为了克服单继承(继承了其他类不能再继承Thread类)的局限性,可以实现此接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程开启了");
}
}
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();//启动线程
}
}
2.3:匿名内部类实现Runnable接口
public class Test {
public static void main(String[] args) {
// 使⽤匿名类创建 Runnable ⼦类对象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("开启线程");
}
});
t2.start();
}
}
2.4:使用Lambda表达式创建线程
public class Test {
public static void main(String[] args) {
new Thread(() -> System.out.println("使用lambda表达式创建线程")).start();
}
}
3:Thread类常用API
3.1:常见构造方法
构造方法摘要 | |
---|---|
Thread() 分配新的 Thread 对象。 |
|
Thread(Runnable target) 分配新的 Thread 对象。 |
|
Thread(Runnable target, String name) 分配新的 Thread 对象。 |
|
Thread(String name) 分配新的 Thread 对象。 |
Thread th1 = new Thread(new MyRunnable());
Thread th2= new Thread("我的名字");
Thread th3 = new Thread(new MyRunnable(), "我的名字");
3.2:线程状态方法
方法摘要 | |
---|---|
static Thread |
currentThread() 返回对当前正在执行的线程对象的引用。 |
long |
getId() 返回该线程的标识符。 |
String |
getName() 返回该线程的名称。 |
int |
getPriority() 返回线程的优先级。 |
StackTraceElement[] |
getStackTrace() 返回一个表示该线程堆栈转储的堆栈跟踪元素数组。 |
Thread.State |
getState() 返回该线程的状态。 |
void |
interrupt() 中断线程。 |
boolean |
isAlive() 测试线程是否处于活动状态。 |
boolean |
isInterrupted() 测试线程是否已经中断。 |
void |
join() 等待该线程终止。 |
void |
join(long millis) 等待该线程终止的时间最长为 millis 毫秒。 |
void |
setName(String name) 改变线程名称,使之与参数 name 相同。 |
void |
setPriority(int newPriority) 更改线程的优先级。 |
static void |
sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
void |
start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
void |
stop(Throwable obj) 已过时。 该方法具有固有的不安全性。有关详细信息,请参阅 stop() 。该方法的附加危险是它可用于生成目标线程未准备处理的异常(包括若没有该方法该线程不太可能抛出的已检查的异常)。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。 |
String |
toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。 |
static void |
yield() 暂停当前正在执行的线程对象,并执行其他线程。 |
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 线程结束");
});
System.out.println(Thread.currentThread().getName() + ": ID: " + thread.getId());System.out.println(Thread.currentThread().getName() + ": 名称: " + thread.getName());
System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState());
System.out.println(Thread.currentThread().getName() + ": 优先级: " + thread.getPriority());
System.out.println(Thread.currentThread().getName() + ": 活着: " + thread.isAlive());System.out.println(Thread.currentThread().getName() + ": 被中断: " + thread.isInterrupted());
thread.start();
}
}
3.3:线程的生命周期
- 新建状态 (New): 线程被创建但尚未开始运行。
- 就绪状态 (Runnable): 线程可以运行并正在等待CPU的时间片。
- 运行状态 (Running): 线程正在执行其任务。
- 阻塞状态 (Blocked): 线程因等待另一个线程释放某个对象的锁而阻塞。
- 死亡状态 (Terminated): 线程的执行结束,无法再次启动。
3.4:关于synchronized关键字解决线程共享问题
这里以存取钱为例子
同步方法:
class BankAccount {
private int balance = 0;
// 同步方法,确保线程安全
public synchronized void deposit(int amount) {
balance += amount;
System.out.println("Deposited: " + amount + ", New Balance: " + balance);
}
public synchronized void withdraw(int amount) {
if (balance >= amount) {
balance -= amount;
System.out.println("Withdrew: " + amount + ", New Balance: " + balance);
} else {
System.out.println("Insufficient funds for withdrawal of: " + amount);
}
}
public int getBalance() {
return balance;
}
}
同步代码块:
class BankAccount {
private int balance = 0;
public void deposit(int amount) {
synchronized (this) { // 使用同步代码块
balance += amount;
System.out.println("Deposited: " + amount + ", New Balance: " + balance);
}
}
public void withdraw(int amount) {
synchronized (this) { // 使用同步代码块
if (balance >= amount) {
balance -= amount;
System.out.println("Withdrew: " + amount + ", New Balance: " + balance);
} else {
System.out.println("Insufficient funds for withdrawal of: " + amount);
}
}
}
public int getBalance() {
return balance;
}
}