[Java]IO流基础
IO流基础
1.基础概念
流:数据在文件和程序之间传输所经历的路径
输入流、输出流:输入输出是以程序为基准的,文件传输数据进程序为输入流,程序保存数据到文件为输出流。
2.文件
2.1 文件的创建
2.1.1 第一种
public File(String pathname) 根据路径构建一个File对象
public void createFileOne(){
File pathname = new File("d:/桌面/News01.txt");
try {
pathname.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
2.1.2 第二种
public File(File parent, String child) 父目录文件+子路径
@Test
public void createFileTwo(){
File parent = new File("d:/桌面/");
String child = "News02.txt";
File file = new File(parent,child);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
2.1.3 第三种
public File(String parent, String child) 父路径+子路径
@Test
public void createFileThree(){
File file = new File("d:/桌面/","News03.txt");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
2.2 获取文件相关信息
public String getName()//获取文件名称
public String getAbsolutePath()//获取文件绝对路径
public String getParent()//父级目录
public long length() //文件大小 以字节计
public boolean exists() //是否存在该文件
public boolean isFile()
public boolean isDirectory()//是否是目录
2.3 目录操作及删除文件
public boolean mkdir()//创建一级目录
public boolean mkdirs()//创建多级目录
delete()//删除空目录或文件
3.流
① IO流(Input/Output),用于处理数据传输,比如文件读取/传输,网络通讯。
② Java中对数据的输入/输出是以流(stream)的方式进行
③ Java.io 包下提供了各种流类和接口,用于存储传输不同的数据
3.1 流的分类
按数据单位类型分为:字节流(8 bit)、字符流
按数据流的方向分为:输入流、输出流
按流的角色不同分为:节点流,包装流
| 字节流 | 字符流 | |
|---|---|---|
| 输入流 | InputStream | Reader |
| 输出流 | OutputSream | Writer |
Java IO流类基本由以上述四个抽象类派生
3.2 FileInputStream 文件字节输入流
3.2.1 第一种读取文件的方式 public int read()
read()返回int类型,当无字节可扫描时返回-1
fileData用于存储read()方法返回的int值,通常为字符的ASCII码
String path = new String("d:\\桌面\\helloWorld.txt");//定义一个字符串记录文件路径
FileInputStream fin = new FileInputStream(path);//创建输入流对象
int fileData;
while((fileData = fin.read()) != -1){//调用read方法
System.out.print((char) fileData);
}
fin.close();//关闭流
3.2.2 第二种public int read(byte b[])
* 字符输入流一次读取一个字节(8 bit),
* 但每个汉字三个字节,如果每次只读取一个字节就打印输出的话就会造成乱码
* 它一次会扫描b.length个字节
* 区别于上面的fileData,当以字节数组扫描文件时,返回的是此次扫描到的字节个数。
public void method2() {
String filePath = new String("d:\\桌面\\helloWorld.txt");
byte[] b = new byte[3]; //创建byte[]数组
int fileData;
FileInputStream fin = null;
try {
fin = new FileInputStream(filePath);
while((fileData = fin.read(b)) != -1){ //fileData存储
System.out.print(new String(b,0,fileData));
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 FileOutputStream 文件字节输出流
①public void write(int b)
输入单个字符。
②public void write(byte b[])
输入字符数组,可以使用String类的getBytes()方法使字符串转换为字节数组
③public void write(byte b[], int off, int len)
off为字节数组开始下标,len为指定字节数组长度
@Test
public void method1(){
String filePath = "d:/桌面/helloWorld.txt";
FileOutputStream fout = null;
String str = "i love China";
try {
fout = new FileOutputStream(filePath);
//public void write(int b)
// fout.write('W');
//public void write(byte b[])
// fout.write(str.getBytes());
//public void write(byte b[], int off, int len) off为字节数组开始下标,len为指定字节数组长度
fout.write(str.getBytes(),2,5);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4 文件复制
文件复制实现过程为
- 使用文件字符输入流读取文件数据
- 使用文件字符输出流将读取的文件数据输出到新建文件中
@Test
public void methodCopy(){
String filePath = "d:/桌面/boom_1.png"; //被复制的文件路径及文件名
String toFilePath = "d:/桌面/爆炸.png"; //复制后的文件存储路径及文件名
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
byte[] buf = new byte[1024];
//因为读取文件时使用文件字节输入流中的第二种read方法,因此定义一个变量用于存储此次读到的数据个数
//用第二种方法是为了提高效率
int fileDataNum;
try {
fileInputStream = new FileInputStream(filePath);
//fileOutputStream = new FileOutputStream(toFilePath,true)
// 当输出流增加第二个参数boolean append,为true时,输出流对文件的操作为->在文件原本数据后继续追加新数据,而不是新数据覆盖旧数据
fileOutputStream = new FileOutputStream(toFilePath);
while((fileDataNum = fileInputStream.read(buf)) != -1){
//因为使用了第二种read()方法
//再输出数据时应该使用第三种wirte()方法,读多少输出多少,避免了读取不存在的数据
fileOutputStream.write(buf,0,fileDataNum);
}
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();
}
}
}
3.5 FileReader 文件字符输入流
//构造器
public FileReader(String fileName)
public FileReader(File file)
具体操作与FileInPutStream相似。
//创建FileReader对象调用read()方法
public int read()
public int read(char cbuf[])
第二种read()方法需要创建一个字符数组。
第一种read方法返回的int值为字符的ASCII值,第二种read方法返回的int值为此次读取的字符个数。
3.6 FileWriter 文件字符输出流
String str = "太空计划";
char[] c = {'A','B','C'};
//.....
//public void write(int c) 单个字符
fileWriter.write('A');
//public void write(String str) 字符串
fileWriter.write(str);
//public void write(String str, int off, int len) 字符串子串
fileWriter.write(str,0,3);
//public void write(char cbuf[]) 字符数组
fileWriter.write(c);
//public void write(char cbuf[], int off, int len) 字符数组子数组
fileWriter.write(c,0,2);
区别于字节输出流,FileWirter在使用完后必须调用flush()、close()方法,否则数据不能写入指定文件。
3.7 包装流
节点流:针对某个特定的数据源(文件、数组等)进行操作的流,比如上面提到的FileInPutStream、FileWirter等
包装流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
包装流使用了一种设计模式->模拟修饰器模式,对基础的节点流功能进行了扩展。
包装类加入了缓冲区,并对节点流功能进行了扩展,提高了读写的效率。
3.7.1 BufferedReader
//它有两种构造器,都需要提供一个Reader子类的实例
public BufferedReader(Reader in)
public BufferedReader(Reader in, int sz)
BufferedReader中正是通过这个Reader in 属性,来封装一个节点流,从而使用节点流,并对节点流的功能进行扩展。
String filePath = "d:/桌面/helloWorld.txt";
String line;
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
while ((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
例如上述中的readLine()方法正是对bufferedreader底层调用的FileReader的功能扩展,在3.5中,FileReader的实例在读取文件数据时有两种方法,一次扫描一个字符或是一次扫描一个字符数组的长度,但BufferedReader类的readLine()方法扩展了原本的read()方法功能,readLine()方法能够一次扫描一行的字符。
在关闭包装流时,只需包装流实例调用close()。
3.7.2 BufferedWirter
与BufferedReader用法别无二样。newLine()正是对FileWirter功能的扩展,该方法的为换行。
bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
bufferedWriter.write("China No.1");
bufferedWriter.newLine();
bufferedWriter.write("China No.1");
bufferedWriter.close();
3.7.3 字节流 BufferedInputStream/BufferedOutputStream
字节流处理二进制文件。
构造器传入InPutStream/OutPutStream子类实例。
若使用字符流处理二进制文件(如视频)会导致文件损坏。
4. 对象流 ObjectInputStream/ ObjectOutputStream
4.1 序列化与反序列化
从上述IO操作来看,我们要将一个数字100保存到文件中是很简单的,但是这里存在一个问题-->我们不能确定被保存至文件中的“数字”100的类型,它可以是int型,它也可以是String类型,那如何保存数据的类型和值呢?
序列化就是指在保存数据时,同时保存数据的类型和值。
反序列化就是指在读取数据时,获得数据的类型和值。
假如我们要保存的数据是某个类的实例(比如Dog类,属性age,name),我们是否要对该类做些什么才能实现序列化?
要实现类的序列化需要类继承一下两个接口其中之一:
-> Serializable (推荐版)
-> Externalizable
4.2 使用
创建ObjectOutputStream时依然需要传入一个OutputStream子类实例。
ObjectOutputStream在写入数据时,传入的实例类必须实现4.1中的两个接口之一,否则会抛出异常NotSerializableException
同时在存入数据后,在文件中保存也并非以纯文本形式保存,而是以一种特有格式完成的序列化保存
反序列化读取数据时,应与序列化存入数据的数据保持一致。例如
//序列化顺序,oos为 ObjectOutPutStream实例化对象
oos.writeInt(100);
oos.writeObject(dog);
//反序列化顺序,ois为 ObjectInPutStream实例化对象
System.out.println(ois.readInt());
System.out.println(ois.readObject());
若顺序不一致,会抛出OptionalDataException
在序列化时,会将存入的实例数据的类(包含包名)的数据也存入文件中。
在反序列化时(ObjectInPutStream),就需要能够访问到存储时的类(同个包下),否则会类型转换异常。
5. (转换流)InputStreamReader / OutputStreamWirter
InPutStreamReader可以解决中文乱码问题。
6. 打印流PrintStream / PrintWirter
6.1 PrintStream 字节
public class TestPrintStream {
public static void main(String[] args) throws IOException {
//实例化打印流对象
PrintStream ps = System.out;
ps.print("hello world~");//这里print()方法其实调用了write()方法
ps.write("你好".getBytes());
//我们还可以修改打印流打印的位置(默认显示屏)
//需要调用setOut()方法
//这里我们修改打印的位置为桌面的文本文件,
System.setOut(new PrintStream("d:/桌面/helloWorld.txt"));
System.out.print("你好");
}
}
6.2 PrintWirter
用法与PrintStream类似,使用后必须使用close()方法关闭流,否则不能将数据写入文件。
7. 配置文件 Properties类
Properties为HashTable子类,是一个专门用于读写配置文件的集合类。
配置文件的格式为:
键 = 值
7.1 常用方法
load() //加载配置文件的键值到Properties对象
list() //将数据显示到指定设备
getProperty(key)// 根据键获取值
setProperty(key,value) // 设置键值到Properties对象
store()//将Properties中的键值对存储至配置文件中,在IDEA中,如果含有中文,会存储为unicode码
7.2 使用示例
//1.创建Properties对象
Properties p = new Properties();
//2.创建键值对保存至内存中
p.setProperty("name", "wsl");
p.setProperty("sNum", "03187023");
p.setProperty("age", "21");
//3.保存至文件中
//public void store (Writer writer, String comments)第二个参数为对配置文件的注释
p.store(new FileWriter("src/mysql.properties"),null);
//4.读取配置文件中的信息
System.out.println("学生姓名为:" + p.getProperty("name"));//->wsl
//5.修改配置文件中信息
p.setProperty("name", "汤姆");
System.out.println("学生姓名为:" + p.getProperty("name"));//->汤姆

浙公网安备 33010602011771号