秋招面试我去了拼多多,直接被问JVMGC底层原理和算法,我吊打面试官

JVM 常用参数设置积累

# 堆的初始值,默认物理内存的1/64
-Xms:
# 堆的最大值,默认物理内存的1/4
-Xmx:
# 年轻代大小「在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8-Xmn:
# 设置年轻代初始值为 1024 M
-XX:NewSize=1024
# 设置年轻代最大值为 1024 M
-XX:MaxNewSize=1024m
# 设置线程栈大小,设置越小,说明一个线程栈里面能分配的栈帧数就越少,但对于 JVM 来讲,能开启的线程数就越多;
-Xss128k
# 方法区大小设置「jdk1.8 之后使用元空间替换了方法区,也使用了其他命令」
-XX:MaxPermSize
# 元空间大小设置
-XX:MetaspaceSize
-XX:MaxMetaspaceSize
# 设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代「只在 Serial 和ParNew两个收集 器下有效」
-XX:PretenureSizeThreshold=1000000
# 设定对象晋升到老年代的年龄阈值「设定能经历 10 次拷贝,对象则晋升至老年代」
-XX:MaxTenuringThreshold=10
# jdk1.8 默认设置了下述参数,设置该参数,就会在每次 minor gc 之前看看老年代的可用内存大小,是否大于之前每一次 minor gc 后进入老年代的对象的平均大小,如果小于则那么就会触发一次 Full gc 
-XX:-HandlePromotionFailure

JVM 排查问题命令积累

# 查询实例个数和占用空间大小
jmap -histo pid
# 导出堆内存信息
jmap -dump:format=b,file=test.hprof pid
# 查找死锁,打印出线程的状态
jstack pid
# 查看当前运行 java 应用的扩展参数
jinfo pid
# 查看内存中各个部分的使用情况「eden、survivor、old」
jstat -gc pid
# 堆内存统计
jstat -gccapacity pid
# 新生代内存统计
jstat -gcnewcapacity pid
# 新生代垃圾回收统计
jstat -gcnew pid
# 老年代内存统计
jstat -gcoldcapacity pid
# 老年代垃圾回收统计
jstat -gcold pid
# 元数据空间统计
//加入Java开发交流君样:593142328一起吹水聊天
jstat -gcmetacapacity pid
# 总结垃圾回收统计
jstat -gcutil pid

JVM 的运行模式有三种:

  1. 解释模式「-Xint」:只使用解释器,执行一行字节码就编译一次机器码「不会去缓存」;
  • 优点:启动块;
  • 缺点:整体执行相比编译模式慢;
  1. 编译模式「-Xcomp」:只使用编译器,现将所有的字节码文件一次性编译为机器码,然后一次性去执行所有的机器码;
  • 优点:好处是执行快;
  • 缺点:启动比解释模式慢;
  1. 混合模式:依旧采用解释模式执行代码,但是对于一些“热点”代码采用编译模式执行,JVM 一般采用混合模式执行代码;
  • 优点:相比解释模式,执行会快,相比编译模式,启动会快;

针对混合模式,JVM 有对应的技术去实现,比如 JIT,也就是即时编辑技术。

JVM 内存分配与回收

  • jvm 内存区域图

avatar

  1. 堆内内存:堆内内存 = 年轻代 + 老年代 + 持久代『jdk1.8 之后没有持久代』
  • 优点:
  • 缺点:
  1. 堆外内存:把内存对象分配在 Java 虚拟机的堆以外的内存「比如:java.nio.DirectByteBuffer」
  • 优点:
  1. 减少了垃圾回收机制(GC 会暂停其他的工作);
  2. 加快了复制的速度「堆内在flush到远程时, 会先复制到直接内存(非堆内存), 然后再发送,而堆外内存(本身就是物理机内存)几乎省略了该步骤」。
  • 缺点:
  1. 内存难以控制「使用了堆外内存就间接失去了JVM管理内存的可行性,改由自己来管理,当发生内存溢出时排查起来非常困难」。

年轻代

  • 伊甸区:survivor from 区:survivor to 区 = 8:1:1
伊甸区
  • 大部分对象都在这里诞生
  • 当Eden区满时, 依然存活的对象将被复制到Survivor区, 当一个Survivor 区满时, 此区的存活对象将被复制到另外一个Survivor
survivor 区
  • survivor from
  • survivor to

老年代

方法区/元空间

  • 在 jdk1.8 之后取消了方法区,命名为元空间

线程栈

