<< 乘 >>除
Collections.shuffle(array);
小写转大写(相差32) c-'0'
基本类型:
32位 | 64位 | |
char | 1 | 1 |
char* | 4 | 8 |
short int | 2 | 2 |
unsigned int | 4 | 4 |
float | 4 | 4 |
double | 8 | 8 |
long | 4 | 8 |
long long | 8 | 8 |
unsigned long | 4 | 8 |
int Integer自动装箱(-128-127):https://blog.csdn.net/to_myslef/article/details/79523055
计算机存储单位换算:
16位机,32位机表示比特(bit)
- bit:计算机中表示数据的最小单位
- Byte:计算机处理数据的单位
1Byte = 8bit
1G=2 10MB = 2 10 10 KB = 2 10 10 10Byte = 8 * 2 10 10 10 bit
GB 与 Gb不同 ==> GByte Gbit
public protected default private范围:
类内部 | 本包 | 子类 | 外部包 | |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
Collection:
List |
ArrayList、Vector、LinkedList |
Map |
HashMap、HashTable、LinkedHashMap、TreeMap、WeakHashMap
LinkedHashMap accessOrder为 true: 访问为时间顺序 / false:插入顺序排序 |
Set |
HashSet、LinkedHashSet、TreeSet
是基于Map的:map(e,PERSENT); //PERSENT为静态Object对象 |
Stack |
基于Vector |
Queue |
ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue、SynchronousQueue |
Deque |
基于Queue ArrayDeque(基于Vector)、LinkedList、LinkedBlockingDeque |
List补充:
- ArrayList 底层为数组,扩容时扩1.5倍
扩容:底层Object数组默认长度为10。扩容时新建一个数组的拷贝,修改原数组指向新数组
- Vector 底层为数组,扩容时扩2倍
- LinkedList 非线程安全
LinkedHashMap有序:
LinkedHashMap继承自HashMap,具有高效性,同时在HashMap的基础上,又在内部增加了一个链表,用以存放元素的顺序。LinkedHashMap内部多了一个双向循环链表的维护,该链表是有序的,可以按元素插入顺序或元素最近访问顺序(LRU)排列,简单地说:
LinkedHashMap=散列表+循环双向链表
TreeMap有序:
TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合
TreeMap提供了四个构造方法,实现了方法的重载。无参构造方法中比较器的值为null,采用自然排序的方法,如果指定了比较器则称之为定制排序.
- 自然排序:TreeMap的所有key必须实现Comparable接口,所有的key都是同一个类的对象
- 定制排序:创建TreeMap对象传入了一个Comparator对象,该对象负责对TreeMap中所有的key进行排序,采用定制排序不要求Map的key实现Comparable接口。
Map补充:
- HashMap hash数组默认16
- HashTable hash数组默认11
解决冲突的方法:
- 开方地址法
- 再hash法
- 链地址法
HashMap实现原理:
位桶 + 链表 + 红黑树(当阈值超过8时,转换为红黑树)
HashMap存储结构:
transient Entry[] table:
/**实际存储的key-value键值对的个数*/
transient int size;
/**阈值,当table == {}时,该值为初始容量(初始容量默认为16);当table被填充了,也就是为table分配内存空间后,
threshold一般为 capacity*loadFactory。HashMap在进行扩容时需要参考threshold,后面会详细谈到*/
int threshold;
/**负载因子,代表了table的填充度有多少,默认是0.75
加载因子存在的原因,还是因为减缓哈希冲突,如果初始桶为16,等到满16个元素才扩容,某些桶里可能就有不止一个元素了。
所以加载因子默认为0.75,也就是说大小为16的HashMap,到了第13个元素,就会扩容成32。
*/
final float loadFactor;
/**HashMap被改变的次数,由于HashMap非线程安全,在对HashMap进行迭代时,
如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),
需要抛出异常ConcurrentModificationException*/
transient int modCount;
staic class Entry<K,V> implements Map.Entry<K,V>{
V values; //concurrent HashMap: volatile V values;
Entry<K,V> next; //concurrent HashMap:final HashEntry<K,V> next;
final int hash;
}
Hash原理:使用key、hashcode找到bucket位置,获取存储的Entry对象
- index 获取: index = (n-1) $ hash
- hash方法: return (h=key.hashcode()^(h>>>16))
- hashcode(): return key==null?0:key.hashcode() ^ value.hashcode()
- get方法:
get(Object key){
for(Entry<k,v> e = table[indexFor(hash,table.length)];e!=null;e=e.next){
Object k;
if(e.hash == hash && ((k=e.key)==key)||key.equals(k)) return e.value;
return null;
} }
Hash攻击:hashCode相同,将HashMap变成了SingleLinkedList(Java8使用TreeMap提高性能)
HashMap死循环:多个线程同时put,同时触发rehash操作,会导致出现循环节点,从而在get的时候,产生死循环
什么时候扩容:通过HashMap源码可以看到是在put操作时,即向容器中添加元素时,判断当前容器中元素的个数是否达到阈值(负载因子*容量>目前Map大小)的时候,就要自动扩容了。
扩容(resize):当表中75%被占用时重新计算容量;而这个扩容是计算出所需容器的大小之后重新定义一个新的容器,将原来容器中的元素放入其中。
https://www.jianshu.com/p/ee0de4c99f87
https://blog.csdn.net/woshimaxiao1/article/details/83661464
BlockingQueue不熟悉的操作:
抛异常 | 返回特殊值 | 执行阻塞 | |
插入 | add | offer | put |
取出 | remove | poll | take |
检验 | element | peek |
并发数据结构
并发List |
Vector、Collections.synchronized(List list) CopyOnWriteArrayList: 当对象进行写操作时,复制该对象;若进行读操作,则直接返回结果 (add方法比Vecor弱) 高并发读选CopyOnWriteArrayList,写频繁用Vector |
并发Set |
CopyOnWriteArraySet(基于CopyOnWriteArrayList) |
并发Map |
HashTable、synchronizedMap()、ConcurrentHashMap() |
并发Queue |
ConcurrentLinkedQueue、BlockingQueue |
并发Deque(双端队列) |
LinkedBlockingDeque |
- 浅拷贝:会复制这个对象,但原对象的“对象”是共享的,即原对象和拷贝对象共享
- 深拷贝:会复制这个对象 + 对象的对象
程序优化手段:
缓冲:控制流速
实例:BufferedWriter、BufferedOutputStream
优点:改善I/O性能,提高显示效果
缓存:存结果
实例:
- HashMap ---> WeakHashMp
- EHCache ---> Hibernate
- OSCache ---> OpenSymphony
- JBossCache ---> JBoss
池:对象池化。线程池、数据库连接池
Java:Jakarta Commons Pool组件
ThreadPool{
List<PThread> idleThreads;
public synchronized static ThreadPool getInstance(){...}
}
负载均衡:
Java Terracotta分布式缓存框架
引用类型:
- 强引用:只要引用存在,永远不会回收
- 软引用:在系统内存紧张的情况下,软引用会被回收(SoftReference)
- 弱引用:在下一次垃圾回收时回收(WeakRefence)
- 虚引用:主要用于跟踪对象回收,不能单独使用,与引用队列联合使用(RefenceQueue:如PhantomQueue 虚引用队列)(PhantomRefence)
String类的hashcode方法:
hash = S[0]*pow(31,n-1)+S[1]*pow(31,n-2)+...+S[n-1];
红黑树:
- 根永远是黑色
- 叶节点是空节点
- 红色节点的两个子节点是黑色的(黑色不一定)
- 每个路径包含相同数量的黑色节点
- 节点要么是红色,要么是黑色
冲突解决方法:1. 改变节点颜色 2. 执行旋转操作
旋转操作不会影响旋转结点的父结点,父结点以上的结构还是保持不变的
左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了
右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了
ps:黑色节点要么是根,要么是两个孩子,单个孩子不可能是黑
插入操作:一查找插入的位置;二插入后自平衡
插入时要解决:红红冲突
插入情景1:红黑树为空树:直接把插入结点作为根结点就行,插入的节点的颜色转换成黑色
插入情景2:插入节点已经存在:把I设为当前结点的颜色,更新当前结点的值为插入结点的值
插入情景3:插入节点的父节点为黑节点:直接插入
插入情景4:插入节点的父节点为红节点:该父结点不可能为根结点,所以插入结点总是存在祖父结点
插入情景4.1:叔叔结点存在并且为红结点:
- 将P和S设置为黑色
- 将PP设置为红色
- 把PP设置为当前插入结点
如果PP的父结点是黑色,那么无需再做任何处理;但如果PP的父结点是红色,根据性质4,此时红黑树已不平衡了,所以还需要把PP当作新的插入结点,继续做插入操作自平衡处理,直到平衡为止
试想下PP刚好为根结点时,那么根据性质2,我们必须把PP重新设为黑色,那么树的红黑结构变为:黑黑红。换句话说,从根结点到叶子结点的路径中,黑色结点增加了。这也是唯一一种会增加红黑树黑色结点层数的插入情景
插入情景4.2:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点
- 将P设为黑色
- 将PP设为红色
- 对PP进行右旋
插入情景4.2.2:插入结点是其父结点的右子结点
这种情景显然可以转换为情景4.2.1
- 对P进行左旋
- 把P设置为插入结点,得到情景4.2.1
- 进行情景4.2.1的处理
插入情景4.3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
插入情景4.3.1:插入结点是其父结点的右子结点
- 将P设为黑色
- 将PP设为红色
- 对PP进行左旋
插入情景4.3.2:插入结点是其父结点的右子结点
- 对P进行右旋
- 把P设置为插入结点,得到情景4.3.1
- 进行情景4.3.1的处理
https://www.jianshu.com/p/e136ec79235c
红黑树与跳表:性能差别不大,更新数据时,跳表更新部分少,且在并发下跳表性能更好
B-:多路搜索树(非二叉)
B+:B-树的变体,多路搜索树。只有到叶子节点才命中
B*:在非根非叶子节点再加入指针链表
AVL:平衡二叉查找树
- 平衡因子:bf(x) = h(x-right) - h(x-left);
- 平衡因子 1、0、-1的节点都被认为是平衡的
LL型、RR型:(1)旋转(2)变换根节点
LR型、RL型:(1)转化LL,RR (2)再进行LL,RR转换
红黑树与AVL树的差别:
AVL是严格的平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多;
红黑是弱平衡的,用非严格的平衡来换取增删节点时候旋转次数的降低;
所以简单说,搜索的次数远远大于插入和删除,那么选择AVL树,如果搜索,插入删除次数几乎差不多,应该选择RB树。
聚合 & 组合:
Family ---> Child
Person ---> Brain
Java内部类:静态内部类、非静态内部类、局部内部类、匿名内部类
为什么匿名内部类必须用final修饰?
内部类编译后class文件与外部不同,仅仅保留了外部引用;外部参数传入内部类时,内部类并不是直接调用,而是对参数备份,保持内部参数与外部参数一致,用final让引用不改变,否则,内外不一致。保证了即使外部生命周期结束,内部依然可用
不可变类:
不可变对象一旦创建之后就不可更改,不可变类自身线程安全;任何修改都会创建一个新的对象
- 成员变量使用private
- 不提供setter方法
- getter方法中,不直接返回对象本身,而是克隆对象
String为不可变类:public final class String extends Object
String 不可变原因:
- 字符串常量池需要
- hash唯一,可放入缓存
- 当参数时,保证使用安全
subString潜在内存泄漏:
内部调用了String(int offset, int count, char value[]){......},很长的字符串,截取一个短小的字符串,如果短小字符串没有被回收,大的字符串也没有被回收。
new(s.substring())
StringTokenizer的使用:
StringTokenizer st = new StringTokenizer(";");
st.hasMoreTokens();
st.nextToken();
String中isEmpty(),null以及""的区别:
- isEmpty():分配了内存空间,值为空(值=空)
- null:未分配内存空间,值为无(值不存在)
- "":分配了内存空间,值为空字符串(值=空字符串)
Enumeration接口和Iterator接口的区别?
- Enumeration的速度是Iterator的两倍,也使用更少的内存
- Iterator更加安全,允许从集合中移除元素(Iterator的remove方法)
Java反射机制底层:
- ClassforName
- 方法反射
- 方法调用
ClassforName:
- findLoadedClass(String)
- 父加载器loadClass方法
- findClass()
- ClassLoader.defineclass将字节数组转化为class实例
- Class.newInstance 创建
方法反射:
- getDeclaredMethod
- SearchMethods
- privateGetDeclaredMethods 找到class中声明方法的列表
- getRefectionFactory().copyMehod返回
方法调用
- MethodAccessor通过RefactionFactory类中的newMethodAccessor创建一个MethodAccessor接口对象
- NativeMethodAccessorImpl,DelegatingMehodAccessorImpl 中的invoke方法
- MehodAccessorGenerator().generateMehod返回
有关序列化:
父类序列化时,子类自动实现序列化,无需显示实现Serializable接口。对象引用到其他对象,跟着序列化
SerialVersionId:提供运行效率,序列化时会计算(hashCode值)
Java如何实现跨平台的?Java虚拟机负责将.class字节码文件翻译成特定平台下的机器码
为什么Java有反射而C++没有?
Java运行时拥有类的一切信息,而C++通过RTT运行时识别类型信息不完整
面向对象五大原则:
- 单一职责原则:仅有一个引起变化的原因
- 开放封闭原则:既开放,又封闭
- 里氏替换原则:子类可以替换父类
- 依赖倒置原则
- 接口隔离原则
异常 & 错误:
- 运行时异常:除数为0,数组越界
- 被检查异常:
- Error:一般与虚拟机有关(系统崩溃、JVM错误、内存空间不足、方法调用栈溢出等)
this逃逸:
对象还没有构造成功,this引用被发布出去,线程中看到该对象状态是没有初始化完的状态。取得对象线程并不一定会等待对象完结后才使用
发生条件:1. 在构造函数中创建内部类 2. 在构造函数中把这个内部类发布出去
避免:避免这两个条件同时出现(private修饰、getInstance方法)
Timsort思想:(待确认???)
合并 + 插入排序
排序单位是一个个块分区,优化了merge最坏情况O(n2),复杂度小于nlongn,最坏nlongn
- 分区:对严格反序做分区,并反转。如果分区过小,用后面元素补足
- 合并:(优化合并排序)
- 小于某值,直接二分插入排序
- 二分插入合并
- 如果两个run长度加起来比前一个长,则中间位置和较短的合并
- 若2个run长度加起来比前面短,合并
蓄水池问题:一共有N个数据,且N个数据未知。把前K个元素放在水库中,对之后的第i个元素,以k/i的概率替换掉某一个元素。
原型模式:
- 简化对象创建过程
- 性能优于new一个对象
- Class.forName可重复创建相似对象
MyObject object = (MyObject)Class.forName("...").newInstance()
Class.forName 与 ClassLoader.loadClass 的区别
- Class.forName将.class文件加载,执行static块 ClassLoader.loadClass将.class文件加载,不执行static块,只有newInstance才执行static块
- Class.forName(xx.xx)等同于Class.forName(xx.xx,true,CALLClass.class.getClassLoader()) 可以控制是否初始化类
- 实际调用了ClassLoader.load(className,false),loadClass加载时是没有初始化的
HashCode算法:
- Object:对象经过处理后的内存地址
- String:只要字符串内容相同,哈希码相同
- Integer包装类:返回对象中包含的整数值
- int,char基础类,如需存储将自动装箱
OOM异常 OutOfMemoryError
除了程序计数器外,虚拟机内存的其他几个运行时区域都有OOM异常的可能
- Java Heap溢出(不断创建对象)-->检查虚拟机的参数-Xmx
- 运行时常量池溢出:如果向常量池中添加内容,使用String.intern()这个native方法
- 方法区溢出
- 虚拟机栈和本地方法栈溢出:
- 线程请求的栈深度大于虚拟机最大深度 StackOverFlowError
- 在扩展栈时无法申请到足够的内存空间 OutOfMemoryError
Java8特性:
- Lambda表达式
- 函数式接口
- 接口方法(父类方法优先于接口)
- Base64编码引入标准包中
- 日期?
- 并行数组
- HashMap的变化
- Stream更强大?
- jjs接收一些JavaScript源码参数
- 类依赖分析器jdeps
Java动态代理
- 通过实现InvocationHandler接口创建自己的调用处理器
- 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
- 通过反射机制获得动态代理类的构造参数
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数传入
Interface proxy =
(Interface)Proxy.newProxyInstance(classloader,new Class[]{Interface.class},new Invocation HandlerImpl...);
- 动态代理主要使用了newProxyInstance
- ClassLoader loader:指定当前目标对象使用类加载器,写法固定
- Class<?>[] interfaces:目标对象实现的接口的类型,写法固定
- InvocationHandler:事件处理接口,需传入一个实现类,一般直接使用匿名内部类
User proxy = (User) Proxy.newProxyInstance(
user.getClass().getClassLoader(), //classloader
user.getClass().getInterfaces(), //interfaces
new InvocationHandler() {//handler
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
return method.invoke(proxy, args);
}
});
CGLib代理:https://github.com/vicotorz/Vcode/blob/master/src/Proxy_CGLib/ProxyFactory.java
算法:
- 动态规划:从开始推到终
- 贪心算法:从终推到开始
- 回溯法:深度优先(所有解)
- 分支限界法:广度优先(一个解)
Bloom Filter:查找一个元素是不是在集合中
原理:用K个Hash函数计算数字的K个位数,在数组中相应位置置为1
查询过程中,检查K个位中的值,若有位数不为1,则一定不存在
- Java BIO : 同步并阻塞 适用于连接数目比较小且固定的架构
- Java NIO : 同步非阻塞 适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器
- Java AIO(NIO.2) : 异步非阻塞 连接数目多且连接比较长(重操作)的架构
https://blog.csdn.net/baiye_xing/article/details/73135566
Java NIO: 相比面向流I/O NIO面向缓存
- 为所有原始类提供缓存支持
- 使用Java.nio.charset.Charset作为字符集编码解决方案
- 增加通道Channel对象
- 支持锁和内存映射文件的文件访问接口
- 基于Selector异步网络I/O
核心:Buffer、Channel、Selector
- Buffer:
类型:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer
MappredBuffer:Java大文件直接映射到内存,比较大可以分段映射(force()、load()、isloaded())
DirectBuffer:直接访问系统内部类,直接分配在物理内存,不占用堆空间。读写比Buffer快,但创建和销毁慢
Buffer使用步骤:
- 分配空间
- 写入到Buffer
- 调用flip方法
- 从Buffer读取到数据
- 调用clear方法
- flip:读写转换
- allocate:创建
- warp:从数组中创建
- rewind:将position置零,清除标志位,为Buffer提取有效数据
- clear:重写Buffer准备
- compact,mark:标记
- duplicate:复制缓冲区,完全一样的Buffer,但position和limit不同
- slice:创建子缓存,缓冲分区
2. Channel:
类型:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel
- SocketChannel.open();
- SocketChannel.write(buffer);
- ScoketChannel.close();
3. Selector:运行单线程处理多个Channel
Selector管理多个I/O
创建一个Selector实例,并将channel注册监控的信道上,调用Selector方法,阻塞等待
channel.register(selector,就绪事件(Connect、Accept、Read、Write))
selector.open() 主要完成建立pipe,并把pipe的wakeupSourceFd放入pollArray中,注册过程中把channel文件描述符放在pollArray中
select对应内核sys_select调用,有事件发生,select会将临时结果写入到用户空间并返回(轮询pollArray中FD)
Linux 中用epoll不断轮询,当事件发生,回调函数把发生事件存储在就绪事件链表,写到用户空间
1. 使用 FileInputStream fin = new FileInputStream(new File(...));
2. Channel FileChannel fc = fin.getChannel();
3. 创建Buffer ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
4. channel(Buffer) fc.read(byteBuffer); fc.write(byteBuffer);
5. 关闭channel fc.close();
6. Buffer关闭 byteBuffer.flip();
NIO补充功能:
- Scatter / Gatter:分散多个buffer,多个buffer写入一个Channel
- transferForm / transferTo:数据直接在内核空间移动
toChannel.transforForm(fromChannel,position,count);
fromChannel.transforTo...
3. Pipe:两个线程之间的单向连接
Thread A ------> sink channel ------> source channel ------>Thread B
sinkChannel = Pipe.sink(); //写入sink通道
sourceChannel = Pipe.source(); //从source通道读
4. DatagramChannel:用于接收UDP数据包
- NIO:非阻塞IO Buffer / Channel / Selector
- NIO2:引入四个异步 Channel,异步阻塞IO (WatchService watch类)
selector 无阻塞io,阻塞唤醒通过:
channel有事件发生
selector.select(timeout)超时
selector.wakeup()主动唤醒
Wakeup() 用于唤醒阻塞在select方法上的线程
在开始建立的pipe的sink端写入一个字节,source文件描述符处于就绪状态
poll方法返回,select方法返回
NIO优化数据访问的方式:
- FileChannel.transferForm / FileChannel.transferTo 数据直接在内核空间移动
- FileChannel.map 适合大文件只读操作
将文件按照一定大小映射到内存区域,程序访问内存区域将直接操作这个文件数据,省去了内核空间到用户空间复制的过程的损耗
DSL语言:Html,shell,make,ant,maven,rpm,dpkg,awk,正则表达式,dc计算机语言
1.为什么等待和通知是在 Object 类而不是 Thread 中声明的?
- wait和notify不仅仅是普通方法或同步工具,更重要的他们是java中两个线程之间的通信机制
- 每个对象都可以上锁
- 在java中为了进入代码临界区,线程需要锁定并等待锁定
- java是基于Hoare的监视器的思想:在Java中,所有对象都有一个监视器。在 Java 中,所有在另一个线程的执行中侵入的操作都被弃用了(例如 stop 方法)
2.为什么Java中不支持多重继承?
- 可能产生钻石型继承问题产生歧义
- 多重继承使设计复杂化并在转换,构造函数链接等过程中产生问题
3.为什么Java不支持运算符重载?
- 简单和清晰
- 避免编译错误
- JVM复杂性:复杂的 JVM 可能导致 JVM 更慢,并为保证在 Java 中运算符行为的确定性从而减少了优化代码的机会
- 让开发工具处理更容易
4.为什么 String 在 Java 中是不可变的?
- 字符串池中的字符改变会影响其他引用的对象
- 字符串已被广泛引用到许多java类的参数
- 线程安全,避免了java中的同步问题
- 允许string缓存其哈希码,不会在每次调用String的hashcode方法时重新计算,在HashMao中作为键时非常快
- 类加载机制使用
5.为什么 char 数组比 Java 中的 String 更适合存储密码?
- String会存在字符串池中,会在内存中持续很长时间,构成安全威胁
- 存在日志文件或打印纯文本的风险
6.为什么 char 数组比 Java 中的 String 更适合存储密码?
- 如果Serializable包含一个不可序列化的成员,会发生什么?:任何序列化尝试都会因NotSerializableException失败,可以通过trancient或static变量解决
- 什么是序列化?:序列化是把对象改成可以存到磁盘或通过网络发送到其他运行中的 Java 虚拟机的二进制格式的过程, 并可以通过反序列化恢复对象状态
- 可序列化接口 & 可外部接口 :Externalizable 给我们提供 writeExternal() 和 readExternal() 方法,让我们灵活控制Java序列化机制,不依赖Java默认序列化。正确实现Externalizable接口可以显著提高程序性能
- serialVersionUID:SerialVerionUID 用于对象的版本控制,不指定 serialVersionUID的后果是,当你添加或修改类中的任何字段时, 则已序列化类将无法恢复, 因为为新类和旧序列化对象生成的 serialVersionUID 将有所不同。Java 序列化过程依赖于正确的序列化对象恢复状态的, ,并在序列化对象序列版本不匹配的情况下引发 java.io.InvalidClassException 无效类异常
- 如果类中一个成员未实现序列化接口,会发生什么?:抛出NotSerializableException
- 假设新类的超级类实现可序列化接口, 如何避免新类被序列化?新类实现writeObject()和readObject()方法,并从该方法引发NotSerializableException异常
7. 为什么Java中 wait 方法需要在 synchronized 的方法中调用?
- Java 会抛出 IllegalMonitorStateException,如果我们不调用来自同步上下文的wait(),notify()或者notifyAll()方法。
- Javac 中 wait 和 notify 方法之间的任何潜在竞争条件(由于竞态条件,我们可能会丢失通知,如果我们使用缓冲区或只使用一个元素,生产线程将永远等待,你的程序将挂起)
8. 能用Java覆盖静态方法吗?如果我在子类中创建相同的方法是编译时错误?
不,你不能在Java中覆盖静态方法,但在子类中声明一个完全相同的方法不是编译时错误,这称为隐藏在Java中的方法
一些设计思考:
秒杀系统:
- 前端:页面静态化、禁止重复提交、用户限流、图片服务器和应用服务器分离
- 后端:限制uuid,消息队列缓存
摇一摇功能:用户座标geohash传入到redis,并设置过期时间
手机扫二维码登录功能:
- index.html ====== GetQrCodeServlet(生成uuid唯一标识,生成二维码)
- index展示二维码
- 手机扫码后发送验证+ uuid到Server
- index调用LongConnectionCheckServlet进行长时间轮询,参数是uuid;拿到uuid后检查loginUsermap是否为空
设计开发连接池:
- 编写class实现DataSource接口
- 在class的构造器一次性创建n个连接,保存在LinkedList中
- 实现getConnection,从LinkedList中返回一个连接
- 提供将连接放回连接池的方法
定时器设计:https://www.jianshu.com/p/5a973f3ac409
时间帧Tick:提高时间精度
Timer:定时器本身属性(时间间隔,作用次数,触发行为)
TimerId:Timer的唯一标识
Timer属主:每个定时器的归属对象
当属主对象释放时,其负责释放所占的Timer资源,以防止Timer泄露。另一方面Timer触发时,如果找不到属主,也会主动释放自己
Timer:时间轮(循环遍历队列),多级时间轮,最小堆