【Java篇】静动交融,内外有别:从静态方法到内部类的深度解析

文章目录

  • 类和对象(下)
    • [八、static 关键字](#八、static 关键字)
      • [8.1 静态变量](#8.1 静态变量)
        • [8.1.1 概念](#8.1.1 概念)
        • [8.1.2 访问方式](#8.1.2 访问方式)
      • [8.2 静态方法](#8.2 静态方法)
        • [8.2.1 概念](#8.2.1 概念)
        • [8.2.2 访问限制](#8.2.2 访问限制)
      • [8.3 static成员变量初始化](#8.3 static成员变量初始化)
      • [8.4 静态成员的使用场景](#8.4 静态成员的使用场景)
    • 九、代码块
      • [8.1 代码块概念以及分类](#8.1 代码块概念以及分类)
      • [8.2 普通代码块(局部代码块)](#8.2 普通代码块(局部代码块))
      • [8.3 构造代码块(实例代码块)](#8.3 构造代码块(实例代码块))
        • [8.3.1 执行时机](#8.3.1 执行时机)
        • [8.3.2 作用和特点](#8.3.2 作用和特点)
      • [8.4 静态代码块(静态初始化块)](#8.4 静态代码块(静态初始化块))
        • [8.4.1 执行时机](#8.4.1 执行时机)
        • [8.4.2 使用场景](#8.4.2 使用场景)
        • [8.4.3 示例](#8.4.3 示例)
        • [8.4.4 注意事项](#8.4.4 注意事项)
      • [8.5 同步代码块(了解)](#8.5 同步代码块(了解))
    • 十、内部类
      • [10.1 内部类概述](#10.1 内部类概述)
      • [10.2 成员内部类](#10.2 成员内部类)
        • [10.2.1 基本语法](#10.2.1 基本语法)
        • [10.2.2 访问规则](#10.2.2 访问规则)
      • [10.3 静态内部类](#10.3 静态内部类)
        • [10.3.1 基本语法](#10.3.1 基本语法)
        • [10.3.2 访问方式](#10.3.2 访问方式)
      • [10.4 局部内部类](#10.4 局部内部类)
        • [10.4.1 特点](#10.4.1 特点)
      • [10.5 匿名内部类(抽象类和接口时详细介绍)](#10.5 匿名内部类(抽象类和接口时详细介绍))
        • [10.5.1 基本写法](#10.5.1 基本写法)
        • [10.5.2 特点](#10.5.2 特点)
      • [10.6 小结](#10.6 小结)
    • 十一、对象的打印
      • [11.1 Object 的 toString()](#11.1 Object 的 toString())
      • [11.2 重写 toString()](#11.2 重写 toString())
        • [11.2.1 基本示例](#11.2.1 基本示例)
        • [11.2.2 重写要点](#11.2.2 重写要点)
      • [11.3 打印数组](#11.3 打印数组)
      • [11.4 小结](#11.4 小结)
    • 十二、总结与展望

类和对象(下)

💬 欢迎讨论:如果你对本篇内容有任何疑问或想深入探讨,欢迎在评论区留言交流!

👍 点赞、收藏与分享:觉得内容有帮助就请点赞、收藏并分享给更多学习Java的小伙伴!

🚀 继续学习之旅 :本篇文章将详细讲解 static 关键字、代码块以及内部类的相关概念和使用,让你在面向对象的世界中更进一步,玩转 Java 的高级特性!


八、static 关键字

static(静态)是 Java 中的一个关键字,主要用于修饰类中的变量或方法。被 static 修饰的成员属于 类本身 而非某个实例对象。它在编译阶段就会被加载到方法区中(Java 8 之后元空间/Metaspace),无需通过创建对象就能访问。其生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

8.1 静态变量

8.1.1 概念
  • 非静态变量:也称为实例变量,每创建一个对象就会产生一份新的变量副本。
  • 静态变量:也称为类变量,只有一份共享的副本,所有该类对象都可以访问和修改。
java 复制代码
public class Counter {
    public static int count = 0; // 静态变量
    public int id;              // 实例变量

    public Counter() {
        count++;
        this.id = count;
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        System.out.println(Counter.count); // 2
        System.out.println(c1.id);         // 1
        System.out.println(c2.id);         // 2
    }
}
  • count 为静态变量,全类共享;
  • id 为实例变量,每个对象拥有自己的 id 值。
8.1.2 访问方式
  • 类名.静态变量(推荐)
  • 对象名.静态变量(不推荐,容易引起误解)
java 复制代码
System.out.println(Counter.count); // 推荐
System.out.println(c1.count);      // 不推荐

8.2 静态方法

8.2.1 概念
  • static 修饰的方法属于类本身。
  • 无需创建对象,就可以通过 类名.方法名() 的方式直接调用。
  • 在静态方法中,不能 直接访问非静态成员(因为实例成员依赖于对象的存在,而静态方法执行时可能尚未创建任何对象)。
java 复制代码
public class MathUtil {
    public static int add(int a, int b) {
        return a + b;
    }
    
    public static void main(String[] args) {
        int sum = MathUtil.add(3, 5);
        System.out.println(sum); // 8
    }
}
8.2.2 访问限制
  • 静态方法中只能访问 静态成员
  • 非静态方法中则可以同时访问静态成员和非静态成员。

8.3 static成员变量初始化

注意:静态成员变量一般不会在构造方法中进行初始化,因为构造方法与对象实例关联,而静态成员变量则属于类本身。

静态成员变量的初始化有两种方式:

  1. 就地初始化:在定义时直接给出初值
  2. 静态代码块初始化 :在 static { ... } 代码块中完成赋值(下文再讲)

就地初始化示例代码:

java 复制代码
public class Student {
    private String name;
    private String gender;
    private int age;
    
    // 静态成员变量就地初始化
    private static String classRoom = "Bit36";

    // 构造方法、普通方法、get/set方法等
    // ...
}

8.4 静态成员的使用场景

  1. 工具类/工具方法 :如 Math 类的 sqrt()abs() 等,方便直接通过类名调用。
  2. 单例模式:通过静态字段持有唯一实例。
  3. 计数器:统计对象数量或方法调用次数。
  4. 常量池public static final 定义常量,方便全局使用且避免重复创建。

九、代码块

8.1 代码块概念以及分类

  • 定义 :在 Java 中使用 {} 包裹的一段或多段代码,即可称为"代码块"。
  • 常见分类
    1. 普通(局部)代码块:最常见,出现在方法体内部、流程控制语句中等,用来限制局部变量作用域或组织逻辑。
    2. 构造代码块(实例代码块) :定义在类中、方法外,不带 static,在构造方法执行之前运行,每次创建对象都会执行。
    3. 静态代码块(静态初始化块) :使用 static {} 修饰,类加载时(只一次)执行,用来初始化静态成员或做只需执行一次的操作。
    4. 同步代码块 :用 synchronized(锁对象) { ... } 来实现多线程同步,控制临界区内的线程安全,(入门只需了解)。

8.2 普通代码块(局部代码块)

普通代码块 通常指在方法或语句块内部,用花括号 {} 包裹的那部分代码。它的主要功能是局部作用域的划分。

示例

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 普通(局部)代码块
        {
            int x = 10;
            System.out.println("x = " + x);
        }

        // x 的作用域仅限于上面的 {} 之内
        // System.out.println(x); // 编译报错

        // 再次声明一个同名变量 x,不会冲突
        int x = 100;
        System.out.println("x2 = " + x);
    }
}

输出

java 复制代码
x = 10
x2 = 100

要点

  1. 普通代码块会限制局部变量的作用范围。
  2. 常用于封装临时逻辑或缩短变量生命周期,防止与其他同名变量冲突或占用资源太久。

8.3 构造代码块(实例代码块)

构造代码块 (又称"实例初始化块")是指在类中、方法外 ,但没有 static 修饰的一段 {} 代码。它在创建对象 时会先于构造方法执行,用来对实例成员进行初始化或执行公共逻辑。从先于构造函数体执行的思想上看和C++的初始化列表有相似之处。

8.3.1 执行时机
  • 每次调用 new 构造对象时,都会先执行构造代码块 ,然后再执行构造方法
  • 如果有多个构造方法,则不管调用哪一个,都会先执行这段构造代码块。

示例

java 复制代码
public class Student {
    private String name;
    private int age;

    // 构造代码块(实例代码块)
    {
        this.name = "bit";
        this.age = 12;
        System.out.println("I am instance init()!");
    }

    public Student() {
        System.out.println("I am Student init()!");
    }

    public void show() {
        System.out.println("name: " + name + "  age: " + age);
    }

    public static void main(String[] args) {
        Student stu = new Student();
        stu.show();
    }
}

输出

java 复制代码
I am instance init()!
I am Student init()!
name: bit  age: 12

可以看到,构造代码块 先于构造方法执行。

8.3.2 作用和特点
  1. 作用 :可在对象创建前做一些实例变量的初始化或公共操作。
  2. 执行次数 :与 new 对象的次数相同,每次创建对象都会执行一次。
  3. 与构造方法的关系
    • 若有多个构造方法,可将公共初始化操作放到构造代码块,避免重复代码。
    • 执行顺序:构造代码块构造方法

8.4 静态代码块(静态初始化块)

静态代码块 使用 static {} 修饰,是属于类级别 的初始化逻辑。它会在类加载的时候执行一次,不随着对象创建反复执行。

8.4.1 执行时机
  • 类第一次加载时,JVM 执行所有静态代码块。
  • 只执行一次,与后续创建多少对象无关。
8.4.2 使用场景
  • 初始化静态成员变量
  • 进行一次性的操作(如注册驱动、加载配置等)。
8.4.3 示例
java 复制代码
public class Student {
    private String name;
    private int age;
    private static String classRoom;

    // 静态代码块
    static {
        classRoom = "bit306";
        System.out.println("I am static init()!");
    }

    // 构造代码块
    {
        this.name = "bit";
        this.age = 12;
        System.out.println("I am instance init()!");
    }

    public Student() {
        System.out.println("I am Student init()!");
    }

    public static void main(String[] args) {
        System.out.println("----开始 main 方法----");
        Student s1 = new Student();
        Student s2 = new Student();
    }
}

输出顺序示例

java 复制代码
I am static init()!
----开始 main 方法----
I am instance init()!
I am Student init()!
I am instance init()!
I am Student init()!
8.4.4 注意事项
  1. 静态代码块只执行一次,无论创建多少对象都不会重复执行。
  2. 如果在同一个类里定义多个 static {},会按照代码顺序依次执行。(即合并)
  3. 静态环境下无法访问实例成员;要访问实例变量或实例方法,需要先创建对象。

8.5 同步代码块(了解)

"同步代码块"并不是为了初始化而存在,而是为了解决多线程并发访问同一资源时的线程安全问题。写法一般是:

java 复制代码
synchronized(锁对象) {
    // 需要线程同步的代码
}
  • 当一个线程进入该代码块并持有"锁对象"时,其他线程只能等待,直到该线程执行完毕并释放锁。
  • 通常"锁对象"可用 this(当前实例)、某个类对象、或专门的锁实例等。

十、内部类

内部类(Inner Class)是将一个类的定义放在另一个类的内部,从而形成逻辑上的隶属关系。Java 提供了多种内部类形式,包括成员内部类静态内部类局部内部类 以及匿名内部类。通过内部类,我们可以更好地封装和管理代码结构,也能直接访问外部类的私有成员,增强代码的灵活性和可读性。


10.1 内部类概述

  1. 定义:在一个类的内部再定义一个类(或接口),该类称之为"内部类"或"嵌套类"。
  2. 分类
    • 成员内部类(非静态内部类)
    • 静态内部类
    • 局部内部类(定义在方法或代码块内)
    • 匿名内部类(没有类名,直接定义并实例化)
  3. 好处
    • 内部类可以直接访问外部类的成员(包括私有成员),从而简化了访问操作。
    • 逻辑上隶属关系更清晰,起到封装隐藏的作用。
    • 通过匿名内部类等方式,可以使代码更简洁,尤其在回调或事件监听等场景中。

10.2 成员内部类

成员内部类 又叫非静态内部类 ,它是定义在外部类的成员位置(与外部类的成员变量、方法同级)但不带 static 关键字的内部类。

10.2.1 基本语法
java 复制代码
public class Outer {
    private String name = "OuterName";

    // 成员内部类
    public class Inner {
        public void show() {
            // 直接访问外部类的私有成员
            System.out.println("Outer name: " + name);
        }
    }

    public void test() {
        Inner inner = new Inner();
        inner.show();
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test();
    }
}

执行流程

  1. 创建 Outer 对象:Outer outer = new Outer();
  2. outer.test() 方法中,实例化 InnerInner inner = new Inner();
  3. 调用 inner.show(),可以直接访问 Outer 类中的 name
10.2.2 访问规则
  • 内部类可以直接访问外部类的所有成员 (包括 private)。
  • 若内部类成员与外部类成员同名,可用 外部类名.this.成员 的方式区分,例如 Outer.this.name
  • 在外部类的非静态方法中,可以直接 创建内部类实例;在外部类的静态方法或其他类中,则需通过 外部类对象.new 内部类构造() 来创建。

10.3 静态内部类

静态内部类,也称静态嵌套类 ,使用 static 修饰。它与成员内部类的主要区别在于:

  1. 静态内部类只能访问外部类的静态成员,无法直接访问外部类的非静态成员。
  2. 创建静态内部类的对象时,不需要外部类对象的实例。
10.3.1 基本语法
java 复制代码
public class Outer {
    private String name = "OuterName";
    private static String staticName = "StaticOuterName";

    // 静态内部类
    public static class Inner {
        public void show() {
            // 只能直接访问外部类的静态成员
            System.out.println("Outer staticName: " + staticName);

            // System.out.println(name); // 非静态成员,无法直接访问
        }
    }

    public static void main(String[] args) {
        // 不需要外部类对象,直接创建静态内部类对象
        Outer.Inner inner = new Outer.Inner();
        inner.show();
    }
}
10.3.2 访问方式
  • 静态内部类 对象的创建方式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
  • 静态内部类中的实例方法,依然需要创建内部类实例来调用;但如果有静态方法或静态变量,可以通过 Outer.Inner.静态方法Outer.Inner.静态变量 直接访问。

10.4 局部内部类

局部内部类 是定义在方法体代码块内部的类,只在该方法或代码块中可见和使用。局部内部类可以看作"更局部化"的内部类,常用于一些只在某个方法中使用的场景。

java 复制代码
public class Outer {
    public void method() {
        class Inner {
            public void show() {
                System.out.println("I am local inner class!");
            }
        }
        // 在方法内部创建并使用
        Inner inner = new Inner();
        inner.show();
    }

    public static void main(String[] args) {
        new Outer().method();
    }
}
10.4.1 特点
  1. 作用域限制:只能在定义它的方法或代码块中创建并使用。
  2. 访问外部变量 :可以访问外部类的成员,也可以访问该方法中被 final 或"事实上的最终"变量所修饰的局部变量(Java 8+ 开始,只要不修改该变量即可,不必显式 final)。

10.5 匿名内部类(抽象类和接口时详细介绍)

匿名内部类 (Anonymous Inner Class)没有类名,通常用于简化创建某些接口或抽象类子类对象的过程,尤其在回调、事件处理等场景中使用广泛。

10.5.1 基本写法
java 复制代码
interface ITest {
    void func();
}

public class Demo {
    public static void main(String[] args) {
        // 匿名内部类:直接 new 接口(或抽象类),然后立刻重写其中的方法
        ITest test = new ITest() {
            @Override
            public void func() {
                System.out.println("Anonymous Inner Class func");
            }
        };

        test.func();
    }
}
  1. new 接口/抽象类() { ... }:创建一个实现该接口或继承该抽象类的匿名子类对象。
  2. 匿名:没有类名,直接在此处定义并实例化。
10.5.2 特点
  1. 只能使用一次:若需要多次创建同样功能的对象,通常还是单独定义一个类或使用 Lambda(对于函数式接口)更好。
  2. 简化代码:不用显式定义一个实现类/子类。

10.6 小结

  1. 成员内部类
    • 需要先创建外部类对象,再通过 外部类对象.new 内部类() 来实例化。
    • 可以访问外部类的所有成员。
  2. 静态内部类
    • 使用 static 修饰,只能直接访问外部类的静态成员。
    • 无需外部类实例即可创建,使用 外部类名.内部类名 方式。
  3. 局部内部类
    • 定义在方法或代码块内部,仅在该方法/代码块中可见。
    • 可以访问外部类的成员,也可以访问方法内"事实上的最终"变量。
  4. 匿名内部类
    • 没有类名,常用于简化接口或抽象类的实现。
    • 使用场景多在回调、事件监听等。

掌握内部类的使用场景与写法,可以使代码的封装性更好,也能让某些实现方式更灵活、更简洁。

在实际开发中,根据需求选择合适的内部类形式:

  • 若需要频繁调用且功能独立,建议成员内部类静态内部类
  • 若仅在某个方法中临时使用,且逻辑不复杂,可选择局部内部类匿名内部类来简化代码。

十一、对象的打印

在 Java 中,当我们使用 System.out.println(obj); 或字符串拼接(如 "" + obj)来打印一个对象时,实质上是自动调用 该对象的 toString() 方法。如果类中没有重写 toString(),则默认会调用 Object 类的 toString() 方法,打印出类似 类名@哈希值 的信息(如 com.bit.demo.Student@3f99bd52),往往并不是我们想要的"可读输出"。


11.1 Object 的 toString()

  1. 默认实现ObjecttoString() 返回的字符串格式一般是:

    java 复制代码
    getClass().getName() + "@" + Integer.toHexString(hashCode())

    也就是"类的完整名称@哈希码"形式,便于识别对象在内存中的"标识",但并不展示对象的具体属性信息。

  2. 为什么常被打印?

    • 在使用 System.out.println(对象引用); 或字符串拼接时,会自动调用 toString()
    • 如果没有重写该方法,就会使用 Object 的默认实现。

11.2 重写 toString()

为了打印出更有意义的信息,我们通常会在自定义的类中重写(Override) toString() 方法。这样当打印对象时,就能输出该对象的关键属性值或其他说明性内容。

11.2.1 基本示例
java 复制代码
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写 toString() 方法
    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }

    public static void main(String[] args) {
        Student stu = new Student("Alice", 20);
        System.out.println(stu); 
        // 自动调用 stu.toString() => 输出: Student{name='Alice', age=20}
    }
}
11.2.2 重写要点
  1. 方法签名 :必须是 public String toString(),且带有 @Override 注解(可选但建议)。
  2. 返回值:返回一个可读性强、能够体现对象核心信息的字符串。
  3. 风格多样:可以根据需要自定义输出格式,或使用 JSON、XML 等形式。
  4. IDE 快捷生成 :大多数 IDE(如 Eclipse、IntelliJ IDEA)可以自动生成 toString() 代码,方便使用。

11.3 打印数组

打印数组对象 时,如果使用 System.out.println(arr); 也会得到类似 [Ljava.lang.String;@1540e19d 这样的结果(同样是 Object 的默认 toString())。如果想查看数组元素,可以使用以下方式:

  1. Arrays.toString(数组):适用于一维数组。

    java 复制代码
    String[] arr = {"Hello", "World"};
    System.out.println(Arrays.toString(arr)); 
    // [Hello, World]
  2. Arrays.deepToString(数组):适用于多维数组。

    java 复制代码
    String[][] arr2D = {{"A", "B"}, {"C", "D"}};
    System.out.println(Arrays.deepToString(arr2D)); 
    // [[A, B], [C, D]]

11.4 小结

  1. 默认打印对象 :调用 Object.toString(),返回"类名@哈希值",可读性差。
  2. 重写 toString() :通过在自定义类中重写 toString(),让打印对象时输出更有意义的属性信息。
  3. 打印数组 :使用 Arrays.toString()Arrays.deepToString() 更好地展示数组内容。

在实际开发中,重写 toString() 不仅方便调试日志记录 ,也能让我们更直观地了解对象的核心数据。合理使用 toString() 让输出信息更友好,对日常开发帮助很大。


十二、总结与展望

本篇内容主要围绕以下三个方面展开:

  1. static关键字

    • 静态变量和静态方法在类级别上提供共享资源和操作,无需创建对象即可使用。
    • 静态代码块为类提供一次性初始化的机会。
  2. 代码块

    • 包括静态代码块、实例代码块等,用于在不同阶段进行初始化操作。
    • 掌握代码块的执行顺序有助于理解对象的生命周期。
  3. 内部类

    • 提供了更灵活的访问方式和封装机制。
    • 主要分为成员内部类、静态内部类、匿名内部类和局部内部类。

未来的学习方向

  • Java 继承与多态:继续探索面向对象的另外两个特性,理解类与类之间的继承关系以及动态绑定机制。
  • 接口与抽象类:深入理解接口与抽象类的应用场景,以及如何运用多态思想进行程序扩展。
  • 常用设计模式:结合内部类与静态特性,在单例、工厂、观察者等常见模式中,静态和内部类都有独特的用武之地。

如果你对本篇内容有任何疑问或想进一步探讨,欢迎在评论区留言。我们下篇文章见,继续一起探索 Java 的广阔天地!


以上就是关于【Java篇】静动交融,内外有别:从静态方法到内部类的深度解析内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

相关推荐
冷琴1996几秒前
基于python+django的图书借阅网站-图书借阅管理系统源码+运行步骤
开发语言·python·django
努力学计算机的小白一枚2 分钟前
//TODO 动态代理的本质?
java
凌冰_16 分钟前
Java 集合 List、Set、Map 区别与应用
java·开发语言
提高记忆力17 分钟前
spring和maven
java·sql·spring
codeloverr18 分钟前
leetcode 的T5 最长回文字符串
java·算法·leetcode
救救孩子把21 分钟前
MyBatis-Flex、MyBatis-Plus 与 Fluent-Mybatis 的比较分析
java·mybatis·mybatis-plus·mybatis-flex·fluent-mybatis
这个懒人26 分钟前
Redis 核心源码解析:从设计哲学到企业级应用实践
开发语言
极客先躯29 分钟前
高级java每日一道面试题-2025年3月09日-微服务篇[Eureka篇]-说一说Eureka自我保护模式
java·微服务·eureka·自我保护机制
故事与他64530 分钟前
Apache中间件漏洞攻略
java·服务器·安全·网络安全·中间件·log4j·apache
茶本无香34 分钟前
Java Collection API增强功能系列之二 List.of、Set.of、Map.of
java·开发语言·list