什么场景下对象会进入老年代

  1. 即将存储的大对象在eden 区域是发现存储不下「就算 Minor gc后还是存储不下」;
  2. 长期存活下来的对象;
  3. Minor gc 后存活的对象Survivor区放不下;

什么是老年代空间分配担保机制

​ 年轻代每次 minor gc 之前 JVM 都会计算下老年代剩余可用空间如果这个可用空间小于年轻代里现有的所有对象大小之和(包括垃圾对象)就会看一个 “-XX:-HandlePromotionFailure”(jdk1.8 默认就设置了)的参数是否设置了,如果有这个参数,就会看看老年代的可用内存大小,是否大于之前每一次 minor gc 后进入老年代的对象的平均大小。

如果上一步结果是小于或者之前说的参数没有设置,那么就会触发一次 Full gc,对老年代和年轻代一起回收一次垃圾,如果回收完还是没有足够空间存放新的对象就会发生 OOM,当然,如果 minor gc 之后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间,那么也会触发 full gc,full gc完之后如果还是没用空间放 minor gc 之后的存活对象,则也会发生 “OOM”。


触发 full gc 的时机

  1. 调用System.gc();
  2. 老生代内存不足的时候;
  3. 即将要放进老年代的对象过大,需要进行老年代回收;「和第二种类似」
  4. 老年代空间分配担保机制中有可能触发;「也就是老年代空间分配担保失败」
  5. 执行 jmap -histo:live 或者 jmap -dump:live 的时候;

如何判断对象可以被回收

1. 引用计数法

给对象添加一个引用计数器,没增加一个地方引用它,计数器就加一,减少一个,计数器就减一,但是解决不了循环引用的问题「会导致内存泄露」,主流的虚拟机都没有使用这个。

2. 可达性分析

通过一系列的称为 GC Roots 的对象作为起点,从这些节点开始向下搜索,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象。

GC Roots 根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等。

具体操作:从gc root根往下搜索,然后三色标记,黑灰白,刚开始是白色,如果搜索到A节点,A节点的子节点还没被搜索,则A节点是灰色,A节点包括子节点全部搜索完毕标记为黑色,到最后白色的就回收了

3. 依据引用类型
java的引用类型一般分为四种:强引用、软引用、弱引用、虚引用。

  • 强引用
    普通的变量引用
public static Person person = new Person();
  • 软引用
    将对象用 SoftReference 软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉。软引用可用来实现内存敏感的高速缓存。
public static SoftReference<Person> person = new SoftReference<Person>(new Person());
  • 弱引用
    将对象用 WeakReference 软引用类型的对象包裹,弱引用跟没引用差不多,GC 会直接回收掉,很少用。
public static WeakReference<Person> person = new WeakReference<Person>(new Person());
  • 虚引用
    虚引用也称为幽灵引用或者幻影引用,是最弱的一种引用关系,几乎不用。
    一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用活着弱引用关联着对象,那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。

jdk中直接内存的回收就用到虚引用,由于jvm自动内存管理的范围是堆内存,而直接内存是在堆内存之外(其实是内存映射文件,自行去理解虚拟内存空间的相关概念),所以直接内存的分配和回收都是有Unsafe类去操作,java在申请一块直接内存之后,会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。

4. 通过 finalize() 方法最终判定对象是否存活

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。标记的前提是对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链。
  • 第一次标记并进行一次筛选。
    筛选的条件是此对象是否有必要执行 finalize() 方法。当对象没有覆盖 finalize 方法,对象将直接被回收。
  • 第二次标记
    如果这个对象覆盖了 finalize 方法,finalize 方法是对象脱逃死亡命运的最后一次机会,如果对象要在 finalize() 中成功拯救自己,只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

垃圾回收算法


1、标记-清除算法

分为两个阶段,即标记和清除,首先会标记所有需要被回收的对象,在标记完成后统一对已经标记的对象进行回收,是最基础的收集算法。

  • 优点
  1. 实现简单
  • 缺点
  1. 内存碎片化
  2. 效率不高
  • 使用场景:主流虚拟机不使用

2、标记-整理算法「也叫标记-压缩算法」

针对老年代进行回收的一种算法,标记的过程和『标记-清除算法』一样,只是在清除完成后,会将还存活的对象朝着一个方向移动,然后固定的清理靠近边界的对象。

  • 优点
  1. 解决了碎片化
  • 缺点
  1. 效率不高
  2. 移动了对象地址,需要更新对象的引用
  • 使用场景:用于老年代垃圾回收

3、复制算法「比标记清理和标记整理快 10 倍以上」

