java重要面试题集锦-date1

1、接口和抽象类的区别是什么?
Java 提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:
接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。 类可以实现很多个接口,但是只能继承一个抽象类 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声 明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。 抽象类可以在不提供接口方法实现的情况下实现接口。
Java 接口中声明的变量默认都是 final 的。抽象类可以包含非 final 的变量。
Java 接口中的成员函数默认是 public 的。抽象类的成员函数可以是 private,protected 或者 是 public。 接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含 main 方法的话是可以被调用的。
2、什么是值传递和引用传递?
对象被值传递,意味着传递了对象的一个副本。因此,就算是改变 了对象副本,也不会影响
源对象的值。
对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用 。因此,外部对引用对 象所做的改变会反映到所有的对象上。
3、概括的解释下线程的几种可用状态。
线程在执行过程中,可以处于下面几种状态:
就绪(Runnable):线程准备运行,不一定立马就能开始执行。 运行中(Running):进程正在执行线程的代码。 等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。 睡眠中(Sleeping):线程被强制睡眠。
I/O 阻塞(Blocked on I/O):等待 I/O 操作完成。 同步阻塞(Blocked on Synchronization):等待获取锁。 死亡(Dead):线程完成了执行。
4、如何确保 N 个线程可以访问 N 个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程 按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出 现死锁了。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ResourceAllocator {
    private static final int N = 3; // 资源数量
    private static final Lock[] locks = new ReentrantLock[N];

    static {
        for (int i = 0; i < N; i++) {
            locks[i] = new ReentrantLock();
        }
    }

    public static void acquireResources(int... indices) {
        // 按照顺序获取锁
        for (int index : indices) {
            locks[index].lock();
        }
    }

    public static void releaseResources(int... indices) {
        // 按照相反顺序释放锁
        for (int i = indices.length - 1; i >= 0; i--) {
            int index = indices[i];
            locks[index].unlock();
        }
    }

    // 其他业务逻辑...
}

在这个示例中:

