Java - 多态、内部类、包装类
一、多态
- 多态的体现格式
-
多态的访问特点
<1> 成员变量与静态方法:编译(写代码的时候)都是看左边,也就是必须父类中有才不会报错;运行也是看左边,也就是说执行的是父类中方法中的内容。
<2> 成员方法:编译看左边,用对象名去调用的时候要保证父类里有;运行的话看的是右边,也就是说 执行的是子类方法中的内容,于是呢,就达到了通过对象名调用方法的时候竟然有不同的效果,这就是我们说的 多态 !!!
-
多态的应用 - 总之就是一个中心点:将返回值类型等类型定义成父类类型,但是我实际上让它 指向子类的对象,于是就实现了多态的效果
~ 须知:Zi、Zi2 都是Fu 类的子类,并且都重写了父类的 method( ) 方法。
<1> 多态的变量
//多态的变量 同一个变量,可以指向不同的子类或实现类对象 可以减少变量的定义。 // 之前的话每个子类的对象都需要定义一个变量去接收 //Zi zi = new Zi(); //Zi2 zi2 = new Zi2(); //现在的话只需要先定义一个父类类型的变量,之后重新赋一下值,变量就会指向子类对象,编译器也不会报错。 Fu f = new Zi(); f.method();//Zi method f = new Zi2(); f.method();//Zi2 method
<2> 多态的形参
//形参多态的使用 修饰符 返回值类型 方法名(父类/接口名 变量名){变量名.方法名(); } 让一个方法,接收不同的子类对象,减少了方法的定义。 //之前要想调用子类重写的方法,只能老老实实定义一个个的方法,然后在方法里调用,如何能接受的参数也很有限,只能单一的类型 展示Zi对象 //public static void showZi(Zi zi){ // zi.method(); //} 展示Zi2对象 //public static void showZi(Zi2 zi2){ // zi2.method(); //} //现在的话,我只需要将形参设置为父类,到时候你传不同的子类对象进来我都能接收,为什么呀,因为这里就用了多态的知识,而且到时候调用方法的时候,调的也是子类对象它们各自的方法。 展示所有的Fu的子类对象 public static void showFu(Fu f) { f.method(); } Zi zi = new Zi(); showFu(zi); Zi2 zi2 = new Zi2(); showFu(zi2);
<3> 多态的返回值类型
//返回值类型多态的使用 修饰符 父类/接口名 方法名(参数) { return 子类/实现类对象; } 可以让一个方法,返回不同的子类对象,减少了方法的定义 //通过一个方法,获取不同的子类对象,我把返回值类型定义成父类的类型,那么我这个方法就可以根据传过来的子类类名,返回不同的父类类型的子类对象 public static Fu getFu(String className) { if (className.equals("Zi")) { return new Zi(); } else if (className.equals("Zi2")) { return new Zi2(); } else { return null; } } Fu f2 = getFu("Zi"); f2.method();//Zi method f2 = getFu("Zi2"); f2.method();//Zi2 method
-
多态的弊端
父类类型的子类对象没办法调用子类对象特有的方法,违背编译看左的规则,并且你是父类的类型,但是父类里压根就找不到特有的方法,如果有那就不叫特有的了。
-
解决多态的弊端 - 要用到类型转换的知识
-
类型转换的分类:
<1> 向上转型(也就是之前说的自动类型转换):父类类型 变量名 = new 子类类型() 或 子类对象
<2> 向下转型(也就是之前说的强制类型转换):子类类型 变量名 = (子类类型) 父类变量名;
-
-
如何判断 变量名 对应的对象所属的类型
<1> 现在有一个问题发生了:在一个方法中,一个父类类型的子类对象要想访问所有子类的特有方法,我们不是可以向下转型吗,如果不加判断,那么就会出现子类之间的互相转换,这是不允许的。
//初次尝试:想在一个方法中调用子类对象的特有方法,结果却发现了一个问题!! public static void main(String[] args) { Cat c = new Cat(); Dog d = new Dog(); showAnimal(c); showAnimal(d); } public static void showAnimal(Animal a) { a.eat(); //调用猫特有功能 Cat c = (Cat) a; c.catchMouse(); //调用狗特有功能 Dog d = (Dog) a;//error 错误的类型转换 //原因分析:按照代码执行顺序,猫类的对象传进了showAnimal 方法中,但是定义的形参的是父类的,没问题,用到了多态的知识;然后向下转型成功输出了猫类特有方法,也没问题;但是呢我现在也想把狗类的特有方法用一下,直接对 a向下转型,但是我忽视了一个问题,现在a 已经是纯正的猫类对象,所以我现在转型,相当于是从猫类 -> 狗类,那肯定不支持的呀,猫狗之间没有继承的关系!! d.lookHome(); }
<2> 解决办法:先判断传进来对象属于哪一个子类,然后在方法中根据它的类型向下转型成对应它原本的纯正的类型,才能分别调用它们自己特有的方法。
public static void showAnimal(Animal a) { //解决办法:变量名 instanceof 数据类型 (判断变量名对应的对象所属的类型) // 如果变量属于该数据类型,返回true。 // 如果变量不属于该数据类型,返回false。 if (a instanceof Cat) {//判断a变量对应的对象,是否是 Cat类型 //调用猫特有功能 Cat c = (Cat) a; c.catchMouse(); } else if (a instanceof Dog) { //调用狗特有功能 Dog d = (Dog) a; d.lookHome(); } }
二、内部类
- 将类B定义在类A里面,类B就称为内部类,类A则称为类B的外部类
- 内部类分为,成员内部类、匿名内部类、局部内部类
- 内部类是一个独立的类,编译后,有单独的class文件,命名为:外部类名+$+内部类类名标识
-
成员内部类
<1> 使用格式
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
<2>访问特点
-
内部类可以直接访问外部类的成员,包括私有成员。
-
外部类要访问内部类的成员,必须要建立内部类的对象。
public static void main(String[] args) { //内部类一般和外部类有内在关联。 汽车类-引擎 电脑-cpu 人-心脏 //创建心脏对象 外部类名.内部类名 对象名 = new 外部类型().new 内部类型(); //Person.Heart h = new Person().new Heart(); 这样连着写不推荐,因为不够灵活,分开写的话可以应对外部类有不同的情况,比如外部类的状态要改变,内部类要能够分开处理才会比较好。例子:人活着心才会跳,人死了心就停了,所以外部类的状态可能会有多种。 Person p = new Person(); Person.Heart h = p.new Heart(); h.jump(); System.out.println("一百年"); p.setLive(false); h.jump(); }
-
-
匿名内部类 - 本质上是一个子类的对象或者实现类的对象
<1> 之前,使用一个父类或接口中的方法需要4步
①定义子(实现)类 -> ②重写方法 -> ③创建子(实现)对象 -> ④调用方法
<2> 现在,只需要写一次
使用前提:重写父类或者接口中的方法,但是我又只用一次,可以避免多写实现类
<3> 格式
new 父类名或者接口名() { // 方法重写 };
//这是一个“飞行”接口的匿名实现类对象 new Flyable() { @Override public void fly() { System.out.println("螺旋式起飞"); } };
<4> 匿名内部类的应用 - 利用了多态的特性
package com.itheima.demo02内部类.p02匿名内部类; //使用场景 public class Test2 { public static void main(String[] args) { //通过多态的形式指向父类引用 Fu f = new Fu(){ @Override public void method(){ System.out.println("method1"); } }; f.method(); //直接调用方法 new Fu(){ @Override public void method(){ System.out.println("method2"); } }.method(); //作为方法参数(实参)传递 showFu(new Fu(){ @Override public void method(){ System.out.println("method3"); } }); } public static void showFu(Fu f){ f.method(); } } class Fu{ public void method(){} }
-
局部内部类 - 定义在方法中的类,实用性不大,不常用
三、包装类
-
介绍
• Java提供了两个类型系统,基本类型与引用类型,
基本类型效率更高,引用类型封装了很多的方法。
• 为了便于操作,java为在lang包下为基本类型创建了对应的引用类型,称为包装类。
-
对应关系 - int 与 char 有点不同
类型 byte short int long float double char boolean 包装类 Byte Short Integer Long Float Double Character Boolean -
常用方法
• public static Integer valueOf (int i) 返回 表示指定的 int 值的 Integer 实例
• public static Integer valueOf (String s) 返回 Integer 对象 s必须是 “100” 这种
• public int intValue() 返回 Integer对象的int形式
-
装箱与拆箱
• 装箱:从基本类型转换为对应的包装类对象 (构造方法/ valueOf)。
• 拆箱:从包装类对象转换为对应的基本类型 ( xxxValue )。
-
自动装箱与自动拆箱
由于这两个操作使用频率高,Java 5开始,装箱、拆箱动作可以自动完成。也就是直接写个赋值就可以转换了。
• 自动装箱:基本类型传递给包装类型
• 自动拆箱:包装类型传递给基本类型
-
基本类型与字符串的转换
<1> 基本类型转换为String
-
方式一:直接在基本类型后加一个空字符串 数据+“”
-
方式二:通过String类静态方法 valueOf (Xxx)
<2> String转换成基本类型
-
方式一:指定包装类的静态方法 valueOf (String s) 将字符串转为对应包装类
-
方式二:通过包装类的静态方法 parseXxx (String s) 将字符串转为对应基本类型
<3> 注意事项:
-
value0f 返回的是包装类型,能完成转换,是因为最后一步系统自动转换的
-
parseXxx 才是一步到位
-
String 转 char 类型只能使用String类中非静态方法 charAt (int index)
-
数据要符合对应数据的类型格式
-