Java学习笔记04(持续更新)

文章目录

  • 4.1 Java异常类及其处理
    • 4.1.2 Java异常类
    • 4.1.2 Java异常的处理
    • 4.1.3 自定义异常类
  • 4.2 Java集合类及其使用
    • 4.2.1 Collection 接口
    • 4.2.2 Iterator 接口
    • 4.2.3 List 接口及其实现类
    • 4.2.5 Set 接口及其实现类
    • 4.2.6 Map接口及其实现类
    • 4.2.7 Java 泛型
  • 4.3 Java输入输出流
    • 4.3.1 文件及File类
    • 4.3.2 文件字节流
    • 4.3.3 文件字符流
    • 4.3.4 缓冲流
    • 4.3.5 转换流
    • 4.3.6 标准输入输出流
    • 4.3.7 序列化与反序列化
    • 4.3.8 随机存取文件类

本博客用于记录本人Java的学习过程,本人才疏学浅,如有错误,请在评论区指出

Java学习笔记(纯干货)(持续更新):
Java学习笔记01
Java学习笔记02
Java学习笔记03

4.1 Java异常类及其处理

异常是在程序运行过程中发生的异常事件。

4.1.2 Java异常类

在程序运行过程中,可能遇到两种错误,一种是致命错误,程序不能简单地恢复运行;另一种是非致命错误,这种错通过修正后程序可以继续运行,通常称为异常。
下面我们来看Java中常见的异常类。

名称 含义
ArithmeticException 算数异常
ArrayIndexOutOfBoundsException 数组下标越界
ArrayStoreException 数组元素赋值时类型不兼容
ClassCastException 类型强制转换异常
ClassNotFoundException 未找到相应类
EOFException 文件已结束
FileNotFoundException 文件未找到
IllegalAccessException 访问某类被拒绝
InstantiationException 使用newInstance()创建一个抽象类或接口实例时出现错误
IOException 输入输出异常
NegativeArraySizeException 负数组长度异常
NUllPointerException 空对象异常
NumberFormatException 字符串转化为数字异常
NoSuchFieldException 字段未找到
NoSuchMethodException 方法未找到
SecurityException 小应用程序执行浏览器的安全设置禁止的动作时抛出的异常
SQLException 操作数据库异常
StringIndexOutOfBoundsException 字符串索引超出范围

4.1.2 Java异常的处理

在异常产生后如果不进行处理,程序则会被终止,为了保证程序运行,就需要我们处理异常。

Java异常处理机制:

  • Java程序执行过程中如出现异常,会自动生成一个异常类对象,该对象将被提交给Java运行时系统(Runtime),这个过程被称为抛出(throw)异常。
  • 当Java运行时系统接收到异常后,会寻找能处理这个异常的代码,并把这个异常交给它处理,这个过程被称为捕获(catch)异常。
  • 如果Java运行时系统找不到可以捕获异常的方法,那么运行时系统就会终止,相应的Java程序也会终止。

异常的处理方法:
主要可以归纳为三种:

  • 程序运行时异常通常不做处理,由Java虚拟机自动处理。
  • 使用 try-catch-finally语句捕获异常。
  • 使用子句throw声明抛出异常

下面我们分别来看看这三种处理方式:

第一种
对于第一种,异常发生后,由java虚拟机自动处理异常,给出异常提示,并终止程序的运行。
对于这样的错误,我们有两种修改代码的方式:
第一种就是直接修改代码,找出出现错误的的语句,并将其修改正确。
另一种就是在不容易修改代码时,添加异常处理语句,即使用try-catch-finallythrow语句。下面我们就介绍这两种语句。

第二种
第二种就是使用try-catch-finally语句,可以通过它来捕获和处理多个异常。
格式:

try{
语句块			//可能抛出异常的代码块
}catch(异常类1 异常对象1){
	捕获异常类1有关的处理程序段
	//程序发生异常时所采取的动作
}catch(异常类2 异常对象2){
	捕获异常类1有关的处理程序段
}catch(异常类n 异常对象n){
	捕获异常类1有关的处理程序段
}
finally {
	语句块		//无论是否捕获到异常,最后必须执行的代码
}

