第6章 面向对象(下)

    Java提供了final关键字类修饰变量、方法和类,系统不允许为final变量重新赋值,子类不允许覆盖父类的final方法,final类不能派生子类。通过使用final关键字,允许Java实现不可变类,不可变类会让系统更加安全。abstract和interface两个关键字分别用于定义抽象类和接口,抽象类和接口都是从多个子类抽象出来的共同特征。但抽象类主要作为多个类的模板,而接口则定义了多类应该遵守的规范。enum关键字用于创建枚举类,枚举类第一种不能自由创建对象的类,枚举类的对象在定义类时已经固定下来。

1:Java8增强的包装类

        JDK1.5提供了自动装箱和自动拆箱功能。所谓自动装箱,就是可以把一个基本类型变量直接赋给对应的包装类变量,或者赋给Object变量;自动拆箱则与之相反,允许直接把包装类对象直接赋给一个对应的基本类型变量。

2:处理对象

    1:toString方法

        toString()方法是Object类里的一个实例方法,所有的Java类都是Object类的子类,因此所有的Java对象都具有toString()方法。

    2:==和equals方法

        Java程序中测试两个变量是否相等有两种方式:一种是利用==运算符,另一种是利用equals()方法。当使用==来判断两个引用变量是否相等时,如果两个变量时基本类型变量,且都是数值类型,则只要两个变量值相等,就将返回true。

        但对于两个引用类型变量,只有它们指向同一个对象是,==判断才会返回true。==不可用与比较类型上没有父子关系的两个对象。

        equals()方法是Object类提供的一个实例方法,因此所有引用变量都可调用该方法来判断是否与其他引用变量相等。但使用这个方法判断两个对象相等的标准与使用==运算符没有区别,同样要求两个引用变量执行变量指向同一个对象才会返回true。

3:类成员

       stati关键字修饰的成员就是类成员,static修饰的类成员属于整个类,不属于单个实例。

    1: 理解类成员

        类变量属于整个类,当系统第一次准备使用该类的时,系统会为该类变量分配内存空间,类变量开始生效,直到该类被卸载,该类的类变量所占有的内存才被系统的垃圾回收机制回收。类变量生存范围几乎等同该类的生存范围。当类初始化完成后,类变量也被初始化完成。

        类方法也是类成员的一种,类方法也是属于类的,通常直接使用类作为调用者来调用类调用方法,但也可以使用对象来调用类方法。

        静态初始化块也是类成员的一种,静态初始化块用于执行类的初始化操作,在类的初始化姐夫安,系统对调用该类的静态初始化块来对类进行初始化。一旦该初始化结束后,静态初始化块将永远不会获得执行机会。

        对static关键字而言,有一条非常重要的规则:类成员不能访问实例成员。因为类成员是属于类的,类成员的作用域比实例成员作用域更大,完全可能出现类成员已经初始化完成,但实例成员还不曾初始化完成的情况,如果允许可成员访问实例成员将会引起大量错误。

    2:单例类

        大部分时候都把类的构造器定义成public访问权限,允许任何类自由创建该类的对象。但在某些时候,允许其他类自由创建该类的对象没有任何意义,还可能再生系统性能下降。如果一个类始终创建一个实例,则这个类被称为单例类。

        在类中用private修饰符把构造器隐藏起来,提供一个public的静态方法用于创建并返回这个类的实例,这样只需要访问这个类的静态方法就能获取一个这个类的实例,而且不管访问几次方法,返回的这个实例都是同一个实例。

