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);
}
}
总结:子类对象实例化过程
(1)为子类对象分配内存空间,对成员变量进行默认的初始化
(2)绑定子类构造方法,将new中的参数传递给构造方法的形式参数。
(3)显式或隐式调用super语句,对从父类继承来的实例变量进行初始化。
(4)按定义顺序执行实例变量初始化操作。
(5)执行子类构造方法的剩余代码。
2. 如何实现两个对象之间互发消息,请举例说明。
class Src
{
int s;
public Src(int _s){//构造函数,保证s的非负
if(_s>=0)
s = _s;
else s=0;
}
public void change(Dis d){//设计一个特殊的方法来改变s的值,每次改变都会给Dis对象发通知
for(int a=1;a<5;a++)
{
d.get();
s=s+a*2;
}
}
}
class Dis
{
Src src;
public Dis(Src s){
this.src = s; //Dis对象中拥有了Src对象的引用
}
public void invoke(){
//Dis中对象发送消息给Src的对象
System.out.println(src.s);
}
public void get()//用来接收通知的方法
{
this.invoke();
}
}
public class Helloworld {
public static void main(String[] args) {
Src src = new Src(5);
//产生Dis对象,并将Src对象引用作为参数传入
Dis dis = new Dis(src);
//发送消息,产生调用关系
dis.src.change(dis);
}
}
3. 谈谈组合与继承的区别以及两者的使用场景(即什么时候宜用组合 ?什么时候宜用继承?)。
组合和继承是面向对象中两种代码复用的方式。
组合:在新类里面创建原有类的对象,重复利用已有类的功能。(has-a关系)
继承:可以使用现有类的功能,并且在无需重复编写原有类的情况下对原有类进行功能上的扩展。(is-a关系)
>>除非两个类之间是“is-a”的关系,否则不要轻易的使用继承,不要单纯的为了实现代码的重用而使用继承,因为过多的使用继承会破坏代码的可维护性,当父类被修改时,会影响到所有继承自它的子类,从而增加程序的维护难度和成本。
>>不要仅仅为了实现多态而使用继承,如果类之间没有“is-a”的关系,可以通过实现接口与组合的方式来达到相同的目的。设计模式中的策略模式可以很好的说明这一点,采用接口与组合的方式比采用继承的方式具有更好的可扩展性。
>>在Java语言中,能使用组合就尽量不要使用继承。
参考原文链接:https://blog.csdn.net/weixin_40995778/article/details/83306945
4. Java中的运行时多态的含义是什么?有什么作用?请举例说明。
多态分为两种情况:编译时多态和运行时多态
方法重载都是编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
运行时多态:程序运行时,Java从实例所属的类开始寻找匹配的方法执行,如果当前类中没有匹配的方法,则沿着继承关系逐层向上,依次在父类或各祖先类中寻找匹配方法,直到Object类。
注意:父类对象只能执行那些在父类中声明、被子类覆盖了的子类方法。
如果要执行子类中父类没有的方法呢?
public class Test { //例子2
public static void main(String[] args) {
Person p = new Man();
System.out.println(((Man) p).getName()); //返回结果为Man
}
}
class Person{}
class Man extends Person{
public String getName(){
String name = "Man";
return name;
}
}
此例中Person类型要引用Man类的实例,因Person中未定义setName()方法,故需要把Person类显式转换为Man类,然后调用Man中的getName方法。
5. 使用接口改写例6.8中的程序。
abstract class Shape {
protected double x,y,z; //子类可见
//抽象方法
abstract public double getArea();
abstract public double getPerimeter();
//抽象类可以有构造方法,初始化从父类继承的实例变量
public Shape(double x,double y,double z){
this.x = x;
this.y = y;
this.z = z;
}
}
class Rect extends Shape {
public Rect(double height, double width) { super(height,width, 0.0); }
public double getArea(){
return x * y;
}
public double getPerimeter(){
return (x + y)*2;
}
}
class Circle extends Shape{
public Circle(double radius){
super(radius,0.0,0.0);
}
public double getArea(){
return( x * x *Math.PI);
}
public double getPerimeter(){
return(2*Math.PI* x);
}
}
class Triangle extends Shape {
public Triangle(double lenX,double lenY,double lenZ){
super(lenX, lenY, lenZ);
}
public double getArea(){
double m= (x + y+ z)/2.0;
return Math.sqrt(m*( m-x)*( m-y)*(m-z));
}
public double getPerimeter(){
return x + y + z;
}
}
public class TestShape {
public static void main(String[] args) {
Rect rect = new Rect(25,25);//长方形
Triangle tri = new Triangle(5,5,8);//三角形
Circle cir = new Circle(12.5);//圆
printArea(rect);
printPerimeter(rect);
printArea(tri);
printPerimeter(tri);
printArea(cir);
printPerimeter(cir);
}
public static void printArea(Shape s){
System.out.println(s.getClass().getName() +
" Area:"+s.getArea());
}
public static void printPerimeter(Shape s){
System.out.println(s.getClass().getName() +
"Perimeter:"+s.getPerimeter());
}
}
改写:
interface Shape {
//抽象方法
public abstract double getArea();
public abstract double getPerimeter();
//无构造方法
}
class Rect implements Shape {
private int x,y;
public Rect(int x,int y) {
this.x=x;
this.y=y;
}
public double getArea(){
return x * y;
}
public double getPerimeter(){
return (x + y)*2;
}
}
class Circle implements Shape{
private double x;
public Circle(double v) {
this.x=v;
}
public double getArea(){
return( x * x *Math.PI);
}
public double getPerimeter(){
return(2*Math.PI* x);
}
}
class Triangle implements Shape {
private int x,y,z;
public Triangle(int x,int y,int z) {
this.x=x;
this.y=y;
this.z=z;
}
public double getArea(){
double m= (x + y+ z)/2.0;
return Math.sqrt(m*( m-x)*( m-y)*(m-z));
}
public double getPerimeter(){
return x + y + z;
}
}
public class TestShape {
public static void main(String[] args) {
Shape rect = new Rect(25,25);//长方形
Shape tri = new Triangle(5,5,8);//三角形
Shape cir = new Circle(12.5);//圆
printArea(rect);
printPerimeter(rect);
printArea(tri);
printPerimeter(tri);
printArea(cir);
printPerimeter(cir);
}
public static void printArea(Shape s){
System.out.println(s.getClass().getName() +
" Area:"+s.getArea());
}
public static void printPerimeter(Shape s){
System.out.println(s.getClass().getName() +
"Perimeter:"+s.getPerimeter());
}
}
6. 自定义一个类,覆写equals方法,以满足自身业务需求
class Pare{}
class Pare1 extends Pare{}
class Pare2 {
public static void main(String[] args){
Pare p = new Pare();
Pare1 p1 = new Pare1();
Pare pp = p1;
if (p1.equals(pp)) {
System.out.println("p1与pp引用相同");}
else {
System.out.println("p1与pp引用不同");}
if (p.equals(pp)) {
System.out.println("p与pp引用相同");}
else {
System.out.println("p与pp引用不同");}
}
}//p1与pp引用相同 p与pp引用不同
7. 举例说明运算符instanceof的使用场景。
instanceof:二元运算符。
格式:a instanceof A.
应用:其中a为对象的引用,A为类。如果a为A的实例或是其子类关系,则返回true。如果a是A父类的实例,则返回false。如果a对象的类和A没有任何关系,则编译不会通过。
例如:
class Uncle{}
class Pare{}
class Pare1 extends Pare{}
class Pare2 extends Pare1{}
class Pare3 {
public static void main(String[] args) {
Uncle u = new Uncle();
Pare p = new Pare();
Pare1 p1 = new Pare1();
Pare2 p2 = new Pare2();
if (p instanceof Pare) {
System.out.println("p instanceof Pare");
}
//子类对象是父类的实例
if (!(p1 instanceof Pare)) {
System.out.println("p1 not instanceof Pare");
} else {
System.out.println("p1 instanceof Pare");
}
//父类对象不是子类的实例
if (p1 instanceof Pare2) {
System.out.println("p1 instanceof Pare2");
} else {
System.out.println("p1 not instanceof Pare2");
}
if (null instanceof String) {
System.out.println("null instanceof String");
} else {
System.out.println("null not instanceof String");
}
}
}
8. 谈谈抽象类与接口的异同以及两者的使用场景。
共同点:
二者都可具有抽象方法,都不能实例化,但都可以有自己的声明,并能引用子类或实现类对象。
不同点:



