美文网首页
冰解的破-J2EE

冰解的破-J2EE

作者: 大佛爱读书 | 来源:发表于2018-07-04 16:34 被阅读0次
    J2EE

    J2EE是一套全然不同于传统应用开发的技术架构,包含许多组件,主要可简化且规范应用系统的开发与部署,进而提高可移植性、安全与再用价值。

    J2EE核心是一组技术规范与指南,其中所包含的各类组件、服务架构及技术层次,均有共同的标准及规格,让各种依循J2EE架构的不同平台之间,存在良好的兼容性,解决过去企业后端使用的信息产品彼此之间无法兼容,企业内部或外部难以互通的窘境。

    J2EE组件和“标准的” Java类的不同点在于:它被装配在一个J2EE应用中,具有固定的格式并遵守J2EE规范,由J2EE服务器对其进行管理。J2EE规范是这样定义J2EE组件的:客户端应用程序和applet是运行在客户端的组件;Java Servlet和Java Server Pages (JSP) 是运行在服务器端的Web组件;Enterprise Java Bean (EJB )组件是运行在服务器端的业务组件。

    学习整理:

    • learn表有kc(课程)、student_id(学生编号)、grade(分数)等字段,求:1.每门课程分数最高的两个同学 2.每门课程分数在31~40名的同学 3.出现过4次以上的课程,并按出现次数降序排列。
    1. 主要是用到子查询,考虑同课程下分数比该学生高的人数小于2
    SELECT * 
    FROM learn a
    WHERE (SELECT COUNT(*) FROM learn b WHERE a.kc = b.kc AND a.grade < b.grade) < 2
    ORDER BY a.kc,a.grade DESC
    
    1. 与上题类似,以1的方法找30名开外和40名以内的同学。
    SELECT * 
    FROM learn a
    WHERE (SELECT COUNT(*) FROM learn b WHERE a.kc = b.kc AND a.grade < b.grade) > 30
    AND (SELECT COUNT(*) FROM learn b WHERE a.kc = b.kc AND a.grade < b.grade) < 40
    ORDER BY a.kc,a.grade DESC
    
    1. 比较简单,主要是sql中group by, having,聚合函数的使用。
    SELECT COUNT(kc) kc_count,student_id
    FROM learn a
    GROUP BY student_id
    HAVING kc_count >= 4
    ORDER BY kc_count DESC
    
    • java线程中sleep()和wait()的区别?
    1. 资源占用和释放上的不同:

    每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。

    每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。

    • sleep()方法正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!);

    • wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。

    注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度;

    1. 程序中出现的位置不同:
    • sleep()方法可以在任何地方使用;
    • wait()方法则只能在同步方法或同步块中使用;
    1. 隶属于不同的对象:
    • sleep()是线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;
    • wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;

    参见:
    Java中sleep()与wait()区别:https://blog.csdn.net/u012050154/article/details/50903326

    • java线程不建议使用suspend()和resume(),为什么?

    首先先来知道suspend和resume是干什么用的。

    • suspend将线程挂起,运行->阻塞;调用后并不释放所占用的锁
    • resume将线程解挂,阻塞->就绪

    那为什么不建议使用呢,两点原因:

    1. 独占:因为suspend在调用过程中不会释放所占用的锁,所以如果使用不当会造成对公共对象的独占,使得其他线程无法访问公共对象,严重的话造成死锁。
    2. 不同步:容易出现因线程暂停导致的数据不同步

    参见:
    suspend和resume弊端:https://blog.csdn.net/u012813201/article/details/64932172

    • j2ee标准有哪些?

    J2EE是一套全然不同于传统应用开发的技术架构,包含许多组件,主要可简化且规范应用系统的开发与部署,进而提高可移植性、安全与再用价值。

    J2EE 十三个规范:

    1. JDBC(Java Database Connectivity)

    JDBC API为访问不同数据库提供了统一的路径,像ODBC一样,JDBC开发者屏蔽了一些细节问题,另外,JDBC对数据库的访问也具有平台无关性.

    JDBC
    1. JNDI(Java Name and Directory Interface)

    JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源如DNS和LDAP,本地文件系统,或应用服务器中的对象。

    JNDI
    1. EJB(Enterprise JavaBean)

    J2EE技术之所以赢得广泛重视的原因之一就是EJB.它提供了一个框架来开发和实施分布式商务逻辑,由此很显著的简化了具有可伸缩性和高度复杂的企业级应用程序的开发.EJB规范定义了EJB组件在何时如何与它们的容器进行交互作用.容器负责提供公用的服务,例如目录服务,事务管理,安全性,资源缓冲池以及容错性.但这里值得注意的是,EJB并不是实现J2EE的唯一路径.正是由于J2EE的开放性,使得所有的厂商能够以一种和EJB平行的方式来达到同样的目地


    EJB
    1. RMI(Remote Method Invoke)

    远程方法请求,RMI协议调用远程对象上的方法.它使用了序列化的方式在客户端和服务器之间传递数据.RMI是一种被EJB使用的更底层的协议.


    RMI
    1. Java IDL/CORBA(通用对象请求代理架构是软件构建的一个标准 )
      在Java IDL的支持下,开发人员可以将Java和CORBA集成在一起。他们可以创建Java对象并使之可在CORBA ORB中展开, 或者他们还可以创建Java类并作为和其它ORB一起展开的CORBA对象的客户。后一种方法提供了另外一种途径,通过它Java可以被用于将你的新的应用和旧的系统相集成。
    2. JSP(Java Server Pages)
      JSP页面由HTML代码和嵌入其中的Java代码所组成。服务器在页面被客户端所请求以后对这些Java代码进行处理,然后将生成的HTML页面返回给客户端的浏览器。
    3. Java Servlet
      Servlet是一种小型的Java程序,它扩展了Web服务器的功能。作为一种服务器端的应用,当被请求时开始执行,这和CGI Perl脚本很相似。Servlet提供的功能大多与JSP类似,不过实现的方式不同。JSP通常是大多数HTML代码中嵌入少量的Java代码,而servlets全部由Java写成并且生成HTML。
    4. XML(Extensible Markup Language)
      XML是一种可以用来定义其它标记语言的语言。它被用来在不同的商务过程中共享数据。
      XML的发展和Java是相互独立的,但是,它和Java具有的相同目标正是平台独立性。通过将Java和XML的组合,您可以得到一个完美的具有平台独立性的解决方案。
    5. JMS(Java Message Service)
      MS是用于和面向消息的中间件相互通信的应用程序接口(API)。它既支持点对点的域,有支持发布/订阅(publish/subscribe)类型的域,并且提供对下列类型的支持:经认可的消息传递,事务型消息的传递,一致性消息和具有持久性的订阅者支持。JMS还提供了另一种方式来对您的应用与旧的后台系统相集成。
    6. JTA(Java Transaction Architecture)
      JTA定义了一种标准的API,应用系统由此可以访问各种事务监控。
    7. JTS(Java Transaction Service):
      JTS是CORBA OTS事务监控的基本的实现。JTS规定了事务管理器的实现方式。该事务管理器是在高层支持Java Transaction API (JTA)规范,并且在较底层实现OMG OTS specification的Java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信资源管理器提供了事务服务。
    8. JavaMail
      JavaMail是用于存取邮件服务器的API,它提供了一套邮件服务器的抽象类。不仅支持SMTP服务器,也支持IMAP服务器。
    9. JAF(JavaBeans Activation Framework)
      JavaMail利用JAF来处理MIME编码的邮件附件。MIME的字节流可以被转换成Java对象,或者转换自Java对象。大多数应用都可以不需要直接使用JAF。

    参见:
    百度百科(J2EE技术):https://baike.baidu.com/item/J2EE%E6%8A%80%E6%9C%AF
    J2EE的十三个标准:
    https://blog.csdn.net/cwl421/article/details/50992558

    • Data Source和Driver manager 获取connector方式的区别?

    在JDBC2.0或JDBC3.0中,所有的数据库驱动程序提供商必须提供一个实现了DataSource接口的类,要使用数据源必须首先在JNDI中注册该数据源对象。

    如果在JNDI中注册了数据源对象,将会比起使用DriverManager来具有两个方面的优势:

    1. 程序不需要像使用DriverManager一样对加载的数据库驱动程序信息进行硬编码,程序员可以选择先在JNDI中注册这个数据源对象,然后在程序中使用一个逻辑名称来引用它,JNDI会自动根据你给出的名称找到与这个名称绑定的DataSource对象。然后就可以使用这个DataSource对象来建立和具体数据库的连接了。
    2. 使用实现了DataSource接口的类所具有的第二个优势体现在连接池和分布式事务上。连接池通过对连接的复用而不是新建一个物理连接来显著地提高程序的效率。从而适用于任务繁忙、负担繁重的企业级分布式事务。

    数据库连接池的基本原理

    传统的数据库连接方式(指通过DriverManager和基本实现DataSource进行连接)中,一个数据库连接对象均对应一个物理数据库连接,数据库连接的建立以及关闭对系统而言是耗费系统资源的操作,在多层结构的应用程序环境中这种耗费资源的动作对系统的性能影响尤为明显。

    在多层结构的应用程序中通过连接池(connection pooling)技术可以使系统的性能明显得到提到,连接池意味着当应用程序需要调用一个数据库连接的时,数据库相关的接口通过返回一个通过重用数据库连接来代替重新创建一个数据库连接。

    通过这种方式,应用程序可以减少对数据库连接操作,尤其在多层环境中多个客户端可以通过共享少量的物理数据库连接来满足系统需求。

    通过连接池技术Java应用程序不仅可以提高系统性能同时也为系统提高了可测量性。

    再来对比下两种方法的使用:

    1. DriverManager
    Class.forName(driverClass);
    conn = DriverManager.getConnection(url, username, password);
    相关数据库的操作; 
    con.close(); 
    
    1. DataSource

    一个DataSource对象通常注册在JNDI命名服务上,应用程序可以通过标准的方式获得到注册在JNDI服务上的DataSource对象。 代码如下:

    Context ctx = new InitialContext(); 
    DataSource ds = (DataSource) ctx.lookup("jdbc/openbase"); 
    

    如果当前DataSource不支持数据库连接池,应用程序将获得一个和物理数据库连接对应的Connection对象。

    而如果当前的DataSource对象支持数据库连接池,应用程序自动获得重用的数据库连接而不用创建新的数据库连接。

    重用的数据库连接和新建立连接的数据库连接使用上没有任何不同。应用程序可以通过重用的连接正常的访问数据库,进行访问数据的操作,完成操作后应显式的调用close()关闭数据库连接。

    Connection con = ds.getConnection("User", "Pwd"); 
    相关数据库的操作; 
    con.close(); 
    

    当关闭数据连接后,当前使用的数据库连接将不会被物理关闭,而是放回到数据库连接池中进行重用。

    参见:
    DataSource跟DriverManager区别及联系:https://blog.csdn.net/laorer/article/details/1827007

    • servlet中dopost、doget、dodelete、service都有什么区别?

    首先servlet是单实例多线程的,然后来看看这些名词的区别。

    1. servlet接口中需要实现三大生命周期方法:init,service,destroy
    • void init(ServletConfig):出生之后(1次)

    在Servlet被创建后,服务器会马上调用Servlet的void init(ServletConfig)方法。请记住, Servlet出生后马上就会调用init()方法,而且一个Servlet的一生,这个方法只会被调用一次。我们可以把一些对Servlet的初始化工作放到init方法中!

    • void service(ServletRequest request, ServletResponse response):每次处理请求时都会被调用;

    当服务器每次接收到请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求,就会调用service() 方法一次,所以service()方法是会被调用多次的。正因为如此,所以我们才需要把处理请求的代码写到service()方法中!

    • void destroy():临死之前(1次)

    Servlet是不会轻易离去的,通常都是在服务器关闭时Servlet才会离去!在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy()方法,我们可以把Servlet的临终遗言放到destroy()方法中,例如对某些资源的释放等代码放到destroy()方法中。

    1. GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。

    在GenericServlet中,定义了一个ServletConfig config实例变量,并在init(ServletConfig)方法中把参数ServletConfig赋给了实例变量。然后在该类的很多方法中使用了实例变量config。

    如果子类覆盖了GenericServlet的init(StringConfig)方法,那么this.config=config这一条语句就会被覆盖了,也就是说GenericServlet的实例变量config的值为null,那么所有依赖config的方法都不能使用了。如果真的希望完成一些初始化操作,那么去覆盖GenericServlet提供的init()方法,它是没有参数的init()方法,它会在init(ServletConfig)方法中被调用。

    GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。

    1. HttpServlet(从事javaweb开发的重点),上面说了servlet接口和GenericServlet都只是httpservlet的父类和父接口
    • HttpServlet覆盖了service()方法

    HttpServlet类中提供service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。

    在HttpServlet的service(ServletRequest,ServletResponse)方法中会把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse)方法,这说明子类可以去覆盖service(HttpServletRequest,HttpServletResponse)方法即可,这就不用自己去强转请求和响应对象了。

    • doGet()和doPost()

    在HttpServlet的service(HttpServletRequest,HttpServletResponse)方法会去判断当前请求是GET还是POST,如果是GET请求,那么会去调用本类的doGet()方法,如果是POST请求会去调用doPost()方法,这说明我们在子类中去覆盖doGet()或doPost()方法即可。

    1. JavaWeb的四大域对象
      四个域的作用域范围大小:PageContext (page域) < request < session < servletContext(application域)
    • ServletContext

    生命周期:当Web应用被加载进容器时创建代表整个web应用的ServletContext对象,当服务器关闭或Web应用被移除时,ServletContext对象跟着销毁。

    作用范围:整个Web应用。

    作用:a) 在不同Servlet 之间转发 b) 读取资源文件。

    • Request 域

    生命周期:在service 方法调用前由服务器创建,传入service方法。整个请求结束,request生命结束。

    作用范围:整个请求链(请求转发也存在)。

    作用: 在整个请求链中共享数据。

    • Session 域

    生命周期:在第一次调用 request.getSession() 方法时,服务器会检查是否已经有对应的session,如果没有就在内存 中创建一个session并返回。当一段时间内session没有被使用(默认为30分钟),则服务器会销毁该session。如果服务器非正常关闭(强行关闭),没有到期的session也会跟着销毁。如果调用session提供的invalidate(),可以立即销毁session。

    作用范围:一次会话。

    • PageContext 域

    生命周期:当对JSP的请求时开始,当响应结束时销毁。

    作用范围:整个JSP页面,是四大作用域中最小的一个,即超过这个页面就不能够使用了。(所以使用pageContext对象向其它页面传递参数是不可能的.)

    作用:(1)获取其它八大隐式对象,可以认为是一个入口对象。
    (2)获取其所有域中的数据

    1. HTTP协议六种请求方法
    • GET:GET可以说是最常见的了,它本质就是发送一个请求来取得服务器上的某一资源。资源通过一组HTTP头和呈现据(如HTML文本,或者图片或者视频等)返回给客户端。GET请求中,永远不会包含呈现数据。
    • HEAD:HEAD和GET本质是一样的,区别在于HEAD不含有呈现数据,而仅仅是HTTP头信息。有的人可能觉得这个方法没什么用,其实不是这样的。想象一个业务情景:欲判断某个资源是否存在,我们通常使用GET,但这里用HEAD则意义更加明确。
    • PUT:这个方法比较少见。HTML表单也不支持这个。本质上来讲, PUT和POST极为相似,都是向服务器发送数据,但它们之间有一个重要区别,PUT通常指定了资源的存放位置,而POST则没有,POST的数据存放位置由服务器自己决定。
    • DELETE:删除某一个资源。基本上这个也很少见,不过还是有一些地方比如amazon的S3云服务里面就用的这个方法来删除资源。
    • POST:向服务器提交数据。这个方法用途广泛,几乎目前所有的提交操作都是靠这个完成。
    • OPTIONS:这个方法很有趣,但极少使用。它用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。
    • TRACE:RACE方法被用于激发一个远程的,应用层的请求消息回路(译注:TRACE方法让客户端测试到服务器的网络通路,回路的意思如发送一个请返回一个响应,这就是一个请求响应回路)
    • CONNECT:HTTP1.1协议规范保留了CONNECT方法,此方法是为了能用于能动态切换到隧道的代理服务器(proxy,译注:可以为代理,也可以是代理服务器)。

    以上方法,我们可以跟数据库的CRUD增删改查操作对应起来: CREATE :PUT READ:GET UPDATE:POST DELETE:DELETE 这样一来就实现了HTTP和数据库操作(其实不光是数据库,任何数据如文件图表都是这样)的完美统一,这也是REST的精髓之一

    1. JavaWeb三大组件
      avaWeb三大组件指的是:Servlet、Filter、Listener
    • Servlet
      Servlet是用来处理客户端请求的动态资源,也就是当我们在浏览器中键入一个地址回车跳转后,请求就会被发送到对应的Servlet上进行处理。
    • Filter

    Filter与servlet在很多的方面极其相似,但是也有不同,例如filter和servlet一样都又三个生命周期方法,同时他们在web.xml中的配置文件也是差不多的、 但是servlet主要负责处理请求,而filter主要负责拦截请求,和放行。

    filter四种拦截方式:

    1. REQUEST:直接访问目标资源时执行过滤器。包括:在地址栏中直接访问、表单提交、超链接、重定向,只要在地址栏中可以看到目标资源的路径,就是REQUEST;
    2. FORWARD:转发访问执行过滤器。包括RequestDispatcher#forward()方法、< jsp:forward>标签都是转发访问;
    3. INCLUDE:包含访问执行过滤器。包括RequestDispatcher#include()方法、< jsp:include>标签都是包含访问;
    4. ERROR:当目标资源在web.xml中配置为< error-page>中时,并且真的出现了异常,转发到目标资源时,会执行过滤器。
    • Listener

    Listener就是监听器,我们在JavaSE开发或者Android开发时,经常会给按钮加监听器,当点击这个按钮就会触发监听事件,调用onClick方法,本质是方法回调。在JavaWeb的Listener也是这么个原理,但是它监听的内容不同,它可以监听Application、Session、Request对象,当这些对象发生变化就会调用对应的监听方法。

    1. ServletContext(监听Application)
    //生命周期监听:ServletContextListener
    void contextInitialized(ServletContextEvent sce):创建Servletcontext时
    void contextDestroyed(ServletContextEvent sce):销毁Servletcontext时
    
    //属性监听:ServletContextAttributeListener
    void attributeAdded(ServletContextAttributeEvent event):添加属性时;
    void attributeReplaced(ServletContextAttributeEvent event):替换属性时;
    void attributeRemoved(ServletContextAttributeEvent event):移除属性时;
    
    1. HttpSession(监听Session)
    //生命周期监听:HttpSessionListener
    void sessionCreated(HttpSessionEvent se):创建session时
    void sessionDestroyed(HttpSessionEvent se):销毁session时
    
    //属性监听:HttpSessioniAttributeListener
    void attributeAdded(HttpSessionBindingEvent event):添加属性时;
    void attributeReplaced(HttpSessionBindingEvent event):替换属性时
    void attributeRemoved(HttpSessionBindingEvent event):移除属性时
    
    1. ServletRequest(监听Request)
    //生命周期监听:ServletRequestListener
    void requestInitialized(ServletRequestEvent sre):创建request时
    void requestDestroyed(ServletRequestEvent sre):销毁request时
    
    //属性监听:ServletRequestAttributeListener
    void attributeAdded(ServletRequestAttributeEvent srae):添加属性时
    void attributeReplaced(ServletRequestAttributeEvent srae):替换属性时
    void attributeRemoved(ServletRequestAttributeEvent srae):移除属性时
    
    1. 感知Session监听
    • HttpSessionBindingListener监听
    1. 在需要监听的实体类实现HttpSessionBindingListener接口;
    2. 重写valueBound()方法,这方法是在当该实体类被放到Session中时,触发该方法;
    3. 重写valueUnbound()方法,这方法是在当该实体类从Session中被移除时,触发该方法 。
    • HttpSessionActivationListener监听
    1. 在需要监听的实体类实现HttpSessionActivationListener接口 ;
    2. 重写sessionWillPassivate()方法,这方法是在当该实体类被序列化时,触发该方法 ;
    3. 重写sessionDidActivate()方法,这方法是在当该实体类被反序列化时,触发该方法。

    参见:
    servlet详解(第一篇):https://blog.csdn.net/qq_28483283/article/details/51220749
    servlet详解(第二篇):
    https://blog.csdn.net/qq_28483283/article/details/51223735
    解析HTTP协议六种请求方法,get,head,put,delete,post有什么区别:
    https://blog.csdn.net/u010529455/article/details/42918639
    JavaWeb三大组件(Servlet、Filter、Listener):
    https://blog.csdn.net/xiaojie119120/article/details/73274759

    • 说出你所知的线程同步方法。

    同步主要是为了解决多线程访问指定数据块时不同线程间相互影响所导致的结果混乱,网上自己找资料学习了下,线程同步方法主要有6种:
    1.synchronized修饰符,java每个对象都会有一个内置锁,调用方法前需要获得锁,否则会进入阻塞态。synchronized修饰符可以作用于static静态方法(等于锁住整个类),但同步性能开销会比较大,一般建议锁住方法中的一部分代码块。
    2.volatile关键字,volatile修饰后会告诉虚拟机该变量可能会被多个线程访问,所以系统在使用带有volatile关键的变量的时候会重新计算该变量,而不是直接使用寄存器中的值。需要注意的是此关键字不能和final关键字连用。
    3.使用重入锁,java5提供了java.util.concurrent包来支持同步,ReentrantLock类便是其中lock接口的一个可重入互斥实现类。常用方法有:ReentrantLock() 创建实例、lock()获得锁、unlock()释放锁。注意,使用后及时释放锁,不然会出现死锁的现象,一般将锁释放放到finally中。
    4.使用局部变量,ThreadLocal变量,该变量会让每个使用该变量的线程都获得一个副本,从而隔绝了因为此变量而产生的相互影响,是一种用空间换时间的方法(synchronized是以时间换空间)。常用方法:ThreadLocal()创建本地变量,get()返回当前线程此局部变量值,initialValue()当前线程此局部变量初始化值,set(value)设置当前线程此局部变量值。
    5.使用阻塞队列,java5的java.util.concurrent中有LinkedBlockingQueue<E>类,一种先进先出的队列。常用方法:LinkedBlockQueue()创建本地变量;put(E e)队尾添加元素,满则阻塞;size()队列中元素个数;take()移除并返回队首元素。
    6.使用原子变量,java.util.atomic包中提供了多种原子类型变量(如AtomicInteger),使用它们可以简化同步操作。

    • short s1 = 1; s1 = s1 +1;是否正确?short s1 = 1;s1 += 1;是否正确?

    这是个主要是涉及一些类型转换的问题,对于short s1 = 1; s1 = s1 + 1,因为1 会被被系统默认为int,所以s1 + 1 结果会向上对齐被存为 int, s1 = s1 + 1 的时候,需要将s1 + 1的int结果转换为s1的short类型,而系统没有从int到short的隐式转换,所以编译会报错,需要强制转换。
    然而对于s1=1;s1 += 1编译是没有问题的,软件逻辑虽然是一样的,但为什么这样就不用强转呢?

    0 iconst_1
    1 istore_1
    2 iload_1
    3 iconst_1
    4 iadd
    5 i2s
    6 istore_1
    7 return
    

    分析下字节码,我们会看偏移量5有i2s字样(int to short),也就是说使用+=的时候,编译器主动添加了转换操作,故第二种情况不会报错。
    补充一些知识:上述情况对于 char、byte、short都适用,特别是对于char、byte、short在栈内作为操作数操作时都是存为int,会自动补齐位数(带符号)拓展到一个字长。int转char、byte、short的时候会先按短类型将int截断,然后带符号压栈为int,而char、byte、short转int的时候因为本来就是转int再压栈,故无需过多转换。
    常见的转换字节码有:i2l,i2s,i2c,i2b.....等等。

    • HashMap、HashTable、ConcurrentMap的区别?

    网上看了一下,大概说一下吧:
    1.HashMap和HashTable,底层实现都是数组+链表的形式,但由于HashTable实现了同步,所以变得线程安全了(反之,HashMap没有实现同步故并不线程安全)
    2.在使用方面,HashMap对数据没那么严格,key和value均可为空,而HashTable一旦有key为null的情况,立马抛出NullPointerException异常。
    所以对于HashMap来说判断是否有key应该用containskey方法(因为用get方法,null值可能代表值为null,也可能代表没有该值)。
    3.最后说说ConcurrentMap,ConcurrentMap是java 5中提供的对HashMap进行加强的Concurrent实现类,实现了同步,并引入了segment的概念(即分桶),不同于HashTable的synchronized同步会锁住整张表,ConcurrentMap通过lock操作,锁定每一个Segment,而一个表可以有很多segment,故对于多线程来说,拥有更好的并发性。

    • 两个对象值相同(x.equals(y) == true),但却可有不同的hashcode,这句话对不对?为什么?

    这个问题涉及到equals和hashcode的关系,按理说这两个并没有什么联系,一个默认是比较两者存储地址,一个是通过hash算法计算在散列表中的位置,但由于很多继承ojbect类的子类有自己重写equals方法(有不少会加入hashcode作为比较条件之一),所以产生了联系。也就是说我们稍微改写下equals方法便可以让问题中的情况发生,故这句话对的。
    再补充一个对 “==“ 号的理解,“==”号对于两个对象而言就是比较存储地址,是否是同一个引用。

    • abstract的方法是否可以同时是static,是否可同时是native,是否可同时是synchronized?为什么?

    首先来理解一下这些个概念:
    abstract 抽象类的标签,标明该类为抽象类,目的是申明一些不用马上实现的方法,具体实现是交由子类完成。
    static 静态标签,从属于类而被实例共享的方法或属性,随着类的创建而生成。
    native 本地化标签,是指java中的一部分方法交由操作系统本地的类库去实现,看成java语言和其他语言的一种交互手段。
    synchronized 同步标签,用于锁住变量、方法或者一段代码,线程同步锁。
    1.abstract和static,我们知道abstract是指抽象类,是为了被子类覆盖而产生多态,而static 是静态的,方法能被子类继承而不能被子类重写,一方面abstract需要子类覆盖来实现多态,而另一方面static又要求不能改写,设计上明显冲突。
    2.abstract和native,abstract要求方法在子类实现,而native表示方法交由操作系统实现,不太可能统一,故冲突。
    3.abstract和synchronized,这一点理解的不是很好,但主要是考虑synchronized同步锁需要指定具体的同步范围,如果不知道具体的方法实现就谈不上同步内容是什么,根据语言的设计思路和实际测试,不能同时使用。

    • 请简述Error、Exception的区别,你通常如何处理他们?

    Error、Exception都共同继承自Throwable,Exception还要分RuntimeException和CheckedException,Error和RuntimeException组成UncheckedException。
    Error主要用于描述java虚拟机相关的错误(如:系统崩溃、内存溢出等),这类错误靠程序无法恢复解决,所以编译器不会去检查,遇到这类错误直接建议终止程序。
    RuntimeException一般是程序设计引起的一些错误(如:除零、数据越界、空指针等),编译器不会去检查这类错误,可处理也可不处理,一般不处理。
    UncheckedException很多是指外部的一些错误(如:IO错误、文件找不到,文件尾后读数据等),程序无法避免,对于这类错误应该加以妥善处理,试程序继续运行。
    在多说一嘴自定义异常:
    自定义异常有助于我们将问题细化,针对不同的问题采取不同的手段(如io异常也要分连接断开,表不存在,文件损坏等多种不同的情况),一般通过继承Exception实现,使用自定义异常能使系统的鲁棒性更好,使用时注意结合业务,并理解其含义。
    补充一点:
    关于try,catch,finally的知识,当出现try-catch-finally结构的时候,其运行顺序应该是,try中return(若有exception,则发生excpetion位置)之前的代码->catch中return之前的代码(若有exception)->finally中的代码->catch中的return(若有exception)->try中的return(若无exception)

    • 请问springMVC中的DispatcherServlet会生成几个实例?

    其实这个问题是坑,因为springMVC下的对象基本都是单实例的,DispatcherServlet 也不列外。但如果你回答正确说是单实例的,面试官又会接着问你那单实例的话怎么处理并发访问呢?这种问题对于互联网公司特别爱问,因为互联公司的并行访问量一般都比较大。那对于这个问题,我们先要清楚,servlet 的工作原理是单实列多线程,因为有springMVC和tomcat为我们处理好了多线程的访问,所以这方面基本不是问题了。
    当然,需要补充的是,DispathcerServlet可以根据业务需求配置多个,所以也有多个实例的情况,但属于不同的Servlet类,回答的时候注意面试官表达的意思。

    • hibernate面对复杂数据应该怎样处理?

    自己在网上找了很多,也看了很多,比较突出的几点就是:

    1. 使用hibernate自带的sql支持或者直接使用jdbc。
    2. 从分利用hibernate框架的懒加载。
    3. 注意控制hibernate中依赖关系的深度。
    4. 调节各种参数的配置以提高性能与数据库交互的性能。
    • springMVC是怎样完成一次页面请求的?

    主要是需要注意到springMVC中的dispatcher实质上也是一个servlet。
    具体流程如下:前端通过ajax传过来命令和数据,dispatcherservlet 分析它的url或者根据其他信息分发到不同的controller下面,controller接着将数据传向下层service,service层再往下传到dao层,而dao层一般就是mybatis或者hibernate框架与mysql数据库进行交互并更新或得到数据,数据得到后反向通过dao、service到controller层,controller将数据封装成合适的格式调用servlet的response返回到前端页面。

    TO BE CONTINUED ......

    相关文章

      网友评论

          本文标题:冰解的破-J2EE

          本文链接:https://www.haomeiwen.com/subject/fezeuftx.html