Java易忘点梳理

<< 乘  >>除

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

解决冲突的方法:

  1. 开方地址法
  2. 再hash法
  3. 链地址法

 

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反射机制底层:

  1. ClassforName
  2. 方法反射
  3. 方法调用

ClassforName:

  1. findLoadedClass(String)
  2. 父加载器loadClass方法
  3. findClass()
  4. ClassLoader.defineclass将字节数组转化为class实例
  5. Class.newInstance 创建

方法反射:

  1. getDeclaredMethod
  2. SearchMethods
  3. privateGetDeclaredMethods 找到class中声明方法的列表
  4. getRefectionFactory().copyMehod返回

方法调用

  1. MethodAccessor通过RefactionFactory类中的newMethodAccessor创建一个MethodAccessor接口对象
  2. NativeMethodAccessorImpl,DelegatingMehodAccessorImpl 中的invoke方法
  3. MehodAccessorGenerator().generateMehod返回

 

有关序列化:

父类序列化时,子类自动实现序列化,无需显示实现Serializable接口。对象引用到其他对象,跟着序列化

SerialVersionId:提供运行效率,序列化时会计算(hashCode值)

 

Java如何实现跨平台的?Java虚拟机负责将.class字节码文件翻译成特定平台下的机器码

 

为什么Java有反射而C++没有?

Java运行时拥有类的一切信息,而C++通过RTT运行时识别类型信息不完整

 

面向对象五大原则:

  1. 单一职责原则:仅有一个引起变化的原因
  2. 开放封闭原则:既开放,又封闭
  3. 里氏替换原则:子类可以替换父类
  4. 依赖倒置原则
  5. 接口隔离原则

 

异常 & 错误:

  • 运行时异常:除数为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算法:

  1. Object:对象经过处理后的内存地址
  2. String:只要字符串内容相同,哈希码相同
  3. Integer包装类:返回对象中包含的整数值
  4. int,char基础类,如需存储将自动装箱

 

OOM异常  OutOfMemoryError

除了程序计数器外,虚拟机内存的其他几个运行时区域都有OOM异常的可能

  • Java Heap溢出(不断创建对象)-->检查虚拟机的参数-Xmx
  • 运行时常量池溢出:如果向常量池中添加内容,使用String.intern()这个native方法
  • 方法区溢出
  • 虚拟机栈和本地方法栈溢出:
    • 线程请求的栈深度大于虚拟机最大深度 StackOverFlowError
    • 在扩展栈时无法申请到足够的内存空间  OutOfMemoryError

Java8特性:

  1. Lambda表达式
  2. 函数式接口
  3. 接口方法(父类方法优先于接口)
  4. Base64编码引入标准包中
  5. 日期?
  6. 并行数组
  7. HashMap的变化
  8. Stream更强大?
  9. jjs接收一些JavaScript源码参数
  10. 类依赖分析器jdeps

Java动态代理

  1. 通过实现InvocationHandler接口创建自己的调用处理器
  2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
  3. 通过反射机制获得动态代理类的构造参数
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数传入
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面向缓存

  1. 为所有原始类提供缓存支持
  2. 使用Java.nio.charset.Charset作为字符集编码解决方案
  3. 增加通道Channel对象
  4. 支持锁和内存映射文件的文件访问接口
  5. 基于Selector异步网络I/O

核心:Buffer、Channel、Selector

  1. Buffer:

类型:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

 

MappredBuffer:Java大文件直接映射到内存,比较大可以分段映射(force()、load()、isloaded())

DirectBuffer:直接访问系统内部类,直接分配在物理内存,不占用堆空间。读写比Buffer快,但创建和销毁慢

 

Buffer使用步骤:

  1. 分配空间
  2. 写入到Buffer
  3. 调用flip方法
  4. 从Buffer读取到数据
  5. 调用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补充功能:

  1. Scatter / Gatter:分散多个buffer,多个buffer写入一个Channel
  2. 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 中声明的?

  1. wait和notify不仅仅是普通方法或同步工具,更重要的他们是java中两个线程之间的通信机制
  2. 每个对象都可以上锁
  3. 在java中为了进入代码临界区,线程需要锁定并等待锁定
  4. java是基于Hoare的监视器的思想:在Java中,所有对象都有一个监视器。在 Java 中,所有在另一个线程的执行中侵入的操作都被弃用了(例如 stop 方法)

