面相对象中级(方法重写,多态,Object类)

一、方法重写

1.1方法重写的基本概念

方法重写就是子类有一个方法,和父类的某个方法的名称,返回类型,参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法。(其实也就是就近原则)

1.2方法重写的基本代码

子类:

package com.override;

public class Dog extends Animal{
    //因为Dog是Animal子类
    //Dog的cry方法和Animal的cry定义的形式一样(名称、返回类型、参数)
    //这时我们么就说Dog的cry方法,重写了Animal的cry方法
    public void cry(){
        System.out.println("小狗汪汪叫..");
    }
}

父类 

package com.override;

public class Animal {
    public void cry(){
        System.out.println("小动物叫唤..");
    }
}

主函数 

package com.override;

public class Override01 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.cry();
    }
}

 这时的输出结果是"小狗汪汪叫",因为父类和子类有名称(cry()),参数以及返回结果一样的方法,在调用这个方法时子类方法覆盖父类方法。

 1.3方法重写的相关细节

(1)子类方法的参数,方法名称,要求父类方法的参数,方法名称完全一样。

(2)子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类。比如:父类的返回类型是Object,子类方法的返回类型是String

(3)子类方法不能缩小父类方法的访问权限(子类方法的访问权限要比父类的访问权限大)

二、多态(非常重要)

2.1多态的基本概念

现在我们引进一个主人喂食的实例。

食物类:

package com.poly_;
public class Food {
    private String name;//为了代码简单只写一个属性
    public Food(String name) {//构造器
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

鱼(猫吃): 

package com.poly_;
public class Fish extends Food{
    public Fish(String name) {
        super(name);
    }
}

骨头(狗吃) :

package com.poly_;
public class Bone extends Food{
    public Bone(String name) {
        super(name);
    }
}

 动物类:

package com.poly_;
public class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

小猫: 

package com.poly_;
public class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
}

小狗: 

package com.poly_;

public class Dog extends Animal{
    public Dog(String name) {
        super(name);
    }
}

 主人(feed()方法):

package com.poly_;
public class Master {
    private String name;
    public Master(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //主人给小狗喂食
    public void feed(Dog dog,Bone bone){
        System.out.println("主人"+name+"给"+dog.getName()+"吃"+bone.getName());
    }
    //主人给小猫喂食
    public void feed(Cat cat,Fish fish){//这其实是一个方法的重载
        System.out.println("主人"+name+"给"+cat.getName()+"吃"+fish.getName());
    }

}

 主函数:

package com.poly_;
public class Poly_ {
    public static void main(String[] args) {
        Master tom = new Master("汤姆");
        Dog dog = new Dog("大黄");
        Bone bone = new Bone("大棒骨");
        tom.feed(dog,bone);

        Cat cat = new Cat("小花");
        Fish fish = new Fish("黄花鱼");
        tom.feed(cat,fish);
    }
}

                                

上述代码确实实现了主人喂食的问题,但是也出现了一个问题:随着动物的不断增多,它们吃的食物也不同,我们就需要不断增加feed()方法吗,这样不利于管理和维护。因此就需要引入多态。 

多态:方法或对象具有多种形态,是面相对象的第三大特征,多态是建立在封装和继承基础之上的。

2.2多态的具体体现

2.2.1重写和重载体现多态(方法的多态 )

package com.poly_;

public class PloyMethond {
    public static void main(String[] args) {
        //方法重载体现多态
        a a = new a();
        //这里我们传入不同的参数,就会调用不同sum方法,就体现多态
        System.out.println(a.sum(10,20));
        System.out.println(a.sum(10,20,30));
        //方法重写体现多态
        b b = new b();
        a.say();
        b.say();//因为所属类的不同,调用say()方法不同,也体现多态
    }
}
class b{
    public void say(){
        System.out.println("B say() 方法被调用...");
    }
}
class a extends b{
    public int sum(int n1,int n2){//和下面sum构成重载
        return n1+n2;
    }
    public int sum(int n1,int n2,int n3){
        return n1+n2+n3;
    }
    public void say(){
        System.out.println("A say() 方法被调用...");
    }
}

2.2.2对象的多态的体现(重难点)、

(1)一个对象的编译类型和运行类型可以不一致( 可以用一个父类的引用指向子类的对象)。

(2)编译类型在定义对象时,就确定了,不能改变

(3)运行类型时可以变化的

(4 )编译类型看定义时 = 号的左边,运行类型看 = 号的右边    

为了深入理解做一个小实验:

动物类:

package com.poly_.objectPoly;

public class Animal {
    public void cry(){
        System.out.println("Animal 动物在叫...");
    }
}

 小猫(这里进行了方法重写):

package com.poly_.objectPoly;

public class Cat extends Animal{
    public void cry(){
        System.out.println("Cat cry(),小猫喵喵叫");
    }
}

 小狗:

package com.poly_.objectPoly;

public class Dog extends Animal{
    public void cry(){
        System.out.println("Dog cry() 小狗汪汪叫...");
    }
}

主函数: 

package com.poly_.objectPoly;

public class objectpoly {
    public static void main(String[] args) {
        //体验对象多态特点

        //anima编译类型就是Animal,运行类型Dog
        Animal animal = new Dog();
        animal.cry();
        //因为运行时,这时就是运行到该行时,animal运行类型时Dog,所以cry就是Dog的cry
        //因此此时的运行结果是"小狗汪汪叫"(注意这里的"这时")
        animal = new Cat();
        animal.cry();
        //这时的运行对象时Cat,所以现在是Cat的cry
        //这时的运行结果是"小猫喵喵叫"
    }
}

  因为对象的多态,我们可以通过使用父类的引用指向子类,使得此时的运行类型是被指的子类,从而达到使用该子类方法的目的。(这里的运行规则还是要注意,在使用父类的引用调用方法时,如果子类和父类都有该方法则运行的是子类的方法,如果子类没有但父类有则运行的是父类的方法,如果是子类的特有方法,则无法调用)(原因会在细节中讲出)。注意:调用时还是要遵从访问权限。

2.3多态的基本代码

现在我们用多态机制对上面主人喂食的问题进行优化(注意对比与前面代码的区别)

package com.poly_;
public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    //animal的编译类型是Animal,可以指向(接收)Animal子类的对象
    //food的编译类型是Food,可以指向(接收)Food子类的对象

    public void feed(Animal animal,Food food){
        System.out.println("主人"+name+"给"+animal.getName()+"吃"+food.getName());
    }

//    主人给小狗喂食
//    public void feed(Dog dog,Bone bone){
//        System.out.println("主人"+name+"给"+dog.getName()+"吃"+bone.getName());
//    }
//    主人给小猫喂食
//    public void feed(Cat cat,Fish fish){
//        System.out.println("主人"+name+"给"+cat.getName()+"吃"+fish.getName());
//    }

}
package com.poly_;

public class Poly_ {
    public static void main(String[] args) {
        Master tom = new Master("汤姆");
        Dog dog = new Dog("大黄~");
        Bone bone = new Bone("大棒骨~");
        tom.feed(dog,bone);

        Cat cat = new Cat("小花~");
        Fish fish = new Fish("黄花鱼~");
        tom.feed(cat,fish);
    }
}

                           

其他类的代码还是一样的,只改变的feed()方法

原代码:

现代码:

在进行优化后,再增加小动物就不需要增加feed()方法了。现在只需要定义好小动物以及食物的相关属性,在主函数中调用即可。 

2.4多态的相关细节

在讲细节之前引入几段代码

子类的所有方法

package com.poly_.detail_;

public class Cat extends Animal{
    public void eat(){//方法重写
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){//子类的特殊方法
        System.out.println("猫抓老鼠");
    }
}

 父类的所有方法

package com.poly_.detail_;

public class Animal {
    String name = "动物";
    public void sleep(){
        System.out.println("睡");
    }
    public void run(){
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hi,你好");
    }

}

细节1:向上转型

package com.poly_.detail_;

public class PolyDetail {
    public static void main(String[] args) {
        //向上转型:父类的引用指向了子类对象
        Animal animal = new Cat();
        animal.eat();
        animal.run();
        animal.show();
        animal.sleep();
        //animal.catchMouse();无法调用该成员方法
}

父类的引用指向子类的对象

细节2:向下转型 

package com.poly_.detail_;

public class PolyDetail {
    public static void main(String[] args) {
        //向下转型
        Animal animal = new Cat();
        Cat cat = (Cat)animal;
        cat.catchMouse();
        System.out.println("ok~~~~~");

    }
}

注:这里就可以调用子类的特殊属性了。 

向下转型的理解:

原本是使用父类(Animal)的引用animal指向一个Cat对象,因此可以通过向下转型,用一个子类(Cat)类的引用去指向当前的Cat对象,也仅限于此,不能使用别的类的引用去指向现在的对象比如:用一个Dog类的引用指向现在的Cat类。

对于向下转型还有一个非常重要的知识点需要学习那就是instanceof===>可以去看这篇文章<===

细节3:属性

属性没有重写之说!属性的值看编译类型

package com.poly_.detail_;
public class PolyDetail01 {
    public static void main(String[] args) {
        base base = new Sub();
        System.out.println(base.count);
    }
}
class base {
    int count = 10; 
}
class Sub extends base{
    int count = 20;
}

 这里的运行结果是10。编译类型在等号左边,因此编译类型时父base类,所以属性值是10。

2.5java的动态绑定机制(重要)

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。

2.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

package com.dynamic_;

public class DynamicBinding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}

class A {
    public int i = 10;
    public int sum(){
        return getI()+10;
    }
    public int sum1(){
        return i + 10;

    }
    public int getI(){
        return i;
    }
}
class B extends A{
    public int i = 20;
    public int getI(){
        return i;
    }
}

以上述代码为例:

a.sum()其运行类型是子类B,先在子类B中找sum()方法,如果没有则在父类A中寻找。在父类A中可以发现需要调用getI(),因为此时的运行类型是子类B,所以还是先在子类中寻找,找到getI()直接返回20。因此最终结果是30。

a.sum1()同理,因为运行类型是子类B,但在子类B中没有a.sum1()方法,于是在父类中寻找,找到后发现要返回i+10,因为属性不参与动态绑定,于是根据父类的属性说明i=10,所以最a.sum1()返回20。

2.6多态的应用

2.6.1多态数组

package com.poly_.polyarr_;

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

    public Person(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;
    }

    public String say(){
        return " "+name + "\t" + age;
    }
}
package com.poly_.polyarr_;

public class Student extends Person{
    private int score;
    public Student(String name, int age, int score) {
        super(name, age);
        this.score = score;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String say() {
        return " 学生 "+super.say()+" score "+score;
    }
    //特有方法
    public void study(){
        System.out.println(" 学生 " + getName()+ " 正在学java...");
    }
}
package com.poly_.polyarr_;

public class Teacher extends Person{
    private double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public String say() {
        return " 老师 "+super.say() + " salary " + salary;
    }
    //特有方法
    public void teach(){
        System.out.println(" 老师 "+ getName()+" 正在教java");
    }

}

 下面的代码为多态数组的基础代码,也包含了向下转型的详细用法(向下转型一定要按照这个模版来先用instanceof判断在进行向下转型)需要重点看。

package com.poly_.polyarr_;

public class PolyArray {
    public static void main(String[] args) {
        //应用实例:现有一个继承结构如下:要求创建1个Person对象
        //2个student对象和2个Teacher对象,统一放在数组中,并调用每个对象say方法
        Person[] persons = new Person[5];
        persons[0] = new Person("smith", 20);
        persons[1] = new Student("jack", 10,30);
        persons[2] = new Student("scott", 15,60);
        persons[3] = new Teacher("mary", 25,3000);
        persons[4] = new Teacher("smith", 30,5000);
        for(int i=0;i<persons.length;i++)
        {
            //person[i]编译类型是Person,运行内存则根据实际情况由jvm判断
            System.out.println(persons[i].say());
            //思考:我们如果需要调用某个类特有的方法应该怎么处理(向下转型)
            if(persons[i] instanceof Student){//判断person[i]的运行类型是不是Student
                Student student = (Student)persons[i];//向下转型
                student.study();
            }
            else if(persons[i] instanceof Teacher){
                ((Teacher)persons[i]).teach();//第二种写法
            }
            else if(persons[i] instanceof Person){
            }
            else{
                System.out.println(" 你的类型有误");
            }
        }
    }
}

 2.6.2多态参数

定义:方法定义的形参类型为父亲类型,实参类型允许为子类型

这个在前面主人喂食的案例中体现了

(1)方法定义类型为父亲类型

(2)实参类型允许为子类型 

三、 Object类

3.1Object类的基本概念

Object类是类层次结构的根类。每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法。

下面数Object类中的方法:

3.2Object类中的方法介绍

3.2.1 equals方法

定义:equals方法指示其他某个对象是否与此对象相等。

面试题:==和equals的对比如下:

[1]"=="的细节

(1)==:即可以判断基本类型,又可以判断引用类型。

(2)==:如果判断基本类型,判断的是值是否相等。示例:int i = 10;double d = 10;i==d

(3)==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

package com.object;

public class Equals01 {
    public static void main(String[] args) {
        A a = new A();//new开辟了一块新的地址空间空间
        A b = a;
        A c = b;
        System.out.println(a==c);//true
        System.out.println(b==c);//true
        // 因为a,b,c指向的同一个地址空间
        B obj = a;//虽然编译类型和c不一样,但是最终指向的地址空间空间是一样的
        System.out.println(obj == c);//true
    }
}
class B{}
class A extends B{}
[2]equals的细节

(1)是Object类中的方法,只能判断引用类型。

(2)默认判断的是地址是否相等,子类(String,integer)中往往重写该方法,用于判断内容是否相同。

[3]equals的源码查看

equals源码的打开方式:

(1)直接把光标停在equals上面用快捷ctrl+B)

(2)如果快捷键不行也可以按下面的步骤来:

一般下好jdk就配置好源码了,如果没配置好自己在网上找办法配置。

源码展示:

Object  的equals(直接查看Object类的源码找到equals方法可以发现,其默认的比较对象是不是同一对象)

public boolean equals(Object anObject) {
    //如果传入的对象与当前对象是同一对象
    return (this == anObject); //则返回true
}

String 的equals (就是将object类中的equals方法进行重写了,变成了一个比较两个字符串内容的函数)

"hello".equals("abc");
  public boolean equals(Object anObject) {
        if (this == anObject) {//如果传入的对象与当前对象是同一对象
                               //即指向了同一个地址空间
            return true;//则返回true
        }
        if (anObject instanceof String) {//判断类型
            String anotherString = (String)anObject;//向下转型
            int n = value.length;
            if (n == anotherString.value.length) {//如果长度相同
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {//然后一个一个比较字符
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;//如果两个字符串的所有字符都相等,则返回true
            }
        }
        return false;//如果比较的不是字符串,则直接返回false
    }

 Integer 的equals(判断两个值是否相等)

 public boolean equals(Object obj) {
        if (obj instanceof Integer) {//先传入的类型是不是integer类型
            return value == ((Integer)obj).intValue();//在判断值是否相等
        }
        return false;
Integer integer1 = new Integer (1000);
Integer integer2 = new Integer(1000);
System.out.println(integer2 == integer1);//false
//因为new了两次,就分别开辟了两块新的地址空间所以返回false
System.out.println(integer2.equals(integer1));//true
//使用equals后比较的就是值的大小

  附:关于integer的介绍以及integer与int的区别==>点击此处<==

3.2.2hashCode

(后面会详细讲解)

定义:返回该对象的哈希码值,支持此方法是为了提高哈希表的性能。

 关于前三点的代码解释(最后一点后面会说到): 

public class hash_Code {
    public static void main(String[] args) {
        AA aa = new AA();
        AA aa2 = new AA();
        AA aa3 = aa;
        System.out.println("aa.hashcoide()="+aa.hashCode());
        System.out.println("aa2.hashcoide()="+aa2.hashCode());
        System.out.println("aa3.hashcoide()="+aa3.hashCode());
    }
}
class AA{}

                    

3.2.3toString方法

大多数情况下用作输出对象的相关信息

[1]源码查看: 
 /*
(1)getClass().getName()类的全类名(包名+类名)
(2)Integer.toHexString(hashCode())将对象的hashCode值转成16进制字符串\
   */
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
[2]代码演示

没有重写直接调用

package com.object;

public class ToString {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "巡山的", 1000);
        System.out.println(monster.toString());
    }
}

class Monster{
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }
}
//输出结果为com.object.Monster@4554617c

因为我们一般使用toString方法打印或者拼接对象所以要进行重写

package com.object;

public class ToString {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "巡山的", 1000);
        System.out.println(monster.toString());
        //System.out.println(monster);与上面那句等价
    }
}

class Monster{
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }
    //重复同string方法
    //用快捷键alt+insert

    @Override
    public String toString() {
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", sal=" + sal +
                '}';
    }
}
//输出结果为Monster{name='小妖怪', job='巡山的', sal=1000.0}

3.2.4 finalize方法

定义:当垃圾回收器确定不存在对该对象的更多引用时,由该对象的垃圾回收器调用此方法

1.当对象被回收时,系统自动调用该对象的finalisze方法,子类可以重写该方法,做一些释放资源的操作

2.什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,则销毁该对象前,会先调用finalize方法。

3.垃圾回收机制的调用,是由系统来决定(有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制,测试:Car[name]。

package com.object;

public class Finalize {
    public static void main(String[] args) {
        Car bm = new Car("宝马");
        bm = null;
        System.gc();//主动调用垃圾回收器
        //让bm不指向任何对象,这使得Car会变成垃圾。垃圾回收器就会回收(销毁)对象(释放堆里面的空间)
        //在销毁对象前,会调用该对象的finalize方法
        //程序员就可以在finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,打开的文件..)
        //如果程序不重写finalize,那么就会调用object类的finalize方法,即默认处理
        //如果程序员重写了finalize,就可以实现自己的逻辑
        System.out.println("程序退出了");
    }
}
class Car{
    private String name;
    public Car(String name){
        this.name = name;
    }
    @Override
    //这里输入fina就可以找到下面的模版代码,这里只做介绍
    //重写资源
    protected void finalize() throws Throwable {
        System.out.println("我们销毁了汽车"+name);
        System.out.println("释放了某些资源");
    }
}

                                      

在实习开发中基本没啥用,但是面试要用了解一下就行。

四、断点调试

断点调试的相关快捷按键。

具体怎么用==>请看这篇文章<==

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值