面向对象二

面向对象二

继承性(inheritance)

为描述和处理个人信息,定义类Person:

1
2
3
4
5
6
7
8
9
class Person{
public String name;
public int age;
public Date birthDate;

public String getInfo(){

}
}

为描述和处理学生信息,定义Student:

1
2
3
4
5
6
7
8
9
10
class Student{
public String name;
public int age;
public Date birthDate;
public String school;

public String getInfo(){

}
}

通过继承,简化Student类的定义

1
2
3
4
5
6
7
8
9
10
11
12
class Person{
public String name;
public int age;
public Date birthDate;

public String getInfo(){

}
}
class Student extends Person{
public String school;
}

Student类继承父类Person的所有属性和方法,并增加一个属性school

Person中的属性和方法,Student都可以使用

img

为什么要有类的继承性?(继承性的好处)

① 减少了代码的冗余,提高了代码的复用性

② 便于功能的扩展

③ 为之后多态性的使用,提供了前提

注意:不要仅为了获取其他类中的某个功能而去继承

继承性的格式

class A extends B{}

A:子类、派生类、subclass

B:父类、超类、基类、superclass

子类继承父类以后有哪些不同

体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。

特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已,可以通过在父类中写getter()与setter()方法,使子类或测试类进行调用

image-20200205171443097

子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。

子类和父类的关系,不同于子集和集合的关系。

extends:延展、扩展

Java中继承性的说明

  1. 一个类可以被多个子类继承

  2. Java中类的单继承性:一个类只能有一个父类

  3. 子父类是相对的概念

  4. 子类直接继承的父类,称为:直接父类;间接继承的父类称为:间接父类

  5. 子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法

img

Java.lang.Object类的理解

  1. 如果我们没显式的声明一个类的父类的话,则此类继承于java.lang.Object类

  2. 所有的java类(除java.lang.Object类)之外都直接或间接的继承于java.lang.Object类

  3. 意味着,所的java类具有java.lang.Object类声明的功能。

方法的重写

  1. 什么是方法的重写(override 或 overwrite)?

子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作

  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
class Circle{

public double findArea(){}//求面积

}

class Cylinder extends Circle{

public double findArea(){}//求表面积

}

class Account{

public boolean withdraw(double amt){}

}

class CheckAccount extends Account{

public boolean withdraw(double amt){}

}
  1. 重写的规则

方法的声明:

1
2
3
 权限修饰符  返回值类型  方法名(形参列表) throws 异常的类型{
//方法体
}

约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法

① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同

② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符

  • 特殊情况:子类不能重写父类中声明为private权限的方法

    ③ 返回值类型:

  • 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void

  • 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类

  • 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)

④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)

  • 子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写,因为static方法是属于类的,子类无法覆盖父类的方法)

面试题

区分方法的重写和重载?

① 二者的概念:

② 重载和重写的具体规则

③ 重载:不表现为多态性。

​ 重写:表现为多态性。

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

四种访问权限修饰符

1577591654571

关键字super

super 关键字

可以理解为:父类的

可以用来调用的结构

属性、方法、构造器

super调用属性、方法

  • 我们可以在子类的方法或构造器中。通过使用”super.属性”或”super.方法”的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略”super.”

  • 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用”super.属性”的方式,表明调用的是父类中声明的属性。

  • 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用”super.方法”的方式,表明调用的是父类中被重写的方法。

super调用构造器

  • 我们可以在子类的构造器中显式的使用”super(形参列表)”的方式,调用父类中声明的指定的构造器

  • “super(形参列表)”的使用,必须声明在子类构造器的首行!

  • 我们在类的构造器中,针对于”this(形参列表)”或”super(形参列表)”只能二一,不能同时出现,且必须放在构造器的首行

  • 在构造器的首行,没显式的声明”this(形参列表)”或”super(形参列表)”,则默认调用的是父类中空参的构造器:super()

  • 在类的多个构造器中,至少一个类的构造器中使用了”super(形参列表)”,调用父类中的构造器

  • 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参构造器,则编译出错

image-20200205222233083 image-20200205222308142

this与super的区别

区别点 this super
访问属性 访问本类中的属性,如果本类没有此属性则从父类中继续查找 直接访问父类中的属性
调用方法 访问本类中的方法,如果本类没有此方法则从父类中继续查找 直接访问父类中的方法
调用构造器 调用本类的构造器,必须放在构造器的首行 调用父类构造器,必须放在子类构造器的首行

子类对象实例化全过程

从结果上看

继承性

  • 子类继承父类以后,就获取了父类中声明的属性或方法。

  • 创建子类的对象,在堆空间中,就会加载父类中所声明的属性。

从过程上看

当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。

图示:

img

强调说明

虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

img

无论通过哪个构造器创建字类对象,需要保证先初始化父类

目的:

当子类继承父类后,“继承”父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化

多态性

多态性的理解

可以理解为一个事物的多种形态

何为多态性

对象的多态性:

父类的引用指向子类的对象(或子类的对象赋给父类的引用)

举例:

Person p = new Man();

Object obj = new Date();

多态性的使用

虚拟方法调用

  • 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

  • 总结:编译,看左边;运行,看右边。

多态性的使用前提

① 类的继承关系 ② 方法的重写

多态性的应用举例

举例一

1
2
3
4
5
public void func(Animal animal){
//Animal animal = new Dog();
animal.eat();
animal.shout();
}

举例二

1
2
3
4
//其中Object利用的就是多态
public void method(Object obj){

}

举例三

1
2
3
4
5
6
7
8
9
10
11
12
class Driver{

public void doData(Connection conn){
//conn = new MySQlConnection(); / conn = new OracleConnection();
//规范的步骤去操作数据
//conn.method1();
//conn.method2();
//conn.method3();

}

}

多态性使用的注意点

对象的多态性,只适用于方法,不适用于属性(在调用属性的时候编译和运行都看左边)

关于向上转型与向下转型

  • 向上转型:多态

  • 向下转型:

  • 为什么使用向下转型:

    有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?

    使用向下转型。

没有子父类关系进行强转会报错

img

如何实现向下转型:

使用强制类型转换符:()

使用时的注意点:

① 使用强转时,可能出现ClassCastException的异常。

② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。

instanceof的使用:

① a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。

② 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。

③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误。

图示:

img

面试题

谈谈你对多态性的理解?

① 实现代码的通用性。

② Object类中定义的public boolean equals(Object obj){ }

​ JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)

③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)

多态是编译时行为还是运行时行为?(可以叫做动态绑定)

运行时行为

子类能否获取父类中private权限的属性或方法?(可以)

权限控制解决是否能调用,继承性解决能否获取的问题

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
/*
* 练习:
* 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
* 系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
*
* 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
* 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
*/

class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;//多态性
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b == s);//true
System.out.println(b.count);//10
b.display();//20
}
}

多态性只针对方法,不针对属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StringTest02 {
public static void main(String[] args) {
Base1 base = new Sub1();
base.add(1, 2, 3);
Sub1 s = (Sub1)base;
s.add(1,2,3);
}
}
class Base1 {
public void add(int a, int... arr) {
System.out.println("base1");
}
}
class Sub1 extends Base1 {
@Override
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a,int b,int c){
System.out.println("sub_2");
}
}

输出结果

1
2
sub_1
sub_2

注:

int... arr可以当成int[] arr,但是int[] arr不能当成int... arr

Object类的使用

java.lang.Object类的说明

  1. Object类是所Java类的根父类
  2. 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
  3. Object类中的功能(属性、方法)具有通用性
  • 属性:无

  • 方法:equals()、toString()、getClass()、hashCode() 、clone() 、finalize()、wait() 、 notify()、notifyAll() 9个

  1. Object类只声明了一个空参的构造器

equals()方法

equals()的使用

  1. 是一个方法,而非运算符

  2. 只能适用于引用数据类型

  3. Object类中equals()的定义

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

在进行字符串和对象进行比较的时候,把对象放在括号中防止空指针异常

说明:

Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体

img

  1. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是

    两个引用的地址是否相同,而是比较两个对象的”实体内容”是否相同。

  2. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的”实体内容”是否相同。那么,我们就需要对Object类中的equals()进行重写,重写的原则:比较两个对象的实体内容是否相同.

