在掌握了类、接口、继承、多态这些基础后,接下来要接触的代码块、内部类、Lambda 表达式、常用 API、GUI 编程


目录

一、类的核心成分:代码块 + 内部类

(一)代码块:

1. 局部代码块

2. 实例代码块

3. 静态代码块

代码块执行顺序

(二)内部类:

1. 成员内部类:

2. 静态内部类:

3. 局部内部类:

4. 匿名内部类:

(1)使用形式

(2)示例

(3)使用场景

二、函数式编程:Lambda 表达式

(一)函数式接口

(二)Lambda 表达式

示例 1:实现自定义函数式接口

示例 2:实现 Runnable 接口

(三)省略规则

综合示例:

(四)方法引用(::)

1. 静态方法引用:

2. 实例方法引用:

3. 特定类型方法引用:

4. 构造器方法引用:

三、常用 API

(一)String 类:

常用方法

核心注意点

(二)String 实现验证码生成

代码说明

(三)ArrayList 集合

核心特点

示例代码:

核心注意点

四、GUI 界面编程:

(一)创建第一个窗口程序

示例代码:

(二)事件处理

(三)事件处理的三种写法

写法 1:自定义类实现 ActionListener 接口

写法 2:匿名内部类实现 ActionListener

写法 3:当前类本身实现 ActionListener 接口(简洁,适合简单界面)

三种写法总结

五、核心知识点总结


一、类的核心成分:代码块 + 内部类

我们之前学过类的成员(属性、方法、构造器),但实际开发中,类里还有代码块内部类这两个重要成分,它们能解决代码复用、简化编程的问题,尤其是内部类,在框架和源码中随处可见。


(一)代码块:

  • 代码块是用{}包裹的一段代码,根据定义位置修饰符,分为 3 种,核心区别是执行时机作用
1. 局部代码块
  • 定义位置:方法 / 语句块内部
  • 执行时机:调用方法时,执行到该代码块位置才运行
  • 核心作用:限制变量的作用域(变量仅在代码块内可用),避免变量名冲突

示例代码:

public class CodeBlockDemo {
    public static void main(String[] args) {
        // 局部代码块
        {
            int a = 10;
            System.out.println("局部代码块:a = " + a); // 输出10
        }
        // System.out.println(a); // 报错:找不到变量a,作用域仅限上面的代码块
    }
}
2. 实例代码块
  • 定义位置:类的成员位置(和属性、方法同级)
  • 执行时机每次创建对象时执行,且在构造方法之前运行
  • 核心作用:提取所有构造方法的共性代码,实现代码复用

示例代码:

public class CodeBlockDemo {
    // 构造代码块(实例代码块)
    {
        System.out.println("构造代码块:创建对象时先执行我");
    }

    // 无参构造
    public CodeBlockDemo() {
        System.out.println("无参构造方法执行");
    }

    // 有参构造
    public CodeBlockDemo(String name) {
        System.out.println("有参构造方法执行:" + name);
    }

    public static void main(String[] args) {
        new CodeBlockDemo();
        // 输出:构造代码块→无参构造方法
        System.out.println("-----");
        new CodeBlockDemo("Java");
        // 输出:构造代码块→有参构造方法
    }
}
3. 静态代码块
  • 定义位置:类的成员位置,用static修饰
  • 执行时机类加载时执行,仅执行一次(无论创建多少对象,都只跑一次)
  • 核心作用:初始化静态资源

示例代码:

public class CodeBlockDemo {
    // 静态变量
    public static String version;

    // 静态代码块
    static {
        version = "Java 1.8";
        System.out.println("静态代码块:类加载时执行,仅一次");
        System.out.println("初始化静态变量:version = " + version);
    }

    public static void main(String[] args) {
        CodeBlockDemo c1 = new CodeBlockDemo();
        CodeBlockDemo c2 = new CodeBlockDemo();
        // 仅输出一次静态代码块内容,c1、c2创建时都不重复执行
    }
}
代码块执行顺序
  • 类加载时:静态代码块 → 创建对象时:构造代码块 → 构造方法 → 调用方法时:局部代码块

