视频加载失败

Java 基础 Day01:基础语法、数组与面向对象入门

2084 字
10 分钟
Java 基础 Day01:基础语法、数组与面向对象入门

基础综合练习#

这一组练习围绕 Scanner 输入、Math.random() 随机数、if/else、switch、while 循环和方法拆分展开。

BMI / BMR 健康计算器#

  • Scanner 依次接收身高、体重、性别、年龄
  • 常量定义边界:private static final double THIN_LIMIT = 18.5;
  • 三元运算符按性别选公式:sex.equals("男") ? 男公式 : 女公式
  • 链式 if-else 判断 BMI 区间(未成年单独提示)
  • 格式化输出:System.out.printf("BMI: %.2f%n", bmi)
double bmr = sex.equals("男")
? 66 + (13.7 * weight) + (5 * height) - (6.8 * age)
: 655 + (9.6 * weight) + (1.8 * height) - (4.7 * age);

猜数字游戏#

  • 随机数生成:int number = (int)(Math.random()*100) + 1;(Math.random() 返回 [0,1),乘以 n 后取整得到 [0, n-1] 的整数,再加偏移得到目标区间,这里是 [1,100])
  • while(true) + break 实现”直到猜对才退出”的循环

简易计算器#

  • JDK 14+ 的 switch 表达式箭头语法:case "1" -> System.out.println(input + input2);
  • 用 String c = sc.next(); if (c.equals("n")) break; 控制是否继续

验证码生成#

  • 字符池法:把数字 + 大小写字母拼成一个字符串,每次随机取下标
  • StringBuffer 拼接(注意:append 返回自身,可链式调用)
String str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int index = (int)(Math.random() * str.length());
sb.append(str.charAt(index));

素数查找#

  • 方法拆分:主方法只负责遍历统计,判断逻辑单独抽成 isPrime(int n)(单一职责)
  • 优化点:for (int i = 2; i <= n/2; i++) 只需判断到 n/2,减少一半循环

数组#

成绩统计#

  • 数组默认初始化:double[] scores = new double[8];(默认全为 0.0)
  • Arrays.sort(scores) 排序后,scores[0] 是最低分,scores[len-1] 是最高分
  • 增强 for 遍历累加求和:for (double score : scores)

斗地主发牌(综合练习)#

  • 方法拆分:initcards() 初始化 54 张牌 → shuffle() 洗牌 → deal() 发牌 → printCards() 展示
  • 嵌套循环生成 52 张普通牌:下标 = 花色索引 * 13 + 牌值索引
  • 取模发牌法:i % 3 == 0/1/2 分给三个玩家,i >= 51 留给底牌
  • 洗牌用经典的交换法:随机下标与当前下标交换
if (i >= 51) dipai[i - 51] = cards[i]; // 最后 3 张是底牌
else if (i % 3 == 0) player1[i / 3] = cards[i];
else if (i % 3 == 1) player2[i / 3] = cards[i];
else player3[i / 3] = cards[i];

数字华容道(二维数组)#

  • 二维数组:int[][] arraystone = new int[n][n];,用 count++ 顺序填充 1~16
  • 打乱:双重循环内随机选一个位置与本位置交换
  • 增强 for 遍历二维数组(双层):外层取行,内层取元素

面向对象入门#

类与对象#

  • 游戏角色类:private 属性 + getter/setter 对外暴露 + show() 方法展示信息
  • 封装雏形:属性私有,外部通过 star.setName("追踪者") 赋值

构造器#

  • 无参构造 / 有参构造 / 构造器重载:同名方法通过参数列表区分
  • 构造器特点:方法名 = 类名、无返回值类型、new 时自动执行
public Student() { } // 无参
public Student(String name, int age) { } // 有参
public Student(String name) { } // 重载

封装#

  • private 修饰敏感数据(chinese/math),对外只暴露 getter/setter
  • 业务方法放类内部:printallscore() / printavgscore() 直接访问私有字段计算
  • 注意:name 是默认访问权限(同包可见),这是与 private 的对比

this 关键字#

  • 哪个对象调用方法,this 就指向哪个对象
  • 常用于:成员变量名与参数名相同时区分(this.name = name)

静态字段#

  • 静态成员(static)属于类,整个程序只加载一份,所有对象共享
  • 实例成员属于对象,每个对象各有一份
  • 演示:s1.name、s2.name 指向同一份静态数据,改一个全变
成员类型归属内存访问方式
静态(static)类只加载一份,共享类名.成员 推荐
实例对象每对象一份对象.成员

静态方法与工具类设计#

  • 静态方法只能访问静态成员(不能直接用 this/实例变量);实例方法可以访问静态成员
  • 判断口诀:方法只需做功能、不需要访问对象数据 → 设计成静态方法;必须依赖对象数据 → 实例方法
  • 工具类设计套路(以验证码工具为例):
public class VerifyCodeUtil {
private VerifyCodeUtil() { // 1. 私有构造器,禁止 new
throw new RuntimeException("不能创建对象");
}
public static String getYanzhengma(int n) { // 2. 全部静态方法
// ...字符池随机取 n 位
}
}
  • 调用方式:VerifyCodeUtil.getYanzhengma(4),登录、注册等不同界面复用同一个工具类

综合案例:数据类与操作类分离#

  • 数据类 + 操作类分离:Movie 只存数据(id/name/type/actor),MovieOperator 负责业务(打印全部、按 id 查询)
  • 构造器注入数据:new MovieOperator(movies) 把电影数组传进操作类
Movie[] movies = new Movie[6];
movies[0] = new Movie(1, "唐顿庄园", "动作", "罗宾·怀特");
MovieOperator operator = new MovieOperator(movies);
operator.printAllMovies();
operator.searchMovieById();

ArrayList 实战补充#

练习环节处理的都是定长数组,ArrayList 的实际上手感来自一个控制台博客项目:有了 ArrayList,动态增删再也不用操心数组长度。

// 1. 创建
ArrayList<Article> articles = new ArrayList<>();
// 2. 新增
articles.add(article); // 自动扩容,不用管长度
// 3. 删除(按下标)
articles.remove(i); // i 之后元素自动前移
// 4. 遍历
for (Article a : articles) { ... } // 增强 for 同数组一样用
// 5. 搜索:contains 判断子串
article.getTitle().contains(keyword); // 标题含关键词
// 6. 大小
articles.size(); // 代替数组的 length

和数组的区别(记忆口诀):

维度数组ArrayList
长度固定,new 时定死动态,自动扩容
长度访问.length(属性).size()(方法)
取值arr[i].get(i)
删除无,只能靠下标覆盖.remove(i) 自动前移
用途长度固定的场景增删频繁的 CRUD

实战教训:

  • 工具类 ScannerUtil 里 nextInt() 后要 nextLine() 吃掉换行,否则会跳过下一次输入(菜单循环里的经典坑)
  • ArticleRepository 用自增 id(nextId++)保证唯一,不用手动传——这就是数据库自增主键的雏形

博客项目实战复盘:控制台版后端的骨架#

一个控制台版的博客后端练习把当天知识点串了起来——这套控制台版就是未来博客后端的骨架,下面的架构与设计决策,正式搭博客时会直接用上。

分层架构#

层类职责未来对应
实体entity/Article数据类:字段 + 读写方法加 @Entity / Lombok @Data
数据层repository/ArticleRepositoryArrayList 存取 + id 自增换成 MyBatis / JPA
业务层service/ArticleService业务动作 + 输入输出加 @Service
工具类util/ScannerUtil统一输入入口保留(工具类思想通用)
入口BlogApplication组装 + 主循环换成 Controller + Vue

关键设计决策#

  1. id 自增由数据层统一维护(nextId++)——调用者不用管,天然唯一。这就是数据库”自增主键”的雏形
  2. 封装 = private + 约定:setId 保留,但约定只有数据层能调用。真实项目(JPA/Lombok)实体 id 就是这么干的——不是”删 setter”,而是”约定谁该用它”
  3. 返回副本保护内部集合:findAll() 返回 new ArrayList<>(articles),防止外部拿到内部集合后绕过 save() 直接改——这是对”破坏封装”的防护
  4. 引用语义:集合里存的是对象引用,update 用 setter 改字段即持久化,不需要再 save;save 只给新文章用
  5. 返回值设计:deleteById 返回被删的文章而不是 boolean——调用方能给出”已删除《xxx》“的人情味提示
  6. Scanner 共享 + 只在退出时关闭:private static final Scanner 类级一份;close 收进 exit(),不散落各方法

踩坑清单#

坑表现正确做法
用 static 存 id所有文章 id 一样(static 共享,后写覆盖先写)每个对象各一份 → 实例字段
用 assert 检查 null默认不生效(要加 -ea 才开),拦不住用 if (x == null) return;
null 分支忘了 return打印提示后继续 getTitle() → NPE 崩溃找不到就 return 结束方法
Scanner.close() 散落各方法关一次后 System.in 就废,后面全读不到只在程序退出时关(exit)
update 里再调 save文章被重新分配 id 并 add 两次(重复出现)改引用即持久化,别重复 save

收获:数据类 + 操作类分离的实战版#

  • service 里没有任何”数据怎么存”的代码,全是业务动作——数据细节被 Repository 挡住了
  • 这套分层直觉,就是 Spring 里 Controller / Service / Mapper 分层的雏形

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Java 基础 Day01:基础语法、数组与面向对象入门
https://ryuki.icu/posts/java-basics-day01/
作者
RK
发布于
2026-08-03
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
RK
Long may the sunshine
公告
Long may the sunshine
分类
标签
最新动态
站点天气
天气加载中…
站点统计
文章
27
分类
6
标签
59
总字数
39,139
篝火已燃
0 天
最后活动
0 天前
每日一签