IO引入
字节流和字符流(一些说明)
在java中io流分为字节流和字符流。字节流和字符流分别对应相应的读取和写入操作。整体的功能就是实现对输入输出的操作。
I/O就是input和output的缩写。而java之所以将此称之为流就是将其抽象化,来表示输入输出的功能。封装为对应的类,实现具体的功能,然后全部都存在io包当中。
用流来表示输入输出是也是非常形象的。可以想象数据传输信道中数据像流水一样进行传输。
字节流就是传输单位按为字节进行传输,字符流就是传输按照字符为单位进行传输
字节流没有用到缓冲区并不是没有用到内存
字节流与字符流的区别是什么呢? 从传输上面讲,字节流是字节在文件测层次上进行操作的,并没有用到缓冲区,字符流会用到缓冲区,然后通过缓冲区操作文件。我们是从传输上面讲,但是不要认为这个缓存区就是缓存,缓存是cache,缓冲区是buffer。这是两个不同的概念。
buffer也就是我们的缓冲区是内存空间的一部分,内存空间是预留了一定的存储空间的,用来缓冲输入或者输出。这就是我们 的缓冲区。当然缓冲区根据输入设备或者是输出设备分文输入缓冲区和输出缓冲区。 但是缓存(cache)就是我们为了内存为了弥补速度上与cpu的差异而划分出来的。当然磁盘上也有缓存,cpu也有。但是我们这里只区分内存上的缓冲区与缓存。这是完全不一样的两个概念。
字节流本身没有用到缓冲区,但是也可以加入缓冲流来加快读取效率。那就是字节缓冲流了
缓冲区是为了缓冲,缓存是为了加快存取速度。
缓冲区不等于内存,没有用到缓冲区不是没有用到内存。如果认为没有用到内存,那计算机就白学了。
在处理或者说是传输数据上面,字节流基本是可以处理任何类型的数据类型,但是字符流只能处理的是字符或者是字符串。字节流是不能直接处理Unicode字符的,字符流是可以进行处理的。在涉及到一些编码的问题上,比如文件中存在汉字,我们可以去用字符流去处理。
为什么输入流是读数据,而输出流是写数据?
不知道你是否有这样的疑问。 为什么输入流是读取数据,而输出流却是写数据。、 因为输入输出都是相对于内存来说的。input是将数据从磁盘读取到内存当中,而输出就是将数据从内存输出道磁盘。 我们程序在运行地时候也会从磁盘中被调入内存中,程序和其运行时数据都是会在内存中驻留,然后在真正执行的时候,cpu会从中读取到相应的数据指令,执行相应的指令。
字节流说明
字节输入流读数据的方法
public abstract class InputStreamextends InputStream
字节输入流InputStream是一个抽象类,直接继承于Object类。所以我们具体在应用功能的时候,最好还是去用到实现它的一些基本的类。
比较主要的读取方法在参数设定上会和字节输出流的写入方法相似。
public abstract int read() throws IOException从输入流读取数据的下一个字节。 值字节被返回作为int范围0至255 。 如果没有字节可用,因为已经到达流的末尾,则返回值-1 。 该方法阻塞直到输入数据可用,检测到流的结尾,或抛出异常。 一个子类必须提供这个方法的一个实现。
public int read(byte[] b, int off, int len) throws IOException从输入流读取len字节的数据到一个字节数组。 尝试读取多达len个字节,但可以读取较小的数字。 实际读取的字节数作为整数返回。 该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。 如果len为零,则不会读取字节并返回0 ; 否则,尝试读取至少一个字节。 如果没有字节可用,因为流是文件的-1则返回值-1 ; 否则,读取至少一个字节并存储到b 。 第一个字节读取存储在元素b[off] ,下一个字节存入b[off+1] ,等等。 读取的字节数最多等于len 。 令k为实际读取的字节数; 这些字节将存储在元素b[off]至b[off+ k -1] ,使元素b[off+ k ]至b[off+len-1]不受影响。 在每种情况下,元件b[0]至b[off]和元件b[off+len]至b[b.length-1]不受影响。 该read(b, off, len)类方法InputStream简单地调用该方法read()反复。 如果第一个这样的呼叫产生一个IOException ,那个异常从呼叫返回到read(b, off, len)方法。 如果任何后续调用read()导致IOException ,则异常被捕获并被视为文件的结尾; 读取到该点的字节被存储到b ,并且返回异常发生前读取的字节数。 该方法的默认实现将阻塞,直到所请求的输入数据len已被读取,文件结束被检测到或异常被抛出为止。 鼓励子类提供更有效的方法实现。
public int read(byte[] b) throws IOException从输入流读取一些字节数,并将它们存储到缓冲区b 。 实际读取的字节数作为整数返回。
字节输入流是用来进行读取数据的,不过读取数据的方式可以有两种,基本的是这样。
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class IoDrmo_2 {
public static void main(String args[]) throws IOException {
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
int by = fis.read();
while(by!=-1)
{
System.out.print((char)by);
by = fis.read();
}
while((by = fis.read())!=-1)
{
System.out.print((char)by);
}
fis.close();
}
}
当然我们也可以按照字节数组去读取,我们可以让一次读取多个字节。这样读取的话,就快一些。
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class IoDemo_4 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
byte[] bytes = new byte[1024];
int len;
while((len= fis.read(bytes))!=-1)
{
System.out.print(new String(bytes,0,len));
}
fis.close();
}
}
一定要记得在操作完毕之后,关闭资源。
字节缓冲输入流
public class BufferedInputStream extends FilterInputStream
提供的主要的构造方法
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。 BufferedInputStream(InputStream in, int size) 创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。
从构造方法可知。我们使用它的时候需要传入一个字节缓冲输入流对象。
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedInputStream bis = new BufferedInputStream(fis);
同样我们可以用这样的对象去进行读取操作。
字节输出流写数据的方法
public abstract class OutputStreamextends OutputStream
字节输出流就是进行写入数据,我们可以进行向文件中写入数据。基本的抽象类还是需要去实现类中实现它的基本功能。
可以这样去实现,输出流的时候可以这样传入参数。
package java_practice;
import java.io.*;
public class FileDemo__ {
public static void main(String args[]) throws IOException {
File file = new File("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(97);
fos.close();
}
}
将这个文件对象传入,当然你也可以向文件输入流一样直接传入文件路径。但是需要注意的是,如果文件不存在的话 输入流是会报错的。因为输入流是读取文件的,被读取的文件不存在,那就会报错。输出流会自动创建文件,输入流不会自动创建文件。
写入数据的时候,比如我们写入一个97, 就会转换为字符a。这就涉及到对应的编码。
对照ASCII码表,我们可以看到十进制97对应的字符为a。这个write()方法会把对应的十进制数字97按照ASCII码进行转换。
0到97的输入,会看到ASCII表进行转换,其余的编码默认会查询系统编码GBK。关于编码的问题,我们在后面说。
当然write()方法也可以接收一个数组类型。一个字节数组。去对应的源码里面一看便知。
当然还可以有不同的参数列表 当然你如果不是很想去查看源码,也可以在javaapi里面找到对应的方法说明。
write public void write(int b) throws IOException将一个 integer(数组长度)写入此流。
public void write(byte[] b) throws IOException将b.length字节从指定的字节数组写入此输出流。 write(b)的一般合约是应该具有与电话write(b, 0, b.length)完全相同的效果。
public void write(byte[] b, int off, int len) throws IOException从指定的字节数组写入len字节,从偏移off开始输出到此输出流。 write(b, off, len)的一般合同是数组b中的一些字节按顺序写入输出流; 元素b[off]是写入的第一个字节, b[off+len-1]是此操作写入的最后一个字节。 该write的方法OutputStream调用写出在每个字节中的一个参数的写入方法。 鼓励子类覆盖此方法并提供更有效的实现。 如果b是null ,则抛出NullPointerException 。 如果off为负数,或len为负数,或off+len大于数组b的长度,则抛出IndexOutOfBoundsException 。
与此相关的方法,都可以查阅文档得到。
byte[] by = {97,98,99,100};
fos.write(by,0,by.length);
fos.write(by);
当然你不能直接给write()中直接传入字符或者是字符串,我们的直接传入类型中是直接接收的是int类型,或者是可以传入一个byte数组。
既然是字节流,我们可以尝试将字符串或者字符转换为对应的byte类型。用到的一个方法是getBytes()。查看一下源码,看看是否转换为了数组类型,你可以验证一下。
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
我们也可以直接用代码进行操作。
fos.write("hello".getBytes());
字节缓冲输出流
public class BufferedOutputStream
extends FilterOutputStream
提供的主要的两个构造方法
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out, int size)
提供的一些具体的方法
void flush()
void write(byte[] b, int off, int len)
void write(int b)
**从构造方法可知,在使用这个缓冲输出流的时候,我们可以传一个输出流的对象进去。**
```javascript
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedOutputStream bos = new BufferedOutputStream(fos);
当然也可以直接这样进行传
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
注意这样的做法叫做匿名对象,而不是匿名内部类
然后后面的写入操作基本还是一样的。可以按照字节进行写入,也可以写入字节数组。但是加入缓冲区是一定会比较快的,
用输入输出实现数据的复制
可以进行文件到文件的复制
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IoDemo_3 {
public static void main(String args[]) throws IOException {
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_01");
int by;
while((by = fis.read())!=-1)
{
fos.write(by);
}
fos.close();
fis.close();
}
}
这样可以简单的实现复制。 前面已经说过,读取文件返回-1表示到达文件结尾。 上边这段代码也只是一次读取一个字节,其实你也可以自己加入一个字节数组。来加快读取写入的速度。 当然我们可以用缓冲区,缓冲流。进行相关的操作。可以用字节缓冲流一次读取一个字节的数据,也可以一次读取一个字节数组大小数据。
package io_demo;
import java.io.*;
public class IoDemo_06 {
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedOutputStream bos = new BufferedOutputStream(fos);
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedInputStream bis = new BufferedInputStream(fis);
int bu;
while((bu=bis.read())!=-1)
{
System.out.println((char)bu);
}
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
bis.close();
fis.close();
}
}
但是其实你也会发现,假如文本中有中文的话,其实字节流处理的话是不太方便的,会有乱码的问题。 单单从文本中读取中文的话,如果再转换为char类型是绝对会出现乱码,但是如果从文件复制到文件的话,是不会出现问题的。因为复制到文件的时候,底层会自动进行拼接 这样处理的话,其实在后面的字符流中可以得到非常方便的处理。
我们采用的字节流一般是默认utf-8编码的,这样的编码是会让一个汉字占用三个字节,但是如果是gbk编码的话,就会占用两个字节。但是无论采取何种编码,汉字的第一个字节总会是负数。这样的话,在数据复制的时候,就可以根据第一个字节是否是负数进行判断是汉字还是一般的字符。如果是第一个字节是负数的话,就会进行拼接。
当然是也是可以实现对视频数据地复制的。选一个视频的话。然后比较一下种复制的效率。
提供一段代码,方法仅供参考,可以自己进行优化。
package io_demo;
import java.io.*;
public class IoDemo_07 {
public static void main(String args[]) throws IOException {
long startTime = System.currentTimeMillis();
method();
method_2();
method3();
method4();
long endTime = System.currentTimeMillis();
System.out.println("共耗时:" + (endTime - startTime) + "毫秒秒");
}
private static void method() throws IOException {
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\1.mv");
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
private static void method_2() throws IOException {
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\1.mv");
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
}
private static void method3() throws IOException {
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int by;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
fis.close();
fos.close();
}
private static void method4() throws IOException {
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
bos.close();
bis.close();
fis.close();
fos.close();
}
}
事实其实字节缓冲流加上一次读取一个字节数组的话,就非常快了。
复制单级文件夹(文件夹中只含有文件,不包含其它文文件夹)
使用字节流来进行复制
这样的单级文件夹得话,复制的时候主要还是需要对文件夹下面的文件进行一个遍历。
package io_demo;
import java.io.*;
public class Only_doc {
public static void main(String args[]) throws IOException {
File srcFolder = new File("D:\\c_doc");
String srcFolderName = srcFolder.getName();
File destFolder = new File("E:\\java_doc\\src\\io_demo", srcFolderName);
if(!destFolder.exists())
{
destFolder.mkdir();
}
File[] listFiles = srcFolder.listFiles();
for(File srcFile:listFiles)
{
String srcFileName = srcFile.getName();
File destFile = new File(destFolder, srcFileName);
copyFile(srcFile,destFile);
}
}
private static void copyFile(File srcFile, File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bytes = new byte[1024];
int len;
while((len=bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
bos.close();
bis.close();
}
}
需要注意的是,复制到源文件的指定父级路径一定要存在。如果不存在的话,会报错。子级别路径可以自己设定。
复制多级文件夹(文件夹中包含文件夹)
package io_demo;
import java.io.*;
public class Mult_doc
{
public static void main(String args[]) throws IOException {
File srcfile = new File("D:\\BaiduNetdiskWorkspace");
File destFile = new File("E:\\java_doc\\src\\io_demo");
copyFolder(srcfile,destFile);
}
private static void copyFolder(File srcfile, File destFile) throws IOException {
if(srcfile.isDirectory())
{
String srcFileName = srcfile.getName();
File new_Folder = new File(destFile,srcFileName);
if(!new_Folder.exists())
{
new_Folder.mkdir();
}
File[] filearry = srcfile.listFiles();
for(File file:filearry)
{
copyFolder(file,new_Folder);
}
}else {
File file = new File(destFile,srcfile.getName());
copyFile(srcfile,file);
}
}
private static void copyFile(File srcFile,File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bytes = new byte[1024];
int len;
while((len= bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
}
}
这里面有一个基本的应用点就是采用了递归调用,基于判断是否遍历出来的是文件还是文件夹来采用是否进行继续遍历。
一个编码与解码的过程
这是一段简单的程序
package io_demo;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class IoDemo_09 {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "中国";
byte[] bytes = s.getBytes();
System.out.println(Arrays.toString(bytes));
byte[] bytes1 = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bytes1));
String ss = new String(bytes);
System.out.println(ss);
}
}
不要疑惑为什么getBytes()可以进行编码,给String()传入这个byte数组却又可以进行解码。直接区看看源码就好啦。可以进行跟进。
好,首先getBytes()。
然后我们跟进它的encode方法
可以看到它这里是指定了编码的。
然后String里面的解码时怎么回事呢?继续跟进。 然后要知道如何区跟进,我们跟进this。
从单词字面意思也可以看出这是一个解码的过程,但是我们可以继续进行跟进。 这里时一段解码的过程。所有的调用都可以进行溯源。
字符流说明
字符输入流读数据的方法
Reader是一个抽象类
public abstract class Readerextends Objectimplements Readable, Closeable
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
我们主要演示的实现类就是InputStreamReader
public class InputStreamReaderextends Reader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。 每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。 为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如: BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
当然这个缓冲流我们在后面举例
InputStreamReader的主要构造方法有这么几个
构造方法摘要 1:InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。 2:InputStreamReader(InputStream in, Charset cs) 创建使用给定字符集的 InputStreamReader。 3:InputStreamReader(InputStream in, CharsetDecoder dec) 4: 创建使用给定字符集解码器的 InputStreamReader。 InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
主要的方法
void close() 关闭该流并释放与之关联的所有资源。 String getEncoding() 返回此流使用的字符编码的名称。 int read() 读取单个字符。 int read(char[] cbuf, int offset, int length) 将字符读入数组中的某一部分。 boolean ready() 判断此流是否已经准备好用于读取。
从提供的构造方法的·参数我们可以了解到这个实现类基本的底层还是有字节流的实现。或者可以跟进源码。
我们可以认为这个字符输入流实现类基本实现的就是字节流加上编码的效果。
现在我们可以去看看它的读取数据的方法。(到源码中) 我们可以这样用字符流进行简单读取
package io_demo;
import java.io.*;
public class IODemo_10 {
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
int ch ;
while((ch=isr.read())!=-1)
{
System.out.println((char)ch);
}
isr.close();
}
}
当然还是可以进行一次读取一个字符数组的方法
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class IoDemo_12 {
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
char[] chs = new char[1024];
int len;
while ((len = isr.read(chs)) != -1) {
System.out.println(new String(chs,0,len));
}
isr.close();
}
}
字符缓冲输入流
我们照样可以使用缓冲流 这个缓冲流也继承了Reader类,基本上也提供了父类有的read()等方法,另外还有readline()方法。我们在后面的例子中使用。
package io_demo;
import java.io.*;
public class DemoPratice01 {
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_01"));
BufferedReader br= new BufferedReader(isr);
int ch;
while((ch=br.read())!=-1)
{
System.out.print((char)ch);
}
char[] chars = new char[1024];
int len;
while ((len = br.read(chars))!=-1)
{
System.out.println(new String(chars,0,len));
}
}
}
使用字符缓输入流可以实现一行一行读取的操作。
BufferedReader br = new BufferedReader(new FileReader("bw.newLine();"));
String line;
while((line = br.readLine())!=null)
{
System.out.println(line);
}
字符输出流写数据的方法
public abstract class Writerextends Objectimplements Appendable, Closeable, Flushable
写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
还是首先选一个实现类开始一些说明
public class OutputStreamWriterextends Writer
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如: Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
主要的构造方法
1:OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。 2:OutputStreamWriter(OutputStream out, Charset cs) 创建使用给定字符集的 OutputStreamWriter。 3:OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建使用给定字符集编码器的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。
常用的方法
void close() 关闭此流,但要先刷新它。 void flush() 刷新该流的缓冲。 String getEncoding() 返回此流使用的字符编码的名称。 void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 void write(int c) 写入单个字符。 void write(String str, int off, int len) 写入字符串的某一部分。
很明显这样的设计和输入流还是十分对称的。 下面还是简单的去使用它。
package io_demo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class IoDemo__ {
public static void main(String args[]) throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice1"));
osw.write("hello");
char[] chars = new char[]{'a','b','c'};
osw.write(chars,0,chars.length);
}
}
字符缓冲输出流
同样可以使用缓冲流
public class BufferedWriter extends Writer {
private Writer out;
private char cb[];
private int nChars, nextChar;
private static int defaultCharBufferSize = 8192;
缓冲流使用到的一些方法
主要的构造方法 一些普通方法
BufferedWriter bw = new BufferedWriter(osw);
同样还有进行读入行的方法 使用的时候这样将字符输出流对象进行传入就可以
BufferedWriter bw = new BufferedWriter(osw);
用输入输出实现数据的复制
package io_demo;
import java.io.*;
public class IoDemo13 {
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\IoDemo_12.java"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\Demo_Copy.java"));
int ch;
osw.close();
isr.close();
}
}
但是其实我们还可以使用这个两个类下的子类,更方便的进行操作。
package io_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemo_14 {
public static void main(String args[]) throws IOException {
FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice");
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_01");
int ch;
while((ch = fr.read())!=-1)
{
fw.write(ch);
}
char[] chars = new char[1024];
int len;
while((len= fr.read(chars))!=-1)
{
fw.write(chars,0,len);
}
fw.close();
fr.close();
}
}
同样该子类也可以结合字符缓冲流
package io_demo;
import java.io.*;
public class IoDemo15 {
public static void main(String args[]) throws IOException {
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("Hello");
bw.close();
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_01"));
int ch;
while((ch=br.read())!=-1)
{
System.out.println((char)ch);
}
char[] chars = new char[1024];
int len;
while((len=br.read(chars))!=-1)
{
System.out.println(new String(chars,0,len));
}
}
}
做一个随机点名器
假设文件中有这些名字,然后我们从中读取信息,并随机输出。这样就时一个随机点名器了。
package io_demo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
public class IoDemo18 {
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice"));
ArrayList<String> array = new ArrayList<String>();
String line;
while((line= br.readLine())!=null)
{
array.add(line);
}
br.close();
Random r = new Random();
int i = r.nextInt(array.size());
String name = array.get(i);
System.out.println("幸运者是"+name);
}
}
集合到文件(将对象信息拼接输入到文件)
首先我们创建一个学术类
package io_demo;
public class Student {
private String sid;
private String name;
private int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
测试代码
package io_demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
public class IoDemo19 {
public static void main(String args[]) throws IOException {
ArrayList<Student> array = new ArrayList<>();
Student s1 = new Student("001", "jgdabc", 19, "山西");
Student s2 = new Student("002", "兰舟千帆", 20, "山西");
Student s3 = new Student("003", "潇潇", 18, "湖北");
Student s4 = new Student("004", "小雨", 21, "河北");
Student s5 = new Student("005", "叶灵芸", 22, "浙江");
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\\\java_doc\\\\src\\\\io_demo\\\\Student.txt"));
for (Student s : array) {
StringBuilder sb = new StringBuilder();
sb.append(s.getSid()).append(",").append(s.getName())
.append(",").append(s.getAge()).append(",").append(s.getAddress());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
bw.close();
}
}
文件到集合(将文件中的人物属性通过对象存放于集合中,然后进行遍历)
还是基本的学术类
package io_demo;
public class Student {
private String sid;
private String name;
private int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
测试类
package io_demo;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class IoDemo_20 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student.txt"));
ArrayList<Student> array = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
String[] strArray = line.split(",");
Student s = new Student();
s.setSid(strArray[0]);
s.setName(strArray[1]);
s.setAge(Integer.parseInt(strArray[2]));
s.setAddress(strArray[3]);
array.add(s);
}
br.close();
for (Student s : array) {
System.out.println(s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
}
}
}
将学生成绩排序,并将数据写入文件中(排序采用多级条件)
我们可以先创建一个学生类
package io_demo;
public class Student01 {
private String name;
private int chinese;
private int math;
private int english;
public Student01() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
public Student01(String name, int chinese, int math, int english) {
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
}
public int getSum() {
return this.chinese + this.english + this.math;
}
}
创建学生类的唯一作用就是来获取对象,存放相关的属性。
然后我们再创建一个测主类
package io_demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class Student01_Demo {
public static void main(String[] args) throws IOException {
TreeSet<Student01> ts = new TreeSet<>(new Comparator<Student01>() {
@Override
public int compare(Student01 s1, Student01 s2) {
int num = s2.getSum() - s1.getSum();
int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
int num4 = num3 == 0 ? s1.getName().compareTo(s2.getName()) : num3;
return num4;
}
});
for (int i = 0; i < 5; i++) {
Scanner sc = new Scanner(System.in);
System.out.println("请录入第" + (i + 1) + "个学生信息");
System.out.println("姓名:");
String name = sc.nextLine();
System.out.println("语文成绩:");
int chinese = sc.nextInt();
System.out.println("数学成绩:");
int math = sc.nextInt();
System.out.println("英语成绩:");
int english = sc.nextInt();
Student01 s = new Student01();
s.setName(name);
s.setChinese(chinese);
s.setMath(math);
s.setEnglish(english);
ts.add(s);
}
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\Student01.txt"));
for (Student01 s : ts) {
StringBuilder sb = new StringBuilder();
sb.append(s.getName()).append(",").append(s.getChinese())
.append(",").append(s.getMath()).append(",").append(s.getEnglish()).append(",").append(s.getSum());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
bw.close();
}
}
标准输入输出流
无论是在api中查看,还是在Ststem中查看,我们都可以了解到System有标准输入和输出流的定义,并且声明为static,于是肯定是可以直接进行按照类名调用的。
api对System类有以下说明
在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。
作为了System类的两个成员变量。这两个流的主要是进行键盘和内存的交互。
标准输入流
public abstract class InputStreamextends Objectimplements Closeable此抽象类是表示字节输入流的所有类的超类。
这个抽象类唯一的构造方法就是InputStream()这个无参构造方法。
简单了解下
package io_demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;
public class IoDemo_21 {
public static void main(String args[]) throws IOException {
InputStream in = System.in;
int by;
while((by=in.read())!=-1)
{
System.out.print((char)by);
}
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
System.out.println("请输入一个字符串:");
String line = br.readLine();
System.out.println("你输入的字符串是"+line);
System.out.println("请输入一个整数:");
int i = Integer.parseInt(br.readLine());
System.out.println("你输入的整数是:"+i);
Scanner sc = new Scanner(System.in);
}
}
平常我们都会使用Scanner这个下面的方法去进行键盘输入数据,但是弄清楚它的本质也是一件非常有意义的事情。 它的底层还是使用了字节输入流。
这样就与流的知识衔接起来了。
标准输出流
public class PrintStreamextends FilterOutputStreamimplements Appendable, Closeable
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 (’\n’)。 PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
所具有的一些构造方法
PrintStream(File file) 创建具有指定文件且不带自动行刷新的新打印流。 PrintStream(File file, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。 PrintStream(OutputStream out) 创建新的打印流。 PrintStream(OutputStream out, boolean autoFlush) 创建新的打印流。 PrintStream(OutputStream out, boolean autoFlush, String encoding) 创建新的打印流。 PrintStream(String fileName) 创建具有指定文件名称且不带自动行刷新的新打印流。 PrintStream(String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
这样我们得到的信息是,我么可以在参数中按照布尔值确定是否进行自动刷新,另外还可以进行指定编码。
这个和标准输入流在System类中定义的格式是一样的。底层话,其实同样也是字节流。简单看下怎么使用。
PrintStream out = System.out;
out.print("hello");
但是你其实也可以向文件中进行输出,也就是将数据输入到文件中。
PrintStream ps = new PrintStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
ps.write(97);
ps.print(97);
ps.close();
PrintWriter pW = new PrintWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
pW.write("hello");
pW.flush();
pW.println("Hello");
pW.flush();
PrintWriter pw = new PrintWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt"),true);
pw.println("Hello");
pw.println("world"
对象序列化流于反序列化流
什么是序列化与反序列化
=java序列化,就是指吧java对象转换为字节序列的过程。而反序列自然就是将字节对象恢复为java对象。==
这样做的意义在哪呢?对象进行序列化,会转换为字节流,这样在网络上传输,或者是进行保存为本地文件都是非常方便的。反序列很明显就是进行对象的重构。
其实你可以和通信联系在一起。网络上的文本,图片,视频,音频都是通过二进制进行传输的,我们的java所创建的对象在传输的时候也应该进行序列化,转换为字节流,然后通过网络,io传入,当我们的对象序列传输完成后,对方进行反序列化,就可以读取到数据内容。
对象序列化流( ObjectOutputStream)
api对这个类有很多的说明,主要说明
public class ObjectOutputStreamextends OutputStreamimplements ObjectOutput, ObjectStreamConstants
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
看一下简单使用
首先我们需要一个对象类
package io_demo;
import java.io.Serializable;
public class Student implements Serializable {
private String sid;
private String name;
int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
然后测试主类
package io_demo;
import java.io.*;
public class IoDemo_23 {
public static void main(String args[]) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
Student s = new Student("001","刘备",28,"汉中");
oos.writeObject(s);
oos.close();
}
}
为什么Student需要实现Serializable,api有说明
只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
为什么没有具体实现? 因为这个接口本身没有具体实现,我们不要要实现什么,这个只是作为一个标识而已。
对象反序列化流(.ObjectInputStream)
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
说明如下
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。 ObjectOutputStream和ObjectInputStream可以分别为与FileOutputStream和FileInputStream一起使用的对象图提供持久性存储的应用程序。 ObjectInputStream用于恢复先前序列化的对象。 其他用途包括使用套接字流在主机之间传递对象,或者在远程通信系统中进行封送和解组参数和参数。
首先还是原来的Student对象类
package io_demo;
import java.io.Serializable;
public class Student implements Serializable {
private String sid;
private String name;
int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
对的,你虽然是从文件中读取,但是如果你不使用构建的对象的话。是绝对无法成功进行反序列化的。
主要的测试代码
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class IoDemo_24 {
public static void main(String args[]) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
Object o = ois.readObject();
Student s = (Student)o;
System.out.println(s.getName()+","+s.getAge());
ois.close();
}
}
这样感觉有时候很像加密与解密的样子。
如果在反序列话的时候原来对象类有修改?
我现在就修改一个属性。我让age属性权限有所变化,变为私有属性
**结果反序列化就报错了,为什么?你看报错 **
再清晰一点就是
local class incompatible: stream classdesc serialVersionUID = -8624680451455869930, local class serialVersionUID = 6474701452341618882
serialVersionUID是什么?(为什么修改对象类会反序列化失败?)
serialVersionUID是序列化的时候会生成的一个的版本标识,。当我们对对象进行修改后,那么这个序列化的版本标识的值就会与之前的不一样了。这样会导致字节流的版本标识与本地的版本标识不一样,不一样就会反序列化失败。
解决修改对象类修改反序列化失败的问题
我们就自己定义一个常量。让这个版本标识符不再变化 注意,一定是在对象类中,不是在测试类。
不想让某些属性被序列化?
这样就可以
集合(Properties)
介绍这个集合的原因就是这个集合可以和流结合使用。 这个集合的具体说明就不在本文详细说明了,具体的在之前的集合文中再补充吧。简单说明一下和其用法。
这个集合首先是Map下的子类,所以也是双列集合。但是不支持泛型。用代码来看看具体使用。简单演示。
package io_demo;
import java.util.Properties;
import java.util.Set;
public class IoDemo_25 {
public static void main(String[] args) {
Properties pro = new Properties();
pro.put(001,"jgdabc");
pro.put(002,"兰舟千帆");
pro.put(002,"lym");
Set<Object> KeySet = pro.keySet();
for(Object key:KeySet)
{
Object value = pro.get(key);
System.out.print(key+","+value);
}
pro.setProperty("000","jgdabc");
pro.getProperty("000");
Set<String> names = pro.stringPropertyNames();
for(String key:names)
{
String value = pro.getProperty(key);
System.out.println(key+value);
}
}
}
==它的put()方法键是Object类型的,setProperty()方法的键必须是String类型。==这个需要注意。
集合到文件,文件到集合
package io_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class IoDemo_26 {
public static void main(String[] args) throws IOException {
myStore();
myLoad();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
prop.load(fr);
fr.close();
System.out.print(prop);
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("000","jack");
prop.setProperty("001","jgdabc");
prop.setProperty("002","john");
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
prop.store(fw,null);
}
}
做一个猜数字游戏
文件中给一个count=0,然后用这个集合读取到数据(需要转换为int类型,然后根据值得限制,限制玩的次数。当然没每次运行完毕都会count+1,运行三次就不能玩了。这个和一直在交互是不一样的。) 首先一个猜数字游戏类
package java_practice;
import java.util.Scanner;
public class GuessNumber {
public static void start()
{
Scanner input = new Scanner(System.in);
int number = (int) (Math.random() * 100)+1;
int guess;
int count = 0;
System.out.println("我心里有一个0到100之间的整数,你猜是什么?");
do {
guess = input.nextInt();
if (number < guess) {
System.out.println("大了点,再猜!");
count++;
} else if (number > guess) {
System.out.println("小了点,再猜!");
count++;
} else {
count++;
break;
}
} while (true);
System.out.println("这个数字是"+number);
System.out.println("您猜的次数是"+count);
if (count == 1) {
System.out.println("你太聪明了!");
} else if (count >= 2 && count <= 5) {
System.out.println("不错,再接再厉!");
} else {
System.out.println("要努力啊!");
}
}
}
然后一个测试主类
package io_demo;
import java_practice.GuessNumber;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLOutput;
import java.util.Properties;
public class IoDemo_27 {
public static void main(String args[]) throws IOException {
GuessNumber guessNumber = new GuessNumber();
Properties prop = new Properties();
FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\count.txt");
prop.load(fr);
fr.close();
String count = prop.getProperty("count");
int number = Integer.parseInt(count);
if(number>=3)
{
System.out.println("游戏试玩结束,想玩请充值");
}else
{
guessNumber.start();
prop.setProperty("count",String.valueOf(number));
number++;
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\count.txt");
prop.store(fw,null);
fw.close();
}
}
}
注意一定要自己建立一个文件,里面存一个标记值。这样功能就可以实现了。
IO异常处理
可以根据自己的需要选择将数据输入到文件中还是将信息输出到控制台。
抛出异常
package io_demo;
import java.io.*;
public class IODemo_10 {
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
OutputStreamWriter osw = new OutputStreamWriter(fos);
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
osw.write("中国");
osw.close();
int ch ;
while((ch=isr.read())!=-1)
{
System.out.println((char)ch);
}
isr.close();
}
}
标准捕获
package io_demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemoException {
public static void main(String args[])
{
FileReader fr=null;
FileWriter fw=null;
try {
fr= new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
fw= new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
char[] chs = new char[1024];
int len;
while((len = fr.read())!=-1)
{
fw.write(chs,0,len);
}
}catch (IOException e)
{
e.printStackTrace();
}finally {
if(fw!=null)
try {
fw.close();
}catch (IOException e)
{
e.printStackTrace();
}
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
但是这样的处理还是比较麻烦的
捕获处理改进版(jdk7特性)
package io_demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemoException01 {
public static void main(String args[])
{
try ( FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\Student.txt");) {
char[] chars = new char[1024];
int len;
while ((len = fr.read()) != -1) {
fw.write(chars, 0, len);
}
}catch (IOException e)
{
e.printStackTrace();
}
}
;
}
这样的处理还是会自动释放资源,不需要人为关闭。
就先介绍这么多。之后有问题会继续更改补充。
我的主页
|