海康Android面试题及参考答案

news2024/11/13 15:43:09

Java 的基本数据类型有哪些?它们各占多少字节?

Java 的基本数据类型分为四类八种。

  • 整数类型

    • byte:字节型,占 1 个字节,取值范围是 -128(-2^7)到 127(2^7 - 1)。主要用于节省内存的场景,比如处理网络协议中单个字节相关的数据。
    • short:短整型,占 2 个字节,取值范围是 -32768(-2^15)到 32767(2^15 - 1)。在某些特定的嵌入式系统或者对内存要求较高且数据范围不大的场景中可能会用到。
    • int:整型,占 4 个字节,取值范围是 -2147483648(-2^31)到 2147483647(2^31 - 1)。这是最常用的整数类型,在大多数情况下用于表示整数数值,比如循环计数、数组下标等。
    • long:长整型,占 8 个字节,取值范围是 -9223372036854775808(-2^63)到 9223372036854775807(2^63 - 1)。当需要表示较大范围的整数时使用,比如处理时间戳(以毫秒为单位)等。
  • 浮点类型

    • float:单精度浮点型,占 4 个字节。它遵循 IEEE 754 标准,可以表示大约 7 位有效数字的浮点数。由于精度相对较低,在一些对精度要求不是极高且需要节省内存的科学计算或图形处理场景中可能会使用。
    • double:双精度浮点型,占 8 个字节。它也遵循 IEEE 754 标准,能表示大约 15 位有效数字的浮点数。在大多数需要处理浮点数的情况,如金融计算、科学计算中广泛使用,因为它的精度更高。
  • 字符类型
    char:字符型,占 2 个字节。用于表示单个字符,它可以存储 Unicode 字符集中的任何字符,包括英文字母、数字、汉字、标点符号等。例如,可以用它来存储用户输入的单个字符或者文本中的一个字符。

  • 布尔类型
    boolean:布尔型,理论上它的大小没有明确规定,但在大多数 Java 虚拟机实现中,它只占 1 位(但实际存储可能会占用 1 个字节)。它只有两个值,true 和 false,常用于条件判断,比如在 if 语句、循环条件等中使用。

浮点数是如何表示的?

在 Java 中,浮点数(float 和 double)是按照 IEEE 754 标准来表示的。

对于单精度浮点数(float),它是 32 位的,其内存布局分为三个部分:符号位(1 位)、指数位(8 位)和尾数位(23 位)。符号位表示浮点数的正负,0 表示正数,1 表示负数。指数位用于表示指数的值,采用偏移码(biased exponent)的形式。对于 float 类型,指数的偏移量是 127。尾数位则表示有效数字部分。例如,对于一个 float 类型的数,将其转换为二进制后,按照 IEEE 754 标准的格式进行存储。在计算实际值时,根据公式 (-1)^ 符号位 × 1. 尾数位 × 2^(指数位 - 127) 来得到。

双精度浮点数(double)是 64 位的,其中符号位 1 位、指数位 11 位(偏移量是 1023)、尾数位 52 位。其表示原理和 float 类似,计算实际值的公式为 (-1)^ 符号位 × 1. 尾数位 × 2^(指数位 - 1023)。这种表示方式可以表示非常大或非常小的数以及有一定精度的小数。但由于浮点数在内存中的这种二进制表示方式,会存在一些精度问题。比如,0.1 在二进制中是一个无限循环小数,在存储为 float 或 double 时会有舍入误差。在进行浮点数比较时,不能直接使用 “==”,而应该考虑使用一个误差范围来判断两个浮点数是否近似相等,以避免由于精度问题导致的错误判断。

Hash、equals 和 == 的区别是什么?

  • ==

    • 基本数据类型比较:当用于比较基本数据类型时,它比较的是值是否相等。例如,int a = 5; int b = 5; 此时 a == b 为 true,因为它们的值都是 5。对于不同的基本数据类型,如 int 和 double,不能直接使用 == 比较,因为它们在内存中的存储格式和表示范围完全不同。
    • 引用数据类型比较:当用于比较引用数据类型时,它比较的是两个对象的内存地址是否相同。例如,有两个不同的对象 A 和 B,即使它们的内容相同,但如果它们是不同的实例,A == B 为 false。比如创建两个不同的 String 对象(不是通过字符串常量池的方式),它们的内存地址不同,使用 == 比较结果就是 false。
  • equals 方法

    • 来源:equals 方法是 Object 类中的方法,在 Object 类中,equals 方法的默认实现就是使用 == 来比较。但是在很多自定义类或者 Java 的一些内置类(如 String、Integer 等)中,equals 方法被重写了。
    • 重写后的行为:以 String 类为例,当比较两个 String 对象时,equals 方法比较的是字符串的内容是否相等,而不是它们的内存地址。比如 String s1 = "abc"; String s2 = new String ("abc"); s1 == s2 为 false,因为它们是不同的对象,内存地址不同,但是 s1.equals (s2) 为 true,因为它们的内容都是 "abc"。对于自定义类,如果需要根据对象的内容而不是内存地址来判断相等性,就需要重写 equals 方法。重写 equals 方法时,需要遵循一些规则,比如自反性(x.equals (x) 应该返回 true)、对称性(如果 x.equals (y) 为 true,那么 y.equals (x) 也应该为 true)、传递性(如果 x.equals (y) 为 true,y.equals (z) 为 true,那么 x.equals (z) 也应该为 true)等。
  • hashCode 方法

    • 作用:hashCode 方法主要用于在一些基于哈希的数据结构中,比如 HashMap、HashSet 等。它返回一个对象的哈希码值,这个哈希码值是一个整数。理想情况下,如果两个对象通过 equals 方法比较相等,那么它们的 hashCode 值应该相同。但是,如果两个对象的 hashCode 值相同,它们不一定通过 equals 方法比较相等(这种情况称为哈希冲突)。
    • 使用场景:在向 HashMap 中添加键值对时,先根据键的 hashCode 值来确定在哈希表中的存储位置,如果这个位置已经有元素了,再使用 equals 方法来进一步判断是否是同一个键。这样可以提高查找和插入的效率。在自定义类中,如果重写了 equals 方法,通常也需要重写 hashCode 方法,以保证在使用哈希相关的数据结构时的正确性。例如,在一个自定义的 Person 类中,如果根据姓名和年龄来判断两个 Person 对象是否相等,那么在重写 equals 方法的同时,需要根据姓名和年龄生成一个合适的 hashCode 值,使得相等的对象有相同的 hashCode 值。

String a = new String ("abc"); 会创建多少个对象?

