当前位置: 首页 > news >正文

使用Guava轻松创建和管理不可变集合

第1章:引言

大家好,我是小黑。今天,我们来聊聊一个在Java编程里超有用的话题:使用Guava创建和管理不可变集合。首先,咱们得明白,什么是不可变集合。简单来说,不可变集合就是一旦创建就不能被修改的集合。

为啥要用不可变集合呢?想象一下,你写了一段代码,把一个集合传给了别的方法。如果那个方法不小心改了你的集合,那岂不是一场灾难?但如果你的集合是不可变的,这种情况就绝对不会发生。不可变集合还有助于编写更加清晰、更容易维护的代码,还能提高程序的性能哦。

第2章:Guava不可变集合简介

Guava是Google推出的一个Java库,里面有一堆好用的工具类,其中就包括了不可变集合。Guava的不可变集合和咱们平时用的Java标准库集合有啥不同呢?主要是Guava的集合一旦创建,就不能被修改,这就大大减少了出错的可能性。

来,让我给你演示一下怎么用Guava创建不可变集合。比如说,咱们要创建一个不可变的列表:

import com.google.common.collect.ImmutableList;public class ImmutableDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");System.out.println(immutableList);}
}

这段代码创建了一个包含三种水果名称的不可变列表。你看,使用ImmutableList.of就能轻松创建。这样一来,无论谁拿到这个列表,都不能添加、删除或者修改里面的元素。

同理,不可变的集合(Set)和映射(Map)也可以用类似的方式创建。只要记住,一旦创建,就不能更改了。这样的特性在很多场景下都非常实用,比如在多线程环境下,不可变集合可以保证数据的安全性,防止出现并发修改异常。

第3章:创建不可变集合

让我们来看看不可变列表(ImmutableList)的创建。咱们已经看过最基本的创建方式了,现在再来看点高级的:

import com.google.common.collect.ImmutableList;public class ImmutableListDemo {public static void main(String[] args) {// 创建一个不可变列表ImmutableList<String> immutableList = ImmutableList.<String>builder().add("Apple").add("Banana").add("Cherry").build();System.out.println(immutableList);}
}

在这个例子中,小黑用了ImmutableList.builder()方法。这个方法特别棒,因为它可以让咱们一步步地添加元素,最后再用build()方法一次性创建不可变列表。

接下来,看看不可变集(ImmutableSet)的创建:

import com.google.common.collect.ImmutableSet;public class ImmutableSetDemo {public static void main(String[] args) {// 创建一个不可变集ImmutableSet<String> immutableSet = ImmutableSet.of("Apple", "Banana", "Cherry");System.out.println(immutableSet);}
}

ImmutableSet.of()方法和列表的创建很像,也是一次性把所有元素传进去。

我们来看看不可变映射(ImmutableMap)的创建:

import com.google.common.collect.ImmutableMap;public class ImmutableMapDemo {public static void main(String[] args) {// 创建一个不可变映射ImmutableMap<String, Integer> immutableMap = ImmutableMap.of("Apple", 1, "Banana", 2, "Cherry", 3);System.out.println(immutableMap);}
}

在这个例子中,ImmutableMap.of()方法是用键值对的方式来创建映射的。这里,“Apple”是键,1是它的值,以此类推。

第4章:不可变集合的优势

安全性

首先是安全性。不可变对象是线程安全的,这意味着在多线程环境中,你完全不需要担心并发修改的问题。因为数据一旦创建就不会改变,所以不会出现线程间的冲突。这在编写并发程序时特别有用。

举个例子吧,假设有这样一个场景:

public class ThreadSafetyDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");// 多线程环境下访问不可变列表Runnable task = () -> {for (String fruit : immutableList) {System.out.println(Thread.currentThread().getName() + " - " + fruit);}};new Thread(task).start();new Thread(task).start();}
}

在这个例子中,即使多个线程同时访问immutableList,也不会引发任何问题,因为它是不可变的。

效率

接下来谈谈效率。不可变对象由于状态不变,可以减少内存的占用,因为它们可以被自由地共享。这就意味着在一定条件下,不可变集合比可变集合更加内存高效。

可读性

不可变集合还能提高代码的可读性和维护性。当你看到一个集合是不可变的,你就可以立即确定这个集合在整个生命周期内都不会改变。这就减少了理解和维护代码的复杂性。

使用Guava的不可变集合可以带来很多好处,特别是在处理安全性、效率和可读性方面。虽然它们在某些情况下可能有些限制,但在正确的场景下使用,绝对可以帮助你写出更优雅、更稳定的Java代码。

第5章:使用场景与最佳实践

使用场景
  1. 常量集合:当你想定义一些永远不变的数据,比如配置项、选项列表等,不可变集合是最佳选择。它们可以公开访问而不用担心被意外修改。

    public class Constants {public static final ImmutableList<String> FRUITS = ImmutableList.of("Apple", "Banana", "Cherry");// 其他常量...
    }
    
  2. 类内部状态:在创建类时,使用不可变集合来保存内部状态可以确保类的实例一旦创建就保持不变,这对于创建不可变类(immutable class)非常有用。

    public class UserPreferences {private final ImmutableSet<String> preferences;public UserPreferences(Set<String> preferences) {this.preferences = ImmutableSet.copyOf(preferences);}// Getter方法...
    }
    
  3. 函数返回值:当函数需要返回集合时,使用不可变集合作为返回类型可以确保调用者不会修改这个集合,从而保证了数据的完整性。

    public class ProductService {public ImmutableList<Product> getAvailableProducts() {// 查询并返回产品列表}
    }
    
最佳实践
  1. 避免预期外的修改:使用不可变集合可以防止调用者意外修改集合内容,从而导致难以追踪的bug。

  2. 提前拷贝:当从可变集合创建不可变集合时,应当在创建时就进行拷贝,以确保不可变集合的独立性。

    public class ConfigLoader {public ImmutableSet<String> loadConfig(Set<String> mutableSet) {return ImmutableSet.copyOf(mutableSet);}
    }
    
  3. 在构造函数中使用不可变集合:当创建对象时,可以使用Guava的不可变集合作为构造函数参数,这样可以在对象创建时就确保其不可变性。

    public class Employee {private final ImmutableList<String> skills;public Employee(List<String> skills) {this.skills = ImmutableList.copyOf(skills);}// Getter方法...
    }
    

第6章:与Java 8及以后版本的整合

利用Streams处理不可变集合

Java 8的Streams API可以和Guava的不可变集合无缝合作。比如说,你可以轻松地将一个不可变集合转换成Stream,进行各种操作,然后再收集回不可变集合。

import com.google.common.collect.ImmutableList;
import java.util.stream.Collectors;public class StreamIntegrationDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");// 使用Stream API处理Guava不可变集合ImmutableList<String> processedList = immutableList.stream().filter(s -> s.startsWith("B")).collect(ImmutableList.toImmutableList());System.out.println(processedList);}
}

在这个例子里,小黑先把一个不可变列表转换成了Stream,然后用filter方法筛选出以"B"开头的元素,最后再收集回一个不可变列表。这就是Java 8 Stream和Guava不可变集合强强联合的例子。

使用Lambda表达式

Java 8的Lambda表达式也可以和Guava的不可变集合很好地结合。它们可以使得对集合的操作更加简洁。

import com.google.common.collect.ImmutableList;public class LambdaIntegrationDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");// 使用Lambda表达式遍历不可变集合immutableList.forEach(fruit -> System.out.println("Fruit: " + fruit));}
}

在这个例子中,咱们使用了forEach方法和Lambda表达式来遍历不可变集合。这种方式比传统的for循环更简洁,更易读。

Guava的不可变集合与Java 8及以后版本的特性相结合,可以提供更强大的数据处理能力,同时让代码变得更加简洁和易于理解。

第7章:避免常见陷阱

不可变集合不等于只读集合

首先要清楚,Guava的不可变集合和只读集合不是一回事。不可变集合是在创建时就确定了内容,而只读集合只是不能修改,其底层数据可能被其他引用修改。看个例子:

import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class ImmutableVsReadOnly {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");// 创建只读集合List<String> readOnlyList = Collections.unmodifiableList(list);// 创建不可变集合ImmutableList<String> immutableList = ImmutableList.copyOf(list);// 修改原始列表list.add("Cherry");// 只读集合的内容发生了变化System.out.println("Read-only list: " + readOnlyList);// 不可变集合的内容没变System.out.println("Immutable list: " + immutableList);}
}

这个例子展示了只读集合和不可变集合的区别。当原始集合修改时,只读集合的内容也会跟着变,但不可变集合的内容不会变。

注意构建时的副作用

在使用ImmutableList.builder()或类似的构建器时,要注意不要引入副作用。比如,不要在添加元素的过程中修改这些元素。

// 错误示范:在构建不可变集合时修改元素
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (String fruit : fruits) {modifyFruit(fruit); // 不应在这里修改fruitbuilder.add(fruit);
}
小心空指针异常

Guava的不可变集合在创建时会对元素进行非空校验,这意味着如果你试图添加一个null元素,它会立即抛出NullPointerException

// 这将抛出NullPointerException
ImmutableList<String> list = ImmutableList.of("Apple", null, "Cherry");

第8章:总结

选择不可变集合不仅仅是为了编码的方便,更重要的是它们提供了额外的安全性、效率和可维护性。在多线程环境下,不可变集合几乎是必不可少的,因为它们天生就是线程安全的。此外,它们还能帮助减少bug和意外行为,尤其是在大型和复杂的项目中。

要注意,在某些情况下,使用不可变集合可能会有性能开销,特别是在需要频繁修改集合的场景中。因此,选择合适的数据结构和集合类型对于任何项目都至关重要。

希望这篇博客能够帮助你更好地理解Guava的不可变集合,让你在Java编程的道路上更进一步。如果你有任何问题或者想深入讨论,欢迎在评论区留言,小黑很乐意和你一起探讨和学习!

相关文章:

使用Guava轻松创建和管理不可变集合

第1章&#xff1a;引言 大家好&#xff0c;我是小黑。今天&#xff0c;我们来聊聊一个在Java编程里超有用的话题&#xff1a;使用Guava创建和管理不可变集合。首先&#xff0c;咱们得明白&#xff0c;什么是不可变集合。简单来说&#xff0c;不可变集合就是一旦创建就不能被修…...

深入了解 Android 中的应用程序签名

深入了解 Android 中的应用程序签名 一、应用程序签名介绍1.1 应用程序签名1.2 应用程序签名的意义1.3 应用程序签名的流程1.4 应用程序签名的方案1.5 签名的重要性和应用场景 二、AOSP 的应用签名2.1 AOSP的应用签名文件路径2.2 应用程序指定签名文件 三、Android Studio 的应…...

说说 style gan 中的感知路径长度(Perceptual Path Length)

我在之前的博库中介绍了 style gan 的基本原理&#xff0c;原文中有提出感知路径长度&#xff08;Perceptual Path Length&#xff09;的概念。这是一种评价生成器质量的方式。 PPL基本思想&#xff1a;给出两个随机噪声 z 1 , z 2 ​ &#xff0c;为求得两点的感知路径长度PPL…...

基于JAVA的厦门旅游电子商务预订系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 景点类型模块2.2 景点档案模块2.3 酒店管理模块2.4 美食管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学生表3.2.3 学生表3.2.4 学生表 四、系统展示五、核心代码5.1 新增景点类型5.2 查询推荐的…...

uniapp中使用封装步骤条组件

针对步骤条封装完终于清清楚楚啦 先看效果&#xff1a; 附上代码&#xff1a;使用可直接复用&#xff1a;数据是写在了当前组件中&#xff0c;如有必须&#xff0c;可以使用其中的props传值stepInfos传递相应的数据&#xff0c;根据steps步数就可以控制走到哪一步啦 <temp…...

【MySQL】sum 函数和 count 函数的相同作用

力扣题 1、题目地址 1174. 即时食物配送 II 2、模拟表 配送表&#xff1a;Delivery Column NameTypedelivery_idintcustomer_idintorder_datedatecustomer_pref_delivery_datedate delivery_id 是该表中具有唯一值的列。该表保存着顾客的食物配送信息&#xff0c;顾客在某…...

在QT Creator下用CMake编译GEOS库

最近&#xff0c;想要在C下编一个可用GDAL模块的地图管理系统&#xff0c;找来找去&#xff0c;找到了GEOS。GEOS&#xff08;Geometry Engine-Open Source&#xff09;开源几何引擎 是一个用于计算几何的JTS库的 C/C实现&#xff0c;专注于地理信息系统 &#xff08;GIS&#…...

【Qt之Quick模块】4. QML语法格式及命名规范

概述 QML&#xff08;Qt Meta-Object Language&#xff09;是一种声明式语言&#xff0c;用于设计用户界面。它是由Qt框架提供的一种描述界面组件的语言&#xff0c;可以与C代码结合使用&#xff0c;用于创建跨平台的应用程序。 QML具有以下特点&#xff1a; 声明式&#xff…...

Python内置类属性__class__属性的使用教程

概要 Python作为一种高级编程语言&#xff0c;提供了丰富的功能和灵活性&#xff0c;使得开发人员能够更加方便地处理各种任务。其中一个强大的功能是内置类属性__class__属性。本文将详细介绍__class__属性的用法&#xff0c;帮助读者更好地理解和利用这一功能。 第一部分&am…...

【后台报错】插入时sql报错,varchar撑爆

后台的一个报错。按照正常的需要复现&#xff0c;或者查一下日志。但是凭借多年经验和大胆猜测&#xff0c;以及对自己代码要自信 引用一下文章 目测7*15 105项。每个id有9个数字加上分隔符刚好十个。大概就是超过了定义的一千的varchar长度。直接改数据库就好了。 简单粗暴…...

OpenSergo使用详解

简介 OpenSergo是一个基于微服务治理的标准和生态&#xff0c;覆盖了服务元信息、流量治理、服务容错、数据库/缓存治理、服务注册发现、配置治理等十几个关键领域&#xff0c;覆盖了完整的微服务生命周期&#xff08;从开发态到测试态&#xff0c;到发布态&#xff0c;再到运…...

Vanilla Pro for Mac 一款隐藏菜单栏图标工具

Vanilla Pro Vanilla Pro是一款简单易于使用的Mac应用程序&#xff0c;可让您隐藏菜单栏图标。只需下载Vanilla&#xff0c;启动应用程序&#xff0c;然后按照提示即可开始。 资源获取 Vanilla Pro for Mac 功能特性 键盘快捷键&#xff1a;设置自定义键盘快捷键来切换菜单…...

freemarkEngine文件ftl的可视化编辑

在做导出word文件功能时&#xff0c;需要准备ftl模板&#xff0c;设置一些通配符&#xff0c;之后通过相关编码&#xff0c;即可以实现业务数据渲染后导出word的功能。但是ftl文件一般我们看不太懂&#xff0c;所以可视化创建和修改就非常合适。 1、安装office2016版本&#x…...

2023 英特尔On技术创新大会直播 | 边云协同加速 AI 解决方案商业化落地

目录 前言边云协同时代背景边缘人工智能边缘挑战英特尔边云协同的创新成果最后 前言 最近观看了英特尔On技术创新大会直播&#xff0c;学到了挺多知识&#xff0c;其中对英特尔高级首席 AI 工程张宇博士讲解的边云协同加速 AI 解决方案商业化落地特别感兴趣。张宇博士讲解了英…...

Q-star计划的更多细节

继续探讨点Q*相关的话题,这个应该是目前X和Reddit上比较火的话题了,其实就是关于Q*的方法是不是让LLM变得会产生意识,会产生自己的好恶和对人类的偏见,关于Q-star的一些介绍可以看我上一篇的扫盲帖 RLAIF方法与传说中的函数Q,揭露OpenAI那不为人知的Qstar计划 (qq.com) 我…...

python3 数据分析项目案例,用python做数据分析案例

本篇文章给大家谈谈python3 数据分析项目案例&#xff0c;以及用python做数据分析案例&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 目录 一丶可视化绘图案例 1.曲线图 2.柱形图 3.点线图 4.3D散点图 5. 绘制漏斗图 6. 绘制词云图 二丶包/模块使用示例 (1)…...

Android 12 (InputMethodManagerService) 替换默认输入法为Pinyin输入法

1.问题场景 由于系统自带的Latin输入法不支持遥控器操作&#xff0c;需要替换为RK的拼音输入法。 2. 替换步骤 1&#xff09;将LatinIME从mk中删除&#xff0c;让系统编译的时候不编译该apk --- a/Android/build/make/target/product/handheld_product.mkb/Android/build/m…...

【模式识别】探秘判别奥秘:Fisher线性判别算法的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《模式之谜 | 数据奇迹解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1f30c;1 初识模式识…...

【XML】TinyXML 详解

1、简介 优点&#xff1a; TinyXML 是一个简单、小型的 C XML 解析器&#xff0c;可以轻松集成到项目中。 TinyXML 解析 XML 文档&#xff0c;并根据该文档构建可读取、修改和保存的文档对象模型 (DOM) TinyXML 是在 ZLib 许可下发布的&#xff0c;因此可以在开源或商业代码中…...

泛社交裂变场景下AB增量科学评估方案

在数据驱动业务的大环境下&#xff0c;AB实验是对策略效果进行评估的重要指南针&#xff0c;并广泛应用在用增/推荐/搜索/内容/商业化等多个领域&#xff0c;尤其是综合评估多个策略对于大盘影响的对比效果&#xff0c;AB增量的计算就尤为重要。 背景 现在普遍常见的泛社交裂变…...

【无标题】json报错

操作失败&#xff0c;JSON parse error: Cannot deserialize value of type com.alibaba.fastjson.JSONArray from String value (token JsonToken.VALUE_STRING); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize valu…...

MyBatis中延迟加载,全局和局部的开启使用与关闭

文章目录 MyBatis中延迟加载&#xff0c;全局和局部的开启使用与关闭1、问题提出2、延迟加载和立即加载延迟加载立即加载 3、三种对应的表关系中的加载4、打开全局延迟加载&#xff08;实现一对一的延迟加载&#xff09;5、实现一对多的延迟加载&#xff08;将上面设置的全局延…...

MyBatis增删改查基础及其xml文件

目录 一.增删改查基础 1.增 增Insert 获取自增ID 对insert参数进行重命名 2.删 3.改 4.查 1)对MySQL查询结果进行重命名 从MySQL层面--as 从mybatis层面--Results注解 复用Results的定义 2)配置自动转换驼峰命名&#xff08;推荐&#xff09; 二.Mybatis xml配置…...

视觉学习(1)—— 理论

一、提问 初入视觉的几个问题&#xff1a; 1、视觉是怎么被控制的 2、怎么让视觉启动 3、视觉需要完成什么东西 4、视觉启动如何将完成的东西反馈给我 二、回答 1、视觉都是通过通讯协议控制&#xff0c;如串口、Modbus TCP等协议 2、视觉中的所有功能块都在流程中&…...

pip 常用指令 pip list 命令用法介绍

&#x1f4d1;pip 常用命令归类整理 pip list 是一个用于列出已安装的 Python 包的命令。这个命令会显示出所有已安装的包&#xff0c;以及它们的版本号。 pip list 命令有以下参数 -o, --outdated&#xff1a;列出所有过时的包&#xff0c;即有新版本可用的包。-u, --uptod…...

【DP】62.不同路径

题目 法1&#xff1a;二维DP 必须掌握&#xff01; class Solution {public int uniquePaths(int m, int n) {int[][] matrix new int[m][n];Arrays.fill(matrix[0], 1);for (int i 0; i < m; i) {matrix[i][0] 1;}for (int i 1; i < m; i) {for (int j 1; j <…...

大数据、知识图谱和强化学习的综合应用

大数据、知识图谱和强化学习各自在不同的方面都发挥了重要作用&#xff0c;它们分别涉及了大规模数据的处理和挖掘、基于图的数据结构和信息提取&#xff0c;以及基于奖励反馈的决策制定。将它们结合使用&#xff0c;可以构建出更加强大和智能的系统。以下是一些可能的应用场景…...

