Day4 Java—面向对象2

本文深入探讨了面向对象的多态概念,包括父类引用指向子类对象、成员变量的使用原则、多态带来的好处与弊端,以及抽象类和接口的定义、特点和应用实例。通过银行交易和消息接收的案例,展示了接口在描述功能规范中的作用。
摘要由CSDN通过智能技术生成

Day4 面向对象

1、多态

1. 概念

  • 多种状态,同一对象在不同情况下表现出不同的状态或行为
  • 要==有继承(实现)==关系
  • 要**有方法重写**
  • 父类引用指向子类对象
/*
    Animal父类
 */
public class Animal {

    private String name;

    public Animal(){};    //无参构造

    public Animal(String name){   //有参构造
        this.name=name;
    }

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

    public String getName() {
        return name;
    }

    public void eat(){
        System.out.println("吃饭");
    }
}
/*
    Dog子类
 */
public class Dog extends Animal{

    @Override               //方法重写
    public void eat(){
        System.out.println(getName()+"吃骨头");
    }
}
/*
    测试类1
 */
public class Test1 {
    public static void main(String[] args) {

        /*
            父类引用指向子类对象
           /通过父类接受子类对象
         */
        Animal a=new Dog();
        a.setName("哈士奇");
        a.eat();
    }
}

//哈士奇吃骨头

上课示例——多态

/*
    父类Animal
 */
public class Animal {

    public Animal(){};

    public void eat(){
        System.out.println("父类吃法");
    }
}

/*
    子类Cat
 */
public class Cat extends Animal{

    public Cat(){};

    @Override  //重写父类方法
    public void eat() {
        System.out.println("猫吃鱼");
    }

    //子类Cat特有的方法
    public void catchMouse(){
        System.out.println("猫特有的方法");
    }
}

/*
    子类Dog
 */
public class Dog extends Animal{

    public Dog(){};

    @Override    //重写父类方法
    public void eat() {
        System.out.println("狗吃骨头");
    }
}

/*
    测试类
 */
public class Test {

    private static Animal animal;

    public static void main(String[] args) {

        /**
         * 多态
         */
        animal = new Animal();
        animal.eat();           //父类吃法

        animal = new Dog();     //编译看左,运行看右
        animal.eat();           //狗吃骨头

        animal = new Cat();
        animal.eat();           //猫吃鱼

        //向下转型,转成猫的类型,就能获取猫特有的方法
        Cat cat = (Cat) animal;
        cat.catchMouse();       //猫特有的方法
    }
}
  • 上述代码表明,多态中调用成员方法是编译看左,运行看右

    • 即看的是左边的类中有没有这个成员,用的是右边的类中的该成员
    • Animal类中有eat方法,则编译成功,Dog类中实际运行eat方法,则运行成功
  • 父类型变量作为参数时,可以接受任意子类型对象(如下代码)

/*
    Animal父类
 */
public class Animal {

    private String name;

    public Animal(){};    //无参构造

    public Animal(String name){   //有参构造
        this.name=name;
    }

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

    public String getName() {
        return name;
    }

    public void eat(){
        System.out.println("吃饭");
    }
}
/*
    Dog子类
 */
public class Dog extends Animal{

    @Override      //方法重写
    public void eat(){
        System.out.println(getName()+"吃骨头");
    }
}
/*
	Mouse子类
*/
public class Mouse extends Animal{
    
    @Override    //方法重写
    public void eat(){
        System.out.println(getName()+"吃奶酪");
    }
}
/*
	测试类2
*/
public class Test2 {
    /*
        父类型可以接收任何子类型对象
     */
    //静态方法,静态方法只要定义了类,不必建立类的实例就可使用。 静态方法只能调用静态变量。
    public static void showAnimal(Animal a){    
        a.eat();
    }

    public static void main(String[] args) {
        Dog d=new Dog();
        d.setName("二哈");
        showAnimal(d);     //showAnimal()是静态方法,所以没建实例,直接调用,
                           //参数被设为父类,所以d虽然是Dog类,但由于是子类,所以也能被接收

        Mouse m=new Mouse();
        m.setName("Jerry");
        showAnimal(m);
    }
}

2. 成员变量

  • 多态关系中,成员变量是不涉及到重写的
  • 多态关系中,使用成员变量,遵循**”编译看左,运行看右“**
    • 编译时,看左边类中有没有这个成员
    • 运行时,使用右边类中的成员
