0%

算法竞赛网课和算法书推荐

网课

AcWing - 算法基础课

Acwing 只推荐算法基础课,学完去打 codeforce 跟 Atcoder 打比赛就可以了,多打打比赛就自己入门了。

左程云的个人空间-左程云个人主页-哔哩哔哩视频

课程好,而且免费, https://github.com/algorithmzuo 也有课程全部 PPT 和代码资料。

董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频

这个老师讲的特别好,但是不成体系,可以用来查漏补缺,给的题目质量也很高,代码在老师的博客园也都有。

算法书

其实下面这两本学完就够了。至于网上有人推荐的《算法导论》,《算法图解》,《算法》之类的书,书是好书,但是跟竞赛关系太小。

只买下面这本罗勇军感觉就够了。

  • 罗勇军 《算法竞赛》

  • 刘汝佳《算法竞赛入门经典》

竞赛经验

算法类竞赛

概述

以下比赛均属于同一类比赛 —- 算法类比赛。

这类比赛的赛题形式是一样的。

形式如下 :

题目描述

给你两个整数 a,b,求 a + b

题目输入描述

$1\leq a,b\leq 2147483647$

题目输出描述

输出 $a+b$ 的和

你需要根据你选的编程语言完成这道题目,这里以 C++ 为例 :

1
2
3
4
5
6
7
8
9
10
#include<bits/stdc++.h>
using namespace std;

int main(){
long long a, b;
cin >> a >> b;
cout << a + b;

return 0;
}

赛制分为以下三种:

  • OI

题目存在部分分(即通过几个测试用例取得对应的分数)

不允许在线评测(只能本地评测,没有在线评测机返回结果)

所有提交以最后一次提交为准,按照总得分排名

  • ACM

题目没有部分分(必须通过所有测试用例)

允许在线评测(评测机会返回程序运行结果,例如 结果错误WA代码超时TLE 等,提交错误会罚时)

按照 通过题目数量 + 罚时 来排名

  • IOI

最友好的赛制 :题目具有部分分 + 允许在线评测 + 按照总分排名

关于赛制问题更详细的说明

蓝桥杯大赛(每年下半学期,4月省赛,六月国赛)

大赛官网

蓝桥杯的赛道很多,这里讲的是程序设计竞赛赛道

根据编程语言,程序设计竞赛赛道又分为 C/C++, python 等语言。

选择自己擅长的语言即可,python的优势是赛道压力小,C/C++的优势是作为算法竞赛的主流语言(实际只包括 C++,很少有人用 C),无论学习的资源还是题目的分享,都比较全面。

个人赛,省一晋级国赛。

国赛一、二、三等奖的比例是 10%, 20%, 30%。保底优胜奖。

OI赛制

睿抗机器人开发者大赛(暑假)

同样分为许多赛道,以程序设计赛道为例 :

  • 难度较低,具有语法基础也能拿奖。

  • 省二即可晋级国赛

  • 国赛 99% 获奖率,前 99% 都至少国三(2024年)

团体程序设计天梯赛(每年下半学期)

10 人一组,按照总分排名,IOI赛制。

需要联系学校指导老师报名

百度之星大赛(暑假)

ACM赛制,OIer 比较多,难度较高,推荐 1 ~ 2 年的算法竞赛训练经验。

ACM-ICPC竞赛(每年上半学期)

维基百科-国际大学生程序设计竞赛

现在一般给ACM比赛叫做XCPC类比赛(以前叫ACM是因为这个比赛的赞助商是ACM)。

XCPC又分为 ICPC 跟 CCPC,其中 ICPC 是指 International Collegiate Programming Contest,CCPC是指 China Collegiate Programming Contest。

比赛难度系数高,含金量与认可度也较高,但是投入的时间跟回报很难成正比。

ACM 赛制。

训练方法

算法竞赛的训练方法是一致的,就是刷题。

一个比较好的入门路径是,先学一遍基础算法,刷对应的题目,然后就开始在网上打公开赛,遇到不会的题目,学习对应的算法和专题。

学基础算法,可以看书,也可以看网课。

书的话 :打算学习算法打算法竞赛(acm)哪些书比较推荐? - 知乎

算法竞赛的网课培训网站五花八门,各有优劣 :

Acwing(便宜,质量适中)

代码源(没用过,听说可以)

洛谷(太贵,做初高中OIer培训)

竞赛的网站如下 :

牛客竞赛 : 中文题面,新手期可以做,题目良莠不齐,题目审核不严格

codeforces : 最强大最好用的题库以及比赛平台,英文题目,缺点是东欧时区,在线比赛想打一般都是 22:35 开始,要打到凌晨一两点

AtCoder :类似于 codeforces,日本人创建的,比赛时间友好,题目质量高。

实际上,比赛做 cf 跟 atcoder 就够用了。

经验贴 :

经验贴

又一篇算法竞赛阶段总结和经验分享 - 知乎

算法竞赛小白如何入门? - 知乎

其余竞赛

英语竞赛

词达人,全国大学生英语竞赛,外研社杯

全国大学生数学竞赛

比赛貌似分为初赛跟决赛。难度不在一个量级。

大唐杯

据说考试形式变化大,有时候线下,有时候线上。线上的话,作弊的情况就比较严重了。

其余比赛

山东省科技节一系列赛事。

计算机设计大赛。

挑战杯。

大创。

简介 | Vue.js

cmd 创建 vue3 项目

1
2
3
4
npm create vue@latest
cd vue-project
npm install
npm run dev

整体理解vue3项目

  • 典型的Vue项目,都是在 index.html 这一个单页面里形成各种交互,这也就是所谓的SPA(Single Page Application)

  • Vue3的核心是通过 createAPP 函数创建一个应用实例,在这个实例中构建各种应用。(main.ts中)

  • 每个vue文件就是一个页面上的组件,组件可以嵌套使用

  • vue文件包含 script template style 三部分

双向绑定

数据跟显示分离

controller层

AdminController

  • public Result<?> login(@RequestBody User user, HttpSession session)

    返回管理员登录结果(如果登录成功记录到 session)

    @PostMapping(“/login”)

  • public Result<?> update(@RequestBody Admin admin)

    返回修改结果

    @PutMapping(“/update”)

StudentController

  • public Result<?> add(@RequestBody Student student)

    返回学生登录结果

    @PostMapping(“/add”)

  • public Result<?> update(@RequestBody Student student)

    返回学生信息更新结果

    @PutMapping(“/update”)

  • public Result<?> delete(@PathVariable String username)

    返回删除学生信息结果

    @DeleteMapping(“/delete/{username}”)

  • 查询学生信息

1
2
3
4
public Result<?> findPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String search)

返回查询结果,使用了 Page 工具类

@GetMapping(“/find”)

  • public Result<?> login(@RequestBody User user, HttpSession session)

    学生登录

    @PostMapping(“/login”)

  • public Result<?> stuNum()

    主页顶部:学生统计

    @GetMapping(“/stuNum”)

  • public Result<?> exist(@PathVariable String value)

    床位信息,查询是否存在该学生

    床位信息,查询床位上的学生信息

    @GetMapping(“/exist/{value}”)

DormManagerController

  • public Result<?> add(@RequestBody DormManager dormManager)

    作用 :添加管理员

    返回值 :添加结果

    @PostMapping(“/add”)

  • public Result<?> update(@RequestBody DormManager dormManager)

    作用 :管理员信息更新

    返回值 :更新结果

    @PutMapping(“/update”)

  • public Result<?> delete(@PathVariable String username)

    作用 :删除宿管信息

    返回值 :删除结果

    @DeleteMapping(“/delete/{username}”)

  • 查询宿管信息

1
2
3
4
 public Result<?> findPage(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String search)

返回查询信息的结果,使用了 Page 工具类

  • public Result<?> login(@RequestBody User user, HttpSession session)

    作用 :宿管登录

    返回值 :登录结果(如果登录成功,记录 session)

    @PostMapping(“/login”)

MainController

  • public Result<?> loadIdentity(HttpSession session)

    获取身份信息

    @GetMapping(“/loadIdentity”)

  • public Result<?> loadUserInfo(HttpSession session)

    获取个人信息(从 Session 中获得,登录成功会存入对应的实体类)

    @GetMapping(“/loadUserInfo”)

  • public Result<?> signOut(HttpSession session)

    退出登录(销毁 Session 信息)

    @GetMapping(“/signOut”)

DormRoomController

  • 房间的增删改查

  • 空宿舍统计

  • 删除学生床位信息

  • 查询学生是否已经有床位

  • 统计住宿人数

  • 住宿分布人数

  • 学生功能(我的宿舍)

  • 检查房间是否满员

  • 检查床位是否已经有人

  • 检查房间是否存在

DormBuildController

  • 楼宇的增删改查及获取信息

AdjustRoomController

  • 调宿信息的增删改查

VisitorController

  • 访客信息的增删改查

RepairController

  • 报修信息的增删改查

  • 首页报修统计

  • 个人申请报修的查询

NoticeController

  • 公告的增删改查

  • 首页公告展示

FileController

管理头像文件,在前端并没有看到对应功能

DAO层

DAO层使用的是 BaseMapper 接口,已经提供了常用的 CRUD 操作方法。

貌似使用的 mabatis-Plus 生成的实现类

Service层

主要就是一些简单的 sql 逻辑了。(也不一定)

之后如果要用到类似功能再回来看吧。

总结

还是很典型的三层架构开发,通过这个项目学习了很多 Javaweb 的知识,以后有什么不会的,也可以从这个项目的代码中拿来借鉴。

引入依赖

使用相关注解,需要首先在 pom.xml 引入依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version> <!-- 具体版本号根据需要选择 -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>

使用方法

在引入依赖之后,使用方法如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class Main {
@BeforeEach
void setUp() {
System.out.println("Execute before every Test method");
}

@Test
public void addTest() {
int A = 3, B = 2;
int result = A + B;
assertEquals(5, result, "结果应该是5");
}

@Test
public void subTest() {
int A = 3, B = 2;
int result = A - B;
assertEquals(1, result);
}

public static void main(String[] args) {
// Main method can be used to run other logic if needed, but not for tests
}

@AfterEach
public void tearDown() {
System.out.println("Execute after every Test method");
}
}
  • 注意可以运行每个测试函数,控制台会输入 Test passed的相关信息

  • 右键点击文件夹或者包,选择 Run Tests in xxx 即可运行全部测试方法。

相对路径是相对于基准路径来说的。

idea的基准路径就是项目根目录所在位置。

比如说项目结构如下 :

  • Test

    • .idea

    • src

      • main

        • java

          • com

            • qust

              • data.txt

              • Main.class

      • test

    • target

    • .gitignore

    • pom.xml

    • f.txt

假设写 data.txt,访问方式如下 :

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("src\\main\\java\\com\\qust\\data.txt", true);
fw.append("Hello World\n");
fw.close();
}
}

假设要访问 f.txt,访问方式如下 :

1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("f.txt", true); // 追加内容
fw.append("Hello World");
fw.close();
}
}

HttpSession监听器简介

在Web开发中,Session用于在HTTP协议中维护客户端和服务器之间的状态,解决了HTTP协议无状态的问题。通过Session,服务器可以追踪用户的多次请求,并保持用户的身份信息和相关数据。Session对象通常会在用户首次访问网站时由服务器创建,并与用户的会话持续存在,直到会话结束或Session过期。

Session的生命周期监听

Session的状态管理通常包括以下几个重要的操作和生命周期阶段,可以通过HttpSessionListener来监听和控制:

  1. 创建Session

    当用户首次访问网站时,服务器会自动创建一个Session对象,并与该用户关联。可以通过监听sessionCreated()事件来处理这一时刻的相关操作,例如记录创建时间、初始化数据等。

  2. 销毁Session

    当用户会话超时、主动退出或浏览器关闭时,Session会被销毁。通过监听sessionDestroyed()事件,可以在Session销毁时执行清理操作,如释放资源、记录日志或更新用户状态。

  3. Session超时

    如果用户在一段时间内没有任何操作,服务器会根据设置的超时时间自动销毁Session。监听器中的sessionDestroyed()方法可以用于处理超时销毁的逻辑。

常见的Session监听器接口

  1. HttpSessionListener

    这是最基本的Session监听器,用于监听Session的创建和销毁。

  • sessionCreated(HttpSessionEvent se):当Session创建时调用。
  • sessionDestroyed(HttpSessionEvent se):当Session销毁时调用。
  1. HttpSessionAttributeListener用于监听Session属性的变化,即Session中数据的添加、移除或替换等。
  • attributeAdded(HttpSessionBindingEvent event):当属性添加到Session时调用。
  • attributeRemoved(HttpSessionBindingEvent event):当属性从Session中移除时调用。
  • attributeReplaced(HttpSessionBindingEvent event):当Session中的属性被替换时调用。
    通过这些监听器,开发者可以对用户会话进行细粒度的控制,例如管理用户认证信息、跟踪用户行为、进行会话统计等。

使用Session监听器的实际场景

  1. 用户登录与注销

    在用户登录时,创建Session以保存用户信息;在用户注销时,销毁Session以清除敏感数据。

  2. 购物车管理

    通过Session存储购物车中的商品信息,监听Session的变化来同步更新购物车。

  3. 防止会话劫持

    通过监听Session创建和销毁事件,可以增强对会话的安全性,如重新生成Session ID以防止会话固定攻击。
    总结来说,Session监听是Web开发中的一种重要机制,它使得开发者能够高效且精细地管理用户的会话状态,确保Web应用能正确处理多用户并发访问的需求。

-

Java 泛型概述

Java 泛型(Generics) 是 JDK 1.5 引入的一项功能,允许在类、接口和方法中定义具有参数化类型的代码。泛型通过在编译时进行类型检查,提高了代码的安全性和可读性,减少了类型转换的需求。

泛型的优势

  • 类型安全:在编译时检查类型,避免运行时的 ClassCastException。

  • 代码重用性:支持定义通用的类、接口和方法,适用于多种数据类型。

  • 可读性和维护性:代码更加清晰,减少了显式类型转换的需要。

泛型的基本语法

  • 泛型类
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.qust;

public class Box<T> {
private T item;

public void setItem(T item) {
this.item = item;
}

public T getItem() {
return item;
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello World");
System.out.println(stringBox.getItem());

Box<Integer> intBox = new Box<>();
intBox.setItem(1);
System.out.println(intBox.getItem());
}
}
  • 泛型接口
1
2
3
4
5
// 定义一个泛型接口
public interface Pair {
K getKey();
V getValue();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.qust;

public class KeyValue<K,V> implements Pair<K, V>{
private K key;
private V value;

public KeyValue(K key, V value) {
this.key = key;
this.value = value;
}

@Override
public K getKey() {
return key;
}

@Override
public V getValue() {
return value;
}
}
1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
Pair<String, Integer> pair = new KeyValue<> ("Age", 25);
System.out.println(pair.getKey() + ' ' + pair.getValue());
}
}
  • 泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.qust;

public class GenericMethod {
public static <T> void printArray(T[] array) {
for(T element : array) {
System.out.println(element);
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3};
String[] strArray = {"A", "B", "C"};

printArray(intArray);
printArray(strArray);
}
}

泛型的类型限制

  • 通配符 ?

    • 表示未知类型,可用于读取时不确定具体类型。

      1
      2
      3
      4
      5
      public static void printList(List<?> list) {
      for (Object item : list) {
      System.out.println(item);
      }
      }
  • 边界限定

    • 上界通配符:<? extends T> 表示类型是 T 或其子类。

      1
      2
      3
      4
      5
      public static void printNumbers(List<? extends Number> list) {
      for (Number num : list) {
      System.out.println(num);
      }
      }
    • 下界通配符:<? super T> 表示类型是 T 或其父类。

      1
      2
      3
      public static void addNumbers(List<? super Integer> list) {
      list.add(1); // 允许添加 Integer 或其子类
      }
  • 类型参数边界

    • 限制泛型的范围。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class Box<T extends Number> {
      private T item;

      public void setItem(T item) {
      this.item = item;
      }

      public T getItem() {
      return item;
      }
      }

泛型的注意事项

  • 类型擦除

    • Java 泛型是通过类型擦除实现的,编译器在编译时会将泛型信息去掉,转换为原始类型(如 Object)。

      • 因此,不能在运行时获取泛型类型参数的信息,例如 List 和 List 在运行时是相同的。
  • 泛型不能用于静态字段或方法

    • 由于泛型类型在编译时确定,而静态成员属于类本身,与实例无关,因此泛型不能应用于静态成员。

      1
      2
      3
      4
      5
      6
      7
      public class GenericClass {
      // 静态变量不能使用泛型
      // private static T staticVar;

      // 静态方法也不能使用泛型
      // public static T getStaticValue() { return null; }
      }
  • 不能创建泛型数组

    1
    2
    // 不允许直接创建泛型数组
    List[] array = new ArrayList[10]; // 编译错误
  • 原始类型

1
2
3
Res res = new Res();
res.a = "Hello"; // 编译通过
res.a = 123; // 仍然编译通过,丢失了类型安全

没有指定泛型的数据类型,导致类型不安全。

常见应用场景

  • 集合框架:Java 的集合框架(如 ArrayList、HashMap 等)广泛使用了泛型。

  • 工具类:如 java.util.Collections 提供了许多泛型方法。

  • 类型安全容器:泛型可以实现自定义的类型安全容器。

@RestController

@RestController 是 Spring 框架中用来标注一个类为 RESTful Web服务控制器 的注解。它是 @Controller@ResponseBody 的组合,主要用来处理 HTTP 请求并返回数据而不是视图。

作用与特点:

  1. 定义控制器类将类标记为一个控制器,用于接收和处理 HTTP 请求。
  2. 自动将返回值转换为 JSON/XML:@RestController 自动将控制器方法的返回值序列化为 JSON 或 XML 格式,通过 HTTP 响应返回给客户端。
  • 这是因为它隐含了 @ResponseBody 注解。
  1. 简化代码无需在每个方法上显式使用 @ResponseBody,整个类的所有方法默认都将返回数据而不是视图。
    常见用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyRestController {

// GET 请求:返回一个简单字符串
@GetMapping("/hello")
public String sayHello() {
return "Hello, World!";
}

// GET 请求:返回一个对象,自动序列化为 JSON
@GetMapping("/user")
public User getUser() {
return new User(1, "Alice", "alice@example.com");
}
}

示例解析:

  1. /api/hello 返回的是字符串 “Hello, World!”,被序列化为 HTTP 响应体。

  2. /api/user 返回的是 User 对象,会自动被序列化为 JSON,例如:

    1
    2
    3
    4
    5
    {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com"
    }

    等效写法:
    如果使用 @Controller,需要配合 @ResponseBody,代码会显得冗长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
@RequestMapping("/api")
public class MyRestController {

@ResponseBody
@GetMapping("/hello")
public String sayHello() {
return "Hello, World!";
}

@ResponseBody
@GetMapping("/user")
public User getUser() {
return new User(1, "Alice", "alice@example.com");
}
}

总结:@RestController 适用于构建 RESTful API,简化开发流程,是 Spring Web 的重要注解之一。如果你的控制器方法大部分是返回数据而不是视图,使用 @RestController 会非常方便。

@Data 注解

  • 作用: 自动生成以下方法:Getter 和 Setter 方法(为所有成员变量)toString() 方法equals() 和 hashCode() 方法canEqual() 方法(用于子类验证)

  • 优势: 减少手写 Getter、Setter 等方法的繁琐代码。

  • 等效代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class Result {
    private String code;
    private String msg;
    private T data;

    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }

    public String getMsg() { return msg; }
    public void setMsg(String msg) { this.msg = msg; }

    public T getData() { return data; }
    public void setData(T data) { this.data = data; }

    @Override
    public String toString() {
    return "Result{" +
    "code='" + code + '\'' +
    ", msg='" + msg + '\'' +
    ", data=" + data +
    '}';
    }

    @Override
    public boolean equals(Object o) {
    // equals 方法实现
    }

    @Override
    public int hashCode() {
    // hashCode 方法实现
    }
    }

@AllArgsConstructor注解

  • 作用: 自动生成一个包含所有成员变量的构造方法。

  • 生成的代码:

    1
    2
    3
    4
    5
    public Result(String code, String msg, T data) {
    this.code = code;
    this.msg = msg;
    this.data = data;
    }
  • 用途: 方便在创建对象时直接初始化所有属性。例如:

    1
    Result result = new Result<>("0", "成功", "数据");

@NoArgsConstructor 注解

  • 作用: 自动生成一个无参构造方法。

  • 生成的代码:

    1
    public Result() {}
  • 用途: 提供无参构造器支持,例如需要通过反射或框架(如 Spring)创建对象时。

    1
    Result result = new Result<>();

@Resource注解

@Resource 是 Java 中用于依赖注入的一个注解,它主要用于自动装配资源(如 Bean 或组件),在 Java EE(现在的 Jakarta EE)和 Spring 等框架中得到广泛使用。该注解通常用于标记成员变量、方法或构造函数,指示容器(如 Spring 或应用服务器)需要自动注入指定的资源。

主要特性:

  • 自动注入:@Resource 自动装配资源,可以通过名称或类型来注入。

  • 名称默认匹配:@Resource 默认根据字段名或方法名来进行资源名称匹配。如果没有找到匹配的名称,它会根据类型来注入。

  • 可以用于字段、方法、构造函数:可以用于类的成员变量(字段)、setter 方法或构造函数。

    使用示例:

1. 注入字段:

1
2
3
4
5
6
7
8
9
import javax.annotation.Resource;

public class MyService {

@Resource
private DataSource dataSource;

// 其他代码...
}

在这个例子中,@Resource 注解会根据 dataSource 字段的名称自动注入一个 DataSource 类型的资源。

2. 注入方法:

1
2
3
4
5
6
7
8
9
10
11
import javax.annotation.Resource;

public class MyService {

private DataSource dataSource;

@Resource
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}

在这个例子中,@Resource 注解被用在了 setter 方法上,容器会自动注入所需的 DataSource

3. 注入构造函数:

1
2
3
4
5
6
7
8
9
10
11
import javax.annotation.Resource;

public class MyService {

private DataSource dataSource;

@Resource
public MyService(DataSource dataSource) {
this.dataSource = dataSource;
}
}

在这个例子中,@Resource 注解应用于构造函数,自动注入 DataSource

注解的属性:

  • name:指定资源的名称,用于自定义资源名称的匹配。例如:@Resource(name=”myDataSource”)

  • type:指定资源的类型,通常不需要显式设置,容器会根据字段的类型自动注入。

    与 @Autowired 比较:

  • @Autowired 是 Spring 框架提供的注解,它主要通过类型来进行依赖注入。与 @Resource 注解相比,@Autowired 主要依据类型进行注入,并且在没有找到唯一匹配的 Bean 时会抛出异常,除非配合 @Qualifier 使用指定 Bean 名称。

  • @Resource 默认通过名称进行注入,只有在没有找到合适的名称匹配时才会通过类型来进行匹配。

在 Java 中,注释的规范化不仅能够提高代码的可读性,还能帮助开发团队更好地维护项目。以下是 Java 注释的规范和建议:

1. 注释的基本类型

Java 支持以下三种类型的注释:

  • 单行注释:使用 // 开头。

  • 多行注释:使用 /* … */。

  • 文档注释:使用 /** … */,通常用于生成 JavaDoc 文档。

    2. 注释的用途

必要时注释,而非注释所有内容

  • 注释的重点:注释应该解释 “为什么”(逻辑、设计意图),而不是 “怎么做”(代码本身已经解释 “怎么做”)。

  • 避免冗余注释:代码本身清晰易懂时不需要注释。

    3. 单行注释规范

  • 用于描述简单的逻辑或特殊处理

  • 位置:通常放在代码行的上方,也可放在代码右侧(不建议太长)。

    1
    2
    3
    4
    // 检查用户是否已登录
    if (user.isLoggedIn()) {
    System.out.println("User is logged in."); // 输出登录信息
    }

4. 多行注释规范

  • 用于描述复杂逻辑屏蔽代码块(屏蔽代码块仅在调试时临时使用,调试完成后应删除)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /*
    * 以下代码段用于初始化数据库连接,
    * 包括加载驱动程序、创建连接以及设置属性。
    */
    try {
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
    } catch (Exception e) {
    e.printStackTrace();
    }

5. 文档注释规范

  • 用于类、方法、接口、枚举等 API 元素的说明

  • 工具:可以使用 IDE 生成 JavaDoc 或通过 javadoc 工具生成 HTML 文档。

    文档注释模板

1
2
3
4
5
6
7
8
9
10
/**
* 描述类/方法的功能。
*
* @author 作者
* @version 版本
* @since 起始版本
* @param 参数名 参数说明(仅针对方法)
* @return 返回值说明(仅针对方法)
* @throws 异常类型 异常说明(仅针对方法)
*/

文档注释示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 用户类,包含用户的基本信息。
*
* @author Wei
* @version 1.0
* @since 2025-01-08
*/
public class User {

/**
* 用户名。
*/
private String username;

/**
* 获取用户名。
*
* @return 用户名。
*/
public String getUsername() {
return username;
}

/**
* 设置用户名。
*
* @param username 新用户名。
* @throws IllegalArgumentException 如果用户名为空。
*/
public void setUsername(String username) {
if (username == null || username.isEmpty()) {
throw new IllegalArgumentException("用户名不能为空!");
}
this.username = username;
}
}

6. 注释规范建议

  1. 保持一致性:确保整个项目的注释风格统一。
  2. 避免过度注释:注释应恰到好处,避免解释每一行代码。
  3. 及时更新注释:修改代码时,确保注释与代码一致。
  4. 语言清晰简洁:使用专业术语,避免冗长复杂的描述。
  5. 注释内容格式化:采用合理的缩进和换行,使注释结构清晰。
    通过遵循上述规范,可以显著提高 Java 代码的可维护性和团队协作效率。