快速掌握封装,继承及多态

ops/2024/11/13 15:55:23/

目录

  • 1. 封装
      • 1.1 封装的语法
      • 1.2 访问修饰限定符
  • *包
      • 1. 包的使用
      • 2. 自定义包
      • 3. 包访问权限(只在包内访问)
      • 4. 常用的包
  • 2. 继承
      • 2.1 继承的语法(子类和父类)
      • 2.2 在子类中访问父类
          • 1.子类 与 访问父类 成员名字不同
          • 2. 子类 与 访问父类 成员同名 --- super
          • * 如何访问同名时的父类?
      • 2.3 子类的构造方法
      • 2.4 super和this
      • 2.5 protected 关键字
      • 2.6 继承后代码的执行顺序
      • 2.7 继承的种类 --- final关键字
  • 3. 组合
  • 4. 多态
      • 4.1 产生多态的条件
  • 5. 多态的底层原理
      • 5.1 重写和动态绑定
          • * 动态绑定和静态绑定
      • 5.2 向上转型和向下转型
          • 1. 向上转型
          • 2. 向下转型
  • 6. 多态的优缺点
        • 6.1 多态优点
        • 6.2 多态缺点

OOP语言的三大主要特点:封装,继承,多态。

1. 封装

封装简单来说就是套壳屏蔽细节,让类的使用者只关注类的功能,而不关注类内的细节。比如笔记本电脑,只需要知道怎么用,而不需要知道电脑内部零件

1.1 封装的语法

java">//封装前
class Dog {public String name;public int age;public static String classRoom = "001";
}
//封装后
class Dog {private String name;private int age;private static String classRoom = "001";
}

封装前
在这里插入图片描述
封装后
在这里插入图片描述

1.2 访问修饰限定符

Java中主要通过类和访问修饰限定符来实现封装,有4种访问修饰限定符:

  1. public,公有的(哪里都能用)
  2. private,私有的(只能在类内使用)
  3. protected,受保护的(主要用于继承,后面讲)
  4. default,什么也不写,包访问权限(只在内使用)

*包

为了更好的管理类,把相关的类放在一个文件夹下,这个文件夹就是包。在同一个项目中允许存在相同名称的类,只要处在不同的包中即可

1. 包的使用

Java中使用类需要导入类所在的包。

如使用Date类,import java.util.Date含义是:导入java下的uitl包下的Date类

在这里插入图片描述
在这里插入图片描述
【注】java.util.*可以导入util下的所有类,但尽量不要用。建议显式指定要导入的类名,不然容易出现冲突,如下面,两个包下都有Data类,会报错

java">import java.util.*;
import java.sql.*;
public class Test {public static void main(String[] args) {java.util.Date date = new java.util.Date();System.out.println(date.getTime());}
}

2. 自定义包

【如何自定义包】

在这里插入图片描述

【注】

  1. 使用包时要在最上方加“package 包名”指定该代码所在的包
  2. 包名一般以公司域名的颠倒形式命名,比如百度,com.baidu.demo
  3. 代码路径要和包名相同
  4. 若一个类没有package语句,则放在默认包中
    在这里插入图片描述
    在这里插入图片描述

3. 包访问权限(只在包内访问)

在这里插入图片描述
由上图可见,Demo类是包访问权限(没有访问修饰符修饰),Main和Demo不在同一个包,Demo1和Demo在同一个包

【代码演示】

java">public class Demo1 {public static void main(String[] args) {Demo demo = new Demo(); //编译成功}
}
public class Main {public static void main(String[] args) {Demo demo = new Demo(); //语法报错}
}

可见,在Main类中new一个Demo对象,由于是包访问权限,编译报错

4. 常用的包

  1. java.lang:常用的基础类(String,Object),jdk1.1后自动导入
  2. java.lang.reflect:反射编程
  3. java.sql:数据库开发
  4. java.net:网络编程开发
  5. java.io:IO编程开发
  6. java.util:提供工具程序,如集合类

2. 继承

继承是为了实现代码复用。比如写1篇文章,有些内容出自某个文献,为了少写字,只需要在文中说明自己去看文献即可,不用重复写。这里的文献就是父类,每篇作文会继承文献

2.1 继承的语法(子类和父类)

  1. 继承中,Animal类叫父类/基类/超类,Dog类和Cat类叫子类/派生类。子类会将父类中除构造方法外的所有成员都继承到子类中,被private修饰成员也会被继承
  2. 继承后,子类可以复用父类的成员(除构造方法),子类在实现时只需要关注新增的成员即可

【语法】

java">修饰符 class 子类 extends 父类 {// ... 
}

【代码演示】

java">//父类
public class Animal {public String name;public int age;
}
//子类
public class Dog extends Animal {}
public class Cat extends Animal {}

