抽象和多态
一:抽象类
如果一个类(被要求)不能明确的描述(实例化)一个对象,那么这个类就被定义为抽象类。
在java语言中被关键字abstract所修饰的类就是一个抽象类。
需要注意的是:如果一个类中含有抽象方法,那么这个类就必须定义为抽象类,但是抽象类中不一定含有抽象方法。
一个类如果被定义为抽象类,不难理解,我们就无法创建该类的对象,即无法进行实例化对象的过程。
代码如下:
abstract class Ch01 {
}
Ch01类就是一个抽象类。
梗概:
u 关键字abstract所修饰的类就是一个抽象类。
u 抽象类可以有属性和非抽象的方法。
u 抽象类只能用作基类,不能实例化,但可被继承。
u 抽象类可以有构造方法,子类实例化时首先调用父类的构造方法。
u 在子类中对父类的非抽象方法进行重写时,同名同参同返回值。
u 构造方法和静态方法不能用abstract修饰。
二:抽象方法
如果一个方法没有方法体,(注意是没有方法体,连花括号都没有),也就是没有具体的方法实现过程,该方法就是抽象方法。
含有抽象方法的类一定是抽象类。
【权限修饰符】 abstract 返回值类型 方法名(形参);
abstract void eat() {
System.out.println("吃东西方法(父类)");
}
在一个方法中,如果只告诉了我们该做什么,而具体的实现过程需要在其子类中实现,那么该方法就定义为抽象方法。
子类必须父类的抽象方法,否则相当于子类中含有抽象方法,那么子类就应当是抽象类,而抽象类又不能创建对象,程序报错。
抽象方法的作用:强制子类进行方法重写,即实现方法的过程/方式,在子类中重写父类的抽象方法也是我们作为一线程序员主要的工作。
父类定义了方法的规则,而不同的子类可以有不同的实现过程。
三:抽象的作用
1:在面向对象领域,抽象类用来进行类型隐藏(不能实例化对象,只能被继承)。
2:利用抽象的概念,可以在项目中创建扩展性很好的架构。
3:抽象类、方法在实际业务中都是由设计者来提供,而具体的实现过程则是需要程序员通过重写抽象方法来事项这些方法。
多态性
面向对象语言三大特性:封装、继承和多态。
1:多态性:
多态的含义:对外表现一种形式,内在有多种具体实现。
Java中多态的具体体现:
1:方法重载
2:方法重写
3:多态参数
2:编译器类型和运行期类型:
Father a = new Son();
A fun();
在编译期,验证a对象的类型(Father类),包括其中的属性和方法,运行期执行Son类的方法。
3:父类引用指向子类对象
当编译器类型是父类,运行期类型是子类时,被称为父类引用指向子类对象
class Ch01 { //父类(基类)
}
class Ch02 extends Ch01{ //子类继承父类
}
Ch01 c1 = new Ch02();
Ch01的应用指向Ch02的对象。
4:多态环境下对属性和方法的调用
Father a = new Son();
1)调用成员方法时
编译看左,最终结果看右边
2)调用static成员方法时
编译和结果都看左边
3)调用成员属性时
编译和结果都看左边
5:多态参数
将父类作为方法的参数,但实际传递的参数可以是任何一个子类的对象,表现为多态参数。
多态性的条件:1、继承2、方法重写3、父类引用指向子类对象
重载与重写的区别:
重载:同名不同参,不同参表现为1、参数类型不同、参数个数不同、参数顺序不同,与返回值类型无关
重写:同名同参同返回值,重写后权限范围不能缩小,只能相等或者变大。
6:多态环境下对象造型
1)向上造型(自动类型提升)
class Animal{ //父类
abstract void eat();
}
class Cat extends Animal{ //子类
void look() {
System.out.println("看家");
}
}
………
Animal x=new Cat() //向上造型,Cat对象提升到Animal对象
x.eat() //只能使用父类中的方法
x.look() //报错!不能使用子类中的方法
如果子类对父类的方法进行重写,那么父类引用指向子类对象,验证父类的方法而执行子类的方法。
如果父类中有的方法在子类中没有被重写,那么x就只能调用父类的方法。
例如:父类中有10中方法,,那么向上造型后,子类的对象就可以直接调用父类的独有方法。
总结为一点:向上造型访问父类独有方法
2)向下造型
Father a = new Son();
Son b = (Son) a; //强制转换
如果父类和子类共有的方法,则调用子类的方法
只有父类调用父类,只有子类调用子类
总结一点:向下造型访问子类独有方法
最后放上一段完整代码供参考学习理解:
————————————————————————————
class Ch01 {
//姓名
static String str1 = "小明";
//年龄
int age = 9;
//无参构造
Ch01(){
System.out.println("我是父类 无 参构造方法");
}
//有参构造
Ch01(String str1){
System.out.println("我是父类 有 参构造方法");
}
//eat方法
void eat() {
System.out.println("吃东西方法(父类)");
}
//run方法
void run() {
System.out.println("跑步方法(父类)");
}
}
—————————————————————————————
class Ch02 extends Ch01{
//姓名
static String str1 = "小红";
//年龄
int num = 3;
//无参构造
Ch02(){
System.out.println(super.str1);
System.out.println("我是Ch02类 无 参构造方法");
}
//有参构造
Ch02(String str1){
this.str1 = str1; //修改str1属性
System.out.println("我是Ch02类 有 参构造方法");
}
//eat方法
void eat() { //protected范围:同包不同类,若不是同包类,必须是该类的子类
System.out.println("吃东西方法(Ch02类)");
}
//dance方法
void dance() {
System.out.println("跳舞方法(Ch02类)");
}
}
————————————————————————————
public class Ch03 extends Ch01 {
//姓名
String str1 = "小黑";
//年龄
int age = 6;
//无参构造
Ch03(){
System.out.println("我是Ch03子类 无 参构造方法");
}
//有参构造
Ch03(String str1){
System.out.println("我是Ch03子类 有 参构造方法");
}
//eat方法
void eat() {
System.out.println("吃东西方法(Ch03子类)");
}
//run方法
void run() {
System.out.println("跑步方法(Ch03子类)");
}
}
————————————————————————————
————————————————————————————
Main函数:
import javax.swing.plaf.synth.SynthSpinnerUI;
public class Main {
public static void main(String[] args) {
Ch03 cc3 = new Ch03(); //子类
Ch02 cc2 = new Ch02(); //子类
Ch01 cc1 = new Ch01(); //父类
Ch01 cc11 = new Ch02();
System.out.println(cc11.str1);
cc2.eat();
//该方法为依赖关系,父类作为方法参数,调用方法时,运行父类的eat方法
fun(cc1);
//cc2是父类的一个子类的对象,当改对象作为参数传入后,依然可以运行其eat方法
fun(cc2);
//cc3是父类的一个子类的对象,当改对象作为参数传入后,依然可以运行其eat方法
用该例子理解多态参数的概念
fun(cc3);
}
//将父类作为参数传入fun方法中
static void fun(Ch01 cc1) {
cc1.eat();
}
}