《JavaSE》---21.<简单认识Java的集合框架包装类泛型>

目录

前言

一、什么是集合框架

1.1类和接口总览

 二、集合框架的重要性

2.1 开发中的使用

2.2 笔试及面试题

三、背后所涉及的数据结构

3.1 什么是数据结构

3.2 容器背后对应的数据结构

四、包装类

4.1 基本数据类型和对应的包装类

4.2 装箱和拆箱

1.最初的写法

2.Integer valueOf(int i)源码 

3.自动装箱和自动拆箱 (简化写法)

 【面试题】

五、泛型

5.1什么是泛型

5.2泛型的引入

5.3泛型的语法

示例代码 

5.4泛型类的使用

1.语法

 示例

 类型推导(Type Inference)

泛型小结:

六、裸类型(Raw Type) (了解)

七、泛型如何编译的

7.1 擦除机制

7.2 为什么不能实例化泛型类型数组

正确的方式:【了解即可】

八、泛型的上界

8.1 语法

九、泛型方法

9.1 定义语法


前言

      大家好,本人是普通一本的在校大学生一枚,目前在学习java。之前也学了一段时间,但是没有发布博客。时间过的真的很快。我会利用好这个暑假,来复习之前学过的内容,并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区进行讨论!!!

      喜欢我文章的兄弟姐妹们可以点赞,收藏和评论我的文章。喜欢我的兄弟姐妹们以及也想复习一遍java知识的兄弟姐妹们可以关注我呦,我会持续更新滴,
     望支持!!!!!!一起加油呀!!!!

语言只是工具,不能决定你好不好找工作,决定你好不好找工作的是你的能力!!!!!

学历本科及以上就够用了!!!!!!!!!!!!!!!!!!!!!!!!!!!!


本篇博客主要讲解Java基础语法中的

集合框架、什么是集合框架、集合框架的重要性、集合框架背后所涉及到的数据结构。

包装类、装箱和拆箱、【面试题】

泛型、裸类型、泛型如何编译的、泛型如何编译的

一、什么是集合框架

        Java 集合框架 Java Collection Framework ,又被称为容器 container ,是定义在 java.util 包下的一组接口 interfaces 和其实现类 classes

        其主要表现为将多个元素 element 置于一个单元中,用于对这些元素进行快速、便捷的存储 store 、检索 retrieve 、 管理 manipulate ,即平时我们俗称的增删查改 CRUD

1.1类和接口总览

每一个集合类,描述和组织数据的方式是不一样的。

我们分析这张图。

1.类与接口之间的关系

左边的部分全部是继承 Iterable 接口的。

接口和接口之间的关系用extends

类和接口之间是实现关系。

类与类也是继承关系

2.接口分别是什么、

        Iterable(是迭代器)、Collection(集合类)。

        实现 List 接口的有四个类。Vector(用得少了)、Stack(栈(也相对用得少  ))、ArrayList(顺序表)、LinkedList(链表,且是双向链表)

        实现 Queue(队列) 接口的有一个类。LinkedList(背后是链表,可以当做普通队列使用)PriorityQueue(优先级队列)

        实现 Set 接口的类有 TreeSet(底层搜索树(红黑树))、HashSet(底层哈希表k模型(里面的元素不能重复))

        实现Map接口的类有 HashMap(底层哈希表k-v模型)、TreeMap(底层也是红黑树)

我们可以通过迭代器来遍历集合、在集合中也可以用到对象比较

我们操作数组的工具类叫Arrays。

操作集合的工具类叫Collections

 二、集合框架的重要性

2.1 开发中的使用

  • 使用成熟的集合框架,有助于我们便捷、快速的写出高效、稳定的代码
  • 学习背后的数据结构知识,有助于我们理解各个集合的优缺点及使用场景

2.2 笔试及面试题

有许多是关于集合框架的。

三、背后所涉及的数据结构

3.1 什么是数据结构

数据结构(Data Structure)

计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。

3.2 容器背后对应的数据结构

该阶段,我们主要讲解以下容器,每个容器其实都是对某种特定数据结构的封装,大概了解一下,后序会在数据结构部分给大家详细讲解并模拟实现:

1. Collection:是一个接口,包含了大部分容器常用的一些方法

2. List:是一个接口,规范了ArrayList 和 LinkedList中要实现的方法

  • ArrayList:实现了List接口,底层为动态类型顺序表
  • LinkedList:实现了List接口,底层为双向链表

3. Stack:底层是栈,栈是一种特殊的顺序表

4. Queue:底层是队列,队列是一种特殊的顺序表

