X

X

Java小白
知识星球
宁波
Java后端
1998
宁波大学
Java后端

该用户为会员

会员专享项目教程/ 答疑等服务

Day 106 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 总结: 单词复习: 认识:69,模糊:21,忘记:24,顽固:3 问题: ArrayBlockingQueue 与 LinkedBlockingQueue 在并发性能和锁机制上有何区别?ArrayBlockingQueue 用单锁,生产与消费互斥,适合固定容量、需公平控制的场景;LinkedBlockingQueue 用双锁分离入队/出队,可并行操作,吞吐量更高,但默认无界有 OOM 风险。 线程池内任务调度时 BlockingQueue 的作用是什么?在线程池中,BlockingQueue 用于缓冲待执行任务,并在生产者(提交任务的线程)与消费者(工作线程)间做流量控制与同步。队列满时阻塞提交,空时阻塞取任务,从而避免忙等和资源过载,是线程池任务调度的核心缓冲与协调机制。 使用 put 方法与 offer 方法处理队列满时有何不同表现?put 会无限期阻塞生产者线程直至有空间,offer 则立即返回 false 不阻塞。前者用于强一致、必须入队的场景,后者适用于需快速失败或可降级的流控场景。 自动装箱/拆箱发生的常见场景:1.赋值2.方法调用3.算术运算4.集合操作 自动装箱/拆箱陷阱:NullPointerException、性能开销、== 运算符在用于对象时,比较的是内存地址;而 .equals() 方法(如果被正确重写)比较的是对象的内容(值)。Integer 缓存:默认情况下,对于 -128 到 127 之间的整数,通过自动装箱(或 Integer.valueOf())创建的 Integer 对象会被缓存。如果值在这个范围内,你会得到指向同一个缓存对象的引用。 int与Integer的区别在以下几点:存储的位置,性能,默认值,类型,比较方式,使用场景 默认的 equals() 方法比较的是两个对象的内存地址,即判断它们是否为同一个对象的引用。这被称为“引用相等性” equals() 方法的重写协定:1.自反性:一个对象必须等于它自己。2.对称性3.一致性:只要对象的状态不变,equals() 的结果就不应该改变。4.传递性5.非空性:任何对象都不等于 null。 写了 equals() 方法,那么你必须重写 hashCode() 方法,hashCode() 用于为对象生成一个整数“哈希码”。 - 如果两个对象根据 equals() 相同,hashCode也必须相同 - 如果两个对象根据 equals() 方法是不相等的,它们的 hashCode() 不一定相同,但最好不相同,提高哈希表的性能。 如果没有重写hashCode会导致,本来两个内容一样的对象会导致hashCode不同,在放到hashMap时,会分配到不同的桶,会导致找不到原先的对象。 重写equals:1先==比较两个对象是否是同一个引用,2检查参数是否为 null,以及类型是否匹配,使用 getClass() 而不是 instanceof 可以确保比较的对象是完全相同的类3.将参数转换为正确的类型,对用到的关键字进行比较。 hashCode重写:使用Objects.hash(Object... values),传入的参数是equals里用到的所有字段。 如果你使用的是 Java 14 或更高版本,使用这个public record PersonRecord(String name, int age) {}

