Java实现数据结构——双链表

目录

一、前言

二、实现

2.1 类的创建

三、对链表操作实现

3.1 打印链表

3.2 插入数据

3.2.1 申请新节点

3.2.2 头插

​编辑

3.2.3 尾插

3.2.4 链表长度

3.2.5 任意位置插入

3.3 删除数据

3.3.1 头删

3.3.2 尾删

3.3.3 删除指定位置数据

3.3.4 删除指定数据

3.3.5 删除所有指定数据 

3.3.6 删除链表 

四、LinkedList

4.1 什么是 LinkedList

4.2 LinkedList 的使用

​编辑

4.2.1 LinkedList的构造方法

4.2.2 常用方法

​编辑

​编辑

五、ArrayList和LinkedList的区别


一、前言

更详细的理论请移步笔者的另一文章

http://t.csdnimg.cn/4Mtne

二、实现

2.1 类的创建

双向链表就是在单链表的基础上加上了一个 prev,存放上一个节点的地址

public class MyLinkedList {
    //    自己实现双向链表
    static class ListNode {
        int val;
        ListNode prev;//前
        ListNode next;//后

        public ListNode(int val) {
            this.val = val;
        }
    }

    ListNode head = null;//头
    ListNode last = null;//尾
}

需要什么方法在后续再补充

三、对链表操作实现

3.1 打印链表

可正序打印

也可逆序打印

 public void printHead() {
        //正序打印
        ListNode cur = this.head;
        if (cur == null) {
            System.out.println("当前链表为空!");
            return;
        }
        while (cur != null) {
            System.out.println(cur.val);
            cur = cur.next;
        }
    }

public void printLast(){
        //逆序打印
        ListNode cur = this.last;
        if(cur == null){
            System.out.println("当前链表为空!");
            return;
        }
        while (cur != null) {
            System.out.println(cur.val);
            cur = cur.prev;
        }
    }

3.2 插入数据

3.2.1 申请新节点

   public ListNode buyNode(int data) {
//    申请新节点
        ListNode newnode = new ListNode(data);
        return newnode;
    }

3.2.2 头插

链表为空就让链表的 headlast 都等于这个新节点

若链表不为空

原头节点的 prev 保存新插入节点的地址

新插入节点的 next 保存原头节点的地址

新插入节点成为新的头节点

 public void addFirst(int data) {
        //头插
        if (this.head == null) {
            this.head = buyNode(data);
            this.last = this.head;
            return;
        }
        ListNode newnode = buyNode(data);
        newnode.next = this.head;//新插入节点的 next 保存原头节点的地址
        this.head.prev = newnode;//原头节点的 prev 保存新插入节点的地址
        this.head = newnode;//新插入节点成为新的头节点
    }

测试

 public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        linkedList.addFirst(4);

        linkedList.printHead();
        System.out.println("==============");
        linkedList.printLast();
    }


3.2.3 尾插

链表为空就让链表的 headlast 都等于这个新节点

若链表不为空

last.next 保存新插入节点的地址

新插入节点的 prev 保存 last 的地址

新插入节点成为 last 

public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addLast(5);
        linkedList.addLast(6);
        linkedList.addLast(7);
        linkedList.addLast(8);

        linkedList.printHead();
        System.out.println("==============");
        linkedList.printLast();
    }

测试

public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addLast(5);
        linkedList.addLast(6);
        linkedList.addLast(7);
        linkedList.addLast(8);

        linkedList.printHead();
        System.out.println("==============");
        linkedList.printLast();
    }


