From 3557013fe867b059f7b8f7f102bdbe58fc8c67ba Mon Sep 17 00:00:00 2001 From: Luojiale <2272532647@qq.com> Date: Mon, 1 Jun 2026 09:30:26 +0800 Subject: [PATCH] Add w5 to w11 projects: shape inheritance, animal interface, exception handling, generics, maven project, repository with analyze command, exception hierarchy with retry --- w10/ARCHITECTURE_AUDIT.md | 135 ++++++++++++++ w10/AnalyzeCommand.class | Bin 0 -> 3608 bytes w10/AnalyzeCommand.java | 85 +++++++++ w10/Article.class | Bin 0 -> 1891 bytes w10/Article.java | 65 +++++++ w10/ArticleAnalyzer.class | Bin 0 -> 311 bytes w10/ArticleAnalyzer.java | 21 +++ w10/ArticleCountAnalyzer.class | Bin 0 -> 853 bytes w10/ArticleCountAnalyzer.java | 18 ++ w10/ArticleRepository.class | Bin 0 -> 2716 bytes w10/ArticleRepository.java | 73 ++++++++ w10/TestMain.class | Bin 0 -> 3579 bytes w10/TestMain.java | 95 ++++++++++ w10/TitleLengthAnalyzer.class | Bin 0 -> 2450 bytes w10/TitleLengthAnalyzer.java | 44 +++++ w11/ExceptionDemo.class | Bin 0 -> 2853 bytes w11/ExceptionDemo.java | 54 ++++++ w11/NetworkException.class | Bin 0 -> 336 bytes w11/NetworkException.java | 9 + w11/README.md | 48 +++++ w11/RetryUtils.class | Bin 0 -> 1964 bytes w11/RetryUtils.java | 36 ++++ w11/UrlFormatException.class | Bin 0 -> 604 bytes w11/UrlFormatException.java | 17 ++ w11/UrlValidator.class | Bin 0 -> 731 bytes w11/UrlValidator.java | 16 ++ w5/Circle.class | Bin 0 -> 382 bytes w5/Circle.java | 6 + w5/Rectangle.class | Bin 0 -> 388 bytes w5/Rectangle.java | 6 + w5/Shape.class | Bin 0 -> 391 bytes w5/Shape.java | 5 + w5/ShapeTest.class | Bin 0 -> 727 bytes w5/ShapeTest.java | 24 ++- w6/Animal.class | Bin 0 -> 206 bytes w6/Animal.java | 3 + w6/AnimalTest.class | Bin 0 -> 771 bytes w6/AnimalTest.java | 27 ++- w6/Cat.class | Bin 0 -> 391 bytes w6/Cat.java | 6 + w6/Dog.class | Bin 0 -> 484 bytes w6/Dog.java | 11 ++ w6/Swimmable.class | Bin 0 -> 120 bytes w6/Swimmable.java | 3 + w7/Account.class | Bin 0 -> 1095 bytes w7/Account.java | 19 ++ w7/AccountTest.class | Bin 0 -> 1490 bytes w7/AccountTest.java | 24 +++ w7/InsufficientFundsException.class | Bin 0 -> 830 bytes w7/InsufficientFundsException.java | 12 ++ w8/Box.class | Bin 0 -> 1289 bytes w8/Box.java | 26 +++ w8/GenericDemo.class | Bin 0 -> 2601 bytes w8/GenericDemo.java | 51 ++++++ w8/GenericUtils.class | Bin 0 -> 1456 bytes w8/GenericUtils.java | 24 +++ w8/Pair.class | Bin 0 -> 1442 bytes w8/Pair.java | 30 ++++ w8/README.md | 54 ++++++ w9/README.md | 127 +++++++++++++ w9/RUNNING.md | 167 ++++++++++++++++++ w9/pom.xml | 40 +++++ w9/src/main/java/com/crawler/Main.java | 43 +++++ .../java/com/crawler/command/Command.java | 6 + .../com/crawler/command/CrawlCommand.java | 36 ++++ .../java/com/crawler/command/ExitCommand.java | 24 +++ .../java/com/crawler/command/HelpCommand.java | 21 +++ .../java/com/crawler/command/ListCommand.java | 25 +++ .../crawler/controller/CommandController.java | 39 ++++ .../main/java/com/crawler/model/Article.java | 55 ++++++ .../java/com/crawler/view/ConsoleView.java | 60 +++++++ w9/test.bat | 10 ++ 72 files changed, 1694 insertions(+), 6 deletions(-) create mode 100644 w10/ARCHITECTURE_AUDIT.md create mode 100644 w10/AnalyzeCommand.class create mode 100644 w10/AnalyzeCommand.java create mode 100644 w10/Article.class create mode 100644 w10/Article.java create mode 100644 w10/ArticleAnalyzer.class create mode 100644 w10/ArticleAnalyzer.java create mode 100644 w10/ArticleCountAnalyzer.class create mode 100644 w10/ArticleCountAnalyzer.java create mode 100644 w10/ArticleRepository.class create mode 100644 w10/ArticleRepository.java create mode 100644 w10/TestMain.class create mode 100644 w10/TestMain.java create mode 100644 w10/TitleLengthAnalyzer.class create mode 100644 w10/TitleLengthAnalyzer.java create mode 100644 w11/ExceptionDemo.class create mode 100644 w11/ExceptionDemo.java create mode 100644 w11/NetworkException.class create mode 100644 w11/NetworkException.java create mode 100644 w11/README.md create mode 100644 w11/RetryUtils.class create mode 100644 w11/RetryUtils.java create mode 100644 w11/UrlFormatException.class create mode 100644 w11/UrlFormatException.java create mode 100644 w11/UrlValidator.class create mode 100644 w11/UrlValidator.java create mode 100644 w5/Circle.class create mode 100644 w5/Circle.java create mode 100644 w5/Rectangle.class create mode 100644 w5/Rectangle.java create mode 100644 w5/Shape.class create mode 100644 w5/Shape.java create mode 100644 w5/ShapeTest.class create mode 100644 w6/Animal.class create mode 100644 w6/Animal.java create mode 100644 w6/AnimalTest.class create mode 100644 w6/Cat.class create mode 100644 w6/Cat.java create mode 100644 w6/Dog.class create mode 100644 w6/Dog.java create mode 100644 w6/Swimmable.class create mode 100644 w6/Swimmable.java create mode 100644 w7/Account.class create mode 100644 w7/Account.java create mode 100644 w7/AccountTest.class create mode 100644 w7/AccountTest.java create mode 100644 w7/InsufficientFundsException.class create mode 100644 w7/InsufficientFundsException.java create mode 100644 w8/Box.class create mode 100644 w8/Box.java create mode 100644 w8/GenericDemo.class create mode 100644 w8/GenericDemo.java create mode 100644 w8/GenericUtils.class create mode 100644 w8/GenericUtils.java create mode 100644 w8/Pair.class create mode 100644 w8/Pair.java create mode 100644 w8/README.md create mode 100644 w9/README.md create mode 100644 w9/RUNNING.md create mode 100644 w9/pom.xml create mode 100644 w9/src/main/java/com/crawler/Main.java create mode 100644 w9/src/main/java/com/crawler/command/Command.java create mode 100644 w9/src/main/java/com/crawler/command/CrawlCommand.java create mode 100644 w9/src/main/java/com/crawler/command/ExitCommand.java create mode 100644 w9/src/main/java/com/crawler/command/HelpCommand.java create mode 100644 w9/src/main/java/com/crawler/command/ListCommand.java create mode 100644 w9/src/main/java/com/crawler/controller/CommandController.java create mode 100644 w9/src/main/java/com/crawler/model/Article.java create mode 100644 w9/src/main/java/com/crawler/view/ConsoleView.java create mode 100644 w9/test.bat diff --git a/w10/ARCHITECTURE_AUDIT.md b/w10/ARCHITECTURE_AUDIT.md new file mode 100644 index 0000000..7ba18e4 --- /dev/null +++ b/w10/ARCHITECTURE_AUDIT.md @@ -0,0 +1,135 @@ +# AI 架构审计:策略模式解耦与封装审查 + +## 一、类签名汇总 + +### 1. Article(数据模型) +```java +public class Article { + private String title; + private String url; + private String content; + private long timestamp; + + public Article(String title, String url) // 防御性参数校验 + public void setTitle(String title) // 防御性参数校验 + public void setUrl(String url) // 防御性参数校验 + // Getter方法 +} +``` + +### 2. ArticleRepository(仓储层) +```java +public class ArticleRepository { + private final List
articles; + + public void add(Article article) // 防御 null + public void addAll(Collection
) // 防御 null 和空集合 + public List
getAll() // 返回不可变视图 + public Article findByTitle(String) // 防御 null + public int count() + public void clear() + public boolean contains(Article) // 防御 null +} +``` + +### 3. ArticleAnalyzer(策略接口) +```java +public interface ArticleAnalyzer { + Map analyze(List
articles); + String getName(); +} +``` + +### 4. ArticleCountAnalyzer(具体策略1) +```java +public class ArticleCountAnalyzer implements ArticleAnalyzer { + public Map analyze(List
); + public String getName(); +} +``` + +### 5. TitleLengthAnalyzer(具体策略2) +```java +public class TitleLengthAnalyzer implements ArticleAnalyzer { + public Map analyze(List
); + public String getName(); +} +``` + +### 6. AnalyzeCommand(命令类) +```java +public class AnalyzeCommand { + private final List
articles; + private final List analyzers; + + public AnalyzeCommand(List
, List); + public void execute(); // 复用策略解析,不存储结果 + public void addAnalyzer(ArticleAnalyzer); +} +``` + +## 二、AI 架构审计请求 + +> 作为 Java 架构师,请审查我的代码设计: +> +> 1. **策略解耦**: +> - ArticleAnalyzer 接口是否定义清晰? +> - 具体策略(ArticleCountAnalyzer、TitleLengthAnalyzer)是否与上下文解耦? +> - 添加新策略是否无需修改现有代码? +> +> 2. **封装性**: +> - ArticleRepository 的内部状态是否得到保护? +> - getAll() 返回不可变视图是否正确? +> - 防御性编程是否过度或不足? +> +> 3. **单一职责**: +> - AnalyzeCommand 是否只负责协调执行,不承担存储职责? +> - 各分析器是否只关注单一分析维度? +> +> 4. **依赖注入**: +> - 依赖是否通过构造函数注入? +> - 是否符合依赖倒置原则? +> +> 请给出改进建议和优化方案。 + +## 三、设计原则应用 + +### 已应用的设计原则 + +| 原则 | 应用位置 | 说明 | +|------|---------|------| +| 单一职责 | ArticleAnalyzer 接口 | 每个分析器只负责一种分析 | +| 开闭原则 | 策略模式 | 添加新分析器无需修改现有代码 | +| 依赖倒置 | AnalyzeCommand | 依赖抽象接口而非具体实现 | +| 里氏替换 | 策略实现 | 所有分析器可互换使用 | + +### 防御性编程 + +| 防御点 | 位置 | 方式 | +|--------|------|------| +| null检查 | Article构造器 | IllegalArgumentException | +| null检查 | Repository.add() | Objects.requireNonNull | +| null检查 | Repository.addAll() | 遍历检查每个元素 | +| 不可变返回 | Repository.getAll() | Collections.unmodifiableList | + +## 四、文件列表 + +- [Article.java](file:///D:\嘻嘻哈哈\Git\java\w10\Article.java) +- [ArticleRepository.java](file:///D:\嘻嘻哈哈\Git\java\w10\ArticleRepository.java) +- [ArticleAnalyzer.java](file:///D:\嘻嘻哈哈\Git\java\w10\ArticleAnalyzer.java) +- [ArticleCountAnalyzer.java](file:///D:\嘻嘻哈哈\Git\java\w10\ArticleCountAnalyzer.java) +- [TitleLengthAnalyzer.java](file:///D:\嘻嘻哈哈\Git\java\w10\TitleLengthAnalyzer.java) +- [AnalyzeCommand.java](file:///D:\嘻嘻哈哈\Git\java\w10\AnalyzeCommand.java) +- [TestMain.java](file:///D:\嘻嘻哈哈\Git\java\w10\TestMain.java) + +## 五、运行方式 + +```bash +# 编译 +javac Article.java ArticleRepository.java ArticleAnalyzer.java \ + ArticleCountAnalyzer.java TitleLengthAnalyzer.java \ + AnalyzeCommand.java TestMain.java + +# 运行 +java TestMain +``` \ No newline at end of file diff --git a/w10/AnalyzeCommand.class b/w10/AnalyzeCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..98334d5ee9f9e24e7e0f4cc492f68b5add984037 GIT binary patch literal 3608 zcmb7GYj73i8GgQ#%Wh6KKsE#j0wx+D$vF@~YBh&MV+0dOqUF#;uosrFB!`^r#&hu{YZLDS*n(dN5kQuLYz;Zc6^$ly&;^QSl&DW6jDhA@(g~svi>1lMw8oe3s@Y)2(_>is1XLs8#5&?8ty<)%=kkyOB9eBchEjg?@Kj|ot_)i^vee&qxeB6c&3N~uE9h(T)4Du2af$U`Lpy>uulg{wF zL&Fzwr@+!4qnoH&9fs2$bGrDKbPUTOti=^gnU43>(IIe$iU`42cY}}AuvSAI?xy=< zj+rnVJ0Y;7!WZK@u*qMK+e4 z7oS9O->KotXd`z&A_dAaqu-6(Oe9*2UV`utb}87c;bDA5pkM|B?h-}QauNeklK>Q^ zEp<`k5^2ANS_NO9MaB&UMmiGRv*c91Me z)X|VapFqBM)Lowgd75>Gv}rG8p8di4nSA?972g#II<{lP8|;+jsQ4ZoX!J2#I?ZO&>U6qP zJW2R^jQ;fWketS>^z^WR=GBL(!HZMJMpXQO`7=3we)8nd)ajwCub#Mi?1RaPH&i^s z^{JNzIi0aEOm1wWkS^!x18-Z&M0p6RxF{l@*$p5q!`zzGI|=Q4r2=1Gu; zGqC@B2g=*=MGZfa8L2w9=Qb55Suv*Xomm&4tS_fDoR+bfW$fc6kr$11T85KKFqLoe zgihZQo16U#5UFD^yY=&S)AhkSbzQ>FA>+kGFr*(@VVYx>*_!IvYbJIY1WKTOE_tR7 zb2v?JWhfw-D`vmhk#fkbAnF($-OOH}dp=E0AC{FXZC`rL)h!scQ;809YfS1>UvpS=;3>9+etbJqqoI$y6k;P6(hV2{P=ad4+hoMp=WDKg^G7%Xr_Zt#V}Mt90J?0Bcp<;y&R*r5JmfwTJbEa;Un9!d;;YD0&Y|-2brqzv68k7kGo; z<;dl?h%6Q(AH@{4l){#w3MIbyGdy{gr-T$HQamXs{{9Yb)>n^WtsK|+qd%{D7fZ%b zEii#E0Nx(gR`_dorPuTt?s*%V1)4+SxJO_ZPw;1}z-4S}A@lG!nkApbXBeL7cQe~6g}>Y_w6W>6ZnQJ z;LK+i&V1G{jXLNX^{ou!ByIs0w{1iUxEU(D&qZ|IGTeeNJJ5Sj&Q4_wOsqv8yTr%X zA-=|)^H_)9qZ)r?G5ZrXVv_CLU$6;(BSb$Tr2Hg-7w7bDZ!o_-6n1qYVc*HvVpFq=RG4N1cBSaSUJk^ZkF| z*cR3wBN)S|&Z;|us<8gJJ1fiPY(-dq!krc6aJC?<54p3P+`5%-l9VCgJu=o-@h#(9 z&UdZ;{Sp1C5&h{A{n?Sw2vbTJ#ovCUqh5%rgAk f{l4vf&%59EaEbdm1?*vS_kM03KEQ|g6-xdKSw-e4 literal 0 HcmV?d00001 diff --git a/w10/AnalyzeCommand.java b/w10/AnalyzeCommand.java new file mode 100644 index 0000000..59191db --- /dev/null +++ b/w10/AnalyzeCommand.java @@ -0,0 +1,85 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 分析命令类 + * 复用策略解析但不存储,只输出统计信息 + */ +public class AnalyzeCommand { + private final List
articles; + private final List analyzers; + + /** + * 构造函数,接收文章列表和分析器列表 + * @param articles 要分析的文章列表 + * @param analyzers 分析器列表 + */ + public AnalyzeCommand(List
articles, List analyzers) { + Objects.requireNonNull(articles, "文章列表不能为null"); + Objects.requireNonNull(analyzers, "分析器列表不能为null"); + + this.articles = new ArrayList<>(articles); + this.analyzers = new ArrayList<>(analyzers); + } + + /** + * 执行分析并输出结果(不存储) + */ + public void execute() { + System.out.println("================================================"); + System.out.println(" 文章分析报告"); + System.out.println("================================================"); + System.out.println("分析文章数量: " + articles.size()); + System.out.println("分析器数量: " + analyzers.size()); + System.out.println("------------------------------------------------"); + + for (ArticleAnalyzer analyzer : analyzers) { + System.out.println("\n【" + analyzer.getName() + "】"); + Map result = analyzer.analyze(articles); + + // 输出分析结果(不存储) + for (Map.Entry entry : result.entrySet()) { + if (!"analyzer".equals(entry.getKey())) { + System.out.println(" " + formatKey(entry.getKey()) + ": " + formatValue(entry.getValue())); + } + } + } + + System.out.println("\n================================================"); + System.out.println(" 分析报告结束"); + System.out.println("================================================"); + } + + /** + * 格式化键名 + */ + private String formatKey(String key) { + switch (key) { + case "totalCount": return "文章总数"; + case "averageLength": return "平均标题长度"; + case "maxLength": return "最长标题长度"; + case "minLength": return "最短标题长度"; + default: return key; + } + } + + /** + * 格式化值 + */ + private String formatValue(Object value) { + if (value instanceof Double) { + return String.format("%.2f", value); + } + return value.toString(); + } + + /** + * 添加分析器 + */ + public void addAnalyzer(ArticleAnalyzer analyzer) { + Objects.requireNonNull(analyzer, "分析器不能为null"); + analyzers.add(analyzer); + } +} \ No newline at end of file diff --git a/w10/Article.class b/w10/Article.class new file mode 100644 index 0000000000000000000000000000000000000000..c68a2dd6f3621f2cdff335407ee47e4e6619a84c GIT binary patch literal 1891 zcma)7-%}e^6#g!RY_cxDB1kFaM=6CQq@Yz>F@;JaSTMBOP#pDX2|IMlW;e5Y(~)s} z^1&zH+c(EIADqz{X&D^#%?JOEjz9hh^}D-iNFX{L*mLha_ndRT^PO{Ve*63AX8F7YGhM0ja#0C29n-9!+$8>k+Z*SkX%2FVH&30|MB+!}8Z0SfKsX;gJ0*n^(lJsqN zS0Ki*A~2B76pv#?n|bBkW1ts(0-7DHRH|}6jQf#>l)&V%!c8mckdLN z-I5r=ISr!*#xPDy%f7VBjwR3~ZK=l8=}^k&yn#vO&{^{x9aFfdVcNh9(yf&22ht)J z{pFhPGmA~TVr|%tV+Uc<>nc*#z$|hCNy*JXnw6@62zTZT%!i=Lo-65{V!>W9a2c-( z3{}iMtKhk1Q+{B}dz{hLm5jU4Ci&AjthXtSz|3>`X0}-5g119nEFst=-V%K8YA(e@sF^yAu*LT=k)P)nB8fKyL6ws+{EQ~NhgBv4^(pbjQ z>k3aode8b#FL^b;Y^~Y~j!`e@oa!vr=7#6VK>B8N!;<&Bogjf#fpp#I)3D}?Gh!w; z!MaA1Mp+icXT5RU)Uk$j4ezQ#6a}V^Th?|Tczf1-gd?iWs#%tv&lZ2!%xv8-f^gGx zcN{C2Dtg{tt(rewswPc~APCz)=TpA6}z;ovTOa zsDMv6$tO^yG z>`@@zYzt!IB#3dE=AR2hgSv^6ATAKZ6hTZA#7ql_Yi&VXIte065Q_~EljK1$5my;s zQinyXsh=cZj$JTM%@Wd-in&qhpOKvTwqYKTe3@I<@mAe*vfeiF?0CKG&7rR_^i^6e yH&ACurwW}?_2W0eFLQ*Kfs-lZ8Tke+J5a#e924|=hu;b|xKhn~3-93$hW`b>>73F4 literal 0 HcmV?d00001 diff --git a/w10/Article.java b/w10/Article.java new file mode 100644 index 0000000..49f40ba --- /dev/null +++ b/w10/Article.java @@ -0,0 +1,65 @@ +public class Article { + private String title; + private String url; + private String content; + private long timestamp; + + public Article(String title, String url) { + if (title == null || title.trim().isEmpty()) { + throw new IllegalArgumentException("标题不能为空"); + } + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("URL不能为空"); + } + this.title = title; + this.url = url; + this.timestamp = System.currentTimeMillis(); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + if (title == null || title.trim().isEmpty()) { + throw new IllegalArgumentException("标题不能为空"); + } + this.title = title; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("URL不能为空"); + } + this.url = url; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return "Article{" + + "title='" + title + '\'' + + ", url='" + url + '\'' + + ", timestamp=" + timestamp + + '}'; + } +} \ No newline at end of file diff --git a/w10/ArticleAnalyzer.class b/w10/ArticleAnalyzer.class new file mode 100644 index 0000000000000000000000000000000000000000..25389bb590406bc95a717d2f3c61302530cb7307 GIT binary patch literal 311 zcmZ8dOAdlC6r6`21U0(w0`7Dp2Y{Hc&=qPTC#a+$H5E-QCVDg%9>7Bx%V$B@b>_{y zdDHLj{Q6@wl%0WmC3PIB1`=SQ{7@*=A4x4J-e9ovqc@> sG!fkRnyZ9wg)B6!lB=RXpFrrCuK+C5xre^Nc7ZT3*nw-_gki~y{*_x*oB#j- literal 0 HcmV?d00001 diff --git a/w10/ArticleAnalyzer.java b/w10/ArticleAnalyzer.java new file mode 100644 index 0000000..aa948a4 --- /dev/null +++ b/w10/ArticleAnalyzer.java @@ -0,0 +1,21 @@ +import java.util.List; +import java.util.Map; + +/** + * 文章分析策略接口 + * 策略模式:定义分析算法的接口,不同的分析算法可以独立变化 + */ +public interface ArticleAnalyzer { + /** + * 分析文章并返回统计结果 + * @param articles 文章列表 + * @return 统计结果映射 + */ + Map analyze(List
articles); + + /** + * 获取分析器名称 + * @return 分析器名称 + */ + String getName(); +} \ No newline at end of file diff --git a/w10/ArticleCountAnalyzer.class b/w10/ArticleCountAnalyzer.class new file mode 100644 index 0000000000000000000000000000000000000000..c18f137ae3be4627286f0d6c1bc983e1825aa662 GIT binary patch literal 853 zcmZuvU279T6g|^TJDbg>Nn=y3wQBuplEy}T5;g=%Q4Az47*Twj#4&E!Zpvo1V1I!1 zOWj4s{s8|+BmN2T?sg=hm4$m}?wmX4?3w%X&$k}{?%P8{?Q@ z$Zm*jQSpS|t*o~J}DoiQ}@3E4cd4k=VAnuVhyS^lRI~3+4J#hM7WrlwBhn6N?P_!Q0(mpFRxU z?ET(-b@=kd;Qi~vcON;53>j_N(5=L3?+FRWG+bYVx zZ^^?njUPjSX3?NbBS^7CyLrlt@_1<<>@zJGmMBKYRHRtOb!v^EO=>kL7r%o)-bb=o z&RL}|un&+k@Ti analyze(List
articles) { + Map result = new HashMap<>(); + result.put("analyzer", getName()); + result.put("totalCount", articles.size()); + return result; + } + + @Override + public String getName() { + return "文章数量分析器"; + } +} \ No newline at end of file diff --git a/w10/ArticleRepository.class b/w10/ArticleRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..4926efc413bb9c06dcfd5a19485b434dc8905816 GIT binary patch literal 2716 zcma)8-%}N37=AuL7nT+9fEpqygm?}J2etgsF%?OaI-m(jQdY}33$84?xMvTE{m%SR zY2#H>(~Z+j7uHP8l!8r7*G>OHw_5oxG)?t9vMGvWITc8draH(_6n586UP-)3e@{fdC#Is-Y=Zp`iw~0xNr6&&*iHF=NKbnV#d$2~_HVEyrBxB94SeRbnN< zfBX2-?>DaBnZNqS`5*4g-?sCXrJ^1U3RY=YjWxtA#swq^R^u9H@}_GHIrb1E1a`#( z_((0Oh-KJwO?xtxh?oMbR5cp0RzZ`7b!aAVF_|REHt5-`z^WzS6JwRwAkY|8aPPB= zcduP8siPV#XjSm2hBj;xXedJWIhMt4n~u#EnV#Y5Y?r|5cu3H1_CRPS#m$H-NNDIl zr$DTTQ*aSbPU*QJxuJj^?T~`43=QX2iS4YWP{&tyuUxo$ z?S`C3P@sy(u|vU54ZEzaN6e3@7=_-e;Q;zrx@X7{Zi^1DR0%%7 zMBesikv!@eSu>-123vj*{R$3gIE(>-HN|2}vXRq-X>m>hyQ4)effy~h1jEnujD|D@ z1*#@YJKJx%l6I*Yj?758=vGR_5gb+UtcGJ4E>T0lg{&O+pp`QO+M}h1ivN#A@SKL@ z7-iFP<~c@EMgwwfoX{{Pw~A$4*}5w&FKRd`mr7;Eu|3_ib1Hi9vW8cn(>Q5(Bc^8& zp;U2U-&JIgRbXhCkQk8-C~zQmu58_+lB$NyNn7{wF0q;xZ615lp=eWkDV&NvCrhW= zv}qed`RQ@P9g*r2(Bk*~62}S6wcIOri^k}%it8o3y`2k}zUZ7ge{lHwTHY8NZt2tGS-mZS)z%}h zehIYuUfrhS@3(I@mwa86WDN8f&lG7f`+q=$7o zYZA9Sd zQ}BVnrU!bmRCx-naC#4SrKp2m@T{rk;SO+1kmoV)lYA?~DOB;9$33qmEj=jb6HwQ2 z3!>wDC?{^A>N}|4__3#Xt)T~?qJoIE(CEDmQ(^RWBFZfFow2%k#6~;5h1$7*N6Ij| z7$C-fgnE)`V5o&@hRO6E?-ji7?2N6K01VzJFt}v_?E;^ol@D74enQVsS8Q7$LsyXD zi+_HVNy;Euu4P5?s*qg^ni0o(Y{v%dz(&~o!fyP98U>sG)XyskzS0%3nj0k+mwIPGUBU=?<%im5%X+vPiBVwx4_t)v0oQhp{bTY*k0+O}umyHfEyFpy#=BI|JG8xyH}Ezt)1uPyK0d@%H2w=)_`m=F literal 0 HcmV?d00001 diff --git a/w10/ArticleRepository.java b/w10/ArticleRepository.java new file mode 100644 index 0000000..471bf69 --- /dev/null +++ b/w10/ArticleRepository.java @@ -0,0 +1,73 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class ArticleRepository { + private final List
articles; + + public ArticleRepository() { + this.articles = new ArrayList<>(); + } + + /** + * 添加单个文章,防御 null + */ + public void add(Article article) { + Objects.requireNonNull(article, "文章不能为null"); + articles.add(article); + } + + /** + * 添加多个文章,防御 null 和空集合 + */ + public void addAll(Collection
articlesToAdd) { + Objects.requireNonNull(articlesToAdd, "文章集合不能为null"); + + for (Article article : articlesToAdd) { + Objects.requireNonNull(article, "集合中包含null文章"); + articles.add(article); + } + } + + /** + * 获取所有文章的不可变视图 + */ + public List
getAll() { + return Collections.unmodifiableList(articles); + } + + /** + * 根据标题查找文章 + */ + public Article findByTitle(String title) { + Objects.requireNonNull(title, "标题不能为null"); + return articles.stream() + .filter(a -> title.equals(a.getTitle())) + .findFirst() + .orElse(null); + } + + /** + * 获取文章数量 + */ + public int count() { + return articles.size(); + } + + /** + * 清空所有文章 + */ + public void clear() { + articles.clear(); + } + + /** + * 检查是否包含指定文章 + */ + public boolean contains(Article article) { + Objects.requireNonNull(article, "文章不能为null"); + return articles.contains(article); + } +} \ No newline at end of file diff --git a/w10/TestMain.class b/w10/TestMain.class new file mode 100644 index 0000000000000000000000000000000000000000..a009fb6f8ab8c7c8bb0f7d932bd0d1212074c80c GIT binary patch literal 3579 zcmaJ@>t7Vt8GeR~Gp^%pqee99#t^x9*~I43K#~?wE4l~)qE(Z&<1)g)?#yO))`Dpo zlT<;x(MAxf@sh+sV#P}vKrnvnhyDls-dW^R|AX4+%q*}gYsKH$Gv~dW^Stl#zUTb& zzvGVp?7)~0dB~ShprR1-BrIvuj%vY(X0`fk8dShq8)}z$2k~CT#avd2~us&{>cB38FwP>Y+MG}hN5&r_IA5K5M(-)|U+eSE| zH|Q}dVc1r@GvJDSSc0W8{3@QqG6~P+@Fqcy3EvTu+m(9zxd-QFhS^eCw$FzZcwWXz z6-5G@(xfNsTFs#TBAe6MJr%5$u-=z_FjI?k9@DF>XjC(sr%$y4YgDWi-u%KvRhE*1 zz|(G~2JSnLZlsP6PCo3P96CMgX1$7+gqxMZ&365WZYGSQdVSn#iEGg)&oAR;2}|dM z$A@B(uTr8<%M_F|5xbc;Zx83h&8dq+3O3Pst8K>;6~Un1p+#d6eM{Ji21O`+D8Uwy z@}Pv09I?w4O$DzKb$zGZYMH6NyQ#CoPWQ>w)m{ZZk+3S)PBB+k!RzVsHHPCx%$~Y6 zFgZS`;7tiDa#^({1je^iRG^Y{(wdtksHLGaS~so5ct^#%_?d*lu$45)H*&MaPexa% z*oJBe&quX(=FSXj_I|@|)T`BqE_G9covt+KNP2V1{K9lOGbQ zgiXDQ_s}3=!4bo3-qzV<*b!aAnjASQn+b`?W|NBdv5zEg(e1R+veFzt75q}d{M422 zQ>Pv|-FN*g|AQiCha_ysxrtkOnRO*j`Jswm;nzgQ-CSh;>AhwnumBnk%LuDz7MWV) zI+_tnC`cH`bU*S@C8K4!#9g@?auG!zjv!w_8=3Mnzf*meSWjY)h+qmEPgW0@PRl_B z7VBUxYn|b%sqO&Sy1&GII3}w8_Y9Mp+>maz z*sZhDPlWU{206PS`EVRxh%*0!gqJe8=Tg*T@k~bC$~umdD!N2tlJyQfoV0ZXrzE_v zqoJX$p(5anUwC}yW~%41(>3lq{M;EE`_IGAWSo|;c9ti5laWZh#m=n9cXWjHn4q|V zvm{vR_BWF^hART8?p~+oTIy`y<8z|{lj?y~f7j%#>z+#m=l`$n)a8>-Z}+tBAIT)q z*cE)qx}9z)qN|E-r=U+lFxS4Gmw%7lcFvq|dT)8h@SMuH$UZ&`sD_2wqD89WEy<{E z+8LlOF|N6sPkuL^y52SQ^?>J)N36D*S;vb*hN(k9Cqa-Ex8m(kJtnW00%Y`Bw;-1?gAW&bBH(|%MSgp?H-JDQx zlPOJ&PB=Z+w@%*aN(aC99NYR-*ZGQoq+pO$lREW)pPrtRCH;<2QrB*>hfGd68(7M`^DvL& zpZT?hzmixjABA*}6A70&3b9a~k1P0!bD`Qm2V!;oeaQZWBUn6!)h#! zvwqe>riIKWpTg&!k591$Jjg=6e-`mbi@CygmFHh7agPODoj~z{QEb>VfszCMGXKU= zY!12eSNvN!uXX3I`QPAtZ~4X%{Iq-%2ku`!g15)`I{TZuzdu(XQX>Q zslRLlbrl6%E-Ls378DhXpm7j|{{7=P;AxEDgNi~SFMNRCNEnCit_y5=Zj3630q_&q zGX5<`fXFrx&#PF4H?W$|fwgGDI)t$v?R=`8zy_S=J^K|(ag~;DBJT-4Mxl&i86`9S zA<0T$fH2UH3p5(M=xmj7lW2hXMGbm!l1S8DR-k|xKVq6_B`zOR!lc`nJY0j zf|)CEzs;Ctt{nAu%&eq$2yL9eF>ducPmf=p@at3WYXZj)lrv$U*D@t1W_hp`Di67d zvD(b&Y~d`(*VikkXS5FEHMHS%bTDcs@fLb{8DBspVe)yK=0*cy3i=YJ$BlByOS!+B zxgLZXVy66OMBXV7+0GK2qafq>q*br~Pcy(aN@>XC(j3zZu4%~u$u-#jwC4ee@1cOe zmSZJ)Na`(w)I$gcBK%FFn@R6aQ|1Wi0RmT0#Uj{7n;o>O=IVA@?jk{IXdj}7-Pnpf z^ioHydeZ1Ue82+K&`7`g=(!0|zSfc?>v0_99v^a-LpVdN3w&1zdaZ- all = repo.getAll(); + System.out.println("获取文章列表大小: " + all.size()); + } + + private static void testAnalyzeCommand() { + // 创建示例文章 + List
articles = new ArrayList<>(); + articles.add(new Article("Java编程入门教程", "https://example.com/java")); + articles.add(new Article("Python数据分析实战", "https://example.com/python")); + articles.add(new Article("JavaScript高级编程", "https://example.com/js")); + articles.add(new Article("Spring Boot指南", "https://example.com/spring")); + articles.add(new Article("Docker容器化部署", "https://example.com/docker")); + + // 创建分析器列表 + List analyzers = new ArrayList<>(); + analyzers.add(new ArticleCountAnalyzer()); + analyzers.add(new TitleLengthAnalyzer()); + + // 创建并执行分析命令 + AnalyzeCommand command = new AnalyzeCommand(articles, analyzers); + command.execute(); + } + + private static void testDefensiveProgramming() { + ArticleRepository repo = new ArticleRepository(); + + try { + repo.add(null); + System.out.println("ERROR: 应该抛出异常!"); + } catch (NullPointerException e) { + System.out.println("正确: 成功捕获 null 文章异常"); + } + + try { + repo.addAll(null); + System.out.println("ERROR: 应该抛出异常!"); + } catch (NullPointerException e) { + System.out.println("正确: 成功捕获 null 集合异常"); + } + + try { + List
listWithNull = new ArrayList<>(); + listWithNull.add(new Article("Test", "https://test.com")); + listWithNull.add(null); + repo.addAll(listWithNull); + System.out.println("ERROR: 应该抛出异常!"); + } catch (NullPointerException e) { + System.out.println("正确: 成功捕获集合中包含 null 异常"); + } + + try { + new Article(null, "https://test.com"); + System.out.println("ERROR: 应该抛出异常!"); + } catch (IllegalArgumentException e) { + System.out.println("正确: 成功捕获空标题异常"); + } + } +} \ No newline at end of file diff --git a/w10/TitleLengthAnalyzer.class b/w10/TitleLengthAnalyzer.class new file mode 100644 index 0000000000000000000000000000000000000000..a31a9d2b4cec0ccd3ac880f36af73aef6dc912bb GIT binary patch literal 2450 zcma)8T~8cU7=8{5%yyU-xAM|8`F4Uyzrvl&<_*6@yd8*{0lVce=x>pW(RiouxSW0=RN2BeBSfCvw!@1 z_g4Ue_(DYmA_^)sRH0g+ZcZ-9gek3TVr*(o&$t5By@qADeFBkq=Q#yYf!)4R(KXD( zuykfeWL|}WNDXSBDcGrD7itA|m9^4_Lr03?4CeCgqVMTq6uSkY(vs%ll3q|zj|K&e z8up+`pnlSDO+Bq!S$DQSP@#jY?vBcwF3=F~OqVmAa0`Z&P1c|p`xLZj*pCAOJInEz zA%o_NOyWd5WLPROxpgP(Jy43`5&D!1dO>D`%v2miyMjX+I&gRm79YEm4%;8XyppUnUG4GItE%dkPpH8_c96(lwE;yHnRTY|2uvWHHQ0PrHFVh~7p zrl&OY<1{ykp+NJ7GRN{>k3{bR4}CBEQSRd%V9!VMAtq^o!t;|eCcdoa0yLuc&blu2ie@iXD(m$upab3D3tWBLB~ z-`@Y`x7E8ptX{i%@4K&7um7mxRa{VTQNwEM-trO& z5tq1<4lKp=`J-S;pf+$&CR62rJxzbLX~WV-i@7PiFe&|9m4K512g9xPIGhYOlB7zK z5fiS=%yS)B@0c*MmUN2+`X1kG-OfvMZZk$Y zQ?=D_y-8Noki&c(=DBg%+gQ|K`qI0e^qz(}U;04Bhxkar#~MDt6@hmDQu-6buomoj zJ(2c{Wn?)cGp=3W@T|#&r9Z=o1V*Jqhov=b>P|=6w&#oaFq&KO-C#JmnAgMV4}Vw+ z`C!{H4+75v(ljRwS7&#%lx69K0aH4T?kKn_aCi$Q+m)x_nn3G=Rmq4F79894B({L$ zw5;ZvpFTb_ln(GM_^#@@1@SW%0@HyKH!10up($0xIE|`#_V+JPF5JS7k?uQC1+L>{ zPpqzM1+hEW8^QUWSZm}q+SY_e!$LeH+@yJzz+D^x?%-GjA14I*)^_{Wcc161TfxBb z%C)1^`q2x)QPt)Q4lGl7L}1yzjhEI_X{-z-7H_O7OE+0pg@x{EkET z3x|>Qy^DWvR-whLAJ-tx{29Qf*OC^#y_Q5`uXt&N8`4#$dQ1uw+XBTX!9Tx(*MHii zy`S1-;X`USQJW-wNbPz=U@=|=Y~=ZFqx?Sq-zE?X!Rnp$)e={qQcI=x&#;Wo(fl8x CZ)L0i literal 0 HcmV?d00001 diff --git a/w10/TitleLengthAnalyzer.java b/w10/TitleLengthAnalyzer.java new file mode 100644 index 0000000..4f64cc9 --- /dev/null +++ b/w10/TitleLengthAnalyzer.java @@ -0,0 +1,44 @@ +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TitleLengthAnalyzer implements ArticleAnalyzer { + @Override + public Map analyze(List
articles) { + Map result = new HashMap<>(); + + if (articles.isEmpty()) { + result.put("analyzer", getName()); + result.put("averageLength", 0); + result.put("maxLength", 0); + result.put("minLength", 0); + return result; + } + + int totalLength = articles.stream() + .mapToInt(a -> a.getTitle().length()) + .sum(); + + int maxLength = articles.stream() + .mapToInt(a -> a.getTitle().length()) + .max() + .orElse(0); + + int minLength = articles.stream() + .mapToInt(a -> a.getTitle().length()) + .min() + .orElse(0); + + result.put("analyzer", getName()); + result.put("averageLength", (double) totalLength / articles.size()); + result.put("maxLength", maxLength); + result.put("minLength", minLength); + + return result; + } + + @Override + public String getName() { + return "标题长度分析器"; + } +} \ No newline at end of file diff --git a/w11/ExceptionDemo.class b/w11/ExceptionDemo.class new file mode 100644 index 0000000000000000000000000000000000000000..4391fad7fb655cf73b9c948c4b4c3ead9853dfb0 GIT binary patch literal 2853 zcmcIm>sJ(I82`QQ!YsoOxG0E-0s^t1tYVshVv3?Dps|3F-G=2A26kt**;#?^mL)2t zWu7jFQmhW1PUYnodhk-8JN+55;-|iLI-TfwXBSw4V||IsJMZOrpXc}dp3BTHe~sS* zunQl_SOkXzr-CFTGi3CrM^tY>)w{g=J9@a^Vo2Vi>6%r;;3)O9rND(039^DzD09Sp z17V8?Ny>=OrKJtFBF*p~Fg4xsSteJ5)h;~7uzIWb$&dbcYVykY{MP1%{OHAzhu;px zH|$dx8CWdAtsoOi7%~@7#vp}+>VQtxN@M11++<7ByQ)2HGO~~@VVQy);ab|Rem@Uc znxXIHL4#qj#lu#s8E8`jTBj=ZUC3o9mJ{(kJbs!`Mu*2I2FK-TE7&5gq|uf77z<>}P3c)8VH<Zt5i@NGK zYFqYjME8qS@%kd6P(b5mwTwNem#|mCJ~WWWb|SPAObSj0@8|xA#YN2QeTMakmgj3r zWGGgE)owTi1^Y=|6Sw*dvv-Di8BN$EhGqpmk&5YdN%$<)-`irUelDnJRnUfmbf)Qk zlLv{l?Rw{K!2%U@y_<|-V_OT zaI;122+)o*sA^QzS*3^O=ZmL}Qp9$!Q4QJh5;RKK1w0vtOh2#F1ZwvTBci`l2*N02I}F1LTc#Rn$wex@2cI8{3uWNQKkF!-CF#lN7Y%PfK@WN} zu-S#zN#BFp=b~Q@+xp&QD2SdIog5j8#iOF4BENLv>KCHFeK!(4f0d*>q~>iINAZq? zcSTklVNw(L`$I9NWA-iV~{VdR^ycZ9ol&d04^+^xm7t$wJYSaF$`^qebbH zAr@*BP(m)E*BGc5F#1oX(NC*9dNQ$Fb`$IxO&B_96vv=modS--G#8@VNP*bRz7ENq zHiq5|dlGHNP5?&vc zlk=I-h^a1;UIXV8Z8;@$(;p)~rci>U>7|5KU=r5-fr8(O0a<*O#62D&Izwkd=^ zi~K62cYiVYQG!Zr#0KJHqm4QPi>By+kkyExDKkSBy(y0DU>w=%vN5!5At1Tqc-97z zD_ug`Z5(QMzi<;T{p41!lDQapu^< { + int currentAttempt = attemptCounter.incrementAndGet(); + System.out.println("执行操作... (尝试次数: " + currentAttempt + ")"); + + // 模拟前3次失败,第4次成功 + if (currentAttempt < 4) { + throw new RuntimeException("模拟失败"); + } + + return "操作成功完成!"; + }); + + System.out.println("结果: " + result); + } catch (NetworkException e) { + System.err.println("错误: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/w11/NetworkException.class b/w11/NetworkException.class new file mode 100644 index 0000000000000000000000000000000000000000..31353b0c7f15e5a5a93d46030a3c42cb503fafda GIT binary patch literal 336 zcmZvXO=N*jPQ;Zsyw0 z#6c)d-PSMk$cSiM8#^)?ZXwgi2|%(F_cE(H*76=Ftt05bH^L3~KRRSG`sE8J{vIJ0 UppsVm2z6~x9Gqvg!bpJX5AirdvH$=8 literal 0 HcmV?d00001 diff --git a/w11/NetworkException.java b/w11/NetworkException.java new file mode 100644 index 0000000..81dce05 --- /dev/null +++ b/w11/NetworkException.java @@ -0,0 +1,9 @@ +public class NetworkException extends Exception { + public NetworkException(String message) { + super(message); + } + + public NetworkException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/w11/README.md b/w11/README.md new file mode 100644 index 0000000..2ab467b --- /dev/null +++ b/w11/README.md @@ -0,0 +1,48 @@ +# Java 异常体系设计文档 + +## 1. 异常层次设计 + +### 1.1 未检查异常 (RuntimeException) +- **UrlFormatException**:继承自 RuntimeException,用于处理 URL 格式校验失败 + - 包含无效 URL 的信息 + - 提供有意义的错误消息 + +### 1.2 已检查异常 (Exception) +- **NetworkException**:继承自 Exception,用于处理网络相关问题 + - 支持链式异常 (cause) + - 提供网络操作的上下文信息 + +## 2. 异常设计原则 + +### 2.1 Checked/Unchecked 选择 +- **UrlFormatException (Unchecked)**:因为 URL 格式校验是编程错误,可以通过预检查避免,使用 Unchecked 异常更合理 +- **NetworkException (Checked)**:因为网络问题是不可预测的外部因素,使用 Checked 异常强制调用者处理 + +### 2.2 异常包装 +- 所有异常都保留原始异常信息 (cause) +- 使用链式异常避免根因丢失 +- 提供构造函数支持 cause 链 + +### 2.3 上下文信息 +- UrlFormatException 保存无效的 URL +- NetworkException 支持传递详细的错误信息 +- 异常消息包含足够的上下文帮助调试 + +## 3. 重试机制 + +### 3.1 指数退避策略 +- 基础等待时间:500ms +- 计算公式:`wait = base * 2^attempt` +- 最大重试次数:5次 +- 等待时间示例:500ms, 1000ms, 2000ms, 4000ms, 8000ms + +## 4. AI 架构审计请求 + +> 作为Java架构师,请审查我的异常层次设计:Checked/Unchecked选择是否合理?异常包装是否丢失根因?日志是否包含足够上下文? + +## 5. 文件列表 +- [UrlFormatException.java](file:///D:\嘻嘻哈哈\Git\java\w11\UrlFormatException.java) +- [NetworkException.java](file:///D:\嘻嘻哈哈\Git\java\w11\NetworkException.java) +- [RetryUtils.java](file:///D:\嘻嘻哈哈\Git\java\w11\RetryUtils.java) +- [UrlValidator.java](file:///D:\嘻嘻哈哈\Git\java\w11\UrlValidator.java) +- [ExceptionDemo.java](file:///D:\嘻嘻哈哈\Git\java\w11\ExceptionDemo.java) \ No newline at end of file diff --git a/w11/RetryUtils.class b/w11/RetryUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..278b07c988774847ca79945431796e4a955364b8 GIT binary patch literal 1964 zcmaJ?-BTM?6#v~o*krqW(^5)5LWMTLN~|B)5{eB0Y>)sp0a~l#5^iB(+0AV3mh$F< zPRAL)Uh4Ru4|VGBAdHkz!0O<;|A7C1q4?yBFV1wVXOlpZ%9xqld(Yl;?)jbHIp^MA z|9Q9#pdY#n7s@1*D{!NNp=M6Kr3Q7?m9VqGmT)}PNj5>TVyc9$-coK7<&5Dkc29R`oib_+ew~s%q7$?$XB_r zmd5DVw1hfBHO$4LY0d9zdC+5%@N_eTSRUYx*538xhJ&{9HQd?_vX0+C?aM8e^m6U_{cl21XNxw;UlqZ~Y= zpcQQljSK2LkC;YM6%(46rBzy}hDb9U@ef9eDa^x`aTb0F0R`=Nc8|j<6b<64hca8&*MD9i9$Y;1jV*2Zit{0?-NG=T9^jr_T4}kjgyAAyRdCs^W#A;Lv^l7cEA4i_pQ~P&sW`)4*IdbDlTUi zfQ&1MN*Gk|I))gk3W#H>n5E>V%tZ>#-`5xDBX)Yj@&3t)a5OO)BX-yzLEOvZxT@P! zG)gRdZ8Drl^v6aMj!LwQJh_RLh&fH$)kB)WhwX(aZY7*Dq_FvIp0ug%s3x*q!EipG z$%ET2R9q^#9u5(XxKNYxF*TJxR^F$X3d|eVW(-x>7Wuo>o#-lRi2MOP|Ld|lkqGsK z0yI+V#!cHw@&U~OUtQ?q4u?7lr`I$^T3Bi-#>K2To%Udo;lfj${Al&V%=QO9wvhH^ zKKmrM+0oJA^FLZy@i8B@b!P1=YW+ZU1ymVRNJ^M?`iOkC7ILH+x6FAS%=HT;f?8`p zO$yUmV)&xC9rB7fNLN&2TIcE3A=8|gIf_@O{Y2Ce^h2||B2}!s1oK%nyLg`J}SD-b8(@Ct~VDIpNnp|7>24E{_o%>NiwZwpkW?O F{{qoX6P*A6 literal 0 HcmV?d00001 diff --git a/w11/RetryUtils.java b/w11/RetryUtils.java new file mode 100644 index 0000000..b1c313b --- /dev/null +++ b/w11/RetryUtils.java @@ -0,0 +1,36 @@ +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +public class RetryUtils { + private static final long BASE_WAIT_MS = 500; + private static final int MAX_ATTEMPTS = 5; + + public static T executeWithRetry(Supplier supplier) throws NetworkException { + int attempt = 0; + while (true) { + try { + return supplier.get(); + } catch (Exception e) { + attempt++; + if (attempt >= MAX_ATTEMPTS) { + throw new NetworkException("重试失败,已达最大重试次数", e); + } + + long waitTime = calculateWaitTime(attempt); + System.out.println("重试失败,等待 " + waitTime + "ms 后重试... (第 " + attempt + " 次尝试)"); + + try { + TimeUnit.MILLISECONDS.sleep(waitTime); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new NetworkException("重试被中断", ie); + } + } + } + } + + private static long calculateWaitTime(int attempt) { + // 指数退避公式:wait = base * 2^attempt + return BASE_WAIT_MS * (long) Math.pow(2, attempt); + } +} \ No newline at end of file diff --git a/w11/UrlFormatException.class b/w11/UrlFormatException.class new file mode 100644 index 0000000000000000000000000000000000000000..b529038d8c4d32ee81c76ed9f26ca430bdaa534d GIT binary patch literal 604 zcmaJ;%}&BV5dH=zl~Sl8h%xctNhl}}An{@#F(DoVy%*TvnzA)bK_5#K4T%RIz=tx< zHUyeLxXkSCd_S`@zklC906I9fkV4u-#)g3`;pEwydoABnlh*xAMRF=SZzJ&%$v_dZ zZK-5*O{mm*Yg|7HrJ7vTA9L74&V*$n51UYW2>oslPQ550VM#Ujd^zSCBQ{lZJ4M}# zLvXjaBp8FIFnINbzF=#@`EKy4NkZl(7z={alSgJ3t^<;6zvrO@Zh$ByvPia0I*ynh_w}xID=e*lkfZXO6 z%#Rpe;#iDJhTbSFm@)BiNo;a0!x-CbG#4ni7h9MX^YXDRp^VCsdCHlXX}F&--&bbY N_|f5b#BV87{{ef0h;sk{ literal 0 HcmV?d00001 diff --git a/w11/UrlFormatException.java b/w11/UrlFormatException.java new file mode 100644 index 0000000..c0c26e5 --- /dev/null +++ b/w11/UrlFormatException.java @@ -0,0 +1,17 @@ +public class UrlFormatException extends RuntimeException { + private String invalidUrl; + + public UrlFormatException(String message, String invalidUrl) { + super(message); + this.invalidUrl = invalidUrl; + } + + public UrlFormatException(String message, Throwable cause, String invalidUrl) { + super(message, cause); + this.invalidUrl = invalidUrl; + } + + public String getInvalidUrl() { + return invalidUrl; + } +} \ No newline at end of file diff --git a/w11/UrlValidator.class b/w11/UrlValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..87db14350e9e3bf2ae41afabbe4eda4f4d76599a GIT binary patch literal 731 zcmZuv%}*0i5dTdnEM2xsTR^Eol?vJxV-r0>yqI#aXkwuTZ?EkG7Ph<1^1zR)7ca(> zG2XZtk6e%xl$!_t4y^ta#(7&JHL!;_@6F8bV<+?DPx~8y5^kG_pwB?mLO%uwi4CzM zimve1ijQj>vaSdN^N#1J`-Hw+zG`9+F$1QBAz1&lS5)A5YlJArCSf#}FQ0LBi*p)n zTNp-yU^rW)W=p*cd7l|b5mL*6yWj^+p-M06vZWl~8^kDKc=<{BlQ_vq)*yUvH*&igmoU{+J#Ak1Gt-D7ZRJ@B84nk(melBeU+4rbUjkEJSHry-Od z@PHrq4au5i$CHn?n>86MX*^+Qr`sgCpAfGoQQuq?t<$Pm@wbDzTyS)2vX}CX{zFV* znpZ)85s+R6K5z1Ck|S~KFMI**@r7s3r!E6g#E`=T-*xLNhG~j>h3|+L_F?QJ{t@;e zk}IhZhL0T|Vtj=!SDt=Cq8#S0&GLKdc#kR4$YO@|BB4MAMkr%*O!Ky7VRO}Dp*ZgH W3gx*PL)11O8kn>gUf@Iovws1N+^rJ; literal 0 HcmV?d00001 diff --git a/w11/UrlValidator.java b/w11/UrlValidator.java new file mode 100644 index 0000000..c16a810 --- /dev/null +++ b/w11/UrlValidator.java @@ -0,0 +1,16 @@ +import java.net.MalformedURLException; +import java.net.URL; + +public class UrlValidator { + public static void validateUrl(String urlStr) { + if (urlStr == null || urlStr.trim().isEmpty()) { + throw new UrlFormatException("URL不能为空", urlStr); + } + + try { + new URL(urlStr); + } catch (MalformedURLException e) { + throw new UrlFormatException("URL格式无效", e, urlStr); + } + } +} \ No newline at end of file diff --git a/w5/Circle.class b/w5/Circle.class new file mode 100644 index 0000000000000000000000000000000000000000..ff173905521121c262ee349ea350687601f4895e GIT binary patch literal 382 zcmZutOHRU26g{`l7D^Qa@$bNa2{^C|z&If~BF3bK`2!k!rXNyUjT#(@lFa!4q3y2C0e zsG6{B)L;`D7vf4dz6hs|dvlq10Z&C~LTC^2j*OhsScZw4#GVLx6)Y0$uebZ`Zk|2d zX77&{8dx&Xw9&#cq4f`nV9peWA2MyH;4L;LT%Pv2LlY}}W?#l*pYJXAqY1nEK!)B) z8jQU7OpNlzBo@~M%Z<`_>>W#0s}-nywHG^RvjpmC;Qzy5yUN)%dt$Hey?~xLAgr-f jZ50#Ne@Az7C{TTd*<6^T{x`1V1rix6Y literal 0 HcmV?d00001 diff --git a/w5/Circle.java b/w5/Circle.java new file mode 100644 index 0000000..c1a7445 --- /dev/null +++ b/w5/Circle.java @@ -0,0 +1,6 @@ +public class Circle extends Shape { + @Override + public void draw() { + System.out.println("绘制圆形"); + } +} \ No newline at end of file diff --git a/w5/Rectangle.class b/w5/Rectangle.class new file mode 100644 index 0000000000000000000000000000000000000000..3c8522dd575178c44751c6c4c22384a5d85fa6b2 GIT binary patch literal 388 zcmZutOHKko5PdZ;Gt4*wg7|mg!USA62f(;7x+2CnhJ6Da95Wv>HX3i>5!{)$bEykV zGlXE?^RXa_s{zmfMaaAFp#s6_h7;zn8BqONP=|`CZRec0+z8~COD;YB@9V$YtHSL6{f0l)b3REUV+$^9-@FpJ*#EEB5#s0j8z&xjHpTc6FHZB$(DHydpmD+K3Ub`>8I@yYq4w8yE` z3KMxcj5{*B5S?kGmx)_~8>GXmD^EgQE6#=cdOLPdVq|ZC`|=_Gy1C2MM9L zuR@g^5X@$)S4IhC8;*-PxPR#0&1E73W<@EB8r|GTMeVa#g^8EMQUsk676|U_^?rJr zP2V17cbS7K7H!mA)UiaU|Dz(<6Yb%LY}@?y%|FPdDjaoMJsZn}f_EV%l3*Q1LvEF> z3gt-}^ksZ5`o10vV{uJzyeN$a@>uCNCl~GMt=L9`v(U!?+o8yHg>PHjiF;x98T812 lu*y}BH72b6Pe(q4;J5eIfc((O6<)eA<=`E@`)9*=qO=zWCy^ zzrYww1Va;lfd2#aR~XOT7KjPiWbT={cg{U$_Wt~P@&mwQ+|x)QZ6ISKi=0AX*L&;L zJ6^a`f7aUd+p$7!JqUyNkwV&Ce5ql=GN5f-k@9Twjko73WVR#ko#aTB=ar2hYIl5% zF*%q}uwIagq;gh3+QJk{2FfaCJh`yF>oOF3E#%XH6{5 zVcx)kjoY|G=}Ctzf1i3Ru{pA=-A00V!-T6))+0qj@6PGx4&sLs=w`1Uwf!f76z9)Xu83(=Xd=Zw45Y!| zgdEo;?q(SkW43w>bx5F4<0@&vmUw2A(hB3S^a#d9sSJ#;X82iH#>%Mtfae)z9u8o> zI)>dCV2pA6=>P@B$<41QPR<OTZAoMQk0 literal 0 HcmV?d00001 diff --git a/w5/ShapeTest.java b/w5/ShapeTest.java index 287ef36..3b805c4 100644 --- a/w5/ShapeTest.java +++ b/w5/ShapeTest.java @@ -1,4 +1,22 @@ -package PACKAGE_NAME; - public class ShapeTest { -} + public static void drawShape(Shape s) { + s.draw(); + } + + public static void main(String[] args) { + // 创建形状对象 + Shape myShape = new Shape(); + Shape myCircle = new Circle(); + Shape myRectangle = new Rectangle(); + + // 测试 drawShape 方法 + System.out.println("测试 Shape 基类:"); + drawShape(myShape); + + System.out.println("\n测试 Circle 子类:"); + drawShape(myCircle); + + System.out.println("\n测试 Rectangle 子类:"); + drawShape(myRectangle); + } +} \ No newline at end of file diff --git a/w6/Animal.class b/w6/Animal.class new file mode 100644 index 0000000000000000000000000000000000000000..b05489d14c245388ee868cbcef0faa28fc91506e GIT binary patch literal 206 zcmX9%yAFat5S)cW1o80$7ItbOKVVFZ(aLC{u|MEq4&g`$_+M5g7Jh&qW!#Hf%*@Va zXFlK88^9KW5Dr`)B7%nwVU)|ej0zR<;=2XLVDz&NDa@$s&h`l#8{B0xsixWaWgVK!MYXQ3N=GD)u4t zU#h~rhNT(gT5D3m@%*5OUdvps>!ww(>Y6aJ9*i+eos%op`}nZ`_DOg3<*x^?x@&K` z8}DKRK*0p|b3{}ez(I!akUR{0MP{v?C4iwdHcPlYlT|VT=ZauVOsgOm)_&&-L;MW{jc5CXLc_fhbg9TS2qk%s%k#Oaa6@InbOsu zQx6ghk^atlr}f%q(rG>Fe0kbywR)SI9J36HU3x)l(5<*~wMC5~kk?J^YSSob^#z%i z07K|RW+HKO7#jwE!4l==d9m`xUm*_lvUXXQ!GXUe=HzW*21$Bxxg22f&1p}OHA#|@ z9GTq)drt~Onsy0G-HQ{LBPm_ma%4!}ZiC<0hBx1aLQY5Pg%}<#Oj~Vq*NZun-ap?*~K#30egaBI!3~39EN|;k*cz_JW;-e<78E z3KIMQ{*Pdpe<02#ppDBi^LF06ndNq(gQ-a zT5G$=!*$^Kn1fF!UWjWEghKa%*3DHaBkqa^DWTGwGAa&E6Q$Eunn)4V^H^Z&>uxx{ zA5KP(|=nMwio8BS^b6@|Zc<1=8fkv2=#8E3HZ1CyImDF6Tf literal 0 HcmV?d00001 diff --git a/w6/Cat.java b/w6/Cat.java new file mode 100644 index 0000000..07d0e2a --- /dev/null +++ b/w6/Cat.java @@ -0,0 +1,6 @@ +public class Cat extends Animal { + @Override + public void makeSound() { + System.out.println("猫叫:喵喵喵!"); + } +} \ No newline at end of file diff --git a/w6/Dog.class b/w6/Dog.class new file mode 100644 index 0000000000000000000000000000000000000000..006d7a098a2077145a98453dba04ff01d610c471 GIT binary patch literal 484 zcmZuuNlF7z5Pg-w9&AlajPsO*khm}hhzR1a3L>;*-!V=2+JkgDic8lnTngd=L{~AO zL4pVH9>!&EAl6SKZUUbge!cgqsQUbQ{{XO$yp9;+3K9mA7$c1Bc*3n7x35i|5&Ms9Eo_*MIYj$uP6*Sj6vrWI{)fv=e0zv0A zII$0eB&w{oDaS(|v%E9HJTEZLp7+Lfk+Ws?#6G$425NIaSYj*7GA0K6LDz>8fc63< gGx3VlKU`h}Nbt&&Oj#LC$&H}ME9p#GWqJ&2zhGWvDF6Tf literal 0 HcmV?d00001 diff --git a/w6/Dog.java b/w6/Dog.java new file mode 100644 index 0000000..b0d86cd --- /dev/null +++ b/w6/Dog.java @@ -0,0 +1,11 @@ +public class Dog extends Animal implements Swimmable { + @Override + public void makeSound() { + System.out.println("狗叫:汪汪汪!"); + } + + @Override + public void swim() { + System.out.println("狗在游泳"); + } +} \ No newline at end of file diff --git a/w6/Swimmable.class b/w6/Swimmable.class new file mode 100644 index 0000000000000000000000000000000000000000..56507729f3da7ea2cb9d3c0e836ebda27478beff GIT binary patch literal 120 zcmX^0Z`VEs1_oCKPId++Mh4E{^32@a#H5^5b_Nzk27#=^vPAuy#JqHU|D>$c`p-*eNz*o ziHTo{n)qyDG%5)KzWX~YiBJ3k z>5g(unN8^ELbryV5DuW1p>+p*>aFNj%S#tzJH+5Lb&<3()OvWrl;T>ckQVk7Pv^-K zSR@u1n=djnrW7}Fle}1a(*No~?DVWq!+#Vcp-0G-vP!lg#!MF{R11t zwNw;4gx{)*uTEg1{E`5srql4E@`B+KH-$^9L4Al5`Q97fS1{d``8LWclk^_13V3Ib1-;Z!kO%w*UYD literal 0 HcmV?d00001 diff --git a/w7/Account.java b/w7/Account.java new file mode 100644 index 0000000..fb3efaa --- /dev/null +++ b/w7/Account.java @@ -0,0 +1,19 @@ +public class Account { + private double balance; + + public Account(double initialBalance) { + this.balance = initialBalance; + } + + public void withdraw(double amount) throws InsufficientFundsException { + if (amount > balance) { + throw new InsufficientFundsException(balance); + } + balance -= amount; + System.out.println("取款成功!当前余额:" + balance); + } + + public double getBalance() { + return balance; + } +} \ No newline at end of file diff --git a/w7/AccountTest.class b/w7/AccountTest.class new file mode 100644 index 0000000000000000000000000000000000000000..863f98a3376da014a9a76e2f7b76f3c57a7435af GIT binary patch literal 1490 zcma)6TW=Fb6#m9BUdP#x;5Z3MOX{R4wF%(Rmfj4hiIbERn@gR#DyWqv>me+$XD#m- z8mUzuDx~(I;-ME6Bwl!EUyuR_B~n`W0sR#`)F?dh1CZLD*$u*x-Ks0?oH;Y+eCIo7 z&deWwZ9D)li(&);bQlPl*ntp3??rxzr^;L|rA{qg6qaHL9kHdYrWiUB$)bTUgK^BV zoVrw@%+(B`01+54H9|K-Ffo%XcA^Kp26mZvmw!jz5v=Xdvn+iFQIM>P`S!C!g zC~ht1dDXKwkfB3qV@fqBIQ5z(=4_4H+Z6nuK0{iXb{yrZ8n5Psy5N-DFlH%kv5TZTTZ<6M@?!X3{!&T_q)ZY@gF$~MP^m8xiC z-drpHzNVYoV+$h7%jJTtM4G{zmr~TSW$wDdHSi(BD=j&-qiSG*VdTF}>8bGZ!YJOR z&eC6iI+Gq28s8;t4@pLH$G&y2J2YWP)2O%gyc0)ol%zI&iXb{je)$`WPh#j?M_2AH zc22Azwt?Ni8eSwdI!1CLhVeD*{S{+*TH3#ci4CNlS&;hL23~)rM&Ixq-uzz~pG7hPJD66wYX^paPU{07Lg51%213i|N{ z1~En6yAk}G90CU3Gw8)*29hIZ^gxLpqx}G;ktG=;{R1k?j5p4C<8hp(od~U-p%@=w F=pWaLf-?XB literal 0 HcmV?d00001 diff --git a/w7/AccountTest.java b/w7/AccountTest.java new file mode 100644 index 0000000..1f0eab4 --- /dev/null +++ b/w7/AccountTest.java @@ -0,0 +1,24 @@ +public class AccountTest { + public static void main(String[] args) { + // 创建账户,初始余额 1000 元 + Account account = new Account(1000.0); + System.out.println("当前账户余额:" + account.getBalance()); + + // 测试取款功能 + System.out.println("\n===== 尝试取款 500 元 ====="); + try { + account.withdraw(500.0); + } catch (InsufficientFundsException e) { + System.out.println("错误:" + e.getMessage()); + System.out.println("当前余额:" + e.getBalance()); + } + + System.out.println("\n===== 尝试取款 700 元 ====="); + try { + account.withdraw(700.0); + } catch (InsufficientFundsException e) { + System.out.println("错误:" + e.getMessage()); + System.out.println("当前余额:" + e.getBalance()); + } + } +} \ No newline at end of file diff --git a/w7/InsufficientFundsException.class b/w7/InsufficientFundsException.class new file mode 100644 index 0000000000000000000000000000000000000000..486f301d285a1557264bc840c5bc734152e3b986 GIT binary patch literal 830 zcmaJ<%}!H66#k}#-u7~(RYU{@{3({Q5cf4MC^V8(U6kO$)%12KLoYMUy>p2?f{Co$ zsEHe2z!-nifDhnvK%y(&z<6$38(hf4P8OBZ%R-U?|ZKPHV zsk>51b1$kfSfRL{frE1vMjd34W0+c1LAbFYT`82QgsKt5N?CPI?OnmZmKlaWs-H}%B5S>Y~xrIW9T=;_Fg~BI{yzgSh=pOCHhY_hW3yYF2V!V%@?p6a~ mK4RqEA4n>yM1UkNkxgI%m&vBddxg#<4HZS{>ygJTO#TM4v*C6C literal 0 HcmV?d00001 diff --git a/w7/InsufficientFundsException.java b/w7/InsufficientFundsException.java new file mode 100644 index 0000000..e422129 --- /dev/null +++ b/w7/InsufficientFundsException.java @@ -0,0 +1,12 @@ +public class InsufficientFundsException extends Exception { + private double balance; + + public InsufficientFundsException(double balance) { + super("余额不足,当前余额为:" + balance); + this.balance = balance; + } + + public double getBalance() { + return balance; + } +} \ No newline at end of file diff --git a/w8/Box.class b/w8/Box.class new file mode 100644 index 0000000000000000000000000000000000000000..95d3acd04f1ac852d17670625e5c1d3a67b6684a GIT binary patch literal 1289 zcmaJ>T~8B16g|_gElZ(TKtKfr3vP>2{5BSep@C=u^3VnoeHzMGmM*i+?vzN3|HFUa ziw~M;G(7lf;*T=k>8^AsjSo9_=I%N7+>f38{paK-fOR}F5kuTSB7-DS3?m2ph?g8L z8>Q{t15sBDsTEt=YLy{w6?O)Y#()7cgF$4-wdTHMFzT*ULJ~H&*~>4R7)I8>NCp>> zJ0nn4o-G?hKjKbXY` zLS~;}35kFz?vnkv% zv4oO=Wj%!34EgTVZF%H2MJdQCn9ByQE7zmSK19100t2CET<$r-&u_YJv)w93at_Oj zc+L=O$1TwZ{yKsW8xb8v7^1=*r)n!fGs!%cQg{`I`@ZlE++&zOS5v=K4Lo3&`9G9C zkDxBh((Q@S$IED?`$KkuPBBa(MSHq{v`&#{nk*xmEPe<3(ji=;Rlf!jv?g(xXc}51 zt3#R^T3q}Fql+I#yu(VsfWEka*cIYWVD9s>@_`0YgQS`UNBL5sCOGa>OE%qqt6 { + private T content; + + public Box() {} + + public Box(T content) { + this.content = content; + } + + public T getContent() { + return content; + } + + public void setContent(T content) { + this.content = content; + } + + public boolean isEmpty() { + return content == null; + } + + @Override + public String toString() { + return "Box{" + "content=" + content + '}'; + } +} \ No newline at end of file diff --git a/w8/GenericDemo.class b/w8/GenericDemo.class new file mode 100644 index 0000000000000000000000000000000000000000..8ed7147ed25c984927a277fc8f462573d2aad6a7 GIT binary patch literal 2601 zcmaJ@*>@9F82{a7nIR0c9cUpewrp)mTgsxM4am|K2$WI{w5A9+O@}g&WNI=gR6QP* z-Lh1m6sfX_faie7VwaS~H$A@iPc$h!d3^EsCDdB6sd=~RHyG^8w2)@J#b9xjG>n6YaRM9?#zV?b z_jW`KHAq@|ln&+9nn0RfQ6JVqhSvzIO3*E0B17SF{uCyD>U?s$yKuF>uP|}1H-4dW zakWq=q$rWR?SYn8stC{sKTUY69S1%;YXicc1`;0%l2=jX4at&r{?H8EUHPFn?Z~Khy`qI*EVqDV3|77)f>>scKe#kJaMS@e zR`LR^VkjOX2Wei9W`#=TMwNtW)DTRoYHU!+*~zYS-%`3VEIc?j)=BVib&=E?cnxJ( zQ7)pE5QYbC#d{x$sN+r@>lu#S6j9GmkP%XnyNR=diF@5i`igjm;#p69Bw{lit?w{) z=^+sfl(?$WQW5VG1^BktAJ*Co5lswo{8rD%&1=I0cN5(|jtm|6$t~L(ja0FIXfJ?nmB$X-gV*`h5+jPWTQsOoeI;9S}j5Z55Qty@i)PX7uq!itr8E}_)WMbk$PWMCHPDYET z=q}(0!}wH9s@0%Q*{srAc*1M7kXjcFHml*yiW#LfsAzO^WmajlABiUpuO1Ei)ioMV zNlqH(0vhYWzsglgl^a2ltQMzeHt! zdiW&Y%eLbi2QJ{EfJ;0(ml@`o#W62qE!3{>Q7e*xB`>5kir>(~#L#o;J)Kfa2D45H zwFK12+*)1V6K!*+@i>*CG)cz6%^hv(80uqn!pjgKeVD-oIV}Ttg=8B^Rz|Y2T>MIs zKFsBU0Tjnj2Bc2+V__YM%T4hjQ_PorSRBLaqf7ZsQ`_y6m*+T5@D-I-Qgd1fJ~glY z8Lk-Cj2_`zYfaGgDO+@UgS@dHD=KaLh%I?k-bB#CL%4hv`E9SyDto;?o4m#Av&)U% zN;_%FzCLVq+6S;*e24;{#Yzs4qRpIaB&U^KFegFeQ>DO9weV9U-tH7EH0|WF-J`N0 z1bqDnRfww|7i>d%z&O(>JXI{5a>rqgiQ5v(yvkx zB$Q(!mLi*Sl8ZXp*+%~s0p!EL6vCKFRdwPbrr|DT;4#tQ7Zl=m!g@-V<)4^?zc7y# zpokTtn3bV~Ex~-|roV=jC}$olV2$+cSFw;qu!!x)V*0`zCqJ@q0w-xMA_jd;yy-H> bQ|5Ta9M78LIdeRZE2Qfny>IayuEY5+Cppm6 literal 0 HcmV?d00001 diff --git a/w8/GenericDemo.java b/w8/GenericDemo.java new file mode 100644 index 0000000..2c320a7 --- /dev/null +++ b/w8/GenericDemo.java @@ -0,0 +1,51 @@ +import java.util.ArrayList; +import java.util.List; + +public class GenericDemo { + public static void main(String[] args) { + // 测试 Box 泛型类 + System.out.println("===== 测试 Box 泛型类 ====="); + Box stringBox = new Box<>("Hello, Generics!"); + System.out.println(stringBox); + + Box integerBox = new Box<>(100); + System.out.println(integerBox); + + integerBox.setContent(200); + System.out.println("修改后:" + integerBox); + + Box doubleBox = new Box<>(); + System.out.println("空盒子:" + doubleBox); + System.out.println("是否为空:" + doubleBox.isEmpty()); + + // 测试 Pair 泛型类 + System.out.println("\n===== 测试 Pair 泛型类 ====="); + Pair nameAgePair = new Pair<>("张三", 25); + System.out.println(nameAgePair); + System.out.println("姓名:" + nameAgePair.getKey()); + System.out.println("年龄:" + nameAgePair.getValue()); + + Pair countryCapitalPair = new Pair<>("中国", "北京"); + System.out.println(countryCapitalPair); + + // 测试泛型方法 + System.out.println("\n===== 测试泛型方法 ====="); + String[] strArray = {"Java", "Python", "C++", "JavaScript"}; + System.out.print("字符串数组:"); + GenericUtils.printArray(strArray); + + Integer[] intArray = {1, 2, 3, 4, 5}; + System.out.print("原始数组:"); + GenericUtils.printArray(intArray); + + GenericUtils.swap(intArray, 0, 4); + System.out.print("交换后数组:"); + GenericUtils.printArray(intArray); + + List doubleList = new ArrayList<>(); + doubleList.add(1.5); + doubleList.add(2.5); + doubleList.add(3.5); + System.out.println("列表元素数量:" + GenericUtils.countListElements(doubleList)); + } +} \ No newline at end of file diff --git a/w8/GenericUtils.class b/w8/GenericUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..8721196e1ac09a7d71454bd2709ad97fc26e36e9 GIT binary patch literal 1456 zcmaJ>%Tg0T6g^D>9mYW*2#OFu1x$hv#W#uZ5qYF4fK`dOVAX^cM2n`_zyA3A1;8R!G;|=w5!ca)E{49RVo&5u zVeRDBx1P$nV(2OumZ6Fav2fm|;2{P(-xkGm$N#B#V5K+&H0Q5JLhPGOkiw2l#sG7PlwfuXbMc|P~jC;Z4 zKGL*~;SA1loYOIhDTd=dZA%$uu41^v6nBm1(vNSM<2-}DEG_96^@ju@@$r(qO~<_z z!;)*Q#+GzyV#}l*&F8=CIAWh+D7_hpPLRBOW`m)psziNvRWyAojyZCy4V0m~YV24- zwH%^O6&4o`(-qU3wMCya?miPuhEwe>EtkD(#|yP17;5?+*)E^D!oELm5y?eH;T;+)7s#H(xZ8w2D zLkFAa?ZPb$Mcn4N)yzlY8kS9D=YU~tzbV_m+hi|V%ppZdm}b>blKj%k78O*<6s{{>j{6K#hkR-$ zm1Buv?Eg?|5P@G*(eoJtz void printArray(E[] array) { + for (E element : array) { + System.out.print(element + " "); + } + System.out.println(); + } + + // 泛型方法:交换数组中的两个元素 + public static void swap(T[] array, int i, int j) { + T temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + // 泛型方法:计算列表中的元素数量 + public static int countListElements(List list) { + return list.size(); + } +} \ No newline at end of file diff --git a/w8/Pair.class b/w8/Pair.class new file mode 100644 index 0000000000000000000000000000000000000000..68c441de445c8c369827496400a1ec5d2c7fe33e GIT binary patch literal 1442 zcmaJ>>rN9v6#k~r-L@8r1q2ijluKJGRq=wfKw=}&fMQZofB0)CV_CZFlHDl@F}{b7 zp@~Gp4om?sCBt@=Ba%9juT!6@?jDgdTb3i< zwGVfNx5IaBiX?NSM`8+OG=1z+65sH++p7?&2Q^DN(gZkYZhRJtV7~ z6GYY_Bd3iM(mikfRcqZQhDv2~rL(enm)Ke-j3HHX+g?L#SV76^&Mc~{LdXBob)_#o ze)LwzeRt1K;4#&&5TEHzt+L`&a2rKtS2>$%^BOxtO<+fe3 zq@dGCzi}MlRc-G3!Z$&Y*^ZjJrD|f8Ve+Vn5I`4qel{>4?SBj+zxhkXirb` zVm}Up)^SWwe3B4LH22V)Df|R0ES$nD6i?Cjok9$EY0RRR#yE`zk}%OvMxpc+A*OLp z3r*0DDP-XX%x@QT { + private K key; + private V value; + + public Pair(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public void setKey(K key) { + this.key = key; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + + @Override + public String toString() { + return "Pair{" + "key=" + key + ", value=" + value + '}'; + } +} \ No newline at end of file diff --git a/w8/README.md b/w8/README.md new file mode 100644 index 0000000..78ca508 --- /dev/null +++ b/w8/README.md @@ -0,0 +1,54 @@ +# Java 泛型类练习 + +本项目包含几个典型的 Java 泛型类练习,展示了泛型的使用方法。 + +## 文件结构 + +1. [Box.java](file:///D:\嘻嘻哈哈\Git\java\w8\Box.java) - 简单的泛型容器类 +2. [Pair.java](file:///D:\嘻嘻哈哈\Git\java\w8\Pair.java) - 存储键值对的泛型类 +3. [GenericUtils.java](file:///D:\嘻嘻哈哈\Git\java\w8\GenericUtils.java) - 包含泛型方法的工具类 +4. [GenericDemo.java](file:///D:\嘻嘻哈哈\Git\java\w8\GenericDemo.java) - 演示程序 +5. README.md - 本文档 + +## 泛型类概述 + +### Box +一个简单的泛型容器类,可以存储任意类型的对象。 +- 使用单个类型参数 T +- 提供构造函数、getter、setter 方法 +- 判断是否为空的方法 + +### Pair +一个存储键值对的泛型类,可以存储两个不同类型的对象。 +- 使用两个类型参数 K 和 V +- 提供构造函数和 getter/setter 方法 + +### GenericUtils +一个包含泛型方法的工具类,展示了如何创建和使用泛型方法。 +- printArray: 打印任意类型数组的元素 +- swap: 交换数组中的任意两个元素 +- countListElements: 计算列表中的元素数量 + +## 编译运行 + +```bash +# 编译所有 Java 文件 +javac Box.java Pair.java GenericUtils.java GenericDemo.java + +# 运行演示程序 +java GenericDemo +``` + +## 运行结果 + +程序会展示泛型类的各种用法,包括: +- Box 类存储不同类型的数据 +- Pair 类存储键值对 +- GenericUtils 的泛型方法的使用 + +## 泛型的主要优点 + +1. **类型安全**:编译时检查,避免运行时类型错误 +2. **代码复用**:一套代码支持多种数据类型 +3. **可读性**:更清晰的代码,更易理解 +4. **避免强制类型转换**:减少代码中的类型转换 \ No newline at end of file diff --git a/w9/README.md b/w9/README.md new file mode 100644 index 0000000..bf0d58f --- /dev/null +++ b/w9/README.md @@ -0,0 +1,127 @@ +# 文章爬虫系统 + +这是一个使用 Maven 创建的 Java 项目,实现了命令行界面的文章爬虫系统。 + +## 项目结构 + +``` +w9/ +├── pom.xml # Maven 配置文件 +├── README.md # 项目文档 +└── src/ + └── main/ + └── java/ + └── com/ + └── crawler/ + ├── Main.java # 主程序入口 + ├── model/ + │ └── Article.java # 文章数据模型 + ├── view/ + │ └── ConsoleView.java # 控制台视图 + ├── command/ + │ ├── Command.java # 命令接口 + │ ├── HelpCommand.java # 帮助命令 + │ ├── ListCommand.java # 列表命令 + │ ├── CrawlCommand.java # 爬取命令 + │ └── ExitCommand.java # 退出命令 + └── controller/ + └── CommandController.java # 命令控制器 +``` + +## 功能特性 + +### 已实现的命令 + +1. **help** - 显示帮助信息 +2. **list** - 显示已抓取的文章列表 +3. **crawl** - 开始抓取文章(目前为存根实现) +4. **exit** - 退出程序 + +### 项目架构 + +- **model**: 数据模型层 - 定义文章数据结构 +- **view**: 视图层 - 处理控制台输出 +- **command**: 命令层 - 实现各种命令 +- **controller**: 控制层 - 管理命令执行 + +## 编译运行 + +### 编译项目 + +```bash +cd w9 +mvn clean compile +``` + +### 运行项目 + +```bash +mvn exec:java +``` + +或者先打包再运行: + +```bash +mvn package +java -jar target/crawler-app-1.0-SNAPSHOT.jar +``` + +## 使用示例 + +``` +================================================ + 欢迎使用文章爬虫系统 +================================================ + +可用命令: + help - 显示帮助信息 + list - 显示已抓取的文章列表 + crawl - 开始抓取文章 + exit - 退出程序 + +请输入命令 > help + +可用命令: + help - 显示帮助信息 + list - 显示已抓取的文章列表 + crawl - 开始抓取文章 + exit - 退出程序 + +请输入命令 > crawl + +正在开始抓取文章... +抓取完成,共抓取了 3 篇文章 + +请输入命令 > list + +已抓取的文章列表: +---------------------------------------- +1. 示例文章1 + URL: https://example.com/article1 +---------------------------------------- +2. 示例文章2 + URL: https://example.com/article2 +---------------------------------------- +3. 示例文章3 + URL: https://example.com/article3 +---------------------------------------- +共 3 篇文章 + +请输入命令 > exit + +感谢使用,再见! +``` + +## 技术栈 + +- Java 8 +- Maven 3.x + +## 扩展功能 + +当前 crawl 命令是存根实现,可以扩展为: + +1. 连接实际的文章网站 +2. 解析 HTML 内容 +3. 提取文章标题、URL、内容等 +4. 保存到数据库或文件 \ No newline at end of file diff --git a/w9/RUNNING.md b/w9/RUNNING.md new file mode 100644 index 0000000..f0916d9 --- /dev/null +++ b/w9/RUNNING.md @@ -0,0 +1,167 @@ +# Maven 项目运行说明 + +## 项目已成功创建并测试完成 + +### 项目信息 + +- **项目类型**: Maven Java 项目 +- **包结构**: com.crawler +- **功能**: 命令行文章爬虫系统 + +### 已实现的包结构 + +``` +w9/ +├── pom.xml # Maven 配置文件 +├── README.md # 详细项目文档 +├── test.bat # Windows 测试脚本 +└── src/ + └── main/ + └── java/ + └── com/ + └── crawler/ + ├── Main.java # 主程序入口,包含命令循环 + ├── model/ + │ └── Article.java # 文章数据模型 + ├── view/ + │ └── ConsoleView.java # 控制台视图(用户界面) + ├── command/ + │ ├── Command.java # 命令接口 + │ ├── HelpCommand.java # help 命令实现 + │ ├── ListCommand.java # list 命令实现 + │ ├── CrawlCommand.java # crawl 命令实现 + │ └── ExitCommand.java # exit 命令实现 + └── controller/ + └── CommandController.java # 命令控制器 +``` + +### 已实现的 4 个命令 + +1. **help** - 显示所有可用命令的帮助信息 +2. **list** - 显示已抓取的文章列表(目前为存根) +3. **crawl** - 开始抓取文章(目前为存根实现,添加3篇示例文章) +4. **exit** - 退出程序 + +### 运行方法 + +#### 方法一:使用 Java 编译器直接运行(已测试) + +```bash +# 1. 编译项目 +cd D:\嘻嘻哈哈\Git\java\w9\src\main\java +javac -d ../../../../target/classes -cp . com/crawler/model/Article.java com/crawler/view/ConsoleView.java com/crawler/command/*.java com/crawler/controller/*.java com/crawler/Main.java + +# 2. 运行程序 +java -cp ../../../../target/classes com.crawler.Main +``` + +#### 方法二:使用 Maven 运行(需要配置 JAVA_HOME) + +```bash +# 设置 JAVA_HOME +set JAVA_HOME=你的Java安装路径 + +# 编译项目 +cd w9 +mvn clean compile + +# 运行程序 +mvn exec:java +``` + +### 测试结果 + +程序已成功运行,所有命令都正常工作: + +``` +================================================ + 欢迎使用文章爬虫系统 +================================================ + +可用命令: + help - 显示帮助信息 + list - 显示已抓取的文章列表 + crawl - 开始抓取文章 + exit - 退出程序 + +请输入命令 > help +可用命令: + help - 显示帮助信息 + list - 显示已抓取的文章列表 + crawl - 开始抓取文章 + exit - 退出程序 + +请输入命令 > list +已抓取的文章列表: +---------------------------------------- +暂无已抓取的文章 +共 0 篇文章 + +请输入命令 > crawl +正在开始抓取文章... +抓取完成,共抓取了 3 篇文章 + +请输入命令 > list +已抓取的文章列表: +---------------------------------------- +1. 示例文章1 + URL: https://example.com/article1 +---------------------------------------- +2. 示例文章2 + URL: https://example.com/article2 +---------------------------------------- +3. 示例文章3 + URL: https://example.com/article3 +---------------------------------------- +共 3 篇文章 + +请输入命令 > exit +感谢使用,再见! +``` + +### 项目架构说明 + +#### MVC 架构模式 + +- **Model (模型层)**: `model/Article.java` - 数据模型 +- **View (视图层)**: `view/ConsoleView.java` - 用户界面输出 +- **Controller (控制层)**: `controller/CommandController.java` - 命令调度 + +#### 命令模式 + +- **Command 接口**: 定义命令的基本结构 +- **具体命令类**: HelpCommand, ListCommand, CrawlCommand, ExitCommand +- **命令控制器**: 管理命令的注册和执行 + +### 扩展建议 + +当前 crawl 命令是存根实现,可以扩展为: + +1. **网络爬虫**: 添加 Jsoup 依赖,实现真实的网页爬取 +2. **数据存储**: 将抓取的文章保存到文件或数据库 +3. **多线程**: 使用 ExecutorService 实现并行爬取 +4. **配置管理**: 添加配置文件管理爬取规则 +5. **日志系统**: 使用 SLF4J 添加日志记录 + +### Maven 依赖配置 + +如需添加 Jsoup 依赖,可以在 pom.xml 中添加: + +```xml + + + org.jsoup + jsoup + 1.17.2 + + +``` + +### 总结 + +✅ Maven 项目结构已创建 +✅ 完整的包结构 (model/view/command/controller) 已实现 +✅ 4 个命令已全部实现并测试通过 +✅ list 命令可展示已抓取的文章 +✅ 命令循环运行正常 +✅ 所有文件已保存在 w9 目录中 \ No newline at end of file diff --git a/w9/pom.xml b/w9/pom.xml new file mode 100644 index 0000000..5373944 --- /dev/null +++ b/w9/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.example + crawler-app + 1.0-SNAPSHOT + jar + + + 1.8 + 1.8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + com.crawler.Main + + + + + \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/Main.java b/w9/src/main/java/com/crawler/Main.java new file mode 100644 index 0000000..2f8f83c --- /dev/null +++ b/w9/src/main/java/com/crawler/Main.java @@ -0,0 +1,43 @@ +package com.crawler; + +import com.crawler.command.*; +import com.crawler.controller.CommandController; +import com.crawler.model.Article; +import com.crawler.view.ConsoleView; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Main { + public static void main(String[] args) { + // 初始化组件 + ConsoleView view = new ConsoleView(); + List
articles = new ArrayList<>(); + boolean[] running = {true}; + + // 创建控制器 + CommandController controller = new CommandController(view); + + // 注册命令 + controller.registerCommand(new HelpCommand(view)); + controller.registerCommand(new ListCommand(view, articles)); + controller.registerCommand(new CrawlCommand(view, articles)); + controller.registerCommand(new ExitCommand(view, running)); + + // 显示欢迎信息 + view.showWelcome(); + view.showHelp(); + + // 创建扫描器读取用户输入 + Scanner scanner = new Scanner(System.in); + + // 命令循环 + while (running[0]) { + view.showPrompt(); + String input = scanner.nextLine(); + controller.executeCommand(input); + } + + scanner.close(); + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/command/Command.java b/w9/src/main/java/com/crawler/command/Command.java new file mode 100644 index 0000000..2450b15 --- /dev/null +++ b/w9/src/main/java/com/crawler/command/Command.java @@ -0,0 +1,6 @@ +package com.crawler.command; + +public interface Command { + String getName(); + void execute(); +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/command/CrawlCommand.java b/w9/src/main/java/com/crawler/command/CrawlCommand.java new file mode 100644 index 0000000..4365246 --- /dev/null +++ b/w9/src/main/java/com/crawler/command/CrawlCommand.java @@ -0,0 +1,36 @@ +package com.crawler.command; + +import com.crawler.model.Article; +import com.crawler.view.ConsoleView; +import java.util.List; + +public class CrawlCommand implements Command { + private ConsoleView view; + private List
articles; + + public CrawlCommand(ConsoleView view, List
articles) { + this.view = view; + this.articles = articles; + } + + @Override + public String getName() { + return "crawl"; + } + + @Override + public void execute() { + view.showCrawlStart(); + + // 这里是存根实现,实际可以在这里调用爬虫逻辑 + Article article1 = new Article("示例文章1", "https://example.com/article1"); + Article article2 = new Article("示例文章2", "https://example.com/article2"); + Article article3 = new Article("示例文章3", "https://example.com/article3"); + + articles.add(article1); + articles.add(article2); + articles.add(article3); + + view.showCrawlComplete(articles.size()); + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/command/ExitCommand.java b/w9/src/main/java/com/crawler/command/ExitCommand.java new file mode 100644 index 0000000..f5b5e9b --- /dev/null +++ b/w9/src/main/java/com/crawler/command/ExitCommand.java @@ -0,0 +1,24 @@ +package com.crawler.command; + +import com.crawler.view.ConsoleView; + +public class ExitCommand implements Command { + private ConsoleView view; + private boolean[] running; + + public ExitCommand(ConsoleView view, boolean[] running) { + this.view = view; + this.running = running; + } + + @Override + public String getName() { + return "exit"; + } + + @Override + public void execute() { + view.showGoodbye(); + running[0] = false; + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/command/HelpCommand.java b/w9/src/main/java/com/crawler/command/HelpCommand.java new file mode 100644 index 0000000..5cdf753 --- /dev/null +++ b/w9/src/main/java/com/crawler/command/HelpCommand.java @@ -0,0 +1,21 @@ +package com.crawler.command; + +import com.crawler.view.ConsoleView; + +public class HelpCommand implements Command { + private ConsoleView view; + + public HelpCommand(ConsoleView view) { + this.view = view; + } + + @Override + public String getName() { + return "help"; + } + + @Override + public void execute() { + view.showHelp(); + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/command/ListCommand.java b/w9/src/main/java/com/crawler/command/ListCommand.java new file mode 100644 index 0000000..51b3484 --- /dev/null +++ b/w9/src/main/java/com/crawler/command/ListCommand.java @@ -0,0 +1,25 @@ +package com.crawler.command; + +import com.crawler.model.Article; +import com.crawler.view.ConsoleView; +import java.util.List; + +public class ListCommand implements Command { + private ConsoleView view; + private List
articles; + + public ListCommand(ConsoleView view, List
articles) { + this.view = view; + this.articles = articles; + } + + @Override + public String getName() { + return "list"; + } + + @Override + public void execute() { + view.showArticles(articles); + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/controller/CommandController.java b/w9/src/main/java/com/crawler/controller/CommandController.java new file mode 100644 index 0000000..f311555 --- /dev/null +++ b/w9/src/main/java/com/crawler/controller/CommandController.java @@ -0,0 +1,39 @@ +package com.crawler.controller; + +import com.crawler.command.Command; +import com.crawler.view.ConsoleView; +import java.util.HashMap; +import java.util.Map; + +public class CommandController { + private Map commands; + private ConsoleView view; + + public CommandController(ConsoleView view) { + this.view = view; + this.commands = new HashMap<>(); + } + + public void registerCommand(Command command) { + commands.put(command.getName(), command); + } + + public void executeCommand(String input) { + String commandName = input.trim().toLowerCase(); + + if (commandName.isEmpty()) { + return; + } + + Command command = commands.get(commandName); + if (command != null) { + command.execute(); + } else { + view.showUnknownCommand(commandName); + } + } + + public boolean hasCommand(String commandName) { + return commands.containsKey(commandName); + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/model/Article.java b/w9/src/main/java/com/crawler/model/Article.java new file mode 100644 index 0000000..63e63b5 --- /dev/null +++ b/w9/src/main/java/com/crawler/model/Article.java @@ -0,0 +1,55 @@ +package com.crawler.model; + +public class Article { + private String title; + private String url; + private String content; + private long timestamp; + + public Article(String title, String url) { + this.title = title; + this.url = url; + this.timestamp = System.currentTimeMillis(); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return "Article{" + + "title='" + title + '\'' + + ", url='" + url + '\'' + + ", timestamp=" + timestamp + + '}'; + } +} \ No newline at end of file diff --git a/w9/src/main/java/com/crawler/view/ConsoleView.java b/w9/src/main/java/com/crawler/view/ConsoleView.java new file mode 100644 index 0000000..6ea93db --- /dev/null +++ b/w9/src/main/java/com/crawler/view/ConsoleView.java @@ -0,0 +1,60 @@ +package com.crawler.view; + +import com.crawler.model.Article; +import java.util.List; + +public class ConsoleView { + public void showWelcome() { + System.out.println("================================================"); + System.out.println(" 欢迎使用文章爬虫系统"); + System.out.println("================================================"); + System.out.println(); + } + + public void showPrompt() { + System.out.print("请输入命令 > "); + } + + public void showHelp() { + System.out.println("\n可用命令:"); + System.out.println(" help - 显示帮助信息"); + System.out.println(" list - 显示已抓取的文章列表"); + System.out.println(" crawl - 开始抓取文章"); + System.out.println(" exit - 退出程序"); + System.out.println(); + } + + public void showArticles(List
articles) { + System.out.println("\n已抓取的文章列表:"); + System.out.println("----------------------------------------"); + + if (articles.isEmpty()) { + System.out.println("暂无已抓取的文章"); + } else { + for (int i = 0; i < articles.size(); i++) { + Article article = articles.get(i); + System.out.println((i + 1) + ". " + article.getTitle()); + System.out.println(" URL: " + article.getUrl()); + System.out.println("----------------------------------------"); + } + } + System.out.println("共 " + articles.size() + " 篇文章\n"); + } + + public void showCrawlStart() { + System.out.println("\n正在开始抓取文章..."); + } + + public void showCrawlComplete(int count) { + System.out.println("抓取完成,共抓取了 " + count + " 篇文章\n"); + } + + public void showGoodbye() { + System.out.println("\n感谢使用,再见!"); + } + + public void showUnknownCommand(String command) { + System.out.println("未知命令: " + command); + System.out.println("输入 'help' 查看可用命令\n"); + } +} \ No newline at end of file diff --git a/w9/test.bat b/w9/test.bat new file mode 100644 index 0000000..b0a9a20 --- /dev/null +++ b/w9/test.bat @@ -0,0 +1,10 @@ +@echo off +cd /d "%~dp0src\main\java" +echo help > input.txt +echo list >> input.txt +echo crawl >> input.txt +echo list >> input.txt +echo exit >> input.txt + +java -cp ..\..\..\..\target\classes com.crawler.Main < input.txt +del input.txt \ No newline at end of file