JAVA 泛型与通配符:从原理到实战应用

JAVA 泛型与通配符:从原理到实战应用

JAVA 泛型与通配符:从原理到实战应用

在这里插入图片描述

1.1 本章学习目标与重点

💡 掌握泛型的核心概念与设计初衷,理解泛型的编译期检查机制。
💡 熟练使用泛型类、泛型接口和泛型方法,解决数据类型安全问题。
💡 理解通配符(?)、上界通配符(? extends T)和下界通配符(? super T)的使用场景。
⚠️ 本章重点是 泛型的擦除机制通配符的灵活运用,这是提升代码通用性和安全性的关键。

1.2 泛型的核心概念与设计初衷

1.2.1 为什么需要泛型

在没有泛型的 JDK 5 之前,集合类只能存储 Object 类型的对象。获取元素时需要强制类型转换,这会带来两个严重问题:

  1. 类型不安全:可以向集合中添加任意类型的对象,运行时可能抛出 ClassCastException
  2. 代码臃肿:频繁的强制类型转换会让代码可读性和维护性变差。

💡 泛型的出现就是为了解决这些问题,它的核心思想是 将类型参数化。在定义类、接口或方法时,用一个标识符表示类型,使用时再指定具体类型。
泛型可以在编译期进行类型检查,避免运行时的类型转换异常,同时简化代码。

1.2.2 泛型的核心优势

  • 编译期类型安全:编译器会检查集合中元素的类型,不允许添加错误类型的对象。
  • 消除强制类型转换:获取元素时无需手动转换类型,代码更简洁。
  • 代码复用性高:一套泛型代码可以适配多种数据类型,无需重复编写。

✅ 核心结论:泛型是一种编译期技术,它的作用是在编译阶段保证类型安全,运行时泛型信息会被擦除。

1.3 泛型的三种使用方式

1.3.1 泛型类

💡 泛型类是在类的定义时声明类型参数,语法格式为 class 类名<T>。其中 T 是类型参数,可以是任意标识符,常用的有 T(Type)、E(Element)、K(Key)、V(Value)。

代码实操:自定义泛型集合类

我们实现一个简单的泛型顺序表 GenericArrayList,支持任意类型的数据存储:

