thinking in java 第五章笔记及思考:夜宿 初始化与清理

  还是老规矩:太基础的就不罗列了,但是本章好像都是基础知识,感觉没有可以讲的部分,但是也不会令你失望。后面章节内容难懂,可能就会有更多书上的有趣内容与大家一块讨论。本系列博客旨在挖掘这本书新奇、难点内容。当然可能会忽略一些重要知识点,欢迎补充、讨论、提建议。也欢迎大家提出批评。共勉!
  本章引子:随着计算机革命的发展,“不安全”的变成方式已逐渐成为变成代价高昂的主因之一。初始化和清理涉及安全的两个问题。

1.书中说:使用对象之前需要初始化,类的构造器使用户在创建对象的时候自动初始化(调用对应构造方法),再使用对象的时候就不需要初始化,保证了初始化的进行。
  我们是不是可以这样理解:如果没有构造方法,在创建对象的时候只会给对象开辟一个内存,而不会初始化内存或者成员。而调用了构造方法,他会给内存清0,类成员变量自动赋对应初值?

2.构造器命名
  命名构造器的时候应该遵循什么样的规则?随便命名?当然不是,统一规范,构造器名与类名相同,一般用public 修饰,没有任何返回值,void也不能有。
  如果你自己创造了一个构造器,系统就不会再帮你创建构造器。(意味着自己创建一个有参构造器,这个类不存在无参构造方法)

3.传参小知识
  如果传入的数据类型(实际参数类型)小于声明的形参类型,实际数据类型会被提升
  如果实际参数类型大于声明的形参类型,实际数据类型会被窄化转换。
  下面给出了窄化转换的例子:
  这里写图片描述

4.重载
  简单的重载这里就不重复了。
  有个问题:可以用返回值的类型区分重载的方法吗?
  先回答这个问题时,我们可以回忆方法签名的定义:方法名 和 参数列表 共同构成了方法签名。她是方法标识。可以知道并没有提到返回参数,我们可以推断出返回值类型应该无法区分重载。这只是推断,我们讲道理要有理有据,知根知底是吧。
  如果有两个方法,如下:

void print(){
    System.out.println("s");
}
int print(){
    System.out.println("s");
    return 0;
}

  如果你只是简单的调用了print():你并没有接受一个返回值,带int的方法也可以直接调用而不接受返回值,因此会导致疑惑,到底调用的是哪个方法。因此不能区分。

5.this关键字
  总结:this 就是一个引用,它指向当前对象。一般在方法中使用,一个方法调用另一个方法用this,但是就算不用this,系统也会自动跟你添加上。为了格式美观,一般不用this.方法名()。构造方法调用构造方法用this,且必须放在第一行。
  I.this用的最多的时候一般是某个方法给成员变量赋值,为了区别成员变量与形参。如下:

public class Finding {
    public int number;
    public Finding(int number) {
        this.number = number;
    }
}

  II.this:我们可以吧this当成一个指向当前对象的引用,这是由编译器偷偷传入的。this只能在方法内部使用。这个原因其实你想想就知道了,如果在类非方法中使用this有何用意?给成员变量赋值还是直接用this调用方法?赋值可以直接赋,在类中方法外直接调用方法奇不奇怪?因此这句话还是有道理的,规范行为。如下:
  这里写图片描述
  III.一个方法调用另一个方法不需要加this,因为系统会默认自动添加。如果你添加也没错,但是看起来别扭。
  IV.下面是书中给的一个例子,返回当前对象的引用:

public class Leaf {
  int i = 0;
  Leaf increment() {
    i++;
    //返回对当前对象的应用
    return this;
  }
  void print() {
    System.out.println("i = " + i);
  }
  public static void main(String[] args) {
    Leaf x = new Leaf();
    x.increment().increment().increment().print();
  }
} /* Output:
i = 3
*/

  V.构造器中调用构造器用this,且this只能放在第一行,一个构造方法里边不能调用两个构造方法
  一个构造器中调用另一个构造方法,按理说可以直接使用另一个构造方法名来调用,可能是为了区别于普通方法吧,并不能直接调用构造方法,这个时候只有使用其引用this。且this只能放在第一行。
  这里写图片描述
  因为this只能放在第一行,因此一个构造方法里边不能调用两个构造方法,因为调用两个构造方法意味着要写两个构造方法(两个this),与this主能放在第一行矛盾。如下:
这里写图片描述
  
