java-面向对象三大特征

面向对象编程的三大特征:继承、封装、多态

继承

  继承是面向对象编程的三大特征之一。继承让我们更加容易实现类的扩展。实现代码的重用,不用再重新发明轮子(don’t reinvent wheels)。

  1. 代码复用,更加容易实现类的扩展
  2. 方便建模

  继承通过extends实现
    格式:class 子类 extends 父类 { } 

继承的实现

  从英文字面意思理解,extends 的意思是“扩展”。子类是父类的扩展。

  

使用 extends 实现继承
public class Test {
    public static void main(String[] args) {
        Student s = new Student("高淇", 176, "Java");
        s.rest();
        s.study();
    }
}

class Person {
    String name;
    int height;

    public void rest() {
        System.out.println("休息一会!");
    }
}

class Student extends Person {  // Student继承person类,为其子类,会自动拥有父类的非私有方法及属性
    String major; // 专业

    public void study() {
        System.out.println("在家,自学Java");
    }

    public Student(String name, int height, String major) { // 子类有参构造,也拥有父类属性
        // 天然拥有父类的属性
        this.name = name;
        this.height = height;
        this.major = major;
    }
}

继承的好处和弊端😀😅

  继承好处

    提高了代码的复用性(多个类相同的成员可以放到同一个类中)

    提高了代码的维护性(如果方法的代码需要修改,修改一处即可)

 继承弊端

    继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性

  继承的应用场景:

    使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承

    is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类

Java中继承的特点

  Java中继承的特点

    1. Java中类只支持单继承,不支持多继承

    错误范例:class A extends B, C { }

    2. Java中类支持多层继承

多层继承示例代码:

public class Granddad {
    public void drink() {
        System.out.println("爷爷爱喝酒");
    }
}

public class Father extends Granddad {
    public void smoke() {
        System.out.println("爸爸爱抽烟");
    }
}

public class Mother {
    public void dance() {
        System.out.println("妈妈爱跳舞");
    }
}

public class Son extends Father {
    // 此时,Son类中就同时拥有drink方法以及smoke方法
}

继承中变量的访问特点

  在子类方法中访问一个变量,采用的是就近原则。

    1. 子类局部范围找

    2. 子类成员范围找

    3. 父类成员范围找

    4. 如果都没有就报错(不考虑父亲的父亲…

class Fu {  
    int num = 10;
}

class Zi {
    int num = 20;

    public void show() {
        int num = 30;
        System.out.println(num);
    }
}

public class Demo1 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show(); // 输出show方法中的局部变量30
    }
}

继承使用要点

  1. 父类也称作超类、基类。 子类:派生类等。

  2. Java 中只有单继承,没有像 C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。

  3. Java 中类没有多继承,接口有多继承。

  4. 子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。

  5. 如果定义一个类时,没有调用 extends,则它的父类是:java.lang.Object。 

 super方法

  this&super关键字:

    this:代表本类对象的引用

    super:代表父类存储空间的标识(可以理解为父类对象引用)

    this和super的使用分别

  成员变量:

    this.成员变量 - 访问本类成员变量

    super.成员变量 - 访问父类成员变量

  成员方法:

    this.成员方法 - 访问本类成员方法

    super.成员方法 - 访问父类成员方法

  构造方法:

    this(…) - 访问本类构造方法

    super(…) - 访问父类构造方法

  注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

  子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

  1. 通过使用super关键字去显示的调用父类的带参构造方法

  2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法

  注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存

public class TestSuper01 {
    public static void main(String[] args) {
        new ChildClass().f();
    }
}

class FatherClass {
    public int value;

    public void f() {
        value = 100;
        System.out.println("FatherClass.value=" + value);
    }
}

class ChildClass extends FatherClass {
    public int value;
    public int age;

    public void f() {
        super.f(); // 调用父类的普通方法
        value = 200;
        System.out.println("ChildClass.value=" + value);
        System.out.println(value);
        System.out.println(super.value); // 调用父类的成员变量
    }

