Day13-面向对象进阶01

1、static

1.1、static静态变量

static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量。(被static修饰的成员方法或者成员变量是共享的,谁用谁去拿)

(1)、被static修饰的成员变量,叫做静态变量。

特点:被该类所有对象共享。不属于对象,属于类。随着类的加载而加载的,优先于对象出现的。静态的内容不会随着对象的变化而变化。

调用方式:类名调用(推荐)、对象名调用。

(2)、被static修饰的成员方法,叫做静态方法。

特点:多用在测试类和工具类当中。javabean类中很少会用。

调用方式:类名调用(推荐)、对象名调用。

复习目前所学过的所有的类

1、JavaBean类
    用来描述一类事物的类。比如:Student、Teacher、Dog、Cat等等。
    书写JavaBean类的时候,要私有化成员变量,书写空参构造方法,书写带全部参数的构造方法,
    还需要针对每一个私有化的成员变量提供其对应的get()和set()方法,如果说还有额外的行为,比如
    sleep或study等等,还有一些额外的成员方法。核心在于JavaBean类是用来描述一类事物的。
2、测试类
    用来检查其他类是否书写正确,带有main方法的类,是程序的入口。
    在测试类当中,创建JavaBean类的对象并进行赋值调用的。
3、工具类
    不是用来描述一类事物的,而是帮我们做一些事情的类。
    特点:
        (1)、类名见名知意。
        (2)、私有化构造方法。为了不让外界创建这个类的对象,因为即使创建了也没意义。
        (3)、方法定义为静态。

案例1:用static修饰成员变量。

需求:写一个javab类来描述这个班级的学生。属性有:姓名、年龄、性别。行为有:学习。

package FFFFFF;

public class Student {
    private String name;
    private int age;
    private String gender;
    //新增教师姓名属性,所有同学都一个老师
    public  static String teacherName;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    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 getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    //行为
    public void study(){
        System.out.println(name + "正在学习");
    }

    //
    public void show(){
        System.out.println(name + "," + age + "," + gender + "," + teacherName);
    }

}

package FFFFFF;

public class StudentTest {
    public static void main(String[] args) {
        Student.teacherName = "大何老师";   //利用类名调用静态变量
        //1、创建第一个学生对象
        Student s1 = new Student();
        s1.setName("张三");
        s1.setAge(23);
        s1.setGender("男");
        //s1.teacherName = "大何老师";  //利用对象名调用静态变量
        /**分析:
         * 1、如果Student类中的teacherName不用static修饰,
         *   1.1、  s1和s2都不给teacherName赋值的时候,s1和s2的teacherName输出值为null。
         *   1.2、 只有s1给teacherName赋值的时候,s1的teacherName输出为赋的值,s2的teacherName输出值为null。
         * 2、如果Student类中的teacherName用static修饰,
         *   2.1、 s1和s2都不给teacherName赋值的时候,s1和s2的teacherName输出值为null。
         *   2.2、 s1和s2中只要有一个给teacherName赋值,s1和s2的teacherName输出值都为赋的值。
         *   2.3、利用类名调用静态变量,对teacherName进行赋值,则s1和s2的teacherName输出值都为赋的值。
         */

        s1.study();
        s1.show();
        /**竖着批量修改,比如把本题中s1改为s2,方便快捷
         * 方法1:按住鼠标滚轮,滑动鼠标选择修改的范围
         * 方法2:按住alt,然后按住鼠标左键选择修改的范围
         */
        //1、创建第二个学生对象
        Student s2 = new Student();
        s2.setName("李红");
        s2.setAge(21);
        s2.setGender("女");

        s2.study();
        s2.show();
    }
}

案例2:定义数组工具类。

需求:在实际开发当中,经常会遇到一些数组使用的工具类。请按照以下要求编写一个数组的工具类:ArrayUtil。

(1)、提供一个工具类方法printArr,用于返回整数数组的内容。返回的字符串格式如下:

        [10,20,50,34,100] (只考虑整数型数组,且只考虑一维数组)

(2)、提供这样一个工具方法getAerage,用于返回平均分。(只考虑浮点型数组,且只考虑                一维数组)

(3)、定义一个测试类TestDome,调用该工具类的工具方法,并返回结果。

package Test01;

public class ArrayUtil {
    //私有化构造方法
    //目的:为了不让外界创建他的对象
    private ArrayUtil(){}

    //需要定义为静态的,方便调用
    public static String printArr(int[] arr){
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < arr.length; i++) {
            if(i == arr.length - 1){
                sb.append(arr[i]);
            } else {
                sb.append(arr[i]).append(",");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public static double getAerage(double[] arr){
        double sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum = sum + arr[i];
        }
        double aerage = sum/arr.length;
        return aerage;
    }
}
package Test01;

public class TestDome {
    public static void main(String[] args) {
        int[] arr1 = {1,2,3,4,5};
        String str = ArrayUtil.printArr(arr1);
        System.out.println(str);
        double[] arr2 = {1.2,2.2,3.2};
        double str2 = ArrayUtil.getAerage(arr2);
        System.out.println(str2);
    }
}

案例3:定义学生工具类。

需求:定义一个集合,用于存储3个学生对象。

学生的属性为:name、age、gender。

定义一个工具类,用于获取集合中最大学生的年龄。

package testDome02;

public class Student {
    private String name;
    private  int age;
    private String gender;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    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 getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
package testDome02;

import java.util.ArrayList;

public class StudentUtil {
    private StudentUtil(){}

