面向对象进阶2(包、权限修饰符、抽象类、接口)
# 面向对象进阶2(包、权限修饰符、抽象类、接口)
# 1. 包
# 1.1 什么是包
包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护
建包的语法格式:package 公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
如:
package com.itheima.javabean; public class Student { }
1
2
3
4
5建包语句必须在第一行,一般IDEA工具会帮助创建
# 1.2 导包
相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!
导包格式:
import 包名.类名
若一个类中要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问
# 2. 权限修饰符
# 2.1 什么是权限修饰符?
用来控制一个成员能够被访问的范围的。
可以修饰成员变量、方法、构造器、内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
# 2.2 权限修饰符的分类和作用范围
四种作用范围由小到大为:private -> 缺省 -> protected - > public
缺省为包访问权限。
修饰符 | 同一个类中 | 同一个包中 其他类 | 不同包下的 子类 | 不同包下的 无关类 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
# 2.3 权限修饰符的使用
自己定义成员(方法,成员变量,构造器等)一般满足如下要求:
- 成员变量一般私有。
- 方法一般公开。
- 如果该成员只希望本类访问,使用 private 修饰。
- 如果该成员只希望本类、同一个包下的其他类和子类访问,使用 protected 修饰。
- 子类访问是说,要在子类中用子类对象访问,用父类对象访问还是会报错的。
# 3. final 关键字及常量、枚举
# 3.1 final 关键字
可以修饰 方法,变量,类
- 修饰方法:表明该方法是最终方法,不能被重写
- 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)
- final 修饰实例成员变量,几乎不用。因为实例成员变量属于对象,当他指定之后,表示每个对象的这个成员变量都是固定的了,没意义。
- final 修饰静态成员变量,该变量就变成了常量。
- 修饰类:表明该类是最终类,不能被继承 <被绝育了!!>
- 比如工具类,就可以用 final 修饰,工具类就不要继承啦,你拿来直接用就行啦~
final 修饰变量的注意
- final 修饰的变量是基本类型:那么变量存储的数据值不能发生改变
- final 修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的
- 这个地址就相当于钥匙,钥匙不能变了,但家里是可以变化的。
- 即当 t 指向了一个对象时,
t=null
是不合法的,但 t 指向的内容中的成员变量值是可以变化的。
# 3.2 常量
# 3.2.1 常量概述和基本作用
- 啥是常量
- 常量是使用了
public static final
修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变 - 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性
- 常量是使用了
- 常量命名规范
- 英文单词全部大写,多个单词下划线连接起来
- 常量的执行原理
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的
# 3.2.2 常量做信息标志和分类
案例:开发超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向
实现:用 UP=1, DOWN=2, LEFT=3, RIGHT=4 分别表示四种动作,调用移动函数 move() 时,就可以直接传 UP 等常量进来,而不是传 1, 2.... 进来,代码可读性更好。
用常量做信息标志和分类:代码可读性好,实现了软编码形式
⚠️ 用常量做信息标志不是很好,用枚举会更好
# 3.3 枚举
# 3.3.1 枚举的概述
枚举是 Java 中的一种特殊类型
枚举的作用:"是为了做信息的标志和信息的分类"
定义枚举的格式:
修饰符 enum 枚举名称{ 第一行都是罗列枚举类实例的名称。 }
1
2
3例:
enum Season{ SPRING , SUMMER , AUTUMN , WINTER; }
1
2
3反编译后的结果:
Compiled from "Season.java" public final class Season extends java.lang.Enum<Season> { public static final Season SPRING = new Season(); public static final Season SUMMER = new Season(); public static final Season AUTUMN = new Season(); public static final Season WINTER = new Season(); public static Season[] values(); public static Season valueOf(java.lang.String); }
1
2
3
4
5
6
7
8
9
10其实就是把普通类定义中的 class 换成了 enum,也可以加权限修饰符
枚举的特征
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承
- ⚠️ 构造器都是私有的,枚举对外不能创建对象
- 枚举类的第一行默认罗列枚举对象的名称
- 枚举类相当于多例模式
- 对外不能再创建对象了,而内部已经创建了四个对象
# 3.3.2 枚举的使用场景
案例:开发超级玛丽游戏
- 选择常量做信息标志和分类:
- 虽然可以实现可读性,但是入参值不受约束,代码相对不够严谨。(定义了 UP=1,我也可以不用 UP,就非得用 1,此时也是可以的。或者我用不合法的数值作为参数,也没有校验)
- 枚举做信息标志和分类:
- 代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用!
# 4. 抽象类
# 4.1 抽象类概述
若一个类中的某个方法的具体实现不能确定,就可以申明成 abstract 修饰的抽象方法(不能写方法体了),对应的类也必须用 abstract 修饰,被称为抽象类。
示例代码:
public abstract class Animal{
public abstract void run();
}
2
3
- 抽象类的作用:
- 可以被子类继承、充当模板,同时也可以提高代码复用
- 抽象类的注意事项:
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
# 4.2 抽象类的案例
系统需求:
加油站推出两种支付卡,金卡和银卡,享受不同的打折优惠,金卡8折,银卡8.5折。分别实现2种卡片进入收银系统后的逻辑,卡片需要包含主人名称,余额,支付功能。
分析实现:
创建一张卡片父类:定义属性包括主人名称、余额、支付功能(具体实现交给子类)
创建一张白金卡类:重写支付功能,按照原价的8折计算输出。
创建一张银卡类:重写支付功能,按照原价的8.5折计算输出。
抽象类的特征和注意事项
- 有得有失: 得到了抽象方法,失去了创建对象的能力
- 即,抽象类不能创建对象
- 原因:
- 若能够创建对象,则调用其中的抽象方法无法正常运行(连方法体都没有)
- 哪怕这个抽象类中没有抽象方法,也不能实例化
- 类有的成员(成员变量、方法、构造器)抽象类都具备
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
- 一般不层层抽象使用
- 不能用abstract修饰变量、代码块、构造器,只能用来修饰类和方法
final 和 abstract 的关系
final 和 abstract 为**互斥关系**:
- abstract 定义的抽象类作为模板让子类继承,final定 义的类不能被继承
- 抽象方法定义通用功能让子类重写,final 定义的方法子类不能重写
# 4.3 设计模式:模板方法模式
模板方法模式为抽象类的应用。
# 4.3.1 模板方法模式的使用场景
当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候,通常用模板方法模式。
# 4.3.2 模板方法模式的实现步骤
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码(一个通用结构)
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现
# 4.3.3 案例:中小学生写作文
需求:中、小学生都要写《我的爸爸》,每类学生的标题、第一段、最后一段的内容必须一样,正文自己发挥。
解决:父类定义为抽象方法,将共同部分实现,剩下的正文书写部分定义为抽象方法,让子类去实现。
模板方法建议使用 final 修饰,更专业,原因:
模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法就失效了。
使用者只需要关心自己需要实现的功能即可。
如,下图中,用 final 修饰模板方法 write,防止子类对其重写。
# 5. 接口
# 5.1 接口的概述和特点
# 5.1.1 啥是接口
接口是一种规范,规范一定是公开的
是一种特殊的抽象类
关键字:interface
定义格式:
public interface 接口名 { // 常量 // 抽象方法 }
1
2
3
4
# 5.1.2 接口的特点
- 是一种特殊的抽象类
- 默认是公开的
- JDK8 之前接口中只能是抽象方法和常量,没有其他成分了。
- 若有其他东西,就变成普通的抽象类了
- 接口不能实例化。
- 接口中的成员都是 public 修饰的,写不写都是,因为规范的目的是为了公开化。
- 成员变量可以省略 public static,也是默认公开的
- 成员方法直接不写 public 常用
# 5.2 接口的基本使用:被实现
接口是用来被类实现(implements)的,实现接口的类称为**实现类**。实现类可以理解成所谓的子类。
示例:
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {
}
2
接口可以被类单实现,也可以被类多实现。
⚠️ 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
interface VS abstract
接口没对象
接口可以多实现(implements)
抽象类一个儿子只能有一个亲爸,可以有多个儿子有同一个亲爸
接口一个儿子可以有多个干爹
提示
- 类和类的关系:单继承
- 类和接口的关系:多实现
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口
- 作用:规范合并,整合多个接口为同一个接口,便于子类实现
- 例如,篮球运动员要实现运动员、遵纪守法、人的接口,在定义这个类时需要对多个接口进行实现,但若晕哦动员接口已经多继承了遵纪守法、人的接口时,篮球运动员就可以只实现运动员接口了
# 5.3 JDK8 开始接口新增方法
JDK8 版本开始后,Java 对接口的成员方法进行了新增。
原因:
当项目 1.0 成功上线没问题之后, 2.0 版本需要对某个接口进行丰富,加入更多抽象方法,此时,如果修改这个接口,就意味着此接口的所有实现类都要去实现所有的这些方法,这些实现类都需要再去修改代码。
问题来啦! 咋就能在丰富接口功能的同时又不对子类代码进行更改呢??
——solve:允许接口中直接定义带有方法体的方法,故JDK8 之后新增的方法有:默认方法、静态方法、私有方法三种,他们都会默认被 public 修饰。
# 5.3.1 默认方法
本质上就是之前提到的普通实例方法,必须有 default 修饰。
默认会 public 修饰。
⚠️ 需要用接口的实现类的对象来调用。
# 5.3.2 静态方法
必须 static 修饰,默认会 public 修饰。
⚠️ 注意:接口的静态方法必须用本身的接口名来调用。
# 5.3.3 私有方法
本质就是私有的实例方法,必须使用 private 修饰,从 JDK 1.9 才开始有的。
只能在**本类中(本接口中)**被其他的默认方法或者私有方法访问。
⚠️ 只能在接口内部被调用。
heima:JDK8 新增的 3 种方法我们自己在开发中很少使用,通常是 Java 源码涉及到的,我们需要理解、识别语法、明白调用关系即可。
接口的注意事项
1、接口不能创建对象。
2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。
因为接口的静态方法只能用接口本身来调用。
3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
父类是亲爸,接口是干爹。
一个 class 要先 extends 再implements。
4、一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。