继承介绍
-
继承:让类与类之间产生关系(子父类关系),子类可以直接使用父类中非私有的成员,能够提高代码的复用性
-
什么时候使用继承:
-
当类与类之间,存在相同(共性)的内容,并且产生了is a 的关系,就可以考虑使用继承,来优化代码
-
继承的格式
-
格式:public class 子类名 extends 父类名{ }
-
范例:public class Zi extends Fu{ }
-
Fu:是父类,也被称为基类、超类
-
Zi:是子类,也被称为派生类
/*
继承:类与类之间产生关系(子父类关系),子类就可以直接使用父类中,非私有的成员
*/
public class ExtendsDemo1{
public static void main(String[] args){
Coder c=new Coder();
c.setName("张三");
c.setAge(24);
c.setSalary(12000);
System.out.println(c.getName()+"---"+c.getAge()+"---"+c.getSalary());
}
}
class Employee{
private String name;
private int age;
private double salary;
//getter、setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
class Coder extends Employee{
}
class Manager extends Employee{
}
继承中成员访问特点
成员变量
子父类中,如果出现了重名的成员变量,使用的时候会依据就近原则优先使用子 类的(使用父类则在变量名前+super.关键字;super:调用父类成员)
public class ExtendsDemo2{
public static void main(String[] args){
Zi z=new Zi();
z.method();
}
}
class Fu{
int num=50;
}
class Zi extends Fu{
int num=20;
public void method(){
int num=10;
System.out.println(num);//打印10
System.out.println(this.num);//打印20,this调用本类成员
System.out.println(super.num);//打印50,调用父类成员
}
}
成员方法
子父类中,出现了方法声明一模一样的方法(方法名,参数,返回值)
在创建子类对象,调用方法的时候,会优先使用子类的方法逻辑。
这虽然是就近原则的现象,但其实是子类的方法,对父类的方法进行了重写操作。
方法重写(Override)/方法覆盖
在子父类中,出现了方法声明一模一样的方法 (方法名,参数,返回值类型一致)
注解:@Override 能够识别出方法是否为重写的方法。
使用场景:当子类需要父类的方法,但是想对父类进行修改或增强,就可以采用方法重写。
快捷键:敲出需要重写的方法名按回车;右键,Generate,Override Methods选择对应的方法。
注意事项:父类中私有方法不能被重写;子类在重写父类方法时,访问权限必须大于等于父类(private<default<protected<public)
构造方法
-
构造方法不能被继承,子类需要自己手动编写构造方法
-
子类在初始化之前,需要提前完成父类的初始化
-
除Object类,在所有的构造方法中的第一行代码,都默认隐藏了一句话super();
通过这句代码,来访问父类的空参构造方法,完成父类的初始化。
细节:Java中所有的类,都直接或间接继承到了Object类(祖宗,最顶层的类)
继承的特点
-
Java只支持单继承,不支持多继承(多个爹),但支持多层继承(儿子的儿子)
this和super关键字
-
this:代表本类对象的引用
-
super:代表父类存储空间的表示
-
关键字 访问成员变量 访问成员变量 访问构造方法 this this.本类成员变量; this.本类成员方法(); this();this(...); 本类构造方法 super super.父类成员变量; super.父类成员方法(); super();super(...); 父类构造方法 -
开闭原则:对功能扩展做开放,对修改代码做关闭
-
注意:this()和super()都在争夺构造方法第一行的位置,所以二者不能共存。
final关键字
final关键字是最终的意思,可以修饰(方法,类,变量)
final修饰的特点
-
修饰方法:表明该方法是最终方法,不能被重写
-
修饰类:表明该类是最终类,不能被继承
-
修饰变量:表明该变量是常量,不能再次被赋值(如Π)
-
修饰基本数据类型:数据值不可改变
-
引用数据类型:地址值不可改变,但是内容可以改变
-
final修饰变量的命名规范:
如果变量名是一个单词,所有字母大写 max Max
如果变量名是多个单词,所有字母大写,中间使用下划线分割 maxValue Max_VALUE
-
final修饰成员变量的注意事项:
-
final修饰成员变量,不允许修饰默认值
-
final修饰成员变量的初始化时机
-
在定义的时候直接赋值
-
在构造方法中完成赋值
-
面向对象高级1
包
-
包本质来说就是文件夹,用来管理类文件的
-
建包的语法格式:package公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
-
建包语句必须在第一行,一般IDEA工具会帮助创建
导包
-
相同包下的类可以直接访问,不同包下的类必须导包,才可以使用。
导包格式:import包名.类名;
-
假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
//示例1
package com.itheima.a;
public class Student{
public void eat(){
System.out.println("学生吃饭");
}
}
package com.itheima.b;
public class Student{
public void sleep(){
System.out.println("学生睡觉");
}
}
package com.itheima.c;
import com.itheima.a.Student;//a包下的Student
public class Test{
public static void main(String[] args){
Student stu1=new Student();
stu1.eat();
//调用b包下的Student,防止与a包Student冲突
//使用全类名创建对象:包名+类名
com.ithema.b.Student stu2=new com.itheima.b.Student();
stu2.sleep();
}
}
//直接使用import java.util.Scanner
public class Scanner{
public static void main(String[] args){
//Scanner sc=new Scanner(System.in)会产生错误,有两个Scanner类
java.util.Scanner sc=new java.util.Scanner(System.in);
}
}
抽象类
抽象类是一种特殊的父类,内部可以编写抽象方法
如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象类的定义格式:public abstract 返回值类型 方法名(参数列表)
抽象方法
当我们将共性的方法,抽取到父类之后,发现这个方法在父类中无法给出具体明确(描述不清了),而且这个方法,还是子类必须要有的方法,就可以设计为抽象方法。
抽象方法的定义格式:public abstract class 类名{ }
public class AbstractTest1{
public static void main(String[] args){
}
}
//抽象(abstract)方法要在抽象类中编写
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal{
//Alt +回车,再回车点OK,抽象类的继承强制重写父类方法
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
public class AbstractTest1{
public static void main(String[] args){
}
}
//抽象(abstract)方法要在抽象类中编写
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal{
//Alt +回车,再回车点OK,抽象类的继承强制重写父类方法
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
public class InterfaceTest1{
public static void main(String[] args){
//创建实现类对象
InterImpl li=new InterImpl();
li.method();
li.show();
}
}
interface Inter{
public abstract void show();
public abstract void method();
}
class InterImpl implements Inter{
@Override
public void show() {
System.out.println("show...");
}
@Override
public void method() {
System.out.println("method...");
}
}
接口中的成员特点
-
成员变量:只能定义常量,因为系统会默认加入三个关键字
public static final
常量命名规范:命名时单词需全部大写
public class InterfacaTest2{
public static void main(String[] args){
//可以通过类名调用说明有static关键字修饰
System.out println(MyInter.num);
//报错,无法更改num的值,说明有final修饰
MyInter.num=20;
/*在其他包内定义一个接口和变量,可以进行跨包访问
说明有public关键字修饰*/
}
}
interface MyInter{
int num=10;//该语句默认为,public static final int num=10
}
-
成员方法:只能是抽象方法,因为系统会默认加入两个关键字
public abstract
编写代码时可省略这两个关键字
-
构造方法:没有
接口和类之间的各种关系
类和接口之间:实现关系,可以单实现,也可以多实现,甚至可以在继承一个类的同时,实现多个接口
//示例:类和接口之间
class Fu{
public void show(){
System.out.println("Fu...show");
}
}
interface A{
void showA();
}
interface B{
void showB();
}
/*可以多实现,接口打破了只能使用单继承的局限性
抽象方法没有逻辑,不会出现逻辑冲突*/
class ABImpl implements A,B{
@Override
public void showA() {
}
@Override
public void showB() {
}
}
/*代码不报错,子类继承了父类,继承了show方法
已经对A、B中的方法进行了重写*/
class Zi extends Fu implements A,B{
}
接口和接口之间:继承关系,可以单继承,也可以多继承
//若均为show方法,也不会冲突,因为没有逻辑,但是重写时只需要重写一个
interface InterA{
void showA();
}
interface InterB{
void showB();
}
//继承A、B
interface InterC extends InterA,InterB{
void showC();
}
//需要重写三个方法
class InterCImpl implements InterC{
@Override
public void showA() {
}
@Override
public void showB() {
}
@Override
public void showC() {
}
}
抽象类和接口的对比
-
抽象类:对事物做抽象(描述事物)
-
接口:对行为抽象(制定规则)
-
成员变量:
抽象类:可以定义变量,也可以定义常量
接口:只能定义常量
-
成员方法:
抽象类:可以定义具体方法,也可以定义抽象方法
接口:只能定义抽象方法
-
构造方法:
抽象类:有
接口:没有
多态
同一个行为具有多个不同表现形式或形态的能力
import java.util.Scanner;
public class Test3 {
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
System.out.println("请输入:1.国内订单 2.国外订单");
OrderService orderService=null;
int choice=sc.nextInt();
switch(choice){
case 1:
//创建国内订单的业务类
orderService=new OrderServiceImpl();
break;
case 2:
//创建国外订单的业务类
orderService=new OrderServiceImpl();
break;
default:
}
//调用的方法相同,但所创建的对象不同,得到的表现形式不同
orderService.create();
orderService.findOne();
orderService.findlist();
orderService.cancel();
orderService.finish();
orderService.paid();
}
}
多态前提
-
有继承/实现关系
-
有方法重写
-
有父类引用指向子类对象
-
对象多态
好处:将方法的形参定义为父类类型,这个方法就可以接受到该父类的任意子类对象
-
行为多态
好处:同一个方法,具有多种不同表现形式,或形态的能力
public class AbstractTest1{
public static void main(String[] args){
/*子类引用指向子类对象
Dog d=new Dog();
*/
//父类引用指向子类对象(对象多态)
Animal a1=new Dog();
Animal a2=new Cat();
}
public static void useAnimal(Animal a){
//对象多态
//第一次调用useAnimal:Animal a=new Dog();
//第二次调用:Animal a=new Cat();
}
}
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
多态成员访问特点
-
成员变量:编译看左边(父类),运行看左边(父类)
-
成员方法:编译看左边(父类),运行看右边(子类)
在编译的时候,会检查父类中有没有这个方法
没有:编译出错
有:编译通过,但是运行的时候,一定会执行子类的方法
原因:担心你调用的方法,在父类中是一个抽象方法
-
多态创建对象,调用静态成员:
静态的成员,推荐类名进行调用
细节:静态的成员,可以使用对象名调用,但这是一种假象
生成字节码文件后,会自动将对象名调用,改成类名调用
注意:
当父类引用指向子类对象,或接口类引用指向实现类对象时,才允许创建对象时左右两边的类型不一致。
多态的好处和弊端
-
好处:提高了程序的扩展性
-
弊端:不能使用子类的特有成员
编译时查看父类,子类特有成员不在父类中,编译出错
多态中的转型
-
向上转型
从子到父(父类引用指向子类对象)
F u f=new Z i;
-
向下转型
从父到子(将父类引用所指向的对象,转交给子类类型)
Z i z=(Z i)f;//强转,可以用于调用子类中特有的成员
注意:强转易出现错误
Class Cast Exception:类型转换异常
在引用数据类型的强转中,[实际类型]和[目标类型]不匹配,就会出现此异常
用if语句和instance of关键字判断左边的引用是否为右边的数据类型可解决此异常
格式:对象名 instanceof类型
返回boolean类型结果
面向对象高级2
接口新特性
-
JDK8新特性:接口中可以定义非抽象方法但是需要使用关键字default修饰,这些方法就是默认方法。
-
允许定义非抽象方法,需要加入default关键字
作用:解决接口的升级问题
注意事项:
-
public可以省略,但是default不能省略
-
默认方法不是抽象方法,所以不强制被重写,实现类是允许重写的,但是需要去掉default关键字
格式:public default 返回值类型 方法名(参数列表){ }
范例:public default void show(){ }
-
如果实现了多个接口,多个接口中存在相同的默认方法,实现类必须重写默认方法
-
-
允许定义静态方法
理解:既然接口已经允许方法带有方法体了,干脆也放开静态方法,可以类名调用
格式:public static 返回值类型 方法名(参数列表){ }
范例:public static void show(){ }
注意事项:
-
public可以省略,但是static不可省略
-
接口中的静态方法,只允许接口名进行调用,不能通过实现类名或者对象名调用
-
-
-
JDK9的新特性:接口中可以定义私有方法。
接口中允许定义私有方法
好处:提升复用性,减少冗余代码
格式1:private static 返回值类型 方法名(参数列表){ }
范例1:private void show(){ }
格式2:private static 返回值类型 方法名(参数列表){ }
范例2:private static void method(){ }
代码块
使用{ }括起来的代码被称为代码块
分类:
-
局部代码块
位置:方法中的一对大括号
作用:限定变量的生命周期,提早的释放内存
-
构造代码块
位置:类中方法外的一对大括号
特点:在创建对象,执行构造方法的时候就会执行代码块(优先于构造方法执行)
作用:可以将多个构造方法中,重复的代码,抽取到构造代码块中,从而提升代码的复用性
public class BlockTest{
public static void main(String[] args){
Student stu1=new Student();
Student stu2=new Student(10);
}
}
class Student{
{
System.out.println("Student类的构造代码块");
}
public Student(){
System.out.println("空参构造方法...");
}
public Student(int num){
System.out.println("带参构造方法...");
}
}
//每次调用构造方法时都会先执行构造代码块
-
静态代码块
位置:类中方法外的一对大括号,需要加入static关键字
特点:随着类的加载而执行,因为类只加载一次,所以也就只执行一次
作用:对数据进行初始化
-
同步代码块
内部类
-
内部类就是定义在一个类里面的类
class Outer{
class Inner{
}
}
创建对象的格式:
格式:外部类名.内部类名 对象名=new 外部类对象().new 内部类对象();
范例:Outer.Inner in=new Outer().new Inner();
public class InnerTest{
public static void main(String[] args){
Outer.Inner oi=new Outer().new Inner();
System.out.println(oi.num);
oi.show();
}
}
class Outer{
class Inner{
int num=10;
public void show(){
System.out.println("show...");
}
}
}
成员访问细节:
-
内部类中,访问外部类成员:直接访问,包括私有
class Outer{
private void method(){
System.out.println("method...");
}
class Inner{
int num=10;
public void show(){
System.out.println("show...");
method();//可以直接访问
}
}
}
外部类中,访问内部类成员:需要创建对象访问
class Outer{
private void method(){
System.out.println("method...");
//创建对象访问
Inner i=new Inner();
System.out.println(i.num);
}
class Inner{
int num=10;
public void show(){
System.out.println("show...");
method();//可以直接访问
}
}
}
注意:在成员内部中访问所在外部类对象
格式:外部类名.this
class MyOuter{
int num=10;
class MyInner{
int num=20;
public void show(){
int num=30;
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(MyOuter.this.num);//10
}
}
}
内部类的分类
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
异常
异常概念
异常:指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
异常体系
异常机制其实是帮助我们找到程序中的问题。
-
所有异常和错误的超类(父类)是:java.langThrowable
-
其下有两个子类: java.lang.Error与java.lang.Exception
-
平常所说的异常指java.lang.Exception
Throwable体系
-
Error:严重错误
无法通过处理的错误,只能事先避免,好比绝症。必须修改源代码,程序才能继续执行
-
Exception:表示异常(编译期异常)
异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。
-
RuntimeException:运行期异常,Java程序运行过程中出现的问题
-
Throwable中的常用方法:
-
public void printstackTrace():
打印异常的详细信息
包含了异常的类型、异常的原因,还包括异常出现的位置,在开发和调试能都得使用printStackTrace
-
public string getMessage():
获取发生异常的原因。
提示给用户的时候就提示错误原因
-
public String toString():
获取异常的类型和异常描述信息(不用)。
出现异常,不要紧张,把异常的简单类名考贝到API中去查。
编译器异常
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;
public class Demo01Exception{
public static void main(String[]args) throws ParseException {
//Exception:编译期异常,进行编译(写代码)java程序出现的问题
SimpleDateFormat sdf= new SimpleDateFormat(“yyyy-MM-dd”);//用来格式化日期
Date date =sdf.parse(“1999-0909”);//把字符串格式的日期,解析为Date格式的日期,出现异常虚拟机终止程序
System.out.printIn(date);
}
}
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date;
public class Demo01Exception{
public static void main(String[]args) /*throws ParseException */{
//Exception:编译期异常,进行编译(写代码)java程序出现的问题
SimpleDateFormat sdf= new SimpleDateFormat(“yyyy-MM-dd”);//用来格式化日期
Date date =sdf.parse(“1999-0909”);//把字符串格式的日期,解析为Date格式的日期,选中该行异常代码Alt+回车选择try catch方式处理异常,处理后程序能够正常运行
System.out.printIn(date);
}
}
运行期异常
int[] arr={1,2,3};
System.out.println("arr[3]");//能够通过编译,但是运行时显示索引越界,导致运行期异常
//处理
int[] arr={1,2,3};
try{
//可能会出现异常的代码
System.out.println("arr[3]");
}catch(Exception e){
//异常的处理逻辑
System.out.println(e);
}
System.out.println("后续代码");//能够打印出后续代码
Error
int[] arr=new int[1024*1024*1024];//内存溢出的错误,创建的数组太大了,超出了给JVM分配的内存,必须修改代码才能解决
异常的产生过程解析
当产生异常时,JVM会:
-
根据异常产生的原因创建一个异常对象,这个对象包含了异常产生的(内容、原因、位置)
-
出现异常的方法中,没有异常处理逻辑(try-catch),那么JVM就会把异常对象抛出给方法的调用者main方法来处理这个异常
-
main方法接收到了这个异常对象,main方法也没有异常的处理逻辑,继续把对象抛出给main方法的调用者JVM处理
-
JVM接收到了这个异常对象,做了两件事
-
把异常对象(内容,原因,位置)以红色的字体打印在控制台
-
JVM会终止当前正在执行的Java程序即中断处理
-
异常的处理
Java异常处理的五个关键字:try,catch,finally,throw,throws 2.1 抛出异常throw
在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。 在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。
具体操作
1.创建一个异常对象,封装一些提示信息(信息可以自己编写)。 2.需要将这个异常对象告知给调用者,怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw 异常对象。 throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。 使用格式: throw new 异常类名(参数);
Objects非空判断
object类组成
它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,对对象为null的值进行了抛出异常操作。
public static <T>T requireNonNul1(T obj):查看指定引用对象不是null, 查看源码发现这里对为null的进行了抛出异常操作:
public static <T> T requirelioniul1(T obj) {
if(obj==null)
throw new NullPointerException();
return obj;
}
声明异常:
将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理,那么必须通过throws进行声明,让调用者去处理
关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常)
声明异常格式:
修饰符 返回值类型 方法名(参数)throws 异常类名1,异常类名2...()
捕获异常try...catch
如果异常出现的话,会立刻终止程序,所以我们得处理异常: 1.该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)。 2.在方法中使用try-catch的语句块来处理异常。 try-catch的方式就是捕获异常。
捕获异常
java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
捕获异常语法
try{
编写可能会出现异常的代码}catch(异常类型 e){
处理异常的代码
}
如何获取异常信息
Throwable类中定义了一些查看方法:
-
public String getMessage():
获取异常的描述信息,原因(提示给用户的时候,就提示错误原因
-
public string tostring():获取异常的类型和异常描述信息(不用)
-
public void printstackTrace():
打印异常的跟踪栈信思并输出到控制台包含了异常的类型异常的原因还包括异常出现的位置在开发和测试的品都博使用brintStackTrace 在开发中呢也可以在catch将编译期异常转换成运行期异常处理。
finally 代码块
finally
有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。什么时候的代码必须最终执行? 当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。
finally的语法:
try….catch....finally:自身需要处理异常,最终还得关闭资源
注意:finally不能单独使用。 比如在我们之后学习的1流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。
异常注意事项
多个异常使用捕获又该如何处理
1.多个异常分制处理 2.多个异常一次摘获,多次处理。 3.多个异常一次捕获一次处理。
一般我们是使用一次捕获多次处理方式,格式如下
try{
编写可能会出现异常的代码
}catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型Be){ 当try中出现8类型异常,就用该catch来捕获
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}
注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
-
运行时异常被抛出可以不处理,即不捕获也不声明抛出
-
如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集
-
父类方法没有抛出异常,子类厦盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
-
在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
自定义异常
概述
为什么需要自定义异常类: 我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。 在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过。
自定义异常类:
在开发中根据自己业务的异常情况来定义异常类 自定义一个业务逻辑异常: RegisterException,一个注册异常类。
异常类如何定义:
1.自定义一个编译期异常:自定义类并继承于 java.lang.Exseption 2.自定义一个运行时期的异常类:自定义类并继承于java.lang.RuntimeException。