    //静态方法
    public static int getMaxAAgeStudent(ArrayList<Student> list){
        //定义一个参照物
        int max = list.get(0).getAge();
        for (int i = 1; i < list.size(); i++) {
            int temp = list.get(i).getAge();
            if( temp > max){
                max = temp;
            }
        }
        return max;
    }
}
package testDome02;

import java.util.ArrayList;

public class tset {
    public static void main(String[] args) {
        //创建一个集合用来存储学生对象
        ArrayList<Student> list = new ArrayList<>();

        //创建3个学生对象
        Student stu1 = new Student("张三",23,"男");
        Student stu2 = new Student("小红",18,"女");
        Student stu3 = new Student("王二",21,"男");

        list.add(stu1);
        list.add(stu2);
        list.add(stu3);

        //调用工具类中的方法
        int maxAgeStudent = StudentUtil.getMaxAAgeStudent(list);
        System.out.println(maxAgeStudent);


    }

}

1.2、static的注意事项

1、静态方法只能访问静态。

2、非静态方法可以访问所有。

3、静态方法中没有this关键字。

1.3、静态方法内容补充:

1、在定义方法的时候,在修饰符的位置加上static,这个方法就是一个静态方法
2、静态方法,可以通过类名去访问,也可以通过对象名去访问
3、只能能通过类名去访问的内容,本质上都是属于类最好都通过类名去访问
4、静态方法使用注意事项:
(1)、静态方法中不能访问成员变量
          静态方法,可以在没有创建对象的时候就可以调用,成员变量是在对象完成之后才可以使
          用,如果静态的方法可以访问成员变量,就相当于在创建对象之前,就使用对象内容
(2)、静态方法能不能访问成员方法,不能,原理同上
(3)、静态方法只能访问静态的内容,可以访问静态的属性,也可以访问静态的方法
(4)、静态方法能不能有this关键字,this表示本类当前对象,有了对象才有this,所以还是不行
5、总结:
(1)、静态的内容不可以访问非静态的内容,非静态的内容可以访问静态的内容

1.4、静态变量和非静态变量的区别

1、概念上所属不同:
    (1)、非静态变量属于对象
    (2)、静态变量属于类
2、内存空间位置不同:
    (1)、非静态变量存储在堆内存中,和对象一个区域
    (2)、静态变量属于类,和类.class文件在一个区域,都在方法区中,只不过静态变量在静态区
3、存储的时间不同,生命周期不同
    (1)、非静态变量属于对象,所以生命周期和对象一致,随着对象的创建而创建,随着对象的消亡而消亡
    (2)、静态变量属于类,所以生命周期和类一致,随着类的加载而加载,.class的销毁而销毁
4、访问方式的不同:
    (1)、非静态变量,只能通过对象名访问
    (2)、静态变量,既可以通过类名访问,也可以通过对象名来访问

2、继承

2.1继承的概述

1、继承

(1)、继承是面向对象的三大特征之一,可以让类和类之间产生子父关系。

(2)、Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。

public class Student extends Person {}

Student称为子类(派生类),Person称为父类(基类或者超类)。

2、使用继承的好处:

(1)、可以把多个子类中重复的代码抽取到父类当中,提高代码的复用性。

(2)、子类可以在父类的基础上,增加其他的功能,使子类更强大。子类拥有父类的所有功能。

(3)、子类可以得到父类的属性和行为,子类可以使用父类的属性和行为。

3、什么时候用继承?

当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码。

4、继承的好处和弊端

(1)、好处:

        a、提高了代码的复用性

        b、提高了代码的可维护性

        c、是类中多态的前提

(2)、弊端:

        提高了代码和代码的耦合性(耦合性:事物和事物之间互相影响的程度)

(3)、开发原则:高内聚、低耦合。

(4)、继承的好处远远大于弊端,所以继承还是要用。

2.2、继承的特点

Java只支持单继承,不支持多继承,单支持多层继承

(1)、单继承:一个子类只能继承一个直接父类。

(2)、不支持多继承:子类不能同时继承多个父类。

(3)、多层继承:子类A继承父类B,父类B可以继承父类C。

(4)、每一个类直接或简介继承于Object(由Java虚拟机自动调用)

(5)、子类可以用间接父类(间接父类类似于现实生活当中的爷爷)中的变量与方法。

(6)、父类中的私有成员不能被继承(不能在子类中直接访问)

        a、父类中的一些私有成员,不能在子类中直接调用。

        b、其实在子类对象中,仍然包含父类这些私有的成员变量。

