1、Java中是单继承,为什么不多继承?
Java 中选择单继承而不支持多继承是为了解决多继承可能引发的一些问题和复杂性。以下是一些原因:
- 钻石继承问题:多继承可能导致钻石继承问题,即当一个类继承自两个具有共同父类的类,并且这两个父类又继承自同一个父类时,会产生冲突。这会导致编译器难以确定如何调用共同父类的方法,从而引发歧义。为了避免这种复杂性,Java 选择了单继承。
- 继承层次的易读性和可维护性:多继承会导致继承层次的复杂性增加,并且可能变得难以理解和维护。当一个类有多个父类时,这些父类可能存在相同的方法名,并且子类必须明确指定要调用哪个父类的方法。这增加了代码的复杂性和可读性。
- 接口的实现:Java 提供了接口的概念,它允许类实现多个接口。接口相比多继承更加灵活,可以在类中实现多个不同的行为,而不同于继承父类的特定实现。通过接口,Java 提供了一种更简单和可控的方式来实现类的多重行为。
尽管 Java 选择了单继承,但它通过接口和组合等机制提供了其他方式来实现类的复用和扩展。这种设计决策在平衡灵活性和复杂性之间起到了重要作用,并且使得 Java 语言具备了较高的可读性、可维护性和可扩展性。
2、Kotlin的继承和Java的继承一样都是单继承,区别在于Kotlin用:
来代替了extends
// 父类Person
open class Person()
// 子类Man
class Man() : Person()
// 简写
class Man: Person()
特别注意:这里还需要注意的是在Kotlin中类默认都是final的(代表该类不让继承)
,需要在class
关键词前面加上open
,表示该类可以被其他类继承和Java一样Kotlin也是单继承的。
Kotlin代码
class Person {
}
编译成Java代码:
注意:class Person{}编译成Java代码,是public 修饰符
public final class com/kana/crazytv/ui/activity/Person {
}
3、Handler、Thread、Handler三者的区别?
Handler、Thread、Looper 是在 Android 平台下用于处理多线程和消息传递的三个重要组件。
-
1、
Thread(线程):
Thread 是 Java 中用于实现多线程编程的基本类之一。在 Android 中,Thread 也用于在后台执行耗时操作,以避免阻塞主线程。通过继承 Thread 类或实现 Runnable 接口,可以创建一个新的线程,并在其 run() 方法中定义线程执行的任务。使用 Thread 可以实现并发执行多个任务,但线程之间的通信需要通过其他机制实现。 -
2、
Looper(消息循环器):
Looper 是 Android 中的一个类,用于实现线程中的消息循环。每个线程只能有一个 Looper,它负责管理线程中的消息队列。通过调用 Looper.prepare() 创建一个 Looper 对象,并调用 Looper.loop() 方法来启动消息循环。在消息循环中,Looper 会不断地从消息队列中取出消息并分发给对应的 Handler 处理。 -
3、
Handler(处理器):
Handler 是 Android 中用于处理消息和线程间通信的类。Handler 可以与特定的线程和 Looper 关联,从而在对应的线程中处理消息。它可以发送消息、处理消息和安排任务等。
通过 Handler,可以实现线程之间的通信,从而在后台线程中更新 UI、发送和处理消息等操作。综上所述,Thread 是 Java 提供的多线程机制,用于实现并发执行多个任务;Looper 是 Android 提供的消息循环机制,用于创建和管理线程中的消息队列;Handler 是 Android 提供的用于处理消息和线程间通信的机制,可以与特定的线程和 Looper 关联,从而在对应的线程中处理消息。它们三者之间存在协作关系,可以实现多线程编程和线程间的消息传递。
4、Android版本的特性,不同API的区别?
- 1、
功能和特性:
不同的 Android API 版本会引入不同的功能和特性。较新的 API 版本通常会提供更多的功能和改进,例如支持新的硬件特性、增强的安全性、性能优化等。开发人员可以根据项目需求选择适合的 API 版本。 - 2、
兼容性:
每个 Android API 版本都有一定的兼容性范围。较新的 API 版本可能不被较旧的设备所支持,因此在开发应用程序时需要考虑目标设备的 API 版本,以确保应用程序在各种设备上能够正常运行。 - 3、
API 接口:
不同的 API 版本可能会引入、修改或废弃某些 API 接口。开发人员需要注意使用的 API 接口的兼容性,并在目标设备上进行适当的版本检查和处理。 - 4、
权限和安全性:
较新的 Android API 版本通常会引入更严格的权限和安全性控制,以保护用户的隐私和提高应用程序的安全性。开发人员需要遵守这些安全性要求,并在应用程序中正确处理权限请求和敏感数据。 - 5、
支持库:
Android 提供了一系列的支持库,用于向低版本的设备提供较新 API 版本的功能和特性。这些支持库可以让开发人员在更多的设备上使用较新 API 版本的功能,同时保持向后兼容性。
- Android 1.0-1.1:这是Android最早的版本,引入了基本的操作系统功能和应用程序框架,如活动、服务、内容提供者和广播接收器。
- Android 1.5(Cupcake):引入了一些重要的用户界面改进,如虚拟键盘、文本选择和复制粘贴功能。
- Android 1.6(Donut):增加了搜索框小部件、快速搜索和Android Market应用程序。
- Android 2.0-2.1(Eclair):引入了新的用户界面和功能,如扩展搜索框、多点触控和HTML5支持。
- Android 2.2(Froyo):增加了新的功能和API,如USB和Wi-Fi热点功能、Adobe Flash Player支持和性能优化。
- Android 2.3(Gingerbread):带来了许多用户界面和性能改进,如新的虚拟键盘、新的下载管理器和更快的图形性能。
- Android 3.0-3.2(Honeycomb):这些版本是专门为平板电脑设计的,引入了全新的用户界面和功能,如片段、操作栏和多面板支持。
- Android 4.0-4.0.4(Ice Cream Sandwich):将手机和平板电脑合并为一个统一的平台,引入了新的用户界面和功能,如全新的锁屏、人脸解锁和Android Beam。
- Android 4.1-4.3(Jelly Bean):带来了更流畅的用户界面和更好的性能,引入了Google Now、可调整大小的小部件和通知优化。
- Android 4.4(KitKat):引入了更轻量级的操作系统,优化了内存管理和性能,引入了新的Immersive Mode、透明系统栏和印刷效果。
- Android 5.0-5.1(Lollipop):带来了全新的Material Design用户界面风格,引入了新的动画效果、通知控制和多用户模式。
- Android 6.0(Marshmallow):引入了运行时权限控制、指纹识别支持和Doze模式等功能。
- Android 7.0-7.1(Nougat):带来了分屏模式、即时应用、通知重组和虚拟现实支持。
- Android 8.0-8.1(Oreo):引入了自适应图标、画中画模式、通知渠道和后台限制等功能。
- Android 9.0(Pie):带来了全新的手势导航、自适应亮度、应用程序操作和数字禁止等功能。
- Android 10.0(Q):引入了全新的系统级黑暗模式、更多的隐私和安全控制、手势导航和增强的智能回复等功能。
- Android 11.0:最新版本,引入了通知历史记录、聊天气泡、屏幕录制和自动重置权限等新功能。
- Android 12.0:最新版本,引入了安全变更、隐私保护、应用休眠等新功能。
5、ListView和RecyclerView的区别
ListView 和 RecyclerView 是 Android 中常用的用于展示列表数据的视图控件,它们的主要区别如下:
- 1、
灵活性:
RecyclerView 比 ListView 更加灵活。RecyclerView 是在 ListView 的基础上进行了扩展和优化,提供了更强大的布局管理和动画效果的支持。RecyclerView 使用了 ViewHolder 模式,可以更好地控制和管理列表项的视图,提供了更好的性能和可扩展性。 - 2、
布局管理器:
RecyclerView 提供了灵活的布局管理器,开发者可以通过设置不同的布局管理器来实现各种不同的列表布局效果,如线性布局、网格布局、瀑布流等。而 ListView 只支持线性布局。 - 3、
数据适配器:
RecyclerView 的数据适配器比 ListView 更加灵活。RecyclerView 的 Adapter 继承自 RecyclerView.Adapter,需要实现自定义的 ViewHolder,并通过 onBindViewHolder() 方法将数据绑定到 ViewHolder 上。这种方式相比于 ListView 的 ArrayAdapter 或 SimpleAdapter,使得数据的展示和更新更加灵活和高效。 - 4、
动画效果:
RecyclerView 对动画效果的支持更好。RecyclerView 提供了内置的动画效果支持,可以方便地为列表项添加动画效果,如淡入淡出、滑动、移除等。而 ListView 需要通过自定义动画实现。
总的来说,如果需要更高灵活性和更好的性能,建议使用 RecyclerView。而如果只是简单地展示线性列表数据,可以选择使用更简单的 ListView。
6、Android View刷新机制
Android 中的 View 刷新机制是通过绘制流程来实现的。当 View 状态发生改变(如数据更新、用户交互等)时,系统会触发 View 的重新绘制,以保证 UI 显示的正确性。以下是 Android 中 View 刷新机制的主要流程:
- 1、
状态变化:
当 View 的状态发生改变时,如数据更新、用户交互等,应用程序会通知 View 更新。 - 2、
requestLayout():
在状态变化后,View 会调用 requestLayout() 方法,该方法会标记 View 为"待重新布局"的状态,表示 View 需要重新计算自身的尺寸和位置。 - 3、
measure():
当 UI 线程空闲时,系统会调用 ViewGroup 的 measure() 方法,该方法会递归地调用所有子 View 的 measure() 方法,计算出子 View 的尺寸和位置。 - 4、
layout():
在 measure() 完成后,系统会调用 ViewGroup 的 layout() 方法,该方法会递归地调用所有子 View 的 layout() 方法,将子 View 放置到正确的位置上。 - 5、
draw():
在 layout() 完成后,系统会调用 View 的 draw() 方法,该方法会递归地调用所有子 View 的 draw() 方法,将每个 View 绘制到屏幕上。 - 6、
invalidate():
在 View 绘制完成后,如果需要刷新 View,可以调用 invalidate() 方法,该方法会使 View 标记为"待重新绘制"的状态,即下一次绘制时会重新执行 measure()、layout() 和 draw()。
需要注意的是,View 的刷新是在 UI 线程上进行的,如果在 UI 线程上执行耗时操作,会导致界面卡顿。为了避免这种情况,可以使用异步任务或线程池来执行耗时操作,以保证界面的流畅性。
7、android中activity 的四种启动模式和生命周期
Android中有四种常见的启动模式:
-
standard(标准模式):
每次启动 Activity 都会创建新的实例,并放入任务栈中。无论是否存在相同的实例,都会创建新的实例。 -
singleTop(单顶模式):
如果要启动的 Activity 已经位于任务栈的顶部,那么不会创建新的实例,而是会调用实例的 onNewIntent() 方法更新状态。如果要启动的 Activity 不在栈顶,会创建新的实例并放入栈顶。 -
singleTask(单例模式):
在整个应用程序中只会创建一个实例。如果要启动的 Activity 已经存在于任务栈中,会将该 Activity 的实例从栈中移动到栈顶,并调用实例的 onNewIntent() 方法更新状态。如果要启动的 Activity 不存在于任务栈中,会创建新的实例并放入栈顶。 -
singleInstance(单独实例模式):
在全局只有一个实例,且该实例独立于其他任务栈。如果要启动的 Activity 已经存在于其他任务栈中,会将该任务栈移动到当前任务栈中并调用实例的 onNewIntent() 方法更新状态。如果要启动的 Activity 不存在于任何任务栈中,会创建新的任务栈并放入其中。
Activity 的生命周期包括以下几个方法:
- onCreate():Activity 被创建时调用,用于初始化界面和数据。
- onStart():Activity 可见但不可交互时调用。
- onResume():Activity 可见且可以与用户交互时调用。
- onPause():Activity 失去焦点但仍可见时调用,用于保存数据和执行轻量级的清理操作。
- onStop():Activity 不可见时调用,用于保存数据和执行较重的清理操作。
- onDestroy():Activity 被销毁时调用,用于释放资源和清理操作。
需要注意的是,当用户离开当前 Activity 或新的 Activity 覆盖当前 Activity 时,当前 Activity 可能会调用 onPause() 和 onStop() 方法,但不一定会调用 onDestroy() 方法。只有当系统内存不足时,当前 Activity 才可能被销毁。
8、Android 低版本SDK 如何实现高版本 api
-
1、添加注解 @TargetApi(Build.VERSION_CODES.N)
-
2、添加注解 @RequiresApi(Build.VERSION_CODES.N)
-
3、添加注解 @SuppressLint(“NewApi”)
Lint 静态检测工具将会忽略被注解元素的警告 -
4、添加运行时SDK版本判断
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//运行系统大于等于指定版本,可运行高于指定版本的api
} else {
}
9、什么情况导致 oom
- 1、
Java堆内存溢出:
当应用程序在堆中分配的对象过多,而垃圾回收器无法及时回收这些对象时,就会导致堆内存溢出。 - 2、
Native堆内存溢出:
Android应用程序中使用的一些本地代码(如C/C++代码)在分配内存时也可能发生溢出。 - 3、
图片内存溢出:
加载大量图片资源时,如果没有正确处理图片的内存占用,就容易引发内存溢出。 - 4、
内存泄漏:
应用程序中存在未正确释放的对象引用,导致这些对象无法被垃圾回收器回收,最终占用大量内存导致OOM异常。
10、当遇到 Native 堆内存溢出时,可以采取以下几种处理方式:
- 1、
优化内存使用:
检查代码中是否存在内存泄漏或者过度使用内存的情况。可以使用工具如 Android Profiler 或 LeakCanary 来帮助检测和解决内存泄漏问题。 - 2、
减少内存占用:
通过优化算法或数据结构,减少内存的使用量。例如,使用更节省内存的数据结构,或者对大数据进行分块处理。增加内存限制:可以通过在 AndroidManifest.xml 文件中的 application 标签下添加 android:largeHeap="true" 属性来增加应用程序的堆内存分配。 - 3、
使用内存缓存:
对于频繁使用的资源,可以将其缓存在内存中,避免重复加载。可以使用 LruCache 或者自定义的缓存机制来实现。 - 4、
分析和优化 Native 代码:
如果 Native 堆内存溢出是由于 Native 代码引起的,可以对 Native 代码进行分析和优化,减少内存使用或者优化算法。 - 5、
增加设备内存:
如果应用程序所使用的内存超出设备的可用内存,可以考虑增加设备内存或者减少应用程序的内存占用。 - 6、
采用更高效的数据传输方式:
如果 Native 堆内存溢出是由于大量的数据传输引起的,可以考虑采用更高效的数据传输方式,如使用文件缓存或者流式传输来减少内存使用。 - 7、
重启应用程序:
如果以上方法无法处理内存溢出问题,可以尝试重启应用程序,重新加载资源和清理内存。需要根据具体情况选择合适的处理方法,并进行详细的分析和测试,以确保解决 Native 堆内存溢出问题。
11、Java堆内存溢出 如何处理?
- 1、
增加堆内存:
通过调整 JVM 的启动参数,增加堆内存的大小。可以通过设置 -Xms 和 -Xmx 参数来分别指定堆的初始大小和最大大小。 - 2、
优化内存使用:
检查代码中是否存在内存泄漏或者过度使用内存的情况。尽量避免创建过多的对象,及时释放不再使用的对象。可以使用工具如 Java Profiler 或者 Eclipse Memory Analyzer 来帮助检测和解决内存泄漏问题。 - 3、
减少内存占用:
通过优化算法或者数据结构,减少内存的使用量。例如,使用更节省内存的数据结构,或者对大数据进行分块处理。 - 4、
增加 PermGen/Metaspace 内存:
PermGen 内存用于存放类的元数据,而 Metaspace 则是 JDK 8+ 替代了 PermGen 的新的内存区域。可以通过调整 -XX:PermSize 和 -XX:MaxPermSize(或 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize)参数来增加 PermGen/Metaspace 的大小。- - 5、使用内存缓存:
对于频繁使用的资源,可以将其缓存在内存中,避免重复加载。可以使用缓存框架如 Guava Cache 或者 Ehcache 来实现。 - 6、
分析和优化代码:
如果堆内存溢出是由于代码中存在的问题引起的,可以进行代码分析和优化,减少内存占用或者优化算法。 - 7、
重启应用程序:
如果以上方法无法处理内存溢出问题,可以尝试重启应用程序,重新加载资源和清理内存。
12、什么是 Kotlin Flow?
答案:Kotlin Flow 是一种用于异步数据流处理的 Kotlin 协程库。它提供了一种类似于 RxJava 的响应式编程模型,使得处理异步数据流变得更加简洁和易于理解。
13、Kotlin Flow 和 RxJava 有什么区别?
答案:Kotlin Flow 和 RxJava 都提供了异步数据流处理的能力,但它们在语法和设计上有一些区别。Kotlin Flow 是基于 Kotlin 协程的,使用 suspend 函数来定义和处理数据流。它更加符合 Kotlin 的语言特性,并且可以无缝地与其他协程代码集成。而 RxJava 是基于观察者模式的库,使用 Observable 和 Observer 来处理数据流。
14、Kotlin Flow 中的冷流和热流有什么区别?
答案:在 Kotlin Flow 中,冷流(cold flow)和热流(hot flow)是两种不同的数据流类型。冷流是一种只在订阅时开始发射数据的流,每个订阅者都会获取到完整的数据流。而热流是一种在创建时就开始发射数据的流,订阅者只能获取到它们订阅之后发射的数据。
15、如何处理 Kotlin Flow 中的异常?
答案:在 Kotlin Flow 中,可以使用 try-catch 块或者 catch 操作符来处理异常。当 Flow 中发生异常时,异常会被捕获并包装为一个特殊的异常类型 FlowException。你可以使用 catch 操作符指定处理异常的逻辑,例如记录日志或者返回默认值。
16、Kotlin Flow 中的 map 和 flatMap 有什么区别?
答案:map 和 flatMap 都是用于对 Flow 中的数据进行转换的操作符。map 操作符将每个元素映射为另一个元素,而 flatMap 操作符将每个元素映射为一个新的 Flow,并将这些 Flow 合并成一个。flatMap 通常用于处理嵌套的异步操作或者需要展平流的情况。
17、mmkv底层原理,总结一下
MMKV(Mutil-Model Key-Value)是一个高性能、易用的 key-value 存储框架,它的底层原理主要包括以下几个方面:
- 1、
存储引擎:
MMKV 使用了 C/C++ 实现的 mmap 存储引擎。mmap 是一种内存映射文件的技术,可以将文件映射到内存中,从而使得对文件的读写操作变得像对内存的操作一样高效。MMKV 使用 mmap 存储引擎可以避免频繁的序列化和反序列化操作,提高存储和读取的效率。 - 2、
数据结构:
MMKV 使用了类似于字典的数据结构来存储 key-value 对。通过哈希表实现快速的数据查找和访问,使得数据的读取和写入操作具有高效性能。 - 3、
缓存机制:
为了提高读取效率,MMKV 使用了内部缓存机制。在读取数据时,首先会在缓存中查找,如果找到了对应的值,则直接返回;如果缓存中没有找到,则从 mmap 中读取数据,并将读取的数据保存在缓存中。这样可以避免频繁的 mmap 操作,提高读取性能。 - 4、
数据持久化:
MMKV 的数据是持久化的,即使应用程序被关闭或设备重启,数据也能够得到保留。这是通过将数据写入到文件中实现的。当应用程序需要将数据持久化时,MMKV 会将缓存中的数据写入到文件中。
总之,MMKV 通过使用 mmap 存储引擎、字典数据结构、缓存机制和数据持久化等技术手段,实现了高性能、易用的 key-value 存储框架。它适用于 Android 平台上需要频繁读写数据的场景,例如数据缓存、配置信息存储等。
18、WorkManager 和service的区别
WorkManager 和 Service 是两种用于后台任务的不同机制。
-
1、
WorkManager:
是一个基于任务调度的库,用于在特定条件下执行后台任务。它提供了更强大和灵活的任务调度功能,可以确保任务在设备满足特定条件时执行,例如设备空闲、充电状态、网络可用等。WorkManager 还具有持久性,即使应用被杀死或设备重启,也能够保证任务的执行。另外,WorkManager 还可以处理周期性和延迟性任务,同时也支持链式任务和触发任务等功能。 -
2、
Service:
是一种在后台运行的组件,用于执行长时间运行的后台任务,而不与用户界面交互。Service 是会被绑定到应用组件(如 Activity)的生命周期的,它可以在后台处理一些耗时的操作,同时也可以与其他组件通信。Service 分为两种类型:前台服务(Foreground Service)和后台服务(Background Service)。前台服务优先级较高,会显示在系统状态栏,用于用户能够感知的任务,如音乐播放、导航等。而后台服务则是在后台执行的任务,如下载、数据同步等。
总结:WorkManager 是一个更加灵活、强大和可靠的后台任务调度库,适用于需要精确控制任务的执行条件和周期的场景。而 Service 是一种通用的后台任务执行机制,适用于需要在后台长时间运行的任务,但不需要太多的控制和调度。
19、string中stringbuffer和stringbuilder的区别?
区别1:线程安全
StringBuffer:线程安全,StringBuilder:线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 StringBuilder 修饰。
StringBuffer 代码片段
22e73bd0b88dc1a2ef4269a9d4e7a22.png
区别2:缓冲区
f3123ac975736f9734aacd8a2010f76.png可以看出,StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。
区别3:性能
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。
20、MD5 是对称加密还是非对称加密?
- MD5(Message Digest Algorithm 5)是一种哈希函数,它属于对称加密算法的范畴。
- 对称加密算法是指加密和解密使用相同的密钥进行操作的加密方法。
- MD5 使用相同的密钥对输入的数据进行加密和解密,生成固定长度的摘要(通常是 128 位),用于验证数据完整性和一致性。然而,需要注意的是,MD5 不属于加密算法,而是哈希算法。
- 哈希算法是一种单向的、不可逆的算法,它将任意长度的数据映射为固定长度的哈希值。
- 在 MD5 对输入数据进行哈希运算后,得到的哈希值是不可逆的,即无法通过哈希值还原出原始数据。
- 与对称加密算法不同,非对称加密算法使用一对密钥(公钥和私钥)进行加密和解密操作。公钥用于加密数据,而私钥用于解密数据。常见的非对称加密算法有 RSA、DSA 等。
总结起来,MD5 是一种对称加密算法,但它实际上是一种哈希算法,用于生成不可逆的摘要。
21、AES是对称加密还是非对称加密?
- AES(Advanced Encryption Standard)是一种对称加密算法。
- 对称加密算法是指加密和解密使用相同的密钥进行操作的加密方法。
- AES 使用相同的密钥对输入的数据进行加密和解密,它是目前应用最广泛的对称加密算法之一。
- 在 AES 加密过程中,输入的数据被划分成固定长度的块,然后通过多轮的加密和混淆操作,使用相同的密钥进行加密。解密过程则是使用相同的密钥进行逆向操作,对密文进行解码还原为原始的明文数据。
- 与对称加密算法不同,非对称加密算法使用一对密钥(公钥和私钥)进行加密和解密操作。公钥用于加密数据,而私钥用于解密数据。
- 常见的非对称加密算法有 RSA、DSA 等。
总结起来,AES 是一种对称加密算法,它使用相同的密钥进行加密和解密操作。
22、MD5和AES的区别?
MD5和AES是两种不同的加密算法,它们具有以下几个主要区别:
- 1、
加密方式:
MD5是一种哈希算法,用于生成固定长度的摘要,而AES是一种对称加密算法,用于对数据进行加密和解密。 - 2、
安全性:
AES是一种高度安全的加密算法,被广泛用于保护敏感数据。它具有128位、192位和256位三种密钥长度的变种,提供了较高的安全性。而MD5由于其较短的摘要长度(128位),已经被认为是不安全的,容易受到碰撞攻击和彩虹表攻击。 - 3、
可逆性:
AES是一种可逆的加密算法,可以使用相同的密钥进行加密和解密操作。而MD5是一种哈希算法,是不可逆的,无法通过哈希值还原出原始数据。 - 4、
用途:
MD5主要用于数据完整性验证和防篡改,常用于校验文件完整性以及密码存储的相对安全性。AES则常用于保护敏感数据的传输和存储,如加密通信、加密文件等。
总结起来,MD5是一种哈希算法,用于数据摘要和完整性验证,而AES是一种对称加密算法,用于数据的加密和解密。MD5已被认为不安全,而AES具有较高的安全性和可靠性。
23、面向对象的特征有哪些?请简要描述每个特征的含义。
- 1、
封装(Encapsulation):
将数据和方法封装在一个类中,通过限制对数据的访问来保证数据的安全性和完整性。 - 2、
继承(Inheritance):
通过扩展现有类的属性和方法,创建新的类,并且可以重用现有类的代码和逻辑。 - 3、
多态(Polymorphism):
同一个方法可以被不同的对象调用并产生不同的行为,实现方法的重载和覆盖。
24、什么是类和对象?它们之间的关系是什么?
- 1、类是用于定义对象的模板或蓝图,描述了对象具有的属性和行为。
- 2、对象是类的一个实例,具有通过类定义的属性和方法。
25、什么是继承?它的作用是什么?
- 1、继承是面向对象编程的一个基本概念,指的是一个类可以派生出另一个类,新类继承了原有类的属性和方法。
- 2、继承的作用是实现代码的重用,避免重复编写相同的代码,同时可以通过扩展现有类的功能来创建新的类。
26、什么是多态?它的实现方式有哪些?
- 1、多态指的是同一类的对象对同一消息可能会有不同的响应。即不同的对象可以对同一个方法产生不同的行为。
- 2、多态的实现方式有两种:静态多态(方法重载)和动态多态(方法重写,使用父类引用指向子类对象)。
27、什么是抽象类和接口?它们之间的区别是什么?
- 1、抽象类是一个不能实例化的类,用于提供给其他类继承的基类,可以包含抽象方法和具体方法。
- 2、接口是一种类似于抽象类的概念,但是只能包含常量和抽象方法的特殊类。
- 3、区别在于抽象类可以有具体方法的实现,而接口只能有抽象方法和常量。类可以实现多个接口,但只能继承一个抽象类。
28、java中,那些对象可以作为GCRoots
在Java中,GCRoot是指一组根对象,它们被认为是活动对象的起点,不会被垃圾回收器回收。GCRoot对象包括以下几种:
- 1、
虚拟机栈中引用的对象:
活动线程的栈帧中引用的对象被视为GCRoot,包括方法参数、局部变量和临时变量等。 - 2、
方法区中静态引用的对象:
静态变量引用的对象被视为GCRoot,这些变量通常是类的静态成员变量或常量。 - 3、
方法区中常量引用的对象:
方法区中的常量池中引用的对象被视为GCRoot,例如字符串常量池中的字符串对象。 - 4、
本地方法栈中JNI引用的对象:
JNI(Java Native Interface)是Java与本地代码(如C/C++)交互的接口,本地方法栈中的JNI引用的对象被视为GCRoot。
需要注意的是,GCRoot对象是不可达对象的起点,而不是所有的不可达对象都是GCRoot。当对象不再被任何GCRoot引用时,它将被认为是不可达对象,并可以被垃圾回收器回收。
28、什么是Lottie动画?
- 1、Lottie是一个用于在移动设备和Web上展示高质量动画的开源库。
- 2、它使用JSON格式的动画文件,可以在不同平台上实现可缩放、可交互和高性能的矢量动画。
29、Lottie动画的主要特点是什么?
- 1、
矢量动画:
Lottie动画使用矢量图形而不是位图,因此可以无限缩放而不会损失画质。 - 2、
高性能:
Lottie动画在动画渲染方面具有良好的性能,可以实现流畅的动画效果。 - 3、
可交互:
Lottie动画可以添加交互功能,例如点击、滑动等。 - 4、
跨平台兼容性:
Lottie动画支持在Android、iOS、Web等平台上展示,并且动画效果保持一致。
30、Lottie动画通过什么方式解析和渲染?
- 1、Lottie动画使用了一种叫做Bodymovin的Adobe After Effects插件来创建和导出动画。
- 2、在客户端,Lottie库将JSON格式的动画文件解析为可供渲染的矢量图形,并使用底层平台的图形引擎进行渲染。
31、Lottie动画在Android上的使用方法是什么?
- 1、在Android上使用Lottie动画,可以通过在项目的build.gradle文件中添加Lottie库的依赖,并在代码中加载和显示动画。
- 2、可以使用LottieAnimationView控件来显示动画,并使用LottieComposition文件来加载动画。
32、Lottie动画可以实现哪些高级效果?
Lottie动画支持许多高级效果,包括:
- 1、帧动画:Lottie动画可以实现帧动画的效果,可以定义关键帧和过渡效果。
- 2、插值器:可以定义动画的插值器,实现渐变、弹性等效果。
- 3、动画控制:可以控制动画的播放速度、暂停、停止等操作。
- 4、多重动画:可以组合多个Lottie动画,实现复杂的动画效果。
- 5、交互功能:可以为动画添加交互功能,例如点击、滑动等。
33、Android中SVG和SVGA的区别
在Android中,SVG(Scalable Vector Graphics)和SVGA(Scalable Vector Graphics Animation)是两种不同的图形格式。
它们的区别如下:
- 1、
格式类型:
- SVG是一种静态的矢量图形格式,它使用XML描述图形,可以通过CSS和JavaScript进行样式和交互操作。SVG文件通常具有.svg的文件扩展名。
- SVGA是基于SVG的矢量动画格式,它扩展了SVG,支持动画效果。SVGA文件包含了描述动画的逐帧矢量图形数据,可以通过播放器播放动画。SVGA文件通常具有.svga的文件扩展名。
- 2、
动画效果:
- SVG通常用于绘制静态图形,可以通过CSS和JavaScript实现一些简单的动画效果,如变换、渐变、过渡等。但SVG本身不支持复杂的逐帧动画效果。
- SVGA是专门用于实现矢量动画的格式,它支持逐帧动画,可以实现复杂的动画效果,如位移、缩放、旋转、淡入淡出、路径动画等。
- 3、
文件大小和性能:
- SVG文件通常比较小,因为它使用矢量图形描述图像,可以进行压缩,并且只需存储一份静态图像数据。
- SVGA文件相对较大,因为它包含了描述动画的逐帧矢量图形数据,可能需要存储多帧图像数据。但SVGA文件可以进行压缩,以减小文件大小。
- 在性能方面,由于SVG是静态图形格式,渲染和展示较为轻量,而SVGA需要进行动画渲染,可能需要较多的计算资源和时间。
总结:SVG是一种静态的矢量图形格式,适用于绘制静态图形,而SVGA是一种基于SVG的矢量动画格式,适用于实现复杂的矢量动画效果。
34、什么是SVGA?
- 1、SVGA是一种基于矢量图形的动画格式,可以实现高质量、高性能、可压缩的矢量动画效果。
- 2、它在移动设备上播放动画时不会损失图像质量,适用于在移动应用和游戏中使用。
35、SVGA的优势和特点是什么?
- 1、
高质量:
SVGA使用矢量图形进行动画渲染,不会损失图像质量。 - 2、
高性能:
SVGA使用硬件加速来播放动画,具有较低的CPU和内存占用。 - 3、
可压缩:
SVGA动画文件可以进行压缩,减小文件大小,节省网络带宽和存储空间。 - 4、
动态交互:
SVGA支持动态交互,可以根据动画状态触发事件和交互效果。
36、如何在Android应用中使用SVGA?
在Android应用中使用SVGA,可以按照以下步骤:
- 1、
引入SVGA库:
将SVGA库添加到项目的依赖中。 - 2、
加载SVGA动画文件:
在代码中加载SVGA动画文件,并将其绑定到指定的View上。 - 3、
播放SVGA动画:
调用相应的方法播放、暂停、停止、循环等操作。
37、SVGA与其他动画格式(如GIF、APNG)的比较?
- 1、
Gif:
GIF是一种较为简单的位图动画格式,不支持矢量图形,文件体积较大,动画质量较低,但在一些简单的动画场景中使用较为方便。 - 2、
APNG:
APNG是一种支持透明度和帧动画的位图动画格式,与GIF相比可以实现更高质量的动画效果,但文件体积较大。
相比之下,SVGA作为一种矢量动画格式,具有更高的动画质量和更小的文件体积,适用于移动设备上播放高质量的动画效果。
38、TCP和UDP的区别
TCP(传输控制协议)和UDP(用户数据报协议)是两种常用的互联网传输协议,它们有以下几个主要区别:
- 1、
连接性:
TCP是面向连接的协议,而UDP是无连接的协议。TCP在通信之前需要建立连接,然后进行数据传输,而UDP则直接发送数据包,不需要建立连接。 - 2、
可靠性:
TCP提供可靠的数据传输,它使用确认和重传机制来确保数据的完整性和可靠性。如果数据包丢失或损坏,TCP会重新发送数据包。而UDP不提供可靠性保证,它不会重传丢失的数据包。 - 3、
速度:
由于TCP提供可靠性保证,它需要进行确认和重传等额外的操作,因此相对于UDP来说速度较慢。UDP没有这些额外的操作,因此传输速度较快。 - 4、
传输方式:
TCP是面向字节流的协议,它将数据划分为字节流进行传输。UDP是面向数据报的协议,它将数据划分为数据报进行传输。 - 5、
适用场景:
由于TCP提供可靠性保证,适用于对数据完整性要求较高的应用,如文件传输、电子邮件等。而UDP适用于对实时性要求较高的应用,如音视频传输、实时游戏等。
总结起来,TCP提供可靠的、有序的、面向连接的数据传输,适用于对数据完整性要求较高的应用;而UDP提供快速的、无连接的数据传输,适用于对实时性要求较高的应用。
39、什么是Socket?
Socket是一种用于网络通信的编程接口,它定义了一套用于网络通信的函数或方法。通过Socket,可以实现不同计算机之间的数据传输和通信。
40、什么是WebSocket?
WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许服务器主动向客户端推送数据,而不需要客户端发送请求。WebSocket在Web开发中常用于实时通信和实时数据更新。
41、WebSocket与HTTP的区别是什么?
- 1、
连接方式:
WebSocket使用长连接,而HTTP使用短连接。 - 2、
通信效率:
WebSocket的通信效率更高,因为它不需要频繁地建立和关闭连接。 - 3、
服务器推送:
WebSocket允许服务器主动向客户端推送数据,而HTTP需要客户端发送请求才能获取数据。 - 4、
数据格式:
WebSocket支持传输二进制数据,而HTTP只支持传输文本数据。
42、WebSocket的优势是什么?
- 1、
实时性:
WebSocket支持服务器主动向客户端推送数据,实现实时通信和实时数据更新。 - 2、
效率
:WebSocket使用长连接,避免了频繁建立和关闭连接的开销,通信效率更高。 - 3、
双向通信:
WebSocket支持全双工通信,客户端和服务器可以同时发送和接收数据。 - 4、
跨平台:
WebSocket可以在不同的平台和设备上使用,包括Web浏览器、移动应用等。
43、socket和webSocket的区别
Socket
是一种通信协议,用于在网络上进行数据传输。它提供了一种可靠的、面向连接的、全双工的通信方式,可以在客户端和服务器之间进行双向通信。
WebSocket
是一种基于HTTP协议的通信协议,它允许在客户端和服务器之间进行双向通信。与传统的HTTP请求-响应模式不同,WebSocket建立了一个持久的连接,可以在任何时候进行双向通信,而不需要每次通信都建立新的连接。
以下是Socket和WebSocket的主要区别:
- 1、
连接方式:
Socket需要在客户端和服务器之间建立连接,而WebSocket在客户端和服务器之间建立一个持久的连接。 - 2、
通信方式:
Socket提供了一种面向字节流的通信方式,可以自由地发送和接收字节流数据。WebSocket提供了一种面向消息的通信方式,可以发送和接收消息。 - 3、
协议支持:
Socket可以使用多种协议进行通信,如TCP、UDP等。WebSocket基于HTTP协议,使用HTTP的握手过程进行连接建立,然后通过自定义的WebSocket协议进行通信。 - 4、
数据格式:
Socket传输的数据没有特定的格式要求,可以是任意的字节流。WebSocket传输的数据通常使用JSON、XML等格式进行封装。 - 5、
服务器推送:
Socket需要客户端主动发送请求来获取服务器的响应,而WebSocket允许服务器主动推送消息给客户端,实现实时通信。
总的来说,Socket是一种通用的网络通信协议,可以用于各种应用场景,而WebSocket是一种基于HTTP的协议,专门用于实现实时通信。
44、Java 多线程有几种状态
Java多线程有以下几种状态:
- 1、
新建(New):
当线程对象被创建时,它处于新建状态。 - 2、
可运行(Runnable)
:当调用线程的start()方法后,线程进入可运行状态。在可运行状态下,线程可能正在执行,也可能正在等待系统资源。 - 3、
运行(Running):
当线程获得CPU资源后,进入运行状态,开始执行run()方法中的代码。 - 4、
阻塞(Blocked):
线程在某些情况下会进入阻塞状态,例如等待输入/输出、等待锁、等待其他线程完成等。 - 5、
等待(Waiting):
线程在调用wait()方法后,进入等待状态,直到其他线程调用notify()或notifyAll()方法唤醒它。 - 6、
超时等待(Timed Waiting):
线程在调用sleep()方法或带有超时参数的wait()方法后,进入超时等待状态,直到超时时间到达或被其他线程唤醒。 - 7、
终止(Terminated):
线程执行完run()方法中的代码后,进入终止状态。
45、java线程的join 和yield 有什么区别
Java线程的join和yield都是用于控制线程执行顺序的方法,但是它们的作用和使用方式有所不同。
1、join方法:
- join方法是Thread类的一个方法,用于等待线程执行完毕。
- 当一个线程调用另一个线程的join方法时,调用线程会被阻塞,直到被调用线程执行完毕。
- join方法可以用于实现线程之间的协作,比如在主线程中创建并启动多个子线程,然后使用join方法等待所有子线程执行完毕后再继续执行主线程的逻辑。
2、yield方法:
- yield方法是Thread类的一个静态方法,用于让出CPU资源,使得其他具有相同优先级的线程有机会执行。
- 当一个线程调用yield方法时,它会暂停当前正在执行的线程,让出CPU资源,然后重新进入就绪状态,等待系统再次调度。
- yield方法可以用于实现线程之间的合理调度,比如在多个线程之间平衡负载,提高系统的响应性能。
总结:
- join方法用于等待线程执行完毕,实现线程之间的协作。
- yield方法用于让出CPU资源,实现线程之间的合理调度。
46、java 线程的拒绝策略有几种?
在Java中,线程池的拒绝策略有四种:
- 1、
ThreadPoolExecutor.AbortPolicy(默认)
:当线程池已满且工作队列已满时,新提交的任务将被拒绝,并抛出RejectedExecutionException异常。 - 2、
ThreadPoolExecutor.CallerRunsPolicy:
当线程池已满且工作队列已满时,新提交的任务将由提交任务的线程执行。 - 3、
ThreadPoolExecutor.DiscardPolicy:
当线程池已满且工作队列已满时,新提交的任务将被直接丢弃,不会抛出任何异常。 - 4、
ThreadPoolExecutor.DiscardOldestPolicy:
当线程池已满且工作队列已满时,新提交的任务将会替换掉工作队列中最早的任务,然后尝试再次提交任务。可以通过ThreadPoolExecutor类的构造方法来指定拒绝策略,
例如:
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueSize), new ThreadPoolExecutor.AbortPolicy());
47、java 有几种创建线程的方式
Java有两种创建线程的方式:
-
1、
继承Thread类:
创建一个继承自Thread类的子类,并重写run()方法。然后通过创建子类的实例来创建线程对象,并调用start()方法启动线程。 -
2、
实现Runnable接口:
创建一个实现了Runnable接口的类,并实现run()方法。然后通过创建Runnable实现类的实例来创建线程对象,并将其作为参数传递给Thread类的构造方法,最后调用start()方法启动线程。
网友评论