不要自卑,去提升实力
互联网行业谁技术牛谁是爹
如果文章可以带给你能量,那是最好的事!请相信自己
加油o~
理解Java的IO流
Java的IO流是实现输入/输出的基础,它可以方便的实现数据的输入/输出操作,在Java中把不同的输入/输出源(键盘、文件、网络连接等)抽象为表述为“流”。
流(Stream)的分类
1、字节流和字符流
- InputStream
- Reader
- OutputStream
- Writer
这两种流的用法基本相似,主要是二者操纵的数据不同,字节流操作的是字节,就是那些二进制文件,而字符流一般就会处理我们能够标识的文本文件这些字符,字节流处理8位的字节,字符流处理16个字节的字符(在Java中一个字符占2个字节)
2、节点流和处理流
节点流:用于读写数据的流,有时也被称为低级流,因为它的执行效率较低
处理流:它是对现有的流进行封装,进而去执行读写等操作,这也就被称为高级流
那么我们如何去分辨一个流是节点流还是处理流呢?
一般节点流的构造参数都是物理地址,而处理流的构造参数一般为流的对象
3、输入流和输出流
输入流:顾名思义,能够从指定对象读取数据
输出流:向指定位置输出数据
一般输入流的方向是从外界到程序
而输出流是从执行程序到内存外界
四大输入输出流基类:
- InputStream
- Reader
- OutStream
- Writer
这四大类都为抽象类,是不能够实例化对象的,一般我们都是创建他们的子类对象,同时这四大类中包含很多读写方法供给使用
字节流和字符流
那么了解了这么多,我们接下来重点讲解下这两大流的使用
输入流:
InputStream的方法:
- int read():从输入流读取单个字节,返回值为读取的字节数据
- int read(byte[ ] arr):从输入流中读取数据存入到字节数组arr中,返回值为读取的字节数,当数据读取完成后返回-1
- int read(byte[ ] arr,int off,int length):从输入流读取length个字节存取到字节数组中,从数组的off位置开始存储
Reader的方法:
- int read():从输入流读取单个字符,返回值为字符所对应ASCII编码
- int read(char[ ] arr):从输入流读取数据,存入到arr字符数组里面
- int read(char[ ] arr,int off,int length):从输入流读取length个字符存取到字符数组中,从数组下标off开始
示例:将Java.txt文件以字节形式读取出来,并输出到控制台
//采用Java7特性,不需要手动关闭资源
try(FileInputStream fis=new FileInputStream("Java.txt")){
byte[] arr=new byte[1024];
int hasRead;
while((hasRead=fis.read(arr))>0){
System.out.println(new String(arr,0,hasRead));
}
}
注意:这里有时会出现问题,当我们有时读取得编码未读全,就是一个字符的字节分两次读,就可能出现乱码问题,当打印的时候,如果单纯是复制文本没有问题。
try(FileReader fr=new FileReader("Java.txt")){
char[] arr=new char[1024];
int hasRead;
while((hasRead=fr.read(arr))>0){
System.out.println(new String(arr,0,hasRead));
}
}
输出流:
OutputStream的方法:
- void write(int a):将指定的字节输出到输出流中,其中a可以是字节,也可以是字符
- void write(byte[ ]/char[ ] arr):将字节数组或者是字符数组中的数据写入到输出流中
- void write(byte[ ]/char[ ] arr,int off,int length):将字符数组从下标off开始,读取length个字符存入到输出流中
Writer的方法:
- void write(String string):将字符串string存入到输出流中
- void write(String string,int off,int length):将指定长度的字符串输出到输出流中
示例:将 Java.txt 文件的内容复制到 Java2.txt 文件中
try(
FileInputStream fis=new FileInputStream("Java.txt");
FileOutputStream fos=new FileOutputStream("Java2.txt");
)
{
byte[] arr=new byte[1024];
int hasRead;
while((hasRead=fis.read(arr))>0){
fos.write(arr,0,hasRead);
}
}
一般情况下,我们处理的是字符用字符流处理,用字节流也可以,因为字节流比较万能,字符也是由二进制转换来的
处理流的使用方法
处理流:对现有流对象进行包装,可以提高我们处理数据的效率,对于一些数据操作也会比较方便
我们讲一种很强大的流PrintStream,它的本质是字节流,他就是一种处理流,用于我们输出数据,如果我们要输出文本内容时,应该将输出流包装成PrintStream流输出
1、首先我们要创建一个输出流对象
2、创建打印流对象,传入参数,包装输出流
示例:
try(
FileOutputStream fos=new FileOutputStream("Java.txt");
PrintStream ps=new PrintStream(fos);
)
{
ps.println("你好,明天");
ps.println(new Date());
}
上述代码执行效果是,将字符串你好明天和Date类的实例对象打印到Java.txt文件中
注意:当我们关闭处理流的同时,被它包装的流也会关闭
转换流
Java提供了两个转换流
- InputStreamReader:将字节输入流转化为字符输入流
- OutputStreamReader:将字节输出流转化为字符输出流
我们将一个事情:在Java中,System.in代表标准输入,即键盘,而System.out表示标准输出,即控制台
有时我们可以将字节输入流转化为字符输入流
示例:本例我们将System.in转化为字符输入流,然后用BufferedReader处理流包装,加快效率
try(
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
)
{
String line=null;
while((line=br.readLine())!=null){
if("exit".equals(line)){
break;
}else{
System.out.println(line);
}
}
}
上述代码首先将标准输入流转化为字符输入流,然后用缓冲流包装,
缓冲流有readLine方法,可以读取一整行内容,本例中,就会从键盘读入一整行内容,去循环判断
重新指向标准输入输出流
对于Java我们经常通过System.in从键盘读取我们想要的内容或者用System.out向控制台输出数据,但有时我们想从文本中读取,或者是想这些数据存到文本,而不是打印到控制台,我们应该怎么办呢?
在System类中提供类两种方法:
- void setIn(InputStream in):重新指向标准输入流,从in指定的流获取数据,而不是键盘
- void setOut(PrintStream out):重新指向标准输出流,将输出内容输出到out输出流中,而不是控制台
示例:
try(
PrintStream ps=new PrintStream(new FileOutputStream("Java.txt"));
)
{
System.setOut(ps);
System.out.println("你好明天");
System.out.println(new Date());
}
上述代码我们讲系统的标准输出指向了PrintStream指定的流,所以之后的System.out.println等操作输出的内容就会进入文本,而不会在控制台显示,其实这可以直接用PrintStream包装指定输出流,用ps.println()也可以实现将数据写入文本
示例:
try(
FileInputStream fis=new FileInputStream("Java.txt");
)
{
System.setIn(fis);
Scanner sc=new Scanner(System.in);
sc.useDelimiter("\n");
while(sc.hasNext()){
System.out.println(sc.next());
}
}
上述代码重新指定了标准输入,输入的内容不来自键盘,而是来自文本,利用循环不断判断文本是否还有数据,如果就进行读取
sc.useDelimiter("\n");
加入这句话的意思是读取内容时以回车为分界,如果不加,默认以空格为分界
强大流:RandomAccessFile流
RandomAccessFile流是Java中功能相当丰富的流,他可以实现任意读取写入数据,即支持“随机访问"。
当我们有时并不需要读取全部数据只要部分数据时,我们可以使用这种流
但是他有个缺点,他只能后操纵文本文件,不可以操纵其他流
既然他可以实现任意读取,就说明它内部存在一个指针记录真当前文件位置
- long getFilePointer():返回文件指针的当前位置
- void seek(long pos):将文件指针移动到pos处
它存在几个读写模式:
- r:以只读形式打开文件
- rw:以既可读也可以写的方式打开文件,如果没有自动创建
示例:读取Java.txt文件中部分数据
try(
RandomAccessFile raf=new RandomAccessFile("Java.txt","r");
)
{
raf.seek(100);
byte[] arr=new byte[1024];
int hasRead;
while((has=raf.read(arr))>0){
System.out.println(new String(arr,0,hasRead));
}
}
对于基本的输入输出流的方法它全部有。
示例:向文件追加文件
try(
RandomAccessFile raf=new RandomAccessFile("Java.txt");
)
{
raf.seek(raf.length());
raf.write("追加文本");
}
将指针指向文件结尾,继续写数据
那么我们如何实现插入数据呢?当我们将指针更改到指定位置,写入数据的话,后面的数据会进行覆盖,所以我们就要先保存这部分文件再还原
File tempFile=File.createTempFile("temp.txt",null);
tempFile.deleteOnExit();
try(
RandomAccessFile raf=new RandomAccessFile(fileName,"rw");
FileInputStream fis=new FileInputStream(tempFile);
FileOutputStream fos=new FileOutputStream(tempFile);
)
{
raf.seek(pos);
byte[] arr=new byte[1024];
int hasRead;
while((hasRead=raf.read(arr))>0){
fos.write(arr,0,hasRead);
}
raf.seek(pos);
raf.write(InsertContent.getBytes());//将字符串转化为字节数组
while((hasRead=fis.read(arr))>0){
raf.write(arr,0,hasRead);
}
}
上述代码我们定义了一个临时文件,并指定当程序结束时,自动删除
我们先将待插入位置后的文本记录到临时文件中,再去插入我们要插入的内容
之后我们再将临时文件中的内容重新写入文件中,即可完成插入操作
注意:
我们向文本追加文件也可以用这种方式:
FileWriter fw=new FileWriter("Java.txt",true);
最后一个参数代表是否追加数据