学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有八种基本类型,我列个表,建议收藏:
| 类型 | 占用字节 | 取值范围 | 用途 |
|---|---|---|---|
| byte | 1 | -128 ~ 127 | 小整数,文件流处理时常用 |
| short | 2 | -32768 ~ 32767 | 极少用 |
| int | 4 | -21亿 ~ 21亿 | 日常整数首选 |
| long | 8 | 超极大整数 | 时间戳、大数据量计数 |
| float | 4 | 约±3.4E38 | 一般不用,精度差 |
| double | 8 | 约±1.7E308 | 小数优先用它 |
| char | 2 | 0 ~ 65535 | 单个字符,用单引号 |
| boolean | 1(逻辑上) | 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; // true3.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计算器改造成方法版。我猜又会踩不少坑,但那正好,坑踩得多了,路也就熟了。