1、什么是IO流
IO即InputStream和OutputStream,意思是输入流和输出流。
2、怎么区分输入流和输出流
刚开始学IO流的时候这个问题把我弄懵了一下
程序操作的数据都应该是在内存里面,内存是你操作的主对象,把数据从其他资源里面传送到内存里面,就是输入,反之,把数据从内存传送到其他资源,就是输出。
读文件
不管你从磁盘读,从网络读,或者从键盘读,读到内存,就是InputStream。数据读入到内存叫输入流。
写文件
不管你写倒磁盘,写到网络,或者写到屏幕,都是OuputStream。从内存写到别的地方去叫输出流。
3、输入流的具体用法
从文件读取内容到内存
例子:读文件的一个字节
read()方法是读取一个字节,返回值就是读到的数据值,如果读取的数据值等于-1,则读取到文件末尾。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MainTest {
public static void main(String[] args) {
// 创建File对象
// File对象描述一个文件
File file=new File("D:\\filetest\\file1.txt");
//!!首先得建立一个通道
try
{
// 新建一个FileInputStream对象
FileInputStream fileInputStream=new FileInputStream(file);
// !!建立通道后你得让流知道进哪个通道,因为你有可能new了几个File();
// 读一个字节
int c1 = fileInputStream.read();
System.out.println(c1);//读出的是数字,是对应的ASCII码
int c2=fileInputStream.read();
System.out.println((char)c2);//读下一个字节,读出的是乱码
int c3=fileInputStream.read();
System.out.println((char)c3);
int c4=fileInputStream.read();
System.out.println((char)c4);
}catch(FileNotFoundException e)
{
//输出堆栈信息
e.printStackTrace();
}catch(IOException e)
{
e.printStackTrace();
}finally {
try {
//关闭输出流,释放资源
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
例子:输出文件里的所有内容
创建一个数组buf
然后用read(buf):实现从文件读取内容到内存,不会乱码,是什么就是什么
public class HuanChongLiu {
public static void main(String[] args) {
// 创建File对象
// File对象描述一个文件
File file = new File("F:\\Day0730test.txt");
/* try {
file.createNewFile(); 这一段代码是为了防止没有描述的文件,
防止程序找不到路径,有了文件的话可不要这段代码
} catch (IOException e1) {
e1.printStackTrace();
}
*/
FileInputStream fileInputStream=null;
try
{
//新建一个FileInputStream对象
fileInputStream=new FileInputStream(file);
//新建一个字节数组,长度为32
byte []buf=new byte[32];
//read(buf):此方法的返回值就是当前读取的字节个数,将数据读取到buf数组
//将readLen变量也就是read方法的返回值,当此变量等于-1,则说明读到文件末尾
int readLen=-1;
while((readLen=fileInputStream.read(buf))!=-1)//
{
//将字节数组转换成字符串
//String(buf,0,readLen)表示字节数组buf从第1位到
//第readLen位都转换成字符
String content=new String(buf,0,readLen);
System.out.print(content);//将转换承德字符输出
}
}catch(FileNotFoundException e)
{
e.printStackTrace();
}catch(IOException e)
{
e.printStackTrace();
}finally {
try {
//文件输入流关闭(释放资源)
fileInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
}
}
4、输出流的具体用法
输出流
将内存的数据写到文件
例子:将多个字符写到文件中
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainTest2 {
public static void main(String[] args) {
// 通过File对象描述文件
File file = new File("D:\\filetest\\file3.txt");
// 创建FileOutputStream对象
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
String content="weclome to gec,I love java";
//将字符串转换成字节数组
byte []buf=content.getBytes();
fileOutputStream.write(buf);
//清除缓冲
fileOutputStream.flush();
/*flush()一般主要用在IO中,即清空缓冲区数据,就是说你用读写流的时候,
其实数据是先被读到了内存中,然后用这里的数据写到文件中,当你文件的数据
读完的时候(即读到内存)不代表你的数据已经写完了(即写到另一个文件),
因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了
close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之
前先flush(),先刷新缓存清空数据。*/
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
// 关闭输出流,释放资源
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
对于将数据写道文件,在new一个File()的时候,如果没有原文件或者路径不对,输出流会自动创建一个文件。
5、练习题
编写一个应用程序,将用户从键盘输入的10个整数存入文件,从文件读取再顺序读出。
import java.io.*;
import java.util.Scanner;
public class Test {
//System.in:默认是键盘设备
public static void main(String[] args) {
File file = new File("F:\\Day0730test.txt");
int i = 0;
System.out.print("请输入十个整数:");
@SuppressWarnings("resource")
Scanner scan = new Scanner(System.in);
FileOutputStream fileOutputStream = null;
//往文件写入10个整数
try {
fileOutputStream = new FileOutputStream(file);
while (i < 10) {
int str = scan.nextInt();//读取键盘输入
fileOutputStream.write(str);//将该行写到文件中
i++;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileOutputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
//读取存入的10个整数并输出
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
for(int q = 0;q<10;q++){
//只读一个数据的情况:int c1 = fileInputStream.read()
System.out.println(fileInputStream.read());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6、IO流又可以分为字节流和字符流
上面讲的FileInputStream和FileOutStream都是字节流,调用read方法读的都是一个字节,会产生乱码。
而字符流就不会产生乱码,因为字符流调用read方法读的是一个字符。
下面来看看字符流的使用吧
字符流的读:FileReader
主要方法
-------------------------------------例子---------------------------------
public class MainTest {
public static void main(String[] args) {
//以File对象描述file5.txt
File file=new File("D:\\filetest\\file5.txt");
//构造FileReader对象
FileReader reader=null;
try
{
reader=new FileReader(file);
//定义字符数组
char []cbuf=new char[16];
//定义读取字符的长度
int readLen=-1;
//不断将数据读到字符数组,read方法的返回值就是读到的字符个数,
//直到readLen等于-1,则意味读取文件的末尾,然后跳出循环
while((readLen=reader.read(cbuf))!=-1)
{
System.out.print(new String(cbuf,0,readLen));
}
}catch(FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(reader!=null)
{
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
字符流的写:FileWriter
主要方法
----------------------例子----------------------------
public class MainTest {
public static void main(String[] args) throws IOException {
//创建文件
File file=new File("file2.txt");
FileWriter fw=new FileWriter(file);
String message="我来自广州";
fw.write(message);
/*String message="java,hello world";
fw.write(message);*/
/*fw.write(0+48);
fw.write(1+48);
char cbuf[]= {'a','b'};
fw.write(cbuf);*/
fw.flush();
fw.close();
}
}
7、转换流
InputStreamReader将字节输入流转换成字符输入流,
OutputStreamWriter将字节输出流转换成字符输出流
InputStreamReader
public class MainTest {
public static void main(String[] args) throws IOException {
File file = new File("file3.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
//将输出字节流---->输出字符流
//将数据以字符方式写到文件里面
OutputStreamWriter writer=new OutputStreamWriter(fileOutputStream);
writer.write("欢迎来到粤嵌,学习java");
writer.flush();
writer.close();
fileOutputStream.close();
}
}
OutputStreamWriter
public class MainTest {
public static void main(String[] args) throws IOException {
File file = new File("file3.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
//将输出字节流---->输出字符流
//将数据以字符方式写到文件里面
OutputStreamWriter writer=new OutputStreamWriter(fileOutputStream);
writer.write("欢迎来到粤嵌,学习java");
writer.flush();
writer.close();
fileOutputStream.close();
}
}
8、缓冲流
缓冲流最大的特点是有一个缓冲区,默认分配一个8k大小的缓冲区,来储存数据,针对大量的读取及写入数据,能提高操作效率。
BufferReader类用法
public class MainTest {
public static void main(String[] args) throws IOException {
FileReader reader=new FileReader(new File("file4.txt"));
BufferedReader br=new BufferedReader(reader);
System.out.println((char)br.read());//a
System.out.println((char)br.read());//b
//设置一个mark:标记一个位置
System.out.println("mark 操作");
br.mark(2);//reset的时候会从第3个元素开始读取,只有缓冲流能这么做,因为他有缓冲区
System.out.println((char)br.read());//c
System.out.println((char)br.read());//d
System.out.println("reset 操作");
br.reset();
System.out.println((char)br.read());//c
System.out.println((char)br.read());//d
//按行读取
/* String line1=br.readLine();
String line2=br.readLine();
String line3=br.readLine();
String line4=br.readLine();
System.out.println(line1);
System.out.println(line2);
System.out.println(line3);
System.out.println(line4);*/
}
}
9、一些例子(他人代码)
使用IO流拷贝文件
import java.io.*;
public class Copy {
public static void main(String[] args) {
//调用方法执行文件复制
boolean isSuccess = copyFile("要复制的文件(全路径或相对路径+文件名)","新的文件名");
if(isSuccess) {
System.out.println("文件复制成功!");
}else {
System.out.println("文件复制失败!");
}
}
private static boolean copyFile(String oldFile, String newFile) {
//定义输入输出流
InputStream is = null;
OutputStream os = null;
StringBuilder builder = new StringBuilder();
try {
//读取
is = new FileInputStream(oldFile);
//输出
os = new FileOutputStream(newFile);
//定义字节缓冲数组
byte[] flush = new byte[1024];
//定义读取长度
int len;
while ((len = is.read(flush)) != -1) {
os.write(flush, 0, len);
}
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//先开启的流 后关闭
try {
if (null != os) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (new File(newFile).exists()) {
return true;
}
return false;
}
}
目录的拷贝
import java.io.*;
import java.time.Duration;
import java.time.Instant;
public class CopyDir {
public static void main(String[] args) {
Instant start = Instant.now();
boolean isOk = dirOrFile("要复制的目录路径", "目标路径");
if(isOk){
System.out.println("目录复制完成!");
}else {
System.out.println("目录复制失败!");
}
Instant end = Instant.now();
System.out.println("耗时:"+Duration.between(start,end).toMillis()+"毫秒!");
}
private static boolean copyDir(String oldDir, String newDir) {
//定义流
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(oldDir);
os = new FileOutputStream(newDir);
//定义缓冲字节数组
byte[] flush = new byte[1024];
//定义读取长度
int len;
while ((len = is.read(flush)) != -1) {
os.write(flush, 0, len);
}
//刷新流
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (null != os) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
private static boolean dirOrFile(String oldDir, String newDir) {
//定义写入写出文件
File old = new File(oldDir);
File copy = new File(newDir);
//判断如果是目录的话 创建目录
if (old.isDirectory()) {
copy.mkdir();
File[] files = old.listFiles();
for (int i = 0; i < files.length; i++) {
dirOrFile(files[i].getAbsolutePath(), newDir + "/" + files[i].getName());
}
}
if (old.isFile()) {
copyDir(oldDir, newDir);
}
if (old.length()==copy.length()){
return true;
}
return false;
}
}
10、缓冲流源码分析
- 分析构造器
//java的源码是不能修改的,你可以把里边的代码复制出来新建一个类。
public MyBufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
//自定义缓存大小
//分配8个字符的缓存大小
cb = new char[sz];
//nextChar:记录下一次读取字符的位置
//nChars:记录当前缓存区可读的字符个数
nextChar = nChars = 0;
}
- 分析read方法
public int read(char cbuf[], int off, int len) throws IOException {
//编写同步锁代码块
synchronized (lock) {
//确认输入流不为空
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
//从缓存区的数据复制到cbuf数组里面
int n = read1(cbuf, off, len);
if (n <= 0) return n;
//将之前处理不完的数据复制到cbuf数组,再次调用read1方法
while ((n < len) && in.ready()) {
int n1 = read1(cbuf, off + n, len - n);
if (n1 <= 0) break;
n += n1;
}
return n;
}
}
- read1方法
private int read1(char[] cbuf, int off, int len) throws IOException {
//nextChar:记录下一次读取字符的位置
//nChars:记录缓存区可读的字符个数
//如果nextChar>nChars,则重新刷新缓存区
if (nextChar >= nChars) {
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, and if line feeds are not
being skipped, do not bother to copy the characters into the
local buffer. In this way buffered streams will cascade
harmlessly. */
if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
return in.read(cbuf, off, len);
}
//针对缓存区的数据,重新刷新,将新的数据重新更新到缓存区
fill();
}
if (nextChar >= nChars) return -1;
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
if (nextChar >= nChars)
fill();
if (nextChar >= nChars)
return -1;
}
}
int n = Math.min(len, nChars - nextChar);
//将缓冲区的数据复制到cbuf
System.arraycopy(cb, nextChar, cbuf, off, n);
//并且更新nextChar的位置值
nextChar += n;
return n;
}
- fill方法
private void fill() throws IOException {
int dst;
//是否有调有mark方法,如果没有设置mark值,则markedChar <= UNMARKED为true
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
} else {
/* Marked */
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
/* Gone past read-ahead limit: Invalidate mark */
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (readAheadLimit <= cb.length) {
/* Shuffle in the current buffer */
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
/* Reallocate buffer to accommodate read-ahead limit */
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
nextChar = nChars = delta;
}
}
int n;
do {
//将输入流的数据读取到缓存区里面,n变量决定读取多个字符
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
//如果n>0,代表已经读到数据
if (n > 0) {
//nChars:缓存区可读的数据
nChars = dst + n;
//下一次可读数据的位置
nextChar = dst;
}
}
- 图如下
缓冲区其实就是一个数组来的
11、打印流PrintStream
PrintStream
一般我们用System.out.println(“hellow world”);是输出到控制台的,如果我们想让他输出到其他的文件,就可以用重定向。例子如下
这是我们之前学的:
FileOutStream afileOutStream = new FileOutpStream(new File("a.txt"));
//你不是要输出到a.txt吗,那就创建一个输出流,将信息输出到文件
PrintStream ps = new PrintStream(afileOutStream);
ps.println("你好啊");
ps.flush();
ps.close();
afileOutStream.close();
此时将在文件a.txt中显示 你好啊
- 现在修改重定向设备:
标准输出到a.txt.
FileOutputStream afileOutStream = new FileOutputStream(new File("a.txt"));
//你不是要输出到a.txt吗,那就创建一个输出流,将信息输出到文件
PrintStream ps = new PrintStream(afileOutStream);
//这里就是修改重标准输出设备了,从原本的控制台,定向到a.txt文件
System.setOut(ps);//如果是标准错误输出,用setErr()
System.out.println("11111");
System.out.println("222222");
PrintWrite
public class Printstream {//类名不能与关键字同名。。。。。
public static void main(String[] args) throws FileNotFoundException {
//获取键盘数据,用PrintStream打印输出到文件
FileOutputStream fileOutputStream = new FileOutputStream(new File("a.txt"));
PrintStream ps = new PrintStream(fileOutputStream);
System.setOut(ps);
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
System.out.println(str);
/*
* 循环捕获键盘输入,当按q时结束循环
* Scanner sc = new Scanner(System.in);
* String content = null;
* while((content=sc.nextLine())!=null){
System.out.println(content);
if(content.equals("q")){ //如果键盘输入q则结束循环
break;
}
}
sc.close();//关闭资源
*/
//获取键盘数据,用PrintWrite打印输出到文件
FileOutputStream a = new FileOutputStream(new File("b.txt"));
PrintWriter pw = new PrintWriter(a);
Scanner sr = new Scanner(System.in);
String st = sr.nextLine();
pw.write(st);
pw.flush();
pw.close();
sr.close();
}
}
12、一个强大的读写文件的类RandomAccessFile(不属于IO流体系但可以任意读取文件)
- RandomAccessFile类的最大特点就是
可以自由访问文件的任意位置
(用seek()方法),支持字节、字符操作
- 例子
package RandomAccessFile实例;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class MainTest {
public static void main(String[] args) throws IOException {
//创建一个File对象
File file=new File("f.txt");
RandomAccessFile randomAccessFile=new RandomAccessFile(file, "rw");
System.out.println((char)randomAccessFile.read());
//将读取的起始位置定位到第4个字符开始读取
System.out.println("将读取的起始位置定位到第4个字符开始读取,调用seek(3)");
randomAccessFile.seek(3);
System.out.println((char)randomAccessFile.read());
System.out.println((char)randomAccessFile.read());
System.out.println((char)randomAccessFile.read());
System.out.println((char)randomAccessFile.read());
System.out.println("将读取的起始位置定位到第4个字符开始读取,调用seek(3)");
randomAccessFile.seek(3);
System.out.println((char)randomAccessFile.read());
System.out.println((char)randomAccessFile.read());
System.out.println((char)randomAccessFile.read());
System.out.println((char)randomAccessFile.read());
//读取行数
System.out.println(randomAccessFile.readLine());
System.out.println(randomAccessFile.readLine());
System.out.println(randomAccessFile.readLine());
System.out.println(randomAccessFile.readLine());
}
}
13、对象流:对象序列化处理
序列化处理
将对象的状态存入文件
class Person implements Serializable{//你不是要将对象的状态存入文件嘛,那就先创建要建对象的那个类
private static final long serialVersionUID = 1L;
private String idCard;
private String name;
private int age;
//private transient String address; //如果用transient修饰成员变量,则不会将这个变量序列化,
//也就是不会将该变量的状态存到文件
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
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;
}
@Override
public String toString() {
return "Person [idCard=" + idCard + ", name=" + name + ", age=" + age + "]";
}
public Person() {
}
public Person(String idCard, String name, int age) {
super();
this.idCard = idCard;
this.name = name;
this.age = age;
}
}
public class XuLieHua {
public static void main(String[] args) throws IOException {
//不一定是txt文件,jpg文件等也行的,只是看图软件打不开
FileOutputStream fo = new FileOutputStream(new File("person.txt"));
ObjectOutputStream ob = new ObjectOutputStream(fo);
Person ps = new Person();
ps.setAge(26);
ps.setIdCard("455186541323136");
ps.setName("王五");
ob.writeObject(ps);
ob.close();
fo.close();
}
}
反序列化处理
将储存对象状态的文件读出来
package com.newclass.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class FanXuLieHua {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("person.jpg"));
ObjectInputStream oi = new ObjectInputStream(fi);
Person pe =(Person) oi.readObject();
System.out.println(pe.toString());
oi.close();
fi.close();
}
}