一 问题
java类的初始化顺序:
加载 父类静态变量,静态代码块 (并列)→
加载 子类静态变量,静态代码块 (并列)→
加载 父类 普通代码块 普通成员变量 (并列)→
基类构造函数 →
加载 子类 普通代码块 普通成员变量 (并列) →
加载 子类构造函数 。
对方法区和永久区的理解以及它们之间的关系:
方法区:又叫静态区,存放的是class类,静态变量,静态方法,常量,运行常量池.
运行常量池存放的是类和接口的常量,和成员变量和成员方法的引用;当一个成员变量被引用的时候,就根据运行常量池中的引用指向成员方法或者变量的在内存的实际地址。
方法区是规定存放什么东西,是属于规范,而永久区是一种实现。
为什么局部变量必须要初始化?
首先说,这是一种设计上的规范,因为相对于成员变量来说,局部变量的赋值取值顺序是确定的,局部变量存储于栈中,当jvm执行方法进行出栈入栈的时候,在去为期赋值,可能会更麻烦。假如我们可以为其初始化附上默认值,那么有可能因为程序员忘了为期在进行赋值操作产生不必要的错误。
读写锁:
有两个构造函数,默认是非公平锁;读写锁中AQS中的state代表了读锁的数量和是否持有锁;state的高16位代表读锁数量,后16位比较写锁的持否。
读锁:
读锁的获取:
读锁是基于AQS的共享模式;
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg); 当小于0时候假如到等待队列中;
tryAcquireShared(arg) 具体执行 :
先判断如果是当前有写线程并且不是本线程不是写线程,无法重入 则返回-1;
否则 判断 是否可以读线程阻塞,是否读线程的数量达到上限,然后cas比较;
判断都通过,然后判断如果是第一个线读线程程就是当前线程,否则判断是否可重入,否则,记录每个线程读的次数;
写锁:
写锁的获取:
写锁基于的是AQS的独占模式;
写锁会直接尝试去获取锁,获取失败了加入到队中等待。
死锁的四个条件:
互斥 :线程之间是互斥的。
持有且不释放 :持有一部分资源,并持续等待另一资源;
不可抢占: 不能抢占其他线程正持有的资源
循环等待;一直在等待需要的资源被释放
字符串创建对象:
s="abc"
JDK1.6
当常量池中不存在“”abc“”这个对象的时候,会在堆中创建一个,并且将其复制到常量池中,返回常量池的对象。
JDK1.7以上
(1) 当常量池中不存在"abc"这个字符串的引用,在堆内存中new一个新的String对象,将这个对象的引用加入常量池。(跟1.6的区别是常量池不再存放对象,只存放引用。)
(2) 当常量池中存在"abc"这个字符串的引用,str指向这个引用
intern方法:
S2 =s.intern;
在常量池中如果没有这个引用,那就把这个对象的引用放到常量池中,并且返回常量池中的引用。
如果有这个引用,直接返回。
String a = new String("abc");
String b = new String ("abc");
第一行:创建了两个对象; 一个是在堆中创建的对象,
一个是在常量池中中创建“abc”的对象;
第二行: 创建了一个对象 ; 在堆中创建的 new 对象; 常量池已经有了不需要在创建;
s2 = s1 + "b";
s1在编译器不能确定,所以s2是堆中新建的对象;
s2.innner();
进程和线程的区别:
进程指的是:是系统资源进行调度分配的基本单位,是一个独立的程序完成一次数据集合上的运动换言之 系统要做一件事,运行一个任务,所有运行的任务通常就是一个程序;
线程: 是cpu调度的基本单位,
一个进程中包含多个线程,我们说的线程并发,指的就是在同一个进程中的多个线程抢占资源。线程和其他本进程中的线程共享内存资源。
int:
int a=128 int b=128
a==b false
int a=100 int b=100
a==b true
在-127-128之间会缓存,结果就是true,超过范围则false;
int系列的比较分为int值的比较,Integer对象的比较。
int的范围是-2的31次方——————2的31次方-1;
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
自动拆箱
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
i是新生成的对象,而j则是指向常量池中的对象,所以false。
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false