当前位置: 首页 > news >正文

java复习 07

我感觉泛型蛮难的,哎。。。算了算了,多练练吧!

1 泛型概述

泛型:是 JDK5 中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型。
它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用 / 调用时传入具体的类型。
这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口

泛型定义格式:

  • <类型>:指定一种类型的格式,这里的类型可以看成是形参
  • <类型 1, 类型 2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
  • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型

2 泛型类

package PTA_training.GenericsTest;public class Main {public static void main(String[] args) {Generic<String> a = new Generic<String>();a.setT("wow");System.out.println(a.getT());Generic<Boolean> flag = new Generic<Boolean>();flag.setT(false);System.out.println(flag.getT());}
}
package PTA_training.GenericsTest;public class Generic <T>{private T t ;public T getT() {return t;}public void setT(T t) {this.t = t;}
}

 

3 方法重载、泛型类和泛型方法的区别

在 Java 中,方法重载、泛型类和泛型方法是三种不同的代码复用机制,它们各有特点。下面通过一个show方法的例子来对比它们的区别。

1. 方法重载(Overloading)

方法重载是指在同一个类中定义多个同名但参数列表不同的方法。编译器根据传入的参数类型来决定调用哪个方法。

public class OverloadingExample {// 重载方法:打印int类型public void show(int num) {System.out.println("Integer: " + num);}// 重载方法:打印double类型public void show(double num) {System.out.println("Double: " + num);}// 重载方法:打印String类型public void show(String str) {System.out.println("String: " + str);}// 测试方法public static void main(String[] args) {OverloadingExample example = new OverloadingExample();example.show(10);         // 调用show(int)example.show(3.14);       // 调用show(double)example.show("Hello");    // 调用show(String)}
}

特点

  • 方法名相同,但参数类型、数量或顺序不同
  • 每个方法处理特定类型的数据
  • 编译时根据参数类型确定调用哪个方法
  • 代码冗余度较高(需要为每种类型编写单独的方法)
2. 泛型类改进

泛型类是指在类的定义中使用类型参数,使得类可以处理多种类型的数据。

public class GenericClassExample<T> {private T value;public GenericClassExample(T value) {this.value = value;}// 泛型类中的方法,可以处理T类型的数据public void show() {System.out.println("Generic: " + value);}public static void main(String[] args) {// 创建Integer类型的泛型类实例GenericClassExample<Integer> intExample = new GenericClassExample<>(10);intExample.show();  // 打印Integer值// 创建Double类型的泛型类实例GenericClassExample<Double> doubleExample = new GenericClassExample<>(3.14);doubleExample.show();  // 打印Double值// 创建String类型的泛型类实例GenericClassExample<String> stringExample = new GenericClassExample<>("Hello");stringExample.show();  // 打印String值}
}

特点

  • 类的定义中使用类型参数<T>
  • 整个类都可以使用该类型参数
  • 需要为每种类型创建单独的实例
  • 类型安全,编译时检查类型错误
  • 减少代码冗余,但使用时需要创建多个实例
3. 泛型方法改进

泛型方法是指在方法的定义中使用类型参数,使得方法可以处理多种类型的数据,而不依赖于类的泛型定义。

public class GenericMethodExample {// 泛型方法:使用<T>声明类型参数public <T> void show(T value) {System.out.println("Generic Method: " + value);}// 泛型方法:可以指定多个类型参数public <T, U> void show(T value1, U value2) {System.out.println("Generic Method: " + value1 + ", " + value2);}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 调用泛型方法,传入不同类型的参数example.show(10);                // 传入Integerexample.show(3.14);              // 传入Doubleexample.show("Hello");           // 传入Stringexample.show(true);              // 传入Boolean// 调用接受两个参数的泛型方法example.show("Hello", 123);      // 传入String和Integerexample.show(3.14, "World");     // 传入Double和String}
}

特点

  • 方法签名中使用<T>声明类型参数
  • 可以在非泛型类中定义泛型方法
  • 同一个实例可以处理多种类型的数据
  • 类型安全,编译时检查类型错误
  • 最灵活的代码复用方式,不需要创建多个实例

三者对比总结

特性方法重载泛型类泛型方法
类型处理为每种类型编写单独方法一个类处理多种类型一个方法处理多种类型
代码复用低(重复代码)中(类级复用)高(方法级复用)
实例创建无需特殊处理需要为每种类型创建实例同一个实例可处理多种类型
类型安全
适用场景参数类型固定且有限类整体需要处理多种类型单个方法需要处理多种类型

