类的继承
在Java中使用extends关键字来标识两个类的继承关系。
例:
class Test{
public Test(){ //构造方法
//SomeSentence
}
protected void doSomething(){ //成员方法
//SomeSentence
}
protected Test doIt(){ //方法返回值类型为Test类型
return new Test();
}
}
class Test2 extends Test{ //继承父类
public Test2(){ //构造方法
super(); //调用父类构造方法
super.doSomething(); //调用父类成员方法
}
public void doSomethingnew(){ //新增方法
//SomeSentence
}
public void doSomething(){ //重写父类方法
//SomeNewSentence
}
protected Test2 doIt(){ //重写父类方法,方法返回值类型为Test2类型
return new Test2();
}
}
当重写父类方法时,修改方法的修饰权限只能从小的范围到大的范围改变。
子类重写父类的方法还可以修改方法的返回值类型,如例子中子类的doIt()方法。这种重写方式需要遵循一个原则,即重写的返回值类型必须是父类中同一方法返回值类型的子类,而Test2类正是Test类的子类。
在Java中一切都以对象的形式进行处理,在继承的机制中,创建一个子类对象,将包含一个父类子对象,这个对象与父类创建的对象是一样的。两者的区别在于后者来自外部,前者来自子类对象的内部。当实例化子类对象时,父类对象也相应被实例化,换句话说,在实例化子类对象时,Java编译器会在子类的构造方法中自动调用父类的无参构造方法。
例:
class Test1{
Test1(){
System.out.println("调用父类的Test1()构造方法");
}
}
class Test2 extends Test1{ //继承Test1类
Test2(){
System.out.println("调用子类的Test2()构造方法");
}
}
public class Test3 extends Test2{ //继承Test2类
Test3(){
System.out.println("调用子类的Test3()构造方法");
}
public static void main(String[] args){
Test3 t = new Test3(); //实例化子类对象
}
}
调用父类的Test1()构造方法
调用子类的Test2()构造方法
调用子类的Test3()构造方法
从本例的运行结果可以看出,在子类Test3的主方法中之调用子类的构造方法实例化子类对象,并且在子类构造方法中没有调用父类构造方法的任何语句,到那时在实例化子类对象时它相应调用了父类的构造方法。
在实例化子类对象时,父类无参构造方法将被自动调用,但有参构造方法并不能被自动调用,只能依赖与super关键字显式地调用父类的构造方法。
如
果
使
用
f
i
n
a
l
i
z
e
(
)
方
法
对
对
象
进
行
清
理
,
需
要
确
保
子
类
的
f
i
n
a
l
i
z
e
(
)
方
法
的
最
后
一
个
动
作
是
调
用
父
类
的
f
i
n
a
l
i
z
e
(
)
方
法
,
以
保
证
当
垃
圾
回
收
对
象
占
用
内
存
时
,
对
象
的
所
有
部
分
都
能
被
正
常
终
止
。
\color{maroon}{如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法的最后一个动作是调用父类的finalize()方法,以保证当垃圾回收对象占用内存时,对象的所有部分都能被正常终止。}
如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法的最后一个动作是调用父类的finalize()方法,以保证当垃圾回收对象占用内存时,对象的所有部分都能被正常终止。
Object类
在Java中,所有的类都直接或间接继承了java.lang.Object类。Object类是所有类的父类,是Java类层中的最高层类。在Object类中主要包括clone(), finalize(), equals(), toString()等方法,其中常用的两个方法为equals()和toString()方法。由于所有的类都是Object类的子类,所以任何类都可以重写Object类中的方法。
O
b
j
e
c
t
类
中
的
g
e
t
C
l
a
s
s
(
)
,
n
o
t
i
f
y
(
)
,
n
o
t
i
f
y
A
l
l
,
w
a
i
t
(
)
等
方
法
不
能
被
重
写
,
因
为
这
些
方
法
被
定
义
为
f
i
n
a
l
类
型
。
\color{red}{Object类中的getClass(), notify(), notifyAll, wait()等方法不能被重写,因为这些方法被定义为final类型。}
Object类中的getClass(),notify(),notifyAll,wait()等方法不能被重写,因为这些方法被定义为final类型。
1. getClass()方法
它会返回对象执行时的Class实例,然后使用此实力调用getName()方法可以取得类的名称。
getClass().getName();
可以将getClass()方法与toString()方法联合使用。
2. toString()方法
功能是将一个对象返回为字符串形式,它会返回一个String实例。在实际的应用中通常重写toString()方法,为对象提供一个特定的输出模式。
3. equals()方法
equals()方法比较的是两个对象的实际内容。
对象类型的转换
1. 向上转型
class Quadrangle{ //四边形类
public static void draw(Quadrangle q){
//somesentence
}
}
public class Parallelogram extends Quadrangle{ //平行四边形类,继承了四边形类
public static void main(String args[]){
Parallelogram p = new Parallelogram();
draw(p); //调用父类方法
}
由于向上转型是从一个较具体的类到较抽象的类的转换,所以它总是安全的,如可以说鹏兴四边形是特殊的四边形,但不能说四边形是平行四边形。
2. 向下转型
向下转型是将较抽象类转换为较具体的类。这样的转型通常会出现问题。
Quadrangle q = new Parallelogram();
Parallelogram p = (Parallelogram) q; //将四边形对象看作是平行四边形对象,称为向下转型
越是具体的对象具有的特性越多,越抽象的对象具有的特性越少,在做向下转型操作时,将特性范围小的对象转换为特性范围大的对象肯定会出现问题,所以这时需要告知编译器这个四边形就是平行四边形。
3. 使用instanceof操作符判断对象类型
当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生ClassCastException异常,所以在执行向下转型之前需要养成好习惯,判断父类对象是否为子类对象的实例。这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是否一个类实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。
instanceof的语法格式:
myobject instanceof ExampleClass
返回值为布尔值,如果返回值为true,说明myobject对象为ExampleClass的实例对象;如果返回值为false,说明myobject对象不是ExampleClass的实例对象。
例:
class Quadrangle{ //四边形类
public static void draw(Quadrangle q){
//somesentence
}
}
class Square extends Quadrangle{
//somesentence
}
class Anything{
//somesentence
}
public class Parallelogram extends Quadrangle{
public static void main(String args[]){
Quadrangle q = new Quadrangle(); //实例化父类对象
//判断父类对象是否为Parallelogram子类的一个实例
if(q instanceof Parallelogram){
Parallelogram p = (Parallelogram) q; //向下转型操作
}
//判断父类对象是否为Square子类的一个实例
if(q instanceof Square){
Square s = (Square) q; //进行向下转型操作
}
//System.out.println(q instanceof Anything);
}
}
5. 方法的重载
方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。
6. 多态
利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。
例:
创建Quagrangle类,再分别创建两个内部类Square和Parallelogramle,他们都继承了Quagrangle类。编写draw()方法,该方法接收Quadrangle类的对象作为参数,即使用这两个内部类的父类作为方法参数。在主方法中分别以两个内部类的实例对象作为参数执行draw()方法
public class Quadrangle{
//实例化保存四边形对象的数组对象
private Quadrangle[] qtest = new Quadrangle[6];
private int nextIndex = 0;
public void draw(Quadrangle q){ //定义draw()方法,参数为四边形对象
if (nextIndex < qtest.length){
qtest[nextIndex] = q;
System.out.println(nextIndex);
nextIndex++;
}
}
public static void main(String[] args){
//实例化两个四边形对象,用于调用draw()方法
Quadrangle q = new Quadrangle();
q.draw(new Square()); //以正方形对象为参数调用draw()方法
q.draw(new Parallelogramgle()); //以平行四边形对象为参数调用draw()方法
}
}
//定义一个正方形类,继承四边形类
class Square extends Quadrangle{
public Square(){
System.out.println("正方形");
}
}
//定义一个平行四边形类,继承四边形类
class Parallelogramgle extends Quadrangle{
public Parallelogramgle(){
System.out.println("平行四边形");
}
}
正方形
0
平行四边形
1
7.抽象类与接口
通常可以说四边形具有4条边,或者更具体一点,平行四边形是具有对边平行且相等特性的特殊四边形,等腰三角形是其中两条边相等的三角形,这些表述都是合乎情理的,但对于图形对象却不能使用具体的语言进行描述,它有几条边,究竟是什么图形,没人能说清楚,这种类在Java中被定义为抽象类。
7.1 抽象类
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。在Java语言中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一种具体的图形,但它的子类却可以。
抽象类的语法如下:
public abstract class Test{
abstract void testAbstract(); //定义抽象方法
}
其中,abstract是定义抽象类的关键字。
7.2 接口
接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。对于7.1中一流的问题,可以将draw()方法封装到一个接口中,使需要draw()方法的类实现这个接口,同时也继承图形类,这就是接口存在的必要性。
接口使用interface关键字进行定义,其语法如下:
public Interface drawTest{
void draw(); //接口内的方法,省略abstract关键字
}
一个类实现一个接口可以使用implements关键字,代码如下:
public class Parallelogram extends Quadrangle implements drawTest{
...//
}
在接口中定义的任何字段都自动是static和final的。
修改上例,将多态技术与接口相结合:
例:在项目中创建 QuadrangleUseInterface类,在类中创建两个继承该类的内部类ParallelogramgleUseInterface和SquareUseInterface;再创建drawTest接口,并使前两个内部类实现该接口;然后再主方法中分别调用这两个内部类的draw()方法。
package notebook;
interface drawTest{ //定义接口
public void draw(); //定义方法
}
//定义平行四边形类,该类继承了四边形类,并实现了drawTest接口
class ParallelogramgleUseInterface extends QuadrangleUseInterface implements drawTest{
public void draw(){ //由于该类实现了接口,所以要覆盖draw()方法
System.out.println("平行四边形.draw()");
}
void doAnyThing(){ //覆盖父类方法
//SomeSentence
}
}
class SquareUseInterface extends QuadrangleUseInterface implements drawTest{
public void draw(){ //由于该类实现了接口,所以要覆盖draw()方法
System.out.println("正方形.draw()");
}
void doAnyThing(){ //覆盖父类方法
//SomeSentence
}
}
class AnyThingUseInterface extends QuadrangleUseInterface{
void doAnyThing(){ //覆盖父类方法
//SomeSentence
}
}
package notebook;
public class QuadrangleUseInterface{ //定义四边形类
public void doAnyTthing(){
//SomeSentence
}
public static void main(String[] args){
drawTest[] d = { //接口也可以进行向上转型操作
new SquareUseInterface(), new ParallelogramgleUseInterface()
};
for (int i=0; i<d.length; i++){
d[i].draw(); //调用draw()方法
}
}
}
正方形.draw()
平行四边形.draw()
在Java中不允许多重继承,但使用接口就可以实现多重继承,因为一个类可以同时实现多个接口,这样可以将所有需要继承的接口放置在implements关键字后并使用逗号隔开,但这可能会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。