Java中Map作为行参是否能够修改?

本文探讨了在Java中遇到的一个问题,即尝试修改传递给方法的Map参数时抛出了`UnsupportedOperationException`。分析指出,可能是由于传入的Map是不可变的。通过检查代码,发现该Map可能已被转换为只读对象,从而导致无法修改。解决办法是确保传递可修改的Map实例给方法,以避免此类异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道,Java对于基础类型和引用类型分的很清晰,如数组、集合等是很常见的引用类型,下面有个案例是将map作为形参,然后在新的方法里对其进行修改:

 运行时却报错:

java.lang.UnsupportedOperationException
    at java.util.Collections$UnmodifiableMap.put(Collections.java:1457)
    at com.jwb.neo4j.QueryKnowledge.replaceMapOfName(QueryKnowledge.java:94)
    at com.jwb.neo4j.QueryKnowledge.querySingleEntry(QueryKnowledge.java:31)
    at com.jwb.neo4j.QueryKnowledgeTest.test_querySingleEntry1(QueryKnowledgeTest.java:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

        理论上来说,map作为形参传递是一个对象的引用,而在新的方法里对其进行修改,改的实际上是内存中那个对象,所以调了这个replaceMapOfName方法,会其原来map进行了修改,但是为什么会抛出异常呢?

分析:

1)debug,运行到 map.put("名称", name); 就抛出异常,也就是说一修改就会报错

2)假设1:是因为 这个map对象放入形参,仅仅是一个拷贝的内存对象副本,而这个副本是只读的,不能对其修改?

3)假设2:由于Map本身就是一个接口,里面的add方法是抽象方法并没有实现,而这里面作为形参

4)假设3:这个对象被改为了 不可修改的map(juc包下的)

最后验证第三个假设时,找到原因了

 

这样就解决了问题 

 