泛型方法通常是最灵活的选择,因为它可以在不创建泛型类的情况下实现类型参数化,并且同一个实例可以处理多种类型的数据。

4 关于泛型接口

泛型接口(Generic Interface)详解

泛型接口是 Java 泛型编程中的重要概念,它允许接口在定义时使用类型参数,使得实现类可以灵活地处理不同类型的数据。下面从定义、实现和应用场景等方面详细介绍泛型接口。

1. 泛型接口的定义与语法

泛型接口的定义与普通接口类似,只需在接口名称后添加类型参数声明(如<T>),类型参数可在接口的方法签名中使用。

java

// 定义泛型接口:声明类型参数<T>
public interface GenericInterface<T> {// 接口方法使用类型参数TT getData();void setData(T data);String showType();
}

语法说明

  • 类型参数(如T)可自定义名称,常用T(Type)、E(Element)、K(Key)、V(Value)等
  • 接口中的方法返回值、参数类型均可使用类型参数
  • 可声明多个类型参数,如interface Map<K, V>
2. 泛型接口的实现方式

实现泛型接口时,有两种方式指定类型参数:明确指定具体类型保留类型参数

方式一:实现时指定具体类型

java

// 实现类明确指定类型为Integer
public class IntGenericClass implements GenericInterface<Integer> {private Integer data;@Overridepublic Integer getData() {return data;}@Overridepublic void setData(Integer data) {this.data = data;}@Overridepublic String showType() {return "类型为 Integer";}
}// 实现类明确指定类型为String
public class StringGenericClass implements GenericInterface<String> {private String data;@Overridepublic String getData() {return data;}@Overridepublic void setData(String data) {this.data = data;}@Overridepublic String showType() {return "类型为 String";}
}
方式二:实现时保留类型参数(让子类决定类型)

java

// 实现类保留类型参数T,由子类或实例化时指定
public class GenericImpl<T> implements GenericInterface<T> {private T data;@Overridepublic T getData() {return data;}@Overridepublic void setData(T data) {this.data = data;}@Overridepublic String showType() {return "类型为 " + data.getClass().getSimpleName();}
}// 使用示例
public class GenericInterfaceDemo {public static void main(String[] args) {// 方式一:实现类指定具体类型GenericInterface<Integer> intImpl = new IntGenericClass();intImpl.setData(100);System.out.println(intImpl.getData() + " - " + intImpl.showType());GenericInterface<String> strImpl = new StringGenericClass();strImpl.setData("Hello Generic");System.out.println(strImpl.getData() + " - " + strImpl.showType());// 方式二:实现类保留类型参数,实例化时指定GenericInterface<Double> doubleImpl = new GenericImpl<>();doubleImpl.setData(3.14);System.out.println(doubleImpl.getData() + " - " + doubleImpl.showType());}
}
3. 泛型接口的应用场景

泛型接口在 Java 框架和实际开发中广泛应用,常见场景包括:

(1)集合框架中的接口

Java 集合框架的核心接口几乎都是泛型接口,例如:

  • List<E>:有序集合,元素可重复
  • Set<E>:无序集合,元素唯一
  • Map<K, V>:键值对映射

java

// 示例:使用泛型接口List
List<String> stringList = new ArrayList<>();
stringList.add("Java");
stringList.add("泛型");List<Integer> intList = new ArrayList<>();
intList.add(10);
intList.add(20);
(2)自定义通用接口

当需要定义一组处理不同类型数据的通用行为时,可使用泛型接口。例如:

java

// 定义数据处理器接口,处理不同类型的数据
public interface DataProcessor<T> {T process(T data);  // 处理数据T validate(T data); // 验证数据
}// 实现类:处理整数数据
public class IntProcessor implements DataProcessor<Integer> {@Overridepublic Integer process(Integer data) {return data * 2;}@Overridepublic Integer validate(Integer data) {return data > 0 ? data : 0;}
}// 实现类:处理字符串数据
public class StringProcessor implements DataProcessor<String> {@Overridepublic String process(String data) {return data.toUpperCase();}@Overridepublic String validate(String data) {return data != null ? data : "";}
}
(3)泛型接口与多态结合

泛型接口可与多态结合,实现更灵活的代码架构:

java

// 定义泛型接口
public interface Printer<T> {void print(T data);
}// 实现类1:打印整数
public class IntPrinter implements Printer<Integer> {@Overridepublic void print(Integer data) {System.out.println("整数:" + data);}
}// 实现类2:打印字符串
public class StringPrinter implements Printer<String> {@Overridepublic void print(String data) {System.out.println("字符串:" + data);}
}// 通用打印服务,接受泛型接口作为参数(多态应用)
public class PrintService {public <T> void printData(Printer<T> printer, T data) {printer.print(data);}
}// 测试多态应用
public class PolymorphismDemo {public static void main(String[] args) {PrintService service = new PrintService();// 使用整数打印机service.printData(new IntPrinter(), 123);// 使用字符串打印机service.printData(new StringPrinter(), "泛型接口演示");}
}
4. 泛型接口与泛型类、泛型方法的对比
特性泛型接口泛型类泛型方法
定义位置接口层面(影响所有实现方法)类层面(影响类中所有成员)方法层面(仅影响当前方法)
主要作用定义通用行为规范封装通用数据结构实现方法级别的类型复用
实现方式由实现类决定类型参数实例化时指定类型参数调用时推断或指定类型参数
典型应用集合接口(如 List<E>)集合类(如 ArrayList<E>)工具方法(如 Collections.sort)
与多态的结合强(实现类可作为接口类型使用)中(实例可作为泛型类型变量)弱(方法级复用,不影响类型层次)
5. 泛型接口的注意事项
  • 类型擦除:泛型接口在编译后会擦除类型参数(如T会被擦除为Object),但编译时仍会进行类型检查。
  • 不能使用基本类型:类型参数不能直接使用intdouble等基本类型,需使用包装类(如IntegerDouble)。
  • 接口继承:泛型接口可以继承其他泛型接口,例如:

    java

    public interface ChildInterface<T> extends GenericInterface<T> {// 扩展方法T convert();
    }
    
  • 静态成员限制:泛型接口中不能定义静态成员变量或静态泛型方法(静态成员属于类,而非实例,与泛型类型参数冲突)。

总结

泛型接口通过类型参数化,将接口的行为抽象为可处理不同类型数据的通用规范,实现了代码的高度复用和类型安全。它在 Java 集合框架、框架设计和业务逻辑分层中扮演重要角色,是泛型编程中连接不同组件的桥梁。通过合理使用泛型接口,可使代码架构更灵活、可维护性更强。

5 类型通配符

为了表示各种泛型 List 的父类,可以使用类型通配符

  • 类型通配符:<?>
  • List<?>:表示元素类型未知的 List,它的元素可以匹配任何的类型
  • 这种带通配符的 List 仅表示它是各种泛型 List 的父类,并不能把元素添加到其中

如果说我们不希望 List<?> 是任何泛型 List 的父类,只希望它代表某一类泛型 List 的父类,可以使用类型通配符的上限~

  • 类型通配符上限:<? extends 类型>
  • List<? extends Number>:它表示的类型是 Number 或者其子类型

除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限

  • 类型通配符下限:<? super 类型>
  • List<? super Number>:它表示的类型是 Number 或者其父类型

=========================================================================

通配符:指定类型范围,避免开发不知道该数据只支持的范围,设置错误类型导致出现bug!

=========================================================================

6 可变参数

可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了

  • 格式:修饰符 返回值类型 方法名 (数据类型... 变量名){ }
  • 范例:public static int sum (int... a) { }

可变参数注意事项

这里的变量其实是一个数组(a是数组)

如果一个方法有多个参数,包含可变参数,可变参数要放在最后!!!

  • Arrays 工具类中有一个静态方法:
    • public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表
    • 返回的集合不能做增删操作,可以做修改操作
  • List 接口中有一个静态方法:
    • public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
    • 返回的集合不能做增删改操作
  • Set 接口中有一个静态方法:
    • public static <E> Set<E> of(E... elements):返回一个包含任意数量元素的不可变集合
    • 在给元素的时候,不能给重复的元素
    • 返回的集合不能做增删操作,没有修改的方法

代码解释(以 Java 语言为例 )

