【JAVA 其它流对象】
一、PrintStream类。
该流是字节流。
public class PrintStream extends FilterOutputStream implements Appendable, Closeable |
java.lang.Object
|--java.io.OutputStream
|--java.io.FilterOutputStream
|--java.io.PrintStream
API描述:
PrintStream
为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream
永远不会抛出 IOException
;而是,异常情况仅设置可通过 checkError
方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream
;这意味着可在写入 byte 数组之后自动调用 flush
方法,可调用其中一个 println
方法,或写入一个换行符或字节 ('\n'
)。
PrintStream
打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用
类。 PrintWriter
简单来说该流的特点有:
(1)它不抛出IO异常。
(2)提供了打印方法可以对多种数据类型值进行打印。并可以保持数据的表示形式。
(3)该流若不指定编码类型,则适用平台默认的编码。
1.构造方法。
构造方法可以接受的参数包括:
(1)字符串路径
(2)File对象
(3)字节输出流
2.常用方法。
void |
close() 关闭流。 |
void |
flush()
刷新该流的缓冲。 |
PrintSteam类若要将信息输出,必须刷新。如果想要自动刷新,则应当使用拥有自动刷新功能的构造方法。如果想要写入文件而且自动刷新,则需要先构造字节输出流,再作为参数传递给构造放方法。
void |
print(基本数据类型 x) 打印基本数据类型不换行,使用平台默认的字符编码。 |
void |
println(节本数据类型 x) 打印基本数据类型到指定的对象,使用平台默认的字符编码并且在末尾添加平台上的行分隔符,实现换行的功能。 |
void |
write(byte[] buf, int off, int len) 将 len 字节从指定的初始偏移量为
off 的 byte 数组写入此流。 |
void |
write(int b)
将指定的字节写入此流。 |
print和write方法的区别是什么?
print方法会将打印的基本数据类型先转变成字符串再
打印;write方法则将打印的整型数据取低八位一个字节再打印。
print(97);结果是97;而write(97);结果则变成a。
二、PrintWriter类。
该流是字符流。
public class PrintWriterextends Writer |
java.lang.Object
|--java.io.Writer
|--java.io.PrintWriter
该类中的方法和PrintStream中的方法几乎相同,不赘述。
但是应当注意,这两者有一个共同的特点,那就是如果直接将数据写向文件,都不带自动刷新功能,如果想要写入文件而且还要自动刷新,则需要先使用输出流将文件封装起来。
三、序列流:SequenceInputStream类。
public class SequenceInputStreamextends InputStream |
java.lang.Object
|--java.io.InputStream
|--java.io.SequenceInputStream
该流能够将多个输入流合并成一个流。
1.构造方法。
SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream ,该参数必须是生成运行时类型为
InputStream 对象的 Enumeration 型参数。 |
SequenceInputStream(InputStream s1, InputStream s2)
通过记住这两个参数来初始化新创建的 SequenceInputStream (将按顺序读取这两个参数,先读取
s1 ,然后读取 s2 ),以提供从此 SequenceInputStream
读取的字节。 |
这两个构造方法中,第一个方法比较常用,因为可以使用集合将各个流存放起来,这么做好处理。
2.常用方法。
略。
3.实例。
将三个文本文件的内容合并到一个文本文件中(使用第一种构造方法)。
方法一:使用标准流程。
1 private static void mergeMultipleFiles() throws IOException { 2 Vector<FileInputStream>v=new Vector<FileInputStream>(); 3 v.add(new FileInputStream("1.txt")); 4 v.add(new FileInputStream("2.txt")); 5 v.add(new FileInputStream("3.txt")); 6 Enumeration<FileInputStream> e=v.elements(); 7 SequenceInputStream sis=new SequenceInputStream(e); 8 FileOutputStream fos=new FileOutputStream("4.txt"); 9 BufferedReader br=new BufferedReader(new InputStreamReader(sis)); 10 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos)); 11 String buf=null; 12 while((buf=br.readLine())!=null) 13 { 14 bw.write(buf); 15 bw.newLine(); 16 bw.flush(); 17 } 18 br.close(); 19 bw.close(); 20 }
方法二:Vector类已经很少用,使用ArrayList代替其功能,这时候需要自定义类并实现Enumeration接口。
1 package p07.SequenceInputStreamDemo.p01.SequenceInputStreamDemo; 2 3 import java.io.BufferedReader; 4 import java.io.BufferedWriter; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStreamReader; 9 import java.io.OutputStreamWriter; 10 import java.io.SequenceInputStream; 11 import java.util.ArrayList; 12 import java.util.Enumeration; 13 import java.util.Iterator; 14 import java.util.List; 15 import java.util.Vector; 16 public class SequenceInputStreamDemo { 17 public static void main(String[] args) throws IOException { 18 // mergeMultipleFiles(); 19 mergeMultipleFiles_1(); 20 } 21 private static void mergeMultipleFiles_1() throws IOException { 22 List<FileInputStream>v=new ArrayList<FileInputStream>(); 23 v.add(new FileInputStream("1.txt")); 24 v.add(new FileInputStream("2.txt")); 25 v.add(new FileInputStream("3.txt")); 26 Iterator<FileInputStream> e=v.iterator(); 27 SequenceInputStream sis=new SequenceInputStream(new EnumerationX(e)); 28 FileOutputStream fos=new FileOutputStream("4.txt"); 29 BufferedReader br=new BufferedReader(new InputStreamReader(sis)); 30 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos)); 31 String buf=null; 32 while((buf=br.readLine())!=null) 33 { 34 bw.write(buf); 35 bw.newLine(); 36 bw.flush(); 37 } 38 br.close(); 39 bw.close(); 40 } 41 } 42 class EnumerationX implements Enumeration<FileInputStream> 43 { 44 Iterator<FileInputStream> it; 45 public EnumerationX(Iterator<FileInputStream> it) 46 { 47 this.it=it; 48 } 49 @Override 50 public boolean hasMoreElements() { 51 return it.hasNext(); 52 } 53 @Override 54 public FileInputStream nextElement() { 55 return it.next(); 56 } 57 }
方法三:不想使用Vector,但是方法二有太麻烦,使用Collections.enumeration方法可以简化方法二中的流程。
1 private static void mergeMultipleFiles_1() throws IOException { 2 List<FileInputStream>v=new ArrayList<FileInputStream>(); 3 v.add(new FileInputStream("1.txt")); 4 v.add(new FileInputStream("2.txt")); 5 v.add(new FileInputStream("3.txt")); 6 SequenceInputStream sis=new SequenceInputStream(Collections.enumeration(v)); 7 FileOutputStream fos=new FileOutputStream("4.txt"); 8 BufferedReader br=new BufferedReader(new InputStreamReader(sis)); 9 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(fos)); 10 String buf=null; 11 while((buf=br.readLine())!=null) 12 { 13 bw.write(buf); 14 bw.newLine(); 15 bw.flush(); 16 } 17 br.close(); 18 bw.close(); 19 }
四、小练习:文件的切割与合并
1.文件切割:将文件(媒体文件等)按照相同大小切割成若干份(这里按照1M字节来划分,使用1K的缓冲区循环1024次得到)。
1 private static void FileSplitsDemo() throws IOException { 2 File file=new File("music.mp3"); 3 FileInputStream fis=new FileInputStream(file); 4 if(file.exists()) 5 { 6 byte buf[]=new byte[1024]; 7 int pos=0; 8 int length=0; 9 stop: 10 while((length=fis.read(buf))!=-1) 11 { 12 FileOutputStream fos=new FileOutputStream("splitFiles/"+pos+".split"); 13 fos.write(buf,0,length); 14 for(int i=1;i<=1024;i++) 15 { 16 length=fis.read(buf); 17 if(length==-1) 18 { 19 fos.close(); 20 break stop; 21 } 22 fos.write(buf,0,length); 23 } 24 pos++; 25 fos.close(); 26 } 27 fis.close(); 28 System.out.println("文件切割成功!"); 29 } 30 else 31 { 32 fis.close(); 33 throw new IOException("指定的文件不存在!"); 34 } 35 }
2.文件合并:将切割后的文件碎片合并成源文件(这里是用了集合存储元素,并使用了过滤器、比较器)。
1 private static void FileMergeDemo() throws IOException { 2 File file=new File("splitFiles"); 3 if(file.exists()) 4 { 5 File[]files=file.listFiles(new FileFilter() { 6 @Override 7 public boolean accept(File pathname) { 8 if(pathname.getName().endsWith(".split")) 9 { 10 return true; 11 } 12 return false; 13 } 14 }); 15 ArrayList<File>list=new ArrayList<File>(); 16 Collections.addAll(list,files); 17 Collections.sort(list,new Comparator<File>() 18 { 19 @Override 20 public int compare(File o1, File o2) { 21 return o1.getName().compareTo(o2.getName()); 22 } 23 }); 24 List<FileInputStream>li=new ArrayList<FileInputStream>(); 25 for(File f:list) 26 { 27 li.add(new FileInputStream(f)); 28 } 29 SequenceInputStream sis=new SequenceInputStream(Collections.enumeration(li)); 30 31 File aim=new File("splitFiles/aim.mp3"); 32 FileOutputStream fos=new FileOutputStream(aim); 33 byte buf[]=new byte[1024*1024]; 34 int length=0; 35 while((length=sis.read(buf))!=-1) 36 { 37 fos.write(buf,0,length); 38 } 39 sis.close(); 40 fos.close(); 41 } 42 else 43 { 44 throw new FileNotFoundException("指定的文件未找到"); 45 } 46 }
3.注意:可以将Properties和此过程结合使用。里面的键值对分别是name=被切割文件的名称 sum=切割出来的文件总数。
五、ObjectInputStream和ObjectOutputStream类:对象流。
1.ObjectInputStream概述。
java.lang.Object
|--java.io.InputStream
|--java.io.ObjectInputStream
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants |
通过该继承层次,不能直接判断出该流的作用,可以试着查看一下构造方法猜一猜该类的作用:
构造方法摘要 | |
---|---|
protected |
ObjectInputStream() 为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。 |
|
ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream。 |
通过该构造方法可以看出该类接收一个InputStram类的对象(肯定是其子类对象),这样我们可以看出该类的作用就是增强InputStream类的功能,其设计方法使用了装饰模式。下面介绍的方法大多采用了这种设计方法,其目的都是增强某一类的功能。
该对象的一个重要方法(体现了其对象流的特点的方法):
Object |
readObject() 从 ObjectInputStream 读取对象。 |
该方法是读取自定义对象的方法,另外,还用从流中读取基本数据对象的方法:readChar、readInt、readLong、readDouble等方法。
关于该类,主要的用途就是从流中一次读取出一个对象,但是对象必须实现Serializable接口。
2.ObjectOutputStream类。
java.lang.Object
|--java.io.OutputStream
|--java.io.ObjectOutputStream
public class ObjectOutputStreamextends OutputStreamimplements ObjectOutput, ObjectStreamConstants |
其构造方法:
构造方法摘要 | |
---|---|
protected |
ObjectOutputStream() 为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。 |
|
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。 |
该类中有一个体现其“对象流”特性的方法:
void |
writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。 |
该方法是针对自定义对象使用的方法,对于基本数据类型对应的对象,则使用writeInt、writeChar、writeDouble等类来实现。
同样的,写入对象对应的类必须实现Serializable接口。
3.Serializable接口。
public interface Serializable |
该接口在java.io包中。
该接口很特殊,其特殊之处在于接口中并没有定义任何方法,所以自定义对象中如果实现了该接口,即使不做其他动作编译器也不会报错。
但是该接口中定义了一个字段serialVersionUID,该字段必须是static 、final、long类型的。所以,经典的定义方法是:
private static final long serialVersionUID = xxxxxL |
该字段值被强烈建议显式实现,我们应当注意凡是“强烈建议”的事项对于程序员来讲就应当是“必须实现”的事项,所以我们必须要显示声明该字段及值。
关键是为什么?
举例说明:假设现在我们用程序将五个Person对象写入了data.obj文件中,现在将文件传输给了小明,小明拿到该文件后不知道怎么解析,因为他不知道Person对象的内部结构(字段、方法),这样我们需要再将Person类的字节码文件传输给小明,这样小明就可以解析该文件了。 在这个过程中有个问题需要讨论:如果小明使用了自己定义的Person类来解析该文件,是否会成功?答案是否定的,因为小明的Person类和我们的Person类不同。那么为什么自己定义的Person类和我们发送给小明的Person类不同呢?明明都是Person类嘛,我们很容易想到由于类中定义的字段、方法等不同所以两个类不同。最简捷的方法就是定义一个数值,标记这个类,说明这个类的唯一性。该数值使用常量serialVersionUID来表示。Eclipse可以根据类名、字段值、方法信息综合考量再根据一个算法算出该值,对于我们来说,如果使用Eclipse的话,可以直接单击小黄色叹号,实现该功能就可以了。将该字段显式声明可以避免由于编译器版本的不同而导致算出的serialVersionUID号的不同并最终使得解析失败的严重后果。
现在开始说该接口的功能:
JDK1.6文档描述:类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数构造方法。可序列化子类的字段将从该流中恢复。
在JDK1.6的描述中,出现了序列化和反序列化的概念,我们可以简单理解为序列化就是放进去,与其对应的类就是ObjectOutputStream,反序列化就是“拿出来”,与其对应的类就是ObjectInputStream类。
综上所述,Serializable接口的目的就是为每个类添加一个ID号,该标准名称为serialVersionUID号,该号用于标识每一个独一无二的类,相当于该类的”身份证“。
4.示例代码:
要求:将Person类写入文件,并再读取出来。
1 package P06; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.ObjectInputStream; 8 import java.io.ObjectOutputStream; 9 import java.io.Serializable; 10 class Student implements Serializable 11 { 12 private static final long serialVersionUID = -4123757772133758079L; 13 private String name; 14 private int age; 15 private int num; 16 private String clazz; 17 18 public Student() { 19 super(); 20 } 21 public Student(String name, int age, int num, String clazz) { 22 super(); 23 this.name = name; 24 this.age = age; 25 this.num = num; 26 this.clazz = clazz; 27 } 28 29 public String getName() { 30 return name; 31 } 32 public void setName(String name) { 33 this.name = name; 34 } 35 public int getAge() { 36 return age; 37 } 38 public void setAge(int age) { 39 this.age = age; 40 } 41 public int getNum() { 42 return num; 43 } 44 public void setNum(int num) { 45 this.num = num; 46 } 47 public String getClazz() { 48 return clazz; 49 } 50 public void setClazz(String clazz) { 51 this.clazz = clazz; 52 } 53 @Override 54 public String toString() { 55 return "name="+name + ", age=" + age + ", num=" + num 56 + ", class=" + clazz; 57 } 58 } 59 public class Task_2 { 60 61 public static void main(String[] args) throws IOException, ClassNotFoundException { 62 FileOutputStream fos=new FileOutputStream(new File("myfile.txt")); 63 ObjectOutputStream oos=new ObjectOutputStream(fos); 64 Student []stu= 65 { 66 new Student("zhangsan",23,01,"计科1201"), 67 new Student("lisi",24,02,"计科1202"), 68 new Student("wangwu",25,03,"计科1201"), 69 new Student("zhaoliu",26,04,"计科1202"), 70 new Student("chenqi",27,05,"计科1201"), 71 new Student("jianba",28,06,"计科1203") 72 }; 73 for(int i=0;i<stu.length;i++) 74 { 75 oos.writeObject(stu[i]); 76 } 77 FileInputStream fis=new FileInputStream(new File("myfile.txt")); 78 ObjectInputStream ois=new ObjectInputStream(fis); 79 Student s=null; 80 for(int i=0;i<stu.length;i++) 81 { 82 s=(Student)ois.readObject(); 83 System.out.println(s); 84 } 85 oos.close(); 86 ois.close(); 87 } 88 89 }
5.关键字:transient
首先讨论一个问题:静态成员变量能否被写入文件?答案是不能,原因略。
假设现在又一个成员变量,既不想向文件中写,有不想静态化,该怎么办?就可以使用transient关键字,该关键字的唯一用途就在于此。
该关键字的意思是“瞬时的,短暂的”意思,而writeObject方法的实现意义正好背道而驰,这是一种科学的造名方法。
六、RandomAccessFile类。
1.概述
java.lang.Object
|--java.io.RandomAccessFile
public class RandomAccessFile extends Object implements DataOutput, DataInput, Closeable |
从该类的继承体系来看,该类不属于字节流和字符流的体系,它的父类是Object,而且该类是从JDK1.0就已经出现了的类,是很早就已经出现了了的元老级干部。
RandomAccessFile类正如其名字所体现的,它能够在文件的任意位置进行内容的修改。
构造方法摘要 | |
---|---|
RandomAccessFile(File file, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 |
|
RandomAccessFile(String name, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 |
通过该类的构造方法,可以看出该类在构造对象的时候必须给一个参数指向某个文件,这说明该类是和文件绑定在一起的。
该类中的重要方法:
int |
read(byte[] b) 将最多 b.length 个数据字节从此文件读入 byte 数组。 |
int |
read(byte[] b,
int off, int len) 将最多 len 个数据字节从此文件读入 byte 数组。 |
该方法是用于从文件中读取字节的方法,它将读取出的内容装进b数组。我们一般使用第一种方法。除此之外,还有readInt、readFloat等方法,这和ObjectInputStream很相似,但是该类只能用于文件,这是一个区别。
void |
write(byte[] b) 将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。 |
void |
write(byte[] b,
int off, int len) 将 len 个字节从指定 byte
数组写入到此文件,并从偏移量 off 处开始。 |
该方法将数组b中的内容写入文件。我们一般使用第二种方法。除此之外还有writeInt、writeFloat等方法。
void |
seek(long pos) 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。 |
该方法是实现“任意性”的关键方法,它将文件指针移动到文件中的指定位置,下一次的读写操作都将会在此地方开始。
long |
getFilePointer() 返回此文件中的当前偏移量。 |
该方法用于得到指针的位置,该方法并不常使用,但是可以用该方法检测指针的位置。
比较特殊的两个方法:
void |
writeUTF(String str) 使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。 |
String |
readUTF() 从此文件读取一个字符串。 |
这两个方法用于从文件中读取字符串和向文件中写入字符串,字符串都是已经被UTF-8修改版的编码编码过的,注意,utf-8修改版和utf-8编码不同,经过它编码过的字符串将不能够被utf-8编码解析。所以这两个方法一定要配对使用才行。
2.该类的特性。
使用该类加载文件的时候并不会将文件全部一次性加载进内存,而是只是加载进来一部分,这样,即使是几G大小的文件,操作起来也没有问题。
3.实例。
要求:将张三、李四、王五、赵六、陈七、剑八六个字符串使用多线程写入文件,并使用多线程再将文件内容读出。注意,这里使用多线程要避免线程安全性问题,也就是要加锁;同时由于使用了多线程,所以写入的顺序可能是无序的,读出的顺序可能也是无序的。
这里,由于源代码文件是UTF-8编码的,所以Eclipse会自动识别此编码并将默认的输入输出都设置为此编码形式(在dos下则为gbk,如果是简体中文版windows的话),所以一个汉字占3个字节,这里是需要注意的。
写入文件aim.date:
1 package p09.RandomAccessFileDemo.p01.RandomAccessFileDemo; 2 3 import java.io.IOException; 4 import java.io.RandomAccessFile; 5 class Resource 6 { 7 public static final Object lock=new Object(); 8 } 9 class WriteThread implements Runnable 10 { 11 private RandomAccessFile raf; 12 private String str; 13 private int pos; 14 public WriteThread(RandomAccessFile raf,String str,int pos) 15 { 16 this.pos=pos; 17 this.str=str; 18 this.raf=raf; 19 } 20 @Override 21 public void run() { 22 synchronized(Resource.lock) 23 { 24 try 25 { 26 this.raf.seek(pos); 27 } catch (IOException e) { 28 e.printStackTrace(); 29 } 30 System.out.println(this.pos+":"+this.str); 31 try { 32 this.raf.write(this.str.getBytes()); 33 } catch (IOException e) { 34 e.printStackTrace(); 35 } 36 } 37 } 38 } 39 40 public class RandomAccessFileWriteDemo { 41 public static void main(String args[]) throws IOException 42 { 43 RandomAccessFileDemoUseMultipleThread(); 44 } 45 46 private static void RandomAccessFileDemoUseMultipleThread() throws IOException { 47 RandomAccessFile raf=new RandomAccessFile("aim.date","rw"); 48 Thread threads[] = new Thread[6]; 49 threads[0]=new Thread(new WriteThread(raf,"张三",0)); 50 threads[1]=new Thread(new WriteThread(raf,"李四",6)); 51 threads[2]=new Thread(new WriteThread(raf,"王五",12)); 52 threads[3]=new Thread(new WriteThread(raf,"赵六",18)); 53 threads[4]=new Thread(new WriteThread(raf,"陈七",24)); 54 threads[5]=new Thread(new WriteThread(raf,"剑八",30)); 55 56 for(int i=0;i<threads.length;i++) 57 { 58 threads[i].start(); 59 } 60 61 } 62 }
从文件aim.date中读出:
1 package p09.RandomAccessFileDemo.p01.RandomAccessFileDemo; 2 3 import java.io.IOException; 4 import java.io.RandomAccessFile; 5 import java.io.UnsupportedEncodingException; 6 class Resource_R 7 { 8 public static final Object lock=new Object(); 9 } 10 class WriteThread_R implements Runnable 11 { 12 private RandomAccessFile raf; 13 private int pos; 14 public WriteThread_R(RandomAccessFile raf, int pos) 15 { 16 this.pos=pos; 17 this.raf=raf; 18 } 19 @Override 20 public void run() { 21 synchronized(Resource_R.lock) 22 { 23 try 24 { 25 this.raf.seek(pos); 26 } catch (IOException e) { 27 e.printStackTrace(); 28 } 29 byte buf[]=new byte[6]; 30 try { 31 this.raf.read(buf); 32 } catch (IOException e) { 33 e.printStackTrace(); 34 } 35 try { 36 System.out.println(this.pos+":"+new String(buf,"utf-8")); 37 } catch (UnsupportedEncodingException e) { 38 e.printStackTrace(); 39 } 40 } 41 } 42 } 43 44 public class RandomAccessFileReadDemo { 45 public static void main(String args[]) throws IOException 46 { 47 RandomAccessFileDemoUseMultipleThread(); 48 } 49 50 private static void RandomAccessFileDemoUseMultipleThread() throws IOException { 51 RandomAccessFile raf=new RandomAccessFile("aim.date","rw"); 52 Thread threads[] = new Thread[6]; 53 threads[0]=new Thread(new WriteThread_R(raf,0)); 54 threads[1]=new Thread(new WriteThread_R(raf,6)); 55 threads[2]=new Thread(new WriteThread_R(raf,12)); 56 threads[3]=new Thread(new WriteThread_R(raf,18)); 57 threads[4]=new Thread(new WriteThread_R(raf,24)); 58 threads[5]=new Thread(new WriteThread_R(raf,30)); 59 60 for(int i=0;i<threads.length;i++) 61 { 62 threads[i].start(); 63 } 64 65 } 66 }
读出的结果:
0:张三 30:剑八 6:李四 24:陈七 12:王五 18:赵六
如果出现了线程安全性问题,可能会发生以下情况:
同一个标号出现两次以上或者同一个人名出现两次以上甚至出现乱码。
七、管道流。PipedInuptStream、PipedOutputStream类。
java.lang.Object
|--java.io.InputStream
|--java.io.PipedInputStream
public class Piped InputStreamextends InputStream |
该类有默认无参的构造方法,也有一个比较重要的构造方法:
PipedInputStream(PipedOutputStream src) 创建 PipedInputStream ,使其连接到管道输出流 src 。 |
该方法会将输入流联系到输出流。
也可以使用connect方法。
此外使用read方法读取数据不再赘述。
java.lang.Object
|--java.io.OutputStream
|--java.io.PipedOutputStream
public class Piped OutputStreamextends OutputStream |
该类的构造方法也有两个,同时有connect方法连接输入流;有write方法将数据写给输入流。
流程:输出流将数据写给输入流,如果管道中有数据,则read方法开始读取数据,否则read方法堵塞。
实例代码:
1 package p10.PipedStreamDemo.p01.PipedStreamDemo; 2 /** 3 * 管道流 4 */ 5 import java.io.IOException; 6 import java.io.InputStreamReader; 7 import java.io.PipedInputStream; 8 import java.io.PipedOutputStream; 9 import java.io.BufferedReader; 10 class Output implements Runnable 11 { 12 PipedOutputStream pos; 13 public Output(PipedOutputStream pos) 14 { 15 this.pos=pos; 16 } 17 @Override 18 public void run() 19 { 20 String str = null; 21 while(true) 22 { 23 try { 24 str=(new BufferedReader(new InputStreamReader(System.in))).readLine(); 25 } catch (IOException e1) { 26 e1.printStackTrace(); 27 } 28 try 29 { 30 pos.write((str+"\r\t").getBytes()); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 } 36 37 } 38 class Input implements Runnable 39 { 40 PipedInputStream pis; 41 public Input(PipedInputStream pis) 42 { 43 this.pis=pis; 44 } 45 @Override 46 public void run() 47 { 48 byte buf[]=new byte[1024]; 49 while(true) 50 { 51 try 52 { 53 int length=pis.read(buf); 54 System.out.println(new String(buf,0,length,"utf-8")); 55 } catch (IOException e) 56 { 57 e.printStackTrace(); 58 } 59 } 60 } 61 62 } 63 public class PipedStreamDemo { 64 65 public static void main(String[] args) throws IOException { 66 pipedStreamDemo(); 67 } 68 69 private static void pipedStreamDemo() throws IOException { 70 PipedOutputStream pos=new PipedOutputStream(); 71 PipedInputStream pis=new PipedInputStream(); 72 pis.connect(pos); 73 new Thread(new Output(pos)).start(); 74 new Thread(new Input(pis)).start(); 75 } 76 }
注意延迟,延迟是由于管道造成的。
注意,这两个流对象必须结合多线程使用,否则容易出现死锁情况。
八、基本数据读写流:DataInputStream、DataOutputStream类。
java.lang.Object
|--java.io.InputStream
|--java.io.FilterInputStream
|--java.io.DataInputStream
java.lang.Object
|--java.io.OutputStream
|--java.io.FilterOutputStream
|--java.io.DataOutputStream
这两个类专门用于操作基本数据类型,有readInt、readDouble、writeInt、writeDouble等方法,和ObjectInputStream、ObjectOutputStream、RandomAccessFile类相比,该类更具有通用性,而前者的使用要在特定的环境中。这在其构造方法中就可以看出。
DataInputStream(InputStream in) 使用指定的底层 InputStream 创建一个 DataInputStream。 |
DataOutputStream(OutputStream out) 创建一个新的数据输出流,将数据写入指定基础输出流。 |
通过一个小例子演示一下该类的使用方法即可。
1 package p11.DataStreamDemo.p01.DataStreamDemo; 2 3 import java.io.DataInputStream; 4 import java.io.DataOutputStream; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.FileNotFoundException; 8 import java.io.FileOutputStream; 9 import java.io.IOException; 10 11 public class DataStreamDemo1 { 12 public static void main(String args[]) throws IOException 13 { 14 // DataBaseInputAndOutputStream(); 15 InputFromKeyboardAndOutputToFile(); 16 } 17 18 public static void InputFromKeyboardAndOutputToFile() 19 throws FileNotFoundException, IOException { 20 FileOutputStream fos=new FileOutputStream(new File("data.data")); 21 DataOutputStream dos=new DataOutputStream(fos); 22 dos.writeInt(128); 23 dos.writeBoolean(true); 24 dos.writeChar('a'); 25 FileInputStream fis=new FileInputStream(new File("data.data")); 26 DataInputStream dis=new DataInputStream(fis); 27 System.out.println(dis.readInt()); 28 System.out.println(dis.readBoolean()); 29 System.out.println(dis.readChar()); 30 dis.close(); 31 dos.close(); 32 } 33 34 public static void DataBaseInputAndOutputStream() throws IOException { 35 DataInputStream dis=new DataInputStream(System.in); 36 DataOutputStream dos=new DataOutputStream(System.out); 37 byte buf[]=new byte[1024]; 38 int length=dis.read(buf); 39 dos.write(buf,0,length); 40 dis.close(); 41 dos.close(); 42 } 43 }
九、字节数组流:ByteArrayInputStream、ByteArrayOutputStream
这两个类和其它类不同之处在于它操作的对象是内存,所以其close方法并不会关闭底层资源(关闭流无效),不声明也没有问题,但是一旦声明就必须抛出或者捕捉。
java.lang.Object
|--java.io.InputStream
|--java.io.ByteArrayInputStream
java.lang.Object
|--java.io.OutputStream
|--java.io.ByteArrayOutputStream
代码演示:
1 package p12.ByteArrayStreamDemo.p01.ByteArrayStreamDemo; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.IOException; 6 7 public class ByteArrayStreamDemo { 8 9 public static void main(String[] args) throws IOException { 10 11 // ByteArrayOutputStreamDemo(); 12 ByteArrayInputStreamDemo(); 13 } 14 15 private static void ByteArrayInputStreamDemo() throws IOException { 16 byte[] buff = new byte[] { 2, 15, 67, -1, -9, 9 }; 17 ByteArrayInputStream in = new ByteArrayInputStream(buff); 18 int data = in.read(); 19 while (data != -1) { 20 System.out.println(data + " "); 21 data = in.read(); 22 } 23 in.close();// ByteArrayInputSystem 的close()方法实际上不执行任何操作 24 } 25 26 public static void ByteArrayOutputStreamDemo() throws IOException { 27 ByteArrayOutputStream baos=new ByteArrayOutputStream(); 28 baos.write("你好".getBytes()); 29 System.out.println(baos.toString()); 30 } 31 32 }