面向对象(中)

封装
  1. 理解封装:指将对象的状态信息隐藏在对象内部,不允许外部访问内部信息,而是通过提供的方法实现对内部信息的操作与访问

  2. 访问控制符:由左向右访问控制级别依次变大

    private—》default—》protected—》public

    private:当前类访问权限

    default:包访问权限

    protected:子类访问权限

    public:公共访问权限

    当不使用任何访问控制符来修饰类和类成员时,系统默认使用default修饰符

  3. 示例:

public class Student {
    //属性私有
    private String name;
    private int id;
    private char sex;

    //提供可以操作的方法,public的get和set方法
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        //合理性校验,用户名不得超过6个字
        if(name.length()>6 || name.length()<2){
            System.out.println("您设置的用户名不符合规范");
        }else {
            this.name = name;
        }
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        //同样规范设置
        if(id!=8){
            System.out.println("id不符合规范");
            return;
        }else {
            this.id = id;
        }
    }
}
继承(extends)

extends实际意思是扩展:子类是对父类的扩展,子类是一种特殊的父类,Java的子类不能获得父类的构造器

Java只能有一个直接父类,有多个间接父类

class Fruit extends Plant{…}

class Apple extends Fruit{…}

方法重写
public class Bird {
    public void fly(){
        System.out.println("我是一只鸟,可以飞");
    }
}
public class Ostrich extends Bird{
    public void fly(){
        System.out.println("我是鸵鸟不能飞");
    }

    public static void main(String[] args) {
        Ostrich os = new Ostrich();
        os.fly();
    }
}

在main方法中os.fly执行的不再是Bird类的fly方法,而是Ostrich中的,这种子类中包含父类同名方法的现象为方法重写(Override),也叫方法覆盖

super限定
  1. 如果需要在子类中调用父类被覆盖的方法,可使用super限定来调用实例方法,父类类名调用类方法
  2. 如果子类没有包含和父类同名的成员变量,则无需显式使用super或父类名作为调用者
public class Bird {
    public void fly(){
        System.out.println("我是一只鸟,可以飞");
    }
}
public class Ostrich extends Bird{
    public void fly(){
        System.out.println("我是鸵鸟不能飞");
    }
    public void OverrideMethod(){
        super.fly();
    }
    public static void main(String[] args) {
        Ostrich os = new Ostrich();
        os.fly();
        os.OverrideMethod();
    }
}
调用父类构造器

子类不会获得父类构造器,但可以调用父类构造器里的初始化代码

在一个构造器中调用另一个重载的构造器使用this,在子类构造器中调用父类构造器使用super

public class Base {
    public double size;
    public String name;

    public Base(double size,String name) {
        this.size = size;
        this.name = name;
    }
}
public class Sub extends Base{
    public String color;

    public Sub(double size, String name, String color) {
        super(size, name);
        this.color = color;
    }

    public static void main(String[] args) {
        Sub s = new Sub(2,"ljh","blue");
        System.out.println(s.size+s.name+s.color);
    }
}

super调用的父类构造器也必须出现在子类构造器的第一行,所以this和super不会同时出现

不管是否适用super调用父类构造器,子类构造器总会调用父类构造器一次:

  • 使用super显式调用

  • 子类使用this显式调用本类中重载的构造器,系统在执行本类中另一个构造器时即会调用父类构造器

  • 子类既无this也无super,系统在执行子类构造器之前会隐式调用父类无参构造器

    不仅如此,执行父类构造器时,系统会再次上溯执行父类构造器,以此类推,创建任何java对象,最先执行的都是java.lang.Object类的构造器

多态
向上转型
public class Base {
    public int book = 6;
    public void base() {
        System.out.println("父类的普通方法");
    }
    public void test(){
        System.out.println("父类被覆盖的方法");
    }
}
public class Sub extends Base{
    public String book = "Java讲义";
    public void test(){
        System.out.println("覆盖父类的方法");
    }
    public void sub(){
        System.out.println("子类普通方法");
    }

    public static void main(String[] args) {
        Base b = new Base();
        System.out.println(b.book);
        b.base();
        b.test();
        Sub s = new Sub();
        System.out.println(s.book);
        s.sub();
        s.test();
        Base bs = new Sub();
        System.out.println(bs.book);
        bs.test();
        bs.base();
    }
}
  • Java允许把一个子类对象直接赋给一个父类变量,无需类型转换,称之为向上转型
  • 编译时 = 运行时
  • 方法具有多态性,而实例不具备多态性不分编译和运行时,什么类型的变量就是什么类型变量的值
  • 引用变量在编译阶段只能调用其编译类型所具有的方法
向下转型

在进行强制类型转换之前,先用instanceof运算符判断是否可以成功转换避免ClassCastException

public class Test {
    public static void main(String[] args) {
        Person person = new Student();
        person.run();
        //person.go();
        //person无法调用go方法,因为调用时只能调用编译时类型具有的方法,若想调用运行是类型的方法需要强制转换
        if(person instanceof Student) {
            Student person1 = (Student) person;
            person1.go();
        }
    }

}
组合

为保证父类良好的封装性,通常有如下规则:

  • 尽量隐藏父类内部数据
  • 不要让子类随意访问,修改父类中的方法
  • 尽量不要在父类构造器中调用即将要被子类重写的方法
public class Base {
    public Base() {
        test();
    }
    public void test(){
        System.out.println("将被子类重写的方法");
    }
}
package com.ljh.oop;

public class Sub extends Base{
   private String name;
   public void test(){
       System.out.println("子类重写父类的方法,"+"其name字符串长度为:"+name.length());
   }

    public static void main(String[] args) {
        Sub sub = new Sub();
    }
}

上述代码会造成空指针异常!

当系统试图创建Sub对象时,同样会先访问父类构造器,如果父类构造器调用了被其子类重写的方法,则会变成调用重写后的方法。

到底何时需要父类派生新类呢?

  • 子类需要额外增加属性,而不仅仅是属性值的修改
  • 子类需要增加独有的行为方式

如果时出于类复用的目的,可以使用组合来完成

public class Animal {
    private void beat(){
        System.out.println("心脏跳动");
    }
    public void breath(){
        beat();
        System.out.println("呼吸中");
    }
}
public class Bird {
    //将父类组合到子类,作为子类的一部分
    private Animal animal;
    public Bird(Animal animal){
        this.animal = animal;
    }
    //重新定义一个子类的breath方法
    public void breath(){
        //直接复用Animal中的breath方法来实现Bird中的breath方法
        animal.breath();
    }
    public void fly(){
        System.out.println("我是鸟,我会飞");
    }
}
public class Wolf {
    //组合关键
    private Animal animal;
    //Wolf类的带参构造
    public Wolf(Animal animal){
        this.animal = animal;
    }
    //子类自己的breath方法
    public void breath(){
        animal.breath();
    }
    public void run(){
        System.out.println("我是狼,我会跑");
    }

    public static void main(String[] args) {
        //需要显式创建被组合的对象
        Animal animal = new Animal();
        Bird bird = new Bird(animal);
        bird.breath();
        bird.fly();
        Animal animal1 = new Animal();
        Wolf wolf = new Wolf(animal1);
        wolf.breath();
        wolf.run();
    }
}

继承要表达的是一种(is a)的关系,组合要表达的是(has a)的关系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值