(二)内部类:

内部类是在一个类的内部定义另一个类,比如在Outer类中定义Inner类,Inner就是内部类。核心作用:隐藏细节、提高代码内聚性、实现多重继承的效果,根据定义位置分为 4 种,入门重点掌握成员内部类、静态内部类、匿名内部类

1. 成员内部类:
  • 定义位置:外部类的成员位置(和属性、方法同级)
  • 核心特点:依赖外部类对象,不能定义静态成员(静态变量 / 方法),可直接访问外部类的所有成员(包括私有)
  • 使用步骤:先创建外部类对象 → 通过外部类对象创建内部类对象

示例代码:

// 外部类
public class Outer {
    private String outerName = "外部类私有属性";
    public int age = 20;

    // 成员内部类
    public class Inner {
        // 不能定义静态成员(入门核心规则)
        // public static String msg = "test"; // 报错
        private String innerName = "内部类属性";

        // 内部类方法:直接访问外部类所有成员
        public void show() {
            System.out.println(outerName); // 访问外部类私有属性
            System.out.println(age); // 访问外部类公有属性
            System.out.println(innerName); // 访问自身属性
        }
    }

    // 测试
    public static void main(String[] args) {
        // 步骤1:创建外部类对象
        Outer outer = new Outer();
        // 步骤2:创建成员内部类对象 → 外部类对象.new 内部类()
        Outer.Inner inner = outer.new Inner();
        // 步骤3:调用内部类方法
        inner.show();
    }
}
2. 静态内部类:
  • 定义位置:外部类的成员位置,用static修饰
  • 核心特点不依赖外部类对象,可独立创建,能定义静态成员,仅能访问外部类的静态成员(私有静态也能访问)
  • 使用步骤:直接通过「外部类名.内部类名」创建对象,无需外部类对象

示例代码:

// 外部类
public class Outer {
    private static String staticName = "外部类私有静态属性";
    public int age = 20; // 非静态属性

    // 静态内部类
    public static class Inner {
        public static String innerStatic = "内部类静态属性";
        private String innerName = "内部类非静态属性";

        public void show() {
            System.out.println(staticName); // 访问外部类静态属性(允许)
            // System.out.println(age); // 报错:不能访问外部类非静态属性
            System.out.println(innerStatic);
            System.out.println(innerName);
        }
    }

    // 测试
    public static void main(String[] args) {
        // 直接创建静态内部类对象 → 外部类名.内部类名 变量名 = new 外部类名.内部类名()
        Outer.Inner inner = new Outer.Inner();
        inner.show();
        // 直接访问内部类静态成员
        System.out.println(Outer.Inner.innerStatic);
    }
}
3. 局部内部类:
  • 定义位置:外部类的方法 / 代码块内部
  • 核心特点:作用域仅限当前方法,不能用访问修饰符(public/private 等),可访问外部类成员和方法内的局部变量(局部变量需是 final,JDK8 后自动隐式 final
  • 使用场景:仅在当前方法中使用的类,用完即弃,隐藏类的定义

示例代码:

// 外部类
public class Outer {
    private String outerName = "外部类属性";

    public void outerMethod() {
        int num = 10; // 方法内局部变量,JDK8后自动隐式final
        // 局部内部类(方法内,无访问修饰符)
        class Inner {
            public void show() {
                System.out.println(outerName); // 访问外部类属性
                System.out.println(num); // 访问方法内局部变量
            }
        }

        // 仅在当前方法中创建内部类对象并使用
        Inner inner = new Inner();
        inner.show();
    }

    // 测试
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.outerMethod(); // 调用外部方法,间接使用局部内部类
    }
}
4. 匿名内部类:
  • 匿名内部类是局部内部类的简化版没有类名,直接通过「继承类 / 实现接口」的方式创建对象,用完即弃,是开发中最常用的内部类!
