韩顺平零基础30天学会Java【章7 面向对象编程(中级)】

P273~P359

  1. 区分相同名字的类
  2. 当类很多时,可以很好的管理类
  3. 控制访问范围

实际上就是创建不同的文件夹来保存类文件

语法

package 包名;

包的命名规则

只能包含数字、字母、下划线、小圆点,不能数字开头,不能关键字或保留字

命名规范

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

常用的包

  • java.lang.*,基本包,默认引入,不需要再引入
  • java.util.*,util包,系统提供的工具包,工具类,使用Scanner
  • java.net.*,网络包,网络开发
  • java.awt.*,做java的界面开发,GUI

新建包

在src文件夹 右键→软件包 输入 com.xiaoming

如何引入包,Import01.java/

package com.pkg;
import java.util.Arrays;
import java.util.Scanner;

public class Import01 {
    public static void main(String[] args) {
        //使用系统提供的Arrays 完成数组排序
        int[] arr = {-1,20,2,13,3};
        //对其排序
        //传统方法是,自己编写排序
        //系统提供相应的类,可以方便完成 Arrays
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

注意事项和细节,PkgDetail.java

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

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

package com.pkg;

import java.util.Arrays;
import java.util.Scanner;

public class PkgDetail {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int[] arr = {-1, 2, 2};
        Arrays.sort(arr);
    }
}

访问修饰符

用于控制方法和属性的访问权限

  1. 公开:public,对外公开
  2. 受保护:protected,对子类和同一个包中的类公开
  3. 默认:没有修饰符号,向同一个包的类公开
  4. 私有:private,只有类本身可以访问
访问级别 访问控制修饰符 同类 同包 子类 不同包<br>
公开 public
受保护 protected x
默认 x x
私有 private x x x

访问修饰符细节

  1. 修饰符可以用来修饰类中的属性,成员方法以及类
  2. 只有默认的和public才能修饰类,并且遵循上述访问权限的特点
  3. 因为没有学习继承,因此关于子类中的访问权限,讲完字类后再看
  4. 成员方法的访问规则和属性完全一样

封装

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

好处

  1. 隐藏实现细节
  2. 可以对数据进行验证,保证安全合理

实现步骤

  1. 将属性进行私有化private
  2. 提供一个公共的set方法,用于对属性判断并赋值
  3. 提供一个公共的get方法,用于对获取属性的值

快速入门,com.encap.Encapsulation01.java

不能随便查看人的年龄、工资等隐私,并对设置的年龄进行合理的验证,年龄合理就设置,否则就默认

年龄必须在1-120之间,年龄,工资不能直接查看,name长度在2-6字符之间

package com.encap;

public class Encapsulation01 {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "Jack";
        person.setAge(10);
        person.setSalay(100.5);
        System.out.println(person.info());
        Person person1 = new Person("smith", 200, 2000);
        System.out.println(person1.info());
    }
}
//    不能随便查看人的年龄、工资等隐私,并对设置的年龄进行合理的验证,
//    年龄合理就设置,否则就默认
//    年龄必须在1-120之间,年龄,工资不能直接查看,name长度在2-6字符之间

class Person {
    public String name;
    private int age;//年龄私有化
    private double salay;

    //构造器
    public Person() {
    }

    //三个属性的构造器
    //可以将set方法写在构造器中,这样仍然可以验证数据
    public Person(String name, int age, double salay) {
        setName(name);
        setAge(age);
        setSalay(salay);
    }


    //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) {
        //    年龄必须在1-120之间,年龄,工资不能直接查看,name长度在2-6字符之间
        if (age >= 1 && age <= 120) {
            this.age = age;
        } else {
            System.out.println("设置年龄错误,年龄需要在1-120");
            this.age = 18;
        }
    }

    public double getSalay() {
        return salay;
    }

    public void setSalay(double salay) {
        this.salay = salay;
    }

    public String info() {
        return "信息为" + name + " " + getAge() + " " + getSalay();
    }
}

课堂练习,com.encap.AccountTest.java和Account.java

创建程序,在其中定义两个类:Account和AccountTest类,体会java封装性

  1. Account类要求具有属性:姓名(长度为2,3,4)、余额(>20),密码(6位),如果不满足给出提示信息,并给默认值
  2. 通过setXxx方法给Account的属性赋值
  3. 在AccountTest中测试
package com.encap;

public class Account {
    private String name;
    private int account;
    private String code;