6.清理:终结处理与垃圾回收
  I.首先说说书中的理解:垃圾回收器只知道回收那些由new创建的对象(我这里有一个问题:用反射生成的对象呢?好像书中并未提及,默认这个也可以回收?我不是很理解)。只有在内存使用到一个阈值才会执行垃圾回收机制:他会首先调用finalize()方法,然后开始清理内存。因此在调用了finalize()而没有执行垃圾回收机制的时候可以理解finalize()没有任何作用。在JVM内存并未面临耗尽的情况下是不是浪费时间清理内存的。
  System.gc()用来强制进行终结动作。
  
  II.垃圾回收器对于提高对象的创建速度有明显的效果
  我们反推:创建对象的时候要在堆中分配内存,因为垃圾回收器对于提高对象的创建速度有明显的效果,这就意味着当内存很多的时候,创建对象的时候很快,但是当内存越来越多的时候创建对象的速度开始变慢。由此看来内存空出量与对象创建速度正比。那么当内存占用量升高时,是什么影响了对象的创建速度?照理说一定是内存分配。只能推到这里,我们就暂且认为是内存分配算法把。
  
  III.java 回收机制算法:停止-复制,标记-清理
  停止-复制
  先暂停程序的运行,将所有存活的对象复制到另一个堆,旧堆可以清理掉了
  优点:对象复制到新堆时是一个一个挨着的,在新堆里紧凑排列
  缺点:效率低(1.因为需要两个堆,因此维护比实际需要多一倍的空间 2.程序稳定甚至不存在垃圾,这样将存在大量复制,而清理量少)
  标记-清理
  产生少量垃圾时速度快。从堆栈和静态引用区出发,遍历所有引用,找出存活对象,有引用指向对象则标记对象,没有引用指定对象则不标记。遍历完成则清理没有标记的对象。
  缺:剩下的堆空间是不连续的

7.成员初始化
  java尽力保证:所有变量在使用前都能得到恰当的初始化。
  先static成员变量,次static代码块,再非static成员变量,然后是构造方法
  I.初始化顺序
  变量:定义顺序即为初始化顺序,静态变量只初始化一遍,且在非静态变量之前。(因为static 成员变量又被称之为类变量,其与对象无关 ,所以他只占用一分内存,且不能用static定义局部便量。)
  初始化顺序举个例子:

//: initialization/StaticInitialization.java
// Specifying initial values in a class definition.
import static net.mindview.util.Print.*;

class Bowl {
  Bowl(int marker) {
    print("Bowl(" + marker + ")");
  }
  void f1(int marker) {
    print("f1(" + marker + ")");
  }
}

class Table {
  static Bowl bowl1 = new Bowl(1);
  Table() {
    print("Table()");
    bowl2.f1(1);
  }
  void f2(int marker) {
    print("f2(" + marker + ")");
  }
  static Bowl bowl2 = new Bowl(2);
}

class Cupboard {
  Bowl bowl3 = new Bowl(3);
  static Bowl bowl4 = new Bowl(4);
  Cupboard() {
    print("Cupboard()");
    bowl4.f1(2);
  }
  void f3(int marker) {
    print("f3(" + marker + ")");
  }
  static Bowl bowl5 = new Bowl(5);
}

public class StaticInitialization {
  public static void main(String[] args) {
    print("Creating new Cupboard() in main");
    new Cupboard();
    print("Creating new Cupboard() in main");
    new Cupboard();
    table.f2(1);
    cupboard.f3(1);
  }
  static Table table = new Table();
  static Cupboard cupboard = new Cupboard();
} /* Output:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)
*///:~

  从上述代码可以看出初始化顺序为:先static成员变量,在非static成员变量,然后是构造方法,再次创建带static 成员变量的类不会再创建static成员变量(即static成员变量只会被初始化一次)。
  
  II.创建对象的过程
  ①. java解释器找到类路径,定位.class文件
  ②.然后载入.class(创建一个Class对象),有关静态初始化的所有动作都会完成(静态成员变量,静态代码块,静态方法),静态初始化只在Class对象首次加载的时候才会被初始化。
  ③.当时用new常见对象时,会先在堆为对象分配一个内存
  ④.内存空间清零,类成员变量基本都被设置成默认值
  ⑤.执行字段定义处的初始化动作
  ⑥.执行构造器

                                2018.8.23
                               于 上海静安

猜你喜欢

转载自blog.csdn.net/u010986518/article/details/81978355
今日推荐