3.2.4 链表长度

 public int size() {
        //返回链表节点个数
        ListNode cur = this.head;
        int count = 0;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

3.2.5 任意位置插入

插入时需要检查坐标的合法性

合法区间是[0,size()]

指定位置合法后

新节点的 prev 存储原位置节点 prev 的地址

新节点的 next 存储原位置节点的地址

原位置的 prev 存储为新节点的地址

原位置前一节点的 next 存储为新节点的地址

为方便观察修改一下打印方法

  public void printHead() {
        //正序打印
        ListNode cur = this.head;
        if (cur == null) {
            System.out.println("当前链表为空!");
            return;
        }
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
    }

    public void printLast() {
        //逆序打印
        ListNode cur = this.last;
        if (cur == null) {
            System.out.println("当前链表为空!");
            return;
        }
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.prev;
        }
    }

测试

 public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addLast(5);
        linkedList.addLast(6);
        linkedList.addLast(7);
        linkedList.addLast(8);
        linkedList.printHead();
        System.out.println();
        System.out.println("==========");
        linkedList.addAny(1, 99);
        linkedList.addAny(2, 199);
        linkedList.addAny(3, 299);
        linkedList.addAny(0, 122);
        linkedList.addAny(linkedList.size(), 999);
        linkedList.printHead();

    }

 


3.3 删除数据

3.3.1 头删

由于此处是基础数据类型

不需要对节点中存储的数据进行置空

如果存储的是引用数据类型就需要置空

将原头节点置空

头节点的下一个节点成为新的头节点

新的头节点的 prev 需要置空

  public void removeFirst() {
        //头删
        if (this.head == null) {
            System.out.println("当前链表为空!");
            return;
        }
        ListNode tmp = this.head.next;
//        this.head.val = null;//由于此处是基础数据类型 不需要对节点中存储的数据进行置空 如果存储的是引用数据类型就需要置空
        this.head = null;
        this.head = tmp;
        if(this.head == null){
            this.last = null;
        }else {
            this.head.prev = null;//新的头节点的 prev 需要置空

        }
    }

测试

  public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        linkedList.addFirst(4);

        linkedList.printHead();

        linkedList.removeFirst();
        System.out.println();
        linkedList.printHead();

        linkedList.removeFirst();
        System.out.println();
        linkedList.printHead();

        linkedList.removeFirst();
        System.out.println();
        linkedList.printHead();

        linkedList.removeFirst();
        System.out.println();
        linkedList.printHead();
    }


3.3.2 尾删

由于此处是基础数据类型

不需要对节点中存储的数据进行置空

如果存储的是引用数据类型就需要置空

尾节点的前一个节点成为新的尾节点

新的尾节点的 next 需要置空

   public void removeLast() {
        //尾删
        if (this.last == null) {
            System.out.println("当前链表为空!");
            return;
        }
//        this.last.val = null;由于此处是基础数据类型 不需要对节点中存储的数据进行置空 如果存储的是引用数据类型就需要置空
        ListNode tmp = this.last.prev;
        this.last = null;
        this.last = tmp;
        if(this.last == null){
            this.head = null;
        }
        else {
            this.last.next = null;//新的尾节点的 next 需要置空

        }
    }

测试

    public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        linkedList.addFirst(4);

        linkedList.printHead();

        linkedList.removeLast();
        System.out.println();
        linkedList.printHead();

        linkedList.removeLast();
        System.out.println();
        linkedList.printHead();

        linkedList.removeLast();
        System.out.println();
        linkedList.printHead();

        linkedList.removeLast();
        System.out.println();
        linkedList.printHead();
    }


3.3.3 删除指定位置数据

删除时需要检查坐标的合法性

合法区间是[0,size())

将删除位置前节点的 next 保存为删除节点位置后节点的地址

将删除位置后节点的 prev 保存为删除节点位置前节点的地址

  public void removeAny(int index) {
        if (this.head == null) {
            System.out.println("当前链表为空!");
            return;
        }
        if (index < 0 && index >= this.size()) {
            throw new IndexillegalityException("下标不合法!");
        }
        if (index == 0) {
            removeFirst();
            return;
        } else if (index == size() - 1) {
            removeLast();
            return;
        }
        ListNode cur = this.head;
        while (index != 0) {
            //找要删除的节点
            cur = cur.next;
            index--;
        }
        cur.prev.next = cur.next;
        cur.next.prev = cur.prev;
        cur = null;

    }

