基础
1. 重写
- 重写发生在子类与父类之间
- 方法的重写(override)两同两小一大原则:
- 方法名相同,参数类型相同
- 子类返回类型小于等于父类方法返回类型,
- 子类抛出异常小于等于父类方法抛出异常,
- 子类访问权限大于等于父类方法访问权限。
- 运行时多态(动态多态)
2. 重载
- 重载发生在同一个类里面
- 方法名字相同,而参数不同
- 如构造函数
- 编译时多态(静态多态)
3. java静态变量、代码块、和静态方法的执行顺序
基本上代码块分为三种:Static静态代码块、构造代码块、普通代码块
代码块执行顺序: 静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块
继承中代码块执行顺序: 父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类 代码块——>子类构造器
4. 面向对象特点
- 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
- 缺点:性能比面向过程低。
三大特性: 封装、继承、多态
5. 抽象类和接口的区别
A:成员的区别
抽象类:
构造方法:有构造方法,用于子类实例化使用。
成员变量:可以是变量,也可以是常量。
成员方法:可以是抽象的,也可以是非抽象的。
接口:
构造方法:没有构造方法
成员变量:只能是常量。默认修饰符:public static final
成员方法:jdk1.7只能是抽象的。默认修饰符:public abstract (推荐:默认修饰符请自己永远手动给出)
jdk1.8可以写以default和static开头的具体方法
6. 关于父类和子类的静态方法的继承或重写问题
父类
public class Father {
public static void show() {
System.out.println(" 父类的静态方法");
}
public static void method() {
System.out.println("父类的method静态方法");
}
}
子类
public class Son extends Father{
public static void show() {
System.out.println("子类的静态方法 ");
}
public static void main(String[] args) {
Father father = new Son();
father.show();//是否重写父类静态方法,表现多态性
Son son = new Son();
son.show();
son.method();//是否继承父类静态方法
}
}
输出结果
父类的静态方法
子类的静态方法
父类的method静态方法
结论
不呈现多态性,
子类不会重写父类的静态方法
当子类和父类都有同一个名字的静态方法时,这两个方法是相互独立的,不存在重写
子类可以继承父类的静态属性和静态方法(父类的不为private)
7. 反射
反射的三种获取方法
- Class clazz = Class.forName(“com.zzt.Person”) //类的具体地址
- Class clazz = Person.class; //类名
- Class clazz = zhangsan.getClass(); //具体对象名
反射获取对象
- 获取类对象实例
Class clazz = Class.forName("com.zzt.Person")
- 根据类对象实例获取Constructor
Constructor personConstructor = clazz.getConstructor()
- 使用construction获取反射类对象
Object personObj = personConstructor.newInstance();
- 调用方法
- 获取方法的Method对象
Method setAgeMethod = clazz.getMethod("setAge",int.class);
- 用invoke调用方法
setAgeMethod.invoke(personObj,18)
8. 虚拟机三个步骤
装载、连接(验证、准备、解析)、初始化
9. 序列化
实现
实现Serializable接口或者Externalizable接口。
序列化接口没有方法或字段,仅用于标识可序列化的语义。
Serializable
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person();
person.setAge(18);
person.setName("zhangsan");
//序列化
FileOutputStream fos = new FileOutputStream("tempFile");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(person);
//反序列化
File tempFile = new File("tempFile");
FileInputStream fis = new FileInputStream(tempFile);
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
System.out.println(o); // Person{name='zhangsan', age=18}
}
如果实现的是Externalizable接口,需要重写两个方法
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = (Integer) in.readObject();
}
测试类代码同上,但是返回结果
Person{name=‘null’, age=18}
只有age
所以,使用实现Externalizable接口可以自己决定要存储哪些信息(age),而name因为没有在重写代码中定义,所以即使set了,也存储不进去。
问题:
序列化中如果有些字段不想进行序列化
可以在变量前面添加transient关键字修饰。 transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中。 在被反序列化后, transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。 transient 只能修饰变量,不能修饰类和方法。静态变量会被序列化吗
不会。因为序列化是针对对象而言的, 而静态变量优先于对象存在, 随着类的加载而加载, 所以不会被序列化。ps:
看到这个结论, 是不是有人会问, serialVersionUID也被static修饰, 为什么serialVersionUID会被序列化?
其实serialVersionUID属性并没有被序列化, JVM在序列化对象时会自动生成一个serialVersionUID, 然后将我们显示指定的serialVersionUID属性值赋给自动生成的serialVersionUID。
10. Error 和 Exception 区别
所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。 Throwable 类有两个重要的子类 Exception (异常)和 Error (错误)。
Exception 和 Error 二者都是 Java 异常处理的重要子类,各自都包含大量子类。
- Exception :程序本身可以处理的异常
可以通过 catch 来进行捕获,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。
Exception 又可以分为运行时异常(RuntimeException, 又叫非受检查异常)和非运行时异常(又叫受检查异常) 。 - Error : Error 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。
例如,系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复。
非受检查异常(运行时异常)和受检查异常(一般异常)区别是什么?
-
非受检查异常:包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。
Java 编译器不会检查运行时异常。
例如: NullPointException(空指针) 、 NumberFormatException(字符串转换为数字) 、 IndexOutOfBoundsException(数组越界) 、 ClassCastException(类转换异常) 、 ArrayStoreException(数据存储异常,操作数组时类型不一致) 等。 -
受检查异常:是Exception 中除 RuntimeException 及其子类之外的异常。
Java 编译器会检查受检查异常。
常见的受检查异常有: IO 相关的异常、 ClassNotFoundException 、 SQLException 等。
非受检查异常和受检查异常之间的区别:
是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检查异常,否则就选择非受检查异常。
11. throw 和 throws 的区别
throws 是作用在方法声明上的上,可以抛出多个异常,用来标识该方法可能抛出的异常列表
public static void main(String[] args) throws IOException, ClassNotFoundException {
...
}
throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。
if(s.equals("abc")) {
throw new NumberFormatException();
} else {
System.out.println(s);
}
12. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?变量怎么确定?
会执行,但是返回分2种情况
- final中不return
public static int getInt() { int a = 10; try { System.out.println(a / 0); a = 20; } catch (ArithmeticException e) { a = 30; return a; /* * return a 在程序执行到这一步的时候,这里不是return a 而是 return 30; 这个返回路 径就形成了 * 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40 * 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30 ; return 的是30 而不是a **/ } finally { a = 40; }return a; } //执行结果:30
- final中直接return
public static int getInt() { int a = 10; try { System.out.println(a / 0); a = 20; } catch (ArithmeticException e) { a = 30; return a; } finally { a = 40; //由于只能return 1个,所以直接return final里的a了 return a; } } //执行结果:40
13. BIO、NIO、AIO的区别
- BIO: 同步并阻塞,在服务器中实现的模式为一个连接一个线程。
也就是说,客户端有连接请求的时候,服务器就需要启动一个线程进行处理
BIO一般适用于连接数目小且固定的架构 - NIO:同步并非阻塞,在服务器中实现的模式为一个请求一个线程。
也就是说,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到有连接IO请求时才会启动一个线程进行处理。
NIO一般适用于连接数目多且连接比较短(轻操作)的架构 - AIO:异步并非阻塞,在服务器中实现的模式为一个有效请求一个线程。
也就是说,客户端的IO请求都是通过操作系统先完成之后,再通知服务器应用去启动线程进行处理。
AIO一般适用于连接数目多且连接比较长(重操作)的架构,充分调用操作系统参与并发操作,编程比较复杂
14. Java IO都有哪些设计模式?
使用了适配器模式和装饰器模式
- 适配器模式:
Reader reader = new INputStreamReader(inputStream);
- 装饰器模式:
new BufferedInputStream(new FileInputStream(inputStream));