1.谈谈你对面向对象的理解
所谓的面向对象就是将我们的程序模块化,对象化,把具体事物的特性属性和通过这些属性来实现一些动作的具体方法放到一个类里面,这就是封装。封装是我们所说的面相对象编程的特征之一。除此之外还有继承和多态。
继承有点类似与我们生物学上的遗传,就是子类的一些特征是来源于父类的,儿子遗传了父亲或母亲的一些性格,或者相貌,又或者是运动天赋。有点种瓜得瓜种豆得豆的意思。面向对象里的继承也就是父类的相关的属性,可以被子类重复使用,子类不必再在自己的类里面重新定义一回,父类里有点我们只要拿过来用就好了。而对于自己类里面需要用到的新的属性和方法,子类就可以自己来扩展了。
当然,会出现一些特殊情况,就是我们在有一些方法在父类已经定义好了,但是子类我们自己再用的时候,发现,其实,我们的虽然都是计算工资的,但是普通员工的工资计算方法跟经理的计算方法是不一样的,所以这个时候,我们就不能直接调用父类的这个计算工资的方法了。这个时候我们就需要用到面向对象的另一个特性,多态。我们要在子类里面把父类里面定义计算工资的方法在子类里面重新实现一遍。多态包含了重载和重写。重写很简单就是把子类从父亲类里继承下来的方法重新写一遍,这样,父类里相同的方法就被覆盖了,当然啦,你还是可以通过super.CaculSalary方法来调用父类的工资计算方法。而重载就是类里面相同方法名,不同形参的情况,可以是形参类型不同或者形参个数不同,或者形参顺序不同,但是不能使返回值。
2. Java对象初始化顺序
- 静态语句块:在类加载到JVM时执行,由于JVM使用类的前提是类装载到JVM虚拟机,所以静态语句块首先执行,类只加载一次,静态语句块只执行一次。
- 静态变量初始化:由于静态变量为类的所有实例所共享,不依赖于具体的对象,因此不是在对象创建时初始化,而是在类加载时初始化,初始化顺序在静态语句块之后,只执行一次。
- 实例语句块:在构造方法前执行,每调用一次构造方法,执行一次。执行顺序在静态变量初始化之后,以成员变量初始化为参照,没有绝对的先后顺序,实际顺序取决于定义的顺序,定义在前,就在成员变量初始化前执行;定义在后,就在成员变量初始化后执行。
- 成员变量初始化:在构造方法前执行,每调用一次构造方法,执行一次。执行顺序在静态变量初始化之后,相对于实例语句块的执行顺序取决于定义顺序。
- 构造方法:最后执行。
综上可知,java对象初始化的执行顺序为:静态语句块>静态变量初始化>实例语句块/成员变量初始化>构造方法,实例语句块与成员变量初始化的相对执行顺序取决于定义的先后顺序。
package com.javase.classtest;
public class InitializationSequence {
static {
System.out.println("静态语句块");
}
static MyInnerClass in = new MyInnerClass("静态变量s");
MyInnerClass in01 = new MyInnerClass("a");
{
System.out.println("实例语句块");
}
MyInnerClass in02 = new MyInnerClass("b");
public InitializationSequence() {
System.out.println("构造函数");
}
@SuppressWarnings("unused")
public static void main(String[] args) {
InitializationSequence obj01 = new InitializationSequence();
System.out.println("-----------------------------------");
InitializationSequence obj02 = new InitializationSequence();
}
}
class MyInnerClass {
public MyInnerClass(String description) {
System.out.println("成员变量初始化::" + description);
}
}
3. Overload 和 Override 的区别?Overload 的方法是否可以改变返回值的类型?
4. int 和Integer有什么区别?
(1)基本使用对比:
- Integer是int的包装类;int是基本数据类型;
- Integer变量必须实例化后才能使用;int变量不需要;
- Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
- Integer的默认值是null;int的默认值是0。
(2)深入对比
(1)由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
(2)Integer变量和int变量比较时,只要两个变量的值是相等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
(3)非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。因为非new生成的Integer变量指向的是静态常量池中cache数组中存储的指向了堆中的Integer对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的对象引用(地址)不同。
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
(4)对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
**对于第4条的原因: java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100)。而java API中对Integer类型的valueOf的定义如下,对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127这个Integer对象进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了。
public static Integer valueOf(int i){
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
5. char 型变量中能不能存贮一个中文汉字,为什么?
char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。
补充:使用 Unicode 意味着字符在 JVM 内部和外部有不同的表现形式,在 JVM内部都是 Unicode,当这个字符被从 JVM 内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以 Java 中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如 InputStreamReader 和 OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于 C 程序员来说,要完成这样的编码转换恐怕要依赖于 union(联合体/共用体)共享内存的特征来实现了。
6.Java 中,Serializable 与 Externalizable 的区别?
- 通过实现java.io.Serializable,您可以获得类的对象的"自动"序列化功能。不需要实现任何其他逻辑,它只会工作。 Java运行时将使用反射来确定如何编组和解组对象。
- 在早期版本的Java中,反射非常慢,因此序列化大对象图(例如在客户端 - 服务器RMI应用程序中)是一个性能问题。为了处理这种情况,提供了java.io.Externalizable接口,它类似于java.io.Serializable,但是使用自定义编写的机制来执行编组和解组功能(您需要在类上实现readExternal和writeExternal方法)。这为您提供了解决反射性能瓶颈的方法。
- 在Java的最新版本(当然是1.3版本)中,反射的性能比以前要好得多,因此这不是一个问题。
- Externalizable的一大缺点是你必须自己维护这个逻辑 - 如果你在类中添加,删除或更改一个字段,你必须改变你的writeExternal / readExternal方法来解释它。
Serializable和Externalizable之间的主要区别
- 标记界面:Serializable是没有任何方法的标记界面。 Externalizable接口包含两个方法:writeExternal()和readExternal()。
- 序列化过程:对于实现Serializable接口的类,将启动默认序列化过程。程序员定义的序列化过程将被启动用于实现Externalizable接口的类。
- 维护:不兼容的更改可能会破坏序列化。
- 向后兼容性和控制:如果您必须支持多个版本,则可以使用Externalizable接口完全控制。您可以支持对象的不同版本。如果您实现Externalizable,则您有责任序列化super类
- public No-arg构造函数:Serializable使用反射构造对象,不需要arg构造函数。但是Externalizable需要public no-arg构造函数。
详细信息参考:https://www.codenong.com/817853/
7.抽象类和接口有什么区别?
接口 | 抽象类 |
被类实现 | 被子类继承 |
只能声明抽象方法 | 可以声明抽象方法,也可以写非抽象方法 |
变量只能是公共的静态的常量 | 普通变量 |
实现来使用, 可以多实现 | 继承来使用, 无法多继承 |
不允许包含static方法(静态方法不能被子类重写,因此接口中不能声明静态方法) | 可以包含static方法 |
不能有构造方法 | 可以有构造方法 |
8.String 和 StringBuilder、StringBuffer 的区别?
1.可变与不可变
String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。
private final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。
char[] value;
2.是否多线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
3.StringBuilder与StringBuffer共同点
StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。
抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。
StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer会在方法上加synchronized关键字,进行同步。
最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
9.阐述 final、finally、finalize 的区别。
- final可以用于修饰变量,方法,类,被修饰的变量的值不能被改变,被修饰的方法不能被重写,被修饰的类不能被继承,它和abstract是反义词
- finally通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常运行还是发生异常,这里的代码只要JVM不关闭都会执行,可以将释放外部资源的代码写在finally块中
- finalize:Object类的方法,Java中允许使用finalize()方法在垃圾回收器将对象从内存中清除出去之前做必要的清理工作,这个方法是由垃圾回收器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作
10.Java 中的异常处理机制的简单原理和应用。
异常是指java程序运行时(非编译)所发生的非正常情况或错误。与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception。
Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了,例如,说内存溢出和线程死锁等系统问题。
Exception表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常
- 系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);
- 普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。
java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try…catch处理或用throws声明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以,编译器不强制用try…catch处理或用throws声明,所以系统异常也称为unchecked异常。