面向对象复习、继承、抽象类模板设计模式
第一章 类和对象
定义类的格式:
修饰符 class 类名{
// 成员变量(private修饰)
// 构造方法(空参,满参)
// set\get方法
// 成员方法(行为)
}
类使用权限修饰符:public,默认
修饰符:public ,protected,包私有(package-private),private
1.1创建对象的格式
-
通过调用构造方法创对象:
类名 对象名 = new 类名(实参);
类名 对象名 = new 构造器(实参);//类名和构造方法的名字相同 -
使用对象:符号.(对象名.要什么点什么)
对象名.成员变量名
对象名.成员方法名(实参) -
3.对象的内存图
- 只要是new对象就会在堆区开辟一块独立的空间
- 只要调用方法,方法就会被加载进栈
- 只要方法执行完毕,方法就会被弹栈
1.2匿名对象
- 什么是匿名对象:就是指“没有名字的对象”
有名字的对象:
Student stu = new Student();
stu.show();
stu.study();
匿名对象:
new Student();
- 特点:匿名对象只能使用一次
第二章 继承
2.1为什么要有继承
继承的含义:
继承:在java中指的是“一个类”可以“继承自”“另一个类”。 "被继承的类"叫做: 父类/超类/基类,"继承其他类的类"叫做:子类。继承后,“子类”中就“拥有”了“父类”中所有的成员(成员变量、成员方法)。 “子类就不需要再定义了”。
继承的好处:
- 提高代码的复用性(减少代码冗余,相同代码重复利用)。
- 使类与类之间产生了关系。
2.2继承的格式
通过extends关键字。
Class 父类{…}
Class 子类 extends 父类{…}
需要注意:Java是单继承的,一个类只能继承一个直接父类,并且满足is-a的关系,例如:Dog is a Animal, Student is a Person
人类:
public class Person {
// 成员变量
String name;
int age;
// 功能方法
public void eat(){
System.out.println("吃东西...");
}
public void sleep(){
System.out.println("睡觉...");
}
}
老师类: extends 人类
public class Teacher extends Person {
}
测试:
public class Test {
public static void main(String[] args) {
Teacher t = new Teacher();
System.out.println(t.name);
System.out.println(t.age);
t.eat();
t.sleep();
}
}
- 通过继承可以将一些共性的属性,行为抽取到一个父类中,子类只需要继承即可,提供了代码的复用性【共性抽取】
2.3继承后成员访问规则【重点】
①构造方法不能被继承
public class Fu{
public Fu(){
}
public Fu(String name,int age){
}
}
public class Zi extends Fu{
}
public class Demo {
public static void main(String[] args) {
Zi z = new Zi("刘德华", 17);//编译错误,Zi类没有全参构造
}
}
②父类的“私有成员”可以被子类继承,但子类不能被直接访问。
public class Fu{
private int num = 100;//私有成员,只能在父类内部使用。
private void method(){
System.out.println("私有成员方法");
}
}
public class Zi extends Fu{
}
public class Demo {
public static void main(String[] args) {
Zi z = new Zi();
System.out.println(z.num);// 编译错误
z.method();// 编译错误
}
}
③当通过“子类”访问非私有成员时,先在子类中找,如果找到就使用子类的,找不到就继续去“父类”中找。
public class Fu{
int money = 100;
public void method(){
System.out.println("Fu 类中的成员方法method");
}
}
public class Zi extends Fu{
int money = 1;
public void method(){
System.out.println("Zi 类中的成员方法method");
}
}
public class Demo{
public static void main(String[] args){
Zi z = new Zi();
System.out.println(z.money);//1
z.method();// Zi 类中的成员方法method
}
}
2.4方法重写
当父类的逻辑不满足子类使用的时候,可以覆盖重写父类的方法
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
public class Fu{
public void eat(){
System.out.println("我吃牛肉炖土豆...");
}
}
public class Zi extends Fu{
@Override
public void eat(){//方法重写
System.out.println("我吃红烧狮子头...");
}
}
//测试类
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.eat();// 我吃红烧狮子头...
}
}
重写的注意事项
- 方法重写是发生在子父类之间的关系。
- 子类方法重写父类方法,返回值类型、方法名和参数列表都要一模一样。
- 子类方法重写父类方法,必须要保证权限大于等于父类权限。
- 访问权限从大到小: public < protected < (默认) < private
建议保持一致 - 使用@Override注解,检验是否重写成功,重写注解校验!
- 建议重写方法都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!
2.5this和super关键字
- this:存储的“当前对象”的引用;
this可以访问:本类的成员属性、成员方法、构造方法;- super:存储的“父类对象”的引用; super可以访问:父类的成员属性、成员方法、构造方法;
this关键字的三种用法
- this访问本类成员变量: this.成员变量
this访问本类成员变量: this.成员变量
public class Student{
String name = "张三";
public void show(){
String name = "李四";
System.out.println("name = " + name);// 李四
System.out.println("name = " + this.name);// 张三
}
}
- this访问本类成员方法: this.成员方法名();
public class Student{
public void show(){
System.out.println("show方法...");
this.eat();
}
public void eat(){
System.out.println("eat方法...");
}
}
- this访问本类构造方法: this()可以在本类的一个构造方法中,调用另一个构造方法
public class Student{
public Student(){
System.out.println("空参构造方法...");
}
public Student(String name) {
this();//当使用this()调用另一个构造方法时,此代码必须是此构造方法的第一句有效代码。
System.out.println("有参构造方法...");
}
}
public class Demo {
public static void main(String[] args) {
Student stu2 = new Student();
}
}
super关键字的三种用法
- super访问父类的成员变量: super.父类成员变量名
public class Fu{
int money = 100;
}
public class Zi extends Fu{
int money = 10;
public void show(){
int monet = 1;
System.out.println(“money : “ + money);//1
System.out.println(“this.money : “ + this.money);//10
System.out.println(“super.money:” + super.money);//100 直接去父类中找
}
}
- super访问父类的成员方法: super.成员方法名();
public class Fu{
public void show(){
System.out.println("父类的show方法...");
}
}
public class Zi extends Fu{
public void show(){
super.show();
System.out.println("子类的show方法...");
}
}
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
}
}
- super访问父类的构造方法: super()
public class Fu{
public Fu(){
System.out.println("Fu 类的空参构造方法..");
}
public Fu(String name, int age) {
System.out.println("Fu 类的有参构造方法..");
}
}
public class Zi extends Fu{
public Zi(){
super();// 调用父类的空参构造方法
System.out.println("Zi 类的空参构造方法..");
}
public Zi(String name,int age){
super(name,age);// 调用父类的有参构造方法
System.out.println("Zi 类的有参构造方法..");
}
}
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println("----------------------");
Zi z2 = new Zi("刘德华", 17);
}
}
注意事项
- super访问成员变量和成员方法: 优先去父类中找,如果有就直接使用,如果没有就去爷爷类中找,如果有,就用,依次类推…
- 子类的构造方法默认会调用父类的空参构造方法,如果父类中的没有空参构造方法,只定义了有参构造方法,会编译报错
2.6继承内存图原理
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。
public class Fu {
int num = 10;
int numFu = 100;
public void method(){
System.out.println("父类中的method方法");
}
}
public class Zi extends Fu {
int num = 20;
public void show(){
int num = 30;
System.out.println("访问局部变量num:"+num);
System.out.println("访问本类成员变量num:"+this.num);
System.out.println("访问父类成员变量num:"+super.num);
}
@Override
public void method() {
super.method();
System.out.println("子类中的method方法");
}
}
public class ExtendsDemo1 {
public static void main(String[] args) {
// 创建一个子类对象
Zi zi = new Zi();
请问:内存中存在几个对象?
1.存在两个,子类对象,父类对象
2.只存在一个子类的对象【正解】
// 使用子类对象调用show方法
zi.show();
// 使用子类对象调用method方法
zi.method();
}
}
继承的特点:
- Java只支持单继承,不支持多继承。
- 一个类只能有一个父类,但是可以有多个子类。
- 可以多层继承。【有爷爷】
第三章 抽象类的概述和定义
抽象类的概述
-
概述: 使用abstract关键字修饰的类就是抽象类
-
特点: 这种类不能被创建对象,它就是用来做父类的,被子类继承的
抽象类的定义 -
格式:
修饰符 abstract class 类名{}
- 例如:
public abstract class Person{}
- 没有方法体,使用abstract修饰的方法就是抽象方法
public void work(){ //具体方法,具体逻辑 }
修饰符 abstract 返回值类型 方法名(形参列表); 例如: public abstract void work();
注意:如果子类是一个抽象类,父类是抽象类,那么子类可以不用重写父类中的抽象方法
如果子类是一个普通类,父类是抽象类,那么子类必须重写父类中所有的抽象方法
3.1模板设计模式
设计模式概述
- 设计模式就是解决一些问题时的固定思路,也就是代码设计思路经验的总结。
特定的解决问题的方案,最佳实践
模板设计模式概述
- 针对某些情况,在父类中指定一个模板,然后根据具体情况,在子类中灵活的具体实现该模板
- 抽象类体现的就是模板思想,模板是将通用的东西在抽象类中具体的实现,而模板中不能决定的东西定义成抽象方法,让使用模板(继承抽象类的类)的类去重写抽象方法实现需求
- 抽象类: — 看成是模板
- 模板是将通用的东西在抽象类中具体的实现 : 非抽象方法定义一些通用的功能
- 模板中不能决定的东西定义成抽象方法
模板模式的实现步骤
- 定义父类(抽象类)作为模板
- 在父类中定义"模板方法"— 实现方法(模板)+抽象方法(填充模板)
- 子类继承父类,重写抽象方法(填充父类的模板)
- 测试类:
- 创建子类对象,通过子类调用父类的“实现的方法”+ “子类重写后的方法” e
案例演示
假如我现在需要定义新司机和老司机类,新司机和老司机都有开车功能,开车的步骤都一样,只是驾驶时的姿势有点不同,新司机:开门,点火,双手紧握方向盘,刹车,熄火,老司机:开门,点火,右手握方向盘左手抽烟,刹车,熄火。那么这个时候我们就可以将固定流程写到父类中,不同的地方就定义成抽象方法,让不同的子类去重写
分析:
- 定义司机抽象类,作为模板
- 开车功能(非抽象方法): 开车的步骤一样的
- 开门
- 点火
- 开车姿势
- 刹车
- 熄火
- 开车姿势功能(抽象方法): 供子类重写
- 开车功能(非抽象方法): 开车的步骤一样的
- 新司机类 继承司机类
- 老司机类 继承司机类
// 司机开车的模板类
public abstract class Driver {
public void go() {
System.out.println("开门");
System.out.println("点火");
// 开车姿势不确定?定义为抽象方法
ziShi();
System.out.println("刹车");
System.out.println("熄火");
}
public abstract void ziShi();
}
司机模板
public class NewDriver extends Driver {
@Override
public void ziShi() {
System.out.println("新司机双手紧握方向盘");
}
}
public class OldDriver extends Driver {
@Override
public void ziShi() {
System.out.println("老司机右手握方向盘左手抽烟...");
}
}
测试类
public class Demo02 {
public static void main(String[] args) {
NewDriver nd = new NewDriver();
nd.go();
OldDriver od = new OldDriver();
od.go();
}
}
可以看出,模板模式的优势是,模板已经定义了通用架构,使用者只需要关心自己需要实现的功能即可!非常的强大!
抽象类存在的意义是为了被子类继承,否则抽象类将毫无意义,抽象类体现的是模板思想,模板是将通用的东西在抽象类中具体的实现,而模板中不能决定的东西定义成抽象方法,让使用模板(继承抽象类的类)的类去重写抽象方法实现需求,这是典型的模板思想。