目录
一.File类
1.FIile的基本应用
File类的每一个实例可以表示硬盘(文件系统)中的一个文件或目录(实际上表示的是一个抽象路径) 使用File可以做到: - 1:访问其表示的文件或目录的属性信息,例如:名字,大小,修改时间等等 - 2:创建和删除文件或目录 - 3:访问一个目录中的子项 但是File不能访问文件数据.
File的常用方法:
- length():返回一个long值,表示占用的磁盘空间,单位为字节。 - canRead():File表示的文件或目录是否可读 - canWrite():File表示的文件或目录是否可写 - isHidden():File表示的文件或目录是否为隐藏的 - createNewFile():创建一个新文件,如果指定的文件所在的目录不存在会抛出异常java.io.FileNotFoundException - mkdir:创建一个目录 - mkdirs:创建一个目录,并且会将所有不存在的父目录一同创建出来,推荐使用。 - delete():删除当前文件或目录,如果目录不是空的则删除失败。 - exists():判断File表示的文件或目录是否真实存在。true:存在 false:不存在 - isFile():判断当前File表示的是否为一个文件。 - isDirectory():判断当前File表示的是否为一个目录 - listFiles():获取File表示的目录中的所有子项 - listFiles(FileFilter filter):获取File表示的目录中满足filter过滤器要求的所有子项
创建案例:
package file;
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) throws IOException {
File file=new File("./text.txt");
if (file.exists()){
System.out.println(file.getName());
System.out.println(file.length());
System.out.println(file.canWrite());
System.out.println(file.canRead());
}
else{
file.createNewFile();
System.out.println("创建完成!");
}
}
}
删除案列:
package file;
import java.io.File;
public class FileDeletDemo {
public static void main(String[] args) {
File file = new File("./D.txt");
if(file.exists()){
file.delete();
System.out.println("删除成功!");
}
}
}
2.Lambda表达式
JDK8之后,java支持了lambda表达式这个特性. - lambda可以用更精简的代码创建匿名内部类.但是该匿名内部类实现的接口只能有一个抽象方法,否则无法使用! - lambda表达式是编译器认可的,最终会将其改为内部类编译到class文件中
语法:
(参数列表)->{
方法体
}
注意事项:
lambda表达式中参数的类型可以忽略不写
lambda表达式方法体中若只有一句代码,则{}可以省略 如果这句话有return关键字,那么return也要一并省略!
案例:
package lambda;
import java.io.File;
import java.io.FileFilter;
public class LambdaDemo {
public static void main(String[] args) {
//匿名内部类形式创建FileFilter
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
return file.getName().startsWith(".");
}
};
FileFilter filter2 = (File file)->{
return file.getName().startsWith(".");
};
//lambda表达式中参数的类型可以忽略不写
FileFilter filter3 = (file)->{
return file.getName().startsWith(".");
};
/*
lambda表达式方法体中若只有一句代码,则{}可以省略
如果这句话有return关键字,那么return也要一并省略!
*/
FileFilter filter4 = (file)->file.getName().startsWith(".");
}
}
二.JAVA IO
Java定义了两个超类(抽象类):
- java.io.InputStream:所有字节输入流的超类,其中定义了读取数据的方法.因此将来不管读取的是什么设备(连接该设备的流)都有这些读取的方法,因此我们可以用相同的方法读取不同设备中的数据
- java.io.OutputStream:所有字节输出流的超类,其中定义了写出数据的方法.
java将流分为两类:节点流与处理流:
- 节点流:也称为低级流.节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的.
- 处理流:也称为高级流.处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作.
实际应用中,我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接,这也是IO的精髓所在.
1.文件流
java.io.FileInputStream和java.io.FileOutputStream
作用是真实连接我们程序和文件之间的"管道"。其中文件输入流用于从文件中读取字节。而文件输出流则
用于向文件中写入字节。
文件流是节点流
JAVA IO将流划分为两类:节点流和处理流
节点流:俗称"低级流",特点:真实连接我们程序和另一端的"管道",负责实际读写数据的流
文件流就是典型的节点流,真实连接我们程序与文件的"管道",可以读写文件数据了。
处理流:俗称"高级流"
特点:
1:不能独立存在(单独实例化进行读写操作不可以)
2:必须连接在其他流上,目的是当数据"流经"当前流时,可以对其做某种加工操作,简化我们的工作、
流的连接:实际开发中经常会串联一组高级流最终到某个低级流上,对数据进行流水线式的加工读写。
常用方法:
-createNewFile()方法,可以创建一个新文件- length():返回一个long值,表示占用的磁盘空间,单位为字节。
- canRead():File表示的文件或目录是否可读
- canWrite():File表示的文件或目录是否可写
- isHidden():File表示的文件或目录是否为隐藏的
- createNewFile():创建一个新文件,如果指定的文件所在的目录不存在会抛出异常java.io.FileNotFoundException
- mkdir:创建一个目录
- mkdirs:创建一个目录,并且会将所有不存在的父目录一同创建出来,推荐使用。
- delete():删除当前文件或目录,如果目录不是空的则删除失败。
- exists():判断File表示的文件或目录是否真实存在。true:存在 false:不存在
- isFile():判断当前File表示的是否为一个文件。
- isDirectory():判断当前File表示的是否为一个目录
- listFiles():获取File表示的目录中的所有子项
- listFiles(FileFilter filter):获取File表示的目录中满足filter过滤器要求的所有子项
文件创建及删除
package file;
import java.io.File;
/**
* 使用File创建目录
*/
public class MkDirDemo {
public static void main(String[] args) {
//在当前目录下新建一个目录:demo
// File dir = new File("demo");
File dir = new File("./a/b/c/d/e/f");
if(dir.exists()){
System.out.println("该目录已存在!");
}else{
// dir.mkdir();//创建目录时要求所在的目录必须存在
dir.mkdirs();//创建目录时会将路径上所有不存在的目录一同创建
System.out.println("目录已创建!");
}
}
}
package file;
import java.io.File;
/**
* 删除一个目录
*/
public class DeleteDirDemo {
public static void main(String[] args) {
//将当前目录下的demo目录删除
File dir = new File("demo");
// File dir = new File("a");
if(dir.exists()){
dir.delete();//delete方法删除目录时只能删除空目录
System.out.println("目录已删除!");
}else{
System.out.println("目录不存在!");
}
}
}
目录的创建与删除案例(删除目录与删除文件相似)
package file;
import java.io.File;
/**
* 使用File创建目录
*/
public class MkDirDemo {
public static void main(String[] args) {
//在当前目录下新建一个目录:demo
// File dir = new File("demo");
File dir = new File("./a/b/c/d/e/f");
if(dir.exists()){
System.out.println("该目录已存在!");
}else{
// dir.mkdir();//创建目录时要求所在的目录必须存在
dir.mkdirs();//创建目录时会将路径上所有不存在的目录一同创建
System.out.println("目录已创建!");
}
}
}
遍历子项并根据要求筛选出符合条件的子项
package file;
import java.io.File;
import java.io.FileFilter;
/**
* 重载的listFiles方法,允许我们传入一个文件过滤器从而可以有条件的获取一个目录
* 中的子项。
*/
public class ListFilesDemo2 {
public static void main(String[] args) {
/*
需求:获取当前目录中所有名字以"."开始的子项
*/
File dir = new File(".");
if(dir.isDirectory()){
// FileFilter filter = new FileFilter(){//匿名内部类创建过滤器
// public boolean accept(File file) {
// String name = file.getName();
// boolean starts = name.startsWith(".");//名字是否以"."开始
// System.out.println("过滤器过滤:"+name+",是否符合要求:"+starts);
// return starts;
// }
// };
// File[] subs = dir.listFiles(filter);//方法内部会调用accept方法
File[] subs = dir.listFiles(new FileFilter(){
public boolean accept(File file) {
return file.getName().startsWith(".");
}
});
System.out.println(subs.length);
}
}
}
文件输入流
字节型输入流:
java.io.InputStream(所有字节输入流的超类)定义着读取字节的相关方法
int read()读取1个字节并以int型整数返回读取到的字节内容,返回的int值中对应的2进制的"低八位", 就是读取到的数据。如果返回的int值为整数-1(这是一个特殊值,32位2进制全都是1)表达的是流读取到了末尾了。
文件复制
文件复制的基本条件: 循环条件是只要文件没有读到末尾就应该复制,
如何直到读取到末尾,必须满足以下前提是:要先尝试读取一个字节,如果返回值是-1就说明读到末尾了 如果返回值不是-1,则说明读取到的是一个字节的内容,就要将他写入到复制文件中
案列
package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 利用文件输入流与输出流实现文件的复制操作
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
//用文件输入流读取待复制的文件
// FileInputStream fis = new FileInputStream("image.jpg");
FileInputStream fis = new FileInputStream("01.rmvb");
//用文件输出流向复制文件中写入复制的数据
// FileOutputStream fos = new FileOutputStream("image_cp.jpg");
FileOutputStream fos = new FileOutputStream("01_cp.rmvb");
int d;//先定义一个变量,用于记录每次读取到的数据
long start = System.currentTimeMillis();//获取当前系统时间
while ((d = fis.read()) != -1) {
fos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:" + (end - start) + "ms");
fis.close();
fos.close();
}
}
块读写操作
块读操作 int read(byte[] data) 一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。 返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。
块写操作 void write(byte[] data) 一次性将给定的字节数组所有字节写入到文件中
void write(byte[] data,int offset,int len) 一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件
块读案列
package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 通过提高每次读写的数据,减少读写次数可以提高读写效率。
*/
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("01.rmvb");
FileOutputStream fos = new FileOutputStream("01_cp.rmvb");
/*
编译完该句代码:byte[] buf = new byte[10240];
在实际开发中,有时候用一个计算表达式更能表现这个值的含义时,我们不妨使用计算表达式
long t = 864000000;
long t = 60 * 60 * 24 * 1000;
*/
byte[] buf = new byte[1024 * 10];//10kb
int len;//记录每次实际读取到的字节数
long start = System.currentTimeMillis();
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕,耗时:" + (end - start) + "ms");
fis.close();
fos.close();
}
}
快写案列
String提供方法: byte[] getBytes(String charsetName) 将当前字符串转换为一组字节
参数为字符集的名字,常用的是UTF-8。 其中中文字3字节表示1个,英文1字节表示1个。
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 使用文件输出流向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输出流
2:将写出的文字先转换为2进制(一组字节)
3:关闭流
文件流有两种创建方式:
1:覆盖模式,对应的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后
在陆续将通过该流写出的内容保存到文件中。
*/
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String line = "让我再看你一遍,从南到北。";
/*
String提供了将内容转换为一组字节的方法:getBytes()
java.nio.charset.StandardCharsets
*/
byte[] data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
line = "像是北五环路蒙住的双眼。";
data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}
块读的两种追加模式:
1:覆盖模式,对应的构造器:
FileOutputStream(String filename)
FileOutputStream(File file)
所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后 在陆续将通过该流写出的内容保存到文件中。2:追加模式,对应的构造器
FileOutputStream(String filename,boolean append)
FileOutputStream(File file,boolean append)
当第二个参数为true时,那么就是追加模式。
所谓追加模式:文件流在创建时若发现该文件已存在,则原内容都保留。通过当前流陆续写出的内容都会被陆续追加到文件末尾。
案列
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 使用文件输出流向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("fos.txt",true);
String line = "qweeer!";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
line = "aaaa";
data = line.getBytes(StandardCharsets.UTF_8);
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 从文件中读取文本数据
*/
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
/*
1:创建一个文件输入流
2:从文件中将字节都读取回来
3:将读取到的字节转换回字符串
*/
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[1024];//1kb
int len = fis.read(data);//块读操作,返回值表达实际读取到了多少字节
System.out.println("实际读取了:"+len+"个字节");
/*
String提供了构造方法可以将一个字节数组还原为字符串
String(byte[] data,Charset charset)
将给定的字节数组data中所有字节按照给定的字符集转换为字符串。
String(byte[] data,int offset,int len,Charset charset)
将给定的字节数组data从下标offset处开始的连续len个字节按照指定的字符集转换为字符串
*/
String line = new String(data,0,len,StandardCharsets.UTF_8);
System.out.println(line.length());//输出字符串长度
System.out.println(line);
fis.close();
}
}
高级流
处理流:也称为高级流.处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作.
特点:
不能独立存在(单独实例化进行读写操作不可以)
必须连接在其他流上,目的是当数据"流经"当前流时,可以对其做某种加工操作,简化我们的工作
流的连接:实际开发中经常会串联一组高级流最终到某个低级流上,对数据进行流水线式的加工读写。
1、缓冲流
缓冲流是一对高级流,在流链接中链接它的目的是加快读写效率。缓冲流内部默认缓冲区为8kb,缓冲流总是块读写数据来提高读写效率。 其继承自java.io.OutputStream
常见构造方法
BufferedOutputStream(OutputStream out):创建一个默认8kb大小缓冲区的缓冲字节输出流,并连接到参数指定的字节输出流上。
BufferedOutputStream(OutputStream out,int size):创建一个size指定大小(单位是字节)缓冲区的缓冲字节输出流,并连接到参数指定的字节输出流上。
注意要点:
flush():强制将缓冲区中已经缓存的数据一次性写出
缓冲流的写出方法功能与OutputStream上一致,需要知道的时write方法调用后并非实际写出,而是先将数据存入缓冲区(内部的字节数组中),当缓冲区满了时会自动写出一次。
案列
package io;
import java.io.*;
/**
* java将流分为节点流与处理流两类
* 节点流:也称为低级流,是真实连接程序与另一端的"管道",负责实际读写数据的流。
* 读写一定是建立在节点流的基础上进行的。
* 节点流好比家里的"自来水管"。连接我们的家庭与自来水厂,负责搬运水。
* 处理流:也称为高级流,不能独立存在,必须连接在其他流上,目的是当数据经过当前流时
* 对其进行某种加工处理,简化我们对数据的同等操作。
* 高级流好比家里常见的对水做加工的设备,比如"净水器","热水器"。
* 有了它们我们就不必再自己对水进行加工了。
* 实际开发中我们经常会串联一组高级流最终连接到低级流上,在读写操作时以流水线式的加工
* 完成复杂IO操作。这个过程也称为"流的连接"。
*
* 缓冲流,是一对高级流,作用是加快读写效率。
* java.io.BufferedInputStream和java.io.BufferedOutputStream
*
*/
public class CopyDemo3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("ppt.pptx");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("ppt_cp.pptx");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d;
long start = System.currentTimeMillis();
while((d = bis.read())!=-1){//使用缓冲流读取字节
bos.write(d);//使用缓冲流写出字节
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+"ms");
bis.close();//关闭流时只需要关闭高级流即可,它会自动关闭它连接的流
bos.close();
}
}
java.io.BufferedInputStream缓冲字节输入流,继承自java.io.InputStream
常见构造方法
BufferedInputStream(InputStream in):创建一个默认8kb大小缓冲区的缓冲字节输入流,并连接到参数指定的字节输入流上。
BufferedInputStream(InputStream in,int size):创建一个size指定大小(单位是字节)缓冲区的缓冲字节输入流,并连接到参数指定的字节输入流上。
案列
package io;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 缓冲输出流写出数据的缓冲区问题
*/
public class BOS_FlushDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String line = "AAAAAA!";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
bos.write(data);
System.out.println("写出完毕!");
/*
缓冲流的flush方法用于强制将缓冲区中已经缓存的数据一次性写出。
注:该方法实际上实在字节输出流的超类OutputStream上定义的,并非只有缓冲
输出流有这个方法。但是实际上只有缓冲输出流的该方法有实际意义,其他的流实现
该方法的目的仅仅是为了在流连接过程中传递flush动作给缓冲输出流。
*/
bos.flush();//冲
bos.close();
}
}
2、对象流
对象流是一对高级流,在流链接中的作用是完成对象的序列化与反序列化
序列化:是对象输出流的工作,将一个对象按照其结构转换为一组字节的过程。
反序列化:是对象输入流的工作,将一组字节还原为对象的过程。
java.io.ObjectInputStream对象输入流,继承自java.io.InputStream
java.io.ObjectOutputStream对象输出流,继承自java.io.OutputStream
序列化
常见构造器
ObjectInputStream(InputStream in):创建一个对象输入流并连接到参数in这个输入流上
常用方法
Object readObject():进行对象反序列化,将读取的字节转换为一个对象并以Object形式返回(多态)。如果读取的字节表示的不是一个java对象会抛出异常:java.io.ClassNotFoundException
序列化案列
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* 对象流(是一对高级流)
* java.io.ObjectInputStream和ObjectOutputStream
* 对象流在流连接中的作用是进行对象的序列化与反序列化
* 其中对象输出流负责对象序列化。对象输入流负责对象反序列化
*
* 所谓对象序列化:
* 将写出的对象按照其结构转换为一组字节的过程。
*/
public class OOSDemo {
public static void main(String[] args) throws IOException {
String name = "aa";
int age = 15;
String gender = "男";
String[] otherInfo = {"摄影技术一流","喜欢拍片儿","是一名技术老师"};
//将该Person对象写入文件person.obj中
Person p = new Person(name,age,gender,otherInfo);
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
/*
对象输出流提供了一个直接写出对象的方法(进行对象序列化的操作)
void writeObject(Object obj)
序列化时可能出现异常:
java.io.NotSerializableException: io.Person
注:冒号后面的io.Person是指序列化的就是这个类的实例出现的错误
原因:
对象输出流在进行序列化对象时,要求该对象所属的类必须实现接口:java.io.Serializable接口
并且该类中所有引用类型属性也必须实现该接口,否则会抛出上述异常。
*/
oos.writeObject(p);
System.out.println("写出完毕!");
oos.close();
}
}
反序列化
注意事项:
需要进行序列化的类必须实现接口:java.io.Serializable 实现序列化接口后最好主动定义序列化版本号这个常量。 这样一来对象序列化时就不会根据类的结构生成一个版本号,而是使用该固定值。 那么反序列化时,只要还原的对象和当前类的版本号一致就可以进行还原。
transient关键字可以修饰属性,用于在进行对象序列化时忽略不必要的属性,达到对象瘦身的目的
案列
package io;
import java.io.Serializable;
import java.util.Arrays;
/**
* 使用当前类实例测试对象流的读写操作
*/
public class Person implements Serializable {
public static final long serialVersionUID = 1L;
private String name;//姓名
private int age;//年龄
private String gender;//性别
private String[] otherInfo;//其他信息
public Person(String name, int age, String gender, String[] otherInfo) {
this.name = name;
this.age = age;
this.gender = gender;
this.otherInfo = otherInfo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getOtherInfo() {
return otherInfo;
}
public void setOtherInfo(String[] otherInfo) {
this.otherInfo = otherInfo;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", otherInfo=" + Arrays.toString(otherInfo) +
'}';
}
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* 使用对象输入流完成对象的反序列化
*/
public class OISDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//从person.obj文件中将对象反序列化回来
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
/*
Object readObject()
该方法会进行对象的反序列化,如果对象流通过其连接的流读取的字节分析并非
是一个java对象时,会抛出异常:ClassNotFoundException
*/
Person p = (Person)ois.readObject();
System.out.println(p);
}
}
3、字符流
java将流按照读写单位划分为字节流与字符流.
java.io.InputStream和OutputStream是所有字节流的超类
而java.io.Reader和Writer则是所有字符流的超类,它们和字节流的超类是平级关系.
Reader和Writer是两个抽象类,里面规定了所有字符流都必须具备的读写字符的相关方法.
字符流最小读写单位为字符(char),但是底层实际还是读写字节,只是字符与字节的转换工作由字符流完成.
4、转换流(是一对高级流,同时是一对字符流)
java.io.InputStreamReader和OutputStreamWriter
它们是字符流非常常用的一对实现类同时也是一对高级流,实际开发中我们不直接操作它们,但是它们在流连接中是非常重要的一环.
注意:
* JAVA IO将流按照读写数据的单位将流分为了两类:字节流与字符流
* java.io.InputStream和OutputStream这两个超类是所有【字节流】的超类
* java.io.Reader和Writer这两个是所有【字符流】的超类
* 这两对超类之间是没有继承关系的,属于平级的。
* 字符流是以字符为最小单位(char)读写数据的。
* 注:底层实际还是读写字节,只不过字符与字节的转换由字符流自动完成了。
* 由于字符流最小读写单位为字符,因此字符流【只适合读写文本数据】
作用:1:衔接字节流与其他字符流
2:将字符与字节相互转换
使用转换输出流向文件中写入文本数据案列
package io;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class OSWDemo {
public static void main(String[] args) throws IOException {
/*
使用这一对流演示转换流的读写字符方法
java.io.Writer所有字符输出流的超类上,定义了写出字符的相关方法
void write(int d)写出一个字符,实际传入的应当是一个char。
void write(char[] data)
void write(char[] data,int offset,int len)
void write(String str) 直接写出一个字符串
*/
FileOutputStream fos = new FileOutputStream("osw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,StandardCharsets.UTF_8);
String line = "如果你突然打了个喷嚏,那一定就是我在想你。";
osw.write(line);//转换流的write(String str)会将写出的字符串转换为字节然后写出
osw.write("如果半夜被手机吵醒,那一定就是我关心。");
System.out.println("写出完毕!");
osw.close();
}
}
使用转换输入流读取文本文件案列
package io;
import java.io.*;
/**
* 转换字符输入流
* 可以将读取的字节按照指定的字符集转换为字符
*/
public class ISRDemo {
public static void main(String[] args) throws IOException {
//将osw.txt文件中的所有文字读取回来.
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
/*
字符流读一个字符的read方法定义:
int read()
读取一个字符,返回的int值实际上表示的是一个char(低16位有效).如果返回的
int值表示的是-1则说明EOF
*/
//测试读取文件中第一个字
// int d = isr.read();
// char c = (char)d;
// System.out.println(c);
//循环将文件所有字符读取回来
int d;
while((d = isr.read()) != -1){
System.out.print((char)d);
}
isr.close();
}
}
转换流的意义:
实际开发中我们还有功能更好用的字符高级流.但是其他的字符高级流都有一个共通点:不能直接连接在字节流上.而实际操作设备的流都是低级流同时也都是字节流.因此不能直接在流连接中串联起来.转换流是一对可以连接在字节流上的字符流,其他的高级字符流可以连接在转换流上.在流连接中起到"转换器"的作用(负责字符与字节的实际转换)
5、字符流
缓冲字符输出流:java.io.PrintWriter
java.io.BufferedWriter和BufferedReader: 缓冲字符流内部也有一个缓冲区,读写文本数据以块读写形式加快效率.并且缓冲流有一个特别的功能:可以按行读写文本数据.
java.io.PrintWriter具有自动行刷新的缓冲字符输出流,实际开发中更常用.它内部总是会自动连接BufferedWriter作为块写加速使用.
案列
package io;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* 练习PrintWriter的流连接操作
*/
public class PWDemo2 {
public static void main(String[] args) throws FileNotFoundException {
//文件输出流(低级流,字节流) 作用:向文件中写出字节
FileOutputStream fos = new FileOutputStream("pw2.txt");
//转换输出流(高级流,字符流) 作用:1衔接字符与字节流的 2:将写出的字符转换为字节
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
//缓冲字符输出流(高级流,字符流) 作用:块写文本数据加速的(内部有一个8k的char数组)
BufferedWriter bw = new BufferedWriter(osw);
//具有自动行刷新功能(高级流,字符流) 作用:1按行写出字符串(println) 2:自动行刷新
/*
当我们创建PrintWriter时,构造方法里第一个参数为一个流,那么就支持再传入
一个boolean值参数表示是否打开自动行刷新功能,传入true则打开。
此时每当我们调用它的println方法写出一行字符串后就会自动flush()一次。
注意:print方法和write方法写出字符串时并不会自动flush()!!!!
*/
PrintWriter pw = new PrintWriter(bw,true);
/*
完成一个简易记事本工具
将控制台上输入的每一行字符串按行写入到该文件中
如果单独输入exit,则程序退出。
思路:
用一个死循环,重复做下面的工作
1:在控制台上输入一行字符串
2:判断输入的字符串是否为"exit"
若是:则break掉循环退出程序
若不是:则将输入的字符串通过println方法写入文件
*/
Scanner scanner = new Scanner(System.in);
while(true) {
String line = scanner.nextLine();
if("exit".equals(line)){
break;
}
pw.println(line);
// pw.print("");//不会自动flush
// pw.write("");//不会自动flush
}
System.out.println("写出完毕!");
pw.close();
}
}
缓冲字符输入流:java.io.BufferedReader
缓冲字符输入流:是一个高级的字符流,特点是块读文本数据,并且可以按行读取字符串。其作用为:
* 1:块读文本数据加速(内部有一个默认8k的char数组)
* 2:可以按行读取字符串
package io;
import java.io.*;
/**
* 使用缓冲字符输入流按行读取字符串
* 该高级流的主要作用:
* 1:块读文本数据加速(内部有一个默认8k的char数组)
* 2:可以按行读取字符串
*/
public class BRDemo {
public static void main(String[] args) throws IOException {
//将当前源代码输出到控制台上
/*
思路:
读取当前源代码文件,按行读取,并且将读取到的每一行字符串都输出到控制台上即可
*/
//文件输入流(低级流,字节流) 作用:从文件中读取字节
FileInputStream fis = new FileInputStream("./src/io/BRDemo.java");
//转换输入流(字符流,高级流) 作用:1衔接字节与字符流 2将读取的字节转换为字符
InputStreamReader isr = new InputStreamReader(fis);
//缓冲字符输入流(字符流,高级流) 作用:1块读字符数据加速 2按行读取字符串
BufferedReader br = new BufferedReader(isr);
/*
BufferedReader缓冲字符输入流
提供了一个独有的方法:readLine()
作用:读取一行字符串。连续读取若干字符直到遇到了换行符位置,并将换行符之前的
内容返回。注意:返回的字符串里不包含最后的换行符。
特殊情况:
如果这一行只有一个换行符,那么返回值为空字符串:""
如果读取到了流的末尾,那么返回值为null。
实际运行时:
当我们第一次调用readLine()方法时,缓冲字符输入流实际会一次性读取8k的char
回来并存入内部的char数组中(块读文本操作)。readLine方法只将char数组中从头
开始一直到第一个换行符位置的内容以一个字符串形式返回。
*/
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
}
IO总结
三、异常处理机制
1、java异常处理机制
-
java中所有错误的超类为:Throwable。其下有两个子类:Error和Exception
-
Error的子类描述的都是系统错误,比如虚拟机内存溢出等。
-
Exception的子类描述的都是程序错误,比如空指针,下表越界等。
-
通常我们程序中处理的异常都是Exception。
异常处理机制中的try-catch
语法:
* try{
* 代码片段...
* }catch(XXXException e){
* 出现错误后的补救措施(B计划)
* }
注意要点
try{ }语句块不能单独写,后面要么跟catch语句块要么跟finally语句块 ,异常处理机制关注的是:明知道程序可能出现某种错误,但是该错误无法通过修改逻辑完全规避掉时,我们会使用异常处理机制,在出现该错误是提供一种补救办法。 凡是能通过逻辑避免的错误都属于bug!就应当通过逻辑去避免!
案例:
package exception;
/**
* 异常处理机制中的try-catch
*/
public class TryCatchDemo {
public static void main(String[] args) {
System.out.println("程序开始了...");
try {
// String str = null;
// String str = "";
String str = "a";
/*
若str=null的情况
当JVM执行到下面代码时:str.length()会出现空指针,此时虚拟机就会根据该情况
实例化一个对应的异常实例出来,即:空指针异常实例 NullPointerException实例
然后将程序从一开始执行到报错这句话的过程设置到该异常实例中,此时该异常通过
类型名字可以表达出现了什么错误,并将来可以通过输出错误信息来得知错误出现在那里
虚拟机会将该异常抛出
当某句代码抛出了一个异常时,JVM会做如下操作:
1:检查报错这句话是否有被异常处理机制控制(有没有try-catch)
如果有,则执行对应的catch操作,如果没有catch可以捕获该异常则视为没有
异常处理动作
2:如果没有异常处理,则异常会被抛出当当前代码所在的方法之外由调用当前方法的
代码片段处理该异常
*/
System.out.println(str.length());//抛出空指针异常
System.out.println(str.charAt(0));
System.out.println(Integer.parseInt(str));
/*
当try中某句代码报错后,就会跳出try执行下面对应的catch块,执行后就会
退出catch继续向后执行。因此try语句块中报错代码以下的内容都不会被执行
*/
System.out.println("!!!!!!!!!!!!!!");
// }catch(NullPointerException e){
// //这里实际开发中是写补救措施的,通常也会将异常信息输出便于debug
// System.out.println("出现了空指针,并解决了!");
// }catch(StringIndexOutOfBoundsException e){
// System.out.println("处理字符串下标越界问题!");
// }
/*
当try语句块中可能出现的几种不同异常对应的处理办法相同时,可以采取合并
catch的做法,用同一个catch来捕获这几种可能出现的异常,而执行措施使用
同一个。
*/
}catch(NullPointerException|StringIndexOutOfBoundsException e){
System.out.println("处理空指针或下标越界!");
/*
当catch捕获某个超类型异常时,那么try语句块中出现它类型异常时都可以被这个
catch块捕获并处理。
如果多个catch捕获的异常之间存在继承关系时,一定是子类异常在上超类异常在下
*/
}catch(Exception e){
System.out.println("反正就是出了个错!");
}
System.out.println("程序结束了...");
}
}
异常处理机制中的finally
finally块定义在异常处理机制中的最后一块。它可以直接跟在try之后,或者最后一个catch之后。
finally可以保证只要程序执行到了try语句块中,无论try语句块中的代码是否出现异常,最终finally都必定执行。
finally通常用来做释放资源这类操作。
案例
package exception;
public class FinallyDemo {
public static void main(String[] args) {
System.out.println("程序开始了...");
try{
String str = "abc";
System.out.println(str.length());
return;
}catch(Exception e){
System.out.println("出错了,并处理了");
}finally{
System.out.println("finally中的代码执行了!");
}
System.out.println("程序结束了");
}
}
自动关闭特性
JDK7之后,java提供了一个新的特性:自动关闭。旨在IO操作中可以更简洁的使用异常处理机制完成最后的close操作。
语法:
try(
定义需要在finally中调用close()方法关闭的对象.
){
IO操作
}catch(XXXException e){
...
}
案例
package exception;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* JDK7之后java推出了一个特性:自动关闭特性
* 旨在让我们用更简洁的语法完成IO操作的异常处理机制(主要就是简化了finally关闭流的操作)
*/
public class AutoCloseableDemo {
public static void main(String[] args) {
/*
该特性是编译器认可的,并非虚拟机。实际上编译器编译完毕后的样子可参考FinallyDemo2
*/
try(
//只有实现了AutoCloseable接口的类才可以在这里定义!编译器最终会补充代码在finally中调用其close关闭
FileOutputStream fos = new FileOutputStream("fos.dat");
){
fos.write(1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
throw与throws关键字
集合框架
参考文档:https://blog.csdn.net/ThinkWon/article/details/98844796