有效括号
java
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));
}
}
最小栈
java
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
}
}
字符串解码
java
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));
}
}
每日温度
java
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)));
}
}
柱状图重点的最多矩形
找左右两遍的边界
java
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. 异常层次结构
-
Throwable-
Error: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 基础的概要,建议结合具体面试题进行模拟练习,尤其是集合和多线程部分。需要针对某一道题的深度解析可以再提问。
碎碎念:后续会更新每天学习的八股和算法 题,开始准备秋招的第74天。努力连续更新100天!以后每天就按,秋招项目【java +agent】,科研,必做项目,算法,八股,锻炼身体来总结。
总结:讨厌杂活,感觉自己好笨,但是坚持把
1.hot100 【acm 】 67/100 ,2到3h,快速把hot100过一遍【11/20】
2.秋招项目,【java 项目】,继续
【agent 项目 】,继续
3.科研。确定方向就搞就可以了
4.实习;
6.背八股,无
7.锻炼身体,无
要点:坚持