Java语言基础(下)

news2025/1/11 20:43:30

toString() 、String.valueof()、(String)?

  • toString():Object 类的方法,可以将一个对象转换为字符串类型
  • String.valueOf():可以将一个对象基本数据类型转换为字符串类型
    如果参数是对象,则调用 toString() 方法;
    如果是基本数据类型,则直接将其转换为字符串类型
  • (String)强制类型转换,需要使用 instanceof 做类型检查,不建议使用

hashCode() 方法?

  • HashCode() 是 Object 类方法,用于返回对象的哈希码,这个值通常用于快速查找对象

hashCode 和 equals 方法判断两个对象是否相等?

  • 两个对象的 hashCode 值不相等,对象不相等
  • 两个对象的 hashCode 值相等,不一定相等(哈希碰撞)
  • 两个对象的 hashCode 值相等,并且 equals() 方法也返回 true,说明这两个对象相等

为什么重写 equals 时必须重写 hashCode 方法?

  • 判断对象是否存在的时候,会先检查 hashCode 是否相等
  • 如果不相等,会直接认为对象不相等,不会再调用 equals 进行比较

String、StringBuffer、StringBuilder?

  • String 类型是不可变的,每次对 String 类型操作时,都会生成新的 String 对象
  • StringBuffer 类型是可变的字符串,表示一个字符序列。是线程安全的,通过使用同步来保证多个线程之间的正确性
  • StringBuilder 类型也是可变的字符串,和 StringBuffer 本质相同,但是是非线程安全

String 为什么是不可变的?

  • String 被声明为 final,不可以被继承
  • 使用字符数组来保存字符串的值,该数组被声明为 final,并且不提供方法以修改字符数组的内容
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

String 不可变的好处?

  • 安全:String 不可变,所以可以用来当作常量使用
  • 线程安全:String 不可变,所以可以在多个线程中安全的共享
  • 编译器优化:编译器可以在编译时优化代码,例如使用常量折叠

String 的特性?

  • 不可变:一旦字符串被创建,它的值就不能更改
  • 比较:可以使用 equals() 方法比较字符串的,也可以使用==运算符比较字符串的引用
  • 字符串池:Java 创建了一个字符串池,用于存储字符串常量。当创建新字符串时,Java 会先检查字符串池中是否已经有该字符串,如果有,则返回该字符串的引用,否则创建新字符串并将其添加到字符串池中

字符串拼接用 + 还是 StringBuilder?

  • Java 中的 + 运算符经过重载后,实际上是通过 StringBuilder 调用 append() 方法实现的
    在循环内使用 + 进行字符串拼接时,由于编译器不会复用 StringBuilder 对象,会导致创建过多的 StringBuilder 对象
  • 应该直接使用 StringBuilder 对象的 append() 方法

String 类和 Object 类的 equals() 方法?

  • Object 的 equals() 方法:比较两个对象的引用是否相等
  • String 类的 equals() 方法:经过重写,比较的是两个字符串的内容是否相等

String s1 = new String(“abc”) 这句话创建了几个对象?

会创建两个对象,这句代码的执行顺序如下:

  • 常量池中创建一个字符串 “abc” 的对象
  • 堆内存中创建一个新的字符串对象,该对象与字符串中的对象具有相同的值 “abc”,将堆创建的对象引用赋值给 s1 变量

String s = new String(“a”) + new String(“b”) 创建了几个对象?

  • 对象 1:StringBuilder 对象,表示字符串拼接的缓冲区
  • 对象 2:new 的 String(“a”) 对象
  • 对象 3: 常量池对象 “a”
  • 对象 4:new 的 String(“b”) 对象
  • 对象 5: 常量池对象 “b”
  • 对象 6: 使用 StringBuilder 的 toString() 方法获取最终的拼接字符串,创建的新的 String 对象 “ab”

intern 方法的作用?

  • String 类的方法,作用是将字符串对象添加到字符串常量池,并返回常量池中该字符串对象的引用