    public Account(String name, int account, String code) {
        setAccount(account);
        setCode(code);
        setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 4) {
            this.name = name;
        } else {
            System.out.println("name长度不对");
            this.name = "无名";
        }
    }

    public int getAccount() {
        return account;
    }

    public void setAccount(int account) {
        if (account > 20) {
            this.account = account;
        } else {
            System.out.println("account<20");
            this.account = 20;
        }
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        if (code.length() == 6) {
            this.code = code;
        } else {
            System.out.println("密码长度不为6");
            this.code = "111111";
        }
    }

    public String info() {
        return "信息为" + getName() + " " + getAccount();
    }
}

import com.encap.Account;
public class AccountTest {
    public static void main(String[] args) {
        Account ac = new Account("aq",30,"abcdef");
        System.out.println(ac.info());
    }
}

继承

继承可以解决代码复用,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的字类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

继承基本语法

class 子类 extends 父类{

}

  1. 子类会自动拥有父类定义的属性和方法
  2. 父类又叫超类、基类
  3. 子类又叫派生类

细节

  1. 子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过公共的方法去访问
  2. 子类必须调用父类的构造器,完成父类的初始化
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定父类的哪个构造器完成对父类的初始化工作。
  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下
  5. super在使用时,需要放在构造器的第一行
  6. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  7. java所有类都是Object类的子类
  8. 父类构造器的调用不限于直接父类,将一直追溯到Object类
  9. 子类最多只能继承一个父类,即java 中是单继承机制
  10. 不能滥用继承,子类和父类必须满足 is a 的逻辑 Cat is an Animal Cat extends Animal

返回细节

按照查找关系来返回信息

  1. 首先看子类是否有该属性
  2. 如果子类有这个属性,并且可以访问,则返回信息
  3. 如果子类没有这个属性,就看父类有没有这个属性
  4. 如果父类没有,就按照(3)的规则,继续找上级父类,直到Object

练习,com.extend.ExtendsExercise.java

编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息

编写PC子类,继承Computer类,添加特有属性【品牌brand】

编写NotePad子类,继承Computer类,添加特有属性【演示color】

编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法并打印输出信息

package com.extend;

public class ExtendsExercise {
    public static void main(String[] args) {
        PC pc = new PC("i7", "1G", "100G", "huawei");

        NotePad np = new NotePad("i7", "2G", "200G", "green");

        System.out.println(pc.getDetails());
        System.out.println(np.getDetails());
    }
}

class Computer {
    private String CPU;
    private String cache;
    private String disk;

    public Computer(String CPU, String cache, String disk) {
        this.CPU = CPU;
        this.cache = cache;
        this.disk = disk;
    }

    public String getDetails() {
        return "信息为:CPU: " + CPU + " cache: " + cache + " disk: " + disk;
    }
}

class PC extends Computer {
    private String brand;

    public PC(String CPU, String cache, String disk, String brand) {
        super(CPU, cache, disk);
        this.brand = brand;
    }

    public String getDetails() {
        return super.getDetails() + " brand: " + brand;
    }
}

class NotePad extends Computer {
    private String color;

    public NotePad(String CPU, String cache, String disk, String color) {
        super(CPU, cache, disk);
        this.color = color;
    }
}

super关键字

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

基本语法

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

使用细节

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

super和this的比较

no 区别点 this super<br>
1 访问属性 访问本类中的属性,如果本类没有此属性,则从父类中继续查找 访问父类中的属性
2 调用方法 访问本类中的方法,如果本类没有此方法,则从父类中继续查找 直接访问父类中的方法
3 调用构造器 调用本类的构造器,必须放在构造器的首行 调用父类构造器,必须放在子类构造器的首行
4 特殊 表示当前对象 子类中访问父类对象

方法重写/覆盖(override)

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

方法重写细节

方法重写要满足以下条件:

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

方法重写和重载的比较

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

方法重写练习,com.override.OverrideExercise.java

  1. 编写一个Person类,包括属性(private name, age),构造器、方法say(返回自我介绍)
  2. 编写一个Student类,继承Person类,增加id、score属性/private,以及构造器,定义say方法
  3. 在main中,分别创建Person和Student对象,调用say方法输出自我介绍
package com.override;

public class OverrideExercise {
    public static void main(String[] args) {
        Person person = new Person("a", 11);
        Student student = new Student("a", 12, 1, 100);
        System.out.println(person.say());
        System.out.println(student.say());
    }
}

class Person {
    private String name;
    private int age;

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

    public String say() {
        return "姓名:" + name + " 年龄:" + age;
    }
}


class Student extends Person {
    private int id;
    private int score;

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

    public String say() {
        return super.say() + " id:" + id + " 分数:" + score;
    }
}

多态

