1 字节流
1.1 IO流概述和分类
- 学习IO流的目的?
- 将数据写到文件中,实现数据永久化存储
- 读取文件中已经存在的数据
- IO流介绍------输入/输出(Input/Output)
- I表示intput,是数据从硬盘进内存的过程,称之为读。
- O表示output,是数据从内存到硬盘的过程。称之为写。
- 流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流,流的本质是数据传输
- IO流就是用来处理设备间数据传输问题的.常见的应用: 文件复制; 文件上传; 文件下载
- IO的数据传输,可以看做是一种数据的流动,按照流动的方向,以内存为参照物,进行读写操作。
- 简单来说:内存在读,内存在写。
- IO流的分类
- 按照数据的流向
- 输入流:读数据
- 输出流:写数据
- 按照数据类型来分
- 字节流
- 字节输入流
- 字节输出流
- 字符流
- 字符输入流
- 字符输出流
- 字节流
- 按照数据的流向
- IO流的使用场景
- 如果操作的是纯文本文件,优先使用字符流
- 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
- 如果不确定文件类型,优先使用字节流.字节流是万能的流
1.2 字节流写数据
- 字节流抽象基类
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
- 字节输出流
- FileOutputStream(String name):创建文件输出流以指定的名称写入文件
- 使用字节输出流写数据的步骤
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 注意事项:
- 如果文件不存在,就创建。
- 如果文件存在就清空。
- 注意事项:
- 调用字节输出流对象的写数据方法
- 注意事项:
- 写出的整数,实际写出的是整数在码表上对应的字母。
- 注意事项:
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
- 注意事项:
- 每次使用完流必须要释放资源。
- 注意事项:
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputDemo1 {
public static void main(String[] args) throws IOException {
//1.创建字节输出流的对象 --- 告诉虚拟机我要往哪个文件中写数据了
/*
注意点:
1.如果文件不存在,会帮我们创建
2.如果文件存在,会把文件清空
*/
FileOutputStream fos = new FileOutputStream("D:\\a.txt");
//FileOutputStream fos = new FileOutputStream(new File("D:\\a.txt"));
//2,写数据
fos.write(97);
//3,释放资源
fos.close();
}
}
1.3 字节流写数据的三种方式
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 |
void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 |
void write(byte[] b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据 |
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputDemo3 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bytestream\\a.txt");
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
}
}
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputDemo4 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bytestream\\a.txt");
// byte[] bys = {97, 98, 99};
// fos.write(bys);
byte[] bys = {97, 98, 99, 100, 101, 102, 103};
fos.write(bys, 1, 2);
fos.close();
}
}
1.4 字节流写数据的两个小问题
- 字节流写数据如何实现换行
- windows:\r\n
- linux:\n
- mac:\r
- 字节流写数据如何实现追加写入
- public FileOutputStream(String name,boolean append)
- 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputDemo5 {
public static void main(String[] args) throws IOException {
//第二个参数就是续写开关,如果没有传递,默认就是false,
//表示不打开续写功能,那么创建对象的这行代码会清空文件.
//如果第二个参数为true,表示打开续写功能
//那么创建对象的这行代码不会清空文件.
FileOutputStream fos = new FileOutputStream("bytestream\\a.txt",true);
fos.write(97);
//能加一个换行
fos.write("\r\n".getBytes());
fos.write(98);
//能加一个换行
fos.write("\r\n".getBytes());
fos.write(99);
//能加一个换行
fos.write("\r\n".getBytes());
fos.write(100);
//能加一个换行
fos.write("\r\n".getBytes());
fos.write(101);
//能加一个换行
fos.write("\r\n".getBytes());
fos.close();
}
}
1.5 字节流写数据加异常处理
- 异常处理格式
- try-catch-finally
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}
- finally特点
- 被finally控制的语句一定会执行,除非JVM退出
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputDemo6 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//System.out.println(2/0);
fos = new FileOutputStream("D:\\a.txt");
fos.write(97);
} catch (IOException e) {
e.printStackTrace();
} finally {
//finally语句里面的代码,一定会被执行.
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.6 字节流小结
- 创建字节输出流对象
- 文件不存在,就创建。
- 文件存在就清空。如果不想被清空则加true
- 写数据
- 可以写一个字节,写一个字节数组,写一个字节数组的一部分
- 写一个回车换行:\r\n
- 释放资源
1.7 字节流读数据(一次读一个字节数据)
- 字节输入流
- FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
- 字节输入流读取数据的步骤
- 创建字节输入流对象
- 注意事项:
- 如果文件不存在,就直接报错。
- 读数据
- 注意事项:
- 读出来的是文件中数据的码表值。 a -> 97
- 释放资源
- 注意事项:
- 每次使用完流必须要释放资源。
- 创建字节输入流对象
import java.io.FileInputStream;
import java.io.IOException;
public class InputDemo7 {
public static void main(String[] args) throws IOException {
//如果文件存在,那么就不会报错.
//如果文件不存在,那么就直接报错.
FileInputStream fis = new FileInputStream("bytestream\\a.txt");
int read = fis.read();
//一次读取一个字节,返回值就是本次读到的那个字节数据.
//也就是字符在码表中对应的那个数字.
//如果我们想要看到的是字符数据,那么一定要强转成char
System.out.println(read);//97
System.out.println((char) read);//A
//释放资源
fis.close();
}
}
1.8 字节流读数据(一次读一个字节数据多次读取)
import java.io.FileInputStream;
import java.io.IOException;
public class InputDemo8 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("bytestream\\a.txt");
//1,文件中多个字节我怎么办?
/*while (true) {
int i1 = fis.read();
System.out.println(i1);
}*/
int b;
while ((b = fis.read()) != -1) {
System.out.println((char) b);
}
fis.close();
}
}
1.9 字节流复制文件
案例需求
- 把“C:\itheima\a.avi”复制到当前模块下
分析
- 复制文件,其实就把文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
- 数据源:
- C:\itheima\a.avi — 读数据 — FileInputStream
- 目的地:
- 模块名称\copy.avi — 写数据 — FileOutputStream
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class InputDemo9 {
public static void main(String[] args) throws IOException {
//创建了字节输入流,准备读数据.
FileInputStream fis = new FileInputStream("C:\\itheima\\a.avi");
//创建了字节输出流,准备写数据.
FileOutputStream fos = new FileOutputStream("bytestream\\a.avi");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
}
1.10 字节流读数据(一次读一个字节数组数据)
- 一次读一个字节数组的方法:
- public int read(byte[] b):从输入流读取最多b.length个字节的数据
- 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class InputDemo10 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("C:\\itheima\\a.avi");
FileOutputStream fos = new FileOutputStream("bytestream\\a.avi");
byte [] bytes = new byte[1024];
int len; //本次读到的有效字节个数 -- 这次读了几个字节.
while((len = fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fis.close();
fos.close();
}
}
2 字节缓冲流
-
BufferOutputStream:字节缓冲输出流
- 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
-
BufferedInputStream:字节缓冲输入流
- 创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
方法名 说明 BufferedOutputStream(OutputStream out) 创建字节缓冲输出流对象 BufferedInputStream(InputStream in) 创建字节缓冲输入流对象 -
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
- 字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
import java.io.*;
public class BufferDemo1 {
public static void main(String[] args) throws IOException {
//就要利用缓冲流去拷贝文件
//创建一个字节缓冲输入流 BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bytestream\\a.avi"));
//创建一个字节缓冲输出流 BufferedOutputStream(OutputStream out)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bytestream\\copy.avi"));
//一次读取一个字节数据
// int b;
// while ((b = bis.read()) != -1) {
// bos.write(b);
// }
//一次读取一个字节数组数据
byte [] bytes = new byte[1024];
int len;
while((len=bis.read())!=-1){
bos.write(bytes, 0, len);
}
//方法的底层会把字节流给关闭
bis.close();
bos.close();
}
}
2.1 小结
字节流:
- 可以操作(拷贝)所有类型的文件
字节缓冲流:
- 可以提高读写效率
- 不能直接操作文件,需要传递字节流
拷贝文件的四种方式:
- 字节流一次读写一个字节
- 字节流一次读写一个字节数组
- 字节缓冲流一次操作一个字节
- 字节缓冲流一次操作一个字节数组
3 字符流
3.1 为什么会出现字符流?
- 如果利用字节流,把文本文件中的中文,读取到内存中,有可能出现乱码。
- 如果利用字节流,把中文写到文本文件中,也有可能出现乱码。
- 字符流 = 字节流 + 编码表
3.2 编码表
基础知识:
- 计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
- 按照某种规则,将字符存储到计算机中,称为编码。
- 按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码 。
- 编码和解码的方式必须一致,否则会导致乱码。
- 简单理解:
- 存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。
- 读取的时候,先把二进制解析出来,再转成97,通过97查找到对应的字符是a。
- 简单理解:
什么是字符集
- 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
- 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
常见的字符集
- ASCII字符集:
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码,是基于拉丁字母的一套电脑编码系统):包括了数字,大小写字符和一些常见的标点符号。用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
- 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
- 注意: ASCII码表中是没有中文的。
- GBXXX字符集:
- GBK:window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字,最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案。
- 注意: GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
- Unicode字符集:
- UTF-8编码:由国际组织ISO 制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
- 编码规则:
- 128个US-ASCII字符,只需一个字节编码
- 拉丁文等字符,需要二个字节编码
- 大部分常用字(含中文),使用三个字节编码
- 其他极少使用的Unicode辅助字符,使用四字节编码
- 编码规则:
- 但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及 UTF-32的编码方式再存储到计算机,其中最为常见的就是UTF-8。
- 注意: Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储
- UTF-8编码:由国际组织ISO 制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
3.3 编码表小结
3.4 字符串中的编码解码问题
方法名 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的 String |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来构造新的 String |
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class CharStreamDemo2 {
public static void main(String[] args) throws UnsupportedEncodingException {
method1();
method2();
}
private static void method1() throws UnsupportedEncodingException {
// byte[] getBytes():
//使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
// byte[] getBytes(String charsetName):
//使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
String s = "程序员";
//利用idea默认的UTF-8将中文编码为一系列的字节
byte[] bytes1 = s.getBytes();
System.out.println(Arrays.toString(bytes1));//[-25, -88, -117, -27, -70, -113, -27, -111, -104]
//byte[] bytes2 = s.getBytes("UTF-8");
byte[] bytes2 = s.getBytes("GBK");
System.out.println(Arrays.toString(bytes2));//[-77, -52, -48, -14, -44, -79]
}
private static void method2() throws UnsupportedEncodingException {
// String(byte[] bytes):
//通过使用平台的默认字符集解码指定的字节数组来构造新的 String
// String(byte[] bytes, String charsetName):
//通过指定的字符集解码指定的字节数组来构造新的 String
//UTF-8
byte[] bytes1 = {-25, -88, -117, -27, -70, -113, -27, -111, -104};
//gbk
byte[] bytes2 = {-77, -52, -48, -14, -44, -79};
//利用默认的UTF-8进行解码
String s1 = new String(bytes1);
System.out.println(s1);//程序员
//利用指定的GBK进行解码
String s2 = new String(bytes2, "gbk");
System.out.println(s2);//程序员
}
}
3.5 字节流读取中文出现乱码的原因
- 为什么字节流读取纯文本文件,可能会出现乱码?
- 因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分所以就会出现乱码问题。
- 为什么字节流拷贝不会出现乱码的问题?
- 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文
- 中文的字节存储方式
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
小结
- 想要进行拷贝,一律使用字节流或者字节缓冲流
- 想要把文本文件中的数据读到内存中,请使用字符输入流。
想要把内存中的数据写到文本文件中,请使用字符输出流 - GBK码表一个中文两个字节,UTF-8编码格式一个中文3个字节。
3.6 字符流写数据
-
介绍
- Writer: 用于写入字符流的抽象父类
- FileWriter: 用于写入字符流的常用子类
-
构造方法
方法名 | 说明 |
---|---|
FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 |
FileWriter(File file, boolean append) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(String fileName, boolean append) | 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 |
- 成员方法
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
- 刷新和关闭的方法
方法名 | 说明 |
---|---|
flush() | 刷新流,之后还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo3 {
public static void main(String[] args) throws IOException {
//创建字符输出流的对象
//FileWriter fw = new FileWriter(new File("charstream\\a.txt"));
FileWriter fw = new FileWriter("charstream\\a.txt");
method1(fw);
method2(fw);
method3(fw);
method4(fw);
method5(fw);
//释放资源
fw.close();
}
private static void method1(FileWriter fw) throws IOException {
//void write(int c) 写一个字符
fw.write(97);
fw.write(98);
fw.write(99);
}
private static void method2(FileWriter fw) throws IOException {
//void write(char[] cbuf) 写出一个字符数组
char[] chars = {97, 98, 99, 100, 101};
fw.write(chars);
}
private static void method3(FileWriter fw) throws IOException {
//void write(char[] cbuf, int off, int len) 写出字符数组的一部分
char[] chars = {97, 98, 99, 100, 101};
fw.write(chars, 0, 3);
}
private static void method4(FileWriter fw) throws IOException {
//void write(String str) 写一个字符串
String line = "程序员abc";
fw.write(line);
}
private static void method5(FileWriter fw) throws IOException {
//void write(String str, int off, int len) 写一个字符串的一部分
String line = "程序员abc";
fw.write(line, 0, 3);
}
}
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo5 {
public static void main(String[] args) throws IOException {
//flush()刷新流。刷新完毕之后,还可以继续写数据
//close()关闭流。释放资源。一旦关闭,就不能写数据
FileWriter fw = new FileWriter("charstream\\a.txt");
fw.write("程序员");
//fw.flush();
fw.write("666");
//fw.flush();
fw.close();
fw.write("aaa");//IOException:Stream closed
}
}
字符流写数据步骤:
- 创建字符输出流对象
- 注意事项:
- 如果文件不存在,就创建。但是要保证父级路径存在。
- 如果文件存在就清空。
- 注意事项:
- 写数据
- 注意事项:
- 写出int类型的整数,实际写出的是整数在码表上对应的字母。
- 写出字符串数据,是把字符串本身原样写出。
- 注意事项:
- 释放资源
- 注意事项:
- 每次使用完流必须要释放资源。
- 注意事项:
3.7 字符流读数据
-
介绍
- Reader: 用于读取字符流的抽象父类
- FileReader: 用于读取字符流的常用子类
-
构造方法
方法名 | 说明 |
---|---|
FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader |
FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader |
- 成员方法
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CharStreamDemo6 {
public static void main(String[] args) throws IOException {
//创建字符输入流的对象
// FileReader fr = new FileReader(new File("charstream\\a.txt"));
FileReader fr = new FileReader("charstream\\a.txt");
//读取数据
//一次读取一个字符
int ch;
while ((ch = fr.read()) != -1) {
System.out.println((char) ch);
}
//一次读一个字符数组数据
char[] chars = new char[1024];
int len;
//read方法还是读取,但是是一次读取多个字符
//他把读到的字符都存入到chars数组。
//返回值:表示本次读到了多少个字符。
while ((len = fr.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
//释放资源
fr.close();
}
}
3.8 案例1 保存键盘录入的数据
案例需求
- 将键盘录入的用户名和密码保存到本地实现永久化存储
实现步骤 - 获取用户输入的用户名和密码
- 将用户输入的用户名和密码写入到本地文件中
- 关流,释放资源
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class CharStreamDemo8 {
public static void main(String[] args) throws IOException {
//将键盘录入的用户名和密码保存到本地实现永久化存储
//要求:用户名独占一行,密码独占一行
//分析:
//1,实现键盘录入,把用户名和密码录入进来
Scanner sc = new Scanner(System.in);
System.out.println("请录入用户名");
String username = sc.next();
System.out.println("请录入密码");
String password = sc.next();
//2.分别把用户名和密码写到本地文件。
FileWriter fw = new FileWriter("charstream\\a.txt");
//将用户名和密码写到文件中
fw.write(username);
//表示写出一个回车换行符 windows \r\n MacOS \r Linux \n
fw.write("\r\n");
fw.write(password);
//刷新流
fw.flush();
//释放资源
fw.close();
}
}
4 字符缓冲流
4.1 字符缓冲流介绍
- BufferedWriter:
- 将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
- BufferedReader:
- 从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
方法名 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建字符缓冲输出流对象 |
BufferedReader(Reader in) | 创建字符缓冲输入流对象 |
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class CharStreamDemo9 {
public static void main(String[] args) throws IOException {
//字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("charstream\\a.txt"));
//读取数据
char[] chars = new char[1024];
int len;
while ((len = br.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
br.close();
}
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo10 {
public static void main(String[] args) throws IOException {
//字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\a.txt"));
//写出数据
//实际写出的是97对应的字符a
bw.write(97);
bw.write("\r\n");
//实际写出的是97 - 101 对应的字符 abcde
char[] chars = {97, 98, 99, 100, 101};
bw.write(chars);
bw.write("\r\n");
//实际写的是abc
bw.write(chars, 0, 3);
bw.write("\r\n");
//会把字符串的内容原样写出
bw.write("程序员");
bw.write("\r\n");
//会把字符串的一部分写出 abcde
String line = "abcdefg";
bw.write(line, 0, 5);
bw.flush();
bw.close();
}
}
4.2 字符缓冲流特有功能
BufferedWriter:
方法名 | 说明 |
---|---|
void newLine() | 写一行行分隔符,行分隔符字符串由系统属性定义 |
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo11 {
public static void main(String[] args) throws IOException {
//字符缓冲流的特有功能
//字符缓冲输出流BufferedWrite : newLine 跨平台的换行符
//创建对象
BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\a.txt"));
//写出数据
bw.write("黑马程序员666");
//跨平台的回车换行
bw.newLine();
bw.write("abcdef");
//跨平台的回车换行
bw.newLine();
bw.write("-------------");
//刷新流
bw.flush();
//释放资源
bw.close();
}
}
BufferedReader:
方法名 | 说明 |
---|---|
String readLine() | 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null |
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CharStreamDemo12 {
public static void main(String[] args) throws IOException {
//字符缓冲流的特有功能
//字符缓冲输入流BufferedReader: readLine 读一整行
//创建对象
BufferedReader br = new BufferedReader(new FileReader("charstream\\a.txt"));
//读取数据
// String line1 = br.readLine();
// String line2 = br.readLine();
// String line3 = br.readLine();
//
// //在之前,如果读不到数据,返回-1
// //但是readLine如果读不到数据返回null
// String line4 = br.readLine();
// String line5 = br.readLine();
//
// System.out.println(line1);
// System.out.println(line2);
// System.out.println(line3);
// System.out.println(line4);
// System.out.println(line5);
//使用循环来进行改进
String line;
//可以读取一整行数据。一直读,读到回车换行为止。
//但是他不会读取回车换行符。
while((line = br.readLine()) != null){
System.out.println(line);
}
// 释放资源
br.close();
}
}
4.3 案例2 符缓冲流操作文件中数据排序
案例需求
- 使用字符缓冲流读取文件中的数据,排序后再次写到本地文件
实现步骤
- 将文件中的数据读取到程序中
- 对读取到的数据进行处理
- 将处理后的数据添加到集合中
- 对集合中的数据进行排序
- 将排序后的集合中的数据写入到文件中
import java.io.*;
import java.util.Arrays;
public class CharStreamDemo14 {
public static void main(String[] args) throws IOException {
//需求:读取文件中的数据,排序后再次写到本地文件
//分析:
//1.要把文件中的数据读取进来。
BufferedReader br = new BufferedReader(new FileReader("charstream\\sort.txt"));
//输出流一定不能写在这里,因为会清空文件中的内容
//BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\sort.txt"));
String line = br.readLine();
System.out.println("读取到的数据为" + line);
br.close();
//2.按照空格进行切换
String[] split = line.split(" ");//9 1 2 5 3 10 4 6 7 8
//3.把字符串类型的数组变成int类型
int[] arr = new int[split.length];
//遍历split数组,可以进行类型转换。
for (int i = 0; i < split.length; i++) {
String smallStr = split[i];
//类型转换
int number = Integer.parseInt(smallStr);
//把转换后的结果存入到arr中
arr[i] = number;
}
//4.排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//5.把排序之后结果写回到本地 1 2 3 4...
BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\sort.txt"));
//写出
for (int i = 0; i < arr.length; i++) {
bw.write(arr[i] + " ");
bw.flush();
}
//释放资源
bw.close();
}
}
5 IO流总结
6 转换流注意点
6.1 字符流中和编码解码问题相关的两个类
- InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
- 它读取字节,并使用指定的编码将其解码为字符
- 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
- OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
- 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
- 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
6.2 转换流读写数据
方法名 | 说明 |
---|---|
InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 |
InputStreamReader(InputStream in,String chatset) | 使用指定的字符编码创建InputStreamReader对象 |
OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 |
OutputStreamWriter(OutputStream out,String charset) | 使用指定的字符编码创建OutputStreamWriter对象 |
import java.io.*;
import java.nio.charset.Charset;
public class ConvertedDemo1 {
public static void main(String[] args) throws IOException {
method1();
method2();
method3();
}
private static void method1() throws IOException {
//这个方法直接读取会产生乱码
//因为文件是GBK码表
//而idea默认的是UTF-8编码格式.
//所以两者不一致,导致乱码
FileReader fr = new FileReader("C:\\Users\\apple\\Desktop\\a.txt");
int ch;
while ((ch = fr.read()) != -1) {
System.out.println((char) ch);
}
fr.close();
}
private static void method2() throws IOException {
//如何解决乱码现象
//文件是什么码表,那么咱们就必须使用什么码表去读取.
//我们就要指定使用GBK码表去读取文件.
InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\apple\\Desktop\\a.txt"), "gbk");
int ch;
while ((ch = isr.read()) != -1) {
System.out.println((char) ch);
}
isr.close();
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\apple\\Desktop\\b.txt"), "UTF-8");
osw.write("我爱学习,谁也别打扰我");
osw.close();
}
private static void method3() throws IOException {
//在JDK11之后,字符流新推出了一个构造,也可以指定编码表
FileReader fr = new FileReader("C:\\Users\\apple\\Desktop\\a.txt", Charset.forName("gbk"));
int ch;
while ((ch = fr.read()) != -1) {
System.out.println((char) ch);
}
fr.close();
}
}
7 对象操作流
案例引入:
- 把这个用户信息保存到本地文件去.
public class User {
private String username;
private transient String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class ConvertedDemo2 {
public static void main(String[] args) throws IOException {
User user = new User("zhangsan", "qwer");
//需求:把这个用户信息保存到本地文件去.
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
bw.write(user.getUsername());
bw.newLine();
bw.write(user.getPassword());
bw.close();
}
}
用户信息会直接保存到a.txt文件中,但是这样不利于保护用户隐私,只要找到a.txt文件就可以看见用户信息,所以java提供了对象操作流。
- 对象序列化介绍
- 对象操作流分为两类:对象操作输入流和对象操作输出流
- 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
- 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
- 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
- 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
- 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
- 对象操作流分为两类:对象操作输入流和对象操作输出流
- 对象操作流的特点
- 可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。
7.1 对象序列化流
- ObjectOutputStream
- 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
- 构造方法
方法名 | 说明 |
---|---|
ObjectOutputStream(OutputStream out) | 创建一个写入指定的OutputStream的ObjectOutputStream |
- 序列化对象的方法
方法名 | 说明 |
---|---|
void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream |
import java.io.Serializable;
//如果想要这个类的对象能被序列化,那么这个类必须要实现一个接口.Serializable
//Serializable 接口的意义
//称之为是一个标记性接口,里面没有任何的抽象方法
//只要一个类实现了这个Serializable接口,那么就表示这个类的对象可以被序列化.
public class User implements Serializable {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ConvertedDemo3 {
public static void main(String[] args) throws IOException {
User user = new User("zhangsan", "qwer");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(user);
oos.close();
}
}
- 注意事项
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
7.2 对象反序列化流
- ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
- 构造方法
方法名 | 说明 |
---|---|
ObjectInputStream(InputStream in) | 创建从指定的InputStream读取的ObjectInputStream |
- 反序列化对象的方法
方法名 | 说明 |
---|---|
Object readObject() | 从ObjectInputStream读取一个对象 |
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ConvertedDemo4 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
User o = (User) ois.readObject();
System.out.println(o);
ois.close();
}
}
7.3 对象操作流注意点serialVersionUID&transient
- serialVersionUID
- 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 会出问题,会抛出InvalidClassException异常
- 如果出问题了,如何解决呢?
- 重新序列化
- 给对象所属的类加一个serialVersionUID
- private static final long serialVersionUID = 42L;
- 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- transient
- 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
- 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
- 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
import java.io.Serializable;
//如果想要这个类的对象能被序列化,那么这个类必须要实现一个接口.Serializable
//Serializable 接口的意义
//称之为是一个标记性接口,里面没有任何的抽象方法
//只要一个类实现了这个Serializable接口,那么就表示这个类的对象可以被序列化.
public class User implements Serializable {
//serialVersionUID 序列号
//如果我们自己没有定义,那么虚拟机会根据类中的信息会自动的计算出一个序列号.
//问题:如果我们修改了类中的信息.那么虚拟机会再次计算出一个序列号.
//第一步:把User对象序列化到本地. --- -5824992206458892149
//第二步:修改了javabean类. 导致 --- 类中的序列号 4900133124572371851
//第三步:把文件中的对象读到内存. 本地中的序列号和类中的序列号不一致了.
//解决?
//不让虚拟机帮我们自动计算,我们自己手动给出.而且这个值不要变.
private static final long serialVersionUID = 1L;
private String username;
private transient String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
import java.io.*;
public class ConvertedDemo5 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.写对象
//2.修改javabean类
//3.读对象
//写对象 --- 序列化
method1();
//读对象 --- 反序列化
method2();
}
private static void method1() throws IOException {
User user = new User("zhangsan", "qwer");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(user);
oos.close();
}
private static void method2() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
User o = (User) ois.readObject();
System.out.println(o);
ois.close();
}
}
7.4 对象操作流练习
案例需求
- 创建多个Javabean类对象写到文件中,再次读取到内存
思路:
- 创建学生对象
- 利用对象操作输出流写到本地
- 利用对象操作输入流读到内存
实现步骤
- 创建序列化流对象
- 创建多个学生对象
- 将学生对象添加到集合中
- 将集合对象序列化到文件中
- 创建反序列化流对象
- 将文件中的对象数据,读取到内存中
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 2L;
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 方式一:
import java.io.*;
public class ConvertedDemo6 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s1 = new Student("杜子腾", 16);
Student s2 = new Student("张三", 23);
Student s3 = new Student("李四", 24);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(s1);
oos.writeObject(s2);
oos.writeObject(s3);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
Object obj;
/* while((obj = ois.readObject()) != null){
System.out.println(obj);
}*/
while (true) {
try {
Object o = ois.readObject();
System.out.println(o);
} catch (EOFException e) {//证明文件读到了结尾
break;
}
}
ois.close();
}
}
- 方式二:
import java.io.*;
import java.util.ArrayList;
public class ConvertedDemo7 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s1 = new Student("杜子腾", 16);
Student s2 = new Student("张三", 23);
Student s3 = new Student("李四", 24);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
ArrayList<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
//我们往本地文件中写的就是一个集合
oos.writeObject(list);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
ArrayList<Student> list2 = (ArrayList<Student>) ois.readObject();
for (Student student : list2) {
System.out.println(student);
}
ois.close();
}
}
8 Properties集合
8.1 Properties作为Map集合的基本使用
- 是一个Map体系的集合类
- Properties中有跟IO相关的方法
- Properties可以保存到流中或从流中加载
- 只存字符串
- 属性列表中的每个键及其对应的值都是一个字符串
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo1 {
public static void main(String[] args) {
Properties prop = new Properties();
//增
prop.put("小龙女", "尹志平");
prop.put("郭襄", "杨过");
prop.put("黄蓉", "欧阳克");
System.out.println(prop);//{郭襄=杨过, 黄蓉=欧阳克, 小龙女=尹志平}
System.out.println("------------");
//删
prop.remove("郭襄");
System.out.println(prop);//{黄蓉=欧阳克, 小龙女=尹志平}
System.out.println("------------");
//改 put --- 如果键不存在,那么就添加,如果键存在,那么就覆盖.
prop.put("小龙女", "杨过");
System.out.println(prop);//{黄蓉=欧阳克, 小龙女=杨过}
System.out.println("------------");
//查
Object value1 = prop.get("黄蓉");
System.out.println(value1);//欧阳克
System.out.println("------------");
//遍历
Set<Object> keys = prop.keySet();
for (Object key2 : keys) {
Object value2 = prop.get(key2);
System.out.println(key2 + "=" + value2);//黄蓉=欧阳克 小龙女=杨过
}
System.out.println("------------");
//装的是所有的键值对对象.
Set<Map.Entry<Object, Object>> entries = prop.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
Object key3 = entry.getKey();
Object value3 = entry.getValue();
System.out.println(key3 + "=" + value3);//黄蓉=欧阳克 小龙女=杨过
}
}
}
8.2 Properties作为Map集合的特有方法
方法名 | 说明 |
---|---|
Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set< String > stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo2 {
public static void main(String[] args) {
//Object setProperty(String key, String value) --- put
//设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
Properties prop = new Properties();
prop.setProperty("江苏", "南京");
prop.setProperty("安徽", "南京");
prop.setProperty("山东", "济南");
System.out.println(prop);//{安徽=南京, 山东=济南, 江苏=南京}
//String getProperty(String key) --- get
//使用此属性列表中指定的键搜索属性
String value1 = prop.getProperty("江苏");
System.out.println(value1);//南京
//Set<String> stringPropertyNames() --- keySet
//从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Set<String> keys = prop.stringPropertyNames();
for (String key2 : keys) {
String value2 = prop.getProperty(key2);
System.out.println(key2 + "=" + value2);//安徽=南京 江苏=南京 山东=济南
}
}
}
8.3 Properties和IO流相结合的方法
方法名 | 说明 |
---|---|
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
load();
store();
}
private static void load() throws IOException {
//void load(Reader reader) 将本地文件中的键值对数据读取到集合中
Properties prop = new Properties();
FileReader fr = new FileReader("prop.properties");
//调用完了load方法之后,文件中的键值对数据已经在集合中了.
prop.load(fr);
fr.close();
System.out.println(prop);
}
private static void store() throws IOException {
//void store(Writer writer, String comments) 将集合中的数据以键值对形式保存在本地
Properties prop = new Properties();
prop.put("zhangsan", "123");
prop.put("lisi", "456");
prop.put("wangwu", "789");
FileWriter fw = new FileWriter("prop.properties");
prop.store(fw, null);
fw.close();
}
}
8.4 Properties集合练习
需求
- 在properties文件中手动写上姓名和年龄,读取到集合中,将该数据封装成javabean对象写到本地文件。
实现步骤
- 创建Properties集合,将本地文件中的数据加载到集合中
- 获取集合中的键值对数据,封装到学生对象中
- 创建序列化流对象,将学生对象序列化到本地文件中
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.io.*;
import java.util.Properties;
public class Test {
//1.将姓名和年龄手动保存在本地properties文件中
//2.读取本地properties文件中的数据
//3.将集合中的数据获取出来并创建对象
//4.将该对象写到本地
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
FileReader fr = new FileReader("prop.properties");
prop.load(fr);
fr.close();
String name = (String) prop.get("name");
int age = Integer.parseInt((String) prop.get("age"));
Student s = new Student(name, age);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(s);
oos.close();
}
}
9 IO和集合强化练习
9.1 案例1 点名器
需求
- 有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
思路
- 把文件中的数据读取到到集合中
- 使用Random产生一个随机数,获得随机索引。
- 通过随机索引获取随机姓名
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
public class Test1 {
//有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
public static void main(String[] args) throws IOException {
//1.先把文件中的姓名读取到集合中.
//字节流输入流 字节缓冲输入流 --- 拷贝 -- pass
//字符输入流 字符缓冲输入流
//单列 List(保留重复)
ArrayList<String> list = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader("collection-io-test\\names.txt"));
String line;
while ((line = br.readLine()) != null) {
list.add(line);
}
br.close();
System.out.println(list);
//2.获取一个随机索引
Random r = new Random();
int index = r.nextInt(list.size());
//3.通过这个随机索引获取对应的元素
String name = list.get(index);
System.out.println(name);
}
}
9.1 案例2 点名器升级版
需求
- 有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
- 第三次必定是张三同学
思路:
- 第一次运行时随机的姓名
- 第二次运行时随机的姓名
- 第三次运行时随机的姓名
难点:
- 如何确定当前是第几次运行程序?
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
public class Test2 {
public static void main(String[] args) throws IOException {
// int count = 0;//第一次运行代码的时候,定义了一个count
//第二次运行代码的时候,重新定义了一个count
// count++;
//1.先把文件中的数据读到集合中
BufferedReader br = new BufferedReader(new FileReader("collection-io-test\\names.txt"));
String line;
ArrayList<String> list = new ArrayList<>();
while ((line = br.readLine()) != null) {
list.add(line);
}
br.close();
System.out.println(list);
//读取本程序已经运行了多少次.
BufferedReader brCount = new BufferedReader(new FileReader("collection-io-test\\count.txt"));
String countStr = brCount.readLine();
//表示程序已经运行了多少次.
int count = Integer.parseInt(countStr);
brCount.close();
//表示当前程序又运行了一次.
count++;
if (count == 3) {
System.out.println("张三");
} else {
//2.随机获取一个索引
Random r = new Random();
int index = r.nextInt(list.size());
//3.通过随机索引获取元素
String name = list.get(index);
System.out.println(name);
}
BufferedWriter bw = new BufferedWriter(new FileWriter("count.txt"));
//是不能直接写一个count的值.
bw.write(count + "");//表示先将数字变成字符串,然后将字符串原样写出. 1
bw.close();
}
}
9.1 案例3 登陆案例
需求
- 写一个登陆小案例。
步骤
- 将正确的用户名和密码手动保存在本地的userinfo.txt文件中。保存格式为:username=zhangsan&password=123
- 让用户键盘录入用户名和密码
- 比较用户录入的和正确的用户名密码是否一致
- 如果一致则打印登陆成功
- 如果不一致则打印登陆失败
import java.util.Objects;
public class User {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(username, user.username) &&
Objects.equals(password, user.password);
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;
public class Test3 {
public static void main(String[] args) throws IOException {
//1.得到文件中正确的用户名和密码
User rightUserInfo = getRightUserInfo();
//2.得到用户键盘录入的用户名和密码
User userInputInfo = getUserInputInfo();
//3.比较两者是否一致.
//if(rightUserInfo.getUsername().equals(userInputInfo.getUsername()) && ){
if(rightUserInfo.equals(userInputInfo)){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
}
private static User getUserInputInfo() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名");
String username = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
return new User(username,password);
}
private static User getRightUserInfo() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("collection-io-test\\userinfo.txt"));
String line = br.readLine();
br.close();
System.out.println(line);//username=zhangsan&password=123
String[] split = line.split("&");
//0索引:username=zhangsan
//1索引:password=123
String username = split[0].split("=")[1];
String password = split[1].split("=")[1];
//可以把用户名和密码封装成一个javabean对象,直接返回这个对象就可以了.
return new User(username,password);
}
}
9.1 案例4 自动登陆案例
需求
- 写一个自动登陆小案例。
步骤
- 将正确的用户名和密码手动保存在本地的userinfo.txt文件中。保存格式为:username=zhangsan&password=123
- 让用户键盘录入用户名和密码
- 比较用户录入的和正确的用户名密码是否一致
- 如果一致则打印登陆成功,并将用户录入的数据保存到本地cookie.txt文件中。
- 保存格式为:username=zhangsan
- password=123
- 保存格式为:username=zhangsan
- 如果不一致则打印登陆失败
- 再次运行时,则从本地cookie.txt文件中读取第一次保存的数据,实现自动登陆。
import java.util.Objects;
public class User {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(username, user.username) &&
Objects.equals(password, user.password);
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
import java.io.*;
import java.util.Scanner;
public class Test4 {
public static void main(String[] args) throws IOException {
//这个是正确的用户名和密码
String rightUsername = "zhangsan";
String rightPassword = "123";
User rightUser = new User(rightUsername, rightPassword);
File file = new File("collection-io-test\\cookie.txt");
User user = null;
if (file.exists()) {
//存在 --- 表示本次是第二次或者多次登录
//从本地读取数据
user = readUserInfo(file);
} else {
//不存在 --- 表示本次是第一次登录
//键盘录入用户名和密码
user = getUserInputInfo();
}
//比较两者是否一致
if (rightUser.equals(user)) {
//一致,提示登录成功,需要将正确的用户名和密码写到本地文件,
//目的:为了下次的自动登录.
System.out.println("登录成功");
if (!file.exists()) {
writeUser(user);
}
} else {
//不一致,提示登录失败
System.out.println("登录失败");
}
}
private static User readUserInfo(File file) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(file));
String usernameInfo = br.readLine();//username=zhangsan
String passwordInfo = br.readLine();//password=123
br.close();
String username = usernameInfo.split("=")[1];
String password = passwordInfo.split("=")[1];
return new User(username, password);
}
private static void writeUser(User user) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("collection-io-test\\cookie.txt"));
bw.write("username=" + user.getUsername());
bw.newLine();
bw.write("password=" + user.getPassword());
bw.close();
}
private static User getUserInputInfo() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名");
String username = sc.nextLine();
System.out.println("请输入密码");
String password = sc.nextLine();
return new User(username, password);
}
}
9.1 案例5 复制多级文件夹
需求
- 把“E:\itcast”这个文件夹复制到 F盘目录下
分析
- 数据源:E:\itcast
- 目的地:F:\
- 判断数据源是目录还是文件
- 是文件:直接复制,用字节流
- 是目录:
- 在目的地下创建该目录
- 遍历获取该目录下的所有文件的File数组,得到每一个File对象
- 回到3继续(递归)
思路
- 创建数据源File对象,路径是E:\itcast
- 创建目的地File对象,路径是F:\
- 写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
- 判断数据源File是否是文件
- 是文件:直接复制,用字节流
- 不是文件:
- 在目的地下创建该目录
- 遍历获取该目录下的所有文件的File数组,得到每一个File对象
- 回到3继续(递归)
import java.io.*;
public class Test5 {
public static void main(String[] args) throws IOException {
//明确一件事情,拷贝,一定是从文件开始,到文件结束的.
File src = new File("D:\\itheima\\src");
File dest = new File("D:\\itheima\\dest");
copy(src, dest);
}
private static void copy(File src, File dest) throws IOException {
dest.mkdirs();//创建一个多级文件夹,如果文件夹存在,就创建失败,如果不存在就创建成功.
//1.进入数据源文件夹,得到里面每一个文件和文件夹的路径
File[] files = src.listFiles();
//2.遍历
for (File file : files) {
//file依次表示src文件夹里面的每一个文件和文件夹的路径
if (file.isFile()) {
//3.判断 --- 拷贝
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
} else {
//4.判断 --- 递归
copy(file, new File(dest, file.getName()));
}
}
}
}