验证继承Dog类中没有任何成员,但却可以点出age和name,说明确实继承了
在这里插入图片描述

2.2 在子类中访问父类

【前提代码】

java">//父类
public class Animal {public String name;public int age;int a;public void barks() {System.out.println(this.name+ "汪汪");}public void method() {System.out.println("Animal");}
}
java">//子类
public class Dog extends Animal{int b = 12;public void method1() {System.out.println("Dog");}
}
1.子类 与 访问父类 成员名字不同
java">public class Test {public static void main(String[] args) {Dog dog = new Dog();//子类对象调用与父类  不同名的方法dog.method1(); //Dogdog.method(); //Animal//子类对象调用与父类  不同名的成员变量System.out.println(dog.a); //0System.out.println(dog.b); //12}
}
2. 子类 与 访问父类 成员同名 — super

修改前提代码的Dog类

java">public class Dog extends Animal{int b = 12;char a = 97;@Overridepublic void method() {System.out.println("Dog");}
}

【代码演示】

java">public class Test {public static void main(String[] args) {Dog dog = new Dog();//子类对象调用与父类  同名的方法dog.method(); // Dog//子类对象调用与父类  同名的变量System.out.println(dog.a); // a}
}

无论子类与父类有没有同名(不考虑数据类型)的成员,子类访问父类中的成员时,会优先访问子类自己的成员,子类没有则访问父类的

* 如何访问同名时的父类?

用super关键字

super的三种用法:super要在方法内部使用,且是非静态方法

  1. super.成员方法
  2. super.成员变量
  3. super() 构造方法

【代码演示】

java">public class Dog extends Animal{int b = 12;char a = 97;//super要在 方法内部 使用,且是 非静态方法//super.方法@Overridepublic void method() {//System.out.println("Dog");super.method();}//super.变量public void father() {super.a = 10;System.out.println(super.a);}//super()  父类的构造方法public Dog(int age) {//调用父类构造方法,对父类进行构造super(age);System.out.println("Dog构造方法");}
}

super()访问父类的构造方法有什么用?用于子类的构造(子类的构造方法)

2.3 子类的构造方法

如上面super()所说,子类在构造时需要先构造父类,具体方式为:在子类的构造方法中调用父类的构造方法来完成对父类的构造

【那为什么之前没有手动对父类构造,却没有报错?】
编译器在用户没有写时,编译器会自动在子类的构造方法中添加父类的不带参数的构造方法super()

【代码演示】

java">public class Animal {public String name;public int age;int a;public Animal(int age) {System.out.println("Animal构造方法");}
}
java">public class Dog extends Animal{int b = 12;char a = 97;//super()  父类的构造方法public Dog(int age) {super(age);System.out.println("Dog构造方法");}
}
java">public class Test {public static void main(String[] args) {Dog dog = new Dog(8);  // Animal构造方法 Dog构造方法   }
}

【注】

  1. super()必须放在第一行
  2. 若父类构造方法不带参数,子类构造方法的第一行会有一个隐藏的super()
  3. 若父类构造方法带参数,子类构造方法的第一行需要手动在第一行调用super(…)
  4. super()只能在构造方法中被调用一次(子类的构造方法只被调用一次,那么super()也只有一次)
  5. super不能和this同时出现(this和super都要求放在第一行)

2.4 super和this

【相同点】

  1. 因为都依赖对象,需要在非静态方法中使用,只能访问非静态的成员
  2. 在构造方法中用super(…)和this(…)时,都必须放在第一行

在这里插入图片描述

【不同点】

  1. this是当前对象的引用,super是子类对象中从父类继承下来的部分的引用
  2. this访问当前类的成员,super访问当前类中继承下来的父类的成员
  3. this(…)调用本类的构造方法,super(…)调用父类的构造方法
  4. 在构造方法无参的条件下,用户不写super(…)编译器会默认隐含super(),this(…)不写则不会

2.5 protected 关键字

protected和private关键字不能定义类,语法上不支持
protected访问权限最大就是不同包下的子类

【代码演示】
在这里插入图片描述

2.6 继承后代码的执行顺序

类和对象中讲了类之间没有继承时代码块的执行顺序,现在看一下继承后
继承前:先执行静态代码,再执行实例,再构造方法
继承后:先执行父类的静态代码,后执行子类的静态代码,再执行父类的实例,构造方法,再执行子类的实例,构造方法

【代码演示】

java">class Parent{public String name = "父亲";static {System.out.println("父类的静态");}{System.out.println("父类的实例");}public Parent(){System.out.println("父类的构造方法");}
}
class Kid extends Parent{public int age;static {System.out.println("子类的静态");}{System.out.println("子类的实例");}public Kid(){super();System.out.println("子类的构造方法");}
}
public class TestStatic {public static void main(String[] args) {Kid kid = new Kid();}
}//运行结果父类的静态
子类的静态
父类的实例
父类的构造方法
子类的实例
子类的构造方法

