一.流的分类:
按操作数据:
字符流: Writer,Reader字符流可以指定编码表。
字节流:InputStream ,OutputStream
按流向:
输入流:Writer,OutputStream
输出流:ReaderInputStream
二、有关对象的注意事项
(1)、FileWrite对象
FileWriterfw=new FileWriter(1.txt);
fw.writer(“aaaaaa”);
fw.flush();//刷新
fw.close();
这句话的注意事项:
1)他会自动创建1.txt 文件,如果已有这个文件,他会覆盖已有的文件。
2)向文件中写入字符串时要进行刷新,因为你写入了流中,进行刷新才会将流中的数据写入文件中。写一次刷一次。
3)close()和flush的区别:close方法是先刷新后关流。
flush刷新后,还可以继续使用,close刷新后,会将流关闭。
写完之后一定要关流。
(2)对异常处理的方式
public static void main(String[] args)
{
FileWriter fw = null;//把引用定义到外边是因为如果你在try里面定义的化,其他代码访问不到。
try
{
fw = newFileWriter("demo.txt");
fw.write("abcdefg");
}
catch (IOException e)
{
System.out.println("catch:"+e.toString());
}
finally
{
try
{
if(fw!=null)
fw.close(); //close()一定要写在finally里,因为流必须得关,当你初始化成功时。
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
文件的读写
1) 对已有文件的续写
FileWriter fw=new FileWrite(“1.txt”,true);
true代表可以已有数据的末尾追加数据,如果文件没有就创建.
fw.Write(“asd\r\n sfds”); //\r\n表示在记事本中的换行。
文本文件的读取
FileReader:
1)读中的close()关闭之前不刷新。
2)读取的文件必须存在,否则发生异常。FileNotFoundException
3)文件读取的结尾标志是-1;(一次读单个字符)int read();返回字符对应的ASCII
读取方式一:
FileReader fr=new FileReader(“1.txt”)
int num=0;
while((num=fr.reader()!=-1)
{
System.out.println(char(num))
}
4)intread(char[ ])返回读到字符的个数。把读到的字符先存到数组中。
读取方式二:
FileReader fr=new FileReader(1.txt);
char[]buf=new char[1024];
intnum=0;
while((num=fr.read(buf))!=-1)
{
System.out.println(newString(buf,0,num);
}
复制文件:实际上就是把一个文件的数据写到另一个文件中。
如下代码:
public class CopyText{
public static voidmain(String[] args) {
FileReaderfr=null;
FileWriterfw=null;
try {
fr=newFileReader("1.txt");
fw=newFileWriter("copy_1.txt");
char[] buf=new char[1024];
int num=0;
while((num=fr.read(buf))!=-1)
{
fw.write(buf,0,num);
}
}catch(IOException e) {
e.printStackTrace();
}finally
{
try {
if(fr!=null)
fr.close();
}catch(Exception e2) {
e2.printStackTrace();
}
if(fw!=null)
try {
fw.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
缓冲区:BufferReader 、BufferWriter
只要用到缓冲区,就要进行刷新。其实关闭缓冲区就是关闭缓冲区中的流操作。
缓冲区中的特有方法:
BufferWriter中newLine();跨平台的换行符。返回的是终止符之间的数据,并不返回终止符。
BufferReader中readLine();一行一行的读。
读文件的方法:
FileReaderfr=new FIleReader(“1.txt”);
BufferedReaderbufr=new BufferedReader(fr);
Stringline=null;
While((line=bufr.readLine()!=null))
{
System.out.println(line);
}
通过缓冲区中复制文件:
readLine()的原理:无论是读一行还是读取多个字符,其实最终都是在硬盘上一个一个读取。最终使用的还是read方法,一次读一个的方法。
代码如下:
BufferedReaderbufr=newBufferedReader(newFileReader("1.txt"));
BufferedWriter bufw=newBufferedWriter(newFileWriter("copy_1.txt"));
String line=null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
自定义缓冲区:
装饰设计模式:当想要对已有的对象进行功能的增强,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。自定义的类称为装饰类。
装饰和继承的区别:继承是不断向上抽取的。
装饰是在原有类的基础上对他进行包装。
字节流:OutPutStream、InPutStream
用于操作图片数据FileOutPutStream
字节流不用刷新
字节流中读一个字符的方法返回的是字符的ASCII
读一个字节数组的方法返回的是文件中字符的个数
字节流进行图片的拷贝
代码如下:
class CopyPic
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
fos = new FileOutputStream("copy_1.bmp");
fis = newFileInputStream("1.bmp");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("复制文件失败");
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
字节流特有的方法:
intavailable();用于获得文件中数据的个数。
拷贝图片:实际上就是用字节流读取一个文件然后再写入一个文件中
字节流的缓冲区:
自定义字节流缓冲区:
读的是字节为什么返回的是int型的呢?
读取键盘录入:System.in
注意事项:每读一行就要清空一下缓冲区
转换流:InputStreamReader
BufferReaderbr=new BufferReader(new InputStreamReader(System.in));
把键盘上的录入存入文件。
按照指定的编码表:转换流中可以指定编码表,
OutputStreamWriter();
在gbk中一个中文代表3个字节
在UTF-8中一个中文代表4个字节
改变设备的输入输出System.setIn(); System.getOut();
异常的日志信息:
把控制台上的异常信息写入一个文件中。
系统信息: Properties getProperties();System.getProperties();
获取系统的配置信息
File的概述
1)构造函数
Filef=new File(“a.txt”);//这个文件不一定存在
separator是默认的目录分割符,具有跨平台性
2)功能
创建: boolean creatNewFile();在指定位置创建文件,如果该文件已经存在则不创建。
临时文件:creatTempFile()
mkdir():创建目录(文件夹)。只能创建一级目录
mkdirs();创建多级文件夹。
删除:boolean delete();
void deleteOnExit();程序退出时删除文件
判断:boolean canExecute():判断是否可执行
Boolean exists();文件是否存在
isFile();是否是文件
isDirectory();是否是目录
判断文件或目录是必须先判断该文件对象封装的内容是否存在
isAbsolute();判断是否是绝对路径。文件不存在也可判断
获取信息:
getName();
getPath();
getParent();//如果没有明确绝对路径的话,返回的是null。
getAbsolutePath()
long lastModified()//最后一次修改的时间
long length()
renameTo();重命名。相当于剪切
listRoot();列出了计算机中的盘符
list()列出当前盘符下的所有文件
文件名过滤:
String[]arr=dir.list(new FilenameFilter()
{
Public Boolean accept(File dir ,String name)
{
Return name.endsWith(“.bmp”);
}
}
)
遍历指定目录下的所有文件。利用递归原理
删除带内容的目录:
代码如下:
import java.io.*;
classFileDemo3
{
public static void main(String[] args)
{
File dir = newFile("d:\\testdir");
System.out.println(dir.delete());
}
public static String getLevel(int level)
{
StringBuilder sb = new StringBuilder();
sb.append("|--");
for(int x=0; x<level; x++)
{
sb.insert(0,"| ");
}
return sb.toString();
}
public static void showDir(File dir,int level)
{
System.out.println(getLevel(level)+dir.getName());
level++;
File[] files = dir.listFiles();
for(int x=0; x<files.length; x++)
{
if(files[x].isDirectory())
showDir(files[x],level);
else
System.out.println(getLevel(level)+files[x]);
}
}
总结:主要思想是利用递归原理,当判断是目录时然后再回调知道不是目录为止,这样一层一层的进入。
列出所有的列表,存放到一个文件中:
Properties的应用:
如下代码:
import java.io.*;
import java.util.*;
class JavaFileList
{
public static void main(String[] args) throwsIOException
{
File dir = new File("d:\\java20");
List<File> list = newArrayList<File>();
fileToList(dir,list);
File file = newFile(dir,"javalist.txt");
writeToFile(list,file.toString());
}
public static void fileToList(Filedir,List<File> list)
{
File[] files = dir.listFiles();
for(File file : files)
{
if(file.isDirectory())
fileToList(file,list);
else
{
if(file.getName().endsWith(".java"))
list.add(file);
}
}
}
public static void writeToFile(List<File>list,String javaListFile)throws IOException
{
BufferedWriter bufw = null;
try
{
bufw = new BufferedWriter(newFileWriter(javaListFile));
for(File f : list)
{
String path = f.getAbsolutePath();
bufw.write(path);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw e;
}
finally
{
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw e;
}
}
}
}
总结:这个代码主要的思想是:先建立一个集合用于存储所遍历到的目录,遍历目录时用的思想是递归算法,然后再利用流把集合中的数据写到文件中。
1. getProperty();//获取键值
2. setProperty();//设置键值
3. stringPropertyName();//返回的是Set集合
Properties存取配置文件:
把文件中的内容加载到Properties中通过load()方法和store()方法进行加载和保存,实际上他就是Map和IO的结合体。
限制一个程序运行的次数。
如下例子:
import java.io.*;
import java.util.*;
class RunCount
{
publicstatic void main(String[] args) throws IOException
{
Propertiesprop = new Properties();
Filefile = new File("count.ini");
if(!file.exists())
file.createNewFile();
FileInputStreamfis = new FileInputStream(file);
prop.load(fis);
intcount = 0;
Stringvalue = prop.getProperty("time");
if(value!=null)
{
count= Integer.parseInt(value);
if(count>=5)
{
System.out.println("您好,使用次数已到!");
return;
}
}
count++;
prop.setProperty("time",count+"");
FileOutputStreamfos = new FileOutputStream(file);
prop.store(fos,"");
fos.close();
fis.close();
}
}
总结:思想是定义一个计数器,每运行一次,计数器加1,然后写到配置文件中,因为写入文件的计数器的值不会改变,当你读到五次时就不让读了
IO中的其他流对象:
PrintStream():
字节打印流构造函数可以接受的参数类型:File对象、字符串路径
字节输出流Outputstream
PrintReader();字符打印流 可以接受的参数类型:file对象,字符串路径字节输出流,字符输出流。
合并流:SequenceInputStream:
操作对象的流:ObjectInputStream ObjectOutputStream
对象的持久化存储;注意事项:
1、 把对象传入并且写到文件中。
2、 对象必须实现Serializable接口
3、 如果想改变类中的内容,在经过ObjectInputStream读时会出现异常,因为当你改变类时与原来的类的序列号不对应了,解决这个问题可以自定义一个固定的序列号,
4、 静态的不能被序列化。
5、 如果不想被序列化,加上transient
管道流:PipedInputStream PipeOutputStream
RandomAccessFile 随机访问文件的读取和写入
1、 只能操作文件
2、 writeIn();写入四个字节
3、 构造函数中要传入模式
4、 可以调整指针来取出想要的seek(8)
5、 SkipBtes();跳过多少个字节。取下一个
6、 可以随机添加
7、 如果模式为只读r,不会创建文件,回去读取一个已存在的文件,如果为rw模式,文件如果没有会创建
可以实现多线程的下载
操作基本数据类型:DataInputStream DataOutputStream
writeUTF(String str)
一、操作对象的流--------àObjectOutputStreamObjectInputStream
主要的特点是它可以将对象存入文件中。
import java.io.*;
public classObjectTest {
public static voidmain(String[] args) throwsIOException {
Read();
Write();
}
public static void Read() throwsIOException,FileNotFoundException, ClassNotFoundException {
ObjectInputStream objIn=
newObjectInputStream(newFileInputStream("G:\\Workspaces\\MyEclipse8.5\\javabase\\src\\day21\\1.txt"));
Person p=(Person)objIn.readObject();
System.out.println(p);
objIn.close();
}
public static void Write() throwsIOException, FileNotFoundException {
ObjectOutputStreamobjOut=
newObjectOutputStream(new FileOutputStream("G:\\Workspaces\\MyEclipse8.5\\javabase\\src\\day21\\1.txt"));
objOut.writeObject(new Person("lisi1",23,”kr”));
objOut.close();
}
}
public class Person {
Stringname;
transient int age;
static String country=”cn”;
publicPerson(String name, intage,String country) {
super();
this.name = name;
this.age = age;
this.country=country;
}
public StringtoString()
{
return name+"::"+age;
}
}
这个小程序注意的问题:
1.要想序列化的类必须实现Serializable接口,这个接口没有方法,它相当于一个标记。如果没实现会产生java.io.NotSerializableException异常
2.当你改变Person类中的内容时,就会生成一个新的序列号。当你在读Person对象时,会产生:java.io.InvalidClassException 这个表示无效的类异常,也就是你刚才的序列号和你现在的序列号不同了。
3.这时如果你想要读出改变后的,可以自定义一个序列号,例如:public static final long serialversionUID=40L(这个值是随便写的)
4.当你对非静态的成员不想进行序列化是可以加上关键字:transient
5. 静态的不可以被序列化,因为:静态在方法区中而没在堆内存中
二、管道流 ------àpipedInputStream pipedOutputStream
不建议使用单线程,因为容易发生死锁,
pipedInputStream in=new pipedInputStream();
pipedOutputStream out=new pipedOutputStream();
in.connect(out) ;----à把两个流联系起来
三、 RandomAccessFile内部封装了一个数组,可以通过getFilePointer()获取指针的位置,可以通过seek改变指针的位置。
注意:该类只能操作文件
在操作文件时时有模式的,例如r,rw
如果你写入的模式是r,则表示只能进行读操作
如果你写入的是rw,则表示既可以进行读操作,又可以进行写操作
RandomAccessFile类可以在任意位置进行读写,因为有skipBytes( )方法,它可以跳过指定的字节数。Seek()可以调整指针的位置
DataInputStream与DataOutputStream
用于操作基本数据类型的数据的流对象。
例如:int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
注意:它里面有一个特殊的方法:writeUTF(" "),用它写入的数据必须用对应的特定的流读出来,否则会出现乱码。
用于操作字节数组的流对象。
ByteArrayInputStream:在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭
有关流的总结:
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。
字符编码:
编码:当把字符串转换成字节时编码
编码也可以这样理解:当你把认识的转成不认识的时就是编码。
解码:当把字节转换成字符时解码
解码也可以这样理解:当你把不认识的转成认识的时就是解码
当你解码是出问题或是解错时,可以采用再编一次,解一次的方式进行,也就是说
String s="你好";
byte[] b =s.getBytes("gbk");
String s1 = new String(b,"iso8859-1");
byte[] b1 =s1.getBytes("iso8859-1");
System.out.println(new String(b1,"gbk"));
当你用jbk编的码而你不小心用了iso8859-1解的码,这时你可以采用你写错的码在进行编一次,然后在进行解码。
总结:当你用到复制文件,键盘录入等等,就一定得有流,流主要记住两大派,字节流和字符流,如果用到编码时,就要想到转换流。
编码的一个主要思想:当你解码是出问题或是解错时,可以采用再编一次,解一次的方式进行