十二、Java 入门进阶:代码块、Lambda、常用 API、GUI 编程
在掌握了类、接口、继承、多态这些基础后,接下来要接触的代码块、内部类、Lambda 表达式、常用 API、GUI 编程。
目录
写法 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 个场景:
- 作为方法参数传递:比如线程、事件监听器(后面 GUI 编程会大量用到),无需单独定义实现类;
- 快速实现简单接口 / 继承简单类:接口 / 父类只有少量抽象方法,无需创建专门的实现类 / 子类;
- 局部临时使用:仅在当前方法中需要一个类的实例,用完后不需要复用,隐藏类定义。
二、函数式编程:Lambda 表达式
Lambda 表达式是 Java8 的重磅特性,核心作用是简化 “函数式接口” 的实现,替代繁琐的匿名内部类,让代码更简洁、更优雅,是 “函数式编程” 的基础,入门阶段重点掌握认识 Lambda、使用示例、省略规则,以及进阶的方法引用。
(一)函数式接口
Lambda 的使用前提是函数式接口,这是核心!
- 定义:只有一个抽象方法的接口(可以有默认方法、静态方法、私有方法,JDK8 后接口允许);
- 标识:可用
@FunctionalInterface注解标注; - 示例:
Runnable(run())、Comparator(compare())、自定义函数式接口;
示例代码:
// 自定义函数式接口(推荐加@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 条,结合示例理解,记熟后直接用!
- 参数类型可省略:编译器能根据函数式接口的抽象方法推断参数类型;
- 单参数时,() 可省略:只有一个参数时,括号可以不写;
- 方法体只有一行时,{} 可省略:方法体仅一条语句,花括号直接省;
- 方法体只有一行 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返回
}
}
代码说明
- 用
StringBuilder拼接字符串:因为 String 不可变,循环拼接用 String 会创建大量临时对象,StringBuilder更高效; - 字符池
CODE_POOL:可自由扩展,比如加小写字母abcdefghijklmnopqrstuvwxyz,实现大小写混合验证码; - 扩展:把 length 改为 6,即可生成 6 位验证码;删除字母,即可生成纯数字验证码。
(三)ArrayList 集合
- 我们之前学的数组是固定长度的,创建后不能修改长度,实际开发中需要动态添加 / 删除元素,此时就用ArrayList—— 它是 “动态数组”,长度可自动扩容,是 Java 中最常用的集合。
核心特点
- 动态扩容:添加元素时,长度不够会自动扩容(默认初始容量 10,扩容为 1.5 倍);
- 有序可重复:元素的存储顺序和添加顺序一致,允许存储重复元素;
- 支持索引访问:和数组一样,通过索引(从 0 开始)获取元素,查询效率高;
- 存储的是对象:不能存储基本数据类型(比如 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);
}
}
}
核心注意点
- 泛型
<E>:创建 ArrayList 时,用泛型限定元素类型(比如<String>、<Integer>),避免存入错误类型的元素,提高代码安全性; - 索引越界:操作索引时(get、set、remove、add),索引不能超出
0 ~ size()-1,否则会报IndexOutOfBoundsException; - 和数组的区别:数组固定长度,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 的交互本质是事件处理:用户对组件做操作,程序感知到并执行相应的代码。事件处理有三个核心要素:
- 事件源:发生事件的组件(比如按钮、文本框、窗口),即 “谁触发的事件”;
- 事件:用户的操作(比如按钮点击事件
ActionEvent、鼠标事件MouseEvent、键盘事件KeyEvent),即 “触发了什么事”; - 事件监听器:实现了监听器接口的对象(比如
ActionListener),里面包含事件处理方法,即 “怎么处理这个事件”。
(三)事件处理的三种写法
写法 1:自定义类实现 ActionListener 接口
- 核心思路:单独定义一个类,实现
ActionListener接口,重写事件处理方法,为按钮注册该类的实例作为监听器; - 优点:监听器逻辑独立,可复用(多个按钮可共用一个监听器);
- 缺点:需要单独定义类,代码稍繁琐,适合复杂的事件处理逻辑。
示例代码:
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
- 核心思路:无需单独定义类,直接用匿名内部类为按钮注册监听器,重写事件处理方法;
- 优点:代码简洁,无需额外定义类,监听器逻辑和组件绑定,适合临时使用、仅一个组件需要的事件处理;
- 缺点:不可复用,多个组件需要重复写代码;
- 使用场景:开发中最常用的写法,尤其是简单的 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 接口(简洁,适合简单界面)
- 核心思路:让主类直接实现
ActionListener接口,重写事件处理方法,为按钮注册this(当前类实例)作为监听器; - 优点:无需额外定义类,代码简洁,适合只有一个或少数组件的简单 GUI 界面;
- 缺点:事件处理方法和主类耦合,适合简单逻辑;
示例代码:
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,仅少数组件 |
五、核心知识点总结
- 代码块 + 内部类:代码块按执行时机分静态、构造、局部,内部类重点掌握匿名内部类(临时使用、简化代码);
- Lambda 表达式:前提是函数式接口,核心语法
(参数)->{方法体},记熟 4 条省略规则,方法引用是 Lambda 的进一步简化(4 种形式); - 常用 API:String 类记熟 10 个常用方法,equals () 比较内容;ArrayList 是动态数组,掌握增删改查;验证码案例结合两者,可直接复用;
- GUI 编程(Swing):快速入门会创建窗口和组件,事件处理三要素(事件源、事件、监听器),三种写法重点掌握匿名内部类(开发最常用)。
更多推荐



所有评论(0)