-
为什么说Java是平台无关的呢?
- 代码的执行不依赖于操作系统
-
Java 异常处理中‘throw’和‘throws’之间的区别?
throw
关键字用于从任何方法或静态块抛出异常,而throws
用于指示此方法可能抛出哪种异常。
-
是否存在我们可以跳过 try catch 中的 finally 块的情况?
- 通过在 try 或 catch 块中调用 System.exit(0),我们可以跳过 finally 块。System.exit(int) 方法可以抛出 SecurityException。如果 System.exit(0) 退出 JVM 时没有抛出该异常,则 finally 块将不会执行。但是,如果 System.exit(0) 确实抛出了安全异常,则将执行 finally 块。
-
什么是匿名类?
- 匿名类正如其名称所暗示的那样——它没有名称。它将类声明和类实例的创建合并为一个步骤。由于匿名类没有名称,因此无法从定义匿名类的类外部实例化对象。事实上,匿名对象只能从定义它的同一范围内实例化。
- 规则:
- 匿名类必须始终扩展超类或实现接口,但不能有显式的扩展或实现子句。
- 匿名类必须实现超类或接口中的所有抽象方法。
- 匿名类始终使用超类的默认构造函数来创建实例。
- 例子:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>MyButton.setOnClickListener(new Button.OnClickListener {@overridepublic void onClick(View view){//some code}}); </code></span></span></span>
-
为什么java中的main方法是静态的?
- 该方法为静态方法,因为否则,对于要调用哪个方法会产生歧义。如果从主方法中删除静态方法,则程序可以成功编译。但在运行时会抛出错误“NoSuchMethodError”。
- 我们可以在 Java 中重载 main 方法。但是当我们运行程序时,程序不会执行重载的 main 方法,我们只需要从实际的 main 方法调用重载的 main 方法。要运行一个方法而不调用此 main 方法,我们需要执行一个静态块。
- 为了避免NoSuchMethodError,我们可以在静态方法之后调用System.exit(0)。
- 注意:任何声明为静态的方法都会在主类执行之前执行。
- 例子:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>public class Hello { static { System.out.println("Hello, World!"); } } </code></span></span></span>
-
什么是垃圾收集器?它如何工作?
- 所有对象都分配在 JVM 管理的堆区域上。只要对象被引用,JVM 就认为它是活动的。一旦对象不再被引用,因此应用程序代码无法访问,垃圾收集器就会将其删除并回收未使用的内存。
-
堆栈内存和堆内存之间的区别?
- 栈用于静态内存分配,堆用于动态内存分配,两者都存储在计算机的 RAM 中。
- 堆栈上分配的变量直接存储在内存中,访问该内存非常快,并且它的分配在程序编译时处理。当一个函数或方法调用另一个函数,而另一个函数又调用另一个函数等时,所有这些函数的执行都将暂停,直到最后一个函数返回其值。堆栈始终按 LIFO 顺序保留,最近保留的块始终是下一个要释放的块。这使得跟踪堆栈变得非常简单,从堆栈中释放一个块只不过是调整一个指针。
- 堆上分配的变量的内存是在运行时分配的,访问这些内存会慢一些,但堆的大小仅受虚拟内存大小的限制。堆的元素彼此之间没有依赖关系,并且可以随时随机访问。您可以随时分配一个块并随时释放它。这使得跟踪在任何给定时间分配或释放堆的哪些部分变得更加复杂。
- 如果你在编译前就清楚需要分配多少数据,而且数据量不是太大,那么你就可以使用堆栈。如果你不知道运行时需要多少数据,或者需要分配大量数据,那么你就可以使用堆。
- 在多线程情况下,每个线程都有自己完全独立的堆栈,但它们将共享堆。堆栈是线程特定的,而堆是应用程序特定的。在异常处理和线程执行中,堆栈是需要考虑的重要因素。
- 堆内存由应用程序的所有部分使用,而堆栈内存仅由一个执行线程使用。
- 每当创建一个对象时,它总是存储在堆空间中,而堆栈内存包含对它的引用。堆栈内存仅包含本地原始变量和堆空间中对象的引用变量。
- 存储在堆中的对象是全局可访问的,而堆栈内存不能被其他线程访问。
- 堆栈中的内存管理采用后进先出 (LIFO) 方式,而堆内存则更为复杂,因为它是全局使用的。堆内存分为年轻代、老一代等,更多详细信息请参阅 Java 垃圾收集。
- 堆栈内存的寿命很短,而堆内存从应用程序执行开始到结束一直存在。
- 当堆栈内存已满时,Java 运行时会抛出 java.lang.StackOverFlowError,而如果堆内存已满,则会抛出 java.lang.OutOfMemoryError:Java Heap Space 错误。
- 与堆内存相比,堆栈内存大小非常小。由于内存分配简单(LIFO),堆栈内存与堆内存相比非常快。
-
解释 OOP 概念
- 面向对象编程是一种涉及类、对象、抽象、封装、继承、多态等概念的编程风格。
-
什么是继承?
- 继承是一个类的对象获取另一个类的属性和对象的过程。使用继承的两个最常见原因是:a) 促进代码重用。b) 使用多态性。
-
Java 是否支持多重继承?
- Java 仅支持通过接口进行多重继承,因为它可以实现多个接口但只能扩展一个类。
-
什么是封装?
- 封装涉及将代码和数据绑定在一起作为一个单元。
- 封装是一种用于隐藏对象的属性和行为并仅允许外部适当访问的技术。它可以防止其他对象直接更改或访问封装对象的属性或方法。
- 例如,如果一个类中的所有变量都被定义为 Private,并且提供了 getter 和 setter 方法,那么这个类就可以是一个封装类。
-
什么是抽象类?
- 抽象类是包含一个或多个抽象方法的类。抽象方法是已声明但不包含实现的方法。
- 如果即使只有一个方法是抽象的,整个类也必须声明为抽象的。
- 抽象类可能无法被实例化,并且需要子类提供抽象方法的实现。
- 您不能将一个类同时标记为抽象类和最终类。
- 非抽象方法可以访问您声明为抽象的方法。
-
什么是接口?
- 接口仅声明了实现类所需的方法。
- 接口不能标记为 final。接口变量必须是 static 或 final。
- 接口不能直接被实例化。
- 标记接口:标记接口是那些未声明任何必需方法的接口。java.io.Serializable 接口是典型的标记接口。这些接口不包含任何方法,但类必须实现此接口才能进行序列化和反序列化。
-
抽象和接口之间的区别?
- 抽象类可以有非抽象方法。它可以有实例变量。我们为抽象类方法提供了默认实现。一个类只能扩展一个抽象类。一个类可以实现多个接口。
-
什么是多态?
- 多态性是指一个对象具有多种形式。例如,String 是 Object 类的子类。
- 多态性在 Java 中表现为多个方法具有相同的名称。
- 在某些情况下,多个方法具有相同的名称,但具有不同的正式参数列表(重载方法)。
- 在其他情况下,多种方法具有相同的名称、相同的返回类型和相同的正式参数列表(重写方法)。
- 多态性是一种能够在不同的上下文中为某事物赋予不同的含义或用法的特性 - 具体来说,就是允许变量,函数或对象等实体具有多种形式。
- 多态性的2种形式:
- 编译时多态性:控制流在编译时本身就已决定。通过重载。
- 运行时多态性:使用继承和接口实现。控制流在运行时决定。覆盖:覆盖将具有相同的方法名称和相同的参数。一个将是父类方法,另一个将是子类方法。当声明相同的方法名称但具有不同的参数时,就会发生重载。
-
什么是方法重载?
- 方法重载意味着在同一个类中有两个或多个同名但具有不同参数的方法。
- 笔记:
- 重载方法必须改变参数列表
- 重载方法可以改变返回类型
- 重载方法可以改变访问修饰符
- 重载方法可以声明新的或更广泛的检查异常
- 方法可以在同一个类中或子类中重载
-
什么是方法覆盖?
- 方法覆盖是指子类声明的方法与其超类声明的方法具有相同的类型参数
- 您不能覆盖标记为 public 的方法并将其设置为 protected
- 不能覆盖标记为 final 的方法
- 不能覆盖标记为静态的方法
- 注意:静态方法不能被覆盖。重载方法仍然可以被覆盖。
-
为什么不在构造函数中调用抽象方法?
- 问题在于该类尚未完全初始化,当在子类中调用该方法时,可能会引起麻烦。
-
组合优于继承?
- 组合通常是“具有”或“使用”关系。在下面的例子中,Employee 类有一个 Person。它不是从 Person 继承,而是获取传递给它的 Person 对象,这就是为什么它是“具有”Person。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>class Person {String Title;String Name;Int Age;public Person(String title, String name, String age) {this.Title = title;this.Name = name;this.Age = age;}}class Employee {Int Salary;private Person person;public Employee(Person p, Int salary) {this.person = p;this.Salary = salary;}
}
</code></span></span></span></span>
-
封装和抽象之间的区别?
- 抽象关注对象的外部视图(即接口)
- 封装(信息隐藏)可防止客户端看到其内部视图。
- 抽象解决设计方面的问题,而封装解决实现方面的问题。
-
构造函数与方法?
- 构造函数必须具有与类名相同的名称,并且没有返回类型。它可用于实例化类中的任何对象,而方法没有这样的规则,并且是类的另一个成员。构造函数不能被继承,但派生类可以调用父类的超级构造函数。
this()
:构造函数使用 this 来引用同一个类中具有不同参数列表的另一个构造函数。super()
:构造函数使用 super 来调用超类的构造函数。- 方法:另一方面,实例方法需要类的实例存在才能调用,因此需要使用 new 关键字创建类的实例。类方法是声明为静态的方法。无需创建类的实例即可调用该方法
-
对象的实例化和初始化有什么区别?
- 初始化是创建新变量时内存分配的过程。应明确为变量赋值,否则它们可能包含使用相同内存空间的上一个变量的随机值。为了避免此问题,Java 语言为数据类型分配默认值。
- 实例化是为声明的变量明确分配确定值的过程。
-
Java 中对象是通过引用还是值传递的?请详细说明。
- Java 始终是按值传递的。当我们传递对象的值时,我们传递的是对该对象的引用。
- Java 创建方法中传递的变量的副本,然后进行操作。因此更改不会反映在主方法中。
- 但是当你将对象引用传递给方法时,会生成此引用的副本,因此它仍然指向同一个对象。这意味着,当方法退出时,你对此对象内部所做的任何更改都会保留。
- Java 通过值而不是对象来复制和传递引用。因此,方法操作将改变对象,因为引用指向原始对象。但由于引用是副本,因此交换将失败。
-
Java 中的原语?
-
Java 中 == 和 .equals() 方法之间的区别?
- 我们可以使用 == 运算符进行引用比较(地址比较),使用
.equals()
方法进行内容比较。 * 简而言之,== 检查两个对象是否指向相同的内存位置,而 .equals() 则计算对象中值的比较。
- 我们可以使用 == 运算符进行引用比较(地址比较),使用
-
为什么字符串是不可变的?
- 一旦将值赋给字符串,该值就无法更改。如果更改,它会创建一个新的字符串对象。而 StringBuffer 则不是这样。
-
什么是 String.intern()?何时以及为什么要使用它?
- String.intern() 方法可用于处理 Java 中的字符串重复问题。通过谨慎使用 intern() 方法,您可以节省大量由重复的 String 实例消耗的内存。如果一个字符串包含与另一个字符串相同的内容但占用不同的内存位置,则该字符串是重复的。
- 通过对字符串对象(例如“abc”)调用 intern() 方法,您可以指示 JVM 将此字符串放入池中,每当其他人创建“abc”时,都会返回此对象而不是创建新对象。这样,您可以在 Java 中节省大量内存,具体取决于程序中有多少字符串是重复的。
- 当调用 intern 方法时,如果字符串池中已经包含该字符串对象,并且 equals() 返回 true,它将从池中返回该字符串对象,否则它将把该对象添加到唯一字符串池中。
-
Java中的字符串池:
- Java 中的字符串池是存储在 Java 堆内存中的字符串池。
- 当我们使用双引号创建一个字符串时,它首先在字符串池中查找具有相同值的字符串,如果找到,它只返回引用,否则它在池中创建一个新的字符串,然后返回引用。
- 但是使用 new 运算符,我们强制 String 类在堆空间中创建一个新的 String 对象。我们可以使用 intern() 方法将其放入池中,或者从字符串池中引用具有相同值的其他 String 对象。
- 例如,下面的语句中创建了多少个字符串;
String str = new String("Cat");
- 上面的语句会创建 1 个或 2 个字符串。如果池中已经有一个字符串文字“Cat”,那么池中只会创建一个字符串“str”。如果池中没有字符串文字“Cat”,那么将首先在池中创建,然后在堆空间中创建,因此总共会创建 2 个字符串对象。
-
Final修饰符?
- Final 修饰符 - 一旦声明就无法修改。Java 中的空白 final 变量是在声明期间未初始化的 final 变量。
- final 类 — final 类不能有子类。
- final 变量- final 变量一旦初始化就不能更改。
- final 方法 - final 方法不能被子类覆盖。
- Final 修饰符 - 一旦声明就无法修改。Java 中的空白 final 变量是在声明期间未初始化的 final 变量。
-
确定关键词?
- Finalize 是在对象被垃圾收集之前执行清理处理的方法。
-
最后关键字?
- finally 是一个代码块,用于放置重要的代码,无论是否处理异常,它都会执行。
-
静态变量?
- 每个类只有一个副本的变量称为静态变量。它们不附属于类的特定实例,而是属于整个类。
- 静态变量与整个类相关联,而不是与类的特定实例相关联。非静态变量对于每个对象实例都具有唯一值。
-
什么是反射?
- Java 反射使在运行时检查类、接口、字段和方法成为可能,而无需在编译时知道类、方法等的名称。还可以使用反射实例化新对象、调用方法并获取/设置字段值。
-
多线程?
- 一个程序中多个任务同时运行。
-
快速失败和安全失败?
- 当一个线程正在迭代集合对象,而另一个线程通过添加、移除或修改底层集合上的对象来结构性地修改集合时,快速失败迭代器会抛出 ConcurrentModificationException。它们之所以被称为快速失败,是因为它们在遇到故障时会尝试立即抛出异常。
- 另一方面,故障安全迭代器对集合的副本而不是原始集合进行操作。
-
关键字synchronized是什么意思?
- 当您有两个线程正在读取和写入同一“资源”(例如名为“test”的变量)时,您需要确保这些线程以原子方式访问该变量。如果没有synchronized关键字,您的线程1可能看不到线程2对test所做的更改。
- 只要前一个线程的执行尚未完成, synchronized就会阻止下一个线程对方法的调用。线程一次只能访问一个方法。
-
关键字 volatile 是什么意思?
- 假设两个线程正在执行一个方法。如果两个线程在不同的处理器上运行,则每个线程可能都有自己的变量本地副本。如果一个线程修改了它的值,则更改可能不会立即反映在主内存中的原始值中。
- 现在另一个线程不知道修改后的值,从而导致数据不一致。本质上,volatile 用于指示变量的值将被不同的线程修改。“volatile”告诉编译器变量的值绝不能被缓存,因为它的值可能会在程序本身的范围之外发生变化。
- 此变量的值永远不会在线程本地缓存:所有读取和写入都将直接进入“主内存”
- 对 volatile 变量的访问绝不会阻塞:我们只进行简单的读取或写入,因此与同步块不同,我们永远不会持有任何锁。
-
什么是自动装箱和拆箱?
- 自动装箱是 Java 编译器在基本类型和其对应的对象包装器类之间进行的自动转换。例如,将 int 转换为 Integer,将 double 转换为 Double,等等。如果转换反过来,这称为拆箱。
-
Java 中的可选项?
- Optional 是一个容器对象,用于包含非空对象。Optional 对象用于表示缺少值的空值。此类具有各种实用方法,可帮助代码将值处理为“可用”或“不可用”,而不是检查空值。
-
什么是外化?
- 在序列化中,JVM 负责写入和读取对象的过程。这在大多数情况下很有用,因为程序员不必关心序列化过程的底层细节。
- 但是,默认序列化不保护密码和凭据等敏感信息。
- 因此,外部化使得程序员在序列化期间能够完全控制读取和写入对象。
- 实现 java.io.Externalizable 接口 - 然后您实现自己的代码以在
writeExternal()
方法中写入对象的状态并在方法中读取对象的状态readExternal()
。
-
什么是数据结构?
- 数据集合的有意排列。数据结构有 5 种基本行为:访问、插入、删除、查找和排序。
-
解释大 O 符号?
- 符号 Ο(n) 是表达算法运行时间上限的正式方式。它衡量最坏情况下的时间复杂度或算法可能需要的最长时间。
- 注意:O(1)表示无论集合中的数据量有多少,它都需要一个常数时间,比如三分钟。O (n)表示它需要的时间与集合的大小成线性关系。
-
解释大欧米茄符号
- 大欧米茄符号用于描述给定算法的最佳运行时间。
-
Java 中的数组?
- 数组是有序集合。它的长度是固定的,需要在初始化时定义,而列表的长度是可变的。数组更容易存储相同数据类型的元素。在堆栈和队列中内部使用。这是表示二维数组的便捷方式。
- 数组不能保存通用数据类型,但列表可以。
- 数组可以存储所有数据类型,而列表不能存储原始数据类型,只能存储对象。
- 数组:复杂性:
算法 平均的 最坏的情况下 空间 Θ(n) 在) 搜索 Θ(n) 在) 插入 Θ(n) 在) 删除 Θ(n) 在) -
Java 中的链表?
- LinkedList 包含头部和尾部。“头部”是 LinkedList 中的第一个项目,而“尾部”是最后一个项目。它不是循环数据结构,因此尾部没有指向头部的指针 - 指针为空。
- 没有索引,但每个节点都有一个指向下一个元素的指针。
- 它们本质上是动态的,这意味着它们只在需要时分配内存。
- 插入、删除、更新简单
- 链表是一组表示序列的节点。每个节点由一个数据和一个指向序列中下一个节点的链接或引用组成。
- 单链表:有一个节点和一个指向序列中下一个节点的指针。
- 双向链表:有一个节点和两个指针 - 一个指向下一个节点,一个指向序列中的上一个节点。如果您需要能够双向遍历存储的元素,这非常方便。
- 链表:运行时复杂度:
算法 平均的 最坏的情况下 空间 Θ(n) 在) 搜索 Θ(n) 在) 插入 Θ(1) 欧拉(1) 删除 Θ(1) 欧拉(1) -
二叉树
- 元素最多有 2 个子元素的树称为二叉树。由于二叉树中的每个元素只能有 2 个子元素,因此我们通常将它们命名为左子元素和右子元素。
- 节点的左子树仅包含小于父节点值的值。
- 节点的右子树仅包含大于或等于该节点值的值。
- 只有满足上述两个标准,该树才被认为是平衡的。
- 二叉树相对于链表的优势:在链表中,项目通过单个下一个指针链接在一起。在二叉树中,只要树是平衡的,每个项目的搜索路径就会比链表中的搜索路径短得多。
- 它们的缺点是,在最坏的情况下,从效率上讲它们会退化为链表。
-
堆栈:
- 堆栈是遵循后进先出机制的抽象集合。
- 主要功能包括
- 推送:将新实体添加到堆栈顶部。
- 弹出:从堆栈顶部移除一个实体。
- 访问存储在串行存取存储器中的数据的过程类似于操作堆栈上的数据。
- 可以定义堆栈具有有限的容量,即,如果堆栈已满并且无法添加新实体,则认为堆栈处于溢出状态。
- 如果堆栈为空并且无法弹出实体,则认为处于下溢状态。
- 堆栈的效率:时间不依赖于堆栈中的物品数量,因此非常高效
O(1)
。
-
队列:
- 队列是遵循先进先出(FIFO)机制的抽象集合。队列中的实体保持有序。
- 主要功能包括
- 入队:将项目添加到队列末尾。出队:从队列开头删除项目。
- Front:从队列中检索第一个项目。
- 可以定义队列具有有限的容量,即,如果队列已满并且无法添加新实体,则认为队列处于溢出状态。
- 如果队列为空并且无法弹出实体,则认为处于下溢状态。
- 队列的效率:时间与队列中的项目数无关,因此非常高效。O(1)。
- 双端队列(deque):是一种抽象集合,它与队列的不同之处在于可以在队列的任一侧添加/删除项目。
- 输入受限双端队列:删除操作发生在任一端,但插入操作仅发生在一端。
- 输出受限双端队列:插入发生在任一端,但删除只发生在一端。双端队列的常见情况是双向链表。
- 优先级队列:与队列相同,但具有与之关联的优先级。根据优先级检索项目
-
阻塞队列:
- 阻塞队列是指当您尝试从队列中出队且队列为空时,或当您尝试将项目加入队列且队列已满时,队列会阻塞。尝试从空队列中出队的线程会被阻塞,直到其他线程将项目插入队列。尝试将项目加入已满队列的线程会被阻塞,直到其他线程在队列中腾出空间。
- 实现阻塞队列的示例
-
堆栈和队列之间的区别?
-
Java中什么是死锁
- 当线程进入等待状态时会发生死锁,因为请求的系统资源被另一个等待进程持有,而该等待进程又在等待另一个等待进程持有的另一个资源。
- 死锁发生的示例
- 防止死锁的示例
-
什么是List接口&Set接口?
- List 接口支持有序的对象集合,并且可能包含重复项。Set 接口提供访问有限数学集合元素的方法。集合不允许重复元素
-
ArrayList 和 Vectors 之间的区别?
- Vector 是线程安全的(同步),而 arraylist 不是。因此 arraylist 的性能优于 vector。* 在 ArrayList 中,您必须从 Arraylist 的开头开始搜索特定元素。但在 Vector 中,您可以从 vector 中的特定位置开始搜索特定元素。这使得 Vector 中的搜索操作比 ArrayList 中的搜索操作更快。Vector 的默认大小为 10,而 arraylist 的大小可以是动态的。
- 与 LinkedList 相比,ArrayList 中的插入和删除速度很慢?
- ArrayList 内部使用一个数组来存储元素,当该数组通过插入元素填充时,将创建一个大小约为原始数组 1.5 倍的新数组,并且旧数组的所有数据都将复制到新数组。
- 在删除过程中,数组中被删除元素之后的所有元素都必须向后移动一步,以填充删除产生的空间。在链表中,数据存储在引用前一个节点和下一个节点的节点中,因此添加元素很简单,只需创建节点并更新最后一个节点上的下一个指针和新节点上的前一个指针即可。链表中的删除操作很快,因为它只涉及更新被删除节点之前的节点中的下一个指针和更新被删除节点之后节点中的前一个指针。
-
Map 的实现?
- TreeMap:按键升序排序。对于在 Map 中插入、删除和定位元素,HashMap 是最佳选择。但是,如果您需要按排序顺序遍历按键,那么 TreeMap 是更好的选择。
- HashTable:不允许空值。它不是故障安全的,并且是同步的,而
- HashMap允许空值,并且它是故障安全的,并且不同步。
- LinkedHashMap:这是 Hashmap 的子类。由于它有一个 linkedList,所以插入顺序得以保留。
-
枚举和迭代器之间的区别?
- 枚举不包括 remove() 方法,而迭代器则包含。枚举器充当只读接口,因为它提供读取和遍历集合的方法。
- ListIterator:就像一个迭代器,除了它允许向前或向后访问集合
-
Hashmap 在 Java 中如何工作?
- Java 中的 HashMap 是基于哈希原理工作的。它是一种数据结构,只要我们知道键,它就允许我们在 O(1) 的常量时间内存储和检索对象。当我们调用 put 方法时,
hashcode()
将调用键对象的方法,以便映射的哈希函数可以找到存储 Entry 对象的存储桶位置。 - 如果两个不同的对象具有相同的哈希码:在这种情况下,将在该存储桶位置形成一个链接列表,并将新条目存储为下一个节点。找到存储桶位置后,我们将调用
keys.equals()
方法在 LinkedList 中识别正确的节点,并在 Java HashMap 中返回该键的关联值对象
- Java 中的 HashMap 是基于哈希原理工作的。它是一种数据结构,只要我们知道键,它就允许我们在 O(1) 的常量时间内存储和检索对象。当我们调用 put 方法时,
-
Java 中的泛型
- 在泛型出现之前,我们可以将任何类型的对象(即非泛型)存储在集合中。现在,泛型迫使 Java 程序员存储特定类型的对象。
- 类型安全:泛型中只能保存单一类型的对象。不允许存储其他对象。
- 不需要类型转换:不需要对对象进行类型转换。
- 编译时检查:在编译时进行检查,这样就不会在运行时出现问题。好的编程策略认为在编译时处理问题比在运行时处理问题要好得多。
- 在使用泛型之前,我们需要进行类型转换。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); //typecasting </code></span></span></span>
- 有了泛型之后,我们不需要对对象进行类型转换。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>List<String> list = new ArrayList<String>(); list.add("hello"); String s = list.get(0); </code></span></span></span>
- 可以引用任何类型的类称为泛型类。在这里,我们使用 T 类型参数来创建特定类型的泛型类。T 类型表示它可以引用任何类型(如 String、Integer、Employee 等)。您为该类指定的类型将用于存储和检索数据。
- ?(问号)符号代表通配符元素。它表示任何类型。如果我们写 <? extends Number>,它表示 Number 的任何子类,例如 Integer、Float、double 等