Java基础编程 - 4、面向对象-中

1、OOP特征二 继承性

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)
类继承语法规则:class Subclass extends SuperClass{ }

1.1 继承性的好处:

  • 少了代码的冗余,提高了代码的复用性
  • 便于功能的扩展
  • 为之后多态性的使用,提供了前提

1.2 继承性的格式:

  • class A extends B{}
  • A:子类、派生类、subclass
    B:父类、超类、基类、superclass

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

特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。

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

子类和父类的关系,不同于子集和集合的关系。
extends:延展、扩展

1.3 Java中关于继承性的规定:

1.一个类可以被多个子类继承。
2.Java中类的单继承性:一个类只能有一个父类
3.子父类是相对的概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法

1.4 Object类

1. 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
2. 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
3. 意味着,所有的java类具有java.lang.Object类声明的功能。

2、方法的重写override overwrite

在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。

2.1 重写的规定

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

约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
>特殊情况:子类不能重写父类中声明为private权限的方法
③ 返回值类型:
>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
>父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)

注意:子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。

3、四种访问权限修饰符

修饰符 类内部 同一个包 不同包的子类 同一个工程
private yes —- —- —-
缺省 yes yes —- —-
protected yes yes yes —-
public yes yes yes yes

对于class的权限修饰只可以用public和default(缺省)

  • public类可以在任意地方被访问。

  • default类只可以被同一个包内部的类访问。

4、关键字super

在Java类中使用super来调用父类中的指定操作:

  • super可用于访问父类中定义的属性

  • super可用于调用父类中定义的成员方法

  • super可用于在子类构造器中调用父类的构造器

注意:

  • 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员

  • super的追溯不仅限于直接父类

  • super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {
protected String name = "张三"; 关键字super举例
protected int age;
public String getInfo() {
return "Name: " + name + "\nage: " + age;
}
}
class Student extends Person {
protected String name = "李四";
private String school = "New Oriental";
public String getSchool() {
return school;
}
public String getInfo() {
return super.getInfo() + "\nschool: " + school;
}
}
public class StudentTest {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.getInfo());
}
}

调用父类构造器举例

  • 子类中所有的构造器默认都会访问父类中空参数的构造器

  • 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行

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

this和super的区别

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

5、子类对象实例化过程

5.1从结果上来看:(继承性)

子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。

5.2从过程上来看:

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

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

6、OOP特征三 多态性

6.1.理解多态性:可以理解为一个事物的多种形态。

6.2.何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

6.3.多态的使用:虚拟方法调用

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

编译,看左边 - 看的是父类的引用(父类中不具备子类特有的方法)

运行,看右边 - 看的是子类的对象(实际运行的是子类重写父类的方法)

6.4.多态性的使用前提:

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

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

6.7.instanceof 操作符

x instanceof A:检验x是否为类A的对象,返回值为boolean型。

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

  • 如果x属于类A的子类B,x instanceof A值也为true。

6.8 对象类型转换 (Casting )

基本数据类型的Casting:

  • 自动类型转换:小的数据类型可以自动转换成大的数据类型
    如long g=20; double d=12.0f
  • 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
    如 float f=(float)12.0; int a=(int)1200L

对Java对象的强制类型转换称为造型

  • 从子类到父类的类型转换可以自动进行
  • 从父类到子类的类型转换必须通过造型(强制类型转换)实现
  • 无继承关系的引用类型间的转换是非法的
  • 在造型前可以使用instanceof操作符测试一个对象的类型

7、 java.lang.Object类的使用

  • 1.Object类是所有Java类的根父类
  • 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
  • 3.Object类中的功能(属性、方法)就具有通用性。
    属性:无
    方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
    wait() 、 notify()、notifyAll()
  • 4.Object类只声明了一个空参的构造器
NO. 方法名称 类型 描述
1 public Object() 构造
2 public boolean equals(Object obj) 普通 对象比较
3 public int hashCode() 普通 取得Hash码
4 public String toString() 普通 对象打印时调用

7.1 == 和 equals() 区别

一、回顾 == 的使用:== :运算符

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

  2. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
    如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

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

二、equals()方法的使用:

  1. 是一个方法,而非运算符
  2. 只能适用于引用数据类型
  3. Object类中equals()的定义:
    public boolean equals(Object obj) {
    return (this == obj);
    }
    说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
  4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是
    两个引用的地址是否相同,而是比较两个对象的”实体内容”是否相同。
  5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的”实体内容”是否相同。那么,我们
    就需要对Object类中的equals()进行重写.
    重写的原则:比较两个对象的实体内容是否相同.

三、重写equals()方法的原则

对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。

自反性:x.equals(x)必须返回是“true”。

传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。

一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管重复x.equals(y)多少次,返回都是“true”。

任何情况下,x.equals(null),永远返回是“false”; x.equals(和x不同类型的对象)永远返回是“false”。

7.2 Object类中toString()的使用

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

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

    1
    2
    3
    public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    1
    2


  3. 像String、Date、File、包装类等都重写了Object类中的toString()方法。
    使得在调用对象的toString()时,返回”实体内容”信息

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

8、包装类的使用

  • 针对八种基本数据类型定义相应的引用类型—包装类(封装类)
  • 有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型 包装类
byte Byte 父类Number
short Short 父类Number
int Integer 父类Number
long Long 父类Number
float Float 父类Number
double Double 父类Number
boolean Boolean
char Character

8.1 装箱 & 拆箱

基本数据类型包装成包装类的实例 —装箱

  • 通过包装类的构造器实现:
    int i = 500; Integer t = new Integer(i);
  • 还可以通过字符串参数构造包装类对象:
    Float f = new Float(“4.56”);
    Long l = new Long(“asdf”); //NumberFormatException

获得包装类对象中包装的基本类型变量 —拆箱

  • 调用包装类的.xxxValue()方法:
    boolean b = bObj.booleanValue();

JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。

字符串转换成基本数据类型

  • 通过包装类的构造器实现:
    int i = new Integer(“12”);
  • 通过包装类的parseXxx(String s)静态方法:
    Float f = Float.parseFloat(“12.1”);

基本数据类型转换成字符串

  • 调用字符串重载的valueOf()方法:
    String fstr = String.valueOf(2.34f);
  • 更直接的方式:
    String intStr = 5 + “”

8.2 Java中的JUnit单元测试

步骤:

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.如果执行结果没有任何异常:绿条
2.如果执行结果出现异常:红条