IO流用来处理设备之间的数据传输
流按操作数据分为两种:字节流与字符流
流按流向分为:输入流和输出流
输入流和输出流相对于内存设备而言:
将外设中的数据读取到内存中:输入
将内存的数据写入到外设中:输出
字符流的由来:
其实就是字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字,
再对这个文字进行操作。简单说:字节流+编码表
字符流的两个顶层父类:
1.InputStream 2.OutputStream
字符流的两个顶层父类:
1.Reader 2.Writer
这些体系的子类都以父类名作为后缀,而且子类名的前缀就是该对象的功能
如果要操作文字数据,优先考虑字符流,而且要将数据从内存写到硬盘上,要使用字符流中的输出流。(Writer)
字符流:
FileWriter:
示例1:
下图中第3行创建一个可以往文件中写入字符数据的字符输出流对象,既然是往一个文件中写入文件数据,那么再创建对象时,就必须明确该文件(用于存储数据的目的地),所以构造函数中指定的形参为写入的文件地址和文件名。没有指定地址则默认为当前目录。如果指定的文件不存在,则会自动创建,如果文件存在,则会被覆盖(覆盖的意思是原文件中的数据会被清空,被一个新建立的文件替换)。第4行用write方法把字符串"abc"写入文件当中,此时查看文件,会发现文件中并没有写入的字符串"abc",是因为此时的字符串是先存储在临时缓冲区中,所以必须调用flush方法刷新缓冲区,把数据立即写入预期目标。如果调用close方法,则会先刷新缓冲区,即调用flush()方法,然后再关闭流,在关闭该流之后,再调用 write() 或 flush() 将导致抛出 IOException
public static void demo1() throws IOException
{
FileWriter fw = new FileWriter("demo.txt");
fw.write("abc");
// fw.flush();
fw.close();
}
示例2:
如果在写入数据的时候需要换行,在windows系统下的换行符号是“\r\t”。也可以如下图第1行,声明一个常量,用System类中的getProperty("line.separator")方法获取当前系统的换行符,这样就不需要在意当前的操作系统了。如果要在一个已有数据的文件中追加数据,需要在构造函数中加入true表示对文件进行续写,如第7行,否则新写入的数据会覆盖原数据。
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void demo2() throws IOException
{
FileWriter fw = new FileWriter("demo2.txt");
fw.write("abc" + LINE_SEPARATOR + "def");
fw.close();
FileWriter fw1 = new FileWriter("demo2.txt", true);
fw1.write("haha");
fw1.close();
}
FileWriter IO异常处理
实例3:
下图中第6、7、18行都可能出现异常,所以用try-catch处理异常。无论有没有出现异常,都要执行第18行,所以把它放在了finally语句块中,为了能在finally中使用fw对象引用,就把fw定义在try语句块之外,如第3行。如果第6行建立对象时出现了异常,那么fw就不能参考至对象,就会出现空指针异常java.lang.NullPointerException,所以必须加入判断fw是否为空,如第15行所示。
public static void demo3()
{
FileWriter fw = null;
try
{
fw = new FileWriter("k:\\demo2.txt");
fw.write("abcdef");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if(fw != null)
try
{
fw.close();
}
catch(IOException e)
{
throw new RuntimeException("关闭失败");
}
}
}
FileReader:
示例1:
下图中第3行创建读取字符数据的流对象,在构造函数中明确了要读取的文件,即用一个读取流关联一个已存在的文件。第4行用read()方法读取一个字符,返回的是一个整数。第5行中打印出来的值为97,字符a对应的ASCII码,第6行把97转换成char类型的,打印出来为a。如果读取已经达到流的末尾,就返回-1。
public static void demo1() throws IOException
{
FileReader fr = new FileReader("demo.txt");
int ch = fr.read();
System.out.println(ch);//97
System.out.println((char)ch);//a
fr.close();
}
示例2:
下图中使用的是read()方法读取多个字符,存储在一个字符数组中。第5行返回的是实际读取到的个数,并把读到的字符存储在buf数组中。如果文件中字符的长度大于数组长度时,那么一次最多只能读到的个数等于数组的长度。如果读到流的末尾没有数据了,会返回-1。所以可以用循环的方式来读取,如第9-11行。第11行是把char类型的数组buf转换成String类型,从0号下标开始,转换的个数为len。
public static void demo2() throws IOException
{
FileReader fr = new FileReader("demo.txt");
char[] buf = new char[5];
// int num = fr.read(buf);
// System.out.println("读到的个数 = " + num);//读到的个数 = 5
// System.out.println(new String(buf));//abcde
int len = 0;
while((len = fr.read(buf)) != -1)
{
System.out.println(new String(buf, 0, len));
/*输出 abcde
f*/
}
}
为了提高写入的效率,可以使用字符流的缓冲区
如下图中,第4行创建一个字符流的缓冲区对象,并和指定要被缓冲的流对象相关联的。第5行使用缓冲区的写入方法将数据先写入到缓冲区中。第6行使用缓冲区的方法,写入一个换行分隔符。第8行使用缓冲区的刷新方法将数据刷到目的地中。第9行关闭缓冲区,其实就是关闭流,即调用fw.close();
public static void demo1() throws IOException
{
FileWriter fw = new FileWriter("demo.txt");
BufferedWriter bufw = new BufferedWriter(fw);
bufw.write("abcdefg");
bufw.newLine();
bufw.write("hijk");
bufw.flush();
bufw.close();
}
如下图中,第4行创建了一个读字符流的缓冲区对象,并和指定要被缓冲的流对象关联。第5行使用缓冲区对象的readLine()方法每次读取一行(行是以回车换行符来区分的),并返回字符串。当数据都读完是,会返回null。
public static void demo1() throws IOException
{
FileReader fr = new FileReader("demo.txt");
BufferedReader bufr = new BufferedReader(fr);
String line1 = bufr.readLine();
System.out.println(line1);//abcdefg
String line2 = bufr.readLine();
System.out.println(line2);//hijk
String line3 = bufr.readLine();
System.out.println(line3);//null
fr.close();
}
字符流:
FileOutputStream:
下图中第6行把字符串“abcdefg”转换成byte数组,因为字符流只能操作字节数组。使用write方法写数据后,不需要使用临时缓冲,会直接将数据写到目的地中,这是字节流的一个特点。
public static void demo_write() throws IOException
{
//创建字节输出流对象,用于操作文件
FileOutputStream fos = new FileOutputStream("bytedemo.txt");
//写数据,直接写入到了目的地中
fos.write("abcdefg".getBytes());
fos.close();//关闭资源
}
下图中第3行创建了字节输入流对象,构造函数中关联了要操作的文件。第四行创建了一个字节数组,长度为fis.available(),该方法是获得当前操作文件的大小,对于小文件可以这样使用,对于大文件不能这样使用,不然会导致内存空间不足。第7行把字节数组转换成为字符串输出。
public static void demo_read() throws IOException
{
FileInputStream fis = new FileInputStream("demo.txt");
byte[] buf = new byte[fis.available()];
System.out.println(fis.available());
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
字节流复制MP3文件的四种方法:
方法一:
使用字节输出流和字节输入流,建立一个长度为1024的字节数组来存放数据。使用while循环进行读写。
public static void copy_1() throws IOException
{
FileInputStream fis = new FileInputStream("D:\\酷狗音乐\\KuGou\\在他乡.mp3");
FileOutputStream fos = new FileOutputStream("D:\\1.mp3");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1)
{
fos.write(buf, 0, len);
}
fos.close();
fis.close();
}
方法二:
使用缓冲区来进行数据的读写,加快了速度。
public static void copy_2() throws IOException
{
FileInputStream fis = new FileInputStream("D:\\酷狗音乐\\KuGou\\在他乡.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("D:\\2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch = bufis.read()) != -1)
{
bufos.write(ch);
}
bufos.close();
bufis.close();
}
方法三:
直接使用available()方法获取文件的大小,建立一个长度刚刚好的字节数组,进行一次性读,一次性写。
public static void copy_3() throws IOException
{
FileInputStream fis = new FileInputStream("D:\\酷狗音乐\\KuGou\\在他乡.mp3");
FileOutputStream fos = new FileOutputStream("D:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
方法四:
使用字节输入流,字节输出流,读取一个字节,写入一个字节,这样的操作很慢,效率低。
public static void copy_4() throws IOException
{
FileInputStream fis = new FileInputStream("D:\\酷狗音乐\\KuGou\\在他乡.mp3");
FileOutputStream fos = new FileOutputStream("D:\\4.mp3");
int ch = 0;
while((ch = fis.read()) != -1)
{
fos.write(ch);
}
fos.close();
fis.close();
}