黑马程序员——java基础——IO流中的其他常用类

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


IO流中的常用类

一、对象流

        对象流ObjectInputStream与ObjectOutputStream用于读取和写入存入文件中的对象数据。文件中的数据是以对象为单位的,对象中封装着类的字段信息。将对象存入文件

中叫做对象的序列化,或对象的持久化。想要对象能被序列化,对象类要实现Serializable接口,该接口中有方法,为一个标记接口。其中对象类中静态成员不可被序列化,对

象类中若有字段不想被序列化,可用关键字transient修饰,修饰后的字段将无法存到文件中。其中有一个serialVersionUID的概念,它是一个由public static final long修饰的变

量,可称为序列码。 序列码可以自动生成,默认是由对象类中所有可被序列化的成员变量通过某种算法产生的一个数,这样做的目的是:首先,程序通过ObjectInputStream的

writeObject方法将对象写入到文件中,若之后,对象类一些原来参与生成serialVersionUID的字段发生改变的话,ObjectOutputStream的readObject方法在读取原来写好的对

象时(这叫反序列化),会重新将改变后的对象类所生成的serialVersionUID与对象中的serialVersionUID进行比较,这时比较结果一定不相等,反序列化将会失败,数据不能

读取出来。当然如果说不想要此功能可以在一开始进行序列化时,就在对象类中显示的给serialVersionUID赋值就好了,只要后面serialVersionUID的值不发生变化,即使对象

类中成员变量改变了也可顺利读取。 

        下面是序列化与反序列化的应用:


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("Person.txt"));
		Person p=(Person)ois.readObject();
		System.out.println(p.toString());
		ois.close();
	}
	public static void writeObj()throws Exception//序列化
	{
		ObjectOutputStream oos=null;
		oos=new ObjectOutputStream(new FileOutputStream("Person.txt"));
		oos.writeObject(new Person("zhangsan",45));
		oos.close();
	}
}
import java.io.*;
class Person  implements Serializable
{
	public static final long serialVersionUID=42L;//序列码的显式指定
	private String name;
	transient private int age;//不可被序列化
	public static String country="China";
	public Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public String toString()
	{
		return name+":"+age+";"+country;
	}
}



二、数据流

        数据流可用于对基本数据类型进行读写操作,它们是隶属于字节流的,分为DataInputStream与DataOutputStream,分别为数据输入流与数据输出流。它们的构造方法中

必须关联字节流对象,以指定输入或输出的目的,常用的方法有:

DataOutputStream:

void writeInt(int v);

void writeDouble(double v);

void writeBoolean(boolean v);

对应的读取方式为

DataInputStream:

int readInt();

double readDouble();

boolean readBoolean();

        其中比较特别的是void writeUTF(String v);与对应的读取方法StringreadUTF();它们对字符串用UTF-8修改版进行的编码,它们的读取须用对应的方法,否则读取会出错。

        下面是数据流常用方法的演示:


import java.io.*;
class DataStreamDemo 
{
	public static void main(String[] args) 
	{
		//writeData();
		/*
		OutputStreamWriter osw=null;
		try
		{
			osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");//可尝试用其他的编码进行字符串的 
编码写入,然后用readUTF进行读取
			osw.write("你好");
		}
		catch (IOException e)
		{
			throw new RuntimeException("数据写出失败!");
		}
		finally
		{
			try
			{
				if(osw!=null)
					osw.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("输出流关闭失败!");
			}
		}
		*/
		readData();
	}
	public static void writeData()
	{
		DataOutputStream dos=null;
		try
		{
			dos=new DataOutputStream(new FileOutputStream("utfdata.txt"));
			/*dos.writeInt(45);
			dos.writeBoolean(false);
			dos.writeDouble(45.17);*/
			dos.writeUTF("你好");
		}
		catch (IOException e)
		{
			throw new RuntimeException("数据写出失败!");
		}
		finally
		{
			try
			{
				if(dos!=null)
					dos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("输出流关闭失败!");
			}
		}
	}
	public static void readData()
	{
		DataInputStream dis=null;
		try
		{
			dis=new DataInputStream(new FileInputStream("utfdata.txt"));
			/*System.out.println(dis.readInt());
			System.out.println(dis.readBoolean());
			System.out.println(dis.readDouble());*/
			System.out.println(dis.readUTF());
		}
		catch (EOFException e)
		{
			throw new RuntimeException("无效读取方式!");
		}
		catch (IOException e)
		{
			throw new RuntimeException("数据读取失败!");
		}
		finally
		{
			try
			{
				if(dis!=null)
					dis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("输入流关闭失败!");
			}
		}
	}
}


三、随机访问文件对象

        在IO包中还有一个很强大的类——RandomAccessFile,该类提供了对文件的随机访问方式,支持对文件的读取和写入。其实,类的内部封装了底层输入输出流。在

