是什么:首先是抽象,把事物抽象成一个类,其次才是封装。对外表示为一个对象,隐藏对象的属性和动作实现的细节,仅对外公开接口。
为什么:对外简化编程。高内聚低耦合使用者不需要了解具体的实现细节,而是要通过外部接口,一特定的访问权限来使用类的成员(包括成员函数和成员变量)。对内保护数据安全,使用者不可随意修改属性值。此外,封装符合面向对象设计原则的第一条:单一性原则,一个类把自己该做的事情封装起来,当内部的逻辑发生改变时,外部调用不用因此而修改。
怎么做:基本要求是把所有的成员变量(对象的属性)私有化。对每个属性提供get和set方法。
补充:与C++相比,Java的封装更彻底(C++向后兼容C,而C是面向过程的程序设计,所以C++允许把函数和变量写在类外,而java不允许)。Java成员函数的实现是直接写在class里面的(不管这个成员函数的实现有多复杂),而C++是在class里面写简短的函数声明,然后在class外面写函数具体的实现。当然C++也可以在类内实现,只是当时老师上课要求统一写在外面。在class里只写声明,在类外通过作用域符::写实现。
师兄补充:程序员封装类的好坏的评定标准:职责单一、扩展性、执行结果是否清晰;
简言之,封装就是无需暴露细节,暴露该暴露的,隐藏该隐藏的,让任意两者之间的耦合恰当。(eg.买车只需要会开即可,不需要关注引擎、发动机等工作细节)
是什么:当两个类具有相同的特征(属性,比如售票员和乘客都是“人”)和行为(方法,比如售票员和乘客都要吃饭和睡觉)时,可以将相同的部分抽取出来放到一个类中作为父类(上面的例子中,“人”就是父类),其他两个类继承这个父类。继承后子类自动拥有了父类的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法,即方法的重写(这里穿插着多态的内容)。子类不能访问父类中访问权限为private的成员变量和方法。子类可以重写父类的方法,即命名与父类同名的成员变量。
为什么:继承可以重复使用代码,降低冗余。代码重用是一点,最重要的一点是可以向上转型(将导出类看为它的基类的过程),这是Java面向对象最重要的特性——多态的基础。
怎么做:子类继承父类所有,只是访问受约束。通过关键字extends继承。
补充:java类可以分为三类
1.类:使用class定义,没有抽象方法
2.抽象类(不能被实例化 比如“水果”是一个抽象类,但无法实例化它,拿不出一个具体的东西):只能被继承用abstract class定义
3.接口(对抽象类进一步的抽象,比如飞机和鸟都会飞 “飞”就可以被设计成一个接口):使用interface定义,只能有抽象方法,所有的属性都是static final来定义的。
师兄补充:简单地说就是为了代码复用,为多态提供基础。
是什么:首先要知道方法的唯一性标识是方法名和参数(参数个数 类型 顺序)。同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。
多态可以说是“一个接口,多种实现”或者说是父类的引用变量可以指向子类的实例,被引用对象的类型决定调用谁的方法。
为什么:程序的可扩展性及可维护性增强。这里我觉得可以类比于C++中的模板类来理解,针对用户输入参数的个数和类型来执行相应的操作。我看其他论坛上也有观点是多态为了接口重用(以我现在的理解来看,就是子类可以复用父类的方法)。
怎么做:首先java实现多态需要有三个条件:继承(在多态中必须存在有继承关系的子类和父类)、重写(子类对父类的某些方法进行重新定义,在调用这些方法时就会调用子类的方法)、向上转型。
具备以上的前提条件后,实现多态的方法(C++中实现多态的方法有函数重写…重写是指子类重新定义父类虚函数的方法…与重载…指允许存在多个同名函数,而这些函数的参数列表不同 …)有编译时多态(方法的重载)和运行时多态(继承时方法的重写)。
编译时多态很好理解,就是编译器在编译的时候会根据函数不同的参数表,对同名函数的不同名称做不同的修饰,这些同名函数就成了不同的函数。也正是因为这一点,师兄说函数重载不算多态。
运行时多态依赖于继承、重写和向上转型(简单的理解为,导出类可以看成它的基类)。基于继承实现多态:
public class Toilet{
public void toilet(){
system.out.print("厕所")
}
}
public class M extend Toilet{
public void toilet(){
system.out.print("男厕")
}
}
public class W extend Toilet{
public void toilet(){
system.out.print("女厕")
}
}
class A {
public static void main(String[] args){
Toilet mToilet = new M(); //父类引用指向子类对象
Toilet wToilet = new W(); //父类引用指向子类对象
mToilet.toilet(); //执行M的toilet()方法
wToilet.toilet(); //执行W的toilet()方法
}
}
程序执行结果: 男厕女厕
代码链接:https://www.jianshu.com/p/1d6219ad43a1
代码来源:简书
|
基于接口实现多态:
public interface Toilet{
public void toilet();
}
//接口实现类
public class M implement Toilet{
public void toilet(){ system.out.print("男厕")
}
}
//接口实现类
public class W implement Toilet{
public void toilet(){
system.out.print("女厕")
}
}
class A{
public void static main(String[] args){
Toilet mToilet = new M(); //接口引用变量指向接口实现类对象
Toilet wToilet = newW(); //接口引用变量指向接口实现类对象
mToilet.toilet();
wToilet.toilet();
}
}
程序执行结果: 男厕女厕
代码链接:https://www.jianshu.com/p/1d6219ad43a1
代码来源:简书
很棒的例子:
class Human {
public void fun1() {
System.out.println("Human fun1");
fun2();
}
public void fun2() {
System.out.println("Human fun2");
}
}
class Programmer extends Human {
//函数的重载
public void fun1(String name) {
System.out.println("Programmer's fun1");
}
//函数的重写
public void fun2() {
System.out.println("Programmer's fun2");
}
}
public class Test {
public static void main(String[] args) {
Human human = new Programmer();//出现“类型不匹配时”,判断口诀:变量多态看左边,方法多态看右边,静态多态看左边
human.fun1();//没有对应的fun1()函数所以向上转型human类型,执行human中的fun1()函数。而funl()中含有fun2(),而此函数在子类中被重写了,所以执行子类的fun2(); }
/*
* Output:
* Human fun1
* Programmer's fun2
*/
}
师兄补充:跳出多态的细节,从整体上看多态就是一种表现形式有多种结果呈现。
刚刚告诉师兄我的作业名“多态继承封装”的时候,师兄告诉我说,我说反了。我听了之后当时还没有意识到这样的顺序有什么问题,但是经过我周末的总结之后,坐在工位上恍然大悟。封装->继承->多态这不单单是三个名词的简单罗列,更是一步步递进的关系。对于面向对象的程序设计,封装是基础,继承是多态的前提条件,三者逻辑顺序不可颠倒。
这三个思想是设计模式的基础,也是整个Java的基础,值得学而时习之。