java解惑知识点总结

  1. 判断是不是奇数的方法:i % 2 != 0 或者 (i & 1) != 0,不能用i % 2 == 1,因为存在负数
  2. 精确计算的时候不要用小数计算,例如“钱”,乘以100用整数代替小数计算。
  3. long aa = 24 * 60 * 60 * 1000 * 1000会造成精度损失,是因为等号右边本身是按照int进行计算的,完成之后才将值转成long,解决办法只需要将其中一个常亮后面加L使其成为长整型。
  4. 多重转型:
    • 如果最初的数值是有符号的,那么就执行符号扩展,
    • 如果是char类型,那么不管被转成什么类型,都执行0扩展
    • 如果目标小于源长度,则直接截取就行了
  5. 谜题9:不要将复合操作符用于byte,short,char上,当用于Int时 ,要确保右侧不是double,float,long类型,避免强制转换,导致精度损失。
  6. +号操作符当且仅当它的操作数中至少有一个是String类型时,才执行字符串链接操作。
  7. 谜题13,只要是常量表达式组成的字符串相等,hello world = hello + world,那么引用相等,但是如果其中一个是变量就不行了,因为静态常量在编译期就转换了,而如果带了变量,就只有在运行期才知道该变量是什么东西了。
  8. new String(char[])时,尽量都指明字符集,不要依赖默认的字符集
  9. j = j++相当于temp = j;j=j+1;j = temp;
  10. for(int i = start;i<= start + 1;i++){}startInteger.max_value时,可以无限循环
  11. while(i == i + 1)i = Double.POSITIVE_INFINITY,也就是无穷大时,可以无限循环
  12. while(i != i),当i = Double.NaN(不是数字的数量)时,为true
  13. while(i != i + 0)i为一个字符串时,就可以true,无限循环
  14. 移位运算符只能是整数
  15. 谜题31,所有的算术操作都会对short,byte,char类型先提升为int类型。
  16. while(i<=j && j<=i&& i!=j){},当i,j等于同一个数的包装类时满足为true,包装类在比较运算符时自动拆包了,但是== ,!=这些时是引用。
  17. int有31位精度,而float只有24位精度
  18. 谜题37,不能随便抛出try中不存在的受检查异常,也就是基本上不能随便加cache但是可以随便捕获Exception异常
  19. 一个方法可以抛出的被检查异常集合是它继承的父类中方法抛出异常的交集,而不是合集
  20. 要确定一个程序是否可以不止一次的对一个空final类型进行赋值是一个很困难的问题,比如,第一次通过方法赋值,出现错误了,那么第二次是否还可以再赋值?所以编辑器采用了保守方法:一个空final变量只有在它是明确未赋过值得地方才可以被赋值,也就是只有第一次,就算失败了,第二次也不能赋值了。
  21. 当调用一个构造器时,实例变量的初始化操作(也就是属性)将先于构造器的程序体运行。
  22. 对于一个对象包含与它自己类相同的实例时,例如,链表列表节点,树节点和图节点,需要避免造成栈满异常(stackOverfloatError),尽量就不要属性是该类,除非是单例,通过static修饰一下
  23. 实例初始化操作抛出的任何异常都会传播给构造器。
  24. 令人混淆的构造器案例:
    1. 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);// array
        }
    }
  1. 啊呀!我的猫变成狗了
    1. 非静态方法可以直接访问静态方法,不加类名。
    2. final类不可继承;final方法能继承,但不能被子类重写;final变量是常量
    3. 静态方法被继承到子类中,那么也可以直接通过SubClass.staticMethod()调用;
  2. 我所得到的都是静态的
    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();//woof
        nipper.bark();//woof    
    }
  1. 对静态方法的调用不存在任何动态的分派机制,在编译时刻已经确定,因为他们都被声明为Dog类型的,所以都会调用Dog类型的静态方法,输出woof;
  2. 更改方法:去掉static,因为静态的东西是在编译期就定了,去掉的话,就是重写,而不是隐藏了。
  3. 谜题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());//-1930
        }
    }
    //1. new Elvls时候,会触发类被初始化,但是该类已经在初始化了,所以会被忽略掉;
    //2.类初始化:先执行所有静态域,并给初始值,然后再执行静态域右边的表达式,来给静态域初始值;
  1. 谜题50,不是你的类型:
    1. null对于每一个引用类型来说都是子类型,但是当instanceof操作符左侧为null时返回false
    2. new Type() instanceof String instanceof操作符要求:如果两个操作数的类型都是类,其中一个必须是另一个子类型,会在编译器就检测出来。
    3. Type type = (Type) new Object()会在运行期报错,但是可以编译通过。
  2. 不要在构造器中调用被子类重写过的方法。
  3. 助手方法(静态方法)通常都优于静态代码块,因为方法可以命名。
  4. Null和Void
    public class Type{
        public static void greet(){
            System.out.println("hello word");
        }
        public static void main(String[] args){
            ((Type)null).greet();//输出了hello word
        }
    }
    //1.静态方法调用在编译器已经确定,
    //2.静态方法的调用前用类名做限定,不要用表达式
  1. Java语言规范不允许本地变量声明语句作为一条语句在for,while,do循环中while Type type = new Type()需要加{}括起来。
  2. BigInteger,BigDecimal,包装类,String都是不可变的,改动之后需要用新的接受String aa = "aa"; aa = aa + "bb";
  3. equal方法重写,可以先用instanceof判断一下类型,相同不,再比较值。比较两个实例是否相等时,可以先判断引用是否相等
  4. split接受的是一个正则表达式;
  5. 对于陌生的数组,可以使用Arrays.deepToString()打印出来
  6. Math.abs(Integer.MIN_VALUE)Integer.MIN_VALUE
  7. 谜题66:一件私事:重写(方法)和隐藏(属性)的区别,重写除非在子类中通过super可以调用到父类的方法,否则调不到了,而隐藏可以转型到父类,就可以访问到了;当声明“属性”,“静态方法”,“内部类”时,如果和父类的名称相同了,会发生隐藏。
  8. final修饰方法时,对于实例方法表示不能“重写”,对于静态方法来说,不能“隐藏”
  9. 隐藏一个成员将阻止其被继承。
  10. 谜题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();//内部是通过wait实现;这里释放的是主线程的cup和锁,也就是谁当前拥有该对象的锁;
        //join:将限定符线程加入到当前线程之前,这里是this,也就是worker线程
    }
    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();
    }
}
//首先,worder开始工作,在300s是看一下还没有下班,然后接着睡觉工作,
//等到400s的时候,主线程设置了下班了,并等待worker线程执行完成,释放了锁和cpu;
//等到500s的时候,Timer线程又开始工作了。
//等到600s的时候,工人查看,还是工作时间;所以就这样一直在工作;不会有输出;
//以上的问题出在  主线程 下班的时候,释放了 worker对象的锁,导致Timer可以进入,所以只需要在分开造两个锁就可以了。
private final Object lock = new Object();
public void quit(){
    synchronized(lock){//keepWorking也同步这个锁
        flag = true;
        join();//这里释放就是当前对象的锁,但 不是  lock这个锁;
    } 
} 
  1. 谜题85,:不要在类初始化的时候启动任何线程,也就是静态代码块中
  2. 谜题89:泛型迷药
    1. 非静态成员类可以访问外部类的泛型,所以该类是不需要自己的泛型的;
    2. 静态成员类需要自己的泛型;
    3. 优先考虑用静态成员类代替非静态成员类;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章