本博客用于记录本人Java的学习过程,本人才疏学浅,如有错误,请在评论区指出
Java学习笔记(纯干货)(持续更新):
Java学习笔记01
Java学习笔记02
Java学习笔记03
异常是在程序运行过程中发生的异常事件。
在程序运行过程中,可能遇到两种错误,一种是致命错误,程序不能简单地恢复运行;另一种是非致命错误,这种错通过修正后程序可以继续运行,通常称为异常。
下面我们来看Java中常见的异常类。
名称 | 含义 |
---|---|
ArithmeticException |
算数异常 |
ArrayIndexOutOfBoundsException |
数组下标越界 |
ArrayStoreException |
数组元素赋值时类型不兼容 |
ClassCastException |
类型强制转换异常 |
ClassNotFoundException |
未找到相应类 |
EOFException |
文件已结束 |
FileNotFoundException |
文件未找到 |
IllegalAccessException |
访问某类被拒绝 |
InstantiationException |
使用newInstance()创建一个抽象类或接口实例时出现错误 |
IOException |
输入输出异常 |
NegativeArraySizeException |
负数组长度异常 |
NUllPointerException |
空对象异常 |
NumberFormatException |
字符串转化为数字异常 |
NoSuchFieldException |
字段未找到 |
NoSuchMethodException |
方法未找到 |
SecurityException |
小应用程序执行浏览器的安全设置禁止的动作时抛出的异常 |
SQLException |
操作数据库异常 |
StringIndexOutOfBoundsException |
字符串索引超出范围 |
在异常产生后如果不进行处理,程序则会被终止,为了保证程序运行,就需要我们处理异常。
Java异常处理机制:
异常的处理方法:
主要可以归纳为三种:
try-catch-finally
语句捕获异常。throw
声明抛出异常下面我们分别来看看这三种处理方式:
第一种
对于第一种,异常发生后,由java虚拟机自动处理异常,给出异常提示,并终止程序的运行。
对于这样的错误,我们有两种修改代码的方式:
第一种就是直接修改代码,找出出现错误的的语句,并将其修改正确。
另一种就是在不容易修改代码时,添加异常处理语句,即使用try-catch-finally
或throw
语句。下面我们就介绍这两种语句。
第二种
第二种就是使用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{};
在程序运行过程中可能出现系统不能识别的运行错误,这时,我们就需要自己创建异常和异常类,使系统能够识别这种错误并进行处理。
创建用户自定义异常时,需要完成以下工作:
集合(也称“容器”)是一系列对象的聚集,代表一组数据对象的一个对象,集合对象中每个元素称为集合的元素。集合中的每一个元素都具有一定的数据类型,任何数据类型的对象都可以存放在集合中,并提供了对集合及其集合元素的相关的操作方法。
Java常用集合中的接口、类之间的继承关系:
(1) Collection 接口是List接口和Set接口的父接口,通常情况下,不直接使用。
(2) List 接口实现了Collection 接口,List 接口允许存放重复的对象,按照对象插入的顺序排列。
(3) Set 接口实现了Collection 接口,Set 接口不允许存放重复的对象,按照自身内部的排列规则排列。
(4) Map接口以键值对(key-value)的形式存放对象,其中的键(key)对象不可以重复,值(value) 对象可以重复。
下面我们来详细介绍这些接口及其实现类的使用。
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接口。
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() ...
}
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() |
移除结尾的对象 |
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) {
}
}
Map不允许包含重复的键,键用来唯一标识键值对元素。
方法名 | 功能 |
---|---|
Object put(key, value) | 将一个键/值映射放入图中 |
Object get(key) | 根据键获取对应的value值 |
Set keySet() | 返回包含键的规则集 |
Collection values() | 返回包含值的集合 |
boolean containsKey(key) | 返回图中是否包含键值key |
Set |
返回一个图中包含条目的规则集 |
int size() | 返回图中的键值对个数 |
Object remove(key) | 删除指定键对应的条目 |
实例化:
Map<String, Integer> map = new HashMap<String, Integer>();
Map还有一个子接口 SortedMap(有序映射),包含的元素按照键来排序,方法与 SortedSet的很相似。常用的实现类有三个:HashMap、LinkedHashMap和TreeMap,很多特性和 set 的很像,这里就不再赘述。
创建 ArrayList 类型的集合对象
List<类型> list = new ArrayList<类型>();
上面的语句在接口和类后面跟的 <类型>
就被称为泛型。
泛型带来的最大好处是类型安全。通过指定集合所含元素的确切类型,程序在编译期就能由编译器检查出被添加的元素是否满足类型约束,而不会把错误延迟到运行期。
除键盘和显示器外,使用最广泛的输入输出设备就是磁盘,存储在磁盘上的文件称为磁盘文件。对文件中的数据进行加工处理,需要利用文件的路径确定文件,创建文件类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() |
设置为只读 |
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类型的异常,需要在程序中给出相应的异常处理。
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个基本的FileXxx流的增强。
基本原理:在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
字节缓冲流:BufferedInputStream
,BufferedOutputStream
字符缓冲流:BufferedReader
,BufferedWriter
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 使用步骤:
BufferedOutputStream使用步骤
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使用步骤:
BufferedWriter使用步骤:
转换流是字节与字符间的桥梁。
字符编码
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照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();
}
}
在键盘输入、显示器显示信息,通常称为标准输入输出或者控制台输入输出。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) %-:表示左对齐。
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);
}
}
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) | 将指定的字节数组写入到文件中(从当前的文件指针开始写) |