目录
1、IO流概述
2、字节流的使用
2.1、FileInputStream字节输入流
2.1.1、读取方式一
2.1.2、读取方式二
2.1.3、字节流读取数据如何避免中文乱码
2.2、OutputStream字节输出流
2.3、案例:复制粘贴小案例
3、字符流
3.1、FileReader字符输入流
3.1.1、读取方式一(性能较差,不建议使用)
3.1.2、读取方式二(用数组来存)
3.2、FileWriter字符输出流
4、缓冲流
4.1、字节缓冲流
4.1.1、字节缓冲输入流BufferedInputStream
4.1.2、字节缓冲输出流BufferedOutputStream
5.转换流
5.1、转换输入流
5.2、转换输出流
6、对象序列化技术
7、打印流printStream/PrintWriter
1、IO流概述
I/O:Input/Output
输入输出流
Input:输入流
Output: 输出流。
引入:
File类只能操作文件对象本身,不能读写文件对象的内容。
读写数据内容,应该使用IO流。
IO流是一个水流模型;IO理解成水管,把数据理解为水流。
按流的方向分为:输入流/输出流。
(1)输出流:以内存为基准,把内存中的数据写出到磁盘文件或者网络介质中的流称为输出流。
(2)输入流:以内存为基准,把磁盘文件中的数据或者网络中的数据读入到内存中去的流称为输入流。
输入流的作用:读取数据到内存。
按照流的内容分为:字节流/字符流。
字节流:流中的数据的最小单位是一个一个的字节,这个流就是字节流。
字符流:流中的数据的最小单位是一个一个的字符,这个流就是字符流。
2、字节流的使用
IO流的体系:
字节流 | 字符流 | |||
字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | |
InputStream | OutputStream | Reader | Writer | 抽象类 |
FileInputStream | FileOutputStream | FileReader | FileWriter | 实现类 |
2.1、FileInputStream字节输入流
作用:以内存为基准,把磁盘文件的数据按照字节的形式读入到内存当中。
简单来说,就是按照字节方式读取文件数据到内存。
构造器:
public FileinputStream(File Path):创建一个字节输入流管道与源文件对象联通。
public FileinputStream(String PathName):创建一个字节输入流管道与源文件路径对接。
方法:
public int read(): 每次读取一个字节返回!!,读取完毕返回-1
2.1.1、读取方式一
一个一个字节读,英文和数字没有问题,
但是中文有问题,可能会出现乱码,
性能较差,禁止使用一个一个读的方案。
public class FileInputStreamDemo01 {public static void main(String[] args) throws Exception {//1.创建文件对象定位test.txt;File file = new File("Day09Demo/src/test.txt");//2.创建一个字节输入流管道与源文件接通InputStream is =new FileInputStream(file);//3.读取一个字节。int read = is.read();//读取一滴水,一个字节。System.out.println((char)read);//97,加char:aint code1 = is.read();//读取一滴水,一个字节。System.out.println((char)code1);int code2 = is.read();//读取一滴水,一个字节。System.out.println((char)code2);//4.使用while读取字节数//定义一个整数变量存储字节。int ch = 0;while ((ch = is.read())!=-1){System.out.println((char) ch);//中文乱码,一个一个读,强行拆开了。}}
}
2.1.2、读取方式二
public int read(byte[] buffer):从字节输入流中读取字节到字节数组里面中
返回读取的字节数量,没有字节可读就返回-1;
public class FileInputStreamDemo02 {public static void main(String[] args) throws Exception {//创建一个文件对象。
// File file = new File("Day09Demo/test.txt");//创建一个字节输入流管道与源文件对象接通。
// FileInputStream fileInputStream = new FileInputStream(file);//3.简化写法:创建一个字节输入流管道与源文件接通FileInputStream fileInputStream1 = new FileInputStream("Day09Demo/src/test.txt");//4.定义一个字节数组读取数组.byte[] buffer =new byte[3];int len = fileInputStream1.read(buffer);//一次读3字节.System.out.println("读取字节数"+len);String res = new String(buffer);System.out.println(res );int len1 = fileInputStream1.read(buffer);//一次读3字节.System.out.println("读取字节数"+len1);String res1 = new String(buffer);System.out.println(res1 );fileInputStream1 = new FileInputStream("Day09Demo/src/test1.txt");
// int len2 = fileInputStream1.read(buffer);//一次读3字节.
// System.out.println("读取字节数"+len2);
// String res2 = new String(buffer);
// System.out.println(res2 );//还是会乱码,例如"12我",一次读三个就乱码了//终极读法
// byte[] bytes =new byte[3];
// while ((len=fileInputStream1.read(bytes))!=-1){
// String s = new String(bytes);
// System.out.println(s);
// /*
// * abc
// xyz
// iyz
// *
// * */
// }//解决方法byte[] bytes =new byte[3];while ((len=fileInputStream1.read(bytes))!=-1){//读取多少倒出多少String s = new String(bytes,0,len);System.out.println(s);/** abcxyzi** */}}
}
小结:使用字节数组读取内容,效率可以,但是使用字节数组读取文本内容输出,也无法避免中文乱码问题。
2.1.3、字节流读取数据如何避免中文乱码
解决办法:
a.使用字符流。
b.一桶水全部装满,定义一个字节输出和文件大小一样大的桶(其实这并不能完美解决,有文件大小限制,只适合读写小文件)
这里引入一个新的方法
readAllBytes(),可以一次性读完全部文件,返回一个byte[]数组
public class FileInputStreamDemo03 {public static void main(String[] args) throws IOException {File file = new File("Day09Demo/src/test3.txt");//b方法:FileInputStream fileInputStream = new FileInputStream(file);
// byte[] buffer = new byte[(int)file.length()];
// fileInputStream.read(buffer);
// String s = new String(buffer);
// System.out.println(s);//sun公司其实也考虑到了这个问题.所有:byte[] bytes = fileInputStream.readAllBytes();String s = new String(bytes);System.out.println(s);}
}
小结:定义一个字节大小和文件大小一样的桶,可以解决中文乱码问题,但是局限性太大,如文件太大,底层会报错内容溢出。
字节流并不适合读取文本文件输出,读取文件建议使用字符流
2.2、OutputStream字节输出流
FileoutputStream文件字节输出流:、
作用:以内存为基准,把内存中的数据,按照字节的形式写到磁盘文件中去。
简单来说,把内存的数据写出到磁盘中的文件中去。
构造器:
public FileOutputStream(File file):创建一个字节输出流管道通向目标文件对象。
public FileOutputStream(String File):创建一个字节输出流管道通向目标文件路径。
public FileOutputStream(FIle file,boolean append):创建一个追加数据的字节输出。
public FileOutputStream(String file,boolean append):创建一个追加数据的字节输出。
方法:
public void write(int a):写一个字节出去。
public void write(byte[] buffer):写一个字节输出出去。
public void write(byte[] buffer,int pos,int len):写一个字节数组的一部分出去 。
public class OutputStreamDemo04 {public static void main(String[] args) throws IOException {//1.创建一个文件对象定位目标文件。
// File file = new File("Day09Demo/src/test4.txt");//2.创建一个字节输出流管道与目标文件对象接通。
// OutputStream os = new FileOutputStream(file);//3.简化写法:FileOutputStream os = new FileOutputStream("Day09Demo/src/test4.txt",true);//默认是覆盖管道。//4.写数据出去。//a.写一个字节出去(写一滴水出去)os.write(97);//写一个aos.write('b');//写一个b
// os.write('张');//有问题,写不进去[ooo],只写出去了[o]os.write("\r\n".getBytes());//写一个字节数组出去byte[] bytes = new byte[]{65,66,67,68};//写个ABCDos.write(bytes);os.write("\r\n".getBytes());byte[] bytes1 ="我爱Java,Java很厉害".getBytes();os.write(bytes1);os.write("\r\n".getBytes());//换行os.write(bytes1,0,10);os.flush();//立即刷新数据到文件中去,刷新后管道还可用os.close();//关闭管道资源,关闭包含了刷新,关闭后管道不能使用了。}
}
小结:
可以写一个字符,也可以写一个字节数组,也可以写一个字节数组的一部分出去。
管道用完需要关闭,数据生效需要刷新,关闭包括刷新,刷新后流还可以继续使用,关闭流无法继续使用。
字节输出流管道默认是覆盖数据管代,启动管道与写数据前都会清空数据。
2.3、案例:复制粘贴小案例
public class CopyDemo01 {public static void main(String[] args) {try(FileInputStream fileInputStream = new FileInputStream("C:\\Users\\javac\\Pictures\\前端所用杂图\\5848e4a37f5e4fb39f4c4384a9c27523.jpg");FileOutputStream fileOutputStream = new FileOutputStream("Day09Demo/src/3.png");){/** 先读取文件,在直接写入就完成了* */byte[] bytes = new byte[1024];int len=0;while((len=fileInputStream.read(bytes))!=-1) {fileOutputStream.write(bytes, 0, len);}System.out.println("完成了");fileInputStream.close();fileOutputStream.close();}catch (Exception e){e.printStackTrace();}}
}
3、字符流
字符流一个一个字符的读取文本内容输出,可以解决中文读取输出乱码问题,
字符流很适合操作文本内容,一个一个读新性能比较差
3.1、FileReader字符输入流
FileReader:文件字符输入流。
作用:以内存为基准,把磁盘文件的数据读入到内存当中,简单来说,读取文本文件内容到内存中去
构造器:
public FileReader(File file):创建一个字符输入流与源文件对象接通。
public FIleReader(StringfilePath):创建一个字符输入流与源文件路径接通。
方法:
public int read():读取一个字符的编号返回!,读取完毕返回-1
public int read(char[] buffer):读取一个字符数组,读取多少个字符就返回多少个字符数量。
3.1.1、读取方式一(性能较差,不建议使用)
public class FileReaderDemo01 {public static void main(String[] args) throws IOException {//创建一个文件对象通向源文件File file = new File("Day10Demo/src/test.txt");Reader fileReader = new FileReader(file);int c;while ((c=fileReader.read())!=-1){System.out.print((char) c);}}
}
3.1.2、读取方式二(用数组来存)
public class FileReaderDemo02 {public static void main(String[] args) throws IOException {FileReader fileReader = new FileReader("Day10Demo/src/test.txt");int len;char[] chars =new char[1024];while ((len=fileReader.read(chars))!=-1){System.out.print(new String(chars,0,len));}}
}
3.2、FileWriter字符输出流
作用:以内存为基准,把内存中的数组按照字节的形式写出到磁盘文件中去。
构造器:
public FileWriter(File file):创建一个字符输出流管道通向目标文件对象。
public FileWriter(String filePath):创建一个字符输出流管道通向目标文件路径。
public FIleWriter(File file,boolean append):创建一个追加数据的字符输出流管道通向文件。
public FileWriter(String filePath,boolean append):创建一个追加数据的字符输出流通向文件目录。
方法:
public void write(int c):写一个字符出去。
public void write(String c):写一个字符串出去。
public void write(char[] buffer):写一个字符数组出去。
public void write(String c,int pos,int len):写字符串出去一部分。
public class FileWriterDemo03 {public static void main(String[] args) throws Exception {//创建一个字符输出流管道通向目标文件FileWriter fileWriter = new FileWriter("Day10Demo/src/test1.txt",true);//true为追加//2.写一个字符出去fileWriter.write(97);fileWriter.write('a');fileWriter.write('我');//3.写一个字符串fileWriter.write("Java是优美的语言");//4.写一个字符数组出去。fileWriter.write("我爱中国".toCharArray());fileWriter.write("这是不是很棒",4,2);fileWriter.write("\n");
// fileWriter.flush();fileWriter.close();}
}
4、缓冲流
缓冲流可以提高文件的读写速度,提高读写性能。
字节流 | 字符流 | |||
字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | |
InputStream | OutputStream | Reader | Writer | 抽象类 |
FileInputStream | FileOutputStream | FileReader | FileWriter | 实现类 |
BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWrite | 缓冲流实现类 |
4.1、字节缓冲流
4.1.1、字节缓冲输入流BufferedInputStream
作用:可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提升读取文件的效率。
构造器:public BufferedInputStream(InputStream in)
原理:缓冲字节输入流管道包装了 低级的字节输入流之后,就自带一个缓冲池,缓冲池是一块区域,一次可以直接提取8kb的数据到内存,以后程序直接在内存的缓冲池里面读取数据,性能很快!!!!
public class BufferedInputStreamDemo01 {public static void main(String[] args) throws Exception{FileInputStream fileInputStream = new FileInputStream("Day10Demo/src/test1.txt");BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);byte[] bytes =new byte[1024];int len;while ((len=bufferedInputStream.read(bytes))!=-1){System.out.println(new String(bytes,0,len));}}
}
4.1.2、字节缓冲输出流BufferedOutputStream
作用:可以把低级的字节输出流包装成一个高级的缓冲输出流,提高写的性能。
构造器:public BufferedoutputStream(OutputStream os)
原理: 缓冲字节流自带了8kb的缓冲池,数据直接写入到缓冲池中去了,性能提高了!!
public class BufferedOutputStreamDemo02 {public static void main(String[] args) throws Exception{FileOutputStream fileOutputStream = new FileOutputStream("Day10Demo/src/test2.txt");BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);//把一个低级的字节输出流包装成一个高级的缓冲字节输出流bos.write('1');bos.write('a');bos.write('b');bos.write('c');bos.write("java".getBytes());int len;byte[] buffer = new byte[]{95,96,97};bos.write(buffer);bos.close();}
}
注意:字符流不同编码读取会产生乱码问题
代码编码 文件编码 中文情况
UTF-8 UTF-8 不乱码
GBK GBK 不乱码
UTF-8 GBK 乱码
例如:(这里0代表一个字节)
“我爱java” =>0000000000(UTF-8)=>(GBK解析)00000000
这样解析,中文自然出问题,反之也是。
识别的时候中文因为在各编码所占字节数不同,所以导致了解析乱码问题,所以转换流就出现了
5.转换流
5.1、转换输入流
作用:可以把原始的字节流按照当前默认的代码编码转换成字符流,
可以把原始的字节流按照指定的编码转换成字符流。
字节流 | 字符流 | |||
字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | |
InputStream | OutputStream | Reader | Writer | 抽象类 |
FileInputStream | FileOutputStream | FileReader | FileWriter | 实现类 |
BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWrite | 缓冲流实现类 |
无 | 无 | InputStreamReader | OutputStreamWriter | 转换流实现类 |
构造器:
public InputStreamReader(InputStream is):可以使用当前代码默认的编码转换成字符流。
public InputStreamReader(InputStream is,String chatset):可以指定编码把字节转换成字符流。
public class InputStreamReaderDemo01 {public static void main(String[] args) throws Exception {FileInputStream fileInputStream = new FileInputStream("Day10Demo/src/gbk2.txt");byte[] bytes =new byte[2];int len;while((len=fileInputStream.read(bytes))!=-1){System.out.print(new String(bytes,0,len,"GBK"));/** 注意,这样的解决办法并不好,因为我们的字符串并不是只有中文,还有数字、标点符号等等* */}/** 优质的办法* */InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"GBK");char[] ch = new char[1024];int len1;while ((len1=inputStreamReader.read(ch))!=-1){System.out.println(new String(ch,0,len));}}
}
小结:
可以解决字节流读取不同编码乱码问题。
字符输入流可以把字节流按照默认编码转换成字符流。
字符输入流可以解决不同编码乱码问题。
5.2、转换输出流
作用:可以指定编码把字节输出流转换成字符输出流。
构造器:
public OutputStreamWriter(OutputStream os):用当前默认的编码UTF-8把字节输出流转换数据写入
public OutputStreamWriter(OutputStream os,String charset):按照指定的编码格式写入进去。
小结:
字符输出转换流可以指定编码把字节输出流转换成字符输出流。
从而实现指定写出去的字符编码!!
public class OutputStreamWriterDemo02 {public static void main(String[] args) throws Exception{FileOutputStream fileOutputStream = new FileOutputStream("E:\\Document\\Java\\JavaPlus\\Day10Demo\\src\\GBK.txt");OutputStreamWriter gbk = new OutputStreamWriter(fileOutputStream, "GBK");gbk.write("我爱Java");gbk.close();}
}
6、对象序列化技术
作用:把Java对象数组直接存储到文件中去,需要的时候又把Java对象数据文件恢复到Java对象中去。
字节流 | 字符流 | |||
字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | |
InputStream | OutputStream | Reader | Writer | 抽象类 |
FileInputStream | FileOutputStream | FileReader | FileWriter | 实现类 |
BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWrite | 缓冲流实现类 |
无 | 无 | InputStreamReader | OutputStreamWriter | 转换流实现类 |
ObjectInputStream | ObjectOutputStream | 对象序列化 |
对象序列化:ObjectOutputStream
作用:把内存中的Java对象数据保存到文件中去。
构造器:public ObjectOutputStream(OutputStream out)
序列化方法:public final void writerObject(Object obj)
对象反序列化: ObjectOutputStream
注意:如果要序列化对象,那么对象必须实现序列化接口:implements serializable
存起来不是给我看的,是一种特殊的由JVM设置的格式
序列化版本号
加入序列号版本:private static final long serialVersionUID =2L;
必须序列化使用版本号和反序列号的版本号一致才能进行正常序列化,否则报错。
package _07序列化;import java.io.Serializable;public class User implements Serializable {private String loginName;private transient String userName;//排除这个序列化private String passWord;public User(String loginName, String userName, String passWord) {this.loginName = loginName;this.userName = userName;this.passWord = passWord;}@Overridepublic String toString() {return "User{" +"loginName='" + loginName + '\'' +", userName='" + userName + '\'' +", passWord='" + passWord + '\'' +'}';}public String getLoginName() {return loginName;}public void setLoginName(String loginName) {this.loginName = loginName;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassWord() {return passWord;}public void setPassWord(String passWord) {this.passWord = passWord;}
}
序列化
public class SerializeDemo01 {public static void main(String[] args) throws Exception{FileOutputStream fileOutputStream = new FileOutputStream("Day10Demo/src/obj.dat");ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);//创建一个对象User user = new User("cloud","12234","xxx");objectOutputStream.writeObject(user);objectOutputStream.close();}
}
反序列化:
public class SerializeDemo02 {public static void main(String[] args) throws Exception {//1.定义一个低级的字节输入流通向目标文件。FileInputStream fileInputStream = new FileInputStream("Day10Demo/src/obj.dat");ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);//2.开始反序列化Object o = objectInputStream.readObject();User user =(User) o;//大范围转小范围强转换。System.out.println(user);System.out.println("反序列化完成!!!");}
}
7、打印流printStream/PrintWriter
printStream以字节输出流为底层
printWriter以字符输出流为底层
打印流的作用:
1.可以方便快速把数据输出到磁盘中去。
2.可以实现打印啥,就是啥出去,不会出现乱码问题处理。
打印流的构造器:
public PrintStream(OutputStream os)
public PrintStream(String filePath)
public class PrintStreamDemo01 {public static void main(String[] args) throws Exception {//打印流(字节输出流为底层),注意,这里因为需要printStream的新特性,是不能用多态写法的PrintStream printStream = new PrintStream("Day10Demo/src/text3.txt");printStream.println("asccaacacac");printStream.print("可以打印任何东西到文件中");printStream.println("烫烫烫烫烫");//注意:加ln换行printStream.close();//打印流(以字符输出流为底层Writer)PrintWriter printWriter = new PrintWriter("Day10Demo/src/text4.txt");printWriter.println("测试测试");printWriter.println("烫烫烫烫");printWriter.close();}
}
扩展:System.setOut()可以改变输出的方向:
Public static void setOut(PrintStream out)
public class PrintStreamDemo02 {public static void main(String[] args) throws Exception{PrintStream printStream = new PrintStream("Day10Demo/src/log.txt");System.setOut(printStream);System.out.println("输出转变到了log文件了");}}
8、Properties属性集对象
Properties属性集对象其实就是一个Map集合,也就是一个键值对集合,但是我们一般不会当作集合来使用,因为已经由Map了。
Properties代表的是一个属性文件,可以把键值对的数据存储到一个文件中去,
属性文件:后缀是.properties结尾的文件,里面的内存都是key=value。
在一些大型框架中经常使用,属性文件都是很重要的系统配置文件。
Properties的方法:
-- properties Obejct setProperty(String key,String value):设置一对属性
-- properties String getProperty(String key):根据属性名获取属性值
-- public Set<String> stringPropertyNames():所有键的名称的集合。
-- public void store(OutputStream out,String comments):保存数据到属性文件中去。
-- public void store(Writer fw,String comments):保存数据到属性文件中去。comments是备注,不是内容
--public synchronized void load(InputStream instream):加载属性文件到集合对象中去。
--public synchronized void load(ReaderStream rs ):加载属性文件到集合对象中去。
写入文件案例:
public class PropertiesDemo01 {public static void main(String[] args) {//1.创建一个属性集对象,Properties的对象。Properties properties = new Properties();
// properties.put("admin","123456"); 不建议使用Map集合properties.setProperty("admin","123456");properties.setProperty("admin","121212");//键值不不能重复覆盖了。properties.setProperty("kuku","12345");System.out.println(properties);//2.把属性集对象的数据存入到属性文件中去(重点)try (FileOutputStream fileOutputStream = new FileOutputStream("Day10Demo/src/users.properties");){/** 参数一:保存文件的输出管道* 参数二:保存心得,对保存的数据注释* */properties.store(fileOutputStream,"I an very happy");}catch (IOException e){e.printStackTrace();}}
}
读出文件案例:
public class PropertiesDemo02 {public static void main(String[] args) {//1.创建一个属性集合对象,Properties properties = new Properties();try(FileInputStream fileInputStream = new FileInputStream("Day10Demo/src/users.properties");){properties.load(fileInputStream);System.out.println(properties.get("admin"));System.out.println(properties);}catch (Exception e){e.printStackTrace();}}
}