第8章 面向对象编程(中级部分)

目录

目录

8.1 - 8.3  IDE(集成开发环境)--IDEA

        常用快捷键(Mac)

8.4  包  package

        8.4.1  包的三大作用

        8.4.2  包基本语法

        8.4.3  包的本质

        8.4.4  包的命名

        8.4.5  常用的包

        8.4.6  如何引入包

        8.4.7  注意事项和使用细节

8.5  访问修饰符  modifier

        8.5.1  基本介绍

        8.5.2  4种访问修饰符的访问范围

        8.5.3  使用的注意事项         

8.6  面向对象编程三大特征

        8.6.1  基本介绍

        8.6.2  封装介绍

        8.6.3  封装的理解和好处

        8.6.4  封装的实现步骤

8.7  快速入门案例

        8.7.1  将构造器和setXxx结合 

8.8  面向对象编程 -- 继承  extends

        8.8.1  为什么需要继承

        8.8.2  继承基本介绍和示意图

         8.8.3  继承的基本语法

        8.8.4  继承的深入讨论/细节问题

        8.8.5  继承的本质分析(重要)

 8.9  super关键字

        8.9.1  基本介绍

        8.9.2  基本语法

        8.9.3  super给编程带来的便利/细节

        8.9.4  super 与 this 比较

8.10  方法重写/覆盖 override

        8.10.1  基本介绍

        8.10.2  注意事项和使用细节

8.11  面向对象编程  --  多态 ploy

        8.11.1  问题

        8.11.2  多态基本介绍

        8.11.3  多态的具体体现

        8.11.4  多态注意事项和细节讨论

        8.11.5  Java的动态绑定机制 dynamic binding(非常非常重要)

        8.11.6  多态的应用

8.12  Object类详解

        8.12.1  equals方法

        8.12.2  如何重写equals方法

        8.12.3  hashCode  方法

        8.12.4  finalize 方法

8.13  断点调试 debug

8.14  项目-零钱通


8.1 - 8.3  IDE(集成开发环境)--IDEA

        常用快捷键(Mac)

        1) 删除当前行                command + delete

        2)复制当前行                command + D

        3)添加或取消注释        command + /

        4)导入该行需要的类

        5)格式化                        command + option + L

        6)快速运行                    shift + control

        7)生成构造器、get set方法、tostring等                control + enter

        8)查看一个类的层级关系

        9)定位到方法                command + B

        10)自动分配变量名       .var

8.4  包  package

        8.4.1  包的三大作用

        1.  区分相同名字的类

        2.  当类很多时,可以很好的管理类

        3.  控制访问范围

        8.4.2  包基本语法

        package  com.hspedu;

        package关键字,表示打包;com.hspedu表示包名

        8.4.3  包的本质

        包的本质实际上就是创建不同的文件夹/目录来保存类文件

2996a0ea18e04d8483e609326237ac31.png

        8.4.4  包的命名

        命名规则:只能包含数字、字母、下划线、小圆点,但数字不能开头,不能是关键字或保留字。

        命名规范:一般是  小写字母 + 小圆点;

                           一般是  com.公司名.项目名.业务模块名

        8.4.5  常用的包

        一个包下包含很多的类,Java中常用的包有:

        1)java.lang.*  基本包,默认引入,无需再次引入

        2)java.util.*   系统提供的工具包,工具类,使用其中的Scanner、Arrays类

        3)java.net.*   网络包,网络开发

        4)java.awt.*  Java的界面开发,GUI

        8.4.6  如何引入包

        引入一个包的目的是要使用该包下的类

        import java.util.Scanner;//只是引入一个类Scanner

        import java.util.*;//表示将util包的所有类引入,不建议使用

        8.4.7  注意事项和使用细节

        1)package的作用是声明当前类所在的包,需要放在类的最上面,一个类中做多只有一句package

        2)import指令 位置放在package的下面,在类定义前面,可以有多句且无顺序要求

8.5  访问修饰符  modifier

        8.5.1  基本介绍

        Java提供四种访问控制修饰符号,用于控制方法和属性的访问权限:

        1)public公开级别:对外公开

        2)protected受保护级别:对子类和同一个包中的类公开

        3)默认级别:没有修饰符号,向同一个包的类公开

        4)private私有级别:只有类本身可以访问,不对外公开

        8.5.2  4种访问修饰符的访问范围

