java面向对象的总结

1.面向对象

面向过程:当需要实现一个功能时,每个具体的步骤都要亲力亲为,详细处理每个细节。(强调过程)

面向对象:当需要实现一个功能时,不关心具体的步骤,而是找一个已经具有该功能的的人或物体,通过该人或物体来实现这个功能。(强调对象)

面向对象编程(Object Oriented Programing,OOP)的本质:以类的方式组织代码,以对象的组织(封装)数据。

面向对象的三大基本特征:封装、继承和多态。

类:是一组相关属性行为的集合,可以看成是一类事务的模板,使用事物的属性特征和行为特征来描述该类事物。

  1. 属性:就是该事物的状态信息。
  2. 行为:就是该事物能够做什么。

对象:是一类事物的具体体现,对象是类的一个实例,必然具备该类事物的属性和行为。

类与对象的关系

  1. 类是对一类事物的描述,是抽象的。
  2. 对象是一类事物的实例,是具体的。
  3. 类是对象的模板,对象是类的实体。

1.1 对象的使用

创建对象:

类名 对象名 = new 类名();

使用对象访问类中的成员:

对象名.成员变量;
对象名.成员方法();

对象的使用格式举例:

对应到Java的类当中:

成员变量(属性):
    String name; // 姓名
    int age; // 年龄
成员方法(行为):
    public void eat() {} // 吃饭
    public void sleep() {} // 睡觉
    public void study() {} // 学习

注意事项:
1. 成员变量是直接定义在类当中的,在方法外边。
2. 成员方法不要写static关键字。
 */
public class Student {

    // 成员变量
    String name; // 姓名
    int age; // 姓名

    // 成员方法
    public void eat() {
        System.out.println("吃饭饭!");
    }

    public void sleep() {
        System.out.println("睡觉觉!");
    }

    public void study() {
        System.out.println("学习!");
    }

}

成员变量的默认值如图所示
在这里插入图片描述
1.2 对象内存图
1.一个对象,调用一个方法内存图
在这里插入图片描述
2.两个对象调用一个方法内存图
在这里插入图片描述
3.将一个对象赋值给令一个对象的内存图在这里插入图片描述
4.使用对象作为方法参数内存图
在这里插入图片描述
5.使用对象类型作为方法返回值内存图
在这里插入图片描述1.3 成员变量和局部变量
变量根据定位位置的不同,有不同的名字。如下图所示:
在这里插入图片描述(1)在类中的位置不同
成员变量:类中,方法外
局部变量:方法中或者方法声明上(形式参数)
(2)作用范围不一样
成员变量:类中
局部变量:方法中
(3)初始值不同
成员变量:有默认值
局部变量:没有默认值,必须定义,赋值,最后使用
(4)在内存中的位置不同
成员变量:堆内存
局部变量:栈内存
(5)生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失

2.封装

封装:禁止直接访问一个对象中的数据,应通过操作接口来访问。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

1.方法就是一种封装
2.关键字private也是一种封装

2.1 private的使用格式

private 数据类型 变量名;

1.使用private修饰成员变量
2.提供getXxx方法/setXxx方法/isXxx方法,可以访问成员变量,代码如下:

/*
问题描述:定义Person的年龄时,无法阻止不合理的数值被设置进来。
解决方案:用private关键字将需要保护的成员变量进行修饰。

一旦使用了private进行修饰,那么本类当中仍然可以随意访问。
但是!超出了本类范围之外就不能再直接访问了。

间接访问private成员变量,就是定义一对儿Getter/Setter方法

必须叫setXxx或者是getXxx命名规则。
对于Getter来说,不能有参数,返回值类型和成员变量对应;
对于Setter来说,不能有返回值,参数类型和成员变量对应。
对于基本类型当中的boolean值,Getter方法一定要写成isXxx的形式,而setXxx规则不变。
 */
public class Person {

    private String name; // 姓名
    private int age; // 年龄
	private boolean male; // 是不是爷们儿

    public void setMale(boolean b) {
        male = b;
    }

    public boolean isMale() {
        return male;
    }

    public void show() {
        System.out.println("我叫:" + name + ",年龄:" + age);
    }

    // 这个成员方法,专门用于向age设置数据
    public void setAge(int num) {
        if (num < 100 && num >= 9) { // 如果是合理情况
            age = num;
        } else {
            System.out.println("数据不合理!");
        }
    }

    // 这个成员方法,专门私语获取age的数据
    public int getAge() {
        return age;
    }

}

2.2 封装优化-----this 关键字

this.成员变量名;

使用this修饰方法中的变量,解决成员变量被隐藏的问题,代码如下:

/*
当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。
如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量名

“通过谁调用的方法,谁就是this。”
 */
 public class Student{
 	private String name;
 	private int age;
 	
 	public void setName(String name){
 		this.name = name;
 		}
	public String getName(){
		return this.name;
		}
		
 	public void setAge(int age){
 		this.age = age;
 		}	
	public int getAge(){
	    return this.age;
	    }

2.3 封装优化------构造方法

当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
(无论是否自定义构造方法,所有类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。)

构造方法的定义格式

修饰符 构造方法名(参数列表){
	//方法体
}

构造方法的写法上,方法名与它所在的类名相同。它没有返回值,所有不需要返回值类型,甚至不需要void,使用构造方法后,代码如下:


/*
构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。
格式:
public 类名称(参数类型 参数名称) {
    方法体
}

注意事项:
1. 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样
2. 构造方法不要写返回值类型,连void都不写
3. 构造方法不能return一个具体的返回值
4. 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。
public Student() {}
5. 一旦编写了至少一个构造方法,那么编译器将不再赠送。
6. 构造方法也是可以进行重载的。
重载:方法名称相同,参数列表不同。
 */
public class Student {

    // 成员变量
    private String name;
    private int age;

    // 无参数的构造方法
    public Student() {
        System.out.println("无参构造方法执行啦!");
    }

    // 全参数的构造方法
    public Student(String name, int age) {
        System.out.println("全参构造方法执行啦!");
        this.name = name;
        this.age = age;
    }

    // Getter Setter
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

}

注意:
1.如果你不提供构造方法,系统就会给出无参数构造方法
2.如果你提供了构造方法,系统将不再提供无参数构造方法
3.构造方法是可以重载的,即可以定义参数,也可以不定义参数

2.4 标准代码--------JavaBean
JavaBean是Java语言编写类的一种标准规范,符合JavaBean的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,提供用来操作成员变量的setget方法。

/*
一个标准的类通常要拥有下面四个组成部分:

1. 所有的成员变量都要使用private关键字修饰
2. 为每一个成员变量编写一对儿Getter/Setter方法
3. 编写一个无参数的构造方法
4. 编写一个全参数的构造方法

这样标准的类也叫做Java Bean
 */
public class Student {

    private String name; // 姓名
    private int age; // 年龄

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }
}

3.继承

定义:多个类中存在相同属性和行为时,将这些共同的属性和行为抽象为一个父类,其他的子类就可以继承父类的属性和行为,使得子类对象具有与父类相同的属性、和行为。

在这里插入图片描述
继承的优点:

  1. 提高代码的复用性
  2. 类与类之间产生了关系,是多态的前提

3.1 继承的格式
通过extends关键字,可以声明一个子类继承一个父类,定义格式如下:

public class 父类名称{//定义父类的格式
..........
}

public class 子类名称 extends 父类名称{
...........//定义子类格式
}

3.2 成员变量重名
在父子类的继承关系中,如果成员变量重名,则创建子类对象时,访问有两种方式。

1.直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找。
2.间接通过成员方法访问成员变量:
该方法属于谁,就优先用谁,没有则向上找

public class Demo02ExtendsField {
    public static void main(String[] args) {
        Father father = new Father();//创建父类对象
        System.out.println(father.numFather);
        System.out.println("====================");

        Son son = new Son();
        System.out.println(son.numFather);//10
        System.out.println(son.numSon);
        System.out.println("====================");

        System.out.println(son.num );//优先子类200
        System.out.println("===================");

        son.methodSon();//这个方法定义在子类,优先用子类,没有再向上找
        son.methodFather(); //这个方法在父类中定义的
    }
}


public class Father {
    int numFather = 10;
    int num = 100;

    public void methodFather(){
        System.out.println(num);
    }
}


public class Son extends Father{
    int numSon = 20;
    int num = 200;

    public void methodSon(){
        System.out.println(num);
    }
}

子类和父类中出现同名的成员变量时,也可以使用super关键字来访问父类中的同名变量。可以用this关键字来访问本类的同名变量。

3.3 成员方法重名——重写(Override)

如果子类和父类中出现重名的成员方法,这时的访问是一种特殊的情况,叫做方法重名(Override)。

方法重写:子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。

在父子类的继承关系中,创建子类对象,访问成员方法规则;
创建的对象是谁,就优先用谁,如果没有则向上找

注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。

重写(Override):方法的名称一样,参数列表也一样。覆盖、覆写。
重载(Overload):方法的名称一样,参数列表不一样。
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。

public class Demo04ExtendsMethod {
    public static void main(String[] args) {
        Son son = new Son();
        son.methodFather();
        son.methodSon();
        System.out.println("===========");
        son.method();
    }
}


public class Father {
    public void methodFather(){
        System.out.println("父类方法执行!");
    }

    public void method(){
        System.out.println("父类方法重名执行!");
    }
}

public class Son extends Father {
    public void methodSon(){
        System.out.println("子类方法执行!");
    }

    @Override
    public void method(){
        System.out.println("子类方法重名执行!");
    }
}

方法覆盖重写的注意事项:
1.必须保证父子类之间方法的名称相同,参数列表也相同。
@Override: 写在方法前面,用来检测是不是有效的的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。

2.子类方法的返回值必须小于父类方法的返回值范围。
Object类是所有类的公共最高父类(祖宗类),java.long.String就是Object的子类。

3.子类方法的权限必须大于等于父类方法的权限修饰符
小扩展提示:public > protected > (default) > private
备注:(default)不是关键字default,而是什么都不写,留空。

3.4 覆盖重写的应用

继承中方法的覆盖重写应用场景
设计原则:
对于已经投入使用的类,尽量不要进行修改。
推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容。

在这里插入图片描述

public class Demo06Phone {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.call();
        phone.send();
        phone.show();
        System.out.println("==========");

        NewPhone newPhone = new NewPhone();

        newPhone.call();
        newPhone.send();
        newPhone.show();
        System.out.println("==========");
    }
}

public class Phone {
    public void call(){
        System.out.println("打电话");
    }

    public void send(){
        System.out.println("发短信");
    }
    public void show(){
        System.out.println("显示号码");
    }
}

public class NewPhone extends Phone {
    @Override
    public void show() {
        super.show();
        System.out.println("显示姓名");
        System.out.println("显示头像");
    }
}

3.5 继承中的构造方法

继承关系中,父类和子类构造方法的访问特点

1.子类构造方法当中有有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造
2.子类构造可以通过super关键字来调用父类重载构造
3.super的父类构造方法必须是子类构造方法的第一个语句,不能一个子类构造调用多次super构造
总结:
子类必须调用父类构造方法,不写则赠送super();写了则用指定的super调用,super只能有一个,还必须是第一个。

public class Demo07Constructor {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println("==========");
        Father father = new Father();

    }
}

public class Father {

    public Father(){
        System.out.println("1-父类构造方法!");
    }
    public Father(int num){
        System.out.println("2-父类构造方法!");
    }
}

public class Son extends Father {

    public Son(){
       super(); //调用父类无参构造
       // super(5);//在调用父类重载构造方法
        System.out.println("子类构造方法!");
    }
}

3.6 super和this

super关键字的用法有三种:
1.在子类的成员方法中,访问父类的成员变量。
2.在子类的成员方法中, 访问父类的成员方法。
3.在子类的构造方法中,访问父类的构造方法。

super关键字用来访问父类内容,而this用来访问本类内容,用法有三种

1.在本类的成员方法中,访问本类的成员变量。
2.在本类的成员方法中,访问本类的另一个成员方法。
3.在本类的构造方法中,访问本类的的另一个构造方法。
在第三种用法当中注意:
A.this(…)调用必须是构造方法的第一个语句,唯一一个。
B.super和this两种构造调用,不能同时使用。

在这里插入图片描述

3.7 继承的特点

在这里插入图片描述
1.java只支持单继承,不支持多继承
一个类的直接父类只能有唯一一个

2.java语言可以多级继承
我有一个父亲,我父亲还有一个父亲,也就是爷爷。

3.子类和父类是一种相对概念
一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类。可以有很多个兄弟姐妹,生二胎。

3.8 抽象类

在这里插入图片描述
抽象类就是将父类进一步抽象出来的类。父类中的方法被它们的子类重写,子类各自的实现都不尽相同,那么父类的方法声明和方法主体只有声明还有意义,方法主体没有存在的意义的意义,把没有方法主体的方法称为抽象方法。包含抽象方法的类就是抽象类。
抽象方法:没有方法体的方法
抽象类:包含抽象类的类

如何使用抽象类和抽象方法:
1.不能直接创建抽象类对象。
2.必须用一个子类继承抽象父类。
3。子类覆盖重写抽象父类当中所有的抽象方法。
覆盖重写(实现),子类去掉抽象方法的关键字abstract,然后补上方法体大括号。
4.创建子类对象进行使用。

抽象方法定义格式

修饰符 abstract 返回值类型 方法名(参数列表)

抽象类定义格式

public abstract class 类名字{
}

抽象的使用
继承抽象类的子类必须重写父类所有的抽象方法,否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。

public abstract class Animal {
    //这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
    public abstract void eat();

    //这是普通的成员方法
    public void normalMethod(){

    }
}

public class Cat extends Animal {
    @Override
    public void eat(){
        System.out.println("猫吃鱼!");
    }

}

public class DemoMain {
    public static void main(String[] args) {
    Cat cat = new Cat();
    cat.eat();
    }
}

注意事项

1.抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象
2.抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
3.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类
4.抽象类的子类,必须重写抽象父类所有的抽象方法,除非该子类也是抽象类。

4.多态

4.1 接口

接口就是多个类的公共规范。接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
在这里插入图片描述

如何定义一个接口的格式:

public interface 接口名称 {
    // 抽象方法
    //默认方法
    //静态方法
    //私有方法
}

备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class。

如果是Java 7,那么接口中可以包含的内容有:

  1. 常量
  2. 抽象方法

如果是Java 8,还可以额外包含有:
3. 默认方法
4. 静态方法

如果是Java 9,还可以额外包含有:
5. 私有方法

接口使用步骤:

  1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
    格式:
    public class 实现类名称 implements 接口名称 {
    // …
    }
  2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
    实现:去掉abstract关键字,加上方法体大括号。
  3. 创建实现类的对象,进行使用。

注意事项:
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。

public class Demo01Interface {

    public static void main(String[] args) {
        // 错误写法!不能直接new接口对象使用。
//        MyInterfaceAbstract inter = new MyInterfaceAbstract();

        // 创建实现类的对象使用
        MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
        impl.methodAbs1();
        impl.methodAbs2();
    }

}

package Demo01;

public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {

    @Override
    public void methodAbs1() {
        System.out.println("这是第一个方法");
    }

    @Override
    public void methodAbs2() {
        System.out.println("这是第二个方法");
    }

    @Override
    public void methodAbs3() {
        System.out.println("这是第三个方法");
    }

    @Override
    public void methodAbs4() {
        System.out.println("这是第四个方法");
    }
}

package Demo01;

public interface MyInterfaceAbstract {
    //这是一个抽象方法
    public abstract void methodAbs1();

    //这也是一个抽象方法
    abstract void methodAbs2();

    public void methodAbs3();

    void methodAbs4();   //也是抽象方法
}

4.1.1 抽象方法的使用

/*
在任何版本的Java中,接口都能定义抽象方法。
格式:
public abstract 返回值类型 方法名称(参数列表);

注意事项:
1. 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2. 这两个关键字修饰符,可以选择性地省略。
3. 方法的三要素,可以随意定义。
 */
public interface MyInterfaceAbstract {

    // 这是一个抽象方法
    public abstract void methodAbs1();

    // 这也是抽象方法
    abstract void methodAbs2();

    // 这也是抽象方法
    public void methodAbs3();

    // 这也是抽象方法
    void methodAbs4();

}

4.1.2 默认方法的使用

可以继承可以重写,只能二选一,但是只能通过实现类的对象来调用

/*
从Java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表) {
    方法体
}

备注:接口当中的默认方法,可以解决接口升级的问题。
 */
public interface MyInterfaceDefault {

    // 抽象方法
    public abstract void methodAbs();

    // 新添加的方法,改成默认方法
    public default void methodDefault() {
        System.out.println("这是新添加的默认方法");
    }

}

4.1.3 静态方法的使用

静态方法与.class文件相关,只能直接使用接口名调用,不可以通过实现类的类名或实现类的对象调用。

/*
从Java 8开始,接口当中允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表) {
    方法体
}
提示:就是将abstract或者default换成static即可,带上方法体。
 */
public interface MyInterfaceStatic {

    public static void methodStatic() {
        System.out.println("这是接口的静态方法!");
    }

}
public class MyInterfaceStaticImpl implements MyInterfaceStatic {
}
/*
注意事项:不能通过接口实现类的对象来调用接口当中的静态方法。
正确用法:通过接口名称,直接调用其中的静态方法。
格式:
接口名称.静态方法名(参数);
 */
public class Demo03Interface {

    public static void main(String[] args) {
        // 创建了实现类对象
        MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();

        // 错误写法!
//        impl.methodStatic();

        // 直接通过接口名称调用静态方法
        MyInterfaceStatic.methodStatic();
    }

}

4.1.4 私有方法的使用

私有方法:只有默认方法调用
私有静态方法:默认方法和静态方法调用
如果一个接口中有多个默认方法,并且方法中有重复的内容,封装到私有方法中,供默认方法去调用。



/*
问题描述:
我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。
但是这个共有方法不应该让实现类使用,应该是私有化的。

解决方案:
从Java 9开始,接口当中允许定义私有方法。
1. 普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表) {
    方法体
}

2. 静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表) {
    方法体
}
 */
public interface MyInterfacePrivateA {

    public default void methodDefault1() {
        System.out.println("默认方法1");
        methodCommon();
    }

    public default void methodDefault2() {
        System.out.println("默认方法2");
        methodCommon();
    }

    private void methodCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }

}

4.1.5 接口常量



/*
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:
public static final 数据类型 常量名称 = 数据值;
备注:
一旦使用final关键字进行修饰,说明不可改变。

注意事项:
1. 接口当中的常量,可以省略public static final,注意:不写也照样是这样。
2. 接口当中的常量,必须进行赋值;不能不赋值。
3. 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
 */
public interface MyInterfaceConst {

    // 这其实就是一个常量,一旦赋值,不可以修改
    public static final int NUM_OF_MY_CLASS = 12;

}

4.1.6 接口的多实现

在继承体系中,一个类只能继承一个父类,对于接口而言,一个类可以实现多个接口,这就叫做接口的多实现,并且,一个类能继承一个父类,同时实现多个接口。



public class MyInterfaceImpl /*extends Object*/ implements MyInterfaceA, MyInterfaceB {

    @Override
    public void methodA() {
        System.out.println("覆盖重写了A方法");
    }


    @Override
    public void methodB() {
        System.out.println("覆盖重写了B方法");
    }

    @Override
    public void methodAbs() {
        System.out.println("覆盖重写了AB接口都有的抽象方法");
    }

    @Override
    public void methodDefault() {
        System.out.println("对多个接口当中冲突的默认方法进行了覆盖重写");
    }
}

4.1.7 接口的多继承

一个接口能继承另一个或者多个接口,这和类之间继承比较相似。接口的继承使用extends ,子接口继承父接口的方法,如果父接口中的默认方法有重名的,那么子接口需要重写一次。

package Demo02;
/*
使用接口的时候,需要注意:

1. 接口是没有静态代码块或者构造方法的。
2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
格式:
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {
    // 覆盖重写所有抽象方法
}
3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
5. 如果实现类锁实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
6. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。
 */
public class Demo01 {
    public static void main(String[] args) {
        Son son = new Son();
        son.methodA();
        son.method();
        System.out.println("==========");

        son.methodAbs();
        son.methodDefault();
    }
}

  1. 类与类之间是单继承的。直接父类只有一个。
  2. 类与接口之间是多实现的。一个类可以实现多个接口。
  3. 接口与接口之间是多继承的。

注意事项:

  1. 多个父接口当中的抽象方法如果重复,没关系。
  2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】。

4.2.多态

在这里插入图片描述
多态:同一行为,具有多个不同表现形式。
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。

在这里插入图片描述

格式:

父类名称 对象名 = new 子类名称();

或者:

接口名称 对象名 = new 实现类名称();

访问成员变量的两种方式:

  1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
  2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
package Demo05;

public class Demo01Multi {
    public static void main(String[] args) {
        Father son = new Son();

        System.out.println(son.num);//父类:10
        System.out.println("==========");
        son.showNum();
        son.method();
        System.out.println("==================");
        son.methodFather();

        //  son.methodSon();  //错误写法

    }
}


public class Father {
    int num = 10;

    public void showNum(){
        System.out.println(num);
    }
    public void method(){
        System.out.println("我是爸爸");
    }

    public void methodFather(){
        System.out.println("父类方法");
    }

}

public class Son extends Father{
    int num = 20;

    @Override
    public void showNum(){
        System.out.println(num);
    }
    @Override
    public void method(){
        System.out.println("我是爸爸的儿子");
    }

    public void methodSon(){
        System.out.println("子类方法");
    }
}


在这里插入图片描述
在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。

4.3引用类型转换

在这里插入图片描述

4.3.1向上转型

多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的 。
当父类引用指向一个子类对象时,便是向上转型。
使用格式:

父类类型 变量名 =  new 子类类型();

向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端:
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。

解决方案:用对象的向下转型【还原】。

4.3.2向下转型

父类类型向子类类型转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:

子类类型 变量名 = (子类类型)父类变量名:

如何才能知道一个父类引用的对象,本来是什么子类?为了避免出现ClassCastException报错,使用instanceof关键字,给引用变量做类型的校验。
格式:

对象 instanceof 类名称

这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。

package Demo06;

public class Demo01Main {
    public static void main(String[] args) {
        Animal animal = new Cat();

        animal.eat();
        System.out.println("=========");
        //向下转型,进行“还原”动作
        Cat cat = (Cat)animal;
        cat.catchMouse();
        System.out.println("===========");
        if(animal instanceof Cat){
            Cat cat1 = (Cat)animal;
            cat1.catchMouse();
        }
        else if(animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
    }

}

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页