抽象类
在面向对象的概念中,所有的对象都是通过类来表述的,但并不是所有的类都能够完整的描绘对象,如果一个类中没有包含足够的信息来描绘一类具体的对象,这样的类就是抽象类.抽象类往往用来表征对问题领域进行分析,设计中的出的抽象概念. 是对一系列看上去不同,但本质上相同的具体概念的抽象.例如:定义一个平面图形Shape . 任何平面图形都有周长和面积,在Shape类中定义两个方法用于计算图形的面积和周长
public class Shape{ //计算圆的面积 public void callArea(){} //计算圆的周长 public void callPerimeter(){} }
通过分析,可以发现平面图形领域存在着园,三角形,长方形等这样一些具体的图形,计算它们的面积和周长是不同的,因此在Shape类中无法统一定义callArea()和callPerimeter()方法 , 但是它们都属于平面领域,都需要这两个方法.
有事定义类的"骨架",对其共同行为提供规范,但并不实现,具体的实现将放在它的子类来完成.这种骨架类在java中叫做 抽象类,通过abstract关键字来描述
[访问符] abstract class 类名{ [访问符] abstract <返回类型> 方法名([参数列表]); }
定义抽象类需要注意一下几点:
1、abstract放在class前面,指明该类是抽象类;
2、abstract放在方法声明中,则该方法是抽象方法,抽象方法没有方法体,即未实现;
3、一个抽象类可以含有多个抽象方法,也可以含有已经实现的方法;
例子:抽象类的定义与使用
package 抽象类; //定义图形抽象类 public abstract class Shape { double dim; public Shape(double dim) { this.dim = dim; } //抽象方法,获得面积 public abstract double callArea(); //抽象方法,获得周长 public abstract double callPerimeter(); }
上面的例子是定义了一个抽象类Shape,并且声明了两个抽象方法callArea()和callPerimeter(),这两个抽象方法没有方法体.在这里需要注意,抽象方法是没有方法体,它和空方法是两个不同的概念
public abstract void callAreas(); //抽象方法,没有 {}括起来的方法体 public void callArea() {}; //空方法,有{}括起来的方法体,但方法体中没有任何语句
抽象类还可以包含具体数据和具体方法,也可以包括构造方法,定义抽象类的目的是提供可由其子类共享的一般形式,子类可以根据自身需要扩展抽象类
例子:定义Shape抽象类的一个子类Circle来演示抽象类的使用
package 抽象类; public class Circle extends Shape{ public Circle(double dim) { super(dim); } //实现抽象方法callArea(); public double callArea() { //返回圆的面积 return 3.14 * dim * dim; } public double callPerimeter() { //返回圆的周长 return 2 * 3.14 * dim; } public static void main(String args[]) { //声明一个Shape对象,指向实现它的子类对象 Shape shape = new Circle(10); //调用callArea()求出圆的面积并输出 System.out.println("圆的面积为:" + shape.callArea()); //调用callPerimeter()求出圆的周长并输出 System.out.println("圆的周长为:" + shape.callPerimeter()); } }
执行结果:
圆的面积为:314.0
圆的周长为:62.800000000000004
从执行的结果来看,当调用Shape类型的变量shape时,实际上是调用了Circle类型对象的方法,即通过基类调用派生类的方法,这也是一种多态的体现
一个类在继承抽象类时,如果没有实现所有抽象类方法,那么该类也必须声明为抽象类:
例子:
package 抽象类; public abstract class Square extends Shape{ public Square(double dim) { super(dim); } //实现抽象方法callArea() public double callArea() { return dim * dim; } //没有实现抽象方法callPerimeter(),Square类中仍然包含抽象方法,所以Square依然是抽象类 }
注意:1、抽象类不能实例化,既不能直接new一个抽象类,但可以指向一个实现它的子类对象
2、抽象方法没有方法体,抽象类提供了子类的规范模板,抽象方法必须在子类中给出具体实现。
3、abstract不能与final同时修饰一个类,abstract不能和static、private、final或者native并列修饰同一方法。
接口
java只支持单一继承,不支持多重继承,即一个类只能继承另一个类,不能继承两个或两个以上的类。单一继承限制了类的多重体现,为了弥补这一缺陷,模仿C++中的多重继承,java语言的设计者提出了一种折中的解决办法,即使用接口:一个类可以实现多个接口。接口的引入,使java拥有了强大的面向对象编程能力,为面向接口编程提供了广泛的扩展空间 。
定义接口
<访问符> interface 接口名{ [访问符] <返回类型> 方法名([参数列表]) ...... }
1、interface是定义接口的关键字;
2、接口是一种特殊的抽象类型,是对抽象类的进一步强化,是方法声明和常量的定义集合,因此接口中的方法都没有方法体,即接口中的方法都是未实现的方法,且不需要abstract关键字进行指明
例子:演示接口的定义和使用
package 接口; public interface MyInterface { public void add(int x, int y); public void volume(int x, int y, int z); }
上述代码定义了一个接口MyInterface,在接口 中声明了两个方法,但这两个方法都没有实现,与抽象类中的抽象方法类似,接口中的方法没有方法体,当然也可以定义既包含常量也包含方法的接口
package 接口; public interface MultiInterface { //在接口中定义一个静态常量PI public static final double PI = 3.1415926; public void callArea(); }
在定义接口的时候,接口中的所有方法和常量自动定义为public,可以省略public关键字.
实现接口
实现接口的语法格式如下:
<访问符> class 类名 implements 接口名[接口列表]{ }
1、implement关键字可以实现多个接口,接口之间用逗号隔开。
2、一个类实现接口时,必须实现接口中定义的所有方法,除非将该类定义为抽象类。
例子:定义一个类Myclass实现MyInterface接口,演示接口的实现过程
package 接口; public class MyClass implements MyInterface{ //实现接口中的add()方法 public void add(int x, int y) { System.out.println("x + y = " +(x+y)); } //实现接口中的volume()方法 public void volume(int x, int y, int z) { System.out.println("X * y * z = " + (x*y*z)); } public static void main(String args[]) { //声明一个MyInterface对象,指向MyClass类的对象 MyInterface mi = new MyClass(); //调用add方法,传递两个参数 mi.add(1, 2); //调用volume()方法,传递三个参数 mi.volume(1, 2, 3); } }
执行结果为:
x + y = 3
X * y * z = 6
上面代码中Myclass类实现MyInterface接口 ,并实现该接口中定义的add()和volume()方法.在main()方法中,先声明一个MyInterface接口类型的变量mi , new一个实现该接口的Myclass类的对象,并将其引用赋值给mi ,最后通过mi调用方法.
与抽象类一样,接口是一种更加虚拟的类结构,因此不能对接口实例化
接口与抽象类有很多相似之处,但要注意这几点区别:
1、抽象类中可以有已经实现的方法,而接口不能有
2、接口中定义的变量默认为public static final 型,且必须赋初值,其实现类中不能重新定义,也不能改变其值,即接口中定义的变量都是最终的静态常量;而抽象类中定义的变量 和普通类一样,默认是friendly,其实现类合一重新定义,也可以根据需要改变其值
3、接口中定义的方法默认都是public修饰,也只能是public,而抽象类则是缺省的friendly
instanceof运算符
java的多态性机制导致了引用变量的声明类型和其实际类型引用的类型可能不一致,在结合动态方法调度到机制可以得出以下结论:声明为同种类型的两个引用变量调用同一个方法时也有可能会有不同的行为.为了更准确的鉴别一个对象的真正类型,java语言引入了instanceof操作符.其实使用的格式如下:
<引用类型变量> instanceof <引用类型>
该表达式为boolean类型的表达式,当instanceof左侧的引用类型变量所引用对象的实际类型是其右侧给出的类型或其子类类型时,整个表达式的结果为true,否则为false
举例:演示instanceof的用法
package instanceof运算符; //定义IBase接口 public interface IBase { public void print(); }
package instanceof运算符; //定义类Derive类实现IBase接口 public class Derive implements IBase{ int b; public Derive (int b) { this.b = b; } public void print() { System.out.println("In Derive!"); } }
package instanceof运算符; //定义Derive的子类Derive1 public class Derive1 extends Derive{ int c; public Derive1(int b, int c) { super(b); this.c = c; } public void print() { System.out.println("In Derive1!"); } }
package instanceof运算符; public class InstanceofDemo { //判断对象类型 public static void typeof(Object obj) { if(obj instanceof Derive) { Derive derive = (Derive) obj; derive.print(); }else if(obj instanceof Derive1) { Derive1 derive1 = (Derive1) obj; derive1.print(); } } public static void main(String[] args) { IBase b1 = new Derive(4); IBase b2 = new Derive1(4,5); System.out.print("b1 is:"); //调用typeof()判断b1 typeof(b1); System.out.print("b2 is:"); //调用typeof()判断b2 typeof(b2); } }
执行结果:
b1 is:In Derive!
b2 is:In Derive1!
上述代码中定义了一个IBase接口,Derive类实现IBase接口,Derive1是Derive的子类.在InstanceofDemo类中定义了一个静态方法typeof(),该方法使用instanceof来判断对象的类型,并调用其相应的方法,main()方法中定义了两个Base类型的对象分别是b1和b2所引用的实际对象类型.
从结果不难看出,instanceof运算符能够鉴别出实际对象类型,并实现相应方法的调用,此种方法模拟了方法的动态调用机制.但这种做法被认为没有很好的利用面向对象中的多态性而是采用了结构化编程模式
注意:在大多数情况下不推荐使用此用算符,应当利用累的多态.
对象转换
在基本数据类型之间进行相互转换的过程中,有些转换可以自行完成,而有些转换必须通过强制转换,对于引用类型,也从在相互转换机制,也分为自动转换和强制转换
自动转换:子类转换成为父类,或者实现类转换接口,转换自动完成
强制转换:父类转换成为子类,或者接口转换成实现类,必须使用强制转换.
Person p = new Teacher(); //创建一个Teacher对象,把引用赋予Person类型变量p Teacher t = (Teacher) p; //把变量p强制转换成Teacher类型变量
注意:无论自动转换还是强制转换,都只能用在有继承关系的对象之间.并不是任意类型都可以转换,只有多态的情况下,原本就是子类类型的对象被声明为父类的类型,才可以通过造型恢复其"真实面目",否则将编译失败.