复习第三、四章:控制程序流程,初始化和清除

一、第三章:控制程序流程

这一章主要讲java中程序流程的控制方法。

(1)==和equals

equals 方法是 java.lang.Object 类的方法,有两种用法:

  1. 对于字符串变量来说,使用“==”和“equals()”方法比较字符串时,其比较方法不同。
  2. 对于主类型的包装类,和上文的String一样,equals是比较值的,而不是地址。
  3. 注意:对于基本类型的比较,不能使用equals,只能使用==。

(2)讨论了如下问题

这里的语句分别做了什么?都在堆中新建了对象吗?

这个问题我在《think in java 第三章 控制程序流程》中做了解释,我现在的观点是从这里来的:https://www.zhihu.com/question/29884421

这个议题非常值得学习,里面回答得非常详细。其实我很想研究一下怎么去证明议题里面dalao们的结论,以后甚至想开篇新的文章来记录一下我自己对这个问题的理解。

这一章的内容主要是逻辑控制的部分,其实还是比较简单的,就不做太多说明了

二、第四章:初始化和清除

这一章内容挺多,也很重要,我足足总结了4章…

主要是讲一些初始化的内容,至于清除部分,没有过多涉及原理,看得我有些懵懂。

(1)构建器初始化

构建器初始化,方法过载:如何区分,主类型过载,不能返回值过载,默认构建器。

1.构建器初始化

Java类的成员变量在被修饰为public、protected和包访问权限时,可以在不同层次上直接给成员变量赋值。但是,赋值的前提是:必须存在持有成员变量的对象。而对象的初始化必须调用构造函数搜索,所以构造函数是必不可缺的。

我的理解:

这个还算是好理解吧,说明了构建器(也就是构造函数的重要性,即java为了保证对象的初始化,百分之百需要调用构造函数,如果你没有自定义的,那么jvm就会负责给你建一个默认的)

至于使用构造函数还是直接声明时给成员变量赋值,那就要看情况而定。如果创建的这个对象是不可变的,那么就必须使用构造函数初始化成员变量,反之,就无所谓。

另外,直接给成员变量赋值,Java是不推荐的,因为这样会破坏它的封装性。所以,建议在构造函数或提供setters方法对变量赋值。

我的理解:

这个应该算是构造函数的作用了。假如我想把一个对象传到另外一个对象里面,用构造函数就挺方便的,用setter()方法也可以。但是如果是直接赋值,虽然也可疑,但是感觉就不是很优雅了。

要弄清楚类中的字段是如何进行初始化的。

初始化字段有三种方式,直接初始化,还可以在代码block中初始化(又分为静态block和普通的block),也可以使用自定义方法进行初始化。

模拟代码如下:

初始化的顺序则是:静态block>>普通block>>构造函数。

  1. 静态block好理解,因为静态字段和静态block中的内容在新建对象时的加载Class的步骤就已经加载完成了,所以初始化最快。
  2. 构造函数则是最后执行的,初始化字段后,字段都是默认值。然后通过构造函数给字段自定义值。
  3. 至于普通block和直接初始化字段,我不知道哪个比较快…(这里可能有误,回头再看)

i首先会被自动初始化为0,然后由于构造器的调用变成8。

对于所有基本类型和对象引用这种情况都是成立的,所以,编译器不会强制一定要在构造器内或在使用它们之前对元素进行初始化,因为它们已经被自动初始化。

2.方法过载

如何区分,主类型过载,不能返回值过载,默认构建器。

这些都挺好理解的,就不展开了。

(2)this和static

主要需要搞清楚用法

1.this

this主要要三种用法:

  1. 表示对当前对象的引用!
  2. 表示用类的成员变量,而非函数参数,注意在函数参数和成员变量同名是进行区分!其实这是第一种用法的特例,比较常用,所以那出来强调一下。
  3. 用于在构造方法中引用满足指定参数类型的构造器(其实也就是构造方法)。但是这里必须非常注意:只能引用一个构造方法且必须位于开始!
  4. 注意:this不能用在static方法中!所以甚至有人给static方法的定义就是:没有this的方法!虽然夸张,但是却充分说明this不能在static方法中使用!

