- 判断是不是奇数的方法:
i % 2 != 0 或者 (i & 1) != 0
,不能用i % 2 == 1
,因为存在负数
- 精确计算的时候不要用小数计算,例如“钱”,乘以100用整数代替小数计算。
long aa = 24 * 60 * 60 * 1000 * 1000
会造成精度损失,是因为等号右边本身是按照int
进行计算的,完成之后才将值转成long
,解决办法只需要将其中一个常亮后面加L
使其成为长整型。
- 多重转型:
- 如果最初的数值是有符号的,那么就执行符号扩展,
- 如果是
char
类型,那么不管被转成什么类型,都执行0扩展
- 如果目标小于源长度,则直接截取就行了
- 谜题9:不要将复合操作符用于
byte,short,char
上,当用于Int
时 ,要确保右侧不是double,float,long
类型,避免强制转换,导致精度损失。
+
号操作符当且仅当它的操作数中至少有一个是String
类型时,才执行字符串链接操作。
- 谜题13,只要是常量表达式组成的字符串相等,
hello world = hello + world
,那么引用相等,但是如果其中一个是变量就不行了,因为静态常量在编译期就转换了,而如果带了变量,就只有在运行期才知道该变量是什么东西了。
new String(char[])
时,尽量都指明字符集,不要依赖默认的字符集
j = j++
相当于temp = j;j=j+1;j = temp;
for(int i = start;i<= start + 1;i++){}
当start
是Integer.max_value
时,可以无限循环
while(i == i + 1)
当i = Double.POSITIVE_INFINITY
,也就是无穷大时,可以无限循环
while(i != i)
,当i = Double.NaN(不是数字的数量)
时,为true
while(i != i + 0)
当i
为一个字符串时,就可以true
,无限循环
- 移位运算符只能是整数
- 谜题31,所有的算术操作都会对
short,byte,char
类型先提升为int
类型。
while(i<=j && j<=i&& i!=j){}
,当i,j
等于同一个数的包装类时满足为true
,包装类在比较运算符时自动拆包了,但是== ,!=
这些时是引用。
int
有31位精度,而float
只有24位精度
- 谜题37,不能随便抛出
try
中不存在的受检查异常,也就是基本上不能随便加cache
但是可以随便捕获Exception
异常
- 一个方法可以抛出的被检查异常集合是它继承的父类中方法抛出异常的交集,而不是合集
- 要确定一个程序是否可以不止一次的对一个空
final
类型进行赋值是一个很困难的问题,比如,第一次通过方法赋值,出现错误了,那么第二次是否还可以再赋值?所以编辑器采用了保守方法:一个空final
变量只有在它是明确未赋过值得地方才可以被赋值,也就是只有第一次,就算失败了,第二次也不能赋值了。
- 当调用一个构造器时,实例变量的初始化操作(也就是属性)将先于构造器的程序体运行。
- 对于一个对象包含与它自己类相同的实例时,例如,链表列表节点,树节点和图节点,需要避免造成栈满异常(stackOverfloatError),尽量就不要属性是该类,除非是单例,通过
static
修饰一下
- 实例初始化操作抛出的任何异常都会传播给构造器。
- 令人混淆的构造器案例:
Java
的重载解析过程是以两个阶段运行的,第一个阶段选取所有可获得并且可以应用的方法或者构造器;第二个阶段是在第一个阶段选取的方法或构造器中选取最精确的一个。如果第一个的参数类型可以传递给第二个,那么就说,第一个精确点,eg:double[]
可以传递给Object
,所以double[]
精确。
public class Confusing{
private Confusing(Object o){
System.out.println("Object");
}
private Confusing(double[] dArray){
System.out.println("array");
}
public static void main(String[] args){
new Confusing(null);
}
}
- 啊呀!我的猫变成狗了
- 非静态方法可以直接访问静态方法,不加类名。
final
类不可继承;final
方法能继承,但不能被子类重写;final
变量是常量
- 静态方法被继承到子类中,那么也可以直接通过
SubClass.staticMethod()
调用;
- 我所得到的都是静态的
class Dog{
public static void bark(){System.out.println("woof");}
}
class BaseJi extends Dog{
public static void bark(){}
}
public static viod main(String[] args){
Dog woofer = new Dog();
Dog nipper = new BaseJi();
woofer.bark();
nipper.bark();
}
- 对静态方法的调用不存在任何动态的分派机制,在编译时刻已经确定,因为他们都被声明为Dog类型的,所以都会调用
Dog
类型的静态方法,输出woof
;
- 更改方法:去掉
static
,因为静态的东西是在编译期就定了,去掉的话,就是重写,而不是隐藏了。
- 谜题49:比生命更大
public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private final int beltSize;
private static final int current_year = Calendar.getInstance().get(Calendar.YEAR);
private Elvis(){
beltSize = current_year - 1930;
}
public int beltSize(){
return beltSize;
}
public static void main(String[] args){
System.out.println(INSTANCE.beltSize());
}
}
- 谜题50,不是你的类型:
null
对于每一个引用类型来说都是子类型,但是当instanceof
操作符左侧为null
时返回false
new Type() instanceof String instanceof
操作符要求:如果两个操作数的类型都是类,其中一个必须是另一个子类型,会在编译器就检测出来。
Type type = (Type) new Object()
会在运行期报错,但是可以编译通过。
- 不要在构造器中调用被子类重写过的方法。
- 助手方法(静态方法)通常都优于静态代码块,因为方法可以命名。
- Null和Void
public class Type{
public static void greet(){
System.out.println("hello word");
}
public static void main(String[] args){
((Type)null).greet();
}
}
Java
语言规范不允许本地变量声明语句作为一条语句在for,while,do
循环中while Type type = new Type()
需要加{}
括起来。
BigInteger,BigDecimal,包装类,String
都是不可变的,改动之后需要用新的接受String aa = "aa"; aa = aa + "bb"
;
equal
方法重写,可以先用instanceof
判断一下类型,相同不,再比较值。比较两个实例是否相等时,可以先判断引用是否相等
split
接受的是一个正则表达式;
- 对于陌生的数组,可以使用
Arrays.deepToString()
打印出来
Math.abs(Integer.MIN_VALUE)
是Integer.MIN_VALUE
;
- 谜题66:一件私事:重写(方法)和隐藏(属性)的区别,重写除非在子类中通过
super
可以调用到父类的方法,否则调不到了,而隐藏可以转型到父类,就可以访问到了;当声明“属性”,“静态方法”,“内部类”时,如果和父类的名称相同了,会发生隐藏。
final
修饰方法时,对于实例方法表示不能“重写”,对于静态方法来说,不能“隐藏”
- 隐藏一个成员将阻止其被继承。
- 谜题77:搞乱锁的妖怪:
public class Worker extends Thread{
private volatile boolean flag = false;
public void run(){
while(!flag){
pretendToWork();
}
System.out.print("beer is good");
}
private void pretendToWork(){
try{
Thread.sleep(300);
}catch(InterruptedException e){}
}
synchronized void quit(){
flag = true;
join();
}
synchronized void keepWorking(){
flag = false;
}
public static void main(String[] args){
Worker worker = new Worker();
worker.start();
Timer t = new Timer(true);
t.schedule(new TimerTask(){
public void run(){worker.keepWorking()}
},500);
Threed.sleep(400);
worker.quit();
}
}
private final Object lock = new Object();
public void quit(){
synchronized(lock){
flag = true;
join();
}
}
- 谜题85,:不要在类初始化的时候启动任何线程,也就是静态代码块中
- 谜题89:泛型迷药
- 非静态成员类可以访问外部类的泛型,所以该类是不需要自己的泛型的;
- 静态成员类需要自己的泛型;
- 优先考虑用静态成员类代替非静态成员类;