一:基本概念:
i:input输入(读取)
o:output输出(写入)
流:数据(字符,字节)。1个字符=2个字节,一个字节=8个二进制位。
输入:把硬盘中的数据,读取到内存中使用。
输出:把内存中的数据,写入硬盘中保存。
IO流分类:字符输入流(Reader),字符输出流(Writer),字节输入流(InputStream),字节输出流(OutputStream)。
二:File类的使用
(1)基础:
java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关。
File能新建、删除、重命名文件和目录,但File不能访问访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
想要在JAVA程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是JAVA程序中的一个File对象,可能没有一个真实存在的文件或目录。
File对象可以作为参数传递给流的构造器。
(2)File类的常用构造器
import java.io.File;
/**
* File类的使用
*
* 1.File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
* 2.File类声明在java.io包下
* 3.File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,
* 并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。
* 4.后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的“终点”。
*/
/**
* File类的常用构造器
*
* 1.public File(String pathname);
* 以pathname为路径创建File对象, 可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
* 绝对路径:是一个固定的路径,从盘符开始。
* 相对路径:是相对于某个位置开始。
* 2.public File(String parent,String child);
* 以parent为父路径,child为子路径创建File对象。
* 3.public File(File parent,String child);
* 根据一个父File对象和文件路径创建File对象。
*/
public class FileTest {
public static void main(String[] args) {
//构造器1
File file1 = new File("hello.txt");//相对于当前module
File file2 = new File("D:\\hgk111\\abc.txt");
//构造器2
File file3 = new File("D:\\hgk1","aaa");
//构造器3
File file4 = new File(file3,"bbb.txt");
}
}
(3)File类中的常用方法
/**
* 一:File类的获取功能
* 1.public String getAbsolutePath();获取绝对路径
* 2.public String getPath();获取路径
* 3.public String getName();获取名称
* 4.public String getParent();获取上层文件目录路径,若无,返回null
* 5.public long length();获取文件长度(即字节数)。不能获取目录的长度
* 6.public long lastModified();获取最后一次的修改时间,毫秒值
* 7.public String[] list();获取指定目录下的所有文件或者文件目录的名称数组
* 8.public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组
* 二:File类的重命名功能
* public boolean renameTo(File dest):把文件重命名为指定的文件路径.
* 例如,file1.renameTo(file2)
* 要想保证返回true,需要保证file1在硬盘中是存在的,且file2不能在硬盘中存在。
* 三:File类的判断功能
* 1.public boolean isDirectory();判断是否是文件目录
* 2.public boolean isFile();判断是否是文件
* 3.public boolean exists();判断是否存在
* 4.public boolean canRead();判断是否可读
* 5.public boolean canWrite();判断是否可写
* 6.public boolean isHidden();判断是否隐藏
*/
public boolean createNewFile()://真正的在硬盘中创建文件。若文件存在,则不创建,返回false。
public boolean mkdirs()://创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,也不创建了。
public boolean mkdirs()://创建文件目录。如果上层文件目录不存在,一并创建。
创建注意事项:如果创建文件或者文件目录没有写盘符,那么,默认在项目路径下。
public boolean delete()://删除文件或者文件夹
删除注意事项:java中的删除不走回收站。要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录。
三:IO流原理及流的分类
(1)原理:
- I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件、网络通讯等。
- JAVA程序中,对于数据的输入/输出操作以“流(Stream)”的方式进行。
- java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
(2)流的分类
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
- 按数据流的流向不同分为:输入流、输出流
- 按流的角色不同分为:节点流、处理流
(3)流的体系结构
抽象基类 节点流(或文件流) 缓冲流(处理流)
InputStream FileInputStream BufferedInputStream
OutputStream FileOutputStream BufferedOutputStream
Reader FileReader BufferedReader
Writer FileWriter BufferedWriter
四:节点流(或文件流)
(1)FileReader读入数据的基本操作
/**
* 将io下的hello.txt文件内容读入程序中,并输出到控制台
* 1.read():返回读入的一个字符,如果达到文件末尾,返回-1.
* 2.为了保证流资源一定可以执行关闭操作,需要执行try-catch-finally处理。
* 3.读入的文件一定要存在,否在就会报“文件不存在”异常。
*/
public static void test(){
FileReader fr = null;
try {
//1.实例化File类对象,指明要操作的文件
File file = new File("D:\\hello.txt");
//2.提供具体的流
fr = new FileReader(file);
//3.数据的读入
//3.1--read():返回读入的一个字符,如果达到文件末尾,返回-1.
/* int data;
while ((data=fr.read()) != -1){
System.out.print((char) data);
}*/
//3.2--对read()操作升级:使用read的重载方法
char[] cbuf = new char[1024];//字符数组
//byte bytes = new byte[1024];//字节数组
int len;
//read(char[] cbuf):返回每次读入到cbuf数组中的字符的个数,如果达到文件末尾,返回-1.
while((len = fr.read(cbuf)) != -1){
System.out.println(new String(cbuf,0,cbuf.length));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//4.关闭流
try {
//以防File并没有被实例化
if (fr != null){
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2)FileWriter写出数据的操作
/**
* 从内存中写出数据到硬盘的文件里
* 说明:
* 1.如果文件不存在,在输出的过程中,会自动创建此文件。
* File对应的硬盘文件如果存在,
* 如果流使用的构造器是:FileWriter(file,false)/FileWriter(file),对原有文件的进行覆盖
* 如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有的基础上追写
*/
public static void testFileWriter() {
//1.提供File类的对象,指明写到的文件
FileWriter fw = null;
try {
File file = new File("hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
fw = new FileWriter(file);
//3.写出的操作
fw.write("I hava a Dream!!!\n");
fw.write("我有一个梦想!!!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
if (fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(3)使用FileReader和FileWriter实现文本复制
public static void testFileReaderFileWriter() {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File对象,指明读入和写出的对象
File file1 = new File("hello1.txt");
File file2 = new File("hello2.txt");
//2.创建输入流和输出流对象
fr = new FileReader(file1);
fw = new FileWriter(file2, true);//在原文件后面追加续写
//3.数据的读入和写出操作
char[] chars = new char[1024];
int len;
while ((len = fr.read(chars)) != -1) {
//每次写出len个字符
fw.write(chars, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
//4.关闭流资源
try {
if (fw != null){
fw.close();
}
}catch (IOException e){
e.printStackTrace();
}
try {
if (fr != null){
fr.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
五:缓冲流
/**
* 案例:使用字节缓冲流实现非文本文件的复制.
* 缓冲流的作用
* 1.提高流的读取、写入的速度
* 提高读写速度的原因:内部提供了一个缓冲区。
* 2.处理流:就是“套接”在已有的流的基础之上。
*/
public static void BufferedStreamTest(){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.创建File对象,指明读入和写出的对象
File file1 = new File("D:\\测试\\小说.zip");
File file2 = new File("D:\\测试\\小小说1.zip");
//2.造流
//2.1:创建字节流
FileInputStream is = new FileInputStream(file1);
FileOutputStream os = new FileOutputStream(file2);
//2.2:创建缓冲流
bis = new BufferedInputStream(is);
bos = new BufferedOutputStream(os);
//3.数据的读入和写出操作
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
//每次写出len个字符
bos.write(bytes, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
//4.关闭流资源
//要求:先关闭外层的流,再关闭内层的流
//说明:关闭外层流的同时,内层流也会自动的进行关闭,因此关于内层流的关闭,可以省略
try {
if (bos != null){
bos.close();
}
}catch (IOException e){
e.printStackTrace();
}
try {
if (bis != null){
bis.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
/**
* 案例:使用字符缓冲流实现文本文件的复制.
*/
public static void BufferedReaderWriterTest(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
//1.造流
br = new BufferedReader(new FileReader(new File("D:\\测试\\1.txt")));
bw = new BufferedWriter(new FileWriter(new File("D:\\测试\\3.txt")));
//2.数据的读入和写出操作
//方式一:使用char[]数组
/*char[] chars = new char[1024];
int len;
while ((len = br.read(chars)) != -1) {
//每次写出len个字符
bw.write(chars, 0, len);
}*/
//方式二:使用String
String data;
//readLine():字符缓冲流中的方法,一次读取一行
while ((data=br.readLine())!=null){
bw.write(data+"\n");//data中不包含换行符
bw.newLine();//提供换行的操作
}
}catch (IOException e){
e.printStackTrace();
}finally {
//3.关闭资源
try {
if (br != null){
br.close();
}
}catch (IOException e){
e.printStackTrace();
}
try {
if (bw != null){
bw.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
六:转换流
字节流中的数据都是字符时,转成字符流操作更高效。
很多时候我们使用转换流来处理文件乱码问题,实现编码和解码的功能。
/**
* 处理流之二:转换流(属于字符流)
* 1.作用:
* 转换流提供了在字节流和字符流之间的转换。
* 2.类型:
* InputStreamReader:将InputStream转换为Reader(解码)
* OutputStreamWriter:将Writer转换为OutputStream(编码)
* 3.解码:字节、字节数组 ---> 字符数组、字符串
* 编码:字符数组、字符串 ---> 字节、字节数组
* 4.字符集:
* 编码表的由来:计算机之只能识别二进制数据,早期由来是电信号,为了方便应用计算机,让它可以识别各个国家的
* 的文字。就将各个国家的文字用数字来表示。并一一对应,形成一张表,这就是编码表。
* 常见的编码表:
* ASCLL:美国标准信息交换码,用一个字节的7位可以表示。
* GBK:中国的中文编码表,最多两个字节编码所有字符
* UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。
*
*/
public class InputStreamReaderTest {
public static void main(String[] args) {
test1();
}
public static void test1(){
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
FileInputStream fis = new FileInputStream("D:\\测试\\故事.txt");
isr = new InputStreamReader(fis,"utf-8");//如果第二个参数不写,则使用系统默认的字符集,utf-8.
FileOutputStream fos = new FileOutputStream("新的故事.txt");
osw = new OutputStreamWriter(fos,"gbk");
//根据什么选择用什么字符集解码?这要根据当初文本文件当初存的时候用的什么字符集编码。
//读、写操作
char[] chars = new char[200];
int len;
while ((len=isr.read(chars))!=-1){
System.out.println(new String(chars,0,len));
osw.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (osw != null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
七:标准输入、输出流
/**
* 标准的输入、输出流:
* 1:
* System.in:标准的输入流,默认从键盘输入
* System.out:标准的输出流,默认从控制台输出
* 2:
* System类的setIn(InputStream is)/setOut(OutputStream os)方式重新指定输入和输出的流。
* 3:练习
* 从键盘输入字符串,要求将读取到的整行字符串转换成大写输出,然后继续进行输入操作,直到输入“e"或者"exit"时,退出程序。
* 方式一:使用Scanner实现,调用next()返回一个字符串。
* 方式二:使用System.in实现。
* System.in --> 转换流 ——> BufferedReader的readLine().
*/
public static void test1(){
BufferedReader br = null;
try {
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
while (true){
System.out.println("请输入字符串:");
String data = br.readLine();
if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)){
System.out.println("程序结束!");
break;
}
String s = data.toUpperCase();
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
八:打印流
/**
* 打印流:
* 实现将基本数据类型的数据格式转化为字符串输出
* 打印流:PrintStream 和 PrintWriter
* 提供了一系列重载的print()和println()方法,用于多种数据类型的输出。
*/
System.SetOut();重新指定一个位置,打印到那里
九:数据流
/**
* 数据流:
* 1.DataInputStream和DataOutputStream
* 2.作用:用于读取或写出基本数据类型的变量或字符串
* 3.注意点:读取不同类型的数据要与当初写入文件时保存的数据的顺序一致!!
*
*/
public static void test1(){
DataOutputStream dos = null;
DataInputStream dis = null;
try {
dos = new DataOutputStream(new FileOutputStream("D:\\abc.txt"));
dis = new DataInputStream(new FileInputStream("D:\\abc.txt"));
//写数据
dos.writeUTF("哈哈哈");
dos.flush();
dos.writeInt(100);
dos.flush();
dos.writeBoolean(true);
dos.flush();
//读数据
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();
System.out.println("name=" +name);
System.out.println("age=" +age);
System.out.println("isMale=" +isMale);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dos!=null){
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
if (dis!=null){
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
十:对象流
/**
* 对象流:ObjectInputStream;ObjectOutputStream
* 1.作用:用于存储和读取基本数据类型数据或对象的处理流,它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
* 2.注意:ObjectInputStream和ObjectOutputStream不能序列化static和transient修饰的成员变量。
* 3.对象需要满足什么条件,才能被序列化?
* (1)需要实现接口:Serializable接口。
* (2)需要提供一个全局常量:serialVersionUID
* 如:public static final long serialVersionUID=478529578257275L;
* (3)除了当前的要序列化的类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。
* 默认情况下,基本数据类型也是可序列化的。
* 4.序列化机制(重点)
* 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久的保存在磁盘上,
或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的Java对象。
*/
public class ObjectInOutputStream {
public static void main(String[] args) {
test1();
test2();
}
/**
* 序列化过程:用ObjectOutputStream类保存基本数据类型或对象的机制。
* 换句话说,就是将内存中的java对象保存到磁盘中或通过网络传输出去。
*/
public static void test1(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("D:\\ccc.txt"));
oos.writeObject(new String("给我一首歌的时间。"));
oos.flush();//刷新操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 反序列化:用ObjectInputStream类读取基本数据或对象的机制。
* 换句话说,就是将磁盘文件中的对象还原为内存中的一个java对象。
*/
public static void test2(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("D:\\ccc.txt"));
Object o = ois.readObject();
String str = (String)o;
System.out.println(str);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}