1.关键字
1.static
static 关键字
static 关键字是静态的意思,是java中的一个修饰符,可以修饰成员方法,成员变量
- 被 static修饰的成员变量,,一般叫做静态变量
- 被 static修饰的成员方法,一般叫做静态方法
static 修饰的特点
- 被类的所有对象共享
- 随着类的加载儿加载,优先于对象存在
- 可以通过类名调用(推荐使用类名调用)
- 静态方法中,只能访问静态成员(成员变量,成员方法)
- 非静态方法中,可以使用静态成员,也可以使用非静态成员
- 静态方法中,没有 this 关键字
2.extends
继承概述
让类与类之间产生关系(父子类关系),子类可以直接使用父类中非私有成员
在子类中访问一个变量:
- 首先在子类局部范围内找
- 其次在子类成员范围内找
- 最后在父类的成员范围找
- 注意:如果子父类中,出现了重名的成员变量,通过就近原则,会优先使用子类的,如果一定要使用父类的,可以通过 super 关键字进行区分
this
:代表对本来对象的引用
super
:代表父类存储空间的表示 (可以理解为父类对象引用)
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 == 访问本类成员变量 | this.成员方法(…) ==访问本类成员方法 | this(…)==访问本类构造方法 |
super | super.成员变量== 访问父类成员变量 | super.成员方法(…) ==访问父类成员方法 | super(…)==访问父类构造方法 |
方法重写的应用场景:
当子类需要父类的功能,而功能主体子类有自己特有内容
可以重写父类中的方法,这样,既沿用父类的功能,有定义了子类的特有内容
注意:
方法重写:在继承体系中,子类出现了和父类一模一样的方法声明(方法名,参数列表,返回值类型)
方法重载:在同一个类中,方法名相同,参数列表不同,与返回值无关
方法重写注意事项:
- 父类中私有方法不能被重写
- 父类静态方法,子类必须通过静态方法进行重写,父类非静态方法,子类也必须通过非静态方法进行重写
注意:静态方法不能被重写,如果子类中,也存在一个方法声明一模一样的方法
可以理解为,子类将父类中同名的方法,隐藏了起来,并非是方法重写 - 子类重写父类方法时,访问权限必须大于等于父类;
继承中构造方法的访问特点:
子类中所有的构造方法默认都会访问父类中无参的构造方法,并且子类不能继承父类的构造方法,只能使用父类的构造方法.
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的变量。所以子类初始化之前,一定要先完成父类初始化
怎么初始化? - 构造方法的第一条语句默认都是:super()
注意:如果我们编写的类,没有手动指定分类,系统也会自动继承Object( java继承体系中的最顶层父类)
继承中构造方法的访问特点
如果父类中没有空参构造方法,只有带参构造方法,会出现什么情况?
1.子类通过 super ,手动调用父类的带参的构造方法
2.子类通过 this 去调用本类的其他构造方法,本类其他构造方法在通过 super 去手动调用父类的带参的构造方法
注意: this(…) super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
总结:
子类中所有的构造方法,默认都会通过 super() 访问父类中无参的构造方法
每个子类构造方法的第一条与家居默认都是 super()
this(…) super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
3. synchronized
修饰对象
-
修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。
-
修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
-
修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
-
修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
synchronized修饰符本身的特性:
- synchronized关键字不能被继承 即父类方法是同步方法 子类方法继承后默认不是同步方法
- synchronized不能修饰接口方法 因为接口是特殊的抽象类 不能新建实例 实例锁应归实现其的类所有
- synchronized不能修饰构造方法(但可在内部使用synchronized代码块来同步
4.volatile
1.堆内存是唯一的,每一个线程都有自己的线程栈
2.每一个线程在使用堆里面的变量的时候,都会先拷贝一份到变量的副本中
3.在线程中,每一次使用的是从变量的副本中获取的
voiatile 可以强制线程在每次使用的该值的时候都会看一下共享区最新的值
5.final
final 关键字是最终的意思,可以修饰(方法,变量,类)
final 修饰的特点:
1.修饰方法:表明该方法是最终方法,不能被重写
2.修饰变量:表明该变量是常量,不能再次被赋值
3.修饰类:表名该类是最终类,不能被继承
final 修饰变量:
- 基本数据类型变量:其值不能被更改
- 引用数据类型变量:地址值不能被更改,但是可以修改对象的属性值
final 修饰成员变量初始化时机:
1.在创建的时候直接给值
2.在构造方法结束之前完成赋值
6.原子性
所谓原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断。要么所有的操作不执行,多个操作是一个不可分割的整体。
count++不是一个原子性操作,所以在执行过程中,有可能被其他线程打断操作
volatile 关键字:只能保证线程每次在使用共享数据的时候是最新值,但是不能保证原子性
原子类(常用 还有其他的)
public AtomicInteger(): 知识化一个默认值为0的原子型Integer
public AtomicInteger(): 知识化一个默认值为0的原子型Integer
//常用方法
int get(); 获取值
int getAndIncrement() 以原子方式将当前值+1 这里返回的是自增前的值
int incrementAndGet() 以原子方式将当前值+1 这里返回的是自增后的值
int addAndGet(int data ) 以原子方式将输入的数值与实例中的值相加,并返回结果
int getAndAdd(int data ) 以原子方式将输入的数值与实例中的值相加,并返回旧值
AtomicInteger原理
自旋锁+CAS算法
CAS算法 :有三个操作数(内存值V,旧的预期值A,要修改的值B)
当旧的预期值A== 内存值 此时修改成功
当旧的预期值A !=内存值 此时修改失败,不做任何操作
并重新获取现在的最新知(这个重新获取的动作就是自旋)
在修改共享数据时,把原来的值记录下来,如果现在内存中的值跟原来的旧值一样,证明没有其他线程操作过内存值,则修改成功
如果现在内存中的值跟原来的旧值不一样了,证明已经有其他线程操作过该数据了,所以修改失败,需要重新获取现在最新的值,再次进行操作,这个重新获取就是自旋
synchronized 和 CAS 的区别
相同点:在多线程的情况下,都可以保证共享数据的安全性
不同点: synchronized 总是从最坏的角度出发,认为每次获取数据时,别人都有可能修改,所以在每次共享操作数据之前都会上锁(悲观锁)
cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁,只不过在修改共享数据时,会检查一下,别人有没有修改过这个数据如果别人修改过,那么重新获取值,如果没有修改,则直接修改共享数据的值
7.泛型
泛型
泛型可以使用的地方
类后面 泛型类
方法声明上 泛型方法
接口后面 泛型接口
//泛型的定义格式
1.<类型>: 指定一种类型的格式
尖括号里面可以任意书写,按照变量的定义规则即可,一般只写一个字母
比如:<E><T><Q><M>
2.<类型,类型2....>:指定多种类型的格式,多种类型之间用逗号隔开
比如:<E,T><Q.M><K,V>
//泛型类的定义格式:
1.格式:修饰符 class 类名<类型>{}
2.范例:public class Generic<T>{}
此处T 可以随便写为任意标识,常见如T,E,K,V 等形式参数常用语表示泛型
//泛型方法
1.格式:修饰符<类型> 返回值类型 方法名(类型 变量名){}
2.范例:public <T> void show(T t){}
//泛型接口
1.实现类也不给泛型
2.实现类确定具体的数据类型
泛型接口的定义格式
格式:修饰符 interfaca 接口名<类型>{}
范例:public interface Generic<T>{}
//类型通配符
1.类型通配符:<?>
2.ArrayList<?>:表示元素类型位置的ArrayList,它的元素可以匹配任何类型
3.但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
4.类型通配符上限<? extends类型 > 定义了上限
比如:ArrayList< ? extends Number>: 他表示的类型是Number 或者其子类型
6.类型通配符下限:< ?super类型 > 定义了下限
比如:ArrayList< ? super Number>: 他表示的类型是Number 或者其父类型
Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。理解类型擦除对于用好泛型是很有帮助的,尤其是一些看起来“疑难杂症”的问题,弄明白了类型擦除也就迎刃而解了。
泛型的类型擦除原则是:
- 消除类型参数声明,即删除
<>
及其包围的部分。 - 根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)。
- 为了保证类型安全,必要时插入强制类型转换代码。
- 自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”。
那么如何进行擦除的呢?
- 擦除类定义中的类型参数 - 无限制类型擦除
当类定义中的类型参数没有任何限制时,在类型擦除中直接被替换为Object,即形如<T>
和<?>
的类型参数都被替换为Object。
- 擦除类定义中的类型参数 - 有限制类型擦除
当类定义中的类型参数存在限制(上下界)时,在类型擦除中替换为类型参数的上界或者下界,比如形如<T extends Number>
和<? extends Number>
的类型参数被替换为Number
,<? super Number>
被替换为Object。
- 擦除方法定义中的类型参数
擦除方法定义中的类型参数原则和擦除类定义中的类型参数是一样的,这里仅以擦除方法定义中的有限制类型参数为例。
枚举
用常量表示季节的弊端
1.代码不够简洁
2.不同类型的数据是通过名字区分的
3.不安全,由于是数字类型,就有可能使用这几个值做一些运算操作
枚举:是指将变量的值一一列出,变量的值只限于列举出来的值的范围内
//格式:
public enum s{
枚举项1,枚举项2,枚举项3;
}
定义枚举类要用关键字 enum
特点:
1.所有定义的枚举类都是Enum的子类
2.我们可以通过"枚举类名.枚举项名称"区访问指定的枚举项
3.每一个枚举项就是该枚举的一个对象
4.枚举也是一个类,可以定义成员变量
5.枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类中有其他东西,那么该分号不可省略
6.枚举可以有构造器,但必须是private的,它默认的也是private,枚举项的用法比较特殊:枚举(“”)
7.枚举类也可以有抽象方法,到那时枚举项必须重写该方法(因为每个枚举项都是新对象)
枚举方法
方法名 | 说明 |
---|---|
String name() | 获取枚举项的名称 |
int ordinal() | 返枚举项在枚举类中的索引值(从0开始) |
int compareTo(E o) | 比较两个枚举项,返回的是索引值的差值 |
String toString | 返回枚举常量的名称 |
static T valueOf(Class type,String name) | 获取指定枚举类中指定名称的枚举值 |
enum[] values() | 获得所有的枚举项 |
8.注解
注解的主要作用:对我们的程序今昔标注和解释
注解名 说明
@Override 描述字类重写父类的方法
@Deprecated 描述方法已过时
@SuppressWarnings 压制警告
自定义注解格式
注解类
public @interface 注解名称{
public 属性类型 属性名 () default 默认值;
}
//例
public @interface Test{
public int a () defau 23;
}
属性类型可以为
- 基本数据类型变型
- String
- Class 文件(字节码文件)
- 注解
- 枚举
- 以上类型的一维数组
注解中只能有属性
注解不是接口,只能被继承,不能被实现
value ,后期我们使用注解的时候,如果我们只需要给注解的value属性赋值
那么value可以省略
元注解 (描述注解的注解)
@Target 制定了注解能在哪里使用(成员方法,类,)
@Retention 可以理解为保留时间(生命周期)
@Inherited 表示修饰的自定义注解可以被子类继承(但是使用declearAnnotations方法时,这个继承并不能继承父类中的元注解)
@Document 表示该自定义注解,会出现在Api文档中
利用注解简化配置文件步骤
1.定义一个注解@WebServlet ,注解内有一个属性 urlpatterns
2.在servlet类上去使用该注解,来指定当前的servlet的访问路径
3.创建一个注解解析类(AnnoParseServletConfig),该类实现ParseServletConfig接口
4.实现parse方法
注解实现原理
1.Class Filed Method Constructor 等都实现了AnnotatedElement 接口, 实现类该接口的类都是可以获取注解。
2.Class 等类 Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。一个类被加载到内存并供我们使用需要经历如下三个阶段:
加载,这是由类加载器(ClassLoader)执行的。通过一个类的全限定名来获取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口,根据字节码在java堆中生成一个代表这个类的java.lang.Class对象。
链接。在链接阶段将验证Class文件中的字节流包含的信息是否符合当前虚拟机的要求,为静态域分配存储空间并设置类变量的初始值(默认的零值),并且如果必需的话,将常量池中的符号引用转化为直接引用。
初始化。到了此阶段,才真正开始执行类中定义的java程序代码。用于执行该类的静态初始器和静态初始块,如果该类有父类的话,则优先对其父类进行初始化。java有两种对象:实例对象和Class对象。每个类的运行时的类型信息就是用Class对象表示的。它包含了与类有关的信息。
其实我们的实例对象就通过Class对象来创建的。Java使用Class对象执行其RTTI(运行时类型识别,Run-Time Type Identification),多态是基于RTTI实现的3.所以每一个备加载进入jvm的类的所有信息都会保存在对应的Class类中。又因为Class 实现AnnotatedElement,所以可以从其中获取类上的注解。
4.获取到每一个注解后,就可以根据注解上的信息进行对应的处理
注意:注解对象其实是一个接口,但是接口不能实例化,所以这里用到了jdk动态代理,生成了一个代理对象,就是能拿到注解对象。
那么分析流程
java.lang.Class#getDeclaredAnnotation 第一步 获取注解
java.lang.Class#createAnnotationData 第二步 创建注解实例
sun.reflect.annotation.AnnotationParser#parseAnnotations 第三步 解析注解 sun.reflect.annotation.AnnotationParser#annotationForMap 第四步 生成代理对象
9.抽象
//抽象类的定义格式:
public abstract class 类名[]
//抽象方法的定义格式:
public abstract 返回值类型方法名(参数列表)
抽象类注意事项:
1.抽象类不能实例化
2.抽象类中不一定有有抽象方法,有抽象方法的类一定是抽象类
3.可以有构造方法
4.抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
10.代码块概述与分类
在java中,使用{}括起来的代码被称为代码块
分类:
1.局部代码块
位置:方法中定义
作用:限定变量的生命周期,及早释放,提高内存利用率
2.构造代码块
位置:类中方法外定义
特点:每次构造方法执行的时候,都会执行该代码块中的代码,并且在构造方法执行前执行
作用:将多个构造方法中相同的代码,抽取到构造代码块,提高代码的复用性3.静态代码块
位置: 类中方法外定义
特点:需要通过 static 关键字修饰,随着类的加载儿加载,并且执行一次。
作用:在类加载的时候做一些数据的初始化的操作.
11.接口
当一个类中所有方法都是抽象方法时,我们可以将其定义为接口(可以理解为制定规则)
接口是一种引用数据类型,他比抽象还抽象
接口存在的两个重要意义
1.规则的定义
2.程序的扩展性
接口用关键字 interface 来定义
1.public interface 接口名{}
2.接口不能实例化(不能创建对象)
3.接口和类之间是实现关系,通过 implements 关键字来表示
public class 类名 implements 接口名 {}4.接口的子类(实现类)
要么重写接口中的所有抽象方法
要么是抽象类
注意:接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口1, 接口2{}
(如果两个接口中定义了同样的方法名,但都没有实现)因为接口中的方法全都没有实际意义,且所有方法都须重写,就不会存在逻辑冲突问题。
接口中成员的特点
1.成员变量
只能是常量
默认修饰符 public static final
因为是被静态修饰符定义,所以只能用类名加方法定义2.构造方法
没有构造方法3.成员方法
只能是抽象方法
默认修饰符 public abstract
在jdk8,jdk9 添加了一些新特性,所以成员方法不再只能是抽象方法
jdk8版本后
一.允许在接口中定义非抽象方法,但是需要使用关键字 default 来修饰 ,这些方法称为默认方法
作用:主要是解决接口升级的问题
不需要将每一个实现类中的代码都进行更新,只需更新接口中的默认方法即可将这些方法也应用于实现类中
格式:
public default 返回值类型 方法名(参数列表) {}
接口中默认方法的注意事项:
1.默认方法不是抽象方法,所以不被强制重写,但是可以被重写,重写时须去掉 default 关键字
2.public 可以省略,但是 default 不能省略
3.如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法重写
二. 接口中允许定义 static 静态方法
接口中静态方法的定义格式:
1.public static 返回值类型 方法名 (参数列表){}
接口中静态方法的注意事项:
1.静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
2.public 可以省略 ,但是 static 不可以省略
jdk9版本后
接口可以定义私有方法
定义格式1:
private 返回值类型 方法名 (参数列表){}
定义格式2:
private static 返回值类型 方法名 (参数列表){}
接口的使用思路
1.当一个类中所有方法都是抽象方法时,我们可以将其定义为接口
2.涉及到了接口大面积更新方法,而不想去修改每个实现类,就可以将更新方法,定义为带有方法体的默认方法
3.希望默认方法调用的更加简洁 ,可以考虑涉及为 static 静态方法 ( 需要去掉 default 关键字)
4.默认方法中出现了重复的代码,可以考虑抽取出一个私有方法 ( 需要去掉 default 关键字)
类和接口的关系
1.类和类的关系
继承关系,只能单继承,但是可以多层继承
2.类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
3.接口和接口的关系
继承关系,可以单继承,也可以多继承
12.多态
一.多态的前提
1.要有(继承\实现)关系
2.要有方法重写(可以没有)
3.要有父类引用,指向子类对象
二. 多态中成员访问特点
1.构造方法:同继承一样,子类会通过 super() 访问父类的构造方法
2.成员变量 :编译看左边(父类), 执行看左边(父类)
3.成员方法: 编译看左边(父类),执行看右边(子类)
为什么成员变量和成员方法的访问不一样?
因为成员方法有重写,而成员变量没有
三.多态的好处和弊端
1.多态的好处:提高了程序的扩展性
具体体现:定义方法的时候,使用父类作为参数,该方法就可以接受这边父类的任意子类对象
2.多态的弊端:不能使用子类的特有功能
四.多态转型
1.向上转型:父类引用指向子类对象
Fu f = new zi();
多态的弊端:不能调用子类的特有成员
解决方法:
A:直接创建子类对象
B:向下转型
向下转型:从父类类型,转换为子类类型
Zi z = (zi) f
类型检查 : 当前对象 instanceof 类 。可以知道当前对象是不是某个类对象
五.多态的三种写法
1.标准写法 :父类类型指向子类对象 父类类型 变量名 = 子类对象
2.引申写法 :间接父类类型指向子类对象 间接父类类型 变量名 = 子类对象
3.引申写法 :接口类型指向实现类对象 接口数据类型 变量名 = 实现类对象
13.内部类
一. 内部类概述:就是在一个类中定义一个类
内部类访问特点:
1.内部类可以直接访问外部类的成员,包括私有
2.外部类要访问内部类的成员必须创建对象
二.成员内部类
按照内部类在类中定义的位置不同,可以分为如下两种形式
1.在类的成员位置:成员内部类
2.在类的局部位置(在方法内):局部内部类
成员内部类,外界如何创建对象使用呢?
格式: 外部类名.内部类名 对象名 = new 外部类对象().new 内部对象();
示例: Outer.Inner i = new Outer().new Inner();
三. 静态成员内部类使用
格式: 外部类名.内部类名 对象名 = new 外部类名.内部类名();
示例:Outer.Inner i = new Outer. Inner();
四 .局部内部类
局部内部类是在方法中定义的类,所以外界是无法直接使用的,需要在方法内部创建对象并使用
该类可以直接访问外部类成员,也可以访问方法内的局部变量
五.匿名内部类
格式: new 类名\接口名(){
重写方法;
}
匿名内部类的理解:将继承\实现,方法重写,创建对象,都放在一步进行。
解释:实现了 Inter 接口的,一个实现类对象
如果接口中存在多个方法,则需将这些方法都重写
匿名内部类在开发中的使用:当方法的形式参数是接口或者抽象类是,可以将匿名内部类作为实际参数进行传递
Lambda表达式:
可以理解为是对匿名内部类的优化
格式:() ->{}
():可以理解为方法的形式参数为空
->:用箭头指向后面要做的事情
{}:包含一段代码,可以看成是方法体中的内容
使用前提
1.有一个接口
2.接口中有且只有一个抽象方法
省略模式
1.参数类型可以省略,但是在有多个参数的情况下,不能只省略一个参数类型
2.如果参数有且只有一个,那么小括号可以省略
3.如果代码块的语句中有一条,可以省略大括号分号,甚至是return(必须一起省略)
Lambda表达式和匿名内部类的区别
1.所需类型不同
1.匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
2.Lambda表达式:只能是接口
使用限制不同
1.如果接口中有且皆有一个抽象方法,那么两中都可以使用
2.如果接口中抽象方法多于一个,那么只能使用匿名内部类
实现原理不同
1.匿名内部类:编译之后,产生一个单独的.class字节码文件
2.Lambda表达式:编译之后,没有一个单独的.class文件字节码文件,对应的字节码会在运行的时候动态生成
14.异常
异常:
就是程序中出现了不正常的情况,程序在执行中,出现的非正常情况最终会导致jvm非正常停止
注意:语法错误不算在异常体系内
异常体系
1、Error与Exception
Error是程序无法处理的错误,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
2、运行时异常和非运行时异常
运行时异常都是RuntimeException类及其子类异常:
如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。非运行时异常:
是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
Exception :
称为异常类,他表示程序本身可以处理的问题
1.RuntimeException及其子类:运行时异常(空指针异常,数组索引异常)
2.除 RuntimeException 之外的所有异常:编译期必须处理的,否则程序无法通过编译(日期格式化异常)
java文件通过javac.exe 变成class文件 属于编译期间 编译时异常,也称为受检异常
class文件通过java.exe 得出运行结果,属于运行期间,此时异常又称为非受检异常
三.运行时jvm处理异常过程
当代码出现了异常,那么就在那行代码那里创建了一个异常对象,
java会首先看,程序中有没有自己处理异常的代码.
如果没有,交给本方法的调用者处理,最终这个异常会交给虚拟机默认处理
jvm默认处理做了几件事
1.将异常信息以红色字体展示在控制台上,
2.停止程序运行 — 哪里出现了异常就在那里停止,并且下面代码不再执行
四.异常处理方式
throws 处理方式
1.格式 throws 异常类名;
这个格式是写在 方法的定义 处,表示声明一个异常
作用:告诉调用者,你调用此方法可能会出现这样的异常
如果没有出现异常,则正常执行,如果出现异常,将此异常交给调用者处理,
如果该异常是一个运行时异常,那么可以省略不写
throw 处理方式
1.格式 throw new 异常对象;
这个格式是写在方法体内的
当代码执行到这里,就会创建一个异常对象
该异常创建之后,暂时没有手动处理,那么就会抛给调用者处理
并且下面的代码不会在执行
throws 和 throw 的区别
throws | throw |
---|---|
用在方法声明后,跟的是异常类名 | 用在方法体内,跟的是异常对象名 |
表示声明异常,调用该方法有可能会出现这样的异常 | 表示手动抛出异常对象,由方法体内语句处理 |
抛出处理的意义
1.在方法中,当传递的参数有误,没有继续运行下去的必要了,则采取抛出处理,表示让该方法结束运行
2.告诉调用者方法中出现了问题
try{
}catch(异常类 变量){
}
//try{中跟有可能出现异常的代码}
//catch(异常类 变量){ 如果出现了这样的异常,我们需要进行的操作}可以用多个catch捕捉多个异常
1.如果try中没有出现异常,怎么执行?
会把try中的所有代码执行完毕,不会执行catch里的代码
2.如果try中遇到了问题,那么try下面的代码还会执行吗?
那么直接跳转到对应的catch中,try下面的代码就不会在执行了
当catch里面的语句全部执行完毕,表示整个体系全部执行了,继续执行体系外下面的代码
3.如果出现的问题没有被捕获,那么程序如何运行?
那么try…catch语句就相当于没有写,默认交给虚拟机执行
4.同时出现多个异常怎么处理?
出现多个异常,那么写多个catch就可以了
注意点:如果多个异常之间存在子父类关系,那么父类的catch一定要在下面,否则捕捉不到子类的错误
Throwable 的成员方法
所有异常对象都可调用
方法名 | 说明 |
---|---|
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public String toString() | 返回此可能抛出的简短描述 |
public void printStackTrace() | 把异常信息输出在控制台(字体为红色,与jvm给出的一致,但是可以执行下面的代码) |
两种处理方式小结
抛出处理的意义
1.在方法中,当传递的参数有误,没有继续运行下去的必要了,则采取抛出处理,表示让该方法结束运行
2.告诉调用者方法中出现了问题
捕获try…catch
捕获:为了让代码继续运行下去
五.自定义异常
1.定义异常类
2.写继承关系 写两个构造方法即可
public class 某个异常 extends RuntimeException{{
public 某个异常{(){}
public 某个异常(String Message ){
super(Message);
}
}
``