19IO流
1. 文件
2. 常用的文件操作
2.1 创建文件对象相关构造器和方法
案例:
public class FileCreate {
public static void main(String[] args) {
}
//方式1:new File(String pathname)
@Test//通过Junit测试
public void create01(){
String filePath = "e:\\news1.txt";//注意这里的"\\"是因为"\\"前一个是转义符,代码中两个\才表示实际中一个\
File file = new File(filePath);//到这里只是在内存中创建了一个对象,但是e盘中还没有文件
try {
file.createNewFile();//执行了createNewFile(),才会在磁盘中创建该文件
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式2:new File(File parent,String child)//根据父目录"文件"+"子路径" 构建
@Test
public void create02(){
File parentFile = new File("e:\\");
String filePath = "news2.txt";
File file = new File(parentFile, filePath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式3:new File(String parent,String child)//根据父目录+子路径构建
@Test
public void create03(){
String parentPath = "e:\\";//实际上这里这样写也可以"e:/"
String filePath = "news3.txt";
File file = new File(parentPath, filePath);
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 获取文件相关信息
案例演示:
//获取文件的信息
@Test
public void info(){
File file = new File("e:\\news1.txt");
//getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
System.out.println("文件名称=" + file.getName());
System.out.println("文件绝对路径=" + file.getAbsolutePath());
System.out.println("文件的父目录=" + file.getParent());
System.out.println("文件的大小(字节)=" + file.length());//txt是utf-8,一个字母一个字节,一个汉字三个字节(hello你好一共11字节)
System.out.println("文件是否存在=" + file.exists());//T
System.out.println("是不是一个文件=" + file.isFile());//T
System.out.println("是不是一个目录=" + file.isDirectory());//F
}
2.3 目录的操作和文件删除
注意:delete删除目录时,这个目录下面必须为空(不包含任何文件)
//判断d:\\news1.txt是否存在,如果存在就删除
@Test
public void m1(){
File file = new File("e:\\news1.txt");
if (file.exists()){
if (file.delete()){
System.out.println(file.getPath() + "删除成功");
}else {
System.out.println(file.getPath() + "删除失败");
}
}else {
System.out.println("文件不存在");
}
}
//判断D:\\demo02是否存在,存在就删除,否则提示不存在
//这里我们要体会到,java编程中,目录也被当做文件
@Test
public void m2(){
File file = new File("D:\\demo02");
if (file.exists()){
if (file.delete()){
System.out.println(file.getPath() + "删除成功");
}else {
System.out.println(file.getPath() + "删除失败");
}
}else {
System.out.println("文件不存在");
}
}
//判断D:\\demo\\a\\b\\c目录是否存在,如果存在就提示已经存在,否则就创建
@Test
public void m3(){
String directoryPath = "D:\\demo\\a\\b\\c";
File file = new File(directoryPath);
if (file.exists()){
System.out.println(directoryPath + "已经存在");
}else {
if (file.mkdirs()){//创建一级目录使用mkdir()如:"D:\\demo",创建多级目录用mkdirs()
System.out.println(directoryPath + "创建成功");
}else {
System.out.println(directoryPath + "创建失败");
}
}
}
3. IO流原理及流的分类
3.1 IO流原理
3.2 IO流分类
注意:
-
字节流效率比字符流低。但是字节流操作二进制文件(声音、视频、word)可以保证无损操作。字符流操作文本文件比较好。
-
InputStream、OutputStream 和 Reader、Writer 都是抽象类,因此在使用时要创建他们的实现子类才行
4. IO流体系-常用的类
4.1 InputStream
/**
* 演示InputStream的子类FileInputStream的使用(字节输入流 文件 -> 程序)
*/
public class FileInputStream_ {
public static void main(String[] args) {
}
/**
* 演示读取文件...
* .read()单个字节的读取,效率较低
*/
@Test
public void readFile01(){
String filePath = "e:\\hello.txt";
FileInputStream fileInputStream = null;//创建FileInputStream对象,用于读取文件
int readData = 0;//用于接收文件数据
try {
fileInputStream = new FileInputStream(filePath);
//从该输入流读取一个字节的数据。如果没有输入可用,此方法将终止
//.read()返回的是一个int类型,这里要转成字符类型。如果返回一个-1就表示读取完毕
while ((readData = fileInputStream.read()) != -1){
System.out.print((char)readData);
}
} catch (IOException e) {//这里要改成IOException,上面的.read()才不会报异常
e.printStackTrace();
}finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 使用 read(byte[] b)效率比较高
*/
@Test
public void readFile02(){
String filePath = "e:\\hello.txt";
FileInputStream fileInputStream = null;//创建FileInputStream对象,用于读取文件
byte[] buf = new byte[8];//一次读取8个字节
int readLen = 0;
try {
fileInputStream = new FileInputStream(filePath);
//从该输入流读取最多buf.length个字节的数据到字节数组。
//.read(byte[] buf) 返回的是一个int类型,为实际读取的字节数。如果返回一个-1就表示读取完毕
while ((readLen = fileInputStream.read(buf)) != -1){
//new String(字符数组, offset, length),由字符数组的[offset,length)构造一个字符串
System.out.print(new String(buf, 0, readLen));
}
} catch (IOException e) {//这里要改成IOException,上面的.read()才不会报异常
e.printStackTrace();
}finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*注意:如果上面的"e:\\hello.txt"文件里本身是hello,word,如果变为hello,word你好,那么上满的readFile01()方法就会hello,word+6个乱码,因为这里是按照一个字节一个字节读的,但是一个汉字字符占3个字节,所以“你好”占6个字节,而每次只读取一个字节并把它转化为char类型输出,所以是乱码
4.2 OutputStream
//以OutputStream的实现子类FileOutputStream为例
public class FileOutputStream01 {
public static void main(String[] args) {
}
/**
* 演示使用FileOutputStream,将数据写到文件中,如果该文件不存在则创建该文件
*/
@Test
public void writeFile(){
String filePath = "e:\\a.txt";
FileOutputStream fileOutputStream = null;
try {
//注意:
//1.new FileOutputStream(filePath) 创建方式,当写入内容时,会覆盖原来的内容
//2.new FileOutputStream(filePath, true) 创建方式,当写入内容时,是追加到文件后面
//fileOutputStream = new FileOutputStream(filePath);
fileOutputStream = new FileOutputStream(filePath,true);
//1.写入一个字节:write(int b) 将指定的字节写入此文件输出流。
//fileOutputStream.write('H');//执行后创建e:\a.txt,里面的内容为‘H’
//2.write(byte[] b) 将 b.length个字节从指定的字节数组写入此文件输出流。
String str = "hsp,world!";
//fileOutputStream.write(str.getBytes());//str.getBytes()可以把字符串转化为字符数组。此时a.txt内容变为hsp,world!
//3.write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。
fileOutputStream.write(str.getBytes(), 0, 3);///执行后创建e:\a.txt,里面的内容为 hsp
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//释放资源
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.3 文件拷贝
//将e盘下面的图片e:\\bg.png拷贝到d:\\bg.png
public class FileCopy {
public static void main(String[] args) {
//完成 文件拷贝,将 e:\\bg.png 拷贝到 d:\\
//思路分析
//1.创建文件的输入流,将文件读入到程序
//2.创建文件的输出流,将读取到的文件数据,写入到指定的文件
//注意:要边写边读,这是为了防止数据过大,内存装不下
//特别特别注意:拷贝图片,一定要用字节流,不能用字符流啊
String srcFilePath = "e:\\bg.png";
String desFilePath = "d:\\bg.png";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(srcFilePath);
fileOutputStream = new FileOutputStream(desFilePath);
//定义一个字节数组,提高读取效率
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buf)) != -1){
//边读边写。每次读取1024个字节就通过fileOutputStream写入(注意:最后一次读取可能不是1024个字节,所以想下面要用readLen)
fileOutputStream.write(buf, 0, readLen);
}
System.out.println("拷贝成功...");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输入输出流,释放资源
try {
if (fileInputStream != null){
fileInputStream.close();
}
if (fileOutputStream != null){
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.2 FileReader 和 FileWriter
FileReader 和 FileWriter 是字符流,即按照字符来操作 io
- 应用案例:
public class FileReader_ {
public static void main(String[] args) {
}
/**
* 单个字符读取文件
*/
@Test
public void readFile01(){
String filePath = "e:\\story.txt";
FileReader fileReader = null;
int data = 0;
try {
//创建FileReader对象,按照字符流操作
fileReader = new FileReader(filePath);
//fileReader.read() 每次读取单个字符,返回的是一个int类型(一个字符)。
while ((data = fileReader.read()) != -1){
System.out.print((char)data);//输出的时候我们要重新转型(char)
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null){
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 字符数组读取文件
*/
@Test
public void readFile02(){
String filePath = "e:\\story.txt";
FileReader fileReader = null;
char[] buf = new char[8];
int readLen = 0;
try {
//创建FileReader对象,按照字符流操作
fileReader = new FileReader(filePath);
//fileReader.read(buf) 返回的是实际读取到的字符数,如果返回-1表示读取文件结束
while ((readLen = fileReader.read(buf)) != -1){//读取到的字符数组存入buf中
System.out.print(new String(buf, 0, readLen));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null){
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class FileWriter_ {
public static void main(String[] args) {
String filePath = "e:\\note.txt";
FileWriter fileWriter = null;
char[] chars = {'a', 'b', 'c'};
try {
fileWriter = new FileWriter(filePath);//默认是覆盖写入
//方式1:write(int) 写入单个字符
fileWriter.write('H');//H
//方式2:write(char[]) 写入指定数组
fileWriter.write(chars);//Habc
//方式3:write(char[], off, len) 写入指定数组的指定部分
fileWriter.write("韩顺平教育".toCharArray(), 0, 3);//Habc韩顺平
//方式4:write(string) 写入整个字符串
fileWriter.write(" 北京你好~");//Habc韩顺平 北京你好~
//方式5:write(string, off, len) 写入字符串的指定部分
fileWriter.write("上海天津", 0, 2);//Habc韩顺平 北京你好~上海
//在数据量大的情况下,可以使用循环操作
} catch (IOException e) {
e.printStackTrace();
} finally {
//对于FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
try {
//.close()等价于 .flush() + 关闭
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5. 节点流和处理流
5.1 基本介绍
注意:
-
节点流只能针对某一种特定的数据进行操作,比如FileReader/FileWriter只能对文件进行操作(按字符),ByteArrayInputStream / ByteArrayOutputStream只能对数组进行操作(按字节操作)等等,这样就造成了节点流是固定的,这是跟数据源直接相关的,更加底层。灵活性不足。
-
包装流,以BufferedReader / BufferedWriter为例,这两个类的内部定义了一个他们的父类属性 Reader / Writer,源码如下:
意味着这两个 Reader/Writer 内部可以接受一个他们父类的属性或者父类下面的所有子类(向上转型),这一点可以看其构造器如下,导致他们可以对任意数据类型进行操作。比如如果对文件进行操作,那么BufferedReader的Reader属性接收一个FileReader对象,就可以对文件进行读操作,如果想对数组、管道进行操作也同理,更加灵活。
这其实是一个修饰器模式:子类里面定义一个父类属性,导致该子类可以通过这个父类属性拥有该父类其他子类的功能。
5.2 节点流和处理流区别于联系
模拟修饰器设计模式案例:
/**
* 模拟Reader类
*/
public abstract class Reader_ {//抽象类
public void stringRead(){}
public void fileRead(){}
}
/**
* 模拟节点流,FileReader类
*/
public class FileReader_ extends Reader_{
public void fileRead(){
System.out.println("读取文件...");
}
}
/**
* 模拟节点流,StringReader类
*/
public class StringReader_ extends Reader_{
public void stringRead(){
System.out.println("读取字符串...");
}
}
/**
* 模拟处理流(包装流)
*/
public class BufferedReader_ extends Reader_{
private Reader_ reader_; //属性是Reader_类型
public BufferedReader_(Reader_ reader_) {
this.reader_ = reader_; //可以接收 一个Reader_的实现子类
}
//可以扩展 fileRead, 多次读取文件,或者加缓冲byte[]
public void filesRead(int num) {
for (int i = 0; i < num; i++) {
reader_.fileRead();
}
}
//可以扩展 stringRead,批量处理字符串数据
public void stringsRead(int num) {
for (int i = 0; i < num; i++) {
reader_.stringRead();
}
}
}
//测试
public class Test_ {
public static void main(String[] args) {
//我们现在想对文件进行操作
BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
bufferedReader_.filesRead(10);
//现在又想对字符串进行操作
bufferedReader_ = new BufferedReader_(new StringReader_());
//注意:如果下面变为.filesRead(5),那么也不会报错,只是执行的是父类的.fileRead()5次(空操作)
bufferedReader_.stringsRead(5);
}
}
5.3 处理流
解释上面的关闭:
BufferedReader buffered = new BufferedReader(new FileReader);
实际上buffered对文件的读操作还是靠对象new FileReader实现的,如果我们关闭了buffered(外层流),也就关闭了FileReader对象
5.3.1 字符处理流
BufferedReader 和 BufferedWriter
- BufferedReader 案例:
/**
* 演示BufferedReader 的使用
*/
public class BufferedReader_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.java";
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//我们调用BufferedReader读取文件,实际上使用的是FileReader读取文件
String line;
//.readLine() 按行读取效率高。
// 1.返回的是一个字符串(文件中的一行数据)
// 2.当返回null时,表示文件读取完毕
while ((line = bufferedReader.readLine()) != null){
System.out.println(line);//注意.readLine()只会返回一行数据,不会读取到换行符所以用.println
}
//关闭流,这里注意只需要关闭BufferedReader,因为底层会自动去关闭节点流FileReader
bufferedReader.close();
/*
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close(); //这里这个in就是定义BufferedReader时传入的FileReader
} finally {
in = null;
cb = null;
}
}
}
*/
}
}
- BufferedWriter 案例:
/**
* 演示BufferedWriter 的使用
*/
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.java";
//使用BufferedWriter来写文件,实际上还是使用FileWriter来写的
//1.new FileWriter(filePath) 表示以覆盖的方式写入
//2.new FileWriter(filePath,true) 表示以追加的方式写入
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
bufferedWriter.write(" hello,韩顺平教育");
bufferedWriter.newLine();//插入一个与系统相关的换行符
bufferedWriter.write(" hello,韩顺平教育");
bufferedWriter.newLine();
//关闭外层流即可,传入的new FileWriter(filePath)会在底层自动关闭
bufferedWriter.close();
}
}
- 拷贝文件:
public class BufferedCopy_ {
public static void main(String[] args) {
//1.BufferedReader 和 BufferedWriter是按照字符操作的,
// 不要去操作二进制文件(声音、视频、doc、pdf),否则会造成文件损坏
String srcFilePath = "e:\\a.java";
String desFilePath = "e:\\b.java";
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
String line;
try {
bufferedReader = new BufferedReader(new FileReader(srcFilePath));
bufferedWriter = new BufferedWriter(new FileWriter(desFilePath));
while ((line = bufferedReader.readLine()) != null){
bufferedWriter.write(line);
bufferedWriter.newLine();//注意这里的.write()要插入一个换行符
}
System.out.println("拷贝完毕...");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.3.2 字节处理流
前面的两个处理流都是按字符操作的字符流,下面讲两个按字节进行操作的字节流——BufferedInputStream 和 BufferedOutputStream
/**
* 演示使用BufferedInputStream 和 BufferedOutputStream,使用他们可以完成二进制文件拷贝
* 思考:字节流可以操作二进制文件,可以操作文本文件吗?可以,没有乱码
*/
public class BufferedCopy02 {
public static void main(String[] args) {
// String srcFilePath = "e:\\bg.png";
// String desFilePath = "e:\\dx.png";
//上面是拷贝图片,下面是测试字节处理流拷贝文档会不会有中文乱码
String srcFilePath = "e:\\a.txt";
String desFilePath = "e:\\b.txt";
//创建BufferedInputStream 和 BufferedOutputStream对象
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//因为FileInputStream 是 InputStream 的子类
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(desFilePath));
//循环的读取文件,并写入到desFilePath
byte[] buf = new byte[1024];
int readLen = 0;
//当返回-1的时候就读取结束
while ((readLen = bis.read(buf)) != -1){
bos.write(buf);
//System.out.println("buf"); //注意:如果这里输出buf中文会乱码,但是上面的写入文件不会乱码
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭外层的处理流即可,底层会去关闭关联的节点流
try {
if (bis != null){
bis.close();
}
if (bos != null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.3.3 对象处理流
- 之前讲的都是只保存值,现在不仅要保存值还要保存数据类型。即不仅要保存数据,还要保存数据的数据类型,保存一个Dog对象,那么取出该对象时仍然 是一个Dog类对象的数据。
- 上面的两个接口,推荐使用 Serializable
下面开始介绍:ObjectInputStream 和 ObjectOutputStream:
上面显示了这两个对象流的构造器,说明了他们也用了 修饰器模式
-
ObjectOutputStream 序列化案例:
/** * 演示ObjectOutputStream 的使用,完成数据的序列化 */ public class ObjectOutputStream_ { public static void main(String[] args) throws IOException { //序列化后,保存的文件格式不是纯文本的,而是按照他的格式来保存 String filePath = "e:\\data.dat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); //序列化数据到e:\data.dat //这里100会自动装箱 int -> Integer(Integer是实现了Serializable接口的,因此可以序列化)。如果是oos.write(100)那就只是存放了值100,而没有类型Integer oos.writeInt(100); oos.writeBoolean(true);//boolean -> Boolean (实现了Serializable) oos.writeChar('a');//char -> Character (实现了Serializable) oos.writeDouble(9.5);//double -> Double (实现了Serializable) oos.writeUTF("韩顺平教育");//String (实现了Serializable) //保存一个Dog类的对象,该Dog类必须实现了两个接口中的一个,不然会出现运行异常 oos.writeObject(new Dog("旺财", 10)); oos.close(); System.out.println("数据保存完毕(序列化形式)"); } } //如果需要序列化某个类的对象,则该类必须实现Serializable接口或Externalizable接口 class Dog implements Serializable { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; } } //文件打开如下:
-
演示ObjectInputStream 反序列化案例:
public class ObjectInputStream02 { public static void main(String[] args) throws IOException, ClassNotFoundException { //指定反序列化的文件 String filePath = "e:\\data.dat"; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath)); //读取(反序列化)的顺序一定要和存放(序列化)的顺序一致。否则会出现异常 System.out.println(ois.readInt()); System.out.println(ois.readBoolean()); System.out.println(ois.readChar()); System.out.println(ois.readDouble()); System.out.println(ois.readUTF()); Object dog = ois.readObject(); //底层会转型 Object -> Dog System.out.println("运行类型: " + dog.getClass()); //Dog System.out.println("dog信息:" + dog); //注意这里如果我们需要调用Dog类的方法,首先要对上面的dog进行向下转型,因此我们要把Dog类放在一个能访问的位置,可以把Dog类声明为public的 //关闭流,关闭外层流即可,底层会关闭 FileInputStream 流 ois.close(); } } //输出如下:
- 注意细节
class Dog implements Serializable {
private String name;
private int age;
//序列化对象时,默认将里面的所有属性都进行序列化,但是除了static或transient修饰的成员
private static String nation;
private transient String color;
//序列化对象时,要求里面属性的类型也需要实现序列化接口
Master master = new Master();//如果Master没有实现序列化接口,则会报错
//构造器..省略
}
5.3.4 标准输入输出流
public class InputAndOutput {
public static void main(String[] args) {
//1.System.in 是System类的 public final static InputStream in = null;
//2.System.in 的编译类型 InputStream
//3.System.in 的运行类型 BufferedInputStream
//4.System.in 表示标准输入,标准输入其实就是键盘
System.out.println(System.in.getClass());
//1.System.out 是System类的 public final static PrintStream out = null;
//2.System.out 的编译类型 PrintStream
//3.System.out 的运行类型 PrintStream
//4.System.out 表示标准输出,标准输出其实就是显示器
System.out.println(System.out.getClass());
}
}
注意:
-
上面的 System.out 就表示标准输出(显示器);
-
Scanner scanner = new Scanner(System.in); 就表示给Scanner扫描器传入了一个BufferedInputStream,并且这个扫描器从键盘获取值
5.3.5 转换流
- 先看一个文件乱码问题,引出学习转换流的必要性
//看一个中文乱码问题
public class CodeQuestion {
public static void main(String[] args) throws IOException {
//思路:先创建字符处理流 BufferedReader, 使用 BufferedReader 对象读取 a.txt
String filePath = "e:\\a.txt";
BufferedReader br = new BufferedReader(new FileReader(filePath));
//默认情况下,读取文件是按照 utf-8 编码,但是如果保存文件时没有按照utf-8就会出现中文乱码
String line = br.readLine();
System.out.println("读取到的内容:" + line);
br.close();
}
}
- 转换流:InputStreamReader 和 outputStreamWriter
- InputStreamReader 和 outputStreamWriter 的构造器如下所示:
InputStreamReader(InputStream, Charset)//可以传入一个InputStream对象,而且可以指定处理的编码
OutputStreamWriter(OutputStream, Charset)//可以传入一个OutputStream对象,而且可以指定处理的编码
/***
* 演示使用 InputStreamReader 转换流解决中文乱码问题
* 将字节流 FileInputStream 转成字符流 InputStreamReader, 指定编码 gbk/utf-8
*/
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\a.txt";
//1.把 FileInputStream(字节节点流) 转换成 InputStreamReader(字符处理流),并制定编码gbk(因为a.txt文件是按照gbk保存的)
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//2.把 InputStreamReader(字符处理流)传入 BufferedReader(处理流)
BufferedReader br = new BufferedReader(isr);
//4.读取。用 BufferedReader 进行处理,但是底层还是使用的是 FileInputStream
String s = br.readLine();
System.out.println("读取到的内容:" + s);
//5.关流。还是关外层流即可
br.close();
}
}
//输出中文不会乱码了
/**
* 演示OutputStreamWriter 使用
* 把FileOutputStream 字节流,转换成字符流 OutputStreamWriter,指定处理的编码 gbk/utf-8 也可以写成 utf8
*/
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\hsp.txt";
String charSet = "gbk";
//按照"gbk"编码保存"e:\\hsp.txt"文件
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
osw.write("hi, 韩顺平教育");
osw.close();
System.out.println("按照" + charSet + " 保存文件成功~");
}
}
6. 打印流
- 注意:PrintStream(字节流) 和 PrintWriter(字符流)。打印流只有输出流,没有输入流。
6.1 字节打印流
/**
* 演示 PrintStream 字节打印流
*/
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
//在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
/*
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
*/
out.print("john, hello");
//因为print的底层是write方法,所以我们可以直接调用write进行打印/输出
//解释为什么上面的write(s)不用转换成字节,因为PrintStream类里面有一个private方法:write(String s)
out.write("韩顺平,你好".getBytes());
//out.close();
//我们可以去修改打印流输出的位置/设备
//1.输出修改成到"e:\\f1.txt"
//2."hello, 韩顺平教育~" 就会输出到 e:\\f1.txt
/*
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);//native方法
}
*/
System.setOut(new PrintStream("e:\\f1.txt"));
System.out.println("hello, 韩顺平教育~");//输出到f1.txt
System.setOut(out);
System.out.println("韩顺平!");//又输出到显示器了
out.close();
}
}
6.2 字符打印流
/**
* 演示PrintWriter使用方式
*/
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
//PrintWriter printWriter = new PrintWriter(System.out);//输出到显示器
PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));//输出到 f2.txt
printWriter.print("hi,你好北京");
printWriter.close();//如果没有关闭,则上面的内容显示不出来(没有close()就不会刷新,就不会真正写东西进去)
}
}
7. Properties 类
7.1 先看一个需求
public class Properties_ {
public static void main(String[] args) throws IOException {
//读取mysql.properties文件,并得到ip,user和pwd
BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
String line = "";
while ((line = br.readLine()) != null) {//因为不知道文件有多少行,所以要循环读取
String[] split = line.split("=");
//如果我们要求指定得到ip值,就要按照如下方法获取,比较麻烦
if ("ip".equals(split[0])) {
System.out.println(split[0] + "值是:" + split[1]);
}
}
br.close();
}
}
7.2 Properties 类基本介绍
7.3 案例
//案例1:读取properties文件
public class Properties02 {
public static void main(String[] args) throws IOException {
//使用Properties 类来读取mysql.properties文件
//1.创建Properties对象
Properties properties = new Properties();
//2.加载指定配置文件。new FileReader以字符的方式读取
properties.load(new FileReader("src\\mysql.properties"));
//3.把k-v显示到控制台
properties.list(System.out);
//4.根据一个key获取对应的值
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("\n用户名=" + user);
System.out.println("密码=" + pwd);
}
}
//案例2:创建properties文件,修改键值对
public class Properties03 {
//使用Properties 类来创建 配置文件,修改配置文件内容
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
//创建
//1.如果该文件没有key 就是创建
//2.如果该文件有 key 就是修改
//3.Properties 父类是 Hashtable, 底层就是 Hashtable。
// 和新方法是Hashtable 的 public synchronized V put(K key, V value)
properties.setProperty("charset", "utf-8");
properties.setProperty("user", "汤姆");//注意:保存时,是中文的unicode码值
properties.setProperty("pwd", "abc111");//到这里,现在这个k-v还在内存里面
properties.setProperty("pwd", "8888888");//修改:abc111 被替换成了 8888888
//存储k-v到文件里面.store(Writer, null) 后面这个参数是注释,写在配置文件的最上面,一般无要求就置空
//properties.store(new FileWriter("src\\mysql2.properties"), null);//按字符写入,写入到配置文件中的中文还是中文
properties.store(new FileOutputStream("src\\mysql2.properties"), null);//按字节写入,写入到配置文件中的中文是unicode码
System.out.println("保存配置文件成功~");
}
}