        c、只不过在子类中,不能直接使用这些私有的内容,需要通过公共的访问方式访问。

案例1:

package EXTENDS;

public class Animal {
    //权限修饰符
    //private:子类就无法访问了
    //私有:只能在本类中访问,比如爸爸的私房钱儿子是不能用的
    //子类只能访问父类中非私有的成员
    public void eat(){
        System.out.println("吃东西");
    }
    public void drink(){
        System.out.println("喝水");
    }
}
package EXTENDS;

public class cat extends Animal {
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
package EXTENDS;

public class Dog extends Animal {
    public void lookHome(){
        System.out.println("狗在看家");
    }
}

package EXTENDS;

public class Husky extends Dog{
    public void breakHome(){
        System.out.println("哈士奇在拆家");
    }
}
package EXTENDS;

public class LiHua extends cat{

}
package EXTENDS;

public class Ragdoll extends cat {

}
package EXTENDS;

public class Teddy extends Dog{
    public void touch(){
        System.out.println("泰迪又在蹭我的腿了");
    }
}
package EXTENDS;

public class Test {
    public static void main(String[] args) {
        Ragdoll ad = new Ragdoll();
        ad.eat();
        ad.drink();
        ad.catchMouse();

        Husky hs = new Husky();
        hs.eat();
        hs.drink();
        hs.breakHome();
        hs.lookHome();

        LiHua lh = new LiHua();
        lh.catchMouse();
        
    }
}

2.3、继承中成员变量的访问特点:

1、就近原则、直接用变量名

2、在用本类中的值:this.变量名

3、用父类中的值:super.变量名

说明:

1、(变量:从局部位置开始往上找)(先在局部位置找,本类成员位置找,父类成员位置找,逐级往上,直到找到为止)

2、(this和super是在子类中使用的,不是在测试类当中使用的)

(this.变量:从本类成员位置开始往上找,直到找到为止)

(super.变量:从父类成员位置开始往上找,直到找到为止)

案例:

package Diaoyong;

public class Test {
    public static void main(String[] args) {
        er er1 = new er();
        er1.zishu();
    }
}

class fu {
    String name = "funame";
}
class er extends fu {
    String name = "ziname";
    public void zishu(){
        String name = "输出名字";
        System.out.println(name);
        System.out.println(this.name);
        System.out.println(super.name);
    }
}

package com.zongjie.demo01;

public class Person {
    //父属性
    public String name;
    private Integer age;
    private  String address;

    //父构造方法(无参)
    public Person() {
    }

    //父构造方法(全参)
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    //getter和setter方法
    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    //    方法
    public void run(){
        System.out.println(this.name+"在跑");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}




package com.zongjie.demo01;

public class Student extends Person{
    //子属性
    private String id;
    private Integer score;
    //说明:由于存在继承关系,所以子类不需要再写属性name和属性age了,也不能写他们的getter和setter方法了,直接用父类的

    //子构造方法(无参)
    public Student() {
    }

    //子构造方法(全参,子类中也有名字和年龄)
    public Student(String id, Integer score,String address, String name,Integer age) {
        super(name, age);   //名字和年龄赋值的时候,利用super调用父类中的构造方法
        this.setAddress(address);   //父类中没有它的带参构造,所以需要这样写
        this.id = id;
        this.score = score;
    }

    //子类的getter和setter方法
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }
    public void hurt(){
        //留意这里
        System.out.println(super.name+"在打架");
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ",address='" + this.getAddress() + '\'' +
                ", score=" + score +
                ", age=" + this.getAge() +
                ", name='" + name + '\'' +
                '}';
    }
}



package com.zongjie.demo01;

public class Test {
    public static void main(String[] args) {
        //创建父类对象
        Person p1 = new Person();
        //因为父类中的name属性是public修饰的,所以可以直接用对象调用赋值
        p1.name = "父亲";
        //因为父类中的age属性是private修饰的,所以只能通过setter方法赋值
        p1.setAge(36);
        //父类调用自己的方法
        p1.run();

        //因为继承关系的存在,子类也拥有父类中的name属性和age属性
        //创建一个子类对象1,利用全参构造对创建的对象进行属性赋值
        Student s1 = new Student("1001",100,"北京","儿子小明",25);
        //创建一个子类对象2
        Student s2 = new Student();
        //因为父类中的name是public修饰,所以可以直接通过对象调用进行赋值
        s2.name="女儿小红";
        //因为父类中的age是public修饰,所以只能通过setter方法赋值
        s2.setAge(16);
        //由于继承关系的存在,子类拥有父类中的方法,所以可以直接调用
        s1.run();
        s2.run();
        s2.hurt();

        System.out.println(p1);
        System.out.println(s1);
        System.out.println(s2);
    }
}

/*
上述代码的运行结果如下:
父亲在跑
儿子小明在跑
女儿小红在跑
女儿小红在打架
Person{name='父亲', age=36, address='null'}
Student{id='1001',address='北京', score=100, age=25, name='儿子小明'}
Student{id='null',address='null', score=null, age=16, name='女儿小红'}
原因分析:
1、因为子类继承了父类,所以子类拥有父类的方法
2、因为父类的方法中使用了this,代表当前对象,谁调用this就代表谁,父亲、儿子、女儿在调,所以打印出了不同的结果,super对变量并没有起到作用
*/

 2.4、继承中:成员方法的访问特点

直接调用满足就近原则:谁离我近,我就调用谁

this调用,从本类开始找,就近原则。

super调用,直接访问父类。

(this和super是在子类中使用的,不是在测试类当中使用的)

(变量:从局部位置开始往上找)

(this.变量:从本类成员位置开始往上找)

(super.变量:从父类成员位置开始往上找)

package cyff;

public class test {
    public static void main(String[] args) {
        Student s= new Student();
        s.lunch();
    }
}

class Person{
    public void eat(){
        System.out.println("吃米饭,吃菜");
    }

