JAVA自学教程之(继承及super使用)。继承:(extends)
很多类,但是有一些成员,都是重复的,所以为提供复用性,把重复的代码抽取出来,就应用了继承 1.提高了代码的复用性
2.让类与类之间产生了关系,继承给多态提供了前提,没继承就没多态
- class MAN
- {
- String name;
- int age;
- }
- class student extends MAN //MAN是student的基类,student是MAN的子类
- {
- void study()
- {
- System.out.println(name+”正在学习”+age);
- }
- }
- class teacher extends MAN
- {
- void work()
- {
- System.out.println(name+”正在工作”+age);
- }
- }
- public class Main
- {
- public static void main (String[] args)
- {
- student BLF = new student();
- BLF.name = “BLF2″;
- BLF.age = 20;
- BLF.study();
- }
- }
class MAN { String name; int age; } class student extends MAN //MAN是student的基类,student是MAN的子类 { void study() { System.out.println(name+"正在学习"+age); } } class teacher extends MAN { void work() { System.out.println(name+"正在工作"+age); } } public class Main { public static void main (String[] args) { student BLF = new student(); BLF.name = "BLF2"; BLF.age = 20; BLF.study(); } }
概念理解图:
单继承和多继承
JAVA支持单继承,不直接支持多继承,但是在C++的多继承机制上进行了改良。
定义:
1、单继承:一个子类只能有一个直接父类。
class A extends B ,B extends C;
2、多继承:一个子类可以有多个直接父类,(java中不允许)进行了改良。
class A extends B;A extend C;
多继承的优点: 类A 中有一些变量,类B中有一些东西,如果C继承了A和B,那么C就同时拥有了A,B的东西 C++多继承的缺点:
见代码:
- #include <iostream>
- using namespace std;
- class A
- {
- public :
- void show()
- {
- cout<<”sd”<<endl;
- }
- }
- class B
- {
- public :
- void show()
- {
- cout<<”sssd”<<endl;
- }
- }
- class C :public A,public B//A,B的show都是同一个函数名
- {
- }
- C BLF;
- int main()
- {
- BLF.show();//此处C++代码的多继承,就会出现混乱,必须让show,show1、show2,名字保证不一样,才可以
- return 0;
- }
#include <iostream> using namespace std; class A { public : void show() { cout<<"sd"<<endl; } } class B { public : void show() { cout<<"sssd"<<endl; } } class C :public A,public B//A,B的show都是同一个函数名 { } C BLF; int main() { BLF.show();//此处C++代码的多继承,就会出现混乱,必须让show,show1、show2,名字保证不一样,才可以 return 0; }
java的多继承:不直接支持多继承,因为,一旦父类中出现了相同的成员,就会产生不确定性,java改良了C++这一缺陷
java的多继承,是通过“多实现”的方式来实现
java支持多层(多重)继承:D 继承 C,C 继承 B,B继承A,就会出现继承体系,
注意:当要使用一个继承体系时:
1.查看体系的最顶层,了解体系的基本功能
2.创建体系的最子类对象,完成功能的使用
继承体系图:
什么时候定义继承?
当类与类之间有所属关系时,使用继承,如:xxx是yyy的一种,那么xxx继承于yyy,谁是谁中的一种 所属关系:A is a B;A继承于B
子父类中成员变量的特点: 1.成员变量 2.成员函数 3.构造函数
一、成员变量
- class father
- {
- int num = 5;
- }
- class son extends father
- {
- int num = 6;
- void show()
- {
- System.out.print(num+” : “+num);//6 : 6
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son zSon = new son();
- zSon.show();
- }
- }
class father { int num = 5; } class son extends father { int num = 6; void show() { System.out.print(num+" : "+num);//6 : 6 } } public class Main { public static void main(String[] args) { son zSon = new son(); zSon.show(); } }
子类中的有,坚决不找父类
当本类的成员和局部变量重名,用this来区分,当子父类中的成员重名,用super区分。
super的用法和this相似。
- class father
- {
- int num = 5;
- }
- class son extends father
- {
- int num= 6;
- void show()
- {
- System.out.print(this.num+” : “+super.num);// 6 :5
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son zSon = new son();
- zSon.show();
- }
- }
class father { int num = 5; } class son extends father { int num= 6; void show() { System.out.print(this.num+" : "+super.num);// 6 :5 } } public class Main { public static void main(String[] args) { son zSon = new son(); zSon.show(); } }
上述代码,作为了解,父类有num,子类继承于父类自然也有num
两者区别: this:代表一个类的引用 super:代表一个父类的空间
子父类内存图:
子类不能直接访问,父类中的私有内容,但是可以间接访问,开发时不常见,面试多见
- class father
- {
- private int num = 5;
- public int getnum()
- {
- return num;
- }
- }
- class son extends father
- {
- int num= 6;
- void show()
- {
- System.out.print(this.num+” : “+super.getnum());
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son zSon = new son();
- zSon.show();
- }
- }
class father { private int num = 5; public int getnum() { return num; } } class son extends father { int num= 6; void show() { System.out.print(this.num+" : "+super.getnum()); } } public class Main { public static void main(String[] args) { son zSon = new son(); zSon.show(); } }
二、成员函数
- class father
- {
- void show1()
- {
- System.out.println(“father”);
- }
- }
- class son extends father
- {
- void show2()
- {
- System.out.println(“son”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son zSon = new son();
- zSon.show1();
- zSon.show2();
- }
- }
class father { void show1() { System.out.println("father"); } } class son extends father { void show2() { System.out.println("son"); } } public class Main { public static void main(String[] args) { son zSon = new son(); zSon.show1(); zSon.show2(); } }
当子父类中出现成员函数一样(函数名一样,返回类型一致,函数参数列表相同)时,会运行子类的函数,成为覆盖操作,父类的操作被覆盖了,这是函数在子父类中的特性
- class father
- {
- void show()
- {
- System.out.println(“father”);
- }
- }
- class son extends father
- {
- void show()
- {
- System.out.println(“son”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son zSon = new son();
- zSon.show();
- zSon.show();
- }
- }
class father { void show() { System.out.println("father"); } } class son extends father { void show() { System.out.println("son"); } } public class Main { public static void main(String[] args) { son zSon = new son(); zSon.show(); zSon.show(); } }
函数的两个特性:1.重载 2.覆盖(重写/复写)
重载是同一个类中,覆盖是在子类中
覆盖注意 :
1.子类方法覆盖父类方法时,子类权限必须 >= 父类的权限,才能覆盖(上述代码父类show前加public就无法覆盖,son的show也加public就行,不能用static修饰其中一个,必须两个都static才行)
2.静态只能覆盖静态,或被静态覆盖
什么时候使用覆盖?
当对一个类,进行子类的扩展时,子类需要保留父类的功能声明,但是在定义子类中功能的特有功能时,就使用覆盖操作完成
比如电话:来电显示功能,后期想添加 号码来自地区
- class phone
- {
- void show()
- {
- System.out.println(“number”);
- }
- void call()
- {}
- void memssage()
- {}
- //….
- }
- class phone2 extends phone//phone中有一些功能不需要改,所以采用继承
- {
- void show()//只需修改show的功能
- {
- System.out.println(“from”);
- super.show();//保留父类的功能
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- phone2 zSon = new phone2();
- zSon.show();
- }
- }
class phone { void show() { System.out.println("number"); } void call() {} void memssage() {} //.... } class phone2 extends phone//phone中有一些功能不需要改,所以采用继承 { void show()//只需修改show的功能 { System.out.println("from"); super.show();//保留父类的功能 } } public class Main { public static void main(String[] args) { phone2 zSon = new phone2(); zSon.show(); } }
子父类中的构造函数的特点:
- class father
- {
- father() {
- System.out.println(“fu”);
- }
- }
- class son extends father
- {
- son() {
- System.out.println(“so”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- //super();
- new son();
- }
- }
class father { father() { System.out.println("fu"); } } class son extends father { son() { System.out.println("so"); } } public class Main { public static void main(String[] args) { //super(); new son(); } }
打印 fu so
在子类构造对象时,发现,访问子类构造函数时,父类构造函数也运行了?
在子类构造函数的第一行有一个默认的隐式语句 super();有super() ,表示正在调用父类中的空参数的构造函数。 (父类构造函数如果有参的话,那么super就要自己写了)
- class father
- {
- father()
- {
- System.out.println(“A”);
- }
- father(int x)
- {
- System.out.println(“B”);
- }
- }
- class son extends father
- {
- son()
- {
- //super();
- System.out.println(“C”);
- }
- son(int x)
- {
- super(x);
- System.out.println(“D”+x);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- new son(6);
- }
- }
class father { father() { System.out.println("A"); } father(int x) { System.out.println("B"); } } class son extends father { son() { //super(); System.out.println("C"); } son(int x) { super(x); System.out.println("D"+x); } } public class Main { public static void main(String[] args) { new son(6); } }
子类的实例化过程,子类中所有的构造函数都会访问父类中空参数的构造函数
为什么实例化的时候,要访问父类的构造函数?
子类继承了父类,拥有了父类的内容(属性和行为)所以在使用父类内容之前,要知道父类是如何进行初始化的,所以子类在构造对象时,就必须访问父类中的构造函数,为了完成这个必须的动作,就在子类的构造函数中加入默认的super()。 如果父类中,没有定义空参数的构造函数,那么子类的构造函数必须用super明确的调用父类中对应构造函数
- class father
- {
- int num;
- father()
- {
- num = 8;
- System.out.println(“A”);
- }
- }
- class son extends father
- {
- son()
- {
- System.out.println(“C”+num);
- }
- }
class father { int num; father() { num = 8; System.out.println("A"); } } class son extends father { son() { System.out.println("C"+num); } }
如果子类不访问父类的构造函数的话,那么打印 C 0,所以访问父类构造函数成为了必须。 内存空间一建立,父类中的num会在子类的num都会在内存中展现出来,所以子类必须知道父类怎么初始化的
注意:super语句必须在子类构造函数的第一行。因为父类的初始化的动作在子类前,必须先完成。
this和super:
this 和super 在构造函数都必须在一行,那是不是就引起冲突呢。 子类函数中,如果使用了this调用了本来的构造函数,但是this和super都必须房子本类构造函数的第一行,所以只能有一个 但是,子类中肯定有其他的构造函数访问父类的构造函数。
代码如下:
- class father
- {
- int num;
- father()
- {
- num = 8;
- System.out.println(“A”);
- }
- }
- class son extends father
- {
- son()
- {
- }
- son(int x)
- {
- this();//使用this,省掉super,原因:使用this调用本类的son(),而son(){} 继承于father,son(){}中有默认 //的super();肯定至少有一个访问父类的构造函数
- System.out.println(“C”+num);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- new son(6);
- }
- }
class father { int num; father() { num = 8; System.out.println("A"); } } class son extends father { son() { } son(int x) { this();//使用this,省掉super,原因:使用this调用本类的son(),而son(){} 继承于father,son(){}中有默认 //的super();肯定至少有一个访问父类的构造函数 System.out.println("C"+num); } } public class Main { public static void main(String[] args) { new son(6); } }
子类的实例化过程:
- class father
- {
- father()
- {
- show();
- }
- void show()
- {
- System.out.println(“asdf”);
- }
- }
- class son extends father
- {
- int num = 8;
- son()
- {
- super();
- //System.out.println(“dfsdf”);
- }
- void show()
- {
- System.out.println(“num is “+num);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son s = new son();
- s.show();
- }
- }
class father { father() { show(); } void show() { System.out.println("asdf"); } } class son extends father { int num = 8; son() { super(); //System.out.println("dfsdf"); } void show() { System.out.println("num is "+num); } } public class Main { public static void main(String[] args) { son s = new son(); s.show(); } }
打印: num is 0 num is 8
再看把上述代码注释部分打开 打印: num is 0 dfsdf num is 8
- class father
- {
- father()
- {
- show();
- }
- void show()
- {
- System.out.println(“asdf”);
- }
- }
- class son extends father
- {
- int num = 8;
- son()
- {
- super();//通过super初始化父类内容时,子类并未进行初始化,等super()父类初始化
- //完毕后,才进行子类的成员变量,显示初始化->分水岭
- }
- void show()
- {
- System.out.println(“num is “+num);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- son s = new son();
- s.show();
- }
- }
class father { father() { show(); } void show() { System.out.println("asdf"); } } class son extends father { int num = 8; son() { super();//通过super初始化父类内容时,子类并未进行初始化,等super()父类初始化 //完毕后,才进行子类的成员变量,显示初始化->分水岭 } void show() { System.out.println("num is "+num); } } public class Main { public static void main(String[] args) { son s = new son(); s.show(); } }
- /*
- * 一个对象实例化过程
- * son s = new son();
- * 1.JVM会读取指定路径下的son.class文见,并加载进内存,如果有直接父类,先加载父类
- * 2.在堆内存中开辟空间,分配地址
- * 3.并在对象空间中,对对象中的属性进行默认初始化
- * 4.调用对应的构造函数进行初始化
- * 5.在构造函数中第一行,会先调用父类的构造函数,进行初始化
- * 6.父类初始完毕,再对子类的属性进行显示初始化,
- * 7.再进行子类构造函数的特定初始化
- * 8.子类初始化完毕后将地址值给引用变量
- * */
/* * 一个对象实例化过程 * son s = new son(); * 1.JVM会读取指定路径下的son.class文见,并加载进内存,如果有直接父类,先加载父类 * 2.在堆内存中开辟空间,分配地址 * 3.并在对象空间中,对对象中的属性进行默认初始化 * 4.调用对应的构造函数进行初始化 * 5.在构造函数中第一行,会先调用父类的构造函数,进行初始化 * 6.父类初始完毕,再对子类的属性进行显示初始化, * 7.再进行子类构造函数的特定初始化 * 8.子类初始化完毕后将地址值给引用变量 * */
PS:C++针对多继承体系重复继承相同基类的同一成员多次继承的弊端,改用虚基类来改良,但是,子类的必须要写他的每一个直接基类的构造函数,代码冗杂,java利用super,来代替原来的基类的构造函数,且是单继承,每一个类的构造函数都含指向有他的直接基类的空间地址
- class FU{
- public int num;
- public String name;
- public FU(int num,String name) {
- this.num = num;
- this.name = name;
- System.out.println(“FU.FU()”);
- }
- class ZI extends FU{
- private int age;
- ZI(int age,int num,String name){
- super(num,name);this.age = age;
- }