面向对象程序的三大特性之继承性:继承性的主要作用就是复用代码.继承性也有一定的限制,如图一
继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。比如可以先定义一个类叫“车”,车有以下属性:车体大小,颜色,方向盘,轮胎,而又由车这个类派生出轿车和卡车两个类,为轿车添加一个小后备箱,而为卡车添加一个大货箱。
1、我们在第2课第2节_Java面向对象编程_封装性_P 中定义了persion.我在这代码基础上定义一个学习类,并继承persion类。
class Persion{
private int age;/* private:表示私有属性,外部无法直接访问,只有本类内可以访问 */
/* public:公有属性 */
/* 只能通过 setAge 间接来访问 */
public void setAge(int age){
if (age < 0 || age > 200)
age = 0;
else
this.age = age;
}
public int getAge(){
return age;
}
}
/* 学生也是人,他也有上面person这个类的一些特性,如果重写太浪费时间 */
/* 所以这个学生类可以去继承上面人这个类,直接在类后面加上extends关键字加上父类名即可 */
class Student extends Persion{
}
public class Ext{
public static void main(String args[]){
Student stu = new Student();
stu.setAge(10);/*这里实例化子类后就可以直接使用父类提供的public方法了*/
System.out.println(stu.getAge());
}
}
编译运行结果
2、在上面的代码基础上,我们的子类继承了父类,我们可以添加自己的属性还有方法.
上面子类继承了父类并可以实现直接调用父类提供的公有成员方法和成员变量,但是上面的子类什么也没做,一般继承的子类不仅是为了直接使用父类的功能,子类会在父类的现有的功能扩展出更多的功能,或者复写父类的功能,如下程序。
class Persion{
private int age;
public void setAge(int age){
if (age < 0 || age > 200)
age = 0;
else
this.age = age;
}
public int getAge(){
return age;
}
}
class Student extends Persion{
private String school;
public void setSchool(String school){
this.school = school;
}
public String getSchool(){
return school;
}
/* 构造方法,在实例化的时候就可以直接设置,不必再另外调用方法 */
public Student(String school){
this.school = school;
}
}
public class Ext2{
public static void main(String args[]){
Student stu = new Student("ustc");
stu.setAge(10);
System.out.println(stu.getAge());
System.out.println(stu.getSchool());
}
}
编译运行结果
3、子类继承父类的方法和属性都可以进行覆写,我们在子类覆写父类的printInfo方法。
上面这段代码不仅可以使用父类提供的设置年龄的功能,同时也可以使用子类设置学校和获取学校名的功能。如果父类已经有了自己的成员方法“printInfo”,用于打印年龄,那子类里面同样也需要这样一个打印信息的方法,但不止是想只能打印年龄,还想要打印学校名怎么办呢,这时可以引入一个复写的概念,具体看下例程序。
class Persion{
private int age;
public void setAge(int age){
if (age < 0 || age > 200)
age = 0;
else
this.age = age;
}
public int getAge(){
return age;
}
public void printInfo(){
System.out.println("age = " + age);
}
}
class Student extends Persion{
private String school;
public void setSchool(String school){
this.school = school;
}
public String getSchool(){
return school;
}
public Student(String school){
this.school = school;
}
/* 如果不提供这个方法,则自动调用父类的 */
/* 复写父类中与之同名的方法 */
public void printInfo(){
System.out.println("school = " + school + " age = " + getAge());
}
}
public class Ext3{
public static void main(String args[]){
Student stu = new Student("ustc");
stu.setAge(10);
System.out.println(stu.getAge());
System.out.println(stu.getSchool());
stu.printInfo();/* 调用子类复写的新方法 */
}
}
编译运行结果
4、实例化子类对象时,先调用父类的构造方法,再调用子类的构造方法,super()函数在子类中可以指定调用父类构造函数的类型。
上面的Student子类实现了一个构造方法,当去调用这个子类的构造方法的同时也会去调用到父类的构造方法,如果此时父类里面没有构造方法,那么系统会帮我们自动调用一个空的构造方法,里面什么也不做,如果我们写了很多构造方法,那会自动调用无参的构造方法,当然我们也可以用super()这个方法来指定调系统调用哪个父类中的构造方法,下面具体看例程的实现。
class Persion{
private int age;
public void setAge(int age){
if (age < 0 || age > 200)
age = 0;
else
this.age = age;
}
public int getAge(){
return age;
}
public void printInfo(){
System.out.println("age = " + age);
}
public Persion(){System.out.println("Persion()");};
public Persion(int age){
System.out.println("Persion(int age)");
this.age = age;
}
}
class Student extends Persion{
private String school;
public void setSchool(String school){
this.school = school;
}
public String getSchool(){
return school;
}
public Student(String school){
/* 会调用父类无参构造方法,即super */
//super();//如果想调用父类无参的构造函数,这一行写不写都一样会调用
/* 指定调用有int参数的父类构造函数 */
super(5);
System.out.println("Student(String school)");
this.school = school;
}
public void printInfo(){
System.out.println("school = " + school + " age = " + getAge());
}
}
public class Ext4{
public static void main(String args[]){
Student stu = new Student("ustc");
//stu.setAge(10);
System.out.println(stu.getAge());
System.out.println(stu.getSchool());
stu.printInfo();
}
}
编译运行结果
那么这里还有个问题,就是假设父类有些内容可以继承,有些内容为私有的不想继承可不可以呢,答案是肯定行的。这里举个很简单的例子,比如说父亲有私房钱,儿子是不能直接随便拿到父亲的私房钱的,但是父亲可以开通某个接口,让儿子从那里去拿,做一些限制,这也是面向对象的优势体现,考虑的非常全面。再比如父亲有身上有祖传的功夫,他想继续往下传宗接代怎么做呢,同样这里可以通过 public 和 private 来实现。当然儿子也可以继续发扬光大或者自己也能开发出自己新的技能。
class Father{
private int money;//私房钱儿子没有权限访问
/* 提供接口 */
public int getMoney(){return money;}
public void setMoney(int money){this.money = money;}
/* private私有的绝招,如果是public即可以使用,继承下去 */
public void printInfo(){System.out.println("This is Father");}
/* 私有的方法:父亲独有的,不传递 */
private void printInfo2(){System.out.println("This is Father");}
}
class Son extends Father{
/* 继承后复写发扬光大,这个继承过来的不能变为私有的,即不能加private */
public void printInfo(){System.out.println("This is Son");}
/* 子类能不能实现一个跟父类里面私有方法的同名方法呢:可以 */
/* 但是不再是对父类方法的复写了,因为根本不知道父类有这个方法,这是自己的东西了 */
public void printInfo2(){System.out.println("This is Father");}
}
public class Ext5{
public static void main(String args[]){
Son son = new Son();
//son.money = 100;//不能这么直接操作父亲的钱
son.setMoney(100);//通过指定接口使用父亲的钱
son.getMoney();
son.printInfo();//父亲传递下来的,儿子继承了并发扬光大
}
}
5、抽象类规定子类必须实现的方法,起“模板”作用,缺点不能实例化对象,子类必须覆写全部抽象方法
接下来再引入一个 “抽象类” 的概念,抽象类在编程中主要起到一个模板的作用,只需要在类名的前面加上关键字“abstract”就可以了,作为抽象类不能去实例化对象,所以它必须去实现它的子类,也就是说我们不能再对抽象类进行类似这样的操作“Father f = new Father();” ,由于抽象类必须要实现子类,所以说他起了模板的作用,当然,我们可以在抽象类的“成员方法”前面也加上“abstract”关键字,来告诉子类必须实现这个同名方法,具体看下面的例程。
abstract class Father {
private int money;//私房钱儿子没有权限访问
/* 提供接口 */
public int getMoney() {return money; }
public void setMoney(int money) {this.money = money; }
/* 如果子类继承这个类,那么他必须实现这个抽象类方法 */
/* “抽象方法”,属于一种不完整的方法,只含有一个声明,没有方法主体 */
public abstract void study();
}
class Son extends Father{
/* 实现抽象类的时候就可以把 abstract 关键字去掉了 */
public void study() {System.out.println("I am study"); }
}
public class Ext6 {
public static void main (String args[]) {
//Father f = new Father();
Son son = new Son();
son.study();
}
}
编译运行结果:
6、作用:跟抽象类相似,起“模板”作用;子类可以继承多个接口,突破“单继承”的限制
还有一个知识点,他跟抽象类的概念非常相似,我们之前讲的继承,都是只能单继承的形式,就是不能一个子类继承多个父类,但是为了弥补这一点,java有“接口”的定义,Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。接口通用也起到程序模板的作用,下面以程序来说明“接口”。
abstract class Father{
private int money;
public int getMoney(){return money;}
public void setMoney(int money){
this.money = money;
}
/* 如果子类继承这个类,那么他必须实现这个抽象类方法 */
public abstract void study();
}
/* 接口类以 interface 关键字作为开头 */
interface A{
/* 全局常量 */
public static final int i = 10;
public abstract int getNum();/* 只能是抽象方法 */
}
interface B{
public static String name = "InterfaceB";
public abstract String getName();
}
/* 以 implements 来指示继承哪个接口 */
/* 这里相当于继承了AB两个特殊的类 */
/* 还可以同时继承一个父类 */
class Son extends Father implements A,B{
/* 需要去实现AB两个接口类的方法 */
public int getNum(){return i;}
public String getName(){return name;}
public void study(){
System.out.println("I am study");
}
}
public class Ext6{
public static void main(String args[]){
Son son = new Son();
System.out.println(son.getName());
System.out.println(son.getNum());
son.study();
}
}
编译运行结果:
这里再简单说下 “抽象类” 和 “接口” 的一些区别,抽象类可以定义常量也可以定义变量,还可以定义普通的方法。接口它只能够定义常量,一个全局的常量,即使没加 final(这个关键字等下说明) 也是常量,接口里面的方法定义即使不加抽象关键字 abstract,它仍然是一个抽象的方法。抽象类比接口能够容纳更多的内容,但是接口可以突破单继承的限制,他们一般用来起模板的作用
最后再说下上面提到的 “final” 这个关键字,final(最终的)关键字,用他来修饰的时候就表示是最终的类,它不能再有子类。 一个方法用final来修饰的时候,就表示它不能再被复写。变量是final的话,就表示他不能再被修改,那么他就是常量。