java基本思想

面向对象java

众所周知,面向对象编程思想是java的基本思想。java的整个体系和技术实现都是以这个思想为基础。(这个经过类和接口能看出来,后面提到)程序员

对这个事情的认知度甚至变成了不少公司的面试标准。好比有的公司会问你什么是面向对象、面向对象和面向过程的区别、面向对象有哪些特性。面试

不过细心的人会发现,没有公司会问你,你是怎么将面向对象思想应用到实际工做中的。这是由于:
其一,大多数人了解面向对象的思想是为了应付面试;
其二,确实不多有人会在实际工做中使用面向对象的思想。
第一点是由于教育机构(大学、培训机构)的老师大都是从c也就是面向过程的语言走过来的。他们的思想已经习惯了使用面向过程编程。而后老师们就使用这个思想去教导学生,这就造成了新一代年轻的程序员也会习惯面向过程的思想去思考问题。师徒制也存在这个问题。如此往复循环,就造成了第二点。而如此积累,当工做量逐渐加大的时候就会出现程序架构设计不合理、数据库设计不方便、不想写注释、不知道些什么注释等状况。而后就变成了“卧槽这代码是我写的么???”。这个时候再赶上个需求变动,心态估计就崩了。数据库

在这里感慨一下,真正将面向对象的思想落地,才是高级编程语言程序员的出路。(有兴趣的能够了解一下领域模型驱动,就是常说的DDD)编程

对于面向对象思想的我的理解:架构

面向对象思想,是一种将现实世界抽象成代码的思想。
即经过将现实世界中独立个体(事物)的属性和行为封装在一个类中,经过抽象和继承实现个体间的相互影响、协做。造成了对象多态和行为多态的特性。
话说得稍微有点抽象了。简单来讲就是把实际转换成代码,代码之间相互做用造成了代码间的相互协调,表现了实际中一个事物的多种形态。这种思想叫作面向对象的思想。数据库设计

与面向过程的区别编程语言

面向过程就是分析出解决问题所须要的步骤,而后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就能够了;面向对象是把构成问题事物分解成各个对象,创建对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
因此,面向对象相较于面向过程来讲更具备拥抱变化的能力。
说到这里,忍不住跟看这篇文章的人推荐一部电影:《模仿游戏》。主演是鼎鼎大名的卷福。故事情节大体是主角为了破译德军的密码建造了一个机器。聚焦于机器的建造过程来看,机器使用了穷举法来实验密码的正确性,设计这个机器就是为了破解这个密码,这样来看是一个面向过程的设计思想。聚焦于整部电影来看,主角建造这个机器有一部分缘由是为了记念儿时的玩伴,甚至还给机器取了玩伴的名字。当须要破译密码的时候就把工做交给这个机器,机器计算完了以后会返回结果。主角在心里里把这个机器当作了一我的,在思想上将这我的抽象成了机器的实现逻辑。这样来看就是面向对象的思想了。这个机器也是人工智能的起源——图灵机。函数

咳咳,有点扯远了。不过我确实推荐朋友们找一找本身专业相关的电影看看,能极大程度提升对专业的兴趣和积极性。this

分割线1,其实这里也没有特别长,可是下面主要讨论技术和思想了,给上面的一些闲扯作个分隔。
封装

正如上面所说,java体现面向对象思想的一个基本点就是封装。这个动做的设计初衷就是将现实世界中的事物描述成代码。因此设计了一个容器叫作类,类中能够放描述事物的属性(成员变量)和描述事物的行为(成员方法)。
封装完类以后将一些类分门别类的放在对应的包中,而后整合成了一个项目。如此一来,经过封装这个动做就把一系列功能封装成了对象。

接口(抽象)

单纯从封装这个动做来看,将实际事物封装为类的思想过程就是抽象。而经过抽象,能够把一些类中的共同点聚集到一个类中复用。那么将不一样实现相同名字的变量和方法抽象到一个不用写逻辑实现的类中,这个类就叫作接口。这个接口就是最高层次的抽象了。

那么从实际工做来看,能够从两个格局理解接口。
一个是系统级的战略格局,一个是代码级的战术格局。

