Java 基础面试系列-05

2022年7月17日
大约 21 分钟

Java 基础面试系列-05

1. Java 中常见的 Exception 和 Error 有哪些对象?

NegativeArrayException:数组负下标异常。

EOFException:文件已结束异常。

FileNotFoundException:文件未找到异常。

NumberFormatException:字符串转换为数字异常。

SQLException:操作数据库异常。

IOException:输入输出异常。

AbstractMethodError:抽象方法错误。当应用试图调用抽象方法时抛出。

AssertionError:断言错。用来指示一个断言失败的情况。

ClassCircularityError:类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。

ClassFormatError:类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。

Error:错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。

ExceptionInInitializerError:初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。

IllegalAccessError:违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。

IncompatibleClassChangeError:不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。

InstantiationError:实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.

InternalError:内部错误。用于指示Java虚拟机发生了内部错误。

LinkageError:链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。

NoClassDefFoundError:未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。

NoSuchFieldError:域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。

NoSuchMethodError:方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。

OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。

StackOverflowError:堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。

ThreadDeath:线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。

UnknownError:未知错误。用于指示Java虚拟机发生了未知严重错误的情况。

UnsatisfiedLinkError:未满足的链接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。

UnsupportedClassVersionError:不支持的类版本错误。当Java虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。

VerifyError:验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。

VirtualMachineError:虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。

ArithmeticException:算术条件异常。譬如:整数除零等。

ArrayIndexOutOfBoundsException:数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

ArrayStoreException:数组存储异常。当向数组中存放非数组声明类型对象时抛出。

ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。

ClassNotFoundException:找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

CloneNotSupportedException:不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。

EnumConstantNotPresentException:枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象并不包含常量时,抛出该异常。

Exception:根异常。用以描述应用程序希望捕获的情况。

IllegalAccessException:违法的访问异常。当应用试图通过反射方式创建某个类的实例、访问该类属性、调用该类方法,而当时又无法访问类的、属性的、方法的或构造方法的定义时抛出该异常。

IllegalMonitorStateException:违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。

IllegalStateException:违法的状态异常。当在Java环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。

IllegalThreadStateException:违法的线程状态异常。当线程尚未处于某个方法的合法调用状态,而调用了该方法时,抛出异常。

IndexOutOfBoundsException:索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。

InstantiationException:实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。

InterruptedException:被中止异常。当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Thread的interrupt方法终止该线程时抛出该异常。

NegativeArraySizeException:数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。

NoSuchFieldException:属性不存在异常。当访问某个类的不存在的属性时抛出该异常。

NoSuchMethodException:方法不存在异常。当访问某个类的不存在的方法时抛出该异常。

NullPointerException:空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。

NumberFormatException:数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。

RuntimeException:运行时异常。是所有Java虚拟机正常操作期间可以被抛出的异常的父类。

SecurityException:安全异常。由安全管理器抛出,用于指示违反安全情况的异常。

StringIndexOutOfBoundsException:字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。

TypeNotPresentException:类型不存在异常。当应用试图以某个类型名称的字符串表达方式访问该类型,但是根据给定的名称又找不到该类型是抛出该异常。该异常与ClassNotFoundException的区别在于该异常是unchecked(不被检查)异常,而ClassNotFoundException是checked(被检查)异常。

UnsupportedOperationException:不支持的方法异常。指明请求的方法不被支持情况的异常。

2. Java 中异常有分类哪几种?

