有效括号
package hot100; import java.util.*; public class lc20 { /*有效的括号 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左括号。 示例 1: 输入:s = "()" 输出:true*/ public boolean isValid(String s) { //栈 Map<Character, Character> hashmap = new HashMap<>(); hashmap.put('(', ')'); hashmap.put('{', '}'); hashmap.put('[', ']'); Deque<Character> stack = new ArrayDeque<>(); for(int i = 0; i < s.length(); i++){ char c = s.charAt(i); if(c == '(' || c == '[' || c == '{'){ stack.push(c); }else{ if(stack.isEmpty()){ return false; } char paidui = stack.pop(); if(hashmap.get(paidui) == c){ continue; }else{ return false; } } } return stack.isEmpty()? true : false; } public static void main(String[] args) { lc20 solution = new lc20(); String s ="()"; System.out.println(solution.isValid(s)); } }最小栈
package hot100; import java.util.*; public class MinStack { public MinStack() { } Deque<int[]> stack = new ArrayDeque<>(); public void push(int value) { //这个写法更好 int min = stack.isEmpty() ? value : Math.min(stack.peek()[1], value); stack.push(new int[]{value,min}); } public void pop() { stack.pop(); } public int top() { return stack.peek()[0]; } public int getMin() { return stack.peek()[1]; } public static void main(String[] args) { MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); System.out.println(minStack.getMin()); // 返回 -3 minStack.pop(); System.out.println(minStack.top()); // 返回 0 System.out.println(minStack.getMin()); // 返回 -2 } }字符串解码
package hot100; public class lc394 { /*字符串解码 给定一个经过编码的字符串,返回它解码后的字符串。 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。 测试用例保证输出的长度不会超过 105。 示例 1 输入:s = "3[a]2[bc]" 输出:"aaabcbc"*/ int i = 0; public String decodeString(String s) { StringBuilder ans = new StringBuilder(); while(i < s.length() && s.charAt(i) != ']'){ char c =s.charAt(i); if(Character.isDigit(c)){ int k= 0; while(Character.isDigit(s.charAt(i))){ k = k*10 + s.charAt(i)-'0'; i++; } i++; String temp = decodeString(s); i++; while(k>0){ ans.append(temp); k--; } }else{ ans.append(c); i++; } } return ans.toString(); } public static void main(String[] args) { String s = "3[a]2[bc]"; lc394 solution = new lc394(); System.out.println(solution.decodeString(s)); } }每日温度
package hot100; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; public class lc739 { /* 每日温度 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 示例 1: 输入: temperatures = [73,74,75,71,69,72,76,73] 输出: [1,1,4,2,1,1,0,0]*/ public int[] dailyTemperatures(int[] temperatures) { Deque<Integer> stack = new ArrayDeque<>(); int[] ans = new int[temperatures.length]; for(int i = 0; i < temperatures.length; i++){ //入栈 if(stack.isEmpty()){ stack.push(i); }else if(temperatures[i] <= temperatures[stack.peek()]){ stack.push(i); }else{ while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){ int jishu = stack.pop(); ans[jishu] = i - jishu; } //对比完也要放回去 stack.push(i); } } return ans; } public static void main(String[] args) { int[] nums = new int[]{73,74,75,71,69,72,76,73}; lc739 solution = new lc739(); System.out.println(Arrays.toString(solution.dailyTemperatures(nums))); } }柱状图中最大的矩形
找左右两遍的边界
package hot100; import java.util.ArrayDeque; import java.util.Deque; public class lc84 { /*柱状图中最大的矩形 已解答 困难 相关标签 premium lock icon 相关企业 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。*/ public int largestRectangleArea(int[] heights) { Deque<Integer> stack = new ArrayDeque<>(); int max = 0; for(int i = 0; i < heights.length; i++){ //每次push的都是i while(!stack.isEmpty()&&heights[i] < heights[stack.peek()]){ int chang = heights[stack.pop()]; //前面比他大的都走路,就是找左右两边的边界 int left = stack.isEmpty() ? -1 :stack.peek(); int kuang = i - left -1; int area = chang * kuang; max = Math.max(area, max); } stack.push(i); } while(!stack.isEmpty()){ int chang = heights[stack.pop()]; int left = stack.isEmpty() ? -1 :stack.peek(); int kuang = heights.length - left -1; int area = chang * kuang; max = Math.max(area, max); } return max; } public static void main(String[] args) { lc84 solution = new lc84(); int[] nums = new int[]{2,1,5,6,2,3}; System.out.println(solution.largestRectangleArea(nums)); } }随机知识
一、Java 语言特性
1. 面向对象三大特性
封装:隐藏内部实现,通过方法暴露访问接口,保护数据。
继承:子类复用父类代码,
extends单继承,implements多实现接口。多态:同一方法调用,不同对象表现不同行为。基于继承/接口,运行时动态绑定。
重载(编译时多态):方法名相同,参数列表不同。
重写(运行时多态):子类覆盖父类方法,
@Override。
2. 重载和重写的区别
| 区别点 | 重载 | 重写 |
|---|---|---|
| 发生位置 | 同一个类 | 父子类 |
| 方法签名 | 方法名相同,参数列表必须不同 | 方法名和参数列表完全相同 |
| 返回类型 | 可不同 | 相同或其子类(协变返回) |
| 访问权限 | 无要求 | 不能比父类更严格 |
| 异常 | 无要求 | 不能抛出更宽泛的检查异常 |
| 多态 | 编译时 | 运行时 |
3. 抽象类和接口的区别
| 对比 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | 不能 | 不能 |
| 构造器 | 有 | 无 |
| 方法 | 可有抽象方法+具体方法 | JDK8 前仅抽象方法;8 后可 default/static;9 后可 private 方法 |
| 变量 | 可定义各种变量 | 仅 public static final 常量 |
| 继承 | 单继承 | 多实现(可继承多个接口) |
| 设计理念 | is-a,共性提取 | like-a,行为规范 |
4. == 和 equals 的区别
==:基本类型比值,引用类型比内存地址。equals():Object 类默认也是比地址,需要重写(如 String、Integer 已重写)来比较内容。规范:重写 equals 必须同时重写 hashCode,保证相等对象的哈希码一致(HashMap 规则)。
5. hashCode 和 equals 的约定
如果两个对象 equals 相等,hashCode 必须相等。
如果 hashCode 相等,equals 不一定相等(哈希冲突)。
重写 equals 必须重写 hashCode。
二、数据类型与核心类
1. 基本数据类型(8 种)
整数:byte(1)、short(2)、int(4)、long(8)
浮点:float(4)、double(8)
字符:char(2)
布尔:boolean(1)
自动装箱/拆箱:基本类型与包装类(Integer、Long 等)自动转换。例如
Integer i = 10使用Integer.valueOf装箱,int j = i拆箱调用intValue。
2. String 为什么不可变?好处?
String类被final修饰,内部保存字符数组private final char value[](JDK9+ 为byte[]),不提供修改方法,返回新对象。好处:安全(网络参数、类加载)、线程安全、字符串常量池复用、HashMap 键的稳定性。
3. String、StringBuilder、StringBuffer 区别
String:不可变,频繁拼接产生大量对象。StringBuilder:可变,线程不安全,单线程高效。StringBuffer:可变,线程安全(synchronized 方法),效率稍低。
4. 包装类的缓存
Integer默认缓存 -128~127(可调上限),valueOf会利用缓存,new Integer 不会。Byte/Short/Long/Character也有类似固定范围缓存。Float/Double无缓存。
三、异常机制
1. 异常层次结构
ThrowableError:JVM 自身错误(OOM、StackOverflow),不处理。Exception:程序可捕获处理。检查异常(Checked):编译时必须捕获或声明,如
IOException、SQLException。非检查异常(Unchecked):运行时异常,
RuntimeException及其子类,如NullPointerException、IndexOutOfBoundsException。
2. try-catch-finally 执行顺序与 return
finally 总是执行(除非
System.exit(0)或 JVM 崩溃)。如果 try 中有 return,finally 先执行再 return;如果 finally 中也 return,会覆盖 try 的返回值。
3. throw 和 throws 的区别
throw:在方法体内抛出异常对象,一次只能抛一个。throws:在方法签名声明可能抛出的异常类型,可声明多个。
四、集合框架(高频必考)
1. List、Set、Map 区别
List:有序,可重复。实现:ArrayList(数组)、LinkedList(双向链表)。Set:无序(除 LinkedHashSet、TreeSet),不可重复。实现:HashSet(基于 HashMap)、TreeSet(红黑树)、LinkedHashSet(哈希+链表)。Map:键值对,Key 不可重复。实现:HashMap、TreeMap、LinkedHashMap、Hashtable(线程安全,遗留类)。
2. ArrayList 和 LinkedList 区别
| 对比 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | Object[] | 双向链表 |
| 随机访问 | O(1) | O(n) |
| 增删 | 尾插 O(1),中间 O(n) | 头尾 O(1),中间 O(1) (定位后) |
| 内存 | 连续空间,有预留容量 | 节点存储指针,额外空间开销 |
| 实现接口 | List, RandomAccess | List, Deque |
3. HashMap 实现原理(JDK8+)
数据结构:数组 + 链表 + 红黑树。默认容量 16,负载因子 0.75。
哈希计算:
h = key.hashCode(),(n-1) & (h ^ (h >>> 16))。put 流程:桶为空直接放;有元素判断是否树化(链表长度≥8 且数组长度≥64),是转红黑树,否则尾插法;key 相同则覆盖 value。
扩容:容量×2,rehash 后,元素要么在原位置,要么在原位置+旧容量。
为何线程不安全:1.7 头插法扩容死循环;1.8 尾插法仍可能数据覆盖、size 不准确。
4. ConcurrentHashMap 实现
JDK7:分段锁 Segment(继承 ReentrantLock),默认 16 个段,并发度 16。
JDK8:CAS + synchronized,锁桶的首节点,粒度更细;红黑树提高查询;支持多线程协助扩容。
5. HashSet 如何保证不重复?
内部基于 HashMap,元素作为 Key,value 为一个常量 Object。add 调用 map.put,利用 HashMap 的 key 唯一性。
6. Comparable 和 Comparator
Comparable:自然排序,类内部实现compareTo方法。Comparator:外部比较器,可定义多种排序规则,不影响原有类。
五、I/O 流
1. 字节流和字符流
字节流:InputStream/OutputStream,处理二进制,如图片、视频。
字符流:Reader/Writer,处理文本,内置编解码,按字符传输。
2. BIO、NIO、AIO 区别
BIO:阻塞 IO,一个线程处理一个连接。
NIO:非阻塞,Selector 多路复用,Channel + Buffer,适合高并发连接。
AIO:异步 IO,回调通知,真正的异步非阻塞,Windows 支持,Linux 模拟不成熟。
六、多线程基础(核心)
1. 线程生命周期
NEW→RUNNABLE→BLOCKED/WAITING/TIMED_WAITING→TERMINATED。sleep():TIMED_WAITING 不释放锁;wait():WAITING 释放锁,需在同步块内,通过notify/notifyAll唤醒。join():等待线程结束。yield():让出 CPU,回到就绪态。
2. 创建线程的方式
继承
Thread类。实现
Runnable接口。实现
Callable接口,结合FutureTask获取返回值。推荐使用线程池管理。
3. synchronized 和 Lock 区别
| 对比 | synchronized | Lock (ReentrantLock) |
|---|---|---|
| 层面 | JVM 关键字,自动释放 | API,需 try-finally 手动释放 |
| 中断 | 等待时不可中断 | lockInterruptibly()可中断 |
| 公平锁 | 非公平 | 可公平可非公平 |
| 条件 | wait/notify 单个条件 | Condition 多条件精确唤醒 |
| 尝试获取 | 无 | tryLock()可尝试、超时获取 |
| 性能 | JDK6 后优化,与 Lock 相当 | 良好 |
4. 线程池核心参数与执行流程
参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue(阻塞队列)、threadFactory、handler(拒绝策略)。
流程:
线程数 < corePoolSize → 创建新线程执行。
队列未满 → 入队。
队列满且线程数 < max → 创建新线程。
队列满且线程数 = max → 执行拒绝策略。
拒绝策略:
AbortPolicy(抛异常)、CallerRunsPolicy(调用者执行)、DiscardOldestPolicy(丢最旧)、DiscardPolicy(直接丢弃)。
5. volatile 作用
保证可见性:写后立即刷新到主存,读从主存取。
禁止指令重排序:通过内存屏障(写前后,读后插屏障)。
不保证原子性:适合一写多读,不适合 i++。
七、JVM 基础
1. 内存模型(运行时数据区)
线程私有:程序计数器、虚拟机栈(栈帧局部变量表等)、本地方法栈。
线程共享:堆(对象实例、数组)、方法区(元空间,类信息、常量、静态变量,JDK8 后移出永久代)。
2. 对象创建过程
类加载检查。
分配内存(指针碰撞/空闲列表,取决于垃圾收集器是否有压缩整理)。
初始化零值。
设置对象头。
执行
<init>方法。
3. 类加载过程与双亲委派
过程:加载 → 验证 → 准备(分配静态变量默认值)→ 解析(符号引用→直接引用)→ 初始化(执行 static 块)。
双亲委派:向上委托父加载器加载,保证核心类安全,避免重复加载。
4. 垃圾回收
判断对象死亡:引用计数法(循环引用问题);可达性分析(GC Roots 开始)。
GC Roots:栈中引用、静态变量、常量、JNI 引用等。
回收算法:
标记清除:碎片多。
标记整理:无碎片,但耗时。
复制:无碎片,空间利用率低,适合新生代。
分代模型:新生代(Eden + Survivor)、老年代。Minor GC 频繁,Full GC 应避免。
常见收集器:Serial、ParNew、Parallel Scavenge、CMS、G1(区域化,可预测停顿)、ZGC(低延迟)。
5. 强、软、弱、虚引用
强引用:
Object o = new Object(),死不回收。软引用:
SoftReference,内存不足时回收。弱引用:
WeakReference,下次 GC 就回收。虚引用:
PhantomReference,配合引用队列,用于跟踪回收状态。
八、Java 新特性(JDK8 重点)
1. Lambda 表达式与函数式接口
实现接口方法,要求接口只有一个抽象方法(
@FunctionalInterface)。常用:
Runnable、Comparator、Supplier、Consumer、Predicate。
2. Stream API
流程:创建流 → 中间操作(filter, map, sorted) → 终止操作(collect, foreach, reduce)。
惰性求值,可并行流。
3. Optional
避免 NullPointer,
Optional.ofNullable(x).orElse(default)等。
以上是 Java 基础的概要,建议结合具体面试题进行模拟练习,尤其是集合和多线程部分。需要针对某一道题的深度解析可以再提问。
碎碎念:后续会更新每天学习的八股和算法 题,开始准备秋招的第75天。努力连续更新100天!以后每天就按,秋招项目【java +agent】,科研,必做项目,算法,八股,锻炼身体来总结。
总结:讨厌杂活,感觉自己好笨,但是坚持把
1.hot100 【acm 】 73/100 ,2到3h,快速把hot100过一遍【12/20】
2.秋招项目,【java 项目】,继续
【agent 项目 】,继续
3.科研。确定方向就搞就可以了
4.实习;
6.背八股,无
7.锻炼身体,无
要点:坚持