(1)使用形式

匿名内部类只能基于两个前提创建:继承一个类实现一个接口

语法格式:

// 形式1:实现接口
接口名 变量名 = new 接口名() {
    // 重写接口中所有抽象方法
    @Override
    public 方法返回值 方法名(参数) {
        // 方法实现
    }
};

// 形式2:继承类
父类名 变量名 = new 父类名(构造参数) {
    // 重写父类中需要的方法
    @Override
    public 方法返回值 方法名(参数) {
        // 方法实现
    }
};
(2)示例

以实现Runnable接口(线程接口,只有一个抽象方法run())为例,对比普通实现类匿名内部类。

示例代码:

// 需求:实现Runnable接口,重写run方法
public class AnonymousInnerDemo {
    public static void main(String[] args) {
        // 方式1:普通实现类(需要单独定义类,繁琐)
        RunnableImpl r1 = new RunnableImpl();
        new Thread(r1).start();

        // 方式2:匿名内部类(无需单独定义类,直接实现,简化)
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类:线程运行中...");
            }
        };
        new Thread(r2).start();

        // 极致简化:直接把匿名内部类作为方法参数
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类:作为方法参数,更简洁...");
            }
        }).start();
    }
}

// 普通实现类(方式1需要单独定义)
class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("普通实现类:线程运行中...");
    }
}
(3)使用场景

匿名内部类的核心是 “临时使用、仅用一次、简化代码”,开发中主要用在 3 个场景:

  1. 作为方法参数传递:比如线程、事件监听器(后面 GUI 编程会大量用到),无需单独定义实现类;
  2. 快速实现简单接口 / 继承简单类:接口 / 父类只有少量抽象方法,无需创建专门的实现类 / 子类;
  3. 局部临时使用:仅在当前方法中需要一个类的实例,用完后不需要复用,隐藏类定义。

二、函数式编程:Lambda 表达式

Lambda 表达式是 Java8 的重磅特性,核心作用是简化 “函数式接口” 的实现,替代繁琐的匿名内部类,让代码更简洁、更优雅,是 “函数式编程” 的基础,入门阶段重点掌握认识 Lambda、使用示例、省略规则,以及进阶的方法引用


(一)函数式接口

Lambda 的使用前提是函数式接口,这是核心!

  • 定义:只有一个抽象方法的接口(可以有默认方法、静态方法、私有方法,JDK8 后接口允许);
  • 标识:可用@FunctionalInterface注解标注;
  • 示例:Runnablerun()Comparatorcompare()、自定义函数式接口;

示例代码:

// 自定义函数式接口(推荐加@FunctionalInterface注解)
@FunctionalInterface
public interface Calculator {
    // 只有一个抽象方法
    int calculate(int a, int b);
    // 可加默认方法/静态方法,不影响
    default void show() {
        System.out.println("计算结果:");
    }
}

(二)Lambda 表达式

Lambda 的本质是函数式接口的 “实例”,用简洁的语法替代匿名内部类。

核心语法:

(参数列表) -> { 方法体 }
  • ():包裹方法的参数列表,和函数式接口的抽象方法参数一致;
  • ->Lambda 操作符,分隔参数和方法体,读作 “指向”
  • {}:包裹方法体,和普通方法的方法体一致。
示例 1:实现自定义函数式接口
public class LambdaDemo {
    public static void main(String[] args) {
        // 方式1:匿名内部类实现函数式接口
        Calculator c1 = new Calculator() {
            @Override
            public int calculate(int a, int b) {
                return a + b; // 实现加法
            }
        };
        System.out.println(c1.calculate(10, 20)); // 30

        // 方式2:Lambda表达式实现(简化,无需写类名、重写注解)
        Calculator c2 = (int a, int b) -> {
            return a + b;
        };
        System.out.println(c2.calculate(10, 20)); // 30
    }
}

