常见面试——基础篇

最近一段时间,身边许多的朋友差不多都已经换过工作了。这里收集了一下他们遇到的常见面试题,作一个整理,也算是为后期跳槽做一个笔记吧!

1、HasHmap和HashTable的区别
关于HasHmap可以参考:数据结构——HashMap,这篇文章已经讲的很详细了。

  • HasHmap允许key、value可以为null,如果key为null,调用putForNullKey方法,放入到table[0]的这个位置。HashTable不允许null键和null值,如果为null时会抛出NullPointerException异常。
  • HasHmap是线程不安全的。HashTable是线程安全的,使用了synchronized锁,但是它是锁住整个数组导致效率特别低所以现在基本被ConcurrentHashMap所代替。
  • HashTable默认的初始大小为11,之后每次扩充为原来的2n+1,也就是说是基数,当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。HashMap将取模改为&,具体参考另一篇博客[数据结构——HashMap]。(https://blog.csdn.net/weixin_40096176/article/details/80340949)

2、怎么解决hash冲突?
我们知道不管是HasHmap还是HashTable都是通过key取hash值计算处理后定位到要存放的数组位置,不同的对象可能会存在相同的hash值,HasHmap还是HashTable通过链表来解决hash冲突,对于hash值相同的对象以链表形式存储。

3、JDK1.7和JDK1.8版本的ConcurrentHashMap区别?
关于ConcurrentHashMap这篇文章也介绍的很详细了[数据结构-ConcurrentHashMap]。(https://blog.csdn.net/weixin_40096176/article/details/80350891)
关于最大的区别应该是结构上的变化。在JDK1.7中采用的是Segment +CAS+ HashEntry,而Sement继承了ReentrantLock,所以自带锁功能,而在JDK1.8中则取消了Segment,作者认为Segment太过臃肿,采用Node + CAS + Synchronized。在JDK1.8中当ConcurrentHashMap的链表个数超过8时,会转换为红黑树

4、说说ArrayList与LinkedList的区别?

  • ArrayList和LinkedList都实现了List接口,ArrayList底层是采用数组来实现的,而且它实现了RandomAccess接口表明这个类可以随机存取,对我们的ArrayList来说也就标志着其数据元素之间没有关联,我们知道通过数组的特性是可以使用索引的方式来快速定位对象的位置,因此对于快速的随机取得对象的需求,使用ArrayList实现执行效率上会比较好。所以一般查询使用ArrayList。
  • LinkedList底层采用链表来实现,它里面的元素相互之间是有关联的,因此在进行insert和remove动作时在效率上要比ArrayList要好得多,因为ArrayList是使用数组实现的,若要从数组中删除或插入某一个对象,需要移动后段的数组元素,从而会重新调整索引顺序,调整索引顺序会消耗一定的时间,所以速度上就会比LinkedList要慢许多. 相反,LinkedList是使用链表实现的,若要从链表中删除或插入某一个对象,只需要改变前后对象的引用即可。另外LinkedList的特有方法(本身定义的方法)如:addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等。

5、如果给你一个字符串,让你反向输出怎么实现,手写一个(也就是字符串反转)?
实现方法有很多,这里写两个最常见的:
例1:利用StringBuffer 的reverse()方法,方便快捷

public static String reverse(String s){
    if(null != s){
        StringBuffer sb = new StringBuffer(s);
        String s2 = sb.reverse().toString();
        return s2;
    }
    return s;
    }

例2:通过String类的charAt()的方法来获取字符串中的每一个字符,然后将其拼接为一个新的字符串。

public static String reverse2(String s) {
        if (null != s) {
            int length = s.length();
            String reverse = "";// 临时变量,后续拼接
            for (int i = 0; i < length; i++) {
                reverse = s.charAt(i) + reverse;
            }
            return reverse;
        }
        return s;
    }

6、说说上面反转字符串的代码的时间复杂度?
参考:十分钟搞定时间复杂度

7、如何删除集合中的多个元素?
参考:如何正确在集合遍历的时候删除多个元素

8、写一个冒泡排序
关于冒泡排序算法的原理如下:
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。
4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
这里写图片描述
冒泡排序可以从前往后比较,也可以从后向前比较。这里给出一个优化后的冒泡排序

public static void main(String[] args){

        int[] a = {1, 3, 2, 9, 4};
        int[] b = doubleSort(a);
        for (int i = 0; i < b.length; i++) {

            System.out.println(b[i]);
        }

    }

public static int[] doubleSort(int[] s){
      int temp;
      boolean flag = false;
      for (int i = 0; i < s.length; i++) {
        flag = true;
        for (int j = 0; j < s.length-i-1; j++) {
            if(s[j]>s[j+1]){
                temp = s[j];
                s[j] = s[j+1];
                s[j+1] = temp;
                flag = false;
                }
            }
            if(flag){
                break;
            }
        }
        return s;

    }

9、说说volatie关键字
参考:volatile关键字详解

10、说说synchronized关键字的作用域
synchronized的作用域主要有两种:一种是作用某个类范围,例如synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。第二是作用某个对象实例内的方法,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线 程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。当然除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象;另外需要注意的是synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;

11、说说内存模型
参考:Java内存模型详解

12、内存溢出和内存泄漏的区别
参考:内存泄漏和内存溢出的区别和联系

13、说说你对泛型的理解及泛型的作用,T、 Object 、?之间有什么区别
我们知道Object是java对象的超类,我们可以将Object 作为参数传递,然后通过强制转换来获取我们想要的结果,但这是有风险的,比如我们对ArrayList存入Integer类型的数据,而我们在获取值时通过String类型来接受,这在编译期间是没有任何问题的,但在运行期间就会抛出java.lang.ClassCastException 类型转换异常。对于程序来说这是绝对不允许的。而泛型则提供了很好的解决办法比如我们常见的List<String> stringValues=new ArrayList<String>();这样我们就知道要放入和取出的类型是String类型,如果我们向ArrayList<String>添加Integer类型的对象,将会出现编译错误。所以说泛型通过类型参数使得我们的程序具有更好的可读性和安全性。

Object相信大家都已经知道。在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,用于接收来自外部使用时候传入的类型实参。而?则表示通配符,可以表示任意类型。T自定义泛型和?通配符泛型。
参考:Java泛型深入理解Java泛型详解

14、说说你对反射的理解,通过反射获取一个成员变量怎么做?
什么是反射:反射是一种间接操作目标对象的机制,在程序程序运行时获取或者设置对象自身的信息。 只要给定类的名字,就可以通过反射获取类的所有信息,接着便能调用它的任何一个方法和属性。它最大的特点就是在运行时对一个对象进行操作,

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时判断任意一个类所具有的成员变量和方法。
  3. 在运行时任意调用一个对象的方法
  4. 在运行时构造任意一个类的对象

反射API用来生成在当前JAVA虚拟机中的类、接口或者对象的信息。
Class类:反射的核心类,可以获取类的属性,方法等内容信息。
Field类:Java.lang.reflect.表示类的属性,可以获取和设置类的中属性值。
Method类:Java.lang.reflect。表示类的方法,它可以用来获取类中方法的信息或者执行方法
Construcor类:Java.lang.reflect。表示类的构造方法。
获取Class类有三种方式:
一、通过类的全限定名称Class c = Class.forName(“com.test.Test”);
二、通过对象的getClass(),比如Class c = Test.getClass();
三、通过直接使用.class属性,比如Class c = Test.class;

获取到Class对象后,就可以任意操作该类了,比如获取类变量Field [] f = c.getDeclaredFields();获取该类所有方法 Method [] method=cla.getDeclaredMethods();获取该类构造器集合Constructor [] constructor=cla.getDeclaredConstructors();反射在框架中也用的比较多,最经典的就是Spring IOC(Java反射机制在Spring IOC中的应用)。

反射的优点:从spring IOC可以看出利用反射我们可以通过配置文件来动态配置和加载类,降低模块之间的耦合度。
反射的缺点:反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射;由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。

15、动态代理有哪几种实现方式?
参考:Java的三种代理模式

16、线程的创建有哪几种?
对于线程的创建我们都知道一般会有两种,一种是继承Thread,一种是实现Runnable接口。在Java中只能单继承,所以一般推荐使用实现接口的方式去创建线程。其实还有一种创建的方式通过Callable和Future创建线程,而这却正是面试官会经常问到的。
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
一个简单的例子:

public class CallableTest implements Callable<String>{

    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        return "5";
    }

    public static void main(String[] args) {
        CallableTest ct = new CallableTest();
        FutureTask<String> f = new FutureTask<>(ct);
        new Thread(f).start();
        try {
            System.out.println(f.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

17、mysql有哪些存储引擎,它们之间有什么区别?

MyISAM存储引擎:不支持事务、也不支持外键,优势是访问速度快,对事务完整性没有 要求或者以select,insert为主的应用基本上可以用这个引擎来创建表

InnoDB存储引擎:该存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM引擎,写的处理效率会差一些,并且会占用更多的磁盘空间以保留数据和索引。(这个应该是最常用的)
InnoDB存储引擎的特点:支持自动增长列,支持外键约束

参考:MySQL中四种常用存储引擎的介绍

18、mysql优化

  • 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
  • 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
  • 只返回需要的值字段
  • 尽量避免左模糊查询会导致全表扫描,左模糊查询’%…’或者’%…%’
  • 使用union代替手动创建临时表
  • 尽量避免使用in或者not in,对于连续的值可以通过between来替换in。比如select id from t where num in(1,2,3) ,可以用select id from t where num between 1 and 3替换
  • 应尽量避免在 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

参考:sql优化的几种方法
mysql数据库面试总结

19、mysql索引的优缺点
索引是对数据库表中一列或多列的值进行排序的一种结构。

优点:
大大加快数据的检索速度
创建唯一性索引,保证数据库表中每一行数据的唯一性
可以加速表和表之间的连接

缺点:
索引需要占物理空间。
当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,
降低了数据的维护速度。
总的来说适当的创建索引能提高查询的效率,但索引会影响到增、删、改的效率,所以并不是说索引越多越好。

20、用sql写一个递归调用
自sqlserver2005之后,新增了with as功能语法,即 公用表达式(CTE),让递归实现起来变的简单了。
一个简单的例子:查询当前用户ID的上级领导有哪些

WITH    Emp
           AS ( SELECT   ID ,
                         EName ,
                         ParentGUID
                FROM     dbo.Employee
                WHERE    ID = '5C8214EC-258B-4C44-9F31-206E499F0285'
                UNION ALL  
                SELECT   d.ID ,
                         d.EName ,
                         d.ParentGUID
                FROM     Emp
                         INNER JOIN dbo.Employee d ON d.ID = Emp.ParentGUID
              )
     SELECT ID,EName
     FROM    Emp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值