Java回顾之一些根蒂根基概念

    添加时间:2013-5-14 点击量:

      第一篇:Java回顾之I/O


      第二篇:Java回顾之收集通信


      第三篇:Java回顾之多线程


      第四篇:Java回顾之多线程同步


      第五篇:Java回顾之凑集


      第六篇:Java回顾之序列化


      第七篇:Java回顾之反射



      这两天,无意间在网上翻到一本关于Java口试解惑的文章集,里面提到了很多根蒂根基的概念,但一不留心,还是可能会“掉到坑里”。里面的文章写的很不错,大师可以经由过程下面的地址:http://zangweiren.iteye.com/blog/241218


      在看上述文章的时辰,顺手写了一些测试代码,以便加深懂得。这也就是这篇文章的起原了。


      类的初始化次序


      在Java中,类里面可能包含:静态变量,静态初始化块,成员变量,初始化块,机关函数。在类之间可能存在着持续关系,那么实例化一个对象时,上述各项目组的加载次序是如何的?


      起首来看代码:



     1 class Parent
    
    2 {
    3 public static StaticVarible staticVarible= new StaticVarible(父类-静态变量1);
    4 public StaticVarible instVarible= new StaticVarible(父类-成员变量1);
    5
    6 static
    7 {
    8 System.out.println(父类-静态块);
    9 }
    10
    11 {
    12 System.out.println(父类-初始化块);
    13 }
    14
    15 public static StaticVarible staticVarible2= new StaticVarible(父类-静态变量2);
    16 public StaticVarible instVarible2= new StaticVarible(父类-成员变量2);
    17
    18 public Parent()
    19 {
    20 System.out.println(父类-实例机关函数);
    21 }
    22 }
    23
    24 class Child extends Parent
    25 {
    26 public static StaticVarible staticVarible= new StaticVarible(子类-静态变量1);
    27 public StaticVarible instVarible= new StaticVarible(子类-成员变量1);
    28
    29 static
    30 {
    31 System.out.println(子类-静态块);
    32 }
    33
    34 public Child()
    35 {
    36 System.out.println(子类-实例机关函数);
    37 }
    38
    39 {
    40 System.out.println(子类-初始化块);
    41 }
    42
    43 public static StaticVarible staticVarible2= new StaticVarible(子类-静态变量2);
    44 public StaticVarible instVarible2= new StaticVarible(子类-成员变量2);
    45
    46
    47 }
    48
    49 class StaticVarible
    50 {
    51 public StaticVarible(String info)
    52 {
    53 System.out.println(info);
    54 }
    55 }


      然后履行下面的语句:



    1 Child child = new Child();


      输出成果如下:



    父类-静态变量1
    
    父类
    -静态块
    父类
    -静态变量2
    子类
    -静态变量1
    子类
    -静态块
    子类
    -静态变量2
    父类
    -成员变量1
    父类
    -初始化块
    父类
    -成员变量2
    父类
    -实例机关函数
    子类
    -成员变量1
    子类
    -初始化块
    子类
    -成员变量2
    子类
    -实例机关函数


      结论  


      从上述成果可以看出,在实例化一个对象时,各项目组的加载次序如下:


      父类静态成员/父类静态初始化块 -> 子类静态成员/子类初始化块 -> 父类成员变量/父类初始化块 -> 父类机关函数 -> 子类成员变量/子类初始化块 -> 子类机关函数


      和String相干的一些事儿


      起首,我们聊一聊Java中堆和栈的事儿。



    • 栈:存放根蒂根基类型,包含char/byte/short/int/long/float/double/boolean

    • 堆:存放引用类型,同时一般会在栈中保存一个指向它的指针,垃圾收受接管断定一个对象是否可以收受接管,就是断定栈中是否有指针指向堆中的对象。


      String作为一种特别的数据类型,它不完全等同于根蒂根基类型,也不是全部的引用类型,很多口试题都有它的身影。


      String类型变量的存储布局


      String的存储布局分为两项目组,我们以String a = abc;为例,描述String类型的存储体式格式:


      1)在栈中创建一个char数组,值分为是a,b,c。


      2)在堆中创建一个String对象。


      Java中的字符串池


      为了节俭空间和资料,JVM会保护一个字符串池,或者说会缓存一项目组曾经呈现过的字符串。


      例如下面的代码:



    1 String v1 = ab;
    
    2 String v2 = ab;


      实际上,v1==v2,因为JVM在v1声明后,已经对“ab”进行了缓存。


      那么JVM对字符串进行缓存的根据是什么?我们来看下面的代码,很是有意思:



     1 public class StringTest {
    
    2 public static final String constValue = ab;
    3 public static final String staticValue;
    4
    5 static
    6 {
    7 staticValue=ab;
    8 }
    9
    10 public static void main(String[] args)
    11 {
    12 String v1 = ab;
    13 String v2 = ab;
    14 System.out.println(v1 == v2 : + (v1 == v2));
    15 String v3 = new String(ab);
    16 System.out.println(v1 == v3 : + (v1 == v3));
    17 String v4 = abcd;
    18 String v5 = ab + cd;
    19 System.out.println(v4 == v5 : + (v4 == v5));
    20 String v6 = v1 + cd;
    21 System.out.println(v4 == v6 : + (v4 == v6));
    22 String v7 = constValue + cd;
    23 System.out.println(v4 == v7 : + (v4 == v7));
    24 String v8 = staticValue + cd;
    25 System.out.println(v4 == v8 : + (v4 == v8));
    26 String v9 = v4.intern();
    27 System.out.println(v4 == v9 : + (v4 == v9));
    28 String v10 = new String(new char[]{a,b,c,d});
    29 String v11 = v10.intern();
    30 System.out.println(v4 == v11 : + (v4 == v11));
    31 System.out.println(v10 == v11 : + (v10 == v11));
    32 }
    33 }


      请重视它的输出成果:



    v1 == v2 : true
    
    v1
    == v3 : false
    v4
    == v5 : true
    v4
    == v6 : false
    v4
    == v7 : true
    v4
    == v8 : false
    v4
    == v9 :true
    v4
    == v11 :true
    v10
    == v11 :false


      我们会发明,并不是所有的断定都返回true,这似乎和我们上方的说法有抵触了。其实不然,因为


      结论


      1. JVM只能缓存那些在编译时可以断定的常量,而非运行时常量。


        上述代码中的constValue属于编译时常量,而staticValue则属于运行时常量。


      2. 经由过程应用 new体式格式创建出来的字符串,JVM缓存的体式格式是不一样的。


        所以上述代码中,v1不等同于v3。


      String的这种设计属于享元模式吗?


      这个话题斗劲有意思,大项目组讲设计模式的文章,在谈到享元时,一般就会拿String来做例子,但它属于享元模式吗?


      字符串与享元的关系,大师可以参考下面的文章:http://www.cnblogs.com/winter-cn/archive/2012/01/21/2328388.html


      字符串的反转输出


      这种景象下,一般会将字符串看做是字符数组,然后哄骗反转数组的体式格式来反转字符串。


      目炫纷乱的办法调用


      有持续关系布局中的办法调用


      持续是面向对象设计中的常见体式格式,它可以有效的实现”代码复用“,同时子类也有重写父类办法的,这就对到底是调用父类办法还是子类办法带来了麻烦。


      来看下面的代码:



     1 public class PropertyTest {
    
    2
    3 public static void main(String[] args)
    4 {
    5 ParentDef v1 = new ParentDef();
    6 ParentDef v2 = new ChildDef();
    7 ChildDef v3 = new ChildDef();
    8 System.out.println(=====v1=====);
    9 System.out.println(staticValue: + v1.staticValue);
    10 System.out.println(value: + v1.value);
    11 System.out.println(=====v2=====);
    12 System.out.println(staticValue: + v2.staticValue);
    13 System.out.println(value: + v2.value);
    14 System.out.println(=====v3=====);
    15 System.out.println(staticValue: + v3.staticValue);
    16 System.out.println(value: + v3.value);
    17 }
    18 }
    19
    20 class ParentDef
    21 {
    22 public static final String staticValue = 父类静态变量;
    23 public String value = 父类实例变量;
    24 }
    25
    26 class ChildDef extends ParentDef
    27 {
    28 public static final String staticValue = 子类静态变量;
    29 public String value = 子类实例变量;
    30 }


      输出成果如下:



    =====v1=====
    
    staticValue:父类静态变量
    value:父类实例变量
    =====v2=====
    staticValue:父类静态变量
    value:父类实例变量
    =====v3=====
    staticValue:子类静态变量
    value:子类实例变量


      结论


      对于调用父类办法还是子类办法,只与变量的声明类型有关系,与实例化的类型没有关系。


      到底是值传递还是引用传递


      对于这个话题,我的概念是值传递,因为传递的都是存储在栈中的内容,无论是根蒂根基类型的值,还是指向堆中对象的指针,都是值而非引用。并且在值传递的过程中,JVM会将值复制一份,然后将复制后的值传递给调用办法。


      遵守这种体式格式,我们来看下面的代码:



     1 public class ParamTest {
    
    2
    3 public void change(int value)
    4 {
    5 value = 10;
    6 }
    7
    8 public void change(Value value)
    9 {
    10 Value temp = new Value();
    11 temp.value = 10;
    12 value = temp;
    13 }
    14
    15 public void add(int value)
    16 {
    17 value += 10;
    18 }
    19
    20 public void add(Value value)
    21 {
    22 value.value += 10;
    23 }
    24
    25 public static void main(String[] args)
    26 {
    27 ParamTest test = new ParamTest();
    28 Value value = new Value();
    29 int v = 0;
    30 System.out.println(v: + v);
    31 System.out.println(value.value: + value.value);
    32 System.out.println(=====change=====);
    33 test.change(v);
    34 test.change(value);
    35 System.out.println(v: + v);
    36 System.out.println(value.value: + value.value);
    37 value = new Value();
    38 v = 0;
    39 System.out.println(=====add=====);
    40 test.add(v);
    41 test.add(value);
    42 System.out.println(v: + v);
    43 System.out.println(value.value: + value.value);
    44 }
    45 }
    46
    47 class Value
    48 {
    49 public int value;
    50 }


      它的输出成果:



    v:0
    
    value.value:
    0
    =====change=====
    v:
    0
    value.value:
    0
    =====add=====
    v:
    0
    value.value:
    10


      我们看到,在调用change办法时,即使我们传递进去的是指向对象的指针,但终极对象的属性也没有变,这是因为在change办法体内,我们新建了一个对象,然后将”复制过的指向原对象的指针“指向了“新对象”,并且对新对象的属性进行了调剂。然则“复制前的指向原对象的指针”依然是指向“原对象”,并且属性没有任何变更。


      final/finally/finalize的差别


      final可以润饰类、成员变量、办法以及办法参数。应用final润饰的类是不成以被持续的,应用final润饰的办法是不成以被重写的,应用final润饰的变量,只能被赋值一次。


      应用final声明变量的赋值机会:


      1)定义声明时赋值


      2)初始化块或静态初始化块中


      3)机关函数


      来看下面的代码:



     1 class FinalTest
    
    2 {
    3 public static final String staticValue1 = 静态变量1;
    4 public static final String staticValue2;
    5
    6 static
    7 {
    8 staticValue2 = 静态变量2;
    9 }
    10
    11 public final String value1 = 实例变量1;
    12 public final String value2;
    13 public final String value3;
    14
    15 {
    16 value2 = 实例变量2;
    17 }
    18
    19 public FinalTest()
    20 {
    21 value3 = 实例变量3;
    22 }
    23 }


      finally一般是和try...catch放在一路应用,首要用来开释一些资料。


      我们来看下面的代码:



     1 public class FinallyTest {
    
    2
    3 public static void main(String[] args)
    4 {
    5 finallyTest1();
    6 finallyTest2();
    7 finallyTest3();
    8 }
    9
    10 private static String finallyTest1()
    11 {
    12 try
    13 {
    14 throw new RuntimeException();
    15 }
    16 catch(Exception ex)
    17 {
    18 ex.printStackTrace();
    19 }
    20 finally
    21 {
    22 System.out.println(Finally语句被履行);
    23 }
    24 try
    25 {
    26 System.out.println(Hello World);
    27 return Hello World;
    28 }
    29 catch(Exception ex)
    30 {
    31 ex.printStackTrace();
    32 }
    33 finally
    34 {
    35 System.out.println(Finally语句被履行);
    36 }
    37 return null;
    38 }
    39
    40 private static void finallyTest2()
    41 {
    42 int i = 0;
    43 for (i = 0; i < 3; i++
    44 {
    45 try
    46 {
    47 if (i == 2) break;
    48 System.out.println(i);
    49 }
    50 finally
    51 {
    52 System.out.println(Finally语句被履行);
    53 }
    54 }
    55 }
    56
    57 private static Test finallyTest3()
    58 {
    59 try
    60 {
    61 return new Test();
    62 }
    63 finally
    64 {
    65 System.out.println(Finally语句被履行);
    66 }
    67 }
    68 }


      履行成果如下:



    java.lang.RuntimeException
    
    at sample.interview.FinallyTest.finallyTest1(FinallyTest.java:
    16
    at sample.interview.FinallyTest.main(FinallyTest.java:
    7
    Finally语句被履行
    Hello World
    Finally语句被履行
    0
    Finally语句被履行
    1
    Finally语句被履行
    Finally语句被履行
    Test实例被创建
    Finally语句被履行


      重视在轮回的过程中,对于某一次轮回,即使调用了break或者continue,finally也会履行。


      finalize则首要用于开释资料,在调用GC办法时,该办法就会被调用。


      来看下面的示例:



     1 class FinalizeTest
    
    2 {
    3 protected void finalize()
    4 {
    5 System.out.println(finalize办法被调用);
    6 }
    7
    8 public static void main(String[] args)
    9 {
    10 FinalizeTest test = new FinalizeTest();
    11 test = null;
    12 Runtime.getRuntime().gc();
    13 }
    14 }


      履行成果如下:



    finalize办法被调用


      关于根蒂根基类型的一些事儿


      根蒂根基类型供分为9种,包含byte/short/int/long/float/double/boolean/void,每种根蒂根基类型都对应一个“包装类”,其他一些根蒂根基信息如下:



    1. 根蒂根基类型:byte 二进制位数:8
    
    2. 包装类:java.lang.Byte
    3. 最小值:Byte.MIN_VALUE=-128
    4. 最大值:Byte.MAX_VALUE=127
    5. 根蒂根基类型:short 二进制位数:16
    6. 包装类:java.lang.Short
    7. 最小值:Short.MIN_VALUE=-32768
    8. 最大值:Short.MAX_VALUE=32767
    9. 根蒂根基类型:int 二进制位数:32
    10. 包装类:java.lang.Integer
    11. 最小值:Integer.MIN_VALUE=-2147483648
    12. 最大值:Integer.MAX_VALUE=2147483647
    13. 根蒂根基类型:long 二进制位数:64
    14. 包装类:java.lang.Long
    15. 最小值:Long.MIN_VALUE=-9223372036854775808
    16. 最大值:Long.MAX_VALUE=9223372036854775807
    17. 根蒂根基类型:float 二进制位数:32
    18. 包装类:java.lang.Float
    19. 最小值:Float.MIN_VALUE=1.4E-45
    20. 最大值:Float.MAX_VALUE=3.4028235E38
    21. 根蒂根基类型:double 二进制位数:64
    22. 包装类:java.lang.Double
    23. 最小值:Double.MIN_VALUE=4.9E-324
    24. 最大值:Double.MAX_VALUE=1.7976931348623157E308
    25. 根蒂根基类型:char 二进制位数:16
    26. 包装类:java.lang.Character
    27. 最小值:Character.MIN_VALUE=0
    28. 最大值:Character.MAX_VALUE=65535


      关于根蒂根基类型的一些结论(来自《Java口试解惑》)



    • 未带有字符后缀标识的整数默认为int类型;未带有字符后缀标识的浮点数默认为double类型。

    • 若是一个整数的值超出了int类型可以或许默示的局限,则必须增长后缀“L”(不区分大小写,建议用大写,因为小写的L与阿拉伯数字1很轻易混合),默示为long型。

    • 带有“F”(不区分大小写)后缀的整数和浮点数都是float类型的;带有“D”(不区分大小写)后缀的整数和浮点数都是double类型的。

    • 编译器会在编译期对byte、short、int、long、float、double、char型变量的值进行搜检,若是超出了它们的取值局限就会报错。

    • int型值可以赋给所稀有值类型的变量;long型值可以赋给long、float、double类型的变量;float型值可以赋给float、double类型的变量;double型值只能赋给double类型变量。


      关于根蒂根基类型之间的转换


      下面的转换是无损精度的转换:



    • byte->short

    • short->int

    • char->int

    • int->long

    • float->double


      下面的转换是会丧失精度的:



    • int->float

    • long->float

    • long->double


      除此之外的转换,是不法的。


      和日期相干的一些事儿


      Java中,有两个类和日期相干,一个是Date,一个是Calendar。我们来看下面的示例:



     1 public class DateTest {
    
    2
    3 public static void main(String[] args) throws ParseException
    4 {
    5 test1();
    6 test2();
    7 test3();
    8 }
    9
    10 private static void test1() throws ParseException
    11 {
    12 Date date = new Date();
    13 System.out.println(date);
    14 DateFormat sf = new SimpleDateFormat(yyyy-MM-dd);
    15 System.out.println(sf.format(date));
    16 String formatString = 2013-05-12;
    17 System.out.println(sf.parse(formatString));
    18 }
    19
    20 private static void test2()
    21 {
    22 Date date = new Date();
    23 System.out.println(Year: + date.getYear());
    24 System.out.println(Month: + date.getMonth());
    25 System.out.println(Day: + date.getDate());
    26 System.out.println(Hour: + date.getHours());
    27 System.out.println(Minute: + date.getMinutes());
    28 System.out.println(Second: + date.getSeconds());
    29 System.out.println(DayOfWeek: + date.getDay());
    30 }
    31
    32 private static void test3()
    33 {
    34 Calendar c = Calendar.getInstance();
    35 System.out.println(c.getTime());
    36 System.out.println(c.getTimeZone());
    37 System.out.println(Year: + c.get(Calendar.YEAR));
    38 System.out.println(Month: + c.get(Calendar.MONTH));
    39 System.out.println(Day: + c.get(Calendar.DATE));
    40 System.out.println(Hour: + c.get(Calendar.HOUR));
    41 System.out.println(HourOfDay: + c.get(Calendar.HOUR_OF_DAY));
    42 System.out.println(Minute: + c.get(Calendar.MINUTE));
    43 System.out.println(Second: + c.get(Calendar.SECOND));
    44 System.out.println(DayOfWeek: + c.get(Calendar.DAY_OF_WEEK));
    45 System.out.println(DayOfMonth: + c.get(Calendar.DAY_OF_MONTH));
    46 System.out.println(DayOfYear: + c.get(Calendar.DAY_OF_YEAR));
    47 }
    48 }


      输出成果如下:



    Sat May 11 13:44:34 CST 2013
    
    2013-05-11
    Sun May
    12 00:00:00 CST 2013
    Year:
    113
    Month:
    4
    Day:
    11
    Hour:
    13
    Minute:
    44
    Second:
    35
    DayOfWeek:
    6
    Sat May
    11 13:44:35 CST 2013
    sun.util.calendar.ZoneInfo[id
    =Asia/Shanghai,offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
    Year:
    2013
    Month:
    4
    Day:
    11
    Hour:
    1
    HourOfDay:
    13
    Minute:
    44
    Second:
    35
    DayOfWeek:
    7
    DayOfMonth:
    11
    DayOfYear:
    131


      须要重视的是,Date中的getxxx办法已经变成deprecated了,是以我们尽量应用calendar.get办法来获取日期的细节信息。


      别的,重视DateFormat,它不仅可以对日期的输出进行格局化,并且可以逆向操纵,将合适Format的字符串转换为日期类型。

    原来,再大的房子,再大的床,没有相爱的人陪伴,都只是冰冷的物质。而如果身边有爱人陪伴,即使房子小,床小,也觉得无关紧要,因为这些物质上面有了爱的温度,成了家的元素。—— 何珞《婚房》#书摘#
    分享到: