黑马视频(7/5)
Java SE
day19 异常
- 异常的概述:异常就是 Java 程序在运行过程中出现的错误。
异常的分类
- Throwable
Error
- 服务器宕机,数据库崩溃等
- Exception
异常的继承体系
Throwable
- Error
Exception (父类是 Throwable)
- RuntimeException
JVM默认是如何处理异常的
main 函数收到这个问题时,有两种处理方式:
- 自己将该问题处理,然后继续运行
- 自己没有针对的处理方式,只有交给调用 main 的 jvm 来处理,jvm 有一个默认的异常处理机制,就将该异常进行处理,并将该异常的名称,异常的信息,异常出现的位置打印在了控制台上,同时将程序停止运行
- try : 用来检测异常
- catch:用来捕获异常
- finally:释放资源,
异常处理的两种方式
try…catch…finally
- try catch
- try catch finally
- try finally
- throws
- try...catch 处理异常的基本格式:try…catch…finally
- 当通过 try ... catch 将问题处理了,程序会继续向下执行。如果后天跟多个 catch ,小的异常放前面,大的异常放后面,根据多态原理,如果大的放前面,就会接受所有的子类对象,后面的 catch 就没有意义了。
- 安卓,客户端开发,一般都是用 try ... catch ( Exception e ) ,来处理异常
- javaEE,服务端开发,一般都是底层开发,从底层向上抛出异常。查看日志,解决问题。
:编译期异常和运行期异常的区别
- Java 中的异常被分为两大类:编译时异常 和 运行时异常。
- 所有的 RuntimeException 类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
- 编译时异常:Java 程序必须显式处理,否则程序就会发生错误,无法通过编译,也叫做未雨绸缪异常,
- 运行时异常:程序员所犯的错误,需要回来修改代码,无需显示处理,也可以和编译时异常一样处理
Throwable 的几个常见方法
- getMessage() : 获取异常信息,返回字符串。
- toString() : 获取异常类名和异常信息,返回字符串,打印异常类名和异常信息
- printStackTrace( ) :获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。jvm 默认使用这种方式处理异常
- throws 的方式处理异常:定义功能方法时,需要把出现的问题暴露出来让调用者去处理,通过 throws 在方法上标识。
- throw 的概述:在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用 throw 把异常对象抛出。
throws 和 throw 的区别
throws
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
throw
- 用在方法体内,跟的是异常对象名
- 只能抛出一个异常对象名
- 表示抛出异常,由方法体内的语句处理
finally的特点
- 被 finally 控制的语句体一定会执行,在 return 语句之后也会执行
- 特殊情况:在执行到finally之前 jvm 退出了 ( 比如 System.exit(0))
- finally的作用: 用于释放资源,在 IO 流操作和数据库操作中会见到
final,finally 和 finalize 的区别
- final 可以修饰类,不能够被继承,修饰方法,不能被重写,修饰变量,只能赋值一次
- finally 是 try 语句中的一个语句体, 不能单独使用,用来被释放资源,
- finalize 是一个方法,当垃圾回收期确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后。
- 先执行一半 return ,将返回路径 装箱,但是没有返回,之后查看是否有 finally ,有的话就执行,之后执行未完成的 return
自定义异常概述,只是为了区分名字,让我们可以更快的检索到哪里出了错,不然全部抛出 Exception , 不知道哪里出了错,但是最后处理的还是依靠父类去处理。比如继承 Exception ,是依靠 Exception 的父类 Throwable 来处理
- 继承自Exception
- 继承自RuntimeException
异常注意事项
- 子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。( 父亲坏了,儿子不能比父亲更坏 )
- 如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
- 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能 try ,不能 throws
如何使用异常处理
- 原则:如果该功能内部可以将问题处理,用 try ,如果处理不了,交由调用者处理,这是用 throws
区别:
- 后续程序需要继续运行就 try
- 后续程序不需要继续运行就 throws
- 如果JDK没有提供对应的异常,需要自定义异常。
File 类的概述
File 更应该叫做一个路径
- 文件路径或者文件夹路径
- 路径分为 绝对路径 和 相对路径
- 绝对路径 是一个固定的路径,从盘符开始
- 相对路径相对于某个位置,在 eclipse 下是指当前项目下,在 dos 下 ,查看API指的是当前路径
- 文件和目录路径名的抽象表示形式
- file.exists() ,判断文件是否存在,
构造方法
- File ( String pathname ) :根据一个路径得到 File 对象
- File ( String parent,,String child ) : 根据一个目录和一个子文件/目录得到 File 对象
- File ( File parent, String child ) :根据一个父 File 对象和一个子文件/目录得到 File 对象
File 类 的创建功能
- public boolean createNewFile( ) : 创建文件 如果存在这样的文件,就不创建了,返回false,如果不存在就创建,返回 true
- public boolean mkdir ( ) :创建文件夹 如果存在这样的文件夹,就不创建了,返回false,如果不存在就创建,返回 true
- public boolean mkdirs ( ) :创建文件夹,如果父文件夹不存在,会帮你创建出来,返回true,存在返回 false
重命名和删除功能
- public boolean renameTo ( File dest ) :把文件重命名为指定的文件路径
- public boolean delete() :删除文件或者文件夹
重命名注意事项
- 如果路径名相同,就是改名。
- 如果路径名不同,就是改名并剪切。
删除注意事项:
- Java 中的删除不走回收站。
- 要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹
判断功能
- public boolean isDirectory() :判断是否是目录
- public boolean isFile(): 判断是否是文件
- public boolean exists() :判断是否存在
- public boolean canRead() :判断是否可读,windows 系统下认为所有的文件都是可读的,
- public boolean canWrite() :判断是否可写,windows 系统可以设置可写,不可写
- public boolean isHidden() :判断是否隐藏..
获取功能
- public String getAbsolutePath() :获取绝对路径
- public String getPath() :获取路径,获取构造方法中传入的路径,
- public String getName() :获取名称,单纯的获取名称
- public long length():获取长度。字节数
- public long lastModified() :获取最后一次的修改时间,毫秒值,可以通过 Date 转换成当前时间
- public String[] list() : 获取指定目录下的所有文件或者文件夹的名称数组,返回的是一个数组
- public File[] listFiles() : 获取指定目录下的所有文件或者文件夹,最后放在 File 数组 中
文件名称过滤器:
- public String[] list ( FilenameFilter filter )
- public File[] listFiles ( FileFilter filter)
day20 字节流
IO 流用来处理设备之间的数据传输
- Java 对数据的操作是通过 流 的方式
- Java 用于操作流的类都在 IO 包中
- 流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
- 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流 : 字符流只能操作纯字符数据,比较方便。
字节流 的抽象父类 :
* InputStream * OutputStream
字符流 的抽象父类 :
* Reader * Writer
IO程序书写
- 使用前,导入IO包中的类
- 使用时,进行IO异常处理
- 使用后,释放资源
read( ) 一次读取一个字节 ,每次读取一次指针向后移动一个,最后一个返回 -1 ,可以当做文件结束的标记
FileInputStream fis = new FileInputStream("aaa.txt"); //创建一个文件输入流对象,并关联aaa.txt int b; //定义变量,记录每次读到的字节 while((b = fis.read()) != -1) { //将每次读到的字节赋值给b并判断是否是-1 System.out.println(b); //打印每一个字节 } fis.close();
read() 方法读取的是一个字节,为什么返回是 int ,而不是 byte:
- 因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回 byte , 有可能在读到中间的时候遇到 111111111 ,那么这11111111 是 byte 类型的 -1 , 我们的程序是遇到 -1 就会停止不读了,后面的数据就读不到了,所以在读取的时候用 int 类型接收, 如果 11111111 会在其前面补上 ,24 个 0 凑足 4 个字节, 那么 byte 类型的 - 1 就变成 int 类型的 255 了这样可以保证整个数据读完,而结束标记的 -1 就是 int 类型。
write() 一次写出一个字节
FileOutputStream fos = new FileOutputStream("bbb.txt"); //如果没有bbb.txt,会创建出一个,如果有,就清空数据 //fos.write(97); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的是一个byte fos.write(98); fos.write(99); fos.close();
追加
// FileOutputStream 的构造方法写出数据如何实现数据的追加写入 FileOutputStream fos = new FileOutputStream("bbb.txt",true); //如果没有bbb.txt,会创建出一个 //fos.write(97); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的一个byte fos.write(98); fos.write(99); fos.close();
- 字节流一次读写一个字节复制音频,弊端:效率太低
字节数组拷贝: available() 方法,可以一次获取到读取的直接数组大小
- int read ( byte[] b ) :一次读取一个字节数组
- write ( byte[] b ) :一次写出一个字节数组
- available( ) : 获取读的文件所有的字节个数
- 弊端:有可能会内存溢出
FileInputStream fis = new FileInputStream("C:\\Users\\renchao\\Desktop\\file\\表情包\\mmp1.png"); // 创建输入流对象 FileOutputStream fos = new FileOutputStream("C:\\Users\\renchao\\Desktop\\copy.png") ; //创建输出流对象 byte[] arr = new byte[fis.available()] ; //创建与文件大小一样大小的字节数组 fis.read(arr) ; //文件中的字节读取到内存中 fos.write(arr); // 字节数组中的字节数据写到文件上 fis.close(); fos.close();
定义小数组 :
- write ( byte[] b )
- write ( byte[] b , int off , int len ) :写出有效的字节个数
字节流一次读写一个字节数组复制图片和视频
FileInputStream fis = new FileInputStream("致青春.mp3"); FileOutputStream fos = new FileOutputStream("copy.mp3"); int len; byte[] arr = new byte[1024 * 8]; //自定义字节数组 while((len = fis.read(arr)) != -1) { //如果忘记加 arr ,没有写到字节数组里,读取的就不是有效的字节个数,而是码表值。 //fos.write(arr); fos.write(arr, 0, len); //写出字节数组写出有效个字节个数 } fis.close(); fos.close();
- BufferedInputStream 和 BufferOutputStream 拷贝:
缓冲思想
- 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
- 这是加入了数组这样的缓冲区效果,java 本身在设计的时候, 也考虑到了这样的设计思想 ( 装饰设计模式 ),所以提供了字节缓冲区流
- 在内存中加入创建的字节数组,效率高很多
BufferedInputStream
- BufferedInputStream 内置了一个缓冲区 ( 数组 )
- 从BufferedInputStream 中读取一个字节时,BufferedInputStream 会一次性从文件中读取 8192 个, 存在缓冲区中, 返回给程序一个, 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取, 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
BufferedOutputStream
- BufferedOutputStream 也内置了一个缓冲区(数组)
- 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中,直到缓冲区写满,,BufferedOutputStream 才会把缓冲区中的数据一次性写到文件里 。
FileInputStream fis = new FileInputStream("xxx.mp3"); //创建文件输入流对象 BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对fis装饰 FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象 BufferedOutputStream bos = new BufferedOutputStream(fos); //创建缓冲区对fos装饰 int b; while((b = bis.read()) != -1) { bos.write(b); } bis.close(); //只关装饰后的对象即可 bos.close();
小数组的读写和带Buffered的读取哪个更快?
- 定义小数组如果是 8192 个字节大小和 Buffered 比较的话,定义小数组会略胜一筹,因为读和写操作的是同一个数组,而Buffered 操作的是两个数组
- flush() 方法: 用来刷新缓冲区的,刷新后可以再次写出入
- close() 方法:用来关闭流释放资源的的,如果是带缓冲区的流对象的close() 方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出 ,关闭之前先刷新到文件上,在关闭
字节流读写中文
字节流读取中文的问题
- 字节流在读中文的时候有可能会读到半个中文,造成乱码
字节流写出中文的问题
- 字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组
- 写出回车换行 write(" \r \n " . getBytes() );
- bos.write(b ^ 123); ,异或可以用来加密
day21 字符流
字符流是什么
- 字符流是可以直接读写字符的 IO 流
- 字符流读取字符,,就要先读取到 字节 数据,,然后转为 字符,如果要写出字符,需要把字符转为 字节 再写出.
FileReader 类的 read() 方法可以按照字符大小读取
FileReader fr = new FileReader("aaa.txt"); //创建输入流对象,关联aaa.txt int ch; while((ch = fr.read()) != -1) { //将读到的字符赋值给ch System.out.println((char)ch); //将读到的字符强转后打印 } fr.close(); //关流
FileWriter 类的 write() 方法可以自动把 字符 转为 字节 写出,可以直接写入汉字在写出来,后台转换成字节不需要手动转换
FileWriter fw = new FileWriter("aaa.txt"); fw.write("aaa"); fw.close();
字符流的拷贝:
FileReader fr = new FileReader("a.txt"); FileWriter fw = new FileWriter("b.txt"); int ch; while((ch = fr.read()) != -1) { fw.write(ch); } fr.close(); fw.close(); // 不关流,内容在2k的缓冲区中,不会写到文件中,底层定义的缓冲区 1024 1k ,代表一个字符,char 数组,代表两个字节,也就是2048 字节 2k,
- 字符流也可以拷贝文本文件,但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
- 无论是纯文本还是非纯文本,建议都用字节流。
- 程序需要读取一段文本,,或者需要写出一段文本的时候可以使用字符流,只读或者只写的时候建议使用。
- 读取的时候是按照字符的大小读取的,所以不会出现半个中文
- 写出的时候可以直接将字符串写出,不用转换为字节数组
字符流是否可以拷贝非纯文本的文件:
- 不可以拷贝非纯文本的文件
- 因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用 ? 代替,写出的时候会将字符转换成字节写出去,如果是 ? ,直接写出,这样写出之后的文件就乱了,无法读取
自定义小数组的拷贝方法:
FileReader fr = new FileReader("aaa.txt"); //创建字符输入流,关联aaa.txt FileWriter fw = new FileWriter("bbb.txt"); //创建字符输出流,关联bbb.txt int len; char[] arr = new char[1024*8]; //创建字符数组 while((len = fr.read(arr)) != -1) { //将数据读到字符数组中 fw.write(arr, 0, len); //从字符数组将数据写到文件上 } fr.close(); //关流释放资源 fw.close();
- 缓冲区大小 8192 = 8*1024 = 16 k
- BufferedReader 的 read() 方法读取字符时会一次读取若干字符到缓冲区,,然后逐个返回给程序,降低读取文件的次数, 提高效率
BufferedWriter 的 write() 方法写出字符时会先写到缓冲区,缓冲区写满时才会写到文件, 降低写文件的次数,,提高效率
BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); //创建字符输入流对象,关联aaa.txt BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt")); //创建字符输出流对象,关联bbb.txt int ch; while((ch = br.read()) != -1) { //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch bw.write(ch); //write一次,是将数据装到字符数组,装满后再一起写出去 } br.close(); //关流 bw.close();
- BufferedReader 的 readLine() 方法可以读取一行字符 ( 不包含换行符号 )
BufferedWriter 的 newLine() 可以输出一个跨平台的换行符号 "\r\n"
BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt")); String line; while((line = br.readLine()) != null) { bw.write(line); //bw.write("\r\n"); //只支持windows系统 bw.newLine(); //跨平台的,写出回车换行符 } br.close(); bw.close();
文本内容反转 ,流 最好什么时候用什么时候开 ,晚开早关,
BufferedReader br = new BufferedReader(new FileReader("xxx.txt")) ; BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt")); //创建集合对象 //流尽量晚开,早关 ArrayList<String> list = new ArrayList<>() ; String line ; while((line = br.readLine())!=null) { list.add(line) ; } br.close(); for (int i = list.size() - 1 ; i >= 0; i--) { bw.write(list.get(i)); bw.newLine(); } bw.close();
LineNumberReader 是 BufferedReader 的子类, 具有相同的功能, 并且可以统计行号
调用 getLineNumber() 方法可以获取当前行号
- 调用 setLineNumber( ) 方法可以设置当前行号
LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt")); String line; lnr.setLineNumber(100); //设置行号 while((line = lnr.readLine()) != null) { System.out.println(lnr.getLineNumber() + ":" + line);//获取行号 } lnr.close();
装饰着设计模式 优点是 耦合性不太强
interface Coder { public void code(); } class Student implements Coder { @Override public void code() { System.out.println("javase"); System.out.println("javaweb"); } } class HeiMaStudent implements Coder { private Student s; //获取到被包装的类的引用 public HeiMaStudent(Student s) { //通过构造函数创建对象的时候,传入被包装的对象 this.s = s; } @Override public void code() { //对其原有功能进行升级 s.code(); System.out.println("数据库"); System.out.println("ssh"); System.out.println("安卓"); System.out.println("....."); } }
- FileReader 是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用 InputStreamReader (字节流,编码表)
FileWriter 是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用 OutputStreamWriter (字节流,编码表)
BufferedReader br = //高效的用指定的编码表读 new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8")); BufferedWriter bw = //高效的用指定的编码表写 new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK")); int ch; while((ch = br.read()) != -1) { bw.write(ch); } br.close(); bw.close();
- 递归的弊端:不能调用次数过多,容易导致栈内存溢出
- 递归的好处:不用知道循环的次数
- 构造方法不能使用递归调用
- 递归调用可以有返回追,也可以没有返回值
day22 IO 序列流
- 序列流可以把多个字节输入流整合成一个,从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.
整合两个: SequenceInputStream ( InputStream , InputStream )
FileInputStream fis1 = new FileInputStream("a.txt"); //创建输入流对象,关联a.txt FileInputStream fis2 = new FileInputStream("b.txt"); //创建输入流对象,关联b.txt SequenceInputStream sis = new SequenceInputStream(fis1, fis2); //将两个流整合成一个流 FileOutputStream fos = new FileOutputStream("c.txt"); //创建输出流对象,关联c.txt int b; while((b = sis.read()) != -1) { //用整合后的读 fos.write(b); //写到指定文件上 } sis.close(); fos.close();
整合多个: SequenceInputStream(Enumeration)
FileInputStream fis1 = new FileInputStream("a.txt"); //创建输入流对象,关联a.txt FileInputStream fis2 = new FileInputStream("b.txt"); //创建输入流对象,关联b.txt FileInputStream fis3 = new FileInputStream("c.txt"); //创建输入流对象,关联c.txt Vector<InputStream> v = new Vector<>(); //创建vector集合对象 v.add(fis1); //将流对象添加 v.add(fis2); v.add(fis3); Enumeration<InputStream> en = v.elements(); //获取枚举引用 SequenceInputStream sis = new SequenceInputStream(en); //传递给SequenceInputStream构造 FileOutputStream fos = new FileOutputStream("d.txt"); int b; while((b = sis.read()) != -1) { fos.write(b); } sis.close(); fos.close();
- 内存输出流:该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据
使用方式
- 创建对象: new ByteArrayOutputStream()
- 写出数据: write( int ), write ( byte [ ] )
- 获取数据: toByteArray()
FileInputStream fis = new FileInputStream("xxx.txt"); ByteArrayOutputStream bais = new ByteArrayOutputStream(); //在内存中创建了一个可以自动增长的数组 int b ; while((b = fis.read())!=-1) { bais.write(b); //读取到的数据写入内存中 } /*byte[] arr = bais.toByteArray() ; // 将缓冲区中的数据全部拿出来,赋值给 arr 数组 System.out.println(new String(arr));*/ System.out.println(bais.toString()); //将缓冲区内容转换为字符串, fis.close();
定义一个文件输入流,调用 read ( byte [ ] b ) 方法,将 xxx.txt 文件中的内容打印出来 ( byte 数组大小限制为 5 )
FileInputStream fis = new FileInputStream("xxx.txt"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); //内存输出流 byte[] arr = new byte[5] ; int len ; while((len = fis.read(arr))!=-1) { baos.write(arr,0,len); } System.out.println(baos); // 不用调用toString方法,会自动调用 fis.close();
随机访问流 RandomAccessFile :
- RandomAccessFile 类不属于流,是 Object 类的子类。但它融合了InputStream 和 OutputStream 的 功能。
- 支持对随机访问文件的读取和写入。
- read(),,读
- write() ,写
- seek() ,在指定位置设置指针,从什么地方读和写
- 对象操作流: 该流可以将一个对象写出, 或者读取一个对象到程序中. 也就是执行了 序列化(存放) 和 反序列化(读取) 的操作.
写出: new ObjectOutputStream(OutputStream ) , writeObject() ,对象输出流,序列化,乱码
public class Demo3_ObjectOutputStream { public static void main(String[] args) throws IOException { Person p1 = new Person("张三", 23); Person p2 = new Person("李四", 24); // FileOutputStream fos = new FileOutputStream("e.txt"); // fos.write(p1); // FileWriter fw = new FileWriter("e.txt"); // fw.write(p1); //无论是字节输出流,还是字符输出流都不能直接写出对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));//创建对象输出流 oos.writeObject(p1); oos.writeObject(p2); oos.close(); } }
读取: new ObjectInputStream(InputStream), readObject() ,对象输入流,反序列化
public class Demo3_ObjectInputStream { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt")); Person p1 = (Person) ois.readObject(); Person p2 = (Person) ois.readObject(); System.out.println(p1); System.out.println(p2); ois.close(); } }
对象操作流优化:
// 将对象存储在集合中写出 Person p1 = new Person("张三", 23); Person p2 = new Person("李四", 24); Person p3 = new Person("马哥", 18); Person p4 = new Person("辉哥", 20); ArrayList<Person> list = new ArrayList<>(); list.add(p1); list.add(p2); list.add(p3); list.add(p4); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f.txt")); oos.writeObject(list); //写出集合对象 oos.close(); // 读取到的是一个集合对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f.txt")); ArrayList<Person> list = (ArrayList<Person>)ois.readObject(); //泛型在运行期会被擦除,索引运行期相当于没有泛型 //想去掉黄色可以加注解 @SuppressWarnings("unchecked") for (Person person : list) { System.out.println(person); } ois.close();
- 要写出的对象必须实现Serializable接口才能被序列化 , 不用必须加 id 号
- 数据输入输出流:DataInputStream , DataOutputStream 可以按照基本数据类型大小读写数据
- 例如按 Long 大小写出一个数字, 写出时该数据占 8 字节. 读取的时候也可以按照 Long 类型读取, 一次读取 8 个字节.
读出方法: DataOutputStream(OutputStream), writeInt(), writeLong()
DataOutputStream dos = new DataOutputStream(new FileOutputStream("b.txt")); dos.writeInt(997); dos.writeInt(998); dos.writeInt(999); dos.close();
*写入方法: DataInputStream ( InputStream ) , readInt( ) , readLong()
DataInputStream dis = new DataInputStream(new FileInputStream("b.txt")); int x = dis.readInt(); int y = dis.readInt(); int z = dis.readInt(); System.out.println(x); System.out.println(y); System.out.println(z); dis.close();
- 打印流:该流可以很方便的将对象的 toString( ) 结果输出,,并且自动加上换行, 而且可以使用自动刷出的模式
System.out就是一个 PrintStream , 其默认向控制台输出信息
PrintStream ps = System.out; ps.println(97); //其实底层用的是Integer.toString(x),将x转换为 数字 字符串 打印 ps.write(97) ; // 查找码表,找到对应的 a 然后打印 ps.println(new Person("张三", 23)); // 默认调用对象的 toString 方法 Person p = null; ps.println(p); //如果是null,就返回null,如果不是null,就调用对象的toString()
使用方式:
- 打印: print(), println()
- 自动刷出: PrintWriter ( OutputStream out, boolean autoFlush, String encoding )
- PrintWriter 和 PrintStream 分别打印 字符流 和 字节流
- 打印流只操作数据目的
PrintWriter pw = new PrintWriter(new FileOutputStream("g.txt"), true); pw.write(97); pw.print("大家好"); pw.println("你好"); //自动刷出,只针对的是println方法 pw.close();
标准输入输出流:
- System.in 是 InputStream, 标准输入流, 默认可以从键盘输入读取字节数据,不论创建多少,都只有一个对象。
- System.out 是 PrintStream , 标准输出流, 默认可以向 Console 中输出字符和字节数据
2.修改标准输入输出流
- 修改输入流: System.setIn(InputStream)
- 修改输出流: System.setOut(PrintStream)
System.setIn(new FileInputStream("a.txt")); //修改标准输入流,改变后指向文件 System.setOut(new PrintStream("b.txt")); //修改标准输出流 InputStream in = System.in; //获取标准输入流 PrintStream ps = System.out; //获取标准输出流 int b; while((b = in.read()) != -1) { //从a.txt上读取数据 ps.write(b); //将数据写到b.txt上 } in.close(); ps.close();
修改标准输入输出流拷贝图片,不推荐使用
System.setIn(new FileInputStream("IO图片.png")); //改变标准输入流 System.setOut(new PrintStream("copy.png")); //改变标准输出流 InputStream is = System.in; //获取标准输入流 PrintStream ps = System.out; //获取标准输出流 int len; byte[] arr = new byte[1024 * 8]; while((len = is.read(arr)) != -1) { ps.write(arr, 0, len); } is.close(); ps.close();
从键盘读入数据的方式:
- BufferedReader 的 readLine 方法。
- Scanner
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //InputStreamReader 转换流 String line = br.readLine() ; System.out.println(line) ; br.close ;
- Properties 类表示了一个持久的属性集。
- Properties 可保存在流中或从流中加载。
- 属性列表中每个键及其对应值都是一个字符串。
Properties的特殊功能
- public Object setProperty(String key,String value) ,设置键和值
- public String getProperty(String key),获取键和值
- public Enumeration
string PropertyNames() , 枚举
Properties 的 load() 和 store() 功能
Properties prop = new Properties() ; System.out.println("读取前:"+prop); prop.load(new FileInputStream("config.properties")); System.out.println("读取后:"+prop); prop.setProperty("username", "sss") ; System.out.println("读取后:"+prop); prop.store(new FileOutputStream("config.properties"),null) ; //第二个参数用来描述文件列表,如果不描述可以传入 null