为什么this不能在static方法中使用?

我们再看看static是怎么回事。

2.static

static其实在上次回顾中已经复习了,现在再来总结一次。

被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。

只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。

用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。

在这里我产生了一个新的疑问:

多个实例的static变量会共享同一块内存区域。怎么进行验证?

运行结果为:

这就足以证明相同类的实例中static变量的值是共享的了(引用指向的都是内存中的同一块区域)。

3.为什么this在static方法中不可以使用?

我先不参考以前的文章,说一说目前的理解:

这个涉及到对象初始化时的顺序问题。

  1. static部分,包括字段、方法和static块,在初始化对象过程中的加载Class的过程中,就已经被创建了。
  2. 至于this,往往代指当前对象。
  3. 问题是,static修饰的方法,在对象还没有创建的时候就已经被初始化了,那么该方法中的this,要如何指向一个不存在的还没被创建的对象呢?

所以这样的使用方式是错误的。

以前的文章中总结的理解:

因为Static方法是类方法,先于任何的实例(对象)存在。即Static方法在类加载时就已经存在了,但是对象是在创建时才在内存中生成的。而this指代的是当前的对象,在这个时候类还在加载呢,还没有到在内存中生成对象的步骤(即当前类对象还不存在),this怎么能代表一个不存在的对象呢?所以这时候就报错了。

感觉理解得不错。

(3)清除:收尾和垃圾收集

主要讲的是一些清除的概念,还是没有涉及原理。单看这里是不够的,要充分了解GC还是要看别的深入的部分才行。

1.finalize()

  1. 对象不一定会被回收。
  2. 垃圾回收不是析构函数。
  3. 垃圾回收只与内存有关。
  4. 垃圾回收和finalize()都是靠不住的,只要JVM还没有快到耗尽内存的地步,它是不会浪费时间进行垃圾回收的。

think in java也说了,我们或许不必过多地使用finalize(),因为它并不是进行普通清除工作的理想场所。

2.必须执行清除(普通的清除工作)

java中的主类型的句柄都在栈内存中分配,存在其作用域,如果超出作用域即释放

java中的new创建的对象都在堆内存中分配,由GC负责回收,GC有其策略。

C/C++中不存在自动回收机制,所以需要手动实用delete()回收内存。

(4)成员初始化

我认为这是这一章最重要的内容,可以总结为如下步骤:

对象的创建过程发生了什么?

对象的创建过程。请考虑一个名为Dog的类:

  1.  类型为Dog的一个对象首次创建时,或者Dog类的static方法/static字段首次访问时,Java解释器必须找到Dog.class(在事先设好的类路径里搜索)。
  2. 找到Dog.class后(它会创建一个Class对象,这将在后面学到),它的所有static初始化模块都会运行。因此,在Class对象首次载入的时候,static初始化仅发生一次。
  3. 创建一个new Dog()时,Dog对象的构建进程首先会在内存堆(Heap)里为一个Dog对象分配足够多的存储空间。
  4. 这种存储空间会清为零,将Dog中的所有基本类型设为它们的默认值(零用于数字,以及boolean和char的等价设定)。
  5. 进行字段定义时发生的所有初始化都会执行。
  6.  执行构建器。正如第6章将要讲到的那样,这实际可能要求进行相当多的操作,特别是在涉及继承的时候。

这个还没有涉及到继承的情况。

(5)数组初始化

1.数组

2.多维数组

一些关于数组的概念,关于初始化的部分我没看得很明白,说到java中的数组,可以存放句柄,也可以存放实际的对象。

总之是能做到很多事情的。一些java集合是通过数组实现的,也就是说,如果算法合理,采用数组往往比使用集合效率更高。

三、总结

到此为止,前四章算是回顾完成了。之后将继续进行新的学习!

发表评论

电子邮件地址不会被公开。 必填项已用*标注