JAVA学习笔记(九):面向对象的三大特征

面向对象三大特征⭐⭐⭐⭐⭐

1、封装(encapsulation)

(1)介绍

将数据和具体实现封装在一起
用户只需要通过简单的行为就可以操作数据。

(2)封装的理解和好处

隐藏实现细节;方法(连接数据库)<–调用(传入参数)

② 可以对数据进行验证,保证安全合理。

(3)封装实现的步骤

① 将属性进行私有化private 【不能直接修改属性】;

② 提供一个公共的(public)set方法,用于对属性判断并赋值;

public void setXxx(类型 参数名){  //Xxx  表示某个属性

    //加入数据验证的业务逻辑

    属性 = 参数名;

}

③ 提供一个公共的(public)get方法,用于获取属性的值;

public 数据类型 getXxx(){  //权限判断,Xxx 某个属性

    return xx;

}

便捷方法:

alt + insert 键 弹出框,选择Getter and Setter即可。

(4)案例

public class Encapsulation01 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("HarryPotter");
        person.setAge(3000);
        person.setSalary(20000);
        person.info();
        //工资不能直接看:
        // System.out.println(person.salary);//报错
        System.out.println(person.getSalary());
        
        //用构造器设置值
        Person person1 = new Person("smith",9000,15000);
    }
}
class Person {
    public String name; //name公开
    private int age;//age私有化
    private double salary; //工资私有化

    //构造器
    public Person() {
    }
    public Person(String name, int age, double salary) {
        //为了避免构造器破环私有访问属性的隐私,可以将set方法在构造器内调用即可。
        setName(name);
        setAge(age);
        setSalary(salary);
    }

    //name's Getter and Setter
    //快捷键生成alt+Insert-->Getter and Setter
    public String getName() {
        return name;
    }
    public void setName(String name) {
        //增加业务逻辑
        if(name.length() >= 2 && name.length() <= 6){
            this.name = name;
        } else {
            System.out.println("名字长度不对,需要(2,6)个字符,默认为\"无名\"");
            this.name = "无名";
        }
    }

    //age's Getter and Setter
    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;
        }

    }

    //salary's Getter and Setter
    public double getSalary() {
        //这里可以增加对当前用户的权限判断
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }

    //信息打印
    public void info(){
        System.out.println("name:" + name + "\tage:" + age + "\tsalary:" + salary);
    }
}

  • 为了避免构造器破环私有访问属性的隐私,可以将set方法在构造器内调用即可

2、继承(extends)

(1)介绍

  • 继承可以解决代码复用
  • 当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类种定义这些相同的属性和方法。
  • 所有的子类不需要重写定义这些属性和方法,只需要通过extends来声明继承父类即可。
    在这里插入图片描述

(2)继承的基本语法

    class 子类 extends 父类{
    }

① 子类就会自动拥有父类定义的属性和方法;

② 父类又叫超类,基类;

③ 子类又叫派生类。

(3)案例

ExtendTest.java

public class ExtendTest {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "jack";
        pupil.age = 10;
        pupil.testing();
        pupil.setScore(50);
        pupil.showInfo();

        Graduate graduate = new Graduate();
        graduate.name = "jack";
        graduate.age = 10;
        graduate.testing();
        graduate.setScore(50);
        graduate.showInfo();
    }
}

Student.java

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 + "成绩");
    }
}

Pupil.java

//这个extends一定不能忘了写!!
public class Pupil extends Student{
    public void testing() {
        System.out.println("小学生" + name + "正在考小学数学...");
    }
}

Graduate.java

//让Graduate继承Student类
public class Graduate extends Student {
    public void testing() {
        System.out.println("大学生" + name + "在考大学数学...");
    }
}
  • 输出
小学生jack正在考小学数学...
学生名:jack年龄10成绩
大学生jack在考大学数学...
学生名:jack年龄10成绩

(4)细节

① 子类访问父类的私有属性和方法

  • 子类继承了所有的属性和方法,父类的私有属性和方法不能在子类直接访问,要通过父类提供的公共的方法去访问;
//测试
    Pupil pupil = new Pupil();
    pupil.setScore(80);
    pupil.sayOK();
//子类Pupil
public class Pupil extends Student{
    public void sayOK(){
        System.out.println(name);
        //通过父类提供的公共方法访问私有属性
        //相当于父类的公共方法在子类中隐藏了,所以可以直接调用里面的公共方法
        System.out.println("score=" + getScore());
        //通过父类提供的公共方法访问私有方法
        callTest100();
    }
}
//父类Person
public class Student{
    private double score;

