【⾯试】三七互娱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时,树结构还原成链表形式。
原因:两个说法。。。我觉得第⼆个对
简单的java游戏代码
说法⼀:
  红⿊树的平均查长度是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+树的区别
⼆叉搜索/排序树:左孩⼦<;根节点<;右孩⼦,但如果是有序数据容易退化成链表-->引⼊平衡⼆叉
平衡⼆叉树:平衡⼆叉树⼀定是⼆叉搜索树,左右⼦树⾼度相差不超过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锁

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。