面试题集合

本文详细介绍了MyBatis中的事务管理,包括JDBC和MANAGED两种模式,以及Spring事务的控制方式。同时,讲解了Maven的打包命令和流程,以及Git的基本版本控制命令。此外,还深入探讨了Spring的业务流程、AOP概念和执行器类型。最后,提到了线程安全问题、Redis的持久化与高可用性,以及Nginx的反向代理实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.MyBatis中的事务是怎样操作的
MP
@Transactional(rollbackFor = Exception.class )
//    spring事务控制 @Transactional 通过环绕通知控制 around
//    around 特征 业务执行前(开启事务控制),执行目标方法 ,业务执行后(成功提交事务 失败回滚事务)
    /**
     * rollbackFor = Exception.class, 遇到异常回滚数据
     * noRollbackFor = Exception.class 遇到异常不回滚数据
     * spring 默认事务策略
     *   1.如果控制的方法出现了运行时异常,则事务自动的回滚
     *   2.如果控制的方法出现了编译时异常, 则事务不会自动回滚,
     *      spring任务程序既然出现提示需要异常处理,则默认由程序员自己处理,spring不控制
     *      IOC底层  反射,工厂设计模式
     *      DI底层
     *      AOP底层 around 动态代理
<insert useGeneratedKeys="true" keyProperty="id" keyColumn="id"></insert>-->主键回显

Mybatis
(a):使用JDBC的事务管理机制:即使用java.Sql.Connection对象完成对事务的提交,回滚和关闭操作。

(b):使用MANAGED的事务管理机制:mybatis本身不会去实现事务管理的相关操作,而是交个外部容器来管理事务。当与spring整合使用后,一般使用spring来管理事务。


Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。


2.#{}和${}的区别
#{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。

Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。

Mybatis在处理时 , 是 原 值 传 入 , 就 是 把 {}时,是原值传入,就是把时,是原值传入,就是把{}替换成变量的值,相当于JDBC中的Statement编译

变量替换后,#{} 对应的变量自动加上单引号 ‘’;变量替换后,${} 对应的变量不会加上单引号 ‘’

#{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入

#{} 的变量替换是在DBMS 中;${} 的变量替换是在 DBMS 外

Mybatis都有哪些Executor执行器?它们之间的区别是什么?
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

2.Maven工具打包是怎样打包的(命令打包方式),你知道Maven命令不?
1.打包,包含进行单元测试
mvn install
2.打包,跳过单元测试
mvn install -Dmaven.test.skip
3.生成source
mvn source:jar 
4.生成jar和souce.jar
mvn clean install source:jar -Dmaven.test.skip
clear清除编译文件  install下载安装jar包    jar  package打包(编译-试运行-打包)
test命令,可在命令提示符界面运行maven项目中测试类中的测试方法,即执行那些带有@Test注解的测试方法。
1、只会执行Maven项目中src/test/java目录下的测试类。
2、只有类的命名规范满足XxxTest.java才会执行。


3.Git或SVN版本控制命令知道多少?
SVN
svn  status 查看状态 st
svn add name 添加到仓库 
svn commit -m “注释" 简:ci
svn updata      //先更新所有数据
svn log            //打印所有版本
svn remove name  移除文件
简:rm

GIT
回滚到某次提交。索引和工作空间的内容是不变的。如果想再次提交,直接commit即可
git reset - -soft commitId

回滚到某次提交(git reset默认的模式)。索引的内容会改变,工作空间的内容不变。如果想再次提交,需要先add,再commit
git reset - - mixed commitId

回滚到某次提交。索引和工作空间的内容都会变成给定提交时的状态,也就是在给定提交后修改的内容都会丢失,所以需要特别注意
git reset - - hard commitId

列出所有本地分支(前面有星号“*”的为当前工作分支)
git branch

查看提交历史
git log
列出所有远程分支
git branch -r

列出所有本地和远程分支
git branch -a

创建名为“name”的新分支
git branch [name]

切换到“name”分支
git checkout [name]

创建名为“name”的新分支,并切换到该分支
git checkout -b [name]

删除名为“name”的分支
git branch -d [name]

将“name”分支合并到当前分支
git merge [name]


4.Spring的业务流程?
1.首先解析 spring.xml 配置文件,把其中 <bean> 解析为 BeanDefinition, 存入beanFactory
<bean id="" class="" init-method="" destroy-method="" scope="" lazy-init="">

2.把 BeanDefinition 中的定义读取到一个map集合中管理起来了,但还没有创建 bean 的单例对象

执行beanFactory的后处理器

3.接下来 由 beanFactory 创建每个类对应的单例对象, 利用了反射根据类名创建每个类的实例对象(构造,初始化方法)

4.执行 bean 的后处理器, 它其中有两个方法,会在 bean 的初始化方法前后被调用
会把这些结果 存入 beanFactory 的 singletonObjects 这样一个map集合里

userService -> userService对象

5.执行依赖注入
主要为了解决循环引用问题
class A {
@Autowired
private B b;
}

class B {
@Autowired
private A a;
}

key value
a new A();
b new B();

map.get(“a”).setB( map.get(“b”) );
map.get(“b”).setA( map.get(“a”) )

容器.getBean(“bean id”) 根据bean 的id,把bean id当做key,进入 singletonObjects 获取值
返回的结果就是 userSerivce对象
  


5.SpringAop是什么
面向切面编程
1.前置通知(Before Advice): 在连接点之前执行的Advice,不过除非它抛出异常,否则没有能力中断执行流。使用 @Before 注解使用这个Advice。
2.返回之后通知(After Retuning Advice): 在连接点正常结束之后执行的Advice。例如,如果一个方法没有抛出异常正常返回。通过 @AfterReturning 关注使用它。
3.抛出(异常)后执行通知(After Throwing Advice): 如果一个方法通过抛出异常来退出的话,这个Advice就会被执行。通用 @AfterThrowing 注解来使用。
4.后置通知(After Advice): 无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会执行在结束后执行这些Advice。通过 @After 注解使用。
5.围绕通知(Around Advice): 围绕连接点执行的Advice,就你一个方法调用。这是最强大的Advice。通过 @Around 注解使用。
Spring AOP 代理是什么?
代理是使用非常广泛的设计模式。简单来说,代理是一个看其他像另一个对象的对象,但它添加了一些特殊的功能。
Spring AOP是基于代理实现的。AOP 代理是一个由 AOP 框架创建的用于在运行时实现切面协议的对象。
Spring AOP默认为 AOP 代理使用标准的 JDK 动态代理。这使得任何接口(或者接口的集合)可以被代理。Spring AOP 也可以使用 CGLIB 代理。这对代理类而不是接口是必须的。
如果业务对象没有实现任何接口那么默认使用CGLIB。

6.冒泡排序、阶乘
冒泡
int i, j, temp;
    for (j = 0; j < len - 1; j++)
    {
        for (i = 0; i < len - 1 - j; i++)
        if (a[i] > a[i + 1])
        {
            temp = a[i];
            a[i] = a[i + 1];
            a[i + 1] = temp;
        }

选择排序:
  int array[] = { 7,10,12,3,9,5 };
  for (int i = 0; i < 6; i++)
  {
    int minkey = array[i];
    for (int j = i + 1; j < 6; j++)
    {
      if (minkey < array[j])
      {
        int tamp = array[j];
        array[j] = minkey;
        minkey = tamp;
      }
    }
    array[i] = minkey;
  }

阶乘:
public class RecursionTest{

    public static void main(String[] args){
        int num = getValue(5);
        System.out.println(num);
    }
    
    public static int getValue(int num){
        if(num < 0 || num == 0 || num == 1){
            return 1;
        }else{
            num = num*getValue(num - 1);
        }
        return num;
    }
}
public Class RepeatTest{

    public static void main(String[] args){
        int num = getValue(5);
        System.out.println(num);
    }
    public static int getValue(int num){
        int sum = 1;
        for(i = 0,i <= num,i++){
            sum*=i;
        }
        return sum;
    }
}

7.你是怎样看待线程安全的问题的

1.多线程环境
2.多个线程要有共享数据
3.多个线程对共享数据进行修改

8.Redis的储存过程(来为大佬解释解释)

9.Redis是怎么实现高可用的
●数据持久化
●主从数据同步(主从复制)
●Redis 哨兵模式(Sentinel)
●Redis 集群(Cluster)

其中数据持久化保证了系统在发生宕机或者重启之后数据不会丢失,增加了系统的可靠性和减少了系统不可用的时间(省去了手动恢复数据的过程);而主从数据同步可以将数据存储至多台服务器,这样当遇到一台服务器宕机之后,可以很快地切换至另一台服务器以继续提供服务;哨兵模式用于发生故障之后自动切换服务器;而 Redis 集群提供了多主多从的 Redis 分布式集群环境,用于提供性能更好的 Redis 服务,并且它自身拥有故障自动切换的能力。

10.Nginx怎样实现反向代理的
Nginx的2种用途

静态内容的web服务器;
反向代理服务器;

反向代理服务器决定哪台服务器提供服务。返回代理服务器不提供服务器。只是请求的转发。

接收用户请求是异步的,即先将用户请求全部接收下来,再一次性发送后后端web服务器,极大的减轻后端web服务器的压力;

nginx代理和后端web服务器间无需长连接;

发送响应报文时,是边接收来自后端web服务器的数据,边发送给客户端的;


11.Java中的数据类型
12.基本数据类型都有什么
8种基本类型 数组,对象


13.Mybatis怎样操作数据库的
配置--mybatis-config.xml 是mybatis的核心配置文件,用来配置事务,数据库的连接信息
        --UserMapper.xml 是用来存储操作User表的大量的SQL
        --SqlSessionFactory 是会话工厂,创建会话
        --SqlSession 是会话,用来执行SQL(select()/insert()....)
--加载核心配置文件mybatis-config.xml
            --创建会话工厂
            --创建会话
            --执行SQL
            --处理返回值
            package cn.tedu.mybatis;
            import org.apache.ibatis.io.Resources;
            import org.apache.ibatis.session.SqlSession;
            import org.apache.ibatis.session.SqlSessionFactory;
            import org.apache.ibatis.session.SqlSessionFactoryBuilder;
            import java.io.IOException;
            import java.io.InputStream;
            import java.util.List;

            //测试 mybatis
            public class TestMybatis {
                public static void main(String[] args) throws IOException {
            //        加载核心配置文件mybatis-config.xml
                 InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            //        创建会话工厂
                    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            //        创建会话
                    SqlSession session = factory.openSession();
            //        执行SQL
            //        session.selectList(namespace的值.sql的id);
                    List<User> list = session.selectList("userMapperNS.getAll");
            //        处理返回值
                    for (User u : list) {
                        System.out.println(u);
                    }
                }
            }
14.Linux命令关键字

15.SpringBoot和SSM的区别


16.SpringBoot和SpringCloud的区别


17.SQL语句关键字 update insert select delete alter table database rename drop group by having limit


18.你用过那些数据库
mysql mariadb oracle


20.说一下SpringMVC
21.Spring中的注解有什么
@Component 标注一个普通的spring bean类;

@Service 在业务逻辑层使用(service层)

@Repository 在数据访问层使用(dao层)

@Controller 在展现层使用,控制器的声明(C)
@Autowired : 自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。可以作用在变量或者方法上
@Resource : 直接按照bean的id注入,可以独立使用。
@Value : 用于注入基本类型和String类型的数据。

22.Shiro框架的权限控制是怎样实现的(一般会说一个情景)
Shiro是Apache 旗下的一个简单易用的权限框架,可以轻松的完成 认证、授权、加密、会话管理、与 Web 集成、缓存等
24.前端的问题也会问到,有的会问的比较深,比如用JavaScript实现一个页面
25.设计模式大概能说两三种就行
//饿汉式
//思考:饿汉式会不会出现线程安全问题?不会产生线程安全问题
public class Cat {
    private static Cat cat = new Cat();
    private Cat(){}
    public static Cat getCat(){
        return cat;
    }
}


//懒汉式
//思考:懒汉式会不会产生线程安全问题?
//如果会产生,请模拟出来
public class Bird {
    private static Bird bird = null;
    private Bird(){}
    public static Bird getBird(){
        if(bird == null) {
            bird = new Bird();
        }
        return bird;
    }
}

public class MyThread12 extends Thread{
    public void run() {
        Bird bird = Bird.getBird();
        System.out.println(bird);
    }
}

public class MyThread13 extends Thread{
    public void run() {
        Bird bird = Bird.getBird();
        System.out.println(bird);
    }
}

public class TestMyThread12 {
    public static void main(String[] args) {
        MyThread12 mt12 = new MyThread12();
        MyThread13 mt13 = new MyThread13();
        mt12.start();
        mt13.start();
    }
}
26.你见过什么异常  
27.Synchronized的用法
一 修饰方法

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。


二 修饰一个代码块

1)一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞


三 修饰一个静态的方法

Synchronized也可修饰一个静态方法,静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。


四  修饰一个类

Synchronized还可作用于一个类,用法如下:
class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public static void method() {
      synchronized(SyncThread.class) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   public synchronized void run() {
      method();
   }


方式一:使用同步代码块
语法格式: synchronized(锁对象){
    需要锁住的代码
}

锁对象可以是任意对象
//模拟售票
public class SaleTicket extends Thread {
    //有100张票
    private static int ticket = 100;
    //创建锁对象
    private static Object o = new Object();
    public void run() {
        synchronized (o) {
            while(ticket > 0) {
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName() + "卖除了1张票,还剩下" + (--ticket) +  "张票");
            }
        }
    }
}
public class TestSaleTicket {
    public static void main(String[] args) {
        SaleTicket s1 = new SaleTicket();
        s1.setName("火车站");
        SaleTicket s2 = new SaleTicket();
        s2.setName("代售点");
        SaleTicket s3 = new SaleTicket();
        s3.setName("网络");
        s1.start();
        s2.start();
        s3.start();
    }
}
28.你知道的map, 怎样遍历map
将键映射到值的对象
一个映射不能包含重复的键
每个键最多只能映射到一个值
public class Test4 {
    public static void main(String[] args) {
        HashMap<Phone,String> map = new HashMap<>();
        map.put(new Phone("Apple",7000),"美国");
        map.put(new Phone("Sony",5000),"日本");
        map.put(new Phone("Huawei",6000),"中国");
        Set<Phone> phones = map.keySet();
        Iterator<Phone> iterator = phones.iterator();//迭代器
        while (iterator.hasNext()){
            Phone next = iterator.next();
            System.out.println(next.getBrand()+"=="+next.getPrice()+"=="+map.get(next));
        }

    }
}
public class Test4 {
    public static void main(String[] args) {
        HashMap<Phone,String> map = new HashMap<>();
        map.put(new Phone("Apple",7000),"美国");
        map.put(new Phone("Sony",5000),"日本");
        map.put(new Phone("Huawei",6000),"中国");
        Set<Map.Entry<Phone, String>> entries = map.entrySet();//增强for循环
        for (Map.Entry<Phone, String> entry : entries) {
            System.out.println(entry.getKey().getBrand()+"==="+entry.getKey().getPrice()+"==="+entry.getValue());
        }
    }
}


29.JVM内部图结构图画出来


第一,JVM分为五个区域:虚拟机栈、本地方法栈、方法区、堆、程序计数器。PS:大家不要排斥英语,此处用英文记忆反而更容易理解。

第二,JVM五个区中虚拟机栈、本地方法栈、程序计数器为线程私有,方法区和堆为线程共享区。图中已经用颜色区分,绿色表示“通行”,橘黄色表示停一停(需等待)。

第三,JVM不同区域的占用内存大小不同,一般情况下堆最大,程序计数器较小。那么最大的区域会放什么?当然就是Java中最多的“对象”了。
30.ArrayList和LinkinList的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值