一、怎样判断对象是否可以转换?可以使用instanceof运算符判断一个对象是否可以转换为指定的类型,参看实例: TestInstanceof.java
public class TestInstanceof { public static void main(String[] args) { //声明hello时使用Object类,则hello的编译类型是Object,Object是所有类的父类 //但hello变量的实际类型是String Object hello = "Hello"; //String是Object类的子类,所以返回true。 System.out.println("字符串是否是Object类的实例:" + (hello instanceof Object)); //返回true。 System.out.println("字符串是否是String类的实例:" + (hello instanceof String)); //返回false。 System.out.println("字符串是否是Math类的实例:" + (hello instanceof Math)); //String实现了Comparable接口,所以返回true。 System.out.println("字符串是否是Comparable接口的实例:" + (hello instanceof Comparable)); String a = "Hello"; //String类既不是Math类,也不是Math类的父类,所以下面代码编译无法通过 //System.out.println("字符串是否是Math类的实例:" + (a instanceof Math)); } }
二、下列语句哪一个将引起编译错误?为什么?哪一个会引起运行时错误?为什么?
m=d;
d=m;
d=(Dog)m;
d=c;
c=(Cat)m;
先进行自我判断,得出结论后,运行TestCast.java实例代码,看看你的判断是否正确
class Mammal{} class Dog extends Mammal {} class Cat extends Mammal{} public class TestCast { public static void main(String args[]) { Mammal m; Dog d=new Dog(); Cat c=new Cat(); m=d; //d=m; d=(Dog)m; //d=c; //c=(Cat)m; } }
d=m;
d=c;
c=(Cat)m;
这三句话有错。
三、请看以下“变态”的类(参看示例ParentChildTest.java),运行以下测试代码,并回答问题:1.程序运行结果是什么?2.你如何解释会得到这样的输出?3.计算机是不会出错的,之所以得到这样的运行结果也是有原因的,那么从这些运行结果中,你能总结出Java的哪些语法特性?
public class ParentChildTest { public static void main(String[] args) { Parent parent=new Parent(); parent.printValue(); Child child=new Child(); child.printValue(); parent=child; parent.printValue(); parent.myValue++; parent.printValue(); ((Child)parent).myValue++; parent.printValue(); } } class Parent{ public int myValue=100; public void printValue() { System.out.println("Parent.printValue(),myValue="+myValue); } } class Child extends Parent{ public int myValue=200; public void printValue() { System.out.println("Child.printValue(),myValue="+myValue); } }
1、结果:
2、Java语法特性:
(1)当子类与父类拥有一样的方法,并且让一个父类变量引用一个子类对象时,到底调用哪个方法,由对象自己的“真实”类型所决定,这就是说:对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。
这个特性实际上就是面向对象“多态”特性的具体表现。
(2)如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。如果子类被当作父类使用,则通过子类访问的字段是父类的!
四、请使用javap查看编译器为TestPolymorphism.java生成的字节码指令,然后通过互联网搜索资料,尝试从底层开始理解Java编译器是如何为多态代码生成字节码指令,在程序运行过程中,多态特性又是如何实现的。
class Parent1 { public int value=100; public void Introduce() { System.out.println("I‘m father"); } } class Son extends Parent1 { public int value=101; public void Introduce() { System.out.println("I‘m son"); } } class Daughter extends Parent1 { public int value=102; public void Introduce() { System.out.println("I‘m daughter"); } } public class TestPolymorphism { public static void main(String args[]) { Parent1 p=new Parent1(); p.Introduce();//子类的方法 System.out.println(p.value);//父类的变量对象 p=new Son(); p.Introduce(); System.out.println(p.value); p=new Daughter(); p.Introduce(); System.out.println(p.value); } }
结果:
五、在实例中理解多态的含义与用途
1、三种动物对应三个类,每个类定义一个eat()方法,表示吃饲养员给它们的食物,再设计一个Feeder类代表饲养员,其name字段保存饲养员名字,三个方法分别代表喂养三种不同的动物,其参数分别引用三种动物对象。
package Zoo1; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); // 饲养员小李喂养一只狮子 f.feedLion(new Lion()); // 饲养员小李喂养十只猴子 for (int i = 0; i < 10; i++) { f.feedMonkey(new Monkey()); } // 饲养员小李喂养5只鸽子 for (int i = 0; i < 5; i++) { f.feedPigeon(new Pigeon()); } } } class Feeder { public String name; public Feeder(String name) { this.name = name; } public void feedLion(Lion l) { l.eat(); } public void feedPigeon(Pigeon p) { p.eat(); } public void feedMonkey(Monkey m) { m.eat(); } } class Lion { public void eat() { System.out.println("我不吃肉谁敢吃肉!"); } } class Monkey { public void eat() { System.out.println("我什么都吃,尤其喜欢香蕉。"); } } class Pigeon { public void eat() { System.out.println("我要减肥,所以每天只吃一点大米。"); } }
2、第一次程序重构:引入继承,简化Feeder类
package Zoo2; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); //饲养员小李喂养一只狮子 f.feedAnimal(new Lion()); //饲养员小李喂养十只猴子 for (int i = 0; i < 10; i++) { f.feedAnimal(new Monkey()); } //饲养员小李喂养5只鸽子 for (int i = 0; i < 5; i++) { f.feedAnimal(new Pigeon()); } } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimal(Animal an) { an.eat(); } } abstract class Animal { public abstract void eat(); } class Lion extends Animal { public void eat() { System.out.println("我不吃肉谁敢吃肉!"); } } class Monkey extends Animal { public void eat() { System.out.println("我什么都吃,尤其喜欢香蕉。"); } } class Pigeon extends Animal { public void eat() { System.out.println("我要减肥,所以每天只吃一点大米。"); } }
3、第二次程序重构,修改feedAnimals方法,让它接收一个Animal数组……
package Zoo3; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); Animal[] ans = new Animal[16]; //饲养员小李喂养一只狮子 ans[0] = new Lion(); //饲养员小李喂养十只猴子 for (int i = 0; i < 10; i++) { ans[1 + i] = new Monkey(); } //饲养员小李喂养5只鸽子 for (int i = 0; i < 5; i++) { ans[11 + i] = new Pigeon(); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Animal[] ans) { for (Animal an : ans) { an.eat(); } } } abstract class Animal { public abstract void eat(); } class Lion extends Animal { public void eat() { System.out.println("我不吃肉谁敢吃肉!"); } } class Monkey extends Animal { public void eat() { System.out.println("我什么都吃,尤其喜欢香蕉。"); } } class Pigeon extends Animal { public void eat() { System.out.println("我要减肥,所以每天只吃一点大米。"); } }
4、第三次重构,修改feedAnimals方法,让其接收一个元素数目可变的对象容器。
package Zoo4; import java.util.Vector; public class Zoo { public static void main(String args[]) { Feeder f = new Feeder("小李"); Vector<Animal> ans = new Vector<Animal>(); //饲养员小李喂养一只狮子 ans.add(new Lion()); //饲养员小李喂养十只猴子 for (int i = 0; i < 10; i++) { ans.add(new Monkey()); } //饲养员小李喂养5只鸽子 for (int i = 0; i < 5; i++) { ans.add(new Pigeon()); } f.feedAnimals(ans); } } class Feeder { public String name; Feeder(String name) { this.name = name; } public void feedAnimals(Vector<Animal> ans) { for (Animal an : ans) { an.eat(); } } } abstract class Animal { public abstract void eat(); } class Lion extends Animal { public void eat() { System.out.println("我不吃肉谁敢吃肉!"); } } class Monkey extends Animal { public void eat() { System.out.println("我什么都吃,尤其喜欢香蕉。"); } } class Pigeon extends Animal { public void eat() { System.out.println("我要减肥,所以每天只吃一点大米。"); } }
5、从这个示例中可以看到,通过在编程中应用多态,可以使我们的代码具有更强的适用性。当需求变化时,多态特性可以帮助我们将需要改动的地方减少到最低限度。
多态编程有两种主要形式:
(1)继承多态:示例程序使用的方法
(2)接口多态:使用接口代替抽象基类。
使用多态最大的好处是:
当你要修改程序并扩充系统时,你需要修改的地方较少,对其它部分代码的影响较小!千万不要小看这两个“较”字!程序规模越大,其优势就越突出。
六、用多态的方法模拟ATM操作流程。
//王荣荣 2016/11/18 import java.util.Scanner; class PersonalAccount{ private String passWord="123456";//密码 private String number;//银行卡号 private int money=0; public int getMoney(){ return money; }//余额 public void setPw(String s){ passWord=s; }//设置密码 public void addMoney(int x){ money+=x; }//加钱 public void minusMoney(int x){ money-=x; }//减钱 public boolean whetherPwTrue(String s){//密码是否正确 if(s.equals(passWord)) return true; else return false; } } abstract class PATM{ abstract boolean withdraw(int x);//取款 abstract void save(int x);//存款 abstract boolean transfer(String s,int x);//转账 abstract boolean ifPass(String s);//判断输入的密码是否正确 abstract int getRest();//查询余额 abstract void setPassword(String s);//设置密码 } class ATM extends PATM{ private String numbers[]={"123451","123452", "123453","123454","123455"};//数据库中已有的账户卡号 private PersonalAccount account=new PersonalAccount(); public boolean withdraw(int x) { if(x>account.getMoney()) return false; else{ account.minusMoney(x); return true; } } public void save(int x) { account.addMoney(x); } public boolean transfer(String s, int x) { //转账 //先判断转到账户号是否存在 //再判断余额是否足够 boolean flag=false; for(int i=0;i<numbers.length;i++) if(s.equals(numbers[i])) flag=true; if(x>account.getMoney()) flag=false; if(x<=account.getMoney()&&flag) account.minusMoney(x);; return flag; } public boolean ifPass(String s) { return account.whetherPwTrue(s); } public int getRest() { return account.getMoney(); } public void setPassword(String s) { account.setPw(s); } } public class Atm1 { public static void main(String[] args) { Scanner in=new Scanner(System.in); ATM atm=new ATM(); int choose=0,num=0; String pw=""; next:while(true){ System.out.println("是否进入账户(0否1是):"); int kk=in.nextInt(); if(kk==0) break; else if(kk!=1){ System.out.println("输入错误!"); continue; } System.out.println("输入账户密码:"); pw=in.next(); if(atm.ifPass(pw)){ while(true){ showFace(); choose=in.nextInt(); switch(choose){ case 1: System.out.println("输入存款金额:"); num=in.nextInt(); atm.save(num); System.out.println("存款成功!"); System.out.println("当前余额:"+atm.getRest()+"元"); break; case 2: System.out.println("请选择:"); int a[]={100,500,1000,1500,2000,5000}; for(int i=0;i<a.length;i++) System.out.println((i+1)+"."+a[i]+"元"); System.out.println("7.其他"); int ch=in.nextInt(); if(ch>=1&&ch<=6){ if(atm.withdraw(a[ch-1])) System.out.println("取款成功!"); else System.out.println("余额不足!"); } else if(ch==7){ System.out.println("请输入取款金额:"); num=in.nextInt(); if(atm.withdraw(num)) System.out.println("取款成功!"); else System.out.println("余额不足!"); } else System.out.println("输入有误!"); System.out.println("当前余额:"+atm.getRest()+"元"); break; case 3: System.out.println("账户号:"); String s=in.next(); System.out.println("转账金额:"); int i=in.nextInt(); if(atm.transfer(s, i)) System.out.println("转账成功!"); else System.out.println("转账失败!"); System.out.println("当前余额:"+atm.getRest()+"元"); break; case 4: System.out.println("输入六位数密码:"); String p=in.next(); atm.setPassword(p); break; case 5: System.out.println("当前余额:"+atm.getRest()+"元"); break; default: continue next; } } } else System.out.println("密码错误!"); } } //显示菜单方法 public static void showFace(){ System.out.println("1.存款"); System.out.println("2.取款"); System.out.println("3.转账汇款"); System.out.println("4.修改密码"); System.out.println("5.查询余额"); System.out.println("6.退卡"); System.out.println("请选择:"); } }
结果: