首页 > 代码库 > java面向对象

java面向对象

     Java语言完全支持面向对象的继承、封装、多态,纯粹的面向对象的程序设计语言。Java以对象为中心,整个程序由类(Java的最小的程序单位)组成。Java是一个静态语言,一个类完成定义后,只要不重新编译,则类和其对象的方法和属性是固定的。Java引入了包的机制,提供类的多层命名空间,用于解决类命名冲突,类文件管理。一旦在源文件中使用了package packageName,则意味着该源文件里的所有类都属于这个包,完整类名为包名+类名。java的包机制需要源文件里的package指定包名还需要class文件放在对应的路径下。同一个包的类可以自由访问,不同的包需加前缀,即使是父子包,例如:xxx.xxx.Test test = new xxx.xxx.Test test();为解决此问题引入了import关键字用于导入指定包层次下的某个类或全部类import package.subpackage...ClassName/*,有时两个包中有相同的类名,此时则又需要使用全路径指定哪个包名下的此类。JDK1.5之后也可以使用import static package subpackage...ClassName.filedName/*来导入类的某个或全部静态属性。

       继承是面向对象实现软件复用的重要手段,子类作为一种特殊的父类拥有父类的属性和方法;Java是单继承的语言(即只能有一个直接父类),因为多重继承可能引发继承结构的混乱,程序难以理解。封装指的是将对象的属性和细节隐藏起来,不允许外部直接访问,通过公有的方法暴露给外部,让方法来操作或访问这些属性,从而隐藏类的细节、在方法内实现逻辑控制、限制对属性的不合理访问、可进行数据检查,有利于保证对象信息的完整性,便于修改,提高代码的可维护性;多态指子类对象可以赋值给父类变量,但运行时依然动态表现出子类的行为特征。类时对象的抽象,对象是类的一个实例,对象具有标识唯一性、分类性、多态性、封装性、模块独立性好等基本特点,而类的封装性则提高了类的内聚,降低了对象之间的耦合。消息时一个类的实例和另一个类的实例之间传递的信息,实现了对象间的交互。

       抽象是将软件系统的功能需求抽象成我们所要使用的数据、行为特征。

       基于对象和面向对象的区别在于,基于对象仅仅实现了封装,未实现继承和多态,例如JS。

       Java语言里,一切皆是对象(除8个基本数据类型),我们通过引用来访问它,而且对象具有唯一性,每个对象都有唯一的标识引用它,如果失去了标识,则GC会进行回收。

       类之间的结构关系:1.一般—特殊结构:也被称为分类结构即继承,表现为"is a"关系,Java通过extands实现,继承关系中从多个子类中抽象出其共有父类的过程,类似组合关系里从整个类里提取出潜入类的过程即继承关系中从父类派生子类的过程,类似于组合关系里把潜入类对象组合成整体类的过程2.整体—部分结构:也被称为组装结构即组合,表现在“has a”关系,Java通过在一个类中保存另一个对象的引用来实现。如果对已有的类做一番改造获取一个特殊的版本即抽象的类能改造成适用于某种特定需求的类,则用继承。例如动物和狼。如果类之间有明确的部分和整体的关系,例如人和手,则应用组合,把手作为人的嵌入属性从而用手的方法实现人的方法。

       类是一种引用数据类型,类里只能包含属性、方法、构造器、初始化块、内部类(包括枚举和接口)5种成员,类一般按如下方式定义:[ 修饰符 ] class 类名{零到多个构造器定义.. 零到多个属性... 零到多个方法...}修饰符可以是public、final也可以完全省略,类名首字母大写。定义属性的格式语法:[ 修饰符 ] 属性类型 属性名 [ =默认值 ]修饰符一般是public、private、protected、static、final等。属性名首字母小写。定义方法的语法:[ 修饰符 ] 方法返回值类型 方法名(形参列表){零到多条语句组成的方法体},修饰符可以是public、private、protected、static、final、abstract。static修饰的方法只能访问static的成员,因为他们均属于类方法,而不用其修饰的属于实例成员和方法(原因参见①)。构造器的语法定义格式如下:[ 修饰符 ] 构造器名 (形参列表){ 零到多条语句组成的方法体 }修饰符可以是public、protected、private其中之一,也可以省略,但不允许修饰符是static,名字必须和类名相同,构造器的返回值是隐式的。系统提供的构造器是没有参数的,当我们自己定义了一个构造器时,系统将不会提供默认的构造器。使用new构造一个对象时,堆和栈中存储的内容和C相同,需要大家仔细研究一下。当对象不被任何引用所使用时,垃圾回收器将回收他们,和C/C++一样将这些引用变量置为null,即切断了引用关系,等待GC回收。成员变量和局部变量的首字母命名小写,其余单词首字母大写。

      Java的参数传递机制是值传递,即传递实际参数的副本,而参数本身不会受到影响。形参可变的方法定义如下public void test(int a, int b, String... persons)他的定义和public void test(int a, int b, String[] persons)效果相同,但调用两者时,形参可变的方法即可传入数组也可以传入多个String值,而后者只能传递数组,定义形参可变的方法只能有一个可变的形参,且位于方法形参列表的最后。

      变量包括成员变量(实例属性和static修饰的类属性)和局部变量(形参,方法局部变量、代码块局部变量),类的属性的作用域同类的生命周期,实例属性的作用域同实例。成员变量定义时无需初始化,但局部变量定义则需要初始化(形参除外,因为形参在调用方法时,系统已完成了初始化)。如果方法中的局部变量和成员变量同名,可使用this或类名访问成员变量。类是在第一次使用的时候初始化,此时类成员变量在堆中分配内存并初始化,而成员变量在第一次new实例时初始化且存储区域不同于类成员变量。局部变量只有在初始化后才会分配内存空间,且位于栈上。

       访问控制符;private->default->protected->public,详细补全图

       构造函数式Java创建对象的重要途径⑦(即使工厂模式、反射等方式创建对象,实质依然是构造器),Java提供至少一个构造器(构造器支持重载)并经常在构造器中进行自定义的初始化操作。由于构造器主要用于被其他方法调用返回类的实例,因此通常将构造函数设置为public,从而允许系统中的其他类创建该类对象,特殊情况才可能设为protected(子类调用),private(不允许其他类创建)。

       继承(扩展)的语法如下:修饰符 class SubClass extends SuperClass{//类定义部分}Java的子类不能继承父类的构造器。扩展和派生是对立的同概念,如果没有显示的指定,类的直接父类是java.lang.Object,要么就是间接父类。继承虽然实现了重用,但会破坏父类的封装性从而造成耦合,而组合实现了类重用且有更好的封装。如果出于类服用的目的,组合也可以实现。通常是在原来作为子类的类中定义private 父类对象并在构造函数中初始化传入此对象赋值给此private对象,同时实现原父类的方法⑩。

       多态,Java的引用类型有两种,一种是编译时的类型,一种是运行时的类型,分别在声明变量的类型和实际运行时赋给变量的对象的类型决定,如果编译时的类型和运行时的类型不一致则为多态。Java允许把一个子类对象赋给一个父类引用变量,无需任何类型转换,被称为系统自动完成向上转型,因为就会出现BaseClass baseclass = new SubClass();baseClass在运行的时候表现出不同的行为,但需注意如果调用了父类中没有而子类中没有的方法,虽然运行时确实存在,但是编译时无法通过(可以通过强制类型转换成子类的类型⑨)。但是属性和方法不同,属性没有多态性即访问编译时的属性,在此过程中实际创建了两个对象(父类和子类对象)。

       初始化块是Java除了属性、方法和构造器外的第四种成员,一个类里面可以有多个初始化块。它也可以对java对象进行初始化操作,语法如下:[static]{ //任何可执行代码}初始化的顺序是先执行初始化块或声明属性时指定的初始值,最后是构造函数的初始化。初始化一般用在给所有对象设置同一个初始值,从而提高代码的可维护性,类初始化块即static修饰的在对象初始化前执行,他不能对实例属性进行初始化,以上的也和构造器一样会向让追溯,类初始化阶段,先执行最顶层父类的静态初始化块,依次向下,仅执行一次,接着对象初始化,先执行最顶层父类的初始化块、构造器然后依次向下。JVM第一次使用类时,准备阶段为所有的静态属性分配内存,初始化阶段即是执行静态初始化块和类属性时指定的初始值。

       ①static方法只能访问static成员的本质原因是在static方法中使用this关键字,无法找到合适的this对象,所以static不能使用this引用,也就不能使用非static的成员和方法。当类中的一个方法调用另一个方法时默认隐藏了this,当调用静态方法和属性时,默认隐藏了类。注意this对象的使用和作用域。static的方法和属性是类和实例共同拥有,因此类和实例(即使实例定义为null,但如果调用的是实例方法和属性则会报NullPointerException)均可调用,且执行结果相同,普通的方法和属性只能用实例对象调用且执行结果可能会因不同的对象而不同。Java在创建某个类对象时会隐式创建该类的父类对象,使用super即是指向父类对象,this则是指向到调用该方法的对象(子类对象),即super指向的是this指向对象的父对象。因此super也不能出现在static的类方法中(找不到匹配的父对象)

      ②递归要向已知方向递归

      ③重载:方法命相同,形参列表不同(方法的返回值不能用来区分重载的原因是因为调用时可能采用f()不需要返回值的情况,此时系统无法识别),如果形参列表是可变参数,则视情况可能需要传入A.test(new String[](""))来区分单字符串的使用。覆盖是子类重写父类的方法,遵循“两同两小一大规则”即方法名相同、形参列表相同,两小指子类方法的返回值类型比父类方法的返回值类型更小或相等,子类方法声明抛出的异常也如此。一大指的子类方法的访问权限应比父类更大或相等。覆盖要求同是类方法或者实例方法,可以使用super调用被覆盖的实例方法,使用父类类名。方法调用父类中被覆盖的类方法。如果父类方法是private,不可覆盖,不可访问。子类可以重载父类的方法。属性覆盖:当子类和父类有同名的属性时,可通过super访问父类的属性,而类属性则可直接用类名访问。

      ④设计原则:1.使用局部变量的好处是缩小了变量的生存时间,减少开销。缩短变量的作用域,提高程序的内聚性。2.封装的目的:一个类往往是一个小模块,我们应该只让这个模块公开外界需知道的内容,隐藏其他一些内容,进而避免在一个模块内直接操作和访问另一个模块的数据,模块的设计追求高内聚(尽可能把模块的内部数据、功能实现细节隐藏在模块内部独立完成,不允许外部直接干预)低耦合(仅暴露少量的方法给外界使用)。3.类里面的绝大部分属性都应该是private,一些static修饰的类似全局变量的属性才考虑使用public修饰。一些辅助实现该类的工具方法,也应该定义为private.如果父类里面的方法仅希望被子类重写,而不想被外面直接调用,则应该用protected修饰这些方法。希望暴露给其他类自由调用的方法应该使用public修饰,例如构造函数,因此父类(顶级类)多数都定义为public,因为大多数情况下希望被其他自由类使用。3.父类的设计原则:尽量隐藏父类内部数据,属性设为private,不让子类直接访问;不要让子类随意访问、修改(使用覆盖即重写)父类的方法,父类中辅助其他的方法应用private修饰,需要被外部访问的方法用public修饰,但是又不希望被子类重写的应该使用final修饰。如果父类希望某个方法被子类重写,但不希望被其他类自由访问,可以使用protected来修饰该方法,不要在父类构造器中调用被子类重写的方法(会引发空指针错误)。如果想把某个类设为最终类(即不能作为父类),则可以使用final修饰这个类,JDK中的java.lang.String和System即是如此,或者将父类的构造器用private修饰,这样子类就无法调用父类的构造器,也就无法继承。此时可提供一个静态方法用于创建该类的实例。什么时候使用继承:子类额外增加属性,而不仅仅是对父类属性值的改变,子类需要增加自己独有的行为方式(包括增加新的方法或重写父类的方法)。

      ⑤一些描述类或者对象的固有信息的变量,应定义为成员变量。

      ⑥java常用包:java.lang包含Java语言的核心类String、System、Math、Thread等,Java默认导入了java.lang,因此使用String,System均不会出错。java.util包含Java大量的工具类/接口和集合框架类/接口,例如Arrays和List、Set等。Java.NET包含了一些Java网络编程相关的类/接口。java.io包含了输入/输出编程相关的类/接口。java.text包含了java格式化相关的类。java.sql包含了java进行JDBC数据库编程的相关类/接口。Java.awt包含了抽象串口工具集相关的类/接口,主要用于构建图形用户界面。java.swing包含Swing图形用户界面编程相关的类/接口,可用于构建平台无关的GUI程序。

      ⑦构造器是否完全负责创建Java对象:不是,在调用构造器前系统已经为对象分配了内存空间并默认初始化,只是此时仍不能被外部调用,只能在构造器中经过this指针引用它,当构造器执行结束,这个对象作为构造器返回值返回,通常还会付给另一个引用型变量,从而给外部访问此对象。并且通常建议如果自定义了有参数构造函数,额外最好编写提供一个无参数构造函数。使用this调用另一个重载的构造器只能在构造器中使用,且必须放在构造器执行体的第一句。子类调用父类的构造器使用super来实现,也必须位于第一行,因此this和super不会同时出现。不管用不用super调用父类构造器的初始化代码,子类都会在自己的构造器初始化之前调用父类构造器。并且会一直追溯到java.lang.Object

     ⑧访问某个方法中的属性,访问顺序为:方法中局部变量->查找当前类中是否有此属性(变量)->查找父类,依次上溯到Object。

     ⑨基本类型的数据类型转换只能在数值间进行,即:整型、字符型、浮点型,但不能和布尔型之间进行数据类型转换。引用类型只能把一个父类变量转换成子类类型且满足此实例编译时是父类对象,运行时是子类对象,否则将发生ClassCastException异常,例如double d =3.14; long a = (long)d; Object obj = "hello" String objStr = (String)obj;Object objPri = new Integer(5)String str = (String)objPri异常;为了使代码更健壮,可以使用if(obj instanceof String){String str = (String)objPri}

     ⑩使用组合关系因为需要new父类并传入子类的构造函数中从而初始化子类中的父类对象,会不会造成多余的开销,因为继承关系时子类也要默认创建父类,只是此时是程序员显示的手动创建被嵌入的类对象,所以不会产生多余的开销。

java面向对象