目录
一 封装
1.1 概述
1.2 为什么要进行封装?
1.3 Java中的封装
1.4 四种访问权限修饰符
1.5 练习
二 继承
2.1 继承的由来
2.2 继承的好处
2.3 语法格式
2.4 继承的特点之一:成员变量
2.4.1 父类成员变量私有化
2.4.2 父类和子类成员变量重名
2.4.3 成员变量的访问特点
2.5 继承的特点之二:成员方法
2.5.1成员方法的访问特点
2.5.2方法重写( override )
2.5.2 方法重载( overload )
2.6 继承的特点之三:构造方法
2.7 继承的特点之四:单继承
三 多态
3.1 概述
3.2多态的应用场景
3.3 多态中调用成员的特点
3.3.1示例
3.3.2多态调用成员的内存图解
3.4多态的优势和弊端
3.4.1 多态的优势
3.4.2多态的弊端
一 封装
1.1 概述
对象代表什么,就得封装对应的数据,并提供数据对呀的行为

- 程序设计追求
高内聚、低耦合:
-
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅对外暴露少量的方法用于使用。
- 封装:隐藏对象内部的复杂性,只对外部公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。换言之,
将该隐藏的隐藏起来,该暴露的暴露出来。
1.2 为什么要进行封装?
- 使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
- 示例:
package com.encapsulation.demo1;
/**
* 动物
*
* @author 逆水行舟
* @version 1.0
* @since 2022-12-01
*/
public class Animal {
/**
* 腿
*/
public int legs;
/**
* 吃饭的方法
*/
public void eat() {
System.out.println("吃饭");
}
/**
* 移动的方法
*/
public void move() {
System.out.println("移动");
}
}
package com.encapsulation.demo1;
/**
* @author 逆水行舟
* @version 1.0
* @since 2022-12-01
*/
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
// 如果这边赋值的是-100?,不合法啊
// 应该将legs属性保护起来,防止乱用
animal.legs = 4;
System.out.println(animal.legs); // 4
animal.eat();
animal.move();
}
}
1.3 Java中的封装
- Java 中通过将数据声明为私有的(
private),再提供公共的(public)方法:setXxx()和getXxx()来实现对该属性的操作,以实现以下的目的: - ① 隐藏一个类中不需要对外提供的实现细节。
- ② 使用者只能通过事先定制好的
方法来访问数据,可以方便的加入控制逻辑,限制对属性的不合理操作。 - ③ 便于修改,增强代码的维护性。
- 示例:
package com.encapsulation.demo2;
/**
* 动物
*
* @author 逆水行舟
* @version 1.0
* @since 2022-12-01
*/
public class Animal {
/**
* 腿 将属性进行私有化
*/
private int legs;
public int getLegs() {
return this.legs;
}
public void setLegs(int legs) {
if (legs != 0 && legs != 2 && legs != 3) {
System.out.println("动物的腿一般为0、2、4");
return;
}
this.legs = legs;
}
/**
* 吃饭的方法
*/
public void eat() {
System.out.println("吃饭");
}
/**
* 移动的方法
*/
public void move() {
System.out.println("移动");
}
}
package com.encapsulation.demo2;
/**
* @author 逆水行舟
* @version 1.0
* @since 2022-12-01
*/
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
// 非法 动物的腿一般为0、2、4
animal.setLegs(-100);
System.out.println(animal.getLegs());
animal.eat();
animal.move();
}
}
1.4 四种访问权限修饰符
- Java 权限修饰符 public、protected 、缺省 、private 置于
类的成员定义前,用来限定对象对该类成员的访问权限。
| 修饰符 | 类内部 | 同一个包中 | 不同包的子类 | 同一个工程 |
| private | √ | |||
| 缺省 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |
- 对于 class 的权限修饰符只可以用 public 和 缺省 :
-
- public 类可以在任意地方被访问。
- 缺省类 只可以被同一个包内部的类访问。
1.5 练习
- 创建程序,在其中定义两个类:Person 和 PersonTest 。定义如下:用 setAge() 设置人的合法年龄( 0~130 ),用 getAge() 返回人的年龄。用PersonTest 类中实例化 Person 类的对象 b ,调用 setAge() 和 getAge() 方法,体会 Java 的封装性。
- 示例:
package com.encapsulation.demo3;
/**
* @author 逆水行舟
* @version 1.0
* @since 2022-12-01
*/
public class Person {
/**
* 年龄
*/
private int age;
/**
* 获取年龄
*
* @return int
*/
public int getAge() {
return this.age;
}
/**
* 设定年龄
*
* @param age
* 年龄
*/
public void setAge(int age) {
if (age < 0 || age > 130) {
System.out.println("输入不合法");
return;
}
this.age = age;
}
@Override
public String toString() {
return "Person{" + "age=" + this.age + '}';
}
}
package com.encapsulation.demo3;
/**
* @author 逆水行舟
* @version 1.0
* @since 2022-12-01
*/
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setAge(50);
System.out.println(person.getAge());
System.out.println(person);
}
}
二 继承
2.1 继承的由来

