面向对象

1. 面向对象

  • 面向过程:自上向下,将一个大问题分解为几个小问题,再将小问题分解为更小的问题,最后将任务划分成一个一个具体的步骤,分别去执行,最小粒度细化到方法层面
  • 面向对象:将程序所有参与角色都看作一个个对象,通过对象和对象之间的相互调用来完成系统的功能,是一种将程序模块化的思想

1.1 类和对象

对象是主体,类是用来创建对象的;类是产生对象的模板,所有的对象都是通过类来创建的

对象的特征:

  • 属性,对象的静态特征
  • 方法,对象的动态特征

类是对象的抽象化描述,对象是类的具体实例

对象的创建:new、调用类的构造方法:

  • 类默认有一个无参构造,手动创建一个有参构造后,该默认构造就会被覆盖
  • 使用有参构造创建对象,只需调用一次方法,就可以同时完成创建和赋值;使用无参需要创建、赋值两步操作

1.2 成员变量和局部变量

作用域:就近原则;成员变量和局部变量重名时,采用就近原则进行优先级取值;

成员变量有默认值,局部变量没有:

  • byte、short、int、long、float、double:0

  • char:’’

  • boolean:false

  • 引用类型:null

1.3 三大特征

封装、继承、多态

封装

将类的各种信息(属性/成员变量和方法)封装到内部,使得外部无法直接访问,提高数据安全性。

  • 属性私有化

  • 提供公有方法访问私有属性(set/get),在方法中添加逻辑,控制数据的安全性

  • 外部通过 set/get 方法访问属性

static

static 表示静态或全局,可用来修饰成员变量、成员方法、代码块,被修饰的资源属于整个类,所有对象都可以共享这些资源;

使用 static 修饰后,方法就会独立于该类的任何一个实例对象,不属于任何对象,而是属于类,访问时不需要依赖于任何一个对象,可以直接通过类来访问。

static 修饰的方法(静态方法)不能使用 this 关键字;this 表示当前对象,静态资源属于类,不属于任何对象;同时静态方法也不能访问类的实例变量和实例方法(非 static 修饰的)

static 修饰代码块,静态代码块的特点是只执行一次,当类被加载到内存时执行,不需要手动调用,会自动执行;且也只能访问静态资源;创建对象时,静态代码块的执行先于构造函数的执行,即类加载 –> 创建对象

this

永远指向构造或调用的当前实例;

可用于在构造器里访问另一个构造器,此时只能放在第一行:

1
2
3
4
5
6
7
8
public Car() {
this("bens", "black"); // 但 this 作为构造器只能放在第一行
}

public Car(String type, String color) {
this.type = type;
this.color = color;
}

this() 调用本类构造函数;注意构造函数不能进行递归,会导致无限创建对象,堆内存溢出

super

super() 调用父类构造函数

继承

类之间资源共享的一种方式,一个类中的信息(成员变量、成员方法)可以直接被另外的类拥有。

子类能继承父类的非私有资源(非 private 修饰的资源)

  • 创建子类对象之前,会自动创建父类对象
  • 无参创建子类对象,默认调用父类的无参构造创建父类对象
  • 有参创建子类对象,也默认调用父类的无参构造创建父类对象

父类也叫超类,子类也叫派生类;创建子类时通过有参构造创建父类对象(super):

1
2
3
4
5
6
7
8
9
public Student() {

super(); // 可省略,默认调用父类无参构造
System.out.println("无参构造创建Student对象");
}
public Student(int id) {
super(1);
System.out.println("有参构造创建Student对象");
}

多态

一种事务可以有多种不同的表现形态,一个对象在不同的业务场景中以不同的形式出现,根据不同的业务场景,对象呈现出不同形式。

  • 必须有继承关系,只有构建继承关系,才能使一个对象可以进行不同形式形式的变形
  • 父类引用指向子类实例
1
2
3
4
5
package com.kk.oop;

public class Member {
public void buyBook() {}
}
1
2
3
4
5
6
7
package com.kk.oop;

public class OrdinaryMember extends Member {
public void buyBook() {
System.out.println("普通会员买书打9折");
}
}
1
2
3
4
5
6
7
package com.kk.oop;

public class SuperMember extends Member {
public void buyBook() {
System.out.println("超级会员买书打6折");
}
}
1
2
3
4
5
6
7
package com.kk.oop;

public class VIPMember extends Member {
public void buyBook() {
System.out.println("VIP会员买书打3折");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
OrdinaryMember ordinaryMember = new OrdinaryMember();
SuperMember superMember = new SuperMember();
VIPMember vipMember = new VIPMember();

Cashier cashier = new Cashier();

cashier.setMember(ordinaryMember);
cashier.settlement();
cashier.setMember(superMember);
cashier.settlement();
cashier.setMember(vipMember);
cashier.settlement();
}

1.4 方法重载和重写

