Java编程思想(Chapter2、4、6)

一切皆对象

用引用操纵对象

Java中操纵的标识符实际上是对象的“引用”。例如想要操纵一个字符串,则可以创建一个String引用。
String s;
此处s只是一个引用。

存储位置

基本类型/对象的引用:堆栈
对象: 堆

作用域

作用域由{}确定,决定了在其中定义的变量名的可见性以及生命周期。
1 {
2     int x =12;
3     {
4         int x = 96; //illegal
5     }
6 }

非法,不能隐藏。

{
    String s = new String("str");
}
引用s在作用域终点消失,而s指向的String对象仍然占据着内存空间,直到gc销毁。

关于值传递还是引用传递的问题

Java中只存在值传递
1.基本类型的传递
  没有任何疑问,传值
2.对象的传递
  参数传递时,只存在传递基本类型和对象引用的问题,并不会直接传对象本身

一个方法不能修改一个基本数据类型的参数

一个方法可以改变一个对象参数的状态

一个方法不能让对象参数引用一个新的对象

参考知乎话题:java到底是值传递还是引用传递?传送门
对于参数是String类型,不修改原的原因在于 String 对象是final类型。

初始化与清理

初始化顺序

以一实例说明
public class Tag {
    public Tag(int marker) {
        System.out.println("tag"+marker);
    }
}
public class Card {
    Tag tag = new Tag(1);
    public Card() {
        System.out.println("card()");
        tag3 = new Tag(33);
    }
    Tag tag2 = new Tag(2);
    void f(){
        System.out.println("f()");
    }
    Tag tag3 = new Tag(3);
}
public class Init01 {
    public static void main(String[] args) {
        Card card = new Card();
        card.f();
    }
}

Result

tag1
tag2
tag3
card()
tag33
f()
View Code
可以看出先进行变量的初始化,再进行构造器调用。

在此基础上,如果还有静态数据时,顺序如何?

public class Bowl {
    public Bowl(int marker) {
        System.out.println("bowl "+marker);
    }
    protected void f(int marker){
        System.out.println("f "+marker);
    }
}
public class Table {
    static Bowl b1 = new Bowl(1);
    public Table(){
        System.out.println("table()");
        b2.f(1);
    }
    void f2(int marker){
        System.out.println("f2 "+marker);
    }
    static Bowl b2 =new Bowl(2);
}
public class Cupboard {
    Bowl b3 =new Bowl(3);
    static Bowl b4 = new Bowl(4);
    Cupboard(){
        System.out.println("cup()");
        b4.f(2);
    }
    void f3(int marker){
        System.out.println("f3 "+marker);
    }
    static Bowl b5 = new Bowl(5);
}
public class Test {
    public static void main(String[] args) {
        System.out.println("create cup in main");
        new Cupboard();
        System.out.println("create cup in main");
        new Cupboard();
        t2.f2(1);
        t3.f3(1);
    }
    static Table t2 = new Table();
    static Cupboard t3 = new Cupboard();
}

Result

bowl 1
bowl 2
table()
f 1
bowl 4
bowl 5
bowl 3
cup()
f 2
create cup in main
bowl 3
cup()
f 2
create cup in main
bowl 3
cup()
f 2
f2 1
f3 1
View Code
可以看出,先进行static静态数据的初始化(只执行一次),再普通变量的初始化,最后再调用构造器。

数组的初始化

每次访问数组时,都将进行数组的边界检测。

复用类

带参数的构造器

如果类没有缺省的参数,或者想调用一个带参数的基类构造器,则必须用super显示地编写调用基类构造器的语句。
public class C3 extends C2{
    public C3(int i) {
        super(i);//
        System.out.println("c3");
    }
    public static void main(String[] args) {
        new C3(1);
    }
}
class C1{
    public C1(int i) {
        System.out.println("c1");
    }
}
class C2 extends C1{
    public C2(int i) {
        super(i);//
        System.out.println("c2");
    }
}
此处,两个super(i)都是必须,不然会报错。

初始化及类的加载

public class Insect {
    protected int i = 9;
    protected int j;
    protected int x3 = print("static Insect.x3 init");
    public Insect() {
        System.out.println("i="+i+" ,j="+j);
        j =39;
    }
    private static int x1 = print("static Insect.x1 init");
    static int print(String s){
        System.out.println(s);
        return 47;
    }
}
public class Beetle extends Insect{
    private int k = print("Beetle.k init");
    public Beetle() {
        System.out.println("k="+k);
        System.out.println("j="+j);
    }
    private static int x2 = print("static Beetle.x2 init");
    public static void main(String[] args) {
        System.out.println("Beetle construct");
        Beetle beetle = new Beetle();
    }
}

Result

static Insect.x1 init
static Beetle.x2 init
Beetle construct
static Insect.x3 init
i=9 ,j=0
Beetle.k init
k=47
j=39
View Code

可以看出,先进行类的加载,先基类再子类。static变量/块执行于类的初始化时期。new之后按照 先初始化再构造器的顺序执行。

posted @ 2016-09-06 21:09  胖胖的半山兄  阅读(358)  评论(0编辑  收藏  举报