    public void drink(){
        System.out.println("喝开水");
    }
}

class Student extends Person{
    public void lunch(){
        this.eat();
        this.drink();

        super.eat();
        super.drink();
    }
    public void eat(){
        System.out.println("chifan");
    }
    public void drink(){
        System.out.println("heshui");
    }
}

继承中成员方法的相关补充:

1、在父子类中,如果写的都是不同名的方法,则子类既可以访问父类,也可以访问子类本身。

2、出现了同名的方法时,在子类调用这个方法的时候,只有子类对这个方法的实现。

3、如果父类和子类的方法名相同,但是实现不同,Java把这个叫做方法的重写。

        a、重载:在一个类中,方法名相同,参数列表不同,与返回值类型无关。

        b、重写:在父子类中,方法名一样,参数列表一致,返回值类型一致,实现体不同。

        c、Java中可以通过@Override检测一个方法是否是重写父类的方法。

补充: 方法的重写

定义:把父类的方法在子类里面重写一遍。

当父类的方法不能满足子类现在的需求时,需要进行方法重写。

 重写的本质:方法被重写之后,相当于覆盖掉了父类中的方法。

 方法重写注意事项和要求

1、重写方法的名称、形参列表必须与父类中的保持一致。
2、子类重写父类方法时,访问权限子类必须大于等于父类(暂时了解:空着不写 < protected < public)
3、子类重写父类方法时,返回值类型子类必须小于等于父类。
4、建议:重写的方法尽量和父类保持一致。(重点强调)
5、私有方法不能被重写。
6、子类不能重写父类的静态方法,如果重写会报错的。
补充:5和6总结起来就是“只有被添加到虚方法表中的方法才能被重写”

练习案例1:利用方法的重写设计继承结构

现在有三种动物:哈士奇、沙皮狗、中华田园犬。

暂时不考虑属性,只要考虑行为。

请按照继承的思想特点进行继承体系的设计。

 分析如下:

package JiCheng;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

public class test {
    public static void main(String[] args) {
        HaShiQi h = new HaShiQi();
        h.eat();
        h.drink();
        h.home();
        h.chaijia();
        System.out.println("--------------");
        ShaPiGou s = new ShaPiGou();
        s.eat();
        s.drink();
        s.home();
        System.out.println("--------------");
        ZhongHua z = new ZhongHua();
        z.eat();
        z.drink();
        z.home();

    }
}

//定义动物类
class Animal{
    public void eat(){
        System.out.println("吃狗粮");
    }
    public void drink(){
        System.out.println("喝水");
    }
    public void home(){
        System.out.println("看家");
    }
}

//定义哈士奇
class HaShiQi extends Animal{
    public void chaijia(){
        System.out.println("拆家");
    }
}

//定义沙皮狗
class ShaPiGou extends Animal{
    //因为沙皮狗吃的狗粮和骨头
    //父类中的方法不能满足我们的需求了,所以需要进行重写。
    @Override
    public void eat(){
        super.eat();    //吃狗粮
        System.out.println("吃骨头");
    }
}
//定义中华田园犬
class ZhongHua extends Animal{
    //父类中的方法不能满足我们的需求了,所以需要进行重写
    //而且中华田园犬完全用不到父类中的代码,所以不需要通过super进行调用。
    @Override
    public void eat(){
        System.out.println("吃剩饭");
    }
}

 2.5、继承中:构造方法的访问特点

package WCGZ;

public class Test {
    public static void main(String[] args) {
        Student s1 = new Student();
        Student s2 = new Student("张三",23);
        System.out.println(s2.name + "," + s2.age);
    }

}
class Person{
    String name;
    int age;
    public Person(){
        System.out.println("父类无参构造");
    }
    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
}

class Student extends Person{
    public Student(){
        //子类构造方法中隐藏的super()去访问父类的无参构造
        super();    //调用了父类的无参构造
        System.out.println("子类的无参构造");
    }

    //给父类变量赋值
    public Student(String name,int age){
        super(name,age);    //调用了父类的有参构造
    }

}

输出为下:
父类无参构造
子类的无参构造
张三,23

对2.5进行小结:继承中构造方法的访问特点是什么?

1、子类不能继承父类的构造方法,但是可以通过super调用。

2、子类构造方法的第一行,有一个默认的super();

3、默认先访问父类中无参的构造方法,再执行自己。

4、如果不想调用父类中的无参构造,想要调用父类当中的有参构造,给成员变量赋值,则super需要自己写,而且要把赋值的数据写在小括号当中就可以了。

5、this语句和super语句只能出现在构造方法的第一行,而且只能出现一个。

this、super使用总结

this:理解为一个变量,表示当前方法调用者的地址值。

super:代表父类存储空间。

 注意:要给一些数据设置一些默认值的时候就会用到this(....)访问本类构造方法。

1、含义:
    this表示本类当前对象的引用
    super表示本类当前对象父类部分的引用,哪个对象调用super所在的方法,super就代表哪个对象父类部分的数据
2、super和this都可以访问成员变量
    super只能访问父类中的成员变量
    this既可以访问父类中的成员变量,也可以访问子类中的成员变量
3、this和super都可以访问成员方法
    this既可以访问父类中的成员方法,也可以访问子类的
    super只能访问父类中的成员方法,格式  super.成员方法名(实际参数)
4、super和this都可以在构造方法中使用,但是使用的特点不同,可以用来访问构造方法的内容,this访问叫做this语句,super访问叫做super语句
    this(实际参数);访问子类的构造方法
    super(实际参数);访问父类的构造方法
    super语句和this语句,只能出现在构造方法的第一行,其他位置均不可以
package FWQT;

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
        System.out.println(s.name + "," + s.age + "," + s.school);//输出为  null,0,传智大学
    }
}