/*
    Animal父类
 */
public class Animal {

   String name="Animal";
}
/*
    Dog子类
 */
public class Dog extends Animal{

   String name="Dog";
}
/*
    测试类
 */
public class Test1 {
    public static void main(String[] args) {

        Animal a = new Dog();
        System.out.println(a.name);    //运行结果是Animal   ???????????????????

        Dog d = new Dog();
        System.out.println(d.name);    //运行结果是Dog
    }
}
  • 多态的好处

    • 可维护性(继承):基于继承关系,只需要维护父类代码,提高了代码的复用性,降低维护程序工作量
    • 可扩展性(多态):把不同的子类对象都当作父类看待,屏蔽了不同子类对象间的差异,做出通用的代码,以适应不同需求,实现了向后兼容
  • 多态弊端

    • 不能使用子类特有成员

    • 当需要使用子类特有功能时,需要进行类型转换

      • 向上转型(自动)

        • 子类型转换为父类型
        • Animal a=new Dog();
      • 向下转型(强制)

      • 父类转换成子类

      • Dog d=(Dog)animal;

    • 只能在继承层次内转换

    • 类对象类之前,使用instanceof进行检查

      • instanceof:左边对象,右边类;当对象是右边类或子类所创建的对象时,返回true;否则,返回false。
/*
    Animal父类
 */
public class Animal {

    String name="Animal";

    public void eat(){
        System.out.println("吃饭");
    }

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

    public String getName() {
        return name;
    }
}
/*
    Dog子类
 */
public class Dog extends Animal{

    String name="Dog";

    //子类特有的方法
    public void watch(){
        System.out.println(getName()+"看家");
    }

    //重写父类方法
    public void eat(){
        System.out.println(super.getName()+"吃饭");
    }

}
/*
    测试类
 */
public class Test {

    public static void main(String[] args) {

        Dog d = new Dog();
        d.setName("二哈");
        showAnimal(d);

    }

    /**
     * 代码块 1
     * @param animal
     */
    public static void showAnimal(Animal animal){

        //当对象是右边类或子类所创建的对象时,返回true
        if(animal instanceof Dog){
            //Dog d=(Dog)animal;   //❤️二哈看家(强制转换的体现)
            Dog d = new Dog();     //❤️Animal看家
            d.watch();
        }
        animal.eat();            //二哈吃饭
    }


    /**
     * 代码块 2
     */
//    public static void showAnimal(Dog d){
//        d.watch();             //二哈看家
//        d.eat();               //二哈吃饭
//    }

}
  • 多态在开发中的应用
    • 以后开发中,我们会面向多态编程(面向抽象,而不是面向具体)
    • OCP原则:设计模式八大原则之一,对扩展开放,对修改关闭,如下代码(体现多态)

问题描述:主人养宠物,宠物逐渐变多,宠物父类,各种宠物子类

/*
    宠物父类
 */
public class Pet {
    private String name;
    private  String food;

    public Pet(String name, String food) {
        this.name = name;
        this.food = food;
    }

    public String getName() {
        return name;
    }

    public String getFood() {
        return food;
    }

    /**
     * 吃法
     */
    public void eat(){
        System.out.println(this.name+"吃"+this.food);
    }
}
/*
    猫·子类
 */
public class Cat extends Pet{

    public Cat(String name, String food) {
        //指向父类的有参构造
        super(name, food);
    }
     
    //方法重写
    public void eat(){
        System.out.println(super.getName()+"吃"+super.getFood());
    }
}
/*
    狗·子类
 */
public class Dog extends Pet{

    public Dog(String name, String food) {
        super(name, food);
    }

    //方法重写
    public void eat(){          
        System.out.println(super.getName()+"吃"+super.getFood());
    }
}

/*
    主人类
 */
public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }

    /**
     * 喂宠物
     */
    public void feed(Pet pet){      //父类作为参数可以接收所有子类对象
        pet.eat();

    }
}
/*
    测试类
 */
