day11--Java面向对象--JavaObject02-2
今天主要学习了以下内容:
1. 继承的优缺点,及继承的限制和注意事项
2. 子类对象的初始化
3. 父类成员变量的隐藏
4. 父类方法的覆盖
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 * 继承概述 8 Java中的继承 和 我们现实生活中的“继承”的含义基本类似,但是含义更广。 9 1. 简单来说都可以表示“不劳而获”(类似于现实世界中继承的含义) 10 2. 类型之间 “ is a” 的关系 11 a. 一种类型(类) 继承 另外一种类型(类) 12 子类 父类 13 b. 从数据类型,可以把子类类型,当做父类类型 14 从数据类型的角度,数据类型:一个数据集合和基于这个数据集合的一组操作 15 当子类继承父类,子类拥有了父类中定义的成员(父类中的数据集和基于数据集的操作) 16 17 c. 一个学生对象 是 一个人 18 19 被继承的类称之为父类(基类或超类),继承其他类的类称之为子类(派生类,导出类) 20 子类可以通过继承机制,不写任何额外代码就可以拥有父类的“所有”成员。 21 22 继承的语法 23 class 子类名 extends 父类名 {} 24 25 26 优缺点: 27 1,继承的优点: 28 a. 代码复用(类) 29 b. 提高了代码的可维护性(这是一把双刃剑) 30 通过继承,实现代码复用,一定程度深,消除了冗余代码 31 c. 弱化Java中的类型约束(多态的前提) 为什么说是优点?在多态中,体现出来 32 33 2. 缺点: 34 缺点:父类的修改可能会出现在所有子类中 35 1. 假设我们开发了一款模拟鸭子的一款游戏,所有鸭子都可以呱呱叫,都可以在河流中移动 36 2. 我们想到了继承(呱呱叫的行为,和游动的行为都放在父类中) 37 3. 于是为了,增加竞争力,我们需要给鸭子添加新的功能,飞行(添加到父类中??????) 38 4. 发现有一种鸭子“模型鸭子(橡皮鸭子)” 39 40 */ 41 public class Demo1 { 42 43 public static void main(String[] args) { 44 // 类型之间 “ is a” 的关系 , 如何从代码上去理解? 45 //b. 从数据类型,可以把子类类型,当做父类类型 46 //c. 一个学生对象 是 一个人 47 Human human = new Student(); 48 49 50 // 弱化Java中的类型约束(多态的前提) 51 // 在没有学习继承之前 52 // 同一个类 引用 = new 类() 53 Human human1 = new Human(); 54 human1 = new Student(); 55 56 57 } 58 59 } 60 61 // 父类(基类或者超类) 62 class Human { 63 64 //姓名属性 65 String name; 66 67 public void eat() { 68 System.out.println(name + "eating"); 69 } 70 71 public void sleep() { 72 System.out.println(name + "sleeping"); 73 } 74 75 } 76 // Student(子类,派生类,导出类) 77 class Student extends Human { 78 79 //学号属性 80 int sno; 81 82 public void study() { 83 //System.out.println(name + "learning"); 84 } 85 }
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 * Java类继承的特点:在Java中类只能实现单重继承。(一个类不能同时继承多个类) 8 * 单重继承:简单来说Java的extends关键字后只能跟一个类名 9 class SubDemo extends Demo{} //ok 10 class SubDemo extends Demo1,Demo2{} //error 11 12 一个类只能继承其他的一个单独的类的成员吗? 不是 13 14 * 15 */ 16 public class Demo2 { 17 18 public static void main(String[] args) { 19 Son son = new Son(); 20 //Son类继承的Father的父类的成员 21 System.out.println(son.grandI); 22 son.grand(); 23 24 //Son类继承的Father中的成员 25 System.out.println(son.fatherI); 26 son.father(); 27 } 28 } 29 30 class A {} 31 class B {} 32 class C extends A {} 33 class D extends B {} 34 // class E extends A, B{} 35 36 // 单重继承 "重" 37 class GrandFather { 38 int grandI; 39 public void grand() {} 40 } 41 42 class Father extends GrandFather{ 43 44 int fatherI; 45 public void father() {}; 46 } 47 48 class Son extends Father { 49 int sonI; 50 public void son() {} 51 }
1 package com.cskaoyan.basic; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 继承的注意事项: 7 1. 子类只能访问父类所有 非私有的成员 (成员方法和成员变量) 8 2. 子类不能继承父类的构造方法 9 10 */ 11 public class Demo3 { 12 13 public static void main(String[] args) { 14 NoticeSon noticeSon = new NoticeSon(); 15 noticeSon.test(); 16 } 17 18 } 19 20 class NoticeFather { 21 22 private int privateValue = 100; 23 int intValue; 24 25 private void privateMethod(){} 26 void method(){ 27 System.out.println(this.privateValue); 28 } 29 } 30 31 class NoticeSon extends NoticeFather { 32 33 int sonI; 34 35 public void test() { 36 // 访问父类中的私有成员变量 37 //System.out.println(privateValue); 38 //privateMethod(); 39 40 //其实,子类有继承父类的私有成员变量 41 this.method(); 42 } 43 44 }
图1 :子类对象的内存映像1
补充yesterday
protect修饰符
在跨包的子类中,可以访问父类中protected成员
package com.cskaoyan.yesterday.antherpackage; import com.cskaoyan.yesterday.onepackage.MemberAccess; /** * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. * @version 1.0 * protected: * 在跨包的子类中,可以访问父类中protected成员 * */ public class TestMemberAccess extends MemberAccess{ public static void main(String[] args) { // 这里访问不到是因为,静态上下文的关系 //System.out.println(protectedValue); } public void testAccess() { //在子类类体中直接访问 System.out.println(this.protectedValue); //创建对象访问 // 通过父类对象不可以 MemberAccess memberAccess = new MemberAccess(); //System.out.println(memberAccess.protectedValue); // 通过子类对象可以访问到 TestMemberAccess testMemberAccess = new TestMemberAccess(); System.out.println(testMemberAccess.protectedValue); } }
跨包的另一代码
package com.cskaoyan.yesterday.onepackage; /** * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. * @version 1.0 */ public class MemberAccess { protected int protectedValue; }
接下来,讲继承相关的几个问题?
Q1:子类对象的初始化问题,在子类对象汇总父类继承数据和子类自己定义的数据,谁先谁后初始化(赋初值)?
Q2:子类和父类中,定义同名成员变量问题
Q3:子类和父类中,能否定义一模一样的方法?
1. 针对Q1:子类对象的初始化问题(Initialize)
在子类对象汇总父类继承数据和子类自己定义的数据,谁先谁后初始化(赋初值)?
1-1 子类对象的隐式初始化implicit(先调用父类构造方法,jvm隐式完成)
1 package com.cskaoyan.intialize.implicit; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 * 对于一个子类对象而言,其内存中的数据,分成了两部分: 8 * 1. 从父类中,继承下来成员变量的值 9 * 2. 子类中,自己定义的成员变量的值 10 * 11 * 这两部分数据,在给他们赋初值的时候,谁先赋初值谁后赋初值(谁先初始化值,谁后初始化值)? 12 * java语言的结论是,一定是先初始化,子类对象中,继承的父类变量的值,后,初始化子类对象中, 13 * 子类自己定义的成员变量值 先父后子 14 * 15 * 如何保证呢? 16 * 核心思路: 17 * 1. 首先,构造方法本身的功能,就是初始化对象成员变量值的 18 * 2. 所以,显然,对我们只需要保证,父类的构造方法,先于子类的构造方法运行 19 * 3. 如何让父类构造方法,先于子类构造方法执行(在子类的构造方法的,第一条语句的位置,先去调用父类) 20 * 21 * 如何具体从语法上,保证先执行父类构造方法,在执行父类构造方法,有两种方式: 22 * 1. 子类对象的 隐式 初始化(先调用父类构造方法,jvm隐式完成) 23 * a. 当父类提供了默认的构造函数(无参构造方法), 24 * b. 且子类的构造方法中, 没有显式调用父类的其它构造方法 25 * c. 则在执行子类的构造方法之前,会(jvm)自动执行父类的构造方法(无参构造方法) 26 * 27 */ 28 public class Demo1 { 29 30 public static void main(String[] args) { 31 //创建子类对象 32 InitializeSon initializeSon = new InitializeSon(100); 33 } 34 35 } 36 37 class InitializeFather { 38 int fatherI; 39 40 public InitializeFather() { 41 System.out.println("InitializeFather 的构造方法执行了"); 42 } 43 44 public InitializeFather(int fatherI) { 45 this.fatherI = fatherI; 46 } 47 } 48 class InitializeSon extends InitializeFather { 49 int sonI; 50 51 public InitializeSon(int sonI) { 52 // 首先调用父类构造方法。 53 System.out.println("InitializeSon 的构造方法执行了"); 54 this.sonI = sonI; 55 56 } 57 }
1-2 子类对象的显式初始化explicit(先调用父类构造方法,我们coder通过代码来保证)
1 package com.cskaoyan.intialize.explicit; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 * 8 * 对于一个子类对象而言,其内存中的数据,分成了两部分: 9 * 1. 从父类中,继承下来成员变量的值 10 * 2. 子类中,自己定义的成员变量的值 11 * 12 * 这两部分数据,在给他们赋初值的时候,谁先赋初值谁后赋初值(谁先初始化值,谁后初始化值)? 13 * java语言的结论是,一定是先初始化,子类对象中,继承的父类变量的值,后,初始化子类对象中, 14 * 子类自己定义的成员变量值 先父后子 15 * 16 * 如何保证呢? 17 * 核心思路: 18 * 1. 首先,构造方法本身的功能,就是初始化对象成员变量值的 19 * 2. 所以,显然,对我们只需要保证,父类的构造方法,先于子类的构造方法运行 20 * 3. 如何让父类构造方法,先于子类构造方法执行(在子类的构造方法的,第一条语句的位置,先去调用父类) 21 * 22 * 子类对象的显式初始化: 23 * 子类对象的显式初始化,是我们coder通过代码来保证,先执行父类构造方法,在执行子类构造方法 24 * 核心问题是,如何在Java语法层面,显式调用父类的构造方法? 通过一个关键字super 25 * 26 * super关键字: super代表父类对象的引用。 27 * 28 * this 关键字: 表示当前对象的引用 super关键字: super代表父类对象的引用。 29 this访问 当前对象的成员变量值 super 访问父类对象中,成员变量的值 30 this访问 当前对象的成员方法 super 访问父类对象,成员方法 31 this 调用 当前类中定义的构造方法 super 调用父类中定义的构造方法 32 this(实参列表) super(实参列表) 33 34 注意事项: 35 1. 必须保证,子类每个构造方法,都要满足子类对象的初始化流程 36 2. 在子类构造方法中,通过super调用,父类构造方法时,该super语句, 37 必须出现在,子类构造方法第一条语句的位置 38 3. this() 和 super()都可以,写在构造方法的方法体中, 它们都需要在第一条语句的位置 39 它们不能共存 40 * 41 */ 42 public class Demo1 { 43 44 public static void main(String[] args) { 45 ExplicitSon explicitSon = new ExplicitSon(1000, 100); 46 System.out.println(explicitSon.fatherI); 47 System.out.println(explicitSon.sonI); 48 49 // 50 ExplicitSon explicitSon1 = new ExplicitSon(1000, 100); 51 System.out.println(explicitSon1.fatherI); 52 System.out.println(explicitSon1.sonI); 53 } 54 55 } 56 57 class ExplicitFather { 58 int fatherI; 59 60 double fatherJ; 61 62 //public ExplicitFather() { 63 // this(100); 64 //} 65 66 public ExplicitFather(int fatherI) { 67 System.out.println("ExplicitFather(int fatherI) 执行了"); 68 this.fatherI = fatherI; 69 } 70 71 public ExplicitFather(int fatherI, double fatherJ) { 72 this.fatherI = fatherI; 73 this.fatherJ = fatherJ; 74 } 75 } 76 class ExplicitSon extends ExplicitFather{ 77 int sonI; 78 public ExplicitSon(int fatherI, int sonI) { 79 //调用一下父类的有参构造方法 80 super(fatherI); 81 //super(); 82 this.sonI = sonI; 83 } 84 85 public ExplicitSon(int fatherI, double fatherJ, int sonI) { 86 // 显式调用父类的2参构造方法,把参数值通过super传递给父类构造方法 87 //System.out.println(); 88 super(fatherI, fatherJ); 89 this.sonI = sonI; 90 } 91 }
图2 :子类对象的内存映像2
2. 针对Q2:子类和父类中定义同名成员变量问题(父类域的隐藏FieldHidden)
1 package com.cskaoyan.fieldhidden; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 * 父类域的隐藏: 8 * 如果,在子类中,定义了和父类同名的成员变量 9 * a. 在子类类体中,通过同名成员变量的变量名,访问到的是,子类中定义的同名成员变量值 10 * 看起来,就好像父类成员变量,在子类中,被隐藏起来了 11 * 12 * b. 注意: 在父类方法的方法体中,通过变量名访问同名成员变量的值访问到的是,父类中定义的成员变量值 13 * 14 * 15 * 1. 在子类中是否可以定义和 父类同名的 成员变量? 可以 16 2. 如果可以,那么在子类中访问这个同名变量, 17 究竟访问到的是父类对象中的值,还是子类对象中的值? 子类中同名成员变量的值 18 19 如果在父类中访问呢? 访问到的是父类中定义的同名成员变量的值 20 21 3. 是否可以在子类对象中,同时访问到子类对象和父类对象中的同名成员变量的值? 可以 22 23 */ 24 public class Demo1 { 25 26 public static void main(String[] args) { 27 FieldSon fieldSon = new FieldSon(); 28 // 调用子类的方法,在子类方法的方法体中,访问同名成员变量 29 //fieldSon.sonAccess(); 30 31 // 在子类对象上,调用父类中定义的方法,在父类方法方法体中,访问同名成员变量 32 fieldSon.fatherAccess(); 33 34 //针对第3文,在子类中,实现同时访问,父类,子类中的同名成员变量的值 35 fieldSon.allAccess(); 36 } 37 } 38 39 class FieldFather { 40 41 int intValue = 100; 42 43 // 在父类方法的方法体中,通过变量名访问同名成员变量的值访问到的是,父类中定义的成员变量值 44 public void fatherAccess() { 45 System.out.println(intValue); //100 46 } 47 } 48 49 class FieldSon extends FieldFather { 50 int intValue = 200; 51 52 public void sonAccess() { 53 System.out.println(intValue); //200 54 } 55 56 /* 57 同时访问到,父类,子类中定义的同名成员变量的值 58 */ 59 public void allAccess() { 60 //访问子类同名成员变量值 61 System.out.println(intValue); 62 63 //利用,super,访问父类中,定义的同名成员变量值 64 System.out.println(super.intValue); 65 } 66 67 public static void testReference() { 68 //this 用不了 69 // 静态上下文的问题 70 //System.out.println(super.intValue); 71 72 } 73 74 }
3. 针对Q3:子类和父类中,能否定义一模一样的方法?(方法重载MethodOverride)
1 package com.cskaoyan.methodoverride; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 * 方法覆盖的问题: 8 * 当子类中定义了和父类"一模一样"的方法,如果在子类方法体中,调用"一模一样"的方法, 9 * 访问到的是子类中定义的那个"一模一样的方法" 10 * 注意:但是,如果我们在父类方法的方法体中,访问那个"一模一样的方法",访问到的仍然是子类中的方法 11 * 所以,对于子类父类定义"相同"方法的问题,方法覆盖 12 * 13 * 1. 子类中能否定义和父类一模一样的方法? 14 * 15 2. 如果可以,那么在子类类体中访问这个一模一样 的方法, 16 究竟访问到的是父类中的方法,还是子类中的方法? 访问到的是,子类中定义的那个和父类方法声明一模一样的方法 17 如果在父类中访问呢? 18 19 3. 是否可以在子类方法的方法体中,同时访问到子类和父类中定义的一模一样的方法? 20 21 22 23 当我们调用子类对象上调用方法,该方法在运行时,怎么确定,运行哪个方法的? 就近原则 24 1. 当方法执行的时候(如果子类方法调用了其他方法), 25 优先在子类中找目标方法,如果在子类中找到了目标方法,执行子类中定义的目标方法 26 27 2. 如果说,在子类中,没有找到目标方法,接着到父类中找目标方法,如果找到, 28 就执行父类中定义的方法 29 30 方法覆盖,就是在子类定义和父类一模一样的方法,修改父类方法的 31 32 着重强调: 对方法覆盖的效果,在子类对象上, 33 调用父类中的方法,在父类中方法的方法体中,调用父类和子类定义的“相同方法” 34 35 */ 36 public class Demo1 { 37 38 public static void main(String[] args) { 39 //创建子类对象 40 OverrideSon overrideSon = new OverrideSon(); 41 // 在子类方法体中,调用 42 //overrideSon.sonAccess(); 43 44 // 在父类方法体中调用 45 overrideSon.fatherAccess(); 46 47 //创建了一个父类对象: 48 //OverrideFather overrideFather = new OverrideFather(); 49 //overrideFather.fatherAccess(); //father access 50 51 //在子类类体中,同时访问子类和父类中定义的一抹一样的方法 52 //overrideSon.allAccess(); 53 54 55 // 说明: 56 // overrideSon.access(); 57 } 58 } 59 60 class OverrideFather { 61 /* 62 父类中定义的方法 63 */ 64 public void access() { 65 System.out.println("father access"); 66 } 67 68 public void fatherAccess() { 69 this.access(); //son access 70 } 71 72 } 73 74 class OverrideSon extends OverrideFather { 75 76 /* 77 子类中定义的和父类一模一样的方法 78 */ 79 public void access() { 80 System.out.println("son access"); 81 } 82 83 public void sonAccess() { 84 // 在子类方法调用一下,子类,父类中定义的一模一样的方法 85 access(); //son access 86 } 87 88 /* 89 同时,在子类方法体中,实现同时访问,父类和子类中定义的一模一样的方法 90 */ 91 public void allAccess() { 92 // 调用子类中的access方法 93 access(); 94 95 //利用super关键字,访问,父类中定义的一模一样的成员方法 96 super.access(); 97 } 98 99 }
方法覆盖,在实际开发中,经常使用的, 使用的场景是什么呢?
1 package com.cskaoyan.methodoverride; 2 3 /** 4 * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/17. 5 * @version 1.0 6 * 7 方法覆盖,在实际开发中,经常使用的, 使用的场景是什么呢? 8 当子类继承父类之后,想要修改父类的方法实现,就可以使用方法覆盖 9 */ 10 public class Demo2 { 11 12 public static void main(String[] args) { 13 // 应该具有飞行行为的普通鸭子 14 FirstTypeDuck firstTypeDuck = new FirstTypeDuck(); 15 firstTypeDuck.fly(); 16 17 // 不应该具有飞行行为的模型鸭子 18 ModelDuck modelDuck = new ModelDuck(); 19 modelDuck.fly(); 20 } 21 22 } 23 24 25 class BaseDuck { 26 27 void quack() { 28 System.out.println("呱呱叫"); 29 } 30 31 void swim() { 32 System.out.println("游泳"); 33 } 34 35 // 添加飞行 36 void fly() { 37 System.out.println("fly"); 38 } 39 40 } 41 42 /* 43 某种类型的鸭子 44 */ 45 class FirstTypeDuck extends BaseDuck{ 46 47 } 48 49 class ModelDuck extends BaseDuck { 50 51 // 在子类中利用方法覆盖,在子类中修改,父类方法的实现 52 void fly() { 53 System.out.println("no fly"); 54 } 55 }