第四周笔记
一、 异常
1. 什么是异常
程序运行是,发生的不被期望的事件,它组织了程序按照程序员的预期正常执行,这就是异常
常见的异常
- 算术异常:ArithmeticException
- 空指针异常:NullPointerException
- 数组下标越界异常:ArrayIndexOutOfBoundsException
- 类型转换异常:ClassCastException
- 数组负下标异常:NegativeArrayException
- 字符串转换为数字异常:NumberFormatException
- 输入类型不匹配:inputMisMatchException
2. 异常处理
2.1 什么是异常处理
异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。
异常处理机制:捕获异常、抛出异常
捕获异常: try、catch、finally
抛出异常: throw、throws
2.2 异常处理
Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。Throwable又派生出Error类和Exception类。
错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。
异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
Exception异常分为两类:运行时异常、编译异常
- 运行时异常(不受检异常):RuntimeException类极其子类表示JVM在运行期间可能出现的错误。比如说试图使用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。
- 编译时异常(受检异常):Exception中除RuntimeException极其子类之外的异常。如果程序中出现此类异常,必须对该异常进行处理,否则编译不通过。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。
3. 异常关键字
Java的异常处理是通过5个关键字来实现的
try、catch、finally 捕获异常
throw、throws 抛出异常
Final和Finally区别
- final 可以用来修饰变量——常量,这个值是一个最终的了,不能被修改了
- 修饰方法 —— 这个方法是最终的方法了,不能被重写了
- 修饰类 —— 这个类是一个最终的类,不能被继承
- finally是异常组合的一个代码块,表示的是一段一定会被执行的代码
- final和finally没有直接关系
Finally和return的执行顺序
- 不管有没有出现异常,finally块中的代码都会执行
- 当try-catch中有return时,finally仍然会执行
- finally是在return语句执行之后,返回之前执行的(此时并没有返回运算的值,而是先把要返回的值保存起来,不管finally中的代码增么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行之前就已经确定了的
- finally中如果包含return,那么程序将在这里返回,而不是try或catch中的return返回,返回值就不是try或catch中保存的返回值了
注意
Finally唯一不被执行的情况:当发生异常事件,在程序中手动的退出系统:System.exit(1);
4. 捕获异常
4.1 try-catch-finally语句
try {
//监控区,有可能出现异常的代码
//如果程序没有异常,则忽略catch
//如果出现了异常,异常后面的代码都不会执行
//立即到catch块中来进行匹配(catch解决异常的,一个catch解决一个异常,可以多个)
//如果没有匹配的catch块,则程序报错
//如果有匹配的异常类型,则会进行catch中,只要进去了,则认为异常被解决了,后面的代码还会执行
//注意:多个catch,异常类型是从小到大完成的。
}catch(异常类型 e) {
}finally{
//最终的:不管程序是不是发送了异常,这里的代码一定会执行
//一般会做资源的释放
}
捕获总结
- try 代码块:用于捕获异常。其后可以接零个或者多个catch块。如果没有catch块,后必须跟finally块,来完成资源释放等操作
- catch 代码块:用于捕获异常,并在处理异常
- finally 代码块:无论是否捕获异常,finally代码总会被执行。如果try代码块或者catch代码块中有return语句时,finally代码块将在方法返回前被执行。唯一一种finally不会被执行的情况就是发送异常时,手动的退出系统。
5. 抛出异常
如果一个方法可能会出现异常,但没有能力处理这种异常,或者自己不想解决的话,可以将异常抛出。
5.1 throws抛出异常
public void methodName() throws Exception1,Exception2….{
}
如果一个方法向外抛出了异常,那异常由谁来解决?
- 调用者,谁调用这个方法就由谁来解决
- 如果调用者也解决不了呢?调用者可以继续向外抛出这个异常
5.2 throw 抛出异常
throw new ExceptionType;
try{
//可能会发生异常的代码
}catch(Exception e){
throw new Exception(e);
}
5.3 throw与throws
throw | throws |
---|---|
生成并抛出异常 | 声明方法内抛出了异常 |
位于方法体内部,可作为单独语句使用 | 必须跟在方法参数列表后面,不能单独使用 |
抛出一个异常对象,且只能是一个 | 声明抛出异常类型,可以跟多个异常 |
6. 自定义异常
class <自定义异常名> extends <Exception>
自定义异常的具体步骤
- 定义一个类继承Exception或者其子类
- 编写构造方法(无参和有参)
- 在需要的地方调用异常类(使用throw抛出实例化后的异常)—— 在方法的声明中需要声明
7. 异常使用原则
从性能角度看
- 不要将所有的代码都放在try中,try只放有可能发送异常的代码
- 在catch中指定具体的异常类型
能解决的使用捕获,不能解决的使用抛出
二、 集合
1. 集合框架介绍
2. Collection介绍
Collection位于java.util包下。
Collection接口是集合体系的顶层接口,它是用来定义集合的最基本的操作行为的。
Collection集合中可以存放多个对象,其中存放的对象称为元素。
Collection直接的子接口List和Set
迭代器
要遍历集合,首先要做事情先判断集合中有没有元素,有元素就把当前这个元素取出来,然后再判断有没有,如果还有继续取出。直到把所有元素全部取出。Iterator接口定义了三个方法:迭代器
- hasNext() 问集合中有没有下一个元素; 调用这个方法会返回一个boolean类型的值,如果有则返回true, 没有返回false
- next() 取出当前这个元素;, 取值
- remove() 删除当前的元素
3. List集合
List接口继承了Collectoin接口,List接口就拿到Collection接口中的所有方法,同时List接口根据自己的特点,还定义了额外,主要是围绕List的下标而设计的特有功能。
List它描述的所有有序集合的公共行为。并且List接口描述的集合它拥有下标(索引index),我们就可以根据这个下标来操作集合中的元素。List接口描述的集合中允许存放重复元素。
List集合存储元素的特点:有序、可重复
List接口的实现类:ArrayList、LinkedList
ArrayList实现了长度可变的数组,在内存中分配连续的空间,遍历元素和随机访问元素的效率比较高 - 查询比较快
LinkedList采用链表存储方式,插入、删除元素时效率比较高
3.1 ArrayList
ArrayList集合的特点:底层使用可变数组 储存的元素:有序、可重复
不足:查询快,删除慢
常用方法
-
增加:
- boolean add( Object element ) 把当前的element元素添加集合的尾部,成功返回true, 失败返回false;
- void add(int index, E element) 在集合中指定位置上添加元素
- **注意:**使用add方法添加元素的时候,一定要保证指定的位置前面有元素。否则会发生下标越界异常。
-
删除:
- remove(Object obj) 删除集合中指定的元素,移除第一次出现的元素,没有不会报错
- Object remove(int index) 删除集合中指定位置上的元素,并返回被删除的元素 。删除时指定的下标的位置一定要保证有元素。
- clear() 清空集合中所有元素,会让集合中的size变成0。
- 注意:使用List调用remove如果指定的是值,这时这个值一定是下标(默认),因此如果要删除集合中存放的包装类型的数据,这时需要把这个int值包装成对应的Integer类型,Integer.valueOf(int)。
-
修改:
- Object set(int index, E element) 修改集合中指定位置上的数据。原来的元素就被删除掉了。返回被替换的那个元素。
-
查询:
- get(int index) 返回此列表中指定位置上的元素
-
常用:
- size() 求一个集合的长度
- isEmpty(), 判断这个集合是不是空的,如果为空集合返回true,否则为false
- boolean contains(Object o) ,判断列表中是否存在指定元素
-
遍历:
-
For
-
Foreach
-
Iterator
-
获取迭代器对象
-
用while循环遍历
-
Iterator iterator = list.iterator(); while(iterator.hasNext()){ Object o = iterator.next(); System.out.println (o); }
-
-
3.2 LinkedList
底层采用的链接列表(链表),由于链表有头和有尾的数据结构,因此LinkedList集合中定义了自己的特有方法,这些方法都围绕链表的头和尾设计的。新增、删除元素时,效率比较高。
方法名 | 说明 |
---|---|
void addFirst(Object o) | 在列表的首部添加元素 |
void addLast(Object o) | 在列表的末尾添加元素 |
Object getFirst() | 返回列表中的第一个元素 |
Object getLast() | 返回列表中的最后一个元素 |
Object removeFirst() | 删除并返回列表中的第一个元素 |
Object removeLast() | 删除并返回列表中的最后一个元素 |
3.3 ArrayList和LinkedList比较
- 二者都是List接口的实现类,存储的元素都是有序可重复的
- ArrayList底层维护的是可变数组来存放元素,查询快,增删慢
- LinkedList底层采用的是链表结构,增删快,查询慢
- LinkedList比ArrayList多了头尾元素的操作方法
4. Set集合
Set接口描述的是一种比较简单的一种集合,集合中的对象并不按特定的方式排序,并且不能保存重复的对象。
HashSet是Set接口的常用实现类。而Set接口继承了Collection接口,同时没有添加新的方法,所以在使用上与List接口的实现类使用方式一致。都是有增加元素的add方法和获取元素个数的size方法。
Set集合存储数据的特点:无序,不可重复
调用set集合添加方法的时候,最终底层在调用object类中的hashCode() - 计算一个对象的hash值的,最终就是一串数字
在Set集合中是不存在下标的,也没有get方法,无法一个一个取值,无法用for循环遍历,只能使用foreach或者迭代器。
HashSet集合如何保证元素不重复?
- 当给Hashset中存放元素的时候会先调用对象的hashCode方法,计算哈希值,根据哈希值来决定当前对象在集合中的存储位置。
- 在存储的时候,如果遇到了哈希值相同的元素,这时集合的底层还会调用当前对象的equals方法,判断当前正要存放的对象和位置上已经存在的对象是否是同一对象,equals方法返回的true,就认为相同对象,不保存,如果equals方法返回的false,当前对象也会被保存。
Set集合可以使用传统for循环来执行遍历吗?
- 不可以,
- Set接口不存在get方法,也就是没有List接口通过索引取值的方法。
5. Map集合
Java中提供Map集合主要用来保存具有一定对应关系的数据。在给Map集合中存放对象的时候,一次要求存放一组(一对)对象。
Collection接口的所有集合容器统称为**单列集合,Map集合称为双列集合**
Map集合的特点
- 存储的时候,每次都要一组一组的进行存储
- 无序的集合
- key,唯一性;value是可以重复的
- 没有提供修改的功能,可以利用key唯一这样的特点,来实现数据的修改
常用方法
- 增加
- put(K key, V value) :把当前的key和value存放到集合中。
- 注意:如果当前的key在集合中已经存在,那么就会用当前的value覆盖key对应的以前的value的值,并且返回被覆盖的那个value值
- 删除
- clear();清空
- remove(Object key) 根据指定的key删除key和value
- 修改
- 可以利用新增方法中key唯一实现对元素的修改
- 获取
- get(Object key) 根据指定key获取Map集合中key对应的value值,如果key不存在,返回null
- size() 获取key-value对应关系的个数
- 判断
- containsKey(Object key) 判断Map是否包含指定的key,有就返回true
- containsValue(Object value) 判断Map是否包含指定的value,有就返回true
- isEmpty() 判断Map集合是否为null,它的size是0
- keySet(),获取map中所有的key组成一个set集合。
- entrySet() ,获取map中所有的键值对组成一个set集合。
Map的遍历
- keySet获取Map集合的key的集合,然后遍历key
for(String key:map.keySet()) {
System.out.println("key=" + key + " value=" + map.get(key).toString());
}
- 通过Map.entrySet遍历key和value
for(Map.Entry<String, Object> entry : map.entrySet()){
System.out.println("key=" + entry.getKey() + " value=" + entry.getValue());
}
6. 泛型
限定容器能够存储的数据类型
泛型的应用:<具体的数据类型>
<>尖括号中书写的类型是引用数据类型。不能是基本数据类型
泛型技术,是编译时期技术,在编译源码的时候,会判断泛型指定的类型统一问题,当编译完成之后,生成的class文件中是没有泛型内容。
三、 枚举
枚举指有一组固定的常量组成的类型
可以将枚举看成一种特殊的类,枚举的思想很简单,也很方便,他代表了一组固定的常量值
public enum enumname{
enum-body, //一般写常量
}
四、 包装类
1. 包装类介绍
Java为每种基本数据类型分别设计了对应的类,称之为包装类
其实集合在存储数据的时候,如果存储的是基本数据类型的话,在存储的时候,会将基本数据类型转换成包装类之后在进行存储
描述基本数据类型与之对应的包装类
基本数据类型 | 对应的包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
2. 装箱和拆箱
2.1 装箱
基本类型转为包装类型称为装箱
装箱的方式有三种:1. 直接转换 2. valueOf 3. 构造方法实现转换
- 直接定义转换
Integer num1 = 11;
- valueof
Integer num2 = Integer.valueOf(num);
- 构造方法实现转换
Integer num3 = new Integer(num);
2.2 拆箱
包装类转为基本数据类型称为拆箱
拆箱的方式有两种:1. 直接转换 2. xxxValue
- 直接转换
Integer num = 22;
int num1 = num;
- xxxValue
Integer num = 22;
int num2 = num.intValue();
Double d = 22.2;
double dd = d.doubleValue();
3. 包装类常用方法
- toString 基本类型 -> 字符串
toString():以字符串方式返回包装对象表示的基本数据类型
String str = Integer.toString(88);
String str = Double.toString(33.3);
- parseXXX 字符串 -> 基本类型
parseXXX():把字符串转换为相应的基本数据类型数据(Character除外)
long long1 = Long.parseLong("123");
boolean b = Boolean.parseBoolean("true");
五、 Math类和Random类
1. Math类
Java的Math类封装了很多与数学相关的属性和方法
- abs —— 求绝对值
- max/min —— 求最大值/最小值
- random —— 生成一个0-1之间的随机数
- ceil —— 向上取整
- floor —— 向下取整
- round —— 四舍五入 +0.5,向下取整
2. Random类
Random类是一个随机数产生器,构造方法Random()中使用当前的时间来初始化Random对象,因为没有任何时刻的时间是相同的,所以可以减少随机数序列相同的可能性。
Random random = new Random();
int n = random.nextInt();//生成一个整数类型的随机数
Random类的nextInt()方法还可以在有限范围内产生随机数
public int nextInt(int n);
该方法返回一个伪随机数,它是从此随机数生成的序列中取出的 [0,n)之间均匀分布的整型值
六、 String
常用方法
- charAt(int index) —— 返回指定位置的字符
- length() —— 求字符串的长度
- equals() —— 比较两个字符串是否相同
- equalsIgnoreCase() —— 忽略大小写比较
- toUpperCase() —— 转换大写
- toLowerCase() —— 转换小写
- concat() —— 字符串拼接 , + 是一样的
- split(String regex) —— 根据给定表达式的匹配拆分字符串,返回字符串数组
方法名 | 说明 |
---|---|
public int indexOf(int ch) | 搜索第一个出现的字符ch(或字符串value),如果没有找到,返回-1 |
public int indexOf(String value) | |
public int lastIndexOf(int ch) | 搜索最后一个出现的字符ch(或字符串value),如果没有找到,返回-1 |
public int lastIndexOf(String value) | |
public String substring(int index) | 提取从位置索引开始的字符串部分 |
public String substring(int beginindex, int endindex) | 提取 beginindex 和 endindex 之间的字符串部分 |
public String trim() | 返回一个前后不含任何空格的调用字符串的副本 |
七、 IO
1. IO介绍
IO技术:把程序中的数据最终输出到持久设备上,或者从持久设备读取已经存在的数据,最后给我们读取到程序中。
IO:input 输入 读操作
output 输出 写操作
2. file类介绍
在Java中使用**File 这个类来描述持久设备上的文件或者文件夹**
文件:是用来保存真实的数据的
文件夹:管理文件和文件夹
File类常用的构造方法有2个,构造方法可以把对应的文件或文件夹来封装成一个File对象。File没有空参数的构造方法
常用的构造方法
- File(String pathname)
- 把一个字符串描述的文件或文件夹封装成File对象。File类的构造方法会把指定的内容封装成File对象
- File(String parent, String child)
- 可以把一个文件或文件夹的父目录单独分离出来,然后再结合当前的子目录一起封装成File对象
3. File常用方法
方法名称 | 说明 |
---|---|
boolean exists() | 判断文件或目录是否存在 |
boolean isFile() | 判断是否是文件 |
boolean isDirectory() | 判断是否是目录 |
String getPath() | 返回此对象表示的文件的相对路径名称 |
String getAbsolutePath | 返回此对象表示的文件的绝对路径名称 |
String getName() | 返回此对象表示的文件或目录的名称 |
boolean delete() | 删除此对象指定的文件或目录 |
boolean createNewFile() | 创建名称的空文件,不创建文件夹 |
long length() | 返回文件的长度,单位为字节,如果文件不存在,则返回0L |
4. 字节流
IO流技术: I:Inputstream O:OutputStream
流的分类
- 按流向划分
- 输出流 —— OutputStream和Writer作为基类
- 输入流 —— InputStream和Reader作为基类
- 按处理数据单元划分
- 字节流
- 字节输入流 —— InputStream基类
- 字节输出流 —— OutputStream基类
- 字符流
- 字符输入流 —— Reader基类
- 字符输出流 —— Writer基类
- 字节流
使用流的目的是把程序中的数据写持久设备上,或者从持久设备上读取数据,在写或者读之前,先要让这个流和持久设备之间建立一个读或者写的通道,只有有了这个通道之后才能在这个通道中开始读写数据。
4.1 InputStream
FileInputStream是InputStream常用子类
FileInputStream流被称为文件字节输入流,意思指对文件数据以字节的形式进行读取操作。
常用的构造方法有:FileInputStream(File file) 和 FileInputStream(String path)
常用的方法
- int read():这个方法被调用一次,它运行一次,会从底层读取一个字节数据,当把这个字节读取完成之后,在文件中的隐式光标自动的移动到第一个字节和第二个字节之间,返回的数据是这个字节数据转成int值,当读取到文件的末位时,会返回-1。
- int read(byte[] b):这个方法每执行一次,就会从底层读取多个字节数据,把读取到的字节数据存储在byte数组中,返回的int值,是表示当前到底从底层读取了几个字节数据。即就是给byte数组中存储了几个字节数据。如果读取到文件末尾会返回-1.
- int read(byte[] b, int off, int len):off从哪里开始读多少
- void close():关闭流,在使用完成之后一定要关闭流
4.2 字节读取文件模板
- 一次读取一个字节的模板代码
//1.先创建流对象和文件进行关联
FileInputStream fis = new FileInputStream("文件");
//2.定义变量,用来记录从底层文件中读取到的那个字节数据
int ch = 0;
//3.定义循环开始从文件中读取数据
while((ch = fis.read()) != -1) {
//处理读取到的数据,数据存储在ch空间中
}
//4.关闭流对象
fis.close();
- 一次读取多个字节数据模板代码
//1.先创建流对象和文件进行关联
FileInputStream fis = new FileInputStream("文件");
//2.定义数组,用来存储从底层读取的多个字节数据,一般都是1024的整数倍
byte[] buf = new byte[1024];
//3.定义变量,用来记录到底从底层读取了多个字节数据
int len = 0;
//4.使用循环从底层开始读取数据
while((len = fis.read(buf)) != -1) {
//处理读取到的数据,数据存储在buf数组中,buf中共计存储了len个字节数据,并不一定把buf存满
}
//5.关闭流对象
fis.close();
4.3 OutputStream
OutputStream:专门用来操作字节数据的字节输出流对象。是所有字节输出流的超类,并且是个抽象类,不能直接创建对象
常用方法
- write(int b):把字节数据直接写在持久设备上
- write(byte[] buf):把字节数组中的数据写在持久设备上
- wirte(byte[] buf, int off, int len):把buf字节数组中的数据从off下标开始共计写出len个字节数据
- close():关闭流,在使用完成之后一定要关闭流
FileOutputStream是OutputStream的子类
FileOutputStream:是专门用于把程序中的数据写到指定的文件中的输出流对象,这个流主要是用来写字节数据的。
FileOutputStream的构造方法
- FileOutputStrean(File file):在创建输出流对象的时候,指定一个文件对象,用于把数据写在指定的这个文件中
- FileOutputStream(String pathName):在创建输出流对象的时候,把这个字符串当前一个具体的文件位置,然后把数据写在这个文件中
- FileOutputStream(String name, boolean append):append表示是追加还是覆盖,true表示追加,false表示覆盖,默认为false
在使用输出流的时候,如果指定的文件不存在,会自动创建这个文件对象,如果文件存在,会创建一个新的把原来的文件覆盖掉。
注意
- 前面两个构造方法如果指定的文件不存在,会自动创建这个文件对象;如果文件存在,会创建一个新的把原来的文件覆盖掉,以前的数据都消失了
- 第三个构造方法写数据时,当文件不存在同样会创建一个新的文件;如果文件存在了,第二个参数表示是否追加,flase表示不追加(默认),和其他两个构造方法结果一致,为true时,表示追加,在原来内容的基础上新增内容
5. 字符流
Java给我们提供专门用于操作字符数据的流对象
字符输入流:Reader
字符输出流:Writer
5.1 Reader
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()
Reader类常用方法
- int read():执行一次,会得到一个字符数据,如果读取到文件末尾,返回的-1
- int read(char[] c):执行一次,会从底层读取多个字符数据,存储在字符数组中,返回本次读取的字符数据个数,如果读取到文件末尾,返回-1
- read(char[] c, int off, int len):将读取到的数据存储在数组中,从off开始,到len结束
- void close():关闭流
子类InputStreamReader常用的构造方法,它又称为转换流
InputStreamReader(InputStream in)
InputStreamReader(InputStream in, String charsetName)
FileReader类是InputStreamReader的子类
FileReader(File file)
FileReader(String name)
5.2 字符读取文件模板
//1. 一次读取一个字符数据
//定义字符输入流对象和文件进行关联
FileReader fr = new FileReader("文件");
//定义变量,存储读取到的字符数据
int ch = 0;
//使用循环读取数据
while((ch = fr.read()) != -1){
//处理读取到的单个字符数据,数据在ch中保存
}
//关闭流对象
fr.close();
//2. 一次读取多个字符数据
//定义字符输入流对象和文件进行关联
FileReader fr = new FileReader("文件");
//定义字符数组,用来存储一次读取到的多个字符数据
char[] buf = new Char[1024];
//定义变量,记录每次读取到字符个数
int len = 0;
//使用循环读取数据
while((len = fr.read(buf)) != -1){
//处理读取到的数据,数据保存在buf中,buf中共计保存了len个字符数据
}
//关闭流
fr.close();
5.3 BufferedReader
BufferedReader为提高字符流读取文本文件,有一个独有的方法readLine(),又称高效流,带有缓冲区的流
当所有文件信息都获取完成时候,readLine()方法将返回null
注意:文档中有空行,读取的时候不是null,而是空内容
BufferedReader常用的构造方法
- BufferedReader(Reader in):需要接受一个字符流
如果读取文件中出现中文乱码问题,可以考虑指定编码,原则如下:
FileInputStream fis = new FileInputStream("文件路径");
//使用InputStreamReader并设置编码格式
InputStreamReader fr = new InputStreamReader(fis, "UTF-8");
BufferedReader br = new BufferedReader(fr);
5.4 Writer
Writer类常用方法
- write(String str):写一个字符串
- write(String str, int off, int len):把str中的字符数据从off开始,共计写len个
- void close():关闭流
- void flush():刷新
子类OutputStreamWriter常用的构造方法
- OutputStreamWriter(OutputStream out)
- OutputStreamWriter(OutputStream out, String charsetName)
FileWriter类是OutputStreamWriter的子类
- FileWriter(File file)
- FileWriter(String name)
注意:
用字符输出流写出数据的时候:
- 其实数据不是直接写在文件中,而是把字节数据存储在缓冲区中。如果这时字符输出流的缓冲区没有写满,或者我们没有调用flush方法,或者没有关闭流对象,在程序结束之前,数据依然在缓冲区中,不会被写到文件中。所以要求,在关闭流之前一定要做到刷新操作。
- 字符输出流(FileWriter)关联的文件如果不存在,会在指定的位置创建,如果文件存在,在创建流对象的时候,没有指定的true值,这时会创建新的文件覆盖原来的文件。
flush和close的区别
flush是把缓冲区的数据刷出到文件中,没有关闭流和文件之间的关联,在刷新之后,依然可以使用流对象继续给文件中写数据
close方法在关闭流之前会先调用flush把缓冲区中的数据写到底层文件中,然后把流和文件的关联关系断开。一旦调用close方法,流就已经被关闭了,就无法再使用当前这个流对象写数据。
5.5 BufferedWriter
使用FileWriter和BufferedWriter类可以提高字符流写文件的效率
BufferedWriter常用的构造方法
- BufferedWriter(Writer out)
6. 流的总结
6.1 字节流:2类
字节输入流:
InputStream:它是字节输入流的超类,它中定义字节输入流的基本操作方法
close():关闭流对象
read():一次读取一个字节数据
read(byte[] buf):一次读取多个字节数据,存储在buf中,然后给buf中读取的字节个数
FileInputStream:专门用来读取文件中字节数据的文件字节输入流对象
创建这个类的对象,读取文件中的数据
//模板代码:
//1. 一次读取一个字节
FileInputStream fis = new FileInputStream("文件");
int ch = 0;
while((ch = fis.read()) != -1){
//处理读取到的数据,数据在ch中
}
fis.close();
//2. 一次读取多个字节数据
FileInputStream fis = new FileInputStream("文件");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1) {
//处理数据,数据在buf中,共计len个
}
fis.close();
字节输入流:
OutputStream:字节输出流的超类,它中定义的基本的输出字节数据的操作
close():关闭流对象
write(int b):写出一个字节数据,注意只能把这个int数据中的最低1个字节数据写出去
write(byte[] buf):把字节数组中的所有数据写到文件中
write(byte[] buf,int off,int len):把字节数组中的数据从off开始,共计写len个
FileOutputStream:它是专门用来给文件中写字节数据的流对象。使用输出流写数据的时候,如果对应的文件不存在会新建一个文件,如果文件存在,在创建对象的时候,没有指定true值,就会继续创建新的文件,覆盖原来的文件。
一般会在读的while循环中使用写的功能,往出把读取到的数据写出去。
6.2 字符流:2类
字符输入流:
Reader:是所有字符输入流的超类
close():关闭流对象
read():一次读取一个字符数据,但返回的字符数据的编码值
read(char[] buf):一次读取多个字符数据,把数据存储在buf中,返回读取的字符个数
FileReader:专门用于读取字符文件字符输入流对象,它内部使用本地默认的编码表(GBK)
字符输出流:
Writer:是所有字符输出流的超类
close():关闭流对象
write(int b):写出一个字符数据 write(‘a’):这个会把这个a转成97,再写出去
字符数据和int数据在一定范围内可以相互赋值
write(char[] buf):把buf数组中的数据全部写出去
write(char[] buf,int off,int len):把buf中的数据从off开始共计写len个
write(String s):把一个字符串写出去
FileWriter:专门用于给字符文件中写字符数据的便捷类,它的底层使用的默认的编码表和默认的缓冲区
7. DataInputStream
在IO包中提供了两个与平台无关的数据操作流
- 数据输出流:DataOutputStream
- 数据输入流:DataInputStream
其使用方法和字节流类似
DataInputStream类
- FilterInputStream的子类
- 与FileInputStream类结合使用读取二进制文件
- 构造方法:DataInputStream(InputStream in)
DataOutputStream类
- FilterOutputStream的子类
- 与FileOutputStream类结合使用写二进制文件
- 构造方法:DataOutputStream(OutputStream out)
8. 序列化和反序列化
序列化是用与将对象状态转换为字节流的过程。需要使用对象输出流ObjectOutputStream。 对象 -> 文件 —— 写的过程
我们使用new关键字创建在内存中的对象,包括对象中的所有数据,持久性的保存到硬盘上,通常是文件中, 对象 -> 文件
反序列化是从特定的流中获取数据重新构建对象的过程。需要使用对象输入流ObjectInputStream。 文件 -> 内存 —— 读的过程
保存在文件中的对象通过反序列化的手段从新加载在内存中
对象的序列化主要有两种用途:
- 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中
- 在网络上传送对象的字节序列
注意:
- 序列化和反序列化都是基于二进制流的,也就是说,在信息转化为二进制存储在文件中之后,用文本编辑器打开查看的话,肯定是会出现乱码的。只有通过反序列化才能将存储的二进制读取出来,然后正常显示在控制台上。
- 如果一个对象要实现序列化的话,则这个类必须要实现一个序列化的接口
实现序列化:序列化是写的过程
实现反序列化:反序列化是读的过程