java基础--IO流之其它流对象
笔记摘要:
本篇文章主要是对于IO流中除了经常见到的读写流之外的一些其他流对象,有
Properties、打印流、合并流SequenceInputStream、对象的序列化,持久化的流对象、
管道流对象、操作基本数据类型的流对象、 随机访问文件流对象以及源和目的都是内存的IO流对象
一、Properties
Properties是hashtable的子类。
也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串。
是集合中和IO技术相结合的集合容器。
该对象的特点:
可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键 = 值。
Properties练习:记录应用程序运行次数,如果使用次数已到,那么给出注册提示
示例说明:
该配置文件使用键值对的形式,这样便于阅读数据,并操作数据。
键值对数据是map集合,数据是以文件形式存储,使用io技术。
那么map+io-->properties.
配置文件可以实现应用程序数据的共享。
程序中发现的问题:
FileOutputStream的创建应该在count自加之后,这样每次创建FileOutputStream时,就可以用新值覆盖旧值。如果在之前创建,需要将boolean值设为true,这样count才不会被覆盖,否则,文件被覆盖,count获取到的值为null,那么count的值在+1后,第二次会重复这样的操作,所以永远是1,这样就会在文件后面写入,所以出现多条除了count其它都相同的信息,所以我们在count+1后再创建FileOutputStream,让它去覆盖
package cn.xushuai.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class RunCount {
public static void main(String[] args) throws IOException {
File file = new File("c:\\info.ini");
if(!file.exists())
try {
file.createNewFile();
} catch (IOException e) {
throw new RuntimeException("创建文件失败");
}
int count =0;
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(file);
//在这里定义输出流,由于没有设为true,所以每次会覆盖源文件,
//所以count只能在本次运行后+1,之后又被覆盖,所以总是1。
//FileOutputStream fos = new FileOutputStream(file);
try {
prop.load(fis);
String value = prop.getProperty("time");
if(!(value == null))
count = Integer.parseInt(value);
if(count>5){
System.out.println("试用期限已到,请到官网注册!");
return ;
}
count++;
//在count自增过后再创建,然后将count的新值覆盖旧值
FileOutputStream fos = new FileOutputStream(file);
prop.setProperty("time", count+"");
prop.store(fos, "");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
二、 打印流
该流提供了打印方法,可以将各种数据类型的数据都原样打印
字节打印流:PrintStream
字符打印流:PrintWriter
字节打印流PrintStream构造函数可以接收的参数类型
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
字符打印流PrintWriter构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer
PrintWriter可以同时传入输出流对象和boolean值,以便实现自动刷
import java.io.*;
class PrintStreamDemo
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);
PrintWriter out2 = new PrintWriter(System.out,true);//打印到控制台,并自动刷新
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out2.println(line.toUpperCase());
//out.flush();
}
out2.close();
bufr.close();
}
}
三、 合并流SequenceInputStream
使用说明:
操作合并流的时候需要将操作的文件读取流封装成对象,存入集合中,然后进行写入,又因为SequenceInputStream接收一个Enumeration 类型的参数,而枚举类型只有在Vector中才有,所以只能使用Vector集合,通过elements方法获取枚举,传入SequenceInputStream。然后确定写入流,进行读写。
由于Vector效率低,所以用ArrayList替代,但要想得到枚举类型,可以利用迭代器,创建Enumeration对象,通过匿名内部类的方法实现Enumeration接口,并使用迭代器的方法覆盖其中的hasMoreElements和nextElement方法,然后确定写入流,进行读写。
文件的切割与合并示例代码:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
public class SplitFileDemo {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//splitFile();
merge();
}
public static void splitFile() throws IOException {
FileInputStream fis = new FileInputStream("c:\\1.mp3");
FileOutputStream fos = null;
byte[] buf = new byte[1024*1024];
int len = 0;
int count=1;
while((len = fis.read(buf))!=-1){
fos = new FileOutputStream("c:\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
/*//应该让程序自动运行,而不是手动指定要切割多少次
for(int i=1;i<13;i++){
//FileOutputStream fos = new FileOutputStream("c:\\"+i+".part");
while((len = fis.read(buf))!=-1){
fos = new FileOutputStream("c:\\"+(count++)+".part");//应该在外部定义,避免创建不需要的对象
//FileOutputStream fos = new FileOutputStream("c:\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
System.out.println("......"+count);
}
}*/
fis.close();
}
public static void merge() throws IOException{
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int i=0;i<12;i++){
al.add(new FileInputStream("c:\\"+i+".part"));
}
//通过Iterator来获取枚举方法
final Iterator<FileInputStream> it = al.iterator();
//通过内部类的形式来创建枚举
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {
@Override
public boolean hasMoreElements() { //使用迭代器的方法来覆盖枚举的方法
return it.hasNext();
}
@Override
public FileInputStream nextElement() {
return it.next();
}
};
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("c:\\merge.mp3");
int len=0;
byte[] buf = new byte[1024];
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
sis.close();
fos.close();
}
}
四、对象的序列化,持久化对象
ObjectInputStream
ObjectOutputStream
可以直接将对象持久的存入介质中,并读出,必须成对使用。
使用注意事项:
1>进行序列化时需要继承Serializable, 以启用其序列化功能。(该序列化只能将堆内存中的数据序列化,序列号是根据类中的成员计算而得)
2>由于定义的类后期可能会进行修改,因此重新编译后会生成新的序列号,为了让原有类被序列化的数据可以继续使用,必须给类进行自定义标识,
该标识必须是静态,最终,长整型的 :static final long serialVersionUID = 42L;
3>静态不能够序列化,由于对象是在对内存中的。
4>对非静态成员也不想序列化,可以用transient修饰,保证其值在堆内存中存在而不在文本文件中存在。
示例代码:
import java.io.*;
class Person implements Serializable
{
public static final long serialVersionUID = 42L;
private String name;
transient int age;
static String country = "cn";
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
import java.io.*;
class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
//writeObj();
readObj();
}
public static void readObj()throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
public static void writeObj()throws IOException
{
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",399,"kr"));
oos.close();
}
}
五、管道流
PipedInputStreamPipedReader
PipedOutputStream PipedWriter
可以直接将输入流与输出流连接,将流中的数据直接写入到读取端,必须结合多线程使用,因为单线程可能导致死锁。
管道流使用示例代码:
import java.io.*;
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void run()
{
try
{
byte[] buf = new byte[1024];
System.out.println("读取前。。没有数据阻塞");
int len = in.read(buf);
System.out.println("读到数据。。阻塞结束");
String s= new String(buf,0,len);
System.out.println(s);
in.close();
}
catch (IOException e)
{
throw new RuntimeException("管道读取流失败");
}
}
}
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out = out;
}
public void run()
{
try
{
System.out.println("开始写入数据,等待6秒后。");
Thread.sleep(6000);
out.write("piped lai la".getBytes());
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args) throws IOException
{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out);
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
六、操作基本数据类型的流对象
DataInputStream
DataOutputStream
直接对基本数据类型进行操作,由于存入数据的类型可能不一致,所以读取时应注意顺序的一致,否则出现乱码
示例代码
import java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
// osw.write("你好");
// osw.close();
// readUTFDemo();
}
public static void writeData()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
ObjectOutputStream oos = null;
oos.writeObject(new O());
}
public static void readData()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
//使用修改版的UTF-8写入,必须用相应的方式读取,用转换流读不出来
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");
dos.close();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
}
七、RandomAccessFile
随机访问文件,自身具备读写的功能,只能操作文件。
使用说明:
1>通过skipBytes(intx) ,seek(int x)来达到随机访问
2>该类不是IO中的子类,而是直接继承自Object,但是它是IO包中的成员,因为它 具备读和写的功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,其完成读写的原理就是内部封装了字节输入流和输出流、
3>该类只能操作文件,而且还有操作模式,
如果模式为只读 r,不会创建文件,会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw,操作的文件不存在,会自动创建,如果存则不会覆盖。
4>该类可以利用多线程实现分段写入,文件的下载就是此原理。
随机读取示例:
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//readFile();
//System.out.println(Integer.toBinaryString(258));
}
//写入数据,同时移动指针
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
//可以任意修改数据
public static void writeFile_2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
}
//可以任意读取数据
public static void readFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中指针。
//raf.seek(8*1);
//跳过指定的字节数
raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
}
八、源和目的都是内存的IO类
用流的读写思想来操作数据
操作字节数组 ByteArrayInputStream ByteArrayOutputStream
字符数组 CharArrayReader CharArrayWrite
字符串 StingReader StringWriter
ByteArrayInputStream :
在构造的时候,需要接收数据源,而且数据源是一个字节数组
ByteArrayOutputStream:
在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
注意:
因为这两个流对象都操作的数组,并没有使用系统资源, 所以,不用进行close关闭。
import java.io.*;
class ByteArrayStream
{
public static void main(String[] args)
{
//数据源。
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
//一次性地将数据写入文件
// bos.writeTo(new FileOutputStream("a.txt"));
}
}