 class Student{
    String name;
    int age;
    String school;

     public Student() {
         //表示调用本类其他构造方法
         //细节:虚拟机就不会再添加super();
         this(null,0,"传智大学");
         //System.out.println("123");     //如果写在this语句的上面系统就会报错
     }

     public Student(String name, int age, String school) {
         this.name = name;
         this.age = age;
         this.school = school;
     }
 }

练习案例3:带有继承结构的标准Javabean类

父类代码如下:

package AL01;

public class Employee {
    /**
     * 1.类名见名知意
     * 2.所有的成员变量都需要私有
     * 3.构造方法(空参  带全部参数的构造)
     * 4.get/set
     */
    //id,name,salary三种变量,经理和厨师都有,所以把它们抽取到父类Person当中
    private String id;
    private String name;
    private double salary;

    public Employee() {
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    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;
    }

    //工作和吃饭这两种方法,经历和厨师都有,所以把他们抽取到父类Person当中
    //工作
    public void work(){
        System.out.println("员工在工作");
    }
    //吃饭
    public void eat(){
        System.out.println("吃米饭");
    }
}

经理模块代码如下:

package AL01;

public class Manager extends Employee {
    //因为父类当中的成员变量只有id,name,salary,不能满足子类需求,所以还需要添加变量jiangjin
    private double jiangjin;

    /**标准JavaBean类要求要有无参和全参构造。
     * 方法:Alt+Insert,然后全选,然后确定,再确定即可
     */
    //空参构造
    public Manager() {
    }
    //带全部参数的构造(父类+子类)
    public Manager(String id, String name, double salary, double jiangjin) {
        super(id, name, salary);
        this.jiangjin = jiangjin;
    }

    /**关于get和set方法,
     * 因为父类当中已经有id,name,salary的set、get方法了
     * 所以在这里就不需要再书写
     *只需要写父类当中没有的jiangjin的set、get方法
     */

    public double getJiangjin() {
        return jiangjin;
    }

    public void setJiangjin(double jiangjin) {
        this.jiangjin = jiangjin;
    }
    //因为父类当中的work方法并不能满足子类要求,所以要重写work方法
    @Override
    public void work(){
        System.out.println("管理其他人");
    }
}

厨师模块如下:

package AL01;

public class Cook extends Employee {
    //1、因为父类当中的成员变量有id,name,salary,能满足子类需求,所以不需要再添加变量

    //2、构造方法一定要写
    //空参构造
    public Cook() {
    }
    //全参构造
    public Cook(String id, String name, double salary) {
        super(id, name, salary);
    }
    //3、关于get和set方法,
    // 因为父类当中有id, name, salary的set、get方法,所以这里就不用写了

    //因为父类当中的work方法并不能满足子类要求,所以要重写work方法
    @Override
    public void work(){
        System.out.println("厨师在炒菜");
    }
}

测试类模块如下:

package AL01;

public class Test {
    public static void main(String[] args) {
        //创建对象并赋值调用
        Manager m = new Manager("001","张三",1500,6000);
        System.out.println(m.getId() + "," + m.getName() + "," + m.getSalary() + "," + m.getJiangjin());
        m.work();
        m.eat();

        Cook c = new Cook();
        c.setId("002");
        c.setName("李四");
        c.setSalary(666);
        System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());
        c.work();
        c.eat();
    }
}

输出结果为:
 

001,张三,1500.0,6000.0
管理其他人
吃米饭
002,李四,666.0
厨师在炒菜
吃米饭

练习案例4:带有继承结构的标准JavaBean类

在黑马程序员中有很多员工(Employee)

按照工作内容不同分教研部员工(Teacher)和行政部员工(AdminStaff)

1、教研部根据教学的方式不同又分为讲师(Lecturer)和助教(Tutor)

2、行政部根据负责事项不同,又分为维护专员(Maintainer),采购专员(Buyer)

3、公司的每一个员工都有编号,姓名和其负责的工作

4、每个员工都有工作的功能,但是具体的工作内容又不一样

父类代码如下:

package YuanGong;

public class Employee {
    private String bianhao;
    private String name;

    public Employee() {
    }

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

    public String getBianhao() {
        return bianhao;
    }

    public void setBianhao(String bianhao) {
        this.bianhao = bianhao;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void work(){
        System.out.println("员工在工作");
    }
}
package YuanGong;

public class JiaoYanBu extends Employee {
    public JiaoYanBu() {
    }
    public JiaoYanBu(String bianhao, String name) {
        super(bianhao, name);
    }
}

package YuanGong;

public class XingZhengBu extends Employee {
    public XingZhengBu() {
    }
    public XingZhengBu(String bianhao, String name) {
        super(bianhao, name);
    }
}

子类代码如下:

package YuanGong;

public class JiangShi01 extends JiaoYanBu {
    public JiangShi01() {
    }
    public JiangShi01(String bianhao, String name) {
        super(bianhao, name);
    }

    @Override
    public void work(){
        System.out.println("讲师在工作");
    }
}
package YuanGong;

public class ZhuJiao02 extends JiaoYanBu{
    public ZhuJiao02() {
    }