<think>好的,用户现在问的是Java方法形参写多了的问题。首先,我需要理解用户的具体需求。他可能是在开发过程中遇到了方法数过多的情况,想知道如何处理这种情况。接下来,我要回忆常见的解决方法,比如重构技术,设计模式的应用,以及Java的新特性。 用户之前询问过关于Servlet过滤器的代码解析,可能有一定的Java开发经验,但可能对代码结构和设计模式还不够熟悉。所以需要给出具体的解决方案,同时结合实例,方便用户理解。 首先,数过多会导致代码可读性下降,维护困难,还可能违反单一职责原则。常见的解决方法包括引入数对象、使用建造者模式、或者利用Java 8以后的特性如函数式数。需要逐一解释这些方法,并给出示例代码。 另外,用户可能还想知道数过多的负面影响,比如测试复杂度增加,方法调用不便等。需要简明扼要地列出这些缺点,帮助用户全面理解问题。 还要考虑用户可能的后续问题,比如如何选择合适的方法重构,或者某些方法的具体应用场景。因此在回答中可以适当提及适用场景,比如数对象适用于多个相关数,建造者模式适合可选数多的情况,函数式接口适合数化的情况。 需要确保回答结构清晰,分点列出问题和解决方案,使用代码示例增强可操作性。同时,语言要简洁明了,避免使用过于专业的术语,但又要准确无误,确保用户能够按照指导进实际操作。 最后,总结部分要强调重构的重要性,以及选择合适方法的原则,比如根据数性质和应用场景来决定。这样用户在面对类似问题时,能够有明确的解决思路。</think>以下是关于Java方法数过多的系统性分析和解决方案: 一、数过多的负面影响 1. 可维护性问题 - 方法签名复杂度:$f(a,b,c,d,e,f)$ 难以直观理解数用途 - 调用风险增加:容易导致数顺序错误(例如`process(userId, orderId)` vs `process(orderId, userId)`) 2. 设计缺陷表现 - 违反单一职责原则(示例代码): ```java // 同时处理用户验证、订单计算、日志记录 public void processOrder(User user, Order order, Logger logger, Validator validator) { ... } ``` 3. 扩展性问题 - 数变更导致调用链级联修改 - 增加单元测试用例组合复杂度($n$个数每个$m$种情况 → $m^n$种组合) 二、重构解决方案 1. **数对象模式**(推荐指数:★★★★★) ```java // 原始方法 public void createUser(String name, int age, String email, String address, String phone) { ... } // 重构为 public class UserParams { private String name; private int age; private String email; private String address; private String phone; // builder模式可选 } public void createUser(UserParams params) { ... } ``` 2. **建造者模式**(适合可选数场景) ```java public class QueryBuilder { private String table; private List<String> columns = new ArrayList<>(); private Map<String, Object> filters = new HashMap<>(); public QueryBuilder table(String table) { this.table = table; return this; } public QueryBuilder addColumn(String col) { columns.add(col); return this; } public String build() { return "SELECT " + String.join(",", columns) + " FROM " + table; } } // 调用示例 new QueryBuilder() .table("users") .addColumn("name") .addColumn("age") .build(); ``` 3. **函数式编程数**(Java 8+) ```java // 原始多方法 public void processData(List<Data> list, Comparator<Data> sorter, Predicate<Data> filter, Consumer<Data> handler) { ... } // 重构为链式调用 public DataProcessor pipeline() { return new DataProcessor(); } public class DataProcessor { private Comparator<Data> sorter = Comparator.naturalOrder(); private Predicate<Data> filter = x -> true; public DataProcessor sortBy(Comparator<Data> sorter) { this.sorter = sorter; return this; } public DataProcessor filter(Predicate<Data> filter) { this.filter = filter; return this; } public void execute(Consumer<Data> handler) { // 执处理逻辑 } } ``` 三、代码质量检测标准 1. 阈值建议: - 严格模式:方法数 ≤ 4个 - 警告阈值:5-6个数 - 重构强制线:≥7个数 2. IDEA检测配置示例: ```xml <rule> <name>ExcessiveParameterCount</name> <priority>MAJOR</priority> <parameters> <parameter> <key>max</key> <value>5</value> </parameter> </parameters> </rule> ``` 四、特殊场景处理 1. 无法避免多数时: ```java // 使用@Parameter注解增强可读性 public void sendEmail( @Param("recipient") String to, @Param("sender") String from, @Param("priority") int level) { ... } ``` 2. 可变数技巧: ```java // 使用Map处理动态数 public void configServer(Map<String, Object> settings) { String host = (String) settings.getOrDefault("host", "localhost"); int port = (int) settings.getOrDefault("port", 8080); } ``` 五、最佳实践原则 1. **语义完整性检查**: - 当多个数频繁同时出现时(如`width`和`height`)应封装为对象 2. **生命周期考量**: - 数对象应与方法作用域匹配(方法内部使用 → 局部类;跨方法使用 → 独立类) 3. **API设计准则**: ```java // 反模式:布尔数陷阱 public void formatText(String text, boolean isBold, boolean isItalic) { ... } // 正解:使用枚举数 public enum TextStyle { BOLD, ITALIC, UNDERLINE } public void formatText(String text, Set<TextStyle> styles) { ... } ``` 六、性能考量(针对高频调用方法) 1. 数封装对GC的影响: - 在循环体内部创建数对象 → 可能增加Young GC频率 - 解决方案:重用对象或采用原生类型 2. 内存布局优化示例: ```java // 原始数 public void draw(float x1, float y1, float x2, float y2) { ... } // 优化为内存连续结构 public class Vector2D { final float x, y; // 保证内存连续性 public Vector2D(float x, float y) { this.x = x; this.y = y; } } public void draw(Vector2D start, Vector2D end) { ... } ``` 总结:当方法数超过5个时,建议优先采用数对象封装。根据数性质可选择Builder模式(可选数多)、策略对象(数)或函数式接口(逻辑传递)。关键原则是保持方法单一职责,同时提升API的语义清晰度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值