九.多态的概念
同一个方法名称,执行不同的操作。方法重载就是一种多态的一种形式。
9.2 方法重写
当子类从父类中继承来的方法不能满足需要时,子类可以重写该方法,重写方法要求方法名与参数列表都相同。
因此如果子类中的方法与父类中的方法同名、并且参数类型也相同,那么子类中的方法就重写了父类中的同名方法。
为什么不直接定义另外一个方法?因为重写方法可以实现运行时多态的效果。
9.2.1 方法重写的规则
两同:方法名相同、参数列表相同
两小:返回值类型更小(子类)或相等、抛出的异常类更小或相等
一大:访问权限更大或相等
9.2.2 方法重写与方法重载的区别
只有当两个方法的名称和类型签名都相同时才会发生重写。如果不是都相同,那么这两个方法就只是简单的重载关系。
9.2 动态方法调度与运行时多态
9.2.1 动态方法调度
当通过父类引用调用重写方法时,在运行时会调用子类中的重写版本。
动态方法调用要以方法重写为前提。
9.2.2 运行时多态
运行时多态的实现机理:动态方法调度
总结:方法重写是前提、动态调度是手段、多态是最终的目的
运行时多态的优点:灵活
运行时多态的两个要素:
(1)在子类中重写超类中的方法
(2)使用超类引用调用重写方法。
在自己的类中定义的toString()方法就是重写方法。
注意不要混淆:
使用超类引用调用成员变量时,调用的是超类的成员变量。
9.2.3 多态的两种形式
运行时多态:动态方法调度实现
编译时多态:重载方法,编译时通过方法匹配实现的
十.抽象方法与抽象类
10.1.1 抽象方法
抽象方法是指在抽象类或接口中声明的方法,但没有具体的实现。它只有方法名、参数列表和返回类型的定义,没有方法体。使用abstract修饰的方法
注意:
-
抽象方法没有方法实现,即没有方法体{},只有定义。类中如果有抽象方法,该类必须是抽象类,必须使用abstract
-
对于抽象方法,abstract不能与private、static同时使用。
-
抽象方法实现多态效果的前提:抽象方法的一个主要目的是实现多态的效果。为了实现多态,需要子类重写父类中的抽象方法,并通过父类引用调用子类重写后的方法,根据实际指向的对象调用相应的重写方法。
-
类需要重写父类的抽象方法,并通过父类引用调用子类的重写方法来实现多态性。
10.1.2抽象类
类定义中使用abstract修饰的类为抽象类。可以有构造方法
注意
-
有抽从语法上讲,抽象类中可以没有抽象方法,但是没有实际意义
-
抽象方法的类必须是抽象类
-
不能创建抽象类的对象,即不能new对象
-
抽象类可以当做一种引用类型来使用,声明变量
-
继承自抽象类的类,必需重写抽象类中的所有抽象方法,否则自身也使用abstract修饰,即也是抽象类。
抽象类的子类,会继承抽象类中的所有抽象方法,子类要么重写所有的抽象方法。如果有一个抽象方法的没有重写的话,子类中也有抽象方法。
说明:
抽象类只定义被其所有子类共享的一般形式,而让每个子类填充其细节。这种类确定了子类必需实现的方法。
注意:
有抽象方法的一定是抽象类。
10.1 接口的概念与定义
接口可以理解为抽象到不能再抽象的类,。可以认为类是一套体系,接口是另外一套体系,只不过类可以实现接口。但是不要将接口和类混为一谈
接口中的方法全部都是抽象方法,不能存在实现的方法。
接口使用interface关键字定义,接口的定义和类很相似。
10.2 接口中的属性和方法
(1)接口中所有方法默认是公有的抽象方法。
隐式地标识为public、abstract,并且接口中的方法也只允许使用这两个修饰符。
注意,在抽象类中必需使用abstract关键字明确指定方法为抽象方法。
(2)在接口中所有变量默认为公有的静态常量。
被隐式地标识为public、static、final。这意味着实现接口的类不能修改它们。同时还必须初始化它们。
10.3 接口的实现
一旦定义了一个接口,一个或多个类就可以实现该接口。为了实现接口,在类定义中需要包含implements子句,然后创建接口定义的方法。
注意:
(1)实现接口的类,必须实现接口的所有抽象方法,如果只实现了部分抽象方法,该类必须声明为抽象类。
(2)一个类可以实现多个接口,实现的多个接口用“,”隔开
(3)实现接口的类可以同时继承一个超类,必须是先继承后实现。
说明:
接口定义了一组抽象方法,实现该接口的类需要实现这些抽象方法,从而实现接口的类就具备了接口所规定的行为(功能)。
在Java中,接口可理解为对象间相互通信的协议,相当于模板。
10.4接口继承
接口可以通过关键字extends继承另一个接口,其语法和类继承相同。如果类实现的接口继承自另外一个接口,则该类必需实现在接口继承链中定义的所有方法。
*10.5 抽象类和接口的区别*
补充:
抽象类有构造方法,接口没有构造方法
类只能单继承,接口可以多重继承接口
抽象类中可以没有抽象方法,但是有抽象方法的类必须是抽象类。
####
1. 抽象类(Abstract Class)
抽象类是一个包含抽象方法的类,它不能被实例化。抽象方法是一种没有方法体的方法,它只包含方法的签名。抽象类可以包含普通的方法,也可以包含抽象方法,而普通方法可以有方法体。
1.1 抽象类的定义
abstract class Shape { // 抽象方法 abstract void draw(); // 普通方法 void display() { System.out.println("Displaying shape"); } } 123456789
1.2 抽象类的特点
-
可以包含抽象方法和普通方法。
-
不能被实例化,只能被继承。
-
子类必须实现抽象类中的所有抽象方法,除非子类也是抽象类。
2. 接口(Interface)
接口是一种抽象类型,它定义了一组方法的签名,但没有提供方法的实现。在Java中,类可以实现一个或多个接口。接口中的方法默认是public
、abstract
的,可以省略这些修饰符。
2.1 接口的定义
interface Drawable { // 抽象方法 void draw(); // 默认方法 default void display() { System.out.println("Displaying drawable"); } } 123456789
2.2 接口的特点
-
定义了一组抽象方法的签名。
-
可以包含默认方法,这是在Java 8中引入的特性,允许在接口中提供方法的默认实现。
-
类可以实现多个接口,实现了接口的类必须实现接口中定义的所有方法。
3. 区别与选择
在使用抽象类和接口时,我们需要考虑它们的特点和适用场景。
3.1 抽象类的适用场景
-
当需要在多个类之间共享代码或状态时,可以使用抽象类。
-
当类的一部分实现是通用的,而另一部分是特定于每个子类的时候,可以使用抽象类。
-
抽象类可以包含成员变量,而接口只能包含常量。
3.2 接口的适用场景
-
当多个类需要实现相同的方法签名但可能包含不同的实现时,可以使用接口。
-
当类已经继承了其他类,但仍需要实现一组方法时,可以使用接口。Java中支持多继承,一个类可以继承一个类同时实现多个接口。
-
当需要定义一组常量时,可以使用接口,因为接口中的字段默认是
public
、static
、final
的。
4. 差异比较
4.1. 继承与实现
-
抽象类:
-
使用
extends
关键字实现继承。 -
一个类只能继承一个抽象类。
-
-
接口:
-
使用
implements
关键字实现接口。 -
一个类可以实现多个接口。
-
4.2. 构造器
-
抽象类:
-
可以有构造器。
-
构造器在子类对象创建时被调用。
-
-
接口:
-
不可以有构造器。
-
没有构造器的概念,接口中定义的字段默认是
public
、static
、final
的。
-
4.3. 字段
-
抽象类:
-
可以包含实例变量。
-
可以包含静态变量。
-
-
接口:
-
只能包含常量(
public
、static
、final
)。
-
4.4. 方法
-
抽象类:
-
可以包含抽象方法。
-
可以包含普通方法。
-
可以包含静态方法。
-
-
接口:
-
只能包含抽象方法(Java 8 之前)。
-
可以包含默认方法(Java 8+)。
-
可以包含静态方法。
-
4.5. 访问修饰符
-
抽象类:
-
可以有访问修饰符,可以是
public
、protected
、default
(包内可见)。
-
-
接口:
-
所有方法默认为
public
,字段默认为public
、static
、final
。 -
方法可以有
public
、default
(包内可见)两种修饰符。
-
5. 最佳选择
在选择抽象类或接口时,应考虑以下几个因素:
-
设计目标:
-
如果设计的是一种类型,希望它有通用的代码和状态,使用抽象类更为合适。
-
如果设计的是一种行为,希望实现类拥有相同的方法签名,使用接口更为合适。
-
-
代码复用:
-
如果多个类之间有共享的代码和状态,使用抽象类更方便。
-
如果多个类之间只有方法签名相同而实现不同的情况,使用接口更适合。
-
-
多继承:
-
如果一个类已经继承了其他类,但需要实现一组方法,可以使用接口。
-
Java中支持多继承,一个类可以继承一个类同时实现多个接口。
-
7. 使用场景
7.1 抽象类的使用场景
-
代码复用性高: 如果多个类之间有很多共同的代码和状态,使用抽象类可以更方便地实现代码复用。
-
共享设计: 当设计的是一种类型,并希望它有通用的代码和状态时,抽象类是一个更自然的选择。
-
适用于层次结构: 抽象类适用于类之间有明显的层次结构的情况,可以定义一些通用的行为。
abstract class Animal { abstract void makeSound(); } class Dog extends Animal { void makeSound() { System.out.println("Bark"); } } class Cat extends Animal { void makeSound() { System.out.println("Meow"); } } 123456789101112131415
7.2 接口的使用场景
-
多继承需求: 如果一个类已经继承了其他类,但仍需要实现一组方法,使用接口更为合适。
-
行为定义: 当设计的是一种行为,并希望实现类拥有相同的方法签名时,使用接口是一种有效的方式。
-
适用于非层次结构: 接口适用于类之间没有明显层次结构的情况,可以定义一些独立的行为。
interface Shape { void draw(); } class Circle implements Shape { void draw() { System.out.println("Drawing Circle"); } } class Rectangle implements Shape { void draw() { System.out.println("Drawing Rectangle"); } } 123456789101112131415
8. 最佳实践
8.1 接口与抽象类的结合使用
在实际开发中,接口与抽象类可以结合使用,以发挥它们各自的优势。一个类可以继承一个抽象类同时实现多个接口,这样可以同时享受到抽象类的代码复用和接口的多继承特性。
abstract class Shape { abstract void draw(); } interface Colorable { void setColor(String color); } class ColoredCircle extends Shape implements Colorable { private String color; void draw() { System.out.println("Drawing Colored Circle"); } public void setColor(String color) { this.color = color; } } 12345678910111213141516171819
8.2 Java 8 中的默认方法
在Java 8中引入了接口的默认方法,这使得接口可以包含非抽象的方法实现。这样的默认方法可以为现有的接口添加新的功能,而不会影响实现该接口的类。
10.6 包装类
Java为8种基本数据类型分别提供了对应的包装类。本质上这些类将基本类型包装到一个类中,因此通常将它们称为类型包装器。
Byte Short Integer Long Float Double Character Boolean
10.6.1Integer与int的区别
int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。在JSP开发中,Integer的默认为null,所以用el表达式在文本框中显示时,值为空白字符串,而int默认的默认值为0,所以用el表达式在文本框中显示时,结果为0,所以,int不适合作为web层的表单数据的类型。
在Hibernate中,如果将OID定义为Integer类型,那么Hibernate就可以根据其值是否为null而判断一个对象是否是临时的,如果将OID定义为了int类型,还需要在hbm映射文件中设置其unsaved-value属性为0。
另外,Integer提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。
10.6.1 Character包装器
Character是char类型的包装器。Character的构造函数为:
Character(char ch)
中,ch指定了将由即将创建的Character对象包装的字符。
为了获取Character对象中的char数值,可以调用charValue(),如下所示:
char charValue( )
该方法返回封装的字符。
10.6.2 Boolean包装器
Boolean是包装boolean值的包装器
它定义了以下构造函数:
Boolean(boolean boolValue)
Boolean(String boolString)
在第一个版本中,boolValue必须是true或false。在第二个版本中,如果boolString包含字符串“true”(大写或小写形式都可以),则新的Boolean对象将为真,否则,将为假。
为了从Boolean对象获取boolean值,可以使用booleanValue()
10.6.3 数值类型的包装器类
1、构造器
所有数值类型包装器都定义了用于从给定数值或数值的字符串表示形式构造对象的构造函数,例如,下面是为Integer定义的构造器:
Integer(int num)
Integer(String str)//a123 “123”
如果str没有包含有效的数字值,则会抛出NumberFormatException异常。
2、从包装器对象中提取数值
最常用类型的包装器是那些表示数值的包装器。包括Byte、Short、Integer、Long、Float以及Double。所有这些数值类型包装器都继承自抽象类Number。Number声明了以不同数字格式从对象返回数值的方法,如下所示:
byte byteValue( )
double doubleValue( )
float floatValue( )
int intValue( )
long longValue( )
short shortValue( )
3、将包装器对象转换成字符串
类型包装器都重写了toString()方法,该方法可以将数值转换成字符串形式。
String str = Integer.toString(100);
10.7.1 自动装箱与自动拆箱
自动装箱是这样一个过程,只要需要基本类型的对象,就自动将基本类型自动封装(装箱)进与之等价的类型包装器中,而不需要明确地构造对象。自动拆箱是当需要时自动抽取(拆箱)已装箱对象数值的过程。不需要调用intValue()或doubleValue()这类方法。
自动装箱和自动拆箱特性极大地简化了一些算法的编码,移除了单调乏味的手动装箱和拆箱数值操作。它们还有助于防止错误。此外,它们对于泛型非常重要,因为泛型只能操作对象。最后,集合框架需要利用自动装箱特性进行工作。
案例:自动装箱与自动拆箱测试
10.7.2 数值与字符串形式之间的转换
最常见的编程杂务之一是将数值的字符串表示形式转换成数值。数值类型的包装器类为此提供了相应的方法。例如:
l Int类的parseInt()方法
l Long类的parseLong()方法
l Double类的parseDouble()方法
十一. 字符类型
11.1.1 字符分类
Character类提供一些静态方法用于判断字符属于哪一类。
static boolean isDigit(char ch) | 如果ch是数字,则返回true。 |
---|---|
static boolean isLetter(char ch) | 如果ch为字母,则返回true。 |
static boolean isLetterOrDigit(char eh) | 如果ch为字母或数字,则返回true。 |
static boolean isLowerCase(char ch) | 如果ch为小写字母,则返回true; |
static boolean isUpperCase(char ch) | 如果ch为大写字母,则返回true。 |
static boolean isSpaceChar(char ch) | 如果ch为空格字符,则返回true。 |
static boolean isWhitespace(char ch) | 如果ch为空白字符,则返回true。 |
11.1.2 包装器类中其他常用的常量和方法
Integer.MAX_VALUE//*表示int数据类型的最大取值数:2 147 483 647*
Integer.MIN_VALUE//*表示int数据类型的最小取值数:-2 147 483 648*
Integer.SIZE //长度,多少bit
Integer.valueOf(100); //根据整数创建Integer对象
Integer. valueOf("100"); //根据字符串创建Integer对象