定义类(只定义属性)
// Student.java
// Java中所有的类都属于引用数据类型,数据类型名为类名
// 定义一个公开的类,类名为Student,即数据类型为Student
public class Student{
//类体 = 属性 + 方法
// 属性描述的是对象的状态信息,通常采用变量的方式来定义
int no; // 学号
/*
虽然所有学生都有学号信息,但是每一个学生的学号是不同的
所以要访问这个学号必须先创建对象,通过对象去访问学号信息
id变量定义在类体当中,方法体之外,这种变量称为成员变量
学号信息不能直接通过类去访问,所以这种变量是成员变量中的实例变量
对象又被称为实例,实例变量又被称为对象变量,即对象级别的变量
不创建对象,id变量的内存空间是不存在的,只有创建对象后,这个id变量的内存空间才会创建
*/
String name; // 名字
boolean sex; // 性别
byte age; // 年龄
String address; // 住址
// 方法描述的是对象的动作信息
}
javac Student.java // 生成Student.class
创建对象(局部变量的引用)
// 测试类,OOTest01.java
public class OOTest01{
public static void main(String[] args){
// int是基本数据类型,i是局部变量的变量名,10是int类型的字面值
int i = 10;
// Student是引用数据类型,s是局部变量的变量名,new Student()是一个学生对象
/*
new是一个运算符,作用是创建对象,是在JVM的堆内存中开辟一块新的内存空间用于存储对象
new Student()是一个对象,保存在JVM堆内存中
s是一个局部变量,保存在JVM栈内存中
s这个变量保存的是对象在堆内存中的内存地址,这种变量称为引用,所以Student是一个引用数据类型
*/
Student s = new Student();
// 访问实例变量的语法格式
// 读取数据:引用.实例变量名
// 修改数据:引用.实例变量名 = 值
// 成员变量没有赋值的话,系统会赋默认值
/*
数据类型 默认值
-------------------------------------
基本数据类型
byte,short,int,long 0
float,double 0.0
boolean false
char \u0000
引用数据类型
String null
Student null
*/
System.out.println("学号 = " + s.no); // null
System.out.println("姓名 = " + s.name); // null
System.out.println("性别 = " + s.sex); // false
System.out.println("年龄 = " + s.age); // 0
System.out.println("住址 = " + s.address); // null
s.no = "10";
s.name = "jack";
s.sex = true;
s.age = 20;
s.address = "北京";
System.out.println("学号 = " + s.no); // 12345
System.out.println("姓名 = " + s.name); // Jack
System.out.println("性别 = " + s.sex); // true
System.out.println("年龄 = " + s.age); // 20
System.out.println("住址 = " + s.address); // 北京
// 再实例化一个全新的对象
//Student是变量的数据类型
//stu是一个引用,同时也是一个局部变量
Student stu = new Student();
System.out.println("学号 = " + stu.no); // null
System.out.println("姓名 = " + stu.name); // null
System.out.println("性别 = " + stu.sex); // false
System.out.println("年龄 = " + stu.age); // 0
System.out.println("住址 = " + stu.address); // null
}
}
实例变量的引用
// 家庭住址类,Address.java
public class Address{
String city; // 城市
String street; // 街道
String zipcode; // 邮编
}
// 用户类,User.java
public class User{
// 用户编号
// int是一种基本数据类型,即整数型
// no是用户变量中的实例变量
int no;
// 用户名
// String是一种引用数据类型,代表字符串
// name是用户变量中的实例变量,同时也是引用
String name;
// 用户住址
// Address是一种引用数据类型,代表家庭住址
// addr是用户变量中的实例变量,同时也是引用
Address addr;
}
// 测试类,OOTest02.java
public class OOTest02{
public static void main(String[] args){
// User是引用数据类型
// u是一个局部变量,是一个引用
// u中保存了User对象在堆内存中的内存地址
User u = new User;
// 输出User对象内部实例变量的值
System.out.println(u.no); // 0
System.out.println(u.name); // null
System.out.println(u.addr); // null
// 修改User对象内部实例变量的值
u.no = 110; // 110是一个int类型的字面值
u.name = "jack"; // "jack"是一个String对象
u.addr = new Address(); // new Address()是一个Address对象
// 在main方法中只有u一个引用,因此一切都只能通过u来访问
System.out.println(u.name + "居住在哪个城市:" + u.addr.city); // null
System.out.println(u.name + "居住在哪个街道:" + u.addr.street); // null
System.out.println(u.name + "邮编:" + u.addr.zipcode); // null
u.addr.city = "北京";
u.addr.street = "朝阳";
u.addr.zipcode = "100000";
}
}
局部变量的引用和实例变量的引用指向同一个对象
// 测试类,OOTest03.java
public class OOTest03{
public static void main(String[] args){
// u是局部变量,同时也是引用
User u = new User();
// 上一个版本编写的
// u.addr = new Address();
// a是局部变量,同时也是引用
Address a = new Address();
u.addr = a;
System.out.println(u.addr.city); // null
a.city = "天津";
System.out.println(u.addr.city); // 天津
}
}
实例变量的引用相互指向对方的对象
// 丈夫类,Husband.java
public class Husband{
// 身份证号码
String idCard;
// 丈夫对象中含有妻子引用
Wife w;
}
// 妻子类,Wife.java
public class Wife{
// 身份证号码
String idCard;
// 妻子对象中含有丈夫引用
Husband h;
}
// 测试类,OOTest04.java
public class OOTest04{
public static void main(String[] args){
// 创建一个丈夫对象
Husband huangXiaoMing = new Husband();
huangXiaoMing.name = "黄晓明";
// 创建一个妻子对象
Wife baby = new Wife();
baby.name = "baby";
// 结婚(通过丈夫能找到妻子,通过妻子也能找到丈夫)
huangXiaoMing.w = baby;
baby.h = huangXiaoMing;
// 得到黄晓明妻子的名字
// 这种方式说明不了baby是黄晓明的妻子,因为有没有结婚这一步结果都是一样的
System.out.println(huangXiaoMing.name + "妻子的名字叫" + baby.name);
// 这种方式才能说明baby是黄晓明的妻子
System.out.println(huangXiaoMing.name + "妻子的名字叫" + huangXiaoMing.w.name);
}
}
内存空间分析总结
-
变量类型
- 局部变量(在方法体内声明)
- 成员变量(在类体内方法体外声明)
- 静态变量(修饰符有static)
- 实例变量(修饰符没有static)
引用可以是局部变量,也可以是成员变量
-
三块内存空间
-
JVM(Java虚拟机)主要包括三块内存空间:方法区内存、栈内存、堆内存
-
三块内存中,变化最频繁的是栈内存,最先出现数据的是方法区内存,垃圾回收器主要针对的是堆内存
-
方法区内存和堆内存各1个,栈内存可以有多个(一个线程一个)
-
方法区内存
- 方法区内存中存储类的代码片段,在类加载的时候这些代码片段会载入
- 静态变量存储在方法区内存中
-
栈内存
-
栈内存中存储方法体中的局部变量
-
方法调用的时候,该方法所需要的内存空间在栈内存中分配,称为压栈;
方法执行结束后,该方法所占用的内存空间释放,称为弹栈
-
-
堆内存
- 堆内存中存储Java对象
- 对象内部的实例变量存储在堆内存中
-
-
垃圾回收器(自动垃圾回收机制,GC机制)
-
当堆内存中的Java对象变成垃圾数据时,会被垃圾回收器回收
-
当没有任何一个引用指向该对象时,该对象就成为垃圾数据
访问对象只能通过引用的方式,一旦没有任何引用指向对象,该对象就无法被访问
-
空指针异常
// 顾客类
public class Customer {
int id;
}
// 测试类
public class Test {
public static void main(String[] args) {
Customer c = new Customer();
System.out.println(c.id); //0
c = null;
// 以下程序编译可以通过,因为符合语法
// 运行出现空指针异常。
// 空引用访问实例相关的数据一定会出现空指针异常
// java.lang.NullPointerException
System.out.println(c.id);
}
}