封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的 暴露出来。这就是封装性的设计思想。
代码:
public class Girl {//女孩 //属性: private int age; //读取年龄: public int getAge(){ return age; } //设置年龄: public void setAge(int age){ if(age >= 30 ){ this.age = 18; }else{ this.age = age; } } }
测试方法:
public class Test { //这是一个main方法,是程序的入口: public static void main(String[] args) { //创建一个Girl类的对象: Girl girl = new Girl(); /*g.age = 33; System.out.println(g.age);*/ //设置年龄: g.setAge(31); //读取年龄: System.out.println(girl.getAge()); } }
上面的代码,对于属性age来说,我加了修饰符private,这样外界对它的访问就受到了限制,现在我还想加上其他的限制条件,但是在属性本身上没有办法再加了,所以我们通过定义方法来进行限制条件的添加。
以属性为案例:
进行封装:
(1)将属性私有化,被private修饰–> 加入权限修饰符
一旦加入了权限修饰符,其他人就不可以随意的获取这个属性
(2)提供public修饰的方法让别人来访问/使用
(3)即使外界可以通过方法来访问属性了,但是也不能随意访问,因为咱们在方法中可以加入 限制条件。
加深练习:
代码:
public class Student { //属性: private int age; private String name; private String sex; //加入对应的setter和getter方法: public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { if("男".equals(sex) || "女".equals(sex) ){//sex是男 或者 是 女 this.sex = sex; }else{ this.sex = "男"; } } //加入构造器: public Student(){ } public Student(int age,String name,String sex){ this.age = age; this.name = name; //this.sex = sex; this.setSex(sex); } }
测试类:
public class Test { //这是一个main方法,是程序的入口: public static void main(String[] args) { //创建一个Student对象: Student s1 = new Student(); s1.setName("张三"); s1.setAge(19); s1.setSex("女"); System.out.println(s1.getName()+"---"+s1.getAge()+"----"+s1.getSex());//张三---19----女 Student s2 = new Student(18,"李四","asdfsadf"); System.out.println(s2.getName()+"---"+s2.getAge()+"----"+s2.getSex());//李四---18----男 } }
特点:
父类:
public class Person { //属性: private int age; private String name; private double height; //提供setter getter方法: public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } //方法: public void eat(){ System.out.println("可以吃饭。。。"); } public void sleep(){ System.out.println("可以睡觉。。。"); } }
子类:
public class Student extends Person {//子类Student 继承 父类Person //属性: private int sno;//学号 public int getSno() { return sno; } public void setSno(int sno) { this.sno = sno; } //方法: public void study(){ System.out.println("学生可以学习"); } }
测试类:
public class Test { //这是一个main方法,是程序的入口: public static void main(String[] args) { //创建子类Student的对象 Student s = new Student(); s.setSno(1001); s.setAge(18); s.setName("菲菲"); s.setHeight(180.4); System.out.println("学生名字为:"+s.getName()+",学生的年纪:"+s.getAge()); //访问方法: s.study(); s.eat(); s.sleep(); }
结果:
学生名字为:张三,学生的年纪:18
学生可以学习
可以吃饭。。。
可以睡觉。。。
1.概述:子类有一个和父类一模一样的方法
2.访问特点:看new的是谁,先调用谁,子类没有找父类
3.注解:@Override–>可以检测此方法是不是重写方法
4.前提:子父类继承父类
5.注意事项:
·子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
public protected 默认(default) private
· 子类方法覆盖父类方法,返回值类型、函数名和参数都要一模一样。
·私有方法不能被重写(父类私有成员子类是不能继承的)
·子类方法重写父类方法,子类的方法返回值要是父类方法值得子类或者一样
总结:方法的重写就是和父类一模一样。
在特殊情况下,当子类和父类的属性重名时,你要想使用父类的属性,必须加上修饰符super.,只能通过super.属性来调用
在特殊情况下,当子类和父类的方法重名时,你要想使用父类的方法,必须加上修饰符super.,只能通过super.方法来调用
在这种情况下,super.就不可以省略不写。
【3】super修饰构造器:
其实我们平时写的构造器的第一行都有:super() -->作用:调用父类的空构造器,只是我们一般都省略不写
(所有构造器的第一行默认情况下都有super(),但是一旦你的构造器中显示的使用super调用了父类构造器,那么这个super()就不会给你默认分配了。如果构造器中没有显示的调用父类构造器的话,那么第一行都有super(),可以省略不写)
如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的super();了
在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,两者不能共存:
因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行:
改正二选一即可:
【4】以后写代码构造器的生成可以直接使用IDEA提供的快捷键:alt+insert
继承的好处:提高代码的复用性
父类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了
需要注意的点:
父类private修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。
总结:
(1)继承关系 :
(2)继承的好处:
(3)父类private修饰的内容,子类也继承过来了。
(4)一个父类可以有多个子类。
(5)一个子类只能有一个直接父类。
但是可以间接的继承自其它类。
(6)继承具有传递性:
Student ->继承自 Person ->继承自Object
Object类是所有类的根基父类。
所有的类都直接或者间接的继承自Object。
【1】多态跟属性无关,多态指的是方法的多态,而不是属性的多态。
【2】案例代入:
public class Animal {//父类:动物: public void shout(){ System.out.println("我是小动物,我可以叫。。。"); } }
狗类:
public class Dog extends Animal{ //喊叫: public void shout(){ System.out.println("我是小狗,我可以汪汪叫"); } public void guard(){ System.out.println("我是小狗,我可以看家护院,保护我的小主人。。。"); } }
猪类:
public class Pig extends Animal{ public void shout(){ System.out.println("我是小猪,我略略略的叫"); } public void eat(){ System.out.println("我是小猪,我爱吃东西。。"); } }
小女孩类:
public class Girl { //跟狗玩耍: /*public void play(Dog dog){ dog.shout(); }*/ //跟猪玩耍: /*public void play(Pig pig){ pig.shout(); }*/ //跟小动物玩耍: public void play(Animal an){ an.shout(); } }
测试类:
public class Test { //这是一个main方法,是程序的入口: public static void main(String[] args) { //具体的小女孩:--》女孩的对象 Girl girl = new Girl(); //具体的狗---》狗的对象: //Dog dog = new Dog(); //小女孩跟狗玩: //girl.play(dog); //我是小狗,我可以汪汪叫 //具体的猪:--》猪的对象 //Pig pig = new Pig(); //小女孩跟猪玩: //girl.play(pig); //我是小猪,我略略略的叫 //---上面写的这两种方法不够灵活---// //具体的动物:--》动物的对象: //Dog dog = new Dog(); Pig pig = new Pig(); Animal an = pig; //Animal an = new Pig(); girl.play(an); //我是小猪,我略略略的叫 } }
【3】总结:
(1)先有父类,再有子类:–》继承 先有子类,再抽取父类 ----》泛化
(2)什么是多态:
多态就是多种状态:同一个行为,不同的子类表现出来不同的形态。
多态指的就是同一个方法调用,然后由于对象不同会产生不同的行为。
(3)多态的好处:
为了提高代码的扩展性,符合面向对象的设计原则:开闭原则。
开闭原则:指的就是扩展是 开放的,修改是关闭的。
注意:多态可以提高扩展性,但是扩展性没有达到最好。-- 反射
(4)多态的要素:
一,继承: Cat extends Animal ,Pig extends Animal, Dog extends Animal
二,重写:子类对父类的方法shout()重写
三, 父类引用指向子类对象:
Pig p = new Pig();
Animal an = p;
将上面的代码合为一句话:Animal an = new Pig();
=左侧:编译期的类型
=右侧:运行期的类型
想访问到eat()方法和weight属性:
public class Demo { //这是一个main方法,是程序的入口: public static void main(String[] args) { Pig p = new Pig(); Animal an = p;//转型:向上转型 转型 an.shout(); //我是小猪,我嗯嗯嗯的叫 //加入转型的代码: //将Animal转为Pig类型: Pig pig = (Pig)an ;//转型:向下转型 转型 pig.eat(); //我是小猪,我爱吃东西。。 pig.age = 10; pig.weight = 60.8; } }
对应内存:
【1】抽象类和抽象方法的关系:抽象类中可以定义0-n个抽象方法。
【2】抽象类作用:
在抽象类中定义抽象方法,目的是为了为子类提供一个通用的模板,子类可以在模板的基础上进行开发,先重写父类的抽象方法,然后可以扩展子类自己的内容。抽象类设计避免了子类设计的随意性,通过抽象类,子类的设计变得更加严格,进行某些程度上的限制。
使子类更加的通用。
【3】代码:
//4.一个类中如果有方法是抽象方法,那么这个类也要变成一个抽象类。 //5.一个抽象类中可以有0-n个抽象方法 public abstract class Person { //1.在一个类中,会有一类方法,子类对这个方法非常满意,无需重写,直接使用 public void eat(){ System.out.println("一顿不吃饿得慌"); } //2.在一个类中,会有一类方法,子类对这个方法永远不满意,会对这个方法进行重写。 //3.一个方法的方法体去掉,然后被abstract修饰,那么这个方法就变成了一个抽象方法 public abstract void say(); public abstract void sleep(); } //6.抽象类可以被其他类继承: //7.一个类继承一个抽象类,那么这个类可以变成抽象类 //8.一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法 //9.子类继承抽象类,就必须重写全部的抽象方法 //10.子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类。 class Student extends Person{ @Override public void say() { System.out.println("我是东北人,我喜欢说东北话。。"); } @Override public void sleep() { System.out.println("东北人喜欢睡炕。。"); } } class Demo{ //这是一个main方法,是程序的入口: public static void main(String[] args) { //11.创建抽象类的对象:-->抽象类不可以创建对象 //Person p = new Person(); //12.创建子类对象: Student s = new Student(); s.sleep(); s.say(); //13.多态的写法:父类引用只想子类对象: Person p = new Student(); p.say(); p.sleep(); } }
【1】接口声明格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] {
常量定义;
方法定义;
}
【2】代码:
package com.msb.test04; /** * 1.类是类,接口是接口,它们是同一层次的概念。 * 2.接口中没有构造器 * 3.接口如何声明:interface * 4.在JDK1.8之前,接口中只有两部分内容: * (1)常量:固定修饰符:public static final * (2)抽象方法:固定修饰符:public abstract * 注意:修饰符可以省略不写,IDE会帮你自动补全,但是初学者建议写上,防止遗忘。 */ public interface TestInterface01 { //常量: /*public static final*/ int NUM = 10; //抽象方法: /*public abstract*/ void a(); /*public abstract*/ void b(int num); /*public abstract*/ int c(String name); } interface TestInterface02{ void e(); void f(); } /* 5.类和接口的关系是什么? 实现关系 类实现接口: 6.一旦实现一个接口,那么实现类要重写接口中的全部的抽象方法: 7.如果没有全部重写抽象方法,那么这个类可以变成一个抽象类。 8.java只有单继承,java还有多实现 一个类继承其他类,只能直接继承一个父类 但是实现类实现接口的话,可以实现多个接口 9.写法:先继承 再实现:extends Person implements TestInterface01,TestInterface02 */ class Student extends Person implements TestInterface01,TestInterface02 { @Override public void a() { System.out.println("---1"); } @Override public void b(int num) { System.out.println("---2"); } @Override public int c(String name) { return 100; } @Override public void e() { System.out.println("---3"); } @Override public void f() { System.out.println("---4"); } } class Test{ //这是一个main方法,是程序的入口: public static void main(String[] args) { //10.接口不能创建对象: //TestInterface02 t = new TestInterface02(); TestInterface02 t = new Student();//接口指向实现类 ---》多态 //11.接口中常量如何访问: System.out.println(TestInterface01.NUM); System.out.println(Student.NUM); Student s = new Student(); System.out.println(s.NUM); TestInterface01 t2 = new Student(); System.out.println(t2.NUM); } }
【3】接口的作用是什么?
定义规则,只是跟抽象类不同地方在哪?它是接口不是类。
接口定义好规则之后,实现类负责实现即可。
【4】
继承:子类对父类的继承
实现:实现类对接口的实现
【5】多态的应用场合:
(1)父类当做方法的形参,传入具体的子类的对象
(2)父类当做方法的返回值,返回的是具体的子类的对象
(3)接口当做方法的形参,传入具体的实现类的对象
(4)接口当做方法的返回值,返回的是具体的实现类的对象
【6】接口和抽象类的区别:
在JDK1.8之后,新增非抽象方法
在JDK1.8之前,接口中只有两部分内容:
(1)常量:固定修饰符:public static final
(2)抽象方法:固定修饰符:public abstract
在JDK1.8之后,新增非抽象方法:
(1)被public default修饰的非抽象方法:
注意1:default修饰符必须要加上,否则出错
注意2:实现类中要是想重写接口中的非抽象方法,那么default修饰符必须不能加,否则出错。
public interface TestInterface { //常量: public static final int NUM= 10; //抽象方法: public abstract void a(); //public default修饰的非抽象方法: public default void b(){ System.out.println("-------TestInterface---b()-----"); } } class Test implements TestInterface{ public void c(){ //用一下接口中的b方法: b();//可以 //super.b();不可以 TestInterface.super.b();//可以 } @Override public void a() { System.out.println("重写了a方法"); } @Override public void b() { } }
(2)静态方法:
注意1:static不可以省略不写
注意2:静态方法不能重写
public interface TestInterface2 { //常量: public static final int NUM = 10; //抽象方法: public abstract void a(); //public default非抽象方法; public default void b(){ System.out.println("-----TestInterface2---b"); } //静态方法: public static void c(){ System.out.println("TestInterface2中的静态方法"); } } class Demo implements TestInterface2{ @Override public void a() { System.out.println("重写了a方法"); } public static void c(){ System.out.println("Demo中的静态方法"); } } class A { //这是一个main方法,是程序的入口: public static void main(String[] args) { Demo d = new Demo(); d.c(); //Demo中的静态方法 Demo.c(); //Demo中的静态方法 TestInterface2.c();//TestInterface2中的静态方法 } }
疑问:为什么要在接口中加入非抽象方法???
如果接口中只能定义抽象方法的话,那么我要是修改接口中的内容,那么对实现类的影响太大了,所有实现类都会受到影响。
现在在接口中加入非抽象方法,对实现类没有影响,想调用就去调用即可。
【1】创建对象的过程:
(1)在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
(2)创建对象,在堆中开辟空间
(3)对对象进行初始化操作,属性赋值都是默认的初始值。
(4)new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值
从上面的效果能够看到:this指代的就是当前对象:
从内存上看:
this关键字 用法:
总结:当属性名字和形参发生重名的时候,或者 属性名字 和局部变量重名的时候,都会发生就近原则,所以如果我要是直接使用变量名字的话就指的是离的近的那个形参或者局部变量,这时候如果我想要表示属性的话,在前面要加上:this.修饰
如果不发生重名问题的话,实际上你要是访问属性也可以省略this.
public class Person { //属性 int age; String name; double height; //空构造器 public Person(){ } //有参构造器 public Person(int age,String name,double height){ this.age = age; this.name = name; this.height = height; } //方法: public void eat(){ int age = 10; System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age System.out.println(this.age);//这里指代的就是属性的age System.out.println("我喜欢吃饭"); } }
总结:在同一个类中,方法可以互相调用,this.可以省略不写。
public class Person { //属性 int age; String name; double height; //空构造器 public Person(){ } //有参构造器 public Person(int age,String name,double height){ this.age = age; this.name = name; this.height = height; } //方法: /*public void eat(){ int age = 10; System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age System.out.println(this.age);//这里指代的就是属性的age System.out.println("我喜欢吃饭"); }*/ public void play(){ /*this.*/eat(); System.out.println("抽烟"); System.out.println("喝酒"); } public void eat(){ System.out.println(/*this.*/age); System.out.println("烫头"); } }
总结:同一个类中的构造器可以相互用this调用,注意:this修饰构造器必须放在第一行
public class Person { //属性 int age; String name; double height; //空构造器 public Person(){ } //有参构造器 public Person(int age,String name,double height){ this(age,name); this.height = height; } public Person(int age,String name){ this(age); this.name = name; } public Person(int age){ this.age = age; } //方法: /*public void eat(){ int age = 10; System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age System.out.println(this.age);//这里指代的就是属性的age System.out.println("我喜欢吃饭"); }*/ public void play(){ /*this.*/eat(); System.out.println("上网"); System.out.println("洗澡"); } public void eat(){ System.out.println(/*this.*/age); System.out.println("吃饭"); } }
static可以修饰:属性,方法,代码块,内部类。
一般推荐访问方式:可以通过类名.属性名的方式去访问
public class MsbStudent { //属性: String name; static String age; //这是一个main方法,是程序的入口: public static void main(String[] args) { MsbStudent.age = "今天女朋友70大寿!"; //访问方式1 //创建学生对象: MsbStudent s1 = new MsbStudent(); s1.name = "张三"; //s1.age = "今天生日"; //访问方式2 System.out.println(s1.age); //今天女朋友70大寿! } }
static修饰属性总结:
(1)在类加载的时候一起加载入方法区中的静态域中
(2)先于对象存在
(3)访问方式: 对象名.属性名 类名.属性名(推荐)
public class Demo { int id; static int sid; public void a(){ System.out.println(id); System.out.println(sid); System.out.println("------a"); } //1.static和public都是修饰符,并列的没有先后顺序,先写谁后写谁都行 static public void b(){ //System.out.println(this.id);//4.在静态方法中不能使用this关键字 //a();//3.在静态方法中不能访问非静态的方法 //System.out.println(id);//2.在静态方法中不能访问非静态的属性 System.out.println(sid); System.out.println("------b"); } //这是一个main方法,是程序的入口: public static void main(String[] args) { //5.非静态的方法可以用对象名.方法名去调用 Demo demo = new Demo(); demo.a(); //6.静态的方法可以用 对象名.方法名去调用 也可以 用 类名.方法名 (推荐) Demo.b(); demo.b(); } }
public class Test { //这是一个main方法,是程序的入口: public static void main(String[] args) { //第1种情况: //final修饰一个变量,变量的值不可以改变,这个变量也变成了一个字符常量,约定俗称的规定:名字大写 final int A = 10;//final修饰基本数据类型 //A = 20; 报错:不可以修改值 //第2种情况: final Dog d = new Dog();//final修饰引用数据类型,那么地址值就不可以改变 //d = new Dog(); -->地址值不可以更改 //d对象里面的属性依然可以改变: d.age = 10; d.weight = 13.7; //第3种情况: final Dog d2 = new Dog(); a(d2); //第4种情况: b(d2); } public static void a(Dog d){ d = new Dog(); } public static void b(final Dog d){//d被final修饰 ,指向不可以改变 //d = new Dog(); --》报错 } }
final修饰方法,那么这个方法不可以被该类的子类重写:
final修饰类,代表没有子类,该类不可以被继承:
一旦一个类被final修饰,那么里面的方法也没有必要用final修饰了(final可以省略不写)
【4】案例:JDK提供的Math类:看源码发现:
(1)使用Math类的时候无需导包,直接使用即可:
(2)Math类没有子类,不能被其他类继承了
(3)里面的属性全部被final修饰,方法也是被final修饰的,只是省略不写了
原因:子类没有必要进行重写。
(4)外界不可以创建对象:
Math m = new Math();
(5)发现Math类中的所有的属性,方法都被static修饰
那么不用创建对象去调用,只能通过类名.属性名 类名.方法名 去调用
【1】在什么情况下,try-catch后面的代码不执行?
(1)throw抛出异常的情况
(2)catch中没有正常的进行异常捕获
(3)在try中遇到return
【2】怎么样才可以将 try-catch后面的代码 必须执行?
只要将必须执行的代码放入finally中,那么这个代码无论如何一定执行。
【3】return和finally执行顺序?
先执行finally最后执行return
【4】什么代码会放在finally中呢?
关闭数据库资源,关闭IO流资源,关闭socket资源。
【5】有一句话代码很厉害,它可以让finally中代码不执行!
System.exit(0);//终止当前的虚拟机执行
代码:
public class Test3 { public static void main(String[] args) { //实现一个功能:键盘录入两个数,求商: try{ Scanner sc = new Scanner(System.in); System.out.println("请录入第一个数:"); int num1 = sc.nextInt(); System.out.println("请录入第二个数:"); int num2 = sc.nextInt(); System.out.println("商:"+num1/num2); System.exit(0);//终止当前的虚拟机执行 return; }catch(ArithmeticException ex){ //throw ex; }finally { System.out.println("----谢谢你使用计算器111"); } } }
注意:程序中语法错误,逻辑错误 都不属于上面的Error,Exception
【2】运行时异常:
public class Test5 { //这是一个main方法,是程序的入口: public static void main(String[] args) { //运行时异常: int[] arr = {1,2,3}; System.out.println(arr.length); /*int[] arr2 = null; System.out.println(arr2.length);*/ System.out.println(arr[10]); } }
【3】检查异常:
处理方式1:try-catch嵌套try-catch
public class Test6 { //这是一个main方法,是程序的入口: public static void main(String[] args) { //检查异常: try { try { Class.forName("com.msb.test01.Test").newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
处理方式2:多重catch
public class Test6 { //这是一个main方法,是程序的入口: public static void main(String[] args) { //检查异常: try { Class.forName("com.msb.test01.Test").newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
处理方式3:throws
public class Test6 { //这是一个main方法,是程序的入口: public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { //检查异常: Class.forName("com.msb.test01.Test").newInstance(); } }
public class Test7 { //这是一个main方法,是程序的入口: public static void main(String[] args) throws Exception { //实现一个功能:两个数相除,当除数为0的时候,程序出现异常。 /*try { devide(); } catch (Exception e) { e.printStackTrace(); }*/ devide(); } public static void devide() throws Exception { Scanner sc = new Scanner(System.in); System.out.println("请录入第一个数:"); int num1 = sc.nextInt(); System.out.println("请录入第二个数:"); int num2 = sc.nextInt(); if(num2 == 0 ){//除数为0 ,制造异常。 //制造运行时异常: /*throw new RuntimeException();*/ //制造检查异常: /*try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); }*/ throw new Exception(); }else{ System.out.println("商:"+num1/num2); } } }
总结:
throw和throws的区别:
(1)位置不同:
throw:方法内部
throws: 方法的签名处,方法的声明处
(2)内容不同:
throw+异常对象(检查异常,运行时异常)
throws+异常的类型(可以多个类型,用,拼接)
(3)作用不同:
throw:异常出现的源头,制造异常。
throws:在方法的声明处,告诉方法的调用者,这个方法中可能会出现我声明的这些异常。然后调用者对这个异常进行处理:
要么自己处理要么再继续向外抛出异常
自定义的异常可以继承:运行时异常
public class MyException extends RuntimeException { static final long serialVersionUID = -70348971907L; public MyException(){ } public MyException(String msg){ super(msg); } }
也可以继承检查异常:
public class MyException extends Exception { static final long serialVersionUID = -70348971907L; public MyException(){ } public MyException(String msg){ super(msg); } }
如果继承的是运行时异常,那么在使用的时候无需额外处理
如果继承的是检查异常,那么使用的时候需要try-catch捕获或者throws向上抛