首页 > 其他分享 >快速掌握封装,继承及多态

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

时间:2024-11-11 18:19:00浏览次数:3  
标签:封装 构造方法 继承 子类 class 多态 Dog 父类 public

目录


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

1. 封装

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

1.1 封装的语法

//封装前
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类,会报错

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在同一个包

【代码演示】

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. 继承后,子类可以复用父类的成员(除构造方法),子类在实现时只需要关注新增的成员即可

【语法】

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

【代码演示】

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

}
public class Cat extends Animal {

}

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

2.2 在子类中访问父类

【前提代码】

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

修改前提代码的Dog类

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

【代码演示】

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() 构造方法

【代码演示】

public class Dog extends Animal{
    int b = 12;
    char a = 97;

    //super要在 方法内部 使用,且是 非静态方法

    //super.方法
    @Override
    public 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()

【代码演示】

public class Animal {
    public String name;
    public int age;
    int a;

    public Animal(int age) {
        System.out.println("Animal构造方法");
    }
}
public class Dog extends Animal{
    int b = 12;
    char a = 97;
    //super()  父类的构造方法
    public Dog(int age) {
        super(age);
        System.out.println("Dog构造方法");
    }
}
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 继承后代码的执行顺序

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

【代码演示】

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修饰的成员和类具有常量属性,无法再进行任何操作(不能继承,重写,修改)

【代码演示】

public final class Son extends Father {

}
class Sonner extends Son{ //报错
	
}

3. 组合

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

【代码演示】

class Teacher{
    
}
class Student{
    
}
public class School{
    Teacher teacher;
    Student student;
}

4. 多态

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

4.1 产生多态的条件

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

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

//父类
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;
    
    @Override
    public 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. 直接赋值
Animal animal = new Dog();
  1. 作方法参数
 public static void sum(Animal animal){
     animal.get();
 }
 Animal a = new Dog(); 
 sum(a);//多态
  1. 作方法返回值
public static Animal ret(Dog dog){
      return dog;
}
ret(new Dog()).get();//多态
2. 向下转型

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

【代码演示】

    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的圈复杂度
class Shape{
    public void draw(){

    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("画◇");
    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("画○");
    }
}
class Flower extends Shape{
    @Override
    public 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. 构造方法没有多态性,构造方法不要调用重写的方法
class B{
    public B(){
        fun();
    }
    public void fun(){
        System.out.println("B.fun()");
    }
}
class D extends B{
    private int num = 1;
    @Override
    public 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

标签:封装,构造方法,继承,子类,class,多态,Dog,父类,public
From: https://blog.csdn.net/2301_79417489/article/details/141358736

相关文章

  • 使用wxpython开发跨平台桌面应用,基类列表窗体的抽象封装处理
    在开发一套系统框架的时候,除了关注实现系统的功能实现外,我们对于系统的各个方面都是应该精益求精,以最少的编码做最好的事情,在开发的各个层次上,包括前端后端,界面处理、后端处理、常用辅助类、控件封装等等方面,我们都可以通过抽象、重用等方式,实现代码的优化、简化,以期达到快速开发......
  • 网址封装(带苹果免签/安卓apk/苹果ipa)仿第八区H5APP封装打包分发系统源码
    此封装系统可以无限封装,也可以给用户开后台让别人无限封装多个“苹果免签”文件市面上一个苹果免签封装价格70到150rmb之间,自己拥有一个封装系统还是特别划算分发网站功能如下:1、苹果免签封装带绿标签名功能(可设置自己的域名显示)免签封装适用于任意网址/网页,不跳浏览器、顶部......
  • 第六章-继承和多态
    一、单项选择题1.有如下的类及对象的定义: classparentclass[] classsubclasslextendsparentclass|} parentclassa=newparentclass();subclass1b=newsubclass1();当执行语句a=b;时,结果是                              ......
  • 使用react+copy-to-clipboard封装双击复制组件
    前言:最近在公司研发后台系统,用户反馈在双击某些信息时希望可以进行复制的操作,多处使用进而封装为组件首先:安装copy-to-clipboardnpmi--savecopy-to-clipboard其次:封装组件importReact,{memo,useCallback}from'react';import{notification}from"antd";......
  • windows C#-多态性
    多态性常被视为自封装和继承之后,面向对象的编程的第三个支柱。Polymorphism(多态性)是一个希腊词,指“多种形态”,多态性具有两个截然不同的方面:在运行时,在方法参数和集合或数组等位置,派生类的对象可以作为基类的对象处理。在出现此多形性时,该对象的声明类型不再与运行时类型......
  • windows C#-继承
    继承(以及封装和多态性)是面向对象的编程的三个主要特征之一。通过继承,可以创建新类,以便重用、扩展和修改在其他类中定义的行为。其成员被继承的类称为“基类”,继承这些成员的类称为“派生类”。派生类只能有一个直接基类。但是,继承是可传递的。如果ClassC派生自ClassB......
  • C++17 多态内存管理 pmr
    C++17多态内存管理pmr概念C++17开始,增加特性PolymorphicMemoryResources多态内存资源,缩写PMR。提供新的内存分配策略,更灵活地控制内存的分配与回收——适用于嵌入式和高并发服务器场景。对内存资源的抽象抽象基类std::pmr::memory_resource定义了用于内存的分......
  • 封装红黑树实现mymap和myset--C++
    源码及框架分析SGI-STL30版本源代码,map和set的源代码在map/set/stl_map.h/stl_set.h/stl_tree.h等几个头文件中。map和set的实现结构框架核心部分截取出来如下://set#ifndef__SGI_STL_INTERNAL_TREE_H#include<stl_tree.h>#endif#include<stl_set.h>#include<st......
  • Vue2中使用Element-ui封装表单(el-form)组件动态渲染数据
    1.创建一个searchForm组件,将需要封装的searchForm组件全局注册,为了方便下次直接使用在main.js文件中全局注册importSearchFormfrom'./components/SearchForm'Vue.component('SearchForm',SearchForm)2.在searchForm组件中创建基本结构<template><divclass="ces-......
  • Three.js 实现原生化组态化 低代码 封装适用与 企业项目使用的编辑器
    开发历程开源地址https://github.com/z2586300277/three-editor预览查看https://z2586300277.github.io/three-editor/dist/#/editorThree.js案例学习https://threehub.cn/保留three.js原生态,只是将three.js内部案例功能做了一个集成,并不对底层库改变,只撰写你......