JAVA常见面试题总结

一.基础知识

1.==与equals区别

==既可以比较基本数据类型也可以比较引用数据类型,对于基本数据类型比较的就是本身的数据值,对于引用类型就是比较内存中的地址值是否相同
equals()是属于java.lang.Object类中的方法,如果该方法没有被重写过默认和==相同,但可以看到String类、Date类等类的equals()方法是被重写过的,比较的是内容是否相同
2.接口和抽象类的区别

(1)抽象类中可以有普通成员变量,接口中没有普通成员变量  (2)抽象类可以有构造方法,接口中不能有构造方法(3)抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法(4) 抽象类中可以包含静态方法,接口中不能包含静态方法(5)抽象类中的抽象方法的访问类型可以是 public,protected ,但接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型(6)一个类可以实现多个接口,但只能继承一个抽象类(7)抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只是public static final 类型,并且默认即为 public static final 类型

3.String常用方法

indexOf(“字符”):查询指定的字符串是否存在,返回的是字符串的位置,不存在返回-1;
CharAt(值):拿到指定位置的字符
trim():去除字符串两端的空格;
split():分割字符串,返回分割后的字符串数组;
length():返回字符串的长度;
substring(int begIndex,int endIndex):截取字符串;
toLowerCase():将字符串转换为小写字母;
toUpperCase():将字符串转换为大写字母;
equalsIgnoreCase(String):忽略大小写比较两个值是否相等;
replace():替换字符串,把第一次出现的位置替换掉;

4.服务注册与发现原理

5.线程启动的几种方式

6.list,set,map区别

1、List、Set都是继承自Collection接口,Map则不是
2、List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。) 
3、Set和List对比: 
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。 
4、Map适合储存键值对的数据
5、线程安全集合类与非线程安全集合类 :
LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;
HashMap是非线程安全的,HashTable是线程安全的;
StringBuilder是非线程安全的,StringBuffer是线程安全的。

7.arraylist与linkedlist区别

两者都继承了list,collection接口,但Linkedlist还继承了Queue接口
两者都是线程不安全的
扩容机制:
1.Arraylist是动态扩容机制,初始容量为10,扩容机制为1.5倍。初始最大容量为Integer.MAX_VALUE - 8,原因是防止内存溢出,增加容错率。但是实际最大容量还是可以达到Integer.MAX_VALUE
2.Linkedlist的扩容就是新建节点进行指针指向即可
增加元素:
1.Arraylist在尾部增加元素很快,时间复杂度为O(1),但是在中间增加元素需要移动大量的元素,时间复杂度为O(n)
2.Linkedlist在尾部和中间增加元素的时间复杂度都是O(1),但是在中间添加元素需要先遍历找到插入位置
删除元素机制和增加元素基本类似
查询元素
1.Arraylist中的get方法直接通过index去获取元素,时间复杂度为O(1)
2.Linkedlist中的就需要遍历链表,时间复杂度为O(n)
总的来说,Arraylist支持高效的随机元素访问,LinkedList在插入和删除元素方面比较高效

8.创建线程的几种方式

一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。

package com.nf147.Constroller;

public class FirstThreadTest extends Thread {

    int i = 0;

    //重写run方法,run方法的方法体就是现场执行体
    public void run() {
        for (; i < 100; i++) {
            System.out.println(getName() + "  " + i);
        }
    }

    public static void main(String[] args) {

        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "  : " + i);
            if (i == 50) {
                new FirstThreadTest().start();
                new FirstThreadTest().start();
            }
        }
    }


}

二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。

package com.nf147.Constroller;

public class RunnableThreadTest implements Runnable{
        private int i;
        public void run()
        {
            for(i = 0;i <100;i++)
            {
                System.out.println(Thread.currentThread().getName()+" "+i);
            }
        }
        public static void main(String[] args)
        {
            for(int i = 0;i < 100;i++)
            {
                System.out.println(Thread.currentThread().getName()+" "+i);
                if(i==20)
                {
                    RunnableThreadTest rtt = new RunnableThreadTest();
                    new Thread(rtt,"新线程1").start();
                    new Thread(rtt,"新线程2").start();
                }
            }

        }
}

 三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

