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

【Code】《代码整洁之道》笔记-Chapter15-JUnit内幕

第15章 JUnit内幕

JUnit是最有名的Java框架之一。就像别的框架一样,它概念简单,定义精确,实现优雅。但它的代码是怎样的呢?本章将研判来自JUnit框架的一个代码例子。

15.1 JUnit框架

JUnit有很多位作者,但它始于Kent Beck和Eric Gamma一次去亚特兰大的飞行旅程。Kent想学Java,而Eric则打算学习Kent的Smalltalk测试框架。“对于两个身处狭窄空间的极客,还有什么会比拿出笔记本电脑开始编码来得更自然呢?”经过3小时高海拔工作,他们写出了JUnit的基础代码。

我们要查看的模块,是用来帮忙鉴别字符串比较错误的一段聪明代码,该模块被命名为ComparisonCompactor。对于两个不同的字符串,如ABCDEABXDE,该模块将用形如<...B[X]D...>的字符串来曝露两者的不同之处。

我们可以做进一步解释,但测试用例会更有说服力。看看代码清单15-1,我们将深入了解到该模块满足的需求,边看代码,边研究该测试的结构,它们能变得更简洁或更明确吗?

代码清单15-1 ComparisonCompactorTest.java

package junit.tests.framework;import junit.framework.ComparisonCompactor;
import junit.framework.TestCase;public class ComparisonCompactorTest extends TestCase {public void testMessage() {String failure= new ComparisonCompactor(0, "b", "c").compact("a");assertTrue("a expected:<[b]> but was:<[c]>".equals(failure));}public void testStartSame() {String failure= new ComparisonCompactor(1, "ba", "bc").compact(null);assertEquals("expected:<b[a]> but was:<b[c]>", failure);}public void testEndSame() {String  failure= new ComparisonCompactor(1, "ab", "cb").compact(null);assertEquals("expected:<[a]b> but was:<[c]b>", failure);}public void testSame() {String failure= new ComparisonCompactor(1, "ab", "ab").compact(null);assertEquals("expected:<ab> but was:<ab>", failure);}public void testNoContextStartAndEndSame() {String failure= new ComparisonCompactor(0, "abc", "adc").compact(null);assertEquals("expected:<...[b]...> but was:<...[d]...>", failure);}public void testStartAndEndContext() {String failure= new ComparisonCompactor(1, "abc", "adc").compact(null);assertEquals("expected:<a[b]c> but was:<a[d]c>", failure);}public void testStartAndEndContextWithEllipses() {String failure= new ComparisonCompactor(1, "abcde", "abfde").compact(null);assertEquals("expected:<...b[c]d...> but was:<...b[f]d...>", failure);}public void testComparisonErrorStartSameComplete() {String failure= new ComparisonCompactor(2, "ab", "abc").compact(null);assertEquals("expected:<ab[]> but was:<ab[c]>", failure);}public void testComparisonErrorEndSameComplete() {String failure= new ComparisonCompactor(0, "bc", "abc").compact(null);assertEquals("expected:<[]...> but was:<[a]...>", failure);}public void testComparisonErrorEndSameCompleteContext() {String failure= new ComparisonCompactor(2, "bc", "abc").compact(null);assertEquals("expected:<[]bc> but was:<[a]bc>", failure);}public void testComparisonErrorOverlapingMatches() {String failure= new ComparisonCompactor(0, "abc", "abbc").compact(null);assertEquals("expected:<...[]...> but was:<...[b]...>", failure);}public void testComparisonErrorOverlapingMatchesContext() {String failure= new ComparisonCompactor(2, "abc", "abbc").compact(null);assertEquals("expected:<ab[]c> but was:<ab[b]c>", failure);}public void testComparisonErrorOverlapingMatches2() {String failure= new ComparisonCompactor(0, "abcdde", "abcde").compact(null);assertEquals("expected:<...[d]...> but was:<...[]...>", failure);}public void testComparisonErrorOverlapingMatches2Context() {String failure= new ComparisonCompactor(2, "abcdde", "abcde").compact(null);assertEquals("expected:<...cd[d]e> but was:<...cd[]e>", failure);}public void testComparisonErrorWithActualNull() {String failure= new ComparisonCompactor(0, "a", null).compact(null);assertEquals("expected:<a> but was:<null>", failure);}public void testComparisonErrorWithActualNullContext() {String failure= new ComparisonCompactor(2, "a", null).compact(null);assertEquals("expected:<a> but was:<null>", failure);}public void testComparisonErrorWithExpectedNull() {String failure= new ComparisonCompactor(0, null, "a").compact(null);assertEquals("expected:<null> but was:<a>", failure);}public void testComparisonErrorWithExpectedNullContext() {String failure= new ComparisonCompactor(2, null, "a").compact(null);assertEquals("expected:<null> but was:<a>", failure);}public void testBug609972() {String failure= new ComparisonCompactor(10, "S&P500", "0").compact(null);assertEquals("expected:<[S&P50]0> but was:<[]0>", failure);}
}

我对用到这些测试的ComparisonCompactor进行了代码覆盖分析,代码被100%覆盖了,每行代码、每个if语句和for循环都被测试执行了。于是我对代码的工作能力有了极强的信心,也对代码作者们的技艺产生了极高的尊敬之情。

