04、字面量(直接量)、变量、常量

一、Java 中的 字面量(直接量)


1、定义

  • 字面量直接量)是代码中直接表示 字面形式,无需通过变量计算获得。

2、分类


2.1、整型(整数直接量)

  • 十进制:123
  • 十六进制:0x10、0X10 表示十进制中的 16 。
  • 八进制:010 表示十进制中的 8 。
  • 八进制表示法比较容易混淆。所以,很少有程序员使用八进制常数
  • 二进制:0b10、0B10 表示十进制中的 2 。
  • 长整型:100L

2.2、浮点型(浮点数直接量)

  • 单精度后缀:3.14f、3.14F
  • 双精度后缀:3.14 或 3.14d

2.3、字符与字符串(字符直接量)

  • 字符:‘A’、‘汉’、‘中’
  • 转义字符:\n(换行)、\t(制表符)等。

2.4、字符串(字符串直接量)

  • 字符串:“Hello world”

2.5、布尔型(布尔直接量)

  • true: 真。
  • false: 假。

2.6、空引用(空引用直接量)

  • null: 空对象引用。
int a = 10;        // 10 是整数字面量
String s = "abc";  // "abc" 是字符串字面量
boolean b = true;  // true 是布尔字面量

3、 特点

  • 不可变:直接量的在代码中固定,无法修改
  • 无存储位置:直接量本身不是变量,只是值的字面表示。
  • 内存优化字符串直接量会被 JVM 放入 字符串常量池,实现重用

二、Java 中的 常量(Constans)


1、定义

  • 常量是程序运行期间不可变变量,通常通过final 关键字声明。
    * 即:通过 final 关键字声明的变量常量
    • 关键字 final 表示这个变量只能被赋值一次。一旦赋值,就不能再更改了。
  • 常量名 使用全大写,有多个单词时,用 _ 隔开。
    • 如:MAX_VALUE
final int MAX_VALUE = 100;             // 基本类型常量 // 100 是整数字面量
final String GREETING = "Hello world"; // 引用类型常量 // "Hello world" 是字符串字面量


// Java 10+ 的 var 不能用于声明常量,必须显式指定 final:
final var PI = 3.14; // 合法,PI 是常量(类型推断为 double)
var MAX = 100;       // MAX 是普通变量,可修改(非常量)


// 枚举常量。 如:RED, GREEN, BLUE 是枚举常量
enum Color { RED, GREEN, BLUE }
  • 在 Java 中,可能经常需要创建一个常量以便在多个类的多个方法中使用。
    • 通常将这些常量称为 类常量(class constant)。
    • 可以使用关键字 static final 设置一个 类常量
      • 包含 枚举常量接口常量
public class HongTiHuan {
// ✅ 编译时常量 :会发生 宏替换。
    public static final int MAX = 100;
    private static final double PI = 3.1415926;
    public static final String GREETING = "Hello";
    // 编译时拼接
    static final String MESSAGE = GREETING + " World!";

    // ✅ 表达式常量: 62.831...
    static final double CIRCUMFERENCE = 2 * Math.PI * 10;

    // ✅ 静态块中赋值的编译时常量
    public static final String DB_URL;
    
    static {
        // 直接字面量
        DB_URL = "jdbc:mysql://localhost:3306/mydb";
    }
}

2、分类


2.1、编译时常量


  • 值在编译时确定(如:final int X = 5 + 3;),会被编译器 优化(类似直接量)。
  • 必须满足的条件:
    • final 修饰的 基本类型 String
    • 局部变量声明直接赋值) 或 类变量声明 直接赋值 或 在静态块中初始化)。
      • 实例变量 就算是在声明直接赋值。编译后,赋值的字节码指令,被放在构造器中。
        • 因此,实例变量不属于编译时常量。
    • 字面量常量表达式 构成不是运行时计算)。
public class HongTiHuan {
    // ✅ 表达式常量: 62.831...
    static final double CIRCUMFERENCE = 2 * Math.PI * 10;
    
    public static void main(String[] args) {
        System.out.println("CIRCUMFERENCE:" + CIRCUMFERENCE);

        final String finalWorld2 = "hello";
        System.out.println("finalWorld2:" + finalWorld2);
    }
}

2.2、运行时常量

  • 值在运行时确定(如 final int NOW = LocalTime.now().getHour();)。
public class HongTiHuan {
    // ❌ 非常量表达式(包含方法调用)
    public static final double AREA = Math.PI * Math.pow(10, 2);

    // 示例变量
    int age = 20;

    // 类变量
    public static String DB_URL;
    static {
        // 直接字面量
        DB_URL = "jdbc:mysql://localhost:3306/mydb";
    }

