继承与接口——Java程序设计基础

一、子类与父类

使用关键字extends来声明一个类的子类,格式如下:
class 子类名 extends 父类名 {
… 
}

例如:
class Student extends People {
… 
}
把Student类声明为People类的子类、People类是Student类的父类

如果一个类的声明中没有使用extends关键字,这个类被系统默认为是Object的子类。Objectjava.lang包中的类。

二、子类的继承性

所谓子类继承父类的成员变量作为自己的一个成员变量,就好像它们是在子类中直接声明了一样,可以被子类中自己定义的任何实例方法操作,所谓子类继承父类的方法作为子类中的一个方法,就像它们是在子类中直接定义了一样,可以被子类中自己定义的任何实例方法调用。也就是说,如果子类中定义的实例方法不能操作父类的某个成员变量或方法,那么该成员变量或方法就没有被子类继承

1.子类和父类在同一包中的继承性

类继承父类中不是private的成员变量作为自己的成员变量,继承不是private的方法作为自己的方法,继承的成员变量或方法的访问权限保持不变。

 2.子类和父类不在同一包中的继承性

子类继承父类的protectedpublic成员变量做为子类的成员变量,继承父类的protectedpublic方法为子类的方法,继承的成员或方法的访问权限保持不变。如果子类和父类不在同一个包里,子类不能继承父类的友好变量和友好方法

3 protected的进一步说明

子类继承父类的protectedpublic成员变量做为子类的成员变量,继承父类的protectedpublic方法为子类的方法,继承的成员或方法的访问权限保持不变。如果子类和父类不在同一个包里,子类不能继承父类的友好变量和友好方法


 子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访问到的。即只是拥有,但是无法使用,要想使用,要转为protected

 A中的protected成员变量和方法可以被它的直接子类和间接子类继承(不要求同包比如BA的子类,CB的子类,D又是C的子类,那么BCD类都继承了A类的protected成员变量和方法。

比如在Other类中用D类创建了一个对象object,该对象通过 运算符访问protected变量和protected方法的权限如下列(a)、(b)所述:

a)对于子类D自己声明protected成员变量和方法,如果object访问protected成员变量和方法,只要Other类和D类在同一个包中就可以了。

b)如果子类D的对象的protected成员变量或protected方法是从父类继承的,那么就要一直追溯到该protected成员变量或方法的“祖先”类,即A类,如果Other类和A类在同一个包中object对象能访问继承的protected变量和protected方法。

 

三、子类对象的构造过程

子类的构造方法总是先调用父类的某个构造方法,也就是说,如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类的不带参数的构造方法。

用子类创建的对象时,不仅子类中声明的成员变量被分配了内存,而且父类的成员变量也都分配了内存空间,但只将其中一部分(子类继承的那部分)作为分配给子类对象的变量。

子类对象内存示意图如图示意。“叉号”表示子类中声明定义的方法不可以操作这些内存单元,“对号”表示子类中声明定义的方法可以操作这些内存单元。

子类中有一部分方法是从父类继承的,这部分方法却可以操作这部分未继承的变量

代码示例:

class A{
	private int x = 10;
	protected int y = 50;
	
	public void f(){
		y=y*x;
	}
}
 
class B extends A{
	int z=12;
	
	public void g(){
		z=y+100;
		System.out.printf("z=%f\n",z);
	}
}
public class Example2 {
	public static void main(String args[]){

		B b=new B();
		b.f(); //调用从父类继承的方法
	    b.g(); //调用子类自身方法
	}

 四、成员变量的隐藏和方法重写

1.成员变量的隐藏

子类中声明的成员变量和父类中的成员变量同名时,子类就隐藏了继承的成员变量,即子类对象以及子类自己声明定义的方法操作与父类同名的成员变量是指子类重新声明定义的这个成员变量。

子类对象可以调用从父类继承的方法操作隐藏的成员变量。

即,如果类隐藏了继承的成员变量,继承的方法只能操作隐藏的成员变量。

class People {

