【面试题集】Java如何实现多重继承及Spring内部类继承实例

概念

品茗IT-面试题集-首发

如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。

多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,C++是允许多继承的,可以加上作用域来访问相应的父类变量和函数;然而我们知道Java为了保证数据安全,它只允许单继承。

一般情况下,我们是不需要使用多重继承的,如果必须使用,就要先考虑下你的代码设计适合合理;但是也不排除它的使用场景,Spring的代码中就有很多多重继承的使用场景。

这里讲述下Java提供了两种实现多重继承的方式:接口和内部类。同时,最后讲述下Spring中内部类继承的实现及如何从Spring当前的HttpServletRequest获取tomcat真实的Request。

接口实现

子类只能继承一个父类,但是却可以实现多个接口,事实上并不算多继承:

下面是使用接口实现多继承实例,假设有两个client:httpclient和tcpclient,想实现一个通用得客户端,根据类型选择http还是tcp:

public interface HttpClient{
    void doHttp();
}
public interface TcpClient{
    void doTcp();
}
public class BaseHttpClient implements HttpClient {
    @Override
    public void doHttp() {
        System.out.println("我是 http");
    }
}
public interface NetType {
    public static final String HTTP = "HTTP";
    public static final String TCP = "TCP";
    
    void choseType(String type);
}
public class NetworkClientTask extends BaseHttpClient implements TcpClient, NetType {
    private String curNetType;

    public void doTask() {
        if (HTTP.equalsIgnoreCase(curNetType)) {
            super.doHttp();
        } else {
            this.doTcp();
        }
    }

    @Override
    public void doTcp() {
        System.out.println("我得自己实现tcp");
    }

    @Override
    public void choseType(String type) {
        this.curNetType = type;
    }
}

上面的代码里,NetworkClientTask 继承了BaseHttpClient ,可以直接使用BaseHttpClient 的方法,但是要自己实现TcpClient的方法。

内部类实现

因为上面的接口方式需要自己实现TcpClient的方法,很不方便,我们可以在上面的基础上加点东西:

public class BaseTcpClient implements TcpClient {
    @Override
    public void doTcp() {
        System.out.println("我是 tcp");
    }
}
public class NetworkClientTask extends BaseHttpClient implements NetType {
    private String curNetType;

    public void doTask() {
        if (HTTP.equalsIgnoreCase(curNetType)) {
            super.doHttp();
        } else {
            new MyTcpClient().doTcp();
        }
    }

    class MyTcpClient extends BaseTcpClient {

    }

    @Override
    public void choseType(String type) {
        this.curNetType = type;
    }
}

上面的代码里,增加了一个实现TcpClient的类BaseTcpClient,同时,在NetworkClientTask类中,我们可以不再实现TcpClient接口,而是让内部类MyTcpClient 继承BaseTcpClient,这样就可以通过调用MyTcpClient来实现调用BaseTcpClient的方法(有人会问,为什么不直接调BaseTcpClient?这个地方只是举例而已,事实上,MyTcpClient可以对BaseTcpClient做装饰,并供外部使用,这样就隐藏了细节,Spring就是这样做的)。

下面简单讲解下Spring中对内部类继承的使用。

Spring内部类继承实例

在Spring的Controller中,我们可以通过将HttpServletRequest作为参数,来获取当然的Request对象,但是这个Request对象距离它的实现(比如tomcat对Request的封装)到底有多远?

下面图是Spring的HttpServletRequest

在这里插入图片描述

Spring当前的HttpServletRequestSecurityContextHolderAwareRequestWrapper类,但是SecurityContextHolderAwareRequestWrapper类拿的是HttpSessionSecurityContextRepository的内部类Servlet3SaveToSessionRequestWrapper的对象,HttpSessionSecurityContextRepository和Request是没有关系的,但是它有个Request相关的内部类,内部类是这样的:

private static class Servlet3SaveToSessionRequestWrapper extends
            HttpServletRequestWrapper {
    private final SaveContextOnUpdateOrErrorResponseWrapper response;

    public Servlet3SaveToSessionRequestWrapper(HttpServletRequest request,
            SaveContextOnUpdateOrErrorResponseWrapper response) {
        super(request);
        this.response = response;
    }

    @Override
    public AsyncContext startAsync() {
        response.disableSaveOnResponseCommitted();
        return super.startAsync();
    }

    @Override
    public AsyncContext startAsync(ServletRequest servletRequest,
            ServletResponse servletResponse) throws IllegalStateException {
        response.disableSaveOnResponseCommitted();
        return super.startAsync(servletRequest, servletResponse);
    }
}

内部类实现了HttpServletRequestWrapper,继而实现了HttpServletRequest接口。但是这个内部类是private static类型的,是这样被开放出去的:

public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
    ···code····

    if (isServlet3) {
        requestResponseHolder.setRequest(new Servlet3SaveToSessionRequestWrapper(
                request, wrappedResponse));
    }

    return context;
}

Spring的HttpServletRequest中获取tomcat真实的Request

Spring通过这种方式,将tomcat的Request包裹的死死的,但是并不是不能拿到,通过多次反射还是可以拿到的。

下面介绍下如何通过Spring当前的HttpServletRequest获取tomcat真实的Request,继而获取session个数:

/**
 * 当前在线人数
 * @param request
 * @return
 * @throws Exception
 */
@RequestMapping(value = "/test", method = { RequestMethod.GET })
public ResultModel num(HttpServletRequest request) throws Exception {
    try {
        int activeSessions = getActiveSessions(request);
        if (activeSessions == -1) {
            while(true){
                Field requestFieldTop = getRequsest(request);
                if(requestFieldTop == null)break;
                requestFieldTop.setAccessible(true);
                request = (HttpServletRequest) requestFieldTop.get(request);
                activeSessions = getActiveSessions(request);
                if(activeSessions != -1){
                    break;
                }
            }

        }
        return ResultModel.ok(activeSessions);
    } catch (Exception e) {
        return ResultModel.ok(10);
    }
}

private int getActiveSessions(HttpServletRequest request) throws Exception {
    if (request instanceof RequestFacade) {
        Field requestField = request.getClass().getDeclaredField("request");
        requestField.setAccessible(true);
        Request req = (Request) requestField.get(request);
        Context context = req.getContext();
        Manager manager = context.getManager();
        int activeSessions = manager.getActiveSessions();
        return activeSessions;
    }
    return -1;
}

private Field getRequsest(HttpServletRequest request) {
    Class<?> className = request.getClass();
    for (; className != Object.class; className = className.getSuperclass()) {// 获取本身和父级对象
        try {
            Field requestFieldtmp = className.getDeclaredField("request");
            if (requestFieldtmp != null) {
                return requestFieldtmp;
            }
        } catch (Exception e) {
            continue;
        }
    }
    return null;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容