double和float比较特殊。一个典型的题目是
System.out.println(0.9); // 输出0.9
System.out.println(2.0 - 0.1); // 输出0.8999999999999999
System.out.println(2.0 - 1.1);
原理解析
小数转2进制的方法为: 乘2取整,余数再乘2取整, 直到余数为0;
0.9和0.1,使用2进制表示时,是个无限循环小数 (你自己试试就知道了 );
那么System.out.println(0.9)
为什么打印的是0.9
, 而不是0.8999999999999999
?
可以通过下面的语句打印出1.1的二进制形式
System.out.println(Long.toBinaryString(Double.doubleToLongBits(1.1)));
输出
11111111110001100110011001100110011001100110011001100110011010
double的数据结构 https://blog.csdn.net/wzj_whut/article/details/85225186
可以看出, 末尾由...11001
的, 变成了...11010
, 应该是执行了4舍5入, 也就是1.1实际上是大了一点点的.
1.100000000000000088817841970012523233890533447265625
System.out.println(new BigDecimal(1.1));
所以2.0-1.1实际上比0.9要小
手动算很困难, 再涉及到四舍五入的问题, 只能根据出题人意图靠猜?
不同于C/C++中的char, java中的一个char占用2个字节.
考试了?!
在java中, 上面这段字符串, 是几个char?
答案是6
1个中文字符为1个char, 1个表情符号为2个char, 1个英语字母为1个char
如果转成utf8字节, 是几个字节?
答案是14
1个中文字符为3个字节, 1个表情符号为4个字节, 1个英语字母为1个字节
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = "he";
String s5 = "he" + "llo";
String s6 = s4 +"llo";
String s7 = s6.intern();
System.out.println(s1==s2); // true
System.out.println(s1==s3); // false
System.out.println(s1==s5); // true
System.out.println(s1==s6); // false
System.out.println(s1==s7); // true
两个不再类型的数值进行二元操作时,
其中一个值是double, 则另一个也会转换成double
否则, 其中一个值是float, 则另一个也会转换成float
否则, 其中一个值是long, 则另一个也会转换成long
否则, 其中一个值是int, 则另一个也会转换成int
带标签的break语句的作用有
注意点:
a. 如果初始化代码先声明, 也会先执行初始化块
b. 如果代码块, 域初始化语句, 构造主体都初始了同一个域, 这个值就被初始化多次
不可变类有哪些?
子类的方法可见性不能低于超类的可见性, 举例:
如果超类方法是protected, 子类必须是protected或public
如果超类方法声明抛出异常, 子类抛出的异常不能比超类更通用, 举例
超类声明抛出了IOException, 子类可以重新声明为抛出FileNotFoundException
子类无法覆盖超类的域变量, 所谓的覆盖, 只是针对方法
class D {
public int b = 1;
}
class E extends D {
public int b = 2;
}
E e = new E();
D d = e;
System.out.println(e.b); // 2
System.out.println(d.b); // 1
接口方法, 包括静态方法, 自动设置为public
接口中的域自动设置为public static final
什么是标记接口, 有什么作用?
不包含方法, 但是可以使用instanceof
函数式接口: 只有一个方法的接口. (不能是抽象类)
lambda捕获的变量必须是final变量
Arrays.sort(strs, String::compareTo);
strs.stream().map(String::new);
// 等同于:
strs.stream().map(str -> {
return new String(str);
});
需要注意的是序列化问题
Erorr和Exception
Exception又分为RuntimeException和其它异常
受检异常/非受检异常:
派生于Error或RuntimeException的异常, 称为非受检异常. (由环境导致的错误)
其它的异常, 称为受检异常. (由于代码疏忽导致的错误)
注意: OutOfMemoryError不是RuntimeException
如果try和finally中都执行return语句, finally的值会覆盖try中的值
真相与约束:
extends, super
规则:
super只允许写, 不允许读. (助记: 如果一个类有好多个super, 都不知道super是哪个类, 读出来是什么类型?)
extends只允许读, 不允许写 (助记. 与super相反, 但是可以写入null)
举例:
List<Child> list1 = new LinkedList<>();
list1.add(new Child());
List<? extends Parent> list2 = list1;
// extends限定, 只允许读, 不允许写
// list2.add(new Parent());
// list2.add(new Child());
Parent x = list2.get(0);
List<Parent> list1 = new LinkedList<>();
List<? super Child> list2 = list1;
list2.add(new Child());
// super限定, 只允许写, 不允许读
// Parent x = list2.get(0);
// Child y = list2.get(0);
新创建, 可运行, 被阻塞, 等待, 计时等待, 被终止
典型死锁例子
class A {
public synchronized void fa(B b) {
sleep(500); // 为了100%死锁
b.call();
}
public synchronized void call() {
}
}
class B {
public synchronized void fb(A a) {
sleep(500); // 为了100%死锁
a.call();
}
public synchronized void call() {
System.out.println("B call");
}
}
final A a = new A();
final B b = new B();
Thread ta = new Thread(new Runnable() {
@Override
public void run() {
a.fa(b);
}
});
Thread tb = new Thread(new Runnable() {
@Override
public void run() {
b.fb(a);
}
});
ta.start();
tb.start();
ta.join();
tb.join();
在两个线程刚启动时, a, b都获取了自己的锁
500毫秒之后, a试图获取b的锁, 但是b已经被锁了, 于a阻塞
b也同样, 最后两个线程都被锁住
死锁线程堆栈分析
"Thread-1" #10 prio=5 os_prio=0 tid=0x15ff0000 nid=0x2714 waiting for monitor entry [0x1614f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at 死锁$A.call(死锁.java:19)
- waiting to lock <0x04e29750> (a 死锁$A)
at 死锁$B.fb(死锁.java:31)
- locked <0x04e2b128> (a 死锁$B)
at 死锁$2.run(死锁.java:57)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" #9 prio=5 os_prio=0 tid=0x15fed800 nid=0xb38 waiting for monitor entry [0x1607f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at 死锁$B.call(死锁.java:34)
- waiting to lock <0x04e2b128> (a 死锁$B)
at 死锁$A.fa(死锁.java:16)
- locked <0x04e29750> (a 死锁$A)
at 死锁$1.run(死锁.java:51)
at java.lang.Thread.run(Thread.java:745)
上面显示:
Thread-1已经锁住了0x04e2b128, 然后试图去锁0x04e29750
Thread-2已经锁住了0x04e29750, 然后试图去锁0x04e2b128
很明显是死锁