说明:
(1)try块 — 捕获异常: 用于监控可能发生异常的程序代码块是否发生异常, 如果发生异常,try代码块将抛出异常类所产生的对象并立刻结束执行,而转向异常处理cach部分。对于系统产生的异常或程序块中未用try 监控所产生的异常,将异常对象抛出,由Java虚拟机处理。

(2) catch块 — 处理异常: 抛出的异常对象如果属于catch内所定义的异常类,则catch会捕获该异常,并进入catch中的对应代码段继续运行程序,如果异常对象不属于catch 中所定义的异常类,则进入finally块继续运行程序。
catch包括两个参数:一个是类名,指出捕获的异常类型,必须是Throwable类的子类;另一个是参数名,用来引用被捕获的对象。

(3) fnally块 — 最终处理: 无论是否发生异常 都会执行的语句块。 比如执行关闭打开的文件、删除临时文件、关闭数据库连接等操作。

(4) catch可以有多个,分别捕获不同的异常对象给出不同的处理,try和fnaly只能有一个。

第三种
在一般方法的声明后加上关键字throws,关键字后面的“异常列表”中指出可能发生且不进行处理的所有异常列表,注意多个异常之间用逗号分隔。
格式:

访问权限修饰符 类型 方法名(参数名) throws 异常列表{};

例如,

public void test(int i) throws ArithmeticException,NumberFormatException{};

4.1.3 自定义异常类

在程序运行过程中可能出现系统不能识别的运行错误,这时,我们就需要自己创建异常和异常类,使系统能够识别这种错误并进行处理。

创建用户自定义异常时,需要完成以下工作:

  • 声明一个新的异常类,使之继承Exception类、Exeption 类的子类或用户自定义的异常类。
  • 为新的异常类定义属性和方法, 或重教父类的属性和方法,便之能够体现出程序中出现的这种异常信息。
  • 抛出用户自定义的异常。用户自定义的异常不可能依靠系统自动抛出,而必须通过throw语句抛出,通常是通过条件判断来确定是否抛出这个异常类的对象。

4.2 Java集合类及其使用

集合(也称“容器”)是一系列对象的聚集,代表一组数据对象的一个对象,集合对象中每个元素称为集合的元素。集合中的每一个元素都具有一定的数据类型,任何数据类型的对象都可以存放在集合中,并提供了对集合及其集合元素的相关的操作方法。

Java常用集合中的接口、类之间的继承关系:
(1) Collection 接口是List接口和Set接口的父接口,通常情况下,不直接使用。
(2) List 接口实现了Collection 接口,List 接口允许存放重复的对象,按照对象插入的顺序排列。
(3) Set 接口实现了Collection 接口,Set 接口不允许存放重复的对象,按照自身内部的排列规则排列。
(4) Map接口以键值对(key-value)的形式存放对象,其中的键(key)对象不可以重复,值(value) 对象可以重复。

下面我们来详细介绍这些接口及其实现类的使用。

4.2.1 Collection 接口

Collection是Java集合框架的根接口,下面面来看看其中包含的主要方法。

方法名 功能
boolean add(E e) 向集合中添加元素e / 返回集合中的元素个数
int size() 返回集合中的元素个数
boolean addAll(Collection c) 将集合c中的所有元素添加到当前这个集合
boolean contains(Object o) 如果该集合中包含对象o,返回true
boolean containsAll(Collectionc) 如果该集合中包含集合c中的所有元素,返回true
boolean isEmpty() 如果集合不包含任何元素,返回true
void clear() 删除集合中的所有元素
Iterator iterator() 返回该集合中元素所有的迭代器
boolean remove(Object o) 从集合中删除元素o
boolean removeAll(Collection c) 从集合中删除集合c中的所有元素
boolean retainAll(Collection c) 保留c和该集合都有的元素(交集)
Object[] toArray() 返回该集合元素构成的Object数组

Collection虽然是集合的最高父接口,但是直接使用Collection接口会造成操作意义不明确,所以在实际开发中不提倡直接使用Collection接口。

4.2.2 Iterator 接口

Iterator(迭代器)接口不是一种集合类型,主要用来遍历集合中的元素,迭代器每用完一次都要更新。

迭代器接口的主要方法:

boolean hasNext()	// 只判断下一个下标有没有元素
Object next()		//获得下一个元素
void remove()		//从集合中删除当前元素

默认迭代器
实例化 Iterator

Iterator<MyClass> myIterator = list.iterator();

迭代方法:

while(myIterator.hasNext()) {
    myIterator.next() ...
}

ListIterator

实例化 ListIterator

ListIterator<myClass> listIterator = list.listIterator();

正序迭代

while(listIterator.hasNext()) {
    // listIterator.next() ...
}

逆序迭代

while(listIterator.hasPrevious()) {
    // listIterator.previous() ...
}

4.2.3 List 接口及其实现类

List接口继承了 Collection接口,用来描述有序列表,而且列表元素可以重复,用索引来访问元素(很像数组)。

除了在 Collection接口中继承了方法,还定义了一些新的方法:

方法名称 功能
void add(int index,Object obj) 在指定的位置插入元素
boolean addAll(int index,Collection coll) 在指定位置插入集合的所有元素
Object remove(int index) 删除指定位置的元素
Object set(int index,Object obj) 替换指定位置的元素
Object get(int index) 获得指定位置的元素
int indexOf(Object obj) 获得指定位置的下标,若有多个则返回第一个索引,没有就返回-1
int lastIndexOf(Object obj) 获得指定位置的下标,若有多个则返回最后一个索引,没有就返回-1
listInterator 获得一个包含所有对象的 ListIterator型实例
listInteror(int index) 用来获得一个从指定位置往后所有对象的ListIterator型实例
Last subList(int fromIndex,int toIndex) 获得从指定位置到指定位置之间的所有对象,重新生成一个 List 对象

实例化:

List<泛型>list1 = new ArrayList<泛型>();	//利用ArrayList类实例化 List
List<泛型>list2 = new LinkedList<泛型>();	//利用 LinkedList类实例化 List

泛型也可以不填,我们后面再详细的介绍泛型。
现在先看 ArrayList 和 LinkedList

顺序列表ArryList
创建时可以指定初始容量,如果内存不够,系统会自动追加容量。
是一个大小可变的数组,在内存中分配连续的空间,遍历元素和随机访问元素的效率比较高。
ArrayList类的大多数方法都重写自List中定义的方法,上面都介绍了,不再赘述。

链式列表 LinkedList

采用链表存储方式,对于链表,各元素所占内存单元的相对位置可以是任意的,为了表示元素的逻辑邻接关系,还需要存放元素下一个位置的元素的指针。
LinkedList的操作特点和ArrayList相反,查找元素需要从首元素开始,时间复杂度是线性级的,而插入、删除元素时间复杂度是常量级别的。

LinkedList 的特有方法:

方法名称 功能
addFirst(E obj) 将指定对象插入到开头
addLast(E obj) 将指定的对象出入到结尾
getFirst() 获得开头的元素
getLast() 获得结尾的对象
removeFirst() 移除开头的对象
removeLast() 移除结尾的对象

4.2.5 Set 接口及其实现类

Set接口继承自Collction,用以描述不能包含重复元素的无序集合。

Set接口的常用实现类有三个: HashSet、 LinkedHashSet 和TreeSet。
下面我们来逐一介绍:

哈希集合: HashSet 和LinkedHashSet

哈希集合根据哈希码来存取集合中的元素。集合中不允许出现重复元素,当向哈希集合中添加元素时,会判断该元素是否重复,判断规则如下:

首先调用元素的hashCode方法计算出该元素的哈希码,如果该值与集合中己有元素的哈希码值都不相同,则认为该元素不重复,可以添加;如果该值与某个元素哈希码值相同,则继续调用equals 方法进一步判断, 若equals方法返回true,则说明重复,否则不重复。
因此,对于哈希集合来说,若重写了元素对应类的equals或hashCode方法中的某一个,则必须重写另一个,以保证两者有相同的判等逻辑。

HashSet实现了Set 接口,不保证元素的迭代顺序,对其进行添加、删除元素等操作的时间复杂度是常量级的。在构造HashSet对象时,可通过构造方法指定初始容量(即最多能存放多少个元素,默认为16个),当容量不够时,系统会自动追加空间。

LinkedHashSet(链式哈希集合)继承自HashSet,其也是根据元素的哈希码来决定元素的存储位置。与HashSet不同的是,LinkedHashSet 使用指针(链)来维护元素的次序,以保证其迭代顺序与添加元素的顺序一致。