Day 105(昨天的打卡) ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 总结: 单词复习: 认识:75,模糊:27,忘记:23,顽固:3 问题: 受检测异常:Java编译器检查这类异常,需要在方法签名中使用throws或者使用try-cath捕获,比如IOException 非受检测异常:主要是程序逻辑上的问题,比如类型转换失败。 finally:无论是否发生异常,都保证会被执行。 但存在问题:异常覆盖:catch 块中已经抛出了一个异常,而 finally 块在执行清理时又抛出了一个新的异常,JVM 会丢弃原始的异常,只向上抛出 finally 中的异常。破坏正常的返回逻辑:finally 块中使用了 return 语句,它会直接导致方法结束,并覆盖掉 try 或 catch 块中原本要返回的值或要抛出的异常。并非绝对可靠。 throw:代码中主动抛出异常对象,通常是自己定义的业务逻辑 throws:用于方法签名上,主要是告诉调用者这个方法会抛出的异常,调用者可以try-catch或者继续向上抛。 只捕获你能处理的异常,不要“吞掉”异常,使用 finally 或 try-with-resources 释放资源,为异常提供有意义的信息在抛出异常时,附带清晰的描述性消息,异常链当捕获一个底层异常并抛出一个新的高层异常时,应将原始异常(cause)包装进去。这保留了完整的堆栈跟踪信息,便于排查问题。优先使用非受检异常处理编程错误,不要用异常来控制程序流程 Java的反射机制,就是程序在运行期间,可以像照镜子一样,动态地获取任何一个类的信息(它的属性、方法、构造函数等),并且能够动态地创建对象、调用方法、修改属性。它赋予了Java一种“自我剖析”和“自我操作”的能力。 获取Class对象有三种主要方式: 通过对象获取:anyObject.getClass() 通过类名获取:ClassName.class 通过类的全限定名获取:Class.forName("com.example.MyClass") 反射的优点: 框架设计的灵魂:Spring/SprintBoot的IoC控制反转(不需要自己去new对象由容器去创建对象) 动态代理:在运行时创建一个实现了指定接口的代理类,用来增强原始类的功能(比如添加日志、事务管理),这是AOP(面向切面编程)的基础。 工具和IDE:比如你用的Eclipse/IntelliJ IDEA,它的代码提示、自动补全、调试器等功能,都需要通过反射来分析你写的类。 注解处理器:需要在运行时被读取和处理,这也要靠反射。 缺点:性能开销大,破坏封装、类型不安全、代码可读性差,维护困难。 阻塞队列:生产者生产数据时,没有地方放了会阻塞,消费者拿数据时没有数据会阻塞。BlockingQueue 自动地协调了生产者和消费者之间的速度差异 ArrayBlockingQueue: - 由数组支持的有界阻塞队列。创建时必须指定容量,且容量不可变。 - 公平/非公平:可以通过构造函数 new ArrayBlockingQueue(capacity, fair) 设置公平性。公平模式下,等待的线程按 FIFO 顺序访问队列;非公平模式下,可能存在插队,吞吐量通常更高。 - 性能:内部使用一个锁(ReentrantLock)来控制生产者和消费者的访问,意味着两者不能同时操作。 LinkedBlockingQueue: - 底层结构:由链表支持的阻塞队列。 - 特点: - 可选有界:可以指定容量,也可以不指定。如果不指定,默认容量是 Integer.MAX_VALUE,相当于一个无界队列。 - 无界时的风险:如果生产者速度远快于消费者,可能导致内存耗尽(OOM)。 - 性能:内部使用两个锁(putLock 和 takeLock),一个用于入队,一个用于出队。这种“读写分离”的设计使得生产者和消费者可以并行操作,在高并发场景下吞吐量通常高于 ArrayBlockingQueue。 PriorityBlockingQueue: - 底层结构:一个支持优先级的无界阻塞队列。 - 特点: - 无界 (Unbounded):队列容量没有限制。 - 优先级排序:存入的元素必须实现 Comparable 接口,或者在构造时传入 Comparator。队列会根据元素的优先级进行排序,优先级高的元素先出队。 - 它不保证 FIFO,而是保证每次 take 出来的都是当前队列中优先级最高的元素。 SynchronousQueue: - 底层结构:一个不存储元素的阻塞队列,容量为 0。 - 特点: - 容量为零:它没有任何内部容量来缓存数据。 - 直接传递 (Hand-off):每个 put 操作必须等待一个对应的 take 操作,反之亦然。它更像一个“一手交钱,一手交货”的交易场所,而不是一个仓库。 - 高吞吐量:非常适合传递性场景,因为避免了数据在队列中的存储和管理开销。Java 的 Executors.newCachedThreadPool() 线程池就使用了它。 DelayQueue: - 底层结构:一个支持延时获取元素的无界阻塞队列。 - 特点: - 延时:队列中的元素只有在其指定的延迟时间到了之后,才能被消费者 take 出来。 - 元素类型:存入的元素必须实现 Delayed 接口(该接口又继承了 Comparable)。 - 应用场景:非常适合实现定时任务、缓存过期等功能。 应用场景:线程池,消息中间件,系统日志,消费者生产者

Day 104 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 总结: 单词复习: 认识:48,模糊:36,忘记:26,顽固:11 问题: String线程安全是对于只读取来说,如果A线程修改了str的引用,B线程访问的是修改引用后的str。 字符串常量池依赖不可变性的原因是什么?str1 与str2引用的都是hello,如果是可变的,str1修改为www,str2也是www,本来要用hello结果导致混乱。 EnumSet:要求枚举值来自同一个枚举类 EnumMap:以枚举类型作为key 任何时候,当你需要表示一个固定的、有限的常量集合时,都应该优先考虑使用枚举。 为什么要使用枚举:1.类型安全 2.代码清晰,可读性强 3.功能强大 4.易于维护 5.与集合框架无缝集成 枚举如何实现接口并支持不同常量的独立行为?在常量后跟{}写匿名内部类覆写接口的方法来实现接口,对于每个常量来说都是独立的子类,调用方法的时候按照多态来执行。所以会有不同常量的独立行为。同理抽象方法也是的。

