2019年Java面试题基础系列228道
上一篇更新1~20题的答案解析java
juejin.im/post/5de8c6…
程序员
2一、描述一下 JVM 加载 class 文件的原理机制?
JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java 中的类加载器是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入类文件中的类。
因为 Java 的跨平台性,通过编译的 Java 源程序并非一个可执行程序,而是一个或多个类文件。当 Java 程序须要使用某个类时,JVM 会确保这个类已经被加载、链接(验证、准备和解析)和初始化。类的加载是指把类的.class 文件中的数据读入到内存中,一般是建立一个字节数组读入.class 文件,而后产生与所加载类对应的 Class 对象。加载完成后,Class 对象还不完整,因此此时的类还不可用。当类被加载后就进入链接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后 JVM 对类进行初始化,包括:1)若是类存在直接的父类而且这个类尚未被初始化,那么就先初始化父类;2)若是类中存在初始化语句,就依次执行这些初始化语句。
类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader 的子类)。从 Java 2(JDK 1.2)开始,从 Java 2(JDK 1.2)开始,类加载过程采起了父亲委托机制(PDM)。PDM 更好的保证了 Java 平台的安全性,在该机制中,JVM 自带的 Bootstrap 是根加载器,其余的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM 不会向 Java 程序提供对 Bootstrap 的引用。下面是关于几个类加载器的说明:
(1) Bootstrap:通常用本地代码实现,负责加载 JVM 基础核心类库(rt.jar);
(2) Extension:从 java.ext.dirs 系统属性所指定的目录中加载类库,它的父加载器是 Bootstrap;
(3) System:又叫应用类加载器,其父类是 Extension。它是应用最普遍的类加载器。它从环境变量 classpath 或者系统属性 java.class.path 所指定的目录中记载类,是用户自定义加载器的默认父加载器。
2二、char 型变量中能不能存贮一个中文汉字,为何?
char 类型能够存储一个中文汉字,由于 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的惟一方法),一个 char 类型占 2 个字节(16 比特),因此放一个中文是没问题的。
补充:使用 Unicode 意味着字符在 JVM 内部和外部有不一样的表现形式,在 JVM内部都是 Unicode,当这个字符被从 JVM 内部转移到外部时(例如存入文件系统中),须要进行编码转换。因此 Java 中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如 InputStreamReader 和 OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于 C 程序员来讲,要完成这样的编码转换恐怕要依赖于 union(联合体/共用体)共享内存的特征来实现了。
2三、抽象类(abstract class)和接口(interface)有什么异同?
抽象类和接口都不可以实例化,但能够定义抽象类和接口类型的引用。一个类若是继承了某个抽象类或者实现了某个接口都须要对其中的抽象方法所有进行实现,不然该类仍然须要被声明为抽象类。接口比抽象类更加抽象,由于抽象类中能够定义构造器,能够有抽象方法和具体方法,而接口中不能定义构造器并且其中的方法所有都是抽象方法。抽象类中的成员能够是 private、默认、protected、public 的,而接口中的成员全都是 public 的。抽象类中能够定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
2四、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不一样?
Static Nested Class 是被声明为静态(static)的内部类,它能够不依赖于外部类实例被实例化。而一般的内部类须要在外部类实例化后才能实例化,其语法看起来挺诡异的,以下所示。
/**
* 扑克类(一副扑克)
* @author 骆昊
*
*/
public class Poker {
private static String[] suites = {"黑桃", "红桃", "草花", "方块"};
private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
private Card[] cards;
/**
* 构造器
*
*/
public Poker() {
cards = new Card[52];
for (int i = 0; i < suites.length; i++) {
for (int j = 0; j < faces.length; j++) {
cards[i * 13 + j] = new Card(suites[i], faces[j]);
}
}
}
/**
* 洗牌 (随机乱序)
*
*/
public void shuffle() {
for (int i = 0, len = cards.length; i < len; i++) {
int index = (int) (Math.random() * len);
Card temp = cards[index];
cards[index] = cards[i];
cards[i] = temp;
}
}
/**
* 发牌
* @param index 发牌的位置
*
*/
public Card deal(int index) {
return cards[index];
}
/**
* 卡片类(一张扑克)
* [内部类]
* @author 骆昊
*
*/
public class Card {
private String suite;
// 花色
private int face;
// 点数
public Card(String suite, int face) {
this.suite = suite;
this.face = face;
}
@Override
public String toString() {
String faceStr = "";
switch(face) {
case 1: faceStr = "A";
break;
case 11: faceStr = "J";
break;
case 12: faceStr = "Q";
break;
case 13: faceStr = "K";
break;
default: faceStr = String.valueOf(face);
}
return suite + faceStr;
}
}
}
测试代码:
class PokerTest {
public static void main(String[] args) {
Poker poker = new Poker();
poker.shuffle();
// 洗牌
Poker.Card c1 = poker.deal(0);
// 发第一张牌
// 对于非静态内部类 Card
// 只有经过其外部类 Poker 对象才能建立 Card 对象
Poker.Card c2 = poker.new Card("红心", 1);
// 本身建立一张牌
System.out.println(c1);
// 洗牌后的第一张
System.out.println(c2);
// 打印: 红心 A
}
}复制代码
class Outer {
class Inner {
}
public static void foo() {
new Inner();
}
public void bar() {
new Inner();
}
public static void main(String[] args) {
new Inner();
}
}复制代码
注意:Java 中非静态内部类对象的建立要依赖其外部类对象,上面的面试题中 foo和 main 方法都是静态方法,静态方法中没有 this,也就是说没有所谓的外部类对象,所以没法建立内部类对象,若是要在静态方法中建立内部类对象,能够这样作:
new Outer().new Inner();复制代码
2五、Java 中会存在内存泄漏吗,请简单描述。
理论上 Java 由于有垃圾回收机制(GC)不会存在内存泄露问题(这也是 Java 被普遍使用于服务器端编程的一个重要缘由);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被 GC 回收,所以也会致使内存泄露的发生。例如Hibernate 的 Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,若是不及时关闭(close)或清空(flush)一级缓存就可能致使内存泄露。下面例子中的代码也会致使内存泄露。
import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
private T[] elements;
private int size = 0;
private static final int INIT_CAPACITY = 16;
public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY];
}
public void push(T elem) {
ensureCapacity();
elements[size++] = elem;
}
public T pop() {
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}复制代码
上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下彷佛没有什么明显的问题,它甚至能够经过你编写的各类单元测试。然而其中的 pop 方法却存在内存泄露的问题,当咱们用 pop 方法弹出栈中的对象时,该对象不会被看成垃圾回收,即便使用栈的程序再也不引用这些对象,由于栈内部维护着对这些对象的过时引 用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无心识的对象保持。若是一个对象引用被无心识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其余对象,即便这样的对象只有少数几个,也可能会致使不少的对象被排除在垃圾回收以外,从而对性能形成重大影响,极端状况下会引起 Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至形成 OutOfMemoryError。
2六、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被 synchronized修饰?
都不能。抽象方法须要子类重写,而静态的方法是没法被重写的,所以两者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,所以也是相互矛盾的。
2七、阐述静态变量和实例变量的区别。
静态变量是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类无论建立多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,须要先建立对象而后经过对象才能访问到它。静态变量能够实现让多个对象共享内存。
补充:在 Java 开发中,上下文类和工具类中一般会有大量的静态成员。
2八、是否能够从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
不能够,静态方法只能访问静态成员,由于非静态方法的调用要先建立对象,在调用静态方法时可能对象并无被初始化。
2九、如何实现对象克隆?
1). 实现 Cloneable 接口并重写 Object 类中的 clone()方法;
2). 实现 Serializable 接口,经过对象的序列化和反序列化实现克隆,能够实现真正的深度克隆,代码以下。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws
Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new
ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用 ByteArrayInputStream 或 ByteArrayOutputStream
对象的 close 方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就可以释放资源,这
一点不一样于对外部资源(如文件流)的释放
}
}复制代码
import java.io.Serializable;
/**
* 人类
* @author 骆昊
*
*/
class Person implements Serializable {
private static final long serialVersionUID = -9102017020286042305L;
private String name;
// 姓名
private int age;
// 年龄
private Car car;
// 座驾
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" +
car + "]";
}
}
/**
* 小汽车类
* @author 骆昊
*
*/
class Car implements Serializable {
private static final long serialVersionUID = -5713945027627603702L;
private String brand;
// 品牌
private int maxSpeed;
// 最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed +
"]";
}
}
class CloneTest {
public static void main(String[] args) {
try {
Person p1 = new Person("Hao LUO", 33, new Car("Benz",
300));
Person p2 = MyUtil.clone(p1);
// 深度克隆
p2.getCar().setBrand("BYD");
// 修改克隆的 Person 对象 p2 关联的汽车对象的品牌属性
// 原来的 Person 对象 p1 关联的汽车不会受到任何影响
// 由于在克隆 Person 对象时其关联的汽车对象也被克隆了
System.out.println(p1);
}
catch (Exception e) {
e.printStackTrace();
}
}
}复制代码
注意:基于序列化和反序列化实现的克隆不只仅是深度克隆,更重要的是经过泛型限定,能够检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用 Object 类的 clone 方法克隆对象。让问题在编译的时候暴露出来老是好过把问题留到运行时。
30、GC 是什么?为何要有 GC?
GC 是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会致使程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能能够自动监测对象是否超过做用域从而达到自动回收内存的目的,Java 语言没有提供释放已分配内存的显示操做方法。Java 程序员不用担忧内存管理,由于垃圾收集器会自动进行管理。要请求垃圾收集,能够调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但 JVM 能够屏蔽掉显示的垃圾回收调用。垃圾回收能够有效的防止内存泄露,有效的使用可使用的内存。垃圾回收器一般是做为一个单独的低优先级的线程运行,不可预知的状况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或全部对象进行垃圾回收。在 Java 诞生初期,垃圾回收是 Java最大的亮点之一,由于服务器端的编程须要有效的防止内存泄露问题,然而时过境迁,现在 Java 的垃圾回收机制已经成为被诟病的东西。移动智能终端用户一般以为 iOS 的系统比 Android 系统有更好的用户体验,其中一个深层次的缘由就在于 Android 系统中垃圾回收的不可预知性。
补充:垃圾回收机制有不少种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的 Java 进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要建立的对象。Java 平台对堆内存回收和再利用的基本算法被称为标记和清除,可是 Java 对其进行了改进,采用“分代式垃圾收集”。这种方法会跟 Java对象的生命周期将堆内存划分为不一样的区域,在垃圾收集过程当中,可能会将对象移动到不一样区域:
(1)伊甸园(Eden):这是对象最初诞生的区域,而且对大多数对象来讲,这里是它们惟一存在过的区域。
(2)幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
(3)终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次彻底收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。
-Xms / -Xmx — 堆的初始大小 / 堆的最大大小
-Xmn — 堆中年轻代的大小
-XX:-DisableExplicitGC — 让 System.gc()不产生任何做用
-XX:+PrintGCDetails — 打印 GC 的细节
-XX:+PrintGCDateStamps — 打印 GC 操做的时间戳
-XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小
-XX:NewRatio — 能够设置老生代和新生代的比例
-XX:PrintTenuringDistribution — 设置每次新生代 GC 后输出幸存者
乐园中对象年龄的分布
-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老
年代阀值的初始值和最大值
-XX:TargetSurvivorRatio:设置幸存区的目标使用率 复制代码
3一、String s = new String(“xyz”);建立了几个字符串对象?
两个对象,一个是静态区的”xyz”,一个是用 new 建立在堆上的对象。
3二、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?
接口能够继承接口,并且支持多重继承。抽象类能够实现(implements)接口,抽象类可继承具体类也能够继承抽象类。
3三、一个”.java”源文件中是否能够包含多个类(不是内部类)?有什么限制?
能够,但一个源文件中最多只能有一个公开类(public class)并且文件名必须和公开类的类名彻底保持一致。
3四、Anonymous Inner Class(匿名内部类)是否能够继承其它类?是否能够实现接口?
能够继承其余类或实现其余接口,在 Swing 编程和 Android 开发中经常使用此方式来实现事件监听和回调。
3五、内部类能够引用它的包含类(外部类)的成员吗?有没有什么限制?
一个内部类对象能够访问建立它的外部类对象的成员,包括私有成员。
3六、Java 中的 final 关键字有哪些用法?
(1)修饰类:表示该类不能被继承;
(2)修饰方法:表示方法不能被重写; 面试
(3)修饰变量:表示变量只能一次赋值之后值不能被修改(常量)。算法
3七、指出下面程序的运行结果
class A {
static {
System.out.print("1");
}
public A() {
System.out.print("2");
}
}
class B extends A{
static {
System.out.print("a");
}
public B() {
System.out.print("b");
}
}
public class Hello {
public static void main(String[] args) {
A ab = new B();
ab = new B();
}
}复制代码
执行结果:1a2b2b。建立对象时构造器的调用顺序是:先初始化静态成员,而后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
提示:若是不能给出此题的正确答案,说明以前第 21 题 Java 类加载机制尚未彻底理解,赶忙再看看吧。
3八、数据类型之间的转换:
(1)调用基本数据类型对应的包装类中的方法 parseXXX(String)或valueOf(String)便可返回相应基本类型;
(2)一种方法是将基本数据类型与空字符串(”“)链接(+)便可得到其所对应的字符串;另外一种方法是调用 String 类中的 valueOf()方法返回相应字符串
3九、如何实现字符串的反转及替换?
方法不少,能够本身写实现也可使用 String 或 StringBuffer/StringBuilder 中的方法。有一道很常见的面试题是用递归实现字符串反转,代码以下所示:
public static String reverse(String originStr) {
if(originStr == null || originStr.length() <= 1)
return originStr;
return reverse(originStr.substring(1)) + originStr.charAt(0);
}复制代码
40、怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的字符串?
String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");复制代码
4一、日期和时间:
(2) 如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到如今的毫秒数?
问题 1:建立 java.util.Calendar 实例,调用其 get()方法传入不一样的参数便可得到参数所对应的值。Java 8 中可使用 java.time.LocalDateTimel 来获取,代码以下所示。
public class DateTimeTest {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));
// 0 - 11
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
// Java 8
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.getYear());
System.out.println(dt.getMonthValue());
// 1 - 12
System.out.println(dt.getDayOfMonth());
System.out.println(dt.getHour());
System.out.println(dt.getMinute());
System.out.println(dt.getSecond());
}
}复制代码
Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis();
// Java 8复制代码
Calendar time = Calendar.getInstance();
time.getActualMaximum(Calendar.DAY_OF_MONTH复制代码
问题 4:利用 java.text.DataFormat 的子类(如 SimpleDateFormat 类)中的format(Date)方法可将日期格式化。Java 8 中能够用java.time.format.DateTimeFormatter 来格式化时间日期,代码以下所示。
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
class DateFormatTest {
public static void main(String[] args) {
SimpleDateFormat oldFormatter = new
SimpleDateFormat("yyyy/MM/dd");
Date date1 = new Date();
System.out.println(oldFormatter.format(date1));
// Java 8
DateTimeFormatter newFormatter =
DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}
}复制代码
补充:Java 的时间日期 API 一直以来都是被诟病的东西,为了解决这一问题,Java8 中引入了新的时间日期 API,其中包括 LocalDate、LocalTime、LocalDateTime、Clock、Instant 等类,这些的类的设计都使用了不变模式,所以是线程安全的设计。
4二、打印昨天的当前时刻。
import java.util.Calendar;
class YesterdayCurrent {
public static void main(String[] args){
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
System.out.println(cal.getTime());
}
}复制代码
在 Java 8 中,能够用下面的代码实现相同的功能。
import java.time.LocalDateTime;
class YesterdayCurrent {
public static void main(String[] args) {
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);
System.out.println(yesterday);
}
}复制代码
4三、比较一下 Java 和 JavaSciprt。
JavaScript 与 Java 是两个公司开发的不一样的两个产品。Java 是原 SunMicrosystems 公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而 JavaScript 是 Netscape 公司的产品,为了扩展 Netscape 浏览器的功能而开发的一种能够嵌入 Web 页面中运行的基于对象和事件驱动的解释性语言。JavaScript 的前身是 LiveScript;而 Java 的前身是 Oak 语言。
(1)基于对象和面向对象:Java 是一种真正的面向对象的语言,即便是开发简单的程序,必须设计对象;JavaScript 是种脚本语言,它能够用来制做与网络无关的,与用户交互做用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,于是它自己提供了很是丰富的内部对象供设计人员使用。
(2)解释和编译:Java 的源代码在执行以前,必须通过编译。JavaScript 是一种解释性编程语言,其源代码不需通过编译,由浏览器解释执行。(目前的浏览器几乎都使用了 JIT(即时编译)技术来提高 JavaScript 的运行效率)
(3)强类型变量和类型弱变量:Java 采用强类型变量检查,即全部变量在编译以前必须做声明;JavaScript 中变量是弱类型的,甚至在使用变量前能够不做声明,JavaScript 的解释器在运行时检查推断其数据类型。
补充:上面列出的四点是网上流传的所谓的标准答案。其实 Java 和 JavaScript最重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语言。在 Java 中类(class)是一等公民,而 JavaScript 中函数(function)是一等公民,所以 JavaScript 支持函数式编程,可使用 Lambda函数和闭包(closure),固然 Java 8 也开始支持函数式编程,提供了对 Lambda表达式以及函数式接口的支持。对于这类问题,在面试的时候最好仍是用本身的语言回答会更加靠谱,不要背网上所谓的标准答案。
4四、何时用断言(assert)?
断言在软件开发中是一种经常使用的调试方式,不少开发语言中都支持这种机制。通常来讲,断言用于保证程序最基本、关键的正确性。断言检查一般在开发和测试时开启。为了保证程序的执行效率,在软件发布后断言检查一般是关闭的。断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true;若是表达式的值为 false,那么系统会报告一个 AssertionError。断言的使用以下面的代码所示:
assert(a > 0); // throws an AssertionError if a <= 0复制代码
assert Expression1 : Expression2 ;
Expression2 能够是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。
要在运行时启用断言,能够在启动 JVM 时使用-enableassertions 或者-ea 标记。要在运行时选择禁用断言,能够在启动 JVM 时使用-da 或者-disableassertions标记。要在系统类中启用或禁用断言,可以使用-esa 或-dsa 标记。还能够在包的基础上启用或者禁用断言。
注意:断言不该该以任何方式改变程序的状态。简单的说,若是但愿在不知足某些条件时阻止代码的执行,就能够考虑用断言来阻止它。
4五、Error 和 Exception 有什么区别?
Error 表示系统级的错误和程序没必要处理的异常,是恢复不是不可能但很困难的状况下的一种严重问题;好比内存溢出,不可能期望程序能处理这样的状况;
Exception 表示须要捕捉或者须要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示若是程序运行正常,从不会发生的状况。
4六、try{}里有一个 return 语句,那么紧跟在这个 try 后的finally{}里的代码会不会被执行,何时被执行,在 return前仍是后?
注意:在 finally 中改变返回值的作法是很差的,由于若是存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕以后再向调用者返回其值,而后若是在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回值会对程序形成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java 中也能够经过提高编译器的语法检查级别来产生警告或错误,Eclipse 中能够在如图所示的地方进行设置,强烈建议将此项设置为编译错误。
4七、Java 语言如何进行异常处理,关键字:throws、throw、try、catch、finally 分别如何使用?
Java 经过面向对象的方法进行异常处理,把各类不一样的异常进行分类,并提供了良好的接口。在 Java 中,每一个异常都是一个对象,它是 Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法能够捕获到这个异常并能够对其进行处理。Java 的异常处理是经过 5 个关键词来实现的:try、catch、throw、throws 和 finally。通常状况下是用 try 来执行一段程序,若是系统会抛出(throw)一个异常对象,能够经过它的类型来捕获(catch)它,或经过老是执行代码块(finally)来处理;try 用来指定一块预防全部异常的程序;catch 子句紧跟在 try 块后面,用来指定你想要捕获的异常的类型;throw 语句用来明确地抛出一个异常;throws 用来声明一个方法可能抛出的各类异常(固然声明异常时容许无病呻吟);finally 为确保一段代码无论发生什么异常情况都要被执行;try 语句能够嵌套,每当遇到一个 try 语句,异常的结构就会被放入异常栈中,直到全部的 try 语句都完成。若是下一级的try 语句没有对某种异常进行处理,异常栈就会执行出栈操做,直到遇到有处理这种异常的 try 语句或者最终将异常抛给 JVM。
4八、运行时异常与受检异常有何异同?
异常表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题一般就不会发生。受检异常跟程序运行的上下文环境有关,即便程序设计无误,仍然可能因使用的问题而引起。Java 编译器要求方法必须声明抛出可能发生的受检异常,可是并不要求必须声明抛出未被捕获的运行时异常。异常和继承同样,是面向对象程序设计中常常被滥用的东西,在 Effective Java 中对异常的使用给出了如下指导原则:
(1)不要将异常处理用于正常的控制流(设计良好的 API 不该该强迫它的调用者为了正常的控制流而使用异常)
(2)对能够恢复的状况使用受检异常,对编程错误使用运行时异常
(3)避免没必要要的使用受检异常(能够经过一些状态检测手段来避免异常的发生)
4九、列出一些你常见的运行时异常?
(1)ArithmeticException(算术异常)
(2) ClassCastException (类转换异常)
(3) IllegalArgumentException (非法参数异常)
(4) IndexOutOfBoundsException (下标越界异常)
(5) NullPointerException (空指针异常)
(6) SecurityException (安全异常)
50、阐述 final、finally、finalize 的区别。
(1) final:修饰符(关键字)有三种用法:若是一个类被声明为 final,意味着它不能再派生出新的子类,即不能被继承,所以它和 abstract 是反义词。将变量声明为 final,能够保证它们在使用中不被改变,被声明为 final 的变量必须在声明时给定初值,而在之后的引用中只能读取不可修改。被声明为 final 的方法也一样只能使用,不能在子类中被重写。
(2)finally:一般放在 try…catch…的后面构造老是执行代码块,这就意味着程序不管正常执行仍是发生异常,这里的代码只要 JVM 不关闭都能执行,能够将释放外部资源的代码写在 finally 块中.
(3)finalize:Object 类中定义的方法,Java 中容许使用 finalize()方法在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。这个方法是由垃圾收集器在销毁对象时调用的,经过重写 finalize()方法能够整理系统资源或者执行其余清理工做。
最后
欢迎你们关注个人公种浩【程序员追风】,整理了1000道2019年多家公司java面试题400多页pdf文档,文章都会在里面更新,整理的资料也会放在里面。喜欢文章记得关注我点个赞哟,感谢支持!编程