- 多个类中存在相同属性和行为的时候,将这些内容抽取到单独的一个类中,那么多个类中无需再定义这些属性和行为,只需要继承那个类即可。

- 此处的多个类称为
子类(派生类),单独的这个类称为父类(基类或超类)。 - 继承描述的是事物之间的所属关系,这种关系是:
is a的关系。例如:图中的猫属于动物,狗也属于动物。由此可见,父类更通用,子类更具体。通过继承,可以使得多种事物之间形成一种关系体系。
2.2 继承的好处
- ① 继承的出现,减少了代码的冗余(重复),提供了代码的复用性。
- ② 继承的出现,有利于功能的扩展。
- ③ 继承的出现,让类和类之间产生了关系,提供了多态的前提。
注意:不要为了获取其它类中的某个功能而去继承。
2.3 语法格式
- 在 Java 中,继承是通过
extends关键字,声明一个子类继承一个父类。 - 语法:
修饰符 class 父类{
...
}
修饰符 class 子类 extends 父类 {
...
}
- 示例
public class Animal {
String name;
int age;
public void eat(){
System.out.println("动物:吃");
}
public void drink(){
System.out.println("动物:喝");
}
private void name(){
System.out.println("名字");
}
}
public class Cat extends Animal {
public void catchMouse(){
System.out.println("捕捉");
}
}
public class Dog extends Animal{
public void look(){
System.out.println("看护");
}
}
public class Tes {
public static void main(String[] args) {
Cat cat=new Cat();
cat.eat();
cat.drink();
//子类只能访问父类中非私有的成员
}
}
2.4 继承的特点之一:成员变量
2.4.1 父类成员变量私有化
●父类的成员,无论是公有( public )还是私有( private ),均为被子类所继承。
●子类虽然会继承父类私有的( private )的成员,但是子类不能对继承的私有成员直接进行访问,却可以通过继承的 setter 和 getter 方法进行范访问。

- 非私有成员变量继承的内存图

