Java学习(三)– 面向对象中级

https://www.bilibili.com/video/BV1fh411y7R8/?spm_id_from=333.999.0.0&vd_source=594d36a0860080a36fe599e0b84e5fb2

由于从这里开始学习了继承,

为了避免多种多样的类混在一起影响复习效率

现在规定每一段演示代码会分成好几个代码块, 如果没有特殊标明, 连续的几个代码块表示一段演示

特殊标明: 像练习一, 练习二这种明确标志, 就是为了给不同的演示做分割

连续代码块的顺序为: 父类 -> 子类 -> 入口类

所以每段演示的题目 (要求) 一般都在 入口类 中, 这一点要特别注意

以免在复习的时候不知道自己在看啥

一、包

1.1 作用

  1. 区分相同名字的类
  2. 当类很多时可以很方便地管理类 (比如 Java API 文档)
  3. 控制访问范围

1.2 基本语法

package 包名

package 表示关键字打包

1.3 包的本质

本质就是创建不同的 文件夹/目录 保存类文件

包的本质

1.4 包的命名规则

  • 只能包含字母, 数字, 下划线, 小圆点
  • 数字不能用作开头
  • 不能是关键字或者保留字
  • 一般是小写字母 + 小圆点

com.公司名.项目名.业务模块名

1.5 常用包

  1. java.lang.* //lang 包是基本包,默认引入,不需要再引入.

  2. java.util.* //util 包,系统提供的工具包, 工具类,使用 Scanner

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

  4. java.awt.* //是做 java 的界面开发,GUI

1.6 导入包

import 包名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hspedu.pkg;

import java.util.Arrays;

//注意:
//老韩建议:我们需要使用到哪个类,就导入哪个类即可,不建议使用 *导入
//import java.util.Scanner; //表示只会引入java.util 包下的 Scanner
//import java.util.*;//表示将java.util 包下的所有类都引入(导入)
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.print(arr[i] + "\t");
}
}
}

1.7 使用细节

  1. package 的作用是声明当前类所在的包, 需要放在类的最上面, 一个类中最多只有一个 package
  2. import 指令放在 package 的下面, 在类定义的前面, 可以有很多句, 没有顺序要求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//package的作用是声明当前类所在的包,需要放在类(或者文件)的最上面,
// 一个类中最多只有一句package

package com.hspedu.pkg;

//import指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求
import java.util.Scanner;
import java.util.Arrays;

//...

//类定义
public class PkgDetail {

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




二、访问修饰符

  1. 公开级别: 用 public 修饰, 对外公开

  2. 受保护级别: 用 protected 修饰, 对子类和同一个包中的类公开

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

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

背下来

  1. 修饰符可以用来修饰类中的属性, 成员方法, 类
  2. 只有 默认和 public 才可以修饰类, 并遵循上述访问权限的特点
  3. 成员方法的访问规则和属性完全一样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

package com.hspedu.modifier;

public class A {

//四个属性,分别使用不同的访问修饰符来修饰
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;
public void m1() {
//在同一类中,可以访问public protected 默认 private 修饰属性和方法
System.out.println("n1=" + n1 + " n2=" + n2 + " n3=" + n3 + " n4=" + n4);
}
protected void m2() { }
void m3() { }
private void m4() { }
public void hi() {
//在同一类中,可以访问public protected 默认 private 修饰属性和方法
m1();
m2();
m3();
m4();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

package com.hspedu.modifier;

public class B {
public void say() {
A a = new A();
//在同一个包下,可以访问 public , protected 和 默认修饰属性或方法,不能访问private 属性或方法
System.out.println("n1=" + a.n1 + " n2=" + a.n2 + " n3=" + a.n3 );

a.m1();
a.m2();
a.m3();
//a.m4(); 错误的
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.hspedu.modifier;

public class Test {
public static void main(String[] args) {
A a = new A ();
a.m1();
B b = new B();
b.say();
}
}

//只有 默认和public 可以修饰类
class Tiger{ }


三、封装

3.1 介绍

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

3.2 好处

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

3.3 如何实现

  1. 将属性进行私有化 private (不能直接修改属性)
  2. 提供一个公共的 (public) set 方法, 用来对属性判断赋值 setter
  3. 提供一个公共的 (public) get 方法, 用来获取属性的值 getter
  4. 还可以将 setter 与构造器结合, 每次调用构造器的时候里面全都是 setter 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.hspedu.encap;

public class Encapsulation01 {

public static void main(String[] args) {
//如果要使用快捷键alt+r, 需要先配置主类
//第一次,我们使用鼠标点击形式运算程序,后面就可以用
Person person = new Person();
person.setName("韩顺平");
person.setAge(30);
person.setSalary(30000);
System.out.println(person.info());
System.out.println(person.getSalary());

//如果我们自己使用构造器指定属性
Person smith = new Person("smith", 80, 50000);
System.out.println("====smith的信息======");
System.out.println(smith.info());


}
}
/*
那么在java中如何实现这种类似的控制呢?
请大家看一个小程序(com.hspedu.encap: Encapsulation01.java),
不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认
年龄, 必须在 1-120, 年龄, 工资不能直接查看 , name的长度在 2-6字符 之间

*/
class Person {
public String name; //名字公开
private int age; //age 私有化
private double salary; //..

public void say(int n,String name) {

}
//构造器 alt+insert
public Person() {
}
//有三个属性的构造器
// 将 setter 与 构造器 相结合
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 和 getXxx 太慢,我们使用快捷键
//然后根据要求来完善我们的代码.
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 = "无名人";
}
}

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;//给一个默认年龄
}
}

public double getSalary() {
//可以这里增加对当前对象的权限判断
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}
//写一个方法,返回属性信息
public String info() {
return "信息为 name=" + name + " age=" + age + " 薪水=" + salary;
}
}

3.4 练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.hspedu.encap;

/**
* 创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性。
* Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>20)、
* 密码(必须是六位), 如果不满足,则给出提示信息,并给默认值(程序员自己定)
* 通过setXxx的方法给Account 的属性赋值。
* 在AccountTest中测试
*/
public class Account {
//为了封装,将3个属性设置为private
private String name;
private double balance;
private String pwd;

//提供两个构造器
public Account() {
}

public Account(String name, double balance, String pwd) {
this.setName(name);
this.setBalance(balance);
this.setPwd(pwd);
}

public String getName() {
return name;
}

//姓名(长度为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 = "无名";
}
}

public double getBalance() {
return balance;
}

//余额(必须>20)
public void setBalance(double balance) {
if (balance > 20) {
this.balance = balance;
} else {
System.out.println("余额(必须>20) 默认为0");
}
}

public String getPwd() {
return pwd;
}