// 在堆中创建字符串对象”Java“
// 将字符串对象”Java“的引用保存在字符串常量池中
String s1 = "Java";
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s2 = s1.intern();
// 会在堆中在单独创建一个字符串对象
String s3 = new String("Java");
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s4 = s3.intern();
// s1 和 s2 指向的是堆中的同一个对象
System.out.println(s1 == s2); // true
// s3 和 s4 指向的是堆中不同的对象
System.out.println(s3 == s4); // false
// s1 和 s4 指向的是堆中的同一个对象
System.out.println(s1 == s4); //true
//创建两个对象,堆空间一个new对象,字符串常量池一个字符串常量对象"a"
String s = new String("a"); //s指向堆空间的对象地址 
s.intern();  //调用此方法之前,字符串常量池已经有"a"
String s2 = "a";  //s2指向字符串常量池"a"
System.out.println(s == s2); //false
String s3 = new String("a") + new String("b"); //等价new String("ab"),但常量池没有"ab"
s3 = s3.intern(); //字符串常量池中创建一个指向堆对象的引用
String s4 = "ab"; //指向常量池中存在的"ab"
System.out.println(s3 == s4); //true

image-20230318092642647

String 类型做 + 运算时发生了什么?

  • String 类型在做 + 运算时,运行时会被转化为 StringBuilder 对象进行操作

在编译期间,Java 编译器会进行常量折叠,对字符串常量的拼接进行优化。但如果在运行时才能知道其确切值,就无法对其优化

String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing"; //常量池中的对象 编译器会进行常量折叠 优化成"String"
String str4 = str1 + str2; //在堆上创建新的StringBuilder对象
//String str4 = new StringBuilder().append(str1).append(str2).toString();
String str5 = "string"; //常量池中的对象
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false

Exception 和 Error 有什么区别?

  • Exception 类是程序在运行时可能出现的异常,比如说输入错误或者网络连接错误,可以通过程序进行处理
  • Error 类表示 Java 虚拟机本身的错误,比如内存溢出等,会终止程序的运行

Checked Exception 和 Unchecked Exception 有什么区别?

  • Checked Exception编译时异常,可以显式捕获,需要在代码中进行处理,否则会产生编译错误。常见的比如:IO 异常、 SQL 异常
  • Unchecked Exception运行时异常,不需要被显式的捕获,交给 JVM 处理
    常见的比如:NullPointerException(空指针异常) ArrayIndexOutOfBoundsException(数组越界错误)

Throwable 类常用方法有哪些?

  • getMessage():获取异常的信息
  • printStackTrace():在控制台打印异常信息
  • getCause():获取抛出当前异常的原因

try-catch-finally 如何使用?

  • try 中包含可能会抛出异常的语句;
  • catch 用来捕获并处理异常;
  • finally 中的代码块一定执行,用于关闭在 try-catch 中打开的资源

finally 中的代码一定会执行吗?

  • 不一定
  • 正常情况下,finally 块中的代码都会执行
  • 但是在程序遇到致命错误,程序立即退出的情况下,不会执行
  • 如果在 finally 执行之前,虚拟机被终止的话,finally 的代码也不会被执行

如何使用 try-with-resources 代替 try-catch-finally?

Java 引入新的 try-with-resources 语句,用于自动关闭程序中使用的资源,比如数据库连接,文件 I/O 流等,在 try 语句块结束后,自动关闭这些资源,不需要再写 catch 和 final 语句来释放资源

  • 需要实现 lang.AutoCloseable 或者 io.Closeable 接口来使用该新语句

什么是泛型?有什么用?

  • 泛型是 Java 的一个重要特性,用于实现参数化类型
  • 泛型的作用:
    • 使用泛型可以在编译时进行类型检测,减少运行时错误
    • 通过泛型参数指定传入的对象类型

泛型的使用方式有哪几种?

  • 泛型
  • 泛型接口
  • 泛型方法
  • 泛型类型通配符

什么是泛型擦除机制?为什么要擦除?

  • 泛型擦除机制是 Java 在编译的时候去除泛型类型的参数信息,将其转换成原始类型
  • 这样做的主要目的是为了保持 Java 代码的向后兼容性,和之前版本的 Java 代码兼容

