多态
类的多态
类的多态:
父类引用指向子类对象
父类引用中存储的是子类对象在堆中的地址
需求:老师骑着自行车上班
分析:老师类、自行车类需求升级/迭代:自行车->小汽车
步骤: 1.创建Car类,编写start、stop
2.在原有Teacher类的基础上编写open、close
注意:违反了OCP原则OCP - 开闭原则:
O - open :在需求升级时,对于创建类是欢迎的
C - close:在需求升级时,改变原有代码是拒绝的
注意:需求升级时,尽量不要改变以前的类,否则容易出bug
需求升级/迭代:自行车->小汽车->飞机
步骤:创建Plane类继承Vehicle
Teacher t = new Teacher(); //类的多态:父类引用指向子类对象 //父类引用中存储的是子类对象在堆中的地址 Vehicle v = new Plane(); t.open(v); System.out.println("欣赏沿途的风景..."); t.close(v);
接口的多态
接口的多态:
实现类的对象指向接口的引用
接口的引用中存放的是实现类对象在堆中开辟空间的地址
public static void main(String[] args) { //需求:鼠标、键盘连接电脑 Computer computer = new Computer(); //接口的多态:实现类的对象指向接口的引用 //接口的引用中存放的是实现类对象在堆中开辟空间的地址 IUSB usb = new Keyboard(); computer.connection(usb); } //电脑类 public class Computer { //连接usb的方法 public void connection(IUSB usb){ usb.use(); } } //USB接口类 public interface IUSB { public void use(); } //键盘类 public class Keyboard implements IUSB{ @Override public void use() { System.out.println("键盘:输入数据~~~"); } }
对象转型
向上转型
- 向上转型:子类类型 转 父类类型
- 可以调用父类非私有的属性
- 可以调用父类非私有的方法
- 可以调用子类重写父类的方法
- 不可以调用子类自身的属性和方法
- 注意:向上转型就是多态
- 多态的缺点:不可以调用子类自己的属性和方法
public class Test01 { public static void main(String[] args) { A a = new B(); System.out.println(a.aAtrr);//父类属性 a.aMethod();//父类方法 a.method();//子类重写父类的方法 } } //父类 public class A { String aAtrr = "父类属性"; public void aMethod(){ System.out.println("父类方法"); } public void method(){ System.out.println("父类方法"); } } //子类 public class B extends A{ String bAtrr = "子类属性"; public void bMethod(){ System.out.println("子类方法"); } @Override public void method(){ System.out.println("子类重写父类的方法"); } }
向下转型
- 向下转型:父类类型 转 子类类型
- 注意:
- 向下转型转不正确就会出现 - ClassCastException (类型转换异常)
- 向下转型一定要使用 instanceof 判断
public static void main(String[] args) { Animal an = new Dog();//向上转型 if(an instanceof Cat){//判断an引用指向的对象是否是Cat类型 Cat cat = (Cat) an;//向下转型 cat.shout(); }else if(an instanceof Dog){//判断an引用指向的对象是否是Dog类型 Dog dog = (Dog) an;//向下转型 dog.eat(); } } //Animal类 public class Animal { } //Cat类 public class Cat extends Animal{ public void shout(){ System.out.println("喵喵喵~~~"); } } //Dog类 public class Dog extends Animal{ public void eat(){ System.out.println("啃骨头~~~"); } }
内部类
含义:在一个类中声明的类
分类:
成员内部类 :在成员变量位置处声明的类
特点:可以调用外部类的所有属性
静态内部类
特点:只能调用外部类的静态属性
接口内部类:在接口中定义的类
特点:接口内部类底层就是静态内部类
局部内部类:在方法中定义的类
注意:该类不能用修饰符修饰
匿名内部类:
分为匿名子类和匿名实现类
成员内部类
特点:可以调用外部类的所有属性
public static void main(String[] args){ //创建成员内部类对象 Inner inner = new Outter().new Inner(); //调用方法 inner.method(); } //外部类 public class Outter { private String str1 = "aaa"; String str2 = "bbb"; protected String str3 = "ccc"; public String str4 = "ddd"; final String str5 = "eee"; static String str6 = "fff"; //成员内部类 class Inner{ private String str1 = "成员内部类的属性~~~"; public void method(){ System.out.println("成员内部类里的方法"); System.out.println(this.str1);//成员内部类里的属性 System.out.println(Outter.this.str1);//aaa:调用外部类的属性 System.out.println(str2);//Outter.this.str2 System.out.println(str3);//Outter.this.str3 System.out.println(str4);//Outter.this.str4 System.out.println(str5);//Outter.this.str5 System.out.println(str6);//Outter.str6 } } }
静态内部类
特点:只能调用外部类的静态属性
public static void main(String[] args){ //创建静态内部类对象(不用创建外部类对象) Inner inner = new Outter.Inner(); //调用方法 inner.method(); } //外部类 public class Outter { private String str1 = "aaa"; String str2 = "bbb"; protected String str3 = "ccc"; public String str4 = "ddd"; final String str5 = "eee"; static String str6 = "fff"; //静态内部类 static class Inner{ public void method(){ System.out.println("静态内部类里的方法"); //静态内部类不能调用外部类的成员变量 // System.out.println(str1); // System.out.println(str2); // System.out.println(str3); // System.out.println(str4); // System.out.println(str5); System.out.println(str6);//Outter.str6 } } }
接口内部类
特点: 接口内部类底层就是静态内部类
public static void main(String[] args) { //创建接口内部类对象 Inner inner = new Outter.Inner(); //调用方法 inner.method(); } //接口 public interface Outter { //接口内部类 默认添加public static class Inner{ public void method(){ System.out.println("接口内部类里的方法"); } } }
局部内部类
特点:该类不能用修饰符修饰
public class Test01 { public static void main(String[] args) { //创建外部类的对象 Outter outter = new Outter(); //调用方法 Object obj = outter.function(); System.out.println(obj); } }
//外部类 public class Outter { public Object function(){ //如果局部内部类中使用到该变量,从JDK1.8开始该变量自动变成常量(常量在整个项目结束后才被回收) //因为防止i在该方法结束后才使用(若i不为常量,方法结束后,变量被回收,若局部内部类访问则报错) int i = 100; //局部内部类 class Inner{ @Override public String toString() { return i+""; } } //局部内部类 class Inner{ public void method(){ System.out.println("局部内部类里的方法"); } } //创建局部内部类的对象 Inner inner = new Inner(); inner.method(); return inner; } }
匿名内部类
匿名子类:
创建匿名子类,继承A,重写method方法
创建匿名子类对象,赋值给父类的引用
A a = new A() { @Override public void method() { } };
匿名实现类:
创建匿名实现类,实现 I1中的method方法
创建匿名实现类的对象,赋值给接口的引用
I1 i1 = new I1() { @Override public void method() { } };
内部类的使用场景
应用场景1:
如果一个类的对象只在另外一个类中使用,就可以考虑把该类变成成员内部类或者静态内部类
- 如果内部类要用到外部类的所有属性就把该类变成成员内部类
- 如果内部类只用到外部类的静态属性就把该类变成静态内部类
应用场景2:
如果一个类的对象只在另外一个类的方法中使用,就可以考虑把该类变成局部内部类,一般不使用这种设计思想
应用场景3:
如果一个类的对象只在接口中使用,就可以考虑把该类变成接口内部类,一般不使用这种设计思想
应用场景4:
抽象类子类的对象或者接口实现类的对象只使用到一次,就可以考虑使用匿名内部类