        public double x;

        public void setX(double x) {
            this.x= X;
        }

        public double getDoublex() {
            return x;
        }


class Student extends People{
        int x;

       public int getX( ){
           //x= 20.56;        //非法,因为子类的x已经是int型,不是double型

           return x;}
}


public class Example5_ 3{

    public static void main(String args[ ]){

        Student stu = new Student( ) ;

        stu.x= 98;     //合法,子类对象的x是int型   
        System. out. println(对象stu的x的值是:" + stu. getX());

        //stu.x= 98. 98;    //非法,因为子类对象的x已经是int型

        stu. setX(98.98);    //子类对象调用继承的方法操作隐藏的double型变量x
        double m = stu. getDoubleX();
                    //子类对象调用继承的方法操作隐藏的double型变重x
        System. out. println( "对象stu隐臧的x的值是:"+ m);

    }
}

 2.方法重写

方法重写是指:子类中定义一个方法,这个方法的类型和父类的方法的类型一致或者是父类的方法的类型的子类型(所谓子类型是指:如果父类的方法的类型是“类”,那么允许子类的重写方法的类型是“子类”),并且这个方法的名字、参数个数、参数的类型和父类的方法完全相同。子类如此定义的方法称作子类重写的方法。

子类通过重写可以隐藏已继承的实例方法(方法重写也称方法覆盖)

重写的目的 :

子类通过方法的重写可以把父类的状态和行为改变为自身的状态和行为。一旦子类重写了父类的方法f,那么子类对象调用方法f一定是调用的是重写方法f。重写方法既可以操作继承的成员变量也可以操作子类新声明的成员变量。如果子类想使用被隐藏的方法,必须使用关键字super,将5.5节讲述super的用法。

Example5_ 4. java

class A{
        double f(float x, float y) {
            return x + y;
        }

        public int g( int X, int y){
            return x+ y;
        }
}

class B extends A {
        double f(float x, float y){
            return X*y;
        }
}


public claass Example_5{
    public static void main(String args[]){

        B b= new B();
        double result= b. f(5,6);    //b调用重写的方法
        System. out. println("调用重写方法得到的结果:”+ result);

        int m=b.g(3,5);    //b调用继承的方法
        System, out, println("调用继承方法得到的结果:" +m);

}

如果子类如下重写方法f将产生编译错误:
float f(float x, float y){

    return x* y;
}

 五、super关键字

1.使用super调用父类的构造方法

子类不继承父类的构造方法如果在子类的构造方法中,没有明显地写出super关键字来调用父类的某个构造方法,那么默认地有:

super();语句,即调用父类的不带参数的构造方法。

子类如果明确使用父类的构造方法,必须在子类的构造方法中使用关键字super调用父类的构造方法而且super必须是子类构造方法中的头一条语句

当我们在父类中定义多个构造方法时,应当包括一个不带参数的构造方法,以防子类省略super时出现错误

代码示例:


class A{
	public int i;
	
	public A(int i) {
		this.i = i;
	}
}

class B extends A{
	public int j;
	
	public B(int i, int j) {
	//	super();
		super(i);
		this.j = j;
	}
}

public class TestSuper_4 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		B bb = new B(1,2);
		System.out.printf("%d\t%d",bb.i,bb.j);
	}

}

 2.使用super操作被隐藏的变量和方法

子类中想使用被子类隐藏的成员变量或方法就可以使用关键字super。比如super.xsuper.play()就是访问和调用被子类隐藏的成员变量x和方法play()

 super调用隐藏的方法时,该方法中出现的成员变量是指被隐藏的成员变量(如图所示)

代码示例:

class A{
    int i;

	public void f() {
		System.out.printf("AAAA");
	}
	
	public void f(int i) {
		System.out.printf("haha");
	}
}

class B extends A{
    int i;