从战略格局来看,系统之间的相互交互须要制定规则和标准;制定了统一的规则和标准以后,你们按照这个标准去开发。这样能够作到多个协调系统之间的并行开发,以及系统之间的耦合性下降。
战略上,接口定义了系统之间相互协调通信的方式和入口。(从单个系统看其余的系统,接口也描述了其余系统的功能)

从战术格局来看,类能够描述实际事物的具体属性和行为。也就是说类能够表明一个具体事物,可是没有办法表示事物能够实现的功能。
举个例子说明:dog类能够描述狗。那么二哈和拉布拉多属于狗的品种,是狗的一种属性,所应该做为dog类中的一个属性(成员变量)。而狗能够啃骨头,能够定义成dog类中的一个成员方法。
可是拉布拉多能够作导盲犬,二哈能够作雪橇犬。固然你能够把这个当作狗的职业封装为一个属性,可是显然是不合适的。由于,导盲犬和雪橇犬能够完成的动做没有几个同样的。动做须要定义成方法。因此这个时候须要一个新的类来定义这两个职业,而不能封装为一个属性。
但是,经过继承的方式实现由有了单根性的限制(一个类只能继承一个类)。对于代码描述事物的功能就很麻烦了。因而java就使用接口解决了这个问题。
因此从战术格局来看,接口描述的是对象的功能(类描述的事对象的属性,这个后面说)。

抽象类(抽象和细节)

抽象类实际上是类到接口中间的一个过分产物。

从代码上来看,抽象类既能够定义复用的方法逻辑还能够定义须要子类实现的抽象方法。
因此说,抽象类是在类设计以后才抽象出来的。
对比接口,能够说接口是设计出来的结果而抽象类是重构出来的结果。二者方向不同。是不一样过程的产物。

实现类(细节)

类是描述实际事物的基本单位,而这里要说的实现类,则是特指实现了接口的类。
这种类虽然在本质上跟普通的类是同样的,可是意义不一样。
实现类是用来实现接口的逻辑的,他有接口的规则限定。而普通的类则没有这么明确的限定。
因此实现类的命名通常是接口名+Impl组成;而普通类根据实际要表明的事物命名便可。

这里小总结一下,接口是抽象的规则,实现类是具体的细节。二者缺其一,那么这个系统从设计上来说是不完整的。接口是编写代码以前设计出来的;其目的是制定规则,以即可以进行并行开发和拥抱变化。而抽象类,是抽象和细节的混合物,是在开发阶段重构出来的结果,目的是复用代码。

继承

继承是面向对象中思想中的一个概念,其实基类拓展超类实如今一个类中能够直接调用另外一个类的方法是经过拓展指针实现的(extends)。不过从实现效果上来看,跟领域模型中的父子很是相像。因此就把基类称为子类,超类称为父类了。

extends

继承的技术实现就是利用extends关键字,其实他就是个指针而已。(这里不叫引用,由于它确实和java中的引用不太同样,因此加以区别、便于理解)
先来看一段代码:

public class A{}
public class B extends A{}

你们都知道B类继承了A类因此B中可使用A中的构造方法和成员(变量和方法)。
这是由于当编译器检测到B类后面的extends关键字时,会把其后的类名(也就是A)的地址(类的彻底限定名)编译在B类的class源码中。

以后运行的时候,类加载器会在加载B类的时候将A类的信息同时加载到方法区中;
而java默认调用子类构造方法的时候会先调用父类的构造方法。
那么这个时候经过B类实例化出来的对象天然就能调用A类的方法了。

因此,这样来看。extends关键字其实就是一个指针变量,其值为class定义时关键字后面的类名,也就是拓展类的地址。

extends翻译过来就是拓展的意思。因此B extends A。B就能调用A中的成员。反过来A并无拓展B ,因此A就不能调用B中的成员。这就造成了子类可使用父类的成员,而父类不能使用子类的。
并且为了保证程序的设计顺序性和可读性,java限制了两个类互相拓展。就是说当B extends A时,A就不能再拓展B了。再加上拓展的单根行,效果就变成了一个父类能够有多个子类,而一个子类只能有一个父类。这样若是把成员看作财产的话;效果就跟领域模型(现实社会)中的父子很是相像了。因此基类和超类也被称为子类和父类,而拓展也被称做继承。

implments

接口的设计一是为了打破java继承的单根性,二是为了跟接口表明的含义更加贴切。
上面说到继承是单根性的。这样就形成了父类只能是经过代码抽象出来的复用代码类,很难从设计阶段定义;并且确实有了多继承的需求。(接口表明了功能,一个对象能够同时有多个功能)
因此java设计了接口来表明设计模型中的功能和系统间的规则。

那么接口中的方法都是没有具体实现的,因此须要一个类来实现这个接口中的规则。那么当一个类跟一个接口关联起来的时候,天然也就称做这个类实现了这个接口。

接口之间是能够相互继承的就像大规则包含小规则;接口和类之间是实现的,由于接口定义的规则最终要经过类来定义运行逻辑。

this

this关键字跟extends其实也是一个指针变量,可是this的用法跟c中的宏定义很是相像;因此要跟extends关键字加以区分。

this() 调用当前对象的构造方法
this.成员(变量或方法) 调用当前对象的成员(变量或方法)

发生上面两种状况的调用时,其实能够直接想象成new一个本类的对象赋值给引用a。而后把this换成a,就好了。所说的this表明当前调用的对象,就是这个意思。

super

因为类的构造方法执行顺序是: 父类构造方法–>本类成员变量初始化–>本类构造方法。

这样就还须要一个指向父类对象的指针。因此就设计了super关键字。用法和this关键字同样,只是super指向了父类。

super() 调用父类对象的构造方法
super.成员(变量或方法) 调用父类对象的成员(变量或方法)

这里要说一下extends 、this、super的区别:
三者都可以当作是指针变量,可是extends存放的是类的地址;
this和super存放的是对象的地址。
换句话说extends是在编译期工做的;而this和extends是在程序运行时工做的。
这样记忆就不容易混淆了。

多态

多态是从面向对象思想和设计的角度来讲的。
当咱们设计了一个类时,在另外一个类中调用这个类的对象,可是表现的好像是这个类的父类;这种状况就是向上造型。
有些时候咱们须要一个类的子类,可是持有的倒是其父类的引用,这个是咱们须要作一个强制类型转换来获得咱们须要的子类。这个操做就是所谓的向下造型。
上下造型在面向对象编程时很是实用。当这种方式在一个系统中频繁使用的时候,表现出来的效果常常是一个对象扮演多种角色,还能够相互转换。这种现象被人们称为多态。

对象多态(类型转换、上下造型)

对象多态是多态中的一种主要表现形式。发生在对象之间,也就是说对象多态的基本单位是对象,不可向下细分。

这里先说一下实现原理,仍是比较巧妙的。

 


咱们来看,main方法第一行第二行不用解释,就是建立了两个对象 (ac表示 aClass bc同理)。
而后会发生以前说extends关键字时说过的现象:bc能够调用A类和本类的成员,也就是变量a和变量b。可是ac却只能调用A类的成员。缘由在extends那里说过了这里就不在详细说明。

第三行就是咱们常说的向上造型。原理大概是这样的:
编译器从左到右检测代码,首先检测到A ab。此时编译器会读取A类的信息,并标记有一个局部变量ab数据类型是A。

而后检测到操做符 = 编译器会去检查右侧的表达式。若是是字面量表达式(好比3+5或者简单字符串拼接)那么会直接在编译期计算,以后检查结果的数据类型是否合规;若是是复杂表达式(除了字面量表达式以外的表达式)则只检查数据类型是否合规。
咱们上面的代码属于复杂表达式,编译器只进行数据类型的检查。
java语法规定new 后面只能是类的构造函数,也就是说这里必须实例化一个对象出来。这里编译器只须要将new关键字后边的字符拿出来,而后把()和;拿掉,就获得了实例化对象的数据类型了。
这也就是java要求构造方法必须与类同名的缘由。

而后编译器拿到了右边表达式结果的数据类型B跟左边的数据类型做比较。这个时候extends关键字就开始起做用了。因为B extends A,编译器会在读取B类信息的时候同时将A类的地址添到信息中,算做B类信息的一部分。这样,B就能匹配两种数据类型,一种是本身,一种是扩展类,也就是A。

