程序员の面试题
2021/7/10 17:12:09
本文主要是介绍程序员の面试题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
程序员の面试题
金三银四钻石七,将现如今会碰到的面试题 整理记录。
中间件
- tomcat
- Weblogic
- JBOSS
- Coldfusion
- Websphere
- GlassFish
SQL效率优化
1.建立索引
索引建立时应有以下事项:
- 应尽量避免在
WHERE
子句中对字段进行NULL
值判断; - 应尽量避免在
WHERE
子句中使用!=
或<>
操作符; - 应尽量避免在
WHERE
子句中使用OR
来连接条件; IN
和NOT IN
。<>
、NOT IN
、NOT EXIST
;非常重要!LIKE
中%
在左边;- 避免在
WHERE
条件中,在索引列上进行计算或使用函数,因为这将导致索引不被使用。
2.索引条件
- 频繁作为查询条件的字段应该创建索引;
- 查询中与其他表有关联的字段,例如外键关系;
- 频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件;
WHERE
条件里用不到的字段,不创建索引;- 高并发的情况下一般选择复合索引;
- 查询中排序的字段创建索引将大大提高排序的速度(索引就是排序加快速查找);
- 查询中统计或者分组的字段;
- 表记录太少的情况,不需要创建索引;
- 经常增删改的表不要建立索引;
3.SQL 注意事项
- 任何地方都不要进行全表检索,使用
SELECT * FROM Table;
- 尽量避免大事务操作,提高系统并发能力
- 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
JAVA日常使用
1.锁表
: SELECT * FROM products WHERE id='3' FOR UPDATE;
锁是什么?
锁有两种锁,即悲观锁和乐观锁;
悲观锁:对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
乐观锁:而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。
在
Java
中,synchronized
关键字和Lock
的实现类都是悲观锁。
Lock
和synchronize
两者区别:
- 首先
synchronized
是java
内置关键字,在jvm
层面,Lock
是个java类
; synchronized
无法判断是否获取锁的状态,Lock
可以判断是否获取到锁;synchronized
会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock
需在finally
中手工释放锁(unlock()
方法释放锁),否则容易造成线程死锁;- 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而
Lock
锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了; synchronized
的锁可重入、不可中断、非公平,而Lock
锁可重入、可判断、可公平(两者皆可)Lock
锁适合大量同步的代码的同步问题,synchronized
锁适合代码少量的同步问题。
2.线程
线程是进程的子集,一个进程可以拥有很多线程,每个线程并行执行不同的任务。不同的进程使用不同的内存空间,所有的线程共同享用相同的内存空间。别把线程和栈内存搞混,每个线程都拥有独立的栈内存来存储本地数据。
在 Java
中实现多线程有两种手段,一种是继承 Thread 类
,另一种就是实现 Runnable
接口
覆写run()
方法,作为线程 的操作主体 ,如果一个类继承 Thread
类,则不适合于多个线程共享资源,而实现了 Runnable
接口,就可以方便的实现资源的共享。
一个多线程的程序如果是通过 Runnable
接口实现的,则意味着类中的属性被多个线程共享,那么这样就会造成一种问题,如果这多个线程要操作同一个资源时就有可能出现资源同步问题。
为什么要使用线程?
- 耗时的操作使用线程,提高应用程序的相应速度;
- 并行操作时使用线程,如C/S架构的服务器并发线程响应的请求;
- 多CPU系统中,使用线程提高CPU利用率;
- 改善程序结构,一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或者半独立的运行部分,这样的程序会利于理解和修改。
// synchronized 代码使用方法 synchronized(同步对象){ 需要同步的代码 }
3.Java 特性
封装(Encapsulation) :
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
优点:
- 将变化隔离。
- 便于使用。
- 提高重用性。
- 提高安全性。
private关键字:
是构造方法,无返回值,可以有参数,与类同名,jvm
加载类时自动调用(变量,代码块,函数)
String类型:
String
的值是不可变的,这就导致每次对String
的操作都会生成新的String对象
,不仅效率低下,而且浪费大量优先的内存空间 StringBuffer
是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer
对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
不可变类: 只是它的实例不能被修改的类
可变类 | 不可变类 |
---|---|
速度更快 | 速度较慢 |
线程不安全 | 线程安全 |
单线程操作字符串 | 多线程操作字符串 |
HashTable
是较为远古的使用Hash算法的容器结构了,现在基本已被淘汰,单线程转为使用HashMap,多线程使用ConcurrentHashMap。
异常和错误
-
异常和错误同属于一个类:
Throwable
;
异常和错误的区别是:异常能被程序本身可以处理,错误是无法处理。 -
处理异常的三个部件:
try
,catch
,finally
-
输入输出异常:
IOException
, -
数组越界异常:
ArrayIndexOutOfBoundsException
, -
空指针异常:
NullPointerException
;
继承(inheritance):
继承是面向对象最显著的一个特性。 继承是从已有的类中派生出新的类, 新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
extends
。重写父类方法
super
关键字是一个特殊的变量, 它提供了对父类的方法。 可以用super
主动调用父类的构造方法、 访问父类中的成员。
多态(polymorphism)
在面向对象语言中, 多态性是指一个方法可以有多种实现版本,即“一种定义, 多种实现”。 利用多态可以设计和实现可扩展的系统, 只要新类也在继承层次中。 新的类对程序的通用部分只需进行很少的修改, 或不做修改。 类的多态性表现为方法的多态性,方法的多态性主要有方法的重载和方法的覆盖。
重载:
方法重载(overload):指在同一个类中的多个方法可以同名但参数列表必须不同。 重载表现为同一个类中方法的多态性。
方法重写(override):指子类中定义了父类中同名的方法。 重写表现为父子与子类之间方法的多态性。
jdk 1.8特性
default关键字
在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default,通过使用default修饰方法,可以让我们在接口里面定义具体的方法实现,
Lambda 表达式
Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。
首先,什么是函数式编程,引用廖雪峰先生的教程里面的解释就是说:函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
简单的来说就是,函数也是一等公民了,在java里面一等公民有变量,对象,那么函数式编程语言里面函数也可以跟变量,对象一样使用了,也就是说函数既可以作为参数,也可以作为返回值了,
Date Api更新
Date Api更新
SpringMVC
SpringMVC核心处理流程:
- 用户发送请求至前端控制器
DispatcherServlet
DispatcherServlet
收到请求调用处理器映射器HandlerMapping
。- 处理器映射器根据请求url找到具体的处理器,生成处理器执行链
HandlerExecutionChain
(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet
。 DispatcherServlet
根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter
处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作- 执行处理器
Handler
(Controller
,也叫页面控制器)。 Handler
执行完成返回ModelAndView
HandlerAdapter
将Handler执行结果ModelAndView
返回到DispatcherServlet
DispatcherServlet
将ModelAndView
传给ViewReslover
视图解析器ViewReslover
解析后返回具体View
DispatcherServlet
对View
进行渲染视图(即将模型数据model填充至视图中)。DispatcherServlet
响应用户。IoC
和AOP
Spring核心容器的主要组件是Bean
工厂(BeanFactory
),Bean
工厂使用控制反转(IoC
)模式来降低程序代码之间的耦合度,并提供了面向切面编程(AOP
)的实现。
简单来说,Spring是一个轻量级的控制反转(IoC
)和面向切面编程(AOP
)的容器框架。
IoC 和 AOP
在具体介绍IoC和AOP之前,我们先简要说明下Spring常用注解
@Controller
:用于标注控制器层组件@Service
:用于标注业务层组件@Component
: 用于标注这是一个受 Spring 管理的组件,组件引用名称是类名,第一个字母小写。可以使用@Component(“beanID”)
指定组件的名称@Repository
:用于标注数据访问组件,即DAO组件@Bean
:方法级别的注解,主要用在@Configuration
和@Component
注解的类里,@Bean
注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中。引用名称是方法名,也可以用@Bean(name = "beanID")
指定组件名@Scope("prototype")
:将组件的范围设置为原型的(即多例)。保证每一个请求有一个单独的action
来处理,避免action的线程问题。
由于Spring默认是单例的,只会创建一个action
对象,每次访问都是同一个对象,容易产生并发问题,数据不安全。@Autowired
:默认按类型进行自动装配。在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired
标注的变量中。@Resource
:默认按名称进行自动装配,当找不到与名称匹配的Bean时会按类型装配。
面向切面编程(AOP)就是纵向的编程:
比如业务A和业务B现在需要一个相同的操作,传统方法我们可能需要在A、B中都加入相关操作代码,而应用AOP就可以只写一遍代码,A、B共用这段代码。并且,当A、B需要增加新的操作时,可以在不改动原代码的情况下,灵活添加新的业务逻辑实现。
设计模式MVC ??
第二种:工厂方法(Factory Method)
设计一个工厂的接口,你想要什么东西,就写个类继承于这个工厂,去调用工厂。抽象就是将更多的产品放到接口中。
第三种:单例模式(Singleton)
确保某一个类只有一个实例,并且提供一个全局访问点,通过get
来调用。
第四种:适配器(Adapter)
原本由于接口不兼容而不能一起工作的那些类可以在一起工作(继承和实现)。
linux常用命令
关机
命令 | 注释 |
---|---|
shutdown -h now | 立刻关机 |
shutdown -h 5 | 5分钟后关机 |
poweroff | 立刻关机 |
shutdown -r now | 立刻重启 |
shutdown -r 5 | 5分钟后重启 |
reboot | 立刻重启 |
目录切换 cd
命令 | 注释 |
---|---|
cd / | 切换到根目录 |
cd /usr | 切换到根目录下的usr目录 |
cd …/ | 切换到上一级目录 或者 cd … |
cd ~ | 切换到home目录 |
cd - | 切换到上次访问的目录 |
目录查看 ls [-al]
命令 | 注释 |
---|---|
ls | 查看当前目录下的所有目录和文件 |
ls -a | 查看当前目录下的所有目录和文件(包括隐藏的文件) |
ls -l 或 ll | 列表查看当前目录下的所有目录和文件(列表查看,显示更多信息) |
ls /dir | 查看指定目录下的所有目录和文件 如:ls /usr |
目录操作【增,删,改,查】
创建目录【增】 mkdir
命令 | 注释 |
---|---|
rm 文件 | 删除当前目录下的文件 |
rm -f 文件 | 删除当前目录的的文件(不询问) |
删除目录或文件【删】rm
命令 | 注释 |
---|---|
rm 文件 | 删除当前目录下的文件 |
rm -f 文件 | 删除当前目录的的文件(不询问) |
删除目录: | |
rm -r aaa | 递归删除当前目录下的aaa目录 |
rm -rf aaa | 递归删除当前目录下的aaa目录(不询问) |
全部删除: | |
rm -rf * | 将当前目录下的所有目录和文件全部删除 |
rm -rf /* | 【自杀命令!慎用!慎用!慎用!】将根目录下的所有文件全部删除 |
注意:rm不仅可以删除目录,也可以删除其他文件或压缩包,为了方便大家的记忆,无论删除任何目录或文件,都直接使用 rm -rf 目录/文件/压缩包
目录修改【改】mv 和 cp
一、重命名目录
命令:mv
当前目录 新目录
例如:mv aaa bbb 将目录aaa改为bbb
注意:mv的语法不仅可以对目录进行重命名而且也可以对各种文件,压缩包等进行 重命名的操作
二、剪切目录
命令:mv
目录名称 目录的新位置
示例:将/usr/tmp目录下的aaa目录剪切到 /usr目录下面 mv /usr/tmp/aaa /usr
注意:mv语法不仅可以对目录进行剪切操作,对文件和压缩包等都可执行剪切操作
三、拷贝目录
命令:cp -r
目录名称 目录拷贝的目标位置 -r代表递归
示例:将/usr/tmp目录下的aaa目录复制到 /usr目录下面 cp /usr/tmp/aaa /usr
注意:cp命令不仅可以拷贝目录还可以拷贝文件,压缩包等,拷贝文件和压缩包时不 用写-r递归
搜索目录【查】find
命令:find
目录 参数 文件名称
示例:find /usr/tmp -name ‘a*’ 查找/usr/tmp目录下的所有以a开头的目录或文件
文件操作命令
文件操作【增,删,改,查】
新建文件【增】touch
命令:touch
文件名
示例:在当前目录创建一个名为aa.txt的文件 touch aa.txt
删除文件 【删】 rm
命令:rm -rf
文件名
修改文件【改】 vi或vim
【vi编辑器的3种模式】
基本上vi可以分为三种状态,分别是命令模式(command mode)
、插入模式(Insert mode)
和底行模式(last line mode)
,各模式的功能区分如下:
-
命令行模式(command mode)
控制屏幕光标的移动,字符、字或行的删除,查找,移动复制某区段及进入Insert mode下,或者到 last line mode。
命令行模式下的常用命令:
【1】控制光标移动:↑
,↓
,j
【2】删除当前行:dd
【3】查找:/字符
【4】进入编辑模式:i
o
a
【5】进入底行模式:: -
编辑模式(Insert mode)
只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。
编辑模式下常用命令:
【1】ESC 退出编辑模式到命令行模式; -
底行模式(last line mode)
将文件保存或退出vi,也可以设置编辑环境,如寻找字符串、列出行号……等。
底行模式下常用命令:
【1】退出编辑::q
【2】强制退出::q!
【3】保存并退出::wq
打开文件
命令:vi
文件名
示例:打开当前目录下的aa.txt文件 vi aa.txt 或者 vim aa.txt
注意:使用vi编辑器打开文件后,并不能编辑,因为此时处于命令模式,点击键盘i/a/o进入编辑模式。
编辑文件
使用vi编辑器打开文件后点击按键:i
,a
或者o
即可进入编辑模式。
i
:在光标所在字符前开始插入
a
:在光标所在字符后开始插入
o
:在光标所在行的下面另起一新行插入
保存或者取消编辑
保存文件:
第一步:ESC
进入命令行模式
第二步::
进入底行模式
第三步:wq
保存并退出编辑
取消编辑:
第一步:ESC
进入命令行模式
第二步::
进入底行模式
第三步:q!
撤销本次修改并退出编辑
ORACLE分页
- 无
ORDER BY
排序的写法。(效率最高)
(经过测试,此方法成本最低,只嵌套一层,速度最快!即使查询的数据量再大,也几乎不受影响,速度依然!)
SELECT * FROM (SELECT ROWNUM AS rowno, t.* FROM emp t WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd') AND TO_DATE ('20060731', 'yyyymmdd') AND ROWNUM <= 20 ) table_alias WHERE table_alias.rowno >= 10;
- 有ORDER BY排序的写法。(效率较高)
(经过测试,此方法随着查询范围的扩大,速度也会越来越慢哦!)
SELECT * FROM (SELECT tt.*, ROWNUM AS rowno FROM ( SELECT t.* FROM emp t WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd') AND TO_DATE ('20060731', 'yyyymmdd') ORDER BY create_time DESC, emp_no) tt WHERE ROWNUM <= 20) table_alias WHERE table_alias.rowno >= 10;
集合类的用法
list
方法 | 描述 |
---|---|
add() | 将元素插入到指定位置的 arraylist 中 |
addAll() | 添加集合中的所有元素到 arraylist 中 |
clear() | 删除 arraylist 中的所有元素 |
clone() | 复制一份 arraylist |
contains() | 判断元素是否在 arraylist |
get() | 通过索引值获取 arraylist 中的元素 |
indexOf() | 返回 arraylist 中元素的索引值 |
removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
remove() | 删除 arraylist 里的单个元素 |
size() | 返回 arraylist 里元素数量 |
isEmpty() | 判断 arraylist 是否为空 |
subList() | 截取部分 arraylist 的元素 |
set() | 替换 arraylist 中指定索引的元素 |
sort() | 对 arraylist 元素进行排序 |
toArray() | 将 arraylist 转换为数组 |
toString() | 将 arraylist 转换为字符串 |
ensureCapacity() | 设置指定容量大小的 arraylist |
lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 |
retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 |
removeRange() | 删除 arraylist 中指定索引之间存在的元素 |
replaceAll() | 将给定的操作内容替换掉数组中每一个元素 |
removeIf() | 删除所有满足特定条件的 arraylist 元素 |
forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
map
方法 | 描述 |
---|---|
clear() | 删除 hashMap 中的所有键/值对 |
clone() | 复制一份 hashMap |
isEmpty() | 判断 hashMap 是否为空 |
size() | 计算 hashMap 中键/值对的数量 |
put() | 将键/值对添加到 hashMap 中 |
putAll() | 将所有键/值对添加到 hashMap 中 |
putIfAbsent() | 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。 |
remove() | 删除 hashMap 中指定键 key 的映射关系 |
containsKey() | 检查 hashMap 中是否存在指定的 key 对应的映射关系。 |
containsValue() | 检查 hashMap 中是否存在指定的 value 对应的映射关系。 |
replace() | 替换 hashMap 中是指定的 key 对应的 value。 |
replaceAll() | 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。 |
get() | 获取指定 key 对应对 value |
getOrDefault() | 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
forEach() | 对 hashMap 中的每个映射执行指定的操作。 |
entrySet() | 返回 hashMap 中所有映射项的集合集合视图。 |
keySet() | 返回 hashMap 中所有 key 组成的集合视图。 |
values() | 返回 hashMap 中存在的所有 value 值。 |
merge() | 添加键值对到 hashMap 中 |
compute() | 对 hashMap 中指定 key 的值进行重新计算 |
computeIfAbsent() | 对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中 |
computeIfPresent() | 对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。 |
迭代 HashMap
可以使用 for-each
来迭代 HashMap
中的元素。
如果你只想获取 key
,可以使用 keySet()
方法,然后可以通过 get(key)
获取对应的 value
,如果你只想获取 value
,可以使用 values()
方法。
实例:
// 引入 HashMap 类 import java.util.HashMap; public class RunoobTest { public static void main(String[] args) { // 创建 HashMap 对象 Sites HashMap<Integer, String> Sites = new HashMap<Integer, String>(); // 添加键值对 Sites.put(1, "Google"); Sites.put(2, "Runoob"); Sites.put(3, "Taobao"); Sites.put(4, "Zhihu"); // 输出 key 和 value for (Integer i : Sites.keySet()) { System.out.println("key: " + i + " value: " + Sites.get(i)); } // 返回所有 value 值 for(String value: Sites.values()) { // 输出每一个value System.out.print(value + ", "); } } }
hashmap 不安全
jdk1.7
死循环,数据丢失:jdk1.8
数据覆盖
SQL链接
内连接:
select * from book as a inner join stu as b on a.sutid = b.stuid
左连接:
select * from book as a left join stu as b on a.sutid = b.stuid
Mybatis中用
#{}
,和${}
传参的区别:
$
符是直接拼成sql的 ,#
符则会以字符串的形式 与sql进行拼接。
/*字符串反转*/ public static String reverse1(String str) { return new StringBuffer(str).reverse().toString(); } public static String reverse3(String str) { char[] arr = str.toCharArray();//String 转换成char数组 String reverse = ""; for (int i = arr.length - 1; i >= 0; i--) { reverse += arr[i]; } return reverse; }
java 事务
为什么要事务?
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。
事务的4个特性(ACID):
- 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
- 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。(实例:转账,两个账户余额相加,值不变。)
- 隔离性(isolation):一个事务的执行不能被其他事务所影响。
- 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
参考链接:https://blog.csdn.net/qq_37751790/article/details/118439711
这篇关于程序员の面试题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27消息中间件底层原理资料详解
- 2024-11-27RocketMQ底层原理资料详解:新手入门教程
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器