浅谈Java中向上造型向下造型和接口回调中的问题
作者:The best are water 发布时间:2023-11-09 13:51:46
最近回顾了一下java继承中的问题,下面贴代码:
public class Base {
protected String temp = "base";
public void fun(){
System.out.print("BASE fun()");
}
public static void main(String[] args) {
Base b =new Base();//实例化Base对象
b.fun(); //调用父类中fun()的方法
System.out.println(b.temp);//访问父类中的成员变量temp
/*****************************************************/
System.out.println("/***************/");
Son son = new Son();//实例化Son对象
son.fun(); //调用son的fun方法
System.out.println(son.temp);//访问son的成员变量temp
Son S_son=new Son(); //实例化Son对象
Base B_s =(Base)S_son; //向上造型-----------相当于Base s =new Son();
B_s.fun(); //调用子类的fun()方法
System.out.println(B_s.temp);//访问父类的成员变量temp
/******************************************************/
System.out.println("/***************/");
Base s =new Son();//向上造型
s.fun(); //调用子类的方法
System.out.println(s.temp);//调用父类的成员变量temp
Base ba =new Son();
Son so = (Son)ba; //向下造型
so.fun(); //调用子类的fun()方法
System.out.println(so.temp);//访问子类的成员变量temp
}
}
class Son extends Base{
protected String temp = "Son";
public void fun(){
System.out.print("SON fun()");
}
}
运行结果:
BASE fun()base
/***************/
SON fun()Son
SON fun()base
/***************/
SON fun()base
SON fun()Son
结论总结:
1、父类的引用变量可以指向子类对象,子类的引用变量不能指向父类对象。
2、向上造型(upcasting):把子类对象直接赋给父类引用,向上转型不用强制转型。如:
Base base = new Son(); 向上造型–隐式
3、向下造型(downcasting):把指向子类对象的父类引用赋给子类引用,要强制转型。
Base b= new Son();
Son s = (Son)base;----------向下造型–必须强制转换,且必须有继承关系
4、upcasting 会丢失子类特有的方法,但是子类overriding 父类的方法,子类对象去 调用方法有效
5、调用方法或成员变量的规律:
前提:子类存在重写方法,父类和子类中同时有相同名称的成员变量。
(1)当进行了向上造型的引用型变量—父类引用变量只能调用子类的重写方法,但父类的引用变量只能访问父类中的成员变量
(2)当进行了向下造型的引用型变量—子类引用变量只调用子类重写方法,子类的引用变量只能访问子类的成员变量。
(3)自己想了个通俗的说法,调用方法,得当前的引用变量指向的对象;调用变量,得看当前引用变量的类型。
即,调方法,看对象,调变量,看类型。(可以试验一下)
接口回调与向上造型
/**
* 接口回调,和向上造型的对比
* @author Character_Painter
*
*/
public class Inter extends Father implements Person,Teacher{
public void study(){
System.out.println("学习");
}
public void sleep(){
System.out.println("子类sleep()方法");
}
public static void main(String[] args) {
Father f = new Inter();
f.sleep();//向上转型---调用
Person p=new Inter();
p.eat(); //接口回调方法
Teacher t = new Inter();
t.teach(); //接口回调方法
}
@Override
public void eat() {
System.out.println("重写eat()方法");
}
@Override
public void teach() {
System.out.println("重写teache()方法");
}
}
interface Person{
public void eat();
}
interface Teacher{
public void teach();
}
class Father{
public void sleep(){
System.out.println("父类sleep()方法");
}
}
运行结果
子类sleep()方法
重写eat()方法
重写teache()方法
总结:
使用接口方式,其目的应该是实现多个父类型的方法实现,强调一个多种实现,而向上造型,针对的是一对一的继承关系,向上造型后,可以调用子类的重写的方法。这就是我认为他们的区别。
补充知识:Java向下转型的意义与塑型的三个方面
一开始学习 Java 时不重视向下转型。一直搞不清楚向下转型的意义和用途,不清楚其实就是不会,那开发的过程肯定也想不到用向下转型。
其实向上转型和向下转型都是很重要的,可能我们平时见向上转型多一点,向上转型也比较好理解。
但是向下转型,会不会觉得很傻,我是要用子类实例对象,先是生成子类实例赋值给父类引用,在将父类引用向下强转给子类引用,这不是多此一举吗?我不向上转型也不向下转型,直接用子类实例就行了。
我开始学习Java时也是这么想的,这误区导致我觉得向下转型就是没用的。
随着技术的提升,我在看开源的项目学习,发现很多地方都用了向下转型的技术,这就让我重视了起来,想要重新来复习(学习)这个知识点。也是搜索了许多博客文章,但都没具体说明向下转型,只是给了例子演示怎么使用,反而是向上转型讲了一堆(可能是我没找到)。
这篇博客就是讲向下转型的,那我们就来学习下向下转型,了解下这种特性的意义和使用场景
新建一个电子产品接口,如下:
public interface Electronics{
}
很简单,什么方法都没有。
新建一个Thinkpad笔记本类,并实现电子产品接口:
public class Thinkpad implements Electronics{
//Thinkpad引导方法
public void boot(){
System.out.println("welcome,I am Thinkpad");
}
//使用Thinkpad编程
public void program(){
System.out.println("using Thinkpad program");
}
}
新建一个Mouse鼠标类,并实现电子产品接口:
public class Mouse implements Electronics{
//鼠标移动
public void move(){
System.out.println("move the mouse");
}
//鼠标点击
public void onClick(){
System.out.println("a click of the mouse");
}
}
新建一个Keyboard键盘类,并实现电子产品接口:
public class Keyboard implements Electronics{
//使用键盘输入
public void input(){
System.out.println("using Keyboard input");
}
}
这里子类比较多,是为了更好的理解。每个类的方法的逻辑实现也很简单。打印了一行信息
接下来,我们想象一个情景:我们去商城买电子产品,电子产品很多吧,比如笔记本电脑,鼠标,键盘,步步高点读机哪里不会点哪里,我们用的手机,等等,这些都属于电子产品。电子产品是抽象的。好,那么我们决定买一台Thinkpad,一个鼠标和一个键盘。
这时,我们需要一个购物车来装这些电子产品吧。我们可以添加进购物车,然后通过购物车还能知道存放的电子产品数量,能拿到对应的电子产品。
那么,一个购物车类就出来了,如下:
import java.util.ArrayList;
import java.util.List;
public class ShopCar{
private List<Electronics> mlist = new ArrayList<Electronics>();
public void add(Electronics electronics){
mlist.add(electronics);
}
public int getSize(){
return mlist.size();
}
public Electronics getListItem(int position){
return mlist.get(position);
}
}
List 集合是用来存放电子产品的,add 方法用来添加电子产品到购物车,getSize 方法用来获取存放的电子产品数量,getListItem 方法用来获取相应的电子产品。
可以看到 List<Electronics> 用了泛型的知识,至于为什么要用泛型?这个不做介绍了,泛型很重要的。
而我觉得比较疑惑的是为什么是放 Electronics 的泛型,而不是放Thinkpad,Mouse,Keyboard,Phone等?
那么如果是List<Thinkpad>,肯定是放不进鼠标Mouse的吧,难道要生成3个集合?这里是定义了3个电子产品类,但是我如果有100种电子产品呢,要定义100个集合?
这太可怕了。所以之前,我们写了一个Electronics接口,提供了一个Electronics的标准,然后让每一个Electronics子类都去实现这个接口。
实际上这里又涉及到了向上转型的知识点,我们虽然在add 方法将子类实例传了进来存放,但子类实例在传进去的过程中也进行了向上转型
所以,此时购物车里存放的子类实例对象,由于向上转型成Electronics,已经丢失了子类独有的方法,以上述例子来分析,Thinkpad实例就是丢失了boot() 和program() 这两个方法,而Mouse实例就是丢失了move()和onClick()这两个方法
但是实际使用Thinkpad或Mouse或Keyboard时,这种情况肯定不是我们想要的
接着我们写一个测试类 Test 去测试购物车里的电子产品。
测试类 Test 如下:
public class Test{
public static final int THINKPAD = 0;
public static final int MOUSE = 1;
public static final int KEYBOARD = 2;
public static void main(String[] args){
//添加进购物车
ShopCar shopcar = new ShopCar();
shopcar.add(new Thinkpad());
shopcar.add(new Mouse());
shopcar.add(new Keyboard());
//获取大小
System.out.println("购物车存放的电子产品数量为 ——> "+shopcar.getSize());
//开始测试thinkpad电脑
Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD);
thinkpad.boot();
thinkpad.program();
System.out.println("-------------------");
//开始测试Mouse鼠标
Mouse mouse = (Mouse)shopcar.getListItem(MOUSE);
mouse.move();
mouse.onClick();
System.out.println("-------------------");
//开始测试Keyboard键盘
Keyboard keyboard = (Keyboard)shopcar.getListItem(KEYBOARD);
keyboard.input();
}
}
运行截图:
举个例子分析就好
//开始测试thinkpad电脑
Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD);
thinkpad.boot();
thinkpad.program();
shopcar.getListItem(THINKPAD)
这句代码是获取到Electronics类型的实例。不是Thinkpad的实例
通过向下转型,赋值给子类引用
Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD);
这样子类实例又重新获得了因为向上转型而丢失的方法(boot 和program)
总结一下吧,很多时候,我们需要把很多种类的实例对象,全部扔到一个集合。(这句话很重要)
在这个例子里就是把Thinkpad笔记本,Mouse鼠标,KeyBoard键盘等实例对象,全部扔到一个Shopcar购物车集合。
但是肯定不可能给他们每个种类都用一个独立的集合去存放吧,这个时候我们应该寻找到一个标准,接口就是一个标准。这些都是各种电子产品,抽象成电子产品。然后一个Electronics接口就出来了。
在回到刚才,我们把很多种类的实例对象全部扔到一个集合。或许这样比较好理解:把很多种类的子类实例对象全部扔到存放父类实例的集合。
经过了这个过程,子类实例已经赋值给了父类引用(即完成了向上转型),但很遗憾的丢失了子类扩展的方法。
很好的是Java语言有个向下转型的特性,让我们可以重新获得丢失的方法,即强转回子类
所以我们需要用到子类实例的时候,就从那个父类集合里拿出来向下转型就可以了,一样可以使用子类实例对象
……
我在搜索java向下转型的意义时,得到一个比较好的答案是这样的:
最大的用处是java的泛型编程,用处很大,Java的集合类都是这样的。
而在Android开发中,我们在Layout文件夹,用xml写的控件。为什么能在Activity等组件中通过 findViewById() 方法找到呢?为什么 findViewById(R.id.textview) 方法传入TextView的id后,还要转型为TextView呢?这就是 Java 向下转型的一个应用
来源:https://blog.csdn.net/kaixuansui/article/details/91544262


