面向对象的三大主线:
- Java类及类的成员:属性、方法、构造器、代码块和内部类;
- 面向对象的三大特征:封装性(Encapsulation)、继承性(Inheritance)、多态性(Polymorphism);
- 其他关键字:this、super、package、import、static、final、abstract、interface等。
1. 关键字package
软件包可以包含类和子包,其帮助管理大型软件系统,将语义相似的类组织到包中,解决类命名冲突的问题。
而关键字package作为Java源文件的第一条语句,即用于声明源文件所在的包;每“.”一次表示一层文件目录,包名都要小写。
其中,JDK中主要的包如下所示:
包名 | 描述 |
---|---|
java.lang | 包含Java语言的核心类,如String、Math、Integer、System、Thread等,提供常用功能 |
java.net | 包含执行与网络相关操作的类和接口 |
java.io | 包含能提供多种输入或输出功能的类 |
java.util | 包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期相关的函数等 |
java.text | 包含一些与java格式化相关的类 |
java.sql | 包含进行JDBC数据库编程的相关类/接口 |
java.awt | 包含了构成抽象窗口工具集(abstract window toolkits)的多个类,其被用来构建和管理应用程序的图形用户界面 |
java.applet | 包含applet运行所需的一些类 |
2. 关键字import
- 语法格式:
import 包名[.子包名…].<类名 | *>
; - 关键字import出现在package语句和类定义之间,用于显式导入指定包下的类或接口;
- 如果导入的类是java.lang包下的,编译器默认自动获取,而不需要再显式声明;
- 理解“.*”的概念,其用于导入指定包下的所有类或接口,但其子包下的类或接口则不会被导入;
- JDK 1.5 加入import static语句,用于导入指定类的静态属性或静态方法。
3. 关键字this
关键字this表示当前对象或当前正在创建的对象,其可以用来修饰属性、方法和构造器。
当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来声明该变量是类成员;在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性。
可以在构造器中通过“this(形参列表)”的方式显式的调用当前类的重载的指定构造器,但要求:必须声明在构造器的首行,且需要保证该类中至少有一个构造器是不用this的。
4. 关键字super
在Java类中使用super来调用父类中的指定操作,即可以修饰或调用父类的属性、方法或构造器。
- 在子类的方法和构造器中,可以通过“super.属性名”或“super.方法”的方式显式地调用父类指定的属性或方法;
- 当子类与父类中有同名的属性或方法时,在子类中必须通过“super.属性名”或“super.方法”显式地调用父类中声明的属性或父类中被重写的方法;
- 在子类中使用“super(形参列表)”的方式可以显式地调用父类中指定的构造器,但需要注意:
- 任何一个类(除Object类)的构造器的首行,要么通过“this(形参列表)”显式地调用本类中重载的其他构造器或通过“super(形参列表)”显式地调用父类中指定的构造器,要么默认调用父类中无参的构造器;
- 因此,在设计类时,应尽量提供一个无参的构造器。
4.1 关键字this和super的比较
对比点 | 关键字this | 关键字super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性,则从父类中继续查找 | 直接访问父类中的属性 |
访问方法 | 访问本类中的方法 | 直接访问父类中的方法 |
调用构造器 | 调用本类中重载的构造器,必须放在构造器的首行 | 调用父类的构造器,必须放在子类构造器的首行 |
特殊 | 表示当前对象 | 无此概念 |
4.2 子类对象实例化全过程
5. 关键字instanceof
- 格式:对象obj instanceof 类ClassName;
- 表示:检验对象obj是否为类ClassName的实例,返回值为boolean型;
- 注意:若对象obj是类ClassName的实例,那么obj也一定是ClassName类的父类的实例。
6. 关键字static
在Java类中,可用static修饰属性、方法、代码块和内部类,而不能修饰构造器;其主要思想是保证无论该类是否产生对象或无论产生多少对象的情况下,某些特定的数据在内存空间中只有一份。其中,被修饰后的成员具有如下特点:
- 随着类的加载而加载,故优先于对象存在;
- 所修饰的成员,被该类的所有对象所共享;
- 访问权限允许时,可不创建对象,直接被类调用。
6.1 类属性
在Java类中,用static修饰的属性为类变量(类属性),而非static修饰的属性为实例变量。
对比点 | 类变量 | 实例变量 |
---|---|---|
加载时间 | 随着类的加载而加载,早于对象 | 随着对象的创建而加载 |
内存位置 | 存放在静态域中,各对象共同拥有一份,一改则改 | 存放在堆空间中,各对象各自拥有一份 |
调用方式 | “类.类变量” 或 “对象.类变量” | 只能在创建对象后通过“对象.实例变量”的形式来调用 |
6.2 类方法
对比点 | 类方法 | 非静态方法 |
---|---|---|
加载时间 | 随着类的加载而加载,各对象共同拥有一份 | 随着对象的创建而加载 |
内部可调用 | 只可调用静态的属性或方法,而不能调用非静态的 | 既可调用非静态的属性或方法,也可以调用静态的 |
内部关键字 | 方法内部不可使用this和super关键字,因为其不需要实例就可以访问 | 方法内部可以使用this和super关键字 |
注意:静态的结构随着类的加载而加载,其生命周期早于非静态的结构,同时被回收也要晚于非静态的结构。
6.3 静态代码块
静态代码块主要作用是为Java类的类属性进行初始化操作,其随着类的加载而加载,只执行一次,也不可调用非静态的方法和属性。
6.4 理解main()方法
package com.whut.qiaobc.practice;
public class Person {
/**
* public : 由于JVM需要调用main()方法执行,故其访问权限为公共的
* static : 由于JVM在执行main()方法时无需创建对象,故其为静态的
* void : 该方法无需为JVM返回值,故其返回值为空
* @param args : 命令行参数列表,即执行该程序时所传入的具体参数
* 即若传入的参数为:"qiaobc", "qiaob", "qiaobb",则输出为:
* args[0] = qiaobc,
* args[1] = qiaob,
* args[2] = qiaobb
*/
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "] = " + args[i]);
}
}
}
6.5 应用:单例设计模式
需求:保证某个类在整个软件系统中只能存在一个对象,且该类只提供一个取得其对象实例的方法;
单例设计模式之饿汉式:
- 私有化构造器,使得在类的外部不能够调用此构造器;
- 在类的内部创建一个对象实例;
- 私有化此对象实例,以体现其封装性,通过公共的方法来调用;
- 创建公共的getter()方法,并静态化,以便通过类来调用该方法获得对象;
- 静态化此对象,因为静态方法中只能调用静态属性。
/**
* 单例设计模式之饿汉式:也可通过静态代码块对其进行初始化
* 应用实例:java.lang.Runtime
* @author qiaobc
*/
public class Singleton1 {
// 私有化构造器
private Singleton1() {
// TODO Auto-generated constructor stub
}
// 创建对象实例并私有化、静态化
private static Singleton1 instance= new Singleton1();
// 创建公共的getter()方法获取对象实例、静态的
public static Singleton1 getInstance() {
return instance;
}
}
单例设计模式之懒汉式:与恶汉式的区别在于,懒汉式是在公共方法中才对类进行实例化的,其可能存在线程的安全问题。
/**
* 单例设计模式之懒汉式:存在线程安全问题,但可修复
* @author qiaobc
*/
public class Singleton2 {
// 私有化构造器
private Singleton2() {
// TODO Auto-generated constructor stub
}
// 定义类属性,并私有化、静态化
private static Singleton2 instance = null;
// 创建公共的getter()方法获取对象实例、静态的
public static Singleton2 getInstance() {
// 特点:只有当对象未实例化时才创建,可能存在线程安全问题
if(instance == null) {
instance = new Singleton2();
}
return instance;
}
}
7. 关键字final
关键字final表示“最终的”,可用来修饰类、属性和方法,具体说明如下:
- 修饰类:final标记的类不能被继承,以提高程序的安全性和可读性,如String类、System类、StringBuffer类等;
- 修饰方法:final标记的方法不能被子类重写,如Object类的getClass()方法;
- 修饰属性:final标记的变量(成员变量或局部变量)称为常量,其名称通常大写,且只能被赋值一次。
关键字final修饰的成员变量不能使用默认初始化,其初始化方式主要有:
- 在声明的同时对其进行显式初始化;
- 声明完后,在代码块中对其显式初始化;
- 声明完后,在构造器中对其显式初始化,但注意需要在所有构造器中均对其进行初始化操作;
- 基本原则:保证在对象创建之前即需要对final修饰的属性进行赋值操作。
注意:关键字static和final可以同时修饰属性,表示全局常量,即可以直接被类所调用的常量,如Math.PI。
8. 关键字abstract
关键字abstract可以用来修饰类和方法,其前提条件是继承;当我们设计一个类且不需要创建此类的实例时,可以考虑将其设置为抽象类,由其子类提供具体实现该类的抽象方法。
8.1 抽象类
- 抽象类有构造器,即凡是类均有构造器,但其却不能被实例化;
- 抽象方法所在的类必须声明为抽象类,但抽象类中可以没有抽象方法;
- 抽象类是用来被继承的,其子类必须重写父类的抽象方法,并提供方法体;若没有重写全部的抽象方法,则仍为抽象类。
8.2 抽象方法
- 格式:抽象方法没有大括号和方法体,如
public abstract void methodName();
- 抽象方法只保留方法的功能,而具体的执行则交给该抽象类的子类,由子类重写该抽象类的抽象方法;
- 注意:关键字abstract不能用来修饰属性、构造器、私有方法、静态方法和final修饰的方法。
8.3 抽象类的应用
作用:抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。
问题:在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离;但卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同,Vehicle类无法提供计算方法,该如何实现?
// 抽象Vehicle类
public abstract class Vehicle{
public abstract double calcFuelEfficiency(); //计算燃料效率的抽象方法
public abstract double calcTripDistance(); //计算行驶距离的抽象方法
}
// Truck类
public class Truck extends Vehicle{
public double calcFuelEfficiency() {
// 实现计算卡车燃料效率的具体方法
}
public double calcTripDistance() {
// 实现计算卡车行驶距离的具体方法
}
}
// RiverBarge类
public class RiverBarge extends Vehicle{
public double calcFuelEfficiency() {
// 实现计算驳船燃料效率的具体方法
}
public double calcTripDistance() {
// 实现计算驳船行驶距离的具体方法
}
}
8.4 应用:模板方法设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展和改造,但子类总体上会保留抽象类的行为方式。其解决的问题主要是:
- 当功能内部一部分实现是确定,一部分实现是不确定的,则可以将不确定的部分暴露出去,让子类去实现;
- 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
// 抽象类
abstract class Template {
public final void getTime() {
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("执行时间是:" + (end - start));
}
public abstract void code(); // 抽象方法
}
class SubTemplate extends Template {
public void code() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
}
}
9. 关键字interface
Java语言只支持类的单继承而不支持多继承,但在实际应用中,又经常需要使用多继承来解决问题。为了解决该问题,Java语言提供了接口来实现类的多重继承功能。
9.1 接口的定义
接口实质上是一个特殊的抽象类,其是常量与抽象方法的集合,不能包含变量、方法和构造器,具体定义格式如下:
// 接口之间是可以多重继承的,而类之间是单继承的,但类可以实现多个接口
[权限修饰符] interface 接口名 [extends 父接口名列表] {
[public] [static] [final] 常量名; //常量都用public static final默认修饰,可省略
[public] [abstract] 方法名(形参列表); //抽象方法都用public abstract默认修饰,也可省略
}
说明:接口主要用来定义规范,解除耦合关系;其与类是并行的概念,故不可和类重名;另外,接口定义的只是一种功能,该功能不能实例化,但可以被类所实现(implements)。
9.2 接口的实现
接口是由类来实现的,实现接口的类必须重写其中所有的抽象方法,才能进行实例化;否则此类只能为抽象类,仍无法进行实例化。其中,接口实现的语法格式如下:
[权限修饰符] class 类名 [extends 父类名] [implements 接口列表]{}
注意:当继承与实现同时存在时,一般先继承后实现;与继承类似,接口与实现类之间也存在多态性。
9.3 接口的匿名类对象
接口名 对象引用名 = new 接口名() {
// 重写接口的所有抽象方法
}
9.4 应用:工厂方法设计模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类,即使一个类的实例化延迟到其子类;其主要适用于:
- 当一个类不知道它所必须创建的对象的类的时候;
- 当一个类希望由它的子类来指定它所创建的对象的时候;
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
工厂方法设计模式的具体实例如下图所示:
9.5 应用:代理模式
为其他对象提供一种代理以控制对该对象的访问,其示意图如下所示:
9.6 接口与抽象类
在实际开发中,一个类不要去继承一个已经实现好的类,而应该继承一个抽象类,或实现接口。其中,抽象类和接口的对比如下:
对比点 | 抽象类 | 接口 |
---|---|---|
定义 | 包含抽象方法的类 | 全局常量和抽象方法的集合 |
组成 | 常量、变量、构造器、普通方法、抽象方法 | 常量、抽象方法 |
使用 | 子类继承抽象类 | 子类实现接口 |
关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
常见设计模式 | 模板设计 | 工厂方法、代理模式 |
对象 | 通过对象的多态性产生实例化对象 | 通过对象的多态性产生实例化对象 |
局限 | 单继承 | 无此局限 |
实质 | 作为一个模板 | 作为一个标准或表示一种能力 |
选择 | 抽象类和接口都可以使用时,优先使用接口 | 优先使用接口,避免单继承的局限 |
特殊 | 一个抽象类可以包含多个接口 | 一个接口可以包含多个抽象类 |