Java基础-面向对象2

本文详细讲解了Java面向对象的多态性,包括多态的理解、使用场景、编译时期与运行时期的区别,以及多态性的前提条件。还介绍了向下转型的原理和目的,以及`instanceof`关键字的应用。同时,讨论了`Object`类的`equals()`和`toString()`方法的区别,以及它们在不同情况下的使用。此外,文章还涵盖了`static`关键字的作用,包括静态变量和静态方法的生命周期及使用注意事项,并探讨了单例设计模式的基本概念。
摘要由CSDN通过智能技术生成
  • 子类对象实例化过程图示:

image.png

思考:
1.为什么super(…)和this(…)调用语句不能同时在一个构造器中出现?

答: 因为他俩都只能出现在首行,所以只能写一个(好比古代皇后只能有一个)

2.为什么super(…)或this(…)调用语句只能作为构造器中的第一句出现?

答: 无论通过哪个构造器创建子类对象,需要保证先初始化父类.
目的: 当子类继承父类后,"继承"父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化.

所以加载顺序是首先加载间接父类,其次加载直接父类,然后才是加载到本类

强调说明:

虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象

图示:

image.png

虽然加载了父类构造器了,但不认为创建了多个对象,对外是整个红框,体现的是一个类型的对象,就是new的Dog

5.6.多态性-多态性的使用

面向对象特征之三: 多态性
1.多态性的理解: 可以理解为一个事物的多种形态
2.什么是多态性: 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
Person p = new Man();
Object obj = new 任意();
3.多态的使用: 虚拟方法使用
有了对象的多态性后,在编译期(编译器编译的时候),只能调用父类中声明的方法,但在运行期,实际执行的是子类重写父类的方法
如果子类中没有重写父类的方法,就执行父类的方法(这样使用多态性没有意义)
p.eat();
总结:编译时看等号左边的类型,运行时看右边的对象(编译时是父类,执行时是子类)
4.多态性的使用前提(只说方法的事没有属性的事,多态性方面跟属性没关系): (1)要有类的继承关系 (2)方法的重写
5.对象的多态性: 只适用于重写的方法,不适用于属性(属性的编译和运行都看左边).
如果Java中没有多态性的话,那抽象类和接口就没有存在的意义,因为抽象类和接口都不能造对象,开发中使用抽象类和接口,一定会提供子类的对象或实现类的对象,这里体现都是多态性
多态性的好处: 减少大量方法的重载

public class PersonTest {
   
    public static void main(String[] args) {
   
        Person p1 = new Person();
        p1.eat();
        Man man = new Man();
        man.eat();
        man.age = 25;
        man.earnMoney();
        System.out.println("*********************");
        // 对象的多态性: 父类的引用(p2)指向子类的对象(new Man())
        Person p2 = new Man(); // 多态的形式
        Person p3 = new Woman();
        // 多态的使用: 当调用子类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
        // 有了多态的形式后,通过引用调用子父类都声明过的方法,真正执行的是子类重写父类的方法
        p2.eat(); // 编译看左,运行看右
        p2.walk();
        // 多态性的使用不能调父类没有的方法属性, 编译时,p2是Person类型
        // p2.earnMoney();  报错
        // p2.isSmoking = true; 报错
        System.out.println(p2.id); // 1001; 多态性与属性无关,p2编译时的类型就是属性的类型
    }
}
public class Person {
   
    String name;
    int age;
    int id = 1001;
    public void eat(){
   
        System.out.println("人吃饭");
    }
    public void walk(){
   
        System.out.println("人走路");
    }
}
public class Man extends Person{
   
    boolean isSmoking;
    int id = 1002;
    public void earnMoney(){
   
        System.out.println("男人负责挣钱");
    }

    /*@Override
    public void eat() {
        System.out.println("男人多吃肉,长肌肉");
    }*/

    @Override
    public void walk() {
   
        System.out.println("男人霸气的走路");
    }
}
public class Woman extends Person{
   
    boolean isBeauty;
    public void goShopping(){
   
        System.out.println("女人喜欢购物");
    }
    @Override
    public void eat() {
   
        System.out.println("女人少吃为了减肥");
    }
    @Override
    public void walk() {
   
        System.out.println("女人窈窕的走路");
    }
}
  • 多态性的使用举例:
import java.sql.Connection;

// 多态性的使用举例一:
public class AnimalTest {
   
