java 解惑系列

谜题一:数据运算的小问题

  1)当问题需要精确答案的时候,要避免使用float 还有double类型,要使用int long BigDecimal类型。这是因为在java虚拟中运算的时候并不是所有的小数都可以表示成二进制的浮点数的精确表示。因此在做商业计算的时候(类似银行计算的时候)推荐使用BigDecimal  当然构造的时候也不要传递一个double类型 用字符串代替。

  2)常整除。对于多个数相乘的过程:long x =24*60*60*1000*1000 在这个运算的过程中会造成int 类型的溢出,因为java不具有目标确定类型的特性所以在两个int类型的数相乘的话还是会得到一个int类型 因此在最后才会才会转为long 但是在此时的运算的过程中早已溢出 所以得到的结果会不准确。解决办法 第一个操作数为目的类型。

  3)负的十进制常量可以很明显的用一个减号来表示,但是十六进制还有八进制不是这样的他们可以有正的还有负的值。如果十六进制还有八进制字常量的最高位置被置位德话,那么他们就是负数。又因为在int类型是有符号整数类型,所以在升级为long的时候会进行符号拓展。

符号拓展的规则:如果最初的数值类型是有符号的话,那么就进行符号拓展(如果符号位为1 就拓展为1,如果符号位为0,则拓展为0)。如果他是char 则不论他被提升到什么精度都是进行0拓展。

  4)混合类型计算问题:如果第二个还有第三个操作数具有相同的数据类型,那么它就是表达式的数据类型。

  如果第一表达式的类型是char byte short 另一个是int的常量表达式 就是1,2这样的数字,那么表达式的类型就是char int byte 否则就会对操作数的数据类型进行提升。表达式的类型就是被提升过的类型。

  2 字符之谜

  1)对于字符串中的+ 在char中是不适用的,但且仅当+操作符的操作数中至少有一个是String类型的时候才会执行字符串的连接。

  2)在char【】的打印的问题上是 会调用toString方法。所以要是想要打印出数组中的内容的时候可以调用String.valueof();

  3) java 对在字符串常量中的Unicode转义字符没有提供任何的特殊处理。总之在字符串和字符字面常量中优先选择的转义字符序列,而不是Unicode转义字符。

  4) 在做字符串的替换的时候要注意有的函数的传递的参数的默认的形式为正则表达式,所以在替换的时候会出错。

  3 循环之谜

  1)当两个操作数都是被包装的数字类型(Integer,Double等)时,数值比较操作符和判等操作符的行为存在着根本的差别。数值比较操作符执行的是值的比较而  判等操作符是执行的引用  标志比较。

  4 异常之谜

  1)对于try-catch-finaly语句块,finally语句块总是在控制权离开try语句块的时候执行。无论try语句块是正常还是意外的结束,情况都是这样。意外的结束是指:在一条语句还是或者一条语句块的抛出一条异常,或者是以 return breadk continue 来结束try语句块。当然在finally语句快中尽量不要使用 上述关键字来结束该块,要尽量让其自然结束。

   2)如果一个catch子句要捕获一个类型为E的手检查的异常。而其对应的try子句中没用抛出对应的异常那么这就是一个编译期错误。不过对于捕获Exception 或Throwable的子句是合法的,不管对应的try子句内容如何。

  3)一个方法可以抛出的受检查的异常是它所适用的所有类型生明要抛出的受检查异常集合的交集。

      4)java的异常检查机制并不是虚拟机强制执行的。它只是编译期工具,被设计用来帮助我们写出正确的程序,但是可以再运行期绕过它。

  5)泛型信息是在编译期而非运行期检查的。

   5 类之谜

  1)java的重载解析过程是 分两阶段进行的。第一阶段选取可获得的并且可应用的方法或者构造器。第二阶段 在第一个阶段的基础上选取的方法或者构造器中选取最      精确的一个。如果一个方法的或者构造器可以接受传递给另一个方法或者构造器的任何参数,那么就说第一个方法比第二个方法 缺乏精确性。

    2)静态方法的调用不存在任何的动态的分派机制。当一个程序调用静态的方法的时候,被调用的的方法都是在编译期被选定的。这种选定是基于修饰符的编译期类型。

  也就是说静态方法的调用的限定表达式是可以计算的,但是他的值将被忽略,即调用只是取决于编译期的类型。((Null)null).greet();测试用例。

 6 库之谜

  1)BigInteger实例是不可变的。举例ss.add(某个Biginteger值)此时ss值没变,但是函数返回的两数之和。

  2)重写equals方法:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

  3) 要避免重用平台类的名字,并且千万不要重用java.lang 中的类名,因为这些名字会被各处的程序自动加载。因为习惯以无限定的形式出现,并且很自然的认为这是java.lang 中的类名。 所以当当这个名字在其自己的包内被使用时,该名字的无限定形式将会引用到新的定义上。

     4)一个包内的私有方法不能被位于另一个包中的某个方法直接覆盖。

     5)当你 调用一个线程的start方法时要多加小心,别弄成run。如果弄错的话就成了方法的调用并不会运行创建的线程。

      6)反射的调用  :尽量使用表示某种可访问类型的Class 对象,即Method m=Iterator.class.getmethod() 避免使用object.getclass().getmethod() 因为后者在后续的运行过程中 会出现7)中的问题。

      7)访问位于其他包中的非公共类型的成员是不合法的。

  7、高级谜题

