面向对象08-12:封装、继承、多态

本文围绕Java面向对象编程展开,详细介绍了封装、继承和多态。封装通过访问控制符隐藏类信息,提高安全性;继承使子类拥有父类特征和行为,Java支持单继承和多重继承;多态是方法的多态,需继承、方法重写和父类引用指向子类对象三个条件。

封装

参考博文

概念
  • 将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。
作用
  • 直接通过操控类对象,不需要对具体实现十分了解,使类属性和方法的具体实现对外不可见。不但方便还起到了保护作用。
其他
  • 该露的露,该藏的藏
    • 程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外界使用。
  • 封装(数据的隐藏)
    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口访问,这称为信息隐藏。
    • 封装主要封装类的属性,一般不封装类的方法
    • 封装的基本步骤:1. 修改类的属性的可见性(设为private)2. 创建getter/setter方法(用于对类中私有化的属性进行读写)3. 在getter/setter方法中加入属性控制语句(对属性值的合理性进行判断)
封装的实现----使用访问控制符

Java是使用“访问修饰符”来控制那些细节需要封装,那些细节需要暴露的。
Java中4种“访问控制符”分别为private、default(默认)、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。
参考博文
在这里插入图片描述private:表示私有,只有自己类能访问。
default:表示没有修饰符,只有同一个包的类能访问
protected:表示可以被同一个包的类以及其他包中的子类访问
public:表示可以被该项目的所有包中的所有类访问

封装使用细节

类的属性的处理:

  1. 一般使用private访问权限。
  2. 提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意boolean变量的get方法是is开头)。
  3. 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
    重点:属性私有,然后通过调用get、set方法对属性进行更改
package com.oop.demo03;
//类
public class Student {
    //属性私有
    private String name;//名字
    private int id;//学号
    private char sex;//性别
    private int age;//年龄

    //提供一些可以操作这个属性的方法!
    //提供一public的get、set方法

    //get获得这个数据
    public String getName(){
        return this.name;
    }
    //set给这个数据设置值
    public void setName(String name){
        this.name=name;
    }
    
    //alt+insert自动生成get、set方法
    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age>120||age<0){//不合法
            this.age = 3;
        }else{
            this.age = age;
        }

    }
}
package com.oop.demo03;

public class Application {
    public static void main(String[] args){
        Student s1 = new Student();

        System.out.println(s1.getName());
        s1.setName("潘潘");

        System.out.println(s1.getName());

        s1.setAge(-1);//不合法
        System.out.println(s1.getAge());
    }
}

运行结果:

null
潘潘
3

继承

参考博文
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
所有类都继承于java.lang.Object,当一个没有继承的关键字,则默认继承object祖先类。

类的继承格式

Java中通过extends关键字可以申明一个类是从另一个类继承而来的。

class 父类{
}

class 子类 extends 父类{
}
继承的类型

注意Java只有单继承,没有多继承,但支持多重继承。
在这里插入图片描述

继承的特性
  • 父类也成为超类、基类、派生类等
  • Java中只有单继承,没有像C++那样的多继承,多继承会引起混乱,使得继承链过于复杂,系统难以维护。
  • Java中类没有多继承,接口有多继承
  • 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见的可以直接访问(如父类私有的属性和方法)。
  • 如果定义一个类时,没有调用extends,则它的父类是Java.lang.Object
  • 子类拥有父类的非private的属性、方法
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法,即对父类方法进行重写。
  • Java的继承是单继承,但是可以多重继承。多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类。
  • 提高了类之间的耦合性(继承的特点,耦合性高就会造成代码之间的联系越紧密,代码独立性越差)
继承关键字
  • extends关键字
    在Java中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
public class Animal { 
    private String name;   
    private int id; 
    public Animal(String myName, String myid) { 
        //初始化属性值
    } 
    public void eat() {  //吃东西方法的具体实现  } 
    public void sleep() { //睡觉方法的具体实现  } 
} 
 
public class Penguin  extends  Animal{ 
}
  • implements关键字
    使用implements关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}
Object类

Object类是所有Java类的根基类,也就意味着所有Java对象都拥有Object类的属性和方法。

super与this

super关键字:可以通过super关键字来访问父类中(被子类覆盖)的方法和属性,用来引用当前对象的父类。
注:子类是不继承父类的构造方法的,它只是调用(显示或隐式)。如果父类的构造器带有参数,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类没有显示的构造方法,则在子类的构造方法中不需要使用super关键字调用父类的构造方法,系统会自动调用父类的无参构造方法。

  • 使用super调用普通方法,语句没有位置限制,可以在子类中随便使用。
  • 若是类的构造方法中的第一行代码没有显示的使用super(…)或this(…)调用父类或本类自身构造方法,那么Java会默认调用super(),含义是调用其父类的无参构造方法,这里super()可以省略。
  • 所有类的构造方法中的第一句代码都是super()(不管显示还是隐式)来调用父类对应的构造方法。
    this关键字:指向本类,用来引用当前对象。
package com.oop.demo04;
//父类
public class Person {
   protected String name = "panpan";

    public Person() {
        System.out.println("执行Person父类无参构造方法");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("执行Person父类有参构造方法");
    }

    public void print(){
       System.out.println("Person");
   }
}
package com.oop.demo04;

//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person{
    private String name = "ouhong";

    public Student() {
        //隐藏代码:隐式调用了父类的无参构造方法,super()
        //若显示调用父类构造方法,必须将super(...)或super放在第一句
        System.out.println("执行student子类构造方法");
    }

    public void print(){
        System.out.println("student");
    }