MFC使用高速绘图控件high-speed Charting Control绘制柱形图

1. 创建MFC单文档工程BarChartDemo。 2. 在工程文件夹下新建文件夹ChartCtrl,将ChartCtrl源码放入,如下图所示。在工程中添加这些项,项目——添加——现有项,全部添加。 3. 添加一个对话框,ID为IDD_DLG_BAR,类名为CBarDlg。 4. 在对话框中添加Custom Control控件,将控…...

vue3 H5项目中实现PDF预览

是需要npm i vue-pdf-embed 安装这个插件的&#xff0c;可兼容ios/Android&#xff0c;下面是本人的使用实例 <template><div class"conten_box"><vue-pdf-embed v-if"pdfSource.url" :source"pdfSource" /></div> <…...

【WebRTC---源码篇】(十一:一)采集编码发送期间使用时间戳的详细解读

一、时间戳定义 1、 NTP时间 NtpTime RealTimeClock::CurrentNtpTime() //获取从1900-01-01 00:00.00到当前时刻经过的时间 int64_t RealTimeClock::CurrentNtpInMilliseconds() //获取从1900-01-01 00:00.00到当前时刻经过的毫秒数,ms int64_t rtc::TimeUTCMicros() //获取…...

惠州营销网站建设/小程序开发公司排行榜