我们定义了N个资源,每个资源都有一个独占锁。
线程在获取资源时,必须按照一定的顺序获取锁。例如,如果一个线程需要获取资源0和资源2,它必须先获取锁0,再获取锁2。
线程在释放资源时,需要按照相反的顺序释放锁。例如,如果一个线程获取了锁0和锁2,它必须先释放锁2,再释放锁0。
通过这种方式,我们可以确保资源分配图中不会形成环路,从而避免了死锁的发生。
需要注意的是,这种方法只适用于静态资源
5、处理hash冲突三种方式:开放地址法(线性探测方式)、二次探测法(Quadratic Probing)、双哈希法(Double Hashing)
线性探测法(Linear Probing)
线性探测法是开放地址法中最简单的一种方式。当发生冲突时,它会检查哈希表中当前位置的下一个位置是否为空,如果不为空,它会继续检查再下一个位置,直到找到一个空位置为止。如果遍历完整个哈希表仍然找不到空位置,表示哈希表已满,需要扩容。
线性探测法的优点是简单易实现,缺点是如果出现聚集(多个键值对被映射到同一片区域),会产生较多的冲突,影响性能。
二次探测法(Quadratic Probing)
二次探测法的做法是:当发生冲突时,它会按照一个固定的二次方程去探测下一个位置。例如,对于当前位置i,下一个探测位置是i+12,再下一个位置是i+22,依此类推,直到找到一个空位置为止。
二次探测法的优点是可以避免出现"聚集"的情况,因为它探测的步长会随着探测次数的增多而增大,所以每个位置都有 相同的概率被探测到。缺点是步长增长较慢,如果哈希表较满的话,探测次数可能较多。
双哈希法(Double Hashing)
双哈希法会使用两个不同的哈希函数。第一个哈希函数用于计算初始位置,第二个哈希函数用于确定探测序列。当发生冲突时,它会按照第二个哈希函数的值作为步长进行探测。
具体实现方式是:初始位置=hash1(key),步长=hash2(key)。如果当前位置i已被占用,就检查i+hash2(key)位置,直到找到空位置为止。
双哈希法的优点是计算探测序列的分布更加均匀,能较好地避免"聚集"现象。缺点是需要两个哈希函数,实现相对复杂一些。
上述三种探测方式各有优缺点,实际应用中需要根据具体情况选择合适的方式。线性探测法简单但易产生聚集,二次探测法分布更均匀但步长增长慢,双哈希法计算开销较大但分布最均匀。
6、HashSet 和 TreeSet 有什么区别?
HashSet 是由一个 hash 表来实现的,因此,它的元素是无序的。add(),remove(),contains()
方法的时间复杂度是 O(1)。
另一方面,TreeSet 是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),
remove(),contains()方法的时间复杂度是 O(logn)。
7、finalize()方法
finalize()方法是Java中的一个实例方法,它属于Object类。finalize()方法的作用是在对象被垃圾回收器回收之前,允许对象执行某些清理操作。
具体来说,finalize()方法具有以下特点:
对象生命周期 当一个对象没有任何引用指向它时,它就成为垃圾,垃圾回收器会在确定没有引用后的某个时刻对它所占用的内存空间进行回收。在这个回收过程的某个时候,垃圾回收器会对这个对象调用它的finalize()方法。
清理操作 finalize()方法允许对象在被回收前执行一些清理工作,比如关闭打开的文件资源、Socket连接等。这样可以避免一些资源无法正常释放的问题。
只会被调用一次 每个对象的finalize()方法只会被JVM调用一次。如果对象在finalize()方法中被重新分配引用,那么对象就会"复活",被重新使用。
不确定性 垃圾回收器何时调用finalize()方法是不确定的,这取决于垃圾回收算法的实现。因此,不能依赖于finalize()方法来执行重要的操作或者释放资源。
低效率 由于finalize()方法的调用是不确定的,而且会对垃圾回收器的效率产生负面影响,因此在Java 9之后,finalize()方法已被标记为过时(deprecated)。
总的来说,finalize()方法的目的是为了在对象被回收前进行一些清理工作,但它存在不确定性和低效率的问题。因此,在实际编程中,最好不要过度依赖finalize()方法,而是应该在对象的生命周期中合理地管理和释放资源。例如使用try-with-resources语句等方式。
8、catch和finally执行顺序
如果try块中没有发生异常:
执行try块中的代码
执行finally块中的代码(无论try块中是否有return语句)
程序继续执行
如果try块中发生了异常,并且该异常与catch块匹配:
执行对应的catch块中的代码
执行finally块中的代码
程序终止或者继续执行,取决于是否在catch块中有显式的控制流语句(如return、throw等)
如果try块中发生了异常,但没有与之匹配的catch块:
执行finally块中的代码
抛出这个异常,由外层的异常处理机制来处理
9、finally中抛出异常
finally块中没有出现异常
如果try块中没有出现异常,则正常执行finally块中的代码。
如果try块中出现异常,先执行finally块中的代码,然后再抛出异常。
finally块中出现异常
如果try块中没有出现异常,但finally块中出现异常,则会抛出finally块中的异常。
如果try块中出现异常,并且finally块中也出现异常,则会抛出finally块中的异常,try块中的异常会被抛弃。
10、java 四种修饰符范围
private 私有的,被private修饰的成员只能在同一个类内部被访问,其他类都无法访问。这是访问范围最小的一种修饰符。
default修饰的成员只能在同一个包内被访问,不能被其他包中的代码访问。
protected修饰的成员不仅可以在同一个包内被访问,还可以被其他包中的子类访问。
public 公开的,被public修饰的成员可以在任何地方被访问,这是访问范围最大的一种修饰符。
11、session 与 cookie 区别
存储位置
Cookie 存储在客户端(浏览器)
Session 存储在服务器端
存储形式
Cookie 以明文的 key-value 形式存储在浏览器中
Session 通常以对象或散列的形式存储在服务器内存或者服务器端文件中
存储大小
单个 Cookie 最多只能存储 4KB 的数据
理论上 Session 可以存储更多的数据,取决于服务器端的内存或存储空间
生命周期
Cookie 可以设置一个过期时间,过期后浏览器会自动删除
Session 通常会设置一个过期时间,但需要服务器端定期清理过期的 Session
安全性
Cookie 存储在客户端,容易被窃取或篡改
Session 存储在服务器端,相对更加安全
跨域访问
Cookie 可以在同一个域下自动发送到服务器端
Session 依赖于 Cookie 传递 Session ID,不能直接跨域访问
状态管理
Cookie 一般用于存储较小且不太敏感的数据
Session 一般用于存储敏感数据和较大的数据,如用户登录状态
12、equals 与 == 的区别
对于基本数据类型,==和equals没有区别。
对于引用类型,比较的是两个对象的内存地址是否相同,而equals比较的是两个对象的内容是否相同(需要重写equals方法)。
一般情况下,对于自定义的类,我们需要重写equals方法,以便能够正确地比较对象的内容。而
运算符用于比较两个对象是否是同一个实例,或比较基本数据类型的值。
重写equals方法时,通常还需要重写hashCode方法,以保证equals和hashCode的一致性。如果两个对象equals返回true,那么它们的hashCode值也应该相同。这一点对于使用哈希表等数据结构很重要。
13、线程生命
新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态
14、观锁的业务场景及实现方式
乐观锁(Optimistic Lock): 每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁, 但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不 进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁, 期间该数据可以被其他线程进行读写操作。
乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可
能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量
的查询操作,降低了系统的吞吐量。
15、redis支持数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zset(sorted set:有序集合)。
16、redis应用场景
随着数据量的增长,MySQL 已经满足不了大型互联网类应用的需求。因此,Redis 基于内 存存储数据,可以极大的提高查询性能,对产品在架构上很好的补充。例如,为了提高服务 端接口的访问速度,尽可能将读频率高的热点数据存放在 Redis 中。这个是非常典型的以 空间换时间的策略,使用更多的内存换取 CPU 资源,通过增加系统的内存消耗,来加快程 序的运行速度。
在某些场景下,可以充分的利用 Redis 的特性,大大提高效率。这些场景包括缓存,会话 缓存,时效性,访问频率,计数器,社交列表,记录用户判定信息,交集、并集和差集,热 门列表与排行榜,最新动态等。
使用 Redis 做缓存的时候,需要考虑数据不一致与脏读、缓存更新机制、缓存可用性、缓 存服务降级、缓存穿透、缓存预热等缓存使用问题。
17、Redis 为什么是单线程的
单纯的网络IO来说,量大到一定程度之后,多线程的确有优势——但并不是单纯的多线程, 而是每个线程自己有自己的epoll这样的模型,也就是多线程和multiplexing混合。
一般这个开头我们都会跟一个“但是”。 但是。
还要考虑Redis操作的对象。它操作的对象是内存中的数据结构。如果在多线程中操作,那 就需要为这些对象加锁。最终来说,多线程性能有提高,但是每个线程的效率严重下降了。 而且程序的逻辑严重复杂化。要知道Redis的数据结构并不全是简单的Key­Value,还有列表,hash,map等等复杂的结 构,这些结构有可能会进行很细粒度的操作,比如在很长的列表后面添加一个元素,在 hash当中添加或者删除一个对象,等等。这些操作还可以合成MULTI/EXEC的组。这样一 个操作中可能就需要加非常多的锁,导致的结果是同步开销大大增加。这还带来一个恶果就 是吞吐量虽然增大,但是响应延迟可能会增加。 Redis在权衡之后的选择是用单线程,突出自己功能的灵活性。在单线程基础上任何原子操 作都可以几乎无代价地实现,多么复杂的数据结构都可以轻松运用,甚至可以使用Lua脚本 这样的功能。对于多线程来说这需要高得多的代价。
并不是所有的KV数据库或者内存数据库都应该用单线程,比如ZooKeeper就是多线程的, 最终还是看作者自己的意愿和取舍。单线程的威力实际上非常强大,每核心效率也非常高, 在今天的虚拟化环境当中可以充分利用云化环境来提高资源利用率。多线程自然是可以比单 线程有更高的性能上限,但是在今天的计算环境中,即使是单机多线程的上限也往往不能满 足需要了,需要进一步摸索的是多服务器集群化的方案,这些方案中多线程的技术照样是用 不上的,所以单线程、多进程的集群不失为一个时髦的解决方案。
18、redis机器为4核,redis应用有几个线程
Redis是一个单线程的程序,它使用了非阻塞 I/O 模型和事件驱动架构,所以在一个Redis实例中只有一个线程在处理命令请求。
不过,为了更好地利用多核CPU的优势,Redis在4.0版本后引入了多线程模型,但仅限于以下几个特殊场景:

  1. 文件事件处理器(File Event Handler)
    用于在AOF重写或者RDB持久化时,执行文件I/O操作的线程。
  2. 来自其他核的客户端读取请求(Client Redis Reader)
    用于读取客户端请求并将其放入请求队列的线程。
  3. 集群发送器(Cluster Mail Sender)
    用于处理集群方面的一些异步工作的线程。
  4. rehashing异步任务(Rehashing Job)
    用于处理rehashing异步任务的线程。
  5. AOF重写异步任务(AOF Rewrite Job)
    用于处理AOF重写异步任务的线程。
    所以,在4核CPU的机器上运行Redis时,实际上还是会有多个线程,其中主线程负责处理命令请求,另外可能会有1-4个线程用于处理上述特殊场景。Redis通过这种方式来最大化利用多核CPU的优势,提高I/O处理效率。
    不过,主线程依然是Redis处理请求的关键,没有其他线程可以直接处理命令请求。主线程的运行性能仍然是Redis性能的瓶颈。因此,即使在多核CPU上,Redis的并发处理能力也是有限的。如果单机Redis性能已经无法满足需求,可以考虑使用Redis集群等方式来进一步扩展性能。
    19、redis主线程执行会换cpu吗?
    Redis主线程在执行过程中,确实会发生CPU切换,不会永远只运行在单个CPU核心上。
    原因如下:
  6. 操作系统的CPU调度机制
    现代操作系统都采用了时间片轮转的调度算法,会周期性地切换不同线程运行的CPU核心,以实现线程公平调度,避免一个线程长期占用CPU。因此,即使Redis只有一个线程,操作系统也会定期将其从当前CPU核心切换到其他空闲核心上运行。
  7. NPTL线程库
    Redis使用的是NPTL(Native Posix Thread Library)线程库,该线程库支持内核级线程,可以充分利用多核CPU。NPTL会将线程均匀分配到不同的CPU核心上运行。
  8. Redis自身的调度机制
    虽然Redis主线程是单线程执行命令,但它的事件循环中包含了处理各种事件(如文件事件、时间事件等)的代码逻辑。在处理不同事件时,线程的CPU亲和性可能会发生变化,从而导致线程在多个CPU核心上交替运行。
    所以,即使是单线程的Redis,在多核CPU环境下,它的主线程也不会一直运行在同一个CPU核心上,而是会在多个CPU核心之间进行切换和迁移。
    不过,需要注意的是,频繁的CPU切换也会带来一定的上下文切换开销。因此,现代操作系统会尽量将同一个线程保留在同一个CPU核心上运行一段时间,以减少不必要的切换开销。只有在线程数多于CPU核心数的情况下,才会频繁发生线程迁移。
    所以,在多核CPU环境下运行Redis时,它的主线程也会在多个CPU核心上交替运行,以充分利用多核CPU的并行计算能力。但操作系统会尽量减少主线程的CPU切换次数,以获得更好的性能表现。
    19、BeanFactory 和 ApplicationContext 有什么区别
    beanfactory顾名思义,它的核心概念就是bean工厂,用作于bean生命周期的管理,而
    applicationcontext这个概念就比较丰富了,单看名字(应用上下文)就能看出它包含的范围
    更广,它继承自bean factory但不仅仅是继承自这一个接口,还有继承了其他的接口(资源、事件发布),所以它不仅仅有bean factory相关概念,更是一个应用系统的上下文,其设计初衷应该是一个包 罗万象的对外暴露的一个综合的API。
    20、ApplicationContext继承类详情
    ApplicationContext 接口是 Spring 框架中的一个核心接口,它继承了以下几个接口:
    EnvironmentCapable 这个接口定义了获取运行时环境对象 Environment 的方法。通过 Environment 可以获取应用程序的环境配置信息。
    ListableBeanFactory 该接口扩展了 BeanFactory 接口,定义了访问 Spring 容器中 Bean 集合的方法。它提供了获取容器中所有 Bean 实例、Bean 名称以及指定类型的 Bean 实例的方法。
    HierarchicalBeanFactory 该接口扩展了 BeanFactory 接口,为 Bean 工厂实现了双亲层次关系。通过getParentBeanFactory() 方法可以获取父级 Bean 工厂。
    MessageSource 该接口定义了国际化功能方法,用于获取本地化消息。
    ApplicationEventPublisher 该接口用于发布应用程序事件,这是 Spring 的事件机制的基础。
    ResourcePatternResolver 该接口定义了加载根目录下的资源文件方法。
    21、JDK动态代理和CGLIB字节码生成的区别?
    JDK动态代理只能对实现了接口的类生成代理,而不能针对类
    (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 因为是继承,所以该类或方法最好不要声明成final
    22、Spring MVC 运行流程
    一、先用文字描述
    1.用户发送请求到DispatchServlet 2.DispatchServlet根据请求路径查询具体的Handler 3.HandlerMapping返回一个HandlerExcutionChain给DispatchServlet HandlerExcutionChain:Handler和Interceptor集合 4.DispatchServlet调用HandlerAdapter适配器 5.HandlerAdapter调用具体的Handler处理业务 6.Handler处理结束返回一个具体的ModelAndView给适配器 ModelAndView:model–>数据模型,view–>视图名称 7.适配器将ModelAndView给DispatchServlet 8.DispatchServlet把视图名称给ViewResolver视图解析器 9.ViewResolver返回一个具体的视图给DispatchServlet
    10.渲染视图
    11.展示给用户
    在这里插入图片描述
    23、Spring 框架中用到了哪些设计模式
    Spring 框架作为一个优秀的企业级应用程序框架,在其内部实现中广泛地使用了面向对象的设计原则和设计模式。下面是 Spring 框架中使用到的一些主要设计模式:
    工厂模式 Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
    单例模式 在 Spring 中,默认情况下,每个 bean 都是单例的。可以通过作用域控制实现不同的模式。
    代理模式 Spring AOP 功能的实现中使用了代理模式,通过代理来增强被代理对象的功能。
    观察者模式 Spring 中的事件驱动模型通过实现 ApplicationListener 接口来实现观察者模式。
    模板方法模式 Spring 中的 JdbcTemplate、HibernateTemplate 等以 Template 结尾的许多类都使用了模板方法模式。
    适配器模式 Spring AOP 开箱即用地使用了适配器模式,用来将 AdviceInterceptor 转换成 Advice。
    装饰模式 Spring 中用了大量装饰器模式来为对象动态地增加行为。
    策略模式 Spring 中定义了不同的资源加载策略,用于加载不同种类的资源。
    外观模式 Spring 中的 ApplicationContext 就是为了简化程序直接调用底层服务的复杂性而引入的一个外观模式实现。
    组合模式 Spring 中的 BeanDefinition 以及其子节点对象就是使用了组合模式。
    24、谈谈业务中使用分布式的场景
    一、解决java集群的session共享的解决方案: 1.客户端cookie加密。(一般用于内网中企业级的系统中,要求用户浏览器端的cookie不能 禁用,禁用的话,该方案会失效)。 2.集群中,各个应用服务器提供了session复制的功能,tomcat和jboss都实现了这样的功 能。特点:性能随着服务器增加急剧下降,容易引起广播风暴;session数据需要序列化, 影响性能。 3.session的持久化,使用数据库来保存session。就算服务器宕机也没事儿,数据库中的 session照样存在。特点:每次请求session都要读写数据库,会带来性能开销。使用内存数 据库,会提高性能,但是宕机会丢失数据(像支付宝的宕机,有同城灾备、异地灾备)。 4.使用共享存储来保存session。和数据库类似,就算宕机了也没有事儿。其实就是专门搞 一台服务器,全部对session落地。特点:频繁的进行序列化和反序列化会影响性能。 5.使用memcached来保存session。本质上是内存数据库的解决方案。特点:存入 memcached的数据需要序列化,效率极低。
    25、分布式锁的场景
    1、比如交易系统的金额修改,同一时间只能又一个人操作,比如秒杀场景,同一时间只能一个
    用户抢到,比如火车站抢票等等
    2、分布式任务调度 在分布式环境中,同一个任务可能会在多个服务节点上同时执行,这种重复执行会造成资源浪费。使用分布式锁可以给定时任务加锁,只允许在获取锁的服务节点上执行该任务,避免重复执行。
    3、减库存 在电商系统中,下单减库存是一个典型的场景。多个并发请求同时对一个商品库存进行操作时,如果没有分布式锁控制,很容易就会发生超卖的问题。分布式锁可以使并发线程串行执行对库存的操作,避免出现超卖。
    4、分布式全局序列号生成器 在分布式环境中需要生成全局唯一序列号时,如订单号、流水号等,需要通过获取分布式锁保证全局序号的严格有序性和唯一性。
    5、分布式限流 对于一些对系统压力特别大的操作,可以通过分布式锁限制并发数,防止系统被冲垮。
    6、分布式缓存操作 缓存是大多数系统的性能瓶颈,如果缓存失效,并发请求直接操作数据库,会造成数据库压力陡增。可以使用分布式锁,只允许一个请求更新缓存,其他线程等待缓存更新后直接读取,避免缓存击穿。
    7、分布式系统中的单点数据更新 在分布式环境中,一个数据可能会被多个服务节点共享。如果这个数据需要修改,未加锁可能导致数据不一致。使用分布式锁可以控制同一时间只有一个节点可以修改数据。
    8、领导选举 在有些场景下,需要选举出一个主节点作为leader。这个选举过程可以基于分布式锁实现,确保同一时间只有一个节点获得锁,成为领导者。
    26、常见防范常见的 Web 攻击
    1、XSS攻击 跨站脚本攻击;
    是什么:攻击者向有XSS漏洞的网站中输入恶意的HTML代码,当其浏览器浏览该网站 时,这段HTML代码会自­ ­ 动执行。(理论上所有可以输入的地方没有对输入的数据进 行处理,都会存在XSS攻击);
    危害: 盗取用户cookie,破坏页面结构,重定向到其他网站;
    防御:对用户输入的信息进行处理,只允许合法的值;
    2、CSRF攻击
    跨站请求伪造
    是什么:攻击者盗用了你的身份,以你的名义发送恶意请求;
    危害:以你的名义发送邮件,盗取帐号,购买东西等;
    原理: 首先个登录某网站,并在本地生成cookie;然后在不登出的情况下,访问危害网 站。
    防御: 可以从服务端和客户端两方面进行考虑。但是在服务端的效果好。 a. 随机的cookie
    b. 添加验证码
    c. 不同的表单包含一个不同的伪随机值
    注意:如果用户在一个站点上同时打开了两个不同的表单。CSRF保护措施不应该影响 到他对任何表单的提交
    3、SQL注入 是什么:通过sql命令伪装成正常的http请求参数,传递到服务端,服务器执行sql命令
    造成对数据库进行攻击
    原理:sql语句伪造参数,然后在对参数机型拼接后形成破坏性的sql语句,最后导致数 据库收到攻击
    防御:
    a. 对参数进行转义
    b. 数据库中的密码不应明文存储,可以对密码使用md5进行加密。
    4、DDOS攻击(分布式拒绝服务攻击) 是什么:简单来说就是ifasong大量的请求使服务器瘫痪。 被攻击的原因:服务器带宽不足,不能挡住攻击者的攻击流量。 防御:
    a. 最直接的方法就是增加带宽;
    b. 使用硬件防火墙;
    c. 优化资源使用提高 web server 的负载能力
    27、基于角色的访问控制和基于数据的访问控制关系
    基于角色的访问控制,只验证访问数据的角色,但是没有对角色内的用户做细分。举个例
    子,用户甲与用户乙都具有用一个角色,但是如果只建立基于角色的访问控制,那么用户甲 可以对用户乙的数据进行任意操作,从而发生了越权访问。因此,在业务场景中仅仅使用基 于角色的访问控制是不够的,还需要引入基于数据的访问控制。如果将基于角色的访问控制 视为一种垂直权限控制,那么,基于数据的访问控制就是一种水平权限控制。在业务场景 中,往往对基于数据的访问控制不够重视,举个例子,评论功能是一个非常常见的功能,用 户可以在客户端发起评论,回复评论,查看评论,删除评论等操作。一般情况下,只有本人 才可以删除自己的评论,如果此时,业务层面没有建立数据的访问控制,那么用户甲可以试 图绕过客户端,通过调用服务端RESTful API 接口,猜测评论 ID 并修改评论 ID 就可以删除 别人的评论。事实上,这是非常严重的越权操作。除此之外,用户之间往往也存在一些私有 的数据,而这些私有的数据在正常情况下,只有用户自己才能访问。
    基于数据的访问控制,需要业务层面去处理,但是这个也是最为经常遗落的安全点,需要引 起重视。这里,再次使用删除评论的案例,通过 Java 语言进行介绍。在这个案例中,核心 的代码片段在于,判断当前用户是否是评论的创建者,如果是则通过,不是则报出没有权限 的错误码。那么,这样就可以很好地防止数据的越权操作。
    总结下,基于角色的访问控制是一种垂直权限控制,通过建立用户与角色的对应关系,使得
    不同角色之间具有高低之分。用户根据拥有的角色进行操作与资源访问。基于数据的访问控
    制是一种水平权限控制,它对角色内的用户做细分,确保用户的数据不能越权操作。基于数
    据的访问控制,需要业务层面去处理,但是这个也是最为经常遗落的安全点,需要引起重
    视。
    28、在SQL语句中,#{} 和 ${}都是 MyBatis 中用于替换参数值的占位符,但它们有一些重要的区别:
    1、单引号逻辑
    #{}可以自动为参数添加单引号,从而防止了SQL注入攻击。
    ${}则是直接字符串替换,MyBatis 不会对其中的字符作任何转义处理,可能存在 SQL 注入的风险。
    2、传参方式
    #{}可以接收参数的任意类型, MyBatis 会根据参数的不同类型自动完成 Java 对象与 JDBC 类型之间的转换。
    ${}则只能接收字符串类型的参数值,SQL 语句中所有的参数值都是字符串字面量。
    3、参数替换规则
    #{}被解析为一个 JDBC 预编译语句(PreparedStatement)的参数标记符(‘?’)。
    ${}被解析为直接字符串替换,相当于直接把原始字符串拼接到 SQL 语句中。
    4、特殊字符处理
    #{}可以对特殊字符(如单引号)进行转义处理,避免出现语法错误。
    ${}则不会对特殊字符进行任何处理,因此有可能导致语法错误。
    综合来看,在大多数情况下,我们应该使用 #{} 来防止 SQL 注入攻击,并且可以传递多种类型的参数值。只有在某些特殊情况下,需要手动拼接 SQL 语句时,才应该使用 ${}。另外,在 SQL 语句中还可以使用 #{}和 ${}的混合形式,这种情况下需要格外小心,防止引起 SQL 注入漏洞。
    29、一致性hash算法
    一致性哈希(Consistent Hashing)是一种分布式哈希算法,常用于负载均衡、分布式缓存等场景中,它可以较好地解决传统哈希算法在节点数量变化时引起的大量数据重新映射的问题。
    一致性哈希算法的主要思想是:
  9. 哈希环(Hash Ring)
    首先把所有的节点(物理机或者虚拟节点)通过哈希函数映射到一个环形空间(哈希环)上。
  10. 数据映射
    当需要存储一个数据时,同样使用哈希函数计算其哈希值,并映射到这个哈希环上。数据应该被存储到环上顺时针方向遇到的第一个节点上。
  11. 节点变化
    当系统中有新节点加入或者节点下线时,只需要对加入/移除的节点进行重新映射,而其他节点的数据映射关系不受影响,从而大大减少了数据交换的量。
    一致性哈希算法相比传统哈希算法的主要优点是:
  12. 数据分布均匀
    通过增加虚拟节点,可以使数据分布更加均匀。
  13. 节点扩展灵活
    添加或删除节点时,只需重新分配哈希环上的部分数据,而不必全部重新映射。
  14. 容错性好
    当某个节点失效时,只需要在哈希环上将它的数据映射到顺时针方向的下一个节点即可。
    常见的一致性哈希算法实现包括Ketama算法(最早用于Memcache分布式缓存)、FNV哈希算法等。一致性哈希算法被广泛应用于负载均衡系统、分布式缓存、分布式文件系统等场景中。
    总的来说,一致性哈希算法是分布式系统设计中一种重要的数据分布技术,它能在节点变化时最小化数据重新分布的代价,提高整个系统的可扩展性和可用性。
    30、如何判断一个对象可以被回收,哪些对象可以作为gc root
    在Java中,一个对象是否可以被垃圾回收(GC)主要通过可达性分析算法(Reachability Analysis)来判断。依据这个算法,如果从GC Root集合到一个对象没有任何引用链相连,则这个对象就是不可达的,可以被回收。
    可达性分析算法
    基本思想是从一组被称为GC Roots的对象作为起点,开始向下搜索所有可达的对象。如果一个对象在这条搜索路径中没有被涉及,即不可达的对象,它就被认为是可以被回收的。
    GC Roots
    在Java中,可以作为GC Root的对象有以下几种:
  15. 虚拟机栈中的引用(栈帧中的本地变量表)
    • 这些是当前正在执行的方法中的局部变量(包括输入参数、临时变量等引用)。
  16. 方法区中的类静态属性引用
    • 方法区(或者说永久代/元空间)中的类静态属性引用的对象。例如,某个类的静态变量引用的对象。
  17. 方法区中的常量引用
    • 常量池中的常量,例如字符串常量或者声明为final类型的常量,长期保存某些引用的对象。
  18. 本地方法栈中的JNI引用(即常说的本地方法native方法引用)
    • 这些是在本地方法中使用JNI(Java Native Interface)引用的对象。
  19. 活动线程
    • 所有正在运行的线程,意味着线程对象以及线程栈上的引用都不能被回收。
  20. Java自身的关键数据结构
    • 例如类加载器(ClassLoader)等,它们持有一个或多个被其他线程持有但没有更新的数据引用。
  21. 同步锁持有的对象
    • 被synchronized关键字锁住的对象,直到锁被释放。
    • 判定步骤
  22. 启动一个GC过程。
  23. 从GC Roots开始,标记所有可达的对象。
  24. 遍历所有Java对象,检查哪些对象没有被标记为可达。
  25. 回收那些不可达的对象。
    通过这种方式,垃圾回收器可以有效地识别出可以回收的对象并释放其占用的内存空间,从而提高内存利用率。
    实际使用
    在实际应用中,开发者无需主动进行垃圾回收,但在设计系统时应当注意避免创建无用的对象引用、及时将无用的对象引用置为null,以便垃圾回收器可以发现这些对象。
    理解GC的工作原理有助于进行性能调优和内存泄漏的排查,但具体的GC策略和行为可能会因为GC的实现不同(如垃圾首次收集器,CMS,G1等)略有差异。
    30、为什么重写了equals方法后,需要重写hashcode方法
    hash的时候,需要保证equals对象映射到同一个位置,如果不重写,就可能导致映射到不同地方,例如对象a有两个属性,如果指定两个属性hash code方法,即使两个属性相同,会被计算出不同的hashcode
    尤其是针对List<对象>场景,必须重写
    31、string、stringbuilder、stringbuffer啥区别
    String: 不可变、线程安全。适用于少量字符串操作以及需要字符串不可变的场景。如果需要对字符串进行大量的修改,String类的效率会非常低,因为每次修改都会创建一个新的String对象。
    StringBuilder: 可变、非线程安全。适用于大量字符串修改操作的单线程环境,性能最佳。
    StringBuffer: 可变、线程安全。适用于多线程环境中需要频繁修改字符串的场景。
    根据具体需求选择合适的类,以达到最佳的性能和线程安全性。在需要大量字符串拼接或修改的情况下,优先考虑使用StringBuilder(单线程)或StringBuffer(多线程)。
    32、深克隆实现方式:
    方式一:实现cloneAble接口(本地使用)
    1、实现cloneAble方法,重写object类的clone方法
    2、new对象以后,调用clone方法,即可完成深克隆
    方式二:序列化和反序列化(远程使用)
    通过将对象序列化为字节流然后反序列化成新的对象来实现深克隆。
    33、LRU Cache(Least Recently Used Cache)算法
    RU Cache(Least Recently Used Cache)是一种缓存机制,用于在内存和性能有限的情况下,优先保留最近使用的数据,将长时间未使用的数据淘汰掉。LRU缓存是一种基于使用时间的缓存淘汰策略,广泛应用于操作系统的页面置换、数据库缓存、Web缓存等场景。
    LRU Cache的工作原理
    初始化缓存容量:设定一个固定的缓存容量(大小)。
    数据访问:
    读取数据:从缓存中读取数据时,如果数据存在,则将其移动到最近使用的位置。如果数据不存在,则从数据源中读取,并将其加入缓存,可能会导致某些旧数据被淘汰出缓存。
    写入数据:向缓存写入数据时,也会将数据移动到最近使用的位置。如果缓存已满,则移除最久未访问的数据以腾出空间。
    淘汰策略:当缓存空间不足时,根据“最久未使用”(Least Recently Used)的原则淘汰数据,即移除最长时间未访问的数据。
    34、wait和sleep区别
    wait必须拿到锁了以后才能调用(object的实例方法),sleep不需要,静态方法,任何时候调用;
    wait 方法线程扭转状态
    线程进入等待状态:当线程调用 wait 方法时,它会释放当前持有的对象锁,并且进入该对象的等待队列(Wait Queue)。线程进入等待状态,等待其他线程调用相同对象的 notify 或 notifyAll 方法。
    唤醒并进入就绪状态:当另一个线程调用对象的 notify 或 notifyAll 方法时,被唤醒的线程会尝试重新获取对象的监视器锁。一旦获取到锁,它就会从等待队列移动到就绪队列(Ready Queue),并等待CPU调度。
    进入运行状态:当CPU调度到该线程时,可以重新进入运行状态。
    sleep线程扭转状态
    线程在sleep方法的休眠时间结束后会变为就绪态(可运行状态),而不是直接变为运行状态。这是因为线程的运行需要CPU资源,只有当CPU调度器选择该线程并分配CPU时间片时,线程才会从就绪态转变为运行态。
    35、死锁的四大条件
    互斥,不剥夺,循环等待,请求与保持
    36、notify和notifyall区别(都是针对用一个对象进行锁操作)
    notify:
    唤醒在此对象的监视器上等待的单个线程。
    被唤醒的线程将尝试重新获取对象的监视器锁。如果被唤醒的线程在竞争锁资源时没有取得锁,它将继续等待。
    如果多个线程在等待该对象的监视器锁,具体唤醒哪个线程是不确定的,由线程调度器决定。
    notifyAll:
    唤醒在此对象的监视器上等待的所有线程。
    被唤醒的每个线程将尝试重新获取对象的监视器锁。运行时环境会选择一个线程来获取锁,其余的线程将继续等待(等待监视器锁的释放)。
    37、string常量池总结
    1、字符串字面量 被自动放入字符串常量池,并且相同字面量只存储一份。
    2、使用 new 关键字创建字符串对象时,会在堆内存中创建一个新的对象,而不是使用常量池中的对象。
    3、调用 intern 方法时,会将该字符串添加到字符串常量池中,并返回池中的字符串对象。
    4、编译时的字符串连接(如 “Ja” + “va”)会被优化为单个字符串字面量,直接存储在字符串常量池中。
    5、运行时的字符串连接会创建新的字符串对象,但可以通过调用 intern 方法使之引用字符串常量池中的对象。
    38、hash冲突的解决办法,hashmap使用哪一种
    链地址法(Separate Chaining)
    链地址法是一种常见的解决哈希冲突的方法,即将所有哈希值相同的元素存储在一个链表中。当有哈希冲突时,每个哈希槽都指向一个链表,链表中存储具有相同哈希值的所有元素。Java的 HashMap 就是采用了这种方法。
    工作原理
    每个哈希桶(bucket)存储一个链表(或其他集合结构)。
    插入元素时,如果哈希槽为空,直接存储该元素;如果哈希槽已经被占用,则在链表中追加该元素。
    查找元素时,通过哈希槽的链表遍历进行查找。
  26. 开放地址法(Open Addressing)
    开放地址法是将所有元素都存储在哈希表数组中,而不是在每个槽位实现一个链表。当发生哈希冲突时,通过某种方法探测下一个可用的槽位。常见的探测方法有线性探测(Linear Probing)、二次探测(Quadratic Probing)和双重哈希(Double Hashing)。
    工作原理
    初始插入时计算哈希值确定槽位。
    如果该槽位已经被占用,通过探测方法寻找下一个可用槽位。
    查找元素时,同样通过探测方法进行查找。
  27. 再哈希法(Rehashing)
    再哈希法是一种试图在冲突发生时使用不同的哈希函数计算另一个哈希值的位置。其解决冲突的方法是采用一组哈希函数,当第一个哈希函数产生冲突时,使用第二个哈希函数,以此类推。
    39、如何拷贝数组,怎样效率最高?为什么?
    1、System.arraycopy() 是效率最高的方式,因为它是本地方法,直接调用底层系统的内存复制功能,性能最佳。
    2、clone() 方法效率也很高,适用于一维数组的拷贝。
    3、Arrays.copyOf() 方法内部调用了 System.arraycopy(),性能稍逊于直接使用 System.arraycopy()。
    4、手动循环copy(代码模式)
    40、volatile特点,读取和写入volatile变量会发生流程
    volatile 的特点
    可见性(Visibility):
    当一个线程修改了被 volatile 修饰的变量,新的值对于其他所有线程是立即可见的。意味着一个线程对 volatile 变量的写操作会立刻反应到主内存中,其他线程读取时会从主内存中获取最新的值。
    有序性(Ordering):
    volatile 禁止重排序。编译器和处理器在进行优化时不会将 volatile 变量的操作与其他内存操作一起重排序。特别是对 volatile 变量的写操作与之前的读或写操作不能重排序,对 volatile 变量的读操作与之后的读或写操作不能重排序。
    不保证原子性(Atomicity):
    volatile 仅保证对变量的读取和写入是单个操作的原子性(比如读取或写入一个基本类型的变量),但不保证复合操作的原子性。例如,自增操作 (i++) 不是原子性的,即便 i 是 volatile 变量。
    读取和写入 volatile 变量时会发生什么
    读取 volatile 变量
    直接从主内存中读取最新值,而不会使用线程的本地缓存(即寄存器或CPU缓存)。
    保证在 volatile 变量读取操作之前的所有内存操作都不会被重排序到该读取操作之后。
    写入 volatile 变量
    直接将值写入到主内存,而不仅仅写入线程的本地缓存。
    保证在 volatile 变量写入操作之后的所有内存操作都不会被重排序到该写入操作之前。
    41、如何使用jmap dump出内存数据,如何在jmap dump数据没反应的时候,dump数据?
    使用 jmap 生成堆转储
    确定 JVM 进程 ID (PID):
    首先,你需要找到正在运行的 Java 应用程序的进程 ID。
    你可以使用 jps 命令来列出所有运行中的 Java 进程及其 PID:
    生成堆转储:
    使用 jmap 命令生成堆转储文件。假设进程ID是 12345,转储文件名为 heapdump.hprof:
    jmap -dump:live,format=b,file=heapdump.hprof 12345
    参数解释:
    -dump: 指定生成堆转储。
    live: 只导出存活对象。
    format=b: 指定二进制格式。
    file=heapdump.hprof: 指定输出文件名。
    12345: Java 应用程序的进程 ID。
    可以加-f,强制进行导出 jmap -dump:live,format=b,file=heapdump.hprof -f 12345
    42、为什么eden、s0、s1的大小比例是8:1:1
    1、大部分对象的存活时间很短暂
    2、无效使用的内存区域占新生代1/10,如果调大,会让更多的内存区域无法使用
    43、CMS垃圾回收过程
  28. 初始标记(Initial Mark)
    目标:标记从GC Roots直接可达的对象。
    特点:这个过程会短暂地暂停所有应用程序线程(Stop-the-World)。
    耗时:初始标记时间通常很短,因为只需要标记一小部分对象。
  29. 并发标记(Concurrent Mark)
    目标:在应用程序继续运行的同时,标记剩余的可达对象。
    特点:并发过程,不会暂停应用程序线程。
    耗时:根据堆大小和对象复杂度,这个过程可能较长,但不会影响应用程序的响应时间。
  30. 重新标记(Remark)
    目标:在并发标记阶段,处理因应用程序运行而发生的标记变化,保证标记的准确性。
    特点:这个过程会再次暂停所有应用程序线程(Stop-the-World),但时间较短。
    耗时:时间比初始标记稍长,但依然一般较短。
  31. 并发清除(Concurrent Sweep)
    目标:在应用程序继续运行的同时,清除不再使用的对象,回收内存。
    特点:并发过程,不需要暂停应用程序线程。
    耗时:这个过程可能比较长,但与应用程序线程同时运行,不影响应用的响应时间。
    44、多次调用线程的start方法会怎么样?
    在Java中,每个线程对象的 start() 方法只能被调用一次。如果尝试多次调用同一线程对象的 start() 方法,就会抛出 IllegalThreadStateException 参数错误。
    45、线程sleep和yield啥区别
    sleep()使当前线程进入停滞状态,在指定时间范围内,一定不会执行,yield()只是让当前线程进入可执行状态(就绪),所以即使使用了yield,当前线程也可能立即执行,sleep可以使得优先级别低或高的线程得到cpu资源进行指定,但是yield只会让同级别的线程得到cpu进行执行机会;两个都是静态方法,可以直接调用;
    46、thread会释放锁吗?
    thread不会释放锁,因为他是线程自我管理,不设计线程间通信;sleep()、yield()方法都是thread的方法,wait、notify、是object的方法,涉及到线程间的通信,所以需要配合对象锁使用,wait方法也可以设置一个时间,自动醒来
    47、考虑设计一个计数器程序,场景一,一个线程写,多个线程读;场景二,多个线程写,多个线程读。分别应对这2种场景设计一个计数器程序 ,要保证线程安全
    场景一:一个线程写,多个线程读
    在这种场景中,我们可以使用 volatile 关键字来确保变量的可见性。因为只有一个线程在写操作,所以不需要使用复杂的同步机制,只需确保读操作能看到最新的写操作即可。
    在这种场景中,多个线程进行写操作和读操作,我们需要使用更强的同步机制,比如 ReentrantReadWriteLock 或 AtomicInteger 来保证线程安全。
    48、Java中,类的加载和初始化顺序
    1、静态属性初始化
    2、静态代码块按顺序执行
    3、静态方法可以随时调用
    4、普通属性初始化
    5、实例代码块按顺序执行
    6、构造方法执行
    49、普通属性和静态属性生命周期区别
    普通属性和默认值的影响
    普通属性:即使一个类有普通属性,这些属性本身并不会影响该类实例的生命周期。它们只是该类实例的一部分,类实例是否被回收取决于这个实例有没有被其他对象引用。
    默认值:无论属性是否有默认值,这都只是该属性的初始值设置。它不会影响垃圾回收的决策。在对象初始化之后,属性的值可以被重新赋值。
    静态属性的生命周期
    类加载:静态属性(类变量)会在类加载时初始化。这通常由类加载器完成,并且静态属性的生命周期与类的生命周期一致。不依赖于对象实例的创建或销毁
    类卸载:当类加载器卸载类时,静态属性也会被垃圾回收。
    类的生命周期
    一个类的生命周期由它的类加载器决定。类加载器会在以下情况下卸载类:
    类加载器本身被垃圾回收。
    没有对类的任何引用(即没有实例存在且没有静态方法被调用)。
    50、java类的加载和初始化是两个过程
    原则:延迟加载,只有需要使用的时候,java才会主动加载
    类的加载、链接和初始化
    加载(Loading):类加载器读取类文件,并创建一个 Class 对象。
    链接(Linking):链接过程包括验证、准备和解析:
    验证:确保被加载的类文件符合Java语言规范,不会危及JVM安全。
    准备:为类的静态变量分配内存,并设置默认初始值。
    解析:将类的符号引用转换为直接引用。
    初始化(Initialization):真正执行类的静态初始化器(类变量初始化)和静态初始化代码块。
    触发类初始化的情况
    类的初始化只会在特定情况下触发,主要包括以下几种:
    创建类的实例。
    访问类的静态变量。
    调用类的静态方法。
    反射操作。
    初始化一个类的子类。
    JVM启动时标识的启动类。
    声明为Java虚拟机的启动类的类。
    51、为什么innodb表的主键最好是自增?
    在InnoDB存储引擎中,主键设置为自增非常普遍,并且有很多实际的好处。这主要是因为InnoDB使用一个称为“聚集索引”的索引结构来组织数据。当主键是自增时,有以下几个主要优点:
    数据插入效率高
    使用自增主键意味着每次插入新记录时,它都会加到表的末尾。这种行为使数据写入的过程变得非常高效,因为它避免了在数据页中插入记录时的频繁分页和重排。
    页分裂和碎片较少
    页分裂:如果主键是自增的,那么新记录总是插入到最后一个数据页,减少了数据页分裂的可能性。
    碎片较少:如果主键不是自增的,记录可能会被插入到表的任何位置,导致频繁的页分裂和较高的碎片化。当主键自增时,记录在页中的物理位置与其逻辑顺序一致,减少了碎片化。
    内存和缓存的使用更高效
    自增主键使得数据的插入和读取顺序有较好的局部性(Spatial Locality),这有助于提高缓存命中率,提升数据库性能。
    二级索引的指针大小更小
    在InnoDB中,二级索引存储的是主键的值。如果主键是一个较短的整数(如自增ID),那么二级索引的存储开销会较小。相比之下,如果主键是一个大的字符串,该字符串不仅占用更多存储空间,还会增加二级索引维护的开销。
    便于范围查询
    由于查询主键是连续的,在进行范围查询的时候,可以在一个块或者连续块中查询到所有数据,减少数据块交换时间
    52、如何分析慢查询,慢查询的分析步骤?
    1、查看慢查询日志信息,拿到慢查询sql,初步得到慢查询可能结果
    2、再次执行sql,查看执行时间是否超长
    3、使用explain,分析sql索引情况
    4、如果sql没走索引,结合业务场景,找到区分度最高的字段,建立索引进行查询
    5、如果走了索引,是因为数据量太大,考虑分步查询或者使用es查询
    6、如果无法使用其他方式查询,可以考虑分库分表,从水平和垂直角度进行拆分数据库表
    53、MySQL索引默认实现是用的什么数据结构,为什么采用这种?
    B+树。
    索引也是磁盘上的,磁盘的I/O存取的消耗是比内存高出几个数量级的,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数,所以要尽量降 低树的高度。要降低树的高度,因此用多分支的树,并且要树的每层的节点尽量的多,B+树将一个节点的大小设为等于一个页,这样每个节点只需 要一次I/O就可以完全载入,由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。
    54、mysql联合索引匹配原则
    mysql会从左向右一直匹配,知道遇到第一个范围查询(>,<,like,between),最左匹配就消息,用到索引的数据为第一个范围查询之前索引(包括本身)
    55、如何删除数据库中重复数据
    建表sql:CREATE TABLE poi_menu_test ( id bigint(30) NOT NULL AUTO_INCREMENT COMMENT ‘ID’, poi_id bigint(20) NOT NULL DEFAULT ‘0’ COMMENT ‘id’, name varchar(64) NOT NULL DEFAULT ‘’ COMMENT ‘’, PRIMARY KEY (id)
    ) ENGINE=InnoDB AUTO_INCREMENT=17621402 DEFAULT CHARSET=utf8 COMMENT=‘poi’;
    删除sql delete pmt from poi_menu_test pmt inner join (select id,poi_id,name from poi_menu_test group by
    poi_id,name having count(*)>1 ) pmt1 on pmt.name=pmt1.name and pmt1.poi_id=pmt.poi_id and pmt1.id!=pmt.id
    56、osi七层和tcp/ip五层结构
    HTTP(超文本传输协议)通常参照 OSI(开放系统互连)模型中的七层结构和 TCP/IP 模型中的五层结构进行描述。在这些模型中,每一层都有不同的功能和协议,并且用于描述网络通信的各个层次。下面详细介绍这两种结构:
    OSI 七层模型
    OSI 模型分为七层,每一层负责特定的网络功能。这里是每一层的描述及其在 HTTP 通信中的作用:
  32. 应用层(Application Layer)
    • 功能:与最终用户直接交互,提供网络服务。
    • HTTP 在此层:HTTP 协议位于应用层。
    • 示例:HTTP、FTP、SMTP等。
  33. 表示层(Presentation Layer)
    • 功能:数据格式转换、加密与解密。
    • 示例:JPEG、MPEG、SSL/TLS。
  34. 会话层(Session Layer)
    • 功能:管理会话与连接(建立、保持和终止)。
    • 示例:RPC(远程过程调用)。
  35. 传输层(Transport Layer)
    • 功能:提供端到端的通信,保证数据传输的可靠性。
    • 示例:TCP、UDP。
    • HTTP 使用 TCP
  36. 网络层(Network Layer)
    • 功能:数据包路由与转发。
    • 示例:IP、ICMP。
    • IP 地址所在层次
  37. 数据链路层(Data Link Layer)
    • 功能:节点间数据传输,检测和纠正物理层的错误。
    • 示例:Ethernet、PPP。
  38. 物理层(Physical Layer)
    • 功能:物理介质上的数据传输。
    • 示例:光纤、电缆。

TCP/IP 五层模型
TCP/IP 模型是实际使用的网络模型,可以看作是对 OSI 七层模型的简化和整合。TCP/IP 模型分为五层:

  1. 应用层(Application Layer)
    • 功能:同样与最终用户直接交互,提供网络服务。
    • HTTP 在此层:HTTP 协议位于应用层。
    • 示例:HTTP、FTP、SMTP等。
  2. 传输层(Transport Layer)
    • 功能:类似于 OSI模型的传输层,提供端到端的通信保障。
    • 示例:TCP、UDP。
    • HTTP 使用 TCP
  3. 网络层(Network Layer)
    • 功能:类似于 OSI模型的网络层,负责数据包路由与转发。
    • 示例:IP、ICMP。
    • IP 地址所在层次
  4. 数据链路层(Data Link Layer)
    • 功能:类似于 OSI模型的数据链路层,处理节点间数据传输。
    • 示例:Ethernet、PPP。
  5. 物理层(Physical Layer)
    • 功能:类似于 OSI模型的物理层,负责物理介质上的数据传输。
    • 示例:光纤、电缆。
      比较
      OSI 七层模型提供了更详细的层次划分,但在实际使用中,TCP/IP 五层模型更为常用和简洁。
      两者在高层次(应用层、传输层和网络层)上的角色和功能定位相似,但在低层次(数据链路层和物理层)上,TCP/IP 模型将其合并为单一的物理传输层。
      HTTP 与 两种模型的关系
  • HTTP 协议在两种模型中都属于最高层(应用层),主要负责网页数据的请求和响应。
  • 在实际数据传输中,HTTP 依赖于底层的 TCP 传输层进行可靠的数据传输,然后由网络层负责路由和转发数据包,最后由数据链路层和物理层完成实际的数据传输任务。
    57、描述一下http请求到返回过程,越详细越好
    还有连接过程三次握手四次挥手(作用于tcp层)
    如果客户端和服务端都开启了Connection: keep-alive,那么tcp就会建立长连接,多次http请求,只需要一次三次握手和四次挥手
    在这里插入图片描述
    58、500、502、504code区别
    500 内部服务器错误(Internal Server Error):
    主要由服务器端的问题导致,不涉及网关或代理。
    表示服务器在处理请求时遇到不可预期的错误。
    502 错误网关(Bad Gateway):
    由作为网关或代理的服务器返回,tomcat没启动
    代理服务器从后端服务器收到无效响应。
    504 网关超时(Gateway Timeout):
    由作为网关或代理的服务器返回。
    代理服务器在等待后端服务器响应时超时。
    59、udp、tcp作用于哪一层,使用场景是啥
    传输层
    在传输层定义了两种服务质量不同的协议。即:传输控制协议TCP(transmission control protocol)和用户数据报协议UDP(user datagram protocol)。
    TCP协议是一个面向连接的、可靠的协议。它将一台主机发出的字节流无差错地发往互联网上的其他主机。 需要三次握手建立连接,才能进行数据传输。在发送端,它负责把上层传送下来的字节流分成报文段并传递给下层。 在接收端,它负责把收到的报文进行重组后递交给上层。TCP协议还要处理端到端的流量控制,以避免缓慢接收的接收方没有足够的缓冲区接收发 送方发送的大量数据。 UDP协议是一个不可靠的、无连接协议,不与对方建立连接,而是直接就把数据包发送过去。主要适用于不需要对报文进行排序和流量控制的 场合。 UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常, 其 实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是 通的。
    60、互联网默认端口号
    http:80,https:443,ftp:21,ssh,22,telent:23,mysql:3306,redis:6379,tomcat:8080
    61、线上oom情况出现,如何进行定位和解决
    在这里插入图片描述
    62、设计spring的ioc可以从以下几个方面考虑
    1、定义Bean的配置方式:可以通过XML配置、注解配置或者Java配置等方式来定义Bean的配置信息。
    2、实现Bean的加载和实例化:根据配置信息,通过反射机制实例化Bean,并将其放入容器中管理。
    3、实现Bean的依赖注入:根据配置信息,解析Bean之间的依赖关系,并通过反射机制将依赖的Bean注入到目标Bean中。
    4、实现Bean的生命周期管理:在Bean的实例化和销毁过程中,触发相应的初始化和销毁方法。
    5、实现Bean的作用域管理:根据配置信息,管理Bean的作用域,如单例、原型等。
    63、聚簇索引和非聚簇索引区别
    在InnoDB 中,因为聚簇索引是和数据顺序相关的,所以只包含一个聚簇索引,即主键索引,如果主键索引不存在则使用唯一索引,如果都不存在,则使用mysql自动生成的一个隐藏的自增索引列作为聚簇索引。
    那么聚簇索引有特点呢?聚簇索引不仅包含了索引值本身,还包含了该行数据的其他列信息。
    其他的普通索引则为非聚簇索引,非聚簇索引则只包含了索引信息,如果需要查询其他信息,根据查找到的索引信息还需要回表进行二次查询,即通过聚簇索引再进行一次B+树查找,影响性能。
    64、慢sql导致的原因
    1、没设置索引;2、没走索引;3、查询内容较多,导致回表查询;4、未限制范围查询,导致查询数量众多,甚至全表扫描;5、排序字段不对,导致聚合查询慢;
    如何优化:1、使用explane分析sql,使其走到正确的索引,索引最好建立在区分度较高地方;2、增加查询条件,限定查询范围;3、该用其他工具查询,比如es,redis;4、分库分表
    65、
  • 35
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值