访问级别                访问控制修饰符同类同包子类不同包
1公开public
2受保护protected
3默认\
4私有private

        8.5.3  使用的注意事项         

        1)修饰符可以用来修饰类中的属性,成员方法以及类

        2)只有默认的和public才能修饰类!且遵循访问权限

        3)子类中访问权限?

        4)成员方法的访问规则和属性完全一样

8.6  面向对象编程三大特征

        8.6.1  基本介绍

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

        8.6.2  封装介绍

        封装(encapsulation)就是把抽象的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过授权的操作(方法),才能对数据进行操作。

        8.6.3  封装的理解和好处

        1)隐藏实现细节:方法(连接数据库)<--- 调用(传入参数...)

        2)可以对数据进行验证,保证安全合理

        8.6.4  封装的实现步骤

        1)将属性进行私有化private(不能直接修改属性)

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

        3)提供一个公共的get方法,用于获取属性的值

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

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

        属性 = 参数名;

}

public void getXxx(类型 参数名){ //权限判断,Xxx表示某个属性

        return xx;        

}

8.7  快速入门案例

        案例:

        不能随便(直接)查看人的年龄,工资等隐私,并对设置的年龄进行合理性验证。年龄合理(必须在1-120)就设置,否则给默认年龄。

public class Encapsulation {
    public static void main(String[] args} {
        
        Person person = new Person("jack", 80, 5000);//age >= 120则会报错
        System.out.println(person.info());
        
    }
}
class Person {
    //属性
    public String name;
    private int age;
    private double sal;

    //构造器
    public Person(){
        //若使用无参构造器,分别初始化属性,则会经过set方法验证,不会出现下面有参构造器的情况

    }
    public Person(String name, int age, double sal){
        //将set方法写在构造器中,仍可以验证;替换this.name = name;等
        //why?
        //如果使用this.name = name;形式,则创建对象输入实际参数时,会跳过set验证方法,导致无
        //法验证数据(如年龄区间,名字长度),所以须将set方法写入构造器。
        setName(name);//等价于this.name = name;
        setAge(age);
        setSal(sal);
    }

    //set get方法
    public String getName(){
        return name;
    }
    public void setName(String name){
        //加入对数据的校验,相当于增加了业务逻辑
        if(name.length() >= 2 && name.length() <= 6) {
            this.name = name;
        } else {
            System.out.println("名字长度不对,将默认名字");
            this.name = "无名人";
        }
    }

    public int getAge(){
        return age;
    }
    public void setAge(int age){
        //加入对数据的校验,相当于增加了业务逻辑
        if(age >= 1 && age <= 120) {
            this.age = age;
        } else {
            System.out.println("年龄不对,将默认年龄");
            this.age = 18;
        }
    }

    public double getSal(){
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }

    //返回属性信息
    public String info() {
        return "信息为 name = " + name + " age = " + age + " sal = " + sal;

}

        8.7.1  将构造器和setXxx结合 

        看上面代码

8.8  面向对象编程 -- 继承  extends

        8.8.1  为什么需要继承

        两个或多个类的属性和方法有很多相同的,如何解决?-- 继承(提高代码复用性、拓展性和维护性)

        8.8.2  继承基本介绍和示意图

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

15e7ef8d313e4c2cb20e847f3e5db6c1.png

         8.8.3  继承的基本语法

class 子类 extends 父类 { // 子类自动拥有父类定义的属性和方法

}

        8.8.4  继承的深入讨论/细节问题

        1)子类继承类所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法在子类中要通过父类提供的公共方法去访问

        2)子类必须调用父类的构造器,完成父类的初始化

        3)当创建子类对象时,不管使用过子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super(参数列表);去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译无法通过。

        4)如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表);

        5)super(参数列表);使用时,必需在构造器中使用并且放在第一行

        6)super 和 this 都只能放在构造器第一行,所以不能共存

        7)Java所有类都是object类的子类,object是所有类的基类

        8)父类构造器的调用不限于直接父类,将一直往上追溯直到object类(顶级父类)

        9)子类最多只能直接继承一个父类,即Java中是单继承机制

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

        8.8.5  继承的本质分析(重要)

        案例:

        创建子类对象时内存中发生什么?(当子类对象创建好后,建立查找对象)

cf19a8b42514459d892a964516a88db5.png

 8.9  super关键字

        8.9.1  基本介绍

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

        8.9.2  基本语法

1.  super.属性名;指访问父类的属性,但不能访问父类的private属性

2.  super.方法名(参数列表);指访问父类的方法,但不能范围父类的private方法

3.  super(参数列表);只能放在构造器第一句,只能出现一句                     

        8.9.3  super给编程带来的便利/细节

        1)调用父类的构造器好处(分工明确,父类属性有父类初始化,子类属性由子类初始化)

        2)当子类中有和父类中的成员重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this和直接 访问效果一样。

        3)super的访问不限于直接父类,若多个基类(上级类)都有重名成员,则遵循就近原则 

        8.9.4  super 与 this 比较

区别thissuper
1访问属性本类开始父类开始
2调用方法本类开始父类开始
3调用构造器调用本类构造器,且放首行        调用父类构造器,且放首行
4特殊表示当前对象子类中访问父类对象

8.10  方法重写/覆盖 override

        8.10.1  基本介绍

        方法重写(覆盖)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法

        8.10.2  注意事项和使用细节

        1)子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一致

        2)子类方法的返回类型和父类方法返回类型一样,或是父类返回类型的子类。如父类返回类型为Object,子类返回类型为String,是可以的

        3)子类方法不能缩小(即可以一样或扩大)父类方法的访问权限

        重载(overload)与 重写(override)

名称发生范围方法名形参列表返回类型修饰符
overload本类必须一样类型、个数或者顺序至少有一个不同无要求无要求
override父子类必须一样类型、个数或者顺序至少有一个不同子类重写的方法返回类型和父类一致,或为其子类子类方法不能缩小父类方法的访问范围

8.11  面向对象编程  --  多态 ploy

        8.11.1  问题

        master类  给  animal类(含有fish、bone等子类)  喂  food类(含有cat、dog等子类)

        传统方法代码冗余,复用性不高,不利于维护 --  引出 多态

        传统方法输出语句为:主人  给  猫/狗/猪  喂  鱼/骨头/苞谷(会写很多行)

        多态:主人  给  动物  喂  食物

public void feed(Animal animal, Food food) {

        System.out.println("animal = " + animal + " food= " + food);

        System.out.println(animal.getName() + " 吃 " + food.getName());

}

        8.11.2  多态基本介绍

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

        8.11.3  多态的具体体现

        1)方法的多态 

                重写 和 重载

        2)对象的多态(核心)

                1.  一个对象的编译类型和运行类型可以不一致

                2.  编译类型在定义对象是,就确定了,不能改变

                3.  运行类型可以改变,可以通过get class()查看

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

public class PolyObject {
    public static void main(String[] args) {
        Animal animal = new Dog();//animal 编译类型为Animal ,运行类型为Dog
        animal.cry();//运行看运行类型,输出 "Dog cry() 小狗在叫..."
        animal = new Cat();//animal 编译类型为Animal ,运行类型为Cat
        animal.cry();//运行看运行类型,输出 "Dog cry() 小猫在叫..."
    }

}

public class Animal {

    public void cry() {
        System.out.println("Animal cry() 动物在叫...");
    }

}

public class Cat extends Animal {

        public void cry() {
        System.out.println("Cat cry() 小猫在叫...");
    }

}

public class Dog extends Animal {

        public void cry() {
        System.out.println("Dog cry() 小狗在叫...");
    }

}

        8.11.4  多态注意事项和细节讨论

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

        2)多态的向上转型

                1.  本质:父类的引用指向了子类的对象(如上代码)

                2.  语法:父类类型  引用名 = new 子类类型();        

                3.  特点:编译类型看左边,运行类型看右边

                                可以调用父类中的所有成员(即调用看编译类型,需遵守访问权限)

                                不能调用子类中特有成员

                                最终运行效果看子类的具体实现!

        3)多态向下转型(实现调用子类特有方法)

                1.  语法:子类类型  引用名 = (子类类型)父类引用

                2.  只能强转父类的引用,不能强转父类的对象

                3.  要求父类的引用必须指向的是当前目标类型的对象

                4.  当向下转型后,可以调用子类类型中所有的成员

        4)属性没有重写之说,属性的值看编译类型

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