当执行 String a = new String ("abc"); 语句时,会创建两个对象。

  • 字符串常量池中的对象:在 Java 中,字符串常量池用于存储字符串字面量。当代码中出现 "abc" 这样的字符串字面量时,Java 会首先在字符串常量池中查找是否已经存在 "abc" 这个字符串。如果不存在,就会在字符串常量池中创建一个 "abc" 的对象。这个过程是由 Java 虚拟机自动完成的。这里的 "abc" 对象在字符串常量池中只创建一次,并且在整个程序的运行期间,如果有其他地方使用相同的字符串字面量 "abc",都会共享这个字符串常量池中的对象。

  • 通过 new 关键字创建的对象:new String ("abc") 这部分代码会在堆内存中创建一个新的 String 对象。这个对象与字符串常量池中的 "abc" 对象内容相同,但它们是不同的对象,有不同的内存地址。这个新创建的 String 对象是我们通过代码明确使用 new 关键字创建的,它有自己独立的内存空间,可以对其进行一些特定的操作,比如改变它的属性(虽然 String 类是不可变的,但从内存和对象的角度来看,它是一个独立的实例)。这种创建字符串对象的方式与直接使用字符串字面量赋值的方式有所不同。例如,String b = "abc"; 此时 b 指向的是字符串常量池中的 "abc" 对象,而 String a = new String ("abc"); 中的 a 指向的是在堆中创建的新的 String 对象。这种区别在内存管理和对象比较等操作中需要特别注意,比如使用 == 比较时,a == b 为 false,因为它们指向不同的对象,一个是堆中的对象,一个是字符串常量池中的对象。

Integer a = 10; Integer b = 10; a==b 返回结果是什么?为什么?

a==b 的返回结果是 true。

在 Java 中,对于整数类型,有一个整数缓存机制。当我们使用自动装箱(将基本数据类型自动转换为包装类型)来创建 Integer 对象时,对于在一定范围内(通常是 -128 到 127)的整数,Java 会复用已经存在的 Integer 对象。在这个例子中,a = 10 和 b = 10,因为 10 在这个缓存范围内,所以实际上 a 和 b 指向的是同一个 Integer 对象。这个缓存机制是为了提高性能和节省内存,避免为每个小整数都创建新的对象。

当我们超出这个缓存范围时,情况就会不同。例如,Integer c = 128; Integer d = 128; 此时 c==d 的结果是 false,因为 128 不在缓存范围内,所以会分别创建新的 Integer 对象,它们的内存地址不同。这种自动装箱和缓存机制的存在使得在处理整数包装类型时需要注意对象的相等性比较。如果我们想要比较两个 Integer 对象的值是否相等,应该使用 equals 方法,因为 equals 方法在 Integer 类中被重写了,它比较的是对象所包装的整数值是否相等,而不是比较对象的内存地址。在实际编程中,尤其是在处理大量整数包装类型数据或者在集合中使用 Integer 对象作为元素时,要清楚地理解这种缓存机制和比较方式的区别,以避免出现意外的结果。

Float a = 1.0F - 0.9F; float b = 0.5f - 0.4f; a==b 返回结果是什么?为什么?

a==b 的返回结果是 false。

在 Java 中,浮点数的存储和运算存在精度问题。对于单精度浮点数(float),它们是按照 IEEE 754 标准存储的。1.0F - 0.9F 和 0.5f - 0.4f 在二进制表示下的实际存储和计算结果会出现精度误差。当计算 1.0F - 0.9F 时,其结果在二进制下并不是精确的 0.1F。同样,0.5f - 0.4f 的结果在二进制下也不是精确的 0.1f。由于浮点数在内存中的存储方式,这些结果在经过舍入等操作后,存储的值并不相同。而且,在比较浮点数是否相等时,不能简单地使用 “==” 操作符。因为浮点数在计算机中的二进制表示可能无法精确地表示某些十进制小数,导致计算结果存在微小的偏差。在这种情况下,即使两个浮点数在数学上看起来应该相等,但在计算机内存中的实际存储值可能不同。正确的比较浮点数是否相等的方法是判断它们的差值是否在一个很小的误差范围内,而不是直接使用 “==”。

String 内部是如何存储的?

在 Java 中,String 类内部是通过字符数组(char [])来存储字符串内容的。

当创建一个 String 对象时,这个字符数组会存储字符串中的每个字符。例如,对于字符串 "abc",会有一个长度为 3 的字符数组,其中分别存储了字符 'a'、'b' 和 'c'。字符串在 Java 中存储的方式和内存管理有一些特点。首先,Java 中有一个字符串常量池。当通过字符串字面量创建字符串时,比如 String str = "abc";,JVM 会先在字符串常量池中查找是否已经存在 "abc" 这个字符串。如果不存在,就会在常量池中创建一个新的 String 对象,其内部的字符数组存储相应的字符。如果已经存在,就直接将引用指向已有的字符串对象。当使用 new 关键字创建字符串时,如 String newStr = new String ("abc");,会在堆内存中创建一个新的 String 对象,这个对象内部也有一个字符数组来存储 "abc",但它和字符串常量池中的对象是不同的对象,尽管它们存储的内容相同。这种存储机制使得字符串在内存中的使用更加高效,并且可以通过字符串常量池来共享一些相同的字符串,减少内存开销。同时,String 类的许多方法,如 charAt ()、substring () 等,都是基于内部的字符数组来实现操作的。

String 是不可变的吗?

String 在 Java 中是不可变的。

从对象的角度来看,一旦一个 String 对象被创建,它的内容就不能被改变。这是因为 String 类内部存储字符串内容的字符数组是被声明为 final 的。例如,当我们有一个 String 对象 str = "abc";,如果我们想要修改这个字符串,比如将它变成 "abcd",实际上并没有改变原来的 String 对象。当我们执行类似 str = str + "d"; 这样的操作时,Java 会创建一个新的 String 对象,这个新对象的字符数组存储了 "abcd",而原来的 String 对象(存储 "abc")仍然存在,只是 str 这个引用变量现在指向了新创建的对象。这种不可变性有很多优点。首先,它使得字符串在多线程环境下是安全的,因为多个线程可以同时访问一个 String 对象,而不用担心它的内容会被其他线程修改。其次,字符串常量池的实现依赖于 String 的不可变性。因为字符串常量池中的对象内容不会改变,所以可以安全地被多个引用共享。另外,在 Java 的许多 API 中,方法的参数传递是值传递,如果 String 是可变的,那么在方法调用过程中可能会意外地修改传入的字符串参数,而不可变性避免了这种情况的发生。

Java 有哪些特性?

