目录
计算机网络:
1.OSI和TCP/IP协议分别有哪几层,每一层都有什么协议
2.TCP和UDP的区别,TCP如何保证可靠性传输
3.如何区分TCP报文和UDP报文?TCP和UDP分别对应的常见应用层协议
4.TCP三次握手四次挥手过程以及原因
5.长连接短连接区别
6.HTTP的几种方法,比如GET、POST都是怎么用的,带了什么参数。
7.GET和POST的区别
8.HTTP和HTTPS的过程,HTTP和HTTPS的区别
9.说一下HTTPS是怎么加密的
11.ip是怎么寻址的?路由算法
12.IP地址分类,A类地址有多少位?
13.BS架构CS架构
14.从输入网址到获得页面的过程
15.HTTP状态码
操作系统:
1.怎么去优化、加快高并发
2.对多线程怎么理解,线程池的理解
3.什么是虚拟内存?
4.什么是缓存?
5. 说一下常用的linux命令
6.进程和线程的区别,线程间通信和同步方式,进程通信方式
7.进程调度和线程调度的方法
数据库:
1.数据库事务的特性,隔离级别,什么是脏读,什么是幻读
2.死锁是什么,死锁产生的原因,四个必要条件,怎么避免死锁
3. 锁的分类
4.内连接,外连接,左连接,右连接
5.主键和外键是什么意思
6.什么是视图以及视图的优缺点,视图的使用场景
7.什么是索引以及索引的优缺点和类型
8.介绍一下三个范式
9.SQL语句顺序,修改表结构,修改表数据的SQL语句
10.什么是存储过程
11.数据库数据太多该怎么存储
12.模糊索引
13. 聚集索引,非聚集索引
数据结构:
1.Java和C++的区别
2.C++的特性,对多态的理解,重载和重写的区别
3. 指针和引用的区别,sizeof指针和数组的区别,指针占多大内存
4.类和对象的区别
5.解释一下static关键字
6.private,protected,public区别
7.冒泡排序,如何优化,以及快排和堆排序的原理
8.整型数字转换成字符串
9.如何比较两个浮点数相等
10.二叉树
11.C++内存模型
12.堆和栈的区别
13.C++中有哪些锁
14.静态编译的作用
15.野指针
16.内存对齐以及他的作用
17.内存溢出,内存溢出怎么解决,内存碎片怎么解决
18.迭代器定义,迭代器失效
JAVA:
1.对Java直观印象,宣传的是write one,write anywhere,对Java的了解
2.jdk和jre的区别
3.java垃圾回收(GC)
4.Java异常处理机制
5.final finally 和finalize区别
6.强引用,软引用,弱引用,幻象引用
7.JAVA的几种线程锁
8.volatile和synchronized的区别
9.wait()和sleep()的区别
10.start()和run()方法的区别
11.Array list和Link list的区别
12.HashMap和Hashtable的区别
13.String,StringBuffer,StringBuilder
14.redis分布式锁原理与实例
15.Junit
16.java复制文件的4种方式
17.java目前流行的framwork框架
测试:
1.项目中测试步骤
2.用例执行的流程
3.什么是回归测试
4.测试的各种模型,瀑布模型,V模型,W模型
5. 黑盒测试和白盒测试的区别以及各自常用的方法
6. 条件覆盖,等价类划分,举例子
7.α测试和β测试有什么区别?
8.什么是bug?怎么描述bug?
9. 发现测试的错误后如何告诉开发人员,如果开发人员不接受怎么办,如果还不接受怎么办?
10. 怎么设计测试方案和测试报告?
11.设计一个网页登录框的测试用例
12.测试微信点赞功能
13.测试输入框只能输入整数功能
14.测试淘宝购物车功能
OSI分层(7层):物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP分层(4层):网络接口层、网际层、运输层、应用层。
物理层是为数据端设备提供传送数据的通路以及传输数据。
数据链路层的主要任务是实现计算机网络中相邻节点之间的可靠传输,把原始的、有差错的物理传输线路加上数据链路协议以后,构成逻辑上可靠的数据链路。
网络层负责对子网间的数据包进行路由选择。此外,网络层还可以实现拥塞控制、网际互连等功能。
传输层起着承上启下的作用,涉及源端节点到目的端节点之间可靠的信息传输。传输层需要解决跨越网络连接的建立和释放,对底层不可靠的网络,建立连接时需要三次握手,释放连接时需要四次挥手。
会话层的主要功能是负责应用程序之间建立、维持和中断会话,同时也提供对设备和结点之间的会话控制,协调系统和服务之间的交流,并通过提供单工、半双工和全双工3种不同的通信方式,使系统和服务之间有序地进行通信。
表示层关心所传输数据信息的格式定义,其主要功能是把应用层提供的信息变换为能够共同理解的形式,提供字符代码、数据格式、控制信息格式、加密等的统一表示。
应用层为操作系统或网络应用程序提供访问网络服务的接口。
TCP/IP协议是一个开放的网络协议簇,它的名字主要取自最重要的网络层IP协议和传输层TCP协议。TCP/IP协议定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。TCP/IP参考模型采用4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求,这4个层次分别是:网络接口层、互联网层(IP层)、传输层(TCP层)、应用层。
网络接口层:TCP/IP协议对网络接口层没有给出具体的描述,网络接口层对应着物理层和数据链路层。
互联网层 ( IP层 ):互联网层是整个TCP/IP协议栈的核心。它的功能是把分组发往目标网络或主机。同时,为了尽快地发送分组,可能需要沿不同的路径同时进行分组传递。因此,分组到达的顺序和发送的顺序可能不同,这就需要上层必须对分组进行排序。互联网层除了需要完成路由的功能外,也可以完成将不同类型的网络(异构网)互连的任务。除此之外,互联网层还需要完成拥塞控制的功能。
传输层 ( TCP层 ):TCP层负责在应用进程之间建立端到端的连接和可靠通信,它只存在与端节点中。TCP层涉及两个协议,TCP和UDP。其中,TCP协议提供面向连接的服务,提供按字节流的有序、可靠传输,可以实现连接管理、差错控制、流量控制、拥塞控制等。UDP协议提供无连接的服务,用于不需要或无法实现面向连接的网络应用中。
应用层:为Internet中的各种网络应用提供服务。
网络层:IP协议、ICMP协议、ARP协议、RARP协议。
传输层:UDP协议、TCP协议。OSPF
应用层:FTP(文件传送协议)、Telenet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议),POP3协议(邮局协议),HTTP协议。RIP
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
数据包校验:目的是检测数据在传输过程中的任何变化,若校验出包有错,则丢弃报文段并且不给出响应,这时TCP发送数据端超时后会重发数据;
对失序数据包重排序:既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重新排序,然后才交给应用层;
丢弃重复数据:对于重复数据,能够丢弃重复数据;
应答机制:当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒;
超时重发:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段;
流量控制:TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。TCP使用的流量控制协议是可变大小的滑动窗口协议。
计算机网络中的带宽、交换结点中的缓存及处理机等都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏,这种情况就叫做拥塞。拥塞控制就是 防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。注意,拥塞控制和流量控制不同,前者是一个全局性的过程,而后者指点对点通信量的控制。拥塞控制的方法主要有以下四种:
1). 慢启动:不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小;
2). 拥塞避免:拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍,这样拥塞窗口按线性规律缓慢增长。
3). 快重传:快重传要求接收方在收到一个 失序的报文段 后就立即发出 重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
4). 快恢复:快重传配合使用的还有快恢复算法,当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半,但是接下去并不执行慢开始算法:因为如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。
看IP头中的协议标识字段,6是tcp,17是udp。
1). TCP对应的应用层协议
FTP:定义了文件传输协议,使用21端口。常说某某计算机开了FTP服务便是启动了文件传输服务。下载文件,上传主页,都要用到FTP服务。
Telnet:它是一种用于远程登陆的端口,用户可以以自己的身份远程连接到计算机上,通过这种端口可以提供一种基于DOS模式下的通信服务。如以前的BBS是-纯字符界面的,支持BBS的服务器将23端口打开,对外提供服务。
SMTP:定义了简单邮件传送协议,现在很多邮件服务器都用的是这个协议,用于发送邮件。如常见的免费邮件服务中用的就是这个邮件服务端口,所以在电子邮件设置-中常看到有这么SMTP端口设置这个栏,服务器开放的是25号端口。
POP3:它是和SMTP对应,POP3用于接收邮件。通常情况下,POP3协议所用的是110端口。也是说,只要你有相应的使用POP3协议的程序(例如Fo-xmail或Outlook),就可以不以Web方式登陆进邮箱界面,直接用邮件程序就可以收到邮件(如是163邮箱就没有必要先进入网易网站,再进入自己的邮-箱来收信)。
HTTP:从Web服务器传输超文本到本地浏览器的传送协议。
2). UDP对应的应用层协议DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。
SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。
TFTP(Trival File Transfer Protocal):简单文件传输协议,该协议在熟知端口69上使用UDP服务。
TCP 三次握手
第一次握手:客户端尝试连接服务器,向服务器发送 syn 包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入 SYN_SEND 状态等待服务器确认
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个 SYN包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
TCP四次挥手
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
(1)可靠的实现TCP全双工链接的终止。
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
(2)允许老的重复的分节在网络中消逝。
假 设在12.106.32.254的1500端口和206.168.1.112.219的21端口之间有一个TCP连接。我们关闭这个链接,过一段时间后在 相同的IP地址和端口建立另一个连接。后一个链接成为前一个的化身。因为它们的IP地址和端口号都相同。TCP必须防止来自某一个连接的老的重复分组在连 接已经终止后再现,从而被误解成属于同一链接的某一个某一个新的化身。为做到这一点,TCP将不给处于TIME_WAIT状态的链接发起新的化身。既然 TIME_WAIT状态的持续时间是MSL的2倍,这就足以让某个方向上的分组最多存活msl秒即被丢弃,另一个方向上的应答最多存活msl秒也被丢弃。 通过实施这个规则,我们就能保证每成功建立一个TCP连接时。来自该链接先前化身的重复分组都已经在网络中消逝了。
3. 为什么不能用两次握手进行连接?
我们知道,3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持。
所谓短连接,指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接,一般银行都使用短连接。
GET: 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器
POST:用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式。
PUT: 传输文件,报文主体中包含文件内容,保存到对应URI位置。(put自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不用)
HEAD: 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效。
GET和POST是HTTP请求的两种基本方法
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
1、url可见性:get,参数url可见;post,url参数不可见
2、数据传输上:get,通过拼接url进行传递参数;post,通过body体传输参数
3、缓存性:get请求是可以缓存的post请求不可以缓存
4、后退页面的反应:get请求页面后退时,不产生影响;post请求页面后退时,会重新提交请求
5、传输数据的大小:get一般传输数据大小不超过2k-4k(根据浏览器不同,限制不一样,但相差不大);post请求传输数据的大小根据php.ini 配置文件设定,也可以无限大。
6、安全性:原则上post肯定要比get安全,毕竟传输参数时url不可见。
HTTP工作流程
1.建立TCP/IP连接,客户端与服务器通过Socket三次握手进行连接
客户端向服务端发起HTTP请求(例如:POST/login.html http/1.1)
2.客户端发送请求头信息,请求内容,最后会发送一空白行,标示客户端请求完毕
3.服务器做出应答,表示对于客户端请求的应答,例如:HTTP/1.1 200 OK
4.服务器向客户端发送应答头信息
5.服务器向客户端发送请求头信息后,也会发送一空白行,标示应答头信息发送完毕,接着就以Content-type要求的数据格式发送数据给客户端。
6.服务端关闭TCP连接,如果服务器或者客户端增Connection:keep-alive就表示客户端与服务器端继续保存连接,在下次请求时可以继续使用这次的连接。
HTTPS 默认工作在 TCP 协议443端口,它的工作流程一般如以下方式:
1.客户使用https的url访问服务器,要求服务器建立ssl连接。
2.服务器接收请求之后,把证书(包含公钥)传输给客户。
3.客户端和服务器端开始协商ssl链接的安全等级(加密等级)
4.协商完成后,客户端浏览器建立会话秘钥,并用网站的公钥加密会话秘钥,传输给服务器端。
5.服务器端通过自己的私钥解密出会话秘钥
6.服务器通过会话秘钥加密
1) HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
2) 使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
3) HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
4) http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
5) HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
1.使用非对称密钥加密方式,传输对称密钥加密方式所需要的 Secret Key,从而保证安全性;
2.获取到 Secret Key 后,再使用对称密钥加密方式进行通信,从而保证效率。
对称密钥加密是指加密和解密使用同一个密钥的方式,这种方式存在的最大问题就是密钥发送问题,即如何安全地将密钥发给对方;而非对称加密是指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。
由于非对称加密的方式不需要发送用来解密的私钥,所以可以保证安全性;但是和对称加密比起来,它非常的慢,所以我们还是要用对称加密来传送消息,但对称加密所使用的密钥我们可以通过非对称加密的方式发送出去。
常用的对称加密:
1).DES: java6只支持例56位秘钥长度,通过BounvyCastle可以将秘钥长度增加至64位。
2).3重DES:作为DES的改良(核心还是DES),针对DES秘钥长度偏短,迭代次数偏少,等问题进行了改良,秘钥长度从56位提升到112或168位,优点:抗穷举的能力显著增加,缺点:加密低效,处理速度较慢。
3).AES:AES的基本要求是: 比三重DES快、至少与三重DES一样安全、数据分组长度为128比特、密钥长度为128/192/256比特。优点:秘钥建立时间短,存储要求低。
常用的非对称加密:RSA:秘钥长度1024位。
单向散列函数的加密算法:MD5、SHA
数字签名保证信息的真实性和完整性;而数字证书则保证信息的不可否认性。
发送方将电子文档Hash运算,得到摘要,然后将摘要用私钥加密,就得到数字签名;
数字签名与电子文档一起发送给接收方,接收方收到后,将电子文档同样进行Hash运算得到摘要,然后将数字签名用公钥解密,并与摘要比较,相等即校验通过。
数字签名可以保证文档的真实性和完整性。是真的,且没被篡改。
真实性的理据在于接收方可以用公钥解密。因为这是非对称加密,私钥加密,公钥解密;
完整性的理据是解密后摘要与运算得到的摘要一致。而数字证书则是为了配合数字签名的辅助手段,用于保证解密数字签名的公钥的合法性,保证其没有被调包。因为黑客可能会偷偷换掉接收方的公钥,然后将用自己的私钥处理过的文档发给接收方,同样可以得到校验通过的结果。
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、所以个人建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中
IP寻址实际上就是ARP地址解析过程,(包括本地网络寻址和非本地网络寻址):首先看本地网络实现IP 寻址,也就是我们所说的同一网段通信过程,现在我们假设有2个主机,他们是属于同一个网段。主机A和主机B,首先主机A通过本机的hosts表或者wins系统或dns系统先将主机B的计算机名转换为IP地址,然后用自己的IP地址与子网掩码计算出自己所出的网段,比较目的主机B的IP地址与自己的子网掩码netmask,发现与自己是出于相同的网段,于是在自己的ARP缓存中查找是否有主机B 的MAC地址,如果能找到就直接做数据链路层封装并且通过网卡将封装好的以太网帧发送有物理线路上去:如果ARP缓存中没有主机B的的MAC地址,主机A将启动ARP协议通过在本地网络上的ARP广播来查询主机B的MAC地址,获得主机B的MAC地址后写入ARP缓存表,进行数据链路层的封装,发送数据。
A类地址:以0开头,第一个字节范围:0~127;
B类地址:以10开头,第一个字节范围:128~191;
C类地址:以110开头,第一个字节范围:192~223;
D类地址:以1110开头,第一个字节范围为224~239;
BS:(Browser/Server,浏览器/服务器模式),web应用 可以实现跨平台,客户端零维护,但是个性化能力低,响应速度较慢。
CS:(Client/Server,客户端/服务器模式),桌面级应用 响应速度快,安全性强,个性化能力强,响应数据较快
C/S 与 B/S 区别:
Client/Server是建立在局域网的基础上的.Browser/Server是建立在广域网的基础上的.
硬件环境不同:
C/S 一般建立在专用的网络上, 小范围里的网络环境, 局域网之间再通过专门服务器提供连接和数据交换服务.
B/S 建立在广域网之上的, 不必是专门的网络硬件环境,例如电话上网,租用设备, 信息管理. 有比C/S更强的适应范围,一般只要有操作系统和浏览器就行
对安全要求不同:
C/S 一般面向相对固定的用户群,对信息安全的控制能力很强,一般高度机密的信息系统采用C/S 结构适宜,可以通过B/S发布部分可公开信息.
B/S 建立在广域网之上, 对安全的控制能力相对弱,面向是不可知的用户群.
对程序架构不同:
C/S 程序可以更加注重流程, 可以对权限多层次校验, 对系统运行速度可以较少考虑.
B/S 对安全以及访问速度的多重的考虑, 建立在需要更加优化的基础之上. 比C/S有更高的要求。B/S结构的程序架构是发展的趋势,从MS的.Net系列的BizTalk 2000 Exchange 2000等,全面支持网络的构件搭建的系统. SUN 和推的JavaBean 构件技术等,使 B/S更加成熟.
软件重用不同:
C/S 程序可以不可避免的整体性考虑,构件的重用性不如在B/S要求下的构件的重用性好.
B/S 对的多重结构,要求构件相对独立的功能. 能够相对较好的重用.
系统维护不同:
系统维护在是软件生存周期中,开销大。-------重要
C/S 程序由于整体性, 必须整体考察, 处理出现的问题以及系统升级. 升级难. 可能是再做一个全新的系统
B/S 构件组成,方面构件个别的更换,实现系统的无缝升级. 系统维护开销减到最小.用户从网上自己下载安装就可以实现升级.
处理问题不同:
C/S 程序可以处理用户面固定, 并且在相同区域, 安全要求高需求, 与操作系统相关. 应该都是相同的系统
B/S 建立在广域网上, 面向不同的用户群, 分散地域, 这是C/S无法作到的. 与操作系统平台关系最小.
用户接口不同:
C/S 多是建立的Window平台上,表现方法有限,对程序员普遍要求较高
B/S 建立在浏览器上, 有更加丰富和生动的表现方式与用户交流. 并且大部分难度减低,减低开发成本.
信息流不同:
C/S 程序一般是典型的中央集权的机械式处理, 交互性相对低
B/S 信息流向可变化, B-B B-C B-G等信息、流向的变化, 更象交易中心
(1). 浏览器查询 DNS,获取域名对应的IP地址:具体过程包括浏览器搜索自身的DNS缓存、搜索操作系统的DNS缓存、读取本地的Host文件和向本地DNS服务器进行查询等。对于向本地DNS服务器进行查询,如果要查询的域名包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析(此解析具有权威性);如果要查询的域名不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析(此解析不具有权威性)。如果本地域名服务器并未缓存该网址映射关系,那么将根据其设置发起递归查询或者迭代查询;
(2). 浏览器获得域名对应的IP地址以后,浏览器向服务器请求建立链接,发起三次握手;
(3). TCP/IP链接建立起来后,浏览器向服务器发送HTTP请求;
(4). 服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器;
(5). 浏览器解析并渲染视图,若遇到对js文件、css文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源;
(6). 浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面。
301/302 Moved Permanently(重定向)请求的URL已移走。响应报文中应该包含一个Location URL,说明资源现在所处的位置
304 Not Modified(未修改) 客户的缓存资源是最新的,要客户端使用缓存内容
501 Internal Server Error 服务器遇到错误,使其无法对请求提供服务
(加缓存?合理控制线程数量?合理使用锁?使用线程池合理调度线程?)
多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。
线程池:基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
线程池的优点
1)避免线程的创建和销毁带来的性能开销。
2)避免大量的线程间因互相抢占系统资源导致的阻塞现象。
3}能够对线程进行简单的管理并提供定时执行、间隔执行等功能。
JAVA:
1.继承Thread类,重写run()方法 启动线程的唯一方法就是通过Thread类的start方法。
2.实现Runnable接口,并实现该接口的run()方法
3.实现callable接口,重写call方法。
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。
缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存汇总查询数据,有则直接执行,不存在时从内存中获取。由于缓存的数据比内存快的多,所以缓存的作用就是帮助硬件更快的运行。
linux操作的指令:
查看内存的命令(top)
查看一个文件里面的第x行到x行的内容(cat filename head-n 1000 tail -n 50啥的)
压缩(tar zxvf)
打包(tar zcvf)
文件统计有多少行数据 wc -l filename
Ps a显示所有进程 Chmod 777 *赋予所有权限 Ls显示文件 Cp复制文件 cd 切换目录 rm 删除 cat 查看文件内容 touch 创建空文件 mkdir创建文件夹
(1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
(2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
(3)进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
(4)线程是轻两级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
(5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
(6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
进程间通信方式:管道,有名管道,信号量,消息队列,信号,套接字
线程间通信方式:事件;信号量;互斥量;临界区
先来先服务(队列);最短优先(优先队列);高优先权优先调度算法;基于时间片的轮转调度算法;电梯调度算法
事务(Transaction)是并发控制单位,是用户定义的一个操作序列,这些操作要么都做,要么都不做,是一个不可分割的工作单位。ACID:原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)
原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
MySQL数据库为我们提供的四种隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
死锁:相互等待资源而产生的一种僵持状态,如果没有外力的干预将一直持续这个状态。
原因:系统资源不足、相互竞争资源、请求资源顺序不当
四个必要条件:互斥、不可抢占、循环等待、请求与保持
避免死锁:因为互斥是不可改变的,所以只能破坏其他三个条件中的一个来解除死锁,方法:剥夺资源、杀死其中一个线程
死锁避免的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。
如果操作系统能保证所有进程在有限时间内得到需要的全部资源,则系统处于安全状态否则系统是不安全的。
1、从数据库系统的角度来看:分为排它锁(即独占锁),共享锁和更新锁。
排他锁(X锁):如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受。执行数据更新命令时,SQL Server会自动使用独占锁。
共享锁(S锁):如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。共享锁下其它用户可以并发读取,查询数据。但不能修改,增加,删除数据。资源共享。
更新锁:当SQL Server准备更新数据时,它首先对数据对象做更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server确定要进行更新数据操作时,它会自动将更新锁换为独占锁,当对象上有其它锁存在时,无法对其加更新锁。
更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。
若要避免这种潜 在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。
2、从程序员的角度看:分为乐观锁和悲观锁。
乐观锁:完全依靠数据库来管理锁的工作;
悲观锁:程序员自己管理数据或对象上的锁处理;
内连接使用比较运算符根据每个表共有的列的值匹配左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
主键:唯一标识一条记录,不能有重复,不允许为空。
外键:表的外键是另一表的主键,外键是可以有重复的,可以是空值。
作用:
主键:用来保证数据完整性
外键:用来和其他表建立联系用
定义:视图是不占用存储空间的,视图只是基本表或者其它视图或者这两者的组合的一个逻辑映像而已。是为了方便用户或者应用程序使用基本表中的记录而设计的。视图可以针对不同的用户显示表中的不同部分的记录,或者在显示的时候可以使用更加人性化的列名。
视图的优缺点:
1.查询简单化。视图能简化用户的操作
2.数据安全性。视图是虚拟的,用户对视图,不可以随意的更改和删除
3.逻辑数据独立性。视图对重构数据库提供了一定程度的逻辑独立性1.性能。数据库必须把视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也把它变成一个复杂的结合体,需要花费一定的时间。
2.修改限制。当用户试图修改视图的某些行时,数据库必须把它转化为对基本表的某些行的修改。事实上,当从视图中插入或者删除时,情况也是这样。对于简单视图来说,这是很方便的,但是,对于比较复杂的视图,可能是不可修改的。
视图的使用场景:
重用SQL语句;
简化复杂的SQL操作。在编写查询后,可以方便的重用它而不必知道它的基本查询细节;
使用表的组成部分而不是整个表;
保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限;
更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据。
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。索引针对列的。可以在一列或者多列上建立索引。其实质就是在这些列上的值放入一个有序的列表中。这样把本来无序的基本表变为有序的了,大大的提高表的查询速度。
索引的优点:
① 建立索引的列可以保证行的唯一性,生成唯一的rowId
② 建立索引可以有效缩短数据的检索时间
③ 建立索引可以加快表与表之间的连接
④ 为用来排序或者是分组的字段添加索引可以加快分组和排序顺序
索引的缺点:
① 创建索引和维护索引需要时间成本,这个成本随着数据量的增加而加大
② 创建索引和维护索引需要空间成本,每一条索引都要占据数据库的物理存储空间,数据量越大,占用空间也越大(数据表占据的是数据库的数据空间)
③ 会降低表的增删改的效率,因为每次增删改索引需要进行动态维护,导致时间变长
什么情况下需要建立索引:
- 数据量大的,经常进行查询操作的表要建立索引。
- 用于排序的字段可以添加索引,用于分组的字段应当视情况看是否需要添加索引。
- 表与表连接用于多表联合查询的约束条件的字段应当建立索引。
索引的类型
主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。
唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。
可以通过 ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引
普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。
可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引
全文索引: 是目前搜索引擎使用的一种关键技术。
可以通过ALTER TABLE table_name ADD INDEX FULLTEXT (column);创建全文索引
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
SQL执行顺序:SELECT[DISTINCT] FROM WHERE GROUP BY HAVING UNION ORDER BY
1.having:用于对where和group by查询出来的分组经行过滤,查出满足条件的分组结果。它是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作。
2.group by:对select查询出来的结果集按照某个字段或者表达式进行分组,获得一组组的集合,然后从每组中取出一个指定字段或者表达式的值。 在说group by的时候,我们还需要了解聚合函数,聚合函数是SQL语言中一种特殊的函数。
rownum 只能判断=1的查询条件,1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,可以使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。
SQL>select * from(select rownum no ,id,name from student) where no>2; --有记录
SQL> select * from(select rownum,id,name from student)where rownum>2; --无记录
SQL> select rownum,id,name from student where rownum <3; --有记录
相关子查询:查询Booka表中大于该类图书价格平均值的图书信息
SELECT * FROM Books As a
WHERE 价格 >
( SELECT AVG(价格) FROM Books AS b WHERE a.类编号=b.类编号 )
存储过程是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
https://www.cnblogs.com/lanzi/archive/2011/06/15/2081460.html
通配符(%)在搜寻词首出现,一般会导致Oracle系统不使用索引。因此,要尽量避免在模糊查询中使用通配符开头,或者是开头结尾都有通配符,这样会导致降低查询速度。
--创建一个name字段的索引 create index IDX_B$L_INTEREST_INFO_NAME on B$L_INTEREST_INFO (NAME);
以下语句不能使用name字段索引:
select * from b$l_interest_info where name like '%瑞德工业园%';
如果遇到模糊查询的例子,尽量将通配符放在末尾,以常量开头,那么可以使用上索引,如下语句所示:
select * from b$l_interest_info where name like '瑞德工业园%';
如果必须将通配符放在开头,以常量结束,那么可以创建一个反向键索引
--在name字段创建一个反向键索引 create index idx_interest_info_name_re on b$l_interest_info(reverse(name)); analyze table b$l_interest_info compute statistics for table for all indexes; --没有用到反向键索引 select * from b$l_interest_info where name like '%瑞德工业园';
因此,要使用反向键索引还必须加上reverse关键字
--用上了反向键索引IDX_INTEREST_INFO_NAME_RE select * from b$l_interest_info where reverse(name) like reverse('%瑞德工业园');
如果开头结尾都要用到通配符,且select获取的字段只有该模糊查询字段,则可以用上索引:
--用到了name字段的一般索引IDX_B$L_INTEREST_INFO_NAME select name from b$l_interest_info where name like '%瑞德工业园%';
但是对于使用%%这种查询且select获取的字段包含了模糊查询字段以外的,就很难用上索引了。
https://blog.csdn.net/riemann_/article/details/90324846
https://www.cnblogs.com/aspnethot/articles/1504082.html
区别:
聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序。
索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
优势与缺点:聚集索引插入数据时速度要慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),查询数据比非聚集数据的速度快。
1.数据库统计有多少行数据,select count(*) from 表名 where 字段名='字段值';
数据库排序输出前top k个,用的是order by xx desc where rownum
2.题目:表1:id,name、age 表2:id,id_ex,score
第一题:创建两张表
第二题:求出名字为李四的同学的分数
第三题:为第二题的查询创建索引
3.学生表、课程表、学生课程表三个表,写sql语句查出所有学生课程一比课程二分数高的学号
1. Java是解释型语言。源码会先经过一次编译,成为中间码,中间码再被解释器解释成机器码。对于Java而言,中间码就是字节码(.class),而解释器在JVM中内置了。C++是编译型语言。源码一次编译,直接在编译的过程中链接了,形成了机器码。
2.java运行在虚拟机上,号称与平台无关。也就是你开发的java程序无论是unix,linux还是windows都可以正常运行。但是实际上这是一个良好的愿望,实际跨平台时还会有各种各样的问题。c和c++都是直接编译成可执行文件,是否能跨平台主要看你用到的编译器特性是否有多平台支持。
3.因为c和c++是直接编译成可执行文件,所以运行效率要比java高。
4. java因为是运行在虚拟机上,不需要考虑内存管理和垃圾回收机制。也是就你可以声明一个对象而不用考虑释放他,虚拟机帮你做这事情。而c和c++语言本身没有多少内存管理的概念,写c和c++程序如果用到指针就一定要考虑内存申请和释放。内存泄漏是c和c++最头疼的问题。
5. 代码重用:java中有一个根类object,所有的类都是其子类,通过这种方式将容器和算法分离,实现一种操作作用于多种对象,提高代码重用。c++中没有总根对象,但是c++提供了另一个更强大的功能“模板”,同样高效地实现了一种操作作用于多种对象,提供了高效的代码重用方法。
6. C++支持多继承,Java中类都是单继承的。但是继承都有传递性,同时Java中的接口是多继承,类对接口的实现也是多实现。
C++的特性是封装继承多态。封装和继承使代码重用,多态则是接口重用
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。
重写和重载的区别:
重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法。必须具有不同的参数列表
重写是子类需要修改父类的一些方法进行扩展,增大功能。重写的参数列表,返回的类型必须完全与被重写的方法相同。
1.引用不可以为空,但指针可以为空。引用是对象的别名,引用为空,对象都不存在,怎么可能有别名,故定义一个引用的时候,必须初始化。但是指针可以为空。
2.引用不可以改变指向,但是指针可以改变指向,而指向其它对象。虽然引用不可以改变指向,但是可以改变初始化对象的内容。例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。
3.从内存上分配看,程序为指针变量分配内存区域,而不用为引用分配内存区域
引用的sizeof为所引用的对象在内存中分配空间的大小(单位字节);指针的sizeof为指针的大小
指针的大小由下列决定:
- cpu位数(32位数4字节,64位数8字节)
- 操作系统位数(32位数4字节,64位数8字节)
- 编译器的位数(32位数4字节,64位数8字节)
当上述3种位数不同,取最小的位数。https://blog.csdn.net/qq_37375427/article/details/80040939
1.static 可以修饰变量、方法、代码块和内部类
2.static 变量是这个类所有,由该类创建的所有对象共享同一个 static 属性
3.可以通过创建的对象名.属性名 和 类名.属性名两种方式访问
4.static 变量在内存中只有一份
5.static 代码块在类被第一次加载时执行静态代码块,且只被执行一次,主要作用是实现 static 属性的初始化
https://blog.csdn.net/scottly1/article/details/24354489
第一: private,public,protected的访问范围:
private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问.
protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问
public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问public:可以被任意实体访问
protected:只允许子类及本类的成员函数访问
private:只允许本类的成员函数访问
第二:类的继承后方法属性变化:
使用private继承,父类的所有方法在子类中变为private;
使用protected继承,父类的protected和public方法在子类中变为protected,private方法不变;
使用public继承,父类中的方法属性不发生改变;
1.1使用函数模板+ostringstream
使用函数模板将基本数据类型(整型、字符型、实型、布尔型)转换成string。//ostringstream对象用来进行格式化的输出,常用于将各种类型转换为string类型
//ostringstream只支持<<操作符
templatestring toString(const T& t){
ostringstream oss; //创建一个格式化输出流
oss<return oss.str();
}
cout<string:输出12301
1.2使用标准库函数std::to_string()
需要include头文件。 函数原型申明如下:string to_string (int val);
abs(a-b)<1e-7
1.完全二叉树和平衡二叉树
完全二叉树:深度为k的,有n个结点的二叉树,当且仅当其每个结点都与深度为k的满二叉树中编号从1至n个结点一一对应。
平衡二叉树(Balanced Binary Tree/Height-Balanced Tree):它的左子树和右子树都是平衡二叉树;且左子树和右子树的深度之差的绝对值不超过1。
2.红黑树(Red Black Tree)
是一种自平衡二叉查找树。都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
3.B+树
3.1 B+tree性质
1.)n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引。
2.)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
3.)所有的非终端结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
4.)B+ 树中,数据对象的插入和删除仅在叶节点上进行。
5.)B+树有2个头指针,一个是树的根节点,一个是最小关键码的叶节点。
3.2 使用B+树的好处
由于B+树的内部节点只存放键,不存放值,因此,一次读取,可以在内存页中获取更多的键,有利于更快地缩小查找范围。
B+树的叶节点由一条链相连,因此,当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)时间找到最小的一个节点,然后通过链进行O(N)的顺序遍历即可。
而B树则需要对树的每一层进行遍历,这会需要更多的内存置换次数,因此也就需要花费更多的时间
3.3 B树和B+树的区别
在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。
B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。
3.4 数据库为什么使用B+树而不是B树
B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。
B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。
增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改。
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。
生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈效率更高
线程之间的锁有:互斥锁、条件锁、自旋锁、读写锁、递归锁。
互斥锁:用于控制多个线程对他们之间共享资源互斥访问的一个信号量。也就是说是为了避免多个线程在某一时刻同时操作一个共享资源。例如线程池中的有多个空闲线程和一个任务队列。任何是一个线程都要使用互斥锁互斥访问任务队列,以避免多个线程同时访问任务队列以发生错乱。
在某一时刻,只有一个线程可以获取互斥锁,在释放互斥锁之前其他线程都不能获取该互斥锁。如果其他线程想要获取这个互斥锁,那么这个线程只能以阻塞方式进行等待。
条件锁:所谓的条件变量,某一个线程因为某个条件为满足时可以使用条件变量使改程序处于阻塞状态。一旦条件满足以“信号量”的方式唤醒一个因为该条件而被阻塞的线程。最为常见就是在线程池中,起初没有任务时任务队列为空,此时线程池中的线程因为“任务队列为空”这个条件处于阻塞状态。一旦有任务进来,就会以信号量的方式唤醒一个线程来处理这个任务。这个过程中就使用到了条件变量pthread_cond_t。
自旋锁:是一种busy-waiting的锁。也就是说,如果T1正在使用自旋锁,而T2也去申请这个自旋锁,此时T2肯定得不到这个自旋锁。与互斥锁相反的是,此时运行T2的处理器core2会一直不断地循环检查锁是否可用(自旋锁请求),直到获取到这个自旋锁为止。
读写锁:允许在数据库上同时执行多个“读”操作,但是某一时刻只能在数据库上有一个“写”操作来更新数据。
静态编译,就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。
与动态编译的区别
动态编译的可执行文件需要附带一个的动态链接库。在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。
野指针就是指针指向的位置是不可知的指针变量
1 什么是内存对齐?
“数据项仅仅能存储在地址是数据项大小的整数倍的内存位置上”。
2 为什么要内存对齐?
(1)硬件原因,一些硬件平台必须要求内存对齐,否则抛出异常;另外涉及到不同平台的移植问题。
(2)性能原因,对齐后访问效率更高。
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用。
内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,占用有用内存。
注:内存泄漏最终会导致内存溢出
解决:1.智能指针。因为智能指针可以自动删除分配的内存。智能指针和普通指针类似,只是不需要手动释放指针,而是通过智能指针自己管理内存的释放。
std::shared_ptr
ptra = std::make_shared (a); 2.养成良好习惯,保证new和delete匹配
内存碎片:空闲内存以小而不连续方式出现在不同的位置。
1>少用动态内存分配的函数(尽量使用栈空间)
2>分配内存和释放的内存尽量在同一个函数中
3>尽量一次性申请较大的内存2的指数次幂大小的内存空间,而不要反复申请小内存(少进行内存的分割)
4>使用内存池来减少使用堆内存引起的内存碎片
5>尽可能少地申请空间。
6>尽量少使用堆上的内存空间~
7>做内存池,也就是自己一次申请一块足够大的空间,然后自己来管理,用于大量频繁地new/delete操作。
迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。迭代器就如同一个指针。但是,迭代器不仅仅是指针,一个数组索引也可以认为是一种迭代器。
迭代器的失效问题:对容器的操作影响了元素的存放位置,称为迭代器失效。
失效情况:
1.当容器调用erase()方法后,当前位置到容器末尾元素的所有迭代器全部失效。
2.当容器调用insert()方法后,当前位置到容器末尾元素的所有迭代器全部失效。
3.如果容器扩容,在其他地方重新又开辟了一块内存。原来容器底层的内存上所保存的迭代器全都失效了。
4.不同容器的迭代器,是不能进行比较运算的。
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。
https://www.zhihu.com/question/20317448
JRE:Java Runtime Environment(java运行时环境)。即java程序的运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。
JDK:Java Development Kit(java开发工具包)。java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包括java源码的编译器javac、监控工具jconsole、分析工具jvisualvm等。
https://www.cnblogs.com/andy-zcx/p/5522836.html
垃圾回收算法一般要做两件基本事情:(1)发现无用的信息对象;(2)回收将无用对象占用的内存空间。使该空间可被程序再次使用。
https://blog.csdn.net/d6619309/article/details/53358250
https://blog.csdn.net/qq_15349687/article/details/82811581
java异常指在程序运行时可能出现的一些错误,如:文件找不到、网络连接失败、非法参数等。异常是一个事件,它发生在程序运行期间,中断了正在执行的程序的正常指令流。Java通过API中Throwable类的众多子类描述各种不同的异常。因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的错误条件。当条件生成时,错误将引发异常。
Java所有异常类都是 Throwable的子类。它包括Java异常处理的两个重要子类:Error和Exception.
Error:Error及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,是程序无法处理的错误,这类错误比较严重。这类的大多数错误与代码编写者执行的操作无关,如,运行代码时,JVM(Java虚拟机)出现的问题,例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。 这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。
Exception:可以通过捕捉处理使程序继续执行,是程序自身可以处理的异常,也称为非致命性异常类。根据错误发生原因可分为RuntimeException异常和除RunTimeException之外的异常,如IOException异常。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
捕捉异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
https://blog.csdn.net/cyl101816/article/details/67640843
1、final修饰符(关键字)。被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承。因此一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证他们在使用的过程中不被修改。被声明为final的变量必须在声明时给出变量的初始值,而在以后的引用中只能读取。被final声明的方法也同样只能使用,即不能方法重写。
【例】
public class finalTest{ final int a=6;//final成员变量不能被更改 final int b;//在声明final成员变量时没有赋值,称为空白final public finalTest(){ b=8;//在构造方法中为空白final赋值 } int do(final x){//设置final参数,不可以修改参数x的值 return x+1; } void doit(){ final int i = 7;//局部变量定义为final,不可改变i的值 } }
2、finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。
3、finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
https://blog.csdn.net/endlessseaofcrow/article/details/80318615
不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。
- 强引用(”Strong”Reference),我们平常典型编码 Object obj=newObject() 中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。当JVM 内存空间不足,JVM 宁愿抛出OutOfMemoryError运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,就是可以被垃圾收集的了,当然具体回收时机还是要看垃圾收集策略。
- 软引用(SoftReference),是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当JVM 认为内存不足时,才会去试图回收软引用指向的对象。JVM 会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。软引用可以和一个引用队(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。
应用场景:软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。
- 弱引用通过WeakReference类实现。弱引用的生命周期比软引用短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
应用场景:弱应用同样可用于内存敏感的缓存。
- 对于幻象引用,(虚引用),你不能通过它访问对象。幻象引用仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。虚引用只是用来得知对象是否被GC。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
应用场景:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知。
对象可达性状态流转分析:
Java定义的不同可达性级别(reachabilitylevel),具体如下:
强可达(StronglyReachable),就是当一个对象可以有一个或多个线程可以不通过各种引用访问到的情况。比如,我们新创建一个对象,那么创建它的线程对它就是强可达。
软可达(SoftlyReachable),就是当我们只能通过软引用才能访问到对象的状态。
弱可达(WeaklyReachable),类似前面提到的,就是无法通过强引用或者软引用访问,只能通过弱引用访问时的状态。这是十分临近finalize状态的时机,当弱引用被清除的时候,就符合finalize的条件了。
幻象可达(Phantom Reachable),上面流程图已经很直观了,就是没有强、软、弱引用关联,并且finalize过了,只有幻象引用指向这个对象的时候。
当然,还有一个最后的状态,就是不可达(unreachable),意味着对象可以被清除了。
synchronized;ReentrantLock;Semaphore;AtomicInteger
1.synchronized
在Java中synchronized关键字被常用于维护数据一致性。
synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是顺序的。
Java开发人员都认识synchronized,使用它来实现多线程的同步操作是非常简单的,只要在需要同步的对方的方法、类或代码块中加入该关键字,它能够保证在同一个时刻最多只有一个线程执行同一个对象的同步代码,可保证修饰的代码在执行过程中不会被其他线程干扰。使用synchronized修饰的代码具有原子性和可见性,在需要进程同步的程序中使用的频率非常高,可以满足一般的进程同步要求。
synchronized实现的机理依赖于软件层面上的JVM,因此其性能会随着Java版本的不断升级而提高。
需要说明的是,当线程通过synchronized等待锁时是不能被Thread.interrupt()中断的,因此程序设计时必须检查确保合理,否则可能会造成线程死锁的尴尬境地。
最后,尽管Java实现的锁机制有很多种,并且有些锁机制性能也比synchronized高,但还是强烈推荐在多线程应用程序中使用该关键字,因为实现方便,后续工作由JVM来完成,可靠性高。只有在确定锁机制是当前多线程程序的性能瓶颈时,才考虑使用其他机制,如ReentrantLock等。
2.ReentrantLock
可重入锁,顾名思义,这个锁可以被线程多次重复进入进行获取操作。
ReentantLock继承接口Lock并实现了接口中定义的方法,除了能完成synchronized所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。
Lock实现的机理依赖于特殊的CPU指定,可以认为不受JVM的约束,并可以通过其他语言平台来完成底层的实现。在并发量较小的多线程应用程序中,ReentrantLock与synchronized性能相差无几,但在高并发量的条件下,synchronized性能会迅速下降几十倍,而ReentrantLock的性能却能依然维持一个水准。
因此我们建议在高并发量情况下使用ReentrantLock。
ReentrantLock引入两个概念:公平锁与非公平锁。
公平锁指的是锁的分配机制是公平的,通常先对锁提出获取请求的线程会先被分配到锁。反之,JVM按随机、就近原则分配锁的机制则称为不公平锁。
ReentrantLock在构造函数中提供了是否公平锁的初始化方式,默认为非公平锁。这是因为,非公平锁实际执行的效率要远远超出公平锁,除非程序有特殊需要,否则最常用非公平锁的分配机制。
ReentrantLock通过方法lock()与unlock()来进行加锁与解锁操作,与synchronized会被JVM自动解锁机制不同,ReentrantLock加锁后需要手动进行解锁。为了避免程序出现异常而无法正常解锁的情况,使用ReentrantLock必须在finally控制块中进行解锁操作。
Lock lock = new ReentrantLock(); try { lock.lock(); //…进行任务操作5 } finally { lock.unlock(); }
3.Semaphore
上述两种锁机制类型都是“互斥锁”,学过操作系统的都知道,互斥是进程同步关系的一种特殊情况,相当于只存在一个临界资源,因此同时最多只能给一个线程提供服务。但是,在实际复杂的多线程应用程序中,可能存在多个临界资源,这时候我们可以借助Semaphore信号量来完成多个临界资源的访问。
Semaphore基本能完成ReentrantLock的所有工作,使用方法也与之类似,通过acquire()与release()方法来获得和释放临界资源。
经实测,Semaphone.acquire()方法默认为可响应中断锁,与ReentrantLock.lockInterruptibly()作用效果一致,也就是说在等待临界资源的过程中可以被Thread.interrupt()方法中断。
此外,Semaphore也实现了可轮询的锁请求与定时锁的功能,除了方法名tryAcquire与tryLock不同,其使用方法与ReentrantLock几乎一致。Semaphore也提供了公平与非公平锁的机制,也可在构造函数中进行设定。
Semaphore的锁释放操作也由手动进行,因此与ReentrantLock一样,为避免线程因抛出异常而无法正常释放锁的情况发生,释放锁的操作也必须在finally代码块中完成。
4.AtomicInteger
首先说明,此处AtomicInteger是一系列相同类的代表之一,常见的还有AtomicLong、AtomicLong等,他们的实现原理相同,区别在与运算对象类型的不同。
我们知道,在多线程程序中,诸如++i或i++等运算不具有原子性,是不安全的线程操作之一。通常我们会使用synchronized将该操作变成一个原子操作,但JVM为此类操作特意提供了一些同步类,使得使用更方便,且使程序运行效率变得更高。通过相关资料显示,通常AtomicInteger的性能是ReentantLock的好几倍。
https://blog.csdn.net/suifeng3051/article/details/52611233
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
https://blog.csdn.net/xyh269/article/details/52613507
https://www.cnblogs.com/hongten/p/hongten_java_sleep_wait.html
https://blog.csdn.net/ningxuezhu/article/details/8043537
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
ArrayList底层是数组结构,LinkList底层是链表结构。我们来说一下数组与链表的区别:
1.数组的定义:是相同数据类型的元素按照一定顺序的排列。也就是说,每一次对数组大小进行更改的时候,数组内部会进行重新排序,按照元素的下标顺序重新排序,数组下班是从0开始的。
2.链表的定义: 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
这里我们主要讲双向链表,双向链表中一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域,由这两部分组成的称之为节点,双向列表的节点是可以互相指向,前一个指向后一个,后一个也指向前一个。单向链表就是 从前往后,循环链表就是 首尾可以相连。
ArrayList和LinkList都是都是实现List接口,用来存储对象,并且都可以增删改查。但是:
对于ArrayList来说,因为是数组结构,在我们往执行add或者remove方法的时候,假如没有指定插入位置,这时候插入速度是可以的。但是,当我们指定了插入下标的位置,这时候插入完成以后要重新对位置进行排序,所以插入效率会受到影响。还有我们在new ArrayList的时候,要指定一下容量大小。否则,我们使用默认的初始容量(10),当我们插入九条的时候,就会进行扩容,每次扩容的大小为原来的1.5倍,扩容使用的是数组复制,这会对系统有一定开销。
对于LinkList来说,是链表结构。所以,不需要动态扩容。在插入和删除的时候,只需要改变指针方向即可。
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
- 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String
String最慢的原因:
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。以下面一段代码为例:
1 String str="abc"; 2 System.out.println(str); 3 str=str+"de"; 4 System.out.println(str);
如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。
另外,有时候我们会这样对字符串进行赋值
1 String str="abc"+"de"; 2 StringBuilder stringBuilder=new StringBuilder().append("abc").append("de"); 3 System.out.println(str); 4 System.out.println(stringBuilder.toString());
这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和
String str="abcde";
是完全一样的,所以会很快,而如果写成下面这种形式
1 String str1="abc"; 2 String str2="de"; 3 String str=str1+str2;
那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。
2. 再来说线程安全
在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
https://blog.csdn.net/rmn190/article/details/1492013
https://www.cnblogs.com/su-feng/p/6659064.html
String:适用于少量的字符串操作的情况。如常量的声明,少量的字符串操作(拼接,删除等)。
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况。不能使用String"+"来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况。如HTTP参数解析和封装等。
https://blog.csdn.net/u010482601/article/details/102464186
https://www.cnblogs.com/happyzm/p/6482886.html
https://blog.csdn.net/u014263388/article/details/52098719
1. 使用FileStreams复制:使用FileInputStream读取文件A的字节,使用FileOutputStream写入到文件B。
2. 使用FileChannel复制:Java NIO包括transferFrom方法,根据文档应该比文件流复制的速度更快。
3. 使用Commons IO复制:Apache Commons IO提供拷贝文件方法在其FileUtils类,可用于复制一个文件到另一个地方。它非常方便使用Apache Commons FileUtils类时,您已经使用您的项目。基本上,这个类使用Java NIO FileChannel内部。
4. 使用Java7的Files类复制:在Java 7中可以使用复制方法的Files类文件,从一个文件复制到另一个文件。
FileChannels拷贝大文件是最好的方法。
微服务,spring等
测试分析,测试计划,测试设计,用例设计,用例执行
单元测试,集成测试,系统测试,验收测试,回归测试
回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。
黑盒测试:已知产品的功能设计规格,可以进行测试证明每个实现了的功能是否符合要求。
白盒测试:已知产品的内部工作过程,可以通过测试证明每种内部操作是否符合设计规格要求,所有内部成分是否以经过检查。
常用的黑盒测试方法:等价类划分法、边界值分析法、正交实验设计法、因果图法、决策表法。
常用的白盒测试方法:(强度由弱到强)语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖、路径覆盖
语句覆盖:语句覆盖是最起码的结构覆盖要求,语句覆盖要求设计足够多的测试用例,使得程序中每条语句至少被执行一次。
判定覆盖:判定覆盖又称为分支覆盖,它要求设计足够多的测试用例,使得程序中每个判定至少有一次为真值,有一次为假值,即:程序中的每个分支至少执行一次。每个判断的取真、取假至少执行一次。
条件覆盖:条件覆盖要求设计足够多的测试用例,使得判定中的每个条件获得各种可能的结果,即每个条件至少有一次为真值,有一次为假值。
判定/条件覆盖:设计足够多的测试用例,使得判定中每个条件的所有可能结果至少出现一次,每个判定本身所有可能结果也至少出现一次。
组合覆盖:要求设计足够多的测试用例,使得每个判定中条件结果的所有可能组合至少出现一次。
路径覆盖:设计足够的测试用例,覆盖程序中所有可能的路径。
等价类划分法将程序所有可能的输入数据(有效的和无效的)划分成若干个等价类。然后从每个部分中选取具有代表性的数据当做测试用例进行合理的分类,测试用例由有效等价类和无效等价类的代表组成,从而保证测试用例具有完整性和代表性。
α测试
α测试是用户在开发环境下的测试,或者是开发内部的用户在模拟实际环境下的测试;
Alpha 测试(α测试)是由一个用户在开发环境下进行的测试,也可以是公司内部的用户在模拟实际操作环境下进行的受控测试,Alpha测试不能由程序员或测试员完成。Alpha测试发现的错误,可以在测试现场立刻反馈给开发人员,由开发人员及时分析和处理。目的是评价软件产品的功能、可使用性、可靠性、性能和支持。尤其注重产品的界面和特色。Alpha测试可以从软件产品编码结束之后开始,或在模块(子系统)测试完成后开始,也可以在确认测试过程中产品达到一定的稳定和可靠程度之后再开始。有关的手册(草稿)等应该在Alpha测试前准备好。
β测试
Beta测试是由软件的一个或多个用户在实际使用环境下进行的测试;
Beta测试(β测试)是软件的多个用户在一个或多个用户的实际使用环境下进行的测试。开发者通常不在测试现场,Beta测试不能由程序员或测试员完成。因而,Beta测试是在开发者无法控制的环境下进行的软件现场应用。在Beta测试中,由用户记下遇到的所有问题,包括真实的以及主管认定的,定期向开发者报告,开发者在综合用户的报告后,做出修改,最后将软件产品交付给全体用户使用。Beta测试着重于产品的支持性,包括文档、客户培训和支持产品的生产能力。只有当Alpha测试达到一定的可靠程度后,才能开始Beta测试。由于Beta测试的主要目标是测试可支持性,所以Beta测试应该尽可能由主持产品发行的人员来管理。
用例编号、用例名称、测试背景、前置条件、优先级、重要级、测试数据、测试步骤、预期结果、备注;(用例不包含实际结果
功能测试:
1.用户名正确/不正确 密码正确/不正确 组合 四种
2.用户名为空
3.密码为空
4.什么都不输入直接点击登录
5.用户名输入空格
6.密码输入空格
7.输入正确的用户名和密码 检查能否正常登录
8.登录按钮点击是否有效
9.登录后是否跳转到主页
10.双击登录按钮是否会发生错误
11.断网的情况下登录
12.弱网的情况下登录,如网络延迟过大,如果多少秒内服务器无响应,应该要有提示
界面:
1.用户名密码文本框是否对齐
2.用户名密码长度、高度是否合理
3.界面的文字是否正确,提示语是否简单易懂
4.颜色搭配、控件是否合理
兼容性:
1.IE6,7,8,9 Chrome Firefox Safari等浏览器界面显示是否正常 功能是否正常 注意移动设备的浏览器是否进行自适应
2.不同操作系统 Windows Mac 移动设备
安全:
1.用户名和密码是否加密后发送给服务器,一般用MD5加密 还有就是启用https
2.是否防止了sql注入 一般是对 ' “ 进行过滤就ok
3.同一账号是否允许不同机器同时登录
4.连续登错几次 会出现提示
性能:
1.访问登录界面,多长时间加载完整个界面
2.点击登录按钮后,几秒登录成功进入主界面
3.同时有100或者更多用户登录,系统响应时间
界面测试:
1. 购物车页面布局是否合理,显示是否完整
2. 鼠标浮动在购物车图标,迷你购物车界面显示是否正常
3. 不同店铺的商品在不同的区域正确显示
4. 页面的菜单功能栏正常显示,并链接正常
功能测试:
1. 页面的所有链接都功能正常,指向正确的页面
2. 页面关联的阿里旺旺的icon,点击后能打开本地软件,或使用网页窗口版阿里旺旺
3. 卖家在线时,旺旺icon高亮;反之,灰色
4. 添加商品后,购物车页面刷新后,能正确显示新的商品,且商品变灰色不能勾选
5. 已添加到购物车的商品,若已下架/库存不足,会提示宝贝已失效
6. 已添加到购物车的商品,若降价,会提示宝贝已降价
7. 同一件商品能否再次添加购物车(选择的商品属性相同和不同时)
8. 不需要的商品,可以删除,界面显示正常
9. 若登录,点击购物车,跳转至登陆界面;若已登录,点击购物车,跳转至购物车结算页面
10. 未勾选任何商品,结算按钮是灰色不可用状态
11. 商品可以全选、取消全选、任意勾选等
12. 勾选商品后,已选商品总价会显示,结算按钮高亮可用,点击结算后,进入确定订单信息页面
13. 可以修改已添加商品的属性信息,并保存成功
14. 可以修改商品数量,对于有限购的情况,选择的商品数量>上限时,会自动更改为上限值
15. 购物车有商品降价或库存紧张的,点击对应的tab,降价或库存紧张商品会归类显示;若没有,tab不可点击。
16. 购物车添加的商品种类有数量上限
17. 退出登录,再次登录,查看购物车,显示正常
性能测试:
1. 打开购物车,响应时间是否符合预期
可用性测试:
1. 复制、粘贴快捷键
2. 页面滑动操作正常
兼容测试:
1. 不同浏览器,测试功能是否正常
2. 不同移动终端,测试功能是否正常
考察测试思路:
①有个界面,有用户名、密码、提交。设计测试用例。我分了五个方面:正确/错误的用户名密码组合、特殊字符、网络状况、界面配色及易用程度、大数据高并发时怎么办。于是就问了我,怎样测试高并发的情况。
②这有一个水杯,设计测试用例,怎么测试。分了几个方面:性能(杯子漏不漏水、有没有盖子密封性怎样)、质量(是否耐高温、材料有毒与否、容量)、用户体验(易用否、是否美观)。