1.流
1.1文件
1.1.1定义
相关记录或放在一起的数据的集合
1.1.2存储的位置
硬盘
1.1.3持久化
把数据存入硬盘这个行为,我们把他称之为持久化,而程序的执行,是在内存当中完成的,需要把数据从硬盘读到内存当中,再来执行
1.1.4文件的分类
① 目录:目录的本质上也是一种文件
② 文件:文件是用来保存数据的主体,但是没有谁规定,文件一定要有后缀。
//File是用来描述文件的一种对象
File f = new File("D:\\斌斌和阿玲不得不说的故事.txt");
System.out.println(f.exists());//文件不存在则返回false
System.out.println(f.isFile());//true
System.out.println(f.isDirectory());//false
System.out.println("-----------------------------");
File f1 = new File("D:\\ioTestDir");
System.out.println(f1.exists());//文件存在的时候,返回true
System.out.println(f1.isFile());//false
System.out.println(f1.isDirectory());//true
1.1.5路径的正确表达方式:
① \:这种斜杠需要转义\
② /:这个可以直接用
1.1.6 File:new出来的对象
只是一个对象,可以new,但是并不意味着一定存在
方法名称 | 说明 |
---|---|
boolean exists( ) | 判断文件或目录是否存在 |
boolean isFile( ) | 判断是否是文件 |
boolean isDirectory( ) | 判断是否是目录 |
String getPath( ) | 返回此对象表示的文件的相对路径名 |
String getAbsolutePath( ) | 返回此对象表示的文件的绝对路径名 |
String getName( ) | 返回此对象表示的文件或目录的名称 |
boolean delete( ) | 删除此对象指定的文件或目录 |
boolean createNewFile( ) | 创建名称的空文件,不创建文件夹 |
long length() | 返回文件的长度,单位为字节,如果文件不存在,则返回0L |
1.1.7路径操作
① 绝对路径:从根目录往后找,叫绝对路径
② 相对路径:以当前文件所在的位置,作为参照,开始寻找目标
③ getPath:你给他相对路径,他就是相对路径,你给他绝对路径,那就是绝对路径
④ getAbsolutePath:获取的是绝对路径,但是,是识别不了.和…/的
⑤ getCanonicalPath:效果和getAbsolutePath类似,但是可以识别.和…/的
File f = new File("../Test1.class");
File f1 = new File("D:\\ioTestDir");
//System.out.println(f.exists());
System.out.println(f.getPath());
System.out.println(f1.getPath());
System.out.println("---------------");
System.out.println(f.getAbsolutePath());
System.out.println(new File(f.getAbsolutePath()).exists());
System.out.println("----------------");
System.out.println(f.getCanonicalPath());
1.1.8创建文件和目录
File f = new File("D:\\ioTestDir\\斌斌的故事");
f = new File(f.getCanonicalPath());
System.out.println(f);
if(!f.exists()){
boolean r = f.createNewFile();
System.out.println(r?"文件创建成功":"文件创建失败");
}
public static void main(String[] args) throws Exception {
// File f = new File("D:\\ioTestDir\\childDir");
File f = new File("D:\\ioTestDir\\childDir\\c1\\c2\\c1");
if(!f.exists()){
// boolean r = f.mkdir();//只会创建当前目录,如果父目录不存在,则报错
boolean r = f.mkdirs();//会创建当前目录,如果父目录不存在,则一并创建
System.out.println(r?"success":"error");
}
}
1.1.9获取文件和目录
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
if(f.exists()){
long length = f.length();//获取文件的字节数
System.out.println(length);
}
File f = new File("D:\\ioTestDir");
//获取的是儿子辈
String[] list = f.list();//获取所有子文件和子目录的绝对路径名
File[] files = f.listFiles();//获取所有的子文件和子目录
//获取父亲辈
String parent = f.getParent();//获取父目录的绝对路径
File parentFile = f.getParentFile();//获取父路径的文件对象
1.2文件的读写
1.2.1如何读写文件?
通过流来读写文件:
① 流是一组有序的数据序列
② 以先进先出方式发送信息的通道
1.2.2流的分类
1.2.2.1按流向区分:输入流/输出流(I/O)
① Input:以当前程序为参照,从程序外往程序里读叫输入流,Iutputstream和Reader作为基类
② Output:以当前程序为参照,往外写叫输出流,OutputStream和Writer作为基类
③输入输出流是相对于计算机内存来说的
1.2.2.2按照处理数据单元划分
1.2.2.2.1字节流:读写的是byte
字节输入流InputStream基类:必须掌握
① 可以一个字节一个字节的读
② 读的字节,不会超过byte的长度
③ 读字节对文件大小没有限制
InputStream类常用方法
- int read( )
- int read(byte[] b)
- int read(byte[] b,int off,int len)
- void close( )
- int available():可以从输入流中读取的字节数目
子类FileInputStream常用的构造方法
- FileInputStream(File file)
- FileInputStream(String name)
使用FileInputStream 读文本文件
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
InputStream in = null;
try{
if(!f.exists()){
throw new Exception("文件不存在");
}
in = new FileInputStream(f);
//the next byte of data, or -1 if the end of thestream is reached.
byte[] bs = new byte[in.available()];//获取当前流当中,该文件可读字节数
// in.read(bs);//通过对地址的访问,直接把读取的字节码保存到指定数组中
byte b = -1;
int index = 0;
while((b = (byte)in.read()) != -1){
bs[index++] = b;
}
String str = new String(bs);
System.out.println(str);
}catch(Exception e){
e.printStackTrace();
}finally{
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
字节输出流OutStream基类:必须掌握
① 可以一个字节一个字节的往外写
② 需要首先获取数据的字节码
③ 输出流是覆盖,不是追加
OutputStream类常用方法
- void write(int c)
- void write(byte[] buf)
- void write(byte[] b,int off,int len)
- void close()
- void flush():强制把缓冲区的数据写到输出流中
子类FileOutputStream常用的构造方法
- FileOutputStream (File file)
- FileOutputStream(String name)
- FileOutputStream(String name,boolean append)
- 注意:1、前两种构造方法在向文件写数据时将覆盖文件中原有的内容。2、创建FileOutputStream实例时,如果相应的文件并不存在,则会自动创建一个空的文件
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
String str = "第一章\r\n那一天,他和她……\r\n自己脑补";
//输出流
OutputStream out = null;
try {
if(!f.exists()){
f.createNewFile();
}
out = new FileOutputStream(f);
byte[] bs = str.getBytes();
// for(byte b : bs){
// out.write(b);
// }
out.write(bs);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(out != null)
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
1.2.2.2.2字符流:读写的是char
字符输入流Reader基类
- 字符流读取,不可以直接使用inputstring的读取长度,因为我们读取的是char类型
Reader类常用方法
- int read( )
- int read(char[] c)
- read(char[] c,int off,int len)
- void close( )
子类InputStreamReader常用的构造方法
- InputStreamReader(InputStream in)
- InputStreamReader(InputStream in,String charsetName)
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
Reader in = null;
try{
if(!f.exists()){
throw new Exception("文件不存在");
}
in = new InputStreamReader(new FileInputStream(f));
StringBuffer sbf = new StringBuffer();
int item = -1;
while((item = in.read()) != -1){
char c = (char)item;
sbf.append(c);
}
System.out.println(sbf.toString());
}catch(Exception e){
e.printStackTrace();
}finally{
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
FileReader类是InputStreamReader的子类
- FileReader(File file)
- FileReader(String name)
- 该类只能按照本地平台的字符编码来读取数据,用户不能指定其他的字符编码类型
- System.out.println(System.getProperty(“file.encoding”)); 获得本地平台的字符编码类型
字符输出流Writer基类
- 一个字符有若干字节组成,所以自带缓冲区,需要调用flush
字节流是 8 位通用字节流,字符流是 16 位 Unicode 字符流
Writer类常用方法
- write(String str)
- write(String str,int off,int len)
- void close()
- void flush()
子类OutputStreamWriter常用的构造方法
- OutputStreamWriter(OutputStream out)
- OutputStreamWriter(OutputStream out,String charsetName)
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
String str = "第一章\r\n那一天,他和她……\r\n自己脑补";
Writer out = null;
//输出流
try {
if(!f.exists()){
f.createNewFile();
}
out = new OutputStreamWriter(new FileOutputStream(f));
// for(char c : str.toCharArray()){
// out.write(c);
// }
out.write(str);
out.flush();//字符流的输出流,自带缓冲区
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(out != null)
try {
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
FileWriter类是OutputStreamWriter的子类
- FileWriter (File file)
- FileWriter (String name)
- 该类只能按照本地平台的字符编码来写数据,用户不能指定其他的字符编码类型
1.2.2.1缓冲流:带缓冲的流
1.2.2.1.1字节流的缓冲流
① 默认缓冲区大小:8192B
② 只要是缓冲流,必须调用flush,一般来说针对输出流
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
InputStream in = null;
try{
if(!f.exists()){
throw new Exception("文件不存在");
}
in = new BufferedInputStream(new FileInputStream(f)); //缓冲输入字节流
//the next byte of data, or -1 if the end of thestream is reached.
byte[] bs = new byte[in.available()];//获取当前流当中,该文件可读字节数
// in.read(bs);//通过对地址的访问,直接把读取的字节码保存到指定数组中
byte b = -1;
int index = 0;
while((b = (byte)in.read()) != -1){
bs[index++] = b;
}
String str = new String(bs);
System.out.println(str);
}catch(Exception e){
e.printStackTrace();
}finally{
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
String str = "第一章\r\n那一天,他和她……\r\n自己脑补";
//输出流
OutputStream out = null;
try {
if(!f.exists()){
f.createNewFile();
}
out = new BufferedOutputStream(new FileOutputStream(f));//缓冲输出字节流
byte[] bs = str.getBytes();
out.write(bs);
out.flush();//清空缓冲区
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(out != null)
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
1.2.2.1.2缓冲字符流
- 输出流:因为有非常优秀的缓冲取,所以不需要考虑一个字符一个字符往外写,直接写,交给缓冲区处理,缓冲区大小8192个char(int)
如何提高字符流写文本文件的效率?
- 使用FileWriter类与BufferedWriter类
BufferedWriter常用的构造方法
- BufferedWriter(Writer out)
使用 BufferedWriter 写文件
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
String str = "第一章\r\n那一天,他和她……\r\n自己脑补";
BufferedWriter out = null;
//输出流
try {
if(!f.exists()){
f.createNewFile();
}
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
out.write(str);
out.flush();//字符流的输出流,自带缓冲区
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(out != null)
try {
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 输入流:可以一行一行的读
如何提高字符流写文本文件的效率?
- 使用FileReader类与BufferedReader类
BufferedReader常用的构造方法
- BufferedReader(Reader in)
- 子类BufferedReader特有的方法
- readLine()
使用 BufferedReader读文本文件
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\斌斌和阿玲不得不说的故事.txt");
BufferedReader in = null;
try{
if(!f.exists()){
throw new Exception("文件不存在");
}
in = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
String line = "";
while((line = in.readLine()) != null){
System.out.println(line);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
1.2.2.4二进制流
① 作用,是读写数据
② 读和写的顺序需要保持一致
DataInputStream类
- FileInputStream的子类
- 与FileInputStream类结合使用读取二进制文件
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\objtest.obj");
DataInputStream in = null;
try{
in = new DataInputStream(new FileInputStream(f));
int id = in.readInt();
System.out.println(id);
String name = in.readUTF();
System.out.println(name);
int age = in.readInt();
System.out.println(age);
}catch(Exception e){
e.printStackTrace();
}finally{
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
DataOutputStream类
- FileOutputStream的子类
- 与FileOutputStream类结合使用写二进制文件
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\objtest.obj");
DataOutputStream out = null;
try{
if(!f.exists()){
f.createNewFile();
}
out = new DataOutputStream(new FileOutputStream(f));
out.writeInt(1);
out.writeUTF("斌斌");
out.writeInt(18);
out.flush();
}catch(Exception e){
e.printStackTrace();
}finally{
if(out != null){
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
1.2.2.5序列化和反序列化
① 序列化:把内存中的对象,变为二进制的字节流输出
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1724067472155516781L;
private Integer id;
private String name;
private Integer age;
private Dog dog;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\objtest2.obj");
Person p = new Person();
p.setId(1);
p.setName("ybb");
p.setAge(18);
p.setDog(new Dog());
//序列化
ObjectOutputStream out = null;
try{
if(!f.exists()){
f.createNewFile();
}
out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(p);
out.flush();
}catch(Exception e){
e.printStackTrace();
}finally{
if(out != null){
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
② 反序列化:把二进制流的输入,变为对象
public static void main(String[] args) {
File f = new File("D:\\ioTestDir\\objtest2.obj");
//反序列化
ObjectInputStream in = null;
try{
in = new ObjectInputStream(new FileInputStream(f));
Object obj = in.readObject();
if(obj instanceof Person){
Person p = (Person)obj;
System.out.println(p);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(in != null){
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
③ 序列化的第一步,是需要制定对象为可序列化对象,以及该类的所有类成员都必须为可序列化对象
④ 序列化的版本ID,在序列化和反序列化时必须保持一致
⑤JDK自带的序列化和反序列化,效果不佳,在真正的大型分布式项目当中,一般使用例如:hessian(jboss)
3.1输入流的本质与缓冲区
3.1.1 I/O流的体系
3.1.2输入流
public class Test1 {
public static void main(String[] args) {
//输入流
//字节流
InputStream in1;
FileInputStream in2;
FilterInputStream in1;
ByteArrayInputStream in4;
BufferedInputStream in5;//缓冲
DataInputStream in6;//读写的数据是固定长度
//字符流
Reader r1;//本质上读取的就是一个char数组
InputStreamReader r2;//这个类,只是在负责字符集的解码工作
// StreamDecoder r1;//字符集的解码工作具体是由这个类来完成的
// new InputStreamReader(new FileInputStream(new File("")),"utf-8");//字符集的解码工作,因为在输出流输出时是已该编码完成的
BufferedReader r1;//缓冲
}
}
//这个是可以扩展的,不存在读取的上限,当缓冲区用完了,会扩展一倍
class MyBufferedInputStream extends MyFilterInputStream{
private static int DEFAULT_BUFFER_SIZE = 8192;//缓冲大小
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;//最大缓冲区大小
protected byte buf[];
protected int count;//上限
protected int pos;
public MyBufferedInputStream(MyInputStream in) {//默认长度
this(in,DEFAULT_BUFFER_SIZE);
}
public MyBufferedInputStream(MyInputStream in , int size) {//指定长度的
super(in);
buf = new byte[size];
}
public synchronized int read() throws IOException {
if (pos >= count) {
fill();//扩展缓冲
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++];
}
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
return buffer;
}
private void fill() throws IOException {
//检查还有多少没读
//1.读完了
//2.没读完
//剩下来的要么扩展一倍,要么就是MAX_BUFFER_SIZE
// pos = 0;
//newSize
int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE;
byte nbuf[] = new byte[nsz];
count = nbuf.length - pos;
}
public synchronized int available() throws IOException {
int n = count - pos;
return n;
}
public void close() throws IOException {
MyInputStream input = super.in;
if (input != null)
input.close();
return;
}
}
//字节数组输入流:这是一个线程安全的字节数组输入流
class MyByteArrayInputStream extends MyInputStream{
protected byte buf[];
protected int pos;
protected int count;
public MyByteArrayInputStream(byte[] buf){
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
public synchronized int read() throws IOException {
// TODO Auto-generated method stub
return buf[pos++];
}
public synchronized int available() {
return count - pos;
}
public void close() throws IOException {
}
}
//其实啥也没干
class MyFilterInputStream extends MyInputStream{
protected MyInputStream in;
public MyFilterInputStream(MyInputStream in){
this.in = in;
}
public int read() throws IOException {
return in.read();
}
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
public int available() throws IOException {
return in.available();
}
public void close() throws IOException {
in.close();
}
}
//file的读取,是有本地调用来实现的
class MyFileInputStream extends MyInputStream{
@Override
public int read() throws IOException {
// TODO Auto-generated method stub
return read0();
}
//本地调用:这个代码是不开源的
private native int read0() throws IOException;
//本地调用
public native int available() throws IOException;
//重写了close
private FileChannel channel = null;
public void close() throws IOException {
if (channel != null) {
channel.close();
}
}
}
abstract class MyInputStream{
//在于该方法到底如何读取数据的
public abstract int read() throws IOException;//这是个抽象方法
public int read(byte b[]) throws IOException {//从偏移量0开始,读所有
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
//读第一个
int c = -1;
for (int i= 0 ; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
return -1;
}
public int available() throws IOException {
return 0;
}
public void close() throws IOException {
}
}
3.1.3输出流
public class Test2 {
public static void main(String[] args) {
//输出流
//字节流
OutputStream out1;
FileOutputStream out2;
FilterOutputStream out1;
ByteArrayOutputStream out4;
BufferedOutputStream out5;//缓冲
DataOutputStream out6;
//字符流
Writer w1;
OutputStreamWriter w2;//主要任务就是负责字符集的编码工作
//StreamEncoder:实际完成编码,
BufferedWriter w1;//缓冲
}
}
//完整的执行过程是:
//缓冲始终没有大于过8192的时候,这个时候,是没有真正往外写的
//往外写的两种情况:
//1.手动调用了flush
//2.缓冲满了一次,满的这一次数组里的东西往外写,但是最后剩余的部分,如果不调用flush,则会丢失
class MyBufferedOutputStream extends MyFilterOutputStream{
protected byte buf[];
protected int count;
public MyBufferedOutputStream(MyOutputStream out ){
this(out,8192);
}
public MyBufferedOutputStream(MyOutputStream out , int size){
super(out);
buf = new byte[size];
count = buf.length;
}
//所谓的写,是在往buf数组当中添加内容
//写完了缓冲的长度以后, flushBuffer();
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
//真正开始写
private void flushBuffer() throws IOException {
if (count > 0) {
super.out.write(buf, 0, count);
count = 0;
}
}
public synchronized void flush() throws IOException {
flushBuffer();
out.flush();
}
}
class MyFilterOutputStream extends MyOutputStream{
protected MyOutputStream out;
public MyFilterOutputStream(MyOutputStream out){
this.out = out;
}
@Override
public void write(int b) throws IOException {
// TODO Auto-generated method stub
}
}
//是一个可以扩容的数组,长度默认是12
//没有实现flush,就意味着当扩容后的内容如果不足12,那么就以12的倍数算
class MyByteArrayOutputStream extends MyOutputStream{
protected byte buf[];
protected int count;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
public MyByteArrayOutputStream(){
this(12);
}
public MyByteArrayOutputStream(int size){
buf = new byte[size];
}
@Override
public void write(int b) throws IOException {
// TODO Auto-generated method stub
buf[count++] = (byte) b;
}
//没有实现flush
}
//具体调用不可见
class MyFileOutputStream extends MyOutputStream{
private native void write(int b, boolean append) throws IOException;
@Override
public void write(int b) throws IOException {
write(b, false);
}
//没有实现flush
}
//输出流,write(int b)方法的实现
//flush:在这里没有起到任何作用
abstract class MyOutputStream {
public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
public void flush() throws IOException {
}
public void close() throws IOException {
}
}
3.1.4提醒
①读源码,只是为了帮助我们更好的了解这个java API的用法和特质,最主要的目的是我们以后的API会越来越多
学会翻阅API,如果不想看API的文档,可以适当阅读源码。看源码看的是逻辑,和特性,不是要你背上。
②API手册上的东西,主要是JDK里的内容
③JDK本身的流,其实不完美,性能不高/如果对流的性能有要求(NIO/BIO)