Java 面向对象

面向过程

优点:性能比面向对象高。因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发

缺点: 没有面向对象易维护、易复用、易扩展

面向对象

优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

缺点: 性能比面向过程低

类、对象

类是模板,确定对象将会拥有的特征(属性)和行为(方法)

类的特点

  • 类是对象的类型
  • 具有相同属性和方法的一组对象的集合
    • 属性: 对象具有的各种静态特征(“有什么”)
    • 方法: 对象具有的各种动态行为(“能做什么”)

对象

对象是运行期的基本实体,它是一个封装了数据和操作这些数据的代码的逻辑实体

对象实例化
  • 声明对象 Person p

  • 实例化对象 new Person();

  • Person p = new Person();

  • 每次 new 对象都会产生新的实例化对象

  • 多个对象可以指向同一块实例化空间

    Person p1 = new Person();
    Person p2 = p1;
  • 对象必须被实例化后才能使用

  • 对象间的引用传递,实际上传递的是堆内存空间的使用权

匿名对象的创建和使用方法

可以不定义对象的引用名称,而直接调用这个对象的方法。这样的对象叫做匿名对象,匿名对象通常都是一次性的。

new Person().eat();

对象的使用
  1. 对象.成员变量
  2. 对象.成员方法()

构造方法

主要用来在创建对象时初始化对象,即为对象的成员变量赋初始值

  • 构造方法与类同名且没有返回值,但不能使用 void 关键字
  • 在对象实例化时调用
  • 当没有指定构造方法时,编译器会在编译时自动添加无参数且方法体为空的构造方法
  • 当有指定构造方法时,无论是有参还是无参,系统都不会自动添加无参的构造方法
  • 一个类中可以有多个构造方法(构造函数的重载)

重载与重写

父类的私有属性和构造方法并不能被继承,所以构造器 Constructor 就不能被重写(override),但是可以被重载(overload),所以你可以看到一个类中有多个构造函数的情况

重载(Overload)

发生在同一个类中,方法名相同,参数类型、个数、顺序至少有一处不同

  • 返回值不同,其它都相同不构成重载关系

重载

重写(Override)

发生在继承体关系中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类

  • 使用 @Override 注解,可以让编译器帮忙检查是否满足重载的限制条件
  • 如果父类方法访问修饰符为 private 则子类就不能重写该方法
重写的例子

重写

  • 子类方法访问权限为 public,大于父类的 protected。
  • 子类的返回类型为 ArrayList,是父类返回类型 List 的子类。
  • 子类抛出的异常类型为 Exception,是父类抛出异常 Throwable 的子类。
  • 子类重写方法使用 @Override 注解,从而让编译器自动检查是否满足限制条件

为 JAVA 类提供了命名空间,用于管理 Java 文件,解决同名文件冲突

  • package 包名; 必须放在Java源文件中的第一行
  • 一个Java源文件只能有一个 package 语句
  • 包名命名采用全英文小写,命名规则: 域名倒序 + 模块 + 功能
  • 一个类的全名应该是 ”包名.类名”
  • 使用javac 编译时需要使用 -d 参数,该参数的作用是依据包名生成相应的文件夹

导入bao

  • import 包名.类名;
  • import com.csthink.*; 导入包中全部类
  • import com.csthink.utils.JdbcUtils; 导入包中指定类

this 关键字

当前对象的默认引用

this 的使用

  • 调用成员变量,解决成员变量与局部变量同名问题
  • 调用成员方法
  • 调用重载的构造方法

this 的使用

static关键字

静态成员随类加载产生,直至类销毁才回收

静态变量

  • 静态变量又称为类变量,该变量属于类,类所有的实例都共享静态变量,可以直接通过类名访问它,静态变量在内存中只存在一份
  • 实例变量,每创建一个实例就会产生一个实例变量,它与该实例同生共死

静态变量

静态方法

静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法

静态方法

静态代码块

静态代码块在类初始时运行,只会运行一次

静态代码块

静态内部类

非静态内部类依赖于外部类的实例,而静态内部类不需要

静态内部类

静态导包

在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低

import static com.xxx.ClassName.*;

静态变量、静态代码块、普通代码块、实例变量、构造方法的初始化顺序

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序

