1、字符流
字符流产生的原因
- 用字节流读取英文字符与数字
- 没有问题 能够正常显示
- 用字节流读取中文字符
- 可能会有问题
编解码
- 一个字符在计算机当中是怎样存储的?
- 基于某个编码表,有与之对应的整数值(编码值)存储在计算机当中的
- 编码
- 基于某个编码表,把字符数据转化成编码值的过程(把人看懂的东西转化成计算机看懂的东西)
- 解码
- 基于某个编码表,把编码值转化成字符数据的过程(把计算机看懂的东西转化成人看懂的东西)
产生乱码的原因
编解码的不一致
中文编码表 : ‘你’ 123
日文编码表 : ‘の’ 123
编码表
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。 128
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。 0000 0000 - 1111 1111
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
GB18030:GBK的取代版本
BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。
Unicode:国际标准码,融合了多种文字。
UTF-8:可变长度来表示一个字符。
UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:
它将Unicode编码为00000000-0000007F的字符,用单个字节来表示 0111 1111 = 7F
它将Unicode编码为00000080-000007FF的字符用两个字节表示
它将Unicode编码为00000800-0000FFFF的字符用3字节表示
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
utf-16:
jvm使用的编码表,用2个字节来编解码
char : 2 字节
开发中常用的编码表
utf-8 GBK ISO8859-1 ASCII
默认编码表
- idea中默认utf-8
- 操作系统Win GBK
字符流的本质
字符流的本质:字节流+编码表
1.1 字符输出流
1.1.1 抽象基类Writer
1.1.2 具体子类
1.1.2.1 OutputStreamWriter转换流
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset
将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
构造方法
OutputStreamWriter(OutputStream out) 创建使用默认字符编码OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的OutputStreamWriter。
例子:``
package com.cskaoyan.charstream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* @description:
* @author: songtao@cskaoyan.onaliyun.com
**/
public class Demo {
public static void main(String[] args) throws IOException {
// 第一种构造方法
FileOutputStream fileOutputStream = new FileOutputStream("a.txt");
// 采用默认字符集 utf-8
OutputStreamWriter out = new OutputStreamWriter(fileOutputStream);
// 第二种
OutputStreamWriter outputStreamWriter =
new OutputStreamWriter(new FileOutputStream("a.txt"), "GBk");
}
}
成员方法
5个write方法
package com.cskaoyan.charstream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* @description: write方法
* @author: songtao@cskaoyan.onaliyun.com
**/
public class Demo2 {
public static void main(String[] args) throws IOException {
// 定义字符串
String s = "我要为你写65页ppt";
// 创建转换输出流对象
OutputStreamWriter out =
new OutputStreamWriter(new FileOutputStream("a.txt"));
// 写数据
// 写单个字符
//writeSingle(s, out);
// 写字符数组
//writeMuti(s, out);
out.write(s);
out.flush();
out.close();
}
private static void writeMuti(String s, OutputStreamWriter out) throws IOException {
char[] chars = s.toCharArray();
out.write(chars);
}
private static void writeSingle(String s, OutputStreamWriter out) throws IOException {
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
// 逐个字符写
out.write(chars[i]);
}
}
}
1.1.2.2 FileWriter简化流
用来写入字符文件的便捷类
构造方法
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 |
---|
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 |
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 |
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 |
例子:
public class Demo1 {
public static void main(String[] args) throws IOException {
// 第一种
FileWriter fileWriter = new FileWriter(new File("a.txt"));
// 第二种
FileWriter fileWriter1 = new FileWriter("a.txt");
}
}
成员方法
5个write方法
例子
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建文件字符输出流对象
FileWriter fileWriter = new FileWriter("a.txt");
// 写数据
String s = "什么是快乐星球?";
fileWriter.write(s);
fileWriter.close();
}
}
1.1.2.3 BuffferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
构造方法
BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 |
---|
BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 |
默认缓冲区大小
- 16KB
特有方法
-
void newLine() 写入一个行分隔符。
例子:
public class Demo {
public static void main(String[] args) throws IOException {
// 创建缓冲输出流对象
BufferedWriter bw = new BufferedWriter(
new FileWriter("a.txt"));
// 写数据
bw.write("RNG夺冠");
bw.newLine();
bw.write("卢本伟牛逼");
bw.close();
}
}
1.2 字符输入流
1.2.1 抽象基类Reader
成员方法
int | read() 读取单个字符。 |
---|---|
int | read(char[] cbuf) 将字符读入数组。 |
abstract int | read(char[] cbuf, int off, int len) |
read():
作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1
1.2.2 具体子类
1.2.2.1 InputStreamReader转换流
构造方法
- InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。
- InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。
例子:
public class Demo {
public static void main(String[] args) throws IOException {
// 第一种构造方法
InputStreamReader in = new InputStreamReader(
new FileInputStream("a.txt"));
// 第二种构造方法
InputStreamReader inputStreamReader = new InputStreamReader(
new FileInputStream("a.txt"), "GBk");
}
}
成员方法
- 3个read方法
例子:读数据
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建输入流对象
InputStreamReader in = new InputStreamReader(
new FileInputStream("a.txt"));
// read数据
// 单个
//readSingle(in);
// 多个
readMuti(in);
// close
in.close();
}
private static void readMuti(InputStreamReader in) throws IOException {
char[] chars = new char[1024];
// readCount表示读取到的字符的个数
int readCount = in.read(chars);
System.out.println(new String(chars,0,readCount));
}
private static void readSingle(InputStreamReader in) throws IOException {
int readData = in.read();
System.out.println(((char) readData));
int readData2 = in.read();
System.out.println(((char) readData2));
}
}
复制文件
- 文本文件复制
public class Demo3 {
public static void main(String[] args) throws IOException {
// 创建输入流对象
InputStreamReader in = new InputStreamReader(
new FileInputStream("D:\\aa.txt"));
// 创建输出流对象
OutputStreamWriter out = new OutputStreamWriter(
new FileOutputStream("aa.txt"));
// 边读边写
// 单个字符的方式
//copySingle(in, out);
// 多个字符的方式
char[] chars = new char[1024];
int readCount;
while ((readCount = in.read(chars)) != -1) {
out.write(chars,0,readCount);
}
// close
in.close();
out.close();
}
private static void copySingle(InputStreamReader in, OutputStreamWriter out) throws IOException {
int readData;
while ((readData = in.read()) != -1) {
out.write(readData);
}
}
}
图片文件复制
public class Demo4 {
public static void main(String[] args) throws IOException {
InputStreamReader in = new InputStreamReader(
new FileInputStream("D:\\mm.jpg"));
OutputStreamWriter out = new OutputStreamWriter(
new FileOutputStream("mm.jpg"));
char[] chars = new char[1024];
int readCount;
while ((readCount = in.read(chars)) != -1) {
out.write(chars,0,readCount);
}
out.flush();
in.close();
out.close();
}
}
// 虽然看起来复制成功 但是根本打不开
乱码问题解决
public class Demo5 {
public static void main(String[] args) throws IOException {
InputStreamReader in = new InputStreamReader(
new FileInputStream("a.txt"),"GBk");
char[] chars = new char[1024];
int readCount = in.read(chars);
System.out.println(new String(chars,0,readCount));
}
}
1.2.2.2 FileReader简化流
用来读取字符文件的便捷类
构造方法
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 |
---|
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。 |
成员方法
3个read方法
例子:
public class Demo3 {
public static void main(String[] args) throws IOException {
// 创建一个字符文件输入流
FileReader reader = new FileReader("a.txt");
// 读取数据
char[] chars = new char[1024];
int readCount = reader.read(chars);
System.out.println(new String(chars, 0, readCount));
reader.close();
}
}
1.2.2.3 转化流 VS简化流
- 转化流使用麻烦,简化流使用相对简单
- 转化流可以显示的指定字符集(构造方法) 简化流没有办法指定字符集,只能使用平台默认字符集
1.2.2.4 BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
构造方法
BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。 |
---|
BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。 |
特有的成员方法
String | readLine() 读取一个文本行。 |
---|---|
注意:
读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (’\n’)、回车 (’\r’) 或回车后直接跟着换行。
返回:
包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
Demo:
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
// 读数据
//readSingleLine(br);
// while循环做
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
private static void readSingleLine(BufferedReader br) throws IOException {
String s = br.readLine();
System.out.println(s);
String s2 = br.readLine();
System.out.println(s2);
String s3 = br.readLine();
System.out.println(s3);
}
}
2 其他流
2.1 数据流
引入:向文本文件当中写入整数 1000 用字符流怎么做?
我们没有办法通过字节流 字符流去写入java基本数据类型 所以就有了数据流
2.1.1 DataOutputStream数据输出流
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入
构造方法
DataOutputStream(OutputStream out) 创建一个新的数据输出流,将数据写入指定基础输出流。
成员方法
- 每种java基本数据类型 都有1个write方法与之对应
Demo
public class Demo {
public static void main(String[] args) throws IOException {
// 用数据流
// 创建数据输出流对象
DataOutputStream out = new DataOutputStream(
new FileOutputStream("a.txt"));
// 写入整数1000
out.writeInt(1000);
out.writeDouble(3.14);
out.close();
// 创建数据输入流对象
DataInputStream in = new DataInputStream(
new FileInputStream("a.txt"));
// 读取数据
int i = in.readInt();
System.out.println(i);
double v = in.readDouble();
System.out.println(v);
}
}
2.1.2 DataInputStream 数据输入流
构造方法
DataInputStream(InputStream in) 使用指定的底层 InputStream 创建一个DataInputStream。
成员方法:
- 每种java基本数据类型 都有1个read方法与之对应
注意:
我们按照怎样的顺序去写入,就需要我们按照相同的顺序去读取 |
public class Demo2 {
public static void main(String[] args) throws IOException {
write();
read();
}
private static void read() throws FileNotFoundException, IOException {
DataInputStream dis = new DataInputStream(
new FileInputStream("dos.txt"));
byte b = dis.readByte();
System.out.println(b);
short s = dis.readShort();
System.out.println(s);
int i = dis.readInt();
System.out.println(i);
long l = dis.readLong();
System.out.println(l);
float f = dis.readFloat();
System.out.println(f);
double d = dis.readDouble();
System.out.println(d);
char ch = dis.readChar();
System.out.println(ch);
boolean bb = dis.readBoolean();
System.out.println(bb);
dis.close();
}
private static void write() throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(
"dos.txt"));
dos.writeByte(1);
dos.writeShort(20);
dos.writeInt(300);
dos.writeLong(4000);
dos.writeFloat(12.34f);
dos.writeDouble(12.56);
dos.writeChar('a');
dos.writeBoolean(true);
dos.close();
}
}
2.2 打印流
练习:
写一个工具类 PrintUtils
成员变量 OutputStream
printInt(int) 专门写入int类型的数据的方法
printIntLn(int) 专门写入int类型的数据的方法 带换行
printDouble(double) 专门写入double类型的数据的方法
printDoubleLn(double) 专门写入double类型的数据的方法 带换行
close方法
提示:核心方法就是把相应的数据类型转换成String 写入文本
public class PrintUtils {
public OutputStream out;
public PrintUtils(OutputStream out) {
this.out = out;
}
public void printInt(int intType) throws IOException {
String s = String.valueOf(intType);
out.write(s.getBytes());
}
public void printlnInt(int intType) throws IOException {
String s = String.valueOf(intType);
out.write(s.getBytes());
out.write(System.lineSeparator().getBytes());
}
}
2.2.1 PrintStream字节打印流和printWriter字符打印流
打印流的4个特点
-
只能操作目的地,不能操作数据来源。
- 没有一个输入流与之对应
-
可以操作任意类型的数据。
- 通过把不同的类型转化成字符串写入
-
如果启动了自动刷新,能够自动刷新。
- PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter。
- autoFlush - boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区
-
可以操作文件的流
- 直接使用构造方法去传文件名作为参数
2.2.2 标准输入流与标准输出流
- 标准输出流 System.out 默认的输出设备是显示器
- 是一个字节打印流
- 标准输入流 System.in 默认的输入设备是键盘
- 是普通的字节输入流
练习:
利用System.in 去实现 Scanner当中的一个nextLine功能
需求:不停的去接收键盘输入的数据,当输入"88" 终止程序
提示:利用BufferedReader
public class Demo7 {
public static void main(String[] args) throws IOException {
// 利用转换流
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
//System.out.println("before");
//String s = br.readLine();
//System.out.println("after");
//System.out.println(s);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
// 给出约定 当输入"88" break
if (line.equals("88")) {
break;
}
}
br.close();
}
}
2.3 对象流
为啥会有对象流?
Student student = new Student("张三",18);
// 通过序列化流 把这个学生对象给存储起来
// 通过反序列化流去 把这个学生对象给还原回来
2.3.1 ObjectOutPutStream 序列化流
只能将支持 java.io.Serializable 接口的对象写入流中
构造方法
ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
成员方法
writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
2.3.2 ObjectInputStream反序列化流
构造方法
ObjectIntputStream(InputStream in)
成员方法
readObject()
注意:
-
java.io.NotSerializableException
-
Serializable是空接口 只起到标记的作用
-
transient修饰的属性 是不会被序列化的 只有默认值
-
java.io.InvalidClassException 类发生了变化 serialVersionUID变了
- 如何强行解决
static final long serialVersionUID = -1141965245606624166l;
3、总结
类型 | 字节输出流 | 字节输入流 | 字符输出流 | 字符输入流 |
---|---|---|---|---|
基类 | OutputStream | InputStream | Writer | Reader |
文件相关 | FileOutputStream | FileInputStream | FileWriter | FileReader |
缓冲相关 | BufferedOutputStream | BufferedInputStream | BufferedWriter | BufferedReader |
转换流 | OutputStreamWriter | InputStreamReader | ||
数据流 | DataOutputStream | DataInputStream | ||
打印流 | PrintStream | PrintWriter | ||
对象流 | ObjectOutputStream | ObjectInputStream |