●示例:
public class Animal {
private String name;
int age;
public void eat(){
System.out.println("动物:吃");
}
public void drink(){
System.out.println("动物:喝");
}
private void name(){
System.out.println("名字");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Cat extends Animal {
public void catchMouse(){
System.out.println("捕捉");
}
}
public class Tes {
public static void main(String[] args) {
Cat cat=new Cat();
// cat.name = "Jerry"; 编译报错
cat.setName("aaa");
}
}
2.4.2 父类和子类成员变量重名
- 子类会继承父类所有的成员变量,那么如果子类出现和父类同名的成员变量会怎么样?
- 父类代码:
public class Animal {
/**
* 年龄
*/
int age = 88;
/**
* 姓名
*/
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
- 子类代码:
public class Cat extends Animal {
int age = 5;
public void showAge() {
System.out.println("Cat的年龄是:" + this.age + ",Animal的年龄是:" + super.age);
}
}
- 测试代码:
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.setName("Jerry");
cat.showAge();
}
}
- 总结:
-
- ① 当父类的成员变量私有化的时候,在子类中是无法直接访问的,所以是否重名没有任何影响;但是,如果想访问父类的私有成员变量,只能通过父类提供的 setter 和 getter 访问。
- ② 当父类的成员变量非私有化的时候,在子类中是可以直接访问的,所以如果有重名,就需要加上
super.父类成员变量名来进行区分。
注意:在实际开发中,虽然我们可以通过 super 关键字来实现区分父子类重名成员变量,但是不建议这么干。
2.4.3 成员变量的访问特点
继承中:成员变量的访问特点————就近原则
先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
name:从局部位置开始往上找
this.name:从本类成员位置开始往上找
supper.name:从父类成员位置开始往上找
- 示例
public class Fu {
String name="fu";
}
class zi extends Fu{
String name="zi";
public void ziShow(){
String name="ziShow";
System.out.println(name); //就近原则:谁离我近,我就用谁
System.out.println(this.name);//name="zi"
System.out.println(super.name);//name="fu"
}
}
2.5 继承的特点之二:成员方法
成员方法是否可以被继承?
只有父类中的虚方法才能被子类继承
虚方法:非private,非static,非final

- 继承内存图

2.5.1成员方法的访问特点
在没有supper的情况下:也遵循就近原则,this调用时逐级往上寻找
- 示例:
class Person{
public void eat(){
System.out.println("吃米饭,吃菜");
}
public void drink(){
System.out.println("喝豆浆");
}
}
class Student extends Person {
public void lunch(){
//先在本类查看eat和drink方法,就会调用子类的,如果没有,就会调用父类中继承下来的eat和drink方法
this.eat();
this.drink();
//直接调用父类中的方法
super.eat();
super.drink();
}
}
2.5.2方法重写( override )
- 定义:在子类中可以根据需要对从父类中继承而来的方法进行改造,也称为方法的
重置、覆盖。在程序执行的时候,子类的方法将覆盖父类的方法。 - 要求:
-
- ① 子类重写的方法
必须和父类被重写的方法具有相同的方法名称、参数列表。 - ② 子类重写的方法的返回值类型
不能大于父类被重写的方法的返回值类型(这对于返回值类型为引用数据类型来说的,如果返回值类型是基本数据类型和 void 类型必须相同,换言之,只有继承,才会有父类和子类)。 - ③ 子类重写的方法使用的访问权限
不能小于父类被重写的方法的访问权限(注意:子类不能重写父类中声明为privat权限的方法或final修饰的方法)。 - ④ 子类方法抛出的异常不能大于父类被重写方法的异常。
- ① 子类重写的方法
注意: 子类和父类中同名同参数的方法必须同时声明为非 static的(重写),或者同时声明为 static 的(不是重写,因为 static 方法是属于类的,子类无法覆盖父类的方法。
- 示例:
public class Phone {
public void sendMessage() {
System.out.println("发送短信");
}
public void call() {
System.out.println("打电话");
}
public void showNum() {
System.out.println("显示来电号码");
}
}
public class SmartPhone extends Phone {
/**
* 重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
*/
@Override
public void showNum() {
// super.父类成员方法,表示调用父类的成员方法。
super.showNum();
// 增加自己的显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
}

public class SmartPhoneTest {
public static void main(String[] args) {
SmartPhone smartPhone = new SmartPhone();
// 调用父类继承而来的方法
smartPhone.call();
// 调用子类重写的方法
smartPhone.showNum();
}
}
打电话
显示来电号码
显示来电姓名
显示头像
2.5.2 方法重载( overload )
- 在同一类中:
public class Overload {
public int max(int a, int b) {
return a > b ? a : b;
}
public double max(double a, double b) {
return a > b ? a : b;
}
public int max(int a, int b, int c) {
return max(this.max(a, b), c);
}
}
- 在父子类中:
public class Father {
public int max(int a, int b) {
return a > b ? a : b;
}
}
public class Son extends Father {
public double max(double a, double b) {
return a > b ? a : b;
}
}

参考 05 面向对象-上 第九章方法重载
2.6 继承的特点之三:构造方法
- 构造方法的定义:
-
- ① 构造方法的名称和类名是一致的。
- ② 构造器不声明返回值类型(和声明 void 不同)。
- ③ 构造器不能被 static 、final 、synchronized 、abstract 、native 修饰,不能有 return 语句返回值。
- 所以,子类是无法继承 父类的构造方法的。
- 构造方法的作用:初始化实例变量的。但是,子类又会从父类那边继承所有成员变量,所以子类在初始化的过程中,必须先执行父类的初始化动作(子类的构造方法中默认有一个
super(),表示调用父类的实例初始化方法,父类成员变量初始化后,才可以给子类使用),才能执行自己的初始化动作。
继承中构造方法访问特点是什么?
- 子类不能继承父类的构造方法,但是可以通过supper()调用
- 子类构造方法的第一行,有默认的supper()
- 默认先访问父类的无参构造方法,再执行自己
- 如果想要方法的父类有参构造,必须手动书写

2.7 继承的特点之四:单继承
- ① Java 只支持单继承,不支持多继承。
// 一个类只能有一个父类
class C extends A{}
- ② Java 支持多层继承。
class A{}
class B extends A{}
class C extends B{}
注意:顶层父类是 Object 类,所有类默认继承 Object 类作为父类。
- ③ 子类和父类是一种相对概念。比如:B 类相对于 A 类来说是子类,但是相对于 C 类来说是父类。
- ④ 一个父类可以同时有多个子类。

三 多态
3.1 概述
- 什么是多态?
同类型的对象,表现出不同形态;对象的多种形态
2.表现形式:
父类类型 变量名 = new 子类类名();
温馨提示:多态也可以应用在抽象类和接口上。
3.多态的前提条件:
-
- ① 有继承关系,或实现关系。
- ② 有方法重写。
- ③ 父类引用指向子类对象。:Fu f=new Zi();

- 示例(不同用户注册)
- 父类Person
/**
* @BelongsProject: StudyList
* @BelongsPackage: com
* @Author: GXY
* @CreateTime: 2022-12-23 15:58
* @Description: TODO
* @Version: 1.0
*/
package com;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("name:"+name+",age:"+age);
}
}
子类1:管理员
package com;
public class Admin extends Person{
@Override
public void show() {
System.out.println("管理员的信息为:"+getName()+","+getAge());
}
}
子类2:老师
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:"+getName()+","+getAge());
}
}
子类3:学生
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:"+getName()+","+getAge());
}
}
测试类:
public class Test {
public static void main(String[] args) {
Student student=new Student();
student.setName("张三");
student.setAge(18);
Teacher teacher=new Teacher();
teacher.setName("苍老师");
teacher.setAge(25);
Admin admin=new Admin();
admin.setName("admin");
admin.setAge(25);
register(student);
register(teacher);
register(admin);
}
//这个方法既能接收学生,又能接收老师,又能接收管理员
//只能写成这三个的父类
public static void register(Person person){
person.show();
}
}
打印结果:
学生的信息为:张三,18
老师的信息为:苍老师,25
管理员的信息为:admin,25
4.多态的好处:
使用父类类型作为参数,可以接收所有子类类型,体现多态的扩展性和便利性。
3.2多态的应用场景