如何重写equals()

手动重写举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User{
String name;
int age;
//重写其equals()方法
public boolean equals(Object obj){
if(obj == this){
return true;
}
if(obj instanceof User){
User u = (User)obj;
return this.age == u.age && this.name.equals(u.name);
}
return false;
}
}

img

开发中如何实现

自动生成的

String中重写equals方法

img

img

回顾 == 运算符的使用

== :运算符

  1. 可以使用在基本数据类型变量和引用数据类型变量中

  2. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)

    如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

补充: == 符号使用时,必须保证符号左右两边的变量类型一致。

toString()方法

toString()的使用

  1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()(输出地址)

  2. Object类中toString()的定义:

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  1. 像String、Date、File、包装类等都重写了Object类中的toString()方法。

    使得在调用对象的toString()时,返回”实体内容”信息

  2. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的”实体内容”

如何重写toString()

举例:

1
2
3
4
5
//自动实现
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}

面试题

① final、finally、finalize的区别?

② == 和 equals() 区别

包装类的使用

为什么要有包装类(或封装类)

为了使基本数据类型的变量具有类的特征,引入包装类。

基本数据类型与对应的包装类

img

需要掌握的类型间的转换

(基本数据类型、包装类、String)

img

基本数据类型<—>包装类

JDK 5.0 新特性,自动装箱 与自动拆箱

基本数据类型、包装类—>String

调用String重载的valueOf(Xxx xxx)

String—>基本数据类型、包装类

调用包装类的parseXxx(String s)

注意:转换时,可能会报NumberFormatException

应用场景举例

① Vector类中关于添加元素,只定义了形参为Object类型的方法:

​ v.addElement(Object obj); //基本数据类型 —>包装类 —>使用多态

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import org.junit.Test;
/*
* 包装类的使用:
* 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
*
* 2.掌握的:基本数据类型、包装类、String三者之间的相互转换
*/
public class WrapperTest {

//String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
@Test
public void test5(){
String str1 = "123";
String str2 = "123a";//报错
//错误的情况:
//int num1 = (int)str1;
//Integer in1 = (Integer)str1;
//可能会报NumberFormatException
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);//124

String str3 = "true1";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);//false
}

//基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)
@Test
public void test4(){
int num1 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式2:调用String的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//12.3

Double d1 = new Double(12.4);
String str3 = String.valueOf(d1);
System.out.println(str2);
System.out.println(str3);//12.4

}
/*
* JDK 5.0 新特性:自动装箱 与自动拆箱
*/
@Test
public void test3(){
//int num1 = 10;
//基本数据类型-->包装类的对象
//method(num1);

//自动装箱:基本数据类型 --->包装类
int num2 = 10;
Integer in1 = num2;//自动装箱

boolean b1 = true;
Boolean b2 = b1;//自动装箱

//自动拆箱:包装类--->基本数据类型
System.out.println(in1.toString());//10
int num3 = in1;//自动拆箱
}
public void method(Object obj){
System.out.println(obj);
}

//包装类--->基本数据类型:调用包装类Xxx的xxxValue()
@Test
public void test2(){
Integer in1 = new Integer(12);

int i1 = in1.intValue();
System.out.println(i1 + 1);//13

Float f1 = new Float(12.3);
float f2 = f1.floatValue();
System.out.println(f2 + 1);//13.3
}

//基本数据类型 --->包装类:调用包装类的构造器
@Test
public void test1(){
int num1 = 10;
//System.out.println(num1.toString());
Integer in1 = new Integer(num1);
System.out.println(in1.toString());//10
Integer in2 = new Integer("123");
System.out.println(in2.toString());//123
//报异常
//Integer in3 = new Integer("123abc");
//System.out.println(in3.toString());
Float f1 = new Float(12.3f);
Float f2 = new Float("12.3");
System.out.println(f1);//12.3
System.out.println(f2);//12.3
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("TrUe");
System.out.println(b2);//true

Boolean b3 = new Boolean("true123");
System.out.println(b3);//false

Order order = new Order();
System.out.println(order.isMale);//false
System.out.println(order.isFemale);//null
}
}
class Order{
boolean isMale;
Boolean isFemale;
}