// 函数式接口
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}
示例 2:实现 Runnable 接口
public class LambdaDemo {
    public static void main(String[] args) {
        // 匿名内部类方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类:线程运行");
            }
        }).start();

        // Lambda方式(极致简化)
        new Thread(() -> {
            System.out.println("Lambda:线程运行");
        }).start();
    }
}

(三)省略规则

Lambda 的语法可以进一步简化,在编译器能 “推断” 出信息的前提下,可以省略部分代码,核心省略规则有 4 条,结合示例理解,记熟后直接用!

  1. 参数类型可省略:编译器能根据函数式接口的抽象方法推断参数类型;
  2. 单参数时,() 可省略:只有一个参数时,括号可以不写;
  3. 方法体只有一行时,{} 可省略:方法体仅一条语句,花括号直接省;
  4. 方法体只有一行 return 时,{}+return 一起省略:return 和花括号必须同时省,不能单独省。
综合示例:
public class LambdaOmitDemo {
    public static void main(String[] args) {
        // 原始Lambda:无省略
        Calculator c1 = (int a, int b) -> { return a + b; };

        // 省略1:参数类型
        Calculator c2 = (a, b) -> { return a + b; };

        // 省略2+3+4:单参数(假设接口方法是int cal(int a))+ 单行return
        // Calculator c3 = a -> a * 2; // 等价于 (int a) -> { return a*2; }

        // 本示例双参数,省略1+4:参数类型+单行return
        Calculator c4 = (a, b) -> a + b; // 最简化写法,开发中直接用这个!

        System.out.println(c4.calculate(10, 30)); // 40
    }
}

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

(四)方法引用(::)

方法引用是Lambda 的进一步简化,当 Lambda 的方法体只有一行代码,且是调用某个已存在的方法时,就可以用方法引用替代 Lambda,核心语法:类名/对象名 :: 方法名

  • 方法引用的核心:复用已有的方法实现,无需重复写方法体。
1. 静态方法引用:
  • 适用场景:Lambda 方法体是调用某个类的静态方法,且 Lambda 的参数全部作为静态方法的参数传递;
  • 格式类名::静态方法名

示例代码:

public class MethodRefDemo {
    public static void main(String[] args) {
        // 需求:实现Calculator接口,计算两个数的和,复用MathUtil的add静态方法
        Calculator c1 = (a, b) -> MathUtil.add(a, b); // Lambda
        Calculator c2 = MathUtil::add; // 静态方法引用(简化,等价于上面的Lambda)
        System.out.println(c2.calculate(10, 20)); // 30
    }
}

// 函数式接口
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

// 工具类:包含静态方法add
class MathUtil {
    public static int add(int a, int b) {
        return a + b;
    }
}
2. 实例方法引用:
  • 适用场景:Lambda 方法体是调用某个对象的实例方法,且 Lambda 的参数全部作为实例方法的参数传递;
  • 格式对象名::实例方法名

示例代码:

public class MethodRefDemo {
    public static void main(String[] args) {
        // 创建实例对象
        MathObj mathObj = new MathObj();
        // Lambda:调用实例方法
        Calculator c1 = (a, b) -> mathObj.sub(a, b);
        // 实例方法引用:简化
        Calculator c2 = mathObj::sub;
        System.out.println(c2.calculate(50, 20)); // 30
    }
}

// 函数式接口
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

// 包含实例方法的类
class MathObj {
    public int sub(int a, int b) {
        return a - b;
    }
}
3. 特定类型方法引用:
  • 适用场景:Lambda 的第一个参数是实例方法的调用者,后续参数是实例方法的参数,此时可用类名引用实例方法;
  • 格式类名::实例方法名

示例代码:

public class MethodRefDemo {
    public static void main(String[] args) {
        // 需求:实现Comparator接口(比较两个字符串长度),复用String的length()和compareTo()
        // Lambda:第一个参数s1是调用者,第二个参数s2是参数
        Comparator<String> c1 = (s1, s2) -> s1.compareTo(s2);
        // 特定类型方法引用:String是类名,compareTo是实例方法
        Comparator<String> c2 = String::compareTo;
        System.out.println(c2.compare("Java", "Python")); // 比较字符串

        // 再示例:比较字符串长度
        Comparator<String> c3 = (s1, s2) -> s1.length() - s2.length();
        // 也可自定义方法实现,再用特定类型引用
    }
}
4. 构造器方法引用:
  • 适用场景:Lambda 方法体是创建某个类的对象,且 Lambda 的参数全部作为构造器的参数传递;
  • 格式类名::new

示例代码:

public class MethodRefDemo {
    public static void main(String[] args) {
        // 需求:实现UserBuilder接口,创建User对象,复用User的构造器
        // Lambda:参数name、age作为构造器参数,创建User对象
        UserBuilder ub1 = (name, age) -> new User(name, age);
        // 构造器方法引用:User是类名,new代表构造器
        UserBuilder ub2 = User::new;
        User user = ub2.build("张三", 18);
        System.out.println(user.getName() + "," + user.getAge()); // 张三,18
    }
}

// 函数式接口:创建User对象
@FunctionalInterface
interface UserBuilder {
    User build(String name, int age);
}

// User类
class User {
    private String name;
    private int age;

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

    // get/set方法
    public String getName() { return name; }
    public int getAge() { return age; }
}

三、常用 API

API(Application Programming Interface)是 Java 提供的现成工具类 / 方法,开发中不用重复造轮子,直接调用即可!入门阶段重点掌握String 类、ArrayList 集合(最常用),以及结合两者实现验证码生成


(一)String 类:

  • String 代表字符串,Java 中所有字符串常量(比如 "abc")都是 String 的实例,核心特点:字符串是不可变的(一旦创建,内容不能修改,修改会创建新对象)。
常用方法
public class StringApiDemo {
    public static void main(String[] args) {
        String s = "Java Programming";
        String s1 = "java";
        String s2 = "  Hello Java  ";

        // 1. 获取长度:int length()
        System.out.println(s.length()); // 16

        // 2. 获取指定索引的字符:char charAt(int index)(索引从0开始)
        System.out.println(s.charAt(0)); // J

        // 3. 判断是否包含指定子串:boolean contains(CharSequence s)
        System.out.println(s.contains("Java")); // true

        // 4. 截取字符串:String substring(int beginIndex, int endIndex)(左闭右开)
        System.out.println(s.substring(0, 4)); // Java(从0开始,到4结束,不包含4)

        // 5. 替换字符串:String replace(CharSequence old, CharSequence new)
        System.out.println(s.replace("Java", "Python")); // Python Programming

        // 6. 分割字符串:String[] split(String regex)(按指定规则分割)
        String[] arr = s.split(" "); // 按空格分割
        System.out.println(Arrays.toString(arr)); // [Java, Programming]

        // 7. 转换为小写/大写:String toLowerCase() / toUpperCase()
        System.out.println(s.toLowerCase()); // java programming
        System.out.println(s1.toUpperCase()); // JAVA

        // 8. 去除首尾空格:String trim()
        System.out.println(s2.trim()); // Hello Java

        // 9. 判断字符串是否相等:boolean equals(Object obj)(必用,不能用==)
        System.out.println("Java".equals(s.substring(0,4))); // true

        // 10. 判断是否以指定字符串开头/结尾:startsWith() / endsWith()
        System.out.println(s.startsWith("Java")); // true
        System.out.println(s.endsWith("ing")); // true
    }
}
核心注意点
  • 比较字符串是否相等,必须用 equals () 方法,不能用====比较的是对象的地址,equals () 比较的是字符串内容;
  • 字符串不可变:所有修改字符串的方法(replace、substring 等),都会返回新的 String 对象,原对象内容不变。

(二)String 实现验证码生成

结合 String、随机数Random,实现4 位数字 + 字母混合验证码生成。

示例代码:

import java.util.Random;

/**
 * 验证码生成:4位数字+大写字母混合
 */
