阿里巴巴实习生初面面经

自我介绍

Java基础

1.Java的基本数据类型有哪些,包装类有哪些?知道自动装箱和拆箱吗?

  • 4类8种基本数据类型。4整数型,2浮点型,1字符型,1布尔型
数据类型 存储需求 取值范围 对应包装类
byte 8位 最大存储数据量是255,存放的数据范围是-128~127之间 Byte
short 16位 最大数据存储量是65536,数据范围是-32768~32767之间 Short
int 32位 最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1 Integer
long 64位 最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1 Long
float 32位 数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F Float
double 64位 数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加 Double
boolean 只有true和false两个取值 Boolean
char 16位 存储Unicode码,用单引号赋值 Character

引用数据类型

  • 类(class)、接口(interface)、数组
  • 自动装箱和拆箱
    • 基本数据类型和它对应的封装类型之间可以相互转换。自动拆装箱是jdk5.0提供的新特特性,它可以自动实现类型的转换
    • 装箱:从基本数据类型到封装类型叫做装箱
    • 拆箱:从封装类型到基本数据类型叫拆箱
1
2
3
4
5
6
7
jdk 1.5
public class TestDemo {
public static void main(String[] args) {
Integer m =10;
int i=m;
}
}

​ 上面的代码在jdk1.4以后的版本都不会报错,它实现了自动拆装箱的功能,如果是jdk1.4,就得这样写了

1
2
3
4
5
6
7
jdk 1.4
public class TestDemo {
public static void main(String[] args) {
Integer b = new Integer(210);
int c = b.intValue();
}
}

2.5个节点的二叉树有几种形态?

​ 5种(纸上画一下就ok了)

3.死锁的四个必要条件

  • 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
  • 请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。
  • 非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。
  • 循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。

4.GC垃圾回收机制(待完善)

​ 待完善

5.ArrayList和LinkedList的区别

  • 1、ArrayList和LinkedList可想从名字分析,它们一个是Array(动态数组)的数据结构,一个是Link(链表)的数据结构,此外,它们两个都是对List接口的实现。前者是数组队列,相当于动态数组;后者为双向链表结构,也可当作堆栈、队列、双端队列

  • 2、当随机访问List时(get和set操作),ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。

  • 3、当对数据进行增加和删除的操作时(add和remove操作),LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。

  • 4、从利用效率来看,ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。

  • 5、ArrayList主要控件开销在于需要在lList列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。

6.一个无序的数组,递归方式,不用排序算法如何排序?(待完善)

7.重载和重写

  • 重载和重写,如何确定调用哪个函数
    • 重载:重载发生在同一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载。
    • 重写:重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。根据不同的子类对象确定调用的那个方法。

8.final和static

final

  • 1. 数据
    • 声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
    • 对于基本类型,final 使数值不变;
    • 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
1
2
3
4
final int x = 1;
// x = 2; // cannot assign value to final variable 'x'
final A y = new A();
y.a = 1;
  • 2. 方法

    ​ 声明方法不能被子类覆盖。

    • private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是在子类中定义了一个新的方法。
  • 3. 类

    • 声明类不允许被继承。

static

  • 1. 静态变量

    ​ 静态变量在内存中只存在一份,只在类初始化时赋值一次。

    • 静态变量:类所有的实例都共享静态变量,可以直接通过类名来访问它;
    • 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
1
2
3
4
public class A {
private int x; // 实例变量
public static int y; // 静态变量
}
  • 2. 静态方法

    静态方法在类加载的时候就存在了,它不依赖于任何实例,所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。

  • 3. 静态语句块

    静态语句块在类初始化时运行一次。

  • 4. 静态内部类

    内部类的一种,静态内部类不依赖外部类,且不能访问外部类的非静态的变量和方法。

  • 5. 静态导包

1
import static com.xxx.ClassName.*

在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。

  • 6. 变量赋值顺序

    静态变量的赋值和静态语句块的运行优先于实例变量的赋值和普通语句块的运行,静态变量的赋值和静态语句块的运行哪个先执行取决于它们在代码中的顺序。

1
public static String staticField = "静态变量";
1
2
3
static {
System.out.println("静态语句块");
}
1
public String field = "实例变量";
1
2
3
{
System.out.println("普通语句块");
}

​ 最后才运行构造函数

1
2
3
public InitialOrderTest() {
System.out.println("构造函数");
}

存在继承的情况下,初始化顺序为:

  • 父类(静态变量、静态语句块)
  • 子类(静态变量、静态语句块)
  • 父类(实例变量、普通语句块)
  • 父类(构造函数)
  • 子类(实例变量、普通语句块)
  • 子类(构造函数)

9.平时怎么调试程序,Gstack了解吗?(待完善)

10.&&与&,||与|的区别

​ (1)&&和&都是表示与,区别是&&只要第一个条件不满足,后面条件就不再判断。而&要对所有的条件都进行判断。

1
2
3
4
5
6
7
8
9
10
// 例如:
public static void main(String[] args) {
if((23!=23)&&(100/0==0)){
System.out.println("运算没有问题。");
}else{
System.out.println("没有报错");
}
}
// 输出的是“没有报错”。而将&&改为&就会如下错误:
// Exception in thread "main" java.lang.ArithmeticException: / by zero
  • 原因:
    • &&时判断第一个条件为false,后面的100/0==0这个条件就没有进行判断。

    • &时要对所有的条件进行判断,所以会对后面的条件进行判断,所以会报错。

(2)||和|都是表示“或”,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。 看下面的程序:

1
2
3
4
5
6
7
8
public static void main(String[] args) {  
if((23==23)||(100/0==0)){
System.out.println("运算没有问题。");
}else{
System.out.println("没有报错");
}
}
// 此时输出“运算没有问题”。若将||改为|则会报错。
  • 原因
    • ||判断第一个条件为true,后面的条件就没有进行判断就执行了括号中的代码
    • 而|要对所有的条件进行判断, 所以会报错

11.Java中的引用和C++中的指针有什么区别

  • 类型:引用是地址的数据元素,可以转换成字符串查看,不用关心它的长度;指针是一个装地址的变量,它的长度是固定的,一般是一个计算机字长
  • 所占内存:引用申明时没有实体,不占用空间;指针申明后用到才会赋值,用不到不会分配内存
  • 类型转换:引用类型转换可能不成功,运行时会抛出异常或者编译不通过;指针只是个内存地址,但可能所指的地址不是程序想要的
  • 初始值:引用初始值是null;指针是int类型,如果不初始化指针,它的值就会不固定,这样很危险
  • 计算:引用是不能进行计算的;指针可以,如自加或自减,可以代替数组下标
  • 控制:引用不可以计算,所以它只存在自己程序中,可以被控制;指针是内存地址,可以计算,所以它有可能指向一个不属于自己程序使用的内存地址,对于其他程序是很危险的,对自己而言也是不容易控制的
  • 内存泄漏:引用不会引起内存泄漏;指针容易产生内存泄漏,所以要及时回收
  • 作为参数:函数内交换两个引用是没有意义的;指针可以作为参数给函数使用