为您推荐:
精华内容
最热下载
问答
  • 1.基础入门: 1.常见命令: 查看当前所有... 关系型数据库: MySQL,可以用SQL语句在一个表或者多个表中做关联查询,所支持的可查询内容 较NOSQL 更加丰富。 安全性能高, 容易理解上手,二维化的表格便于人们操作理解。

    1.基础入门:

    1.常见命令:

    1. 查看当前所有的数据库 show databases;
    2. 打开指定的库 use 库名;
    3. 查看当前库的所有表 show tables;
    4. 查看当前其他库的所有表 show tables from 库名;
    5. 创建表 creat table 表名
      ( 列名 列类型,
      列名 列类型,
      。。。 );
    6. 查看表结构 desc 表名;
    7. 查看服务器版本 select version();

    2.MySQL 语法规范:

    1.不区分大小写,单建议关键字大写,表名,列名小写
    2.每条命令最好分号结尾
    3.每条命令根具需要进行缩进或换行。

    1.1 事务四大特性

    以银行A账户向B账户转账为例子。
    在这里插入图片描述

    原子性,转账作为一个完整的事务, 1,A账户减少100元和 2,B账户增加100元不能分隔开。即该事务要么执行,要么不执行。
    隔离性,假如A账户在向B账户转账的过程中同时C账户也在向B账户转账。即并行发生的事务相互隔离,按照串行操作来完成。
    一致性,当转账事务执行完毕后,。A,B账户的总金额保持不变,即 事务前后,数据总额一致
    持久性,一旦A,B转账事务提交,任何事务或者故障都不会导致数据丢失,即对数据的改变是永久性的。

    1.2 事务的隔离级别

    在描述事务隔离级别之前需要掌握几个基本概念:
    1.脏写: 当事务并发运行的时候,事务A在对某一数据进行读写的时候,事务B 先于A对数据进行了重写,导致A回滚操作的时候覆盖了B重写的数据。
    2.脏读: 同上理。事务A重写的数据但是还没等到回滚操作,事务B 就来读取了错误的数据。
    3.不可重复读: 不同的事务A,B对同一数据读取的结果不一样,因为其中一个事务在读取之后还对数据进行了修改更新
    4.幻读: 当事务A读取某个范围的数据时,前后数据因为有其他的事务对数据进行插入和删除而导致数据不一样,产生了幻觉一样。
    所谓的隔离级别就是对以上四种情况的一个允许程度。
    读未提交:即可以读未提交的内容,查询不会加锁,即隔离的一致性是最差的。
    读已提交:读已经提交的内容,能有效避免脏读,不能避免不可重复读,幻读等情况。
    可重复读:专门针对“不可重复读”而制定的隔离级别,是MySQL默认的隔离级别,不允许在事务进行中对数据进行修改,能有效隔离不可重复读,但是不能避免幻读,幻读主要是因为插入和删除引起的。
    串行化:最高的隔离级别,事务 串行化顺序执行,执行效率差。

    事务隔离级别脏读不可重复读幻读
    读未提交(read-uncommitted)
    读已提交(read-committed)
    可重复读(repeatable-read)
    串行化(serializable)

    2.如何提高查询速度?

    在一个千百万级的数据表中,使用查询需要注意的是:首先是服务器硬件配置上的更新和优化。比如增加内存,使用多核处理器,硬盘升级等等措施。
    1.数据库设计方面

    1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
    2. 从系统层面增强mysql的性能:优化数据表结构、字段类型、字段索引、分表,分库、读写分离等等。

    2.SQL语句方面

    1. 优化 SQL 语句,合理使用字段索引。
    2. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null; 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0;
    3. 避免在 where 子句中对字段进行 != 、<、>、 等符号判断,这会引发全盘扫描。

    3.数据库操作上

    1. 从代码层面增强性能:使用缓存和NoSQL数据库方式存储,如MongoDB/Memcached/Redis来缓解高并发下数据库查询的压力。
    2. 减少数据库操作次数,尽量使用数据库访问驱动的批处理方法。
    3. 提升数据库服务器硬件配置,或者搭建数据库集群。

    3.加了索引就快了?

    Mysql索引主要有两种结构:B+Tree索引和Hash索引。我们平常所说的索引,如果没有特别指明,一般都是指B树结构组织的索引(B+Tree索引)。
    在这里插入图片描述

    1. 最外层浅蓝色磁盘块1里有数据17、35(深蓝色)和指针P1、P2、P3(黄色)。P1指针表示小于17的磁盘块,P2是在17-35之间,P3指向大于35的磁盘块。真实数据存在于子叶节点也就是最底下的一层3、5、9、10、13……非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35。
    2. 查找过程:例如搜索28数据项,首先加载磁盘块1到内存中,发生一次I/O,用二分查找确定在P2指针。接着发现28在26和30之间,通过P2指针的地址加载磁盘块3到内存,发生第二次I/O。用同样的方式找到磁盘块8,发生第三次I/O。
    3. 真实的情况是,上面3层的B+Tree可以表示上百万的数据,上百万的数据只发生了三次I/O而不是上百万次I/O,时间提升是巨大的。

    是否需要创建索引?

    索引虽然能非常高效的提高查询速度,同时却会降低更新表的速度。实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。

    建立索引的代价:

    1.索引需要占硬盘空间,这是空间方面的代价。
    2.一旦插入新的数据,就需要重新建索引,这是时间上的代价。所以,索引不适合需要大量增删改得表

    建立索引的策略:

    1. 并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
    2. 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
    3. 应尽可能的避免更新索引数据列,因为索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新索引数据列,那么需要考虑是否应将该索引建为索引。

    4.数据库索引底层结构

    索引本质上是搜索查询的数据结构,常见的查询结构有顺序查找、二分查找、二叉排序树(实则也为二分查找)、平衡多路搜索树(B-Tree),其中B-tree中的变种 B+Tree 是大多树索引的最优查询结构。
    其中-树和B+树最重要的一个区别就是B+树只有叶节点存放数据,其余节点用来索引,而B-树是每个索引节点都会有Data域所以B+Tree 适合用作索引结构,存储外部数据于叶子节点上。
    每次从磁盘中把数据拷贝到内存中 称为 一次 I/O 操作,相比与已经拷贝到内存中去进行搜索等操作,磁盘到内存之间的数据 I/O 所消耗的时间远多得多。所以尽可能减少I/O操作,是优化数据库的最大问题。
    采用B+Tree存储结构,搜索时I/O次数一般不会超过3次,所以用B+Tree作为索引结构效率是非常高的。
    在这里插入图片描述

    5.mySQL与memcache的区别

    6.mysql常见三种存储引擎

    1. InnoDB存储引擎
    InnoDB给MySQL的表提供了事务处理、回滚、崩溃修复能力和多版本并发控制的事务安全。

    2.MyISAM存储引擎
    MyISAM是MySQL中常见的存储引擎,曾经是MySQL的默认存储引擎。MyISAM是基于ISAM引擎发展起来的,增加了许多有用的扩展。

    3.MEMORY存储引擎
    Memory存储引擎是MySQL中的一类特殊的存储引擎。其使用存储在内存中的内容来创建表,而且所有数据也放在内存中。这些特性都与InnoDB,MyISAM存储引擎不同。

    7.MySQL B+Tree索引和Hash索引的区别?

    B+Tree索引,是一种严格平衡的多路搜索二叉树结构,在大范围搜索的情形之下拥有非常强大的搜索能力,一般最多经历3次I/O就可以找到所需要的数据。哈希索引即是利用不同的键值将其通过设定的哈希函数计算得到哈希地址,所以一般通过一次哈希算法即可立即定位到相应的位置,速度非常快。

    但是:哈希索引不能检索范围查询,经过哈希函数计算得出的索引变成不连续了,就没有办法完成范围查询、顺序查询检索等,哈希还需要防止键值重复问题,哈希冲突等问题。但是在等值查询中,哈希的速度占据绝对优势。所以选择的时候还得看具体使用环境。

    8.B+树索引和哈希索引的明显区别是:

    如上:

    9.非关系型数据库和关系型数据库区别,优势比较?

    非关系型数据库:

    1. NOSQL 基于键值对,所以主键 + 值;查询速度极快。
    2. 基于键值对,数据之间没有耦合性,易于水平扩展,课扩展性强。
    3. 分布式,严格意义上不是数据库,而是一种结构化存储数据的一种集合。

    关系型数据库:

    1. MySQL,可以用SQL语句在一个表或者多个表中做关联查询,所支持的可查询内容 较NOSQL 更加丰富。
    2. 安全性能高, 容易理解上手,二维化的表格便于人们操作理解。
    展开全文
    GJQJFJ 2020-07-14 10:25:46
  • 在本科阶段的空闲时间,室友一般会选择去实习,而自己则喜欢出去旅行。 因为大部分的时间都花在去游山玩水上了,所以有很长一段时间,对比室友,我总觉得自己成长得很慢。 但是,在某节课上,当遇到需要拿出一套...

    01 旅行的意义

    在本科阶段的空闲时间,室友一般会选择去实习,而自己则喜欢出去旅行。

    因为大部分的时间都花在去游山玩水上了,所以有很长一段时间,对比室友,我总觉得自己成长得很慢。

    但是,在某节课上,当遇到需要拿出一套小组成果整体方案的问题时,我却比室友更快的想到了完整的解决方案。

    在回顾这件事时,我去追溯原因,唯一的理由来自于这些旅行。

    在旅行前,我必须根据预算来制定方案;

    在旅行中,我必须处理各种突发问题,比如火车没赶上,是继续滞留一天,还是赶紧买另外的车票,这就要求我必须有 Plan B;

    在旅行结束后,我也会把心得写成游记,记录的同时也方便之后的旅行。

    再后来,这件事让我会发现,很多东西的本质上的底层逻辑都一样,只不过是表现形式不同罢了。

    所以,当我看到这个题目「大学生如何增强自己的知识储备」时,我的大脑中第一浮现的,是第一个建议:重要的不是做什么,而是沉淀什么。

    沉淀来自于对经历的不抛弃,并且从中提炼出通用的内容,套用在其他相关的事件和任务上,实现经验的转移。

    后来我也去实习,每天写总结,总结的内容又为后续的工作提供支撑。

    我想,这就是所谓的螺旋式上升吧。

    好了,道理讲了这么多,那么到底应该储备什么呢?

    02 知识与技能孰轻孰重?

    在回答这个问题前,不好意思,还有点道理要说:到底什么是「知识储备」?

    通常来看,它包括两个层次的内容:一是知识,二是技能。

    知识是一系列概念形成的体系。

    比如,要研究一家企业,知识就包括:企业的组织架构有哪些类型、企业的三个报表对应哪些内容、企业的生命周期理论等。

    而技能则是解决问题的具体操作方法。

    还是研究企业,那么技能就是:如何找到企业的报表,如果用Excel来计算一系列财务指标(如总资产周转率、速动比率),如何做访谈等。

    一般来说,积累知识更难看到效果,而技能因为学来就能用,大家一般更偏爱。比如《我用这十个神器提升办公效率》肯定就比《设计的十个原则》阅读量更高。

    但是,这是个可怕的陷阱。

    记得在读书阶段,我有选修一门平面设计课。老师花了很多时间来讲解设计的基本理论、分析一系列案例,到中后段时才切入到工具的教学。

    我们就很疑惑:老师,我们都学了半学期了,为什么还不教 PS 啊!

    老师的答案是:将来你会发现,要实现创意,哪个工具都可以。可以是手绘,可以是电脑设计,甚至是一些拼贴。但是,你跟别人的差异,来自于底层的积累,而非表面的技巧。

    我当时不理解,直到之后看到 iPhone 的系列广告:shot on iphone 。这些摄影师,他们能拍出这些作品,并不是来自设备的差异,而是他们本身与我们的差异。

    这是发现美的眼睛,是捕捉美的意识,是重塑美的角度。

    所以,在这里提出第二个建议:在做知识储备时,不要仅沉浸在技能输入上的愉悦上,也要保持与技能相对应的知识输入。

    03 哪些内容需要去储备?

    要做任何事之前,我们需要明确目的。在大学阶段进行知识储备,目的无外乎三个:未来求职、培养兴趣和继续深造。

    下面的内容主要针对「未来求职」这一块。结合这些年的工作经历,重点强调三类:职场习惯、办公技能和科学的思考逻辑。

    第一,职场习惯。

    这句话看起来很玄乎,但是只要是实习过,大家应该都会理解:它指代的最重要一点就是「使命必达」。

    比如,他人给你一份任务,你要做的不是多么辛苦地操作,而是无论如何把它完成。

    如果理解了这层,那很多的工作方法就比较清晰了。

    比如领导让写一份策划,你可以是自己去研究案例,然后依葫芦画瓢完成;也可以是请教前辈,在指导下形成提纲,再不断完善。

    在这个过程中,你发现自己需要:跟一系列利益相关者保持良性互动,要保持对这个行业和领域的基本信息获取。

    于是,在沟通技巧上,不能只是索取,要形成互动;在解决问题时,一条路不行时,要及时转向;在进行汇报时,要有 Plan A 和 Plan B……

    所有这些,在大学阶段有了涉猎,能极大方便后续融入到职场生活,减少碰壁的可能。

    要养成职场习惯,这里提出第三个建议:尽量去一家大企业实习,学习一套相对正规的职场礼仪和做事逻辑。

    第二,基础的办公能力。

    这个能力可以很窄,也可以很宽。

    窄的层面,就是办公技能,典型的就是 Office 三件套:Word、Excel、PPT。

    但其实,了解了这些其实还不够。大家可以看看下面这个清单,有多少问题可以回答出来:

    Q1.拿到一份 PDF ,知道如何转换成 Word 进行编辑。

    Q2.知道去哪里下载上市公司的年报。

    Q3.知道去哪里下载免费的高质量 PPT 模版。

    Q4.知道如何快速将录音转为文字。

    Q5.知道如何制作一张二维码名片。

    Q6.知道如何快速找到 Windows 电脑里的某个文件。

    Q7.知道如何快速将英文网站翻译成中文。

    Q8.知道如何找到免版税的图片。

    测试过一轮,大多数朋友普遍能回答 4 个左右。

    好了,参考答案如下:

    办公技能宽的层面,就是如何搜集、整理和呈现信息。

    比如利用印象笔记的「剪藏」功能,把网页信息整理到印象笔记;然后利用思维导图工具 xmind ,把信息进行归总;最后,提炼出最重要的信息,再用 word 或 PPT 呈现给他人。

    通过把工具进行串联,形成工作流,于是工作的效率便自然而然地实现提升。

    第三,科学的思考逻辑。

    这个就比较考验功夫了。很多人会给出各种玄妙的答案,但其实如果我们有心,读书阶段的论文写作,就一直在锤炼我们的这项技能。

    我们来看一个论文的行文逻辑:

    · 先有摘要,告诉他人分析思路与观点。

    · 然后交待背景,为什么这个问题重要要解决。

    · 接下来进行文献综述,了解其他人做了哪些。说明 gap,也就是目前自己研究的这块还没有解决。

    · 然后给出自己的分析路径。可能是数据分析,可能是文献研究,可能是实地调研。

    · 最后,给出结论,并说明运用前景和不足之处,后续应该如何改进。

    这个逻辑,基本也适合大多数分析问题的场景。

    比如要做任何事情,写任何汇报,逻辑也是类似:

    · 先有个简要的说明。

    · 接下来说明为什么做这件事,做了有什么好处,不做有什么坏处。

    · 接下来从各种渠道了解其他人是怎么做的,取长补短。

    · 然后给出具体的实施方案。

    · 最后说明需要什么保障措施等。

    你看,这些逻辑训练,并不用看太多其他的东西。这里提出第四个建议:好好学习,认真写好论文,这是锻炼思考逻辑的好方法。

    第四,一些小 tips 。

    最后,再给出一些有帮助的小建议。

    1. 提前了解目标岗位需要的能力。

    在我们不知道提升什么能力时,可以去看看企业招聘通知里的岗位要求。具体来说:

    第一步:通过各大求职网站,找到心仪目标公司目标岗位的JD;

    第二步:将岗位描述中一些对具体能力和技能的要求提取出来;

    第三步:列出自己当前的能力清单,然后和第二步提取出的公司能力要求清单做对照,找出自己的差距;接着就是制定时间计划,有节奏的进行知识储备。

    以腾讯校招官网上的「产品策划/运营」为例:

    当我们提取出了「突出的文字能力和沟通能力」这一要求后,发现自己在这方面还比较空白时,可以将它做重点标记,然后把自己未来知识储备的重心,放在写作和一些沟通练习上。比如说读书完一本书进行读书笔记写作,又或是刻意练习演讲等。

    然后在每个时间节点上标记,自己应该达到什么目标,什么样的效果,以及脑海中会先有一个将来知识储备完成时的样子。

    2. 多渠道学习。

    了解清楚需要补充什么内容后,接下来就要找到学习的路径。常用的包括:

    (1)书本学习

    这是最重要的一个渠道,没有之一。它的系统程度、含金量,是其他都难以比拟的。

    (2)互联网账号资讯

    如公众号、知乎、微博等互联网内容资讯平台。这里面有很多具体的案例,可以丰富书本里的体系,相对比较好理解。

    (3)网络课程和微课。

    如得到、中国大学MOOC、网易云课堂等。这也只能作为补充,不能作为主要信息获取渠道。

    (4)向他人学习

    可以是和擅长这方面的朋友吃饭聊天,听听”过来人“的建议;也可以是参加专业性的培训或讲座,如参加 PPT 沙龙、摄影沙龙等,可以获取到在这个知识领域专家的一些”前沿“资讯;

    还可以是加入学习小组或者社群,如作为从事新媒体行业的小编,会积极活跃在各个新媒体社群中。

    3. 做好记录和复盘

    最后,顺便推荐三个常用的知识储备管理工具:

    印象笔记:信息储藏室;

    幕布:进行信息的逻辑化梳理;

    GoodNotes:iPad 上的手绘笔记工具。

    ↑ GoodNotes

    我是小结

    又不小心说了这么多,照例该来个小结了:

    本文授权转载自公众号「曹将」,未明学院诚意推荐,文章略有删改。

    展开全文
    EnlightenAcademy 2019-06-11 16:33:22
  • java后台面试知识储备(整理中)

    Java后台面试知识储备(整理中)

    知识储备图

    1、java基础

    1.1、 封装、继承、多态

    1、封装

    把一个个的事物变成一个个的类:类中包含成员对象和成员方法。
    权限大小顺序;
    public(所有类都可以访问)>protected(子类访问权限)>default(包访问权限)>private(类内部访问权限)

    2、继承

    让子类可以获得父类的属性和方法。

    3、多态

    一句话描述多态就是:多态可以通过一个指向父类的指针使用子类的方法;
    案例
    定义一个动物类,一个狗类,一个猫类,狗和猫都去继承动物类,三个类都写了自己的eat()方法。

    Animal dog=new Dog();
    Animal cat=new Cat();
    dog.eat();
    cat.eat();
    
    狗吃肉
    猫吃鱼
    

    1.2、 核心类源码

    Object源码

    java体系中每个类默认有一个超类就是这个Object,每个对象都默认实现了Object类中的方法。
    Object类中主要的方法有getClass()、equals()、hashcode()、clone()、notify()、notifyAll()、wait()

    getClass方法
    public final native Class<?> getClass();

    1、该方法使用native去修饰,表示该方法需要jvm自己去调用本地c或者c++已经实现的方法;
    2、该方法使用final修饰,表示该方法不可被重写;
    3、该方法的主要作用获取当前正在运行的类。

    hashCode()方法
    public native int hashCode();

    1、该方法的作用是返回对象的hash值,主要服务于HashMap等集合;
    2、如果两个对象相等,则hashCode的值相等;但如果两个对象的hashCode的值相等,但两个对象不一定相等;

    equals()方法
    public boolean equals(Object obj) {
    return (this == obj);
    }

    1、该方法可以被重写;
    2、该方法用于比较两个独享是否相等;
    3、如果两个对象相等,则hashCode的值相等;但如果两个对象的hashCode的值相等,但两个对象不一定相等;

    clone()方法
    protected native Object clone() throws CloneNotSupportedException;

    1、该方法用于对象的复制,当我们使用该方法时,需要实现一个Cloneable()接口;
    2、(1)浅拷贝:创建一个对象指针,让它指向原有的对象所在的内存;(2)深拷贝:创建一个新的对象,复制原有对象的内容。Object中clone方法采用的时浅拷贝。

    nofity方法、notifyAll()方法、wait()方法

    1、wait()方法是让当前线程进入等待状态,释放锁资源;
    2、notify()方法的作用是唤醒指定的处于等待状态的线程,notifyAll()是唤醒所有处于等待状态的线程;
    3、wait()方法和sleep()方法的区别:wait方法会释放锁资源,sleep方法不会释放锁资源。

    1.2、java集合容器

    HashMap集合

    Hashmap集合类可以把任意长度的输入转化为有限长度的输出。

    Hashmap的结构是什么样的

    数组+链表+红黑树

    为什么需要链表

    hash冲突,当key经过hash后得到的值是相同的,比较它们原来的key,如果key相同,replace;key不相同,在当前桶上添加链表。

    Hashmap的容量是怎么样的

    Hashmap默认是一个散列表,它是懒加载的,只有在第一次put数据的时候,才会创建出这张散列表。散列表的默认大小为2^4=16,默认扩容因子为0.75,也就是当散列表的使用空间达到16乘以0.75=12,散列表扩容到32。

    扰动函数

    扰动函数的作用是为了优化hash函数,它采用的方法是高8位异或低8位,因为高8位一般是用不到的,通过这种方式可以减少hash冲突。

    Hashmap的put流程

    主要分为4中情况:

    1. 当桶为空,无链表时,直接放入key与value;
    2. 当桶不为空,无链表时,比较原来的key值,当key值相等,替换,key不相等,在桶后插入链表;
    3. 当桶不为空,有链表时,依次判断每个节点,key值相等,替换。key值不相等,插入节点并判断长度是否大于8,如果大于8,转换为红黑树;
    4. 当桶不为空,有红黑树,插入节点,根据红黑树的算法左旋右旋。

    1.3、java并发编程

    在这里插入图片描述

    1.3.1、并发包常用类实现原理(lock、atomic)

    什么是Lock?

    Lock是concurent下的类,通过Lock类加锁,可以防止多个线程同时访问到资源。在Lock包出来之前,一直使用的是synchronized关键字。

    lock和synchronized的区别是什么?

    locksynchronized
    lock需要手动上锁和解锁synchronized不需要手动上锁和解锁
    lock可以判断锁的状态synchronized不可以可以判断锁的状态
    lock是一个类synchronized是java的关键字,属于JVM层面的
    lock用于大量资源需要同步synchronized用于少量资源需要同步,因为效率低

    **AQS(抽象队列同步器)**参考博客https://segmentfault.com/a/1190000015739343

    state状态属性

    AQS中有一个state的属性,该属性使用volatile修饰。当state=1时,表示该锁已被占用;当state=0时,表示该锁没有被占用;当state>1时,表示该锁已被重入。

    private volatile int state;
    

    光知道所被占用了还是不够的,还需要知道占用了锁的是哪一个线程。

    exclusiveOwnerThread属性

    private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer
    

    exclusiveOwnerThread属性表示当前持有锁的线程。

    双端队列

    AQS中,保存着一个双向链表,每一个想要获取锁的线程会被封装成一个Node对象放入这个链表中。
    在这里插入图片描述
    上图就是AQS中线程等待队列的模型图,其中头结点为一个哑结点,其中的thread指向的永远为空,因此第一个进入队列的线程会被包装成第二个节点进入等待。

    • thread属性代表当前等待的线程为哪一个;
    • waitStatus代表该线程目前的状态;
    • pre、和next代表前驱和后继指针;
    • nextWaiter:永远指向为空,表示处于独占模式。
      CAS(比较并交换)
      AQS中有大量该方法的使用,CAS(V,E,N)中V代表要修改的变量,E代表期望的值,N代表新的值。如果E和N不相等,会开始自旋。

    可重入锁

    可重入锁和不可重入锁的区别:
    可重入锁当一个线程获得锁后,状态加一,这个线程可以再次进入这个锁,状态再加一。当这个线程释放一次锁,状态减一,只有当这个线程状态为0的时候,别的线程才能够获取这个锁。
    不可重入锁当一个线程获取锁资源后,不可再次进入,其他线程等待该线程执行完毕释放锁资源。

    1.3.2、常用关键字(synchronized、volatile)

    synchronized

    synchronized的作用是使多个线程可以共享同一个变量,但同一个时间只能有一个线程调用。synchronized是重量级锁,效率非常的低。

    synchronized效率为什么低

    java中的线程会映射到操作系统中的线程上,synchronized通过操作系统完成互斥锁,需要对线程挂起和唤醒。而线程之间的切换需要在用户态和内核态之间转换,因此性能消耗非常的高。

    java是如何解决synchronized效率低的问题

    java在1.6之后加入了自旋锁、偏向锁、轻量级锁,极大地优化了性能。

    synchronized有哪些应用场景?

    • synchronized void method() { //业务代码 }这种方式就是对当前对象加锁
    • synchronized void staic method() { //业务代码 }这种方式是对当前类加锁,因为静态方法是属于类的,不属于任何一个对象。因此,当线程A调用类的静态方法,线程B调用类的非静态方法,这两者之间是不会互斥的。(也就是说,对象锁和类锁相互不影响)。
    • synchronized(this) { //业务代码 }这样代表锁住了这个对象,但是synchronized(类.class) { //业务代码 }代表锁住了这个类。

    synchronized 的应用场景(双重检测锁可以回答面试题:写一个你认为最好的单例模式

    //双重检测锁
    public class Singleton {
        private volatile static Singleton singleton;
        private Singleton(){};
        public Singleton getSingleton(){
            if (singleton==null){
                synchronized (Singleton.class){
                    if (singleton==null){
                        singleton=new Singleton();
                    }
                }
            }
            return singleton;
        }
    }
    
    • 第一次检测是为了避免没有必要的锁资源争夺
    • 第二次检测是为了防止多个进程抢到锁资源二导致的对象重复创建
    • singleton对象设置为volatile主要是为了防止指令重排,因为在new Singleton()的时候其实是分三步执行的(1)在内存中为该对象开辟一块内存;(2)创建singleton的对象指针;(3)将该指针指向新开辟的内存地址。

    如果不用volatie会出现什么问题?

    如果上面三步的执行顺序变为(1)(3)(2),可能会出现一个线程获得了一个还没有初始化的实例。

    sychronized底层原理是什么样的?

    主要分为两种情况:
    1、 修饰同步代码块,用到了c++底层的两个变量monitorenter和mnitorexit,可以获取monitor对象。wait方法和notify方法也是通过这个对象,这个对象可以控制一个计数器。
    2、 修饰成员方法,监控一个标识ACC_SYNCHRONIZED,该标识表示该方法为一个同步方法。

    volatile

    volatile主要有以下三点特性:

    1. 保证可见性;
    2. 不保证原子性;
    3. 禁止指令重排。

    1.3.3、多线程基本概念、共享内存、消息传递

    多线程其实就是并发,因为一个进程中可以包含多个线程,这多个线程可以在一段时间内一起执行,因为系统会给每个线程分配时间片。

    线程之间是如何通信的?

    1、共享内存
    共享内存就是指多个线程可以读取内存中同一块公共区域来传递信息,这是隐式通信
    2、消息传递
    消息传递就是指多个线程通过发送消息的方式传递信息,这是显式通信

    java中实现多线程的方式有哪些?

    1、继承Thread类

    public class MyThread  extends Thread{
    
        @Override
        public void run() {
            System.out.println("线程开始啦");
        }
    
        public static void main(String[] args) {
    
            for (int i=0;i<4;i++){
                new MyThread().start();
            }
            System.out.println("主线程====");
        }
    }
    

    2、实现Runnallable接口

    class MyRunnable implements Runnable{
    
        @Override
        public void run() {
            System.out.println("MyRunnable方式实现多线程=====");
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 4; i++) {
                new Thread(new MyRunnable()).start();
            }
            System.out.println("主线程====");
        }
    }
    

    3、实现Callable接口

    class MyCallable implements Callable{
        int i=0;
        @Override
        public Object call() throws Exception {
            System.out.println(Thread.currentThread().getName()+"  i的值为"+i);
            return i++;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            MyCallable myCallable=new MyCallable();
            for (int i = 0; i < 10; i++) {
                FutureTask futureTask=new FutureTask(myCallable);
                new Thread(futureTask,"子线程"+i).start();
                System.out.println("子线程的返回值"+futureTask.get()+"\n");
            }
        }
    }
    

    2、线程池
    下面会有详解~

    1.3.4、JMM内存模型(主存和工作内存)、三大特性(原子性、可见性、有序性)

    JMM内存模型:分为主存和工作内存,数据放在主存中,当一个线程需要使用数据时,先将数据从主存中拷贝到本地的工作内存中,在线程使用完后再将数据放回主存。
    1、原子性
    原子性表示是不可分割的,是线程执行的最小单位。

    原子性通俗说明

    i++这条语句就是非原子性,它分为三步,(1)从内存中取出i;(2)i=i+1;(3)将i放回内存中。
    2、可见性
    可见性是为了保证多线程情况下当一个线程对数据进行了修改,其他线程应该立刻知道这个修改。

    可见性通俗说明

    线程A对a++,线程B对a++,如果不保证可见性a=1;
    3、有序性
    JVM会将代码转化为指令供CPU执行,而JVM是允许指令重排的,使用volatile可以禁止指令重排,保证有序性。

    有序性通俗说明

    Singleton singleton=new Singleton();
    

    双重检测锁中定义的对象需要用volatile,就是为了保证他的有序性,如果不使用volatile,可能获取到一个没有初始化的对象。

    1.3.5、线程池的使用、原理(参考博客https://www.jianshu.com/p/7726c70cdc40)

    线程池出现的目的

    线程池是池化技术中的一种,由于每次调用线程都需要创建和销毁线程,资源有所浪费。
    线程池采用的方法是预先创建好一定数量的线程,当需要使用线程时,直接使用线程池中的线程,用完放回线程池即可。

    线程池有什么好处?

    • 增强了线程的管理,如果无限制的创建线程会出现OOM异常;
    • 加快了请求响应的速度,因为省去了创建线程的过程;
    • 减少了资源的损耗,线程使用完会放回到线程池中。

    线程池的主要参数有哪些?

    • 核心线程数
    • 最大线程数
    • 非核心线程空闲存活时间
    • 非核心线程空闲存活时间的单位
    • 任务阻塞队列
    • 拒绝策略

    线程池的主要流程是什么样的?

    如果核心线程池满了,就将任务添加到任务阻塞队列中,当任务队列已满,且任务队列中任务的数量加上核心线程数<最大线程数,就创建一个救急线程处理任务请求。如果已经达到最大线程数,则采用拒绝策略。

    1.3.6、锁优化、锁膨胀

    1.4、java i/o

    在这里插入图片描述

    1.4.1、BIO、NIO、AIO

    1. BIO(同步阻塞)
    客户端向服务端发送请求,在服务端处理完请求前,客户端一直处于阻塞状态,不允许做其他事情。

    这样的IO模型存在什么问题

    很明显,这种客户端来一个请求,服务端创建一个线程去处理,如果请求数量变多了,服务器过载。

    BIO模型的代码是怎样实现的?

    public class SocketBIO {
        public static void main(String[] args) throws IOException {
            //创建服务端,设置端口号
            ServerSocket server = new ServerSocket(9090);
            System.out.println("new ServerSocket(9090)");
            while (true){
                //接受服务端,这个位置阻塞,等待客户端连接
                Socket client = server.accept();
                System.out.println("客户端端口号:"+client.getPort());
                //每次连接给他开辟一个线程,也是BIO最大的弊端
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //创建输入流
                        InputStream in=null;
                        try {
                            in = client.getInputStream();
                            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                            while (true){
                                //阻塞,等待传输数据
                                String dataLine = reader.readLine();
                                if (dataLine!=null){
                                    System.out.println(dataLine);
                                }else {
                                    client.close();
                                    break;
                                }
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }
    
    

    2. NIO(同步非阻塞)
    NIO模型是对BIO模型的一个改进,因为BIO模型每来一个连接就会开启一个线程,也不管这个线程是否需要使用I/O资源。在NIO模式中,所有的请求首先会来到多路复用器上,多路复用器轮询所有的请求,然后为有I/O需求的请求开启一个线程。
    3. AIO(异步非阻塞)
    AIO模型是对NIO模型的一个改进,AIO模型需要操作系统首先为请求准备好所需要的资源,然后再通知服务器开启线程去处理请求。

    1.4.2、阻塞、非阻塞、同步、异步

    1.5、深入理解JVM

    在这里插入图片描述

    1.5.1、垃圾回收机制(可达性分析、垃圾回收算法)

    GC(垃圾回收)是如何分类的?

    垃圾回收主要分为两类:
    1、部分回收:

    • 新生代收集
    • 老年代收集
    • 混合收集
      2、整堆回收
      对整个java堆和方法区回收。

    垃圾回收时如何判断对象是否死亡?

    1. 引用计数法:给每个对象添加一个引用计数器,每当一个对象被引用,
    2. 可达性分析(JVM使用的就是可达性分析):GC Roots向下搜索对象的引用链,对没有搜索到的对象打上一次标记。接着看这个对象是否覆盖了finalize方法,如果在finalize方法中还是没有引用对象,就会打上第二次标记,被二次标记的对象就会被回收。

    垃圾回收的各种算法?

    1. 标记清除法
    对不用回收的对象打上标记,回收没有打上标记的对象。

    这样的垃圾回收算法存在什么样的问题?

    • 效率低下;
    • 会产生很多的不连续碎片。
      2. 复制算法
      内存会被分为大小相等的快,当一半被回收完,剩余的对象复制到另一半。
      在这里插入图片描述
      3. 标记整理法
      前面的步骤和标记清除一样,将存活下来的对象向一端移动。
      4. 分代收集算法
      根据各个代的特点使用最适合的垃圾回收算法。
      新生代垃圾回收会有大量对象死亡,使用复制算法;老年代对象存活几率高,使用标记清除或者标记整理法。

    常见的垃圾收集器有哪些?

    1. Serial收集器
    单线程垃圾收集器,进行垃圾回收的时候暂停其他的工作线程。
    新生代用复制算法,老年代标记-整理算法。
    2. ParNew收集器
    多线程版本的Serial收集器。
    新生代用复制算法,老年代标记-整理算法
    3. Parallel Scavenge收集器
    重点关注:吞吐量
    4. Serial Old收集器
    Serial收集器老年代版本
    5. Parallel Old收集器
    Parallel Scavenge收集器老年代版本。
    6. CMS收集器
    重点关注:线程停顿时间
    7. G1收集器
    以极高的概率满足线程停顿时间的要求,还具备高吞吐量。

    Full GC的触发条件

    1. System.gc建议JVM进行Full GC;
    2. 老年代空间不足,执行Full GC。如果Full GC后空间仍然不足,则会报出内存溢出异常;
    3. 永久代空间不足;
    4. gc担保失败:发生minor gc时,老年代最大连续空间小于平均晋升空间。

    1.5.2、内存分配策略(堆、方法区、虚拟机栈、本地方法栈、程序计数器)

    在这里插入图片描述
    java内存结构主要分为两块:内存共享区(多个线程共享堆、方法区)、内存独占区(每个线程私有虚拟机栈、本地方法栈、程序计数器)。

    java1.8之后内存分配有什么变化?

    1.8之后方法区取消了,变成元空间,放入了直接内存中。

    1、堆

    1. java1.7之前所有对象的内存都是在堆分配内存的,后来开启了逃逸分析(如果一个对象未被返回或者被外面调用),对象可以在栈中分配内存;
    2. java1.7将堆划分为新生代(新生代分为Eden、From Survival、To Survial,它们的比例为8:1:1)、老年代永久代(方法区);
    3. jdk1.8将方法区变为元空间放入了直接内存。
      在这里插入图片描述

    对象在堆中的流程是怎样的?

    一个对象首先会在Eden区中创建, 当进行一次新生代垃圾回收,对象年龄+1,当对象的年龄达到15, 对象被转移到老年代。

    2、方法区

    方法区是线程共享的,它记录着类的信息,常量和静态变量。

    方法区和永久代是什么关系?

    方法区只在Hotspot中才有,方法区是逻辑上的概念,方法区是对永久代的一个理论实现。

    字符串常量池

    运行时常量池逻辑包含字符串常量池位于方法区中。

    字符串常量池在java的各个版本有什么变化?

    1. java1.7以前字符串常量池在方法区中;
    2. java1.7时字符串常量池转移到了堆中,其他的不动;
    3. jdk1.8时,方法区取消,字符串常量池依然待在堆中,其他的变为元空间到了直接内存中

    jdk1.8为什么要将方法区变为元空间,放入直接内存中?

    1. 方法区受到JVM设置的限制,容易发生溢出;
    2. 元空间存放类的元数据,不受参数的影响,可以加载更多的类;
    3. 只有Hotspot有方法区,合并代码的时候,需要单独为它留下一片内存空间。

    3、虚拟机栈

    每执行一个方法会创建一个栈帧,当一个方法执行完毕,栈帧弹出。

    java虚拟机栈会出现什么异常?

    1. 栈溢出异常:当一个线程创建的栈帧超过JVM允许的栈的最大深度,就会出现此异常;
    2. 内存溢出异常:当JVM的堆中没有足够的内存,去垃圾回收无法获得足够的内存,出现此异常。

    4、本地方法栈

    虚拟机栈是执行java方法,本地方法栈执行本地的Native方法,它的结构和虚拟机栈是相同的。

    5、程序计数器

    定义:程序计数器占用一小块的内存,用于记录当前字节码执行到的行号

    程序计数器有什么作用?

    主要有以下两点作用:

    1. 控制代码的执行顺序;
    2. 多线程线程切换,记录下切换前线程执行到的位置。

    程序计数器是否会内存溢出?

    不会,内存中唯一不会溢出的区域。

    程序计数器的生命周期?

    程序计数器的生命周期从线程创建直到线程。

    1.5.3、类加载机制(加载、验证、准备、解析、初始化)

    1、加载
    将字节码文件转变为二进制流,加载到内存中去。
    2、验证
    验证二进制流是否符合JVM的规范,用于安全验证。
    3、准备
    为静态变量赋上默认的值,如0,null。

    public String a=“a”;(1)
    public static b=“b”;(2)
    public static final c=“c”;(3)

    上面这个案例中(1)在准备阶段不会赋值,(2)在准备阶段会赋值为null,(3)为字符串常量,与类变量不同,因此会被赋值为“c”;
    4、解析
    将常量池中的符号引用(在编译期间,不知道类的具体位置,用符号代替)转化为直接引用(指向实际的内存地址)。
    5、初始化
    准备阶段是赋上默认的值,初始化阶段是赋上期望的值。

    类加载器有哪些种类?

    • 启动类加载器
    • 拓展类加载器
    • 应用程序类加载器
    • 自定义类加载器

    双亲委派模式是什么?

    双亲委派模式简单来讲,就是一个类的在加载的时候会委托给上层的类加载器。

    不能理解,通过一个案例来讲解

    如果我们自定义一个Object类,那如果我们创建一个Object对象时类是如何加载的。实际上会一直向上委托,一直到启动类加载器,发现已经存在Object类,然后就不会加载自定义的Object类。

    1.5.4、常用的调优方法(堆内存使用、GC情况、线程堆栈)

    1.6、java框架

    1.6.1、Spring框架

    spring的核心:IOC和AOP

    什么是IOC?

    IOC就是控制反转,顾名思义就是把对象的创建和调用交给spring容器。

    为什么要使用IOC?

    减少代码之间的耦合,每当你的需求变更的时候,你就需要对你的代码进行修改,但是使用IOC只需要修改XML文件即可。

    IOC的底层原理使用了哪些技术?

    工厂模式+XML解析+反射

    原始调用对象的方式?

    //原始方式
    class UserService{
        public void execute(){
            UserDao userDao=new UserDao();
            userDao.add();
        }
    }
    class UserDao{
        public void add(){
        }
    }
    

    使用工厂的方式创建对象?

    //工厂模式
    class UserFactory{
        public static UserDao getDao(){
            return new UserDao();
        }
    }
    class UserService1{
        public void execute(){
            UserDao userDao = UserFactory.getDao();
            userDao.add();
        }
    }
    
    

    IOC底层的执行流程

    在这里插入图片描述

    1. 第一步,在XML文件中配置我需要创建的对象,设置好对象的属性;
    2. 第二步,通过XML文件解析获取到配置对象的class值;
    3. 第三步,通过forClass方法加载类,在通过newInstance方法创建对象。

    Spring提供了两种IOC容器实现的方式BeanFactoryApplicationContext

    BeanFactoryApplicationContext有什么区别?

    1. ApplicationContext是BeanFactory的子接口,因此它具有更多的功能。
    2. BeanFactory只有在获取对象的时候才会创建对象,ApplicationContext在加载配置文件的时候就创建好了对象。

    AOP原理

    手写动态代理

    public interface UserService01 {
        public void say();
    
        public static void main(String[] args) {
            JdkProxy jdkProxy=new JdkProxy();
            UserService01 userService= (UserService01) jdkProxy.getProxy(new UserService01Impl());
            userService.say();
        }
    }
    class UserService01Impl implements UserService01{
    
        @Override
        public void say() {
            System.out.println("Hello World");
        }
    }
    class JdkProxy implements InvocationHandler {
    
        private Object object;
    
        public JdkProxy(){}
    
        public Object getProxy(Object object){
            this.object=object;
            return Proxy.newProxyInstance(getClass().getClassLoader(),object.getClass().getInterfaces(),this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理执行前");
            Object result = method.invoke(object, args);
            System.out.println("代理执行后");
            return result;
        }
    }
    

    spring容器初始化bean的过程

    在这里插入图片描述
    spring事务

    事务的传播性(参考博客https://blog.csdn.net/gao2175/article/details/107914689)

    事务传播的7大特性
    1、无事务
    2、@Transactional(propagation=PROPAGATION_REQUIRED)
    存在事务,加入事务,不存在,创建事务
    3、@Transactional(propagation=Propagation.SUPPORTS)
    调用方有事务,事务运行,没有就非事务
    4、mandatory(强制性的)
    没有事务就报错,不能创建事务,所以用required
    5、propagation_ required _new
    不管有没有事务,创建新事务执行
    6、propagation_ not _support
    有事务就挂起,以非实物执行
    7、propagation_never
    有事务,就出异常

    事务的隔离性

    1.6.1、MyBatis框架

    MyBatis是一款优秀的持久层框架,他可以将java对象映射成MySql中的数据。
    MyBatis常问的面试题

    #{}和${}的区别

    这两者的区别主要分为以下三点:

    • #{}是预编译处理,${}是简单的字符串替换;
    • #Mybatis将#{}中的参数替换成?,然后使用PreparedStatement的参数设置;
    • #{}可以防止sql注入,更加的安全。

    Mybatis中Dao的底层原理,Dao中的方法是否可以重载?

    每个Dao会对应一个xml文件,xml文件中会有namespace(对应dao的全类名)、id(对应Dao类中的方法名),将两者结合可以得到key,根据这个key可以找MappedStatement(四种类型:select、delete、update、add)。
    Dao中的方法不可以重写,因为程序是根据namespace+id组成的key去查询MappedStatement的,如果重载就找不到了。
    Dao接口的底层原理是动态代理,代理对象proxy拦截接口的方法,去执行 MappedStatement中的sql,返回执行结果。

    常见的分页方式有哪些?

    1. sql分页;
    2. 拦截器分页;
    3. RowBounds;
    4. 数组分类。

    Mybatis是怎么实现分页的,分页原理是什么?

    mybatis使用的是RowBounds,它针对结果集ResultSet采用的是逻辑分页,它的底层原理是拦截sql语句,对sql语句进行修改,例如select *from student;变为select ._ from (select *from student)s limit (0,10);

    1.6.1、SpringSecurity框架

    什么是授权?

    定义:根据权拥有的限访问对应的资源。

    权限管理模型是什么样的?

    模型分三层:主体权限资源
    在这里插入图片描述
    三层关系图:
    在这里插入图片描述

    1. RBAC权限管理模型

    1. 基于角色的权限控制(Role-based access conroller)

    if(主体.hasRole(“总经理id”){
    	查询工资
    }
    

    2. 基于资源的权限控制

    if(主体.hasPermission(“”查询工资权限标识){
    	查询工资
    }
    

    2. 基于属性的权限验证(ABAC: Attribute-Based Access Control)

    属性分为四类:
    用户:用户年龄、地址
    环境:当前时间
    操作:增、删、改、查
    对象:资源属性
    案例:允许班主任在上课时间进出校门
    用户:班主任;环境:上课时间;操作:进出;对象:校门

    认证和授权有什么区别?

    1、认证是验证你的账号和密码是否符合要求
    2、授权是管理哪些用户可以访问哪些资源

    Cookie
    cookie是保存在客户端的,用于存储用户的信息和记录用户的行为。

    Cookie可以用于哪些应用场景?

    1、 比如保存账号密码、设置信息啥的;
    2、 还可以保存session和token;
    3、 记录和分析用户的行为:比如用户在哪个商品停留。

    Cookie和Session有什么区别,如何使用session进行身份验证

    session是服务器端用于记录用户的状态,应用场景:购物车,用session记录哪个用户选择哪件商品。
    Cookie是存储在客户端,记录用户信息和行为,没session安全

    session是如何进行身份验证的?

    SessionID存放在Redis数据库中,登录成功,返回给客户端带有SessionID的cookie,客户端请求服务器端的时候,带上SessionID,就能验证用户的身份。
    主要步骤
    1、 用户输入账号和密码
    2、 服务端创建session,将session的信息存入数据库
    3、 服务端返回SessionID,存入用户Cookie
    4、 用户用Cookie+SessionID登录
    5、 服务器端用SessionID和数据库的session中的信息对比,进行验证
    注意点
    1、 使用seeion的关键业务注意开启cookie
    2、 注意session的过期时间

    如果禁用了cookie,session还能用吗?

    如果你方案使用的session保存sessionID,就不能
    但是你可以吧sessionID通过url传输,但是安全性会小很多

    为什么Cookie无法防止CSRF攻击,但token可以?

    CSRF(跨站请求伪造)通过让用户点击链接获取用户cookie中的sessionID向服务端发起请求。tocken因为在用户登录成功后,在每个向服务器的请求中后面跟上tocken令牌,而攻击者是无法获得令牌的。注意:cookie和token都无法阻止XSS(跨站脚本攻击)攻击。

    session存在什么问题?

    1、不安全,无法防止SCRF攻击
    2、不适合移动端,因为需要cookie
    3、需要服务端可用性保证,比如你用redis保存,就需要保证服务器redis不挂掉

    JWT(Json Web Token)

    主要由以下三部分组成:
    1、Header:签名算法+token类型
    2、payLoad(负载):里面放置和需要传递的信息
    3、signature(签名):使用header+payload+secret(秘钥)生成一个签名
    主要步骤(4步):
    1、 用户成功登陆服务器
    2、 服务器端为该用户生成jwt:其中包含了该用户的信息
    3、 用户在每个请求中加上jwt
    4、 服务器端在请求Header的Authorization字段中找到用户的信息比对

    1.6.1、Redis框架

    Redis数据库有什么特点?

    • C语言写的,内存数据库,读写贼快;
    • Redis可以用来做分布式锁、消息队列。

    redis和memcached的区别:

    1、redis做分布式缓存(问题:单机缓存容量受限,分布式两个服务可以共用缓存)更好
    2、redis数据类型更丰富,有list、set、zset、hash
    3、支持持久化,aof和rdb
    4、有灾难恢复机制
    5、单线程,多路IO复用
    

    用redis缓存访问数据有什么好处?

    高并发,mysql的QPS(每秒访问次数)是1w,redis可以10w到30w;
    

    Redis有哪些数据类型?

    String

    	//添加数据
    	set k1 v1
    	//获取数据
    	get k1
    	//批量插入获取数据
    	mset k1 v1 k2 v2
    	mget k1 k2
    	//设置数据过期
    	expire key 60
    

    List

    结构:双向链表
    //左压,左弹出,右压,右弹出
    lpush、lpop、rpush、rpop
    Hash
    数组+链表,类似于HashMap
    //增加,删除
    hset、hget
    

    set
    定义:类似于HashSet,去重无序集合

    set类型有什么特别的方法?

    1、 判断一个元素是否在某个集合;
    2、 求交集、并集
    应用场景:将一个用户所有关注、所有粉丝分别放入集合,方便共同好友、共同粉丝
    sorted set
    定义:和set相比,增加权重score,根据score排序
    应用场景:礼物排行

    I/O多路复用模型是什么样的

    • 多个socket连接
    • I/O多路复用程序
    • 文件事件分派器(把每个socket连接到对应的事件处理器)
    • 事件处理器

    为什么Redis不采用多线程的方式去实现

    1、 单线程容易维护
    2、 redis性能瓶颈不在cpu,而在内存和网络
    3、 多线程就要处理死锁、上下文切换,性能消耗

    Redis热点过期时间是什么,有什么作用?

    防止内存溢出,一般只会给热点数据永不过期
    应用场景:短信验证码,1分钟过期

    Redis是如何判断数据过期的?

    采用了过期字典:字典的key指向redis数据库中某个key,value为long long类型的时间

    Redis过期数据删除策略是什么样的?

    惰性删除:取到过期==》删除,对cpu友好
    定期删除:定期取出一批删除过期,对内存友好
    redis采用定期+惰性
    问题:两种方式都漏掉的数据堆积

    Redis内存淘汰机制有哪些?

    1、lru:最近最少使用
    2、ttl:即将要过期的
    3、random:随机淘汰
    4、key-lru:最近最少使用的key
    5、eviction:禁止写入数据

    Redis持久化机制是什么样的?

    RDB
    数据库当前数据快照
    AOF
    数据库操作记录

    Redis事务

    ACID
    注意:redis不支持回滚,因此没有原子性

    缓存穿透

    缓存中没有,请求全部到达数据库
    解决方法:
    1、缓存无效key:写一个到缓存中
    2、布隆过滤器:把有效数据存进去,里面没有的,返回参数异常

    缓存雪崩

    缓存失效,大量请求到达数据库
    解决方案:
    1、 redis集群,防止单点故障
    2、 限流

    缓存和数据库一致性

    解决问题:更新数据库,删除缓存失败
    1、 缓存失效时间设置短一点
    2、 删除失败,多次重试

    在这里插入图片描述

    2、算法部分

    2.1、链表、队列、二叉树、栈等数据结构,时间和空间复杂度

    2.2、各种排序算法的时间空间复杂度

    2.3、查找算法原理(二分查找)

    2.4、其他常用算法的思想(分治法、动态规划、回溯法、剪枝法、贪心算法)

    在这里插入图片描述

    3、计算机网络

    3.1、七层协议、子网划分,了解tcp/ip协议栈

    ISO七层协议有哪几层?

    应用层、会话层、表示层、传输层、网络层、数据链路层、物理层。

    TCP/IP四层协议分别是哪几层?

    应用层、传输层、网络层、网络接口层。

    3.2、应用层中http、https协议

    Http1.0协议和Http1.1协议有什么区别?

    主要有以下4点的不同:

    • Http1.0协议采用的是短连接(短连接指一次请求后,连接断开。如果需要再次请求,需要再次建立连接。),Http1.1协议采用的是长连接(长连接在一次请求后会一直保持连接,直到全部请求完毕才会断开连接),长连接又分为2种模式(1)流水线模式;(2)非流水线模式(非流水线模式下,客户端只有在接收到Http响应报文后才会发送下一次请求);
    • 增加了26个错误状态码,比如410(服务器资源永久删除);
    • Http1.1添加了许多缓存控制策略
    • 网络传输优化:Http1.1允许传输部分对象,而在Http1.0时,必须要传输完整的对象。

    Http协议和Https协议的区别?

    1. Http的端口号是80,Https的端口号为443;
    2. Https是加密传输协议,传输的内容采用的是对称加密(秘钥只有一个,加密和解密使用同一个秘钥),加密的秘钥在服务端采用非对称加密(秘钥是成对出现的,公钥私钥无法相互推测)。

    3.3、传输层TCP、UDP协议

    TCP协议和UDP协议的区别?

    主要有以下4点:

    • TCP是面向连接的,UDP是面向无连接的;
    • TCP传输效率低,UDP传输效率高;
    • TCP是可靠的,UDP不可靠;
    • TCP是以字节流传输的,UDP是以数据报传输的。

    3.4、DNS解析过程

    3.5、计算机网络常问面试题

    URI和URL的区别?

    URI:统一资源标识符,定位某一种资源,相当于一个人的身份证。
    URL:统一资源定位符,标识某一种资源,相当于一个人的家庭地址。

    浏览器输入URL到浏览器显示页面的过程?

    该过程主要分为6步:

    1. 通过DNS解析将域名转化为ip地址;
    2. 建立TCP连接;
    3. 发送Http请求;
    4. 服务器处理请求返回Http报文;
    5. 浏览器渲染界面;
    6. 结束连接。
      在这里插入图片描述

    4、操作系统

    什么是操作系统?(操作系统有什么用?)

    • 操作系统是用来管理系统的硬件资源软件资源的;
    • 操作系统的核心是内核,它主要用于系统的管理。主要分为以下四部分的管理:(1)应用程序;(2)文件系统;(3)内存管理;(4)硬件管理;
    • 操作系统屏蔽了底层硬件的复杂性。

    4.1、换页算法(OPT、FIFO、LRU)

    4.2、进程和线程

    进程是资源分配的最小单位,线程是CPU调度的最小单位。

    进程和线程有什么区别?

    1. 一个进程包含多个线程;
    2. 进程之间很难进行数据交换,但是统一进程下的线程很容易资源共享;
    3. 进程之间相互是不会影响的,但是一个线程挂掉了他所在的进程也会挂掉。

    4.3、死锁的防治和避免

    死锁产生的原因

    线程A获得一部分资源,但是它还需要另一个线程B的资源;线程B获得一部分资源,但是它还需要另一个线程A的资源。两个线程都无法推进,从而形成了死锁。

    死锁产生的必要条件(必须满足所有条件,缺一不可)

    • 互斥条件:资源只能一个进程拥有,不能共享;
    • 不可剥夺条件:资源在一个进程用完后,别的进程拿不走;
    • 请求和保持条件:进程要拥有一部分资源,还需要请求一部分资源;
    • 循环等待:请求之间要形成一个闭环。

    预防死锁的四种方式

    1. 破坏互斥条件:使用spooling技术;
    2. 破坏不可剥夺条件:当一个进程请求资源失败,释放拥有的资源;
    3. 破坏请求和保持条件:一开始就让一个进程获取所有的资源;
    4. 破坏循环等待:给每个进程一个执行顺序。

    避免死锁的方式(银行家算法

    银行家算法的场景
    在这里插入图片描述
    银行家算法的核心是判断每次分配资源后,系统是否会进入不安全状态(所谓的不安全状态就是系统剩下的资源无法满足任何一个进程的需求)。

    4.4、虚拟内存

    4.5、操作系统常问面试题

    除了上面需要掌握的知识点,操作系统还经常会问到如下的面试题。

    什么是系统调用?

    我们调用一般的应用程序都是用户态,只有调用内核态的资源才是系统调用

    那么,什么是用户态和内核态?

    用户态:处于用户态的进程可以调用用户程序所有的资源;
    内核态:处于内核态的进程可以调用系统几乎所有的资源。

    系统调用有哪几种类别?

    • 进程控制;
    • 进程通信;
    • 内存管理
    • 设备管理
    • 文件管理。

    进程调度的5中算法是什么?

    • 先来先服务:先进入队列的进程先会被调用;
    • 短作业优先:执行时间短的进程先会被执行;
    • 优先级调度:优先级高的线程先会被执行;
    • 时间片轮转:系统根据每个进程被分配的时间片调用进程;
    • 多级反馈队列调度:同时采用优先级调度和短作业优先(这是目前公认的最好的调度算法,UNIX系统就是使用的这种调度算法)
      在这里插入图片描述

    5、数据库

    5.1、数据库三大范式(参考博客https://www.cnblogs.com/wsg25/p/9615100.html)

    三大范式有什么作用?

    防止信息的重复导致增删改查发生错误。

    1、第一范式

    在这里插入图片描述
    第一范式要求每一个列都是不可分割的,上图中家庭信息和学校信息都是可以分割的。

    2、第二范式

    在这里插入图片描述
    第二范式要求在第一范式的基础上,所有的普通列都得依赖于主键,上图中的订单号和产品号各自管理着一些属性。改成如下图就好了。
    在这里插入图片描述

    3、第三范式

    在这里插入图片描述
    第三范式要在第二范式的基础上消除传递依赖,班主任姓名依赖于学号,班主任其他属性依赖于版这人姓名,应该修改为如下图。
    在这里插入图片描述

    5.2、触发器、存储过程

    1. 触发器

    对特定的数据进行增删改是触发,执行触发器中的语句。

    触发器的应用场景有哪些?

    1. 数据的安全性检查

    //禁止在工作时间插入员工
    create or replace trigger security_trigger
    before insert on emp
    begin
    	if 条件一 or 条件2
    	操作
    	end if
    end
    

    2. 数据的验证

    //涨工资后的工资要高于原有的工资
    create or replace trigger check_salary
    before update on emp for each row
    begin
    	if 条件一 or 条件2
    	操作
    	end if
    end
    

    5.3、数据库索引使用场景,B树、B+树概念

    B树

    在这里插入图片描述

    1. 索引不重复,从左向右是递增的;
    2. 索引和数据放在了一起;
    3. 叶子结点都指向null。

    B+树

    在这里插入图片描述

    1. B+树的非叶子结点只存放索引,这样它可以存放更多的索引
    2. 叶子结点添加了指针,可以横向查找;
    3. 叶子结点默认大小16KB。

    5.4、事务的特性,事务的隔离级别,对应的锁

    ACID(原子性、一致性、隔离性、持久性)
    原子性:事务是不可分割的最小单位;
    一致性:事务执行前后处于一致性状态,A给B转账,A减少500块,B必须要增加500块,不然不满足一致性;
    隔离性:多个并发事务之间相互不影响的;
    持久性:事务一旦被提交,对数据库中的数据修改是永久的。

    脏读

    事务一读取了事务二更新但是没有提交的数据。

    幻读

    事务一读取数据,事务二插入数据,事务二多读了几行。

    不可重复读

    事务一读取数据,事务二把数据更新,事务一再次读取数据,两次数据不一样。

    事务的隔离级别有哪几种?

    • 可重复读
    • 读已提交
    • 读未提交
    • 串行读
      在这里插入图片描述

    5.5、熟悉sql语句,联表查询优缺点

    5.6、sql优化手段

    1. 数据库中有哪些锁?

    根据数据的操作类型锁可以分为读锁写锁

    读锁

    读锁是共享锁,允许多个线程读取同一份数据。

    写锁

    写锁是排他锁,同一时间只允许一个线程进行写数据。

    根据锁的细粒度锁可以分为行锁表锁

    行锁

    行锁只对特定的行上锁,因此行锁可以支持事务。
    数据库引擎Innodb使用的是行锁。

    表锁

    当一个线程操作表中的数据时,对整张表进行上锁,其他线程无法对表进行修改,很明显表锁的并发度非常的低。
    比如数据库引擎MyIsam使用的是表锁。

    间隙锁

    间隙锁的作用是让某个处于间隙的数据被锁定,不会发生变化。

    间隙锁有什么危害?

    间隙锁可能会锁定一个根部不存在的键值,导致无法对间隙进行操作。

    展开全文
    tfak47 2021-04-11 12:55:55
  • 当然,初始一门语言,可以无须太过深入的去研究这些,但也要在心中建立起一个Python的知识框架,在接下来的学习过程中,不断的其填充和完善这一框架,有如造房筑楼。 初识Python Python 支持OOP(Object ...

    目录

    前言

    前面的博文从记录了如何Setup Python开发环境,到IPython的基本使用。现在便由浅入深的学习Python编程艺术。学习一门编程语言,首先要了解其底层的实现机制和程序处理的过程,也就是了解其设计思想和解析实现。当然,初始一门语言,可以无须太过深入的去研究这些,但也要在心中建立起一个Python的知识框架,在接下来的学习过程中,不断的其填充和完善这一框架,有如造房筑楼。

    初识Python

    Python 支持OOP(Object Oriented Programming面向对象编程实现),是一种解析型语言,有别于编译型语言。我们对比其他不同类型的语言和两种主流的程序设计思想来理解Python的OOP和解析型的特性。

    解析型与编译型

    编译型语言:如C/C++,在运行前有一个独立的编译过程,会将其编译成二进制机器语言的执行文件(.exe),以后的运行无需再次编译,效率较高。特点在于,一次编译多次运行,快速的执行程序,但是也因为没有虚拟机的中间件,只能编译成符合本机指令集的可执行文件,所以不能支持跨平台。而且其在编译时既可以发现存在的语法、数据类型转换、变量类型匹配等错误,尤其会有明确的数据类型检测,因此也被归为强类型语言。
    解析型语言:如Java/Python,在每次运行时都会将源代码转换成字节码(bytecode)文件(.pyc/.javac)而非二进制的机器语言。然后在将字节码交由虚拟机(jvm/pvm)处理,进而在虚拟机中根据平台的不同编译出兼容当前平台指令集的机器码。这也是Java和Python 能具有跨平台优势的根本实现,但相对的,程序执行效率也会因此而降低。而且解析型语言中的代码错误,只有在运行的时候才会Output出来,之后再对代码作出修正。这样也会造成不断运行->ERROR->修改->运行的不便,因此一般的解析型语言会配有Shell的功能,可以方便程序或代码行的调试。这也是博猪将IPython的介绍置于本篇之前的原因,学习好IPython对Python 程序的编写如虎添翼。
    脚本语言:如ASP/PHP/Javascript,则是需要相应的脚本引擎来支持解析和执行。

    OOP与POP

    OOP、POP是两种主流的程序设计思想,各有千秋,能与契合度高的编程语言配合编写出在不同的执行平台和环境中均能发挥出最好性能的应用程序。编程思想的发展转变以计算机硬件发展为主要导向之一,从来都是时间和空间的较量。从最初硬件资源缺乏而诞生以时间换空间的POP,到现在硬件资源过剩而改进的以空间换时间,甚至满足更复杂需求的OOP。但退一步来说,并不是意味POP就逊色于OOP,谁优谁劣,只在于摆放的位置而已。在理解OOP和POP之前,首先我们对面向对象和面向过程要有一个了解,并不要将面向对象和OOP划上等号。总而言之,程序编写的修行,从来都是以编程思维和程序结构设计的研习为重中之重。
    面向过程:以过程、步骤为中心。是一种步进、流程化且注重顺序的思考方式。分析为了解决问题所要执行步骤,编写函数来实现这些步骤,最后程序执行时再一个一个调用函数来最终解决问题。而POP就是基于这种思维发展而来的“面向对象的编程实现”。
    面向对象: 面向对象的内容包含了面对过程,且较之更抽象和复杂。面向对象不仅是一种编程思维,更是解决问题和处理数据方式的抽象,适用于程序结构设计、数据库系统等多个方面。从较为经典的一句话来形容,那就是:面向对象是一种对现实世界的理解和抽象的方法。现今以面向对象思维为基础发展而来的技术主要有:OOA(Object Oriented Analysis)面向对象的分析,OOD(Object Oriented Design)面向对象的设计,OOP面向对象的编程实现(Object Oriented Programming)。其中面向对象的程序设计实现主要有三要素:封装继承多态。OOP以数据为中心,所有的对象都围绕数据来展开,设计数据结构来组织数据,并提供对此类数据所允许的操作。程序中的对象都应该能够接受数据,处理数据并传达数据给其他的对象。所以对象也是OOP中的基本单位,Python更是秉承了一切皆对象的设计理念。根据面向对象的性质能带来以下的好处:
    1). 以人更容易理解的方式对复杂的系统进行分析、设计。(封装成类后,只需要关心数据的Input和Output,降低程序框架设计难度,可以快速的搭建系统)
    2). 降低编程难度,提高编程效率。(继承、多态,实现了极高的代码重用性)
    3). 软件拥有更高的可扩展性。(程序松耦合模块化设计,对单一模块的修改并不会影响到整合系统)

    相关概念1

    面向对象的一般概念
    ⑴对象。
    对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
    ⑵对象的状态和行为。
    对象具有状态,一个对象用数据值来描述它的状态。
    对象还有操作,用于改变对象的状态,对象及其操作就是对象的行为。
    对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中
    ⑶类。
    具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。
    类具有属性,它是对象的状态的抽象,用数据结构来描述类的属性。
    类具有操作,它是对象的行为的抽象,用操作名和实现该操作的方法来描述。
    ⑷类的结构。
    在客观世界中有若干类,这些类之间有一定的结构关系。通常有两种主要的结构关系,即一般–具体结构关系,整体–部分结构关系。
    ①一般–具体结构称为分类结构,也可以说是“或”关系,或者是“is a”关系。
    ②整体–部分结构称为组装结构,它们之间的关系是一种“与”关系,或者是“has a”关系。
    ⑸消息和方法。
    对象之间进行通信的结构叫做消息。在对象的操作中,当一个消息发送给某个对象时,消息包含接收对象去执行某种操作的信息。发送一条消息至少要包括说明接受消息的对象名、发送给该对象的消息名(即对象名、方法名)。一般还要对参数加以说明,参数可以是认识该消息的对象所知道的变量名,或者是所有对象都知道的全局变量名。
    类中操作的实现过程叫做方法,一个方法有方法名、返回值、参数、方法体。
    面向对象的特征
    ⑴对象唯一性。
    每个对象都有自身唯一的标识,通过这种标识,可找到相应的对象。在对象的整个生命期中,它的标识都不改变,不同的对象不能有相同的标识。
    ⑵抽象性。
    抽象性是指将具有一致的数据结构(属性)和行为(操作)的对象抽象成类。一个类就是这样一种抽象,它反映了与应用有关的重要性质,而忽略其他一些无关内容。任何类的划分都是主观的,但必须与具体的应用有关。
    ⑶继承性。
    继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。
    继承性是面向对象程序设计语言不同于其它语言的最重要的特点,是其他语言所没有的。
    在类层次中,子类只继承一个父类的数据结构和方法,则称为单重继承。
    在类层次中,子类继承了多个父类的数据结构和方法,则称为多重继承。
    多重继承,JAVA、VB、NET、Objective-C均仅支持单继承,注意在C++多重继承时,需小心二义性。
    在软件开发中,类的继承性使所建立的软件具有开放性、可扩充性,这是信息组织与分类的行之有效的方法,它简化了对象、类的创建工作量,增加了代码的可重用性。
    采用继承性,提供了类的规范的等级结构。通过类的继承关系,使公共的特性能够共享,提高了软件的重用性。
    ⑷多态性(多形性)
    多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。
    多态性允许每个对象以适合自身的方式去响应共同的消息。
    多态性增强了软件的灵活性和重用性。

    Python的解释器

    解析器是程序源代码和计算机硬件之间的软件逻辑层,它将源代码文件翻译成计算机可执行的文件,既Python的交互式运行环境。Python拥有多种使用不同语言实现的解释器。常用的有如:
    1). CPython(C):是速度最快也是最常用的官方默认的Python解析器,会将Python解释为字节码(bytecode)
    2). Jython(Java):可以将Java模块加载到Python模块中使用,能直接调用java类库。其使用了JIT(Just In Time即时编译)技术,运行Python代码时,会将其转化为Java代码,再交由JRE来处理。并且Jython可以将Python程序做成Jar包,使用Jython让Python程序员可以编写Java程序。
    注1:JIT(即时编译技术),会将经JVM翻译的字节码后生成的机器码保存起来,当需要再次使用的时候直接调用保存好的机器码,从而不需要再次执行编译过程。提高解析型语言执行效率,使其更靠近编译型语言。但当前的JIT为了减轻编译负载,只会对经常使用的代码进行编译,如:循环。
    注2:Java中的JIT,最早的Java建置方案是由一套转译程式(interpreter),将每个Java指令都转译成对等的微处理器指令,并根据转译后的指令先后次序依序执行,由于一个Java指令可能被转译成十几或数十几个对等的微处理器指令,这种模式执行的速度相当缓慢。 针对这个问题,业界首先开发出JIT(just in time)编译器。当Java执行runtime环境时,每遇到一个新的类别(class:类别是Java程式中的功能群组),类别是Java程式中的功能群组-JIT编译器在此时就会针对这个类别进行编译(compile)作业。经过编译后的程式,被优化成相当精简的原生型指令码(native code),这种程式的执行速度相当快。花费少许的编译时间来节省稍后相当长的执行时间,JIT这种设计的确增加不少效率,但是它并未达到最顶尖的效能,因为某些极少执行到的Java指令在编译时所额外花费的时间可能比转译器在执行时的时间还长,针对这些指令而言,整体花费的时间并没有减少。基于对JIT的经验,业界发展出动态编译器(dynamic compiler),动态编译器仅针对较常被执行的程式码进行编译,其余部分仍使用转译程式来执行。也就是说,动态编译器会研判是否要编译每个类别。动态编译器拥有两项利器:一是转译器,另一则是JIT,它透过智慧机制针对每个类别进行分析,然后决定使用这两种利器的哪一种来达到最佳化的效果。动态编译器针对程式的特性或者是让程式执行几个循环,再根据结果决定是否编译这段程式码。这个决定不见得绝对正确,但从统计数字来看,这个判断的机制正确的机会相当高。事实上,动态编译器会根据「历史资料」做决策,所以程式执行的时间愈长,判断正确的机率就愈高。以整个结果来看,动态编译器产生的程式码执行的速度超越以前的JIT技术,平均速度可提高至50%。2
    3). PyPy(Python): 使用Python的自实现,会将Python程序直接转化为机器码,性能比CPython更好。
    4). IronPython(C#):能够直接调用.net平台的函数库,也可以将Python程序编译成.net程序。
    5). Cython(C/C++):Cyhon将Python代码转化为C/C++代码,相同的也可以在Python代码中直接使用C/C++函数模块。Cython执行Python程序的效率比CPython要更加快,Cython也可以交Python程序编译成一个类库(Linux的.so文件、Win的.dll文件、Python的Module文件都是函数和属性的集合,叫法上有所区别),并可以将这个类库当作一个模块直接使用。Cython的文件扩展名为.pyx。

    Python程序设计的思想

    Python语言的设计理念是“简单”、“语法严格”、“可读性高”,其中Python独特的缩进格式就是为此而生,严格的缩进语法,可以使代码看起来更加清晰而优雅。而且为了Python程序的可读性和可维护性,以此来支撑大型系统的开发,设计团队提倡“用一种最好的方法,来解决一个问题”的程序实现思路。Python也被称之为“胶水语言”,其提供了丰富的API和不同类型的解释器,使其可以轻松的嵌入多种语言来扩充模块。相对的,Python编译器本身也能够嵌入到其他需要脚本语言的程序内。

    Python的编程风格

    代码组:缩进相同的一组语句构成的代码块。代码组首行以关键字开始,以:结束,”:”是代码块的起始标识,并且以关键字开始的一个语句的末尾必须有” : “才可以通过语法的检测。在代码组中的同一行若有多个语句则需要使用” ; “隔开。
    注释:在Python中,以”’ ‘”三引号来作为注释的标识,可以作为单行注意,亦可以作为多行注释。同时也被用作每一个Python的Mudole文件的文档声明,之后便可以通过Module的.__doc__方法来调用Module文档。

    最后

    总而言之,要响深入的学习一门语言,需要从理论到实际的双向结合、同步进行,才可以编写出符合该语言风格的程序来。再往后的博文中,我们继续来一起学习Python的程序编写和其理论的底层实现。

    Jmilk


    1. Stanley B.Lippman / Josee Lajoie / Barbara E.Moo .《C++ Primer》:人民邮电出版社,2010
    2. JIT百度百科
    展开全文
    Jmilk 2015-09-08 18:37:49
  • xiaonuo911teamo 2018-09-22 23:06:09
  • weixin_39742958 2021-04-21 19:15:11
  • guwenxi999 2021-03-02 21:07:25
  • qq_49101841 2021-07-17 02:52:07
  • han422858897 2021-07-05 10:51:46
  • qq_41405475 2021-01-22 14:01:46
  • weixin_42137700 2020-07-20 12:38:00
  • Cccrush 2019-09-17 10:25:34
  • m0_37777700 2018-11-01 15:04:39
  • SUNXIOAJIE 2021-03-19 14:11:49
  • lsfreeing 2017-09-10 13:00:49
  • dQCFKyQDXYm3F8rB0 2020-08-06 17:22:35
  • u013628152 2016-05-04 21:44:15
  • shengqianfeng 2021-01-22 23:33:48
  • wangjun_huster 2017-05-06 21:38:51
  • m0_46521785 2021-11-14 20:54:55
  • qingwufeiyang12346 2018-12-06 00:06:42
  • hongguocai 2019-06-23 18:32:55
  • weixin_32316643 2021-06-27 01:15:19
  • f101690321 2015-06-05 21:32:02
  • qq_40506723 2021-09-08 08:19:55
  • weixin_39642981 2020-12-22 07:57:20
  • Shb_derek 2019-09-06 10:04:10
  • qq_42229253 2020-08-15 17:24:46
  • qq_41306849 2021-08-30 11:37:38
  • fxd1232 2021-06-21 16:58:45

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,791
精华内容 4,316
关键字:

丰富自己的知识储备