public class Test {
    public static void main(String[] args) {

        Master m=new Master("zj");
        Dog d=new Dog("哈","骨头");
        Cat c=new Cat("猫","猫粮");
        
         /**
            法 1
         */
        m.feed(d);
        m.feed(c);

        System.out.println();

        /**
            法 2
         */
        //体现多态,如果没有多态,不同的动物就无法放在一个数组里了
        Pet[] prr1=new Pet[2];
        //添加宠物到数组中
        prr1[0]=d;
        prr1[1]=c;
        //遍历
        for(Pet pet:prr1){
            m.feed(pet);
        }

        System.out.println();

        /**
             法 3
         */
        ArrayList<Pet> prr2=new ArrayList<>();
        prr2.add(d);
        prr2.add(c);
        for(Pet pet:prr2){
            m.feed(pet);
        }

    }
}

//哈吃骨头
//猫吃猫粮

//哈吃骨头
//猫吃猫粮

//哈吃骨头
//猫吃猫粮

2、抽象类

  • 针对某一抽象事物,在此基础上再抽象,则定义一个抽象类
  • 包含抽象方法的类,用abstract修饰
  • 只有方法声明没有方法体的方法,用abstract修饰
  • 当需要定义一个方法,却不明确方法的具体实现时,可以将方法定义为abstract,具体实现延迟到子类
/*
    Animal父类
 */
public abstract class Animal {

   private String name;
   
    /*
        定义抽象方法,子类中必须实现
     */
   public abstract void eat();

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

    public String getName() {
        return name;
    }
}
/*
    Dog子类
 */
public class Dog extends Animal{

   @Override
   public void eat(){     //实现父类中未实现的抽象方法
       System.out.println(getName()+"看家");
   }

}

1. 抽象类特点

  • 必须用abstract修饰(方法/类)

    • 修饰符 abstract class 类名{}
    • 修饰符 abstract 返回类型 方法名{}
  • 抽象类**不能被实例化**,只能创建子类对象

  • 抽象类子类的两个选择

    • 重写父类所有抽象方法——普通子类
    • 定义成抽象类——抽象子类
  • 成员变量

    • 可以有普通方法,也可以有抽象方法
    • 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类(或接口)
    • 抽象方法一定只在抽象类中
  • 构造方法

    • 抽象类有构造方法,且可以重载(接口没有构造方法,因为它不能被实例化,不需要初始化成员)
  • 当子类继承了抽象类后,必须在子类中==重写==抽象类中的抽象方法

2. final关键字

  • 修饰类:则该类不能被继承

  • 修饰方法:则该方法不能被重写不能与abstract共存

  • 修饰变量/属性:最终变量,即常量,只能赋值一次

    • 不建议修饰引用类型数据,因为仍然可以通过引用修改对象的内部数据,意义不大
    • 如果是引用数据类型,则该引用不能指向别的内存空间
  • 常量定义格式:全大写,下划线隔开(运行期间,值不变)

  • 定义常量private static final String COUNTRY_NAME="中国"

    • static,在方法区中开辟空间,随着类加载而加载,为了节约系统资源
    • 变量内存开辟在

3. static关键字

  • 用于修饰类的成员

    • 成员变量:类变量

      • 被本类所有对象共享
    • 成员方法:类方法

  • 调用方式

    • 类名.成员变量名;
    • 类名.成员方法名(参数);
public class Developer {
    String name;
    String work;

    //使用static修饰的成员变量,所以对象共享
    static String departmentName="研发部";

    //public final static String departmentName="研发部";
    //加上final后就不允许修改了,公共的静态常量

    public void selfIntroduction(){
        System.out.println("我是"+departmentName+"的"+name+",我的工作内容是"+work);
    }

}
public class Test3 {
    public static void main(String[] args) {
        Developer d1=new Developer();
        d1.name="01";
        d1.work="代码";
        d1.departmentName="开发部";
        d1.selfIntroduction();

        Developer d2=new Developer();
        d2.name="02";
        d2.work="吃饭";
        //部门名字被01修改了
        d2.selfIntroduction();
    }
}

//我是开发部的01,我的工作内容是代码
//我是开发部的02,我的工作内容是吃饭

上述代码修改部门名不符合规范,所以可以修改如下:

public class Test3 {
    public static void main(String[] args) {
        Developer d1=new Developer();
        d1.name="01";
        d1.work="代码";
        d1.selfIntroduction();

        Developer d2=new Developer();
        d2.name="02";
        d2.work="吃饭";
        d2.selfIntroduction();

        System.out.println("------------------------------");
        Developer.departmentName="开发部";      //使用类名直接修改
        d1.selfIntroduction();
        d2.selfIntroduction();
    }
}
  • 静态方法

    • 静态方法中没有对象this,所以不能访问非静态成员
  • 静态方法的使用场景

    • 只需访问静态成员
    • 不需要访问对象状态,所需参数都由参数列表显示提供

