重写
方法重写:
指的是在子类中重新定义已有的方法
重写的方法必须同名同参同返回值
举个例子
例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下运行通过】
(如有错误,敬请指正)