重写、多态、抽象类、final及接口

重写

方法重写:        

指的是在子类中重新定义已有的方法

重写的方法必须同名同参同返回值

举个例子

例1:

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

class B extends A {
	public void f() {
		super.f(); // 调用从父类继承过来的f方法
		System.out.printf("BBBB\n");
	}
}

public class Test {
	public static void main(String[] args) {
		B bb = new B();
		bb.f();
	}
}

输出结果为:

AAAA

BBBB

本程序的第9行依旧可以调用父类中的f()方法,严格的讲,重写这个词与其功能并不一致

如果子类中有函数重载,则继承过来后,又有一些情况(这个不重要)

示例如下

例2:

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

	public void f(int i) {
		System.out.printf("ZZZZ\n");
	}
}

class B extends A {
	public void f() {
		super.f(); // 调用从父类继承过来的f方法
		f(10);//在B中也有被继承过来的带参方法
		System.out.printf("BBBB\n");
	}
}

public class Test {
	public static void main(String[] zhangsan) {
		B bb = new B();
		bb.f();
	}
}

输出结果为:

AAAA

ZZZZ

BBBB


覆盖方法时,不能使用比父类中被覆盖的方法更严格(权限更小)的访问权限

子类中不允许出现与父类同名同参但不同返回值的方法,如果出现了,编译时会报错

如果子类对继承自父类的方法不满意,就可以重写父类的方法

如下例所示

例3:

class Human {
	private String name;
	private int age;

	public Human() {
	}

	public Human(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getInfor() {
		String strInf = name + ": " + age;
		return strInf;
	}
}

class Student extends Human {
	public String school;

	public Student() {
	}

	public Student(String name, int age, String school) {
		super(name, age);
		// this.name = name; //error 因为name时私有的
		// this.age = age; //error 同上
		this.school = school;
	}

	public void setSchool(String school) {
		this.school = school;
	}

	public String getInfor() {
		// String strInf = name + ": " + age + ": " + school; //error 因为age是私有的
		String strInf = super.getInfor() + ": " + school;
		return strInf;
	}
}

public class Test {
	public static void main(String[] args) {
		Student st1 = new Student("东方不败", 24, "笑傲江湖");
		System.out.printf("%s\n", st1.getInfor());
	}
}

输出结果为:

东方不败: 24: 笑傲江湖

在本程序中,在Human中故意将name和age定义为private的属性,最终通过重写实现了所要实现的功能(输出了姓名和年龄),这个程序没什么实际含义,仅仅是举个例子

多态

一个父类的引用类型变量,既可以指向父类对象,也可以指向子类对象,它可以根据当前时刻指向的不同,自动调用不同对象的方法,这就是多态

举个例子

例4:

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

class B extends A {
	public void f() {//重写方法的权限不能低于被重写方法的访问权限
		System.out.printf("BBBB\n");
	}
}

public class Test {
	public static void main(String[] args) {
		A a = new A();
		B b = new B();

		a.f();
		b.f();

		a = b; // 把b当做a来看待 因为子类可以当做父类看待,所以本语句OK
		// b = a; // 把a当做b来看待, 因为父类不能当做子类看待,所以本语句error
		a.f();
	}
}

输出结果为:

AAAA

BBBB

BBBB

利用多态,可以用同一段代码做不同的事,即便以后再添加新的类,依旧可以用原来的代码

5

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

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

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

class D extends C {
	public void f() {
		System.out.printf("DDDD\n");
	}
}

public class Test{
	public static void g(A aa) {
		aa.f(); // 类似于C语言的: (*aa).f();
	}

