JavaSE类与对象 二
本文不会大篇幅的讲类与对象的基础,主要针对面试 以及我在学习类与对象过程中的疑难和困惑
一、多态
关于面向对象三大特征:封装、继承、多态。
- 封装隐藏了类的内部实现机制,可以不影响使用的情况下改变类的内部结构,同时保护了数据,对外隐藏内部细节,只暴漏其访问的方法。注:反射会破坏程序的封装性
- 继承为了重用父类代码
- 多态:程序中定义的引用变量所指向的实例变量的类型,在编译期间并不确定,亦或该引用变量发出的方法调用是哪个类中实现的方法,必须在程序运行时才能确定具体的类。这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上。
总之多态:同一个引用类型,使用不同的实例而执行不同操作
实现多态的三要求:
- 要有继承或接口实现
- 子类必须重写父类或接口的方法
- 父类引用指向子类对象
1.1关于静态绑定和动态绑定
关于绑定:指一个方法的调用和该方法所属的类(所在的类)相关联,意思就是在执行方法调用的时候,jvm所知道调用了哪个类的方法,类和调用方法相关联
静态绑定:程序在执行前就知道了该方法所属的类,即在编译期已经绑定,在java中只有private、static、修饰的方法和构造方法是静态绑定
- private:所修饰的方法无法被继承,即子类无法调用该方法
- static:被其修饰的方法可以被继承,但子类不能重写父类的静态方法,但如果子类有同名的静态方法,子类可以隐藏父类的该方法。但子类对象向上转型为父类对象时,不论子类有没有定义这个静态方法,都会使用到父类的静态方法
- final:被final修饰的方法虽然可以被继承,但无法重写覆盖,即子类可以调用该方法,实际上还是调用父类的方法
总之:一个方法不可以被继承或 继承后不可以被覆盖,那么和个方法就采用静态绑定
动态绑定:运行时绑定是指在程序运行时根据对象类型进行绑定
运行时绑定通过方法表的结构来处理
1.2多态和设计模式
- 使用设计模式宗旨提高内聚降低耦合,而通过多态可以降低代码耦合度
耦合:模块与模块之间接口的复杂程度,模块之间联系越复杂,耦合度越高,即牵一发动全身
通过多态,使每个模块尽可能独立完成某个特定的子功能
23种设计模式很多都使用到了多态,所以不得谈谈多态的好处
多态提升了代码的可扩展性,在几乎不修改原有代码基础上,加入新的功能,使代码更加健壮,易于维护。
设计模式对于多态的例子比比皆是,面向对象设计有一个最根本的原则“开闭原则”:对添加开放,对修改关闭。而多态提升了代码的可扩展性则体现了开闭原则
1.2.1 对象转型(Casting)
- 向上转型:多态中,父类引用指向子类对象就是向上转型的案例,向上转型不需要强制类型转换
- 向下转型:即父类对象转为子类对象,此时必须进行强制类型转换
提到类型转换,不得不提到LSP(哈哈,名字起的怪怪的)即里氏替换原则,什么意思呢
LSP:任何基类可以出现的地方,子类一定可以出现。即子类可以使用父类继承下来的所有方法,但反过来却不行(LSP反过来却不成立)。
向上转型是安全的,但向下转型并且对象的具体类型不明确时通常需要instanceof 判断类型
1.2.2 instanceof关键字
对象 instanceof 类(接口)
可以判断引用对象所指向的实例 是否属于一个类,或是继承了这个类(或是实现了这个接口)
实际案例中,通常在重写equals方法中使用instanceof关键字,个人感觉不如之间通过判断其Class对象是否一致来的更细致
二、接口
接口是方法声明的集合,让没有如何关系的对象可以相互联系。接口是实现多继承的手段。
1.1接口与类的区别
- 接口中只能定义成员方法,不能定义成员变量。
- 接口中的成员方法都是抽象方法(没有具体实现、全为public abstract)
- 接口中的成员变量都是public static final 修饰的
- 接口没有构造方法,因此不能被实例化
public interface UsbInterface {
int a =10; //本质public static fina int a =10;
void service(); //本质public abstrac void service()
}
1.2 JDK1.8 接口的特性
- 静态方法(使用static来修饰放啊,可以通过接口名直接调用)接口中可以添加抽象方法(static),实现类不能重写,只能通过接口名调用。
interface Phone {
static void makePhone(){
System.out.println("制造手机");
}
}
class XiaomiPhone implements Phone {
//不能重写,也就无法通过多态来调用这个实现方法,只能从属于子类
public void makePhone {
System.out.println("制造小米手机");
}
}
2.接口中可以添加非抽象方法(default),实现类可以重写(但是必须去掉default),只能通过对象名来调用。此外实现类可以直接使用default方法(不重写default方法)。
public interface Phone {
default void play(){
System.out.println("接口调用");
}
}
public class PhoneImpl implements Phone{
//实现类可以重写这个default方法,但default关键字不见了
@Override
public void play() {
Phone.super.play(); //调用上级Phone接口的defalut方法
}
public static void main(String[] args) {
Phone phone=new PhoneImpl();
phone.play();
}
}
1.3 接口和设计模式(依赖倒置原则)
前面讲了设计模式的 LSP原则和开闭原则,主要针对面向对象的设计目标。那么依赖倒置原则则是面向对象的主要机制
依赖倒置原则:要依赖抽象,而非具体。换句话说,依赖倒置针对的就是接口编程,不针对实现编程。依赖接口编程就是应当使用接口或抽象类,强调的是一个系统内实体间关系的灵活性。如果设计者要遵守“开闭原则”,那么依赖倒置原则则是实现开闭原则的途径。
三、浅谈设计模式几个原则
1、单一职责原则:一个类,最好只做一件事。(解耦)
2、开闭原则:软件实体应当对修改关闭,对扩展开放 (提高扩展性)
3、依赖倒置原则:依赖于抽象,而不要依赖于具体,因为抽象相对稳定。
4、接口隔离原则:尽量应用专门的接口,而不是单一得总接口,接口应该面向用户,将依赖建立在最小得接口上。(解耦)
5、LSP原则:子类必须能够替换其基类
6、聚合复用原则:新对象聚合已有对象,使之成为新对象的成员,从而这些对象达到复用的目的。聚合复用相较于继承耦合更加松散,所以多聚合少继承
7、迪米特法则(最少知识原则):软件实体应该尽可能少的和其他软件实体发生相互作用。