也就是说,LinkedHashSet的遍历结果与添加顺序一致,而HashSet的遍历是无序的。

实例化:

Set<String> set = new HashSet<String>();
Set<String> set = new LinkedHashSet<String>();

树形集合:TreeSet

TreeSet与HashSet特性类似,不同的是TreeSet采用树形结构来存取集合元素,集合元素较多时,操作性能将低于HashSet 和LinkedHashSet。但是TreeSet实现了SortedSet接口,允许对集合元素排序,排序既可以是元素的自然顺序,也可以是自定义排序。

下面我们来看看SortedSet接口的主要方法:

方法名称 功能
Comparator comparator() 获得对集合中元素排序的比较器,若使用元素的自然顺序排序返回null
Object first() 返回在集合中的排序位于第一的对象
Object last() 返回在集合中的排序位于最后的对象
SortedSet headSet(Object to) 返回从集合头到to位置的元素组成的子集合
SortedSet subset(Object fom,Object to) 返回从from到to位置的元素组成的子集合
SortedSet tailSet((Object from)/ 返回从from到集合尾的元素组成的子集合

排序方式分为自然排序和自定义排序,其中自然排序是默认的排序方式。

自然排序

java.lang包下提供名为Comparable的接口,其中定义了int compareTo(Object o)方法,实现该接口的类的对象可以相互比较“大小”:

(1)若方法返回0,则表示当前对象与对象o“相等”。
(2)若返回正数,则表示当前对象“大于”对象o
(3)若返回负数,则表示当前对象“小于”对象0。

一般应使compareTo方法返回0的逻辑与该类的equals方法返回true的逻辑相同。要使用自然排序方式,元素对应类必须实现Comparable接口。TreeSet会根据重写的compareTo方法的返回值,将集合中元素按大小关系升序排列。

自定义排序

自定义排序方式通过TreeSet构造方法指定的比较器(实现了java.til.Comparator接口的类的对象)来确定被比较的两个元素的“大小”关系,然后将集合元素按照大小关系升序排列。Comparator接口所定义的方法int compare(Object o1,Object o2)的返回值意义同"compareTo"方法是一致的。 自定义排序的优先级高于自然排序方式。

Comparetor接口可以弥补Comparable接口的不足,可以更加灵活地实现不同的排序规则,可以对同一个类的对象按需要选择不同的规则排序
写法

比较器类

public class MyClassComparator implements Comparator<myClass> {
    @Override
    public int compare(myClass o1, myClass o2) {

    }
}

4.2.6 Map接口及其实现类

Map不允许包含重复的键,键用来唯一标识键值对元素。

方法名 功能
Object put(key, value) 将一个键/值映射放入图中
Object get(key) 根据键获取对应的value值
Set keySet() 返回包含键的规则集
Collection values() 返回包含值的集合
boolean containsKey(key) 返回图中是否包含键值key
Set entrySet() 返回一个图中包含条目的规则集
int size() 返回图中的键值对个数
Object remove(key) 删除指定键对应的条目

实例化:

Map<String, Integer> map = new HashMap<String, Integer>();

Map还有一个子接口 SortedMap(有序映射),包含的元素按照键来排序,方法与 SortedSet的很相似。常用的实现类有三个:HashMap、LinkedHashMap和TreeMap,很多特性和 set 的很像,这里就不再赘述。

4.2.7 Java 泛型

创建 ArrayList 类型的集合对象

List<类型> list = new ArrayList<类型>();

上面的语句在接口和类后面跟的 <类型> 就被称为泛型。

泛型带来的最大好处是类型安全。通过指定集合所含元素的确切类型,程序在编译期就能由编译器检查出被添加的元素是否满足类型约束,而不会把错误延迟到运行期。

4.3 Java输入输出流

4.3.1 文件及File类

除键盘和显示器外,使用最广泛的输入输出设备就是磁盘,存储在磁盘上的文件称为磁盘文件。对文件中的数据进行加工处理,需要利用文件的路径确定文件,创建文件类File的对象,然后基于该文件对象创建文件输入或者输出流,进行读写,最后关闭输入或者输出流。我们称为文件的读写。

1.文件与文件路径

存储在磁盘中的文件叫磁盘文件,简称文件,与输入输出设备对应的是设备文件。每个个文件具有唯一的文件标识,即文件全称。

Windows系统环境下文件格式为:文件路径\文件名。
文件路径由磁盘盘符和文件夹名构成。文件名由主文件名和扩展名构成。

在Java语言中路径的描边述有两种方式:
第一种:采用转义字符的间隔符,即“\ \”。
第二种,采用反斜线间隔符,即“/”。

例如,
在Windows系统中的路径:D:\java\cj.dat
在Jave中的描述形式为:

 D:\\java\\cj.dat或者为: D:/java/cj.dat

2.文件类型

在Java语言中,根据数据的存取方式,文件可分为顺序文件、随机文件;根据存取内容,文件可分为文本文件和二进制文件。

顺序文件:顺序文件的结构比较简单,当读取数据时,只能从文件头开始,一个数据一个数据的按顺序读取,直到找到要查找的记录为止或到文件尾;当写入数据时,都是在文件尾添加新数据。Java 语言主要提供的就是顺序读写文件,对于一个文件,要么是读操作,要么是写操作,同一个文件不能同时进行读写操作。

随机文件:随机文件又称直接存取文件。可以在文件的任意位置进行读写,并且可以同时进行读写操作。Java语言专门提供了RandomAccessFile类实现随机文件的读写操作。

二进制文件:以“字节”方式保存文件。通常使用“字节流”实现对二进制文件的读写。

文本文件:以“字符“”方式保存文件,也可以采用“字节流”,实现对该类文件的读写。在实际应用中,根据读写数据的特点,选择采用字节流或者字符流。

3.File 类与使用

Jana 提供 java.io.File 类实施文件和文件夹管理,通过调用 File 类提供的各种方法,能够创建、删除、重命名文件(文件夹),并设置、查询文件(文件夹)的有关属性。

File 类的常用构造方法有4种

File(String pathname)	/pathame:文件路径字符串,包括文件路径和文件名

File (String dir,String filename)	//dir:父路径字符串: flename: 文件名

File(File dir,String filename)	//dir:是父路径File对象: filename: 文件名

File(URI uri)//uri: 统一资源标识, 基于uri创建File对象

注意:创建File对象时,可以使用绝对路径,也可以使用相对路径。

例如,创建5个File对象,其中 f2,f3,f4,f5 都是指同一个文件的不同对象名称。

File fl=new File("d:/java"); //使用路径,创建文件夹对象f1

File f2=new File("d:/java/test.txt");	//使用路径,创建文件对象f2

File f3=new File("d:/java","test.tx");	//创建文件对象f3,与f2等价

File f4=new File(f1,"test.txt");	//创建文件对象f4,与f2、f3等价

File f5=new File("file//:/java/tetxt"); //创建文件对象f5, 与2、f3、 f4等价

File 类提供的方法

方法名称 功能描述
String getName() 获取文件的名字
String getParent() 获取文件的父路径字符串
String getPath() 获取文件的相对路径字符串
String getAbsolutePath() 获取文件的绝对路径字符串
boolean exists() 判断文件或文件夹是否存在
boolean canRead() 判断文件是否可读
boolean isFile() 判断文件是否是个正常的文件,而不是目录
boolean canWrite() 判断文件是否可被写入
boolean isDirectory() 判断是不是文件夹类型
boolean isAbsolute() 判断是不是绝对路径
boolean isHidden() 判断文件是否是隐藏文件
boolean delete() 删除文件或文件夹,如果删除成功返回结果为true
boolean mkdir() 创建文件夹,如果创建成成功返回结果为true
boolean mkdirs() 创建路径中p包含的所的所有父文件夹和子文件夹,如果所有父文件夹和子文件夹都成功创建,返回结果为true
boolean createNewFile() 创建一个新文件
long length() 获取文件的长度
long lastModified() 获取文件的最后修改日期
String[] list() 列出目录中的文件
String[] list(FilenameFiliter filter) 使用过速器列出目录中的文件
Boolean setReadOnly() 设置为只读

4.3.2 文件字节流

InputStream

InputStream类是字节输入流的抽象类,是所有字节输入流的父类,其各种子类实现了不同的数据输入流。

抽象类InputStream主要定义了4个方法,

方法名称 功能
abstract int read() 从输入流中读取一个字节,返回0~255整数,遇到数据流结束,则返回-1
int read(byte b[]) 从输入流读取多个字节,存入字节数组b,返回读取字节的个数,如果返回-1,表示数据流结束
int read(byte b[], int offset,int length) 从输入流的offset 位置开始,读取length字节,存入字节数组b,返回实际读取字节的个数,如果返回 -1,表示数据流结束
void close() 关闭输入流,并释放相关资源

OutputStream

OutputStream类是字节输入流的抽象类,是所有字节输出流的父类,其各种子类实现了不同的数据输出流。

方法名称 功能
abstract int write(int b) 向输出流写入一个字节,写入整数b的低字节,前三个高字节被忽略
int write(byte b[]) 将b中的数据写入到输出流
int write(byte b[], int offset,int length) 从数组b的offset位置开始,写入 length 个字节数到输出流中
void close() 关闭输出流,并释放相关资源

上面讲的这些方法都抛出IOException类型的异常,需要在程序中给出相应的异常处理。

4.3.3 文件字符流

Reader

Reader类是字符输入流的抽象类,是所有字符输出流的父类。

方法:

名称 功能
int read() 读取一个字符,返回字符对应的编码整数值,遇到数据流结束,则返回-1
int read(char c) 读取多个字符,存入字符数组,返回读取字节的个数,如果返回-1,表示数据流结束
abstract int read(char c[],int offset,int length) 从输入流的offset 位置开始,读取length字节,存入字符数组b,返回实际读取字节的个数,如果返回 -1,表示数据流结束
void close() 关闭输出流,并释放相关资源

Writer

Writer类是字符输出流的抽象类,是所有字符输出流的父类。

方法名称 功能
abstract int write(int c) 向输出流写入一个字符,写入整数c的低位2个字节,前2个高字节被忽略
int write(char c[]) 将c中的数据写入到输出流
int write(char c[], int offset,int length) 从数组c的offset位置开始,写入 length 个字节数到输出流中
void close() 关闭输出流,并释放相关资源

这些方法也都抛出IOException类型的异常,需要在程序中给出相应的异常处理。

4.3.4 缓冲流

缓冲流,也叫高效流,是对4个基本的FileXxx流的增强。
基本原理:在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

字节缓冲流:BufferedInputStreamBufferedOutputStream
字符缓冲流:BufferedReaderBufferedWriter

1. BufferedInputStream & BufferedOutputStream
字节缓冲流
构造方法

public BufferedInputStream(InputStream in)		创建一个 新的缓冲输入流。
public BufferedOutputStream(OutputStream out)	 创建一个新的缓冲输出流。

例如:

// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));

// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

BufferedInputStream 使用步骤:

  1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
  2. 创建BufferedInputStream对象,构造方法中传递FiLeInputStream对象,提高FiLeInputStream对象的读取效率
  3. 使用BufferedInputStream对象中的方法read,读取文件
  4. 释放资源

BufferedOutputStream使用步骤

  1. 创建FiLeOutputStream对象,构造方法中绑定要输出的目的地
  2. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FiLeOutputStream对象效率
  3. 使用BufferedoutputStream对象中的方法write,把数据写入到内部缓冲区中
  4. 使用Buffered0utputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
  5. 释放资源(会先调用fLush方法刷新数据,第4部可以省略)

2.BufferedReader & BufferedWriter

字符缓冲流
构造方法

public BufferedReader(Reader in) 	创建一个 新的缓冲输入流。
public BufferedWriter(Writer out) 	创建一个新的缓冲输出流。

例如:

// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));

// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

特有方法
字符缓冲流的基本方法与普通字符流调用方式一致,不再赘述,我们来看特有方法。

BufferedReader:public String readLine(): 读一行文字。
BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。

readLine方法演示,代码如下:

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
      	 // 创建流对象
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));
		// 定义字符串,保存读取的一行文字
        String line  = null;
      	// 循环读取,读取到最后返回null
        while ((line = br.readLine())!=null) {
            System.out.print(line);
            System.out.println("------");
        }
		// 释放资源
        br.close();
    }
}

