目录
2:数据操作流(操作基本类型数据的流)(理解)
3:内存操作流(理解)
4:打印流(掌握)
5:标准输入输出流(理解)
6:随机访问流(理解)
7:合并流(理解)
8:序列化流(掌握)
9:Properties(掌握)
(3)Properties和IO流结合的方法
10:NIO(了解)
(1)可以操作基本类型的数据
(2)流对象名称
数据输入流:DataInputStream
DataInputStream(InputStreamin)
数据输出流:DataOutputStream
DataOutputStream(OutputStreamout)
public class DataStreamDemo { publicstatic void main(String[] args) throws IOException { //写 //write(); //读 read(); } privatestatic void read() throws IOException { //DataInputStream(InputStream in) //创建数据输入流对象 DataInputStreamdis = new DataInputStream(new FileInputStream("dos.txt")); //读数据 byteb = dis.readByte(); shorts = dis.readShort(); inti = dis.readInt(); longl = dis.readLong(); floatf = dis.readFloat(); doubled = dis.readDouble(); charc = dis.readChar(); booleanbb = dis.readBoolean(); //释放资源 dis.close(); System.out.println(b); System.out.println(s); System.out.println(i); System.out.println(l); System.out.println(f); System.out.println(d); System.out.println(c); System.out.println(bb); } privatestatic void write() throws IOException { //DataOutputStream(OutputStream out) //创建数据输出流对象 DataOutputStreamdos = new DataOutputStream(new FileOutputStream("dos.txt")); //写数据了 dos.writeByte(10); dos.writeShort(100); dos.writeInt(1000); dos.writeLong(10000); dos.writeFloat(12.34F); dos.writeDouble(12.56); dos.writeChar('a'); dos.writeBoolean(true); //释放资源 dos.close(); } }
(1)用于处理临时存储信息的,程序结束,数据就从内存中消失。
(2)三种
字节数组:
ByteArrayInputStream
ByteArrayOutputStream
字符数组:
CharArrayReader
CharArrayWriter
字符串:
StringReader
StringWriter
public class ByteArrayStreamDemo { publicstatic void main(String[] args) throws IOException { //写数据 //ByteArrayOutputStream() ByteArrayOutputStreambaos = new ByteArrayOutputStream(); //写数据 for(int x = 0; x < 10; x++) { baos.write(("hello"+ x).getBytes()); } //释放资源 //通过查看源码我们知道这里什么都没做,所以根本需要close() //baos.close(); //public byte[] toByteArray() byte[]bys = baos.toByteArray(); //读数据 //ByteArrayInputStream(byte[] buf) ByteArrayInputStreambais = new ByteArrayInputStream(bys); intby = 0; while((by = bais.read()) != -1) { System.out.print((char)by); } //bais.close(); } }
* 需求:DataStreamDemo.java复制到Copy.java中
* 数据源:
* DataStreamDemo.java-- 读取数据 -- FileReader -- BufferedReader
* 目的地:
* Copy.java-- 写出数据 -- FileWriter -- BufferedWriter-- PrintWriter
public class CopyFileDemo { publicstatic void main(String[] args) throws IOException { //以前的版本 //封装数据源 //BufferedReader br = new BufferedReader(new FileReader( //"DataStreamDemo.java")); 封装目的地 //BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java")); //String line = null; //while ((line = br.readLine()) != null) { //bw.write(line); //bw.newLine(); //bw.flush(); //} //bw.close(); //br.close(); // 打印流的改进版 //封装数据源 BufferedReaderbr = new BufferedReader(new FileReader("DataStreamDemo.java")); //封装目的地 PrintWriter pw = new PrintWriter(newFileWriter("Copy.java"), true); Stringline = null; while((line=br.readLine())!=null){ pw.println(line); } pw.close(); br.close(); } }
(1)字节打印流,字符打印流
字节流打印流 PrintStream
字符打印流 PrintWriter
(2)特点:
A:只有写数据的,没有读取数据。只能操作目的地,不能操作数据源。
B:可以操作任意类型的数据。
C:如果启动了自动刷新,能够自动刷新。
D:该流是可以直接操作文本文件的。
哪些流对象是可以直接操作文本文件的呢?
FileInputStream
FileOutputStream
FileReader
FileWriter
PrintStream
PrintWriter
看API,查流对象的构造方法,如果同时有File类型和String类型的参数,一般来说就是可以直接操作文件的。
流:
基本流:就是能够直接读写文件的
高级流:在基本流基础上提供了一些其他的功能
public class PrintWriterDemo { publicstatic void main(String[] args) throws IOException { //作为Writer的子类使用 PrintWriterpw = new PrintWriter("pw.txt"); pw.write("hello"); pw.write("world"); pw.write("java"); pw.close(); } }
* 1:可以操作任意类型的数据。
* print()
* println()
* 2:启动自动刷新
* PrintWriterpw = new PrintWriter(new FileWriter("pw2.txt"), true);
* 还是应该调用println()的方法才可以
* 这个时候不仅仅自动刷新了,还实现了数据的换行。
* println()
* 其实等价于于:
* bw.write();
* bw.newLine();
* bw.flush();
public class PrintWriterDemo2 { publicstatic void main(String[] args) throws IOException { //创建打印流对象 //PrintWriter pw = new PrintWriter("pw2.txt"); PrintWriterpw = new PrintWriter(new FileWriter("pw2.txt"), true); //write()是搞不定的,怎么办呢? //我们就应该看看它的新方法 //pw.print(true); //pw.print(100); //pw.print("hello"); pw.println("hello"); pw.println(true); pw.println(100); pw.close(); } }
(1)System标准输入流。是从键盘获取数据的
System类中的两个成员变量:
publicstatic final InputStream in “标准”输入流。
publicstatic final PrintStream out “标准”输出流。
InputStreamis = System.in;
PrintStreamps = System.out;
public class PrintWriterDemo2 { publicstatic void main(String[] args) throws IOException { //创建打印流对象 //PrintWriter pw = new PrintWriter("pw2.txt"); PrintWriterpw = new PrintWriter(new FileWriter("pw2.txt"), true); //write()是搞不定的,怎么办呢? //我们就应该看看它的新方法 //pw.print(true); //pw.print(100); //pw.print("hello"); pw.println("hello"); pw.println(true); pw.println(100); pw.close(); } }
(2)三种键盘录入方式
A:main方法的args接收参数
javaHelloWorld hello world java
B:System.in通过字符缓冲流包装标准输入流实现
BufferedReader br = newBufferedReader(new InputStreamReader(System.in));
C:Scanner(JDK5以后的)
* Scannersc = new Scanner(System.in);
* Strings = sc.nextLine();
* intx = sc.nextInt()
public class SystemInDemo { publicstatic void main(String[] args) throws IOException { 获取标准输入流 //InputStream is = System.in; 我要一次获取一行行不行呢? 行。 怎么实现呢? 要想实现,首先你得知道一次读取一行数据的方法是哪个呢? readLine() 而这个方法在哪个类中呢? BufferedReader 所以,你这次应该创建BufferedReader的对象,但是底层还是的使用标准输入流 BufferedReader br = new BufferedReader(is); 按照我们的推想,现在应该可以了,但是却报错了 原因是:字符缓冲流只能针对字符流操作,而你现在是字节流,所以不能是用? 那么,我还就想使用了,请大家给我一个解决方案? 把字节流转换为字符流,然后在通过字符缓冲流操作 //InputStreamReader isr = new InputStreamReader(is); //BufferedReader br= new BufferedReader(isr); BufferedReaderbr = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入一个字符串:"); Stringline = br.readLine(); System.out.println("你输入的字符串是:" + line); System.out.println("请输入一个整数:"); //int i = Integer.parseInt(br.readLine()); line= br.readLine(); inti = Integer.parseInt(line); System.out.println("你输入的整数是:" + i); } }
(3)转换流的应用
public class SystemOutDemo2 { publicstatic void main(String[] args) throws IOException { //获取标准输入流 PrintStream ps = System.out; OutputStream os = ps; //OutputStream os = System.out; // 多态 我能不能按照刚才使用标准输入流的方式一样把数据输出到控制台呢? //OutputStreamWriter osw = new OutputStreamWriter(os); //BufferedWriter bw = new BufferedWriter(osw); BufferedWriterbw = new BufferedWriter(new OutputStreamWriter(System.out)); bw.write("hello"); bw.newLine(); //bw.flush(); bw.write("world"); bw.newLine(); //bw.flush(); bw.write("java"); bw.newLine(); bw.flush(); bw.close(); } }
* 随机访问流:
* RandomAccessFile类不属于流,是Object类的子类。
* 但它融合了InputStream和OutputStream的功能。
* 支持对文件的随机访问读取和写入。
* public RandomAccessFile(String name,String mode):
第一个参数是文件路径,第二个参数是操作文件的模式。
* 模式有四种,我们最常用的一种叫"rw",这种方式表示我既可以写数据,也可以读取数据
public class RandomAccessFileDemo{ publicstatic void main(String[] args) throws IOException { //write(); read(); } privatestatic void read() throws IOException { //创建随机访问流对象 RandomAccessFileraf = new RandomAccessFile("raf.txt", "rw"); inti = raf.readInt(); System.out.println(i); //该文件指针可以通过 getFilePointer方法读取,并通过 seek 方法设置。 System.out.println("当前文件的指针位置是:" + raf.getFilePointer()); charch = raf.readChar(); System.out.println(ch); System.out.println("当前文件的指针位置是:" + raf.getFilePointer()); Strings = raf.readUTF(); System.out.println(s); System.out.println("当前文件的指针位置是:" + raf.getFilePointer()); //我不想重头开始了,我就要读取a,怎么办呢? raf.seek(4); ch= raf.readChar(); System.out.println(ch); } privatestatic void write() throws IOException { //创建随机访问流对象 RandomAccessFileraf = new RandomAccessFile("raf.txt", "rw"); //怎么玩呢? raf.writeInt(100); raf.writeChar('a'); raf.writeUTF("中国"); raf.close(); } }
(1)把多个输入流的数据写到一个输出流中。
(2)构造方法:
A:SequenceInputStream(InputStreams1, InputStream s2)
B:SequenceInputStream(Enumeration<?extends InputStream> e)
* 以前的操作:
* a.txt -- b.txt
* c.txt -- d.txt
* 现在想要:
* a.txt+b.txt -- c.txt
public classSequenceInputStreamDemo { publicstatic void main(String[] args) throws IOException { //SequenceInputStream(InputStream s1, InputStream s2) //需求:把ByteArrayStreamDemo.java和DataStreamDemo.java的内容复制到Copy.java中 InputStreams1 = new FileInputStream("ByteArrayStreamDemo.java"); InputStreams2 = new FileInputStream("DataStreamDemo.java"); SequenceInputStreamsis = new SequenceInputStream(s1, s2); BufferedOutputStreambos = new BufferedOutputStream(new FileOutputStream("Copy.java")); //如何写读写呢,其实很简单,你就按照以前怎么读写,现在还是怎么读写 byte[]bys = new byte[1024]; intlen = 0; while((len = sis.read(bys)) != -1) { bos.write(bys,0, len); } bos.close(); sis.close(); } }
* a.txt -- b.txt
* c.txt -- d.txt
* e.txt -- f.txt
* 现在想要:
* a.txt+b.txt+c.txt -- d.txt
public classSequenceInputStreamDemo2 { publicstatic void main(String[] args) throws IOException { //需求:把下面的三个文件的内容复制到Copy.java中 //ByteArrayStreamDemo.java,CopyFileDemo.java,DataStreamDemo.java //SequenceInputStream(Enumeration e) //通过简单的回顾我们知道了Enumeration是Vector中的一个方法的返回值类型。 //Enumeration<E> elements() Vector<InputStream>v = new Vector<InputStream>(); InputStreams1 = new FileInputStream("ByteArrayStreamDemo.java"); InputStreams2 = new FileInputStream("CopyFileDemo.java"); InputStreams3 = new FileInputStream("DataStreamDemo.java"); v.add(s1); v.add(s2); v.add(s3); Enumeration<InputStream>en = v.elements(); SequenceInputStreamsis = new SequenceInputStream(en); BufferedOutputStreambos = new BufferedOutputStream(new FileOutputStream("Copy.java")); //如何写读写呢,其实很简单,你就按照以前怎么读写,现在还是怎么读写 byte[]bys = new byte[1024]; intlen = 0; while((len = sis.read(bys)) != -1) { bos.write(bys,0, len); } bos.close(); sis.close(); } }
(1) 序列化流(ObjectOutputStream):把对象按照流一样的方式存入文本文件或者在网络中传输。
对象 -- 流数据(ObjectOutputStream)
反序列化流(ObjectInputStream):把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 -- 对象(ObjectInputStream)
(2)如何实现序列化呢? NotSerializableException:未序列化异常
让被序列化的对象所属类实现序列化接口。
类通过实现java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态 序列化或反序列化。
该接口居然没有任何方法,类似于这种没有方法的接口被称为标记接口。
(3)为什么会有问题呢?
* Person类实现了序列化接口,那么它本身也应该有一个标记值。
* 这个标记值假设是100。
* 开始的时候:
* Person.class-- id=100
* wirte数据: oos.txt -- id=100
* read数据: oos.txt -- id=100
* 现在:
* Person.class-- id=200
* wirte数据: oos.txt -- id=100
* read数据: oos.txt -- id=100
* 我们在实际开发中,可能还需要使用以前写过的数据,不能重新写入。怎么办呢?
* 回想一下原因是因为它们的id值不匹配。
* 每次修改java文件的内容的时候,class文件的id值都会发生改变。
* 而读取文件的时候,会和class文件中的id值进行匹配。所以,就会出问题。
* 但是呢,如果我有办法,让这个id值在java文件中是一个固定的值,这样,你修改文件的时候,这个 id值还会发生改变吗?
* 不会。现在的关键是我如何能够知道这个id值如何表示的呢?
* 不用担心,你不用记住,也没关系,点击鼠标即可。
* 你难道没有看到黄色警告线吗?
* 我们要知道的是:
* 看到类实现了序列化接口的时候,要想解决黄色警告线问题,就可以自动产生一个序列化id值。
* 而且产生这个值以后,我们对类进行任何改动,它读取以前的数据是没有问题的。
* 注意:
* 我一个类中可能有很多的成员变量,有些我不想进行序列化。请问该怎么办呢?
* 使用transient关键字声明不需要序列化的成员变量
publicclass Person implements Serializable { private static final longserialVersionUID = -2071565876962058344L; private String name; // private int age; private transient int age; // int age; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" +name + ", age=" + age + "]"; } }
public class ObjectStreamDemo { publicstatic void main(String[] args) throws IOException,ClassNotFoundException { //由于我们要对对象进行序列化,所以我们先自定义一个类 //序列化数据其实就是把对象写到文本文件 //write(); read(); } privatestatic void read() throws IOException, ClassNotFoundException { //创建反序列化对象 ObjectInputStreamois = new ObjectInputStream(new FileInputStream("oos.txt")); //还原对象 Objectobj = ois.readObject(); //释放资源 ois.close(); //输出对象 System.out.println(obj); } privatestatic void write() throws IOException { //创建序列化流对象 ObjectOutputStreamoos = new ObjectOutputStream(new FileOutputStream("oos.txt")); //创建对象 Personp = new Person("林青霞", 27); //public final void writeObject(Object obj) oos.writeObject(p); //释放资源 oos.close(); } }
(4)面试题:
什么时候序列化?
如何实现序列化?
什么是反序列化?
(1)属性集合类。是一个可以和IO流相结合使用的集合类
Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串
是Hashtable的子类,说明是一个Map集合。
public class PropertiesDemo { publicstatic void main(String[] args) { //作为Map集合的使用 // 下面这种用法是错误的,一定要看API,如果没有<>,就说明该类不是一个泛型类,在使用的时候就不能加泛型 //Properties<String, String> prop = new Properties<String, String>(); Propertiesprop = new Properties(); //添加元素 prop.put("it002","hello"); prop.put("it001","world"); prop.put("it003","java"); //System.out.println("prop:" + prop); //遍历集合 Set<Object>set = prop.keySet(); for(Object key : set) { Objectvalue = prop.get(key); System.out.println(key+ "---" + value); } } }
(2)特有功能
* public ObjectsetProperty(String key,String value): 添加元素
* public String getProperty(String key): 获取元素
* public Set<String> stringPropertyNames(): 获取所有的键的集合
public class PropertiesDemo2 { publicstatic void main(String[] args) { //创建集合对象 Propertiesprop = new Properties(); //添加元素 prop.setProperty("张三", "30"); prop.setProperty("李四", "40"); prop.setProperty("王五", "50"); //public Set<String> stringPropertyNames():获取所有的键的集合 Set<String>set = prop.stringPropertyNames(); for(String key : set) { Stringvalue = prop.getProperty(key); System.out.println(key+ "---" + value); } } }
把键值对形式的文本文件内容加载到集合中
public void load(Reader reader)
public void load(InputStreaminStream)
把集合中的数据存储到文本文件中
public void store(Writerwriter,String comments)
public void store(OutputStreamout,String comments)
------------------------------------------------------------------------------------------------------------------------------------------
* 单机版游戏:
* 进度保存和加载。
* 三国群英传,三国志,仙剑奇侠传...
* 吕布=1
* 方天画戟=1
public class PropertiesDemo3 { publicstatic void main(String[] args) throws IOException { //myLoad(); myStore(); } privatestatic void myStore() throws IOException { //创建集合对象 Propertiesprop = new Properties(); prop.setProperty("林青霞", "27"); prop.setProperty("武鑫", "30"); prop.setProperty("刘晓曲", "18"); //publicvoid store(Writer writer,String comments):把集合中的数据存储到文件 Writerw = new FileWriter("name.txt"); prop.store(w,"helloworld"); w.close(); } privatestatic void myLoad() throws IOException { Propertiesprop = new Properties(); //public void load(Reader reader):把文件中的数据读取到集合中 //注意:这个文件的数据必须是键值对形式 Readerr = new FileReader("prop.txt"); prop.load(r); r.close(); System.out.println("prop:"+ prop); } }
(4)案例:
A:我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么。判断是否有键为"lisi"的,如果有就修改其值为100
* 分析:
* A:把文件中的数据加载到集合中
* B:遍历集合,获取得到每一个键
* C:判断键是否有为"lisi"的,如果有就修改其值为"100"
* D:把集合中的数据重新存储到文件中
public class PropertiesTest { publicstatic void main(String[] args) throws IOException { //把文件中的数据加载到集合中 Propertiesprop = new Properties(); Readerr = new FileReader("user.txt"); prop.load(r); r.close(); //遍历集合,获取得到每一个键 Set<String>set = prop.stringPropertyNames(); for(String key : set) { //判断键是否有为"lisi"的,如果有就修改其值为"100" if("lisi".equals(key)) { prop.setProperty(key,"100"); break; } } //把集合中的数据重新存储到文件中 Writerw = new FileWriter("user.txt"); prop.store(w,null); w.close(); } }
B:写一个程序实现控制猜数字小游戏程序不能玩超过5次
public class PropertiesTest2 { publicstatic void main(String[] args) throws IOException { // 读取某个地方的数据,如果次数不大于5,可以继续玩。否则就提示"游戏试玩已结束,请付费。" //创建一个文件 //File file = new File("count.txt"); //if (!file.exists()) { //file.createNewFile(); //} //把数据加载到集合中 Propertiesprop = new Properties(); Readerr = new FileReader("count.txt"); prop.load(r); r.close(); //我自己的程序,我当然知道里面的键是谁 Stringvalue = prop.getProperty("count"); intnumber = Integer.parseInt(value); if(number > 5) { System.out.println("游戏试玩已结束,请付费。"); System.exit(0); }else { number++; prop.setProperty("count",String.valueOf(number)); Writerw = new FileWriter("count.txt"); prop.store(w,null); w.close(); GuessNumber.start(); } } }
* nio包在JDK4出现,提供了IO流的操作效率。但是目前还不是大范围的使用。
* 有空的话了解下,有问题再问我。
* JDK7的之后的nio:
* Path:路径
* Paths:有一个静态方法返回一个路径
* publicstatic Path get(URI uri)
* Files:提供了静态方法供我们使用
* public static long copy(Path source,OutputStream out): 复制文件
* public static Path write(Path path,Iterable<? extendsCharSequence> lines,Charset cs,OpenOption... options)
publicclass NIODemo { public static void main(String[] args)throws IOException { // public static long copy(Pathsource,OutputStream out) //Files.copy(Paths.get("ByteArrayStreamDemo.java"), new // FileOutputStream( // "Copy.java")); ArrayList<String> array =new ArrayList<String>(); array.add("hello"); array.add("world"); array.add("java"); Files.write(Paths.get("array.txt"),array, Charset.forName("GBK")); } }