面向对象进阶(一)
一、继承
1. 继承概述
1.1什么是继承?
- 继承是类与类之间的一种关系。
- 多个类继承单独的某个类,多个类就可以使用单独的这个类的属性和行为。
- 多个类称为子类(派生类),单独的这个类称为父类(基类或超类)。
1.2 为什么用继承?
- 使用继承的好处:提高代码的复用。减少代码冗余、增强类的功能拓展性。
1.3 继承后子类的特点:
- 子类继承父类,子类可以得到父类的属性和行为,子类可以使用。
- Java中子类更为强大。
2.继承的设计规范、内存原理
继承设计规范:
- 子类们相同特征(共同属性,共性方法)放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。
为什么?
- 如果子类的独有属性、行为定义在父类中,会导致其他子类也会得到这些属性和行为,这不符合面向对象逻辑。
3.继承的特点
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
- Java是单继承模式:一个类智能继承一个直接父类
- Java不支持多继承,但是支持多层继承
- Java中所有的类都是Object类的子类
4.继承后:成员变量、成员方法的访问特点
在子类方法中访问成员(成员变量、成员方法)满足:就近原则
- 先子类局部范围找
- 然后子类成员范围找
- 然后父类成员范围找,如果父类范围还没有找到则报错
**如果子父类中出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
- 可以通过super关键字,指定访问父类成员。
- 格式:super.父类成员变量/父类成员方法
5. 继承后:方法重写
什么是方法重写
- 在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类的这个方法是重写的方法。
方法重写的应用场景
- 当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。
- 子类可以重写父类中的方法。
@Override重写注解
- @Override是放在重写后的方法上,作为重写是否正确的校验注解。
- 加上该注解后如果重写错误,编译阶段会出现错误提示。
- 建议重写方法都加@Override注解,代码安全,优雅!
方法重写注意事项和要求
- 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
- 私有方法不能被重写
- 子类重写父类方法时,访问权限必须大于或者等于父类
- 子类不能重写父类的静态方法,如果重写会报错。
6.继承后:子类构造器的特点
子类继承父类后构造器的特点
- 子类中所有的构造器默认都会先访问父类中的无参构造器,再执行自己。
为什么
- 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
- 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
怎么调用父类的无参构造器的?
- 子类构造器的第一行默认语句都是:super(),不写也存在
7.继承后:子类构造器访问父类有参构造器
super调用父类的有参构造的作用:
- 初始化继承父类的数据。
如果父类中没有无参构造器,只有有参构造器,会出现什么现象?
- 会报错。因为子类默认调用父类无参构造器的、
如何解决?
- 子类构造器中可以通过书写super(...),手动调用父类的有参构造器
8.this和super的总结
this和super详解
- this代表本类数据的引用;super是表示父类存储空间的标识。
this(...)和super(...)使用注意点:
- 子类通过this(...)去调用本类的其他构造器,本类其他构造器会通过super去手动调用父类的无参构造器,最终还是会调用父类的构造器的。
- 注意:this(...) super(...)都只能放在构造器的第一行,所以二者不能存在于同一个构造器中。
二、包
什么是包
- 包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护。
- 建包的语法格式:package 公司域名倒写.技术名称。包名建议全部小写,且具备意义
- 建包语句必须放在第一行,一般IDEA工具会帮助创建
导包
- 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:import 包名.类名;
- 假如一个类中需要用到不同类,而这两个类的名称是一样的,那么默认只能导入一个类,另一个类需要带包名访问。
三、权限修饰符
什么是权限修饰符
- 权限修饰符:是用来控制一个成员能够被访问的范围的。
- 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制
权限修饰符的分类和具体作用范围:
- 权限修饰符:有四种作用范围由小到大(private --> 缺省(不写) -->protected -->public)
四、final
final的作用
- final关键字是最终的意思,可以修饰(方法,类,变量)
- 修饰方法:表面该方法是最终方法,不能被重写
- 修饰变量:表示该变量第一次赋值后,不能被再次赋值(有且仅能被赋值一次)
- 修饰类:表明该类是最终类,不能被继承。
final修饰变量的注意
-
final修饰的变量是基本类型:那么变量存储的数据值是不能发生改变的。
-
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生改变的。
final int[] arr = {1,2,3,4}; //int[] arr = {1,3};会报错,不可以二次赋值 //修改内容 arr[1] = 22;//可以的,对象的地址不会变
五、常量
常量概述和基本作用
常量
-
常量是使用了public static final修饰的成员变量,必须有初始值,而且执行的过程中其值不能被改变,
-
常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
-
常量命名规范:英文单词全部大写,多个单词用下划线连接起来
常量的执行原理
- 在编译阶段会进行"宏替换",把使用常量的地方全部替换成真是的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
常量做信息标志和分类
- 代码的可读性好,实现了软编码形式。
六、枚举
6.1 枚举的概述
- 枚举是java中的一种特殊类型
- 枚举的作用:"是为了做信息的标志和信息的分类"。
6.2 定义枚举类的格式:
/*
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称。
}
*/
enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
6.3 反编译后观察枚举的特征
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
public static Season valueOf(java.lang.String);
static {};
}
枚举的特征
- 枚举类都是继承了枚举类型:java.lang.Eumn
- 枚举类都是最终类,不可以被继承
- 枚举类的构造器都是私有的,枚举对外不能创建对象
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 枚举类相当于是多例模式。
选择常量做信息标志和分类:
- 虽然可以实现可读性,但是入参值不受约束,代码相对不够严谨。
枚举做信息标志和分类:
- 代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用!
七、抽象类
- 某个父类知道其所有子类要完成某功能,但是每个子类完成情况都不一样,父类就只定义该功能的基本要求,具体实现由子类完成,这个类就可以是一个抽象类,抽象类其实就是一种不完全的设计图。
- 抽象类必须要用abstract修饰;
修饰符 abstract class 类名{}
抽象方法
- 就是抽象类中定义的子类必须完成的功能的基本要求
- 没有方法体,只有方法签名,必须用abstract修饰。
修饰符 abstract 返回值类型 方法名称(形参列表);
抽象类的使用总结与注意事项
- 抽象类用来被继承的,抽象方法是交给子类重写的。
- 一个类如果继承的抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类。
7.1 抽象类特征、注意事项小结
特征
- 有得有失:得到了抽象方法,失去了创建对象的能力。
- 抽象类为什么不可以创建对象?
- 类有的成员(成员对象,方法、构造器)抽象类都具备
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 一个类继承了抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类
- 不能用abstract修饰变量、代码块、构造器。
final和abstract是什么关系?
- 互斥关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
- 抽象方法定义通用功能让子类来重写,final定义的方法子类不能重写。
7.2 抽象类的应用知识:模板方法模式
什么时候使用模板方法
- 使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
模板方法模式实现步骤
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现
模板方法建议使用final修饰,这样更专业,为什么呢?
答:模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了,模板方法就失效了。
模板方法解决了什么问题
- 极大的提高了代码的复用性
- 模板方法已经定义了通用结构,模板不能确定的定义成抽象方法。
八、接口
8.1 什么是接口?
- 接口就是体现规范的,其中用抽象方法定义的一组行为规范,接口是更加彻底的抽象
- 体现了现实世界中,“如果你是这类事物...则必须完成某些行为...”的思想。
8.2 接口的定义与特点
- 接口的定义格式如下:
接口用关键字interface来定义
public interface 接口名{
//常量
//抽象方法
}
- JDK8之前接口中只能是抽象方法和常量,没有其他成分了
- 接口不能实例化。
- 接口中的成员都是public修饰的,写不写都是,因为规范就是为了公开化。
8.3 接口的基本使用:被实现
接口的用法:
- 接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。
修饰符 class 实现类 implements 接口1,接口2,接口3,...{
}
实现的关键字:implements
- 从上面可以看出,接口可以被类单实现,也可以被类多实现。
接口实现的注意事项:
- 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
8.4 接口与接口的关系:多继承
基本小结
- 类和类的关系:单继承
- 类和接口的关系:多实现
- 接口和接口的关系:多继承,一个接口可以同事继承多个接口。
接口多继承的作用
- 规范合并,整合多个接口为同一个接口,便于子类实现。
8.5 JDK8 开始接口新增方法:
JDK8版本开始后,Java只对接口的成员方法进行了新增。
第一种:默认方法
- 类似之前写的普通实例方法:必须用default修饰
- 默认是用public修饰,需要用接口的实现类的对象来调用
public interface SportManInter{
/**
1.JDK8开始:默认方法(实例方法)
必须default修饰,默认用public修饰
默认方法,接口不能创建对象,这个方法只能过继给实现类,由实现类对象调用
*/
default void run(){
System.out.println("开始跑。。。");
}
}
class PingPongMan implements SportManInter{
}
class Test{
public static void main(String[] arsg){
PingPongMan = new PingPongMan();
p.run();
}
}
第二种:静态方法
-
默认会public修饰,必须要用static修饰
-
注意:接口的静态方法的必须用本身的接口名来调用
/** 2.静态方法 必须使用static修饰,默认用public修饰 接口的静态方法,必须接口名自己调用 */ static void inAddr(){ System.out.println("加油学习!"); } class Test{ public static void main(String[] arsg){ SportManInter.inAdrr(); } }
第三种:私有方法
- 就是私有的实例方法,必须用private修饰,从JDK 1.9才开始有的。
- 只能在本类中被其他默认方法或私有方法访问。
/**
3.私有方法(实例方法)
---JDK1.9才开始支持的
--必须在接口内部才能被访问
*/
default void run{
go();
}
private void go(){
System.out.println("开始");
}
8.6 使用接口的注意事项
-
接口不能创建对象
-
一个类实现多个接口,多个接口中有同样的静态方法不冲突
-
一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
-
一个类继承了父类,同时又实现了接口,父类和接口中有同名方法,默认用父类的。
-
一个接口继承多个接口,是没有问题的,如果多个接口中存在规则冲突则不能多继承。