    public ZhuJiao02(String bianhao, String name) {
        super(bianhao, name);
    }

    @Override
    public void work(){
        System.out.println("助教在工作");
    }
}
package YuanGong;

public class WeiHu03 extends XingZhengBu {
    public WeiHu03() {
    }

    public WeiHu03(String bianhao, String name) {
        super(bianhao, name);
    }

    @Override
    public void work(){
        System.out.println("维护员在工作");
    }
}
package YuanGong;

public class CaiGou04 extends XingZhengBu{
    public CaiGou04() {
    }

    public CaiGou04(String bianhao, String name) {
        super(bianhao, name);
    }

    @Override
    public void work(){
        System.out.println("采购员在工作");
    }
}

测试类代码如下:

package YuanGong;

public class Test {
    public static void main(String[] args) {
        JiangShi01 js = new JiangShi01("001","张三");
        System.out.println(js.getBianhao() + "," + js.getName());
        js.work();

        ZhuJiao02 zj = new ZhuJiao02("002","李四");
        System.out.println(zj.getBianhao() + "," + zj.getName());
        zj.work();

        WeiHu03 wh = new WeiHu03("003","王二");
        System.out.println(wh.getBianhao() + "," + wh.getName());
        wh.work();

        CaiGou04 cg = new CaiGou04("004","麻子");
        System.out.println(cg.getBianhao() + "," + cg.getName());
        cg.work();
    }
}

3 多态

3.1 什么是多态?

同类型的对象,表现出的不同形态。

多态的表现形式

父类类型 对象名称 = 子类对象;
例如:
Person p = new Student();

多态的前提

1、有继承的关系或者实现关系
2、有父类引用指向子类对象   Fu f = new Zi();
3、有方法重写

多态的好处

使用父类型作为参数,可以接收所有子类对象,从而体现了多态的扩展性与便利。

案例1

父类代码如下:

package DuoTai;

public class Person {
    private String name;
    private int age;

    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 void show(){
        System.out.println(name + "," + age);
    }
}

子类代码如下:

package DuoTai;

public class Student extends  Person{
    public Student() {
    }

    public Student(String name, int age) {
        super(name, age);
    }
    @Override
    public void show(){
        System.out.println("学生的信息为:" + getName() + "," + getAge());
    }
}
package DuoTai;

public class Teacher extends  Person {
    public Teacher() {
    }

    public Teacher(String name, int age) {
        super(name, age);
    }
    @Override
    public void show(){
        System.out.println("教师的信息为:" + getName() + "," + getAge());
    }
}
package DuoTai;

public class Admin extends Person {
    public Admin() {
    }

    public Admin(String name, int age) {
        super(name, age);
    }
    @Override
    public void show(){
        System.out.println("管理员的信息为:" + getName() + "," + getAge());
    }
}

测试类代码如下:

package DuoTai;

public class Test {
    public static void main(String[] args) {
        //创建三个对象,并调用register方法
        Student s = new Student();
        s.setName("张三");
        s.setAge(18);

        Teacher t = new Teacher();
        t.setName("李华");
        t.setAge(25);

        Admin ad = new Admin();
        ad.setName("管家");
        ad.setAge(35);

        register(s);
        register(t);
        register(ad);


    }
    //既然这个方法既能接收老师,又能接收老师,还能接收管理员
    //只能把参数写成这三个类型的父类
    public static void register(Person p){
        p.show();
    }
}

3.2 多态调用成员变量的特点

变量调用:编译看左边,运行也看左边。

方法调用:编译看左边,运行看右边。

案例实现:

package DuoTaiA;

public class Test {
    public static void main(String[] args) {
        //创建对象 (多态方式)
        //Fu f = new Zi();
        Animal a = new Dog();
        /**调用成员变量:编译看左边,运行也看左边
         * 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
         * 运行也看左边:java运行代码的时候,实际获取的就是左边父类中的成员变量的值
         */
        System.out.println(a.name);//动物

        /**调用成员方法:编译看左边,运行看右边
         * 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
         * 运行也看左边:java运行代码的时候,实际获取的就是子类中的方法。
         */
        a.show();   //Dog---show方法

        /**理解方法:
         * Animal a = new Dod();
         * 现在用a去调用变量和方法
         * 而a是Animal类型的,所以默认都会从Animal这个类中去找
         *
         * 成员变量:在子类的对象中,会把父类的成员变量也继承下去。 父:name   子:name,因为父类的变量还在,所以用了父类的变量
         * 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。父类的方法被覆盖了(相当于不在了)所以调用了子类的方法
         */
    }
}
class Animal{
    String name = "动物";
    public void show(){
        System.out.println("Animal---show方法");
    }
}

class Dog extends Animal{
    String name = "狗";
    @Override
    public void show(){
        System.out.println("Dog---show方法");
    }
}

class Cat extends Animal{
    String name = "猫";
    @Override
    public void show(){
        System.out.println("Cat---show方法");
    }
}

3.3 多态的优势与弊端

1、多态的优势

a、在多态形式下,右边对象可以实现解耦合,便于扩展和维护。(修改下面被红线圈中的部分即可)

b、定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。

 

