3.枚举
3.1枚举的基本定义和使用
3.1.1枚举的定义
定义枚举类型, 需要使用到关键字 enum 。 枚举的名字是一个标识符, 遵循大驼峰命名 法。
//解释枚举原理
public class Demo1{
public static void main(String[] args){
Dog dog=Dog.female;
}
}
class Dog{
final static Dog male = new Dog();
final static Dog female = new Dog();
}
enum Gender{
male,femalex
}
3.1.2枚举的使用
枚举是一种自定义的数据类型,可以声明变量。在使用的时候,直接使用枚举类型.枚举值这样的形式进行枚举值的获取。
3.2枚举中的成员定义
3.2.1枚举的分析
枚举,其实可以认为是Object类的一个最终子类。 不能被其他的类、枚举继承。
3.2.2枚举中的属性定义
public enum Gender {
Male, Female;
// 1. 在枚举中定义属性、方法、构造方法... 是需要写在枚举元素的下方!
// 如果需要在枚举中定义成员,需要在最后一个枚举元素后面添加一个分号。
public String desc;
}
3.2.3枚举中的构造方法定义
public class Test2 {
// 为了防止和当前包中的Gender枚举重复,在这里写成了静态内部枚举
private static enum Gender {
// 其实,所谓枚举中的元素,其实就是一个静态的、当前类的对象。
Male("男"), Female("女");
// 添加属性
private String desc;
// 添加构造方法,为这个属性赋值
// 在枚举中定义构造方法,一般情况下,只是在当前的枚举中使用
// 所以,枚举的构造方法,一般情况下,权限都是私有的
Gender(String desc) {
this.desc = desc;
}
}
public static void main(String[] args) {
// 1. 枚举对象的获取
Gender gender = Gender.Male;
}
}
3.2.4枚举中的方法定义
public enum Gender {
Male, Female;
// 1. 在枚举中定义属性、方法、构造方法... 是需要写在枚举元素的下方!
// 如果需要在枚举中定义成员,需要在最后一个枚举元素后面添加一个分号。
public String desc;
// 2. 定义方法
public void show() {
System.out.println("枚举中的方法定义");
}
public static void display() {
System.out.println("枚举中的静态方法的定义");
}
}
3.2.5枚举中的方法重写
private static enum Gender {
// 其实,所谓枚举中的元素,其实就是一个静态的、当前类的对象。
Male("男"), Female("女");
// 添加属性
private String desc;
// 添加构造方法,为这个属性赋值
// 在枚举中定义构造方法,一般情况下,只是在当前的枚举中使用
// 所以,枚举的构造方法,一般情况下,权限都是私有的
Gender(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return this.desc;
}
}
3.2.6枚举实现接口
enum Gender implements MyInterface {
@Override
public void test() {
System.out.println("接口中的方法");
}
}
interface MyInterface {
void test();
}
3.2.7枚举值
enum Gender implements MyInterface {
// 其实,所谓枚举中的元素,其实就是一个静态的、当前类的对象。
Male("男") {
// 这里,其实就相当于是一个匿名内部类
// 在这里,可以重写Gender中的方法
@Override
public void test() {
System.out.println("Male的重写实现");
}
},Female("女");
// 添加属性
private String desc;
// 添加构造方法,为这个属性赋值
// 在枚举中定义构造方法,一般情况下,只是在当前的枚举中使用
// 所以,枚举的构造方法,一般情况下,权限都是私有的
Gender(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return this.desc;
}
@Override
public void test() {
System.out.println("接口中的方法");
}
}
4.异常
4.1异常的结构和分类
4.1.1异常的结构
在Java中,用 Throwable 类来描述所有的不正常的情况。Throwable 有两个子类: Exception 和 Error 。
Error: 描述发生在JVM虚拟机级别的错误信息, 这些错误无法被处理, 不做为现在的重点。
StackOverflowError: 栈溢出错误
Exception: 描述程序遇到的异常。 异常是可以被捕获处理的, 是现在考虑的重点内容。
未经处理的异常,会导致程序无法进行编译或者运行。 因此在异常部分, 重点内容是:
异常该如何捕获处理。
Java中的异常的继承体系:
根类: Throwable
错误: Error
异常: Exception
运行时异常: RuntimeException
4.1.2异常的分类
-
根据异常发生的位置
第一:普通的异常,会导致程序无法完成编译。 这样的异常被称为 -- 编译时异常。 (NonRuntime Exception: 非运行时异常,但是由于异常是发生在编译时期的,因此,常常称为编 译时异常。)
第二:Exception有一个子类-RuntimeException类,在这个类中,对异常进行了自动的处 理 这种异常不会影响程序的编译,但是在运行中如果遇到了这种异常 会导致程序执行的强制停止。这样的异常被称为 -- 运行时异常。
-
根据创建异常类的主体
第一:系统异常,系统提前定义好的,我们直接使用
第二:自定义异常,需要我们自己定义.
4.1.3异常的工作原理
public class Demo7 {
public static void main(String[] args) {//4
Math math = new Math();
math.div(3,0);//3
}
}
class Math{
public int div(int a,int b){//2
return a/b;//1
}
}
原理说明
-
1.在异常最初发生的位置创建一个异常的对象(new ArithmeticException()) 因为这里没有处理异常的能力,所以会将异常往上抛,抛给他所在的方法
-
2.div()方法也没有处理异常的能力,所以会继续往上抛,抛给调用这个方法的位置
-
3.调用div()方法的位置也没有处理异常的能力,所以会继续往上抛,抛给他所在的方法
-
4.main方法也没有处理异常的能力,所以会继续往上抛,抛给JVM,JVM会调用异常对象的打印方法,将异常信息打印到控制台
4.1.4异常的特点
程序出现异常的时候,会打印异常的信息并中断程序,所以当有多个异常同时出现的时候,默认只 能执行第一个。
4.2异常的捕获处理
4.2.1try-catch
如果一个异常不去处理, 会导致程序无法编译或者运行。
try {
// 将可能出现异常的代码写到这里
// 如果这里的代码出现了异常,从出现异常的位置开始,到try代码段结束,所有的代码不执行。
}
catch (异常类型 标识符) {//捕获异常
// 如果try中的代码出现了异常,并且异常的类型和catch的异常的类型是可以匹配上的,就会执行这里的逻辑
}
catch会对try里面的代码进行监听,如果try里面的代码没有发生异常,catch不会执行,会直接执 行后面的代码.如果try里面的代码发生了异常,catch会立刻捕获(效果:try里面的代码会立刻中 断,直接执行catch)。
注意:
catch中捕获的异常类型, 一定要和try中实际出现的异常类型一致。 否则将捕获不到异常, 会导致try中实际出现的异常没有被捕获处理, 依然可以终止程序的编译或运行。
4.2.2多个catch子句
-
使用场景
如果在try代码段中, 出现了多种类型的异常, 此时如果需要对这些异常进行不同的处理, 可以写多个catch子句。
在实际使用中:
·如果要针对不同的异常,进行不同的处理,可以用多个catch。
·如果要针对每一种异常,进行的处理方式相同,直接catch父类异常即可。
-
语法
try{
可能发生异常的代码
}catch(异常一 e){ //捕获异常 e就是要捕获的异常
对当前异常的处理
}catch(异常二 e){ //捕获异常 e就是要捕获的异常
对当前异常的处理
}catch(Exception e){ //捕获异常 e就是要捕获的异常
对当前异常的处理
}
go on
-
注意事项
多个catch书写的先后顺序, 对异常捕获有影响吗?
- 如果多个catch捕获的异常类型之间, 没有继承关系存在, 此时先后顺序无所谓。
- 如果多个catch捕获的异常类型之间, 存在继承关系, 则必须保证父类异常在后, 子类异
常在前。
4.2.3一个catch捕获多种异常
-
使用场景
如果try中出现了多种异常,并且某些类型的异常,处理方式相同。 并且与其他类型的处理方 式不同。 此时, 可以使用一个catch捕获处理多种异常。区分一个catch里面的多个异常时通 过instanceof.
缺点:这种分类方式代码很混乱,所以不推荐
-
注意事项
在一个catch子句中捕获的多种类型的异常中,不允许出现有继承关系的异常类型.
4.2.4 final子句
-
概念
finally出现在try-catch子句的结尾, finally代码段中的内容, 始终会执行。
-
语法
try{
可能发生异常的代码
}catch(Exception e){ //捕获异常 e就是要捕获的异常
对当前异常的处理
}finally{
必须执行的代码:主要用于资源的释放:比如关闭数据库,关闭流,关闭锁等
}
-
特点
无论try代码段中有没有异常出现,无论try里面出现的异常没有被捕获处理,finally中的代码 始终会执行。基于这个特点,常常在finally中进行资源释放、流的关闭等操作
-
作用范围
我们发现在catch中执行return后main方法结束,finally还能正常执行
catch中执行System.exit(0)后,程序退出,finally不能执行了
结论:只要当前的程序在运行,finally代码就能执行.
4.2.5 try-finally语句
-
语法
try{
获取资源
}finally{
释放资源
}
-
特点
这个结构跟异常没有关系,主要用于资源的管理.
4.3两个关键字
4.3.1throw
-
概念/使用场景
一个异常对象,被实例化完成后,没有任何意义。不会影响到程序的编译或者运行。
如果希望某一个异常对象生效(可以影响程序的编译、运行),需要使用关键字 throw 进行
异常的抛出。
4.3.2throws
-
概念/使用场景
用在方法的声明部分,写在参数列表后面,方法体前面。
定义在方法中,表示这个方法过程结束中,可能会遇到什么异常。
定义了throws异常抛出类型的方法,在当前的方法中,可以不处理这个异常,由调用方处理。
4.4自定义异常
4.4.1为什么要自定义异常
使用异常, 是为了处理一些重大的逻辑BUG。这些逻辑BUG可能会导致程序的崩溃。此 时,可以使用异常机制,强迫修改这个BUG。
系统中,提供很多很多的异常类型。但是, 异常类型提供的再多,也无法满足我们所有的需求。当我们需要的异常类型,系统没有提供的时候,此时我们就需要自定义异常了。
4.4.2如何自定义异常
其实,系统提供的每一种异常,都是一个类。所以,自定义异常,其实就是写一个自定义的 异常类。
-
如果要自定义一个编译时的异常,需要继承自 Exception 类。
特点:对异常进行处理的所有工作都要我们手动完成
-
如果要自定义一个运行时的异常,需要继承自 RuntimeException 类。
特点:所有的工作我们都可以不管
规范:
自定义的异常类, 理论上来讲, 类名可以任意定义。 但是出于规范, 一般都会以
Exception 作为结尾。
例如: ArrayIndexOutOfBoundsException、 NullPointerException、
ArithmeticException...
常见的异常:订单异常 用户异常 负数异常
4.4.3在重写方法中使用异常
注意点:
-
子类的同名方法中声明的异常等级要<=父类的.
-
如果子类同名方法声明了异常,父类必须声明异常.
-
父类抛出了异常,子类在重写方法的时候,可以不抛出异常
如果父类方法抛出的异常是编译时异常子类重写方法的时候,可以抛出相同的异常,或子类异常
子类重写方法的时候,可以抛出运行时异常
如果父类方法抛出的异常是运行时异常子类在重写的时候,可以是任意的运行时异常,不能是编译时异常
5.字符串
5.1字符串的分类
不可变字符串:
-
对应的类:String.
-
特点:字符串本身不能发生改变,与指向字符串的引用无关.
-
直接使用"",创建的是不可变字符串
可变字符串:
-
对应的类:StringBuilder/StringBuffer.
-
特点:字符串本身可以发生变化,与指向可变字符串的引用无关
-
创建可变字符串 StringBuffer stringBuffer = new StringBuffer("hello world");
5.2字符串的常用方法
5.2.1字符串的构造方法
5.2.2字符串的非静态方法
因为字符串, 是常量。 任何的修改字符串的操作, 都不会对所修改的字符串造成任何的影 响。 所有的对字符串的修改操作, 其实都是实例化了新的字符串对象。 在这个新的字符串 中, 存储了修改之后的结果。 并将这个新的字符串以返回值的形式返回。 所以, 如果需要 得到对一个字符串修改之后的结果, 需要接收方法的返回值。
-
常用的非静态方法
5.2.3字符串的静态方法
-
常用的静态方法
5.3 StringBuffer和StringBuilder类
5.3.1概念
都是用来操作字符串的类,我们成为可变字符串
字符串都是常量,所有的操作字符串的方法,都不能直接修改字符串本身。如果我们需要得到修改之后的结果,需要接收返回值。
StringBuffer和StringBuilder不是字符串类,是用来操作字符串的类。在类中维护了一个字 符串的属性,这些字符串操作类中的方法,可以直接修改这个属性的值。对于使用方来说,可以不去通过返回值获取操作的结果。
在StringBuffer或者StringBuilder类中,维护了一个字符数组。这些类中所有的操作方法,都是对这个字符数组进行的操作。
5.3.2常用方法
5.3.3区别
StringBuffer和StringBuilder从功能上来讲,是一模一样的。但是他们两者还是有区别的:
-
StringBuffer是线程安全的。
-
StringBuilder是线程不安全的。
使用场景:
-
当处于多线程的环境中, 多个线程同时操作这个对象, 此时使用StringBuffer。
-
当没有处于多线程环境中, 只有一个线程来操作这个对象, 此时使用StringBuilder。
5.3.4备注
但凡是涉及到字符串操作的使用场景, 特别是在循环中对字符串进行的操作。 一定不要使用 字符串的方法, 用StringBuffer或者StringBuilder的方法来做。
由于字符串本身是不可变的, 所以String类所有的修改操作,其实都是在方法内实例化了一 个新的字符串对象,存储拼接之后的新的字符串的地址,返回这个新的字符串。 如果操作比 较频繁,就意味着有大量的临时字符串被实例化、被销毁, 效率极低。 StringBuffer、StringBuilder不同,在内部维护了一个字符数组,所有的操作都是围绕这个字符数组进行的 操作。当需要转成字符串的时候,才会调用 toString() 方法进行转换。当频繁用到字符串操 作的时候,没有中间的临时的字符串出现,效率较高。