疯狂Java学习笔记(012)
Since this day,I will write by my thoughts.
一、什么是多态?
- 概念,Polaymorphosim,父类类型的变量引用了实现子类的一个对象。
- 同样的变量,由于只想的子类对象不同,导致了同样的代码,执行结果不同!
二、引用数据类型的两个时期:
- 编译期类型,写在等号左侧,用来定义变量的类型
- 运行时类型,写在等号右侧,用来表示对象的类型
- 例如:Animal aa = new Dog( );
- 特点:左右不一致,有实现关系
- 父类类型的变量引用子类的对象
三、多态的要求:
- 具有继承或者实现关系
- 编译期类型和运行类型不一致,即等号左右不一致
- 必须有方法的重写或方法实现
四、多态的好处
通过配置文件、工厂模式获取一个实现子类对象,在不更改源码的情况下,更改.txt配置文件动态替换修改具体子类类名,就可以达到动态替换的目的。
/* 多态的案例,普通类多态 */ class Animal{ public void eat(){ System.out.println("animal eat"); } } class Dog extends Animal{ public void eat(){ System.out.println("dog eat"); } } class Cat extends Animal{ public void eat(){ System.out.println("cat eat"); } } public class PolymorphismDemo{ public static void main(String[] args){ //等号左右两侧的类型相同,不是多态! Animal a = new Animal(); a.eat(); //父类类型的变量引用子类的对象 Animal aa = new Dog(); aa.eat(); //此处执行的是子类重写的方法 aa = new Cat(); //aa指向了一个Cat实例对象 aa.eat(); //此处执行的是Cat类的eat方法 } }
五、抽象类多态
- 抽象类型 = new 实现子类型();
- 编译期类型是抽象类类型;
- 运行期类型是实现子类型;
- 不能再为 Animal a = new Animal( );因为是抽象类型,只能为Animal a = new Dog( ); a.eat( );
- 抽象类型调用的是子类中已经实现的方法;
/* 抽象类多态 */ abstract class Animal{ public abstract viod eat(); } class Dog extends Animal{ public void eat(){ System.out.println("dog eat"); } } class Cat extends Animal{ public void eat(){ System.out.println("cat eat"); } } public class PolyDemo2{ public static void main(String[] args){ // Dog d = new Dog(); // d.eat(); //抽象类多态 Animal a = new Dog(); a.eat(); //调用的是子类中已经实现的方法 a = new Cat(); a.eat(); //调用的是cat中的方法 } }
六、接口多态
编译期类型是接口类型,运行期是实现子类型
/* 接口多态: */ interface InterA{ public abstract void method(); } class A implements InterA{ public void method(){ System.out.println("A method"); } } class B implements InterA{ public void method(){ System.out.println("B method"); } } public class PolyDemo3{ public static void main(String[] args){ //接口多态 InterA a = new A(); a.method(); //调用的是A实现子类的method方法 // a = new B(); a.method(); //调用的是B实现子类的method方法 } }
七、普通类之间有哪些成员多态性
1. 成员变量没有多态性
例如::Father f = new Son( ); 下面调用f.age 其实还是父类自己的age,不会因为子类也有age而返回子类的值。
2. 静态方法没有多态性
例如:定义父类中静态方法,public static void test2(){System.out.println(" father ")}即使子类也有,当Father f = new Son( ); f.test2();调用的还是父类的静态成员方法。
3.父类类型的变量无法引用子类特有的方法
例如:子类特有test3( );方法,子类再继承父类已有的方法基础上,父类无法调用,编译报错。
程序执行过程:编译期,会检查父类方法的定义;编译期,看子类是否重写。
- 如果方法父类没有,子类有则编译报错;
- 如果方法父类有,子类重写的情况,执行子类。如果子类未重写情况,执行父类。
/* 演示:
普通之间,哪些成员有多态性?
方法调用:
引用数据类型的转换: */ class Father{ //成员变量 public int age = 30; //普通的成员方法 public void test(){ System.out.println("father test"); } //静态方法 public static void test2(){ System.out.println("father test2"); } } class Son extends Father{ // public int age = 10; // public void test(){ // System.out.println("son test"); // } //静态方法 public static void test2(){ System.out.println("son test2"); } //子类特殊的方法 public void test3(){ System.out.println("子类特有的方法"); } } public class PolyDemo4{ public static void main(String[] args){ Father f = new Son(); // f.test(); //调用的是子类重写之后的方法 // System.out.println(f.age); //成员变量没有多态性!! // f.test2(); //成员方法没有多态性!! // f.test3(); //类型不对 //把f变量强转成子类型 Son s = (Son)f; //引用数据类型强转的前提: //1.必须有继承关系 //2.必须真实指向的对象类型和要赋值的数据类型匹配!! s.test3(); //简写:.的优先级高于()优先级 ((Son)f).test3(); } }
八、引用数据类型转换
1. 向上转换:即子类→父类,自动转换
书写格式:Father f = new son( );
f.test3();
强制转换:Son s = (son)f;
s.test3();
也可简写为:(Son(f)).test3(),其中,“ . ”的优先级大于“()”的优先级
2.向下转换:即父类→子类,强制转换
存在前提:①存在子父类关系,即有继承关系。
②必须真实指向的对象类型和要赋值的数据类型匹配
例如:Animal a = new Dog( );
Cat c = (Dog)a;
这种情况就是错的。
书写格式:Father f = new son( );
Son s = (son)f;
s.test3();
也可简写为:(Son(f)).test3(),其中,“ . ”的优先级大于“()”的优先级。
九、内部类
概念:内部类又称内置类、嵌套类,就是在一个类的内部定义的类。
class Outer{ //成员内部类 public class Inner{ public void method(){ System.out.println("Inner class method"); } } //局部内部类 public void test(){ class Inner2{ public void method(){
//int a = 10; System.out.println("Inner2 class method");
//System.out.print(a++) } } } }
说明:代码中,Inner为成员内部类,Inner2为局部内部类(在方法内部)
1. 局部内部类(不常用)
其中,在局部内部类中如果上面代码,里面加a++,会报错,原因是局部内部类中定义的变量是final形式,不可以编号。
2. 成员内部类
可以包含成员方法、构造方法、成员变量。
class Outer{ private int age = 10; //成员位置定义内部类: public class Inner{ public void method(){ System.out.println(++age); //11 System.out.println(age); //11 System.out.println("Inner class method"); } }
调用外部类的方法,new Inner().method(); new+外部类名+内部类名
3.访问特点:①内部类可以直接访问外部类中的成员,包括私有成员;
如上面代码里,private int age = 10,Inner里就可以直接使用。
②外部类要访问内部类中的成员必须要求建立内部类的对象。如
//内部类创建对象 public void test2(){ //创建内部类对象,调用内部类的方法 new Inner().method(); }
public class InnerDemo1{ public static void main(String[] args){ //调用外部类方法, new Outer().test2(); //局部内部类调用 // new Outer().test(); } }
4.我们已知四种权限修饰符:protected\public\priviate\默认
5.运行后,文件中会多Out$Inner2.class的字节码文件,就是内部类的字节码文件。
十、非静态成员内部类和静态成员内部类
1. 非静态成员的内部类,能定义非静态成员,不能定义静态成员,但都能使用;
2.静态成员的内部类,能定义非静态和静态,但只能使用静态,不能使用非静态;
3.使用方法:
非静态:Outer.Inner il = new Outer().new Inner();
il.show();
静态: Outer.Inner il = new Outer().new Inner();
il.show();
十一、匿名内部类
1.匿名内部类适用于这个类只使用一次的情况,不适用这个内部类被多次使用的情况
2.使用匿名内部类的前提:
内部类可以继承或实现一个外部类或者接口
什么情况下,内部类只被使用一次呢?
最常见的就是方法的形参列表上
格式1:
new 接口名() {
//匿名内部类的类体:通常是实现抽象方法
}
格式2:
new 父类名(形参列表){
//匿名内部类的类体:通常是实现抽象方法
}
从以上格式可以看出:
- 匿名内部类不能是抽象类,必须实现所有抽象方法
- 匿名内部类不能有构造方法,因为它没有名字
- 内部类实现接口时,只能加(),继承一个抽象类时,父类名后可以指定抽象类的构造方法
/* 匿名内部类: */ abstract class Animal{ public abstract void eat(); } class Test{ public void test(Animal an){ an.eat(); } } /* class Dog extends Animal{ public void eat(){ System.out.println("dog test"); } } */ public class InnerDemo3{ /* //成员内部类:继承外部的抽象类 private static class Dog extends Animal{ public void eat(){ System.out.println("dog test"); } } */ public static void main(String[] args){ // Dog d = new Dog(); // new Test().test(d); //匿名内部类方式创建对象,传递给方法 new Test().test(new Animal(){ public void eat(){ System.out.println("dog eat"); } }); } }
-----------------------------------------------------------------------------------------------------------
中午练习:
/* 方法的参数是一个普通类,调用方法时临时创建一个子类对象. */ class Father{ public Father(){ System.out.println("父类的空参构造方法"); } public Father(int a){ System.out.println("父类的带参构造方法"); } public void method(){ System.out.println("father method"); } } class Test{ public void test(Father f){ f.method(); } } public class InnerDemo5{ public static void main(String[] args){ // Father f = new Father(); // new Test().test(f); //调用方法时,临时创建一个没名的子类对象! new Test().test(new Father(0){ //父类是普通类,或抽象类,可以指定父类的构造方法!! //如果是接口,什么都不能写!! //重写父类的method方法 public void method(){ System.out.println("overwrite method"); } }); } }
/* 接口类型的参数方法,调用方法时,临时生成一个此接口的实现子类的对象!!! */ interface InterA{ public abstract void method(); } class Test{ public void test(InterA a){ a.method(); } } public class InnerDemo6{ public static void main(String[] args){ new Test().test(new InterA(){ //实现接口中所有的抽象方法 public void method(){ System.out.println("实现子类的方法"); } }); } }
---------------------------------------------------------------------------------
/* 匿名内部类给接口类型变量赋值 */ interface InterA{ public abstract void show(); } public class InnerDemo7{ public static void main(String[] args){ //创建一个实现了InterA接口的实现子类对象,并赋值给接口类型的变量 InterA a = new InterA(){ //接口多态: = 左边是接口类型的变量 // = 右边是接口的实现子类的对象 // public void show(){ System.out.println("子类实现了接口的方法"); } // }; // a.show(); //此处调用的是实现子类的实现方法!!! } }