存在继承的情况下,初始化顺序为:

  1. 父类(静态变量、静态语句块)
  2. 子类(静态变量、静态语句块)
  3. 父类(实例变量、普通语句块)
  4. 父类(构造函数)
  5. 子类(实例变量、普通语句块)
  6. 子类(构造函数)

反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
  • Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
  • Constructor :可以用 Constructor 创建新的对象。

一个反射的例子(通过反射去访问类属性、成员方法)

Robot 实体类

反射操作

抽象类和抽象函数

抽象函数

抽象函数就是只有函数的定义,没有函数体的函数

abstract void fun();

抽象类(天生当爹的)

抽象类也称为基类,它只能作为父类被子类继承。在子类中需复写抽象类中的抽象函数,以便子类在继承

  • 抽象类不能够生成对象
    • 也就是不能调用这个类的构造函数来生成对象
    • 如果抽象类能生成对象,则意味着该对象也将会调用其中的抽象函数,而抽象函数是没有函数体的,这也将导致该调用的失败。所以不能调用抽象类来生成对象
  • 如果一个类当中包含有抽象函数,这个类必须被声明为抽象类
  • 一个类当中没有包含抽象函数,这个类也可以被声明为抽象类
  • 抽象类也是有构造函数的,而且子类的构造函数同样会调用抽象父类的构造函数

抽象类

使用场景

  • 需要在几个相关的类中共享代码
  • 需要能控制继承来的成员的访问权限,而不是都为 public
  • 需要继承非静态和非常量字段

接口

接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现,从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类

  • 使用 interface 定义
  • 接口当中的方法都是抽象方法
  • 接口当中的方法都是 public 权限,方法可以不用手动使用 public 修饰
  • 实现接口使用 implements 关键字
  • 一个类可以实现多个接口
  • 一个接口可以继承多个接口
  • 接口的字段默认都是 static 和 final 的

接口

使用场景

  • 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
  • 需要使用多重继承

面向对象编程三大特性: 封装 继承 多态

封装

封装把一个对象的属性私有化,同时提供一些可以被外界访问属性的方法

  • 将类的某些信息隐藏在类内部,不允许外部程序直接访问
  • 通过该类提供的方法来实现对隐藏信息的操作和方法
  • 隐藏类的实例细节,方便修改和实现
  • 留出访问的接口,通过规定的方法来访问数据

封装实现的步骤

  • 修改成员属性的可见性为 private
  • 创建 getter/setter 方法,可见性为 public,用于对属性的访问
  • 在 getter/setter 方法中加入属性控制语句

public 公共权限

  • 公共权限的类,其类名必须与源文件的文件名保持一致
  • 只有声明为公共权限的类,才能在不同的包中进行访问

private 私有权限

  • 一般情况下,私有权限仅用于修饰成员函数和成员变量
  • private 修饰的成员函数和成员变量只能在当前的类当中使用

default 包级别访问权限/默认权限

  • 此权限无需在类名、变量名或函数名前面使用权限修饰符 default
  • 在不同的包里面,不能访问 default 权限的类

protected 受保护权限

  • protected 用于修饰成员,表示在继承体系中父类成员对于子类可见,但是这个访问修饰符对于类没有意义
  • protected 权限拥有和 default 权限一样的功能
  • protected 权限可以跨包进行使用

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码,JAVA 仅支持单继承,即一个子类只允许继承一个父类,但一个父类可以拥有多个子类。

  • 子类拥有父类非 private 的属性和方法
  • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展
  • 子类可以用自己的方式实现父类的方法
  • 父类的构造方法不能被继承

继承

super 关键字

  • 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作,super() 必须是子类构造方法的第一行
  • 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现,super.父类xx方法()

final 关键字

  • 声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量
    • 对于基本类型,final 使数值不变;
    • 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改
  • 声明方法不能被子类重写
    • private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法
  • 声明类不允许被继承

多态

多态是指不同事物具有不同表现形式的能力。多态机制使具有不同内部结构的对象可以共享相同的外部接口,通过这种方式减少代码的复杂度

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量调用的方法在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象及该引用变量调用的方法到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

在Java中有两种形式可以实现多态:

  • 继承(多个子类对同一方法的重写)
  • 接口(实现接口并覆盖接口中同一方法)
Author: csthink
Link: csthink.com/java/oop.html
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
支付宝打赏
微信打赏