newLine方法演示,代码如下:

public class BufferedWriterDemo throws IOException {
  public static void main(String[] args) throws IOException  {
    	// 创建流对象
  	BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
    	// 写出数据
      bw.write("黑马");
    	// 写出换行
      bw.newLine();
      bw.write("程序");
      bw.newLine();
      bw.write("员");
      bw.newLine();
  	// 释放资源
      bw.close();
  }
}
输出效果:
黑马
程序
员

BufferedReader使用步骤:

  1. 创建字符缓冲输入流对象,构造方法中传递字符输入流
  2. 使用字符缓冲输入流对象中的方法read/readL ine读取文本
  3. 释放资源

BufferedWriter使用步骤:

  1. 创建字符缓冲输出流对象,构造方法中传递字符输出流
  2. 调用字符缓冲输出流中的方法write ,把数据写入到内存缓冲区中
  3. 调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
  4. 释放资源

4.3.5 转换流

转换流是字节与字符间的桥梁。

字符编码
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。

编码:字符(能看懂的)–字节(看不懂的)

解码:字节(看不懂的)–>字符(能看懂的)

字符编码Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。

编码表:生活中文字和计算机中二进制的对应规则

字符集
字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

ASCII字符集 :
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。

ISO-8859-1字符集:
拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
ISO-8859-1使用单字节编码,兼容ASCII编码。