    public void f2() {
        System.out.println(age);
    }
}

  构造方法调用顺序:
    构造方法第一句总是:super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到 Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

    注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复。

继承中成员方法的访问特点 

  通过子类对象访问一个方法

    1. 子类成员范围找

    2. 父类成员范围找

    3. 如果都没有就报错(不考虑父亲的父亲…)

   super内存图

方法重写 override

  1、方法重写概念

    子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)

  2、方法重写的应用场景

    当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容

  3、Override注解

    用来检测当前的方法,是否是重写的方法,起到【校验】的作用 

  子类重写父类的方法,可以用自身行为替换父类行为。重写是实现多态的必要条件。 

/**
 * 测试方法的重写
 */
public class TestOverride {
    public static void main(String[] args) {
        Horse h = new Horse();
        Plane p = new Plane();
        h.run();
        h.getVehicle();
        p.run();
    }
}

class Vehicle { // 交通工具类
    public void run() {
        System.out.println("跑....");
    }

    public Vehicle getVehicle() {
        System.out.println("给你一个交通工具!");
        return null;
    }
}

class Horse extends Vehicle { // 马也是交通工具
    @Override
    public void run() {
        System.out.println("得得得....");
    }

    @Override
    public Horse getVehicle() {
        return new Horse();
    }
}

class Plane extends Vehicle {
    @Override
    public void run() {
        System.out.println("天上飞....");
    }
}
public class Fu {
    private void show() {
        System.out.println("Fu中show()方法被调用");
    }

    void method() {
        System.out.println("Fu中method()方法被调用");
    }
}

public class Zi extends Fu {
    /* 编译【出错】,子类不能重写父类私有的方法 */
    @Override
    private void show() {
        System.out.println("Zi中show()方法被调用");
    }

    /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    private void method() {
        System.out.println("Zi中method()方法被调用");
    }

    /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    public void method() {
        System.out.println("Zi中method()方法被调用");
    }
}

 权限修饰符

  关于 protected 的两个细节:

    1. 若父类和子类在同一个包中,子类可访问父类的 protected 成员,也可访问父类对象的protected 成员。

    2. 若子类和父类不在同一个包中,子类可访问父类的 protected 成员,不能访问父类对象的 protected 成员。

 

final 关键字 

  final 关键字的作用:

    修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。

    final int MAX_SPEED = 120;

  修饰方法:该方法不可被子类重写但是可以被重载!

    final void study(){}

  修饰类: 修饰的类不能被继承。比如:Math、String 等。

    final class A {}

继承和组合

  除了继承,“组合”也能实现代码的复用!“组合”核心是“将父类对象作为子类的属性”。 

  

public class demo1 {
    public static void main(String[] args) {
        Student s = new Student("张三", 172, "Java");
        s.person.rest(); // s.rest();
        s.study();
    }
}

class Person {
    String name;
    int height;

    public void rest() {
        System.out.println("休息一会!");
    }
}

class Student /* extends Person */ {
    Person person = new Person();  // 实例化person类,将person类中的各种公共属性和方法变成student的局部变量
    String major; // 专业 

    public Student(String name, int height, String major) {
        // 拥有父类的对象,通过这个对象间接拥有它的属性和方法
        this.person.name = name; // this.name = name;  这是person类中的属性
        this.person.height = height; // this.height = height;  这是person类中的属性
        this.person.rest();
        this.major = major;
    }
}

组合比较灵活。继承只能有一个父类,但是组合可以有多个属性。

比如:上面的例子,Student is a Person 这个逻辑没问题,但是:Student has a Person就有问题了。这时候,显然继承关系比较合适。

