Java从零到熟练(八):泛型与注解

泛型让代码更安全,注解让代码更简洁,反射让代码更灵活。

目录

  • [1. 泛型基础](#1. 泛型基础)
  • [2. 泛型方法](#2. 泛型方法)
  • [3. 类型边界](#3. 类型边界)
  • [4. 注解基础](#4. 注解基础)
  • [5. 反射机制](#5. 反射机制)
  • [6. 实战案例](#6. 实战案例)
  • [7. 总结](#7. 总结)
  • 参考资源

1. 泛型基础

1.1 为什么需要泛型?

在泛型出现之前,集合使用Object存储元素:

java 复制代码
// 旧写法(没有泛型)
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // 需要强制转换,可能出错

使用泛型后:

java 复制代码
// 新写法(使用泛型)
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);  // 自动类型安全,无需强制转换

1.2 泛型类

java 复制代码
// 通用容器类
public class Box<T> {
    private T content;
    
    public Box(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
    
    public void setContent(T content) {
        this.content = content;
    }
    
    @Override
    public String toString() {
        return "Box{" + content + "}";
    }
}

// 使用泛型类
public class BoxDemo {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>("Hello");
        Box<Integer> intBox = new Box<>(123);
        
        System.out.println(stringBox);  // Box{Hello}
        System.out.println(intBox);     // Box{123}
    }
}

1.3 多类型参数

java 复制代码
// 键值对容器
public class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
    
    @Override
    public String toString() {
        return "Pair{key=" + key + ", value=" + value + "}";
    }
}

// 使用多类型参数
public class PairDemo {
    public static void main(String[] args) {
        Pair<String, Integer> pair = new Pair<>("age", 25);
        System.out.println(pair);  // Pair{key=age, value=25}
    }
}

2. 泛型方法

2.1 定义泛型方法

java 复制代码
public class GenericMethods {
    
    // 泛型方法:在返回类型前声明类型参数
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    
    // 返回泛型类型的泛型方法
    public static <T extends Comparable<T>> T findMax(T[] array) {
        T max = array[0];
        for (T element : array) {
            if (element.compareTo(max) > 0) {
                max = element;
            }
        }
        return max;
    }
    
    public static void main(String[] args) {
        Integer[] numbers = {3, 1, 4, 1, 5, 9, 2, 6};
        String[] fruits = {"apple", "banana", "orange"};
        
        System.out.print("数字:");
        printArray(numbers);
        
        System.out.print("水果:");
        printArray(fruits);
        
        System.out.println("最大数字:" + findMax(numbers));
        System.out.println("最大水果:" + findMax(fruits));
    }
}

3. 类型边界

3.1 上界(extends)

限制泛型必须是某个类或接口的子类:

java 复制代码
// 数值型容器,只能存放Number及其子类
public class NumberBox<T extends Number> {
    private T number;
    
    public NumberBox(T number) {
        this.number = number;
    }
    
    public double doubleValue() {
        return number.doubleValue();
    }
}

// 可比较的容器
public class ComparableBox<T extends Comparable<T>> {
    private T value;
    
    public ComparableBox(T value) {
        this.value = value;
    }
    
    public boolean isGreaterThan(T other) {
        return value.compareTo(other) > 0;
    }
}

3.2 通配符

java 复制代码
public class WildcardDemo {
    // 上界通配符:只读
    public static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number num : list) {
            total += num.doubleValue();
        }
        return total;
    }
    
    // 下界通配符:只写
    public static void addNumbers(List<? super Integer> list) {
        for (int i = 1; i <= 5; i++) {
            list.add(i);
        }
    }
    
    // 无界通配符
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        List<Integer> integers = List.of(1, 2, 3);
        List<Double> doubles = List.of(1.1, 2.2, 3.3);
        
        System.out.println("整数和:" + sum(integers));
        System.out.println("浮点数和:" + sum(doubles));
        
        List<Number> numberList = new ArrayList<>();
        addNumbers(numberList);
        System.out.println("添加的数字:" + numberList);
        
        printList(integers);
        printList(doubles);
    }
}

4. 注解基础

4.1 什么是注解?

注解是元数据,提供关于程序元素但不影响程序执行的信息。

java 复制代码
@Override  // 标记方法重写父类方法
public String toString() {
    return "Hello";
}

@SuppressWarnings("unchecked")  // 抑制警告
List<String> list = new ArrayList<>();

@Test  // 标记测试方法
public void testMethod() {
    // 测试代码
}

4.2 内置注解

java 复制代码
public class AnnotationDemo {
    
    @Override
    public String toString() {
        return "重写toString";
    }
    
    @Deprecated
    public void oldMethod() {
        System.out.println("过时的方法");
    }
    
    @SuppressWarnings("unchecked")
    public void uncheckedOperation() {
        List raw = new ArrayList();
        raw.add("Hello");
        List<String> typed = raw;
    }
}

4.3 自定义注解

java 复制代码
import java.lang.annotation.*;

// 定义注解
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时
@Target(ElementType.METHOD)  // 只能用在方法上
public @interface LogExecutionTime {
    String value() default "";  // 注解元素,有默认值
}

// 定义另一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Validate {
    int minLength() default 0;
    int maxLength() default Integer.MAX_VALUE;
    boolean required() default true;
}

5. 反射机制

5.1 什么是反射?

反射是Java在运行时获取类信息并操作类的能力:

java 复制代码
import java.lang.reflect.*;

public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        // 获取Class对象
        Class<?> clazz = Class.forName("java.lang.String");
        
        // 获取类信息
        System.out.println("类名:" + clazz.getName());
        System.out.println("简单名:" + clazz.getSimpleName());
        
        // 获取方法
        System.out.println("\n公共方法:");
        for (Method method : clazz.getMethods()) {
            if (method.getDeclaringClass() == clazz) {
                System.out.println("  " + method.getName());
            }
        }
    }
}

