继承(上)
好处:
- 提高了代码的复用性。
- 让类与类之间产生了关系,给第三个特征多态提供了前提。
上个Demo和图欣赏一下:
class Person
{
String name;
int age;
}
class Student extends/*继承*/ Person
{
void study()
{
System.out.println(name+"..Student Study.."+age);
}
}
class Worker extends/*继承*/ Person
{
void work()
{
System.out.println(name+"..Worker Work.."+age);
}
}
public class ExtendsDemo
{
public static void main(String[] args)
{
Student s = new Student();
s.name = "xiaoming";
s.age = 20;
s.study();
Worker w = new Worker();
w.name = "aaaa";
w.age = 31;
w.work();
}
}
Java中支持单继承,不直接支持多继承,但对C++中的多继承进行了改良。
单继承:一个子类只能有一个直接父类。
多继承:一个子类可以有多个直接父类(Java中不允许,进行改良,不直接支持,因为多个父类有相同成员,会产生调用不确定性,在java中通过“多实现”解决这问题)。
Java支持多层(多重)继承。比如,C继承B,B继承A。就会出现继承体系。
当要使用一个继承体系时:
- 查看该体系中的顶层类,了解该体系的基本功能。
- 创建体系中的最子类对象,完成功能的使用。
什么时候使用继承呢?
当类与类之间存在所属关系的时候,就定义继承。比如,xxx是yyy的一种。那么xxx extends yyy。所属关系就是 is a 关系。
子父类中成员的特点体现:
1、成员变量。
- 当本类的成员和局部变量同名的时候,使用this区分。this:代表一个本类对象的引用。
- 当子父类中的成员变量相同的时候,使用super关键字进行区分。super:代表一个父类空间。
- this和super的用法很相似。
class DemoA { int num = 5; } class DemoB extends DemoA { int num = 6; void show() { System.out.println(this.num+"..."+super.num); // 6...5 } }
2、成员函数。
当子父类中出现成员函数一模一样的情况,会运行子类的函数。
这种现象,称为覆盖操作。这是函数在子父类中的特性。
函数两个特性:
- 重载。同一个类中。overload
- 覆盖。子类中。覆盖也称为重写,覆写。override
覆盖注意事项:
- 子类方法覆盖父类方法时,子类方法权限必须大于等于父类的。
- 静态只能覆盖静态的。
class Fu
{
void show()
{
System.out.println("Fu show run");
}
}
class Zi extends Fu
{
// 覆盖,这里的权限还可以是public
void show()
{
System.out.println("Zi show run");
}
}
class ExtendsDemo2
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show(); // Zi show run
}
}
什么时候使用覆盖操作呢?
当对一个类进行子类的扩展时,子类需要保留父类的功能声明,但是要定义子类中该功能的特有内容时,就使用覆盖操作完成。class Phone
{
void call(){}
void show()
{
System.out.println("number");
}
}
class NewPhone extends Phone
{
void show()
{
System.out.println("name");
System.out.println("piv");
super.show();
}
}
3、构造函数。
子父类中的构造函数的特点。
1)、在子类构造对象时,发现访问子类构造函数时,父类的构造函数也运行了。
为什么呢?
原因是:在子类的构造函数中第一行有一个默认的隐式语句:super();。
子类的实例化过程:子类中所有的构造函数默认都会访问父类中的空参数的构造函数。
2)、为什么子类实例化的时候要访问父类中的构造函数呢?
那是因为子类继承了父类,获取到了父类中的内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。
所以子类在构造对象时,必须访问父类中的构造函数。
为什么完成这个必须的动作,就在子类的构造函数中加入super();语句。
如果父类中没有定义空参数构造函数,那么子类的构造函数必须用super明确要调用父类中的哪个构造函数。同时子类构造函数中如果使用了this调用本类的构造函数时,那么super就没有了。因为super和this都必须定义在第一行。所以只能有一个。但是可以保证子类中肯定会有其他构造函数访问父类的构造函数。
注意:super语句必须要定义在子类构造函数的第一行,因为父类的初始化动作要先完成。
Demo:
class Fu
{
int num;
Fu()
{
num = 10;
System.out.println("A fu run" + num);
}
Fu(int x)
{
System.out.println("B fu run" + x);
}
}
class Zi extends Fu
{
Zi()
{
// super(); // 默认调用的就是父类中的空参数构造函数
System.out.println("C zi run");
}
Zi(int x)
{
// 默认是super();但也可以指明具体的父类构造函数(如果没有空参数的构造函数,就必须指明具体的)
super(x);
System.out.println("D zi run" + x);
}
}
class ExtendsDemo3
{
public static void main(String[] args)
{
new Zi();
System.out.println("----------");
new Zi(6);
}
}
一个对象实例化过程:
- Person p = new Person();
- JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下);
- 在堆内存中开辟空间,分配地址;
- 并在对象空间中,对对象中的属性进行默认初始化;
- 调用对应的构造函数进行初始化;
- 在构造函数中,第一行会先到调用父类中的构造函数进行初始化;
- 父类的初始化完毕之后,再对子类的属性进行显式初始化;
- 在进行子类构造函数的特定初始化;
- 初始化完毕后,将地址值赋值给引用变量。
final关键字
- final是一个修饰符,可以修饰类,方法,变量
- final修饰的类不可以被继承,因为被final修饰了的类成为最终类
- final修饰的方法不可以被覆盖
- final修饰的变量是一个常量。只能被赋值一次 如:final double PI = 3.14159;
- 内部类只能访问被final修饰的局部变量
为什么要用final修饰变量?其实在程序如果一个数据是固定的,那么直接使用这个数据可以了,但是这样阅读性差,所以给该数据起个名称。
而且这个变量名称的值不能变化,所以加上final固定。写法规范:常量所有字母都大写,多个单词,中间用下划线(_)连接。
继承(下)
抽象类:
抽象:笼统,模糊,看不懂!不具体!
特点:
- 方法只有声明没有实现时,该方法就是抽象方法,需要被abstract修饰。抽象方法必须定义在抽象类中,该类必须被abstract修饰。
- 抽象类不可以被实例化。为什么?因为调用抽象方法没意思,没意义。
- 抽象类必须有其子类覆盖所有的抽象方法后,该子类才可以实例化。否则,这个子类还是抽象类。
Demo:
abstract /*抽象,修饰符*/ class Demo
{
abstract void show();
}
class DemoA extends Demo
{
void show()
{
System.out.println("DemoA run");
}
}
class DemoB extends Demo
{
void show()
{
System.out.println("DemoB run");
}
}
关于抽象类的五个问题:
1、抽象类中有构造函数吗?
有,用于给子类对象进行初始化。
2、抽象类可以不定义抽象方法吗?
可以的。但是很少见,目的就是不让改了创建对象。AWT的适配器对象就是这种类。
通常这个类中的方法有方法体,但是却没有内容。
abstract class Demo
{
void show1(){ }
void show2(){ }
}
3、抽象关键字不可以和哪些关键字共存?
- private 因为子类要覆盖抽象方法,私有化的话,那就覆盖不了了
- static 因为不需要对象就能类名调用抽象方法,抽象方法运行没意义,所以static和abstract不能共存
- final 很简单,final修饰的类是最终类,不被继承;final修饰的方法,不能被覆盖
4、抽象类和一般类的区别。
相同点:
抽象类和一般类都是用来描述事物的,都在内部定了成员。
不同点:
1、一般类有足够的信息描述事物;抽象类描述事物的信息有可能不足。
2、一般类中不可以定义抽象方法,只能定义非抽象方法;抽象类既然定义抽象方法,也可以定义非抽象方法。
3、一般类可以被实例化;抽象类不可以被实例化。
5、抽象类一定是一个父类吗?
是的。因为需要子类覆盖其方法后才可以对子类实例化。
练习:
/*
雇员示例:
需求:公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
对给出需求进行数据建模。
分析:
在这个问题领域中,先找出涉及的对象。通过名词提炼法。
程序员:
属性:姓名,工号,薪水。
行为:工作。
经理:
属性:姓名工号,薪水,奖金。
行为:工作。
程序员和项目经理不存在着直接继承关系,但是程序员和经理却又具有共性内容。
可以进行抽取,因为他们都是公司的雇员,可以将程序员和经理进行抽取,建立体系。
*/
// 描述雇员
class Employee
{
private String name;
private String id;
private double pay;
Employee(String name, Stirng id, double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
public void setPay(double pay)
{
this.pay = pay;
}
public double getPay()
{
return pay;
}
}
// 描述程序员
class Programmer extends Employee
{
Programmer(String name, String id, String pay)
{
super(name, id, pay);
}
}
// 描述项目经理
class Manager extends Employee
{
private double bonus; // 奖金
Manager(String name, String id, double pay, double bonus)
{
super(name, id, pay);
this.bonus = bonus;
}
public void setBonus(double bonus)
{
this.bonus = bonus;
}
public double getBonus()
{
return bonus;
}
}
接口
定义:
当一个抽象类中的方法都是抽象的时候,这是可以将抽象类用另一种形式定义和表示,就是接口——interface。定义接口使用的关键字不是class,而是interface.
对于接口当中常见的成员:而且这些成员都有固定的修饰符。
- 全局变量:public static final
- 抽象方法:public abstract
简单小例子:
interface Demo
{
public static final int NUM = 4;
public abstract void show1();
public abstract void show2();
}
接口的特点:
- 接口是对外暴露的规则。
- 接口是程序的功能扩展。
- 接口的出现降低耦合性。
- 接口可以用来多实现。
- 类与接口之间的实现关系,而且类可以继承一个类的同时实现多个接口。(下面有例子)
- 接口与接口之间可以有继承关系。(支持多继承)
注意:其中全局变量的public static final和抽象方法的public abstract可以省略不写,底层会自动加上,但是省略不写的话,阅读性极差,有可能误导。所以建议不要省略。
另外,得出一个结论,接口的成员都是公共的权限。
作用:接口的出现避免了单继承的局限性。
实现例子:
interface Demo
{
public static final int NUM = 4;
public abstract void show1();
public abstract void show2();
}
//类与类之间是继承关系,类与接口之间是实现关系。
/*
接口不可以实例化。
只能有实现了接口的子类并覆盖了接口中是所有抽象方法后,该子类才可以实例化。
否则,这个子类就是一个抽象类。
*/
class DemoImpl implements Demo
{
public void show1()
{
System.out.println("show1 run");
}
public void show2()
{
System.out.println("show2 run");
}
}
class InterfaceDemo
{
public static void main(String[] args)
{
DemoImpl d = new DemoImpl();
System.out.println(d.NUM);
System.out.println(DemoImpl.NUM);
System.out.println(Demo.NUM);
}
}
多实现的例子:
在Java中不直接支持多继承,因为会出现调用不确定性。
所以Java将多继承机制进行改良,在Java中变成了多实现。
一个类可以实现多个接口。
interface A
{
public abstract void show();
}
interface B
{
public abstract void show();
}
class Test implements A, B // 多实现
{
// 这个show方法覆盖了A和B上的两个抽象show方法
public void show()
{
System.out.println("show run");
}
}
继承+实现:
/*
一个类在继承另一个类的同时,还可以实现多个接口。
*/
class Q
{
public void func(){}
}
interface A
{
public abstract void show();
}
interface B
{
public abstract void show();
}
class Test extends Q implements A, B // 多实现
{
public void func(){}
public void show()
{
System.out.println("show run");
}
}
接口支持多继承:
interface AA
{
public abstract void show();
}
interface BB
{
public abstract void method();
}
interface CC extends AA, BB // 接口与接口之间是继承关系,而且接口之间支持多继承。
{
public abstract void show();
}
接口和抽象类的区别:
共性:都是不断抽取出来的抽象的概念(都是不断向上抽取而来的)。
区别1:抽象类体现继承关系,一个类只能单继承;接口体现实现关系,一个类可以多实现
区别2:抽象类是继承,是“is a”关系;接口是实现,是“like a”关系。
区别3:抽象类中可以定义非抽象方法,供子类直接使用;接口的方法都是抽象,接口中的成员都有固定修饰符
多态
定义
某一类事物的存在多种形态。
例子
动物中猫,狗。
猫这个对象对应的类型是毛类型:猫 c = new 猫();
同时猫也是动物中的一种,也可以把猫称为动物:动物 a = new 猫();
动物是猫和狗具体事物中抽取出来的父类型,父类型引用指向了子类对象。
简单说:就是一个对象对应着不同类型。
多态在代码中的体现:父类或者接口的引用指向其子类的对象。
abstract class Animal
{
abstract void eat();
}
class Dog extends Animal
{
void eat()
{
System.out.println("啃骨头");
}
void seeHome()
{
System.out.println("看家");
}
}
class Cat extends Animal
{
void eat()
{
System.out.println("吃鱼");
}
void catchMouse()
{
System.out.println("捉老鼠");
}
}
class Pig extends Animal
{
void eat()
{
System.out.println("饲料");
}
void gongDi()
{
System.out.println("拱地");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Animal aCat = new Cat();
Animal aDog = new Dog();
Animal aPig = new Pig();
eatMethod(aCat);
eatMethod(aDog);
eatMethod(aPig);
}
public static void eatMethod(Animal a)
{
a.eat();
}
}
多态的好处与弊端
好处:提高了代码的扩展性,前期定义的代码可以使用后期的内容。
弊端:前期定义的内容不能使用(调用)后期子类的特有内容。
多态的前提
- 必须有关系,继承,实现。
- 要有覆盖。
转型
例子:
abstract class Animal
{
abstract void eat();
}
class Dog extends Animal
{
void eat()
{
System.out.println("啃骨头");
}
void seeHome()
{
System.out.println("看家");
}
}
class Cat extends Animal
{
void eat()
{
System.out.println("吃鱼");
}
void catchMouse()
{
System.out.println("捉老鼠");
}
}
class Pig extends Animal
{
void eat()
{
System.out.println("饲料");
}
void gongDi()
{
System.out.println("拱地");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
/* Animal aCat = new Cat();
Animal aDog = new Dog();
Animal aPig = new Pig();
eatMethod(aCat);
eatMethod(aDog);
eatMethod(aPig); */
Animal a = new Cat();// 一个对象,两种形态
// 自动类型提升,猫对象提升成了动物类型。但是特有功能无法访问。
// 作用就是限制对特有功能的访问
// 专业讲:向上转型。
a.eat();
// 如果还想用具体动物猫的特有功能,可以将该对象进行向下转型。
Cat c = (Cat)a;// 向下转型的目的就是为了使用子类中的特有数据。
c.eat();
c.catchMouse();
// 反例,注意:对于转型,自始自终都是子类对象在做着类型的变化。
// Animal a1 = new Dog();
// Cat c1 = (Cat)a1; //ClassCastException异常
}
public static void eatMethod(Animal a)
{
a.eat();
}
}
instanceof-类型判断
public static void eatMethod(Animal a)
{
a.eat();
// instanceof:用于判断对象的具体类型,只能用于引用数据类型判断
// 通常在向下转型前用于健壮性的判断
if(a instanceof Cat)
{
Cat c = (Cat)a;
c.catchMouse();
}else if(a instanceof Dog)
{
Dog d = (Dog)a;
d.seehome();
}
}
多态成员的特点
1、成员变量:
- 编译时,参考引用型变量所属的类中的是否有调用成员变量,若有,则编译通过,否则,编译失败。
- 运行时,参考引用型变量所属的类中的是否有调用成员变量,并运行该所属类中的成员变量。
- 简单而言,编译和运行都参考等于号的左边,that's all.
class Fu
{
int num = 3;
}
class Zi extends Fu
{
int num = 4;
}
class DuoTaiDemo2
{
public static void main(String[] args)
{
// 参考左边的 Zi z
Zi z = new Zi();
System.out.println(z.num); // 4
// 参考左边的 Fu f
Fu f = new Zi();
System.out.println(f.num); // 3
}
}
2、成员函数
- 编译时,参考引用型变量所属的类中的是否有调用的成员函数,若有,则编译通过,否则,编译失败。
- 运行时,参考的是对象所属的类中的是否有调用的函数。
- 简单而言,编译看左边,运行看右边。
class Fu
{
void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
void show()
{
System.out.println("zi show");
}
}
class DuoTaiDemo2
{
public static void main(String[] args)
{
Fu f = new Zi();
f.show(); // zi show
}
}
3、静态函数
- 编译时,参考引用型变量所属的类中的是否有调用的静态函数。
- 运行时,参考引用型变量所属的类中的是否有调用的静态函数。
- 简单而言,编译和运行都看左边。
- 其实对于静态方法,是不需要对象的,直接用类名调用即可。
内部类
内部类访问的特点
- 内部类可以直接访问外部类中的成员。
- 外部类要访问内部类的数据,必须要建立内部类的对象。
内部类的用法
一般用于类的设计,分析事物时,发现该事物描述中还有其他事物,而且这个事物还要访问被描述事物的内容,这时就是还有的事物定义成内部类来描述。
class Outer
{
private int num1 = 3;
class Inner1
{
void show()
{
System.out.println("show run..."+num1);
}
}
private static int num2 = 33;
static class Inner2
{
void show()
{
System.out.println("show run..."+num2);
}
}
private static int num3 = 333;
static class Inner3
{
void show()
{
System.out.println("show run..."+num3);
}
// 如果内部类中定义了静态成员,那么内部类也必须是静态的。
static void func()
{
System.out.println("func run..."+num3);
}
}
public void method()
{
Inner1 in = new Inner1();
in.show();
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
// 在外部类中的方法中实例化内部类对象来访问内部类内容
Outer out = new Outer();
out.method();
// 直接访问外部类中的内部类中的内容
Outer.Inner1 in1 = new Outer().new Inner1();
in1.show();
// 如果内部类是静态的,相当于一个外部类
Outer.Inner2 in2 = new Outer.Inner2();
in2.show();
// 如果内部类是静态的,而且内部类的承成员也是静态的
Outer.Inner3.func();
}
}
小细节
为什么内部类能直接访问外部类中成员呢?
那是因为内部类持有了对外部类的应用。外部类名.this。
局部内部类
内部类可以存放在局部位置上。
内部类在局部位置上只能访问局部中被final修饰的局部变量。
class Outer
{
int num = 3;
void method(final int Y)
{
final int X = 9;
class Inner
{
void show()
{
System.out.println("show..." + X + "-" + Y);
}
}
new Inner().show();
}
}
class InnerClassDemo2
{
public static void main(String[] args)
{
new Outer().method(8);
}
}
匿名内部类
就是内部类的简写格式。
必须有前提:内部类必须继承或者实现一个外部类或者接口。
简单而言:匿名内部类其实就是一个匿名子类对象。
格式:new 父类or接口(){子类内容}
abstract class Demo
{
abstract void show();
}
class Ouetr
{
int num = 8;
void method()
{
// 匿名内部类
new Demo()
{
void show()
{
System.out.println("show...." + num);
}
}.show();
}
}
interface Inter
{
void show1();
void show2();
}
class Outer
{
int num = 6;
void method()
{
Inter in = new Inter()
{
void show1()
{
System.out.println("show1..." + num);
}
void show2()
{
System.out.println("show2..." + num);
}
}
in.show1();
in.show2();
}
}
内部类通常使用场景
1、当函数参数是接口类型时,而且接口中的方法不超过三个。可以用匿名内部类作为实际参数进行传递。
class InnerClassDemo2
{
public static void main(String[] args)
{
// new Outer().method(8);
show(new Inter()
{
void show1()
{
//...
}
void show2()
{
//...
}
});
}
public static void show(Inter in)
{
in.show1();
in.show2();
}
}
异常
定义
是在运行时期发生的不正常情况。
在Java中用类的形式对不正常情况进行了描述和封装对象。
描述不正常情况的类,就称为异常类。
以前正常流程代码和问题处理代码相结合,现在有了异常类之后,将正常流程代码和问题处理代码分离,提高阅读性。
其实异常类就是Java通过面向对象的思想将问题封装成了对象。用异常类对其进行描述。
不同的问题用不同的类进行具体的描述,比如角标越界,空指针异常等等等。
Demo
class ExceptionDemo
{
public static void main(String[] args)
{
sleep(-5);
sleep(1000000);
}
public static void sleep(int time)
{
if(time < 0)
{
抛出 new FuTime();
}
if(time > 100000)
{
抛出 new BigTime();
}
System.out.println("睡觉...." + time + "分钟");
}
}
class Funtime extends Exception
{
处理
}
class BigTime extends Exception
{
处理
}
体系
问题很多,意味着描述的类很多,将其共性进行向上抽取,形成了异常体系。
最终问题(不正常情况)就分成两大类。
Throwable:无论是error,还是Exception都是问题,问题发生就应可以抛出,让调用者知道并处理。
该体系的特点就在于Throwable及其所有的子类都具有可抛性。
可抛性到底指的是什么呢?怎么体现可抛性呢?
其实是通过两个关键字来体现的。
throws,throw,凡是可以被这两个关键字所操作的类和对象都具有可抛性。
1、一般不可以处理的——Error。
特点:是有JVM抛出的严重性的问题。
这种问题发生一般不针对性处理,直接修改程序。
2、可以处理的——Exception。
该体系的特点:
子类的后缀名都是用其父类名作为后缀名的,阅读性很强。
自定义异常
先看看一个例子
class FuShuIndexException extends Exception
{
FuShuIndexException(){}
FuShuIndexException(String msg)
{
super(msg);// 通过父类可以将msg显示在控制台上
}
}
class Demo
{
public void method(int[] arr, int x) throws FuShuIndexException // 在次声明异常
{
if(x < 0)
throw new FuShuIndexException("角标可不能为负数喔!");
System.out.println(arr[x]);
}
}
class ExceptionDemo3
{
public static void main(String[] args) throws FuShuIndexException // 要在此进行声明次异常,如果FuShuIndexException继承RuntimeException,则次声明不需要
{
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr, -5);
}
}
对于角标是整数不存在,可以用角标越界来表示;对于角标为负数的情况,准备用负数角标异常来表示。
负数角标这种异常在Java中并没有定义过,那就按照Java异常的创建思想,面向对象,将负数角标进行自定义描述,并封装成对象。
这种自定义的问题描述称为自定义异常。
注意:如果让一个类称为异常类,必须要继承异常体系,因为只有称为异常体系的子类才有资格具有可抛性。才可以被两个关键字所操作,throws throw .
异常的分类
1、编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。
这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。
2、编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。
那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修改。
所以自定义异常时,要么继承Exception,要么继承RuntimeException。
throw和throws的区别
1、throws使用在函数上,而throw使用在函数内。
2、throws抛出的是异常类,可以抛出多个,用逗号隔开。throw抛出的是异常对象。
异常捕捉
异常处理的捕捉形式:
这是可以对异常进行针对性处理的方式。
具体格式:
try
{
// 需要被检测异常的代码
}
catch(异常类 变量) // 该变量用于接收发生的异常对象
{
// 处理异常的代码
}
finally // 此处可以根据需求选择要不要
{
// 一定会执行的代码
}
Demo:
class ExceptionDemo4
{
public static void main(String[] args)
{
int[] arr = new int[3];
Test t = new Test();
try
{
int num = t.method(arr, -5);
System.out.println("num = "+num);
}
catch (FuShuIndexException e)
{
System.out.println(e.getMessage());
e.printStackTrace(); // jvm默认的异常处理机制就是调用异常对象的这个方法。
}
System.out.println("over");
}
}
class Test
{
public int method(int[] arr, int x) throws FuShuIndexException
{
if(x < 0)
throw new FuShuIndexException("角标出现负数啦!");
return arr[x];
}
}
class FuShuIndexException extends Exception
{
FuShuIndexException(){}
FuShuIndexException(String msg)
{
super(msg);
}
}
多catch情况
如果有多个catch,且还有父类的catch,那么父类的catch放在最后,否则编译失败。
class ExceptionDemo4
{
public static void main(String[] args)
{
int[] arr = new int[3];
Test t = new Test();
try
{
int num = t.method(arr, -5);
System.out.println("num = "+num);
}
catch (FuShuIndexException e)
{
System.out.println(e.getMessage());
e.printStackTrace(); // jvm默认的异常处理机制就是调用异常对象的这个方法。
}
catch(NullPointerException e)
{
System.out.println(e.getMessage());
}
System.out.println("over");
}
}
class Test
{
public int method(int[] arr, int x) throws FuShuIndexException, NullPointerException
{
if(arr == null)
throw new NullPointerException("没有任何数组实体!");
if(x < 0)
throw new FuShuIndexException("角标出现负数啦!");
return arr[x];
}
}
class FuShuIndexException extends Exception
{
FuShuIndexException(){}
FuShuIndexException(String msg)
{
super(msg);
}
}
异常处理的原则:
1、函数内容如果抛出需要检测的异常,那么函数上必须要声明。否则必须在函数内用trycatch捕捉,否则编译失败。
2、如果调用到了声明异常的函数,要么trycatch要么throws,否则编译失败。
3、什么时候catch,什么时候throws呢?
功能内容可以解决,用catch。如果解决不了,用throws告诉调用者,由调用者解决。
4、一个功能如果抛出了多个异常,要么调用时,必须有对应多个catch进行针对性的处理。
内部又几个需要检测的异常,就抛出几个异常,抛出几个,就catch几个。
finally代码块
例子:
finally代码块是很有用的。讲讲老毕说的例子:
连接数据库
查询
关闭连接
因为每一台设备被连接的数目是有限的,不能一直占用着资源,因为别人也要用到。那么服务器会判断一定的时间内没有跟数据库有信息交互的话,会自动关闭连接,如果在查询的时候出现了异常,那么也需要将连接关闭,释放资源。
try catch finally 代码块组合的特点:
1、第一种用法有 :try catch finally
2、try catch(多个) 当没有必要的资源要释放的时候,可以不用定义finally。
3、try finally 如果异常无法直接用catch解决不了,那么就不要catch了,但是要抛出去给调用者进行异常处理。
包(package)
特点
1、对类文件进行分类管理
2、给类提供多层命名空间
3、写在程序文件的第一行
4、类名的全称的是 包名.类名
5、包也是一种封装形式
Demo:
Package mypack;
class PackageDemo
{
public static void main(String[] args)
{
System.out.println("Hello Package!");
}
}
编译的时候,产生一个mypack的文件夹,里边有PackageDemo.class文件,在doc控制台上编译:javac -d . PackageDemo.java
包-包之间的访问-protected
包与包之间的类进行访问,被访问的包中的类必须要public的,被访问的包中的类的方法也必须要public权限。
public protected default private
同一类中 ok ok ok ok
同一包中 ok ok ok
子 类 中 ok ok
不同包中 ok
导入import
Package mypack;
import packa.DemoA; // 导入了packa包中的DemoA类。
//import packa.*; // 导入了packa包中的所有的类。开发过程中,不建议使用此方法。
class PackageDemo
{
public static void main(String[] args)
{
System.out.println("Hello Package!");
}
}
导包的原则:用到哪里个类,就导入哪个类。