程序员の面试题

2021/7/10 17:12:09

本文主要是介绍程序员の面试题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

程序员の面试题

金三银四钻石七,将现如今会碰到的面试题 整理记录。

中间件

  • tomcat
  • Weblogic
  • JBOSS
  • Coldfusion
  • Websphere
  • GlassFish

SQL效率优化

1.建立索引

索引建立时应有以下事项:

  1. 应尽量避免在 WHERE 子句中对字段进行 NULL 值判断;
  2. 应尽量避免在 WHERE 子句中使用!=<>操作符;
  3. 应尽量避免在 WHERE 子句中使用 OR 来连接条件;
  4. INNOT IN<>NOT INNOT EXIST非常重要!
  5. LIKE%在左边;
  6. 避免在WHERE条件中,在索引列上进行计算或使用函数,因为这将导致索引不被使用。

2.索引条件

  1. 频繁作为查询条件的字段应该创建索引;
  2. 查询中与其他表有关联的字段,例如外键关系;
  3. 频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件;
  4. WHERE条件里用不到的字段,不创建索引;
  5. 高并发的情况下一般选择复合索引;
  6. 查询中排序的字段创建索引将大大提高排序的速度(索引就是排序加快速查找);
  7. 查询中统计或者分组的字段;
  8. 表记录太少的情况,不需要创建索引;
  9. 经常增删改的表不要建立索引;

3.SQL 注意事项

  1. 任何地方都不要进行全表检索,使用SELECT * FROM Table;
  2. 尽量避免大事务操作,提高系统并发能力
  3. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

JAVA日常使用

1.锁表

: SELECT * FROM products WHERE id='3' FOR UPDATE;

锁是什么?
 锁有两种锁,即悲观锁和乐观锁;
 悲观锁:对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
  乐观锁:而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。

Java中,synchronized关键字和Lock的实现类都是悲观锁。

Locksynchronize 两者区别:

  • 首先synchronizedjava内置关键字,在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核心处理流程:

SpringMVC核心处理流程

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用处理器映射器HandlerMapping
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet
  4. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
  5. 执行处理器Handler(Controller,也叫页面控制器)。
  6. Handler执行完成返回ModelAndView
  7. HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
  8. DispatcherServletModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServletView进行渲染视图(即将模型数据model填充至视图中)。
  11. DispatcherServlet响应用户。
  12. IoCAOP
    Spring核心容器的主要组件是Bean工厂(BeanFactory),Bean工厂使用控制反转(IoC)模式来降低程序代码之间的耦合度,并提供了面向切面编程(AOP)的实现。
    简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面编程(AOP)的容器框架。

IoC 和 AOP

在具体介绍IoC和AOP之前,我们先简要说明下Spring常用注解

  1. @Controller:用于标注控制器层组件
  2. @Service:用于标注业务层组件
  3. @Component : 用于标注这是一个受 Spring 管理的组件,组件引用名称是类名,第一个字母小写。可以使用@Component(“beanID”) 指定组件的名称
  4. @Repository:用于标注数据访问组件,即DAO组件
  5. @Bean:方法级别的注解,主要用在@Configuration@Component注解的类里,@Bean注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中。引用名称是方法名,也可以用@Bean(name = "beanID")指定组件名
  6. @Scope("prototype"):将组件的范围设置为原型的(即多例)。保证每一个请求有一个单独的action来处理,避免action的线程问题。
    由于Spring默认是单例的,只会创建一个action对象,每次访问都是同一个对象,容易产生并发问题,数据不安全。
  7. @Autowired:默认按类型进行自动装配。在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。
  8. @Resource:默认按名称进行自动装配,当找不到与名称匹配的Bean时会按类型装配。

面向切面编程(AOP)就是纵向的编程:
比如业务A和业务B现在需要一个相同的操作,传统方法我们可能需要在A、B中都加入相关操作代码,而应用AOP就可以只写一遍代码,A、B共用这段代码。并且,当A、B需要增加新的操作时,可以在不改动原代码的情况下,灵活添加新的业务逻辑实现。

设计模式MVC ??
第二种:工厂方法(Factory Method)
设计一个工厂的接口,你想要什么东西,就写个类继承于这个工厂,去调用工厂。抽象就是将更多的产品放到接口中。
第三种:单例模式(Singleton)
确保某一个类只有一个实例,并且提供一个全局访问点,通过get来调用。
第四种:适配器(Adapter)
原本由于接口不兼容而不能一起工作的那些类可以在一起工作(继承和实现)。

linux常用命令

关机

命令注释
shutdown -h now立刻关机
shutdown -h 55分钟后关机
poweroff立刻关机
shutdown -r now立刻重启
shutdown -r 55分钟后重启
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编辑器打开文件后点击按键:ia或者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):

  1. 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
  2. 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。(实例:转账,两个账户余额相加,值不变。)
  3. 隔离性(isolation):一个事务的执行不能被其他事务所影响。
  4. 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

参考链接:https://blog.csdn.net/qq_37751790/article/details/118439711



这篇关于程序员の面试题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程