Java基础知识(四)——面向对象(下)

Java基础知识(四)——面向对象(下)

Java8的增强包装类:

为了解决8种基本数据类型不能当成Object类型变量使用的问题。

 

JDK1.5提供了自动装箱(Autoboxing)和自动拆箱·(AotuUnboxing)功能,所谓自动装箱,就是可以把一个基本类型变量直接赋给对应的包装类变量,或者Object变量(子类对象可以直接赋给父类变量)。借助包装类,开发者可以把基本类型的变量“近似”地当成对象来使用(所以装箱、拆箱系统会自动完成)。

此外,包装类还可以实现基本类型变量和字符串之间的转换。把字符串的值转换为基本类型的值有两种方式:

利用包装类提供的parseXxx(String s)静态方法

利用包装类提供的Xxx(String s)构造器

String类提供了多个重载的valueOf()方法,用于将基本类型变量转换成字符串。(如果希望把基本类型变量转换成字符串,可以将其和””连接)

 

虽然包装类型的变量是引用数据类型,但包装类的实例可以与数值类型的值进行比较,这种比较是直接取出包装类实例所包装的数值来进行比较。

两个包装类的实例进行比较就会复杂,因为包装类的实例实际上是引用类型,只有两个包装类引用指向同一个对象时才会返回。

 

系统把一个-128~127之间的同一个整数自动装箱成·Integer实例时,并放入了一个名为cache的数组中缓存起来。如果以后把一个-128~127之间的整数自动装箱成一个Integer实例时,实际上是直接指向对应的数组元素,因此

-128~127之间的同一个整数自动装箱成Integer实例时,永远都是引用cache数组的同一个数组元素,所以他们全部相等,但每次不在该范围内的整数自动装箱成Integer实例时,系统总是重新创建一个Integer实例。

 

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

而且,所有的Java对象都可以和字符串进行连接运算,当Java对象和字符串经学连接运算时,系统自动·调用Java对象toString()方法的返回值和字符串进行连接运算。

toString()方法:

返回该对象实现类的“类名+@+hashCode”值。

toString()方法通常是一个自我描述的方法,通常会(被重写)用于实现:自我描述。返回return 类名[field=值1,field=值2];

==和equals()方法:

当使用==判断两个变量是否相等时,如果两个变量都是基本类型,且都是数值类型,则只要两个变量的值相等,则返回true。

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

“hello”和new String(”hello”)区别:

“hello”:JVM将会使用常量池(constant pool)管理字符串。

new String(”hello”):会先使用常量池(constant pool)管理字符串,在调用String类构造器创建一个新String对象,该对象保存在堆内存中。

常量池,专门用于管理在编译时被确定并被保存在已编译的.class文件中的数据。

equals()方法是Object类提供的一个实例方法,因此所有的引用变量都可以调用该方法来判断是否与其他引用变量相等。直接使用与==没有区别,通常(被重写)用于自定义判断是否相等。

类成员:

Java类里只能包含成员变量、方法、构造器、初始化块、内部类(包括接口、枚举)5种成员。其中static不能修饰构造器,被static修饰的成员就是类成员。类成员属于整个类,而不是单个对象。

类成员不能访问实例成员:类成员的作用域比实例成员的作用域更大,完全可能出现类成员已经初始化完成,实例成员还未初始化。

单例类:

如果一个类只能创建一个实例,则这个类被称为单例类。

在一些特殊场景下,要求不允许自由创建该类的对象,而只允许为该类创建一个对象。为了避免其他类自由创建该类的实例,应该把该类的构造器使用private修饰,从而把该类的所有构造器隐藏起来。

根据良好封装的原则:一旦把该类的构造器隐藏起来,就需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰

(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。

除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象,也就无法保证只创建一个对象。为此该类需要使用一个成员变量来保存曾经创建的对象,因为该成员变量需要被上面的静态方法访问,固该成员变量必须使用static修饰。

final修饰符:

final关键字用于表示他修饰的类、方法和变量(类变量、实例变量、局部变量、形参)不可改变。

 

final成员变量:

final成员变量随类初始化或对象初始化而初始化。

final修饰的成员变量一旦有了初始值(由程序显式的指定初始值),就不能被重新赋值。

类变量:必须在静态初始化块声明该实例变量时其中之一指定初始值。

实例变量:必须在非静态初始化块、声明该实例变量构造器其中之一指定初始值。

 

final局部变量:

final局部变量既可以在定义时指定(系统不会对其初始化,由程序显式初始化,)默认值,也可以不指定默认值(后面代码中只能赋值一次)。

 

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

基本类型变量不能重新赋值,但对于引用类型变量而言,他只是一个引用,final只保证这个引用类型变量所引的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。

 

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

对于一个final变量,只要满足三个条件,他就不再是一个变量,而是一个直接量(宏变量):

  1. 使用final修饰
  2. 定义final变量时指定了初始值
  3. 该初始值在编译时就被确定下来

final方法:

被修饰方法不可被重写。

Object类有一个final方法:getClass()。

使用final可以修饰一个private访问权限的方法,因为private方法仅在当类中可见,子类无法重写,如果子类有一个与父类private修饰的方法有相同方法名、相同形参列表、相同返回值类型的方法,实际上是子类定义了一个全新的方法。

final类:

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

 

不可变(immutable)类:

创建该类的实例后,该实力的实例变量是不可改变。Java的八个包装类和java.lang.String类都是不可变类。

自定义不可变类规则:

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

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

3.仅为该类的成员变量提供getter方法,不要为该类的成员变量提供setter方法,因为普通方法无法修改final修饰的成员变量。

4.如果有必要,重写Object类的hashCode()和equals()方法。equals()方法根据关键成员变量来作为两个对象是否相等的标准,此外,还应该保证两个用equals()方法判断为相等的对象的hashCode()也相等。

 

缓存实例的不可变类:

不可变类的实例状态不可改变,可以很方便的被多个对象所共享。如果程序经常使用相同的不可变实例,则应该考虑缓存这种不可变类实例。

 

 

抽象类:

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

 

抽象方法和抽象类:

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

  1. 抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体
  2. 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创造实例、
  3. 抽象类可以包含成员变量、方法、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要适用于被其子类调用。
  4. 含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。

抽象类相比普通类:可以包含抽象方法,但不能用于创建实例。

抽象类只需在普通类上增加abstract修饰符即可。甚至普通类(不含抽象方法的类)增加abstract修饰符后会变成抽象类。

抽象方法和空方法不是同一概念:

public abstract void test();

public void test(){}

当使用abstract修饰类时,表明该类只能被继承;当使用abstract修饰方法时,表明该方法必须由子类提供实现(即重写)。而final修饰类不能被继承,final修饰的方法不能被重写,因此final和abstract不能一起使用。

static和abstract虽然不能同时修饰某个方法,但可以同时修饰内部类。

private和abstract不能同时使用,因为abstract必须被重写才有意义。

抽象类的作用:

抽象类不能创建实例,只能当成父类来被继承。抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。

 

 

Java8改进的接口:

抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口(interface)。

接口里不能包含普通方法,接口里所有方法都是抽象方法。Java8对接口进行了改进,允许在接口中定义默认方法,默认方法可以提供方法实现。

接口定义的是多个类的公共行为规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用方法。

 

[修饰符] interface 接口名 extends 父接口1, 父接口2{

}

修饰符:可以使public或省略,默认权限即只有在相同包结构下才可以被访问。

由于接口定义的是一种规范,因此接口里不能包含构造器和初始块定义。

接口里的成员变量只能是静态变量,方法只能是抽象方法,类方法和默认方法。

接口里的所有成员可以省略访问修饰符,如果指定访问修饰符,则只能使用public访问控制修饰符。

接口里定义的静态常量不管是否使用public static final修饰符,系统会自动给其修饰,而且静态变量只能在定义时赋值。

接口里定义的方法,如果不是默认方法,系统将自动为普通方法增加abstract修饰符;定义普通方法时不管是否使用public abstract修饰,系统自动为其修饰。普通方法不能有实现(方法体);但类方法、默认方法都必须有方法实现。

接口里定义的内部类、内部接口、内部枚举不管是否有public static修饰,系统都自动为其修饰。

接口中允许定义默认方法(必须用default修饰),该方法不能用static修饰,默认方法如果没有被public修饰,系统会自动为其修饰。由于默认方法没有被static修饰,因此不能直接使用接口来调用默认方法,需要使用接口的实现类的实例来调用。

 

接口的继承:

接口完全支持多继承,即一个接口可以有多个直接父接口。

 

使用接口:

接口不能用于创建实例,但接口可以用于声明引用变量类型。当使用来声明引用变量时,这个引用变量必须引用到其实现类的对象。除此之外,接口的主要用途就是被实现类实现。归纳起来,用途如下:

1.定义变量,也可用于进行强制类型转换。

2.调用接口中定义的常量。

3.被其他类实现。

一个类可以实现一个或多个接口,继承使用extends关键字,实现则使用implements关键字。

[修饰符] class 类名 extends 父类 implements 接口1, 接口2…{

}

一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,则该类必须被定义成抽象类。

可以把实现接口理解成实现一种特殊的继承,相当于实现类继承了一个彻底抽象的类(除默认方法外,所有方法都是抽象方法的类)。

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

 

接口不能显式继承任何类,但所有接口类型的引用变量都可以直接赋给Object类型的引用变量。

 

接口和抽象类:

相同点:

  1. 1.   都不能被实例化,他们都位于继承树顶端,用于被其他类实现和继承。
  2. 2.   都可以包含抽象方法,实现接口类或继承抽象类的子类都必须全部实现这些抽象方法。

不同点:

  1. 1.   接口只能包含抽象方法和默认方法,不能为普通方法提供方法实现;抽象类

则完全可以包含普通方法。

  1. 2.   接口里不能定义静态方法;抽象类里可以定义静态方法。
  2. 3.   接口里只能定义静态常量,不能定义普通成员变量;抽象类里既能定义静态常量,也能定义普通成员变量。
  3. 4.   接口里不包含构造器;抽象类可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造起来完成属于抽象类的初始化操作。
  4. 5.   接口里不能包含初始化块;抽象类可以包含初始化块。
  5. 6.   一个类最多只有一个父类,包括抽象类;但是一个类可以有多个父接口。

 

 

面向接口编程:

简单工厂模式,命令模式。

 

 

 

内部类:

大部分时候,类被定义为一个独立的程序单元在某些情况下,也会把一个类放在

另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(嵌套类),包含内部类的类也叫外部类(宿主类)。

1.内部类提供更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。假设需要创建Cow类,Cow类需要组合一个CowLeg对象,CowLeg类只有在Cow类才有效,离开Ciw类之后没有任何意义。在这种情况下就可把CowLeg定义成Cow的内部类,不允许其他类访问CowLeg。

2.内部类成员可以直接访问外部类的私有数据,因为内部类被当成其他外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的细节,如内部类的成员变量。

3.匿名内部类适合用于创建那些仅需要一次使用的类。

从语法角度来看定义内部类和定义外部类语法大致相同,内部类除了需要定义在其他类里面之外还需要:

  1. 内部类比外部类可以多使用三个修饰符:private、protected、static。
  2. 非静态内部内不能拥有静态成员。

 

大部分时候,内部类都被作为成员内部类定义,而不是局部内部类。成员内部类是一种与成员变量、方法、构造器、初始化块相似的类成员;局部内部类和匿名内部类不是类成员。

外部类的上一级程序单元是包,所以他只有两个作用域:同一个包内和任何位置,因此只需要两种访问权限:包访问权限和公开访问权限,正好对应省略访问控制符和public访问控制符。省略访问控制符是包访问权限,即同一包中的其他类可以访问省略访问控制符的成员。因此,如果一个外部类不使用任何访问控制修饰符,则只能被同一个包中其他类访问。而内部类的上一级程序单元是外部类,他就具有4个作用域:同一个类、同一个包、父子类和任何位置,因此可以使用4种访问控制权限。

成员内部内分为两种,静态内部类和非静态内部类:

猜你喜欢

转载自www.cnblogs.com/Azog/p/10217245.html