IO(Input Output)流
IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流
按流向分为:输入流和输出流
IO流常用基类
字节流的抽象基类:
InputStream,OutputStream
字符流的抽象基类:
Reader,Writer
注意:由这四个类派生出来的子类名称都是以其父类名的后缀
如:InputStream的子类FileInputStream
Reader的子类FileReader
字符流和字节流
既然io流是用于操作数据的,那么数据的最常见的体现形式是:文件
需求:创建一个文件,写入一些文字数据
//创建一个FileWriter对象。该对象一被初始化,就要明确要操作的文件。如果在该目录下已有该文件,原文件会被覆盖。
//其实这里就是明确数据的存放地
FileWriter fw = new FileWriter("text.txt");
//调用writer方法写入内容
fw.write("你好!hello world!");
//刷新流对象中的缓冲中的数据,将数据刷到目标文件里
fw.flush();
fw.write("早上好");
fw.flush();
关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,将数据刷到目标文件中,和flush()的区别是:flush()刷新后可以继续使用,close()刷新后,流就会被关闭。
fw.close();
//fw.write("早上好");
创建一个文件,并且往里面输入内容
FileWriter fw = null;
try {
// 创建一个 FileWriter 实例对象,文件写句柄,
fw = new FileWriter("copy1.txt");
// 创建一个 BufferedWriter 实例对象,获取文件的写入句柄,向文件里面写入信息
BufferedWriter bw = new BufferedWriter(fw);
//通过write()方法往文件里面写入信息。
bw.write("你好!hello world!");
//关闭
bw.close();
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
读取文件,并输出在控制台
FileReader fr;
try {
// FileReader 创建一个文件读句柄
fr = new FileReader("copy1.txt");
//创建一个文件内容读取句柄,读取文件内容
BufferedReader br = new BufferedReader(fr);
String str = null ;
try {
// System.out.println("213");
while((str = br.readLine()) != null){
System.out.println(str);
}
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
往文件里面添加数据
// 直接写会把里面的内容覆盖掉
FileWriter fw = new FileWriter("copy1.txt",true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("
你上午好"+new Date());
bw.close();
FileReader fr = new FileReader("copy1.txt");
char[] chr = new char[1024];
int num = 0;
while((num = fr.read(chr)) != -1){
System.out.println(new String(chr,0,num));
}
fw.close();
复制文件
复制:复制的原理:其实就是将文件数据存储到另一个文件中
1、先创建一个文件,用于存储复制的数据
2、定义读取流和要拷贝的文件进行关联
3、通过不断的读写完成数据的存储
4、关闭资源
复制方法1:
FileWriter fw = null;
try {
//创建一个新的文件,用于复制的内容存储
fw = new FileWriter("copy2.txt");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileReader fr;
try {
fr = new FileReader("copy1.txt");
int ch = 0;
try {
while((ch = fr.read()) != -1){
fw.write(ch);
}
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
第二种读取方式:通过字符数组进行读取
FileReader fr = new FileReader("copy1.txt");
char[] chr = new char[3];
// int num = fr.read(chr);
// System.out.println(num+" "+new String(chr));
// int num1 = fr.read(chr);
// System.out.println(num1+" "+new String(chr));
// int num2 = fr.read(chr);
// System.out.println(num2+" "+new String(chr));
int num = 0;
while((num = fr.read(chr)) != -1){
System.out.println(new String(chr,0,num));
}
复制方法2:
FileWriter fw = null;
FileReader fr = null;
try {
//写入的目标文件
fw = new FileWriter("copy2.txt");
//读取的目标文件
fr = new FileReader("copy1.txt");
//创建每次读取的大小是1024字符
char[] chr = new char[1024];
int len = 0;
while((len = fr.read(chr) )!= -1 ){
fw.write(chr,0,len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(fr != null){
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fw != null){
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
练习:读取一个java文件,在控制台输出这个java文件
IO流注意:
我们在关闭流的时候一定要在finally里面,在关闭的时候要加判断,判断我们的文件是否存在,如果前面代码抛出异常是不会运行后面的代码,所以在关闭的时候要加上if判断其是否为空,不为空则关闭。
finally{
try {
if(fw != null){
fw.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
字符流缓冲区
缓冲区的出现提高了对数据的读写效率。
所以在创建缓冲区之前,必须要有流对象。
对应的类:
BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedWriter 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
缓冲区要结合流才可以使用
在流的基础上对流的功能进行了增强。
字符流输入缓冲区:
FileWriter fw = null ;
BufferedWriter bw = null;
try {
// 创建一个字符写入流对象
fw = new FileWriter("abc.txt");
// 为了提高字符写入流效率,加入了缓冲技术,只要讲需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
bw = new BufferedWriter(fw);
//
for(int i = 0 ; i < 10;i++){
bw.write("早上好"+i);
// 该缓冲区提供了一个跨平台的换行符newLine()
bw.newLine();
bw.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(bw != null){
bw.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
字符流读取缓冲区:
try {
// 创建一个读取流对象和文件相关联
FileReader fr = new FileReader("abc.txt");
// 为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数
BufferedReader br = new BufferedReader(fr);
String str;
try {
while((str = br.readLine()) !=null){
System.out.println(str);
}
// String str = br.readLine();
// System.out.println(str);
// String str1 = br.readLine();
// System.out.println(str1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
练习:根据BufferedReader类中的方法readLine()。自己定义一个类(MyBufferedReader)中包含一个功能,和readLine()方法一致的方法。
装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类就称之为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
public class Person {
public void eat(){
System.out.println("吃饭!");
}
}
class SuperPerson{
private Person p;
SuperPerson(Person p){
this.p = p;
}
public void superEat(){
System.out.println("开胃酒");
p.eat();
System.out.println("小吃、甜点");
}
装饰和继承有什么区别?
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
装饰和继承
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader//装饰类
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。
LineNumberReader
FileReader fr = null;
LineNumberReader lnb = null;
try {
fr = new FileReader("abc.txt");
lnb = new LineNumberReader(fr);
String line = null;
try {
while((line = lnb.readLine()) != null){
System.out.println(lnb.getLineNumber()+":"+line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
lnb.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
练习:模拟LineNumberReader自定义一个实现类。功能有getLineNumber()和readLine()方法。
字节流:
想要操作图片数据,我们就要用到字节流
输入流:
FileOutputStream fos = new FileOutputStream("text.txt");
fos.write("dsadsa".getBytes());
fos.close();
输出流:
// 一个一个的读
int ch = 0;
while((ch = fis.read()) != -1){
System.out.println((char)ch);
}
//装进数组读
byte[] bt = new byte[1024];
int ch = 0;
while((ch = fis.read(bt)) != -1){
System.out.println(new String(bt,0,ch));
}
//装进一个刚刚好的数组里读
// 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
// 简单讲:返回就是字节数
int num = fis.available();
// System.out.println(num);
// 定义一个刚刚好的缓冲区,省掉循环
byte[] bt = new byte[num];
fis.read(bt);
System.out.println(new String(bt));
复制图片:
思路:
1、用字节读取流对象和图片关联。
2、用字节写入流对象创建一个图片文件,用语存储获取到的图片数据
3、通过循环读写,完成数据的存储
4、关闭资源
字节流缓冲区
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//通过字节流缓冲区 演示map3的的复制
public class CopyMp3Demo {
public static void main(String[] args) throws IOException{
long starttime = System.currentTimeMillis();
copy();
long endtime = System.currentTimeMillis();
System.out.println("所需时间:"+(endtime - starttime)+"毫秒");
}
// 通过字节流缓冲区完成复制
public static void copy() throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("/Users/chenbo/Documents/workspace/Study1/src/com/rf/IO/庄心妍-再见只是陌生人.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("/Users/chenbo/Documents/workspace/Study1/src/com/rf/IO/再见只是陌生人.mp3"));
int ch = 0;
while((ch = bis.read()) != -1){
bos.write(ch);
}
bis.close();
bos.close();
}
}
读取键盘录入
//读取键盘录入
import java.io.IOException;
import java.io.InputStream;
public class ReadIn {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
InputStream in = System.in;
int ch = in.read();
System.out.println(ch);
}
}
/*
需求:通过键盘录入数据,当录入一行后,就将该行进行打印。
如果录入的是数据是over 那么就停止录入
*/
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true){
int ch = in.read();
if(ch == '
'){
continue;
}if(ch == '
'){
String str = sb.toString();
if("over".equals(str)){
break;
}
System.out.println(str);
sb.delete(0, sb.length());
}else{
sb.append((char)ch);
}
}
/*读取转换流
是否可以直接食用readLine方法来完成键盘录入的一行数据的读取呢?
readLine 方法是字符流BufferdReader类中的方法,而键盘录入的read方法是字节流InputStream的方法。
那么是否可以将我们的字节流转换为字符流,再使用我们字符流缓冲区的readLine方法
*/
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
// 获取键盘录入对象
InputStream in = System.in;
// 将字节流对象转成字符流对象,使用转换流InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
// 为了提高效率,将字符流进行缓冲区技术的高效操作,使用BufferedReader
BufferedReader br = new BufferedReader(isr);
String str = null;
while((str = br.readLine()) != null){
System.out.println(str);
}
br.close();
}
输出转换流
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bw = new BufferedWriter(osw);
// 获取键盘录入对象
InputStream in = System.in;
// 将字节流对象转成字符流对象,使用转换流InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
// 为了提高效率,将字符流进行缓冲区技术的高效操作,使用BufferedReader
BufferedReader br = new BufferedReader(isr);
String str = null;
while((str = br.readLine()) != null){
// System.out.println(str);
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
流操作的基本规律:
流对象有很多,我们不知道该用哪一个。我们通过下面三个明确来确定
1、明确源和目的
源:输入流 InputStream Reader
目的:输出流 OutputStream Writer
2、操作的是否是纯文本
是:字符流
不是:字节流
3、当体系明确后,再明确要使用哪个具体对象
通过设备来进行区分
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
改变标准输入设备
package com.rf.IO;
import java.io.*;
public class InputStreamDemo {
public static void main(String[] args) throws IOException{
// System.setIn(new FileInputStream("/Users/chenbo/Documents/workspace/Study1/src/com/rf/IO/jjjjjjjj.java"));
System.setOut(new PrintStream("/Users/chenbo/Documents/workspace/Study1/src/com/rf/IO/jjjjjjjj1.txt"));
// 键盘最常见的写法
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/Users/chenbo/Documents/workspace/Study1/src/com/rf/IO/Input.java"),"UTF-8"));
String str = null;
while((str = br.readLine()) != null){
if("over".equals(str)){
break;
}
bw.write(str);
bw.newLine();
bw.flush();
}
br.close();
}
}
File类
用来将文件或者文件夹封装成对象
方便对文件与文件夹的属性信息进行操作
File对象可以作为参数传递给流的构造函数
了解File类中的常用方法
注意:流只能操作数据,我们想要操作被数据封装成文件到信息必须要操作File对象
在指定目录中创建文件
//将/Users/chenbo/Documents/workspace/Study1/该目录下的abc.txt
//封装成file对象,可以将已有的和未出现的文件或者文件夹封装成对象
File file1 = new File("/Users/chenbo/Documents/workspace/study2/src/com/rf/cdj/io/abc.txt");
File file2 = new File("/Users/chenbo/Documents/workspace/study2/src/com/rf/cdj/io","abc1.txt");
File file = new File("/Users/chenbo/Documents/workspace/study2/src/com/rf/cdj/io");
File file3 = new File(file,"abc2.txt");
System.out.println(file1);
System.out.println(file2);
System.out.println(file3);
// 上述打印都会打印出我们的绝对路径,因为我们封装的就是绝对路径,如果我们封装的是相对路径那么打印结果会是相对路径
// 其次我们要注意的是,在不同操作系统中,我们的分隔符写法会有所不同,java是跨平台的,在java中有提供分隔符separator属性
// 创建:
// createNewFile() 在指定位置创建文件,如果该文件已经存在,则不会再创建,返回false
// 和输出流不一样,输出流一建立对象就会创建文件对象,如果文件存在则会被覆盖
try {
file1.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
删除
// boolean delete() 删除成功返回true 删除失败返回false
file1.delete();
// void deleteOnExit() 在程序退出时删除指定对象
file.deleteOnExit();
判断文件是否存在
File file = new File("cba.txt");
System.out.println("文件是否存在:" + file.exists());
//判断是否是一个目录
System.out.println(file.isDirectory();
//判断是否是一个文件
System.out.println(file.isFile());
//注意:在判断是否是目录或者是文件时,必须要先判断该文件对象封装的内容是否存在。通过exists()判断
获取信息
file.getName();//获取名字
file.getPath();//获取路径
file.getParent();//获取父目录
file.getAbsolutePath();//获取绝对路径 返回是String
file.getAbsoluteFile();//获取绝对路径 返回的是File对象
文件重命名
File oldname = new File("copy1.txt");
File newname = new File("copy5.txt");
if(oldname.renameTo(newname)){
System.out.println("重命名成功!");
}else{
System.out.println("重命名失败!");
}
计算文件的大小
File file = new File("copy1.txt");
//file.exists()判断文件是否存在,有则返回true
//file.isFile()表示文件是否是一个标准文件
if (!file.exists() || !file.isFile()) {
System.out.println("文件不存在");
}
System.out.println(file.length());
设置文件为只读
File file = new File("copy5.txt");
//设置文件为只读
System.out.println("设置文件为只读是否成功:" + file.setReadOnly());
//判断文件是否可以写入
System.out.println("文件是否可以写入:" + file.canWrite());
文件修改时间
File file = new File("copy5.txt");
Date date = new Date(file.lastModified());
SimpleDateFormat sformat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("文件创建日期:" + sformat.format(date));
修改文件的最后修改日期
File file = new File("copy5.txt");
Date date = new Date(file.lastModified());
SimpleDateFormat sformat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("文件创建日期:" + sformat.format(date));
System.out.println(file.setLastModified(System.currentTimeMillis()));
Date updatetime = new Date(file.lastModified());
System.out.println("文件修改日期:" + sformat.format(updatetime));
文件目录比较
File file1 = new File("/Users/zhengbing/Desktop/java1701/2222/abc.txt");
File file2 = new File("/Users/zhengbing/Desktop/java1701/2222/abc.txt");
if(file1.compareTo(file2) == 0) {
System.out.println("文件路径一致!");
} else {
System.out.println("文件路径不一致!");
}
二、Java目录操作
public static void main(String[] args) {
// 递归创建目录
String path = "/Users/chenbo/Desktop/a/b/c/d/e/f/g";
addFile(path);
}
public static void addFile(String path){
File file = new File(path);
// mkdir 创建单个目录
if (file.mkdirs()) { // 创建多个目录
System.out.println("目录" + path + "创建成功");
} else {
System.out.println("目录" + path + "创建失败");
}
}
file.isDirectory() // 判断 file 是否是目录
file.list() // 获取目录目录下面的文件与文件夹
file.delete() // 目录 dir 必须要是空的,才能被删除
实现一个递归删除目录的方法
获取指定目录下的所有目录
public static void main(String[] args) {
printDirsStruct("/Users/chenbo/Desktop/Study1",1);
}
public static void printDirsStruct(String pathName,int index) {
File f = new File(pathName);
// 1 判断是不是目录
if (f.isDirectory()) {
for (int i = 0; i < index; i++) {
System.out.print("-");
}
// 2 如果是目录,打印自己
System.out.println(f.getName());
// 3 如果该目录里面还有子目录,就要调用自己打印该子目录。
for (String str : f.list()) {
String newPath = pathName + "/" + str;
File f1 = new File(newPath);
if (f1.isDirectory()) {
printDirsStruct(newPath, index + 1);
}
}
}
}
练习:打印该目录下的所有目录及目录里面的文件