Object 类详解

  Object 类基本特性 

    1. Object 类是所有类的父类,所有的 Java 对象都拥有 Object 类的属性和方法。

    2. 如果在类的声明中未使用 extends,则默认继承 Object 类。

 toString 方法

    Object 类中定义有 public String toString()方法,其返回值是 String 类型。Object类中 toString 方法的源码为:

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

  根据如上源码得知,默认会返回“类名+@+16 进制的 hashcode”。在打印输出或者用字符串连接对象时,会自动调用该对象的 toString()方法。

  重写 toString()方法 

class Person {
    String name;
    int age;

    @Override
    public String toString() {
        return name + ",年龄:" + age;
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        p.age = 20;
        p.name = "张三";
        System.out.println("info:" + p);
        Test t = new Test();
        System.out.println(t);
    }
}

==和 equals 方法

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

  equals() 提供定义“对象内容相等”的逻辑。比如,我们在公安系统中认为 id 相同的人就是同一个人、学籍系统中认为学号相同的人就是同一个人。

  equals()默认是比较两个对象的 hashcode。但可以根据自己的要求重写 equals 方法。

public class TestEquals {
    public static void main(String[] args) {
        Person p1 = new Person(123, "张三");
        Person p2 = new Person(123, "李四");
        System.out.println(p1 == p2); // false,不是同一个对象
        System.out.println(p1.equals(p2)); // true,id相同则认为两个对象内容相同
        String s1 = new String("阿里巴巴");
        String s2 = new String("阿里巴巴");
        System.out.println(s1 == s2); // false, 两个字符串不是同一个对象
        System.out.println(s1.equals(s2)); // true, 两个字符串内容相同
    }
}

class Person {
    int id;
    String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        } else {
            if (obj instanceof Person) {
                Person c = (Person) obj;
                if (c.id == this.id) {
                    return true;
                }
            }
        }
        return false;
    }
}

  JDK 提供的一些类,如 String、Date、包装类等,重写了 Object 的 equals 方法,调用这些类的 equals 方法, x.equals (y) ,当 x 和 y 所引用的对象是同一类对象且属性内容相等时(并不一定是相同对象)返回 true 否则返回 false。

封装

  private关键字

  概述 : private是一个修饰符,可以用来修饰成员(成员变量,成员方法)

  特点 : 被private修饰的成员,只能在本类进行访问,针对private修饰的成员变量,如果需要被其他类使用, 提供相应的操作

  提供“get变量名()”方法,用于获取成员变量的值方法用public修饰

  提供“set变量名(参数)”方法,用于设置成员变量的值方法用public修饰

 编程中封装的具体优点:

    提高代码的安全性。

    提高代码的复用性。

    高内聚:封装细节,便于修改内部代码,提高可维护性。

    低耦合:简化外部调用,便于调用者使用,便于扩展和协作。 

  开发中封装的简单规则:

    属性一般使用 private 访问权限。

    属性私有后, 提供相应的 get/set 方法来访问相关属性这些方法通常是public 修饰的,以提供对属性的赋值与读取操作(注意:boolean 变量的 get方法是 is 开头!)。

    方法:一些只用于本类的辅助性方法可以用 private 修饰,希望其他类调用的方法用 public 修饰。

  JavaBean 的封装演示

public class Person {
    // 属性一般使用 private 修饰
    private String name;
    private int age;
    private boolean flag;

    // 为属性提供 public 修饰的 set/get 方法
    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 boolean isFlag() {// 注意:boolean 类型的属性 get 方法是 is 开头的
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
// 封装的使用
class Person {
    private String name; // 私有属性,只能本类访问
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        // this.age = age;//构造方法中不能直接赋值,应该调用 setAge 方法 因为是私有属性,只有调用set方法来设置值;
        setAge(age);
    }

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

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        // 在赋值之前先判断年龄是否合法
        if (age > 130 || age < 0) {
            this.age = 18;// 不合法赋默认值 18
        } else {
            this.age = age;// 合法才能赋值给属性 age
        }
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

public class Test2 {
    public static void main(String[] args) {
        Person p1 = new Person();
        // p1.name = "小红"; //编译错误
        // p1.age = -45; //编译错误
        p1.setName("小红");
        p1.setAge(-45);
        System.out.println(p1);
        Person p2 = new Person("小白", 300);
        System.out.println(p2);
    }
}

  this关键字

   概述 : this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)

