练习1:(1)在ToyTest.java中,将Toy的默认构造器注释掉,并解释发生的现象。
//: typeinfo/toys/ToyTest.java
// Testing class Class.
package typeinfo.toys;
import static net.mindview.util.Print.*;
interface HasBatteries {}
interface Waterproof {}
interface Shoots {}
class Toy {
// Comment out the following default constructor
// to see NoSuchMethodError from (*1*)
Toy() {}
Toy(int i) {}
}
class FancyToy extends Toy
implements HasBatteries, Waterproof, Shoots {
FancyToy() { super(1); }
}
public class ToyTest {
static void printInfo(Class cc) {
print("Class name: " + cc.getName() +
" is interface? [" + cc.isInterface() + "]");
print("Simple name: " + cc.getSimpleName());
print("Canonical name : " + cc.getCanonicalName());
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("typeinfo.toys.FancyToy");
} catch(ClassNotFoundException e) {
print("Can't find FancyToy");
System.exit(1);
}
printInfo(c);
for(Class face : c.getInterfaces())
printInfo(face);
Class up = c.getSuperclass();
Object obj = null;
try {
// Requires default constructor:
obj = up.newInstance();
} catch(InstantiationException e) {
print("Cannot instantiate");
System.exit(1);
} catch(IllegalAccessException e) {
print("Cannot access");
System.exit(1);
}
printInfo(obj.getClass());
}
} /* Output:
Class name: typeinfo.toys.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name : typeinfo.toys.FancyToy
Class name: typeinfo.toys.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name : typeinfo.toys.HasBatteries
Class name: typeinfo.toys.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name : typeinfo.toys.Waterproof
Class name: typeinfo.toys.Shoots is interface? [true]
Simple name: Shoots
Canonical name : typeinfo.toys.Shoots
Cannot instantiate
*///:~
结果是抛出一个实例化异常InstantiationException,原因是没有找到无参构造器来实例化对象。为什么要找无参构造器呢?因为你丫的没给传参数啊,当然默认去找用无参构造器。
练习2:(2)将新的interface加到ToyTest.java中,看看这个程序是否能正确检测出并加以显示
可以。
练习3:(2)将Rhomboid(菱形)加入Shape.java中,创建一个Rhomboid,将其向上转型为Shape,然后向下转型为Rhomboid。试着将其向下转型为Circle,看看会发生什么。
public class Shapes {
public static void main(String[] args) {
List<Shape> shapeList = Arrays.asList(new Circle(),new Square(),new Triangle(),new Rhomboid());
for (Shape shape : shapeList) {
shape.draw();
}
//向上转型为shape
Shape shape = new Rhomboid();
//向下转型Rhomboid
Rhomboid r = (Rhomboid) shape;
//再向下转型为Circle
//Circle c = (Circle)shape;
//编译时不会报错,但是运行时候会报错,报的是ClassCastException,原因是强制类型转换对象,必须要有继承关系,这个话题下面会提到~简单讲的话,就是A—继承—>B,C-继承->B,但是,A、C之间并没有继承关系,所以不能类型转换,会报错。
}
}
下面就讲一下强制类型转换这个问题,试问,必须有互相继承关系的才能强制类型转换吗?
不一定,这样说不严谨。 如果是强制转换变量类型 ,就不需要继承 .如果是强制转换对象,那肯定要继承.毕竟继承是多态的基础!
练习4:(2)修改前一个练习,让你的程序在执行向下转型之前先运用instanceof检查类型。(如果是,就不执行)
public static void main(String[] args) {
List<Shape> shapeList = Arrays.asList(new Circle(), new Square(),
new Triangle(),new Rhomboid());
for (Shape shape : shapeList) {
shape.draw();
}
// 向上转型为shape
Shape shape = new Rhomboid();
// 向下转型Rhomboid
Rhomboid r = (Rhomboid) shape;
// 向下转型之前进行instanceof检查
Circle c = null;
if (shape instanceof Circle) {
c = (Circle) shape;
} else {
System.out.println("形状不是圆的!");
}
}/* Output:
Square.draw()
Triangle.draw()
Rhomboid.draw()
形状不是圆的!
*///:~
练习5:(3)实现Shapes.java中的rotate(Shape)方法,让它能判断它所旋转的是不是Circle(如果是,就不执行)。<经过查阅,已经修正~>
package typeinfo;
import java.util.*;
abstract class RShape {
void draw() { System.out.println(this + ".draw()"); }
abstract public String toString();
void rotate(int degrees) {
System.out.println("Rotating " + this +
" by " + degrees + " degrees");
}
}
class RCircle extends RShape {
public String toString() { return "Circle"; }
void rotate(int degrees) {
throw new UnsupportedOperationException();
}
}
class RSquare extends RShape {
public String toString() { return "Square"; }
}
class RTriangle extends RShape {
public String toString() { return "Triangle"; }
}
public class Shapes{
static void rotate(List<RShape> shapes) {
for(RShape shape : shapes)
if(!(shape instanceof RCircle))
shape.rotate(45);
}
public static void main(String[] args) {
List<RShape> shapes = Arrays.asList(
new RCircle(), new RSquare(), new RTriangle()
);
rotate(shapes);
}
} /* Output:
Rotating Square by 45 degrees
Rotating Triangle by 45 degrees
*///:~
练习6:(4)修改Shapes.java使这个程序能将某个特定类型的所有形状都“标示”出来(通过设标示)。每一个导出的Shape类的toString()方法应该能够指出Shape是否被标示。
package Exam;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class HShape {
boolean highlighted = false;
public void highlight() {
highlighted = true;
}
public void clearHighlight() {
highlighted = false;
}
void draw() {
System.out.println(this + " draw()");
}
public String toString() {
return getClass().getName()
+ (highlighted ? " highlighted" : " normal");
}
static List<HShape> shapes = new ArrayList<HShape>();
public HShape() {
shapes.add(this);
}
static void highlight1(Class<?> type) {
for (HShape shape : shapes) {
if (type.isInstance(shape)) {
shape.highlight();
}
}
}
static void clearHighlight1(Class<?> type) {
for (HShape shape : shapes) {
if (type.isInstance(shape)) {
shape.clearHighlight();
}
}
}
static void forEach(Class<?> type, String method) {
try {
Method m = HShape.class.getMethod(method, (Class<?>[]) null);
for (HShape shape : shapes) {
if (type.isInstance(shape)) {
m.invoke(shape, (Object[]) null);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static void highlight2(Class<?> type) {
forEach(type, "highlight");
}
static void clearHighlight2(Class<?> type) {
forEach(type, "clearHighlight");
}
}
class HCircle extends HShape {
}
class HSquare extends HShape {
}
class HTriangle extends HShape {
}
public class E06_Highlight {
public static void main(String[] args) {
List<HShape> shapes = Arrays.asList(new HCircle(), new HSquare(),
new HTriangle(), new HSquare(), new HTriangle(), new HCircle(),
new HCircle(), new HSquare());
HSquare.highlight1(HCircle.class);
HSquare.highlight2(HCircle.class);
for (HShape shape : shapes) {
shape.draw();
}
System.out.println("**************");
HSquare.highlight1(HShape.class);
HSquare.highlight2(HShape.class);
for (HShape shape : shapes) {
shape.draw();
}
System.out.println("**************");
HShape.clearHighlight1(ArrayList.class);
HShape.clearHighlight2(ArrayList.class);
for (HShape shape : shapes) {
shape.draw();
}
}
}/*
Exam.HCircle highlighted draw()
Exam.HSquare normal draw()
Exam.HTriangle normal draw()
Exam.HSquare normal draw()
Exam.HTriangle normal draw()
Exam.HCircle highlighted draw()
Exam.HCircle highlighted draw()
Exam.HSquare normal draw()
**************
Exam.HCircle highlighted draw()
Exam.HSquare highlighted draw()
Exam.HTriangle highlighted draw()
Exam.HSquare highlighted draw()
Exam.HTriangle highlighted draw()
Exam.HCircle highlighted draw()
Exam.HCircle highlighted draw()
Exam.HSquare highlighted draw()
**************
Exam.HCircle highlighted draw()
Exam.HSquare highlighted draw()
Exam.HTriangle highlighted draw()
Exam.HSquare highlighted draw()
Exam.HTriangle highlighted draw()
Exam.HCircle highlighted draw()
Exam.HCircle highlighted draw()
Exam.HSquare highlighted draw()
*///:~
在main方法中,通过给对象设置标示(例子中是HCircle,HShape,ArrayList),指定特定形状的标示,经过检查是否为shape的实例对象后对各种形状的对象设标示。最后由提toString()打印出来。
练习7:(3)修改SweetShop.java,使每种类型对象的创建由命令行参数控制。即,如果命令行是“java SweetShop Candy”,那么只有Candy对象被创建。注意你是如何通过命令行参数来控制加载哪个Class对象的。
class Candy {
static {
System.out.println("Loading Candy");
}
}
class Gum {
static {
System.out.println("Loading Gum");
}
}
class Cookie {
static {
System.out.println("Loading Cookie");
}
}
public class SweetShop {
public static void main(String[] args) throws Exception {
for (String string : args) {
Class.forName(string);
}
}
} /* Output:
首先在命令行执行:javac SweetShop.java 把其编译成.class文件
然后在命令行执行:java SweetShop Candy即,输出为: Loading Candy
在命令行执行:java SweetShop Candy Cookie,输出为Loading Candy,(/n)Loading Cookie
*///:~
即在命令行可以传参,在main方法中根据所传的参数判断调用的方法~
练习8:(5)写一个方法,令它接受任一对象作为参数,并能够递归打印出该对象所在的继承体系中的所有类。
package typeinfo;
public class E08_RecursiveClassPrint {
static void printClasses(Class<?> c) {
if (c == null) {
return;
} else {
System.out.println(c.getName());
}
for (Class<?> k : c.getInterfaces()) {
System.out.println("Interface" + k.getName());
printClasses(k.getSuperclass());
}
printClasses(c.getSuperclass());
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++) {
System.out.println("Displaying " + args[i]);
printClasses(Class.forName(args[i]));
if (i < args.length - 1) {
System.out.println("===================");
}
}
}
}/*
Displaying Circle
Circle
Shape
java.lang.Object
*///:~
这个比较麻烦,在命令行中敲javac编译后,敲java E08_RecursiveClassPrint Circle之前还要先编译一下Shapes.java,最好要在同一个目录下,然后才会打印以上信息~
练习9:(5)修改前一个练习,让这个方法使用Class.getDeclaredFields()来打印一个类中的域的相关信息。
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import static net.mindview.util.Print.*;
interface Iface {
int i = 47;
void f();
}
class Base implements Iface {
String s;
double d;
@Override
public void f() {
System.out.println("Base.f");
}
}
class Composed {
Base b;
}
class Derived extends Base {
Composed c;
String s;
}
public class E09_GetDeclaredFields {
static Set<Class<?>> alreadyDisplayed = new HashSet<Class<?>>();
static void printClasses(Class<?> c) {
// getSuperclass() 对象返回空
if (c == null) {
return;
} else {
print(c.getName());
Field[] fields = c.getDeclaredFields();
if (fields.length != 0) {
print("Fields:");
}
for (Field field : fields) {
print(" " + field);
}
for (Field field : fields) {
Class<?> k = field.getType();
if (!alreadyDisplayed.contains(k)) {
printClasses(k);
alreadyDisplayed.add(k);
}
}
// 打印这个类实现的接口
for (Class<?> k : c.getInterfaces()) {
print("interfaces:" + k.getName());
printClasses(k.getSuperclass());
}
printClasses(c.getSuperclass());
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++) {
print("Displaying " + args[i]);
printClasses(Class.forName(args[i]));
if (i < args.length - 1) {
print("===============");
}
}
}
}/*
编译完命令行输入:java E09_GetDeclaredFields Derived
Displaying Derived
Derived
Fields:
Composed Derived.c
java.lang.String Derived.s
Composed
Fields:
Base Composed.b
Base
Fields:
java.lang.String Base.s
double Base.d
java.lang.String
Fields:
private final char[] java.lang.String.value
private int java.lang.String.hash
private static final long java.lang.String.serialVersionUID
private static final java.io.ObjectStreamField[] java.lang.String.serialPersi
stentFields
public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_OR
DER
private static final int java.lang.String.HASHING_SEED
private transient int java.lang.String.hash32
[C
interfaces:java.lang.Cloneable
interfaces:java.io.Serializable
java.lang.Object
int
long
[Ljava.io.ObjectStreamField;
interfaces:java.lang.Cloneable
interfaces:java.io.Serializable
java.lang.Object
java.util.Comparator
interfaces:java.io.Serializable
interfaces:java.lang.Comparable
interfaces:java.lang.CharSequence
java.lang.Object
double
interfaces:Iface
java.lang.Object
java.lang.Object
Base
Fields:
java.lang.String Base.s
double Base.d
interfaces:Iface
java.lang.Object
*///:~
该程序使用一个有趣的类层次结构的接口和复杂类型的组合。
这里,HashSet保持输出整齐,只显示一次字段。练习10可以帮助您理解输出
练习10:(3)写一个程序,使它能判断char数组究竟是个基本类型,还是个对象。
package typeinfos;
import static net.mindview.util.Print.*;
public class E10_PrimitiveOrTrue {
public static void main(String[] args) {
char[] ac = "Hello,World!".toCharArray();
print("ac.getClass():"+ac.getClass());
print("ac.getClass().getSuperclass():"+ac.getClass().getSuperclass());
char c = 'c';
//!c.getClass(); //不能这样写,基本类型不是真正的对象
int[] ia = new int[3];
print("ia.getClass():"+ia.getClass());
long[] la = new long[3];
print("la.getClass():"+la.getClass());
double[] da = new double[3];
print("da.getClass():"+da.getClass());
String[] sa = new String[3];
print("sa.getClass():"+sa.getClass());
E10_PrimitiveOrTrue[] pot = new E10_PrimitiveOrTrue[3];
print("pot.getClass():"+pot.getClass());
//多维数组
int[][][] threed = new int[3][][];
print("threed.getClass():"+threed.getClass());
}
}/*
ac.getClass():class [C
ac.getClass().getSuperclass():class java.lang.Object
ia.getClass():class [I
la.getClass():class [J
da.getClass():class [D
sa.getClass():class [Ljava.lang.String;
pot.getClass():class [Ltypeinfos.E10_PrimitiveOrTrue;
threed.getClass():class [[[I
*///:~
通过定义一个String类型对象调用的toCharArray()方法创建一个char类型的数组。注意到你能够调用getClass()方法和getSuperclass()方法因为它是一个真正类的对象。但是如果你想要为一个基本类型变量来去调用getClass()方法,例如char c这个变量,那么编译器就会报错——不是所有元素都是对象(记住这个论断,当别人说Java是个“纯(pure)”面向对象的语言的时候)