整理的一些Java细节问题

news2024/11/25 1:01:06

1. 为什么要有无参构造?

在 Java 中,如果一个类没有显式定义构造方法,编译器会自动生成一个默认的无参构造方法(也称为默认构造方法)。无参构造方法是一个没有任何参数的构造方法。

无参构造方法的存在有几个重要原因:

  1. 默认初始化:如果没有定义任何构造方法,Java 编译器会自动提供一个无参构造方法,确保类的实例可以被实例化。这允许对象在被创建时执行默认的初始化。

  2. 继承:当一个类继承自另一个类,如果父类有默认的构造方法且没有显式提供其他构造方法,子类也将继承这个默认的无参构造方法。这使得子类实例化时可以使用父类的默认初始化。

  3. 反射和实例化:在某些情况下,例如通过 Java 的反射机制,需要在运行时动态创建对象实例。如果没有提供其他构造方法,可以使用默认的无参构造方法来实例化对象。

尽管编译器会在需要时自动创建无参构造方法,但是在编写 Java 类时,有时会明确地提供一个无参构造方法是一个良好的实践。这样可以确保对类的实例化始终具有一致的行为,并提供额外的灵活性。

2. 为什么声明了有参,就必须声明无参?

在 Java 中,如果一个类显式定义了有参构造方法,但没有显式定义无参构造方法,Java 编译器不会自动生成默认的无参构造方法。

这是因为当你定义了有参构造方法时,Java 编译器默认提供的无参构造方法就不再存在,除非你明确地定义它。这意味着如果在类的其他地方(比如其他类实例化时)使用了无参构造方法来创建对象,但该类中没有定义无参构造方法,就会导致编译错误。

这种行为是 Java 语言的规范之一,确保编程人员在需要时可以明确地控制对象的创建方式。如果你需要提供无参构造方法,即使你已经定义了有参构造方法,可以显式地定义一个无参构造方法,或者利用有参构造方法中的默认值来实现类的实例化。

3. 为什么重写了equals就必须重写hashcode?

在 Java 中,equals() 和 hashCode() 方法之间存在关联,这两个方法用于实现对象的相等性和哈希值。如果你重写了 equals() 方法来定义两个对象相等的条件,那么为了维持哈希表数据结构的一致性,通常也需要重写 hashCode() 方法。

这是由于哈希表数据结构在存储对象时使用了 hashCode() 方法。哈希表是一种常见的数据结构,如 HashMap、HashSet,它们根据对象的哈希码(由 hashCode() 方法返回)将对象存储在内部数组的特定位置。当你需要通过键来查找对象时,哈希表首先根据键的哈希码确定可能的位置,然后再使用 equals() 方法来精确查找这个位置上的对象。

如果两个对象根据 equals() 方法判断相等,那么它们的哈希码应该相等。这是因为哈希表的实现依赖于这个规则,如果这个规则被打破,可能导致对象无法准确地从哈希表中检索。

因此,当你重写 equals() 方法时,为了确保符合相等对象具有相等哈希码的原则,通常也需要重写 hashCode() 方法。在重写 hashCode() 方法时,应该保证如果两个对象根据 equals() 方法相等,它们的哈希码应该一致。

当重写 equals() 方法但未重写 hashCode() 方法时,可以导致在集合中出现相同的对象

比如:考虑一个 Book 类,它包含书名和作者。我们重写了 equals() 方法来比较两本书是否具有相同的书名和作者,但忘记了重写 hashCode() 方法。

import java.util.HashSet;
import java.util.Set;

public class Book {
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return title.equals(book.title) && author.equals(book.author);
    }
}

现在,让我们创建几本书并将它们放入 HashSet 中:

public class Main {
    public static void main(String[] args) {
        Set<Book> bookSet = new HashSet<>();

        Book book1 = new Book("Java 101", "John Doe");
        Book book2 = new Book("Java 101", "John Doe");

        bookSet.add(book1);
        bookSet.add(book2);

        System.out.println("HashSet size: " + bookSet.size());
    }
}

