“ 关键字: 每日高频面试题 ,一起冲进大厂"

导读:

大家好,我是程序源代码,算算入行的时候我已算入行10以上的老兵了,毕业在北京北漂8年,13年回到省会石家庄,一直就在河北这块土地上生活着。现在人了快到了中年,感觉现在生活压力都比较多大。梦想着有一天能进入大厂,实现一个大厂梦。其实我感觉大家应该和我一样,都想有进入大厂工作毕竟生活需要理想,还需要苟且(挣高工资),其实现在大厂并不是特别容易进,作为一名好多年的Java选手,其实我感觉进入大厂,首先要把自己的技术和面试这两个重要点做好。

最近这几天我一直在看java相关的基础,也在看一些面试相关的资源和视频,其实感觉不管什么技术面试,其实基础是很重要的。现在正好暑假了,天气炎热,作为宅男的我本来就不想出去,所以还不如呆在家里,静下心来好好准备下基础的面试。所以这个暑假(虽然我没有暑假)我准备用60天时间,把Java面试中的面试题看一看,从头复习一遍。计划是每天1-3题的形式学一遍(希望自己能坚持下来)。

  主要涵盖的知识点我简单列了一下,计划从这些方面出发进行学:Java基础知识、集合容器、并发编程、JVM、Spring全家桶、MyBatis等ORMapping框架、MySQL数据库、Redis缓存、RabbitMQ消息队列、Linux操作技巧等。

一、java基础

String,StringBuffer, StringBuilder 的区别是什么?

1、从对象是否可变角度(可变与不可变)

String类中使用字符数组保存字符串,因为有“final”修饰符,所以string对象是不可变的。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,这两种对象都是可变的。

2、是否多线程安全性

String中的对象是不可变的,也就可以理解为常量,显然线程安全。StringBuilder是非线程安全的。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

3、在使用性能上分析

每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

String为什么要设计成不可变的?

1.便于实现字符串池(String pool)

在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。如果字符串是可变的,某一个字符串变量改变了其值,那么其指向的变量的值也会改变,String pool将不能够实现!

2.使多线程安全

在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。

3.避免安全问题

在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。

4.加快字符串处理速度

由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。

总体来说,String不可变的原因要包括 设计考虑,效率优化,以及安全性这三大方面。

java对象的创建c

什么是不可变对象?好处是什么?

不可变对象指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象,如 String、Integer及其它包装类.不可变对象最大的好处是线程安全.

能否创建一个包含可变对象的不可变对象?

当然可以,比如final Person[] persons = new Persion[]{}. persons是不可变对象的引用,但其数组中的Person实例却是可变的.这种情况下需要特别谨慎,不要共享可变对象的引用.这种情况下,如果数据需要变化时,就返回原对象的一个拷贝.

java 创建对象有哪几种方式?

java中提供了以下四种创建对象的方式:- new创建新对象- 通过反射机制- 采用clone机制- 通过序列化机制。前两者都需要显式地调用构造方法。对于clone机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在java中序列化可以通过实现Externalizable或者Serializable来实现。

JDK 和 JRE 有什么区别?

JDK:Java Development Kit 的简称,可以理解成Java 开发工具包,它提供了 Java 的开发环境和运行环境。

JRE:Java Runtime Environment 的简称,可以理解成Java 运行环境,它为 Java 的运行提供了所需环境。

具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多

Java 程序调试和分析的工具(jconsole)。在安装JDK时我们一般会发现在安装好的目录下会有两个重要的文件夹bin、lib,其实bin文件夹就是java程序运行时环境、lib文件夹就是java程序运行时需要的资源。再简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。

== 和 equals 的区别是什么?

对于==的解读:一般作用对于基本类型和引用类型 == 的作用效果是不同的,如下所示:基本类型(char、boolean、byte、short、int、long、float、double。):比较的是值是否相同;引用类型:比较的是引用是否相同;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。再简单说string类中被重写的equals()方法其实是比较多两个字符串string的内容值的,只要值相同就是ture;

String x = “hello”;

String y = “hello”;

String z = new String(“hello”);

System.out.println(x==y); // true

System.out.println(x==z); // false

System.out.println(x.equals(y)); // true

System.out.println(x.equals(z)); // true

final 在 Java 中有什么作用?

修饰对象不同,启动的作用不同

final 修饰的类叫最终类,该类不能被继承。

final 修饰的方法不能被重写,但可以被子类重载。

final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改

final 修饰成员变量,可以在非static中声明变量或者在代码块中赋值、或者在其构造器中赋值。

final 修饰类新变量,只能在static中初始化变量值。

int 和integer的区别?

java是面向对象的编程语言,为了编程方便java引入了基本数据类型(8种:byte/short/int/long/float/double/boolean/char)。但为了操作这8种数据类型java从5开始引入了对应的包装类型,通过自动拆箱、装箱实现每种类型间的转换。

public class Main<intger> {

    public static void main(String[] args) {

        int a = 1;
        Integer b = new Integer(1);
        Integer c = 1;

        System.out.println(a==b);
        System.out.println(c==b);
        System.out.println(a==c);
    }
}

两个对象的 hashCode() 相同,则 equals() 也一定为 true吗?

public class Test1 {
    public static void main(String[] args){
        String a1= "1";
        String a2= "1";
        System.out.println(a1.hashCode());
        System.out.println(a2.hashCode());
        System.out.println(a1.equals(a2));

        //很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表
        //中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等
        String s1= "通话";
        String s2= "重地";
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println(s1.equals(s2));

    }
}

