一.多态概念
注:Python支持多态(同样还有鸭子类型【可以不必继承重写方法,只要方法名相同,动态语言的特性】)
意味着允许不同类的对象对同一信息做出不同的反应。
分类:
编译时多态:设计时多态,方法重载
运行时多态:程序运行时动态决定调用哪个方法
Java多态必要条件:
满足继承关系;
父类引用指向子类对象
二.向上转型和向下转型
1.向上转型(隐式转型/自动转型):
父类引用指向子类实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法【小类转型为大类】
注意:父类中的静态方法无法被子类重写,所以向上转型之后,只有调用父类原有的静态方法(如果想调用子类的静态方法就得向下转型)
2.向下转型(强制类型转换):
子类引用指向父类对象,此处必须进行强转,可以调用子类特有的方法【 必须满足转换条件才能进行强制转换】
1 package com.swpu.test; 2 3 import com.swpu.plo.Animal; 4 import com.swpu.plo.Cat; 5 import com.swpu.plo.Dog; 6 7 public class Test { 8 9 public static void main(String[] args) {10 /**11 * 向上转型,隐式转型,自动转型12 * 父类引用指向子类实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法13 * 【小类转型为大类】14 */15 Animal one =new Animal();16 Animal two=new Cat();17 Animal three=new Dog();18 one.eat();19 //无法调用Cat和Dog独有的方法20 two.eat();21 three.eat();22 /**23 * 输出:24 * 所有的动物都要吃东西25 小猫吃东西!!!26 狗吃东西!!!27 28 */29 System.out.println("**************");30 31 /**32 * 向下转型,强制类型转换33 * 子类引用指向父类对象,此处必须进行强转,可以调用子类特有的方法34 * 必须满足转换条件才能进行强制转换35 */36 Cat tmp=(Cat)two;37 tmp.eat();38 tmp.run();39 //会报错,Cat和Dog没有关系,能转换为Cat是因为two原来指向的就是Cat空间【相当于还原】40 // Cat tmp2 =(Dog)two;41 42 43 44 }45 46 }
3.instanceof:判断对象是否是某个类的实例
注:相当于Python中的isinstance方法(会检查继承链,肯定是所实例类的对象实例,也是其所继承类的实例),而Python中type只判断是否指向同一个对象(不会检查继承链)
1 if(two instanceof Cat){ 2 Cat tmp=(Cat)two; 3 tmp.eat(); 4 tmp.run(); 5 System.out.println("我是猫对象"); 6 } 7 8 if (two instanceof Dog){ 9 Dog tmp=(Dog)two;10 tmp.eat();11 tmp.sleep();12 System.out.println("我是狗对象");13 }14 if (two instanceof Animal){15 System.out.println("我是Animal对象");16 17 18 }19 if (two instanceof Object){20 System.out.println("我是Object对象");21 22 }23 /*输出:24 * 小猫吃东西!!!25 小猫跑步~~~26 我是猫对象27 我是Animal对象28 我是Object对象29 30 */31 32
4.类型转换总结:
注意:父类的静态方法不能在子类中被重写,可以同名(但是不构成重写,是子类特有的静态方法)
5.instanceof使用例子:
1 package com.swpu.plo; 2 3 public class Master { 4 /** 5 * 猫:吃完东西就跑 6 * 狗:吃完东西就睡觉 7 * 如果有很多动物,则需要一直重载方法 8 */ 9 //方案一:编写方法,传入不同类型得动物,调用各自的方法10 // public void feed(Cat cat){11 // 12 // cat.eat();13 // cat.run();14 // }15 // public void feed(Dog dog){16 // 17 // dog.eat();18 // dog.sleep();19 // }20 /**21 * 使用instanceof22 */23 //方案二:编写方法传入动物的父类,方法中通过类型的转换,调用指定子类的方法24 //视情况用不同的方案25 public void feed(Animal obj){26 obj.eat();27 if(obj instanceof Cat){28 Cat tmp=(Cat)obj;29 //tmp.eat();30 tmp.run();31 }32 else if(obj instanceof Dog){33 Dog tmp=(Dog)obj;34 //tmp.eat();35 tmp.sleep();36 }37 }38 39 /**40 * 饲养何种动物41 * 时间多:养狗42 * 时间少:养猫43 */44 //方案一45 public Dog hasManytime(){46 System.out.println("时间多,养狗");47 return new Dog();48 }49 public Cat hasLittletime(){50 System.out.println("时间少,养猫");51 return new Cat();52 }53 //方案二54 public Animal raise(boolean haManyTime){55 if(haManyTime){56 System.out.println("时间多,养狗");57 return new Dog();58 }59 else{60 System.out.println("时间少,养猫");61 return new Cat();62 }63 }64 }
1 package com.swpu.test; 2 3 import com.swpu.plo.Animal; 4 import com.swpu.plo.Cat; 5 import com.swpu.plo.Dog; 6 import com.swpu.plo.Master; 7 8 public class MasterTest { 9 10 public static void main(String[] args) {11 Cat cat=new Cat();12 Dog dog=new Dog();13 Master master=new Master();14 master.feed(cat);15 master.feed(dog);16 System.out.println("**************");17 boolean hasManyTime=true;18 // if(hasManyTime){19 // master.hasManytime();20 // }21 // else{22 // master.hasLittletime();23 // }24 Animal tmp=master.raise(hasManyTime);25 26 27 28 }29 30 }
三.抽象类和抽象方法
1.抽象类:加关键字abstract
不允许实例化,可以通过向上转型指向子类实例。
注:Python中使用abc模块实现抽象基类和抽象方法,但是抽象基类仍然可以是实例化,但抽象方法必须被重写。
无法调用抽象类Animal(如果要用,可以向上转型,Animal one=new Cat();)
2.抽象方法:
不允许包含方法体且抽象方法需要在抽象类中,子类当中需要重写父类的方法,否则子类也是抽象类。
3.总结:
abstract定义抽象类;
抽象类不能直接实例化,只能被继承,可以通过向上转型完成对象实例;
abstract定义抽象方法,不需要具体实现;
包含抽象方法的类是抽象类;
抽象类中可以没有抽象方法;
static(不允许子类重写),final(最终得,不能改变),private(私有的,只能当前类使用)不能和abstract并存。
四.接口
如电脑,手机,只能表都有照相的功能,如果分别写,则需要给每个类定义照相的方法。
1.定义接口并测试:
建立关系,把接口引用指向实现类的方式,从而完成不同的功能
1.1接口定义:
1 package com.swpu.photos;2 //定义接口3 public interface Iphoto {4 //具有照相功能5 public void photo();6 7 }
1.2重写接口方法:
1 package com.swpu.photos; 2 3 public class Computer implements Iphoto { 4 public void game() { 5 System.out.println("电脑可以玩游戏"); 6 7 } 8 9 @Override10 public void photo() {11 System.out.println("电脑也可以照相");12 13 }14 }
1 package com.swpu.photos; 2 3 public class Phone implements Iphoto{ 4 public void phone(){ 5 System.out.println("手机可以打电话"); 6 } 7 @Override 8 public void photo() { 9 System.out.println("手机可以照相");10 11 }12 }
1 package com.swpu.photos; 2 3 public class Watch implements Iphoto{ 4 public void times() { 5 System.out.println("智能手表可以看时间"); 6 7 } 8 @Override 9 public void photo() {10 System.out.println("智能手表也可以照相");11 12 }13 14 }
1.3测试:
package com.swpu.test;import com.swpu.photos.Computer;import com.swpu.photos.Iphoto;import com.swpu.photos.Phone;import com.swpu.photos.Watch;public class Test1 { public static void main(String[] args){ Iphoto ip=new Phone(); ip.photo(); ip =new Computer(); ip.photo(); ip=new Watch(); ip.photo(); } }/** * 输出: * 手机可以照相 电脑也可以照相 智能手表也可以照相**/
2.接口成员:
2.1接口规则:
接口定义了某一批类所需要遵守的规范;
接口不关心这些类的内部数据,也不关心这些类里的方法的实现细节,它只规定这些类里必须提供什么方法。
注:
接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的;
接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】;
当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去;
接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP);
如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量
1 package com.swpu.photos; 2 3 //接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的 4 public interface INet { 5 // 接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】 6 // 当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去 7 public void net(); 8 public void connection(); 9 10 // 接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP)11 // 如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量12 int TMP = 20;13 }
3.默认方法和静态方法:
3.1默认方法:
default:默认方法 可以带方法体 JDK1.8后新增;
可以在实现类中重写,并可以通过接口的引用调用。3.2静态方法:
static:静态方法 可以带方法体 JDK1.8后新增;
不可以在实现类中重写,可以使用接口名调用。3.3例:
1 package com.swpu.photos; 2 3 //接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的 4 public interface INet { 5 // 接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】 6 // 当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去 7 public void net(); 8 9 // 接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP)10 // 如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量11 int TMP = 20;12 //default:默认方法 可以带方法体 JDK1.8后新增13 //可以在实现类中重写,并可以通过接口的引用调用14 default void connection(){15 System.out.println("我是接口中的默认方法");16 }17 //static:静态方法 可以带方法体 JDK1.8后新增18 //不可以在实现类中重写,可以使用接口名调用19 static void stop(){20 System.out.println("我是接口中的静态方法");21 }22 }
1 package com.swpu.photos; 2 3 public class Phone implements INet{ 4 public void phone(){ 5 System.out.println("手机可以打电话"); 6 } 7 @Override 8 public void net() { 9 System.out.println("手机能上网");10 }11 @Override12 public void connection() {13 INet.super.connection();//调用接口中默认方法14 }15 16 17 }
3.4处理多接口同名的默认方法:
需要在子类中重写同名方法。
1 package com.swpu.photos; 2 3 //接口最好以I开头【规范】,接口访问修饰符只能是public或者是默认的 4 public interface INet { 5 // 接口当中抽象方法可以不写abstract关键字,修饰符可以不写public【默认也会使用public】 6 // 当类实现接口时,需要去实现接口中的所有抽象方法,否则可以将类定义为抽象类不重写,继续延续下去 7 public void net(); 8 9 // 接口中可以定义常量 ,默认public static final,可以用接口名(引用)加常量直接访问(INet.TMP)10 // 如果实现类和接口中的有同名的常量,如果是接口引用指向实现类,调用的是接口的常量,如果是实现类引用指向实现类实例,则是实现类中的常量11 int TMP = 20;12 //default:默认方法 可以带方法体 JDK1.8后新增13 //可以在实现类中重写,并可以通过接口的引用调用14 default void connection(){15 System.out.println("我是INet接口中的默认方法");16 }17 //static:静态方法 可以带方法体 JDK1.8后新增18 //不可以在实现类中重写,可以使用接口名调用19 static void stop(){20 System.out.println("我是接口中的静态方法");21 }22 }
1 package com.swpu.photos; 2 //定义接口 3 public interface Iphoto { 4 //具有照相功能 5 public void photo(); 6 default void connection(){ 7 System.out.println("我是Iphoto接口中的默认方法"); 8 } 9 10 11 }
1 package com.swpu.photos; 2 3 public class Phone implements INet,Iphoto{ 4 public void phone(){ 5 System.out.println("手机可以打电话"); 6 } 7 @Override 8 public void net() { 9 System.out.println("我是INet接口中的方法,手机能上网");10 }11 @Override12 public void connection() {13 System.out.println("我自己的collection方法 ");14 }15 @Override16 public void photo() {17 System.out.println("我是Iphoto接口中的方法,我可以照相");18 19 }20 21 22 }
3.5注:
可以继承加实现接口(注继承要在实现类前,如public class Phone extends Tel implements IPhoto,INet(){},只能继承一个类,当可以实现多个接口)
如果继承的类里面和实现的接口里面有同名的方法,默认指向父类当中的方法。
3.6处理多接口同名的常量:
需要明确表明是哪个接口中的常量。
1 package com.swpu.test; 2 interface one{ 3 static int x=10; 4 } 5 interface two{ 6 static int x=20; 7 } 8 9 public class Test2 implements one,two{10 public void test(){11 System.out.println(one.x);12 System.out.println(two.x);13 }14 public static void main(String[] args){15 new Test2().test();16 }17 18 }
如果继承的类里面和实现的接口有同名的常量:无法像同名方法那样默认为父类,需要自己在实现类中从新定义常量才不会报错。
4.接口的继承:
接口的继承可以继承多个接口,如果有重名的默认方法,需要在子接口中自定义。
1 package com.swpu.photos;2 3 public interface ISon extends IFather1,IFather2{4 void fly();5 void run();6 }
五.内部类
1.含义:
在Java中,可以将一个类定义在另一个类里面或者方法里面,这样的类被称为内部类。与之对应,包含内部类的类被称为外部类。
2.作用:
内部类隐藏在外部类之内,更好的实现消息隐藏。
3.内部类分类:
成员内部类;静态内部类;方法内部类;匿名内部类
3.1成员内部类:
内部类中最常见的,也成为普通内部类。
1 package com.swpu.people; 2 //外部类 3 public class Person { 4 int age; 5 public Heart getheart(){ 6 7 return new Heart(); 8 } 9 public void eat(){10 System.out.println("吃东西");11 }12 //成员内部类13 /*14 * 1.内部类在外部使用时,无法直接实例化,需要借由外部信息才能完成实例化;15 * 2.内部类的访问修饰符可以任意。但是访问范围会受到影响;16 * 3.内部类可以直接访问外部类的成员;如果出现同名的属性,优先访问内部类定义的;17 * 4.可以通过外部类.this.成员的方式,访问外部类中同名的信息;18 * 5.外部类访问内部类信息,需要通过内部类实例,无法直接访问;19 * 6.内部类编译后.class文件命名:外部类$内部类.class20 */21 class Heart{22 public void eat(){23 System.out.println("我是内部类里的吃东西");24 }25 public String beat(){26 //调用外部类的eat方法27 Person.this.eat();28 return age+"heart is running";29 }30 }31 32 }
1 package com.swpu.people; 2 3 public class PeopleTest { 4 5 public static void main(String[] args){ 6 Person p1=new Person(); 7 p1.age=15; 8 /** 9 * 获取内部类对象实例:10 * 方式一:new 外部类.new 内部类11 * 12 */13 Person.Heart heart=new Person().new Heart();14 System.out.println(heart.beat());15 //方式二:外部类对象.new 内部类16 heart =p1.new Heart(); 17 System.out.println(heart.beat());18 //方式三:外部类对象.获取方法19 heart =p1.getheart();20 System.out.println(heart.beat());21 22 }23 }
3.2静态内部类:
静态内部类对象可以不依赖于外部类对象直接创建。
1 package com.swpu.people; 2 //外部类 3 public class Person { 4 int age; 5 public Heart getheart(){ 6 7 return new Heart(); 8 } 9 public void eat(){10 System.out.println("吃东西");11 }12 /*13 * 静态内部类:14 * 1.静态内部类中,只能直接访问外部类的静态方法,如果需要调用非静态方法可以通过对象实例调用15 */16 static class Heart{17 static void say(){18 System.out.println("Hello");19 }20 public String beat(){21 //调用外部类的eat方法22 new Person().eat();23 return new Person().age+"heart is running";24 }25 }26 27 }
package com.swpu.people;public class PeopleTest { public static void main(String[] args){ Person p1=new Person(); p1.age=15; /* * 获取静态内部类实例的方法: * 1.静态内部类中,只能直接访问外部类的静态成员,如果需要调用非静态成员,可以通过对象实例; * 2.静态内部类对象实例时,可以不依赖于外部类对象; * 3.可以通过外部类.内部类.静态成员的方式,访问内部类的静态成员; * 4.当内部类属性与外部类属性同名时,默认直接调用内部类中的成员; * 如果需要访问外部类中的静态属性,则可以通过 外部类.属性 的方式 * 如果需要访问外部类中的非静态属性,则可以通过 new 外部类().属性 的方式 * */ Person.Heart heart=new Person.Heart(); System.out.println(heart.beat()); Person.Heart.say(); }}
3.3方法内部类:
定义在外部类方法中的内部类,也称局部内部类。
1 package com.swpu.people; 2 //外部类 3 public class Person { 4 int age; 5 public void eat(){ 6 System.out.println("吃东西"); 7 } 8 9 public Object getheart(){10 /*11 * 方法内部类:12 * 1.定义在方法内部,作用范围也在方法内;13 * 2.和方法内部成员使用规则一样,class前面不能加public,private,protected,static;14 * 3.类中不能包含静态成员’15 * 4.类中可以包含final,abstract修饰的成员(不推荐)16 * 17 */18 class Heart{19 public void say(){20 System.out.println("Hello");21 }22 public String beat(){23 //调用外部类的eat方法24 new Person().eat();25 return new Person().age+"heart is running";26 }27 }28 return new Heart().beat();29 30 }31 32 }
1 package com.swpu.people; 2 3 public class PeopleTest { 4 5 public static void main(String[] args){ 6 Person p1=new Person(); 7 p1.age=15; 8 /* 9 * 获取方法内部类10 */11 System.out.println(p1.getheart());12 }13 }
3.4匿名内部类:
没有名字,将类的定义和创建一起完成(如果只用一次可以这样,对系统内存消耗相对较小)
1 package com.swpu.test; 2 3 import com.swpu.people.Man; 4 import com.swpu.people.Person; 5 import com.swpu.people.Woman; 6 7 public class PersonTest { 8 //方案一 9 /*10 public void getRead(Man man){11 man.read();12 }13 public void getRead(Woman woman){14 woman.read();15 }16 */17 //方案二18 public void getRead(Person p){19 p.read();20 }21 public static void main(String[] args) {22 PersonTest p1=new PersonTest();23 // TODO Auto-generated method stub24 /*25 Man man=new Man();26 Woman woman =new Woman();27 p1.getRead(man);28 p1.getRead(woman);29 */30 //匿名内部类31 /*32 * 1.匿名内部类没有类型名称,实例对象名称;33 * 2.编译后的文件命名:外部类$数字.class;34 * 3.无法使用private/public/protected/adstract/static等;35 * 4.无法在匿名内部类编写构造方法,可以添加构造代码块;36 * 5.不能出现静态成员37 * 6.匿名内部类可以继承父类,也可以实现接口(但是不能同时实现)38 */39 p1.getRead(new Person(){40 @Override41 public void read() {42 // TODO Auto-generated method stub43 System.out.println("我是匿名内部类");44 45 }46 });47 48 49 }50 51 }