java 基础
来源:互联网 发布:jquery ajax get json 编辑:程序博客网 时间:2024/06/08 03:04
类的初始化顺序
在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 Parent25 {26 public static StaticVarible staticVarible= new StaticVarible("子类-静态变量1"); 27 public StaticVarible instVarible= new StaticVarible("子类-成员变量1");28 29 static30 {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 StaticVarible50 {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 : truev1 == v3 : falsev4 == v5 : truev4 == v6 : falsev4 == v7 : truev4 == v8 : falsev4 == v9 :truev4 == v11 :truev10 == 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 ParentDef21 {22 public static final String staticValue = "父类静态变量";23 public String value = "父类实例变量";24 }25 26 class ChildDef extends ParentDef27 {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 Value48 {49 public int value;50 }
它的输出结果:
v:0value.value:0=====change=====v:0value.value:0=====add=====v:0value.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 try13 {14 throw new RuntimeException();15 }16 catch(Exception ex)17 {18 ex.printStackTrace();19 }20 finally21 {22 System.out.println("Finally语句被执行");23 }24 try25 {26 System.out.println("Hello World");27 return "Hello World";28 }29 catch(Exception ex)30 {31 ex.printStackTrace();32 }33 finally34 {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 try46 {47 if (i == 2) break;48 System.out.println(i);49 }50 finally51 {52 System.out.println("Finally语句被执行");53 }54 }55 }56 57 private static Test finallyTest3()58 {59 try60 {61 return new Test();62 }63 finally64 {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 WorldFinally语句被执行0Finally语句被执行1Finally语句被执行Finally语句被执行Test实例被创建Finally语句被执行
注意在循环的过程中,对于某一次循环,即使调用了break或者continue,finally也会执行。
- Java基础01:基础
- Java基础:基础加强
- Java基础-基础
- java基础的基础
- JAVA基础---基础常识
- Java基础:基础加强
- [Java 基础]基础语法
- Java基础
- java基础
- java 基础
- java基础
- Java基础
- Java基础
- Java基础
- JAVA基础
- JAVA基础
- JAVA基础
- java基础
- 10.子程序的高级特性1
- SSh服务端口转发
- 在shell中使用echo命令输出带颜色的文本
- 识别简单的验证码
- 毕向东Java学习过程
- java 基础
- 两个关于进制转换的经典面试题
- 在云服务器上搭建JSP环境并发布web项目(通过域名访问自己写的项目)
- top K问题
- 1791 合法括号子段
- 超实数(Hyper-reals)是什么人发明的?
- java使用RSA加密方式,实现数字签名
- 计蒜客NOIP模拟赛 小区划分
- cannot create 表全名