public class PolyDeatail {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb instanceof BB);// true
        System.out.println(aa instanceof AA);// true

        AA aa = new BB(); // 向下
        System.out.println(aa instanceof AA);// true 子类型
        System.out.println(aa instanceof BB);// true

        Object obj = new Object();
        System.out.println(obj instanceof AA);// false 

        String str = "hello";
        System.out.println(str instanceof obj);// true 子类型
    }
}

class AA {

}

class BB extends AA {

}

        8.11.5  Java的动态绑定机制 dynamic binding(非常非常重要)

        Java重要特性:动态绑定机制

        1)当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定

        2)当调用对象属性时,没有动态绑定机制,哪里声明,那里使用

public class DynamicBinding {
    public static void main(String[] args){
        A a = new B(); //向下
        System.out.println(a.sum());// 40; 注释掉B类sum方法,30
        System.out.println(a.sum1());// 30;注释掉B类sum1方法,20
    }
}

class A {
    public int i = 10;

    public int sum() {
        return getI() + 10;//因动态绑定机制调用子类 getI方法
    }

    public int sum1() {
        return i + 10;
    }

    public int getI() {
        return i; //使用本类i,因为属性没有动态绑定机制
    }

}

class B extends A {
        public int i = 20;

    public int sum() {
        return i + 20;
    }

    public int sum1() {
        return i + 10;
    }

    public int getI() {
        return i + 10;
    }

}

        8.11.6  多态的应用

        1)多态数组 

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

        实例:

        要求创建1个Person对象、2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象

        

public class PolyArray {
    public static void main(String[] args) {
        //定义数组
        Person[] p = new Person[5];
        //静态初始化
        p[0] = new Person("jack", 20);
        p[1] = new Student("mary", 20, 100);
        p[2] = new Student("smith", 19, 33);
        p[3] = new Teacher("Scott", 30, 20000);
        p[4] = new Teacher("king", 50, 30000);    
        //遍历数组,调用say方法
        for(int i = 0; i <= p.length; i++) {
            System.out.println(p[i].say());//动态绑定
            //调用特有方法
            if(p[i] instanceof Student) {
                ((Student)p[i]).study();
                //Student student = (Student)p[i];
                //student.study();
            } else if (p[i] instanceof Teacher) {
                ((Teacher)p[i]).teach();
            } else if (p[i] instanceof Person) {
                System.out.println();
            } else {
                System.out.println("输入有误");
            }
        }
    }
}

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

    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 String getAge() {
        return age;
    }
    
    public void setAge(Int age) {
        this.age = age;
    }

    public String say() {
        return name + "\t" + age;
    }
}

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 setName(double score) {
        this.score = score;
    }
    
    @Override
    public String say() {
        return super.say() + score;
    }

    //特有方法
    public void study() {
        System.out.println("学生 " + getName() + " 正在学习");
    }
}

public class Teacher extends Person {
    private double sal;

    public Student(String name, int age, double sal) {
        super(name, age);
        this.sal = sal;
    }

    public double getScore() {
        return sal;
    }
    
    public void setName(double sal) {
        this.sal = sal;
    }
    
    @Override
    public String say() {
        return super.say() + sal;
    }

    //特有方法
    public void teach() {
        System.out.println("老师 " + getName() + " 正在教课");
    }
}


        2)多态参数

        方法定义的形参类型为父类类型,实参类型允许为子类类型

        实例1:主人喂动物吃东西

8.12  Object类详解

        8.12.1  equals方法

        == (比较运算符) 和  equals方法  对比

        1)==:既可以判断基本类型,又可以判断引用类型

        2)==:如果判断基本类型,判断的事值是否相等

        3)==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

        4)equals:是Object类中的方法,只能判断引用类型(学习看源码)

        5)Object类中的equals方法默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。(可以比较Object类和其子类如Integer、String类中的equals方法源码)

        8.12.2  如何重写equals方法

        实例:

        判断两个Person对象的内容是否相等,如果两个person对象的各个属性值都一样,则返回true,否则返回false

public class EqualsExercise {
    public static void main(String[] args) {
        Person person1 = new Person("jack", 10 ,'男');
        Person person1 = new Person("jack", 20 ,'男');
        System.out.println(person1.equals(person2));//假

    }
}

