封装继承多态

封装,继承和多态

封装

概念:

封装就是把抽象出来的数据(属性)和对数据的操作(方法)封装在一起,数据封装在方法内部,我们只需要调用方法就能对数据进行操作

例子:电视剧,开机关机,调音量,我们只要按按钮(方法)就能操作,内部的复杂操作就被封装起来了

好处:

  1. 隐藏实现细节 :方法(连接数据库)《== 调用 (传入参数)
  2. 对数据进行验证,在方法内部

实现步骤:

  1. 属性进行私有化private(不能直接修改属性)
  2. 提供一个public公共的set的方法,设置时内部可以验证数据
  3. 提供一个public公共的get方法用来获取数据

注意;在构造器传参会绕过我们的set里的验证,怎么解决呢,把set放进构造器就行了

继承:

为什么需要继承(解决了代码复用):

当有一个男人和女人两个类,男人和女人都有吃喝拉撒睡,思考等等方法,那我们写两个是不是太麻烦

关系图:

1641620137716

细节:

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以直接访问但是私有的属性和方法不能直接访问,可以通过公共方法调用(委托

    父类:   
    private void fa(){
            System.out.println("私有方法");
        }
        public void getfa(){
            fa();
        }  
    子类:
    public void a(){
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);
    //        System.out.println(d);//私有的属性不能直接调用,这个时候我们获取get方法调用
    //        fa();//调用私有方法调用不了,这个时候我们把私有方法丢到公共方法里,调用公共方法
            getfa();
        }
    
  2. 子类必须先调用父类的无参构造器默认父类无参构造器必须写在子类构造器第一行),完成对父类的初始化,无论哪个子类构造器,都会先调用父类的无参构造器,如果父类没有无参构造器,必须调用指定的父类构造器,不然编译错误

  3. 父类的构造器必须写在子类构造器第一行,很好理解,你要想拿到父类的数据,肯定得先加载父类

  4. super()和this()都只能放在构造器第一行,因此不能共存,可以这样理解,this()构造器里也会有super()构造器,所以会造成编译不通过

  5. Object类是所有类得父类

  6. 父类构造器的调用不限于直接父类,可以是父类的父类,因为父类的构造器第一行也会执行父类的父类的构造器有点像递归最后回溯回来,所以你可以调用到最顶级父类的东西,例如Object

  7. 只能有一个继承(java单继承机制

  8. 不能滥用继承,必须是is -a的关系

继承本质分析:

1641625325019

class GrandPa{
    int age=70;
    String name="yeye";
}

class Father1 extends GrandPa{
    int age=40;
    String hobby="打球";
}

class Son1 extends Father1{
    int age=17;
    String SmallName="xc";
    private String hobby;
}

class test1{
    public static void main(String[] args) {
        Son1 son1 = new Son1();
        System.out.println(son1.age);//首先会调用到本类的属性,要是没有往上找,直到找到Object还没有就爆错
        // 为什么这样,因为先加载父类,后加载子类,所以就被子类覆盖
        System.out.println(son1.name);
        System.out.println(son1.SmallName);
//        如果再调用的时候先调到子类有一个私有的hobby(调用不到),父类有一个hobby,那么会调用到父类的吗
//        不会,先找到子类的,其实已经找到了,但是权限不够
//        System.out.println(son1.hobby);
    } 

例题:

1641627082334

Super关键字:

代表父类的引用,跟this一样的作用(本类),

  1. 可以调用方法和属性(不能调用私有的),构造器(必须再构造器第一行)
  2. Super不限于父类,爷爷类以上都可以查找

1641628037611

方法重写:

  1. 子类方法的返回类型(或者是父类的子类类型)和参数要和父类完全一样
  2. 子类不能缩小父类方法的访问权限

class Father1{
    Father1 r;
    Father1 a(){
       return r;
   }
}

class Son1 extends Father1{
    Son1 s;
    public Son1 a(){ //子类的重写类型可以是父类类型的子类,并且子类的权限不能缩小父类权限
      return s;
    }

多态:

提高代码的复用性,提高维护性

什么是多态?

使方法和对象具有多种形态,是建立在封装和继承的继承上的

多态的具体体现:

  • 方法的多态:
    1. 方法重载(根据行参个数不同调用不同的方法)
    2. 方法重写(根据对象不同调用了不同的方法)都属于多态
  • 对象的多态(核心):
    1. 父类的引用指向子类的对象 比如 :Father f=new Son(); son才是对象,Father只是引用,所以f的编译类型是Father,运行类型是Son
    2. 编译类型在定义对象时,一旦确定就不能改变
    3. 运行类型是可以变化的
    4. 编译类型看=左边,运行类型看右边

  public static void main(String[] args) {
        Master master = new Master();
        master.set(new Dog(),new Fish());//多态的实用
        master.set(new Cat(),new Fish());
    }
    public void set(Animal a,Food f){	//传入父类,可以使用任何他的子类
        System.out.println("主人让"+a.eat()+f.getName());
    }

细节:

  1. 向上转型:(父类的引用指向子类的对象)多态
  2. father f=new son();f可以调用父类father里的所有可以访问的属性和方法,但是不能调用子类son的特有方法和属性,因为在编译阶段,能调用哪些类型是由编译类型决定
  3. 最终的运行效果看子类son的具体实现,因为真正运行的是Son对象,调用方法还是跟前面继承时调用方法顺序一样,先在子类找,没找到向父类上找
  4. 向下转型:子类类型 引用名=(子类类型)父类引用,只能强转父类的引用,不能强转父类类型
  5. 向下转型前,一定之前进行过向下转型(分清楚编译类型和运行类型,当你没有向上转型而是创建一个普通的Animal对象,编译类型和运行类型都是Animal,你这个时候向下转型,就变成了子类的引用指向父类的对象,就报错了)
  6. 向下转型就可以调用到子类的所有可访问成员
  7. 向下转型,之前如果进行了向上转型,那么之前运行类型为子类,内存中的是子类,现在向下转型将父类转成子类,只是转的编译类型,真正还是指向内存中的子类
  8. 属性不重写,编译是哪个类型就调用哪个类型
  9. instanceof(比较xx运行类型是不是xx类型或者子类) aa instanceof AA 比较这个对象aa的运行类型是不是AA的子类
   public static void main(String[] args) {
        Animal a=new Cat();//向上转型
        Cat c=(Cat)a;      //向下转型
        Animal a2=new Animal();
//        Cat c2=(Cat)a2;     //没有此时是Cat的编译类型指向Animal的运行类型,运行会报错
        System.out.println(c.eat());
        A1 a1 = new B1();
        System.out.println(a1.a); //向上转型调属性是编译类型的属性
        B1 b1 = new B1();
        if (b1 instanceof A1){      //比较b1的运行类型是不是A1或者是我的子类
            System.out.println(true);
        }
    }

动态绑定机制(重要):

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性的时候,没有动态绑定机制,哪里声明,哪里使用
public class One {
    public int i=10;
    public int sum(){
        return getI()+10;      //1.这里的getI()方法子类和父类都有,
        // 这里就有动态绑定机制里说的,此时调用sum的o对象运行类型是Two,所以这个getI用的Two类里的
    }
    public int sum1(){         //2.属性是没有动态绑定机制的,属性看编译类型所以就用的是One里的i
        return i+10;
    }
    public int getI(){
        return i;
    }
}
class Two extends One{
    public int i=20;
//    public int sum(){
//        return i+20;              //当没有这个sum时,会去父类找
//    }
    public int getI(){
        return i;
    }
//    public int sum1(){
//        return i+10;             //当没有这个sum1,去父类
//    }
}
class test{
    public static void main(String[] args) {
        One o=new Two();
        System.out.println(o.sum());
        System.out.println(o.sum1());
    }
}

多态的应用:

多态数组:

    public static void main(String[] args) {
        Person p[]=new Person[4];
        p[0]=new Student(88,"小刘");
        p[1]=new Student(99,"小李");
        p[2]=new Teather(99,"刘老师");
        p[3]=new Teather(99,"李老师");
        for (Person p1:p){
            if (p1 instanceof Teather){
                Teather t=(Teather) p1;
                t.tech();
            }else{
                Student s=(Student) p1;
                s.study();
            }
        }
    }

多态参数:

class son1 extends Father{
    void eat();
}
class son2 extends Father{
    void eat();//重写方法
}
class Father{
    void eat();//重写方法
}
class Test{
    main{
   Father f=new Father();
        f.play(new son1());//调用的是son1的eat方法
    }
    public void play(Father f){
        sout(f.eat())
    }
}

总结:

  • 为什么要封装?

    • 封装成方法,提高代码复用性
    • 隐藏实现细节
  • 封装的步骤

    • 私有化属性
    • 提供set,get方法,set方法里可做属性校验
  • 为什么需要继承?

    • 提高复用性
    • 例如我们写狗和猫的类,都有吃喝睡,那么写两个几乎相同类就会使代码复用性降低
  • 继承的特点

    • 继承后不能访问的私有化方法和属性,其实这些方法和属性也被继承了,只是由于权限问题不能访问,我们可以通过委托的方式,把他们放到公共修饰的方法里访问到
    • 继承是继承
    • 继承后,子类将能得到父类的的属性和方法,但是只能调用可访问的
    • 继承要注意构造器,继承后,子类的构造器第一行是无参父类构造器super()(一般时候是隐藏不显示),先执行父类的构造器,当父类中没有无参构造器,需要我们在子类构造器第一行,写入父类的有参构造器
    • this()和super()都必须在构造器第一行,所以两个只能有一个,不能同时存在
  • 继承的本质

    • 也就是继承后,访问方法啊属性啊的顺序,当我们实例化子类对象,或者this.(也可以说是自己写方法()或者属性,因为默认有this,反正只要表示的是当前子类的)首先先在子类中确认有没有这个方法和属性,没有再去父类中找
    • super跟这个正好相反,super会直接去父类找
  • 为什么需要多态?

    • 提高复用性
    • 多态是基于封装和继承的
    • 例如有个父类动物类,和两个实现了父类的狗类和猫类,父类中有一个eat方法,两个子类也都重写了这个方法,如果没有多态,我们就会去实例化两个子类,分别调用各自的eat方法,这样的化也会使降低了复用性,所以就有了多态,在测试类中创建一个方法,参数为动物类,方法体实现了动物类的eat,我们只需要调用这个方法,根据传入的参数,传入cat就是cat的eat,传dog就是dog的eat
  • 多态有哪些特点

    • 父类的引用指向子类的对象
    • =左边是编译类型,右边是运行类型,编译类型一旦确定就不能改变,而运行类型不会
    • 使用多态调用方法时,使用的是运行类型的方法调用属性时使用的是编译时的属性多态也就是向上转型
    • 那么当我们子类里有于父类不同,特有的方法,编译器是按照编译类型调方法的,此时调不到子类的方法,所以就有了向下转型 ==》子类的类型 子类引用=子类类型 父类的引用
    • 记住向下转型之前要有向上转型,不然如果你直接把普通对象父类引用转成子类会报错,因为此时父类的运行类型是父类,你将父类转成子类怎么可能
    • instanceof的使用,x1 instanceof x2判断x2是不是x1的类或者父类
  • 动态绑定机制

    • 动态绑定机制其实就是,调用方法时,就会把内存绑定到运行时类型,调用方法调的是运行类型的类
    • 调用属性,没有动态绑定机制,属性看编译类型,是哪个类编译就用编译类型的属性
  • 多态应用:常用的就是上面例子的多态参数,还有多态数组

  • 三大特性其实都为了提高代码复用性

posted @   又菜又ai  阅读(999)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示