depth: 3 title: 目录一、Java集合框架1.1 什么是集合框架Java集合框架Java Collection Framework也叫容器Container是java.util包下一组接口Interfaces和实现类Classes的总称。它就像一个“数据收纳箱”能把多个元素集中管理支持快速的存储、检索、增删改查操作。1.2 集合框架的结构总览集合框架的核心分为两大体系Collection单元素集合和Map键值对集合再加上迭代器、比较器和工具类构成了完整的结构顶层接口Iterable支持迭代、Collection单元素核心、Map键值对核心Collection分支List有序、可重复 ArrayList动态顺序表LinkedList双向链表Vector线程安全顺序表Stack栈特殊顺序表Queue队列先进先出PriorityQueue优先队列Deque双端队列继承自QueueSet无序、不可重复HashSet哈希桶实现TreeSet红黑树实现支持有序Map分支键值对K-VHashMap哈希桶查询快TreeMap红黑树键有序辅助组件Iterator迭代器、Comparable/Comparator对象比较、Arrays/Collections工具类二、算法复杂度2.1 时间复杂度时间复杂度不是计算代码实际运行时间而是衡量“基本操作执行次数”与输入规模数据量N的关系用大O渐进表示法描述。1大O推导3步法则用“1”取代所有加法常数比如2N10→2N1只保留最高阶项比如2N1→2N去掉最高阶项的系数比如2N→O(N)。2常见时间复杂度案例场景代码示例时间复杂度分析常数操作循环100次O(1)执行次数固定与N无关线性操作遍历长度为N的数组O(N)执行次数随N线性增长嵌套循环冒泡排序O(N²)最坏情况执行N*(N-1)/2次最高阶项为N²二分查找有序数组中找目标值O(log₂N)每次排除一半数据比如N8时只需3次2³8递归斐波那契fib(N)fib(N-1)fib(N-2)O(2ⁿ)递归树呈指数增长N20时就有百万级操作注意实际分析时我们更关注“最坏情况”比如数组查找最坏要遍历所有元素因为它决定了代码的“上限”。2.2 空间复杂度空间复杂度衡量代码运行时“额外占用的变量个数”同样用大O表示。现在计算机内存充足空间复杂度优先级低于时间复杂度但仍需了解O(1)只用到常数个变量比如冒泡排序只用了end、sorted等变量O(N)用到与N相关的数组或递归栈比如用数组存斐波那契数列或递归N次的阶乘。三、泛型在集合框架中泛型解决了容器存多种类型数据导致的类型转换异常问题让代码更通用、更安全。3.1 包装类泛型只支持“引用类型”而Java的基本类型int、char等不是引用类型因此需要“包装类”将基本类型封装成对象基本类型包装类基本类型包装类byteBytefloatFloatshortShortdoubleDoubleintIntegercharCharacterlongLongbooleanBoolean装箱与拆箱装箱基本类型→包装类比如int i10→Integer iiInteger.valueOf(i)拆箱包装类→基本类型比如int jii.intValue()自动装箱/拆箱Java会自动完成转换Integer ii10、int jii底层还是调用valueOf装箱和intValue拆箱。inta0;Integeria;IntegeriInteger.valueOf(a);//底层intbi;intbbi.intValue()//底层面试题以下代码输出什么Integera127,b127;Integerc128,d128;System.out.println(ab);// trueSystem.out.println(cd);// false原因Integer有缓存池-128~127127在缓存中a和b指向同一个对象128不在缓存中会新建对象比较地址时为false。3.2 类型参数化泛型的本质是“将类型作为参数传递”让容器只存指定类型的数据编译时就做类型检查避免运行时类型转换异常。1泛型类的定义与使用比如定义一个“只能存指定类型的数组容器”// 泛型类T是类型占位符表示“待指定的类型”classMyArrayT{privateObject[]arraynewObject[10];// 底层用Object数组存储// 存值val必须是T类型publicvoidsetVal(intpos,Tval){array[pos]val;}// 取值返回T类型无需强制转换publicTgetPos(intpos){return(T)array[pos];}}// 使用泛型类publicclassTest{publicstaticvoidmain(String[]args){// 指定存Integer类型MyArrayIntegerarrnewMyArray();arr.setVal(0,10);// 正确arr.setVal(1,hello);// 编译报错类型不匹配intnumarr.getPos(0);// 无需强制转换直接用}}2泛型的语法规范类型占位符常用大写字母表示比如TType、EElement、KKey、VValue传参时必须传入类类型不可以传入简单类型。eg可以传Integer不可以传int泛型上界用extends限制类型比如MyArrayE extends Number表示E只能是Number的子类Integer、Double等泛型方法静态方法需要单独声明泛型因为静态方法不依赖类实例比如// 静态泛型方法E声明泛型swap支持任意类型数组publicstaticEvoidswap(E[]array,inti,intj){Etemparray[i];array[i]array[j];array[j]temp;}3泛型的底层擦除机制泛型是编译的的机制运行时没有泛型的概念。编译时会把所有T替换成Object这就是“擦除机制”。比如MyArrayInteger编译后底层还是Object[]但编译器会在编译时做类型检查避免错误类型的传入。四、通配符泛型通配符?是Java解决泛型类型兼容问题的关键用于兼容多种泛型类型并保障类型安全主要分为三类用法1无界通配符?接收任意泛型类型的实例解决单一类型泛型方法兼容性差的问题。publicstaticvoidfun(Message?temp){System.out.println(temp.getMessage());// 可读取// temp.setMessage(100); // 不可修改类型不确定}特点只能读取数据无法写入适用于单纯读取、不关心具体类型的场景。2上界通配符? extends 类型仅接收“上界类型”及其子类的泛型实例如? extends Fruit仅接收Fruit、Apple等子类泛型。publicstaticvoidfun(Message?extendsFruittemp){Fruitfruittemp.getMessage();// 可读取转为上界类型// temp.setMessage(new Apple()); // 不可写入子类类型不确定}特点支持读取结果转为上界类型禁止写入适合限制输入类型范围的读取场景。3下界通配符? super 类型仅接收“下界类型”及其父类的泛型实例如? super Fruit仅接收Fruit、Food等父类泛型。publicstaticvoidfun(Plate?superFruittemp){temp.setPlate(newApple());// 可写入支持下界及子类// Fruit fruit temp.getPlate(); // 不可读取父类类型不确定}特点支持写入下界及其子类均合法禁止读取无法确定具体父类类型适合限制输入类型范围的写入场景。结论无界通配符任意类型、只读不写上界通配符子类范围、只读不写下界通配符父类范围、只写不读五、JDK17新增语法1.yield 关键字从Java13开始引入的yield关键字为switch语句返回值提供了更简洁的方式。在传统switch语句中我们需要先定义一个变量接收结果再在每个case分支中为变量赋值并使用break跳出代码较为繁琐。publicstaticvoidmain(String[]args){Stringdataone;intresult0;// 接收数据的返回值switch(data){caseone:result1;// 为result重新赋值break;casetwo:result2;// 为result重新赋值break;default:result-1;// 为result重新赋值break;}System.out.println(result);}而使用yield关键字后无需额外定义变量和break语句直接在case分支中通过yield返回对应值代码更加简洁直观。publicstaticvoidmain(String[]args){Stringdataone;intresultswitch(data){caseone:yield1;casetwo:yield2;default:yield-1;};System.out.println(result);}当然也可以使用指向符-简化写法不过在某些复杂业务场景下yield关键字的可读性更有优势。2.var关键字Java10引入的var关键字能让编译器自动推断变量类型有效减少代码中的冗余类型声明。尤其是在处理复杂泛型类型时效果更为明显。比如定义一个复杂的Map类型变量传统写法需要重复书写冗长的类型声明publicstaticvoidmain(String[]args){MapString,ListMapInteger,StringcomplexMapnewHashMapString,ListMapInteger,String();}使用var关键字后代码瞬间简洁不少且不影响代码的可读性publicstaticvoidmain(String[]args){varcomplexMap2newHashMapString,ListMapInteger,String();}var关键字也有一些注意事项避免误用不能用于声明类的字段不能作为方法参数类型不能作为方法返回类型声明变量时必须初始化且初始化值不能为null。以下是错误用法示例classOrgCat{publicvarname;// error不能用于字段声明publicvareat(varstr){// error不能用于方法参数}}publicstaticvoidmain(String[]args){varstrnull;// error不能初始化为null}3.空指针异常优化在开发过程中空指针异常是常见的错误类型但在JDK8中当一行代码包含多个方法或变量时异常信息往往无法精准定位问题所在。例如publicclassArticleController{publicstaticvoidmain(String[]args){Stringsnull;Strings1s.toLowerCase();}}运行后JDK8的异常信息仅提示在某行发生空指针异常无法直接得知是哪个变量导致的Exception in thread main java.lang.NullPointerException at com.example.blogserver.controller.ArticleController.main(ArticleController.java:29)而JDK17对空指针异常信息进行了优化能明确指出是哪个变量为null导致异常publicclassTest{publicstaticvoidmain(String[]args){Stringsnull;Strings1s.toLowerCase();}}异常信息清晰明了Exception in thread main java.lang.NullPointerException: Cannot invoke String.toLowerCase() because s is null at demo.Test.main(Test.java:38)这一优化大大减少了开发者排查空指针问题的时间提高了调试效率。4.密封类密封类通过关键字sealed和permits对类的继承和接口的实现进行严格约束避免类被随意继承增强代码的安全性和可维护性。基本使用定义一个密封类Animal通过permits指定只能被Dog类继承sealedclassAnimalpermitsDog{publicStringname;publicintage;publicvoideat(){System.out.println(eat()....);}}// 继承密封类的子类需使用non-sealed表示不限制后续继承non-sealedclassDogextendsAnimal{Overridepublicvoideat(){System.out.println(this.name正在吃狗粮....);}}关键规则被sealed修饰的类必须有子类若子类也被sealed修饰则该子类也必须有自己的子类。例如sealedclassAnimal{publicStringname;publicintage;publicvoideat(){System.out.println(eat()....);}}sealedclassDogextendsAnimal{Overridepublicvoideat(){System.out.println(this.name正在吃狗粮....);}}// 此时Dog是密封类必须有子类否则会报错使用non-sealed修饰子类表示该子类不限制后续类的继承任何类都可以继承它。父类没有写permits则允许所有类继承如果写了permits那么只有被permits允许的类才能继承未被permits允许的类无法继承密封类。例如若Animal仅permits Dog则PetDog无法直接继承AnimalsealedclassAnimalpermitsDog{publicStringname;publicintage;publicvoideat(){System.out.println(eat()....);}}// 错误PetDog未在Animal的permits列表中non-sealedclassPetDogextendsAnimal{}正确的做法是在permits中添加PetDogsealedclassAnimalpermitsDog,PetDog{publicStringname;publicintage;publicvoideat(){System.out.println(eat()....);}}non-sealedclassDogextendsAnimal{Overridepublicvoideat(){System.out.println(this.name正在吃狗粮....);}}non-sealedclassPetDogextendsAnimal{}5.接口中的私有方法Java 8允许接口拥有默认方法而从Java 9开始接口支持定义私有方法进一步增强了接口的功能封装性。私有方法可以在接口内部被默认方法调用避免代码重复同时不对外暴露实现细节。interfaceHelloService{publicvoidsayHello();// 默认方法defaultvoidsaySomething(){sayEngHello();sayHello();}// 私有方法仅接口内部可调用privatevoidsayEngHello(){System.out.println(Hello!);}}这样一来接口的默认方法可以通过调用私有方法实现复杂逻辑同时保证私有方法的实现细节不被外部访问和修改。6.instanceof模式匹配在传统的instanceof用法中判断对象类型后还需要手动进行类型转换代码略显繁琐if(objinstanceofString){Stringstr(String)obj;// 后续使用str处理逻辑}JDK16JDK17兼容对instanceof进行了模式匹配优化判断类型的同时完成类型转换并创建临时变量代码更加简洁if(objinstanceofStringstr){// 直接使用str处理逻辑无需手动转换}这一优化减少了代码行数同时避免了因手动转换可能出现的错误。7. 其他实用特性拓展开发能力除了上述语法特性外JDK17还有许多提升开发体验和系统性能的特性新一代垃圾回收器ZGC和G1垃圾回收器的优化大幅降低垃圾回收停顿时间提高系统吞吐量尤其适合对响应时间要求高的大型应用。record类简化不可变数据类的定义自动生成构造方法、equals()、hashCode()和toString()方法减少重复代码。Stream API改进新增更多便捷的方法增强流处理能力让数据处理代码更简洁高效。HttpClient升级重写的HttpClient支持HTTP/2.0协议性能更优同时提供更简洁的API方便开发者进行网络请求操作。集合工厂方法支持List.of()、Set.of()、Map.of()和Map.ofEntries()等工厂方法快速实例化集合对象简化集合创建代码且创建的集合为不可变集合提高线程安全性。