【面试】三七互娱java游戏开发二面准备

java的基本数据类型

int32 short16 long64 float32 double64 char16 byte8 boolean1位

说一下String

String是java中的一个类,他的底层实现是一个char数组,String一旦创建大小就不能再改变,如果要改变String的值,只能重新创建一个String类并把修改后的char数组copy到新建String中

为什么String不可变呢,因为底层数组char[]被private final修饰,且没有对外提供set方法,因此不可变

不可变的好处:不可变对象更容易被构造、测试、使用

真正不可变对象都是线程安全的

对象变化的问题得到了避免

用过哪些数据结构或者集合的类

list:

    arraylist:底层实现是数组,适合随机查询,线程不安全

    linkedlist:底层实现是链表,方便增删,适合大量数据操作,线程不安全

    vector:和arraylist的区别是vector是线程安全的,因为vector中的方法都被同步了,多线程无法同时访问

set:元素唯一性

    hashset:HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。底层数据结构是哈希表。
哈希表是一个元素为链表的数组,综合了数组与链表的优点。

hashSet具有以下特点:

    不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也可能发生变化;
    HashSet不是同步的;
    集合元素值可以是null;

map:

    hashmap:线程不安全,初始容量为16,默认加载因子0.75(存储数据达到size的0.75时扩容),扩容为原来的两倍,key和value都可以为null,数据结构:数组+链表+红黑树(链表长度大于8时转换为红黑树,小于6时转回为链表)

    hashtable:hashtable 是线程安全的,方法是Synchronized 的,适合在多线程环境中使用,效率稍低: HashMap不是线程安全的,方法不是Synchronized的,效率稍高,初始容量为11,扩容机制为2倍原来大小+1,key和value都不允许为null

    concurrenthashmap:线程安全且高效的HashMap实现,1.8后采用CAS + synchronized 来保证并发安全性

arraylist 和 linkedlist 的区别