    public static void main(String[] args) {
        System.out.println("AREA:" + AREA);

        HongTiHuan hongTiHuan = new HongTiHuan();
        System.out.println(hongTiHuan.age);

        System.out.println("DB_URL:" + DB_URL);

        String hw = "hello world";
        System.out.println("hw:" + hw);
    }
}

3、特点

  • 不可变:常量一旦初始化后,值不能再修改。
  • 存储位置:常量是变量,有明确的内存地址

4、编译时常量化 – (类似:宏替换)


4.1、概述


  • 在 Java 中,编译时常量化(Compile-Time Constant) :
    • 是指某些 变量表达式的值 在编译阶段就能被确定,并被编译器直接替换字面量的过程。
  • 这种常量化的核心是值不可变,且其值在编译时即可完全计算出来。

4.2、示例


  • 源码
public class HongTiHuan {
    // ✅ 表达式常量: 62.831...
    static final double CIRCUMFERENCE = 2 * Math.PI * 10;

    // ❌ 非常量表达式(包含方法调用)
    public static final double AREA = Math.PI * Math.pow(10, 2);

    // 实例变量
    int age = 20;

    // 类变量
    public static String DB_URL;
    static {
        // 直接字面量
        DB_URL = "jdbc:mysql://localhost:3306/mydb";
    }

    public static void main(String[] args) {
        System.out.println("CIRCUMFERENCE:" + CIRCUMFERENCE);
        System.out.println("AREA:" + AREA);

        HongTiHuan hongTiHuan = new HongTiHuan();
        System.out.println(hongTiHuan.age);

        System.out.println("DB_URL:" + DB_URL);

        String hw = "hello world";
        System.out.println("hw:" + hw);

        final String finalWorld2 = "hello";
        System.out.println("finalWorld2:" + finalWorld2);
    }
}
  • class 文件,反编译之后的代码:
public class HongTiHuan {
    static final double CIRCUMFERENCE = 62.83185307179586;
    public static final double AREA = Math.PI * Math.pow((double)10.0F, (double)2.0F);
    int age = 20;
    public static String DB_URL = "jdbc:mysql://localhost:3306/mydb";

    public static void main(String[] args) {
        // 编译时常量化 -- (类似:宏替换)
        System.out.println("CIRCUMFERENCE:62.83185307179586");
        
        System.out.println("AREA:" + AREA);
        HongTiHuan hongTiHuan = new HongTiHuan();
        System.out.println(hongTiHuan.age);
        System.out.println("DB_URL:" + DB_URL);
        String hw = "hello world";
        System.out.println("hw:" + hw);
        
        String finalWorld2 = "hello";
        // 编译时常量化 -- (类似:宏替换)
        System.out.println("finalWorld2:hello");
    }
}

4.3、小结


  • 小结:
变量类型是否可发生宏替换必要条件
static final 类变量✅ 是基本类型 / String,声明时或静态块中赋常量值
final 实例变量❌ 否值在运行时确定,属于对象级别
final 局部变量✅ 是(局部)基本类型 / String,声明时赋常量值

4.4、应用 – 字符串比较


  • 单独使用 “” 引号创建的字符串都是常量,编译期就已经确定存储到常量池中;
  • 使用 new String(“”) 创建的对象会存储到堆内存中,是运行期新创建的;
  • 使用只包含常量的字符串连接符如 “aa” + “bb” 创建的也是常量。
    • 编译期就能确定,已经确定存储到常量池中;
  • 使用包含变量的字符串连接符(如:“aa” + s1 )创建的对象是运行期才创建的,存储在堆中;
public static void main(String[] args) {
    String nb1 = "final";
    String nb2 = "test";

    String nb4 = nb1 + nb2;
    // 不是宏变量:编译期间值不能确定
    final String nb6 = nb1 + nb2;

    String nb = "finaltest";

    // 编译时常量:宏变量1
    final String nb11 = "final";
    // 编译时常量:宏变量2
    final String nb22 = "test";
    // 宏替换
    String nb5 = nb11 + nb22;
    
    // 初始编译的时候 nb3 会确定初始值为 finaltest
    String nb3 = "final" + "test";

    // true
    System.out.println(nb == nb3);
    // false
    System.out.println(nb == nb4);
    // true
    System.out.println(nb == nb5);
    // false
    System.out.println(nb == nb6);
    // false
    System.out.println(nb4 == nb6);
}

三、Java 中的 变量


  • 与所有程序设计语言一样,Java 也使用变量存储值
    • 常量就是 不变变量
  • Java语言是一个 强类型语言
    • 所有变量,必须先声明类型后使用
    • 指定类型的变量只能装该类型对应的数据