    public static void main(String[] args) {
   
        AnimalTest test = new AnimalTest();
        test.func(new Dog());
        test.func(new Cat());
    }
    // 使用多态性: 凡是new子类的对象
    // 每种动物对一件事表现出来的形态都不一样 就是多态
    public void func(Animal animal){
    // Animal animal = new Dog(); 对象多态性的形式
        // 因为形参是Animal类型的,所以只能调Animal类里的方法,但实际new了一个Dog类的对象,真正运行的时候,是Dog重写父类方法的执行
        animal.eat();
        animal.shout();
    }
    // 不用多态性的写法: 声明什么类型只能new这个类型的对象
    public void func(Dog dog){
   
        dog.eat();
        dog.shout();
    }
    public void func(Cat cat){
   
        cat.eat();
        cat.shout();
    }
}
// 父类
class Animal{
   
    public void eat(){
   
        System.out.println("动物进食");
    }
    public void shout(){
   
        System.out.println("动物叫");
    }
}
// 子类
class Dog extends Animal{
   
    @Override
    public void eat() {
   
        System.out.println("狗吃骨头");
    }

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

    @Override
    public void shout() {
   
        System.out.println("喵喵喵");
    }
}
// 举例二:
class Order{
   
    public void method(Object obj){
   

    }
}
// 举例三:
class Driver{
   
    // 形参里传哪个对象就建立跟哪个数据库的连接,因为子类方法都重写过了,自然就能实现对那个数据库中表的操作
    public void doData(Connection conn){
    // conn = new MySQLConnection();/ conn = new OracleConnection();/...
        // 规范步骤去操作数据 都是子类重写过的方法
        /*conn.method1();
        conn.method2();
        conn.method3();*/
    }
}
  • 虚拟方法调用的再理解

image.png

  • 面试题: 多态是编译时行为还是运行时行为?
编译时期和运行时期的区别

image.png

面试题: 多态是编译时行为还是运行时行为?

运行时行为;看代码看不出来调的是哪个,因为有随机数,运行了才知道随机数是多少

/ 面试题: 多态是编译时行为还是运行时行为?
// 证明结论: 运行时行为,main函数中的animal引用指向哪个子类的对象,是由运行时产生的随机数再调用了判断得到的,最后运行结果是返回对象类型中eat()方法而不是编译时Animal类中的eat()方法
import java.util.Random;

public class InterviewTest {
   
    public static void main(String[] args) {
   
        int key = new Random().nextInt(3); // 取随机数: 0~2
        System.out.println(key);
        // 通过下面new的对象来看,这实际上就是多态
        Animal animal = getInstance(key);
        animal.eat();
    }
    public static Animal getInstance(int key){
   
        switch (key){
   
            case 0:
                return new Cat();
            case 1:
                return new Dog();
            default:
                return new Sheep();
        }
    }
}
class Animal{
   
    protected void eat(){
   
        System.out.println("animal eat food");
    }
}
class Dog extends Animal {
   
    @Override
    protected void eat() {
   
        System.out.println("dog eat bone");
    }
}
class Cat extends Animal {
   
    @Override
    protected void eat() {
   
        System.out.println("cat eat fish");
    }
}
class Sheep extends Animal {
   
    @Override
    protected void eat() {
   
        System.out.println("sheep eat grass");
    }
}
  • 小结: 方法的重载和重写

image.png

5.7.向下转型的使用
内存解析的说明

引用类型的变量,只可能储存两类值:

  • null
  • 内存地址值,包含变量(对象)的类型

例如,直接打印一个实例化后对象的值
Phone p = new Phone();
System.out.println(p)
得到的结果为:

image.png

image.png

  • instanceof关键字的使用
向下转型的目的: a instanceof A 判断 对象a 运行时是不是多态中子类的A类类型,如果是,则要向下转型,向下转型的目的是可以让对象a点出运行时实际(真实)类型对象的特有的属性或方法

a instanceof A: 判断对象a是否是类A的实例.如果是,返回true,如果不是,返回false
使用情境,为了避免在向下转型时出现ClassCastException的异常,在向下转型之前,
先进行instanceof的判断,一旦返回true,就进行向下转型.如果返回false,不进行向下转型.
如果 a instanceof A 返回 true,则 a instanceof B 也返回true,
其中类B和类A的不都是同一父类的子类

