● 请你讲讲数组(Array)和列表(ArrayList)的区别?什么时候应该使用Array而不是ArrayList?


考察点:Array


参考回答:

Array和ArrayList的不同点:


  • Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
    • ArrayList 存储基本类型时,会自动装箱成对应的包装类,只存其引用,而不能存基本类型!
  • Array 大小是固定的,ArrayList 的大小是动态变化的(动态扩容数组)。
  • ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。

扩展: ​​ArrayList源码分析​

● 请你解释什么是值传递和引用传递?


考察点:JAVA引用传递


参考回答:


  • 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量;
  • 引用传递一般是对于对象(引用)型变量而言的,传递的是该对象地址的一个副本,,并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象;
  • 一般认为,java 内的传递都是值传递;

案例:

public class StringBase {

public static void main(String[] args) {
int c = 66; //c 叫做实参
String d = "hello"; //d 叫做实参

StringBase stringBase = new StringBase();
stringBase.test5(c, d); // 此处 c 与 d 叫做实参

System.out.println("c的值是:" + c + " --- d的值是:" + d);// c的值是:66 --- d的值是:hello
}

public void test5(int a, String b) { // a 与 b 叫做形参
a = 55;
b = "no";
}
}

可以看出通过方法传递后,int 类型与 String 类型的原值并没有受到前面 test5 方法执行后的影响,还是输出了原值。这种形为通常被说成值传递。如果原值经过 test5 方法后被改变了,这种形为通常被描述为引用传递

● 请你解释为什么会出现4.0-3.6=0.40000001这种现象?


考察点:计算机基础


参考回答:

原因简单来说是这样:2进制的小数无法精确的表达10进制小数,计算机在计算10进制小数的过程中要先换为2进制进行计算,这个过程中出现了误差。

● 请你讲讲一个十进制的数在内存中是怎么存的?


考察点:计算机基础


参考回答:

补码的形式。

● 你知道java8的新特性吗,请简单介绍一下


考察点:java8


参考回答:


  • Lambda表达式:允许把函数作为一个方法的参数(函数作为参数传递进方法中。
  • Stream流:函数式编程,流式计算
  • 默认方法:默认方法就是一个在接口里面有了一个实现的方法。​​​
  • 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。​​​

默认方法举例:

// 接口
public interface MyInterface {

// 抽象方法
String abstractMethod();

// 默认方法
default String defaultMethod(){
return "MyInterface---->defaultMethod";
}

// 静态方法
static String staticMethod(){
return "MyInterface---->staticMethod";
}
}

// 接口实现类
public class MyInterfaceImpl implements MyInterface{

// 实现接口中的抽象方法
@Override
public String abstractMethod() {
return "MyInterface---->abstractMethod";
}
}

// 测试
public class MyInterfaceTest {

public static void main(String[] args) {

MyInterfaceImpl myInterface = new MyInterfaceImpl();

// 接口实现类调用接口中的默认方法
System.out.println(myInterface.defaultMethod());// MyInterface---->defaultMethod

// 接口实现类调用接口中的抽象方法
System.out.println(myInterface.abstractMethod());// MyInterface---->abstractMethod

// 直接调用接口静态方法
System.out.println(MyInterface.staticMethod());// MyInterface---->staticMethod
}
}

方法引用代码举例:


  • 引用静态方法: ​​类名::静态方法名​
  • 引用某个对象的方法: ​​对象::实例方法​
  • 引用特定类型的方法:​​特定类::实例方法​
  • 引用构造方法: ​​类名::new​

public class Test04 {
public static void main(String[] args) {

// 引用静态方法: 类名::静态方法名
Inter1<Integer, String> inter1 = String :: valueOf;
String str1 = inter1.m1(100);
System.out.println(str1); // 100

// 引用某个对象的普通方法: 对象::实例方法
Inter2<String> inter2 = "HELLO" :: toLowerCase;
String str2 = inter2.m2();
System.out.println(str2); // hello

// 引用特定类型的方法: 特定类::实例方法
Inter3<String> inter3 = String :: compareTo;
int res = inter3.m3("aa", "bb");
System.out.println(res); // -1

// 引用构造方法: 类名::new
Inter4<Book> inter4 = Book :: new;
Book b = inter4.m4("java编程入门", 25.36);
System.out.println(b); // Book{name='java编程入门', price=25.36}
}
}

interface Inter1<T, E> {
public E m1(T t);
}

interface Inter2<T> {
public T m2();
}

interface Inter3<T> {
public int m3(T t1, T t2);
}

interface Inter4<T> {
public T m4(String s, double d);
}

class Book {
String name;
double price;

public Book() {

}
public Book(String name, double price) {
this.name = name;
this.price = price;
}

@Override
public String toString() {
return "Book{" + "name='" + name + '\'' +
", price=" + price + '}';
}
}

● 请你说明符号“==”比较的是什么?


考点:基础


参考回答:


  • 如果​​==​​两边是基本类型,就比较数值是否相等。
  • 如果​​==​​两边是对象类型,就对比两个对象基于内存引用,如果两个对象的引用完全相同(指向同一个对象)时返回true,否则返回false。

● 请你解释Object 中的 hashCode()是如何计算出来的?


考点:基础


参考回答:

Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法直接返回对象的内存地址。

● 请你解释为什么重写equals还要重写hashcode?


考点:java基础


参考回答:

equals只是判断对象属性是否相同,hashcode要判断二者地址是否相同。如果要判断两个对象是否相等,需要同时满足​​地址 + 属性​​都相同!

​​


  • 如果两个对象相同(即:用 equals 比较返回true),那么它们的 hashCode 值一定要相同;
  • 如果两个对象的 hashCode 相同,它们并不一定相同(即:用 equals 比较返回 false);

举例子:

只重写 equals() 方法,不重写 hashcode() 方法:

public class Student {
private String name;
private int age;

public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
// 省略 get,set方法...
}

执行下面的程序看看效果:

public class hashTest {
@Test
public void test() {
Student stu1 = new Student("Jimmy",24);
Student stu2 = new Student("Jimmy",24);

System.out.println("两位同学是同一个人吗?"+stu1.equals(stu2));
System.out.println("stu1.hashCode() = "+stu1.hashCode());
System.out.println("stu1.hashCode() = "+stu2.hashCode());
}
}

执行结果:

两位同学是同一个人吗?true
stu1.hashCode() = 379110473
stu1.hashCode() = 99550389

如果重写了 equals() 而未重写 hashcode() 方法,可能就会出现两个没有关系的对象 equals 相同(因为equal都是根据对象的特征进行重写的),但 hashcode 不相同的情况。因为此时 Student 类的 hashcode 方法就是 Object 默认的 hashcode方 法,由于默认的 hashcode 方法是根据对象的内存地址经哈希算法得来的,所以 stu1 != stu2,故两者的 hashcode 值不一定相等。

根据 hashcode 的规则,两个对象相等其 hash 值一定要相等,矛盾就这样产生了。上面我们已经解释了为什么要使用 hashcode 算法,所以即使字面量相等,但是产生两个不同的 hashCode 值显然不是我们想要的结果。

2. 如果我们在重写 equals() 时,也重写了 hashCode() 方法:

public class Student {
private String name;
private int age;

public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
// 省略 get,set方法...
}

执行结果:

两位同学是同一个人吗?true
stu1.hashCode() = 71578563
stu1.hashCode() = 71578563

从 Student 类重写后的 hashcode() 方法中可以看出,重写后返回的新的 hash 值与 Student 的两个属性是有关,这样就确保了对象和对象地址之间的关联性。

一句话:实现了“两个对象 equals 相等,那么地址也一定相同”的概念!