publicclassGenericArrayList<T>{// 底层数组,存储泛型类型数据privateObject[] elementData;// 集合大小privateint size;// 默认初始容量privatestaticfinalintDEFAULT_CAPACITY=10;// 无参构造publicGenericArrayList(){ elementData =newObject[DEFAULT_CAPACITY];}// 有参构造,指定初始容量publicGenericArrayList(int initialCapacity){if(initialCapacity <0){thrownewIllegalArgumentException("初始容量不能为负数:"+ initialCapacity);} elementData =newObject[initialCapacity];}// 添加元素publicvoidadd(T t){// 扩容判断if(size == elementData.length){grow();} elementData[size++]= t;}// 获取元素@SuppressWarnings("unchecked")publicTget(int index){// 索引合法性检查if(index <0|| index >= size){thrownewIndexOutOfBoundsException("索引越界:"+ index);}// 强制类型转换,通过泛型保证类型安全return(T) elementData[index];}// 数组扩容privatevoidgrow(){int oldCapacity = elementData.length;// 扩容为原来的1.5倍int newCapacity = oldCapacity +(oldCapacity >>1); elementData =java.util.Arrays.copyOf(elementData, newCapacity);}// 获取集合大小publicintsize(){return size;}// 测试方法publicstaticvoidmain(String[] args){// 1. 存储String类型GenericArrayList<String> strList =newGenericArrayList<>(); strList.add("Java"); strList.add("泛型");// 编译期检查,不允许添加非String类型// strList.add(123); 编译报错String str = strList.get(0);System.out.println("String集合元素:"+ str);// 2. 存储Integer类型GenericArrayList<Integer> intList =newGenericArrayList<>(); intList.add(1); intList.add(2);Integer num = intList.get(1);System.out.println("Integer集合元素:"+ num);}}

输出结果

String集合元素:Java Integer集合元素:2 

⚠️ 注意事项:

  1. 泛型类的类型参数不能是基本数据类型,只能是引用数据类型。如果需要存储基本类型,需使用对应的包装类,如 Integer 代替 int
  2. 泛型类的静态方法中不能使用类的泛型参数,因为静态方法属于类,而泛型参数是在创建对象时指定的。

1.3.2 泛型接口

💡 泛型接口的定义与泛型类类似,语法格式为 interface 接口名<T>。实现泛型接口时,可以指定具体类型,也可以继续使用泛型参数。

代码实操:自定义泛型迭代器接口
// 泛型迭代器接口publicinterfaceGenericIterator<T>{// 判断是否有下一个元素booleanhasNext();// 获取下一个元素Tnext();}// 实现类1:指定具体类型为StringpublicclassStringIteratorimplementsGenericIterator<String>{privateString[] array;privateint index;publicStringIterator(String[] array){this.array = array;this.index =0;}@OverridepublicbooleanhasNext(){return index < array.length;}@OverridepublicStringnext(){return array[index++];}}// 实现类2:继续使用泛型参数publicclassArrayIterator<T>implementsGenericIterator<T>{privateT[] array;privateint index;publicArrayIterator(T[] array){this.array = array;this.index =0;}@OverridepublicbooleanhasNext(){return index < array.length;}@OverridepublicTnext(){return array[index++];}}// 测试类publicclassGenericIteratorTest{publicstaticvoidmain(String[] args){// 测试StringIteratorString[] strArray ={"A","B","C"};GenericIterator<String> strIterator =newStringIterator(strArray);while(strIterator.hasNext()){System.out.println(strIterator.next());}// 测试ArrayIteratorInteger[] intArray ={1,2,3};GenericIterator<Integer> intIterator =newArrayIterator<>(intArray);while(intIterator.hasNext()){System.out.println(intIterator.next());}}}

输出结果

A B C 1 2 3 

1.3.3 泛型方法

💡 泛型方法是在方法声明时指定类型参数,它可以定义在普通类中,也可以定义在泛型类中。语法格式为 public <T> 返回值类型 方法名(T 参数)

泛型方法的核心特点是 类型参数由方法的调用者决定,与类的泛型参数无关。

代码实操:泛型工具方法封装
importjava.util.Arrays;publicclassGenericMethodUtil{// 泛型方法:数组转集合publicstatic<T>GenericArrayList<T>arrayToList(T[] array){GenericArrayList<T> list =newGenericArrayList<>();for(T t : array){ list.add(t);}return list;}// 泛型方法:交换数组中两个元素的位置publicstatic<T>voidswap(T[] array,int i,int j){if(i <0|| i >= array.length || j <0|| j >= array.length){thrownewIndexOutOfBoundsException("索引越界");}T temp = array[i]; array[i]= array[j]; array[j]= temp;}// 测试方法publicstaticvoidmain(String[] args){// 测试数组转集合String[] strArray ={"Java","泛型","方法"};GenericArrayList<String> strList =GenericMethodUtil.arrayToList(strArray);System.out.println("数组转集合大小:"+ strList.size());// 测试交换数组元素Integer[] intArray ={10,20,30,40};System.out.println("交换前数组:"+Arrays.toString(intArray));GenericMethodUtil.swap(intArray,1,3);System.out.println("交换后数组:"+Arrays.toString(intArray));}}

输出结果

数组转集合大小:3 交换前数组:[10, 20, 30, 40] 交换后数组:[10, 40, 30, 20] 

💡 技巧:泛型方法可以配合可变参数使用,进一步提升灵活性。例如:

publicstatic<T>GenericArrayList<T>createList(T... elements){GenericArrayList<T> list =newGenericArrayList<>();for(T t : elements){ list.add(t);}return list;}// 调用方式GenericArrayList<Integer> list =GenericMethodUtil.createList(1,2,3,4);

1.4 泛型的高级特性

1.4.1 泛型的擦除机制

💡 泛型是编译期特性,在运行时 JVM 会擦除泛型信息,这个过程称为类型擦除
类型擦除的规则:

  1. 泛型类、接口和方法的类型参数会被擦除为其上限类型,如果没有指定上限,则擦除为 Object
  2. 编译器会在必要的地方插入强制类型转换代码,保证运行时的类型正确性。

例如,我们定义的 GenericArrayList<String> 在运行时会被擦除为 GenericArrayList<Object>。获取元素时,编译器会自动插入 (String) 强制类型转换代码。

类型擦除的验证

通过反射可以验证类型擦除的存在,因为反射是运行时机制,可以获取到擦除后的类型信息:

importjava.lang.reflect.Field;publicclassGenericErasureTest{publicstaticvoidmain(String[] args)throwsNoSuchFieldException{GenericArrayList<String> strList =newGenericArrayList<>();// 通过反射获取底层数组的类型Field field =GenericArrayList.class.getDeclaredField("elementData"); field.setAccessible(true);Class<?> type = field.getType().getComponentType();System.out.println("底层数组的类型:"+ type.getName());}}

输出结果

底层数组的类型:java.lang.Object 

⚠️ 注意事项:类型擦除会导致泛型不支持重载。例如,以下两个方法在编译后会变成相同的签名,编译器会报错:

// 编译报错:方法重复定义publicvoidmethod(GenericArrayList<String> list){}publicvoidmethod(GenericArrayList<Integer> list){}

1.4.2 泛型的上下界限定

💡 在定义泛型时,可以通过 extends 关键字限制类型参数的上限,通过 super 关键字限制类型参数的下限(仅在通配符中使用)。
上限限定的语法:<T extends 类型>,表示 T 必须是该类型的子类或本身。
多个上限的语法:<T extends 类型1 & 类型2>,表示 T 必须同时实现多个接口(类只能有一个,且放在最前面)。

代码实操:泛型上限限定

我们实现一个泛型方法,用于计算数组中元素的总和,要求元素类型必须是 Number 的子类:

publicclassGenericBoundsTest{// 泛型上限限定:T必须是Number的子类publicstatic<TextendsNumber>doublesum(T[] array){double sum =0.0;for(T t : array){ sum += t.doubleValue();}return sum;}publicstaticvoidmain(String[] args){Integer[] intArray ={1,2,3,4,5};System.out.println("Integer数组总和:"+sum(intArray));Double[] doubleArray ={1.1,2.2,3.3};System.out.println("Double数组总和:"+sum(doubleArray));// String类型不是Number的子类,编译报错// String[] strArray = {"1", "2"};// sum(strArray);}}

输出结果

Integer数组总和:15.0 Double数组总和:6.6 

1.5 通配符的使用与场景

1.5.1 通配符(?)的基本使用

💡 通配符 ? 表示任意类型,它可以用来解决泛型的类型不兼容问题。
例如,GenericArrayList<Object> 不能接收 GenericArrayList<String> 对象,因为泛型是不协变的。此时可以使用通配符 GenericArrayList<?> 来接收任意类型的泛型实例。

代码实操:通配符的基本应用
publicclassWildcardBasicTest{// 使用通配符接收任意类型的GenericArrayListpublicstaticvoidprintList(GenericArrayList<?> list){for(int i =0; i < list.size(); i++){System.out.println("元素:"+ list.get(i));}}publicstaticvoidmain(String[] args){GenericArrayList<String> strList =newGenericArrayList<>(); strList.add("Hello"); strList.add("Wildcard");GenericArrayList<Integer> intList =newGenericArrayList<>(); intList.add(100); intList.add(200);// 可以接收任意类型的泛型集合printList(strList);printList(intList);}}

输出结果

元素:Hello 元素:Wildcard 元素:100 元素:200 

⚠️ 注意事项:使用无界通配符 <?> 的集合,只能读取元素,不能添加元素(除了 null)。因为编译器无法确定集合的具体类型,添加任何类型的元素都可能破坏类型安全。

1.5.2 上界通配符(? extends T)

💡 上界通配符 ? extends T 表示任意 T 的子类或 T 本身。它的核心作用是 限定读取的上限,适合只读的场景。

代码实操:上界通配符的应用

我们实现一个方法,用于获取泛型集合中的最大值,要求元素类型必须实现 Comparable 接口:

publicclassWildcardExtendsTest{// 上界通配符:T必须实现Comparable接口publicstatic<TextendsComparable<T>>TgetMax(GenericArrayList<?extendsT> list){if(list.size()==0){returnnull;}T max = list.get(0);for(int i =1; i < list.size(); i++){T current = list.get(i);if(current.compareTo(max)>0){ max = current;}}return max;}publicstaticvoidmain(String[] args){GenericArrayList<Integer> intList =newGenericArrayList<>(); intList.add(5); intList.add(12); intList.add(3);System.out.println("Integer集合最大值:"+getMax(intList));GenericArrayList<String> strList =newGenericArrayList<>(); strList.add("Apple"); strList.add("Banana"); strList.add("Orange");System.out.println("String集合最大值:"+getMax(strList));}}

输出结果

Integer集合最大值:12 String集合最大值:Orange 

✅ 核心结论:上界通配符 ? extends T 适合读取数据的场景,它可以接收 T 及其子类的泛型实例。

1.5.3 下界通配符(? super T)

💡 下界通配符 ? super T 表示任意 T 的父类或 T 本身。它的核心作用是 限定写入的下限,适合只写的场景。

代码实操:下界通配符的应用

我们实现一个方法,用于向泛型集合中添加指定类型的元素:

publicclassWildcardSuperTest{// 下界通配符:向集合中添加Integer类型的元素publicstaticvoidaddIntegers(GenericArrayList<?superInteger> list){ list.add(1); list.add(2); list.add(3);// 可以添加Integer及其子类的元素// list.add(new Number()); 编译报错,Number是Integer的父类}publicstaticvoidmain(String[] args){// 情况1:集合类型为IntegerGenericArrayList<Integer> intList =newGenericArrayList<>();addIntegers(intList);System.out.println("Integer集合大小:"+ intList.size());// 情况2:集合类型为Number(Integer的父类)GenericArrayList<Number> numList =newGenericArrayList<>();addIntegers(numList);System.out.println("Number集合大小:"+ numList.size());}}

输出结果

Integer集合大小:3 Number集合大小:3 

✅ 核心结论:下界通配符 ? super T 适合写入数据的场景,它可以接收 T 及其父类的泛型实例。

1.5.4 通配符的使用原则:PECS 原则

💡 PECS 原则是通配符使用的黄金法则,全称是 Producer Extends, Consumer Super

  • Producer Extends:如果泛型对象是生产者(提供数据,只读),使用 ? extends T
  • Consumer Super:如果泛型对象是消费者(消费数据,只写),使用 ? super T

例如:

  • 读取数据的 getMax 方法,使用 ? extends T
  • 写入数据的 addIntegers 方法,使用 ? super T
  • 既读又写的场景,不使用通配符,直接使用具体的泛型类型。

1.6 泛型的常见问题与解决方案

1.6.1 泛型数组的创建问题

⚠️ 不能直接创建泛型数组,例如 new T[10] 会编译报错。因为类型擦除后,JVM 无法确定数组的具体类型。
解决方案

  1. 创建 Object 数组,然后强制类型转换。
  2. 使用反射创建泛型数组。
// 方式1:Object数组强制转换T[] array =(T[])newObject[10];// 方式2:反射创建泛型数组publicstatic<T>T[]createArray(Class<T> clazz,int size){return(T[])Array.newInstance(clazz, size);}// 调用方式String[] strArray =createArray(String.class,5);

1.6.2 泛型与异常的问题

⚠️ 泛型不能用于异常类的定义和捕获,例如 class GenericException<T> extends Exception 会编译报错。
原因:异常的处理是运行时机制,而泛型是编译期机制,类型擦除后无法区分不同的泛型异常类型。
解决方案:如果需要传递泛型数据,可以将泛型类型作为异常类的成员变量。

publicclassGenericExceptionextendsException{privateObject data;public<T>GenericException(T data){this.data = data;}@SuppressWarnings("unchecked")public<T>TgetData(){return(T) data;}}

1.6.3 泛型与静态方法的问题

⚠️ 泛型类的静态方法不能使用类的泛型参数,因为静态方法属于类,在类加载时就已经确定,而泛型参数是在创建对象时指定的。
解决方案:将静态方法定义为泛型方法,使用自己的类型参数。

publicclassGenericClass<T>{// 错误写法:静态方法使用类的泛型参数// public static void method(T t) {}// 正确写法:使用泛型方法的类型参数publicstatic<E>voidmethod(E e){}}

1.7 实战案例:泛型集合工具类封装

1.7.1 需求分析

💡 封装一个通用的泛型集合工具类 GenericCollectionUtil,提供以下功能:

  1. 集合去重:去除泛型集合中的重复元素,保留插入顺序。
  2. 集合过滤:根据自定义规则过滤集合中的元素。
  3. 集合转换:将一种类型的集合转换为另一种类型的集合。

1.7.2 代码实现

importjava.util.ArrayList;importjava.util.LinkedHashSet;importjava.util.List;importjava.util.function.Predicate;importjava.util.function.Function;publicclassGenericCollectionUtil{/** * 泛型集合去重,保留插入顺序 * @param list 待去重的集合 * @param <T> 集合元素类型 * @return 去重后的集合 */publicstatic<T>List<T>distinct(List<T> list){if(list ==null|| list.isEmpty()){returnnewArrayList<>();}returnnewArrayList<>(newLinkedHashSet<>(list));}/** * 泛型集合过滤 * @param list 待过滤的集合 * @param predicate 过滤规则 * @param <T> 集合元素类型 * @return 过滤后的集合 */publicstatic<T>List<T>filter(List<T> list,Predicate<T> predicate){if(list ==null|| list.isEmpty()|| predicate ==null){returnnewArrayList<>();}List<T> result =newArrayList<>();for(T t : list){if(predicate.test(t)){ result.add(t);}}return result;}/** * 泛型集合转换 * @param list 源集合 * @param function 转换规则 * @param <T> 源元素类型 * @param <R> 目标元素类型 * @return 转换后的集合 */publicstatic<T,R>List<R>convert(List<T> list,Function<T,R> function){if(list ==null|| list.isEmpty()|| function ==null){returnnewArrayList<>();}List<R> result =newArrayList<>();for(T t : list){R r = function.apply(t); result.add(r);}return result;}// 测试方法publicstaticvoidmain(String[] args){// 测试去重List<Integer> numList =newArrayList<>(); numList.add(1); numList.add(2); numList.add(1); numList.add(3);List<Integer> distinctList =GenericCollectionUtil.distinct(numList);System.out.println("去重后的集合:"+ distinctList);// 测试过滤:过滤出偶数List<Integer> evenList =GenericCollectionUtil.filter(distinctList, num -> num %2==0);System.out.println("过滤后的偶数集合:"+ evenList);// 测试转换:将Integer转换为StringList<String> strList =GenericCollectionUtil.convert(evenList,String::valueOf);System.out.println("转换后的字符串集合:"+ strList);}}

输出结果

去重后的集合:[1, 2, 3] 过滤后的偶数集合:[2] 转换后的字符串集合:[2] 

1.7.3 案例总结

✅ 这个泛型工具类充分利用了泛型的特性,实现了对任意类型集合的通用操作。通过结合函数式接口(PredicateFunction),进一步提升了代码的灵活性和复用性。在实际开发中,这样的工具类可以大幅减少重复代码,提升开发效率。

1.8 本章总结

  1. 泛型的核心是类型参数化,它在编译期保证类型安全,运行时会发生类型擦除。
  2. 泛型的三种使用方式:泛型类、泛型接口、泛型方法,其中泛型方法的灵活性最高。
  3. 泛型的上下界限定可以限制类型参数的范围,提升代码的安全性。
  4. 通配符分为无界通配符(?)、上界通配符(? extends T)和下界通配符(? super T),遵循 PECS 原则使用。
  5. 泛型的常见问题包括泛型数组创建、泛型与异常、泛型与静态方法,需要通过特定方案解决。
  6. 泛型可以大幅提升代码的复用性和安全性,是 JAVA 进阶开发的必备技能。

Read more

放弃无效编码!AI+SDD 重构复杂业务研发范式,新手也能落地

放弃无效编码!AI+SDD 重构复杂业务研发范式,新手也能落地

在当前复杂业务系统研发中,我们常陷入诸多困境:需求反复变更导致开发返工,AI辅助编程易出现幻觉生成无效代码,多人协作时重复开发浪费精力,上线后频繁出现回归bug,文档与代码脱节成为“无效资产”。这些问题的核心,是缺乏一套统一可落地的研发范式,让需求、设计、开发、测试全流程形成闭环,而规格驱动开发(SDD,Spec-Driven Development),正是解决这一痛点的关键。 很多开发者对SDD的认知停留在“先写文档再写代码”的表面,甚至觉得它是“额外负担”,尤其在工期紧张的复杂项目中,更倾向于跳过规格设计直接编码。但事实上,SDD并非传统意义上的“文档绑架”,而是结合AI时代研发特点,形成的一套高效可落地的工程化方法。 本文结合OpenSpec这一主流SDD工具,从实操层面拆解SDD在复杂业务系统中的落地全流程,解答工具使用、流程设计、痛点解决等关键问题,帮助每一位开发者真正用好SDD,提升复杂系统研发效率与质量。 核心概念明确 SDD中的Spec(Specification,规格),本质是对业务需求、技术设计、实现细节的标准化描述,是整个研发流程的“唯一真理来源”。与传统

By Ne0inhk
把废弃的腾讯云服务器改为 Openclaw 仅需一句话!!!(附带免费白嫖AI模型)

把废弃的腾讯云服务器改为 Openclaw 仅需一句话!!!(附带免费白嫖AI模型)

大家好,我是热爱探索AI前沿技术的LucianaiB。 前面我尝试了,感兴趣的可以才是部署一下试试 1.在 Windows 上部署 Openclaw:https://mp.weixin.qq.com/s/iF3ED1e649kkmdR26Y1xiw 2.把 Openclaw 接入到 Moltbook:https://mp.weixin.qq.com/s/QUrB50iwRGGdkl1LO-Tl8Q 相信很多技术爱好者都有这样的经历:趁着双十一或者大促,脑子一热买了一台腾讯云或者阿里云的服务器。买的时候雄心勃勃,想着要搭建博客、跑脚本、做图床。结果呢?大概率是跑了几个自动化签到脚本后,它就静静地躺在控制台里“吃灰”,每个月白白扣费。 但是在自己的电脑运行 Openclaw 无法做到24小时的在运行,于是我就想到了我有一个好久不用的腾讯云服务器,之前购买主要是跑一些自动化签到脚本,并没有实际做什么具体工作。于是我就想到把废弃的腾讯云服务器改为 Openclaw 的24小时的服务器。 于是,

By Ne0inhk
云平台DeepSeek满血版:引领AI推理革新,开启智慧新时代

云平台DeepSeek满血版:引领AI推理革新,开启智慧新时代

引言:人工智能的未来——云平台的卓越突破 在当今科技飞速发展的时代,人工智能(AI)技术正深刻地改变着我们生活与工作方式的方方面面。作为AI领域的创新者与领航者,云平台始终走在技术前沿,凭借无穷的热情与智慧,致力于发掘AI的无限潜能,努力为全球用户描绘更加智能、高效、便捷的未来。 向阿里、腾讯等多家云平台也紧跟潮流推出最新力作——DeepSeek满血版。将为用户带来前所未有的体验革新。DeepSeek满血版的问世标志着云平台在AI技术征途上的一个新里程碑,推动行业迈向更加辉煌的未来。为了让更多的用户亲身感受这一革命性技术成果,云平台推出了免费Tokens的特权活动,让每一位用户都能够充分体验DeepSeek满血版的强大功能,并一同见证AI推理技术的突破性进展。 推荐阅读:Deepseek R1模型本地化部署+API接口调用详细教程:释放AI生产力 技术创新与核心突破:DeepSeek满血版的飞跃 DeepSeek满血版是云平台在AI推理领域的重要突破,它是首个完全通过强化学习训练的大型语言模型。与传统的监督微调方式不同,DeepSeek满血版仅凭奖励信号便开发出了出色的推理

By Ne0inhk
商贸赛道“智选优品”—基于大数据与AI驱动的跨境电商平台项目参考逐字稿

商贸赛道“智选优品”—基于大数据与AI驱动的跨境电商平台项目参考逐字稿

商贸赛道“智选优品”—基于大数据与AI驱动的跨境电商平台项目参考逐字稿 文章目录 * 商贸赛道“智选优品”—基于大数据与AI驱动的跨境电商平台项目参考逐字稿 * 开场与团队介绍 (3分钟) * 第一部分:项目背景与政策支持 (8分钟) * 第二部分:项目目标与预期成果 (5分钟) * 第三部分:技能要点展示 (15分钟) * 第四部分:项目体系与人员分工 (8分钟) * 第五部分:创新点与独特之处 (10分钟) * 第六部分:情景演示与设备调试 (8分钟) * 第七部分:总结与致谢 (3分钟) 近期我们团队收到很多粉丝的后台留言请求,随着每年一届的“全国职业院校技能大赛”(以下称为国赛)升级为“世界职业院校技能大赛”(以下称为世校赛),那么由原先的围绕技术厂商的服务器端进行开展技能模块转变为了:三自主即自主确定参赛项目名称,自主设计参赛项目内容,自主选择参赛设备。赛制进行了一次重大级别的改革,很多参赛院校包括指导老师们对这种新模式的流程不熟悉,于是在后台给我们团队留言,希望我们能对新赛制的各个赛道出一些可以参考的资料,我们

By Ne0inhk