测试

 public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        linkedList.addFirst(4);
        linkedList.printHead();
        System.out.println();

        linkedList.removeAny(1);
        linkedList.printHead();
        System.out.println();

        linkedList.removeAny(1);
        linkedList.printHead();
        System.out.println();

        linkedList.removeAny(1);
        linkedList.printHead();
        System.out.println();

        linkedList.removeAny(1);
        linkedList.printHead();
        System.out.println();
    }

抛了一个空指针异常

说明在链表只剩下一个节点的时候需要特殊处理

    public void removeAny(int index) {
        if (this.head == null) {
            System.out.println("当前链表为空!");
            return;
        }
        if (index < 0 && index >= this.size()) {
            throw new IndexillegalityException("下标不合法!");
        }
        if (index == 0) {
            removeFirst();
            return;
        } else if (index == size() - 1) {
            removeLast();
            return;
        }
        ListNode cur = this.head;
        while (index != 0) {
            //找要删除的节点
            cur = cur.next;
            index--;
        }
        if(cur == null ){
            //判断是否只有一个节点
            //cur在移动后如果等于空,就说明一定只剩下一个节点
            this.last = null;
            this.head = null;
        }else {
            cur.prev.next = cur.next;
            cur.next.prev = cur.prev;
            cur = null;
        }
    }


3.3.4 删除指定数据

将删除位置前节点的 next 保存为删除节点位置后节点的地址

将删除位置后节点的 prev 保存为删除节点位置前节点的地址

对头和尾做单独处理

 public void removeData(int data){
        //删除指定数据
        if (this.head == null) {
            System.out.println("当前链表为空!");
            return;
        }
        ListNode cur = this.head;
        while (cur != null){
            if(cur.val == data){
                if(cur == head){
                    removeFirst();
                    return;
                }else if(cur == last){
                    removeLast();
                    return;
                }else {
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
            }
            cur = cur.next;
        }
        System.out.println("当前链表中没有您要删除的数据");
    }

测试

 public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        linkedList.addFirst(4);
        linkedList.printHead();
        System.out.println();

        linkedList.removeData(4);
        linkedList.printHead();
        System.out.println();

        linkedList.removeData(3);
        linkedList.printHead();
        System.out.println();
        
        linkedList.removeData(2);
        linkedList.printHead();
        System.out.println();

        linkedList.removeData(1);
        linkedList.printHead();
    }


3.3.5 删除所有指定数据 

将链表中所有节点值等于 data 的节点全部删除

将3.3.4 的方法中进行删除后继续遍历链表