class Person {
    private String name;
    private int age;
    private char gender;

    //重写object的equals方法
    public boolean equals(Object obj) {
        //判断如果比较的两个对象是同一个对象,则返回true
        if(this == obj) {
            return true;
        }
        //类型判断
        if(obj instanceof Person) {
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }
        return false;
    }
    
    //构造器 set get方法略
}

        8.12.3  hashCode  方法

        1)提高具有哈希结构的容器的效率

        2)两个引用,如果指向同一个对象,则哈希值一样;如果指向两个不同对象,则哈希值不一样。

        3)哈希值主要根据地址号转变来,但不等价于地址

        4)hashCode如需也会重写,(集合讲解)

        8.12.4  toString  方法

        1)基本介绍

        默认返回:全类名 + @ + 哈希值的十六进制

        子类往往重写toString方法,用于返回对象的属性信息

        2)重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式

        3)当直接输出一个对象时,toString方法会被默认的调用

        8.12.4  finalize 方法

        1)当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作

        2)什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。

        3)垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动出发垃圾回收机制(但不一定成功)

        4)实际中几乎不用该方法(应付面试)

8.13  断点调试 debug

        练习

8.14  项目-零钱通

        8.14.1  项目界面 

        先按照字符串拼接方法(面向过程),进行创建该项目,与OOP做对比。代码如下:

package com.hspedu.samallchange;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class SmallChangeSys {
    //化繁为简
    //1,先完成显示菜单,并可以选择菜单,给出对应提示
    //2,完成零钱通明细(3种 字符串拼接;数组;对象)
    //3,完成收益入账
    //4,完成消费
    //5,退出
    //6,改进代码,要求用户输入4退出时,给出提示"你确定要退出吗? y/n",必须输入正确的y/n,否则循环输入指令,直到输入y或者n。
    //7,在收益入账和消费时,判断金额是否合理,并给出相应的提示。
    public static void main(String[] args) {
        //定义相关变量
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);
        String key = "";

        //完成零钱通明细
        String details = "---------------零钱通明细---------------";

        //完成收益入账
        //定义新变量
        double money = 0;
        double balance = 0;
        Date date = null;//date 是 java.util.Date类型,表示日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");//用于日期
                                                               //格式化为中国使用格式

        //消费
        String note;

        do {
            System.out.println("\n==================零钱通菜单=================");
            System.out.println("\t\t\t1 零钱通明细");
            System.out.println("\t\t\t2 收益入账");
            System.out.println("\t\t\t3 消费");
            System.out.println("\t\t\t4 退   出");

            System.out.println("请选择(1 - 4):");
            key = scanner.next();

            //使用switch 分支控制
            switch (key) {
                case "1" :
                    System.out.println(details);
                    break;
                case "2" :
                    System.out.println("收益入账金额:");
                    money = scanner.nextDouble();
                    //校验 找出不正确的条件,若用正确条件判断代码冗余
                    if (money <= 0) {
                        System.out.println("收益入账金额范围 需要大于0");
                        break;
                    }

                    balance += money;
                    //拼接收益信息
                    date = new Date();//获取当前日期
                    details += "\n收益入账\t" + money + "\t" + sdf.format(date) + 
                                "\t" + balance; //+= ??
                    break;
                case "3" :
                    System.out.println("消费金额");
                    money = scanner.nextDouble();
                    //校验
                    if (money <= 0 || money > balance) {
                        System.out.println("消费金额范围 需要小于余额");
                        break;
                    }

                    System.out.println("消费说明");
                    note = scanner.next();
                    balance -= money;
                    //拼接消费信息
                    date = new Date();//获取当前日期
                    details += "\n" + note + "\t-" + money + "\t" +
                                 sdf.format(date) + "\t" + balance;//+=??

                    break;
                case "4" :
                    String choice = "";
                    while (true) {
                        System.out.println("你确定要退出吗: y/n");
                        choice = scanner.next();
                        if ("y".equals(choice) || "n".equals(choice)) {
                            break;
                        }
                    }
                    if (choice.equals("y")) {
                        loop = false;
                    }

                    System.out.println("4 退   出");
                    break;
                default:
                    System.out.println("选择有误,请重新选择");

            }

        } while (loop);
        System.out.println("----------退出了零钱通项目-----------");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值