面向对象是编程的重点,涉及到类的封装、继承、抽象、多态及接口的设计等。其中,封装、继承、多态是面向对象的三大特征。MVC架构中的"Model"部分的设计,实际上就是基于对象的设计,就是面向对象编程。而面向对象编程,是一门较深的学问。面向对象编程的方法可以通过具体实例总结,而在不同的具体环境及需求中,对象设计及方法的应用,是需要长期的实践经验来积淀。通过不断熟练面向对象编程思维,权衡功能以尽可能实现需求,来完善代码,进一步增强代码可读性和简洁性、实用性。因此,“武功秘诀”虽简单,但练就绝非一朝一夕。
本文基于Java面向对象编程,引用了一些实例及相应方法,以尽可能总结Java面向对象编程封装和继承的要素。
封装
-
封装:是一种面向对象编程方法,对类的具体实现细节部分进行包装隐藏,只暴露方法接口,但方法的实现不完全暴露给用户。
-
前提:正确地表达现实生活中的问题,并保证代码的拓展性和可维护性。
-
整体思路:是将把代码分成两个部分:接口和实现,以类的形式进行封装。其中,接口将涉及和外部的交互,属于对用户暴露的部分,应该保持稳定,因此修改维护代码的时候,可以只改变内容而不改变接口,使用方式不变;内部实现不需要暴露给外部用户,在接口功能不被影响的前提下,可以随意修改和重构。
-
封装方法:对类的内部状态的访问进行控制,根据需求只提供该提供的信息。采用private和final等修饰符对相应成员变量或方法进行访问或修改控制,再用getter和setter等方法作为接口,赋予私有变量的可读或可写权限。final限制了setter的使用,进一步防止私有变量被修改。
-
原因:可以防止用户使用的时候出错,或降低出错的可能。用户在调用编写的类的时候,有一定概率会出错,不能低估用户的想象力。但在团队合作中,必须考虑什么事情该自己承担,什么是用户的责任,什么是自己的责任。因此设计产品的时候,优先设计为private隐藏信息,再考虑是否有暴露出去的必要,根据具体需求改成public或其他访问权限。
-
原则:高内聚,低耦合,良好的封装是解耦的基础
低耦合:保留必须的方法
高内聚:大部分具体实现代码可以修改 -
应用场景:
1.发现代码有问题,需要进行错误修正或优化算法来改变实现
2.发展新能力,通过修改方法内容来实现,例如读取文件的方式可以从txt文档读取,发展为从word文档中读取,或从本地文件读取发展为网络信息读取
封装代码实例
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class News {
private String title;
private String content; //private限制了外部访问
public News(){} //缺省构造函数
public News(String title,String content) { //构造函数初始化成员变量
this.title = title;
this.content = content;
}
//可改变title和content,相当于一种setter方法
//title和content被final修饰时,此方法会报错
public void read(String fileUrl) throws IOException{
try {
//从Url读取内容
BufferedReader reader = new BufferedReader(new FileReader(new File(fileUrl)));
title = reader.readLine(); //赋值给title
content = reader.readLine(); //赋值给content
}
catch (IOException e) {
System.out.println("新闻读取出错"); //异常抓取
}
}
public String getTitle() { //getter方法,为title提供可读接口
return title;
}
public String getContent() { //getter方法,为content提供可读接口
return content;
}
//控制如何显示,显示格式,相当于一种getter方法
public String display() {
return title + "\n" + content;
}
}
测试代码
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
News news = new News("abc","blabla");
System.out.println(news.display());
news.read("");
System.out.println(news.display());
}
}
测试结果
abc
blabla
新闻读取出错
-
总结:尽可能向用户暴露更少的信息
原因:
1.给予用户的接口越少,他操作时基于的东西就越少,他需要知道的知识越少,这个程序在使用上就越简洁,使用成本越低
2.暴露的代码越少,客户依赖于我们写的代码就会越少,我们在修改维护时就可以进行更灵活的更改。而任何暴露给用户的内容,基本上是不能更改的,或者说更改暴露内容是万不得已的选择
继承
-
继承:是Java面向对象编程技术的一块基石,因为他允许创建分等级层次的类,即分层
-
方法:在声明子类的时候,通过关键字extends表达继承
-
特性:
1.子类拥有父类非private的成员变量和方法
2.子类可以拥有自己的成员变量和方法,即子类可以对父类进行扩展
3.子类可以重新实现父类的方法,override
4.Java只支持单继承,即只能有一个父类,省去了多继承带来的麻烦(C++不一样,可以多继承)
5.super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
6.this关键字:指向自己的引用
7.final <类>:防止类被继承 -
关系:is-a
父类具有子类的公共变量和方法,子类是更具体的父类,子类继承父类的特征和行为,使得子类对象具有父类对象的特征和方法,并拓展额外的功能。因此设计时将对象的公共部分抽象出来,从子类抽象出父类,会更直接更符合设计的直觉 -
原则:没有重复代码
-
构造方法:先有的父类,才有的子类,因此要先初始化父类,在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
其中,子类不能继承父类的构造器(构造方法或者构造函数),如果父类的构造器带有参数,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类的构造器没有参数,则在子类的构造器中不需要使用super关键字调用父类构造器,系统会自动调用父类的无参构造器(并不是不用)
父类代码实例
public class News {
protected String title; //可被子类访问
protected String content;
//无参构造器
public News(){}
// 构造的自由和责任交给用户
public News(String title, String content) {
this.title = title;
this.content = content;
}
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
// 控制如何显示
public String display() {
return title + "\n" + content;
}
}
子类代码实例
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class FileNews extends News{
public FileNews(String title ,String content) {
super(title, content); //调用父类构造函数
}
public FileNews() {} //默认有个super()
public void read(String url) throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader(new File(url)));
title = reader.readLine(); // 读取title
reader.readLine(); // 跳过空行
content = reader.readLine(); // 读取content
}
catch (IOException e) {
System.out.println("新闻读取出错"); // 异常处理
}
}
public String display() { // display()方法的override
return super.display() + "from 子类";
}
}
测试代码
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
News news = new News("abc","父类");
System.out.println(news.display());
FileNews fileNews = new FileNews("123","子类");
System.out.println(fileNews.display());
}
}
测试结果
abc
父类
123:子类
- 总结:继承为了继承成员变量,可能会牺牲private和final,protected修饰,但会牺牲部分安全性(只能用注释声明告诉用户不要修改它),因此这又是一个权衡的过程与责任的分配,因此在写类的时候要考虑其是否将来会被继承,来决定变量的访问限制。
网友评论