一些基本概念:
内存:临时存储
硬盘:永久存储
流数据:(字符,字节) 1个字符 = 2个字节 = 8个比特位
输入:就是把硬盘中的数据,读取到内存中使用
输出:把内存中的数据,写入到硬盘中保存.
字节流
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
字节输出流:(OutputStream)
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地,它定义了字节输出流的基本共性功能方法.
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b)
:将指定的字节输出流。(最后显示的是字符)
close的方法,在完成流的操作时候,必须调用这个方法,释放系统的资源.
FileOutputStream 类 (extends OutputStream)
作用:把内存中的数据写入到硬盘中的文件中
构造方法:
FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流
FileOutputStream(File file):创建一个向具有指定file对象的文件中写入数据的输出文件流
参数:
String name:目的地是一个文件的路径
File file:目的地是一个文件
构造方法的作用:
-
创建一个FileOutputStream对象
-
会根据构造方法中传递的文件/文件路径,创建一个空的文件
-
会把FileOutputStream对象指向创建好的文件.
可以帮我们创建文件,但是给出的文件夹路径必须对,否则可能就报空指针异常
写入数据的原理:(内存->硬盘)
java程序->jvm->os->调用写数据的方法->把数据写入到文件中
使用步骤:
-
创建一个fileoutputStream对象,构造方法中传递写入数据的目的地
-
调用fileoutputStream中的方法write,把数据写到文件中
-
释放资源(流的使用会占用一定的内存,使用完毕要把内存清空,提高程序的运行效率)
写数据的时候,会把十进制的整数,转换成为二进制的整数,写入到硬盘中
-
任意的文本编辑器,在打开文件的时候,都会查询编码表,把字节转换为字符表示
-
0-127:查询ACCII表
-
其他值:查询系统默认码表
存入数字的时候,可以使用字符串。
一次写多个内容:
public void write(byte[] b)
:当一次写多个字节的时候:
如果写的第一个字节是正数(0-127):那么显示的时候会查询ASCII表
如果写的第一个字节是负数:那么第一个字节会和第二个字节组成一个中文显示,查询默认码表GBK(即会变成汉字)
GBK:两个字节是一个中文
UTF-8:三个字节是一个中文
最终存入计算机的还是unicode表对应的二进制的数字(01字符串)
文件的追加写入:构造方法的第二个参数是true,就可以追加,默认是false,不追加
写入一个空格或者换行:
windows:\r\n
linux:/n
mac:/r,从macosX开始与linux统一
字节输入流:(InputStream)
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read()
: 从输入流读取数据的下一个字节。public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
close方法,当完成流的操作时,必须调用此方法,释放系统资源。(在字节流中不关闭也能写进去,但是在字符流中不关闭就写不进去)
FileInputStream 类 (extends InputStream)
文件字节输入流
作用:把硬盘文件中的数据,读取到内存中使用
构造方法以及作用与输出流类似.
读取数据的原理:(硬盘->内存)
java程序->jvm->os->os读取数据的方法->读取文件
字节输入流的使用步骤
-
创建FileInputStream对象,构造方法中绑定要读取的数据源
-
使用FileInputStream对象中的方法read,读取文件
-
读取完毕,释放资源
int read()
读取文件中的一个字节并返回,读取到文件的末尾返回-1(就算多读取还是-1)
使用int len来记录读写的写法
int len;
while((len = fis.read()) != -1){
System.out.println((char)len);
}
int read(byte[]b)
:一次读取多个字节,返回值就是读取的有效字节个数
方法中参数的byte[] b作用:
-
起到缓冲作用,存储每次读取到的多个字节
-
数组的长度一般定义为1024(1kb)或者1024的整数倍
int len;
byte[] bytes = new byte[1024];
while((len = fis.read) != -1){
System.out.println(new String(bytes));
}
//这时候会有bug,会出现上次读取的数据没有被完全替换的情况,就会打印出不想要的数据
//所以使用String的构造方法来优化(获取有效的字节)
sout(new String(bytes,0,len));
练习:图片的复制
题目:把D:\a.jpg --(复制到)-->E:\b.jpg
public class Copy {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("E:\\b.jpg");
FileInputStream fis = new FileInputStream("D:\\a.jpg");
int len;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes)) != -1){
//如果是读的操作,应该写出来为
//sout(new String(bytes,0,len));
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}
}
在文件的复制的过程中关闭资源的时候,先关闭写的,再关闭读的.
字符流(只能读文本类,不能读电影等等)
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
字符输入流:(Reader)
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。public void close()
:关闭此流并释放与此流相关联的任何系统资源。public int read()
: 从输入流读取一个字符。public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。
FileReader类(extends InputStreamReader extends Reader)
构造方法:
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的名称。当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。
字符流的使用步骤与字节流一样.但是缓冲区变成了字符数组(字节流使用的是字节数组)
字符输出流:(Writer)
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。void write(int c)
写入单个字符。void write(char[] cbuf)
写入字符数组。abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分,off数组的开始索引,len写的字符个数。void write(String str)
写入字符串。void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索 引,len写的字符个数。void flush()
刷新该流的缓冲。void close()
关闭此流,但要先刷新它。
FileWriter类(extends OutputStreamWriter extends Writer)
作用:把内存中的字符数据写入到文件
构造方法以及作用:类比OutputStream
使用步骤:
-
创建FileWriter对象
-
使用FileWriter中的方法写入到内存缓冲去(字符转换为字节的过程)
-
使用FileWriter中的方法flush,把内存缓冲区中的数据刷新到文件中
-
释放资源
如果没有调用flush或者close方法,就不会写到文件中.而close方法之后流已经关闭了,已经从内存中消失了,不能再用了.
写出其他数据
写出字符数组 :write(char[] cbuf)
和 write(char[] cbuf, int off, int len)
,每次可以写出字符数组中的数据,用法类似FileOutputStream,
写出字符串: write(String str)
和 write(String str, int off, int len)
,每次可以写出字符串中的数据,更为方便.
续写和换行:操作类似于FileOutputStream。
(就是在构造的时候参数多一个true)(write("\r\n"))
IO异常处理
在jdk1.7之前使用:try catch finally
格式:
try{
可能产生异常的代码
}catch(异常类型 变量){
异常处理逻辑
}finally{
一定执行的代码
资源释放
}
public class FileWriterDemo01 {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("a.txt");
fw.write(97);
} catch (Exception e) {
System.out.println(e);
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
几个注意的点:
1.在对创建对象时候的异常处理的时候,要把对象声明到try...catch之外,并初始化
2.在finally代码块中对资源释放的时候,要注意对空指针的判断
在jdk7的新特性
在try的后面可以增加一个(),在()可以定义流对象,那么这个流对象的作用域在try中有效,try中的代码执行完毕,会自动把流对象释放,不用再写finally,如果有多个对象,要使用;隔开
try(定义流对象){
可能产生异常的代码
}catch(异常类型 变量){
异常处理逻辑
}
public class FileWriterDemo01 {
public static void main(String[] args) {
try( FileWriter fw = new FileWriter("a.txt")) {
fw.write(97);
} catch (Exception e) {
System.out.println(e);
}
}
}
在try后面的括号中参数使用;隔开
在jdk9的新特性
try前面可以定义对象,在try后面的()中可以直接引入流对象的名称(变量名),在try代码执行完毕之后,流对象也可以释放掉,不用写finally
格式:
int a = 1;
int b = 2;
try(a;b){
可能产生异常的代码
}catch(异常类型 变量){
异常处理逻辑
}
比较麻烦..尽量还是使用第一种,或者第二种
属性集
概述
java.util.Properties
继承于 Hashtable
,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时, System.getProperties()
方法就是返回一个 Properties 对象。
Properties类
构造方法:
public Properties()
:创建一个空的属性列表
基本的存储方法
public Object setProperty(String key, String value)
: 保存一对属性。public String getProperty(String key)
:使用此属性列表中指定的键搜索属性值。public Set<String> stringPropertyNames()
:所有键的名称的集合。
package com.qin.study;
import java.util.Properties;
import java.util.Set;
/**
* Created by SunYuqin in 2018/8/14
* Description:
**/
public class PropertiesDemo01 {
public static void main(String[] args) {
//创建属性集对象
Properties pro = new Properties();
//添加键值对元素
pro.setProperty("filename","a.txt");
pro.setProperty("length","200");
pro.setProperty("location","D:\\a.txt");
//打印对象集对象
System.out.println(pro);
//通过键,获取属性值
System.out.println(pro.getProperty("filename"));
System.out.println(pro.getProperty("length"));
System.out.println(pro.getProperty("location"));
// 遍历属性集,获取所有键的集合
Set<String> strings = pro.stringPropertyNames();
for (String string : strings) {
System.out.println(string+"--"+pro.getProperty(string));
}
}
}
结果
{length=200, filname=a.txt, location=D:\a.txt}
a.txt
200
D:\a.txt
length--200
filname--a.txt
location--D:\a.txt
与流相关的方法
public void load(InputStream inStream)
: 从字节输入流中读取键值对。
public class ProDemo2 {
public static void main(String[] args) throws FileNotFoundException {
// 创建属性集对象
Properties pro = new Properties();
// 加载文本中信息到属性集
pro.load(new FileInputStream("read.txt"));
// 遍历集合并打印
Set<String> strings = pro.stringPropertyNames();
for (String key : strings ) {
System.out.println(key+" ‐‐ "+pro.getProperty(key));
}
}
}
输出结果:
filename ‐‐ a.txt
length ‐‐ 209385038
location ‐‐ D:\a.txt
ps:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。