Stream File IO流
Stream File IO流
1.0 Stream 简介
Stream流是jdk8新增的一套api,用于操作集合或者数组的数据,Stream流大量集合了Lambda的语法风格,增强
了代码的可读性和简易性
来看一下简单应用
package MyStream;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("1");
list.add("11");
list.add("2");
List<String> list1=list.stream().filter(s->s.startsWith("1")&&s.length()==1).collect(Collectors.toList());
}
}
该代码利用Stream流将List中特定特征的元素收集起来,用collect传递给Collectors中toList()方法创建的ArrayList
中
Stream开发思想分几步
第一步为获取Stream流
第二步为调用Stream中的方法,对数据进行处理计算,比如过滤,排序,去重
最后一步为获取处理到的结果,放到新的集合中
1.1获取Stream流
看下面的程序
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("1");
list.add("11");
list.add("2");
Stream<String> stream =list.stream();
System.out.println(stream.count());
Map<String,Integer> map=new HashMap<>();
stream=map.keySet().stream();
//获取键流
Stream<Integer> stream1=map.values().stream();
//获得值流
Stream<Map.Entry<String,Integer>> stream2=map.entrySet().stream();
//获得entry流
String[] strings={"1","2"};
Stream<String> stream3=Arrays.stream(strings);
Stream<String> stream4=Stream.of(strings);
//两种方法都可以获取数组的Stream流
}
}
1.2 Stream流常见方法
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("1");
list.add("11");
list.add("111");
Stream<String> stream =list.stream();
List list1 =list.stream().filter(s ->s.startsWith("1")).toList();
System.out.println(list1);
list.stream().filter(s ->s.startsWith("1")).forEach(System.out::println);
//遍历过滤后的集合并输出
//list.stream().sorted().forEach(System.out::println);
//排序后输出
List<Student> list2=new ArrayList<>();
list2.add(new Student(22));
list2.add(new Student(21));
list2.add(new Student(20));
list2.stream().sorted((o1, o2) -> o1.getAge()- o2.getAge()).forEach(System.out::println);
//排序重写CompareTo方法或者指定排序规则
}
}
1.3 Stream流的终结方法
终结方法使用完流就结束了,因为终结方法并没有返回值。
max方法用来返回最大值
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<Student> students=new ArrayList<>();
students.add(new Student(11));
students.add(new Student(12));
students.add(new Student(13));
//Optional<Student> opt=students.stream().max((o1, o2) -> o1.getAge()-o2.getAge());
Student maxStudent=null;
//Optional 为了避免产生空指针对象
//不过一般这样写
maxStudent=students.stream().max((o1, o2) -> o1.getAge()-o2.getAge()).get();
System.out.println(maxStudent);
}
}
其中需要对max方法指定具体的比较方式
需要注意的是stream流的数据只能使用一次,不能二次调用,所以stream只用来处理数据,最终的结果还是需要
数组或者集合来保存的
下面看下转换方式
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<Student> students=new ArrayList<>();
students.add(new Student(11));
students.add(new Student(12));
students.add(new Student(13));
List<Student> list1=students.stream().filter(o->o.getAge()==11).toList();
System.out.println(list1);
//list1.add(new Student(44));
//上面的写法也行,不过在java16后才允许使用,而且返回的是不可变list,不能修改其中的值
list1=students.stream().filter(o->o.getAge()==12).collect(Collectors.toList());
list1.add(new Student(22));
//该方法可选返回是可变List还是不可变List
}
}
我们使用两种方式将处理后的数据转换为List,使用第二种方法,collect的底层对stream流的数据进行了遍历,然
后加到Collections的toList方法创建的新的List中
下面这个例子显示了流如果用两次会怎样
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<Student> students=new ArrayList<>();
students.add(new Student(11));
students.add(new Student(12));
students.add(new Student(13));
Stream<Student> stream=students.stream();
List<Student> list1=stream.filter(o->o.getAge()==11).toList();
System.out.println(list1);
Set<Student> set=stream.collect(Collectors.toSet());
//该方法可选返回是可变List还是不可变List
}
}
报错为;Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:260)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:702)
at MyStream.StreamTest.main(StreamTest.java:14)
此时如果要重新操作需要再拿一个Stream流
将结果转化为Map
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<Student> students=new ArrayList<>();
students.add(new Student(11));
students.add(new Student(12));
students.add(new Student(13));
Stream<Student> stream=students.stream();
Map<Integer,Integer> map=stream.limit(3).collect(Collectors.toMap(o1->o1.getAge(),o2->o2.getAge()));
System.out.println(map);
}
}
转化中需要指定键和值的映射
但我们需要注意,MAP集合中键是不能重复的,所以我们在设置映射关系导致键出现了重复则会报错,如果在出现重复键时我们想固定选取某一个,可以再传递一个匿名内部类去表示选择决策
package MyStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.*;
public class StreamTest {
public static void main(String[] args) {
List<Student> students=new ArrayList<>();
students.add(new Student(11));
students.add(new Student(11));
students.add(new Student(13));
Stream<Student> stream=students.stream();
Map<Integer,Integer> map=stream.limit(3).collect(Collectors.toMap(o1->o1.getAge(),o2->o2.getAge(),(v1,v2)->v1));
System.out.println(map);
}
}
这样写就是当出现两个键重复时选第一个
1.4 File 和IO
File是io下的类,File类的对象用于代表当前操作系统的文件
提供了很多对文件进行操作的接口,不过File类只能对文件本身进行操作,不能操作里面的数据
如果我们需要读写文件中的数据,就需要IO流进行操作
1.5 创建File对象
package FileIo;
import java.io.*;
public class Mytest {
public static void main(String[] args) {
//如果使用相对路径,需要注意的是是从文件的根目录而不是代码目录开始
File f1=new File("src/FileIo/text.txt");
System.out.println(f1.length());
//获得大小,如果获得的是文件夹的大小,显示的只是文件夹的大小,不是所有文件总和的大小
}
}
1.6 File的判断方法
package FileIo;
import lombok.Data;
import java.io.*;
import java.time.format.DateTimeFormatter;
public class Mytest {
public static void main(String[] args) {
File f1=new File("src/FileIo/text.txt");
System.out.println(f1.exists());//判断文件是否存在
System.out.println(f1.isFile());//判断打开的是否是文件
System.out.println(f1.isDirectory());//判断打开的是否是文件夹
System.out.println(f1.getName());//获得文件名
System.out.println(f1.length());//获得文件长度
long time = f1.lastModified();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDate = java.time.Instant.ofEpochMilli(time)
.atZone(java.time.ZoneId.systemDefault())
.format(dtf);
System.out.println("Last modified: " + formattedDate);
//获得文件上一次的修改时间
System.out.println(f1.getPath());
//获得创建文件对象时的路径
System.out.println(f1.getAbsolutePath());
//获得绝对路径
}
}
1.7 删除和创建文件
package FileIo;
import lombok.Data;
import java.io.*;
import java.time.format.DateTimeFormatter;
public class Mytest {
public static void main(String[] args) throws IOException {
File f1=new File("src/FileIo/a.txt");
System.out.println(f1.createNewFile());
//创建一个文件
File f2=new File("src/FileIo/myfile/newfile");
//System.out.println(f2.createNewFile());
//由于创建文件夹只能一次创建一级,当前我的路径下FileIo后的文件夹都不存在,需要创建两级所以失败
System.out.println(f2.mkdirs());
//使用mkdirs则可以一次创建多级,不过如果使用mkdir则不行
f2.delete();
f1.delete();//删除两个文件夹和文件
//需要注意的是,只能删除空文件夹或者文件,想要实现一个存在文件的文件夹删除需要使用递归删除
}
}
1.8 遍历File
package FileIo;
import lombok.Data;
import java.io.*;
import java.time.format.DateTimeFormatter;
public class Mytest {
public static void main(String[] args) throws IOException {
File f2=new File("src/FileIo/myfile/newfile");
f2.mkdirs();
File f1=new File("src/FileIo/myfile/newfile/a.txt");
f1.createNewFile();
String[] str=f2.list();//将当前文件对象目录下一级的文件名放在字符串数组中
for(String s:str){
System.out.println(s);
}
File [] files=f2.listFiles();//获得当前文件对象目录下一级文件对象将其放在文件对象数组中
for(File file:files){
System.out.println(file.getAbsolutePath());
}
}
}
注意
1 id是,当主调是文件,或者路径不存在时,我们调用其listFiles()或者list()时会返回NULL
2 当主调为空文件夹时返回一个长度为0的数组,不是null
3 当主调有内容时,正常返回一级路径下的内容
4 即使一级目录下有隐藏文件,我们仍然可以正常读取
5 当主调是一个文件夹,我们没有权限访问,此时会返回null
1.9 递归搜索文件
有了上面的这些知识,我们就可以实现递归搜索文件了
package FileIo;
import java.io.File;
import java.sql.SQLOutput;
import java.util.*;
public class Search {
public static void main(String[] args) {
File dir=new File("F:/TEST");
searchFile(dir,"a.txt");
}
public static void searchFile(File dir,String fileName){
if(dir==null||!dir.exists()|| dir.isFile()){
return ;
}
File[] files=dir.listFiles();
if(files==null||files.length==0){
return ;
}
for(File file:files){
if(file==null){
continue;
}
if(file.getName().equals(fileName)&&file.isFile()){
System.out.println(file.getPath());
continue;
}
if(file.isDirectory()){
searchFile(file,fileName);
}
}
}
}
2.0 字符集的编码和解码
package FileIo;
import java.io.UnsupportedEncodingException;
import java.sql.SQLOutput;
import java.util.Arrays;
public class CharSet {
public static void main(String[] args) throws UnsupportedEncodingException {
String str="你好世界";
byte[] bytes=str.getBytes();
//UTF-8编码下一个汉字三个字节
System.out.println(Arrays.toString(bytes));//默认编码方式为utf-8
System.out.println(new String(bytes));
//默认解码方式,使用utf-8
bytes=str.getBytes("GBK");
System.out.println(Arrays.toString(bytes));
System.out.println(new String(bytes,"GBK"));//指定解码方式
}
}
2.1 IO流简介
I指Input 称为输入流,负责把数据读到内存中去
O值Output ,称为输出流,负责写数据出去
按照流中最小数据单位,IO流可以分为字节流和字符流,其中字节流适合操作所有类型的文件,字符流只适合操
作文本类型的文件
按照上面的分类,IO流总共可以分为下面这四大类,
字节输入流 字节输出流 字符输入流 字符输出流
不同的流进行操作的对象不同,操作的方式也不同,这就是他们的区别
2.2 文件字节输入流
字节输入流的抽象类是 InputStream 对应的实现类为FileInputStream
即文件字节输入流
先看一个简单运用,我们试着一个字节一个字节的读取文件中的内容
package FileIo;
import java.io.*;
public class Finput {
public static void main(String[] args) throws IOException {
// InputStream is =new FileInputStream(new File("src/FileIo/myfile/newfile/a.txt"));
//上面为完整写法
InputStream is = new FileInputStream("src/FileIo/myfile/newfile/a.txt");
/*
* int b1=is.read();//read方法用于读取文件的一个字节,如果读取不到则返回值-1
* System.out.println((char) b1);
* */
int b ;
while ((b=is.read())!=-1) {//注意,赋值语句的返回值是所赋给的值本身
System.out.print((char) b);
}
}
}
但这样写是有问题的,第一是效率很差,第二是无法正确读取汉字
所以我们需要一次读多个字节,
package FileIo;
import java.io.*;
public class Finput {
public static void main(String[] args) throws IOException {
// InputStream is =new FileInputStream(new File("src/FileIo/myfile/newfile/a.txt"));
//上面为完整写法
InputStream is = new FileInputStream("src/FileIo/myfile/newfile/a.txt");
byte[] bytes=new byte[3];//创建一个字节数组
int len=is.read(bytes);//这样会从文件中依次读取字节,直到这个字节数组被填满,或者读取文件的指针指向文件终止符
System.out.println(new String(bytes));
/*
需要注意的是,如果我们使用同一个字节数组进行多次读取操作,每次
读取后进行新一次读取时上一次读取的值是不会自动删除的,只会产生覆盖,所以
对于没有覆盖到的地方,还是上一次读取的值。
*/
//System.out.println(new String(bytes,0,len));//通过指定解码范围来避免出现无效字符
}
}
下面使用循环进行优化
package FileIo;
import java.io.*;
public class Finput {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
byte[] bytes=new byte[3];
int len;
while((len=is.read(bytes))!=-1){
System.out.print(new String(bytes,0,len));
}
}
}
这样就实现了一次读取多个字符,但读取汉字还是存在问题,因为该字节数组定义长度为三,所以任然可能会对汉
字进行分割读取,从而无法识别
如果我们想要正确读取汉字不产生乱码,即解决这种不正确的分割问题,我们可以一次读取全部字节
package FileIo;
import java.io.*;
public class Finput {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
File f=new File("src/FileIo/myfile/newfile/a.txt");
// byte[] bytes=new byte[f.length()]; //这样写不行,因为文件的长度返回的是一个Long
byte[] bytes=new byte[(int) f.length()];//所以如果文件很大的时候,是不能直接这样的
is.read(bytes);
System.out.print(new String(bytes));
}
}
java官方提供一个更简单的写法
package FileIo;
import java.io.*;
public class Finput {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
byte[] bytes=is.readAllBytes();
System.out.print(new String(bytes));
}
}
上面的写法从Jkd9开始支持
从上面我们可以知道,读文本文件使用字节流显得有点麻烦,后面我们使用字符流可以简化这个过程
2.3 文件字节输出流
作用是以内存为基准,把内存中的数据以字节形式写到文件中去
package FileIo;
import java.io.*;
import java.util.Objects;
public class Finput {
public static void main(String[] args) throws IOException {
OutputStream os=new FileOutputStream("src/FileIo/myfile/newfile/a.txt");
os.write('b');
os.write(65);
os.write(66);
/*使用方法1 write(int a) 写入一个字节
该方法只会写入一个字节,超出的部分不写入,
当我们想上面这样写入字节时,写入的内容会被当作ASCII码进行处理
如果只想要写入数字而不是ASCII码,可以像下面这样写。
*/
os.write(String.valueOf(66666666).getBytes(),0,1);//此时写入的就是数字了
byte[] bytes="sss".getBytes();
os.write(bytes);//使用write(byte[] x);直接将字节数组写入
os.write("\r\n".getBytes());
os.write("abc".getBytes(),0,2);
/*
上面的方法指定写入字节数组的哪部分,第二个参数是起点,第三个参数是写入的字节数
*/
}
}
但当前使用的是覆盖管道,写入的内容直接从开头对原来的内容进行覆盖,接下来学习怎么追加
想要实现追加,只需要在一开始文件输出流的时候加一个参数即可
outputStream os=new FileOutputStream("src/FileIo/myfile/newfile/a.txt",true);
但io流管道会消耗内存资源和系统资源,所以我们需要进行关闭操作,而且如果数据还在缓冲区中我们不关闭管道
直接结束程序,就会出现数据并未写入的情况,所以关闭管道很重要
2.4 文件复制
让我们写一个简单使用,来实现文件复制功能
package FileIo;
import java.io.*;
import java.util.Objects;
public class Finput {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
OutputStream os=new FileOutputStream("src/FileIo/myfile/newfile/b.txt");
byte[] bytes=is.readAllBytes();
os.write(bytes);
is.close();
os.close();
}
}
这样写是通过一个字节数组将整个文件装进来,但可能会出现文件太大的情况,这时候就要优化一下写法,分多次
装入
package FileIo;
import java.io.*;
import java.util.Objects;
public class Finput {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
OutputStream os=new FileOutputStream("src/FileIo/myfile/newfile/b.txt",true);
byte[] bytes=new byte[1024];
int len;
while((len=is.read(bytes))!=-1){
os.write(bytes,0,len);
}
is.close();
os.close();
}
}
这种方法也是先了该功能,但需要注意的是,当我们创建字节输出文件流时,当地址中的文件不存在时,会自动
创建文件,不过要是当输入地址中的文件夹也不存在,则会抛出一个错误
2.5 安全的管道关闭方式
package FileIo;
import java.io.*;
import java.util.Objects;
public class Finput {
public static void main(String[] args) {
InputStream is=null;
OutputStream os=null;
try {
is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
os=new FileOutputStream("src/FileIo/myfile/newfile/b.txt");
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
try {
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
我们发现我们这样写是安全了,不过太麻烦了,所以从jdk7中,提供了更简单的资源释放方法
即 try-with-resource
其形式为:
try(定义资源1;定义自愿2;.....){
可能出现异常的代码;
}
catch(异常类名 变量名){
异常处理代码;
}
其中()中只能方资源,要不然报错,资源的定义是继承了AutoCloseable接口
当资源使用结束后,会自动运行重写的close法
对于上面的方法,我们简化后为:
package FileIo;
import java.io.*;
import java.util.Objects;
public class Finput {
public static void main(String[] args) {
try (InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
OutputStream os=new FileOutputStream("src/FileIo/myfile/newfile/b.txt");){
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
该技术自jdk7开始可以使用
2.6 文件字符输入流(FileReader)
就是以字符的形式将硬盘中的数据读到主存中,
package FileIo.char_stream;
import java.nio.file.Paths;
import java.io.*;
public class MyFileReader {
public static void main(String[] args) throws Exception {
Reader fr = new FileReader("src/FileIo/myfile/newfile/a.txt");
int c1= fr.read();
System.out.println(c1);
int c2= fr.read();
System.out.println(c2);
}
}
其中read方法返回的是读取的字符对应的十进制数,如果是英文字符或者数字等可以被ascii码编码的返回的就是
ascii码,其余返回其存储的二进制数对应的十进制。
package FileIo.char_stream;
import java.nio.file.Paths;
import java.io.*;
public class MyFileReader {
public static void main(String[] args) throws Exception {
Reader fr = new FileReader("src/FileIo/myfile/newfile/a.txt");
int c;
while((c=fr.read())!=-1){
char ch=(char)c;
System.out.print(ch);
}
}
}
这样可以读取文本中的所有内容并输出,但性能较差
我们对其进行更改;
package FileIo.char_stream;
import java.nio.file.Paths;
import java.io.*;
public class MyFileReader {
public static void main(String[] args) throws Exception {
try(Reader fr = new FileReader("src/FileIo/myfile/newfile/a.txt")){
char[] buffer=new char[1024];
int len;
while((len=fr.read(buffer))!=-1){
String rs=new String(buffer,0,len);
System.out.print(rs);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
如果我们用字符数组去存储读到的结果,read返回的结果是读到的字符长度
此时的读取方式可以避免乱码,读取后会自动关闭管道,性能优秀
2.7 文件字符输出流FileWriter
package FileIo.char_stream;
import java.io.*;
public class MyFileWriter {
public static void main(String[] args) {
try (Writer writer=new FileWriter("src/FileIo/myfile/newfile/a.txt",true)){
writer.write(49);
writer.write("\r\n");
writer.write("aaaa");
writer.write("abcd",0,2);//限定写入的范围
}
catch (Exception e){
e.printStackTrace();
}
}
}
2.8字节缓冲流
我们先前学习的FileInputStream FileReader因为其性能不够强大,被称为原始流或者低级流
而对其进行优化的缓冲流被称为包装流,或者叫处理流
其优化原理就是在主存开设输入缓冲区和输出缓冲区,减少主存和外部存储的数据交换次数,从而达到性能优化的
作用
package FileIo.char_stream;
import java.io.*;
import java.util.Objects;
public class Mybufferin{
public static void main(String[] args) {
try (InputStream is=new FileInputStream("src/FileIo/myfile/newfile/a.txt");
InputStream bi =new BufferedInputStream(is);
OutputStream os=new FileOutputStream("src/FileIo/myfile/newfile/b.txt");
OutputStream bo =new BufferedOutputStream(os);){
byte[] bytes = new byte[1024];
int len;
while ((len = bi.read(bytes)) != -1) {
os.write(bytes,0,len);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
字节流的缓冲池大小为8KB ,字符流的缓冲流大小为8K字符
对于字符流也是一样,将原始流包起来就可以使用,功能上没有变化,只是性能上的提升
但对于缓冲字符输入流,新加了按行读取的方法readLine
对于缓冲字符输出流,新加了换行的方法newLine
注意,由于使用了子类的新方法,所以我们在这里不应该使用多态。
package FileIo.char_stream;
import java.nio.file.Paths;
import java.io.*;
public class Mybufferin {
public static void main(String[] args) throws Exception {
try(Reader fr = new FileReader("src/FileIo/myfile/newfile/a.txt");
BufferedReader bfr=new BufferedReader(fr);
Writer ou=new FileWriter("src/FileIo/myfile/newfile/b.txt");
BufferedWriter bou =new BufferedWriter(ou);){
String line=null;
while ((line=bfr.readLine())!=null){
bou.write(line);
bou.newLine();
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
2.9 字符输入转换流
当我们读入的字符编码方式和我们java选择的编码方式不同时,读取的字符就会乱码,这时候就需要使用字符输入
转换流, 即InputStreamReader
该字符输入转换流的解决思路为:先获取文件的原始字节流,再将其按真是的字符集编码转换成字符输入流
package FileIo.char_stream;
import java.io.*;
public class MyInputStreamReader {
public static void main(String[] args) {
try(
InputStream is= new FileInputStream("src/FileIo/myfile/newfile/a.txt");
Reader isr= new InputStreamReader(is,"GBK");
BufferedReader bis =new BufferedReader(isr);
){
String line ="";
while((line=bis.readLine())!=null){
System.out.println(line);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
其中InputStreamReader是一个字符输入流,但并不是我们先前的FileReader
3.0 字符输出转换流
同样,字符输出转换流可以指定输出编码方式
package FileIo.char_stream;
import java.io.*;
public class MyInputStreamReader {
public static void main(String[] args) {
try(
InputStream is= new FileInputStream("src/FileIo/myfile/newfile/a.txt");
Reader isr= new InputStreamReader(is,"GBK");
BufferedReader bis =new BufferedReader(isr);
OutputStream ou=new FileOutputStream("src/FileIo/myfile/newfile/b.txt");
Writer our=new OutputStreamWriter(ou,"GBK");
BufferedWriter bou=new BufferedWriter(our);
){
String line ="";
while((line=bis.readLine())!=null){
bou.write(line);
bou.newLine();
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
3.1 打印流
为了简化输出,java中还给出了打印流,打印流分为字节打印流PrintStream和字符打印流PrintWriter两种
使用打印流非常方便和高效,所以用打印流更方便一点,也是官方更推荐的io方法,相比我们之前的缓冲流简便不
少
package FileIo.char_stream;
import java.io.PrintStream;
public class MyPrint {
public static void main(String[] args) {
try(
PrintStream printStream=new PrintStream("src/FileIo/myfile/newfile/b.txt");
){
printStream.println("你好锕");
}
catch (Exception e){
e.printStackTrace();
}
}
}
需要注意,字节打印流只会选择系统默认的编码方式,而字符打印流更便捷一点,可以选择任意的编码方式
但默认使用打印流是直接对原文件进行覆盖,而不是进行追加,想要进行追加需要将其包装到低级管道中。
package FileIo.char_stream;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class MyPrint {
public static void main(String[] args) {
try(
PrintStream printStream=new PrintStream(new FileOutputStream("src/FileIo/myfile/newfile/b.txt",true));
){
printStream.println(true);
}
catch (Exception e){
e.printStackTrace();
}
}
}
但注意,想要将低级管道转换到字节打印流只能选择字节流进行转换,而不能选择字符流
使用打印流还可以实现输出重定向功能
正常我们输出是输出给控制台,我们现在想要控制输出的对象,可以这样操作
package FileIo.char_stream;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class MyPrint {
public static void main(String[] args) {
try(
PrintStream printStream=new PrintStream(new FileOutputStream("src/FileIo/myfile/newfile/b.txt",true));
){
System.setOut(printStream);
System.out.println("你好你好");
}
catch (Exception e){
e.printStackTrace();
}
}
}
这样我们所有的System.out.println();都写到了我们指定的文件中
3.2 特殊数据流
这两个流允许将数据和类型一并写出去,分为特殊数据输入流和特殊数据输出流,这两个流都是字符流
通过特殊数据输出流写入的数据不是采用utf-8也不是采取gbk编码,其目的并不是给用户看所以无法正确解码,使
用特殊数据输入流可以读入我们先前写入的数据,注意,我们写入的顺序要和我们读入的顺序相同,不能跳着读
下面是一个例子:
package FileIo.char_stream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class DataStream {
public static void main(String[] args) {
try(
DataOutputStream ou=new DataOutputStream(new FileOutputStream("src/FileIo/myfile/newfile/b.txt"));
DataInputStream in =new DataInputStream(new FileInputStream("src/FileIo/myfile/newfile/b.txt"));
){
ou.write(1);
ou.writeBoolean(true);
ou.writeDouble(1.2);
ou.writeByte(69);
ou.writeUTF("你好");
int i=in.read();
System.out.println(i);
Boolean b=in.readBoolean();
System.out.println(b);
Double c=in.readDouble();
System.out.println(c);
Byte d=in.readByte();
System.out.println(d);
String s=in.readUTF();
System.out.println(s);
}
catch (Exception e){
e.printStackTrace();
}
}
}
3.3 序列化
java中肯定也有序列化和反序列化操作,序列化就是将java对象变成数据形式存储下来,反序列化就是读取对象序
列化后的数据形成对象
反序列化流即,ObjectInputStream继承于字节输入流,序列化流ObjectOutputStream 继承于字节输出流
我们这里给出一个学生类,将对此类的对象进行序列化和反序列化操作
package FileIo.char_stream;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NewStudent implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String email;
}
注意,想要实现序列化和反序列化,被操作的类必须继承序列化接口,也就是上面的Serializable
虽然这个接口里什么都没有,是一个空接口,但作为标志告诉jvm虚拟机需要用序列化的形式存储
下面是反序列化和序列化操作:
package FileIo.char_stream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
public class ObjectStream {
public static void main(String[] args) {
try(ObjectOutputStream ou=new ObjectOutputStream(new FileOutputStream("src/FileIo/myfile/newfile/b.txt"));
ObjectInputStream in=new ObjectInputStream(new FileInputStream("src/FileIo/myfile/newfile/b.txt"));
){
NewStudent student =new NewStudent("BOB",12,"28878989@QQ.COM");
ou.writeObject(student);
NewStudent student1=(NewStudent) in.readObject();
System.out.println(student1.getName());
}
catch (Exception e){
e.printStackTrace();
}
}
}
对于我们的一些敏感属性,比如密码,如果序列化后文件泄露则很容易暴露出去,我们控制其不进行序列化,其实
也很简单,用transient修饰一下我们想要不序列化的属性即可,这样在序列化时不会将此属性进行序列化,反序
列化后该属性为null
例如:
private transient String email;
如果我们有多个对象需要序列化,我们直接将对象放在ArrayList里面,对ArrayList对象直接进行序列化即可。
3.4 使用io框架进行文件操作
我们io操作如果还需要这样慢慢写未免也太麻烦了,所以可以使用已经写好的io框架进行操作,这里使用
conmmons-io
package FileIo.char_stream;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
public class ioframe {
public static void main(String[] args) {
try {
FileUtils.copyFile(new File("src/FileIo/myfile/newfile/a.txt"),new File("src/FileIo/myfile/newfile/c.txt"));
// FileUtils.copyDirectory(new File(""),new File("")); 拷贝文件夹
// FileUtils.deleteDirectory(); 删除文件夹
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
3.5 复制文件夹和删除文件夹
虽然FileUtils中提供了直接操作文件夹的方法,但我们这里不使用,自己写的练下手
复制文件夹:
package FileIo.char_stream;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
public class CopyDir {
public static void main(String[] args) throws IOException {
copyDir(new File("D:\\java\\code\\test1"),new File("D:\\java\\code\\test2"));
}
public static void copyDir(File sfile,File dfile) throws IOException {
if(sfile==null||dfile==null||!sfile.exists()||sfile.isFile()||dfile.isFile()||!dfile.exists()){
return ;
}
File deNameDir=new File(dfile,sfile.getName());
deNameDir.mkdirs();
File [] files=sfile.listFiles();
if(files==null||files.length==0){
return ;
}
for (File file : files) {
if(file.isFile()){
FileUtils.copyFile(file,new File(deNameDir,file.getName()));
}
else {
copyDir(file,deNameDir);
}
}
}
}
删除文件夹
package FileIo.char_stream;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
public class DeleteDir {
public static void main(String[] args) throws IOException {
deleteDir(new File("D:\\java\\code\\test2"));
}
public static void deleteDir(File sfile) throws IOException {
if(!sfile.exists())
return ;
File[] filelist=sfile.listFiles();
if(filelist.length==0||filelist==null){
FileUtils.delete(sfile);
return;
}
for(File file : filelist){
if(file.isFile()){
FileUtils.delete(file);
}
else{
deleteDir(file);
FileUtils.delete(file);
}
}
FileUtils.delete(sfile);
}
}
到这里java的stream 和io知识点就结束了
完结撒花

浙公网安备 33010602011771号