遇到节点值为 data 的继续删除即可

  public void removeDataAll(int data){
        //删除所有节点值为data的节点
        if (this.head == null) {
            System.out.println("当前链表为空!");
            return;
        }
        boolean flg = false;

        ListNode cur = this.head;
        while (cur != null){
            if(cur.val == data){
                flg = true;
                if(cur == head){
                    removeFirst();
                }else if(cur == last){
                    removeLast();
                }else {
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
            }
            cur = cur.next;
        }
        if(!flg){
            System.out.println("当前链表中没有您要删除的数据!");

        }else {
            System.out.println("删除成功!");
        }
    }

测试

public static void main(String[] args) {
        MyLinkedList linkedList = new MyLinkedList();
        linkedList.addFirst(4);
        linkedList.addFirst(4);
        linkedList.addFirst(4);
        linkedList.addFirst(4);
        linkedList.addFirst(2);
        linkedList.addFirst(1);
        linkedList.addFirst(5);
        linkedList.addFirst(6);
        linkedList.addFirst(4);

        linkedList.printHead();
        System.out.println();

        linkedList.removeDataAll(4);
        linkedList.printHead();
        System.out.println();

    }


3.3.6 删除链表 

即将所有节点全部删除

让每个节点的 next ,prev 都置空

  public void clear(){
        //删除整个链表
        if (this.head == null) {
            System.out.println("当前链表为空!");
            return;
        }
        ListNode cur = this.head;
        while (cur!= null){
//            cur.val = null;如果是引用数据类型就需要置空
            ListNode tmp = cur.next;
            cur.next = null;
            cur.prev = null;
            cur = tmp;
        }
        this.head = null;
        this.last = null;
    }

四、LinkedList

Java 中已经封装好了 LinkedList

4.1 什么是 LinkedList

LinkedList 官方文档 https://docs.oracle.com/javase/8/docs/api/java/util/LinkedList.html

LinkedList  的底层是双向链表结构 ( 链表后面介绍 ) ,由于链表没有将元素存储在连续的空间中,元
素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素
时,不需要搬移元素,效率比较高。
1. LinkedList  实现了  List 接口
2. LinkedList  的底层使用了双向链表
3. LinkedList  没有实现  RandomAccess  接口,因此  LinkedList  不支持随机访问
4. LinkedList  的任意位置插入和删除元素时效率比较高,时间复杂度为 O(1)
5. LinkedList  比较适合任意位置插入的场景

4.2 LinkedList 的使用

4.2.1 LinkedList的构造方法

    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        LinkedList<Number> linkedList2 = new LinkedList<>();

    }


4.2.2 常用方法

方法功能
boolean add (E e)
尾插 e
void add (int index, E element)
将 e 插入到 index 位置
e 插入到 index 位置
boolean addAll (Collection<? extends E> c)
尾插 c 中的元素
E remove (int index)
删除 index 位置元素
E get (int index)
获取下标 index 位置元素
E set (int index, E element)
将下标 index 位置元素设置为 element
void clear ()
清空
boolean contains (Object o)
判断 o 是否在线性表中
int indexOf (Object o)
返回第一个 o 所在下标
int lastIndexOf (Object o)
返回最后一个 o 的下标
List<E> subList (int fromIndex, int toIndex)
截取部分 list
…………

这里只说说 addAll subList

 public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(new Integer(2));
        linkedList.add(new Integer(3));
        linkedList.add(new Integer(4));
        linkedList.add(new Integer(5));
        System.out.println(linkedList);

        List<Integer> list2 = linkedList.subList(1,3);
        System.out.println(list2);

        linkedList.set(1,10);
        System.out.println(list2);
    }

与 ArrayList 实现的 subList

subList 只是将对应区间的地址截取出来返回

而不是产生新的对象返回


与 ArrayList 实现的 addAll 类似

形参 c 的类型必须是实现了 Collection 接口 

且其中存放的数据必须是 E 本身或 E 的子类

 public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(new Integer(2));
        linkedList.add(new Integer(3));
        linkedList.add(new Integer(4));
        linkedList.add(new Integer(5));

        LinkedList<Number> linkedList2 = new LinkedList<>(linkedList);
        System.out.println(linkedList);
        System.out.println(linkedList2);
        
    }

五、ArrayListLinkedList的区别

不同点
ArrayList
LinkedList
存储空间上
物理上一定连续
逻辑上连续,但物理上不一定连续
随机访问
支持 O(1)
不支持: O(N)
头插
需要搬移元素,效率低 O(N)
只需修改引用的指向,时间复杂度为 O(1)
插入
空间不够时需要扩容
没有容量的概念
应用场景
元素高效存储 + 频繁访问
任意位置插入和删除频繁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/797514.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ensp实现ICMP重定向实验

1 概述 ICMP重定向报文是ICMP控制报文中的一种。在特定的情况下&#xff0c;当路由器检测到一台机器使用非优化路由的时候&#xff0c;它会向该主机发送一个ICMP重定向报文&#xff0c;请求主机改变路由。路由器也会把初始数据包向它的目的地转发。 2 实验复现 拓扑如下 PC1配…

Tomcat部署以及优化