String s1="i"与 String s2=new String(“i”)一样吗?

public class Test2 {

    public static void main(String[] args) {
        String s1 = "i";
        String s2 = new String("i");
        System.out.println(s1==s2);
        System.out.println(s1.equals(s2));
    }

请仔细阅读下边的代码,请问下边的a、b两个变量的值是否能交换成功?

public class Test {

    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println("a="+a+"  b="+b);
        changNum(a,b);
        System.out.println("a="+a+"  b="+b);
    }

    public static void changNum(int number1,int number2){
        int i ;
        i = number1;
        number1= number2;
        number2= i;
    }

}

数组的三种创建初始化

1、默认初始化

数组是引用类型,所以数组在创建完成后即分配空间后,其中的数据元素会默认按照数组类型进行隐式初始化

2、静态初始化

直接在定义数组的同时,为数组元素分配空间并赋值

3、动态初始化

数组定义和赋值是分开进行的。

public class Test5 {
    public static void main(String[] args) {
        //1、默认初始化
        int[] arry = new int[3];
        System.out.println(arry);

        //2、静态初始化
        int[] arry1 = new int[]{1,2,3};
        System.out.println(arry1[0]);

        //3、动态初始化
        int[] arry2 = new int[3];
        arry2[0]=1;
        arry2[1]=2;
        arry2[2]=3;
        System.out.println(arry2[0]);
    }
}

数组的复制

1、copyOf(arryobject,4) 完成数组的复制

2、copyOfRange(arryobject,1,4) 完成数组的区间复制

3、arraycopy()

public static void main(String[] args) {
    //定义数组1长度10
    int[] arry1 = new int[10];
    arry1[0]= 11;
    arry1[1]= 12;
    arry1[2]= 13;
    arry1[3]= 14;
    arry1[4]= 15;
    arry1[5]= 16;
    arry1[6]= 17;
    arry1[7]= 18;
    arry1[8]= 19;
    arry1[9]= 20;

    //定义数组2,长度20
    int[] arry2 = new int[19];

    //copyOf(arryobject,4)  完成数组的复制,只能是从数组的第一个元素进行复制,不能定义位置。
    //arry2 = Arrays.copyOf(arry1,4 );

    //copyOfRange(arryobject,1,4)  完成数组的区间复制
    //arry2 = Arrays.copyOfRange(arry1,1,4);

    //arraycopy();
     System.arraycopy(arry1,1,arry2,5,5);

    System.out.println(arry2[5]);
    System.out.println(arry2[6]);
    System.out.println(arry2[7]);
    System.out.println(arry2[8]);
    System.out.println(arry2[9]);
}

数组与List 转换

public static void main(String[] args) {

    //将list 转成数组的方法
    List list1 = new ArrayList();
    list1.add("程序源代码");
    list1.add("itcoder");

    list1.toArray();

    System.out.println(list1.toArray().length);

    //数组转成list
    String[] arry1 = new String[2];
    arry1[0]= "程序源代码";
    arry1[1]= "itcoder";

    List list2 =  Arrays.asList(arry1);
    System.out.println(list2);

}

throw throws 的区别是什么?

Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常。

throws 关键字在方法上声明该方法要拋出的异常,

throw 着急字在方法内部拋出异常对象。

throws 关键字和 throw 关键字在使用上的几点区别如下

throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常;

throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个

方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否

则也要在方法签名中用 throws 关键字声明相应的异常。

public class Test8 {

    //定义除法方法
    public static int divisor(int a, int b) {
        return a/b;
    }

    //在main函数中调用方法
    public static void main(String[] args){
        divisor(157, 0);
    }
}

用最有效率的方法计算 2 乘以 8

2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)。

  1. Java 容器都有哪些?

Java 容器分为 Collection 和 Map 两大类,其下又有很多子类,如下所示:

Collection、List、ArrayList、LinkedList、Vector、Stack、Set、HashSet、LinkedHashSet、TreeSet、MapHashMap、LinkedHashMap、TreeMap、ConcurrentHashMap、Hashtable

  1. Collection 和 Collections 有什么区别?

Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子

类,比如 List、Set 等。

Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方

法: Collections. sort(list)。

  1. List、Set、Map 之间的区别是什么?

List、Set、Map 的区别主要体现在两个方面:元素是否有序、是否允许元素重复。

三者之间的区别,如下表:

区别图

  1. HashMap 和 Hashtable 有什么区别?

存储:HashMap 运行 key 和 value 为 null,而 Hashtable 不允许。

线程安全:Hashtable 是线程安全的,而 HashMap 是非线程安全的。

推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使

用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。

  1. 如何决定使用 HashMap 还是 TreeMap?

对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的

插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

  1. 说一下 HashMap 的实现原理?

HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,

HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算

出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的

value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。

  1. 说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现

比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允

许重复的值。

  1. ArrayList 和 LinkedList 的区别是什么?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。

随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据

存储方式,所以需要移动指针从前往后依次查找。

增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList

增删操作要影响数组内的其他数据的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推

荐使用 LinkedList。

  1. 如何实现数组和 List 之间的转换?

数组转 List:使用 Arrays. asList(array) 进行转换。

List 转数组:使用 List 自带的 toArray() 方法。