关于Java(5)
论述题
1. 实验:利用IDE的debug功能给例6.4和例6.5的new语句设置断点,使用单步调试(step into/step over)跟踪子类对象实例化(初始化)的执行顺序,并总结该过程。
- 例6.4:显式使用super调用父类的构造方法
class AddClass {
private int x=0,y=0,z=0;
AddClass (int x) {
this.x=x;
}
AddClass (int x,int y) {
this(x);
this.y=y;
}
AddClass (int x,int y,int z) {
this(x,y);
this.z=z;
}
public int add() {
return x+y+z;
}
}
public class SonAddClass extends AddClass{
int a=0,b=0,c=0;
SonAddClass (int x) {
super(x); a=x+7;
}
SonAddClass (int x,int y){
super(x,y); a=x+5; b=y+5;
}
SonAddClass (int x, int y,int z){
super(x,y,z); a=x+4; b=y+4; c=z+4;
}//super(x,y,z)如果去掉会如何?
public int add() {
System.out.println("super:x+y+z="+super.add());
return a+b+c;
}
public static void main(String[] args){
SonAddClass p1=new SonAddClass (2,3,5);
SonAddClass p2=new SonAddClass (10,20);
SonAddClass p3=new SonAddClass (1);
System.out.println("a+b+c="+p1.add());
System.out.println("a+b="+p2.add());
System.out.println("a="+p3.add());
}
}
- 例6.5:隐式使用super调用父类的构造方法
class Pare {
int i=3;
Pare(){
System.out.println("call super()");
}
}
class Construct extends Pare {
int i = 10;
Construct() {
System.out.println("execute Construct()");
}
Construct(int num) {
this(); //如果去掉此句呢?
System.out.println("execute Construct(int)");
}
public static void main(String[] args) {
Construct ct = new Construct(9);
System.out.println(ct.i);
}
}
执行顺序:
① 为子类对象分配内存空间,对成员变量进行默认的初始化。
② 调用与传入参数对应的子类构造方法,将new中的参数传递给构造方法的形式参数。
③ 显式或隐式调用super方法,进行父类初始化方法 。
④ 执行初始化字段,最后进行子类初始化方法的剩余语句。
2. 如何实现两个对象之间互发消息,请举例说明。
两个对象可以通过组合的现象实现互发消息,即两个对象互为对方的属性。
class A{
private int value;
public A(int value){
this.value=value;
}
public void speak(){
System.out.println(value);
}
}
class B{
A a;
public B(A a){
this.a=a;
}
}
public class test{
public static void main(String[] args) {
A a=new A(9);
B b=new B(a);
b.a.speak(); //9
}
}
3. 谈谈组合与继承的区别以及两者的使用场景(即什么时候宜用组合?什么时候宜用继承? ) 。
区别:组合现象是一个类的对象引用是另一个类的属性,对象引用可以指向其对应类的任何一个对象,具有低耦合性,但对象只能调用其规定好的方法,无法修改;继承是子类将父类的属性和方法全部继承下来,子类可以重载和覆盖父类的方法,继承具有类似一对一的属性,高耦合性。
宜用组合的场景:
一个类的对象与另一个类的对象具有一对多关系,一个类的方法对于另一个类来说永远不用改变。
宜用继承的场景:
两个类的对象具有一对一的关系,一个类需要重载或覆盖另一个类的方法;抽象类。
组合只是将两个类结合起来其中一个类是另外一个类的域变量两个类之间无上下级关系。而继承则是如果子类建立那么父类一定要先建立的这种关系。所以当你只需要使用另外一个类的方法时使用组合,但是如果你需要使用另外一个类的方法,需要覆写时用继承。
4. Java中的运行时多态的含义是什么?有什么作用?请举例说明。
运行时多态:当同一个引用变量(父类引用)指向不同的子类实例,然后访问引用变量成员方法, 方法会有不同的表现。换句话说,运行时多态就是重写,就是子类方法重写了父类方法。使用父类引用指向子类对象,再调用某一父类中的方法时,不同子类会表现出不同结果。
作用:运行时多态性提供了更大的灵活性,因为所有事情都在运行时得到解决。
//父类
class A {
void fun()
{
System.out.println(“I am A”);
}
}
//A的子类B
class B extends A
{
void fun()
{
System.out.println(“I am B”);
}
}
//A的子类C
class C extends A
{
void fun()
{
System.out.println(“I am C”);
}
}
class Test
{
public static void main(String[] args)
{
//定义父类对象引用变量
A a;
//子类对象
B b = new B();
C c = new C();
//父类对象引用变量 引用 子类对象
a=b;
a.fun(); //输出 I am B
a=c;
a.fun(); // //输出 I am C
}
}
5. 使用接口改写例6.8中的程序。
interface Shapes{
public abstract double getArea();
public abstract double getPerimter();
}
class Rect implements Shapes{
private double x,y;
public Rect(double x,double y){
this.x=x;
this.y=y;
}
public double getArea(){
return x*y;
}
public double getPerimter(){
return 2*(x+y);
}
}
class Triangle implements Shapes{
private double x,y,z,m;
public Triangle(double x,double y,double z){
this.x=x;
this.y=y;
this.z=z;
this.m=x+y+z;
}
public double getArea(){
return(Math.sqrt(m+(m-x)*(m-y)*(m-z)));
}
public double getPerimter(){
return x+y+z;
}
}
class Circle implements Shapes{
private int r;
public Circle(int r){
this.r=r;
}
public double getArea(){
return Math.PI*r*r;
}
public double getPerimter(){
return 2*Math.PI*r;
}
}
public class test{
public static void main(String[] args) {
Rect r=new Rect(10,20);
Triangle t=new Triangle(10, 20, 30);
Circle c=new Circle(10);
printArea(r);
printPerimter(r);
printArea(t);
printPerimter(t);
printArea(c);
printPerimter(c);
/**
Rect Area:200.0
Rect Perimter:60.0
Triangle Area:245.07141816213493
Triangle Perimter:60.0
Circle Area:314.1592653589793
Circle Perimter:62.83185307179586
*/
}
public static void printArea(Shapes s){
System.out.println(s.getClass().getName()+" Area:"+s.getArea());
}
public static void printPerimter(Shapes s){
System.out.println(s.getClass().getName()+" Perimter:"+s.getPerimter());
}
}
6. 自定义一个类,覆写equals方法,以满足自身业务需求。
class Person{
private String name;
private int age;
private int weight;
public String getName(){
return name;
}
public int getAge(){
return age;
}
public int getWeight(){
return weight;
}
public Person(String name,int age,int weight){
this.name=name;
this.age=age;
this.weight=weight;
}
@Override
public boolean equals(Object o){
if(getName()==((Person)o).getName()&&getAge()==((Person)o).getAge()&&getWeight()==((Person)o).getWeight()){
return true;
}else{
return false;
}
}
}
public class test{
public static void main(String[] args) {
Person p1=new Person("zma",19,120);
Person p2=new Person("zma",19,120);
Person p3=new Person("zma",18,130);
System.out.println(p1.equals(p2));//true;
System.out.println(p2.equals(p3));//false;
}
}
7. 举例说明运算符instanceof的使用场景。
- A instanceof B:
A为对象,B为类 |
---|
A为B的实例或者为B子类的示例,返回true |
A为B父类的示例返回false |
A与B没有任何关系,编译错误 |
class Animal{
public void getName(){}
public void getAge(){}
}
class Dog extends Animal{
private String name;
private int age;
Dog(String name,int age){
this.name=name;
this.age=age;
}
@Override
public void getName(){
System.out.println("Dog's name is:"+name);
}
@Override
public void getAge(){
System.out.println("Dog's name is:"+age);
}
}
class Person{}
public class test{
public static void main(String[] args){
Animal a=new Animal();
Dog d=new Dog("jack",5);
Person p=new Person();
System.out.println(d instanceof Animal);//true;
System.out.println(a instanceof Dog);//false;
// System.out.println(p instanceof A);编译错误 Incompatible conditional operand types Person and A
}
}
8. 谈谈抽象类与接口的异同以及两者的使用场景。
抽象类的使用场景
既想约束子类具有共同的行为(但不再乎其如何实现),又想拥有缺省的方法,又能拥有实例变量
如:模板方法设计模式,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现。
接口的应用场景
① 约束多个实现类具有统一的行为,但是不在乎每个实现类如何具体实现
② 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
③ 实现类需要具备很多不同的功能,但各个功能之间可能没有任何联系。
④ 使用接口的引用调用具体实现类中实现的方法(多态)
参考资料: 深入理解Java的接口和抽象类