IO流
1.输入流与输出流
- 流:数据在数据源(文件)和程序(内存)之间的路径
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
常用的文件操作
File类实现了Serializable与Comparable接口
File类的构造器
new File(String pathname)//根据路径构建一个File对象
new File(File parent,String pathname)//根据父目录文件+子路径构建一个File对象
new File(String parent,Sting child)//根据父路径+子路径构建一个File对象
new File (URI uri)//通过将给定的 file:
URI 转换为一个抽象路径名来创建一个新的 File
实例。
public static void main(String[] args) throws IOException, URISyntaxException {
File file1=new File("d:/news1.txt");
File file2=new File("d:\\","news2.txt");
File file3=new File("d:\\");
File file4=new File(file3,"news3.txt");
URI uri=new URI("file:///d:/news4.txt");
File file5= new File(uri);
//上面的file1、file2..等 只是在java中的一个对象
//只有执行了 createNewFile()方法,才会真正在磁盘中创建该文件 if(file1.createNewFile()&&file2.createNewFile()&&file4.createNewFile()&&file5.createNewFile()){
System.out.println("文件创建成功!");
}else {
System.out.println("文件创建失败!");
}
}
File对象常用的一些方法
- file.getName()//返回由此抽象路径名表示的文件或目录的名称。
- file.getAbsoluteFile()//返回此抽象路径名的绝对路径名形式。返回类型是File
- file.getAbsolutePath()//返回此抽象路径名的绝对路径名字符串。返回类型是String
- file.getParent()//返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null
- file.length()//返回由此抽象路径名表示的文件的长度。
- file.exists()//测试此抽象路径名表示的文件或目录是否存在。
- file.isFile()//测试此抽象路径名表示的文件是否是一个标准文件。
- file.isDirectory()//测试此抽象路径名表示的文件是否是一个目录。
- file.mkdir()// 创建此抽象路径名指定的目录(一级目录)。
- file.mkdir()//创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。(创建多级目录)
流的分类
字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer
字节输入流(InputStream)常用的子类
- FileInputStream:文件输入流
- BufferedInputStream:缓冲字节输入流
- ObjectInputStream:对象字节输入流
2.字节流
FileInputStream
/**
* 单个字节读取效率低
*/
public void read01() {
String path="d:\\news1.txt";
FileInputStream fileInputStream=null;
try {
fileInputStream=new FileInputStream(path);
int read = 0;
while ((read=fileInputStream.read())!=-1){
System.out.print((char) read);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void read2(){
String path="d:\\news1.txt";
FileInputStream fileInputStream=null;
//定义字节数组
byte[] buf=new byte[8];
try {
fileInputStream=new FileInputStream(path);
int readLen= 0;
//fileInputStream.read(buf) 如果读取正常返回实际读取的字节数 每次最多读8字节
//如果返回-1,表示读取完毕
while ((readLen=fileInputStream.read(buf))!=-1){
System.out.print(new String(buf,0,readLen));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileOutPutStream
public class OutPutStream_ {
public static void main(String[] args) {
new OutPutStream_().writeFile();
}
public void writeFile(){
String filePath="d:\\news.txt";
FileOutputStream fos=null;
try {
//若没有该文件,则会自动创建
fos=new FileOutputStream(filePath);
/**
* 1、new FileOutputStream(filePath);这种创建方式,当写入内容时,会覆盖其内容
* 2、new FileOutputStream(filePath,true);这种方式创建,当写入内容时,会以追加的方式,不会覆盖原内容
*/
//写入一个字节
fos.write('a');
String str="wanna";
//将字符串转化为字符数组 str.getBytes()
fos.write(str.getBytes());
/**
* write(byte[] b, int off, int len)
* 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
*/
fos.write(str.getBytes(),0,3);//截取前三个字节写入
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
两者结合使用 拷贝文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
String filePath="d:\\a.jpg";
String destFilePath="c:\\a.jpg";
byte[] bytes=new byte[1024];
int readLen=0;
try {
fis=new FileInputStream(filePath);
fos=new FileOutputStream(destFilePath);
while ((readLen=fis.read(bytes))!=-1){
fos.write(bytes,0,readLen);//一定要使用这个方法
}
System.out.println("拷贝完成!");
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (fis != null) {
fis.close();
}
if (fos != null) {
fos.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
3.字符流
FileReader与FileWriter
FileReader与FileWriter都是字符流,按照字符操作IO
FileReader
import java.io.FileReader;
import java.io.IOException;
public class FileReader_ {
public void reader01(){
FileReader fileReader=null;
try {
fileReader=new FileReader("d:\\news.txt");
int data=0;
while ((data=fileReader.read())!=-1)
{
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void reader02(){
FileReader fileReader=null;
try {
fileReader=new FileReader("d:\\news.txt");
int data=0;
char[] chars=new char[5];
while ((data=fileReader.read(chars))!=-1)
{
System.out.print(new String(chars,0,data));
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new FileReader_().reader02();
}
}
FileWriter
import java.io.FileWriter;
import java.io.IOException;
public class FileWriter_ {
public void Writer01(){
String filePath="d:\\news.txt";
FileWriter fw=null;
try {
fw=new FileWriter(filePath);
/**
*
* void write(char[] cbuf, int off, int len)
* 写入字符数组的某一部分。
* void write(int c)
* 写入单个字符。
* void write(String str, int off, int len)
* 写入字符串的某一部分。
*/
String str="你好这是void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。\n";
fw.write(str.toCharArray(),0,20);
fw.write(77);
fw.write('o');
fw.write("\n这是FileWriter......");
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
/**
* 对于FileWriter,一定要关闭或者刷新流,才能将数据写入文件!!!!
* flush()与close()在底层实现上都掉用了writeBytes() 才将数据写入文件
*/
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new FileWriter_().Writer01();
}
}
4.节点流与处理流
基本介绍
1.节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter(对文件读写)等。还有一些是对管道、数组等进行读写操作。
2.处理流也叫包装流是连接已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter。
两者的区别与联系:
- 节点流是底层流/低级流,直接和数据源相连
- 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法完成输入输出。
- 处理流(也叫包装流)是对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
字符处理流:
BufferedReader与BufferedWriter属于字符流,按照字符来读取数据。所以不适合读取二进制文件。
BufferedReader
package Reader_;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReader_ {
public static void main(String[] args) {
String filePath="d:\\news.txt";
BufferedReader bufferedReader=null;
try {
bufferedReader = new BufferedReader(new FileReader(filePath));
String line;//按行读取
/**
* bufferedReader.readLine 按行读取
* 当返回为null时读取完毕
*/
;
while (( line=bufferedReader.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
/**
*这里不需要关闭 FileReader
*只需要关闭外层包装流,它会调用你穿进来的流的close()方法
*/
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedWriter
package Writer_;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String filePath="d:\\news1.txt";
/**
* new FileWriter(filePath,true) 是以追加的方式进行写入
* new FileWriter(filePath)是以覆盖的方式写入
*/
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
bufferedWriter.write("祝大家百万年薪!!!");
bufferedWriter.newLine();//插入一个和系统相关的换行
bufferedWriter.write("我们都是打工人!!!");
/**
* 这里关闭外层包装流即可,它会在底层调用 你传进来的流的close方法
*/
// bufferedWriter.flush();
bufferedWriter.close();
}
}
使用BufferedWriter与BufferedReader拷贝文件
package Copy;
import java.io.*;
public class BufferedCopy_ {
public static void main(String[] args) throws IOException {
String srcPath="d:\\news.txt";
String destPath="d:\\news2.txt";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(destPath));
BufferedReader bufferedReader=new BufferedReader(new FileReader(srcPath));
String line=null;
while ((line=bufferedReader.readLine())!=null){
//每读入一行就写入 同时加上换行
bufferedWriter.write(line);
bufferedWriter.newLine();
}
//关闭流
if (bufferedReader!=null){
bufferedReader.close();
}
if (bufferedWriter!=null){
bufferedWriter.close();
}
}
}
字节处理流:
BufferedInputStream与BufferedOutStream属于字节流,可以用来进行二进制文件(音频、视频、pdf、word…)的读写。
使用BufferedInputStream与BufferedOutStream拷贝文件
package Copy;
import java.io.*;
public class BufferedCopy_02 {
public static void main(String[] args) {
String srcPath="d:\\news.txt";
String destPath="d:\\news3.txt";
BufferedOutputStream bos=null;
BufferedInputStream bis=null;
try {
bos= new BufferedOutputStream(new FileOutputStream(destPath));
bis =new BufferedInputStream(new FileInputStream(srcPath));
byte[] bytes=new byte[1024];
int readline=0;
while( (readline=bis.read(bytes))!=-1){
bos.write(bytes,0,readline);
}
System.out.println("拷贝完成!!");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
/**
* 同理也是关闭外层流
*/
if (bis!=null){
bis.close();
}if (bos!=null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.对象流
对象流:ObjectInputStream和ObjectOutputStream
对一个对象的数据及类型进行保存
这就牵扯到 序列化与反序列化
序列化 :就是在保存数据时,保存数据的值与数据类型。
反序列化:就是在恢复数据时,恢复数据的值与数据类型。
要让对象支持序列化机制,必须让其类是可序列化的,这个类必须实现以下两个接口之一:
- Serializable(推荐使用):这是一个标记接口,标记该类可以系列化,它里面没有任何方法。
- Externalizable:它继承了Serializable接口,有一些扩充方法,需要实现这些方法。
ObjectInputStream提供了对基本类型及对象类型的反序列化功能
ObjectOutputStream提供了对基本类型及对象类型的序列化功能
package Object_;
import java.io.Serializable;
public class Dog implements Serializable {
private String name;
private int age;
//serialVersionUID 序列化的版本号,可以提供兼容性
private static final long serialVersionUID=1l;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public Dog() {
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
对 对象的写入(序列化形式)
package Object_;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStream_ {
public static void main(String[] args) {
String ObjPath="d:\\dog.dat";
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(new FileOutputStream(ObjPath));
oos.writeObject(new Dog("小花",5));
System.out.println("写入完成(序列化形式)...");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
对对象的读取
package Object_;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStream_ {
public static void main(String[] args) {
String ObjPath="d:\\dog.dat";
ObjectInputStream ois=null;
try {
ois = new ObjectInputStream(new FileInputStream(ObjPath));
Dog o = (Dog)ois.readObject();
System.out.println(o);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
finally {
if (ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意事项
读写顺序要一致。
要求实现序列化或反序列化对象,需要实现Serializable接口。
序列化的类中建议添加SerialVersionUID,提高版本兼容性。
序列化对象时,默认将里面的所有属性都进行序列化,但除了static或transient修饰的成员。
(1)为什么要用transient关键字?
在持久化对象时,对于一些特殊的数据成员(如用户的密码,银行卡号等),我们不想用序列化机制来保存它。为了在一个特定对象的一个成员变量上关闭序列化,可以在这个成员变量前加上关键字transient。(2)transient的作用
transient是Java语言的关键字,用来表示一个成员变量不是该对象序列化的一部分。当一个对象被序列化的时候,transient型变量的值不包括在序列化的结果中。而非transient型的变量是被包括进去的。序列化对象时,要求里面属性的类型也需要实现序列化接口。
序列化具有可继承性,某类实现了序列化,则它的所有子类默认实现了序列化。
6.标准输入输出流
类型 | 默认设备 | |
---|---|---|
System.in 标准输入 | InputStream | 键盘 |
System.out 标准输出 | PrintStream | 显示器 |
package Standard;
import java.io.InputStream;
import java.util.Scanner;
public class InputAndOutput {
public static void main(String[] args) {
/**
* public final static InputStream in = null;
* 1.编译类型是InputStream
* 2.运行类型是BufferedInputStream
* 表示的是标准输入 ->键盘
*/
System.out.println(System.in.getClass());
Scanner scanner = new Scanner(System.in);//例如
/**
*public final static PrintStream out = null;
* 1.编译类型是PrintStream
* 2.输出类型是PrintStream
* 表示的是默认输出 ->显示器
*
*/
System.out.println(System.out.getClass());
System.out.println("你真帅!");//例如
}
}
7.转换流
转换流:InputStreamReader与OutputStreamWriter
作用是将字节流转换为字符流(解决字符乱码问题)
介绍:
- InputStreamReader是Reader的子类,可以将InputStreamReader(字节流)包装(转换)成Reader(字符流)
- OutputStreamWriter是Writer的子类,可以将OutputStreamWriter(字节流)包装(转换)成Writer(字符流)
- 当处理纯文本数据时,使用字符流效率更高,并且可以有效解决中文乱码问题,所以建议将字节流转化为字符流
- 可以在使用时制定编码格式(例如:utf-8,gbk,gb2312等)
未更改文件的字符编码时
[
更改后:出现乱码
演示InputStreamReader_解决中文乱码问题
package transformation;
import java.io.*;
/**
* 演示InputStreamReader_解决中文乱码问题
*/
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath="d:\\news1.txt";
/**
* 1.将FileInputStream转化为InputStreamReader
* 2.指定编码格式
*/
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//3.将InputStreamReader传入BufferedReader
BufferedReader bufferedReader=new BufferedReader(isr);
/**
* 将2.3步结合到一起
* BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"gbk"));
*/
//4.读取
String s=bufferedReader.readLine();
System.out.println(s);
//5.关闭外层流
bufferedReader.close();
}
}
演示使用OutputStreamWriter 使用指定编码写入文件
package transformation;
import java.io.*;
/**
* 演示使用OutputStreamWriter 使用指定编码写入文件
*/
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
String filePath="d:\\news.txt";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
osw.write("转换流写入");
osw.close();
System.out.println("写入成功");
}
}
8.打印流
PrintStream(字节流)与PrintWriter(字符流)
打印流只有输出流没有输出流
字节打印流PrintStream
package Writer_;
import java.io.IOException;
import java.io.PrintStream;
/**
* 演示PrintWriter(字节打印流)
*/
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream ps=System.out;
//在默认情况下 PrintStream的输出位置是 标准输出,即显示器 控制台
ps.print("这是标准输出!!!");
//因为print的底层是write,所以可以直接调用write
ps.write("这是标准输出!!!".getBytes());
ps.close();
//修改打印位置
System.setOut(new PrintStream("d:\\news.txt"));
System.out.println("你好!PrintStream");
}
}
字符打印流PrintWriter
package Writer_;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
//修改打印位置
PrintWriter pw=new PrintWriter(new FileWriter("d:\\news.txt"));
// PrintWriter pw=new PrintWriter(System.out);
pw.write("你好 java~~~~");
pw.close();
}
}
9.使用IO读取properties配置文件
例如读取连接数据库的配置文件
package properties_;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
*这是使用传统方式进行读取
*比较繁琐
*若只需要某一个值,例如ip 还需要进一步判断
*/
public class properties01 {
public static void main(String[] args) throws IOException {
//读取myql.properties并得到 ip user pw
BufferedReader br = new BufferedReader(new FileReader("src\\properties_\\mysql.properties"));
String line=null;
while ((line=br.readLine())!=null){
String[] spLit=line.split("=");
System.out.println(spLit[0]+"的值是"+spLit[1]);
}
br.close();
}
}
使用properties类读取配置文件
介绍:
专门读取配置文件的集合类
配置文件的格式:
键=值
键=值
注意:键值对不需要有空格,值不需要用引号。默认类型是String
String |
getProperty(String key) | 用指定的键在此属性列表中搜索属性。 |
---|---|---|
String |
getProperty(String key ,String defaultValue) | 用指定的键在属性列表中搜索属性。 |
void |
list(PrintStream out) | 将属性列表输出到指定的输出流 |
void |
list(PrintWriter out) | 将属性列表输出到指定的输出流。 |
void |
load(InputStream inStream) | 从输入流中读取属性列表(键和元素对)。 |
void |
load(Reader reader) | 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 |
Object |
setProperty(String key, String value) | 调用 Hashtable 的方法 put 。 |
void |
store(OutputStream out, String comments) | 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。 |
void |
store(Writer writer, String comments) | 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。 |
package properties_;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Parameter;
import java.util.Properties;
public class properties02 {
public static void main(String[] args) throws IOException {
/**
* 使用Properties读取mysql.properties文件
*/
//1.创建Properties对象
Properties properties=new Properties();
//2.加载指定配置文件
properties.load(new FileReader("src\\properties_\\mysql.properties"));
//3.把k-v现实至打印台
properties.list(System.out);
//4.根据k获取v
System.out.println("账号是"+properties.getProperty("user"));
System.out.println("密码是"+properties.getProperty("pwd"));
}
}
使用Properties创建修改配置文件
package properties_;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class properties03 {
public static void main(String[] args) throws IOException {
//Properties创建配置文件,修改配置文件内容
//Properties的父类就是HashTable,底层就是HashTable核心方法
Properties properties=new Properties();
//创建
//如果该文件没有key则为创建 有key则为修改
properties.setProperty("user","汤姆");//注意使用FileOutputStream保存时,保存的是中文的unicode码值
properties.setProperty("pwd","123");
properties.setProperty("charset","utf8");
//将k-v存储到文件中即可 中文注释在存储时也会保存的是unicode码
properties.store(new FileOutputStream("src\\properties_\\mysql.properties"),"这个是注释");
System.out.println("保存配置成功~");
}
}
10.使用io进行文件夹的拷贝
package Copy;
import java.io.*;
public class copyDirectoryDemo {
public static void main(String[] args) {
File srcFolder = new File("C:\\Users\\xuanlong\\Desktop\\日语");
File destFolder = new File("C:\\Users\\xuanlong\\Desktop\\日语Copy");
fun(srcFolder, destFolder);
}
public static void fun(File srcFolder, File destFolder) {
File[] fileArray = srcFolder.listFiles();
if (!destFolder.exists()) {
destFolder.mkdir();
}
for (File file : fileArray) {
if (file.isDirectory()) {
String folderName = file.getName();
File newDestFolder = new File(destFolder, folderName);
fun(file, newDestFolder);
} else {
String fileName = file.getName();
File destFile = new File(destFolder, fileName);
copy(file, destFile);
}
}
}
public static void copy(File file, File destFile) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(file));
bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}