第四章:面向对象编程
第四章:面向对象编程
4.1:面向过程与面向对象
-
面向过程(POP)与面向对象(OOP)
- 二者都是一种思想,面向对象是相对于面向过程而言的。
- 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
- 面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
-
面向对象的三大特征
封装
Encapsulation
、继承Inheritance
、多态Polymorphism
-
面向对象的思想概述
- 程序员从面向过程的执行者转化成了面向对象的指挥者
- 面向对象分析方法分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的现实世界中的实体。
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。
4.2:Java的基本元素:类和对象
-
类
Class
和对象Object
是面向对象的核心概念- 类:是对一类事物的描述,是抽象的、概念上的定义。
- 对象是实际存在的该类事物的每个个体,因而也称为实例(
instance
)。 - 面向对象程序的重点是类的设计。
- 设计类,就是设计类的成员。
-
Java
类及类的成员- 属性:对应类中的成员变量。
Field = 属性 = 成员变量
- 行为:对应类中的成员方法。
Methon = (成员)方法 = 函数
- 属性:对应类中的成员变量。
-
类的语法格式
修饰符 class 类名{属性声明;方法声明; }
class Person{// 属性String name;int age = 1;boolean isMale;// 方法public void eat(){System.out.println("人可以吃饭");}public void sleep() {System.out.println("人可以睡觉");}public void talk(String language) {System.out.println("人可以说话,使用的是:" + language);} }
4.3:对象的创建和使用
-
创建对象
类名 对象名 = new 类名();
Person p1 = new Person();
-
调用对象的结构:属性、方法
-
属性:
对象名.属性
p1.name = "Tom"; p1.isMale = true;
-
方法:
对象名.方法
p1.eat(); p1.sleep(); p1.talk("Chinese");
注意:如果创建了一个类的多个对象,对于类中定义的属性,每个对象都拥有各自的一套副本,且互不干扰。
-
-
内存解析
- 堆
Heap
:此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 - 栈(
Stack
):是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用。方法执行完,自动释放。 - 方法区(
Method Area
):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 堆
-
匿名对象的使用
- 理解:我们创建的对象,没有显式的赋给一个变量名。即匿名对象。
- 特征:匿名对象只能调用一次。
- 使用:
- 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 我们经常将匿名对象作为实参传递给一个方法调用。
4.4:类的成员之一:属性
-
语法格式
修饰符 数据类型 属性名 = 初始化值;
-
成员变量与局部变量
- 在方法体外,类体内声明的变量称为成员变量。
- 在方法体内部声明的变量称为局部变量。
-
成员变量(属性)和布局变量的区别
成员变量 局部变量 声明的位置 直接声明在类中 方法形参或内部、代码块内、构造器内等 修饰符 private
、public
、static
、final
等不能用权限修饰符修饰,可以用 final
修饰初始值 有默认初始化值 没有默认初始化值,必须显示赋值,方可使用 内存加载位置 堆空间或静态域内 栈空间 -
对象属性的默认初始化赋值
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。
数组元素类型 元素默认初始化值 byte
0 short
0 int
0 long
0L float
0.0F double
0.0 char
0或 \u0000
boolean
false
引用类型 null
4.5:类的成员之二:方法
-
什么是方法(函数)
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
- 将功能封装为方法的目的是,可以实现代码重用,简化代码。
- Java里的方法不能独立存在,所有的方法必须定义在类里。
-
声明格式
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ....) {方法体程序代码;return 返回值; }
public void eat() {System.out.println("客户吃饭"); }public void sleep(int hour) {System.out.println("休息了" + hour + "个小时"); }public String getName() {return name; }public String getNation(String nation) {String info = "我的国籍是:" + nation;return info; }
说明:
- 返回值类型
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用
return
关键字来返回指定类型的变量或常量:return 数据
。 - 如果方法没有返回值,则方法声明时,使用
void
来表示。通常,没有返回值的方法中,就不需要使用return
。但是,如果使用的话,只能return;
表示结束此方法的意思。
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用
return
关键字的使用- 使用范围:使用在方法体中。
- 作用:
- 结束方法。
- 针对于有返回值类型的方法,使用
return 数据
方法返回所要的数据。
- 注意点:
return
关键字后面不可以声明执行语句。
- 方法的使用中,可以调用当前类的属性或方法,特殊的,方法A中又调用了方法A【递归方法】。方法中,不可以定义方法。
- 返回值类型
-
方法的分类
方法的分类:按照是否有形参及返回值
无返回值 有返回值 无形参 void 方法名() {} 返回值的类型 方法名() {} 有形参 void 方法名(形参列表) {} 返回值的类型 方法名(形参列表) {} -
方法的重载
-
定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
两同一不同
:同一个类、相同方法名,参数列表不同【参数个数不同,参数类型不同】。 -
判断是否是重载
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
-
-
可变个数的参数
JavaSE 5.0
中提供了Varargs(variable number of arguments)
机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方法,来传递个数可变的实参。- 可变个数形参的格式:
数据类型 ... 变量名
- 当调用可变个数形参的方法时,传入的参数个数可以是:0个、1个、2个…
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载。
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
- 可变个数形参在方法的形参中,必须声明在末尾。
- 可变个数形参在方法的形参中,最多只能声明一个可变形参。
- 可变个数形参的格式:
-
方法参数的值传递机制
方法,必须由其所在类或对象调用才有意义。若方法含有函数:
- 形参:方法声明时的参数。
- 实参:方法调用时实际传给形参的参数值。
java
的实参值如何传入方法:Java
里方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响- 形参时基本数据类型:将实参基本数据类型变量的
数据值
传递给形参。 - 形参时引用数据类型:将实参引用数据类型变量的
地址值
传递给形参。
- 形参时基本数据类型:将实参基本数据类型变量的
-
递归方法
- 递归方法:一个方法体内调用它自身。
- 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。递归一定要向已知方法递归,否则这种递归就变成了无穷递归,类似于死循环。
4.6:封装与隐藏
-
高内聚、低耦合
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
-
信息的封装和隐藏
Java
中通过将数据声明为私有的private
,在提供公共的public
方法:getXxx()
和setXxx()
实现对该属性的操作,以实现下述目的:- 隐藏一个类中不需要对外提供的实现细节。
- 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作。
- 便于修改,增强代码的可维护性。
public class AnimalTest {public static void main(String[] args) {Animal a = new Animal();a.name = "大黄";a.show();a.setLegs(6);a.setLegs(-6);a.show();System.out.println(a.name);}}class Animal{String name;private int age;private int legs;public void setLegs(int l) {if(l >= 0 && l % 2 == 0) {legs = l;} else {legs = 0;}}public int getLegs() { return legs; }public int getAge() { return age; }public void setAge(int a) { age = a; }public void eat() {System.out.println("动物进食");}public void show() {System.out.println("name = " + name + ", age = " + age + ", legs = " + legs);}}
-
四种访问权限修饰符
Java
权限修饰符public
、protected
、(缺省)、private
置于类的成员定义前,用于来限定对象对该类成员的访问权限。修饰符 类内部 同一个包 不同包的子类 同一个过程 private Yes (缺省) Yes Yes protected Yes Yes Yes public Yes Yes Yes Yes 总结:
- 修饰类的话,只能使用:缺省、
public
Java
提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
- 修饰类的话,只能使用:缺省、
4.7:类的成员之三:构造器
-
构造器的作用
- 创建对象。
- 初始化对象的信息。
-
格式
权限修饰符 类名(形参列表) {}
-
说明
- 如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器。
- 一个类中定义的多个构造器,彼此构成重载。
- 一旦我们显示的定义类的构造器之后,系统就不再提供默认的空参构造器。
- 一个类中,至少会有一个构造器。
-
代码示例
public class PersonTest {public static void main(String[] args) {Person p1 = new Person();p1.eat();Person p2 = new Person("Tom");System.out.println(p2.name);}}class Person{String name;int age;// 构造器public Person() {System.out.println("Person()....");}public Person(String n) {name = n;}public Person(String n, int a) {name = n;age = a;}}
-
属性赋值过程
- 赋值的位置
- 默认初始化
- 显式初始化
- 构造器中初始化
- 通过
对象.属性
或对象.方法
的方式赋值。
- 赋值的先后顺序:
1 - 2 - 3 - 4
- 赋值的位置
-
JavaBean
JavaBean
是一种Java
语言写成的可重用组件。- 所谓
JavaBean
,是指符合如下标准的Java
类- 类是公共的。
- 有一个无参的公共的构造器。
- 有属性,且有对应的
get
、set
方法。
- 用户可以使用
JavaBean
将功能、处理、值、数据库访问和其他任何可以用Java
代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP
页面、Servlet
、其他JavaBean
、applet
程序或者应用来使用这些对象。用户可以认为JavaBean
提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
4.8:this的使用
-
this
关键字的使用this
可以用来修饰、调用:属性、方法、构造器。 -
this
修饰属性和方法this
理解为:当前对象或当前正在创建的对象。- 在类的方法【或构造器】中,我们可以使用
this.属性
或this.方法
的方式,调用当前对象【或当前正在创建的】对象属性或方法。但是,通常情况下,我们都选择省略this.
。特殊情况下,如果方法【构造器】的形参和类的属性同名时,我们必须显式的使用this.变量
的方式,表名此变量是属性,而非形参。
- 在类的方法【或构造器】中,我们可以使用
-
this
调用构造器- 我们在类的构造器中,可以显式的使用
this(形参列表)
方式,调用本类中指定的其他构造器。 - 构造器中不能通过
this(形参列表)
方式调用自己。 - 如果一个类中有
n
个构造器,则最多有n - 1
构造器中使用了this(形参列表)
。 - 规定:
this(形参列表)
必须声明在当前构造器的首行。 - 构造器内部,最多只能声明一个
this(形参列表)
,用来调用其他的构造器。
- 我们在类的构造器中,可以显式的使用
4.9:关键字:package、import
-
package
关键字的使用- 为了更好的实现项目中类的管理,提供包的概念。
- 使用
package
声明类或接口所属的包,声明在源文件的首行。 - 包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
- 每
.
一次,就代表一层文件目录。
补充:同一个包下,不能命名同名的接口、类。不同的包下,可以命名同名的接口、类。
-
MVC
设计模式
MVC
是常用的设计模式之一,将整个程序分为三个层次:视图模型层、控制器层、与数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。- 模型层
model
:主要处理数据- 数据对象封装:
model.bean/domain
- 数据库操作类:
model.dao
- 数据库:
model.db
- 数据对象封装:
- 控制层
controller
:处理业务逻辑- 应用界面相关:
controller.activity
- 存放
fragment
:controller.fragment
- 显示列表的适配器:
controller.adapter
- 服务相关的:
controller.service
- 抽取的基类:
controller.base
- 应用界面相关:
- 视图层
view
:显示数据- 相关工具类:
view.utils
- 自定义
view
:view.ui
- 相关工具类:
- 模型层
-
import
关键字的使用- 在源文件中显式的使用
import
结构导入指定包下的类、接口。 - 声明在包的声明和类的声明之间。
- 如果需要导入多个结构,则并列写出即可。
- 可以使用
xxx.*
的方式,表示可以导入xxx
包下的所有结构。 - 如果使用的类或接口是
java.lang
包下定义的,则可以省略import
结构。 - 如果使用的类或接口是本包下定义的,则可以省略
import
结构。 - 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示。
- 使用
xxx.*
方式表名可以调用xxx
包下的所有结构。但是如果使用的是xxx
子包下的结构,则仍需要显式导入。 import static
导入指定类或接口中的静态结构:属性或方法。
- 在源文件中显式的使用
4.10:继承性
-
继承性的好处
- 减少了代码的冗余,提高了代码的复用性。
- 便于功能的扩展。
- 为之后多态性的使用,提供了前提。
-
继承性的格式
class A extends B {} // A:子类、派生类、subclass // B:父类、超类、基类、superclass
-
体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为
private
的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。 -
子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
-
-
Java
中关于继承性的规定- 一个类可以被多个子类继承。
Java
中类的单继承性:一个类只能有一个父类。- 子父类是相对的概念。
- 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类。
- 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法。
- 如果我们没有显示的声明一个类的父类的话,则此类继承于
java.lang.Object
类。 - 所有的
java
类(除java.lang.Object
类之外)都直接或间接的继承于java.lang.Object
类。 - 意味着,所有的
java
类具有java.lang.Object
类声明的功能。
4.11:方法的重写
-
重写
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。
-
应用
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
-
重写的规定
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型 {// 方法体 } // 约定俗称:子类中的叫重写的方法,父类中叫被重写的方法
-
子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同。
-
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符。
特殊情况:子类不能重写父类中声明为
private
权限的方法。 -
返回值类型:
- 父类被重写的方法的返回值类型是
void
,则子类重写的方法的返回值类型只能是void
。 - 父类被重写的方法的返回值类型是
A
类型,则子类重写的方法的返回值类型可以是A
类或A
类的子类。 - 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型也是相同的基本数据类型
- 父类被重写的方法的返回值类型是
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
注意:子类和父类中的同名同参数的方法要么都声明为非
static
的(考虑重写),要么都声明为static
的(不是重写)。 -
4.12:super关键字
-
super
理解为父类的
-
super
可以用来调用属性、方法、构造器
-
super
调用属性和方法- 我们可以在子类的方法或构造器中。通过使用
super.属性
或super.方法
的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略super
。 - 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显示的使用
super.属性
的方式,表明调用的是父类中声明的属性。 - 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用
super.方法
的方式,表明调用的是父类中被重写的方法。
- 我们可以在子类的方法或构造器中。通过使用
-
super
调用构造器- 我们可以在子类的构造器中显式的使用
super(形参列表)
的方式,调用父类中声明的指定的构造器。 super(形参列表)
的使用,必须声明在子类构造器的首行。- 我们在类的构造器中,针对于
this(形参列表)
或super(形参列表)
只能二选一,不能同时出现。 - 在构造器的首行,没有显式的声明
this(形参列表)
或super(形参列表)
,则默认调用的是父类中空参的构造器:super()
- 在类的多个构造器中,至少有一个类的构造器中使用了
super(形参列表)
,调用父类中的构造器。
- 我们可以在子类的构造器中显式的使用
-
this
和super
的区别区别点 this super 访问属性 访问本类中的属性,如果本类没有此属性则从父类中继续查找 直接访问父类中的属性 调用方法 访问本类中的方法,如果本类没有此方法则从父类中继续查找 直接访问父类中的方法 调用构造器 调用本类构造器,必须放在构造器的首行 调用父类的构造器,必须放在子类构造器的首行
4.13:多态性
-
理解多态性
可以理解为一个事物的多种形态。
-
何为多态性
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
父类 变量名 = new 子类();
-
多态的使用
-
虚拟方法调用
有了对象的多态性以后,我们在编译器,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
-
多态性的使用前提
- 类的继承关系
- 方法的重写
-
对于对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
-
-
instanceof
关键字// 对象a是类A的实例,返回true,不是,返回false boolean flag = a instanceof A;
使用情境:
为了避免在向下转型时出现
ClassCastException
的异常,我们在向下转型之前,先进行instanceof
的判断,一旦返回true
,就向下转型。如果返回false
,不进行向下转型。
子类 变量名 = (子类) 父类对象;
4.14:Object类的使用
Object
类是所有Java
类的根父类,如果在类的声明中未使用extends
关键字指明其父类,则默认父类为java.lang.Object
类。Object
类中的功能(属性、方法)就具有通用性。
public class ObjectTest {public static void main(String[] args) {Order order = new Order();System.out.println(order.getClass().getSuperclass());}
}
class Order {}
-
Object
类中的主要结构方法名称 类型 描述 public Object()
构造 构造器 public boolean equals(Object obj)
普通 对象比较 public int hashCode()
普通 取得 Hash
码public String toString()
普通 对象打印时调用 -
equals
方法的使用-
是一个方法,而非运算符,只能使用于引用数据类型。
-
Object
类中equals()
的定义:和==
的作用是相同的,比较两个对象的地址值是否相同。public boolean equals(Object obj) {return (this == obj); }
-
通常情况下,我们自定义类的如果使用
equals()
的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object
类中的equals()
进行重写。
-
-
toString
的使用-
当我们输出一个对象的引用时,实际上就是调用当前对象的
toString()
。 -
Object
类中toString()
的定义public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
自定义类也可以重写
toString()
方法,当调用此方法时,返回对象的"实体内容"。
-
4.15:包装类的使用
java
提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征。
-
基本数据类型、包装类、
String
三者之间的相互转换public class WrapperTest {//String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)@Testpublic void test5(){String str1 = "123";int num2 = Integer.parseInt(str1);System.out.println(num2 + 1);}//基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)@Testpublic void test4(){ int num1 = 10;//方式1:调用String的valueOf(Xxx xxx)float f1 = 12.3f;String str2 = String.valueOf(f1);System.out.println(str2);}// JDK 5.0 新特性:自动装箱 与自动拆箱@Testpublic void test3(){//自动装箱:基本数据类型 --->包装类int num2 = 10;Integer in1 = num2;//自动装箱//自动拆箱:包装类--->基本数据类型System.out.println(in1.toString());int num3 = in1;//自动拆箱}//包装类--->基本数据类型:调用包装类Xxx的xxxValue()@Testpublic void test2(){Integer in1 = new Integer(12);int i1 = in1.intValue();System.out.println(i1 + 1);}//基本数据类型 --->包装类:调用包装类的构造器@Testpublic void test1(){ int num1 = 10;Integer in1 = new Integer(num1);System.out.println(in1.toString());}}
4.16:static关键字
static
可以用来修饰:属性、方法、代码块、内部类。
-
使用
static
修饰属性:静态属性- 我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
- 静态变量随着类的加载而加载。可以通过
类.静态变量
的方式进行调用。 - 静态变量的加载要早于对象的创建。
- 由于类只会加载一次,则静态变量在内存中只会存在一份,存在方法区的静态域中。
-
使用
static
修饰方法:静态方法- 随着类的加载而加载,可以通过
类.静态方法
的方式进行调用。 - 静态方法中,只能调用静态的方法或属性。
- 非静态方法中,可以调用非静态的方法或属性,也可以调用静态的方法或属性。
- 随着类的加载而加载,可以通过
-
static
注意点- 在静态的方法内,不能使用
this
关键字、super
关键字。 - 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。
- 在静态的方法内,不能使用
-
单例设计模式
-
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
-
饿汉式和懒汉式
-
饿汉式
public class SingletonTest1 {public static void main(String[] args) {Bank bank1 = Bank.getInstance();Bank bank2 = Bank.getInstance();System.out.println(bank1 == bank2);} }class Bank {// 私有化类的构造器private Bank() {}// 内部创建类的对象private static Bank instance = new Bank();// 提供公共的静态的方法,返回类的对象。public static Bank getInstance() {return instance;} }
-
懒汉式
public class SingletonTest2 {public static void main(String[] args) {Order order1 = Order.getInstance();Order order2 = Order.getInstance();System.out.println(order1 == order2);} }class Order {// 私有化类的构造器private Order() {}// 声明当前类对象,没有初始化private static Order instance = null;// 声明public、static的返回当前类对象的方法public static Order getInstance() {if(instance == null) {instance = new Order();}return instance;}}
-
-
饿汉式和懒汉式的区别
-
饿汉式
坏处:对象加载时间过长。
好处:饿汉式是线程安全的。
-
懒汉式
好处:延迟对象的创建。
-
-
单例模式的应用场景
- 网站的计数器。
- 应用程序的日志应用。
- 数据库连接池。
- 项目中,读取配置文件的类。
Application
也是单例的典型应用。Windows
的Task Manager(任务管理器)
就是很典型的单例模式。Windows
的Recycle Bin(回收站)
也是典型的单例应用。
-
4.17:代码块
代码块的作用是用来初始化类、对象。代码块如果有修饰的话,只能使用static
。
-
静态代码块
-
格式
static{语句; }
-
随着类的加载而执行,而且只执行一次。
-
作用:初始化类的信息。
-
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
-
静态代码块的执行要优先于非静态代码块的执行。
-
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构。
-
-
非静态代码块
-
格式
{语句; }
-
随着对象的创建而执行。
-
每创建一个对象,就执行一次非静态代码块。
-
作用:可以在创建对象时,对对象的属性等进行初始化。
-
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
-
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。
-
-
对属性可以赋值的位置
- 默认初始化。
- 显示初始化 / 在代码块中赋值。
- 构造器中初始化。
- 有了对象以后,可以通过
对象.属性
或对象.方法
的方式,进行赋值。
4.18:final关键字
final
可以用来修饰的结构:类、方法、变量。
-
final
用来修饰一个类此类不能被其他类所继承。
-
final
用来修饰方法表明此方法不可以被重写。
-
final
用来修饰变量此时的
变量
就称为是一个常量。-
final
修饰属性可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化。
-
final
修饰局部变量尤其是使用
final
修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
-
4.19:抽象类与抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
其中,要把一个类变成抽象类的话要使用关键字abstract
。abstract
可以用来修饰的结构有类,方法。
-
abstract
修饰类-
格式
权限修饰符 abstract class 类名 {}
-
此类不能实例化。
-
抽象类中一定有构造器,便于子类实例化时调用。【子类对象实例化的全过程】
-
开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。
-
-
abstract
修饰方法-
格式
权限修饰符 abstract 返回值类型 方法名(形参列表);
-
抽象方法只有方法的声明,没有方法体。
-
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
-
若子类重写了父类中的所有的抽象方法后,此子类方可实例化。若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用
abstract
修饰。
-
-
abstract
注意点abstract
不能用来修饰:属性、构造器等结构。abstract
不能用来修饰私有方法、静态方法、final
的方法、final
的类。
-
抽象类的匿名子类
public class PersonTest{ public static void main(String[] args) {new Person() {public void eat(){System.out.println("人要吃有营养的食物");}public void walk(){System.out.println("人走路");}};} }abstract class Person {String name;int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public abstract void eat();public abstract void walk(); }
-
模板方法设计模式
在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
public class BankTemplateMethodTest {public static void main(String[] args) {BankTemplateMethod btm = new DrawMoney();btm.process();BankTemplateMethod btm2 = new ManageMoney();btm2.process();} }abstract class BankTemplateMethod {public void takeNumber() {System.out.println("取号排队");}public abstract void transact();public void evaluate() {System.out.println("反馈评分");}public final void process() {this.takeNumber();this.transact();this.evaluate();}}class DrawMoney extends BankTemplateMethod {public void transact() {System.out.println("我要取款!!!");} }class ManageMoney extends BankTemplateMethod { public void transact() {System.out.println("我要理财!我这里有很多钱");} }
4.20:接口
接口就是规范,定义的是一组规则,体现了现实世界中"如果你是/要…则必须能…"的思想。继承是一个"是不是",而接口实现则是"能不能"的关系。
接口的本质是契约,标准,规范。其中,接口使用interface
来定义。
-
JDK7
及以前定义接口只能定义全局常量和抽象方法。
-
全局常量
interface Flyable {// 全局常量public static final int MAX_SPEED = 7900;// 省略格式的全局常量int MIN_SPEED = 1; }
-
抽象方法
interface Flyable {// 抽象方法public abstract void fly();// 省略格式的抽象方法void stop(); }
-
接口中不能定义构造器的!意味着接口不可以实例化。
-
Java
开发中,接口通过让类去实现【implements
】的方式来使用。如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化;如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍认为一个抽象类。 -
Java
类可以实现多个接口。权限修饰符 class AA extends BB implements CC, DD, EE {}
-
接口与接口之间可以继承,而且可以多继承。
-
接口的具体使用,体现多态性。
-
-
JDK8
定义接口除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法。
-
静态方法
public interface CompareA {//静态方法public static void method1(){System.out.println("CompareA:北京");} }
-
默认方法
public interface CompareA {//默认方法public default void method2(){System.out.println("CompareA:上海");}// 省略的默认方法default void method3(){System.out.println("CompareA:上海");} }
-
接口中定义的静态方法,只能通过接口来调用。
-
通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用是,仍然调用的是重写以后的方法。
-
如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中同名同参数的方法。【类优先原则】
-
如果实现类实现了多个接口,而这多个接口中定义了同名的默认方法。这时就必须在实现类中重写此方法。
-
在子类(或实现类)的方法中调用父类、接口中被重写的方法。
方法名(); // 调用自己定义的重写方法 super.方法名(); // 调用的是父类中声明的方法 接口名.super.方法名(); // 调用接口中的默认方法
-
4.21:内部类
Java
中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。
内部类的分类:成员内部类【静态、非静态】、局部内部类【方法内、代码块内、构造器内】
class Person{//静态成员内部类static class Dog{}//非静态成员内部类class Bird{}//局部内部类public void method(){class AA{}}{class BB{}}public Person(){class CC{}}
}
-
内部类的使用
- 作为外部类的成员
- 调用外部类的结构。
- 可以被
static
修饰。 - 可以被4种不同的权限修饰。
- 作为一个类
- 类内可以定义属性、方法、构造器等。
- 可以被
final
修饰,表示此类不能被继承。反之,则表示可以被继承。 - 可以被
abstract
修饰。
- 作为外部类的成员
-
如何实例化成员内部类的对象
// 静态成员内部类 外部类名.内部类名 变量名 = new 外部类名.内部类名();// 非静态成员内部类 外部类名 外部类变量名 = new 外部类名(); 外部类名.内部类名 内部类变量名 = 外部类变量名.new 内部类名();
-
如何在成员内部类中区分调用外部类的结构
public class InnerClassTest {public static void main(String[] args) {Person p = new Person();Person.Bird bird = p.new Bird();bird.display("黄鹂");} }class Person {String name = "小明";class Bird{String name = "杜鹃";public void display(String name) {System.out.println(name); // 方法的形参System.out.println(this.name); // 内部类的属性System.out.println(Person.this.name); // 外部类的属性}} }