SQL
数据库设计的原则
数据库设计是确保数据库系统能够有效、高效地满足用户需求的过程。以下是一些基本的数据库设计原则:
-
需求分析:在设计数据库之前,首先要明确数据库需要支持的业务需求。
-
数据模型:选择合适的数据模型(如关系模型、文档模型等)来表示数据。
-
规范化:通过规范化过程减少数据冗余和依赖,提高数据一致性。规范化通常涉及将表分解成更小的、关系更清晰的表。
-
实体-关系模型(ER模型):使用ER模型来表示数据实体及其之间的关系,这有助于设计逻辑数据模型。
-
数据完整性:确保数据库中的数据是准确和一致的。这包括实施实体完整性、参照完整性和用户定义的完整性约束。
-
性能考虑:在设计时考虑查询性能,包括索引的使用、查询优化和适当的数据分区。
-
安全性:设计数据库时要考虑数据的安全性,包括访问控制、加密和审计。
-
可扩展性:设计时应考虑未来可能的扩展,确保数据库能够适应数据量的增长和新需求的添加。
-
可维护性:数据库设计应便于维护和更新,包括数据的备份和恢复策略。
-
用户和使用场景:设计应考虑最终用户的需求和使用场景,确保数据库能够满足他们的操作习惯和性能要求。
-
避免数据冗余:尽量减少数据重复,以减少存储空间的浪费和维护的复杂性。
-
灵活性:设计时应留有一定的灵活性,以便未来可以轻松地添加新的数据类型或修改现有结构。
这些原则是数据库设计过程中的重要指导思想,有助于创建一个健壮、高效和易于管理的数据库系统。
mysql中索引为什么使用B+树?
首先,B树和B+树都是自平衡的多路查找树 小的在做,大的在右
两者的区别:
1.B树每个节点存储键值和数据-树更高(每个节点都存储了数据和索引);B+数据只存储在叶子结点-树更矮,非叶子节点只存储索引
2.无叶子结点链表结构-范围支持不好,叶子节点通过上香链表相连-查询范围性能更好
更准确回答:
- B 树的所有节点既存放键(key) 也存放数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
- B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
- B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
- 在 B 树中进行范围查询时,首先找到要查找的下限,然后对 B 树进行中序遍历,直到找到查找的上限;而 B+树的范围查询,只需要对链表进行遍历即可。
综上,B+树与 B 树相比,具备更少的 IO 次数、更稳定的查询效率和更适于范围查询这些优势。
Java
Java创建对象的过程
类的定义、实例化、内存分配和构造方法的调用、使用对象
1.Java中堆区的内存分配?
堆区用于存储所有对象实例和数组,是动态分配内存的区域,所有对象都在堆中分配内存
JVM 的内存区域
线程共享:堆、字符串常量池、方法区、运行时常量池
线程私有的:虚拟机栈、本地方法栈、程序计数器
程序计数器:当前线程锁执行的字节码的行号指示器,可以保证线程切换后恢复到正确的执行位置
Java虚拟机栈:生命周期和线程相同,随着线程的创建而创建。。。,方法调用的数据需要通过栈进行传递,每一个方法调用都会有一个对应的栈帧被压入栈中,包括:局部变量表、操作数栈、动态连接、方法返回地址【为虚拟机执行是Java方法服务】
本地方法栈:和虚拟机栈方法类似,为虚拟机使用到的native方法服务
堆:new的对象都存放在堆中,所有对象实例和数组
方法区:虚拟机需要使用一个类时,需要读取并解析class文件获取相关信息,再将信息存入到方法区中,存储已经被虚拟机加载的类信息、字段信息、方法信息、常量等数据
运行时常量池:可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
【方法区主要存放类的信息,运行时常量池】
字符串常量池:是针对字符串开辟的一快区域,主要目的是为了避免字符串的重复创建
2.垃圾回收机制
新生代:采用的是一种简单高效的复制算法进行垃圾回收 可谓一快速完成内存回收减少暂停时间-有效处理大量短暂对象的分配与回收
老年代:存储长期存活的对象和大对象
1.JVM创建对象的过程:创建一个新的对象时,JVM首先会在堆中分配内存空间,大部分情况下,新对象会被分到新生代的eden区,新生代包括Eden区和两个Surivior区,当Eden区满了后会进行minior GC ,在GC过程中中,存活的对象会在两个Surivor区域进行转移和交换,经过多次GC,仍然存活的对象会被晋升到老年代,老年代用来存储长期存活的对象和大对象
2.堆内存中划分新生代和老年代的作用
新生代采用的是一种简单高效的复制算法进行垃圾回收机制,快速完成回收减少暂存时间,大部分对象都是短暂存储的,可以有效处理大量短暂对象的分配与回收
针对不同生命周期的对象采用不同回收策略,新生代频繁回收,老年代较少回收,提高操作系统的性能
Java中垃圾回收算法
java中有四种垃圾回收算法,分别是标记清除法、标记整理法、复制算法、分代收集算法;
标记清除法: 第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记; 第二步:在遍历一遍,将所有标记的对象回收掉; 特点:效率不行,标记和清除的效率都不高;标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC;
标记整理法:第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记; 第二步:将所有的存活的对象向一段移动,将端边界以外的对象都回收掉; 特点:适用于存活对象多,垃圾少的情况;需要整理的过程,无空间碎片产生;
复制算法: 将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除; 特点:不会产生空间碎片;内存使用率极低;
分代收集算法:根据内存对象存活周期不同,将内存划分成几块,例如新生代和老年代,新生代采用复制算法,老年代采用标记整理法或者标记清除法,
6.面向对象的好处有哪些?抽象类和接口的区别
3.线程 进程以及各自的区别
进程:指的是计算机中正在运行的一个程序实例,是系统运行程序的基本单位,例如打开的微信就是一个进程,哥进程之间是独立的。
线程:一个比进程更小的执行单位,一个进程在执行过程中可以产生多个线程,并且多个线程之间可以共享进程的堆和方法区资源,但是每个线程有自己的程序计数器、虚拟机栈和本地方法栈,因此系统在产生一个线程,或者线程之间做切换时,负担要比进程小得多,因此,线程被称为轻量级进程。
线程和进程之间的区别?
一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
线程是进程划分成的更小的运行单位,线程和进程最大的不同在于基本上个进程是独立的,各个线程则一不定,因为同一进程中的线程极有可能会相互影响,线程执行开销小,但不利于资源的管理和保护,而进程正相反。
Java中创建线程的几种方式:
- 通过继承Thread类创建线程,重写run方法 没有返回值
- 实现runable接口,重写run方法
- 实现callAble接口
归根到底,都是通过Thread().start()创建线程的
多线程和线程池有什么区别?
1.概念:
多线程:多线程是指在一个程序中同时执行多个线程,每个线程都是一个轻量级的执行单元。线程可以并发的执行不同的任务;多线程可以通过继承Tread或实现Runable接口创建线程,每创建一个线程,系统都需要为期分配资源,并进行上下文切换,每个线程的生命周期由操作系统管理,创建和销毁线程需要消耗资源,频繁创建和销毁会降低性能。
线程池:线程池是一种管理和复用线程的机制,预先创建一定数量的线程,被存储在池子中,当有任务需要执行时,线程池回从池中分配一个空闲的线程来执行任务,任务完成后,线程不会被销毁,返回池中等待下一个任务。通过重用现有线程来减少线程的创建和销毁过程。线程池管理线程的生命周期,包括创建和销毁,允许系统根据负载动态调整线程数量,支持任务的排队和调度,使得任务的执行更加高效
2.使用场景:
多线程:适用于一些简单的异步操作或者任务执行时间较长、任务数量较少的场景
线程池:更适合于处理大量的短任务
threadLocal怎么用的?线程池会复用线程,那这个线程先前访问的资源对象会被修改吗?
ThreadLocal
是 Java 中用于为每个线程提供独立变量副本的机制,每个线程可以独立使用自己的变量副本,而不会影响其他线程。它是解决线程安全问题的一种方式,避免了多线程环境下对共享变量的竞争。
线程池复用线程时 的问题:
当一个线程被复用时,TreadLocal 中存储的变量不会自动清空,新任务可能会使用前一个任务的遗留数据,造成意外错误。
线程池中的线程 T1
执行任务 A 后修改了 ThreadLocal
中的值,线程池将 T1
复用到任务 B 中时,如果没有清除或更新 ThreadLocal
的值,任务 B 可能会读取到任务 A 遗留的值。
解决方案:手动调用ThreadLocal.remove()方法,在线程任务结束时清除ThreadLocal中的数据,避免线程复用时出现数据污染。
如何保证线程安全?
1.使用synchronized关键字:类、方法或者代码块被其修饰时,同一时间只能有一个线程访问该方法或者代码块。
2.使用ReentrantLock 提供了更灵活的锁机制,如锁的可重入性、等待可中断、尝试锁定和超时功能等 加锁 解锁 lock.lock lock.unlock
3.使用volatile 关键字 确保了一个线程对变量的写操作对其他线程可见
4.使用线程局部变量(ThreadLocal):为每个线程提供了副本,避免了多个线程共享同一个变量,从而实现线程安全
进程的状态
创建状态
就绪状态:进程已经处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源即可运行
运行状态
阻塞状态:又称为等待状态,进程正在等待某一事件而暂停运行:例如等待某资源可用或等待IO操作完成
结束状态:
进程间的通信方式
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信
1.匿名管道:具有亲缘关系的父子进程或兄弟进程之间的通信
2.有名管代:先进先出,实现本机任一两个进程通信
3.信号:通知接收进程某个时间已经发生
4.消息队列:可以实现消息的随机查询,消息不一定要先进先出的次序读取,也可以安小熙的类型读取
5.信号量:用于多进程对共享数据的访问,在于进程之间同步
6.共享内存:不同进程可以及时看到对方进程中堆共享内存中数据的更新
7.套接字:客户端和服务器端通过网络进行通信,可以看做不同主机之间的进程进行双向通信的一种约定
进程之间的调度方法
先到先服务调度算法
短作业优先调度算法
时间片轮转调度算法
多级反馈队列调度算法
优先级调度算法
线程之间的通信方式
- 共享变量:通过
synchronized
、volatile
等保证可见性和一致性。 wait()
/notify()
/notifyAll()
:协调多个线程的执行顺序。Future
和Callable
:线程池中线程之间通过返回值通信。BlockingQueue
:生产者-消费者模式,线程通过队列进行通信。PipedInputStream
和PipedOutputStream
:通过管道流在线程之间传递数据。CountDownLatch
和CyclicBarrier
:用于多线程协作和同步。
sleep()和wait()方法对比
共同点:两者都可以暂停线程的执行
区别:
1.sleep()方法没有释放锁,仍然持有锁的对象,其他线程无法访问该对象的同步代码块;wait()方法释放了锁,线程进入等待状态,其他线程可以获得该锁。
2.sleep():线程在指定时间到达后自动恢复运行,无需其他线程唤醒;wait方法必须等地啊其他方法调用来唤醒 notify() 或 notifyAll()唤醒
3.sleep是Thread类的静态方法,wait()是Object类的本地方法
4.使用场景:sleep()让当前线程暂停执行一段时间,通常用于实现线程的定时操作,不涉及线程间的通信
wait用于线程间的协调。
synchronized锁static方法有什么用
synchronized
关键字用于实现同步,防止多个线程同时访问共享资源,synchronized
锁定一个 static
方法时,其作用是确保该类的所有实例共享同一个锁,从而限制多个线程同时执行该类的 static
方法
synchronized
锁定 static
方法和锁定普通方法之间的区别在于:
锁定普通方法:用在非static关键字上,锁的对象是当前实例对象,每个实例对象都有自己的锁,只有当一个线程持有该对象的锁时,其他线程才无法访问该实例的同步方法
锁定static方法:用在static上时,锁定的对象是整个类对象
有哪些场景导致栈溢出
1.无限递归:当函数不断调用自身而没有适当的终止条件时,栈帧会不断增加,导致栈空间耗尽
2.大局部变量:函数中定义了大量的局部变量,导致栈空间不足
3.过多的函数调用:如果程序中存在大量的嵌套函数调用,栈空间也可能会耗尽
4.错误的线程管理:线程栈空间设置过大,或线程创建过多,也会导致栈溢出
4.怎么用栈实现队列
5.list,set, map谈一谈他们的特点
首先,List、Set、map是三种常用的集合接口
1.List:内部是基于动态数组实现的
特点:
有序:保持元素的插入顺序; 可重复:允许存储元素重复 索引访问:通过索引访问元素
常用的list集合:ArrayList、LinkedList
应用场景:需要保存元素顺序的场景:如购物车、任务列表等
2.Set
特点:无序:不保证元素的顺序; 不重复:不允许存储重复的元素
常用Set集合:HashSet(哈希表实现)、LinkedHashSet(哈希表+链表实现)、TreeSet(基于红黑树实现)
应用场景:需要保持元素唯一性场景, 用户注册、标签管理等
3.Map
键值对:存储键值对,每个检是唯一的
HashMap、LinkedHashMap、TreeMap
5.面向对象的好处有哪些?抽象类和接口的区别
1.面向对象不需要关注创建对象的过程,例如,创建汽车对象,不需要关注其是如何制造的,只需要关注汽车的属性以及他的功能即方法
面向对象的好处:
易维护:由于良好的结构和封装性,更容易维护
易复用:通过集成和多态,可以使代码有更高的复用性
易扩展:模块化设计使得系统变得更加容易和灵活
2.抽象类和接口的区别:
共同点:接口和抽象类都不能直接实例化,只能被实现或者继承后才能创建具体的对象; 抽象类和接口都包含抽象方法,抽象方法没有方法体,必须咱子类或实现类中实现
区别:
设计目的:接口主要用于对类的行为进行约束,实现了某个接口就有了对应的行为;抽象类主要十位了代码复用
继承和实现:一个类只能继承一个类,单继承;而接口可以多实现,一个接口可以实现一个或多个接口
成员变量:接口中的成员变量只能是Public static final 类型的,不能被修改且必须有初始值,抽象类中的成员变量可以有任何修饰符,可以在子类中被重新定义或者赋值
6.死锁
多个线程/进程同时被阻塞,他们中的一个或者多个都在等待某个资源释放
产生死锁的四个条件:
互斥:资源之间不共享,即当前资源只有一个进程或线程可以使用
占有并等待:进程至少占有一个资源,并等待另一个额资源,且该资源别其他进程占有
非抢占:资源不能抢占,只有在持有资源的进程完成任务后,该资源才能释放
循环等待:p0等待p1,.....,pn等待p0
解决或预防死锁:
7.java 中如何实现自定义注解
1.自定义注解 使用@interface关键字定义注解,
2.在类、方法、字段等地方使用自定义注解
3.通过反射机制可以读取注解并处理其属性
8.反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象。都能够调用它的任意一个方法和属性;动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。可与动态编译结合Class.forName('com.mysql.jdbc.Driver.class');
,
反射机制的优缺点有哪些?
优点:能够运行时动态获取类的实例,提高灵活性;可与动态编译结合Class.forName('com.mysql.jdbc.Driver.class');
,加载MySQL的驱动类。
缺点:使用反射性能较低,需要解析字节码,将内存中的对象进行解析。其解决方案是:通过setAccessible(true)关闭JDK的安全检查来提升反射速度;多次创建一个类的实例时,有缓存会快很多;ReflflectASM工具类,通过字节码生成的方式加快反射速度。
如何获取反射中的class对象
class.forName("类的路径")
类名.class
对象名.getClass()\
为什么引入反射概念?反射机制的应用有哪些
反射可以让开发人员通过外部类的全部路径名创建对象,并使用这些类,实现一些扩展的功能,并且可以枚举出类的全部成员,包括狗仔函数、属性、方法
测试时可以利用反射APIf昂文类的私有成员,保证测试代码覆盖率
应用:
JDBC 数据库的连接
Spring框架的使用,最经典的就是XML的配置模式
9.什么是装饰器?(python中装饰器)
一种设计模式,允许在不修改现有对象结构或代码的基础上,动态的给对象添加额外的功能,装饰器本质上是一个函数,它接受一个函数作为输入,并返回一个新的函数。具体使用方法就是在函数名签@+函数名可以实现绑定给函数的第二个功能
应用场景:
10.什么是闭包?
闭包是指在一个函数内部定义的函数,并且这个内部函数可以访问其外部函数的变量,及时外部函数已经执行完毕,内部函数依然可以访问和操作浙西额变量。本质上讲,闭包是讲函数内部和外部链接起来的一个桥梁
应用场景:
数据隐藏和封装:可以通过闭包隐藏一些变量,使得外部代码无法直接访问这些变量,只有通过闭包内部提供的函数才能访问和修改
事件处理和回调函数:闭包经常用于事件处理和回调函数。例如,在一个按钮点击事件的回调函数中,可能需要访问外部的变量,闭包就可以很好地实现这个功能,使得回调函数可以访问和操作定义它时所在的外部环境中的变量。
11.解释Java字节码
在Java中,JVM可以理解的代码就叫做字节码(即是 扩展名为.class的文件),他不面向任何特定的处理器,只面向虚拟机,因此在一定程度上解决了解释型语言编译效率低的问题,因此Java程序运行时相对来说还是比较高效的,还有一点就是字节码并不针对特定的机器,因此,一次编写,到处运行,Java程序无需重新编译便可在多种不同的操作系统的计算机上运行
字节码的好处:
跨平台性:上述
安全性:字节码在执行前可以进行多种安全检查,可以检查字节码是否符合Java语言规范,是否有非法的操作(例如 数组越界、非法类型转换等)
12 深拷贝、浅拷贝 ——Linux中硬链接和软连接(快捷键)
浅拷贝:当一个对象被浅拷贝时,新对象被创建,但对象内部的引用成员并没有被复制,也就是说,新对象和原始对象共享相同的引用成员
若引用成员指向的是不可变对象(String、Integer等),那么浅拷贝通常是安全的
若引用成员指向的是可变对象,那么修改新对象后原对象的值时都会发生改变
深拷贝:当一个对象被深拷贝时,新对象被创建,同时对象内部的所有引用成员即地址也会被复制,新对象和原对象完全独立
深拷贝确保了原始对象与复制对象之间的修改不会相互影响
深拷贝通常比浅拷贝更加耗时
13.捕获异常
14.== 和equals的区别
== 对于基本数值类型,比较的是值
==对于引用数据类型,比较的是对象的内存地址 (引用类型变量存的值是对象的地址)
equals()不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等,equals存在于Object类中,所有类都有equals方法
如果没有重写equals方法时,比较的是对象的内存地址
如果重写equals方法时,比较的是重写之后的方法,例如String中重写equals方法后,比较的就是两个对象的值是否相等
15.hashCode有什么用?重写equals()必须重写hashCode方法?
1.hashCode()的作用是获取哈希码,哈希码的作用是确定该对对象在哈希表中的索引位置
以HashSet为例,向里面插入对象时,首先会计算对象的hashCode值来判断对象加入位置,同时也会与 已经加入的对象的hashCode值作比较,若没有与之相同的哈希值,直接加入hashSet中,若有相同的HashSet中,则使用equals()方法进行比较,判断他们的值是否相同,指相同,不允许加入,值不同时,重新散列到其他位置,大大减少了equals的次数,提高执行速度。
2.两个对象hashCode相等,对象不一定相等(哈希碰撞)
两个对象的hashCode值相等并且equals()方法也返回true,才认为两个对象相等
两个对象的hashCode值不相等,对象不相等
因为两个相等的对象的 hashCode
值必须是相等。也就是说如果 equals
方法判断两个对象是相等的,那这两个对象的 hashCode
值也要相等。
哈希碰撞
开放地址法:我们在遇到哈希冲突时,去寻找一个新的空闲的哈希地址。
1.线性探测法:所需要的位置被占了,就往后面一直加1 并对M取模直到存在一个空余的地址共我们存放值,取模是为了保证找到的位置在0~m-1 的有效空间之中。
2.平方探测法:当我们的所需要存放值的位置被占了,会前后寻找而不是单独方向的寻找。
公式:h(x)=(Hash(x) +i)mod (Hashtable.length);(i依次为+(i^2)和-(i^2))
再哈希法:同时构造多个不同的哈希函数,等发生哈希冲突时就使用第二个、第三个……等其他的哈希函数计算地址,直到不发生冲突为止。虽然不易发生聚集,但是增加了计算时间
链地址法:将所有哈希地址相同的记录都链接在同一链表中。
16.String、StringBuffer、StringBuilder的区别?
String:是不可变的
1.线程安全:String中的对象是不可变的,也就可以理解为常量,是线程安全的;Stringbuilder、StringBuffer中定义了一些字符串的基本操作,StringBuffer对方法加了同步锁,是线程安全的,StringBuilder没有对方法加同步锁,非线程安全的
2.性能:String类型每次改变的时候,都会生成新的String对象。StringBuffer与StringBuilder每次都会对StringBuffer对象本身进行操作,因此性能比较高
应用场景:
操作少量数据:使用String
单线程 操作字符串缓冲区下操作大量数据:StringBuilder
多线程操作字符串缓冲区下操作大量数据:StringBuffer
17.JVM、JDK、JRE
JVM 运行字节码的虚拟机,JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果
JRE:运行已编译Java程序所需的环境,主要包括JVM、Java基础类库
JDK:是一个功能齐全的Java开发工具包,供开发者使用,用于创建和编译程序
18.break、continue、return的区别以及作用?
break :结束当前的循环
continue:跳出本次循环,继续执行下次循环(结束正在执行 的循环 进入下一个条件的循环)
return:程序返回,不再执行下面的方法(结束当前的方法,直接返回)
19.final、finally、finalize的区别
1.final用于修饰变量、方法和类
final修饰变量:变量不可变,引用不可变和对象不可变 final修饰的变量必须初始化,通常被修饰的变量为常量
final修饰方法:不允许子类重写,但可以被子类使用
final修饰类:不能被继承,并且类中所有方法不能被重写
2.finally 用于捕获异常 try-catch-finally finally中的语句块是一定要被执行的,无论是否抛异常
try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会执行,在 return 前执行。
在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回值会对程序造成很大的困扰,Java 中也可以通过提升编译器的语法检查级别来产生警告或错误。
3.finalize:是Object中的方法,每一个对象都有一个这样的方法,在垃圾回收时启动,该对象被回收时进行调用
20.static关键字
21.Java 静态代码块、构造代码块、普通代码块执行顺序
静态代码块——》构造代码块——》普通代码块
22.包装类型是什么?
Java为每一个基本数据类型都引入了对应的包装类型,int的包装类型就是Integer,把基本类型转换为包装类型的过程叫做装箱;反之,将包装类型转换成基本类型的过程叫做拆箱,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
基本类型和包装类型的区别
1.包装类型可以为NULL,而基本类型不可以;
基本类型在未复制的时候有默认值,如int默认是0,Boolean默认类型时false;包装类型是对象,未初始化时默认值为null
2.包装类型可用于范型,而基本类型不可以。范型不能使用高级本类型,因为使用基本类型时会编译出错
3.基本类型比包装类型更高效。基本类型由于直接存储,内存占用小,执行速度快;包装类型由于需要创建对象,存储引用,内存占用大,且需要额外的拆箱和装箱操作,性能较差
4.基本类型可以用== 比较两个值,比较的是数值; 包装类型用==比较时比较的是对象的引用地址,而不是数值,一般使用equals()方法
23.泛型
泛型就是将类型参数化,一开始也不知道具体的参数类型,只有在编译的时候才可以具体绑定
泛型核心思想:使得类、接口和方法可以应用于多种类型,使得代码能够处理多种数据类型,避免重复代码
List<T>
T 占位符 或者? 通配符
? extends T
:表示上界通配符,允许泛型类型是T
或T
的子类。? super T
:表示下界通配符,允许泛型类型是T
或T
的父类
通过泛型,可以在编译期检查类型安全,同一段代码可以处理多种类型的数据,而不需要为每种类型写单独的代码;并且可以消除冗余的类型转换,提高代码的可读性和可维护性
基本类型不能作为泛型类型参数:只能使用引用类型,Java的泛型是基于对象的
泛型能不能被继承
泛型本身是不能继承的,例如 List<Integer>
不是 List<Number>
的子类;但是泛型类型的类或者接口是可以继承或实现的
泛型类与接口的继承:可以让泛型类继承另一个泛型类,只要参数类型相同即可
24.序列化与反序列化
序列化:将Java对象转换成有序字节流,以便在网络上传输或者保存到本地文件中,核心作用是对象状态的保存与重建 例如U盘 Java对象序列化成可存储或传输的形式,例如保存在文件中,每次需要的时候,从文件中读取二进制流,在从二进制流中反序列化出对象
序列化目的:java.io.Serializable接口
持久化:将对象的状态保存到文件中,便于以后进行恢复
网络传输:将对象通过网络发送给其他应用或者进程
深度复制:可以通过序列化和反序列化实现对象的深度复制
反序列化:将字节序列转换回为Java对象的过程 ObjectInputStream
类来实现反序列化
主要用途:从网络、文件或数据库中读取对象并重新构造对象
为什么需要序列化与反序列化
对内存中的对象进行持久化或网络传输,这个时候都需要序列化和反序列化
25.异常:error和exception
error和exception都是继承Throwable类
exception:程序本身可以处理的异常,通过catch进行捕获,对其进行处理,使程序可以继续正常运行;可以分为:运行是异常和非运行时异常
error:程序无法处理的错误,不能通过catch进行捕获,例如:内存不足堆栈溢出等,一旦发生这种错误,通常应用程序会被终止,需要人工处理
运行时异常:表示在JVM在运行期间可能出现的异常,RuntimeException类及其子类:空指针异常、数组越界、类型转换异常
受检查异常:是Exception 中除 RuntimeException
及其子类之外的异常;IO相关的异常、ClassNotFoundException、SQLException异常
26.throw 和 throws区别
- throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。
- throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。
27.什么是 Spring IOC 容器?
1.控制反转【Inversion of Control】一种设计思想 控制: 控制对象的创建过程;反转:创建对象的主体,由程序员转换为容器,使得开发人员更专注于业务逻辑,而不是对象的创建和管理
IOC容器就像一个工厂一样,当需要创建一个对象的时候,只需要配置文件、注解,完全不用考虑对象是如何被创建出来的【 IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。】
容器:单例工厂 每一个对象Bean 管理单例Bean
创建过程和耗时过程全部在启动期间完成,内存当中保存的对象都是单例状态,每次使用直接从缓存中获取即可,保证程序的可靠性和稳定性
解耦:高层模块依赖于接口,底层模块需要实现接口
好处:
1.降低耦合度:降低了组件之间的耦合度,使得对象之间的关系更加灵活【不直接创建对象,通过IOC容器注入,减少了组件直接的直接依赖,提高了代码的灵活性和可维护性】
2.提高可维护性:通过容器管理对象的生命周期,使得代码更易于维护和理解 例如在修改对象实例化方式或更新依赖关系时,只需要修改IOC容器的配置,不需要在整个代码中修改
3.增强可测试性:支持依赖注入没事的单元测试更加容易
28.什么是Spring的AOP
@Aspect 切面
面向切面编程,将一些跟业务没有关联性的逻辑剥离出来统一处理
AOP 通切面将横切关注点从业务逻辑中分离出来,提高了代码的模块性,允许对一些横跨对个业务对象的非业务逻辑(日志、事务、安全性)等进行统一的处理,是代码更加清晰易懂,提高了代码的重用性和可维护性。
链接点:在应用程序执行过程中,切面可以插入的点,如方法执行的时候
切入点:定义一组连接点的表达式,指定切面在何处执行
29.Spring事务传播机制
定义了事务方法如何与已经存在的事务进行交互,简单理解就是多个事务方法相互调用时,事务如何在这些方法间传播
在繁琐业务场景中,多个事务方法间的调用可能引起事务不一致,如数据丢失,重复提交等
七种事务传播级别:
30.Autowired和 Resource 的区别
都是用于依赖注入的注解,用户将其他 组件或资源自动注入到目标对象中
@Autowired :属于Spring内置的注解,在获取Bean时,先通过类型进行匹配,在根据名称进行匹配,可以用于构造器、字段、方法或者参数上
@Resource :JDK提供的注解 先通过名称进行匹配,在根据类型进行匹配,不能用于构造器
31.BeanFactory 和 FactroyBean 的区别
31.Spring SpringMVC
核心功能主要是IOC和AOP
Core Container:Spring-Core【核心工具类】、beans【Bean对象的创建、管理配置】、
AOP:“aspects、AOP”
Data Access:
Web:
Spring:Java开源框架为其他Spring组件提供核心功能和基础设施 例如L以来注入、面向漆面编程、事务管理
Spring MVC:构建Web应用程序 遵循 模式-试图-控制器设计模式;模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
SpringBoot:是快速开发框架,简化Spring应用的开发过程,提供开箱急用的配置和默认配置
Spring 通常需要大量的XML或Java配置来定义Bean和其依赖关系
Springboot :通过简单发注解和少量的配置文件来快速设置应用,减少了繁琐的配置工作
32.什么是Spring Bean?
Bean 代指哪些被IOC容器所管理的对象
33.注入Bean的方式有哪些?
1.构造函数注入:
2.setter注入:通过类的Setter方法注入依赖项
3.字段注入:直接在类的字段上使用注解如 @Autowired
或 @Resource
)来注入依赖项
构造函数注入还是Setter注入:
34.Spring IOC 容器的工作原理
Spring IOC 容器的工作原理主要依赖于依赖注入,其过程大致如下:
-
读取配置:容器读取应用的配置文件或注解(XML 配置文件、Java 配置类、注解等),了解需要创建和管理的 Bean 及其依赖关系。
-
创建 Bean:容器根据配置或注解实例化所需的 Bean,并将它们放入容器中进行管理。
-
注入依赖:容器通过构造函数、Setter 方法或字段注入的方式,将 Bean 的依赖自动注入到相应的对象中。
-
管理 Bean 的生命周期:容器负责 Bean 的整个生命周期,包括创建、初始化、销毁等操作。开发者可以通过实现
InitializingBean
接口或定义@PostConstruct
、@PreDestroy
等注解来自定义 Bean 的初始化和销毁逻辑。
35.Spring IOC 容器的生命周期
Spring IOC 容器管理 Bean 的完整生命周期。生命周期的大致过程如下:
- 实例化 Bean:容器实例化 Bean 对象。
- 依赖注入:容器根据配置或注解为 Bean 注入依赖。
- 初始化 Bean:Bean 完成依赖注入后,可以执行初始化逻辑。可以通过实现
InitializingBean
接口或使用@PostConstruct
注解来自定义初始化行为。 - 使用 Bean:Bean 处于可使用状态,随时可以处理请求。
- 销毁 Bean:容器关闭时,可以调用 Bean 的销毁方法。可以通过实现
DisposableBean
接口或使用@PreDestroy
注解来定义销毁行为。
36.Spring MVC 工作原理
- 客户端(浏览器)发送请求,
DispatcherServlet
拦截请求。 DispatcherServlet
根据请求信息调用HandlerMapping
。HandlerMapping
根据 URL 去匹配查找能处理的Handler
(也就是我们平常说的Controller
控制器) ,并会将请求涉及到的拦截器和Handler
一起封装。DispatcherServlet
调用HandlerAdapter
适配器执行Handler
。Handler
完成对用户请求的处理后,会返回一个ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,包含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的View
。ViewResolver
会根据逻辑View
查找实际的View
。DispaterServlet
把返回的Model
传给View
(视图渲染)。- 把
View
返回给请求者(浏览器)
37.动态代理用什么实现的?动态代理有几种?
动态代理是指在程序运行时,动态的创建代理对象来为目标对象生成代理逻辑,而不需要在编译时定义代理类
动态代理主要用于AOP(面向切面编程)场景,比如权限控制、日志记录、事务管理等。
Java中的动态代理实现
Java中动态代理的实现主要有两种方式:
- JDK动态代理:基于Java内置的反射机制,只支持接口的代理,不能代理类本身
- CGLIB动态代:通过字节码操作实现,可以代理没有接口的类
38.ArrayList和LinkedList的区别
1.底层数据结构:ArrayList底层 使用的是Object数组,LinkedList底层使用的是双向链表的数据结构
2.两者都是不同步的,就是不保证线程安全
3.ArrayList采用数组存储,插入和删除操作手元素位置的影响,例如:插入操作,若在末尾进行插入操作,事件复杂度为O(1),在指定位置进行插入或删除,时间复杂度为O(n).LinkedList采用双向链表存储,在头部尾部进行插入删除时,事件复杂度为O1,在指定位置是,时间复杂度为ON。因为要先找到指定位置
4.ArrayList支持元素的随机访问,LinkedList不支持元素的随机访问
5.LinkedList需要吧ArrayList多消耗一些空间,因为每一个元素都需要存储他的直接前驱和后继。
39.HashSet、linkedhashSet TreeSet三者的异同
1.三者都是Set接口的实现类,都能保证元素的唯一性,并且都是线程不安全的
2.底层数据结构不同:HashSet 是基于哈希表 HashMap实现,LinkedHashSet 是链表和哈希表实现的,TreeSet底层数据结构式红黑树,元素是有序的
3.应用场景:HashSet不需要保证元素插入和取出顺序的场景,LinkedHashSet用于保证元素的插入和取出顺序满足先进先出的场景,TreeSet用于支持元素自定义排序规则的场景
40.ArrayDeque 和 LinkedList的区别
两者都实现了deque接口,都有队列的功能
1.ArrayDeque是基于可变长的数组和双指针实现,LinkedList通过链表实现
2.ArrayDeque不支持存储NULL数据,但LinkedList支持
3.ArrayDeque
插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然 LinkedList
不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。
41.HashMap和HashTable的区别
1.HashMap是线程不安全的,hashTable是线程安全的,hashTable内部的方法都经过synchronized修饰
2.HashMap要比hashtable效率高一些
3.HashMap可以存储NULL的key和value,但null作为键只能有一个,hashTable不允许有null值,否则会抛出空指针异常
42.hashSet如何检查重复
hashCode和equals()方法
1.8 HashMap 数组+链表实现
1.8之后: HashMap 数组+链表/红黑树 当链表长度大于某个阈值 默认为8时,就会转为红黑树,以减少搜索时间
43.comparable和comparator有什么区别
两者都是用于在Java中进行对象比较的接口
1.Comparable:接口位于java.lang包中,定义在类内部,通常用于对象的自然排序
Comparator:接口位于java.util中,定义在类外部,允许多个用户创建多个不同的比较器
2.Comparable:只包含一个方法 compareTo(T o)
,该方法用于定义“自然排序”,如果当前对象小于、等于或大于参数对象,则返回负数、零或正数
Compatator:包含两个方法,compare(T o1, T o2)
和 equals(Object obj)
,但通常只需要实现 compare
方法。用于比较两个不同的对象,并返回负数、零或正数,表示 o1 小于、等于或大于 o2
计网
1.TCP三次握手
第一次:客户端向服务器发送请求报文,申请建立连接,等待服务端的确认
第二次:服务器收到客户端的请求后,对客户端做出响应,同意建立连接
第三次:客户端收到服务器的响应后,再次向服务端发送报文,建立连接,至此,双方都知道对方收发数据是正常的
2.为什么要三次握手
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
- 第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
- 第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
- 第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常
三次握手就能确认双方收发功能都正常,缺一不可。
3.get和post的区别
1.从作用上来看:get通常用于获取或查询资源,post通常用于创建或修改资源
2.get是幂等的,多次重复执行同一个get请求 查询结果不会发生改变,而多次重复执行同一个post请求是,可能会产生不同的结果或影响资源的状态,例如在实习期间,post 请求方法,扩缩容,每次发送都会重新扩容
3.get请求的参数通常放在URL中,形成查询字符串,而post请求的参数通常放在请求体中,可以有多重编码格式,如表单类型、Json类型等
4.由于get请求是幂等的,因此可以被浏览器或者中间节点缓存起来,以提高性能和效率,而post请求不适合缓存
5.安全性:由于ge请求参数都是放在URL中,相比post请求而言更加容易泄露数据
4.HTTP与https区别
1.首先是端口号不同:HTTP默认80,https默认443
2.URL前缀:HTTPURL前缀是 http:// https前缀是:https://
3.安全性和资源消耗:HTTP协议运行在TCP协议之上,所有传输都是铭文传输,客户端和服务器端都无法验证对方的身份,是不安全的
https是运行在SSL/TLS 之上的HTTP协议,所有传输内容都经过加密,加密采用对称加密,但对称加密的秘钥采用服务器方的整数进行了非对称加密,所以安全性较高,但同时也耗费了更多的资源
SSL:安全套接字协议
SSL/TSL工作原理:SSL/TLS 的核心要素是非对称加密。非对称加密采用两个密钥——一个公钥,一个私钥。在通信时,私钥仅由解密者保存,公钥由任何一个想与解密者通信的发送者(加密者)所知——邮局的例子:发送者可以用公钥发送邮件,但只有接收者 保存的私钥进行解密,这样通信信息就不会被其他人截获了
5.TCP四次挥手
TCP是全双工通信,可以双向传输数据,任何一方都可以在数据传送后发出连接释放的通知,待对方确认后进入半关闭状态,当另一方也没有数据要发送时,则发出连接释放的通知,对方确认就完全关闭了TCP连接
- 第一次挥手:客户端发送一个 FIN(SEQ=x) 标志的数据包->服务端,用来关闭客户端到服务端的数据传送。然后客户端进入 FIN-WAIT-1 状态。
- 第二次挥手:服务端收到这个 FIN(SEQ=X) 标志的数据包,它发送一个 ACK (ACK=x+1)标志的数据包->客户端 。然后服务端进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。
- 第三次挥手:服务端发送一个 FIN (SEQ=y)标志的数据包->客户端,请求关闭连接,然后服务端进入 LAST-ACK 状态。
- 第四次挥手:客户端发送 ACK (ACK=y+1)标志的数据包->服务端,然后客户端进入TIME-WAIT状态,服务端在收到 ACK (ACK=y+1)标志的数据包后进入 CLOSE 状态。此时如果客户端等待 2MSL 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。
举个例子:A 和 B 打电话,通话即将结束后。
- 第一次挥手:A 说“我没啥要说的了”
- 第二次挥手:B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话
- 第三次挥手:于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”
- 第四次挥手:A 回答“知道了”,这样通话才算结束。
6.介绍一下计算机网络常见的七层或者五层模型,各层的功能
OSI模型:国际标准化提出的网络分层模型
- 应用层:为计算机用户提供服务
- 表示层:数据处理(编解码、加密解密、压缩解压缩)
- 会话层:管理(建立、维护、重连)应用程序之间的会话
- 传输层:为两台主机进程之间传输数据
- 网络层:路由和寻址(决定数据在网络的游走路径)
- 数据链路层:帧编码和误差纠正控制
- 物理层:透明的传送比特流
TCP/IP四层模型:
应用层:为计算机用户提供服务【应用协议支持 如http、https】、用户接口和数据处理
传输层:端口寻址和复用(通过端口号来区分不同的应用程序),提供可靠或不可靠的传输服务
网络层:寻址和路由选择(通过IP地址来表示网络中的设备),路由器根据目的地IP地址,通过查找路由表中的最佳路径,将数据分组转发到下一个合适的节点;分组转发:将上层传来的数据分科成较小的数据包(IP数据包)并且在数据包头部添加源IP地址和目标IP地址
网络接口层:将二进制流转换位物理信号,并通过物理介质(如光纤、双绞线等)进行传输
五层模型
应用层
传输层
网络层
数据链路层
物理层
7.从浏览器输入一个网址具体做了什么
8.http1.0 1.1 2.0 3.0的改进?
1、HTTP 1.0
特点:
每个请求建立一个连接:每次客户端请求资源时,服务器都需要重新建立TCP连接。一个请求-响应完成后,连接就回关闭。这种方式会产生大量的开销。
缺乏持久连接:连接时短暂的,每次请求都会重新打开和关闭TCP连接,导致性能低下
2、HTTP 1.1
持久连接:默认情况下,连接多个请求之间保持打开状态,可以避免频繁建立和关闭连接
管道化:允许客户端在收到响应之前发送多个请求,从而减少延迟
更好的缓存控制:通过 Cache-Control
头部实现了更灵活的缓存策略,比 HTTP/1.0 更强大。
1.X 文本协议
3、HTTP 2.0
二进制传输:减少了解析开销和错误
多路复用:允许多个请求和响应在一个连接中并行发送,会造成TCP对头阻塞
HTTP/1.0 是最早的版本,每次请求都会重新建立一个连接,响应后立即关闭连接,这种机制导致了大量的连接开销和低效的资源请求。
HTTP/1.1 改进了持久连接(即在多个请求之间保持连接),引入了管道化、分块传输编码和更灵活的缓存机制。同时支持虚拟主机,通过 Host
头解决了多域名问题。但其多请求依然会因为头部阻塞(Head-of-Line Blocking)问题而受到限制。
HTTP/2.0 在性能上有了重大提升,采用了二进制传输,支持多路复用,允许多个请求和响应并行在同一连接上进行,减少了头部阻塞问题。它还引入了头部压缩和服务器推送功能,提高了带宽利用率。不过,HTTP/2 依然使用 TCP,仍存在 TCP 层的队头阻塞问题。
HTTP/3.0 是基于 QUIC 协议(使用 UDP)实现的,完全解决了 TCP 队头阻塞问题。QUIC 支持更快的握手,减少延迟,并且具备真正的多路复用。HTTP/3 默认加密传输,进一步提升了安全性和性能,尤其适用于高延迟或丢包的网络环境。
9.什么是RPC?用来做什么的?
RPC:远程过程调用:是一种计算机通信协议,允许一个程序调用另一个地址空间中的函数或过程,就像调用本地函数一样。通过 RPC,开发者可以让应用程序在分布式系统中通过网络轻松地进行跨进程通信,而无需关心底层网络通信的细节。
RPC 的主要概念:
- 客户端-服务器模型:RPC 通常采用客户端-服务器模式。客户端发起调用请求,服务器响应并执行指定的函数。
- 透明性:客户端调用远程服务时,过程看起来和调用本地函数一样,隐藏了网络通信的复杂性,提升了开发的便利性。
- 序列化和反序列化:RPC 框架负责将客户端的请求参数序列化为字节流,发送到服务器端,再将服务器返回的结果反序列化为客户端能够处理的数据
10.TCP和UDP区别
TCP和UDP都工作在传输层
1.UDP在传输数据前不需要建立连接,TCP提供的是面向连接的服务,因此在传输数据前需要建立连接,数据传输完成后需要释放连接
2.UDP是不可靠的,在远方主机收到UDP报文之后,不需要给出任何确认,不保证数据不丢失,不保证是否按顺序到达;TCP是可靠的传输,数据传输前,通过三次握手建立连接,在数据传递时有确认、重传、拥塞控制等机制,保证数据无差错、不丢失、按序到达
3.UDP是无状态的,在数据发送之后就不管之后的事情了;TCP是有状态的,在数据发送之后会确认对方是否接收到数据等
4.TCP传输效率相较于UDP 较低 ,因为在传输数据时多了连接、确认和重传机制
5.UDP是面向报文的,TCP是面向字节流的
7.UDP支持一对一、一对多、多对多通信、TCP只支持点对点通信
8.应用场景:
- UDP 一般用于即时通信,比如:语音、 视频、直播等等。这些场景对传输数据的准确性要求不是特别高,比如你看视频即使少个一两帧,实际给人的感觉区别也不大。
- TCP 用于对传输准确性要求特别高的场景,比如文件传输、发送和接收邮件、远程登录等等
11.http与UDP、TCP的关系
1.HTTP位于应用层,超文本传输协议,负责数据的格式、请求和响应
Tcp/UDP位于传输层:负责数据的传输和可靠性
2.HTTP 通常基于 TCP 进行传输。TCP 提供了可靠的、面向连接的通信,确保数据的顺序和完整性,这对大多数 HTTP 应用(如网页加载)是至关重要的。虽然 HTTP 通常不使用 UDP,但在某些场景下(如实时视频或音频传输)可以使用 UDP。这种情况下,数据传输速度快,但不保证数据的可靠性或顺序。
基于TCP的协议:HTTP/HTTPS、FTP、SMTP、SSH
基于UDP的协议:DNS
3.可以在扩展下TCP UDP的区别
数据结构
1.介绍栈、队列、和堆以及各自的优缺点
1. 栈是一种先进后出的数据结构,即最后入栈的元素最先出栈,栈通常用一维数组或链表实现,顺序栈、链式栈,有入栈和出栈两种操作
优点:简单且高效、易于实现
缺点:容易导致栈溢出,特别是递归深度过大时 只能在一端进行操作,限制了灵活性
应用:浏览器的回退和前进功能、检查符号是否成对实现、维护函数调用、深度优先遍历
2.队列:是一种先进先出的数据结构,例如食堂排队打饭的场景,最先入队的元素最先出对,可以通过数组和链表实现,有入队和出队等操作
优点:简单易用,适合处理按顺序到达的任务
缺点:只能在一端插入、另一端删除,限制了操作的灵活性,如果队列未进行大小限制,可能会造成内存浪费
3.堆:是一种特殊的数据结构,堆中的每一个节点值都大于等于(小于等于)子树中所有节点,可以分为大跟对和小跟堆,有插入和删除操作
优点:插入删除比较方便 例如:对于任意节点i ,左子节点2*i,右子节点 2*i+1 讲下从子节点插入的过程
缺点:不是一个完美的平衡树,可能导致较高的常数时间开销
栈和堆的区别
相同点:栈和堆都是一种数据结构
1.存储方式:
栈遵循先进后出的原则,并且栈是由系统自动分配和释放内存空间的,栈中的数据存储时连续的,有入栈、出栈等操作
堆存储动态分配的内存,堆内存是由程序员手动操作的,例如 新建一个对象时,就会在堆中分配一块内存,有插入和删除操作
2.数据访问速度:
由于栈内存是连续的,栈的数据访问速度较快
堆的访问速度比较慢,因为堆内存是连续的,需要拿到引用地址去找到堆中存放的内容,
3.应用:
栈:递归、图的深度优先遍历 、括号是否匹配、浏览器的回退和前进功能
堆:当我们只关心所有数据中的最大值或者最小值,存在多次获取最大值或者最小值,多次插入或删除数据时,就可以使用堆。
2.图有哪些存储结构
图是一种较为复杂的非线性结构,由顶点和顶点之间的边组成,通常表示为G(V,E)
相邻接矩阵存储:二维矩阵存储,如果第 i 个顶点和第 j 个顶点之间有关系,且关系权值为 n,则 A[i][j]=n
。 通俗来说:用两个数组表示:一维数组表示有那些顶点,二维数组表示哪些顶点式相连的,1表示相连,0表示不相连
优点:这种存储方式比较简单直接,获取两个顶点之间的关系也比较高效,但是比较浪费空间
邻接表存储:使用数组加链表的方式表示,对于一个节点后跟的链表,表示某个顶点后继相邻的顶点,比较节省空间。
图的遍历:广度有限搜索(队列)、深度优先搜索(栈)
3.哈夫曼树
哈夫曼树:也称最优二叉树,是一种带权路径长度最短的的二叉树
一些名词
带权最短路径:根节点到耨一个节点的路劲个长度乘以该节点的权值,该值就是戴荃李静长度,哈夫曼树就是带权路径长度最小的数
哈夫曼树构造过程:首先,将n个权值看做N个独立的二叉树,选权值最小的两颗二叉树,合并为一颗新的二叉树,根节点为这两颗子树的权值之和。一直重复上述步骤。
应用场景:最优编码、消息传输、数据压缩等方面
4.二叉树
完全二叉树:除了最底层的叶子结点没有填满外,其余每层节点数都达到了最大值,并且叶子结点中都是从左到右一次排列
满二叉树:满二叉树每一层的节点数都达到了最大值 节点个数2^depth - 1;
二叉搜索树:对于每个节点,左节点的值都小于根节点,右节点的值都大于根节点,不保证平衡
平衡二叉搜索树:在二叉搜索树的基础上,任意节点的左右子树高度不小于1【AVL树、红黑树】
完全平衡二叉树:完全平衡二叉树是一种严格平衡的二叉树,除了叶子节点之外的所有节点都有两个子节点,且所有的叶子节点都在同一层。
高度:根节点距离叶子结点的距离
深度:叶子结点距离根节点的距离
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
5.红黑树
自平衡二叉搜索树
特点:
- 根节点必须是黑色
- 每个节点要么是红色 要么是黑色
- 每个叶子结点都是黑色的
- 如果一个节点是红色,则他的两个子节点都是黑色,即红色节点不能连续
- 从任一节点到其叶子结点中的所有路径包含相同数目的黑色节点
这些性质保证了红黑树的高度近似平衡,保证查找、插入、和删除操作时间复杂度为O(logn)
核心操作:旋转和重新着色,保证了红黑树的平衡性
6.B树
自平衡多路查找树
MySQL
1.MySQL事务
事务是一组操作的集合,他是不可分割的单位,要么同时执行,要么同时不执行
例如银行转账:小红向小明转账100 小明账户减少100,小明账户增加100,这组操作要么同时成功,要么同时失败
作用:数据库事务可以保证多个对数据库的操作构成一个逻辑的整体
事务的四大特性:
- 原子性:事务是一个整体,不可分割,确保了这组操作要么全部成功,要么全部失败
- 一致性:执行事务前后,数据保持一致。例如:转账前后AB的总额保持不变
- 隔离性:当并发访问数据时,多个事务之间不会造成干扰,事务之间是独立的
- 永久性:一个事务提交后,对数据库中 的数据改变是持久的
2.给定一张学生表,查找成绩第二高的学生姓名,注意,可能会出现相同的成绩。
select name
from student
where score = (
select distinct score
from student
order by score desc
limit 1 offset 1
);
3.数据库中左连接和右连接的区别
连接适用于多表查询的条件
1.内连接:返回两个表中满足条件的行的组合,只有当了两个表中的条件都匹配时,才会返回结果
示例:假设有两个表,一个是 “学生表(students)”,包含学生的基本信息,如学生 ID(student_id)、姓名(name)等;另一个是 “成绩表(scores)”,包含学生的成绩信息,如学生 ID(student_id)、课程(course)、分数(score)等。如果我们想获取每个学生及其对应的成绩信息,就可以使用内连接。连接条件是两个表中的学生 ID 相等。
SELECT * FROM students INNER JOIN scores ON students.student_id = scores.student_id;
2.左外连接:返回左表中的所有行,以及右表中与左表连接匹配的行,如果右表中没有与左表连接匹配的行,那么左表集中右表对应的列将填充为NULL
例子:上述的学生表和成绩表 有学生存在没有成绩的情况,则左外连接返回NULL值
3.右外连接:返回右表中的所有行,以及左表中符合条件的行,如果左表中没有与右表某行匹配的记录,那么在结果集中左表对应的列将填充为NULL
。
例子:用成绩记录对对学生的信息,可以使用右外连接
4.慢查询怎么找原因?
1.慢查询日志 slow query Log ,可以记录超过指定时间的查询,这里需要启用慢查询日志:slow_query_log
为 ON
2.分析执行计划:使用explain 或 describe 命令分析SQL查询的执行计划,查看查询在数据库中是如何执行的
常见问题:全表扫描(意味着没有使用索引)、索引使用不当
3.检查索引:
缺少索引
冗余索引
正确选择索引
4.优化查询语句、索引设计、避免锁和并发瓶颈
5.表设计问题:数据冗余和表大小
6.调整数据库配置,使用缓存和表统计信息来提升性能
7.定期分析和优化表设计,选择合适的存储引擎
5.数据库优化有哪些思路?
- 查询优化,包括减少不必要的查询、优化 SQL 语句和分页查询;
- 索引优化,如建立合适的索引、组合索引、避免索引失效;
- 表结构优化,通过规范化、分库分表、分区和字段类型优化;
- 缓存优化,包括查询缓存、应用层缓存和数据库自带缓存;
- 锁和事务优化,合理使用事务和减少锁争用;
- 硬件和配置调优,包括数据库参数调整和硬件优化;
- 数据维护,包括定期分析表、数据归档和清理冗余数据。
各个点之间适当补充
6.索引失效有哪些场景?模糊查询都会失效吗
索引失效场景:
1.使用LIKE模糊查询:数据库无法通过索引快速定位匹配的行,只能进行全表扫描
2.对索引列进行函数操作:如果对索引列应用了函数或表达式,数据库将无法利用索引,因为索引中存储的是原始值。
3.数据类型不一致(隐式类型转换):查询条件的类和传入的值数据类型不匹配,随列进行隐式转换,导致索引失效,SELECT * FROM users WHERE phone_number = 12345;phone_number
是 VARCHAR
类型,而查询条件使用了数字,则会触发类型转换,导致索引失效。
3.对索引列进行运算操作,索引会失效
总结:
- 索引失效的场景 包括使用前导
%
的模糊查询、对索引列进行函数或运算操作、数据类型不匹配、组合索引未遵循最左前缀原则、使用不等运算符等。 - 模糊查询 并非都会导致索引失效。如果
%
在字符串的前面或中间,索引会失效;而如果%
只在后面,索引仍然有效。
7.mysql有哪几种索引类型
应用层次分:普通索引、唯一索引、复合索引
普通索引:一个索引只包含单个列,一个表可以有多个单列索引
唯一索引:索引列的值必须唯一,但允许有空值
复合索引:多列值组成一个索引,专门用于组合搜索,效率大于索引合并
聚簇索引:数据存储方式,InnoDB的聚簇索引就是B+树数据结构
非聚簇索引:
存储结构划分:BTree索引、Hash索引、全文索引
Hash索引:基于哈希表实现,只有精确匹配索引所有列的查询才有效,对于每一行数据,存储引擎都会为索引计算一盒哈希值,将哈希值存储在索引中,并且索引保存指向每一行数据的指针
Linux
1.Linux:查cpu负载linux
1.使用top命令,实时查看系统性能的命令,不仅显示CPU负载,还显示内存使用情况和系统上运行的进程,按下Q 可以退出
2.使用uptime命令
uptime
命令会简洁地显示系统的运行时间、当前登录用户数量以及系统的平均负载(load average)。CPU 负载信息在输出的最后一部分
uptime
uptime
14:06:12 up 10 days, 2:34, 1 user, load average: 0.25, 0.30, 0.35
最后三个数字分别是系统在过去 1 分钟、5 分钟和 15 分钟的平均负载
2.要已知端口,如何使用命令查服务
1.ss命令:用于查看当前的网络连接状态,特别适合查找某个端口上监听的服务。
ss -tuln | grep :<端口号>
ss -tuln | grep :80
-
-t:显示TCP连接
-
-u:显示UDP连接
-
-l:仅显示监听状态的连接
-
-n:以数字形式显示端口号,而不是以服务名称显示
2.netstat 命令 同SS命令
3.lsof 命令
3.linux 创建目录、查询进程、查看系统资源都使用哪些命令
1.创建目录:mkdir 目录名
2.查询进程:PS命令【用于查看当前系统中的进程】 或者 top
命令【实时显示系统运行情况】
根据进程名称查询进程ID:pgrep
返回指定程序的进程ID:pidof
终止进程:
kill
和 killall
命令
kill <PID>
:通过进程 ID (PID
) 终止进程。killall <进程名>
:通过进程名终止所有相关进程。
3.查看系统资源的命令
- 内存:
free
- 磁盘:
df
,du
- CPU:
lscpu
- 网络:
netstat
,ss
- 硬件:
lshw
- 负载:
uptime
- 综合资源:
vmstat
4.Linux修改权限
通常使用chmod命令
权限分为三种:owner group others all
权限表示法
权限可以使用两种表示法来定义:
- 符号表示法:使用字符来表示权限。
u g o a 用户、组、其他人、所有人
+ - = 添加权限、删除权限、设置权限
2.八进制表示法:使用数字表示权限
4 读R 2 写W 1 执行X ——》 可以将数字相加起来组合权限
chmod u+x file.txt # 给文件的用户添加执行权限
chmod g-w file.txt # 从文件的组中删除写权限
chmod o=r file.txt # 将文件的其他人权限设置为只读
chmod a+r file.txt # 给所有人添加读权限
chmod 755 file.txt # 用户有读、写、执行权限,组和其他人有读、执行权限
chmod 644 file.txt # 用户有读、写权限,组和其他人有读权限
chmod 700 file.txt # 只有用户有读、写、执行权限
查看当前权限:
ls -l 文件名
5.ls 查看文件后有一串数字代表什么含义?
ls -l
查看文件详细信息时,输出的信息一般包括以下几个部分:
文件类型和权限、链接数、所有者、所属组、文件大小(单位通常是字节)、最后修改日期和时间、文件或目录的名称
因此一串数字表示某一个文件的大小
drwxr-xr-x 2 user group 4096 Sep 30 14:32 directory_name
:这是一个名为 directory_name
的目录,拥有者有读、写、执行权限,组用户和其他用户只有读、执行权限。
drwxr-xr-x
表示:
d
:这是一个目录。rwx
:拥有者有读、写和执行权限。r-x
:组用户有读和执行权限,但没有写权限。r-x
:其他用户有读和执行权限,但没有写权限。
6.Linux如何判断一条命令正常退出?
在 Linux 中,使用 $?
可以方便地判断命令是否正常退出。通过检查退出状态码:
0
表示命令执行成功。- 非零的状态码表示命令失败或执行中遇到某种问题。
$ echo "hello world" | grep "hello"
hello world
$ echo $?
0
.bash
文件是一些 Bash 的配置文件,主要用于设置环境变量、别名、函数、启动命令等,帮助用户定制 Bash Shell 的工作方式
7.Linux查看日志文件
tail -500 application.log
这个命令将从 application.log
文件中显示最近的 500 行。如果文件少于 500 行,它会显示整个文件。
8.Linux中如何查看网络连接状态
1.使用netstat命令
netstat -ant
2.ss 命令
ss -ant
-a
:显示所有连接(包括正在监听的套接字)。-n
:以数字形式显示地址和端口。-t
:仅显示 TCP 连接。
3.ping 命令
ping
是用来检查主机是否可以访问以及网络延迟的常用工具。
9.Linux中在一个文件夹下查找所有以数字开头的文件
find /path/to/directory -type f -regex '.*/[0-9].*'
-regex '.*/[0-9].*'
:正则表达式,匹配以数字开头的文件名
find /path/to/directory -type f -name '*a*'
测开
1.AI机器人怎么测试?
1.功能测试:验证AI机器人是否能按照预期执行特定的任务和功能
- 输入与输出验证:确保在给定输入条件下,AI机器人能正确生成预期的输出,举例:
- 流程控制:测试机器人的逻辑流程,确保能够正确的执行任务步骤,例如:输入:“你好”,输出应该是“您好,有什么可以帮助您的吗?”
- 异常处理:确保机器人在遇到异常或无效输入时,能做出合理的响应,如提示输入无效或重新输入 以及提供帮助信息
2. 对话测试
对话式 AI 机器人的核心是人机交互,这种交互的自然性和智能性直接影响用户体验。对话测试的重点在于:
- 意图识别:验证机器人能否准确识别用户的意图。测试 AI 是否能在不同语境中理解相同的指令。例如,“天气如何?” 和 “今天的天气怎么样?” 应该产生相同的响应。
- 上下文管理:检查 AI 是否能在多轮对话中保持上下文。例如,如果用户先问“今天的天气怎么样?” 机器人回答后,用户接着问“那明天呢?” AI 应该能够理解问题的上下文,继续提供明天的天气信息。
- 多轮对话:验证机器人是否能进行连续的对话,而不是每次都重新开始。这个测试会涉及不同会话场景的设计和处理。
示例测试:
- 用户提问:“帮我订张明天去北京的机票”,机器人应该识别“订机票”的意图。
- 后续提问:“几点有航班?” 机器人应能理解这是针对前一个问题的延续。
3. 性能测试
性能测试确保 AI 机器人在高负载条件下仍然能保持正常的响应速度和服务质量。包括:
- 响应时间:测试机器人处理用户请求的速度,尤其是在高并发请求时,响应时间是否保持在合理范围内。
- 系统负载:在大量用户同时使用时,机器人是否还能保持稳定。
- 扩展性测试:验证系统在高负载下是否能有效扩展以应对增加的用户请求。
示例测试:
- 模拟 1000 个用户同时向机器人发送请求,检查响应速度是否明显下降。
- 在并发高峰时是否发生崩溃或出现错误。
4. 智能性测试
智能性测试用于验证 AI 机器人的学习和推理能力,特别是在 AI 使用了机器学习或自然语言处理技术时。主要涉及:
- 自然语言处理(NLP)能力:测试机器人是否能处理不同的语言结构、口音、拼写错误、同义词等。
- 机器学习模型验证:如果机器人依赖机器学习模型,测试应验证模型是否对新数据有良好的适应性,模型是否有过拟合或欠拟合的问题。
- 自适应性:测试机器人是否能根据交互数据持续改进其响应。例如,机器人是否能通过学习常见问题提高回答的准确度。
5. 安全性测试
AI 机器人涉及用户隐私和数据安全时,安全性测试是必不可少的,尤其在处理敏感信息时。测试内容包括:
- 数据保护:验证机器人是否正确处理和存储用户数据,确保隐私和机密信息不会泄露。
- 权限管理:检查机器人的访问权限,确保不同角色的用户只能访问适合其权限的数据。
- 防止滥用:确保机器人不会成为黑客攻击的目标,如通过 SQL 注入或脚本注入来破坏系统。
2.谈谈对性能测试的了解
性能测试是评估软件应用程序在不同负载和使用条件下的响应时间、稳定性和资源使用情况的过程,目标是确保应用在高负载下仍能正常运行,并满足用户的性能期望
1.性能测试分类
负载测试:模拟用户并发访问,检查系统在特定负载下的表现
压力测试:超出系统的最大负载,以评估系统的稳定性和恢复能力
基准测试:与已有系统或版本进行比较,确保性能的改进
容量测试:确保系统在不同负载下的表现
2.目标
确保系统的响应时间和处理能力
识别性能瓶颈,如CPU、内存或网络等的限制
验证系统高负载下的稳定性和可靠性
3.关注的指标:
响应时间:用户请求的处理时间
吞吐量:单位时间内处理的请求数量
并发用户数:系统能够同时支持的用户数量
资源利用率:CPU、内存、磁盘和网络的使用情况。
3.两个并行的电梯设计测试用例
4.测试开发中,自动化测试用例覆盖率是多少?如何优化
自动化用例覆盖率通常指的是通过自动化测试执行的代码或功能的比例
1.首先分析覆盖率
在美团实习期间,通过分析功能覆盖、代码行覆盖、条件覆盖等,然后拉回进行讨论以便进行针对性的讨论
2.优先考虑关键功能
将重点放在应用的核心功能上,确保这些部分有充分的测试覆盖
或者针对用户最常用的功能,编写全面的自动化测试用例
3.使用数据驱动测试方法,重用测试逻辑,提高覆盖率二部增加代码量
例如:在实习期间编写自动化用例时,使用TestNG测试框架,编写Json格式的测试用例,重复利用测试逻辑,提高工作效率
自动化用例执行失败的原因
1.环境问题:
可能因为测试环境未能正确配给或者资源不足而失败,例如,在实习期间,在流水线上配置自动化用例环境时,语言环境配置错误会引起测试用例执行失败,或者在配置机器信息时,机器的配置不符合要求和会导致盈利执行失败
2.测试用例设计问题
测试用例本身设计存咋缺陷,例如断言结果不正确,或者参数错误等错误都会引起用例执行失败
3.网络问题:
- 网络延迟:网络不稳定可能导致请求超时,从而影响测试结果
- 服务器相应问题:响应慢火返回错误响应码
6. 依赖项问题
-
库或框架版本不匹配:测试所依赖的库或框架版本变化,可能导致兼容性问题。
-
外部资源:对外部文件、数据库或配置文件的访问问题。
7. 配置问题
-
测试脚本配置错误:测试脚本中的配置文件、参数设置错误可能导致执行失败。
-
路径问题:文件或资源路径设置不正确,导致无法找到所需文件。
5.移动端和web端测试的区别
1.设备类型
移动端:通常指的是智能手机或平板电脑,这些设备具有触摸屏和便携性
Web端:通常指的是桌面或笔记本电脑,使用鼠标和键盘进行交互
2.操作系统
andriod ios等
Windows Linux Mac等
3.屏幕尺寸和分辨率
尺寸和分辨率多种多样,
4.性能和资源:
客户端:设备性能和资源有限,需要重点测试 例如带电量、或者各种硬件设施 应用能否很好的匹配
5.用户交互:
-
移动端:主要通过触摸屏幕进行交互,包括点击、滑动、缩放等手势。
-
Web端:通过鼠标和键盘进行交互,包括点击、拖拽、滚动等。
6.回归测试如何提升测试的效率?
回归测试:应用在经过修改后,为确保原有功能不受影响而进行的测试
1.测试用例优化
- 首先分析功能的变更范围,只选择与变更相关的功能模块进行测试,例如:在实习期间HOM平台的回归测试 只针对宿主机替换的功能重点测试,那么只需要选择功能变更部分的用例进行测试执行,对于长期稳定且未发生变更的模块,可以适当减少用例的执行频率
- 若回归测试的时间很紧,用例优先级划分:根据测试用例的重要性和风险程度进行优先级排序,;例如设计核心功能、用户使用频繁发功能优先级较高。低优先级的测试用例可以在资源允许的情况下后续执行
2.自动化测试
- 选用合适的自动化框架进行用例的编写,以便在后期应用的迭代过程中可以轻松添加新的测试用例以适应功能的变化
- 选用自动化测试框架还有一方面就是可以提高测试脚本的编写效率和测试效率,边缘后期测试人员的维护
3.持续集成与持续测试
- 使用持续集成工具,例如美团使用的DevTools工具,每次代码提交后,自动出发构建和回归测试,及时发现问题并返回给测试人员
- 配置合理的构建和测试流程,例如在建立上提到的搭建自动化测试流水线,包含源代码检出、静态代码扫描、Plus构建、cargo部署、自动化用例测试
- 最后实时监控测试结果,实时跟踪测试进度和结果,例如在定时流水线上跑自动化用例时,将流水线与大象同步,这样可以实时看到测试用例的执行结果,若执行失败,可以及时排查原因。
4.团队协作与沟通以及知识共享
7.postman接口测试中,如何授权?
第一种:基本认证
在需要测试的接口中设置 Authorization 标签
在type下拉菜单中选择 Basic Author ,然后输入Username和Password
发送请求,点击send postman会自动在请求头中添加授权编码的凭据
第二种:Bearer Token
Authorization标签 选择Bearer Token Token中输入的令牌
发送请求,点击send发送 postman会在请求头中添加 Authorization :token
第三种:API key
设置认证:
点击header标签
添加一个新的请求头,通常为 x-api-key或其他指定名称,在值字段中输入api key
发送请求:点击send,postman会将api key 作为请求头的一部分发送
第四种:Cookie认证
设置Cookie: 添加需要的Cookie名称和值
发送请求:点击send
【补充】:获取Cookie的几种方式:
1.开发者工具或者抓包工具例如fiddle
2.postman 发送URL 在点击Cookie字段
3.通过代码 例如python中request请求
先发送请求,在获取Cookie‘
request.get(url) respose.cooikes 最后在拿到Cookie的名 以及对应的值’
【令牌】令牌是一种用于身份验证和授权的数字字符串 主要作用是代表用户的身份或访问权限
1.身份验证:令牌在用户登录成功后身份验证由服务器生成,包含用户身份信息和相关权限
2.授权:令牌可以被用来授权访问特定资源
类型
1.访问令牌:用于访问受保护的资源,并包含用户的权限信息
2.刷新令牌:用户获取新的令牌,通常是长期有效的,确保用户在绘画国企后仍然可以保持登 录状态。
工作流程
- 用户登录后,身份验证服务器生成令牌并返回给客户端。
- 客户端在后续请求中将令牌附加到请求头中,以证明其身份。
- 资源服务器验证令牌的有效性,然后授予访问权限。
cookie 和 令牌的区别以及各自的用途
ChatGPT 说:
Cookie 和令牌(Token)都是用于身份验证和状态管理的技术,但它们在实现方式、用途和安全性等方面存在一些重要区别。以下是它们的对比:
1. 定义
Cookie:
Cookie 是由服务器设置并存储在客户端浏览器中的小数据块。它用于保存用户状态和偏好设置。
令牌:
令牌是一种数字字符串,用于代表用户的身份和访问权限,通常在身份验证后由服务器生成。
2. 存储位置
Cookie:
存储在用户的浏览器中,可以在同一域名下的多个请求中自动发送。
令牌:
通常存储在客户端(例如,内存、LocalStorage、SessionStorage)中,开发者需要在每次请求中手动附加到请求头中。
3. 传输方式
Cookie:
浏览器在每次请求时会自动附带 Cookie。
令牌:
客户端需要在请求中显式地将令牌包含在请求头(如 Authorization: Bearer <token>)中。
4. 用途
Cookie:
常用于保持用户登录状态、保存用户偏好设置、实现会话管理等。
支持 HttpOnly 和 Secure 标志,可以提供一定的安全性。
令牌:
主要用于 API 访问和身份验证,特别是在单页应用(SPA)和移动应用中。
允许跨域请求,通常采用 JWT(JSON Web Token)格式,便于在不同服务之间传递。
5. 有效性和续期
Cookie:
可以设置过期时间和持久性,支持自动续期。
令牌:
访问令牌通常是短期有效的,可能会有刷新令牌用于获取新的访问令牌。
6. 安全性
Cookie:
可能受到 CSRF(跨站请求伪造)攻击,需要通过 CSRF 令牌等方式防护。
令牌:
令牌通常使用 HTTPS 传输,并且可以采用签名和加密来增加安全性。使用时要注意令牌的泄露风险。
总结
Cookie 适用于传统的网页应用,用于会话管理和存储用户状态。
令牌 适合于现代应用(如 RESTful API 和单页应用),提供灵活的身份验证和访问控制机制。
8.测试中,如何判断是前端的 bug 还是后端的 bug 呢?
1.查看错误信息:
前端Bug:通常会在浏览器控制台中看到错误,使用浏览器开发者工具,可以看到是否有JavaScript报错、CSS 样式问题、资源加载失败等
后端Bug:如果返回的是4XX 或5XX的HTTP状态码,表示可能后端服务器请求失败了,例如404【资源未找到】、500【服务器内部错误】等
2.检查网络请求:
前端Bug:例如请求没有正确发出,可能是前端构造请求是出现了问题,例如参数可是不正确或参数错误
后端Bug:若请求发出后,后端返回了响应错误:500 或503 ,表名后端处理请求时出现了问题
3.观察用户界面表现:
前端Bug:页面布局不正确,按钮无响应、交互出现问题
后端Bug:如果数据加载不出来,或操作返回意外结果,可能是后端错误
9.jmeter测试性能时,如何用户授权?
1.手动从浏览器获取Cookie 添加到authorization字段
2.使用jmeter授权管理器,在测试计划中添加一个HTTP授权管理器,输入基础URL(不要带参数),用户名和密码。这样,JMeter会在后续的请求中自动包含这些授权信息。
3.HTTP Cookie管理器自动管理Cookies,可以像浏览器一样存储和发送Cookies,如果一个HTTP请求的响应中包含Cookie,JMeter会自动保存这些Cookie,并在所有后来发送到该站点的请求中使用这些Cookie【使用方法:在线程组下配置http Cookie管理器的元件】
或者也可以手动添加Cookie到Cookie管理器中
4.使用正则表达式提取器:配置提取器以匹配响应头中的Set-Cookie字段,并提取所需的Cookie值。
10.介绍下selenium
Selenium 是一个功能强大的开源自动化测试工具,专门用于网页应用程序的测试。Selenium的核心是WebDriver,它提供了一个API,允许我们模拟用户在浏览器中的所有操作,如点击、输入文本和页面导航。
Selenium与所有主流浏览器兼容,包括 Chrome、Firefox、Internet Explorer、Edge 和 Safari,这确保了测试的浏览器兼容性
11.selenium如何在不同浏览器版本上进行测试
下载浏览器驱动:根据你想要测试的浏览器,下载相应的WebDriver
配置WebDriver:在你的代码中,创建一个WebDriver实例,并指定浏览器驱动的路径
编写测试脚本然后测试执行
12.软件的生命周期
-
- 需求分析:这是生命周期的最初阶段,涉及收集和分析用户需求,明确项目目标,并定义软件所要解决的问题。
- 系统设计:在这个阶段,开发者设计软件的架构和组件。这包括数据结构、软件架构、接口设计以及详细的设计计划。
- 实现/编码:开发者根据设计文档编写代码,将设计转化为实际的软件产品。
- 测试:开发完成后,软件需要经过严格的测试来确保其质量。这包括单元测试、集成测试、系统测试和验收测试等。
- 部署:测试无误后,软件将部署到生产环境中供用户使用。
- 维护:软件部署后,仍然需要定期进行维护,包括修复缺陷、更新功能以及改善性能等
其他
1.常见的设计模式
1.策略模式:定义了一系列算法,将每个算法封装起来,使他们之间可以相互替换。可以让算法的变化独立于使用算法的客户
例如有多个if...else时,并且每个分支条件,可以单独封装起来,可以使用策略模式
示例:支付系统有不同的支付方式:微信、支付宝、银行卡等,可以通过特定的策略模式将每个支付方法封装在其中
优点:可以避免使用多重条件判断,提高算法的可扩展性
2.工厂模式:定义一个创建对象的接口,让子类自己决定实例化哪一个类
解耦、代码复用,创建的工作交给工厂类
简单工厂模式、工厂方法模式、抽象工厂模式
例如:生产宝马、奥迪、比亚迪等汽车 定义一个工厂接口,指定相关的规则,然后具体的工厂类去生产对应的汽车
3.责任链模式:一种处理请求的模式,为多个对象提供机会处理请求,将这些对象串联成一条链,每个对象知道如何处理请求,并将未处理的请求传递给链上的下一个对象。
优点:请求的发送者和接收者解耦,动态的处理请求链条,灵活性比较高
应用:有多个对象可以处理请求,但处理请求的具体对象不确定时。
例如:汇丰笔试题 中心系统处理客户关于GPU的请求:交付、软件、硬件、报修等请求,每个类有特定的处理方法。
4.模版方法模式:定义一个算法的骨架,将某些步骤延迟到子类去实现。可以使得子类在不改变算法结构的情况下重新定义算法的某些步骤
优点:代码复用性比较高,算法的主流程在父类中实现,子类可以根据需求调整部分逻辑
应用:一个算法需要再多个子类中实现,但逻辑的大部分是相同的
示例:开发游戏时,可以将游戏的初始化、开始、进行、结束等步骤定义为模版,而不能的游戏可以通过集成父类且实现一些自己方法实现
5.观察者模式:定义了一种一对多的依赖关系,使得对象的状态发生改变时,所有依赖于他的对象都得到通知并完成业务的更新
优点:支持广播通信,多个观察者之间可以同时接受到通知,观察者和被观察者之间的耦合性低。
应用:投递简历 成功后会收到短信和邮件通知 或者在社交媒体应用中,用户可以关注其他用户,当被关注者发布新动态时,所有关注者都会收到通知。
2.贪心算法
局部最优解推出全局最优解
一堆钞票 取十次,如何得到最多的钱? 每次取面额最大的钞票
3.Dcoker
镜像:特殊的文件系统,除了提供容器运行时所需要的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如:环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会改变 【分层存储】
容器:镜像运行时的实体。容器可以被创建、启动、停止、删除等。【容器存储层 保存在容器存储层的信息回随着容器的删除而消失】
容器不应该向其存储层内写入任何数据,所有文件写入操作,都应该使用数据卷、或者绑定宿主目录,使用数据卷后,容器可以随意删除、重新运行,数据不会丢失
仓库:集中存放镜像文件的地方——可以理解为代码仓库
Build Ship Run
- Build(构建镜像):镜像就像是集装箱包括文件以及运行环境等等资源。
- Ship(运输镜像):主机和仓库间运输,这里的仓库就像是超级码头一样。
- Run (运行镜像):运行的镜像就是一个容器,容器就是运行程序的地方。
Docker 运行过程也就是去仓库把镜像拉到本地,然后用一条命令把镜像运行起来变成容器。所以,我们也常常将 Docker 称为码头工人或码头装卸工,这和 Docker 的中文翻译搬运工人如出一辙。
Docker常见命令
docker version # 查看docker版本
docker images # 查看所有已下载镜像
docker ps #查看正在运行的容器
------------------------------
拉取镜像:
docker search mysql # 查看mysql相关镜像
docker pull mysql:5.7 # 拉取mysql镜像
docker image ls # 查看所有已下载镜像
构建镜像:
docker build -t imageName:1.0.0 .
删除镜像:删除镜像之前首先要确保这个镜像没有被容器引用(可以通过标签名称或者镜像 ID 删除)
Docker ps
doceker stop
docker rmi 镜像名
镜像推送:
docker push 将本地镜像推送到仓库
启动容器:
Docker Run
Docker Compose
负责实现对Docker容器集群的快速编排,可以使用yaml文件来配置应用的所有服务,然后只需要一个简单的命令即可创建和启动所有服务。
核心功能:
多容器管理:允许用户在一个yaml文件中定义和管理多个容器
服务编排:配置容器间的网络和依赖关系
一键部署:通过简单的命令,如docker-compose up
和docker-compose down
,可以轻松地启动和停止整个应用程序。
底层原理
4.Maven
1.什么是Maven
Maven是一个项目管理和构建的自动化工具,主要服务于Java项目,可以帮助开发人员方便的管理项目的构建、文档生成等
核心概念是基于项目的对象模型,每一个Maven项目都有一个pom.xml文件,包含项目的配置信息,例如项目的依赖库、构建的插件等,可以通过这个文件理解项目结构和管理所需要的任务
例如:项目需要用到TestNG测试框架,不需要手动下载,只需要在pom.xml文件中申明对TestNG的依赖,Maven会自动下载这个库并添加到项目类路径中,极大的简化了库管理和项目构建的过程。
2.Maven作用
依赖管理
项目构建的自动化:提供了一套默认的生命周期和一系列的构建阶段(如编译、测试、打包盒部署),只需要简单的命令就可以完成自动化构建
项目标准化:通过标准化的项目结构和构建生命周期,是的不同的项目能够以相同的方式进行构建和管理,降低学习成本,提高团队的协作效率
易于项目跨环境移植:是项目可以在不同的环境中构建和部署,提高了项目的可移植性
3.Maven依赖冲突解决
最短路径优先原则:Maven会选择距离项目根最近的依赖版本,如果两个版本距离相同,会选择在pom.xml中最先申明的那个版本
4.Maven坐标
坐标是用来唯一标识Maven项目中的依赖项的一组参数,在Maven中,每一个库、构建=都被存储在Maven仓库中,通过坐标来检索
本地仓库、中央仓库(社区维护的一个仓库)、远程仓库【企业内部的私有仓库或者其他第三方公开的远程仓库】
groupId:项目或组织的唯一命名空间 公司域名反向构成 例如com.samkuai
artifactId:唯一标识一个项目或模块、通常是项目的名称
version:版本号
除了这三个基本坐标外,还有两个可选坐标可以提供更多信息:
packaging:定义了构件的打包方式,如jar、war、ear等,默认值是jar。
classifier:用于区分具有相同groupId、artifactId、version的不同构件,例如,用来区分同一个库的源码包和二进制包
5.Maven生命周期
clean生命周期:项目清理
default生命周期:项目的部署
site生命周期:项目站点文档=的生成 包括:生成项目网站的各种报告、文档
6.Maven多模块管理:
将一个项目分为多个模块,每个模块只负责单一的功能实现,一个Maven项目中不止有一个pom.xml文件,会在不同的目录下有多个pom.xml文件,实现多模块管理
好处:。。。。。。
5.git
1.git 本地文件如何上传到代码仓库
添加文件到暂存区:git add .
提交更改: git commit -m "提交理由"
推送到远程仓库: git push origin master
2.如何解决冲突
在合并分支时可能会出现冲突
git pull 拉取最新的远程代码也可能会冲突、
git status 查看冲突的文件
IDEA中打开冲突解决工具:左侧本地代码、右侧远程代码、中间合并后的结构、
手动解决冲突后
git add .
git commit
3.git其他命令
# 查看当前分支 展示所有分支,当前分支下面有*号
git branch
# 创建分支
git branch 分支名
# 切换到目标分支
git checkout 分支名
# 添加修改到暂存区
git add . 或者 git add 文件名
# 提交修改
git commit
# 本地文件推送到远程
git push 远程仓库名 分支名
# 检查提交状态 如果正常,会提示没有更改的文件, 若有冲突,会展示冲突的文件
git status
# 忽略文件
gitignore
# 从远程仓库获取最新的代码并合并到当前分支
git pull origin 分支名
git pull origin master 这会从远程 origin 仓库的 master 分支拉取最新代码,并与本地的当前分支合并
6.回溯和递归的区别
递归是指一个函数在其定义中直接或间接调用自身,解决问题的方法,通过将问题分解成较小的子问题,直到这些子问题被解决
典型特征:基准条件(终止条件)、递归调用
应用场景:二叉树遍历、归并排序、快速排序、阶乘等
回溯是一种通过试探和撤回来解决问题的算法,特殊形式的递归,回溯通过不断构造解的各个部分,尝试所有可能的选择,当前选择不能构成正确选择时,会回撤到上一步并尝试其他可能的选择,通常用来解决组合和排列问题、N皇后问题
本质区别:
递归是一种编程技巧或思维方式,通过函数自调用解决问题,不一定探索所有可能的路径,通常针对分解为相似子问题解决问题
回溯 是一种算法,用于在探索解空间时“试探”并“回撤”来寻找合适的解。它不仅递归调用自身,还涉及到“回退”操作(即回溯),在回溯时撤销之前的选择以探索其他路径。
7.排序算法
冒泡排序:每次比较相邻的两个元素,顺序不对就交换
选择排序:每次从未排序的部分找到最小会最大的元素,放到已经排序的末尾
插入排序:将序列分为已排序和未排序两部分,每次从未排序部分选出一个元素插入到已排序部分的正确位置。
归并排序:将序列不断分成两半,分别对每一半进行排序,最后将两部分进行合并,采用分治算法思想
快速排序:选择一个枢纽,通常是数组的中间值,通过一趟排序将序列分为两部分,使得一部分比枢纽小,一部分比枢纽大,递归的对两部分进行排序