Java_面向对象编程-3

目录

主要内容

学习目标

第一节 继承

1.1. Object类

1.2. 成员变量的隐藏

1.3. 继承情况下构造方法的调用过程

第二节 继承

2.1. super关键字

2.2. ==和equals方法

2.3. 组合

第三节 多态

3.1. 引入和使用多态

3.2. 多态之向上转型

第四节 多态

4.1. 多态之向下转型

4.2. 简单工厂模式-返回值是父类类型


主要内容

  1. Object
  2. 继承情况下构造方法的调用过程
  3. super关键字
  4. ==和equals方法
  5. 组合
  6. 引入和使用多态
  7. 多态之向上转型
  8. 多态之向下转型

学习目标

节数

知识点

要求

第一节(继承)

Object

理解

成员变量的隐藏

了解

继承情况下构造方法的调用过程

掌握

第二节(继承)

super关键字

理解

==和equals方法

掌握

组合

了解

第三节(多态)

引入和使用多态

理解

多态之向上转型

掌握

第四节(多态)

多态之向下转型

掌握

简单工厂模式-返回值是父类类型

理解

第一节 继承

1.1. Object

        Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。

面试题目:请你写出Object类的6个方法

方法摘要

 boolean

equals(Object obj)  指示其他某个对象是否与此对象“相等”。

 Class<?>

getClass() 返回此 Object 的运行时类。

 int

hashCode() 返回该对象的哈希码值。

 void

notify()  唤醒在此对象监视器上等待的单个线程。

 void

notifyAll()    唤醒在此对象监视器上等待的所有线程。

 String

toString() 返回该对象的字符串表示。

 void

wait()  在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

 void

wait(long timeout)    在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。

 void

wait(long timeout, int nanos)     在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。

        以上方法是Object类的所有方法吗?不是  是Object类所有的public方法。 除此之外可能还有private、protected、默认的方法 

方法摘要

protected  Object

clone()      创建并返回此对象的一个副本。

protected  void

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

扩展:native关键字

  1. 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
  2. 在定义一个native方法时,并不提供实现体,因为其实现体是由非Java语言在外面实现的。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
  3. JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。 

1.2. 成员变量的隐藏

        如果父类和子类中有同名的成员变量,不存在变量的重写,分别占有自己的空间。子类的成员变量优先,称为成员变量的隐藏。

【示例1】成员变量的隐藏

public class Animal {
    String color="Animal的color";
    int age;
    public String getColor(){
        return color;
    }
}
public class Dog extends Animal {
    String color ="Dog的color";
    String nickName;
    public String getColor(){
        return color;
    }
    public String getSuperColor(){
        return super.getColor();
    }
    public void show(){
        String color = "方法的color";
        System.out.println(color);
        System.out.println(this.color);
        System.out.println(super.color);
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.show();
        System.out.println(dog.getColor());
        System.out.println(dog.getSuperColor());
    }
}

1.3. 继承情况下构造方法的调用过程

继承条件下构造方法的执行顺序

  1. 构造方法的第一条语句默认是super(),含义是调用父类无参数的构造方法
  2. 构造方法的第一条语句可以显式的指定为父类的有参数构造方法:super(.....)
  3. 构造方法的第一条语句可以显式的指定为当前类的构造方法:this(.....)

注意事项

  1. 每个类最好要提供无参数的构造方法
  2. 构造方法的第一条语句可以是通过super或者this调用构造方法,须是第一条语句
  3. 构造方法中不能同时使用super和this调用构造方法,并不是说不能同时出现this和super

【示例2】继承情况下构造方法的调用过程

public class Animal {
    String color;
    private int age;
    public Animal(){
        super();
    }
    public Animal(String color,int age){
        this.color = color;
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class Dog extends  Animal {
    private String nickName;
    private String type; 
    public Dog(){
        super();
    }
    public Dog(String color,int age,String nickName){
        //super();
        super(color,age);
        //this(color,age,nickName,"aaa");
        this.nickName = nickName;
    }
    public Dog(String color,int age,String nickName,String type){
        //super();
        //super(color,age);
        this(color,age,nickName);
        //this.nickName = nickName;
        this.type = type;
    }
    public String toString() {
        return this.color+" "+ this.getAge()+" "+this.nickName+"  "+this.type;
    }
    public static void main(String[] args) {
        Dog dog = new Dog("黑色",3,"旺财","泰迪");
        System.out.println(dog.toString());
    }
}

本节作业

  1. Object类的常用方法及其作用
  2. 说明继承条件下构造方法的执行过程

第二节 继承

2.1. super关键字

        前面的示例中已经多次使用到了super关键字。

        super“可以看做”是直接父类对象的引用。每一个子类对象都会有一个super引用指向其直接父类对象。

使用super可以

  1. 调用成员变量  super.color
  2. 调用成员方法  super.introduce();
  3. 调用构造方法  super(color,age);

注意

  1. 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
  2. 在一个类中,若是构造方法的第一行代码没有显式的调用super(...)或者this(...);那么Java默认都会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。

2.2. ==和equals方法

        “==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。

        Object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。比如,判断两个Dog是否是一个Dog,要求color、age、nickName等所有属性都相同。

        Object 的 equals 方法默认就是比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。显然,这无法满足子类的要求,可根据要求重写equals方法。

【示例3】重写equals方法

public class Animal {
    String color;
    private int age;
    public boolean equals(Object obj) {
        Animal other = (Animal) obj;
        //如果参数是null、,直接返回false
        if(obj == null){
            return false;
        }
        //如果两个变量指向同一个空间,直接返回true
        if(this == obj ){
            return true;
        }
        if(this.color.equals(other.color) && this.age == other.age){
            return true;
        }else{
            return false;
        }
    }
}
public class Dog extends Animal {
    private String nickName;
    private String type;
    public boolean equals(Object obj) {
        Dog other = (Dog) obj;
        boolean flag = super.equals(obj);
        if (!flag) {
            return false;
        } else {
            //如果Animal的equals返回true,需要再比较nickName、type
            if (this.nickName.equals(other.nickName) 
&& this.type.equals(other.type)) {
                return true;
            } else {
                return false;
            }
        }
    }
}

2.3. 组合

继承和组合是复用代码的两种方式;

继承  is-a   Dog is-a Animal  Cat is-a Animal

组合    has-a   Computer has-a cpu memery mainBoard。

扩展: 面向对象的设计原则之一:组合聚合复用原则(优先使用组合,而不是继承)

  1. 除非两个类之间是“is-a”的关系,否则不要轻易的使用继承,不要单纯的为了实现代码的重用而使用继承,因为过多的使用继承会破坏代码的可维护性,当父类被修改时,会影响到所有继承自它的子类,从而增加程序的维护难度和成本。
  2. 不要仅仅为了实现多态而使用继承,如果类之间没有“is-a”的关系,可以通过实现接口与组合的方式来达到相同的目的。设计模式中的策略模式可以很好的说明这一点,采用接口与组合的方式比采用继承的方式具有更好的可扩展性

【示例4】定义CPU类及其子类

public class Cpu {
    private String name;//intel amd
    private double rate;//速度 频率
    public void calc(){
        System.out.println("cpu calc......");
    }
    public void ctrl(){//control
        System.out.println("cpu control...");
    }
}
public class AMDCpu extends Cpu {
    public void calc() {
        System.out.println("AMD calc");
    }
}
public class IntelCpu extends Cpu {
    public void calc() {
        System.out.println("IntelCpu calc");
    }
}

【示例5】定义MailBoard 类和Memory

public class MailBoard {
    public void connect(){
        System.out.println(" mainBoard connecting .....");
    }
}
public class Memory {
    public void process(){
        System.out.println("memory process.....");
    }
}

【示例6】使用组合定义Computer类

public class Computer{
    private Cpu cpu = new Cpu();
    private Memory memory = new Memory();
    private MailBoard mailBoard = new MailBoard();
    private void setCpu(Cpu cpu){
        this.cpu = cpu;
    }
    public void computer(){
        cpu.calc();
        cpu.ctrl();
        memory.process();
        mailBoard.connect();
    }
    public static void main(String[] args) {
        Computer computer = new Computer();
        //computer.setCpu(new IntelCpu());
        Cpu cpu = new AMDCpu();
        Cpu cpu2 = new IntelCpu();
        Cpu cpu3 = new Cpu();
        computer.setCpu(cpu);
        computer.computer();
    }
}

本节作业

  1. super关键字的作用和使用
  2. ==和equals()的区别和联系
  3. 重写Animal和Dog类的equals()方法并进行判断
  4. 组合和继承的联系和区别

第三节 多态

       多态(polymorphism)是面向对象三大特征之一。同一行为,通过不同的子类,可以体现出来的不同的形态。

3.1. 引入和使用多态

        多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游,王五是听音乐; 同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。

        编译器类型指的是‘=’左边的类型,运行期类型指的是‘=’右边的类型。当有继承关系时,可能发生编译期类型和运行期类型不同的情况,即编译期类型是父类类型,运行期类型是子类类型。即:父类引用指向子类对象

多态的要点:

  1. 多态是方法的多态,不是属性的多态(多态与属性无关)。
  2. 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
  3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

【示例7】为多态做准备:定义子类是重写方法

public class Programmer {
    public String name ="proName";//姓名
    //1.子类继承的
    public void writeCode(){
        System.out.println("writing code............");
    }
    //2.子类重写的
    public void eat(){
        System.out.println("eating with mouse.............");
    }
}
public class Chinese extends  Programmer{
    public String name = "ChinName";
    //1.从父类继承一个方法 writeCode()
    //2.重写父类的一个方法 eat
    public void eat() {
        System.out.println(" Chinese eat rice with chopsticks.....");
    }
    //3.子类特有的方法
    public void playShadowBoxing(){
        System.out.println("Chinese play showdowBoxing every day......");
    }
}
public class English extends Programmer {
    //1.从父类继承一个方法 writeCode()
    //2.重写父类的一个方法 eat
    public void eat() {
        System.out.println("English eating meat with knife.....");
    }
    //3.子类特有的方法
    public void raceHorse(){
        System.out.println("English like racing horse......");
    }
}
public class Italian extends  Programmer {
    //1.从父类继承一个方法 writeCode()
    //2.重写父类的一个方法 eat
    public void eat() {
        System.out.println("Italian eating pizza with hand......");
    }
    //3.子类特有的方法
    public void playFootBall(){
        System.out.println("Italian like play football.....");
    }
}

【示例8】实现eat()多态

public class Test {
//    public static  void showEat(Chinese ch){
//        ch.eat();
//    }
//    public static void showEat(Italian it){
//        it.eat();
//    }
//    public static void showEat(English en){
//      en.eat();
//    }
    public static void showEat(Programmer pro){
        pro.eat();
    }
public static void main(String[] args) {
        //Chinese ch = new Chinese();
        Programmer ch = new Chinese();
        showEat(ch);
        English en = new English();
        showEat(en);
        Italian it = new Italian();
        showEat(it);
        Programmer pro = new Programmer();
        showEat(pro);
    }
}
public class Test2 {
    public static void main(String[] args) {
        Chinese ch = new Chinese();
        ch.writeCode();//从父类继承的
        ch.eat();//重写父类的
        ch.playShadowBoxing();//子类特有的方法
        Programmer ch2 = new Chinese();
        ch2.writeCode(); //非多态
        ch2.eat(); //  多态  调用的是子类重写的方法
        //ch2.playShadowBoxing(); // 无法调用子类特有的方法
    }
}

        使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,符合开闭原则。

        父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。另外即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。

扩展: 面向对象的设计原则之一:开闭原则OCP

  1. 对扩展开放,对修改关闭
  2. 更通俗翻译:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能
  3. 面向对象设计需要遵守的最高原则,也是最终目标

3.2. 多态之向上转型

        将子类对象赋给父类引用,称为向上转型(upcasting),自动进行类型转换。向上转型可以调用的子类继承的方法,但不能调用子类特有的方法。需要特别理解的是如果子类重写了父类的方法,向上转型后通过父类引用调用的却是真实子类重写的方法,

【示例7】 向上转型

public class TestPoly {
    public static void main(String[] args) {
        //基本数据类型的自动转换
        int n = 10;
        System.out.println( n );
        double d = n;  // 左>右  自动转换
        System.out.println(d);
        //引用数据类型的自动转换
        Programmer programmer = new Chinese(); // 自动转换  向上转型  左>右
        programmer.writeCode();
        programmer.eat();
        //programmer.playShadowBoxing();
    }
}

理解向上转型

  1. 招聘程序员,来个英国籍程序员,满足要求,不需要特别声明
  2. 不管是哪个国籍的,写到代码都是Java代码
  3. 中午休息了,大家都去食堂开始吃饭,原形毕露
  4. 老板随便找一个程序员,说一起赛马吧,不可以;因为对方可能是中国或意大利程序员

本节作业

  1. 多态的含义及其前提条件
  2. 使用多态实现不同国籍的程序员吃饭。

第四节 多态

4.1. 多态之向下转型

        将父类的引用变量转换为子类类型,称为向下转型(downcasting)。向下转型后就可以调用子类特有的方法了。

  • 需要进行强制转换Chinese ch = (Chinese)pro;
  • 强制转换不是做手术,必须转换成真实子类型,否则ClassCastException;
  • 向下转型之前肯定发生了向上转型
  • 为了避免ClassCastException,向下转型之前使用instanceof先判断一下   

pro instanceof Italian

对象    instanceof  类或者接口

使用instancof的前提:左边的对象和右边的类型在继承树上有上下级关系

【示例8】向下转型

public class TestPoly2 {
    public static void main(String[] args) {
        //基本数据类型的强制转换
        double d = 3.14;
        System.out.println(d);
        int n = (int)d; // 左 < 右  做手术
        System.out.println(n);
        //引用数据类型的强制转换
        Programmer programmer = new Chinese();
        programmer.eat();//多态
        //programmer.playShadowBoxing();
//        Chinese ch = (Chinese) programmer; // 左<右  不做手术,必须转换成原来//的真实子类型
//        ch.playShadowBoxing();//
//        English en = (English)programmer;
//        en.raceHorse();
        if(programmer instanceof Chinese){
            Chinese ch = (Chinese) programmer;
            ch.playShadowBoxing();
        }else if(programmer instanceof English){
            English en = (English) programmer;
            en.raceHorse();
        }else{
            Italian it = (Italian) programmer;
            it.playFootBall();
        }
        //java.lang.ClassCastException:
        // com.bjsxt.poly0.Chinese cannot be cast to com.bjsxt.poly0.English
        System.out.println(programmer instanceof Chinese); //false
        System.out.println(programmer instanceof English); //true
        System.out.println(programmer instanceof Programmer);//true
        System.out.println(programmer instanceof Object);//true
        //System.out.println(programmer  instanceof  String);
    }
}

        注意:多态之和方法有关,和属性无关。入下面示例所示。

【示例9】多态和属性无关

public class TestPoly3 {
    public static void main(String[] args) {
        Chinese chinese  = new Chinese();
        System.out.println(chinese.name);

        Programmer programmer = new Programmer();
        System.out.println(programmer.name);

        Programmer programmer2 = new Chinese();
        System.out.println(programmer2.name); //ChinName  proName
        programmer2.eat();
    }
}

4.2. 简单工厂模式-返回值是父类类型

        不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。

【示例10】使用父类作为返回值

public class TestPoly4 {
    public static void main(String[] args) {
        //自己培养了一个程序员
        //Programmer pro = new Chinese();
        Programmer pro = SxtSchool.getProgrammer("en");
        //让程序员干活
        pro.writeCode();
    }
}
class SxtSchool{
        public static Programmer getProgrammer(String type){
            Programmer pro = null;
            if("ch".equals(type)){
                pro = new Chinese();
            }else if("en".equals(type)){
                pro = new English();
            }else{
                pro = new Italian();
            }
            return  pro;
        }
}

        以上代码其实是简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是

  • 定义一个static方法,通过类名直接调用
  • 返回值类型是父类类型,返回的可以是其任意子类类型
  • 传入一个字符串类型的参数,工厂根据参数创建对应的子类产品

扩展: 设计模式

  1. 有23中经典的设计模式,是一套被多数人知晓、经过分类编目的、反复使用的优秀代码设计经验的总结。每个设计模式均是特定环境下特定问题的处理方法。
  2. 面向对象设计原则是面向对象设计的基石,面向对象设计质量的依据和保障,设计模式是面向对象设计原则的经典应用。
  3. 简单工厂模式并不是23中经典模式的一种,是其中工厂方法模式的简化版

本节作业

  1. 向下转型的相关注意事项
  2. 面向对象设计原则有哪些
  3. 23种设计模式分为哪3大类,分别是哪些
  4. 使用简单工厂模式模拟sxt培训不同专业的程序员

猜你喜欢

转载自blog.csdn.net/weixin_45859844/article/details/120486379