一、对象的序列化(对象的持久化,就是把对象存储到硬盘上,方便今后使用)
操作对象,被操作的对象需要实现Serializable(标记接口,没有方法的接口)
ObjectInputStream
ObjectOutputStream
ObjectOutputStream 将Java对象的基本数据类型和图形写入OutputStream,可以使用ObjectInputStream读取对象。
static 成员不能被序列化,因为static成员是在方法区,而序列化在堆内存中。
若非static成员不想被序列化,用transient修饰,表示被修饰的成员在堆内存中,但是在文件中不能存在
import java.io.*;
class ObjectStreamDemo
{
public static void main(String[] args) throws IOException
{
//二者成对使用
writeObj();
readObj();
}
public static void writeObj() throws IOException
{
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi",33));//将对象存入到obj.txt中
oos.close();
}
public static void writeObj() throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
sop(p)
ois.close();
}
}
class Person implements Serializable//类实现该接口,启动序列化功能,未实现该接口的类不能序列化
//或被反序列化。实现该接口后,类Person会有个UID标识
{
public static final long serialVersionUID = 42L;
String name ;
//private String name;编译运行后,若把String name私有化,那么产生的对象的UID就不一样了,
//在readObject()时就产生问题,
//若想私有,又想依然能使用原对象,可以人为给类加上UID,这样就能使用了。
int age;
Person(String name,int age)
{
this.name = name ;
this.age = age;
}
public String toString()
{
return name+">>"+age;
}
}
二、 管道流,构造函数---PipedInputStream
1、PipedInputStream(PipeOutputStream src)创建PipeInputStream,使其连接到管道输出流src
2、PipedInputStream(PipeOutputStream src,int pipeSize),创建一个PipedInputStream,使其连接到管道输出流src,并对管道缓冲区使用指定的管道大小
输入输出可以直接进行连接,通过结合线程使用。
管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从PipeInputStream对象读取,并由其他线程将其吸入到相应的PipeOutputStream。不建议对着两个对象常识使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开,如果连接管道输出流提供数据自己的线程不再存在,则认为该管道已经损坏。
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];
int len = in.read(buf);
String s = new String(buf,0,len);
System.out.println(s);
in.close();
}
catch(IOException e)
{
throw new RuntimeException("read failed");
}
}
}
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out. = out ;
}
public void run()
{
try
{
out.write("piped come here"),getBytes());
out.close();
}
catch(IOException e)
{
throw new RuntimeException("piped out failed");
}
}
}
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();
Write w = new Write();
Thread t1 = new Thread(r);
Thread t2 = new Thread(w);
t1.start();
t2.start();
}
}
三、RandomAccessFile,随机访问文件,自身具备读写的方法。通过skipBytes(int x),seek(int x)来达到随机访问。
1、特点:该类不是IO体系中子类,而是直接继承自Object
但是它是IO包中成员,因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。
2、完成读写原理:就是内部封装了字节输入流和输出流
3、局限性,通过构造函数可以看出,该类智能操作文件。而且操作文件还有模式
4、构造函数
RandomAccessFile(File file,String mode),创建从中读取和向其中写入的随机访问文件流,该文件由File参数指定。
RandomAccessFile(String name,String mode),创建从中读取和向其中写入的随机访问文件流,该文件具有指定名称
mode参数指定用以打开 文件的访问模式,允许的值及其含义为:
"r",以只读方式打开,调用结果对象的任何write方法都将导致抛出IOException。
”rw",打开以读取和写入,如果该文件不存在,则尝试创建该文件
"rws",打开以便读取和写入,对于"rw"还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd",打开以便读取和写入,对于“rw",还要求对文件内容的每个更新都同步写入到底层存储设备
5、如果模式为只读r,不会创建文件,回去读取一个已经存在文件,如果文件不存在,则会出现异常
如果模式为rw,操作的文件不存在,会自动创建,如果存在,则不会覆盖
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args)
{
}
public static void writeFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
raf.write(97);
raf.write(258);//注意:write写的只是写二进制的最低八位,如果超过255,
//二进制就会是最低9位,而write只写8位,因此会造成数据丢失。
//取完8为后,再查GBK的表,有时出现乱码
raf.writeInt(258);//writeInt方法,以32位二进制来写,就不会出现上述情况了,
//而在文本中显示时,也是四位,比如writeInt(97),是a ,在文本中显示是: a,
//注意a前面有三个空格。同时写多个write(Int),后面的会覆盖前面的。
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
public static void readFile()
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中的指针
// raf.seek(8);//表示将指针指向8,那么下文代码读取的时候,就会从8开始。指针前后都可以指定
//跳过指定的字节数
raf.skipBytes(8);//只能往后跳,不能往前跳,也就意味着,只能读后面的,不能读前面的
byte[] buf = new byte[4];
raf.read(buf);
String s = new String(buf);
ing age = raf.readInt();
System.out.println(name);
System.out.println(age);
raf.close();
}
public static void writeFile_2()
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(24);//若24位置已经存在,则会修改24位置上的内容
raf.write("周期".getBytes());//表示直接在24位置写“周期"
raf.write(102);
raf.close();
}
}
四、可以用于操作基本数据类型的流对象
DataOutputStream,数据输出流允许应用程序以适当方式将基本java数据类型写入输出流中,然后应用程序可以使用数据输入流将数据读入
构造函数:DateOutputStream(OutputStream out),创建一个新的数据输出流,将数据系融入指定基础输出流
方法:void writeUTF(String str),以与机器无关方式使用UTF-8修改版编码将一个字符串写入基础输出流。
DataInputStream
impor java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
writeData();
writeUTFDemo();
readUTFDemo();
//注意英文不涉及编码
//
修改版UTF-8 占用4个字节 ;一般的UTF-8占用3个字节;GBK占用2个字节;(指的是每个汉字占用的字节数)
//因此,用一般UTF-8不能读取
修改版UTF-8
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt","gbk"));
osw.write("你好");//此时文件中的”你好“占用4个字节
osw.close();
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("utf8.txt","UTF-8"));
osw1.write("你好");//此时文件中的”你好“占用6个字节
osw1.close();
}
public static void writeData() throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
//注意:读取的时候与存入时的顺序必须一致。
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(989.343);
dos.close();
}
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);
System.out.println(b);
System.out.println(d);
dis.close();
}
//用writeUtf()时,必须用对应的readUTF()来读取,因为用writeUTF时,是以8字节存放的(这是修改版的UTF-8),而一般的UTF-8是6字节的,GBK是4字节的
public static void writeUTFDemo() throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");//此时的文件中的 “你好”是8字节的
dos.close();
}
public statci void readUTF()Demo throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputSteam("utfdate.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
}
五、操作字节的流
public class ByteArrayInputStream extends InputStream
包含一个内部缓冲区,该缓冲区包含从流中读取的字节,内部急速器跟踪read方法要提供的下一个的字节。关闭ByteArrayInputStream无效,此类中的方法在关闭流后仍可被调用,而不产生IOException
构造方法:
ByteArrayInputStream(byte[] bfu),
创建一个 ByteArrayInputStream ,使用buf作为其缓冲区数组。
ByteArrayInputStream(byte[] buf,int offset,int leanth)
创建 ByteArrayInputStream ,使用buf作为其缓冲区数组
public class ByteArrayOutputStream extends OutputStream
此类实现了一个输出流,其中的数据流写入了一个byte数组,缓冲怒会随着数据的不断写入而自动增长,可使用toByteArray()和toString()获取数据。关闭ByteArrayOutputStream无效,此类中的方法在关闭此流后仍可被调用,不会产生任何IOException
构造方法:
ByteArrayOutputStream()
创建一个新的byte数组输出流
ByteArrayOutputStream(int size)
创建一个新的byte数组输出流,它具有指定大小的缓冲区容量(以字节为单位)
特点:
ByteArrayInputStream:在构造的时候,需要接受数据源,而且数据源是一个字节数组
ByteArrayOutputStream:在构造的时候,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源,所以,不用进行close关闭。
在流操作规律讲解时:
源设备:
键盘:System.in
硬盘:FileStream
内存:ArrayStream
目的设备:
控制台:System.out
硬盘:FileStream
内存:ArrayStream
用流的思想来操作数组
import java.io.*;
class ByteArrayDemo
{
public static void main(String[] args)
{
//数据源
ByteArrayInputStream bis = new ByteArrayInputStream("abcdef".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"));//一次性将bos内容写到a.txt中
}
}
六、操作字符数组
CharArrayReader
CharArrayWriter
七、操作字符串
StingReader
StringWriter
八、字符编码
1、字符流的出现为了方便操作字符
2、更重要的是加入了编码转换
3、通过子类转换流来完成
InputStreamReader
OutputStreamWriter
4、编码表由来:计算机智能识别二进制数据,早期由来是电信号。为了方便应用计算机,让他可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表
常见编码表:
ASCII:美国标准信息交换吗。用一个字节的7位可以表示
ISO8859-1:拉丁码表,欧洲码表。用一个字节的8位表示。
GB2312:中国的中文编码表。两个字节十六位表示
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
5、在两个对象进行构造的时候可以加入字符集
import java.io.*;
class EncodeStream
{
public static void main(String[] args)
{
}
public static void writeText()
{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"));//默认GBK
osw.write("你好");//此时文件gbk.txt 的 你好 占4个字节
osw.close();
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("utf8.txt","UTF-8"));//使用UTF-8
osw1.write("你好");//此时文件utf8.txt 中的 你好 占6个字节
osw1.close();
}
public static void readText()
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"));//默认GBK
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
InputStreamReader isr1 = new InputStreamReader(new FileInputStream("utf8.txt","UTF-8"));//指定UTF-8
char[] buf = new char[10];
int len = isr1.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr1.close();
//不同编码表之间的查找,两找三找不到(??),三找二,多一个。
InputStreamReader isr31 = new InputStreamReader(new FileInputStream("utf8.txt","GBK"));//指定GBK,但是GBK是两个字节,UTF-8是三个字节,没有匹配的,因此 读出来的是?
char[] buf = new char[10];
int len = isr3.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr3.close();
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("gbk.txt","UTF-8"));//用UTF-8编码读gbk.txt, 你好六个字节,而gbk两个字节一个字,因此能找到3个字,只不过不是你好
char[] buf = new char[10];
int len = isr2.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr2.close();
}
}
编码:字符串-->字节数组
解码:字节数组-->字符串
String-->byte[]: str.getBytes();//默认GBK,但可以指定,用str.getBytes(charsetName);
byte[]-->String: new String(byte[]);//默认GBK,但可以指定,用new String(byte[],charsetName);
import java.util.*;
class EncodeDemo
{
public static void main(String[] args)
{
//正确形式:
String s = "你好";
byte[] b1 = s.getBytes("GBK");//编码,字符串-->字节数组
String s2 = new String(b1,"GBK");//解码,字节数组-->字符串
System.out.println(Arrays.toString(b1));
System.out.println(s2);
//编码错误
String s = "你好";
byte[] b1 = s.getBytes("ISO8859-1");//编码错误,就没有办法进行下去
// String s2 = new String(b1,"GBK");
//System.out.println(Arrays.toString(b1));
// System.out.println(s2);
//解码错误,用ISO8859-1解码gbk,
String s = "你好";
byte[] b1 = s.getBytes("GBK");//编码,字符串-->字节数组
//用ISO8859-1去解码GBK,会返回一串????
String s2 = new String(b1,"ISO8859-1");//解码,字节数组-->字符串
//解决方式,用ISO8859-1再去编码,这样就回到初始的字节数组了
byte[] b2 = s2.getBytes("ISO8859=1");
//对字节数组再次进行正确编码
String s3 = new String(b2,"GBK");
System.out.println(s3);
//注意:如果用UTF-8解码GBK了,则不能按上述方式去反编,因为这二者都识别中文。而ISO8859-1不识别中文。
}
]
九、字符编码--联通
class EncodeDemo2
{
public static void main(String[] args) throws Exception
{
String s = "联通";
byte[] by = s.getBytes("gbk");
for(byte b:by)
{
System.out.println(b);
System.out.println(Integer.toBinaryString(b&255));
}
}
}
理解:UTF-8在编码后会有标示头,而UTF-8根据这个标示头去决定读1个字节、2个字节还是3个字节。内存中字节以二进制存在,如果是二进制是以0xxxxxxx开头,那么就读1个字节;如果是以110xxxxx开头表示读2个字节,如果是以1110xxxx开头,表示读3个字节。记事本默认GBK,字符串”联通“二进制形式正好符合UTF-8的规则,结果记事本按UTF-8去解码了,就造成了乱码情况,即记事本存不了联通的情况。想解决这种问题,只需要打破这种正好符合的规则就可以,比如在联通前加个汉字,但是不能加英文,因为英文二进制以0开头,仍符合UTF-8规则。
练习:
/*
有五个学生,每个学生有3门课的成绩。从键盘输入以上数据(包括姓名,三门课成绩),输入的格式:zhangsan,30,40,50并计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在stud.txt中。
*/
/*
分析:
1、描述学生对象
2、定义一个可操作学生对象的工具类
3、通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
4、因为学生有很多,那么就需要存储,使用到集合,因为要对学生的总分排序,所以可以使用TreeSet。
5、键该集合的信息写入到一个文件中。
*/
import java.io.*;
import java.util.*;
class Studeng implements Comparable<Studengt>
{
private String name;
private int ma;
private int cn;
private int en;
private in sum;
Studeng(String name,int ma ,int cn,int en)
{
this.name = name;
this.ma = ma;
this.cn = cn;
this.en = en;
sum = ma+cn+en
}
public int compareTo(Student s)
{
int num = new Integer(this.sum).compareTo(new Integer(s.sum));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String getName()
{
return name;
}
public int getSum()
{
return sum;
}
public int hashCode()
{
return name.hashCode()+sum*78;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s =(Student)obj;
return this.name.equals(s.name)&&this.sum==s.sum;
}
public String toString()
{
return "student["+name+","+ma+","+cn+","+en+"]";
}
}
class StudentInfoTool
{
public static Set<Student> getStudents() throws IOException
{
return getStudent(null);
}
public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
Set<Student> stus =null;
if(cmp==null)
stus = new TreeSet<Student>();
else
stus = new TreeSet<Student>(cmp);
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
String[] info = line.split(",");
Student stu = new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3]));
stus.add(stu);
}
bufr.close();
return stus;
}
public static void write2File(Set<Student> stus) throws IOException
{
BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
for(Student stu:stus)
{
bufw.write(stu.toString()+"\t");
bufw.write(stu.getSum()+" ");
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
class StudentInfoTest
{
public static void main(String[] args) throws IOException
{
Comparator<Student> cmp = Collections.reverseOrder();
Set<Student> stus = StudentInfoTool.getStudent(cmp);
StudentInfoTool.write2File(stus);
}
}