方法调用
静态方法、非静态方法
/*
* 有static的变量,方法。用来修饰的方法可以直接用 类名.变量名/方法名 来调用。而没有static变量、方法则不能通过 类名.变量名/方法名 调用,只能通过声明对象的方式,使用对象来调用
*
* static和类一起加载,没有static的变量,方法在类实例化(new 对象)之后才存在
*/
//静态方法 有static
//非静态方法 没有static
形参、实参
public class Demo03 {
public static void main(String[] args) {
//实际参数和形式参数的类型要一致
int a = new Demo03().add1(7, 8); //7, 8 是实际参数,是真实的值
int b = Demo03.add2(6,8);
System.out.println(a);
System.out.println(b);
}
public int add1(int a, int b){
return a + b;
}
public static int add2(int a, int b){ //int a, int b 就是形式参数
return a + b;
}
}
值传递、引用传递
牢记:Java的8大基本类型是值传递,其他如对象等都是引用传递
//值传递
public class Demo04 {
public static void main(String[] args) {
int a = 1;
System.out.println("执行了main方法体前的a:" + a); //a为1
Demo04.f(a); //值传递,将main中a的值1传给f中a
System.out.println("执行了main方法体后的a:" + a); //打印出来的还是main中的a,因为是值传递,所有main中a并未改变
}
public static void f(int a){
System.out.println("未执行方法体前f中的a:" + a);
a = 10;
System.out.println("执行方法体后f中的a:" + a);
}
}
//引用传递:对象,本质还是值传递
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println("调用f方法之前person.name:" + person.name); //null
Demo05.f(person);
System.out.println("调用f方法之后person.name:" + person.name); //zwx
}
public static void f(Person person){
System.out.println("未执行方法体前f中的person.name:" + person.name);
//person是一个对象,这是一个具体的人可以改变属性
person.name = "zwx";
System.out.println("未执行方法体前f中的person.name:" + person.name);
}
}
class Person{
String name;
}
类与对象
创建与初始化对象
使用new关键字创建对象
构造器,也叫构造方法,在创建对象时必须要调用。构造方法有以下两个特点:
- 方法名和类名必须相同
- 不能有返回值,也不能写void
构造器总结
构造器:
1 和类名相同
2 没有返回值,不能写void
作用:
1 new本质就是调用构造方法
2 初始化对象的值
注意点:
如果定义了有参构造,则最好要写出无参构造,如public Person(){}
生成构造器快捷键:
alt + insert
this.name = name 中前者是类里面的,后者是参数传进来的
public class Person {
//一个类即使什么都不写,也会存在一个方法---显示的定义构造方法
String name;
//无参构造方法
/*构造器两大核心作用
1、 使用new关键字,本质是在调用构造器,因此必须要有构造器
*/
public Person(){
//this.name = "zwx";
}
/*构造器两大核心作用
2、 有参构造:一旦定义了有参构造,就必须写出无参构造器,否则报错
*/
public Person(String name){
this.name = name; //name是参数传递下来的,this.name是指类中的name
}
}
创建对象内存分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VcswHHiH-1684735763604)(https://gitee.com/zwx0203/cloudimage/raw/master/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20230518111227.jpg)]
小总结
1 类与对象
类是一个模板,抽象。对象是一个类的具体的实例
2 方法
定义、调用
3 值传递,引用传递
值传递: 8大基本类型
引用传递:除8大基本类型都是引用传递,如对象就是引用传递
4 属性:字段Field 成员变量
默认初始化:
数字: 0 0.0
char: u0000
boolean: false
引用: null
修饰符 属性类型 属性名 = 属性值
5 对象的创建和使用
必须使用new关键字创建对象 Person zwx = new Person()
要会使用构造器
调用对象的属性 zwx.name
调用对象的方法 zwx.sleep()
6 类
类里只有属性和方法
封装、继承、多态
封装
封装大多数是针对属性来的,针对方法的一般比较少,只需记住属性私有,get/set
使用alt + insert 自动生成get,set方法
public class Student {
//封装只需记住一句话:属性私有,get/set,set方法中有时会做一些安全性的验证,判断。
//alt + insert 自动生成get,set方法
//封装:禁止直接访问对象中的属性,而是通过get,set来访问
/*封装的意义
1 提高程序安全性,保护数据
2 隐藏代码的实现细节
3 统一接口
4 提升系统可维护性
*/
//属性方法
private String name;//名字
private int styid;//学号
private char sex;//性别
private int age; //年龄
//get,set 提供一些可以操作这些属性的方法
//get 获得这个数据
public String getName(){
return this.name;
}
//set 给这个数据设值
public void setName(String name){
this.name = name;
}
public int getStyid() {
return styid;
}
public void setStyid(int styid) {
this.styid = styid;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 120 && age > 0){
this.age = age;
}else {
this.age = 3;
}
}
}
继承 extends
在Java中只有单继承,没有多继承。一个儿子只能有一个爸爸,但一个爸爸可以有多个儿子
public 一般让子类继承的一些属性和方法,都设置为public,但一般属性都是私有的
protected 受保护的,访问范围是:当前包所有的类+当前包以外的子类。
default 什么修饰符都没写就是default 默认的
private 一般来说属性都是私有的
Person类 父类(基类)
package com.oop.demo05;
//父类 基类
public class Person /*extends Object*/ {
/*
public 一般让子类继承的一些属性和方法,都设置为public,但一般属性都是私有的
protected 访问范围是:当前包所有的类+当前包以外的子类。
default 什么修饰符都没写就是default 默认的
private 一般来说属性都是私有的
*/
//在Java中,所有类都默认直接或间接继承Object类
//在Java中只有单继承,没有多继承。一个儿子只能有一个爸爸,但一个爸爸可以有多个儿子
private int money = 10_0000_0000;
private String happy = "very happy"; //私有属性,无法被子类继承
public void say(){
System.out.println("说了一句话!");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public String getHappy() {
return happy;
}
public void setHappy(String happy) {
this.happy = happy;
}
}
Student类 子类(派生类)
//学生 是 人 : 派生类,子类
//子类继承了父类,就会拥有父类的全部方法和属性(前提修饰符是public)
public class Student extends Person {
}
Teacher类 子类(派生类)
//老师 是 人
public class Teacher extends Person {
}
final修饰的类不能被继承
final之后断子绝孙了!!啊哈哈!
public final class Person {} //父类,若父类用了final修饰,表示常量,不可以被修改
public class Student extends Person {} //子类,直接报错
object类
在Java中,所有类都默认直接或间接继承Object类
super,this关键字
super注意点:
1. super()调用父类的构造方法和this()调用本类构造方法,都必须在子类构造方法里最前面
2. super(),this() 不能同时调用构造方法(由1可推出)
3. super只能出现在子类的方法或构造方法中
super Vs this:
代表的对象不同:
this:代表本身这个对象
super:代表父类对象
前提
this:没继承也能使用
super:只能在继承后才能使用
构造方法
this(): 本类的构造方法
super(): 父类的构造
public class Person /*extends Object*/ {
protected String name = "zwx";
public Person() {
System.out.println("Person无参构造执行");
}
//私有方法或属性均无法被继承
public void print(){
System.out.println("Person");
}
}
public class Student extends Person {
//隐藏代码:默认调用了父类的无参构造
public Student() {
super(); //调用父类的构造器,必须在子类构造器最前面(默认调用父类的无参构造)
System.out.println("Student无参构造执行");
}
//有参构造
public Student(String name) {
this.name = name;
}
private String name = "周文星";
public void print(){
System.out.println("Student");
}
public void test(String name){
System.out.println(name); //形参name htt
System.out.println(this.name); //本类的name 周文星
System.out.println(super.name); //父类的name zwx
}
public void test1(){
print(); //Student
this.print(); //Student
super.print(); //Person
}
}
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.test("htt");
s1.test1();
}
}
输出:
Person无参构造执行
Student无参构造执行
htt
周文星
zwx
Student
Student
Person
方法重写
不能被重写的方法:
1. static 修饰的方法,属于类,它不属于实例,不能被重写 1. final 修饰的方法,常量,不能被重写 1. private 修饰的方法,私有方法,不能被重写
重写:需要有继承关系,而且是子类重写父类的方法(重写只针对非静态方法,也就是子类不能重写带有static修饰符的父类方法)
1. 方法名必须相同
2. 参数列表必须相同(重载是在同一个类里写多个方法名一样的方法,列表,返回值等可以不同)
3. 修饰符:范围可以扩大: public > Protected > Default > Private
4. 重写可能会抛出异常,抛出的异常:范围可以被缩小,但不能扩大;ClassNotFoundException(小) --不能–> Exception(大)
重写 子类的方法和父类的方法必须要一致,方法体可不同!
为什么需要重写:
父类的功能,子类不一定需要,或者不满足快捷键:alt + insert:override
public class Person {
public void run(){
System.out.println("父亲跑起来了!");
}
}
public class Student extends Person{
@Override //子类重写了父类的方法,则父类引用子类执行的是子类的方法
public void run() {
System.out.println("儿子跑起来了!");
}
public void eat(){
System.out.println("吃饭!");
}
}
public class Application {
public static void main(String[] args) {
//一个对象的实际类型是确定的
//但可以指向的引用类型就不确定了
//Student 能调用的方法都是自己的或者是继承过来的
Student s1 = new com.oop.demo06.Student();
//Person 父类型,可以指向子类,但不能调用子类独有的方法
Person s2 = new com.oop.demo06.Student();//父类的引用指向子类
//Student s3 = new Person(); 报错,因为子类的引用不能指向父类
Object s3 = new com.oop.demo06.Student();
s2.run();
//如果子类重写了父类方法则调用子类方法,没重写则调用父类方法,运行时看右边类型
//对象能执行哪些方法,主要看对象左边的类型,和右边new 类型关系不大
//s2.eat(); //父类中没有eat方法,不能调用
((Student)s2).eat();//强制类型转换,把Person类型转成Student类型
s1.run();
s1.eat();
}
}
多态
多态的功能
1多态可以使同一方法根据调用对象的不同,导致方法行为不同
2 多态可以实现动态编译,就是类型在执行过程中才可以确定,写代码时确定不了,提升程序可扩展性
即同一方法根据对象的不同而采用不同的行为方式
一个对象的实际类型是确定的,但可以指向对象的引用类型很多
多态总结
多态注意事项:
多态是方法的多态,属性没有多态
父类和子类,有联系 类型转换异常! ClassCastException
多态存在条件缺一不可:
有继承关系(extends)
有方法重写(子类重写父类方法),alt + insert 中 override
父类引用指向子类,如Person s2 = new Student()
注意:编译期间(写代码期间)看左边类型,运行期间看右边类型
Object object = new Student(); //这里X是object对象,X的左边类型是Object,x的右边类型是Student
不能被重写的方法:
1. static 修饰的方法,属于类,它不属于实例,不能被重写 1. final 修饰的方法,常量,不能被重写 1. private 修饰的方法,私有方法,不能被重写
instanceof 关键字
语法:
X instanceof Y //X一定得是对象,Y一定得是类 否则编译报错
//例如
Object object = new Student();
//这里X是object对象,X的左边类型是Object,x的右边类型是Student
Student student = new Student();
object instanceof Student; //true
object instanceof Teacher; //false
object instanceof String; //false
student instanceof Student; //true
student instanceof Teacher; //编译不通过
student instanceof String; //编译不通过
//详细分析情况请见下面代码中的注释
作用:判断类与类之间是否存在关系
左边是引用类型(编译报不报错看左边,也就是编译时看X的左边类型与Y类是否有直系关系)
右边是实际类型(返回ture or false看右边,运行时看X的右边类型与Y类是否有直系关系)
//Object > String
//Object > Person > Student
//Object > Person > Teacher
这里 Object与Person、Student、Teacher、String均有直系关系
但Student、Teacher、String三者之间没有任何关系
public static void main(String[] args) {
//Object > String
//Object > Person > Student
//Object > Person > Teacher
Object object = new Student(); //左边是引用类型(能不能编译通过看左边),右边是实际类型(返回ture or false看右边,运行时看右边)
//Object > Person > Student
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
//Object > Person > Teacher 而Student与这条线无关
System.out.println(object instanceof Teacher);//false。因为左边Object类与Teacher有直系关系所有可以通过编译;又因为右边Student类与Teacher类没有直系关系所有返回false
//Object > String 而Student与这条线无关
System.out.println(object instanceof String);//false。因为左边Object类与String有直系关系所有可以通过编译;又因为右边Student类与String类没有直系关系所有返回false
Person person = new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
//Object > Person > Teacher 而Student与这条线无关
System.out.println(person instanceof Teacher);//false。因为左边Person类与Teacher有直系关系所以可以通过编译;又因为右边Student类与Teacher类没有直系关系所有返回false
System.out.println(person instanceof String);//编译报错。因为左边Person类与String类没有直系关系
Student student = new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
System.out.println(student instanceof Teacher);//编译报错。因为左边Student类与Teacher类没有直系关系
System.out.println(student instanceof String);//编译报错。因为左边Student类与String类没有直系关系
//总结总结总结总结!!!!
//System.out.println(X instanceof Y); //编译取决于X的左边类型和Y类型之间是否有直系关系。true、false取决于x对象右边类型和Y类型是否有直系关系
}
类型转换
1 子类转父类,低转高,直接转就行
Student student = new Student();
Person person = student;把子类对象赋给父类叫向上转型,不强制转换,会丢失子类特有方法。前父后子,不强制
2 父类转子类,高转低,强制转换
Person s1 = new Student();
((Student) s1).go();
//上面这句代码等于后面这两句,后第一句就是强制转换
Student s2 = (Student) s1; //把指向子类对象的父类引用s1赋给子类引用s2叫向下转型,要强制转型。前子后父,强制
s2.go();
代码实例:
public class Person {
public void run(){
System.out.println("父亲跑起来了!");
}
}
public class Student extends Person{
public void go(){
System.out.println("student gogogo!");
}
}
public class Application {
public static void main(String[] args) {
//类型之间的转换:
//父类转子类 ((Student) s1).go();======================================================================
//高 低
Person s1 = new Student();
s1.go(); //s1无法调用Student类中go方法,解决方法:把s1这个对象类型转成Student类型,就可以使用Student类中的go()方法
//Person转成Student是高转低,要强制类型转换,语法如下:
Student s2 = (Student) s1;
s2.go();
//上面两句代码可以合成下面这一句代码:
((Student) s1).go();
//子类转父类 转成以后会丢失子类本来独有的方法=======================================================================
Student student = new Student();
student.go();
Person person = student;//把子类对象赋给父类叫向上转型,不强制转换,会丢失子类特有方法
person.go(); //转成父类之后,丢失了子类独有的方法
}
}
static关键字详解
加了static 成为类属性/方法,用类可以调用。
补充: 建议使用类名去操作静态变量,这样很清楚的知道其是静态变量
静态变量对于类,在内存中只有一个,供类的所有实例(对象)一起使用
非静态方法与静态使用上的区别: 前提–在同一个类里
1 非静态方法可以直接调用静态方法/属性
2 静态方法可以调用静态方法/属性
3 静态方法不可以调用非静态方法/属性
package com.oop.demo07;
import com.sun.xml.internal.ws.addressing.WsaActionUtil;
//static总结
public class Student extends Person {//b
private static int age; //静态变量 多线程会用到!
private double score; //非静态变量
//加了static 成为类变量,用类可以调用。
//补充: 建议使用类名去操作静态变量,这样很清楚的知道其是静态变量
// 静态变量对于类,在内存中只有一个,供类的所有实例(对象)一起使用
非静态方法与静态使用上的区别: 前提--在同一个类里
//非静态方法可以直接调用静态方法/属性
//静态方法可以调用静态方法/属性
//静态方法不可以调用非静态方法/属性
public void run(){
System.out.println("run");
go(); //非静态方法可以调用静态方法
eat(); //非静态方法可以调用非静态方法
System.out.println(age); //非静态方法可以调用静态属性
System.out.println(score); //非静态方法可以调用非静态属性
}
public void eat(){}
public static void go(){
System.out.println("go");
}
public static void main(String[] args) {
Student s1 = new Student();
//在同一个类里
System.out.println(age); //静态方法中可以调用静态变量
System.out.println(score); //静态方法中不能调用非静态属性,报错
go(); //静态方法中可以调用静态方法
run(); //静态方法中不可以调用非静态方法,报错
System.out.println(s1.age);
System.out.println(Student.age);//类变量用类调用。
//补充: 建议使用类名去操作静态变量,这样很清楚的知道其是静态变量
// 静态变量对于类,在内存中只有一个,供类的所有实例(对象)一起使用
System.out.println(s1.score);
//System.out.println(Student.score);//报错,没加static 不是类变量,不能用类调用
Student.run(); //报错,run()不是静态方法,不能用类名来调用
new Student().run();
new Student().go();
Student.go();
}
}
抽象类,接口,内部类
抽象类(用的不多)
何为抽象类,抽象方法
用abstract来修饰的类就叫抽象类
用abstract来修饰的方法就叫抽象方法
二者区别与联系
1 抽象类不能实例化,故不能new抽象类,只能靠子类继承它实现它里面的抽象方法
2 抽象类里面可以写非抽象方法,但有抽象方法的类一定是抽象类
3 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
4 子类继承抽象类,就必须重写抽象类的抽象方法,否则该子类也要声明为抽象类
思考?
1 抽象类不能实例化(不能new),那还存在构造器嘛? —存在
2 抽象类存在的意义? —把共有方法抽象出来~ 提升开发效率,扩展性
3 抽象类为什么用的没有接口多? —抽象类本质是类,需要用extends继承,但java中继承只能单继承,导致抽象类用的没那么多。 (接口可以多继承)
接口(用的多)
关键字:interface
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范。 实现约束和实现分离:面向接口编程
接口的作用:
1 约束
2 定义一些方法,让不同的人实现
3 接口中的方法都是 public abstract, 常用
4 接口中的属性都是 public abstract final,但一般不怎么用
5 接口不能被实例化(不能new,因为接口中没有构造方法。况且抽象类都不能实例化,比抽象类还抽象的接口那肯定不能实例化)
6 通过implement关键字可以实现多个接口,中间用逗号隔开
7 实现接口必须要重写接口里面的所有方法
//UserService接口
public interface UserService {
//接口中的所以定义的属性都是常量,都是public static final。但一般没有人在接口里面定义常量
public static final int AGE = 99;
//接口中的所以定义的方法都是抽象的,都是 public abstract。常用
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
//TimeService接口
public interface TimeService {
void time();
}
//实现UserService接口和TimeService接口
public class UserServiceImpl implements UserService,TimeService {
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void time() {
}
}
内部类(奇葩)
内部类就是在一个类的内部定义一个类。如,A类中定义一个类名叫B的类,那么B类就是A的内部类,A类就是B的外部类
内部类最强大的点:内部类可以访问外部类的私有变量
内部类分为:
1 成员内部类—成员内部类可以访问外部类的私有变量
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//成员内部类可以访问外部类的私有变量
public void getID(){
System.out.println(id);
}
}
}
2 静态内部类—静态内部类访问不了外部类的私有变量
public static class Inner{ //内部类前面加了static
public void in(){
System.out.println("这是内部类的方法");
}
//静态内部类访问不了外部类的私有变量
public void getID(){
System.out.println(id);//报错,静态内部类访问不了外部类的私有变量
}
}
3 局部内部类
public void method(){
//局部内部类
class InnerTwo{
public void in(){
}
}
}
4 匿名内部类
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
异常
什么是异常
异常指程序运行中出现的不期而至的各种状况,如:文件找不到,网络连接失败,非法参数等
异常发送在程序运行期间,影响正常程序执行流程
异常简单分为三类
1 检查性异常 由用户错误导致
2 运行时异常 编译时看不到,只有运行时才能看到,也是最容易被程序员避免的异常
3 错误error 错误不是异常,一般不是程序员造成的,可能是栈溢出了等导致的,在编译时也检查不到
异常体系结构
Java把异常当作对象来处理,基类java.lang.Throwable为所有异常的超类
Java中定义的异常类分为两大类,错误Error(无法预见)和异常Exception(可以预到)
示例图如下:
Error类对象由Java虚拟机生成并抛出,一般与程序员无关。
Exception 一般是由程序逻辑错误引起,与程序员有关
Exception由一个重要的子类RuntimeException 运行时异常,其他的异常统称为 非运行时异常
Error 与 Exception区别:
Error通常是致命的错误,程序无法控制和处理Error,当出现Error时,Java虚拟机(JVM)一般会终止线程
Exception通常是可以被程序处理的,并且在程序中应该尽可能去处理
Java异常处理机制
抛出异常
throw //主动抛出异常
捕获异常
try{
}catch(){
}finally{
}
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 0;
//假设要捕获多个异常,必须从小到大(先子类再父类)去捕获,否则编译会报错
//try{}catch(){}是必须要的,finally{}可以不要
//finally IO流中资源的关闭统一放着finally中(善后工作)
try {//监控区域,在这里面的代码有异常就可以捕获的到
System.out.println(a / b);
//new Test().a();
}catch (Error e){ //catch(想要捕获的异常类型) 捕获异常
System.out.println("Error");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable e){ //超类写后面,子类写前面
System.out.println("Throwable");
}finally { //处理善后工作,无论有没有异常,finally中的代码始终都会执行。如 IO流中资源的关闭统一放着finally中(善后工作)
System.out.println("finally");
}
}
public void a(){b();}
public void b(){a();}
}
异常处理五个关键字:try(尝试)、catch(捕获)、finally(无论执不执行都会走的)、throw(抛出异常)、throws (抛出异常)
捕获异常的意义:如果异常不捕获,程序会停止运行;异常被捕获后,程序会接着运行下去。
假设要捕获多个异常,必须从小到大(先子类再父类)去捕获,否则编译会报错
自定义异常
用户自定义异常类,只需继承Exception类即可
在程序中使用自定义异常类,大体可分为以下几个步骤:
1 创建自定义异常类
2 在方法中通过throw关键字抛出异常对象
3 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
4 在出现异常方法的调用者中捕获并处理异常
//自定义异常类,若数字>10则异常
public class MyException extends Exception {
//传递数字,若数字>10则抛出异常
private int detail;
public MyException(int a) {
this.detail = a;
}
//toString:异常的打印信息
@Override
public String toString() {
return "MyException{" + "detail=" + detail + '}';
}
}
//测试类
public class Test {
//可能会存在异常的方法
static void test(int a) throws MyException {
System.out.println("传递的参数为:" + a);
if (a > 10){
throw new MyException(a); //抛出
}
System.out.println("ok");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
//增加一些处理异常的代码
System.out.println("MyException=>" + e);
}
}
}
实际应用总结
处理运行时异常时,采用逻辑规避同时辅助try-catch处理
在多重catch块后面加一个最大的catch(Exception e)来处理可能被遗漏的异常
对不确定的代码,也可以加上try-catch,处理潜在异常
尽量去处理异常,尽量不要只调用printStackTrace()打印输出
根据不同的业务处理不同的异常
尽量添加finally语句去释放占用的资源,如IO~Scanner