  • 重载:在同一个类中,两个方法的方法名相同参数列表不同(数据类型、形参个数)与返回值无关
  • 重写:子类在继承父类方法的基础上,对父类方法进行重新定义,覆盖父类方法的操作叫做重写;规则:
    • 方法名相同
    • 参数列表相同
    • 子类方法的返回值与父类方法的返回值类型相同或者是其子类(返回值:父类 > 子类)
    • 子类方法的访问权限不能小于父类(访问权限:子类 > 父类)但能被子类重写的方法一定是非 private 的

权限修饰符

修饰符 当前类 同 package 子孙类 其他 package
public
protected ×
friendly × ×
private × × ×

1.5 抽象类和抽象方法

抽象方法:只把方法定义出来,但不做实现

抽象类:一个类中一旦出现一个抽象方法,则该类必须定义为抽象类;抽象类中那个可以没有抽象方法,也可以有普通方法

1
2
3
public abstract class Member {
public abstract void buyBook();
}

2. 面向对象高级部分

2.1 Object 类

Object 是所有 Java 类的共同父类

Java 所有对象有一些共性,如:hashCode() 获取地址,getClass() 获取类信息,……

方法 描述
toString() 以字符串的形式返回对象的信息
equals(Object obj) 判断两个对象是否相等
hashCode() 返回对象的内存地址

equals

Object 类中的 equals 方法:

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

String 类对 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
public boolean equals(Object anObject) {
// 判断是否为同一对象
if (this == anObject) {
return 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;
}
}
return false;
}

自定义类重写的 equals 方法:

1
2
3
4
5
6
7
8
9
public boolean equals(Object o) {
// 判断是否是同一对象
if (this == o) return true;
// 判断对象对象是否为空、对象类型是否相同
// 判断是否为空,因为不能使用值为null的引用类型变量调用非静态方法
if (o == null || getClass() != o.getClass()) return false;
People people = (People) o;
return id == people.id && Objects.equals(name, people.name);
}

注:基本数据类型的值直接在栈中存放,所以用 “==” 比较;引用数据类型在栈中存放的是对象在堆中的地址,不能用 “==” 比较,应用 equals() 比较值

hashCode 和 equals 的关联:

都是用来判断两个对象是否相等;hashCode 的效率更高;

  • hashCode 的值不相等,则两个对象一定不是同一个对象;

  • hashCode 的值相等,两个对象不一定是同一个对象,不能确实对象的关系;

需要考虑效率的场景下,需要两者配合判断对象是否相等以提高效率,如:

集合框架底层实现,允许/不允许存储重复数据。

**重写 equals() 时必须重写 hashCode()**:

因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等;

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。、

可变参数

可作为一个数组进行操作,传入任意数量对应类型形参。只能出现一个,且只能放在最后。

2.2 包装类

包装类是 Java 提供的一组类,专门用来创建 8 种基本数据类型对应的对象:

Byte、Short、Integer、Long、Float、Double、Character、Boolean

装箱和拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来

    构造方法、valueOf()

    1
    2
    3
    4
    5
    int i = 3;
    Integer num = new Integer(i); // 遵循类型自动转换规则(小->大)
    Integer num1 = new Integer("3"); // 除Character外,都有转字符串的构造方法
    Integer num2 = Integer.valueOf(i); // 第二种装箱方法
    ...
  • 拆箱:将包装类型转换为基本数据类型

    *Value()parse*

    1
    2
    int num4 = num.intValue(); // 拆箱
    int num5 = Integer.parseInt("3"); // 字符串还原为基本数据类型,Character没有

2.3 接口

面向接口编程:将程序的业务逻辑进行分离,以接口的形式去对接不同的业务模块,接口只串联不实现,真正的业务逻辑实现交给接口的实现类来完成。

好处:当用户需求发生变更时,只需要切换不同的实现类,而不需要修改串联模块的接口,减少对系统的影响;

使用这种方式编程,代码的耦合度低,灵活性高且易于扩展。

接口和抽象类的关系:

接口是一个极度抽象的抽象类,接口中的方法全部都是抽象方法;抽象类中可以有非抽象方法。

接口的实现类用来实现接口中的抽象方法,将抽象的概念具体化;实现类实现接口,需要对接口内部所有抽象方法进行实现,同时要求访问权限修饰符、返回类型、方法名和参数列表完全相同。


面向对象
http://example.com/2022/08/07/面向对象/
作者
fkxia
发布于
2022年8月7日
许可协议