第3章 面向对象的核心特征
3-1 什么是类?什么是对象?他们之间的关系是怎样的?
【答】在面向对象的概念中,类是既包括数据又包括作用于数据的一组操作的封装体。类中的数据称为成员变量,类中的数据操作称为成员方法。类中的成员变量和成员方法统称为类的成员。
对象是类的实例。对象与类的关系就像变量与数据类型的关系一样。是抽象与具体,模板与实例的关系,类是抽象的、是模板,对象是具体的、是实例。
3-2 作为引用数据类型,对象在赋值和方法的参数传递方面与基本数据类型的变量有什么不同?
【答】作为引用数据类型,两个对象之间的赋值是引用赋值,对象可被赋值为null。具体可参见课本第三章图3.1的(d)。方法声明中形式参数的数据类型,既可以是基本数据类型,也可以是引用数据类型。如果形式参数的数据类型是基本数据类型,则实际参数向形式参数传递的是值;如果形参的数据类型是引用数据类型,则实参向形参传递的是引用。
同样,方法返回值的数据类型,既可以是基本数据类型,也可以是引用数据类型,两者分别传递值和引用。
3-3 面向对象技术的三个核心特性是什么?
【答】类的封装、继承和多态。
3-4 什么是封装?为什么要将类封装起来?封装的原则是什么?
【答】封装性是面向对象的核心特征之一,它提供一种信息隐藏技术。
类的封装包含两层含义:一是将数据和对数据的操作组合起来构成类,类是一个不可分割的独立单位;二是类中既要提供与外部联系的方法,同时又要尽可能隐藏类的实现细节。软件扩充和维护的需要需对类进行封装。封装原则:隐藏内部实现细节。
3-5 类中的方法与C++中的函数有什么差别?
【答】Java类中的成员方法与C语言中的函数很像,但在声明、调用等方面存在很大差别。具体方法可参考课本P66~P67。
3-6 类的构造方法和析构方法有什么作用?它们分别被谁调用?它们的访问权限范围应该是怎样的?是否每个类都必须设计构造方法和析构方法?没有设计构造方法和析构方法的类执行什么构造方法和析构方法?
【答】类的构造方法和析构方法是类特殊的成员方法,构造方法用于在创建实例时进行初始化;析构方法用于在释放实例时执行特定操作。构造方法由new运算符调用;析构方法可由对象调用,或被虚拟机自动执行。它们的访问权限范围通常都是public。
构造方法不能继承,析构方法能够继承。一个类可以不声明构造方法和析构方法。当一个类没有声明构造方法时,Java为它提供一个无参数的默认构造方法,约定自动调用父类的默认构造方法(无参数);当一个类没有声明析构方法时,它执行继承来的父类的析构方法。
3-7 Java定义了几个关键字用于表示几种访问权限?各表示什么含义?类有几种访问权限?类中成员有几种访问权限?分别使用什么关键字?
【答】Java定义了三个表示权限的关键字(public、protected、private)。类有2种访问权限分别是:公有public,缺省。类中成员有4种访问权限分别是:公有public,可被所有类访问;保护protected,可被同一包及包外所有子类访问;缺省,可被当前包中所有类访问;私有private,只能被当前类访问。
3-8 this引用有什么作用?this引用有几种使用方法?
【答】Java类中成员方法与C语言中函数还有一个重要差别就是,Java类中每个成员方法都可以使用代词this引用调用该方法的当前对象自己,this引用有以下3种用法:
(1)this用于指代调用成员方法的当前对象自身,语法格式如下:
this
(2)通过this可以调用当前对象的成员变量,调用当前对象的成员方法。语法格式如下:
this.成员变量
this.成员方法([参数列表])注意:Java中的this是引用方式,不是C++中的指针方式。
(3)this引用还可以用在重载的构造方法中,调用本类已定义好的构造方法。语法格式如下:
this([参数列表])注意:在构造方法中,this()引用必须是第一行语句。
3-9 说明类成员与实例成员的区别。
【答】Java的类中可以包括两种成员:实例成员和类成员。
实例成员是属于对象的,实例成员包括实例成员变量和实例成员方法。类成员是属于类的,需要用关键字static标识,也称为静态成员。具体区别如下:
1.实例成员变量与类成员变量
(1) 两者声明时的差别。当一个类声明成员变量时,没有使用关键字static声明的为实例成员变量,使用关键字static声明的为类成员变量。
(2) 两者存储结构的差别。当创建一个对象时,系统会为每一个对象的每一个实例成员变量分配一个存储单元,使得属于不同对象的实例成员变量有不同的值;而为每一个类成员变量只分配一个存储单元,使得所有对象公用一个类成员变量。
(3) 两者引用方式的差别。实例成员变量属于对象,必须通过对象访问;类成员变量属于类,既可以通过对象,也可以通过类访问。
2.实例成员方法与类成员方法
(1) 两者声明时的差别。当一个类声明成员方法时,没有使用关键字static声明的为实例成员方法,使用关键字static声明的为类成员方法。
(2) 两者方法体中语句的差别。类成员方法只能访问类成员变量;实例成员方法既可以访问类成员变量,也可以访问实例成员变量。在实例成员方法体中,可以使用this引用指代当前对象;而在类成员方法体中,则不能使用this引用。
(3) 两者引用方式的差别。实例成员方法必须通过对象访问;类成员方法既可以通过对象,也可以通过类访问。
3-10 什么是继承?继承机制的作用是什么?子类继承了父类中的什么?子类不需要父类中的成员时怎么办?能够删除它们吗?Java允许一个类有多个父类吗?
【答】继承性是面向对象的核心特征之一,是一种由已有的类创建新类的机制。被继承的类称为父类或超类,通过继承产生的新类称为子类或派生类。继承机制是面向对象程序设计中实现软件可重用性的最重要手段。
通过继承,子类自动拥有父类的所有成员,包括成员变量和成员方法(不包括构造方法)。子类可以更改父类成员,还可以增加自己的成员,但是,不能删除父类的成员。
在Java中以“单重继承+接口”的方式代替多重继承,不允许一个类有多个父类。
3-11 子类能够访问父类中什么样权限的成员?
【答】虽然子类继承了父类的成员变量和成员方法,但并不是对所有的成员都有访问权限。访问权限说明如下:
(1)子类对父类的私有成员(private)没有访问权限。
(2)子类对父类的公有成员(public)和保护成员(protected)具有访问权限。
(3)子类对父类中缺省权限成员访问权限分为两种情况,对同一包中父类的缺省权限成员具有访问权限,而对不同包中父类的缺省权限成员没有访问权限。
3-12 如果子类声明的成员与父类成员同名会怎么样?
【答】如果子类重定义父类的同名成员变量,则子类隐藏了父类成员变量。如果子类重定义父类的同名成员方法,当子类方法的参数列表与父类方法的参数列表完全相同时,则称子类成员方法覆盖了成员方法。如果子类重定义父类的同名成员方法,当子类方法的参数列表与父类方法的参数列表不同时,子类继承了父类的成员方法,并重载了继承来的该成员方法。
3-13 super引用有什么作用?super引用有几种使用方法?
【答】当子类重定义了父类成员时,则存在同名成员问题。此时,在子类方法体中,成员均默认为子类成员。如果需要引用父类同名成员,则需要使用supper引用。在以下两种同名成员情况下,需要使用supper引用。
(1)子类隐藏父类成员时,如需要访问父类同名成员变量时,需要使用supper指代父类的同名成员变量。语法如下:
super.成员变量
(2)子类覆盖父类成员时,如需要访问父类同名成员方法时,需要使用supper指代父类的同名成员方法。语法如下:
super.成员方法([参数列表])
注意:super引用不能像this引用一样单独使用。
3-14 什么是多态性?什么是方法的重载?方法的重载和覆盖有何区别?
【答】在面向对象语言中,多态是指一个方法可以有多种实现版本,类的多态性表现为方法的多态性。重载是指同一个类中的多个方法可以同名但参数列表必须不同。重载表现为同一个类中方法的多态性。覆盖是指子类重定义了父类中的同名方法。覆盖表现为父类与子类之间方法的多态性。
3-15 什么是运行时多态?方法的重载和覆盖分别是什么时的多态性?
【答】如果在编译时不能确定、只有在运行时才能确定执行多个同名方法中的哪一个,则称为运行时多态。方法的重载都是编译时多态。方法的覆盖变现出两种多态性,当对象获得本类的实例时,为编译时多态,否则为运行时多态。
3-16 什么是抽象类?在什么情况下需要设计抽象类?抽象类中是否必须有抽象方法?
【答】使用关键字abstract声明的类称为抽象类,使用abstract声明的成员方法为抽象方法。抽象类中可以不包含抽象方法,但包含抽象方法的类必须被声明为抽象类。
3-17 什么是最终类?在什么情况下需要设计最终类?最终类中是否必须有最终方法?
【答】使用关键字final声明的类称为最终类,最终类不能被继承。使用final声明的成员方法称为最终方法,最终方法不能被子类覆盖。最终类中包含的都是最终方法,非最终类也可以包含最终方法。
3-18 将辗转相除法求两个整数的最大公因数gcd(a,b)用递归方法实现,辗转相除法题意见例2.11,再设计下列方法:
(1)求两个整数a﹑ b的最小公倍数;
(2)求三个整数a﹑b﹑c的最大公约数。
〖解答〗程序如下。
public class GCD_recursion
{
public static int gcd(int a,int b) //返回a,b的最大公因数
{
if (b==0)
return a;
if (a<0)
return gcd(-a,b);
if (b<0)
return gcd(a,-b);
return gcd(b, a%b);
}
public static int gcd(int a,int b,int c) //返回a,b,c的最大公因数
{
return gcd(gcd(a,b),c);
}
public static int multiple(int a,int b) //返回a,b的最小公倍数
{
return a*b/gcd(a,b);
}
public static void main(String args[])
{
int a=12,b=18,c=27;
System.out.println("gcd("+a+","+b+")="+gcd(a,b));
System.out.println("gcd("+(-a)+","+b+")="+gcd(-a,b));
System.out.println("gcd("+a+","+b+","+c+")="+gcd(a,b,c));
System.out.println("multiple("+a+","+b+")="+multiple(a,b));
}
}
程序运行结果如下:
gcd(12,18)=6
gcd(-12,18)=6
gcd(12,18,27)=3
multiple(12,18)=36
3-19 用递归方法求 n个数的无重复全排列。
〖解答〗程序如下。
public class Permutation
{
private int[] table;
public Permutation(int n) //构造方法
{
if (n>0)
{
table = new int[n];
for (int i=0;i<n;i++)
table[i] = i+1;
permute(n);
}
else
table = null;
}
private void output() //输出数组元素
{
for (int i=0;i<table.length;i++)
System.out.print(" "+table[i]);
System.out.println();
}
private void swap(int i,int j) //交换数组两个元素值
{
if (table!=null && i>=0 && i<table.length && j>=0 && j<table.length)
{
int temp = table[i];
table[i] = table[j];
table[j] = temp;
}
}
private void permute(int n) //用递归方法求n个数的无重复全排列
{
if (n==1)
this.output();
else
{
permute(n-1);
for (int j=0;j<n-1;j++)
{
swap(n-1,j);
permute(n-1);
swap(n-1,j);
}
}
}
public static void main(String args[])
{
new Permutation(3);
}
}
程序运行结果如下:
1 2 3
2 1 3
3 2 1
2 3 1
1 3 2
3 1 2
3-20 Java为什么不支持指针?C++的指针类型存在哪些潜在的错误?没有指针,Java如何实现在C++中用指针实现的功能?例如,通过访问指针访问数组元素,通过指针使用字符串,方法参数传递地址,方法参数用于输出,方法返回非基本数据类型等。
【答】由于指针往往在带来方便的同时也导致代码不安全的根源,如内存泄露等,同时也会使程序变得非常复杂难以理解,Java语言明确说明取消了指针。Java使用引用代替了指针。
3-21 Java为什么不支持C++的运算符重载特性?
【答】这是为了防止运算符重载使得代码的功能变的不清晰。