5. Deque:是一个接口

6. Set:集合,是一个接口,里面放置的是K模型

  • HashSet:底层为哈希表,查询的时间复杂度为O(1)
  • TreeSet:底层为红黑树,查询的时间复杂度为O(log2N ),关于key有序的

7. Map:映射,里面存储的是K-V模型的键值对

  • HashMap:底层为哈希表,查询时间复杂度为O(1)
  • TreeMap:底层为红黑树,查询的时间复杂度为O(log2N),关于key有序

四、包装类

在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了 一个包装类型。

4.1 基本数据类型和对应的包装类

除了 Integer 和 Character, 其余基本类型的包装类都是首字母大写。

4.2 装箱和拆箱

1.最初的写法

装箱:把一个基本数据类型转换为包装类型的过程。

拆箱:把一个包装类型转换为基本数据类型的过程。

int a = 10;// 装箱操作,新建一个 Integer 类型对象,将 i 的值放入对象的某个属性中
Integer b = Integer.valueOf(i);
Integer c = new Integer(i);// 拆箱操作,将 Integer 对象中的值取出,放到一个基本数据类型中
int d = b.intValue();

2.Integer valueOf(int i)源码 

3.自动装箱和自动拆箱 (简化写法)

为了减少开发者的负担,java 提供了自动机制。

自动装箱和自动拆箱

int a = 10;Integer b = a; // 自动装箱
Integer c = (Integer)a; // 自动装箱int j = b; // 自动拆箱
int k = (int)b; // 自动拆箱

 【面试题】

下列代码输出什么,为什么?

public static void main(String[] args) {Integer a = 127;Integer b = 127;Integer c = 128;Integer d = 128;System.out.println(a == b);System.out.println(c == d);
}

答:输出

true
false

 我们看Integer valueOf(int i)源码

IntegerCache.low=(-128)

IntegerCache.high=127

因此如果传入的数据为【-128,127】之间,就直接在这个数组中拿对应的数据了

如果不是【-128,127】之间则新建了一个Integer对象。因此一个是true一个是false

五、泛型

5.1什么是泛型

泛型是在JDK1.5引入的新的语法,通俗讲,

泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的 代码,这种刻板的限制对代码的束缚就会很大。

----- 来源《Java编程思想》对泛型的介绍。

5.2泛型的引入

class MyArray {public Object[] array = new Object[10];public Object getPos(int pos) {return this.array[pos];}public void setVal(int pos,Object val) {this.array[pos] = val;}
}public class TestDemo {public static void main(String[] args) {MyArray myArray = new MyArray();myArray.setVal(0,10);myArray.setVal(1,"hello");//字符串也可以存放String ret = myArray.getPos(1);//编译报错System.out.println(ret);}
}

我们发现,实现上述代码后

1. 任何类型数据都可以存放

2. 1号下标本身就是字符串,但是确编译报错。因为大范围转小范围(父类给子类)必须进行强制类型转换 。

泛型的主要目的:

就是指定当前的容器,持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。

5.3泛型的语法

class 泛型类名称<类型形参列表> {// 这里可以使用类型参数
}eg:
class ClassName<T1, T2, ..., Tn> {  
}class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {// 这里可以使用类型参数
}class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {// 可以只使用部分类型参数
}

示例代码 

class MyArray<T> {public Object[] array = new Object[10];//1public T getPos(int pos) {return (T)array[pos];}public void setVal(int pos,T val) {this.array[pos] = val;}
}
public class TestDemo {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<>();//2myArray.setVal(0,10);myArray.setVal(1,12);int ret = myArray.getPos(1);//3System.out.println(ret);myArray.setVal(2,"bit");//4}
}

在类名的后面写一个<T>代表当前类是一个泛型类。 

此时我们在新建对象的时候

MyArray<Integer> myArray = new MyArray<>();

在类型名后面加上<>并在其中指定我们要放入的数据。我们就会发现此时这个数组只能放入我们指定的类型,不能放入别的类型了。比如上面就只能放入整型。就不能放入字符串了。

<>当中只能是引用类型,不能是基本类型

1. 类名后的 <T> 代表占位符,表示当前类是一个泛型类

了解: 【规范】类型形参一般使用一个大写字母表示,常用的名称有:

  • E 表示 Element
  • K 表示 Key
  • V 表示 Value
  • N 表示 Number
  • T 表示 Type
  • S, U, V 等等 - 第二、第三、第四个类型

2. 注释1处,不能new泛型类型的数组  

T[] ts = new T[5];//是不对的

3. 注释2处,类型后加入 指定当前类型

4. 注释3处,不需要进行强制类型转换

5. 注释4处,代码编译报错,此时因为在注释2处指定类当前的类型,此时在注释4处,编译器会在存放元素的时候帮助我们进行类型检查。


5.4泛型类的使用

1.语法

泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象

 示例

MyArray<Integer> list = new MyArray<Integer>();

注意:泛型只能接受类,所有的基本数据类型必须使用包装类! 

 类型推导(Type Inference)

MyArray<Integer> list = new MyArray<>(); // 可以推导出实例化需要的类型实参为 Integer

当编译器可以根据上下文推导出类型实参时,可以省略类型实参的填写

泛型小结:

1. 泛型是将数据类型参数化,进行传递

2. 使用 表示当前类是一个泛型类。

3. 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换


六、裸类型(Raw Type) (了解)

裸类型是一个泛型类但没有带着类型实参,例如 MyArrayList 就是一个裸类型

裸类型实际上就是没有了类型实参的限制,又什么类型都能传入。

MyArray list = new MyArray();

注意: 我们不要自己去使用裸类型,裸类型是为了兼容老版本的 API 保留的机制 下面的类型擦除部分,我们也会讲到编译器是如何使用裸类型的。 

七、泛型如何编译的

7.1 擦除机制

那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他 还是需要一定的时间打磨。

通过命令:javap -c 查看字节码文件,所有的T都是Object。

 在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制

Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

也就是

编译的过程中将所有的T擦除为Object。

运行的时候没有泛型这样的概念。

提出问题:

1、那为什么,T[] ts = new T[5]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new Object[5]吗?

java中,数组是一个非常特殊的存在,在JVM中,相当于一种新的数据类型了

在 Java 中,数组是协变的(covariant),这意味着如果 Sub 是 Super 的子类型,

那么Sub[ ]也是Super[ ]的子类型。这种设计导致了类型不安全的问题。

例如:

Object[] objArray = new String[10];
objArray[0] = 10; // 运行时错误:ArrayStoreException

为了避免类似的问题,Java 不允许直接创建泛型数组,因为在运行时无法检查泛型类型的具体类型。例如:

T[] ts = new T[5]; // 编译错误

详细解释

  1. 类型擦除:在编译时,泛型类型被擦除,意味着 T 被替换为其上限(默认为 Object),但数组的运行时类型信息仍然需要保留。

  2. 数组的运行时类型信息:Java 数组会在运行时保留类型信息,例如 new String[10] 创建一个 String 类型的数组,而 new Object[10] 创建一个 Object 类型的数组。这种类型信息在运行时是固定的。

  3. 类型安全:允许 T[] 会导致类型不安全问题,因为 T 在运行时是不可知的。例如,如果允许 T[] ts = new T[5];,在运行时类型信息只能是 Object[],但这与泛型类型检查机制不一致。

不能直接创建泛型数组是因为类型擦除和数组的协变性设计导致的类型安全问题。使用集合类或通过反射创建数组是常见的解决方案。尽管这些方法可以绕过编译限制,但需要注意它们可能带来的运行时问题。

2、类型擦除,一定是把T变成Object吗?

总结

  • 未指定上界的泛型类型:类型擦除后,泛型类型 T 被替换为 Object
  • 指定了上界的泛型类型:类型擦除后,泛型类型 T 被替换为其上界。
  • 泛型方法:类型擦除后的泛型方法会根据泛型参数的上界进行替换。

类型擦除确保了泛型在运行时不产生新的类型,但依然提供了编译时的类型检查和安全性。通过理解类型擦除的机制,可以更好地掌握泛型的使用和限制。

7.2 为什么不能实例化泛型类型数组

class MyArray<T> {public T[] array = (T[])new Object[10];public T getPos(int pos) {return this.array[pos];}public void setVal(int pos,T val) {this.array[pos] = val;}public T[] getArray() {return array;}
}public static void main(String[] args) {MyArray<Integer> myArray1 = new MyArray<>();Integer[] strings = myArray1.getArray();}/*
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;at TestDemo.main(TestDemo.java:31)
*/

数组不能整体进行强制类型转换

除非实例化一个具体类型的数组

原因:替换后的方法为:将Object[]分配给Integer[]引用,程序报错。 

public Object[] getArray() {return array;
}

通俗讲就是:返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,运行的时候,直接转给Integer类型的数组,编译器认为是不安全的。 

正确的方式:【了解即可】

class MyArray<T> {public T[] array;public MyArray() {}/*** 通过反射创建,指定类型的数组* @param clazz* @param capacity*/public MyArray(Class<T> clazz, int capacity) {array = (T[])Array.newInstance(clazz, capacity);}public T getPos(int pos) {return this.array[pos];}public void setVal(int pos,T val) {this.array[pos] = val;}public T[] getArray() {return array;}
}
public static void main(String[] args) {MyArray<Integer> myArray1 = new MyArray<>(Integer.class,10);Integer[] integers = myArray1.getArray();}

八、泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。

8.1 语法

简单示例

class 泛型类名称<类型形参 extends 类型边界> {...
}public class MyArray<E extends Number> {...
}

只接受 Number 的子类型作为 E 的类型实参

MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型
error: type argument String is not within bounds of type-variable EMyArrayList<String> l2;^
where E is a type-variable:E extends Number declared in class MyArrayList

了解: 没有指定类型边界 E,可以视为 E extends Object 


复杂示例 

public class MyArray<E extends Comparable<E>> {...
}

 E必须是实现了Comparable接口的

写一个方法,求指定类型数组的最大值

class Alg<T>{public T findMax(T[] array){T max = array[0];for(int i = 1;i < array.length; i++){if(array[i] > max){max = array[i];}}return max;}    
}

以为这样就对了吗? 

我们发现不能比大小,会报错,那是为什么呢。因为Object类中没有比较的方法。

我们必须实现 comparable 接口来进行比较 

这样就可以了 

这就类似于上面所提到的

public class MyArray<E extends Number> {...
}

九、泛型方法

9.1 定义语法

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }

 示例

