《Java编程思想》第9章 练习题

源代码地址:https://github.com/yangxian1229/ThinkingInJava
练习1:修改第8章练习9中的Rodent,使其成为一个抽象类。只要可能,就将Rodent的方法声明为抽象方法。
Note that the root class method Object.toString( ) can be left out of the abstract base class.
练习2:创建一个不包含任何抽象方法的抽象类,并验证我们不能为该类创建任何实例。
练习3:创建一个基类,让它包含抽象方法print(),并在导出类中覆盖该方法。覆盖后的方法版本可以打印导出类中定义的某个整型变量的值。在定义该变量之处,赋予它非零值。在基类的构造器中调用这个方法。现在,在main()方法中,创建一个导出类对象,然后调用它的print()方法。请解释发生的情形。

package ch9;

abstract class E03A{
	abstract void print();
	E03A(){
		System.out.print("E03A: ");
		print();
	}
}

class E03B extends E03A{
	private int b = 22;
	void print(){
		System.out.println("b="+b);
	}
	E03B(){
		super();
		System.out.print("E03B: ");
		print();
	}
}

public class E03 {
	public static void main(String[] args) {
		E03B eb = new E03B();
	}
}/* Output:
E03A: b=0
E03B: b=22
*///:~

The java virtual machine zeroes the bits of the object after it allocates storage, producing a default value for b before any other initialization occurs. The codeThinking in Java, 4 128 th Edition Annotated Solution Guide calls the base-class constructor before running the derived-class initialization, so we see the zeroed value of b as the initial output.
The danger of calling a method inside a constructor is when that method depends on a derived initialization. Before the derived-class constructor is called, the object may be in an unexpected state (in Java, at least that state is defined; this is not true with all languages – C++, for example). The safest approach is to set the object into a known good state as simply as possible, and then perform any other operations outside the constructor.
练习4:创建一个不包含任何方法的抽象类,从它那里导出一个类,并添加一个方法。创建一个静态方法,它可以接受指向基类的引用,将其向下转型到导出类,然后再调用该静态方法。在main()中,展现它的运行情况。然后,为基类中的方法加上abstract声明,这样就不再需要进行向下转型。

package ch9;

abstract class E04A{}

class E04B extends E04A{
	public void hello(){
		System.out.println("E04B say hello!");
	}
}

abstract class E04A1{
	abstract public void hi();
}

class E04B1 extends E04A1{
	public void hi(){
		System.out.println("E04B1 say hi!");
	}
}


public class E04 {
	static public void test(E04A e04){
		//must downcast
		System.out.println("E04.test(): ");
		((E04B)e04).hello();
	}
	
	static public void test1(E04A1 e041){
		//no downcast necessary
		System.out.println("E041.test1(): ");
		e041.hi();
	}

	public static void main(String[] args) {
		E04A e = new E04B();
		test(e);
		
		E04A1 e1 = new E04B1();
		test1(e1);
	}
}

test( ) needs the downcast to call hello()), while test1( ) doesn’t need a downcast because hi() is defined in the base class.
练习5:在某个包内创建一个接口,内含三个方法,然后在另一个包中实现此接口。
练习6:证明接口内所有方法都自动是public的。
备注:略。
练习7:修改第8章中的练习9,使Rodent成为一个接口。
练习8:在polymorphism.Sandwich.java中,创建接口FastFood并添加加合适的方法,然后修改Sandwich以实现FastFood接口。
练习9:重构Music5.java,将在Wind,PrecussionStringed中的公共方法移入一个抽象类中。
备注:移入抽象类中可以消除重复代码
练习10:修改Music5.java,添加Playable接口。将play()的声明从Instrument中移动Playable中。通过将Playable包括在implements列表中,把Playable添加到导出类中。修改tune()使它接受Playable而不是Instrument作为参数。
练习11:创建一个类,它有一个方法用于接受一个String类型的参数,生成的结果是将该参数中每一对字符进行互换。对该类进行适配,使得它可以用于interfaceprocessor.Apply.process()。

package ch9;

public interface Processor {
  String name();
  Object process(Object input);
} ///:~

package ch9;

import static net.mindview.util.Print.*;

public class Apply {
  public static void process(Processor p, Object s) {
    print("Using Processor " + p.name());
    print(p.process(s));
  }
} ///:~

package ch9;

class CharacterPairSwapper {
	static String swap(String s) {
		StringBuilder sb = new StringBuilder(s);
		for (int i = 0; i < sb.length() - 1; i += 2) {
			char c1 = sb.charAt(i);
			char c2 = sb.charAt(i + 1);
			sb.setCharAt(i, c2);
			sb.setCharAt(i + 1, c1);
		}
		return sb.toString();
	}
}

class SwapperAdapter implements Processor {
	@Override
	public String name() {
		return getClass().getSimpleName();
	}
	@Override
	public String process(Object input) {
		return CharacterPairSwapper.swap((String) input);
	}
}

public class E11 {
	public static void main(String[] args) {
		Apply.process(new SwapperAdapter(),"1234");
		Apply.process(new SwapperAdapter(),"abcde");
	}
}/* Output:
Using Processor SwapperAdapter
2143
Using Processor SwapperAdapter
badce
*///:~

适配器设计模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。
练习12:在Adventure.java中,按照其他接口的样式,增加一个CanClimb接口。
练习13:创建一个接口,并从该接口继承两个接口,然后从后面两个接口多重继承第三个接口。

package ch9;

interface E13A{
	void f();
}

interface E13B1 extends E13A{
	void f();
}

interface E13B2 extends E13A{
	void f();
}
interface E13C extends E13B1,E13B2{
	void f();
}

public class E13 implements E13C{
	public void f(){
		System.out.println(this+" f()");
	}
	public static void main(String[] args) {
		E13 e = new E13();
		e.f();
	}
}///:~

Java allows multiple interface inheritance but not multiple implementation inheritance, which eliminates ambiguity about which of two identical members we use when combining implementations of the same base class. We replicate f( ) in the interfaces above to demonstrate that Java avoids the “diamond problem” (so called because of the diamond-shaped class diagram produced by multiple inheritance. C++ requires extra base-class syntax resolve the ambiguity created by concrete multiple inheritance).
练习14:创建三个接口,每个接口都包含两个方法。继承出一个接口,它组合了这三个接口并添加了一个新方法。创建一个实现了该新接口并继承了某个具体类的类。现在编写四个方法,每一个方法接受这四个接口之一作为参数。在main()方法中,创建这个类的对象,并将其传递给这四个方法。
练习15:将前一个练习修改为:创建一个抽象类,并将其继承到一个导出类中。
练习16:看完第13章字符串再回来写。
练习17:证明在接口中的域隐式地是static和final的。

package ch9;

interface StaticFinalTest{
	String RED = "red";
}

class E17Test implements StaticFinalTest{
	public E17Test(){
		//cannot assign a value to final variable RED:
		//! RED = "blue";
	}
}

public class E17 {
	public static void main(String[] args) {
		// Accessing as a static field:
		System.out.println("StaticFinalTest.RED = "+
		StaticFinalTest.RED);
	}
}/* Output:
StaticFinalTest.RED = red
*///:~

The compiler tells you RED is a final variable when you try to assign a value to it. RED is clearly static because you can access it using static syntax.
练习18:创建一个Cycle接口及其Unicycle,Bicycle和Tricycle实现。对每种类型的Cycle都创建相应的工厂,然后编写代码使用这些工厂。
练习19:使用工厂方法来创建一个框架,它可以执行抛硬币和掷骰子功能。

猜你喜欢

转载自blog.csdn.net/lanzijingshizi/article/details/84281110