一、IO流概述
IO 流简单来说就是 Input 和 Output 流,IO 流主要是用来处理设备之间的数据传输,Java 对于数据的操作都是通过流实现的,而Java用于操作流的对象都在 IO 包中。
分类
- 按操作数据分为:字节流和字符流。如:Reader 和 InputStream
- 按流向分为:输入流和输出流。如:InputStream 和 OutputStream
IO 流常用的基类: InputStream , OutputStream
字符流的抽象基类:Reader,Writer
二、流的概念
程序通过流来完成输入/输出。流是生产或消费信息的抽象,流通过输入输出与物理设备链接,尽管与它们链接的物理设备不尽相同,所有流的行为具有相同的方式。这样就意味一个输入流能够抽象多种不同类型的输入:从磁盘文件、从键盘或从网络套接字;同样,一个输出流可以输出到控制台、磁盘文件或相连的网络。
三、字符流
简介
1) 字符流中的对象融合了编码表,也就是系统默认的编码表,我们的系统一般是GBK编码。
2) 字符流只用来处理文本数据,字节流用来处理媒体数据。
3) 数据最常见的表现方式是文件,字符流用于操作文件的子类一般是 FileReader 和 FileWriter 。
注意事项:
- 1.写入文件后必须要用 flush() 刷新。
- 2.用完流后记得要关闭流
- 4.使用流对象要抛出 IO 异常
- 3.定义文件路径时,可以用 File.separator 、 “/” 或者 “\”。
- 5.在创建一个文件时,如果目录下有同名文件将被覆盖。
- 6.在读取文件时,必须保证该文件已存在,否则出异常
不关闭流的后果
如果不关闭的话,那么这个 IO 资源就会被他一直占用,这样别人想用就没有办法用了,所以这回造成资源浪费。但是在 Java7 中,资源的连接如果是在 try()中,凡是实现接口 AutoCloseable 的类都可以使用这种方式进行资源访问,并且会自动释放。
Java7 中新的语法其实就是一种编译器优化,资源总会关闭,不管是否发生异常。人通常会犯错,但是编译器却不会,所以使用新的方式进行资源访问,能够避免隐藏的bug,而在java7中绝大多数的资源访问都已经重新实现了 AutoCloseable 接口,包括网络访问 socket 等,所以基本上可以放心的使用,就算没实现,编译器也会快速报错。
如何关闭流
关闭流需要按照从外到内的顺序关闭。关闭流只需要关闭最外层的包装流,其他流会自动调用关闭,这样可以保证不会抛异常。
从内到外关闭报错信息:
Exception in thread "main" java.io.IOException: Stream closed at sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:45) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:118) at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129) at java.io.BufferedWriter.close(BufferedWriter.java:264) at IOTest.main(IOTest.java:18)
四、JDK1.7新特性–自动关闭类
JDK1.7之后出现了一个重要的接口,以及改造了一个重要的方法结构:
- AutoCloseable自动关闭接口
- try(){ } ----- catch{ } ----- finally{ }
相应的一些资源也实现了该接口,如 preparedStatement 、 Connection 、 InputStream 、outputStream 等等资源接口。
接口的实现类要重写 close() 方法,将要关闭的资源定义在 try() 中,这样当程序执行完毕之后,资源将会自动关闭。
/** Java7 之前 **/
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/** Java7 之后 **/
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("");) {
fis.read();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
五、字节流
简介
在 Java 中,字节流一般适用于处理字节数据(诸如图片、视频),字符流适用于处理字符数据(诸如文本文件),但二者并没有严格的功能划分,因为有转换流的存在,使得对于数据的处理变得更加灵活。
InputStream 和 OutputStream 分别是字节输入流与字节输出流的基类,它们的子类都是字节流,主要用在按字节来处理二进制数据。
一个简单的样例
/**
* 使用IO技术,创建一个目录,然后复制一个文件到该目录!
*/
public class CreatDemo {
public static void main(String[] args) throws IOException {
File file = new File("." + File.separator + "test" + File.separator + "demonstration.txt");
FileInputStream fis = new FileInputStream(file.getCanonicalPath());
byte[] b = new byte[(int) file.length()];
fis.read(b);
File copyfile = new File("." + File.separator + "test" + File.separator + "copy.txt");
copyfile.createNewFile();
FileOutputStream fos = new FileOutputStream(copyfile);
fos.write(b);
fos.close();
fis.close();
Runtime.getRuntime().exec("notepad " + copyfile.getCanonicalPath());
}
}
额外补充
/**
* 使用IO技术,开发出一个控制台的资源管理器!
* 要求:
* 从命令行输入一个路径!
* 如果存在将该目录下所有的文件和文件夹列举出来,
* 如果不存在则输出不存在该路径。
*/
public class ResourceAdmin {
public static void listAllFiles(File dir) throws IOException{
File[] files = dir.listFiles();
if(files == null || files.length == 0) {
System.out.println("此目录为空!!");
return;
}
for(File f : files) {
if(f.isDirectory()) {
System.out.println("文件夹:" + f.getCanonicalPath());
listAllFiles(f);
}else {
System.out.println("文件:" + f.getCanonicalPath());
}
}
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个文件夹路径");
String src = sc.nextLine();
File dir = new File(src);
listAllFiles(dir);
}
}
六、字符流与字节流的区别
在 java.io 包中操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。在字节流中输出数据主要是使用 OutputStream 完成,输入使的是 InputStream ,在字符流中输出主要是使用 Writer 类完成,输入流主要使用 Reader 类完成。(这四个都是抽象类)
字符流处理的单元为2个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode 字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。
所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列。
字节流提供了处理任何类型的IO操作的功能,但它不能直接处理 Unicode 字符,而字符流就可以
字节流是最基本的,所有的 InputStrem 和 OutputStream 的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的 encode 来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader , OutputStreamWriter来关联,实际上是通过 byte[] 和 String 来关联 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。
一个简单的网络爬虫
public class FoundUrl {
public static String Connect(String address) throws IOException {
URL url = new URL(address);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream in = conn.getInputStream();
// 要爬网页的编码
BufferedReader reader = new BufferedReader(new InputStreamReader(in,"utf-8"));
StringBuffer sb = new StringBuffer();
String line = null;
while((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
@SuppressWarnings("static-access")
public static void main(String[] args) throws IOException {
FoundUrl fu = new FoundUrl();
// 要爬的网页
String url = "https://www.baidu.com/";
String found = fu.Connect(url);
System.out.println(found);
String information = found;
// 输出路径
File file = new File("C:\\Users\\Administrator\\Desktop\\Demo.html");
FileWriter fw = new FileWriter(file);
fw.write(information);
fw.close();
Runtime.getRuntime().exec("notepad " + file.getCanonicalPath());
}
}
总结图示
本文内容部分取自百度内容,如有雷同部分请见谅。