标签:format eric xca 包装 for 成功 内联 静态块 缓存
开发莫忘基础,写业务写多了很多基础内容容易忘。这里将寻根溯源,总结Java语言规范和基础类中的一些细节问题。所有关于Java语言规范的细节问题,都可以参考 The Java? Language Specification, Java SE 8 Edition(JLS8) .
本文将不断补充。。
Math.floor(x)返回小于等于x的最接近整数,返回类型为double;Math.round(x)相当于四舍五入,返回值为long或int;Math.ceil(x)返回大于等于x的最接近整数,返回类型为double。静态块:用static申明,JVM加载类时执行,仅执行一次且优先于主函数。
构造块:类中直接用{}定义,每一次创建对象时执行,相当于往构造器最前面加上构造块的内容(很像往每个构造器那里插了内联函数,构造块就相当于内联函数)。
执行顺序优先级:静态块 > 构造块 > 构造方法
有继承关系时,执行顺序通常是:父类静态块=>子类静态块=>父类构造块=>父类构造方法=>子类构造块=>子类构造方法
测试:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class test {     public static void main(String[] args) {         new Derived();     } } class Base {         static {             System.out.println("fucking => Base::static");         }         {             System.out.println("fucking => Base::before");         }         public Base() {             System.out.println("Base::Base<init>");         }     }     class Derived extends Base {         static {             System.out.println("fucking => Derived::static");         }         {             System.out.println("fucking => Derived::before");         }         public Derived() {             super();             System.out.println("Derived::Derived<init>");         }     } | 
输出:
| 1 2 3 4 5 6 | fucking => Base::static fucking => Derived::static fucking => Base::before Base::Base<init> fucking => Derived::before Derived::Derived<init> | 
代码片段:
| 1 2 3 4 5 | byte b1 = 1, b2 = 2, b3, b6; final byte b4 = 4, b5 = 6; b6 = b4 + b5; b3 = (b1 + b2); System.out.println(b3 + b6); | 
结果:第四行编译错误。
表达式的数据类型自动提升, 关于类型的自动提升,注意下面的规则。
byte,short,char型的值将被提升为int型long型,计算结果是long型float型,计算结果是float型double型,计算结果是double型而声明为final的变量会被JVM优化,因此第三句在编译时就会优化为b6 = 10,不会出现问题。
| 1 | if (fabs(x) < 0.00001f) | 
float类型的还有double类型的,这些小数类型在趋近于0的时候不会直接等于零,一般都是无限趋近于0。因此不能用==来判断。应该用|x-0| < err来判断,这里|x-0|表示绝对值,err表示限定误差,用程序表示就是fabs(x) < 0.00001f。
1.首先执行到try里的return,但是有finally语句还要执行,于是先执行return后面的语句,例如(x++),把要返回的值保存到局部变量。
2.执行finally语句的内容,其中有return语句,这时就会忽略try中的return,直接返回。
返回值问题。可以认为try(或者catch)中的return语句的返回值放入线程栈的顶部:如果返回值是基本类型则顶部存放的就是值,如果返回值是引用类型,则顶部存放的是引用。finally中的return语句可以修改引用所对应的对象,无法修改基本类型。但不管是基本类型还是引用类型,都可以被finally返回的“具体值”具体值覆盖。
三目运算符里的类型必须一致,比如下面的代码:
| 1 2 3 4 |  int i = 40; String as_e1 = String.valueOf(i < 50 ? 233 : 666); String as_e2 = String.valueOf(i < 50 ? 233 : 666.0); assertEquals(true, as_e1.equals(as_e2)); | 
结果是测试不通过,这里就涉及到三元操作符的转换规则:
Object对象int => long => float => double观察下面的一段代码:
| 1 2 3 4 5 6 7 8 9 10 | public class AutoIncTraps {     public static void main(String[] args) {         int count = 0;         for(int i = 0; i < 10; i++) {             count = count++;         }         System.out.println(count);     } } | 
这段代码的打印结果是0,也就是说自增在这里并没有什么卵用,这和C++是不一样的。反编译一下看一下字节码(main函数部分):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public static main([Ljava/lang/String;)V    L0     LINENUMBER 6 L0     ICONST_0     ISTORE 1    L1     LINENUMBER 7 L1     ICONST_0     ISTORE 2    L2    FRAME APPEND [I I]     ILOAD 2     BIPUSH 10     IF_ICMPGE L3    L4     LINENUMBER 8 L4     ILOAD 1     IINC 1 1     ISTORE 1    L5     LINENUMBER 7 L5     IINC 2 1     GOTO L2    L3     LINENUMBER 10 L3    FRAME CHOP 1     GETSTATIC java/lang/System.out : Ljava/io/PrintStream;     ILOAD 1     INVOKEVIRTUAL java/io/PrintStream.println (I)V    L6     LINENUMBER 11 L6     RETURN | 
这里相当于创建了一个局部变量存放count++,但没有返回,因此count相当于没变。看了字节码后可能没感觉,写一下编译器处理后的代码吧:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class AutoIncTraps {     public AutoIncTraps() {     }     public static void main(String[] args) {         byte count = 0;         for(int i = 0; i < 10; ++i) {             int var3 = count + 1;             count = count;         }         System.out.println(count);     } } | 
总结一下这里count的处理流程:
单纯看这一个的字节码比较抽象,来看一下这三句的字节码,比较一下更容易理解:
| 1 2 3 | count = ++count; count = count++; count++; | 
字节码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 |  L4  LINENUMBER 9 L4  IINC 1 1  ILOAD 1  ISTORE 1 L5  LINENUMBER 10 L5  ILOAD 1  IINC 1 1  ISTORE 1 L6  LINENUMBER 11 L6  IINC 1 1 | 
另外,自增操作不是原子操作,在后边总结并发编程的时候会涉及到。
instanceof操作符左右两边的操作数必须有继承或派生关系,否则不会编译成功。因此,instanceof操作符只能用于对象,不能用于基本类型(不会自动拆包)。
下面是一些典型的例子:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class FuckingIOF {     public void test() {         List<Object> list = new ArrayList<>();         list.add("String" instanceof Object);         list.add(new String() instanceof Object);         list.add(new Object() instanceof String);         //list.add(‘a‘ instanceof Character); //此句会编译错误         list.add(null instanceof String);         list.add((String)null instanceof String);         list.add(null instanceof Object);         list.add(new Generic<String>().isDataInstance(""));         list.forEach(System.out::println);     } } class Generic<T> {     public boolean isDataInstance(T t) {         return t instanceof Date;     } } | 
运行结果和分析:
| 1 2 3 4 5 6 7 | true => String是Object的子类 true => 同上 false => 同上 false => Java语言规范规定null instanceof ? 都是false false => 同上,无论怎么转换还是null false => 同上 false => 由于Java泛型在编译时会进行类型擦除,因此这里相当于Object instanceof Date了 | 
根据 JLS8 4.2.3,对NaN有以下规定:
注意到Double.NaN == Double.NaN返回false,这其实是遵循了IEEE 754 standard。NaN 代表一个非正常的数(比如除以0得到的数),其定义为:
| 1 2 3 4 5 6 | /**  * A constant holding a Not-a-Number (NaN) value of type  * {@code double}. It is equivalent to the value returned by  * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.  */ public static final double NaN = 0.0d / 0.0; | 
这个问题是在StackOverflow上看到的。以下三个表达式:
| 1 2 3 | System.out.println(Integer.valueOf("127") == Integer.valueOf("127")); System.out.println(Integer.valueOf("128") == Integer.valueOf("128")); System.out.println(Integer.parseInt("128") == Integer.valueOf("128")); | 
结果分别是:
| 1 2 3 | true false true | 
为什么是这样的结果呢?我们看一下valueOf方法的源码:
| 1 2 3 4 5 6 7 8 9 | public static Integer valueOf(String s) throws NumberFormatException {     return Integer.valueOf(parseInt(s, 10)); } public static Integer valueOf(int i) {     if (i >= IntegerCache.low && i <= IntegerCache.high)         return IntegerCache.cache[i + (-IntegerCache.low)];     return new Integer(i); } | 
可以看到valueOf方法是在parseInt方法的基础上加了一个读取缓存的过程。我们再看一下IntegerCache类的源码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /**  * Cache to support the object identity semantics of autoboxing for values between  * -128 and 127 (inclusive) as required by JLS.  *  * The cache is initialized on first usage.  The size of the cache  * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.  * During VM initialization, java.lang.Integer.IntegerCache.high property  * may be set and saved in the private system properties in the  * sun.misc.VM class.  */ private static class IntegerCache {     static final int low = -128;     static final int high;     static final Integer cache[];     static {         // high value may be configured by property         int h = 127;         String integerCacheHighPropValue =             sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");         if (integerCacheHighPropValue != null) {             try {                 int i = parseInt(integerCacheHighPropValue);                 i = Math.max(i, 127);                 // Maximum array size is Integer.MAX_VALUE                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);             } catch( NumberFormatException nfe) {                 // If the property cannot be parsed into an int, ignore it.             }         }         high = h;         cache = new Integer[(high - low) + 1];         int j = low;         for(int k = 0; k < cache.length; k++)             cache[k] = new Integer(j++);         // range [-128, 127] must be interned (JLS7 5.1.7)         assert IntegerCache.high >= 127;     }     private IntegerCache() {} } | 
原来JVM会缓存一部分的Integer对象(默认范围为-128 - 127),在通过valueOf获取Integer对象时,如果是缓存范围内的就直接返回缓存的Integer对象,否则就会new一个Integer对象。返回的上限可通过JVM的参数-XX:AutoBoxCacheMax=<size>设置,而且不能小于127(参照JLS 5.1.7)。这样我们就可以解释Integer.valueOf("127") == Integer.valueOf("127")为什么是true了,因为它们获取的都是同一个缓存对象,而默认情况下Integer.valueOf("128") == Integer.valueOf("128")等效于new Integer(128) == new Integer(128),结果自然是false。
我们再来看一下parseInt方法的原型,它返回一个原生int值:
| 1 | public static int parseInt(String s) throws NumberFormatException | 
由于一个原生值与一个包装值比较时,包装类型会自动拆包,因此Integer.parseInt("128") == Integer.valueOf("128")就等效于128 == 128,结果自然是true。
本文标题:Java基础技术细节总结 - 语言规范
文章作者:sczyh30
发布时间:2015年09月18日
原始链接:http://www.sczyh30.com/posts/Java/java-basic-summary-01/
许可协议: "知识共享-保持署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。
标签:format eric xca 包装 for 成功 内联 静态块 缓存
原文地址:http://www.cnblogs.com/lsohvaen001/p/7857890.html