GB就是国标的意思,是为了显示中文而设计的一套字符集。

GB2312:
简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
GBK:
最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
GB18030:
最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。

Unicode字符集 :
Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,
编码规则:
128个US-ASCII字符,只需一个字节编码。
拉丁文等字符,需要二个字节编码。
大部分常用字(含中文),使用三个字节编码。
其他极少使用的Unicode辅助字符,使用四字节编码。

InputStreamReader类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法

InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

例如:

InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

指定编码读取

public class ReaderDemo2 {
    public static void main(String[] args) throws IOException {
      	// 定义文件路径,文件为gbk编码
        String FileName = "E:\\file_gbk.txt";
      	// 创建流对象,默认UTF8编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
      	// 创建流对象,指定GBK编码
        InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
		// 定义变量,保存字符
        int read;
      	// 使用默认编码字符流读取,乱码
        while ((read = isr.read()) != -1) {
            System.out.print((char)read); 
        }
        isr.close();
      
      	// 使用指定编码字符流读取,正常解析
        while ((read = isr2.read()) != -1) {
            System.out.print((char)read);
        }
        isr2.close();
    }
}

OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法

OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

例如,

OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");

指定编码写出

public class OutputDemo {
    public static void main(String[] args) throws IOException {
      	// 定义文件路径
        String FileName = "E:\\out.txt";
      	// 创建流对象,默认UTF8编码
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
        // 写出数据
      	osw.write("你好"); // 保存为6个字节
        osw.close();
      	
		// 定义文件路径
		String FileName2 = "E:\\out2.txt";
     	// 创建流对象,指定GBK编码
        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
        // 写出数据
      	osw2.write("你好");// 保存为4个字节
        osw2.close();
    }
}

