您的位置:首页 > 编程语言 > Java开发

java面向对象思想之继承、多态

2009-08-17 08:57 627 查看
一、 类的访问修饰符
1.1 类的访问修饰符
Ø public:可以被所有的类访问
Ø default: 不加访问说明符时,如:class TestClass{……},只能被同一个包的类访问,其它包的类如访问将报错
Ø final:不能被继承,如:String
Ø abstract:抽象类
1.2 方法的访问修饰符
Ø public(公共的)
Ø protected(保护型的):主要体现于父类和子类继承关系
Ø default(缺省的): 不加访问说明符时,如:class TestClass{void output(){……}}
Ø private(私有的)
这几个是属于访问控制修饰符,除此之外,还有其它几个修饰符
Ø static
Ø final
Ø abstarct
Ø native
Ø synchronized
对于前面四个访问控制修饰符,在同类、同包不同类和继承上的访问权
限是怎么样?
public
protected
default
private
同类




同包(不同类)



子类(不同包)


通用性

现在来定义一个类,以实例来说明同类的访问权限:
public class test {
public void pubMethod(){
System.out.println("pubMethod");
}

public void proMethod(){
System.out.println("proMethod");
}

void defMethod(){
System.out.println("defMethod");
}

public void priMethod(){
System.out.println("priMethod");
}

public static void main(String args[]){
test t = new test();
t.pubMethod();
t.proMethod();
t.defMethod();
t.priMethod();
}
}
输出结果: pubMethod
proMethod
defMethod
priMethod
二、 类的继承和派生
1.1 类的继承和派生说明
类的继承是JAVA面向对象程序设计很重要的思想,理解继承是理解面向对象思想的关键。继承的核心就是软件重用的思想。
类的继承和派生的层次关系,可以说是人类对自然界中事物进行分类、分析和认识的过程在程序设计的体现。现实世界中的事物都是相互联系、相互作用,直接或间接,人类在认识过程中,根据它们的实际特征,捉住其共同特性和细小差别,利用分类的方法进行分析和描述。比如下图的交通工具分类层次图。这个分类反映了交通工具的派生关系,最高层是抽象程序最高的,是最具有普通和一般意义的概念,下层具有了上层的特性,同时加入了自己的新特性,而最下层是最为具体的。在这个层次中,由上而下,是一个具体化、特殊化的过程;由下到上,是一个抽象化的过程。上下层之间的关系就可以看作是基类和派生类的关系。

交通工具
火车
汽车
飞机
轮船
卡车
旅行车
小汽车
工具车
轿车
面包车
在Java中,通过关键字extends继承一个已有的类,被继承的类称为父类(超类,基类),新的类称为子类(派生类)。而且在java中一个子类不能有多个父类,也就是说不充许多继承,这一点和C++不同的。
1.2 访问控件修饰符
当一个父类派生出一个子类时,子类除了具有父类的征性外,还加入本身的新特性,是”青于蓝而胜于蓝”。如果说子类没有新特性的话,那么继承是没有任何意义的。
那是不是说父类所有的属性和方法都可以被父类派生的子类所继承呢?刚才在前面提到了四种访问控制修饰符,分别是public、protected、default、private,可用来表示一个类属性和方法的访问控制权限。
现在来定义父类A和其派生的子类B两个类(注意:两个类不在同一包中):
package a;
public class A {
public int pubAtt; //公共属性
protected int proAtt; //保护型属性
int defAtt; //缺省属性
private int priAtt; //私有属性

public void pubMethod(){ //公共函数
System.out.println("pubMethod");
}
protected void proMethod(){ //保护型函数
System.out.println("proMethod");
}
void defMethod(){ //缺省函数
System.out.println("defMethod");
}
private void priMethod(){ //私有函数
System.out.println("priMethod");
}
}

package b;
import a.A;
public class B extends A {
public static void main(String args[]){
B b = new B();
b.pubAtt = 1 ;
b.proAtt = 1;
b.defAtt = 1;
b.priAtt = 1;
b.pubMethod();
b.proMethod();
b.defMethod();
b.priMethod();
}
}
编译的结果(用的是Eclipse编译器):
Exception in thread "main" java.lang.Error: 无法解析的编译问题:
字段 A.defAtt 不可视
字段 A.priAtt 不可视
类型 A 中的方法 defMethod()不可视
类型 A 中的方法 priMethod()不可视
从这个实例可以说明了子类是不能继承父类default、private访问控制修
饰符修饰的属性和函数,只能继承private、protected修饰的属性和函数。
三、 多态
1.1 方法的覆盖(override)
在子类中定义一个与父类同名、参数个数、参数顺序、参数类型均相同一个方法,称为方法的覆盖。覆盖除了发生在方法外,也可以发生在属性。覆盖发生在子类与父类之间。
现在来定义父类A和其派生的子类B两个类:
package a;
public class A {
public void output(){
System.out.println("A's output method");
}
}

package b;
import a.A;
public class B extends A {
public void output(){
System.out.println("B's output method");
}
public static void main(String args[]){
B b = new B();
b.output();
}
}

编译结果:
B's output method
如果子类实例调用一个函数时,如果该函数子类和父类都实现,那么调用子类的;如果子类没有实现,那么调用父类的。
1.2 super变量
覆盖发生在父类和子类之间,当父类和子类出现同名、参数个数、参数顺序、参数类型均相同的方法及属性时,父类的方法和属性被子类所覆盖。当子类对象调用这样的方法或属性时,调用的是子类的,不是父类的。
这样说那是不是父类的被覆盖的方法和属性是不是在子类中不能被调用呢?
在JAVA中有个super的关键字,super是一个变量,类似于this。super代表父类对象变量,提供了对父类的访问,this代表子类对象变量。子类的构造函数或成员函数是可以利用super变量来调用父类中被覆盖的方法或属性。当然,在子类中super变量也可以调用父类没有被覆盖的方法或属性,只是这种情况下的调用和this变量调用是一样的。
现在来定义父类A和其派生的子类B两个类:
package a;
public class A {
public void print1(){
System.out.println("A print1()");
}

public void print2(){
System.out.println("A print2()");
}
}

