- 什么是
类
- 把一类事物的静态属性和动态操作组合在一起就形成了类;
- 类是抽象的,用来模拟一类事物;
- 类只是对象的设计模板,并不占用内存,在创建对象的时候根据类的设计分配内存;
- 什么是
对象
- 对象是类的一个实体;
- 对象是具体的,实际存在的;
- 对象具有生命周期;
比如,可以将类比作是一辆汽车的设计图纸,一辆辆汽车就是根据这个类生产出的对象:
2.1.如何定义类?
- 定义类使用
class
关键字; - 类中定义的变量称为这个类的属性;
- 类中定义的函数称为这个类的方法;
比如定义一个Car类:
class Car
{
//Car类的属性
int number; //车辆编号
//Car类的方法
void go()
{
//类中的方法访问类的属性
System.out.printf("My number is %d, go go go!", number);
}
}
2.2.如何创建一个对象?
在主方法中创建一个Car类的对象并访问其成员和方法:
class CarTest
{
public static void main(String[] args)
{
//创建一个动态对象
Car testCar = new Car();
//访问对象的属性并修改
testCar.number = 1;
//调用对象的方法
testCar.go();
}
}
编译运行:
javac CarTest
java CarTest
运行结果:
!!!
这里说的内存指的是程序运行时占用的内存,可以理解为RAM空间。
!!!
- 编写好程序源代码后,执行
javac CarTest
,生成的CarTest.class
可执行程序保存在硬盘上;
- 当我们执行
java CarTest
命令后,操作系统将整个程序加载到内存中,准备运行; - 程序从main入口函数开始执行;
- 程序在执行过程中进行内存分配,接下来我们开始逐步解析:
3.1.类的内存空间如何分配?
类只是一个概念,是抽象的,用来模拟一个事物,这里用来模拟一个车辆,所以类不会占用任何内存空间。
3.2.对象的内存如何分配?
Car testCar = new Car();
-
Car()
函数称为Car类的构造函数,虽然我们并没有定义,但是这个函数是默认存在的,在创建一个该类对象时自动调用,在第4节具体讲述; -
new
关键字表明这个对象创建时内存空间要动态分配,所以一个new出来的对象其内存空间在堆中,new
关键字的作用和C语言中malloc
函数的作用相同,只不过更为简单,总结:系统按照类中的定义在堆中分配内存,然后将首地址返回; - 这个对象的首地址返回后,保存在testCar变量中,所以按照C语言来说是testCar指向堆中的Car对象,按照Java对象来说是testCar变量引用了堆中Car对象的地址。
- testCar是一个Car类型的变量,并且在主函数中定义,所以是一个局部变量,其内存分配在栈中。
3.3.堆中对象的属性和方法如何访问?
如上图所示,如果我们想要访问堆中Car对象的number属性和go方法,该如何访问呢?
显然,我们不可能直接去访问,车到山前必有路,testCar变量中保存着Car对象的首地址,所以,我们有了testCar变量后无所不能!
如上面的演示程序,直接通过testCar.nunber
就可以访问number属性,通过testCar.go()
就可以访问go方法。
4.1.构造函数的作用
在创建一个对象时,系统首先会为该对象分配内存,然后自动调用该类的构造函数。
4.2.默认无参构造函数
在创建对象时,我们调用了Car()
函数,但是这个函数在我们的类定义中并没有,那么这个函数是如何调用的呢?
Car testCar = new Car();
这里的Car()
就是Car类
的构造函数,在我们定义了Car类
后,系统会自动生成默认构造函数Car()
。
4.3.自定义构造函数
自定义构造函数的时候,需要注意构造函数有以下特点:
- 函数名和类名相同
- 参数可以没有,也可以有多个
- 无返回值
- 构造函数必须是public或者默认修饰符
- 可以有多个构造函数
- 一旦自定义构造函数,系统不会再自动生成默认无参构造函数
比如,这里我自定义一个无参构造函数和带参构造函数:
class Car
{
//Car类的属性
int number; //车辆编号
//无参构造函数
public Car()
{
System.out.printf("无参构造函数被调用.\n");
}
//有参构造函数
public Car(int i)
{
number = i;
System.out.printf("有参构造函数被调用.\n");
}
//Car类的方法
void go()
{
//类中的方法访问类的属性
System.out.printf("My number is %d, go go go!\n", number);
}
}
使用无参构造函数和有参构造函数分别创建对象:
class CarTest
{
public static void main(String[] args)
{
//使用无参构造函数创建一个动态对象
Car testCar1 = new Car();
//访问对象的属性并修改
testCar1.number = 1;
//调用对象的方法
testCar1.go();
//使用有参构造函数创建一个动态对象
Car testCar2 = new Car(2);
//调用对象的方法
testCar2.go();
}
}
运行结果如下:
为了验证“当我们自定义构造函数,系统是否会再自动生成默认构造函数?“”,我们将无参构造函数去掉,再次运行,系统报错,提示默认无参构造函数没有定义:
所以我们可以得出:
一旦自定义构造函数,系统不会再自动生成默认构造函数。
4.4.构造函数中数据成员的赋值问题
对于类中的数据成员(类的属性),系统先执行定义时赋的初值,然后再执行构造函数中赋的初值。
比如,我在定义时将number
属性赋值为1,构造函数中赋值为2,最后的结果为2:
class Car
{
//Car类的属性
int number = 1; //车辆编号
//无参构造函数
public Car()
{
System.out.printf("无参构造函数被调用.\n");
}
//有参构造函数
public Car(int i)
{
number = i;
System.out.printf("有参构造函数被调用.\n");
}
//Car类的方法
void go()
{
//类中的方法访问类的属性
System.out.printf("My number is %d, go go go!\n", number);
}
}
class CarTest
{
public static void main(String[] args)
{
//使用有参构造函数创建一个动态对象
Car testCar = new Car(2);
//调用对象的方法
testCar.go();
}
}
运行结果:
接收更多精彩文章及资源推送,欢迎订阅我的微信公众号:『mculover666』。