4.3.6 标准输入输出流

在键盘输入、显示器显示信息,通常称为标准输入输出或者控制台输入输出。Java语言提供了Java.lang.System 类中的对象实现标准输入和输出。

(1) System.out: 标准输出流对象,默认为显示器,它是PrintStream类的对象。
(2) System.in: 标准输入流对象,默认为键盘,它是InputStream类的对象。
(3) System.err:标准错误输出流对象,默认为显示器,是PrintStream类的对象。

下面我们分别来看看:

1. 标准输入流System.in

System.in作为字节输入流类InputStream的对象实现标准输入,通过read()方法从键盘接受数据。
构造:

int read() 
int read(byte b[]) 
int read(byte b[],int offset,int len)

例如:

import java.io.IOException;
public class StdInput{
    public static void main(String[] args) throws IOException 
    {
        byte b[]=new byte[512];
        int count=System.in.read(b);
        for(int i=0;i<count;i++)
            System.out.print(b[i]+" ");
	}
}

2. 标准输出流System.out

作为打印流类 PrintStream 的对象实现标准输出,其中定义了print和printIn方法,支持leva任意基本类型、字符串类型的输出。我们前面都学习过了。

现在来看另外一种;
数据格式化输出方法printf其语法格式如下(和c语言类似):

printf(String format, Object. .args)

控制符号的含义:

(1) %s: 表示要输出是字符串。

