学习笔记2-IO流
来源:互联网 发布:qr是什么软件 编辑:程序博客网 时间:2024/05/18 00:30
IO流
引子:输入/输出系统不仅需要考虑三种不同种类(文件、控制台、网络连接)的IO,而且需要通过大量不同的方式与它们通信(顺序、随机访问、二进制、字符、按行、按字等等)。
IO流的定义与分类
定义:可以理解为数据的流动。就是一个数据流。Java程序通过流来完成输入/输出,IO流最终要以对象来体现。
用处:流的概念使得文件、网络、内存等设备可以被统一处理。用来处理设备之间的数据传输.JAVA对数据的操作是通过流的方式.
流的操作只有两种:读和写。
分类:
数据类型 相对内存方向 功能
字节流 输入 节点流
字符流 输出 链接流
字符编码
Ø 计算机里只有数字,字符与数字对应的编码固定下来后,这套编码规则被称为ASCII码
Ø 许多国家都把本地的字符集引入了计算机,扩展了计算机中字符的范围,中国大陆为每一个中文字符都指定了一个对应的数字,这套编码规则称为GBK
Ø 为了解决各个国家和地区使用自不同的本地化字符编码带来的不便,人们将全世界所有的符号进行了统一编码,称之为Unicode编码
为什么要有字符流呢?
字节流:处理字节数据的流对象。设备上的数据无论是图片或dvd,文字等,都是以二进制存储的。二进制最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据。但因为每个国家的字符都不一样。所以涉及到了字符编码问题。那么GBK编码的中文用unicode编码解析是有问题的。所以需要获取中文字节数据的同时+指定的编码表才可以解析正确数据。为了方便于文字的解析。所以将字节流和编码表封装成对象。这个对象就是字符流。这就是字符流的由来。只要操作的是字符数据,优先考虑使用字符流体系。
流的体系
因为功能不同,但有共性内容,所以不断抽取形成基本体系。该体系有四个基类。而且都是抽象类。
共性特点:子类名后缀都是父类名。前缀名都是这个子类的功能名称。
共性方法
read方法用来读入数据,不带参数的read方法一次读入一个字节,返回值表示读入的数据;带byte数组的方法一次可读入多个字节,返回值表示本次读入了多少个字节,但这样得到的是一个字节数组,有时在程序中处理起来并不方便,所以有了缓冲流。当读到一个流的结尾时,这两个方法的返回值为-1,因此一般通过一个循环读入一个输入流中的所有数据:详细范例代码参见FileInputStream。
close方法用来关闭输入流,大多数输入流在使用完毕后都需要调用close方法关闭输入流。
当在程序中创建一个IO流对象时,计算机内存中实际上产生了一个java程序中类的实例对象,一个系统本身产生的某种资源。 Java垃圾回收器只能管理程序中的类的实例对象,没法去管理系统产生的资源,所以程序需要调用close方法,去通知系统释放自身产生的资源。
iO流的读写示例
close()和flush()的区别:
flush():将缓冲区的数据刷到目的地中后,流可以使用。
close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定要做。
--------------------------------------------------------------------------------------------------------------------
读取数据:自定义缓冲区。较为高效。
import java.io.*;
class FileReaderDemo2{
public static void main(String[] args) throws IOException{
//创建读取流对象时会同时调用系统底层资源,在指定的位置创建一个存储数据的文件
FileReader fr = new FileReader("demo.txt");
//使用read(char[])方法,要将读取到的字符存入数组。所以要创建一个一般长度是1024的整数倍的字符数组。。
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fr.close();//关闭流,其实就是关闭java调用的系统底层资源。在关闭前,会自动先刷新该流。
}
}
--------------------------------------------------------------------------------------------------------------------
将指定目录中的文本文件复制到另一个目录中。
import java.io.*;
class CopyText2{
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader("FileWriterDemo2.java");
fw = new FileWriter("write2_copy.txt");
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
fw.flush();
}
}
catch (IOException e)
{
//System.out.println("write :"+e.toString());
throw new RuntimeException("复制失败");
}
finally
{
if(fw!=null)
try
{
fw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
if(fr!=null)
try
{
fr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
}
}
}
流的操作规律
流对象:就是读取和写入。但因为功能的不同,流体系中提供了很多对象。那到底该用哪个对象更为合适呢?这就需要明确流的操作规律。
想要知道开发时用到哪些对象。只需要通过四个明确即可。
1, 明确源和目的(汇).
源:InputStream Reader
目的:OutputStream Writer
2, 明确数据是否是纯文本数据。
源:如果是纯文本:Reader
不是纯文本:InputStream
目的:是纯文本Writer
不是纯文本:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3, 明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组,(其实就是缓冲区)
网络:Socket流。
目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流。
4, 是否需要其他额外功能。
a, 是否需要高效(缓冲区):
是,就加上buffer。
b,是否需要转换流
需求1:复制一个文本文件。
1, 明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2, 是否是纯文本:是
源:Reader
目的:Writer
3, 明确具体设备。
源:硬盘:FileFileReader fr = new FileReader(“a.txt”);
目的:硬盘:FileFileWriter fw = new FileWrtiter(“b.txt”);
4, 需要额外功能吗?
需要,需要高效。
---------------------------------------------------------------------------------------------------------------------------------
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是。
源:Reader
目的:Writer
3,明确设备:
源:键盘。System.inInputStream in = System.in
目的:硬盘。FileFileWriter fw = new FileWriter(“b.txt”);
这样做可以完成,但麻烦。将读取的字节数据转成字符串,再由字符流操作。
4,需要额外的功能吗?
需要:转换。将字节流转换成字符流。因为明确的源是Reader,这样操作文本数据做便捷。所以要将已有的字节流转换成字符流。使用字节转换字符流:InputStreamReader.
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter(“b.txt”);
需要高效吗?需要。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter(“b.txt”));
======================================================================
需求3:将以个文本文件数据显示在控制台上。
1, 明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2, 是否是纯文本呢?
是。
源:Reader
目的:Wrter
3, 明确具体设备:
源:硬盘:File
目的:控制台:System.out
FileReader fr = new FileReader(“a.txt”);
OutputStream out = System.out; //PrintStream
4, 需要额外的功能吗?
需要:转换。
FileReader fr = new FileReader(“a.txt”);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要高效吗?需要
BufferedReader bufr = new BufferedReader(new FileReder(“a.txt”);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
======================================================================
需求4:读取键盘录入数据,显示在控制台上。
1, 明确源和目的:
源:InputStream Reader
目的:OutputStream Writer
2, 是否是纯文本呢?
是
源:Reader
目的:Writer
3, 明确设备:
源:键盘:System.in
目的:控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4, 明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据,所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter isw = new OutputStreamWriter(System.out);
需要将其高效
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out);
======================================================================
需求5:将一个中文字符串数据按照指定的编码表写入到一个文本文件中。
1, 目的:OutputStream Writer
2, 是纯文本,Writer
3, 设备:硬盘File
FileWriter fw = new FileWriter(“a.txt”);
fw.write(“你好”);
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部使用的是默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,对象应该是FileOutputStream。
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“a.txt”).charsetName);
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(“a.txt),charsetName));
转换流
转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
转换流的最强功能就是基于字节流+编码表。凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
发现转换流有一个子类就是操作文件的字符流对象。
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
想要操作文本文件,必须要进行编码转换。而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
但是子类有一个局限性。就是子类中使用的编码是固定的是本机默认的编码表。对于简体中文版的系统默认码表是GBK.
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
这两行代码的功能一摸一样。如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt");
如果需要制定码表,必须用转换流。
标准输入输出流
Ø 语言包java.lang中的System类管理标准输入/输出流和错误流
Ø 标准输入输出是操作系统提供的输入输出流,默认情况下: 标准输入System.in对应键盘的输入,是InputStream类型的,程序使用System.in可以读取从键盘上输入的数据;标准输出System.out对应控制台,是PrintStream类型的;标准错误输出System.err 默认情况下也会输出到控制台,在Eclipse开发环境中,标准错误输出打印出的内容用红色字体显示
Ø System类提供了一些用于重定向流的静态方法。
q setIn(InputStream in):对标准输入流重定向
q setout(PrintStream out):对标准输出流重定向
q setErr(PrintStream out):对标准错误输出流重定向
打印流PrintStream
1:提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。
2:自动刷新。不用手动刷新
3:使用本机默认字符编码.
4:该流的print方法不抛出IOException。
该对象的构造函数。
Ø PrintStream(File file) 创建具有指定文件且不带自动行刷新的新打印流。
Ø PrintStream(String fileName) 创建具有指定文件名称且不带自动行刷新的新打印流。
Ø PrintStream(OutputStream out) 创建新打印流。
Ø 前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。
PrintStream可以操作目的:
1:File对象。
2:文件的字符串路径。
3:字节输出流。
当目的是一个字节输出流时,如果使用的println方法。可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新。而不是等待缓冲区满了再刷新,最终print方法都将具体的数据转成字符串。而且都对IO异常进行了内部处理。
PrintWriter对象与PrintStream区别?
1, 可以手动指定编码,可以转化成字符来操作
2, 目标地比PrintStream多了一个输出字符流。
3, 两个相对尽量多使用PrintWriter,因为操作字符的多
PrintWriter具备PrintStream的特点,同时还有自身特点。开发时尽量使用PrintWriter。
/*
读取键盘录入将数据转成大写显示在控制台.
*/
//源:
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//目的:又想把数据写到文件中。还想自动刷新。
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase());
//out.flush();
}
/*
注意:
System.in,System.out这两个标准的输入输出流,在jvm启动时已经存在了。
随时可以使用。当jvm结束了。这两个流就结束了。
但是,当使用了显示的close方法关闭时,这两个流在提前结束了。
*/
out.close();
bufr.close();
}
}
其他流
管道流:
• 管道流用来把一个程序、线程或代码块的输出连接到另一个程序、线程或代码块的输入。主要作用是可以连接两个线程间的通信。管道流也分为字节流(PipedInputStream、PipedOutputStream)与字符流(PipedReader、PipedWriter)
• 管道读取流和管道写入流可以像管道一样对接上。管道读取流就可以读取管道写入流写入的数据。
通过构造方法连接
PipedInputStream(PipedOutputStream pos);PipedOutputStream(PipedInputStream pis);
通过各自的connect()方法连接
在类PipedInputStream中,connect(PipedOutputStream pos);
在类PipedOutputStream中,connect(PipedInputStream pis);
注意:需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法,是阻塞式的。没有数据的read方法会让线程等待。
public static void main(String[] args) throws IOException
{
PipedInputStream pipin = new PipedInputStream();
PipedOutputStream pipout = new PipedOutputStream();
pipin.connect(pipout);
new Thread(new Input(pipin)).start();
new Thread(new Output(pipout)).start();
}
ByteArrayOutputStream&ByteArrayInputStream:
1, 操作设备目的是File和数组
2, 两个源和目的都是内存。把内存中的一个缓冲区(ByteArray)作为输入流和输出流使用
3, 里面就是封装了数组,本身只有一个length属性,可以读取和设置
数据流类DateInputStream和DateInputStream:是FilterInputStream的子类,专门用于处理java的基本数据类型,必须成对出现
序列流SequenceInputStream
Ø 作用就是将多个读取流合并成一个读取流。表示其他输入流的逻辑串联。这样做,可以更方便的操作多个读取流。
Ø 它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。
Ø 序列流的构造函数参数是枚举,想要获取枚举,需要有Vector集合。而枚举和迭代器是功能一样的。所以,可以用迭代替代枚举。
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
这样定义很麻烦。应该想到集合框架中工具类是否有提供相应的转换方法。
Collections.enumeration(Collection c);该方法的原理往上看。
RandomAccessFile:
特点:
1:该对象能操作的源和目的必须是文件。该对象既可读取,又可写入,允许对文件内容同时完成读和写操作
2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组 。
3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。
4:其实该对象内部封装了字节读取流和字节写入流。
注意:实现随机访问,最好是数据有规律。
File类
Ø File类是IO包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操纵文件
Ø File类将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。
Ø Java约定使用“/”来作路径分隔符,或者“\\”
Ø RandomAccessFile类是Java语言中功能最为丰富的文件访问类
RandomAccessFile能以只读或读写方式打开文件
new RandomAccessFile(f,"rw");// 读写方式
new RandomAccessFile(f,"r");// 只读方式
File类常见方法:
构造函数
q File(String path) File f1 = new File("/");
q File(String path, String filename) File f2 = new File("/","autoexec.bat");
q File(File file, String filename) File f3 = new File(f1,"autoexec.bat");
1:创建。
boolean createNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。
而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除非续写。
boolean mkdir():创建此抽象路径名指定的目录。
boolean mkdirs():创建多级目录。
2:删除。
boolean delete():删除此抽象路径名表示的文件或目录。
void deleteOnExit():在虚拟机退出时删除。
注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。
注意:window的删除动作,是从里往外删。java删除文件不走回收站,要慎用。
3:获取.
long length():获取文件大小。
String getName():返回由此抽象路径名表示的文件或目录的名称。
String getPath():将此抽象路径名转换为一个路径名字符串。
String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
String getParent():返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回null。
long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为“;”。
File.Separator:返回当前系统默认的目录分隔符,windows默认为“\”。
4:判断:
boolean exists():判断文件或者文件夹是否存在。
boolean isDirectory():判断此抽象路径名是否是目录。
boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。
boolean isAbsolute():测试此抽象路径名是否为绝对路径名。
5:重命名。
boolean renameTo(File dest):可以实现移动的效果。剪切+重命名
String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。
如果调用list方法的File 对象中封装的是一个文件,那么list方法返回数组为null。
如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个方法才有效。
FilenameFilter与FileFilter的区别?
当需要文件夹对象的时候可以用过滤器,可以过滤一些自定义的条件
1, 当文件夹的对象以字符串的形式返回子目录的一些文件,需要定义FilenameFilter接口,实现里面的方法accipt(File dir,String name)
2, 当文件夹的对象是以File的形式返回的一些子目录的文件,需要FileFilter这个接口,实现里面的方法accept(File pathname)
递归
Ø 就是一个方法每次用不同的参数值反复调用自己。其实递归就是在栈内存中不断的加载同一个方法。
何时用递归呢?
当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。
简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。
递归的注意事项:
1:一定要定义递归的条件。
2:递归的次数不要过多。容易出现StackOverflowError 栈内存溢出错误。
Test:删除一个目录下的所有文件.
public static void del(File f) {
File[] files = f.listFiles();
for(File file:files){
if(file.isDirectory()){
del(file);
}else{
file.delete();
}
}
f.delete();
}
- 学习笔记2-IO流
- 学习笔记--IO流
- IO流学习笔记
- IO流学习笔记
- 学习笔记---IO流
- IO流学习笔记
- IO流学习笔记
- IO流学习笔记
- 黑马程序员--学习笔记/IO流2
- 黑马程序员-IO流学习笔记-2
- Java IO流学习笔记2
- java学习笔记 IO学习笔记2 IO流-字节流
- java学习笔记--IO流
- Java IO流学习笔记
- java IO流学习笔记
- Java IO流 学习笔记
- java学习笔记-IO流
- IO流的学习笔记
- hdu 1875 最小生成树 点用坐标表示
- 使用积累(图片像素修改)
- iOS旋转概念
- 蓝桥杯 2014 本科B组 c++ 预赛 第九题 地宫取宝
- Css3 tooltip设计实践
- 学习笔记2-IO流
- Java---27---Set集合及其子类HashSet
- github 克隆和上传代码速度太慢的解决方法
- Java多线程中 synchronized和Lock的区别
- 黑马程序员----Java中反射机制的总结
- C++中 inline函数的来龙去脉
- 表驱动法在项目中的应用
- [BZOJ 1025] SCOI 2009 游戏 · 动态规划
- 超级安卓模拟器genymotion