面向对象是编程的重点,涉及到类的封装、继承、抽象、多态及接口的设计等。其中,封装、继承、多态是面向对象的三大特征。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修饰,但会牺牲部分安全性(只能用注释声明告诉用户不要修改它),因此这又是一个权衡的过程与责任的分配,因此在写类的时候要考虑其是否将来会被继承,来决定变量的访问限制。
作者:迷路的丸子
链接:https://www.jianshu.com/p/db392c22b808