##准备实习日记
这是我准备实习的课程经历,记录的只是学习途中个人认为需要记录和新学习到的知识,所说的也全都是个人的理解,如有错误,欢迎指正讨论。JAVA基础课程是我学习的地方。
2022.01.14 JAVA基础——单例模式
2022.01.15 JAVA基础——异常处理
2022.01.15 JAVA基础——I/O
输入/输出
前言
输入指的是从储存器中读取,输出指的是写入到储存器中。一般的输入输出对象都是文件。这里总结,如何创建和使用文件对象,如何使用各种输入输出流来读取文件内容或写入文件。
一、文件对象
文件和文件夹都是File的代表。可以使用绝对路径或相对路径来创建File对象,但是创建了File对象并不意味着,在此路径下真正创建了这个文件或文件夹。要想真正创建文件夹,需要调用创建方法。
import java.io.File;
public class TestFile {
public static void main(String[] args) {
// 绝对路径,代表一个文件夹,可以通过此文件夹来代表路径
File f1 = new File("d:/Hello world");
System.out.println("f1的绝对路径:" + f1.getAbsolutePath());
// 相对路径,工作目录
File f2 = new File("Helloworld.exe");
System.out.println("f2的绝对路径:" + f2.getAbsolutePath());
// 把f1作为父目录创建文件对象
File f3 = new File(f1, "Helloworld.exe");
System.out.println("f3的绝对路径:" + f3.getAbsolutePath());
}
}
File对象中内置很多方法,可以查询该文件或文件夹的具体信息,还可以创建文件。
import java.io.File;
import java.util.Date;
public class TestFile {
public static void main(String[] args) {
File f = new File("d:/Hello world/Helloworld.exe");
System.out.println("当前文件是:" +f);
//文件是否存在
System.out.println("判断是否存在:"+f.exists());
//是否是文件夹
System.out.println("判断是否是文件夹:"+f.isDirectory());
//是否是文件(非文件夹)
System.out.println("判断是否是文件:"+f.isFile());
//文件长度
System.out.println("获取文件的长度:"+f.length());
//文件最后修改时间
long time = f.lastModified();
Date d = new Date(time);
System.out.println("获取文件的最后修改时间:"+d);
//设置文件修改时间为1970.1.1 08:00:00
f.setLastModified(0);
//文件重命名
File f2 =new File("d:/Hello world/Hello.exe");
f.renameTo(f2);
/*注意: 需要在D:\\Hello world确实存在一个Hello.exe
才可以看到对应的文件长度、修改时间等信息*/;
// 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
f.list();
// 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
File[]fs= f.listFiles();
// 以字符串形式返回获取所在文件夹
f.getParent();
// 以文件形式返回获取所在文件夹
f.getParentFile();
// 创建文件夹,如果父文件夹skin不存在,创建就无效
f.mkdir();
// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹
f.mkdirs();
// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
f.createNewFile();
// 所以创建一个空文件之前,通常都会创建父目录
f.getParentFile().mkdirs();
// 列出所有的盘符c: d: e: 等等
f.listRoots();
// 刪除文件
f.delete();
// JVM结束的时候,刪除文件,常用于临时文件的删除
f.deleteOnExit();
}
}
二、输入/输出流
流一般指数据流,就是一系列的数据。
当不同的介质之间有数据交互的时候,JAVA就使用流来实现。例如读取硬盘中的数据到缓存中,站在程序的角度来看,就叫做输入流。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序。
1.字节流
用于以字节的形式读取和写入数据。所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放。常见的编码如ASCII码。
a. 字节输入流
InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class TestStream {
public static void main(String[] args) {
try {
//准备文件Hello.txt其中的内容是AB,对应的ASCII分别是65 66
File f =new File("d:/Hello world/Hello.txt");
//创建基于文件的输入流,注意创建流并不等于开始读取
FileInputStream fis =new FileInputStream(f);
//创建字节数组,其长度就是文件的长度
byte[] all =new byte[(int) f.length()];
//以字节流的形式读取文件所有内容,这里才是真正开始读取
fis.read(all);
for (byte b : all) {
//打印出来是65 66
System.out.println(b);
}
//每次使用完流,都应该进行关闭
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
b. 字节输出流
OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据。
注: 如果文件d:/Hello.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/Hello world/Hello.txt,而目录/Hello world又不存在,会抛出异常。
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestStream {
public static void main(String[] args) {
try {
// 准备文件Hello.txt其中的内容是空的
File f = new File("d:/Hello.txt");
// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };
// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);
// 把数据写入到输出流
fos.write(data);
// 关闭输出流
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
所有的流在使用后都应该正常关闭,否则会不利于项目的进行。比如,在某些情况下只允许建立一个或几个流,如果每个流在使用后都不关闭,那么很快就无法建立新的流供他人使用。
2.字符流
Reader字符输入流,Writer字符输出流,专门用于字符的形式读取和写入数据。
a. 字符输入流
FileReader 是Reader子类,以FileReader 为例进行文件读取。
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class TestStream {
public static void main(String[] args) {
// 准备文件Hello.txt其中的内容是AB
File f = new File("d:/Hello.txt");
// 创建基于文件的Reader
//这里将File对象的创建放在了try中,是关闭流的方式之一,省去了调用关闭方法
try (FileReader fr = new FileReader(f)) {
// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];
// 以字符流的形式读取文件所有内容
fr.read(all);
for (char b : all) {
// 打印出来是A B
System.out.println(b);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
b. 字符输出流
FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件。
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class TestStream {
public static void main(String[] args) {
// 准备文件Hello.txt
File f = new File("d:/Hello.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {
// 以字符流的形式把数据写入到文件中
String data="asdfghjkl";
char[] cs = data.toCharArray();
fr.write(cs);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3.缓存流
实际上,字节流和字符流的使用有很大弊端,例如,如果访问介质是硬盘,那么字节流和字符流的每一次读写命令都会访问硬盘,如果读写频率太高的话,性能就不太好。
缓存流就是为了解决这个问题。缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区满了,才把这些数据一起写入硬盘。
a.缓存流读取
缓存字符输入流 BufferedReader 可以一次读取一行数据。
File f = new File("d:/Hello.txt");
// 创建文件字符流
// 缓存流的参数必须是一个存在的流
try (
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
)
{
while (true) {
// 一次读一行
String line = br.readLine();
if (null == line)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b.缓存流写入
PrintWriter 缓存字符输出流, 可以一次写出一行数据
File f = new File("d:/Hello.txt");
try (
FileWriter fw = new FileWriter(f);
PrintWriter pw = new PrintWriter(fw);
) {
pw.println("abcdefg");
pw.println("higklmn");
pw.println("opq rst");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c.flush方法
使用缓存流时,有时也需要立即把数据写入硬盘,不能等缓存满了才写入, 可以使用flush方法。
File f =new File("d:/Hello.txt");
//创建文件字符流
//缓存流必须建立在一个存在的流的基础上
try(
FileWriter fr = new FileWriter(f);
PrintWriter pw = new PrintWriter(fr);
) {
pw.println("abcdefg");
//强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.flush();
pw.println("hijklmn");
pw.flush();
pw.println("opq rst");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
4.数据流
当进行字节或字符流写入时,如果需要写入很多int 型数字,比如先写入了258,再写入456,那么读取时,会发现无法分离相应的数字,到底是在258分开还是在25分开,这就有问题了。所有需要写入其他的标识符表示分割例如空格。但是数据流可以解决标识符的问题。数据流的writeUTF()和readUTF() 可以进行格式化的顺序读写。
本例中,通过DataOutputStream 向文件顺序写出 布尔值,整数和字符串。 然后再通过DataInputStream 顺序读入这些数据。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestStream {
public static void main(String[] args) {
write();
read();
}
private static void read() {
File f =new File("d:/Hello.txt");
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();
System.out.println("读取到布尔值:"+b);
System.out.println("读取到整数:"+i);
System.out.println("读取到字符串:"+str);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void write() {
File f =new File("d:/Hello.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(1000);
dos.writeUTF("abcdefg");
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.对象流
对象流是指把一个对象以流的形式整体传输给其他介质。一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口。不举例了。
三、关闭流
1.在try代码块中关闭
在try的作用域里关闭文件输入流,但是这有一个弊端:如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。
try {
File f = new File("d:/hello.txt");
FileInputStream fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
// 在try代码段中关闭流
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
2.在finally代码块中关闭
finally代码块时放在try catch代码块后面的代码块,在执行完try catch代码块之后,一定会执行的部分。
这种方法是关闭流的标准方法
1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
2. 在finally关闭之前,要先判断该引用是否为空
3. 关闭的时候,需要再一次进行try catch处理
File f = new File("d:/hello.txt");
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 在finally 代码块中关闭流
if (fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3.使用try(…)的方式
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭。所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
File f = new File("d:/hello.txt");
//try,catch或者finally结束的时候,这种方式会自动关闭流
try (FileInputStream fis = new FileInputStream(f)) {
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}