1. Java面向对象的知识结构
- 1.1 Java语法以及关键字、如接口与类、内部类,final/finally/finalize, throw/throws,域访问符权限等;
- 1.2 Java面向对象思想以及体系,例如设计思想。
2. 经典面试题
2.1 Java 有没有 goto? 如果有,一般用在什么地方?如果没有,如何跳出当前的多重嵌套循环?
goto是Java中的保留字,在目前Java版本中没有使用。
在Java中跳出多重循环的的方法有三种:
1. break + 标签,在外层循环前加上一个标签lable,
然后在最里层循环使用 break lable.
public static void main(String[] args) {
label: //标记
for (int i = 0 ; i < 10; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i = " + i + ", j = " + j);
if(j == 5) { //满中一定条件跳到某个标记
break label;
}
}
}
}
2. 通过异常捕获
public static void main(String[] args) {
try {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i = " + i + ", j = " + j);
if (j == 5) {// 满足一定条件抛异常
throw new RuntimeException("test exception for j = 5");
}
}
}
} catch (RuntimeException e) { //循环外层捕获异常
e.printStackTrace();
}
}
3. 通过标置变量
public static void main(String[] args) {
boolean flag = false; //初始化标置变量
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
System.out.println("i = " + i + ", j = " + j);
if (j == 5) { //满足一定条件进行设置标置变量
flag = true;
}
if (flag) { //内层循环判断标置变量
break;
}
}
if (flag) {//外层循环判断标置变量
break;
}
}
}
2.2 抽象类(abstract class)和接口(interface)有什么异同?
- 相同点
1. 不能直接实例化。如果要实例化,抽象变量必须实现所有抽象方法,接口变量必须实现接口未实现的方法。
2. 都可以有实现方法(Java1.8之前不能有实现方法)
3. 都可以不需要实现类或者继承者去实现所有方法(Java8 以前的接口,Java8 及以后的接口中可以包括默认方法,不需要实现者实现)。
- 不同点
1. 抽象类和接口所反映的设计理念不同,抽象类表示的是对象/类的抽象,接口表示的是行为的抽象。
2. 抽象类不可以多重继承,接口可以多重继承。即一个类只能继续一个抽象类,却可以继承多个接口。
3. 抽象类中的方法可以用 public protected 和 default abstract 修饰符,不能用 private、static、synchronize、native 修饰;变量可以在子类中重新定义,也可以重新赋值;
接口的方法默认修饰符是 public abstract, Java8 开始出现静态方法,多加 static 关键字;变量默认是 public static final 型,且必须给其初值,在实现类中也不能重新定义,也不能改变其值。
4. 抽象类可以有构造器,接口没有构造器。
2.3 Java 创建对象的方式有哪些?
1. 使用 new关键字
2. 反射创建,使用java.lang.Class 类的newInstance 方法
这种方式会调用无参的构造函数来创建对象,有两种实现方式。
//方式一,使用全路径包名
User user = (User)Class.forName("com.imooc.interview.demo.User").newInstance();
//方法二,使用class类
User user = User.class.newInstance();
反射,使用 java.lang.reflect.Constructor 类的 newInstance 方法。
Constructor<User> constructor = User.class.getConstructor();
User user = constructor.newInstance();
使用 clone 方法。
public class User implements Cloneable {
/** 构造方法 */
public User(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
private Integer age;
// 重写(Overriding)Object的clone方法
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
public static void main(String[] args) throws Exception {
User person = new User(new Integer(200));
User clone = person.clone();
System.out.println("person == clone, result = " + (person == clone)); // false,拷贝都是生成新对象
System.out.println("person.age == clone.age, result = " + (person.getAge() == clone.getAge())); // true,浅拷贝的成员变量引用仍然指向原对象的变量引用
}
}
浅拷贝和深拷贝
- 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,对拷贝后对象的引用仍然指向原来的对象。
- 深拷贝:不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。
3. 使用反序列化。
为了序列化 / 反序列化一个对象,需要该类实现空接口 Serializable
序列化时首先创建一个输出流对象 oos, 使用 oos 的 writeObject () 方法将 p 对象写入 oos 对象中去。使用反序列化创建对象时,首先创建一个输入流对象 ois,使用输入流对象 ois 的 readObject () 方法将序列化存入的对象读出,重新创建一个对象。
序列化是深拷贝。