Featured image of post 【合集】Java常用代码

【合集】Java常用代码

Java集合

Guava

【合集】EasyExcel-写出excel

【合集】EasyExcel-读取excel

【合集】EasyPOI-写出excel

【合集】EasyPOI-读取excel

Java并发编程

Json使用

Java枚举类示例

 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 lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;

/**
 * 用户状态
 */
@Getter
@RequiredArgsConstructor
public enum UserStatusEnum {
   ACTIVE("ACTIVE", "活跃"),
   INACTIVE("INACTIVE", "不活跃"),
   DELETED("DELETED", "已删除"),
   OTHER("OTHER", "其他"),
   ;

   private final String code;
   private final String desc;

   public static Optional<UserStatusEnum> getByCode(String code) {
      return Arrays.stream(values())
              .filter(e -> Objects.equals(e.code, code))
              .findFirst();
   }

   public static Optional<UserStatusEnum> getByDesc(String desc) {
      return Arrays.stream(values())
              .filter(e -> Objects.equals(e.desc, desc))
              .findFirst();
   }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public enum SeasonEnum {
    SPRING, SUMMER, AUTUMN, WINTER;
}

// 枚举类的编译以后源代码:
//public final class Season extends java.lang.Enum<Season> {
//    public static final Season SPRING = new Season();
//    public static final Season SUMMER = new Season();
//    public static final Season AUTUMN = new Season();
//    public static final Season WINTER = new Season();
//
//    public static Season[] values();
//    public static Season valueOf(java.lang.String);
//}

阿里巴巴Java开发手册错误码-枚举示例

  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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 阿里巴巴Java开发手册-崇山版-2020.08.03 错误码整理
 * 错误码:
 * 1. 五位组成
 * 2. A代表用户端错误
 * 3. B代表当前系统异常
 * 4. C代表第三方服务异常
 * 4. 若无法确定具体错误,选择宏观错误,如A0001:一级宏观错误码 A0100:二级宏观错误码
 * 6. 大的错误类间的步长间距预留100
 */
@Getter
@AllArgsConstructor
public enum ErrorCodeEnum {

    // 成功
    SUCCESS("00000", "一切 ok"),
    ERROR("99999", "未归类的错误"),

    // 用户端错误
    USER_ERROR_A0001("A0001", "用户端错误"),
    // A0100
    USER_ERROR_A0100("A0100", "用户注册错误"),
    USER_ERROR_A0101("A0101", "用户未同意隐私协议"),
    USER_ERROR_A0102("A0102", "注册国家或地区受限"),
    USER_ERROR_A0110("A0110", "用户名校验失败"),
    USER_ERROR_A0111("A0111", "用户名已存在"),
    USER_ERROR_A0112("A0112", "用户名包含敏感词"),
    USER_ERROR_A0113("A0113", "用户名包含特殊字符"),
    USER_ERROR_A0120("A0120", "密码校验失败"),
    USER_ERROR_A0121("A0121", "密码长度不够"),
    USER_ERROR_A0122("A0122", "密码强度不够"),
    USER_ERROR_A0130("A0130", "校验码输入错误"),
    USER_ERROR_A0131("A0131", "短信校验码输入错误"),
    USER_ERROR_A0132("A0132", "邮件校验码输入错误"),
    USER_ERROR_A0133("A0133", "语音校验码输入错误"),
    USER_ERROR_A0140("A0140", "用户证件异常"),
    USER_ERROR_A0141("A0141", "用户证件类型未选择"),
    USER_ERROR_A0142("A0142", "大陆身份证编号校验非法"),
    USER_ERROR_A0143("A0143", "护照编号校验非法"),
    USER_ERROR_A0144("A0144", "军官证编号校验非法"),
    USER_ERROR_A0150("A0150", "用户基本信息校验失败"),
    USER_ERROR_A0151("A0151", "手机格式校验失败"),
    USER_ERROR_A0152("A0152", "地址格式校验失败"),
    USER_ERROR_A0153("A0153", "邮箱格式校验失败"),
    // A0200
    USER_ERROR_A0200("A0200", "用户登录异常"),
    USER_ERROR_A0201("A0201", "用户账户不存在"),
    USER_ERROR_A0202("A0202", "用户账户被冻结"),
    USER_ERROR_A0203("A0203", "用户账户已作废"),
    USER_ERROR_A0210("A0210", "用户密码错误"),
    USER_ERROR_A0211("A0211", "用户输入密码错误次数超限"),
    USER_ERROR_A0220("A0220", "用户身份校验失败"),
    USER_ERROR_A0221("A0221", "用户指纹识别失败"),
    USER_ERROR_A0222("A0222", "用户面容识别失败"),
    USER_ERROR_A0223("A0223", "用户未获得第三方登录授权"),
    USER_ERROR_A0230("A0230", "用户登录已过期"),
    USER_ERROR_A0240("A0240", "用户验证码错误"),
    USER_ERROR_A0241("A0241", "用户验证码尝试次数超限"),
    // A0300
    USER_ERROR_A0300("A0300", "访问权限异常"),
    USER_ERROR_A0301("A0301", "访问未授权"),
    USER_ERROR_A0302("A0302", "正在授权中"),
    USER_ERROR_A0303("A0303", "用户授权申请被拒绝"),
    USER_ERROR_A0310("A0310", "因访问对象隐私设置被拦截"),
    USER_ERROR_A0311("A0311", "授权已过期"),
    USER_ERROR_A0312("A0312", "无权限使用 API"),
    USER_ERROR_A0320("A0320", "用户访问被拦截"),
    USER_ERROR_A0321("A0321", "黑名单用户"),
    USER_ERROR_A0322("A0322", "账号被冻结"),
    USER_ERROR_A0323("A0323", "非法 IP 地址"),
    USER_ERROR_A0324("A0324", "网关访问受限"),
    USER_ERROR_A0325("A0325", "地域黑名单"),
    USER_ERROR_A0330("A0330", "服务已欠费"),
    USER_ERROR_A0340("A0340", "用户签名异常"),
    USER_ERROR_A0341("A0341", "RSA 签名错误"),
    // A0400
    USER_ERROR_A0400("A0400", "用户请求参数错误"),
    USER_ERROR_A0401("A0401", "包含非法恶意跳转链接"),
    USER_ERROR_A0402("A0402", "无效的用户输入"),
    USER_ERROR_A0410("A0410", "请求必填参数为空"),
    USER_ERROR_A0411("A0411", "用户订单号为空"),
    USER_ERROR_A0412("A0412", "订购数量为空"),
    USER_ERROR_A0413("A0413", "缺少时间戳参数"),
    USER_ERROR_A0414("A0414", "非法的时间戳参数"),
    USER_ERROR_A0420("A0420", "请求参数值超出允许的范围"),
    USER_ERROR_A0421("A0421", "参数格式不匹配"),
    USER_ERROR_A0422("A0422", "地址不在服务范围"),
    USER_ERROR_A0423("A0423", "时间不在服务范围"),
    USER_ERROR_A0424("A0424", "金额超出限制"),
    USER_ERROR_A0425("A0425", "数量超出限制"),
    USER_ERROR_A0426("A0426", "请求批量处理总个数超出限制"),
    USER_ERROR_A0427("A0427", "请求 JSON 解析失败"),
    USER_ERROR_A0430("A0430", "用户输入内容非法"),
    USER_ERROR_A0431("A0431", "包含违禁敏感词"),
    USER_ERROR_A0432("A0432", "图片包含违禁信息"),
    USER_ERROR_A0433("A0433", "文件侵犯版权"),
    USER_ERROR_A0440("A0440", "用户操作异常"),
    USER_ERROR_A0441("A0441", "用户支付超时"),
    USER_ERROR_A0442("A0442", "确认订单超时"),
    USER_ERROR_A0443("A0443", "订单已关闭"),
    USER_ERROR_A0444("A0444", "名称不能为空"),
    // A0500
    USER_ERROR_A0500("A0500", "用户请求服务异常"),
    USER_ERROR_A0501("A0501", "请求次数超出限制"),
    USER_ERROR_A0502("A0502", "请求并发数超出限制"),
    USER_ERROR_A0503("A0503", "用户操作请等待"),
    USER_ERROR_A0504("A0504", "WebSocket 连接异常"),
    USER_ERROR_A0505("A0505", "WebSocket 连接断开"),
    USER_ERROR_A0506("A0506", "用户重复请求"),
    // A0600
    USER_ERROR_A0600("A0600", "用户资源异常"),
    USER_ERROR_A0601("A0601", "账户余额不足"),
    USER_ERROR_A0602("A0602", "用户磁盘空间不足"),
    USER_ERROR_A0603("A0603", "用户内存空间不足"),
    USER_ERROR_A0604("A0604", "用户 OSS 容量不足"),
    USER_ERROR_A0605("A0605", "用户配额已用光"),
    // A0700
    USER_ERROR_A0700("A0700", "用户上传文件异常"),
    USER_ERROR_A0701("A0701", "用户上传文件类型不匹配"),
    USER_ERROR_A0702("A0702", "用户上传文件太大"),
    USER_ERROR_A0703("A0703", "用户上传图片太大"),
    USER_ERROR_A0704("A0704", "用户上传视频太大"),
    USER_ERROR_A0705("A0705", "用户上传压缩文件太大"),
    // A0800
    USER_ERROR_A0800("A0800", "用户当前版本异常"),
    USER_ERROR_A0801("A0801", "用户安装版本与系统不匹配"),
    USER_ERROR_A0802("A0802", "用户安装版本过低"),
    USER_ERROR_A0803("A0803", "用户安装版本过高"),
    USER_ERROR_A0804("A0804", "用户安装版本已过期"),
    USER_ERROR_A0805("A0805", "用户 API 请求版本不匹配"),
    USER_ERROR_A0806("A0806", "用户 API 请求版本过高"),
    USER_ERROR_A0807("A0807", "用户 API 请求版本过低"),
    // A0900
    USER_ERROR_A0900("A0900", "用户隐私未授权"),
    USER_ERROR_A0901("A0901", "用户隐私未签署"),
    USER_ERROR_A0902("A0902", "用户摄像头未授权"),
    USER_ERROR_A0903("A0903", "用户相机未授权"),
    USER_ERROR_A0904("A0904", "用户图片库未授权"),
    USER_ERROR_A0905("A0905", "用户文件未授权"),
    USER_ERROR_A0906("A0906", "用户位置信息未授权"),
    USER_ERROR_A0907("A0907", "用户通讯录未授权"),
    // A1000
    USER_ERROR_A1000("A1000", "用户设备异常"),
    USER_ERROR_A1001("A1001", "用户相机异常"),
    USER_ERROR_A1002("A1002", "用户麦克风异常"),
    USER_ERROR_A1003("A1003", "用户听筒异常"),
    USER_ERROR_A1004("A1004", "用户扬声器异常"),
    USER_ERROR_A1005("A1005", "用户 GPS 定位异常"),

    // 系统执行出错
    SYSTEM_ERROR_B0001("B0001", "系统执行出错"),
    // B0100
    SYSTEM_ERROR_B0100("B0100", "系统执行超时"),
    SYSTEM_ERROR_B0101("B0101", "系统订单处理超时"),
    // B0200
    SYSTEM_ERROR_B0200("B0200", "系统容灾功能被触发"),
    SYSTEM_ERROR_B0210("B0210", "系统限流"),
    SYSTEM_ERROR_B0220("B0220", "系统功能降级"),
    // B0300
    SYSTEM_ERROR_B0300("B0300", "系统资源异常"),
    SYSTEM_ERROR_B0310("B0310", "系统资源耗尽"),
    SYSTEM_ERROR_B0311("B0311", "系统磁盘空间耗尽"),
    SYSTEM_ERROR_B0312("B0312", "系统内存耗尽"),
    SYSTEM_ERROR_B0313("B0313", "文件句柄耗尽"),
    SYSTEM_ERROR_B0314("B0314", "系统连接池耗尽"),
    SYSTEM_ERROR_B0315("B0315", "系统线程池耗尽"),
    SYSTEM_ERROR_B0320("B0320", "系统资源访问异常"),
    SYSTEM_ERROR_B0321("B0321", "系统读取磁盘文件失败"),

    // 调用第三方服务出错
    SERVICE_ERROR_C0001("C0001", "调用第三方服务出错"),
    // C0100
    SERVICE_ERROR_C0100("C0100", "中间件服务出错"),
    SERVICE_ERROR_C0110("C0110", "RPC 服务出错"),
    SERVICE_ERROR_C0111("C0111", "RPC 服务未找到"),
    SERVICE_ERROR_C0112("C0112", "RPC 服务未注册"),
    SERVICE_ERROR_C0113("C0113", "接口不存在"),
    SERVICE_ERROR_C0120("C0120", "消息服务出错"),
    SERVICE_ERROR_C0121("C0121", "消息投递出错"),
    SERVICE_ERROR_C0122("C0122", "消息消费出错"),
    SERVICE_ERROR_C0123("C0123", "消息订阅出错"),
    SERVICE_ERROR_C0124("C0124", "消息分组未查到"),
    SERVICE_ERROR_C0130("C0130", "缓存服务出错"),
    SERVICE_ERROR_C0131("C0131", "key 长度超过限制"),
    SERVICE_ERROR_C0132("C0132", "value 长度超过限制"),
    SERVICE_ERROR_C0133("C0133", "存储容量已满"),
    SERVICE_ERROR_C0134("C0134", "不支持的数据格式"),
    SERVICE_ERROR_C0140("C0140", "配置服务出错"),
    SERVICE_ERROR_C0150("C0150", "网络资源服务出错"),
    SERVICE_ERROR_C0151("C0151", "VPN 服务出错"),
    SERVICE_ERROR_C0152("C0152", "CDN 服务出错"),
    SERVICE_ERROR_C0153("C0153", "域名解析服务出错"),
    SERVICE_ERROR_C0154("C0154", "网关服务出错"),
    // C0200
    SERVICE_ERROR_C0200("C0200", "第三方系统执行超时"),
    SERVICE_ERROR_C0210("C0210", "RPC 执行超时"),
    SERVICE_ERROR_C0220("C0220", "消息投递超时"),
    SERVICE_ERROR_C0230("C0230", "缓存服务超时"),
    SERVICE_ERROR_C0240("C0240", "配置服务超时"),
    SERVICE_ERROR_C0250("C0250", "数据库服务超时"),
    // C0300
    SERVICE_ERROR_C0300("C0300", "数据库服务出错"),
    SERVICE_ERROR_C0311("C0311", "表不存在"),
    SERVICE_ERROR_C0312("C0312", "列不存在"),
    SERVICE_ERROR_C0321("C0321", "多表关联中存在多个相同名称的列"),
    SERVICE_ERROR_C0331("C0331", "数据库死锁"),
    SERVICE_ERROR_C0341("C0341", "主键冲突"),
    // C0400
    SERVICE_ERROR_C0400("C0400", "第三方容灾系统被触发"),
    SERVICE_ERROR_C0401("C0401", "第三方系统限流"),
    SERVICE_ERROR_C0402("C0402", "第三方功能降级"),
    // C0500
    SERVICE_ERROR_C0500("C0500", "通知服务出错"),
    SERVICE_ERROR_C0501("C0501", "短信提醒服务失败"),
    SERVICE_ERROR_C0502("C0502", "语音提醒服务失败"),
    SERVICE_ERROR_C0503("C0503", "邮件提醒服务失败");

    private final String errorCode;
    private final String errorMessage;
}

Java手机号正则校验

示例1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * 手机号校验
 * @param mobileNumber
 */
public static boolean mobileValidate(String mobileNumber) {
    String regex = "^1[3456789]\\d{9}$";
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(mobileNumber);
    return matcher.matches();
}

示例2

 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
public class MobileNumberValidator {  
  
    // 定义正则表达式  
    private static final String MOBILE_REGEX = "^1(3|4|5|6|7|8|9)\\d{9}$";  
  
    /**  
     * 校验手机号  
     *   
     * @param mobile 手机号  
     * @return 是否为合法的手机号  
     */  
    public static boolean isValidMobile(String mobile) {  
        if (mobile == null || mobile.trim().isEmpty()) {  
            return false;  
        }  
        return mobile.matches(MOBILE_REGEX);  
    }  
  
    public static void main(String[] args) {  
        String[] mobiles = {"13800138000", "12345678901", "14512345678", "17612345678", "not a mobile", null, ""};  
        for (String mobile : mobiles) {  
            System.out.println(mobile + " is valid mobile number? " + isValidMobile(mobile));  
        }  
    }  
}

Java校验车牌号格式

 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
public class LicensePlateValidator {  
  
    // 正则表达式解释:  
    // ^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z·]{0,1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$  
    // ^ 表示字符串开始  
    // [京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领] 表示省份简称  
    // [A-Z]{1} 表示第一个字母(新能源车牌可能不含此部分)  
    // [A-Z·]{0,1} 表示新能源车牌中的点(可选),普通车牌则不包含  
    // [A-Z0-9]{4} 表示接下来的4位字母或数字  
    // [A-Z0-9挂学警港澳]{1} 表示最后一位字母、数字或特殊字符(挂、学、警、港、澳)  
    // $ 表示字符串结束  
  
    private static final String LICENSE_PLATE_REGEX = "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z·]{0,1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$";  
  
    /**  
     * 校验车牌号  
     *   
     * @param licensePlate 车牌号  
     * @return 是否为合法的车牌号  
     */  
    public static boolean isValidLicensePlate(String licensePlate) {  
        if (licensePlate == null || licensePlate.trim().isEmpty()) {  
            return false;  
        }  
        return licensePlate.matches(LICENSE_PLATE_REGEX);  
    }  
  
    public static void main(String[] args) {  
        String[] licensePlates = {"京A12345", "沪A·1234D", "川A1234挂", "京A1234学", "粤B12345港", "不存在的车牌", "京A123", "京A123456"};  
        for (String licensePlate : licensePlates) {  
            System.out.println(licensePlate + " is valid? " + isValidLicensePlate(licensePlate));  
        }  
    }  
}

注意:

  1. 上述正则表达式假设省份简称和特殊字符(如“挂”、“学”、“警”、“港”、“澳”)都是大写。如果实际情况中可能包含小写,请将对应的部分修改为不区分大小写的正则表达式(Java中可以通过Pattern.CASE_INSENSITIVE标志来实现,但这里为了简单起见没有包含)。
  2. 新能源车牌中的点(·)在正则表达式中可能需要根据实际编码进行转义(在某些环境下可能需要写作\u00B7或其他形式的转义序列)。上面的示例中直接使用了·字符,这可能在某些环境下无法正常工作。
  3. 该正则表达式可能无法覆盖所有特殊情况,如临时车牌、军用车牌、警用车牌等,这些车牌的格式与普通车牌和新能源车牌有所不同。如果需要校验这些特殊车牌,请根据实际情况调整正则表达式。

OkHttp3请求示例

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>5.0.0-alpha.14</version>
</dependency>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
private final OkHttpClient client = new OkHttpClient();

HttpUrl url = HttpUrl.get("https://api.weixin.qq.com/wxa/getwxacodeunlimit").newBuilder()
            .addQueryParameter("access_token", "accessToken")
            .build();
Map<String, String> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
String jsonString = new ObjectMapper().writeValueAsString(map);
RequestBody requestBody = RequestBody.create(jsonString, MediaType.parse("application/json"));
Headers headers = new Headers.Builder()
        .add("Authorization", "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImUwMzAyNDI5LTZmMzEtNDM2Ny1iNDk1LWIxZGY3MTM2ZTQ0NSJ9.QtRJtXAI-yUc39QsfWgpL3WoHi7RGDG1IOBkfyLLSE_wDWICp6PDtf9bzoSV7vi2SkQUHi3ndE75nNWwUbKeMA")
        .add("User-Agent", "Apifox/1.0.0 (https://www.apifox.cn)")
        .add("Accept", "*/*")
        .add("Host", "localhost:8600")
        .add("Connection", "keep-alive")
        .add("Content-Type", "application/json")
        .build();
Response response = client.newCall(new Request.Builder().url(url).build()).execute();
Response response2 = client.newCall(new Request.Builder().url(url).post(requestBody).headers(headers).build()).execute();

Java测试NullPointerException空指针异常

 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
class NPE {

    public static void main(String[] args) {
        List<User> nulls = null;
        Object[] arr = null;

        // NPE
        Arrays.stream(arr);
        nulls.stream();
        nulls.forEach(u -> reverseOrder());
        for (User user : nulls) {
        }
        // null流在map等操作NPE
        Lists.newArrayList(null, new User()).stream()
                .map(User::getAge);
        // 如果value为null,Collectors.toMap会调用Map.merge方法,而在这个方法中,null值会导致抛出NullPointerException异常。
        Lists.newArrayList(User.builder().age(1).name(null).build(),
                        User.builder().age(1).name(null).build()).stream()
                .collect(toMap(User::getAge, User::getName, (s1, s2) -> s1));
        // 可以加上.filter(s -> s.getName() != null)

        // 不会NPE
        Collections.emptyList().stream();
        Collections.emptyList().forEach(u -> reverseOrder());
        for (Object o : Collections.emptyList()) {
        }
        Optional.ofNullable(nulls).orElse(Collections.emptyList()).forEach(s -> {

        });

    }
}

IntelliJ IDEA - HTTP Client 插件http请求示例GET POST PUT DELETE

环境

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "dev": {
    "host": "http://localhost:8080",
    "deviceNo": "28b222012b46332609c45ad4e6b29061b",
    "authorization": "eyJhbGciOiJIUzUxMiJ9"
  },
  "fat": {
    "host": "http://localhost:8080",
    "deviceNo": "28b222012b46332609c45ad4e6b29061b",
    "authorization": "eyJhbGciOiJIUzUxMiJ9"
  }
}

GET

1
2
3
4
### GET请求
GET {{host}}/mobile/getUserList?pageNum=1&pageSize=10
authorization: {{authorization}}
deviceNo: {{deviceNo}}

POST

1
2
3
4
5
6
7
8
### POST请求
POST {{host}}/mobile/switchStatus
authorization: {{authorization}}
token: 15b5f8e4212d4a108a5b31a224ff269b
deviceNo: {{deviceNo}}
Content-Type: application/json

{"userId":"123","status":false}

java.math使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 格式化成货币 1 -> "¥1.00"
String format1 = NumberFormat.getCurrencyInstance().format(bigDecimal);
// 格式化成百分比 1 -> "100%"
String format2 = NumberFormat.getPercentInstance().format(bigDecimal);

// 绝对值
int abs = Math.abs(-3); // 3
// 向上取整
double ceil = Math.ceil(4.00000001); // 5.0
// 向下取整
double floor = Math.floor(4.99999999); // 4.0
// 四舍五入取整
long round = Math.round(4.49999); // 4
long round1 = Math.round(4.500000); // 5
// 获取a的b次幂
Math.pow(2, 3); // 2^3 = 8.0

// 字符串支持16进制字符串,并且会自动去除字符串中的空格:
NumberUtils.parseNumber("1", Integer.class);
// 自动处理数字溢出(抛出异常)
NumberUtils.convertNumberToTargetClass(1, Integer.class);

类的五大成分:成员变量,方法,构造器,代码块,内部类。

静态内部类

有static修饰,类有的成分它都有,属于外部类本身,只会加载一次。 所以它的特点与外部类是完全一样的,只是位置在别人里面而已。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Outter {
    public static class Inner {
    }
}

// 使用
Outter.Inner in = new Outter.Inner();
in.setName("张三");
System.out.println(in.getName());
in.show();

实例内部类。(成员内部类)

无static修饰的内部类,属于外部类的每个对象的,跟着对象一起加载的。 实例内部类中不能定义静态成员,其他都可以定义。可以定义常量。

 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
class Outter {
    public static int age = 1;
    private double salary;

    // 实例内部类:无static修饰,属于外部类的对象
    public class Inner {
        private String name;

        public static final String schoolName = "黑马";
        // 不能在实例内部类中定义静态成员!!!
//      public static String schoolName = "黑马";
//      public static void test(){
//
//      }

        // 实例方法
        public void show() {
            System.out.println(name + "名称!");
            System.out.println(age);
            System.out.println(salary);
        }
    }
}

// 使用
Outter.Inner in = new Outter().new Inner();
in.show();

局部内部类(几乎不用,局部内部类没啥用)

定义在方法中,在构造器中,代码块中,for循环中定义的内部类。 只能定义实例成员,不能定义静态成员,可以定义常量的。

 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
public class InnerClass {

    static {
        abstract class A {
        }
    }

    public static void main(String[] args) {
        class A {
            private String name;

            public void test() {
            }
        }
        A a = new A();
        a.test();
    }

    public static void test() {
        class Animal {
        }
        class Cat extends Animal {
        }
    }
}

匿名内部类(重点)

  1. 简化代码,也是开发中常用的形式。
  2. 匿名内部类是一个没有名字的内部类。
  3. 匿名内部类一旦写出来,就会立即创建一个匿名内部类的对象返回。
  4. 匿名内部类的对象的类型相当于是当前new的那个的类型的子类类型。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
new 类名|抽象类|接口(形参){
	方法重写
}

// 匿名内部类写法
new Thread(new Runnable(){
    @Override
    public void run(){
        for(int i=0;i< 10;i++){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}).start();

方法继承

  1. 子类不能继承父类的构造器:子类有自己的构造器。
  2. 子类是否可以继承父类的私有成员(私有成员变量,私有成员方法)? 需要暴力访问。
  3. 子类是否可以继承父类的静态成员?共享并非继承。
  4. 继承后-成员变量/成员方法的访问特点:就近原则
  5. 继承后-构造器的特点:子类的全部构造器默认一定会先访问父类的无参数构造器,再执行子类自己的构造器。

方法重写

  1. 方法签名相同(申明不变,重新实现)
  2. 两小一大
    • 返回值类型:子类≤父类
    • 抛出的异常:子类≤父类
    • 权限修饰符:子类≥父类
  • @Override优势:可读性好,安全,优雅!!
  • super可以用在子类的实例方法中调用父类被重写的方法。
  • 静态方法和私有方法不可以被重写(拓展语法)

多态

 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
37
38
39
40
class Animal {
    public void run() {
        System.out.println("动物跑!");
    }
}

class Dog extends Animal {
    @Override
    public void run() {
        System.out.println("🐕跑的贼快~~~~!");
    }

    // 独有功能
    public void lookDoor() {
        System.out.println("🐶看门");
    }
}

class Cat extends Animal {
    @Override
    public void run() {
        System.out.println("🐱跑的飞快~~~~!");
    }

    // 独有功能
    public void catchMouse() {
        System.out.println("🐱抓🐀");
    }
}

class Wolf extends Animal {
    @Override
    public void run() {
        System.out.println("狼跑的飞快~~~");
    }

    public void catchSheep() {
        System.out.println("🐺抓🐏");
    }
}
  • 父类类型 对象名称 = new 子类构造器;
  • 接口 对象名称 = new 实现类构造器;
  • 父类类型的范围 > 子类类型范围的。
  • 多态的识别技巧: 对于方法的调用:编译看左边,运行看右边。 对于变量的调用:编译看左边,运行看左边。
1
2
3
4
5
6
7
class class1 {
    viod method() {
        Animal dlam = new Dog();
        dlam.run(); // 对于方法的调用:编译看左边,运行看右边。
        dlam.lookDoor(); // 报错了,多态形式下,编译看左边,左边没有独有功能
    }
}

多态的使用前提

  1. 必须存在继承或者实现关系。
  2. 必须存在父类类型的变量引用子类类型的对象。
  3. 需要存在方法重写。
  • 在多态形式下,右边对象可以实现组件化切换,业务功能也随之改变, 便于扩展和维护。可以实现类与类之间的解耦。
  • 实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法, 可以传入一切子类对象进行方法的调用,更能体现出多态的扩展性与便利。

Java关键字-final

 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
/**
 * 局部内部类和匿名内部类只能访问局部final变量
 */
class final_test {
    public void test() {
        // jdk8在这里做了优化, 不用写,语法糖,但实际上也是有的,也不能修改
        // final int a = 10;
        int a = 10;
        //  a = 1; // 如果修改,则语法糖失效,就不是final了
        new Thread(() -> System.out.println(a)).start();
    }

    // jdk8在这里做了优化, 不用写,语法糖,但实际上也是有的,也不能修改
    // public void test2(final int a) {
    public void test2(int a) {
        //  a = 1; // 如果修改,则语法糖失效,就不是final了
        new Thread(() -> System.out.println(a)).start();
    }

    static class OutClass {
        private final int age = 12;

        public void outPrint(final int x) {
            class InClass {
                public void InPrint() {
                    System.out.println(x);
                    System.out.println(age);
                }
            }
            new InClass().InPrint();
        }
    }

}

Java关键字-finally

 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
37
38
39
40
41
42
/**
 * 以前用来释放资源,现在用try-with-resources语句代替
 */
class finally_test {
    /*
        - finally的作用: 可以在代码执行完毕以后进行资源的释放操作。 什么是资源?资源都是实现了Closeable接口的,都自带close()关闭方法!!
        - try: 1次。 catch:0-N次 (如果有finally那么catch可以没有!!)
          finally: 0-1次
     */
    public static void chu() {
        InputStream is = null;
        try {
            is = new FileInputStream("D:/cang.png");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 回收资源。用于在代码执行完毕以后进行资源的回收操作!
            try {
                if (is != null) is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /*
        从 java 7 build 105 版本开始,java 7 的编译器和运行环境支持新的 try-with-resources 语句,称为 ARM 块(Automatic Resource
        Management) ,自动资源管理。
     */
    private static void customBufferStreamCopy(File source, File SDCard) {
        try (InputStream fis = new FileInputStream(source);
             OutputStream fos = new FileOutputStream(SDCard)) {
            byte[] buf = new byte[8192];
            int i;
            while ((i = fis.read(buf)) != -1) {
                fos.write(buf, 0, i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Java关键字-synchronized

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class synchronized_Test {
    public static class SynchronizedDemo {
        public void method1() throws InterruptedException {
            synchronized (this) {
                SynchronizedDemo demo = new SynchronizedDemo();
                demo.wait();
                System.out.println("synchronized 代码块");
            }
        }

        public synchronized void method2() {
            System.out.println("synchronized 修饰方法");
        }
        /*
            查看字节码信息:
            javac SynchronizedDemo.java
            javap -c -s -v -l SynchronizedDemo.class
         */
    }
}

Java队列示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 做一个队列:先进先出
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
queue.addLast("4号");
System.out.println(queue); // [1号, 2号, 3号, 4号]
// 出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);

Java栈示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 做一个栈:先进后出
LinkedList<String> stack = new LinkedList<>();
// 压栈
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack); // [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
// 弹栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);

Java反射

 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
class 反射机制 {
    public static void main(String[] args) throws Exception {
        // 方式1
        User user = new User();
        Class<? extends User> clazz = user.getClass();

        // 方式2
        Class<?> clazz2 = Class.forName("com.zx.alice.allcode.Java_反射机制.Fruit");
        // 无参构造
        Fruit fruit = (Fruit) clazz2.getConstructor().newInstance();
        // 有参构造
        Fruit fruit2 = (Fruit) clazz2.getConstructor(String.class).newInstance("Apple");

        // 方式3
        Class<User> clazz3 = User.class;

    }

    static class Fruit {
        public Fruit() {
            System.out.println("无参构造器 Run...........");
        }

        public Fruit(String type) {
            System.out.println("有参构造器 Run..........." + type);
        }
    }

    static class User {

    }

}

Java-可变参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * - 可变参数的格式:数据类型... 参数名称
 * - 可变参数在方法内部本质上就是一个数组。
 * - 可变参数的注意事项
 * - 一个形参列表中可变参数只能有一个!
 * - 可变参数必须放在形参列表的最后面!!
 */
class 可变参数 {
    public static void main(String[] args) {
        sum(); // 可以不传输参数。
        sum(10); // 可以传输一个参数。
        sum(10, 20, 30); // 可以传输多个参数。
        sum(new int[]{10, 30, 50, 70, 90}); // 可以传输一个数组。
    }

    public static void sum(int... nums) {
        // 可变参数在方法内部本质上就是一个数组。
        System.out.println("元素个数:" + nums.length);
        System.out.println("元素内容:" + Arrays.toString(nums));
        System.out.println("--------------------------");
    }
}

Java接口与抽象类

img_168.png

接口中,没有构造器,不能创建对象。(重点)

接口只能定义静态常量,抽象类可以定义非静态的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public abstract class One {
    public static final String n = null;
    private String n2 = null;
    abstract void method1();
}

// public static final 在IDEA里面,不写也默认有
public interface Two {
    public static final String n = null;
}

java中,子类继承父类,子类实例化,首先要执行父类的构造器,所以抽象类里面有构造器,有构造器就有实例化,只是这种实例化是比较特殊的实例化,也不能通过new创建实例,但是可以通过该特殊的实例化调用该抽象类里面的非静态字段,通过下面的代码可以发现

Java泛型

泛型接口

 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
修饰符 interface 接口名称<泛型变量>{

}
public interface Data<E> {
    void add(E stu);

    void delete(E stu);

    void update(E stu);

    E query(int id);
}

// 操作学生数据
public class StudentData implements Data<Student> {
    @Override
    public void add(Student stu) {
        System.out.println("添加学生!");
    }

    @Override
    public void delete(Student stu) {
        System.out.println("删除学生!");
    }

    @Override
    public void update(Student stu) {
    }

    @Override
    public Student query(int id) {
        return null;
    }
}

泛型通配符

通配符:?

? 可以用在使用泛型的时候代表一切类型。

E , T , K , V是在定义泛型的时候使用代表一切类型。

泛型的上下限:

? extends Car : 那么?必须是Car或者其子类。(泛型的上限)

? super Car :那么?必须是Car或者其父类。(泛型的下限。不是很常见)

 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
37
38
39
public class GenericDemo {
    public static void main(String[] args) {
        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        bmws.add(new BMW());
    
        bmws.add(new BMW());
        run(bmws);

        ArrayList<BENZ> benzs = new ArrayList<>();
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        run(benzs);

        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        dogs.add(new Dog());
        // run(dogs); // 就进不来了!
    }

    // 定义一个方法,可以让很多汽车一起进入参加比赛
    public static void run(ArrayList<? extends Car> cars) {

    }
}

class Car {
}

class BMW extends Car {
}

class BENZ extends Car {
}

class Dog {
}

自定义泛型方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
修饰符 <泛型变量> 返回值类型 方法名称(形参列表){

}
class class1 {
    public static <T> String arrToString(T[] nums) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (nums != null && nums.length > 0) {
            for (int i = 0; i < nums.length; i++) {
                T ele = nums[i];
                sb.append(i == nums.length - 1 ? ele : ele + ", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }
}

自定义泛型类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
修饰符 class 类名<泛型变量>{

}
泛型变量建议使用 E , T , K , V
class MyArrayList<E> {
    private ArrayList lists = new ArrayList();

    public void add(E e) {
        lists.add(e);
    }

    public void remove(E e) {
        lists.remove(e);
    }

    @Override
    public String toString() {
        return lists.toString();
    }
}

Java数组在内存中的分配

对于 java 数组的初始化,有以下两种方式,这也是面试中经常考到的经典题目。

静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度,如:

1
2
3
4
5
//只是指定初始值,并没有指定数组的长度,但是系统为自动决定该数组的长度为4  
String[]computers={"Dell","Lenovo","Apple","Acer"}; //1  

//只是指定初始值,并没有指定数组的长度,但是系统为自动决定该数组的长度为3  
String[]names=new String[]{"多啦A梦","大雄","静香"}; //2

动态初始化:初始化时由程序员显示的指定数组的长度,由系统为数据每个元素分配初始值,如:

1
2
//只是指定了数组的长度,并没有显示的为数组指定初始值,但是系统会默认给数组数组元素分配初始值为null  
String[]cars=new String[4]; //3

因为 java 数组变量是引用类型的变量,所以上述几行初始化语句执行后,三个数组在内存中的分配情况如下图所示

img_169.png

由上图可知,静态初始化方式,程序员虽然没有指定数组长度,但是系统已经自动帮我们给分配了,而动态初始化 方式,程序员虽然没有显示的指定初始化值,但是因为 java 数组是引用类型的变量,所以系统也为每个元素分配 了初始化值 null ,当然不同类型的初始化值也是不一样的,假设是基本类型int类型,那么为系统分配的初始化值 也是对应的默认值0。

Pagehelper逻辑分页

1
2
3
4
5
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
1
PageInfo<Demo> demoPageInfo = logicPagination(pageNum, pageSize, list);
 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
37
38
39
40
41
42
43
44
45
/**
 * 逻辑分页
 *
 * @param pageNum  当前页
 * @param pageSize 每页的数量
 * @param list     所有数据的集合
 */
private static PageInfo<Demo> logicPagination(int pageNum, int pageSize, List<Demo> list) {
    PageInfo<Demo> pageInfo = new PageInfo<>();
    //当前页
    pageInfo.setPageNum(pageNum);
    //每页的数量
    pageInfo.setPageSize(pageSize);
    //当前页的数量
    pageInfo.setSize(0);
    //总页数
    pageInfo.setPages(1);
    //总行数
    pageInfo.setTotal(0);
    if (ObjectUtils.isEmpty(list)) {
        return pageInfo;
    }
   
    // 总大小
    int count = list.size();
    // 总页数
    int pageTotals = (count / pageSize) + ((count % pageSize > 0) ? 1 : 0);

    pageInfo.setPages(pageTotals);
    pageInfo.setTotal(count);

    // 要返回的列表
    List<Demo> contentList = new ArrayList<>();
    if (pageNum <= pageTotals) {
        // 开始下标
        int fromIndex = (pageNum - 1) * pageSize;
        // 结束下标
        int toIndex = ((pageNum == pageTotals) ? count : (pageNum * pageSize));
        contentList = list.subList(fromIndex, toIndex);
    }
    pageInfo.setSize(contentList.size());
    pageInfo.setList(contentList);
    
    return pageInfo;
}

WeakReference弱引用回收测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class 弱引用回收测试 {
    public static WeakReference<String> weakReference;

    public static void main(String[] args) throws InterruptedException {
        test1();

        // 可以输出hello值,此时两个弱引用扔持有对象,而且未进行gc
        System.out.println("未进行gc时,只有弱引用指向value内存区域:" + weakReference.get()); // value

        // 方法执行完毕,hello强引用的地址出栈,String对象仅有一个弱引用,GC会回收对象。
        System.gc();         // 对象已被GC,此时输出都为null

        Thread.sleep(100);

        System.out.println("进行gc时,只有弱引用指向value内存区域:" + weakReference.get()); // null
    }

    public static void test1() {
        String hello = "value";
        weakReference = new WeakReference<>(hello);
        System.gc();        // 此时gc不会回收弱引用,因为字符串"value"仍然被hello强引用
        System.out.println("进行gc时,强引用与弱引用同时指向value内存区域:" + weakReference.get()); // value
    }
}

TimerTask定时任务

 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
public class 定时任务_TimerTask {
    public static void main(String[] args) throws InterruptedException {
        timerTask();
    }

    // TimerTask  单线程
    private static void timerTask() throws InterruptedException {
        Timer timer = new Timer();

        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("hi, 欢迎关注:java技术栈");
            }
        };

        // 第一次任务延迟时间
        long delay = 2000;

        // 任务执行频率
        long period = 3 * 1000;

        // 开始调度
        timer.schedule(timerTask, delay, period);

        // 指定首次运行时间
        // timer.schedule(timerTask, DateUtils.addSeconds(new Date(), 5), period);

        Thread.sleep(20000);

        // 终止并移除任务
        timer.cancel();
        timer.purge();
    }
}

测试数据生成

https://gitee.com/binary/java-generator

1
2
3
4
5
6
<!-- 生成测试数据的 -->
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>java-testdata-generator</artifactId>
    <version>1.1.2</version>
</dependency>
 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
37
38
public class 数据生成 {
    public static void main(String[] args) throws IOException {

        String address = ChineseAddressGenerator.getInstance().generate(); // 河南省漯河市休缄接路7556号窄箍小区3单元2148室

        String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); // 41841319980703055X
        if (idCard.charAt(idCard.length() - 2) % 2 == 0) {
            System.err.println("女");
        } else {
            System.err.println("男");
        }

        String issueOrg = ChineseIDCardNumberGenerator.generateIssueOrg(); // 巴音郭楞蒙古自治州公安局某某分局
        String result = ChineseIDCardNumberGenerator.generateValidPeriod(); // 19730519-19930519

        String mobileNum = ChineseMobileNumberGenerator.getInstance().generate(); // 18844719147
        String mobileNumFake = ChineseMobileNumberGenerator.getInstance().generateFake(); // 19825602718

        String chineseName = ChineseNameGenerator.getInstance().generate(); // 牛恩苞
        String chineseNameOdd = ChineseNameGenerator.getInstance().generateOdd(); // 金煣

        String email = EmailAddressGenerator.getInstance().generate(); // e4jpwflcf3@cuojs.ajv
        String englishName = EnglishNameGenerator.getInstance().generate(); // Paula

        CSVFileGeneratorTest();
    }

    static void CSVFileGeneratorTest() throws IOException {
        String fileName = 数据生成.class.getResource("/").getPath() + "test" + File.separator + "tmp.csv";
        List<HashMap<String, Object>> data = Lists.newArrayList(
                new HashMap<>(ImmutableMap.<String, Object>of("1", "1a", "2", "2a", "3", "3a")),
                new HashMap<>(ImmutableMap.<String, Object>of("1", "1b", "2", "2b", "3", "3b")),
                new HashMap<>(ImmutableMap.<String, Object>of("1", "1c", "2", "2c", "3", "3c")));
        CSVFileGenerator.generate(data, new String[]{"1", "2", "3"}, fileName);

        List<String> strings = Files.readLines(new File(fileName), StandardCharsets.UTF_8); // [1a,2a,3a, 1b,2b,3b, 1c,2c,3c]
    }
}

Java-接口防刷、防止重复访问

1
2
3
4
5
private static final String LOCK_BATTERY_SWAP_KEY = "brpLock:batterySwap:";

public static String getLockBatterySwapKeyKey(String userId) {
    return LOCK_BATTERY_SWAP_KEY + userId;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/**
 * 接口防刷
 */
private void antiBush() {
    String lockKey = RedisKeyConstant.getLockBatterySwapKeyKey(UserHolderUtils.getUserId());
    Object isLock = stringRedisTemplate.opsForValue().get(lockKey);
    if (Objects.isNull(isLock)) {
        stringRedisTemplate.opsForValue().set(lockKey, "1", 60, TimeUnit.SECONDS);
    } else {
        throw new RespStatusException(HttpStatus.TOO_MANY_REQUESTS, "正在换电中,请勿重复点击");
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/**
 * 50秒之内不能重复提交相同数据
 */
public void duplicateGetNumber(String submitContent, Duration duration) {
    String redisKey = "duplicateGetNumber:" + submitContent;
    if(stringRedisTemplate.opsForValue().get(redisKey) != null){
        log.info("50秒之内重复取号");
        throw new RespStatusException(HttpStatus.FORBIDDEN, "请勿重复提交,可以稍候重试,或者尝试联系管理员");
    } else {
        stringRedisTemplate.opsForValue().set(redisKey, "1", duration);
    }
}

Java操作-传id更新,不传id新增,少的删除

 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
37
// 传id更新,不传id新增,少的删除 操作

// 1. 传入id的更新
// 传入有id值的对象列表
List<BrpContactInfoDto> haveIds = contactList.stream().filter(s -> s.getId() != null).collect(Collectors.toList());
// 根据id更新
haveIds.forEach(dto -> {
    BrpContactInfo contactInfo = new BrpContactInfo();
    contactInfo.setId(dto.getId());
    contactInfo.setInOutType(dto.getInOutType());
    brpContactInfoMapper.updateByPrimaryKeySelective(contactInfo);
});

// 2. 减少的部分删除
// 数据库现有对象列表
List<BrpContactInfo> select = brpContactInfoMapper.select(BrpContactInfo.builder().fleetId(fleet.getId()).status("1").build());
Set<Long> idSelects = select.stream().map(s -> s.getId()).collect(Collectors.toSet());
Set<Long> idIns = haveIds.stream().map(s -> s.getId()).collect(Collectors.toSet());
Sets.difference(idSelects, idIns)
        .forEach(id -> brpContactInfoMapper.updateByPrimaryKey(BrpContactInfo.builder().id(id).status("-1").build()));

// 3. 添加的部分插入
// 传入无id值的对象列表
List<BrpContactInfoDto> noIds = contactList.stream().filter(s -> s.getId() == null).collect(Collectors.toList());
List<BrpContactInfo> toInsert = noIds.stream()
        .map(dto -> {
                BrpContactInfo contactInfo = new BrpContactInfo();
                contactInfo.setId(null);
                contactInfo.setFleetId(fleet.getId());
                contactInfo.setInOutType(dto.getInOutType());
                return contactInfo;
            })
        .collect(Collectors.toList());

if (!CollectionUtils.isEmpty(toInsert)) {
    brpContactInfoMapper.insertList(toInsert);
}

HttpServletResponse导出文件,输入流InputStream流导出文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * 通过inputStream流导出文件
 */
@SneakyThrows
private void inputStreamExportExport(HttpServletResponse response) {
    try (BufferedInputStream in = new BufferedInputStream(this.getClass().getResourceAsStream("/换电站运营周报表模板.xlsx"))) {
        // 构建response
        response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode("fileName", StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20")
                .replaceAll("%2F", "/")
                + ".xlsx");
        response.setContentType("application/vnd.ms-excel"); // application/json application/pdf
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        byte[] buffer = new byte[1024];
        int len;
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
        out.flush();
    }
}

Properties

 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
/**
 * <p>
 * <p> {@link Properties#setProperty(String, String)} 保存一对属性
 * <p> {@link Properties#getProperty(String)} 根据key获取value
 * <p> {@link Properties#stringPropertyNames()} 所有key的名称Set
 * <p> {@link Properties#store(Writer, String)} 保存数据到属性文件中去
 * <p> {@link Properties#store(OutputStream, String)} 保存数据到属性文件中去
 * <p> {@link Properties#load(Reader)} 加载属性文件的数据到属性集对象中去
 * <p> {@link Properties#load(InputStream)} 加载属性文件的数据到属性集对象中去
 * Properties
 * </p>
 *
 * @author zhangxin
 * @since 2023/4/8
 */
public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.load(new FileInputStream("Day10Demo/src/users.properties"));
        properties.load(Files.newInputStream(Paths.get("Day10Demo/src/users.properties")));
        System.out.println(properties);
        System.out.println(properties.getProperty("dlei"));
        System.out.println(properties.getProperty("admin"));

    }
}

重试机制实现

 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
37
38
39
40
41
@RequiredArgsConstructor
@Slf4j
public class 重试机制 {

    private final DemoRepository demoRepository;

    /*
        1. 代码方式:尝试5次查询Demo
     */
    private final Map<String, AtomicInteger> retryMap = new ConcurrentHashMap<>();
    private Demo get(DemoDTO dto, Supplier<Demo> supplier) throws JsonProcessingException {
        Demo demo = supplier.get();
        if (demo != null) {
            return demo;
        }
        // 最多尝试5次共10秒
        int retryTimes = 5;
        AtomicInteger ai = retryMap.computeIfAbsent(dto.getName(), k -> new AtomicInteger(1));
        if (ai.getAndIncrement() <= retryTimes) {
            try {
                Thread.sleep(2000);
                return get(dto, supplier);
            } catch (Exception e) {
                log.error("getApplyRetry={}, e=", new ObjectMapper().writeValueAsString(dto), e);
                retryMap.remove(dto.getName());
                return null;
            }
        }
        retryMap.remove(dto.getName());
        return demo;
    }

    /*
        2. 注解方式:getDemo方法将在发生异常时重试5次,每次重试之间的间隔为2秒(2000毫秒)
     */
    @Retryable(value = {Exception.class}, maxAttempts = 5, backoff = @Backoff(delay = 2000))
    public List<Demo> getDemo(String name) {
        return demoRepository.findAll(Example.of(Demo.builder().name(name).build()));
    }

}

ThreadLocal的生命周期

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ThreadLocalTest {
    private final List<String> messages = Lists.newArrayList();

    public static final ThreadLocal<ThreadLocalTest> holder = ThreadLocal.withInitial(ThreadLocalTest::new);

    public static void main(String[] args) {
        ThreadLocalTest.add("zxalive.com");
        System.out.println(holder.get().messages);
        // 清除并返回清除的内容
        ThreadLocalTest.clear();
    }

    public static void add(String message) {
        holder.get().messages.add(message);
    }

    public static List<String> clear() {
        List<String> messages = holder.get().messages;
        holder.remove();

        System.out.println("size: " + holder.get().messages.size());
        return messages;
    }
}

IO流

img_137.png

正则Pattern

 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
 * <p>
 * 正则表达式
 * </p>
 * <a href="https://www.runoob.com/regexp/regexp-syntax.html">正则表达式 – 语法 | 菜鸟教程</a>
 *
 * <p>  字符类
 * <p> [abc] a、b 或 c(简单类)
 * <p> [^abc] 任何字符,除了 a、b 或 c(否定)
 * <p> [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
 * <p> [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
 * <p> [a-z&&[def23]] d、e 或 f(交集)
 * <p> [a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
 * <p> [a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)
 *
 * <p> 预定义字符类
 * <p> . 任何字符
 * <p> \d 数字:[0-9]
 * <p> \D 非数字: [^0-9]
 * <p> \s 空白字符:[ \t\n\x0B\f\r]
 * <p> \S 非空白字符:[^\s]
 * <p> \w 单词字符:[a-zA-Z_0-9]
 * <p> \W 非单词字符:[^\w]
 *
 * <p> 以上正则匹配只能校验单个字符。
 *
 * <p> Greedy 数量词
 * <p> X? X,一次或一次也没有
 * <p> X* X,零次或多次
 * <p> X+ X,一次或多次
 * <p> X{n} X,恰好 n 次
 * <p> X{n,} X,至少 n 次
 * <p> X{n,m} X,至少 n 次,但是不超过 m 次
 * <p> public String[] split(String regex);// 按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。
 * <p> public String replaceAll(String regex,String newStr);// 按照正则表达式匹配的内容进行替换
 */
public class 正则 {
    public static void main(String[] args) {
        // 正则表达式爬取信息中的内容。
        String rs = "来zxalive.com序学习java,电话050-4565843,或者联系邮箱" +
                "itlearn@来zxalive.com,电话1875454633,02033322323" +
                "邮箱tlearn@来zxalive.com,400-10330-4565843 ,02033322323";
        // 需求:从上面的内容中爬取出 电话号码和邮箱。
        // 1.定义爬取规则
        String regex = "(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15})|400-?\\d{3,8}-?\\d{3,8}";
        // 2.编译正则表达式成为一个匹配规则对象
        Pattern pattern = Pattern.compile(regex);
        // 3.通过匹配规则对象得到一个匹配数据内容的匹配器对象
        Matcher matcher = pattern.matcher(rs);
        // 4.通过匹配器去内容中爬取出信息
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }
}

Pattern.matches正则使用

 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
// 计算站外充电的服务费价格,它与当月费率的服务费价格一致。
private BigDecimal getAverageServicePrice(BillVO s) {
    String settlementCycle = s.getSettlementCycle();
    if (settlementCycle == null || settlementCycle.isEmpty()) {
        return BigDecimal.ONE;
    } else if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2} ~ \\d{4}-\\d{2}-\\d{2}$", settlementCycle)) {
        String[] parts = settlementCycle.split("~");
        LocalDate startDate = LocalDate.parse(parts[0].trim(), dateFormatter);
        LocalDate endDate = LocalDate.parse(parts[1].trim(), dateFormatter);
        return rateTemplateMapper.selectAverageServicePrice(startDate, endDate, s.getStationNo());
    } else if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}~\\d{4}-\\d{2}-\\d{2}$", settlementCycle)) {
        String[] parts = settlementCycle.split("~");
        LocalDate startDate = LocalDate.parse(parts[0].trim(), dateFormatter);
        LocalDate endDate = LocalDate.parse(parts[1].trim(), dateFormatter);
        return rateTemplateMapper.selectAverageServicePrice(startDate, endDate, s.getStationNo());
    } else if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}-\\d{4}-\\d{2}-\\d{2}$", settlementCycle)) {
        String[] parts = settlementCycle.split("-");
        LocalDate startDate = LocalDate.parse(parts[0].trim(), dateFormatter);
        LocalDate endDate = LocalDate.parse(parts[1].trim(), dateFormatter);
        return rateTemplateMapper.selectAverageServicePrice(startDate, endDate, s.getStationNo());
    } else if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$", settlementCycle)) {
        LocalDate localDate = LocalDate.parse(settlementCycle, dateTimeFormatter);
        return rateTemplateMapper.selectAverageServicePrice(localDate, localDate, s.getStationNo());
    }
    return BigDecimal.ONE;
}

图书管理系统

  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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212

/**
 * <p> 目标:图书管理系统的开发。
 * <p>
 * <p> 业务需求分析:
 * <p> (1)查看全部书籍。query
 * <p> (2)添加书本信息。add
 * <p> (3)删除书本信息。delete
 * <p> (4)修改书本信息。update
 * <p> (5)退出系统。 exit
 * <p>
 * <p> 书本信息的结构:
 * <p> 类型                书名            价格        作者
 * <p> ---------------------------------------------------
 * <p> 言情小说
 * <p> 《金瓶梅》         99.9        阿猫
 * <p> 《红楼梦》         198.2       曹雪芹
 * <p> 武侠小说
 * <p> 《三少爷的剑》      98.2        古龙
 * <p> 《神雕侠侣》        98.2        金庸
 * <p> ------------------------------------------------------
 * <p> 分析:
 * <p> (1)定义一个书本类。
 * <p> (2)定义一个集合表示图书馆用于存储书本信息数据:Map<String,List<Book>>。
 * <p> (3)展示操作界面。
 * <p> (4)完成相应功能。
 */
public class 图书管理系统 {
    /**
     * 1.定义一个集合表示图书馆用于存储书本信息数据:Map<String,List<Book>>。
     * 使用Map集合,键是橱柜的栏目类型名称,值是橱柜的List集合对象存储书本信息
     */
    public static final Map<String, List<Book>> BOOK_STORE = new HashMap<>();
    public static final Scanner SYS_SCANNER = new Scanner(System.in);

    public static void main(String[] args) {
        /** 2.展示操作界面 :独立功能建立封装成方法调用 方便互相之间的逻辑调用 */
        showCommand();
    }

    /**
     * 展示操作界面的命令
     */
    private static void showCommand() {
        System.out.println("===============欢迎您进入系统===================");
        System.out.println("(1)查看全部书籍。query");
        System.out.println("(2)添加书本信息。add");
        System.out.println("(3)删除书本信息。delete");
        System.out.println("(4)修改书本信息。update");
        System.out.println("(5)退出系统。 exit");
        System.out.print("请您输入您的操作命令:");
        String command = SYS_SCANNER.nextLine();
        // 判断用户的命令是想干啥
        switch (command) {
            case "query":
                // 查看全部书籍
                queryBooks();
                break;
            case "add":
                // 添加书籍
                addBook();
                break;
            case "delete":
                // 删除书籍
                break;
            case "update":
                // 修改书籍
                updateBook();
                break;
            case "exit":
                // 退出系统
                System.out.println("退出成功,期待您下次光临!");
                System.exit(0);
                break;
            default:
                System.err.println("您的命令输入有无,请重新确认!");
        }
        showCommand(); // 调用自己
    }

    /**
     * 修改书本信息。
     */
    private static void updateBook() {
        if (BOOK_STORE.size() == 0) {
            System.out.println("您现在根本没有任何栏目可以修改!");
        } else {
            queryBooks();
            System.out.println("===============欢迎您进入修改书本业务=================");
            while (true) {
                System.out.print("请您输入修改书本的栏目:");
                String type = SYS_SCANNER.nextLine();
                // 1.判断是否存在该栏目
                if (BOOK_STORE.containsKey(type)) {
                    while (true) {
                        // 存在该栏目
                        // 2.请输入要修改的书名
                        System.out.print("请您输入修改书本的名称:");
                        String name = SYS_SCANNER.nextLine();
                        // 3.判断该栏目下是否存在该书本对象。
                        // 根据栏目和书名去栏目下查询出这本书对象
                        Book book = getBookByTypeAndName(type, name);
                        if (book == null) {
                            System.err.println("您的输入的书名不存在,,请重新确认!");
                        } else {
                            // 书名正确了,开始正式修改
                            System.out.println("请您输入修改书本的新名称:");
                            String newName = SYS_SCANNER.nextLine();
                            System.out.println("请您输入修改书本的新价格:");
                            String newPrice = SYS_SCANNER.nextLine();
                            System.out.println("请您输入修改书本的新作者:");
                            String newAuthor = SYS_SCANNER.nextLine();
                            book.setName(newName);
                            book.setPrice(Double.valueOf(newPrice));
                            book.setAuthor(newAuthor);
                            queryBooks();
                            System.out.println("您修改的书本成功,请看如上信息确认!");
                            return; // 结束修改的方法!
                        }
                    }
                } else {
                    // 不存在该栏目
                    System.err.println("您输入的栏目不存在,请重新确认!");
                }
            }
        }

    }

    /**
     * 在某个栏目下,根据书名查询出这本书对象返回!
     *
     * @param type 栏目名称
     * @param name 书名称
     * @return 书本对象
     */
    public static Book getBookByTypeAndName(String type, String name) {
        // 1.先提取这个栏目的橱柜,根据栏目提取它的值
        List<Book> books = BOOK_STORE.get(type);
        for (Book book : books) {
            // 如果本书的名称与要找的名称一样,该书就是我们要的,直接返回!
            if (book.getName().equals(name)) {
                return book;
            }
        }
        return null;// 没有这本书
    }

    /**
     * 查询全部书本信息
     * Map<String, List<Book>> BOOK_STORE = {type1=[b1, b2, b3] , type2=[b1] }
     */
    private static void queryBooks() {
        System.out.println("===============欢迎您进入查询书本业务=================");
        if (BOOK_STORE.size() == 0) {
            System.out.println("您的图书馆一本书都没有,请赶紧买书去!");
        } else {
            System.out.println("类型\t\t\t\t书名\t\t\t\t\t价格\t\t\t作者");
            BOOK_STORE.forEach((type, books) -> {
                System.out.println(type);
                // 遍历该类型下的橱柜中的全部书本对象
                for (Book book : books) {
                    System.out.println("\t\t\t\t" + book.getName() + "\t\t\t" + book.getPrice() + "\t\t" + book.getAuthor());
                }
            });
        }
    }

    /**
     * 添加书本信息数据
     * Map<String, List<Book>> BOOK_STORE = {type1=[] , }
     */
    private static void addBook() {
        System.out.println("===============欢迎您进入添加书本业务=================");
        System.out.print("请您输入添加书本的栏目:");
        String type = SYS_SCANNER.nextLine();
        // 定义一个List集合用于指向栏目的橱柜
        // 这个橱柜变量要根据栏目是否存在来确定是用已经存在的橱柜,还是创建一个新橱柜!
        List<Book> books = null;
        // 1.判断是否存在该栏目。
        if (BOOK_STORE.containsKey(type)) {
            // 3.存在该栏目
            // 直接得到该栏目存在的橱柜对象
            books = BOOK_STORE.get(type);
        } else {
            // 2.该栏目是第一次添加。
            // 为该栏目创建一个橱柜对象(List<Book>)
            books = new ArrayList<>();
            // 新橱柜和新栏目必须手工加入到图书馆!
            BOOK_STORE.put(type, books);
        }
        System.out.println("请您输入添加书本的名称:");
        String name = SYS_SCANNER.nextLine();
        System.out.println("请您输入添加书本的价格:");
        String price = SYS_SCANNER.nextLine();
        System.out.println("请您输入添加书本的作者:");
        String author = SYS_SCANNER.nextLine();
        // 定义一个书本对象封装用户输入的书本信息。
        Book book = new Book(name, Double.valueOf(price), author);
        books.add(book);
        System.out.println("您添加在" + type + "下的书本" + book.getName() + "成功!");
    }

    @Data
    @AllArgsConstructor
    static class Book {
        private String name;
        private Double price;
        private String author;
    }

}

斗地主游戏-Map集合实现

  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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package com.zx.alice.allcode;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.*;

/**
 * <p> 目标:Map集合实现斗地主游戏
 * <p>
 * <p> 业务需求分析:
 * <p> 斗地主的做牌,洗牌,发牌,排序(拓展知识), 看牌
 * <p> 业务:总共有54张牌。
 * <p> 点数: "3","4","5","6","7","8","9","10","J","Q","K","A","2"
 * <p> 花色: "♠", "♥", "♣", "♦"
 * <p> 大小王: "👲" , "🃏"
 * <p> 点数分别要组合4种花色,大小王各一张。
 * <p> 斗地主:发出51张牌,剩下3张作为底牌。
 * <p>
 * <p> 功能:
 * <p> 1.做牌。
 * <p> 2.洗牌
 * <p> 3.定义3个玩家。
 * <p> 4.发牌。
 * <p> 5.
 * <p> 6.看牌。
 * <p>
 * <p> 用面向对象设计案例:
 * <p> a.定义一个牌类,代表牌对象。 一个牌对象代表一张牌。
 * <p> b.定义一个集合存储54张牌,集合只需要一个(因为牌只需要一副)
 * <p> {card1=0 , card2=1, ......}
 */
public class Map集合实现斗地主游戏 {
    /**
     * 1.定义一个Map集合存储54张牌对象,键是牌对象,值是其大小 用于实现大小排序
     */
    public static final Map<Card, Integer> ALL_CARDS_SIZE = new HashMap<>();
    // 真正存储54张牌对象的List集合,用于做牌,洗牌,发牌!
    public static final List<Card> ALL_CARDS = new ArrayList<>();

    /** 2.做牌:静态代码块初始化54张牌对象  */
    static {
        // 1.定义一个数组存储牌的点数,类型确定,个数确定请用数组存储!
        String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
        // 2.定义一个数组存储牌的花色,类型确定,个数确定请用数组存储!
        String[] colors = {"♠", "♥", "♣", "♦"};

        // 3.先遍历点数与四种花色组装成牌对象存入到集合中去
        int index = 0;
        for (String number : numbers) {
            // 遍历花色
            for (String color : colors) {
                // 创建一张牌对象封装点数和花色
                Card card = new Card(number, color);
                ALL_CARDS.add(card);
                ALL_CARDS_SIZE.put(card, index++);
            }
        }
        Card c1 = new Card("", "🃏");
        Card c2 = new Card("", "👲");

        ALL_CARDS.add(c1);
        ALL_CARDS.add(c2);
        ALL_CARDS_SIZE.put(c1, index++);
        ALL_CARDS_SIZE.put(c2, index++);
        System.out.println("新牌:" + ALL_CARDS);
    }

    public static void main(String[] args) {
        /**
         c.洗牌(把新牌的牌顺序打乱)
         */
        Collections.shuffle(ALL_CARDS);
        System.out.println("洗牌后:" + ALL_CARDS);

        /**
         d.定义3个玩家。
         */
        List<Card> lingHuChong = new ArrayList<>();
        List<Card> jiuMoZhi = new ArrayList<>();
        List<Card> dongfangbubai = new ArrayList<>();

        /**
         e.发牌 依次发出51张牌出去。
         ALL_CARDS = [J♠, 5♠, 2♠, 9♣, 5♦, 4♠, Q♣, 6♥, 8♠, ......
         0   1   2   3   4    5   6   7   8   % 3(轮询的长度)
         */
        for (int i = 0; i < ALL_CARDS.size() - 3; i++) {
            // 得到当前这张牌对象
            Card c = ALL_CARDS.get(i);
            // 判断这个牌发给谁
            if (i % 3 == 0) {
                // 请令狐冲接牌
                lingHuChong.add(c);
            } else if (i % 3 == 1) {
                // 请啊鸠接牌
                jiuMoZhi.add(c);
            } else if (i % 3 == 2) {
                // 请阿东接牌
                dongfangbubai.add(c);
            }
        }

        /**
         * f.对牌进行排序(拓展,了解)
         */
        sortCards(lingHuChong);
        sortCards(jiuMoZhi);
        sortCards(dongfangbubai);

        /**
         g.看牌
         */
        System.out.println("令狐冲:" + lingHuChong);
        System.out.println("鸠摩智:" + jiuMoZhi);
        System.out.println("东方不败:" + dongfangbubai);
        //System.out.println("底牌:"+ALL_CARDS.get(53) +"-"+ALL_CARDS.get(52) + "-" +ALL_CARDS.get(51) );
        // 拓展: 截取集合的最后三张牌到一个新的List集合中去。
        List<Card> lastThreeCards = ALL_CARDS.subList(ALL_CARDS.size() - 3, ALL_CARDS.size());
        System.out.println("底牌:" + lastThreeCards);

    }

    /**
     * 对牌的List集合进行排序(降序排序)
     */
    private static void sortCards(List<Card> cards) {
        // cards = [ 3♦ , 👲,  10♣ , 9♦ , 8♦ , J♠ , ...
        Collections.sort(cards, new Comparator<Card>() {
            @Override
            public int compare(Card o1, Card o2) {
                // o1   Q♦
                // o2   👲
                // 牌的大小就是当前牌在Map集合中的值存储的大小!
                return ALL_CARDS_SIZE.get(o2) - ALL_CARDS_SIZE.get(o1);
            }
        });
    }

    // 牌类
    @Data
    @AllArgsConstructor
    static class Card {
        private String number;
        private String color;

        @Override
        public String toString() {
            return number + color;
        }
    }

}
皖ICP备2024056275号-1
发表了80篇文章 · 总计150.57k字
本站已稳定运行