public class Util {//静态的泛型方法 需要在static后用<>声明泛型类型参数public static <E> void swap(E[] array, int i, int j) {E t = array[i];array[i] = array[j];array[j] = t;    }
}

使用示例-可以类型推导 

Integer[] a = { ... };
swap(a, 0, 9);String[] b = { ... };
swap(b, 0, 9);

使用示例-不使用类型推导

Integer[] a = { ... };
Util.<Integer>swap(a, 0, 9);String[] b = { ... };
Util.<String>swap(b, 0, 9);

 写一个方法,求指定类型数组的最大值(泛型方法示例)

也可以是设置成一个静态方法,不实例化对象,用类名调用这个方法 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/48916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

org.springframework.context.ApplicationContext发送消息

1、创建消息的实体类 package com.demo;/*** 监听的实体类**/ public class EventMessage {private String name;public EventMessage(String name) {this.name name;}public String getName() {return name;}public void setName(String name) {this.name name;} }2、创建消…

【Linux】如何使用docker快速部署Stirling-PDF并实现远程处理本地文档

文章目录 前言1. 安装Docker2. 本地安装部署StirlingPDF3. Stirling-PDF功能介绍4. 安装cpolar内网穿透5. 固定Stirling-PDF公网地址 前言 本篇文章我们将在Linux上使用Docker在本地部署一个开源的PDF工具——Stirling PDF&#xff0c;并且结合cpolar的内网穿透实现公网随时随…

Java 集合框架:Java 中的双端队列 ArrayDeque 的实现

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 019 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

共享模型之无锁

一、问题提出 1.1 需求描述 有如下的需求&#xff0c;需要保证 account.withdraw() 取款方法的线程安全&#xff0c;代码如下&#xff1a; interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程&#xf…

GraphPad prism处理cck-8获得ic50

C组为空白对照组&#xff0c;a组为dmso对照组&#xff0c;b组为细胞加药组&#xff0c;八个梯度的药物浓度 一、数据转化 首先&#xff0c;打开软件&#xff0c;选项中选择x的第一项&#xff0c;y的第二项&#xff0c;单一药物浓度设定了几个孔就选几 把自己的药物浓度直接复制…

ubuntu22安装拼音输入法

专栏总目录 一、安装命令&#xff1a; sudo apt update sudo apt install fcitx sudo apt install fcitx-pinyin 二、切换输入法

游戏常用运行库安装包 Game Runtime Libraries Package

游戏常用运行库安装包&#xff08;Game Runtime Libraries Package&#xff09;是一个整合了多种游戏所需运行库的安装程序&#xff0c;旨在帮助玩家和开发者解决游戏无法正常运行的问题。该安装包支持从Windows XP到Windows 11的系统&#xff0c;并且具备自动检测系统并推荐合…

代码随想录训练第二十七天|LeetCode56.合并区间、LeetCode738.单调递增的数字、LeetCode968.监控二叉树

