一、Java
1.Java中finally、final、finalize的区别
1.性质不同
(1)final为关键字;
(2)finalize()为方法;
(3)finally为为区块标志,用于try语句中;
2. 作用
(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中
被final修饰的类不能被继承;修饰的方法不能被重写;修饰的变量不能被改变
(2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象 进行垃圾回收;
(3)finally{}用于标识代码块,与try{ }进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行
2.hashMap和hashTable区别
1.继承不同
Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
2.允不允许NULL值
HashMap不是线程安全的:HashMap是map接口的子类,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null key和null value,而hashtable不允许;
3.线程安全不一样
Hashtable 中的方法是同步的,而HashMap中的方法在默认情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
4.方法
HashMap有containsvalue和containsKey方法;HashTable有contains方法
5.效率
HashMap效率相对Hashtable高一点
参考:HashMap与HashTable的区别 - jack_ou - 博客园 (cnblogs.com)
同步与非同步
同步
A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去
异步
A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待
特点
显然,同步最最安全,最保险的。而异步不安全,容易导致死锁,这样一个线程死掉就会导致整个进程崩溃,但没有同步机制的存在,性能会有所提升
3.hashMap底层原理
1、HashMap是基于哈希表实现的,而哈希表的底层是数组加上链表的形式。
2、数组内存连续查询效率高,链表内存分散增删改效率高,哈希表采用此种存储数据的形式极大的提高操作数据的效率。
3、哈希表的默认长度是16,编号从0开始。图中编号0-4的长方形代表了一个数组,箭头指向的代表了一个一个的链表。
4.如何保证线程安全以及Java中实现多线程的方式?
线程:操作系统中能够进行最小调度的单位——并发、并行的概念
并发:在同一时刻,多个指令在单个CPU上交替进行
并行:在同一时刻,多个指令在多个CPU上同时进行
进程:程序执行的基本实体
4.1Java中实现多线程
- 继承Thread类的方式实现 并重写run方法
- 实现Runnable接口实现 重写run方法 1&2 方法都没有返回值
- 利用Callable接口重写call方法 和Future接口【管理callAble运行的结果】方式实现
Java中实现多进程:
Java中没有直接支持多进程的API,但可以通过Runtime类或ProcessBuilder类来创建和执行外部进程。
- Runtime类:通过调用Runtime.getRuntime().exec(command)方法可以创建并执行一个新的进程,其中command参数是要执行的命令。
- ProcessBuilder类:可以通过ProcessBuilder类构造一个进程,并设置进程的相关属性,然后通过start()方法启动进程。
需要注意的是:在多线程和多进程实现的过程中,要注意死锁和竞争等问题
死锁:线程中多个锁之间嵌套容易引发死锁
如何保证线程安全
① 同步代码块:把操作共享的数据的代码锁起来【排队卫生间案例(关门上锁)】
② 同步方法:就是把synchronized关键字加到方法上
③ Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块
4.2.进程和线程的区别
- 进程是操作系统中的一个独立运行的实体,是系统资源分配的基本单位,每个进程都拥有自己的进程标识符,操作系统可以对进程进行调度和管理
- 线程是进程中的一个执行单元,共享所属进程的内存空间和系统资源
- 线程是调度的基本单位,多个线程之间可以进行切换(例如微信的多个聊天框)
5.多态
多态是面向对象三大特征【封装、继承、多态】。同一行为,通过不同的子类,可以体现出不同的形态。多态的前提:有继承关系,重写父类的方法
同一个方法调用时,由于对象不同可能会有不同的行为【例如:同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。】
举例:
例如Person类 子类有Student、teacher类
在实例化时可以这样写:
Person teacher = new Teacher(XXX);
6.接口和抽象类的区别
-
定义和实现:
- 接口定义了一组抽象方法的集合,但不包含方法的实现。所有在接口中声明的方法默认是抽象的,没有方法体。类通过实现(implements)接口来提供方法的具体实现。
- 抽象类可以包含抽象方法和具体方法。抽象方法是没有实现的方法,而具体方法有方法体。类通过继承(extends)抽象类来获得方法的具体实现。
-
多继承:
- 接口支持多继承,一个类可以实现多个接口。这允许一个类具备多个行为。
- 抽象类不支持多继承,一个类只能继承一个抽象类。这是因为Java是单继承的。
-
构造方法:
- 接口不能有构造方法,因为接口不能被实例化。
- 抽象类可以有构造方法,用于初始化抽象类的成员变量或执行其他初始化操作。
-
成员变量:
-
接口中只能包含常量(被
public static final
修饰)和抽象方法,不能包含实例变量。 -
抽象类可以包含实例变量,常量,抽象方法和具体方法。
-
-
目的:
-
接口用于定义一种规范或契约,它规定了类需要提供的方法,并且提供了一种多态性的机制。
-
抽象类用于表示一种抽象概念,它提供了一些通用的方法和成员变量,但是具体实现需要由子类提供
-
8.Java反射是什么?怎么实现反射?
1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
2、Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
个人理解:
- 我们编译时知道类或对象的具体信息,此时直接对类和对象进行操作即可,无需使用反射(reflection)
- 如果编译不知道类或对象的具体信息,此时应该如何做呢?这时就要用到 反射 来实现。比如类的名称放在XML文件中,属性和属性值放在XML文件中,需要在运行时读取XML文件,动态获取类的信息
9.Java的回收机制
为什么垃圾回收:
方便管理内存,方便开发人员【c++中没有回收机制】
回收哪些内存:
可达性分析算法:没有被GC roots引用的对象都是可以被回收的对象
哪些对象可以作为GC root?
- 虚拟机栈中的引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象
- 本地方法栈中JNI(Native方法)的引用的对象
回收算法:
Marking-Sweep(标记清除法):将没有被引用的对象西安标记出来,然后在清除【原因:没有进行对对象的移动】,会造成碎片,可能造成内存不足
Marking-Compat(标记整理法):先标记,将可用的对象移到一边,将剩余的删除,没有碎片,有大量的空间
复制算法: 将内存按一定的比例分为对象面和空闲面
JVM 分代:
分代原因:生命周期不一样,可以很好的进行内存管理
10.java中==和equals区别?
【注意区分python中 == 和 is 的区别】
1."=="运算符用于比较两个对象的引用是否指向内存中的同一个地址,也就是比较它们是否是同一个对象的实例。如果两个对象的引用指向相同的内存地址,则"=="返回true,否则返回false。
2."equals"方法是Object类中定义的一个方法,用于比较两个对象的内容是否相等。通常情况下,如果没有对该方法进行重写,它与"=="的作用是一样的,即比较两个对象的引用是否指向内存中的同一个地址。但是,可以通过在具体类中重写"equals"方法来改变对象相等的定义,使其比较对象的内容而不是引用。
11.java如何实现单例继承
在子类中重写父类的实例获取方法
构建一个父类,确保构造方法是私有的,以防止外部类直接实例化它
子类利用entends继承父类,重写父类的方法
以上可以实现单例继承
12.JVM类加载
13.重写和重载
-
重写 (Override):【子类重写父类的方法】
- 重写指的是子类定义了一个与父类中具有相同名称、参数列表和返回类型的方法,并且子类可以在方法体内提供自己的实现。
- 重写主要用于实现子类对父类方法的定制化行为。子类重写父类方法时,被重写的方法在父类和子类之间具有多态性,即在运行时根据对象的实际类型来确定调用的方法。
- 在 Java 中,重写的方法必须具有相同的方法签名,包括方法名、参数列表和返回类型,且访问修饰符不能是比父类中被重写方法的访问修饰符更严格。
-
重载 (Overload):【重载用于在同一个类中定义多个相同名称的方法】
- 重载指的是在同一个类中,可以定义多个方法,它们具有相同的名称但是参数列表不同(参数类型、参数个数或者参数顺序不同)。
- 重载的目的是为了提高代码的灵活性和复用性,以适应不同的需求。通过在同一个类中定义多个方法来处理不同的输入参数,从而实现同一方法名的多种行为。
- 在 Java 中,重载的方法必须具有不同的参数列表,但是可以有不同的返回类型
14.链表和数组的区别?
-
数组(Array):
- 数组是一种线性数据结构,它由一组连续的内存空间组成,用于存储相同类型的数据。
- 数组的大小是固定的,一旦创建后,其大小不能动态改变。
- 对于数组,可以直接通过索引来访问元素,因为数组中的元素是连续存储的,所以访问元素的时间复杂度为 O(1)。
- 但是在插入和删除元素时,由于数组的大小是固定的,需要移动元素以保持连续性,这可能导致时间复杂度为 O(n) 的操作,其中 n 是数组的大小。
-
链表(Linked List):
- 链表是一种线性数据结构,它由一组节点组成,每个节点包含数据和指向下一个节点的指针(或引用)。
- 链表的大小可以动态改变,因为它不需要一块连续的内存空间来存储数据,节点可以分布在内存中的任何位置。
- 在链表中,要访问特定位置的元素,需要从头节点开始顺序遍历直到找到目标节点,因此访问元素的时间复杂度为 O(n)。
- 在插入和删除元素时,由于链表中的节点可以动态分配和释放,并且只需要修改节点之间的指针,所以插入和删除的时间复杂度通常是 O(1)。
总结:
- 数组适合用于需要随机访问元素的情况,但不适合频繁的插入和删除操作。
- 链表适合用于需要频繁插入和删除操作的情况,但访问元素的效率较低。
15.面向对象和面向过程 (举例:造一辆车)
面向对象编程中:程序的主要组织单位是对象,它们封装了数据和行为;先定义车辆类:具有自己的属性和方法、然后在创建对象即实例化类、再调用相关的方法
面向过程编程中:程序主要由一系列函数或过程组成,以解决问题;定义一系列函数来执行造车的过程,例如创建车辆,启动车辆,加速,刹车等
面向对象的方法更加模块化和灵活,因为它将数据和行为封装在对象中,并通过对象之间的交互来完成任务。而面向过程的方法则更加侧重于步骤和流程,将任务分解为一系列的函数或过程来执行。
二、python
python中垃圾回收机制
引用计数器+一个循环垃圾收集器来工作
垃圾回收器是一段独立的代码,用来寻找引用计数为0的对象
sys.getrefcount(XXX)
# 使用上述方法查看引用计数
计数器增加情况:
1.对象被创建
2.或另外的别名被创建
3.被作为参数传递给函数
4.成为容器对象的第一个元素
计数器减少情况:
1.本地引用离开了作用范围
2.对象的别名被显式销毁
3.对象的一个别名被赋值给其他对象
4.对象被从一个窗口对象中移除
5.窗口对象本身被销毁
__del__语句:
析构方法,当对象内存被释放时,自动执行(开发人员无需关心内存分配和释放)
1.数据结构中哪个是不可变的
python中的数据结构有:列表 元组 集合 字典
不可变的数据结构:元组
2.is 和 == 区别?
-
==
运算符用于比较对象的值是否相等。当使用==
比较两个对象时,Python 会检查它们的值是否相同,即使它们可能是不同的对象类型。 -
is
运算符用于比较对象的身份是否相同,即比较对象是否是同一个对象。当使用is
运算符比较两个对象时,Python 实际上是比较它们在内存中的地址是否相同,即它们是否指向同一个内存地址。
简而言之,==
比较对象的值,而 is
比较对象的身份。在大多数情况下,使用 ==
来检查对象的值是否相等更为常见,而使用 is
来检查对象是否是同一个实例通常用于检查对象的身份是否相同。
3.列表(list)和元组(tuple)的区别
-
可变性:
- 列表是可变的 (Mutable),意味着列表中的元素可以被修改、添加或删除。
- 元组是不可变的 (Immutable),一旦创建,元组的内容不能被修改、添加或删除。
-
语法表示:
- 列表使用方括号
[ ]
来表示,例如:[1, 2, 3]
。 - 元组使用圆括号
( )
来表示,例如:(1, 2, 3)
。注意,即使元组只有一个元素,也需要在元素后面加上逗号,例如:(1,)
。
- 列表使用方括号
-
性能:
- 由于元组是不可变的,因此在某些情况下,元组的访问和操作速度可能比列表更快。因为不需要额外的内存空间来支持动态增长和缩小。
- 列表的可变性允许它们更灵活地进行操作,但也导致了在插入、删除等操作时需要更多的开销。
-
适用场景:
- 当需要存储一组数据,并且希望这组数据是可变的时,通常会选择列表。
- 当需要确保数据的不可变性,并且希望提高性能时,可以选择使用元组
4.如何判断两个对象相等【同第二题】
is
三、SQL
mysql字段类型
1.数据库索引失效
首先索引是提升查询速度的一种数据结构
索引失效的原因:
1.查询条件中有or: 创建一个user 表和一个job表,执行如下sql
explain SELECT name,age,address FROM user where name = '光头强' or age=9
上述SQL中name字段上有索引index_name,走的是全表扫描
2.like查询是以%开头,如果想让以‘%’开头仍然使用索引,则需要使用覆盖索引,即只查询带索引字段的列,如下所示:
explain SELECT name FROM user where name like '%头强'
3.对查询的列上有运算或者函数的
4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
explain SELECT name,age,address FROM user where name = 10
--等价于 mysql有个类型转换规则就是将“字符串转换为数字”
explain SELECT name,age,address FROM user where cast(name as signed)= 10
5.左连接查询或者右连接查询查询关联的字段编码格式不一样
例如user 表的name字段编码是utf8mb4,而job表的name字段编码为gbk
6.如果mysql估计使用全表扫描要比使用索引快,则不使用索引
7.如果查询中没有用到联合索引的第一个字段,则不会走索引
8.连接查询中,按照优化器顺序的第一张表不会走索引【不懂】
2.索引的分类、联合索引和单索引的应用场景
- 单列索引:索引只包含一个列,适用于基于单个列进行查询的场景。
- 联合索引(复合索引):索引包含两个或更多列,适用于基于多个列进行查询的场景。
- 唯一索引(unique key):确保索引列中的每个值都是唯一的,适用于需要强制数据唯一性的场景,如用户邮箱、身份证号等。
- 主键索引(primary key):特殊的唯一索引,每个表只能有一个主键,适用于标识表中每一行的唯一记录。
- 全文索引(FullText):针对文本数据的索引,适用于全文搜索场景,如搜索文章、博客等含大量文本的数据库。
联合索引的应用场景:
联合索引涉及到多个列,创建时顺序很重要,因为数据库会基于索引中列的顺序来优化查询。其适用场景包括:
- 多条件查询:当经常需要同时根据多个列进行查询时,联合索引可以提高查询效率。
- 排序与分组:当查询包含ORDER BY或GROUP BY多个列时,联合索引可以提高处理速度。
- 索引覆盖查询:如果查询只需要索引中的列,数据库可以直接使用索引来返回结果,而无需访问表中的数据,这称为索引覆盖查询。
3.索引底层为什么用B+树而不用红黑树等
红黑树【平衡二叉查找树】:
一颗有N个节点的红黑树的高度最多不超过2log(n+1),查找效率较高,稳定 ; 选择红黑树作为查找、删除、插入的存储结构
B+树:
通常用于数据库和操作系统中的索引和文件系统
所有的值这都出现在叶子点解,而内部节点(非叶子结点)仅用于索引,不实际存储数据,这样的设计使得B+树特别适合于系统中需要大量范围查询的场景
索引使用B+树的原因:
1.磁盘I/O效率:数据库存储在磁盘上,而磁盘访问速度远低于内存。B+树是一种多级索引结构,其树比较矮,这意味着每次磁盘I/O可以加载大量的节点。
2.查询效率较高:B+树通过减少访问节点的数量来优化查询操作,所有的值查询都在叶子节点上进行,并且叶子节点之间是相互链接的,这使得范围查询(如查找在某个区间内的所有记录)非常高效。红黑树等平衡二叉树在进行范围查询时可能需要频繁地遍历树的不同层级,效率较低。
3.空间利用率:B+树的设计使得每个节点都几乎完全填满,这提高了空间利用率。而二叉树结构中,每个节点只包含一个元素和两个子节点链接,这在大规模数据存储中可能导致较低的空间效率。
4.顺序访问优化:B+树的叶子节点之间通过指针相连,这为顺序访问和范围访问提供了很大的便利。在实际应用中,顺序访问数据的需求远远高于随机访问,B+树在这方面的优化使其更适合数据库索引的需求。
5.写操作优化:B+树的结构允许在分裂和合并节点时局部化更新,减少了对整个树结构的影响,从而提高了写操作的效率。而在红黑树等平衡二叉树中,插入和删除操作可能需要通过旋转来维持树的平衡,这在某些情况下可能会涉及到多个节点的调整。
红黑树的插入和删除操作可能需要进行颜色变换和旋转操作,这增加了实现的复杂性。特别是在频繁的插入删除操作场景下,这些操作可能会造成性能的下降。
红黑树的每个节点都需要额外存储一个颜色信息,这增加了内存占用。相比之下,B树和B+树的节点结构相对简单,能够更有效地利用内存空间。
红黑树在范围查询操作中可能需要进行中序遍历,而且遍历过程中的节点访问顺序是不确定的,这导致了范围查询的效率较低。
4.创建索引 怎么选择字段
-
在where语句中使用最频繁的字段,如User表中字段id
-
联接语句中的连接字段
-
字段具有唯一性,(例如没有重复的值)
-
有序的字段在插入数据库过程中,仍能保持B+树的索引结构,不需要频繁更新索引文件,性能更好
-
不在经常被修改的字段上创建索引
-
区分度比较低的字段不建议创建索引【例如:用户表中的性别区分度比较低,不如生日字段适合创建索引】
-
过长的字段不适合创建索引,过长的字段会占用更多的空间,不适合创建索引
5.事务的目的 特性
事务是一组操作的集合,不可分割的单位,事务会把所有的操作作为一个整体其一向系统提交或撤销操作请求,这些操作要么同时成功,要么同时失败。
事务的特性(ACID):
原子性:不可分割
一致性:事务完成时,必须使所有的数据都保持一致状态【事务前后的数据完整性要保持一致】
隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
永久性:事务一旦提交或回滚,他对数据库中的数据的改变就是永久的【事务一旦提交则不可逆,被持久话到数据库中】
6.第二高薪水
selsect *
from Employee
where salary = {
select distinct salary
from employee
order by salary DESC
limit 1 offset 1
};
-- limit 每页显示一行内容
-- offset 从第几条记录开始显示 计数是从0开始的
-- SELECT DISTINCT语句在SQL中用于返回唯一不同的值。当使用SELECT DISTINCT时,如果有多个相同的值
-- 则这些值只会被列出一次。
7.返回入职时间倒数第二员工
select *
from employee
where inDate = {
select inDate
from employee
order by inDate DECS
limit 1 offset 1
}
8.表中添加一列
例如:员工表中添加薪水列
alter table employee
add colum salary varchar(20)
-- 写法二
ALTER TABLE Employees
ADD COLUMN Salary DECIMAL(10, 2) DEFAULT 0.00;
--DECIMAL(10, 2)意味着这个数最多可以有10位数字,其中小数点后面最多有2位,适用于表示薪水等金额
9.以美团订单详情系统设计 设计为例,设计数据库
数据库需要考虑多个方面,包括订单、用户、商家、商品、支付和评价等。为了简化,我们将重点放在核心的几个表上:用户(Users)、商家(Merchants)、商品(Products)、订单(Orders)和订单详情(OrderDetails)
- 数据类型:选择合适的数据类型以优化存储空间和查询性能,如使用
INT
类型存储ID,VARCHAR
存储字符串,DECIMAL
存储金额。 - 索引:可以为经常用于搜索的列(如
OrderId
、UserId
、MerchantId
等)创建索引以提高查询效率。 - 规范化:设计时考虑数据库的规范化以避免数据冗余和维护数据一致性。