1、反射的了解
就是在运行时才知道要操作的类是什么, 是一种允许程序在运行时动态地获取类的信息并操控类对象的机制
2、字符串中常用的方法
-
length()
:返回字符串的长度。javaString s = "hello"; int len = s.length(); // 5
-
charAt(int index)
:返回指定位置的字符。javachar c = s.charAt(1); // 'e'
-
substring(int beginIndex, int endIndex)
:返回子字符串。javaString sub = s.substring(1, 4); // "ell"
-
indexOf(String str)
:返回指定子字符串首次出现的位置。javaint index = s.indexOf("l"); // 2
-
toLowerCase()
:将字符串转换为小写。javaString lower = s.toLowerCase(); // "hello"
-
toUpperCase()
:将字符串转换为大写。javaString upper = s.toUpperCase(); // "HELLO"
-
trim()
:去除字符串两端的空白字符。javaString trimmed = " hello ".trim(); // "hello"
-
replace(CharSequence target, CharSequence replacement)
:替换字符串中的指定字符或子字符串。javaString replaced = s.replace("l", "x"); // "hexxo"
-
split(String regex)
:将字符串按指定正则表达式分割成字符串数组。javaString[] parts = "a,b,c".split(","); // ["a", "b", "c"]
-
concat(String str)
:连接两个字符串。String result = s.concat(" world")
3、StringBuffer和StringBuilder的区别
StringBuffer
是线程安全的,方法是同步的,因此在多线程环境中更安全,但性能较低;
StringBuilder
是非线程安全的,方法不是同步的,因此在单线程环境中性能更好。
4、重写和重载的区别
重写(Override)
- 定义:子类重新定义父类中已经存在的方法,以便子类可以提供其特定的实现。
- 目的:用于实现多态,使得子类可以对父类的方法进行自定义实现。
- 方法名、返回类型、参数列表必须与父类中的方法完全一致。
- 方法的访问修饰符不能比父类中的方法更严格(例如,父类中的方法是
protected
,子类中的重写方法不能是private
)。 - 方法可以抛出与父类方法相同或更少的异常(即,子类方法抛出的异常不能比父类方法更多或更具限制性)。
重载(Overload)
- 定义:在同一个类中定义多个方法,方法名相同但参数列表不同。
- 目的:允许同一个方法名用于不同的参数,使得方法可以适应不同的输入。
- 方法名相同,但参数列表(类型、数量、顺序)不同。
- 返回类型可以相同或不同,但不能仅通过返回类型来进行重载。
- 访问修饰符可以不同。
- 方法可以抛出不同的异常。
5、final关键字作用
用于定义常量、禁止方法重写、禁止类继承。
6、ArrayList和LinkedList的区别
ArrayList
基于数组,随机访问快;LinkedList
基于链表,插入删除操作快。
7、多态的理解
多态主要通过以下两种方式实现:
-
方法重写(Override):在子类中重新定义父类的方法。方法的调用由对象的实际类型决定,运行时才确定调用哪个方法的实现。
示例:
javaclass Animal { void makeSound() { System.out.println("Some sound"); } } class Dog extends Animal { @Override void makeSound() { System.out.println("Bark"); } } class Cat extends Animal { @Override void makeSound() { System.out.println("Meow"); } } public class Test { public static void main(String[] args) { Animal a; a = new Dog(); a.makeSound(); // Output: Bark a = new Cat(); a.makeSound(); // Output: Meow } }
-
方法重载(Overload):同一方法名,但参数列表不同。虽然这是方法的一个变种,重载本身并不算是多态的核心,但在某些情况下也会涉及到多态的应用。
2. 多态的类型
- 编译时多态(静态多态):通过方法重载实现,编译器在编译时就确定了方法的调用。
- 运行时多态(动态多态):通过方法重写实现,程序在运行时决定调用哪个方法。通常表现为:
- 父类引用指向子类对象。
- 调用的方法由对象的实际类型决定,而不是引用的类型
它允许对象以多种形式表现,增强了程序的灵活性和可维护性。通过方法重写和接口实现,多态使得不同的对象可以使用相同的方法调用,并表现出不同的行为。
8、设计模式
设计模式主要分为三类,每类包含多个具体模式,作用是提高代码的复用性、灵活性和可维护性。以下是常见的设计模式及其作用:
1. 创建型模式
- 单例模式(Singleton):确保一个类只有一个实例,并提供全局访问点。
- 工厂方法模式(Factory Method):定义一个接口用于创建对象,但由子类决定实例化哪个类。
- 抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 建造者模式(Builder):分步骤构建一个复杂对象,允许不同的表示方法。
- 原型模式(Prototype):通过复制现有对象来创建新对象,而不是通过实例化。
2. 结构型模式
- 适配器模式(Adapter):将一个类的接口转换成客户希望的另一种接口。
- 装饰器模式(Decorator):动态地给对象添加额外的职责。
- 外观模式(Facade):为子系统中的一组接口提供一个一致的接口。
- 组合模式(Composite):将对象组合成树形结构以表示部分-整体层次结构。
- 代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。
3. 行为型模式
- 观察者模式(Observer):定义对象之间的一对多依赖,使得当一个对象改变状态时,其所有依赖者都会收到通知并自动更新。
- 策略模式(Strategy):定义一系列算法,将每一个算法封装起来,使它们可以互换。
- 迭代器模式(Iterator):提供一种方法顺序访问一个集合对象中的元素,而不暴露该对象的内部表示。
- 中介者模式(Mediator):定义一个对象来封装一组对象的交互,使得对象之间不需要显式地互相引用。
- 备忘录模式(Memento):在不暴露对象实现细节的情况下,捕获对象的内部状态,并在以后恢复它。
- 解释器模式(Interpreter):为语言定义一个表示,并定义一个解释器来解释该语言的句子。
9、Web开发中转发和重定向有什么区别
转发在服务器内部发生,重定向是浏览器发起新请求;转发不会改变URL,重定向会。
10、cookie和session有什么区别
cookie
存储在客户端,session
存储在服务器;session
比cookie
安全,但不适合存储大量数据。
11、过滤器和拦截器的区别
过滤器用于处理请求和响应,拦截器用于处理业务逻辑;过滤器在请求前后执行,拦截器在方法调用前后执行。
12、Ajax的理解
定义
Asynchronous JavaScript And Xml的缩写
浏览器在不刷新全部页面的情况下,和服务端进行通信的过程
Ajax就是实现局部刷新的异步通信技术
工作原理
通过XmlHttpRequest对象和服务端进行通信,浏览器收到服务端的返回值后,对局部区域的内容进行设置
13、MVC架子模型 特点
-
Model(模型):负责应用程序的数据和业务逻辑。它直接与数据源交互(如数据库),并处理数据的获取和存储。
-
View(视图):负责数据的呈现和用户界面的展示。它从模型获取数据并将其展示给用户。
-
Controller(控制器):充当模型和视图之间的中介,处理用户输入并更新模型和视图。控制器接收用户请求,调用模型进行处理,然后选择视图进行呈现。
特点:提高代码的可维护性 增强代码的可重用性 提高用户界面的灵活性 支持多种视图
14、Mybatis中一级缓存和二级缓存
1. 一级缓存(Local Cache)
- 定义:一级缓存是 MyBatis 的默认缓存机制,它是基于
SqlSession
的缓存,也被称为会话缓存。 - 作用域:缓存的作用域限于一个
SqlSession
对象。换句话说,一级缓存仅在同一个SqlSession
的生命周期内有效。不同的SqlSession
之间是不会共享一级缓存的。 - 特点:
- 当一个
SqlSession
查询同样的 SQL 语句时,MyBatis 会首先检查一级缓存,如果缓存中有数据,则直接返回缓存数据而不执行数据库查询。 - 一级缓存的数据会在
SqlSession
关闭时被清空。 - 插入、更新或删除操作会清空一级缓存,以确保数据的一致性。
- 当一个
- 配置:一级缓存是 MyBatis 的默认行为,不需要额外的配置。要使用一级缓存,只需正常使用
SqlSession
。
2. 二级缓存(Global Cache)
- 定义:二级缓存是 MyBatis 提供的全局缓存机制,它是基于
Mapper
的缓存,也被称为全局缓存。 - 作用域:二级缓存的作用域超出了单个
SqlSession
,在整个Mapper
的作用域内共享。即,二级缓存可以跨SqlSession
实例共享缓存数据。 - 特点:
- 当一个
SqlSession
关闭时,数据会被存入二级缓存。其他SqlSession
可以共享这些缓存数据,从而避免对数据库的重复查询。 - 二级缓存的数据会在
SqlSession
关闭时依然保持,直到缓存被刷新或被清空。 - 插入、更新或删除操作会使相关的数据从二级缓存中失效。
- 当一个
- 配置:
- 启用二级缓存:需要在 MyBatis 的配置文件中启用二级缓存。具体来说,需要在
mapper
XML 文件中添加<cache/>
元素。 - 配置缓存:可以在
mybatis-config.xml
文件中进行全局的缓存配置,或者在每个Mapper
的 XML 文件中单独配置缓存。 - 缓存实现:默认的缓存实现是基于
HashMap
的简单缓存实现。也可以自定义缓存实现,或者使用第三方缓存框架(如 Ehcache、Redis 等)。
- 启用二级缓存:需要在 MyBatis 的配置文件中启用二级缓存。具体来说,需要在
15、Comparable和Comparator 接口的区别?
16、Redis 是一个开源的内存数据存储系统,因其高性能和快速响应而被广泛使用。其速度快的原因主要包括以下几个方面:
1. 内存存储
Redis 将数据存储在内存中,而不是传统的磁盘存储。内存访问速度比磁盘快得多,减少了数据的读写延迟。虽然 Redis 也可以将数据持久化到磁盘,但其主要的读写操作都是在内存中完成的。
2. 单线程模型
Redis 使用单线程事件循环模型处理所有请求。单线程模型的优点包括:
- 避免了上下文切换:多线程模型中线程上下文切换会带来额外的开销。Redis 的单线程模型通过避免这种切换来减少延迟。
- 简化了并发编程:单线程避免了多线程编程中的复杂性,如竞态条件和锁问题,从而减少了调试和性能调优的难度。
3. 高效的数据结构
Redis 提供了多种高效的数据结构,如字符串、列表、集合、有序集合和哈希等。每种数据结构都有经过优化的操作方法,使得常见的数据操作(如插入、删除、查找)可以在常量时间(O(1))或对数时间(O(log n))内完成。
4. 事件驱动和非阻塞I/O
Redis 使用了事件驱动的非阻塞 I/O 模型,通过 epoll
(在 Linux 上)或 kqueue
(在 BSD 系统上)等机制,能够处理大量并发连接而不会阻塞。这种方式允许 Redis 在高并发环境中高效地处理大量请求。
17、Redis采用的过期策略
Redis采用的过期策略是惰性删除+定期删除同时使用
惰性删除:当尝试访问一个过期的键时,Redis 会检查该键是否已经过期。如果是,Redis 会将其从数据库中删除并返回一个空值 这种策略即为惰性删除,它的优点是避免了不必要的计算开销,但缺点是可能会导致一些过期键在访问时才被删除,增加了访问延迟。
定期删除:Redis 会定期扫描数据库中的过期键。默认情况下,Redis 每隔 100 毫秒会随机检查一部分键的过期情况,并删除那些过期的键。
当 Redis 达到内存限制时,除了过期键的删除策略,Redis 还提供了内存淘汰策略,以保证 Redis 继续正常运行
Redis 通过设置键的过期时间和应用多种过期策略来管理数据的生命周期。这些策略帮助 Redis 处理过期数据、优化内存使用,并确保系统高效稳定运行
18、@Bean
注解在 Spring 框架中用于定义一个 Spring 容器管理的 Bean。它通常用于以下场景:
-
配置类中定义 Bean:在
@Configuration
注解的类中使用@Bean
注解来显式定义和配置 Bean。这允许你在 Java 代码中控制 Bean 的创建和配置,而不是通过 XML 配置文件。 -
替代 XML 配置:使用
@Bean
可以代替传统的 XML 配置方式,使配置更具可读性和可维护性,同时也支持 Java 编程模型。 -
依赖注入:定义的 Bean 可以通过依赖注入注入到其他组件中,确保 Spring 容器能够管理它们的生命周期和依赖关系。
-
创建第三方库的 Bean:对于没有使用
@Component
注解的第三方类或不方便修改的类,使用@Bean
可以在配置类中创建和配置这些 Bean。 -
条件创建 Bean:结合
@Conditional
注解,@Bean
可以用于在特定条件下创建 Bean,这在配置多环境或条件 Bean 时特别有用。19、使用 Redis 存储数据可以应对高并发负载和提供快速访问速度。以下是一些优化 Redis 存储的策略:
-
数据分片:利用 Redis 集群功能,将数据分片到不同的节点上,以提高存储容量和并发处理能力。
-
内存管理:配置适当的内存策略和淘汰机制(如 LRU、LFU)以避免内存溢出,并确保 Redis 持续高效运行。
-
持久化设置:根据需要配置 RDB 快照和 AOF 日志,以在 Redis 重启时恢复数据,但要权衡持久化开销和性能。
-
缓存策略:使用 Redis 作为缓存层来减少数据库的读负载。可以设置适当的过期时间和缓存失效策略。
-
异步操作:对于写入操作,可以使用 Redis 的异步写入功能,以提高写入性能并减少阻塞。
-
监控与调优:监控 Redis 性能指标,如内存使用、命令执行时间等,进行定期调优以维持最佳性能。
-
数据结构优化:选择适合的数据结构(如哈希表、集合、列表等)来存储数据,优化存储和操作效率。
20、基于Redis的分布式锁
- 原理:使用Redis的原子操作(如SETNX)来实现分布式锁。常见的实现方式是使用Redis的
SET key value NX PX expire
命令,这个命令会在键不存在时设置键的值,并设置键的过期时间,从而实现锁的获取。 - 优点:性能高,支持高并发。Redis的
SETNX
命令原子性保证了锁的唯一性。 - 缺点:需要处理网络分区和Redis故障带来的锁失效问题。常见的解决方案包括使用
Redisson
库提供的高级锁功能,或在锁失效时使用Lua脚本等方式确保锁的正确释放。
-
21、看门狗机制工作原理:
- 锁获取: 当一个进程或线程希望获取分布式锁时,它会尝试在分布式锁服务中申请锁。如果成功获得锁,它会得到一个锁的标识符(ID)和一个锁的过期时间(TTL, Time-To-Live)。
- 启动看门狗: 获取锁的进程或线程会启动一个看门狗线程或定时任务,这个看门狗线程会周期性地向锁服务发送续期请求,以延长锁的有效期。
- 续期操作: 看门狗线程会定期更新锁的过期时间,以确保锁不会因为超时而自动释放。如果看门狗线程无法按时进行续期操作,分布式锁服务会认为持有者可能已经失效,进而释放锁。
- 锁释放: 当锁持有者完成对共享资源的操作后,它会主动释放锁,或者在看门狗机制无法再续期时,锁会在过期后自动释放,允许其他进程或线程获取锁。
通过Timeout,每10s钟,内部执行lua脚本,判断是否存在指定的key和filed,如果存在,说明业务还没有执行完,将锁的超时时间设置为30s
如下方法中,如果第二个参数设置为0,内部默认使用30s的过期时间,而且看门狗机制会生效
lock.tryLock(0, 0, TimeUnit.SECONDS)
private void renewExpiration() {
RedissonBaseLock.ExpirationEntry ee = (RedissonBaseLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());
if (ee != null) {
Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
public void run(Timeout timeout) throws Exception {
RedissonBaseLock.ExpirationEntry ent = (RedissonBaseLock.ExpirationEntry)RedissonBaseLock.EXPIRATION_RENEWAL_MAP.get(RedissonBaseLock.this.getEntryName());
if (ent != null) {
Long threadId = ent.getFirstThreadId();
if (threadId != null) {
CompletionStage<Boolean> future = RedissonBaseLock.this.renewExpirationAsync(threadId);
future.whenComplete((res, e) -> {
if (e != null) {
RedissonBaseLock.log.error("Can't update lock " + RedissonBaseLock.this.getRawName() + " expiration", e);
RedissonBaseLock.EXPIRATION_RENEWAL_MAP.remove(RedissonBaseLock.this.getEntryName());
} else {
if (res) {
RedissonBaseLock.this.renewExpiration();
} else {
RedissonBaseLock.this.cancelExpirationRenewal((Long)null);
}
}
});
}
}
}
}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
}
RedissonBaseLock.this.cancelExpirationRenewal((Long)null);
}
}
});
}
}
}
}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
}
##