第一节 转换流
1.作用:
a.实现字节流到字符流的转换
b.解决中文乱码的问题
2.中文编码
GB2312 (采用两个字节保存字符汉字,英文数字一个字节)
GBK (采用两个字节保存字符汉字,英文数字一个字节)
GB18030 (英文数字都是一个字节,中文是两个或四个字节)
ANSI 在简体中文Windows操作系统中, ANSI 编码代表 GBK 编码
Big5 繁体中文
ISO-8859-1 收录除ASCII外,还包括西欧,希腊语,泰语,阿拉伯语,希伯来语对应的文字符号
3.Unicode字符集(包含每个国家的所有字符)国际通用
unicode编码 使用两个字节---65536个字符,浪费空间
为了节省空间使用转码形式
utf-8 使用 1 、2、3个字节(EF BB BF:记事本添加的BOM(Byte Order Mark)头,编码的标记)
utf-16 使用两个字节---65536个字符(FF FE 小端(尾) FE FF 大端(尾))
utf-32 使用4个字节
4.只有转换流才能指定读取和写入的字符集
1.1 InputStreamReader类
InputStreamReader:把外部设备的二进制字节流转换成字符流读取到内存,并指定编码
代码实现:
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//1.实例化File的对象
//File file = new File("file/input1.txt");
//2.实例化转换输入流的对象
//注意:当一个流的存在的意义是为了实例化另外一个流,则这个流不需要手动进行关闭
//InputStream input = new FileInputStream(file);
//InputStreamReader reader = new InputStreamReader(input);
//使用默认的字符集【GBK】进行实例化转换流
//InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("file/input1.txt")));
//使用指定字符集进行实例化转换流
//字符集一般使用字符串直接传参,不区分大小写,但是,如果字符集书写有误的话,则会跑出java.io.UnsupportedEncodingException
InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("file/input1.txt")),"UTF-8");
//3.读取
char[] arr = new char[16];
int len = 0;
while((len = reader.read(arr)) != -1) {
String string = new String(arr, 0, len);
System.out.println(string);
}
reader.close();
}
}
1.2 OutputStreamWriter类
OutputStreamWriter:把内存中的字符使用指定的编码,转换成二进制字节流存储到外部设备
代码实现:
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//需求:将一段文本以utf-8的格式写入到文件中【注,文件格式为默认格式】
//1.实例化FIle对象
//注意:对于所有的输出流而言,文件可以不存在,在进行写入的过程中可以自动进行创建
//但是,对于所有的输入流而言,文件必须先存在,然后才能操作,否则,会抛FileNotFounedException
File file = new File("file/output1.txt");
//2.实例化转换输出流
//如果不想覆盖源文件中的内容时,则在传参的时候,设置一个参数为true
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file,true), "utf-8");
//3.写入
writer.write("客户放假,刚回家");
//4.刷新
writer.flush();
//5.关闭
writer.close();
}
}
第二节 缓冲流
作用:主要是为了增强基础流的功能,提高流的读写效率,减少访问磁盘的次数
注意:如果使用记事本创建的文件,文件是utf-8或者unicode编码,文件的前面有一个BOM(Byte Order Mark)头,BOM头作用是指定文件使用的编码类型。GBK编码没有添加bom头。
utf-8:EF BB BF
unicode 小端: FF FE 66 00
unicode 大端 :FE FF 00 66
2.1 BufferedInputStream类
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
//1.实例化一个File对象
File file = new File("file/test22.txt");
//2.实例化一个缓冲字节输入流的对象
BufferedInputStream input = new BufferedInputStream(new FileInputStream(file));
//3.读取
byte[] arr = new byte[1024];
int len = 0;
while((len = input.read(arr)) != -1) {
String string = new String(arr, 0, len);
}
}
}
2.2 BufferedOutputStream类
BufferedOutputStream内部默认的缓存大小是8KB,每次写入时存储到缓存中的byte数组中,当数组存满时,会把数组中的数据写入文件,并且缓存下标归零。
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
//实例化FIle对象
File file = new File("test33.txt");
//实例化换种字节输出流
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file));
//写
output.write("你好hello".getBytes());
//刷新
output.flush();
//关闭
output.close();
}
}
2.3 BufferedReader类
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
//1.实例化FIle对象
File file = new File("test33.txt");
//2.实例化缓冲字符流的对象
BufferedReader reader = new BufferedReader(new FileReader(file));
//方式一:read循环读取
char[] arr = new char[8];
int len = 0;
while((len = reader.read(arr)) != -1) {
String string = new String(arr, 0, len);
}
//方式二:readLine循环读取
String result = null;
while((result = reader.readLine()) != null) {
System.out.println("第一行:" + result);
}
reader.close();
}
}
2.4 BufferedWriter类
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
// 实例化FIle对象
File file = new File("test33.txt");
// 实例化缓冲字符输出流
BufferedWriter writer = new BufferedWriter(new FileWriter(file,true));
// 写
writer.write("今天天气还可以");
// 作用:主要就是为了换行
writer.newLine();
// 刷新
writer.flush();
// 关闭
writer.close();
}
}
第三节 内存流
3.1ByteArrayIn(Out)putStream类
输入和输出都是从文件中来的,当然,也可将输出输入的位置设置在内存上,这就需要ByteArrayInputStream和ByteArrayOutputStream
ByteArrayInputStream:将内容写入到内存中,是Inputstream的子类
ByteArrayOutputStream:将内存中数据输出,是OutputStream的子类
注:此时的操作应该以内存为操作点
注意:内存操作流的操作对象,一定是以内存为主准,不要以硬盘为准。
案例:完成一个字母大小写转换的程序
public class TextDemo02 {
public static void main(String[] args) throws IOException {
//定义一个字符串,全部由大写字母组成
String string = "HELLOWORLD";
//1.内存输入流
//向内存中输出内容,注意:跟文件读取不一样,不设置文件路径
ByteArrayInputStream bis = new ByteArrayInputStream(string.getBytes());
//2.内存输出流
//准备从内存中读取内容,注意:跟文件读取不一样,不设置文件路径
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int temp = 0;
//3.read()方法每次只读取一个字符
while((temp = bis.read()) != -1) {
//将读取的数字转为字符
char c = (char)temp;
//将字符变为大写
bos.write(Character.toLowerCase(c));
}
//循环结束之后,所有的数据都在ByteArrayOutputStream中
//取出内容,将缓冲区内容转换为字符串
String newString = bos.toString();
//4.关闭流
bis.close();
bos.close();
System.out.println(newString);
}
}
第四节 标准输入输出流
4.1PrintStream和PrintWriter
Java的标准输入/输出分别通过System.in和System.out实现,默认情况下分别代表是键盘和显示器
PrintStream类:PrintStream为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
PrintWriter类:向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream中的所有 print方法。它不包含用于写入原始字节的方法。
代码实现:
public static void main(String[] args)throws Exception {
//1创建PrintStream
//PrintStream ps=new PrintStream("d:\\print.txt");
PrintWriter pw=new PrintWriter("d:\\print.txt");
//2打印
pw.println(true);
pw.println(3.14);
pw.println(100);
pw.println("我爱北京");
//3刷新
pw.flush();
//4关闭
pw.close();
}
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//System.out.println("hello world");
//创建打印流的对象
//注意:默认打印到控制台,但是,如果采用setOut方法进行重定向之后,将输出到指定的文件中
PrintStream print = new PrintStream(new FileOutputStream(new File("test33.txt")));
static void setErr(PrintStream err) //重新分配“标准”错误输出流。
static void setIn(InputStream in) //重新分配“标准”输入流。
static void setOut(PrintStream out) //重新分配“标准”输出流。
//将标准输出重定向到print的输出流
System.setOut(print);
System.out.println("hello world");
}
}
public class InputStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//重新初始化System.out
PrintStream ps = new PrintStream("Taylor.txt");
System.setOut(ps);//输出重定向,输出到Taylor.txt文件中
for (int i = 0; i < 5; i++) {
System.out.println("好久不见,Taylor");
}
//关流
ps.close();
}
}
第五节 对象流
5.1ObjectInputStreamh和ObjectOutputStream
1.流中流动的数据是对象
将一个对象写入到本地文件中,被称为对象的序列化(Serializable)
将一个本地文件中的对象读取出来,被称为对象的反序列化(Deserializable)
2.ObjectInputStream/ObjectOutputStream:增强了读写8种基本数据类型,字符串,对象的功能
注意:
a.序列化对象的类型必须实现Serializable接口。否则不能序列化。
b.如果向将多个对象序列化到本地,可以借助于集合,【思路:将多个对象添加到集合中,将集合的对象写入到本地文件中,再次读出来,获取到的仍然是集合对象,遍历集合】。
3.对象中以下字段不能序列化:
1)transient 修饰的字段
2)static 静态字段
4.在要序列化类中添加serialVersionUID字段,保证序列化和反序列化是同一个类
private static final long serialVersionUID = 100L;
5.读取到文件尾部的标志:java.io.EOFException (End Of File Exception)
public class ObjectStreamDemo {
public static void main(String[] args) {
//objectOutputStreamUsage();
objectInputStreamUsage();
}
//写:将对象进行序列化
public static void objectOutputStreamUsage() {
//1.实例化一个Person的对象
Person person = new Person("张三", 10, 'B');
//2.实例化一个对象输出流的对象
ObjectOutputStream output = null;
try {
output = new ObjectOutputStream(new FileOutputStream(new File("file/person.txt")));
//3.将对象写入到流中
output.writeObject(person);
//4.刷新
output.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读:反序列化
public static void objectInputStreamUsage() {
//1.实例化对象输入流的对象
try {
ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File("file/person.txt")));
//2.读取
Object object = input.readObject();
//3.对象的向下转型
if(object instanceof Person) {
Person p = (Person)object;
System.out.println(p);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:在使用对象流的时候,用于初始化对象流的参数只能是字节流(将对象转换为二进制的形式,然后再把二进制写入文件)
补充:
1.合并流 SequenceInputStream
2.字符串流 StringReader / StringWriter
3.管道流 PipedInpurStream / PipedOutputStream
第六节 RandomAccessFile类
RandomAccessFile是用来访问那些保存数据记录的文件的,可以用seek( )方法来访问记录,并进行读写。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。
案例一:RandomAccessFile类的应用
public class TextDemo01 {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");
// 以下向file文件中写数据
file.writeInt(20);// 占4个字节
file.writeDouble(8.236598);// 占8个字节
//这个长度写在当前文件指针的前两个字节处,可用readShort()读取
file.writeUTF("这是一个UTF字符串");
file.writeBoolean(true);// 占1个字节
file.writeShort(395);// 占2个字节
file.writeLong(2325451l);// 占8个字节
file.writeUTF("又是一个UTF字符串");
file.writeFloat(35.5f);// 占4个字节
file.writeChar('a');// 占2个字节
//把文件指针位置设置到文件起始处
file.seek(0);
// 以下从file文件中读数据,要注意文件指针的位置
System.out.println("——————从file文件指定位置读数据——————");
System.out.println(file.readInt());
System.out.println(file.readDouble());
System.out.println(file.readUTF());
//将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。
file.skipBytes(3);
System.out.println(file.readLong());
//跳过文件中“又是一个UTF字符串”所占字节
//注意readShort()方法会移动文件指针,所以不用写2。
file.skipBytes(file.readShort());
System.out.println(file.readFloat());
// 以下演示文件复制操作
System.out.println("——————文件复制(从file到fileCopy)——————");
file.seek(0);
RandomAccessFile fileCopy = new RandomAccessFile("fileCopy.txt", "rw");
int len = (int) file.length();// 取得文件长度(字节数)
byte[] b = new byte[len];
//全部读取
file.readFully(b);
fileCopy.write(b);
System.out.println("复制完成!");
}
}
第七节 Properties集合
Properties是Map接口的一个实现类,并且是Hashtable的子类
Properties集合中元素也是以键值对的形式存在的
Properties特点:
1.存储属性名和属性值
2.属性名和属性值都是字符串
3.和流有关系
4.没有泛型
public class PropertiesDemo {
public static void main(String[] args) {
//1.实例化一个Properties的对象
Properties pro = new Properties();
System.out.println(pro);
//2.把文件userlist.properties中的键值对同步到集合中
//实质:读取
/*
void load(InputStream inStream); //从输入流中读取属性列表(键和元素对)。
*/
try {
pro.load(new BufferedInputStream(new FileInputStream(new File("file/userlist.properties"))));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(pro);
//3.向集合中添加一对键值对
/*
* Object setProperty(String key, String value)
调用 Hashtable 的方法 put。
*/
pro.setProperty("address", "china");
System.out.println(pro);
try {
//4.store
//实质:写入
//comments:工作日志
pro.store(new BufferedOutputStream(new FileOutputStream(new File("file/userlist.properties"))), "add a pair of key and value");
} catch (Exception e) {
e.printStackTrace();
}
}
}
第八节 装饰者设计模式
1.装饰模式指的是在不必改变原类文件和继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是通过装饰类来包裹真实的对象。(真实对象变成了装饰类的成员变量!)
2.应用场景:需要扩展一个类的功能,或给一个类添加附加职责。
案例:
1.抽象类 ReadFile -->read抽象方法
2.定义一些ReadFile的实现子类
ReadTextFile 读取文本文件
ReadMusicFile 读取音乐文件
ReadVideoFile 读取视频文件
3.要求:提高三个子类的功能:带缓冲
3.1继承
BufferedReadTextFile继承ReadTextFile 重写 read方法
BufferedReadMusicFile继承ReadMusicFile 重写 read
BufferedReadVideoFile继承ReadVideoFile 重写 read
缺点:1.类体系太庞大 2.耦合性太高
3.2装饰者设计模式 :采用组合的关系
BufferedReadFile{
private ReadFile readFile;//被强化的类
public BufferedReadFile(ReadFile readFile){
this.readFile=readFile;
}
public void read(){
///重写方法实现
}
}
优点:耦合性低,提高重用性
代码实现:
/**
* 抽象人类
*/
public abstract class AbstractPerson {
public abstract void eat();
}
/**
*子类1
*/
public class Person extends AbstractPerson {
String name;
public void eat() {
System.out.println(name+"正在吃东西.........");
}
}
/**
* 子类2
*/
public class Person2 extends AbstractPerson{
String name;
@Override
public void eat() {
System.out.println(name+"吃......");
}
}
import java.io.BufferedInputStream;
/**
* 增强Person
* @author wgy
*/
public class StrongPerson extends AbstractPerson {
//使用person创建一个变量
AbstractPerson p;
public StrongPerson(AbstractPerson p) {
this.p = p;
}
public void eat() {
p.eat();
System.out.println("抽一口");
System.out.println("眯一会");
System.out.println("写会java代码");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Person benwei=new Person();
benwei.name="本伟";
Person2 zhengshuai=new Person2();
zhengshuai.name="郑帅";
//装饰者设计
StrongPerson strongPerson=new StrongPerson(benwei);
StrongPerson strongPerson2=new StrongPerson(zhengshuai);
strongPerson.eat();
strongPerson2.eat();
}
}
面试题
1.BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法?
//字符缓冲流,主要用来增强基础流,提高读写效率。read()读取字符数组,readLine()一次读取一行。
2.怎么样把输出字节流转换成输出字符流,说出它的步骤?
//OutputStreamWriter把字节数据指定编码后转换成字符流。
3.流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流(过滤流)是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?
//关闭用close()方法,在finally代码块里面关闭比较好。最先创建的流最后关,关闭调用的流时,默认关闭传入的流。
4.什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作?什么时候对象需要被序列化呢?
//将对象写入本地文件中,叫做对象序列化。
//将对象从本地文件中读取出来,叫做反序列化。
//实现序列化,需要对象所对应的类实现Serializable接口
//1.需要把对象保存到文件中存储到物理介质(比如:硬盘)的时候 2.对象需要在网络上进行传输的时候
5.在实现序列化接口的时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用
//叫做序列化版本ID,用于鉴别序列化与反序列化是否是同一个类
6.什么是内存流?有什么作用
//以内存为操作点,对数据进行读写操作