当今的互联网&#xff0c;用户为王。传统的用户单方面接收信息&#xff08;Web 1.0&#xff09;、用户创造信息&#xff08;Web 2.0&#xff09;的爆炸时代已经趋缓&#xff0c;互联网逐步向用户之间交流信息、分享信息的平台发展。如何打造一个优秀的社区&#xff0c;累积有价…...

刚开始的网站开发公司/网站如何快速收录

hadoop是用Java语言实现的开源软件框架&#xff0c;可以支持多种语言&#xff0c;我学习的时候用得自然就是Java了。 在开始编程之前需要做一些配置工作&#xff1a; Hadoop开发&#xff1a;Hadoop为HDFS和Mapreduce提供了基础的支持&#xff0c;叫hadoop common。Hadoop有一个…...

那个网站可以做网站测速对比/百度推广后台登录页面

文章目录1.下载安装包2.上传安装包3.修改配置文件3.1修改zeppelin-site.xml文件3.2修改zeppelin-env.sh文件4.启动zeppelin5.配置hive解释器5.1环境和变量配置5.2 在web界面配置集成hive6.使用Zepplin的hive解释器1.下载安装包 官网直达 选择zeppelin-0.8.1-bin-all.tgz 2.上…...

门户网站界面设计/seo是什么地方

题目表述 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。示例 1&#xff1a; 输入&#xff1a;n 4 输出&#xff1a;4 解释&#xff1a; T_3 0 1 1 2 T…...

dw动态网站怎么做搜索框/常见的网络营销方式有哪些

只要能存储数据的器件都可以称之为存储器&#xff0c;它的含义覆盖了寄存器&#xff0c;缓存&#xff0c;内存&#xff0c;硬盘。 cpu访问快慢的速度依次为:寄存器-> 缓存->内存->硬盘 寄存器&#xff08;register&#xff09;是中央处理器的组成部分&#xff0c;是…...

聊城网站制作信息/网站设计平台

前言 最近为了学习C和qt5&#xff0c;跟着教程写了一个翻金币的小游戏&#xff0c;源码和资源文件的链接在这里&#xff1a;翻金币小游戏源码和资源文件的下载链接。里面有自己写的超多代码注释&#xff0c;只要2个C币&#xff0c;去秒。   作为第一个QT项目&#xff0c;还是…...