  1. Arrays.asList 方法
import java.util.Arrays;
import java.util.List;
public class Main {public static void main(String[] args) {String[] arr = {"a", "b", "c"};// 使用 Arrays.asList 将数组转换为 ListList<String> list = Arrays.asList(arr);// 可以修改元素,因为支持修改操作list.set(0, "A");System.out.println(list); // 输出: [A, b, c]// 不能进行增删操作,会抛出 UnsupportedOperationException 异常// list.add("d"); // list.remove(0);}
}

Arrays.asList 接收可变参数(本质是数组),返回的 List 是由原数组支持的固定大小列表。它基于数组,所以长度不可变(增删会改变长度,不允许 ),但元素内容可通过 set 等修改方法变更。
2. List.of 方法

java

import java.util.List;
public class Main {public static void main(String[] args) {// 使用 List.of 创建不可变 ListList<String> list = List.of("x", "y", "z");// 不能进行增删改操作,以下操作都会抛出 UnsupportedOperationException 异常// list.add("w");// list.remove(0); // list.set(0, "X");System.out.println(list); // 输出: [x, y, z]}
}

List.of 接收可变参数,创建的是不可变 List。其内部结构不允许进行元素的增加、删除、修改操作,主要用于创建一个内容固定、后续无需改变的列表场景,比如存储常量集合。
3. Set.of 方法

java

import java.util.Set;
public class Main {public static void main(String[] args) {// 元素不能重复,否则编译或运行报错(如传入两个 "m" 会抛 IllegalArgumentException)Set<String> set = Set.of("m", "n", "p"); // 不能进行增删操作,以下操作会抛出 UnsupportedOperationException 异常// set.add("q"); // set.remove("m");System.out.println(set); // 输出: [m, n, p]}
}

Set.of 接收可变参数创建不可变 Set,遵循 Set 元素不重复的特性,若传入重复元素会报错。且创建后的集合不支持增删操作,常用于创建固定、不重复且后续无需变动的集合,像存储一组唯一的常量标识等场景 。

7 基本数据类型和引用数据类型

在 Java 里,基本数据类型和引用数据类型是重要概念,二者有明显区别,下面为你详细介绍:

一、基本数据类型

Java 中基本数据类型共 8 种,是语言内置、非对象的简单数据类型,直接存于栈内存,特点如下:

  1. 分类及取值范围
    • 整数类型
      • byte:占 1 字节(8 位),范围 -128 ~ 127 ,适合存储小整数值,如 byte num = 10; 。
      • short:占 2 字节(16 位),范围 -32768 ~ 32767 ,如 short s = 1000; 。
      • int:占 4 字节(32 位),范围 -2147483648 ~ 2147483647 ,日常整数默认类型,像 int age = 20; 。
      • long:占 8 字节(64 位),范围极大,声明时需加 L 或 l (推荐 L 避免混淆),如 long bigNum = 10000000000L; 。
    • 浮点类型
      • float:占 4 字节(32 位),单精度,声明时加 F 或 f ,如 float price = 3.14F; 。
      • double:占 8 字节(64 位),双精度,是浮点型默认类型,如 double weight = 60.5; 。
    • 字符类型
      • char:占 2 字节(16 位),存 Unicode 字符,用单引号,如 char ch = 'A'; 、char ch2 = '中'; 。
    • 布尔类型
      • boolean:只有 true 和 false 两个值,用于条件判断,如 boolean isReady = true; 。
  2. 特性
    直接存储值,变量名对应数据值,非引用地址;栈内存存取快;不是对象,无 toString() 、equals() 等对象方法(但 Java 会自动装箱让基本类型可调用部分方法,如 Integer.valueOf(10).toString() );赋值是按值传递,新变量获原变量值副本,相互独立 。

二、引用数据类型

引用数据类型基于类,是对象类型,变量存堆内存中对象的引用地址(指针),栈内存存引用地址,特点如下:

  1. 常见类型
    • 类(Class):如自定义类 Person 、系统类 String 、ArrayList 等,Person p = new Person(); 中 p 存对象在堆内存的引用。
    • 接口(Interface):像 List 、Map 等接口,List<String> list = new ArrayList<>(); 里 list 存 ArrayList 对象的引用。
    • 数组(Array):无论基本还是引用类型数组,数组变量是引用类型,int[] arr = new int[5]; 中 arr 存数组在堆内存的引用 。
    • 枚举(Enumeration):Java 5.0 引入,如 enum Season { SPRING, SUMMER } ,Season s = Season.SPRING; 中 s 存枚举实例引用。
  2. 特性
    堆内存 存对象实际数据,栈内存 存引用地址;变量名 存引用地址,非数据本身;是对象,有属性和方法;赋值是传递引用地址,多个变量可能指向同一对象,操作会相互影响(如 Person p1 = new Person(); Person p2 = p1; p2.setName("Tom"); ,p1 对应的对象名称也会改变 );比较相等性时,== 比较引用地址(是否同一对象),equals (需重写才比较内容,如 String 类重写了 equals 方法 )比较对象内容 。

三、二者关键区别总结

对比项基本数据类型引用数据类型
存储位置栈内存直接存值栈存引用地址,堆存对象实际数据
类型本质非对象,是简单数据值是对象,基于类创建的实例
赋值方式按值传递,新变量获值副本,相互独立传递引用地址,多个变量可能指向同一对象
相等性比较== 比较值== 比较引用地址,equals (合理重写后)比较内容
方法调用本身无对象方法(自动装箱后可调用部分)可调用对象的属性和方法

理解这些区别,有助于在 Java 编程中合理使用不同数据类型,避免因类型处理不当引发 bug ,比如集合框架(如 List 、Map )需用引用类型,基本数据类型需借助包装类(如 Integer 、Double )才能存入集合 。

四、为什么要把这两个数据类型做区分?

Java 区分基本数据类型和引用数据类型,主要基于 内存管理、性能、使用场景 等角度设计,背后是语言对效率、复杂度、数据特性的权衡,以下从核心原因展开:

1. 内存存储与效率需求