2.7 继承的种类 — final关键字

  1. 单继承
  2. 不同类继承同一个类
  3. 多层继承,B类继承A类,C类继承B类
  4. 不支持多继承,C类不能既继承A类,又继承B类(接口解决了这个问题)

继承关系一般不要超过三层,为了从语法上限制,可使用final关键字,被final修饰的成员和类具有常量属性,无法再进行任何操作(不能继承,重写,修改)

【代码演示】

java">public final class Son extends Father {}
class Sonner extends Son{ //报错}

3. 组合

组合,它不是OOP语言的特点,指将一个类的实例作为另一个类的属性,数据结构中用组合比较多

【代码演示】

java">class Teacher{}
class Student{}
public class School{Teacher teacher;Student student;
}

4. 多态

多态,指在完成某个行为时,不同的对象会产生不同形态

4.1 产生多态的条件

  1. 两个类是继承关系
  2. 子类要重写父类的方法
  3. 通过父类的引用调用父类的重写方法
    代码在运行时,传递不同的子类对象会调用子类的重写方法,因而产生不同的效果

【代码演示】子类重写父类的方法后再用父类对象调用,依旧调用的子类重写的方法

java">//父类
public class Animal {public String name;public int age;public void get(){System.out.println(this.name + " "+ this.age);}
}
//派生类
public class Dog extends Animal{int b = 12;@Overridepublic void get() {System.out.println("多态");}
}//测试类
public class Test {public static void sum(Animal animal){animal.get();}public static Animal ret(Dog dog){return dog;}public static void main(String[] args) {//方法一Dog dog = new Dog();dog.get();//多态//方法二  向上转型Animal animal = new Dog();animal.get();//多态//方法三  将对象作为参数Animal a = new Dog();Animal b = new Dog();sum(a);//多态sum(b);//多态//方法四ret(new Dog()).get();//多态}
}

为什么会这样?看下面

5. 多态的底层原理

5.1 重写和动态绑定

重写,指方法名,返回值类型,参数类型均相同,只修改方法内核。
若两个类是父子关系,返回值类型可以不相同。
访问修饰限定符必须是子类的权限≥父类的权限

【注】

  1. 被private修饰的方法无法被重写(只能在其所在的类内访问)
  2. 被static修饰的方法无法被重写(类方法是独属于某个类的方法)
  3. 被final修饰的方法无法被重写(具有常量属性的方法无法被再次修改)
  4. 构造方法无法重写

【重写和重载的区别】

  1. 重写:方法名相同,参数列表相同,返回值相同
  2. 重载:方法名相同,参数列表不同,返回值不做要求
* 动态绑定和静态绑定
  1. 动态绑定:编译时,根据用户传的实参类型就确定了具体调用哪个方法,如方法·重载
  2. 静态绑定:编译时,不能确定方法的行为,需等程序运行时才能确定具体调用哪个方法

注:

  1. 不要在构造方法中调用实例方法,容易出bug
  2. 动态绑定的发生不一定发生在向上转型,只要子类重写了父类的方法,无论有没有发生向上转型,都会发生动态绑定,调用子类重写的方法

5.2 向上转型和向下转型

1. 向上转型

父类的引用,引用子类的对象。换句话说,就是创建一个子类的对象,将其当成父类对象来用。父类类型可以引用子类对象,因为是从小范围向大范围转换。

优点:让代码实现更简单灵活
缺点:不能调到子类特有的方法

【代码演示】

Animal animal = new Dog();

【使用场景】

  1. 直接赋值
java">Animal animal = new Dog();
  1. 作方法参数
java"> public static void sum(Animal animal){animal.get();}Animal a = new Dog(); sum(a);//多态
  1. 作方法返回值
java">public static Animal ret(Dog dog){return dog;
}
ret(new Dog()).get();//多态
2. 向下转型

子类的引用,引用父类的对象,需要强转,因为是从大范围向小范围转换。
当子类向上转型当成父类使用后,再无法调用子类的方法,但有时有需要调用子类特有的方法,此时需要将父类引用再还原成子类对象,即向下转型

【代码演示】

java">    public static void main(String[] args) {Animal animal = new Dog();animal.barks();Dog dog = (Dog) animal;dog.barks();//错误写法Cat cat = (Cat) animal;cat.barks(); //抛异常//正确写法if(animal instanceof Cat){Cat cats = (Cat) animal;cats.barks();}else{System.out.println("该对象不是当前类的实例");}}

向下转型用的比较少,并不安全,一旦转换失败就抛异常。为了提高安全性,需要用instanceof

6. 多态的优缺点

6.1 多态优点
  1. 降低if else的圈复杂度
java">class Shape{public void draw(){}
}
class Rect extends Shape{@Overridepublic void draw() {System.out.println("画◇");}
}
class Cycle extends Shape{@Overridepublic void draw() {System.out.println("画○");}
}
class Flower extends Shape{@Overridepublic void draw() {System.out.println("画❀");}
}public class MyShape {//没用多态public static void main(String[] args) {Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle","rect","cycle","rect","flower"};//圈复杂度for (String shape:shapes) {if (shape.equals("cycle")) {cycle.draw();} else if (shape.equals("rect")) {rect.draw();}else if(shape.equals("flower")){flower.draw();}}}//有多态public static void main1(String[] args) {Shape[] shapes = {new Rect(),new Cycle(),new Flower()};for (int i = 0; i < shapes.length; i++) {shapes[i].draw();}}
}
  1. 代码可扩展性强
    想画别的图形只需要重新添加一个类即可,不用动原有代码
