第4章 接口与多态
4 接口与多态
4.1 接口
- 接口:纯抽象类,只能有抽象方法;
接口中可以规定方法的原型,不规定方法主体;
可以包含基本数据类型的数据成员,都默认为static和final; - 接口作用:
继承多个设计;
建立类和类之间的“协议”(接口可以按照类的不同功能分组,让没有继承/组合关系的类实现统一对外接口);
接口可以隐藏实现的细节; - 声明格式:[接口修饰符] interface接口名称 [extends父接口名]
接口数据成员一定要有初值且不能再更改(可省略final);
接口中方法可省略public 和 abstract; - 实现接口:implements,必须实现接口中所有方法,来自接口的方法必须public;
可以同时实现多个接口; - 接口可以多继承,实现接口时必须实现超接口;
4.2 类型转换、方法的查找
- 类型转换规则:
基本类型之间转换:将值从一种类型转换成另一种类型;
引用变量类型转换:将引用转换为另一类型的引用,并不改变对象本身类型;
引用变量转型:转换为超类(向上转型)、所属类实现的接口(向上转型)、引用指向的对象的类型(唯一可以向下转型);
引用被转换为其超类引用后,只能访问超类中声明的方法; - 隐式类型转换(自动类型转换):
基本数据类型存储容量低自动向存储容量高转换;
引用变量被转成更一般的类(超类),被塑形为对象所属类实现接口; - 显示类型转换(强制转换):
基本数据类型(类型名)内容,可能丢失数据;
引用变量向下转型,只能转为引用指向的对象的类型; - 类型转换主要应用场合:赋值转换,方法调用转换,算数表达式转换,字符串转换;
- 实例方法的查找:从对象创建时的类开始,沿类层次向上查找;
类方法的查找:在引用变量声明时所属类中查找;
4.3 多态的概念、应用举例
- 多态:超类对象和从相同超类派生出的多个子类的对象,可被当作同一种类型对象对待;
实现同一接口不同类型的对象,可被当作同一种类型的对象对待;
可向这些不同类型的对象发送同样的消息,由于多态性,这些不同类对象响应同一消息时的行为可以有所差别; - 多态的目的:使代码简单且容易理解,程序可扩展性更好;
- 绑定:将一个方法调用表达式与方法体代码结合起来;
早绑定:程序运行前绑定;
晚绑定(动态绑定/运行期绑定):基于对象类别在程序运行时绑定; - 应用举例:二次分发;
4.4 构造方法和多态性
- 构造子类对象时构造方法的调用顺序:
首先调用超类的构造方法,最后执行当前子类构造方法;
未写明时会直接调用超类空参数的构造方法; - 实现构造方法的注意事项:
用尽可能少的动作把对象状态设置好;
尽量避免调用任何方法(构造方法中调用动态方法可能存在出现潜在问题,尽量避免);
构造方法内唯一能够安全调用的是超类中final方法(及private方法)。
部分习题
1.以下是接口I的定义:
interface I{
void setValue(int val);
int getValue();
}
以下哪段代码能够通过编译:
A. class A extends I{int value;public void setValue(int val){value=val;}public int getValue(){ return value;}}
B. class B implements I{int value; void setValue(int val){value=val;} public int getValue(){ return value;}}
C. interface C extends I {void add();} interface C extends I {void add();
D. interface D implements I{void add();}
答案:选择C;
选项A:The type I cannot be the superclass of A; a superclass must be a class;
选项B:Cannot reduce the visibility of the inherited method from I;(未加public)
选项D:implements未实现全部方法;
2.已知
import java.io.*;
class Person{
public static void print(){System.out.print("Person");}
}
class Employee extends Person{
public void print(){
System.out.print("Employee");}
}
class Manager extends Employee{
public void print(){
System.out.print("Manager");}
}
public class Test{
public static void main(String[] args){
Manager man = new Manager();
Employee emp1 = new Employee();
Employee emp2 = (Employee)man;
Person person = (Person)man;
emp2.print();
System.out.print("#");
person.print();}
}
对于以上代码,其输出结果是
A. Employee#Person
B. Manager#Person
C. Employee#Manager
D. Manager#Manage
答案:选择D。
对于题2中的Person,Employee,Manager的print方法,若均是static的方法,其输出结果是
A. Employee#Person
B. Employee#Person
C. Manager#Person
D. Manager#Manager
答案:选择A。
实例方法的查找:从对象创建时的类开始,沿类层次向上查找;
类方法的查找:在引用变量声明时所属类中查找;
3.关于下面的程序,说法正确的是()
class Base{
int m;
public Base(int m){ this.m=m+1;}
}
public class TestDemo extends Base{
public TestDemo(){
m=m+1;
}
public static void main(String args[]){
TestDemo t = new TestDemo();
System.out.println(t.m);
}
}
A. 输出结果为0
B. 输出结果为1
C. 输出结果为2
D. 编译错误
答案:选择D。
超类中构造函数有参数,无默认构造函数,子类构造时不能省略;
4.以下哪个语句可以放到插入行
class A{}
class B extends A{}
class C extends A{}
public class Test{
public static void main(String[] args){
A x= new A();
B y= new B();
C z= new C();
//此处插入一条语句
}}
A. x=y
B. z=x
C. z=(C )y
D. y=(A)y
答案:选择A。
引用变量可被转成更一般的类(超类),被塑形为对象所属类实现接口;
引用变量向下转型,只能转为引用指向的对象的类型。
编程练习题
1. 偶数分解
题目
歌德巴赫猜想:任何一个大于六的偶数可以拆分成两个质数的和,打印出所有的可能
输入n为偶数,输出n的所有分界可能
如输入
100
输出:
100=3+97
100=11+89
100=17+83
100=29+71
100=41+59
100=47+53
思路
无复杂度要求,可先计算n范围内的质数,再遍历求解。
代码
import java.util.Scanner;
public class homework4_1 {
static boolean prime(int num) {
for (int i = 2; i < num; i++)
if ( num % i == 0 )
return false;
return true;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
boolean[] boo = new boolean[n];
in.close();
boo[2] = true;
for (int i = 3; i < n; i++) {
boo[i] = prime(i);
}
int hn = n / 2 + 1;
for (int i = 3; i < hn; i++) {
if ( boo[i] && boo[n - i] )
System.out.println(n + "=" + i + "+" + (n - i));
}
}
}
2. 最大公约数和最小公倍数
题目
输入两个正整数m和n,求其最大公约数和最小公倍数
输入
34 8
输出
2 136
思路
先求解出最大公约数,计算得到最小公倍数。
代码
import java.util.Scanner;
public class homework4_2 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int a = in.nextInt();
int b = in.nextInt();
in.close();
if ( a < b ) {
int tmp = a;
a = b;
b = tmp;
}
int gcd;
if ( a % b == 0)
gcd = b;
else {
int hb = b / 2 + 1;
gcd = 1;
for (int i = 2; i < hb; i++) {
if (( a % i == 0 ) && ( b % i == 0))
gcd = i;
}
}
int lcm = a * b / gcd;
System.out.println(gcd + " " + lcm);
}
}
3. 铺砖问题
题目
有两种砖,分别是1 * 1的砖和1 * 2的砖,用这两种砖铺1 * N的地面,问共有多少种铺法。输入为N,请输出相应的铺法数
输入:
3
输出:
3
思路
动态规划,划分为 [n - 1] 和 [n - 2] 两种状态,ans[i] = ans[i - 2] + ans[i - 1],结果即斐波那切数列。
代码
import java.util.Scanner;
public class homework4_3 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.close();
long[] ans = new long[n + 1];
ans[1] = 1;
ans[2] = 2;
if (n > 2) {
for (int i = 3; i <= n; i++) {
ans[i] = ans[i - 2] + ans[i - 1];
}
}
System.out.println(ans[n]);
}
}