尽管 book1 和 book2 的 equals() 方法返回 true,但由于没有重写 hashCode() 方法,HashSet 会将它们视为两个不同的对象,因此向集合中添加 book2 也会成功。这将导致集合中出现相同的对象,违反了预期的行为。
在这里插入图片描述

解决方法是在 Book 类中重写 hashCode() 方法,确保相等的对象具有相等的哈希码:

import java.util.Objects;

public class Book {
    // ... 其他代码保持不变

    @Override
    public int hashCode() {
        return Objects.hash(title, author);
    }
}

在这里插入图片描述

通过重写 hashCode() 方法,相等的对象会具有相等的哈希码,从而能够正确地存储和检索于集合类中。

4. HashSet去重细节

在 Java 中,HashSet 是基于哈希表实现的集合类,用于存储不重复元素。当元素被添加到 HashSet 中时,它会执行以下步骤来确保不重复:

  1. 计算哈希码(Hash Code):当一个元素被加入 HashSet 时,HashSet 会调用该元素的 hashCode() 方法来获取其哈希码。哈希码是一个用来快速定位对象的整数值。

  2. 计算存储位置:HashSet 使用哈希码和内部的哈希函数,计算出该元素在内部存储数组中的位置。每个位置对应一个“桶”(bucket)。

  3. 检查是否存在相同哈希码的元素:如果该位置上已经有元素存在(发生哈希冲突),则会进行进一步的检查。

    • 相同哈希码:HashSet 首先会比较待加入元素的哈希码与该位置上已存储元素的哈希码。如果两者相同,它会调用这两个元素的 equals() 方法进行精确比较。

    • 如果两个元素相等:如果 equals() 方法返回 true,则该元素已经存在于 HashSet 中,新元素不会被加入。这是为了保证 HashSet 中不会存储重复的元素。

  4. 插入元素:如果没有相同哈希码的元素存在,且没有发生哈希冲突,新元素将被添加到 HashSet 中。

通过这种方式,HashSet 利用哈希码快速确定元素的存储位置,并利用 equals() 方法进行最终的比较来确保集合中没有重复元素。

5. String为什么是不可变的?

在 Java 中,String 对象是不可变的,即一旦创建后,它的值就无法被修改。这种不可变性是由于以下几个原因:

  1. 安全性(Security)
    由于字符串是不可变的,它们在作为参数传递给方法、作为键用于哈希表等场景时更加安全。因为不可变性可以确保字符串不被意外更改,从而避免在多个地方使用相同的字符串时发生潜在的安全隐患。

  2. 线程安全(Thread Safety)
    不可变性使得字符串成为线程安全的,因为多个线程可以同时访问一个字符串对象而不需要担心它的值被修改。这样就不需要同步控制来保护字符串对象。

  3. 字符串池(String Pool)
    Java 中的字符串池(String Pool)机制利用字符串的不可变性。当你创建一个字符串时,如果这个字符串已经存在于字符串池中,Java 将返回现有的字符串对象而不是创建一个新的。这样可以节省内存空间,提高性能。

  4. 缓存哈希值
    字符串的不可变性使得它们的哈希码可以在创建时缓存。由于字符串的哈希码在使用哈希表等数据结构时经常被需要,缓存它们可以提高性能。

  5. 优化字符串操作
    不可变性允许字符串操作(如连接、截取等)产生新的字符串,而不会修改原始字符串。这样可以避免频繁地创建新的字符串对象。

总的来说,字符串的不可变性是 Java 设计的一部分,它提供了安全、线程安全、性能和一些优化的好处,使得字符串在很多场景下更易于管理和使用。

6. 什么是泛型?有什么好处?

