java引用对象的实践

关于操作引用类型的对象的一些体会

bg:今天在做一个面试题的接口的时候,遇到一个多级目录的问题。其实也比较好理解实现思路。但丢给gpt给了一段代码让我有点钻牛角尖了

下面先给一下实体类对应的结构和sql吧

CREATE TABLE `xchl_category` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `category_pid` bigint(20) NOT NULL DEFAULT '0' COMMENT '顶级类目id',
  `category_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '类目ID',
  `category_name` varchar(30) NOT NULL DEFAULT '' COMMENT '类目名称',
  `chinese_name` varchar(30) NOT NULL DEFAULT '' COMMENT '类目中文名',
  `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
  `update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `xchl_category_category_id_source_uindex` (`category_id`,`category_pid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1304 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='类目表';
import java.io.Serializable;
import java.util.List;

/**
 * @author Ramsey
 * @since 2024/6/9 12:49
 */
@Data
public class Category implements Serializable {
    private Long id;
    private Long categoryPid;
    private Long categoryId;
    private String categoryName;
    private String chineseName;
    private Long createTime;
    private Long updateTime;

    // 子类目列表
    private List<Category> children;

}

上面是对应的实体类以及数据库字段,需求本质上就是要返回多级目录给到前端,下面是代码实现

   List<Category> categories = categoryMapper.getAllCategories();
        Map<Long, Category> categoryMap = new HashMap<>();
        List<Category> topLevelCategories = new ArrayList<>();

        // 将分类放入map,并找出顶级分类
        for (Category category : categories) {
            categoryMap.put(category.getCategoryId(), category);
            if (category.getCategoryPid().equals(category.getCategoryId())) {
                topLevelCategories.add(category);
            }
        }

        // 递归建立分类树
        for (Category category : categories) {
            if (!category.getCategoryPid().equals(category.getCategoryId())) {
                Category parent = categoryMap.get(category.getCategoryPid());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(category);
                }
            }
        }
return topLevelCategories;

这里其实就遇到我不理解的点了:为什么返回的是topLevelCategories但是第二次遍历的时候并没有对topLevelCategories进行操作,里面的内容却自动的讲父子关系填充好了?

询问gpt回复是:

通过这些操作,所有的分类对象被正确地组织在一起。如果某个分类是顶级分类,它的子分类会通过上述逻辑添加到它的 children 列表中。因为 topLevelCategories 列表中的顶级分类是引用类型对象,它们的子分类会自动被填充。所以,尽管在第二次遍历中我们没有直接对 topLevelCategories 列表进行操作,但 topLevelCategories 中的每个顶级分类对象因为是引用类型,它们的内部结构已经被递归地填充完毕。因此,返回 topLevelCategories 列表就已经包含了完整的分类树结构。

在这里插入图片描述
通过debug我们可以看到。在第二次遍历的时候。操作的是内存编号6899的放入了6900的父品牌里面。在这里插入图片描述

那么操作结果也是显而易见的,实际上,我们并没有对顶级目录topLevelCategories进行操作,而是拿到了它里面的Category的引用,进行的父子目录的组合。从何实现了效果。这下也让我明白了对象引用的精髓

下面是gpt给的几个对象引用的案例,供大家学习参考

例子1:修改列表中的对象

假设我们有一个员工列表,每个员工有一个部门属性。如果我们想要修改从属于某一部门的所有员工的部门名称,我们可以直接操作对象,而不用直接修改列表。

class Department {
    String name;

    // constructor and getter/setter
    public Department(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Employee {
    String name;
    Department department;

    // constructor and getter/setter
    public Employee(String name, Department department) {
        this.name = name;
        this.department = department;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}

public class Main {
    public static void main(String[] args) {
        Department dept1 = new Department("Accounting");
        Department dept2 = new Department("IT");

        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("Alice", dept1));
        employees.add(new Employee("Bob", dept2));
        employees.add(new Employee("Charlie", dept1));

        // 修改部门名称
        dept1.setName("Finance");

        // 打印员工信息
        for (Employee emp : employees) {
            System.out.println(emp.name + " works in " + emp.getDepartment().getName());
        }
    }
}

输出:

Alice works in Finance
Bob works in IT
Charlie works in Finance

在这个例子中,通过修改 dept1 的名称,所有引用 dept1 的员工对象的部门名称都被修改了。

例子2:树形结构的生成

假设我们有一个简单的树结构,我们可以通过操作子节点对象来构建整个树。

class TreeNode {
    String value;
    List<TreeNode> children;

    public TreeNode(String value) {
        this.value = value;
        this.children = new ArrayList<>();
    }

    public void addChild(TreeNode child) {
        this.children.add(child);
    }

    public List<TreeNode> getChildren() {
        return children;
    }

    public String getValue() {
        return value;
    }
}

public class TreeDemo {
    public static void main(String[] args) {
        TreeNode root = new TreeNode("Root");
        TreeNode child1 = new TreeNode("Child 1");
        TreeNode child2 = new TreeNode("Child 2");

        root.addChild(child1);
        root.addChild(child2);

        TreeNode grandchild1 = new TreeNode("Grandchild 1");
        child1.addChild(grandchild1);

        // 输出整棵树的结构
        printTree(root, "");
    }

    public static void printTree(TreeNode node, String indent) {
        System.out.println(indent + node.getValue());
        for (TreeNode child : node.getChildren()) {
            printTree(child, indent + "  ");
        }
    }
}

输出:

Root
  Child 1
    Grandchild 1
  Child 2

在这个例子中,通过操作节点对象的子节点引用,我们构建了一棵树,然后通过递归的方式将其打印出来。

例子3:共享对象的状态变更

假设我们有多个引用指向同一个对象,我们可以通过其中任何一个引用来改变这个对象的状态,其他引用将会反映出这些改变。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/b0a21bdb767742eab7713ee16964b916.png#pic_center)

class SharedObject {
    String state;

    public SharedObject(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

public class SharedObjectDemo {
    public static void main(String[] args) {
        SharedObject shared = new SharedObject("Initial State");

        SharedObject ref1 = shared;
        SharedObject ref2 = shared;

        System.out.println("Ref1 state: " + ref1.getState());
        System.out.println("Ref2 state: " + ref2.getState());

        // 修改 shared 对象的状态
        ref1.setState("Modified State");

        // 其他引用也会反映出这些修改
        System.out.println("Ref1 state: " + ref1.getState());
        System.out.println("Ref2 state: " + ref2.getState());
    }
}

输出:

Ref1 state: Initial State
Ref2 state: Initial State
Ref1 state: Modified State
Ref2 state: Modified State

在这个例子中,通过 ref1 修改 shared 对象的状态,ref2 也反映出了这些变化,因为 ref1ref2 都引用了同一个 shared 对象。

总结

state: " + ref2.getState());
}
}


输出:

Ref1 state: Initial State
Ref2 state: Initial State
Ref1 state: Modified State
Ref2 state: Modified State


在这个例子中,通过 `ref1` 修改 `shared` 对象的状态,`ref2` 也反映出了这些变化,因为 `ref1` 和 `ref2` 都引用了同一个 `shared` 对象。

### 总结

这些例子证明了如何通过一个引用来修改对象,而其他指向同一对象的引用将反映这些修改。这也是为什么在多级分类树的构建中,通过 `categoryMap` 修改对象,最终返回的 `topLevelCategories` 会反映出所有的子分类结构。这是因为 `topLevelCategories` 中的对象引用已经被正确地构建和修改。

  • 32
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值