第8章 IO
1、File类
File类 io包中唯一代表磁盘文件本身信息的类,而不是文件中的内容。如文件的创建时间等
File类定义了一些与平台无关的方法来操纵文件,如创建、删除、重命名文件,判断文件访问权限、文件是否存在,设置查询最近修改时间 在java中,目录被当作一种特殊的文件使用,list方法可以返回目录中的所有子目录和文件名
在Unix下的路径分隔符为/,Dos下的为/。
举例判断某文件是否存在,在则删除,不存在则创建
import java.io.*;
import java.util.*;
public class FileTest {
/**
* Method main
*
*
* @param args
*
*/
public static void main(String[] args) {
// TODO: Add your code here
File f=new File("1.txt");
if(f.exists())
{
f.delete();
}
else
{try{
f.createNewFile();}catch(Exception e){
e.printStackTrace();}
}
System.out.println("file name:"+f.getName());
System.out.println("file AbsolutePath:"+f.getAbsolutePath());
System.out.println("file path:"+f.getPath());
System.out.println(f.exists()?"exists":"not exist");
System.out.println("file space"+f.getFreeSpace());
System.out.println("last modify time"+new Date(f.lastModified()));
}
}
2.RandomAccessFile类
是java类中最丰富的访问文件的类,提供很多文件访问方法 支持随机访问的方式,可以跳到任何地方读写操作
其在随机读写等长记录格式的文件时有很大的优势。
其仅限于操作文件,不能访问其他IO设备,如网络,内存映像等。
两个构造方法:
new RandomAccessFile(f,"rw");//读写方式
new RandomAccessFile(f,"r");//只读方式
举例:
import java.io.*;
public class RandomFile {
/**
* Method main
*
*
* @param args
*
*/
public static void main(String[] args)throws Exception
{
// TODO: Add your code here
Employee el =new Employee("zhangsan",23);
Employee e2 =new Employee("lisi",24);
Employee e3=new Employee("wangwu",25);
RandomAccessFile ra =new RandomAccessFile("employee.txt","rw");
ra.write(el.name.getBytes());
ra.write(el.age);
ra.write(e2.name.getBytes());
ra.write(e2.age);
ra.write(e3.name.getBytes());
ra.write(e3.age);
ra.close();
int le=0;
// String strName = null;
byte[]buf=new byte[8];
RandomAccessFile raf =new RandomAccessFile("employee.txt","r");
raf.skipBytes(9);
le=raf.read(buf);
String strName1 = new String(buf,0,le);
System.out.println(strName1+":"+raf.read());
raf.seek(0);//定位到多少字节处
le=raf.read(buf);
String strName2 = new String(buf,0,le);
System.out.println(strName2+":"+raf.read());
raf.skipBytes(9);
le=raf.read(buf);
String strName3 = new String(buf,0,le);
System.out.println(strName3+":"+raf.read());
raf.close();
}
}
3 节点流
流是字节序列的抽象概念文件是数据的静态存储形式,而流是指数据传输时的形态流类分为两个大类:节点流类和过滤流类(也叫处理流类)InputStream类程序可以从中连续读取字节的对象叫输入流,在java中,用InputStream类来描述所有输入法流的抽象概念
int read() //从输入流中读取数据的下一个字节
int read(byte[] b) //从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
int read(byte[] b, int off, int len) //将输入流中最多 len 个数据字节读入 byte 数组
skip(long n) //跳过和丢弃此输入流中数据的 n 个字节
(包装类)available() //返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数
mark(int readlimit) //在此输入流中标记当前的位置
markSupported() //测试此输入流是否支持 mark 和 reset 方法
reset() //将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
close() //关闭此输入流并释放与该流关联的所有系统资源
OutputStream类程序可以向其中其中写入字节的对象叫输出流,在java中,用OutPutStream类来描述所有输出流的抽象概念
write(byte[] b) //将 b.length 个字节从指定的 byte 数组写入此输出流
write(int b) //将指定的字节写入此输出流
write(byte[] b, int off, int len) //将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
flush() //刷新此输出流并强制写出所有缓冲的输出字节
close() //关闭此输出流并释放与此流有关的所有系统资源
FileInputStream与FileOutputStreamFileInputStream和FileOutputStream类分别用来创建磁盘文件的输入和输出流对象,通过它们的构造函数来指定文件路征和文件名创建FileInputStream实例对象时,指定的文件应当是存在和可读的。创建FileOutStream实例对象时,如果指定文件已经存在,这个文件的原内容将被覆盖清除。对同一个磁盘文件创建FileInputStream对象的两种方式:
(1)FileInputStream inOne = new FileInputStream("hello.txt");
(2)File f = new File("hello.txt");
FileInputStream inTwo = new FileInputStream(f);
创建FileOutputStrem实例对象时,可以指定还不存在的文件名,不能指定一个已被其它程序打开了的文件。实例:
import java.io.*;
public class FileStream {
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
FileOutputStream out =new FileOutputStream("hello.txt");
out.write("www.it315.org".getBytes());
out.close();
byte buf[]=new byte[1024];
File f = new File("hello.txt");
FileInputStream in = new FileInputStream(f);
int len =in.read(buf);//读到字节数组里面
System.out.println(new String(buf,0,len));
in.close();
}
}
4、Reader与Writer(字符)
Reader和Writer 是所有字符流的抽象基类,用于简化对字符串的输入输出编程,即用于读写文本数据FileWriter用于写入字符流 FileOutputStream 要写入原始字节流实例:
import java.io.*;
public class FileStream2 {
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
FileWriter out =new FileWriter("hello2.txt");
out.write("www.it315.org");
out.close();
char [] buf =new char[1024];
FileReader in=new FileReader("hello2.txt");
int len = in.read(buf);
System.out.println(new String(buf,0,len));
}
}
备注:write是将文本写入了缓冲区 必须调用close关闭才能刷新到目标文件上
5、PipedInputStream与PipedOutStram类
PipedInputStream和PipedOutputStream 以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端,输入流是管道的接收端。实例:
发送代码
import java.io.*;
import java.io.IOException;
import java.io.PipedOutputStream;
public class Sender extends Thread
{ private PipedOutputStream out= new PipedOutputStream();
public PipedOutputStream getOutputStream()
{
return out;
}
public void run()
{
String strInfo = new String("hello,recevier!");
try{out.write(strInfo.getBytes());
out.close();}
catch(IOException e){e.printStackTrace();}
}
}
接收代码
import java.io.*;
import java.io.IOException;
import java.io.PipedInputStream;
public class Recevier extends Thread
{ private PipedInputStream in = new PipedInputStream();
public PipedInputStream getInputStream()
{
return in;
}
public void run()
{
byte buf[]=new byte[1024];
try{int len=in.read(buf);
System.out.print("the following message comes from sender:/n"+
new String(buf,0,len));
in.close();}
catch(IOException e){e.printStackTrace();}
}
}
联合线程的代码
import java.io.*;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class PipedStreamTest {
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
Sender t1=new Sender();
Recevier t2=new Recevier();
PipedOutputStream out=t1.getOutputStream();
PipedInputStream in=t2.getInputStream();
out.connect(in);
t1.start();
t2.start();
}
}
7、PipedWriter和PipedReader类。
(字符管道通信)使用管道流类,可以实现各个程序模块之间的松耦合通信。ByteArrayInputStream与ByteArrayOutputStream类ByteArrayInputStream和ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据用于以IO流的方式来完成对字节数组内容的读写,来支持类似内存虚拟文件或者内存映像文件的功能。
ByteArrayInputStream的两个构造函数: ByteArrayInputStream(byte[] buf) ByteArrayInputStream(byte[] bbuf, int offset, int len);ByteArrayOutputStream的两个构造函数: ByteArrayOutputStream() ByteArrayOutputStream(int len);实例小写变大写:
Java代码
import java.io.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ByteArrayTest
{ public static void main(String[] args)
{
String tmp = "abcdefghigklmnopqrstuvwxyz";
byte[] src = tmp.getBytes();
ByteArrayInputStream input = new ByteArrayInputStream(src);
ByteArrayOutputStream output = new ByteArrayOutputStream();
transform(input, output);
byte[] result =output.toByteArray();
System.out.println(new String(result));
}
public static void transform(InputStream in,OutputStream out)
{
int ch=0;
try{
while((ch=in.read())!=-1)
{int upperCh=(int)Character.toUpperCase((char)ch);
out.write(upperCh);}
}
catch(Exception e){ }
}
}
8、重视IO程序代码的复用System.in连接键盘,是InputStream类型的实例对象 System.out 连接显示器,是PrintStream类的实例对象。不管各种底层物理设备用什么方式实现数据的终止点,InputStream的read方法总是返回-1来表示输入流的结束。在Windows下,按下Ctrl+Z组合可以产生键盘输入流的结束标记,在liunx下,则是按下Ctrl+D组合键来产生键盘输入流的结束标记。建议:要编程从键盘上连续读取一大段数据时,应尽量将读取数据的过程放在函数中完成,使用-1来作为键盘输入的结束点。在该函数中编写的程序代码不应直接使用System.in读取数据,而是用一个InputStream类型的形式参数对象来读取数据,然后将System.in作为实参传递给InputStream类型的形式参数来调用该函数。字符编码计算机里只有数字,计算机软件里的一切都是用数字来表示的,屏幕上显示的一个个数字也不例外。字符a对应数字是97,这种字符与数字对应的编码规则被称为ASCII(美国标准信息交换码)。ASCII的最高位是0,也就是说这些数字都在0到127之间。中国大陆将每一个中文字符都用两个字节的数字表示,中文字符的每个字节的最高位bit都为1,中国大陆为每个中文字符制定的编码规则称为GB2312(国标码)。在GB2312的基础上,对更多的中文字符(包括繁体)进行了编码,新的编码规则称为GBK。在中国大陆使用的计算系统上,GBK和GB2312就被称为该系统的本地字符集。台湾地区对中文字符集的编码规则称为BIG5(大五码)。Unicode编码ISO(国际标准化组织)将全世界所有的符号进行了统一编码,称之为Unicode编码。Unicode编码的字符都占用两个字节的大小,对于ASCII码所表示的字符,只是简单地在ASCII码原来占用的一个字节前面,增加一个所有bits为0的字节。Unicode只占用两个字节,在全世界范围内所表示的字符个数不会超过2的16次方(655536),实际上,Unicode编码中还保留了两千多个数值没有用于字符编码。java中的字符使用的Unicode编码,java在通过Unicode保证跨平台特性的前提下,也支持本地平台字符集。UTF-8编码ASCII码符保持原样,仍然只占用一个字节,对于其它国家的字符,UTF-8使用两个或三个字节来表示。使用UTF-8编码的文件,通常都要用EE BB BF作为文件开头的三个字节数据。UTF-8优点 不出现内容为0x00字节 便于应用程序检测数据在传输过程中是否发生了错误 直接处理使用ASCII码的英文文档UTF-8的缺点 在某些特殊的文字需要三个字节(如中日韩)UTF-16编码UTF-16编码在Unicode基础上进行了一些细节上的扩充,增加了对Unicode编码没有包括的那些字符的表达式。UTF-16对Unicode的扩充并没有影响Unicode编码所包括的哪些字符,只是增加了对Unicode编码没有包括的那些字符的表达方式,一个使用Unicode编码的字符就是UTF-16格式的。Unicode编码将0XD800-0XDFFF区间的数值保留出来,UTF-16扩充的字符,占用4个字节,前面两个字节的数值为0XD8000-0XD8FF之间,后面两个字节的数值为0XDC00-0XDFFF之间。为什么不让前面后面的两个字节的数值都位于0XD800-0XDFFF之间呢? 0XDC00-0XDFFF已经足够满足全世界各国文字;前后数值都位于0XD800-0XDFFF不便于区分两个字节。在不同体系结构的计算机系统中,UTF-16编码的Unicode字符在内存中的字节存储顺序是不同的。对于0X1234这样一个双字节数据,使用Little-Endian和Big-Endian两个方式在内存中存储的格式如下图所示:如果文件以0XFE 0XFF这两个字节开头,则表明文本的其余部分是Big-Endian的UTF-16编码;如果文件以0XFF 0XFE这两个字节开头,则表明文本的其余部分是Little-Endian的UTF-16编码。实例:
Java代码
package cn.com.io;
import java.io.UnsupportedEncodingException;
public class Charcode {
public static void main(String[] args) {
System.setProperty("sun.jnu.encoding", "iso8859-1"); //设置属性文件
System.getProperties().list(System.out); //获得属性文件
String strChina = "中国";
for(int i=0;i<strChina.length();i++){//toHexString(int i) 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式
System.out.println(Integer.toHexString((int)strChina.charAt(i)));
}
try {
byte[] buf = strChina.getBytes("gb2312");
for(int str : buf){
System.out.println(Integer.toHexString(str));
}
for(int i=0;i<buf.length;i++){
System.out.write(buf[i]); //输出流形式输出
}
System.out.println(); //刷新缓冲区
System.out.write(buf,0,4); //自动刷新缓冲区
System.out.println("/n中国"); //PrintStream.println(String str)
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
Java代码
package cn.com.io;
import java.io.IOException;
public class CharDecode {
public static void main(String[] args) {
System.getProperties().put("file encoding", "iso8859-1");
System.out.println("please enter a chinese String:");
byte[] buf = new byte[1024];
int pos = 0;
String strInfo;
int ch;
while(true){
try {
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(new String(strInfo.getBytes("iso8859-1"),"gb2312"));
pos = 0;
break;
default:
buf[pos++] = (byte) ch;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
9包装类的概念与作用
通过完成某个特殊功能的类,称为包装类或者称为过滤流类BufferedInputStream与BufferedOutputStream类缓冲流为I/O流增加了内存缓冲区,增加缓冲区有两个基本的:1、允许java程序一次不只操作一个字节,这样提高了程序的性能。2、由于有了缓冲区,使得在流上执行skip、mark和reset方法都成为可能。BufferedInputStream和BufferedOutputStream是java提供的两个缓冲区包装类,不管底层系统是否使用了缓冲区,这两个类在自己的实例对象中创建缓冲区。BufferedInputStreamr的两个构造函数: BufferedInputStream(InputStreamin) BufferedInputStream(InputStream in,int size)BufferedOutputStream的两个构造函数: BufferedOutputStream(OutputStream out) BufferedOutputStream(OutputStream out,int size)BufferedReader的readLine方法可以一次读取一行文本,BufferedWriter的newLine方法可以向字符流中写人不同操作系统下的换行符。DataInputStream与DataOutStream类DataOutputStream类提供了三个写入字符的方法: public final void writeBytes(String s) public final void writeChars(String s) public final void writeUTF(String s)DataInputStream类中有一个readUTF方法,并没有“readBytes”和“readChars”方法。 (原因读取编码不明)编程实例:分别使用DataOutputStream类的writeUTF、writeBytes和writeChars方法,比较这几个方法的差异。程序中所使用的流栈如下:关闭流栈中的最上层的流对象,将会自动关闭流栈中的所有底层流对象。
Java代码
import java.io.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.DataOutputStream;
public class DataStreamTest {
public static void main(String[] args) throws Exception
{
// TODO: Add your code here
FileOutputStream fos =new FileOutputStream("count.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
DataOutputStream dos=new DataOutputStream(bos);
dos.writeUTF("ab中国");
dos.writeBytes("ab中国");
dos.writeChars("ab中国");
dos.close();
FileInputStream fis =new FileInputStream("count.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(new String(buf,0,len));
fis.close();
}
}
10、PrintStream类PrintStream类
提供了一系列的print和println方法,可以将基本数据类型的数据格式化成字符串输出。格式化输出:例如,97被格式化输出的实际字节数据为0X39和0X37。PrintStream的3个构造函数:PrintStream(OutputStrean out)PrintStream(OutputStream out,boolean outoflush)PrintStream(OutputStream out,noolean outoflush,String encoding)与PrintStream对应的PrintWriter类,即使遇到了文本换行符(/n),PrintWriter类也不会自动清空缓冲区。PrintWrite的println方法能根据操作系统的不同而生成相应的文本换行标识符。在Windows下的文本换行标识符是“/r/n”,而Linux下的文本换行标识符是"/n";ObjectInputStream与ObjectOutputStream类ObjectInputStream和ObjectOutputStream这两个包装类,用于从底层输入流中读取对象类型的数据和将对象类型的数据写入到底层输出流。ObjectInputStream与ObjectOutputStream类所读写的对象必须实现了Serializable接口。对象中的transient(临时变量)和static类型的成员变量不会被读取和写入。一个可以被序列化的MyClass类的定义:
实例:
Java代码
import java.io.*;
import java.io.Serializable;
public class Student implements Serializable{
int id;
String name;
int age;
String department;
public Student(int id,String name,int age,String department)
{
this.id=id;
this.name=name;
this.age=age;
this.department=department;
}
}
Serialization类代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Serialization {
public static void main(String[] args) throws Exception {
Student stu1 = new Student(19,"zhangsan",25,"huaxue");
Student stu2 = new Student(20,"lisi",23,"wuli");
FileOutputStream fos = new FileOutputStream("student.txt");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(stu1);
os.writeObject(stu2);
os.close();
FileInputStream fis = new FileInputStream("student.txt");
ObjectInputStream is = new ObjectInputStream(fis);
stu1 = (Student) is.readObject();
stu2 = (Student) is.readObject();
is.close();
System.out.println("id:"+stu1.id+" name:"+stu1.name+" age:"+stu1.age+" department:"+stu1.department);
System.out.println("id:"+stu2.id+" name:"+stu2.name+" age:"+stu2.age+" department:"+stu2.department);
}
}
11、字节流与字符流的转换
BufferedReader in = new BufferedReader(new InputStreamReader(System.in))读取键盘上输入的一行字符。InputStreamReader和OutputStreamWriter,是用于将字节流转换成字符流来读写的两个类,InputStreamReader可以将一个字节流中的字节解码成字符后读取,OutputStreamWriter将字符编码成字节后写入到一个字节流中。InputStreanReader的两个主要的构造函数: InputStreamRead(InputStream in) InputStreamRead(InputStream in,String CharsetName);OutputStreanWriter的两个主要的构造函数: OutputStreamWriter(OutputStream out) OutputStreamWriter(OutputStream out,String CharsetName);避免频繁地在字符与字节间进行转换,最好不要直接使用InputStreamRead和OutputStreamWriter类来读写数据,应尽量使用BufferedWrite类包装OutputStreamWriter类,用于BufferderReader类包装InputStreamrader.java程序与其它进程的数据通信在
java程序可以用Process类的实例对象来表示子进程,子进程的标准输入和输出不再连接到键盘和显示器,而是以管道流的形式连接到父进程的一个输出流和输入流对象上。调用Process类的getOutputStream和getInputStream方法可以获得连接到子进程的输出流和输入流对象。编程实例:在TestInOut类中启动java.exe命令执行另外一个MyTest类,TestInOut和MyTest通过进程间的管道相互传递数据。管道缓冲区满后,与PipedInputStram相连的pipedOutputStream无法再写入新的数据,PiedOutputStream.write方法处于阻塞状态。记住调用Process类的destroy方法结束子进程的运行。
Java代码
import java.io.*;
public class MyTest {
public static void main(String[] args)throws Exception
{
// TODO: Add your code here
while(true)
{ System.out.println("hi:"+
new BufferedReader(new InputStreamReader(System.in)).readLine());
}
}
}
Java代码
import java.io.*;
import java.io.InputStream;
public class TestInOut implements Runnable{
Process p=null;
public TestInOut()
{
try{
p=Runtime.getRuntime().exec("java MyTest");//获得java虚拟机运行时的对象也就是当前情况下运行的程序
new Thread(this).start();//开始新线程
}catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
TestInOut tio = new TestInOut();
tio.send();
}
public void send()
{
try{
OutputStream ops=p.getOutputStream();
while(true)
{
ops.write("help/r/n".getBytes());
}
}catch(Exception e){e.printStackTrace();}
}
public void run()
{
try{
InputStream in=p.getInputStream();
BufferedReader bfr=new BufferedReader(
new InputStreamReader(in));
while(true){
String strLine =bfr.readLine();
System.out.println(strLine);
}
}catch(Exception e){e.printStackTrace();}
}
}
12、字节输入流类字节输出流类
字符输入流类字符输出流类Decorator设计模式在程序中用一个对象(the Decorators)1包装另外的一个对象,这是一种被称为Decorator的设计模式。如果要设计自己的IO包装类,这个类需要继承以FilterXXX命名的类,设计一个对输入输出包装类:RecordInputStream和RecordOutputStram,来完成从数据库文件中读取记录和往数据库文件中写入记录。Exception类从Throwable类继承的三个PrintStackTrace方法的定义如下: public void printStackTrace(); public void printStackTrace(PrintStream s) public void printStackTrace(PrintWriter s)实例:(将异常信息储存到字符串中)
Java代码
package cn.com.io;
import java.io.PrintWriter;
import java.io.StringWriter;
public class TestPrintWriter {
public static void main(String[] args) {
try{
throw new Exception("test");
}catch(Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw =new PrintWriter(sw);
e.printStackTrace(pw);
System.out.println(e.getMessage());
System.out.println(sw.toString());
}
}
}