package b;
import a.A;
public class B extends A {
public void print1(){ //覆盖了A类的print1()方法
super.print1(); //利用super变量调用父类的print1()方法
super.print2(); //这里的super.print2()等同于this.print2(),因为父类的print2()没有被覆盖
this.print2();
System.out.println("B print1()");
}
public static void main(String args[]){
B b = new B();
b.print1();
}
}

编译的结果:
A print1()
A print2()
A print2()
B print1()
每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
现在来定义父类A和其派生的子类B两个类:
package a;
public class A {
A(){
System.out.println("A construct");
}
}

package b;
import a.A;
public class B extends A {
B(){
System.out.println("B construct");
}
public static void main(String args[]){
B b = new B();
}
}

编译结果:
A construct
B construct
可以发现父类A先于子类B先实例化,由于子类B继承了父类A,对子类B进行实例化时,必须先实例化父类A,当然这个由系统代劳,这样子类的构造函数才能调用B。要有孩子,先要有父亲。比如:
package a;
public class A {
protected String name;
public void print(){
System.out.println("A print");
}
}

package b;
import a.A;
public class B extends A {
B(){ //实现子类构造函数调用父类的print()方法
super.print(); //调用父类的print()方法,调用前父类已经被实例化
super.name="This is test!"; //调用父类的name属性,调用前父类已经被实例化
System.out.println(name);
}
public static void main(String args[]){
B b = new B();
}
}

编译结果:
A print
This is test!
如果说父类构造函数带参的情况下,子类的构造函数必须实现对父类的初始化,如果在子类构造函数第一条语句由系统隐含地调用super()或人为书写了super(),但super()中的形参个数、顺序、类型在父类中匹配不到这样的一个构造函数时,系统会报错。例如:
package a;
public class A {
private String name;
A(String name){
this.name = name;
}
}

package b;
import a.A;
public class B extends A {
B(){
super("ddxkj"); //如果屏蔽该语句,会出错
}
public static void main(String args[]){
B b = new B();
}
}
1.3 多态
在JAVA中,我们可以通过两种方式创建多态型引用:利用继承和利用接口。本节只描述利用继承实现多态。利用继承实现多态是通过覆盖父类的方法来实现,在运行时根据传递的对象引用,来调用相应的方法。要实现多态性引用要满足两个条件:
Ø 存在着继承关系
Ø 一个类被一个或多个类继承后,父类最小要有一个函数被子类所覆盖
例如:
package a;
public class A {
public void print(){
System.out.println("A print()");
}
public static void main(String args[]){
Object obj = new A();
obj.print();
}
}
编译的结果:
Exception in thread "main" java.lang.Error: 无法解析的编译问题:
没有为类型 Object 定义方法 print()
因为Object类根本没有print()这个函数,print()方法是存在A类,
所以虽然两个存在继承关系,但print()方法没有被覆盖
通常,一个引用型变量的类型就是它引用对象的类型。比如:B b = new B(),那么我们就可以说定义了b引用,这个引用指向了一个B类实例化对象。一个多态型的引用可以在程序不同的运行时间指向不同类型的对象,当然这里的对象是父类或子类的对象。
在JAVA中,一个引用变量可以引用任何继承自该类的对象。这种关系在整个类继承层次上都有效。如果把这一思想推广到极至,那么Object类实例可以引用任何对象,因为所有的类都是Object类的子类,Object类是所有类的老祖宗。
1.4 instanceof运算符
instanceof运算符用来表示一个对象是否是一个类的实例,是返回true,否则返回false。例如:
package a;
public class Animal {
public void walk(){
System.out.println("Animal walk");
}
}

package b;
import a.Animal;
public class Person extends Animal {
public void walk(){
System.out.println("Perpon walk");
}
public static void main(String args[]){
Animal an = new Animal();
Person per = new Person();
if(an instanceof Animal){
System.out.println("an is Animal's instance");
}
if(per instanceof Person){
System.out.println("per is Person's instance");
}
if(an instanceof Person){
System.out.println("an is Person's instance");
}else{
System.out.println("an is not Person's instance");
}
if(per instanceof Animal){
System.out.println("per is Animal's instance");
}else{
System.out.println("per is not Animal's instance");
}
}
}

编译结果:
an is Animal's instance
per is Person's instance
an is not Person's instance
per is Animal's instance
该实例中,类A被类B所继承,为什么子类B的对象是父类的实例?在JAVA中有这样的规定,子类对象一定是父类的实例,所以per对象除了是本身实例之外,还一定是Animal类实例。但反过来却不行呢?不是说不行,是此该不行。an这个对象是对象引用变量,此时此该它指向Animal类的对象,an是Animal类实例,不是Person类实例。如果要做到” an is Person's instance”,只需an对象引用指向Person类的对象就行了,在Person per = new Person()语句加上an = per;就行了。
总之,子类对象一定是本身实例,也一定是父类实例;父类对象一定是本身实例,但不一定是子类实例,如果父类对象引用指向子类对象,就是子类实例,否则不是。
多态引用的好处就在这里,它所调用的方法是动态的,不是在编译时定死的。父类对象除了可以调用自身的方法,也可以调用子类的方法。这里的方法指父类方法被子类覆盖的所有方法。子类对象也是可以,要强制转换,但不建议,很容易出问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: