ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

零基础学Java第二天:变量、流程控制与首个交互程序

零基础学Java第二天:变量、流程控制与首个交互程序

学Java第二天,说实话我是有点慌的。所谓“中登”,就是那种上有老下有小、发际线已经开始告急、但还硬着头皮啃新东西的中年人。昨天光是配置JDK和IDEA就折腾了半宿,环境变量配了三回才跑通第一个Hello World,今天坐下来继续学,突然发现Java这东西特别“讲道理”——语法就是一堆明明白白的规则,不玩玄学。第二天我给自己定的任务很朴素:搞懂变量、数据类型、运算符和流程控制,然后用这些知识写一个能交互的小程序。

这篇文章就是给跟我一样零基础起步、或者学过但一直没系统敲过代码的朋友看的。今天的内容不碰面向对象、不碰集合框架,只讲最基础的语法地基,但我会把每一步“为什么这么做”都说清楚,还会把我自己踩过的坑原原本本摆出来。学编程最怕的不是不会,而是没人告诉你错在哪、怎么改。这篇就当是一个中登学Java的现场直播。

1. 第二天怎么学:先把路线理顺再动手

1.1 为什么第二天不能继续只配环境

我见过不少新手,在环境配置这个环节耗了太久。今天装个JDK,明天换个版本,后天又研究编译器,代码一行没写,热情先耗光了。我的建议是:环境能用就行,版本别追新,正式学习从“能跑通Hello World”那一刻就直接开始语法。第二天的核心不是工具,而是让电脑按你的规则算数、做判断、反复执行同一件事——这就是程序的本质。

第一天如果把Java比喻成学做饭,那JDK只是把锅和灶台搬进厨房了。第二天才是真正学习“洗菜、切菜、下锅”的动作。只有开始写代码,你才会知道编译器报错长什么样、分号漏了是什么感觉、变量的类型用错有多崩溃。这些体验,配环境配得再熟练也学不到。

1.2 我给自己排的学习路线

我前一天晚上花了十分钟做了个简单计划,第二天就按这个节奏走:

  • 上午:复习第一天的Hello World,顺手把IDEA里项目结构看明白,src、包名、类名这些概念混个脸熟。
  • 下午:学习变量、八种基本类型、运算符,这一点动手敲,每学一个就在代码里跑一遍。
  • 晚上:学if、switch、for、while,然后用Scanner写一个带输入的BMI计算器,作为第二天的综合小练习。

这个顺序是有讲究的:变量相当于“认字”,运算符相当于“组词”,流程控制相当于“造句”,最后的综合案例相当于“写一段话”。初学者最容易犯的毛病是跳跃式学习,今天看集合明天看线程,结果连int和double都分不清。我告诉自己,第二天就老老实实打地基。

1.3 对抗遗忘的两个土办法

中登学东西最大的敌人是遗忘。我用的办法很土但很有效:一个是“错题本”,每次编译报错我都把错误信息和对应代码粘到一个文档里,标上原因;另一个是“默写”,睡前不看书,自己把当天学的知识点用大白话写一遍,写不出来的第二天早上再看。

第二天学习结束时你大概率会有一个感受:好像什么都会一点,又好像什么都记不住。这太正常了。语法这东西,不背十遍、不敲烂十次,根本不算会。别灰心,继续往下看。

2. 变量与数据类型:Java里的一切都是“盒子”

2.1 变量的本质:贴了标签的储物柜

我学变量时喜欢把它想成储物柜。每个柜子里只能放一类东西,柜门上都贴着一个名字。当你往柜子里放新东西时,旧东西就被盖住了——变量只能存最新值。

int age = 30; age = 31;

这里age就是一个柜子,第一次往里面放了30,第二次换成31。记住一个关键点:变量名是“名字”,而真正的数据躺在内存里。你操作变量,就是通过名字找到那块内存,读取或修改里面的值。初学者不用管内存地址这些细节,但脑子里要有“变量只是引用”这个概念,后面学数组、对象时你会感谢现在的自己。

Java是强类型语言,什么意思?就是每个变量在出生前就必须说清楚自己是什么类型,不能一会儿存整数一会儿存字符串。这一开始觉得麻烦,写多了反而觉得安心——类型不对,编译器立刻告诉你,总比运行时跑到一半才崩要强。

2.2 八种基本类型,先把常用的四个吃透

Java有八种基本类型,我列个表,建议收藏:

类型占用字节取值范围用途
byte1-128 ~ 127小整数,文件流处理时常用
short2-32768 ~ 32767极少用
int4-21亿 ~ 21亿日常整数首选
long8超极大整数时间戳、大数据量计数
float4约±3.4E38一般不用,精度差
double8约±1.7E308小数优先用它
char20 ~ 65535单个字符,用单引号
boolean1(逻辑上)true / false判断条件

我学的时候只盯住了四个:int、double、boolean、char。String不是基本类型,它是类,但日常使用频率极高,所以我把它当“最熟悉的陌生人”先记着。

重点说int。它的默认值是0,范围大约21亿。我一开始不理解为什么要记范围,直到我写了:

int num = 2147483647; num = num + 1;

结果num变成了-2147483648。这就是溢出。我当时整个人懵了,后来才知道整数在内存里是二进制补码表示的,最大值加一会翻到最小值。这个坑在工作中特别常见,尤其是计算金额、统计数量时。处理大数,要么用long,要么用double谨慎点,要么后期学BigDecimal。

2.3 声明变量的几种写法

我最开始看代码,总觉得别人写得很随意,其实规则非常固定。常见的有三种写法:

int a; // 先声明,后赋值 a = 10; int b = 20; // 声明的同时赋值 int c = 30, d = 40; // 一行声明多个,但不推荐

第三种写法虽然合法,但可读性差,我建议不要用。另外一个非常容易踩的坑:局部变量必须初始化才能使用。

int e; System.out.println(e); // 编译报错:variable e might not have been initialized

我一开始很烦这个报错,觉得编译器多管闲事。后来才明白,这是Java在保护你。未初始化的变量到底存的是什么垃圾值?编译器不知道,干脆不让你用。这个特性在后面的“方法内局部变量”场景里尤其重要。

2.4 命名的规矩和几个“狗皮膏药”式细节

变量命名只有一个硬性规则:不能以数字开头,不能是Java关键字,其他随你,但行业通用的是小驼峰:第一个单词首字母小写,后面每个单词首字母大写。

int myAge = 18; // 推荐 int MyAge = 18; // 不规范,类名才这么写 int my_age = 18; // 下划线风格,Python常用,Java里不推荐

真正让新手栽跟头的不是命名,而是这个小细节:long类型的字面量要加L,float要加F。

long big = 123456789012345L; float f = 3.14F;

不加L,编译器会认为123456789012345是int,结果直接报“过大的整数”。不加F,3.14会被当成double,给float赋值就报精度损失。这俩坑我一天踩了七八回,后来自动形成肌肉记忆:见long就L,见float就F。

3. 运算符与表达式:算数、判断和逻辑的混合操练

3.1 算术运算符:加、减、乘、除、取余

Java有五个基本算术运算符:+、-、*、/、%。大部分人都以为这些是小学知识,但Java里有两个反直觉的地方。

第一,整数相除结果还是整数,小数部分直接丢掉。10除以3,结果是3不是3.333。

int result = 10 / 3; // result = 3

如果你真想要小数,至少让一边变成小数:

double result = 10.0 / 3; // 结果是3.3333333333333335

第二,%是取余数。10 % 3结果是1。这个符号最大的用处是判断奇偶、判断整除,后面写算法题几乎天天见面。

int num = 17; boolean isOdd = num % 2 == 1; // true

3.2 自增自减:a++和++a到底差在哪

这是第二天所有初学者都会卡半小时的经典问题。我先给结论:a++是“先使用,后加1”;++a是“先加1,后使用”。

int a = 5; int b = a++; // b = 5,a = 6 int c = 5; int d = ++c; // d = 6,c = 6

核心区别在于整个表达式的结果值不同。我用个生活化类比:a++就像“先拍照后付钱”,你先把当前的值交给别人,自己再涨价;++a就像“先付款再拿货”,价格先变了,再交易。实际工作中,我强烈建议不要在一个复杂表达式里混用自增自减。比如这样写:

int i = 5; int result = i++ + ++i;

最终结果是12还是?我没算第二次就不敢猜了。这种代码没有任何人喜欢,别秀操作,拆成两行写它不香吗。

3.3 比较运算符和逻辑运算符:短路是真的秀

比较运算符就是==、!=、>、<、>=、<=。最容易犯的错误是把==写成=,等号是赋值,双等号才是比较。这个错误编译器会报错,但如果你在if条件里误写了一个赋值,某些情况下编译器还不一定拦得住,逻辑就彻底歪了。

逻辑运算符有三个:&&(与)、||(或)、!(非)。值得花力气理解的是“短路”机制。

int a = 10; if (a > 5 && a < 20) { // 两个条件都满足,走这里 }

&&必须前后两个条件都为true,整体才为true。但Java有优化:如果第一个条件已经为false,第二个条件根本不会执行。

if (obj != null && obj.getName().equals("张三")) { // 安全,不会空指针 }

这句话我在工作时见过无数次。只要obj是null,第一个条件为false,第二个条件就不会执行,空指针被巧妙地规避了。||同理,第一个条件为true,第二个就不执行了。理解这个机制后,你写复杂多条件判断时的思路会清晰很多。

3.4 类型转换:自动转和强转的恩怨

我第一天看到double d = 3;时,心里咯噔一下:int不是不能直接赋给double吗?实际上这是“自动类型转换”。规则很简单:小范围可以自动转成大范围,因为大类型装得下小类型。

int a = 100; double d = a; // 没问题,int自动变成double long l = a; // 没问题

反过来就不行,大范围给小范围会丢数据,必须强制转换。

double d = 3.99; int a = (int) d; // a = 3,小数部分直接没了

强制转换有个大坑:不是四舍五入,而是直接截断。3.99变成3,-3.99变成-3。我以前天真地以为会变成4和-4,结果被现场打脸。如果你要四舍五入,得用Math.round方法,这个后面学到工具类时再展开。

另外char和int可以直接互相转,因为char本质上是字符对应的Unicode编码。

char c = 'A'; int code = c; // code = 65

知道这个,后面处理英文字母大小写转换时能省不少事。

4. 流程控制:让程序学会选择和循环

4.1 if-else分支:程序第一次有了“想法”

学完变量和运算符,你已经能让程序算数了,但它永远是直线执行:从第一行跑到最后一行。if-else打破了这个直线。

int score = 76; if (score >= 60) { System.out.println("及格了,稳住"); } else { System.out.println("没及格,复习去"); }

几个容易犯的错必须说清楚:

  • 悬空else。else总是跟最近的if配对,不是跟缩进配对。如果你写了嵌套if再看结果,经常怀疑人生。
  • 花括号风格。不管单行还是多行,我都建议写花括号,哪怕里面只有一句话。没有花括号的if,后面只跟一行,新手一压缩代码就屎山。
  • 条件顺序。把最容易满足的条件放前面,还是把最具体的放前面?经验是:先判断边界条件,再判断通用条件。比如判断成绩等级,先判断是否超出100,再判断优秀、良好这些。
if (score > 100 || score < 0) { System.out.println("成绩不合法"); } else if (score >= 90) { System.out.println("优秀"); } else if (score >= 60) { System.out.println("及格"); } else { System.out.println("不及格"); }

4.2 switch分支:匹配后小心“一路穿到底”

switch是另一种分支方式,适合“拿一个值去匹配多个固定选项”的场景。

int day = 3; switch (day) { case 1: System.out.println("周一"); break; case 2: System.out.println("周二"); break; case 3: System.out.println("周三"); break; default: System.out.println("其他"); break; }

新手最大的坑就是漏写break。如果不写break,匹配到case 3后,它会继续往下执行case 4、5……直到遇到break或switch结束。这叫“穿透”。我第一次写漏了,输出结果是“周三周四……”,直接惊呆。穿透行为并非完全没用,有些场景故意利用它合并多个case,但新手别学,老实加break。

JDK 7以后switch可以匹配字符串,这在工作中很好用:

String fruit = "apple"; switch (fruit) { case "apple": System.out.println("苹果"); break; case "banana": System.out.println("香蕉"); break; }

4.3 for和while:循环的两张脸

循环学了之后,程序才算真正有“自动化”的味道。我第二天晚上的代码量都是靠for循环刷出来的。

for循环的结构是固定的:初始化、条件、更新。

// 打印1到5 for (int i = 1; i <= 5; i++) { System.out.println(i); }

三要素缺一不可:int i = 1是起点,i <= 5是终点,i++是步长。只要有一个写错,要么死循环,要么一次都不执行。我建议你手写几个变体练手,比如打印偶数、倒序打印、累加求和。

while适合“不知道循环多少次,只知道满足条件就继续”的场景。

int n = 100; while (n > 0) { n -= 30; }

如果条件从一开始就为false,while一次都不会执行。还有一种do-while,至少会执行一次。记住这个区分:while是“先判断后执行”,do-while是“先执行后判断”。

4.4 综合小练习:打印九九乘法表

我建议每个初学者都敲一遍九九乘法表,它是嵌套循环的经典案例,逻辑短但非常锻炼思维。

for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { System.out.print(j + "x" + i + "=" + (i * j) + "\t"); } System.out.println(); // 每行结束后换行 }

外层循环控制行数,内层循环控制每行打印多少列。关键点在于内层循环的条件是j <= i,不是j <= 9。我第一次写成了j <= 9,打印出的是倒三角形,哈哈哈。这个练习特别适合体会“循环嵌套时变量作用域”的感觉:i在外层循环里定义,内层循环能用它;而j是内层循环自己的变量,外层循环看不见它。

5. 第一次“交互”:用Scanner写一个BMI计算器

5.1 先搞清楚IDEA里的项目结构

第二天实操前,我把IDEA的项目结构认真看了一眼。Java项目的基本组织是:项目(Project)下面有包(Package),包下面有类(Class)。包名一般用公司域名反写或学习用的自定义名字,比如com.study.demo。我们目前写的所有代码都放在.java文件里,一个public类对应一个同名文件。

有个血泪教训我必须说:类名和文件名必须完全一致,而且类名首字母大写。我第一天把类名写成了helloWorld,文件叫HelloWorld,一运行直接报错。IDEA一般会自动建一个Main类,我建议直接在里面写,别自己乱建文件。

public class Main { public static void main(String[] args) { // 代码写在这里 } }

main方法是整个程序的入口,你现在只需要知道这句话是固定的,public、static、void这些修饰词的含义后面学到方法时自然就懂了。抄这个固定句式,别问为什么,问就是Java的运行约定。

5.2 Scanner读取输入:从“输出世界”到“倾听用户”

到目前为止,我们写的所有程序都是单向的:程序算完,打印结果。但真正的程序是能跟人对话的。Java里最基础的输入方式是Scanner。

import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入你的年龄:"); int age = scanner.nextInt(); System.out.println("我记住了,你" + age + "岁。"); scanner.close(); } }

第一行的import是把Scanner这个工具从java.util包里搬进来,没有它编译器不认识Scanner。scanner.nextInt()会阻塞住,等待用户输入一个整数再继续执行。我用“阻塞”这个词你可能陌生,实际感受就是:运行后程序停在那儿,光标一闪一闪等你打字,敲完回车才继续。这是很多初学交互程序时会觉得“程序卡死”的真相——其实它在等你。

5.3 完整案例:计算BMI并给出健康建议

我第一天的Hello World只是向世界打了招呼,第二天的BMI计算器才让我觉得“能写点有用的东西了”。直接上一个完整代码:

import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入你的身高(米):"); double height = scanner.nextDouble(); System.out.print("请输入你的体重(千克):"); double weight = scanner.nextDouble(); double bmi = weight / (height * height); System.out.printf("你的BMI是:%.2f%n", bmi); if (bmi < 18.5) { System.out.println("偏瘦,注意营养"); } else if (bmi < 24) { System.out.println("正常,保持"); } else if (bmi < 28) { System.out.println("偏胖,该动了"); } else { System.out.println("肥胖,建议体检后科学减重"); } scanner.close(); } }

运行后交互效果大概是:

请输入你的身高(米):1.75 请输入你的体重(千克):70 你的BMI是:22.86 正常,保持

注意几个细节:printf里的%.2f表示保留两位小数;%n是换行符,跟\n效果类似但跨平台兼容性更好;bmi公式为什么是weight除以height的平方,这是国际通用标准,不需要背公式,会用就行。

这个案例的价值在于:你把第二天的语法全部串起来了——变量声明、Scanner输入、算术运算符、格式化输出、if分支判断。写完它,你会突然发现,原来编程就是“输入数据、处理数据、输出结果”的循环往复。

5.4 改进思路:常量和方法,为明天埋钩子

我的代码里现在有一堆魔法数字:18.5、24、28。如果以后标准变了,你得满屏改。专业的做法是用final定义常量:

final double LOW_WEIGHT_LIMIT = 18.5; final double NORMAL_WEIGHT_LIMIT = 24;

另一个可以改进的方向是“方法”。把BMI计算单独抽成一个方法:

public static double calcBMI(double height, double weight) { return weight / (height * height); }

看到没有,学习路径是滚雪球的。你不需要今天就把这些全掌握,但写代码时留个“这里可以更好”的心思,学起来会有方向感。

6. 中登踩坑实录:从编译错误到逻辑错误

6.1 编译期最常见的5个错误

编译期错误是编译器直接给你报出来的,不解决根本运行不了。我第二天几乎把所有常见错误挨个犯了一遍,这里列个速查表:

错误类型典型报错原因解法
缺少分号';' expected语句结束没写分号看上一行末尾
括号不匹配reached end of file while parsing大括号少了或多了用IDE的括号高亮检查
类名文件不一致类名报红/找不到主类public类名与文件名不一致改成一致
中文分号illegal character输入法没切英文代码全程英文标点
未初始化variable might not have been initialized局部变量没赋值就用声明时赋个初值

我个人的体会是:编译错误看着吓人,其实最友好。它至少告诉你在哪一行出错,你花两分钟改完,就会产生一种“我战胜了电脑”的快感,这在学习枯燥期特别提气。

6.2 运行期最常见的几个问题

编译通过不代表程序正确,运行时错误更隐蔽。

第一个经典问题:除数为0。

int a = 10; int b = 0; int c = a / b; // 抛出ArithmeticException

整数除以0会直接崩,但如果是double除0不会崩,结果会是Infinity。这个差异我在测试BMI时没注意,输入身高为0,结果直接输出Infinity,人给整懵了。所以写代码时要考虑用户可能输入非法的数据。怎么解决,可以先判断:

if (height <= 0) { System.out.println("身高不合法"); return; }

第二个经典问题:Scanner输入类型不匹配。

int age = scanner.nextInt(); // 用户输入了 abc // 抛出InputMismatchException

用户才不会按你的剧本输入,所以真实产品里要做异常处理,那是try-catch的内容,第二天知道有这个问题就行。

第三个是逻辑错误,最阴险:程序不报错,但结果不对。

double bmi = weight / height * height;

这行代码数学上等价于weight / height再乘height,结果接近weight,根本不是BMI。我当初写的时候没加括号,程序跑得飞快,输出却离谱。排查这种问题,最土的办法就是打印中间变量,把每一步的结果打出来看。

6.3 排错三板斧,以及“心态”就是生产力

我第二天最大的收获,不是会写多少语法,而是学会了怎么面对报错。

第一板斧:看错误信息的第一行,里面通常有文件名和行号,比如Main.java:12。点过去就知道错在哪了。

第二板斧:用打印大法。

System.out.println("调试标记1:" + bmi);

我不会算,就让电脑算,把算式拆成几个临时变量,逐个打印,哪儿不对改哪儿。

第三板斧:二分注释法。当一段代码整体报错找不到原因时,把代码从中间注释掉一半,看哪一半还在报错,然后继续二分,错误范围迅速缩小。

学编程还有一个很玄但很真实的心态问题:编译报错别烦。报错是编译器在跟你说话,告诉你怎么改。真正该怕的是程序静悄悄跑完,结果却是一堆垃圾数据。把“报错=坏事”改成“报错=老师在批改作业”,心态会稳很多。

最后再分享一点“第二天之后”的心里话

第二天结束时,我电脑上已经多了十几个练习文件,从最简单的变量打印,到BMI计算器,再到九九乘法表。说实话,没有那种“突然成了高手”的爽感,反而是长时间盯着代码后的头昏脑涨。但我有一个很深的感受:Java的语法规则虽然多,但它们之间环环相扣,每解决一个报错,你对整个体系的信任就多一分。

我个人觉得,学编程最难的不是某一句话看不懂,而是面对大段大段看不懂的知识时,不知道从哪里下口。所以我特别建议你像我一样,第二天就只看四个东西:变量、运算符、if分支、循环。别的什么都不碰。把这三个小时的量砸实,比你硬啃五小时还强。

有个技巧我越用越顺:每天结束前,把当天的代码翻出来,找一小段自己写的,然后故意造两个错——比如删个分号、把==改成=,然后看编译器怎么骂你。这个玩法又解压又能巩固记忆,还顺便把常见报错混了个脸熟。

第三天的计划我也订好了:进入方法的世界,学参数、学返回值,然后把我那个BMI计算器改造成方法版。我猜又会踩不少坑,但那正好,坑踩得多了,路也就熟了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进