8.1访问修饰符
8.1.1基本介绍
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
- 公开级别:用 public 修饰,对外公开
- 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
- 默认级别:没有修饰符号,向同一个包的类公开. 4) 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.
4种访问修饰符的访问范围
使用的注意事项
8.2面向对象编程三大特征
8.2.1基本介绍
面向对象编程有三大 特征:封装,继承,和多态
封装:encapsulation就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授予的操作【方法】,才能对数据进行操作。
8.2.2封装的理解和好处
8.2.3封装的实现步骤(三步)
8.3快速入门
代码略
可以将构造器和setXxx结合
//有三个属性的构造器
public Person(String name, int age, double salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
//我们可以将 set 方法写在构造器中,这样仍然可以验证
setName(name);
setAge(age);
setSalary(salary);
}
setXxx方法中可以设置一些约束
例如:
//姓名(长度为 2 位 3 位或 4 位)
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 4) {
this.name = name;
} else {
System.out.println("姓名要求(长度为 2 位 3 位或 4 位),默认值 无名");
this.name = "无名";
}
}
//余额(必须>20)
public void setBalance(double balance) {
if (balance > 20) {
this.balance = balance;
} else {
System.out.println("余额(必须>20) 默认为 0");
}
}
//密码(必须是六位)
public void setPwd(String pwd) {
if (pwd.length() == 6) {
this.pwd = pwd;
} else {
System.out.println("密码(必须是六位)默认密码为 000000");
this.pwd = "000000";
}
}
8.4面向对象编程-继承
8.4.1为什么需要继承?
8.4.2继承基本介绍和示意图
8.4.3继承的基本语法
8.4.4继承给编程带来的便利
- 代码的复用性提高了
- 代码的扩展性和维护性提高了
8.4.5继承的深入讨论/细节问题
输入 ctrl + H 可以看到类的继承关系
8.5继承的本质分析
package com.hspedu.extend_;
/**
* 讲解继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();//内存的布局
//?-> 这时请大家注意,要按照查找关系来返回信息
//(1) 首先看子类是否有该属性
//(2) 如果子类有这个属性,并且可以访问,则返回信息
//(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
//(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object... System.out.println(son.name);//返回就是大头儿子
//System.out.println(son.age);//返回的就是 39
//System.out.println(son.getAge());//返回的就是 39
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 = "大头儿子";
}
子类创建的内存布局
课堂练习:
8.6super关键字
8.6.1基本介绍
super代表父类的引用,用于访问父类的属性、方法、构造器
8.6.2基本语法
super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员;
如果多个基类(上级类)中都有同名的成员,使用 super 访问遵循就近原则。A->B->C
super().xx和this.xx:
找xx 和 this.xx()),顺序是:
(1)先找本类,如果有,则调用
(2)如果没有,则找父类(如果有,并可以调用,则调用)
(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object 类
// 提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access
// 如果查找方法的过程中,没有找到,则提示方法不存在
xx 和 thisxx 查找的规则是
(1) 先找本类,如果有,则调用
(2) 如果没有,则找父类(如果有,并可以调用,则调用)
(3) 如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object 类
super给编程带来的便利/细节
super和this的比较
8.7方法重写/覆盖(override)
8.7.1基本介绍
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就说子类的这个方法覆盖了父类的方法。
8.7.2注意事项和使用细节
方法重写也叫方法覆盖,需要 满足以下条件:
方法重载和重写的比较:
8.8面向对象编程-多态
8.8.1多[多种]态[状态]基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
1)方法的多态
重写和重载就体现多态
package com.hspedu.poly_;
public class PloyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//这里我们传入不同的参数,就会调用不同 sum 方法,就体现多态
System.out.println(a.sum(10, 20));
System.out.println(a.sum(10, 20, 30));
//方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
class B { //父类
public void say() {
System.out.println("B say() 方法被调用...");
}
}
class A extends B {//子类
public int sum(int n1, int n2){//和下面 sum 构成重载
return n1 + n2;
}
public int sum(int n1, int n2, int n3){
return n1 + n2 + n3;
}
public void say() {
System.out.println("A say() 方法被调用...");
}
}
2)对象的多态
重点注意:
1)一个对象的编译类型和运行类型可以不一致;
2)编译类型在定义对象时,就可以确定了,不能改变;
3)运行类型是可以改变的;
4)编译类型看定义时=号的左边,运行类型看=号的右边。
例子:
public class PolyObject {
public static void main(String[] args) {
//体验对象多态特点
//animal 编译类型就是 Animal , 运行类型 Dog
Animal animal = new Dog();
//因为运行时 , 执行到改行时,animal 运行类型是 Dog,所以 cry 就是 Dog 的 cry
animal.cry(); //小狗汪汪叫
//animal 编译类型 Animal,运行类型就是 Cat
animal = new Cat();
animal.cry(); //小猫喵喵叫
}
}
8.8.2多态注意事项和细节讨论
√属性没有重写之说!属性的值看编译类型
√instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
public static void main(String[] args) {
//向上转型: 父类的引用指向了子类的对象
//语法:父类类型引用名 = new 子类类型();
Animal animal = new Cat();
Object obj = new Cat();//可以吗? 可以 Object 也是 Cat 的父类
//多态的向下转型
//(1)语法:子类类型 引用名 =(子类类型)父类引用;
//问一个问题? cat 的编译类型 Cat,运行类型是 Cat
Cat cat = (Cat) animal;
cat.catchMouse();//猫抓老鼠
//(2)要求父类的引用必须指向的是当前目标类型的对象
}
}
课堂练习:
8.8.2java的动态绑定机制
1)当调用对象方法 的时候,该方法会和该对象的内存地址/运行类型绑定
2)当调用对象属性时,没有动态绑定机制,哪里申明,哪里使用
8.8.3多态的应用
1)多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个Teacher 对象, 统一放在数组中,并调用每个对象的say 方法.
应用实例升级:如何调用子类特有的方法,比如Teacher 有一个 teach , Student 有一个 study怎么调用?
public class PloyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、
// 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用 say
for (int i = 0; i < persons.length; i++) {
//老师提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况有 JVM 来判断
System.out.println(persons[i].say());//动态绑定机制
//这里大家聪明. 使用 类型判断 + 向下转型. if(persons[i] instanceof Student) {//判断 person[i] 的运行类型是不是 Student
Student student = (Student)persons[i];//向下转型
student.study();
//小伙伴也可以使用一条语句 ((Student)persons[i]).study();
} else if(persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i];
teacher.teach();
} else if(persons[i] instanceof Person){
//System.out.println("你的类型有误, 请自己检查...");
} else {
System.out.println("你的类型有误, 请自己检查...");
}
}
}
}
2)多态数组
8.9Object类详解
8.9.1equals方法
8.9.2如何重写equals方法
//重写 Object 的 equals 方法
public boolean equals(Object obj) {
//判断如果比较的两个对象是同一个对象,则直接返回 true
if(this == obj) {
return true;
}
//类型判断
if(obj instanceof Person) {//是 Person,我们才比较
//进行 向下转型, 因为我需要得到 obj 的 各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
//如果不是 Person ,则直接返回 false
return false;
}
8.9.3hashCode方法
8.9.4toString方法
- 基本介绍
默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】
子类往往重写 toString 方法,用于返回对象的属性信息 - 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式.
- 当直接输出一个对象时,toString 方法会被默认的调用
重写 toString 方法, 输出对象的属性使用快捷键即可 alt+insert -> toString
8.9.5finalize方法
- 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法
- 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法。
- 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制
8.10断点调试
在断点调试过程中,是运行状态,是以对象的运行类型来执行的
快捷键:
F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)
F7:跳入方法内
F8: 逐行执行代码. shift+F8: 跳出方法
零钱通项目
重点:代码思想
化繁为简.
- 先完成显示菜单,并可以选择
- 完成零钱通明细.
- 完成收益入账
- 消费
- 退出
package myself.smallchange.oop;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Mainmenuoop {
//相关变量
boolean loop = true;//是否循环显示
Scanner scanner = new Scanner(System.in);
String key = "";
double money = 0;
double balance = 0;//余额
String detail = "-----------零钱通菜单-----------";
Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String note = "";
String hint = "";
//查看零钱通明细
public void showDetail(){
System.out.println(detail);
}
//收益入账
public void earn(){
System.out.print("收益入账金额:");
money = scanner.nextDouble();
if(money <= 0){
System.out.println("收益入账金额输入错误!");
return;//退出方法不再执行后面的代码
}
balance += money;
date = new Date();//获取当前日期
//拼接收益入账信息到detail
detail += String.format("\n收益入账\t+" + money + "\t"
+ sdf.format(date) + "\t" + "余额:" + balance );
}
//消费
public void spend(){
System.out.print("消费备注:");
note = scanner.next();
System.out.print("消费金额:");
money = scanner.nextDouble();
//如果消费金额小于等于0,或者余额不够就提示消费金额输入错误
if(money <= 0 || balance < money){
System.out.println("消费金额输入错误!");
return;
}
balance -= money;
date = new Date();//获取当前日期
//拼接消费入账信息到detail
detail += String.format("\n" + note + "\t\t-" + money + "\t"
+ sdf.format(date) + "\t" + "余额:" + balance );
}
//退出
public void exit(){
//定义一个choice,接收用户的输入
//(1)使用while+break来处理接收到的输入是y或者n
//(2)退出while后,再判断choice是y还是n,就可以决定是否退出
while (true){
//用户必须输入y/n,否则就一直循环
System.out.println("你确定要退出嘛(y/n)?");
hint = scanner.next();
if ("y".equals(hint) || "n".equals(hint)){
break;
}
}
if (hint.equals("y")){
loop = false;
System.out.println("您已经退出零钱通。");
}
}
//主菜单
public void mainMenu(){
do{
System.out.println("-----------零钱通菜单oop-----------");
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.print("请选择(1-4):");
key = scanner.next();
switch (key){
case "1" :
showDetail();
break;
case "2" :
earn();
break;
case "3" :
spend();
break;
case "4" :
exit();
break;
default:
System.out.println("输入有误,请重新选择!");
break;
}
}while (loop);
}
}
package myself.smallchange.oop;
public class smallchangeoop {
public static void main(String[] args) {
Mainmenuoop mainmenuoop = new Mainmenuoop();
mainmenuoop.mainMenu();
}
}
总结:
1.return;和break;作业没有记清楚
2.swich(){
case " " :
结构
3.Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm”);
data = new Data()
使用 sdf.format(data);
日期,和日期格式化
4.拼接信息的思想
5.while (true){
//用户必须输入y/n,否则就一直循环
System.out.println(“你确定要退出嘛(y/n)?”);
hint = scanner.next();
if (“y”.equals(hint) || “n”.equals(hint)){
break;
}
}
反向限制,考虑不符合的情况,简化代码
一个功能用一个模块来实现,方便以后扩展