Java回忆录(七)-------面向对象-----进阶篇

来源:互联网 发布:c语言编程简易小游戏 编辑:程序博客网 时间:2024/06/11 00:06

         最近太累了,每天都是12个小时以上,上周末加班了,博客整理的有些乱,也没有更新前两章,将就的先看看吧,以后有时间我会把他们弄的好看一些。今天和大家一起继续复习一下Java的面向对象特性。上文中提到了所有的类都是Object的子类,因此当我们调用System.out.println(p)打印p所引用的对象,此时实际调用的是Object的toString()方法,即类名+@+hashCode,子类可以重写此方法实现对象的自我描述即:重写toString,并返回“类名[Filed1=值1,Field2=值2,...]”例如:return "p[weight=" + weight +", height=" + height +"]"。

        类成员(包括方法、初始化块、内部类和枚举类)不能访问实例成员(包括属性Field、方法、初始化块、内部类和枚举类),因为类成员是属于类的,作用域比实例大,类成员初始化时,实例成员还未初始化。

       单例类:始终只能创建一个实例的类。一般应用在其他类创建此类实例时无意义,为了节省系统的性能(创建和回收对象的系统开销),此时把该类的构造函数设为私有的。这样在调用该类的方法时还不存在对象(实例),因为调用该方法的不能是对象,只能是类,所以需要在类中提供一个公有的static方法给外部访问本类中缓存的instance。请参考:http://cantellow.iteye.com/blog/838473

      自定义不可变类:使用private和final修饰符修饰Field;提供带参数构造器,用于根据传入参数来初始化类里的Field;仅为该类提供getter方法,不提供setter,因为普通方法无法修改final的Field。重写Object类的hashCode和equals方法,equals方法以关键Field来作为判断两个对象是否相等,还要保证相等的两个对象的hashCode方法也相等(例如String)。如果不可变类的成员中引用的是一个引用,此情况后续补齐。

      抽象方法和抽象类:使用abstract修饰(abstract不能和final、static、private共存)抽象类即是一种模板的设计即模板模式、它为子类提供了模板,子类在此基础上进行扩展、改造、但子类总体上会大致保留抽象类的行为方法。抽象类不能创建实例、只能被当做父类来继承。模板模式的设计规则:1抽象父类可以只定义需要使用的某些方法,把不能实现的部分抽象成抽象方法,留给其子类实现。2父类中可能包含需要调用的其他系列方法的方法,这些方法可由父类实现,也可子类实现。父类提供的方法只是定义了一个通用算法,其实现也许并不由自身实现,而必须依赖于其子类的辅助。

      接口:更加彻底的抽象,只可以包含抽象方法。接口体现了实现和规范分离的设计。降低了各个模块之间的耦合,为系统提供更好的可扩展性和可维护性。接口定义语法如下:[修饰符]interface 接口名 extends 父接口1,父接口2...{零到多个常量定义;零到多个抽象方法定义}修饰符可以是public或者默认(包权限:只有在相同包结构下的才可以放访问此接口),接口仅能继承接口、不能继承类。接口里不能包含构造器和初始化定义,只能包含Field(只能是常量)方法(只能是抽象实例方法)内部类(内部接口、枚举)且所有的均为public或者省略。,接口里定义的常量系统自动化把它定义为public static final且定义时需初始化(因为接口里无构造器和初始化块)接口里定义的方法默认会加上public abstract,接口里定义的内部类、接口、枚举默认都采用public static修饰,一个Java的源文件中最多只能有一个public接口,且该文件名与public接口名相同。接口支持多继承,接口不能用于创建实例,但是可以定义引用类型的变量,此时引用类型的变量必须引用到其实现类的对象。所有接口类型的引用变量都可直接赋值给Object类型的变量。因为该接口一定是某个类的显式或隐式子类。且在实现接口的方法时,类的方法必须都是public,因为子类的访问权限要大于等于父类(接口)。

      实现接口:[修饰符]class 类名 extends 父类 implements 接口1,接口2...{ 类体部分 }类必须实现接口的全部抽象方法,否则此方法只能定义成抽象类。

      内部类(嵌套类):把一个类定义在另一个类的内部。包含内部类的类被称为外部类或者宿主类。内部类的作用:1内部类提供了更好的封装,可以把它隐藏在外部类中,不允许同一个包中其他类访问该类,当需要在外部类中组合另外一个类,且该类只在外部类中有效,则可以这样定义。2内部类成员可以直接访问外部类的私有数据,因为内部类相当于外部类的成员,同一个类的成员之间可以互相访问,但外部类不能访问内部类的实现细节,例如内部类的成员变量。3匿名内部类适合于那些仅需要使用一次的类。命令模式中的即是。

       内部类的定义,public class OuterClass{//定义内部类}在方法中定义的内部类被称为局部内部类(不可成为类成员,匿名内部类也不叫类成员),成员内部类分为静态和非静态两种,外部类可在方法中定义new非静态内部类,然后调用其方法和属性,和使用普通类一样。而内部类则可以直接访问外部类的任何方法和属性。原因主要是在外部类创建的时候不会同时创建其内部类,但内部类存在的时候,一定存在外部类。在非静态内部类中保存了一个它寄存的外部类对象的引用,所以当外部类成员变量、内部类成员变量与成员方法中的局部变量同名时,可通过this、外部类.this进行区分编译生成的文件如下:OuterClass$InnerClass.class。外部类的静态成员不允许访问内部类的任何细节包括内部类创建的实例(比如new内部类),非静态内部类不允许定义任何的静态成员。

      静态内部类(类内部类):使用static修饰后,内部类即属于外部类,而不属于外部类的某个对象。但static不能修饰外部类(因为外部类的上一级是包)静态内部类只能访问外部类的静态成员,因为静态内部类的对象并不是寄存在外部类对象里的,而是寄存在外部类的类库本身,静态内部类的对象存在时,并不存在一个被它寄宿的外部类的对象静态内部类的对象里只有外部类的引用,但没有外部类对象的引用。外部类静态方法、静态初始化块中可以使用静态内部类来定义变量、创建对象等。外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。接口中定义的内部类默认是public static,接口里也可定义内部接口,但无意义。

     使用内部类:(1)在外部类内部使用内部类:如上所述(2)在外部类以外使用非静态内部类,则内部类不能使用private修饰,private修饰的只能在外部类的内部使用,省略访问控制修饰符的内部类,只能被与外部类处于同一个包中的其他类所访问。使用protected修饰的内部类,可被与外部类处于同一个包中的其他类和外部类的子类所访问,public可以在任何地方被访问。在外部类以外的地方定义内部类变量如下:OuterClass.InnerClass varName,在外部类以外的地方创建非静态内部类的语法如下:OuterInstance.new InnerConstructor();(3)在外部类以外使用静态内部类,创建静态内部类时,不需要创建外部类对象,因此语法如下:new OuterClass.InnerConstructor(),静态内部类的外部类相当于一个包名。

     局部内部类:只能定义在外部类的方法中,不可使用访问控制符和static修饰符(所有的局部成员均不可,因为他们的上级程序单位都是方法)局部内部类定义变量、创建实例或派生子类,那么都只能在局部内部类所在的方法中进行。文件名的各式如下:OuterClass$NInnerClass.class,用以区分同名的布局内部类。

     匿名内部类:匿名内部类适合创建那种只需要一次使用的类,创建实例后立刻消失。不能重复使用,定义的语法如下:new 父类构造器(实参列表)| 实现接口(){ 类体部分}匿名内部类必须实现一个接口或者继承一个父类,匿名内部类不能是抽象类,因为系统在创建匿名内部类时会立即创建它的对象。匿名内部类因为没有类名,所以无构造器,但匿名内部类可以定义实例初始化块,通过实例初始化块来完成构造器的需要完成的事情,最常用的创建匿名内部类的方式是需要创建某个接口类型的对象。匿名内部类必须实现接口或抽象父类里的所有抽象方法。待补充

     闭包和回调:需代码才能分享清楚

     枚举类:类的对象(实例)是有限的而且固定的。手动实现枚举类(1.通过private将构造器隐藏起来,2.把这个类所有可能的实例都是用public staic final属性来保存,3.如果有必要,提供一些静态方法,允许其他程序根据特定参数来获取与之匹配实例)J2SE1.5之后增加了enum用以定义枚举类。枚举类是一种特殊的类,他一样可以有自己的属性和方法,可以实现一个或者多个接口,也可以定义自己的构造器,一个Java源文件最多只能有一个public权限的枚举类,且文件名和该枚举同名。枚举类和普通类的去区别:枚举类可以实现一个或多个接口,默认继承了java.lang.Enum(实现了Serializable和Comparable)而非Object,枚举类的构造器只能是由private修饰,默认是由private修饰;枚举类的所有实例必须在枚举类中显式列出,系统会自动添加public static final,所有的枚举类都提供了一个values方法,用于遍历。枚举类的实例只能是枚举值可通过public static <T extends Enum<T>>T valueOf(Class<T>enumType,String name)来返回指定枚举类中指定名称的枚举值。枚举类应该设计成不可变类,即它的属性都应该为private final。定义的时候如下MALE("男")等价于public static final Gender MALE = new Gender("男");有几个重要的实现接口和匿名内部类及抽象方法的方式,后面补全。

         ①JDK1,5提供自动装箱和拆箱:可以把Java的基本类型的值包装成为它提供的包装类的对象使用,即:允许把基本类型值直接.赋值给对应的包装类的引用变量,反之亦然。包装类(基本数据类型的引用类型)的作用时为了解决基本数据类型不能被当成Object类型变量使用的问题。

         ②Final关键字的作用:修饰类、方法、变量表示它修饰之后初始化后的变量不可再改变,即系统不允许为final变量重新赋值,子类不允许覆盖父类final方法。final类不能派生子类,通过final,实现不可变类,系统更安全。类的final Field必须在类的静态初始化块或声明Field时指定初始值。实例final Field必须在非静态初始化块、声明该Field或构造器中指定初始值。系统不会为非final成员隐式默认初始化为0、“\u0000”、false、null,这也是为什么Filnal Field要初始化的原因,否则将一直是这些无意义的值。由于系统在定义时并不为普通的局部变量赋初值,因此final局部变量可以暂不初始化,但一经初始化,不允许重新赋值。由于形参final变量在传参时已初始化,则在方法中将不能再被重新赋值。当Final修饰的非上面的基本类型而是引用类型时,只要保证这个引用类型的变量指向的地址不变、即引用的是同一对象,内存中保存的值可以改变,即对象的属性可改变。当满足变量用final修饰、且定义时即被指定初始值、且在编译时就可确定的变量即为“宏替换”。final定义的方法不能被覆盖,(注意覆盖指的是子类重写父类的public方法)例如Object类的getClass方法即为final,final定义的方法可以被重载。final修饰的类不可以有子类,java.lang.Math即是。不可变(immutable)类指的是创建该类后,类的Field不可变,Java8提供了8个包装类和java.lang.String都是不可变类,他们创建的实例,Field不可变。例如String str = new String("Hello")

         ③abstract和interface分别用于抽象类和接口,均是从子类中抽象出共同特征。但抽象类主要作为多个类的模板,而接口定义的是多个类遵循的规范。

         ④enum关键字用于创建枚举类,枚举类是一种不能自由创建对象的类,枚举类的对象在定义类时已经固定下来。(java.lang,NumberFormatException)

         ⑤接口和抽象类共同点:都不能被实例化,都位于继承树的顶端,用于被其他类实现和继承,都可以包含抽象方,实现接口和抽象类的子类必须实现这些抽象方法。不同点:1接口里只能包含抽象方法,不包含已提供实现的方法,抽象类则可以包含普通方法。2接口不能定义静态方法,抽象类可以。3接口里只能定义静态常量,不能定义普通Field,抽象类则可以。4接口里不能包含构造器,抽象类则可以,但并不是用来构造对象而是给子类调用实现抽象类自身的初始化。5接口里不能包含初始化块,抽象类则可以。6一个类最多只能有一个继承类包括抽象类,但却可以实现多个接口。

        ⑥访问控制权限:外部类的上一级程序单元是包,所以它只有两个作用域:同一个包内和任何位置。因此只需要两个访问控制权限:包访问控制权限(可被同一个包中的其他类访问)和公开访问权限,而内部类有四个作用域:同一个类,同一个包,父子类,任何位置,因此它有四个访问权限。

        ⑦继承内部类子类的内部类构造:讲解清楚略复杂,后续用图示补全。

        ⑧内部类时外部类的成员,那么外部类的子类可否重写内部类呢,因为他们所属的命名空间即外部类名空间不同,所以即使定义两个一样的内部类名,也不可能被重写。

1 0