方法或对象具有多种形态。

  1. 重写和重载体现多态
  2. 对象多态,com.poly.PolyObject.java
    1. 一个对象的编译类型和运行类型可以不一致
    2. 编译类型在定义对象时,就确定了,不能改变
    3. 运行类型是可以变化的
    4. 编译类型看定义时 = 左边,运行类型看 = 右边 Animal animal = new Dog() animal编译类型时Animal,运行类型是Dog animal = new CatI() animal运行类型变成Cat,编译类型仍是Animal
package com.poly;

public class PolyObject {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.cry();
        animal = new Cat();
        animal.cry();
    }
}

class Animal {
    public void cry() {
        System.out.println("Animal cry()...");
    }
}

class Cat extends Animal {
    @Override
    public void cry() {
        System.out.println("Cat cry()...");
    }
}

class Dog extends Animal {
    @Override
    public void cry() {
        System.out.println("Dog cry()...");
    }
}

多态细节

  • 向上转型

    • 本质:父类的引用指向了子类的对象

      • Animal animal = new Dog();
    • 语法:父类类型 引用名 = new 子类类型();

    • 特点:

      • 编译类型看左边,运行类型看右边
      • 可以调用父类中的所有成员
      • 不能调用子类中特有成员
      • 最终运行效果看子类的具体实现
  • 向下转型

    • 语法:子类类型 引用名 = (子类类型)父类引用;

      • Cat cat = (Cat) animal;
    • 只能强转父类的引用,不能强转父类的对象

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

    • 向下转型后可以调用子类类型中所有的成员

  • 属性没有重写之说

  • instanceOf,用于判断对象的运行类型是否为XX类型或XX类型的子类型

  • Java的向上转型与向下转型

java动态绑定机制

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

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

多态应用

多态数组

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

多态参数

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

Object类详解

equals方法

== 和equals的对比

  1. ==:既可以判断基本类型,又可以判断引用类型
  2. ==:如果判断基本类型,判断的是值是否相等
  3. ==:如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
  4. equals:是Object类中的方法,只能判断引用类型
  5. 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String

hashCode方法

  1. 提高具有哈希结构的容器的效率
  2. 两个引用,如果指向同一个对象,则哈希值肯定一样
  3. 两个引用,如果指向不同对象,则哈希值不一样
  4. 哈希值主要根据地址号来的,不能完全将哈希值等价于地址
  5. 在集合中,hashCode也会重写

toString方法

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

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

finalize方法

  1. 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
  2. 什么时候被回收?当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
  3. 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc()主动出发垃圾回收机制

项目 零钱通

项目需求说明

使用Java开发零钱通项目,可以完成收益入账,消费,查看明细,退出系统等功能

项目代码实现

SmallChangeSys.java完成基本功能,完成基本功能

代码实现改进

  1. 用户输入4退出时,给出提示“你确定要退出吗”y/n,必须输入正确的y/n,否则循环输入指令,直到输入y或n
  2. 在收益入账和消费时,判断金额是否合理
  3. 将面向过程的代码修改成面向对象的方法,编写SmallChangeSysOOP.java并使用SmallChangeSysApp.java完成测试

本章作业

1、定义一个Person类{name,age,job},初始化Person对象数组,有3个person对象,并按照age从大到小进行排序,提示,使用冒泡排序。Homework01.java

2、编写老师类,Homework02.java

  1. 要求有属性name、age、possalary
  2. 编写业务方法,introduce(),实现输出一个教师的信息
  3. 编写教师类的三个字类:教授类、副教授类、讲师类。工资级别:教授:1.3,副教授:1.2,讲师:1.1.在三个子类里重写introd()方法
  4. 定义并初始化一个老师对象,调用业务方法,实现对象基本信息的后台打印
package com.homework;

public class Homework01 {
    public static void main(String[] args) {
        Person[] pArr = new Person[3];
        pArr[0] = new Person("a", 30, "driver");
        pArr[1] = new Person("b", 20, "student");
        pArr[2] = new Person("c", 40, "teacher");
        Person temp;
        for (int i = 0; i < pArr.length; i++) {
            for (int j = i; j < pArr.length; j++) {
                if (pArr[i].getAge() > pArr[j].getAge()) {
                    temp = pArr[i];
                    pArr[i] = pArr[j];
                    pArr[j] = temp;
                }
            }
        }
        for (int i = 0; i < pArr.length; i++) {
            System.out.println(pArr[i].toString());
        }
    }
}

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

    public Person(String name, int age, String job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }
    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 getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", job='" + job + '\'' +
                '}';
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_65656674/article/details/126413444