每日一面系列之Java基础(上)

1.你知道运算符&和&&,|和||的区别吗?(考察短路运算符和位运算)

&按位与操作,只有对应的两个二进制数都为1的时候,结果才为1。
例:1 & 1 = 1,1 & 0 = 0,0 & 1 = 0,0 & 0 = 0
|按位或操作,只要对应的两个二进制数中其中一个为1的时候,结果就为1。
例:1 | 1 = 1,1 | 0 = 1,0 | 1 = 1,0 | 0 = 0

&和&&都可以表达和(并且)的意思,区别在于使用&时,两边都要参与运算,而使用&&的时候,只要左边是false右边就不会参与运算,判断语句中推荐使用&&,效率更高。

|和||都可以表达或(或者)的意思,区别在于使用|时,两边都要参与运算,而使用||的时候,只要左边是true右边就不会参与运算。因此&&和||被称为短路运算符。

2.计算2乘以8最有效的方法是什么?(考察位运算)

将一个数左移n位,相当于乘以2的n次方,位运算是CPU直接支持的,所以效率比较高。2<<3。

常见的JDK源码中HashMap的初始化容量为16,具体计算如下:
int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
1左移4位也就是10000,转化为十进制就是16,直接以二进制进行计算,运算效率更高。

3.讲一下Java数据类型分类。(考察Java数据类型的划分)

基本数据类型:byte、short、int、long、float、double、char、boolean
引用数据类型:其他的都是引用数据类型,包括自己创建的对象。(String和Enum是引用数据类型)

4.==和equals有什么区别?(考察值与内存地址的比较)

==比较两个对象的内存地址是否相等,也就是判断两个对象是否为同一个对象,基本数据类型使用它比较的是值,引用数据类型使用它比较比较的是内存地址。

equals也可以判断两个对象是否相等,如果没有重写equals方法,那么通过equals方法比较两个对象时,等同于使用“==”比较这两个对象。如果重写了equals方法,一般我们都会重写equals方法来比较两个对象的值(内容),如果值相等则返回true,表示这两个对象相等。

举个例子:

public class test1 {
    public static void main(String[] args) {
        String a = new String("ab"); // a 为一个引用
        String b = new String("ab"); // b为另一个引用,对象的内容一样
        String aa = "ab"; // 放在常量池中
        String bb = "ab"; // 从常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false,非同一对象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}

说明:String类中的equals方法是被重写过的,因为object中的equals方法比较的是两个对象的内存地址,而String中的equals方法比较的是两个对象的值(内容)。

5.下面代码中的try-catch-finally语句,try里面有return,finally里面也有return,那么执行结果是什么?为什么?(考察try-catch-finally语句块的执行)

public static int test1() {
 	int a = 1;
 	try {
 		System.out.println(a / 0);
	 	a = 2;
 	} catch (ArithmeticException e) {
	 	a = 3;
 		return a;
 	} finally {
 		a = 4;
 	}
 	return a;
 }
//执行结果为3
 public static int test2() {
 	int a = 1;
 	try {
 		System.out.println(a / 0);
 		a = 2;
 	} catch (ArithmeticException e) {
 		a = 3;
 		return a;
 	} finally {
		 a = 4;
 		return a;
	 }
 }
 //执行结果为4

在执行try-catch中的return之前一定会先执行finally中的代码(如果finally存在),如果finally中有return语句,就会直接执行finally中的return,如果finally中没有return(return在try或者catch中),则会先确定return返回值,然后再执行finally中的代码,最后再执行return。

7.讲一下重写与重载的区别,构造器Constructor是否可以被override?(考察重载与重写)

重载:发生在同一个类中,方法名必须相同,参数列表不同,返回值和访问修饰符可以不同,发生在编译时。

重写:发生在父子类中,方法名、参数列表必须相同,返回值范围必须小于等于父类,抛出异常范围必须小于等于父类,访问修饰符范围必须大于等于父类,如果父类方法访问修饰符为private则子类不可以重写该方法。

因为父类的私有属性和构造方法不能被继承,所以Constructor不可以被override(重写),但是可以被overload(重载),所以可以看到一个类中有多个构造函数的情况。

8.谈谈Java面向对象的四大特性。(考察对面向对象OOP思想的理解)

抽象:关键词abstract修饰的类叫做抽象类,abstract声明的方法叫做抽象方法,一个类包含一个或多个抽象方法,则该类就是抽象类,抽象方法属于一种特殊的方法,只有一个声明,没有方法体。

封装:封装就是把一个对象的属性私有化,同时提供一个方法让外界访问可以访问的属性,如果属性不想背外界访问,可以不提供外界访问属性的方法。但是一个类如果没有给外界提供访问任何属性的方法,那么这个类也就没有了存在的意义。

继承:继承就是使用现有的类(父类)定义新类(子类),子类可以使用父类的方法、属性,子类也可以增加新的方法、属性。但是不能选择性的继承父类,使用继承能够让我们更加方便的是同以前的代码,继承拥有以下3个重要的特性:

  1. 子类拥有父类所有的属性和方法(包括私有属性和私有方法),但是父类中私有的属性和方法子类是不可以访问的,只有拥有权。
  2. 子类可以拥有自己的属性和方法,可以对父类进行扩展。
  3. 子类可以使用自己的方式实现父类的方法(重写)。

多态:多态是同一个行为具有不同表现形式和形态的能力,具体体现就是同一个接口使用不同的实例而执行不同的操作。多态存在的三个必要条件是继承、重写、父类引用只想子类对象。Java中有两种方式可以实现多态:继承(多个子类对父类的同一个方法进行重写)和接口(实现接口并且覆盖接口中的同一个方法)。

9.接口和抽象类的区别是什么?抽象类中必须有抽象方法吗?抽象类能使用final修饰吗?(考察接口与抽象类)

接口与抽象类的区别:

  1. 接口的方法默认是public,接口中的方法不能有具体的实现(Java8中可以再接口中定义方法的默认实现,也就是可以定义静态方法,外界可以直接使用接口名进行调用,实现类和实现是不可以调用的,如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错),抽象类中可以有非抽象的方法。
  2. 接口中的实例变量默认是final修饰的,抽象类中则不一定。
  3. 一个类可以实现多个接口,但是最多实现(继承)一个抽象类。
  4. 一个类如果实现一个接口,需要实现该接口的所有方法,而抽象类不一定。
  5. 接口不能用new关键字实例化,但是可以声明,必须引用一个实现该接口的对象。从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的一种抽象,是对行为的规范。

抽象类不一定包含抽象方法,但是有抽象方法的类一定是抽象类。

抽象类不能是用final修饰,被final修饰的类不可以被继承,final类中的成员方法都会被隐式的指定为final方法,这就违背了抽象类存在的意义。

10.String str = new String(“Jesse”);创建了几个对象?String str1 = "abcd"与String str2 = new String(“abcd”)一样吗?str1和str2相等吗?(考察对象的创建)

问题1:
如果常量池中存在“Jesse”这个String对象,那么就在堆中创建String对象str,然后指向“Jesse”对象,一共创建了一个对象。
如果常量池中不存在“Jesse”这个String对象,那么先在常量池中创建String对象“Jesse”,然后再堆中创建一个String对象str,再指向“Jesse”对象,一共创建了两个对象。

问题2:
这两种不同的创建方式是有一定的差别的。

第一种方式:可能创建一个或者不创建对象。如果“abcd”这个String对象再常量池中不存在,则会在常量池中创建一个String对象“abcd”,然后str1指向“abcd”的内存地址,无论以后用这种方式创建多少个值为”abcd”的字符串对象,始终只有一个内存地址被分配,之后的都是String的拷贝,Java中称为“字符串驻留”,所有的字符串常量都会在编译之后自动地驻留。

第二种方式:至少会创建一个对象,也可能是两个对象。因为使用到了new关键字,所以必然会在堆中创建一个String对象“str2”,同时会看“abcd”这个String在不在常量池中,如果在就直接指向,如果不在就先创建再指向。

问题3:
str1和str2不相等。因为一个是堆内存中的String对象,一个是常量池中的String对象,内存地址不同。

11.String、StringBuffer和StringBuilder的区别是什么?分别在那些场景下使用?String为什么是不可变的?(考察字符串相关构建类的使用和区别)

问题1:

  1. String对象是不可变的,每次对String类型对象进行改变的时候都会创建一个新的String对象,然后将指针指向新的String对象,而StringBuffer和StringBuilder是可变的,每次改变都是对对象本身进行修改。
  2. StringBuffer对方法或者调用它的方法加了同步锁(synchronized),所以是线程安全的。
  3. StringBuilder不需要加锁,线程不安全,效率更高。

问题2:
操作少量的数据使用String。
单线程下操作字符串缓冲区下大量数据使用StringBuilder。
多线程下操作字符串缓冲区下大量数据使用StringBuffer。

问题3:String类中使用final关键字修饰字符数组来保存字符串。而StringBuilder和StringBuffer都是继承与AbstractStringBuilder类,AbstractStringBuilder类中的字符串数组是没有被final修饰的,所以StringBuffer和StringBuilder是可变的。

12.是否了解JDK8中接口的新特性?(考察JDK8新特性)

1. 接口中可以有静态方法,但是方法必须有实现体,该方法属于接口,可以使用接口名调用该方法。
2. 接口中新增default关键字修饰的方法,default方法只能定义在接口中,子类或者子接口中可以重新default方法且必须有方法体。
3. 父接口中的default方法如果在子类或者子接口中被重写,那么子类或者子接口调用该方法时以重写的方法为准。
4. 本类、接口如果没有重写父类的default方法,则在调用default方法的时候,使用父类(接口)中定义的default方法。

13.什么是hashCode?你重写过hashCode和equals吗?为什么重写equals方法时必须重写hashCode方法?(考察hashCode和equals的关系)

问题1:
hashCode()方法的作用就是获取哈希码,也成为散列码,该方法的返回值是一个int类型的整数,这个哈希码的作用是确定该对象再哈希表中的索引位置。

问题2&问题3:
如果两个对象相等,则hashCode一定是相等的。对两个相等的对象调用equals方法返回结果为true。两个具有相同hashCode的对象不一定相等,因为不同的对象也可能会产生相同的hashCode,这是概率性问题,hashCode的默认行为是对堆上的对象产生独特值,如果没有重写hashCode方法,则该class的两个对象无论如何都不会相等,即使这两个对象指向相同的数据。说白了就是为了保证equals比较之后相同的两个对象的hashcode值也相等。
**

14.什么是反射机制?反射机制的有缺点是什么?反射机制的应用场景有哪些?(考察Java反射机制)

问题1:
Java反射机制是指在运行状态中,对于任意一个类,都能够知道它的属性和方法,对于任意一个对象,都能够调用它的任意属性和方法,这种动态获取类信息以及调用对象方法的功能在Java语音中成为反射机制。

问题2:
优点:运行期类型的判断,动态加载类,提高代码的灵活性。
缺点:反射相等于一系列解释性操作,是直接通知JVM需要做什么事情,要比直接执行的Java代码再性能上面慢很多。

问题3:
反射是框架设计的灵魂,在日常开发中基本上很少会直接用到反射。除了日常所用的框架用到了反射,在我们进行模块化开发,通过反射调用对应字节码,动态代理设计模式也用到了反射。比如我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库驱动程序,Spring框架的XML哦诶之模式也用到了反射。

15.什么是值传递?什么是引用传递?为什么Java中只有值传递?(考察对象的传递)

问题1&问题2:
值传递:在调用函数时将实际参数的值复制一份传递到函数中,函数中接受到的参数只是实际参数的一个副本,这样再函数中如果对参数进行了修改,也不会影响实际参数的值。

引用传递:在调用函数时将实际参数的内存地址传递给函数,这样再函数中如果对参数进行修改,就会影响实际参数的值。

总结:值传递就是接收方接收到的是调用者提供的值,引用传递就是接收方接受到的是调用者提供的参数内存地址,一个方法可以修改传递引用锁对应的变量值,不能修改传递值对用的变量值。

问题3:
Java程序设计语言总是按照值传递的方式来传递参数,也就是说方法得到的参数是实际参数值的一个拷贝,方法不能修改传递给它的参数的实际值。在Java中对对象的引用实际上值对对象的值的传递。

在Java中方法参数有以下三种情况:

  1. 一个方法不能修改一个基本数据类型的参数。
  2. 一个方法可以改变一个对象参数的状态。
  3. 一个方法不能让对象参数引用一个新的对象。

16.为什么从上家公司离职?(一般HR必问)

  1. 千万不能说上家公司的不好,比如常加班、不涨工资、公司制度混乱等负面情绪。
  2. 可以说搬家了,上家公司距离比较远。
  3. 想追求更高的生活品质。
  4. 对自己有更高的要求,希望自己能够在新的平台得到提升。
  5. 个人职业规划,想在某个领域(传统CRM、电商、ERP项目等)发展等。

每日一面,终成面霸!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章