🌹作者:云小逸
📝个人主页: 云小逸的主页
📝Github: 云小逸的Github
🤟motto:要敢于一个人默默的面对自己, ==强大自己才是核心==。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==🤟
👏专栏:C++👏 👏专栏:Java语言👏
👏专栏:C语言初阶👏👏专栏:数据结构👏
前言
今天这篇文章主要讲述Java语言的三大特征:继承、封装、多态。
——————————————————————————————
首先先摘抄上几句话:献给坚持创作的我和点开这篇文章希望进步的你:
1.一旦你出现明天开始努力的想法,而不是从现在开始,那明天的你大概率还是虚度光阴。
2.真正让你成长的,永远是那些让你害怕,逃避,疼痛的事情。
3.我们对年龄的恐惧,其实并不在于年龄增长所来的苍老。而是恐惧随着年龄的增长,我们仍然一无所得。
4.你不跳出舒适区,把薪酬都用在吃喝玩乐的陷阱里,日复一日地随波逐流,突破口只会离你越来越远。
5.以前我觉得成绩不重要。清华、北大、复旦、交大°,只能代表学生时代的成就。后来我发现,努力是种习惯,它会贯穿终生。
1.继承
a.继承概述:
(1)什么是继承?
Java允许一个类通过==extends==与另一个类建立父子关系,这就是继承。
(2)继承的格式:
子类 extends父类
public class Student extends People {}
Student称为子类(派生类),People称为父类(基类 或超类)。
(3)继承后子类的特点?
子类继承父类,子类可以得到父类的属性和行为,子类可以使用。
Java中子类更强大
(4)继承的核心优点:
提高代码的复用性,多个子类的相同代码可以放在父类中,增强了类的扩展性。
b.继承的设计规范、内存运行原理
设计规范:子类共用属性和行为放在父类,==子类独有属性和行为==放在子类自己那里。
c.继承的特点
1.子类可以继承父类的属性和行为,但是子类不能继承==父类的构造器==。
类有自己的构造器,父类构造器用于==初始化父类对象==。
2.子类可以继承父类私有成员吗?
==有争议的==.
通常意义上说Java的子类不能继承父类的private变量。因为不能直接的访问父类的private变量.但是当你实例化子类时,在内存中有一个父类的私有变量已经放到了内存中。这一点通过子类访问父类的public或protect方法可以访问到父类的私有变量。
3.子类可以直接用子类名访问父类静态成员。那么子类继承了父类静态成员吗??
==有争议的知识点==。
子类可以直接使用父类的静态成员(共享)
但个人认为:==子类不能继承父类的静态成员。(共享并非继承)==
4.Java是单继承模式:一个类只能继承一个直接父类。
单继承:子类只能继承一个直接父类
不支持多继承:子类不能同时继承多个父类
public class 子类C extends 父类A , 父类B{
public static void main(String[] args) {
子类 z = new 子类();
z.method(); // 复习啥?出现二义性
//听哪个爸爸的呢?java懵了!因此不支持多继承
}
}
5.Java不支持多继承、但是支持多层继承。
子类 A 继承父类 B ,父类B 可以 继承父类 C
6.Java中所有的类都是Object类的子类。
Java中所有类,要么直接继承了Object , 要么默认继承了Object , 要么间接继承了Object, Object是祖宗类。
d.继承后:成员变量、成员方法的访问特点
(1)就近原则
先子类局部范围找
然后子类成员范围找
然后父类成员范围找,如果父类范围还没有找到则报错。
(2)如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?
this.子类自己的成员变量
super.父类成员变量/父类成员方法
格式:super.父类成员变量/父类成员方法
e.继承后:方法重写
(1)概念:
子类重写了一个申明==与父类一样的方法==,==覆盖==父类的方法。子类认为父类的该方法不好用,以后用自己重写的方法。
(2)重写方法建议加上一个重写校验注解:@Override
作用1:要求必须是正确重写的才不报错
作用2:可读性好
(3)方法重写注意事项和要求:
1.重写方法的==名称和形参列表==必须与父类被重写方法的名称和形参列表一致
2.私有方法不能重写
3.静态方法不能重写
4.重写方法的权限 >= 被重写方法的访问权限。
f.继承后:子类构造器的特点
特点:
子类的全部构造器默认都会先访问父类的无参数构造器,再执行自己的构造器
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
简要理解:
先有爸爸才有儿子。 先调用它爸爸的构造器初始化父类的数据,再调用自己的构造器初始化自己的数据。
代码层面:
默认子类构造器的第一行都有一个super() 访问父类的无参数构造器,写不写都有
。
g.继承后:子类构造器访问父类有参构造器
(1)super调用父类有参数构造器的作用:
初始化继承自父类的数据。
(2)如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?
会报错。因为子类默认是调用父类无参构造器的。
(3)如何解决?
子类构造器中可以通过书写 super(…),手动调用父类的有参数构造器
总结:
调用父类有参数构造器,初始化继承自父类的数据。
super(....) 根据参数调用父类构造器
h.this、super使用总结
this,super的意义:
**this:代表本类对象的引用;super:代表父类存储空间的标识。
this访问子类当前对象的成员。super:在子类方法中指定访问父类的成员。**
this(...),super(...)的作用:
this(...) : 访问本类兄弟构造器
super(...):在本类构造器中指定访问父类的构造器
注意事项:super(...) 必须放在第一行,this(...) 也必须在第一行。因此2者不能共存一个构造器中。
2.封装
封装的基本思想:
决定属性和行为归属谁的问题
定义人类(名称,年龄,吃饭、睡觉)
定义圆类(半径,画圆)
定义门类(开门,高宽)
定义票类( 票价,地址,买票)
封装的原则:
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
如何更好的封装呢?
成员变量建议private私有化,只能本类访问了。
合理暴露:
提供成套的getter和setter方法暴露取值和赋值
3.多态:
a.什么是多态?
多态就是同一个行为有很多个表示形态,我们知道三角形是一个形态,四边形也是一个形态,这里的多态其实就是一个端口,多态的出现其实就是对多种端口形态的体现。
更准确的说是:对象的多种形态。
b.多态的前提:
1.有继承关系\实现关系
2.有父类引用指向子类对象。
Fu f=new Zi();
有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
c.多态的格式:
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
d. 多态的使用场景
如果没有多态,在下图中register方法只能传递学生对象,其他的Teacher和administrator对象是无法传递给register方法方法的,在这种情况下,只能定义三个不同的register方法分别接收学生,老师和管理员。
有了多态之后,方法的形参就可以定义为共同的父类Person。
要注意的是:
- 当一个方法的形参是一个类,我们可以传递这个类所有的子类对象。
- 当一个方法的形参是一个接口,我们可以传递这个接口所有的实现类对象(后面会学)。
- 而且多态还可以根据传递的不同对象来调用不同类中的方法。
代码示例:
父类:
public class Person {
private String name;
private int age;
空参构造
带全部参数的构造
get和set方法
public void show(){
System.out.println(name + ", " + age);
}
}
子类1:
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理员的信息为:" + getName() + ", " + getAge());
}
}
子类2:
public class Student extends Person{
@Override
public void show() {
System.out.println("学生的信息为:" + getName() + ", " + getAge());
}
}
子类3:
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老师的信息为:" + getName() + ", " + getAge());
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建三个对象,并调用register方法
Student s = new Student();
s.setName("张三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建国");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理员");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//这个方法既能接收老师,又能接收学生,还能接收管理员
//只能把参数写成这三个类型的父类
public static void register(Person p){
p.show();
}
}
f.多态的运行特点
**调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,==运行看右边==**
案例理解:
//创建对象(多态方式)
//Fu f = new Zi();
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
//运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
System.out.println(a.name);//动物
//调用成员方法:编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
//运行看右边:java运行代码的时候,实际上运行的是子类中的方法。
a.show();///Dog --- show方法
//理解:
//Animal a = new Dog();
//现在用a去调用变量和方法的呀?是的
//而a是Animal类型的,所以默认都会从Animal这个类中去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下的。父:name 子:name
//成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
编译的时候都会先看一看==父类==有没有这个成员变量或者成员方法。这也成了多态的弊端
g.多态的优势
方法中,使用父类作为参数,可以接收所有子类对象
Animal a = new Dog();
a.eat();
**当想使用其他子类的时候,直接将第一行中的Dog改为其他子类就好了,
其他不需要改变,这个就是多态的优势。**
h.多态的弊端:
不能使用==子类的特有功能==
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//a.lookHome();
//lookHom()是Dog子类的特有的功能
lookHom()是Dog子类的特有的功能,使用多态后就不可以使用了。
i.引用类型转换
(1)为什么要转型
使用多态后就无法访问子类独有功能了。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
回顾基本数据类型转换
- 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
- 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
(2) 向上转型(自动转换)
- 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。所以子类范围小可以直接自动转型给父类类型的变量。
(3) 向下转型(强制转换)
- 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
(4)案例演示
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
转型演示,代码如下:
定义类:
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void watchHouse() {
System.out.println("看家");
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
(5)转型的异常
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。
(6)instanceof关键字
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
所以,转换前,我们最好先做一个判断,代码如下:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
(7)instanceof新特性
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
(8)强制类型转换能解决什么问题?
Person p=new Student();
Student s = ( Student)p;
**●可以转换成真正的子类类型,从而调用子类独有功能。
●转换类型与真实对象类型不一致会报错
●转换的时候用instanceof关键字进行判断**
最后
十分感谢你可以耐着性子把它读完和我可以坚持写到这里,摘抄几句话,对你,也对我:
1.别把安全感建立在别人身上,那样就等于把自己人生的选择权,交给了别人。
2.要么努力到出类拔萃,要么就懒得乐知天命。最怕你见识打开了,可努力又跟不上,骨子里清高至极,性格上又软弱无比。
3.想要过什么样的生活,就得有什么样的代价。我没有承担那种代价的能力,所以我甘愿选择默默无闻的平凡。有人活得不平凡,而你只是没看到他的代价而已;何况活成那种不平凡,是没有回头路可走的。
——七董年
没有人会关心你付出过多少努力,撑得累不累,摔得痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷。
5.重蹈覆辙的下场就是自取其辱,失而复得的东西根本回不到当初,重读一本书可能会有新的感悟,但不会有新的结局。
最后如果觉得我写的还不错,请不要忘记==点赞==✌,==收藏==✌,加==关注==✌哦(。・ω・。)
愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚==菜鸟==逐渐成为==大佬==。加油,为自己点赞!
注:本篇文章是本人在b站学习黑马程序员的学习笔记,无任何商用用途,仅为自己和读者的Java语言学习的学习资料。