  • 基本数据类型:直接存值(如 int a = 10a 变量在栈内存直接存 10 )。这类数据简单、大小固定(如 int 占 4 字节 ),栈内存读写速度极快,适合频繁使用的 “简单数据”(像数值计算、布尔判断 ),满足程序高效处理基础操作的需求。
  • 引用数据类型:存的是对象在堆内存的地址(如 String s = new String("Hello")s 在栈存地址,堆存实际字符串内容 )。对象往往复杂、大小不固定(如一个 ArrayList 可动态扩容 ),堆内存用于灵活存储 “复杂数据结构”,但访问需通过地址跳转,开销比栈大。分开存储能让简单数据走高效栈,复杂数据用灵活堆,平衡效率与功能。

2. 数据特性与使用场景

  • 基本数据类型:代表 “原子性” 数据(数字、字符、布尔等 ),无需拆分,直接存值就能满足需求(如统计年龄用 int ,判断状态用 boolean )。
  • 引用数据类型:代表 “复合型” 数据(对象、数组、集合等 ),需包含多个属性或功能(如 Person 类有姓名、年龄,List 可存一系列元素 )。通过 “引用” 关联堆中对象,能实现 数据共享、复杂逻辑封装(多个变量可指向同一对象,修改一处联动更新 ),支撑面向对象编程的 “封装、继承、多态” 特性。

3. 语言设计的实用性

  • 避免性能浪费:若所有数据都按 “对象” 存堆(类似其他语言的纯对象模式 ),简单操作(如 a + b )会因频繁创建对象、跳转堆内存,拖慢程序。基本数据类型用栈直接存值,让基础运算 “轻量化”。
  • 兼容历史与习惯:Java 设计参考 C/C++ ,保留类似 “基本类型” 的高效用法,降低开发者学习成本,同时通过 包装类(如 Integer 对应 int ),让基本类型也能 “伪对象化”,适配泛型、集合等需要对象的场景,灵活又高效。

简单说,区分是为了 “让简单的更快,让复杂的更灵活”:基本类型专攻高效基础操作,引用类型支撑复杂对象逻辑,共同构建 Java 灵活又高效的编程体系。

 ======

心情复杂,我觉得我还没有掌握这些,泛型还是有点抽象的

 

相关文章:

  • Rust 学习笔记:通过 Send 和 Sync trait 实现可扩展并发性
  • C++11列表初始化:从入门到精通
  • 电脑扩展屏幕工具
  • dbeaver 查询clickhouse,数据库时间差了8小时
  • echarts 数据大屏(无UI设计 极简洁版)
  • 条件概率:AI大模型概率统计的基石
  • (二)TensorRT-LLM | 模型导出(v0.20.0rc3)
  • 二叉树进阶:经典算法题详解
  • 6月10日day50打卡
  • 可视化图解算法50:最小的K个数
  • SpringBoot集成Tess4j :低成本解锁OCR 图片识别能力
  • 鹰盾视频的AI行为检测是怎样的风控?
  • [极客时间]LangChain 实战课 ----- 01|LangChain系统安装和快速入门(2)
  • 第四章 软件需求工程
  • 论文略读: CUT YOUR LOSSES IN LARGE-VOCABULARY LANGUAGE MODELS
  • 如何通过DNS解析实现负载均衡?有哪些优势?
  • 期权卖方是谁?
  • Linux动态库与静态库详解:从入门到精通
  • mysql-innoDB存储引擎事务的原理
  • 智能合约安全专题(一):什么是重入攻击?——从 DAO 事件谈起
  • 七牛链接wordpress/济南seo整站优化招商电话
  • b2b b2c c2c o2o例子/seo企业优化方案
  • 网页设计制作价格/武汉网站开发公司seo
  • seo搜索营销分析方案/我们seo
  • flask网站开发源码/网站一级域名和二级域名
  • 武汉电商网站建设/seo综合查询站长工具关键词