一、构造器
1 堆与栈
内存中有两种区域:堆heap和栈stack;
其中,堆,又可垃圾回收的堆,存放所有对象;栈存放方法调用和局部变量;
2 实例变量和局部变量
实例变量是被声明在类中方法之外的变量,而不是在方法中,存在所属的对象中;
实例变量有默认值,原始的默认值是0/0.0/false,引用的默认值是null;
// 每个Duck对象都会有独立的size
public class Duck{
int size;
}
局部变量被声明在方法或方法的参数中,是暂时的,生命周期只限于方法被放在栈上的这段时期;
局部变量没有默认值;
// 参数x和变量i, b都是局部变量
public void foo(int x){
int i = x + 3;
boolean b = true;
}
3 方法
当调用一个方法时,该方法会放在调用栈的栈顶;
栈顶上的方法是目前正在执行的方法;
若foo()方法调用bar()方法,则bar()方法会放在foo()方法上面;
4 对象引用
若局部变量是对对象的引用,则局部变量本身放在栈上,对象本身放在堆上;
若实例变量是对对象的引用,则实例变量和对象都在堆上;
5 构造函数
Duck myDuck = new Duck();
上例中,看似在调用Duck()方法,但其实是调用Duck的构造函数。
5.1 概念
构造函数不是方法,因为方法有返回类型,构造函数没有返回类型;
构造函数的函数名一定与类的名称相同;
构造函数不会被继承;
默认构造函数没有参数;
在初始化一个对象时执行,即在构造函数外调用构造函数的方法只有新建一个类;
若没有编写构造函数,编译器会帮忙编写一个构造函数;
5.2 对象状态的初始化
使用无参数的构造函数;
public class Duck{
int size;
public Duck(){
}
public void setSize(int theSize){
size = theSize;
}
}
public class DuckTest{
public static void main(String[] args){
Duck duck = new Duck();
duck.setSize(3);
}
}
使用有参数的构造函数;
public class Duck{
int size;
public Duck(int duckSize){
size = duckSize;
}
}
public class DuckTest{
public static void main(String[] args){
Duck duck = new Duck(25);
}
}
注意:在编写构造函数时,一定要有没有参数的构造函数;因为编译器只会在没有设定构造函数时才会调用,若编写一个有参数的构造函数,且需要一个没有参数的构造函数,则必须手动编写!
5.3 重载构造函数
必须保证参数的类型和顺序不同,编译器不看参数的名称;
public class Mushroom{
public Mushroom(){}
public Mushroom(int size){}
public Mushroom(boolean isMagic){}
public Mushroom(boolean isMagic, int size){}
public Mushroom(int size, boolean isMagic){}
}
6 父类、继承与构造函数的关系
6.1 父类的构造函数在对象的生命中扮演的角色
在创建新对象时,所有继承下来的构造函数都会执行,即每个父类都有一个构造函数,且每个构造函数都会在子类对象创建时执行;
抽象类也有构造函数,只是不能对其进行new操作,但抽象类可以是父类,因此其构造函数会在具体子类创建时执行;
6.2 调用父类构造函数
在构造函数中,用super()调用父类的构造函数;
注意:父类的部分必须在子类创建完成之前就必须完整的成型,即先执行父类的构造函数,再执行子类的构造函数;
// 通过
public Boop(){
super();
}
//通过
public Boop(int i){
super();
size = i;
}
// 通过,编译器自动将super()加到最前面
public Boop(){
}
// 通过
public Boop(int i){
size = i;
}
// 编译不通过
public Boop(int i){
size = i;
super();
}
6.3 有参数的父类构造函数
public abstract class Animal{
// 每个Animal都会有名字
private String name;
// Hippo会继承这个getter
public String getName(){
return name;
}
// 有参数的构造函数,用以设定name
public Animal(String theName){
name = theName;
}
}
public class Hippo extends Animal{
public Hippo(String name){
// 传给Animal的构造函数
super(name);
}
}
public class HippoTest{
public static void main(String[] args){
Hippo h = new Hippo("Buffy");
System.out.println(h.getName());
}
}
6.4 调用重载版本
使用this()从某个构造函数调用同一个类的另一个构造函数;
this()只能用在构造函数中,且必须是第一行语句;
每个构造函数可选择调用super()或this(),但不能同时调用;
class Mini extends Car{
Color color;
// 无参数的构造函数以默认的颜色调用真正的构造函数
public Mini(){
this(Color.Red);
}
// 真正的构造函数
public Mini(Color c){
super("Mini");
color = c;
}
// 编译不通过,一个构造函数不能同时调用this()和super()
public Mini(int size){
this(Color.Red);
super(size);
}
}
二、垃圾收集器
1 对象的生命周期
1.1
局部变量只会在声明该变量的方法中;
实例变量的寿命与对象相同,若对象还活着,则实例变量也会活着;
1.2
引用变量只能在处于它的范围中才能被引用,即除非引用变量是在它的范围中,不然就不能使用对象的遥控器;
只要有活着的引用,对象也就会活着;
当最后一个引用消失时,对象就会变成可回收的。
2 释放对象的引用的方法
2.1 引用永久性的离开它的范围
void go(){
Life z = new Life();
}
2.2 引用被赋值到其它的对象上
Life z = new Life();
z = new Life();
2.3 直接将引用设定为null
Life z = new Life();
z = null;