【14-1】面向对象-继承-概述 /* //描述学生 class Student { //属性 String name; int age; //行为 void study() { System.out.println("good good study"); } } //描述工人 class worker { //属性 String name; int age; //行为 void work() { System.out.println("hard work"); } } */ /* 为了提高代码的复用性,只建立一份代码。 一个类只要和另一个类产生关系就可以了。 这种关系叫:继承 发现了获取到所需内容的同时也获取到了不该具备的内容。 为什么? 发现原来这两个类之间根本就不存在继承的关系。 怎么解决呢? 两个之间没有继承关系。 但是他们有共性的内容。 是不是可以找到他们的共性类型呢? 无论是学生还是工人 都是人。Person Person都具备着name age. 现在找到了学生和工人的共性内容,就将要提供复用的代码进行抽取 定义到一个共性类型的类中。 怎么在代码体现中让学生和Person产生关系呢? 只要通过关键字extends(继承),就行了。 */ class Person { String name; int age; } class Student extends Person //学生继承了Perosn ,则Person就是父类(基类,超类) { void study() { System.out.println("good good study"); } } class worker extends Person { void worker() { System.out.println("hard work"); } } /* 面向对象的另一个特征:继承。 好处:提高了代码的复用性,让类与类产生了关系,给另一个特征多态提供了前提。 */ class { public static void main(String[] args) { Studen stu = new Student(); stu.name = "xiaoming"; stu.age = 16; stu.study(); } } 【14-2】面向对象-继承-单继承&多继承&多重继承(*****学习一个继承体系时,要看顶层,建底层*****) /* //描述学生 class Student { //属性 String name; int age; //行为 void study() { System.out.println("good good study"); } } //描述工人 class worker { //属性 String name; int age; //行为 void work() { System.out.println("hard work"); } } */ /* 为了提高代码的复用性,只建立一份代码。 一个类只要和另一个类产生关系就可以了。 这种关系叫:继承 发现了获取到所需内容的同时也获取到了不该具备的内容。 为什么? 发现原来这两个类之间根本就不存在继承的关系。 怎么解决呢? 两个之间没有继承关系。 但是他们有共性的内容。 是不是可以找到他们的共性类型呢? 无论是学生还是工人 都是人。Person Person都具备着name age. 现在找到了学生和工人的共性内容,就将要提供复用的代码进行抽取 定义到一个共性类型的类中。 怎么在代码体现中让学生和Person产生关系呢? 只要通过关键字extends(继续),就行了。 */ class Person { String name; int age; } class Student extends person //学生继承了Perosn ,Person就是父类(基类,超类) { void study() { System.out.println("good good study"); } } class worker { void worker() { System.out.println("hard work"); } } /* 面向对象的另一个特征:继承。 好处:提高了代码的复用性,让类与类产生了关系,给另一个特征多态提供了前提。 什么时候定义继承呢? 必须保证类与类之间有所属(is a)关系,xxx是zzz中的一种。 如:苹果是水果中的一种,狗是犬科中的一种。 在Java中继续的体现特点是什么呢? Java中允许单继承。不直接支持多继承。将多继承进行了其他方式的体现(进行了优化)。 单继承:一个子类只能有一个父类。 多继承:一个子类可以有多个父类。 看上去,多继承很厉害!为什么? class Fu1 { void show1() {} } class Fu2 { void show2() {} } //多继承 class Zi extends Fu1,Fu2 { } Zi z = new Zi() //如果是多继承,则这个z就同时有了Fu1 和Fu2的方法 z.show1(); z.show2(); //问题随之而来 万一多个父类具备了相同的功能呢? class Fu1 { void show() { System.out.println("fu1 show run"); } } class Fu2 { void show() { System.out.println("fu2 show run"); } } //多继承 class Zi extends Fu1,Fu2 { } Zi z = new Zi() //如果是多继承,则这个z就同时有了Fu1 和Fu2的方法 z.show(); //运行哪个呢? 调用会产出不确定性 但是Java将这个问题解决了。 //将这个多继承的机制保留并进行了改良(用多实现来体现)。 后面会讲到。(会用到final吗???) ------------------------------------------------------------------------------ Java还支持多重继承 class A {} class B extends A {} class C extends B {} new c //new一个C的对象,也可以调用A的功能。这就是多重继承 */ class { public static void main(String[] args) { Studen stu = new Student(); stu.name = "xiaoming"; stu.age = 16; stu.study(); } } 【14-3】面向对象-继承-子父类中成员变量的特点(JAVA详解图-->参看【2】子父类成员变量详解图) /* 继承出现后在代码中的体现 重点在于成员变量的体现: 1,成员变量。 重点明确原理。 特殊情况: 子,父类中定义了一模一样的成员变量。 都存在于子类对象中。 如何在子类中访问同名的父类中的变量呢? 可以通过关键字super来完成。 super和this的用法很相似 this代表的是本类对象的引用。 super代表的是父类的内存空间。 注意:这种情况开发见不到。因为父类一旦描述完了属性,子类直接使用就可以了。 当父类的成员变量被私有了时,子类不能直接访问父类中被私有的变量,但可以通过 setxxx 和getxxx 的方法进行间接访问。 2,成员函数。 3,构造函数。 */ //父类 class FU { /*private*/ //int num1 = 3; //一旦私有,在子类中就不能直接调用了。可以间接调用。 int num = 3; } class Zi extends Fu { //int num2 = 4; int num = 4; //如果把这个注释掉了,(***1***)中就会输出num=3(父类的num) void show() { (***1***)System.out.println("zi num="+/*this.*/num);//输出结果会是num=4 输出的是本类中对象这个num=4 System.out.println("fu num="+super.num) //这个输出的就是父类中的num = 3 System.out.println(this) //可以,输出的是对象的类型和哈希值。 System.out.println(super) //不可以, } } class Extendsemo { public static void main(String[] args) { Zi z = new zi(); //System.out.println(z.num1); //System.out.println(z.num2); //这样调用都是没问题的,但是当num1私有了,就不能 //这样调用了。 z.show(); //输出结果会是num=4 输出的是本类中对象这个num } } 【14-4】面向对象-继承-子父类中成员函数的特点-重写() /* 子父类中成员函数的特点: 特殊情况: 子父类中定义类一模一样的函数。 */ ------------------------------------------- class Fu { void show1() { System.out.println("Fu show1 run"); } } class Zi extends Fu { void show2() { System.out.println("zi show2 run"); } } class ExtendsDemo { public static void main(String[] args) { Zi z = new zi(); z.show1(); //这样调用完全正确 z.show2(); //这样调用完全正确 } } //这个程序是子父类中定义的函数不同。 ---------------------------------------------------------- class Fu { void show() { System.out.println("Fu show1 run"); } } class Zi extends Fu { void show() { System.out.println("zi show2 run"); } } class ExtendsDemo { public static void main(String[] args) { Zi z = new zi(); z.show(); //输出了zi show2 run } } /*特殊情况: 子父类中定义类一模一样的函数 运行的结果是子类的函数在运行。 下面程序中,子父类中定义了相同的方法,明明知道只要子继承了 父类后就可以直接调用父类中的方法了,为什么还要在子类定义 一个相同的方法呢? 这是因为,虽然子父类中定义了相同的方法, 但是方法中输出的内容有可能不一样,所以即使父类中有了这个方法,还是 要在子类中定义一次,以达到输出同方法不同内容的目的。 但是从下面输出的结果来看,只输出了子类中的内容,父类的内容 被覆盖掉了,这种情况在子父类中,是函数的另一个特性,叫重写(override)(以前 学的是重载(overload)) 重写什么时候用? 举例: //描述手机 class Phone { //来电显示。 void show() { System.out.println("电话号码"); } } class PhoneTest { public static void main(String[] args) { Phone p = new Phone(); p.show(); } } //随着电话的升级,只显示号码不爽,希望显示姓名,大头贴。 修改源码,虽然费劲但是可以解决,不利于后期的维护和扩展。 为了扩展方便,新功能是不是新的电话具备的呢? 单独描述单独封装。新电话也是电话中的一种,继承,直接获取父类中的功能。 但是新电话的来显功能已经变化,需要重新定义。 定义一个新功能合适吗?比如NewShow,不合适,因为父类已经将来显功能定义完了, 子类完全不需要重新定义新功能,直接调用就可以了,如果子类的来显功能内容不同, 只需要保留来显功能,定义子类的内容即可,这就是重写的应用。 class NewPhone extends phone { void show() { //sop("电话号码"); (*******)super.show(); 如果还需要父类中原有的功能,可以通过super调用。 sop("姓名"); sop("大头贴"); } } */ 【14-5】面向对象-继承-子父类中成员函数的特点-重写的注意事项 /* 重写的注意事项: 1,子类覆盖(重写)父类,子类的权限必须大于或等于父类的权限 Fu: public void show() {} zi: void show() //此时,子类是不能覆盖父类的,因为父类本身的权限是public (权限最大),而一旦被子类覆盖后,权限就降低了,这是不可以的。 但是不把私有(private)列在覆盖范围之内,因为如果父类中的方法 定义成了私有,子类中无法直接访问。(私有private)只能在本类中直接访问 2,静态覆盖静态。 因为如果父类中的方法是非静态的,而子类中的方法是静态的,假设子类此时能 覆盖父类,一旦覆盖后,父类中的方法也变成了静态的了(此时,如果在父类中的那个非静态的 方法在没被静态的子类方法覆盖之前又访问到了父类本身的非静态成员变量,那么一旦父类中的非静态 方法被子类中的静态方法覆盖后,变成了静态的方法,就不能再访问了父类本类中的非静态成员变量了 ,同样,也可以反过来思考,所以说,只能是静态访问静态) 3,写法上必须一模一样。函数的返回值类型,函数名,参数列表都要一样。 */ 【14-6】面向对象-继承-子父类中成员函数-构造函数的特点(JAVA笔记 详解图-->参看【3】子父类中构造函数特点) /* 重写的注意事项: 1,子类覆盖(重写)父类,子类的权限必须大于或等于父类的权限 Fu: public void show() {} zi: void show() //此时,子类是不能覆盖父类的,因为父类本身的权限是public (权限最大),而一旦被子类覆盖后,权限就降低了,这是不可以的。 但是不把私有(private)列在覆盖范围之内,因为如果父类中的方法 定义成了私有,子类中无法直接访问。(私有private)只能在本类中直接访问 2,静态覆盖静态。 因为如果父类中的方法是非静态的,而子类中的方法是静态的,假设子类此时能 覆盖父类,一旦覆盖后,父类中的方法也变成了静态的了(此时,如果在父类中的那个非静态的 方法在没被静态的子类方法覆盖之前又访问到了父类本身的非静态成员变量,那么一旦父类中的非静态 方法被子类中的静态方法覆盖后,变成了静态的方法,就不能再访问了父类本类中的非静态成员变量了 ,同样,也可以反过来思考,所以说,只能是静态访问静态) 3,写法必须一模一样。函数的返回值类型,函数名,参数列表都要一样。 */ /* 子父类中构造函数的特点。 当子父类都有构造函数时,发现结果为: fu constructor run zi constructor run 先执行了父类的构造函数,再执行子类的构造函数。 这是因为: 子类的所有的构造函数中的第一行都有一句隐式语句 super(); 已经知道了this()是调用本类中空参数的构造函数, 而写super()是默认调用父类中的空参数的构造函数, 子类中的构造函数为什么有一句隐式的super? 原因是:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。 才可以更方便的使用父类中的内容。 当父类中没有空参数构造函数时,子类的构造函数必须通过显示的super语句指定要访问的父类中的构造函数 这就是子类实例化过程。 */ class Fu { Fu() { System.out.println("fu constructor run"); } } class Zi extends Fu { Zi() { //super(); System.out.println("zi constructor run"); } } class ExtendsDemo { public static void main(String[] args) { new Zi(); //运行结果是:fu constructor run //zi constructor run //此句执行的过程是:先new一个Zi类对象 //然后Zi类中的构造方法Zi()就进栈了, //紧接着,在执行Zi这个构造方法时,又调用了 //父类中的构造方法(通过super()来调用),所以 //结果就明白是怎么回事了。 } } ------------------------------------------------------------------ class Fu { //int count = 3; //Fu(){} //写这一句便于编译,但是这个不建议写 Fu(int x) { System.out.println("fu constructor run..."+x); } } class Zi extends Fu { Zi(int x) { super(1); //如果父类的构造函数的有参数,则这里必须用super(参数列表)明确出来 //否则会报错 //为了不出错,也可以在父类中写如上一句Fu(){} 但不建议这样写 直接在这里写上super(x)更好 System.out.println("zi constructor run。。。"+x); } } class ExtendsDemo { public static void main(String[] args) { new Zi(4); } } //运行的结果是:fu constructor run ...1 zi constructor run ...4 ---------------------------------------------------------------------------- class Fu { Fu() { System.out.println("fu constructor run..A.."); } Fu(int x) { System.out.println("fu constructor run..B.."+x); } } class Zi extends Fu { Zi() { System.out.println("zi constructor run..C.."); } Zi(int x) { //super(x); System.out.println("zi constructor run..D.."+x); } } class Demo { public static void main(String[] args) { new Zi(); //打印结果是 A C A D...4 (B根本就不会打印) new Zi(4); } } 【14-7】面向对象-继承-子父类中成员函数-构造函数的特点-子类构造函数实例化细节 细节: 1,如果子类的构造函数第一行写了this调用了本类中其他的构造函数,那么super调用父类的语句还有吗? 答案是 没有。 因为this()或super()只能定义在第一行 因为初始化动作要先执行。 2.子类构造函数有super,那么父类构造函数里面有super吗? 也是有的。 只要是构造函数,其默认第一行都是super,除非自己定义了this(),或super()。 那么父类的父类是谁呢? super调用的到底是谁的构造函数呢? Java体系在设计时,定义了一个所有对象的父类Objict;(相当于Java中的上帝) 总结: 类中的构造函数默认第一行都有隐式的super语句,在访问父类中的构造函数。 所以父类的构造函数既可以给自己的对象初始化,也可以给自己的子类对象初始化。 如果默认的隐式super语句没有对应的构造函数,必须在构造函数中通过this或者super的形式明确调用的构造函数。 问题: 1,this语句和super语句是否可以出现在同一个构造函数中呢?不可以,因为this或者super都必须定义在第一行, 2,为什么要定义在第一行呢?因为初始化动作要先执行。 class Fu { Fu() { //int count = 3; System.out.println("fu constructor run..A.."); } Fu(int x) { System.out.println("fu constructor run..B.."+x); } } class Zi extends Fu { Zi() { System.out.println("zi constructor run..C.."); } Zi(int x) { //super(x); //有了下面这句,这个super已经不存在了。 this(); //一旦写了这句,则上面的super这句不存在 System.out.println("zi constructor run..D.."+x); } } class Demo { public static void main(String[] args) { //new Zi(); //打印结果是 A C A D...4 (B根本就不会打印) new Zi(4); } } 【14-8】面向对象-继承-子父类中成员函数-构造函数的特点-子类实例化过程的应用(也是super调用的应用) //子类的实例化过程的应用,也是super调用的应用。 //什么时候用super调用父类中的构造函数,只要使用父类的指定初始化动作,就在子类中通过super(参数列表)格式调用就可以了 class Person { private String name; private int age; public person(String name,int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } } class Student extends Person { public Student(String name,int age) { //调用父类,使用父类的初始化动作。 super(name,age); } public void study() { System.out.println("good good study"); } } class Worker extends Person { public Worker(String name,int age) { super(name,age); } public void work() { System.out.println("hard work"); } }