能解决「标记-清理算法」带来碎片化问题,复制算法首先将内存分为大小相同的两块,每次只使用其中的一块,但这一块被使用完后「或者是没法提供所需的连续长度的内存」,就会将这一块的内存复制到另一块去,然后再一次性将这块的内存空间全部清理掉。

  • 优点
  1. 解决了碎片化
  2. 效率高
  • 缺点
  1. 内存使用率不高
  • 使用场景:用于年轻代垃圾回收

4、分代回收算法

​ 这种算法不是新鲜的算法,而是针对不同的内存分区,采用不同的回收算法,比如在新生代中,每次收集都会有大量对象(近 99%)死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保「老年代多是大对象,很可能是需要连续内存地址的对象」,所以我们必须选择「标记清除算法」或「标记整理算法」进行垃圾收集。

垃圾收集器「回收算法的具体实现」

image.png

1、Serial 收集器「-XX:+UseSerialGC -XX:+UseSerialOldGC」

新生代采用复制算法,老年代采用标记-整理算法

Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程「也就是应用程序线程」,直到它收集结束。

Serial 收集器执行过程

avatar

  • 优点
  1. 没有多线程交互,单线程实现简单;
  2. 相比其他单线程收集器,效率最高「当然是比不上多线程收集器」;
  • 缺点:
  1. STW 时间长,用户体验不好
  • 使用场景:

​ 一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。

2、ParNew 收集器「-XX:+UseParNewGC」

新生代采用复制算法,老年代采用标记-整理算法

ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。默认的收集线程数跟 CPU 核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。

ParNew 收集器执行过程


avatar

  • 优点:
  1. 相比 Serial 效率高
  • 缺点:
  1. 实现稍复杂
  • 使用场景:

3、Parallel 收集器「-XX:+UseParallelGC(年轻代) -XX:+UseParallelOldGC(老年代)」

新生代采用复制算法,老年代采用标记-整理算法

Parallel Scavenge 收集器关注点是吞吐量(高效率的利用CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。 Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。

avatar

  • 优点:

  • 缺点:

  • 使用场景:

Parallel 收集器执行过程

4、CMS 收集器「-XX:+UseConcMarkSweepGC(old)」

新生代采用复制算法,老年代采用标记-整理算法

CMS(Concurrent Mark Sweep)以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

CMS 收集器执行过程

初始标记-》并发标记-》重新标记-》并发清理-》并发重置

其中只有『初始标记』不能和用户线程并发,其他的四个是可以的。

avatar

  • 优点:
  1. 并发收集、低停顿;
  2. 吞吐量高;
  • 缺点:
  1. 对 CPU 资源敏感(会和服务抢资源);
  2. 无法处理浮动垃圾(即在并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次 GC 再清理了);
  3. 它使用的回收算法“标记-清除”算法会导致收集结束时会有大量空间碎片产生,当然通过参数 -XX:+UseCMSCompactAtFullCollection 可以让 jvm 在执行完标记清除后再做整理;
  4. 执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发 full gc,也就是“concurrent mode failure”,此时会进入 stop the world,使用 serial old 垃圾收集器来回收;
  • 使用场景

注重用户体验的系统,低延时。

何为[ concurrent mode failure 错误

5、G1 收集器「-XX:+UseG1GC」

一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。会预测的停顿的时间,以及一些抉择,比如 200ms 回收 10MB 和 50ms 回收 20MB 两种选择,会选择第二种,用以达到有限时间内最大的回收效率;

  • 优点:

  • 缺点:

  • 使用场景:

逃逸分析

public void test() {Person person = new Person();
}

上面的代码,会经历如下几个步骤:

  1. 加载 Person.class 到内存上;
  2. 在栈中开辟一段空间,用于 test 方法的入栈,然后在 test 方法的栈空间分配一个变量 p;
  3. 在堆内存中创建一块区域空间,分配内存地址「new 关键字的作用」;
  4. 对空间的属性空间分配,默认初始化;
  5. 构造函数初始化;
  6. 将分配的地址赋值给变量 p,即 p 指向了刚刚划分并且初始化好的堆地址;

按照上面的步骤,每个对象的分配,对象会直接分配在堆上,但如果需要分配的对象非常多,并且生命周期都比较短,比如在某个循环中一直 new 某一个类的对象,并且创建的对象不会作为返回值『或者是返回值的一部分 』,返回给调用者,那么这些数量多且生命周期短的对象,将会占用较多的堆空间,这些被占用的会由 GC 定时去清理,但如果有一种手段,尽量的让这些对象都存储在栈里面,也就是方法栈,这些对象的销毁会随着方法的出栈而消亡,就不再需要 GC 去耗费宝贵的时间和资源去回收堆内存了,STW 的时间自然也会短,GC 的次数也会少,这种手段就是逃逸分析,在 JDK8 中逃逸分析是默认开启。

『但一种手段的出现,肯定是有利也有弊,开启逃逸分析也会耗费时间和资源,就需要我们自己去测试分析,手上的项目是否合适,不能保证逃逸分析的性能收益必定高于它的消耗』

逃逸分析的分类

  • 方法逃逸
  • 线程逃逸

方法逃逸

当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递到其它方法中。

线程逃逸

这个对象甚至可能被其它线程访问到,例如赋值给类变量或可以在其它线程中访问的实例变量。

逃逸分析总结

当一个对象,在其生命周期内,被其他对象所持有,那么就会发生逃逸。

最后,祝大家早日学有所成,拿到满意offer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php中使用exec,system等函数调用系统命令

2019独角兽企业重金招聘Python工程师标准>>> 注意:要想使用这二个函数php.ini中的安全模式必须关闭&#xff0c;要不然为了安全起见php是不让调用系统命令的。  先看一下php手册对这二个函数的解释:  exec --- 执行外部程式  语法 : string exec ( string com…

了解jQuery技巧来提高你的代码

jQuery之所以如此流行并被从大公司到个人博客的几乎每个人都广泛使用&#xff0c;是因为它上手和使用相当简单&#xff0c;而且为我们提供了一些人都不知道的相当棒的特性。我认为jQuery的大多数用户更趋向于使用jQuery插件来解决面临的难题&#xff0c;这通常是明智的选择。但…

如何主动清空.NET数据库连接池?

一般我们的项目中会使用1到2个数据库连接配置&#xff0c;同程艺龙的数据库连接配置被收拢到统一的配置中心&#xff0c;由DBA统一维护&#xff0c;业务方通过某个配置字符串拿到的是开箱即用的Connection对象。DBA能在对业务方无侵入的情况下&#xff0c;给业务方切换备份数据…

假如有人在今天炸了支付宝的存储服务器...

全世界只有3.14 % 的人关注了青少年数学之旅今天在知乎看到了一个问题《假如有人把支付宝存储服务器炸了&#xff08;物理炸&#xff09;&#xff0c;大众在支付宝里的钱是不是就都没有了呢&#xff1f;》外行人问题。网站都是有服务器的&#xff0c;服务器都是有实体的。那么支…

Cookie全解

1. Cookie 可以存储哪些值 在 Cookie 中只能存储个人可识别信息. 个人可识别信息是指可以用来识别或联系用户的信息. 例如用户的姓名, 电子邮件, 家庭住址等. 必须强调的是, 这些可识别信息必须是非机密或重要信息. 2. 使用 Cookie 对象保存和读取客户端信息. 要存储一个 Cooki…

代码格式

2019独角兽企业重金招聘Python工程师标准>>> 1.参考&#xff1a;JavaScript程序编码规范 转载于:https://my.oschina.net/u/1791074/blog/283578

94年出生,6篇SCI,一作发Science,你还不放下手上玩的泥巴

全世界只有3.14 % 的人关注了 青少年数学之旅 2019年9月27日&#xff0c;国际顶尖期刊《科学》&#xff08;Science&#xff09;杂志在线以全文Article的形式发表了北京航空航天大学材料科学与工程学院赵立东教授课题组在热电材料研究上取得的新进展&#xff0c;北京航空航天大…

一个问题让我直接闭门思过!!!拼多多面试必问项之List实现类:LinkedList

一、LinkedList概述 1、对于频繁的插入或删除元素的操作&#xff0c;建议使用LinkedList类&#xff0c;效率较高。 2、LinkedList是一个实现了List接口和Deque接口的双端链表。 3、LinkedList底层的链表结构使它支持高效的插入和删除操作&#xff0c;另外它实现了Deque接口&a…

Docker小白到实战之开篇概述

前言“不对啊&#xff0c;在我这运行很正常啊”&#xff0c;这句话小伙伴们在前几年应该听得很多&#xff1b;每次一到安装、部署时总有一堆问题&#xff0c;毕竟操作系统版本、软件环境、硬件资源、网络等因素在作怪&#xff0c;此时难免会导致开发小伙伴和运维哥们互相甩锅&a…

设置su为不需要密码切换为root

设置su为不需要密码 如果需要对某用户su命令也不需要输入密码&#xff0c;则需要修改下列的&#xff1a;1--->如果没有wheel组 则用sudo groupadd wheel创建命令为 sudo groupadd wheel&#xff1b;2---->sudo vim /etc/group将username和root加入到wheel用户组内 如图&a…

被女朋友拉黑后,我写了个“舔狗”必备神器

全世界只有3.14 % 的人关注了 青少年数学之旅 “ 在一个阳光明媚的清晨&#xff0c;我打开窗户呼吸了一口新鲜空气。阳光灿烂&#xff0c;岁月静好&#xff0c;又是一个约女朋友出去爬山吃饭看电影的好日子。 图片来自包图网 想到女朋友的大眼睛&#xff0c;我脸上不禁洋溢起了…

涨薪关键之反射机制,引得项目经理对你的看重,加薪触手可及!!!!

前言 就比如我前几天被面试官问什么是反射&#xff1f;&#xff1f;&#xff1f; 而我的回答是&#xff01;&#xff01;&#xff01; 反射是动态语言的关键&#xff0c;反射允许程序在执行期间借助Reflection API取得任何类的内部信息&#xff0c;并能直接操作任曦对象的内…

如何摆脱「自我否定」状态

大家好&#xff0c;我是Z哥。你最近正处于自我否定的状态吗&#xff1f;如果不是的话&#xff0c;回想一下最近的一次处于这种状态是什么时候&#xff1f;当时的感受如何&#xff1f;以及&#xff0c;最终是如何走出这个状态的&#xff1f;不着急&#xff0c;给你 1 分钟回忆一…

struct and union

[url]http://hi.baidu.com/tweigh/blog/item/5303d2ef6e2720eace1b3e9d.html[/url]1. struct的巨大作用面对一个人的大型C/C程序时&#xff0c;只看其对struct的使用情况我们就可以对其编写者的编程经验进行评估。因为一个大型的C/C程序&#xff0c;势必要 涉及一些(甚至大量)进…

编码GBK的不可映射字符

为什么80%的码农都做不了架构师&#xff1f;>>> 由于JDK是国际版的&#xff0c;在编译的时候&#xff0c;如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式&#xff0c; 则javac.exe首先获得我们操作系统默认采用的编码格式&#xff0c; 也即在编译java…

低调的大神!他改变了半导体产业!史上唯一两次获得诺贝尔物理奖,却几乎被人遗忘...

全世界只有3.14 % 的人关注了青少年数学之旅两次获得诺贝尔奖的科学家&#xff0c;世界上仅有这四个人&#xff01;他们是&#xff1a;1. 居里夫人(Marie Curie,1867~1934),波兰科学家,他的丈夫叫皮埃尔居里,两人合称“居里夫妇”! 1903年,居里夫妇和亨利...2.约翰巴丁 美国物理…

入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???

公司各种各样的祖传代码都是令新人虎躯一震的代码&#xff0c;因为有时候你根本不知道它是干嘛的&#xff0c;甚至觉得它毫无用处&#xff0c;关键是 还绝对不能动&#xff0c;碰一段改半年&#xff0c;别问我怎么知道的。最讽刺的是&#xff0c;你可能为了修改代码&#xff0c…

设计模式之迭代器

迭代器模式介绍集合的结构迭代器模式是一种行为设计模式&#xff0c;让你能在不暴露集合底层表现形式(列表、栈、树等)的情况下遍历集合中所有的元素。迭代器模式满足了单一职责和开闭原则&#xff0c;外界的调用方也不需要知道任何一个不同的数据结构在使用上的遍历差异。迭代…

倒啤酒竟能拿到诺贝尔物理学大奖!明明是普通操作,凭什么这么强?

全世界只有3.14 % 的人关注了青少年数学之旅倒啤酒&#xff0c;是艺术&#xff0c;更是一门学问。每个初来社会报道的男孩&#xff0c;都必须掌握这个酷炫技能。它是从古自今的文化传承&#xff0c;也是一个男人成熟的标志。向快手老铁致敬然而&#xff0c;理想很丰满&#xff…

铁山靠之——HarmonyOS基础 - 1.0

HarmonyOS学习第一章 一、HarmonyOS简介1.1 安装和使用DevEco Studio1.2 环境配置1.3 项目创建1.4 运行程序1.5 基本工程目录1.5.1 工程级目录1.5.2 模块级目录1.5.3 app.json51.5.4 module.json51.5.5 main_pages.json 二、TypeScript快速入门2.1 简介2.2 基础类型2.2.1 布尔值…