栈与堆:生存空间
对象的生存空间是堆(heap)
方法调用及变量的生存空间是栈(stack)
变量
实例变量
实例变量是被声明在类而不是方法里面。它们代表每个独立对象的“字段”(每个实例都能有不同的值)。实例变量存在于所属的对象中。
public class Person{
String name;
int age;
}
//name和age就是实例变量,每个Person对象都会有自己的name和age
局部变量(栈变量)
局部变量和方法的参数都是被声明在方法中。它们是暂时的,且生命周期只限于方法被放在栈上的这段期间(也就是方法调用至执行完毕为止)。
public void fun(int x){
int i = x + 3;
boolean b = true;
}
//x,i,b都是局部变量
堆栈块
当你调用一个方法时,该方法会放在调用栈的栈顶。实际被堆上栈的是堆栈块,它带有方法的状态,包括执行到哪一行代码以及所有的局部变量的值。栈顶上的方法是目前正在执行的方法。
public void fun1(){
boolean a = true;
fun2(1);
}
public void fun2(int x){
int y = x + 4;
fun3();
}
public void fun3(){
char c = 'a';
}
以上代码的运行过程:
对象的局部变量
无论对象是否声明或创建,如果局部变量是个对该对象的引用,只有变量本身会放在栈上,对象本身只会存在于堆上。
public class StackRef {
public void foo() {
bar() ;
}
public void bar() {
Duck d = new Duck(24) ;
}
}
内存布局:
创建对象
Person person = new Person();
声明对象和赋值有三个步骤:
1.声明引用变量
2.创建对象
3.连接对象和引用
构造函数
- 构造函数是在新建类时会执行的程序。
- 构造函数必须与类的名字一样,且没有返回类型。
- 如果你没有写构造函数,则编译器会帮你写一个没有参数的
- 一个类可以有很多个构造函数,但不能有相同的参数类型和顺序,这叫作重载过的构造函数。
无参构造函数
看似上面的代码好像在调用Person()这个方法,实际上是在调用它的构造函数。对于构造函数,如果你没有写,编译器会自动生成一个无参构造函数。
有参构造函数
如果某种对象不应该在状态被初始化之前就使用,就别让任何人能够在没有初始化的情况下取得该种对象!让用户先构造出Person对象再来设定大小是很危险的。如果用户不知道,或者忘记要执行setSize()怎么办?
最好的方法是把初始化的程序代码放在构造函数中,然后把构造函数设定成需要参数的。
public class Person{
int size;
public Person(int newSize){
size = newSize;
System.out.println("size is " + size);
}
}
public class UsePerson{
public static void main(String[] args){
Person p = new Person(13);
}
}
但是如果只有一个有参构造函数,调用时又不知道适当大小,那么该怎么办?
这时,我们可以创建两个构造函数,一个是有参的,一个是无参的,使用默认容量大小。
注意:
如果你已经写了一个有参构造函数,这时编译器不会自动帮你生成一个无参的构造函数了,你需要自己手动写。
重载构造函数的意思代表你有一个以上的构造函数且参数都不相同
父类及继承与构造函数之间的关系
在创建新对象时,所有继承下来的构造函数都会执行。
调用父类构造函数的唯一方法是super
注意:
每个构造函数可以选择调用super()或this(),但不能同时调用!
对象的生命周期
1.局部变量只会存活在生命该变量的方法中;
2.实例变量的寿命与对象相同,如果对象还活着,则实例变量也会是活的;
3.当最后一个引用消失时,对象就会变成可回收的
① 引用永久性的离开它的范围;
② 引用被赋值到其他的对象上;
③ 直接将引用设定为null。