    public void setScore(double score){
        this.score = score;
    }
    public double getScore(){
        return score;
    }
    private void test100(){
        System.out.println("test100");
    }
    public void callTest100(){ //公共的方法调用本类的test100
        test100();
    }
}

② 子类必须调用父类的构造器

  • 子类必须调用父类的构造器,完成父类的初始化
//测试
public class ExtendTest {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
    }
}
//子类
public class Pupil extends Student {
    public Pupil(){
    //这里其实有一个:
    //super();      //默认调用父类的无参构造器
        System.out.println("子类Pupil()无参构造器被调用");
    }
}
//父类
public class Student {
    public Student(){
        System.out.println("父类Student()无参构造器被调用");
    }
}
  • 结果
父类Student()无参构造器被调用
子类Pupil()无参构造器被调用

③ 子类用super(形参列表)调用父类构造器

  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
  • 如果父类没有提供无参构造器,则必须在子类的构造器种用super(形参列表)去指定使用父类的哪个构造器=>完成对父类的初始化工作。
  • 否则,编译不会通过;
//测试
public class ExtendTest {
    public static void main(String[] args) {
        //创建子类对象pupil
        Pupil pupil = new Pupil();
    }
}
//父类
public class Student {
    String name;
    int age;
    private double score;

    //name,age,score
    public Student(String name, int age, double score){
        System.out.println("父类Student(name,age,score)构造器被调用");
    }
}
//子类
public class Pupil extends Student {
    public Pupil(){//
        super("smith",18,90.0); //父类构造器在这里被调用
        System.out.println("子类Pupil()无参构造器被调用");
    }
}
父类Student(name,age,score)构造器被调用
子类Pupil()无参构造器被调用

④ super的放置限制

  1. super在使用时,必须放在子类构造器的第一行;(super只能在构造器中使用)

  2. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器;

⑤ object类

java所有类都是Object类的子类,Object是所有类的基类;

⑥ super多层继承时的调用顺序

  • 父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类),从上向下依次调用;

  • 即:A是B的父类,B是C的父类,那么C可以调用A

  • 并且在构造器里,会从顶级类一层层往下调用

  • 即:A构造器被调用->B构造器被调用->C构造器被调用

⑦ 单继承机制

  • 子类最多只能继承一个父类(指直接继承),即java中是单继承机制;

思考:如何让A类继承B类和C类?

答:可以让A类继承B类,B类继承C类,则A可以用B和C中的方法,相当于让A继承了B和C;

⑧ 继承必须满足逻辑关系

不能滥用继承,子类和父类之间必须满足is - a(包含) 的逻辑关系

Person is a Music?  //人不属于音乐,音乐也不属于人,不能建立继承关系
Person Music //不合理
Music extends Person //不合理

(5)继承的本质分析⭐⭐⭐⭐⭐

  • 案例
public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();
    }
}
class GrandPa { //爷爷类
    String name = "大头爷爷";
    String hobby = "旅游";
}
class Father extends GrandPa { //父类
    String name = "大头爸爸";
    int age = 39;
}
class Son extends Father { //子类
    String name = "大头儿子";
}

在这里插入图片描述

//访问顺序(调用属性或者方法的顺序)

  1. 查看子类是否有该属性
  2. 如果子类有这个属性,并且可以访问,则返回信息
  3. 如果子类没有,就看父类,如果有,并且可以访问,就返回信息
  4. 一直从下级往上级找,直到object或者找到后返回了信息
  5. 如果要查找的属性在中间层级是私有的,在高层级是公有的。不会跳过中间层级,而会报错
public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();
        //按照查找关系来返回信息

        System.out.println(son.name); //大头儿子
        System.out.println(son.getAge());//39  age不能直接访问
        System.out.println(son.hobby);//旅游
    }
}
class GrandPa { //爷爷类
    String name = "大头爷爷";
    String hobby = "旅游";
}
class Father extends GrandPa { //父类
    String name = "大头爸爸";
    private int age = 39;

    public int getAge() {
        return age;
    }
    
}
class Son extends Father { //子类
    String name = "大头儿子";
}

(6)练习

  • 编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
  • 编写PC子类,继承Computer类,添加特有属性【品牌brand】
  • 编写NotePad子类,继承Computer类,添加特有属性【color】
  • 编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用getInfo方法打印输出信息
