Day10|Java变量全景
一、变量的本质
变量由三部分构成:
- 类型:决定了变量能够装什么
- 名称:变量的标识符
- 值:变量里存储的数据本体
可以把变量理解成快递柜的格子。
类型就是格子的规格(长宽高),决定了这个格子能装多大的快递。
名称就是格子上的编号标签,让你能够快速识别到这个格子。
值自然就是放到格子里的快递包裹。
package com.lazy.snail.day10;
/**
* @ClassName Day10Demo
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 14:28
* @Version 1.0
*/
public class Day10Demo {
public static void main(String[] args) {
int a = 1;
double b = 2.0;
}
}
示例代码中定义了两个变量:
类型分别是int\double;名称分别是a\b;值分别是1\2.0;
二、变量的分类
分类 | 位置 | 生命周期 | 默认值 | 存储位置 |
---|---|---|---|---|
局部变量 | 方法/代码块内 | 方法执行时创建,结束销毁 | 无 | 栈内存 |
实例变量 | 类中无 static | 对象创建时生成 | 有 | 堆内存 |
类变量(静态) | 类中带 static | 类加载时初始化,程序结束销毁 | 有 | 方法区/元空间 |
局部变量
局部变量是在方法或者代码块内部声明的变量,作用域仅限其被声明的区域。
package com.lazy.snail.day10;
/**
* @ClassName Day10Demo
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 14:28
* @Version 1.0
*/
public class Day10Demo {
void method() {
int a = 1;
}
}
在方法method中声明初始化了一个a变量,这个a就只能在method中使用。
方法结束,a变量也就"消失"了。
public class Day10Demo {
void method() {
int a = 1;
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
System.out.println(a);
System.out.println(i); // 编译错误
}
}
for循环中使用了一个i变量,i的作用域就在for循环这个代码块中,超出这个代码块使用就会编译错误。
局部变量还有一个注意点,在使用之前必须初始化。
如果像下面这么写,一样会出现编译错误:
package com.lazy.snail.day10;
/**
* @ClassName Day10Demo
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 14:28
* @Version 1.0
*/
public class Day10Demo {
void method() {
int a;
System.out.println(a);
}
}
IDE会提示你
TIP
如果你写过C/C++,你会有疑问,为什么C/C++允许没有初始化的局部变量,Java不允许。 Java只是想通过这种强制性的设置,提前暴露问题(编译期间,而不是运行时),确保安全性。
实例变量
实例变量是定义在类内部、但位于方法之外的变量,这些变量属于每个具体对象(实例)。
package com.lazy.snail.day10;
/**
* @ClassName Student
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 15:15
* @Version 1.0
*/
public class Student {
int id;
String name;
}
这个Student定义了两个变量,id(学号)、name(姓名)。
我们在main方法中创建两个实例:
package com.lazy.snail.day10;
/**
* @ClassName Student
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 15:15
* @Version 1.0
*/
public class Student {
int id;
String name;
public static void main(String[] args) {
Student student1 = new Student();
System.out.println(student1.id);
System.out.println(student1.name);
Student student2 = new Student();
System.out.println(student2.id);
System.out.println(student2.name);
}
}
输出发现,这两个变量好像都有默认值。int类型默认值0,String类型默认值null。
假如我们把student1的名字改成"懒惰蜗牛",student2的名字改成"蜗牛",再输出:
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "懒惰蜗牛";
System.out.println(student1.id);
System.out.println(student1.name);
Student student2 = new Student();
student2.name = "蜗牛";
System.out.println(student2.id);
System.out.println(student2.name);
}
student1和student2的name属性输出各是各的。
所以:
每个对象的实例变量独立存储,修改不影响其他对象(相当于每个对象都有变量副本)。
实例变量会自动初始化(给默认值),这点与局部变量不一样。
实例变量会随着对象的销毁而被清理。
TIP
实例变量最好使用private修饰,通过公共方法(get/set)进行访问,保证数据的安全性和可控性
类变量
类变量也可以叫静态变量或者static变量,是使用static关键字声明的变量,属于类本身,而不是具体的实例。
我们在上面Student类中加一个类变量"学校名称":
package com.lazy.snail.day10;
/**
* @ClassName Student
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 15:15
* @Version 1.0
*/
public class Student {
public static String schoolName = "懒惰蜗牛学院";
int id;
String name;
public static void main(String[] args) {
Student student = new Student();
student.id = 1;
student.name = "懒惰蜗牛";
System.out.println(student.schoolName);
System.out.println(Student.schoolName);
}
}
注意最后两行输出,一个使用的是类名Student、一个使用的是实例student。
上面不是说静态变量属于类吗?为什么实例也能访问?
那是因为编译器悄悄的把实例访问转换成了类名访问。
public static void main(String[] args) {
Student student = null;
System.out.println(student.schoolName);
}
student明明是null,用他去访问任何东西不是应该抛出NullPointerException吗?
运行一下,看看输出,正常输出了"懒惰蜗牛学院"。
其实这跟上面是一个道理,静态变量的访问,编译器都做了转换。
所以:
所有实例共享同一个类变量,内存中仅存一份。
类在被加载的时候,静态变量就会初始化(自动初始化默认值),程序结束,才销毁。
直接通过类名进行访问(不需要创建对象)。
TIP
虽然可以通过实例名访问静态变量,但是不推荐。
不可变变量
final修饰的变量表示****不可变的值或引用。
这个修饰符可以用来修饰上面我们提到的局部变量、成员、静态变量。
修饰局部变量
声明的时候直接初始化
void method() {
final int a = 2;
}
延迟初始化
void method() {
final int a;
a = 2;
}
如果你在a初始化后(已经赋值为2),想要修改a的值:
void method() {
final int a;
a = 2;
a = 3;
}
会得到一个编译错误:
修饰实例变量
必须在声明的时候或构造方法/实例初始化块****中赋值。
package com.lazy.snail.day10;
/**
* @ClassName Day10Demo
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 14:28
* @Version 1.0
*/
public class Day10Demo {
final int a = 5;
final int b;
public Day10Demo() {
b = 10;
}
}
a是在声明时就初始化的;b是在构造方法中才初始化的。
修饰类变量
必须在声明的时候或者静态初始化块里赋值。
package com.lazy.snail.day10;
/**
* @ClassName Day10Demo
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 14:28
* @Version 1.0
*/
public class Day10Demo {
static final int A = 1;
static final int B;
static {
B = 2;
}
}
A是在声明时初始化的;B是在静态代码块时初始化的。(这里名称大写是规范,不是强制性要求)
TIP
一般常量的标准写法是全大写 + 下划线分隔
所以:
final修饰的变量只能赋值一次,一旦初始化了,值或者引用就不能修改了。
必须初始化,要在声明的时候或者构造方法或者静态代码块中显式的赋值。
三、变量新特性
var局部变量类型推断
var是Java10引入的,编译器可以根据初始化值自动推断局部变量的类型。
package com.lazy.snail.day10;
/**
* @ClassName Day10Demo
* @Description TODO
* @Author lazysnail
* @Date 2025/5/16 14:28
* @Version 1.0
*/
public class Day10Demo {
public static void main(String[] args) {
int a = 1;
var b = 1;
}
}
b变量会被自动推断为int类型。
这个类型推断只适用于局部变量。
必须显示的初始化(不然没有推断依据了)。
不能用于方法参数、返回值或成员变量。
TIP
不要滥用var,否则未来哪一天的你看不懂你现在写的代码。
四、变量设计的一些原则
作用域最小化:限制变量的可见范围,降低耦合,不要动不动就全局变量。
不可变性优先:多用final,减少状态变化带来的复杂性。
命名即注释:就是所谓的见名知意,通过名称传达变量用途,减少注释依赖。
类型选择策略:基本类型追求性能,引用类型面向抽象。
结语
变量是Java程序的核心数据载体,分为基本类型(直接存储值)与引用类型(存储对象地址),作用域涵盖局部变量(栈内存)、实例变量(堆内存)和类变量(方法区)。
通过final关键字实现不可变性,保障数据安全;
遵循驼峰命名和类型精准化原则,提升代码可读性。
变量管理体现程序的状态逻辑和封装性,是构建健壮、可维护系统的基础。