目录
测开面试复习文档
一:网络知识方面
基础的网络通信知识
各层的协议
IP 是无连接的
TCP/IP
短连接和长链接
● 请你说一说PC网络故障,以及如何排除障碍
HTTP和HTTPS的区别
GET 和 POST 的区别
TCP和UDP的区别、特点
三次握手
四次挥手:
请你说一下在浏览器中输入一个网址它的运行过程是怎样的?
请你说一说DNS解析过程
请你说一下为什么tcp可靠,哪些方法保证可靠
ARP协议
网页很卡的原因
二、数据库方面
数据的索引
索引的优点
索引的缺点
数据库的三大范式
关系型和非关系型数据库的区别
数据的操作(增删改查)
关系型数据库与NOSQL
MySQL中char、varchar和text三者的区别
查询借了“数据库”书籍的所有读者的的姓名
查询借书期限超过两个月的所有读者的姓名、所借书籍和借书日期
按读者姓名查询指定读者的借还书历史记录,假设读者姓名为刘勇。
三、Java语言
JAVA多线程开启的三种方式
Integer和int的区别
判断一个类是否“无用”,则需同时满足三个条件:
java类的加载:加载、链接、初始化
主方法
输入和输出
创建字符串
创建数组
数据类型转换
java的三大特性是:封装、继承、多态。
多态:
多态存在的三个必要条件
多态的实现方式
继承的注意事项:
l 继承中的成员方法关系:
抽象
序列化和反序列化
泄漏诊断
Java主动调用gc的方法
JAVA的反射机制
死锁
死锁发生原因
解决死锁的基本方法
死锁检测方法1、Jstack命令
四、数据结构和算法
堆栈空间分配区别:
堆栈缓存方式区别:
堆栈数据结构区别:
存储过程和触发器的区别有:
Hash表即散列表
单向链表反转的多种实现方法
数组和链表的区别
先、中、后序遍历
红黑树
现在有100W个账户密码,要存起来,要求查找时速度尽可能快,你选择什么数据结构?为什么?
查找方法和复杂度
冒泡排序
选择排序法
归并排序法
二分法查找
设计模式
设计模式的六大原则
五、测试常识
软件测试流程:
压力测试和负载测试
下面是依据的文档:
单元测试的策略:
设计用例的方法、依据有那些
单元测试、集成测试、系统测试
有效等价类和无效等价类
六、操作系统
对文件和目录进行操作的命令
pwd:显示工作目录路径
cd:更改工作目录路径
ls:列出目录和文件信息
touch:创建空文件、更改文件时间
mkdir:创建目录
cp:复制文件的和目录
mv:文件和目录改名、移动文件和目录路径
rm:删除文件或目录
wc:统计文件行数、单词数、字节数和字符数
du:计算机文件或目录的容量
linux系统显示的命令
uname:显示计算机及操作系统相关信息
hostname:显示或修改计算机主机名
free:查看内存信息
linux命令,找出关键字出现的次数
常用命令
进程和线程
Linux命令
显示终端上的所有进程
Linux杀死某个进程的方法
七、智力题
Cookie:记住身份信息,将我们在网站的行为记录号,自动登录账号,不用重新登录账号,可以将我们查的东西来推广告
Session:是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
Token:在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。
token其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。
Cache : 缓存之前浏览过的东西,数据被缓存到附近的主机,再次打开的时候,速度就很快。cdn
Host:接受请求的服务器地址,可以是IP或者是域名
状态码,100~199表示请求已收到继续处理,200~299表示成功,300~399表示资源重定向,400~499表示客户端请求出错,500~599表示服务器端出错
200:响应成功
302:跳转,重定向
400:客户端有语法错误
403:服务器拒绝提供服务
404:请求资源不存在
500:服务器内部错误
OSI七层模型:
TCP/IP:
数据链路层:ARP,RARP
网络层: IP,ICMP,IGMP
传输层:TCP ,UDP,UGP
应用层:Telnet,FTP,SMTP,SNMP.
OSI:
物理层:EIA/TIA-232, EIA/TIA-499, V.35, V.24, RJ45, Ethernet, 802.3, 802.5, FDDI, NRZI, NRZ, B8ZS
数据链路层:Frame Relay, HDLC, PPP, IEEE 802.3/802.2, FDDI, ATM, IEEE 802.5/802.2
网络层:IP,IPX,AppleTalk DDP
传输层:TCP,UDP,SPX
会话层:RPC,SQL,NFS,NetBIOS,names,AppleTalk,ASP,DECnet,SCP
表示层:TIFF,GIF,JPEG,PICT,ASCII,EBCDIC,encryption,MPEG,MIDI,HTML
应用层:FTP,WWW,Telnet,NFS,SMTP,Gateway,SNMP
HTTP: 80端口
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。Http协议是以明文方式发送信息的,如果黑客截取了Web浏览器和服务器之间的传输报文,就可以直接获得其中的信息。虽然HTTP本身是一个协议,但其最终还是基于TCP的。
HTTPS: 443 端口
是以安全为目标的Http通道,是Http的安全版。Https的安全基础是SSL。
SSL协议可分为两层:SSL记录协议(SSL Record Protocol),它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。SSL握手协议(SSL Handshake Protocol),它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
FTP是File Transfer Protocol,文件传输协议;简单说HTTP是面向网页的,而FTP是面向文件的。
TCP/IP 是用于因特网 (Internet) 的通信协议。计算机通信协议是对那些计算机必须遵守以便彼此通信的规则的描述。
TCP 使用固定的连接
TCP 用于应用程序之间的通信。
当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。
这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。
IP 用于计算机之间的通信。
IP 是无连接的通信协议。它不会占用两个正在通信的计算机之间的通信线路。这样,IP 就降低了对网络线路的需求。每条线可以同时满足许多不同的计算机之间的通信需要。
通过 IP,消息(或者其他数据)被分割为小的独立的包,并通过因特网在计算机之间传送。
IP 负责将每个包路由至它的目的地。
TCP/IP 意味着 TCP 和 IP 在一起协同工作。
TCP 负责应用软件(比如你的浏览器)和网络软件之间的通信。
IP 负责计算机之间的通信。
TCP 负责将数据分割并装入 IP 包,然后在它们到达的时候重新组合它们。
IP 负责将包发送至接受者。
在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
但从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。
域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用TCP和UDP端口53[1]。
首先是排除接触故障、使用ipconfig查看计算机的上网参数、使用ping命令测试网络的连通性、ping本机IP、ping网关
(1)首先是排除接触故障
即确保你的网线是可以正常使用的
(2)使用ipconfig查看计算机的上网参数
输入ipconfig,按Enter确认,可以看到机器的配置信息,输入ipconfig/all,可以看到IP地址和网卡物理地址等相关网络详细信息。
(3)使用ping命令测试网络的连通性,定位故障范围
ping 127.0.0.1,如显示”请求超时“,则表明本机网卡的安装或TCP/IP协议有问题,接下来就应该检查网卡和TCP/IP协议,卸载后重装即可。
(4)ping本机IP
可以检查本机的IP地址是否设置有误
(5)ping网关
如果不成功,可能是网关设备自身存在问题,也可能是本机上网参数设置有误,检查网络参数。
(6)Ping本地DNS地址
这样做是为了检查本地DNS服务器是否工作正常。
(7)Ping远程IP地址
可以检查本网或本机与外部的连接是否正常 。
采用TCP,一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大。
UDP对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成影响。
1、端口号:用来标识同一台计算机的不同的应用进程。
1)源端口:源端口和IP地址的作用是标识报文的返回地址。
2)目的端口:端口指明接收方计算机上的应用程序接口。
2、序号和确认号:是TCP可靠传输的关键部分。序号是本报文段发送的数据组的第一个字节的序号。在TCP传送的流中,每一个字节一个序号。e.g.一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。确认号即ACK,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。
3、数据偏移/首部长度:4bits。
4、保留:为将来定义新的用途保留,现在一般置0。
5、控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。
1)URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针。
2)ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。
3)PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
4)RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求。
5)SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。
6)FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。
6、窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小时一个16bit字段,因而窗口大小最大为65535。
7、校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。
8、紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。
9、选项和填充:最常见的可选字段是最长报文大小,又称为MSS
10、数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。四次挥手过程:
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
四次挥手原因:这是因为服务端的LISTEN状态下的
参考回答:
1、查询DNS,获取域名对应的IP。
(1)检查浏览器缓存、检查本地hosts文件是否有这个网址的映射,如果有,就调用这个IP地址映射,解析完成。
(2)如果没有,则查找本地DNS解析器缓存是否有这个网址的映射,如果有,返回映射,解析完成。
(3)如果没有,则查找填写或分配的首选DNS服务器,称为本地DNS服务器。服务器接收到查询时:
如果要查询的域名包含在本地配置区域资源中,返回解析结果,查询结束,此解析具有权威性。
如果要查询的域名不由本地DNS服务器区域解析,但服务器缓存了此网址的映射关系,返回解析结果,查询结束,此解析不具有权威性。
(4)如果本地DNS服务器也失效:
如果未采用转发模式(迭代),本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后,会判断这个域名(如.com)是谁来授权管理,并返回一个负责该顶级域名服务器的IP,本地DNS服务器收到顶级域名服务器IP信息后,继续向该顶级域名服务器IP发送请求,该服务器如果无法解析,则会找到负责这个域名的下一级DNS服务器(如http://baidu.com)的IP给本地DNS服务器,循环往复直至查询到映射,将解析结果返回本地DNS服务器,再由本地DNS服务器返回解析结果,查询完成。如果采用转发模式(递归),则此DNS服务器就会把请求转发至上一级DNS服务器,如果上一级DNS服务器不能解析,则继续向上请求。最终将解析结果依次返回本地DNS服务器,本地DNS服务器再返回给客户机,查询完成。
2、得到目标服务器的IP地址及端口号(http 80端口,https 443端口),会调用系统库函数socket,请求一个TCP流套接字。客户端向服务器发送HTTP请求报文:
(1)应用层:客户端发送HTTP请求报文。
(2)传输层:(加入源端口、目的端口)建立连接。实际发送数据之前,三次握手客户端和服务器建立起一个TCP连接。
(3)网络层:(加入IP头)路由寻址。
(4)数据链路层:(加入frame头)传输数据。
(5)物理层:物理传输bit。
3、服务器端经过物理层→数据链路层→网络层→传输层→应用层,解析请求报文,发送HTTP响应报文。
4、关闭连接,TCP四次挥手。
5、客户端解析HTTP响应报文,浏览器开始显示HTML
参考回答:
1、浏览器先检查自身缓存中有没有被解析过的这个域名对应的ip地址,如果有,解析结束。同时域名被缓存的时间也可通过TTL属性来设置。
2、如果浏览器缓存中没有(专业点叫还没命中),浏览器会检查操作系统缓存中有没有对应的已解析过的结果。而操作系统也有一个域名解析的过程。在windows中可通过c盘里一个叫hosts的文件来设置,如果你在这里指定了一个域名对应的ip地址,那浏览器会首先使用这个ip地址。
但是这种操作系统级别的域名解析规程也被很多黑客利用,通过修改你的hosts文件里的内容把特定的域名解析到他指定的ip地址上,造成所谓的域名劫持。所以在windows7中将hosts文件设置成了readonly,防止被恶意篡改。
3.如果至此还没有命中域名,才会真正的请求本地域名服务器(LDNS)来解析这个域名,这台服务器一般在你的城市的某个角落,距离你不会很远,并且这台服务器的性能都很好,一般都会缓存域名解析结果,大约80%的域名解析到这里就完成了。
4. 如果LDNS仍然没有命中,就直接跳到Root Server 域名服务器请求解析
5. 根域名服务器返回给LDNS一个所查询域的主域名服务器(gTLD Server,国际顶尖域名服务器,如.com .cn .org等)地址
6. 此时LDNS再发送请求给上一步返回的gTLD
7. 接受请求的gTLD查找并返回这个域名对应的Name Server的地址,这个Name Server就是网站注册的域名服务器
8. Name Server根据映射关系表找到目标ip,返回给LDNS
9. LDNS缓存这个域名和对应的ip
10. LDNS把解析的结果返回给用户,用户根据TTL值缓存到本地系统缓存中,域名解析过程至此结束
参考回答:
[1] 确认和重传机制
建立连接时三次握手同步双方的“序列号 + 确认号 + 窗口大小信息”,是确认重传、流控的基础
传输过程中,如果Checksum校验失败、丢包或延时,发送端重传。
[2] 数据排序
TCP有专门的序列号SN字段,可提供数据re-order
[3] 流量控制
滑动窗口和计时器的使用。TCP窗口中会指明双方能够发送接收的最大数据量,发送方通过维持一个发送滑动窗口来确保不会发生由于发送方报文发送太快接收方无法及时处理的问题。
[4] 拥塞控制
TCP的拥塞控制由4个核心算法组成:
“慢启动”(Slow Start)
“拥塞避免”(Congestion avoidance)
“快速重传 ”(Fast Retransmit)
“快速恢复”(Fast Recovery)
索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息。
索引的作用:
索引的一个主要目的就是加快检索表中数据,亦即能协助信息搜索者尽快的找到符合限制条件的记录ID的辅助数据结构。
通过建立索引可以极大地提高在数据库中获取所需信息的速度,同时还能提高服务器处理相关搜索请求的效率,从这个方面来看它具有以下优点 [1] :
在设计数据库时,通过创建一个惟一的索引,能够在索引和信息之间形成一对一的映射式的对应关系,增加数据的惟一性特点。
能提高数据的搜索及检索速度,符合数据库建立的初衷。
能够加快表与表之间的连接速度,这对于提高数据的参考完整性方面具有重要作用。
在信息检索过程中,若使用分组及排序子句进行时,通过建立索引能有效的减少检索过程中所需的分组及排序时间,提高检索效率。
建立索引之后,在信息查询过程中可以使用优化隐藏器,这对于提高整个信息检索系统的性能具有重要意义。
虽然索引的建立在提高检索效率方面具有诸多积极的作用,但还是存在下列缺点 [1] :
在数据库建立过程中,需花费较多的时间去建立并维护索引,特别是随着数据总量的增加,所花费的时间将不断递增。
在数据库中创建的索引需要占用一定的物理存储空间,这其中就包括数据表所占的数据空间以及所创建的每一个索引所占用的物理空间,如果有必要建立起聚簇索引,所占用的空间还将进一步的增加
在对表中的数据进行修改时,例如对其进行增加、删除或者是修改操作时,索引还需要进行动态的维护,这给数据库的维护速度带来了一定的麻烦。
第一范式:保证列的原子性,保证列不可再分。
第二范式:第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言),也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
第三范式:每列都与主键有直接关系而不是间接关系,不存在传递依赖;(限制一对多的关系,在从表中建立一个外键,通过外键来引用主表的信息)
PS:第二范式要遵循第一范式,第三范式要遵循第二范式。
1、不符合第一范式的例子(关系数据库中create不出这样的表):
表:姓名,性别,电话
问题:若某个人有两个电话,家庭电话和手机,这样则不符合第一范式。
解决:把电话列分成两个列即可。
2、不符合第二范式的例子:
表:学号, 姓名, 年龄, 课程名称, 成绩, 学分;
这个表明显说明了两个事务:学生信息, 课程信息,不符合第二范式。
存在问题:数据冗余,每条记录都含有相同信息。
解决:分成学生表和课程表分别存储即可。
3、不符合第三范式的例子:
学号, 姓名, 年龄, 所在学院, 学院联系电话,关键字为单一关键字"学号";
存在依赖传递: (学号) → (所在学院) → (学院地点, 学院电话)
存在问题:
数据冗余:有重复值;
解决:分成学生表,学院表即可。
1、数据存储方式不同。
关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。
与其相反,非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。非关系型数据通常存储在数据集中,就像文档、键值对或者图结构。你的数据及其特性是选择数据存储和提取方式的首要影响因素。
2、扩展方式不同。
SQL和NoSQL数据库最大的差别可能是在扩展方式上,要支持日益增长的需求当然要扩展。
要支持更多并发量,SQL数据库是纵向扩展,也就是说提高处理能力,使用速度更快速的计算机,这样处理相同的数据集就更快了。
因为数据存储在关系表中,操作的性能瓶颈可能涉及很多个表,这都需要通过提高计算机性能来客服。虽然SQL数据库有很大扩展空间,但最终肯定会达到纵向扩展的上限。而NoSQL数据库是横向扩展的。
而非关系型数据存储天然就是分布式的,NoSQL数据库的扩展可以通过给资源池添加更多普通的数据库服务器(节点)来分担负载。
3、对事务性的支持不同。
如果数据操作需要高事务性或者复杂数据查询需要控制执行计划,那么传统的SQL数据库从性能和稳定性方面考虑是你的最佳选择。SQL数据库支持对事务原子性细粒度控制,并且易于回滚事务。
1.插入数据:
(1)单行INSERT语句
INSERT INTO [表名] (字段1,字段2) VALUES (100,'51WINDOWS.NET')
(2)多行INSERT语句
INSERT INTO [表名] (字段1,字段2) SELECT (字段1,字段2) FROM [表名2] WHERE [条件]
2.删除数据:
DELETE FROM [表名] WHERE [字段名]>100
更新数据:
UPDATE [表名] SET [字段1] = 200,[字段2] = '51WINDOWS.NET' WHERE [字段三] =
\'HAIWA\'
3.查询数据:
select (字段1,字段2) from [表名] where [条件] order by [字段] desc;
4.删除表:
Drop table [表名]
5.新增字段:
ALTER TABLE [表名] ADD [字段名] NVARCHAR (50) NULL
6.删除字段:
ALTER TABLE [表名] DROP COLUMN [字段名]
7.修改字段:
ALTER TABLE [表名] ALTER COLUMN [字段名] NVARCHAR (50) NULL
8.删除表中的所有字段:
truncate table tb;
在MySQL中,char、varchar和text类型的字段都可以用来存储字符类型的数据,char、varchar都可以指定最大的字符长度,但text不可以。
它们的存储方式和数据的检索方式也都不一样。
数据的检索效率是:char > varchar > text
SELECT name
FROM reader
WHERE cardid IN
(SELECT cardid
FROM borrow
WHERE bookid IN
(SELECT bookid
FROM book
WHERE bookname = “数据库”))
SELECE name, bookname, bdate
FROM reader, book, borrow
WHERE book.bookid = borrow.bookid
AND book.cardid = borrow.cardid
AND MONTHS_BETWEEN(SYSDATE,bdate)>2
AND sdate IS NULL
SELECT bookname, bdate, sdate
FROM book, borrow, reader
WHERE book.bookid = borrow.bookid
AND borrow.cardid = reader.cardid
AND reader.name = “刘勇”
1、继承Thread类,新建一个当前类对象,并且运行其start()方法
2、实现Runnable接口,然后新建当前类对象,接着新建Thread对象时把当前类对象传进去,最后运行Thread对象的start()方法
3、实现Callable接口,新建当前类对象,在新建FutureTask类对象时传入当前类对象,接着新建Thread类对象时传入FutureTask类对象,最后运行Thread对象的start()方法
方法一代码:
public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.start(); // 启动新线程
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("start new thread!");
}
}
方法二代码:
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 启动新线程
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}
方法三代码:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableDemo {
public static void main(String[] args) throws Exception{
//参数表示此线程池里有多少可用的线程数量
ExecutorService es = Executors.newFixedThreadPool(3);
//提交线程任务,返回一个Future接口,参数为Callable实现类对象
Future future = es.submit(new MyThreadClass());
//获取返回值
String s = future.get();
System.out.println(s);
}
}
public class MyThreadClass implements Callable{
//V call() 由于该方法的返回值是个泛型V,所以具体的返回类型可以自己定义,下面返回值设置为了String,
@Override
public String call() throws Exception {
return "笑笑";
}
}
一:加载
(1)Java虚拟机将.class文件读入内存,并为之创建一个Class对象。
(2)任何类被使用时系统都会为其创建一个且仅有一个Class对象。
(3)这个Class对象描述了这个类创建出来的对象的所有信息,比如有哪些构造方法,都有哪些成员方法,都有哪些成员变量等。
二: 链接
链接包括验证、准备以及解析三个阶段。
(1)验证阶段。主要的目的是确保被加载的类(.class文件的字节流)满足Java虚拟机规范,不会造成安全错误。
(2)准备阶段。负责为类的静态成员分配内存,并设置默认初始值。
(3)解析阶段。将类的二进制数据中的符号引用替换为直接引用。
三:初始化
初始化,则是为标记为常量值的字段赋值的过程。换句话说,只对static修饰的变量或语句块进行初始化。
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
import java.util.scanner;
import java.util.*;
public class test{
public static void main(String args[]){
System.out.println("Hello, 欢迎学习JAVA");
}
}
Scanner sc = new Scanner(System.in); 构造一个输入对象
System.out.println("enter your name:"); 输出
String name = sc.nextLine(); 输入字符创
System.out.println("enter your age:");
int age = sc.nextInt(); 输入整数型数据
System.out.println("enter your occupation:");
Double occ = sc. nextDouble(); 输入双精度型数
1)通过引用字符数组来创建字符串
char a[]={'A','b','c','E'};
String str1=new String(a);
System.out.println(str1);
2)先定义后赋值
String str2;
str2="this is a book";
System.out.println(str2);
3)通过截取字符数组的一部分来创建字符串
char a3[]={'a','b','c','D','6','p'};
String str3=new String(a3,2,4);
System.out.println(str3);
4)通过类的实例化对象方法实例化一串字符来创建字符串
String str4=new String("this is a book");
System.out.println(str4);
一、声明并赋值
int[] arr = {1,2,4, …};
注意这里的花括号不是语句块,而且而且花括号后的分号也不能省
二、声明数组名开辟空间并且赋值
int[] arr;
arr = new int[]{1,2,3, …};
三、声明数组时指定元素个数然后赋值
int[] arr1= new int[3];
字符串转化为整形数组
String str="123456";
int[] a = new int[str.length()];
for(int i=0;i
a[i] = str.charAt(i)-'0';
}
字符串转化为字符数组
String str="123456";
char[] c = str.toCharArray() ;
System.out.println(c);
字符数组转化为字符串
char[] c = {'a','s','d','4','5',};
String str = new String(c);
System.out.println(str);
字符数组转化为整型数组
char[] c = { '1', '2', '3', '4', '5', };
int[] a = new int[c.length];
for (int i = 0; i < 5; i++) {
a[i] = c[i] - '0';
System.out.println(a[i]);
}
整型数转化为字符串
1.String str = Integer.toString(i); 将整数型转换为字符串
2.String s = String.valueOf(i); 这个可以将double类型转换为字符串
3.String s = "" + i;
字符串转化为整型数
1、int i = Integer.valueOf(str).intValue();
2、double b = Double.parseDouble(str); 将字符串转换为double型
3、int b = Integer.parseInt(str); 将字符串转换为int型
如果
String str1 = “ad45nfdf”;
则
str1.charAt(0) 是为 “a”;
str1.charAt(7)是为”f”;
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作
多态性是对象多种表现形式的体现。
现实中,比如我们按下 F1 键这个动作:
同一个事件发生在不同的对象上会产生不同的结果。
方式一:重写
方式二:接口
方式三:抽象类和抽象方法
1,使用关键字 extends 让类与类之间 产生继承关系
2, 父类私有的成员,子类不能继承,因为根本看不到
3,不能为了继承某个功能而随意进行继承操作, 必须要符合 is a 的关系
苹果 is a 水果
男人 is a 人
狗 is a 人 , 这种情况就不能继承了
l 继承中的成员变量关系:
不同名的变量:子类直接继承使用
同名的变量:默认访问的是子类自己的成员变量, 想访问父类中的同名变量,请使用 super.成员变量;
不同名的方法:子类直接继承使用
同名的方法:默认访问的是子类自己的成员方法,想访问父类中的同名方法,请使用 super.成员方法();
l super:用来表示当前对象中包含的父类对象空间的引用
调用父类的成员变量:
super.成员变量;
调用方法的成员方法:
super.成员方法();
l 方法重写(override):指 在子父类中,出现了方法声明相同的情况,也叫做方法覆盖,方法复写
l 方法重写的注意事项:
1、 子类的方法声明要与父类相同
2、子类要重写父类的方法,方法的权限修饰符不能比父类的更低
访问权限从高到低:public protected 默认(什么都不写) private
3, 父类私有的方法,子类不能够进行方法重写
方法重载(overload):指 在同一个类中,多个方法名称相同,它们的参数列表不同(个数不同,数据类型不同)
抽象方法: 方法只有声明部分,没有方法体
抽象类: 包含抽象方法的类,一定是抽象类
使用 abstract 修饰的类,是抽象类
抽象类的特点:
1,抽象类与抽象方法都必须使用 abstract来修饰
2,抽象类不能直接创建对象
3,抽象类中可以有抽象方法,也可以没有抽象方法
4,抽象类的子类
a,实现了抽象方法的具体类
b,抽象类
抽象类面试题:
1,抽象类中是否可以没有抽象方法?如果可以,那么,该类还定义成抽象类有意义吗?为什么?
可以没有抽象方法,有意义,不会让其他人直接创建该类对象
抽象类总结
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
JAVA 接口和抽象类的区别
本质:从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范
区别:
1.接口的方法默认是public,所有方法在接口中不能有实现,抽象类可以有非抽象的方法
2.接口中的实例变量默认是final类型的,而抽象类中则不一定
3.一个类可以实现多个接口,但最多只能实现一个抽象类
4.一个类实现接口的话要实现接口的所有方法,而抽象类不一定
5.接口不能用new实例化,但可以声明,但是必须引用一个实现该接口的对象
参数 |
抽象类 |
接口 |
默认的方法实现 |
它可以有默认的方法实现 |
接口完全是抽象的。它根本不存在方法的实现 |
实现 |
子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 |
子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 |
抽象类可以有构造器 |
接口不能有构造器 |
与正常Java类的区别 |
除了你不能实例化抽象类之外,它和普通Java类没有任何区别 |
接口是完全不同的类型 |
访问修饰符 |
抽象方法可以有public、protected和default这些修饰符 |
接口方法默认修饰符是public。你不可以使用其它修饰符。 |
main方法 |
抽象方法可以有main方法并且我们可以运行它 |
接口没有main方法,因此我们不能运行它。 |
多继承 |
抽象方法可以继承一个类和实现多个接口 |
接口只可以继承一个或多个其它接口 |
速度 |
它比接口速度要快 |
接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 |
如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 |
如果你往接口中添加方法,那么你必须改变实现该接口的类。 |
在大多数情况下,诊断内存泄漏需要非常详细地了解相关应用程序。警告:该过程可能很长并且是迭代的。
我们寻找内存泄漏的策略将相对简单:
3.1 识别症状
正如所讨论的,在许多情况下,Java进程最终会抛出一个OOM运行时异常,这是一个明确的指示,表明您的内存资源已经耗尽。在这种情况下,您需要区分正常的内存耗尽和泄漏。分析OOM的消息并尝试根据上面提供的讨论找到罪魁祸首。
通常,如果Java应用程序请求的存储空间超过运行时堆提供的存储空间,则可能是由于设计不佳导致的。例如,如果应用程序创建映像的多个副本或将文件加载到数组中,则当映像或文件非常大时,它将耗尽存储空间。这是正常的资源耗尽。该应用程序按设计工作(虽然这种设计显然是愚蠢的)。
但是,如果应用程序在处理相同类型的数据时稳定地增加其内存利用率,则可能会发生内存泄漏。
3.2 启用详细垃圾收集
断言确实存在内存泄漏的最快方法之一是启用详细垃圾回收。通常可以通过检查verbosegc输出中的模式来识别内存约束问题。
具体来说,-verbosegc参数允许您在每次垃圾收集(GC)过程开始时生成跟踪。也就是说,当内存被垃圾收集时,摘要报告会打印到标准错误,让您了解内存的管理方式。
此GC跟踪文件中的每个块(或节)按递增顺序编号。要理解这种跟踪,您应该查看连续的分配失败节,并查找随着时间的推移而减少的释放内存(字节和百分比),同时总内存(此处,19725304)正在增加。这些是内存耗尽的典型迹象。
3.3 启用分析
不同的JVM提供了生成跟踪文件以反映堆活动的不同方法,这些方法通常包括有关对象类型和大小的详细信息。这称为分析堆。
3.4 分析路径
本文重点介绍Java VisualVM生成的跟踪。跟踪可以有不同的格式,因为它们可以由不同的Java内存泄漏检测工具生成,但它们背后的想法总是相同的:在堆中找到不应该存在的对象块,并确定这些对象是否累积而不是释放。特别感兴趣的是每次在Java应用程序中触发某个事件时已知的临时对象。应该仅存少量,但存在许多对象实例,通常表示应用程序出现错误。
System.gc();
// 或者下面,两者等价
Runtime.getRuntime().gc();
一 反射机制的概念:
指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法.这种动态获取信息,以及动态调用对象方法的功能叫java语言的反射机制.
二 反射机制的应用:
生成动态代理,面向切片编程(在调用方法的前后各加栈帧).
三 反射机制的原理:
1 首先明确的概念: 一切皆对象----类也是对象.
2 然后知道类中的内容 :modifier constructor field method.
3 其次明白加载: 当Animal.class在硬盘中时,是一个文件,当载入到内存中,可以认为是一个对象,是java.lang.class的对象.
所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。
a. 竞争资源
b. 进程间推进顺序非法
预防死锁:
资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
避免死锁:
预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全的状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。
银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此,状态包含两个向量Resource(系统中每种资源的总量)和Available(未分配给进程的每种资源的总量)及两个矩阵Claim(表示进程对资源的需求)和Allocation(表示当前分配给进程的资源)。安全状态是指至少有一个资源分配序列不会导致死锁。当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程知道同意该请求后系统状态仍然是安全的。
检测死锁
首先为每个进程和每个资源指定一个唯一的号码;
然后建立资源分配表和进程等待表。
解除死锁:
当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:
剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。 Jstack工具可以用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
2、JConsole工具
Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。
1、栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
2、堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
1、栈使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放;
2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。
存储过程可以采用输入参数而触发器不可以;存储过程可以返回零或n值而触发器无法返回值,存储过程中可以使用事务,而触发器不允许
其最突出的优点是查找和插入删除具有常数时间的复杂度
其实现原理是:把Key通过一个固定的算法函数即所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的
方法一:头节点插入法
实现步骤:
创建一个带头节点的链表resultList
定义一个循环链表变量p,p的初始值为待反转的链表
遍历待反转的链表,遍历条件为 (p !=null)
3.1 定义一个临时链表变量tempList保存p->next的值(因为p->next值会改变),用于下一次循环。
3.2 把p当前指向的首节点和resultList链表的头节点之后的节点拼接起来。
3.3 把3.2步骤中拼接的节点 再拼接到resultList头节点后。
3.4 p重新赋值为3.1步骤中保存的tempList的值。
返回resultList->next.即反转后的链表。
方法二:就地反转
实现步骤
创建一个带头节点的链表resultList,头结点指向待反转的链表。
创建p、pNext两个用于循环操作,分别指向两个待反转节点的位置,初始值如图所示,指向1和2
遍历带反转的链表,循环条件是pNext!=null.
3.1 从链表中分割出pNext节点,也就是让p指向pNext->next。
3.2 让pNext指向经过3.1操作之后的resultList.next(1->3->4->5)
3.3 让resultList头结点指向pNext(2->1->3->4->5)
3.4 让pNext指向p的下一个节点
难点在于理解循环中resultList.next指向性的变化,以及p和pNext两个变量的变化,p指向的链表首结点永远是1,只是节点1在resultList链表中位置在发生变化,而pNext是随着p移动的,脑子中间可以有一个摆绳模型,在起始点位置发力,绳子的高点位置会移到绳尾,那个最高点就是p变量位置。
我们知道遍历数组和链表的时间复杂度是O(n),但是在实际中确实数组的速度要比链表快,这是为什么呢?
1.首先,数组是具有相同的数据类型且按一定次序排列的一组变量的集合体,构成一个数组的这些变量称为数组元素
数组在内存中的地址是连续相邻的,而链表在内存的地址是散列的,不连续的
2. CPU缓存会把一片连续的内存空间读入, 因为数组结构是连续的内存地址, 所以数组全部或者部分元素被连续存
在CPU缓存里面,而链表的节点是分散在堆空间里面的,这时候CPU缓存帮不上忙,只能是去读取内存,
而缓存的速率要比内存快。
3. CPU --》寄存器--》缓存 --》内存
cpu 取数据,处理数据,都要放到寄存器中处理(存放指令),缓存就是吧内存中提取的数据暂时保存在里面。
如果寄存器要获取内存中同一位置的数据,就从缓存中获取,如果寄存器获取的不是同一个内存地址的数
据(或者获取的内存地址缓存中不存在),就从内存中查找获取
从上述比较中,我们可以看出数组的查询,要比链表的快
参考回答:
选择hash_map,因为其查找速度与数据量基本无关,是常数级别,但是对空间的要求很高,所以是已空间换时间
查找方法 |
平均查找长度 |
适用范围 |
|
顺序查找 |
(n+1)/2 |
|
|
折半查找 |
Log2(n+1)-1 |
有序表 |
|
静态树表查找 |
O(nlogn) |
|
|
二叉查找树 |
0(log2n) |
|
|
平衡二叉树 |
|
|
|
1、最简单的排序方法(直接插入排序法)
排序方法 |
复杂度 |
最大移动次数 |
最坏的情况(比较次数) | 最好情况(比较次数) |
|
直接插入排序法 |
|
(n-1)(n+4)/2 |
(n-1)(n+2)/2 | n-1 | 最简单的排序方法 |
冒泡排序法 |
|
|
n-1 |
|
|
快速排序法 |
|
|
O(n*log2n) |
|
|
希尔排序法 |
O(n²) |
|
|
||
直接选择排序法 |
O(n²) |
3(n-1) |
均为n(n-1)/2 |
|
|
折半插入排序法 |
O(n²) |
|
均是 n*log2n(2为底) |
|
/*冒泡排序 */
1 public class BubbleSort{
2 public static void main(String[] args){
3 int score[] = {67, 69, 75, 87, 89, 90, 99, 100};
4 for (int i = 0; i < score.length -1; i++){ //最多做n-1趟排序
5 for(int j = 0 ;j < score.length - i - 1; j++){ //对当前无序区间score[0......length-i-1]进行排序
6 if(score[j] < score[j + 1]){ //把小的值交换到后面
7 int temp = score[j];
8 score[j] = score[j + 1];
9 score[j + 1] = temp;
10 }
11 }
12 System.out.print("第" + (i + 1) + "次排序结果:");
13 for(int a = 0; a < score.length; a++){
14 System.out.print(score[a] + "\t");
15 }
16 System.out.println("");
17 }
18 System.out.print("最终排序结果:");
19 for(int a = 0; a < score.length; a++){
20 System.out.print(score[a] + "\t");
21 }
22 }
23 }
public class afrr4{
public static void main(String[] args){
int score[] = {67, 69, 75, 87, 89, 90, 99, 100,8,9,45,98};
System.out.print("原始的序列:"+"\t");
for(int b = 0; b < score.length; b++){
System.out.print(score[b] + "\t");
}
System.out.println("");
for (int i = 0; i < score.length; i++){ //最多做n-1趟排序
for(int j = i+1 ;j < score.length; j++){ //对当前无序区间score[0......length-i-1]进行排序(j的范围很关键,这个范围是在逐步缩小的)
if(score[i] < score[j]){ //把小的值交换到后面,也就是把大的值放在第i位
int temp = score[j];
score[j] = score[i];
score[i] = temp;
}
}
System.out.print("第" + (i + 1) + "次排序结果:");
for(int a = 0; a < score.length; a++){
System.out.print(score[a] + "\t");
}
System.out.println("");
}
System.out.print("最终排序结果:");
for(int a = 0; a < score.length; a++){
System.out.print(score[a] + "\t");
}
}
}
import org.junit.Test;
public class MergeSort {
//两路归并算法,两个排好序的子序列合并为一个子序列
public void merge(int []a,int left,int mid,int right){
int []tmp=new int[a.length];//辅助数组
int p1=left,p2=mid+1,k=left;//p1、p2是检测指针,k是存放指针
while(p1<=mid && p2<=right){
if(a[p1]<=a[p2])
tmp[k++]=a[p1++];
else
tmp[k++]=a[p2++];
}
while(p1<=mid) tmp[k++]=a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while(p2<=right) tmp[k++]=a[p2++];//同上
//复制回原素组
for (int i = left; i <=right; i++)
a[i]=tmp[i];
}
public void mergeSort(int [] a,int start,int end){
if(start
public static int search(int[] arr, int key) {
int start = 0;
int end = arr.length - 1;
while (start <= end) {
int middle = (start + end) / 2;
if (key < arr[middle]) {
end = middle - 1;
} else if (key > arr[middle]) {
start = middle + 1;
} else {
return middle;
}
}
return -1;
}
号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 |
|
2 | 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 |
|
3 | 行为型模式 这些设计模式特别关注对象之间的通信。 |
|
4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 |
|
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
1、需求分析 2、软件测试计划(写测试计划书) 3、软件测试设计阶段(写测试用例) 4、软件测试执行阶段 5、评估阶段(出具测试报告)
软件在一些超负荷情况下的运行情况属于压力测试,或者说属于负载测试应该也是合理的。而压力测试和负载测试属于性能测试,所以选B
压力测试是通过逐步增加系统负载的方式来测试系统性能的变化,最终确定在什么负载条件下系统性能处于失效状态
单元测试,详细设计文档
集成测试,概括设计文档
系统测试,系统设计文档
SOW:statement of work,工作任务说明书
HLD: High Level Design,概要设计说明书
LLD: Low Level Design,详细设计说明书
UTC: Unit Testing Cases,单元测试用例
等值分析测试=等价类划分+边界值分析测试
无效等价类是指对于软件规格说明而言,是没有意义的、不合理的输入数据集合。利用无效等价类可以找出程序异常说明情况,检查程序的功能和性能的实现是否有不符合规格说明要求的地方。
有效等价类是指输入数据完全满足程序输入的规格说明,是有效、有意义的输入数据所构成的集合。利用有效等价类可以检验程序是否满足规格说明所规定的功能和性能。
命令语法:pwd [选项]
选项 | 含义 |
---|---|
-L | 目录链接时,输出链接路径 |
-P | 输出物理路径 |
命令语法:cd[选项][目录]
选项 | 含义 |
---|---|
cd或cd ~ | 回到用户主目录 |
cd .. | 变当前工作目录路径位置至当前目录的父目录。 |
cd -P | 如果是链接路径,则进入链接路径的源物理路径 |
cd ~li | 改用户工作目录路径位置至用户li的主目录。 |
选项 | 选项含义 |
---|---|
-a | 显示指定目录下所有子目录的文件,包括隐藏文件 |
-A | 显示指定目录下所有子目录与文件,包括隐藏文件,但不列出“.”和“..” |
-c | 配合-lt:根据车体么排序并显示ctime |
-d | 如果参数是目录,只显示其名称而不显示其下的歌文件和子目录 |
-F | 显示文件类型 |
-i | 在输出的第一列显示文件的inode号 |
-l | 以长格式显示文件的详细信息 |
-r | 逆序排列 |
-t | 根据修改时间排序 |
-s | 一块数形式先生每个文件分配的尺寸 |
-S | 根据文件大小排序 |
例子:显示目录/var下文件的子目录的简单信息
~]# ls /var
显示/root目录下所以文件和子目录的详细信息,包括隐藏文件
~]# ls -al /root
显示/etc目录下的文件和子目录信息,用标记标出文件类型
~]# ls -F /etc
命令语法:touch [选项] [文件]
选项 | 选项含义 |
---|---|
-a | 只更改访问时间(atime) |
-m | 更改文件的修改时间记录(mtime) |
-c | 假如目标文件存在,则不会建立新的文件 |
-r<文件> | 使用指定文件的时间属性而非当前时间 |
-d<字符串> | 使用指定字符串表示时间而非当前时间 |
-t<日期时间> | 使用[CC]YY]MMDDhhmm[.ss]格式的时间而非当前时间 |
例如:在当前目录下创建文件file1、file2、file3
[root@localhost ~]# touch file1
[root@localhost ~]# touch file2 file3
[root@localhost ~]# ls -l file1 file2 file3
-rw-r--r--. 1 root root 5 Aug 3 22:12 file1
-rw-r--r--. 1 root root 6 Aug 3 22:12 file2
-rw-r--r--. 1 root root 0 Aug 3 22:12 file3
将文件file1的时间记录改为9月17日19点30分
[root@localhost ~]# ls -l /root/file1
-rw-r--r--. 1 root root 5 Aug 4 2019 /root/file1
[root@localhost ~]# touch -c -t 09171930 /root/file1
[root@localhost ~]# ls -l /root/file1
-rw-r--r--. 1 root root 5 Sep 17 2019 /root/file1
时间格式是MMDDHHmm,如果要加上2019年年份
[root@localhost ~]# touch -c -t 09171930 /root/file1
命令语法:mkdir [选项] [目录]
选项 | 选项含有 |
---|---|
-m<权限模式> | 对新创建的目录设置权限,在没有-m选项时,默认权限是755 |
-v | 每次创建新目录都显示信息 |
-p | 可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动创建那些尚不存在的目录,即以此可以建立多个目录 |
例子:创建目录newdir1 ,其默认权限为755
[root@localhost ~]# touch -c -t 09171930 /root/file1
[root@localhost ~]# ls -ld newdir1
drwxr-xr-x. 2 root root 6 Jun 22 22:27 newdir1
创建目录newdir2,其权限为777
[root@localhost ~]# mkdir -m 777 newdir2
[root@localhost ~]# ls -ld newdir2
drwxrwxrwx. 2 root root 6 Aug 3 22:39 newdir2
rmdir:删除空目录 命令语法:rmdir [选项] [目录]
选项 | 选项含义 |
---|---|
-p | 递归删除目录,当子目录删除后其父目录为空时,也一同被删除 |
-v | 输出处理的目录详情 |
例子:同时删除/root/newdir2和/root/newdir2/newdir3这两个空目录
[root@localhost ~]# mkdir /root/newdir2
[root@localhost ~]# mkdir /root newdir2/newdir3
[root@localhost ~]# rmdir -pv /root/newdir2/newdir3
rmdir: removing directory, ‘/root/newdir2/newdir3’
rmdir: removing directory, ‘/root/newdir2’
rmdir: removing directory, ‘/root’
rmdir: failed to remove directory ‘/root’: Device or resource busy
命令语法:cp [选项] [源文件|目录] [目标文件|目录]
选项 | 选项含义 |
---|---|
-a | 在复制目录时保留链接、文件属性、并递归地复制目录,等同于-dpr选项 |
-d | 复制时保留链接 |
-f | 在覆盖目标文件之前不给出提示信息要求用户确认 |
-i | 和-f选项相反看,在覆盖目标文件之前给出提示信息,要求用户确认 |
-p | 出复制源文件的内容外,还把其修改时间和访问权限也复制到新文件中 |
-l | 不做复制,只是链接文件 |
-r | 如果给出的源文件是一个目录文件,将递归复制该目录下所有的子目录和文件。此时目标必须为一个目录名 |
例子:将/etc/grub2.cfg文件复制到/root目录下,并改名为grub
[root@localhost ~]# cp /etc/grub2.cfg /root/grub
cp: overwrite ‘/root/grub’? y
[root@localhost ~]# ls
anaconda-ks.cfg grub newdir1
将/etc/grub2.cfg文件复制到/root目录下
[root@localhost ~]# cp /etc/grub2.cfg /root
[root@localhost ~]# ls
anaconda-ks.cfg grub grub2.cfg newdir1
[root@localhost ~]#
将/boot目录以及该目录中的所有文件和子目录都复制到/root目录中
[root@localhost ~]# cp -r /boot /root
[root@localhost ~]# ls -l /root
total 24
-rw-------. 1 root root 1260 Jun 9 14:21 anaconda-ks.cfg
dr-xr-xr-x. 5 root root 4096 Jun 22 23:21 boot
-rw-r--r--. 1 root root 4287 Jun 22 23:15 grub
-rw-r--r--. 1 root root 4287 Jun 22 23:18 grub2.cfg
drwxr-xr-x. 2 root root 6 Jun 22 22:27 newdir1
命令语法:mv [选项] [源文件|目录] [目标文件|目录]
选项 | 选项含义 |
---|---|
-i | 覆盖前询问 |
-f | 覆盖前不询问 |
-n | 不覆盖已存在的文件 |
-u | 只有在源文件文件比目标文件新,或目标文件不存在时才进行移动 |
-T | 将目标文件视作普通文件处理 |
例子:将/root/pic目录下所以的后缀名为“.png”的文件移动到/usr/local/share/pic目录下
[root@localhost ~]# mv -f /root/pic/*.png /usr/local/share/pic
把/root/pic/kpic.png文件改名为/root/pic/life.png
[root@localhost ~]# mv /root/pic/kpic.png /root/pic/life.png
[root@localhost ~]# ls /root/pic
{kpic.png life.png
把/root/pic目录名称更改为/root/mypic
root@localhost ~]# mv /root/pic /root/mypic
[root@localhost ~]# ls /root
123.png} anaconda-ks.cfg boot grub grub2.cfg mypic newdir1
命令语法:rm [选项] [文件|目录]
选项 | 选项含义 |
---|---|
-f | 强制删除。忽略不存在的文件,不给出提示信息 |
-r | 递归删除目录及其内容 |
-i | 在删除前需要确认 |
例子:删除当前目录下的file4文件
root@localhost ~]# rm file4
rm: remove regular empty file ‘file4’? y
[root@localhost ~]# ls
123.png} anaconda-ks.cfg boot grub grub2.cfg mypic newdir1
连同/root/ab/a文件和/root/ab目录一起删除
[root@localhost ~]# mkdir /root/ab
[root@localhost ~]# touch /root/ab/a
[root@localhost ~]# ls -l /root/ab/a
-rw-r--r--. 1 root root 0 Jun 22 23:51 /root/ab/a
[root@localhost ~]# rm -rf /root/ab
[root@localhost ~]# ls /root
123.png} anaconda-ks.cfg boot grub grub2.cfg mypic newdir1
命令语法:wc [选项] [文件]
选项 | 选项含义 |
---|---|
-l | 统计行数 |
-w | 统计单词数 |
-c | 统计字节数 |
-m | 统计字符数 |
-L | 统计文件中最长行的长度 |
例子:统计/root/aa文件的行数、单词数和字节数
[root@localhost ~]# wc -l /root/aa/ce.log
3 /root/aa/ce.log
统计/root目录下有多少子目录和文件
[root@localhost ~]# ls /root|wc -l
8
命令语法:du [选项]...[文件或目录]...
选项 | 选项含义 |
---|---|
-h | 人性化显示容量信息 |
-a | 查看所有目录以及文件的容量信息 |
-s | 仅显示总容量 |
-c | 显示总计信息 |
-l | 如果是硬连接,就多次计算其尺寸 |
-x | 跳过处于不同文件系统之上的目录 |
-S | 不包括子目录的占用量 |
-L | 找出任何符号链接指示的真正目的地 |
查看/root目录及子目录的容量信息
[root@localhost ~]# du /root
[root@localhost ~]# du -a /root
查看/root所占磁盘空间总和
[root@localhost ~]# du -sh /root
100M /root
以MB为单位显示/root目录磁盘占用量
[root@localhost ~]# du -sh /root
100M /root
选项 | 选项含义 |
---|---|
-h | 人性化显示容量信息 |
-a | 查看所有目录以及文件的容量信息 |
-s | 仅显示总容量 |
-c | 显示总计信息 |
-l | 如果是硬连接,就多次计算其尺寸 |
-x | 跳过处于不同文件系统之上的目录 |
-S | 不包括子目录的占用量 |
-L | 找出任何符号链接指示的真正目的地 |
查看/root目录及子目录的容量信息
[root@localhost ~]# du /root
[root@localhost ~]# du -a /root
查看/root所占磁盘空间总和
[root@localhost ~]# du -sh /root
100M /root
以MB为单位显示/root目录磁盘占用量
[root@localhost ~]# du -sh /root
100M /root
命令语法:uname [选项]
选项 | 选项含义 |
---|---|
-a | 显示全部的信息 |
-m | 显示计算机硬件架构名称 |
-n | 显示在网络上的主机名称 |
-r | 显示操作系统的内核发行号 |
-s | 显示操作系统名称 |
例子:显示操作系统的内核发行号
[root@localhost ~]# uname -r
3.10.0-957.el7.x86_64
显示操作系统的全部信息
[root@localhost ~]# uname -a
Linux localhost.localdomain 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
命令语法:
hostname [选项] [主机名|-F <文件>]
hostname [选项]
选项 | 选项含义 |
---|---|
-s | 显示短主机名 |
-i | 显示IP地址 |
-f | 显示长主机名 |
-d | 显示DNS |
例子:显示当前计算机主机名
[root@localhost ~]# hostname
localhost.localdomain
设置当前计算机的主机名为linux[1]
[root@localhost ~]# hostname linux
[root@localhost ~]# hostname
linux
命令语法:free [选项]
选项 | 选项含义 |
---|---|
-c <次数> | 显示指定次数结果数据 |
-t | 显示内存加上swap总的容量 |
-b | 以字节为单位显示内存使用情况 |
-k | 以KB为单位显示内存使用情况 |
-m | 以MB为单位显示内存使用情况 |
-g | 以GB为单位显示内存使用情况 |
例子:查看系统物理内存和swap使用情况
[root@localhost ~]# free
total used free shared buff/cache available
Mem: 995896 148252 403780 7828 443864 621672
Swap: 2097148 0 2097148
显示系统的物理内存加上swap总的容量
[root@localhost ~]# free -t
total used free shared buff/cache available
Mem: 995896 147960 404052 7828 443884 621964
Swap: 2097148 0 2097148
Total: 3093044 147960 2501200
grep NullPointerException task-hbase-transform.log|wc -l
。grep 'objStr1\|objStr2' filename|wc -l
#直接用 | 链接起来即可。 ps -a
1.定位进程
ps -aux | grep ***
2.杀死进程
我们可以通过 进程的名字和进程的ID(PID)来结束进程。
结束命令:
kill:通过进程ID来结束进程
killall:通过进程名字结束进程
最常使用的结束进程的信号是:
Signal Name |
Single Value | Effect |
SIGHUP | 1 | 挂起 |
SIGINT | 2 | 键盘的中断信号 |
SIGKILL | 9 | 发出杀死信号 |
SIGTERM | 15 | 发出终止信号 |
SIGSTOP | 17, 19, 23 | 停止进程 |
我们可以通过Single Value的值来代替信号的名字。所以我们现在来杀死python进程:
kill SIGNAL PID
SIGNAL 是要发送的信号,PID是进程号。
kill -9 14992
上面的命令就是杀死python进程的。如果有多个python程序在运行,想要全部结束的话,可以
killall -9 python
1、10个堆,每堆10个苹果,其中9个堆里苹果是50g/个,一个堆里苹果是40g/个,有一杆秤只能称一次,所称重量为x,求40g苹果所在堆。
2、5L和6L水桶,得到三升水。
3、两个一小时蚊香怎么得到15分钟的记时
4、4分钟沙漏和7分钟沙漏怎么漏出9分钟
5、八个球,其中有一个是其余球重量的1.5倍,有什么方案找出来
6、桌上100个球,每次可以拿一到五个, 现在我们两个人依次拿球,你先拿,使用怎样的拿球策略,可以使你最终能拿到最后一个球?
7、有10个石头,每人每次可以拿1-2个,轮流拿,最后一个拿的人算输,有什么必赢的方案。
8、一亿数据获取前1000个最大值 https://zhuanlan.zhihu.com/p/73233544
9、经典智力题:飞机加油问题