Day 103 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 3. 智能云图库-团队空间管理 总结: 单词复习: 认识:61,模糊:33,忘记:16,顽固:2 问题: 匿名内部类与普通内部类的区别:匿名内部类只能在定义处一次性使用,没办法复用,因为匿名内部类无构造方法,无类名导致的。 匿名内部类没有构造方法也没有可以替代的方法:使用实例初始化块在对象创建时完成字段赋值与初始化逻辑 匿名内部类访问局部变量:要求局部变量为final或事实上是final,因为局部变量在栈中,当用完后会被销毁,但内部类是在堆中,可能内部类还存在,为了能继续引用,JVM 在编译期将变量值拷贝进内部类,并在字节码层面添加合成字段持有该副本。若变量可变,则副本与原始值可能不一致,破坏闭包语义。 什么情况下只能用匿名内部类而无法用 Lambda 表达式?需继承类(含抽象类)、实现多抽象方法接口、内部需定义实例字段或多个方法,或对 this 指向自身实例有强依赖。因为 Lambda 只能针对单抽象方法接口且无状态,this 指向外部类,无法满足这些结构与语义需求。 同一类被不同加载器加载会造成什么问题?同一类被不同类加载器加载会导致JVM视为两个不同类,引发类型不匹配、ClassCastException及逻辑错误。双亲委派模型通过统一委派至父加载器避免此问题,确保类全局唯一性与核心库安全。 Tomcat如何实现不同Web应用间的类库隔离?Tomcat通过为每个Web应用创建独立的WebAppClassLoader,并重写loadCla ss()方法打破Java双亲委派模型,实现类库隔离。其核心是先自加载再委派,让不同应用可各自使用私有版本的依赖,避免版本冲突。 非静态内部类为何不能定义非 final 的静态成员?因为其内部类实例天然绑定外部类实例,而静态成员属于类、与实例无关,两者在设计语义上冲突。Java 为避免“既属类又属实例”的逻辑混乱,直接禁止此类定义,仅允许 static final 编译时常量。仅 static final 编译时常量被允许,因其值在编译期确定并可内联,不触发运行时类加载/初始化矛盾,避免了语义与实现上的不一致。 创建非静态内部类实例时必须依赖外部类实例的原因是什么?非静态内部类对象隐式持有外部类实例引用,创建时必须通过 outerInstance.new Inner(),生命周期与外部实例绑定。

Day 102 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 总结: 单词复习: 认识:50,模糊:25,忘记:35,顽固:5 问题: 浅拷贝与深拷贝:主要区别在于,对象内部的成员,如果存在引用类型,那浅拷贝会导致原对象以及拷贝的对象使用的引用数据是同一份(拷贝的是地址),深拷贝是对这段引用类型的数据也完全拷贝过来。 实现深拷贝的方法:1.拷贝构造函数 2.序列化 3. 重写clone方法 少量修改 -> String 单线程大量修改 -> StringBuilder 多线程大量修改 -> StringBuffer (谨慎使用) 一个类里面,有两个方法,这两个方法都用当前这个类的静态锁,那多线程的时候,是依次还是并发执行? 使用隐式锁即 synchronized 直接加在方法上: - 如果是同一个对象实例:它们使用的是同一把实例锁(this),所以是依次执行(互斥)的。 - 如果是不同的对象实例:每个对象都有自己独立的 this 锁,所以它们之间可以并发执行。 使用的是显式锁(如 ReentrantLock): - 如果是同一个对象实例:两个方法竞争的是同一个 lock 对象,所以是依次执行(互斥)的。 - 如果是不同的对象实例:每个对象都有自己独立的 lock 成员变量,所以它们之间可以并发执行。 使用了特定的静态锁对象: - 无论是不是同一个对象实例,因为锁是 static 的(全局唯一),所以它们始终是依次执行(互斥)的。 耦合与解耦: 耦合 (Coupling):模块之间“绑得有多紧”。 解耦 (Decoupling):把绑得紧的模块“松开”,让它们各自独立。

Day 101 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 3. 智能云图库-团队空间管理 总结: 单词复习: 认识:58,模糊:20,忘记:29,顽固:4 问题: 基本类型:局部变量在栈中,实例变量在堆,静态变量在方法区或堆 引用数据是在哪:引用在哪,看它在哪定义;对象在哪,永远在堆里。

Day 100 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 3. 智能云图库-团队空间管理 总结: 单词复习: 认识:47,模糊:9,忘记:39,顽固:3 问题: JDK动态代理为什么要代理类实现一个接口:因为生成的代理对象已经继承了一个类,Java中只允许单继承,所以通过实现接口的形式来增强方法,这也导致了,只能增强接口的方法,无法增强自身特有的方法。 java.util.Objects.equals(name, person.name):这里的equals调用的方法内部是 return (a == b) || (a != null && a.equals(b));先比较地址是否相同,再比较内容。

Day 99 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 4. 智能云图库-团队空间管理 总结: 单词复习: 认识:59,模糊:24,忘记:27,顽固:3 这几天有点忙,但也一直在学,忘记了打卡,今天开始接着打

Day 98 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 1. Java中Thread.sleep(0)的作用是什么? 2. 为什么Netty不使用ThreadLocal而是自定义一个FastThreadLocal? 总结: 单词复习: 认识:53,模糊:28,忘记:19 ,顽固:2

Day 97 ✅ 今天做了: 1. 背单词√ 2. 面试题:√ 1. Java创建线程池有哪些方式? 2. Synchronized修饰静态方法和修饰普通方法有什么区别?? 3. Java中如何创建多线程? 总结: 单词复习: 认识:62,模糊:26,忘记:12 ,顽固:2

下载 APP