Throwable类是Java异常类型的顶层父类,一个对象只有是Throwable类的(直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别。JDK中内建了一些常用的异常类,我们也可以自定义异常。

异常的分类

Throwable包含了错误(Error)和异常(Exception两类)。

Error:一般为底层的不可恢复的类; Exception:分为未检查异常(RuntimeException)和已检查异常(非RuntimeException)。

未检查异常是因为程序员没有进行必需要的检查,因为疏忽和错误而引起的错误。

Java中比较经典RunTimeException对象如下:

java.lang.NullPointerException;
java.lang.ArithmaticException;
java.lang.ArrayIndexoutofBoundsException;

3. 构造器 Constructor 是否可被重写(Override)?

造器不能被继承,因为每个类名都不相同,而构造器的名称与类名相同,所以构造器不能被继承,也不能被重写。

构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。

4. Java 中能否继承 String 类?

String类是final类所以不可以被继承。

5. Java 中最有效率方法算出 2 乘以 8 等于几?

2 << 3

public static void main(String args[]) {
	int i = 2;
	i = i << 3;
	System.out.println("输出:" + i);
}

执行结果

输出:16

6. Java 中 >、>>、>>> 三者有什么区别?

1、“>”表示大于,如:if(a>b)…结果是boolean类型。

2、“>>”在c++中,移位运算符有双目移位运算符:<<(左移)和>>(右移)。

移位运算符组成的表达式也属于算术表达式,其值为算术值。

左移运算是将一个二进制位的操作数按指定移动的位数向左移位,移出位被丢弃,右边的空位一律补0。

右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位或者一律补0,或者补符号位,这由不同的机器而定。

在使用补码作为机器数的机器中,正数的符号位为0,负数的符号位为1。

通俗的讲就是指需要移动的数转换成2进制,右移几位就去掉右边的几位数,左移几位就在右边加几个0,比如14右移2位就是转成二进制变成1110,去掉右边的10,变成11,11转成十进制就是3;左移2位就是111000,转成十进制就是56。

实例

int i=15; i>>2;

执行的结果是3,移出的部分将被抛弃。

转为二进制,0000 1111(15)右移2位的结果是0000 0011(3),0001 1010(18)右移位的结果是0000 0011(3)。

3、“>>>”表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。

实例

res = 20 >> 2;

20的二进制为0001 0100,右移2位后为0000 0101,则结果就为res =5。

res = -20 >> 2;

-20的二进制为1110 1011,右移2位,此时高位补0,即0011 1010,结果为res =- 5。

7. 简述逻辑操作(&,|,^)和条件操作(&&,||)有什么区别?

1、条件操作只能操作布尔型的,而逻辑操作不仅可以操作布尔型,而且可以操作数值型。

2、逻辑操作不会产生短路。

8. Java 中实现多态的机制是什么?

多态就是指一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

多态通俗的讲就是父类或接口定义的引用变量可以指向子类或实现类的实例对象,程序在运行时,该引用变量的方法是内存中正在运行的那个方法(子类或实现类里的方法),而不是引用变量的类型中定义的方法。

方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。

9. 内部类引用其他类的成员有什么限制?

一个内部类对象可以访问创建它的外部类对象的内容。

如果内部类没有被static修饰,那么它可以访问创建它的外部类对象的所有属性。否则会编译报错:

Cannot make a static reference to the non-static field

静态内部类只能访问静态成员。

如果内部类是被static修饰,即为nested class,那么它只可以访问创建它的外部类对象的所有static属性和static方法。

10. Java 中 JDBC 调用数据库有哪几步骤?

使用JDBC来实现访问数据库记录几个步骤:

1、注册驱动,通过驱动器管理器获取连接接口。

2、获得Statement或它的子类。

3、限制Statement中的参数。

4、执行Statement。

5、查看返回的行数是否超出范围。

6、关闭Statement。

7、处理其它的Statement。

8、关闭连接接口。

// 1.注册驱动
Class.forname("com.mysql.jdbc.Driver");// 这是连接mysql数据库的驱动
// 2.获取数据库连接
java.sql.Connection conn = java.sql.DriverManager.getConnection();
// 3.获取表达式
java.sql.Statement stmt = conn.createStatement("jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK","root", "123456");
// 4.执行SQL
java.sql.ResultSet rs=stmt.executeQuery("select * from user");
// 5.显示结果集里面的数据
while(rs.next())
{
	System.out.println(rs.getInt(1));
	System.out.println(rs.getString("username"));
	System.out.println(rs.getString("password"));
	System.out.pringln();
}
// 执行插入语句
// stmt.executeUpdate("insert into user values(1,'中文','345')");
// 6.关闭连接接口,释放资源
rs.close();stmt.close();conn.close();

11. Java 和 C++ 有什么区别?

Java和C++都是面向对象的语言,都支持封装、继承和多态。

Java不提供指针来直接访问内存,程序内存更加安全。

Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。

Java有自动内存管理机制,不需要程序员手动释放无用内存。

12. 为什么有 int 类型还要设计 Integer 类型?

对象封装有很多好处,可以把属性也就是数据跟处理这些数据的方法结合在一起,比如Integer就有parseInt()等方法来专门处理int型相关的数据。

另一个非常重要的原因就是在Java中绝大部分方法或类都是用来处理类类型对象的,如ArrayList集合类就只能以类作为他的存储对象,而这时如果想把一个int型的数据存入list是不可能的,必须把它包装成类,也就是Integer才能被List所接受。所以Integer的存在是很必要的。

13. 为什么静态方法中不能调用非静态方法或变量?

非静态的方法可以调用静态的方法,但是静态的方法不可以调用非静态的方法。

类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。

在一个类的静态成员中去访问其非静态成员之所以会出错是因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。

14. static 修饰变量、代码块时何时执行?执行几次?

在类加载的init阶段,类的类构造器中会收集所有的static块和字段并执行;static块只执行一次。

注意的是static语句块,不是在实例化的时候被执行的;在调用类中任何一个方法时,jvm进行类加载,static语句块是在类加载器加载该类的最后阶段进行初始化的。并且只会被初始化一次。

注:若一次性调用多个方法,则只会执行一次static代码块。

15. Java 中为什么要定义一个没有参数的构造方法?

Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。

如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。

解决办法是在父类里加上一个没有参数的构造方法。

16. 子类继承父类时,父类构造方法何时调用?

实例化一个子类对象时会先执行其父类的构造函数,然后再执行子类的构造函数。

super()必须先被调用;如果子类构造方法中没有写super(),编译器会自动调用super()方法,即调用父类的默认无参构造方法,因此不可以父类中只定义了有参数的构造方法。

Java中如果一个类没有定义构造方法,编译器会默认插入一个无参数的构造方法;但是如果一个构造方法在父类中已定义,在这种情况,编译器是不会自动插入一个默认的无参构造方法。

17. Java 中构造方法有哪些特性?

1、名字与类名相同。

2、没有返回值,但不能用void声明构造函数。

3、生成类的对象时自动执行,无需调用。

18. 成员变量与局部变量有那些区别?

从语法形式上看: 成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public、private、static等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。

从变量在内存中的存储方式来看: 如果成员变量是使用static修饰的,那么这个成员变量是属于类的,如果没有使用static修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。

从变量在内存中的生存时间上看: 成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。

成员变量如果没有被赋初值: 则会自动以类型的默认值而赋值,一种情况例外被final修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。

19. String 编码 UTF-8 和 GBK 有什么区别?

GBK全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification) ,中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订,国家技术监督局标准化司、电子工业部科技与质量监督司1995年12月15日联合以技监标函1995 229号文件的形式,将它确定为技术规范指导性文件。经实际测试和查阅文档,GBK是采用单双字节变长编码,英文使用单字节编码,完全兼容ASCII字符编码,中文部分采用双字节编码。简洁一点就是是指中国的中文字符,其它它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符。

UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部份修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。它是一种全国家通过的一种编码,如果你的网站涉及到多个国家的语言,那么建议你选择UTF-8编码。

两者的区别:

UTF8编码格式很强大,支持所有国家的语言,正是因为它的强大,才会导致它占用的空间大小要比GBK大,对于网站打开速度而言,也是有一定影响的。

GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。

20. Java 中 Hash 冲突有哪些解决办法?

1、开放定址法

这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:

Hi=(H(key)+di)% m i=1,2,…,n

其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不同,相应的再散列方式也不同。主要有以下三种:

1)线性探测再散列

dii=1,2,3,…,m-1

这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

2)二次探测再散列

di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 )

这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。

3)伪随机探测再散列

di=伪随机数序列。

具体实现时,应建立一个伪随机数发生器,(如i=(i+p) % m),并给定一个随机数做起点。

例如,已知哈希表长度m=11,哈希函数为:H(key)= key % 11,则H(47)=3,H(26)=4,H(60)=5假设下一个关键字为69,则H(69)=3,与47冲突。

如果用线性探测再散列处理冲突,下一个哈希地址为H1=(3 + 1)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 + 2)% 11 = 5,还是冲突,继续找下一个哈希地址为H3=(3 + 3)% 11 = 6,此时不再冲突,将69填入5号单元。

如果用二次探测再散列处理冲突,下一个哈希地址为H1=(3 + 12)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 - 12)% 11 = 2,此时不再冲突,将69填入2号单元。

如果用伪随机探测再散列处理冲突,且伪随机数序列为:2,5,9,……,则下一个哈希地址为H1=(3 + 2)% 11 = 5,仍然冲突,再找下一个哈希地址为H2=(3 + 5)% 11 = 8,此时不再冲突,将69填入8号单元。

2、再哈希法

这种方法是同时构造多个不同的哈希函数:

Hi=RH1(key) i=1,2,…,k

当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

3、链地址法

这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。

4、建立公共溢出区

这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。