Java类与对象的描述及内存原理
一、举例引入
假如有一只柴犬叫小柴,今年1岁,是个男孩子,它会吃饭,会撒娇;有一只哈士奇叫小哈,今年2岁,也是个男孩子,它也会吃饭,也会撒娇;还有只白土松叫奶油,也是个男孩子,今年1岁,它也会吃饭,也会撒娇。
如果要将以上的文字转化成Java的程序来描述,从变量和方法的角度来看,姓名,年龄,性别这些都属于变量,而吃饭,撒娇这些属于方法。那么就需要给小柴、小哈和奶油分别定义品种,姓名,年龄,性别,以及吃饭,撒娇的行为方法。这样做会产生大量的变量和方法,不仅不利于对变量和方法的管理,而且会使效率变得十分低下。

既然它们都有品种,姓名,年龄,性别这些相同的特征,又有吃饭,撒娇这样相同的行为方法。那么就完全可以把相同的地方抽象提取出来,构建一个新的类别,这个新的类别具备了以上各个小狗的特征以及行为方法。当要对这个类别一个具体的事物进行描述时,就可以创建一个这个类别的对象,这个对象就已经具备了这些特征和行为。特征就作为类的属性,而行为作为类的方法。要使用和管理的时候,可以直接创建对象,并通过对象名来设置属性和方法,管理的时候只要通过管理对象就可以了,这样就可以提高编程效率了。

类是一种数据类型,是自己定义的数据类型;而对象就是具体描述的事物。对象诞生于类,而类可以描述对象。
通过类可以将相同的代码总结到一起,如果要使用的时候,直接新建对象就可以了。可以提高代码的复用性;也便于维护,如果某个类需要添加新的属性,直接在类中添加相应的代码即可;而且便于管理,使用类中的属性或方法的时候直接进行相应的调用就可以了。
二、具体实现
类的属性或叫成员变量既可以是基本数据类型,也可以是引用数据类型。
class A {int age; //基本数据类型String name; //引用数据类型
}
上面的例子用类的形式描述成Java语言如下:
class Dog {//属性String name;int age;char gender;//吃饭的方法public void eat() {System.out.println(name + "正在吃饭");}//撒娇的方法public void spoil() {System.out.println(name + "正在撒娇");}//显示信息的方法public void showInfo() {System.out.println("name:" + name + ",age:" + age + "岁,gender:" + gender);}
}
属性就是描述类的具体特征;方法是描述类的具体行为,构造器是对类进行初始化,在创建对象时具备基本的特征。
在main方法中创建对象并调用相应的方法:
public class Test {public static void main(String[] args) {//分别创建对象,并添加相关信息Dog d1 = new Dog();d1.name = "小柴";d1.age = 1;d1.gender = '男';Dog d2 = new Dog();d2.name = "小哈";d2.age = 2;d2.gender = '男';Dog d3 = new Dog();d3.name = "奶油";d3.age = 1;d3.gender = '男';//打印信息d1.showInfo();d2.showInfo();d3.showInfo();//调用方法d1.eat();d2.eat();d3.spoil();}
}
运行结果如下:
三、内存原理
创建对象格式:
Dog d = new Dog();
这里的d只是对象的引用(对象名),而new Dog()才是真正的对象。
Java内存机制:
- 栈:存放基本数据类型
- 堆:存放对象,给对象和引用数据类型开辟空间
- 方法区:常量池(存放字符串常量),加载类的信息(属性和方法)
当创建一个对象的时候:
- 先在方法区中加载类的信息;
- 在堆中开辟空间,此时类中的属性为初始的默认值;
- 将堆中的地址值返回给栈中的变量;
- 对属性进行初始化,将类中的属性更新为设置的值。
以一段创建对象的代码为例:
Dog d3 = new Dog();
d3.name = "奶油";
d3.age = 1;
d3.gender = '男';
以上的内存图如下:
创建对象的内存图
分析:
- main方法先进栈,然后方法区中加载Dog类的信息,包括类的属性和方法;
- 在堆中开辟一块空间,假设地址为0x2233,此时类中的属性值为初始的默认值,之后会把地址0x2233返回给栈中的d3,d3就通过这个地址指向堆中相应的空间;
- 因为字符串存储在方法区的常量池中,所以会在常量池中开辟一块空间,假设地址为0x2244,会把0x2244返回给堆中相应空间的name,所以name就通过这个地址指向常量池对应的空间中(String类型的数据并不会直接存储在堆中,而是存储在方法区的常量池中,和创建对象的原理有点类似);
- 之后age和gender会从初始值更新为1和'男'。