	public void f() {
        super.i = 50;//  使用被隐藏的成员变量  
		super.f();	 //  使用被隐藏的方法
		super.f(10);
		System.out.printf("BBBB");
	}
}
public class Usuper_2  {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		//B.f();
	}

}

六.final关键字

fifinal类:final 类不能被继承,即不能有子类

final方法:不允许子类重写类的final方法。

常量:成员变量或局部变量被修饰为final的,就是常量。常量在声明时没有默认值,所以在声明常量时必须指定该常量的值,而且不能再发生变化

代码示例:

/*
 	final修饰类中的若干属性
		○ final修饰类中的若干属性表示该属性必须且被赋值并且只能被赋一次值
		○ 初始化方式有两种:
					® 在定义成员变量的同时初始化
					® 在类中所有的构造函数中初始化
		○ 注意:
			 一个类的所有普通方法内部都不可以修改final修饰过的成员变量的值

 */

class A{
	final public int i = 10;	//	常变量
	
	public A() {
		//i = 10;	// error	该属性只能被赋一次值
	}
	
	public void f() {
		//i = 22;	error		普通方法不可以修改final修饰过的成员变量的值
	}
	
	public void g() {			//	如果在public前面加final,则编译时会报错,方法只能被继承,不能被重写
		System.out.printf("AAAA");
		
}

public class TestFinal_2 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根

	}

}

七、继承与多态

多态性就是指父类的某个方法被其子类重写时,可以各自产生自己的功能行为。

当一个类有很子类时,并且这些子类都重写了父类中的某个方法。那么当我们把子类创建的对象的引用放到一个父类的对象中时,就得到了该对象的一个上转型对象,这个上转型对象在调用这个方法时就可能具有多种形,因为不同的子类在重写父类的方法时可能产生不同的行为

代码示例:

/*
 		多态
			一个父类的引用类型变量它即可以指向父类对象也可以指向子类对象,
			他可以根据当前时刻指向的不同自动调用不同对象的方法,这就是多态
			
		总结:
			aa可以根据它当前时刻指向的是A类对象还是A子类对象,而自动决定调用哪个对象的f方法,这就是多态

 */
class A{
	public void f() {
		System.out.printf("AAAA\n");
	}
}

class B extends A{
	public void f() {
		System.out.printf("BBBB\n");
	}
}

public class TestPoly_1 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		A aa = new A();
		B bb = new B();
		//	分别调用各自的方法
		aa.f();
		bb.f();
		
		aa = bb;	//	把bb当作aa来看待,因为子类可以当做父类看待,所以本语句ok(父类引用)
		aa.f();
		bb.f();
		//bb = aa;	//	把aa当作bb来看待,因为父类不能被当做子类看待,所以本语句error
	}

}

八、abstract类和abstract方法

用关键字abstract修饰的类称为abstract类(抽象类)。如:

public abstract  class A  {

      public abstract int min(int x,int y);

}

用关键字abstract修饰的方法称为abstract方法(抽象方法),对于abstract方法,只允许声明,不允许实现,而且不允许使用finalabstract同时修饰一个方法,例如:

          public abstract int min(int x,int y)


1. abstract类中可以有abstract方法

2abstract类不能用new运算创建对象

3.  abstract类的子类

如果一个非abstract类是abstract类的子类,它必须重写父类的abstract方法,即去掉abstract方法的abstract修饰,并给出方法体。如果一个abstract类是abstract类的子类,它可以重写父类的abstract方法,也可以继承父类的abstract方法

4. abstract类的对象作上转型对象

可以使用abstract类声明对象,尽管不能使用new运算符创建该对象,但该对象可以成为其子类对象的上转型对象,那么该对象就可以调用子类重写的方法。

5. 理解abstract

 抽象类的语法很容易被理解和掌握,但更重要的是理解抽象类的意义,这一点是更为重要的。理解的关键点是:

1抽象类可以抽象出重要的行为标准,该行为标准用抽象方法来表示。即抽象类封装了子类必需要有的行为标准。