6.2 多态缺点
  1. 属性没有多态性,当向上转型后,只能用父类的属性,无法用子类的属性
  2. 构造方法没有多态性,构造方法不要调用重写的方法
java">class B{public B(){fun();}public void fun(){System.out.println("B.fun()");}
}
class D extends B{private int num = 1;@Overridepublic void fun() {System.out.println("D.fun()"+num);}
}public class Struct {public static void main(String[] args) {D d = new D();}
}//运行结果
//D.fun()0

【代码解析】

  1. D调用构造方法时,也调用了B的构造方法
  2. B的构造方法中调用了fun(),fun()又因为重写发生了动态绑定,只能调用到子类的fun(),但fun()里的num还未初始化,此时就会出现bug

http://www.ppmy.cn/ops/132978.html

相关文章

解决因为TortoiseSVN未安装cmmand line client tools组件,导致idea无法使用svn更新、提交代码

一.错误信息 1.更新代码时&#xff1a;SVN: 更新错误 找不到要更新的版本管理目录。 2.提交代码&#xff1a;检测不到任何更新&#xff08;实际上有代码修改&#xff09;。 3.Cannot run program "svn"。 二.原因分析 在电脑上新安装的的客户端TortoiseSVN、ide…

Springboot苍穹外卖实战:四、分页查询(PageHelper实现、日期格式转换)

分页查询 使用PageHelper插件实现。其实可以升级为MybatisPlus的。 接口设计 设计DTO类 com.sky.dto.EmployeePageQueryDTO 已设计好&#xff0c;为EmployeePageQueryDTO&#xff1b; 封装PageResult com.sky.result.PageResult com.sky.result.Result 已封装好&#xff0…

车-路-站-网”信息耦合的汽车有序充电

电动汽车作为一种环保、的交通工具&#xff0c;正逐渐成为未来交通的发展趋势。然而&#xff0c;大规模电动汽车的无序充电可能导致电网负荷波动、电压下降等问题&#xff0c;影响电网的安全稳定运行。为了解决这些问题&#xff0c;需要制定有效的电动汽车有序充电策略&#xf…

解题--多数元素

给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#xff1a;3 此算法用…

11 Oracle Golden Gate 高可用解决方案:Golden Gate 助力企业保障业务连续性

文章目录 Oracle Golden Gate 高可用解决方案&#xff1a;Golden Gate 助力企业保障业务连续性一、Oracle Golden Gate基本概念二、设计异地灾备策略2.1 需求分析2.2 网络规划2.3 部署架构 三、实施异地灾备策略3.1 环境准备3.2 配置Golden Gate3.3 验证与测试 四、数据保护策略…

Redis - 渐进式遍历

Redis 使⽤scan命令进⾏渐进式遍历键&#xff0c;进⽽解决直接使⽤keys获取键时可能出现的阻塞问 题。每次scan命令的时间复杂度是O(1)&#xff0c;但是要完整地完成所有键的遍历&#xff0c;需要执⾏多次scan。整 个过程如图2-29所⽰。 图2-29scan命令渐进式遍历 ⾸次scan从0…

鸿蒙系统的发展及开发者机遇

鸿蒙系统&#xff08;HarmonyOS&#xff09;凭借其分布式架构和跨设备协同能力&#xff0c;展现出强大的发展潜力&#xff0c;在智能手机、智能穿戴、车载、家居等行业领域应用日益广泛&#xff0c;已逐渐形成与安卓、iOS 三足鼎立的市场格局。 开发者面临的挑战 1. 技术适应与…

《无线重构世界》射频模组演进

射频前端四大金刚 射频前端由PA、LNA、滤波器、开关“四大金刚” 不同的模块有自己的工艺和性能特点 分层设计 射频前端虽然只由PA、LNA、开关、混频器4个模块构成&#xff0c;但不同模块之间相互连接且相互影响。如果将射频系统当成一个整体来理解&#xff0c;其中的细节和…