Java基础语法入门:从main方法到变量、流程控制与数组排序
发布时间:2026/10/6 19:28:06 作者:尧图编辑部 阅读量:1,286

1. 先聊清楚Java程序的最小可运行单元长什么样学Java到了Day4意味着你已经把JDK装好、环境变量配好甚至已经在命令行里敲过几行Hello World了。但很多初学者到这一步会卡住一个很奇怪的地方为什么明明只写了一行打印语句却要套上一大堆外层的花括号为什么类名必须和文件名一致为什么main方法一定要写成public static void main(String[] args)而不能换一种写法先说结论Java并不是非要这么写不可但JVM在启动程序时会按一套固定约定去找入口——就像你进一家公司前台只会认工牌上的姓名和部门而不认识你今天穿什么颜色的衣服。JVM启动时会去指定的类中寻找一个签名严格匹配的静态方法作为入口这个签名是public static void main(String[] args)缺一不可。public表示JVM可以从类外部访问它static让这个方法属于类本身而不是某个实例对象void表示不返回任何值String[] args则是用来接收命令行参数的。这段骨架代码虽然固定但它的存在意义值得理解透彻。很多人直接照抄模板抄了五年还是只知其然。举个例子如果把public改成private程序能编译通过但运行时JVM会直接报Main method not found in class——因为JVM无权访问一个私有方法这就是访问修饰符在底层真正起作用的地方。再看类这个大壳。Java所有的代码都必须在类中存放这是它和C语言最大的习惯差异。C可以先写函数再写调用Java必须先声明一个类把逻辑放进类的方法里。这种设计的本质是万物皆对象的体现——类是这个语言里组织代码的唯一基本单位。还有一个新手高频疑问为什么默认程序叫Main因为JVM在启动时需要一个类名作为入口参数你java Main就是在告诉JVM去加载名为Main的类然后在里面找main方法。如果你文件名是Test那就必须写public class Test然后执行java Test。这一点千万记死否则编译时出现的class Main is public, should be declared in a file named Main.java会让你一头雾水。这段最基础的结构我用一个最简单的例子配合说明public class Main { public static void main(String[] args) { System.out.println(你好Java); } }写单行注释用//写多行注释用/* ... */还有一个不太常用但必须知道的文档注释/** ... */它可以被javadoc工具识别并生成API文档。这个基础结构里System.out.println也是一个值得展开讲的概念——System是Java标准库里的一个类out是它的一个静态字段类型是PrintStreamprintln才是那个真正干活的方法。所以整行的含义是从System类的out对象调用println方法打印一行内容。等你后面学了面向对象再回来看这行代码会有完全不同的理解深度。2. 变量与数据类型内存租约与类型选择的车祸现场Day4如果只学了固定结构那还只能算照葫芦画瓢真正开始有编程感是从变量开始的。Java是强类型语言这是它和Python、JavaScript这些弱类型语言最大的气质差异每一个变量在声明时就必须确定类型而且之后不能改变。这意味着两件事第一编译器会在编译阶段找出大量类型错误而不是等到运行期第二你写代码时被迫多想一层——这个数据到底是整数、小数、字符还是布尔值Java的八大基本类型是标配中的标配我直接给你一张表建议保存类型占用空间取值范围常见用途byte1字节-128 ~ 127文件流处理、省内存的字节操作short2字节-32768 ~ 32767极少使用被int替代int4字节约正负21亿整数默认类型90%场景够用long8字节极大范围时间戳、大数值后缀Lfloat4字节约7位有效数字精确度低慎用double8字节约15位有效数字小数默认类型char2字节Unicode字符单个字符单引号boolean未定义true/false逻辑判断这里有几个坑必须提前踩平。第一long类型的字面量如果超出int范围必须加后缀L或者l否则编译直接报错。第二float赋值时必须加F或f比如float price 12.5f;因为小数默认是double直接把double值塞进float变量会有精度丢失风险编译器不允许这种没打招呼的降级。第三也是最多人栽跟头的Java的整数除法会直接舍去小数部分。int result 7 / 2;的结果是3而不是3.5。很多人第一次写购物车计算总价发现金额永远少几毛钱就是这个原因。想保留小数必须让两个操作数里至少有一个是浮点类型比如7 / 2.0。再说说自动类型转换和强制类型转换的区别。自动类型转换也叫隐式转换发生在小范围向大范围升级时比如int转long、long转double这种升级不会丢数据编译器默认允许。但反过来大范围向小范围收缩就属于强制类型转换比如double price 99.99; int intPrice (int) price; // 结果为99小数部分被直接砍掉这种强制转换不报错但属于数据截断小数部分说没就没。实际业务中这里最容易出现隐性Bug你把一个4.9999转成int得到4如果后续拿它做数量计算每单都会吃亏。我见过一个生产事故运营后台对账差了两千多块查了半天就是有人把金额的double值强转成了int再乘以数量。除了基本类型还必须认识引用类型这个概念。String、数组、自定义的类都属于引用类型。基本类型存的是值本身引用类型存的是地址——这个区别用一句话类比基本类型是直接住在家里引用类型是手里只有一串开门的钥匙。理解了这一点后续学方法传参时的值传递和引用传递才能想明白。字符串String虽然用起来像基本类型但它本质上是个类。两个字符串比较是否相等绝对不能直接用要用equals()方法。这又是一个老生常谈但每次都能坑到新人的点我们到后文专门展开。3. 运算符的优先级、短路逻辑与自增减陷阱运算符这一章难度不大但坑非常多而且这些坑面试时特别爱问。首先记住Java运算符的优先级大致规律先算乘除取余再算加减然后比较然后逻辑最后赋值。如果你想跳过这些规则统一用括号这是最稳妥也最推荐的做法。自增自减和--是最容易让初学者产生认知混乱的地方。int a 5; int b a;执行完之后a是6b是5。而int c a;执行完之后a是7c是7。口诀是在前先加后用在后先用后加。这个口诀不难记难的是在实际代码里遇到一堆自增减混在一起时还能冷静地按先取出当前值参与表达式计算再修改变量的两步逻辑去推导。比较运算符在字符串上有一个经典大坑。刚才提到过String用比较的隐患这里具体展开String s1 hello; String s2 hello; String s3 new String(hello); System.out.println(s1 s2); // true System.out.println(s1 s3); // false System.out.println(s1.equals(s3)); // true同样的字符串的结果却不一样原因在于s1和s2同时指向常量池里同一个对象而new String()强制在堆内存新建了一个对象。对象地址不同自然不相等。无论是谁养成习惯比较字符串内容一律用equals()哪怕是初学者阶段也直接把这个习惯焊死。逻辑运算符里要重点提一下短路特性。和||在运算时如果左侧已经能确定整个表达式的真假右侧就不会再执行。这个特性不只是省了一次计算这么简单它在实际代码里被大量用来做先判断后操作的防护。比如if (list ! null list.size() 0) { // 只有list不为空时才执行 }如果list为null直接短路右侧的list.size()根本不会执行从而避免了空指针异常。这就是利用短路特性做防御式编程的雏形。反过来和|则不具备短路能力左右两边都会执行一般用在位运算场景别拿它们顶替逻辑运算。位运算这块初学者往往觉得用不上但如果你的目标是算法竞赛比如蓝桥杯或者底层优化它是绕不开的。按位与、|按位或、^按位异或、~取反、左移、右移。其中有一个非常出圈的小技巧用异或交换两个变量的值不用临时变量int x 10, y 20; x x ^ y; y x ^ y; x x ^ y; // 现在x是20y是10这个技巧笔试、面试偶尔会碰到理解原理即可——异或的性质是a ^ b ^ b等于a。不过日常工作还是老老实实用临时变量代码可读性永远优先。最后讲一个容易忽略的点Java的号有重载行为。当任意一侧是字符串时会变成字符串拼接运算符。Java 2026的结果是Java2026而不是数字运算。这个行为在拼接日志、拼SQL时非常常用但也有一点必须强调拼接操作每执行一次就会产生新的String对象大量拼接时性能很差。所以后来JDK引入了StringBuilder这就牵扯到我们后面章节要讲的方法与对象的问题了。4. 流程控制if的深层逻辑与三种循环的选型思路程序之所以能在不同的输入下做出不同反应核心就是流程控制。Day4这一天的重点基本都压在这一块顺序、条件、循环三种基本结构构成一切复杂逻辑的基础。条件判断if在语法上没什么好讲的但怎么写好条件却值得好好说道。新手最常见的坏习惯是条件里堆了一长串逻辑也不加括号也不换行。比如if (a 10 b 20 || c 0) { // 这一行到底表达什么可读性极差 }正确的做法是能拆就拆能用方法封装就封装实在写在一起也要把关键的逻辑组合用括号显式圈出来。另外if后面不加花括号也能写一行语句if (a 10) System.out.println(大于10);虽然语法合法但我强烈建议永远给if加上花括号哪怕里面只有一行。为什么因为后续你往里加第二行时很容易忘记补括号然后就等着看什么叫幽灵逻辑吧——第二行代码不再受if条件控制而是成了无条件执行的语句这种Bug极其隐蔽排查起来很抓狂。switch语句是多个等值判断时的好选择尤其JDK 14之后引入了箭头语法写法简洁许多switch (day) { case 1 - System.out.println(周一); case 2 - System.out.println(周二); default - System.out.println(其他); }箭头语法自带break语义不会像传统的case穿透那样一路执行到底。传统写法的case 1:如果忘了写break程序会继续执行case 2的代码这个穿透行为非常考验脑子——利用它可以实现一些巧妙逻辑但多数情况下都是Bug之源。建议新学就直接上手新语法简洁且省心。循环有三种for、while、do-while。它们的本质区别在于判断时机for和while是先判断后执行——条件不满足就一次都不执行do-while是先执行一次再判断——不管条件是否满足至少执行一次。日常开发中do-while用得很少典型场景是至少要给用户展示一次菜单然后再根据输入决定是否继续。选型思路其实很简单知道循环次数比如遍历数组、遍历列表的每个元素直接用for。不确定次数依赖某个条件退出比如读取文件直到末尾用while。需要先执行一次再判断比如做校验提示时用do-while。遍历数组和集合时for-each是最省心的方式int[] scores {90, 85, 78, 92}; for (int score : scores) { System.out.println(score); }这里要特别说一下for-each只能遍历不能修改数组元素。想修改元素比如把所有分数加5分必须老老实实使用带下标的传统for循环。另外for-each在遍历集合时删除元素会触发ConcurrentModificationException这也是一个非常常见的运行期异常——要删除请用迭代器。循环里还有两个控制词break直接跳出整个循环continue跳过本次循环剩余部分进入下一次。嵌套循环里还有一个带标签的技巧可以让break跳出外层循环outer: for (int i 0; i 5; i) { for (int j 0; j 5; j) { if (j 2) { break outer; // 直接退出两层循环 } } }标签名可以随便起但要符合Java命名规范。这个技能在写搜索算法时偶尔用得到知道有这回事就行别写太多带标签的代码——可读性很差review的时候容易被人吐槽。5. 方法与数组传参机制、可变参数和冒泡排序的完整拆解到这里Day4的语法学习才算真正进入工程化的阶段。方法解决了代码复用问题数组解决了批量数据处理问题两者一结合你才具备解决实际题目的能力。先定义方法的基本格式修饰符 返回值类型 方法名(参数列表) { // 方法体 return 返回值; }比如写一个求两个数最大值的工具方法public static int max(int a, int b) { return a b ? a : b; // 三元运算符a大于b则返回a否则返回b }定义方法时有几个容易踩的坑。第一方法不能定义在另一个方法内部除非是局部内部类那是Java高级内容所有方法都平级地放在类中。第二返回值类型为void时方法里可以不写return也可以写return;直接结束。第三方法名建议用动词开头的驼峰写法如getTotalPrice、findMax这是无论面试还是工作都讲究的代码规范。方法的重载是Java的重要特性同一个类中方法名相同、参数列表不同类型、个数、顺序即构成重载。JVM在调用时根据传入参数自动匹配最合适的方法public static int add(int a, int b) { return a b; } public static double add(double a, double b) { return a b; }你可以用add(1, 2)调用第一个重载也可以用add(1.5, 2.5)调用第二个重载。System.out.println其实就是一个被疯狂重载的方法所有数据类型它都吃就是靠重载实现的。方法的传参机制是初学者另一个高频困惑点。Java传参遵循的基本原则是基本类型传的是值的拷贝引用类型传的是引用的拷贝也就是地址的拷贝。这导致一个非常经典的误解场景public static void change(int a) { a 100; } int x 10; change(x); // x仍然是10因为change里改的是拷贝不是原件但如果是引用类型public static void addScore(Student s) { s.score 10; } Student stu new Student(); stu.score 90; addScore(stu); // stu.score变成了100因为s和stu指向堆内存里同一个对象总结一句话Java永远是值传递但引用类型传的是地址值所以你可以通过地址修改对象的内容却不能让变量重新指向一个新对象。这个概念面试必考务必理解透。可变参数是JDK 5引入的语法糖写法是String... args它本质上是一个数组参数允许调用时传任意个参数public static int sum(int... nums) { int total 0; for (int n : nums) { total n; } return total; }调用时sum(1, 2, 3)和sum(1, 2, 3, 4, 5)都合法。注意可变参数必须放在参数列表最后而且一个方法最多只能有一个可变参数。底层实现上编译器会自动把多个参数包装成数组所以它在方法内部就是一个数组。说完方法说数组。数组的声明有两种写法int[] arr是推荐写法——把中括号放在类型后面一眼就能看出这是整型数组。初始化方式有三种int[] arr1 new int[5]; // 长度固定为5默认值为0 int[] arr2 new int[]{1, 2, 3}; // 直接指定元素 int[] arr3 {4, 5, 6}; // 简化写法等价于arr2数组有一个硬性特点和三大致命短板长度一旦确定不可修改访问越界会抛异常默认有初值。短板在于长度不可动态变化需要扩容时必须新建一个更大的数组重新赋值内存空间连续插入删除元素需要批量移动数组本身没有内置实用的查找排序方法。这些短板推动了一大批集合类ArrayList、HashMap等的出现那已经是Java进阶内容等学到集合框架时你会明白为什么它们能打败数组。最后必须讲一个最常见的算法练习——冒泡排序。热搜里很多人都搜过面试笔试也爱出代码本身不复杂但理解它的过程比抄代码重要得多。冒泡排序的核心思想是每一轮从头开始相邻两两比较如果顺序不对就交换一轮结束最大的数像气泡一样浮到末尾下一轮就可以少比较一个数public static void bubbleSort(int[] arr) { int n arr.length; for (int i 0; i n - 1; i) { // 内层循环相邻元素比较 for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { // 交换两个元素 int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } } }时间复杂度分析很直观外层循环n-1次内层循环从n-1递减到1总比较次数约为n(n-1)/2所以时间复杂度是O(n²)。这个复杂度在数据量大时非常低效所以实际排序时几乎没人手写冒泡直接用Arrays.sort()——它的底层实现是双枢轴快速排序性能远超手写冒泡。但作为算法入门的第一课冒泡还是绕不开因为它用最简单的代码把循环嵌套和交换思想都练到了。优化冒泡还有一个经典思路如果某一轮从开头比较到结尾都没发生交换说明数组已经有序下一轮可以直接跳出。用一个布尔标记就可以实现boolean swapped; for (int i 0; i n - 1; i) { swapped false; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped true; } } if (!swapped) break; // 本轮无交换已经有序 }这算是在基本功之上多长了一个心眼面试聊到冒泡能主动说出这个优化点的人印象分会高不少。6. 从Day4开始建立的三条习惯与常见报错自救学语法最忌讳的是只看不敲。我自己带过不少新人Day1到Day3都很兴奋到了Day4语法开始变多反而开始慌。这种慌的本质不是什么天赋问题而是语法知识的组织方式没建立起来。我根据带新人过程中的高频问题总结三条从Day4就必须养成的习惯。第一条所有新语法必须手敲5遍以上。不是照着抄而是合上书自己凭记忆完整写出来。写不出来就回去看看完了再写。这看起来笨但语法肌肉记忆就像学自行车不能光看别人骑。第二条报错信息是老师。很多人一看到红色报错就慌英文也看不懂直接截图发群里问。正确的做法是先读报错的第一行找到关键字比如cannot find symbol表示找不到符号array index out of bounds表示数组越界possible lossy conversion表示类型转换可能丢失精度然后回到代码里定位到你写的那一行逐字对比。我以最常遇到的报错为例列个表格报错信息原因自检方向cannot find symbol变量或方法名拼写错误、作用域不对检查字母大小写main method not found入口方法签名不规范检查是否public static void main(String[])possible lossy conversiondouble到int等缩窄转换加上强制类型转换或改声明类型ArrayIndexOutOfBoundsException访问了不存在的下标检查循环边界确认length-1NullPointerException引用变量为null却访问了它的成员检查对象是否已初始化incompatible types类型不兼容确认两侧类型至少存在转换关系第三条每个代码块写完立即编译运行不要攒了一堆再运行。语法这种东西错误发现得越早定位越容易。晚上的上机时间请像做实验一样对待写一小段运行一次看结果再写下一小段。还有一个小技巧值得推荐用命令行手动编译运行至少坚持一周再上IDE。虽然IDEA、Eclipse这些工具写代码很舒服但手敲javac Main.java和java Main能让你更强烈地感知编译与运行的区别——编译报错和运行报错是两种完全不同的错误。等后来你开始看JVM相关的东西这个阶段建立的编译期/运行期直觉会非常有价值。Day4结束的时候建议做一个综合小项目来收尾写一个程序从控制台循环读取若干个整数存入数组然后调用你写好的排序方法完成排序最后打印排序结果。这个程序涉及的Scanner输入、数组存储、循环遍历、方法调用、排序算法几乎覆盖了Day4所有语法点。能独立完成并通过边界测试比如空数组、单元素数组、全部相同元素Day4就算真正过关了。到这一步Java基础语法的大梁已经立起来了。后面不管是学面向对象三大特性还是往集合框架、异常处理、IO流这些方向走回头再看今天这一天的内容你会发现在无数地方都能看到这些基础语法的影子——尤其当你发现对象其实就是在类这个大骨架里嵌套了更多属性与方法时你会有一种豁然开朗的感觉。