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

封装

参考博文

概念
  • 将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。
作用
  • 直接通过操控类对象,不需要对具体实现十分了解,使类属性和方法的具体实现对外不可见。不但方便还起到了保护作用。
其他
  • 该露的露,该藏的藏
    • 程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外界使用。
  • 封装(数据的隐藏)
    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口访问,这称为信息隐藏。
    • 封装主要封装类的属性,一般不封装类的方法
    • 封装的基本步骤: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. 抽象类和抽象方法(还未整理)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值