(2) %d: 表示输出的是整数。

(3)%f:表示输出的是实数。

(4)%b:表示输出的是布尔。

(5) %c:表示输出的是字符。

(6) %10k:表示输出最多占10位,K可以是上面的任意一种类型。

(7) %-:表示左对齐。

4.3.7 序列化与反序列化

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

ObjectOutputStream类

将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法

public ObjectOutputStream(OutputStream out)		创建一个指定OutputStream的ObjectOutputStream。

举个例子,

FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);

一个对象要想序列化,必须满足两个条件:

  • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
public class Employee implements java.io.Serializable {
    public String name;
    public String address;
    public transient int age; // transient瞬态修饰成员,不会被序列化
    public void addressCheck() {
      	System.out.println("Address  check : " + name + " -- " + address);
    }
}

写出对象方法

public final void writeObject (Object obj): 将指定的对象写出。

public class SerializeDemo{
   	public static void main(String [] args)   {
    	Employee e = new Employee();
    	e.name = "zhangsan";
    	e.address = "beiqinglu";
    	e.age = 20; 
    	try {
      		// 创建序列化流对象
          ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
        	// 写出对象
        	out.writeObject(e);
        	// 释放资源
        	out.close();
        	fileOut.close();
        	System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
        } catch(IOException i)   {
            i.printStackTrace();
        }
   	}
}
输出结果:
Serialized data is saved

ObjectInputStream类

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法

public ObjectInputStream(InputStream in)	创建一个指定InputStream的ObjectInputStream。

反序列化操作1
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

public final Object readObject () : 读取一个对象。
public class DeserializeDemo {
   public static void main(String [] args)   {
        Employee e = null;
        try {		
             // 创建反序列化流
             FileInputStream fileIn = new FileInputStream("employee.txt");
             ObjectInputStream in = new ObjectInputStream(fileIn);
             // 读取一个对象
             e = (Employee) in.readObject();
             // 释放资源
             in.close();
             fileIn.close();
        }catch(IOException i) {
             // 捕获其他异常
             i.printStackTrace();
             return;
        }catch(ClassNotFoundException c)  {
        	// 捕获类找不到异常
             System.out.println("Employee class not found");
             c.printStackTrace();
             return;
        }
        // 无异常,直接打印输出
        System.out.println("Name: " + e.name);	// zhangsan
        System.out.println("Address: " + e.address); // beiqinglu
        System.out.println("age: " + e.age); // 0
    }
}

对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。

反序列化操作2
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

public class Employee implements java.io.Serializable {
     // 加入序列版本号
     private static final long serialVersionUID = 1L;
     public String name;
     public String address;
     // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
     public int eid; 
     public void addressCheck() {
         System.out.println("Address  check : " + name + " -- " + address);
     }
}

4.3.8 随机存取文件类

RandomAccessFile类声明在java.io包下,但直接继承于java.lang.Object类。并且它实现了Datalnput、DataOutput这两个接口, 也就意味 着这个类既可以读也可以写。
RandomAccessFile类支持“随机访问"的方式,程序可以直接跳到文件的任意地方来读、写文件
支持只访问文件的部分内容
可以向已存在的文件后追加内容

RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile类对象可以自由移动记录指针:

long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到pos位置

构造

public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)

创建RandomAccessFile类实例需要指定一个mode参数,该参数指定RandomAccessFile的访问模式:

r:以只读方式打开
rW:打开以便读取和写入
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新

我们常用前面两个,
说明:
模式为只读r时,不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。
模式为rw读写时,如果文件不存在则会去创建文件,如果存在则不会创建。

主要方法:

方法 描述
RandomAccessFile(File f, String mode) 使用指定的File对象和模式创建随机文件流 (r/rw)
long getFilePointer() 返回以字节计算的文件偏移量(从文件开始计),作为下一个Reader或Writer的起点
long length() 返回文件中的字节数
setLength(long) 为文件设置新长度
int read() 从文件中读取字节数据
int read(byte[]) 从文件中读取字节数据
void seek(long pos) 设置流的偏移量(以字节为单位)
int skipBytes(int n) 跳过n个字节
write(byte[] b) 将指定的字节数组写入到文件中(从当前的文件指针开始写)

你可能感兴趣的:(Java)