package test;
import java.util.*;
public class Pair <T>{
    private final T first;
    private final T second;
    public Pair(T first,T second){
        this.first = first;
        this.second = second;
    }
    
    public T first(){
        return first;
    }
    
    public T second(){
        return second;
    }
    
    public List<String> stringList(){
        return Arrays.asList(String.valueOf(first),String.valueOf(second));
    }
    
    public static void main(String[] args){
        Pair<Object> p = new Pair<Object>(23,"skidoo");
        System.out.println(p.first()+""+p.second());
        for(String s:p.stringList())
            System.out.println(s+"");
    }
}
View Code

 上面的程序是会在编译的期间出现错误的,这是因为程序出现了原始类型而引起的。一个原始类型就是一个没有任何类型参数的泛型类后者泛型接口的名字。例如,List<E> 是个泛型接口List<String> 就是一个参数化的的类型,list就是一个原始类型。在我们的程序中,唯一用到原始类型的地方就是main方法中对变量P的声明

Pair p = new Pair<Object>(23,"skioo");

   一个原始类型很像其对应的参数化的类型,但是它的所有实例成员都要被替换掉,而替换物就是这些实例成员被擦掉的对应部分之后剩下的东西。具体的说就是,在一个实例方法声明中出现的每个参数化类型。都要被其对应的原始部分所取代。我们程序中的变量P是属于原始类型Pair的。所以它的实例化的方法都要执行这种擦除。这也包括了声明返回List<String> 的方法stringList,编译器会将这个方法解释为返回原始类型的List。解决方法:Pair<Object> p = new Pair<Object>(23,"skioo");

注意:原始类型List和参数化类型List<Object>是不一样的。

8 内部类问题。

   1)要想实例化一个内部类,需要提供一个外围类的实例给构造器。一般情况下,他是隐式的传递给构造器的,但是它也可以是以express.super()的方式通过超类构造器调用。如果外围类实例是隐式传递的,编译器会自动产生表达式:它使用this  来指代最内部的超类是一个成员变量的外围类。

public class Outer {
   class Inner1 extends Outer{}
   class Inner2 extends Inner1{}

}
View Code

 上述代码中那个超类就是Inner1.因为当前类Inner2间接扩展了Outer类,Inner1 便是它的一个继承而来的成员。因此,超类构造器的限定表达式直接就是this。编译器提供外围类实例,将super重写成this.super 。可以看出此处会出现错误 默认的Inner2的构造器试图在超类构造器被调用前调用this,这是一个非法的操作。

 解决方法:无论何时你写了一个成员类,都要问你自己,是否这个成员类真的需要使用外围类实例?如果答案否定的,那么应该就把它设定为静态的。

9 this 以及super 关键字的理解

  有些人认为super 与this 引用是类似的概念,实际上这样比较并不太适当,因为super 不是一个对象的引用,不能将super赋给另一个对象的变量,他只是一个Java里面指示编译器调用超类方法的关键字。

this有两个用途:1 引用隐式参数,2是调用该类的其他的构造器,super也有两个用途:1 调用超类的方法 2 调用超类的构造器。

调用构造器的语句只能作为另一个构造器的第一条语句出现。

posted on 2015-03-15 11:18  duoyu  阅读(281)  评论(0编辑  收藏  举报

导航