什么是桥方法?

  • 桥方法是一种虚拟机技术,是用来处理泛型类型中的继承和多态问题的。
  • 编译器会在继承类或者实现接口中自动生成桥方法,保证编译后的字节码可以正确的执行。
  • 桥方法的作用是将一个泛型方法转换为一个桥接方法,其实现是调用原方法,并将参数进行强制类型转换,最终返回结果。

泛型有哪些限制?为什么?

  • 不能使用基本数据类型作为泛型类型参数:基本类型无法转换成 Object 类型
  • 不能创建泛型数组,数组的类型必须确定:泛型的类型参数在编译时需要进行擦除
  • 不能直接使用泛型类型实例化:因为泛型类型在运行时不能获取其具体类型。

什么是通配符?有什么作用?

  • 通配符是一种符号,代表任何类型
  • 通常和关键字 extends/super 结合使用,用来限制泛型类型的范围
<? extends Person> //限制类型为Person的子类
<? super Manager> //限制类型为Person的父类

通配符?和常用的泛型 T 有什么区别?

  • 通配符?是一个符号,可以代表任何类型,需要和关键字 extends/super 结合使用来限制范围
  • 泛型 T 是一个具体的类型参数,用于指定一个具体的数据类型

什么是无界通配符?

  • 没有指定通配符类型的上界,表示可以接受任何类型的对象
  • 通常有两种使用情况
    • 泛型类型参数只用于方法的参数传递,不需要方法的返回值
    • 泛型类型参数只用于类的成员变量,不进行任何操作

List<?>和 List 的区别?

  • List 是一个具体类型的泛型列表,需要在创建时指定元素类型,可以添加该类型的元素
  • List<?>是一个通配符泛型列表,表示未知类型,适合用于读取和遍历列表,但不能直接添加元素。
List<?> unknownList = new ArrayList<String>(); // 可以接受任何类型的列表

什么是上边界通配符?什么是下边界通配符?

  • 上边界通配符:使用 extends 关键字限定泛型类型的上界,表示该泛型必须是其子类
  • 下边界通配符:使用 super 关键字限定泛型类型的下界,表示该泛型必须是其父类。

反射?

  • Java 的反射是指在运行时动态的获取和操作的信息
  • 包括类的结构、方法和构造函数等
  • Java 提供反射 API,在编译时,通过名称获取类的信息

反射的应用场景了解么?

  • 实现注解和注解处理器
  • 动态代理
  • Java Bean 的动态操作
  • 调试工具
  • 配置文件解析
  • 单元测试
  • 序列化和反序列化
  • 依赖注入

谈谈反射机制的优缺点

  • 反射使代码更加灵活,使程序具有更高的动态性和可扩展性
  • 反射的灵活性降低了程序的性能,同时也增加了安全问题

获取 Class 对象的四种方式

  • 使用类名.class 方法
  • 使用对象的 getClass() 方法
  • 使用 Class.forName() 方法
  • 使用类加载器的 ClassLoader 的 loadClass() 方法

反射的一些基本操作

  • 获取 Class 对象:例如使用类名.class 方法获取类的 Class 对象
  • 获取类的属性:使用 Class 的 getDeclaredFields() 方法获取类的所有属性
  • 获取类的方法:使用 Class 的 getDeclaredMethods() 方法获取类的所有方法
  • 创建对象:使用 Class 的 newInstance() 方法根据类的信息动态创建对象
  • 调用方法:使用 Method 类的 invoke() 方法对方法进行调用
  • 设置属性:使用 Field 类的 set() 方法对属性进行设置

注解是什么?

  • 注解 (Annotation) 是一种元数据,可以用来为程序中的代码元素,如类、方法、属性等添加额外的信息和标识,并可以在运行时动态获取这些信息或者在编译时进行相应的处理。