ArrayList和LinkedList两者都实现了List接口,但是它们之间有些不同。
(1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问
(2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快
(3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用

arraylist(vector)的扩容策略:

arraylist的底层实现是数组,扩容时分配更大的内存新建一个数组,释放之前的内存,将目前的数据放入新建数组,初始容量为10(也可以自定义指定初始容量),扩容倍数0.5(vector的扩容倍数是1)

为什么hashmap容量大于8时会转换为红黑树

HashMap在jdk1.8之后引入了红黑树的概念,表示若桶中链表元素超过8时,会自动转化成红黑树;若桶中元素小于等于6时,树结构还原成链表形式。

原因:两个说法。。。我觉得第二个对

说法一:

  红黑树的平均查找长度是log(n),长度为8,查找长度为log(8)=3,链表的平均查找长度为n/2,当长度为8时,平均查找长度为8/2=4,这才有转换成树的必要;链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。

还有选择6和8的原因是:

  中间有个差值7可以防止链表和树之间频繁的转换。假设一下,如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果一个HashMap不停的插入、删除元素,链表个数在8左右徘徊,就会频繁的发生树转链表、链表转树,效率会很低。

说法二:

    理想情况下,在随机哈希代码下,桶中的节点频率遵循泊松分布,文中给出了桶长度k的频率表。由频率表可以看出,桶的长度超过8的概率非常非常小。所以作者应该是根据概率统计而选择了8作为阀值。

栈和队列的区别

栈是先进后出,栈顶进栈顶出,只能从一边操作,遍历时需要新开辟空间,效率更慢

队列是先进先出,队尾进对头出,两边都有操作,队列是基于地址指针进行遍历,无需开辟空间,效率更快

堆和栈的区别

最主要的区别就是栈内存用来存储局部变量和方法调用。
而堆内存用来存储Java中的对象。无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中。
独有还是共享
栈内存归属於单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。
而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
异常错误
如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError。
而如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。
空间大小
栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满。如果递归没有及时跳出,很可能发生StackOverFlowError问题。

红黑树、二叉树、平衡二叉树、b+树的区别

https://blog.csdn.net/wyqwilliam/article/details/82935922

二叉搜索/排序树:左孩子<根节点<右孩子,但如果是有序数据容易退化成链表-->引入平衡二叉树

平衡二叉树:平衡二叉树一定是二叉搜索树,左右子树高度相差不超过1

红黑树:平衡二叉树的一种,主要用它存储有序数据,时间复杂度是O(logN),效率非常高

B树:多路搜索树、一棵m阶B树是一棵平衡的m路搜索树,非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1(m=3,则0<j<3-1);设计成多路——进一步降低树的高度,提高搜索性能,路数越多,性能越高?不一定,如果设计成无限多路,退化成有序数组,B树多用于文件系统和数据库的索引,索引存储在硬盘上,数据量大的话不一定一次能加载到内存中,就无法进行查找了,而B树每次加载一个节点,一步步往下查找

在内存中红黑树效率更高,但是涉及到硬盘操作,B树更优

B+树基于B树改造,数据都在叶子节点,同时叶子节点之间还加入了指针形成链表

B+树在数据库索引中用的比较多,数据库操作可能会选取多条数据,B树可能会做局部的中序遍历,进行跨层访问,效率降低,而B+树数据都在叶子节点,不用跨层,同时有链表结构,只要找到首位,就能通过链表把所有数据取出

锁有哪些

【1】公平锁和非公平锁。
公平锁:是指按照申请锁的顺序来获取锁,
非公平所:线程获取锁的顺序不一定按照申请锁的顺序来的。

【2】共享锁和独享锁
独享锁:一次只能被一个线程所访问
共享锁:线程可以被多个线程所持有。
ReadWriteLock 读锁是共享锁,写锁是独享锁。

【3】乐观锁和悲观锁。读取频繁使用乐观锁,写入频繁使用悲观锁
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果发生冲突上层会不断地retry,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

版本号机制:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。


悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁(行锁,表锁等,读锁,写锁)

乐观锁和悲观锁往往依靠数据库提供的锁机制实现,数据库锁才能真正保证数据访问的排他性

【4】分段锁
1.7及之前的concurrenthashmap。并发操作就是分段锁,其思想就是让锁的粒度变小。

【5】偏向锁  是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价
轻量级锁
重量级锁

【6】自旋锁
自旋锁

死锁

产生条件:缺一不可

1)互斥条件(不可破坏):指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

避免死锁:

加锁顺序
加锁时限
死锁检测

mysql锁

开销、加锁速度、死锁、粒度、并发性能

  • 表锁:开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低
  • 行锁:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高
  • 页锁:开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般

JVM模型

线程共享区域:堆、方法区

线程非共享区域:java虚拟机栈,本地方法栈,程序计数器

堆:对象实例的存放区域

方法区:常量、类信息、静态变量、当前java程序编译后的字节码信息

程序计数器:可看作当前线程所执行的字节码的行号指示器

java虚拟机栈:方法调用时会在此生成栈帧,存储局部变量,方法出口,

本地方法栈:jvm调用的native本地方法服务

数据库索引

底层实现是B+树

在所有的叶子结点中增加了指向下一个叶子节点的指针

MyISAM与InnoDB两者之间的区别

1、MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法。不是事务安全的,而且不支持外键,如果执行大量的select,insert MyISAM比较适合。

2、InnoDB:支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。如果有大量的update和insert,建议使用InnoDB,特别是针对多个并发和QPS较高的情况。

https://www.cnblogs.com/xc-chejj/p/11245034.html

https://www.zhihu.com/question/20596402?sort=created

udp与tcp的区别

         TCP(TransmissionControl Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

         UDP是User Datagram Protocol,一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。可靠性由上层应用实现,所以要实现udp可靠性传输,必须通过应用层来实现和控制。

TCP如何实现可靠性传输?

建立连接、确认机制、重传机制、滑动窗口。

如何使用udp进行可靠传输

发送:包的分片、包确认、包的重发

接收:包的调序、包的序号确认

目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。

  • 1、添加seq/ack机制,确保数据发送到对端
  • 2、添加发送和接收缓冲区,主要是用户超时重传。
  • 3、添加超时重传机制。

http的报文格式

GET /admin_ui/rdx/core/images/close.png HTTP/1.1
Accept: */*
Referer: http://xxx.xxx.xxx.xxx/menu/neo
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Host: xxx.xxx.xxx.xxx
Connection: Keep-Alive
Cookie: startupapp=neo; is_cisco_platform=0; rdx_pagination_size=250%20Per%20Page; SESSID=deb31b8eb9ca68a514cf55777744e339

HTTP的请求报文包括:请求行(request line)、请求头部(header)、空行请求数据(request data) 四个部分组成。

请求行包括: 请求方法,URL(包括参数信息),协议版本这些信息(GET /admin_ui/rdx/core/images/close.png HTTP/1.1)

请求头部(Header)是一个个的key-value值,比如

Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)
空行(CR+LF):请求报文用空行表示header和请求数据的分隔

请求数据:GET方法没有携带数据, POST方法会携带一个body

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0xacbbb9d800005133
Cache-Control: private
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html
Cxy_all: baidu+f8b5e5b521b3644ef7f3455ea441c5d0
Date: Fri, 12 Oct 2018 06:36:28 GMT
Expires: Fri, 12 Oct 2018 06:36:26 GMT
Server: BWS/1.1
Set-Cookie: delPer=0; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1433_21112_18560_26350_27245_22158; path=/; domain=.baidu.com
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
 
<!DOCTYPE html>
<!--STATUS OK-->

HTTP的响应报文包括:状态行,响应头,空行,数据(响应体)

状态行包括:HTTP版本号,状态码和状态值组成。

响应头类似请求头,是一系列key-value值

Cache-Control: private
Content-Encoding: gzip
Server: BWS/1.1
Set-Cookie: delPer=0; path=/; domain=.baidu.com
空白行:同上,响应报文也用空白行来分隔header和数据

响应体:响应的data,本例中是一段HTML

原文链接:https://blog.csdn.net/zx_emily/article/details/83024065

http状态码

https://www.cnblogs.com/xflonga/p/9368993.html

200:成功相应

403:客户端发送的url正确,但是服务端由于某些原因拒绝响应

404:客户端请求的资源不存在

400:其他4开头的状态码不适用时使用400,往往是服务器不理解客户端的请求

500:这是一个通用的服务器错误响应。对于大多数web框架,如果在执行请求处理代码时遇到了异常,它们就发送此响应代码。

505:当服务器不支持客户端试图使用的HTTP版本时发送此响应代码。

 

http的长连接机制

在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。

而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:

Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。

快排时间复杂度

nlogn

堆排序

https://segmentfault.com/a/1190000016881385?utm_source=tag-newest

线程创建方法

------------------------继承Thread类创建线程---------------------

1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。

2】创建Thread子类的实例,也就是创建了线程对象

3】启动线程,即调用线程的start()方法

------------------------实现Runnable接口创建线程---------------------

1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体

2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象

3】第三部依然是通过调用线程对象的start()方法来启动线程

------------------------使用Callable和Future创建线程---------------------

1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

线程池参数

https://blog.csdn.net/ye17186/article/details/89467919

四种线程池详析

https://blog.csdn.net/qq_40033365/article/details/79951507

计算机网络每层的协议

 

输入baid.com到显示网页的整个过程

dns:url->ip

三次握手建立tcp连接

http协议发送请求

获得资源

浏览器渲染

linux命令

top:查看cpu使用情况

free:查看内存使用情况

ls:查看文件目录

pwd:查看当前路径

mkdir:新建文件夹

ps -ef:查看进程

ps -ef | grep 进程名:查看某个进程的启动文件路径

rm -rf:-r:强制删除;-f:目录递归删除

mv:移动

cp:拷贝

ifconfig:查看网络设备信息

chmod:给予权限

wget:下载

java垃圾回收机制,使用的算法有哪些,现在商用主要使用哪种回收算法

https://www.cnblogs.com/jiangtunan/p/11025521.html

标记清除算法

  • 效率问题

  • 空间问题(标记清除后会产生大量不连续的碎片)

复制算法

  • 优点:解决碎片化问题,顺序分配内存简单高效

  • 缺点:只适用于存活率低的场景,如果极端情况下如果对象面上的对象全部存活,就要浪费一半的存储空间。

标记整理算法

为了解决复制算法的缺陷,充分利用内存空间,提出了标记整理算法。该算法标记阶段和标记清除一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。 如下图所示:

分代回收算法

如何判断是垃圾

引用计数法

可达性分析法

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章