ComparisonCompactor的代码如代码清单15-2所示。

代码清单15-2 ComparisonCompactor.java(原始版本)

package junit.framework;public class ComparisonCompactor {private static final String ELLIPSIS = "...";private static final String DELTA_END = "]";private static final String DELTA_START = "[";private int fContextLength;private String fExpected;private String fActual;private int fPrefix;private int fSuffix;public ComparisonCompactor(int contextLength, String expected, String actual) {fContextLength = contextLength;fExpected = expected;fActual = actual;}public String compact(String message) {if (fExpected == null || fActual == null || areStringsEqual())return Assert.format(message, fExpected, fActual);findCommonPrefix();findCommonSuffix();String expected = compactString(fExpected);String actual = compactString(fActual);return Assert.format(message, expected, actual);}private String compactString(String source) {String result = DELTA_START + source.substring(fPrefix, source.length() -fSuffix + 1) + DELTA_END;if (fPrefix > 0)result = computeCommonPrefix() + result;if (fSuffix > 0)result = result + computeCommonSuffix();return result;}private void findCommonPrefix() {fPrefix = 0;int end = Math.min(fExpected.length(), fActual.length());for (; fPrefix < end; fPrefix++) {if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))break;}}private void findCommonSuffix() {int expectedSuffix = fExpected.length() - 1;int actualSuffix = fActual.length() - 1;for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))break;}fSuffix = fExpected.length() - expectedSuffix;}private String computeCommonPrefix() {return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);}private String computeCommonSuffix() {int end = Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");}private boolean areStringsEqual() {return fExpected.equals(fActual);}
}

你可能会对这个模块有所抱怨,例如,里面有些长表达式,有些奇怪的+1操作,如此等等。不过,总的来说,这个模块很不错,毕竟它原本可能被写成如代码清单15-3中的样子。

代码清单15-3 ComparisonCompactor.java(背离版本)

package junit.framework;public class ComparisonCompactor {private int ctxt;private String s1;private String s2;private int pfx;private int sfx;public ComparisonCompactor(int ctxt, String s1, String s2) {this.ctxt = ctxt;this.s1 = s1;this.s2 = s2;}public String compact(String msg) {if (s1 == null || s2 == null || s1.equals(s2))return Assert.format(msg, s1, s2);pfx = 0;for (; pfx < Math.min(s1.length(), s2.length()); pfx++) {if (s1.charAt(pfx) != s2.charAt(pfx))break;}int sfx1 = s1.length() - 1;int sfx2 = s2.length() - 1;for (; sfx2 >= pfx && sfx1 >= pfx; sfx2--, sfx1--) {if (s1.charAt(sfx1) != s2.charAt(sfx2))break;}sfx = s1.length() - sfx1;String cmp1 = compactString(s1);String cmp2 = compactString(s2);return Assert.format(msg, cmp1, cmp2);}private String compactString(String s) {String result ="[" + s.substring(pfx, s.length() - sfx + 1) + "]";if (pfx > 0)result = (pfx > ctxt ? "..." : "") +s1.substring(Math.max(0, pfx - ctxt), pfx) + result;if (sfx > 0) {int end = Math.min(s1.length() - sfx + 1 + ctxt, s1.length());result = result + (s1.substring(s1.length() - sfx + 1, end) +(s1.length() - sfx + 1 < s1.length() - ctxt ? "..." : ""));}return result;}
}

即便作者们把这个模块写得已经很棒,但童子军军规却告诉我们,离时要比来时整洁。所以,我们怎样才能改进代码清单15-2中的原始代码呢?

我们首先看到的是成员变量f前缀[N6]。在现今的运行环境中,这类范围性编码纯属多余。所以,先删除所有的f前缀。

private int contextLength;
private String expected;
private String actual;
private int prefix;
private int suffix;

下一步,在compact函数开始处,有一个未封装的条件判断[G28]。

public String compact(String message) {if (expected == null || actual == null || areStringsEqual())return Assert.format(message, expected, actual);findCommonPrefix();findCommonSuffix();String expected = compactString(this.expected);String actual = compactString(this.actual);return Assert.format(message, expected, actual); 
}

这个条件判断应当封装起来,从而更清晰地表达代码的意图。我们拆解出一个方法,来解释这个条件判断。

public String compact(String message) {if (shouldNotCompact())return Assert.format(message, expected, actual);findCommonPrefix();findCommonSuffix();String expected = compactString(this.expected);String actual = compactString(this.actual);return Assert.format(message, expected, actual);
}private boolean shouldNotCompact() {return expected == null || actual == null || areStringsEqual();
}

我也不太喜欢compact函数中的this.expected符号和this.actual符号。这个是我们把fExpected改为expected时发生的。为什么函数中的变量会与成员变量同名呢?它们不是该表示其他意思吗[N4]?我们应该区分这些名称。

String compactExpected = compactString(expected);
String compactActual = compactString(actual);

否定式稍微比肯定式难理解一些[G29]。我们把if语句放到上头,调转条件判断。