4. 案例1——反转数组

  • 定义**静态方法**,在类定义的时候已经被装载和分配,只能调用静态成员或者方法
  • 非静态方法,在类定义时没有占用内存,只有在类被实例化成对象时,对象调用该方法才被分配内存
public class ReverseArr {
    /*
        静态方法,反转一个数组
     */
    public static void reverse(int[] arr){
        for(int i=0;i<arr.length/2;i++){
            int temp=arr[i];
            arr[i]=arr[arr.length-i-1];
            arr[arr.length-i-1]=temp;
        }
    }
}
import static com.zj.Day5.ReverseArr.reverse;     //引进类

public class Test4 {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5,6,7,8};
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]);
        }

        System.out.println();

        ReverseArr.reverse(arr);        //调用ReverseArr类中的静态方法reverse
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]);
        }
    }
}

//12345678
//87654321

5. 案例2——银行交易(抽象类)

/**
    抽象银行父类
    银行共同业务(方法):查询,转账,取钱,存钱(所有这些业务逻辑都一样)
    银行共同属性:银行名称,账户名,账户余额
 */
public abstract class Bank {
    
    //银行名称
    private String BankName;

    //账户名
    private String userName;

    //账户余额
    private double balance;
    
    //getBankName方法
    public String getBankName() {
        return BankName;
    }
    
	//getUserName方法
    public String getUserName() {
        return userName;
    }
    
	//setBalance方法
    public void setBalance(double balance) {
        this.balance = balance;
    }
    
    //getBalance方法
    public double getBalance() {
        return balance;
    }
    
    //构造方法——初始化对象(为了创建对象)
    public Bank(String userName) {
        this.userName = userName;
    }
    
	 /**
     * 查询余额
     * @return
     */
    public void getbalance(Bank bank) {
        System.out.println("---------------查询业务---------------");
        System.out.println(bank.getUserName()+"的"+bank.getBankName()+"账户余额为:"+bank.getBalance());
    }

    /**
     * 转账
     * @param bank1
     * @param bank2
     */
    public void transfer(Bank bank1,Bank bank2,double money){

        System.out.println("-------------------转账业务----------------");
        if(money>0){

            if(money>bank1.getBalance()){
                System.out.println("余额不足!");
            }else{
                //扣钱
                bank1.setBalance(bank1.getBalance()-money);
                //加钱
                bank2.setBalance(bank2.getBalance()+money);

                System.out.println("转账成功!\n"+ bank1.getUserName()+"的"+bank1.getBankName()+"账户余额为:"+bank1.getBalance()+"\n"+
                        bank2.getUserName()+"的"+bank2.getBankName()+"账户余额为:"+bank2.getBalance());
            }
        }else{
            System.out.println("输入金额不合法");
        }
    }

    /**
     * 存钱
     * @param money
     * @param bank
     */
    public void save(double money,Bank bank){
        System.out.println("------------------存钱业务-----------------");
        if(money>0){
            bank.setBalance(bank.getBalance()+money);
            System.out.println("存钱成功!"+bank.getUserName()+"的"+bank.getBankName()+"账户余额为:"+bank.getBalance());
        }else{
            System.out.println("金额不合法");
        }
    }


    /**
     *取钱
     * @param bank
     * @param money
     */
    public void getMoney(Bank bank,double money){
        System.out.println("------------------取钱业务-------------------");
        if(money>0){
            if(money>bank.getBalance()){
                System.out.println("余额不足!");
            }else {
                bank.setBalance(bank.getBalance() - money);
                System.out.println("取钱成功!" + bank.getUserName() + "的"+bank.getBankName()+"账户余额为:" + bank.getBalance());
            }
        }else{
            System.out.println("金额不合法");
        }
    }
}
/**
	其中一个银行子类
*/
public class China extends Bank{

    //银行名称设为常量,不可更改
    private final static String BANK_NAME="中国银行";

    //获取本银行名称
    public String getBankName() {
        return BANK_NAME;
    }