常用的注解?

  • @Override:标识一个方法重写了父类的方法
  • @Deprecated:标识一个方法或类已经过时,不应该再使用
  • @SuppressWarnings:用于抑制编译器的警告信息
  • @Autowired:自动依赖注入对象
  • @Component:标识一个类为组件,可以被自动扫描和注册到容器中
  • @RestController:标识一个类为 RESTful 服务的控制器
  • @RequestMapping:标识一个方法对应的 URL 地址HTTP 请求方法
  • @Transactional:标识一个方法或类需要进行事务管理
  • @NotNull:标识一个方法或参数不能为空
  • Spring 框架中的@Service、@Repository、@Controller 等
  • JUnit 测试框架的@Test、@Before、@After 等。

注解的解析方法有哪几种?

  • 编译期直接扫描:在编译时,编译器会扫描源代码中的注解,并根据注解的定义执行相应的处理。
    例如,@Override 注解用于检查方法是否实际重写了父类的方法。编译器会在编译时检查并报告相关错误。
  • 运行期通过反射处理:在运行时,通过反射机制可以获取类、方法、字段等的注解信息,并根据注解的定义执行相应的逻辑。这种方式适用于框架和库,在程序运行时动态地根据注解来实现特定的功能。

SPI 和 API 有什么区别?

  • API应用程序接口,是用于访问功能的标准接口,是一种调用方式,用于集成不同模块的功能。
  • SPI软件提供接口, 是一种软件设计模式,用于实现插件和扩展,提供了一种实现方式,让应用程序可以动态地选择和加载不同的实现

ServiceLoader 具体实现?

  • ServiceLoader 是 Java 提供的一种用于实现 SPI 的机制。允许定义一组接口,并允许不同的服务提供者在运行时注册和被加载
  • 具体实现流程:
  1. 服务提供者的注册: 服务提供者将其实现类在规定的路径下创建配置文件,写入实现类的全限定名
  2. 加载服务: 当调用 load 方法时,ServiceLoader 会根据传入的接口类获取其类加载器,并加载对应的配置文件,读取实现类的全限定名
  3. 实例化类:ServiceLoader 使用反射机制实例化每个实现类,然后将它们加入到内部的缓存中
  4. 提供迭代器:通过调用 iterator() 方法,获取已加载的实现类的迭代器,从而遍历并使用这些实现类

ServiceLoader 的工作原理主要包括服务提供者的注册、加载服务、实例化类和提供迭代器。这个机制使得可以在应用程序中不修改代码的情况下,通过添加或修改配置文件来加载新的服务实现,从而实现了一种插件化和可扩展的设计

示例配置文件(META-INF/services/com.example.MyService):

com.example.impl.MyServiceImpl
com.example.impl.AnotherServiceImpl

示例代码:

import java.util.ServiceLoader;

public class ServiceLoaderExample {
    public static void main(String[] args) {
        ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
        for (MyService service : serviceLoader) {
            service.doSomething();
        }}}

其中,MyService 是接口,MyServiceImplAnotherServiceImpl 是实现类。

什么是序列化和反序列化?

  • 序列化和反序列化是将对象转换为二进制数据流和将二进制数据流还原为对象的过程
  • 可以用于网络传输、缓存系统等,对应于四层模型的应用层

常见序列化协议有哪些?

  • JDK 自带的序列化方式
  • JSON
  • XML

JDK 自带的序列化方式

  • JDK 自带的序列化,只需实现 java.io.Serializable 接口即可

serialVersionUID 有什么作用?

  • serialVersionUID 是序列化号,用于在 Java 序列化和反序列化中比较两个对象的版本是否一致
  • 如果序列化号不一致会抛出 InvalidClassExpection 异常

serialVersionUID 不是被 static 变量修饰了吗?为什么还会被“序列化”?

  • serialVersionUID 虽然是一个 static 变量,但不会被序列化保存在对象中,只是一个版本号
  • 在反序列化时,会读取对象的的 serialVersionUID 值,并与本地的做比较,如果不一致,则会抛出异常。

如果有些字段不想进行序列化怎么办?

  • 使用 transient 关键字
  • 使用 transient 标记的变量其值会在序列化和反序列化过程中被忽略掉,因此在反序列化之后需要重新初始化或者赋值,否则其值为 null
  • transient 只能修饰变量

