一、面向对象基础
1、面向对象思想
Java是一种面向对象的编程语言。面向过程关注的是如何去执行;面向对象关注的是具备某种功能的对象。面向过程→面向对象:执行者→指挥者。
例如:把大象放进冰箱
面向过程:打开冰箱,把大象放进冰箱,关闭冰箱;
面向对象:指挥(调度)一个会操作冰箱的和一个搬运大象的去完成这项工作。
三大思想:
1. OOA:面向对象分析;
2. OOD:面向对象设计;
3. OOP:面向对象编程;
三大特征:(重点)
1. 封装性:将不想让外部知道的内容封装起来,只提供接口。
2. 继承性:子类继承父类的功能,提高代码复用性。
3. 多态性:是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,使得同一个属性或方法在父类及其各个子类中具有不同的含义。
- 编译时多态:方法的重载。
- 运行时多态:方法的重写,通过动态绑定来实现的。(大家通常所说的多态)
实现多态的3个必要条件:继承、重写、向上转型。
1. 继承:在多态中必须存在有继承关系的子类和父类。
2. 重写:子类重写父类的方法,在方法调用时就会调用子类的方法。
3. 向上转型:需要将子类的引用赋值给父类。
二、面向对象进阶
1、this 关键字
表示当前对象。调用类中的属性;调用类中的方法或构造方法。
2、static 关键字
static表示静态的意思,可以用来修饰成员变量和成员方法。static主要作用于创建独立于具体对象的域变量或者方法,static修饰的方法或成员变量可以通过类名直接访问。
特点:
1. 静态成员在类加载时加载并初始化。
2. 无论一个类存在多少个对象,静态的属性,永远在内存中只有一份。
3. 访问时:静态不能访问非静态,非静态可以访问静态。
3、代码块
1.普通代码块:在执行的流程中出现的代码块。
2.构造代码块:在类中的成员代码块,在每次对象创建时执行,执行在构造方法之前。
3.静态代码块:在类中使用static修饰的成员代码块,在类加载时执行,从启动到关闭,只会执行一次。
4.同步代码块:使用synchronized修饰的代码块。
面试题:执行顺序:静态代码块→构造代码块→构造方法→普通代码块
三、面向对象高级
1、抽象类
抽象类必须使用abstract class声明,一个抽象类中可以没有抽象方法,抽象方法必须使用abstract关键字声明。
特点:
1. 抽象类不能使用final声明,抽象类能有构造方法。
2. 抽象类必须用public或protected修饰,默认缺省为public。
3. 抽象类不能使用new实例化,一个抽象类必须被子类所继承,被继承的子类必须重写抽象类中的全部抽象方法,如果有未实现的抽象方法,那么子类必须为abstract类。
2、接口
1. 接口是更严格的抽象类,全部方法都是抽象方法,全部属性都是全局常量。
2. 面向接口编程思想:接口是定义与实现的分离。
- 优点:降低程序的耦合性;易于程序的扩展;有利于程序的维护。
3. 接口因为都是抽象部分,不存在具体的实现,所以允许多继承。一个接口要想使用,必须依靠子类。子类如果不是抽象类则要实现接口中的所有抽象方法。
4. 因为接口本身都是由全局常量和抽象方法组成,所以接口中的成员定义可以简写:
- 全局常量:省略public static final
- 抽象方法:省略public abstract
3、接口和抽象类的区别
1. 抽象类要被子类继承(extends),接口要被抽象类实现(implements)。
2. 接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
3. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
4. 抽象类使用继承来使用,无法多继承。接口使用实现来使用,可以多实现。
5. 抽象类中可以包含static方法,但是接口不允许(静态方法不能被子类重写)
6. 接口不能有构造方法,抽象类可以有。
4、多态
多态就是对象的多种表现形式。子类就是父类的一种形态。
重载:一个类中方法的多态性体现。
重写:子父类中方法的多态性体现。
向上转型:父类 父类对象 = 子类实例;
5、instanceof 关键字
作用:判断某个对象是否是指定类的实例。
格式:实例化对象 instanceof 类
6、Object 类
1. Object类是所有类的父类,如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
2. Object的多态:使用Object可以接收任意的引用数据类型。
3. toString:Object中的toString方法返回对象的内存地址。建议重写toString方法。
7、equals 方法和 hashCode方法(重点)
1. Object类中的equals方法等同于“==”,比较的是地址值是否相等。
2. String类和包装类中的equals方法重写了,比较的是内容是否相等。
3. equals方法重写时的特性:自反性、对称性、传递性、一致性、非空性。
建议重写自定义类的equals方法,因为自定义类继承了Object类的equals方法,比较的是地址值。
注意:
重写equals方法时,一定要重写hashCode方法。
为什么要重写hashCode方法:
因为equals和hashCode存在如下关系:为了确保equals相等。
1. 两个对象的equals相等,hashCode一定相等;
2. 两个对象的equals不相等,hashCode一定不相等;
3. 两个对象的hashCode相等,equals不一定相等;
4. 两个对象的hashCode不相等,equals一定不相等;
注意:
为了防止内存泄露,如果将对象的属性参与到hashCode的运算中,在进行删除时,就不能对其属性进行修改,否则会出现严重问题。
8、内部类
1、成员内部类
1. 位置:位于另一个类的内部。
2. 特点:成员内部类可以无条件访问外部类的所有成员属性和成员方法。如果内部类拥有和外部类相同的成员属性和成员方法时,遵循就近原则,即访问的是内部类的成员属性和方法。
3. 内部类访问外部类的同名成员:
- 外部类.this.成员变量/成员方法
4. 外部类访问内部类:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner();
2、局部内部类
1. 位置:定义在一个方法或者一个作用域里面。
2. 特点:局部内部类的访问仅限于方法内或该作用域内。
注意:局部内部类就像是方法里面的一个局部变量,不能有public、protected、private、static修饰符。
3、匿名内部类
1. 格式:
new 父类构造器(参数列表)| 实现接口(){
//匿名内部类的类体部分
}
2. 特点:匿名内部类必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类
或者实现一个接口。只能使用一次。
注意:
1. 必须且同时只能继承一个类或实现一个接口。
2. 匿名内部类中不能定义构造器。
3. 匿名内部类中不能定义任何静态成员变量和静态方法。
4. 匿名内部类为局部内部类,限制同局部内部类。
5. 匿名内部类不能是抽象的,必须要实现类或接口的所有抽象方法。
6. 只能访问final型的局部变量。
4、静态内部类
1. 位置:同局部内部类
2. 特点:静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法。
9、包装类
1. 自动装箱:基本数据类型→包装类
2. 自动拆箱:包装类→基本数据类型
10、可变参数
一个方法中定义完了参数,则在调用的时候必须传入与其一一对应的参数,但是在JDK 1.5之后提供了新的功能,可以根据需要自动传入任意个数的参数。
语法:
返回值类型 方法名(数据类型...参数名称){
//参数在方法内部,以数组的形式接收
}
注意:可变参数只能出现在参数列表的最后。
11、递归
递归算法是一种直接或间接调用自身方法的算法。
注意:一定要明确递归结束条件。
四、异常
异常是在程序中导致程序中断运行的一种指令流。
处理异常格式:
try{
// 有可能发生异常的代码段
}catch(异常类型1 对象名1){
// 异常的处理操作
}catch(异常类型2 对象名2){异常体系结构
// 异常的处理操作
} ...
finally{
// 异常的统一出口
}
1、try + catch 的处理流程
1. 一旦产生异常,则系统会自动产生一个异常类的实例化对象。
2. 异常发生在try语句,则会自动找到匹配的catch语句执行,如果没有在try语句中,则会将异常抛出。
3. 所有的catch根据方法的参数匹配异常类的实例化对象,如果匹配成功,则表示由此catch进行处理。
2、finally
在进行异常的处理之后,finally作为异常的统一出口,最终都要执行此段代码(电脑关机等外界因素除外)。
3、异常体系结构
1. Error:表示的是错误,是JVM发出的错误操作,只能尽量避免,无法用代码处理。
2. Exception:一般表示所有程序中的错误,一般在程序中进行try…catch的处理。
注意:
捕获更大的异常不能放在捕获更小的异常之前。
可以将所有的异常都使用Exception进行捕获。
多异常捕获:
catch(异常类型1 | 异常类型2 对象名){
//表示此块用于处理异常类型1、2的异常信息
}
4、throws 和 throw
throws:定义在方法上,不处理异常,而是向上抛出,给调用者处理。调用者可以不处理,继续向上抛出,直到最顶层处理。
throw:在程序中人为的抛出一个异常。
例如: throw new Exception();
5、Exception 和 RuntimeException 的区别
1. Exception :受检查的异常,这种异常是强制我们catch或throw的异常。你遇到这种异常必须进行catch或throw,如果不处理,编译器会报错。比如:IOException。
2. RuntimeException:运行时异常,这种异常我们不需要处理,完全由虚拟机接管。比如我们常见的NullPointerException,我们在写程序时不会进行catch或throw。
6、异常处理常见面试题
1. try-catch-finally中哪个部分可以省略?
finally可以省略。
2. try-catch-finally中,如果catch中return了,finally还会执行吗?
会。
执行流程:
1. 先计算返回值,并将返回值存储起来,等待返回。
2. 执行finally代码块。
3. 将之前存储的返回值返回出去。
注意:
1. 返回值是在finally运算之前就确定了并且缓存了,所以finally对其值的任何改变,返回的值都不会改变。
2. finally代码块中不建议包含return,因为程序会在finally中的return处退出,返回的值不是try-catch中的值。
3. 如果try-catch中停止了JVM,则finally不会执行。例如停电等,或者是通过JVM:System.exit(0)。
五、泛型
泛型是将类型由原来具体的类型参数化,在使用或调用时传入具体的类型。
1. 泛型类:
格式:
public class ClassName<T>{
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
2. 泛型接口:
格式:
public interface IntercaceName<T>{
T getData();
}
指定类型:
public class Interface1 implements IntercaceName<String> {
private String text;
@Override
public String getData() {
return text;
}
}
不指定类型:
public class Interface1<T> implements IntercaceName<T> {
private T data;
@Override
public T getData() {
return data;
}
}
3. 泛型方法:
private static <T> T 方法名(T a, T b) {}
1、泛型限定类型
在使用泛型时,可以指定泛型的限定区域。
例如:必须是某类的子类或某接口的实现类
<T extends 类或接口1 & 接口2>
2、泛型通配符 ?
1. <? extends Parent>:指定了泛型类型的上界
2. <? super Child>:指定了泛型类型的下届
3. <?>:指定了没有限制的泛型类型
3、泛型的作用
1. 提高代码复用率。
2. 泛型中的类型在使用时指定,不需要强制类型转换。
4、去泛型化
在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。