面向对象的三大特征(封装、继承、多态)

目录

一、面向对象

1.面向对象概述

(1)概述

(2)举例

(3)区别

(4)特点

  2.类和对象

(1)类

(2)对象

(3)类与对象的关系

(4)面向对象和面向过程区别

3.类的定义

(1)事物与类的对比

(2)类的定义格式

4.对象的使用

(1)对象的使用格式

(2)成员变量的默认值

5.类与对象的练习

6.对象内存图

(1)一个对象,调用一个方法内存图

 (2)两个对象,调用同一方法内存图

 (3)一个引用,作为参数传递到方法中内存图

​ (4)一个引用作为返回值

7.成员变量和局部变量区别

二、封装

1.封装概述

(1)概述

(2)原则

2.封装的步骤

3.封装的操作——private关键字

(1)private的含义

(2)private的使用格式

4.封装优化1——this关键字

(1)this的含义

(2)this使用格式

5.封装优化2——构造方法

(1)构造方法的定义格式

(2)注意事项

6.标准代码——JavaBean

三、 继承

1.概述

(1)由来

(2)定义

(3)好处

2.继承的格式

3.继承后的特点——成员变量

(1)成员变量不重名

(2) 成员变量重名

4.继承后的特点——成员方法

(1)成员方法不重名

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

(3)重写注意事项

(4)重写的应用

(5)注意事项

5.继承后的特点——构造方法

6.super和this

(1)父类空间优先于子类对象产生

(2)super和this的含义

(3)super和this的用法

(4)super关键字的三种用法

(5)this关键字用法

7.继承的特点

四、多态

1.概述

(1)引入

(2)定义

(3)前提

2.多态的体现

 3.多态下的成员变量访问

(1)成员变量重名

(2)间接通过成员方法访问成员变量

4.多态下的成员方法访问

5.多态的好处

6.引用类型转换

(1)向上转型

(2)向下转型

(3)转型原因

(4)转型的异常


一、面向对象

1.面向对象概述

(1)概述

        Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下, 使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算 机事件的设计思想。它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去 操作实现。

(2)举例

洗衣服:

面向过程:把衣服脱下来-->找一个盆-->放点洗衣粉-->加点水-->浸泡10分钟-->揉一揉-->清洗衣服-->拧干-->晾起来

面向对象:把衣服脱下来-->打开全自动洗衣机-->扔衣服-->按钮-->晾起来

(3)区别

面向过程:强调步骤。

面向对象:强调对象,这里的对象就是洗衣机。

(4)特点

        面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态。

  2.类和对象

(1)

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

b.属性:就是该事物的状态信息。

c.行为:就是该事物能够做什么。

d.举例:小猫。

属性:名字、体重、年龄、颜色。 行为:走、跑、叫。

(2)对象

a.对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性和行为。现实中,一类事物的一个实例:一只小猫。

b.举例:一只小猫。

属性:tom、5kg、2 years、yellow。 行为:溜墙根走、蹦跶的跑、喵喵叫。

(3)类与对象的关系

a.类是对一类事物的描述,是抽象的

b.对象是一类事物的实例,是具体的

c.类是对象的模板,对象是类的实体

(4)面向对象和面向过程区别

a.面向过程:当你要去实现一个功能的时候,每一个具体的步骤,你都得亲力亲为,详细的处理每一个细节。

b.面向对象:当你需要实现一个功能的时候,不用关心具体的步骤,而是找一个已经具备该功能的人,来帮我做事。

public class Test {

    public static void main(String[] args) {
        int[] array = {10,20,30,90,100,10000};

        //要打印数组中的元素 [10,20,30,90,100];
        //面试过程思维
        System.out.print("["); //]
        for (int i = 0; i < array.length; i++) {
            if ( i == array.length -1){ //如果是最后一个元素 加 ]
                System.out.println(array[i] + "]");
            }else {  //否则  加逗号
                System.out.print(array[i]+",");
            }
        }
        System.out.println("============");

        //面向对象方式编程
        //找了一个JDK给我们提供好的Arrays工具类
        //其中有有个toString方法,直接把数组变成你想要的格式.
        System.out.println(Arrays.toString(array));
    }

}

3.类的定义

(1)事物与类的对比

现实世界的一类事物:

a.属性:事物的状态信息。

b.行为:事物能够做什么。

Java中用class描述事物也是如此:

a.成员变量:对应事物的属性。

b.成员方法:对应事物的行为。

(2)类的定义格式

public class ClassName { 
    //成员变量 
    //成员方法
}

a.定义类:就是定义类的成员,包括成员变量成员方法

b.成员变量:和定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外

c.成员方法:和定义方法几乎是一样的。只不过把static去掉。  

d.类的定义格式举例:

public class Student { 
    //成员变量 
    String name; //姓名 
    int age;//年龄
        
    //成员方法
    //学习的方法 
    public void study() {
        System.out.println("好好学习,天天向上");
    }
    
    //吃饭的方法 
    public void eat() { 
        System.out.println("学习饿了要吃饭");
    }
}

4.对象的使用

(1)对象的使用格式

a.创建对象:

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

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

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

c.对象的使用格式举例:

public class Test { 
    public static void main(String[] args) { 
        //创建对象格式:类名 对象名 = new 类名(); 
        Student s = new Student(); 
        System.out.println("s:"+s); 
        
        //直接输出成员变量值 
        System.out.println("姓名:"+s.name); //null 
        System.out.println("年龄:"+s.age); //0 
        System.out.println("‐‐‐‐‐‐‐‐‐‐"); 
        
        //给成员变量赋值 
        s.name = "赵六"; 
        s.age = 18; 
        //再次输出成员变量的值 
        System.out.println("姓名:"+s.name); //赵六
        System.out.println("年龄:"+s.age); //18 
        System.out.println("‐‐‐‐‐‐‐‐‐‐"); 
        //调用成员方法 
        s.study(); // "好好学习,天天向上" 
        s.eat(); // "学习饿了要吃饭" 
    }
}

(2)成员变量的默认值

数据类型默认值
基本类型整数(byte,short,int,long)0
浮点数(float,double)0.0
字符(char)‘\u0000'
布尔(boolean)false
引用类型数组,类,接口null

5.类与对象的练习

a.定义手机类:

public class Phone { 
    // 成员变量 
    String brand; //品牌 
    int price; //价格 
    String color; //颜色 
    
    // 成员方法 
    //打电话 
    public void call(String name) { 
        System.out.println("给"+name+"打电话"); 
    }
    //发短信 
    public void sendMessage() { 
        System.out.println("群发短信"); 
    } 
}

b.定义测试类:

public class Test { 
    public static void main(String[] args) {
        //创建对象
        Phone p = new Phone(); 
        
        //输出成员变量值 
        System.out.println("品牌:"+p.brand);//null 
        System.out.println("价格:"+p.price);//0 
        System.out.println("颜色:"+p.color);//null 
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐");
        
        //给成员变量赋值 
        p.brand = "锤子"; 
        p.price = 2999; 
        p.color = "棕色"; 
        
        //再次输出成员变量值 
        System.out.println("品牌:"+p.brand);//锤子 
        System.out.println("价格:"+p.price);//2999 
        System.out.println("颜色:"+p.color);//棕色 
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐"); 
        //调用成员方法 
        p.call("孙悟空");         
        p.sendMessage();
    } 
}

6.对象内存图

(1)一个对象,调用一个方法内存图

 

 (2)两个对象,调用同一方法内存图

 (3)一个引用,作为参数传递到方法中内存图

public class Test {
    public static void main(String[] args) {
        Phone  one = new Phone();
        one.brand = "华为";
        one.color = "8号色";
        one.price = 3999.0;
        method(one);
    }
    //方法,参数是对象类型的参数
    public static void method(Phone param){
        System.out.println(param.brand);
        System.out.println(param.color);
        System.out.println(param.price);
    }
}

 (4)一个引用作为返回值

public class Test {

    public static void main(String[] args) {
        Phone  phone = getPhone();
        System.out.println(phone.brand);
        System.out.println(phone.color);
        System.out.println(phone.price);
    }

    public static Phone getPhone(){
        Phone  one = new Phone();
        one.brand = "华为";
        one.color = "黑色" ;
        one.price = 4999.0;
        return one;
    }
}

7.成员变量和局部变量区别

变量根据定义位置的不同,我们给变量起了不同的名字。如下图所示:

区别:
1.定义的位置不一样。
a.局部变量:  在方法的内部。
b.成员变量: 在方法外部,类的内部。

2.作用范围不一样。
a.局部变量: 只有在方法当中才可以使用,出了方法就不能再使用。
b.成员变量: 整个类中都可以使用。

3.默认值不一样。
a.局部变量: 没有默认值,如果想使用,必须手动进行赋值。
b.成员变量: 如果没有赋值,会有默认值,规则和数组一样。

4. 内存的位置不一样。
a.局部变量:  位于栈内存。
b.成员变量: 位于堆内存。

5.生命周期不一样。
a.局部变量: 随着方法进栈诞生的,随着方法出栈而消失。
b.成员变量: 随着对象的创建而诞生,随着对象被垃圾回收而消失。

public class Test {

    //在方法的外面,类的里面定义的变量就交成员变量
    String name; //成员变量

    public void method(){
        int num; // 局部变量
        System.out.println(name);
    }
    public void methodB(int param){  //局部变量
      //  System.out.println(num);// 错误写法
        int age;
       // System.out.println(age);//没有赋值不能使用
        System.out.println(param);  //因为调用方法的时候肯定得给赋值
    }
}

二、封装

1.封装概述

(1)概述

        面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

(2)原则

        将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。

 2.封装的步骤

a.使用 private 关键字来修饰成员变量。

b.对需要访问的成员变量,提供对应的一对 getXxx 方法 、 setXxx 方法。

public class Test {

    public static void main(String[] args) {
        int[] array = {10,30,20,15,50};
        int max = getMax(array);

        System.out.println(max);
    }

    public static int getMax(int[] array){
        int  max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max){
                max = array[i];
            }
        }
        return max;
    }
}

3.封装的操作——private关键字

 (1)private的含义

a.private是一个权限修饰符,代表最小权限。

b.可以修饰成员变量和成员方法。

c.被private修饰后的成员变量和成员方法,只在本类中才能访问。

(2)private的使用格式

private 数据类型 变量名 ;

 a.使用 private 修饰成员变量,代码如下:

public class Student { 
    private String name; 
    private int age; 
}

b.提供 getXxx 方法 / setXxx 方法,可以访问成员变量,代码如下:

public class Student { 
    
    private String name; 
    private int age; 
    
    public void setName(String n) { 
        name = n; 
    }
    public String getName() {
        return name; 
    }
    public void setAge(int a) { 
        age = a;
    }
    public int getAge() { 
        return age; 
    } 
}

4.封装优化1——this关键字

(1)this的含义

        this代表所在类的当前对象的引用(地址值),即对象自己的引用。

tips :方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。

 (2)this使用格式

this.成员变量名;

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

public class Student { 
    
    private String name; 
    private int age; 
    public void setName(String name) { 
        //name = name; 
        this.name = name; 
    }
    public String getName() { 
        return name;
    }
    public void setAge(int age) { 
        //age = age; 
        this.age = age; 
    }
    public int getAge() { 
        return age;
    } 
}

tips:方法中只有一个变量名时,默认也是使用 this 修饰,可以省略不写。  

5.封装优化2——构造方法

        当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。

tips:无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。

(1)构造方法的定义格式

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

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

public class Student {
    private String name;
    private int age;

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

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

    public Student(String name,int age){
        System.out.println("执行了有参构造方法....");
        this.name = name;
        this.age = age;

    }

}

(2)注意事项

a.如果你不提供构造方法,系统会给出无参数构造方法。

b.如果你提供了构造方法,系统将不再提供无参数构造方法。

c.构造方法是可以重载的,既可以定义参数,也可以不定义参数。

d.构造方法名必须和类名完全一致,大小写一样。

e.定义有参的构造方法的时候,最好也写一个无参的构造方法。

f.构造方法中不能有返回值类型,void也不可以。

6.标准代码——JavaBean

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

public class ClassName{ 
    //成员变量 
    //构造方法
    //无参构造方法【必须】 
    //有参构造方法【建议】 
    //成员方法 
    //getXxx()
    //setXxx()
}

 a.编写符合 JavaBean 规范的类,以学生类为例,标准代码如下:

public class Student { 
    //成员变量 
    private String name; 
    private int age; 
    //构造方法 
    public Student() {
        
    } 
    
    public Student(String name,int age) { 
        this.name = name; 
        this.age = age;
    }
    
    //成员方法 
    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;
    } 
}

b.测试类,代码如下:

public class Test { 
    public static void main(String[] args) { 
        //无参构造使用 
        Student s= new Student(); 
        s.setName("张三");
        s.setAge(18); 
        System.out.println(s.getName()+"‐‐‐"+s.getAge());
        //带参构造使用 
        Student s2= new Student("李四",18); 
        System.out.println(s2.getName()+"‐‐‐"+s2.getAge());
    } 
}

三、 继承

1.概述

(1)由来

        多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。 继承描述的是事物之间的所属关系, 我们通过继承,可以使多种事物之间形成一种关系体系。

(2)定义

        继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

 (3)好处

a.提高代码的复用性。

b.类与类之间产生了关系,是多态的前提。

2.继承的格式

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

class 父类 { 
    ... 
}

class 子类 extends 父类 {
    ... 
}

继承演示,代码如下:

/** 
定义员工类Employee,做为父类
*/ 
class Employee { 
    String name; // 定义name属性 
    // 定义员工的工作方法 
    public void work() { 
        System.out.println("尽心尽力地工作");
    }
}

/** 
定义讲师类Teacher 继承 员工类Employee 
*/ 

class Teacher extends Employee { 
    // 定义一个打印name的方法 
    public void printName() {
        System.out.println("name=" + name);
    } 
}

/*
  定义助教类 继承员工类Employee
*/

class Assistant extends Employee {
    //定义打印名称
    public void  workEat(){
        System.out.println("name" + name);
    }
    
}



/** 定义测试类 */
public class Test {
    public static void main(String[] args) { 
        // 创建一个讲师类对象 
        Teacher t = new Teacher(); 
        
        // 为该员工类的name属性进行赋值
        t.name = "小明";
        
        // 调用该员工的printName()方法 
        t.printName();// name = 小明 
        
        // 调用Teacher类继承来的work()方法 
        t.work(); // 尽心尽力地工作 
        
        // 创建一个助教类对象 
        Assistant ass = new Assistant(); 
        
        // 为该员工类的name属性进行赋值
        ass.name = "小红";
        
        // 调用该员工的printName()方法 
        ass.printName();// name = 小红 
        
        // 调用Assistant类继承来的work()方法 
        ass.work(); // 尽心尽力地工作 
        
    }
}

3.继承后的特点——成员变量

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

a.直接通过子类对象访问成员变量:等号左边是谁,就优先使用谁,没有则向上找。

b.间接的通过成员方法去访问成员变量:该方法属于谁,就优先用谁,没有则向上找。

(1)成员变量不重名

如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。代码如下:

public class Fu {

    int numFu = 10;
    int num = 100;

    public void methodFu(){
        //使用的是本类中的num, 不会向子类找
        System.out.println(num);
    }
}

public class Zi extends Fu {

    int numZi = 20;
    int num = 200;

    public void  methodZi(){
        //因为本类中有num 所以用的是本类的num,如果没有会去父类找
        System.out.println(num);
    }
}

public class Test  {

    public static void main(String[] args) {
        Fu fu = new Fu();
        System.out.println(fu.numFu);

        Zi  zi = new Zi();
        System.out.println(zi.numFu);//10
        System.out.println(zi.numZi);//20
        System.out.println("================");
        System.out.println(zi.num);//200
        // System.out.println(zi.abc); // 父类也没有就会报错.

        System.out.println("======");
        //这个方法是子类的,优先用子类的,没有再向上找
        zi.methodZi();
        //这个方法是在父类中定义.
        zi.methodFu();
    }
}

(2) 成员变量重名

如果子类父类中出现重名的成员变量,这时的访问是有影响的。代码如下:

class Fu { 
    // Fu中的成员变量。
    int num = 5; 
}

public class Zi extends Fu{

    int num = 20;

    public void method(){
        int num = 30;
        System.out.println(num);// 30 局部变量.
        System.out.println(super.num);//10 父类成员变量
        System.out.println(this.num);// 20 本类成员变量

    }
}

/*
   局部变量:  直接写成员变量名   优先使用局部变量。
   本类的成员变量: this.成员变量名。
   父类的成员变量: super.成员变量名。
 */
public class Test {
    public static void main(String[] args) {
        Zi  zi = new Zi();
        zi.method();// 30
    }
}

子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super关键字,修饰父类成员变量 ,使用格式:

super.父类成员变量名 

 子类方法需要修改,代码如下:

class Zi extends Fu { 
    // Zi中的成员变量 
    int num = 6; 
    public void show() { 
        //访问父类中的num 
        System.out.println("Fu num=" + super.num); 
        //访问子类中的num 
        System.out.println("Zi num=" + this.num);
    } 
}

tips: Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,可以在父类中提供公共的getXxx方法和setXxx方法。  

4.继承后的特点——成员方法

(1)成员方法不重名

        如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。代码如下:

class Fu{ 
    public void show(){ 
        System.out.println("Fu类中的show方法执行"); 
    } 
}

class Zi extends Fu{ 
    public void show2(){ 
        System.out.println("Zi类中的show2方法执行"); 
    } 
}

public class Test { 
    public static void main(String[] args) { 
        Zi z = new Zi(); 
        //子类中没有show方法,但是可以找到父类方法去执行
        z.show(); 
        z.show2();
    } 
}

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

        如果子类父类中出现重名的成员方法,该访问是一种特殊情况,叫做方法重写(Override)。方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。代码如下:

public class Fu {

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

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

public class Zi extends Fu {

    public  void methodZi(){
        System.out.println("子类方法执行!");
    }

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

public class Test {

    public static void main(String[] args) {
        Zi zi = new Zi();

        zi.methodFu();
        zi.methodZi();

        //new  的是子类的对象,所以优先使用子类
        zi.method();
    }
}

(3)重写注意事项

a.必须保证父类和子类之间方法名称相同,参数列表相同,@Override:写在方法前面,用来检测是不是有效的重写.,不满足重写条件,则报错。

b.子类方法的返回值必须要小于等于父类的返回值范围。

c.子类方法的权限必须大于等于父类方法的权限修饰符(public > protected > (default一般都是省略不写) > private)。

(4)重写的应用

        子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。比如新的手机增加来电显示头像的功能,代码如下:

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("显示头像");
    }
}

public class Test {

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

tips:这里重写时,用到super.父类成员方法,表示调用父类的成员方法。  

(5)注意事项

a.子类方法覆盖父类方法,必须要保证权限大于等于父类权限。

b.子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。

5.继承后的特点——构造方法

定义格式和作用:

a.构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。

b.构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super(),表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。代码如下:

public class Fu {

    public Fu(){
        System.out.println("父类无参构造方法!");
    }

    public Fu(int num){
        System.out.println("父类有参的构造方法"+num);
    }
}

public class Zi extends Fu {

    public Zi(){
        super(20);  //子类调用父类重载的构造方法
        System.out.println("子类的构造方法...");
    }

    public void method(){
        ///super(); 错误写法,只有在子类构造方法中才能调用父类的构造犯法
    }
}


public class Test {

    public static void main(String[] args) {
            Zi  zi = new Zi();
    }
}

继承关系中,父子类构造方法访问的特点:
a.子类的构造方法当中有一个默认隐含的 "super()" 调用,所以一定要先先调用父类的的构造方法,后执行的子类的构造方法。
b.可以通过super关键字在子类中调用父类的重载构造方法。
c.super的父类构造方法调用,必须写到子类的构造方法的第一行,一个子类的构造方法不能调用多次父类的构造方法,只有在子类构造方法中才能调用父类的构造方法 。

6.super和this

(1)父类空间优先于子类对象产生

        在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空 间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构 造方法调用时,一定先调用父类的构造方法。

(2)super和this的含义

a.super :代表父类的存储空间标识(可以理解为父类的引用)。

b.this :代表当前对象的引用(谁调用就代表谁)。

(3)super和this的用法

a.访问成员

this.成员变量 ‐‐ 本类的 
super.成员变量 ‐‐ 父类的 
this.成员方法名() ‐‐ 本类的 
super.成员方法名() ‐‐ 父类的

b.访问构造方法

this(...) ‐‐ 本类的构造方法 
super(...) ‐‐ 父类的构造方法

   tips:子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。

(4)super关键字的三种用法

a.在子类成员方法中访问父类的成员变量。

b.在子类的成员方法中,访问父类的方法。

c.在子类的构造方法中,访问父类的构造方法。

public class Fu {
    int num = 50;
    
     public void method(){
        System.out.println("父类方法");
    }
}

public class Zi extends Fu {

    int num = 20;
    
    public Zi(){
        super();//在子类的构造方法中,访问父类的构造方法.
    }
    
    public void methodZi(){
        System.out.println(super.num);//父类的
    }
    //在子类的成员方法中,访问父类的方法
    public void method(){
        super.method();
        System.out.println("子类方法");
    }
}

public class Fu {

    int  num = 100;
}

public class Zi extends Fu {

    public  Zi(){
        this(10);
    }
    public Zi(int n){

    }

    public Zi(int a, int b){

    }
    int num = 20;

    public void showNum() {
        int num = 10;
        System.out.println(num);//局部变量  10
        System.out.println(this.num);//20  本类中的成员变量
        System.out.println(super.num); //父类中的成员变量
    }

    public void methodA() {
        System.out.println("AAAAA");

    }


    public void methodB(){
        this.methodA();
        System.out.println("BBBBB");
    }
}

(5)this关键字用法

a.在本类的成员方法中,访问本类的成员变量。

b.在本类的成员方法中,访问本类的另一个成员方法。

c.在本类的构造方法中,访问本类的另一个构造方法。

在第三种用法中要注意:
a. this(....)调用必须是构造方法的第一个语句,唯一一个。
b. super和this两种构造调用,不能同时使用。

public class Fu {
    int num = 30;
}

public class Zi extends Fu {

    public Zi(){
        this(20);//本类的无参构造,调用本类的有参构造.
        //this(22,12);//错误写法
    }
    public Zi(int n){
        this(20,12);
    }
    public Zi(int a,int b){

    }

    int  num = 20;
    public void showNum(){
        int num = 10;
        System.out.println(num); //局部变量
        System.out.println(this.num);//本类中的成员变量
        System.out.println(super.num);//父类中的成员变量
    }

    public void methodA(){
        System.out.println("AAA");
    }
    public void methodB(){
        this.methodA();// 起到强调作用
        System.out.println("BBB");
    }

}

7.继承的特点

a.Java只支持单继承,不支持多继承。

//一个类只能有一个父类,不可以有多个父类。 
class C extends A{} //ok 
class C extends A,B... //error

b.ava支持多层继承(继承体系)。

class A{}
class B extends A{} 
class C extends B{}

tips:顶层父类是Object类。所有的类默认继承Object,作为父类。

c.子类和父类是一种相对的概念。  

四、多态

1.概述

(1)引入

        多态是继封装、继承之后,面向对象的第三大特性。生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也 是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

(2)定义

多态: 是指同一行为,具有多个不同表现形式。

(3)前提

a.继承或者实现(二选一 )。

b.方法的重写(意义体现:不重写,无意义 )。

c.父类引用指向子类对象(格式体现 )。

2.多态的体现

a.多态体现的格式:

父类类型 变量名 = new 子类对象; 
变量名.方法名();

tips:父类类型:指子类对象继承的父类类型,或者实现的父接口类型。  

代码如下:

Fu f = new Zi(); 
f.method();

 b.当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写 后方法。代码如下:

定义父类:

public abstract class Animal { 
	public abstract void eat(); 
}

定义子类:

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

class Dog extends Animal { 
    public void eat() {
        System.out.println("吃骨头");
    } 
}

定义测试类:

public class Test { 
    public static void main(String[] args) { 
        // 多态形式,创建对象 
        Animal a1 = new Cat(); 
        // 调用的是 Cat 的 eat 
        a1.eat(); 
        
        // 多态形式,创建对象 
        Animal a2 = new Dog();
        // 调用的是 Dog 的 eat 
        a2.eat(); 
    }
}

 3.多态下的成员变量访问

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

a.直接通过对象名称访问成员变量,看等号左边是谁,优先用谁,没有则向上找。

b.间接通过成员方法访问:看该方法属于谁,优先用谁,没有则向上找。

(1)成员变量重名

父类引用指向子类对象的方式,访问成员变量,会访问父类里的成员变量。

//父类
public class Fu {
    int num = 10;
}

//子类
public class Zi extends Fu {
    
    int  num =20;
}

public class Test{
    public static void main(String[] args){
        Fu  fu = new Zi();
        System.out.println(fu.num);
    }
}

(2)间接通过成员方法访问成员变量

public class Fu {
    int num = 10;

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


public class Zi extends Fu {

    int  num =20;

    int  age = 30;
}

public class Test {
    public static void main(String[] args) {
        Fu fu = new Zi();
        System.out.println(fu.num);
        System.out.println("=============");
        fu.show();//打印10
    }
}

4.多态下的成员方法访问

在多态下成员方法的访问规则是: 看new的是谁,就优先使用谁,没有则向上找(编译看左边,运行看右边)。

//父类
public class Fu {
       public void method(){
        System.out.println("父类方法");
    }
    
    public void methodFu(){
        System.out.println("父类特有方法!!");
    }
}

//子类
public class Zi extends Fu {

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

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

public class Demo02Mutil {
    public static void main(String[] args) {
        Fu fu = new Zi();// 多态

        fu.method();//父子都有,优先用子
        fu.methodFu();//子类没有,父类有,向上找到父类
        //编译看左边,左边是Fu,父当中没有methodZi()方法,所以编译报错
        //fu.methodzi();//错误写法
    }

}

5.多态的好处

        实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。代码如下:

 定义父类:

public abstract class Animal { 
    public abstract void eat(); 
}

定义子类:

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

public class Dog extends Animal { 
    public void eat() { 
        System.out.println("吃骨头"); 
    } 
}

定义测试类:

public class Test {
    public static void main(String[] args) { 
        Cat c = new Cat();
        Dog d = new Dog();     
        // 调用eat
        c.eat();
        // 调用 d
        d.eat(); 
        /*
        以上两个方法,可以使用多态方式调用
        而执行效果一致 
        */
        // 多态形式,创建对象 
        Animal a = new Cat();
        Animal b = new Dog();
        a.eat();
        b.eat();
    }
    
}

由于多态特性的支持,无论右边new的时候换成哪个子类对象,等号左边调用方法都不会发生变化。

6.引用类型转换

多态的转型分为向上转型与向下转型两种:

a.向上转型。

b.向下转型。

(1)向上转型

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

使用格式:

父类类型 变量名 = new 子类类型(); 
如:Animal a = new Cat();

 (2)向下转型

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

使用格式:

子类类型 变量名 = (子类类型) 父类变量名; 
如:Cat c =(Cat) a;

 (3)转型原因

        当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子 类特有的方法,必须做向下转型。转型演示,代码如下:

定义类:

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

(4)转型的异常

转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

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。

所以,转换前,我们最好先做一个判断(instanceof),代码如下:

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
        } 
        
        if (a instanceof Dog){
            Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse 
        }
    } 
}

 

 

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值