2抽象类声明的对象可以成为其子类的对象的上转型对象,调用子类重写的方法,即体现子类根据抽象类里的行为标准给出的具体行为。

代码示例:

abstract class A{
	abstract public void f();
}

class B extends A{
	public void f() {
		System.out.printf("哈哈\n");
	}
}

public class TestAbstract_2 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		//A aa = new A();		// error  不能new出抽象类对象
		
		B bb = new B();	//	OK
		bb.f();			//	ok
		
		A aa;//	ok  可以定义一个抽象类的引用,但是不可以定义一个抽象类的对象
		aa = bb;
		aa.f();
		
	}

}

九、接口

1.接口的定义与使用

通过使用关键字interface定义接口格式:

  interface  接口的名字  //接口声明

 {

         //接口体        接口体中包含static常量和方法定义两部分

 }


 (1)接口体中的抽象方法和常量

JDK8版本之前,接口体中只有抽象方法,访问权限一定都是public(允许省略publicabstract修饰符)。所有static常量的访问权限一定都是public(允许省略publicfinalstatic修饰符,接口中不会有变量

interface Printable {

       public static final int MAX=100;  //等价写法:int MAX=100;

       public abstract void add();           //等价写法:void add();

       public abstract float sum(float x ,float y); 
        //等价写法:float    sum(float x ,float y);

}

(2)接口体中的default实例方法

JDK8版本开始,允许使用default关键字,在接口体中定义称作default的实例方法(不可以定义defaultstatic方法),default的实例方法和通常的普通的方法比就是用关键字default修饰的带方法体的实例方法。default实例方法的访问权限一定是public(允许省略public修饰符)。

注意不可以省略default关键字,因为接口里不允许定义通常的带方法体的实例方法。

interface Printable {

       public final int MAX = 100;  //等价写法:int MAX=100;

       public abstract void add();   //等价写法:void add();

       public abstract float sum(float x ,float y);

       public default int max(int a,int b) {   //default方法

            return a>b?a:b;

       }

}  

3)接口体中的static方法

JDK8版本开始,允许在接口体中定义static方法

注 不可以用static和abstract同时修饰一个方法。

public interface Printable {

       public static final int MAX = 100;  //等价写法:int MAX=100;

       public abstract void on();   //等价写法:void on();

       public abstract float sum(float x ,float y);

       public default int max(int a,int b) {   //default方法

              return a>b?a:b;

       }

       public static void f ()  {

             System.out.println("注意是从Java SE 8开始的");

      }

}

一个类通过使用关键字 implements 声明自己实现一个或多个接口。如果实现多个接口,用逗号隔开接口名,如A类实现PrintableAddable接口:

class A  implements  Printable ,  Addable {

}

一个类实现了某个接口,那么这个类就自然拥有了接口中的常量,default方法(去掉了default关键字),该类也可以重写接口中的default方法(注意,重写时需要去掉default关键字)。如果一个非abstract类实现了某个接口,那么这个类必须重写该接口的所有abstract方法,即去掉abstract修饰给出方法

特别注意的是,类实现某接口,但类不拥有接口的static方法

2.接口回调

可以把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量中,那么该接口变量就可以调用被类重写的接口方法以及接口中的default方法

实际上,当接口变量调用被类重写的接口方法或接口中的default方法时,就是通知相应的对象调用这个方法。

1)接口可以抽象出重要的行为标准,该行为标准用抽象方法来表示。

2)可以把实现接口的类的对象的引用赋值给接口变量,该接口变量可以调用被该类实现的接口方法,即体现该类根据接口里的行为标准给出的具体行为。 

接口的思想在于它可以要求某些类有相同名称的方法,但方法的具体内容可以不同,即要求这些类实现接口,以保证这些类一定有接口中所声明的方法(即所谓的方法绑定)。接口在要求一些类有相同名称的方法的同时并不强迫这些类具有相同的父类 

代码示例:

/*
 		a. 接口中定义的属性必须是public static final 的,而接口中定义的方法则必须是public abstract的,
 		   因此这些修饰符可以部分或全部省略
		b. 接口中定义的属性的值在实现类中不能被更改
		c. 一个类只能实现某个接口,不能继承某个接口
		d. 但接口可以继承接口
		e. 接口不但可以继承接口,而且可以继承多个接口,即接口允许多继承
		f. 如果一个类实现了一个接口的部分方法,则该类必须得声明为抽象类
		g. 一个类可以在继承一个父类的同时实现一个或多个接口,但extends关键字必须得在implements之前
		h. 不可以new接口对象,但可以定义一个接口引用类型的变量并将其指向实现接口的对象,达到多态的目的

 */
class D{
}

interface It3{
}

interface It4{	
}

//	接口可以多重继承,即一个接口可以有多个父接口,但是一个类只能有一个父类
interface It5 extends It3,It4{	
}

interface It6{
	int i = 20;	//	接口中属性的值的类型是public static final
}

//	一个类可以在继承一个父类的同时实现一个或多个接口,但extends关键字必须必须在最前面
class E extends D implements It5,It6{
}

public class TestInterface_3 {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		System.out.printf("嘿嘿!\n");
	}

}

 3.接口和多态

代码示例:


/*
 	不可以new接口对象,但可以定义一个接口引用类型变量来指向实现接口的对象,达到多态的目的
 	
 */

interface A{
	void f();
}
class B implements A{
	public void f() {		//	public 既不能省略,也不能被修改
		System.out.printf("AAAA");
	}
}

public class TestInterface_7 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
	//	A aa = new A();	//	error	不可以new一个对象
		A aa = new B();	//	但可以定义一个接口引用类型变量来指向实现接口的对象,达到多态的目的	
		aa.f();
	}

}

 接口产生的多态就是指不同的类在实现同一个接口时可能具有不同的实现方式,那么接口变量在回调接口方法时就可能具有多种形态

使用接口进行程序设计的核心思想是使用接口回调,即接口变量存放实现该接口的类的对象的引用,从而接口变量就可以回调类实现的接口方法


1abstract类和接口都可以有abstract方法。

2.接口中只可以有常量,不能有变量;而abstract类中即可以有常量也可以有变量。

3abstract类中也可以非abstract方法,但不可以有default实例方法。接口不可以有非abstract的方法(不是default方法,还带有方法体的方法),但可以有default实例方法。

          在设计程序时应当根据具体的分析来确定是使用抽象类还是接口。abstract类除了提供重要的需要子类去实现的abstract方法外,也提供了子类可以继承的变量和非abstract方法。如果某个问题需要使用继承才能更好的解决,比如,子类除了需要实现父类的abstract方法,还需要从父类继承一些变量或继承一些重要的非abstract方法,就可以考虑用abstract类。如果某个问题不需要继承,只是需要若干个类给出某些重要的abstract方法的实现细节,就可以考虑使用接口。

十、小结

继承是一种由已有的类创建新类的机制。利用继承,我们可以先创建一个共有属性的一般类,根据该一般类再创建具有特殊属性的新类。
所谓子类继承父类的成员变量作为自己的一个成员变量,就好像它们是在子类中直接声明一样,可以被子类中自己声明的任何实例方法操作。
所谓子类继承父类的方法作为子类中的一个方法,就像它们是在子类中直接声明一样,可以被子类中自己声明的任何实例方法调用。
多态是面向对象编程的又一重要特性。子类可以体现多态,即子类可以根据各自的需要重写的父类的某个方法,子类通过方法的重写可以把父类的状态和行为改变为自身的状态和行为。接口也可以体现多态,即不同的类在实现同一接口时,可以给出不同的实现手段。
在使用多态设计程序时,要熟练使用上转型对象或接口回调,以便体现程序设计所提倡的“开-闭”原则

猜你喜欢

转载自blog.csdn.net/Mr_Morgans/article/details/121314345