Java 的 Unsafe 类?

  • JDK 提供的不安全的工具类,提供了可以直接操作内存和执行底层操作的方法
  • Java 已经不再支持 Unsafe 类的使用

Java 语法糖?

  • 提供给开发人员便于开发的一种语法
  • 在编译时会在编译时被翻译成底层的底层语言

JDK1.8 都有哪些新特性?

  • 接口默认方法静态方法
  • Lambda 表达式:本质上是一段匿名内部类,简化代码
  • Stream API:用函数式编程方式在集合类进行复杂操作
  • Optional 类:用来解决空指针异常的问题

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

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

相关文章

sql注入:延时注入python万能脚本

sql注入:延时注入python脚本 import requests import binascii def judgment_delay(complete_url):headers{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"}try:requ…

Zynq-Linux移植学习笔记之65- 国产ZYNQ在linux下usleep时间精度不准问题解决

1、背景介绍 采用复旦微的ZYNQ&#xff0c;跑linux操作系统&#xff0c;在应用程序中使用usleep进行延时时&#xff0c;发现存在10ms以下采用usleep试验都为10ms的情况 2、解决办法 使能设备树中的PS TTC设备&#xff0c;默认不是打开的 timere0024000 {compatible "s…

rangenet++运行 bonnetal训练 点云标签 记录

准备原始点云数据&#xff0c;为bin格式 安装point_labeler工具&#xff0c;根据readme步骤安装&#xff0c;并设定好格式进行标签 修改bonnetal的配准 安装semantic-kitti&#xff0c;查看点云标签content&#xff0c;并修改bonnetal上的设置 ./content.py --dataset ~/da…

AI 编程工具带来了什么

注册 地址为 https://web.devchat.ai/signup/ AI 编程工具带来了什么 深度学习框架&#xff1a; 用于实现神经网络和深度学习模型的框架&#xff0c;如TensorFlow、PyTorch、Keras等。这些工具提供了高级的API和工具&#xff0c;使得实现和训练神经网络变得相对容易。 自然语…

基础代码介绍

#include<pic.h> //包含单片机内部资源字义 __CONFIG(0xFF32); //4M __CONFIG(0XFF32)void main() {TRISD0;//D口输出PORTD0b00000001;//第一个LED灯点亮&#xff0c;高电平1while(1);// } /* void main() { TRISD0; PORTD0x01 while(1); }*/ 点亮一颗LEDD灯&#xff1a…

OpenText Exceed TurboX (ETX) —— 对图形密集型应用程序进行高性能远程访问

OpenText Exceed TurboX (ETX) —— 对图形密集型应用程序进行高性能远程访问 OpenText Exceed TurboX使团队(无论位于何处)能够对图形密集型应用程序进行高性能远程访问&#xff0c;提高生产力并减少 IT 支出&#xff0c;以确保快速投资回报。 亮点&#xff1a; 降低IT支出…

【python高级】asyncio 并发编程

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍python高级篇的事件循环&#xff0c;task取消和协程嵌套、call_soon、call_later、call_at、 call_soon_threadsafe、asyncio模拟http请求、asyncio同步和通信、aiohttp实现高并发实践。 后续会继续分享其他重要知…

完整版付费进群带定位源码

看到别人发那些不是挂羊头卖狗肉&#xff0c;要么就是发的缺少文件引流的。恶心的一P 这源码是我付费花钱买的分享给大家&#xff0c;功能完整。 搭建教程 nginx1.2 php5.6--7.2均可 最好是7.2 第一步上传文件程序到网站根目录解压 第二步导入数据库&#xff08;shujuk…

滤波器实现

滤波器实现 卷积和滤波 滤波的数学基础是卷积。对于有限冲激响应 (FIR) 滤波器&#xff0c;滤波运算的输出 y(k) 是输入信号 x(k) 与冲激响应 h(k) 的卷积&#xff1a; y(k)∞∑l−∞h(l) x(k−l). 如果输入信号也是有限长度的&#xff0c;您可以使用 MATLAB conv 函数来执行…

java项目之高校奖学金管理系统(ssm框架+源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的高校奖学金管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 管理员&#xff1a;首…

es6过滤对象里面指定的不要的值filter过滤

//过滤出需要的值this.dataItemTypeSelectOption response.data.filter(ele > ele.dictValue tree||ele.dictValue float4);//过滤不需要的值this.dataItemTypeSelectOption response.data.filter((item) > {return item.dictValue ! "float4"&&it…

Hbase 迁移小结:从实践中总结出的最佳迁移策略

在数据存储和处理领域&#xff0c;HBase作为一种分布式、可扩展的NoSQL数据库&#xff0c;被广泛应用于大规模数据的存储和分析。然而&#xff0c;随着业务需求的变化和技术发展的进步&#xff0c;有时候我们需要将现有的HBase数据迁移到其他环境或存储系统。HBase数据迁移是一…

观点|周鸿祎:大模型真正的竞争在于使其与用户场景相结合

【网易科技11月9日报道】目前&#xff0c;人工智能技术尚未达到向手机一样的刚性、高频需求&#xff0c;各国和企业都在加大研发和应用力度&#xff0c;探索不同的技术路线和商业模式。 360集团创始人、董事长周鸿祎在2023世界互联网大会乌镇峰会上表示&#xff0c;目前人工智能…

文件扩展名批量修改:txt文件扩展名批量修改为doc文档,高效办公的方法

在我们的日常工作中&#xff0c;经常需要处理大量的文本文件&#xff0c;这些文件可能以.txt为扩展名&#xff0c;而我们需要将其修改为.doc扩展名以方便进一步的操作。这种情况下&#xff0c;我们引用云炫文件管理器来将扩展名批量修改&#xff0c;提升办公的效率。在进行文件…

【数据结构】堆详解!(图解+源码)

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 数据结构解析 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f324;️前言&#x1f324;️堆的理论☁️二叉树的顺序存储☁️堆的概念 &#x1f324;️堆的实现…

Android 进阶——Binder IPC之学习Binder IPC架构及原理概述(十二)

文章大纲 引言一、Binder IPC 基础架构1、Binder IPC核心角色2、Binder IPC的数据流 二、Binder IPC 协议通信流程三、Binder IPC 核心角色详解1、Server 进程及Server 组件2、Client进程及Client组件3、Service Manager 与实名 Binder4、Binder 驱动 四、Binder 通信过程五、开…

解锁潜在商机的钥匙——客户管理系统公海池

在竞争激烈的市场环境下&#xff0c;企业需要更智能、高效的方式管理客户&#xff0c;从而挖掘潜在商机。客户管理系统的公海池&#xff0c;就是为此而生的利器&#xff0c;让你轻松解锁商机&#xff0c;提升客户管理效能。 公海池&#xff0c;打破信息孤岛&#xff0c;释放潜在…

树之二叉排序树(二叉搜索树)

什么是排序树 说一下普通二叉树可不是左小右大的 插入的新节点是以叶子形式进行插入的 二叉排序树的中序遍历结果是一个升序的序列 下面是两个典型的二叉排序树 二叉排序树的操作 构造树的过程即是对无序序列进行排序的过程。 存储结构 通常采用二叉链表作为存储结构 不能 …

一致性算法介绍(二)

1.4. NWR N &#xff1a;在分布式存储系统中&#xff0c;有 多少份备份数据 W &#xff1a;代表一次成功的更新操作要求至少有 w 份数据写入成功 R &#xff1a; 代表一次成功的读数据操作要求至少有 R 份数据成功读取 NWR值的不同组合会产生不同的一致性效果&#xff0c;当WR…

【LeetCode刷题笔记】堆和优先级队列

358. K 距离间隔重排字符串 解题思路: 大根堆 + 队列 , 1)首先 计数数组 统计 每个字符出现的次数 ,然后将 计数 > 0 的 字符 和 次数 一起放入 大根堆 ,大根堆中