java 重写 与多态性和, 对象转型 instanceOf 关键字 toString()方法的使用

方法的重写(override/overwrite)

定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称 为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法

 

要求:

1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表 
2. 子类重写的方法的返回值类型必须和父类被重写的方法的返回值类型相同 
  > 父类被重写的方法的返回值类型是void,则子类的返回值类型必须也是void
  > 父类被重写的返回值类型是基本数据类型(double),则子类的必须也是基本数据类型(double)
3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
    子类不能重写父类中声明为private权限的方法
4. 子类方法抛出的异常不能大于父类被重写方法的异常

 

注意;

子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。

 

 

 

 

 问答:

如果现在父类的一个方法定义成private访问权限,在子类中将此方 法声明为default访问权限,那么这样还叫重写吗?(NO)

因为子类不可重写父类中private的方法 只能当作是子类自己的方法

 

重写之后当创建子类对象以后,通过子类对象调用子父类中同名同参数的方法,实际上都是执行子类重写后的父类的方法,其实就是子类去自己的内存结构中先找自己的方法

 

 

 

 

多态性

 

Java的多态性是面向对象中最重要的概念

 

1:理解多态性: 可以理解为一个事务的多态性

2 多态性 子类对象的多态性,父类的引用指向子类的对象(子类的对象赋予父类的引用)

3,可以理解为右边对象的多态性,右边真实对象在提供真实对象值的多种形态


Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明 该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。

    简 称:编译时,看左边;运行时,看右边。

  若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)

多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
      “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

 

 

 

 

多态的实现

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:

 

 

 

多态是对象多种形式的表现

现实中,比如我们按下 F1 键这个动作:

如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
如果当前在 Word 下弹出的就是 Word 帮助;
在 Windows 下弹出的就是 Windows 帮助和支持。
同一个事件发生在不同的对象上会产生不同的结果。

 

多态的优点

1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性

 

多态存在的三个必要条件

1,继承   
2,重写   (子类重写了父类的方法然后通过不同的对象调用是不同的表现)
3,父类引用指向子类对象 (Person p = new Chrid() 父类名 父类实例名 = new 子类对象())

 

多态的使用

比如我们有一个父类  Person  然后一个子类Chrid 继承了Person类

Person  person = new Chrid() 这就是多态的使用

为什么多态不能对属性使用呢? 因为属性只能有一个特定的数据类型而引用数据类型可能指向多种不同的类型的对象

对象的多态 —在Java中,子类的对象可以替代父类的对象使用 
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向(引用)多种不同类型的对象
      Person p = new Student(); //Object类型的变量o,指向Person类型的对象 o = new Student()
      Object o = new Person();//Object类型的变量o,指向Student类型的对象子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向 上转型(upcasting)。

 

 

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

 

 

eg:

public class AprilSevenForenoonPerson {
    String name;
    int age;
    int id = 1001;
    
    public void eat(){
        System.out.println("吃饭");
    }
    
    public void walk(){
        System.out.println("This is a People walk");
    }
}
定义一个 父类Person

 

public class AprilSevenForenoonMan extends AprilSevenForenoonPerson {

    int age;
    private boolean isSmoking;
    public void earnMoney(){
        System.out.println("挣钱养家");
    }

    @Override
    public void eat(){
        System.out.println("男人吃饭");
    }

    @Override
    public void walk() {
        System.out.println("男人走路");
    }

}
再定义一个子类man类继承Person类

 

public class AprilSevenForenoonWoman extends AprilSevenForenoonPerson {
    boolean isBeautiful;
    int id = 1003;

    public void goShopping(){
        System.out.println("女人去购物");
    }

    @Override
    public void eat() {
        System.out.println("女人吃饭");
    }

//    @Override
//    public void walk() {
//        System.out.println("女人走路");
//    }
}
再定义一个woman子类继承Person类

 

public class AprilSevenForenoonPersonTest {
    public static void main(String[] args) {
        AprilSevenForenoonPerson person = new AprilSevenForenoonPerson();
        person.eat();

        AprilSevenForenoonMan man = new AprilSevenForenoonMan();  
        man.eat(); 
        man.earnMoney();
     AprilSevenForenoonPerson person1 = new AprilSevenForenoonMan();// 此时就是多态的实现
     AprilSevenForenoonPerson person2 = new AprilSevenForenoonWoman();  // 父类的引用指向子类的对象
     person1.eat();   // 通过不同的子类实例化的多态对象调用相同的方法的到的结果是不同的  
     person2.eat();
     System.out.println(person2.id);
// 此时我们在Person类中定义的是1001 得出的也是1001所以是调用父类中的属性 属性是没有多态的 }

 

上面通过不同实际生成的多态对象获取的同一个方法是要看实例化此多态对象的类是哪个 就调用其中的方法

 

 

 

 

多态的应用

       // 多态的应用: 当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法的调用
        

 调用的时候看编译就是看父类中是否有这个方法 不能调用子类特有的方法,在编译的时候看父类是否有此方法
          但是运行的时候看右边子类的  实际运行的是右边子类重写的方法

 

 

注意点:

1,当编译的时候 我们看的是左边 就是  调用的方法必须是父类有的  不然会报错,但是运行的时候实际执行的是右边的,看右边实例化的是哪个类的,就回去执行哪个类中的同名方法

2, 如果实例完多态对象后,父类中有一个特有非私有的方法子类没有重写这个时候使用生成的多态对象是可以调用此方法的, 
  如果使用得到的多态对象去调用子类特有的方法是会报错的,因为写代码还没有运行的时候 是属于编译期,是看左边的 所以子类特有的方法是看右边了违反了编译看左边的原理,
  如果这个得到的多态对象去调用父类中特有的非私有的方法是可以的 运行时也会得到父类中特有的非私有的方法的输出,
  因为运行看右边的时候子类继承了父类同时也获得了父类中所有的属性和方法,同时也能调用父类非私有的这个特有方法方法
即,使用多态对象可以调用父类中非私有的方法,不能调用子类中特有的方法

有了对象的多态性以后,内存中实际上是加载了子类的特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类声明的方法和属性,子类特有的属性和方法不可调用

 

 

多态编译看左边的体现

eg: 

  现在堆Person父类中添加一个特有的方法

    public void own(){
        System.out.println("This is my only method");
    }

子类man中也添加一个特有方法

    public void manOwn(){
        System.out.println("This is man's method");
    }

 

 

 

 而使用生成的多态对象去调用父类中非私有的方法会得到父类的方法的输出,因为非私有的  再运行的时候子类是可以获取父类中的方法的访问的

 

多态运行看右边的体现 

如果是私有的就会提示不可以使用,

这个时候如果我们把父类Person中的own()方法修改权限为Private

    private void own(){
        System.out.println("This is my only method");
    }

 

 

 

 这就体现了运行看右边, 因为都私有了子类是无法获取的 

 

一个引用类型变量如果声明为父类的类型,但实际引用的是子类
对象,那么该变量就不能再访问子类中特有的的属性和方法

Student m = new Student();  //合法,Student类有school成员变量
m.school = “pku”;
Person e = new Student();
e.school = “pku”;
//非法,Person类没有school成员变量 属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编 译错误

 

应用举例

多态的应用场景2

方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法

public class Test { 
  
    public void method(Person e) { //定义的是父类Person类型

      // ……
        e.getInfo();
    }
    public static void main(Stirng args[]) {
        Test t = new Test();
        Student m = new Student();
        t.method(m);
// 子类的对象m传送给父类类型的参数e 实际传递的是子类 :Person e = new Student()
    }
}

 

多态的应用场景2

那么多态我们知道了后面的使用呢? 其实再工作中大多数是下面的使用

    // 多态的应用场景
    public void method(Object object){    // Object object = new 任意类()  多态的存在就是此处可以随便定义传递对象,如果没有多态我们就要堆多个要时候此处的对象要建立对应的多个方法,会造成大量重复工作
    }

Object可以接收到任意的类,实例化出来的

 

 

 

 

 

 

多态小结

  1。 理解多态性:可以理解为一个事务的多种形态
        2. 何为多态性:
                对象的多态性: 父类的引用指向子类的对象(或这类的对象赋给父类的引用)
        3. 多态的使用: 虚拟方法的调用
                有了对象的多态性以后,我们在编译期,只能调用父类声明的方法,但是在运行期实际执行的是子类重写父类的方法
                 总结: 编译看左边,运行看右边
        4.多态性的使用前提: 1 类的继承(必须是继承的基础上才能使用)
                          2 方法的重写(必须是子类重写父类后的方法时候才能调用,不然调用后还是父类中的方法)


        多态性好处: 如果没有多态性 那就是声明什么类型只能new什么类型的对象
                    没有多态性就需要造好多重载的方法

        对象的多态是适用于方法不适用于属性
        反正一句话,访问变量看声明(左边定义的类),访问方法看实际对象类型(new出来的类型)

        在实例化一个子类的同时,系统会给子类所有实例变量分配内存,也会给他的父类的实例变量分配内存,
        即使父子类中存在重名的实例变量,也会两个都分配内存的,
        这个时候子类只是隐藏了父类的这个变量,但还是会给它分配内存,
        然后可以用super来访问属于父类的变量,
        也就是你使用左边的引用去访问属性的时候他回去自己的栈空间中去找自己的属性而方法就会使用右边new出来的去找自己的堆空间

 

 

虚拟方法

 

正常的方法调用 
    Person e = new Person(); 
    e.getInfo(); 
    Student e = new Student(); 
    e.getInfo(); 

虚拟方法:

 虚拟方法调用(多态情况下) 
    子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父 类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法 确定的

 

编译时类型和运行时类型 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类 的getInfo()方法。——动态绑定

 

 

 

 

 

多态的实现案例

eg:

建立一个Animal父类


public class AprilSeventAnimal {
    public void movie(){
        System.out.println("动物可以移动");
    }
}


建立子类Cat
public class AprilSeventCat extends AprilSeventAnimal {
    public void movie(){
        System.out.println("猫在跳");
    }
}

再建立子类Dog

public class AprilSeventDog extends AprilSeventAnimal {
    public void movie(){
        System.out.println("狗在移动");
    }
}


实现类
public class AprilSeventAnimalTest {
    public static void main(String[] args) {
        AprilSeventAnimal animal = new AprilSeventAnimal();
        AprilSeventAnimal dog = new AprilSeventDog();
        AprilSeventAnimal cat = new AprilSeventCat();
        animal.movie(); // 执行animal的movie方法
        dog.movie();  // 执行dog的movie方法
        cat.movie();  // 执行cat的movie方法
    }
}

 

 

输出

动物可以移动
狗在移动
猫在跳

 

 

Practices

1.    什么是多态性?什么是虚拟方法调用?
    对象的多态性:父类的引用指向子类的对象。
    Person p = new Man();
    p.eat();
    调用方法时,编译时看左边,运行时看右边。
2.    一个类可以有几个直接父类?(只有一个)一个父类可有多少个子类?(多个)子类能获取直接父类的父类中的结构吗?(可以)子类能否获取父类中private权限的属性或方法?(可以的)

3.    方法的重写(override/overwrite)的具体规则有哪些?
    1,方法名、形参列表相同
    2,权限修饰符不可以比父类的小
    3,返回值相同  ,但是必须是父类返回值的派生类(java5 及更早版本        
         返回类型要一样,java7 及更高版本可以不同)。
    4,抛出的异常必须比父类的大

4.super调用构造器,有哪些具体的注意点
   1, "super(形参列表)" 的方式调用父类的构造器必须声明在子类构造器的首行

   2,我们再类的构造器中 针对 "this(形参列表)" 或者"super(形参列表)" 只能二选一  不能同时出现\

3,在构造器的首行 没有显示的声明"this(形参列表)" 或者"super(形参列表)" 则默认调用的是父类中空参的构造器

4,在类中的多个构造器 至少有一个类的构造器中使用了 "super(形参列表)"调用父类中的构造器

 

 

 

 

 

toString()方法

1: 当我们调用一个对象的引用时,实际就是调用当前对象的toString方法.
2: String  Date, File 包装类都重写了Object类中的toString()方法,使得在调用对象的toString()方法时,返回的 "实体内容" 信息

3: 自定义类  也可以重写toString()方法,当调用此方法的时候, 返回这个对象的实体内容

 

 

 

instanceOf  关键字

为了在向下转型之前出现异常我们先用instanceOf关键字进行判断

instanceOf  是判断一个对象是否是另一个类的类型

a  instanceOf A   判断对象a是否是类A的实例,如果是返回true如果不是返回false

 

 

对象的类型转换

 

切记 无任何子父类继承关系的对象转型一律都是耍流氓

基本数据类型的Casting

  

自动类型转换:小的数据类型可以自动转换成大的数据类型 
        如long g=20; double d=12.0f 
强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型 如     
         float f=(float)12.0; int a=(int)1200L     

 

对Java对象的强制类型转换称为造型 

从子类到父类的类型转换可以自动进行 
从父类到子类的类型转换必须通过造型(强制类型转换)实现 
无继承关系的引用类型间的转换是非法的 
在造型前可以使用instanceof操作符测试一个对象的类型

 

向下转型  父类---》子类

        //向下转型
        AprilSevenForenoonPerson p3 = new AprilSevenForenoonMan();
        // p3.num;  此处是不能调用Man中的私有属性的  因为编译看左边认为是Person的没有此属性
        AprilSevenForenoonMan m1 = (AprilSevenForenoonMan) p3;  // 向下转型  把多态转型到指向到子类对象
        m1.num = 0; // 转型完毕之后就可以使用子类中独有的属性了
        ((AprilSevenForenoonMan) p3).num = 30;

 

// 在转型的时候 没有子父类的关系是不能转的  不然是会报错的
        /*
        // 编译时通过 运行时不通过
        AprilSevenForenoonPerson person4 = new AprilSevenForenoonWoman();
        AprilSevenForenoonMan man1 = (AprilSevenForenoonMan) person4;
        // 因为woman类型的了你强转为man是不行的

         */

        /*
        编译通过  运行也通过


        Object obj = new Woman();
        Person p = (Person)obj;
         */

 

 

子类继承了父类之后  对于属性值和方法的重写

若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的 同名方法,系统将不可能把父类里的方法转移到子类中。 
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的 实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量

 

 

 

 

练习:

定义两个类,父类GeometricObject代表几何形状,子类Circle代表圆形。

 

 

 

 

 

解答:

 

public class GeometricObject {
    protected String color;
    protected double weight;


    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeigut(double weight) {
        this.weight = weight;
    }

    protected GeometricObject() {
        super();
        this.color = "white";
        this.weight = 1.0;
    }


    protected GeometricObject(String color, double weight) {
        super();
        this.color = color;
        this.weight = weight;
    }


}
GeometricObject

 

public class Circle extends GeometricObject {
    private double radius;

    public Circle() {
        super();
//        this.color = "white";
//        this.weight = 1.0;
        this.radius = 1.0;
    }

    public Circle(double radius) {
        super();
        this.radius = radius;
    }

    public Circle(double radius, String color, double weight) {
        super(color, weight);
        this.weight = weight;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    /**
     * 求圆的面积
     * @return
     */
    public double findArea(){
        return 3.14 * radius * radius;
    }


    @Override
    public  boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        if(obj instanceof  Circle){
            Circle circle = (Circle) obj;
            return this.radius == circle.radius;
        }
        return false;
    }

    @Override
    public  String toString(){
        return "Circle [ radios =" + radius + " ]";
    }

}
Cicle

 

public class CicleTest {
    public static void main(String[] args) {
        Circle circleOne = new Circle(6.0);
        Circle circleTwo = new Circle(6.0,"white",2.0);
        System.out.println(circleOne.findArea());

        System.out.println(circleOne.getColor().equals(circleTwo.getColor()));
        System.out.println(circleOne.equals(circleTwo));

        System.out.println();
    }
}
CicleTest

 

 

Practices

 

1. 继承成员变量和继承方法的区别

public class AprilFourteenForenoon {
    public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.count);
        s.display();
        Base b = s;
        System.out.println(b.count);  
        b.display();
    }
}


class Base{
    int count = 10;

    public void display(){
        System.out.println(this.count);
    }
}


class Sub extends  Base{
    int count = 20;

    public void display(){
        System.out.println(this.count);
    }
}
继承成员变量和继承方法的区别

 

20
20
10  // 因为属性没有多态所以调用的还是当前类型的属性值
20
1答案

 

2: 

 class AprilFourteenForenoonPerson {
    protected String name = "person";
    protected int age = 50;

    public String getInfo() {
        return "AprilFourteenForenoonPerson{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}



class AprilFourteenForenoonStudent extends AprilFourteenForenoonPerson{
    protected String school = "puk";
    public String getInfo(){
        return "name= "+name+"\nage"+age+"\nschool"+school;
    }
}

class  AprilFourteenForenoonGraduate extends  AprilFourteenForenoonStudent{
    public String major="IT";
    public String getInfo() {
        return  "Name = : "+ name + "\nage: "+ age + "\nschool: "+ school+"\nmajor:"+major;
    }
}





建立InstanceTest 类,在类中定义方法 method(Person e); 在method中: (1)根据e的类型调用相应类的getInfo()方法。
 (2)根据e的类型执行: 
      如果e为Person类的对象,输出: “a person”;
      如果e为Student类的对象,输出: “a student” “a person ”
      如果e为Graduate类的对象,输出: “a graduated student” “a student” “a person”

 

    public static void main(String[] args) {

        AprilFourForenoonInstanceOfTest instanceOfTest = new AprilFourForenoonInstanceOfTest();
        instanceOfTest.method(new AprilFourteenForenoonStudent());
    }

    public void method(AprilFourteenForenoonPerson person){
        String info = person.getInfo();
        System.out.println(info);
        if(person instanceof AprilFourteenForenoonGraduate){
            System.out.println("a graduated student");
            System.out.println("a student");
            System.out.println("a person");
        }else if(person instanceof AprilFourteenForenoonStudent){
            System.out.println("a student");
            System.out.println("a person");
        }else{
            System.out.println("a Person");
        }
    }
解答

 

 

 

多态是编译时行为还是运行时行为?

运行时行为, 因为比如定义多个子类的实现这个时候就不清楚到底是调用哪一个需要运行方知道

 


 

posted @ 2019-10-08 16:54  可爱的红领巾  阅读(717)  评论(0)    收藏  举报