用于记程序要处理的数据,代指各种类型的数据,便于扩展和维护,是内存中一块区域,与C语言一样。Java程序通过变量来访问内存中的特定数据。变量声明才可使用,适用范围由所属的“{ }”决定,使用前必须有值。
定义格式
数据类型 变量名 = 数据
对象的默认引用:this
当创建一个Java对象后,JVM会给它分配一个引用自身的指针this,所有对象都有一个共同的默认引用变量名字。JVM运行哪个变量所指对象的方法时,this关键字就指向这个变量所引用的对象。
变量类型
- 成员变量:在类中声明,作用域是整个类。
- 局部变量:在一个方法内部声明,作用域是当前方法。
- 方法参数:作用域是当前方法。
两种成员变量
1.static修饰静态变量(类变量)
//定义静态变量
package mypack1;
public class Employee{
String name; //实例变量
static int count; //静态变量,初值默认0
//Employee类带参数的构造方法
public Employee(String name){
count++; //累计所创建的Employee对象个数
this.name = name;
}
public static void main(String[] args){
Employee m1 = Employee("阿明");
Employee m2 = Employee("阿红");
System.out.println(count); //打印2
}
}
2.不用static修饰的实例变量
两者区别
- 类的静态变量在内存中只有一个,被类的所有实例共享,可直接通过类名来访问。
- 类的每个对象都有相应的实例变量,每创建一个对象,JVM就会为它的实例变量分配内存。
两种类的方法
1.成员方法
- 静态方法(类方法)
不需要创建类的实例,可以直接通过类名来访问静态方法。
package mypack2;
import java.util.*;
public class Employee{
//all集合用来存放所有Employee对象的引用
static Set<Employee> all = new HashSet<Employee>();
String name;
public Employee(String name){
this.name = name;
all.add(this); //向集合加入当前的Employee对象
}
public static void printInfo(){
System.out.println("员工数目:"+all.size()); //all.size()返回所有Employee对象数目
//打印all集合所有Employee对象的name属性
for(Employee m: all)
System.ou.println(m.name);
}
public static void main(String[] args){
new Employee("阿明");
new Employee("阿红");
printInfo();
}
}
在静态方法中不能实用this,也不能直接访问所属类的实例变量和实例方法。作为程序入口的main()方法就是静态方法,实例变量需要通过引用变量来访问。
Employee m = new Employee("阿明");
System.out.println(m.name);
- 实例方法
类的实例方法依赖特定实例才能运行。在实例方法中,可以直接访问当前类的静态变量和实例变量。
//Employee类中介绍自己的方法
public void selfInfo(){
System.out.println("大家好,我是"+name);
System.out.println("我的年龄是"+age); //假设age是静态变量
}
2.构造方法
在同一个作用域内不允许定义同名的多个变量。在一方法内,可以定义和成员变量同名的局部变量或参数,此时成员变量被屏蔽。在这个方法中,若访问同名的实例变量,可通过this来访问;若访问同名的静态变量,可通过类名访问。
//Employee类的构造方法
public Employee(String name){ //name是方法参数
this.name = name; //this.name是实例变量
}
参数传递
不管是基本类型变量还是引用类型变量,向参数传递的都是变量的取值。
package mypack3;
//main()方法调用add()方法
public class Sample{
public static void main(String[] args){
int a = 1; //main()基本类型局部变量
add(a); //调用add(int a)方法
System.out.println(a); //打印1,这里打印的还是局部变量
}
static void add(int a){ //方法参数a
a++; //使方法参数的取值递增1
System.out.println(a); //打印2,这里打印的还是方法参数
}
}
对于引用变量,向参数传递的是所引用对象的内存地址,那么局部变量和参数引用同一个类对象,局部变量值会随参数改变。
package mypack4;
//main()方法调用add()方法
public class Sample{
int data = 1; //成员变量
public static void main(String[] args){
Sample a = new Sample; //main()引用类型局部变量
add(a); //调用add(Sample a)方法
System.out.println(a); //打印2
}
static void add(Sample a){ //方法参数a
a.data++;
System.out.println(a.data); //打印2
}
}
变量的生命周期
- 局部变量、方法参数:在方法运行时开始,分配内存;方法运行结束时结束,回收内存。
- 类、静态变量:两者同步,JVM加载类时,分配静态变量内存,直至程序运行结束,类和静态变量才结束生命周期。(JDK 8 以后的JVM,类的类型数据位于元空间,对象、静态变量、实例变量位于堆区,局部变量位于方法调用栈区。)
- 对象、实例变量:两者同步,当通过new语句创建对象时,JVM会为对象和它的实例变量分配内存,当对象不再被任何引用变量引用,他的内存计科回收。
Employee m = new Employee("阿明");
m.selfInfo();
m = new Employee("阿红"); //此时m不再引用代表"阿明"的Employee对象,执行完此句,"阿明"的Employee对象就可以被JVM回收
m.selfInfo();
用new创建对象
- 为对象分配内存,所有实例变量都会被分配内存,并且初始化为默认值(若无声明显示初始化)。
- 调用构造方法。如果一个类中没有定义任何构造方法,这个类就拥有一个隐含的不带参数的默认构造方法,方法体为空。
public Sample(){}
- 返回对象引用。
变量的初始化和默认值
- 成员变量:不管程序有没有显式地对其初始化,都初始化为默认值。
- 静态变量:既可在声明时对其显式初始化,也可在静态代码中对其显式初始化。
- 实例变量:既可在声明时对其显式初始化,也可在构造方法中对其显式初始化。
- 局部变量:JVM不会自动将其初始化为默认值,必须进行显式初始化才能使用,否则报错。
!静态变量与常量的区别
- 常量在编译时就已经确定了它的值,在整个程序的运行中保持不变;而静态变量的值可以在运行时改变。
- 常量通常用于存储一些固定的值,如数学常数、配置信息等,而静态变量通常用于存储可变的数据,如计数器、全局状态等。
- 静态常量类不需要加载即可访问,因为常量值存储在JVM内存中的常量池中,在类不加载时即可访问,但是在编译期间不能确定的值的常量不行,因为不能完成编译期优化,必须要类加载。
!其他注意
- 被使用的静态变量必须在静态代码块前面声明,静态方法只能调用静态变量;非静态代码块或非静态方法可以调用任何(静态+非静态)变量。
- 先声明的代码块先执行,非静态代码块在实例化对象时,于构造方法之前执行。静态变量、静态代码块、构造方法的执行顺序执行顺序如下:
1.父类静态变量和静态代码块
2.子类静态变量和静态代码块
3.父类的变量和代码块
4.父类的构造方法
5.子类的变量和代码块
6.子类的构造方法