//测试
public class Test {
    public static void main(String[] args) {
        PC pc = new PC("3060",8,128,"intel");
        NotePad notePad = new NotePad("3090",16,512,"red");
        pc.getInfo();
        notePad.getInfo();
    }
}
//PC
public class PC extends Computer{
    private String brand;
    public PC(String cpu, int memory, int disk, String brand) {
        super(cpu, memory, disk);
        this.brand = brand;
    }
    public void getInfo(){
        System.out.println("====PC信息====");
        getDetails();
        System.out.println("PC品牌:" + brand);
    }
}
//NotePad
public class NotePad extends Computer{
    private String color;

    public NotePad(String cpu, int memory, int disk, String color) {
        super(cpu, memory, disk);
        this.color = color;
    }
    public void getInfo(){
        System.out.println("====NotePad信息====");
        getDetails();
        System.out.println("NotePad颜色:" + color);
    }
}

3、多态

(1)引入

解决代码的复用性

(2)基本介绍

方法和对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础上的。

(3)具体表现

  1. 方法的多态
  • 方法的重载体现多态
System.out.println(a.sum(10,20));
System.out.println(a.sum(10,20,30));
  • 方法的重写体现多态
a.say();
b.say();

  1. 对象的多态⭐⭐⭐⭐⭐
  • 一个对象的编译类型和运行类型可以不一致;
Dog extends Animal
Animal animal = new Dog();
//父类的对象引用(对象名字,在栈里)可以指向子类的对象(在堆里)  
//animal 编译类型是Animal,运行类型Dog(其中Animal是Dog的父类)
  • 编译类型在定义对象时,就确定了,不能改变;

Animal animal = new Dog();

animal类型是Animal不能改变

  • 运行类型是可以改变的;

animal = new Cat();

即:对象引用的指向是可以改变的,不指向Dog对象,指向Cat类型

编译类型看定义时 = 号的左边,运行类型看 = 号的右边

(3)例子

//类关系
Cat extends Animal;
Dog extends Animal;

//调用方法
Cat cat = new Cat();
Dog dog = new Dog();
Person person = new Person();

person.touch(cat);
person.touch(dog);

//方法定义--只需要传入父类的参数,可以接收父类下面所有子类对象
public void touch(Animal animal,Food food){
    System.out.println("主人"+name+"摸"+animal.getName());
}

(4)多态注意事项和使用细节

多态的前提是:两个对象(类)存在继承关系;

  1. 多态的向上转型:
  • 本质:父类的引用指向(接收)了子类的对象;
  • 语法:父类类型 引用名 = new 子类类型();
  • 特点如下:
    • 可以调用父类中的所有成员(需要遵循访问权限);
    • 不能调用子类的特有的成员,因为在编译阶段,能够调用哪些成员,是由编译类型来决定的
    • 最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则和前面讲的方法调用规则一致。
      先看子类->然后看父类->一直向上找
  1. 多态的向下转型
  • 语法:子类类型 引用名 = (子类类型)父类引用
//实例:
Animal animal = new Cat();
Cat cat = (Cat) animal;
//调用,此时能够调用子类cat的特有方法
cat.catchMouse();
  • 只能强转父类的引用,不能强转父类的对象;
  • 要求父类的引用必须指向的是当前目标类型(即等号前的子类类型)的对象;

在这里插入图片描述

相当于animal原来指向一个Cat对象
现在创建了个新名字(新引用),还是指向Cat(通过animal来引用)

//实例:
Animal animal = new Cat();
Dog dog = (Dog) animal;//报错,因为animal指向cat,新的编译类型是Dog,和cat没有继承关系,不能指向cat
  • 当向下转型后,可以调用子类类型中所有的成员。

  • 属性没有重写(覆盖)之说,属性的值看编译类型

也就是只会查找父类的

class Sub extends Base;
Base base = new Sub();
System.out.println(base.count); //输出父类Base的count属性值
  • instanceof 比较操作符:用于判断对象aa的运行类型是否为BB类型或者BB类型的子类型。aa是否包含于BB。返回值为true/false

例如:aa extends BB

则aa instanceof BB => true;

(5)练习

在这里插入图片描述
例子2:

Object obj = 13.4 //ok,double型先自动装箱成Double类,然后向上转型
int objn = (int)obj //ok,obj原先指向Double,先自动拆箱转为doule,然后强制转换为int,这里没有发生对象的向下转型
  • 子类对象向上转型为父类对象,OK
  • 父类对象再想向下转型,只能转为原先的子类对象或者子类对象的子类对象。(只能往更低处转,不能同级转也不能往高处转)

(6)多态的应用

① 多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

实例:现有一个继承结构如下,要求创建1个Person对象,2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象的say方法。

在这里插入图片描述

//主类
public class PolyArray {
    public static void main(String[] args) {
        //先创建一个Person数组对象
        Person[] persons = new Person[5];
        //向上转型,persons[i]的编译类型是Person,运行类型根据实际情况由jvm的判断
        //对每一个数组元素,都要new一个对象
        persons[0] = new Person("hack",30);
        persons[1] = new Student("jack1",31,60);
        persons[2] = new Student("jack2",35,80);
        persons[3] = new Teacher("mark1",50,9000);
        persons[4] = new Teacher("mark2",55,10000);
        for (int i = 1; i < persons.length; i++) {
            //动态绑定机制,从子类(对象的运行类型)开始找say
            System.out.println(persons[i].say());

            //调用子类的特有方法,不能用父类的编译类型去调用。因为编译这一步就过不去(编译的时候,用的是编译类型的对象去调用)
            //persons[i].teach(); //报错
            //persons[i].study(); //报错

            if(persons[i] instanceof Student){ //persons[i]是否为Student类型(或者其子类)
                Student student = (Student)persons[i]; //向下转型
                student.study();
                //上面两行等价于:((Student)persons[i]).study
            } else if(persons[i] instanceof Teacher){//persons[i]是否为Teacher类型(或者其子类)
                ((Teacher)persons[i]).teach;
            } else if(persons[i] instanceof Person){
                //
            } else {
                System.out.println("你的类型有误")
            }
        }
    }
}
//Person类
public class Person {
    private String name = "li";
    private int age = 23;
 
    public Person() {
    }
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
    public String say(){
        return name + "\t" + age;
    }
}
//Student类
public class Student extends Person {
    private double score;
 
    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }
 
    public double getScore() {
        return score;
    }
 
    public void setScore(double score) {
        this.score = score;
    }
 
    @Override
    //重写父类say()
    public String say() {
        return super.say() + "\t" + score;
    }
    public void study(){
        System.out.println("学生" + getName() + "正在听课...");
    }
}
//Teacher类
public class Teacher extends Person{
    private double sal;
 
    public Teacher(String name, int age, double sal) {
        super(name, age);
        this.sal = sal;
    }
 
    public double getSal() {
        return sal;
    }
 
    public void setSal(double sal) {
        this.sal = sal;
    }
 
    @Override
    public String say() {
        return super.say() + "\t" + sal;
    }
    public void teach(){
        System.out.println("老师" + getName() + "正在讲课...");
    }
}

② 多态参数

  • 定义员工类Employee,包含姓名和月工资【private】,以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法;

  • 测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法;

  • 测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法。

  • Employee.java

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getAnnual(){
        return salary*12;
    }
}

  • ComEmployee.java
public class ComEmployee extends Employee{
    public ComEmployee(String name, double salary) {
        super(name, salary);
    }

    public void work(){
        System.out.println("普通员工" + getName() + "正在工作");
    }


    @Override
    public double getAnnual() {
        return super.getAnnual();
    }
}

  • Manager.java
public class Manager extends Employee{
    private double bonus;

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public void manage(){
        System.out.println("经理"+getName()+"正在管理员工");
    }

    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

  • Test.java
public class Test {
    public static void main(String[] args) {
        Employee[] employees = new Employee[3];
        employees[0] = new ComEmployee("pu1",12.5);
        employees[1] = new Manager("man1",20,2);
        employees[2] = new ComEmployee("pu2",10);
        Test test = new Test();
        for (int i = 0; i < 3; i++) {
            System.out.println(employees[i].getName() + "年薪有:" +
                    test.showEmpAnnual(employees[i]) + "k");
            test.testWork(employees[i]);
        }

    }

    public double showEmpAnnual(Employee e){
        return e.getAnnual();
    }

    public void testWork(Employee e){
	    if(e instanceof ComEmployee){
	            ((ComEmployee)e).work();
	        } else if(e instanceof Manager){
	            ((Manager)e).manage();
	        } else {
	            System.out.println("出错");
	        }
	    }
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值