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