RandomAccessFile的构造方法中,要传入一个模式参数,常用的两个模式为读写rw与只读r。可以这样理解,该类封装了一个文件数组,就是在关联文件以后,可以想象文件

数据被写到了一个和其自身大小等同的字节数组中,这样,可以通过一个数组指针任意进行文件的读写,如通过long getFilePointer()方法来获取当前文件指针的偏移量,通过

void seek(long pos)方法来设置文件指针的位置,通过int skipBytes(int n)方法跳过n个字节,注意这个方法只能往前跳。该类也提供了对基本数据类型数据的操作。

        现在用RandomAccessFile类来演示文件的随机读写:


import java.io.*;
class RandomAccessFileDemo 
{
	public static void main(String[] args) 
	{
		//write();
		read();
	}
	public static void write()
	{
		RandomAccessFile raf=null;
		try
		{
			raf=new RandomAccessFile("accessfile.txt","rw");
			raf.write("李四".getBytes());
			raf.writeInt(12);
			raf.write("王五".getBytes());
			raf.writeInt(45);
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件创建失败!");
		}
		finally
		{
			try
			{
				if(raf!=null)
					raf.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("随机流关闭失败!");
			}
		}
	}
	public static void read()
	{
		RandomAccessFile raf=null;
		try
		{
			raf=new RandomAccessFile("accessfile.txt","r");
			byte[] buf=new byte[4];
//			raf.seek(8);
			raf.skipBytes(8);
			raf.read(buf);
			System.out.println(new String(buf,"GBK"));
			System.out.println(raf.readInt());
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件读取失败!");
		}
		finally
		{
			try
			{
				if(raf!=null)
					raf.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("随机流关闭失败!");
			}
		}
	}
}

四、字节数组流

        ByteArrayInputStream与ByteArrayOutputStream称之为字节数组流,其内部都封装了一个字节数组,其中ByteArrayInputStream在构造时需要传入一个字节数组作为其内部

缓冲数组,ByteArrayOutputStream创建时不需要从外部传入数组,其内部在创建时会创建一个长度可变的数组,初始时长度为0。可以这样理解,这两个流其实就是包装了一个

内存中的字节数组,我们可以通过这两个流进行数组数据的获取和写入,从ByteArrayInputStream中获取,写入到ByteArrayInputStream的可变数组中。

        参看下面的代码:


import java.io.*;
class ByteArrayStreamDemo 
{
	public static void main(String[] args) 
	{
		ByteArrayInputStream bis=new ByteArrayInputStream("abcdefg".getBytes());
		ByteArrayOutputStream bos=new ByteArrayOutputStream();
		System.out.println(bos.size());
		int b=0;
		while((b=bis.read())!=-1)
		{
			bos.write(b);
		}
		System.out.println(bos.toString());
	}
}


五、关于编码

        除了文本数据外的其他数据都不涉及到编码,编码是操作文本数据所独有的。各个国家因其语言文字的不同而产生了各自的编码表如ASCII码表用于英文字母的编码,GBK

码表中因融入了ASCII从而能编码英文和中文,它是Java中的默认码表,其中通用的码表有ISO和UTF-8,后者为前者的修改版。ASCII中每个字符用一个最高位为0的字节即可

编码所有英文字符,而汉字由于个数较多,故GBK采用2个字节表示,其中为了不与ASCII中的编码形式相冲突,因而采用的最高位为1的形式编码。UTF-8中的编码较为复杂,

如图:





        头一个字节中最高位若是0,则读取一个字节后进行编码;头一个字节中前三个字节若为110,则读取当前字节以及后一个字节后进行编码;若头一个字节前四位为1110,

则读取当前字节以及后两个字节进行编码。

读者可用如下的代码进行各种编码测试:


import java.io.*;
class EncodingTrans 
{
	public static void main(String[] args) 
	{
		//writeText();
		readText();
	}
	public static void writeText()
	{
		OutputStreamWriter osw=null;
		try
		{
			osw=new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");
			osw.write("你好");
		}
		catch (IOException e)
		{
			throw new RuntimeException("写入失败!");
		}
		finally
		{
			try
			{
				if(osw!=null)
					osw.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("输出流关闭失败!");
			}
		}
	}
	public static void readText()
	{
		InputStreamReader isr=null;
		try
		{
			isr=new InputStreamReader(new FileInputStream("gbk.txt"),"utf-8");
			char[] chs=new char[10];
			int len=0;
			len=isr.read(chs);
			System.out.println(new String(chs,0,len));
		}
		catch (IOException e)
		{
			throw new RuntimeException("读取失败!");
		}
		finally
		{
			try
			{
				if(isr!=null)
					isr.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取流关闭失败!");
			}
		}
	}
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值