• java 基础知识面试题(持续更新)


    1.权限修饰符 public ,protected ,默认, private  的区别是?

          当前类  相同包下  子类  项目中其他包下

    public      √     √     √      √ 

    protected   √     √     √      ×

    默认     √     √     ×      ×

    private    √        ×     ×        × 

    2.java中的基本数据类型有哪些? String 属于基本类型吗?

     答:java中基本类型有8种 , byte short,int ,long ,float , double ,char ,boolean  ,String 属于引用类型,不属于基本类型

          整数类型: byte, short ,int ,log          

          byte (1字节,8位 取值范围:-128 ~ 127) ,short(2字节,16 位,很少用) ,int(4字节,32位最常用)  ,long(8字节,64位)            

    数值类型

           小数类型:  float(4字节 32位) , double (8字节,64位)

    字符类型  : char   占2个字节,16位 ; 单引号引起的字符,可以为中文

    布尔类型  : boolean   值为true 或者 false 

    java中默认的整数类型是 int , 默认的小数类型是double

    类型的自动提升:

    例如:

    double d = 1L;//类型小转大,可以自动提升
    
    long l = 1.0; //类型大转小,只能强转

    3. int 与 Integer 的区别是什么?

    答:1、Integer是int的包装类,int则是java的一种基本数据类型 
    2、Integer变量必须实例化后才能使用,而int变量不需要 
    3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 
    4、Integer的默认值是null,int的默认值是0

    拓展: int 与 Integer 的比较 , Integer 与 Integer 的比较

    //情形1 Integer 与 int类型比较
    Integer i   =   1;
    int     j   =   1;
    System.out.println(i == j);
    
    
    //情形2 Integer 与 Integer 比较(不通过new 的形式)
    Integer i1 =   100;
    Integer i2 =   100;
    System.out.println(i1 == i2);
    
    
    //情形3 Integer 与 Integer 比较(不通过new 的形式)
    Integer i3 =   128;
    Integer i4 =   128;
    System.out.println(i3 == i4);
    
    
    //情形4 Integer 与 Integer 通过new
    Integer i5 =   new Integer(110);
    Integer i6 =   110;
    System.out.println(i5 == i6);
    
    
    //情形5 Integer 与 Integer 通过new
    Integer i7 =   new Integer(120);
    Integer i8 =   new Integer(120);
    System.out.println(i7 == i8);

    最终执行结果:

    true
    true
    false
    false
    false

    分析:

    //写在前面 ,对于 == ,int 类型之间的比较,比较的是值, Integer对象之间比较的是内存地址
    
    /*
    情形一:
    Integer 与 int 的比较 , 会将Integer 拆箱成 int 类型的值再做比较,所以最终是两个int类型的比较. 而两个int类型比较的是字面值,所以结果为true
    */
    
    /*
    情形二:
    Integer i1 = 100;这句最终执行的代码是 Integer i1 = Integer.valueOf(100);
    此时查看Integer.valueOf(int)的源码:
    */
    public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
    }
    
    //继续查看IntegerCache的源码:
     private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];
    
            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
                if (integerCacheHighPropValue != null) {
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;
                //即相当于 new Integer[256];
                cache = new Integer[(high - low) + 1];
                int j = low;
                //最终cache[0] ~ cache[255] 等价于 -128 ~ 127
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
    
                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;
            }
    
            private IntegerCache() {}
        }
    
    
    /*
    也就是说, i1 与 i2 都是从IntegerCache 数据中取出,且内存地址一致.所以结果为true;
    */
    
    /*
    情形三:
    同上,仍然会调用Integer.valueOf()方法,由于不满足判断条件:
    */
    if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
    }
    /*
    所以 两次i3 ,i4 相当于分别执行了 new Integer(128); 而Integer 对象之间的比较,比较的是内存地址;
    由于new 了两次,所以内存地址不同,故结果为false;
    */
    
    /*
    情形四:
    参考情形二, i5,i6 相当于重新new了两次,内存地址不一致,故比较结果为false
    
    
    情形五:        
    同上,每次new 都会新开辟内存空间,故结果为false;
    
    */

    4.String s = "Hello", s = "World" 此时s的值已经改变了, 为什么还说String 是不可变的?

    答: 第一次 s = ''Hello'',相当于创建了 1个 String对象 ''Hello'' , 同时将该对象在内存中的引用地址赋给了变量s

    而当执行 s = ''Hello''时,同样也会创建一个String对象 "World", 将此对线的地址赋给 变量s ,并打破原来的引用关系

    也就是说,每次更改 s 的值都会新开辟一块内存空间,而不是修改原有内存空间中的值 ,所以说 String 是不可变的;

    如何验证?

    可以通过System.identityHashCode(obj)验证,该方法可以根据变量的内存地址计算出相应的hashCode值(官方API,并没有这么说):

    String s = "Hello";
    System.out.println(System.identityHashCode(s));
    //s = "World";
    System.out.println(System.identityHashCode(s));

    此时执行的结果为:

    1068824137
    1068824137

    不难发现,两次执行的结果是一致的,因此 s 引用的内存地址没有改变,  将上面的注释打开再执行,结果为:

    1068824137
    864237698

    可以发现,两次的值不一致,可以直观的说明.s的内存地址已经发生了改变

    除此之外,查看String 代码可以发现 String 是有 char[] 组成;

    private final char value[];//等价于  private final char[] value; 只是写法不同而已

    而char[] value 使用了private 与 final修饰 所以一旦初始化就不可以更改;

    通过 内存地址 与 String中的成员变量 足以说明String是不可变的;

    4.  String ,StringBuilder,StringBuffer 之间的区别是什么?

    从拼接速度上来说:StringBuilder > StringBuffer > String

    测试案例:

    @Test
        public void testAppend() {
            long start_time = 0;
            long end_time = 0;
    
    
            start_time = System.currentTimeMillis();
            StringBuffer sf = new StringBuffer("str");
            for (int j = 0; j < 100000; j++) {
                sf = sf.append("str");
            }
            end_time = System.currentTimeMillis();
            System.out.println("StringBuffer 拼接总耗时:"+(end_time - start_time)+"ms");
    
    
    
            /*start_time = System.currentTimeMillis();
            StringBuilder sb = new StringBuilder("str");
            for (int j = 0; j < 100000; j++) {
                sb = sb.append("str");
            }
            end_time = System.currentTimeMillis();
            System.out.println("StringBuilder 拼接总耗时:"+(end_time - start_time)+"ms");*/
    
    
            /*start_time = System.currentTimeMillis();
            String s = "str";
            for (int j = 0; j < 100000; j++) {
                s += "str";
            }
            end_time = System.currentTimeMillis();
            System.out.println("String 拼接总耗时:"+(end_time - start_time)+"ms");*/
        }

    同样执行10W次拼接str字符串,5次执行取平均耗时结果:

    StringBuffer :4.4ms

    StringBuilder:3.4ms

    String:16.4ms

    从结果来看 StringBuffer 与 StringBuilder的拼接速度远远大于String的拼接, 这是因为 StringBuffer与StringBuilder 的拼接是对同一对象进行操作,而String的拼接则是不停的开辟新的内存

    StringBuilder 与 StringBuffer 的差异会随着拼接次数的增大而增大,有兴趣的伙伴可以动手试一下

    从线程安全上来看:StringBuffer > StringBuilder

    原因:StringBuffer 的 append方法 都加上了 synchronized关键字,实现了方法的同步,多线程下是安全的,而StringBuilder中在多线程中可能出现数据不一致的情况

    5. java中重写是什么意思? 重写的原则是什么?

    答:重写是指复写父类中的方法

    原则: 1同 , 2小, 1大

    1同:方法签名相同,即方法名称,参数类型,参数个数完全相同

    2小:子类抛出的异常类型小于等于父类 , 子类的返回值类型小于等于父类

    1大:子类方法的访问权限大于等于父类

     6.什么是overload ,即方法的重载?

    类中存在多个,方法名称相同,但是参数列表不同(参数个数, 参数类型)的方法 .这些方法为重载关系

    重载的注意事项:

    1. 在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int,float),但是不能为fun(int,int));
    2. 不能通过访问权限、返回类型、抛出的异常进行重载;
    3. 方法的异常类型和数目不会对重载造成影响;
    4. 对于继承来说,如果某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

    7.Error 与 Exception 有什么区别? 运行时异常与受检异常有什么区别?

    答:

    Error 与 Exception 都是Throwable的子类:

    1.Error 表示是JVM无法处理的重大错误,例如:OutOfMemoryError  ;Error是我们无法处理的错误 ;

    2.Exception 可以分为两大类: Check(受检)异常,Runtime(运行时)异常:

      一般运行时异常指的是RuntimeException下的子类,除了RuntimeException外.其他的Exception都称之为受检异常

      2.1 受检异常:我们必须要在程序中去处理他,否则代码编译不通过;

        处理的方式有两种:①在方法声明时通过throws直接抛出 ②使用try ... catch ...代码块捕获处理

        常见的受检异常有:IOException

      2.2 运行时异常:程序不要求代码中一定要处理他,不会影响编译通过,只有在运行时的时候出现才会抛出;

        一般RuntimeException的出现,都是代码编写的逻辑不够严谨导致的;

        常见的RuntimeException有:

        Java.lang.ArithmeticException  (算数异常)

        Java.lang.NumberFormatException (数字格式化异常:;例如:Integer.parseInt("a");)

        Java.lang.ClassCastException(类型转换异常)

        Java.lang.IndexOutOfBoundsException(索引越界)
        Java.lang.NullPointerException(常见的空指针异常)

        以下列会触发上述异常的代码:

        

         //System.out.println(1 / 0);//java.lang.ArithmeticException: / by zero
    //int a = Integer.parseInt("a");//java.lang.NumberFormatException: For input string: "a"
    //new ArrayList<>().get(0);//java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

    String s = null; s.toString();//java.lang.NullPointerException

     

    8. ArrayList,Vector, LinkedList 的存储性能和特性的区别:

    对于查找,更改而言:

     ArrayList 与 Vector 基于Object数组,可以直接根据下标获取到元素,LinkedList 是基于链表的结构,获取某个元素,需要通过首尾一级级才可以找到

    对于增删而言:

    而Object数组 在指定索引位置插入 或 删除一个数据则需要将索引后面的所有元素索引进行加一,链表结构则比较擅长插入与删除

    结论: ArrayList,Vector 查找和更改速度优于 LinkedList , 增删速度则慢于LinkedList;

    ArrayList 与Vector的区别:

    安全性方面:

    Vector中的大部分方法都是synchronized的,所以Vector是线程安全的,ArrayList则不是

    速度方面:

    synchronized会增加性能的消耗,因此ArrayList处理速度是要快于Vector的

    如何选择:

    如果是多线程场景建议选择Vector,保证数据的安全;

    如果是单线程,没有线程安全的问题;则选择ArrayList更好

    9.HashMap ,Hashtable的区别?

    从两方面解答:

    1.安全性方面:Hashtable中的许多方法是synchronized的,而HashMap不是;

    2.速度方面:安全意味着性能的降低;

    3.key,value方面:HashMap的key,value 都可以为null ; 而 Hashtable中不允许为null,否则会抛出NullPointerException异常

    10. final, finally, finalize 的区别?

    虽然长得挺像,但是功能却差别很大,此题只需回答出各自的功能就行;

    final:

      修饰类:表示该类不可以被继承

      修饰方法:表示方法不可以被重写 

      修饰变量:如果修饰的是基本类型的变量,则表示该变量初始化后 值不可以再被更改;修饰引用类型的变量则初始化后,不能再指向别的对象

    finally:

      一般搭配 try - catch - finally 或 try - finally 使用;表示不管try-catch语句中发生了什么 ,执行完try - cathch后都会执行finally中的代码;

    finalize:

      是object中的一个protected方法,在GC(垃圾收集器)清理对象之前,会调用该对象的finalize方法

        

     11.java中 try - catch - finally 语句中如果都还有return,那么执行哪个return?

    12.在try-catch代码块中如果有return,那么在执行return之前会判断是否存在finally代码块?

    如果不存在:很好理解,程序未出异常则执行try中的return ,如果出了异常则执行catch中的return

    如果存在:再判断finally中是否有return语句

      如果没有return:等待finally执行完毕之后再执行 try 或 catch中的return

      如果有return:则不再使用try 或 catch中的return,直接使用finally中的return作为返回值;

    额外的,如果try-catch-finally中不管执行了谁的return, 程序都不再往后执行

    13.关于 new String("HelloWorld");总共创建了几个对象?

    对于String而言,通过new 关键字创建对象,是一定会在堆中创建1个对象;

    但java对String做了优化,除了堆中之外,还有一个String的常量池;

    每当创建String类型的对象时(不管是通过new的形式 还是 字面量的形式) ,都会首先在常量池中寻找是否已经有了"HelloWorld"这个对象

    如果有的话则不会在额外创建,如果没有的话,则会在常量池中创建1个新的对象

    所以答案是:最少创建1个新对象,可能创建1个或2个新对象;

    而String s = "HelloWorld" 则是 最少0个,可能创建1个新对象;

    更详细的可以参考这篇博客:new String()究竟创建几个对象?

    14.String中的实例方法,intern()作用是什么?

    答:intern方法的作用时,检查常量池中是否存在某个字符串,如果存在则该方法返回该字符串在常量池中的引用,否则将字符串添加到常量池中同时返回在常量池中的引用;

    String s = new String("hello") + new String("World");
    System.out.println(s == "helloWorld");//false 此时比较的是s在堆中的地址
    System.out.println(s.intern() == "helloWorld");//true 此时比较的是"helloWorld"在常量池中的地址

    更详细的intern()介绍,请参考该博客(提示:文章有一定长度,请耐心看):Java技术——你真的了解String类的intern()方法吗

    15.关于String类型的拼接

    如果是字面量值之间的拼接则会在编译阶段进行优化,例如:

    String s = "Hello" + "World"

    编译优化后:

    String s = "HelloWorld"

    如果不是字面量值的拼接,则会转为StringBuilder的append拼接,例如:

    String s = new String("Hello") + new String("World")

    这种情况,不会再进行编译优化;通过反编译可以看到:

    String s = (new StringBuilder("Hello")).append(new String("World")).toString();

    详情参考本文:关于字符串拼接的问题

  • 相关阅读:
    关于http请求头的一些事
    关于http抓取字段的一些事
    多线程的一些事
    正则的一些事
    出现问题集及解决方案
    常见的正则
    Http常见状态码
    list&tuple简记
    函数式编程--高阶函数--sorted
    函数式编程--高阶函数--filter
  • 原文地址:https://www.cnblogs.com/lzzRye/p/9452670.html
Copyright © 2020-2023  润新知