1、tomcat tomcat是用java代码的程序&#xff0c;运行的是java的web服务器 tomcat和php一样都是用来处理动态页面&#xff0c;tomcat也可以做为web应用服务器&#xff0c;开源的 php处理.php为结尾的 tomcat 处理.jsp nginx 处理 .html 2、tomcat的特点和功能 1、servle…

如何将LLM 集成到自动化生产系统

关键词&#xff1a;自动化系统、自主系统、LLM、数字孪生、微服务、多智能体系统 在技术不断发展的背景下&#xff0c;自动化和自主系统这两个术语经常交织在一起&#xff0c;但在影响上有明显的不同。两者都涉及到使用技术执行任务&#xff0c;最小化或不需要人工干预&#x…

Re:从零开始的C++世界——(一)入门基础

文章目录 C发展历史1.命名空间1.1 namespace的价值1.2 namespace的定义1.3 命名空间使⽤ 2.C输⼊&输出3.缺省参数3.1 缺省参数的概念3.2 缺省参数的分类 4.函数重载5.引⽤5.1引⽤的概念和定义5.2 引⽤的特性5.3 const引⽤5.4 使用场景5.5 指针和引⽤的关系 6.内联函数6.1内…

linux_进程周边知识——理解冯诺依曼体系结构

前言&#xff1a; 本篇内容是为了让友友们较好地理解进程的概念&#xff0c; 而在真正了解进行概念之前&#xff0c; 要先了解一下冯诺依曼体系结构。 所以博主会先对冯诺伊曼体系结构进行解释&#xff0c; 然后再讲解进程的概念。 ps&#xff1a; 本篇内容适合了解一些linux指…

【redis操作语句】

1.数据库操作 redis默认有16个数据库&#xff0c;编号为0~15&#xff0c;且默认访问0号数据库 获取当前键值对数量:先set创建一个键值对,再用dbsize获取&#xff0c;flushdb清空再获取。 127.0.0.1:6379> set k1 v1 OK 127.0.0.1:6379> dbsize (integer) 1 127.0.0.1:…

opencv 中如何通过欧式距离估算实际距离(厘米)

1&#xff1a;这个方法个人测试觉得是正确的&#xff0c;误差较小&#xff0c;目前满足我当前的需求&#xff0c;如果方法不对&#xff0c;请大家评论&#xff0c;完善。 2&#xff1a;确保拍摄的参照物是垂直的&#xff0c;如果不垂直&#xff0c;就会有误差&#xff0c;不垂…

基于SpringBoot+Hadoop+python的物品租赁系统(带1w+文档)

基于SpringBootHadooppython的物品租赁系统(带1w文档) 基于SpringBootHadooppython的物品租赁系统(带1w文档) 物品租赁系统是电子、信息技术相结合&#xff0c;是一种必然的发展趋势。以互联网为基础&#xff0c;以服务于广大用户为目的&#xff0c;发展整体优势&#xff0c;扩…

Flutter实现局部刷新的几种方式

目录 前言 1.局部刷新的重要性 1.概念 2.重要性 2.局部刷新实现的几种方式 1.使用setState方法进行局部刷新 2.使用StatefulWidget和InheritedWidget局部刷新UI 3.ValueNotifier和ValueListenableBuilder 4.StreamBuilder 5.Provider 6.GetX 7.使用GlobalKey 前言 …

游戏三倍补帧工具 Lossless Scaling v2.9.0

运行时请将游戏窗口化或全屏 比如你的显示器是144hz 把游戏限制帧率到48帧后开启三倍补帧 允许撕裂和垂直同步一起来延迟更低 72,48,36&#xff0c;分别对应1/2&#xff0c;1/3&#xff0c;1/4&#xff0c;性能够的话&#xff08;补帧后满144fps&#xff09;就优先锁72fps&a…

自动驾驶中的人机互相接管问题讨论

