此文主要内容:
注意: equals在Object中,就是 使用“==” 进行判断 的,而在String重写后,则是 先使用 “==”进行地址判断 ,然后使用instanceof进行 String类型判断 ,最后使用char[]进行挨个 字符判断 。
注意: ArrayList默认长度为10,负载因子为0.75。
数据库加乐观锁,是否能够完全做到线程安全?
答案: 使用TreeSet实现,根据其提供的Comparable或者Comparator来进行排序。
Set集合的特点是无序且不可重复,但值得一提的是,无序指的是,存储顺序与插入顺序不同,但元素存储有指定规则的。例如HashSet是根据hash值进行的元素存储位置的计算,而TreeSet的可以指定,存储顺序规则,默认自然排序。
HashMap是双列集合,即键值对,其底层是数组+链表+红黑树。
首先,当元素存入HashMap中,内部根据hash方法与key计算出元素在数组中的存储位置,数组默认长度为16。
其次,由于不同的数据,也会计算出相同的哈希值,因此一个位置不能只放一个元素。所以最后决定,在位置中放入链表(Noded对象),当出现hash冲突时,则将其放入对应位置的链表中。值得一提的是,它并不会出作为尾节点出现,而是作为头节点出现,因为大佬们认为 后存多取 。
最后,由于链表的查询十分缓慢,当数据较多时,不利于查询。因此,当某节点下元素个数达到8时,Node对象则会变成TreeNode,也就是链表变成红黑树。而进行删除之后,个数只有6时,则会由红黑树变成链表。
负载因子也是0.75,而默认长度则是16。负载因子过小容易造成空间浪费,负载因子过大,容易加入Hash冲突。
public class MyThread extends Thread{
@Override
public void run(){
System.out.println("方式一");
}
}
//调用
//MyThread th = new MyThread();
//th.start();
public class MyThread implements Runnable{
@Override
public void run(){
System.out.println("方式二");
}
}
//调用
//Thread th = new Thread(new MyThread());
//th.start();
class MyThread<T> implements Callable {
@Override
public T call() throws Exception {
return null;
}
}
//调用
// MyThread th = new MyThread();
// FutureTask task = new FutureTask<>(th);
// Thread thread = new Thread(task);
// thread.start();
// Integer integer = task.get();
// System.out.println(integer);
线程池创建线程的方法,归根结底应该还是三者之一。每一次线程时间片结束,而线程未完成时,下一次之所以又可以接着执行,是因为JVM中有一个程序计数器,它负责记录程序的执行位置,每一个线程都有一个私有的计数器。
虚拟机栈与本地方法站都是私有的,而堆与元空间都是共享的。对于JVM内存的理解,具体可以参考这篇文章,14年的大佬写得很棒。
Integer i1 = 120;
Integer i2 = 120;
System.out.println(i1==2); //true
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i3==i4); //false
注意: 以上代码之所以会有两种不同的结果,是因为Java在内存中,存入了整数常量,而存储的范围则是[-128-127]。所以,i1与i2都是指向的同一个常量,而i3与i4却不是。
String s1 = "张三";
Stirng s2 = "张三";
String s3 = new String("张三");
String s4 = "张三123";
String s5 = "123";
String s6 = "张三"+"123";
String s7 = s1 + s5;
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s4==s6); //true
System.out.println(s4==s7); //false
注意: 首先,当一个字符串没有使用new的方式创建时,会去常量池中看,是否存在该字符串。如果存在则直接引用,如果不如在,则存入常量池,在引用。其次,如果使用new创建,那么会在堆中开辟一片空间,变量指向的是堆地址。最后,字符串的 “+” 操作,如果双方都是 " " 的形式,那么Java字节会把两个字符串合并。如果双方有一个是对象,那么底层实现时,会创建一个 StringBuilder来append其他字符串 。
这里的常量池介绍完全不足,需要找寻专业文章,查看常量池详情。
双亲委派机制指的是,当一个类加载器收到加载请求时,并不会马上区加载,而是会将请求委派给父级。依次递推,直至启动类加载器,如果父级无法执行,那么会逐级下放。主要解决了Java基础类统一加载的问题。
由于请求是逐级下方,所以BootStrap ClassLoader无法使用应用程序加载类,于是在特定情况下,便存在缺陷,例如厂家自定义组件,SPI还需要详细了解。
GC即是Java的垃圾回收机制,也就是回收内存。而内存的几大分区中,虚拟机栈、本地方法站、PC寄存器(程序计数器)都是私有的,一旦线程结束便会被回收,所以,GC主要回收的是堆。