public class VerifyCodeDemo {
    // 验证码字符池:包含所有可能出现的字符
    private static final String CODE_POOL = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static void main(String[] args) {
        String verifyCode = generateVerifyCode(4); // 生成4位验证码
        System.out.println("生成的验证码:" + verifyCode);
    }

    /**
     * 生成指定长度的验证码
     * @param length 验证码长度
     * @return 生成的验证码字符串
     */
    public static String generateVerifyCode(int length) {
        if (length <= 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder(); // 用于拼接字符串(比String高效)
        Random random = new Random(); // 随机数对象
        int poolLength = CODE_POOL.length(); // 字符池长度

        // 循环length次,每次生成一个随机字符
        for (int i = 0; i < length; i++) {
            // 生成0到poolLength-1的随机索引
            int randomIndex = random.nextInt(poolLength);
            // 根据索引获取字符池中的字符
            char c = CODE_POOL.charAt(randomIndex);
            // 拼接字符
            sb.append(c);
        }
        return sb.toString(); // 转换为String返回
    }
}
代码说明
  1. StringBuilder拼接字符串:因为 String 不可变,循环拼接用 String 会创建大量临时对象,StringBuilder更高效;
  2. 字符池CODE_POOL:可自由扩展,比如加小写字母abcdefghijklmnopqrstuvwxyz,实现大小写混合验证码;
  3. 扩展:把 length 改为 6,即可生成 6 位验证码;删除字母,即可生成纯数字验证码。

(三)ArrayList 集合

  • 我们之前学的数组是固定长度的,创建后不能修改长度,实际开发中需要动态添加 / 删除元素,此时就用ArrayList—— 它是 “动态数组”,长度可自动扩容,是 Java 中最常用的集合。
核心特点
  1. 动态扩容:添加元素时,长度不够会自动扩容(默认初始容量 10,扩容为 1.5 倍);
  2. 有序可重复:元素的存储顺序和添加顺序一致,允许存储重复元素;
  3. 支持索引访问:和数组一样,通过索引(从 0 开始)获取元素,查询效率高;
  4. 存储的是对象:不能存储基本数据类型(比如 int、char),需用包装类(Integer、Character),JDK5 后自动装箱 / 拆箱,无需手动转换。
示例代码:
import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 1. 创建ArrayList对象:存储字符串类型(泛型<String>限定元素类型,避免类型错误)
        ArrayList<String> list = new ArrayList<>();

        // 2. 添加元素:boolean add(E e)(尾部添加)、void add(int index, E e)(指定索引添加)
        list.add("Java");
        list.add("Python");
        list.add("C++");
        list.add(1, "JavaScript"); // 在索引1的位置添加
        System.out.println(list); // [Java, JavaScript, Python, C++]

        // 3. 获取元素:E get(int index)
        String s = list.get(2);
        System.out.println("索引2的元素:" + s); // Python

        // 4. 修改元素:E set(int index, E e)(返回原元素)
        String old = list.set(3, "Go");
        System.out.println("修改后的集合:" + list); // [Java, JavaScript, Python, Go]
        System.out.println("被替换的原元素:" + old); // C++

        // 5. 删除元素:E remove(int index)(按索引删,返回被删元素)、boolean remove(Object o)(按元素删)
        String del1 = list.remove(1);
        list.remove("Go");
        System.out.println("删除后的集合:" + list); // [Java, Python]
        System.out.println("按索引删除的元素:" + del1); // JavaScript

        // 6. 获取集合长度:int size()
        System.out.println("集合长度:" + list.size()); // 2

        // 7. 判断是否包含指定元素:boolean contains(Object o)
        System.out.println(list.contains("Java")); // true

        // 8. 判断集合是否为空:boolean isEmpty()
        System.out.println(list.isEmpty()); // false

        // 9. 遍历集合:三种方式(入门重点掌握前两种)
        // 方式1:普通for循环(带索引,适合需要操作索引的场景)
        System.out.println("普通for循环遍历:");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        // 方式2:增强for循环(foreach,无需索引,适合单纯遍历)
        System.out.println("增强for循环遍历:");
        for (String str : list) {
            System.out.println(str);
        }
    }
}
核心注意点
  1. 泛型<E>创建 ArrayList 时,用泛型限定元素类型(比如<String><Integer>),避免存入错误类型的元素,提高代码安全性;
  2. 索引越界:操作索引时(get、set、remove、add),索引不能超出0 ~ size()-1,否则会报IndexOutOfBoundsException
  3. 和数组的区别:数组固定长度,ArrayList 动态长度;数组可存基本类型 / 对象,ArrayList 只能存对象。

四、GUI 界面编程:

GUI(Graphical User Interface)是图形用户界面,区别于控制台的纯文本交互,能创建窗口、按钮、文本框等可视化组件,Java 中常用的 GUI 框架是Swing

  • 本文讲 Swing 的快速入门(创建窗口、添加组件)和事件处理(按钮点击、文本框输入等交互),以及事件处理的三种核心写法,看完能做出第一个 Java 可视化小程序!

(一)创建第一个窗口程序

示例代码:
import javax.swing.*;
import java.awt.*;

/**
 * Swing快速入门:创建第一个GUI窗口
 */
public class FirstGuiDemo {
    public static void main(String[] args) {
        // 1. 创建JFrame窗口对象(顶级容器,所有组件都放在窗口中)
        JFrame frame = new JFrame("我的第一个Java GUI程序");

        // 2. 设置窗口属性(大小、位置、关闭方式、布局)
        frame.setSize(400, 300); // 窗口大小:宽400像素,高300像素
        frame.setLocationRelativeTo(null); // 窗口居中显示(无参表示相对于屏幕)
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口时退出程序
        frame.setLayout(new FlowLayout()); // 布局管理器:流式布局(组件按添加顺序排列)

        // 3. 创建组件(JLabel标签、JButton按钮)
        JLabel label = new JLabel("欢迎使用Java GUI编程!"); // 文本标签
        JButton button = new JButton("点击我"); // 按钮

        // 4. 将组件添加到窗口中
        frame.add(label);
        frame.add(button);

        // 5. 设置窗口可见(必须最后一步,否则组件不显示)
        frame.setVisible(true);
    }
}

(二)事件处理

  • GUI 的交互本质是事件处理:用户对组件做操作,程序感知到并执行相应的代码。事件处理有三个核心要素
  1. 事件源:发生事件的组件(比如按钮、文本框、窗口),即 “谁触发的事件”
  2. 事件:用户的操作(比如按钮点击事件ActionEvent、鼠标事件MouseEvent、键盘事件KeyEvent),即 “触发了什么事”
  3. 事件监听器:实现了监听器接口的对象(比如ActionListener),里面包含事件处理方法,即 “怎么处理这个事件”

(三)事件处理的三种写法

写法 1:自定义类实现 ActionListener 接口
  1. 核心思路:单独定义一个类,实现ActionListener接口,重写事件处理方法,为按钮注册该类的实例作为监听器;
  2. 优点:监听器逻辑独立,可复用(多个按钮可共用一个监听器);
  3. 缺点:需要单独定义类,代码稍繁琐,适合复杂的事件处理逻辑。

示例代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 事件处理写法1:自定义类实现ActionListener
 */
public class GuiEventDemo1 {
    public static void main(String[] args) {
        // 1. 创建窗口和组件
        JFrame frame = new JFrame("事件处理-写法1");
        frame.setSize(400, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());

        JButton button = new JButton("点击我(写法1)");
        frame.add(button);

        // 2. 为按钮注册监听器:传入自定义监听器实例
        button.addActionListener(new MyActionListener());

        frame.setVisible(true);
    }
}

