Java之路:对象的声明和使用
一、对象的声明
下面定义了由类产生对象的基本形式:
类名 对象名 = new 类名();
创建属于某类的对象,需要通过下面两个步骤实现:
⑴ 声明指向“由类所创建的对象”的变量。
⑵ 利用new创建新的对象,并指派给先前所创建的变量。
class Person { 定义一个Person类
private String name;
private int age;
public void talk();
}
Person p = new Person();
// 也可以用下面这个方法
Person p1; // 先声明Person类对象p1
p1 = new Person(); // 再用new关键字实例化Person的对象p1
对象只有在实例化之后才能被使用,而实例化对象的关键字就是new。
对象实例化过程如下:
从图中可以看出,当语句执行到Person p1的时候,只是在“栈内存”中声明了一个Person对象p1的引用,但是这个时候p1并没有在“堆内存”中开辟空间。
对象的“引用”本质上就是一个对象在堆内存的地址,所不同的是,在Java中,用户无法向C/C++那样直接操作这个地址。
本质上,“new Person()”就是使用new关键字,来调用构造方法Person(),创建一个真实的对象,并把这个对象在“堆内存”中的占据的内存首地址赋予p1,这时p1才能称为一个实例化的对象。
二、对象的使用
如果要访问对象里的某个成员变量或方法,可以通过下面的语法来实现:
对象名称.属性名 //访问属性
对象名称.方法名() //访问方法
例如,想访问Person类中的name和age属性,可用如下方法来访问:
p1.name; //访问Person类中的name属性
p1.age; //访问Person类中的age属性
因此若想将Person类的对象p中的属性name赋值为“张三”,年龄赋值为25,则可采用下面的写法:
p1.name = "张三" ;
p1.age = 25 ;
如果想调用Person中的talk()方法,可以采用下面的写法:
p1.talk(); //调用Person类中的talk()方法
三、匿名对象
匿名对象是指就是没有名字的对象。
实际上,根据前面的分析,对于一个对象实例化的操作来讲,对象真正有用的部分是在堆内存里面,而栈内存只是保存了一个对象的引用名称(严格来讲是对象在堆内存的地址)。
所以,所谓的匿名对象就是指,只开辟了堆内存空间,而没有栈内存指向的对象。
public class NoNameObject {
public void say() {
System.out.println("面朝大海,春暖花开!");
}
public static void main(String[]args) {
new NoNameObject().say(); // 这是匿名对象,没有被其他对象所引用
}
}
【结果】
由于“new NoNameObject()”创建的是匿名对象,所以就用“NoNameObject()”整体来作为新构造匿名对象的引用,它访问类中的方法,就如同普通对象一下,使用点操作符(.):
NoNameObject().say();
匿名对象有如下两个特点:
⑴ 匿名对象是没有被其他对象所引用,即没有栈内存指向。
⑵ 由于匿名对象没有栈内存指向,所以其只能使用一次,之后就变成无法找寻的垃圾对象,故此会被垃圾回收器收回。
注
当创建一个对象后,Java虚拟机(JVM)就会给这个对象分配一个自身的引用——this。由于this是和对象本身相关联的,所以this只能在类中的非静态方法中使用。
静态属性及静态方法属于类,它们与具体的对象无关,所以静态属性及静态方法是没有this的。
public class StaticThis {
static int i;
public static void init() {
this.i = 10; // 错误,this只能在非静态方法中使用
}
public static int f() {
init();
i++;
return i;
}
public void print() {
System.out.println(this.f()); // 虽然这样访问静态方法不会报错,但是不推荐使用
System.out.println(StaticThis.f()); // 因为静态方法与静态属性是属于类的,所以直接用类名访问比较好
}
public static void main(String[] args) {
StaticThis st = new StaticThis();
st.print();
}
}
同一个类定义下的不同对象,每个对象都有自己的this,虽然都叫this,但指向的对象不同。
四、栈内存和堆内存的区别
1、栈
在Java中,栈(stack)是由编译器自动分配和释放的一块内存区域,主要用于存放一些基本类型(如int、 float等)的变量、指令代码、常量及对象句柄(也就是对象的引用地址)。
栈内存的操作方式类似于数据结构中的栈(仅在表尾进行插入或删除操作的线性表)。
栈的优势在于,它的存取速度比较快,仅次于寄存器,栈中的数据还可以共享。
其缺点表现在,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
2、堆
堆(heap)是一个程序运行动态分配的内存区域。
在Java中,构建对象时所需要的内存从堆中分配。 这些对象通过new指令“显式”建立,放弃分配方式类似于数据结构中的链表。
堆内存在使用完毕后,是由垃圾回收(Garbage Collection,简称GC)器“隐式”回收的。 在这一点上,是和C/C++是有显著不同的,在C/C++中,堆内存的分配和回收都是显式的,均由用户负责,如果用户申请了堆内存,而在使用后忘记释放,则会产生“内存溢出”问题——可用内存存在,而其他用户却无法使用。
堆的优势是在于动态地分配内存大小,可以“按需分配”,其生存期也不必事先告诉编译器,在使用完毕后,Java的垃圾收集器会自动收走这些不再使用的内存块。
其缺点为,由于要在运行时才动态分配内存,相比于栈内存,它的存取速度较慢。