 由于参数为Object类,所以可以添加任何类,因为Object类是所有类的父类。

2、多态的弊端是:不能使用子类的特有功能。(重点!!!)

3、 引用数据类型的类型转换,有几种方式?

自动类型转换属于向上转型,是多态的体现,本质上缩小了对象本身访问的范围,减少了访问权限,只能访问父类中的内容,而不能访问子类特有的内容。(由于继承关系,父类的内容子类都有,但是子类中的内容父类不一定有,所以子类的内容父类多。)

强制类型转换属于向下转型,前提条件为一定是一个向上转型的对象。本质上是恢复子类本身的访问范围。

注意:不可以把父类对象进行向下转型,必须是父类的引用指向子类对象这种类型,才可以向下转型。

 4、 强制类型转换能解决哪些问题?

可以转换成真正的子类类型,从而调用子类独有功能。

转换类型与真实对象类型不一致会报错 。

转换的时候用instanceof关键字进行判断。

instanceof关键字案例实践如下:

package DTBD;

public class Test {
    public static void main(String[] args) {
        //创建对象
        Animal a = new Dog();
        //编译看左边,运行看右边
        a.eat();    //狗在吃骨头

        /**多态的弊端
         * 不能调用子类的特有功能,比如Dog里面的lookHome,还有Cat里面的catchMouse
         * 例如:a.lookHome();  系统会报错
         * 因为当调用成员方法的时候,编译看左边,运行看右边。
         * 那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
         */

        /**弊端的解决方案:
         * 变回原来的子类型就可以了,例如:把Animal类型的变量a转换成Dog类型,然后就可以调用子类Dog当中的lookHome了
         * 细节:转换的时候,不能瞎转,如果转换成其他类的类型,就会报错,例如把上
         * 面的Animal类型的变量a转换成Cat类型就会报错
         */
        //Dog d = (Dog) a;
        //d.lookHome();   //狗在看家

        if(a instanceof  Dog){
            Dog d = (Dog) a;
            d.lookHome();
        } else if(a instanceof Cat){
            Cat c = (Cat)  a;
            c.catchHouse();
        } else {
            System.out.println("没有这个类型无法转换");
        }

        //java14新特性
        //先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
        //如果不是Dog类型,则不强转,结果直接是false
        /*if(a instanceof  Dog d){
            d.lookHome();
        } else if(a instanceof Cat c){
            c.catchHouse();
        } else {
            System.out.println("没有这个类型无法转换");
        }*/


    }
}
class Animal{
    public void eat(){
        System.out.println("动物在吃东西");
    }
}

class Dog extends Animal{
    @Override
    public void eat(){
        System.out.println("狗在吃骨头");
    }
    public void lookHome(){
        System.out.println("狗在看家");
    }
}

class Cat extends Animal{
    @Override
    public void eat(){
        System.out.println("猫在吃小鱼干");
    }
    public void catchHouse(){
        System.out.println("猫捉老鼠");
    }
}

3.4 多态的综合练习

 分析如下:

 父类代码如下:

package DTZHLX;

public class Animal {
    private int age;
    private String color;

    public Animal() {
    }

    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
    public void eat(String something){
        System.out.println("动物在吃"+something);
    }
}

子类代码如下:

package DTZHLX;

public class Dog  extends Animal{
    public Dog() {
    }

    public Dog(int age, String color) {
        super(age, color);
    }

    //行为:
    //eat(String something)(something表示吃的东西)
    //看家lookHome方法(无参数)
    @Override
    public void eat(String something){//由于继承关系,getAge()+"岁的"+getColor()会自动调用父类当中对应的的get方法
        System.out.println(getAge()+"岁的"+getColor()+"颜色的狗两只前腿死死地抱住"+something+"猛吃");
    }
    //书写Dog类型特有的方法
    public void lookHome(){
        System.out.println("狗在看家");
    }
}
package DTZHLX;

public class Cat extends Animal {
    public Cat() {
    }

    public Cat(int age, String color) {
        super(age, color);
    }
    //eat(String something)方法(something表示吃的东西)
    //捉老鼠catchHouse方法(无参数)
    @Override
    public void eat(String something){//由于继承关系,getAge()+"岁的"+getColor()会自动调用父类当中对应的的get方法
        System.out.println(getAge()+"岁的"+getColor()+"颜色的猫眯着眼睛侧着头吃"+something);
    }
    //书写Cat类型特有的方法
    public void catchHouse(){
        System.out.println("猫在捉老鼠");
    }
}

Person类代码如下:

package DTZHLX;

public class Person {
    private String name;
    private int age;

    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;
    }

    /**Animal dongwu利用多态
     * instanceof的作用是判断,dongwu instanceof Dog是对输入的参数dongwu进行判断,判断是否为Dog类型
     * 如果是Dog类型,则如果没有对dongwu进行强制转换,则不能调用子类特有的方法。例如下面标
     * 记处1如果没执行,则标志处2会报错。但是无论有没有标记处1,标志处3都不受影响
     */

    public void keepPet(Animal dongwu, String something){
        if(dongwu instanceof Dog){
            Dog d =( Dog )dongwu;   //标记处1
            System.out.println("年龄为"+age+"岁的"+name+"养了一只"+dongwu.getColor()+"颜色的"+dongwu.getAge()+"岁的狗");   //标志处3
            dongwu.eat(something);  //调用子类重写的方法
            d.lookHome();   //标志处2,调用Dog类特有的方法
        } else if(dongwu instanceof Cat){
            Cat c = ( Cat )dongwu;
            System.out.println("年龄为"+age+"岁的"+name+"养了一只"+c.getColor()+"颜色的"+c.getAge()+"岁的猫");
            c.eat(something);
        } else{
            System.out.println("没有这种动物");
        }
    }
}

