面向对象的三大特性

## 三大特性

封装

一句话:属性私有化,get/set

在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。

封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。

要访问该类的代码和数据,必须通过严格的接口控制。

封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。

适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

优点

  1. 提高程序的安全性,保护数据。
  2. 隐藏信息,实现细节。
  3. 统一接口
  4. 减少耦合。

实现封装

  1. 修改属性的可见性来限制对属性的访问(一般限制为private), 只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
  2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问
public class Student {
    private int age;// 年龄
    private String name;// 名字
    
    // 提供一些public 的get、set方法。

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 100 || age < 0) {
            this.age = 3;
        } else {
            this.age = age;
        }
    }
}

采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。

public class Application {
    public static void main(String[] args) {
        Student student = new Student();
        //student.name; // private私有化之后,外部类无法访问该属性。
        student.setName("小明");
        student.setAge(18); // 年龄在内部处理逻辑
        // student.setAge(999);  //处理不合理的年龄
        String name = student.getName();
        int age = student.getAge();
        System.out.println(name + age +"岁啦");
    }
}

以上实例中public方法是外部类访问该类成员变量的入口。

通常情况下,这些方法被称为getter和setter方法。

因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。

继承

一句话:子类继承父类的特征和行为。

extends 是"扩展"的意思,子类是父类的扩展。

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

特性

  • 子类拥有父类非 private 的属性、方法。
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。(重写)
  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

关键字

继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,

当一个类没有继承的两个关键字,则默认继承object祖先类(这个类在 java.lang 包中,所以不需要 import)。

extends:在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

implements:可以同时继承多个接口(接口跟接口之间采用逗号分隔),使java具有多继承的特性。

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

super:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this:指向自己的引用。

final :声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:

区别 前提 构造方法
this 表示本身调用者这个对象 没有继承也可以使用 this()
super 代表父类对象的引用 只能在继承条件才可以使用 super()

注意点

  • super()调用父类的构造方法,必须在构造方法的第一个
  • this()调用自己的构造方法,必须在构造方法的第一个,所以this() super() 不能同时出现
  • super()只能出现在子类的方法或构造方法中!

继承格式

class 父类 {
}
 
class 子类 extends 父类 {
}

父类Person

/**
 * 隐式继承Object类
 */
public class Person {
    private String name;
    public void say() {
        System.out.println("说话说话");
    }
}

子类Student继承Person

public class Student extends Person {

}
public class Application {
    public static void main(String[] args) {
        Student student = new Student();
        student.say();
    }
}

子类继承了父类的公共方法和属性

继承类型

继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等

img

IDEA快捷键 ctrl + H 查看继承关系

方法重写

重写都是方法的重写,与属性无关

image-20200510214544325

重写:需要有继承关系,子类重写父类的非静态方法!

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符可以扩大但不能缩小 public >protected>default>private
  4. 抛出的异常范围:可以被缩小但不能扩大;

为什么需要重写?父类的功能,子类不一定需要,或者不一定满足。

多态

一句话:同一个方法可以根据发送对象的不同而采取多种不同的行为方式。

使用手机扫描二维码支付时,二维码并不知道客户是通过何种方式进行支付,只有通过二维码后才能判断是走哪种支付方式执行对应流程。

img

多态可以实现动态编译:类型在执行过程中才可以确定:增强可扩展性~

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。

前提条件

  1. 继承
  2. 重写
  3. 父类引用指向子类

特征

  • 对象能执行的方法和左边的类型有关,和右边的关系不大
  • 子类能调用的方法都是自己的或者继承父类的。
  • 父类型可以指向子类,但是不能调用子类独有的方法。
  • 父类想要执行子类的方法需要向下转型,高转低是强制转换!

优点

  1. 减耦合
  2. 增强可以替换性
  3. 可扩展性
  4. 灵活性等

关键词instanceof

instanceof:测试它左边的对象是否是它右边的类的实例

判断一个对象是什么类型,instanceof可以判断两个类之间是否存在父子关系

image-20200512092823693

public class Application {
    public static void main(String[] args) {
        // instanceof
        // 1.先判断左侧的引用类型与右边类是否有关系
        // 2.再判断左侧的实际类型与右边类是否有关系

        // 继承关系
        //      Object
        //      /   \
        //  String  Person
        //          /   \
        //     Student   Teacher

        // 公式 ClassA obj = new ClassB();
        // obj instanceof 任意类
        // 能够通过编译 取决于ClassA 与 任意类 有没有关系
        // true/false 取决于ClassB 与 任意类 有没有关系


        // 对象的引用类型是Student类
        Student obj1 = new Student();
        System.out.println("=======Student->Student==========");
        System.out.println(obj1 instanceof Student);
        System.out.println(obj1 instanceof Person);
        System.out.println(obj1 instanceof Object);
        // System.out.println(obj1 instanceof Teacher); // Student 和 Teacher 是兄弟关系,报错~
        // System.out.println(obj1 instanceof String); // Student 与 String无直接联系,报错!


        Person obj2 = new Student();
        System.out.println("=======Person->Student==========");
        System.out.println(obj2 instanceof Student);
        System.out.println(obj2 instanceof Person);
        System.out.println(obj2 instanceof Object);
        System.out.println(obj2 instanceof Teacher);
        // obj2 指向的是Student类,与Teacher无直接联系
        //System.out.println(obj2 instanceof String); // Person 与 String无直接联系,报错!

        Object obj3 = new Student();
        System.out.println("=======Object->Student==========");
        System.out.println(obj3 instanceof Student);
        System.out.println(obj3 instanceof Person);
        System.out.println(obj3 instanceof Object);
        System.out.println(obj3 instanceof Teacher);
        System.out.println(obj3 instanceof String);

        Person obj4 = new Person();
        System.out.println("=======Person->Person==========");
        //
        System.out.println(obj4 instanceof Student);
        System.out.println(obj4 instanceof Person);
        System.out.println(obj4 instanceof Object);
        System.out.println(obj4 instanceof Teacher);
        // System.out.println(obj4 instanceof String);
        // Person 与 String无直接联系,报错!
    }
}

