多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Java自学第二天:基础语法、数组排序与面向对象入门

Java自学第二天:基础语法、数组排序与面向对象入门 学Java的第二天往往比第一天更劝退。第一天装好JDK、跑出Hello World还能靠新鲜感撑着第二天开始碰数据类型、运算符、控制流和数组代码开始“不讲道理”地报错脑子里的概念也开始打架。如果你正卡在这个阶段这篇日记就是写给你看的。今天的内容围绕Java最核心的基础语法展开包括8种基本数据类型、运算符的优先级陷阱、三种循环结构的适用场景、数组的初始化与越界问题、冒泡排序的手写实现以及面向对象编程里的类、对象、构造方法、封装和static关键字最后整理了我当天踩过的报错和排查过程。适合刚学完环境配置、准备系统性过一遍Java基础的自学者也适合正在刷Java面试题或备战蓝桥杯的入门选手——今天讲的很多点笔试里真的会考环境变量配置和基础语法也是面试八股里绕不开的环节。1. 第二天的学习路线从“能运行”到“能写逻辑”1.1 为什么第二天不急着写项目很多自学Java的人第二天就想做点“看得见的东西”——做个计算器、写个贪吃蛇。我的建议是打住。第一天你只是验证了环境通了Java语法、运行机制其实还没真正入门。这时候强行写界面、做交互你会同时面对语法、逻辑、工具链三重问题报错都不知道该查哪个方向。趁这个机会把基础语法过一遍性价比高得多。Java的学习路线里基础语法这关越扎实后面接触Spring Boot、MyBatis这类工程框架时就越能看懂“为什么这么写”。我见过不少直接上手框架的学习者写是能写但一问到”这段代码在内存里发生了什么“就答不上来——面试时这就成了硬伤。1.2 今天的学习目标拆解我给自己定的第二天目标很克制不多但要求弄懂、能默写搞清楚Java的8种基本数据类型是什么各自的取值范围和默认值以及和包装类的区别熟练使用运算符重点是自增自减、短路逻辑、类型转换这几块容易出错的细节掌握三种循环结构在什么场景下选哪个以及break和continue的区别手写一遍冒泡排序用Arrays工具类再实现一遍体会两种写法的差距初步认识类与对象理解构造方法、封装和static关键字这个清单看着不多实际学下来要六到八个小时。学完最大的感受是Java语法的严谨程度远超想象很多东西不是“能运行就行”而是从编译层面就帮你挡住了低级错误。只要你方向对了速度慢一点没关系。2. 数据类型与变量Java代码的“地基”2.1 8种基本数据类型速览Java是静态类型语言这意味着每个变量在声明时就必须明确类型。这既是Java和Python这类动态语言最直观的区别也是很多Python转Java的人最初不适应的点。Python里你写x 1就行Java里必须写int x 1;。8种基本数据类型我整理成了表格方便贴在电脑前面类型占用空间取值范围默认值典型用途byte1字节-128~1270文件流读取、节省内存的大数组short2字节-32768~327670极少直接用知道就行int4字节约正负21亿0最常用的整数类型long8字节极大范围0L时间戳、大数值计算float4字节约正负3.4E380.0f科学计算但精度有限double8字节约正负1.7E3080.0d默认小数类型char2字节0~65535\u0000单个字符boolean不确定true/falsefalse判断条件只存这两个值有个细节特别容易踩坑整数字面量默认是int类型小数字面量默认是double类型。所以给short赋值时如果数字超出short的范围直接编译报错给float赋值时数字后面必须加f或F不然编译不了。我当时就在float price 12.5;这行报了类型转换错误查了半分钟才反应过来得写成12.5f。2.2 包装类与自动拆装箱和基本数据类型配套的是它们各自的包装类Integer、Long、Double、Boolean、Character等。你需要理解的关键逻辑是基本类型存在于栈内存高效但“功能不全”包装类是对象能调用方法能放进集合框架但占用的内存和开销更大。日常开发里两者会自动转换这就是自动拆装箱。比如Integer count 100; // 自动装箱int - Integer int value count; // 自动拆箱Integer - int这里藏着一个面试常考的知识点Integer的缓存机制。对于 -128~127 范围内的整数Java会复用同一个对象所以Integer a 127; Integer b 127; a b的结果是true但到了128结果就是false了。我当天没学到这里后来刷题遇到才明白。现在提前告诉你免得以后面试被问倒整数比较要用equals()别用。2.3 字符串不是基本类型初学者最容易搞混的就是String。它看着用起来简单但本质上是一个类。官方推荐用String的地方非常多但你要记住一个原则判断字符串相等必须用equals()方法不能用。因为比较的是引用地址而不是内容。这是我当天实际翻车的地方后面第6章我会专门展开讲。字符串还有一个不可变性String被创建后内容就不能变了。你看到的那种拼接操作实际上是创建了新的字符串对象。所以频繁拼接字符串的代码性能会很差——这时就该用StringBuilder或StringBuffer。3. 运算符与控制流让程序“长脑子”3.1 运算符优先级一个隐蔽的大坑运算符大概是第二天最容易出bug的地方之一。Java的运算符优先级从高到低有一长串规则但真正需要记住的痛点并不多。我整理几个实际编码时最常用的算术运算符*/%优先于-赋值运算符优先级最低逻辑运算符!高于高于||比较运算符!在逻辑运算符之前真正让我栽跟头的是自增自减。i是先取值后加i是先加后取值。单独写没毛病但放进表达式里就乱了。我当时的代码int i 3; int result i i;这行代码最终结果是8。过程是先取i的当前值3参与运算然后i变成4接着i先把i变成5再取5参与运算。3加5等于8运算结束后i是5。这种代码在实际开发里能不用就不用可读性极差。但面试和笔试里它出现的频率超高所以你必须搞懂。还有一个细节是短路逻辑。a b如果a为falseb根本不会执行a || b如果a为trueb也不会执行。这个特性用好了能避免空指针比如if (str ! null str.length() 0)这段代码一旦str是null因为短路机制后一个判断不会执行所以不会抛空指针异常。要是写成和|那两边都会执行null调用length()直接崩。3.2 if/else与switch怎么选条件控制里日常用量最大的就是if/else。但这里有个代码质量的问题多层嵌套的if/else会有很多层我第二天就写过六层嵌套的代码把自己都看晕了。后来学到的处理方式是能提前return就提前return能用switch就用switch能用枚举映射就用枚举映射。switch在Java 8时只能匹配整数和枚举Java 7开始支持StringJava 14之后有了更简洁的箭头写法。我当时学的还是传统的case写法每个case里记得加break不然会出现“穿透”——后面的case会继续执行。这个特性偶尔会被故意用来做技巧但新手阶段老老实实加break别玩花活。一个实用的判断原则判断条件是范围型比如大于、小于、区间用if/else判断条件是确定的多值比如星期几、订单状态用switch或枚举。把这句话记下来你以后的代码会清爽很多。3.3 三种循环的适用场景Java里有for、while、do-while三种循环很多新手分不清区别其实逻辑很简单for循环知道循环次数时首选。for (int i 0; i 10; i)while循环不知道次数只知道循环条件时用。比如读文件读到结尾为止do-while不管条件是否成立先执行一次再判断。这个用得最少但有些场景比如菜单展示至少要出现一次必须用它循环有个非常重要但容易出错的点break退出整个循环continue跳过本轮循环。两者作用范围都只限于当前一层循环。我当时写双重循环的时候就想用break退出外层结果只是退出了内层效果完全不对。解决办法是给外层循环起标签名outer: for (int i 0; i 5; i) { for (int j 0; j 5; j) { if (j 2) { break outer; } } }这种带标签的break用得不多但确实是Java合法的语法。网上不少Java笔试题会考这种冷门知识点知道有这么回事就好。4. 数组与经典排序实战备战蓝桥杯的第一课4.1 数组的声明、初始化和遍历第二天下午我学的是数组。Java里数组是一个容器可以存放固定数量的同一类型数据。数组的声明和初始化有三种写法int[] arr1 new int[5]; // 声明长度默认值填充int类型默认0 int[] arr2 new int[]{1, 2, 3}; // 声明并赋值 int[] arr3 {4, 5, 6}; // 简化写法只能同时声明和赋值这里有个值得注意的坑int[] arr1 new int[5]创建数组后里面的默认值是0而不是“空”。如果你拿这个数组去参与运算结果是0很容易被当成逻辑bug。引用类型数组比如String[]默认值才是null。遍历数组最推荐增强for循环也就是foreach写法for (int num : arr1) { System.out.println(num); }这种写法简单、不易越界适合只读场景。但如果要修改数组里的值或者需要知道当前是第几个元素就得用传统for配合索引。4.2 手写冒泡排序必会的基本功排序算法是java面试和蓝桥杯的高频基础而冒泡排序基本是必修第一课。我当天手写了一遍代码不多但第一次写错了两处内层循环的边界条件写错导致数组越界还有一个是提前结束的优化标志没加白跑了很多趟。标准的冒泡排序逻辑每一轮把相邻的两个元素比较如果顺序不对就交换这样每一轮结束后数组中最大的数会“冒”到末尾。重复这个操作n-1轮数组就排好了。代码长这样public static void bubbleSort(int[] arr) { int n arr.length; for (int i 0; i n - 1; i) { boolean 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; // 本轮没有交换说明已经有序提前结束 } } }关键点是内层循环的j n - 1 - i因为每完成一轮末尾就有i个元素已经就位不需要再比较。如果不减i比较次数就白白多了很多。加swapped标志是我后来优化的如果某一轮完全没有交换说明已经有序可以直接退出循环——在数据基本有序的情况下这个优化能把时间复杂度从O(n²)降到接近O(n)。入门阶段我建议你先手写、别急着用现成工具手写能帮你建立对循环和交换操作的直观理解。4.3 Arrays工具类排序的开挂方式手写完冒泡排序之后Java还自带了一个工具类Arrays里面封装了排序、查找、填充、比较等常用操作。实际开发里你不会手写冒泡而是直接用Arrays.sort()int[] arr {5, 2, 8, 1, 9}; Arrays.sort(arr); // 从小到大排序 System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 5, 8, 9]Arrays.sort()底层用的是双轴快速排序对基本类型数组性能很好比自己写的冒泡快得多。所以我建议的学习策略是先手写一遍冒泡理解原理再学Arrays工具类了解工程上怎么用。两样都掌握笔试让你手写你写得出实际开发你也知道该调哪个接口。Arrays里还有几个我很常用的方法int[] copy Arrays.copyOf(arr, arr.length); // 复制数组 int index Arrays.binarySearch(arr, 8); // 二分查找数组必须有序 Arrays.fill(arr, 0); // 把数组全部填充为04.4 数组越界的调试经验当天我遇到了一个特别典型的报错ArrayIndexOutOfBoundsException数组越界异常。原因是我拿遍历索引的时候写了i arr.length多算了一次最后一个元素访问越界。这个异常是所有Java新手都会遇到的第一个运行时异常知道怎么排查很重要看报错堆栈里的行号精确告诉你出错在哪一行检查循环条件里是还是检查是否有从别的地方传入的数组长度和实际不匹配我可以跟你直说排查这类问题最快的方法不是瞪眼读代码而是像我在实际操作里做的那样先在关键行打印一下索引值和数组长度看一眼。打日志是Java调试的万能起手式尤其在你还不会用断点调试之前。5. 面向对象初步从“写代码”到“建模”5.1 类与对象图纸和实物的关系学完基础语法下午我开始碰面向对象编程。这是Java的核心思维也是从“写脚本”转变到“写工程”的分水岭。这里可以用一个生活类比帮助理解类是图纸对象是按图纸造出来的实物。一张图纸可以造出无数个实物每个实物独享自己的数据但它们的方法逻辑来自同一份图纸。定义一个类实际上是在定义一种新数据类型public class Student { String name; int age; void introduce() { System.out.println(我叫 name ,今年 age 岁); } }再通过new关键字创建对象Student s new Student(); s.name 张三; s.age 18; s.introduce();这里要理解的核心是Java对象在内存中默认分配在堆上变量s只是指向这个对象的一个引用。这也是为什么对象比较要用equals而不是——因为比较的是引用地址两个内容完全相同但地址不同的对象用判断是false。5.2 构造方法与封装让对象更安全创建对象时new Student()后面的括号其实是在调用一个特殊的方法——构造方法。构造方法的名字必须和类名完全一样没有返回值也不能写void。如果不写构造方法Java会提供默认的无参构造。但如果你自己写了有参构造默认的无参构造就没了需要手动补上不然new Student()会编译报错。这个细节我在网上看过很多次也真的踩到过public class Student { private String name; private int age; // 无参构造 public Student() { } // 有参构造 public Student(String name, int age) { this.name name; this.age age; } }构造方法之外还有一个重要概念是封装。封装的核心做法是把类的属性设为private对外只提供public的getter和setter方法。这么做的好处是给属性赋值时可以在setter里做校验避免非法数据进入对象。比如age设置成负数就抛异常public void setAge(int age) { if (age 0 || age 150) { throw new IllegalArgumentException(年龄不合法); } this.age age; }很多Java新手觉得封装麻烦多写一堆getter和setter但这是Java开发规范里约定俗成的习惯面试八股里会问实际项目管理里也会要求。IDE可以直接生成这些方法别嫌麻烦。5.3 static关键字属于类的不属于对象static是Java里很特别的一个关键字。被static修饰的成员属于类本身所有对象共享一份而不是每个对象各有一份。我当天用代码验证了一下public class Counter { static int count 0; int instanceNum 0; public Counter() { count; instanceNum; } } Counter c1 new Counter(); Counter c2 new Counter(); // count 2, c1.instanceNum 1, c2.instanceNum 1这个例子能直观看出区别static的count是全局共享的创建两个对象后它变成2instanceNum是每个对象自己的各是1。static修饰的方法也不需要通过对象调用直接用类名调用就行。这就是为什么Main方法被定义为public static void main(String[] args)——它是入口方法必须在创建任何对象之前就能被JVM调用。static还有一个实用方向是常量。比如public static final int MAX_SIZE 100;这种写法在整个项目里是全局共享且不可变的比到处写裸数字要规范得多。5.4 枚举类型初探第二天傍晚我提前看了一眼枚举算是给自己预习。枚举用enum关键字定义是一组固定常量集比如订单状态public enum OrderStatus { PENDING, PAID, SHIPPED, COMPLETED }枚举在Java里不是简单的一组常量它是一个完整的类可以加构造方法、属性和方法而且还自带values()方法可以遍历所有枚举值。蓝桥杯和公司笔试题里都爱考枚举在switch里的用法。我当时只是了解到这个程度已经很受震撼了——一个看起来简单的关键字背后居然藏着这么多面向对象的特性。6. 今天踩过的坑与排查记录6.1 环境变量配置与启动失败第二天早上我同学的电脑上出现了第一天没暴露的问题JDK装好了但命令行输入java -version提示找不到命令启动直接失败。我帮他排查后确认是环境变量没配置好这里我把配置过程的关键点也记录一下墙裂建议直接用系统自带的“环境变量”编辑界面操作不要自己手动拼路径否则容易漏掉分号和后缀名导致失败需要配置三个位置JAVA_HOME指向JDK安装目录比如D:\Java\jdk-17Path加上%JAVA_HOME%\binCLASSPATH设置为.;%JAVA_HOME%\lib或者.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar前面的点是当前目录表示允许从当前目录加载类文件配置完成后一定要重新打开命令行窗口再测试因为旧窗口读不到新配置。如果你还用的是 Windows 11 系统在“编辑系统环境变量”里注意 Path 要拆成一条一条加不要连在一起拼一个长字符串这样既规范也方便后续维护。如果你是macOS或LinuxJAVA_HOME通常用/usr/libexec/java_home命令定位导出的写法也比较固定。总之版本要跟实际安装的JDK严格对应JDK 17 和 JDK 8 的路径是不一样的。6.2 中文乱码问题我在控制台打印中文的时候出现了一堆乱码。原因很简单代码文件是UTF-8编码但Windows命令行默认用GBK编码输出两边对不上。解决办法是在启动参数里加上-Dfile.encodingUTF-8或者把源码文件编码改成GBK不推荐治标不治本。IDE比如IntelliJ IDEA里可以通过File Encoding设置全局编码为UTF-8新版本的IDE一般默认就是UTF-8但老项目经常是各种编码混用遇到乱码先统一编码再排查其他的。6.3 String用比较翻了车我之前提到过字符串比较的问题当天实际写的代码是这样的String a hello; String b new String(hello); System.out.println(a b); // false System.out.println(a.equals(b)); // true因为Java对不通过new创建的字符串字面量会被放入字符串常量池所以a b在某些情况下两个直接赋字面量是true但new出来的对象在堆上地址不同所以就是false。这个判断规则不统一的情况让很多新手抓狂最后记住一条就行字符串比较永远用equals。在开发中规范上用hello.equals(b)这种写法也能避免b为null时抛空指针。6.4 常见异常速查表第二天我攒了好几个异常整理成表方便你对照参考常见异常触发场景排查思路ArrayIndexOutOfBoundsException数组越界检查索引范围尤其是循环条件里的边界NullPointerException对象为null时调用方法确认对象是否初始化先判空再调用ClassNotFoundException类找不到检查classpath和jar包依赖NumberFormatException字符串转数字失败确认字符串是纯数字别混入空格或字母Compilation error类型不匹配类型用错检查基本类型和包装类检查类型转换ConcurrentModificationException迭代集合时修改集合改用迭代器的remove或收集后再统一处理遇到异常先看编译器的提示再看运行时的堆栈信息绝大多数情况下堆栈信息已经把行号说清楚了照着改就行。7. 给同样在自学Java的人几句实在话7.1 代码量是唯一硬指标第二天结束我统计了一下今天大概写了200行代码包括练习和改错反复写的部分。对于入门阶段来说每天有效代码量在150到300行左右就足够踏实了。重要的是每行代码都要懂不懂就查、就问、就改。建议你准备一本实验记录把每次改bug的过程记录清楚——我自己的体会是用编号记录问题、描述现象、尝试过哪些方案、最后怎么解决的这套习惯能显著加快你的排错速度因为在Java起步阶段你遇到的90%错误都是重复发生的有个记录本比每次重新排查高效得多。7.2 手写代码作业不可替代今天最大的收获其实是手写冒泡排序那一遍。后来大厂面试和蓝桥杯的笔试里手写排序题还没少出。有些人平时背答案没手写过到了笔试就卡壳——手写是肌肉记忆加逻辑记忆的双重保险很难出错。我自己就建议所有自学者即使有了Arrays.sort这种工具也给经典算法留出每周手写一遍的时间。7.3 别忘了去了解“为什么”学习Java的最大误区之一是能跑就行。能跑和会了是两回事。今天内容里前前后后能看到很多“为什么”为什么short赋值要转类型为什么Integer比较要用equals为什么数组默认值是0为什么static成员是共享的当你能把这些为什么解释清楚才真正算吃透了这一块。后面的学习里不管是数据一致性、定时任务、并发、Spring Boot框架还是JVM调优底层逻辑都离不开这些基础概念。地基打得牢上层建筑才盖得高这句老话在Java学习路线上绝对算是一条铁律。最后再分享一个小技巧第二天结束的时候把今天写的代码里最容易出错的几段删掉合上资料重新再打一遍。这比看十遍教程都有用。这一遍打完你不需要有什么特别的感觉就把代码备份好第三天可以安心过渡到字符串处理、集合和常用库函数的学习了。Java学习是一场持久战第二天只是热身但热身热到位了后面跑起来会轻松得多。
返回列表