//密码(必须是六位)
public void setPwd(String pwd) {
if (pwd.length() == 6) {
this.pwd = pwd;
} else {
System.out.println("密码(必须是六位)默认密码为 000000");
this.pwd = "000000";
}
}
//显示账号信息
public void showInfo() {
//可以增加权限的校验
System.out.println("账号信息 name=" + name + " 余额=" + balance + " 密码" + pwd);
// if() {
// System.out.println("账号信息 name=" + name + " 余额=" + balance + " 密码");
// }else{
// System.out.println("你无权查看...");
// }
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.hspedu.encap;

public class TestAccount {
public static void main(String[] args) {
//创建Account
Account account = new Account();
account.setName("jack");
account.setBalance(60);
account.setPwd("123456");

account.showInfo();
}
}


四、继承

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

继承

4.2 继承的基本语法

class 子类 extends 父类 {

}

  1. 子类会自动拥有父类定义的属性和方法
  2. 父类又叫 超类, 基类
  3. 子类又叫 派生类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.hspedu.extend_.improve_;

import com.hspedu.extend_.Graduate;
import com.hspedu.extend_.Pupil;

public class Extends01 {
public static void main(String[] args) {
com.hspedu.extend_.Pupil pupil = new Pupil();
pupil.name = "银角大王~";
pupil.age = 11;
pupil.testing();
pupil.setScore(50);
pupil.showInfo();

System.out.println("=======");
com.hspedu.extend_.Graduate graduate = new Graduate();
graduate.name = "金角大王~";
graduate.age = 23;
graduate.testing();
graduate.setScore(80);
graduate.showInfo();
}
}

1
2
3
4
5
6
7
8
9
package com.hspedu.extend_.improve_;

public class Graduate extends Student {

public void testing() {//和Pupil不一样
System.out.println("大学生 " + name + " 正在考大学数学..");
}
}

1
2
3
4
5
6
7
8
9
package com.hspedu.extend_.improve_;

//让Pupil 继承 Student类
public class Pupil extends Student {
public void testing() {
System.out.println("小学生 " + name + " 正在考小学数学..");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.hspedu.extend_.improve_;

//父类,是Pupil 和 Graduate的父类
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 + " 成绩 " + score);
}
}

4.3 好处

  1. 代码复用性提高
  2. 代码的扩展性和维护性提高

4.4 使用细节

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

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

  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]

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

  5. super 在使用时,必须放在构造器第一行 (super 只能在构造器中使用)

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

    注意 不管怎么样子类都会通过某一个方法调用 super 的

  7. java 所有类都是 Object 类的子类, Object 是所有类的基类.

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

  9. 子类最多只能继承一个父类 (指直接继承),即 java 中是单继承机制。思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.hspedu.extend_;

public class ExtendsDetail {
public static void main(String[] args) {
// System.out.println("===第1个对象====");
// Sub sub = new Sub(); //创建了子类对象 sub
// System.out.println("===第2个对象====");
// Sub sub2 = new Sub("jack"); //创建了子类对象 sub2
System.out.println("===第3对象====");
Sub sub3 = new Sub("king", 10); //创建了子类对象 sub2
//sub.sayOk();
}
}

1
2
3
4
5
6
7
8
9
10
package com.hspedu.extend_;

