Java:File、字符集、IO流、缓冲流、转换流、打印流、数据流、序列化流
文章目录
1. File 类
File 是Java.io包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件或文件夹)。
File类只能对文件本身进行操作(比如获取文件大小、文件名、修改时间,或者创建文件/文件夹,删除文件/文件夹),不能读写文件里面存储的数据。如果要读写数据,需要使用到Java IO流。
2. 字符集
- ascii编码中字符a的ascii编码为97,标准的ASCII使用1个字节存储一个字符;
- gbk编码(汉字编码字符集,gbk中一个中文字符编码成两个字节的形式存储,GBK兼容ASCII字符集);
- utf-8编码(unicode字符集的一种编码方式,采取可变长编码方案,英文、数字字符只占用一个字节,汉字字符占用3个字节);
使用gbk编码方式,只占用7个字节。
package File_study;
import java.util.Arrays;
public class File_study {
public static void main(String[] args) throws Exception {
// 编码
String _str = "我a你";
byte[] bytes = _str.getBytes();
byte[] bytes1 = _str.getBytes("gbk");
System.out.println(Arrays.toString(bytes));
// utf-8 编码中上述字符串占用7个字节
System.out.println(Arrays.toString(bytes1));
// gbk 编码中上述字符串占用5个字节
// 解码
String string = new String(bytes);
System.out.println(string);
String s = new String(bytes1);
System.out.println(s);
// 编码使用的是utf-8,而解码使用的是gbk,因此出现了乱码
}
}
运行结果:
3. IO流
- 按流的方向分为输入流、输出流;
- 按流中数据的最小单位,分为字节流、字符流,其中字节流适合操作所有类型的文件,而字符流只适合操作纯文本文件;
因此常用的有字符输入流(Reader)、字符输出流(Writer)、字节输入流(InputStream)、字节输出流(OutputStream),但上述指定的都是抽象类,它们的具体实现类分别为FileReader、FileWriter、FileInputStream、FileOutputStream。
3-1. FileInputStream(文件字节输入流)
用于从文件中读取字节,文件字节输入流常用的构造方法有如下图中的前两种,可以是文件路径或者File对象都可以。
通过文件字节输入流的read方法读取文件数据。
- read 无参数表示每次读取一个字节,返回值类型为int,读取到最后返回值为-1;
- read 一个参数时表示把读取的字节数组传给参数,它的返回值表示此次读取的字节长度,读取到最后返回值为-1。
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) throws IOException {
File file = new File("1/1.txt");
// 文件里边的数据为:我a你,使用的编码方式为utf-8
InputStream inputStream = new FileInputStream(file);
int a;
while((a = inputStream.read())!=-1){
System.out.println(a);
}
inputStream.close();
}
}
运行结果:
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) throws IOException {
File file = new File("1/1.txt");
// 文件里边的数据为:我a你,使用的编码方式为utf-8
InputStream inputStream = new FileInputStream(file);
byte[] bytes1 = new byte[3];
int len;
while((len = inputStream.read(bytes1))!=-1){
System.out.println(new String(bytes1,0,len));
}
inputStream.close();
}
}
为了避免读取的汉字不乱码,可以定义一个长度和文件大小一致的字节数组,这样就可以读取所有的字节了。
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) throws IOException {
File file = new File("1/1.txt");
long len = file.length();
// 文件里边的数据为:我a你,使用的编码方式为utf-8
InputStream inputStream = new FileInputStream(file);
byte[] bytes1 = new byte[(int) len];
int read = inputStream.read(bytes1);
System.out.println(read+" "+new String(bytes1));
}
}
3-2. FileOutputStream(文件字节输出流)
用于把字节数据写入到文件中去。常用的构造方法也有两种,一种是直接写文件名,另外一种则是写File对象。
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) throws IOException {
FileOutputStream os = new FileOutputStream("1.txt");
os.write(97);
os.write("我a你".getBytes());
os.close();
}
}
写入方式是会把原来的数据覆盖掉,如果想在文件后面追加数据,可以直接在文件字节输出流后面加上参数true即可,如下:
FileOutputStream os = new FileOutputStream("1.txt",true);
3-3. 通过FileInputoutStream、FileOutputStream实现文件的复制
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) throws IOException {
// 实现文件的复制
InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");
OutputStream os = new FileOutputStream("1.jpg",true);
byte[] bytes = new byte[1024*1024];
int len;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
os.close();
is.close();
}
}
3-3-1. 通过try-catch-finally来释放资源
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
// 实现文件的复制
try{
is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");
os = new FileOutputStream("2.jpg",true);
byte[] bytes = new byte[1024*1024];
int len;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if(os != null)
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3-3-2. 通过try-catch-resource来释放资源
通过这种方式,把需要释放的资源放到try()小括号里边,这样代码运行完之后就会自动释放。
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) {
// 实现文件的复制
try(
InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");
OutputStream os = new FileOutputStream("3.jpg",true);
){
byte[] bytes = new byte[1024*1024];
int len;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
这里根本看不出它是否释放了资源,通过查询FileInputStream继承关系看看,发现最终它的close方法最终来自于接口AutoCloseable下的close方法。
为此我们可以定义一个类,让其实现AutoCloseable接口下的close方法来进行演示,看在小括号中这个自定义的类对象是否执行了close方法,如下:
package File_study;
public class CloseTest implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("在这里释放资源");
}
}
package File_study;
import java.io.*;
public class File_study2 {
public static void main(String[] args) {
// 实现文件的复制
try(
InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");
OutputStream os = new FileOutputStream("3.jpg",true);
CloseTest closeTest = new CloseTest();
){
byte[] bytes = new byte[1024*1024];
int len;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
System.out.println(closeTest);
}catch (Exception e){
e.printStackTrace();
}
}
}
运行结果:
4. FileReader(文件字符输入流)
从文件中读取字符出来,常用的构造方法两种,一种写文件存储路径,第二种参数为File对象。
package File_study;
import java.io.*;
public class File_study3 {
public static void main(String[] args) {
try(
Reader r = new FileReader("src/File_study/File_study2.java");
){
int c;
while((c= r.read())!=-1){
System.out.print((char) c);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
关于读取方法read,常用的有如下两种:
上述读取效率较低,为此,可以使用字符数组来存储读取到的字符。
package File_study;
import java.io.*;
public class File_study3 {
public static void main(String[] args) {
try(
Reader r = new FileReader("src/File_study/File_study2.java");
){
int len;
char[] chars = new char[10];
while((len= r.read(chars))!=-1){
System.out.print(new String(chars,0,len));
// 读取多少,输出多少
}
}catch (IOException e){
e.printStackTrace();
}
}
}
5. FileWriter(文件字符输出流)
把字符写入到文件中去。
常用的写入字符到文件中去的方法writer有如下几种:
package File_study;
import java.io.*;
public class File_study3 {
public static void main(String[] args) {
try(
Writer w = new FileWriter("src/File_study/1.txt");
){
w.write('我');
// 写入单个字符
w.write("\r\n");
w.write("我a你");
// 写入字符串
w.write("\r\n");
char[] chars = {'我','a','你'};
w.write(chars);
// 写入字符数组
w.write("\r\n");
w.write("我a你",1,1);
// 写入字符串 “a”
w.write("\r\n");
w.write(chars,1,1);
// 写入字符数组 'a'
}catch (IOException e){
e.printStackTrace();
}
}
}
需要注意的是:字符输出流写出数据后,必须刷新流,或者关闭流,否则不能生效。 这是因为文件字符输入流它执行writer方法后,会先把数据先写入到缓冲区中去,然后再经过系统调用写入到文件中。所以在没有执行close方法或flush方法时,数据是没有写入到文件中去的,在执行close方法时会调用flush方法。
6. 缓冲流
对上述中的一些的原始流(文件字节输入流、文件字节输出流、文件字符输入流、文件字符输出流)进行了包装,以提高原始流的读写数据的性能。
6-1. 字节缓冲流
提高字节流读写的性能。原理:字节缓冲输入流自带8kb缓冲池;字节缓冲输出流也自带了8kb的缓冲池。把上述那个复制文件的代码修改为使用字节缓冲流的形式,如下:
package File_study;
import java.io.*;
public class File_study4 {
public static void main(String[] args) {
// 实现文件的复制
try(
InputStream is = new FileInputStream("C:\\Users\\Administrator\\Pictures\\5.jpg");
InputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream("5.jpg",true);
OutputStream bos = new BufferedOutputStream(os);
){
byte[] bytes = new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
查看字节缓冲流的源码可以看到的确有8kb的缓冲池,如下:
如果想设置缓冲池的大小,直接在字符缓冲流后加上参数即可。
InputStream bis = new BufferedInputStream(is,8192*2);
OutputStream bos = new BufferedOutputStream(os,8192*2);
6-2. 字符缓冲输入流
自带8kb(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能。
package File_study;
import java.io.*;
public class File_study5 {
public static void main(String[] args) {
try(
Reader r = new FileReader("src/File_study/File_study3.java");
BufferedReader br = new BufferedReader(r)
){
int len;
char[] chars = new char[100];
while((len=br.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
}catch (IOException e){
e.printStackTrace();
}
}
}
按照行来读取数据,使用方法readLine,如下:
package File_study;
import java.io.*;
public class File_study5 {
public static void main(String[] args) {
try(
Reader r = new FileReader("src/File_study/File_study3.java");
BufferedReader br = new BufferedReader(r)
){
String outcome;
while((outcome=br.readLine())!=null){
System.out.println(outcome);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
6-3. 字符缓冲输出流
自带8kb(8192)的字符缓冲池,可以提高字符输出流写字符数据的性能。
package File_study;
import java.io.*;
public class File_study6 {
public static void main(String[] args) {
try(
Writer os = new FileWriter("3.txt");
BufferedWriter bos = new BufferedWriter(os);
){
bos.write("爱我中华。。");
bos.newLine();
// 换行
bos.write('爱');
}catch(IOException e){
e.printStackTrace();
}
}
}
7. 测试复制文件代码的性能
这里通过四种方式来测试,分别为使用一个一个字节来实现文件的复制、使用字节数组来实现文件的复制、使用缓冲流并一个一个字节来实现文件的复制、使用缓冲流并字节数组来实现文件的复制,参考代码如下:
package File_study;
import java.io.*;
public class File_study7 {
private static final String FILE_PATH = "E:/Download/2.png";
private static final String DIR = "E:/Download/";
public static void main(String[] args) {
copy1(FILE_PATH,DIR);
copy2(FILE_PATH,DIR);
copy3(FILE_PATH,DIR);
copy4(FILE_PATH,DIR);
}
public static void copy1(String file_path,String DIR){
long start = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(file_path);
OutputStream os = new FileOutputStream(DIR+"3.png");
){
int c;
while((c=is.read())!=-1){
os.write(c);
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("通过一个一个字节来复制文件花费的时间为:"+(end-start)/1000.0+"s");
}
public static void copy2(String file_path,String DIR){
long start = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(file_path);
OutputStream os = new FileOutputStream(DIR+"4.png");
){
byte[] bytes = new byte[1024];
int len;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("通过字节数组来复制文件花费的时间为:"+(end-start)/1000.0+"s");
}
public static void copy3(String file_path,String DIR){
long start = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(file_path);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream(DIR+"5.png");
BufferedOutputStream bos = new BufferedOutputStream(os);
){
int c;
while((c=bis.read())!=-1){
bos.write(c);
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("通过缓冲字符流、一个一个字节来复制文件花费的时间为:"+(end-start)/1000.0+"s");
}
public static void copy4(String file_path,String DIR){
long start = System.currentTimeMillis();
try(
InputStream is = new FileInputStream(file_path);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = new FileOutputStream(DIR+"6.png");
BufferedOutputStream bos = new BufferedOutputStream(os);
){
byte[] bytes = new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("通过缓冲流和字节数组来复制文件花费的时间为:"+(end-start)/1000.0+"s");
}
}
运行结果如下:
这里没有运行copy1,效率太低了。如果把copy2中字节数组大小修改为1024*8(或者更大吧!),其他的不变,只运行copy2和copy4,可以发现它们两者的运行效率基本上差不了多少。
8. 转换流
用于解决不同编码读写时的乱码情况,这里有一段代码如下:
package File_study;
import java.io.* ;
public class File_study8 {
public static void main(String[] args) {
try(
Reader r = new FileReader("3.txt");
BufferedReader br = new BufferedReader(r)
){
String line;
while((line=br.readLine())!=null){
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
此时出现了乱码,因为这个文件的编码为gbk,而代码运行文件编码为utf-8。
8-1. 字符输入转换流(InputStreamReader)
解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转换成字符输入流,这样字符输入流中的字符就不乱码了。
package File_study;
import java.io.* ;
public class File_study8 {
public static void main(String[] args) {
try(
InputStream fis = new FileInputStream("3.txt");
Reader r = new InputStreamReader(fis,"gbk");
BufferedReader bfr = new BufferedReader(r);
){
String line;
while((line= bfr.readLine())!=null){
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
8-2. 字符输出转换流(OutputStreamWriter)
控制写出去的字符使用特定的字符集编码。
解决思路:先获取字节输出流,再将其按指定的字符集编码转换成字符输出流,之后写出去的字符就会使用该字符集编码了。
package File_study;
import java.io.*;
public class File_study9 {
public static void main(String[] args) {
try (
OutputStream fos = new FileOutputStream("4.txt");
Writer osw = new OutputStreamWriter(fos,"gbk");
BufferedWriter bw = new BufferedWriter(osw);
){
bw.write("爱我中华");
bw.write("我爱中国。。");
} catch (IOException e) {
e.printStackTrace();
}
}
}
9. 打印流(PrintStream/PrintWriter)
作用:更方便、更高效的把数据打印出去,能够实现打印啥出去就是啥出去
9-1.PrintStream
package File_study;
import java.io.*;
public class File_study10 {
public static void main(String[] args) {
try(
PrintStream ps = new PrintStream("printStream.txt", "gbk");
){
ps.println("爱我中华");
ps.print("1");
ps.println();
ps.print("我爱中国");
}catch (IOException e){
e.printStackTrace();
}
}
}
9-2. PrintWriter
package File_study;
import java.io.*;
public class File_study10 {
public static void main(String[] args) {
try(
PrintWriter pw = new PrintWriter("printWriter.txt");
){
pw.println("爱我中华");
pw.println("1");
}catch (IOException e){
e.printStackTrace();
}
}
}
9-3. 关于打印流的高效
直接查看源码,可以发现在它们的下面使用了缓冲流。
PrintStream
PrintWriter
9-4. 把系统默认的打印流修改为自定义的打印流
系统运行结果都会打印到控制台上,如果想把打印的数据写入的文件中去,可以使用打印流,如下:
package File_study;
import java.io.*;
public class File_study10 {
public static void main(String[] args) {
System.out.println("我爱中国!");
try(
PrintStream ps = new PrintStream("printstream1.txt");
){
System.setOut(ps);
System.out.println("爱我中华");
}catch (IOException e){
e.printStackTrace();
}
}
}
10. 数据流
允许把数据和其类型一并写出去。
10-1. 数据输出流(DataOutputStream)
package File_study;
import java.io.*;
public class File_study11 {
public static void main(String[] args) {
try(
OutputStream os = new FileOutputStream("dataoutputstream.txt");
DataOutputStream dos = new DataOutputStream(os);
){
dos.write(1);
dos.writeFloat(0.1f);
dos.writeBoolean(false);
dos.writeUTF("爱我中华2");
}catch (IOException e){
e.printStackTrace();
}
}
}
10-2. 数据输入流(DataInputStream)
package File_study;
import java.io.*;
public class File_study11 {
public static void main(String[] args) {
try(
InputStream is = new FileInputStream("dataoutputstream.txt");
DataInputStream dis = new DataInputStream(is);
){
int i = dis.read();
float v = dis.readFloat();
boolean b = dis.readBoolean();
String s = dis.readUTF();
System.out.println(i+" "+v+" "+b+" "+s);
}catch (IOException e){
e.printStackTrace();
}
}
}
数据是怎样写入的,就怎么读取出来。
11. 序列化流
11-1. 对象字节输出流 (ObjectOutputStream)
Java对象进行序列化:把Java对象存入到文件中去。
把对象序列化,需要把对象对应的类实现Serializable这个接口。
package File_study.enity;
import java.io.Serializable;
public class User implements Serializable {
private String name;
private Integer age;
private char sex;
public User(String name, Integer age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
package File_study;
import File_study.enity.User;
import java.io.*;
public class File_study12 {
public static void main(String[] args) {
try(
OutputStream fos = new FileOutputStream("object.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
){
User user = new User("张三", 20, 'm');
oos.writeObject(user);
}catch (IOException e){
e.printStackTrace();
}
}
}
11-2. 对象字节输入流 (ObjectInputStream)
Java对象反序列化:从文件中读取Java对象。
package File_study;
import File_study.enity.User;
import java.io.*;
public class File_study12 {
public static void main(String[] args) {
try(
InputStream fis = new FileInputStream("object.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
){
User o = (User) ois.readObject();
System.out.println(o);
}catch (Exception e){
e.printStackTrace();
}
}
}
原文地址:https://blog.csdn.net/qq_45404396/article/details/144006098
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!