Java IO流
File类
文件和路径名的抽象表示
- 文件和目录是通过
File封装成对象的。 - 对于
File而言,其封装的并不是一个整整存在的文件,仅仅是一个路径名而已。它可以是真实存在,也可以是不存在的。将来要通过具体的操作把这个路径的内容转换为具体存在的。
常用的文件操作
文件的创建
new File(String pathname) 根据路径构建一个File对象
new File(File parent, String child) 根据父目录文件 + 子路径构建
new File(String parent,String child) 根据父目录 + 子路径构建
boolean createNewFile() 创建一个新文件
如果文件不存在,则创建一个新文件,返回true
如果文件存在,则返回false
其他常见操作
public boolean mkdir(); 目录的创建
public boolean mkdirs(); 创建File构造的所有目录及文件
File类判断和获取功能
public boolean isDirectory(); File是否为目录
public boolean isFile(); File是否为文件
public boolean exists(); File是否存在
public String getAbsolutePath(); 获取绝对路径
public String getPath(); 获取相对路径
public String getName(); 获取文件/目录名称
public String[] list(); 获取File下的文件及目录
public File[] listFiles(); 获取File下所有文件及目录
File类删除功能
public boolean delete(); 删除由此抽象路径名表示的文件或目录
代码演示
public static void main(String[] args) throws IOException {
// 创建文件方式1
File file = new File("./temp/temp.txt");
// 创建文件方式2
File file1 = new File(new File("./temp"),"temp2.txt");
// 创建文件方式3
File file2 = new File("./temp","temp3.txt");
// 在磁盘创建文件
file.createNewFile();
file1.createNewFile();
file2.createNewFile();
}
文件的创建过程

打印File文件下的所有文件
public static void main(String[] args) {
File file = new File("./");
getAllFiles(file);
}
/**
* 获取目录下所有文件
* @param file
*/
public static void getAllFiles(File file){
// 获取目录下所有文件或目录
File[] files = file.listFiles();
for (File f : files) {
if(f.isDirectory()){
// 递归调用
getAllFiles(f);
}else{
System.out.println(f.getName());
}
}
}
IO流
I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通信等。
Java程序中,对于数据的输入/输出操作以流(Stream)的方式进行。
java.io报下提供了各种流类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。
原理
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

分类
按照数据流向,可以将流分为输入流和输出流
输入流只能读取数据、不能写入数据,而输出流只能写入数据、不能读取数据
按照数据类型,可以将流分为字节流和字符流
字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符
按照处理功能,可以将流分为节点流和处理流
节点流可以直接从/向一个特定的IO设备(磁盘、网络等)读/写数据,也称为低级流,而处理流是对节点流的链接或封装,用于简化数据读/写功能或提高效率,也成为高级流。
如果数据通过windows自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,否则使用字节流,如果不知道该使用那种类型的流就使用字节流。
Java提供了大量的类来支持IO操作,其中,黑色字体的是抽象基类,其他所有的类都继承自它们。红色字体的是节点流,蓝色字体的是处理流。