public String compact(String message) {if (canBeCompacted()) {findCommonPrefix();findCommonSuffix();String compactExpected = compactString(expected);String compactActual = compactString(actual);return Assert.format(message, compactExpected, compactActual);} else {return Assert.format(message, expected, actual);}
}private boolean canBeCompacted() {return expected != null && actual != null && !areStringsEqual();
}

函数名很奇怪[N7]。尽管它的确会压缩字符串,但如果canBeCompacted()false,那么它实际上就不会压缩字符串。用compact来命名,隐藏了错误检查的副作用。注意,该函数返回一条格式化后的消息,而不仅是压缩后的字符串。所以,函数名其实应该是formatCompactedComparison。在用以下参数调用时,读起来会好很多:

public String formatCompactedComparison(String message) {

两个字符串是在if语句体中被压缩的。我们应当拆分出一个名为compactExpectedAndActual的方法。然而,我们希望formatCompactComparison函数完成所有的格式化工作。而compact...函数除了压缩之外什么都不做[G30]。所以,做如下拆分:

...private String compactExpected;private String compactActual;...public String formatCompactedComparison(String message) {if (canBeCompacted()) {compactExpectedAndActual();return Assert.format(message, compactExpected, compactActual);} else {return Assert.format(message, expected, actual);}}private void compactExpectedAndActual() {findCommonPrefix();findCommonSuffix();compactExpected = compactString(expected);compactActual = compactString(actual);}

注意,这要求我们向成员变量举荐compactExpectedcompactActual。我不喜欢新函数最后两行返回变量的方式,但前两个可不是这样。它们没采用一以贯之的约定[G11]。我们应该修改findCommonPrefixfindCommonSuffix,分别返回前缀和后缀值。

private void compactExpectedAndActual() {prefixIndex = findCommonPrefix();suffixIndex = findCommonSuffix();compactExpected = compactString(expected);compactActual = compactString(actual);
}private int findCommonPrefix() {int prefixIndex = 0;int end = Math.min(expected.length(), actual.length());for (; prefixIndex < end; prefixIndex++) {if (expected.charAt(prefixIndex) != actual.charAt(prefixIndex))break;}return prefixIndex;
}private int findCommonSuffix() {int expectedSuffix = expected.length() - 1;int actualSuffix = actual.length() - 1;for (; actualSuffix >= prefixIndex && expectedSuffix >= prefixIndex;actualSuffix--, expectedSuffix--) {if (expected.charAt(expectedSuffix) != actual.charAt(actualSuffix))break;}return expected.length() - expectedSuffix;
}

我们还应该修改成员变量的名称,使之更准确一点[N1],毕竟它们都是索引。

仔细检查findCommonSuffix,其中隐藏了一个时序性耦合[G31],该函数它依赖prefixIndex是由findCommonPrefix计算得来的事实。如果这两个方法不按这样的顺序调用,调试就会变得困难。为了曝露这个时序性耦合,我们将prefixIndex作为find的参数。

private void compactExpectedAndActual() {prefixIndex = findCommonPrefix();suffixIndex = findCommonSuffix(prefixIndex);compactExpected = compactString(expected);compactActual = compactString(actual);
}private int findCommonSuffix(int prefixIndex) {int expectedSuffix = expected.length() - 1;int actualSuffix = actual.length() - 1;for (; actualSuffix >= prefixIndex && expectedSuffix >= prefixIndex; actualSuffix--, expectedSuffix--) {if (expected.charAt(expectedSuffix) != actual.charAt(actualSuffix))break;}return expected.length() - expectedSuffix;
}

我对这样的方式不太满意,因为传递prefixIndex参数有些随意[G32],该参数成功维持了执行次序,但对于解释排序的需要却毫无作用。其他程序员可能会抹杀我们刚完成的工作,因为并没有迹象说明该参数确属必要。所以还是采取别的做法吧。

private void compactExpectedAndActual() {findCommonPrefixAndSuffix();compactExpected = compactString(expected);compactActual = compactString(actual);
}private void findCommonPrefixAndSuffix() {findCommonPrefix();int expectedSuffix = expected.length() - 1;int actualSuffix = actual.length() - 1;for (;actualSuffix >= prefixIndex && expectedSuffix >= prefixIndex;actualSuffix--, expectedSuffix--) {if (expected.charAt(expectedSuffix) != actual.charAt(actualSuffix))break;}suffixIndex = expected.length() - expectedSuffix;
}private void findCommonPrefix() {prefixIndex = 0;int end = Math.min(expected.length(), actual.length());for (; prefixIndex < end; prefixIndex++)if (expected.charAt(prefixIndex) != actual.charAt(prefixIndex))break;
}

我们恢复findCommonPreffixfindCommonSuffix的原样,把findCommonSuffix的名称改为findCommonPrefixAndSuffix,让它在执行其他操作之前,先调用findCommonPrefix。这样一来,就以一种比前种手段有效的方式建立了两个函数之间的时序关系。

private void findCommonPrefixAndSuffix() {findCommonPrefix();int suffixLength = 1;for (; !suffixOverlapsPrefix(suffixLength); suffixLength++) {if (charFromEnd(expected, suffixLength) !=charFromEnd(actual, suffixLength))break;}suffixIndex = suffixLength;
}private char charFromEnd(String s, int i) {return s.charAt(s.length()-i);
}private boolean suffixOverlapsPrefix(int suffixLength) {return actual.length() - suffixLength < prefixLength ||expected.length() - suffixLength < prefixLength;
}

这样就好多了。它曝露出suffixIndex其实是后缀的长度,而且名字没起好,对于prefix也是如此。虽然在那种情形下indexlength是同义的,但使用length一词更有一贯性。问题在于,suffixIndex变量并不是从0开始,而是从1开始的,所以并非真正的长度。这也是computeCommonSuffix中那些+1存在的原因[G33]。来修正它们吧,修正结果就是代码清单15-4。

代码清单15-4 ComparisonCompactor.java(过渡版本)

public class ComparisonCompactor {
...private int suffixLength;
...private void findCommonPrefixAndSuffix() {findCommonPrefix();suffixLength = 0;for (; !suffixOverlapsPrefix(suffixLength); suffixLength++) {if (charFromEnd(expected, suffixLength) != charFromEnd(actual, suffixLength))break;}}private char charFromEnd(String s, int i) {return s.charAt(s.length() - i - 1);}private boolean suffixOverlapsPrefix(int suffixLength) {return actual.length() - suffixLength <= prefixLength ||expected.length() - suffixLength <= prefixLength;}...private String compactString(String source) {String result = DELTA_START + source.substring(prefixLength, source.length() - suffixLength) + DELTA_END;if (prefixLength > 0)result = computeCommonPrefix() + result;if (suffixLength > 0)result = result + computeCommonSuffix();return result;}...private String computeCommonSuffix() {int end = Math.min(expected.length() - suffixLength +contextLength, expected.length());return expected.substring(expected.length() - suffixLength, end) +(expected.length() - suffixLength < expected.length() - contextLength ? ELLIPSIS : "");}

我们用charFromEnd中的那个-1替代了computeCommonSuffix中的一堆+1,前者更为合理,suffixOverlapsPrefix中的两个“<=”操作符也同理。这样我们就能修改suffixIndexsuffixLength的名称,极大地提升了代码的可读性。

不过还有一个问题。在消灭那些+1时,我注意到compactString中的以下代码:

if (suffixLength > 0)

看看代码清单15-4中的这行代码。因为suffixLength现在要比原本少1,所以我们应该把“>”操作符改为“>=”操作符。那本无道理,不过现在却有意义!这表示这么做没道理,而且可能是个缺陷。嗯,也不算是个缺陷。从之前的分析中我们可以看到,if语句现在会放置添加长度为零的后缀。在作出修改之前,if语句没有作用,因为suffixIndex永不会小于1。

这说明compactString中的两个if语句都有问题!看起来它们都该被删除。所以,我们将其注释掉,运行测试。测试通过了!那就重新构建compactString,删除没用的if语句,将函数改得更加简洁[G9]。

private String compactString(String source) {returncomputeCommonPrefix() +DELTA_START +source.substring(prefixLength, source.length() - suffixLength) +DELTA_END +computeCommonSuffix();
}

这样就好多了!现在我们看到,compactString函数只是把片段组合起来。我们甚至可以让它更清晰,还有许多细微的整理工作可做。与其拖着你遍历剩下的那些修改,我更愿意直接展示代码清单15-5中的结果。

代码清单15-5 ComparisonCompactor.java(最终版)

package junit.framework;public class ComparisonCompactor {private static final String ELLIPSIS = "...";private static final String DELTA_END = "]";private static final String DELTA_START = "[";private int contextLength;private String expected;private String actual;private int prefixLength;private int suffixLength;public ComparisonCompactor(int contextLength, String expected, String actual) {this.contextLength = contextLength;this.expected = expected;this.actual = actual;}public String formatCompactedComparison(String message) {String compactExpected = expected;String compactActual = actual;if (shouldBeCompacted()) {findCommonPrefixAndSuffix();compactExpected = compact(expected);compactActual = compact(actual);} return Assert.format(message, compactExpected, compactActual);}private boolean shouldBeCompacted() {return !shouldNotBeCompacted();}private boolean shouldNotBeCompacted() {return expected == null ||actual == null ||expected.equals(actual);}private void findCommonPrefixAndSuffix() {findCommonPrefix();suffixLength = 0;for (; !suffixOverlapsPrefix(); suffixLength++) {if (charFromEnd(expected, suffixLength) !=charFromEnd(actual, suffixLength))break;}}private char charFromEnd(String s, int i) {return s.charAt(s.length() - i - 1);}private boolean suffixOverlapsPrefix() {return actual.length() - suffixLength <= prefixLength ||expected.length() - suffixLength <= prefixLength;}private void findCommonPrefix() {prefixLength = 0;int end = Math.min(expected.length(), actual.length());for (; prefixLength < end; prefixLength++)if (expected.charAt(prefixLength) != actual.charAt (prefixLength))break;}private String compact(String s) {return new StringBuilder().append(startingEllipsis()).append(startingContext()).append(DELTA_START).append(delta(s)).append(DELTA_END).append(endingContext()).append(endingEllipsis()).toString();}private String startingEllipsis() {return prefixLength > contextLength ? ELLIPSIS : "";}private String startingContext() {int contextStart = Math.max(0, prefixLength - contextLength);int contextEnd = prefixLength;return expected.substring(contextStart, contextEnd);}private String delta(String s) {int deltaStart = prefixLength;int deltaEnd = s.length() - suffixLength;return s.substring(deltaStart, deltaEnd);}private String endingContext() {int contextStart = expected.length() - suffixLength;int contextEnd =Math.min(contextStart + contextLength, expected.length());return expected.substring(contextStart, contextEnd);}private String endingEllipsis() {return (suffixLength > contextLength ? ELLIPSIS : "");}
}

这的确很漂亮。模块分解成了一组分析函数和一组合成函数。它们以一种拓扑方式排序,每个函数的定义都正好在其被调用的位置后面。所有的分析函数都先出现,而所有的合成函数都最后出现。

仔细阅读,你会发现我推翻了在本章较前位置做出的几个决定。例如,我将几个分解出来的方法重新内联为formatCompactComparison,修改了souldNotBeCompacted表达式的意思,这种做法很常见。重构常会导致另一次推翻此次重构的重构。重构是一种不停试错的迭代过程,不可避免地集中于我们认为是专业人员该做的事。

15.2 小结

如此我们遵循了童子军军规。模块比我们发现它时更整洁了,不是说它原本不整洁,作者们做了卓越的工作,但模块都能再改进,我们每个人都有责任把模块改进得比发现它时更整洁。

相关文章:

【Code】《代码整洁之道》笔记-Chapter15-JUnit内幕

第15章 JUnit内幕 JUnit是最有名的Java框架之一。就像别的框架一样&#xff0c;它概念简单&#xff0c;定义精确&#xff0c;实现优雅。但它的代码是怎样的呢&#xff1f;本章将研判来自JUnit框架的一个代码例子。 15.1 JUnit框架 JUnit有很多位作者&#xff0c;但它始于K…...

【Java八股】

JVM JVM中有哪些引用 在Java中&#xff0c;引用&#xff08;Reference&#xff09;是指向对象的一个变量。Java中的引用不仅仅有常规的直接引用&#xff0c;还有不同类型的引用&#xff0c;用于控制垃圾回收&#xff08;GC&#xff09;的行为和优化性能。JVM中有四种引用类型…...

深入探究AI编程能力:ChatGPT及其大规模模型的实现原理

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4-turbo模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 随着人工智能的快速发展&#xff0c…...

高德地图 JS-SDK 实现教程

高德地图 JS-SDK 实现教程&#xff1a;定位、地图选点、地址解析等 适用地点选择、地址显示、表单填写等场景&#xff0c;全面支持移动端、手机浏览器和 PC端环境 一、创建应用&Key 前端&#xff08;JS-SDK、地图组件&#xff09; 登陆 高德开放平台创建应用&#xff0c;…...

【信息系统项目管理师】高分论文:论信息系统项目的整合管理(银行数据仓库项目)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 正文一、制定项目章程二、制定项目管理计划三、指导和管理项目的实施四、管理项目知识五、监控项目工作六、实施整体变更控制七、结束项目或阶段正文 2023年6月,我以项目经理的身份,参加了 xx银行xx省分行数…...

dev中使用auto的方法

在dev-c中使用新特性是一样的道理&#xff0c;在他启动编译器来编译代码的时候我们让他加上这个参数就行了&#xff0c;设置方法是:在Tools里面找到Compiler Options打开它&#xff0c;然后把那个Add the following commands when calling compiler:选上勾&#xff0c;在里面加…...

C 语言中经典的数据结构

在 C 语言中&#xff0c;经典的数据结构通常包括以下几种&#xff0c;每种都有其特定的应用场景和实现方式&#xff1a; 1. 数组&#xff08;Array&#xff09; 定义&#xff1a;连续内存空间存储相同类型的数据。 特点&#xff1a;随机访问快&#xff08;O(1)&#xff09;&am…...

小白学习java第12天(下):网络编程

上面我们了解TCP就是三次握手&#xff01;&#xff01;&#xff01; 下面我们就详细介绍一下UDP&#xff0c;就是进行发包&#xff08;TCP协议类似于就是打电话&#xff0c;你必须进行连接才能进行传输&#xff0c;但是UDP类似于发消息&#xff0c;连不连接我都是可以的&#…...

论文精度:双分支图Transformer网络:视频驱动的3D人体网格重建新突破

论文地址:https://arxiv.org/pdf/2412.01179 目录 一、背景与问题定义 1.1 3D人体网格重建的意义 1.2 现有方法的困境 二、核心创新:DGTR网络架构 2.1 整体框架设计 2.2 全局运动感知分支(GMA) 2.3 局部细节优化分支(LDR) 2.3.1 局部信息聚合 2.3.2 调制图卷积…...

华三IRF堆叠技术

IRF&#xff08;Intelligent Resilient Framework&#xff0c;智能弹性架构&#xff09;是华三通信&#xff08;H3C&#xff09;自主研发的网络设备虚拟化技术&#xff0c;通过将多台物理设备整合为单一逻辑设备&#xff0c;实现统一管理、高可靠性和灵活扩展。以下是其核心要点…...

第一阶段补充知识

目录 书写脚本使用的相关知识&#xff1f; 备份和冗灾的区别&#xff1f;什么叫DD备份&#xff0c;什么叫DD冗灾&#xff1f; 关于Linux系统优化以及Linux的安全加固&#xff1f; 系统优化 硬件系统优化&#xff1a; 内核参数优化&#xff1a; 网络性能优化&#xff1a; 进程管…...

STM32 HAL库 L298N电机驱动模块实现

一、引言 在机器人、自动化设备等众多应用场景中&#xff0c;电机驱动是一个关键的部分。L298N 是一款常用的电机驱动模块&#xff0c;它可以驱动两个直流电机或一个步进电机。STM32F407 是一款高性能的 ARM Cortex-M4 内核微控制器&#xff0c;结合 HAL 库可以方便地实现对 L…...

Redis的Key的过期策略

我们都知道Redis的键值对是可以设置过期时间的&#xff0c;那么就会涉及到一个问题&#xff0c;Redis到底是如何做到响应快的同时有能快速地释放掉过期的键值对的呢&#xff1f;不卖关子了&#xff0c;直接说答案&#xff0c;那就是Redis两个策略&#xff1a;定期删除和惰性删除…...

ubuntu桌面版使用root账号进行登录

这里写自定义目录标题 第一步&#xff1a;给root账户设置密码&#xff0c;并切换至root账户第二步&#xff1a;注释gdm-autologin文件内的相关内容第三步&#xff1a;注释gdm-password文件内的相关内容第四步&#xff1a;重启系统即可使用root账户登录 第一步&#xff1a;给roo…...

贪心算法(18)(java)距离相等的条形码

在一个仓库里&#xff0c;有一排条形码&#xff0c;其中第 i 个条形码为 barcodes[i]。 请你重新排列这些条形码&#xff0c;使其中任意两个相邻的条形码不能相等。 你可以返回任何满足该要求的答案&#xff0c;此题保证存在答案。 示例 1&#xff1a; 输入&#xff1a;barco…...

CentOS服务器能ping通却无法yum install:指定镜像源解决

文章目录 前言一、问题记录二、解决过程1.修改DNS无效2.指定镜像源 总结 前言 今天有一个项目现场要在一个远程centos服务器上部署产品服务&#xff0c;发现能ping通百度&#xff0c;但是无法yum install 安装基础软件包&#xff0c;开始以为DNS服务器的问题&#xff0c;结果配…...

WebSocket与MQTT

在物联网&#xff08;IoT&#xff09;领域&#xff0c;​WebSocket和MQTT确实都可以实现实时通信&#xff0c;但它们的核心设计目标、适用场景和角色存在显著差异。以下是两者的对比分析&#xff1a; ​1. 协议设计初衷​ ​WebSocket​ ​目标​&#xff1a;提供浏览器与服务器…...

【论文解读】MSVM-UNet: Multi-Scale Vision Mamba UNet for Medical Image Segmentation

MSVM-UNet: Multi-Scale Vision Mamba UNet for Medical Image Segmentation 论文链接&#xff1a; https://arxiv.org/abs/2408.13735 Code&#xff1a; https://github.com/gndlwch2w/msvm-unet 来源&#xff1a; 2024 IEEE International Conference on Bioinformatics an…...

Vue表单组件el-form校验规则rules,条件判断rules表单验证显示必填或非必填

在使用 Element UI&#xff08;一个基于 Vue 的前端框架&#xff09;的表单验证功能时&#xff0c;你可能想要实现一个规则&#xff0c;使得某些字段在特定条件下成为必填项&#xff0c;或者在满足某些条件时不允许为空。这通常通过自定义校验规则来实现。 <template>&l…...

手动关闭ArcGIS与ArcGIS Online连接的方法

【关闭软件启动时ArcGIS与ArcGIS Online连接方法】 打开C盘找到文件夹“C:\Program Files (x86)\Common Files\ArcGIS\bin”&#xff0c;如下图&#xff0c;删除“ArcGISConnection.exe”与“ArcGISConnectionTest.exe”文件&#xff0c;软件下次启动的时候就不会建立与ArcGIS …...

(二十五)安卓开发一个完整的登录页面-支持密码登录和手机验证码登录

下面将详细讲解如何在 Android 中开发一个完整的登录页面&#xff0c;支持密码登录和手机验证码登录。以下是实现过程的详细步骤&#xff0c;从布局设计到逻辑实现&#xff0c;再到用户体验优化&#xff0c;逐步展开。 1. 设计登录页面布局 首先&#xff0c;我们需要设计一个用…...

【过程控制系统】PID算式实现,控制系统分类,工程应用中控制系统应该注意的问题

目录 1-1 试简述过程控制的发展概况及各个阶段的主要特点。 1-2 与其它自动控制相比&#xff0c;过程控制有哪些优点&#xff1f;为什么说过程控制的控制过程多属慢过程&#xff1f; 1-3 什么是过程控制系统&#xff0c;其基本分类是什么&#xff1f; 1-4 何为集散控制系统…...

测试第三课-------自动化测试相关

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…...

关于数据清洗和数据处理实践学习笔记

一些可能想要知道的&#xff1a; pandas是一个模板&#xff0c;它读取的数据都是dataframe的格式&#xff0c;即df Matplotlib是一个用于数据可视化的Python库&#xff0c;能够生成各种静态、动态和交互式图表 pyplot&#xff1a;Matplotlib 的核心接口模块&#xff0c;提供类…...

ubuntu学习day2

linux常用命令 3.文件查看及处理命令 3.1查看文件内容 cat[选项][文件] -b 对非空输出行编号 -E 在每行结束处显示$ -n 对输出的所有行编号 -s 不输出多行空行 标准输入、标准输出和标准错误 在 Linux 中&#xff0c;每个进程默认有三个文件描述符&#xff1a; 标准输入&…...

JavaScript `new Date()` 方法移动端 `兼容 ios`,ios环境new Date()返回NaN

在 iOS 环境下&#xff0c;new Date() 方法会返回 NaN&#xff0c;这通常是由于时间字符串的格式问题。iOS 的 Date 构造函数对时间字符串的格式要求比其他平台更严格。 原因&#xff1a;ios端不兼容“-”为连接符的时间。 解决办法&#xff1a; 替换时间格式 IOS 不支持某…...

考研408参考用书:计算机组成原理(唐朔飞)介绍,附pdf

我用夸克网盘分享了「《计算机组成原理》第2,3版 唐朔飞」&#xff0c; 链接&#xff1a;https://pan.quark.cn/s/6a87d10274a3 1. 书籍定位与适用对象 定位&#xff1a;计算机组成原理是计算机科学与技术、软件工程等专业的核心基础课程&#xff0c;涉及计算机硬件的底层工作原…...

案例-索引对于并发Insert性能优化测试

前言 最近因业务并发量上升&#xff0c;开发反馈对订单表Insert性能降低。应开发要求对涉及Insert的表进行分析并提供优化方案。   一般对Insert 影响基本都在索引&#xff0c;涉及表已按创建日期做了分区表&#xff0c;索引全部为普通索引未做分区索引。 优化建议&#xff…...

@Async 为什么要自定义线程池,使用默认线程池风险

为什么要自定义线程池而非使用默认线程池 使用Spring的Async注解时&#xff0c;如果不自定义线程池而使用默认线程池&#xff0c;可能会带来一些风险和问题。以下是主要原因&#xff1a; 默认线程池的风险 无限制的资源消耗 默认线程池使用SimpleAsyncTaskExecutor&#xff0…...

Spark-SQL简介与编程

1. Spark-SQL是什么 Spark SQL 是 Spark 用于结构化数据(structured data)处理的 Spark 模块。 Hadoop与Spark的对比 Hadoop的局限性 Hadoop无法处理结构化数据&#xff0c;导致一些项目无法推进。 例如&#xff0c;MySQL中的数据是结构化的&#xff0c;Hadoop无法直接处理。…...

如何分析 JVM OOM 内存溢出 Dump 快照日志

文章目录 1、需求背景2、OOM 触发3、Dump 日志分析 1、需求背景 企业开发过程中&#xff0c;如果系统服务客户量比较大&#xff0c;偶尔会出现OOM内存溢出问题&#xff0c;导致服务发生宕机&#xff0c;停止对外提供访问。 这种情况就需要排查定位内存溢出的原因&#xff08;…...

系统监控 | 简易多个内网服务器的CPU和内存使用率监控 system_moniter

效果图 原理 一台主机A上运行mysql数据库&#xff0c;接收数据。 其他主机设置定时任务&#xff0c;每6分钟发送一次自己的CPU和内存使用百分数到主机A。 主机A上提供flask为后台的可视化网页&#xff0c;见上图。 源码库 https://github.com/BioMooc/system_moniterhttps:/…...

【神经网络】python实现神经网络(四)——误差反向传播的基础理论

一.反向传播 本章将介绍能够高效计算权重参数的梯度的方法——误差反向传播法,这里简单介绍一下什么是反向传播,加入有个函数y = f(x),那么它的反向传播为图下这个样子: 反向传播的计算顺序是,将输入信号E乘以节点的局部导数,然后将结果传递给下一个节点。这里所…...

Django 开发服务器

$ python manage.py runserver $ python manage.py runserver 666 # 用 666 端口 $ python manage.py runserver 0.0.0.0:8000 # 让局域网内其他客户端也可访问 $ python manage.py runserver --skip-checks # 跳过检查自动检查 $ python manage.py runserver --…...

嵌入式基础(二)ARM基础

嵌入式基础&#xff08;二&#xff09;ARM基础 1.精简指令集和复杂指令集的区别⭐⭐⭐ 精简指令集 (RISC) 精简指令集 (Reduced Instruction Set Computing) 具有简洁、精简的指令集&#xff0c;每条指令执行的操作都很基础&#xff0c;使得处理器设计更简单。RISC 处理器通…...

RNA免疫共沉淀测序(RIP-seq)

技术简介 RNA免疫共沉淀测序&#xff08;RNA Immunoprecipitation Sequencing, RIP-seq&#xff09;是一种将RNA免疫共沉淀&#xff08;RIP&#xff09;与二代测序技术&#xff08;NGS&#xff09;相结合&#xff0c;用于研究细胞内RNA与蛋白相互作用的技术。 技术原理 利用目…...

期指跌对股市的影响是什么?

国内股指期货对大盘的影响&#xff0c;这种一般就是不想再买这种指数&#xff0c;大多数都在蓝筹股方面&#xff0c;题材股很少&#xff0c;股指期货是保证金交易&#xff0c;一手大概在15-18W&#xff0c;它的价格是根据指数&#xff08;如上证指数、深证成指&#xff09;来确…...

基于Python的​​LSTM、CNN中文情感分析系统

大家好&#xff0c;我是徐师兄&#xff0c;一个有着7年大厂经验的程序员&#xff0c;也是一名热衷于分享干货的技术爱好者。平时我在 CSDN、掘金、华为云、阿里云和 InfoQ 等平台分享我的心得体会。 &#x1f345;文末获取源码联系&#x1f345; 2025年最全的计算机软件毕业设计…...

Neovim安装及lazy配置

安装neovim 官网下载 配置lazy插件总成 lazy官网 一般在C盘里会有一个nvim-data&#xff0c;然后用官网里的命令会生成一个nvim 安装C编译器 参考此文 插件都放在目录’C:\Users\wnlea\AppData\Local\nvim\lua\plugins’中&#xff0c;所以新建一个插件&#xff0c;起名为vi…...

什么叫“架构”

我们学硬件架构的时候常常被一些名词和概念绕晕&#xff0c;这篇就来讲一讲“架构”这个概念&#xff0c;一种“架构”指的是什么&#xff0c;如何去学习一种新的架构。 1.架构&#xff1a;硬件设计与指令集的统一体 这里放上我大二下的手写笔记&#xff1a; 就是说硬件设计…...

【Python浅拷贝与深拷贝详解】

目录 前言&#xff1a;技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解关键技术模块技术选型对比 二、实战演示环境配置要求核心代码实现&#xff08;10个案例&#xff09;案例1&#xff1a;列表嵌套列表案例2&#xff1a;字典嵌套列表案例3…...

numpy.ma.masked_where:屏蔽满足条件的数组

1.函数功能 屏蔽满足条件的数组内容&#xff0c;返回值为掩码数组 2.语法结构 np.ma.masked_where(condition, a, copyTrue)3. 参数 参数含义condition屏蔽条件a要操作的数组copy布尔值&#xff0c;取值为True时&#xff0c;结果复制数组(原始数据不变)&#xff0c;否则返回…...

力扣hot100_技巧_python版本

一、136. 只出现一次的数字 思路&#xff1a; 任何数和 0 做异或运算&#xff0c;结果仍然是原来的数&#xff0c;即 a⊕0a。任何数和其自身做异或运算&#xff0c;结果是 0&#xff0c;即 a⊕a0。异或运算满足交换律和结合律&#xff0c;即 a⊕b⊕ab⊕a⊕ab⊕(a⊕a)b⊕0b。 代…...

用队列实现栈

队列实现栈 用队列实现栈一、队列数据结构的基础定义与操作&#xff08;一&#xff09;队列节点与队列结构体定义&#xff08;二&#xff09;队列大小计算函数&#xff08;三&#xff09;队列初始化函数&#xff08;四&#xff09;队列销毁函数&#xff08;五&#xff09;队列元…...

Android WebView深度性能优化方案

一、启动阶段优化 预初始化策略 冷启动优化&#xff1a;在Application或后台线程提前初始化WebView new Thread(() -> {WebView preloadWebView new WebView(getApplicationContext());preloadWebView.loadUrl("about:blank"); }).start();WebView复用池 private…...

国标GB28181视频平台EasyCVR打造线下零售平台视频+AI全流程监管坚实防线

一、背景概述 在全球经济增长放缓、电商崛起、经营成本攀升的形势下&#xff0c;零售行业正经历深刻变革。数字化转型成为新零售发展的必由之路&#xff0c;但多数零售企业在信息化建设上困难重重&#xff0c;既缺乏足够重视&#xff0c;又因过高投入而犹豫。 随着大数据、人工…...

QML中打印Item的坐标

在 QML 中&#xff0c;你可以通过多种方式获取和打印 Item 的坐标信息。以下是几种常见的方法&#xff1a; 1. 打印相对坐标&#xff08;相对于父项&#xff09; qml Item {id: myItemx: 50y: 100width: 200height: 200Component.onCompleted: {// 打印相对于父项的坐标cons…...

基于【Lang Chain】构建智能问答系统的实战指南

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《深度探秘&#xff1a;AI界的007》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是Lang Chain 2、LangChain在问答系统中的核心优…...

Vue使用axios实现:上传文件、下载文件

Vue 使用 axios 框架,系列文章: 《Vue使用axios实现Ajax请求》 《Vue使用axios二次封装、解决跨域问题》 《Vue使用axios实现:上传文件、下载文件》 在实际开发过程中,浏览器通常需要和服务器端进行数据交互。而 Vue.js 并未提供与服务器端通信的接口。Axios 提供了一些方便…...

泊松分布详解:从理论基础到实际应用的全面剖析

泊松分布详解&#xff1a;从理论基础到实际应用的全面剖析 目录 引言&#xff1a;事件的罕见性与随机计数泊松分布的历史源流泊松分布的数学定义与性质 概率质量函数 (PMF)累积分布函数 (CDF)期望、方差与其他矩矩生成函数 (MGF) 与特征函数 (CF) 泊松分布的严格推导 极限推导…...