IO
所谓IO就是输出输出(input/output)。一般的理解都是相对于计算机而言的输入输出。
比如:
输出设备:显示器,耳机,音响,打印机…..
输入设备:键盘,鼠标,麦克风……
上面的所有的输入输出都是相对于计算机而言的。
我们这里说的输入输出是相对于内存而言的
- 数据读取到内存就是输入。
- 数据从内存写出就是输出。
输入输出的原理:
输出的数据流称之为输出流,输入的数据流称之为输入流。
面试题:java的流是如何分类的? 按照方向分为:输入流和输出流。 按照处理方式:分为字节流和字符流。
File类
在java.io包下有一个File。 这个类的所有对象都表示一个文件。(一个File类的对象就是一个文件或者文件夹的抽象表示)
内部的常量
- static String pathSeparator 与系统相关的路径分隔符字符,为方便起见,表示为字符串。
- static char pathSeparatorChar 与系统相关的路径分隔符。
- static String separator 与系统相关的默认名称 – 分隔符字符,以方便的方式表示为字符串。
- static char separatorChar 与系统相关的默认名称分隔符。
前面两个常量是路径相关的分隔符。后面两个是名称分隔符。
以前在linux中分隔符是 "/" 在windows中是 "\"
构造方法
- File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例。
- File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
- File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。
- File(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。
相对路径和绝对路径。 所谓相对路径,就是相对于当前的文件而言,目标的位置。 所谓绝对路径,就是从盘符开始的路径。 一般建议使用相对路径。
undefined
File类的一些API:
获取信息的—
- exists()测试此抽象路径名表示的文件或目录是否存在。
- getName() 返回由此抽象路径名表示的文件或目录的名称。
- getAbsolutePath()返回此抽象路径名的绝对路径名字符串。
- getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。
- getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。
- isDirectory()测试此抽象路径名表示的文件是否为目录。
- isFile()测试此抽象路径名表示的文件是否为普通文件。
- lastModified() 返回此抽象路径名表示的文件上次修改的时间。
- length() 返回由此抽象路径名表示的文件的长度。
- list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。
- listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。
案例:
undefined
我们查看文件的大小,从现在开始都是看字节:
1B byte = 8bit 1K = 1024B 1M = 1024K 1G = 1024M 1T = 1024G 1P = 1024T
创建删除判断的一些API—-
- canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件。
- canRead() 测试应用程序是否可以读取由此抽象路径名表示的文件。
- canWrite() 测试应用程序是否可以修改由此抽象路径名表示的文件。
- delete() 删除由此抽象路径名表示的文件或目录。
- boolean mkdir() 创建由此抽象路径名命名的目录。
- boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
- createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
异常问题:
案例:
undefined
字节输入流InputStream
所有的字节输入流的老祖宗就是 java.io.InputStream。
字节流的特点:是以字节的方式处理数据
这个类是一个抽象类,其目的就是为了让其他的类实现。所以我们都是使用这个类的子类。
浏览API:
- int available() 返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 (这个方法对于网络流是无效的)
- void close() 关闭此输入流并释放与流相关联的任何系统资源。
- void mark(int readlimit) 标记此输入流中的当前位置。
- boolean markSupported() 测试这个输入流是否支持 mark和 reset方法。
- abstract int read() 从输入流读取数据的下一个字节。
- int read(byte[] b) 从输入流读取一些字节数,并将它们存储到缓冲区 b 。
- int read(byte[] b, int off, int len) 从输入流读取最多 len字节的数据到一个字节数组。
- void reset() 将此流重新定位到上次在此输入流上调用 mark方法时的位置。
- long skip(long n) 跳过并丢弃来自此输入流的 n字节数据。
FileInputStream–InputStream的实现类
构造方法:
- FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
- FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
API:就是参考父类的API。
使用:按照单个的字节读取数据。
undefined
输入流还提供了一些API可以按组读取数据
按组读取数据的API-1:
public class FileInputStreamTest1 {
public static void main(String[] args) {
// 通过文件名构建一个字节输入流
InputStream in = null;
try {
// 根据文件创建输入流
File file = new File("names.txt");
in = new FileInputStream(file);
// 按组读取
// int read(byte[] b)读取最多b.length字节的数据到字节数组 返回值实际读取的长度。如果到达文件末尾返回-1
// 准备一个字节数组
byte [] buff = new byte[500];
// 读取数据到缓冲数组
int len = in.read(buff);
System.out.println("实际读取的长度是:"+len);
String str = new String(buff);// 根据整个数组创建字符串
System.out.println(str);
System.out.println("--------------");
// 根据实际读取的字节长度创建字符串
//(字节数组,偏移量,使用长度)
String info = new String(buff,0,len);
System.out.println(info);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(in!=null){
in.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
按组读取数据API-2:
undefined
实际应用中读取文件:
上面的按组读取的例子中,都是一次性将所有的数据读取到内存中。
如果一个文件有一个T,我们肯定无法一次性读取到内存。
解决方案:按组,循环读取,重复使用缓冲数组。
看程序:
undefined
字节输出流OuputStream
OuputStream是所有的字节输出流的老祖宗。
API:
- void close() 关闭此输出流并释放与此流相关联的任何系统资源。
- void flush()刷新此输出流并强制任何缓冲的输出字节被写出。
- void write(byte[] b) 将 b.length字节从指定的字节数组写入此输出流。
- void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
- abstract void write(int b) 将指定的字节写入此输出流。
FileOutputStream–OutputStream的实现类
- FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。
- FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
api就是父类中的方法。
构造方法案例:
public class FileOutputStreamTest1 {
public static void main(String[] args) {
// 申明字节输出流
OutputStream out = null;
try{
// 创建流
// 根据文件创建输出流
File file = new File("st.txt");
// 从文件开头开始写数据。 覆盖了原有的数据
out = new FileOutputStream(file);
// 根据文件创建输出流
// append:
// true 从文件的末尾开始写,续在文件原有数据的后面。
// false 从文件的开头开始写,覆盖原有数据
out = new FileOutputStream(file,true);
// 根据文件名创建输出流
// 从文件开头开始写数据。 覆盖了原有的数据
out = new FileOutputStream("hell.txt");
// append:
// true 从文件的末尾开始写,续在文件原有数据的后面。
// false 从文件的开头开始写,覆盖原有数据
out = new FileOutputStream("hell.txt",true);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
tips:如果我们创建的输出流指向的文件不存在,当我们写出数据的时候会自动创建这个文件。
三个写数据的API:
输出一个字节:
public class FileOutputStreamTest1 {
public static void main(String[] args) {
// 申明字节输出流
OutputStream out = null;
try{
//--------------------------------------------------
// 创建流
out = new FileOutputStream("hell.txt",true);// ***
// 准备一个字符串
String str = "吃鸡全靠苟";
// 将字符串转换为字节数组
byte[] bytes = str.getBytes();//???
// 循环的将字节数组的内容写入文件
for(byte b : bytes) {
out.write(b);// ****
}
//-------------------------------------------------
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
写出一个字节数组:
public class FileOutputStreamTest2 {
public static void main(String[] args) {
// 申明字节输出流
OutputStream out = null;
try{
//------------------------
// 创建流
// 根据文件创建输出流
File file = new File("st1.txt");
// 从文件开头开始写数据。 覆盖了原有的数据
out = new FileOutputStream(file);
// 准备一个字符串
String str = "吃鸡全靠苟";
// 将字符串转换为字节数组
byte[] bytes = str.getBytes();
// 直接写出一个字节数组
out.write(bytes);
//------------------
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
写出字节数组的一部分:
undefined
一个需求:
从控制台输入一个商品的信息,将商品信息写入商品文件。文件中一行存储一个商品信息。
undefined
再来一个需求:
将上一个需求写文件的商品,读取出来,使用[9527,尤克里里,1525] 形式输出到控制台。
undefined
文件拷贝:
文件拷贝流程:
我们来拷贝一个压缩文件:
undefined
字符流
字符流,就是按照字符处理文件的。
字节流,按照字节处理文件。可以处理所有的文件。一般使用中,字节流偏多。
文本文件:纯文本文件。不保存任何的格式,文件头之类的信息。
字符流往往是用来 处理纯文本文件或者网络上的文本信息。
字符输入流Reader
Reader本身是一个抽象类
API:
- abstract void close() 关闭流并释放与之相关联的任何系统资源。
- void mark(int readAheadLimit) 标记流中的当前位置。
- boolean markSupported() 告诉这个流是否支持mark()操作。
- int read() 读一个字符
- int read(char[] cbuf) 将字符读入数组。
- abstract int read(char[] cbuf, int off, int len) 将字符读入数组的一部分。
- int read(CharBuffer target) 尝试将字符读入指定的字符缓冲区。
- boolean ready() 告诉这个流是否准备好被读取。
- void reset() 重置流。
- long skip(long n) 跳过字符
FileReader –Reader类的子类
也是我们常用的字符输入流. (FileInputStream)
构造方法
- FileReader(File file) 创建一个新的 FileReader ,给出 File读取。
- FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
API:所有的API都是来自于父类。
使用:
undefined
字符输出流Writer
API:
- Writer append (char c) 将指定的字符附加到此作者。
- Writer append(CharSequence csq) 将指定的字符序列附加到此作者。
- Writer append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。
- abstract void close() 关闭流,先刷新。
- abstract void flush() 刷新流。
- void write(char[] cbuf) 写入一个字符数组。
- abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
- void write(int c) 写一个字符
- void write(String str) 写一个字符串
- void write(String str, int off, int len) 写一个字符串的一部分。
FileWriter–在字节流中对应:FileOutputStream
构造方法:
- FileWriter(File file) 给一个File对象构造一个FileWriter对象。
- FileWriter(File file, boolean append) 给一个File对象构造一个FileWriter对象。
- FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。
- FileWriter(String fileName, boolean append) 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
没有自己的特殊API。全部是父类的API。
案例:
undefined
下载一个HTML文件
package com.qidian.spider; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection;
字符流的文件拷贝
package com.qidian.demo1; import java.io.*;
缓冲字符输入流:BufferedReader
构造方法:
- BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
- BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。
在缓冲流中用到了” 装饰设计模式“。 使用BufferedReader包裹一个Reader,实现缓冲。
其中大部分API和Reader没啥区别。有一个特殊的API:
- *String readLine() 读一行文字。
案例:
undefined
缓冲字符输出流–BufferedWriter
构造方法:
- BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
- BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
创建BufferedWriter的时候需要包裹一个Writer对象
特殊的API:
- *void newLine() 写一行行分隔符。
缓冲输出流要注意的问题:
当调用了缓冲输出流的write方法之后,数据可能只是写在缓冲区,并没有实际写到输出流。理论上当关闭输出流的时候,所有缓冲区的数据会刷出。 一般建议我们手动调用 flush方法刷出缓冲区数据。
案例:
undefined
缓冲字节输入流–BufferedInputStream
构造方法
- BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
- BufferedInputStream(InputStream in, int size) 创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。
其他的API,不需要说明,和FileOutputStream用法完全一致。只不过提高了底层的效率。
缓冲字节输出流:BufferedOutputStream
构造方法
- BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
- BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
API……..你就把它当FileOutputStream使用就行了。
我们使用 缓冲(高效)字节流完成一个文件拷贝的过程。
案例:
undefined
转换流目的:将字节流转换为字符流。 能不能将字符流转换为字节流?NO
转换输入流:InputStreamReader
构造方法:
- InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。
- InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。
- InputStreamReader(InputStream in, CharsetDecoder dec) 创建一个使用给定字符集解码器的InputStreamReader。
- InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。
API和Reader一致。
转换输出流:OutputStreamWriter
- OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建一个使用给定字符集编码器的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。
API和writer完全一致。
案例: 转换输入流。
undefined
字节输出流转换为字符输出流:
案例:
public class OutputStreamWriterTest {
public static void main(String[] args) {
OutputStream out = null;
OutputStreamWriter writer = null;
try {
// 创建字节流
out = new FileOutputStream("hehe.txt");
// 将字节流包裹成字符流
writer = new OutputStreamWriter(out);
// 写字符串
writer.write("一个字符串\n");
writer.write("下午两点上课\n");
writer.write("上完可,做好防护,去做核算");
writer.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null){
out.close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
对象流
序列化
为了方便数据的存储或者传输,将数据或者对象转换为可存储或者传输的形式的过程称之为序列化。相反就是反序列化。
例子:在线购物,卖家给你打包的过程就是序列化。你拆包裹的过程就是反序列化。
项目中的:
将一个对象转换为json输出到客户端。将对象转换为json就是序列化。
将JSON转换对象的过程就是反序列化。
对象流就是专门用来对对象进行序列化和反序列化。
对象输出流:ObjectOutputStream(写出)
专门用来序列化对象的。(卖家打包)
构造方法:
- ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
对象输出流内部其实是包裹了一个字节输出流。
对象输出流的作用:
API:
- void close() 关闭流。
- void write(byte[] buf) 写入一个字节数组。
- void write(byte[] buf, int off, int len) 写入一个子字节数组。
- void write(int val) 写一个字节。
- void writeBoolean(boolean val) 写一个布尔值。
- void writeByte(int val) 写入一个8位字节。
- void writeBytes(String str) 写一个字符串作为字节序列。
- void writeChar(int val) 写一个16位的字符。
- void writeChars(String str) 写一个字符串作为一系列的字符。
- void writeDouble(double val) 写一个64位的双倍。
- void writeFloat(float val) 写一个32位浮点数。
- void writeInt(int val) 写一个32位int。
- void writeLong(long val) 写一个64位长
- void writeShort(int val) 写一个16位短。
- void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
对象输出流是写一个对象出去的操作。
java中都有哪些对象?
java中的对象真的很多,但是 所有的对象统称为Object。其实对象流特意准备了几个方法,写这些对象。
- writeXxxx(Xxx xxx) 这里的Xxxx表示任何一个基本类型。
- writeObject(Object obj) 其余的类型全部使用Object。
案例1: 序列化基本类型
public class ObjectWriteTest1 {
public static void main(String[] args) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try{
// 创建字节输出流
out = new FileOutputStream("ints.txt");
// 创建对象流
objOut = new ObjectOutputStream(out);
// 写出一个整形
objOut.writeInt(12);
objOut.writeDouble(12.5);
// 刷出数据
objOut.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
案例2: 序列化自定义类型
准备一个实体类:
public class User {
private int id;
private String name;
}
测试:
出现异常。
这个异常产生的原因是因为ObjectOutputStream 写的对象 必须实现接口java.io.Serializable。否则无法序列化。这个接口本身是没有任何的方法和成员的,它就是用来标记一个类的对象是否可以被序列化。
public interface Serializable {
}
我们让我们的User类实现这个接口:
完整的测试代码:
public class ObjectWriteTest2 {
public static void main(String[] args) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try{
// 创建字节输出流
out = new FileOutputStream("u.txt");
// 创建对象流
objOut = new ObjectOutputStream(out);
// 创建一个User
User u = new User(1,"就乌尔奇");
// 写出对象u
objOut.writeObject(u);
// 刷出数据
objOut.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
对象输入流:ObjectInputStream(读取)
专门用来反序列化对象的。(买家拆包裹)
构造方法:
- ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。
ObjectInputStream会包裹一个InputStream
其他的API:
- int read() 读取一个字节的数据。
- int read(byte[] buf, int off, int len) 读入一个字节数组。
- boolean readBoolean() 读取布尔值。
- byte readByte() 读取一个8位字节。
- char readChar() 读一个16位字符。
- double readDouble() 读64位双倍。
- float readFloat() 读32位浮点数。
- int readInt() 读取一个32位int。
- long readLong() 读64位长。
- short readShort() 读取16位短。
- Object readObject() 从ObjectInputStream读取一个对象。
有个小问题:无法判定是否到达了文件的末尾。
当使用ObjectInputStream读取数据的时候,如果出现了EOFException。说明到达了文件的末尾。
案例1: 读取上一小节写入文件的int和double。
public class ObjectInputTest1 {
public static void main(String[] args) {
InputStream in = null;
ObjectInputStream objIn = null;
try{
// 创建字节流
in = new FileInputStream("ints.txt");
// 创建对象流
objIn = new ObjectInputStream(in);
// 反序列化数据
int i = objIn.readInt();
System.out.println(i);
double d = objIn.readDouble();
System.out.println(d);
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(objIn!=null)
objIn.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
案例2:读取写入的自定义对象
public class ObjectInputTest2 {
public static void main(String[] args) {
InputStream in = null;
ObjectInputStream objIn = null;
try{
// 创建字节流
in = new FileInputStream("u.txt");
// 创建对象流
objIn = new ObjectInputStream(in);
// 反序列化数据
Object o = objIn.readObject();
// 类型转换
User u = (User) o;
System.out.println(u);
}catch (Exception e){
e.printStackTrace();
}finally {
try{
if(objIn!=null)
objIn.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
一组对象的读写
情况1: 循环读写
循环的写入数据
public class ObjectWriteTest3 {
public static void main(String[] args) {
OutputStream out = null;
ObjectOutputStream objOut = null;
try{
// 创建字节输出流
out = new FileOutputStream("us.txt");
// 创建对象流
objOut = new ObjectOutputStream(out);
// 创建一组User
List us = new ArrayList<>();
for (int i = 0;i< 100; i++){
us.add(new User(i+1,"鸣人"+i));
}
// 循环的写出对象
for (User u : us) {
objOut.writeObject(u);
}
// 刷出数据
objOut.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(out!=null)
out.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
循环的读数据
public class ObjectInputTest3 {
public static void main(String[] args) {
InputStream in = null;
ObjectInputStream objIn = null;
try{
// 创建字节流
in = new FileInputStream("us.txt");
// 创建对象流
objIn = new ObjectInputStream(in);
// 反序列化数据
// 循环读取数据
// 知道循环次数的处理
// for (int i = 0;i < 100;i++){
// Object o = objIn.readObject();
// User u = (User) o;
// System.out.println(u);
// }
// 不知道循环次数
while(true){
// 当到达文件的末尾的时候,会抛出异常EOFException
Object o = objIn.readObject();
User u = (User) o;
System.out.println(u);
}
}catch(EOFException e){
System.out.println("读取完成");
} catch (Exception e){
e.printStackTrace();
}finally {
try{
if(objIn!=null)
objIn.close();
}catch (IOException ex){
ex.printStackTrace();;
}
}
}
}
Original: https://www.cnblogs.com/xiaoxiaodeboke/p/16032817.html
Author: 潇潇消消气
Title: java高级-续1
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581269/
转载文章受原作者版权保护。转载请注明原作者出处!