Java是一种基于面向对象概念的编程语言,使用高度抽象化来解决现实世界的问题。 面向对象的方法将现实世界中的对象进行概念化,以便于在应用之间进行重用。例如:椅子、风扇、狗和电脑等。html
Java里的类(Class)是一个蓝图、模板,或者称之为原型,它定义了同一类事物的相同属性和行为。实例(Instance)是某个类的一个具体实现,同一个类全部的实例拥有相同的属性。举例来讲,你能够定义一个类叫作“房子(House)”,这个类拥有一个属性叫作“房间数(number of room)”,这样你就能够建立一个“房间数”为2的“房子”实例,你还能够建立一个“房间数”为3的“房子”实例,等等等等。前端
优势:java
面向对象软件开发的若干优势在于:面试
面向对象软件系统(OOPS)的四大主要特征为:编程
封装机制在对象之间提供了一种隐藏域可见性的协议。Java中使用可见性修饰符private将方法和变量限制在类内部。Java提供的可见性修饰符包括public/ default/ protected/ private,用来在不一样层面上隐藏变量、方法和类,但最终目的在于封装那些不须要进行修改的东西。实践代表,每一个类应该只存在一种被修改的缘由,而封装(Encapsulate)让这种“惟一缘由”原则成为现实。后端
同时,最佳实践代表,封装意味着将会常常改变的东西隐藏起来,以防止对其余类形成破坏。安全
多态是指使用相同的接口来应对不一样的底层形式(例如数据类型)的能力。这就意味着同一个类可使用一个共同的接口来实现多种不一样的功能,并能经过传递特定的类引用来动态触发这些功能。多线程
一个经典的多态的实例为“形状”类(Shape),以及全部继承Shape的类,如方形(square)、圆形(circle)、多面体(dodecahedron)、不规则多边形(irregular polygon)和长条(splat)等。在这个例子中,每一个类中都拥有一个本身的Draw()函数,客户端程序代码能够简简单单地以下所示:并发
Shape shape=new Square ();
执行Shape.area() 能够获得任何形状的正确面积。app
多态的美妙之处在于,不一样类里的代码不须要知道本身所在的是哪一个类,它们的使用方式都是同样的。
面向对象的编程语言在运行时所实现的多态过程叫作动态绑定。
注:多态是指根据调用函数的对象来选择更具针对性的方法的特性。当没有抽象类的时候就可使用多态。
方法覆盖实现多态:覆盖涉及到两个不一样的方法,一个父类的方法,另外一个则是子类中的方法,两个方法具备相同的函数名和方法签名。
覆盖能够以不一样的方式对不一样的对象类型定义相同的操做,例如:
while(it.hasNext()) { Shape s = (Shape) it.next(); totalArea += s.area(dim); //多态方法调用,将根据对象类型自动调用正确的方法 }
重载涉及的是同一个类内具备相同名称,但方法签名不一样的多个方法。能够用不一样的方法为不一样的数据定义相同的操做。咱们常常所说的静态多态实际上并非真正意义上的多态。
方法重载实际上就是指两个方法使用相同的名称,但参数不一样。这与继承和多态彻底没有关系。重载方法不是覆盖方法。[Head First Java深刻浅出]
当进行类声明时,一个属性域名称能够与多种不一样的数据类型相关联,一个方法也能够与不一样的参数类型和返回类型相关联,Java支持使用泛型的参数多态性。例如,一个list对象能够经过泛型来接收它所包含的数据类型:
List<String> list = new ArrayList<String>();
覆盖依赖于具体的类实例。多态的关键之处在于你能够继承一个类,而该类的子类所定义的实例对象又能够对父类中定义的方法进行了覆盖。而静态方法是不依赖与具体实例的,所以覆盖这一律念不适用于静态方法。
在Java设计早起有两点考虑直接致使了这一现象。第一是对性能方面的考虑:以前人们对Smalltalk语言(一种面向对象编程语言)运行太慢(垃圾回收和多态调用所致)的批评不绝于耳,Java的设计者决定回避这一弊端。第二是考虑到Java的预期受众主要是C++开发人员,而使静态方法能直接被调用恰好能迎合C++编程人员的开发习惯,同时因为不用上溯类层级结构来查找要调用的方法,而是直接调用指定类中的特定方法,这一设计使得代码运行很是快速。
继承是指派生类中包含了基类中的全部的行为(即方法)和状态(即变量),并能经过该派生类进行访问。继承的关键好处在于它提供了代码重用和避免重复的一遍机制。
继承类经过重用父类的方法并添加一些新的功能来扩展应用程序的功能。这回致使紧耦合,若是你想对父类进行修改,你必须清楚其全部子类的具体细节以防止阻断。
这是一种软件复用性,新类(子类)继承已有的父类,重用父类的特征并能添加一些新的功能。
所以,举例来讲,若是你有一个Parent类和一个扩展(使用关键字extends)Parent类的Child类,那么Child类继承了Parent类全部特征。
抽象意味着只须要开发类的接口和功能声明,而不须要实现具体的实施细节。抽象类呈现的是接口,而不须要具体实现,将对象的实施与行为或实现分离开来,进而经过隐藏无关的细节来减小复杂度。
封装做为一种策略,被用做广义抽象的一部分。封装是与对象状态相关的——对象将本身的状态封装起来并对外界不可见,类外部的用户只能经过该类的方法来与其进行交互,但不能直接改变其状态。所以,类能够将与状态相关的实施细节经过抽象隔离开来。
抽象是一个更泛化的概念,能够经过子类来实现具体的功能。例如:在Java标准库中,List是“一串事物”的抽象,ArrayList和LinkedList是List的两个具体的类型,做用于抽象List的代码一样抽象地不指明具体所使用的List类型。
若是没有经过封装隐藏底层状态,也就不可能进行抽象处理。也就是说,若是一个类的内部状态所有都是公开的,内部功能没法被更改,该类也就没法进行抽象。
在程序设计过程当中,你但愿基类只是其派生类的一个接口,也就是说,你不但愿任何人能实例化该基类。你只是想隐式(能够实现多态性)地提出它,以即可以使用它的接口。那么你可使用abstract关键字来定义一个抽象类。
为该抽象类设定一些限制,全部使用该抽象类的子类都必须实现其中的抽象方法,并提供多态性。
抽象类中能够既包括抽象方法和具体方法,若是一个方法是抽象方法,其所在的类必须被声明成抽象类。反之否则,若是一个类是抽象类,其中不必定包括抽象方法。
若是一个方法只提供了方法签名,但没有被具体实现,则这个方法是一个抽象方法,该方法的具体实现是在扩展该抽象类的子类中进行的。
抽象方法不能被实例化,其余类只能扩展它。
抽象类定义了一些默认的行为,而将具体的功能留给子类来实现。例如:List是一个接口,而抽象类AbstractList提供了List的默认方法,这些默认方法能够被子类ArrayList继承或从新定义。
Interface关键字使得接口相比于抽象类更进了一步,接口中不能定义实现的方法。实现(使用关键字implements)接口的非抽象类必须实现该接口的全部方法。接口是面向对象(OO)中的一个很是有用和经常使用的概念,它将接口和具体实现分离开来,并保证数据安全性:
接口是抽象类的延伸,java了保证数据安全是不能多重继承的,也就是说继承只能存在一个父类,可是接口不一样,一个类能够同时实现多个接口,无论这些接口之间有没有关系,因此接口弥补了抽象类不能多重继承的缺陷,可是推荐继承和接口共同使用,由于这样既能够保证数据安全性又能够实现多重继承。
合成
代码的重用性能够经过集成和合成来实现,可是用合成实现代码重用比继承居右更好的封装性,由于对后端代码的修改无需任何对仅依赖于前端类的代码的破坏。
合成石实现类之间“has-a”关系的设计技术,咱们可使用Java的继承或对象合成来实现代码重用。
合成表示的是对象之间的关系,以椅子chair为例,一把椅子chair有一个座位seat,一个靠背back,几条腿legs,词组“has a”表示一把椅子所包含,或所使用的其余实体,这种“has a”关系就是合成的基础。
No. | 合成Composition (has a) | 继承Inheritance (is a) |
1 | 主张多态和代码重用 | 主张多态和代码重用 |
2 | 运行时已经得到对象 | 编译时动态得到对象 |
3 | 运行时能够替换实施 | 编译时能够更换实施 |
4 | 松耦合,子类的存在并不依赖与父类(特别是接口驱动的情形下) | 紧耦合,子类严格依赖于父类 |
5 | 当House中有一个Bathroom时可使用,不能说House是一种Bathroom | 继承是单向的,例如:House是一个Building,但Building不是一个House |
注:不要仅仅只为了代码重用而使用继承,若是类之间不存在“is a”关系,建议采用合成来实现代码重用。
对象关系中合成与聚合的区别:
聚合:聚合是一个类属于一个集合的关系。描述的是一种“部分与总体”的关系,“总体”不存在的状况下,“部分”是能够存在的,这是一种弱关系类型,没有循环依赖性。例如,订单和产品的关系。
合成:合成也是一个类隶属于一个集合的关系。描述的是一种只有“总体”存在,“部分”才能存在的“部分与总体”的关系。在这种关系下,若是“总体”被删除,则“部分”也就不复存在,体现的是一种强关系类型。例如,多边形和组成多边形的点,订单和订单明细。
对于基本的OOPS(面向对象)概念,请看Java面试参考指南的第一部分。访问修饰符规定了一个类如何访问另外一个类及它的成员(包括方法和变量)。
Java中有下列访问修饰符:
注意:
下表描述了对各个成员修饰符所运行的访问权限:
修饰符 | 被修饰者 | 描述 |
public | 外部(Outer)类,接口,构造器,内部(Inner)类,方法和变量 | 可在包外部访问 |
protected | 构造器,内部类,方法和变量 | 包内访问或包外的任何子类访问 |
private | 构造器,内部类,方法和变量 | 只可以在它们被声明的内部访问 |
无描述符(default) | 外部类,内部类,接口,构造器,方法和变量 | 只可以在它们所在包中被访问 |
除了上述基本的访问符以外,下表描述了Java中的其它描述符。它们可以改变类和它的成员(如:方法、变量和内部类)的行为。
修饰符 | 内部类/类 | 方法 | 变量 |
static | 一个静态内部类是另外一个普通类的成员,不是该类的对象成员。 | 一个静态方法能够直接被类访问,而且被该类的对象所共享。 | 静态变量是类级变量且在jvm中只可以出现一次。 |
abstract | 抽象类不可以被实例化,它只能被其它类所继承。 | 抽象方法只能被声明在抽象类中,它的实现代码只能被包含在子类中。 | 不适用 |
synchronized | 不适用 | 它在类上加了一个监视器,使得访问该方法只能经过得到该方法所属对象的锁来进行。 | |
transient | 变量不可以被序列化。 | ||
final | 类不能被继承 | 方法不能被重载。 | 变量值变成常量。 |
final修饰符只可以做用在类、方法和变量上。它的做用是全部被final修饰的内容不能被改变。如:
注意:若是一个final变量是对象的引用,这意味着该引用的值是必定不会改变的,可是对象的成员值能够改变。
static修饰符能够被应用在变量、方法、代码段和内部类中。若是须要Java实例变量被存储在单独的内存中,或须要变量对一个单独类的全部对象所通用,那么静态修饰符须要被应用在该变量的声明中。
在Java中,并非全部的类都必须单独定义。你能够把一个类定义在另外一个类的内部中。这个在内部定义的类就叫作内部类。包围类则被称为外部类。所以,当你定义了一个内部类,它和包围类的其它变量、方法和构造器成员同样成为该类的成员。
当你访问外部类的私有数据成员时,JDK会在外部类中建立包级访问权限的(package-acess)成员函数以便内部类来访问外部类的私有成员。这种结果会致使一个安全漏洞。一般状况下,咱们应当尽可能避免使用内部类。
只有当内部类仅仅只和外部类的上下文相关或者内部类须要被设置为私有的且只能被外部类访问的这些状况下,咱们才使用内部类。内部类主要被用来实现相似Iterators、Comparators这些辅助类。它们被使用在外部类的上下文中。
非静态嵌套类(即内部类)对它嵌套的外部类的成员拥有所有的访问权限。而静态嵌套类并不包含对外部类的实例引用,这就使得静态嵌套类不可以调用外部类实例的非静态方法或者访问外部类的非静态字段。
在声明成员字段和方法上,非静态嵌套类不可以定义静态字段和方法。可是,静态内部类则能够定义静态字段和方法也能够定义非静态字段和方法。
非静态内部类的实例是经过使用外部类的对象引用来建立的,也就在说在内部类中已经定义了外部类实例。可是静态嵌套类实例的建立过程当中并不涉及到外部类的引用,这就是说它并不拥有包围类的实例。
示例:
public class OuterClass { class InnerClass { // static int x; not allowed here } static class StaticInnerClass { static int x; // allowed here } } class Test { public static void main(String... str) { OuterClass oc = new OuterClass(); OuterClass.InnerClass obj1 = oc.new InnerClass();// need of inclosing // instance OuterClass.StaticInnerClass obj2 = new OuterClass.SIC(); // no need of reference of object of outer class } }
native修饰符只可以修饰方法。native修饰符意味着方法的实现体是在JVM以外。
注意:抽象方法的实现体是在子类中,而native方法的实现体则彻底不在Java的虚拟内存中,而是在其它程序库中。
transient修饰符只可以应用在变量中,transient变量不作为它所属对象的持久状态的一部分进行存储。它并不须要被序列化,主要用做安全键(security key)或者链接(connection)等。
synchronized修饰符在多线程程序中对临界区代码的访问进行控制。synchronized关键字是保持代码线程安全的工具之一。
对于某一对象的一个同步方法进行交叉访问是不可能的。当一个线程在执行某一对象的同步方法时,全部调用该同步方法的其它线程都将被阻塞直到第一个线程完成对该方法的调用。
其次,当一个同步方法执行结束时,对相同对象的后续同步方法调用,它会自动创建以前发生(happens-before)的顺序关系。这保证了该对象的状态更改对全部的线程均可见。
当你将一段代码块标记为同步化时,你须要使用对象做为该同步块的参数。当一个执行线程到达该代码块时,它首先须要等待在该对象的同步块上已经没有其它的执行线程。然而,一个线程能够进入不一样对象锁定的同步方法块。
但对同一对象的非同步方法能够直接方法而无需锁检测。
若是你同步化的是静态方法,那么你同步时获取的是该方法类的锁而不是实例的。这就意味着当你同步的是静态方法时,整个类都将被阻塞。这样其它的静态同步方法也将被阻塞。
当一个线程进入同步化实例的方法时,其它线程则不可以再进入该实例的其它同步化方法。
当一个线程进入同步化的静态方法时,其它线程则不可以再进入该类的其它同步化静态方法。
注意:同步化的静态方法和同步化的非静态方法之间是没有多少联系的。例如:若是静态和非静态同步方法可以并发的执行,就须要将你的非静态方法显式的声明为在它本身的类上进行同步(如:同步MyClass.class{…})。
只有变量可以被定义为volatile。这些变量可能会被异步修改,所以编译器须要对他们额外的关注。Volatile修饰符保证任何读取该字段的线程都可以获取它的最近修改值。
volatile使用:使用volatile的一种通用状况是将它用做boolean变量并做为判断线程终止的标志。
volatile和synchronize之间的区别:
所以,volatile关键字只用来在线程内存和主内存之间同步单个变量值,synchronized关键字用来同步线程内存和主内存之间的全部变量值以及如何锁定和释放一个监视器。清楚的是,synchronized比volatile有着更大的开销。
volatile变量不容许出现和当前主存中的数据值不一样的的本地副本。
更准确地说,被声明为volatile的变量必须保证它的数据值在全部线程的中是同步的。也就是当你在任一线程中访问或者更新一个变量时,全部其它线程可以当即访问到相同的值。
abstract修饰符适用于类和方法。抽象类不可以被实例化,它必须被继承才可以访问。abstract不可以应用到类的成员变量。
示例:Animal类能够有travel方法同时它的各子类(蛇,狗,鸟)也有它们本身的travel方法。所以就不可以在超类中提供travel方法,而是须要将travel方法在超类中声明为抽象的。
注意:
设计上,你但愿基类对继承类仅仅只做为接口。这意味着你不但愿任何人实例化该基类。你只想进行向上类型转换(隐式转换,它可以提供多态性),这样就只能使用该基类的接口了。这是经过使用abstract关键字来修饰该类实现的。
提供限制来不实例化抽象类,这样就须要任何使用该抽象类的人来实现它。并提供多态性。
何时使用抽象类?
抽象类让你能够定义一些默认行为并促使子类提供任意特殊化行为。
例如:Spring的依赖注入就使得代码实现了集合框架中的接口原则和抽象实现。