Java 有以下一些重要特性:

  • 面向对象编程(Object - Oriented Programming,OOP):Java 是一种纯粹的面向对象编程语言。一切皆对象,它支持封装、继承和多态这三大面向对象的基本特性。封装允许将数据和操作数据的方法组合在一起,隐藏对象的内部细节,只对外提供必要的接口。例如,在一个类中,将成员变量声明为 private,然后通过 public 的方法来访问和修改这些变量,这就是封装的体现。继承使得子类可以继承父类的属性和方法,从而实现代码的复用。比如,定义一个动物类作为父类,有吃和移动的方法,然后定义子类如猫类和狗类,它们可以继承动物类的这些基本方法,并且可以添加自己特有的行为。多态则允许不同的子类对象对同一方法的调用产生不同的行为,这是通过方法重写和动态绑定实现的。例如,在一个图形绘制程序中,有一个抽象的图形类,它有一个绘制方法,然后不同的图形子类(如圆形、矩形)可以重写这个绘制方法,根据自己的形状来实现具体的绘制行为。

  • 平台独立性(Write Once, Run Anywhere):Java 程序可以在不同的操作系统上运行,这是通过 Java 虚拟机(Java Virtual Machine,JVM)实现的。Java 代码被编译成字节码(.class 文件),字节码是一种中间形式的指令集。当运行 Java 程序时,字节码会被 JVM 解释执行或者即时编译(Just - In - Time compilation,JIT)成机器码来运行。不同的操作系统有对应的 JVM 实现,比如 Windows JVM、Linux JVM 和 Mac JVM 等。这样,只要在相应的操作系统上安装了合适的 JVM,相同的 Java 字节码就可以在不同的操作系统上运行,而不需要针对每个操作系统重新编写代码。

  • 自动内存管理(Automatic Memory Management):Java 有垃圾回收(Garbage Collection,GC)机制来自动管理内存。程序员不需要手动分配和释放内存,这大大减少了内存泄漏和悬空指针等问题。在 Java 中,当对象不再被引用时,垃圾回收器会自动检测并回收这些对象占用的内存空间。例如,在一个方法中创建了一个对象,当这个方法执行完毕后,如果这个对象没有被其他地方引用,垃圾回收器就会在适当的时候回收这个对象的内存。垃圾回收器使用了多种算法,如标记 - 清除算法、复制算法、标记 - 整理算法等,来有效地管理内存。

  • 安全性(Security):Java 提供了多种安全机制。在语言层面,它有严格的类型检查,防止不合法的类型转换和操作。例如,不能将一个整数直接赋值给一个字符串变量。在运行环境层面,Java 通过字节码验证来确保字节码的合法性。当字节码被加载到 JVM 时,JVM 会检查字节码是否符合 Java 语言规范,防止恶意篡改的字节码或者有安全隐患的字节码进入系统。同时,Java 还提供了安全管理器(Security Manager)来控制对系统资源的访问,如文件系统、网络等。只有经过授权的代码才能访问这些资源,这在网络应用和分布式系统中非常重要。

继承和抽象类的区别是什么?

  • 继承

    • 概念:继承是面向对象编程中的一种机制,它允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以直接使用父类中定义的非私有属性和方法,就好像这些属性和方法是子类自己定义的一样。例如,有一个父类 Animal,它有属性 name 和方法 eat (),当定义一个子类 Dog 继承自 Animal 时,Dog 类就自动拥有了 name 属性和 eat () 方法。这使得代码可以复用,减少重复代码的编写。
    • 实现方式:在 Java 中,通过使用 extends 关键字来实现继承。例如,class Dog extends Animal {...}。子类可以在继承父类的基础上添加自己的新属性和方法,也可以重写父类的方法来改变方法的行为。例如,Animal 类的 eat () 方法可能只是简单地打印 “动物在进食”,而 Dog 类可以重写这个方法,打印 “狗在啃骨头”。
    • 实例化:当创建子类的对象时,会先调用父类的构造函数来初始化从父类继承的部分,然后再初始化子类自己的部分。例如,当创建一个 Dog 对象时,会先调用 Animal 类的构造函数来初始化 name 属性等,然后再初始化 Dog 类特有的属性。
  • 抽象类

    • 概念:抽象类是一种不能被实例化的类,它主要用于作为其他类的基类(父类),提供一个通用的模板或者框架。抽象类中可以包含抽象方法和非抽象方法。抽象方法是没有具体实现的方法,只有方法的声明,它强制要求子类必须实现这些抽象方法。例如,定义一个抽象类 Shape,它有一个抽象方法 area (),这个方法没有具体的实现,因为不同的形状(如圆形、矩形)计算面积的方式不同。
    • 实现方式:在 Java 中,通过使用 abstract 关键字来定义抽象类和抽象方法。例如,abstract class Shape {... abstract double area (); }。抽象类可以有构造函数,主要用于子类在继承时初始化从抽象类继承的部分。
    • 使用场景:抽象类用于定义一组相关类的通用行为和属性,但又不希望这个类被直接实例化。它可以确保子类实现了某些特定的方法,从而保证了整个继承体系的一致性。例如,在一个图形绘制程序中,Shape 抽象类定义了图形的基本属性和绘制方法的框架,具体的图形类(如圆形、矩形)继承自 Shape 抽象类,实现自己的绘制方法和计算面积等方法,这样可以使整个图形绘制系统的代码结构更加清晰和易于维护。

MVP、MVC 和 MVVM 模式是什么?

  • MVC(Model - View - Controller)模式
    • Model(模型):它是应用程序中用于处理数据逻辑的部分。这包括数据的存储、检索以及业务规则的实现。例如,在一个用户管理系统中,Model 负责处理用户数据的存储(如数据库操作)和业务逻辑,像用户注册时的数据验证(检查用户名是否合法、密码强度是否足够等)。
    • View(视图):主要负责展示数据给用户。它是用户界面部分,通常是由 HTML、XML 等标记语言或者是 Android 中的布局文件等来构建。例如,在网页应用中,View 可能是一个用户注册页面的表单展示,包括用户名输入框、密码输入框等各种 UI 元素。
    • Controller(控制器):作为 Model 和 View 之间的桥梁。它接收用户在 View 上的操作(如点击按钮),然后根据这些操作来调用 Model 中的方法进行数据处理,再将处理后的结果反馈给 View 进行更新。例如,当用户在注册页面点击 “提交” 按钮,Controller 会获取 View 中的用户输入数据,交给 Model 进行验证和存储,之后将验证结果(成功或失败)返回给 View,让 View 展示相应的提示信息。
  • MVP(Model - View - Presenter)模式
    • Model(模型):功能和 MVC 中的 Model 类似,负责数据的存储和业务逻辑处理。比如在一个电商应用中,Model 负责商品数据的存储和价格计算等业务逻辑。
    • View(视图):主要负责展示数据和接收用户输入,但相比于 MVC,它更加 “被动”。在 MVP 中,View 通常定义了一系列接口,供 Presenter 调用,来更新 UI 或者获取用户输入。例如,在一个 MVP 架构的登录界面,View 只负责提供如 “显示登录错误信息” 这样的接口,而具体的错误信息内容由 Presenter 决定。
    • Presenter(主持人):这是 MVP 的核心部分。它负责从 Model 获取数据,然后将数据格式化后传递给 View 进行展示,同时处理用户在 View 上的操作并和 Model 交互。例如,在上述电商应用中,Presenter 会从 Model 获取商品列表数据,处理后传递给 View 展示商品信息,并且当用户在 View 中点击购买按钮时,Presenter 会调用 Model 中的购买逻辑进行处理。
  • MVVM(Model - View - ViewModel)模式
    • Model(模型):同样负责数据的存储和业务逻辑,和前面模式中的 Model 功能基本一致。例如,在一个金融应用中,Model 负责存储用户账户余额、交易记录等数据并进行相关业务计算。
    • View(视图):专注于 UI 展示,它和 MVVM 中的 ViewModel 是数据绑定的关系。通过数据绑定,View 可以自动更新 UI 元素,当 ViewModel 中的数据发生变化时,与之绑定的 View 部分会自动更新。例如,在一个 MVVM 架构的天气应用中,当 ViewModel 中的天气温度数据改变时,View 中的温度显示部分会自动更新。
    • ViewModel(视图模型):它是连接 Model 和 View 的桥梁。ViewModel 对 Model 中的数据进行加工处理,使其更适合 View 展示。同时,它实现了数据的双向绑定,不仅可以将 Model 数据传递给 View,还可以将 View 中的用户输入数据传递给 Model。例如,在上述金融应用中,ViewModel 可以将 Model 中的账户余额数据格式化为带有货币符号的形式传递给 View 展示,并且当用户在 View 中输入转账金额时,ViewModel 会将这个数据传递给 Model 进行转账操作。

内存泄漏的原因是什么?如何解决?

  • 内存泄漏的原因
    • 长生命周期对象引用短生命周期对象:当一个长生命周期的对象持有一个短生命周期对象的引用时,短生命周期对象无法被垃圾回收。例如,在一个 Activity 中定义了一个内部类,这个内部类可能会持有 Activity 的引用。如果这个内部类被一个静态变量引用,或者被一个长生命周期的对象引用,那么当 Activity 应该被销毁时,由于这个引用关系,Activity 无法被垃圾回收,导致内存泄漏。比如,在一个网络请求的回调中,如果这个回调是一个内部类,并且在网络请求完成后,回调对象仍然存在,且持有 Activity 引用,就会出现这种情况。
    • 资源未正确释放:比如在使用数据库连接、文件流或者网络连接等资源时,如果没有正确地关闭这些资源,会导致内存泄漏。以文件流为例,当打开一个文件读取数据后,如果没有关闭文件流,那么这个文件流所占用的内存资源就无法被释放,并且可能会导致文件无法被其他程序正确访问。
    • 静态集合类的不合理使用:如果将对象添加到静态集合中,并且没有在合适的时候从集合中移除这些对象,那么这些对象会一直存在于内存中。例如,一个静态的 List 集合,用于存储一些临时的业务对象。如果不断地向这个集合添加对象,而没有清除不再需要的对象,就会导致内存泄漏,因为静态集合的生命周期是和应用程序一样长的。
  • 解决内存泄漏的方法
    • 合理管理对象引用:对于长生命周期对象引用短生命周期对象的情况,可以使用弱引用或者软引用。弱引用在垃圾回收时,如果对象只有弱引用,那么这个对象就会被回收。软引用则是在内存不足时,对象会被回收。在 Android 开发中,对于一些可能导致 Activity 内存泄漏的内部类,可以使用静态内部类,并通过 WeakReference 来引用 Activity。
    • 正确释放资源:在使用资源时,要确保在合适的位置关闭资源。比如,使用 try - finally 块来保证文件流的关闭。在打开文件流后,将关闭文件流的操作放在 finally 块中,这样无论在读取文件过程中是否出现异常,文件流都会被关闭。对于数据库连接和网络连接等资源,也需要在使用完毕后及时关闭,可以通过相应的关闭方法来实现。
    • 谨慎使用静态集合:如果使用静态集合,要确保在对象不再需要时从集合中移除。可以在对象的生命周期结束时,例如在对象的析构函数或者合适的清理方法中,将对象从静态集合中移除。另外,也可以考虑使用一些具有自动清理功能的集合类,或者限制静态集合的大小,当集合达到一定大小后,根据一定的规则清理其中的对象。

Handler 机制的原理是什么?

在 Android 中,Handler 机制主要用于在不同线程之间进行通信,特别是用于更新 UI 线程。

  • 基本组成部分

    • Handler(处理器):它是主要的接口,用于发送和处理消息。可以通过 Handler 的 sendMessage () 方法发送消息,并且可以重写 handleMessage () 方法来处理接收到的消息。例如,在一个工作线程中,当完成一个耗时任务后,可以通过 Handler 发送一个消息到 UI 线程,告知任务完成。
    • Message(消息):这是在 Handler 机制中传递的信息载体。它包含了消息的内容,如数据、消息的类型等。消息可以携带简单的数据,如整数、字符串等,也可以是复杂的对象。例如,一个消息可以携带从网络请求中获取到的用户信息,用于在 UI 线程中更新用户界面。
    • MessageQueue(消息队列):它是一个存储消息的队列,按照消息发送的时间顺序来存储消息。消息队列是一个先进先出(FIFO)的结构。当通过 Handler 发送消息时,消息会被添加到消息队列中。例如,多个工作线程可能会同时发送消息到 UI 线程的消息队列,这些消息会按照发送的先后顺序排队等待处理。
    • Looper(循环器):它是一个循环,用于不断地从消息队列中取出消息,并将消息分发给对应的 Handler 进行处理。在 UI 线程中,默认已经有一个 Looper 在运行,它会一直循环,直到应用程序结束。例如,当一个消息被添加到 UI 线程的消息队列后,Looper 会检测到这个消息,然后将消息传递给对应的 Handler 进行处理。
  • 工作流程

    • 首先,Handler 和 Looper 是关联在一起的。在创建 Handler 时,通常会绑定一个 Looper。在 UI 线程中,系统已经自动创建了 Looper,而在其他线程中,如果要使用 Handler,需要先创建 Looper。当通过 Handler 发送消息时,消息会被添加到与 Handler 关联的消息队列中。这个消息队列由 Looper 管理。然后,Looper 会不断地从消息队列中取出消息,通过消息的 target(即发送消息的 Handler)来调用 Handler 的 handleMessage () 方法处理消息。这样就实现了在不同线程之间通过消息传递进行通信的目的。例如,在一个后台线程中下载图片,下载完成后,通过 Handler 发送一个带有图片数据的消息到 UI 线程的消息队列,UI 线程的 Looper 取出这个消息,交给对应的 Handler,Handler 在 handleMessage () 方法中更新 UI,显示下载好的图片。

如何实现 ViewGroup 拦截某个事件?

在 Android 中,ViewGroup 是一个可以包含多个子 View 的视图容器,事件拦截是其重要的功能之一。

  • 事件拦截方法:ViewGroup 中有一个 onInterceptTouchEvent () 方法,这个方法用于拦截触摸事件。当触摸事件传递到 ViewGroup 时,会首先调用这个方法来判断 ViewGroup 是否要拦截这个事件。如果 onInterceptTouchEvent () 方法返回 true,那么后续的触摸事件(如 MOVE、UP 事件)就不会再传递给子 View,而是由 ViewGroup 自己来处理。例如,在一个自定义的 ViewGroup 布局中,包含了多个按钮。如果想要实现当用户在 ViewGroup 的某个区域滑动时,不触发子按钮的点击事件,就可以在 ViewGroup 的 onInterceptTouchEvent () 方法中,根据触摸事件的位置和滑动距离等条件,判断是否拦截事件。

  • 实现步骤

    • 第一步:继承 ViewGroup 类:创建一个自定义的 ViewGroup,比如命名为 CustomViewGroup。在这个类中,需要重写 onInterceptTouchEvent () 方法来实现事件拦截。
    • 第二步:判断拦截条件:在 onInterceptTouchEvent () 方法中,可以获取触摸事件的各种参数,如触摸点的坐标、事件的类型(DOWN、MOVE、UP)等。根据这些参数来判断是否满足拦截条件。例如,如果想要拦截水平方向的滑动事件,可以在 MOVE 事件中,计算触摸点的水平位移,如果水平位移超过一定的阈值,就返回 true,表示拦截事件。
    • 第三步:处理拦截后的事件:当 onInterceptTouchEvent () 方法返回 true 后,后续的触摸事件会传递到 ViewGroup 的 onTouchEvent () 方法中。在这个方法中,可以实现自己想要的事件处理逻辑。例如,在拦截了水平滑动事件后,可以在 onTouchEvent () 方法中实现一个水平滚动的效果,就像在一个水平滚动的列表视图中一样。

