目录
面向对象的三大特征:封装、继承、多态
1. 面向对象的三大特征之封装
1.1 封装的含义
面向对象的封装有两层含义
-
类:属性、方法这些定义在类内部,其实就是一种封装,但并不是这里的三大特征之一
-
面向对象的封装是使用访问修饰符private将属性私有化的设计思想。
封装(英语:Encapsulation),是指为了安全考虑,在开发的过程中,会讲属性私有化 (private关键字修饰) ,需要提供对应接口完成设置和访问 (公开的setter和getter方法) 的过程。
1.2 封装的目的
-
隐藏一个类中不需要对外提供的实现细节;
-
使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑, 限制对属性的不合理操作;
-
便于修改,增强代码的可维护性
1.3 封装的优点
-
良好的封装能够减少耦合。
-
类内部的结构可以自由修改。
-
可以对成员变量进行更精确的控制。
-
隐藏信息,实现细节。
1.4 封装的实现过程
1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
public class Person {
private String name;
private int age;
}
这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法(公开的setter和getter方法) ,用于对私有属性的访问,例如:
public class Person{
private String name;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
访问上面这个封装后的属性,通过创建对象,调用对应的get/set方法进行属性的操作
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setAge(2);
System.out.println(person.getAge()); // 2
}
}
1.5 拓展知识:JavaBean
POJO对象:(Plain Ordinary Java Object) 标准的Java bean 对象
根据封装来写,私有化属性,提供公开的setter和getter方法,至少两个或者以上的构造方法
JavaBean是一种Java语言写成的可重用组件。符合如下标准的Java类:
-
类是公共的
-
有一个无参的公共的构造器
-
有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以 用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP 页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用 户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关 心任何改变。
案例:博客系统设计
板块:Board、文章:Article、作者:Author、回复:Comment/Reply等类
Board类
//import java.util.Arrays;
public class Board {
private int id; // 板块id
private String name; // 板块名称
private String intro; // 简介或描述
// 开发中尽量使用单项关联
// private Article[] articles;
@Override
public String toString() {
return "Board [id=" + id + ", name=" + name + ", intro=" + intro + "]";
}
public Board() {
super();
}
public Board(int id, String name, String intro) {
super();
this.id = id;
this.name = name;
this.intro = intro;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// public Article[] getArticles() {
// return articles;
// }
//
// public void setArticles(Article[] articles) {
// this.articles = articles;
// }
}
Article类
import java.util.Date;
//import java.util.Arrays;
public class Article {
private int id; // 文章id
private String tittle; // 标题
private String content; // 内容
private Author blogAuthor; // 作者
private Board board; // 所属板块
private Date publishTime; // 发布时间
private Date modifyTime; // 修改时间
@Override
public String toString() {
return "Article [id=" + id + ", tittle=" + tittle + ", content=" + content + ", blogAuthor=" + blogAuthor
+ ", board=" + board + ", publishTime=" + publishTime + ", modifyTime=" + modifyTime + "]";
}
public Article(int id, String tittle, String content, Author blogAuthor, Board board, Date publishTime,
Date modifyTime) {
super();
this.id = id;
this.tittle = tittle;
this.content = content;
this.blogAuthor = blogAuthor;
this.board = board;
this.publishTime = publishTime;
this.modifyTime = modifyTime;
}
public Article() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTittle() {
return tittle;
}
public void setTittle(String tittle) {
this.tittle = tittle;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Author getBlogAuthor() {
return blogAuthor;
}
public void setBlogAuthor(Author blogAuthor) {
this.blogAuthor = blogAuthor;
}
public Board getBoard() {
return board;
}
public void setBoard(Board board) {
this.board = board;
}
public Date getPublishTime() {
return publishTime;
}
public void setPublishTime(Date publishTime) {
this.publishTime = publishTime;
}
public Date getModifyTime() {
return modifyTime;
}
public void setModifyTime(Date modifyTime) {
this.modifyTime = modifyTime;
}
}
Author类
//import java.util.Arrays;
public class Author {
private int id; // 作者id
private String name; // 姓名
private boolean gender; // 性别
private int age; // 年龄
private String intro; // 简介或描述
// private Article[] articles;
@Override
public String toString() {
return "Author [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", intro=" + intro + "]";
}
public Author(int id, String name, boolean gender, int age, String intro) {
super();
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.intro = intro;
}
public Author() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
}
Comment类
package com.openlab.day12.blog;
import java.util.Date;
public class Comment {
private int id; // 回复id
private String content; // 内容
private Author blogAuthor; // 作者
private Date publishTime; // 发布时间
private Date modifyTime; // 修改时间
private Article article; // 文章
@Override
public String toString() {
return "Comment [id=" + id + ", content=" + content + ", blogAuthor=" + blogAuthor + ", publishTime="
+ publishTime + ", modifyTime=" + modifyTime + ", article=" + article + "]";
}
public Comment(int id, String content, Author blogAuthor, Date publishTime, Date modifyTime, Article article) {
super();
this.id = id;
this.content = content;
this.blogAuthor = blogAuthor;
this.publishTime = publishTime;
this.modifyTime = modifyTime;
this.article = article;
}
public Comment() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Author getBlogAuthor() {
return blogAuthor;
}
public void setBlogAuthor(Author blogAuthor) {
this.blogAuthor = blogAuthor;
}
public Date getPublishTime() {
return publishTime;
}
public void setPublishTime(Date publishTime) {
this.publishTime = publishTime;
}
public Date getModifyTime() {
return modifyTime;
}
public void setModifyTime(Date modifyTime) {
this.modifyTime = modifyTime;
}
public Article getArticle() {
return article;
}
public void setArticle(Article article) {
this.article = article;
}
}
2. 面向对象的三大特征之继承
2.1 继承的概念
在面向对象中,类与类之间可以存在继承关系,继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
生活中的继承:
兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
食草动物和食肉动物又是属于动物类。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
生活中继承的例子还有很多,如财产继承、人的特征等等
2.2 如何实现继承
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
类的继承格式
class 父类 { } class 子类 extends 父类 { }
父类 (超类、基类);子类
案例:RichMan类和他的子类Son类
RichMan类
package com.openlab.day12.inherit;
public class RichMan {
public String surname = "张";
public double money = 1000000;
private String honor = "Java";
// 仅提供有参的构造方法 不提供无参的
public RichMan(String surname, double money, String honor) {
super();
this.surname = surname;
this.money = money;
this.honor = honor;
}
// public RichMan() {
// // TODO Auto-generated constructor stub
// }
@Override
public String toString() {
return "RichMan [surname=" + surname + ", money=" + money + ", honor=" + honor + "]";
}
public void say() {
System.out.println("我的钱留给我的儿子!");
}
void test1() {
System.out.println(this.money);
}
protected void test2() {
System.out.println(this.honor);
}
}
Son(与父类不同包下)
package com.openlab.day12;
import com.openlab.day12.inherit.RichMan;
public class Son extends RichMan {
/*
* 发现当父类只存在有参构造方法的时候 ,子类去继承会报错
* 原因是每一个构造方法都会存在一个隐藏的super()去调用父类的无参构造器
* 而这里父类只是显示的声明了有参构造器 并没有声明无参构造器
* 在子类的构造方法中,一定会调用父类的某个构造方法
* 处理方法:通过super关键字指定调用父类的哪个构造方法 如果未指定就会自动调用父类无参构造
*
* 简单的说:在子类的构造方法里调用了父类有参的构造方法就行了
*/
public Son() {
super("张", 1, "1");
}
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.money);
System.out.println(son.surname);
// System.out.println(son.honor); // 错误:honor is not visible
// son.test1(); // 错误:The method test1() from the type RichMan is not visible
son.test2();
son.say();
}
// 在子类中,重写方法,
@Override
public void say() {
// TODO Auto-generated method stub
super.say();
System.out.println("111111");
}
// @Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("Hello!");
}
}
注意:private关键字修饰的属性和方法都是无法被子类继承的
protected关键字修饰的方法,就是用来给子类继承的!!!
2.3 继承的作用
-
继承的出现减少了代码冗余,提高了代码的复用性。
-
继承的出现,更有利于功能的扩展。
-
继承的出现让类与类之间产生了关系,提供了多态的前提。
2.4 继承的类型
在Java中,也存在继承,Java是一种典型的单继承编程语言。需要注意的是 Java 不支持多继承,但支持多重继承。
2.5 继承的特点
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
3. 私有化构造函数和单例模式
3.1 私有化构造函数的常景
1. 工具类 (静态方法或者静态属性)
一般在一些工具类中,把全都是一些静态属性和方法,不需要修改,只需要调用即可,会把他的构造函数私有化,以不让其他的类创建该类的实例。之间通过类名称访问,不需要创建对象,如Math、Arrays等。
2. 单例模式
将构造函数私有化,使得该类不能以new的方式创建对象,而让该类使用自己创建好的那一个对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
3.2 单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
应用实例:
- 1、一个班级只有一个班主任。
- 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
- 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
- 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
3.2.1 单例模式 (饿汉式)
饿汉式,是多线程安全的,实现容易,这种方式比较常用,但容易产生垃圾对象。
public class Singleton {
// 饿汉式:单例模式
private static Singleton singleton = new Singleton();
// 1.私有化构造函数
private Singleton() {
}
// 2.给室友者提供提供创建好的对象
public static Singleton newInstance() {
return singleton;
}
public static void main(String[] args) {
Singleton s1 = Singleton.newInstance();
Singleton s2 = Singleton.newInstance();
System.out.println(s1 == s2); // true
}
}
3.2.2 单例模式 (懒汉式)
懒汉式,是线程不安全的,实现容易,这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式不要求线程安全,在多线程不能正常工作。
public class Singleton2 {
// 懒汉式:单例模式
private static Singleton2 singleton2 = null;
private Singleton2() {
}
private static Singleton2 newInstance() {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
public static void main(String[] args) {
Singleton2 s3 = Singleton2.newInstance();
Singleton2 s4 = Singleton2.newInstance();
System.out.println(s3 == s4); // true
}
}
4. 方法重写 (覆盖)
重写 (OverWrite)
覆盖 (OverRide)
发生在继承中,指的是,子类继承了父类的方法后,该方法不能满足子类,重写该方法,以达到满足子类使用。
访问修饰符 返回值类型 方法名() {}
注意重写的方法中不可缩小访问权限,尽量不要更改,返回值类型不可改变
方法重载 (OverLoad):
5. 注解 (annotation)
JDK5.0提供的新特性,利用反射技术,可以很轻松使用标准方法、属性、类等,从而扩展功能。
@ 加在属性、方法的上方
@Override :覆盖
@Deprecate :过时警告
6. super关键字
super 在Java中,是一个指针,类似于this关键字
this关键字代表当前对象的引用,用在当前类中。主要有三个用法
- this(),代表在当前类中调用其他的构造方法,并且只能在构造方法中写,只能调用一次,只能写在第一行。
- this.date,代表访问当前类中的成员属性。
- this.func(),代表调用当前类中的其他成员方法。
注意:this并不代表当前对象,而是代表当前对象的引用,因为在当前类的构造方法中就已经可以使用this关键字了,而只有当构造方法完成之后该类的对象才算是被实例化出来。
super关键字代表父类对象的引用,主要有以下三个用法
- super(),代表在当前类中调用其父类的构造方法,默认是不写的,是调用父类的无参构造。
- super.data,代表访问父类的成员属性。
- super.func(),代表访问父类的成员方法。
7. 对象的创建流程
-
使用java命令将源码(.java)进行编译,生成字节码文件(.class)
-
javac命令执行字节码文件
-
将字节码文件加载进虚拟机(JVM),静态方法区开始加载静态资源
-
JVM从静态方法区读取主函数,并加载进栈(执行栈被创建)
-
main函数开始执行,创建对象的代码,如:Son son = new Son();
-
在堆内存中开辟对象的内存空间,并分配地址
-
创建成员变量并进行默认初始化
-
子类构造函数从非静态方法区加载进栈开始执行
-
第一句先执行父类的构造函数
-
父类构造函数执行,为子类继承到的成员变量进行初始化(对象内存空间里的父类空间)
-
父类构造函数弹栈执行完张
-
子类构造函数继续执行,此时先对成员变量进行显式初始化
-
再执行子类构造函数的内容,进行针对性初始化
-
执行完张,子类构造函数弹栈,将对象的内存空间地址赋予相应的引用变量
文章的结尾给大家普及几个面向对象的专业术语:
-
OO (Oriented Object) :面向对象
-
OOP (Oriented Object Programming):面向对象的编程
-
OOD (Oriented Object Design):面向对象的设计
-
OOA (Oriented Object Analysis):面向对象的分析
-
OOT (Oriented Object Test):面向对象的测试