// 自定义监听器类:实现ActionListener接口
class MyActionListener implements ActionListener {
    // 重写事件处理方法:按钮点击时执行此方法
    @Override
    public void actionPerformed(ActionEvent e) {
        // 弹出提示框(JOptionPane是Swing的弹窗工具类)
        JOptionPane.showMessageDialog(null, "你点击了按钮!(写法1)");
        // 可添加其他业务逻辑:比如修改标签文本、打开新窗口等
    }
}
写法 2:匿名内部类实现 ActionListener
  1. 核心思路:无需单独定义类,直接用匿名内部类为按钮注册监听器,重写事件处理方法;
  2. 优点:代码简洁,无需额外定义类,监听器逻辑和组件绑定,适合临时使用、仅一个组件需要的事件处理;
  3. 缺点:不可复用,多个组件需要重复写代码;
  4. 使用场景:开发中最常用的写法,尤其是简单的 GUI 程序!

示例代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 事件处理写法2:匿名内部类实现ActionListener(最常用)
 */
public class GuiEventDemo2 {
    public static void main(String[] args) {
        // 1. 创建窗口和组件
        JFrame frame = new JFrame("事件处理-写法2");
        frame.setSize(400, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());

        JButton button = new JButton("点击我(写法2)");
        frame.add(button);

        // 2. 为按钮注册监听器:匿名内部类(核心)
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 事件处理逻辑:点击按钮弹出提示框
                JOptionPane.showMessageDialog(null, "你点击了按钮!(写法2-匿名内部类)");
            }
        });

        frame.setVisible(true);
    }
}
写法 3:当前类本身实现 ActionListener 接口(简洁,适合简单界面)
  1. 核心思路:让主类直接实现ActionListener接口,重写事件处理方法,为按钮注册this(当前类实例)作为监听器;
  2. 优点:无需额外定义类,代码简洁,适合只有一个或少数组件的简单 GUI 界面;
  3. 缺点:事件处理方法和主类耦合,适合简单逻辑;

示例代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 事件处理写法3:当前类实现ActionListener接口
 */
public class GuiEventDemo3 implements ActionListener {
    public static void main(String[] args) {
        // 创建当前类实例,调用初始化界面方法
        new GuiEventDemo3().initUI();
    }

    // 初始化界面方法
    public void initUI() {
        JFrame frame = new JFrame("事件处理-写法3");
        frame.setSize(400, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());

        JButton button = new JButton("点击我(写法3)");
        frame.add(button);

        // 注册监听器:this(当前类实例,已实现ActionListener)
        button.addActionListener(this);

        frame.setVisible(true);
    }

    // 重写事件处理方法:当前类实现接口,必须重写
    @Override
    public void actionPerformed(ActionEvent e) {
        JOptionPane.showMessageDialog(null, "你点击了按钮!(写法3-当前类实现接口)");
    }
}

三种写法总结
写法 核心思路 优点 缺点 适用场景
1. 自定义类实现 单独定义监听器类 可复用,逻辑独立 代码繁琐,需额外定义类 复杂 GUI,多个组件共用监听器
2. 匿名内部类 直接在注册时实现接口 最简洁,无需额外类 不可复用 大部分场景,尤其是简单 GUI
3. 当前类实现 主类实现监听器接口 简洁,无需额外类 逻辑耦合,可维护性差 超简单 GUI,仅少数组件

五、核心知识点总结

  1. 代码块 + 内部类:代码块按执行时机分静态、构造、局部,内部类重点掌握匿名内部类(临时使用、简化代码);
  2. Lambda 表达式:前提是函数式接口,核心语法(参数)->{方法体},记熟 4 条省略规则,方法引用是 Lambda 的进一步简化(4 种形式);
  3. 常用 API:String 类记熟 10 个常用方法,equals () 比较内容;ArrayList 是动态数组,掌握增删改查;验证码案例结合两者,可直接复用;
  4. GUI 编程(Swing):快速入门会创建窗口和组件,事件处理三要素(事件源、事件、监听器),三种写法重点掌握匿名内部类(开发最常用)。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