java基础06 IO流
IO流用于在设备间进行数据传输的操作。
凡是从外部设备流向中央处理器cpu的数据流,称为输入流,即程序读数据的时候用输入流;
凡是从中央处理器CPU流向外部设备的数据流,称为输出流,即程序把数据写入外设的时候用输出流;
Java IO流类图结构:
IO流分类
字节流:
InputStream
FileInputStream
BufferedInputStream
OutputStream
FileOutputStream
BufferedOutputStream
字符流:
Reader
FileReader
BufferedReader
Writer
FileWriter
BufferedWriter
一、FileInputStream/FileOutputStream
/** * IO流读取操作,将项目下的read.txt的内容写到write.txt中 * * @author sun * */ public class DemoIO { public static void main(String[] args) throws IOException { FileInputStream fileInputStream = new FileInputStream("read.txt"); FileOutputStream fileOutputStream = new FileOutputStream("write.txt"); byte[] bys = new byte[1024]; int len = 0; while ((len = fileInputStream.read(bys)) != -1) { fileOutputStream.write(bys, 0, len); } // 关闭流,采用“先开后关原则” fileOutputStream.close(); fileInputStream.close(); } }
二、FileInputStream/BufferedInputStream; FileOutputStream/ BufferedOutputStream
使用实例:
/** * 需求:把当前项目目录下的read.txt复制到当前项目目录下的write.txt中 * * @author sun * */ public class Demo { public static void main(String[] args) throws IOException { method4("read.txt", "write.txt"); } // 高效字节流一次读写一个字节数组: public static void method4(String srcString, String destString) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcString)); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString)); byte[] bys = new byte[1024]; int len = 0; while ((len = bis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); bis.close(); } // 高效字节流一次读写一个字节: public static void method3(String srcString, String destString) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcString)); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString)); int by = 0; while ((by = bis.read()) != -1) { bos.write(by); } bos.close(); bis.close(); } // 基本字节流一次读写一个字节数组 public static void method2(String srcString, String destString) throws IOException { FileInputStream fis = new FileInputStream(srcString); FileOutputStream fos = new FileOutputStream(destString); byte[] bys = new byte[1024]; int len = 0; while ((len = fis.read(bys)) != -1) { fos.write(bys, 0, len); } fos.close(); fis.close(); } // 基本字节流一次读写一个字节 public static void method1(String srcString, String destString) throws IOException { FileInputStream fis = new FileInputStream(srcString); FileOutputStream fos = new FileOutputStream(destString); int by = 0; while ((by = fis.read()) != -1) { fos.write(by); } fos.close(); fis.close(); } }
当我们创建字节输出流对象时,做了几件事情?
1:调用系统功能创建文件;
2:创建对象;
3:让该对象指向文件;
三、字符流 = 字节流 + 编码表
字符流
Reader
|--InputStreamReader
|--FileReader
|--BufferedReader
Writer
|--OutputStreamWriter
|--FileWriter
|--BufferedWriter
/** * 把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中 * * @author sun * */ public class DemoIO { public static void main(String[] args) throws IOException { // 封装数据源 BufferedReader br = new BufferedReader(new FileReader("a.txt")); // 封装目的地 BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); // 两种方式其中的一种一次读写一个字符数组 char[] chs = new char[1024]; int len = 0; while ((len = br.read(chs)) != -1) { bw.write(chs, 0, len); bw.flush(); } // 释放资源 bw.close(); br.close(); } }
public class CopyFileDemo2 { public static void main(String[] args) throws IOException { // 封装数据源 BufferedReader br = new BufferedReader(new FileReader("a.txt")); // 封装目的地 BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt")); // 读写数据 String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 bw.close(); br.close(); } }
四、数据操作流(操作基本类型数据的流)
(1)可以操作基本类型的数据
(2)流对象名称
DataInputStream
DataOutputStream
/** * * @author sun * */ public class DemoIO { public static void main(String[] args) throws IOException { // 写 // write(); // 读 read(); } private static void read() throws IOException { // DataInputStream(InputStream in) // 创建数据输入流对象 DataInputStream dis = new DataInputStream(new FileInputStream("write.txt")); // 读数据 byte b = dis.readByte(); short s = dis.readShort(); int i = dis.readInt(); long l = dis.readLong(); float f = dis.readFloat(); double d = dis.readDouble(); char c = dis.readChar(); boolean bb = 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); } private static void write() throws IOException { // DataOutputStream(OutputStream out) // 创建数据输出流对象 DataOutputStream dos = new DataOutputStream(new FileOutputStream("read.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)三种
A:ByteArrayInputStream,ByteArrayOutputStream
B:CharArrayReader,CharArrayWriter
C:StringReader,StringWriter
/* * 内存操作流:用于处理临时存储信息的,程序结束,数据就从内存中消失。 * 字节数组: * ByteArrayInputStream * ByteArrayOutputStream * 字符数组: * CharArrayReader * CharArrayWriter * 字符串: * StringReader * StringWriter */ public class ByteArrayStreamDemo { public static void main(String[] args) throws IOException { // 写数据 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 写数据 for (int x = 0; x < 10; x++) { baos.write(("hello" + x).getBytes()); } byte[] bys = baos.toByteArray(); // 读数据 // ByteArrayInputStream(byte[] buf) ByteArrayInputStream bais = new ByteArrayInputStream(bys); int by = 0; while ((by = bais.read()) != -1) { System.out.print((char) by); } baos.close(); bais.close(); } }
六、打印流
(1)字节打印流,PrintWriter ;字符打印流,PrintStream ;
(2)特点:
A:只操作目的地,不操作数据源
B:可以操作任意类型的数据
C:如果启用了自动刷新,在调用println()方法的时候,能够换行并刷新
D:可以直接操作文件
问题:哪些流可以直接操作文件呢?
看API,如果其构造方法能够同时接收File和String类型的参数,一般都是可以直接操作文件的
(3)复制文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("b.txt"),true);
String line = null;
while((line=br.readLine())!=null) {
pw.println(line);
}
pw.close();
br.close();
public class PrintWriterDemo2 { public static void main(String[] args) throws IOException { // 创建打印流对象 // PrintWriter pw = new PrintWriter("pw2.txt"); PrintWriter pw = new PrintWriter(new FileWriter("read.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类下面有这样的两个字段
in 标准输入流
out 标准输出流
(2)三种键盘录入方式
A:main方法的args接收参数
B:System.in通过BufferedReader进行包装
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
C:Scanner
Scanner sc = new Scanner(System.in);
(3)输出语句的原理和如何使用字符流输出数据
A:原理
System.out.println("helloworld");
PrintStream ps = System.out;
ps.println("helloworld");
B:把System.out用字符缓冲流包装一下使用
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
public class Demo { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入一个字符串:"); String line = br.readLine(); System.out.println("你输入的字符串是:" + line); System.out.println("请输入一个整数:"); line = br.readLine(); int i = Integer.parseInt(line); System.out.println("你输入的整数是:" + i); } }
八、随机访问流
(1)可以按照文件指针的位置写数据和读数据。
/* * 随机访问流: * RandomAccessFile类不属于流,是Object类的子类。 * 但它融合了InputStream和OutputStream的功能。 * 支持对文件的随机访问读取和写入。 * * public RandomAccessFile(String name,String mode):第一个参数是文件路径,第二个参数是操作文件的模式。 * 模式有四种,我们最常用的一种叫"rw",这种方式表示我既可以写数据,也可以读取数据 */ public class RandomAccessFileDemo { public static void main(String[] args) throws IOException { // write(); read(); } private static void read() throws IOException { // 创建随机访问流对象 RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw"); int i = raf.readInt(); System.out.println(i); // 该文件指针可以通过 getFilePointer方法读取,并通过 seek 方法设置。 System.out.println("当前文件的指针位置是:" + raf.getFilePointer()); char ch = raf.readChar(); System.out.println(ch); System.out.println("当前文件的指针位置是:" + raf.getFilePointer()); String s = raf.readUTF(); System.out.println(s); System.out.println("当前文件的指针位置是:" + raf.getFilePointer()); // 我不想重头开始了,我就要读取a,怎么办呢? raf.seek(4); ch = raf.readChar(); System.out.println(ch); } private static void write() throws IOException { // 创建随机访问流对象 RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw"); // 怎么玩呢? raf.writeInt(100); raf.writeChar('a'); raf.writeUTF("中国"); raf.close(); } }
九、合并流
(1)把多个输入流的数据写到一个输出流中。
(2)构造方法:
A:SequenceInputStream(InputStream s1, InputStream s2)
B:SequenceInputStream(Enumeration<? extends InputStream> e)
public class SequenceInputStreamDemo2 { public static void main(String[] args) throws IOException { // 需求:把下面的三个文件的内容复制到Copy.java中 Vector<InputStream> v = new Vector<InputStream>(); InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java"); InputStream s2 = new FileInputStream("CopyFileDemo.java"); InputStream s3 = new FileInputStream("DataStreamDemo.java"); v.add(s1); v.add(s2); v.add(s3); Enumeration<InputStream> en = v.elements(); SequenceInputStream sis = new SequenceInputStream(en); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("Copy.java")); byte[] bys = new byte[1024]; int len = 0; while ((len = sis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); sis.close(); } }
十、序列化流
(1)可以把对象写入文本文件或者在网络中传输
(2)如何实现序列化呢?
让被序列化的对象所属类实现序列化接口。
该接口是一个标记接口。没有功能需要实现。
(3)注意问题:
把数据写到文件后,在去修改类会产生一个问题。
如何解决该问题呢?
在类文件中,给出一个固定的序列化id值。
而且,这样也可以解决黄色警告线问题
/* * 序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输。对象 -- 流数据(ObjectOutputStream) * 反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 -- 对象(ObjectInputStream) */ public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { // write(); read(); } private static void read() throws IOException, ClassNotFoundException { // 创建反序列化对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( "oos.txt")); // 还原对象 Object obj = ois.readObject(); // 释放资源 ois.close(); // 输出对象 System.out.println(obj); } private static void write() throws IOException { // 创建序列化流对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( "oos.txt")); // 创建对象 Person p = new Person("sunting", 7); // public final void writeObject(Object obj) oos.writeObject(p); // 释放资源 oos.close(); } }
十一、Properties
(1)是一个集合类,Hashtable的子类
(2)特有功能
A:public Object setProperty(String key,String value)
B:public String getProperty(String key)
C:public Set<String> stringPropertyNames()
(3)和IO流结合的方法
把键值对形式的文本文件内容加载到集合中
public void load(Reader reader)
public void load(InputStream inStream)
把集合中的数据存储到文本文件中
public void store(Writer writer,String comments)
public void store(OutputStream out,String comments)
public class PropertiesDemo{ public static void main(String[] args) throws IOException { // myLoad(); myStore(); } private static void myStore() throws IOException { // 创建集合对象 Properties prop = new Properties(); prop.setProperty("sun", "7"); prop.setProperty("ting", "3"); prop.setProperty("s", "8"); //public void store(Writer writer,String comments):把集合中的数据存储到文件 Writer w = new FileWriter("name.txt"); prop.store(w, "helloworld"); w.close(); } private static void myLoad() throws IOException { Properties prop = new Properties(); // public void load(Reader reader):把文件中的数据读取到集合中 // 注意:这个文件的数据必须是键值对形式 Reader r = new FileReader("prop.txt"); prop.load(r); r.close(); System.out.println("prop:" + prop); } }
十二、NIO
(1)JDK4出现的NIO,对以前的IO操作进行了优化,提供了效率。但是大部分我们看到的还是以前的IO
(2)JDK7的NIO的使用
Path:路径
Paths:通过静态方法返回一个路径
Files:提供了常见的功能
复制文本文件
把集合中的数据写到文本文件
/* * nio包在JDK4出现,提供了IO流的操作效率。但是目前还不是大范围的使用。 * * JDK7的之后的nio: * Path:路径 * Paths:有一个静态方法返回一个路径 * public static Path get(URI uri) * Files:提供了静态方法供我们使用 * public static long copy(Path source,OutputStream out):复制文件 * public static Path write(Path path,Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options) */ public class NIODemo { public static void main(String[] args) throws IOException { // public static long copy(Path source,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")); } }
最后的最后,让我们来聊聊路径~~
在JavaIO流中读取文件,必然逃脱不了文件路径问题。接下来我们聊聊文件的路径问题。
十四、关于文件读取时路径问题:
在eclipse中的情况:
Eclipse中启动jvm都是在项目根路径上启动的.比如有个项目名为demo,其完整路径为:D:\workspace \demo.那么这个路径就是jvm的启动路径了.所以以上代码如果在eclipse里运行,则输出结果为” D:\workspace \demo.”
Tomcat中的情况.
如果在tomcat中运行web应用,此时,如果我们在某个类中使用如下代码:
File f = new File(“.”);
String absolutePath = f.getAbsolutePath();
System.out.println(absolutePath);
那么输出的将是tomcat下的bin目录.我的机器就是” D:\apache-tomcat-8.0.37\bin\.”,由此可以看出tomcat服务器是在bin目录下启动jvm 的.其实是在bin目录下的” catalina.bat”文件中启动jvm的.