🌹作者:云小逸
📝个人主页:云小逸的主页
📝Github:云小逸的Github
🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==🤟
👏专栏:C++👏 👏专栏:Java语言👏
👏专栏:C语言初阶👏👏专栏:数据结构👏
今天这篇文章主要讲述Java语言的三大特征:继承、封装、多态。
——————————————————————————————
Java允许一个类通过extends与另一个类建立父子关系,这就是继承。
子类 extends父类
public class Student extends People {}
Student称为子类(派生类),People称为父类(基类 或超类)。
子类继承父类,子类可以得到父类的属性和行为,子类可以使用。
Java中子类更强大
提高代码的复用性,多个子类的相同代码可以放在父类中,增强了类的扩展性。
设计规范:子类共用属性和行为放在父类,子类独有属性和行为放在子类自己那里。
类有自己的构造器,父类构造器用于初始化父类对象。
有争议的.
通常意义上说Java的子类不能继承父类的private变量。因为不能直接的访问父类的private变量.但是当你实例化子类时,在内存中有一个父类的私有变量已经放到了内存中。这一点通过子类访问父类的public或protect方法可以访问到父类的私有变量。
有争议的知识点。
子类可以直接使用父类的静态成员(共享)
但个人认为:子类不能继承父类的静态成员。(共享并非继承)
单继承:子类只能继承一个直接父类
不支持多继承:子类不能同时继承多个父类
public class 子类C extends 父类A , 父类B{
public static void main(String[] args) {
子类 z = new 子类();
z.method(); // 复习啥?出现二义性
//听哪个爸爸的呢?java懵了!因此不支持多继承
}
}
子类 A 继承父类 B ,父类B 可以 继承父类 C

Java中所有类,要么直接继承了Object , 要么默认继承了Object , 要么间接继承了Object, Object是祖宗类。

先子类局部范围找
然后子类成员范围找
然后父类成员范围找,如果父类范围还没有找到则报错。
this.子类自己的成员变量
super.父类成员变量/父类成员方法
格式:super.父类成员变量/父类成员方法
子类重写了一个申明与父类一样的方法,覆盖父类的方法。子类认为父类的该方法不好用,以后用自己重写的方法。
作用1:要求必须是正确重写的才不报错
作用2:可读性好
1.重写方法的名称和形参列表必须与父类被重写方法的名称和形参列表一致
2.私有方法不能重写
3.静态方法不能重写
4.重写方法的权限 >= 被重写方法的访问权限。
子类的全部构造器默认都会先访问父类的无参数构造器,再执行自己的构造器
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
简要理解:
先有爸爸才有儿子。 先调用它爸爸的构造器初始化父类的数据,再调用自己的构造器初始化自己的数据。
默认子类构造器的第一行都有一个super() 访问父类的无参数构造器,写不写都有
。
初始化继承自父类的数据。
会报错。因为子类默认是调用父类无参构造器的。
子类构造器中可以通过书写 super(…),手动调用父类的有参数构造器
调用父类有参数构造器,初始化继承自父类的数据。
super(…) 根据参数调用父类构造器
this:代表本类对象的引用;super:代表父类存储空间的标识。
this访问子类当前对象的成员。super:在子类方法中指定访问父类的成员。

this(…) : 访问本类兄弟构造器
super(…):在本类构造器中指定访问父类的构造器
注意事项:super(…) 必须放在第一行,this(…) 也必须在第一行。因此2者不能共存一个构造器中。
决定属性和行为归属谁的问题
定义人类(名称,年龄,吃饭、睡觉)
定义圆类(半径,画圆)
定义门类(开门,高宽)
定义票类( 票价,地址,买票)
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
成员变量建议private私有化,只能本类访问了。
合理暴露:
提供成套的getter和setter方法暴露取值和赋值
多态就是同一个行为有很多个表示形态,我们知道三角形是一个形态,四边形也是一个形态,这里的多态其实就是一个端口,多态的出现其实就是对多种端口形态的体现。
更准确的说是:对象的多种形态。
Fu f=new Zi();
有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
如果没有多态,在下图中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();
}
}
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
案例理解:
//创建对象(多态方式)
//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
//成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
编译的时候都会先看一看父类有没有这个成员变量或者成员方法。这也成了多态的弊端
方法中,使用父类作为参数,可以接收所有子类对象
Animal a = new Dog();
a.eat();
当想使用其他子类的时候,直接将第一行中的Dog改为其他子类就好了,
其他不需要改变,这个就是多态的优势。
不能使用子类的特有功能
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//a.lookHome();
//lookHom()是Dog子类的特有的功能
lookHom()是Dog子类的特有的功能,使用多态后就不可以使用了。
使用多态后就无法访问子类独有功能了。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
回顾基本数据类型转换
- 自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
- 强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。所以子类范围小可以直接自动转型给父类类型的变量。
子类类型 变量名 = (子类类型) 父类变量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
转型演示,代码如下:
定义类:
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
}
}
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
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对象的。
为了避免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
}
}
}
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("没有这个类型,无法转换");
}
Person p=new Student();
Student s = ( Student)p;
●可以转换成真正的子类类型,从而调用子类独有功能。
●转换类型与真实对象类型不一致会报错
●转换的时候用instanceof关键字进行判断
十分感谢你可以耐着性子把它读完和我可以坚持写到这里,摘抄几句话,对你,也对我:
1.别把安全感建立在别人身上,那样就等于把自己人生的选择权,交给了别人。
2.要么努力到出类拔萃,要么就懒得乐知天命。最怕你见识打开了,可努力又跟不上,骨子里清高至极,性格上又软弱无比。
3.想要过什么样的生活,就得有什么样的代价。我没有承担那种代价的能力,所以我甘愿选择默默无闻的平凡。有人活得不平凡,而你只是没看到他的代价而已;何况活成那种不平凡,是没有回头路可走的。
——七董年
没有人会关心你付出过多少努力,撑得累不累,摔得痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷。
5.重蹈覆辙的下场就是自取其辱,失而复得的东西根本回不到当初,重读一本书可能会有新的感悟,但不会有新的结局。
最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)