package com.nf147.Constroller;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableThreadTest implements Callable<Integer> {


    public static void main(String[] args) {
        CallableThreadTest ctt = new CallableThreadTest();
        FutureTask<Integer> ft = new FutureTask<>(ctt);
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
            if (i == 20) {
                new Thread(ft, "有返回值的线程").start();
            }
        }
        try {
            System.out.println("子线程的返回值:" + ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i;
    }


}

 9.http响应码使用 

10.常用集合框架原理(list,set,map)

11.synchronized与volatile区别

        1.volatile仅能使用在变量级别;
           synchronized则可以使用在变量、方法、和类级别的
        2.volatile仅能实现变量的修改可见性,并不能保证原子性;
           synchronized则可以保证变量的修改可见性和原子性
        3.volatile不会造成线程的阻塞;
           synchronized可能会造成线程的阻塞。
        4.volatile标记的变量不会被编译器优化;
           synchronized标记的变量可以被编译器优化

12.java幂等性解决方案

二.spring

1.spring统一异常处理

  • 使用 HandlerExceptionResolver 接口,并且 Spring 已经提供默认的实现类 SimpleMappingExceptionResolver。
  • 使用@ExceptionHandler注解实现异常处理;

2.springbean的生命周期

3.springbean的作用域

作用域描述
singleton在spring IOC容器仅存在一个bean实例,bean以单例方式存在,bean作用域范围的默认值
prototype每次从容器中调用bean时,都返回一个新的实例,即每次调用getbean()时,相当于执行newXxxBean()
request每次http请求都会创建一个新的bean,该作用域仅适用于web的spring webApplicationContext环境
session同一个http session共享一个bean,不同session使用不同的bean。该作用域仅适用于web的pring webApplicationContext环境
application限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境

4.对spring ioc aop的理解

IOC:控制反转也叫依赖注入,IOC利用java反射机制,AOP利用代理模式。所谓控制反转是指,本来被调用者的实例是有调用者来创建的,这样的缺点是耦合性太强,IOC则是统一交给spring来管理创建,将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。
AOP:面向切面编程。(Aspect-Oriented Programming)
AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码,属于静态代理

5.spring的注解

(1)@Controller:通常用于controller类,也就是控制层(mvc)

(2)@Service:通常用于注解service类,服务层

(3)@Repository:通常用于注解DAO层,持久层

(4)@Autowired:

6、spring的controller层是线程安全的吗,如何避免

不是线程安全的,默认是单例

1、不要在controller中定义成员变量。2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式。3、在Controller中使用ThreadLocal变量

三.redis

1.redis支持的数据类型

String,list,set,hash,zset

2.redis持久化

redis提供了RDB和AOF两种方式:

RDB其实就是把数据以快照的形式保存在磁盘上,通过把某个时刻的所有数据生成一个快照来保存,提供了三种机制:save、bgsave、自动化;

①、优势

(1)RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。

(2)生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。

(3)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

②、劣势

RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

AOF机制:

redis会将每一个收到的写命令都通过write函数追加到文件中;

AOF的三种触发机制:(1)每修改同步always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好
(2)每秒同步everysec:异步操作,每秒记录 如果一秒内宕机,有数据丢失
(3)不同no:从不同步

 优点:

1)AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
(2)AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
(3)AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
(4)AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
缺点:

(1)对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
(2)AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的
(3)以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。

3.redis集群

4.redis中hash类型的应用场景

(1)购物车

(2)存储对象

当对象的某个属性需要频繁修改时,不适合用string+json,因为它不够灵活,每次修改都需要重新将整个对象序列化并赋值,如果使用hash类型,则可以针对某个属性单独修改,没有序列化,也不需要修改整个对象。比如,商品的价格、销量、关注数、评价数等可能经常发生变化的属性,就适合存储在hash类型里。

  当然,不常变化的属性存储在hash类型里也没有问题,比如商品名称、商品描述、上市日期等。但是,当对象的某个属性不是基本类型或字符串时,使用hash类型就必须手动进行复杂序列化,比如,商品的标签是一个标签对象的列表,商品可领取的优惠券是一个优惠券对象的列表(如下图所示)等,即使以coupons(优惠券)作为field,value想存储优惠券对象列表也还是要使用json来序列化,这样的话序列化工作就太繁琐了,不如直接用string + json的方式存储商品信息来的简单。

四.oracle

1.oracle常用函数   

 to_char():把数据转换为字符串类型:to_char(字符串,类型);
   sysdate:该函数返回系统时间;
   lower(char); 把字符串转换为小写格式;
   upper(char);把字符串转换为大写格式;
 length(char);返回字符串的长度;
 substr(char,m,n);取字符串的字串;
 replace(char,search_char,replace_str);

2.oracle分级查询

SELECT [LEVEL], COLUMN, ...  
FROM   TABLE  
[WHERE CONDITION(S)]  
[START WITH CONDITION(S)]  
[CONNECT BY PRIOR COLUMN1 = COLUMN2] ;<strong>  
</strong> 

说明:
1.LEVEL代表层级。 在具有树结构的表中,每一行数据都是树结构中的一个节点,由于节点所处的层次位置不同,所以每行记录都可以有一个层号。层号根据节点与根节点的距离确定。不论从哪个节点开始,该起始根节点的层号始终为 1 ,根节点的子节点为 2 , 依此类推。
2.START WITH定义查找起始节点。在自顶向下查询树结构时,不但可以从根节点开始,还可以定义任何节点为起始节点,以此开始向下查找。这样查找的结果就是以该节点为开始的结构树的一枝。 不但可以指定一个根节点,还可以指定多个根节点。START WITH 子句为可选项,若该子句被省略,则表示所有满足查询条件的行作为根节点。
3.CONNECT BY 子句指定层次检索的顺序。 根据PRIORY 运算符放置在连接关系的位置,从而确定查找树结构是的顺序是自顶向下还是自底向上。CONNECT BY PRIOR PARENT KEY= CHILD KEY,表明顺序是自顶向下;CONNECT BY PRIOR CHILD KEY= PARENT KEY,表明顺序是自底向上。

