面试准备
- java面向对象的基本特征
- 重写
- 异常
- 泛型
- append的实现
- hashmap
- List<> 、ArrayList<>
- java线程调度线程调度
- JVM
- IOC
- spring自动装配
- 数据库
- 自我介绍
面试官您好:模板改写
面试总结
自我介绍+项目经历+反问面试官(公司常用的技术栈+项目新成立的吗)
java面向对象的基本特征
- 封装:明确表示出可供外部成员使用的成员和函数
- 继承:从父类继承,属性和基本方法(子类不能继承父类的私有属性,但是如果子类中公有的方法影响到了父类私有属性,那么私有属性是能够被子类使用的)
- 多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同(List list=new ArrayList();)
重写
- 子类重写父类的方法,只有实例方法可以重写,重写后的方法仍为实例方法。成员变量和静态方法都不能被重写,只能被隐藏。
方法的重写(override)两同两小一大原则:
- 方法名相同,参数类型相同
- 子类返回类型小于等于父类方法返回类型
- 子类抛出异常小于等于父类方法抛出异常
- 子类访问权限大于等于父类方法访问权限
异常
java中有error和Exception ,都是继承自Throwable类
对比:
- error不可控制,exception可以被控制
- error是程序报错,而exception可以在程序应用级别被处理(
@ExceptionHandler
)
泛型
泛型可以使类型(类和接口)在定义类、接口和方法时进行参数化
在使用泛型相比于直接使用 Object
有以下几个好处:
- 强制的类型检查:Java 编译器会对泛型代码进行强制类型检查,如果违反类型安全则会抛出错误。在编译阶段解决类型错误,能更有效的减少 Bug
- 消除类型强制转换:如果不使用泛型,则在进行代码编写是需要手动进行类型转换
泛型类型
/**
* Generic version of the Box class.
* @param <T> the type of the value being boxed
*/
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
如上面代码所示,所有 Object
都被替换为了 T
,T
是一个可以代表除基本类型外的所有类型:任意类、任意接口、任意数组类型甚至还可以是其他的类型参数(eg: List<T>
)
append的实现
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
append
会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展,实际使用的长度用count
体现。具体来说,ensureCapacityInternal(count+len)
会确保数组的长度足以容纳新添加的字符,str.getChars
会拷贝新添加的字符到字符数组中,count+=len
会增加实际使用的长度。
java代理
静态代理:因为需要对一些函数进行二次处理,或是某些函数不让外界知道时,可以使用代理模式,通过访问第三方,间接访问原函数的方式
动态代理:当动态生成的代理类调用方法时,会触发 invoke
方法,在 invoke
方法中可以对被代理类的方法进行增强。
hashmap
哈希表:根据关键字节进行数据访问的结构
构造哈希函数:(除留余数)
处理冲突的方法:
hi(X)=(Hash(X)+i)mod TableSize
- 线性探测法- (i取值从0到表大小减去1)
- 平方探测法:i的取值为1到k正负平方 ,其中 4k+3=TableSize
- 再散列法:使用两个哈希函数
拉链法:把所有同义词存储在一个线性链表中,有散列地址唯一标识
List<> 、ArrayList<>
List
List是集合最大的父类,它包含了ArrayList。
如果直接声明为ArrayList
而声明成:List
比如要用链表存数据的话直接用LinkedList,使用ArrayList或者Vector直接通过list = new LinkedList
很多需求只能用一个list,内存有限,或者线程同步,不能有更多的集合对象,使得List总的接口来管理对象
java线程
Java线程模型是基于操作系统原生线程模型实现的,实现线程有三种方式:内核线程实现、用户线程实现、混合线程实现。
java线程调度线程调度
线程调度分为协同式和抢占式。
协同式调度
:线程的执行时间由线程自己控制,这种的实现很简单,但是很可能造成很严重的后果。抢占式调度
:由操作系统分配线程执行的时间,线程切换的决定权在操作系统。
有时候我们需要为某些线程多分配时间,这时我们就需要用到线程优先级的方法,Java提供了10种优先级。Java优先级是在操作系统的原生线程优先级上实现的,所以对于同一个优先级,不同的操作系统可能有不同的表现,也就是说 Java线程优先级不是可靠的
Java线程状态切换
Java线程模型定义了 6 种状态,在任意一个时间点,一个线程有且只有其中一个状态:
新建(New)
:新建的Thread,尚未开始。运行(Runable)
:包含操作系统线程状态中的Running、Ready,也就是处于正在执行或正在等待CPU分配时间的状态。无限期等待(Waiting)
:处于这种状态的线程不会被分配CPU时间,等待其他线程唤醒。限期等待(Timed Waiting)
:处于这种状态的线程不会被分配CPU时间,在一定时间后会由系统自动唤醒。阻塞(Blocked)
:在等待获得排他锁。结束(Terminated)
:已终止的线程
JVM
JVM 的基本架构如上图所示,其主要包含三个大块:
- 类加载器:负责动态加载Java类到Java虚拟机的内存空间中。
- 运行时数据区:存储 JVM 运行时所有数据
- 执行引擎:提供 JVM 在不同平台的运行能力
垃圾回收
对象存活检测
Java堆中存放着大量的Java对象实例,在垃圾收集器回收内存前,第一件事情就是确定哪些对象是活着的,哪些是可以回收的
IOC
Ioc—Inversion of Control
,即“控制反转”,不是什么技术,而是一种设计思想。在Java 开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
- 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
- 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
IoC能做什么
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
IoC和DI
DI—Dependency Injection
,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
- 谁依赖于谁:当然是应用程序依赖于IoC容器;
- 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
- 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
- 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
spring自动装配
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的
META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot
我们先看一下 SpringBoot 的核心注解 SpringBootApplication
.
大概可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:
@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制- 自动配置包
- 自动配置注册包
- 自动配置包注册
@Configuration
:允许在上下文中注册额外的 bean 或导入其他配置类@ComponentScan
: 扫描被@Component
(@Service
,@Controller
)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。
总结:Spring Boot 通过@EnableAutoConfiguration
开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories
中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional
按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx
包实现起步依赖
数据库
数据库设计的阶段
- 需求分析 : 分析用户的需求,包括数据、功能和性能需求。
- 概念结构设计 : 主要采用 E-R 模型进行设计,包括画 E-R 图。
- 逻辑结构设计 : 通过将 E-R 图转换成表,实现从 E-R 模型到关系模型的转换。
- 物理结构设计 : 主要是为所设计的数据库选择合适的存储结构和存取路径。
- 数据库实施 : 包括编程、测试和试运行
- 数据库的运行和维护 : 系统的运行与数据库的日常维护。
事务
事务是作为单个逻辑工作单元执行的一系列操作。一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务。
- 原子性(Atomicity): 事务
要么全部完成,要么全部取消
。 如果事务崩溃,状态回到事务之前(事务回滚)。 - 隔离性(Isolation): 如果2个事务 T1 和 T2 同时运行,事务 T1 和 T2 最终的结果是相同的,不管 T1和T2谁先结束。
- 持久性(Durability): 一旦事务提交,不管发生什么(崩溃或者出错),数据要保存在数据库中。
- 一致性(Consistency): 只有合法的数据(依照关系约束和函数约束)才能写入数据
自我介绍
面试官您好:模板改写
面试问题(记得的)
- 自我介绍
- 谈谈spring aop
- java线程调度了解多少
- 线程池
- jvm 组成部分
- springboot自动装配
- sql左连接,右连接
- tcp、udp
- 非关系型数据库
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端