Bootstrap

Java 面试知识点合集

一、基础篇

1.1 java基础
1.面向对象的特征:封装、继承、多态

(1).封装:属性能够描述事物的特征,方法能够描述事物的动作。封装就是把同一类事物的共性(包括属性和方法)归到同一类中,方便使用。

封装的好处有: 隐藏数据及实现细节,对每个属性加以不同的修饰符可以防止对信息的错误改动,更加安全。 降低模块间的耦合度,以及代码复用。

举个栗子:有一个类,当需要使用时,只需要new一辆车,然后点击启动,前进,后退,就好。而不用知道车的轮胎多大,发动机型号,或者知道车是如何前进的。

(2).继承:Java继承是面向对象的最显著的一个特征。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一个父类,易于管理程序,父类是子类的一般化,子类是父类的特化(具体化)。

好处:提高代码复用率,以及易维护性。

举个栗子:有一个类叫动物,它有属性:腿的数量。有方法:可以叫。 新建一个他的子类,猫就自动拥有了上述属性集方法,你还可以添加一个新的属性:毛的颜色。 这样就不需要重新写一遍腿的数量,可以叫的代码。

(3).多态:不同类对象对相同行为的不同反应,我们就把它叫做多态。 与继承相对应的是多态提供了对同一类对象差异性的处理方法,子类通过多态重写从父类继承的方法来实现子类的差异性。

直接举个栗子:

class Animal{ 叫;}//叫是动物的一个方法  
class 猫 extend Animal{叫;}
class 狗 extend Animal{叫;}

Animal 狗 = new 狗();
Animal 猫 = new 猫();  

狗.叫();  
猫.叫();

复制

猫和狗同样是Animal,调用同一个方法却可以得到不同的叫声。

2.final,finally,finalize的区别

这个问题,,,我好想知道他们有什么联系。 final final 关键字可以用来修饰变量,方法,类。 用于变量时,此变量只能被赋值一次之后不能修改,且赋值必须在定义时或者构造方法中进行,常用来定义一个常量。 finally finally用于异常处理,在try-catch块之后,可以不存在,存在的时候无论是否捕获错误都会执行finally块中的代码,常用于资源的释放,即无论是否捕获错误,都要将一些连接,文件关闭,将资源释放掉。 finalize() finalize()是一个定义域Object类的方法,也就是所有的java类都继承了此方法,主要用于java虚拟机进行垃圾回收,删除此对象之前对此对象调用finalize()方法。

3.Exception、Error、运行时异常与一般异常有何异同

首先引入一张java异常相关类的类图:

首先,所有的javaException以及Error都继承自Throwable。

Error:表示程序无法处理的错误,大多数情况和程序员的代码无关,而是JVM相关的资源错误。发生此类错误时,程序无法继续,系统能做的只是尽力的安全退出程序。

Exception:可以被应用程序捕获及处理的。

先忘记上面的分类,java Exception通常可以分为checked exception(可查异常)(Exception 类下除RuntimeException之外)unchecked exception(不可查异常)(RuntimeException和Error)

可查异常:此类的异常是经常出现的,有迹可循甚至可查预测到可能会抛出的,因此编译器会强制要求我们去捕获/抛出它,显式的对其进行处理(不对其进行处理编译无法通过)。比如IOException和SQLException。 在读写一个文件时,我们可以想象会出现哪些情况的异常,比如文件不正常关闭,此时应该怎么处理,都有迹可循。所以我们可以在编写程序时就对此类异常作出处理。

不可查异常 Error情况严重,一般发成程序都会结束。 RuntimeException:运行时异常,RuntimeException及其子类,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。(此类异常不try-catch,不 throw 也可以通过编译。) 比如空指针异常,我们也可以在每个可能引发空指针的地方捕获处理,但显然是不科学的,我们应该设计严谨的逻辑使得程序不会出现此类异常。

4.请写出你最常见到的5种运行时异常
  • NullPointerException - 空指针引用异常
  • ClassCastException - 类型强制转换异常。
  • IllegalArgumentException - 传递非法参数异常。
  • ArithmeticException - 算术运算异常
  • IndexOutOfBoundsException - 下标越界异常
  • NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常.(属于IllegalArhumentException的子类)
5.java的基础类型

java的基础类型,其实多用一点很自然的就记住了,但是如果你还没有记住,建议在面试前一定要背过去,因为如果这个都不会,真的是很尴尬。。 整型:byte,int,short,long。 浮点型:float,double。 字符:char。 布尔:boolean。

6.Integer和int的区别,Integer的缓存范围

在java中,有八种基础类型,详见上一条。 而java是一种面对对象的语言,很多时候不可以用基础数据类型而需要一个类。因此加入了Integer,Boolean等包装类。 区别: 1.int是一个基础数据类型,Integer是一个包装类,是一个对象。 2.类初始化时,int的值为0,Integer的值为null。 当你只是需要一个值来进行一些加减乘除的运算及作为参数传递一下,那么可以定义为int,如果需要对其做一些类型的转换,尽量定义为一个Integer,因为java的对象会继承以及拥有一些自己的方法,使用起来更加方便。 Integer的缓存问题:

 public static void compare1(){
         Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128;
         System.out.println(i1  == i2);
         System.out.println(i1.equals(i2));
         System.out.println(i3  == i4);
         System.out.println(i3.equals(i4));
     }

复制

输出结果为: true,true,false,true。

当时用Integer i= 127这种操作时,会进行装箱操作,即发生Integer i = Integer.valueOf(127). 当值处于(-128,127)之间时,不会去重新new一个Integer,而是从缓存中取。因此在此范围之内的Integer,使用 == 判断时也会返回true,因为他们本身就是同一个对象(缓存中)。而equal方法就是判断其值是否相等,返回true,并没有问题。 为了避免造成错误,可以使用Integer i = new Integer(127),这样就不会出现因为java缓存导致的问题。

7.包装类,装箱与拆箱

包装类:java是一个面对对象的语言,但是java中的基本数据类型却不是面对对象的,为了弥补这一点,对每一个基本数据类型设计了一个与之相对应的类型,这个类型就是包装类。八种基本数据类型与对应的包装类为:

  • int ->Integer
  • short -> Short
  • long -> Long
  • byte -> Byte
  • float -> Float
  • double -> Double
  • char -> Character
  • boolean -> Boolean

包装类共同的方法

  • 带有基本值参数并创建包装类对象的构造函数。如利用Integer包装类创建对象,Integer obj=new Integer(145);
  • 带有字符串参数并创建包装类对象的构造函数.如:new Integer(“-45.36”);
  • 可生成对象基本值的typeValue方法,如:obj.intValue();
  • 将字符串转换为基本值的parseType方法,如:Integer.parseInt(args[0]);
  • 生成哈稀表代码的hashCode方法,如:obj.hasCode();
  • 对同一个类的两个对象进行比较的equals()方法,如:obj1.eauqls(obj2);
  • 生成字符串表示法的toString()方法,如:obj.toString().

装箱与拆箱

装箱是将基本类型转换为对应的包装类,拆箱反之。

在java1.5之前,如果你需要将一个基本类型放入集合(Collection)中,需要先将其转化为对象,然后将转化后的对象放入集合中,这样操作十分繁琐。因此,在java1.5中引入了自动装箱与拆箱操作。

何时发生自动装箱与拆箱?

当需要的是一个对象而传入的是一个基本类型时(或者反之)。

最经典的就是在处理Collection时:

	//在这个过程中发生了装箱与拆箱
    List<Integer> integerList = new ArrayList<>();
    integerList.add(3);//autoboxing
    int i = integerList.get(0);//unboxing

复制

此外还有 赋值时:

Integer i = 3;
int j = i;

复制

方法调用时:

	//尽量自己注意声明类型,这种代码会产生很多不必要的装箱与拆箱,耗费资源,降低程序性能。
  show(3);

  int show(Integer i ){
    return i;
  }

复制

在自动装箱与拆箱的过程中,有哪些问题需要注意呢?