猜你喜欢
- 最近比较空闲没有项目做,于是乎捋了捋平时工作会遇到的一些常见问题,首先想到了多用户登录限制问题,下面就对此问题做一点思考讲解。
- Java 利用poi把数据库中数据导入Excel效果:使用时先把poi包导入工程的path,注意只需要导入poi包即可,下载后有三个jar包
- DatagramSocket只允许数据报发送给指定的目标地址,而MulticastSocket可以将数据报以广播的方式发送至多个客户端。其主
- 前言:如果简单地拍照片并非您应用的主要目标,那么您可能希望从相机应用中获取图片并对该图片执行一些操作。一、这就是第一种方法,比较简单,不用将
- Hutool Java工具类库_ExcelUtil依赖<!--Hutool Java工具包--> &l
- 大家可能在做app的时候,或多或少需要使用联系人,而根据google提供的api,你需要编写大量的代码,例如首先需要查询数据库,涉及到数据库
- 本文实例讲述了Android编程实现加载等待ProgressDialog的方法。分享给大家供大家参考,具体如下:显示progressDial
- ReentrantLock内部由Sync类实例实现。Sync类定义于ReentrantLock内部。Sync继承于AbstractQueue
- 微信转账输入框规则(可能不全)1、小数点后两位2、起始输入小数点,显示0.3、删除到第一个位置是小数点的时候,第一个位置为0 ,避免出现小数
- 1.spring boot * 默认有:HandlerInterceptorAdapterAbstractHandlerMappingUse
- 本文演示android中图片加载到内存首先设计界面:代码如下:<LinearLayout xmlns:android="ht
- 一. 首先Swagger是什么?Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
- 最近公司有一个公交项目,要生成报站语音,采用的是 报站前缀 + 站点名 + 报站后缀,3个MP3文件拼接的方式,拼接成一个完整的语音,且需要
- 流,就是一系列的数据。当不同介质之间有数据交互的时候,JAVA就使用流来实现。数据源可以是文件,还可以是数据库、网络甚至其他的程序。比如读取
- Java Spring Controller 获取请求参数的几种方法 1、直接把表单的参数写在Controller相应的方法的形参
- foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,colle
- MybatisPlus分页排序查询字段带有下划线如果使用MybatisPlus的自动转驼峰命名法,分页排序查询的字段带有下划线时,会出问题。
- package com.abc.dao;import java.sql.Connection;import java.sql.DriverM
- 在java的开发中,java开发人员建议,尽量少用内部类,要把内部类提出他所处的那个类,单独生成一个类。直接来代码:package com.
- 说点废话Android开发中,TextView类的控件应该说是很常用了。一般来说我们是通过android:textSize="20