io是input 和output,输入和输出,在java中分为输入流和输出流
也可以根据数据的处理方式分为字节流,字符流
java的io流的40 多个类,是从以下的四个抽象类基类中抽象出来的
inputstream /reader,前者字节输入,后者字符输入
outputstream/writer
inputstream 字节输入流:用于从源头通常是文件 读取数据到内存中,是所有字节输入流的父类
read():返回输入流中的下一个字节数据
read(byte b[]) 从输入流中读取一些字节存储到数组b中,如果数组长度为0 则不读取,返回-1,如果有,则最多读取b.length,返回读取的字节数
Fileoutputstream:
字节输出流的细节:参数可以是字符串表示的路径或者file对象,如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的,如果文件已经存在会清空文件
写数据:write 方法的参数是整数,实际上写到本地文件中的整数是ascii上对应的字符
释放资源,每次使用完都要释放
字节输出流写出数据的三种方式: 一次写一个数据 write(int b),write(byte[]b) ,write (byte []b ,int off,int len)
换行写怎么写:
在俩个write中间写一个换行符,windows操作系统\r\n 换行 linux \n ios \r
不想清空原来的怎么写,续写:打开续写功能,在new创建的时候写上true,默认是false
fileinputstream:
read 表述读取数据,读取一次数据就移动一次指针
创建对象:如果文件不存在 会直接报错,因为数据很重要
读取数据:如果读不到会返回-1,一次读一个字节,读出来的是ascii上对应的数字,读到空格会对应ascii上的32,读取到-1的时候会分开先读负数号
释放资源
循环读取:循环的条件是不为-1 ,要用一个数据对象去接受,因为read 读取一次就移动一次指针
public class fileinputstream {
public static void main(String[] args) throws IOException {
FileInputStream fi = new FileInputStream("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\q");
int a;
while ((a = fi.read()) !=-1){
System.out.println((char)a);
}
}
}
文件拷贝:边读边写
public class demo1 {
public static void main(String[] args) throws IOException {
FileInputStream fio= new FileInputStream("C:\\Users\\77918\\Desktop\\file.txt");
FileOutputStream fos = new FileOutputStream ("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\file.txt");
int b;
while ((b=fio.read())!=-1){
fos.write((char)b);
}
fio.close();
fos.close();
}
}
文件拷贝的弊端: 一次读一个数据,缓慢
Fileinputstream :一次读多个字节,read(byte [] buffer )一次读一个字节数组数据,1024 的整数倍,会尽可能把数组读满
IO流中不同的jdk版本捕获异常:jdk7 java提出一个接口 可以在特定情况下,可以自动释放资源
字节流读取文件时,文件中不要有中文:
字符集详解:
计算机的存储规则:
以二进制来存储
ascii 存储英文 一个字节(一个字节八位,编码,存储,不够补0)
gbk:字符集,ansi,windows默认 ansi,完全兼容ascii,中文俩个字节,高位字节二进制一定是以1 开头,转成十进制一定是负数,为了和英文区分开,英文一个 第一位是0
unicode:国际标准字符集
utf-16:2-4个字节保存
utf-32:固定四个字节baocun
utf-8:用1-4个字节保存 中文三个字节表示,英文一个字节表示,不是字符集是一种编码方式
为什么会有乱码:
原因一:读取数据时未读完整个汉字,字节流一次只能读一个字节,原因二:编码和解码的方式不统一
扩展: 字节流读取会乱码,但是拷贝不会乱码的原因,拷贝时把字节拷贝过去,数据没有丢失,读取时也是保持一致所以不会乱码
java中编码和解码的方法:
字符流:底层字节流,字节流+字符集,输入流一次读一个字节,遇到中文时一次读多个字节
输出流:底层会把数据按照指定的编码方式编码,变成字节再写道文件中
使用场景 一般用于纯文本文件进行读写操作
Reader:字符输入流 filereader 有参read(),空参reader()
writer:字符输出流 filewriter
细节:读到文件末尾,read返回-1,需要资源关闭
read()细节:读取后会进行解码转成十进制,十进制对应的时字符集里的数字
字符流原理解析:
创建字符输入流对象:底层关联文件,创建缓冲区
读取数据:
判断缓冲区是否有数据可以读取,有则直接读取,无则读取文件,每次尽可能装满缓冲区,如果文件中也没有数据,则返回-1,
空参的read()方法,读取,解码成十进制 返回
有参的read()方法:读取字节,转码,强转
flush 和close
字节流和字符流的使用场景
字节流:拷贝任意类型的文件
字符流:读取纯文本文件中的数据,往纯文本文件中写出数据
练习1 拷贝文件
public class demo2 {
//拷贝文件夹
//参数一:数据源
//参数二:目的地
public static void main(String[] args) throws IOException {
//
//1.创建对象表示数据源
File src = new File("C:\\Users\\77918\\Desktop\\task");
//创建目的源对象
File dest = new File("C:\\Users\\77918\\Desktop\\task\\bb");
//调用方法开始拷贝
copydir (src,dest);
}
// 作用 拷贝文件夹
private static void copydir(File src, File dest) throws IOException {
dest.mkdirs();//以防没有这个文件夹
// 递归: 进入数据源,遍历数组,文件和文件夹处理的判断,文件拷贝,文件夹递归
File []files =src.listFiles();
//遍历数组
for (File file :files){
if(file.isFile()){
//拷贝文件夹,从文件到文件
FileInputStream fis= new FileInputStream(file);
FileOutputStream fos=new FileOutputStream(new File(dest,file.getName()) );
byte []bytes =new byte[1024];
int len;
while ((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}
else{
//判断文件夹,递归
copydir(file,new File(dest,file.getName()));
}
}
}
}
练习2:文件加密 为了保证文件的安全性,需要对原始文件进行加密存储,使用时在对其进行解密处理
加密原理:
对原始文件中的每一个字节数据进行更改,将更改后的数据存储到新文件夹中
解密原理:
读取加密之后的文件,按照加密的规则反向操作,变成原始文件
public class demo3 {
public static void main(String[] args) throws IOException {
File src= new File("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\file.txt");
File dest =new File("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\file1.txt");
FileInputStream fis= new FileInputStream(src);
FileOutputStream fos=new FileOutputStream(dest);
int len ;
while ((len=fis.read())!= -1){
fos.write(len^2);
}
fos.close();
fis.close();
}
}
加密
![](https://img-blog.csdnimg.cn/img_convert/1f143633239cf9a822dd34a788a49359.png)
源文件
![](https://img-blog.csdnimg.cn/img_convert/23c6fea38a4da4dedff050f080713a56.png)
解密
![](https://img-blog.csdnimg.cn/img_convert/8eb9aa1a4043daefabab837898241bf8.png)
修改文件中的数据排序
public class demo4 {
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\b");
StringBuilder sb=new StringBuilder();
int ch;
while ((ch=fr.read())!=-1) {
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
String str=sb.toString();
String[]arrstr=str.split("-");
ArrayList<Integer> list =new ArrayList<>();
for (String s:arrstr){
int i = Integer.parseInt(s);//parseInt() 方法用于将字符串参数作为有符号的十进制整数进行解析。如果方法有两个参数, 使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
list.add(i);
}
Collections.sort(list);
System.out.println(list);
FileWriter fw= new FileWriter("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\b");
for (int i =0;i<list.size();i++){
if(i==(list.size()-1)){
fw.write(list.get(i)+"");
}
else{
fw.write(list.get(i)+"-");
}
}
fw.close();
}
}
![](https://img-blog.csdnimg.cn/img_convert/70b4bbd91951c7f26e5c2563d42c4d2b.png)
![](https://img-blog.csdnimg.cn/img_convert/8988ab4dbc70475d5a5c246455a75c96.png)
方法二
public class demo5 {
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\b");
StringBuilder sb=new StringBuilder();
int ch;
while ((ch=fr.read())!=-1) {
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
// 排序
Integer[] arr=Arrays.stream(sb.toString().split("-")).map(Integer::parseInt)
.sorted().toArray(Integer[]::new);
//写出
FileWriter fe=new FileWriter("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\b");
String s =Arrays.toString(arr).replace(", ","-");
String re= s.substring(1,s.length()-1);
System.out.println(re);
fe.write(re);
fe.close();
}
}
字节缓冲流
基本流
高级流 基本流+缓冲流
字节缓冲流:BufferedInputStream BufferedOutputStream
字符缓冲流: BufferedReader BufferedWriter
原理:底层自带了8K缓冲区提高性能,可以显著提高字节流的读写功能,对字符流提升不明显但是字符流
创建对象时需要关联基本流
字节缓冲流 一次读取一个字节和一次读取多个字节:
字符缓冲流: 把基本流变成高级流,有readLine()方法
newLine 跨平台换行(输出流)
续写功能 开启续写功能,但是bufferedwriter 没有这个功能,需要写在关联的filewriter
转换流
属于字符流,高级流,包装基本流,inputstreamreader outputstreamreader
可以把字节流转换为字符流,filewriter filereader可以通过 charset.forName指定字符编码
序列化流:
高级流 把基本流包装成高级流
objectInputStream ObjectOutputStream 反序列化流 序列化流
序列化流: 把java对象写到本地文件中,通过此流读取出来
反序列化流:writeObject 方法
小细节: 需要Javabean 类实现 一个serializable接口,这个接口没有抽象方法标记类接口,实现这个接口,就表示类可以被序列化
public class demo6 {
public static void main(String[] args) throws IOException {
Student s= new Student("z",12);
ObjectOutputStream oop= new ObjectOutputStream(new FileOutputStream("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\b"));
oop.writeObject(s);
oop.close();
}
}
public class Student implements Serializable {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反序列化流:
readObject方法
把序列化到本地文件中的对象读取到程序中来
public class demo7 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream oop= new ObjectInputStream(new FileInputStream("C:\\Users\\77918\\Documents\\temp\\timacatdemo\\src\\main\\resources\\b"));
Object oo= oop.readObject();
System.out.println(oo);
oop.close();
}
}
![](https://img-blog.csdnimg.cn/img_convert/3b23a8f54067f6c92bfff614a2bb82bd.png)
细节:序列化流写到对象时需要实现一个接口,否则会出现异常,序列化流写到文件中的数据是不能更改的,一旦修改就无法读取回来,序列化对象后,修改了 javabean类 再次反序列化,会抛出 invalidclassexception异常,解决方法给Javabean 加上seriversionUID b版本号
不想被序列化的可以加上transient关键字修饰
打印流:只有输出流,高级流 ;只有打印不能读,原样写出,特有的写出方法,可以自动刷新自动换行