我在千峰培训的日子第十二–十三天------面向对象【继承】
今日感悟
人生最遗憾的莫过于放弃了不该放弃的,固执的坚持了不该坚持的。
老师知识点
一、局部变量跟成员变量
形参和实参
形参:定义在方法中小括号中的变量 例如 public void eat(String name) {}
形参的作用域同局部变量一样,只在自己的方法中有用
实参:实际传给形参的数值, 例如: Person p = new Person("王策");
成员变量和局部变量区别
区别 | 成员变量 | 局部变量 |
---|---|---|
定义位置 | 定义在类大括号中 | 定义在方法大括号中 |
作用域 | 哪里有对象地址,哪里就是成员变量的作用域 | 方法对应大括号中 |
内存储存 | 堆空间中 | 栈空间中 |
初始化值 | 在没有被构造方法初始化值的情况下,成员变量存储的都是对应数据类型的零值。 | 局部变量在没有被赋值的情况下不能进行除赋值以外的所有操作 |
生存期 | 成员变量随着对象的创建而出现,随着类的销毁而销毁,JVM通过GC机制管理内存 | 在对应的方法大括号内 |
零值:基本数据类型:int short byte ----0, long----0L, char----'\0' 编码集中编号为 0 的字符 nul 不可见字符 double----0.0, float----0.0F, bolean----false
引用数据类型: null
GC内存回收机制简介
轮询机制:会在单位时间内来巡查整个内存使用情况,判断当前对象是否有引用地址,如果第一次发现无主内存就标记起来,第二次发现当前内存有人使用,就取消标记,还是无主内存的话就会回收内存
内存引用计数技术:一块内存,有多个引用指向,有几个计数器就会计数几个,如果所有的引用都不在指向当前内存,当前内存明确为无主内存,销毁。
二、继承
继承格式
引入:【基类】--包含了一类事物的基本属性跟基本行为
【派生类】--在基类的基础上加入自己独有的属性,数据,特点
【重点】
对与Java而言,继承使用更多是为了保证数据类型的一致化,数据类型的统一化,和方法的规范化。
//关键字: extends
//格式:
class Son extends Father {
}
//java中是单继承,一个子类只能可以有一个父类,而一个父类可以有不同的子类
class Father {
// public 修饰的成员变量
public String test = "EDG NB!!!";
// private 修饰成员变量【私有化】
private double btc = 100000000000000000000.01;
// public 修饰成员方法
public void test() {
System.out.println("EDG NB!!!");
}
private void testPrivate() {
System.out.println("LPL No.1");
}
}
class Son extends Father {
}
public class Demo1 {
public static void main(String[] args) {
Son son = new Son();
// 子类对象可以使用通过继承得到的父类 pubilc 修饰成员变量 Field
System.out.println(son.test);
// 子类对象可以使用通过继承得到的父类 pubilc 修饰成员方法 Method
son.test();
/*
* The field Father.btc is not visible
* 子类不可以通过继承得到父类 private 修饰的私有化成员变量 Field
*/
// System.out.println(son.btc);
/*
* The method testPrivate() from the type Father is not visible
* 子类不可以通过继承得到父类 private 修饰的私有化成员方法 Method
*/
// son.testPrivate();
}
}
super关键字的使用
当父类和子类的属性不同名的时候这时候调用属性时没有影响的,如果同名变量,在子类中访问父类的非私有成员变量时,需要使用super关键字,修饰父类成员变量。
class Fu {
// Fu中的成员变量。
int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num2 = 6;
// Zi中的成员方法
public void show() {
// 访问父类中的num,
System.out.println("Fu num="+num);
// 继承而来,所以直接访问。
// 访问子类中的num2
System.out.println("Zi num2="+num2);
}
}
class ExtendDemo02 {
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show方法
z.show();
}
}
演示结果:
Fu num = 5
Zi num2 = 6
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
//访问父类中的num
System.out.println("Fu num=" + super.num);
//访问子类中的num
System.out.println("Zi num=" + this.num);
}
}
演示结果:
Fu num = 5
Zi num = 6
小贴士:Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能 直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员 变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
方法的重写
当父类和子类的方法不同名的时候这时候调用方法时没有影响的,对象调用方法的时候会现在子类中查看是不是有该方法,如果没有就会执行父类中的方法。
但是当父类中的方法跟子类的方法返回值类型,方法名,参数列表都相同的时候,会出现覆盖效果,也叫复写重写
//@Override 注解---开启重写格式的严格检查,要求子类重写方法声明和父类必须完成一致。如果不一致,直接报错
class Father {
int age;
String name;
public void game() {
System.out.println("麻将");
}
}
class Son {
int age;
String name;
@Override
public void game() {
System.out.println("LOL WOT PUBG GTA5");
}
}
public class Demo {
public static void main(String[] args) {
Boy boy = new Boy();
/*
* 子类可以使用父类的成员方法,但是父类的成员方法不能满足子类实际需求。
*
* 想满足以下情况
* 1. 方法不要多,降低方法的冗余度,保持方法名称一致
* 2. 方法执行的目标不一致,方法体不一致,
* 【重写】
* Override
* 在满足方法声明不变的情况下,降低方法的冗余和记忆压力,通过修改方法体内容
* 完成子类方法的执行目标修改,更加满足子类需求
*/
boy.game();
boy.work();
}
}
注意 1.子类对象重写父类方法的时候,权限必须大于等于父类的权限
2.重写方法返回值类型,方法名,参数列表必须完全一样
创建子类对象会执行父类的构造方法
1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的
2. 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构 造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。代码如下
class Fu {
private int n;
Fu() {
System.out.println("Fu()");
}
}
class Zi extends Fu {
Zi() {
// super(),调用父类构造方法
super();
System.out.println("Zi()");
}
}
public class ExtendsDemo {
public static void main (String args[]) {
Zi zi = new Zi();
}
}
输出结果:
Fu()
Zi()
package com.qfedu.b_extends;
class Yeye {
public Yeye() {
System.out.println("爷爷类构造方法");
}
}
class Fu extends Yeye {
public Fu() {
System.out.println("父类构造方法");
}
}
class Ez extends Fu {
public Ez() {
System.out.println("子类构造方法");
}
}
//执行结果
爷爷类构造方法
父类构造方法
子类构造方法
继承技术点总结
1.在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空 间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构 造方法调用时,一定先调用父类的构造方法。
2. 子类可以通过继承获得父类的非私有化成员 ==>【成员变量 和 成员方法】
3. 子类不可以通过继承获取父类的【私有化成员】 ==> 留一手
4.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HZOwCNGt-1636456044095)(C:\Users\wc\AppData\Roaming\Typora\typora-user-images\image-20211108185726445.png)]
三、关键字final
final 最后的,最终的
可以修饰以下内容
1. 成员变量
final修饰成员变量
【语法要求】
final修饰的成员变量,必须在定义时初始化,并且一旦赋值,无法修改,为【常量】 ==> 带有名字的常量
2. 成员方法
final修饰成员方法
【语法要求】
final修饰的成员方法,不能被子类重写, 为最终方法。 --> 一般用于核心方法,涉密方法。
3. 局部变量
final修饰局部变量
【语法要求】
一旦被赋值,无法修改
4. 类
final修饰类
【语法要求】
final修饰的类,不能被其他类继承,没有子类,断子绝孙
class SingleDog {
public String name;
public int age;
}
public class Demo {
public static void main(String[] args) {
final SingleDog sd = new SingleDog();
/*
请问以下操作哪一个正确, 哪一个错误
1.sd = new SingleDog();
2.sd.name = "大哥";
3.sd.age = 12;
【注意】
指向地址不可变,指向内容可变!!!
当前final修饰一个类对象 ==> 引用数据类型变量
当前引用数据类型变量中存储的数据不可以变 ==> 引用数据类型变量存储的内容不可变 ==> 内容是一个【地址】
==> 指向地址不可变
SingleDog类的成员变量 name age 没有任何的 final 修饰,存储数据
*/
// sd = new SingleDog(); sd 是一个 final 修饰的引用数据类型变量,指向地址不可变,指向内容可变
sd.name = "大哥";
sd.age = 12;
}
}
四、多类合作
场景分析:
台式机电脑:
台式机电脑需要 屏幕和键盘 来组成
台式机电脑是一个类,屏幕和键盘是否可以看做是另一个类
成员方法:
换键盘,换屏幕
键盘类:
成员变量:
品牌
按键个数
屏幕类:
成员变量:
品牌
尺寸
/**
* 把另一个类对象作为参数传递给一个类的对象的创建
*/
public class MainProject {
public static void main(String[] args) {
/*
* 创建屏幕类对象和键盘类对象【进货】
*/
Screen screen = new Screen("AOC", 24);
Keyboard keyboard = new Keyboard("Cherry", 108);
/*
* 组装电脑, 利用键盘类对象和屏幕类对象作为方法的参数,创建Computer类对象。
*/
Computer computer = new Computer(keyboard, screen);
/*
* 看一下电脑的配置,当前电脑的屏幕属性和键盘属性
*/
computer.show();
/*
* 屏幕出现问题
*/
System.out.println("--------------------屏幕出现问题--------------------");
Screen screen2 = new Screen("华硕", 26);
/*
* 当前Computer类对象 computer 调用setter方法,更换屏幕
*/
computer.setScreen(screen2);
/*
* 电脑展示配置
*/
computer.show();
/*
* 键盘出现问题
*/
System.out.println("--------------------键盘出现问题--------------------");
Keyboard keyboard2 = new Keyboard("IKBC", 87);
/*
* 当前Computer类对象 computer 调用 setter 方法,更换键盘
*/
computer.setKeyboard(keyboard2);
/*
* 再次展示电脑配置
*/
computer.show();
}
}
五、抽象类
引入:父类中的方法被子类重写,那么父类中的方法主体会被子类里面重新覆盖,就没啥卵用了,只保留方法声明就可以,我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
/**定义:
抽象方法 : 没有方法体的方法
抽象类:包含抽象方法的类
修饰符 abstract 返回值类型 方法名 (参数列表);*/
class LolHero {
public void q() {
System.out.println("Q技能");
}
public void w() {
System.out.println("W技能");
}
public void e() {
System.out.println("E技能");
}
public void r() {
System.out.println("R技能");
}
}
/**
* HappyWindBoy 继承 LOLHero 类 这里可以通过继承得到 QWER 方法
*
*
*/
class HappyWindBoy extends LOLHero {
}
/**
* Aphelios 厄斐琉斯 继承LOLHero类,可以通过继承得到 QWER 方法
*
*
*/
class Aphelios extends LOLHero {
}
public class Demo1 {
public static void main(String[] args) {
HappyWindBoy happyWindBoy = new HappyWindBoy();
Aphelios aphelios = new Aphelios();
/*
* 从目前的 Java 程序运行角度,每一个方法都没有问题,每一个技能都没有问题。
*
* 如果英雄联盟每一个英雄的技能都一样,还有什么可玩性???
* 没有可玩性
*
* 【要求】
* 目前代码期望子类可以重写父类中的方法,从而满足子类的特征性,非一致性。
* 当前代码没有重写父类方法的情况下,语法没有任何的错误!!!【强制重写语法要求】
*
* 【强制重写语法要求】
* 子类没有重写/没有实现父类的方法,语法报错。
*/
happyWindBoy.q();
happyWindBoy.w();
happyWindBoy.e();
happyWindBoy.r();
System.out.println();
aphelios.q();
aphelios.w();
aphelios.e();
aphelios.r();
}
}
//希望的代码
abstract class LOLHero {
/*
* 期望子类可以重写 q 方法,使用 abstract 关键字修饰 q 方法
*/
abstract public void q();
abstract public void w();
abstract public void e();
abstract public void r();
}
/**
* HappyWindBoy 继承 LOLHero 类 这里可以通过继承得到 QWER 方法
* @author Anonymous
*
*/
class HappyWindBoy extends LOLHero {
@Override
public void q() {
System.out.println("斩钢闪");
}
@Override
public void w() {
System.out.println("风之障壁");
}
@Override
public void e() {
System.out.println("踏前斩");
}
@Override
public void r() {
System.out.println("狂风绝息斩");
}
}
/**
* Aphelios 厄斐琉斯 继承LOLHero类,可以通过继承得到 QWER 方法
* @author Anonymous
*
*/
class Aphelios extends LOLHero {
@Override
public void q() {
System.out.println("武器技能");
}
@Override
public void w() {
System.out.println("月相轮转");
}
@Override
public void e() {
System.out.println("武器队列系统");
}
@Override
public void r() {
System.out.println("清辉夜凝");
}
}
public class Demo1 {
public static void main(String[] args) {
HappyWindBoy happyWindBoy = new HappyWindBoy();
Aphelios aphelios = new Aphelios();
/*
* 从目前的 Java 程序运行角度,每一个方法都没有问题,每一个技能都没有问题。
*
* 如果英雄联盟每一个英雄的技能都一样,还有什么可玩性???
* 没有可玩性
*
* 【要求】
* 目前代码期望子类可以重写父类中的方法,从而满足子类的特征性,非一致性。
* 当前代码没有重写父类方法的情况下,语法没有任何的错误!!!【强制重写语法要求】
*
* 【强制重写语法要求】
* 子类没有重写/没有实现父类的方法,语法报错。
*/
happyWindBoy.q();
happyWindBoy.w();
happyWindBoy.e();
happyWindBoy.r();
System.out.println();
aphelios.q();
aphelios.w();
aphelios.e();
aphelios.r();
}
}
注意:
1.abstract 关键字修饰的方法没有方法体
2.abstract 修饰的方法有且只能定义在 abstract 修饰的类内 或者 interface 接口内
3.一个非 abstract 修饰的类 继承 abstract 类,必须实现在 abstract 类内所有使用 abstract 修饰没有方法体的方法。
4. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义
5.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
6. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
7.abstract 修饰的方法、对应的类、包括后期学习的接口,都是在制定规则,制定准则。abstract 后期也会作为【承上启下】的功能存在。