	public static void main(String[] args) {
		A aa = new A();
		B bb = new B();
		C cc = new C();
		D dd = new D();

		g(aa);
		g(bb);
		g(cc);
		g(dd);
	}
}

输出结果为:

AAAA

BBBB

BBBB

DDDD

本程序中,解决输出问题的是第25到28行代码


注意:子类对象可以直接赋给父类引用,但父类对象在任何情况下都不可直接赋给子类对象引用,因为子类是父类的一种,但父类不是子类的一种,比如说,狗是动物的一种,但动物不是狗的一种

通过父类引用只能访问子类对象从父类继承过来的成员

通过父类引用不能访问子类对象所特有的成员


父类引用永远不能直接赋给子类引用:

    只有在父类引用本身指向的就是一个子类对象时,才可以把父类引用转化为子类引用对象

例6:

class A {
}

class B extends A {
}

public class Test {
	public static void main(String[] args) {
		A aa = new A();
		B bb = new B();
		// bb = aa; //error
		// bb = (B)aa; //12行 编译没有错误,但运行时出错! 因为aa指向的是父类对象
		aa = bb;
		bb = (B) aa;

		A aa2 = new B();
		// bb = aa2; //error 永远不可以把父类引用直接赋给子类引用
		bb = (B) aa2; // OK 因为aa2 本身指向的就是一个B类对象 所以可以进行强制转化,注意与24行的区别
	}
}

本程序没有实际意义,第13、14行代码等同于16、18行代码

抽象类

抽象类通常用来作为一个类族的最顶层父类,而最底层的类则用来表示现实中的具体事务,最顶层的类(抽象类)表示该类族所有事物的共性

例如,像下图这样:

抽象方法

    在定义方法时可以只给出方法头,而不给出方法内部的实现代码,这样的方法就是抽象方法

凡是没有方法体的方法(抽象方法)必须使用abstract修饰

抽象类   

    凡是含有抽象方法的类就是抽象类

    抽象类也必须使用abstract关键字修饰

    当然,有抽象方法的类一定是抽象类,但抽象类也可以没有抽象方法,只是后者没什么意义,比较少见


下面举个例子

例7:

//有抽象方法的类一定是抽象类
abstract class A {
	abstract public void f(); // 没有方法体的方法叫做抽象方法, 抽象方法要求末尾必须得加分号,前面必须得加abstract
}

// 抽象类不一定有抽象方法
abstract class B {
	public void g() {
	}
}

public class Test {
	public static void main(String[] args) {
	}
}

这个程序也没有什么实际意义,只是举例说明一下概念


下面使用抽象类在说一下多态的问题

例8:

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

//如果实现了父类中的抽象方法,则可以不用再将子类声明为抽象类型
class B extends A {
	public void f() {
		System.out.printf("BBBB\n");
	}
}

//如果没有实现父类中的抽象方法,则子类也必须声明为 abstract类型
abstract class C extends A {
}

public class Test {
	public static void main(String[] args) {
		// A aa = new A(); //error 18行

		B bb = new B(); // OK
		bb.f(); // OK

		A aa; // 23行 OK 可以定义一个抽象类的引用,但是不可以定义一个抽象类的对象,所以18行error, 本行OK
		aa = bb;
		aa.f();
	}
}

输出结果为:

BBBB

BBBB

final

final可以修饰

1、整个类

表示该类不能被继承

如果认为一个类已经很完美且不需要子类来继承它时,可以使用final

格式:

public final class A{......}

public和final的位置可以互换

2、类中的成员(包括属性和方法)

    final修饰类中的属性表示该属性必须被赋值并且只能被赋一次值(注意默认值不算真正的赋值)

final修饰的属性初始化有两种(只能使用一种):

(1)在定义成员变量的同时初始化

(2)在类的构造方法中初始化

下面举例子

例9:

class A {
	final public int i; // = 10  常变量

	public A() {
		i = 10;
	}