OkHttp 的拦截器是如何工作的(拦截器责任链)?

OkHttp 中的拦截器是按照责任链模式工作的,它允许在网络请求和响应的过程中进行一系列的拦截和处理。

  • 拦截器的类型和作用

    • 应用层拦截器(Application Interceptors):这些拦截器是由应用程序开发者添加的,用于处理和修改请求或响应的内容。例如,可以在应用层拦截器中添加公共的请求头,如认证信息、用户代理等。应用层拦截器在网络请求被发送之前和响应被接收之后进行操作,它不关心网络连接的细节,只关注应用层的请求和响应数据。
    • 网络层拦截器(Network Interceptors):网络层拦截器主要用于处理网络连接相关的问题。它可以在请求被发送到服务器之前和响应从服务器返回之后,对请求和响应进行操作,比如可以对请求进行重试、缓存处理等。网络层拦截器可以看到更底层的网络信息,如 HTTP 协议的细节、连接池的使用等。
  • 责任链的形成和工作流程

    • 请求阶段:当发起一个网络请求时,请求首先会进入一个拦截器链。这个链是由多个拦截器按照添加的顺序组成的。请求会依次通过每个拦截器的 intercept () 方法。在每个拦截器的 intercept () 方法中,可以对请求进行修改,如添加请求头、修改请求体等。然后,拦截器可以决定是继续将请求传递给下一个拦截器,还是直接返回一个响应。例如,第一个应用层拦截器可能会添加一个自定义的请求头,然后将修改后的请求传递给下一个网络层拦截器。
    • 响应阶段:当服务器返回响应后,响应会沿着拦截器链反向传递。每个拦截器可以对响应进行处理,如解析响应头、修改响应体等。例如,一个网络层拦截器可能会检查响应的状态码,如果状态码表示需要重试,就可以重新发起请求。最后,经过所有拦截器处理后的响应会被返回给调用者。这样,通过拦截器责任链,OkHttp 可以灵活地处理网络请求和响应的各个环节,实现诸如缓存、认证、重试等多种功能。

Retrofit2 的原理是什么?它使用了哪些设计模式?

Retrofit2 是一个用于 Android 和 Java 的类型安全的 HTTP 客户端。

从原理上讲,Retrofit2 的核心是通过动态代理来创建网络请求接口的实例。当定义一个接口并使用 Retrofit 的注解来描述 HTTP 请求(如 @GET、@POST 等)时,Retrofit 会在运行时为这个接口生成一个动态代理类。这个代理类会将接口方法的调用转换为实际的 HTTP 请求操作。在内部,Retrofit 依赖于 OkHttp 来进行网络通信。它会将接口方法上的注解信息(包括请求方法、请求路径、请求参数等)解析出来,构建一个 OkHttp 的请求对象。当调用接口方法时,就相当于通过 OkHttp 发送一个网络请求。例如,假设有一个接口定义了一个获取用户信息的方法,注解为 @GET 并指定了请求路径,Retrofit 会把这个方法调用转换为一个向相应路径发送 GET 请求的操作。

在设计模式方面,Retrofit2 运用了多种模式。首先是代理模式,通过动态代理来处理接口方法调用,使得开发者可以像调用本地方法一样轻松地进行网络请求。其次是建造者模式,在构建 Retrofit 实例以及请求对象时,使用了建造者模式。例如,通过 Retrofit.Builder 来配置 Retrofit 的各种属性,如 BaseUrl、Converter 等,这种链式调用的方式方便用户根据自己的需求构建合适的 Retrofit 对象。另外,它还使用了工厂模式,通过 Converter.Factory 来创建不同类型的数据转换器,用于将网络响应的数据转换为 Java 对象,方便开发者使用。这些设计模式的运用使得 Retrofit2 具有高度的灵活性和可扩展性,方便开发者高效地进行网络请求操作。

图片下载是如何实现的?Glide 库有哪些特点?

图片下载的实现方式

在 Android 中,图片下载通常涉及到网络请求和本地存储。首先,需要通过网络请求从服务器获取图片数据。这可以使用 HttpURLConnection 或者 OkHttp 等网络库来发送请求。以 OkHttp 为例,通过构建一个请求对象,发送 GET 请求到图片的 URL 地址,然后获取响应的输入流,这个输入流包含了图片的二进制数据。之后,将获取到的二进制数据转换为 Bitmap 或者其他适合存储和显示的图片格式。如果要将图片保存到本地,可以通过创建文件输出流,将二进制数据写入本地文件。在显示图片方面,将获取到的 Bitmap 对象设置到 ImageView 等视图组件中进行展示。

Glide 库的特点

Glide 是一个功能强大的图片加载库。它具有高效的缓存机制,会在内存和磁盘上对图片进行缓存。当加载一张图片时,首先会检查内存缓存,如果内存缓存中有这张图片,就直接从内存中获取并显示,这样可以快速地加载已经访问过的图片,提高加载速度。如果内存缓存中没有,就会检查磁盘缓存,从磁盘中读取图片并加载到内存缓存中。

Glide 支持多种图片格式,包括常见的 JPEG、PNG、GIF 等,并且可以很好地处理动态图片,如 GIF 的加载和播放。它还具有生命周期感知能力,能够根据 Activity 或者 Fragment 的生命周期自动管理图片加载任务。例如,当 Activity 进入暂停状态时,Glide 会暂停图片加载,避免浪费资源和可能出现的内存泄漏;当 Activity 恢复时,又会自动继续加载图片。

另外,Glide 提供了丰富的转换功能,如图片的裁剪、缩放、圆形处理等。可以通过简单的配置来对图片进行各种自定义的转换操作,以满足不同的 UI 设计需求。

Activity 的生命周期是怎样的?

Activity 的生命周期包含了一系列的回调方法,这些方法在 Activity 的不同状态转换时被调用。

首先是 onCreate () 方法,这个方法在 Activity 被创建时调用。在这里可以进行一些初始化的操作,如设置布局、初始化视图组件、绑定数据等。例如,通过 setContentView () 方法加载布局文件,初始化在布局文件中定义的按钮、文本框等组件的属性。

当 Activity 变得可见但还没有获取焦点时,会调用 onStart () 方法。此时,Activity 已经对用户可见,但是还不能与用户进行交互,比如用户还不能点击按钮或者输入文字。

接着是 onResume () 方法,在 Activity 获取焦点并且可以和用户交互时被调用。在这个阶段,Activity 处于前台运行状态,用户可以进行各种操作,如点击按钮、滚动屏幕等。

当有其他 Activity 进入前台,导致当前 Activity 失去焦点但仍然可见时,会调用 onPause () 方法。在这个阶段,可以进行一些轻量级的暂停操作,比如暂停动画、保存一些临时数据等。例如,一个音乐播放 Activity,在 onPause () 时可以暂停音乐播放。

如果 Activity 完全不可见了,就会调用 onStop () 方法。此时可以释放一些资源,如停止网络请求、关闭数据库连接等。

当 Activity 重新回到前台时,会依次调用 onRestart ()、onStart () 和 onResume () 方法。

最后,当 Activity 被销毁时,会调用 onDestroy () 方法,在这里可以进行最后的资源清理工作,如释放内存中的对象等。

