生存空间:堆与栈
变量存在哪一个空间要看它是哪种变量,而不是看类型
当你调用一个方法时,该方法会放在调用栈的栈顶
栈顶的方法是目前正在执行的方法
eg:foo()方法调用bar()方法则bar()方法会放在foo()方法的上面;
有关对象的局部变量
非prinmitive的变量只是保存对象的引用而已,而不是对象本身,无论对象是否声明或创建,如果局部变量实根该对象的引用,只有变量本身会放在栈上,对象本身只会存在堆上;
public class StackRef{
public void foof(){
barf();;
}
public void barf(){
Duck d=new Duck(24);
}
}
实例变量的位置
1.
2.
3.
构建函数
构建函数带有你在初始化对象时会执行的程序代码。也就是新建一个对象时就会被执行。
就算你没有自己写构造函数,编译器也会帮你写一个。
public Duck(){
}
唯一能够调用构造函数的办法就是新建一个类(严格来说,这是唯一在构造函数之外能够调用构造函数的方式);
它带有new的时候会执行的程序代码,这段程序代码会在你初始化一个对象的时候执行。
构造函数的一项关键特征是它会在对象能够被赋值给引用之前就执行,代表你可以有机会在对象被引用之前介入;
//构造函数让你有机会介入new的过程
public class Duck{
public Duck(){
System.out.println("Quack");
}
}
public class UseADuck{
public static void main(String[ ] args){
Duck d=new Duck();//这会启动Duck的构造函数;所以会输出Quack;
}
}
新建Duck状态的初始化
大部分的人使用构造函数类初始化对象的状态,也就是设置和给对象的实例变量赋值
public Duck(){
size =34;
}
这是在开发者知道Duck类应该有多大时没问题的,但如果是由使用Duck的程序员来决定时该怎么办
使用setSize()来设定大小,会让Duck暂时处于没有大小数值的状态(实例变量没有默认值)
public class Duck{
int size;//实例变量
public Duck(){
System.out.println("Quack");//构造函数
}
public void setSize(int newSize){//setter方法
size=newSize;
}
}
public class UseADuck{
public static void main(String[ ] args){
Duck d=new Duck();//Duck在此处已经建立,但是却没有size值,你必须依赖Duck的用户记得要设置大小。
d.setSize(42);
}
}
如果你在创建对象时需要有程序代码帮忙初始化,那你就得自己编写构造函数。
使用构建函数来初始化Duck的状态
如果对象不应该在状态被初始化之前就使用,就别让任何人能够在没有初始化的情况下取得该对象让用户先构造出Duck对象再来设定大小是很危险的,如果用户不知道,或者忘记执行setSize()怎么办
最好的方法是把初始化的程序代码放在构造函数中,然后把构造函数设定成需要参数的
public class Duck{
int size;
public Duck(int duckSize){//给构造函数加上参数
System.out.println("Quack");
size=duckSize;//使用参数的值类设定size这个实例变量
System.out.println("size is"+size);
}
}
public class UseADuck{
public static void main(String[ ] args){
Duck d=new Duck(42);传值给构造函数,只用一行就可以创建新的Duck并且设定好大小
}
}
一定要有不需要参数的构造函数
可以让用户在创建Duck时有两个选项:一个可以指定Duck的大小(通过构造函数的参数),另一个使用默认值无需指定大小。
你需要有两种方法来创建出新的Duck
public class Duck2{
int size;
public Duck2(){
//指定默认值
size =27;
}
public Duck2(int duckSize){
//使用参数设定
size=duckSize;
}
}
知道大小时
Duck2 d=new Duck2(15);
不知道大小时
Duck2 d2=new Duck2();
因此这会需要两个构造函数来分辨两种选项,一个需要参数,另外一个不需要参数。如果一个类有一个以上的构造函数,这代表它们也是重载的。
编译器不一定会帮你写出一个没有参数的构造函数
如果你已经写了一个有参数的构造函数,并且你需要一个没有参数的构造函数,则你必须要自己动手写。只有完全没有写,编译器才会帮你写一个;
如果类有一个以上的构造参数,则参数一定要不一样;
重载构造函数代表你有一个以上的构造函数且参数都不相同;
public class Mushroom{
public Mushroom(int size){}
public Mushroom(){}
public Mushroom(bollean isMagic){}
public Mushroom(boolean isMagic,int size){}
public Mushroom(int size,boolean isMagic){}//顺序不同也可以
}
父类的构造函数在对象的生命中所扮演的角色
在创建新对象时,所有继承下来的构造函数都会执行
如何调用父类的构造函数
public class Duck extends Animal{
int size;
public Duck(int newSize){
Animal();//错误
super();//正确
size=newSize;
}
}
如果我们没有调用super(),编译器会帮我们加上super()的调用
如果没有写构造函数
public ClassNmae(){
super;
}
如果有构造函数没有调用super();
则会加上super();
编译器加的一定是没有参数的版本,假设父类有多个重载版本,也只有无参数的这个版本会被调用到
有参数的父类构造函数
public abstract class Animal{
privat String name;//每个Animal都会有名字
public String getName(){//Hippo会继承这个getter
return name;
}
public Animal(String theNAme){
nmae=theName;//有参数的构造函数,用来设定name
}
}
public class Hippo extends Animal{
public Hippo(String name){//这个构造函数会要求名称
super(nmae);//传给Animal的构造函数
}
}
public class MakeHoppo{
public static void main(String[ ] args){
Hippo h=new Hippo("Buffy"");//创建Hippo,传入名字然后再列出来看
Stystem.out.println(h.getNmae());
}
}
使用this()从某个构造函数调用重载版(同一个类)的另一个构造函数
调用this()或this(aString)或this(27,x),this就是对对象本身的引用
this()只能用在构造函数中,且它必须是第一行语句;
每个构造函数都可以选择调用super()或this(),但不能同时调用
对象生命周期
对象生命周期要看引用变量的生命周期
1.局部变量只会存活在声明该变量的方法中
2.实例变量的寿命与对象相同,如果对象还活着,则实例变量也会是活的
life:
只要变量的堆栈还存在堆栈上,局部变量就算活着;
Scope:
局部变量的范围只限于声明它的方法之内。此方法调用其他方法的时候,该变量还活着,但不在目前范围内,执行其他方法回来时,范围也跟着回来
只要有活着的引用,对象也就活着,如果某个对象的引用已经不在它的范围中,但此引用还是活着的,对象就会继续活在堆上;
对象的唯一引用死了,对象就会从堆中被踢开;