测试类代码如下:

package DTZHLX;

public class Test {
    public static void main(String[] args) {
        //由于多态的存在,创建Person对象的时候,只需要创建一个就行了
        Person p = new Person("老王",36);

        Dog d = new Dog(8,"黑");
        p.keepPet(d,"骨头");

        System.out.println("----------------------");
        Cat c = new Cat(9,"黄");
        p.keepPet(c,"鱼");

    }

}

代码运行后,输出结果如下:

年龄为36岁的老王养了一只黑颜色的8岁的狗
8岁的黑颜色的狗两只前腿死死地抱住骨头猛吃
狗在看家
----------------------
年龄为36岁的老王养了一只黄颜色的9岁的猫
9岁的黄颜色的猫眯着眼睛侧着头吃鱼

4 包、final关键字

4.1 什么是包?

包就是文件夹。用来管理各种不同功能的Java类,方便后期代码维护。

包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意。

全类名:包名+类名

使用其他类的规则:

(1)、使用同一个包中的类时,不需要导包。

(2)、使用java.lang包中的类时,不需要导包。

(3)、其他情况都需要导包。

(4)、如果同时使用两个包中的同名类,需要用全类名。

 实践案例如下:

package DaoBaoLianXi01;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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;
    }
}
package DaoBaoLianXi02;

public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(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;
    }
}
package DaoBaoLianXi03;

public class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(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;
    }
}
package DaoBaoLianXi01;

import DaoBaoLianXi02.Teacher;

public class Test {
    public static void main(String[] args) {
        //创建对象,验证使用同一个包中的类时,不需要导包
        Student s = new Student();
        s.setName("张三");
        s.setAge(23);
        System.out.println(s.getName()+","+s.getAge());

        //验证使用java.lang包中的类时,不需要导包。(String属于java.lang包中的字符串对象)
        String str = "abc";
        System.out.println(str);

        //验证 其他情况下都需要导包
        //调用了DaoBaoLianXi02里的Teacher类,所以第三行的时候导包了,不然会报错
        //导包时的代码 import DaoBaoLianXi02.Teacher;
        //Teacher t = new Teacher();

        //验证 如果同时使用两个包中的同名类,需要全类名
        //验证方式:新建两个包DaoBaoLianXi02和DaoBaoLianXi03,在这两个包里面分别定义一个Teacher类
        //当同时使用两个包中的同名类,需要全类名,不然会报错。
        DaoBaoLianXi02.Teacher t2 = new DaoBaoLianXi02.Teacher();
        t2.setName("王二");       t2.setAge(12);
        System.out.println(t2.getName()+","+t2.getAge());
        DaoBaoLianXi03.Teacher t3 = new DaoBaoLianXi03.Teacher();
        t3.setName("三三三");     t3.setAge(18);
        System.out.println(t3.getName()+","+t3.getAge());
    }
}

4.2 final关键字

被final修饰后,就不能再改变。final可以修饰方法、类、变量。

(1)修饰方法时:表明该方法是最终方法,不能被重写。(一般用于设置某种不希望被改变的规则。)

(2)修饰类时:表明该类是最终类,不能被继承。

(3)修饰变量时:被修饰的变量叫做常量,只能被赋值一次,且不能被修改。(可以定义和赋值分开写)

修饰方法:
public final void shout(){

}

修饰类:
final class Animal{

}

修饰变量:
final int a = 10;    //变量被final修饰的同时必须直接赋值,否则会报错。

比如以下情况会报错:
final int b;
b = 4;

 细节验证案例如下:

package finalLianXi;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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;
    }
}
package finalLianXi;

public class Test {
    public static void main(String[] args) {
        /**
         * final 修改基本数据类型:记录的值不能发生改变。
         * final 修饰引用数据类型:记录的地址值不能发生改变,内部的属性值还是可以改变的。
         */
        //核心:常量记录的数据是不会发生改变的。(下面的s和Arr是引用变量,记录的都是地址值)

        final double PI = 3.14; //基本数据类型

        //创建对象
        final Student s = new Student("张三",23); //s为引用数据对象
        // s = new Student();   会报错,因为这行代码运行后改变了s的地址值
        s.setName("李四");    //对属性值name进行修改
        s.setAge(24);   //对属性值age进行修改
        System.out.println(s.getName()+","+s.getAge());     //李四,24

        //数组
        final int[] Arr ={1,2,3,4,5};
        //Arr = new int[10];  会报错,因为这行代码运行后改变了Arr的地址值
        Arr[0] = 10;
        Arr[1] = 20;
        //遍历数组
        for (int i = 0; i < Arr.length; i++) {
            System.out.print(Arr[i]+"\t");  //10	20	3	4	5
        }
    }
}

final练习案例:常量的练习

要求:将学生管理系统中用户的操作改写为常量的形式。

思路:把学生管理系统中的switch结构中的选项换成英语单词(英文单词提前被final修饰并赋值),从而增加了代码的可读性。

说明:途中的图片均为黑马程序员视频截图,来源于B站视频

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值