IO(Input Output)流概述:
IO流特点:
- IO流(数据流)用来处理设备间的数据传输。
- Java对数据的操作是通过流的方式。
- Java用于操作流的对象都在IO包中。
- 流按操作数据分为两种:字节流和字符流。
- 流按流向分为:输入流和输出流。
IO流的抽象基流:
字节流的抽象基流:InputStream和OutputStream
字符流的抽象基流:Reader和Writer
如InputStream子类FileInputStream
字符流:
既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。
/*
需求:在硬盘上,创建一个文件,并写入文字数据。
找到一个专门用于操作文件的Writer子类对象。FileWriter 后缀名是父类名,前缀名是该流对象的功能
*/
import java.io.*;
class FileWriteDemo
{
public static void main(String []args) throws IOException
{
//创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件
//而且该文件会被创建到指定的目录下。如果该目录下已有同名文件,将被覆盖
//其实该步就是在明确数据要存放的目的地。
FileWriter fw =new FileWriter("C:/demo.txt");
fw.write("abcde");
//刷新流对象中缓冲中的数据
//将数据刷到目的地中
fw.flush();
//关闭流资源,但是关闭之前会刷新一次缓冲中的数据
//讲数据刷到目的地中,flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
}
}
IO异常的处理方式:
import java.io.*;
class FileWriteDemo2
{
public static void main(String []args)
{
//在外面建立引用,在try内初始化
FileWriter fw=null;
try{
//try里面定义的变量在外面访问不到
//FileWriter fw=new FileWriter("demo.txt");
//向磁盘中写入文件
fw=new FileWriter("c:\\demo.txt");
//向文件中写入内容
fw.write("abcdefg");
}catch(IOException e)
{
System.out.println("catch:"+e.toString());
//finally内关闭资源,一定会被执行的代码往里面放入
}finally{
try{
// 对流对象进行判断,如果不为空就关闭,为空时不能调用close()
if(fw!=null)
{
fw.close(); //流必须关闭,因此写入到finally中
}
}catch(IOException e)
{
System.out.println(e.toString());
}
}
}
}
文件的续写:
//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据的续写
fw=new FileWriter("c:\\demo.txt",true);
文本文件读取方式1:
/*第一种方式:一次读取一个字符*/import java.io.*;
class FileReaderDemo
{
public static void main(String []args) throws IOException
{
//创建一个文件读流对象,和指定名称的文件相关联
//要保证该文件时已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr=new FileReader("C:/demo.txt");
//调用读取流对象的read方法
//raed()方法一次读入一个字符,而且会自动往下读。
/*
int ch=fr.read();
System.out.println("ch="+(char)ch);
int ch1=fr.read();
System.out.println("ch="+(char)ch1);
*/
//read方法作为整数读取的字符,范围在0到65535之间,如果达到流的末尾,则返回-1
/*
while(true)
{
int ch=fr.read();
if(ch==-1)
break;
System.out.println("ch="+(char)ch);
}
fr.close();
*/
int ch=0;
while((ch=fr.read())!=-1)
{
System.out.println("ch="+(char)ch);
}
}
}
文本文件读取方式2:
/*
第二种方式,通过字符数组进行读取。
这种读取方式最有效
*/
import java.io.*;
class FileReaderDemo2
{
public static void main(String []args) throws IOException
{
FileReader fr=new FileReader("C:/demo.txt");
//定义一个字符数组。用于存储读到字符
//该read(char[])返回的是读到的字符个数
char [] buf=new char[1024];
/*
int num=fr.read(buf);
System.out.println("num="+num+"..."+new String(buf));
int num1=fr.read(buf);
System.out.println("num1="+num1+"..."+new String(buf));
int num2=fr.read(buf);
System.out.println("num2="+num2+"..."+new String(buf,0,1));
*/
int num=0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num));
}
fr.close();
}
}
拷贝文本文件:
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
import java.io.*;
class CopyText
{
public static void main(String[] args) throws IOException
{
copy_2();
}
public static void copy_2()
{
FileWriter fw = null;
FileReader fr = null;
try
{
fw = new FileWriter("SystemDemo_copy.txt");
fr = new FileReader("SystemDemo.java");
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
if(fr!=null)
try
{
fr.close();
}
catch (IOException e)
{
}
if(fw!=null)
try
{
fw.close();
}
catch (IOException e)
{
}
}
}
//从C盘读一个字符,就往D盘写一个字符。
public static void copy_1()throws IOException
{
//创建目的地。
FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
//与已有文件关联。
FileReader fr = new FileReader("RuntimeDemo.java");
int ch = 0;
while((ch=fr.read())!=-1)
{
fw.write(ch);
}
fw.close();
fr.close();
}
}
BufferedWriter和BufferedReader:字符流缓冲区
- 缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象
- 该缓冲区中提供了一个跨平台的换行符:newLine()。
- 该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,
//bufw.write("abcde");
//bufw .write("\r\n");
//LineNumberReader继承BufferedReader是带行号的缓冲区
/*
字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
*/
import java.io.*;
class BufferedReaderDemo
{
public static void main(String []args) throws IOException
{
//创建一个读取流对象和文件相关联
FileReader fr=new FileReader("buf.txt");
//为了提高效率,加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数
BufferedReader bufr=new BufferedReader(fr);
String line=null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
/*
String s1=bufr.readLine();
System.out.println("s1:"+s1);
String s2=bufr.readLine();
System.out.println("s2:"+s2);
*/
bufr.close();
}
}
自定义BufferedReader
/*
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readline一致的方法
来模拟一下BufferedReader
*/
import java.io.*;
class MyBufferedReader extends Reader
{
//装饰设计模式
private Reader r=null;
MyBufferedReader(Reader r)
{
this.r=r;
}
//可以一次读一行数据的方法
public String myReadLine() throws IOException
{
//定义一个临时容器,员BufferedReader封装的是字符数组
//为了演示方便,定义一个StringBulilder容器,因为最终还是要将数据变成字符串。
StringBuilder sb=new StringBuilder();
int ch=0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
//如果没有遇到\n,sb中而且有数据的话也要返回
if(sb.length()!=0)
return sb.toString();
return null;
}
/*
覆盖类中的抽象方法
*/
public int read(char[] cbuf,int off,int len)throws IOException
{
return r.read(cbuf,off,len);
}
public void close() throws IOException
{
r.close();
}
public void myClose() throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("buf.txt");
MyBufferedReader myBuf=new MyBufferedReader(fr);
String line=null;
while((line=myBuf.myReadLine())!=null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
装饰设计模式:- 当想要对已有的对象进行功能增强时,
- 可以定义一个类,将已有对象传入,基于已有的功能,并提供加强功能
- 那么自定义的该类称为装饰类
- 装饰类通常会通过构造方法接受被装饰的对象
- 并基于被装饰的对象的功能,提供更详细的功能。
装饰和继承的区别:
1·装饰模式比继承要灵活,通过避免了继承体系的臃肿,且降低了类与类间的关系。
2·装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3·从继承结构转为组合结构。
class Person
{
public void chifan()
{
System.out.println("吃饭");
}
}
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
public void superChifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
System.out.println("吸根烟");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p=new Person();
// p.chifan();
SuperPerson sp=new SuperPerson(p);
sp.superChifan();
}
}
字节流:
由于媒体数据中都是以字节存储的,所以,字节流对象可直接对媒体进行操作,可以不用再进行刷新流的动作。
不用进行刷新流的动作的原因:
因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。
int available() ---> 放回数据字节的长度,包含终止符
在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()] fos为字节流对象)
但是,对于这个方法要慎用,如果字节过大,比如一部电影(几个G),那么如此大的数组就会损坏内存,超过jvm所承受的大小(指定内存为64M)。
import java.io.*;
class FileStream{
public static void main(String []args) throws IOException
{
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
public static void readFile_1() throws IOException
{
FileInputStream fis=new FileInputStream("fos.txt");
int ch=0;
while((ch=fis.read())!=-1)
{
System.out.println((char)ch);
}
fis.close();
}
public static void readFile_2() throws IOException
{
FileInputStream fis=new FileInputStream("fos.txt");
//以这种方式为主,这是最好,最优化的方式
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
fis.close();
}
public static void readFile_3() throws IOException
{
FileInputStream fis=new FileInputStream("fos.txt");
// int num=fis.available();
//这种方式要慎用,数据不是太大的话,可以这样使用
byte[] buf=new byte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了
fis.read(buf);
// System.out.println("num="+num);
System.out.println(new String(buf));
fis.close();
}
public static void writeFile() throws IOException
{
FileOutputStream fos=new FileOutputStream("fos.txt");
//将字符串变为字节数组
fos.write("abcde".getBytes());
fos.close();
}
}
字节流缓冲区:
read():会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
/*
通过缓冲区,演示Mp3的复制
BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3{
public static void main(String []args) throws IOException
{
long start=System.currentTimeMillis();
//copy_1();
copy_2();
long end=System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
//通过字节流的缓冲区,完成复制
public static void copy_1() throws IOException
{
//缓冲区里已经定义了数组
BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("C:\\0.mp3"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("C:\\1.mp3"));
int by=0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
public static void copy_2() throws IOException
{
MyBufferedInputStream bufis=new MyBufferedInputStream(new FileInputStream("C:\\0.mp3"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("C:\\2.mp3"));
int by=0;
//读了一次没有写出去
// System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.myClose();
}
}
自定义字节流缓冲区:
- 取出的是byte型,返回的是int型,这里存在提升的动作,
- 当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,,read循环条件就结束了
/*
11111111 -->提升了一个int类型,那不还是-1,吗?是-1的原因是因为在8个1的前面补得的是1导致的
那么我只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现,那么怎,补0呢
11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
-------------------------------------
00000000 00000000 00000000 11111111
这样就可以避免-1的发生
byte:-1 ---->int:-1
read方法在提升,write方法将指定的字节写入此缓冲的输出流(强制转换,取最低的8位)
*/
import java.io.*;
class MyBufferedInputStream{
private InputStream in;
private byte[] buf=new byte[1024*4];
private int pos=0;
private int count=0;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
//一次读一个字节,从缓冲区(字节数组)获取
public int myRead() throws IOException
{
//通过in对象,读取硬盘上的数据,并存储到buf中
if(count==0)
{
count= in.read(buf);
if(count<0)
return -1;
pos = 0;
//取数组第一个元素pos=0;
byte b=buf[pos];
count--;
pos++;
//b与255进行与操作,用十六进制表示就是0xff
return b&255;
}else if(count>0)
{
byte b=buf[pos];
count--;
pos++;
//b与255进行与操作,用十六进制表示就是0xff
return b&255;
}
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
读取键盘录入
/*
需求:
通过键盘录入数据,当录入一行数据后就进行打印。
如果当录入的数据时over,那么停止录入
Dos下键盘录入操作终止可以使用Ctrl+C操作
*/
import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in=System.in;
StringBuilder sb=new StringBuilder();
/*
int ch=0;
while((ch=in.read())!=-1)
{
System.out.println(ch);
}
*/
while(true)
{
int ch=in.read();
if(ch=='\r')
continue;
if(ch=='\n')
{
String s=sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
//sb=new StringBuilder();
sb.delete(0,sb.length()); //清除缓冲区里的内容,不然录入的数据会一直在缓冲区中,结束标记失效。
}
else
sb.append((char)ch);
}
in.close();
//数据类型转换
System.out.println('\r'+0);
System.out.println('\n'+0);
/*
int by=in.read();
int by1=in.read();
int by2=in.read();
System.out.println(by);
System.out.println(by1);
System.out.println(by2);
*/
}
}
----------- java培训、java技术博客、期待与您交流! ------------