4:final修饰符

    final关键字可用于修饰类、变量和方法,用于表示它修饰的类、方法和变量不可改变。final修饰变量时,表示该变量一旦获得了初始值就不可能被改变,final即可以修饰成员变量,也可以修饰局部变量、形参。

    1:final成员变量

        Java语法规定:final修饰的成员变量必须由程序员显式地指定初始化值。

        final修饰的类变量、实例变量能指定初始值的地方如下:

        ①类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。

        ②实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始化值而且只能在三个地方的其中之一指定。  

        final修饰实例变量,要么在定义该实例变量时指定初始值,要么在普通初始化块或构造器中为该实例变量指定初始值。final修饰的类变量,要么在定义该类变量时指定初始值,要么在静态初始化块中为该类变量指定初始值。

    2:final局部变量

        系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。因此使用final修饰局部变量时,既可以在定义时指定默认值,也可以不指定默认值。如果final修饰的局部变量在定义时没有指定默认值,则可以在后面代码中对该final变量符初始化值,但只能一次。不能重复;如果final修饰局部变量在定义时已经指定默认值,则后面代码中不能再对该变量赋值。

        final在修饰形参,形参在调用该方法时已经根据传入的参数来完成初始化,所以形参不能再被赋值。

    3:final修饰基本类型变量和引用类型的区别

        当使用final修饰基本类型变量时,不能对基本类型重新赋值,因此基本类型变量不能被改变。但对于引用类型变量而言,它保持的仅仅是一个引用,final只保持这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。

        使用final修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容。

    4:可执行“宏替换”的final变量

        对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要改变量满足三个条件,这个final变量就不再是一个变量,而是相当于一个直接量:①使用final修饰符修饰;②在定义该final变量时指定了初始值;③该初始值可以在编译时就被确定下来。

        final修饰符的一个重要用途就是定义了一个final局部变量,并在定义该final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。

    5:final方法

        final修饰的方法不可被重写,如果不希望子类重写该方法,则可以使用final来修饰该方法。对于有一个private方法有,因为它尽在当前类中可见,其子类无法访问该方法,所以子类无法重写该类方法,如果子类定义一个与父类private方法有相同方法名、相同参数列表、相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。因此,即使使用final修饰一个private访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同参数列表、相同返回值类型的方法。

    6:final类

        final修饰的类不可以有子类。

    7:不可变类

        不可变类意思是创建该类的实例后,该实例的实例变量不可改变的。

        如果需要创建自定义的不可变类,可遵循如下规则:

        ①使用private和final修饰符类修饰该类的成员变量。

        ②提供带参数构造器,用于根据传入参数来初始化类里的成员变量。

        ③仅为该类的成员变量提供getting方法,不要为该类的成员变量提供setting方法,因为普通方法无法修改final修饰的成员变量。

        ④如果有必要,重写Object类的HashCode()和equals()方法。equals()  方法根据关键成员变量类作为 两个对象是否相等的标准。

5:抽象类

    抽象方法是只有方法签名,没有方法实现的方法。

    1:抽象方法和抽象类

        抽象方法和抽象类必须使用abstract修饰符类定义,有抽象方法的类只能被定义为抽象类,抽象类里可以没有抽象方法。

        抽象方法和抽象类的规则如下:①抽象类必须使用abstract修饰符来修饰,抽象方法也必须由abstract修饰符来修饰,抽象方法不能有方法体;②抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例,即使抽象类里不包含抽象方法,这个抽象类也不能创建实例;③抽象类可以包含成员变量、方法、构造器、初始化块、内部类。抽象类的构造器不能用于创建实例,主要是用于被子类调用;④含有抽象方法的类只能被定义成抽象类。

        定义抽象方法只需在普通方法上增加abstract修饰符,并把普通方法的方法体全部去掉,并在方法后面增加分号即可。定义抽象类只需在普通类上增加abstract修饰符即可,甚至一个普通类增加abstract修饰符后也变成抽象类。    

        利用抽象类和抽象方法的优势,可以更好的发挥堕胎的优势,是得程序更加灵活。

        当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类提供实现。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用。

        abstract不能用于修饰成员变量,不能用于修饰局部变量,即没有抽象变量、没有抽象成员变量等说法;abstract也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器。

        当使用static修饰一个方法时,表明这个方法属于该类本身,即通过类就可调用该方法,但如果该方法被定义成抽象方法,则将导致通过该类调用该方法时出现错误,因为这个方法没有方法体。因此static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法。static和abstract虽然不能修饰某个方法,但它们可以同时修饰内部类。

        abstract关键字修饰的方法必须被其子类重写才有意义,否则这个方法将永远不会有方法体,因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。

    2:抽象类的作用

        抽象父类提供了多个子类的通用方法,并把一个或多个方法留给子类来实现,这就是一种模板模式,模板模式也是十分常见且简单的设计模式。

        模本模式可以只定义需要使用的某些方法,把不能实现的部分抽象成抽象方法,留给其子类去实现。

        父类中可能包含需要调用其他系列方法的方法,这些被调方法既可以有父类实现,也可以有其子类实现。父类里提供的方法这定义了一个通用算法,其实现也许并不完全由自身实现,而必须依赖于其子类的辅助。