文章目录 56.合并区间思路 738.单调递增的数字思路 968.监控二叉树思路确定遍历顺序如何隔两个节点放一个摄像头 56.合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一…

Step-DPO 论文——数学大语言模型理解

论文题目&#xff1a;STEP-DPO: STEP-WISE PREFERENCE OPTIMIZATION FOR LONG-CHAIN REASONING OF LLMS 翻译为中文就是&#xff1a;“LLMs长链推理的逐步偏好优化” 论文由港中文贾佳亚团队推出&#xff0c;基于推理步骤的大模型优化策略&#xff0c;能够像老师教学生一样优…

String 和StringBuilder字符串操作快慢的举例比较

System.currentTimeMillis(); //当前时间与1970年1月1日午夜UTC之间的毫秒差。public class HelloWorld {public static void main(String[] args) {String s1 "";StringBuilder s2 new StringBuilder("");long time System.currentTimeMillis();long s…

git命令学习分享

分布式版本控制系统&#xff0c;本地仓库和远程仓库相互独立。 使用repository仓库进行控制&#xff0c;可以对里面的文件进行跟踪&#xff0c;复原。 git config --global --list&#xff1a;查看git配置列表 cd ** &#xff1a;进入** cd .. &#xff1a;退回上一级 echo…

AI Agent项目探索与实践记录

AI Agent项目探索与实践记录 1. 概述2. 总体结构2.1 记忆模块2.2 模型服务模块2.2.1 LLM服务2.2.2 retrieval服务2.2.3 rerank服务 2.3 Agent系统2.3.1 Planner2.3.2 Code/SQL Generator2.3.3 Code Executor2.3.4 Responser2.3.5 Round Compressor2.3.6 New Turn Discriminator…

基于Llama Index构建RAG应用(Datawhale AI 夏令营)

前言 Hello&#xff0c;大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者&#xff0c;本文参与活动是2024 DataWhale AI夏令营&#xff1b;&#x1f632; 在本文中作者将通过&#xff1a; Gradio、Streamlit和LlamaIndex介绍 LlamaIndex 构…

全局 loading

好久不见&#xff01; 做项目中一直想用一个统一的 loading 状态控制全部的接口加载&#xff0c;但是一直不知道怎么处理&#xff0c;最近脑子突然灵光了一下想到了一个办法。 首先设置一个全局的 loading 状态&#xff0c;优先想到的就是 Pinia 然后因为页面会有很多接口会…

数据结构——栈(链式结构)

一、栈的链式存储结构 如果一个栈存在频繁进栈和出栈操作&#xff0c;可以考虑链式结构。 栈的链式存储结构是指使用链表来实现栈这种数据结构。在链式存储结构中&#xff0c;栈的每个元素被封装成一个节点&#xff0c;节点之间通过指针相连&#xff0c;形成一个链表。栈顶元…

Linux下开放指定端口

比如需要开放82端口&#xff1a; #查询是否开通 firewall-cmd --query-port82/tcp#开放端口82 firewall-cmd --zonepublic --add-port82/tcp --permanent#重新加载防火墙 firewall-cmd --reload

java学习--代码块

package com.block.test01; class Main{public static void main(String[] args) {Block block new Block("你好&#xff0c;李焕英");new Block("你好",12,24);} } public class Block {String name;int begin_time;int end_time; //如果在调用构造器时都…

SwiftUI 5.0(iOS 17)滚动视图的滚动目标行为(Target Behavior)解惑和实战

概览 在 SwiftUI 的开发过程中我们常说&#xff1a;“屏幕不够&#xff0c;滚动来凑”。可见滚动视图对于超长内容的呈现有着多么秉轴持钧的重要作用。 这不&#xff0c;从 SwiftUI 5.0&#xff08;iOS 17&#xff09;开始苹果又为滚动视图增加了全新的功能。但是官方的示例可…

Linux----Mplayer音视频库的移植

想要播放视频音乐就得移植相关库到板子上 Mplayer移植需要依赖以下源文件&#xff1a;(从官网获取或者网上) 1、zlib-1.2.3.tar.gz &#xff1a;通用的内存空间的压缩库。 2、libpng-1.2.57.tar.gz :png格式图片的压缩或解压库 3、Jpegsrc.v9b.tar.gz : jpeg格式图片的压…

数据结构day3

一、思维导图 二、顺序表实现学生管理系统 //头文件 #ifndef TEST_H #define TEST_H #define MAX_SIZE 100//定义学生类型 typedef struct {char name[20]; //姓名int age; //年龄double score; //分数 }datatype;//定义班级类型 typedef struct {datatype student[MAX…