1.java的IO体系
文件读写的基本类(io)
输入流:文件到程序
输出流:程序到文件
技巧;数据向内存流动就是输入流,反之就是输出流
java的流分两种:字节流和字符流
字节流:以字节为单位去读取的 可以用于读写二进制文件以及任何类型的文件
接口:InputStream OutputStream
类: FileInputStream FileOutputStream
方法 read write
能用记事本打开的都是文本文件,否则是二进制文件
字符流:可以用于读写文本文件,但是不能操作二进制文件
接口:Reader Writer
类: FileReader FileWriter
方法: read write
InputStream:表示那些从不同数据源产生输入的类,
ByteArrayInputStream:允许将内存的缓冲区当作InputStream来使用。
StringBufferInputStream:将String转换为InputStream
FileInputStream:用于从文件中读取信息
PipedInputStream:产生用于写入相关PipedOutInputStream 的数据,实现管道化的概念。
SequenceInputStream:将两个或多个InputStream对象转换成单一的InputStream
FilterInputStream:抽象类,作为装饰器的接口,装饰器类为其他InputStream类提供有用的功能。
BufferedInputStream:使用它可以防止每次读取时都得进行实际写操作。
OutputStream:该类别的类决定了输出所要去往的目标
ByteArrayOutputStream:在内存中创建缓冲区,所有送往流的数据都要放置在此缓冲区
FileOutputStream:用于将信息写至文件
PipedOutputStream:任何写入其中的信息,都会自动作为相关PipedOutputStream的输出,实现管道化的概念。
FilterOutputStream:抽象类,作为装饰器的接口,装饰器类为其他OutputStream类提供有用的功能。
Reader和Writer提供兼容Unicode和面向字符得功能。设计Reader和Writer继承层次结构主要是为了国际化。另外新的类库得设计使得他的操作比旧的类库快。 所以明智的做法是尝试使用Reader和Writer,一旦代码无法编译过,我们就会发现自己不得不使用面向字节的类库。比如java.util.zip类库就是面向字节而不是面向字符的。
IO的典型使用方式
1.缓冲输入文件
如果想要打开一个文件用于字符串的输入,使用FileReader当然可以直接读取,但是我们希望提高速度,所以使用BufferdReader进行读取:
public class BufferedInputFile {
public static String read(String fileName) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName));
StringBuilder sb = new StringBuilder();
String str;
while ((str = bufferedReader.readLine()) != null) {
sb.append(str + "\n");
}
bufferedReader.close();
return sb.toString();
}
public static void main(String[] args) throws IOException {
String read = read("demo.txt");
System.out.println(read);
}
}
2.从内存中输入
读取上个程序输入到内存中的数据,使用StringReader读取,调用read()方法,每次读一个字符
public class MemoryInput {
public static void main(String[] args) {
try (StringReader stringReader = new StringReader(BufferedInputFile.read("demo.txt"))) {
int c;
while ((c = stringReader.read()) != -1) {
System.out.println((char) c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.格式化的内存输入
如果要读取格式化数据,我们要用到 DataInputStream,它是一个面向字节的 I/O 类(不是面向字符的)。因此我们必须使用 InputStream 类而不是 Reader 类。当然,我们可以用 InputStream 以字节的形式读取任何数据(例如一个文件),不过,在这里使用的是字符串。为了将字符串转换为成适用于 ByteArrayInputStream 的字节数组,String 包含了一个可以实现此项工作的 getBytes()方法。至此,我们就持有了一个可传递给DataInputStream 的 InputStream。
public class FormattedMemoryInput {
public static void main(String[] args) {
try (DataInputStream dataInputStream = new DataInputStream(
new ByteArrayInputStream(BufferedInputFile.read("demo.txt").getBytes()))) {
// while (true) {
// System.out.println((char) dataInputStream.readByte());
// }
while (dataInputStream.available() != 0) {
System.out.println((char) dataInputStream.readByte());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
中文有乱码。
4.基本文件的输出
通常我们会选用FileWriter,但是为了显著的增加IO操作的性能,我们通常会使用BufferWriter将其包装起来,用以缓冲输出。如果要使用格式化的机制。可以使用PrintWriter, 这里是将文件读取出来,格式化写入一个新的文件中
public class BasicFileOutput {
private static final String FILE_NAME = "demo2.txt";
public static void main(String[] args) {
try {
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter(FILE_NAME)));
BufferedReader bufferedReader = new BufferedReader(new FileReader("demo.txt"));
String str;
int lineCount = 1;
while ((str = bufferedReader.readLine()) != null) {
printWriter.println(lineCount++ + ":" + str);
}
printWriter.close();
bufferedReader.close();
System.out.println(BufferedInputFile.read(FILE_NAME));
} catch (IOException e) {
e.printStackTrace();
}
}
}
java 5之后PrintWriter不用去装饰就可以便捷的输出文件,但是仍然在进行缓存,只是不需要自己去实现。
5.存储和恢复数据
格式化输出文件方便人们阅读,但是要恢复数据我们需要DataInputStream,使用DataOutputStream写入数据,保证准确的存储和读取数据,这一点很有价值。
public class StringAndRecoveringData {
public static void main(String[] args) throws IOException {
DataOutputStream dataOutputStream = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("demo.txt")));
dataOutputStream.writeDouble(3.14159);
dataOutputStream.writeUTF("我是钢铁侠");
dataOutputStream.close();
DataInputStream dataInputStream = new DataInputStream(
new BufferedInputStream(new FileInputStream("demo.txt")));
String s;
System.out.println(dataInputStream.readDouble());
System.out.println(dataInputStream.readUTF());
}
}
6.文件读写的实用工具:
public class TextFile extends ArrayList<String> {
/**
* 读取文件的方法,一行一行的读
*
* @param fileName
* @return
*/
public static String read(String fileName) {
StringBuilder sb = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName));) {
String str;
while ((str = bufferedReader.readLine()) != null) {
sb.append(str + "\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
/**
* 写入指定的文件
*
* @param fileName
* @param text
*/
public static void write(String fileName, String text) {
try (PrintWriter printWriter = new PrintWriter(new File(fileName).getAbsoluteFile());) {
printWriter.print(text);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 读取二进制文件
*
* @param file
* @return
*/
public static byte[] readBinary(File file) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(
new FileInputStream(file));) {
byte[] bytes = new byte[bufferedInputStream.available()];
bufferedInputStream.read(bytes);
return bytes;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static byte[] readBinary(String file) throws IOException {
return readBinary(new File(file).getAbsoluteFile());
}
public TextFile(String fileName, String splitter) {
super(Arrays.asList(read(fileName).split(splitter)));
if (Objects.isNull(get(0))) {
remove(0);
}
}
public TextFile(String fileName) {
this(fileName, "\n");
}
public void write(String fileName) {
try (PrintWriter printWriter = new PrintWriter(new File(fileName).getAbsoluteFile());) {
for (String item : this) {
printWriter.println(item);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
文件的压缩:
java 字节流与字符流的区别?:
实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作 文件就是说程序没有关闭,流没有关闭的时候字符流操作的数据还留在缓冲区如果想在不关闭时也可以将字符流的内容全部输出,则 可以使用Writer类中的flush()方法完成。
使用字节流好还是字符流好?
在回答之前,先为读者讲解这样的一个概念,所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。
基于磁盘的io接口:file
基于网络的io接口:socket
InputStreamReader类是从字符到字节的转化桥梁
1.File类:将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。File可以用来检查和创建目录,判断目录是否存在等等
File类:可以找到磁盘上的一个文件
File类与FileInputStream类的区别:
FileInputStream类关注的是文件内容,而File类关注的是文件在磁盘上的存储。
File不属于文件流,只能代表一个文件或是目录的路径名而已。
2.代码:
public static void main(String[] args) throws IOException { //读、写都会发生IO异常
/*
1:创建一个字符输出流对象,用于操作文件。该对象一建立,就必须明确数据存储位置,是一个文件。
2:对象产生后,会在堆内存中有一个实体,同时也调用了系统底层资源,在指定的位置创建了一个存储数据的文件。
3:如果指定位置,出现了同名文件,文件会被覆盖。
*/
FileWriter fw = new FileWriter("demo.txt"); // FileNotFoundException
/*
调用Writer类中的write方法写入字符串。字符串并未直接写入到目的地中,而是写入到了流中,(其实是写入到内存缓冲区中)。怎么把数据弄到文件中?
*/
fw.write("abcde");
fw.flush(); // 刷新缓冲区,将缓冲区中的数据刷到目的地文件中。
fw.close(); // 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。
}
public static void main(String[] args) throws IOException {
/*
创建可以读取文本文件的流对象,FileReader让创建好的流对象和指定的文件相关联。
*/
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch = fr.read())!= —1) { //条件是没有读到结尾
System.out.println((char)ch); //调用读取流的read方法,读取一个字符。
}
fr.close();
}
}
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt"); //创建读取流对象和指定文件关联。
//因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。
char[] buf = new char[1024];
int len = 0;
while(( len=fr.read(buf)) != —1) {
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
BufferedWriter(FileWriter fileWriter ):将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。传入一个FileWriter 就明确关联了FileWriter 流对象,
//记住,只要一读取键盘录入,就用这句话。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台
FileWriter fw = new FileWriter("bufdemo.txt");
BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联。
for(int x=0; x<4; x++){
bufw.write(x+"abc");
bufw.newLine(); //写入一个换行符,这个换行符可以依据平台的不同写入不同的换行符。
bufw.flush();//对缓冲区进行刷新,可以让数据到目的地中。
}
bufw.close();//关闭缓冲区,其实就是在关闭具体的流。
流的操作规律:
1,明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;
2,操作的数据是否是纯文本数据?
如果是:数据源:Reader
数据汇:Writer
如果不是:数据源:InputStream
数据汇:OutputStream
3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其他功能吗?比如缓冲。
如果需要就进行装饰。
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。转换流的最强功能就是基于 字节流 + 编码表 。没有转换,没有字符流。
发现转换流有一个子类就是操作文件的字符流对象:
InputStreamReader
|——FileReader
OutputStreamWriter
|——FileWrier
想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
以上两句代码功能一致,
如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。
3.问题:
- 为什么会有两种流的出现?
GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流
2.close()和flush()的区别:他们都有将数据刷新到缓冲区的功能,但是一个关闭了流。一个没有关闭流。
3.缓冲区是提高效率用的,给谁提高呢?
缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率,给他传入的那个流对象提高效率用的。
4. 使用流的原则:清楚输入输出设备,清楚文件的类型。
5.为什么适配器类可以进行流类型的转换(这个转换时字节流到字符流)?原因在于,将获取到的字节通过查编码表获取到指定对应字符。
6.操作什么文件的时候需要进行编码转换?或者什么时候使用转换流?
操作文本文件的时候就需要进行编码转换,但是这个适配器类下面有两个子类,分别是FileReader和FileWrier。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。编码转换就被流自动完成了。默认的情况下,就是使用本机的默认码表,对于简体中文版的系统默认码表是GBK。但是如果需要指定码表就必须使用转换流对象。凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
7.除了输入输出流以外还有什么流?
打印流:PrintStream 序列流:SequenceInputStream 管道流:PipedInputStream
8.对象为什么要进行序列化?
将一个具体的对象进行持久化,写入到硬盘上。对象都是在堆内存中的。所以不在对内存上的数据是不能被序列化的。注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。
1.字节流和字符流是怎么区分的?什么时候应该用字节流,什么时候应该用字符流(使用场景)?
2.字节流只能读字节或者字节数组,还有专门以二进制的格式读写java基本类型的DataInputStream 比如他的用法是
double readDouble = dataInputStream.readDouble();
3.如何从文件中以二进制的方式读入数字? 嵌套过滤器来添加多重功能。
4.为什么需要使用缓冲区?如何使用缓冲区?
5.如何对流进行预览?如果不是想要的 可以推回流中。
6. 在存储文本字符串时需要考虑编码,字节流和字符流如何相互的转换?
7. 以二进制的方式处理数据,快速且高效。