java基础
1. Java 面向对象的三大特性及理解
默认情况下,面向对象有三大特性:继承、封装、多态。但如果考官让回答4大特性,我们就把抽象加上去。
1、继承
继承就是从已有的类继承信息创建新类的过程,被继承的类称为父类(也叫基类、超类),继承的类叫做子类(也叫派生类)。子类可以全盘接受父类的所有属性和方法(甚至是private修饰的,也可以继承,但是不能在父类之外访问,提供共有的访问方法(比如封装set()、get())就可以用)。
2、封装
封装就是把数据和操作数据的方法绑定起来,对外提供简单的接口,在java中对类中的方法的定义就是对细节的一种封装,简单地说,封装就是隐藏一切可以隐藏的内容,对外提供一个简单的接口。
3、多态
多态就是指不同子类型对于同一消息做出不同的反应,简单地说,就是对于同一对象,调用相同的方法,但做了不同的事情,多态具体体现在方法重载和方法重写,方法重载实现的是编译时的多态,而方法重写实现的是运行时的多态,需要注意的是当发生向上转型时,子类独有的方法不可以被调用。
4、抽象
抽象就是把不同的对象相同的特征总结出来提取成一个类,抽象只关注对象有哪些的属性和方法,,从不关心这些方法具体怎么实现。
2.什么是面向对象?什么是面向过程?
面向过程(Procedure Oriented 简称 PO):把事情拆分成几个步骤(相当于拆分成一个个的方法和数据),然后按照一定的顺序执行。
面向对象(Object Oriented 简称 OO):面向对象会把事物抽象成对象的概念,先抽象出对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法。
举例:用洗衣机洗衣服,来看一下两者的差别。
面向过程:
放衣服(方法)–>加洗衣粉(方法)–> 加水(方法)–> 清洗(方法)–> 甩干(方法)
面向对象:
new 出两个对象 ”人“ 和 ”洗衣机“
”人“ 加入属性和方法:放衣服(方法)、加洗衣粉(方法)、加水(方法)
”洗衣机“ 加入属性和方法:清洗(方法)、甩干(方法)
然后执行:
人.放衣服(方法)-> 人.加洗衣粉(方法)-> 人.加水(方法)-> 洗衣机.清洗(方法)-> 洗衣机.甩干(方法)
2. 面向对象和面向过程的优缺点对比
面向过程:
优点:
- 效率高,因为不需要实例化对象。
缺点:
- 代码维护和扩展相对困难,特别是在大型系统中。
- 数据和行为分离,修改数据结构可能影响多个函数。
面向对象:
优点:
- 代码维护和扩展相对容易,通过继承和多态实现扩展。
- 封装和模块化设计使得系统更易维护和扩展。
缺点:效率比面向过程低。
3. Java为什么要有类
1. 面向对象编程的核心
类是面向对象编程(OOP)的基本构建模块。面向对象编程是一种通过将数据和行为封装在对象中的编程范式。类提供了一种定义和创建对象的模板,使得面向对象编程成为可能。
2. 封装数据和行为
类允许将数据(字段)和行为(方法)封装在一起。这种封装有助于组织代码,增强代码的可维护性和可读性,并保护数据不被外部直接访问或修改。
3. 实现抽象
类允许创建抽象的数据类型,通过隐藏实现细节,只暴露必要的接口给外部使用者。这种抽象能力使得复杂系统可以分解为更简单、更可管理的部分。
4. 继承和代码复用
类支持继承,可以从现有的类派生出新的类,从而实现代码的复用和扩展。子类继承父类的属性和方法,可以重用现有代码并添加新的功能。
5. 多态性
类支持多态性,这使得相同接口的不同实现可以以统一的方式使用。多态性通过方法重写和接口实现实现,允许代码更加灵活和可扩展。
6. 模块化和组织代码
类提供了一种自然的方式来组织和模块化代码。在大型项目中,使用类可以将相关的功能、数据和行为集中在一个模块中,使代码更易于理解、管理和维护。
4. java如何创建一个线程?
- 通过继承
Thread
类:
- 创建一个新的类继承
Thread
类。 - 重写
run
方法,在run
方法中定义线程要执行的任务。 - 创建该类的实例并调用
start
方法启动线程。
1 |
|
- 通过实现
Runnable
接口:
- 创建一个实现
Runnable
接口的类。 - 实现
run
方法,在run
方法中定义线程要执行的任务。 - 创建一个
Thread
对象,并将实现Runnable
接口的实例作为参数传递给Thread
构造函数。 - 调用
Thread
对象的start
方法启动线程。
1 |
|
- **通过实现
Callable
接口并使用FutureTask
**:
通过使用 Callable
和 FutureTask
,你可以创建一个带返回结果的线程,并在需要时获取其结果。这种方式非常适合用于需要并行执行并获取结果的任务。
1 |
|
- 通过使用
ExecutorService
框架:
- 使用
Executors
创建一个线程池。 - 提交实现
Runnable
或Callable
接口的任务给线程池来执行。
1 |
|
5. Spring如何创建一个线程
- 使用
@Async
注解
@Async
注解用于将某个方法标记为异步执行。要使用它,你需要启用Spring的异步支持。
1 |
|
- 使用
TaskExecutor
TaskExecutor
是Spring提供的一组接口,用于抽象并发编程的细节。你可以使用 ThreadPoolTaskExecutor
来创建一个线程池。
1 |
|
在服务类中注入 TaskExecutor
并使用它执行任务:
1 |
|
6. Java8引入stream有什么意义?
1. 更简洁的代码
- 使用流API,可以用更少的代码完成对集合的操作。例如,对集合进行过滤、转换、排序和聚合等操作可以通过链式调用实现,使代码更加简洁和易读。
2. 并行处理能力
- 流API支持轻松的并行操作,只需将
stream()
方法更换为parallelStream()
,就可以利用多核处理器的能力来加速数据处理。这对于处理大型数据集合尤为重要。
3. 惰性求值
Stream
API 采用惰性求值策略,即只有在最终需要结果时才会执行中间操作。此外,一些终端操作(如findFirst()
或anyMatch()
)具有短路特性,这意味着一旦找到符合条件的第一个元素,后续的操作就会停止,从而节省计算资源。
4. 易于组合和重用
- 由于
Stream
操作是高度模块化的,你可以轻松地将不同的操作组合在一起形成复杂的处理流程,也可以方便地复用这些操作。
7 @Transactional 常用的一些配置?
propagation(传播行为)
定义事务在遇到已有事务时的行为。
常用值
REQUIRED
(默认):如果当前存在事务,则加入该事务;否则创建一个新的事务。REQUIRES_NEW
:总是创建一个新的事务,如果当前存在事务,则挂起它。如:在日志的save时,即使createUser
方法失败并导致事务回滚,日志记录操作仍然会被提交。1
2
3
4
5
6
7
8
9
10
11@Transactional
public void createUserWithLogging(User user) {
userService.createUser(user);
loggingService.logMessage("User created: " + user.getName());
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logMessage(String message) {
logRepository.save(new Log(message));
}SUPPORTS
:如果当前存在事务,则加入该事务;否则以非事务方式运行。NOT_SUPPORTED
:以非事务方式运行,如果当前存在事务,则挂起它。MANDATORY
:如果当前存在事务,则加入该事务;否则抛出异常。NEVER
:以非事务方式运行,如果当前存在事务,则抛出异常。NESTED
:如果当前存在事务,则在嵌套事务内执行;否则与REQUIRED
行为相同。
rollbackFor(回滚异常)
指定哪些异常会导致事务回滚,默认情况下只有
RuntimeException
和Error
会触发回滚。1
@Transactional(rollbackFor = {SQLException.class, IOException.class})
timeout(超时时间)
定义事务的最大持续时间(秒),超过该时间将抛出异常并回滚事务。当某个事务可能长时间运行时,可以通过设置超时时间来避免长时间占用资源。
1
@Transactional(timeout = 30)
isolation(隔离级别)
定义事务的隔离级别,控制事务之间的可见性。
常用值
DEFAULT
(默认):使用底层数据库的默认隔离级别。READ_UNCOMMITTED
:允许读取未提交的数据更改,可能导致脏读、不可重复读和幻读。READ_COMMITTED
:允许从已经提交的事务中读取数据,防止脏读。REPEATABLE_READ
:对同一字段的多次读取结果一致,除非数据被当前事务本身改变,可以防止脏读和不可重复读。SERIALIZABLE
:完全串行化,最高级别的隔离,防止所有并发问题,但性能较低。
readOnly(只读标志)
如果设置为
true
,表示该事务是只读的,优化器可能会进行一些优化。这种方式适用于查询操作,避免不必要的锁和写操作。1
@Transactional(readOnly = true)