1、定义

  • 变量:在程序的执行过程中,它的是可以被改变的。
  • 语法格式:
【修饰符】类型  field名 = 默认值;
  • 变量名的命名规则:
    • 首字母小写,有多个单词组成时,要满足驼峰标识

2、变量的声明、赋值、使用、修改

  • 声明栈内存申请空间
    • 对于基本类型的变量(如:int、float 等),声明时系统会为其分配固定的内存空间。
      • 如:int 类型占用 4 字节,double 类型占用 8 字节‌。
    • 对于引用类型的变量(如:对象、数组等),声明时不会直接分配内存来存储数据
      • 而是分配一个存储引用值的内存空间。
      • 这个引用指向实际对象数组的内存地址‌
  • 赋值向内存中填内容
    • new 指令就是向堆内存申请空间,并为成员变量 赋予默认值,此时对象处于半初始化状态
      • 接着:调用对象的构造方法,为 变量 赋值
  • 使用通过名字调用内存中的值
  • 修改用新的值替代旧的值
// 变量的声明
int vacationDays;
// 变量的初始化
vacationDays = 12;

// 变量的声明 和 初始化
int vacationDays1 = 12;

3、局部变量(可以有的修饰符:final)


  • 方法中的 参数方法语句块内部定义的变量
    • 方法里的局部变量,仅在该方法内有效。
    • 代码块里的局部变量,仅在代码块内有效。
    • 形参,在整个方法内有效。

  • 规则:
    • 声明一个局部变量后,必须用赋值语句显式地初始化变量。
      • 编译器不会为局部变量分配默认值
    • 在使用局部变量之前,必须显式初始化它们,否则将发生编译错误
public class Example {  
    public static void main(String[] args) {  
        int x;  

        // Compilation error: The local variable x may not have been initialized  
        System.out.println(x); 
    }  
}  
  • 在 Java 10 中,如果可以从变量的初始值推导出变量数据类型
    • 那么,可以用 var 关键字声明局部变量,而无须指定数据类型
      • 即:从等号右边明显看出数据类型,在这种情况下都可以使用 var 表示法。
  • 注意:var 关键字只能用于方法中的局部变量参数字段数据类型必须声明
var date = new Date();

4、实例变量 / 对象属性


  • 从有实例开始,实例变量就存在了。
  • 实例被销毁,实例变量就不存在了。
  • 0~N个,程序每创建一个实例,系统就为实例变量分配一块内存。

  • 规则:系统会为成员变量执行默认的初始化。
    • 整型初始化为 0
    • 浮点型初始化为 0.0
    • boolean 类型初始化为 false
    • char 类型的初始化为 ‘\u0000’
    • 引用数据类型初始化为 null 。

5、类变量 / 静态属性 / 类属性

  • 从类开始,类变量就存在了。
  • 一个 JVM 里,类信息只有一个。
  • 每个 JVM 最多只加载一个类一次,系统只为类变量分配一块内存。
  • 规则:系统会为成员变量执行默认的初始化。
    • 整型初始化为 0
    • 浮点型初始化为 0.0
    • boolean 类型初始化为 false
    • char 类型的初始化为 ‘\u0000’
    • 引用数据类型初始化为 null 。
public class AboutNull {

    static int staticFiled = 123;
    int ordinaryFiled = 123;

    private static void staticMethod(){ System.out.println("静态方法:staticMethod"); }

    private void ordinaryMethod(){ System.out.println("普通方法:ordinaryMethod"); }

    public static void main(String[] args) {
        AboutNull aboutNull = null;
        // false
        System.out.println(aboutNull instanceof AboutNull);

        // 通过上边 实例变量 来调用静态属性和静态方法,没有抛出 NullPointerException 异常。
            // 验证:当使用实例变量来调用静态元素时,底层实际上是通过委托类来访问的。
        // 输出: 静态方法:staticMethod
        aboutNull.staticMethod();
        // 输出: 123
        System.out.println(aboutNull.staticFiled);

        // 抛出异常:NullPointerException
        System.out.println(aboutNull.ordinaryFiled);
        // 抛出异常:NullPointerException
        aboutNull.ordinaryMethod();

        aboutNull = new AboutNull();
        // true
        System.out.println(aboutNull instanceof AboutNull);
    }
}
  • 注意:当程序 通过实例来访问 类变量 时----由于类变量本身就不属于实例
    • 因此,底层实际上委托为,通过 来访问的

6、为什么成员变量命名不建议用 isXXX?



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值