Java static关键字怎么用?一文搞懂静态变量和方法
1 static修饰符
1.1 static简介
1.1.1 概述
static是Java静态修饰符,用于修饰类成员(变量/方法),核心特征:
1. 属于类(类成员),非单个对象;
2. 被所有对象共享,一处修改全局生效;
3. 类加载时初始化,无需创建对象即可访问。
- 属于类本身,而非类的单个对象,因此也称为“类成员”
- 被该类的所有对象共享,一处修改会影响所有对象的访问结果
- 加载时机早于对象,类加载时静态成员就已初始化,无需创建对象即可访问
典型场景:描述类对象公共属性,如所有中国人的国籍,定义为静态变量供共享。
1.1.2 案例实现
定义中国人类(Chinese),国籍(country)为静态变量,姓名等为普通变量,演示共享特性:
/**
* @ClassName: Chinese
* @Description: 中国人类,演示静态变量共享特性
*/
public class Chinese {
// 静态共享变量
public static String country = "中国";
// 对象独有变量
private String name;
private int age;
private String work;
// 构造器
public Chinese() {}
public Chinese(String name, int age, String work) {
this.name = name;
this.age = age;
this.work = work;
}
// getter/setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getWork() { return work; }
public void setWork(String work) { this.work = work; }
}测试类(StaticDemo)验证共享性:
/**
* @ClassName: StaticDemo
* @Description: 静态变量共享性测试
*/
public class StaticDemo {
public static void main(String[] args) {
Chinese c1 = new Chinese("王宝强", 34, "演员");
System.out.println(c1.getAge() + "岁的" + c1.getName() + "是" + c1.country + "人");
Chinese c2 = new Chinese("周星驰", 48, "演员/导演/制片");
System.out.println(c2.getAge() + "岁的" + c2.getName() + "是" + c2.country + "人");
// 修改静态变量
c1.country = "中华人民共和国";
System.out.println("===============================");
System.out.println(c1.getAge() + "岁的" + c1.getName() + "是" + c1.country + "人");
System.out.println(c2.getAge() + "岁的" + c2.getName() + "是" + c2.country + "人");
}
}运行结果:修改一个对象访问的静态变量后,所有对象访问结果均改变,验证共享性。
1.2 静态的使用方式及加载原理
1.2.1 访问方式规范
静态成员推荐类名直接访问(对象访问会警告),因静态成员属于类,与对象无关。
访问类型 | 语法格式 | 推荐度 |
静态变量 | 类名.静态变量名 | 强烈推荐 |
静态变量 | 对象名.静态变量名 | 不推荐(警告) |
静态方法 | 类名.静态方法名(参数) | 强烈推荐 |
静态方法 | 对象名.静态方法名(参数) | 不推荐(警告) |
1.2.2 静态方法的访问限制
静态方法加载早于对象,不能直接访问非静态成员(依赖对象);非静态方法可直接访问静态成员。
/**
* @ClassName: StaticDemo2
* @Description: 静态方法访问限制测试
*/
public class StaticDemo2 {
private String name = "张三"; // 非静态变量
// 非静态方法
public void normalMethod() {
System.out.println("非静态方法");
staticMethod(); // 可访问静态方法
}
// 静态方法
public static void staticMethod() {
System.out.println("静态方法");
// 错误:静态方法不能访问非静态成员
// System.out.println(name);
// normalMethod();
}
public static void main(String[] args) {
StaticDemo2.staticMethod(); // 类名直接调用
}
}2.final关键字
2.1 概述
final意为“最终的”,修饰类、方法、变量,核心作用是限制修改,保障代码安全。继承场景中可防止父类核心逻辑被篡改。
常见应用:定义常量(如PI)、限制核心类继承(如String)、限制核心方法重写。
2.2 具体使用方式
2.2.1 修饰类:不可被继承
final修饰的类不可继承(“类绝育”),如String、Math等核心类,确保功能不被篡改。
// final修饰类不可继承
final class Fu {
public void show() { System.out.println("父类方法"); }
}
// 错误:无法继承final类
// class Zi extends Fu {}2.2.2 修饰方法:不可被重写
final修饰的方法不可重写,但可继承调用,适用于父类固定逻辑的核心方法。
class Fu2 {
// final方法不可重写
final public void show1() { System.out.println("父类不可重写方法"); }
// 普通方法可重写
public void show2() { System.out.println("父类可重写方法"); }
}
class Zi2 extends Fu2 {
// 错误:无法重写final方法
// @Override
// public void show1() {}
// 正确重写普通方法
@Override
public void show2() { System.out.println("子类重写方法"); }
}2.2.3 修饰变量:不可重复赋值
final修饰的变量仅能赋值一次,按类型分局部变量和成员变量,赋值方式不同。
1. 修饰局部变量
基本类型final局部变量值不可改;引用类型地址不可改,但对象属性可修改。
public class FinalDemo1 {
public static void main(String[] args) {
// 基本类型局部变量
final int a = 10;
// 错误:不可重复赋值
// a = 20;
// 引用类型局部变量
final Chinese c = new Chinese("李四", 25, "教师");
// 错误:地址不可改
// c = new Chinese("王五", 30, "医生");
// 正确:属性可改
c.setName("李四更新");
System.out.println(c.getName());
}
}注意:循环中final局部变量每次循环是新变量,可正常赋值:
for (int i = 0; i < 3; i++) {
final int b = i; // 每次循环都是新的b,合法
System.out.println(b); // 输出:0、1、2
}2. 修饰成员变量
final成员变量需显式赋值(JVM默认值无效),仅一次赋值机会,两种方式:显式初始化(定义时赋值)、构造器初始化(所有构造器均需赋值)。
显式初始化:定义时直接赋值
构造器初始化:所有构造器中均赋值
public class Student {
final int num1 = 1001; // 显式初始化
final int num2; // 构造器初始化
public Student() { num2 = 2001; }
public Student(int num2) { this.num2 = num2; }
// 错误:普通方法不能赋值final成员变量
// public void setNum2(int num2) { this.num2 = num2; }
}命名规范:final静态常量全大写,多单词下划线分隔,如public static final int MAX_AGE = 120;
3 抽象类与抽象方法
3.1 抽象类与抽象方法的引入
部分类仅描述共性,无需创建对象(如“员工”),具体功能由子类实现。不同子类对同一方法实现不同,需抽象类和抽象方法解决。
为解决这种场景,Java引入抽象类和抽象方法:
抽象方法:仅签名无方法体,abstract修饰
抽象类:含抽象方法的类,abstract修饰,不能创建对象
3.2 定义格式
类型 | 定义格式 | 说明 |
抽象类 | abstract class 类名 { ... } | abstract在class前 |
抽象方法 | 修饰符 abstract 返回值类型 方法名(参数列表); | 无方法体,分号结束 |
3.3 案例实现
以“员工-服务员”为例,抽象员工类定义抽象工作方法,服务员类实现该方法:
/**
* @ClassName: Employee
* @Description: 抽象员工类
*/
public abstract class Employee {
public abstract void work(); // 抽象工作方法
}
/**
* @ClassName: Waiter
* @Description: 服务员类(实现抽象方法)
*/
public class Waiter extends Employee {
@Override
public void work() {
System.out.println("服务员:引导就座,记录订单,上菜");
}
}
/**
* @ClassName: AbstractDemo
* @Description: 抽象类测试
*/
public class AbstractDemo {
public static void main(String[] args) {
// 错误:抽象类不能创建对象
// Employee e = new Employee();
Waiter waiter = new Waiter();
waiter.work();
}
}3.4 抽象类的构造方法
抽象类不能创建对象,但有构造方法,供子类调用以初始化父类成员变量,子类对象可使用这些变量。
/**
* @ClassName: Employee
* @Description: 带构造器的抽象员工类
*/
public abstract class Employee {
private String name;
private String id;
public Employee(String name, String id) {
this.name = name;
this.id = id;
}
public abstract void work();
// getter
public String getName() { return name; }
public String getId() { return id; }
}
/**
* @ClassName: Manager
* @Description: 经理类(继承抽象类)
*/
public class Manager extends Employee {
public Manager(String name, String id) {
super(name, id); // 调用父类构造器
}
@Override
public void work() {
System.out.println(getName() + "(工号:" + getId() + "):管理员工,制定计划");
}
}
// 测试类
public class AbstractDemo2 {
public static void main(String[] args) {
Manager manager = new Manager("张三", "M001");
manager.work();
}
}3.5 抽象类的核心特点与注意事项
3.5.1 核心特点
- 抽象类和方法需abstract修饰,抽象方法必在抽象类中
- 抽象类不能直接创建对象,需通过非抽象子类
- 子类需重写所有抽象方法,否则子类也为抽象类
- 抽象类体现模板思想,封装共性,抽象方法定规范
3.5.2 常见误区解析
- 误区1:抽象类仅含抽象方法? 纠正:可含普通方法、变量、构造器,纯普通方法抽象类用于限制创建对象。
- 误区2:子类需重写所有方法? 纠正:仅重写抽象方法,普通方法可继承。
- 误区3:抽象方法有方法体? 纠正:无方法体,有则非抽象方法。
3.6 模板模式(抽象类的典型应用)
模板模式:抽象父类定义固定流程(普通方法),不确定步骤为抽象方法由子类实现,保证流程一致、细节灵活。
案例:新司机和老司机开车的流程一致(开门→点火→驾驶→刹车→熄火),但驾驶姿势不同,用模板模式实现:
/**
* @ClassName: Driver
* @Description: 司机抽象类(模板类)
*/
public abstract class Driver {
// 模板方法:固定流程
public final void drive() {
System.out.println("1. 开门上车");
System.out.println("2. 点火启动");
drivePosture(); // 抽象步骤
System.out.println("4. 刹车停车");
System.out.println("5. 熄火下车");
}
// 抽象方法:驾驶姿势(子类实现)
public abstract void drivePosture();
}
// 新司机类
public class NewDriver extends Driver {
@Override
public void drivePosture() {
System.out.println("3. 新司机:双手握方向盘,目视前方");
}
}
// 老司机类
public class OldDriver extends Driver {
@Override
public void drivePosture() {
System.out.println("3. 老司机:右手握盘,左手搭扶手");
}
}
// 测试类
public class TemplateDemo {
public static void main(String[] args) {
new NewDriver().drive();
System.out.println("====================");
new OldDriver().drive();
}
}总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
详解Java8 CompletableFuture的并行处理用法
Java8中有一个工具非常有用,那就是CompletableFuture,本章主要讲解CompletableFuture的并行处理用法,感兴趣的小伙伴可以了解一下2022-04-04
springboot使用RedisRepository操作数据的实现
本文主要介绍了springboot使用RedisRepository操作数据的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2022-05-05
Springboot实现连接多个ElasticSearch数据库的过程
这篇文章介绍了如何在Springboot项目中实现与多个Elasticsearch数据库的高效交互,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧2026-03-03


最新评论