ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java基础学习全攻略:从环境配置到面试与工程实战

Java基础学习全攻略:从环境配置到面试与工程实战 Java这个语言从1995年诞生到现在快三十年了。放在编程语言里这算非常长寿。这些年里我不止一次听到“Java要凉了”的说法可一转头看企业后端、Android开发、大数据平台、中间件生态Java依然是存量最大、岗位需求最稳的语言之一。如果你正打算入门或者已经自学了一阵子但总觉得基础不牢这篇文章就是写给你的我会把Java基础学习中最容易卡住、面试里最常被追问的点从环境配置、语法细节到面向对象、集合容器、排序算法再到数据一致性、行级权限这些工程问题一次性梳理清楚尽量说人话。我见过太多自学Java的人卡在环境变量上浪费一下午或者背了一堆“八股文”却写不出一个像样的冒泡排序又或者明明能做CRUD一被问到HashMap原理就露怯。问题往往不是不努力而是没把基础阶段的每一个知识点和它背后的“为什么”打通。这篇文章不打算做成一本电子教材而是按我自己的学习路径和带人经验来拆解哪些东西必须深挖哪些可以先用起来再补理论怎么排查常见异常怎么把基础换成面试里的分数、项目里的生产力。希望能给你一条能直接照着走的路线。1. 为什么我现在还劝人认真学Java基础1.1 Java不是“老了”是生态稳了很多新手一上来就问Java是不是已经过时了我可以直接说没有。你看招聘网站就知道了后端开发岗位里Java的占比依然是第一梯队。这背后的逻辑很简单大型企业系统需要稳定、可维护、有严格类型约束的语言Java在这方面的积累是几十年沉淀出来的。银行、电商、物流、政企系统里跑着的核心业务绝大多数还是Java。Java的“老”是成熟的代名词。它的语法变化不大你今天学的Java基础知识十年后照样能用。像Java 8的Stream、LambdaJava 11的接口私有方法Java 17、21的密封类、虚拟线程都是在原有基础上做增强而不是推翻重来。这意味着你投入在基础上面的时间长期来看是极划算的。另外Java还有一个容易被忽略的好处静态类型带来的“安全感”。一个几千人协作的大型项目里IDE能帮你提前发现类型错误编译期就把问题暴露出来这比在线上排查半夜日志要舒服太多了。学Java基础练的不仅是语法更是一种“工程化思维”这一点在你后面接触Spring、微服务、高并发时会越来越有体会。1.2 Java和Python到底怎么选别被带偏“Python简单Java难”这句话误导了很多人。真要较真Java的入门曲线确实比Python陡一点因为要理解类、对象、编译、JVM这些概念但这点门槛换来的是更强的工程约束和后端岗位适应性。我做了一张对比表供你参考维度JavaPython语法风格强类型、偏严谨类与对象是核心动态类型、偏简洁脚本感强运行方式编译为字节码跑在JVM上解释执行也有编译优化岗位方向后端开发、Android、大数据、中间件数据分析、AI、自动化脚本、后端学习曲线前期概念多中期稳定上手极快深入后性能调优有挑战就业面大厂后端岗集中岗位基数大数据/AI岗位依赖学历和算法背景较多我的建议是如果你目标明确要做Web后端、进大厂或者想长期吃技术这碗饭从Java入手更合适。如果你只是想快速做点小工具、处理数据、写爬虫注意合规或者你还在大学低年级想找“能快速出成果”的感觉Python也不错。但成年人可以全都要——先学熟一门再横向学另一门语言只是工具底层思路是相通的。1.3 一份可以直接照抄的Java自学路线图网上各种Java学习路线图很多动辄几十个节点反而让人不知道怎么安排时间。我根据自己的经历给一个收敛到“半年能干活”的版本第一阶段第1-4周JavaSE基础语法。重点是数据类型、运算符、流程控制、数组、方法、字符串处理。目标是能独立写一些几十行的小程序比如数字统计、简易计算器。第二阶段第5-8周面向对象。类与对象、封装继承多态、接口、异常、集合框架、泛型。这一阶段是面试和后续框架的基石建议边学边写小型管理系统比如学生信息管理。第三阶段第9-12周进阶特性。IO流、多线程、网络编程Socket、反射、注解、JVM基础。不需要一次吃透但要对内存模型、类加载机制有画面感。第四阶段第13-18周数据库与Web基础。MySQL、JDBC、Maven、HTTP协议、Servlet上手一个单体项目例如博客系统、商城后台。第五阶段第19-24周主流框架。Spring、SpringBoot、MyBatis至少做一个完整的项目带着知识点走一遍。然后回头补JVM调优、并发编程、设计模式去刷面试题。资源方面官方文档永远是第一手资料中文可以先看菜鸟教程、廖雪峰的Java教程视频建议找一套含实战的、更新日期近的课程。免费网站不是问题问题是别只收藏不看。2. 环境变量配置详细教程装完JDK却提示“java不是内部或外部命令”2.1 JDK版本先选对否则后面全是坑环境配置是很多人的第一个拦路虎。先说版本选择不要盲目装最新版大多数公司线上还在用Java 8或Java 17尤其是Java 8稳如老狗。Java 8对应的version通常以1.8.0_开头比如需要注意的“Java 8 Update 201”它就是1.8.0_201。我建议新手直接装JDK 17语法上有一些新特性但核心和8差异不大未来兼容性也更好。下载要通过正规渠道优先Oracle官网、OpenJDK、AdoptiumEclipse Temurin不要从来路不明的第三方下载站拿“破解版”那里面捆绑环境的概率非常高。下载时注意操作系统位数Windows系统要选x64版本有些开发工具比如某些老版本安卓测试框架对位数很敏感装错了它会提示找不到Java。2.2 Win11下JAVA_HOME和PATH的配置步骤安装JDK时最好记下安装路径比如C:\Program Files\Java\jdk-17。然后按这个步骤配置环境变量第一步右键“此电脑” - “属性” - “高级系统设置” - “环境变量”。第二步在系统变量里新建JAVA_HOME变量值填JDK的根路径比如C:\Program Files\Java\jdk-17注意不要写到bin那一层。第三步找到系统变量里的Path双击编辑新建一条%JAVA_HOME%\bin放在最前面。第四步新建CLASSPATH变量填.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar不过现在很多场景不配CLASSPATH也能跑只是为了兼容以前的工具。配置完成后一定要新开一个命令行窗口再验证不要用配置前就开着的旧终端。输入java -version和javac -version如果两条命令显示的版本一致说明环境变量生效了。不一致的话通常是系统里装过多个JDKPath里老的路径排在前面把%JAVA_HOME%\bin上移到最顶部就行。2.3 java启动失败和“找不到 java”的常见排查有时代码写对了但程序启动不起来这类问题十有八九和环境有关。我整理了几种最高频的情况第一种提示“java不是内部或外部命令”95%是JAVA_HOME没配好或者Path没生效。注意win11的系统变量和用户变量里可能同时存在Path两个都会影响命令解析找找看有没有旧的JDK路径残留。第二种java -version能跑但javac -version报错。这说明JRE存在但JDK的bin目录没进Path或者你装的是JRE而不是JDK。记住开发必须装JDK不是JRE。第三种程序运行时一直报Could not find or load main class。这往往是classpath问题也可能是类名和文件名不一致。初学者最容易在“公共类名必须与文件名一致”这个规则上翻车。第四种一些第三方工具比如安卓安全测试工具drozer、老版本IDE启动时报“找不到java”这通常是工具本身依赖64位Java而你系统里装的是32位或者工具通过JAVA_HOME找Java而JAVA_HOME没配置。另外注意这类工具还会要求特定的Java版本版本太高反而找不到这时候不是环境坏了是版本匹配问题。3. Java基础语法从数据类型到字符串判断3.1 标识符命名规则面试也会问Java里给变量、方法、类起名字必须遵守语言规定标识符可以由字母、数字、下划线_、美元符$组成但不能以数字开头不能是Java关键字比如class、public、static都不能用作变量名。这是硬性规定写错编译直接报错。具体建议见下方代码注释。// 合法的写法 int studentAge 20; String $name 张三; // $能用但不建议到处用 double _score 99.5; // 下划线开头能用同理谨慎使用 // 非法写法 // int 123abc 10; // 数字开头不行 // String class x; // 关键字不行除了合法还要“可读”。业内约定成俗的是类名大驼峰StudentInfo变量名和方法名小驼峰studentAge、getStudentName常量全大写加下划线MAX_COUNT。这个规范本身不是语法强制的但团队协作、面试手撕代码时命名是否规范直接反映你的工程素养。我面试候选人的时候如果代码里全是a、b、c这种变量名即使逻辑对了也会扣不少印象分。3.2 基本数据类型与类型转换的内存直觉Java有8种基本类型byte、short、int、long、float、double、char、boolean。很多人记不住取值范围其实不需要死记只需要有“内存大小”的直觉。byte占1个字节short占2个int占4个long占8个float占4个但精度低double占8个。char占2个字节可以存一个中文字符。boolean在JVM里的具体大小规范没有明确规定你只需要知道它只有true和false两种值。类型转换是高频出错点。从小到大自动转换比如int转long、float转double直接赋值没问题。从大到小需要强转比如double转int会丢失小数部分而且如果数值超出目标范围结果会“绕圈”变成意想不到的数字。int a 10; long b a; // 自动转没问题 double c 3.99; int d (int) c; // 强制转d 3小数直接丢弃 int big 300; byte small (byte) big; // 300超出byte范围结果不是你所想3.3 流程控制与数组越界异常排查流程控制就是if、switch、for、while这些东西没有太高深的内容但有几个细节值得注意。swtich在Java 14之后支持了箭头写法个人觉得比传统写法清爽太多for-each适合只读遍历但如果你需要知道下标或者按索引修改元素还是要用普通for。数组是新手绕不开的坎。数组的长度是固定的创建之后不能变。访问数组最后一个元素应该用arr.length - 1不是arr.length。一旦越界JVM会抛ArrayIndexOutOfBoundsException这是编译期发现不了的错误只能运行时暴露。int[] scores {90, 85, 78}; for (int i 0; i scores.length; i) { System.out.println(scores[i]); // i3时越界报错 }遇到这类异常第一反应不是查网上“怎么解决”而是看异常堆栈里的提示它会明确告诉你“数组下标越界长度3下标3”然后回代码里找循环边界。排查工具其实就藏在报错信息里。3.4 字符串处理怎样判断字符串中是否不是字母和数字字符串是Java里最常用的类之一面试里围绕String的问题也特别多。先说String的不可变性String对象一旦创建内部char数组就不能被修改了每次字符串“改变”其实都是创建了新对象。因此拼字符串不要用 硬拼循环里用StringBuilder。关于“判断字符串中是否不是字母和数字”我建议掌握两种方式public static boolean containsNonLetterOrDigit(String str) { // 方式一正则注意要匹配“任意一个”非字母数字 return str.matches(.*[^a-zA-Z0-9].*); } public static boolean containsNonLetterOrDigit2(String str) { // 方式二遍历每个字符更灵活也更快 for (char c : str.toCharArray()) { if (!Character.isLetterOrDigit(c)) { return true; } } return false; }方式二用Character.isLetterOrDigit它天然支持中英文和Unicode不会因为字符串里混了个中文就误判成“非字母数字”这个细节在中文场景里很重要。正则方式更简洁但要注意正则的写法.*[^a-zA-Z0-9].*漏掉两边的.*就会匹配不到这也是新手容易踩的坑。4. 面向对象编程与设计模式学完之后才知道面试官在问什么4.1 封装、继承、多态怎么理解才不白学面向对象是Java基础中的核心也是面试的重灾区。很多教程讲封装、继承、多态讲得特别抽象我来换个说法。封装把数据和处理数据的方法放在一起对外只暴露应该暴露的部分。生活类比就是手机你只需要知道按电源键能开机不需要知道里面电路怎么走。放到代码里私有字段加public方法就是最经典的封装。继承子类复用父类的属性和方法然后做扩展。比如动物类有吃和睡狗类继承之后再加一个看家。多态同一个方法在不同对象上有不同表现。比如同样是调用eat()猫吃鱼狗吃肉。用接口或者父类引用指向子类对象调用的方法在运行期动态绑定这就是多态。学完这些概念之后一定要能答出“重载和重写的区别”。重载是同一个类里方法名相同、参数列表不同重写是子类对父类方法的重新实现方法签名必须一致。另一个高频点是“抽象类和接口的区别”抽象类可以有构造器、可以定义普通方法适合“是什么”的继承关系接口强调能力契约Java 8之后可以有default方法适合“能做什么”的组合关系。4.2 Java对象深度拷贝的三种实现方式“对象深度拷贝”是面试里比较能拉开差距的题。先说浅拷贝和深拷贝的区别浅拷贝就是新对象里的基本类型字段是新值但引用类型字段和原对象还是同一个对象深拷贝则是连引用的对象也一起复制一份新旧完全独立。举一个场景你有一个人对象里面带了地址对象浅拷贝后两个Person的address还指向同一块内存改一个人的地址另一个也变。实现深拷贝常见三种方式第一种重写clone()方法并且让所有字段也实现Cloneable缺点是需要层层重写耦合高。第二种通过序列化和反序列化实现要求所有相关类实现Serializable可以用ObjectOutputStream写到字节数组再读回来代码简单但性能一般。第三种手写复制构造函数或者工厂方法把所有引用类型字段都new一遍最直白也最容易理解。// 序列化方式 ByteArrayOutputStream bos new ByteArrayOutputStream(); ObjectOutputStream oos new ObjectOutputStream(bos); oos.writeObject(original); ObjectInputStream ois new ObjectInputStream( new ByteArrayInputStream(bos.toByteArray())); Person copy (Person) ois.readObject();这个方式最省事但请注意静态字段不会被序列化transient字段也不会被复制。如果对象的字段里有线程、Socket这类不可序列化的资源就得退回手动拷贝了。4.3 单例模式和工厂模式面试和写代码都用得上设计模式是面向对象知识的综合运用。我不建议新手一上来就背23种设计模式先把最常用的几个搞明白尤其是单例和工厂。单例模式保证一个类只有一个实例。写法很多饿汉式、懒汉式、双重检查锁。双重检查锁最经典也最容易记public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }这里的关键点有两个第一外层判空是为了避免每次调用都加锁第二instance必须加volatile否则在多线程下可能出现“分配了内存但对象还没构造完成”的情况导致别的线程拿到半成品。面试官问“单例有什么坑”潜台词往往就是这个volatile。工厂模式则解决“创建对象的过程变来变去”的问题。比如不同支付方式你提供一个工厂方法根据参数返回对应实现类调用方不用关心具体new谁。学设计模式时不要只背UML图多想想自己项目里哪些地方创建对象的逻辑变来变去那种地方就适合抽出工厂。5. 集合容器与常用工具类别再只会ArrayList5.1 List、Set、Map的选型对照Java集合是开发中每天都要用到的工具。很多人只记住ArrayList遇到别的场景就容易写错。我直接给你一张选型表接口常用实现类底层结构是否有序是否允许重复适用场景ListArrayList动态数组有允许频繁按索引访问ListLinkedList双向链表有允许频繁头尾插入删除SetHashSetHashMap无不允许去重SetTreeSet红黑树自然排序不允许需要排序的集合MapHashMap数组链表红黑树无key不允许键值查找MapTreeMap红黑树key排序key不允许按键有序遍历ArrayList和LinkedList怎么选大多数人直接选ArrayList就对了因为数组的缓存局部性好随机访问快。LinkedList在中部插入有优势但在现代计算机的内存访问模式下实际优势不如教科书里写的大。TreeSet和TreeMap适合需要排序的场景代价是插入和查询的常数时间比Hash系列大如果不需要排序别因为它们“看起来高级”就用。另外注意线程安全HashMap不是线程安全的并发环境下用ConcurrentHashMapVector和Hashtable虽然是线程安全的但过于古老且性能差除了面试题里被拿出来比较生产环境很少用。5.2 HashMap面试连环问一次说完HashMap是Java面试必然会问的。核心要记住这条主线数组加链表加红黑树put和get的流程扩容机制为什么线程不安全。put一个key-value时流程是先对key的hashCode做一个扰动计算高16位和低16位异或然后用(n - 1) hash确定桶的位置。如果桶是空的直接放进去如果桶不为空就检查链表或红黑树里的key是否equals找到了就更新value找不到就尾插新节点。链表长度超过8且数组长度超过64时链表转红黑树把查找时间从O(n)降到O(log n)。扩容的默认加载因子是0.75意思是数组用了75%时触发扩容容量扩容为原来的2倍。为什么是0.75这是一个时间和空间的折中加载因子太高比如1.0空间利用率上去了但冲突变多查询变慢太低则浪费空间。相对的每次扩容都要重新计算所有元素的桶位置比较消耗性能所以如果能预估容量创建HashMap时直接给个初始值是个好习惯。线程不安全体现在并发put可能导致数据覆盖JDK7里还可能出现扩容时链表成环的问题导致get死循环。所以并发场景不要用HashMap老老实实用ConcurrentHashMap它的设计从JDK8开始就是CAS加synchronized锁桶头节点并发度比JDK7的分段锁更好。5.3 定时任务框架怎么选写业务系统时经常需要定时任务比如每天凌晨汇总报表、定期清理缓存。Java里的定时任务方案按复杂度从低到高排列方案特点适用场景TimerJDK自带单线程任务抛出异常会终止整个Timer极简场景不建议新代码用ScheduledExecutorService基于线程池相对安全单机、少量定时任务Spring Scheduled集成简单支持cron表达式SpringBoot项目普遍标配Quartz支持持久化、集群、复杂调度分布式、任务编排复杂XXL-JOB等分布式调度平台可视化控制台、动态调整大型分布式系统我个人建议SpringBoot项目直接用Scheduled起步一行注解就能跑Scheduled(cron 0 0 2 * * ?) public void reportJob() { // 每天凌晨2点执行 System.out.println(生成日报); }cron表达式初看难懂可以拆成“秒 分 时 日 月 周”0 0 2 * * ?就是“每天2点0分0秒”。注意周日期的?和*别混用这是老手也会踩的坑。当你需要让定时任务支持“运行中途修改时间、失败重试、在多个机器上只跑一次”时再升级到Quartz或分布式调度平台。6. 排序算法与竞赛题冒泡排序到蓝桥杯数字题6.1 冒泡排序的标准实现与优化排序算法是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 tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; } } } }这个版本的时间复杂度稳定为O(n²)。优化点在于如果某一轮从头到尾一次交换都没发生说明数组已经有序了可以直接退出外层循环。public static void bubbleSortOptimized(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 tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; swapped true; } } if (!swapped) return; } }理解冒泡排序不只是为了手撕它更重要的是理解几种经典排序背后的思想选择排序是每一轮挑最小的放前面插入排序是把当前元素往前插到合适位置快速排序是选定基准值后分区递归归并排序是先分后合。建议你把这几类的Java实现都自己敲一遍敲完就去对比它们的时间复杂度和稳定性比自己读十遍书都管用。6.2 排序方法对比与Arrays.sort的稳定性实际开发里很少手写排序都是调用工具类。java.util.Arrays.sort是最常用的但要记住一个细节它对基本类型数组用的是双轴快速排序平均O(n log n)对对象数组用的是TimSort一种稳定的归并排序变体时间复杂度O(n log n)且稳定。稳定排序的意思是什么就是相等元素的相对顺序在排序前后保持不变。举个实际场景先对学生按班级排序再按总分排序如果第二次用的是不稳定排序第一个“按班级排好”的结果可能就被打乱了班级内学生顺序会乱。所以当业务里有“多条件依次排序”的需求注意稳定性的影响。Collections.sort底层也是TimSort本质一样的。如果想让排序规则更灵活可以实现Comparable接口重写compareTo或者传一个Comparator给sort方法。我用Comparator最常用的写法来举例ListStudent students new ArrayList(); students.sort(Comparator.comparing(Student::getScore).reversed());6.3 蓝桥杯数字类题目的通用解法蓝桥杯里数字类题目出现频率很高核心就几类拆位、质数、最大公约数、进制转换、回文数。我把这几种的通用代码整理一下。先说拆位把整数每一位提取出来。我常用“取个位然后整除10”的循环直到数字变成0while (num 0) { int digit num % 10; // 处理digit num / 10; }关于进制转换十进制转二进制、十六进制可以直接用Integer.toBinaryString(num)、Integer.toHexString(num)但竞赛里有时要求自己实现那就用除基取余法。判断质数常用6倍判速法除2和3外质数都在6的倍数附近即i*i num的循环里检查num % i 0。最大公约数直接背模板辗转相除法public static int gcd(int a, int b) { return b 0 ? a : gcd(b, a % b); }还要熟悉一个数学函数Math类和Character类是Java基础里最被低估的两个工具类。Math.max、Math.abs、Math.sqrt、Character.isDigit、Character.isLetter竞赛里经常用能在十来行代码里帮你省掉无数if判断。7. 工程难题科普数据一致性、行级权限、接口防爬7.1 Java后端怎么保证数据一致性数据一致性听起来高大上对你现在可能还用不上但面试和实际项目里迟早躲不开。我先说最简单的一种单机数据库事务。一个事务里的操作要么全部成功要么全部失败。Spring里用Transactional注解就能给方法加事务但有几个坑很容易踩。第一个坑自调用失效。同一个类里的A方法调B方法B上有 Transactional是不生效的因为事务是通过代理对象实现的自调用走的是this而不是代理。第二个坑事务粒度太大。很多人把整个接口都加上事务里面还掺着远程调用、文件读写导致数据库连接长时间占用并发一高直接打满连接池。正确做法是只给必要的数据库写操作加事务远程服务调用尽量放到事务外面。分布式场景下事情就复杂了。微服务之间要保证数据最终一致常见的思路是本地消息表、事务消息比如RocketMQ的事务消息、以及幂等重试。幂等设计是最基础的一环支付回调、MQ消费者这类“可能重复调用”的接口必须保证重复调用不产生重复数据。最简单的实现是给业务表加一个唯一键或者状态机约束重复请求落到数据库时不满足唯一约束就直接忽略。7.2 行级权限的落地思路不是往SQL里硬拼行级权限的意思是不同用户登录同一个系统同一个接口返回的数据行不一样。最常见的场景就是“销售只能看自己客户的订单经理能看全部门”。它的难点在于如果只在业务代码里if判断每个接口都要改一遍维护成本很高。比较成熟的落地方案是三层配合注解定义权限规则AOP拦截请求然后在SQL层动态注入条件。大致流程是先定义一个DataScope注解标注在Controller或Service方法上里面写清按什么字段过滤比如按dept_id、按user_id再用AOP切面解析当前登录用户的角色和部门最后通过MyBatis的拦截器在SQL执行前改写语句自动加上where dept_id ?。这一步的好处是业务代码不用每个方法都写权限判断坏处是SQL改写有学习成本而且调试时容易出问题。新手阶段不需要真去实现但至少要有这个思路面试被问到可以说清楚“不是拼接SQL而是通过拦截器统一改写”这就能和只会写CRUD的人拉开差距。7.3 Controller层防爬虫的几种实用手段后端接口被爬虫盯上是很常见的事。有些爬虫会把你的列表接口当成自己的数据库高频请求拖垮服务。Controller层防爬不能只靠某一种方法要叠几层第一层频率限制。这是最有效的按IP或者按用户维度做滑动窗口计数比如一分钟内超过阈值就拒绝。实现可以借助Redis的INCR加EXPIRE代码不复杂。第二层请求头验证。很多初级爬虫不会模拟完整的浏览器Headers你至少校验User-Agent是否合法虽然这很容易被绕过但能过滤掉80%的简单脚本。第三层签名机制。在请求参数里带上服务端下发的token和时间戳服务端用同样的算法验证签名。这个防的就不是脚本而是有目标的技术爬虫。第四层验证码和风控。登录类接口加滑块验证、图形验证码高风险行为触发二次验证。注意防爬一定要考虑误伤正常用户阈值别设太死。另外再补充一点防爬不是只在Controller层做日志监控更重要看到某个IP的QPS异常升高及时做熔断和封禁这比代码里堆多少层校验都关键。8. 面试八股文与Java核心机制的“人话版”8.1 高频基础面试题清单基础阶段学完很多人就开始背八股文。先把高频题清单列出来你可以对着自查 和 equals 的区别是什么String、StringBuilder、StringBuffer 有什么区别ArrayList 和 LinkedList 的区别HashMap 的底层实现和扩容机制线程有哪几种创建方式synchronized 和 ReentrantLock 的区别JVM 内存区域划分类加载过程什么是反射你平时哪里用到过深拷贝和浅拷贝的区别这几道题看起来基础但每道都能往下深挖。比如“和equals”如果只答“比较值比较引用”只能算及格。要拿高分你得补充Object的equals默认也是比较引用所以重写equals时必须重写hashCode否则HashMap里会出现“明明同一个对象却查不到”的诡异问题。8.2 用“讲给别人听”的方式真正吃透八股背八股最忌讳的是死记硬背。我比较推荐“费曼学习法”把知识点当成讲给一个完全不懂的朋友听中间卡壳的地方就是你没理解透的地方。举个synchronized的例子。如果你能流畅讲出下面这段说明真的懂了synchronized在JDK早期是重量级锁直接依赖操作系统的互斥量线程阻塞和唤醒都要切到内核态所以性能差。JDK6之后引入了锁升级机制一个对象刚被锁时是无锁状态只有一个线程访问时升级为偏向锁避免每次加锁都做CAS一旦发生竞争升级为轻量级锁用CAS自旋尝试获取自旋超过一定次数或线程数过多才升级为重量级锁。JVM里加锁的标记就存在对象头里对象头里有Mark Word。volatile关键字则是保证可见性和禁止指令重排它不保证原子性所以i这种操作标成volatile也还是线程不安全。这些内容一层一层往下讲面试官会感觉你是“懂原理”而不是“背答案”。8.3 面试手撕代码的应试小技巧技术面试通常都有一道手撕算法题。我总结几个实用技巧。先谈思路再动手。面试官并不只关心你写对没更关心你怎么思考。拿到题目先明确输入输出和边界条件用普通人的话说一遍解法比如“我先排序然后双指针往中间夹”再开始写。代码要规范。变量名不要用a、b、temp方法名也要有意义。写完主动补测试用例正常情况、边界情况、空输入。比如写个字符串反转主动说“如果输入为空字符串应该直接返回空”就很有亮色。遇到不会的题先说能想到的暴力解法再说优化方向。最忌讳的是捂着不说话。面试官通常愿意引导只要你及时接住提示分数不会太差。基础阶段的排序、字符串处理、数组操作都要做到不看代码能手写出来。9. 常见异常与自学踩坑实录9.1 Java常见异常速查表做个表格把新手阶段最高频的异常和排查思路放一起遇到问题直接查表异常类型出现场景首选排查思路NullPointerException空引用调用方法、访问字段看堆栈行号找到哪个对象可能为null打印或断点确认ArrayIndexOutOfBoundsException访问数组越界检查循环边界是否用了length而不是length-1ClassNotFoundException运行时找不到类检查classpath和jar是否包含该类IDE重新构建NoClassDefFoundError编译期有运行期类缺失多半是打包问题或依赖冲突Port in use端口被占用Windows下netstat -ano找PIDtaskkill结束进程OutOfMemoryError内存溢出先看是堆溢出还是栈溢出加JVM参数或排查大对象ConcurrentModificationException遍历集合时修改集合用Iterator的remove方法或先收集再统一删除这些异常每个都不可怕可怕的是看到英文报错心里发慌。我的经验是先把报错信息从头到尾读一遍90%的答案就在里面。比如Exception in thread main java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3已经把数组长度和越界索引写得清清楚楚了。9.2 自学过程中我踩过的几个坑踩坑是自学的必经之路但我希望你能避开一些不必要的坑。第一个坑用记事本写代码编码没统一。Windows记事本默认可能存成GBK开发环境要求UTF-8编译一跑全是乱码。建议早一点用IDE或者至少把文件编码固定为UTF-8。第二个坑环境变量配完不重开终端。Windows环境变量的修改不会实时同步到已经打开的终端窗口你配了半天以为没生效其实只要重新开个命令行窗口就好了。这个问题我当年至少浪费了半小时。第三个坑遇到bug不读日志直接把报错复制到搜索引擎。正确顺序是先读日志、理解堆栈再带着自己的想法搜索关键词。比如看到空指针先想“哪个变量是null”而不是直接搜“空指针怎么解决”搜出来的答案不一定和你场景匹配。第四个坑跟着视频“抄代码”。眼睛会了手不会哪怕是最简单的代码也要关掉视频自己敲一遍。敲完再改改需求比如把案例里的学生成绩换成订单金额你才知道自己是真的会了。9.3 给自学者一句实在的建议学习Java基础最大的障碍不是智商而是“进度感”。我见过很多人学了半个月还停留在“看过语法”的阶段因为一直在收藏资料、规划路线就是不动手。我的建议很朴素每天至少写几十行代码不要多但必须是自己想出来的逻辑。哪怕是写一个很笨的冒泡排序然后看着它跑出结果也比刷三天教程视频有用。基础阶段把这一步坚持住后面的框架、项目、面试题都会顺利得多。我自己最开始学Java时也卡在环境变量上折腾了一整个下午。后来回头看那段“折腾”其实特别值因为我从那时起就养成了一个习惯遇到问题先看报错信息再拆解环境最后动手验证。这个习惯一直用到现在。所以如果你今天刚好也卡在某个很基础的问题上别灰心这恰恰说明你在真正进入状态了。
返回列表