在前篇文章中,我们学习了面向对象的static关键字和继承等,下面我们接着来学习第二部分吧!
目录
- 多态
- 包
- final关键字
- 权限修饰符
- 代码块
- 抽象类
多态
什么是多态?多态有什么作用呢?
我们来回想一下Java的三大特征是什么呢?是不是封装、继承、多态呀!前面我们学习了封装和继承,你还记得嘛,接下来我们就来学习最后一个多态吧!
我们先来回顾一下吧:
- 封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
那现在就有个问题了,如果我的JavaBean类越来越多之后,重复的内容就越来越多了,这个时候怎么办呢?是不是就要用到继承呀,将相同的属性抽取到父类当中,子类就不用重复写代码了。提高了复用性。
那么继承是多态的前提条件,没有继承就没有多态。
以前创建学生对象的方式:
有了多态以后,我们还可以把学生对象赋值给他的父类类型,这个时候学生对象就有了两种形态:
那下面我们就来了解一下多态吧:
-
什么是多态
- 同类型的对象,表现出的不同形态
-
多态的表现形式
- 父类类型 对象名 = 子类对象
-
多态的前提
- 有继承关系
- 有父类引用指向子类对象
- 有方法重写
-
多态调用成员的特点
- 变量调用:编译看左边,运行也看左边。
- 方法调用:编译看左边,运行看右边
- 成员变量:在子类的对象中,会把父类的成员变量也继承下来。
- 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
-
变量调用:
- 编译看左边:Java编译代码的时候,会看左边的父类中有没有这个变量,如果有就编译成功,如果没有就编译失败!
- 运行看左边:Java运行代码的时候,实际获取的就是左边父类中成员变量的值。
-
方法调用:
- 编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个变量,如果有就编译成功,如果没有就编译失败!
- 运行看右边:Java运行代码的时候,实际上运行的是子类中的方法。
下面我们来实操一下吧:
创建父类属性:姓名和年龄
private String name;
private int age;
public void show(){
System.out.println(name + "," + age);
}
创建学生子类:
// 继承父类的属性
public class Student extends Person{
// 改写方法
@Override
public void show(){
System.out.println("学生的信息为:" + getName() + "," + getAge());
}
}
创建老师子类:
public class Teachar extends Person{
@Override
public void show(){
System.out.println("老师的信息为:" + getName() + "," + getAge());
}
}
创建管理员子类:
public class Admin extends Person{
@Override
public void show(){
System.out.println("管理员的信息为:" + getName() + "," + getAge());
}
}
定义一个方法接收三
// 这个方法既能接收老师,又能接收学生,还能接收管理员
// 只能把参数写成这三个类型的父类
public static void register(Person p){
p.show();
}
创建三个对象,并调用register方法:
Student stu = new Student();
stu.setName("张三");
stu.setAge(18);
Teachar t = new Teachar();
t.setName("李四");
t.setAge(25);
Admin admin = new Admin();
admin.setName("管理员");
admin.setAge(30);
调用register方法并传递对象:
register(stu);
register(t);
register(admin);
我们来运行看一下吧:
是不是能根据你传递的对象不同选择不同的方法呀,这就是多态的最基本的应用。
下面我们来说说多态的弊端吧:
多态的优势和弊端
- 多态的好处
- 使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
- 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
- 多态的弊端
- 父类不能调用子类的特有功能(会报错),解决方案:把类型变回子类类型就可以了。
- 引用数据类型转换,有几种方式
- 自动类型转换、强制类型转换
- 强制类型转换能解决什么问题
- 可以转换成真正的子类类型,从而调用子类独有功能。
- 转换类型与真实对象类型不一致会报错
- 转换的时候用instanceof关键字进行判断
下面我们来了解一下,报错的原因。
当调用方法的时候,编译看左边,运行看右边,那么在编译的时候会先检查左边父类中有没有这个类,如果没有直接报错。
那怎么解决呢?其实很简单。
变回子类类型就可以了,转换的时候不能瞎转,如果转成其他类的类型就会报错。这个时候我们可以利用一个instanceof关键字来给他判断啦,我们看下面这段代码:
if(a instanceof Dog d){ // 判断a是不是Dog类型,如果是就强转为Dog类型,转换之后变量名为d,如果不是就不转,结果直接false
d.方法名();
}else if(a instanceof Cat c){ // 判断a是不是Cat类型,如果是就强转为Cat类型,转换之后变量名为c如果不是就不转,结果直接false
c.方法名();
}else{
sout("没有这个类型,无法转换");
}
包
-
什么是包
- 包就是文件夹,用来管理各种不同功能的Java类,方便后期维护代码。
-
包名的规则
- 公司域名反写+包的作用,需要全部英文小写,见名知意。
-
使用其他类的规则
- 使用其他类时,需要使用全类名。
例:
impoer com.itheima.domain.Student com.itheima.domain:包 Student:类名 -
导包的情况
- 使用同一个包中的类时,不需要导包。
- 使用Java.lang包中的类时,不需要导包。
- 其他情况下都需要导包。
- 如果同时使用两个包中的同名类,需要用全类名。
final关键字
- 什么是final?final翻译过来是不是最终的意思呀!
那final能修饰什么呢?其实呀能修饰方法、类、变量。下面我们就展开来看看吧:
-
final修饰方法:表明该方法是最终方法,不能被重写。
下面我们来看一下:
我们来用final修饰一下父方法看一下:
是不是直接就报错了呀,那就表示被final修饰的方法就不能被子类重写,表示是最终的方法。通常在当前这个方法是一种规则,不想被改变就可以使用final修饰方法。
-
final修饰类:表明该类是最终类,不能被继承。
下面我们来看一下:
是不是被修饰的类也不能继承呀,表明是最终的类。
-
final修饰变量:这个时候就得叫常量了,只能被赋值一次。
下面是重点了,如果我的变量的值不想被改变,这个时候就可以使用final修饰变量。
下面我们加final修饰符看一下:
是不是就直接报错了呀,被修饰后就不叫变量了,就叫常量了,常量只可以被赋值一次。下面我们就来展开了解一下常量吧:
-
常量
实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。
-
常量的命名规则:
- 单个单词:
- 全部大写。
- 多个单词:
- 全部大写,单词之间用下划线隔开。
- 单个单词:
-
细节:
- final修饰的变量是基本类型,那么变量存储的数据值不能发生改变。
- final修饰的变量是引用类型,那么变量存储的地址值不能发生改变,对象内部的可以改变。
好啦,包就讲到这里啦,下面我们来看一下权限修饰符!
权限修饰符
- 权限修饰符
- 权限修饰符:是用来控制一个成员能够被访问的范围的。
- 可以修饰成员变量,方法,构造方法,内部类。
- 权限修饰符的分类
- 有四种作用访问由小到大:(private < 空着不写 < protected < public)
我们来看一下他们的作用范围吧:
- 权限修饰符的使用规则
- 实际开发中,一般只用最小的:private和最大的:public
- 成员变量私有
- 方法公开
代码块
什么是代码块呢?加了单独的大括号,这个就叫做代码块。
根据出现的位置不同,可以分成三种代码块!
-
局部代码块
局部代码块我们需要分为两部分:局部和代码块。
局部指的是在方法里面。局部代码块指的是写在方法里面的代码块。
例:
-
构造代码块
我们来看一下:
此时是不是就有重复的代码出现了呀?那怎么办呢?这个时候就可以把重复的代码抽取到一个构造方法去。
下面我们来说一下构造方法的注意点:
1.写在成员位置的代码块
2.作用:可以把多个构造方法中重复的代码抽取出来。
3.执行时间:我们在创建本类对象的时候会先执行构造代码块在执行构造方法
-
静态代码块
- 格式:static{};
- 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次。
- 使用场景:在类加载的时候,做一些数据初始化的时候使用
我们来看一下创建静态代码块吧:
-
下面我们来总结一下吧:
-
局部代码块的作用:
- 提前结束变量的生命周期(已淘汰)
-
构造代码块的作用:
- 抽取构造方法中重复的代码(不够灵活)
-
静态代码块的作用:
- 数据初始化(重点)
抽象类
- 抽象关键字:abstract
我们来看这张图片:
这个父类中是不是有个work工作属性子类的student和子类的teacher的工作方式都不一样呀,那怎么办呢?之前是不是学习了在子类方法上重写呀,那是可以的,但是有个弊端,那我如果不重写呢,怎么办?那我们就可以使用抽象类治治他们了。
如果不确认方法体,那可以使用abstract去修饰,被abstract修饰的方法子类必须重写,否则子类的代码就会直接报错。这个方法就叫做抽象方法。而抽象方法所在的类就叫做抽象类。
下面我们来了解一下抽象类和抽象方法的二个需要掌握的知识点吧:
1.抽象类的定义格式
-
抽象类:
- 如果一个类中存在抽象方法,那么该类就必须声明为抽象类。
-
抽象类的定义格式:
- public abstract class 类名{};
public abstract class Person{};
-
抽象类的注意格式:
-
抽象类不能实例化。(创建对象)
-
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
可以有构造方法(当创建了子类对象时,给属性进行赋值的)
-
抽象类的子类
- 要么重写抽象类中的所有抽象方法
- 要么是抽象类
-
2.抽象方法的定义格式
-
抽象方法:
- 将共性的行为(方法)抽取到父类之后,由于每一个子类的内容是不一样的,所以在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
-
抽象方法的定义格式:
- public abstract 返回值类型 方法名(参数列表); // 不写方法体,直接分号结束
public abstract void word();
3.抽象类和抽象方法的意义:
public abstract void eat();- 强制子类必须按照这种格式进行重写方法。
总结
- 抽象类的作用是什么:
- 抽取共性时,无法确定方法体,就把方法定义为抽象的。强制让子类按照某种格式重写。抽象方法所在的类必须是抽象类。
- 抽象类和抽象方法的格式:
public abstract 返回值类型 方法名(参数列表);public abstract class 类名{};
- 继承抽象类有哪些注意:
- 要么重写抽象类中所有的抽象方法
- 要么是抽象类
好啦,这篇文章就学到这里,我们下篇继续学习接口、内部类。有什么不懂的可以在评论区评论一起探讨哟,我们下期不见不散!!!
==最后非常感谢您的阅读,也希望能得到您的反馈 ==