类型转换

基础类型转换

  • 低 ------>高 隐式转换
  • 高------->低 强制转换

引用类型转换

  • 低(子类)----->高(父类)

  • 低转高:Person obj1 = new Student(); 即向上转换,不需要强制转换

  • 高转低 Student obj2 = (Student)obj1; 即向下转换,需要强制转换

注意:之前学习继承的时候说,子类是父类的扩展,所以子类转换成父类时会丢失一些本身的方法或属性。

父类型可以指向子类,但是不能调用子类独有的方法。因为子类独有的方法丢失了,无法调用。

多态小结

  • 多态是方法的多态,属性没有多态

  • 父类和子类

    • 有联系。instanceof
    • 类型转换异常!ClassCastException!
    • 子类转换成父类,向上转型,丢失方法
    • 父类转换成子类,向下转型 (强制转换)
  • 存在条件

    • 继承关系
    • 方法需要重写
    • 父类引用指向子类对象!
  • 不能重写的方法

    • static 静态方法属于类,不属于实例
    • final常量修饰不能继承
    • private 方法私有

OOP面向对象的编程思想就是抽象

[代码区](# 多态代码)

多态的表现

多态代码

第一种情况 对象的实际类型确定,指向的引用类型不确定(可以用父类引用类型指向子类对象)

如下有Person类

Person.java

public class Person {
    public void say() {
        System.out.println("father---say");
    }
}

Student.java

// 继承 Person 类
public class Student extends Person {
}

有继承关系才可以指向

Application.java

/**
 * 测试多态的表现
 */
public class Application {
    public static void main(String[] args) {
        // 一个对象的实际类型是确定的
        // new Student(); // 创建出一个学生
        // new Person();  // 创建出一个人

        // 可以指向的引用类型就不确定了
        Student s1 = new Student(); // new 一个学生 名字叫 学生
        Person s2 = new Student();// new 一个学生 名字叫 人(学生也是人,他继承了人)
        Object s3 = new Student();
        s2.say();
    }
}

运行结果

father---say

子类能调用的方法都是自己的或者继承父类的。如果子类中没有say方法,则调用继承于父类的say方法。

如果在子类重写了父类的say方法,则调用本身的say方法。如下代码重写了父类的say方法:

Student.java

public class Student extends Person {
    @Override
    public void say() { // 重写父类say方法
        System.out.println("son ---- say");
    }
}

Application.java

/**
 * 测试多态的表现
 */
public class Application {
    public static void main(String[] args) {
        // 一个对象的实际类型是确定的
        // new Student(); // 创建出一个学生
        // new Person();  // 创建出一个人

        // 可以指向的引用类型就不确定了
        Student s1 = new Student(); // new 一个学生 由 学生
        Person s2 = new Student();// new 一个学生 由人接收(学生也是人,他继承了人)
        Object s3 = new Student();
        s1.say();// 学生 说
        s2.say();// 学生 说
        
    }
}

运行结果

son ---- say
son ---- say

因为实例是确定的,s1,s2都是new 出来的Student()实例对象,方法的功能与实例的实现有关。

如果在Student类中添加一个eat()方法

public class Student extends Person {
    @Override
    public void say() {
        System.out.println("son ---- say");
    }
    
    public void eat() {
        System.out.println("son ---- eat");
    }
}

public class Application {
    public static void main(String[] args) {

        Student s1 = new Student();
        Person s2 = new Student();

        //对象能执行的方法和左边的类型有关,和右边的关系不大
        s1.say();// 学生 说
        s2.say();// 学生 说

        s1.eat(); // s1 左边是学生类,学生类里面有say方法。成功调用
        //s2.eat(); // s2 左边是人类,人类中没有eat方法,又不能继承,所以调用失败
    }
}

对象能执行的方法和左边的类型有关,涉及到类型转换,高-->低 会丢失精度。比如

public class Test {
    public static void main(String[] args) {
        float a = 1.5f;
        long b = 99999999999999L;
        System.out.println((int)a); // 结果打印1 丢失了小数部分
        System.out.println((int)b); // 结果打印276447231  丢失数据溢出部分
    }
}

但在学习继承的时候知道,子类继承父类获取父类的非私有属性和方法,子类是父类的扩展。

这里引用类型和基本类型的转换是存在区别的,基本类型高精度转型为低精度会丢失数据。而引用类型子类转型为父类会丢失方法或属性。

posted @ 2020-05-13 15:44  小橘子ღ  阅读(342)  评论(0编辑  收藏  举报