    //获取本银行的账户
    public China(String userName) {
        super(userName);
    }
}
/**
	测试类
    1、张三往农业银行卡存钱4000元
    2、李四往建设银行卡存钱2000元
    3、王五往中国银行卡存钱1000元
    4、赵六往工商银行卡存钱5000元
    5、李四往王五的账户上转了1000元
    6、赵六往李四的账户上转账1500元
    7、张三取钱2000元
    8、最后查询所有人余额
 */
public class Test {
    public static void main(String[] args) {
        //创建用户对象
        Bank b1=new NongYe("张三");
        Bank b2=new JianShe("李四");
        Bank b3=new China("王五");
        Bank b4=new GongShang("赵六");

        b1.save(4000,b1);
        b2.save(2000,b2);
        b3.save(1000,b3);
        b4.save(5000,b4);

        b2.transfer(b2,b3,1000);
        b4.transfer(b4,b2,1500);

        b1.getMoney(b1,2000);

        b1.getbalance(b1);
        b2.getbalance(b2);
        b3.getbalance(b3);
        b4.getbalance(b4);
    }
}

3、接口

  • 接口技术用于描述类具有什么功能,但并不给出具体实现,类要遵从接口描述的统一规则进行定义,所以,接口是对外提供的一组规则、标准(以后开发中的规范,制定需求前先制定规范)
  • 结合多态一起使用(多态提高可扩展性,接口使程序面向模糊)
  • 是抽象类的一种,不能实例化对象只能定义常量和抽象方法
  • 是引用数据类型,编译时会生成字节码文件
/*
	接口Smoking
*/
public interface Smoking {
    /**
     * 接口类中定义的成员变量会默认加上public abstract
     */
    public abstract void smoke();
}
/*
	接口的子类Teacher
*/
public class Teacher implements Smoking{

    /**
     * 子类中需要重写接口中的抽象方法
     */
    @Override
    public void smoke() {
        System.out.println("抽烟有害健康!");
    }
}
/*
	测试类
*/
public class Test5 {
    public static void main(String[] args) {
        /*
            接口不能实例化,需要
            创建接口的子类(Teacher)的对象来完成实例化操作
         */
        Smoking sm=new Teacher();
        sm.smoke();
    }
}
  • 接口不能实例化,需要创建其子类对象来完成实例化

  • 接口的子类

    • 如果是普通类,则必须重写接口中的所有抽象方法
    • 如果是抽象类,则不用重写接口中的抽象方法
  • 接口和类的关系**(单继承,多实现)**

    • 类与类

      • 继承关系只能单继承==(extends后面只能跟一个类)==,不能多继承,但可以多层继承
    • 类与接口

      • 实现关系,可以单实现,也可以多实现==(implements 后面可跟多个接口)==
    • 接口与接口

      • 继承关系,可单继承,也可多继承==(implements 后面可跟多个接口)==
  • 对于一个类

    • 他的父类(继承关系)中定义的都是共性内容
    • 他的父接口(实现关系)中定义的都是扩展内容
  • 通用

    • package:类所在包
    • import:引进别的包的资源

1. 接口成员

  • 接口没有成员变量,只有公有的、静态的常量

  • JDK7及以前,公有的、抽象方法,JDK8以后,可以有默认方法和静态方法,JDK9以后,可以有私有方法

  • 接口不能实例化,没有需要初始化的成员,所以接口没有构造方法

2. 案例1

  • 问题描述:人可以接收消息,一开始是QQ,微信,后面还有推特,短信,彩信等等,用接口实现
/**
 * 接口 Message
 */
public interface Message {
    /**
     *接口中的方法public static可以省略
     */
    void getMessage();
}
/**
 * 微信完成获取消息的动作,接口的普通子类
 */
public class WeiXin implements Message{
    /**
     * 接口具体实现,即方法重写
     */
    @Override
    public void getMessage() {
        System.out.println("接收微信消息");
    }

}
/**
 * 人完成接收消息的动作
 */
public class Person {
    /**
     * 把接口作为参数,后面就可以调用接口子类的对象(因为接口不能实例化)
     * @param message
     */
    public void receive(Message message){
        message.getMessage();
    }
}

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        //接口不能实例化,要创建接口Message的子类WeiXin的对象
        Message m=new WeiXin();
        Person p=new Person();
        p.receive(m);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值