5.2 动态创建对象

java 复制代码
import java.lang.reflect.*;

public class DynamicObjectCreation {
    public static void main(String[] args) throws Exception {
        // 获取Class对象
        Class<?> clazz = Class.forName("java.util.ArrayList");
        
        // 创建实例
        Object list = clazz.getDeclaredConstructor().newInstance();
        
        // 调用方法
        Method addMethod = clazz.getMethod("add", Object.class);
        addMethod.invoke(list, "Hello");
        addMethod.invoke(list, "World");
        
        Method sizeMethod = clazz.getMethod("size");
        int size = (int) sizeMethod.invoke(list);
        System.out.println("列表大小:" + size);
        
        Method toStringMethod = clazz.getMethod("toString");
        System.out.println("列表内容:" + toStringMethod.invoke(list));
    }
}

6. 实战案例

6.1 基于注解的字段验证器

java 复制代码
import java.lang.annotation.*;
import java.lang.reflect.*;

// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface NotBlank {
    String message() default "不能为空";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Email {
    String message() default "邮箱格式不正确";
}

// 验证器
public class Validator {
    public static <T> List<String> validate(T object) throws IllegalAccessException {
        List<String> errors = new ArrayList<>();
        Class<?> clazz = object.getClass();
        
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            
            if (field.isAnnotationPresent(NotBlank.class)) {
                String value = (String) field.get(object);
                if (value == null || value.trim().isEmpty()) {
                    NotBlank annotation = field.getAnnotation(NotBlank.class);
                    errors.add(field.getName() + ": " + annotation.message());
                }
            }
            
            if (field.isAnnotationPresent(Email.class)) {
                String value = (String) field.get(object);
                if (value != null && !value.matches("^.+@.+\\..+$")) {
                    Email annotation = field.getAnnotation(Email.class);
                    errors.add(field.getName() + ": " + annotation.message());
                }
            }
        }
        
        return errors;
    }
}

// 使用示例
class RegistrationForm {
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    public RegistrationForm(String username, String email) {
        this.username = username;
        this.email = email;
    }
}

class ValidatorDemo {
    public static void main(String[] args) throws Exception {
        RegistrationForm form = new RegistrationForm("", "invalid-email");
        List<String> errors = Validator.validate(form);
        
        if (errors.isEmpty()) {
            System.out.println("验证通过!");
        } else {
            System.out.println("验证失败:");
            errors.forEach(error -> System.out.println("  - " + error));
        }
    }
}

7. 总结

本篇我们学习了:

泛型类和方法 :类型参数的定义和使用

类型边界 :extends、super关键字

通配符 :上界、下界、无界通配符

类型擦除 :泛型的实现机制和限制

注解 :内置注解、自定义注解、元注解

反射:运行时获取类信息、动态操作对象

核心要点:

  1. 泛型提供编译时类型安全
  2. 类型擦除是泛型的实现机制,有其限制
  3. 注解是元数据,不改变程序逻辑
  4. 反射可以在运行时操作类,但要注意性能

下一篇预告: 《Java从零到熟练(九):并发编程基础》

  • 学习多线程编程
  • 理解线程同步和锁
  • 掌握并发集合和线程池

参考资源

  1. Java泛型教程
  2. Java注解教程
  3. Java反射API

下一篇: Java从零到熟练(九):并发编程基础

相关推荐
m沐沐1 小时前
【机器学习】Python 实现垃圾邮件分类(随机森林 + 可视化 + 特征重要性)
人工智能·python·随机森林·机器学习·分类·pycharm·回归算法
SilentSamsara1 小时前
命令行工具开发:Click/Typer + 打包为独立二进制
linux·服务器·开发语言·前端·python·青少年编程·fastapi
Ulyanov1 小时前
深入QML滑块与进度控制:构建动态数据可视化界面:QML+PySide6现代开发入门(六)
开发语言·python·算法·ui·信息可视化·雷达电子对抗仿真
扫地僧9851 小时前
一个基于 PyTorch 手语翻译模型Xuanmen_Net
人工智能·pytorch·python
zyl837211 小时前
Python 函数、模块、异常处理 超详细入门教程
开发语言·windows·python
苏州IT威翰德1 小时前
苏州IT基础架构IQ/OQ/PQ确认服务 | 服务器网络验证
开发语言·php
半夜修仙1 小时前
RabbitMQ入门概述
java·rabbitmq·java-rabbitmq
fengxin_rou1 小时前
【滑动窗口与前缀和算法实战】:LeetCode560.438 高频题深度解析
java·算法·leetcode
dusk_star1 小时前
go语言--笔记--接口
java·笔记·golang