目录
1.线程和协程的区别
协程的应用场景
2.索引,以及它们的好处和坏处
3.请你说说多线程
4.说说怎么保证线程安全
5.请你说说死锁定义及发生的条件
6.请你说说进程间的通信方式
7.说说你对MVC的理解
8.详细的说说Redis的数据类型
String(字符串)
Hash(哈希)
List(列表)
Set(集合)
sadd 命令
zset(sorted set:有序集合)
zadd 命令
9.请你说说乐观锁和悲观锁
10.设计模式了解么
11.说说你对AOP的理解
12.说说Redis的持久化策略
13.请你讲讲单例模式、请你手写一下单例模式
概念
简单-懒汉式
14.请你说说虚拟内存和物理内存的区别
15.说说你对IoC的理解
16.请你说说内存管理
17.请你说说IO多路复用(select、poll、epoll)
18.请你说说线程和协程的区别
19.请你说说MySQL的事务隔离级别
20.如何利用Redis实现一个分布式锁?
21.请说说你对反射的了解
22.请你说说ArrayList和LinkedList的区别
23.请你说说聚簇索引和非聚簇索引
24.数据库为什么不用红黑树而用B+树?
25.请你说说Redis的数据类型
26.请你讲讲工厂模式,手写实现工厂模式
27.你知道哪些线程安全的集合?
28.请你说说ConcurrentHashMap
29.说说缓存穿透、击穿、雪崩的区别
30.Redis如何与数据库保持双写一致性
31.说说你了解的线程同步方式
32.请你说说innodb和myisam的区别?
33.String、StringBuffer、Stringbuilder有什么区别
34.请你说说HashMap底层原理
35.说说你了解的JVM内存模型
36.说说JVM的垃圾回收机制
37.说说类加载机制
38.epoll原理
I/O
事件
通知机制
epoll的通俗解释
epoll的API详解
1. int epoll_create(int size)
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epoll的两种触发方式
1.水平触发的时机
2.边缘触发的时机
epoll与select、poll的对比
1. 用户态将文件描述符传入内核的方式
2. 内核态检测文件描述符读写状态的方式
3. 找到就绪的文件描述符并传递给用户态的方式
4. 重复监听的处理方式
epoll更高效的原因
39.请你说一下抽象类和接口的区别
40.请你说说==与equals()的区别
延伸:
==:
equals:
41.说说synchronize的用法及原理
42.说说你对AQS的理解
43.Java哪些地方使用了CAS
44.说说JVM的垃圾回收算法
45.请你说说Redis数据类型中的zset,它和set有什么区别?底层是怎么实现的?
46.说说static修饰符的用法
47.说说线程的状态
48.说说你对ThreadLocal的理解
49.说说Spring Boot常用的注解
50.说说Bean的生命周期
51.synchronized和Lock有什么区别
52.说说volatile的用法及原理
53.说说Redis的单线程架构
54.如何实现Redis高可用
55.请你说一下final关键字
56.请你说说重载和重写的区别,构造方法能不能重写
57.请说说你对Java集合的了解
58.请你说说IO多路复用
59.请你说说索引怎么实现的B+树,为什么选这个数据结构?
60.请你讲一下Java 8的新特性
61.请你说说泛型、泛型擦除
62.说说你了解的线程通信方式
63.请你说说JUC
64.请你说说HashMap和Hashtable的区别
65.HashMap是线程安全的吗?如果不是该如何解决?
66.请你说说Java的四种引用方式
67.请你讲下G1垃圾回收器
68.请你说说内存溢出
69.请你说说内存泄漏
70.请你说说数据库引擎有哪些,各自有什么区别
71.简单介绍Spring
72.介绍一下MyBatis的缓存机制
73.请你说说String类,以及new和
74.请你说说hashCode()和equals()的区别,为什么重写equals()就要重写hashcod()
75.说说线程的创建方式
76.说说你对ArrayList的理解
77.请你说说BIO、NIO、O
78.说说你对Spring Boot的理解,以及它和Spring的区别?
79.说说Spring Boot的自动装配
80.说说@Autowired和@Resource注解的区别
81.说说Redis的主从同步机制
82.说说Redis的缓存淘汰策略
83.说说垃圾收集器
84.请你说说Java的特点和优点,为什么要选择Java?
85.介绍一下包装类的自动拆装箱与自动装箱
86.说说wt()和sleep()的区别
87.说说你对线程池的理解
88.简单说下你对JVM的了解
89.说说Java运行时数据区
90.请你讲下CMS垃圾回收器
91.说说JVM的双亲委派模型
92.请你说说数据库索引的底层数据结构
93.说说Spring Boot 的启动流程
94.介绍一下Spring MVC的执行流程
95.在MyBatis中$和#有什么区别
96.请你说说Java基本数据类型和引用类型
97.请你说说Java的异常处理机制
98.说说你对面向对象的理解
99.请介绍一下访问修饰符
100.说说Java中常用的锁及原理
101.请你说说List与Set的区别
102.请你讲一下Java NIO
103.说说GC的可达性分析
104.说说类的实例化过程
105.请你讲讲B树和B+树
106.MySQL主从同步是如何实现的?
107.请你介绍一下数据库的ACID
108.请你说说数据库的索引是什么结构,为什么不用哈希表
109.请你说说InnoDB的MVCC
110.说说Spring Boot的起步依赖
111.说说Spring事务管理
113.说说Bean的作用域,以及默认的作用域
114.说说BeanFactory和FactoryBean的区别
115.说说你对Redis的了解
整理出了一百多道面试题目,供自己日常浏览,也欢迎大家关注收藏!
延伸:
协程(coroutine)是一种程序运行的方式,即在单线程里多个函数并发地执行。
在协程中的函数, 如果执行过程中遇到了I/O密集型任务, 被给定的
YieldInstruction
(让出指令)暂停执行(yield), 交出执行权到其他函数, 直到被给定的YieldInstruction
(让出指令)结束, 再继续执行。
协程的原理:
当出现IO阻塞的时候,由协程的调度器进行调度,通过将数据流立刻yield掉(主动让出),并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去跑,这样看上去好像跟写同步代码没有任何差别,这整个流程可以称为coroutine,而跑在由coroutine
负责调度的线程称为Fiber
。比如Golang里的 go关键字其实就是负责开启一个Fiber
,让func
逻辑跑在上面。
由于协程的暂停完全由程序控制,发生在用户态上;而线程的阻塞状态是由操作系统内核来进行切换,发生在内核态上。
因此,协程的开销远远小于线程的开销,也就没有了ContextSwitch上的开销。
协程的应用场景主要在于 :I/O 密集型任务。
这一点与多线程有些类似,但协程调用是在一个线程内进行的,是单线程,切换的开销小,因此效率上略高于多线程;
当程序在执行 I/O 时操作时,CPU 是空闲的,此时可以充分利用 CPU 的时间片来处理其他任务;
在单线程中,一个函数调用,一般是从函数的第一行代码开始执行,结束于 return 语句、异常或者函数执行(也可以认为是隐式地返回了 None );
有了协程,我们在函数的执行过程中,如果遇到了I/O密集型任务,函数可以临时让出控制权,让 CPU 执行其他函数,等 I/O 操作执行完毕以后再收回其他函数的控制权。
好处:
1、帮助用户提高查询速度
2、利用索引的唯一性来控制记录的唯一性
3、可以加速表与表之间的连接
4、降低查询中分组和排序的时间
坏处:
1、存储索引占用磁盘空间
2、执行修改操作(insert、delete、update)产生索引维护
延伸:
索引是为了加速对表中数据行的检索而创建的一种分散的存储结构实现原理(B+树具体可以看:MySQL索引底层:B+树详解 - 知乎) 通俗点说类似于一本书的目录,通过目录可以快速查到你想要的数据。
索引分类
1.普通索引普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。
语句:
创建索引语句:
CREATE INDEX index_name ON table(column)
修改表时加索引语句:
ALTER TABLE table_name ADD INDEX index_name ON (column)
2.唯一索引
索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
创建唯一索引语句:
CREATE UNIQUE INDEX indexName ON table(column)
修改表时加索引语句:
ALTER TABLE table_name ADD UNIQUE indexName ON (column)
3.主键索引
是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。
创建主键索引语句:
CREATE TABLE 'table' ( 'id' int(11) NOT NULL AUTO_INCREMENT , PRIMARY KEY ('id') );
4.复合索引(组合索引)
用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引) 查询时使用创建时第一个开始 索引才有效 使用遵循左前缀集合
复合语句:
ALTER TABLE `table` ADD INDEX indexName (name,xb,age);
线程是操作系统调度的最小单元,它可以让一个进程并发地处理多个任务,也叫轻量级进程。所以,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈、局部变量,并且能够共享进程内的资源。由于共享资源,处理器便可以在这些线程之间快速切换,从而让使用者感觉这些线程在同时执行。 总的来说,操作系统可以同时执行多个任务,每个任务就是一个进程。进程可以同时执行多个任务,每个任务就是一个线程。一个程序运行之后至少有一个进程,而一个进程可以包含多个线程,但至少要包含一个线程。
延伸:
提到多线程这里要说两个概念,就是串行和并行,搞清楚这个,我们才能更好地理解多线程。
所谓串行,其实是相对于单条线程来执行多个任务来说的,我们就拿下载文件来举个例子:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,也就是说,必须等下载完A之后才能开始下载B,它们在时间上是不可能发生重叠的。
进程:电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如下图中的QQ、酷狗播放器、电脑。
什么是线程呢?
进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。
1. 更多的CPU核心:现代计算机处理器性能的提升方式,已经从追求更高的主频向追求更多的核心发展,所以处理器的核心数量会越来越多,充分地利用处理器的核心则会显著地提高程序的性能。而程序使用多线程技术,就可以将计算逻辑分配到多个处理器核心上,显著减少程序的处理时间,从而随着更多处理器核心的加入而变得更有效率。
2. 更快的响应时间:我们经常要针对复杂的业务编写出复杂的代码,如果使用多线程技术,就可以将数据一致性不强的操作派发给其他线程处理(也可以是消息队列),如上传图片、发送邮件、生成订单等。这样响应用户请求的线程就能够尽快地完成处理,大大地缩短了响应时间,从而提升了用户体验。
3. 更好的编程模型:Java为多线程编程提供了良好且一致的编程模型,使开发人员能够更加专注于问题的解决,开发者只需为此问题建立合适的业务模型,而无需绞尽脑汁地考虑如何实现多线程。一旦开发人员建立好了业务模型,稍作修改就可以将其方便地映射到Java提供的多线程编程模型上。
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
如何保证呢: 1、使用线程安全的类; 2、使用synchronized同步代码块,或者用Lock锁;
由于线程安全问题,使用synchronized同步代码块 原理:当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。 另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
导致线程不安全的原因,主要有三点:
- 原子性:一个或者多个操作在CPU执行的过程中被中断
- 可见性:一个线程对共享变量的修改,另外一个线程不能立刻看到
- 有序性:程序执行的顺序没有按照代码的先后顺序执行
- 静态编译器是将.java文件编译成.class文件,之后便可以解释执行。
- 动态编译器是将.class文件编译成机器码,之后再由jvm运行。
有时候,动态编译器为了程序的整体性能会对指令进行重排序
虽然重排序可以提升程序的性能,但是重排序之后会导致源代码中指定的内存访问顺序与实际的执行顺序不一样,就会出现线程不安全的问题。
java程序如何保证线程安全呢?
针对原子性问题:
JDK里面提供了很多atomic类比如AtomicInteger、AtomicLong、AtomicBoolean等等这些类本身可以通过CAS来保证操作的原子性。另外Java也提供了各种锁机制,来保证锁内的代码块在同一时刻只能有一个线程执行比如使synchronized加锁这样,就能够保证一个线程在对资源进行读、改、写操作时,其他线程不可对此资源进行操作从而保证了线程的安全性。
针对可见性问题:
同样可以通过synchronized关键字加锁来解决。与此同时,java还提供了volatile关键字,要优于synchronized的性能,同样可以保证修改对其他线程的可见性。volatile一般用于对变量的写操作不依赖于当前值的场景中,比如状态标记量等。
针对重排序问题:
可以通过synchronized关键字定义同步代码块或者同步方法保障有序性另外也可以通过Lock接口来保障有序性。
死锁定义:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。(集合中的每一个进程都在等待只能由本集合中的其他进程才能引发的事件,那么该组进程是死锁的。)
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
一不满足,就不会发生死锁。
死锁的处理方法
1、预防死锁:事先预防策略,容易实现,通过实现设置限制,破坏产生死锁的四个条件之一。(如对资源采用按序分配策略)
2、避免死锁:事先预防策略,在资源的动态分配过程中,用某些方法防止系统禁图不安全状态。常见的方法有银行家算法。
3、检测死锁:通过检测机构等及时检测出死锁,采取适当措施,把进程从死锁中解脱。
4、解除死锁:检测出死锁后,采取措施解决。比如剥夺资源,撤销进程。
这四种方法对死锁的防范逐渐减弱,但对应的是资源利用率的提高。
1.管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间用。进程的亲缘关系通常是指父子进程关系。
2.管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3.消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4.共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
5.量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6.套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
7.信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,主要提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。主要使用的技术:数据模型:实体类(JavaBean),数据访问:JDBC,Hibernate等。
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,比如JSP,Html等
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。主要使用的技术:servlet,Struts中的Action类等。
MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式。
MVC 是软件工程里面一个常用的概念。通常来讲,C 主要是指请求 url 的路由规则,即把请求打到对应的控制器。控制器调用模型实现业务逻辑和底层数据的获取,这一步骤一般在代码里会抽象出多个层次,比如 service,model,handler,controller 等等,看开发者的个人喜好,统称为 Model 层。最后交给渲染器渲染成 html 这样的客户端直接可用的格式,返回给客户端,这一步骤一般叫视图层。不过 MVC 在 web 开发里面已经有点昨日黄花的意味。现在讲究服务化,REST等等,前后端进一步分离,出现专门写逻辑的前端和 MVVM 模式,View 层在后端可以说已经用不到了,当然你也可以把生成的 json 理解成 View 层。MVC 模式里面容易让人误解的就是 Model 层,其实 model 层不仅包含了从数据库获取数据,还包含了与请求相关的各种业务逻辑,可以说 model 层包含了你 90% 以上的代码。
在代码中,前后端分离项目,大多就是controller-service-dao三层架构,三层架构和mvc并不是两个对立的东西,service层与dao层是对model的进一步解耦而形成的分层,而controller(web)层本来就不该含有业务逻辑
(我以前以为model是数据,controller是逻辑,这是错的)
很多程序员偏爱于将业务逻辑放在Controller中,我极力反对这种做法。
我们知道在写程序时,业务逻辑的重复使用是经常要面对的场景。 如果业务逻辑写在控制器中, 要重用它的唯一方法就是将它提升到父类之中,通过继承来达到代码复用的效果。但这么做会带来一个巨大的副作用,违背了一项重要的面向对象设计原则:接口隔离。(?)
各Model之间是可以相互调用的, Controller也可以无障碍的调用Model,因此将业务逻辑放在Model中可以灵活的使用组合的方式复用代码。
而Controller之间是不可以相互调用的,要复用代码只能将代码提升至父类,通过继承实现,显然这种做法既不正确,也不灵活,因此完全不提倡。
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。
string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
添加元素到集合,元素在集合中存在则更新对应score
悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;
特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;
手动加悲观锁:读锁LOCK tables test_db read释放锁UNLOCK TABLES;
写锁:LOCK tables test_db WRITE释放锁UNLOCK TABLES;
乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现);
特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁。
什么是设计模式?
某个问题的固定的解决方案。(可以被重复使用。)
你知道哪些设计模式?
GoF设计模式:
通常我们所说的23种设计模式。(Gang of Four:4人组提出的设计模式)
单例模式
工厂模式
代理模式
门面模式
责任链设计模式
观察者模式
模板方法设计模式
.....
JavaEE设计模式:
DAO
DTO
VO
PO
pojo
....
....
什么是模板方法设计模式?
在模板类的模板方法当中定义核心算法骨架,具体的实现步骤可以延迟到子类当中完成。
模板类当中的抽象方法就是不确定实现的方法,这个不确定怎么实现的事儿交给子类去做。
快速记忆23种设计模式 - 知乎
【设计模式目录】简单阐述一下设计模式 - 掘金
AOP全称叫做 Aspect Oriented Programming 面向切面编程。它是为解耦而生的,解耦是程序员编码开发过程中一直追求的境界,AOP在业务类的隔离上,绝对是做到了解耦,在这里面有几个核心的概念:
切面(Aspect): 指关注点模块化,这个关注点可能会横切多个对象。事务管理是企业级Java应用中有关横切关注点的例子。 在Spring AOP中,切面可以使用通用类基于模式的方式(schema-based approach)或者在普通类中以@Aspect
注解(@AspectJ 注解方式)来实现。
连接点(Join point): 在程序执行过程中某个特定的点,例如某个方法调用的时间点或者处理异常的时间点。在Spring AOP中,一个连接点总是代表一个方法的执行。
通知(Advice): 在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”, “before” and “after”等等。通知的类型将在后面的章节进行讨论。 许多AOP框架,包括Spring在内,都是以拦截器做通知模型的,并维护着一个以连接点为中心的拦截器链。
切点(Pointcut): 匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行(例如,当执行某个特定名称的方法时)。切点表达式如何和连接点匹配是AOP的核心:Spring默认使用AspectJ切点语义。
引入(Introduction): 声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被通知的对象上。例如,可以使用引入来使bean实现 IsModified
接口, 以便简化缓存机制(在AspectJ社区,引入也被称为内部类型声明(inter))。
目标对象(Target object): 被一个或者多个切面所通知的对象。也被称作被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,那么这个对象永远是一个被代理(proxied)的对象。
AOP代理(AOP proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被被通知的对象的过程。这个过程可以在编译时(例如使用AspectJ编译器)、类加载时或运行时中完成。 Spring和其他纯Java AOP框架一样,是在运行时完成织入的。
这些概念都太学术了,如果更简单的解释呢,其实非常简单:
任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码冗余太多,因此我们需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去,AOP正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。
redis的持久化方式有俩种,持久化策略有4种:
RDB(数据快照模式),定期存储,保存的是数据本身,存储文件是紧凑的AOF(追加模式),每次修改数据时,同步到硬盘(写操作日志),保存的是数据的变更记录如果只希望数据保存在内存中的话,俩种策略都可以关闭也可以同时开启俩种策略,当Redis重启时,AOF文件会用于重建原始数据RDBRDB定时备份内存中的数据集。服务器启动的时候,可以从 RDB 文件中恢复数据集。
优点
存储的文件是紧凑的
适合用于备份,方便恢复不同版本的数据
适合于容灾恢复,备份文件可以在其他服务器恢复
最大化了Redis的性能,备份的时候启动的是子线程,父进程不需要执行IO操作
数据保存比AOF要快
缺点
如果Redis因为没有正确关闭而停止工作是,到上个保存点之间的数据将会丢失
由于需要经常fork子线程来进行备份操作,如果数据量很大的话,fork比较耗时,如果cpu性能不够,服务器可能是卡顿。属于数据量大的时候,一个服务器不要部署多个Redis服务。
创建快照有以下5种形式:
客户端发送BGSAVE指令,服务端会fork一条子线程将快照写入磁盘客户端发送SAVE指令,服务端在主线程进行写入动作。一般不常使用,一般在内存不够去执行BGSVAE的时候才用设置了SAVE配置项,如SAVE 300 100,那么当“300秒内有100次写入”时,Redus会自动触发BGSAVE命令。如果有多个配置项,任意一个满足,都会触发备份Redis通过SHUTDOWN命令接收到关闭服务器的请求、或者TERM信号时,会执行SAVE命令,这时候会阻塞所有客户端,不在执行客户端发送的任何命令当一个Redis服务器连接另外一个Redis服务器,并像对方发送SYNC命令开始一次复制操作时,如果主服务器目前没有在执行BGSAVE操作,或者主服务器刚刚执行完,那么主服务器就会执行GBSAVERedis的持久化策略 - 腾讯云开发者社区-腾讯云
1.概念
单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
单例模式就是严格控制一个类的实例,要求在一个进程或一个线程中只存在一个实例,如果没有就创建一个实例返回,如果有就返回已经已经存在的实例。不同的单例模式都是为了确保只有一个实例。
2.特点
单例类在一个进程或线程中只能存在一个实例。
单例类的构造器必须私有化。不允许其它类来创建单例类,只有它自己能够创建自己。
单例类需要向其它类提供获取实例的方法,一般是静态的。
3.优点
减少了内存的开销。因为单例模式要做是严格控制只能存在一个实例,这样就会极大地减少实例反复创建时带来的内存开销。
更加容易管理和维护。没什么好说的,全部内容都写在这个类里,只需要修改这个类就行了。
4.缺点
扩展性不强。单例模式只专注于只有能存在一个实例,这就牺牲了它的扩展性。
与单一职责冲突。因为所有方法都要封装在单例类中,所以会越来越庞大,职责也会越来越不清晰。
二、单例模式的分类
1.饿汉式
就像饿汉遇到美食一样,一下子就全部吃完。
饿汉式单例,就是在装载.class文件是就会在静态区生成一个实例,其它类都是调用这个类。
package singletion;
/**
* @author xxj
* 饿汉单例
*/
public class HungrySingletion {
private static HungrySingletion instance=new HungrySingletion();
private HungrySingletion(){}
public static HungrySingletion getInstance(){
return instance;
}
}
特点
饿汉式就是简单粗暴,一上来就在静态区生成一个实例,保证了线程安全,不会产生多余的实例,但是创建的实例会导致初始化比较缓慢。下一种单例模式就是解决了这个问题。
2.懒汉式
懒汉嘛就是懒,不到火烧眉毛的时候绝对不会去做事情。
懒汉式单例,就是只在调用向其它类提供获取实例的方法时,才会去生成实例,但是这样就会存在一些线程问题。
package singletion;
/**
* @author xxj
* 懒汉式单例
*/
public class LazySingleton {
private static LazySingleton instance=null;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(instance==null)
instance=new LazySingleton();
return instance;
}
}
特点
一开始instance指向的是null,所以它不会一上来就在静态区生成一个实例,而是延迟加载,但是它存在线程问题。一旦多个线程同时访问这个获取实例的方法时,就会出现创建多个实例的情况,那它在这种极端的情况下,就不能算是单例模式了,所以下一个懒汉模式解决了这个问题。
3.静态内部
public class Singleton {
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.instance ;
}
}
解释
这种使用静态内部类实现的单例模式,其实是饿汉式的升级版,首先是在单例类的内部创建了一个静态内部类,内部类里面有一个单例类的实例;然后,单例类给其它类提供一个获取实例的方法。
当其它类调用这个方法时,类加载器才会去装载内部类,一装载,就会生成一个单例类的实例。
特点
不仅继承了饿汉式的线程安全的特性,而且实现了延迟加载的功能。
4.枚举式
package singletion;
/**
* @author xxj
* 枚举类单例模式
*/
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return EnumSingleton.INSTANCE;
}
}
特点
首先即使实现简单方便,用枚举类实现单例实际上和饿汉式是一样的,它可以保证线程安全,但是同样初始化比较缓慢。
再说一点,枚举类的确可以实现单例化,我验证过hashCode,都是一样的,但是它的写法就有点让人摸不着头脑。
【设计模式】面试官:请你手写一个单例模式!我:我要写五个! - 掘金
操作系统有虚拟内存与物理内存的概念。在很久以前,还没有虚拟内存概念的时候,程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于CPU的地址线条数。比如在32位平台下,寻址的范围是2^32也就是4G。并且这是固定的,如果没有虚拟内存,且每次开启一个进程都给4G的物理内存,就可能会出现很多问题:
因为我的物理内存时有限的,当有多个进程要执行的时候,都要给4G内存,很显然你内存小一点,这很快就分配完了,于是没有得到分配资源的进程就只能等待。当一个进程执行完了以后,再将等待的进程装入内存。这种频繁的装入内存的操作是很没效率的
由于指令都是直接访问物理内存的,那么我这个进程就可以修改其他进程的数据,甚至会修改内核地址空间的数据,这是我们不想看到的因为内存时随机分配的,所以程序运行的地址也是不正确的。于是针对上面会出现的各种问题,虚拟内存就出来了。
进程分配资源介绍过一个进程运行时都会得到4G的虚拟内存。这个虚拟内存你可以认为,每个进程都认为自己拥有4G的空间,这只是每个进程认为的,但是实际上,在虚拟内存对应的物理内存上,可能只对应的一点点的物理内存,实际用了多少内存,就会对应多少物理内存。
进程得到的这4G虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它通常是被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,在需要时进行数据交换。
进程开始要访问一个地址,它可能会经历下面的过程每次我要访问地址空间上的某一个地址,都需要把地址翻译为实际物理内存地址所有进程共享这整一块物理内存,每个进程只把自己目前需要的虚拟地址空间映射到物理内存上进程需要知道哪些地址空间上的数据在物理内存上,哪些不在(可能这部分存储在磁盘上),还有在物理内存上的哪里,这就需要通过页表来记录页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。
spring是一个ioc容器,容器就是放数据的,java里面的容器就是集合类。ioc容器实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点||repository、service、controller、component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射new对象放到map里;扫描到打上上述注解的类还是通过反射new对象放到map里。
这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。
至于为什么要这么做,要知道工厂模式(控制反转)解决了什么问题又带来了什么问题。
1.解决的问题:解决了类与类之间强引用关系的问题,强引用关系导致了代码的耦合性高,维护性差。(控制反转的作用,举个例子,现在有个变量String name=“小明”,在两个类里都要用到小明这个字符串,如果不使用控制反转,在class A中要定义一个String name=“小明”;在class B中也要定义一个String name=“小明”;这个时候我需要把小明改成李四,那我需要找到所有用到“小明”的类然后一个一个去修改。实际上我们是定义一个静态常量,然后在需要用到的地方进行引用,这样的话当我们需要把小明修改成李四只需要去定义这个静态常量的那个类里去修改一次,所有引用到的地方都被修改了。这其实就是控制反转,定义静态常量的类就是工厂类,被定义的常量就是ioc容器内的各种对象)
2.带来的问题:导致工厂类的代码冗长,每增加一个接口都要在工厂类里加一段代码。
3.为了解决工厂模式带来的这个问题,spring通过配置化的方式来解决,也就是上面说的xml配置文件(通过遍历bean节点的方式new对象)
4.随着时间的发展,程序员们又觉得使用xml配置文件很麻烦,因为项目大了之后,各种配置文件眼花缭乱,就开始了注解之路了,于是这个时候SpringBoot出现了.....
内存管理(Memory Management)是操作系统设计中最重要和最复杂的内容之一。虽然计算机硬件一直在飞速发展,内存容量也在不断增长,但是仍然不可能将所有用户进程和系统所需要的全部程序和数据放入主存中,所以操作系统必须将内存空间进行合理地划分和有效地动态分配。操作系统对内存的划分和动态分配,就是内存管理的概念。
有效的内存管理在多道程序设计中非常重要,不仅方便用户使用存储器、提高内存利用率,还可以通过虚拟技术从逻辑上扩充存储器。
内存管理的功能有:
- 内存空间的分配与回收:由操作系统完成主存储器空间的分配和管理,使程序员摆脱存储分配的麻烦,提高编程效率。
- 地址转换:在多道程序环境下,程序中的逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供地址变换功能,把逻辑地址转换成相应的物理地址。
- 内存空间的扩充:利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存。
- 存储保护:保证各道作业在各自的存储空间内运行,互不干扰。
IO多路复用的三种机制Select,Poll,Epoll - 简书
IO多路复用的三种机制Select、Poll、Epoll_一口Linux的博客-CSDN博客
线程和协程的区别的通俗说明 - 知乎
MySQL事务隔离级别和实现原理(看这一篇文章就够了!) - 知乎
【Redis】利用 Redis 实现分布式锁 - 周二鸭 - 博客园
谈谈你对反射的理解_摸鱼使我快乐的博客-CSDN博客_说说你对反射的理解
什么是反射?你敢说你非常了解吗? - 知乎
【java基础】程序员你真的理解反射机制吗?_宜春的博客-CSDN博客
数据库索引为什么要用 B+ 树而不用红黑树呢?_buyulian的博客-CSDN博客_索引为什么不用红黑树
数据库:为什么使用B+树而不使用红黑树_五山口老法师的博客-CSDN博客
手写工厂模式_喵了个咪的回忆丶的博客-CSDN博客_手写工厂模式
Java线程安全的集合详解_「已注销」的博客-CSDN博客_线程安全的集合
说说ConcurrentHashMap - 知乎
缓存穿透、缓存击穿、缓存雪崩区别和解决方案_每天进步一点点yes的博客-CSDN博客_缓存击穿
Redis 与 MySQL 双写一致性如何保证?_戴国进的博客-CSDN博客_redis如何保证双写一致
如何保证Redis和数据库双写一致性的问题? - 腾讯云开发者社区-腾讯云
线程同步的几种方式 - sherlock_lin - 博客园
MyISAM与InnoDB 的区别(9个不同点)_Chackca的博客-CSDN博客_myisam
String、StringBuffer、StringBuilder的区别——(详细叙述)_开机发呆小菜鸟的博客-CSDN博客
最通俗易懂搞定HashMap的底层原理 - 知乎
JVM内存模型(详解) - 知乎
JVM垃圾回收机制,万字详解_黑马程序员官方的博客-CSDN博客_jvm垃圾回收机制
Java类加载机制 - 你个小秃头 - 博客园
epoll是一种I/O事件通知机制,是linux 内核实现IO多路复用的一个实现。
IO多路复用是指,在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作。
输入输出(input/output)的对象可以是文件(file), 网络(socket),进程之间的管道(pipe)。在linux系统中,都用文件描述符(fd)来表示。
通知机制,就是当事件发生的时候,则主动通知。通知机制的反面,就是轮询机制。
结合以上三条,epoll的通俗解释是一种当文件描述符的内核缓冲区非空的时候,发出可读信号进行通知,当写缓冲区不满的时候,发出可写信号通知的机制
epoll的核心是3个API,核心数据结构是:1个红黑树和1个链表
功能:
size参数表示所要监视文件描述符的最大值,不过在后来的Linux版本中已经被弃用(同时,size不要传0,会报invalid argument错误)
功能:
typedef union epoll_data {
void *ptr; /* 指向用户自定义数据 */
int fd; /* 注册的文件描述符 */
uint32_t u32; /* 32-bit integer */
uint64_t u64; /* 64-bit integer */
} epoll_data_t;
struct epoll_event {
uint32_t events; /* 描述epoll事件 */
epoll_data_t data; /* 见上面的结构体 */
};
对于需要监视的文件描述符集合,epoll_ctl对红黑树进行管理,红黑树中每个成员由描述符值和所要监控的文件描述符指向的文件表项的引用等组成。
op参数说明操作类型:
struct epoll_event结构描述一个文件描述符的epoll行为。在使用epoll_wait函数返回处于ready状态的描述符列表时,
常用的epoll事件描述如下:
功能:
阻塞等待注册的事件发生,返回事件的数目,并将触发的事件写入events数组中。
events: 用来记录被触发的events,其大小应该和maxevents一致
maxevents: 返回的events的最大个数
处于ready状态的那些文件描述符会被复制进ready list中,epoll_wait用于向用户进程返回ready list。events和maxevents两个参数描述一个由用户分配的struct epoll event数组,调用返回时,内核将ready list复制到这个数组中,并将实际复制的个数作为返回值。注意,如果ready list比maxevents长,则只能复制前maxevents个成员;反之,则能够完全复制ready list。
另外,struct epoll event结构中的events域在这里的解释是:在被监测的文件描述符上实际发生的事件。
参数timeout描述在函数调用中阻塞时间上限,单位是ms:
epoll监控多个文件描述符的I/O事件。epoll支持边缘触发(edge trigger,ET)或水平触发(level trigger,LT),通过epoll_wait等待I/O事件,如果当前没有可用的事件则阻塞调用线程。
select和poll只支持LT工作模式,epoll的默认的工作模式是LT模式。
当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你。如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率。
当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你。这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符。
在ET模式下, 缓冲区从不可读变成可读,会唤醒应用进程,缓冲区数据变少的情况,则不会再唤醒应用进程。
举例1:
举例2:(以脉冲的高低电平为例)
JDK并没有实现边缘触发,Netty重新实现了epoll机制,采用边缘触发方式;另外像Nginx也采用边缘触发。
JDK在Linux已经默认使用epoll方式,但是JDK的epoll采用的是水平触发,而Netty重新实现了epoll机制,采用边缘触发方式,netty epoll transport 暴露了更多的nio没有的配置参数,如 TCP_CORK, SO_REUSEADDR等等;另外像Nginx也采用边缘触发。
虽然epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
图文详解 epoll 原理【Redis,Netty,Nginx实现高性能IO的核心原理】epoll 详解 - 腾讯云开发者社区-腾讯云
Java中接口和抽象类的定义语法分别为interface与abstract关键字。
抽象类:在Java中被abstract关键字修饰的类称为抽象类,被abstract关键字修饰的方法称为抽象方法,抽象方法只有方法的声明,没有方法体。抽象类的特点:
a、抽象类不能被实例化只能被继承;
b、包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法;
c、抽象类中的抽象方法的修饰符只能为public或者protected,默认为public;
d、一个子类继承一个抽象类,则子类必须实现父类抽象方法,否则子类也必须定义为抽象类;
e、抽象类可以包含属性、方法、构造方法,但是构造方法不能用于实例化,主要用途是被子类调用。
接口:Java中接口使用interface关键字修饰,特点为:
a、接口可以包含变量、方法;变量被隐士指定为public static final,方法被隐士指定为public abstract(JDK1.8之前);
b、接口支持多继承,即一个接口可以extends多个接口,间接的解决了Java中类的单继承问题;
c、一个类可以实现多个接口;
d、JDK1.8中对接口增加了新的特性:(1)、默认方法(default method):JDK 1.8允许给接口添加非抽象的方法实现,但必须使用default关键字修饰;定义了default的方法可以不被实现子类所实现,但只能被实现子类的对象调用;如果子类实现了多个接口,并且这些接口包含一样的默认方法,则子类必须重写默认方法;(2)、静态方法(static method):JDK 1.8中允许使用static关键字修饰一个方法,并提供实现,称为接口静态方法。接口静态方法只能通过接口调用(接口名.静态方法名)。
如下例子所示:
public interface Person{
public static final int a=10;
//JDK1.8
default void sayHello(){
System.out.println("Hello World");
}
public void say();
}
public abstract class Person{
public abstract void say();
public void eat(){};
}
如上述代码所示:
接口只能是功能的定义,而抽象类既可以为功能的定义也可以为功能的实现。
相同点
(1)都不能被实例化 (2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。
不同点
(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
(3)接口强调特定功能的实现,而抽象类强调所属关系。
(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
一、对象类型不同
1、equals():是超类Object中的方法。
2、==:是操作符。
二、比较的对象不同
1、equals():用来检测两个对象是否相等,即两个对象的内容是否相等。
2、==:用于比较引用和比较基本数据类型时具有不同的功能,具体如下:
(1)、基础数据类型:比较的是他们的值是否相等,比如两个int类型的变量,比较的是变量的值是否一样。
(2)、引用数据类型:比较的是引用的地址是否相同,比如说新建了两个User对象,比较的是两个User的地址是否一样。
三、运行速度不同
1、equals():没有==运行速度快。
2、==:运行速度比equals()快,因为==只是比较引用。
equals()和==的源码定义:
public boolean equals(Object obj) {
return (this == obj);
}
由equals的源码可以看出这里定义的equals与==是等效的(Object类中的equals没什么区别),不同的原因就在于有些类(像String、Integer等类)对equals进行了重写。
但是没有对equals进行重写的类就只能从Object类中继承equals方法,其equals方法与==就也是等效的,除非在此类中重写equals。
对equals重新需要注意五点:
1、自反性:对任意引用值X,x.equals(x)的返回值一定为true;
2、对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
3、传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true ;
4、 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变;
5、非空性:任何非空的引用值X,x.equals(null)的返回值一定为false 。
== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
1、比较的是操作符两端的操作数是否是同一个对象。
2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如:
int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。
String s="abce"是一种非常特殊的形式,和new 有本质的区别。它是java中唯一不需要new 就可以产生对象的途径。
以String s="abce";形式赋值在java中叫直接量,它是在常量池中而不是象new一样放在压缩堆中。这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象。
如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象,如果没有,则在常量池中新创建一个“abcd"”,下一次如果有Strings1="abcd";又会将s1指向“abcd”这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象。
而String s=new String("abcd”);和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。
也可以这么理解:String str="hello”;先在内存中找是不是有“hello”这个对象,如果有,就让str指向那个“hello”。
如果内存里没有"hello",就创建一个新的对象保存"hello”.String str=new String(“hello")就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。
深入理解synchronized底层原理,一篇文章就够了! - 腾讯云开发者社区-腾讯云
Java 并发高频面试题:聊聊你对 AQS 的理解? - 知乎
JAVA CAS实现原理与使用_装满哈希的Map的博客-CSDN博客_cas java
JVM的垃圾回收机制——垃圾回收算法 - 知乎
Redis中hash、set、zset的底层数据结构原理 - 腾讯云开发者社区-腾讯云
Java static静态修饰符详解:静态变量、静态方法及静态代码块
Java线程的6种状态及切换(透彻讲解)_潘建南的博客-CSDN博客_线程状态
谈谈对ThreadLocal的理解_woaichihanbao的博客-CSDN博客
Spring Boot常用注解(绝对经典)_哪 吒的博客-CSDN博客_springboot常用注解
面试题:Spring Bean的生命周期_ZhSuZhan的博客-CSDN博客_springbean的生命周期面试
5分钟搞清楚Synchronized和Lock的概念与区别 - 腾讯云开发者社区-腾讯云
深入理解Volatile关键字及其实现原理_平静的起步吧的博客-CSDN博客_volatile关键字原理
【Redis破障之路】三:Redis单线程架构_三分恶的博客-CSDN博客
Redis 是如何实现高可用的?_Andrew.J的博客-CSDN博客
final关键字用法总结_老周聊架构的博客-CSDN博客_final关键字修饰方法
1.方法重写(Override)
方法的重写主要体现在Java的继承体系中,子类出现了和父类一模一样的方法,方法重写也称为方法覆盖、方法复写。
方法重写的特点:
子类与父类的方法名和返回类型相同;
子类与父类方法的参数列表相同;
子类方法的访问范围不能比父类小;
返回值类型必须和被重写方法的类型相等或是其子类;
子类不能重写父类的构造方法,
2.方法重载(Overload)
在一个类中,有多个同名的方法,但是每个方法有不同的实现。
方法重载的特点:
方法名相同
方法的参数个数、参数类型不同
仅仅是返回值类型不同不能构成重载
构造方法可以重载
例:比如现在有一个eat 方法
//eat方法有两个参数
public void eat(Date time, String food){
}
//这种重载方式是可以的,参数的顺序不一致,也就认为是不同的参数
public void eat(String food, Date time){
}
//这个就不行了,会认为是同一个方法
public void eat(Date time1, String food1){
}
//仅仅是返回值类型不同是不能构成重载的,以下写法就会报错
int f(int a, int b){
return a + b;
}
boolean f(int a, int b){
return false;
}
为什么只有返回值类型不同不能构成重载?
在Java语言中,要重载一个方法,除了要与原方法具有相同的函数名称外,还要求要与原方法有一个不同的特征签名。特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,而返回值不会包含在所谓的特征签名之中,所以Java不允许只有返回值不同的重载。
为什么构造函数不能被重写?
首先我们要明白,重写的前提是要能被被继承。
构造函数的特点:
方法名和类名相等
无返回值类型
无返回值
根本无法满足函数重写条件。
另外假设可以重写父类构造器,那么子类名字就得和父类相等,显然这是不可能的,所以根本不可能重写父类构造方法。
方法的重写和重载是实现Java多态的方式,重载一般称为编译时多态,重写通过继承实现,一般称为运行时多态。
浅谈对java集合的理解_客驿风尘面的博客-CSDN博客
对JAVA的集合的理解 - huanshare - 博客园
一文看懂IO多路复用 - 知乎
【面试】彻底理解 IO多路复用_Hollis Chuang的博客-CSDN博客
面试官:MySQL索引为什么要用B+树实现?_Java识堂的博客-CSDN博客_mysql索引为什么使用b+树
Java8 新增了非常多的特性,我们主要讨论以下几个:
Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
Date Time API − 加强对日期与时间的处理。
Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
泛型使用与泛型擦除_情谊风月的博客-CSDN博客_泛型和泛型擦除
Java泛型中的类型擦除详解 - 知乎
线程间的通信方式_LallanaLee的博客-CSDN博客_线程间的通信方式
juc简单理解_bluesmi的博客-CSDN博客_juc的了解
HashMap和Hashtable的区别_xuhuaabc的博客-CSDN博客_hashmap和hashtable
HashMap 和 Hashtable 有什么区别?_ConstXiong的博客-CSDN博客_hashmap和hashtable有什么区别
面试官:HashMap是线程安全的吗?有什么替代方案? - 知乎
hashmap是线程安全的吗?怎么解决?_【16期】你能谈谈HashMap怎样解决hash冲突吗_weixin_39681161的博客-CSDN博客
Java的四种引用方式_林老师带你学编程的博客-CSDN博客_java引用
G1垃圾回收器详解 - 简书
G1垃圾回收器 - 知乎
面试题:什么是内存泄漏?内存溢出? - 知乎
面试题:什么是内存泄漏?内存溢出? - 知乎
内存溢出怎么解决 内存溢出和内存泄露有哪些区别【详细介绍】-太平洋IT百科
数据库常用的几种引擎,区别和比较_-Aurora的博客-CSDN博客_数据库引擎区别
MySQL有哪些数据库存储引擎,有什么区别?_永学者的博客-CSDN博客_mysql的存储引擎有哪些,区别是什么
Spring简介、框架核心、优缺点、应用场景_ThinkWon的博客-CSDN博客_spring框架的优点
MyBatis|缓存机制 - 简书
Mybatis缓存机制详解 - 知乎
java中String与new String的区别_成长的小牛233的博客-CSDN博客_java new string
面试官:重写 equals 时为什么一定要重写 hashCode? - 腾讯云开发者社区-腾讯云
线程创建的四种方式_愿好的博客-CSDN博客_创建线程的几种方式
线程的四种创建方式 - 知乎
Java高频面试题:谈谈你对ArrayList的理解? - 知乎
说说BIO、NIO、AIO 的区别?_学一次的博客-CSDN博客_bio、nio和aio的区别
SpringBoot:简述SpringBoot和Spring的区别_王小二(海阔天空)的博客-CSDN博客_springboot和spring
面试官:小伙子,给我说一下Spring 和 Spring Boot 的区别吧 - 知乎
Spring Boot 自动装配原理与实现 - 知乎
@Autowired和@Resource注解的区别和联系(十分详细,不看后悔)_莫小兮丶的博客-CSDN博客_autowired和resource注解的区别
说说Redis的同步机制?_ConstXiong的博客-CSDN博客_redis同步机制
深入理解redis——Redis的缓存过期淘汰策略 - SegmentFault 思否
JVM基础(五)垃圾收集器 - 知乎
Java的特点和优点_学习者666的博客-CSDN博客_java的优点
深入理解Java包装类与自动拆装箱_有头发的代码匠的博客-CSDN博客
sleep() 和 wait() 有什么区别? - 清晨的第一抹阳光 - 博客园
Java中sleep()和wait()的区别 - 简书
每日一面——谈谈你对线程池的理解(上)_有理想的菜鸡的博客-CSDN博客
面试官 谈谈线程池的理解 - 知乎
关于Jvm知识看这一篇就够了 - 知乎
JAVA运行时数据区域 - fengbs - 博客园
CMS垃圾回收器详解_朱清震的博客-CSDN博客_cms垃圾回收器
JVM双亲委派机制详解 - 知乎
数据库底层设计(MYSQL索引的数据结构) - 知乎
MySQL索引底层数据结构及原理深入分析_问北的博客-CSDN博客_mysql底层数据结构是什么
SpringBoot-启动流程 - 楠予 - 博客园
SpringMVC的执行流程以及运行原理__springmvc原理
面试题:Mybatis中 $ 和 # 的区别?_>no problem<的博客-CSDN博客
Java基础数据类型和引用类型的区别 - 腾讯云开发者社区-腾讯云
Java的异常处理机制_BoSeal的博客-CSDN博客_java异常处理机制
面向对象思想--谈谈你对面向对象的理解_一海星辰_1997的博客-CSDN博客_怎么理解面向对象
访问修饰符(总结完整)_Laity(俗人)!的博客-CSDN博客
Java 中的各种锁及其原理_Pan's pidr的博客-CSDN博客_java锁的原理
List和Set的区别_Star_Gemini的博客-CSDN博客_list set
Java NIO?看这一篇就够了!_归海一刀之渔舟唱晚的博客-CSDN博客_nio
GC 可达性分析算法_谭谈谈的博客-CSDN博客_gc可达性分析
类的加载和实例化过程_doforfuturedx的博客-CSDN博客_类实例化的过程
B+树和B树的区别 - 简书
Mysql主从同步的实现原理_OkidoGreen的博客-CSDN博客_mysql主从同步原理
数据库ACID_hengliang_的博客-CSDN博客_数据库acid
为什么mysql索引用B+树而不用哈希表?_JackMa_的博客-CSDN博客
InnoDB的MVCC实现原理(InnoDB如何实现MVCC以及MVCC的工作机制)_生而知之者为妖的博客-CSDN博客_innodb mvcc
Spring Boot起步依赖和自动配置_sscout的博客-CSDN博客_springboot起步依赖与自动配置
Spring事务管理详解_GrayHJX的博客-CSDN博客_spring事务管理
Spring bean 的5个作用域 Spring Bean的默认作用域为:singleton。它相比其他作用域的优点是系统开销小,Bean实例一旦创建成功便可重复使用。 1.singleton作用域_门里有什么的博客-CSDN博客_spring默认作用域
BeanFactory和FactoryBean的区别_wangbiao007的博客-CSDN博客_beanfactory和factorybean
Redis 简介 | 菜鸟教程