07Java面向对象-中
Eclipse 快捷键
补全代码 Ctrl + /
快速修复 Ctrl + 1
批量导包 Ctrl + shift + o
批量单行注释 Ctrl + /
使用多行注释 Ctrl + shift + \
取消多行注释 Ctrl + alt + down 或 Ctrl + alt + up
删除指定行代码 Ctrl + d
上下移动代码 alt + up 或者 alt + down
切换到下一行代码空位 shift + enter
切换到上一行代码空位 Ctrl + shift + enter
查看源码 Ctrl + 选中指定结构
光标选中指定类,查看继承结构 Ctrl + t
调出生成 getter/setter 构造器等结构:alt + shift + s
Ctrl + t 查看类的继承关系
完成项目二
类的继承性
继承性的好处
- 减少了代码的冗余,提高代码复用性
- 便于扩展功能
- 为之后多态的使用提供了前提
继承性的体现
- 继承的格式为
class A extends B{}
, 表示为 A 继承了 B,A 为子类,B 为父类 - 一旦 A 继承了B那么A类中就获取了B类中的所有属性和方法
- 子类继承父类以后还可以拥有自己的属性和方法实现功能上的扩展、延申
Java中关于继承的规定
- 一个类可以被多个子类继承
- Java中类为单继承性,一个类只能有一个父类
- 子父类是相对概念
- 子类直接继承的父类称为直接父类,间接继承的父类称为简介父类
- 子类继承父类后就获取了直接父类和间接父类的所有属性和方法
Object 类
- Java中所有的类都直接或者间接的继承 Object 类
- 当我们没有显示的给一个类设计继承关系时,则默认的直接继承 Object
- 所有的类都拥有 Object 类的所有属性和方法
1 | public class ExtendsTest01 { |
Java中多重继承的体现
1 | public class ExtendsTest02 { |
Eclipse 调试
1.新建和删除断点,在需要设置的断点的行号前双击即可添加一个断点,再次双击这个断点就会取消断点
2.开始调试
3.在打开的调试页面一行行执行代码,查看变量值和程序运行逻辑
方法重写
1.定义:子类继承父类后,可以对父类中的同名方法进行重写覆盖操作
2.应用:重写以后,当创建了子类对象后,调用子类中重写父类的同名同参方法时,实际调用的是子类中重写的方法,不会再去调用父类中的同名方法
3.重写的规定
- 子类中叫做重写的方法,父类中叫做被重写的方法
- 子类中重写的方法名和参数名应该都和父类相同
- 子类重写的方法的权限修饰符不小于父类被重写方法的权限修饰符
- 特殊情况:子类不能重写父类中权限是 private 的方法
- 返回值的类型
- 父类中被重写的方法返回值时 void 时,子类中重写的方法返回值也只能是 void
- 父类中被重写的方法的返回值是 A 类,则子类中重写的方法返回值可以是 A 类或者 A 的子类
- 父类中被重写的方法返回值是基本数据类型时,子类中的重写方法的返回值也必须和父类被重写的方法的返回值相同
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
4.子类和父类同名同参的方法要么都是 static 或者要么都不是 static 类型
1 | /** |
1 | public class OverridesTest04 extends Overrides03{ |
再看权限修饰符
- private
- 缺省 define
- protected
- public
设置一个父类有四种权限级别的属性和方法
1 | public class QuanXian05 { |
在同包不同类中使用情况,可以使用 default(缺省)、protected、public
1 | public class QuanXianTest06 { |
在不同包的子类中,能使用 protected、public
1 | import main.songzx.java01.QuanXian05; |
在不同包下不是子类中,只能使用 public
1 | import main.songzx.java01.QuanXian05; |
super 使用
- 我们可以在子类的方法或者构造器中使用
super.属性
或super.方法
的方法来显示的调用父类的属性和方法,但是在通常情况下,我们会省略 super. 这样默认会使用 this. 的方法来调用,this 会先查找本类中的属性和方法,找不到是在去父类中查找,而 super. 会直接去父类查找 - 当子类和父类中定义了同名的属性时,我们想要在子类中使用父类的属性,则必须显示的使用
super.属性
,表明调用的是父类中属性 - 当子类和父类中定义了同名的方法时,我们想要在子类中使用父类的方法,也必须使用
super.方法
来调用父类中的方法,表明调用的是父类中被重写的方法
例如:在父类中声明一个 int 类型的 id,和一个 show 方法
1 | public class Super07 { |
然后在子类中使用 super 来显示的调用父类中的属性和方法
1 | public class SuperTest08 { |
super 调用构造器
- 我们可以在子类中显示的使用
super(形参列表)
来调用父类中指定的构造器 super(形参列表)
必须定义在子类构造器的首行- 我们在类的构造器中使用
super(形参列表)
或者this(形参列表)
只能二选一,不能同时存在 - 我们在构造器首行如果没有显示的声明
super(形参列表)
或者this(形参列表)
默认使用的是super(形参列表)
- 在类的 n 个构造器中至少有一个类的构造器使用了
super(形参列表)
在父类中声明一个带参数的构造器
1 | public class Super07 { |
在子类中使用 super 显示的调用父类中的构造器
1 |
|
子类对象实例化的过程
从结果上看(继承性)
- 子类继承父类后,就获取了父类中的所有属性和方法
- 创建子类对象,在堆空间中就会加载所有父类的属性和方法
从过程上看
当我们使用子类的构造器创建子类对象时,我们一定会直接或者间接的调用父类的构造器,然后父类会直接或者间接的调用父类的父类的构造器,一直调用到 java.long.Object 的空参构造器为止。正因为加载过所有父类的构造器,所以才可以看到内存中父类的结构,子类对象才可以进行调用
明确:虽然创建子类对象时调用了父类的构造器,但是自始至终就创建了一个对象,就是 new 出来的那个对象
super 和继承性练习
多态性的使用
1.理解多态性
- 可以理解为一种事物的多种形态
2.什么是多态
- 对象的多态,父类的引用指向子类的对象,或子类的对象赋给父类的引用
3.多态的使用,虚拟方法调用
- 有了对象的多态性以后,我们在编译器只能调用父类中声明的方法,但在运行期,我们执行的是子类重写父类的方法。
- 总结:编译看左边,运行看右边
4.多态性使用的前提
- 类的继承关系
- 子类重写父类的方法
1 | /** |
1 | /** |
新建一个测试类,理解多态的使用
1 | /** |
虚拟方法
正常情况下,调用类中的方法是这样的
1 | public class Person01 { |
虚拟方法的调用(多态的情况下)
子类中定义了和父类同名同参的方法,在多态的情况下,此时父类的方法被称为虚拟方法。父类根据赋给他的不同子类对象,动态调用属于子类的方法,这样的方法在编译期是无法确定的,只有在运行时期才能确定。
1 | public class Person01 { |
编译时类型和运行时类型
上述代码在编译时 p 为 PersonTest 类型,而 eat 方法调用是在运行时确定的,调用的是 Student 类中的 eat 方法,这中称为 动态绑定
区分重载和重写
定义不同
- 重载:在同一个类中,我们可以定义同名不同参的方法,同名方法之间就构成了重载,构造器也可以进行重载
- 重写:在子类继承父类的前提下,子类可以定义和父类同名同参的方法,这样就构成了重写
定义的规则不同
- 重载:两同一不同:同一个类、同一个方法名、形参列表不同
- 重写:子父类之间有继承关系,同一个方法名,形参列表相同,返回值相同,子类的权限大于等于父类权限,抛出的异常小于等于父类
重载不认为是多态性的体现,重写认为是多态性。
向下转型的使用
1 | public class DownType { |
instanceof 关键字的使用
1 | /** |
多态调用与属性无关
1 | /** |
多态调用练习
1 | public class BaseTest { |
equals 使用
1.回顾 == 的使用
- 基本数据类型使用 == 判断时比较的是具体的值,除了不能和布尔类型的比较外,其他的基本数据类型都可以使用 == 来判断两个数据是否相等
- 当两个引用数据类型的数据使用 == 判断时则比较的是引用地址
2.equals 的使用
equals 是一个方法,首先就排除了基本数据类型使用 equals
Object 中的 equals 方法定义比较的还是两个引用数据类型的地址,源码如下
1
2
3
4// boolean java.lang.Object.equals(Object obj)
public boolean equals(Object obj) {
return (this == obj);
}像 String、Date、File、包装类等都重写了 Object 中的 equals 方法,使之比较的是两个对象的 “实体内容” 是否相等
通常情况下,我们自定义类使用 equals 比较的话也是比较两个对象的 实例内容 是否相等,那么我们就需要对 Object 类中的 equals 方法进行重写
重写的原则:比较两个对象的实体内容是否相等
1 | // 引用数据类型的比较 |
重写 equals
一般情况下,我们使用 equals 并不是想判断两个对象实例的引用地址是否相同,而是想像 == 那样判断两个实例的具体值是否相等。那么针对这种情况我们就需要对 equals 对象进行重写
以 Customer 类为例,来重写 Object 类中的 equals 方法。
1 | // 声明一个Customer类,里面有age和name两个属性,和一个构造器,以及重写的equals方法 |
重写完成之后 new 出来两个对象,使用 equals 判断两个对象是否相等
1 | // 自定义类比较 |
自动生成 equals 方法
在 eclipse 开发工具中我们也可以自动生成 equals 方法,具体操作如下
1.首先点击顶部菜单栏中的 Source
2.在弹出的菜单中选择 Generate hashCode() and equals()
,然后选择要进行比较的数据,点击 Generate
3.系统会自动生成 hashCode()
和 equals()
1 |
|
== 和 equals 的区别
- == 即可以比较基本数据类型也可以比较引用数据类型,对于基本数据类型比较的是具体的值,对于引用数据类型比较的是内存地址
- equals 是 java.lang.Object 类里面的一个方法,如果该方法没有被重写,则默认也是 ==,String、Date等类的 equals 方法是被重写过的
- 通常情况下。重写 equals 方法都是比较类中的属性是否都相等
toString 的使用
toString 的使用前提是值不能是 null,否则会报错
1 | public class ToStringTest { |
单元测试的方法
步骤:
- 1.选中当前工程 - 右键选择:build path -add libraries - JUnit 4 下一步
- 2.创建一个Java类,进行单元测试
- 此时的Java类要求
- 此类时 public 类型的
- 此类提供一个空参的构造器,默认的就可以
- 3.在类中声明单元测试方法
- 方法的权限是 public
- 方法没有返回值,没有参数
- 4.在单元测试上添加注解:@Test,并在单元测试类中导入
import org.junit.Test
- 5.声明好单元测试方法以后,就可以在方法体内测试相关代码
- 6.写完代码后,左键双击方法名,右键选择
run as - JUnit Test
1 | // 要在类中导入单元测试包 |
包装类(Wrapper)
- 针对八种基本数据类型定义相应的引用类型 – 包装类
- 有了类的特点,就可以调用类中的方法
- 除了 int、char 这两种基本数据类型的包装类名称有点不同,其他基本数据类型的包装类名称都是首字母大写的类
基本数据类型 | 对应的包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
基本数据类型转换为包装类
1 | import org.junit.Test; |
包装类转换成基本数据类型
调用某个包装类的 xxxValue() 方法可以返回这个基本数据类型的数据,然后用这个基本数据类型来接收,就得到该基本数据类型的数据
1 | // 声明一个 int 包装类 |
包装类之自动装箱和自动拆箱
此功能为jdk5.0新特性,在此版本之前不能使用
自动装箱:
表示我们声明某个类型的包装类时不用再通过 new 的方式去创建,而是可以让某个包装类直接等于某个基本数据类型的数据
1 | // 自动装箱,可以让一个包装类直接等于基本数据类型的数据,不用在通过 new 类创建 |
自动拆箱:
自动拆箱表示我们可以使用一个基本数据类型去等于该类型包装类的数据,不需要在通过 xxxValue() 的方式去获取该类型包装类的数据
1 | // 自动拆箱。我们可以使用基本数据类型等于该基本数据类型的包装类 |
String 类型和 基本数据类型、包装类之间相互转换
基本数据类型转换成 String
调用 String 类型的 valueOf 方法
1 | // 基本数据类型转换成 String 类型 |
包装类转换成 String
1 | // 包装类转换成 String |
String 类型转换成基本数据类型
调用该基本数据类型包装类的 parseXXX 方法,例如: Integer.parseInt()
1 | // String 类型转换成基本数据类型 |
String 类型转换成包装类
1 | // String 类型的数据转换成包装类 |
包装类的常见面试题
三元运算符中表达式1和表达式2必须可以统一成一种类型
下面的第一个题目中 int 类型自动提升成了 double 类型,所以结果打印 1.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 在三元运算符中,先不管最终那个代码会被执行
// 会先要求两个运算返回结果的类型必须统一
// 然后再去进行三元判断
// 下面的题目中 int 类型会自动向上类型提升为 double,所以结果打印 1.0
Object i1 = true ? new Integer(1) : new Double(1.0);
System.out.println(i1.toString()); //=> 1.0
// 两个执行代码中类型提升和三元判断有关,if 判断不会自动类型提升
Object i2;
if(true) {
i2 = new Integer(1);
}else {
i2 = new Double(1.0);
}
System.out.println(i2.toString()); //=> 1Integer 包装类中缓存好的包装类数组
查看下面题目中的打印结果
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/*
* 显示的使用 new 类中声明了一个 int 类型的包装类
* 此时 i1 和 i2 的数据类型是引用类型
* 虽然默认读取i1和i2会走toString方法返回具体的数字
* 但是在使用 == 比较两个引用数据类型时比较的是地址值,所以返回 false
* 使用 equals 去判断时返回的是 true
*/
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1 == i2); //=> false
System.out.println(i1.equals(i2)); //=> true
/*
* 在 Integer 内部定义了一个 IntegerCache 结构
* IntegerCache 中定义了一个 Integer 的数组
* 这个数组中保存了从 -128 ~ 127 范围内的整数的包装类
* 如果我们使用自动装箱的方式,给 Integer 赋值的范围在 -128~127之间
* 则使用的是数组中的元素,因此当使用自动装箱声明了两个相同的在这个范围内的值给 Integer 时
* 他们两个指向的地址值的相同的,所以用 == 去判断这个两个地址值一定会返回 true
* 当赋的值超过了这个范围时,就相当于重新 new 了一个 Integer,所以地址值是不相同的,固结果返回 false
*/
Integer j1 = 1;
Integer j2 = 1;
System.out.println(j1 ==j2); //=> true
// 因为声明的值超出了 -128~127 这个范围,相当于重新new了两个Integer,所以地址值是不一样的
Integer k1 = 128;
Integer k2 = 128;
System.out.println(k1 == k2); //=> false包装类和自动拆箱自动装箱练习
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
49import java.util.Scanner;
import java.util.Vector;
/*
* 自动装箱和自动拆箱练习
*/
public class ScoreTest {
public static void main(String[] args) {
Vector v = new Vector();
Scanner s = new Scanner(System.in);
int Max = 0;
for(;;) {
System.out.println("请输入学生成绩(负数结束)");
int score = s.nextInt();
if(score < 0) {
break;
}
if(score > 100) {
continue;
}
// 将score转换成Integer
// jdk5.0之前
// Integer scoreGer = new Integer(score);
// 获取一个最大值
Max = Max > score ? Max : score;
// jdk5.0之后可以直接传递
v.addElement(score);
}
System.out.println("最高分是:" + Max);
char level;
for(int i = 0; i < v.size(); i++) {
// 下面的操作分为两步
// 1.先将 Object 类型强转成了 Integer
// 2.然后利用自动拆箱,将 Integer 转换成 int 类型
int score = (Integer)v.elementAt(i);
if(Max - score <= 10) {
level = 'A';
}else if(Max - score <= 20) {
level = 'B';
}else if(Max - score <= 30) {
level = 'C';
}else {
level = 'D';
}
System.out.println("第" + (i+1) +
"位的学生成绩为:" + score + "。成绩等级是:" + level);
}
}
}