6:Java8改进的接口

        抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口,接口里不能包含普通方法,接口里的所有的方法都是抽象方法。

    1:接口的概念

        接口是从多个相似类中抽象出来的概念,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学,接口定义的是多个类共同的公共行为规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公共方法。

    2:Java8中接口的定义

        接口定义使用关键字是interface,接口定义的基本语法是:

[修饰符] interface 接口名 extends 父接口1,父接口2.....
{
常量定义.....
方法定义.....
内部类、接口、枚举定义....
默认方法、类方法定义...
}

        说明:①修饰符可以是public或者省略,如果省略public访问修饰符。则默认采用包权限访问修饰符,即只有在相同包结构下才可以访问该接口;②接口名应与类名采用相同的命名规则,即如果仅从语法角度看,接口名只要是合法的标识符即可;如果要遵守Java可读性规范,则接口名应由多个有意义的单词连缀而成,每个单词首字母大写,单词与单词之间无需任何分隔符。③一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。

        由于接口定义是一种规范,因此接口里不能包含构造器和初始化块定义。接口里的成员只能是静态常量,接口里的方法只能是抽象方法、类方法或默认方法。接口里定义的是多个类共同的公共行为规范,因此接口里的所有成员,包括常量、方法、内部类和内部枚举都是public访问权限。对于接口里定义的静态常量而言,它们的接口相关的,因此系统会自动为这些成员变量增加static和final两个修饰符。

        接口里定义的干法只能是抽象方法、类方法或默认方法,因此如果不是定义默认方法,系统将自动为普通方法增加abstract;定义接口里的普通方法时不管是否使用public abstract修饰符,接口里的普通方法总是使用public abstract来修饰。接口里的普通方法不能有方法实现;但类方法、默认方法都必须由方法实现。

        Java8允许在接口中定义类方法,类方法必须使用static修饰,该方法不能使用default修饰,无论程序是否指定,类方法总是使用public修饰。

    3:接口的继承

        接口支持多继承,即一个接口可以有多个父接口。一个接口继承多个父接口时,多个父接口排在extends关键字之后,多个父接口之间以英文逗号(,)隔开。

    4:使用接口

        接口不能用于创建实例,但接口可以用于声明引用类型变量。接口的只要用途就是被实现类实现。归纳起来,接口主要有如下用途:①定义变量,也可用于进行强制类型转换;②调用接口中定义的常量;③被其他类实现。

        一个类可以实现一个或多个接口,继承使用extends关键字,实现则使用implements关键字。因为一个类可以实现多个接口,这也是Java为单继承灵活性不足所做的补充。类实现接口的语法格式如下:

    

[修饰符] class 类名 extends 父类 implements 接口1,接口2...
{
类体部分
}

        一个类可以继承一个父类,并同时实现多个接口,implements部分必须放在extends部分之后。一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法;否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。

        实现接口方法时,必须使用public访问修饰符,因为接口里的方法都是public的,而子类重写父类方法时访问权限只能相等或者更大,所以实现类实现接口里的方法时只能使用public访问权限。

    5:接口和抽象类

        接口和抽象类很像,它们都具有如下特征:①接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。②接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

        接口和抽象类在用法上存在如下差别:①接口里只能包含抽象方法、静态方法和默认方法,不能为普通方法提供方法实现;抽象类则完全包含普通方法。②接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。③接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。④接口里不能包含初始化块;但抽象类则完全可以包含初始化块。⑤一个类最多只能有一个直接父类,包括抽象类;单一个类可以实现多个接口,通过实现多个接口可以弥补Java单继承的不足。   

7:内部类

    大部分时候,类被定义成一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类,包含内部类的类也被称为外部类。内部类主要有如下作用:①内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类;②内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的实现细节;③匿名内部类适用于创建那些仅需要一次使用的类。

    从语法角度来看,定义内部类与定义外部类的语法大致相同,内部类除了需要定义在其他类里面之外,还存在如下两点区别:①内部类比外部类可以多使用三个修饰符:private、protected、static——外部类不可以使用这三个修饰符;②非静态内部类不能拥有静态成员。

猜你喜欢

转载自blog.csdn.net/Hot_snail/article/details/79748429