  方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量

  方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量   

标准类的代码编写和使用

/*
JavaBean类: 封装数据
*/
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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 void show() {
        System.out.println(name + "..." + age);
    }
}

public class TestStudent {
    public static void main(String[] args) {
        // 1. 无参数构造方法创建对象, 通过setXxx方法给成员变量进行赋值
        Student stu1 = new Student();
        stu1.setName("张三");
        stu1.setAge(23);
        stu1.show();
        // 2. 通过带参数构造方法, 直接给属性进行赋值
        Student stu2 = new Student("李四", 24);
        stu2.show();
    }
}

多态(polymorphism)

  多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。

  多态的要点:

    1. 多态是方法的多态,不是属性的多态(多态与属性无关)。

    2. 多态的存在要有 3 个必要条件:继承,方法重写,父类引用指向子类对象。

    3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。 

多态和类型转换

class Animal {
    public void shout() {
        System.out.println("叫了一声!");
    }
}

class Dog extends Animal {
    public void shout() {
        System.out.println("旺旺旺!");
    }

    public void seeDoor() {
        System.out.println("看门中....");
    }
}

class Cat extends Animal {
    public void shout() {
        System.out.println("喵喵喵喵!");
    }
}

public class TestPolym {
    public static void main(String[] args) {
        Animal a1 = new Cat(); // 向上可以自动转型
        // 传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。
        animalCry(a1);
        Animal a2 = new Dog();
        animalCry(a2);// a2 为编译类型,Dog 对象才是运行时类型。
        /*
         * 编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。
         * 否则通不过编译器的检查。
         */
        Dog dog = (Dog) a2;// 向下需要强制类型转换
        dog.seeDoor();
    }

    // 有了多态,只需要让增加的这个类继承 Animal 类就可以了。
    static void animalCry(Animal a) {
        a.shout();
    }
    /*
     * 如果没有多态,我们这里需要写很多重载的方法。
     * 每增加一种动物,就需要重载一种动物的喊叫方法。非常麻烦。
     * static void animalCry(Dog d) {
     * d.shout();
     * }
     * static void animalCry(Cat c) {
     * c.shout();
     * }
     */
}

我们可以看出多态的主要优势是提高了代码的可扩展性。但是多态也有弊端,就是无法调用子类特有的功能,比如,我不能使用父类的引用变量调用 Dog 类特有的seeDoor()方法。

  对象的转型(casting)

    1. 父类引用指向子类对象,我们称这个过程为向上转型属于自动类型转换

       转型的意思是:如把float类型转成int类型,把double类型转成float类型,把long类型转成int类型;,这些都叫转型。把一种形式转成另外一种形式就叫转型

    2. 向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型

    子类的对象可以当作基类的对象来使用称作 向上转型(upcasting)[即父类对象引用指向子类对象],反之称为向下转型(downcasting)

对象的转型  

public class TestCasting {
    public static void main(String[] args) {
        Object obj = new String("祖宗object"); // 向上可以自动转型
        // obj.charAt(0) 无法调用。编译器认为 obj 是 Object 类型而不是 String 类型
        /*
         * 编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。
         * 不然通不过编译器的检查。
         */
        String str = (String) obj; // 向下转型
        System.out.println(str.charAt(0)); // 位于 0 索引位置的字符
        System.out.println(obj == str); // true.他们俩运行时是同一个对象
    }
}

 实就是多态写法,相当于把子类当作父类来使用。格式:父类 对象 = new 子类()。

 向上转型一定是安全的,因为是从小范围转向大范围,相当于猫一定是动物,但动物不一定是猫。

posted @ 2022-09-18 22:08  link-零  阅读(194)  评论(0编辑  收藏  举报