2.为什么Java中不支持多重继承?

  1. 可能产生钻石型继承问题产生歧义
  2. 多重继承使设计复杂化并在转换,构造函数链接等过程中产生问题

3.为什么Java不支持运算符重载?

  1. 简单和清晰
  2. 避免编译错误
  3. JVM复杂性:复杂的 JVM 可能导致 JVM 更慢,并为保证在 Java 中运算符行为的确定性从而减少了优化代码的机会
  4. 让开发工具处理更容易

4.为什么 String 在 Java 中是不可变的?

  1. 字符串池中的字符改变会影响其他引用的对象
  2. 字符串已被广泛引用到许多java类的参数
  3. 线程安全,避免了java中的同步问题
  4. 允许string缓存其哈希码,不会在每次调用String的hashcode方法时重新计算,在HashMao中作为键时非常快
  5. 类加载机制使用

5.为什么 char 数组比 Java 中的 String 更适合存储密码?

  1. String会存在字符串池中,会在内存中持续很长时间,构成安全威胁
  2. 存在日志文件或打印纯文本的风险

6.为什么 char 数组比 Java 中的 String 更适合存储密码?

  1. 如果Serializable包含一个不可序列化的成员,会发生什么?:任何序列化尝试都会因NotSerializableException失败,可以通过trancient或static变量解决
  2. 什么是序列化?:序列化是把对象改成可以存到磁盘或通过网络发送到其他运行中的 Java 虚拟机的二进制格式的过程, 并可以通过反序列化恢复对象状态
  3. 可序列化接口 & 可外部接口 :Externalizable 给我们提供 writeExternal() 和 readExternal() 方法,让我们灵活控制Java序列化机制,不依赖Java默认序列化。正确实现Externalizable接口可以显著提高程序性能
  4. serialVersionUID:SerialVerionUID 用于对象的版本控制,不指定 serialVersionUID的后果是,当你添加或修改类中的任何字段时, 则已序列化类将无法恢复, 因为为新类和旧序列化对象生成的 serialVersionUID 将有所不同。Java 序列化过程依赖于正确的序列化对象恢复状态的, ,并在序列化对象序列版本不匹配的情况下引发 java.io.InvalidClassException 无效类异常
  5. 如果类中一个成员未实现序列化接口,会发生什么?:抛出NotSerializableException
  6. 假设新类的超级类实现可序列化接口, 如何避免新类被序列化?新类实现writeObject()和readObject()方法,并从该方法引发NotSerializableException异常

7. 为什么Java中 wait 方法需要在 synchronized 的方法中调用?

  1. Java 会抛出 IllegalMonitorStateException,如果我们不调用来自同步上下文的wait(),notify()或者notifyAll()方法。
  2. Javac 中 wait 和 notify 方法之间的任何潜在竞争条件(由于竞态条件,我们可能会丢失通知,如果我们使用缓冲区或只使用一个元素,生产线程将永远等待,你的程序将挂起)

8. 能用Java覆盖静态方法吗?如果我在子类中创建相同的方法是编译时错误?

不,你不能在Java中覆盖静态方法,但在子类中声明一个完全相同的方法不是编译时错误,这称为隐藏在Java中的方法


一些设计思考:

秒杀系统:

  • 前端:页面静态化、禁止重复提交、用户限流、图片服务器和应用服务器分离
  • 后端:限制uuid,消息队列缓存

 

摇一摇功能:用户座标geohash传入到redis,并设置过期时间

 

手机扫二维码登录功能:

  • index.html ====== GetQrCodeServlet(生成uuid唯一标识,生成二维码)
  • index展示二维码
  • 手机扫码后发送验证+ uuid到Server
  • index调用LongConnectionCheckServlet进行长时间轮询,参数是uuid;拿到uuid后检查loginUsermap是否为空

设计开发连接池:

  1. 编写class实现DataSource接口
  2. 在class的构造器一次性创建n个连接,保存在LinkedList中
  3. 实现getConnection,从LinkedList中返回一个连接
  4. 提供将连接放回连接池的方法

定时器设计:https://www.jianshu.com/p/5a973f3ac409

时间帧Tick:提高时间精度

Timer:定时器本身属性(时间间隔,作用次数,触发行为)

TimerId:Timer的唯一标识

Timer属主:每个定时器的归属对象

当属主对象释放时,其负责释放所占的Timer资源,以防止Timer泄露。另一方面Timer触发时,如果找不到属主,也会主动释放自己

Timer:时间轮(循环遍历队列),多级时间轮,最小堆

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