public class PersonTest {
   
    public static void main(String[] args) {
   
        Person p1 = new Person();
        p1.eat(); // 父类调用自己的方法
        Man man = new Man();
        man.eat(); // 子类继承父类调用父类的方法
        man.age = 25;
        man.earnMoney();
        System.out.println("*********************");
        // 对象的多态性: 父类的引用(p2)指向子类的对象(new Man())
        Person p2 = new Man(); // 多态的形式
        Person p3 = new Woman();
        Person p4 = new Person();
        Man m1 = new Man();
        // 多态的使用: 编译时当调用子类同名同参数的方法时,认为是父类的方法,实际执行的是子类重写父类的方法---虚拟方法调用
        // 有了多态的形式后,通过引用调用子父类都声明过的方法,真正执行的是子类重写父类的方法
        p2.eat(); // 编译看左,运行看右; 在编译期认为p2是Person类型,所以只能调用Person中声明的属性和方法
        p2.walk();
        System.out.println("===================");
        p2.name = "tom";
        // 多态性的使用不能调父类没有子类特有的方法,属性, 编译时,p2是Person类型
        // p2.earnMoney(); // 报错
        // p2.isSmoking = true; // 报错
        System.out.println(p2.id); // 1001; 多态性与属性无关,p2编译时的类型就是属性的类型
        // 有了对象的多态性后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型.
        // 导致编译时,只能调用父类中声明的属性和方法,(子类特有的属性和方法相当于被屏蔽了)子类特有的属性和方法不能调用.
        // 如何才能调用子类特有的属性和方法?
        // Man m1 = p2; // java中左右赋值要么左右类型一样,要么是基本数据类型且有自动类型提升; 父类型变量不能赋给子类型变量
        System.out.println(p4);
        System.out.println(m1);
        p4  = m1; // 类类型变量,子类型变量可以赋给父类型变量,表现为多态;
        // 向下转型: 使用强制类型转换符
        System.out.println(p2); // p2运行时是Man类型
        Man m2 = (Man) p2; // 编译时,把Person类型的变量强转成Man类型的一个新变量,相当于m2是Man类型的p2
        m2.earnMoney();
        m2.isSmoking = true;
        // 使用强转时,出现ClassCastException的异常
        // Woman w1 = (Woman) p2; // 编译虽然通过,但运行时p2已经是Man类型了,子类类型之间不能不能互相转换,地址类型不相同,
        // w1.goShopping(); // 执行时出错,出现ClassCastException的异常

        /*
         * instanceof关键字的使用
         * a instanceof A: 判断对象a是否是类A的实例.如果是,返回true,如果不是,返回false
         * 使用情境,为了避免在向下转型时出现ClassCastException的异常,在向下转型之前,
         * 先进行instanceof的判断,一旦返回true,就进行向下转型.如果返回false,不进行向下转型.
         * 如果 a instanceof A 返回 true,则 a instanceof B 也返回true,
         * 其中类B是类A的父类
         */
        // 本质就是把p2的new的类型能否赋值给声明时的类型,是子类对象一定是父类对象,是父类对象不一定是子类对象
        if (p2 instanceof Person){
   
            System.out.println("person");
        }
        if (p2 instanceof Man){
   
            Man m3 = (Man) p2;
            m3.earnMoney();
            System.out.println("man");
        }
        if (p2 instanceof Woman){
   
            Woman w1 = (Woman) p2;
            w1.goShopping();
            System.out.println("woman");
        }
        // 练习:
        // 问题一: 编译时通过,运行时不通过
        /*Person p5 = new Woman();
        Man m4 = (Man) p5;*/
        Person p6 = new Man();
        Man m6 = (Man) p6;
        // 问题二: 编译时通过,运行时也通过
        Object obj = new Woman();
        Person p = (Person) obj;
        // 问题三: 编译时不通过,运行时也不通过
        // Man m5 = new Woman();
        // String s = new Date(); // Date类型和String类没任何关系
    }
}
public class Person {
   
    String name;
    int age;
    int id = 1001;
    public void eat(){
   
        System.out.println("人吃饭");
    }
    public void walk(){
   
        System.out.println("人走路");
    }
}
public class Man extends Person{
   
    boolean isSmoking;
    int id = 1002;
    public void earnMoney(){
   
        System.out.println("男人负责挣钱");
    }

    @Override
    public void eat(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值