java封装、继承和多态

面向对象变成三大特征:封装、继承和多态

封装(encapsulation)

封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。(把属性和方法封装)
eg:打开电视,调节音量(内部很复杂),对电视机的操作(用户很简单)就是封装
封装的理解和好处

  1. 隐藏实现细节 : 方法(连接数据库)<--调用(传入参数..)
  2. 可以对数据进行验证,保证安全合理

封装的实现步骤(三步)

  1. 将属性进行私有化private【不能直接修改属性】(隐藏细节)
  2. 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型参数名){//Xxx表示某个属性
	//加入数据验证的业务逻辑
	属性=参数名;
}
  1. 提供一个公共的(public)get方法,用于获取属性的值
public 数据类型 getXxx(){//权限判断,Xxx某个属性
	return xx;
}

例子:
//我们可以将set写在构造器中,可以起到防护机制

package encap;

public class Encap01 {
    public static void main(String[] args) {
        Person p1=new Person();
    p1.setName("xx");
    p1.setAge(2000);
    p1.setSalary(2000000);
    System.out.println(p1.info());
    //如果直接使用构造器指定属性,set失效了
    new Person("smith",200,500000);

    }
}

class Person{
    public String name;//名字公开
    private int age ;//年龄和薪水私有
    private double salary;
    public Person(){

    }
    public Person(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        //我们可以将set写在构造器中,可以起到防护机制
        setName(name);
        setAge(age);
        setSalary(salary);
    }
    


    
    public String getName() {
        return name;
    }
    //封装过程
    //可以直接快捷键生成getter & setter
    public void setName(String name) {
        //加入数据校验,增加业务逻辑
        if(name.length()>=2 && name.length()<=6){
            this.name = name;
        }else{
            System.out.println("name set error.set default");
            this.name="无名";
        }
        
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if(age>=1 && age<=120){
            this.age = age;
        }else{
            System.out.println("年龄设置错误,需要在(1-120),给默认年龄18");
            this.age=18;
        }
        
    }
    public double getSalary() {
        //可以增加对当前对象的权限范围
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    ///写一个方法返回属性信息
    public String info(){
        return "name:"+name+"  sarlary:"+salary;
    }
    

}

继承

两个类的属性和方法有很多相同==>继承
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
示意图:
image

继承基本语法:

class 子类 extends 父类{

}
  1. 子类就会自动拥有父类定义的属性和方法
  2. 父类又叫超类,基类。
  3. 子类又叫派生类。
package extend.improve;

//父类,Pupil FUFEI
public class Student {
    //公有方法:
    public String name;
    public int age;
    private double score;
    public void setScore(double score) {
        this.score = score;
    }
    public void showInfo(){
        System.out.println("学生:"+name+"age:"+age);
    }
}

package extend.improve;

//继承student类
public class Graduate extends Student {
    public void testing(){//和小学生不一样
        System.out.println("学生:"+name+"正在考大学数学.......");
    }
}

继承细节讨论

  1. 子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问(可间接访问),要通过父类提供的公共的方法去访问
  2. 子类必须调用父类的构造器,完成父类的初始化
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
    如果父类只有无参构造器,子类初始化的时候会调用他;如果父类有参数的构造器,子类初始化的时候,一定要用super()指定构造器:
    super("ss",10);
  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
  5. super在使用时,必须放在构造器第一行(super只能在构造器中使用)
  6. super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  7. java所有类都是Object类的子类, Object是所有类的基类.
  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
  9. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
    思考:如何让A类继承B类和C类?(A->B->C)
  10. 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系(猫继承动物)
    继承的本质
    image

用子类查找一个属性时,按照查找关系,向上查找(private不能直接访问)
image

super关键字

super代表父类的引用,用于访问父类的属性、方法、构造器

  1. 访问父类的属性,但不能访问父类的private属性super.属性名;
  2. 访问父类的方法,不能访问父类的private方法super.方法名(参数列表);
  3. 访问父类的构造器:
    super(参数列表);只能放在构造器的第一句,只能出现一句!

在继承关系中,可以用super访问父类的属性和方法:super.name;super.test(),但是不能访问私有类型。
super()只能在构造器第一行使用,用于指定参数的构造器

super带来的便利:

  1. 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!
  3. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C

image

方法重写/覆盖(override)

基本介绍
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
子类按照自己的需求重写父类的方法,只有方法块能变
image

多态

方法或对象具有多种形态。建立在封装和继承基础上
多态的具体体现:

  1. 方法的重载:重写和重载
    重载:传入不同的参数,调用不同的方法,体现多态
  2. 对象的多态
    (1)一个对象的编译类型和运行类型可以不一致
    (2)编译类型在定义对象时,就确定了,不能改变
    (3)运行类型是可以变化的.
    (4)编译类型看定义时=号的左边,运行类型看=号的右边
    Animal animal = new Dog(); //【animal编译类型是Animal,运行类型Dog】(父类的引用指向子类的对象)
    animal = new Cat(): 【animal的运行类型变成了Cat,编译类型仍然是 Animal)
    image
    多态细节
    多态的前提:两个对象(类)存在继承关系
    多态的向上转型:(把子类对象向上转型)
    1)本质:父类的引用指向了子类的对象
    2)语法:父类类型 引用名=new 子类类型();
    3)特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员
    Animal animal=new Cat()
    √多态的向下转型
    1)语法:子类类型 引用名 = (子类类型) 父类引用
    2)只能强转父类的引用,不能强转父类的对象
    3)要求父类的引用必须指向的是当前目标类型的对象
    4)当向下转型后,可以调用子类类型中所有的成员
    Cat cat = (Cat) animal;另一个引用,也是指向cat对象
    父类和子类都有个eat(),向上转型是让父类能直接用子类的eat()方法,而向下转型是为了让父类能用父类没有,但是子类有的fly()
    animal对象只能调用父类的,
    只有向上转型过的对象才能向下转型,向下转型为恢复子类所有功能
    Animal animal=new Cat()->向上转型
    Cat cat = (Cat) animal;->向下转型
    Animal 是 Cat的父类,向上转型后,animal的编译类型是父类Animal,指向的是一个子类Cat实例;为了能用这个cat实例的他有但是父类没有的方法,就再来个引用,这个引用的编译类型是cat,让他指向Cat,就能用Cat的自己的方法。animal 和 cat指向的都是这个Cat实例,但是编译类型不一样,所能访问的方法和属性就不一样。
    image

属性没有重写之说!属性的值看编译类型

public class poly01 {
    public static void main(String[] args) {
        Base base=new Sub();//向上转型
    System.out.println(base.count);//结果为10,因为base的编译类型为Base
    Sub sub=new Sub(); //sub.count=20
    System.out.println(sub.count);//结果为20
    Sub sub1=(Sub) base;
    System.out.println(sub1.count);//结果为20
    }
}
class Base{//父类
	int count=10;
}
class Sub extends Base{//子类
	int count=20;
}

instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用(当前类)
package poly_;

public class poly03 {
    public static void main(String[] args) {
        AA aa=new BB();//向上转型
        System.out.println(aa.sum());//结果为20+10=30
        //运行类型是BB,先去BB找方法,没找到,去父类AA找
        //在AA里面找到sum(i+10),然后getl(),又回到子类BB找到i=20
        /*如果                         
         * class AA{
         *      public int sum(){
         *              return i+10;
         *              }
         *       }
         * 运行最终结果为20,i在当前声明类中找 
        */
    }
}
class AA{
    int i=10;
    public int sum(){
        return getl()+10;
    }
    public int sum1(){
        return i+10;
    }
    public int getl(){
        return i;
    }
}
class BB extends AA{
    int i=20;
    /*public int sum(){
        return i+20;
    }*/
    public int sum1(){
        return i+10;
    }
    public int getl(){
        return i;
    }
}

多态应用

  1. 多态数组:
    数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
Person[] persons=new Person[3]
Person[0] = new Person("a",20);
Person[1] = new Student("c",30,100);
Person[2] = new Teacher("c",40,10000);

for(int i= 0;i < persons,length; i++){
	//person[i]编译类型是 Person,运行类型是是根据实际情况有JVM来判断
	persons[i].say();//多态绑定机制
	//如何调用子类的私有方法
	if(persons[i] instanceof Student){//判断person[i]的运行类型是不是Student
	//((Student)persons[i]).study();一条语句实现
	Student student=(Student) persons[i];//向下转型,和上面等效
	student.study();
	}
}

}

  1. 多态参数
    方法定义的形参类型为父类类型,实参类型允许为子类类型
posted @   zzzzzzzk  阅读(28)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示