第七章 IO/输入与输出
● File类
● RandomAccessFile类
● 各种节点流类
● 字符编码
● 各种过滤流与包装类
● IO类的相关应用
File类
● File类是IO包中唯一代表磁盘文件本身信息的类,而不是文件中的内容。
● File类定义了一些与平台无关的方法来操作文件,创建、删除文件和重命名文件。
● Java中的目录被当作一种特殊的文件使用,list方法可以返回目录中的所有子目录和文件名。
● 在Unix下的路径分隔符为(/),在Dos下的路径分隔符为(\),Java可以正确处理Unix和Dos的路径分隔符。
RandomAccessFile类
● 此类的实例支持对随机访问文件的读取和写入。
● RandomAccessFile类提供了众多的文件访问方法。
● RandomAccessFile类支持“随机访问”方式。
● RandomAccessFile类在随机(相对顺序而言)读写等长记录格式的文件时有很大的优势。
● RandomAccessFile类公限于操作文件,不能访问其他的IO设备,如网络、内存映像等。
● 两种构造方法:
nNew RandomAccessFile(f,”rw”);//读写方式
nNew RandomAccessFile(f,”r”);//只读方式
编程实例:往文件中写入三名员工的信息,每个员工含有姓名和年龄两个字段,然后按照第二名、等一名、第三名的先后顺序读出员工信息。
public class Employee
{
public String name = "";
public int age = 0;
public static final int LEN = 8;
public Employee(String name, int age)
{
// TODO: Add your code here
if(name.length()>LEN)
{
name=name.substring(0,LEN);
}
else
{
while(name.length()<LEN)
{
name+="\u0000";
}
}
this.name=name;
this.age=age;
}
}
import java.io.*;
public class TestRandomAccessFile
{
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
Employee em1=new Employee("zhangsan",18);
Employee em2=new Employee("李四",26);
Employee em3=new Employee("wangwu",22);
RandomAccessFile raf=new RandomAccessFile("employee.txt","rw");
raf.writeChars(em1.name);
raf.writeInt(em1.age);
raf.writeChars(em2.name);
raf.writeInt(em2.age);
raf.writeChars(em3.name);
raf.writeInt(em3.age);
RandomAccessFile raf1=new RandomAccessFile("employee.txt","r");
String strName="";
raf1.skipBytes(Employee.LEN*2+4);
for(int i=0;i<Employee.LEN;i++)
{
strName+=raf1.readChar();
}
System.out.println(strName.trim()+":"+raf1.readInt());
raf1.seek(0);
strName="";
for(int i=0;i<Employee.LEN;i++)
{
strName+=raf1.readChar();
}
System.out.println(strName.trim()+":"+raf1.readInt());
raf1.skipBytes(Employee.LEN*2+4);
strName="";
for(int i=0;i<Employee.LEN;i++)
{
strName+=raf1.readChar();
}
System.out.println(strName.trim()+":"+raf1.readInt());
}
}
节点流
● 理解流的概念
● InputStream与OutputStream类
● FileInputStream与FileOutputStream类
● Reader与Writer类
● PipedInputStream与PipedOutputStream类
● ByteArrayInputStream与ByteArrayOutputStream类
● 重视程序代码的复用性
理解流的概念
● 流是字节序列的抽象概念。
流机制是JAVA及C++中的一个重要的机制,通过流能使我们能自由地控制包括文件,内存,IO设备等等中的数据的流向。如:可以从文件输入流中获取数据,经处理后再通过网络输出流把数据输出到网络设备上;或利用对像输出流把一个程序中的对象输出到一个格式流文件中,并通过网络流对象将其输出到远程机器上,然后在远程机器上利用对象输入流将对象还原。像这些机制是别的高级语言所不能比拟的。但要掌握好这些流对象,流的概念是很重要的。流是一串连续不继的数据的集合,就像水管一里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段一段地向数据流管道中写入数据,这些数据段会按先向顺序形成一个长的数据流。对数据的读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。在JAVA中的流按流动方向可以分为输入流及输出流两种,按流的处理位置可分为节点流和包装流。输入流、输出流是以程序为参考点来说的,所谓的输入流就是程序从中获取数据的流,输出流就是程序要其写数据的流。在输入流的一边是程序,而另一边就是流的数据源。而输出流的一边则目标,一边就是程序。(如下图)
● 文件是数据的静态存储形式,而流是指数据传输时的形态。
● 流类分为两个大类:节点流类和过滤流类(也叫处理流类)。
InputStream
程序可以从中连续读取字节的对象叫输入流,在Java中,用InputStream类来描述所有输入流的抽象概念。
InputStream类的方法:
方法摘要
|
int
|
available () 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。
|
void
|
close () 关闭此输入流并释放与该流关联的所有系统资源。
|
void
|
mark (int readlimit) 在此输入流中标记当前的位置。readlimit 参数告知此输入流在标记位置失效之前允许读取的字节数
|
boolean
|
markSupported () 测试此输入流是否支持 mark 和 reset 方法。
|
abstract int
|
read () 从输入流中读取数据的下一个字节。
|
int
|
read (byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
|
int
|
read (byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。
|
void
|
reset () 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
|
long
|
skip (long n) 跳过和丢弃此输入流中数据的 n 个字节。
|
OutputStream
程序可以从向其中连续写入字节的对象叫输出流,在Java中,用OutputStream类来描述所有输出流的抽象概念。此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
OutputStream类的方法:
方法摘要
|
void
|
close () 关闭此输出流并释放与此流有关的所有系统资源。
|
void
|
flush () 刷新此输出流并强制写出所有缓冲的输出字节。
|
void
|
write (byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。
|
void
|
write (byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
|
abstract void
|
write (int b) 将指定的字节写入此输出流。将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 OutputStream 的子类必须提供此方法的实现
|
FileInputStream与FileOutputStream类
● FileInputStream与FileOutputStream类分别用来创建磁盘文件的输入流和输出流对象,通过它们的构造函数来指定文件路径和文件名。
● 创建FileInputStream实例对象时,指定的文件应当是存在和可读的。创建FileOutputStream实例对象时,如果指定的文件已经存在,这个文件中的原来内容将被覆盖清除。
● 对同一个磁盘文件创建FileInputStream对象的两种方式:
u FileInputStream inOne=new FileInputStream(“hello.test”);
u File f=new File(“hello.test”);
FileInputStream inTwo=new FileInputStream(f);
● 创建FileOutputStream实例对象时,可以指定还不存在的文件名,不能指定一个已被其他程序打开了的文件。
编程举例:用FileOutputStream类向文件中写入一个串字符,然后用FileInputStream读出写入的内容。
import java.io.*;
public class TestInputStream
{
public static void main(String[] args)
{
// TODO: Add your code here
try
{
FileOutputStream outOne=new FileOutputStream("test.txt");
outOne.write("Hello World".getBytes());
outOne.close();
FileInputStream inOne=new FileInputStream("test.txt");
byte[]buf=new byte[1024];
int len=inOne.read(buf);
System.out.println(new String(buf,0,len));
inOne.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Reader与Writer类
● Reader和Writer是所有字符流类的抽象基类,用于简化对字符串的输入输出编程,即用于读写文本数据。
● 二进制文件和文本文件的区别。
如果一个文件专用于存储文本字符,而没有包含字符以外的其它数据,就称为文本文件;除此之外的文件就称为二进制文件。
Reader和Writer类主要用于读取文本格式的内容,而InputStream和OutputStream及它们的子类主要用于读取二进制文件。
编程举例:用FileWriter类向文件中写入一个字符串,然后用FileReader读出写入的内容。
import java.io.*;
public class TestFileRead
{
public static void main(String[] args)
{
// TODO: Add your code here
try
{
FileWriter fw=new FileWriter("file.txt");
fw.write("Hello World!");
fw.close();
FileReader fr=new FileReader("file.txt");
char []buf=new char[1024];
int len=fr.read(buf);
System.out.println(new String(buf,0,len));
fr.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
从输入方法中获取字符串到文件:
import java.io.*;
import java.util.*;
public class TestFileWriter
{
public static void main(String[] args)
{
// TODO: Add your code here
int ch=0;
String str=null;
StringBuffer sb=new StringBuffer();
System.out.println("请输入一串字符:");
try
{
while(true)
{
ch=System.in.read();
if(ch=='\r'||ch=='\n')
{
break;
}
else
{
sb.append((char)ch);
}
str=sb.toString();
}
File f=new File("test.txt");
FileWriter fw=new FileWriter(f);
fw.write(str);
fw.close();
FileReader fr=new FileReader(f);
char []buf=new char[1024];
int len=fr.read(buf);
System.out.println(new String(buf,0,len));
fr.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
PipedInputStream与PipedOutputStream类
● PipedInputStream类与PipedOutputStream类用于在应用程序中的创建管道通信。
这两个类主要用来完成线程之间的通信,一个线程中的PipedInputStream对象可以从另外一个线程中的PipedOutputStream对象中读取数据。
● PipedInputStream类与PipedOutputStream类编程实例:
import java.io.*;
public class Sender implements Runnable
{
private PipedOutputStream out=new PipedOutputStream();
public PipedOutputStream getOutPutStream()
{
return out;
}
public void run()
{
try
{
out.write("Hello World!".getBytes());
out.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
import java.io.*;
public class Receiver implements Runnable
{
private PipedInputStream in=new PipedInputStream();
public PipedInputStream getInputStream()
{
return in;
}
public void run()
{
try
{
byte []buf=new byte[1024];
int len=in.read(buf);
System.out.println("The Follow message comes from Sender:\n"+
new String(buf,0,len));
in.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
import java.io.*;
public class PipedStreamTest
{
public static void main(String[] args)
{
// TODO: Add your code here
Sender ts=new Sender();
Receiver tr=new Receiver();
PipedOutputStream out=ts.getOutPutStream();
PipedInputStream in=tr.getInputStream();
try
{
out.connect(in);
}
catch(Exception e)
{
e.printStackTrace();
}
new Thread(ts).start();
new Thread(tr).start();
}
}
● PipedWriter和PipedReader类。
● 使用管道流类,可以实现各个程序模块之间的松耦合通信。
ByteArrayInputStream与ByteArrayOutputStream类
● ByteArrayInputStream与ByteArrayOutputStream类,用于以IO流的方式来完成对字节数组内容的读写,来支持类似内存虚拟文件或者内存映像文件的功能。ByteArrayInputStream
是把字节数组当成源的输入流。该类有两个构造函数,每个构造函数需要一个字节数组提供数据源。(包含一个内部缓冲区,该缓冲区存储从流中读取的字节。内部计数器跟踪 read
方法要提供的下一个字节。)ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个字节数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray()
和 toString()
检索数据。
●
ByteArrayInputStream类的两个构造函数:
● ByteArrayOutputStream类的两个构造函数:
方法摘要
|
void
|
close () 关闭 ByteArrayOutputStream 无效。
|
byte[]
|
toByteArray () 创建一个新分配的字节数组。
|
String
|
toString () 将缓冲区的内容转换为字符串,根据平台的默认字符编码将字节转换成字符。
|
String
|
toString (String enc) 将缓冲区的内容转换为字符串,根据指定的字符编码将字节转换成字符。
|
void
|
write (byte[] b, int off, int len) 将指定字节数组中从偏移量 off 开始的 len 个字节写入此字节数组输出流。
|
void
|
write (int b) 将指定的字节写入此字节数组输出流。
|
void
|
writeTo (OutputStream out) 将此字节数组输出流的全部内容写入到指定的输出流参数中,这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。
|
● 编程举例:编写一个函数,把输入流中的所有英文字母变成大写字母,然后将结果写入到一个输出流对象中。用这个函数来将一个字符串的所有字符转换成大写。
方法一:
import java.io.*;
public class ByteArrayStream
{
public static void main(String[] args)
{
// TODO: Add your code here
String str="abcdefg";
byte []source=str.getBytes();
ByteArrayInputStream in=new ByteArrayInputStream(source);
ByteArrayOutputStream out=new ByteArrayOutputStream();
getUpperChar(in,out);
byte []result=new byte[1024];
result=out.toByteArray();
System.out.println(new String(result));
System.out.println(out.toString());
try
{
out.writeTo(new FileOutputStream("test.txt"));
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public static void getUpperChar(InputStream in,OutputStream out)
{
int ch=0;
try
{
while((ch=in.read())!= -1)
{
int upperCh=Character.toUpperCase(ch);
out.write(upperCh);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
方法二:
import java.io.*;
public class ByteArrayStream
{
public static void main(String[] args)
{
// TODO: Add your code here
byte []buf="hello".getBytes();
ByteArrayInputStream bis=new ByteArrayInputStream(buf);
ByteArrayOutputStream bos=new ByteArrayOutputStream();
changeChar(bis,bos);
changeChar(System.in,System.out);
}
public static void changeChar(InputStream in,OutputStream out)
{
try
{
int ch=0;
int pos=0;
byte []buf=new byte [1024];
while((ch=in.read())!=-1)
{
int charUPP=Character.toUpperCase(ch);
out.write(buf[pos++]=(byte)charUPP);
}
System.out.println(new String(buf,0,pos));
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
● StringReader类和StringWriter类以字符IO流的方式处理字符串
StringReader类的构造函数:
StringWriter类的构造函数:
StringWriter类的方法:
例:用StringReader和StringWriter改写上面的例子程序
import java.io.*;
public class TestStringReader
{
public static void main(String[] args)
{
// TODO: Add your code here
String strTemp=new String("hello world.");
StringReader sr=new StringReader(strTemp);
StringWriter sw=new StringWriter();
changUpper(sr,sw);
System.out.println(sw.toString());
}
public static void changUpper(Reader re,Writer wr)
{
int ch=0;
try
{
while((ch=re.read())!=-1)
{
int upperCh=Character.toUpperCase(ch);
wr.write(upperCh);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
重视IO程序代码的复用
● System.in连接到键盘,是InputStream类的实例对象。System.out连接到显示器,是PrintStream类的实例对象。
● 不管各种底层物理设备用什么方式实现数据的终止点,InputStream的read方法总是返回-1来表示输入流的结束。
● 在Windows下按下Ctrl+Z组合键可以产生键盘输入流的结束标记,在Linux下,则是按下Ctrl+D组合键来产生键盘输入流的结束标记。
编程举例:借助上一页编写的函数,将键盘国输入的内容转变成大写字母后打印在屏幕上。
import java.io.*;
public class TestSystem
{
public static void main(String[] args)
{
// TODO: Add your code here
System.out.println("输入将变成大写字母的小写字母:");
changUpper(System.in,System.out);
}
public static void changUpper(InputStream in,OutputStream out)
{
int ch=0;
try
{
while((ch=in.read())!=-1)
{
int upperCh=Character.toUpperCase(ch);
out.write(upperCh);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
建议:要编写从键盘上连续读取一大段数据时,应尽量将读取数据的过程放在函数中完成,使用-1来作为键盘输入的结束点。在该函数中编写的程序代码不应直接使用System.in读取数据,而是用一个InputStream类型形式参数对象来读取数据,然后将System.in作为实参传递给InputStream类型的形式参数来调用函数。
字符编码
● 计算机里只有数字,计算机软件里的一切都是用数字来表示的,屏幕上显示的一个个字符也不例外。
● 字符a对应数字97,字符b对应数字为98等,这种字符与数字对应的编码规则被称为ASCII(美国标准信息交换码)码。ASCII的最高bit位都是0,也就是说这些数字都在0到127之间。
● .中国大陆将每一个中文字符才用两个字节的数字来表示,中文字符的每个字节的最高bit都是1,中国大陆为每个中文字符制定的编码规则称为GB2312(国标码)。
● 在GB2312的基础上,对更多的中文字符(包括繁体)进行了编码,新的编码规则称为GBK。
● 在中国大陆使用的计算机系统上,GBK和GB2312就被称为系统的本地字符集。
● “中国”的“中”字,在中国大陆的编码是十六进制的D6D0,而在中国台湾的编码中十六进制的A4A4,台湾地区对中文字符集的编码规则称为BIG5(大五码)。
● 在一个国家的本地系统中出现的一个字符,通过电子邮件传送到另外一个国家的本地化系统中,看到的就不是那个原始字符了,而是另外那个国家的一个字符或乱码。
Unicode编码
● ISO(国际标准化组织)将全世界所有的符号进行了统一编码,称之为Unicode编码。
● “中”这个符号,在全世界的任何角落始终对应的都是一个十六进制的数字4e2d。
● 如果所有的计算机系统都使用Unicode编码,在中国大陆的本地化系统中显示的“中”这个符号,发送到伊拉克的本地化系统中,显示的仍然是“中”这个符号。
● Unicode编码的字符都占用两个字节的大小,对于ASCII码所表示的字符,只是简单地在ASCII码原来占用的一个字节前面,增加一个所有bits为0的字节。
● Unicode只占用两个字节,在全世界范围内所表示的字符个数不会超过2的16次方(65536),实际上Unicode编码中还保留了两千多个数值没有用于字符编码。
● 在相当长的一段时期内,本地化字符编码将与Unicode编码共存。
● Java中的字符使用的都是Unicode编码,Java在通过Unicode保证跨平台特性的前提下,也支持本地平台字符集。
UTF-8编码
● ASCII码字符保持原样,仍然只占用一个字节,对于其它国家的字符,UTF-8使用两个或三个字节表示。使用UTF-8编码的文件,通常都要用EF BB BF作为文件开关的三个字节数据。
字符编码的操作体验
● 查看中文字符的GB2312码
● 查看中文字符的UTF-8码
● 查看中文字符的Unicode码
● 在Windows记事本程序中用不同的编码格式存储文本文件
UTF-8的优点:
● 不出现内容为0x00字节
● 便于应用程序检测数据在传输过程中是否发生了错误。
● 直接处理使用ASCII码的英文文档。
UTF-8的缺点:
● 有的字符要占用3个字节。
UTF-16编码
● UTF-16编码在Unicode基础上进行了一些细节上的扩充,增加了对Unicode编码没有包括的那些字符的表示方式。
● UTF-16对Unicode的扩充并没有影响Unicode编码所包括的那些字符,只是增加了对Unicode编码没有包括的那些字符的表示方式,一个使用Unicode编码的字符就是UTF-16格式的。
● Unicode编码将0xD800-0xDFFF区间的数值保留出来,UTF-16扩充的字符,占用四个字节,前面两个字节的数值为0xD800-0xD8FF之间,后面两个字节的数值为0xDC00-0xDFFF之间。
● 在不同体系结构的计算机系统中,UTF-16编码的Unicode字符在内存中的字节存储顺序是不同的。
● 对于0x1234这样一个双字节数据,使用Little-Endian和Big-Endian两种方式在内存中存储的格式如图所示:
● 如果文件以0xFE 0xFF这两个字节开头,则表明文本的其余部分是Big-Endian的UTF-16编码;如果文件以0xFF 0xFE两个字节开头,则表明文本的其余部分是Little-Endian的UTF-16编码。
字符编码的编程体验
● 打印中文字符的Unicode码
● 打印中文字符的GB2312码
● 验证写入到屏幕输出流的中文字符所采用的编码
● 查看系统的缺省编码
● 修改系统的缺省编码
● 验证从键盘输入流中读取的中文字符所采用的编码
● 研究GB2312码到Unicode码的解码过程
● GB2312码的中文字符被按照ISO8859-1字符集解码生成了Unicode字符串后,如何将这个字符串转换成正确Unicode编码字符串
public class TestCode
{
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
String str="中国";
System.out.println("打印"+"\""+str+"\""+"每个字符Unicode码:");
for(int i=0;i<str.length();i++)
{
System.out.println(Integer.toHexString((int)str.charAt(i)));
}
byte []buf=str.getBytes(/*"GB2312"*/);
System.out.println("打印"+"\""+str+"\""+"每个字符GB2312码:");
for(int i=0;i<buf.length;i++)
{
System.out.println(Integer.toHexString(buf[i]));
}
System.out.println("验证写入到屏幕输出流的字符"+"\""+str+"\""+"所采用的编码:");
for(int i=0;i<buf.length;i++)
{
System.out.write(buf[i]);
}
System.out.println();
/*System.out.write(buf);
System.out.println();*/
System.out.println("查看系统的缺省编码:");
System.getProperties().list(System.out);
System.out.println("修改系统的缺省编码");
//System.setProperty("file.encoding","ISO8859-1");
System.getProperties().put("file.encoding","ISO8859-1");
System.getProperties().list(System.out);
}
}
public class TestDecode
{
public static void main(String[] args) throws Exception
{
int ch=0;
byte []buf=new byte [1024];
int pos=0;
String strInfo=null;
System.setProperty("file.encoding","iso8859-1");
System.out.println("请输入一串中文字符:");
while(true)
{
ch=System.in.read();
System.out.println(Integer.toHexString(ch));
switch(ch)
{
case '\r':
break;
case '\n':
strInfo=new String(buf,0,pos/*,"Gb2312"*/);
for(int i=0;i<strInfo.length();i++)
{
System.out.println(Integer.toHexString(strInfo.charAt(i)));
}
System.out.println("中国");
System.out.println(new String(strInfo.getBytes("ISO8859-1"),"GBK"));
pos=0;
break;
default:
buf[pos++]=(byte)ch;
}
}
}
}
过滤流与包装类
● 包装类的概念与作用
● BufferedInputStream与BufferedOutputStream类
● DataInputStream与DataOutputStream类
● PrintStream类
● ObjectInputStream与ObjectOutputStream 类
● 字节流与字符流的转换
包装类的概念与作用
DataInputStream与DataOutputStream类
● DataOutputStream类的提供了往各种输出流对象中写入各种类型的数据的方法。传递一个FileOutputStream输出流对象给DataOutputStream实例对象和调用DataOutputStream实例对象的方法。
● DataOutputStream并没有对应到任何具体的流设备,一定要给它传递一个对应具体流设备的输出流对象。完成类似DataOutputStream功能的类就是一个包装类,也叫过滤类或处理流类。
构造方法摘要
|
DataInputStream (InputStream in) 使用指定的底层 InputStream 创建一个 DataInputStream。
|
|
方法摘要
|
int
|
read (byte[] b) 从包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b 中。
|
int
|
read (byte[] b, int off, int len) 从包含的输入流中将最多 len 个字节读入一个 byte 数组中。
|
boolean
|
readBoolean () 参见 DataInput 的 readBoolean 方法的常规协定。
|
byte
|
readByte () 参见 DataInput 的 readByte 方法的常规协定。
|
char
|
readChar () 参见 DataInput 的 readChar 方法的常规协定。
|
double
|
readDouble () 参见 DataInput 的 readDouble 方法的常规协定。
|
float
|
readFloat () 参见 DataInput 的 readFloat 方法的常规协定。
|
void
|
readFully (byte[] b) 参见 DataInput 的 readFully 方法的常规协定。
|
void
|
readFully (byte[] b, int off, int len) 参见 DataInput 的 readFully 方法的常规协定。
|
int
|
readInt () 参见 DataInput 的 readInt 方法的常规协定。
|
String
|
readLine () 已过时。 该方法无法将字节正确转换为字符。从 JDK 1.1 开始,读取文本行的首选方法是使用 BufferedReader.readLine() 方法。使用 DataInputStream 类读取文本行的程序可以改为使用 BufferedReader 类,只要将以下形式的代码:
DataInputStream d = new DataInputStream(in);
替换为:
BufferedReader d
= new BufferedReader(new InputStreamReader(in));
|
long
|
readLong () 参见 DataInput 的 readLong 方法的常规协定。
|
short
|
readShort () 参见 DataInput 的 readShort 方法的常规协定。
|
int
|
readUnsignedByte () 参见 DataInput 的 readUnsignedByte 方法的常规协定。
|
int
|
readUnsignedShort () 参见 DataInput 的 readUnsignedShort 方法的常规协定。
|
String
|
readUTF () 参见 DataInput 的 readUTF 方法的常规协定。
|
static String
|
readUTF (DataInput in) 从流 in 中读取用 UTF-8 修改版格式编码的 Unicode 字符格式的字符串;然后以 String 形式返回此字符串。
|
int
|
skipBytes (int n) 参见 DataInput 的 skipBytes 方法的常规协定。
|
|
|
|
构造方法
|
DataOutputStream (OutputStream out) 创建一个新的数据输出流,将数据写入指定基础输出流。
|
|
方法摘要
|
void
|
flush () 清空此数据输出流。
|
int
|
size () 返回计数器 written 的当前值,即到目前为止写入此数据输出流的字节数。
|
void
|
write (byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入基础输出流。
|
void
|
write (int b) 将指定字节(参数 b 的八个低位)写入基础输出流。
|
void
|
writeBoolean (boolean v) 将一个 boolean 值以 1-byte 值形式写入基础输出流。
|
void
|
writeByte (int v) 将一个 byte 值以 1-byte 值形式写出到基础输出流中。
|
void
|
writeBytes (String s) 将字符串按字节顺序写出到基础输出流中。
|
void
|
writeChar (int v) 将一个 char 值以 2-byte 值形式写入基础输出流中,先写入高字节。
|
void
|
writeChars (String s) 将字符串按字符顺序写入基础输出流。
|
void
|
writeDouble (double v) 使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。
|
void
|
writeFloat (float v) 使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后将该 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。
|
void
|
writeInt (int v) 将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。
|
void
|
writeLong (long v) 将一个 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。
|
void
|
writeShort (int v) 将一个 short 值以 2-byte 值形式写入基础输出流中,先写入高字节。
|
void
|
writeUTF (String str) 以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。
|
BufferedInputStream与BufferedOutputStream类
● 缓冲流为I/O流增加了内存缓冲区,增加缓冲区有两个基本目的:
n允许Java程序一次不只操作一个字节,这样提高了程序的性能。
n由于有了缓冲区,使得在流上执行skip、mart和reset方法都成为可能。、
● BufferedInputStream与BufferedOutputStream是java提供的两个缓冲区包装类,不管底层系统是否使用了缓冲区,这两个类在自己的实例对象中创建缓冲区。这种缓冲区与底层系统提供的缓冲区的区别:底层系统提供的系统缓冲区直接与目标设备交换数据,而包装类提供的缓冲区需要调用包装类所包装的输出流对象将缓冲区的数据写入到目标或底层缓冲区中,或者调用包装类所包装的输入流对象从目标设备或底层缓冲区中读取数据到包装类的缓冲区中。底层缓冲区可以一次从硬盘读取大量数据或者一次向硬盘写入大量数据,而BufferedInputStream一次只能从底层缓冲区中读取一个数据,将读取到数据缓存到BufferedOutputStream缓冲区中,然后一次读取多个数据。
构造方法摘要
|
BufferedInputStream (InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in ,以便将来使用。
|
|
BufferedInputStream (InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in ,以便将来使用。
|
|
方法摘要
|
int
|
available () 返回可以从此输入流读取(或跳过)、且不受此输入流接下来的方法调用阻塞的估计字节数。
|
void
|
close () 关闭此输入流并释放与该流关联的所有系统资源。
|
void
|
mark (int readlimit) 参见 InputStream 的 mark 方法的常规协定。
|
boolean
|
markSupported () 测试此输入流是否支持 mark 和 reset 方法。
|
int
|
read () 参见 InputStream 的 read 方法的常规协定。
|
int
|
read (byte[] b, int off, int len) 从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。
|
void
|
reset () 参见 InputStream 的 reset 方法的常规协定。
|
long
|
skip (long n) 参见 InputStream 的 skip 方法的常规协定。
|
方法摘要
|
void
|
flush () 刷新此缓冲的输出流。
|
void
|
write (byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。
|
void
|
write (int b) 将指定的字节写入此缓冲的输出流
|
编程实例:分别使用DataOutputStream类的writerUTF、writerBytes和writerChars方法,比较这几个方法的差异。程序中所使用的流栈如下:
● 关闭流栈中的最上层的流对象,将会自动关闭流栈中的所有底层流对象
import java.io.*;
public class DataStreamTest
{
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
FileOutputStream fos=new FileOutputStream("test.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
DataOutputStream dos=new DataOutputStream(bos);
dos.writeUTF("ab中国");
dos.writeChars("ab中国");
dos.writeBytes("ab中国");
dos.close();
FileInputStream fis=new FileInputStream("test.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
DataInputStream dis=new DataInputStream(bis);
System.out.println(dis.readUTF());
byte []buf=new byte [1024];
int len=dis.read(buf);
System.out.println(len);
System.out.println(new String(buf,0,len));
System.out.println();
dis.close();
}
}
BufferedReader和BufferedWriter类
BufferedReader从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如:
BufferedReader in = new BufferedReader(new FileReader("foo.in"));
将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。
BufferedWriter将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,
PrintWriter out
= new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。
BufferedReader的readLine方法可以一次读取一行文本,BufferedWriter的newLine方法可以向字符流中写入不同操作系统下的换行符。
PrintStream类
PrintStream
为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream
永远不会抛出 IOException
;而是,异常情况仅设置可通过 checkError
方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream
;这意味着可在写入 byte 数组之后自动调用 flush
方法,可调用其中一个 println
方法,或写入一个换行符或字节 ('\n'
)。
PrintStream
打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter
类。
● PrintStream类提供了一系列的print和println方法,可以将基本数据类型的数据格式化成字符串输出。
● 格式化输出是什么意思?例如:97被格式化输出的实际字节数据为0x39和0x37。
构造方法摘要
|
PrintStream (File file) 创建具有指定文件且不带自动行刷新的新打印流。
|
|
PrintStream (File file, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
|
|
PrintStream (OutputStream out) 创建新的打印流。
|
|
PrintStream (OutputStream out, boolean autoFlush) 创建新的打印流。
|
|
PrintStream (OutputStream out, boolean autoFlush, String encoding) 创建新的打印流。
|
|
PrintStream (String fileName) 创建具有指定文件名称且不带自动行刷新的新打印流。
|
|
PrintStream (String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
|
|
方法摘要
|
PrintStream
|
append (char c) 将指定字符添加到此输出流。
|
PrintStream
|
append (CharSequence csq) 将指定字符序列添加到此输出流。
|
PrintStream
|
append (CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此输出流。
|
boolean
|
checkError () 刷新流并检查其错误状态。
|
protected void
|
clearError () 清除此流的内部错误状态。
|
void
|
close () 关闭流。
|
void
|
flush () 刷新该流的缓冲。
|
PrintStream
|
format (Locale l, String format, Object... args) 使用指定格式字符串和参数将格式化字符串写入此输出流中。
|
PrintStream
|
format (String format, Object... args) 使用指定格式字符串和参数将格式化字符串写入此输出流中。
|
void
|
print (boolean b) 打印 boolean 值。
|
void
|
print (char c) 打印字符。
|
void
|
print (char[] s) 打印字符数组。
|
void
|
print (double d) 打印双精度浮点数。
|
void
|
print (float f) 打印浮点数。
|
void
|
print (int i) 打印整数。
|
void
|
print (long l) 打印 long 整数。
|
void
|
print (Object obj) 打印对象。
|
void
|
print (String s) 打印字符串。
|
PrintStream
|
printf (Locale l, String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此输出流的便捷方法。
|
PrintStream
|
printf (String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此输出流的便捷方法。
|
void
|
println () 通过写入行分隔符字符串终止当前行。
|
void
|
println (boolean x) 打印 boolean 值,然后终止行。
|
void
|
println (char x) 打印字符,然后终止该行。
|
void
|
println (char[] x) 打印字符数组,然后终止该行。
|
void
|
println (double x) 打印 double,然后终止该行。
|
void
|
println (float x) 打印 float,然后终止该行。
|
void
|
println (int x) 打印整数,然后终止该行。
|
void
|
println (long x) 打印 long,然后终止该行。
|
void
|
println (Object x) 打印 Object,然后终止该行。
|
void
|
println (String x) 打印 String,然后终止该行。
|
protected void
|
setError () 将该流的错误状态设置为 true 。
|
void
|
write (byte[] buf, int off, int len) 将 len 字节从指定的初始偏移量为 off 的 byte 数组写入此流。
|
void
|
write (int b) 将指定的字节写入此流。
|
|
|
|
PrintWriter类
向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream
中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。与 PrintStream
类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 checkError()
是否出现错误
● 与PrintStream对应的PrintWriter类,即使遇到了文本换行标识符(\n),PrintWriter类也不会自动清空缓冲区。
● PrintWriter的println方法能根据操作系统的不同而生成相应的文本换行标识符。在Windows下的文本换行标识符是”\r\n”,而Linux下的文本换行标识符是”\n”。
构造方法摘要
|
PrintWriter (File file) 使用指定文件创建不具有自动行刷新的新 PrintWriter。
|
|
PrintWriter (File file, String csn) 创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。
|
|
PrintWriter (OutputStream out) 根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。
|
|
PrintWriter (OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建新的 PrintWriter。
|
|
PrintWriter (String fileName) 创建具有指定文件名称且不带自动行刷新的新 PrintWriter。
|
|
PrintWriter (String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。
|
|
PrintWriter (Writer out) 创建不带自动行刷新的新 PrintWriter。
|
|
PrintWriter (Writer out, boolean autoFlush) 创建新 PrintWriter。
|
|
方法摘要
|
PrintWriter
|
append (char c) 将指定字符添加到此 writer。
|
PrintWriter
|
append (CharSequence csq) 将指定的字符序列添加到此 writer。
|
PrintWriter
|
append (CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此 writer。
|
boolean
|
checkError () 如果流没有关闭,则刷新流且检查其错误状态。
|
protected void
|
clearError () 清除此流的错误状态。
|
void
|
close () 关闭该流并释放与之关联的所有系统资源。
|
void
|
flush () 刷新该流的缓冲。
|
PrintWriter
|
format (Locale l, String format, Object... args) 使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。
|
PrintWriter
|
format (String format, Object... args) 使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。
|
void
|
print (boolean b) 打印 boolean 值。
|
void
|
print (char c) 打印字符。
|
void
|
print (char[] s) 打印字符数组。
|
void
|
print (double d) 打印 double 精度浮点数。
|
void
|
print (float f) 打印一个浮点数。
|
void
|
print (int i) 打印整数。
|
void
|
print (long l) 打印 long 整数。
|
void
|
print (Object obj) 打印对象。
|
void
|
print (String s) 打印字符串。
|
PrintWriter
|
printf (Locale l, String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。
|
PrintWriter
|
printf (String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。
|
void
|
println () 通过写入行分隔符字符串终止当前行。
|
void
|
println (boolean x) 打印 boolean 值,然后终止该行。
|
void
|
println (char x) 打印字符,然后终止该行。
|
void
|
println (char[] x) 打印字符数组,然后终止该行。
|
void
|
println (double x) 打印双精度浮点数,然后终止该行。
|
void
|
println (float x) 打印浮点数,然后终止该行。
|
void
|
println (int x) 打印整数,然后终止该行。
|
void
|
println (long x) 打印 long 整数,然后终止该行。
|
void
|
println (Object x) 打印 Object,然后终止该行。
|
void
|
println (String x) 打印 String,然后终止该行。
|
protected void
|
setError () 指示已发生错误。
|
void
|
write (char[] buf) 写入字符数组。
|
void
|
write (char[] buf, int off, int len) 写入字符数组的某一部分。
|
void
|
write (int c) 写入单个字符。
|
void
|
write (String s) 写入字符串。
|
void
|
write (String s, int off, int len) 写入字符串的某一部分。
|
|
|
|
ObjectInputStream
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。ObjectOutputStream和ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
readObject
方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。在Java中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。可以使用 DataInput上的适当方法从流读取基本数据类型。
默认情况下,对象的反序列化机制会将每个字段的内容恢复为写入时它所具有的值和类型。反序列化进程将忽略声明为瞬态或静态的字段。对其他对象的引用使得根据需要从流中读取这些对象。使用引用共享机制能够正确地恢复对象的图形。反序列化时始终分配新对象,这样可以避免现有对象被重写。
读取对象类似于运行新对象的构造方法。为对象分配内存并将其初始化为零 (NULL)。为不可序列化类调用无参数构造方法,然后从以最接近 java.lang.object 的可序列化类开始和以对象的最特定类结束的流恢复可序列化类的字段。
例如,要从由 ObjectOutputStream 中的示例写入的流读取:
FileInputStream fis = new FileInputStream("t.tmp");
ObjectInputStream ois = new ObjectInputStream(fis);
int i = ois.readInt();
String today = (String) ois.readObject();
Date date = (Date) ois.readObject();
ois.close();
类控制实现 java.io.Serializable 或 java.io.Externalizable 接口时的序列化方式。
实现 Serializable 接口允许对象序列化,以保存和恢复对象的全部状态,并且允许类在写入流时的状态和从流读取时的状态之间变化。它自动遍历对象之间的引用,保存和恢复全部图形。
● ObjectInputStream和ObjectOutputStream这两个包装类,用于从底层输入流中读取对象类型的数据和将对象类型的数据写入到底层输出流。
● ObjectInputStream和ObjectOutputStream类所读写的对象必须实现了Serializable接口。对象中的transient(临时的)和static类型的成员变量不会被读取和写入。
● 一个可以被序列化的MyClass类的定义:
public class MyClass implements Serilizable
{
public transient Thread t;
private String customerID;
private int total;
}
ObjectOutputStream
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。
还可以使用 DataOutput 中的适当方法将基本数据类型写入流中。还可以使用 writeUTF 方法写入字符串。
对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用(瞬态和静态字段除外)也会导致写入那些对象。可使用引用共享机制对单个对象的多个引用进行编码,这样即可将对象的图形恢复为最初写入它们时的形状。
例如,要写入可通过 ObjectInputStream 中的示例读取的对象,请执行以下操作:
FileOutputStream fos = new FileOutputStream("t.tmp");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeInt(12345);
oos.writeObject("Today");
oos.writeObject(new Date());
oos.close();
编程举例:创建了一个可序列化的学生对象,并用ObjectOutputStream类把它存储到一个文件(student.txt)中,然后再用ObjectInputStream类把存储的数据读取到一个学生对象,即恢复保存的学生对象。
import java.io.Serializable;
public class Student implements Serializable
{
String name="";
int age;
public Student(String name,int age)
{
this.name=name;
this.age=age;
}
}
import java.io.*;
public class ObjectStream
{
public static void main(String[] args)
{
// TODO: Add your code here
Student stu1=new Student("张三",18);
Student stu2=new Student("李四",20);
File f=new File("student.txt");
try
{
FileOutputStream fos=new FileOutputStream(f);
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(stu1);
oos.writeObject(stu2);
oos.writeObject(new Date());
oos.close();
FileInputStream fis=new FileInputStream(f);
ObjectInputStream ois=new ObjectInputStream(fis);
stu1=(Student)ois.readObject();
stu2=(Student)ois.readObject();
System.out.println("姓名="+stu1.name);
System.out.println("年龄="+stu1.age);
System.out.println("姓名="+stu2.name);
System.out.println("年龄="+stu2.age);
System.out.println((Date)ois.readObject());
ois.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
InpuStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset
读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset
将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:
BufferedWriter out
= new BufferedWriter(new OutputStreamWriter(System.out));
字节流与字符流的转换
● 能不能找到一种简单的方式来读取键盘上输入的一行字符,如何找?
● InputStreamReader和OutputStreamWriter,是用于将字节流转换成字符流来读写的两个类,InputStreamReader可以将一个字节流中的字节解码成字符后读取,OutputStreamWriter将字符编码成字节后写入到一个字节流中。
● 避免频繁地在字符与字节之间进行转换,最好不要直接使用InputStreamReader和OutputStreamWriter类来读写数据,应尽量使用BufferedWriter类包装OutputStreamWriter类,用BufferedReader类包装InputStreamReader。
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));
BufferedWriter out
= new BufferedWriter(new OutputStreamWriter(System.out));
例:
import java.io.*;
public class BufferedTest
{
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
System.out.println("请输入文字:");
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String str=br.readLine();
br.close();
//System.out.println(str);
FileReader fr=new FileReader("b.txt");
char[]buf=new char [1024];
int len=fr.read(buf);
String strInfo=new String(buf,0,len);
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
bw.write(strInfo);
System.out.println(strInfo);
bw.close();
FileWriter fw=new FileWriter("buffered.txt");
fw.write(str);
fw.close();
FileOutputStream fos=new FileOutputStream("fos.txt");
byte[]buf=str.getBytes();
fos.write(buf);
fos.close();
FileReader fr=new FileReader("a.txt");
char[]bufChar=new char[1024];
int len=fr.read(bufChar);
System.out.println(new String(bufChar,0,len));
fr.close();
}
}
Java程序与其它进程的数据通信
● 在Java程序中可以用Process类的实例对象来表示子进程,子进程的标准输入和输出不再连接到键盘和显示器,而是以管道流的形式连接到父进程的一个输出流和输入流对象上。
● 调用Process类getOutputStream和getInputStream方法可以获得连接到子进程的输出流和输入流对象。
● 管道缓冲区满后,与PipedInputStream相连的PipedOutputStream无法再写入新的数据,PipedOutputStream.writer方法将处于阻塞状态。
● 用Process类的destroy方法结束子进程的运行。
● 编程实例:在TestInOut类中启动java.exe命令执行另外一个MyTest类,TestInOut和MyTest通过进程间的管道相互传递数据。
分析:
父进程(TestInOut)启动子进程(MyTest类),主线程(main)不停向子进程发送数据(send方法),另一线程不停从子进程中读取数据(run方法),在构造函数中启动子进程和一线程。
import java.io.*;
public class TestInOut implements Runnable
{
Process p=null;
public TestInOut()
{
try
{
p=Runtime.getRuntime().exec("java MyTest");
new Thread(this).start();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
// TODO: Add your code here
TestInOut ti=new TestInOut();
ti.send();
}
public void send()
{
try
{
OutputStream ops=p.getOutputStream();
while(true)
{
ops.write("Hello\r\n".getBytes());
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void run()
{
try
{
InputStream ips=p.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(ips));
while(true)
{
String str=br.readLine();
if(str!=null)
{
System.out.println(str);
}
else
{
return ;
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
import java.io.*;
public class MyTest
{
public static void main(String[] args)
{
// TODO: Add your code here
try
{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
while(true)
{
String str=br.readLine();
if(str!=null)
{
System.out.println(str+"World");
}
else
{
return ;
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
字节输入流类
字节输出流类
字符输入流类
字符输出流类
Decorator设计模式
● 在程序中用一个对象(the Decorators)包装另外一个对象,这是一种被称为Decorator的设计模式。
● 如果要设计自己的IO包装类,这个类需要继承以FilterXXX命名的类,例如,设计一对输入输出包装类:RecordInputStream和RecorderOutputStream,来完成从数据库文件中读取记录和往数据库文件中写入记录。
● Excption类从Throwable类继承的三个printStackTrace方法的定义如下:
n Public void printStackTrace();
n Public void printStackTrace(PrintStream s);
nPublic void printStackTrace(PrintWriter s);
该如何把printStackTrace方法打出的详细异常信息存储到一个字符串中?
编写下面的程序代码,分析和观察程序的运行结果:
import java.io.*;
public class TestPrintWriter
{
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
try
{
throw new Exception("test");
}
catch(Exception e)
{
/*StringWriter sw=new StringWriter();
PrintWriter pw=new PrintWriter(sw);
e.printStackTrace(pw);
System.out.println(sw.toString());
File f=new File("a.txt");
PrintStream ps=new PrintStream(f);
e.printStackTrace(ps);
FileInputStream fis=new FileInputStream(f);
byte []buf=new byte [1024];
int len=fis.read(buf);
System.out.println(new String(buf,0,len));*/
FileOutputStream fos=new FileOutputStream("b.txt");
PrintStream ps=new PrintStream(fos);
e.printStackTrace(ps);
System.out.println(fos.toString());
}
}
}
思考与实践
编写下面的程序代码,分析和观察程序的运行结果:
import java.io.*;
class InputReader
{
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
InputStreamReader ips=new InputStreamReader(System.in,"iso8859-1");
BufferedReader br=new BufferedReader(ips);
System.out.println("请输入文字:");
String str=br.readLine();
int len=str.length();
for(int i=0;i<len;i++)
{
System.out.println(Integer.toHexString((int)str.charAt(i)));
}
System.out.println(str);
ips.close();
}
}
程序运行结果:
请输入文字:
中国
d6
d0
b9
fa
???ú
请按照下面的两种要求修改上面的程序代码,让程序能够正常打印出输入的中文字符:
(1) 修改程序中的语句:
InputStreamReader ips=new InputStreamReader(System.in,"iso8859-1");
修改如下:
InputStreamReader ips=new InputStreamReader(System.in);
(2) 修改下面的语句:
System.out.println(str);
修改如下:
System.out.println(new String(str.getBytes("iso8859-1"),"GBK"));