1.equals 与 == 的区别。 equals方法是可以重写的,也就是说具体怎么定义‘相等’可以由你设定,而 == 用在基础类型是判定其值是否相等,用在对象是判定比较双方是否为同一个对象。这里需要注意一下Integer的缓存问题,详见Integer的缓存问题

2.拆箱过程中的空指针问题

当将包装类与原始类型比较时,如果未初始化包装类,在拆箱过程中调用obj.xxxvalueU时会发生空指针异常。

  public static Integer i ;
  	//在方法中,方法省去了。
    if (i > 10){
      System.out.println("huyanshi");
    }

复制

以上代码运行时会抛出NullPointException。

3.创建很多无用的对象,为GC增加压力。

Integer sum = 0;

 for(int i=1000; i<5000; i++){

   sum+=i;

}

复制

这个操作中,+操作并不适用于Integer对象,因此会进行自动拆箱,而拿到+的结果后又会装箱为Integer。在这个过程中,会创建几千个无用的对象,降低程序的性能。因此应尽量避免。

总的来说,自动装箱和拆箱着实为开发者带来了很大的方便,但是在使用时也是需要格外留意,避免引起出现文章提到的问题。

8.String,StringBuilder,StringBuffer

这个题目我单独写了一篇博文用来分析,见2018-09-09-String-StringBuilder-StringBuffer异同.md</>

9.重写和重载的区别

其实我们要明白,这两个根本不是相似的概念,只是名字相近罢了。

重写

  1. 重写发生在继承关系当中,当父类中指定了一个方法,子类可以更加具体的重写他。 例如:
//动物有叫的方法,接受次数n,输出n次叫声
public class Animal {

  void noise(int n ){
    for (int i =0 ;i < n;i++){
      System.out.print("noise");
    }
  }

  //新建一个动物,指向一条狗,让他叫,得到结果为汪汪汪
  public static void main (String [] args){
    Animal animal = new Dog();
    animal.noise(10);
  }

}

//狗是一种动物,具体的实现了叫声为汪汪汪
class Dog extends Animal {
  void noise(int n){
    for (int i =0 ;i < n;i++){
      System.out.print("wang");
    }

  }
}

复制

在该例中,Dog类重写了Animal类的方法,将叫声更为具体化,当拿到一条狗,我们只需要知道它是个动物,就可以让他叫,叫声依它自己的实现而定。

重载

重载发生在一个类中,可以使用两个名字相同的方法,来对不同的输入进行不同的反应,这就是重载。

例如:

//人,比较聪明
public class Person {

  //当你传入数字,人就会根据你的数字发出hHA的声音
  void noise(int n) {
    for (int i = 0; i < n; i++) {
      System.out.print("HA");
    }

  }

  //当你传入字符串,人就会把他念出来
  void noise(String noise) {
    System.out.print(noise);
  }

  public static void main(String[] args) {
    Person person = new Person();
    person.noise(10);
    person.noise("HEHE");
  }
}

复制

上面两个例子说明了重载和重写在日程编码过程中的应用。

接下来说一下他们各自的限制。

重写的限制

(一) 父类方法的参数列表必须完全与被子类重写的方法的参数列表相同,否则不能称其为重写而是重载。

(二) 父类的返回类型必须与被子类重写的方法返回类型相同,否则不能称其为重写而是重载。

(三) Java中规定,被子类重写的方法不能拥有比父类方法更加严格的访问权限。

(四) 在继承过程中如果父类当中的方法抛出异常,那么在子类中重写父类的该方法时,也要抛出异常,且只能抛出父类异常的子类,即:异常不能大于父类。

重载的限制

(一)在使用重载时只能通过不同的参数列表,必须具有不同的参数列表。

(二)不能通过访问权限、返回类型、抛出的异常进行重载。

(三)方法的异常类型和数目不会对重载造成影响。

(四)可以有不同的返回类型,只要参数列表不同就可以了。

(五)可以有不同的访问修饰符。

(六)可以抛出不同的异常。

尤其注意:参数的数量,顺序,类型才可以构成重载,仅有返回值不同时不构成重载

共性

;