1.软件设计六大原则
1 单一职责原则
原则思想 : 一个方法只做一件事,一个类只负责一个职责,这样当前职责改动,不会对其他程序产生影响
常识原则,几乎所有程序员都会遵循这个原则
优点 : 降低类和类之间的耦合度,提高可读性,可维护性,可扩展性,降低可变性的风险
2 里氏替换原则
原则思想 : 能使用父类的地方,就一定可以使用子类
子类还可以扩展父类的功能,但是不能更改父类的功能,并且子类还可以有自己特有的功能
优点 : 增加了程序的健壮性,即使有了新的子类,原子类不受影响,可以正常执行
3 依赖倒置原则
原则思想 : 高层次不应该依赖低层次,都应该依赖于抽象
优点 : 拥抱多种变化,可以减少需求变化带来的工作量
4 接口隔离原则
原则思想 : 类和接口之间应该建立在最小接口上
5 迪米特法则
原则思想 : 又称为最少知识原则,一个对象尽可能的少了解其他对象
一个类中尽量减少对其他类的依赖
6 开闭原则
原则思想 : 对修改关闭,对扩展开放
尽量通过扩展软件实体来解决需求的变化,而不是通过修改已有的代码来完成变化
2.什么是多态 :
多态就是里氏替换原则的一种体现
能使用父类的地方就一定可以使用子类
多态 就是 父类引用指向子类对象
父类引用 : 使用父类类型 创建的引用类型变量
指向 : 通过这个引用可以找到谁
通过父类创建的一个引用类型变量,找到了子类的对象
父类类型 变量名 = new 子类();
Animal a = new Animal();
Animal a = new Cat() ; 多态
有变量的地方,就能发生多态,并且多态发生在赋值的时候
多态的缺点 : 丢失子类特有的属性
3.多态的几种形式 :
1 直接多态 : 父类 变量 = new 子类();
2 形参/实参 : 参数列表用父类声明, 调用方法 传递子类对象
public void m1(Animal a){}
m1(new Cat());
3 返回值多态 : 返回值类型是父类类型,但是返回的对象 是子类对象
public Animal m2(){
return new Cat();
}
Animal a = m2();
4.多态进行属性调用 :
1 父类没有的,都调用不了(因为多态缺点,丢失子类特有的属性)
2 父类有的,子类没有的,执行父类
3 父类和子类都有,除了成员方法执行子类,其他全部执行父类(因为只有成员方法可以覆写)
// 直接多态
Animal a = new Cat();
// 返回值多态
Animal i = m1();
// 形参实参多态
m2(new Cat());
}
public static void m2(Animal a ){
}
public static Animal m1(){
return new Cat();
}
5.多态的优点
// 需求 : 编写方法,要求能够接收所有的对象,并调用该对象的move方法
// 如果后续想接收其他动物,就需要改动源码,扩展性不强
// 比如我有钱了,想养老虎,那么我这里就需要更改,添加一个接受老虎的接口
public static void test(Cat_1 cat) {
cat.move();
}
public static void test(Dog_1 dog) {
dog.move();
}
// 多态形式,只要传递的对象是Animal_1的子类都是可以的
// 所以后续想接收其他动物,这里也不需要改动,更能拥抱变化
// 就算我有钱了想养老虎,老虎属于动物,我这里还是不用更改
public static void test(Animal_1 animal_1) {
animal_1.move();
6.缺点:丢失子类特有的属性
// 编译阶段,和对象没有关系,就算赋值为null都可以,大不了运行时出现空指针异常,但是是可以运行的
// 说明 编译时检查语法结构的时候,和是哪个对象没有关系,就看类型
Sub_03 sub = null;
System.out.println(sub.age);
// 同理 ,就算后面不是null,编译阶段也和对象没有关系,都没有运行,哪来的对象
// 目前编译器只知道对应的 Sup_03 中 没有age属性,所以编译时就报错了
// 所以 导致多态的缺点 : 丢失子类特有的属性
Sup_03 sup = new Sub_03();
// System.out.println(sup.age);
// 类型转换为子类类型,就能访问
Sub_03 sub1 = (Sub_03) sup;
System.out.println(sub1.age);
7.多态 : 又叫向上转型
子类到父类 是向上
父类到子类 是向下
想要向下转型 必须先发生向上转型
public static void main(String[] args) {
Animal a = new Cat();
// 强制类型转换为cat类型
// Cat c = (Cat)a;
// java.lang.ClassCastException: _10_Polymorphic.Cat cannot be cast to _10_Polymorphic.Dog
// 强制转换 可能会出现类型转换异常
// Dog d = (Dog) a;
// instanceof : 判断某个对象是否由某个类实例化而来
if (a instanceof Dog) {
Dog d = (Dog) a;
System.out.println("狗");
}else if (a instanceof Cat) {
Cat c1 = (Cat)a;
System.out.println("猫");
}
8.多态属性的调用
1.父类没有的,都调用不了(因为多态的缺点,丢失子类特有属性)
2.父类有的,子类没有的执行父类
3.父类和子类都有的,除了成员方法执行子类,其他全部执行父类(应为成员方法可以覆写)
9.比较隐秘的多态
class Sup_04 {
int i = 22;
public void m1() {
// this 保存的是子类的内存地址,因为用哪个对象调用的这个方法,this就指向谁
// this 是对象中第一个成员变量,保存当前对象的内存地址
// 既然是变量,肯定有类型,可以保存当前类对象的类型有
// 1 当前类类型(this所在的类就是当前类)
// 2 父类类型(多态,缺点 : 丢失子类特有的属性)
// 而 this 可以调用当前类中特有的属性,所以 this的类型 应该是当前类类型
// this在哪个类,哪个类就是当前类,而现在这个程序 this在 Sup_04 类中
// 所以 就等于是 Sup_04 this;
// 而 谁调用的这个方法,this就指向谁,这个m1方法最终是由 new Sub_04() 调用的,所以 this指向 new Sub_04()
// 相当于 : Sup_04 this = new Sub_04();
// 总结 : 通过子类,调用父类的方法的时候,此时父类这个方法中的上下文环境,就是多态环境
System.out.println("m1--"+this);
System.out.println(this.i);
m2();
10.抽象 : 抽象类往往表示设计中得出的抽象概念
比如 动物 , 它只是一个抽象的概念,并没有具体的一个东西叫动物,所以它并不能代表一个实体
这种情况下 我们就适合把他作为一个抽象类
比如 Animal 表示所有动物,但是没有一个具体东西叫动物,
它可以作为所有动物的祖类,可以把所有动物共有的属性和行为保存到Animal中
然后再有Cat , Dog之类的 具体的动物类,都继承Animal类,并且Cat还具有他们特有的属性
那么此时 Cat 表示猫,而一个Cat对象就表示具体的某一个猫
但是 Animal 不应该有对象,因为没有一个东西是叫动物,动物是一个统称
那么此时 Animal 就适合作为抽象类
11.abstract : 修饰符,修饰的类是抽象类,修饰的成员方法是抽象方法
抽象方法 : 使用abstract修饰的成员方法,并且该方法没有方法体,就是没有大括号
抽象类 : 不能被实例化,只能用于被继承
一般抽象类中,有功能的定义,但是没有功能的实现,要求必须让子类覆写
比如我们的Animal和Cat,使用多态可以降低耦合度,但是Animal中 ,只需要有move的方法,不需要有具体的实现
因为具体实现应该由Cat实现,但是Animal中还必须得有,要么多态使用不了,丢失子类特有的属性
但是如果Animal中写了move方法,那么子类中 覆写不覆写都可以,根本没办法强制要求
这个时候 可以再Animal中编写一个抽象方法,但是抽象方法必须要在抽象类中的
所以 继承这个抽象类之后,必须要实现抽象类中的所有抽象方法
实现 : 子类对抽象方法加上方法体,完成功能实现
含有抽象方法的类型一定是抽象类 正确
抽象类中一定含有抽象方法 错误
抽象类和普通类几乎完全一样,只有两点不同
1 不能创建对象
2 可以有抽象方法
非抽象类继承抽象类 必须实现所有的抽象方法
抽象类继承一个抽象类 可以实现 0~N个抽象方法
12.抽象语法
abstract class A{
public abstract void m1();
public abstract void m2();
}
// 抽象类继承一个抽象类 可以实现 0~N个抽象方法
abstract class B extends A{
@Override
public void m1() {
}
}
// 非抽象类继承抽象类 必须实现所有的抽象方法
class C extends A{
@Override
public void m1() {
}
@Override
public void m2() {
}
13.注意 :
abstract 和 final 不能同时出现
abstract修饰的类 是抽象类,就是用来被继承的 而 final修饰的类 不能被继承
abstract修饰的方法 是抽象方法,而抽象方法就是用来被覆写实现的, 而 final修饰的成员方法 不能被覆写
static public void main(String[] args) {
}
}
// abstract final class A1 {
abstract class A1 {
// public abstract final void m1();
abstract public void m1();
14. 引用数据类型 : 类,数组,接口
接口是一种引用数据类型,可以看做是一个特殊的类,是为了解决java中单继承功能变弱问题
一个类只能继承一个父类,但是可以实现N个接口
1 [修饰符] interface 接口名 { }
2 1.8之前 接口中只有抽象方法和常量(public static final)
-2147483648~2147483647
3 接口中 public static fianl 还有 abstract 都可以省略(接口中没有变量,只有常量,写个方法就是抽象方法)
4 接口没有构造方法,不能创建对象
5 一个类 实现一个接口,需要实现所有的抽象方法
6 一个抽象类 实现一个接口,需要实现0~N个抽象方法
15.Java1.8接口新特性
1.8 之前 只能有抽象方法
1.8开始 可以有静态方法和默认方法(可以理解为类中的成员方法)
静态方法 和 常量 使用接口名.xxx 调用就行
而默认方法,需要子类对象调用才行
1.9开始 支持private方法
16.用法
17.// psf 可以省略
String B = "xxx";
public abstract void m1();
// public 和 abstract 可以省略
void m2();
// 静态方法
public static void m3(){
System.out.println("静态方法");
}
public default void m4(){
System.out.println("默认方法");
}
18.interface A{
void m1();
}
interface B {
void m2();
}
// 接口和接口之间是多继承 多个以逗号隔开
interface C extends A,B {
void m3();
19.多实现
// 类和接口直接是多实现,保留类继承,所以 当抽象类和接口都能满足需求的时候,优先使用接口
// 一个非抽象类实现接口后,需要实现所有抽象方法
// 一个抽象类实现接口后,需要实现0~N个抽象方法
class D extends Object implements A,B{
@Override
public void m2() {
// TODO Auto-generated method stub
}
@Override
public void m1() {
// TODO Auto-generated method stub
}