3.3 多态中调用成员的特点
- 成员变量:编译看左边,运行也看左边
- 成员方法:编译看左边,运行看右边
3.3.1示例
-
- 成员变量:在子类的对象中,会把父类的成员变量也继承下的。
- 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
public class Test2 {
public static void main(String[] args) {
Animal animal=new Dog();
System.out.println(animal.name);//动物 1
animal.show(); //Dog--------show
}
}
class Animal{
String name="动物";
public void show(){
System.out.println("Admin--------show");
}
}
class Dog extends Animal{
String name="狗";
@Override
public void show(){
System.out.println("Dog--------show");
}
}
class Cat extends Animal{
String name="猫";
@Override
public void show(){
System.out.println("Cat--------show");
}
}
3.3.2多态调用成员的内存图解

3.4多态的优势和弊端
3.4.1 多态的优势
- 在多态形势下,右边对象可以实现解耦合,便于扩展和维护。
Person p= new Student();
p.work();//业务逻辑发生改变时,后续代码无需修改
- 定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性和便利性。
StringBuilder sb=new StringBuilder();

3.4.2多态的弊端
- 不能调用子类的特有功能
public class Test2 {
public static void main(String[] args) {
Animal animal=new Dog();
System.out.println(animal.name);//动物 1
animal.show(); //Dog--------show
//那么在编译的时候会先检查左边的父类中有没有这个方法,若果没有直接报错
animal.lookHome();
解决方案:
}
}
class Animal{
String name="动物";
public void show(){
System.out.println("Admin--------show");!
}
}
class Dog extends Animal{
String name="狗";
@Override
public void show(){
System.out.println("Dog--------show");
}
public void lookHome(){
System.out.println("Dog--------看家");
}
}
class Cat extends Animal{
String name="猫";
@Override
public void show(){
System.out.println("Cat--------show");
}
public void catchMouse(){
System.out.println("Cat--------捉老鼠");
}
}
- 解决方案:
//变回子类类型就可以了
//细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错
//Cat c = (Cat) a;
//c.catchMouse();
- 转换的时候不能瞎转,如果转成其他类的类型,就会报错
//先判断:转换类型与真实类型是否一致 instanceof
if(a instanceof Dog){
Dog d= (Dog) a;
d.1ookHome();
}else if(a instanceof Cat){
CatC=(Cat) a;
c.catchMouse();
}else{
System.out.printin(“没有这个类型,无法转换");
}
- jdk14新特性:先判断就转换
if(a instanceof Dog d){
d.1ookHome();
}

