各个流的作用
- 以
File开头的文件流用于访问文件; - 以
ByteArray/CharArray开头的流用于访问内存中的数组; - 以
Piped开头的管道流用于访问管道,实现进程之间的通信; - 以
String开头的流用于访问内存中的字符串; - 以
Buffered开头的缓冲流,用于在读写数据时对数据进行缓存,以减少IO次数; InputStreamReader、InputStreamWriter是转换流,用于将字节流转换为字符流;- 以
Object开头的流是对象流,用于实现对象的序列化; - 以
Print开头的流是打印流,用于简化打印操作; - 以
Pushback开头的流是推回输入流,用于将已读入的数据推回到缓冲区,从而实现再次读取; - 以
Data开头的流是特殊流,用于读写Java基本类型的数据。
字节流
InputStream:抽象类,表示字节输入流的所有类的超类
OutputStream:抽象类,表示字节输出流的所有类的超类
子类名特点:子类名称都是其父类名作为子类名的后缀
字节流写数据
构造方法
FileOutputStream(String name); 创建文件输出流以指定的名称写入文件
FileOutputStream(File file); 创建文件输出流以写入由特定的File对象的文件
void write (int b); 将指定的字节写入此文件输出流,一次写一个字节。
void write (byte[] b); 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组。
void write (byte[] b,int off,int len); 将len字节从指定的字节开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据。
代码演示
public static void main(String[] args) throws IOException {
// 1.创建字节流对象
/**
* 做了三件事情:
* A:调用系统功能创建了文件
* B:创建了字节输出流对象
* C:让字节输出流对象指向创建好的文件
*/
FileOutputStream fos = new FileOutputStream(new File("./temp.txt"));
// 2.写数据
// 2.1 void write (int b)
fos.write(97);
// 2.2 void write (byte[] b)
fos.write(new byte[]{98,99,100});
// 2.3 void write (byte[] b,int off,int len)
fos.write(new byte[]{101,102},0,1);
// 3.关闭流
fos.close();
}
结果
abcde
字节流写数据的两个小问题
- 字节流写数据如何换行?
windows:\r\n
linux:\n
mac:\r
- 字节流写数据如何实现追加写入?
public FileOutputStream(String name,boolean append );创建文件输出流以指定的名称写入文件。如果第二个为true,则字节流写入的是末尾不是开头。
代码演示
public static void main(String[] args) throws IOException {
// 1.创建输出流
FileOutputStream fos = new FileOutputStream("./temp.txt",true);
// 2.写数据
for (int i=0;i<3;i++){
fos.write("hello".getBytes());
// 换行符
fos.write("\r\n".getBytes());
}
// 3.关闭流
fos.close();
}
结果
abcdehello
hello
hello
由于在使用try-catch捕捉异常时,finally无论何时都会被执行,所以可以把关闭流close()的操作放到finally执行。
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("ByteStream\\fos.txt")
fos.write("world".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流读数据
int read(); 一次读取一个字节
int read(byte b[]); 一次读取一个数组
int read(byte b[], int off, int len) 根据偏移量读取数组
一次读取一个字节
public static void main(String[] args) {
// 1.创建字节流
FileInputStream fis = null;
try {
fis = new FileInputStream(new File("./temp.txt"));
// 2.读数据
int buffer;
while ((buffer = fis.read()) != -1) {
System.out.println((char) buffer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
控制台
a
b
c
d
e
....
一次读取一个数组
public static void main(String[] args) {
// 1.创建字节流
FileInputStream fis = null;
try {
fis = new FileInputStream(new File("./temp.txt"));
// 2.读数据
byte[] bytes = new byte[1024];
int buffer;
while ((buffer = fis.read(bytes)) != -1) {
System.out.println((new String(bytes,0,buffer)));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
控制台
abcdehello
hello
hello
字节缓冲流
BufferOutputStream:通过缓冲输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
BufferInputStream:通过BufferInputStream将创建一个内部缓冲去数组。当缓冲流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重写填充,一次很多字节。
构造方法
字节缓冲输出流:BufferedOutputStream(OutputStream out)
字节缓冲输入流:BufferedInputStream(InputStream in)
字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作。
字节流的方法对比
/**
* 1.基本字节流一次读一个字节
* @throws IOException
*/
public static void method1(File file) throws IOException {
//创建字节输入流对象
FileInputStream fis=new FileInputStream(file);
//创建字节输出流对象
FileOutputStream fos=new FileOutputStream(file);
int by;
while((by=fis.read())!=-1){
fos.write(by);
}
//释放内存
fis.close();
fos.close();
}
/**
* 2.基本字节流一次读一个字节数组
* @throws IOException
*/
public static void method2(File file)throws IOException{
FileInputStream fis=new FileInputStream(file);
FileOutputStream fos=new FileOutputStream(file);
byte[] bys=new byte[1024];
int len;
while ((len=fis.read())!=-1){
fos.write(bys,0,len);
}
fis.close();
fos.close();
}
/**
* 3.缓冲字节流一次读一个字节
* @throws IOException
*/
public static void method03(File file) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file));
int by;
while ((by=bis.read())!=-1){
bos.write(by);
}
bis.close();
bos.close();
}
/**
* 缓冲字节流一次读一个字节数组(推荐)
* @throws IOException
*/
public static void method04(File file) throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file));
byte[] bys=new byte[1024];
int len;
while ((len=bis.read())!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
字符流
字符流=字节流+编码表
汉字存储:
UTF-8:一个汉字三个字节
GBK:一个汉字两个字节
无论哪种编码存储,第一个字节都是负数
编码表
采用何种规则编码,就要采用对应规则解码,否则就会出现乱码
/*
编码:
byte[] getBytes(); 使用平台默认的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName); 使用指定的字符集将该String 编码为一系列字节,将结果存储到新的字节数组中
解码:
String (byte[] bytes); 通过使用平台的默认字符集解码指定的字节数组来构造新的String
String (byte[] bytes,String charsetName); 通过特定的字符集解码指定的字节数组来构造新的String
*/
public static void main(String[] args) throws UnsupportedEncodingException {
String s="中国";
//编码
byte[] bys = s.getBytes();
System.out.println(Arrays.toString(bys));
byte[] gbks = s.getBytes("GBK");
System.out.println(Arrays.toString(gbks));
//解码
String s1 = new String(bys);
System.out.println(s1);
String s2 = new String(gbks,"GBK");
System.out.println(s2);
}
字符流两个基类
Reader:抽象类,字符输入流
Writer:抽象类,字符输出流
构造方法
OutputStreamWriter(OutputStream out, String charsetName); 通过输出流和编码格式进行创建
OutputStreamWriter(OutputStream out); 通过输出流创建
常用写操作
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); 字符串的部分写入
void flush() 刷新操作。将字符写入字符流中,需要刷新流之后才能真正的写入。否则读取不到。
常用读操作
int read() 一次读取一个字符
int read(char[] cbuf) 一次读取一个字符数组
int read(char[] cbuf, int off, int len) 读取部分字符数组
代码演示
/**
* 一次写入一个字符
*
* @param osw
* @throws IOException
*/
public static void write(OutputStreamWriter osw) throws IOException {
osw.write(97);
}
/**
* 一次写入一个字符数组(或部分)
* @param osw
* @param chars
* @throws IOException
*/
public static void write(OutputStreamWriter osw, char[] chars) throws IOException {
// osw.write(chars);
osw.write(chars,0,chars.length-2);
}
/**
* 一次写入一个字符串(或部分)
* @param osw
* @param s
* @throws IOException
*/
public static void write(OutputStreamWriter osw,String s) throws IOException {
// osw.write(s);
osw.write(s,1,s.length()-1);
}
测试
public static void main(String[] args) throws IOException {
// 1.创建字节流
FileOutputStream fos = new FileOutputStream("./temp1.txt");
FileInputStream fis = new FileInputStream("./temp1.txt");
// 2.创建字符流
OutputStreamWriter osw = new OutputStreamWriter(fos);
InputStreamReader isr = new InputStreamReader(fis);
// 3.调用方法
write(osw,"hello");
// 4.刷新流
osw.flush();
// 5.读数据
/**
* int ch;
* while((ch = isr.read()!=-1)){
* System.out.println((char)ch)
*/
char[] chars = new char[1024];
int len;
while((len=isr.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
// 6.关闭流
osw.close();
fos.close();
}
控制台
ello
复制文件
FileReader(String fileName): 用于读取字符文件的便捷类
FileWriter(String fileName): 用于写入字符文件的便捷类
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("CharStream\\demo01.txt");
FileWriter fw = new FileWriter("CharStream\\demo01p.txt");
char[] chs = new char[1024];
int len;
while ((len=fr.read(chs))!=-1){
fw.write(chs,0,len);
}
fw.close();
fr.close();
}
字符缓冲流
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途。
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途。
构造方法
BufferedWriter(Writer out)通过输出字符流创建
BufferedReader(Reader in)通过输入字符流创建
特有功能
void newLine();写数据时换行
String readLine();读一行文字
结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null
代码演示
public static void main(String[] args) throws IOException {
// 1.创建字节流
FileOutputStream fos = new FileOutputStream("./temp1.txt");
FileInputStream fis = new FileInputStream("./temp1.txt");
// 2.创建字符流
OutputStreamWriter osw = new OutputStreamWriter(fos);
InputStreamReader isr = new InputStreamReader(fis);
// 3.创建缓冲流
BufferedWriter bw = new BufferedWriter(osw);
BufferedReader br= new BufferedReader(isr);
// 4.写数据
String[] str = {"java","python","c++"};
Arrays.stream(str).forEach(str1 -> {
try {
// 写数据
bw.write(str1);
// 换行
bw.newLine();
// 刷新
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
// 5.读数据
String line;
while((line=br.readLine())!=null){
System.out.println(line);
}
// 6.关闭流
bw.close();
br.close();
}
控制台
java
python
c++
特殊操作流
标准输入流
public static final InputStream in;标准输入流,通常对应键盘输入或由主机环境或用户指定的另一个输入源。
Java提供一个类实现键盘输入
Scanner sc=new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入一个字符串:");
String s = br.readLine();
System.out.println(s);
System.out.println("请输入一个整数:");
int i = Integer.parseInt(br.readLine());
System.out.println(i);
//Scanner类
Scanner sc = new Scanner(System.in);
}
标准输出流
public static final PrintStream out;标准输出流,通常对应于显示输出货主机环境或用户指定的另一个输出目标
输出语句的本质是一个标准的输出流
System.out
public static void main(String[] args) {
PrintStream ps = System.out;
//能够方便的打印各种数据值
ps.println("hello");
ps.println(100);
//sout 的本质是一个字节输出流
System.out.println("hello");
System.out.println(100);
}
字节打印流
- 只负责输出数据,不负责读取数据
- 有自己的特有方法
构造方法
PrintWriter(String fileName);使用指定的文件名创建一个新的PrintWriter而不需要手动进行刷新
PrintWriter(OutputStream out,boolean aotuFlush);创建一个新的PrintWriter
out:输出流
autoFlush: 每当写入字节数组、调用 println 方法之一或写入换行符或字节 (‘\n’) 时,是否将刷新输出缓冲区
使用继承父类的方法写数据,查看时会转码,使用自己特有的方法写数据,查看时原样输出
代码演示
public static void main(String[] args) throws FileNotFoundException {
// 1.创建流
PrintStream ps = new PrintStream("./ps.txt");
// 2.写数据
ps.write(97); // 转为字节
ps.println(98); // 普通数字
// 关闭流
ps.close();
}
写入的数据
a98
对象序列化流
将Java对象的原始数据类型和图形写入OutStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
构造方法
ObjectOutputStream(OutputStream out);输入
序列化对象方法
void writeObject(Object obj);指定对象写入
注意
- 一个对象要想被序列化,该对象所属的类必须实现
Serializable接口Serializable是一个标记接口,实现该接口,不需要重写任何方法。
异常处理
InvalidClassException 数据读取异常
给对象所属类加一个serialVersionUID
private static final long serialVersionUID=1L;
如果对象中的某个成员变量值不想被实例化
- 给成员变量加
transient关键字修饰
Properities
- Map体系集合类
- Properties可以保存到流中或从流中加载
Properties作为集合的特有方法
Object setProperty(String key,String value); 设置集合的键和值,底层是Hashtable的out方法
String getProperty(String key); 使用此属性列表中指定的键搜索属性
Set<String> stringPropertyNames(); 从该属性列表中返回一个不可修改的键类,其中键和对应的值是字符串
Properties和IO流相结合的方法
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
void loar(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store(OutputStream out,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer,String comments) 将此属性列表(键和元素对)写入此Properties表中使用load(Reader)方法的格式写入输出字符流
游戏次数
public static void main(String[] args) throws IOException {
//从文件中读取数据到Properties集合,用load方法实现
Properties prop = new Properties();
FileReader fr = new FileReader("OtherStream\\game.txt");
prop.load(fr);
fr.close();
//通过Properties集合获取到玩游戏的次数
String count = prop.getProperty("count");
int i = Integer.parseInt(count);
if(i>=3){
System.out.println("游戏试玩结束,请充值(www.baidu,com)");
}else {
Game.start();
i++;
prop.setProperty("count",String.valueOf(i));
FileWriter fw=new FileWriter("OtherStream\\game.txt");
prop.store(fw,null);
fw.close();
}
}
游戏类
private Game() {}
public static void start(){
//生成随机的值,
Random random = new Random();
int i = random.nextInt(100) + 1;
System.out.println(i);
while (true){
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要猜的值:");
int number = sc.nextInt();
if (number>i){
System.out.println("你猜的值太大了");
}else if (number<i){
System.out.println("你猜的值太小了");
}else {
System.out.println("恭喜你、猜中了");
break;
}
}
}

586

被折叠的 条评论
为什么被折叠?



