第一讲 字节流
1)字节输出流操作步骤:
A:创建字节输出流对象
B:调用写数据的方法
C:释放资源
练习如下:
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
// FileOutputStream fos = new FileOutputStream("a.txt",true); //为true时,就追加
FileOutputStream fos = new FileOutputStream("a.txt");
// 调用写数据的方法
<strong>//方法一: 写入一个字节</strong>
// fos.write(97);
// fos.write(98);
// fos.write(99);
// fos.flush(); 字节流可以不用刷新,因为底层存储的就是字节
<strong>// 方法二:写一个字节数组</strong>
// byte[] bys = { 97, 98, 99, 100, 101 };
byte[] bys = "abcde".getBytes();
// fos.write(bys);
// 写一个字节数组的一部分
fos.write(bys, 0, 2);
// 释放资源
fos.close();
}
}
2)字节输入流操作步骤:
A:创建字节输入流对象
B:调用读取数据的方式,并显示
C:释放资源
练习如下:
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("b.txt");
// 调用读取数据的方式,并显示
// 方式1
// int by = 0;
// while ((by = fis.read()) != -1) {
// System.out.println(by);
// }
// 方式2
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
<strong>System.out.print<span style="color:#cc0000;">(new String(bys, 0, len));</span></strong>
}
// 释放资源
fis.close();
}
}
3)用字节流复制文本文件:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 用字节流复制文本文件:
* 把项目路径下的FileWriterDemo.java复制到f:\\Copy01.java中。
* 数据源:
* FileWriterDemo.java-----读取数据---FileInputStream
* 目的地
* f:\\Copy01.java------写入数据----FileOutputStream
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
//封装数据源
FileInputStream fis=new FileInputStream("FileWriterDemo.java");
//封装目的地
FileOutputStream fos=new FileOutputStream("f:\\Copy01.java");
//基本读写操作
//方式一
// int by=0;
// while((by=fis.read())!=-1){
// fos.write(by);
// }
//方式二
byte[] bys=new byte[1024];
int len=0;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
//释放资源
fos.close();
fis.close();
}
}
运行后结果为:
4)分别用字节流和字符流复制图片
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 复制图片:将项目路径下的psb12.jpg复制到f:\\psb.jpg
* A:用字节流
* B:用字符流
*
* 二进制流数据:图片,视频,音频等。
* 由于二进制的合并为字符流,在我们使用的编码表中没有对应的数据,那么将来在写回去的时候,就会有数据的丢失。
* 所以,二进制数据只能使用字节流进行操作。
*/
public class CopyImage {
public static void main(String[] args) throws IOException {
method1();
method2();
}
//用字节流一次一个字节数组
private static void method2() throws IOException {
//封装数据源和目的地
FileInputStream fis=new FileInputStream("psb12.jpg");
FileOutputStream fos=new FileOutputStream("f:\\psb.jpg");
//读写
byte[] bys=new byte[1024];
int len=0;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
//释放资源
fos.close();
fis.close();
}
//用字符流一次一个字符
private static void method1() throws IOException {
//封装数据源和目的地
FileReader fr=new FileReader("psb12.jpg");
FileWriter fw=new FileWriter("f:\\psb1.jpg");
//读写数据
int ch=0;
while((ch=fr.read())!=-1){
fw.write(ch);
fw.flush();
}
//释放资源
fw.close();
fr.close();
}
}
先把method1()注释掉,运行后在F盘,可以找到复制成功的图片,为:
再把method2()注释掉,放开method1(),运行后找不到图片,即复制不成功。
二进制流数据:图片,视频,音频等。
由于二进制的合并为字符流,在我们使用的编码表中没有对应的数据,那么将来在写回去的时候,就会有数据的丢失。
所以,二进制数据只能使用字节流进行操作。
总结:以后在复制东西的时候,如果能用Windows自带的记事本打开,且能读懂的就用字符流,除此之外都用字节流。
第二讲 BufferedWriter和BufferedReader
概述:我们开始自己定义数组,给出缓冲区大小,是为了提高效率。那么java在设计类的时候,它也考虑到了这一点,所以就提供了一种高效的流,带缓冲区的流。
BufferedWriter : 写入数据
BufferedReader : 读取数据
1)BufferedWriter
构造方法: BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
为什么传递的是Writer呢?
因为 BufferedWriter这种流,被称为缓冲流,它只是提供数据的缓冲功能,真正的读写还的靠别人。所以我们这里将使用 BufferedWriter作为参数传递。缓冲区只提供缓冲功能,没有真正的读写。
基本流:能直接进行读写的流对象。
高效流:站在基本流的基础上,提供一些特殊的功能。(可以理解为处理流)
(1)、BufferedWriter的读取数据和写入数据练习:
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
//创建缓冲流对象
// FileWriter fw = new FileWriter("c.txt"); //方式一
// BufferedWriter bw = new BufferedWriter(fw);
BufferedWriter bw=new BufferedWriter(new FileWriter("d.txt")); //方式二
//写入数据
bw.write("我要进黑马!");
bw.flush();
//释放资源
//fw.close(); //这个不用关闭,因为bw关闭就相当于把fw也关了
bw.close();
}
}
注:方式一和方式二都行,但是一般我们都用方式二,内部类来创建缓冲流对象
(2)同理,字符缓冲流读取数据的练习就容易多啦~~
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
// 创建字符缓冲流对象
BufferedReader br = new BufferedReader(new FileReader("c.txt"));
// 读取数据
// int ch = 0;
// while ((ch = br.read()) != -1) {
// System.out.print((char) ch);
// }
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
System.out.print(new String(chs, 0, len));
}
// 释放资源
br.close();
}
}
(3)综上两个小练习,写一个高效流复制文本文件的练习。
/*
* 用字符高效流复制文本文件。
* 左边:一次一个字符
* 右边:一次一个字符数组
*
* 数据源:
* FileOutputStreamDemo.java
* 目的地:
* Copy.java
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
// 封装数据源和目的地
BufferedReader br = new BufferedReader(new FileReader(
"FileOutputStreamDemo.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
// 一次一个字符
// int ch = 0;
// while ((ch = br.read()) != -1) {
// bw.write(ch);
// // bw.flush();
// }
// 一次一个字符数组
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
}
// 释放资源
bw.close();
br.close();
}
}
(4)写一个工具类,专门用于复制文本文件,图片等。
/**
*
* @author itcast 工具类,专门用于复制文本文件,图片等
*/
public class Copys {
private Copys() {
}
/**
* 复制文本文件
*
* @param src
* 数据源
* @param dest
* 目的地
*/
public static void copyFile(String src, String dest) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(src));
BufferedWriter bw = new BufferedWriter(new FileWriter(dest));
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
bw.flush();
}
bw.close();
br.close();
}
/**
* 复制二进制流数据
*
* @param src
* 数据源
* @param dest
* 目的地
*/
public static void copyBinaryData(String src, String dest)
throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
src));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(dest));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
bis.close();
}
}
//测试类
public class CopysTest {
public static void main(String[] args) throws IOException {
// 需求:复制c:\\a.txt到b.txt
// Copys.copyFile("c:\\a.txt", "d:\\b.txt");
Copys.copyBinaryData("c:\\a.txt", "d:\\b.txt");
Copys.copyBinaryData("d:\\干程序员前后.bmp", "cxy.bmp");
}
}
(5)案例:复制MP3,加入异常处理标准格式。
/*
* 复制MP3,加入异常处理标准格式。
*
* 数据源:
* f:\\大城小爱.mp3
* 目的地:
* copy.mp3
*/
public class CopyMP3 {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream("f:\\大城小爱.mp3"));
bos = new BufferedOutputStream(new FileOutputStream("copy.mp3"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注:上面这种格式,在面试的时候建议这样写,在实际的开发中,根据实际情况而写。
/**
* 需求:用字符缓冲流的特殊功能复制文本文件
* 数据源:
* FileWriterDemo.java -- Reader -- FileReader -- BufferedReader
* 目的地:
* Copy.java -- Writer -- FileWriter -- BufferedWriter
*/
public class BufferedCopyFile {
public static void main(String[] args) throws IOException{
BufferedReader br=new BufferedReader(new FileReader("FileWriterDemo.java"));
BufferedWriter bw=new BufferedWriter(new FileWriter("Copy.java"));
String line=null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
}
注:以后如果要实现复制文本文件,上面这种格式是比较提倡的
到此,所有的基本的流都已经介绍完了,大家留意的话,可能会发现这篇博客中代码占了好大一部分,因为这些知识点比较特殊,我想先把应用介绍完了,通过一个个的小练习,然后做一个总结:
1:IO流
(1)分类
字节流:
输入流:
InputStream
int read()
int read(byte[] bys)
FileInputStream
BufferedInputStream
输出流:
OutputStream
write(int by)
write(byte[] bys,int index,int len)
FileOutputStream
BufferedOutputStream
字符流:
输入流:
Reader
int read()
int read(char[] chs)
FileReader
BufferedReader
String readLine()
输出流:
Writer
write(int ch)
write(char[] chs,int index,int len)
FileWriter
BufferedWriter
write(String line)
void newLine()
(2)这么多,到底该使用谁呢?
答:用记事本打开能读懂的,用字符流,否则用字节流。另外,如果你根本就不知道,就用字节流。
(3)复制文本文件的所有方式:
9种方式:
字节流:
4种
基本流一次读写一个字节
基本流一次读写一个字节数组
高效流一次读写一个字节
高效流一次读写一个字节数组
字符流:
5种
基本流一次读写一个字符
基本流一次读写一个字符数组
高效流一次读写一个字符
高效流一次读写一个字符数组
高效流一次读写一个字符串
老师总结的:
一般来说,明明知道是文本文件,那么,肯定不用字节流。那么,如果是使用字符流,有5种方式,
选择哪一种呢? 一般都选择高效流读写一个字符串的方式。
(4)复制二进制流数据:(图片,视频,音频等)
字节流:
4种
基本流一次读写一个字节
基本流一次读写一个字节数组
高效流一次读写一个字节
高效流一次读写一个字节数组
一般来说,我们选择使用高效流一次读写一个字节数组
接下来写几个案例:
1、需求:键盘录入数据写入文本文件。
/*
* 需求:键盘录入数据写入文本文件。
*
* 数据源:
* 键盘录入
* 目的地:
* 文本文件
*
* 键盘录入数据,一定要有结束标记。自己定义一个。
*/
public class ScannerDemo {
public static void main(String[] args) throws IOException {
// 封装数据源
Scanner sc = new Scanner(System.in);
// 封装目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("sc.txt"));
String line = null;
while((line=sc.nextLine())!=null){//"null"
if("over".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
sc.close();
}
}
2、需求:在ArrayList里面存储了3个字符串元素,请把这三个元素写入文本文件。并在每个元素后面加入换行。
/*
* 需求:在ArrayList里面存储了3个字符串元素,请把这三个元素写入文本文件。并在每个元素后面加入换行。
*
* 数据源:
* ArrayList
* 目的地:
* BufferedWriter
*/
public class ArrayListDemo {
public static void main(String[] args) throws IOException {
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
BufferedWriter bw = new BufferedWriter(new FileWriter("array.txt"));
//遍历集合
for(String str : array){
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
}
}
3、 需求:键盘录入数据写入文本文件。
/*
* 需求:键盘录入数据写入文本文件。
*
* 数据源:
* 键盘录入
* 目的地:
* 文本文件
*
* <strong><span style="color:#cc0000;">键盘录入数据,一定要有结束标记。自己定义一个。</span></strong>
*/
public class ScannerDemo {
public static void main(String[] args) throws IOException {
// 封装数据源
Scanner sc = new Scanner(System.in);
// 封装目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("sc.txt"));
String line = null;
while((line=sc.nextLine())!=null){//"null"
if("over".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
sc.close();
}
}
(5)读取键盘录入
标准输入输出流:
System.in:对应的是标准输入设备,键盘。
System.out:对应的是标准输出设备,控制台(屏幕)。
System.in 的类型是 InputStream。
System.out 的类型是 PrintStream。
转换流:
个人理解:学过了IO流,如果我们想实现通过键盘录入的方式,往某个文本文件中写数据,而字节的输入流有两种读取方式:
方式1:一次读取一个字节,但是,这样的话,我们每次都需要对读取到的数据进行保存,将来判断是否一行结束,麻烦。
方式2:一次读取一个字节数组,但是,这个时候,最大的问题是,数组的长度定义为多大不好确定。
所以,这两种方式都不够好,那有没有一种方案,直接一次读取一行数据。我们就想到了字符缓冲输入流的readLine()方法,按照默认的写法去做了这件事情,但是,发现报错了,因为BufferedReader需要的是一个字符流,而你现在有的却是一个字节流。假如能有一个流对象能够实现字节流转成字符流,我们就可以用了。所以转换流就出现啦~~
转换流:字符流与字节流之间的桥梁,方便了字符流与字节流之间的操作。
InputStreamReader 将字节流通向字符流
读取键盘录入步骤
1.、获取键盘录入对象。
InputStream in = System.in;
2、 将字节流对象转成字符流对象,使用转换流。
InputStreamReader isr = new InputStreamReader(in);
3、 为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReader br = new BufferedReader(isr);
键盘录入最常见写法
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter 字符流通向字节流
字符通向字节:录入的是字符,存到硬盘上的是字节。
/*
* 数据源:
* 键盘录入 -- System.in -- InputStream
* 目的地:
* 文本文件 -- BufferedWriter
*
* 转换流:InputStreamReader 是字节流通向字符流的桥梁
*/
public class SystemInDemo {
public static void main(String[] args)throws IOException {
// 封装数据源
InputStream is=System.in;
InputStreamReader isr=new InputStreamReader(is);
BufferedReader br=new BufferedReader(isr);
//封装目的地
BufferedWriter bw=new BufferedWriter(new FileWriter("syy.txt"));
//写数据
String line=null;
while((line=br.readLine())!=null){
//主要是键盘录入,就需自己定义结束条件,遇到Over结束
if("over".equals(line)){
break;
}
bw.write(line); //将读取的一整行写入文本
bw.newLine(); //换行
bw.flush(); //刷新缓冲区
}
//关闭流对象
bw.close();
br.close();
}
}
/*
* 需求:把文本文件的数据通过标准输出流的方式显示在控制台。
*
* 数据源:
* 文本文件 -- BufferedReader
* 目的地:
* 控制台显示 -- System.out -- PrintStream -- OutputStream
*
* OutputStreamWriter 是字符流通向字节流的桥梁
*/
public class systemOut {
public static void main(String[] args)throws IOException {
// 封装数据源
BufferedReader br=new BufferedReader(new FileReader("syy.txt"));
// 封装目的地
OutputStream os=System.out;
OutputStreamWriter osw=new OutputStreamWriter(os);
BufferedWriter bw=new BufferedWriter(osw);
String line=null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
}
PrintStream :字节打印流
PrintWriter :字符打印流
A:可以写入任意类型的数据。
B:可以自动刷新。必须先启动,并且是使用println,printf及format方法才有效。
C:可以直接对文件进行写入。
哪些流对象是可以直接对文件进行操作的?
看构造方法,是否有同时接受File和String类型的参数构造。如果同时有String和File的构造参数,那么该流对象就可以读写文件。
注意:打印流只有写数据的,没有读数据的。
构造方法:PrintWriter (String fileName)
用打印流改进复制文本文件的操作
数据源:
c:\\a.txt
目的地:
d:\\b.txt
BufferedReader br = new BufferedReader(new FileReader("c:\\a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("d:\\b.txt"),true);
String line = null;
while((line=br.readLine())!=null)
{
pw.println(line); //写数据,换行,刷新 ,而pw.print(line); 则不能
}
pw.close();
br.close();
小练习:
/*
* 复制文本文件:
* 数据源:
* PrintWriterDemo.java -- BufferedReader
* 目的地:
* Copy.java -- PrintWriter改写
*
* 注意:
* A:读取文件的时候,如果是文件夹,会出现:FileNotFoundException: test (拒绝访问。)
* B:写文件的时候,如果没有后缀名,它也会自动生成一个文件,而这个种类型的文件通过记事本类型的软件是可以打开的。
*/
public class PrintWriterTest {
public static void main(String[] args) throws IOException {
// 封装数据源
BufferedReader br = new BufferedReader(new FileReader(
"PrintWriterDemo.java"));
// 封装目的地
PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true);
String line = null;
while((line=br.readLine())!=null){
pw.println(line);
}
pw.close();
br.close();
}
}
序列化流:
(1) 序列化:把对象按照流一样的方式在网络中传输,或者存储到文本文件
反序列化:把流数据还原成对象
把对象存储到文本文件。
ObjectInputStream : 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化
ObjectOutputStream : 将 Java 对象的基本数据类型和图形写入 OutputStream。
(2) 如何实现序列化?
A:被序列化的对象所属的类必须实现序列化接口
B:用序列化流对象进行操作
(3) 序列化(对象)流对象
ObjectInputStream : Object readObject()
ObjectOutputStream : void writeObject(Object obj)
(4) 掌握:
A:看到类实现了序列化接口,就知道该类可以被序列化流对象操作
B:看到类实现了序列化接口,知道点击鼠标就可以解决黄色警告问题
Properties:
(1)个人理解: Properties 其实就是Map体系的一个集合类。它可以从流中加载数据或者把数据保存到流中,键和值都是字符串。 是一个唯一可以和IO流结合使用的集合类。特殊的是它没有泛型。
(2)特点:
A:可以把集合中的数据保存到文本文件,也可以把文本文件的数据加载到集合中。
B:该集合的键和值都是String类型。
下面介绍一下它的特殊功能:
修改功能:
setProperty(String key,String value) 设置键值对(打印时默认用 = 链接)如果键相同会覆盖。
获取功能:
getProperty(String key) 根据键获取值
getProperty(String key,String defaultValue)
Set<String> stringPropertyNames() 获取Properties集合中所有的键。
和IO流结合的方法:
A: list 通过打印流把数据保存到文件中。
public void list(PrintStream out)
public void list(PrintWriter out)
把集合中的数据按照键值对的形式存储到文本文件中。
例如:
B: load 通过任意流把文本中的数据加载到集合中。
C: store 通过任意流把数据保存到文件中。
字符编码
编码表:就是字符和对应的数据组成的一张表。
编码表的由来:
计算机智能识别二进制数据,早期由来是电信号,为了方便应用计算机,让他可以识别各个国家的文字,
就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。
常见编码表
ASCII:美国标准信息码表,用一个字节的7位可以表示
ISO8859-1:拉丁码表,欧洲码表,用一个字节的8位表示。
GB2312:中国的中文编码表
GBK:中文编码表升级,融合了更多的中文文字符号,2个字节一个字
Unicode:国际标准码,融合了多种文字,所以文字都用2个字节表示,Java语言使用的就是Unicod
UTF-8:最多用3个字节来表示一个字符(字节能用一个就用一个,不够了再多用)
A:字符流 = 字节流+编码表
B:通过转换流写入数据,指定为UTF-8编码,并通过UTF-8编码读取。
个人总结: 如果你想在IO流中使用指定编码下数据,用转换流。
编码问题其实很简单,永远使用同一种编码即可。
但还是要注意:
还有一点要明白:
用什么编码表编的码,就用什么编码表解码。什么编的就用什么解。
不管中间用了那个编码表,解出了什么乱码,最后要想返回正常的文字,还是要用编码时候用的编码表。
因为比如我用GBK变了”你好”这个字符串的码,就变成了二进制数据,用ASCII解码之后肯定是乱码,因为二进制对应的字不同,
但是文字对应的还是这段二进制数,再用ASCII编码之后又成了二进制数,不管用了什么编码表,一直都是这段二进制数。
数据不会改变,但是想看到正常的结果,还是要用编码时候的编码表进行二进制数据的解码。
注意:因为GBK和UTF-8都识别中文,所以在G编,U解,U编,G解 的过程中会发生错误。
GBK和IOS8859-1进行上面的动作就不会发生错误。
到此,IO流的所有知识点儿就总结完了,有些理解的不对的地方,真诚地希望大家帮我指正出来,谢谢!!!