【Java】优化重复冗余代码的8种方式
文章目录
- 前言
- 1. 抽取公用方法
- 2. 抽工具类
- 3. 反射
- 4.泛型
- 5. 继承与多态
- 6.使用设计模式
- 7.自定义注解(或者说AOP面向切面)
- 8.函数式接口和Lambda表达式
前言
日常开发中,我们经常会遇到一些重复代码。大家都知道重复代码不好,它主要有这些缺点:可维护性差、可读性差、增加错误风险等等。最近呢,我优化了一些系统中的重复代码,用了好几种的方式。感觉挺有用的,所以本文给大家讲讲优化重复代码的几种方式。
-
抽取公用方法
-
抽个工具类
-
反射
-
泛型
-
继承和多态
-
设计模式
-
函数式
-
AOP
1. 抽取公用方法
抽取公用方法,是最常用的代码去重方法~
比如这个例子,分别遍历names列表,然后各自转化为大写和小写打印出来:
public class TianLuoExample {public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "TianLuo");System.out.println("Uppercase Names:");for (String name : names) {String uppercaseName = name.toUpperCase();System.out.println(uppercaseName);}System.out.println("Lowercase Names:");for (String name : names) {String lowercaseName = name.toLowerCase();System.out.println(lowercaseName);}}
}
显然,都是遍历names过程,代码是重复的,只不过转化大小写不一样。我们可以抽个公用方法processNames,优化成这样:
public class TianLuoExample {public static void processNames(List<String> names, Function<String, String> nameProcessor, String processType) {System.out.println(processType + " Names:");for (String name : names) {String processedName = nameProcessor.apply(name);System.out.println(processedName);}}public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "TianLuo");processNames(names, String::toUpperCase, "Uppercase");processNames(names, String::toLowerCase, "Lowercase");}
}
2. 抽工具类
我们优化重复代码,抽一个公用方法后,如果发现这个方法有更多共性,就可以把公用方法升级为一个工具类。比如这样的业务场景:我们注册的时候,修改邮箱,重置密码等,都需要校验邮箱
实现注册功能时,用户会填邮箱,需要验证邮箱格式,
public class RegisterServiceImpl implements RegisterService{private static final String EMAIL_REGEX ="^[A-Za-z0-9+_.-]+@(.+)$";public boolean registerUser(UserInfoReq userInfo) {String email = userInfo.getEmail();Pattern pattern = Pattern.compile(EMAIL_REGEX);Matcher emailMatcher = pattern.matcher(email);if (!emailMatcher.matches()) {System.out.println("Invalid email address.");return false;}// 进行其他用户注册逻辑,比如保存用户信息到数据库等// 返回注册结果return true;}
}
在密码重置流程中,通常会向用户提供一个链接或验证码,并且需要发送到用户的电子邮件地址。在这种情况下,也需要验证邮箱格式合法性:
public class PasswordServiceImpl implements PasswordService{private static final String EMAIL_REGEX ="^[A-Za-z0-9+_.-]+@(.+)$";public void resetPassword(PasswordInfo passwordInfo) {Pattern pattern = Pattern.compile(EMAIL_REGEX);Matcher emailMatcher = pattern.matcher(passwordInfo.getEmail());if (!emailMatcher.matches()) {System.out.println("Invalid email address.");return false;}//发送通知修改密码sendReSetPasswordNotify();}
}
我们可以抽取个校验邮箱的方法出来,又因为校验邮箱的功能在不同的类中,因此,我们可以抽个校验邮箱的工具类:
public class EmailValidatorUtil {private static final String EMAIL_REGEX ="^[A-Za-z0-9+_.-]+@(.+)$";private static final Pattern pattern = Pattern.compile(EMAIL_REGEX);public static boolean isValid(String email) {Matcher matcher = pattern.matcher(email);return matcher.matches();}
}//注册的代码可以简化为这样啦
public class RegisterServiceImpl implements RegisterService{public boolean registerUser(UserInfoReq userInfo) {if (!EmailValidatorUtil.isValid(userInfo.getEmail())) {System.out.println("Invalid email address.");return false;}// 进行其他用户注册逻辑,比如保存用户信息到数据库等// 返回注册结果return true;}
}
3. 反射
我们日常开发中,经常需要进行PO、DTO和VO的转化。所以大家经常看到类似的代码:
//DTO 转VO
public UserInfoVO convert(UserInfoDTO userInfoDTO) {UserInfoVO userInfoVO = new UserInfoVO();userInfoVO.setUserName(userInfoDTO.getUserName());userInfoVO.setAge(userInfoDTO.getAge());return userInfoVO;
}//PO 转DTO
public UserInfoDTO convert(UserInfoPO userInfoPO) {UserInfoDTO userInfoDTO = new UserInfoDTO();userInfoDTO.setUserName(userInfoPO.getUserName());userInfoDTO.setAge(userInfoPO.getAge());return userInfoDTO;
}
我们可以使用BeanUtils.copyProperties() 去除重复代码,BeanUtils.copyProperties()底层就是使用了反射:
public UserInfoVO convert(UserInfoDTO userInfoDTO) {UserInfoVO userInfoVO = new UserInfoVO();BeanUtils.copyProperties(userInfoDTO, userInfoVO);return userInfoVO;}public UserInfoDTO convert(UserInfoPO userInfoPO) {UserInfoDTO userInfoDTO = new UserInfoDTO();BeanUtils.copyProperties(userInfoPO,userInfoDTO);return userInfoDTO;}
4.泛型
泛型是如何去除重复代码的呢?给大家看个例子,我有个转账明细和转账余额对比的业务需求,有两个类似这样的方法:
private void getAndUpdateBalanceResultMap(String key, Map<String, List<TransferBalanceDTO>> compareResultListMap,
List<TransferBalanceDTO> balanceDTOs) {List<TransferBalanceDTO> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());tempList.addAll(balanceDTOs);compareResultListMap.put(key, tempList);
}private void getAndUpdateDetailResultMap(String key, Map<String, List<TransferDetailDTO>> compareResultListMap,List<TransferDetailDTO> detailDTOS) {List<TransferDetailDTO> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());tempList.addAll(detailDTOS);compareResultListMap.put(key, tempList);
}
这两块代码,流程功能看着很像,但是就是不能直接合并抽取一个公用方法,因为类型不一致。单纯类型不一样的话,我们可以结合泛型处理,因为泛型的本质就是参数化类型.优化为这样:
private <T> void getAndUpdateResultMap(String key, Map<String, List<T>> compareResultListMap, List<T> accountingDTOS) {
List<T> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());
tempList.addAll(accountingDTOS);
compareResultListMap.put(key, tempList);
}
5. 继承与多态
假设你正在开发一个电子商务平台,需要处理不同类型的订单,例如普通订单和折扣订单。每种订单都有一些共同的属性(如订单号、购买商品列表)和方法(如计算总价、生成订单报告),但折扣订单还有特定的属性和方法。
在没有使用继承和多态的话,会写出类似这样的代码:
//普通订单
public class Order {private String orderNumber;private List<Product> products;public Order(String orderNumber, List<Product> products) {this.orderNumber = orderNumber;this.products = products;}public double calculateTotalPrice() {double total = 0;for (Product product : products) {total += product.getPrice();}return total;}public String generateOrderReport() {return "Order Report for " + orderNumber + ": Total Price = $" + calculateTotalPrice();}
}//折扣订单
public class DiscountOrder {private String orderNumber;private List<Product> products;private double discountPercentage;public DiscountOrder(String orderNumber, List<Product> products, double discountPercentage) {this.orderNumber = orderNumber;this.products = products;this.discountPercentage = discountPercentage;}public double calculateTotalPrice() {double total = 0;for (Product product : products) {total += product.getPrice();}return total - (total * discountPercentage / 100);}public String generateOrderReport() {return "Order Report for " + orderNumber + ": Total Price = $" + calculateTotalPrice();}
}
显然,看到在Order和DiscountOrder类中,generateOrderReport() 方法的代码是完全相同的。calculateTotalPrice()则是有一点点区别,但也大相径庭。
我们可以使用继承和多态去除重复代码,让DiscountOrder去继承Order,代码如下:
public class Order {private String orderNumber;private List<Product> products;public Order(String orderNumber, List<Product> products) {this.orderNumber = orderNumber;this.products = products;}public double calculateTotalPrice() {double total = 0;for (Product product : products) {total += product.getPrice();}return total;}public String generateOrderReport() {return "Order Report for " + orderNumber + ": Total Price = $" + calculateTotalPrice();}
}public class DiscountOrder extends Order {private double discountPercentage;public DiscountOrder(String orderNumber, List<Product> products, double discountPercentage) {super(orderNumber, products);this.discountPercentage = discountPercentage;}@Overridepublic double calculateTotalPrice() {double total = super.calculateTotalPrice();return total - (total * discountPercentage / 100);}
}
6.使用设计模式
很多设计模式可以减少重复代码、提高代码的可读性、可扩展性.比如:
- 工厂模式: 通过工厂模式,你可以将对象的创建和使用分开,从而减少重复的创建代码。
- 策略模式: 策略模式定义了一族算法,将它们封装成独立的类,并使它们可以互相替换。通过使用策略模式,你可以减少在代码中重复使用相同的逻辑。
- 模板方法模式:模板方法模式定义了一个算法的骨架,将一些步骤延迟到子类中实现。这有助于避免在不同类中重复编写相似的代码。
我给大家举个例子,模板方法是如何去除重复代码的吧,业务场景:
假设你正在开发一个咖啡和茶的制作流程,制作过程中的热水和添加物质的步骤是相同的,但是具体的饮品制作步骤是不同的。
如果没有使用模板方法模式,实现是这样的:
public class Coffee {public void prepareCoffee() {boilWater();brewCoffeeGrinds();pourInCup();addCondiments();}private void boilWater() {System.out.println("Boiling water");}private void brewCoffeeGrinds() {System.out.println("Brewing coffee grinds");}private void pourInCup() {System.out.println("Pouring into cup");}private void addCondiments() {System.out.println("Adding sugar and milk");}
}public class Tea {public void prepareTea() {boilWater();steepTeaBag();pourInCup();addLemon();}private void boilWater() {System.out.println("Boiling water");}private void steepTeaBag() {System.out.println("Steeping the tea bag");}private void pourInCup() {System.out.println("Pouring into cup");}private void addLemon() {System.out.println("Adding lemon");}
}
这个代码例子,我们可以发现,烧水和倒入杯子的步骤代码,在Coffee和Tea类中是重复的。
使用模板方法模式,代码可以优化成这样:
abstract class Beverage {public final void prepareBeverage() {boilWater();brew();pourInCup();addCondiments();}private void boilWater() {System.out.println("Boiling water");}abstract void brew();private void pourInCup() {System.out.println("Pouring into cup");}abstract void addCondiments();
}class Coffee extends Beverage {@Overridevoid brew() {System.out.println("Brewing coffee grinds");}@Overridevoid addCondiments() {System.out.println("Adding sugar and milk");}
}class Tea extends Beverage {@Overridevoid brew() {System.out.println("Steeping the tea bag");}@Overridevoid addCondiments() {System.out.println("Adding lemon");}
}
在这个例子中,我们创建了一个抽象类Beverage,其中定义了制作饮品的模板方法 prepareBeverage()。这个方法包含了烧水、倒入杯子等共同的步骤,而将制作过程中的特定步骤 brew() 和 addCondiments() 延迟到子类中实现。这样,我们避免了在每个具体的饮品类中重复编写相同的烧水和倒入杯子的代码,提高了代码的可维护性和重用性。
7.自定义注解(或者说AOP面向切面)
使用 AOP框架可以在不同地方插入通用的逻辑,从而减少代码重复。
业务场景:
假设你正在开发一个Web应用程序,需要对不同的Controller方法进行权限检查。每个Controller方法都需要进行类似的权限验证,但是重复的代码会导致代码的冗余和维护困难。
public class MyController {public void viewData() {if (!User.hasPermission("read")) {throw new SecurityException("Insufficient permission to access this resource.");}// Method implementation}public void modifyData() {if (!User.hasPermission("write")) {throw new SecurityException("Insufficient permission to access this resource.");}// Method implementation}
}
你可以看到在每个需要权限校验的方法中都需要重复编写相同的权限校验逻辑,即出现了重复代码.我们使用自定义注解的方式能够将权限校验逻辑集中管理,通过切面来处理,消除重复代码.如下:
@Aspect
@Component
public class PermissionAspect {@Before("@annotation(requiresPermission)")public void checkPermission(RequiresPermission requiresPermission) {String permission = requiresPermission.value();if (!User.hasPermission(permission)) {throw new SecurityException("Insufficient permission to access this resource.");}}
}public class MyController {@RequiresPermission("read")public void viewData() {// Method implementation}@RequiresPermission("write")public void modifyData() {// Method implementation}
}
就这样,不管多少个Controller方法需要进行权限检查,你只需在方法上添加相应的注解即可。权限检查的逻辑在切面中集中管理,避免了在每个Controller方法中重复编写相同的权限验证代码。这大大提高了代码的可读性、可维护性,并避免了代码冗余。
8.函数式接口和Lambda表达式
业务场景:
假设你正在开发一个应用程序,需要根据不同的条件来过滤一组数据。每次过滤的逻辑都可能会有些微的不同,但基本的流程是相似的。
没有使用函数式接口和Lambda表达式的情况:
public class DataFilter {public List<Integer> filterPositiveNumbers(List<Integer> numbers) {List<Integer> result = new ArrayList<>();for (Integer number : numbers) {if (number > 0) {result.add(number);}}return result;}public List<Integer> filterEvenNumbers(List<Integer> numbers) {List<Integer> result = new ArrayList<>();for (Integer number : numbers) {if (number % 2 == 0) {result.add(number);}}return result;}
}
在这个例子中,我们有两个不同的方法来过滤一组数据,但是基本的循环和条件判断逻辑是重复的,我们可以使用使用函数式接口和Lambda表达式,去除重复代码,如下:
public class DataFilter {public List<Integer> filterNumbers(List<Integer> numbers, Predicate<Integer> predicate) {List<Integer> result = new ArrayList<>();for (Integer number : numbers) {if (predicate.test(number)) {result.add(number);}}return result;}
}
我们将过滤的核心逻辑抽象出来。该方法接受一个 Predicate函数式接口作为参数,以便根据不同的条件来过滤数据。然后,我们可以使用Lambda表达式来传递具体的条件,这样最终也达到去除重复代码的效果。
相关文章:
【Java】优化重复冗余代码的8种方式
文章目录 前言1. 抽取公用方法2. 抽工具类3. 反射4.泛型5. 继承与多态6.使用设计模式7.自定义注解(或者说AOP面向切面)8.函数式接口和Lambda表达式 前言 日常开发中,我们经常会遇到一些重复代码。大家都知道重复代码不好,它主要有这些缺点:可…...

rabbitmq卸载重新安装3.8版本
卸载之前的版本的rabbitmq 卸载rabbitmq 卸载前先停止rabbitmq服务 /usr/lib/rabbitmq/bin/rabbitmqctl stop查看rabbitmq安装的相关列表 yum list | grep rabbitmq卸载rabbitmq相关内容 yum -y remove rabbitmq-server.noarch 卸载erlang 查看erlang安装的相关列表 …...

MyBatis分页思想和特殊字符
目录 一、MyBatis分页思想 1.1 使用场景 1.2 代码演示 二、MyBatis特殊字符 2.1代码演示 一、MyBatis分页思想 1.1 使用场景 Mybatis分页应用场景: MyBatis是一个Java持久层框架,它提供了一种将SQL查询和结果映射到Java对象的简单方式。分页是MyBa…...

设计模式大白话——命令模式
命令模式 一、概述二、经典举例三、代码示例(Go)四、总结 一、概述 顾名思义,命令模式其实和现实生活中直接下命令的动作类似,怎么理解这个命令是理解命令模式的关键!!!直接说结论是很不负责…...

[线程/C++(11)]线程池
文章目录 一、C实现线程池1. 头文件2. 测试部分 二、C11实现线程池1. 头文件2. 测试部分 一、C实现线程池 1. 头文件 #define _CRT_SECURE_NO_WARNINGS #pragma once #include<iostream> #include<string.h> #include<string> #include<pthread.h> #…...

VR防地质灾害安全教育:增强自然灾害知识,提高自我保护意识
VR防地质灾害安全教育系统是一种虚拟仿真技术,可以通过虚拟现实技术模拟地震、泥石流、滑坡等地质灾害的发生和应对过程,帮助人们提高应对突发自然灾害的能力。这种系统的优势在于可以增强自然灾害知识,提高自我保护意识,锻炼人们…...
Mybatis多对多查询案例!
在MyBatis中执行多对多查询需要使用两个主要表和一个连接表(通常称为关联表)来演示。在这个示例中,我们将使用一个示例数据库模型,其中有三个表:students、courses 和 student_courses,它们之间建立了多对多…...

Android OpenCV(七十五): 看看刚”转正“的条形码识别
前言 2021年,我们写过一篇《OpenCV 条码识别 Android 平台实践》,当时的条形码识别模块位于 opencv_contrib 仓库,但是 OpenCV 4.8.0 版本开始, 条形码识别模块已移动到 OpenCV 主仓库,至此我们无需自行编译即可轻松地调用条形码识别能力。 Bar code detector and decoder…...

数据结构——布隆计算器
文章目录 1.什么是布隆过滤器?2.布隆过滤器的原理介绍3.布隆过滤器使用场景4.通过 Java 编程手动实现布隆过滤器5.利用Google开源的 Guava中自带的布隆过滤器6.Redis 中的布隆过滤器6.1介绍6.2使用Docker安装6.3常用命令一览6.4实际使用 1.什么是布隆过滤器…...

金融学复习博迪(第6-9章)
第6章 投资项目分析 学习目的:解释资本预算;资本预算基本法则 资本预算过程包含三个基本要素: 一提出针对投资项目的建议 一对这些建议进行评价 一决定接受和拒绝哪些建议 6.1项目分析的特性 资本预算的过程中的基本单位是单个的投资项目。投…...

解决idea登录github copilot报错问题
试了好多方案都没用,但是这个有用, 打开idea-help-edit custonm vm options 然后在这个文件里面输入 -Dcopilot.agent.disabledtrue再打开 https://github.com/settings/copilot 把这个设置成allow,然后重新尝试登录copilot就行就行 解决方…...

什么是Flex布局?请列举一些Flex布局的常用属性。
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Flex布局(Flexible Box Layout)⭐ Flex布局的常用属性⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之…...
React + TypeScript + antd 常见开发场景
时间戳转格式 // 获取当前时间戳(示例) const timestamp Date.now(); // 或者使用特定的时间戳值// 创建一个新的Date对象,并传入时间戳 const date new Date(timestamp);// 获取年、月、日的值 const year date.getFullYear(); const mon…...

前端基础踩坑记录
前言:在做vue项目时,有时代码没有报错,但运行时却各种问题,没有报错排查起来就很费劲,本人感悟:写前端,需要好的眼神!!!谨以此博客记录下自己的踩坑点。 一、…...
k8s删除pod镜像没响应marking for deletion pod TaintManagerEviction
使用命令强制删除 Pod的状态为"Marking for deletion"表示该Pod正在被标记为待删除状态,但实际上并没有被删除。这可能是因为以下原因之一: 删除操作被阻塞:可能是由于某些资源或容器正在使用该Pod,导致删除操作被阻塞…...
Nginx 使用 lua-nginx-module 来获取post请求中的request和response信息
如果想要在nginx中打印出 http request 的所有 header,需要在编译nginx时开启 1、安装编译所需的依赖 apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev2、创建下载路径 mkdir -p /opt/download3、下载所需的文件 # 不要下载…...
【Opencv】三维重建之cv::recoverPose()函数(1)
官网链接 从估计的本质矩阵和两幅图像中的对应点恢复相机之间的旋转和平移,使用光束法则进行检验。返回通过检验的内点数目。 #include <opencv2/calib3d.hpp>int cv::recoverPose ( InputArray E, InputArray points1, InputArray points2, InputArray …...

Perl兼容正则表达式函数-PHP8知识详解
在php8中有两类正则表达式函数,一类是perl兼容正则表达式函数,另一类是posix扩展正则表达式函数。二者区别不大,我们推荐使用Perl兼容正则表达式函数。 1、使用正则表达式对字符串进行匹配 用正则表达式对目标字符串进行匹配是正则表达式的主…...
Python处理空值NaN
fork_address_tempread_excel_column_to_list(./eqp_info.xls,Sheet1,车辆地址)for i in fork_address_temp:print(type(i))fork_address[0 if address nan else address for address in fork_address_temp]fork_address结果 <class float><class float><class…...
软件机器人助力交通运输局数据录入,实现高效管理
随着科技的迅速发展,许多传统的行业正在寻求通过科技创新优化工作流程、提升效率。在这样的大背景下,交通运输部门也开始注重引入科技手段改善工作流程。博为小帮软件机器人正逐步改变着交通运输局的工作方式。 软件机器人:交通管理的利器 博…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001
qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类,直接把源文件拖进VS的项目里,然后VS卡住十秒,然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分,导致编译的时候找不到了。因…...

Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...