onPause 和 onStop 的区别是什么?请举例说明。

onPause 和 onStop 这两个方法在 Activity 生命周期中都表示 Activity 状态的改变,但它们有明显的区别。

onPause

onPause () 方法在 Activity 失去焦点但仍然部分可见时被调用。它主要用于处理一些轻量级的暂停操作。例如,当一个 Activity A 启动了另一个 Activity B 时,Activity A 的 onPause () 方法会被调用。在这个阶段,Activity A 仍然可以被用户看到一部分。比如,在一个视频播放应用中,当用户打开一个视频详情页面(Activity B)时,视频播放列表页面(Activity A)的 onPause () 方法会被调用。此时可以暂停视频的自动播放,但视频的缩略图等信息仍然可以在屏幕上看到。这是因为 onPause () 只是表示 Activity 失去了焦点,并不是完全不可见。

onStop

onStop () 方法是在 Activity 完全不可见时被调用。它表示 Activity 已经不在前台,可能被其他 Activity 完全覆盖或者被系统回收。继续以上面的视频播放应用为例,当用户在视频详情页面(Activity B)中按下返回键,关闭 Activity B 回到视频播放列表页面(Activity A)时,如果 Activity A 之前因为启动 Activity B 而调用了 onStop () 方法,那么此时会重新调用 onStart () 和 onResume () 方法来恢复 Activity A 的可见和可交互状态。在 onStop () 阶段,可以进行一些资源释放的操作。例如,在一个地图导航应用中,当 Activity 完全不可见时,在 onStop () 中可以停止地图的更新,释放地图相关的一些资源,因为用户此时看不到地图,不需要这些资源继续运行。

Activity 间如何传递数据(Intent)?

在 Android 中,通过 Intent 可以在 Activity 之间传递数据。

一种常见的方式是使用 Intent 的 putExtra () 方法。当启动一个新的 Activity 时,可以先创建一个 Intent 对象,通过 putExtra () 方法添加需要传递的数据。例如,要传递一个字符串类型的数据,可以使用 intent.putExtra ("key", "value"),其中 “key” 是用于在接收方 Activity 中获取数据的标识符,“value” 是要传递的字符串。在接收方 Activity 中,可以通过 getIntent () 方法获取启动它的 Intent,然后使用相应的 get 方法来获取数据。比如,在接收方 Activity 的 onCreate () 方法中,可以使用 String value = getIntent ().getStringExtra ("key") 来获取传递过来的字符串。

除了传递基本数据类型,还可以传递自定义的对象。不过,自定义对象需要实现 Serializable 或者 Parcelable 接口。以实现 Serializable 接口为例,先将对象标记为可序列化,然后在发送方 Activity 中,通过 intent.putExtra ("key", object) 将对象放入 Intent 中传递。在接收方 Activity 中,同样通过 getIntent ().getSerializableExtra ("key") 来获取传递过来的对象。

另外,还可以使用 Bundle 来传递数据。Bundle 是一个可以存储多种数据类型的容器。可以将数据放入 Bundle 中,然后通过 Intent 的 putExtras () 方法将 Bundle 添加到 Intent 中进行传递。在接收方 Activity 中,通过 getIntent ().getExtras () 获取 Bundle,再从 Bundle 中取出数据。这种方式在需要传递多个数据或者复杂数据结构时比较方便。

如果网络请求失败了,应该如何处理?

当网络请求失败时,可以从多个方面进行处理。

首先是用户体验方面,要给用户一个友好的反馈。可以通过在界面上显示一个提示信息,告知用户网络请求出现问题,比如弹出一个 Toast 或者在页面的某个位置显示一段文字,像 “网络连接失败,请检查网络设置”。这样用户能够清楚地知道请求没有成功的原因可能是网络问题。

从技术角度,可以进行重试机制。判断请求失败的原因,如果是因为网络波动、临时性的网络故障等原因导致的失败,可以尝试重新发送请求。但是要注意控制重试的次数,避免无限重试造成资源浪费或者对服务器造成过大压力。可以设定一个合理的重试次数,比如 3 次或 5 次。每次重试之间还可以设置一个适当的间隔时间,例如每次重试间隔 1 - 2 秒,这样可以给网络恢复一些时间。

还可以对失败的请求进行记录。将请求的相关信息,如请求的 URL、请求的时间、失败的原因等记录到本地文件或者日志系统中。这样有助于后续的问题排查,开发人员可以通过查看这些记录来分析是网络环境问题、服务器问题还是代码本身的问题导致的请求失败。

另外,如果请求失败是因为服务器返回的错误信息,比如 404(资源未找到)、500(服务器内部错误)等,可以根据不同的错误码来进行针对性的处理。对于 404 错误,可以提示用户请求的资源不存在;对于 500 错误,可以考虑过一段时间再重试,因为可能是服务器暂时出现故障。

GET 和 POST 请求的区别是什么?GET 能做的 POST 都能做吗?

区别

  • 请求参数的传递方式

    • GET 请求的参数是通过 URL 传递的,参数会显示在浏览器的地址栏中。例如,在访问一个搜索页面时,搜索关键词会以 “?key=value” 的形式附加在 URL 后面,像 “https://example.com/search?keyword=java”,其中 “keyword=java” 就是传递的参数。这种方式使得参数对用户可见,并且参数的长度受到 URL 长度的限制。
    • POST 请求的参数是放在请求体(Request Body)中传递的,不会在 URL 中显示。这样可以隐藏参数内容,适合传递一些敏感信息,如用户的密码等,而且参数的大小理论上没有像 GET 请求那样受到严格的 URL 长度限制。
  • 请求的语义和用途

    • GET 请求主要用于获取数据,它是幂等的,即多次执行相同的 GET 请求应该返回相同的结果,不会对服务器上的数据产生修改。例如,查询一篇文章的内容、获取用户列表等场景适合使用 GET 请求。
    • POST 请求通常用于提交数据,会对服务器上的数据产生影响,如创建新的用户、提交订单等操作。它不是幂等的,多次执行相同的 POST 请求可能会在服务器端产生多个不同的结果。
  • 缓存方面

    • GET 请求的结果可以被浏览器和中间代理服务器缓存。因为 GET 请求主要是获取数据,缓存机制可以提高数据的访问效率。例如,浏览器可以缓存一张图片的 GET 请求结果,下次访问相同的图片 URL 时,直接从缓存中获取,减少网络请求。
    • POST 请求一般不会被缓存,因为它可能会对服务器的数据产生修改,缓存 POST 请求的结果可能会导致数据不一致等问题。

GET 能做的 POST 都能做吗?

从功能角度讲,POST 请求在一定程度上可以模拟 GET 请求的功能。因为 POST 请求也可以用来获取数据,只是这种用法不太符合它的语义。但是,由于浏览器和服务器对 GET 和 POST 请求有不同的处理方式,GET 请求的一些特性 POST 请求无法完全替代。例如,GET 请求能够利用浏览器和代理服务器的缓存机制来提高性能,POST 请求很难做到这一点。而且,GET 请求因为参数在 URL 中,更方便用户分享链接,用户可以直接复制带有参数的 URL 发送给其他人,POST 请求则不便于这样的操作。

HTTP 响应码的含义是什么?

