面向对象

类的基本概念

函数的容器

我们先简单将类看做是函数的容器,提供编码时经常用到的一些操作(常用的工具类)。但更多时候类表示自定义数据类型

  1. public class NumberUtils{
  2. public static final double PI = 3.1415926515;
  3. public static boolean isOdd(long num){
  4. return num % 2 != 0;
  5. }
  6. public static boolean isEven(long num){
  7. return num % 2 == 0;
  8. }
  9. }

自定义数据类型

自定义数据类型就是除了8种基本数据类型以外的其他类型。包含了属性和方法。属性分为类属性/类变量/静态变量对象属性/实例属性统称为成员变量。方法分为类方法/静态方法实例方法/统称为成员方法

类变量

类本身具有的属性通过类变量体现。

  1. public class Math{
  2. public static final double PI = 3.1415926535;
  3. }

实例变量实例方法

对象具有的属性通过实例变量体现。

定义第一个类

  1. public class Student(){
  2. private int age;
  3. private int score;
  4. private String name;
  5. // ........
  6. }

使用第一个类

  1. Student student;
  2. student = new Student();

类的组合

代码的组织机制

jar包

在Java中,编译后的一个或多个包的Java Class文件可以打包为一个文件,Java中打包命令为jar,打包后的扩展名为.jar,一般称之为jar包。

首先进入到Java Class文件的根目录,然后运行如下命令:

  1. jar -cvf <包名>.jar <最上层包名>

比如,对前面介绍的类打包,如果Hello.class位于E:\bin\shou\laoma.Hello.java,则可以到目录E:\bin下,然后运行:

  1. jar -cvf hello.jar shou

hello.jar就是jar包,jar包就是一个压缩文件,可使用压缩工具打开。

如果要使用jar包,将jar包添加到类路径(classpath)中即可。

程序的编译与链接


类的继承

面向对象中经常使用继承来表达抽象的结果,在继承关系中有子类和父类,父类也叫基类,子类也叫派生类。使用继承一方面是可以复用代码,公共的属性和行为可以放到父类,而子类只需要关注子类特有的就可以了;另一方面不同的子类对象可以更为方便地被统一处理。

基本概念

根父类Object

即使没有声明父类,也有一个隐含的父类,这个类就是Object。Object没有定义属性,但是定义了一些方法。

方法名 含义
Object clone() 创建并返回此对象的副本
boolean equals(Object obj) 判断当前对象与参数obj是不是同一个引用,本质上是调用了==判断
void finalize() 已过时
Class<?> getClass() 返回此对象的运行时类。
int hashCode() 返回此对象的哈希值
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程
String toString() 返回对象的字符串表示形式
void wait() 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法
void wait(long timeout) 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法,或指定的时间已过。
void wait(long timeout, int nanos) 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法,或者某些其他线程中断当前线程,或者已经过了指定的时间。

toString()方法:因为Object类并不知道具体对象的属性,不知道怎么用文本描述,但又要区分不同对象,只能写一个hash值。

方法重写

多态

把子类对象赋值给父类引用,叫向上转型,转型即转换流类型,向上就是转换为父类类型。

多态,即一种类型变量,可引用多种实际类型对象。这样,对于父类引用,它有两个类型,一个是父类变量,我们称之为静态类型;一个类型是向上转型的子类,我们称之为动态类型。在调用子类方法时,这种方法称为动态绑定,发生在运行期。

为什么要使用多态

创建对象的代码和操作对象的代码,经常不在一起,操作对象的代码往往只知道对象是某种父类型。

多态和动态绑定,使得操作对象的程序不需要关注对象的实际类型,从而可以处理不同对象,但又能实现每个对象的特有行为。

小结
  • 每个类有且只有一个父类,没有显示声明父类的,其父类为Object,子类继承了父类非private的属性和方法,可以增加自己的属性和方法,以及重写父类的方法。
  • new过程中,父类先进行初始化,可以通过super调用父类相应的构造方法,没有使用super的情况下,调用父类的默认构造方法。
  • 子类变量和父类变量重名的情况下,可通过super强制访问父类的的变量和方法
  • 子类对象赋值父类引用这叫多态;实际调用的是子类实现,这叫动态绑定。

继承的细节

构造方法
  • 子类可以通过super调用父类的构造方法,如果没有通过super显示调用父类构造方法那么会默认调用父类的无参构造方法,如果父类没有无参构造方法,那么他的任何子类都必须显示通过super调用父类带参构造方法,否者会发生编译错误。
  • 构造方法必须写在第一行。

重名和静态绑定

子类可以重写父类非private的方法,当调用的时候,会动态绑定,执行子类的方法。而对于实例变量、静态方法、静态变量重名是可以的,重名之后实际上有两个变量或方法。

  • 对于private修饰的变量和方法只能在当前类访问,访问对象也永远是当前类,即:在子类访问的是子类的;在父类访问是父类的。
  • 对于public修饰的变量和方法,这要看访问的方式

    • 在类中,访问的是当前类的,但子类可以通过super明确指示调用父类的属性或方法。
    • 在类外,则要看访问变量的静态类型:如果类型是父类,则访问的是父类变量和方法;类型是子类,则访问的是子类的的变量和方法。

当通过b访问的时候,访问的是b的变量和方法;当通过c访问的时候,访问的是c的变量和方法,这称为静态绑定,即访问绑定到变量的静态类型。静态绑定是发生在编译阶段即可决定的,而动态绑定则要等到程序运行时。实例变量、静态方法、静态变量、private方法都是静态绑定的。

重写和重载
  • 重载是指在同一个类中,方法名相同但是参数签名不同(参数个数、类型、顺序)不同,与返回值无关。
  • 重写是指在子类重写与父类相同参数签名的方法。

当有多个重名函数时,在决定要调用那个函数的过程中,首先是按照参数类型匹配的,换句话说,寻找所有重载版本中最匹配的,然后才看变量的动态类型,进行动态绑定。

父子类型转换

把父类型的变量赋值给子类型的变量称为向下转型,但不一定能转换成功。一个父类的变量能不能转换为一个子类的变量,取决于这个父类变量的引用对象(或动态类型)是不是子类或者这个子类的子类。

可以通过instanceof关键字判断变量引用的对象是不是该类对象或者该类的子类的对象。

继承访问权限
  • 模板方法

可见性重写

子类重写父类方法时,子类方法不能降低父类方法的可见性(不能降低父类方法访问的可见性范围)。

继承反应的是”is-a”关系,子类必须支持父类所有对外的行为,将可见性降低就会减少子类对外的行为,从而破坏”is-a”的关系,但子类可以增加父类的行为,所以提升可见性是没关系的。

防止继承final

有的时候我们不希望类被继承,可以通过final关键字实现。


继承实现的基本原理

示例

为什么说继承是把双刃剑