day03
今日内容
- 继承
- 继承中的成员访问特点
- 方法重写
- 权限修饰符
- 抽象类
- final关键字
今日目标
【理解】继承的好处与弊端
【理解】继承中的成员访问特点
【应用】方法的重写
【理解】方法重写的注意事项
【理解】权限修饰符的访问特点
【应用】抽象类的使用
【理解】final修饰成员变量的特点
1. 继承
1.1 继承的实现(掌握)
-
继承的概念
-
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
-
问题:如何实现继承?
通过extends关键字 class 类名 extends 父类类名{}
-
问题:继承带来的好处有哪些?—类的使用上
能让类与类之间发生关系,从而产生父子类之间的关系 子类可以直接使用父类中非私有的成员(成员变量+成员方法)
1.2 继承的好处和弊端(理解)
-
继承好处(开发中)
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
好几个类中都用到了同样的成员变量以及相同的重复方法----里面共同的东西抽取到一个类,并且所有类直接继承该类
-
继承弊端
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
-
继承的应用场景:
- 使用继承,需要考虑类与类之间是否存在is…a的关系,不能盲目使用继承
- is…a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
- 使用继承,需要考虑类与类之间是否存在is…a的关系,不能盲目使用继承
1.3. Java中继承的特点(掌握)
-
Java中继承的特点
- Java中类只支持单继承,不支持多继承
- 错误范例:class A extends B, C { }
- Java中类支持多层继承
- Java中类只支持单继承,不支持多继承
-
多层继承示例代码:
public class Granddad { public int age = 100; public void drink() { System.out.println("我爱喝酒"); } } public class Father extends Granddad { public int age = 10000; public void smoke() { int sum = 10;//跟着方法走,方法被调用的时候栈内存中进行保存 System.out.println("我爱抽烟"); } } public class Mother { public void dance() { System.out.println("我爱跳舞"); } } public class Son extends Father { // 此时,Son类中就同时拥有drink方法以及smoke方法 }
2. 继承中的成员访问特点
2.1 继承中变量的访问特点(掌握)
在子类方法中访问一个变量,采用的是就近原则。
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲…)
如果一定要使用父类的,可以通过super关键字,进行区分
2.2 super关键字(掌握)
- this&super关键字:
- this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
- this和super的使用分别
- 成员变量:
- this.成员变量 - 访问本类成员变量
- super.成员变量 - 访问父类成员变量
- 成员方法:
- this.成员方法 - 访问本类成员方法
- super.成员方法 - 访问父类成员方法
- 成员变量:
- 构造方法:
- this(…) - 访问本类构造方法
- super(…) - 访问父类构造方法
2.3 继承中成员方法的访问特点(理解)
问题:当子类和父类中出现了相同的方法时,调用子类中的方法会用谁的呢?
比如下面代码:
public class Fu {
public void show(){
System.out.println("父类show方法");
}
public void add(){}
}
public class Zi extends Fu {
int a =10;
private void add(){}
public void show(){
super.show();
System.out.println("子类show方法");
this.add();
sout(this.a);
}
}
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();//打印什么?
}
}
追问:如果Test中的代码不变,我想同时调用子类和父类的show()方法怎么办?
追问:如果在子类和父类中同时增加一个add()方法,在子类的show()方法中调用add()方法调用的是谁的?
注意:我们在调用一个类的成员变量和成员方法时,系统会默认的在前面加上this关键字
2.4 方法重写(掌握)
-
1、方法重写概念
- 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
-
2、方法重写的应用场景
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
-
3、Override注解
用来检测当前的方法,是否是重写的方法,起到【校验】的作用
2.5 方法重写的注意事项(掌握)
- 方法重写的注意事项
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能更低(public > 默认 > 私有)
- 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法
-
示例代码:
public class Fu { private void show() { System.out.println("Fu中show()方法被调用"); } void method() { System.out.println("Fu中method()方法被调用"); } } public class Zi extends Fu { /* 编译【出错】,子类不能重写父类私有的方法*/ @Override private void show() { System.out.println("Zi中show()方法被调用"); } /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */ @Override private void method() { System.out.println("Zi中method()方法被调用"); } /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */ @Override public void method() { System.out.println("Zi中method()方法被调用"); } }
2.6限修饰符 (理解)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iALe9ueW-1634489088337)(day03随堂笔记.assets\02_权限修饰符.png)]
2.7继承中构造方法的访问特点(理解)
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
-
问题:为什么?
-
看一段代码:
//父类 public class Fu { int num = 10; } //子类 public class Zi extends Fu{ public Zi(){ System.out.println(num); } } //测试类 public class Test { public static void main(String[] args) { Zi zi = new Zi();//这行代码执行完,控制台会报错还是打印10? } }
大家思考一个问题:如果一个类的对象都没有被创建,那么这个对象中的成员变量也不会存在堆内存中,
那么子类为什么就可以直接访问到父类中的成员变量呢?
-
总结:
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据,所以子类初始化之前,一定要先完成父类初始化;每一个子类构造方法的第一条语句默认都是:super()
注意: 如果我们编写的类,没有手动指定父类,系统也会自动继承Object(Java继承体系中的最顶层父类)
问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
-
通过使用super关键字去显示的调用父类的带参构造方法-------掌握
-
子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法
-----了解
注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
2.8 super内存图(理解)
-
思考:继承了父类的子类在内存中被创建的时候,在内存中是如何运转?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wm6J47Nl-1634489088338)(day03随堂笔记.assets\01_super内存图-1612277809334.png)]
2.9 黑马信息管理系统使用继承改进 (掌握)
-
黑马信息管理系统改进
-
步骤1:抽取Person类。
-
步骤2:优化StudentController类中,inputStudentInfo方法,将setXxx赋值方式,改进为构造方法初始化
**注意:直接修改这种操作方式,不符合我们开发中的一个原则开闭原则 ( 对扩展开放对修改关闭 ) **
尽量在不更改原有代码的前提下以完成需求
解决:重新创建一个OtherStudentController类编写新的inputStudentInfo方法
-
步骤3:根据StudentController类、OtherStudentController类,向上抽取出BaseStudentController类
再让StudentController类、OtherStudentController类,继承BaseStudentController类
-
3.抽象类
3.1抽象类的概述(理解)
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
3.2抽象类的特点(记忆)
-
抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义 public abstract class 类名 {} //抽象方法的定义 public abstract void eat();
-
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
抽象类不能实例化
-
抽象类可以有构造方法
-
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
-
需求:实现99乘法表
3.3模板设计模式
设计模式简介
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
-
总结:
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
-
历史:
1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。
这 23 种设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。
当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择:
对于简单的程序,可能写一个简单的算法要比引入某种设计模式更加容易;但是对于大型项目开发或者框架设计,用设计模式来组织代码显然更好。
设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
模板设计模式
-
模板设计模式
把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法
让使用模板的类(继承抽象类的类)去重写抽象方法实现需求 -
模板设计模式的优势
模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可
网站涉及到支付----支付的模板类-----获取订单的信息—封装 —调用支付的代码—得到结果(具体的支付方式不确定—抽象方法)------三个子类继承模板类(实现微信支付、支付宝支付、银行卡支付)
现在我们是一个游戏公司—开发一款软件: 英雄流氓 ----涉及到皮肤或者英雄的购买
设计一个模板类来实现支付的过程—组长写这个模板类,但是里面涉及到的三种支付方式可以交给三个哥们去实现这个模板类分别实现,but如果你的这个模板类中的支付方法没有加final------子类直接重写
A哥们–微信支付 有点坏心眼: 重写支付的方法::::
if(username.equals(“渣渣辉”)){
money = 0;
}
3.5final(应用)
-
final关键字的作用
- final代表最终的意思,可以修饰成员方法,成员变量,类
-
final修饰类、方法、变量的效果
-
fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
-
final修饰方法:该方法不能被重写
-
final修饰变量:表明该变量是一个常量,不能再次赋值
-
变量是基本类型,不能改变的是值
-
变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
-
举例
public static void main(String[] args){ final Student s = new Student(23); s = new Student(24); // 错误 s.setAge(24); // 正确 }
-
-
3.6黑马信息管理系统使用抽象类改进 (应用)
- 需求
- 使用抽象类的思想,将BaseStudentController 中的 inputStudentInfo 方法,定义为抽象方法
- 将不希望子类重写的方法,使用 final 进行修饰
4.代码块
4.1代码块概述 (理解)
在Java中,使用 { } 括起来的代码被称为代码块
4.2代码块分类 (理解)
-
局部代码块
-
位置: 方法中定义
-
作用: 限定变量的生命周期,及早释放,提高内存利用率
-
示例代码
public class Test { /* 局部代码块 位置:方法中定义 作用:限定变量的生命周期,及早释放,提高内存利用率 */ public static void main(String[] args) { { int a = 10; System.out.println(a); } // System.out.println(a); } }
-
-
构造代码块
-
位置: 类中方法外定义
-
特点: 每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
-
作用: 将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
-
示例代码
public class Test { /* 构造代码块: 位置:类中方法外定义 特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性 */ public static void main(String[] args) { Student stu1 = new Student(); Student stu2 = new Student(10); } } class Student { { //执行时机:在构造方法调用之前就会执行---构造方法什么时候执行? 创建对象 //new Student() new Student(10); 只要已创建对象 构造代码块一定会执行 System.out.println("好好学习"); } public Student(){ System.out.println("空参数构造方法"); } public Student(int a){ System.out.println("带参数构造方法..........."); } }
-
-
静态代码块
- 位置: 类中方法外定义
- 特点: 需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
- 作用: 在类加载的时候做一些数据初始化的操作
- 示例代码
public class Test { /* 静态代码块: 位置:类中方法外定义 特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次 作用:在类加载的时候做一些数据初始化的操作 */ public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(10); } } class Person { static { System.out.println("我是静态代码块, 我执行了"); } public Person(){ System.out.println("我是Person类的空参数构造方法"); } public Person(int a){ System.out.println("我是Person类的带...........参数构造方法"); } }
4.3黑马信息管理系统使用代码块改进 (应用)
-
需求
使用静态代码块,初始化一些学生数据
-
实现步骤
- 在StudentDao类中定义一个静态代码块,用来初始化一些学生数据
- 将初始化好的学生数据存储到学生数组中
今日作业
-
通过继承改造黑马信息管理系统
-
三个步骤的更改
① 将Student这个类以及Teacher类共性内容抽取:Person
public class Person { private String id; private String name; private String age; private String birthday; String address; public Person() { } public Person(String id, String name, String age, String birthday) { this.id = id; this.name = name; this.age = age; this.birthday = birthday; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday; } }
②:抽取controller的基类:
先复制一份StudentController类为:OtherStudentController,修改inputStudentInfo这个方法,改成有参构造的方式封装student
复制一份StudentController类为:BaseStudentController 将StudentController 和 OtherStudentController分别去继承 BaseStudentController 类,可以将大量的重复的方法以及属性去掉,因为父类有了可以直接使用
-
使用抽象类的思想,将BaseStudentController 中的 inputStudentInfo 方法,定义为抽象方法
将不希望子类重写的方法,使用 final 进行修饰
-
通过模板设计模式实现 99乘法表的改造应用 / 作文题练习
-
阅读书籍:《大话设计模式》
-
开闭原则—第四章
-
模板设计模式----第十章
通读一遍,加深理解
-
-
静态代码块和静态成员变量谁写在前谁就先执行,都会在类的创建之前就会被执行
复的方法以及属性去掉,因为父类有了可以直接使用
使用抽象类的思想,将BaseStudentController 中的 inputStudentInfo 方法,定义为抽象方法
将不希望子类重写的方法,使用 final 进行修饰
-
通过模板设计模式实现 99乘法表的改造应用 / 作文题练习
-
阅读书籍:《大话设计模式》
-
开闭原则—第四章
-
模板设计模式----第十章
通读一遍,加深理解
-
-
静态代码块和静态成员变量谁写在前谁就先执行,都会在类的创建之前就会被执行