public class TopBase { //父类是Object

public TopBase() {
//super(); Object的无参构造器
System.out.println("构造器TopBase() 被调用...");//1
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.hspedu.extend_;

public class Base extends TopBase { //父类
//4个属性
public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;

public Base() { //无参构造器
System.out.println("父类Base()构造器被调用....");
}
public Base(String name, int age) {//有参构造器
//默认super()
System.out.println("父类Base(String name, int age)构造器被调用....");
}
public Base(String name) {//有参构造器
System.out.println("父类Base(String name)构造器被调用....");
}
//父类提供一个public的方法,返回了n4
public int getN4() {
return n4;
}
public void test100() {
System.out.println("test100");
}
protected void test200() {
System.out.println("test200");
}
void test300() {
System.out.println("test300");
}
private void test400() {
System.out.println("test400");
}
//call
public void callTest400() {
test400();
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.hspedu.extend_;

import java.util.Arrays;

//输入ctrl + H 可以看到类的继承关系
public class Sub extends Base { //子类

public Sub(String name, int age) {
//1. 老师要调用父类的无参构造器, 如下或者 什么都不写,默认就是调用super()
//super();//父类的无参构造器
//2. 老师要调用父类的 Base(String name) 构造器
//super("hsp");
//3. 老师要调用父类的 Base(String name, int age) 构造器
super("king", 20);

//细节: super在使用时,必须放在构造器第一行
//细节: super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
//this() 不能再使用了
System.out.println("子类Sub(String name, int age)构造器被调用....");


}

public Sub() {//无参构造器
//super(); //默认调用父类的无参构造器
super("smith", 10);
System.out.println("子类Sub()构造器被调用....");
}
//当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
public Sub(String name) {
super("tom", 30);
//do nothing...
System.out.println("子类Sub(String name)构造器被调用....");
}

public void sayOk() {//子类方法
//非私有的属性和方法可以在子类直接访问
//但是私有属性和方法不能在子类直接访问
System.out.println(n1 + " " + n2 + " " + n3);
test100();
test200();
test300();
//test400();错误
//要通过父类提供公共的方法去访问
System.out.println("n4=" + getN4());
callTest400();//
}

}

4.5 继承的本质 (※)

  1. 现在方法区中加载类, 并识别类之间的继承关系
  2. 然后先从 最顶级的父类开始加载 Object -> GrandPa -> Father -> Son , 将他们的属性及方法加载出来
  3. 先加载 GrandPa 类的两个属性, 再加载 Father 类的两个属性, 再加载 Son 类的属性
  4. 调用属性时遵循就近原则, 如果子类没有就向上找, 没有就一直向上找, 如果没有或者无访问权限就报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

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 = "大头儿子";
}


继承的本质

4.6 练习

  • 练习一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.hspedu.extend_.exercise;

public class ExtendsExercise02 {
public static void main(String[] args) {
C c = new C();
}
}

class A {//A类

public A() {
System.out.println("我是A类");
}
}

class B extends A { //B类,继承A类 //main方法中: C c =new C(); 输出么内容? 3min
public B() {
System.out.println("我是B类的无参构造");
}

public B(String name) {
System.out.println(name + "我是B类的有参构造");
}
}

class C extends B { //C类,继承 B类
public C() {
this("hello");
System.out.println("我是c类的无参构造");
}

public C(String name) {
super("hahah");
System.out.println("我是c类的有参构造");
}
}


  • 练习二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.hspedu.extend_.exercise;

public class ExtendsExercise03 {
public static void main(String[] args) {
PC pc = new PC("intel", 16, 500, "IBM");
pc.printInfo();
}
}
/*
编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
编写PC子类,继承Computer类,添加特有属性【品牌brand】
编写NotePad子类,继承Computer类,添加特有属性【color】//同学们自己写。
编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,
以及从Computer类继承的属性赋值,并使用方法并打印输出信息
*/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hspedu.extend_.exercise;

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

public class PC extends Computer{

private String brand;
//这里IDEA 根据继承的规则,自动把构造器的调用写好
//这里也体现: 继承设计的基本思想,父类的构造器完成父类属性初始化
//子类的构造器完成子类属性初始化
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand = brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void printInfo() {
System.out.println("PC信息=");
// System.out.println(getCpu() + getMemory() + getDisk());
//调用父类的getDetails方法,得到相关属性信息..
System.out.println(getDetails() + " brand=" + brand);
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.hspedu.extend_.exercise;

//编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
public class Computer {
private String cpu;
private int memory;
private int disk;
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
//返回Computer信息
public String getDetails() {
return "cpu=" + cpu + " memory=" + memory + " disk=" + disk;
}

public String getCpu() {
return cpu;
}

public void setCpu(String cpu) {
this.cpu = cpu;
}

public int getMemory() {
return memory;
}

public void setMemory(int memory) {
this.memory = memory;
}

public int getDisk() {
return disk;
}

public void setDisk(int disk) {
this.disk = disk;
}
}


五、super

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

5.1 基本语法

  1. 访问父类的属性 (不能访问父类的 private 属性) super.属性名
  2. 访问父类的方法 (不能访问父类的 private 方法) super.方法名(参数列表)
  3. 访问父类的构造器 super(参数列表); 只能放在构造器的第一句l,只能出现一句
1
2
3
4
5
6
7
8
9
10
11
package com.hspedu.super_;

public class Super01 {
public static void main(String[] args) {
B b = new B();//子类对象
//b.sum();
b.test();
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.hspedu.super_;

public class Base { //父类是Object

public int n1 = 999;
public int age = 111;
public void cal() {
System.out.println("Base类的cal() 方法...");
}
public void eat() {
System.out.println("Base类的eat().....");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.hspedu.super_;

public class A extends Base{
//4个属性
//public int n1 = 100;
protected int n2 = 200;
int n3 = 300;
private int n4 = 400;

public A() {}
public A(String name) {}
public A(String name, int age) {}

// public void cal() {
// System.out.println("A类的cal() 方法...");
// }

public void test100() {
}

protected void test200() {
}

void test300() {
}

private void test400() {
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.hspedu.super_;

public class B extends A {

public int n1 = 888;

//编写测试方法
public void test() {
//super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;
// 如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C

System.out.println("super.n1=" + super.n1);
super.cal();
}

//访问父类的属性 , 但不能访问父类的private属性 [案例]super.属性名
public void hi() {
System.out.println(super.n1 + " " + super.n2 + " " + super.n3 );
}
public void cal() {
System.out.println("B类的cal() 方法...");
}
public void sum() {
System.out.println("B类的sum()");
//希望调用父类-A 的cal方法
//这时,因为子类B没有cal方法,因此我可以使用下面三种方式

//找cal方法时(cal() 和 this.cal()),顺序是:
// (1)先找本类,如果有,则调用
// (2)如果没有,则找父类(如果有,并可以调用,则调用)
// (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object类
// 提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access
// 如果查找方法的过程中,没有找到,则提示方法不存在
//cal();
this.cal(); //等价 cal

//找cal方法(super.call()) 的顺序是直接查找父类,其他的规则一样
//super.cal();

//演示访问属性的规则
//n1 和 this.n1 查找的规则是
//(1) 先找本类,如果有,则调用
//(2) 如果没有,则找父类(如果有,并可以调用,则调用)
//(3) 如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object类
// 提示:如果查找属性的过程中,找到了,但是不能访问, 则报错, cannot access
// 如果查找属性的过程中,没有找到,则提示属性不存在
System.out.println(n1);
System.out.println(this.n1);

//找n1 (super.n1) 的顺序是直接查找父类属性,其他的规则一样
System.out.println(super.n1);

}
//访问父类的方法,不能访问父类的private方法 super.方法名(参数列表);
public void ok() {
super.test100();
super.test200();
super.test300();
//super.test400();//不能访问父类private方法
}
//访问父类的构造器(这点前面用过):super(参数列表);只能放在构造器的第一句,只能出现一句!
public B() {
//super();
//super("jack", 10);
super("jack");
}
}

5.2 好处

  1. 调用父类构造器, 分工明确, 父类属性由父类构造器初始化, 子类属性由子类构造器初始化

  2. 当子类中有和父类中的成员 (属性 或 方法) 重名时, 为了访问父类的成员, 必须通过 super

    如果没有重名, 使用 super, this, 啥都不用 效果一样

  3. super 的访问不限于直接父类, 如果爷爷类和本类中有同名的成员, 也可以使用 super 去访问爷爷类的成员, 如果多个基类 (上级类) 中都有同名的成员, 使用 super 访问遵循就近原则, 也遵守访问权限相关规则

5.3 super & this

super 跟 this 很像, 就是找属性或方法时直接跳过本类找父类

super 和 this 的比较


六、方法重写 (覆盖)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.hspedu.override_;

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

public Object m1() {
return null;
}

public String m2() {
return null;
}

public AAA m3() {
return null;
}
protected void eat() {

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.hspedu.override_;

public class Dog extends Animal{
//老韩解读
//1. 因为Dog 是 Animal子类
//2. Dog的 cry方法和 Animal的 cry定义形式一样(名称、返回类型、参数)
//3. 这时我们就说 Dog的cry方法,重写了Animal的cry方法
public void cry() {
System.out.println("小狗汪汪叫..");
}

//细节: 子类方法的返回类型和父类方法返回类型一样,
// 或者是父类返回类型的子类 比如 父类 返回类型是 Object ,
// 子类方法返回类型是String
public String m1() {
return null;
}
//这里Object 不是 String的子类,因此编译错误
// public Object m2() {
// return null;
// }

// public BBB m3() {
// return null;
// }
//细节: 子类方法不能缩小父类方法的访问权限 【演示】
//public > protected > 默认>private
public void eat() {

}
}

class AAA {

}

class BBB extends AAA {

}

1
2
3
4
5
6
7
8
9
10
11
package com.hspedu.override_;

public class Override01 {
public static void main(String[] args) {
//演示方法重写的情况
Dog dog = new Dog();
dog.cry();//ctrl+b
}
}


6.1 使用细节

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

  2. 子类方法的返回类型和父类方法的返回类型一样, 或者是父类返回类型的子类

    返回类型要一样

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

    不可以缩小

6.2 重写 & 重载

重写 & 重载

6.3 练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.hspedu.override_;

//编写一个Person类,包括属性/private(name、age),构造器、方法say(返回自我介绍的字符串)
public 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=" + name + " 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;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.hspedu.override_;


//编写一个Student类,继承Person类,增加id、score属性/private,以及构造器,定义say方法(返回自我介绍的信息)。
public class Student extends Person{
private int id;
private double score;

public Student(String name, int age, int id, double score) {
super(name, age);//这里会调用父类构造器
this.id = id;
this.score = score;
}
//say
public String say() { //这里体现super的一个好处,代码复用.
return super.say() + " id=" + id + " score=" + score;
}
public int getId() {
return id;
}

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

public double getScore() {
return score;
}

public void setScore(double score) {
this.score = score;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.hspedu.override_;

public class OverrideExercise {
public static void main(String[] args) {
//在main中,分别创建Person和Student对象,调用say方法输出自我介绍
Person jack = new Person("jack", 10);
System.out.println(jack.say());

Student smith = new Student("smith", 20, 123456, 99.8);
System.out.println(smith.say());
}
}



七、多态

方法或对象具有多种形态

7.1 问题引出

问题引出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.hspedu.poly_;

public class Animal {
private String name;

public Animal(String name) {
this.name = name;
}

public String getName() {
return name;
}

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

1
2
3
4
5
6
7
8
package com.hspedu.poly_;

public class Cat extends Animal {
public Cat(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
package com.hspedu.poly_;

public class Dog extends Animal {
public Dog(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
package com.hspedu.poly_;

public class Pig extends Animal {
public Pig(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.hspedu.poly_;

public class Food {
private String name;

public Food(String name) {
this.name = name;
}

public String getName() {
return name;
}

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

1
2
3
4
5
6
7
8
package com.hspedu.poly_;

public class Bone extends Food {
public Bone(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
package com.hspedu.poly_;

public class Fish extends Food {
public Fish(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
package com.hspedu.poly_;

public class Rice extends Food {
public Rice(String name) {
super(name);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.hspedu.poly_;

public class Master {
private String name;

public Master(String name) {
this.name = name;
}

public String getName() {
return name;
}

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

//使用多态机制,可以统一的管理主人喂食的问题
//animal 编译类型是Animal,可以指向(接收) Animal子类的对象
//food 编译类型是Food ,可以指向(接收) Food子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());
}

//主人给小狗 喂食 骨头
// public void feed(Dog dog, Bone bone) {
// System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
// }
// //主人给 小猫喂 黄花鱼
// public void feed(Cat cat, Fish fish) {
// System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
// }

//如果动物很多,食物很多
//===> feed 方法很多,不利于管理和维护
//Pig --> Rice
//Tiger ---> meat ...
//...

}

使用多态机制解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.hspedu.poly_;

public class Poly01 {
public static void main(String[] args) {

Master tom = new Master("汤姆");
Dog dog = new Dog("大黄~");
Bone bone = new Bone("大棒骨~");
tom.feed(dog, bone);

Cat cat = new Cat("小花猫~");
Fish fish = new Fish("黄花鱼~");
System.out.println("===========-------");
tom.feed(cat, fish);

//添加 给小猪为米饭
Pig pig = new Pig("小花猪");
Rice rice = new Rice("米饭");
System.out.println("===================");
tom.feed(pig, rice);
}
}

7.2 方法的多态

重写 和 重载就体现了多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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() 方法被调用...");
}
}

7.3 对象的多态 (※)

  1. 一个对象的百年一类型和运行类型可以不一致
  2. 编译类型在定义对象时就确定了, 不能改变
  3. 运行类型是可变的
  4. 编译类型看定义时 = 左边, 运行类型看 = 右边
1
2
3
4
5
6
7
8
package com.hspedu.poly_.objectpoly_;

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

1
2
3
4
5
6
7
8
9
package com.hspedu.poly_.objectpoly_;

public class Cat extends Animal {

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

1
2
3
4
5
6
7
8
9
package com.hspedu.poly_.objectpoly_;

public class Dog extends Animal {

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.hspedu.poly_.objectpoly_;

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(); //小猫喵喵叫
}
}

7.4 注意事项

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

7.4.1 向上转型

  1. 本质: 父类的引用指向了子类的对象 (子类向上转型成了父类)

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

  3. 特点:

    • 编译类型看左边, 运行类型看右边

    • 可以调用父类中的所有成员 (要遵守访问权限)

    • 不能调用子类中的特有成员

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

      子类和父类可能具有相同的方法, 此时进行向上转型

      编译的指令是 javac, 编译通过的是父类的方法, 但是此时子类有与父类同名的方法 (重写)

      运行的指令是 java, 与编译的指令无关, 哪个方法通过编译, 哪个方法就可以运行

      则此时运行的方法其实是子类重写过后的与父类同名的方法

7.4.2 向下转型

  1. 语法: 子类类型 引用名 = (子类类型) 父类引用; (父类向下转型成了子类)

  2. 只能强转父类的引用, 不能强转父类的对象 (对象创建出来是啥就是啥, 不能强转)

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

    就是在这里要对父类的引用进行向下转型时, 要向下转型的这个子类姑且设为 A

    而这个要向下转型的父类在向上转型时, 它当时指向的对象必须是 A 类的对象

    否则不可以被向下转型

    相当于一上一下转回来了, 目的就是使用子类的特有方法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.hspedu.poly_.detail_;

public class Animal {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void eat(){
System.out.println("吃");
}
public void show(){
System.out.println("hello,你好");
}

}

1
2
3
4
5
6
7
8
9
10
11
package com.hspedu.poly_.detail_;

public class Cat extends Animal {
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){//Cat特有方法
System.out.println("猫抓老鼠");
}
}

1
2
3
4
5
package com.hspedu.poly_.detail_;

public class Dog extends Animal {//Dog是Animal的子类
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.hspedu.poly_.detail_;

public class PolyDetail {
public static void main(String[] args) {

//向上转型: 父类的引用指向了子类的对象
//语法:父类类型引用名 = new 子类类型();
Animal animal = new Cat();
Object obj = new Cat();//可以吗? 可以 Object 也是 Cat的父类

//向上转型调用方法的规则如下:
//(1)可以调用父类中的所有成员(需遵守访问权限)
//(2)但是不能调用子类的特有的成员
//(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
//animal.catchMouse();错误
//(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
//,然后调用,规则我前面我们讲的方法调用规则一致。
animal.eat();//猫吃鱼..
animal.run();//跑
animal.show();//hello,你好
animal.sleep();//睡

//老师希望,可以调用Cat的 catchMouse方法
//多态的向下转型
//(1)语法:子类类型 引用名 =(子类类型)父类引用;
//问一个问题? cat 的编译类型 Cat,运行类型是 Cat
Cat cat = (Cat) animal;
cat.catchMouse();//猫抓老鼠
//(2)要求父类的引用必须指向的是当前目标类型的对象
Dog dog = (Dog) animal; //报错

System.out.println("ok~~");
}
}

  • 属性没有重写这一说, 属性的值要看编译类型

编译的时候, 属性就已经确定了, 这跟向上转型思想差不多 (忘了就看看我在上面的分析笔记)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.hspedu.poly_.detail_;

public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub();//向上转型
System.out.println(base.count);// ? 看编译类型 10
Sub sub = new Sub();
System.out.println(sub.count);//? 20
}
}

class Base { //父类
int count = 10;//属性
}
class Sub extends Base {//子类
int count = 20;//属性
}

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

A instanceof B, A 是不是 B 的….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.hspedu.poly_.detail_;

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

//aa 编译类型 AA, 运行类型是BB
//BB是AA子类
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);

Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);
System.out.println(str instanceof Object);//true
}
}

class AA {} //父类
class BB extends AA {}//子类

7.5 动态绑定机制

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

    就是先在子类找方法, 没有的话就向上找, 在父类的方法里找, 一直到 Object, 没有的话报错

    对比 继承 那部分的内存图

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.hspedu.poly_.dynamic_;

public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
// 将子类的 sum() 注释掉
System.out.println(a.sum());//? 20 + 20 -> 20 + 10
// 将子类的 sum1() 注释掉
System.out.println(a.sum1());//? 20 + 10 -> 10 + 10
}
}

class A {//父类
public int i = 10;
//动态绑定机制:

public int sum() {//父类sum()
// 这个 getI() 是使用的子类的方法, 只要有同名的就用子类的, 动态转
return getI() + 10;//20 + 10
}

public int sum1() {//父类sum1()
// 属性没有动态绑定机制,在 A 类中声明的, 就用 A 类的
return i + 10;//10 + 10
}

public int getI() {//父类getI
return i;
}
}

class B extends A {//子类
public int i = 20;

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

public int getI() {//子类getI()
return i;
}

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

7.6 多态数组

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.hspedu.poly_.polyarr_;

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 int getAge() {
return age;
}

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

public String say() {//返回名字和年龄
return name + "\t" + age;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hspedu.poly_.polyarr_;

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;
}
//重写父类say

@Override
public String say() {
return "学生 " + super.say() + " score=" + score;
}
//特有的方法
public void study() {
System.out.println("学生 " + getName() + " 正在学java...");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hspedu.poly_.polyarr_;

public class Teacher extends Person {
private double salary;

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

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}
//写重写父类的say方法

@Override
public String say() {
return "老师 " + super.say() + " salary=" + salary;
}
//特有方法
public void teach() {
System.out.println("老师 " + getName() + " 正在讲java课程...");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.hspedu.poly_.polyarr_;

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("你的类型有误, 请自己检查...");
}

}

}
}

7.7 多态参数

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.hspedu.poly_.polyparameter_;

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

public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
//得到年工资的方法
public double getAnnual() {
return 12 * 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;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.hspedu.poly_.polyparameter_;

public class Manager extends Employee{

private double bonus;

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

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manage() {
System.out.println("经理 " + getName() + " is managing");
}
//重写获取年薪方法
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.hspedu.poly_.polyparameter_;

public class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工 " + getName() + " is working");
}

@Override
public double getAnnual() { //因为普通员工没有其它收入,则直接调用父类方法
return super.getAnnual();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.hspedu.poly_.polyparameter_;

public class PloyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
PloyParameter ployParameter = new PloyParameter();
ployParameter.showEmpAnnual(tom);
ployParameter.showEmpAnnual(milan);

ployParameter.testWork(tom);
ployParameter.testWork(milan);

}

//showEmpAnnual(Employee e)
//实现获取任何员工对象的年工资,并在main方法中调用该方法 [e.getAnnual()]
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());//动态绑定机制.
}
//添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
public void testWork(Employee e) {
if(e instanceof Worker) {
((Worker) e).work();//有向下转型操作
} else if(e instanceof Manager) {
((Manager) e).manage();//有向下转型操作
} else {
System.out.println("不做处理...");
}
}
}


八、Object 类详解

8.1 equals 方法

8.1.1 ==equals 的对比 (面试)

==

  1. 既可以判断基本类型, 又可以判断引用类型
  2. 如果判断基本类型, 判断的是值是否相等
  3. 如果判断引用类型, 判断的是地址是否相等, 即判定是不是一个对象

equals

  1. 是 Object 类中的方法, 只能判断引用类型 (使用前记得查看 jdk 源码)

  2. 默认判断的是地址是否相等, 子类中往往重写该方法, 用于判断内容是否相等

    比如 Integer, String (看源码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.hspedu.object_;

public class Equals01 {

public static void main(String[] args) {
A a = new A();
A b = a;
A c = b;
System.out.println(a == c);//true
System.out.println(b == c);//true
B bObj = a;
System.out.println(bObj == c);//true
int num1 = 10;
double num2 = 10.0;
System.out.println(num1 == num2);//基本数据类型,判断值是否相等

//equals 方法,源码怎么查看.
//把光标放在equals方法,直接输入ctrl+b
//如果你使用不了. 自己配置. 即可使用.

/*
//带大家看看Jdk的源码 String类的 equals方法
//把Object的equals方法重写了,变成了比较两个字符串值是否相同
public boolean equals(Object anObject) {
if (this == anObject) {//如果是同一个对象
return true;//返回true
}
if (anObject instanceof String) {//判断类型
String anotherString = (String)anObject;//向下转型
int n = value.length;
if (n == anotherString.value.length) {//如果长度相同
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//然后一个一个的比较字符
if (v1[i] != v2[i])
return false;
i++;
}
return true;//如果两个字符串的所有字符都相等,则返回true
}
}
return false;//如果比较的不是字符串,则直接返回false
}
*/


"hello".equals("abc");

//看看Object类的 equals 是
/*
//即Object 的equals 方法默认就是比较对象地址是否相同
//也就是判断两个对象是不是同一个对象.
public boolean equals(Object obj) {
return (this == obj);
}
*/


/*
//从源码可以看到 Integer 也重写了Object的equals方法,
//变成了判断两个值是否相同
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
*/
Integer integer1 = new Integer(1000);
Integer integer2 = new Integer(1000);
System.out.println(integer1 == integer2);//false
System.out.println(integer1.equals(integer2));//true

String str1 = new String("hspedu");
String str2 = new String("hspedu");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true




}
}

class B {}
class A extends B {}

8.1.2 如何重写 equals 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.hspedu.object_;

public class EqualsExercise01 {
public static void main(String[] args) {
Person person1 = new Person("jack", 10, '男');
Person person2 = new Person("jack", 20, '男');

System.out.println(person1.equals(person2));//假
}
}
//判断两个Person对象的内容是否相等,
//如果两个Person对象的各个属性值都一样,则返回true,反之false
class Person{ //extends Object
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,我们才比较

//进行 向下转型, 因为我需要得到obj的 各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
//如果不是Person ,则直接返回false
return false;

}

public Person(String name, int age, char 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 char getGender() {
return gender;
}

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

}

8.1.3 练习

  • 练习一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.hspedu.object_;

public class EqualsExercise02 {
public static void main(String[] args) {

Person_ p1 = new Person_();
p1.name = "hspedu";

Person_ p2 = new Person_();
p2.name = "hspedu";

System.out.println(p1==p2); //False
System.out.println(p1.name .equals( p2.name));//T
System.out.println(p1.equals(p2));//False

String s1 = new String("asdf");

String s2 = new String("asdf");
System.out.println(s1.equals(s2));//T
System.out.println(s1==s2); //F

}
}

class Person_{//类
public String name;
}

  • 练习二
1
2
3
4
5
6
7
8
9
10
11
12
//代码如下 EqualsExercise03.java 2min
int it = 65;
float fl = 65.0f;
System.out.println(“6565.0f 是否相等?” + (it == fl));//T
char ch1 = ‘A’; char ch2 = 12;
System.out.println(“65 和‘A’是否相等?” + (it == ch1));//T
System.out.println(“12 和 ch2 是否相等?” + (12 == ch2));//T
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println("str1 和 str2 是否相等?"+ (str1 == str2)); //F
System.out.println(“str1 是否 equals str2?”+(str1.equals(str2)));//T
System.out.println(“hello” == new java.sql.Date());

8.2 hashCode方法

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

  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!

  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的

  4. 哈希值主要根据地址号来的, 不能完全将哈希值等价于地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.hspedu.object_;

public class HashCode_ {
public static void main(String[] args) {

AA aa = new AA();
AA aa2 = new AA();
AA aa3 = aa;
System.out.println("aa.hashCode()=" + aa.hashCode());
System.out.println("aa2.hashCode()=" + aa2.hashCode());
System.out.println("aa3.hashCode()=" + aa3.hashCode());


}
}
class AA {}

8.3 toString 方法

  1. 基本介绍

    默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】

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

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

  3. 当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用 monster.toString()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.hspedu.object_;

public class ToString_ {
public static void main(String[] args) {

/*
Object的toString() 源码
(1)getClass().getName() 类的全类名(包名+类名 )
(2)Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/

Monster monster = new Monster("小妖怪", "巡山的", 1000);
System.out.println(monster.toString() + " hashcode=" + monster.hashCode());

System.out.println("==当直接输出一个对象时,toString 方法会被默认的调用==");
System.out.println(monster); //等价 monster.toString()



}
}

class Monster {
private String name;
private String job;
private double sal;

public Monster(String name, String job, double sal) {
this.name = name;
this.job = job;
this.sal = sal;
}

//重写toString方法, 输出对象的属性
//使用快捷键即可 alt+insert -> toString
@Override
public String toString() { //重写后,一般是把对象的属性值输出,当然程序员也可以自己定制
return "Monster{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
'}';
}

@Override
protected void finalize() throws Throwable {
System.out.println("fin..");
}
}

8.4 finalize 方法

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

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

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

  4. 在实际开发中,几乎不会运用 finalize , 所以更多就是为了应付面试.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.hspedu.object_;

//演示 Finalize的用法
public class Finalize_ {
public static void main(String[] args) {

Car bmw = new Car("宝马");
//这时 car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象, 在销毁对象前,会调用该对象的finalize方法
//,程序员就可以在 finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
//,如果程序员不重写 finalize,那么就会调用 Object类的 finalize, 即默认处理
//,如果程序员重写了finalize, 就可以实现自己的逻辑
bmw = null;
System.gc();//主动调用垃圾回收器

System.out.println("程序退出了....");
}
}
class Car {
private String name;
//属性, 资源。。
public Car(String name) {
this.name = name;
}
//重写finalize
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁 汽车" + name );
System.out.println("释放了某些资源...");

}
}


九、断点调试

在断点调试过程中, 是运行状态, 是以对象的 运行类型 来执行的

9.1 介绍

  1. 断点调试是指在程序的某一行设置- 个断点,调试时,程序运行到这一行就会停住,然后你可以一步-步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug
  2. 断点调试是程序员必须掌握的技能。
  3. 断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的Java水平。

9.2 快捷键

  • F7( 跳入) 跳入方法内
  • F8 (跳过) 逐行执行代码.
  • shift+F8 (跳出) 跳出方法
  • F9 (resume,执行到下一个断点)

断点调试快捷键

9.3 应用案例

  • 查看变量的变化情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.hspedu.debug_;

public class Debug01 {
public static void main(String[] args) {
//演示逐行执行代码
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += i;
System.out.println("i=" + i);
System.out.println("sum=" + i);
}
System.out.println("退出for....");
}
}

  • 检查数组越界的异常
1
2
3
4
5
6
7
8
9
10
11
12
package com.hspedu.debug_;

public class Debug02 {
public static void main(String[] args) {
int[] arr = {1, 10, -1};
for (int i = 0; i <= arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("退出for");
}
}

  • 追溯源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.hspedu.debug_;

import java.util.Arrays;

public class Debug03 {
public static void main(String[] args) {
int[] arr = {1, -1, 10, -20 , 100};
//我们看看Arrays.sort方法底层实现.->Debug
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
}

  • 直接执行到下一个断点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hspedu.debug_;

import java.util.Arrays;

//演示执行到下一个断点,同时支持动态的下断点.
public class Debug04 {
public static void main(String[] args) {

int[] arr = {1, -1, 10, -20 , 100};
//我们看看Arrays.sort方法底层实现.->Debug
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}

System.out.println("hello100");
System.out.println("hello200");
System.out.println("hello300");
System.out.println("hello400");
System.out.println("hello500");
System.out.println("hello600");
System.out.println("hello700");
}
}

  • 对象创建过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.hspedu.debug_;

//debug对象创建的过程,加深对调试的理解
public class DebugExercise {
public static void main(String[] args) {
//创建对象的流程
//(1) 加载 Person类信息
//(2) 初始化 2.1默认初始化, 2.2 显式初始化 2.3 构造器初始化
//(3) 返回对象的地址
Person jack = new Person("jack", 20);
System.out.println(jack);
}
}
class Person {
private String name;
private int age;

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

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

  • 还可以根据上一章的代码熟悉 动态绑定机制

十、练习

练习一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.hspedu.homework;

public class Homework01 {

public static void main(String[] args) {

//初始化Person 对象数组,有3个person对象
Person[] persons = new Person[3];
persons[0] = new Person("mary",30, "PHP工程师");
persons[1] = new Person("tom",50, "大数据工程师");
persons[2] = new Person("smith",10, "JavaEE工程师");



//输出当前对象数组
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);//默认对象的.toString()
}

//使用冒泡排序
Person tmp = null;//临时变量,用于交换
for(int i = 0; i < persons.length -1 ;i++) {//外层循环
for(int j = 0; j < persons.length -1 - i; j++) {//内层循环
//并按照 age 从 大到 小进行排序, 如果前面的人的age < 后面人的年龄,就交换
//要求按照名字的长度从小到大 if(persons[i].getName().length() > persons[i+1].getName().length())
if(persons[j].getAge() > persons[j+1].getAge()) {
tmp = persons[j];
persons[j] = persons[j+1];
persons[j+1]= tmp;
}

}
}

System.out.println("排序后的效果");
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);//默认对象的.toString()
}

}

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

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 + '\'' +
'}';
}
}


练习二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.hspedu.homework;


/*
(1) 要求有属性“姓名name”,“年龄age”,“职称post”,“基本工资salary”
(2) 编写业务方法, introduce(),实现输出一个教师的信息。

*/
public class Teacher {
private String name;
private int age;
private String post;
private double salary;
//这里我们在增加一个工资级别
private double grade;

public Teacher(String name, int age, String post, double salary, double grade) {
this.name = name;
this.age = age;
this.post = post;
this.salary = salary;
this.grade = grade;
}

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 getPost() {
return post;
}

public void setPost(String post) {
this.post = post;
}

public double getSalary() {
return salary;
}

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

public double getGrade() {
return grade;
}

public void setGrade(double grade) {
this.grade = grade;
}
public void introduce() {
System.out.println("name: " + name + " age: " + age
+ " post: " + post + " salary:" + salary + " grade:" + grade);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.hspedu.homework;

//子类
public class Professor extends Teacher {

//特有属性...
public Professor(String name, int age, String post, double salary, double grade) {
super(name, age, post, salary, grade);
}

@Override
public void introduce() {
System.out.println(" 这是教授的信息 ");
super.introduce();
}
}

1
2
3
4
5
6
7
8
package com.hspedu.homework;

public class Homework03 {
public static void main(String[] args) {
Professor professor = new Professor("贾宝玉", 30, "高级职称", 30000, 1.3);
professor.introduce();
}
}

练习三

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.hspedu.homework;

import java.sql.SQLOutput;

public class Employee {

//属性
//员工属性:姓名,单日工资,工作天数
private String name;
private double daySal;
private int workDays;
//分析出还有一个属性等级
private double grade;

//方法[构造器,getter 和 setter]
//打印工资方法
//方法 void printSal() {}
public void printSal() {
System.out.println(name + " 工资=" + daySal * workDays * grade);
}

public Employee(String name, double daySal, int workDays, double grade) {
this.name = name;
this.daySal = daySal;
this.workDays = workDays;
this.grade = grade;
}

public String getName() {
return name;
}

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

public double getDaySal() {
return daySal;
}

public void setDaySal(double daySal) {
this.daySal = daySal;
}

public int getWorkDays() {
return workDays;
}

public void setWorkDays(int workDays) {
this.workDays = workDays;
}

public double getGrade() {
return grade;
}

public void setGrade(double grade) {
this.grade = grade;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.hspedu.homework;

public class Worker extends Employee{
//分析普通员工没有特有的属性

public Worker(String name, double daySal, int workDays, double grade) {
super(name, daySal, workDays, grade);
}

//重写printSal
//因为普通员工和Employee输出工资情况一下,所以直接调用父类的printSal()
@Override
public void printSal() {
System.out.print("普通员工 ");//自己的输出信息
super.printSal();//调用父类的方法,复用代码
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.hspedu.homework;

public class Manager extends Employee {
//特有属性
private double bonus;
//创建Manager对象时,奖金是多少并不是确定的,因为老师在构造器中,不给bonus
//,可以通过setBonus
public Manager(String name, double daySal, int workDays, double grade) {
super(name, daySal, workDays, grade);
}

//方法:重写父类的 printSal


@Override
public void printSal() {
//因为经理的工资计算方式和Employee不一样,所以我们重写
System.out.println("经理 " + getName() + " 工资是="
+ (bonus + getDaySal() * getWorkDays() * getGrade()));
}

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.hspedu.homework;

public class Homework04 {
public static void main(String[] args) {
Manager manage = new Manager("刘备", 100, 20, 1.2);
//设置奖金
manage.setBonus(3000);
//打印经理的工资情况
manage.printSal();

Worker worker = new Worker("关羽",50, 10, 1.0);
worker.printSal();

}
}

练习四

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.hspedu.homework.homework5;

public class Employee { //父类
//属性
//分析有一个带薪的月份 13 , 15, 12
private String name;
private double sal;
private int salMonth = 12;
//方法
//打印全年工资
public void printSal() {
System.out.println(name + " 年工资是: " + (sal * salMonth));
}

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

public String getName() {
return name;
}

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

public double getSal() {
return sal;
}

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

public int getSalMonth() {
return salMonth;
}

public void setSalMonth(int salMonth) {
this.salMonth = salMonth;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.hspedu.homework.homework5;

public class Peasant extends Employee {//子类
//属性
//农民,服务生只有基本工资 sal

//方法
public Peasant(String name, double sal) {
super(name, sal);
}
//年工资

@Override
public void printSal() {
System.out.print("农民 ");
super.printSal();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hspedu.homework.homework5;

public class Scientist extends Employee{ //子类

//特有属性
//年终奖 bonus
private double bonus;

//方法
public Scientist(String name, double sal) {
super(name, sal);
}
//重写年工资打印

@Override
public void printSal() {
System.out.print("科学家 ");
System.out.println(getName() + " 年工资是: " + (getSal() * getSalMonth() + bonus));
}

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.hspedu.homework.homework5;

public class Teacher extends Employee{//子类
//特有属性
private int classDays; //一年上课次数
private double classSal; //课时费

public Teacher(String name, double sal) {
super(name, sal);
}
//方法-重写printSal

@Override
public void printSal() { //老师不能使用super.printSal()
System.out.print("老师 ");
System.out.println(getName() + " 年工资是: "
+ (getSal() * getSalMonth() + classDays * classSal ));
}

public int getClassDays() {
return classDays;
}

public void setClassDays(int classDays) {
this.classDays = classDays;
}

public double getClassSal() {
return classSal;
}

public void setClassSal(double classSal) {
this.classSal = classSal;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.hspedu.homework.homework5;

public class Worker extends Employee{ //子类
//属性
//工人,农民,服务生只有基本工资 sal
public Worker(String name, double sal) {
super(name, sal);
}

//方法
@Override
public void printSal() {
System.out.print("工人 ");
super.printSal();//使用父类的printSal()
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hspedu.homework.homework5;

public class Homework05 {
public static void main(String[] args) {
Worker jack = new Worker("jack", 10000);
jack.setSalMonth(15);//灵活额修改带薪月份
jack.printSal();

Peasant smith = new Peasant("smith", 20000);
smith.printSal();

//老师测试
Teacher teacher = new Teacher("顺平", 2000);
//老师有课时费
teacher.setClassDays(360);
teacher.setClassSal(1000);
teacher.printSal();

//科学家
Scientist scientist = new Scientist("钟南山", 20000);
scientist.setBonus(2000000);
scientist.printSal();
}
}

练习五

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.hspedu.homework;

public class Homework07 {
public static void main(String[] args) {

}
}
class Test{ //父类
String name = "Rose";
Test(){
System.out.println("Test");//(1)
}
Test(String name){//name john
this.name = name;//这里把父类的 name 修改 john
}
}
class Demo extends Test{//子类
String name="Jack";
Demo() {
super();
System.out.println("Demo");//(2)
}
Demo(String s){
super(s);
}
public void test(){
System.out.println(super.name);//(3) Rose (5) john
System.out.println(this.name);//(4) Jack (6) Jack
}
public static void main(String[] args) {
//老韩分析
//1. new Demo()
new Demo().test(); //匿名对象
new Demo("john").test();//匿名
}
}

练习六

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hspedu.homework;

public class BankAccount {//父类
private double balance ;//余额
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
//存款
public void deposit(double amount) {
balance += amount;
}
//取款
public void withdraw(double amount) {
balance -= amount;
}

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hspedu.homework;


/*
在上面类的基础上扩展 新类CheckingAccount对每次存款和取款都收取1美元的手续费
*/
public class CheckingAccount extends BankAccount{//新的账号
//属性
public CheckingAccount(double initialBalance) {
super(initialBalance);
}

@Override
public void deposit(double amount) {//存款
super.deposit(amount - 1);//巧妙的使用了父类的 deposit
//1 块钱转入银行的账号
}

@Override
public void withdraw(double amount) {//取款
super.withdraw(amount + 1);
//1 块钱转入银行的账号
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.hspedu.homework;

/*
扩展前一个练习的BankAccount类,
新类SavingsAccount每个月都有利息产生(earnMonthlyInterest方法被调用),
并且有每月三次免手续费的存款或取款。在earnMonthlyInterest方法中重置交易计数
*/
public class SavingsAccount extends BankAccount {

//老韩分析
//新增加属性
private int count = 3;
private double rate = 0.01;//利率

public void earnMonthlyInterest() {//每个月初,我们统计上个月的利息,同时将count=3
count = 3;//
super.deposit(getBalance() * rate);
}

@Override
public void deposit(double amount) {
//判断是否还可以免手续费
if(count > 0) {
super.deposit(amount);
} else {
super.deposit(amount - 1);//1 块转入银行
}
count--;//减去一次
}

@Override
public void withdraw(double amount) {//取款
//判断是否还可以免手续费
if(count > 0) {
super.withdraw(amount);
} else {
super.withdraw(amount + 1);//1 块转入银行
}
count--;
}

public SavingsAccount(double initialBalance) {
super(initialBalance);
}


public int getCount() {
return count;
}

public void setCount(int count) {
this.count = count;
}

public double getRate() {
return rate;
}

public void setRate(double rate) {
this.rate = rate;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.hspedu.homework;

public class Homework08 {
public static void main(String[] args) {
// CheckingAccount checkingAccount = new CheckingAccount(1000);
// checkingAccount.deposit(10);// 1010 - 1 = 1009
// checkingAccount.withdraw(9);//1009 - 9 = 1000 -1= 999
// System.out.println(checkingAccount.getBalance());

//测试SavingsAccount
SavingsAccount savingsAccount = new SavingsAccount(1000);
savingsAccount.deposit(100);
savingsAccount.deposit(100);
savingsAccount.deposit(100);
System.out.println(savingsAccount.getBalance());//1300
savingsAccount.deposit(100);
System.out.println(savingsAccount.getBalance());//1400-1=1399

//月初,定时器自动调用一下 earnMonthlyInterest
savingsAccount.earnMonthlyInterest();//统计利息
System.out.println(savingsAccount.getBalance());//1399 + 13.99 =1412.99
savingsAccount.withdraw(100);//免手续
System.out.println(savingsAccount.getBalance());//1412.99 -100 =1312.99
savingsAccount.withdraw(100);//免手续
savingsAccount.withdraw(100);//免手续
System.out.println(savingsAccount.getBalance());//1412.99 -200 =1112.99
savingsAccount.deposit(100);//扣手续费
System.out.println(savingsAccount.getBalance());//1112.99 + 100 = 1212.99 - 1 = 1211.99

}
}


练习七

1
2
3
4
5
6
7
8
9
10
11
12
package com.hspedu.homework;

public class Point {
private double x;
private double y;

public Point(double x, double y) {
this.x = x;
this.y = y;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.hspedu.homework;

public class LabeledPoint extends Point {
//特有属性
private String label;

public LabeledPoint(String label, double x, double y) {
super(x, y);
this.label = label;
}
//方法
}

1
2
3
4
5
6
7
8
package com.hspedu.homework;

public class Homework09 {
public static void main(String[] args) {
new LabeledPoint("Black",1929,230.07);
}
}

练习八

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.hspedu.homework;

public class Doctor {
//属性
//{name, age, job, gender, sal}
private String name;
private int age;
private String job;
private char gender;
private double sal;
//5个参数的构造器

public Doctor(String name, int age, String job, char gender, double sal) {
this.name = name;
this.age = age;
this.job = job;
this.gender = gender;
this.sal = sal;
}

//方法
//相应的getter()和setter()方法

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

public char getGender() {
return gender;
}

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

public double getSal() {
return sal;
}

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

//重写父类(Object)的equals()方法:public boolean equals(Object obj),并判断测试类中创建的两个对象是否相等。相等就是判断属性是否相同
public boolean equals(Object obj) {
//判断两个比较对象是否相同
if (this == obj) {
return true;
}
//判断obj 是否是 Doctor类型或其子类
//过关斩将 校验方式
if (!(obj instanceof Doctor)) { //不是的话
return false;
}

//向下转型, 因为obj的运行类型是Doctor或者其子类型
Doctor doctor = (Doctor)obj;
return this.name.equals(doctor.name) && this.age == doctor.age &&
this.gender == doctor.gender && this.job.equals(doctor.job) && this.sal == doctor.sal;

}
}

1
2
3
4
5
6
7
8
9
10
11
12
package com.hspedu.homework;

public class Homework10 {
public static void main(String[] args) {
//测试
Doctor doctor1 = new Doctor("jack", 20, "牙科医生", '男', 20000);
Doctor doctor2 = new Doctor("jack", 21, "牙科医生", '男', 20000);

System.out.println(doctor1.equals(doctor2));//T
}
}

练习九

练习十

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.hspedu.homework.homework13;
/*
抽取一个父类Person类,将共同属性和方法放到Person类
*/
public class Person {//父类
private String name;
private char gender;
private int age;
//方法

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

public String getName() {
return name;
}

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

public char getGender() {
return gender;
}

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

public int getAge() {
return age;
}

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

//编写一个play 方法, 把共有的输出内容写到父类
public String play() {
return name + "爱玩";
}
//返回一个基本信息
/*
姓名:张飞
年龄:30
性别:男
*/
public String basicInfo() {
return "姓名: " + name + "\n年龄: " + age + "\n性别: " + gender;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.hspedu.homework.homework13;

/*
Student类有名称(name),性别(sex),年龄(age),学号(stu_id),
做合理封装,通过构造器在创建对象时将4个属性赋值。

学生需要有学习的方法(study),在方法里写生“我承诺,我会好好学习。”
*/
public class Student extends Person{ //
//属性

private String stu_id;
//方法
public Student(String name, char gender, int age, String stu_id) {
super(name, gender, age);
this.stu_id = stu_id;
}

public String getStu_id() {
return stu_id;
}

public void setStu_id(String stu_id) {
this.stu_id = stu_id;
}

//我承诺,我会好好学习
public void study() {
System.out.println(getName() + "承诺,我会好好学习 老韩讲的 java");
}

/**
* 学生爱玩足球
* @return
*/
@Override
public String play() {
return super.play() + "足球";
}

//编写一个输出信息的方法,这样体现封装
public void printInfo() {
System.out.println("学生的信息:");
System.out.println(super.basicInfo());
System.out.println("学号: " + stu_id);
study();//组合, 变化万千
System.out.println(play());
}

@Override
public String toString() {
return "Student{" +
"stu_id='" + stu_id + '\'' +
'}' + super.toString();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.hspedu.homework.homework13;

/*
写一个Teacher类,Teacher类有名称(name),性别(sex),年龄(age),工龄(work_age),
做合理封装,通过构造器在创建对象时将4个属性赋值
*/
public class Teacher extends Person {
//属性
private int work_age;
//方法

public Teacher(String name, char gender, int age, int work_age) {
super(name, gender, age);
this.work_age = work_age;
}


public int getWork_age() {
return work_age;
}

public void setWork_age(int work_age) {
this.work_age = work_age;
}

//教师需要有教学的方法(teach),在方法里写上“我承诺,我会认真教学。
public void teach() {
System.out.println(getName() + "承诺,我会认真教学 java...");
}
/**
* 老师爱玩象棋
*/
@Override
public String play() {
return super.play() + "象棋";
}
//输出信息方法
public void printInfo() {
System.out.println("老师的信息:");
System.out.println(super.basicInfo());
System.out.println("工龄: " + work_age);
teach();
System.out.println(play());
}

@Override
public String toString() {
return "Teacher{" +
"work_age=" + work_age +
'}' + super.toString();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.hspedu.homework.homework13;

public class Homework13 {
public static void main(String[] args) {

//测试老师
Teacher teacher = new Teacher("张飞", '男', 30, 5);

teacher.printInfo();

//测试
Student student = new Student("小明", '男', 15, "00023102");
System.out.println("-----------------------------------");
student.printInfo();//封装


//定义多态数组,里面保存2个学生和2个教师,要求按年龄从高到低排序
Person[] persons = new Person[4];
persons[0] = new Student("jack", '男', 10, "0001");
persons[1] = new Student("mary", '女', 20, "0002");
persons[2] = new Teacher("smith", '男', 36, 5);
persons[3] = new Teacher("scott", '男', 26, 1);

//创建对象
Homework13 homework13 = new Homework13();
homework13.bubbleSort(persons);

//输出排序后的数组
System.out.println("---排序后的数组-----");
for(int i = 0; i < persons.length; i++) {
System.out.println(persons[i]);
}

//遍历数组,调用test方法
System.out.println("=======================");
for (int i = 0; i < persons.length; i++) {//遍历多态数组
homework13.test(persons[i]);
}

}

//定义方法,形参为Person类型,功能:调用学生的study或教师的teach方法
//分析这里会使用到向下转型和类型判断
public void test(Person p) {
if(p instanceof Student) {//p 的运行类型如果是Student
((Student) p).study();
} else if(p instanceof Teacher) {
((Teacher) p).teach();
} else {
System.out.println("do nothing...");
}
}

//方法,完成年龄从高到底排序
public void bubbleSort(Person[] persons) {
Person temp = null;
for (int i = 0; i < persons.length - 1; i++) {
for (int j = 0; j < persons.length - 1 - i; j++) {
//判断条件, 注意这里的条件可以根据需要,变化
if(persons[j].getAge() < persons[j+1].getAge()) {
temp = persons[j];
persons[j] = persons[j + 1];
persons[j + 1] = temp;
}
}
}
}

}

练习十一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hspedu.homework;

public class Homework15 {
public static void main(String[] args) {
AAA obj = new BBB();//向上转型
AAA b1 = obj;
System.out.println("obj的运行类型=" + obj.getClass());//BBB
obj = new CCC();//向上转型

System.out.println("obj的运行类型=" + obj.getClass());//CCC
obj = b1;

System.out.println("obj的运行类型=" + obj.getClass());//BBB
}
}

class AAA {//超类

}
class BBB extends AAA {//父类

}
class CCC extends BBB {//子类

}