HTTP 响应码是服务器对客户端请求的响应状态的标识,它是一个三位数字的代码。

  • 1xx(信息性状态码)

    • 这组响应码主要是提供信息,表示请求已经被接收,正在处理中。例如,100 Continue,当客户端发送一个包含较大请求体的请求时,先发送部分请求头,服务器返回 100 Continue 表示可以继续发送请求体,这有助于提高性能,避免客户端发送不必要的请求体。
  • 2xx(成功状态码)

    • 这是最常见的成功响应码类别。其中 200 OK 表示请求成功,服务器已经成功处理了客户端的请求,并返回了请求的数据。例如,当客户端发送一个 GET 请求获取一篇文章内容,服务器返回 200 OK,并在响应体中包含文章内容。
    • 201 Created 用于表示请求成功并且在服务器上创建了新的资源。比如,当客户端发送一个 POST 请求创建一个新用户,服务器成功创建用户后返回 201 Created。
  • 3xx(重定向状态码)

    • 这些响应码表示客户端需要进行额外的操作才能完成请求。301 Moved Permanently 表示请求的资源已经永久移动到了新的位置,客户端在收到这个响应码后,应该更新书签或者记录新的 URL,下次请求时直接访问新的 URL。
    • 302 Found 表示资源临时移动,客户端在收到这个响应码后,会临时重定向到新的位置,下次请求时还是会尝试访问原来的 URL。
  • 4xx(客户端错误状态码)

    • 400 Bad Request 表示客户端发送的请求有语法错误或者参数不合法。例如,请求的格式不符合服务器要求,或者缺少必要的参数。
    • 401 Unauthorized 表示客户端没有权限访问请求的资源,通常需要进行身份认证才能访问。比如,用户没有登录就尝试访问需要登录才能查看的页面时,服务器会返回 401 Unauthorized。
    • 403 Forbidden 表示服务器理解客户端的请求,但是拒绝执行。这和 401 Unauthorized 的区别在于,403 可能是因为用户权限不足,即使进行身份认证也无法访问,比如用户没有足够的权限访问某些管理页面。
    • 404 Not Found 是比较常见的响应码,表示服务器无法找到客户端请求的资源。可能是因为 URL 错误或者资源已经被删除。
  • 5xx(服务器错误状态码)

    • 500 Internal Server Error 表示服务器在处理请求时遇到了内部错误。这可能是因为服务器代码出现问题、数据库故障等原因导致无法正常处理请求。
    • 502 Bad Gateway 通常是因为服务器作为网关或者代理,从上游服务器获取响应时出现错误。例如,服务器在转发请求到另一个服务器时,收到了无效的响应。
    • 503 Service Unavailable 表示服务器目前无法处理请求,可能是因为服务器过载、维护等原因。服务器会在一段时间后恢复正常服务,客户端可以在适当的时候重试请求。

CPU 密集情况下,线程池的核心线程数量应如何分配?

在 CPU 密集型任务场景下,线程池核心线程数量的分配主要考虑 CPU 的核心数。

理想情况下,线程池的核心线程数量应该等于 CPU 的核心数。这是因为 CPU 密集型任务主要是依赖 CPU 的计算能力来执行任务。如果核心线程数量超过 CPU 核心数,会导致过多的线程竞争 CPU 资源,增加线程上下文切换的开销。例如,假设有一个四核 CPU,同时有 8 个线程在竞争 CPU 资源,CPU 需要频繁地在这些线程之间切换,保存和恢复线程的执行状态,这会浪费大量的 CPU 时间在上下文切换上,而不是真正用于执行任务。

然而,在实际情况中,还需要考虑一些其他因素。如果任务中有一些可能会被阻塞的操作,比如等待 I/O 操作完成,即使是 CPU 密集型任务,也可以适当增加核心线程数量。但增加的数量也不宜过多,一般可以根据任务的具体情况,通过性能测试来确定一个合适的比例。例如,在一些复杂的计算任务中,可能会偶尔需要读取文件中的数据作为计算参数,这种情况下可以适当增加 1 - 2 个核心线程。

另外,还需要考虑系统的负载情况和任务的优先级。如果系统还有其他非 CPU 密集型任务需要同时处理,或者有高优先级的任务可能会抢占 CPU 资源,那么需要根据实际情况对核心线程数量进行调整。可以通过监控系统的 CPU 使用率、任务的执行时间等指标,来动态地调整线程池的核心线程数量,以达到最优的性能。

Android 的事件分发机制是怎样的?

Android 的事件分发机制主要涉及三个重要的方法:dispatchTouchEvent ()、onInterceptTouchEvent () 和 onTouchEvent (),它们存在于 ViewGroup 和 View 这两个主要的组件中。

  • ViewGroup 中的事件分发

    • 当一个触摸事件发生时,首先会传递到最顶层的 ViewGroup 的 dispatchTouchEvent () 方法。这个方法负责将事件分发给它的子 View 或者自己处理。它会按照一定的顺序遍历子 View,通过判断触摸点的位置等来决定将事件传递给哪个子 View。例如,在一个线性布局(LinearLayout)中,如果触摸点在某个子按钮的范围内,就会尝试将事件传递给这个按钮。
    • 在分发过程中,ViewGroup 可以通过 onInterceptTouchEvent () 方法来拦截事件。如果这个方法返回 true,那么后续的触摸事件(如 MOVE、UP 事件)就不会再传递给子 View,而是由 ViewGroup 自己来处理。比如,当实现一个可滑动的 ViewGroup 时,如果检测到用户的滑动操作符合一定的条件,就可以拦截事件,自己处理滑动逻辑。
  • View 中的事件处理

    • 当事件传递到 View 后,会调用 View 的 dispatchTouchEvent () 方法。这个方法会首先判断 View 是否设置了 OnTouchListener,如果设置了,会先调用 OnTouchListener 的 onTouch () 方法。如果 onTouch () 方法返回 true,那么 View 的 onTouchEvent () 方法就不会被调用,事件被 OnTouchListener 消费掉。
    • 如果没有设置 OnTouchListener 或者 OnTouchListener 的 onTouch () 方法返回 false,就会调用 View 的 onTouchEvent () 方法来处理事件。在 onTouchEvent () 方法中,会根据事件的类型(如点击、长按等)进行相应的处理。例如,对于一个按钮,在 onTouchEvent () 方法中会判断是否是点击事件,如果是,就会触发按钮的点击逻辑,执行点击事件的回调方法。
  • 事件传递的顺序

    • 事件的传递是从最外层的 ViewGroup 开始,一层一层地向内传递,直到找到能够处理事件的 View。如果在传递过程中某个 ViewGroup 拦截了事件,那么事件就会在这个 ViewGroup 中处理,不再继续向子 View 传递。而且,触摸事件是按照 DOWN、MOVE、UP 的顺序依次传递的,DOWN 事件用于确定初始的触摸点和接收事件的 View,MOVE 事件用于跟踪触摸点的移动,UP 事件用于确定触摸操作的结束,如点击操作的抬起手指动作。

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

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

相关文章

【Linux系列】命令行中的文本处理:从中划线到下划线与大写转换

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

不一样的CSS(一)

