IO
IO(Input/Output)是计算机输入/输出的接口。java的核心库java.io提供了全方面的IO接口,包括:文件系统的操作,文件读写,标准设备输出等等
File类及使用
File对象封装了文件或路径属性,但不包含对文件读写数据的方法
部分File类常用方法
public String getName() 获取文件或目录的名字
public String getParent() 获取父目录的路径字符串
public File getParentFile() 获取父目录路径的File对象
public String getAbsolutePath() 获取文件或目录的绝对路径
public boolean exists() 判断文件或目录是否存在
Public File[] listFiles() 获得目录里的文件和目录封装成File对象放到File数组里
public boolean isDirectory() 判断是不是文件夹或者目录
public boolean isFile() 判断是不是文件
public long length() 获取文件的大小
boolean delete() 删除文件或文件夹
boolean mkdir() 创建文件夹
package my;
import java.io.*;
public class IO {
public static void main(String[] args)
{
File file1 = new File("c:\\test");
System.out.println(file1.getAbsolutePath());
System.out.println(file1.getParent()); //test的父目录
System.out.println(file1.isDirectory()); //是否存在test文件夹
System.out.println(file1.isFile()); //是否存在test文件
System.out.println(file1.exists()); //是否存在
System.out.println(file1.length());
System.out.println(file1.delete());//存在直接删除test文件夹
//只能删除空文件夹,若里面有文件则需要递归删除
File file2 = new File("c:\\apple");
System.out.println("apple " + file2.mkdir());
//创建成功返回true,若原来有此文件夹则返回false
File file3 = new File("c:\\apple\\1.txt");
try {
System.out.println("1.txt " + file3.createNewFile());
//创建文件,成功true,原来有返回false
//使用creatNewFile的时候会抛出异常,需要捕获
}
catch(IOException e){
e.printStackTrace();
}
String[] files1 = file2.list();
for(String f:files1) {
System.out.println("files1 " + f);
}
//获取一个文件里面的所有文件名和目录名
//返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录
String[] files2 = file2.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
//dir : 被找到的文件所在的目录
//name:文件的名称
return name.endsWith(".txt");
}
});
for(String f:files2) {
System.out.println("files2 " + f);
}
//返回文件里面指定值的文件名和目录名
//返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录
File[] files3 = file2.listFiles();
for(File f:files3) {
System.out.println("files3 " + f.getName() + "--" + f.length());
}
//返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件
//包括一些文件属性:文件大小等
File[] files4 = file2.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".txt");
}
});
for(File f:files4) {
System.out.println("files4 " + f.getName() + "--" + f.length());
}
//返回一个抽象路径名数组,这些路径名表示此抽象路径名满足指定过滤器的文件和目录
//包括一些文件属性:文件大小等
File[] files5 = file2.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".txt");
}
});
for(File f:files5) {
System.out.println("files5: " + f.getName() + "--" + f.length());
}
//返回一个抽象路径名数组,这些路径名表示此抽象路径名满足指定过滤器的文件和目录
//包括一些文件属性:文件大小等
}
}
Console:
//c:\test
//c:\
//false
//false
//false
//0
//false
//apple false
//1.txt false
//files1 1.doc
//files1 1.mpp
//files1 1.txt
//files1 2.txt
//files2 1.txt
//files2 2.txt
//files3 1.doc--9216
//files3 1.mpp--178688
//files3 1.txt--0
//files3 2.txt--0
//files4 1.txt--0
//files4 2.txt--0
//files5: 1.txt--0
//files5: 2.txt--0
FileInputStream和FileOutputStream
FileInputStream类和FileOutputStream类用于从文件读取/写入字节,他们所有的方法都是从InputTream类和OutputStream类中继承的,没有引进新方法
I/O类中几乎所有的方法都会抛出异常IOExecption,所以必须在方法中声明抛出IOException异常,或者将代码放到tyr-catch块这种
对文件的读写操作使用缓冲区会大大提高文件读写效率
FileInputStream能从文件读取字节的InputStream类
FileOutputStream表示能向文件写入字节的OutputStream类
package my;
import java.io.*;
public class FileInputStreamOutputStream {
public static void main(String[] args) {
try {
FileCopyUtil.copyFile(new File("c:\\picture\\1.png"), new File("c:\\test\\1.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class FileCopyUtil{
public static void copyFile(File src, File dst) throws IOException{
FileInputStream fis = new FileInputStream(src); //从原文件读取文件
FileOutputStream fos = new FileOutputStream(dst); //写到指定文件当中
//直接从输入流中读到输出流中
long time1 = System.currentTimeMillis();
int data = -1;
while((data = fis.read()) != -1){
fos.write(data);
}
fis.close();
fos.close();
long time2 = System.currentTimeMillis();
//将输入流中的数据存到缓冲区中最后一起读到输出流
byte [] buf = new byte[1024*1024];//创建一个1M大小的缓冲区,用来存放输入流中的字节数
int len = 0;
long time1 = System.currentTimeMillis();
while((fis.read(buf)) != -1) {
fos.write(buf,0,len);
}
fis.close();
fos.close();
long time2 = System.currentTimeMillis();
System.out.println("finished : " + (time2 - time1));
}
}
ByteArrayInput/OutputStream
ByteArrayInputStream是把字节数组当成源的输入流
ByteArrayOutputStream是把字节数组当做目标的输出流
package my;
import java.io.*;
public class ByteArrayInputStreamByteArrayOutputStream {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//把字符串转换成字节数组,再从这个字节数组里读出来一个个打印输出
String str = "hello,world";
ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());
int data = -1;
while((data = bis.read()) != -1) {
System.out.print((char)data);
}
bis.close();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(97);
bos.write(65);
bos.write("hello".getBytes());
byte[] buff = bos.toByteArray();
for(byte data : buff) {
System.out.print((char)data);
}
FileOutputStream fos = new FileOutputStream("c:\\test\\1.txt", true);
//只会追加不会覆盖源文件内容
bos.writeTo(fos);
//把ByteArrayOutputStream内部缓冲区的数据写到对应的文件输出流中
fos.close();
}
}
FilterInputStream和FilterOutputStream(过滤流)
是为某种目的过滤字节的数据流,基本字节输入流提供的读取方法只能用来读取字节,如果要读取整数值、双精度值或字符串,需要一个过滤器类来包装字节输入流
常用的过滤流 BufferedInputStream和BufferedOutputStream, DataInputStream和DataOutputStream
DataInputStream从数据流中读取字节并转化为适当的基本类型值或字符串,DataOutputStream将基本类型的值或字符串转化为字节,并输出到输出数据流
BufferedInputStream和BufferedOutputStream可以通过减少读写次数来提高输入输出速度,BufferedOutputStream类为存储字节在流中添加一个缓冲区,以提高处理效率
BufferedInputStream类和BufferedOutputStream类
import java.io.*;
public class BufferedInputStreamOutputStream {
public static void main(String[] args) {
try {
FileUtil.copyFile(new File("c:\\picture\\1.png"), new File("c:\\test\\1.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class FileUtil{
public static void copyFile(File src, File dst) throws IOException{
FileInputStream fis = new FileInputStream(src); //从原文件读取文件
FileOutputStream fos = new FileOutputStream(dst); //写到指定文件当中
BufferedInputStream bis = new BufferedInputStream(fis);
//对fis这个文件增加缓冲区的功能
BufferedOutputStream bos = new BufferedOutputStream(fos);
int data = 0;
long time1 = System.currentTimeMillis();
while((data = bis.read()) != -1) {
bos.write(data);
}
//关闭包装类就可以
bis.close();
bos.close();
long time2 = System.currentTimeMillis();
System.out.println("finished : " + (time2 - time1) + "毫秒");
}
}
DataInputStream类和DataOuputStream类
package my;
import java.io.*;
public class DataInputStreamOutputStream {
public static void main(String[] args) throws IOException {
String name = "zhangsan";
int age = 10;
boolean flag = true;
char sex = 'm';
double money = 100.2;
//写入文件操作
DataOutputStream dos = new DataOutputStream(new FileOutputStream("c:\\test\\b.txt"));
//创建一个文件并将它实例化
dos.writeUTF(name);
dos.writeInt(age);
dos.writeBoolean(flag);
dos.writeChar(sex);
dos.writeDouble(money);
dos.close(); //关闭文件
//将文件信息读取出来
DataInputStream dis = new DataInputStream(new FileInputStream("c:\\test\\b.txt"));
//读的必须和写的顺序一致
System.out.println(dis.readUTF());
System.out.println(dis.readInt());
System.out.println(dis.readBoolean());
System.out.println(dis.readChar());
System.out.println(dis.readDouble());
dis.close();
}
}
BufferedInputStream和BufferedOutputStream:需要使用已经存在的节点流来构造,提供带缓冲的读写,提高了读写的效率
DataInputStream和DataOutputStream:数据输入输出流允许应用程序读写基本java数据类型。应用程序可以使用数据输出流写入稍后有数据输入流读取。读写顺序要保持与一致
装饰模式
装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展
装饰模式中的角色有
抽象构件(Component)角色:给出一个抽象接口,以规范准备接受附加责任的对象
具体构件(ConcreteComponent)角色:定义一个将要接受附加责任的类
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件方法一致的方法
具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任
Component为统一接口,也是装饰类和被装饰类的基本类型。
ConcreteComponent为具体实现类,也是被装饰类,他本身是个具有一些功能的完整的类。
Decorator是装饰类,实现了Component接口的同时还在内部维护了一个ConcreteComponent的实例,并可以通过构造函数初始化。而Decorator本身,通常采用默认实现,他的存在仅仅是一个声明:我要生产出一些用于装饰的子类了。而其子类才是赋有具体装饰效果的装饰产品类。
ConcreteDecorator是具体的装饰产品类,每一种装饰产品都具有特定的装饰效果。可以通过构造器声明装饰哪种类型的ConcreteComponent,从而对其进行装饰
public interface Component { //接口
public void doThingA();
}
public class Decorator implements Component {
private Component component = null;
public Decorator(Component component) {
this.component = component;
}
@Override
public void doThingA() {
// TODO Auto-generated method stub
component.doThingA();//调用被装饰对象的方法
}
}
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
// TODO Auto-generated constructor stub
}
@Override
public void doThingA() {
// TODO Auto-generated method stub
super.doThingA();//调用被包装类的方法
doThingB();
}
//扩展的功能
private void doThingB() {
System.out.println("do B thing");
}
}
public class ConcreteDecorator2 extends Decorator {
public ConcreteDecorator2(Component component) {
super(component);
// TODO Auto-generated constructor stub
}
@Override
public void doThingA() {
// TODO Auto-generated method stub
super.doThingA();//调用被包装类的方法
doThingC();
}
//扩展的功能
private void doThingC() {
System.out.println("do C thing");
}
}
public class Test {
public static void main(String[] args) {
ConcreteComponent component = new ConcreteComponent();
//component.doThingA();
ConcreteDecorator decorator1 = new ConcreteDecorator(component);
//decorator1.doThingA();
ConcreteDecorator2 decorator2 = new ConcreteDecorator2(decorator1);
decorator2.doThingA();
}
}
字符流
字节流提供处理任何类型输入/输出操作的足够功能,但不能直接操作Unicode字符,因而需要字符流
字符流层次结构额顶层是Reader和Writer抽象类
Reader是定义java的流式字符输入模式的抽象类
Writer是定义流式字符输出的抽象类,该类方法都返回void值并在出错条件下抛IOException
FileReader和FileWriter
FileReader类表示可以读取文件内容的Reader类
FileWriter表示可以写文件的Writer类
import java.io.*;
public class FileReaderWriter {
public static void main(String[] args) throws IOException {
//将一个文件的字符读取出来写到另一个文件里去
FileReader fr = new FileReader("c:\\test\\1.txt");
FileWriter fw = new FileWriter("c:\\test\\4.txt");
char [] buff = new char[100];
int len = 0; //实际读到的字符个数
while((len = fr.read(buff)) != -1) {
fw.write(buff, 0, len);
}
fr.close();
fw.close();
}
}
BufferedReader和BufferedWriter
BufferedReader通过缓冲输入提高性能
BufferedWriter通过缓冲输出提高性能
其他流
ObjectInputStream/OutputStream
InputStreamReader/OutputStreamWriter
RandomAccessFile
ObjectInputStream/OutputStream
ObjectInputStream和ObjectOutputStream分别与FileOutputStream和FileInputStream一起使用时,可以为应用程序提供对对象的持久存储。我们把对象以某种特定的编码格式写入称之为“序列化”。把写入的编码格式内容还原成对象称之为“反序列化”
被序列化的对象必须实现Serializable接口
ObjectOutputStream将java对象的基本数据类型和图形写入OutputStream
writeObject(Object obj):将指定的对象写入ObjectOutputStream
ObjectInputStream对以前使用ObjectOutputStream写入的基本数据和对象进行反序列化
readObject():从ObjectInputStream读取对象
package otherio;
import java.io.*;
public class ObjectInputOutStream {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
//向文件里输入对象,序列化
Student stu = new Student("zhangsan", 30);
//FileOutputStream fos = new FileOutputStream("c:\\test\\10.txt");
//ObjectOutputStream oos = new ObjectOutputStream(fos);
//与上面的等价
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c:\\test\\10.txt"));
oos.writeObject(stu);//把对象序列化到指定的文件输出流中
oos.close();//释放资源
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c:\\test\\10.txt"));
try {
Student stu = (Student)ois.readObject();
System.out.println(stu);
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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 toString() { //覆盖原有类里的toString方法
return "Student [name = " + name + ", age =" + age + "]";
}
}
eg:Student [name = zhangsan, age =30]
InputStreamReader/OutputStreamWriter
转换流是指将字节流与字符流之间的转换
转换流的出现方便了对文件的读写, 它在字符流与字节流之间架起了一座桥梁,使原本毫无关联的两种流操作能够进行转化,提高了程序的灵活性
字节流中的数据都是字符时,转成字符流操作更高效
如果使用非默认编码保存文件或者读取文件时,需要用到转换流,因为字节流的重载构造方法中有指定编码格式的参数,而FileReader与FileWriter是默认编码的文本文件
常见的编码表
ASCII:美国标准信息交换码,用一个字节的7位可以表示
ISO8859-1:拉丁码表,欧洲码,表示用一个字节的8位表示
GB2312:中国的中文编码表
GBK:中国的中文编码表升级
Unicode:国际标准码,融合了多种文字,所有文字都用两个字节来表示
UTF-8:最多用三个字节来表示一个字符
InputStreamReader是字节流通向字符流的桥梁
OutputSteamWriter是字符流通向字节流的桥梁
package otherio;
import java.io.*;
public class InputStreamWriterReader {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
//写入文件操作
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("c:\\test\\10a.txt"), "utf-8");
BufferedWriter bw = new BufferedWriter(osw);//加了缓冲区
bw.write("你好");
bw.close();
//从文件读取操作,运用缓冲区
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("c:\\test\\10a.txt"),"utf-8"));
String line = null;
while((line = br.readLine())!= null) {
System.out.println(line);
}
}
}
eg:你好
RandomAccesFile(随机访问文件)
支持对随机访问文件的读取和写入
随机访问文件的行为类似存储在文件系统中的一个大型byte数组存在指向该隐含数组的光标或索引,称为文件指针
输入操作从文件指针开始读取字节,随着对字节的读取而迁移此文件指针
如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,随着对字节的写入而前移此文件指针
写入隐含数组末尾之后的输出操作导致该数组扩展
该文件指针可以通过getFilePointer方法读取,通过seek方法设置
package otherio;
import java.io.*;
import java.util.Scanner;
public class RandomAccessFileio {
public static void main(String[] args) throws IOException{
Person[] persons = {new Person("lisi",90), new Person("justin",24), new Person("zhangsan",18), new Person("wangwu", 39)};
//初始化对象
java.io.RandomAccessFile raf = new java.io.RandomAccessFile("c:\\test\\io.txt", "rw");
//写入数据到RandomAccessFile对象中去
for(int i = 0; i < persons.length; i++) {
raf.writeChars(persons[i].getName());
raf.writeInt(persons[i].getAge());
}
//读取指定位置上的的Person对象
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt(); //输入一个数,取第num个人的姓名和年龄
//使用seek方法来操作存取位置,从第0个开始,所以要seek-1
raf.seek((num-1) * Person.size());
Person person = new Person();
person.setName(readName(raf));
person.setAge(raf.readInt());
System.out.println("姓名" + person.getName());
System.out.println("年龄" + person.getAge());
raf.close();
}
private static String readName(RandomAccessFile raf) throws IOException {
char[] name = new char[15];
for(int i = 0; i < name.length; i++) {
name[i] = raf.readChar();
}
return new String(name).replace('\u0000', ' ');
}
}
class Person{
private String name; //2个字节,unicode编码,将名字长度固定为15个字符
private int age; //4个字节
//person一共有34个字节
public Person() {
}
public Person(String name, int age) {
StringBuilder builder = null;
//动态字符串,Stringbuilder默认大小:传进去字符串+16
if(name != null) {
builder = new StringBuilder(name); //传过来名字不为空,就等于其名字
}else {
builder = new StringBuilder(15); //为空就等于15
}
builder.setLength(15);//设置固定长度为15个,空字符'\u0000'
this.name = builder.toString() ;
this.age = age;
}
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 static int size() {
return 34;
}
}