这样在比较左右两边数据类型的时候就变成了 A = B|A 。表达式成立,编译经过。

同理mian方法第四句,因为A并无拓展B。因此数据类型比较不成立就会编译报错。

到这里就是向上造型的大概实现原理了。一样的原理也能够推出继承的几种限制,好比成员访问权限和抛出异常类型的限制。这里不作详细介绍了。

可能有人会问上面的第四句,比较左右数据类型的时候不该该是B|A = A嘛?这样应该也成立呀。
这个实际上是编译器的识别规则,上面提到了构造方法的执行顺序,第一步就是执行父类构造方法。也就是说真正运行起来的时候,只有执行构造方法,才能有父类的对象,才能调用父类的成员。因此这里的表达式应该是B = A,是不成立的。

说完了向上造型,向下造型就很好理解了。上面的例子中,A类型的变量ab 实际的值确实B类的对象。
而作.运算的时候,编译器只根据变量的数据类型决定能.出来的东西。因此父类型的引用,虽然是向上造型,可是仍然不能调用子类的成员。若是某一个时刻咱们确实须要使用子类的成员,可使用强制类型转换。也就是:

B b = (b)ab;
1
这里编译器不会报错,是由于检测到了强制类型转换。可是转换成功的前提是ab的值确实是B类型的。若是不是,在程序运行的时候会抛出类型转换异常。

到这里,上下造型就说完了。利用上下造型极大的提升了程序的灵活性,使得java编程跟面向对象思想更加贴切。在上下造型被大范围应用的时候,同一个对象能够赋值给不一样类型的变量,在程序中某一个运行时段表明的意义也就不同,这就造成了对象多态的效果。

行为多态(重写和重载)

行为多态的基本单位是方法,不可向下细分。行为多态是发生在对象内部的。
表现为一个对象在表明的意义不一样时行为也不一样。

这里提一下重写和重载的区别,好多面试里也问道。其实只要搞清楚这两个动做是怎么回事就很容易回答这个问题了。

先说重载。通常发生在同一个类中,编译期就会绑定,方法名相同参数列表不一样。
这个设计是为了模仿银行窗口这样的对象。假设客户要取钱,可是事先并不知道客户要那什么凭证来取钱。因此设立一个窗口,不管什么凭证,只要是取钱的业务均可以到窗口办理。
有了重载就能够很方便的实现了。

再说重写。发生在父子类中,运行期才会肯定,方法名相同参数列表也相同。
注意若是在父子类中方法名相同参数列表不一样的状况是重载不是重写。虽说父子类不是同一个类,可是有拓展关系。就是说在编译期就能看到两个类中的成员,因此在父子类中也能够发生重载。
这里来举个例子:

 


仍是上面的AB类,此次在B类中定义了一个跟A类中print方法彻底相同的print();

当在编译时,第一句向上造型上面说过了。ab.print(1);编译器会去查看ab变量的数据类型,也就是A类,A类中含有print方法,参数为int。符合语法,编译经过。同理第三句也是同样。

当在运行时,程序会取ab的值,这个时候ab就是一个B对象了,那么去内存中B对象的领域里取print方法运行,实际上执行的逻辑是B类中定义的print方法的逻辑。因此就打印了balabala。这个现象好像A的方法被从新写了一遍同样,因此就把这种现象叫作重写了。

而后执行第四句,一样的逻辑,找到ac的数据类型,比较正确编译经过;
执行的时候取ac的值,执行A对象的print方法,天然就打印1了。

到这里重写和重载就说了个大概了。这两种语法规定的出现,让java对象实现了一个对象多种行为姿态的效果。被称为行为多态。

行为多态和对象多态放在一块儿组成了java语言的第三大特性,统称为多态。而java语言又是面向对象的表明语言,天然也就说面向对象的第三大特性是多态了。

其实这里是推崇了一下java,事实应该是先有思想的特性才有语言的实现。上面所说只是为了调一下胃口,并不表明真实历史。 不过单凭java的开源,我的以为就值得推崇一下。
---------------------
做者:AgentRich
来源:CSDN
原文:https://blog.csdn.net/Agent_Rich/article/details/81986910

相关文章
相关标签/搜索