目录 前言: 一、规则图形 1.介绍: 2.正方形与长方形(实心与空心) 2.1正方形: 2.2长方形 3.圆形与椭圆形(空心与实心) 3.1圆形与椭圆形 4.不同方向的三角形 4.1原理 4.2边框属性 5.四…

HPM6750EVK2开发板程序烧录测试

对于HPM6750EVK2开发板,官方板子上没有板载调试器,从淘宝上购买了一个,据说配套的调试器,进行测试,仅此进行记录。 开发板HPM6750EVK2 openocd调试器图片 openocd调试器,淘宝链接 http://e.tb.cn/h.TZH7b…

斐波那契数的第n个数代码分享(c基础)

1&#xff1a;迭代 //斐波那契数的第n个数 #include<stdio.h> //unsigned long long Fib(n) //{ // // if (1 n || 2 n) // return 1; // else return Fib((n - 1) Fib((n - 2); // // //} unsigned long long Fib(n) {if (n 1 || n 2)return 1;else{int j 3;u…

测试实项中的偶必现难测bug--一键登录失败

问题描述:安卓和ios有出现部分一键登录失败的场景,由于场景比较极端,衍生了很多不好评估的情况。 产生原因分析: 目前有解决过多次这种行为的问题,每次的产生原因都有所不同,这边根据我个人测试和收集复现的情况列举一些我碰到的: 1、由于我们调用的是友盟的一键登录的…

私域流量圈层在新消费时代的机遇与挑战:兼论开源 AI 智能名片、2 + 1 链动模式、S2B2C 商城小程序的应用

摘要&#xff1a;本文剖析了私域流量圈层在新消费时代呈现出的独特温度与信任优势&#xff0c;阐述了从传统销售到新消费转型中用户心理的变化。同时&#xff0c;强调了内容对于私域流量的关键作用&#xff0c;并分析开源 AI 智能名片、2 1 链动模式、S2B2C 商城小程序在私域流…

WPF之iconfont(字体图标)使用

1&#xff0c;前文&#xff1a; WPF的Xaml是与前端的Html有着高度相似性的标记语言&#xff0c;所以Xaml也可同Html一般轻松使用阿里提供的海量字体图标&#xff0c;从而有效的减少开发工作度。 2&#xff0c;下载字体图标&#xff1a; 登录阿里图标库网iconfont-阿里巴巴矢量…

[Meachines] [Medium] MonitorsThree SQLI+Cacti-CMS-RCE+Duplicati权限提升

信息收集 IP AddressOpening Ports10.10.11.30TCP:22&#xff0c;80 $ nmap -p- 10.10.11.30 --min-rate 1000 -sC -sV -Pn PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) | …

AndroidStudio-文本显示

一、设置文本的内容 1.方式&#xff1a; &#xff08;1&#xff09;在XML文件中通过属性&#xff1a;android:text设置文本 例如&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andr…

pyspark入门基础详细讲解

1.前言介绍 学习目标&#xff1a;了解什么是Speak、PySpark&#xff0c;了解为什么学习PySpark&#xff0c;了解课程是如何和大数据开发方向进行衔接 使用pyspark库所写出来的代码&#xff0c;既可以在电脑上简单运行&#xff0c;进行数据分析处理&#xff0c;又可以把代码无缝…

uniapp上拉刷新下拉加载

方法一&#xff1a; z-paging 的组件库&#xff1a; show-loading-more-no-more-view"false" 该属性控制是否显示 "加载更多" 或 "没有更多" 的提示。如果设为 false&#xff0c;则不会显示这些提示。如果设为 true&#xff0c;当数据加载完毕…

CSS教程(二)- CSS选择器

1. 作用 匹配文档中的某些元素为其应用样式。根据不同需求把不同的标签选出来。 2. 分类 分类 基础选择器 包含 标签选择器、ID选择器、类选择器、通用选择器等 复合选择器 包含 后代选择器、子代选择器、伪类选择器等 1 标签选择器 介绍 又称为元素选择器&#xff0c;根…

LeetCode 56.合并区间

思路&#xff1a; 类似于用最少的箭射气球题目&#xff0c;最主要是要处理区间之间是否有重叠&#xff0c;如果无重叠则加入数组&#xff0c;如果有重叠&#xff0c;则需要重新设判断的边界&#xff0c;与下一个区间继续判断。 难点在于 代码用法 需熟练掌握 思想简单&#…

【MySQL】MySQL基础知识复习(上)

前言 本篇博客将复习MySQL的基础知识&#xff0c;及着重复习CRUD&#xff08;增删查改&#xff09;操作。 目录 一.MySQL数据库基础知识 1.数据库操作 1.1显示当前的数据库 1.2 创建数据库 1.3 使用数据库 1.4 删除数据库 2.数据类型 2.1.数字类型 2.2字符串类型 2.3…

华为大变革?仓颉编程语言会代替ArkTS吗?

在华为鸿蒙生态系统中&#xff0c;编程语言的选择一直是开发者关注的焦点。近期&#xff0c;华为推出了自研的通用编程语言——仓颉编程语言&#xff0c;这引发了关于仓颉是否会取代ArkTS的讨论。本文将从多个角度分析这两种语言的特点、应用场景及未来趋势&#xff0c;探讨仓颉…

稀硫酸介质中 V 型球阀的材质选择与选型要点-耀圣

稀硫酸介质中 V 型球阀的材质选择与选型要点 在工业生产中&#xff0c;稀硫酸是一种常见的化学介质&#xff0c;对于输送和控制稀硫酸的阀门&#xff0c;正确的材质选择和选型至关重要。本文将介绍稀硫酸介质中 V 型球阀的材质选择&#xff0c;并提供一些选型的要点。 一、稀硫…

昇思大模型平台打卡体验活动:项目3基于MindSpore的GPT2文本摘要

昇思大模型平台打卡体验活动&#xff1a;项目3基于MindSpore的GPT2文本摘要 1. 环境设置 本项目可以沿用前两个项目的相关环境设置。首先&#xff0c;登陆昇思大模型平台&#xff0c;并进入对应的开发环境&#xff1a; https://xihe.mindspore.cn/my/clouddev 接着&#xff0…

定时器输入捕获实验配置

首先&#xff0c;第一个时基工作参数配置 HAL_TIM_IC_Init( ) 还是一样的套路&#xff0c;传参是一个句柄&#xff0c;先定义一个结构体 Instance&#xff1a;指向TIM_TypeDef的指针&#xff0c;表示定时器的实例。TIM_TypeDef是一个包含了定时器寄存器的结构体&#xff0c;用…

计算机视觉读书系列(1)——基本知识与深度学习基础

研三即将毕业&#xff0c;后续的工作可能会偏AI方向的计算机视觉方面&#xff0c;因此准备了两条线来巩固计算机视觉基础。 一个是本系列&#xff0c;阅读经典《Deep Learning for Vision System》&#xff0c;做一些总结跑一些例子&#xff0c;也对应本系列文章 二是OpenCV实…

运维智能化转型:AIOps引领IT运维新浪潮

1. AIOps是什么&#xff1f; AIOps&#xff08;Artificial Intelligence for IT Operations&#xff09;&#xff0c;即人工智能在IT运维中的应用&#xff0c;通过机器学习技术处理运维数据&#xff08;如日志、监控信息和应用数据&#xff09;&#xff0c;解决传统自动化运维…