1期Java面试题
-
==和equals的区别?
==号
- 基本数据类型:比较值
- 引用数据类型:比较引用地址
equals
- equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较
-
HashCode 的作用有那些?
-
一个对象的HashCode就是一个简单的Hash算法的实现,关系到对象在存取时的性能好坏,不同的HashCode可能会使你的对象存取产生,成百上千倍的性能差别。如果两个对象相同 equals 方法一定返回 true,并且这两个对象的 HashCode 一定相同。两个对象的 HashCode 相同,并不一定表示 。两个对象就相同,即equals()不一定为 true,只能够说明这两个对象在一个散列存储结构中。
-
如果对象的 equals 方法被重写,那么对象的 HashCode 也尽量重写
-
当集合要添加新的元素 时,先调用这个元素的 HashCode 方法,就一下子能定位到它应该放置的物理位置上:
(1)如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比 较了
(2)如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了
(3)equals不相同的话,也就是发生了 Hash key 相同导致冲突的情况,那么就在这个 Hash key 的地方产生一个链表,将所有产生相同 HashCode的对象放到这个单链表上去,串在一起(很少出现)
-
-
Java中操作字符串的有哪些类?他们之间有哪些区别?
- String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作
- StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer
-
Java容器有哪些?
-
创建线程有哪几种方式?
①. 继承Thread类创建线程类
- run伟线程类的核心方法,相当于主线程的main方法,是每个线程的入口
- 一个线程只能start()一次
- run()是jvm创建完本地操作系统级线程后回调的方法,所以只能用start()启动,不能手动调,否则就是普通方法了
②. 通过Runnable接口创建线程类
1. 可以避免单继承的局限 2. 当子类实现runnable接口后,子类和Thread类作为代理去负责资源的调度和线程的创建
③. 通过覆写Callable接口(要使用FutureTask包装)
-
核心方法为call(),有返回值
-
在Thread调用start前,需要用FutureTask包装子类
-
案例
public class MyThread implements Callable<String> { private int count = 20; @Override public String call() throws Exception { for (int i = count; i > 0; i--) { System.out.println(Thread.currentThread().getName()+"当前票数:" + i); } return "sale out"; } public static void main(String[] args) throws InterruptedException, ExecutionException { Callable<String> callable =new MyThread(); FutureTask <String>futureTask=new FutureTask<>(callable); Thread mThread=new Thread(futureTask); Thread mThread2=new Thread(futureTask); Thread mThread3=new Thread(futureTask); mThread.start(); mThread2.start(); mThread3.start(); System.out.println(futureTask.get()); } }
④通过线程池启动多线程
Executors工具类创建线程池有三种方式:
-
固定线程池大小(newFixdThreadPool(int size)),适用于需要限制线程数量的场景
public class Test { public static void main(String[] args) { ExecutorService ex=Executors.newFixedThreadPool(5); for(int i=0;i<5;i++) { ex.submit(new Runnable() { @Override public void run() { for(int j=0;j<10;j++) { System.out.println(Thread.currentThread().getName()+j); } } }); } ex.shutdown(); } }
-
单线程池(newSingleThreadExecutor()),但需要保证顺序执行各个任务的场景
ExecutorService ex=Executors.newSingleThreadExecutor();
-
缓存线程池(newCachedThreadPool()),当提交任务的速度大于线程池处理业务逻辑的速度时,缓存线程池会不断的创建线程
-
Java中如何更好的去终止一个线程?
java终止一个线程有三种方式:标志位、stop()方法、interrupet()方法
标志位
public class ServerThread extends Thread { //volatile修饰符用来保证其它线程读取的总是该变量的最新的值 public volatile boolean exit = false; @Override public void run() { ServerSocket serverSocket = new ServerSocket(8080); while(!exit){ serverSocket.accept(); //阻塞等待客户端消息 ... } } public static void main(String[] args) { ServerThread t = new ServerThread(); t.start(); ... t.exit = true; //修改标志位,退出线程 } }
stop()终止线程
- 此方法已过时,且不安全
- 立刻停止run()中的全部工作,包括在catch或finally中的,并抛出ThreadDeath异常(不需要显示捕获该异常),这样会造成一些清理性的工作无法完成,如:流操作文件、数据库连接等的关闭
- 导致数据的不一致性
interrupt()方法终止线程
- 调用该方法,不会立刻终止线程,只是向执行线程发起中断标识,真正中断操作还需要目标线程来决定
- isInterrupeted()判断是否被中断
- interrupeted()判断是否被中断,并清除中断标志
public class ThreadInterrupt extends Thread { public void run() { while (!isInterrupted()) { //do something } } public static void main(String[] args) throws Exception { Thread thread = new ThreadInterrupt(); thread.start(); thread.interrupt(); thread.join(); System.out.println("线程已经退出!"); } }
-
如何保证线程安全?
可以通过两种上锁的方式来实现线程安全:synchronized关键字、lock接口与ReentrantLock实现类来实现
synchronized关键字
- 基于jvm层面上的重量级锁;
- 不需要手动释放锁
- 效率低,当一个对象A获取锁后,其他需要获取该锁的对象只能等A释放了锁才能获取(不可中断锁)
- 非公平锁
Lock接口
- 不是java内置的,而是一个类,可以通过这个类来实现同步访问
- 需要手动动用unlock方法来释放锁
- 可以让等待锁的线程响应中断(中断锁)
- 默认非公平锁,但可设置为公平锁
- 可判断是否为公平锁、是否有等待线程等方法
总结:当资源竞争不激烈的情况下,两种锁的的效果差不多;但当资源竞争激烈时,Lock锁的效率会比synchronized高
-
什么是spring?它有哪些优点?
IOC
spring主要是控制管理对象的生命周期和对象之间的关系的,他的核心思想IOC(控制反转),IOC只是一种思想,他的实际操作时di注入,注入的方式有setter、构造器和自定义注入。
AOP
面向切面编程,可以在编译、装载、运行期间,在不修改源代码的情况下,给程序动态添加功能的这样一个技术。说白了也就是把一些重复用的功能给提取出来,在适当的时候植入到程序中,如:日志、事务管理等。
-
SpringBean的生命周期是怎样的?
-
SpringMVC的运行流程?
- 浏览器向spring发送请求,首先会通过前端控制器DispatcherServlet进入到HandlerMapping中;
- HandlerMapping要做的事情就是根据对应的请求找到对应的Controller组件;
- Controller负责处理业务逻辑,处理完业务逻辑后,将返回的数据和视图名称装到ModelAndView中;
- 当前端控制器接收到ModelAndView对象后,会调用ViewResolver组件,将页面效果给显示出来。
-
说说mybatis的执行流程?
- 首先通过Resource加载mybatis的主配置文件
- 解析配置文件,生成configuration、和一个个MappedStatement
- sqlSessionFactoryBuilder通过configuration对象来生成sqlSessionFactory,从而开启sqlSession
- sqlSession对象完成和数据库的交互(sqlSession本身是不直接操作数据库的,由Executor操作)
- 调用api的Statement Id找到对应的MappedStatement对象
- 解析MappedStatement对象(sql传参,动态sql拼接等),生成JDBC Statement对象
- jdbc执行sql
- 通过MappedStatement的结果映射关系,获取数据的结果封装到javaBean、HashMap中
-
mybatis传参有哪几种方式?
-
顺序传参
<select id="selectUser" resultType="com.wyj.entity.po.User"> select * from user where userName = #{0} and deptId = #{1} </select>
-
@Param注解传参
public User selectUser(@Param("userName") String name, int @Param("deptId") id); <select id="selectUser" resultType="com.wyj.entity.po.User"> select * from user where userName = #{userName} and deptId = #{deptId} </select>
-
Map集合传参
public User selectUser(Map<String, Object> params); <select id="selectUser" parameterType="java.util.Map" resultType="com.wyj.entity.po.User"> select * from user where userName = #{userName} and deptId = #{deptId} </select>
-
JavaBeand对象传参
<select id="selectUser" parameterType="com.wyj.entity.po.User" resultType="com.wyj.entity.po.User"> select * from user where userName = #{userName} and deptId = #{deptId} </select>
-
-
说下final、finally、finalize有什么区别?
- final是用来做修饰的关键字,修饰类、变量、方法
- finally用于在异常操作时做清除操作的
- finalize是GC垃圾回收器中的一个方法,垃圾回收器在清理内存中的对象前会判断这个对象有没有被引用时调用
-
left join和right join(oracle、mysql数据库)有什么区别?
- left join:左连接,左表作为主表(左表中的数据会全部查出来,哪怕右表的数据为空也会显示)
- right join:右连接,右表作为主表(和左表相反)
-
mysql、oracle的sql优化有了解吗?如何避免索引失效?
-
索引概念
对表中的某些列进行排序,那么当查找这些列时,就不需要全表查询了,可在排序好的列中进行快速搜索,比如二分法等
-
优化
-
通过explain工具可分析sql
-
-
索引失效
- 通配符为遵循最左前缀原则
- 索引列上存在空数据
- 使用了内置函数
- 索引做了隐士类型转换
- 使用了“||”联接列
- 在索引列上做了运算操作等
-