例:

3.哪些情况下sql会失效

(1)条件中用or,即使其中有条件带索引,也不会使用索引查询 
(2)对于多列索引,不是使用的第一部分,则不会使用索引
(3)like的模糊查询以%开头,索引失效
(4)如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不会使用索引

4.索引类型

普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索

5.怎么判断索引是否创建成功

--建立索引
create index index_bankseq on bank(bankseq);
--drop index bankseq;
--查看索引
select status,T.* from user_indexes T where table_name='BANK';
--查看执行计划(看索引是否被执行)
explain plan for select * from bank where bankseq='1';
select * From Table(dbms_xplan.display());

6查找重复姓名的sql

方式一:select * from 学生表 where 姓名 in(select 姓名 from 学生表 group by 姓名 having count(姓名)>=2)
分析:from 学生表 :找到要查询的表名, where 姓名 in:过滤条件让姓名符合小括号里面内容  group by 姓名 :按照姓名来分组,也就是说姓名相同的会放在同一组里面,其他字段可能包括多条信息,having count(姓名)>=2:过滤分组内容中姓名达到两个以及以上的信息)
方式二:select 姓名,count(姓名) from 学生表 group by 姓名 having count(姓名)>=2
注意方式二:select 姓名 from 学生表 group by 姓名 having count(姓名)>=2即可,count(姓名)是自己又在返回的视图看到了另一个字段,这个字段用来显示出现的重复姓名的次数。

7.sql优化

(1)对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。    
(2)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:    
select id from t where num is null    
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:    
select id from t where num=0     
(3)应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。    
(4)应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:    
select id from t where num=10 or num=20    
可以这样查询:    
select id from t where num=10    
union all    
select id from t where num=20    
(5)in 和 not in 也要慎用,否则会导致全表扫描,如:    
select id from t where num in(1,2,3)    
对于连续的数值,能用 between 就不要用 in 了:    
select id from t where num between 1 and 3    
(6)下面的查询也将导致全表扫描:    
select id from t where name like '%abc%'    
(7)应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:    
select id from t where num/2=100    
应改为:    
select id from t where num=100*2    
(8)应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:    
select id from t where substring(name,1,3)='abc'--name以abc开头的id    
应改为:    
select id from t where name like 'abc%'    
(9)不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。    
(10)在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。    
(11)不要写一些没有意义的查询,如需要生成一个空表结构:    
select col1,col2 into #t from t where 1=0    
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:    
create table #t(...)    
(12)很多时候用 exists 代替 in 是一个好的选择:    
select num from a where num in(select num from b)    
用下面的语句替换:    
select num from a where exists(select 1 from b where num=a.num)     
(13)并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。      
(14)索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,    
因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。    
一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。  
(15)尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。    
这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。    
(16)尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,    
其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。    
(17)任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。    
(18)避免频繁创建和删除临时表,以减少系统表资源的消耗。
(19)临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。    
(20)在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,    
以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
(21)如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。    
(22)尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。    
(23)使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
(24)与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。
在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
(25)尽量避免大事务操作,提高系统并发能力。
(26)尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

8.左连接,右连接,内连接区别

 left join (左连接):返回包括左表中的所有记录和右表中连接字段相等的记录。
right join (右连接):返回包括右表中的所有记录和左表中连接字段相等的记录。
inner join (等值连接或者叫内连接):只返回两个表中连接字段相等的行。
full join (全外连接):返回左右表中所有的记录和左右表中连接字段相等的记录。

9.sql返回自增长id

insert into Table1 (CreatedDate)  output  inserted.id  values (getdate())

output 从数据修改语句中返回输出,可以看作是“返回结果的DML”
SQL2005之后 Insert,Delete,Update语句 均支持Output语句。
在Output语句中可以引用inserted和deleted。使用方法同触发器类似。
该方法最大弊端就是需要指定主键。output Inserted.要返回的值。

五.Mysql

1.mysql数据类型

主要包括以下五大类:
整数类型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT
浮点数类型:FLOAT、DOUBLE、DECIMAL
字符串类型:CHAR、VARCHAR、TINY TEXT、TEXT、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB
日期类型:Date、DateTime、TimeStamp、Time、Year
其他数据类型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等


2.mysql优化

六.springboot

1.springboot如何自动配置,默认几个配置文件

通常我们使用 @SpringBootApplication 来注解一个应用的入口类,@EnableAutoConfiguration注解能够开启Spring ApplicationContext 的自动配置功能,通过扫描类路径下的组件并注册符合条件的bean。
SpringBoot 在spring-boot-autoconfigure-{version}.jar中提供了各种各样的AutoConfiguration类,负责注册各种各样的组件。

七.mybatis

1.mybatis中$与#区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值