面向对象
类的基本概念
函数的容器
我们先简单将类看做是函数的容器
,提供编码时经常用到的一些操作(常用的工具类)。但更多时候类表示自定义数据类型
。
public class NumberUtils{
public static final double PI = 3.1415926515;
public static boolean isOdd(long num){
return num % 2 != 0;
}
public static boolean isEven(long num){
return num % 2 == 0;
}
}
自定义数据类型
自定义数据类型就是除了8种基本数据类型以外的其他类型。包含了属性和方法。属性分为类属性/类变量/静态变量
和对象属性/实例属性
统称为成员变量
。方法分为类方法/静态方法
和实例方法/
统称为成员方法
。
类变量
类本身具有的属性通过类变量体现。
public class Math{
public static final double PI = 3.1415926535;
}
实例变量实例方法
对象具有的属性通过实例变量体现。
定义第一个类
public class Student(){
private int age;
private int score;
private String name;
// ........
}
使用第一个类
Student student;
student = new Student();
类的组合
代码的组织机制
jar包
在Java中,编译后的一个或多个包的Java Class文件可以打包为一个文件,Java中打包命令为jar,打包后的扩展名为.jar
,一般称之为jar包。
首先进入到Java Class文件的根目录,然后运行如下命令:
jar -cvf <包名>.jar <最上层包名>
比如,对前面介绍的类打包,如果Hello.class位于E:\bin\shou\laoma.Hello.java
,则可以到目录E:\bin
下,然后运行:
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关键字实现。