Boolean类型的包装类进行了优化

1
2
3
public Boolean(String s) {
this(parseBoolean(s));
}
1
2
3
public static boolean parseBoolean(String s) {
return ((s != null) && s.equalsIgnoreCase("true"));
}

面试题

1

如下两个题目输出结果相同吗?各是什么?

image-20200206105715922
1
2
3
4
5
6
7
8
public class StringTest02 {

@Test
public void test1(){
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);
}
}

输出结果

1
1.0

三目运算符在进行计算的时候,会在编译的时候将结果的两个类型进行统一,此时Integer类型自动向上提升为Double类型

1
2
3
4
5
6
7
8
9
10
@Test
public void test2(){
Object o2;
if (true) {
o2 = new Integer(1);
}else {
o2 = new Double(2.0);
}
System.out.println(o2);
}

输出结果

1
1

2

1
2
3
4
5
6
7
8
9
10
11
public void method1() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);
Integer m = 1;
Integer n = 1;
System.out.println(m == n);
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
}

输出结果

1
2
3
false
true
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
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}

Integer内部定义了IntegerCache结果,IntegerCache中定义了Integer[]数组,保存了从-128127范围的整数。如果使用自动装箱的方式,给Integer赋值的范围在-128127范围内,可以直接使用数组中的元素,就不用再new

目的:提高效率

文章目录
  1. 1. 面向对象二
    1. 1.1. 继承性(inheritance)
      1. 1.1.1. 为什么要有类的继承性?(继承性的好处)
      2. 1.1.2. 继承性的格式
      3. 1.1.3. 子类继承父类以后有哪些不同
      4. 1.1.4. Java中继承性的说明
      5. 1.1.5. Java.lang.Object类的理解
    2. 1.2. 方法的重写
    3. 1.3. 四种访问权限修饰符
    4. 1.4. 关键字super
      1. 1.4.1. super 关键字
      2. 1.4.2. 可以用来调用的结构
      3. 1.4.3. super调用属性、方法
      4. 1.4.4. super调用构造器
      5. 1.4.5. this与super的区别
    5. 1.5. 子类对象实例化全过程
      1. 1.5.1. 从结果上看
      2. 1.5.2. 从过程上看
      3. 1.5.3. 强调说明
    6. 1.6. 多态性
      1. 1.6.1. 多态性的理解
      2. 1.6.2. 何为多态性
      3. 1.6.3. 多态性的使用
      4. 1.6.4. 多态性的使用前提
      5. 1.6.5. 多态性的应用举例
      6. 1.6.6. 多态性使用的注意点
      7. 1.6.7. 关于向上转型与向下转型
        1. 1.6.7.1. 如何实现向下转型:
        2. 1.6.7.2. 使用时的注意点:
        3. 1.6.7.3. instanceof的使用:
        4. 1.6.7.4. 图示:
      8. 1.6.8. 面试题
        1. 1.6.8.1. 谈谈你对多态性的理解?
        2. 1.6.8.2. 多态是编译时行为还是运行时行为?(可以叫做动态绑定)
    7. 1.7. Object类的使用
      1. 1.7.1. java.lang.Object类的说明
      2. 1.7.2. equals()方法
        1. 1.7.2.1. equals()的使用
        2. 1.7.2.2. 如何重写equals()
          1. 1.7.2.2.1. 手动重写举例
          2. 1.7.2.2.2. 开发中如何实现
        3. 1.7.2.3. 回顾 == 运算符的使用
      3. 1.7.3. toString()方法
        1. 1.7.3.1. toString()的使用
        2. 1.7.3.2. 如何重写toString()
      4. 1.7.4. 面试题
    8. 1.8. 包装类的使用
      1. 1.8.1. 为什么要有包装类(或封装类)
      2. 1.8.2. 基本数据类型与对应的包装类
      3. 1.8.3. 需要掌握的类型间的转换
      4. 1.8.4. 应用场景举例
      5. 1.8.5. 面试题
|