泛型(Generics)是 Java 中的一个强大特性,它允许在编写代码时参数化类型,使得类、接口和方法能够在编译时具有更强的类型安全性。

  • 基本概念:
    参数化类型:允许你在使用类、接口或方法时指定类型。
    泛型类和接口:可以在类或接口中声明类型参数,并在实例化时指定具体类型。
    泛型方法:可以在方法级别使用泛型,允许方法在调用时接受不同类型的参数。

  • 好处:
    类型安全:通过在编译时捕获错误,避免了类型转换错误。编译器能在编译阶段发现并解决类型不匹配的问题。

    代码重用:泛型使得类和方法可以适用于多种类型,提高了代码的重用性。

    性能:泛型在编译时执行类型检查,避免了运行时的类型转换,可以提高性能。

    简化代码:泛型能够减少手动类型转换,使代码更简洁易读。

    集合类的使用:Java 中的集合框架(如 ArrayList, HashMap 等)广泛使用泛型,使得集合中存储的元素类型更明确,减少了强制类型转换。

    扩展数据类型的适用范围:通过泛型,代码可以对多种数据类型进行操作,而不需要为每种类型编写不同的逻辑。

总的来说,泛型提供了类型安全、代码重用、简化代码、提高性能等好处,使得 Java 编程更为灵活、高效和安全。

7. 什么是泛型擦除?有什么好处?

泛型擦除(Generics Erasure)是 Java 泛型机制中的一个概念,指的是在编译时将泛型信息从泛型代码中删除的过程。Java 的泛型是通过类型擦除来实现的。

  • 基本概念:
    类型擦除:Java 的泛型只存在于编译期,在编译后的字节码中,并不保留泛型的类型信息。编译器会擦除泛型类型,并使用原始类型来处理。

    原始类型:在类型擦除后,所有泛型类型参数都会被替换为它们的上限边界或 Object 类。比如 List 在编译后会变成 List。

  • 好处:
    向后兼容性:泛型擦除允许 Java 5 之后引入泛型,同时保留了与旧代码的向后兼容性。因为擦除后的代码与旧的非泛型代码兼容。

    减少运行时负担:擦除泛型信息可以减少字节码的大小,降低运行时内存开销。因为泛型信息在运行时不再存在。

    简化代码:在源代码级别使用泛型,能够提供更好的类型安全性和更清晰的代码结构,但在运行时不需要额外的泛型信息。

尽管泛型擦除提供了许多好处,但也有一些限制和注意事项,比如无法直接获取泛型的类型信息(在运行时),以及某些情况下可能导致潜在的类型不安全性。通常,泛型擦除是为了兼容性和性能而存在的。

8. 抽象类和抽象方法细节

抽象类和抽象方法是面向对象编程中的重要概念,用于实现对继承、多态和封装等特性的支持。

  • 抽象类(Abstract Class):

    1. 概念:
      抽象类是一个不能被实例化的类,用 abstract 关键字定义。它可以包含抽象方法,也可以包含普通方法。

    2. 特点:

      抽象类不能被实例化,只能用于派生其他类。

      可以包含抽象方法和普通方法。

      抽象类的子类必须实现其所有的抽象方法,除非子类也声明为抽象类。

    3. 用途:
      提供通用的方法实现,但也留有抽象方法供子类实现,促进了代码重用和多态性。

  • 抽象方法(Abstract Method):

  1. 概念:
    抽象方法是在抽象类中声明但没有具体实现的方法,不包含方法体。使用 abstract 关键字标记。

  2. 特点:

    抽象方法只能存在于抽象类中。

    抽象方法没有实际的实现,其实现必须在子类中完成。

  3. 用途:
    定义一种契约,要求继承的子类必须实现这些方法,从而促进了多态性和多态方法调用。

示例:

// 抽象类
abstract class Shape {
    // 抽象方法
    public abstract double area(); // 无方法体
    // 普通方法
    public void display() {
        System.out.println("Displaying shape...");
    }
}

// 抽象类的子类
class Circle extends Shape {
    private double radius;

    // 实现抽象方法
    public double area() {
        return Math.PI * radius * radius;
    }
}

抽象类和抽象方法提供了一种设计机制,让代码更具扩展性和灵活性,通过强制要求继承类实现抽象方法,达到对特定行为进行标准化的目的。

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

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

相关文章

【vscode输出中文乱码】

vscode输出中文乱码为一个个的问号。 这个链接亲测有用 win11对应的界面在这里&#xff1a;

产品经理入门学习(二):产品经理问题思考维度

参考引用 黑马-产品经理入门基础课程 1. 抓住核心用户 1.1 为什么要抓住核心用户 什么是用户&#xff1f; 所有和产品有关系的群体就是用户&#xff0c;他们是一群既有共性&#xff0c;又有差异的群体组合 做产品为什么要了解用户&#xff1f; 了解用户的付费点、更好的优化产…

文件同步工具推荐:挑选高效实用的工具大揭秘

随着工作的累积&#xff0c;会持续产出大量电子资料和文件。如何妥善管理这些文件资料&#xff0c;成了一个问题。有需求就有市场&#xff0c;当下市场上也有很多文件同步工具。 有什么好用的文件同步工具&#xff1f; Zoho WorkDrive 同步网盘就是一款好用的文件同步工具&am…

Leetcode—199.二叉树的右视图【中等】

2023每日刷题&#xff08;十九&#xff09; Leetcode—199.二叉树的右视图 深度优先遍历实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(…

【数据结构】冒泡排序

冒泡排序 前言冒泡排序运行图例算法实现基本思路算法实现步骤算法码源详解冒泡排序效率分析&#xff08;一&#xff09;时间复杂度——O&#xff08;N^2&#xff09;&#xff08;二&#xff09;空间复杂度——O&#xff08;1&#xff09;&#xff08;三&#xff09;稳定性&…

人工智能基础_机器学习018_手写代码实现_MBGD小批量梯度下降---人工智能工作笔记0058

然后我们继续来看这里的小批量梯度下降,小批量梯度下降,其实就是 用少量的样本数据,进行梯度下降,上面是公式 然后我们来看代码 import numpy as np 导入数学计算包 #X,y创建数据集X=np.random.rand(100,1) x是100行1列 w,b=np.random.randint(1,10,size=2) 然后获取w和截距…

干货分享:10个行业可视化大屏模板(附 Python 源码)

大家好&#xff0c;数据大屏是一种用于展示和分析数据的可视化工具&#xff0c;通常用于监控、分析和报告数据。大屏可以帮助组织更好地理解和管理其数据&#xff0c;支持数据驱动决策&#xff0c;提高业务效率和决策的质量。 本文的所有大屏都是基于Python开发&#xff0c;因…

根据一个类型 获取该类型的 特殊判断 优雅写法

需求&#xff1a;一个统计接口&#xff0c;时间类型参数有以下&#xff1a;今日、近七天、近三十日等 如果我要查询的话&#xff0c;SQL 里的条件必定是一个时间范围&#xff0c;所以就需要根据类型来算好这个时间范围&#xff0c;所以可以写成下面这样。 到时候直接就是 获取…

前端vue,后端springboot。如何防止未登录的用户直接浏览器输入地址访问

前端&#xff0c;使用Vue框架来实现前端路由拦截&#xff1a; 设置需要登录校验的页面&#xff1a; 登录成功后&#xff0c;去设置LocalStorage里面的IsLogin为true:

[LeetCode]-链表中倒数第k个结点-CM11 链表分割-LCR 027. 回文链表

目录 链表中倒数第k个结点 题目 思路 代码 CM11 链表分割 题目 思路 代码 LCR 027.回文链表 题目 思路 代码 链表中倒数第k个结点 链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId…

Web3游戏的十字路口:沿用传统IP还是另起炉灶?

人们经常问我对 Web3 游戏有什么看法。因此&#xff0c;我想以书面形式概述一下我目前的想法。 让我先澄清一下&#xff1a;我不是专家。这不是一篇深入探讨游戏世界精细指标如 MAU 或 D14 等的全面分析。请把这看作是我根据个人交流和研究&#xff0c;这反映我在游戏领域关注…

学习Opencv(蝴蝶书/C++)相关——1. 前言 和 第1章.概述

文章目录 1. 整体架构1.1 OpenCV3.01.2 Opencv4.xX. Opencv cheatsheet(小抄)1. 整体架构 1.1 OpenCV3.0 对于Opencv3.x版本,网上最常见的图,图自OpenCV Tutorial-Itseez 现在已经不是500+的算法了,而是2500+,详见:About

喜报|英码科技荣登“广州首届百家新锐企业名单”、“2022年度中国好技术项目库名单”榜单

近日&#xff0c;英码科技喜报连连&#xff0c;在刚刚公布的2022年度“中国好技术”项目库入选名单和广州首届百家新锐企业名单中&#xff0c;英码科技凭借出色的技术创新能力和优秀的企业竞争力荣登榜单。 2022年度“中国好技术” 近期&#xff0c;2022年度“中国好技术”征集…

如何从站长的角度选择高防CDN以节省成本

在当今的数字化世界中&#xff0c;网站站长需要面对越来越复杂的网络安全威胁&#xff0c;如DDoS攻击、恶意爬虫和恶意请求等。为了保护网站的可用性和数据安全&#xff0c;站长通常会寻求使用高防CDN&#xff08;内容分发网络&#xff09;。然而&#xff0c;如何在选择高防CDN…

隐私保护多领域推荐的紧密度共聚类联邦概率偏好分布模型

论文链接 Federated Probabilistic Preference Distribution Modelling with Compactness Co-Clustering for Privacy-Preserving Multi-Domain Recommendation 引言 这篇论文提出的概率偏好分布是通过使用高斯分布来表示用户和项目的偏好。在论文中&#xff0c;作者提出了一…

11.1 Linux 设备树

一、什么是设备树&#xff1f; 设备树(Device Tree)&#xff0c;描述设备树的文件叫做 DTS(DeviceTree Source)&#xff0c;这个 DTS 文件采用树形结构描述板级设备&#xff0c;也就是开发板上的设备信息&#xff1a; 树的主干就是系统总线&#xff0c; IIC 控制器、 GPIO 控制…

EASYX中的消息处理

eg1:点击鼠标的左键右键绘制不同的图形 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <easyx.h> #include <iostream> #include <math.h> #include <stdlib.h> #include <conio.h> #include <time.h> #define PI …

selenium自动化测试入门 —— 操作浏览器!

1、启动浏览器&#xff08;实例化浏览器&#xff09; 启动Chrome浏览器&#xff08;驱动已放入path环境变量下&#xff09; driver webdriver.Chrome() 指定驱动路径驱动Chrome 浏览器 # .\driver\chromedriver.exe 为驱动存放位置,可以是相对路径或者绝对路径 driver we…

包装印刷行业万界星空科技云MES解决方案

印刷业的机械化程度在国内制造行业内算是比较高的&#xff0c;不算是劳动密集型企业。如书本的装订、包装的模切、烫金、糊盒等都已经有了全自动设备。印刷厂除了部分手工必须采用人工外&#xff0c;大部分都可以采用机器&#xff0c;也就意味着可以由少量工人生产出大量产品。…

Leetcode-LCR 126 斐波那契数

本题答案需要取模 1e97(1000000007) 定义一个变量 1000000007&#xff0c;答案%变量&#xff0c;完整题目要求 HashMap方法 class Solution {private Map<Integer,Integer> storeMap new HashMap();public int fib(int n) {int constant 1000000007;if(n0){return 0…