第六章 构造函数 和GC (实际已是书的第九章了)
堆和栈
本地变量,和函数的调用都存在于Stack上,而对象的申请,都存活于Heap上。
正在运行的函数,处在栈的顶端。
如果一个函数的本地变量是个引用,然后申请了一个对象,让该引用去指向,那么这个引用存活于栈上,而其引用的对象,存活于Heap上。
注意:对象总存在于堆上,不管它的引用在哪里。
注意:成员变量,依附于对象,存活于Heap上。
对于基本类型的成员变量,在堆上创建对象的时候,就根据各种基本类型各自的大小,划分好了空间。比如int是32位,byte 8位等。而对于引用类型,也是划分好引用类型的大小,用于装那个遥控器的大小,当然不需要装下其指向的对象。
构造函数
Duck myDuck =
new Duck();
上面加粗部分,就是在调用构造函数Duck()。
如果自己没有明确写出构造函数,编译器会帮你安一个,形式如下:
public Duck() {
}
注意:上面构造函数的示例里面没写内容,实际的类不一定里面什么都没有。类如果有成员变量,那自动添加的构造函数是会对成员变量进行初始化的。
注意:java是允许一个类里,有一个与类同名的方法的。但是这个方法必然有返回值,这就是它与构造函数的区分之处。
注意:编译器自动添加一个构造函数,是在你完全没有写任何构造函数的时候。如果你自己写了一个,比如public Duck(int size) { duckSize = size;} ,那编译器就不会给你自动添加一个不带参数的构造函数了。所以,比较好的编程方式是,
最好给自己的类添加一个不带参数的构造函数。因为使用这个类的人,不见得就一定知道该给什么参数,有一个默认的会方便很多。
当然这条不是规定啊,还是有很多类,不给参数就无法用,没有指定默认参数的构造函数存在。
注意:重载,仅参数顺序不同也是可以的。比如 public Duck(int size, String name) 和 public Duck(String name , int size) 是可以构成重载的。overload。当然两个参数类型一样时候怎么换顺序都没用了。
注意:构造函数不一定非要是public的,也可以是private default,我自己亲测protected也行。
注意:虚类也是有构造函数的,他如果有成员变量的话也是要在自己的构造函数中初始化的。虽然没有办法通过 new AClass();的方法来调用这个构造函数。它的调用是在其子类的构造函数调用时,被调用的。
构造函数的调用线
如下图所示,三个类在同一条继承线上。如果我现在调用Hippo的构造函数,实例化一个Hippo,Hippo aHippo = new Hippo();那么首先会调用Hippo的构造函数,但是刚一进去就会先调用Animal的构造函数,然后进入Animal构造函数后,会立即调用Object的构造函数,把 object构造函数执行完毕后,会再继续执行Animal构造函数,最后执行完Hippo的构造函数。
构造函数的调用线,一直持续到调用到Object类的构造函数为止。
注意:一个对象中的父类部分必须完全建立起来后,才能建立子类部分。原因是,子类需要依赖父类存在,可能会用到父类的部分,如果父类没建好就建子类,就可能出问题,所以java规定了这么个调用线。C++也一样。
即,执行过程如下:
父构造函数的调用
在子构造函数中,要主动调用父构造函数,加入super();语句就行了。如:
注意:任何一个构造函数的第一个语句,必须是super()。当然小声说个例外,就是下面那行粗体字。如果一个构造函数调用的是另一个构造函数,那开头就不是super()了,就没有super(),下面会讲。
一般情况下,如果你没显式写出super();,那编译器会自动在你子类的构造函数的开头处,加入这句话,来完成构造函数调用线。
注意:如果你有多个重载的构造函数,编译器会在每个重载的构造函数开头,加入super(),
除非这个构造函数调用的是另外一个同一个类中的构造函数。
注意:因为编译器自动添加的只有super();这句话,所以只会调用父类的没有参数的那个构造函数,如果父类有好几个重载的构造函数,那只会自动调用那个没参数的。
注意:如果父类只有 需要参数的构造函数,那一个子类如果想继承于它,必须手动调用super();并给super();传参数。
构造函数 调用重载的另一个构造函数 this()的使用
这也是为了避免重复,因为几个构造函数中的工作可能差不多,大同小异。那就另一个构造函数为真正的构造函数,其他重载的构造函数只是接收参数,并调用这个真正的构造函数。
方法就是使用 this();
this()调用的是同一个类中的,重载的,另一个构造函数。
注意:this()只能在构造函数中调用,并且如果要被使用,那就必须是第一个语句。
注意:构造函数的第一个语句要么是this() 要么 是 super() ,且两者不能同时存在,且不可能有别的情况出现了。
Every constructor can have a call to super() or this(), but never both!
第七章 数字 和 static
static 方法
static方法,就是不需要对一个类进行实例化就能调用的方法,其内部也不会用到任何成员变量,这样该方法的行为就不会因为不同的对象,而发生变化。
如,
类Math下的所有方法都是static的。
所以可以这样调用,Math.abs(); Math.min();等。
注意:Math类的构造函数被声明为private,目的就是让它无法实例化。Math类也没有任何成员变量。
一个类中,既可以有static的方法,也可以有non-static的方法,可以共存。比如main函数就是static的。
注意:因为一个类下,static方法和non-static方法可以共存,所以用一个实例化了的对象去调用static方法是可以的。即,
static方法可以由对象调用,虽然它不依赖于对象。但是这么做并不规范,不好。
注意:static的方法,不能使用non-static的成员变量,注意,一定是成员变量,使用本地变量没问题的。因为如果要使用成员变量,必然牵扯到对象,每个对象的成员变量可能不同。
注意:static方法,不能使用non-static的方法(成员函数)。理由同上,因为成员函数里可以使用成员变量,即便哪个成员函数现在没使用成员变量,但不定什么时候会使用,所以,禁止static方法,调用non-static方法。
注意:non-static方法,既可以使用static方法,也能访问static的成员变量。
注意:获取一个对象的途径,一个是new,另一个是Java Reflection API,但第二个书上没怎么讲,查查。
类无法实例化的方法
1、类前加上 abstract,即声明该类为虚类。
2、将此类的构造函数前,加上private。
static 变量
批注一下,instance variables就是指成员变量。
初始化static变量
类的static变量的初始化,是在这个类第一次加载的时候就完成的。什么时候加载呢?比较典型的就是用new,实例化一个类的时候,或者使用一个类的static方法或static变量的时候。当然程序员可以自己控制什么时候对类进行加载,但没什么必要,交给JVM就好。
注意:static变量初始化,JVM会保证:
1、static变量初始化,必会在该类的任何一个对象被创造前完成。
2、static变量初始化,必会在该类的任何一个static方法被使用前,完成。
如果没有显式的给static变量初值,那它会自动初始化,和基本类型一样。数字类型初始化为0 或0.0 。布尔是false,引用是null。
使用一个static变量和使用static方法一样,Player.variables = 5;就行了。不用实例化对象。
static final 变量
static final 变量就是常量。
注意:static final 不会自动初始化,你不明确写初值,就报错。
注意:static final的变量,名称必须为大写。这是小规矩。
static final 初始化方法:
static {
BAR_SIGN = 5;
}
注意:上面的static块,叫做
static initializer,这个类一加载,就会立即运行这个块内的内容。
final 用途
1、在类中,
作为非static变量前时,用于保证这个值不会被更改。
2、
对于一个方法,声明为final,表示这个方法不能被重写 overridden
3、
对于一个类,声明为final,表示这个类不能被继承。
注意:一个类被声明为final后,其下的方法就没必要声明为final了。因为该类无法被继承,其内部方法就不可能被重写了。但是非要写也可以。主要是,如果你觉得一个类下的所有方法,都不想被重写,想保证他们的行为,那就把整个类声明为final。如果只是这个类中的部分方法,不想被重写,其它的允许重写,那就挑出不许重写的方法,标为final。
Math类的方法
有很多,这只写几个。
Math.random()随机产生 0.0 ~ 1.0 不包括两边界的小数。
Math.abs() 返回参数的绝对值,参数可以是int 可以是double。
Math.round() 把小数圆整,返回int或long,根据参数是float还是double。
Math.max() Math.min()
基本类型的包装
当你需要将一个基本类型变量,当做一个对象来使用的时候,就需要将它包装一下。
包装的函数有:
Boolean
Character
Byte
Short
Integer
Long
Float
Double
包装函数名称,基本就是基本类型的声明类型名,但有两个比较特殊,记忆一下。
java5.0之前,没有自动包装,非常麻烦。每次要使用一个基本类型对象的时候,先要把基本类型包装,然后用完了再解包装,java 5.0之后就没这么麻烦了。
以下示例是
5.0之前,ArrayList的使用方法:
有了自动包装后,ArrayList的使用方法就变得简单了,如下:
自动包装(autoboxing)的其他功能:
1、如果一个方法的参数类型是包装类型,有了自动包装后,既可以传基本类型参数,也能传包装类型参数。
2、当然,返回值也可以像上面一样。如果为包装类型的话,实际返回的既可以是基本类型,也能是包装类型。
3、用到布尔表达式的地方,既可以是表达式,也能是布尔的包装类型,也能是布尔的基本类型。
4、最神的是这个,以前只能用在基本变量上的操作符,也能用在包装类型上了,即使用在一个对象上。比如:
这在以前是不可能的,或者如:
这个实际不是改变了语法规则,只是编译器做了手脚,帮你把包装类型变为基本类型后,才做的运算。
5、赋值的时候也能用,被赋值类型可以是基本类型,也可以是包装类型。
包装类型提供的method
包装类型还提供的好处就是静态的函数,方法。
字符串转数字或布尔:
Integer.parseInt("222") Integer.parseDouble("2.56")
boolean b = new Boolean("true").booleanValue();
注意:布尔的怎么转换的看一下,没有parseBoolean这个函数。
反着也行,数字转字符串:
数字格式控制
书的294开始,到307没怎么看。和printf有点点类似。
Calendar API 可以做跟日历相关的事情。
还有个static import,没看310 311左右