一:IO流
io流(input/output)
- 作用:实现两个设备之间数据的通信
- 设备:磁盘(硬盘),内存,键盘,文件,网络,控制台
网络:当前主机之外的网上资源
- 分类:
根据操作的方式:输入流和输出流;
根据数据的类型:字节流和字符流
- 字节流:传输的是字节,可以操作任意类型的文件 ----音频,视频,文件,图片等
- 字符流:传输的是字节,不同点是在传输过程中加入了编码的操作,让我们的操作更方便 -----文本
- 以内存为参考 :
字节流:
字节输入流:InputStream
字节输出流:OutputStream
字符流的两个父类:
字符读入流(将数据输入内存):Reader
字符写出流(将数据从内存取出):Writer
- 字符流与字节流的基本介绍
字符流:----传输的是字符,只能传输字符
字符写出流 Writer
write() write(字符数组) write(字符串)
字符读入流 Reader
read() read(数组)
字符缓冲写出流 BufferedWriter
newline() 跨平台换行
字符缓冲读入流 BufferedReader
readLine() 读一行
字节流:---传输的是字节,可以传输任何类型的数据
字节输出流 OutputStream
字节输入流 InputStream
字节缓冲输出流 BufferedOutputStream
字节缓冲输入流 BufferedInputStream
字符流
- 以磁盘的数据存储为例,将数据写入文件
分析:因为操作的是文本,所以使用字符流
写入文件-----写出流------FileWriter
- 写内容---FileWriter
- 代码
package com.qf.test; import java.io.FileWriter; import java.io.IOException; public class Demo1 { public static void main(String[] args) throws IOException { //1创建FileWriter对象并关联相应的文件 //注意点: //一:如果只写文件的名字,不写具体路径,默认路径是当前的工程 //二:对于关联的文件,如果之前不存在,程序会自动创建一个,如果存在,会将原来的内容覆盖 //三:可以自己指定路径,但是必须保证路径是真实存在的,否则报异常---FileNotFountException(系统找不到指定的路径。) FileWriter fileWriter = new FileWriter("test1.txt"); //2.写 //注意点四:在执行write方法时,数据被临时放到了流对象的内部数组中,这个数组是一个字节数组,会默认去查编码表 fileWriter.write("chenchen"); //3.//刷新---将临时数组中的数据放入磁盘 //fileWriter.flush(); //4.关闭流--两个功能:a:关闭流 b:刷新 //第五个注意点:流对象使用完后必须关闭 fileWriter.close(); //第六个注意点:当流对象关闭之后,不能再进行操作,否则会报异常:Stream closed //fileWriter.write("haha"); } }
- 文件的续写:FileWriter(String file,boolean value)
当value为true的时候,不会将原来的内容覆盖,会接着写
- 代码
package com.qf.test; import java.io.FileWriter; import java.io.IOException; public class Demo2 { public static void main(String[] args) { //1. FileWriter fileWriter = null; try { fileWriter = new FileWriter("test2.txt",true); fileWriter.write("haha"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (fileWriter != null) { try { fileWriter.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
- 读内容----FileReader
实例:从文件中读取内容,并显示在控制台-------FileReader
int read() ----一个字符一个字符的读,每次读出一个字符(返回的是ASCII码 0--65535,读到末尾为-1)
- 代码
package com.qf.test; import java.io.FileReader; import java.io.IOException; public class Demo3 { public static void main(String[] args) throws IOException { //1.创建FileReader的对象 FileReader fileReader = new FileReader("test2.txt"); //2.读 /* * read():一个字符一个字符的读,每次读出一个字符 * read(数组):一次可以读出多个字符 ,数组的作用:每次会将读出的字符临时放到这个数组中 */ //read():一个字符一个字符的读,每次读出一个字符 int num = 0; // num = fileReader.read();//返回的是ASCII码 // System.out.println((char)num); // num = fileReader.read(); // System.out.println((char)num); // num = fileReader.read(); // System.out.println((char)num); // num = fileReader.read(); // System.out.println((char)num); // num = fileReader.read(); // System.out.println((char)num); // num = fileReader.read(); // System.out.println(num); while ((num = fileReader.read())!= -1) { System.out.println((char)num); } //3.关闭资源 fileReader.close(); } }
read(数组):一次可以读出多个字符,数组的作用:每次会将读出的字符临时放到这个数组中
数组的大小决定我们一次可以读到的字符个数,一般数组大小<=1kb 返回值代表本次读到的真实的字符个数,如果返回值是-1代表读完了
- 代码
package com.qf.test; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; public class Demo4 { public static void main(String[] args) throws IOException { //1. FileReader fileReader = new FileReader("test2.txt"); //2. /* * read():一个字符一个字符的读,每次读出一个字符 * read(数组):一次可以读出多个字符 ,数组的作用:每次会将读出的字符临时放到这个数组中 */ //read(数组):一次可以读出多个字符 ,数组的作用:每次会将读出的字符临时放到这个数组中 /* 数组是临时存放数据的地方,我们会将读到的字符放到临时数组中,数组的大小决定了我们一次可以读到的字符个数. * 一般这个数组的大小<=1kB * 返回值代表本次读到的真实的字符个数,如果返回值是-1代表读完了. */ char[] arr = new char[2]; int num = 0; // num = fileReader.read(arr); // System.out.println(Arrays.toString(arr)+" num:"+num); // num = fileReader.read(arr); // System.out.println(Arrays.toString(arr)+" num:"+num); // num = fileReader.read(arr); // System.out.println(new String(arr,0,num)+" num:"+num); // num = fileReader.read(arr); // System.out.println(Arrays.toString(arr)+" num:"+num); while ((num = fileReader.read(arr)) != -1) { System.out.println(new String(arr,0,num)+" num:"+num); } fileReader.close(); } }
- 实例:完成文件的复制
(\代表转义字符 \t:制表符 \n换行符 \\:表示普通的\ 在代表路径的时候,\\与 / 是一个意思 )
- 代码
package com.qf.test; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* * 实例:完成文件的复制 * 将Demo1的东西复制到Demo1copy.java * * D:\workspace\BigDataSZ1903N19\src\com\qianfeng\test\Demo1.java:绝对路径 * BigDataSZ1903N19\src\com\qianfeng\test\Demo1.java:相对路径 * * 相对路径:从路径中间的某个部位开始一直到当前的文件 * 绝对路径:一个文件的完整路径,从根目录开始到当前的文件 */ public class Demo5 { public static void main(String[] args) throws IOException { //1.创建读入流和写出流 FileReader fileReader = new FileReader("D:\\workspace/BigData1923N19\\src\\com\\qf\\test\\Demo1.java"); FileWriter fileWriter = new FileWriter("Demo1copy.java"); //读写 //使用一次读一个 int num =0 ; // while ((num = fileReader.read()) != -1) { // fileWriter.write(num);//可以实现自动转换 // fileWriter.flush(); // } //使用一次读多行 char[] arr = new char[10]; while ((num = fileReader.read(arr)) != -1) { fileWriter.write(arr,0,num);//可以实现自动转换 fileWriter.flush(); } fileReader.close(); fileWriter.close(); } }
- 字符缓冲流(字符缓冲区)
定义:为了提高读写的能力,本身没有读写的能力想要进行读写就必须借助于字符流实现
可以将缓冲流类比于催化剂
字符缓冲流分类:
字符缓冲读入流:BufferReader 没有读的能力
字符缓冲写出流:BufferWriter 没有写的能力
注意:window系统 换行符 \r\n linux/unix 换行符 \n
bufferedWriter.write("\r\n") //换行
bufferedWriter.newLine() //换行---支持跨平台
- 代码
package com.qf.test; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; public class Demo6 { public static void main(String[] args) throws IOException { //0.创建字符写出流对象 //1.创建字符缓冲写出流对象 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("text3.txt")); //2.写 bufferedWriter.write("bingbing"); //windows系统 换行符 \r\n linux/unix 换行符 \n //bufferedWriter.write("\r\n");//换行 bufferedWriter.newLine();//换行---支持跨平台 bufferedWriter.write("chenchen"); //3.关闭资源 a.关闭内部的写出流 b.关闭自己 c.刷新 bufferedWriter.close(); } }
字符缓冲读入流 ------ 一次读一个字符 int read()
一次读多个字符 int read(char[] arr)
一次读一行 String readLine()
原理:一个字符一个字符的读,直到读到换行符为止,然后将所有读到的字符返回
注意点:不会讲当前的换行符返回;返回值就是我们读到的内容,如果读完了,返回null
- 代码
package com.qf.test; import java.io.FileReader; import java.io.IOException; import java.io.BufferedReader; public class Demo7 { public static void main(String[] args) throws IOException { //1.创建字符缓冲读入流 BufferedReader bufferedReader = new BufferedReader(new FileReader("text3.txt")); //读 //一次读一个字符 int num = 0; // while ((num = bufferedReader.read()) != -1) { // System.out.print((char)num); // } //一次读多个字符 // char[] arr = new char[5]; // while ((num = bufferedReader.read(arr)) != -1) { // System.out.print(new String(arr,0,num)); // } //一次读一行 readline() //原理:一个字符一个字符的读,直到读到换行符为止.然后将所有读到的字符返回 //注意点:不会将当前的换行符返回 ;返回值就是我们读到的内容.如果读完了,返回null String data = null; while ((data = bufferedReader.readLine()) != null) { System.out.print(data); //System.out.println();//换行 System.out.print("\r\n"); } bufferedReader.close(); } }
- 实例:使用缓冲流实现文件的复制
package com.qf.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* * 使用缓冲流实现文件的复制 */ public class Demo8 { public static void main(String[] args) throws IOException { //1.创建读入流和写出流 FileReader fileReader = new FileReader("D:\\workspace/BigData1923N19\\src\\com\\qf\\test\\Demo1.java"); FileWriter fileWriter = new FileWriter("Demo1copy.java"); BufferedReader bufferedReader = new BufferedReader(fileReader); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); //读写 //使用一次读一个 int num =0 ; while ((num = bufferedReader.read()) != -1) { bufferedWriter.write(num);//可以实现自动转换 fileWriter.flush(); } //使用一次读多行 // char[] arr = new char[10]; // while ((num = bufferedReader.read(arr)) != -1) { // bufferedWriter.write(arr,0,num);//可以实现自动转换 // fileWriter.flush(); // } //使用一次读一行 String data = null; while ((data = bufferedReader.readLine()) != null) { bufferedWriter.write(data); bufferedWriter.newLine(); bufferedWriter.flush(); } bufferedReader.close(); bufferedWriter.close(); } }
- 模拟字符缓冲读入流BufferedReader
分析:
1.要属于流的体系
2.要有一个Reader类型的成员变量
3.要有一个带参数的构造方法接收外部传入的流对象
4.模拟readLine(),实现读一行的功能
5.关闭流
- 代码
import java.io.FileReader; import java.io.IOException; import java.io.Reader; //1.要属于流的体系 class MyBufferedReader extends Reader{ //2.要有一个Reader类型的成员变量 Reader reader; //3.要有一个带参数的构造方法接收外部传入的流对象 public MyBufferedReader(Reader reader) { this.reader = reader; } //4.模拟readLine(),实现读一行的功能 public String readLine() throws IOException { //a.创建一个临时的可变字符串,用于装当前行的字符 StringBuffer stringBuffer = new StringBuffer(); //b.使用read()方法读 int num = 0; while ((num = this.reader.read()) != -1) { if (num == '\r') { continue; }else if (num == '\n') { return stringBuffer.toString(); }else { stringBuffer.append((char)num); } } //当文本为空时 if (stringBuffer.length() == 0) { return null; } //当文本只有一行,没有换行符 return stringBuffer.toString(); //增加效率的代码 } //5.关闭流 public void close() throws IOException { //关闭自己 this.reader.close(); } @Override public int read(char[] cbuf, int off, int len) throws IOException { // TODO Auto-generated method stub return 0; } } public class Demo1 { public static void main(String[] args) throws IOException { MyBufferedReader myBufferedReader = new MyBufferedReader(new FileReader("text3.txt")); String data = null; while ((data = myBufferedReader.readLine()) != null) { System.out.print(data); System.out.println(); } myBufferedReader.close(); } }
LineNumberReader : 时BufferedReader的子类,不能读,但是可以提高效率,特有功能:设置行号,获取行号
- 代码
package com.qf.test; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; public class Demo2 { public static void main(String[] args) throws IOException { LineNumberReader lineNumberReader = new LineNumberReader(new FileReader("D:\\workspace/BigData1923N19\\src\\com\\qf\\test\\Demo1.java")); //设置行号,默认从0开始,从1开始打印 lineNumberReader.setLineNumber(10); String data = null; while ((data = lineNumberReader.readLine()) != null) { System.out.print(lineNumberReader.getLineNumber());//获取行号 System.out.print(data); System.out.println(); } lineNumberReader.close(); } }
字节流
- FileInputStream与FileOutputStream
FileInputStream read() read(byte[] b)
FileOutputStream write(byte[] b)
- 代码
package com.qf.test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class Demo3 { public static void main(String[] args) throws IOException { //写入数据 write(); //读1 read1(); //读2 read2(); //读3 read3(); } //将数据写入磁盘 public static void write() throws IOException { //1.创建字节输出流并关联文件 FileOutputStream fileOutputStream = new FileOutputStream("test1.txt"); //2.写--这里只能写字节 fileOutputStream.write("bingbing".getBytes()); //3.关闭资源 fileOutputStream.close(); } //读1 一次读一个字节 public static void read1() throws IOException { //1.创建字节输入流并关联文件 FileInputStream fileInputStream = new FileInputStream("test1.txt"); //2.读 int num = 0; while ((num = fileInputStream.read()) != -1) { System.out.print((char)num); } //3.关闭资源 fileInputStream.close(); } //读2 一次读多个字节 public static void read2() throws IOException { //1.创建字节输入流并关联文件 FileInputStream fileInputStream = new FileInputStream("test1.txt"); //2.读 int num = 0; byte[] bytes = new byte[5]; while ((num = fileInputStream.read(bytes)) != -1) { System.out.print(new String(bytes,0,num)); } //3.关闭资源 fileInputStream.close(); } //读3 一次全部读完 public static void read3() throws IOException { //1.创建字节输入流并关联文件 FileInputStream fileInputStream = new FileInputStream("test1.txt"); //获取文件的字节个数 //注意:这种方式适合文件的字节数比较小的时候,大概是几kb之内. int num1 = fileInputStream.available(); //2.读 byte[] bytes = new byte[num1]; fileInputStream.read(bytes); System.out.println(new String(bytes)); //3.关闭资源 fileInputStream.close(); } }
字节缓冲流 BufferedInputStream BufferedOutputStream
- 代码(利用字节缓冲流实现图片的复制)
package com.qf.test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Demo5 { public static void main(String[] args) throws IOException { /* * 使用字节缓冲流实现图片的复制 */ //1.创建字节输入流 FileInputStream fileInputStream = new FileInputStream("D:\\workspace\\BigData1923N20\\100.jpg"); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); //2.创建字节输出流 FileOutputStream fileOutputStream = new FileOutputStream("D:\\workspace\\BigData1923N20\\100copy1.jpg"); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); //3.进行读写 int num = 0; while ((num = bufferedInputStream.read()) != -1) { bufferedOutputStream.write(num); } bufferedInputStream.close(); bufferedOutputStream.close(); } }
标准输入输出流
标准输入流:--- System.in:"标准"输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。
输入源:可以发送数据的设备 输出源:可以接受数据的设备
1.当前的流已经打开并关联了输入源---键盘
2.如果不想让键盘充当输入源,可以通过steIn进行更换
3.是一个字节流
标准输出流:---System.out
- 代码
package com.qf.test; import java.io.IOException; import java.io.InputStream; public class Demo6 { public static void main(String[] args) throws IOException { InputStream inputStream = System.in; //int num = inputStream.read();//把标准输入流的read方法称为阻塞式方法 /* * 实例:实现从键盘不断接收字符的程序 * 要求:一行一行的接收 */ myReadLine(inputStream); } public static void myReadLine(InputStream inputStream) throws IOException { StringBuffer stringBuffer = new StringBuffer(); while (true) { int num = inputStream.read(); if (num == '\r') { continue; }else if (num == '\n') { System.out.println(stringBuffer.toString()); //当用户输入over的时候,结束程序 if (stringBuffer.toString().equals("over")) { break; } //将上一次的值清除掉 stringBuffer.delete(0, stringBuffer.length()); }else { stringBuffer.append((char)num); } } } }
- 不同输入输出源代码的演示
package com.qf.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class Demo8 { public static void main(String[] args) throws IOException { /* * 设备之间数据传输的总结: * 输入源:键盘 * 输出源:控制台 * * 输入源:键盘 * 输出源:文件 * * 输入源:文件 * 输出源:控制台 * * 输入源:文件 * 输出源:文件 */ //将键盘作为输入源 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); //将文件作为输入源 BufferedReader bufferedReader1 = new BufferedReader(new FileReader("temp1.txt")); //将控制台作为输出源 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out)); //将文件作为输出源 BufferedWriter bufferedWriter1 = new BufferedWriter(new FileWriter("temp2.txt")); // * 输入源:键盘 // * 输出源:控制台 String data = null; while ((data = bufferedReader.readLine()) != null) { bufferedWriter.write(data); bufferedWriter.newLine(); bufferedWriter.flush(); } // * 输入源:键盘 // * 输出源:文件 // * 输入源:文件 // * 输出源:控制台 // * 输入源:文件 // * 输出源:文件 bufferedReader.close(); bufferedReader1.close(); bufferedWriter.close(); bufferedWriter1.close(); } }
- 更换输出元 System.setIn()方法代码的演示
注意:这里是临时更换,只能在当前程序中使用更换后的输入源输出源,如果在其他的程序中会自动变回原来的输入源输出源
package com.qf.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintStream; public class Demo9 { public static void main(String[] args) throws IOException { //更换输入源 //注意:这里是临时更换,只能在当前程序中使用更换后的输入源输出源,如果在其他的程序中会自动变回原来的输入源输出源 //从键盘接收数据更替成从文件接收数据 System.setIn(new FileInputStream("D:\\workspace\\BigData1923N20\\src\\com\\qf\\test\\Demo1.java")); //从输出到控制台更替成输出到文件 System.setOut(new PrintStream("D:\\workspace\\BigData1924N11\\Democopy.java")); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out)); String data = null; while ((data = bufferedReader.readLine()) != null) { bufferedWriter.write(data); bufferedWriter.newLine(); bufferedWriter.flush(); } bufferedReader.close(); bufferedWriter.close(); } }
转换流
InputStreamReader:输出转换流
OutputStreamWriter:输入转换流
实现的是从字节流到字符流,本身是字符流
模拟的场景:使用字符缓冲流的readline和newline方法实现字节流的功能
- 代码
package com.qf.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class Demo7 { public static void main(String[] args) throws IOException { //1将标准字节输入流转成字符缓冲读入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); //2.将标准字节输出流转成字符缓冲写出流 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out)); //3.读写 String data = null; while ((data = bufferedReader.readLine()) != null) { bufferedWriter.write(data); bufferedWriter.newLine(); bufferedWriter.flush(); //当输入的内容是over时,结束 if (data.equals("over")) { break; } } bufferedReader.close(); bufferedWriter.close(); } }
打印流
字节打印流:PrintStream:除了拥有输出流的特点之外,还有打印的功能
字符打印流:PrintWriter
字节打印流支持的设备:
1.File类型的文件
2.字符串类型的文件
3.字节输出流
- 字节打印流代码
package com.qf.test; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; public class Demo10 { public static void main(String[] args) throws IOException { PrintStream printStream1 = new PrintStream(new File("test2.txt")); PrintStream printStream2 = new PrintStream("test2.txt"); PrintStream printStream3 = new PrintStream(new FileOutputStream("test.txt")); printStream3.println("haha"); printStream1.close(); printStream2.close(); printStream3.close(); PrintStream printStream4 = new PrintStream(new FileOutputStream("test5.txt")); printStream4.write(97);//00000000 00000000 00000000 01100001 默认将前三个字节砍掉 01100001 a printStream4.write(353);//00000000 00000000 00000001 01100001 默认将前三个字节砍掉 01100001 a printStream4.print(353); //这是print方法的内部实现原理 //先将353转成字符串再转成字节数组 printStream4.write(String.valueOf(353).getBytes()); printStream4.close(); } }
字符打印流支持的设备:
1.File类型的文件
2.字符串类型的文件
3.字节输出流
4.字符输出流
eg : public PrintWriter(Writer out, boolean autoFlush)
autoFlush - boolean 变量 :如果为true, 则println、printf、format方法将刷新输出缓冲区
- 代码
package com.qf.test; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class Demo11 { public static void main(String[] args) throws IOException { //3.支持字节输出流 PrintWriter printWriter = new PrintWriter(new FileOutputStream("test6.txt")); //4.支持字符写出流 PrintWriter printWriter2 = new PrintWriter(new FileWriter("test6.txt")); printWriter.close(); printWriter2.close(); } }
Properties
实际上就是Map集合,存储的是属性,属性以键值对的方式存储,这里的键和值都必须是字符串,所以不需要考虑泛型
Properties的使用和流紧密相关
优点:1.以键值对的形似存储数据
2.内部针对属性的存储封装了大量专有方法:load、store、list
1 . Properties的基础
//1.Properties的基础 public static void fun1() { //创建对象 Properties properties = new Properties(); properties.setProperty("a", "java"); properties.setProperty("b", "html"); System.out.println(properties); // {b=html, a=java} //根据key取值 System.out.println(properties.getProperty("a")); // java //key是唯一的 Object object = properties.setProperty("a", "BigData"); System.out.println(object); //java //当前的key在Properties中不存在时,会打印python System.out.println(properties.getProperty("c", "python")); }
2.利用Properties获取系统属性
public static void fun2() { Properties properties = System.getProperties(); //遍历 Set<String> set = properties.stringPropertyNames(); // Iterator<String> iterator = set.iterator(); // while (iterator.hasNext()) { // String string = (String) iterator.next(); // System.out.println("key:"+string+" value:"+properties.getProperty(string)); // } //增强for循环 for (String key : set) { System.out.println("key:"+key+" value:"+properties.getProperty(key)); } //对系统属性的某个属性进行更改 properties.setProperty("sun.jnu.encoding", "UTF8"); System.out.println(properties.getProperty("sun.jnu.encoding")); //重新获取一遍值 //原理:会先到内存中找属性集合的对象,如果有,直接使用.如果没有,会重新初始化一个新的对象,并获取属性集合. Properties properties1 = System.getProperties(); System.out.println(properties1.getProperty("sun.jnu.encoding")); properties1.list(System.out); }
3.Peoperties在实际中的应用
public static void fun3() throws FileNotFoundException, IOException { //创建Properties对象 Properties properties = new Properties(); //利用load方法将内容从磁盘读到内存 //注意:使用的文件内容的格式应用是key=value properties.load(new FileReader("text3.txt")); properties.list(System.out); //更改内容 properties.setProperty("a", "hehe"); //写会磁盘 第二个参数是提醒信息 properties.store(new FileWriter("text3.txt"), "hello world"); }
序列化
将短期存储的数据实现长期存储,这个过程对应的流就是序列化流
- 数据的存储分成两类:
1.短期存储:存放在内存中,随着程序的关闭而释放----对象,集合,变量,数组
2.长期存储:存储在磁盘中,即使程序关闭了,数据仍然存在---文件
- 序列化:将数据从内存放入磁盘,可以实现数据的长久保存---数据持久化的手段
- 反序列化:将数据从磁盘放回内存
- 进行序列化的步骤----通过对象的序列化讲解
1.创建一个类
2.使用对应的流将对象存入磁盘中----序列化-----ObjectOutputStream
3.使用对应的流将对象从磁盘中取出放回内存----反序列化----ObjectInputStream
4.关闭流
注意点:序列化流在工作时也要关联对应的基本输入输出流
解释:
一个类如果没有实现Serializable,进行序列化会报异常:NotSerializableException
- 实现了Serializable接口的类可以达到的目的:
1.可以进行序列化
2.进行序列化的类的元素都必须支持序列化
3.接口本身没有方法或字段,只是用来表示可序列化的语义
- 注意点
1. ClassNotFoundException:当前的类没有找到
分析:将Person对象进行序列化之后,将Person类删除,再进行反序列化的时候出现了异常
原因:反序列化在执行的时候依赖字节码文件,当类没有了,字节码文件无法创建,反序列化失败
2.java.io.InvalidClassException 无效的类
出现的原因:没有声明自己的serialVersionUID,而使用系统的.在进行反序列化的时候,类被改动了,系统认为现在的类
已经不是原来的类了(在使用系统的id进行识别的时候,重写给Person设置了id),认为此类无效
3.使用系统的serialVersionUID与自定义的ID的区别?
使用系统的,序列化和反序列化,id不能手动设置,使用的是编译器默认生成的,一旦类发生了改动,id会重新赋值
使用自定义的,序列化和反序列化,id不会发生改变,所以当反序列化的时候,即使对Person类进行了一些改动,也能继续反序列化
4.总结序列化,反序列化工程的注意点:
a.合理使用序列化流和反序列化流,要与输入流与输出流配合使用
b.进行序列化的类一定要实现Serializable接口,只要实现了接口就可以序列化.包括集合,包装类等
c.进行序列化的类要保证当前类与内部的类都要实现Serializable接口5.当一个属性被transient修饰后,这个对象在序列化时属性的值会被忽略,忽略不必要的属性可以达到对象序列化“瘦身”的功能
- 序列化与反序列化代码的演示
package com.qf.test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; //要想让Person类的对象可以实现序列化,必须让Person类实现Serializable接口 //类通过实现 java.io.Serializable 接口以启用其序列化功能。 //未实现此接口的类将无法使其任何状态序列化或反序列化。 //可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段, //仅用于标识可序列化的语义 //注意:不仅要求当前类可序列化,而且要求当前类的所有子类型本身都是可序列化的(比如:ArrayList) class Person implements Serializable{ // /** // * generated:由编译器自动生成的,后面加L表示long型 // */ private static final long serialVersionUID = -7224641225172644265L; // /** // * default:UID是由用户自己指定的,默认值是1L // */ // private static final long serialVersionUID = 1L; String name; //int age; public Person(String name) { this.name = name; } public String toString() { return "Person [name=" + name + "]"; } } public class Demo13 { public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { //对象的序列化 //objectWrite(); //对象的逆序列化流 objectRead(); } //对象的序列化 // public static void objectWrite() throws FileNotFoundException, IOException { // // ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test6.txt")); // //objectOutputStream.writeInt(100); // //对象序列化 // Person person = new Person("bingbing"); // objectOutputStream.writeObject(person); // objectOutputStream.close(); // } //对象的逆序列化流 //序列化的东西要通过逆序列化流读取 public static void objectRead() throws FileNotFoundException, IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test6.txt")); // int value = objectInputStream.readInt(); // System.out.println(value); Object object = objectInputStream.readObject(); System.out.println(object); objectInputStream.close(); } }
文件File类
File类,用来操作文件和路径(目录)
- api代码的演示
package com.qf.test; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo14 { public static void main(String[] args) throws IOException { /* * 文件:File类,用来操作文件和路径(目录) * * 创建文件 * 创建路径 * 创建多路径 * * 判断是否是文件 * 判断是否是路径 * 判断是否隐藏 * 获取最后修改文件的时间 * * 获取根目录(路径) * 获取指定目录下的文件或文件夹 */ //创建File类的对象 //第一种方式:直接指定文件的绝对路径 File file1 = new File("D:\\workspace\\BigData1923N21\\src\\com\\qf\\test\\Demo1.java"); //第二种:通过父路径和子路径的字符串形式 File file2 = new File("D:\\workspace\\BigData1923N21","src\\com\\qf\\test\\Demo1.java"); //第三种:先得到父路径的对象形式,再跟子路径拼接 File file3 = new File("D:\\workspace\\BigData1923N21"); File file4 = new File(file3,"src\\com\\qf\\test\\Demo1.java"); // * 创建文件,在创建时,如果当前的文件已经存在了,不会覆盖 File file5 = new File("D:\\workspace\\BigData1923N21\\test1.txt"); file5.createNewFile(); // * 创建单层路径,只能创建单层路径,只能创建目录 File file6 = new File("D:\\workspace\\BigData1924N11\\a\\test2.txt"); //file6.mkdir(); // * 创建多路径,也可创建单层目录,只能创建目录 file6.mkdirs(); // * 判断是否是文件 System.out.println(file6.isFile());//false // * 判断是否是路径 System.out.println(file6.isDirectory());//true // * 判断是否隐藏 System.out.println(file6.isHidden());//false // * 获取最后修改文件的时间 long lastTime = file5.lastModified(); System.out.println(lastTime); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String value = simpleDateFormat.format(new Date(lastTime)); System.out.println(value); * 获取根目录(路径)-当前主机的所有的根目录 File[] files = File.listRoots(); for (File file : files) { System.out.println(file); } System.out.println("***********************"); // * 获取指定目录下的文件或文件夹 特点:1.只获取的是当层的内容,不管子路径 2.只获取内容的名字,不是全路径 File file = new File("D:\\\\workspace\\\\BigData1924N11"); String[] strings = file.list(); for (String string : strings) { System.out.println(string); } System.out.println("***********************"); //获取指定目录下的文件或文件夹 的全路径 File[] file2 = file.listFiles(); for (File file3 : file2) { System.out.println(file3); } } }
递归
有条件的自己调用自己(方法)
- 条件:1.要给一个出口,负责结束当前的循环
2.循环的次数不能太多,否则占用内存量过大,死机
- 代码
package com.qf.test; public class Demo16 { public static void main(String[] args) { /* * 求:1-10之间的和 * / Person10.run(); } } class Person10{ static int sum = 0; static int i=1; public static void run() { sum+=i; System.out.println(sum); if ( i++ == 10) { return;//给了一个出口 } run(); } }
- 序列流(了解):把多个输入流的内容一次性的打印(操作)---字节流
代码演示:
package com.qf.test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; public class Demo18 { /* * 序列流:把多个输入流的内容一次性的打印(操作)---字节流 */ public static void main(String[] args) throws IOException { //创建三个输入流 FileInputStream fileInputStream1 = new FileInputStream("src\\com\\qf\\test\\Demo2.java"); FileInputStream fileInputStream2 = new FileInputStream("src\\com\\qf\\test\\Demo2.java"); FileInputStream fileInputStream3 = new FileInputStream("src\\com\\qf\\test\\Demo1.java"); //将三个输入流放入序列流 //方式一:先放入一个Vector // Vector<FileInputStream> vector = new Vector<>(); // vector.add(fileInputStream1); // vector.add(fileInputStream2); // vector.add(fileInputStream3); // // //得到枚举器 // Enumeration<FileInputStream> e1 = vector.elements(); //方式二:先放入一个list ArrayList<FileInputStream> list = new ArrayList<>(); list.add(fileInputStream1); list.add(fileInputStream2); list.add(fileInputStream3); //将集合转换成枚举 Enumeration<FileInputStream> e2 = Collections.enumeration(list); //创建序列流对象并关联相关的文件--参数是一个枚举器 //SequenceInputStream sequenceInputStream = new SequenceInputStream(e1); SequenceInputStream sequenceInputStream = new SequenceInputStream(e2); //创建输出流 FileOutputStream fileOutputStream = new FileOutputStream("temp2.txt"); //读写 byte[] arr = new byte[1024]; int num; while ((num = sequenceInputStream.read(arr)) != -1) { fileOutputStream.write(arr, 0, num); fileOutputStream.flush(); } sequenceInputStream.close(); fileOutputStream.close(); } }
- 数据流:字节流
DataInputStream : 数据输入流
DataOutputStream : 数据输出流
注意:数据流要与字节输入流,输出流配合使用
代码演示:
package com.qf.test; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Demo19 { public static void main(String[] args) throws IOException { //写 writeData(); //读 readData(); } public static void writeData() throws IOException { DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("temp3.txt")); //写 dataOutputStream.writeInt(97);//4个字节 00000000 00000000 000000000 011000001 00000001 dataOutputStream.writeBoolean(true);//1个 dataOutputStream.write(33);//1个 dataOutputStream.writeDouble(34.56);//8个 //关闭流 dataOutputStream.close(); } public static void readData() throws IOException { DataInputStream dataInputStream = new DataInputStream(new FileInputStream("temp3.txt")); //这里的boolean型和int型的数据,在读的时候由于与之前写的顺序相反了,所以读取的数据错误 /* * 注意点:1.读的顺序要与写的顺序一致 2.类型保持一致 */ System.out.println(dataInputStream.readBoolean());// 00000000 System.out.println(dataInputStream.readInt());//00000000 000000000 011000001 00000001 System.out.println(dataInputStream.readByte()); System.out.println(dataInputStream.readDouble()); dataInputStream.close(); } }
- 内存流(byte数组流)
ByteArrayInputStream : 写入内存,在内部有一个数组,数据被放在这里面
ByteArrayOutputStream : 将数据取出,放在字节数组里面
代码演示:
package com.qf.test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class Demo20 { public static void main(String[] args) throws IOException { //创建输入流,关联一个byte型的数组,作为缓冲区数据 ByteArrayInputStream bais = new ByteArrayInputStream("hello world".getBytes()); //创建输出流-不需要指定参数 ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] arr = new byte[1024]; int num; while ((num = bais.read(arr)) != -1) { baos.write(arr, 0, num); } System.out.println(new String(arr)); bais.close(); baos.close(); //注意:将流关闭了之后,还可以调用方法,不会报错. baos.write(45); } }
字符集
- 转换流的编码问题 :
中国的字符集:GBK/GB2312
欧洲的 : ISO8859-1
国际通用 : utf-8
美国的 : ASCII
- 编码:将字符串转换为byte序列的过程
- 解码:将byte序列转换成字符串的过程
编码错误:乱码:在执行读与写的时候,由于使用的字符集不同,造成了编码的错误
解决方案:可以先编码再解码
- 开发中的编码
常用字符集:一个汉字:GBK : 2字节 ISO8859-1 : 1个字节 utf-8 : 3个字节 unicode : 2个字节(内部编码)
utf-8,GBK是支持中文的,ISO8859-1不支持中文
- 编码
byte[ ] getBytes() //对于中文 默认的格式
使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
byte[] getBytes(Charset charset)
使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
- 解码
new String(byte[] bytes) //对于中文 默认是格式
通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。new String(byte[] bytes, Charset charset)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。代码演示:
public static void main(String[] args) throws UnsupportedEncodingException { //使用GBK编码,GBK解码 String str1 = "你好"; byte[] str1b = str1.getBytes("GBK"); System.out.println(new String(str1b,"GBK"));//你好 System.out.println(Arrays.toString(str1b));//[-60, -29, -70, -61] //使用utf-8编码,utf-8解码 String str2 = "你好"; byte[] str2b = str2.getBytes(); System.out.println(new String(str2b));//你好 System.out.println(Arrays.toString(str2b));//[-28, -67, -96, -27, -91, -67] //使用ISO8859-1编码,解码 String str3 = "你好"; byte[] str3b = str3.getBytes("ISO8859-1"); System.out.println(new String(str3b,"ISO8859-1"));//?? System.out.println(Arrays.toString(str3b));//[63, 63] }
- 中文乱码出现的情况研究
注意点:乱码解决的办法是再编码再解码
但是如果是编码出错了,无法解决.如果是解码出错了,可以利用再编码再解码
编码 解码 结果
GBK utf8 不可以(GBK2个字节,utf83个字节)
GBK ISO8859-1 可以
utf8 GBK 有时可以
utf8 ISO8859-1 可以
ISO8859-1 GBK 不可以(编码就出错了)
ISO8859-1 utf8 不可以(编码就出错了)注意 : 因为中文在utf8中是三个字节,在gbk中是两个字节,所以单数个中文用utf8编码后再用gbk解码这时由于编码出的字节数是单数的,gbk解码会自动补充一个字节,不仅造成解码出现乱码,也导致在编码在解码也无法解决
在编码在解码的演示
String s1 = "你好"; byte[] s1b = s1.getBytes("GBK"); String sr1b = new String(s1b,"utf-8"); System.out.println("utf8解码:"+sr1b);//??? //再编码 byte[] s1bb = sr1b.getBytes("utf-8"); //再解码 System.out.println("GBK再编码:"+new String(s1bb,"GBK"));//锟斤拷锟?
装饰设计模式
- 定义:基于已经实现的功能,提供增强的功能,装饰设计模式的由来就来自于对缓冲流的实现
- 特点:从缓冲流的角度讲解
1.使流原来的继承体更加的简单
2.提高了效率
3.由于是在原来的基础上提高增强的功能,所以他还要属于原来的体系
- 演示:如果自己设计装饰设计模式,怎么处理?
1.原来的类 Test---Reader
2.装饰类 BTest----MyBufferedReader
步骤:1.让BTest继承自Test
2.在BTest内有一个Test类型的成员变量。
3.通过BTest内一个带参数的构造方法接受外部传入的一个Test类型的对象,交给内部的Test的属性
4.在实现功能的时候,调用传入的Test类型的对象实现原有的功能,自己实现增强的功能
适配器设计模式:通常可以变相地理解为装饰设计模式
实例:要求在子类中只使用play方法
分析:Dog是继承了ZiMidel类,ZiMidel类实现了Inter接口
当Dog类想要实现Inter接口的一个方法的时候,如果直接实现Inter接口,就必须将所有的方法都实现
如果在Dog类与Inter接口之间插入一个类,让这个类去实现Inter接口的所有方法,作为这个类的子类只需要实现自己需要的方法
我们将中间的这个类就可以成为适配器类
- 代码
interface Inter{ public void play(); public void song(); public void run(); public void eat(); public void jump(); } //适配器类 class ZiMidel implements Inter{ @Override public void play() { // TODO Auto-generated method stub } @Override public void song() { // TODO Auto-generated method stub } @Override public void run() { // TODO Auto-generated method stub } @Override public void eat() { // TODO Auto-generated method stub } @Override public void jump() { // TODO Auto-generated method stub } } //创建狗类,我只想让她实现play方法? class Dog extends ZiMidel{ public void play() { // TODO Auto-generated method stub } } class Cat extends ZiMidel{ public void song() { } }