目录
面向对象篇
- 面向对象
- 封装
- 继承
- 多态
1.面向对象
1.1.什么是面向对象?
1.2.面向对象与面向过程
假如你要买手机:
1.面向过程的买手机方式:注重的是买手机的过程,少了一个环节可能都不行
按照该种方式来写代码,将来扩展或者维护起来会比较麻烦。
2.而面向对象的买手机方式:就不关注买手机的过程,具体怎么买,怎么激活,这些都交给秘书去 处理,你只要通过对象之间的交互完成即可--->手机,秘书,使用
注意:面向过程和面相对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景。
2.封装
2.1.什么是封装?
概念:在实现上采用private修饰成员变量和成员方法,这样在类外就不能直接访问了;达到了
对外隐藏实现的细节,只提供公开的方法,来操作你的这些封装好的成员变量和成员方法
简述:公开的方法 和 私有的属性之间结合
看代码:
class Cat {
//private修饰成员变量
private String name;
private int age;
private String color;
//提供公开的对应的Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class TestDemo {
public static void main(String[] args) {
Cat cat = new Cat();
cat.setName("喵喵");//设置值
String name = cat.getName();//获取值
System.out.println(name);
}
}
也可以用构造方法来达到这种效果。
封装的另一好处:降低代码的耦合度
在公司里,每个人负责自己的代码部分,如果这样写代码的饿话,某一天,你的同时心情很差,就把name改成myname了,那你的代码部分相应的地方也需要做修改,耦合度就很高,但是封装之后,改成员名就丝毫不影响你的代码,你只需要通过get,set来设置值,获取值即可。
3.继承
3.1.为什么需要继承?什么是继承?
![](https://i-blog.csdnimg.cn/blog_migrate/851bf7d01d557f2e487204e163ba7c66.png)
那能否将这些共性抽取呢?面相对象思想中提出了继承的概念,专门用来进行共性抽取,实现代 码复用。
再谈谈什么是继承:
![](https://i-blog.csdnimg.cn/blog_migrate/7861ad37a48b0bbc05721c30354b2091.png)
上图所示:继承之后,你会发现,共同的成员变量只需要在父类中写好即可,子类中只需要关心新增的成员变量。
3.2.继承的语法
![](https://i-blog.csdnimg.cn/blog_migrate/bad14e8d047e5feab8642a091d13c674.png)
3.3.重载
1.什么是重载:重载针对成员方法,方法重载的三个条件:
1.方法名相同; 2.方法的参数列表不相同(参数个数,参数类型,不同类型参数的顺序不同); 3.方法的返回值不做要求
重载的好处,比如:当我们要使用同一个自定义方法时, 我们要实现的逻辑相同,但是参数不同,或者参数类型和顺序不同的时候,使用方法的重载避免了再重新定义一个函数名,这样就很方便,而不会说像这样:两个参数的add方法,我给它定义成add1(), 而三个参数的方法,我给它定义成add2().........
3.4.父类成员的访问
- 如果访问的成员变量子类中有,优先访问自己的成员变量。
- 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
- 如果访问的成员变量与父类中成员变量同名,则优先访问自己的(不管类型是否有差异),即:子类将父类同名成员隐藏了。
让我们来看一下运行结果证明以下以上三点说法:
从而得出结论:成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
3.5. 父类成员方法的访问
1. 成员方法名字不同
父类有,就调用父类的,父类没有,就调用子类的,都没有,编译错误。都是可以直接访问的
![](https://i-blog.csdnimg.cn/blog_migrate/a2213481ecb920a37065640fde2a9ec6.png)
【说明】
- 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
- 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致(重写-后面讲),则只能访问到子类的,父类的无法通过派生类对象直接访问到。
3.6.super
由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员
![](https://i-blog.csdnimg.cn/blog_migrate/87868186c5e5eb47f73c5f19e61e2912.png)
在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。
【注意事项】
3.7.super和this的区别
1.相同点:
a.都是Java中的关键字
b.只能在类的非静态方法中使用,用来访问非静态成员方法和字段
c.在构造方法中调用时,必须是 构造方法中的第一条语句,并且不能同时存在
2.不同点
a.this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父 类继承下来部分成员的引用
b.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方 法和属性
c.this是非静态成员方法的一个隐藏参数,super不是隐藏参数
d.成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this 来访问的;在子类中如果通过super访问父类成员,编译之后在字节码super实际是不存在的
e.在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用 不能同时在构造方法中存在
f.继承里面构造方法中一定会存在super(...)的调用,用户没有写,编译器也会增加,但是this(...) 用户不写则没有
3.7.final关键字
![](https://i-blog.csdnimg.cn/blog_migrate/a40c91572b8aebe74cbc3e5d07b42128.png)
2.修饰类,表示该类不能被继承(不能派生出子类),也叫密封类;
3.修饰方法,该方法就不能被重写
4.多态
多态:简而言之,就是一种事物,多种形态
4.1.多态实现条件
1.必须在继承体系下
2.子类必须要对父类中的方法进行重写
3.通过父类的引用调用重写的方法
多态的体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法
多态的思想:同一个方法,因为调用这个方法的引用,引用的对象不同,所执行的行为也不一样
4.2.方法的重写
【重写规则】:
1.方法名称相同;2.方法的返回值相同;3.方法的参数列表相同;
4.非静态方法,非private修饰的方法,非final方法;
5.子类的访问修饰限定符的权限要大于等于父类的;
6.JDK以后,被重写的方法返回值类型可以不同,但是必须是具体父子关系的;
【重写和重载的区别】
区别点 | 重载 | 重写(override) |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
访问限定符 | 可以修改 | 一定不能做严格的限制(可以降低限制) |
![](https://i-blog.csdnimg.cn/blog_migrate/a354786ec887de0aa28be5ba7ade16d6.png)
【重写的设计原则】
4.3.向上转型
什么是向上转型? --> 父类的引用引用了子类的对象
向上转型的三种方式:1.直接赋值 (上图就是直接赋值) 2.方法的参数 3.方法的返回值
2.方法的参数:
3.方法的返回值
4.4.动态绑定
什么是动态绑定:通过父类的引用,调用重写的方法,此时会发生动态绑定
动态绑定的三个条件:
1.在继承体系下;2.子类必须要对父类中方法进行重写;3.通过父类的引用调用重写的方法
【分析】
图中代码 animal.eat();为什么是调用子类的eat方法,实际上编译的时候,这里还是调用animal自己的eat方法,但是在运行的时候,就变了调用子类的eat方法了,所以动态绑定又叫运行时绑定
补充:既然有动态绑定,那就有静态绑定(编译期绑定),例如:重载就是静态绑定。
4.5.向下转型(了解即可)
向下转型:子类的引用引用父类(不安全)
如何解决不安全问题?利用关键字:instanceof
4.6.多态的优缺点
【使用多态的好处】
1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else
什么叫 " 圈复杂度 " ?圈复杂度是一种描述一段代码复杂程度的方式 . 一段代码如果平铺直叙 , 那么就比较简单容易理解 . 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂 .因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数 , 这个个数就称为 " 圈复杂度 ". 如果一个方法的圈复杂度太高, 就需要考虑重构 .不同公司对于代码的圈复杂度的规范不一样 . 一般不会超过 10 .
例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:
4.7.避免在构造方法中调用重写的方法
这里为什么是调用子类的func方法?
- 构造 D 对象的同时, 会调用 B 的构造方法
- B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
-
此时 D 对象自身还没有构造 , 此时 num 处在未初始化的状态 , 值为 0.