    public void test(){

        //不建议采用此格式调用本类属性或方法
        System.out.println(name);//调用本类属性,与this.name效果相同
        System.out.println(this.name);//调用本类属性,建议使用的格式
        System.out.println(super.name);//调用父类属性
    }
    public void test1(){
        print();//student,调用子类方法
        this.print();//student,调用子类方法
        super.print();//Person,调用父类方法
    }
}
package com.oop;

import com.oop.demo04.Student;

public class Application {
    public static void main(String[] args){
        Student student = new Student();
        System.out.println();
        student.test();
        System.out.println();
        student.test1();
    }

}

运行结果:

执行Person父类无参构造方法
执行student子类构造方法

ouhong
ouhong
panpan

student
student
Person
final关键字

final关键字的作用:

  1. 修饰变量:被他修饰的变量,一旦赋初值了,就不能再重新赋值。
final int MAX_NUM = 120;
  1. 修饰方法:该方法不可被子类重写,但是可以被重载。
final void study(){
}
  1. 修饰类:修饰的类不能被继承,比如:Math、String等。
final class A{
}

多态

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

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

父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。若想要调用它运行时类型的方法,这时,我们就需要进行类型的强制转换,我们称之为向下转型。

package com.oop.demo05;

//重写都是方法的重写,和属性无关
public class Parent {
    public void test(){
        System.out.println("Parent-->test()");
    }
}

package com.oop.demo05;

public class Child extends Parent{
    //Override  重写
    @Override//有功能的注解
    public void test() {
        System.out.println("Child-->test()");
    }
    public void eat(){
        System.out.println("eat()");
    }
}

package com.oop;

import com.oop.demo04.Student;
import com.oop.demo05.Child;
import com.oop.demo05.Parent;

public class Application {
    public static void main(String[] args){
    	//Child子类能调用的方法都是自己的或者继承父类的
        Child c = new Child();
        c.test();

        System.out.println();
        //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
        //即若子类重写了父类的该方法,则调用子类的该方法;若子类没重写该方法,则调用父类的该方法。
        //父类调用指向子类
        Parent p = new Child();//子类重写了父类的方法,执行子类的方法
        p.test();
        //p.eat();//报错,Parent父类,可以指向子类,但是不能调用子类独有的方法
        ((Child) p).eat();//向下转型

    }

}

运行结果:

Child-->test()

Child-->test()
eat()
方法的重写

参考博文
重写与重载参考博文

  • 子类通过重写父类的方法,可以用自身的行为替换父亲的行为。
  • 当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
  • 要想调用父类中被重写的方法,则必须使用关键字 super。

重写的条件:
参考博文
方法的重写需要符合下面的三个要点:

  1. 重写只跟非静态方法(成员方法)有关,与静态方法无关。静态方法和非静态方法不一样,在静态方法中,方法的调用只和左边声明的对象类型有关,而与右边无关,是哪个类型,就调用对应的方法。即父类引用指向子类,无论子类中是否有与父类相同的方法,都只调用父类的方法。
  2. 声明为static的方法不能被重写,但是能够再次声明。
  3. 子类和父类在同一个包中时,子类可以重写父类除了声明为 private 和 final 方法的其他方法。
  4. 子类和父类不在同一个包中时,子类只能重写父类的声明为 public 和 protected 的非 final 方法。
  5. 方法名、形参列表必须相同,只是方法体不同。
  6. 返回值类型和声明异常类型范围,子类小于等于父类。
  7. 访问权限,子类大于等于父类,public>protected>default>private
package com.oop.demo05;

public class Employee {
    private String name;
    private String address;
    private int number;
    public Employee(String name, String address, int number) {
        System.out.println("Employee 构造函数");
        this.name = name;
        this.address = address;
        this.number = number;
    }
    public void mailCheck() {
        System.out.println("邮寄支票给: " + this.name
                + " " + this.address);
    }
    public String toString() {
        return name + " " + address + " " + number;
    }
    public String getName() {
        return name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String newAddress) {
        address = newAddress;
    }
    public int getNumber() {
        return number;
    }
}
package com.oop.demo05;

public class Salary extends Employee{
    private double salary; // 全年工资
    public Salary(String name, String address, int number, double salary) {
        super(name, address, number);
        setSalary(salary);
    }
    public void mailCheck() {
        System.out.println("Salary 类的 mailCheck 方法 ");
        System.out.println("邮寄支票给:" + getName()
                + " ,工资为:" + salary);
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double newSalary) {
        if(newSalary >= 0.0) {
            salary = newSalary;
        }
    }
    public double computePay() {
        System.out.println("计算工资,付给:" + getName());
        return salary/52;
    }
}
package com.oop;


import com.oop.demo05.Employee;
import com.oop.demo05.Salary;

public class Application {
    public static void main(String[] args){
        Salary s = new Salary("员工 A", "北京", 3, 3600.00);
        Employee e = new Salary("员工 B", "上海", 2, 2400.00);
        System.out.println("使用 Salary 的引用调用 mailCheck -- ");
        s.mailCheck();
        System.out.println("\n使用 Employee 的引用调用 mailCheck--");
        e.mailCheck();

    }

}

运行结果:

Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck -- 
Salary 类的 mailCheck 方法 
邮寄支票给:员工 A ,工资为:3600.0

使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法 
邮寄支票给:员工 B ,工资为:2400.0

例子解析:

  • 实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
  • 当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。
  • e 是 Employee 的引用,但引用 e 最终运行的是 Salary 类的 mailCheck() 方法。
  • 在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。
多态的实现方式
  1. 方法的重写
  2. 接口(还未整理)
  3. 抽象类和抽象方法(还未整理)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值