	public void f() {
		// i = 22;
	}
}

class Test {
	public static void main(String[] args) {
		A a = new A();
		System.out.printf("i = %d\n", a.i);
	}
}

本程序的第五行代码和第二行代码注释部分的 = 10功能相同

例10:

class A {
	public final void f() { // 如果在public前面加final,则编译时就会报错
		System.out.println("AAAA");
	}
}

class B extends A {
	// public void f()
	// {
	// System.out.println("BBBB");
	// }
}

public class Test {
	public static void main(String[] args) {
	}
}

注意,本程序和上面那个程序的public和final前后位置不同,但都正确

接口

就是抽象方法和常量值的集合。从本质上讲,接口是一种特殊的抽象类

接口的格式:

[public]interface interfacename [extends SuperlnterfaceList]

其中extends SuperlnterfaceList是继承超类(父类)接口列表

一个类只能实现某个接口,不能继承某个接口

下面举个例子

例11:

interface It1 {
	public static final int i = 20;

	public abstract void f();
}

interface It2 {
	int i = 20;

	void f();
}

class A implements It2 // implements不能改为extends 因为类可以继承类,但类不能继承接口,逻辑意义不通,
// 类可以实现接口
{
	public void f() {
		// i = 99; //error
		System.out.printf("i = %d\n", i);
	}
}

class Test {
	public static void main(String[] args) {
		A aa = new A();
		aa.f();
	}
}

输出结果为:

i = 20


接口中定义的属性必须是public static final的,而接口中定义的方法则必须是public abstract的,因此这些修饰符可以部分或全部省略

接口中定义的属性的值在实现类中不能被改变

例12:

//接口中是不可以定义构造函数的
interface It {
	int i = 10; // 不能改为 int i;
}

class A implements It {
	public A(int j) {
		// this.i = j; // 接口It中的属性i 是public static final 类型,不可以在实现类中被改变
	}
}

class Test {
	public static void main(String[] args) {
		A aa = new A(10);
		System.out.println("aa.i = " + aa.i);
		System.out.println("aa.i = " + A.i);
	}
}

输出结果为:

aa.i = 10

aa.i = 10


一个类可以实现多个接口,但不能继承多个类

而一个接口不但可以继承接口,而且可以继承多个接口(接口名之间要用逗号隔开),即接口允许多继承

如果一个类只实现了一个接口的部分方法,则该类必须声明为抽象类

一个类在继承一个父类的同时可以实现一个或多个接口,不过extends关键字必须放在implements前面

例13:

class A {
}

interface It1 {
}

interface It2 {
}

// 接口可以多重继承,即一个接口可以有多个父接口,但是一个类只能有一个父类,Java类不允许多继承,接口却允许多继承
interface It3 extends It1, It2 {
}

interface It4 {
	int i = 20;
}

// 一个类可以在继承一个父类的同时实现一个或多个接口,但extends关键字必须的在implements 之前
class T extends A implements It4, It3 {
}

class Test {
	public static void main(String[] args) {
		System.out.println("仙人指路!");
	}
}

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

例14:

interface It {
	void f();
}

class A implements It {
	public void f() {
		System.out.printf("AAAA");
	}

	public void g() {
	}
}

class Test {
	public static void main(String[] args) {
		// int i;
		It it;
		it = new A();
		it.f();

		// it.g(); //error
		// It it2 = new It(); //error
	}
}

输出结果为:

AAAA


第18行是一个接口IT的实现类A把它的地址发送给it,则it就指向了该类

接口是抽象的,但是可以定义实现该接口的对象,然后通过接口的引用,调用该类中的成员

通过接口,只能调用子类从父类继承过来的成员,不能调用子类所特有的成员


接口的作用

1、通过接口可以实现不相关类的相同行为

2、接口提供了不同对象进行协作的平台

3、接口可以实现多继承,从一定程度上弥补了类只能单继承的缺陷

4、接口是我们了解一个类功能的重要途径


接口与抽象类的区别

接口中的方法不允许有方法体,但抽象类允许

Java类不允许多继承,但接口可以


【所有代码均在windows系统下eclipse环境JDK 1.7下运行通过】

(如有错误,敬请指正)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值