一、背景 人机接管&#xff08;human takeover&#xff09;是指在自动驾驶过程中&#xff0c;当系统遇到超出其处理能力或预设安全阈值的情况时&#xff0c;将控制权交还给驾驶员的过程。这一环节的设计直接关系到自动驾驶技术的实用性与安全性&#xff0c;是目前研究和实践中…

深入解析双向链表与单向链表的区别:示例详解

文章目录 一、单向链表与双向链表的定义及结构二、单向链表与双向链表的区别示例插入操作删除操作 三、完整示例四、总结 链表是一种灵活的数据结构&#xff0c;它通过指针连接一系列节点&#xff0c;实现了动态数组的特性。在众多链表类型中&#xff0c;单向链表和双向链表是最…

vue2学习笔记2-老规矩,从Hello World开始,了解Vue实例和模板

想要实现的效果&#xff1a;在页面上展示“Hello&#xff0c;World”字符串 1、准备一个【容器】div 想要把“Hello&#xff0c;World”放置在页面上&#xff0c;首先需要准备一个HTML的块级元素div&#xff0c;来承接内容。所以&#xff0c;我们先在<body>中定义<di…

Android12 MultiMedia框架之GenericSource extractor

前面两节学习到了各种Source的创建和extractor service的启动&#xff0c;本节将以本地播放为例记录下GenericSource是如何创建一个extractor的。extractor是在PrepareAsync()方法中被创建出来的&#xff0c;为了不过多赘述&#xff0c;我们直接从GenericSource的onPrepareAsyn…

《昇思25天学习打卡营第17天|K近邻算法实现红酒聚类》

K近邻算法原理介绍 K近邻算法&#xff08;K-Nearest-Neighbor, KNN&#xff09;是一种用于分类和回归的非参数统计方法&#xff0c;最初由 Cover和Hart于1968年提出是机器学习最基础的算法之一。它正是基于以上思想&#xff1a;要确定一个样本的类别&#xff0c;可以计算它与所…

springboot在线教育平台-计算机毕业设计源码68562

摘要 在数字化时代&#xff0c;随着信息技术的飞速发展&#xff0c;在线教育已成为教育领域的重要趋势。为了满足广大学习者对于灵活、高效学习方式的需求&#xff0c;基于Spring Boot的在线教育平台应运而生。Spring Boot以其快速开发、简便部署以及良好的可扩展性&#xff0c…

TypeError: Rule.__init__() got an unexpected keyword argument ‘method‘报错的解法

报错如图&#xff1a; 原代码&#xff1a; app.route(/query,method[get,post]) 解决办法很简单&#xff0c;method后加s​​​​​​​ app.route(/query,methods[get,post]) 重新执行代码&#xff0c;不报错了

C++ QT开发 学习笔记(1)

C QT开发 学习笔记(1) 考试系统 创建项目 新建Qt桌面应用程序&#xff0c;项目名&#xff1a;ExamSys。 类信息&#xff1a;类名LoginDialog继承自QDialog &#xff08;1&#xff09; ExamSys.pro 工程文件&#xff0c;包含当前工程的相关信息。 QDialog 是 Qt 框架中用…

大数据基础:Hadoop之MapReduce重点架构原理

文章目录 Hadoop之MapReduce重点架构原理 一、MapReduce概念 二、MapReduce 编程思想 2.1、Map阶段 2.2、Reduce阶段 三、MapReduce处理数据流程 四、MapReduce Shuffle 五、MapReduce注意点 六、MapReduce的三次排序 Hadoop之MapReduce重点架构原理 一、MapReduce概…

JavaScript中的面向对象编程

OPP在JavaScript的表现方式&#xff1a;原型 传统的OPP&#xff1a;类 ● 对象&#xff08;实例&#xff09;由类实例化&#xff0c;类的功能类似于蓝图&#xff0c;通过蓝图来实现建筑&#xff08;实例&#xff09; ● 行为&#xff08;方法&#xff09;从类复制到所有实例 …