赞
踩
1.数据库查询使用索引
2.查询数据不使用*,使用具体字段
3.语句大写
4.使用exists代替in,not exists代替not in
5.用>=代替>
6.尽量使用commit,因为会释放一些资源 例如
* 回滚段上用于回复的数据
* 被程序语句获得的锁等
* oracle管理这些资源的花费
- 原理:
- 1. 读取并解析配置文件
- 2. 读取并解析映射信息,创建SessionFactory
- 3. 打开Sesssion
- 4. 创建事务Transation
- 5. 持久化操作
- 6. 提交事务
- 7. 关闭Session
- 8. 关闭SesstionFactory
-
- 为什么要用:
- ● 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
-
- ● Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
-
- ● hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
-
- ● hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
-
- 2. Hibernate是如何延迟加载?
- 1. Hibernate 延迟加载实现:a)实体对象 b)集合(Collection)
-
- 2. Hibernate 提供了属性的延迟加载功能
-
- 当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。
-
- 3.Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
-
- 类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many、
-
- 4. 说下Hibernate的缓存机制
- 1. 内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存
- 2. 二级缓存:
- a) 应用级缓存
- b) 分布式缓存
- 条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非关键数据
- c) 第三方缓存的实现
- 5. Hibernate的查询方式
- Sql、Criteria,object comptosition
- Hql:
- 1. 属性查询
- 2. 参数查询、命名参数查询
- 3. 关联查询
- 4. 分页查询
- 5. 统计函数
-
- 6. 如何优化Hibernate?
- ● 使用双向一对多关联,不使用单向一对多
- ● 灵活使用单向一对多关联
- ● 不用一对一,用多对一取代
- ● 配置对象缓存,不使用集合缓存
- ● 继承类使用显式多态
- ● 表字段要少,表关联不要怕多,有二级缓存撑腰
-
- spring
- 为什么用:
- {AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP 后,公共服务 (比如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。
- IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置 所表明的,IOC 就像反 过来的 JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straight construction),每一个对象都是用其协作对象构造的。因此是由容器管理协作对象(collaborator)。
- Spring即使一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。}
-
- Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。
- 组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
- ☆ 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
-
- ☆ Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
-
- ☆ Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
-
- ☆ Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
-
- ☆ Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
-
- ☆ Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
-
- ☆ Spring MVC 框架:MVC 框架是一个全功能的构建Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
-
- Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。
-
- IOC 和 AOP
- 控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器(在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。
- 在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。下表列出了 IOC 的一个实现模式。
- Spring 框架的 IOC 容器采用类型 2 和类型3 实现。
- 面向方面的编程
- 面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。
- AOP 和 IOC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就是 Java 类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring AOP 编写的应用程序代码是松散耦合的。
- AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。
- IOC 容器
- Spring 设计的核心是 org.springframework.beans 包,它的设计目标是与 JavaBean 组件一起使用。这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介。下一个最高级抽象是 BeanFactory 接口,它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory 也可以管理对象之间的关系。
- BeanFactory 支持两个对象模型。
- □ 单态 模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。Singleton 是默认的也是最常用的对象模型。对于无状态服务对象很理想。
- □ 原型 模型确保每次检索都会创建单独的对象。在每个用户都需要自己的对象时,原型模型最适合。
- bean 工厂的概念是 Spring 作为 IOC 容器的基础。IOC 将处理事情的责任从应用程序代码转移到框架。正如我将在下一个示例中演示的那样,Spring 框架使用 JavaBean 属性和配置数据来指出必须设置的依赖关系。
- BeanFactory 接口
- 因为 org.springframework.beans.factory.BeanFactory 是一个简单接口,所以可以针对各种底层存储方法实现。最常用的 BeanFactory 定义是 XmlBeanFactory,它根据 XML 文件中的定义装入 bean,如清单 1 所示。
- 清单 1. XmlBeanFactory
- BeanFactory factory = new XMLBeanFactory(new FileInputSteam("mybean.xml"));
- 在 XML 文件中定义的 Bean 是被消极加载的,这意味在需要 bean 之前,bean 本身不会被初始化。要从 BeanFactory 检索 bean,只需调用 getBean() 方法,传入将要检索的 bean 的名称即可,如清单 2 所示。
-
- 清单 2. getBean()
- MyBean mybean = (MyBean) factory.getBean("mybean");
- 每个 bean 的定义都可以是 POJO (用类名和 JavaBean 初始化属性定义) 或 FactoryBean。FactoryBean 接口为使用 Spring 框架构建的应用程序添加了一个间接的级别。
-
- Struts工作机制?为什么要使用Struts?
- 工作机制:
- Struts的工作流程:
- 在web应用启动时就会加载初始化ActionServlet,ActionServlet从struts-config.xml文件中读取配置信息,把它们存放到各种配置对象..
- 当ActionServlet接收到一个客户请求时,将执行如下流程.
- (1)检索和用户请求匹配的ActionMapping实例,如果不存在,就返回请求路径无效信息;
- (2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中;
- (3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法;
- (4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActionErrors对象, 就表示表单验证成功;
- (5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪个Action,如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法;
- (6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给ActionForward对象指向的JSP组件;
- (7)ActionForward对象指向JSP组件生成动态网页,返回给客户;
- 为什么要用:
- JSP、Servlet、JavaBean技术的出现给我们构建强大的企业应用系统提供了可能。但用这些技术构建的系统非常的繁乱,所以在此之上,我们需要一个规则、一个把这些技术组织起来的规则,这就是框架,Struts便应运而生。
- 基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件
- Struts的validate框架是如何验证的?
- 在struts配置文件中配置具体的错误提示,再在FormBean中的validate()方法具体调用。
- 说下Struts的设计模式
- MVC模式: web应用程序启动时就会加载并初始化ActionServler。用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据,ActionServler根据Struts-config.xml 文件配置好的设置决定是否需要表单验证,如果需要就调用ActionForm的Validate()验证后选择将请求发送到哪个Action,如果 Action不存在,ActionServlet会先创建这个对象,然后调用Action的execute()方法。Execute()从 ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给 ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。
- 单例模式
- Factory(工厂模式):
- 定义一个基类===》实现基类方法(子类通过不同的方法)===》定义一个工厂类(生成子类实例)
- ===》开发人员调用基类方法
- Proxy(代理模式)
-
- spring工作机制及为什么要用?
- 1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责负责对请求进行真正的处理工作。
- 2.DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller.
- 3.DispatcherServlet请请求提交到目标Controller
- 4.Controller进行业务逻辑处理后,会返回一个ModelAndView
- 5.Dispathcher查询一个或多个ViewResolver视图解析器,找到ModelAndView对象指定的视图对象
- 6.视图对象负责渲染返回给客户端。
-
- 为什么用:
- AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP 后,公共服务 (比 如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。
- IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置 所表明的,IOC 就像反 过来的 JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straight construction),每一个对象都是用 其协作对象构造的。因此是由容器管理协作对象(collaborator)。
- Spring即使一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。
- 网友自出的几道面试题
- 1、 简述你对IoC(Inversion of Control)的理解,描述一下Spring中实现DI(Dependency Injection)的几种方式。
- 2、 Spring的Bean有多种作用域,包括:
- singleton、prototype、request、session、global session、application、自定义
- 3、 简单描述Spring Framework与Struts的不同之处,整合Spring与Struts有哪些方法,哪种最好,为什么?
- 4、 Hibernate中的update()和saveOrUpdate()的区别
- 5、 Spring对多种ORM框架提供了很好的支持,简单描述在Spring中使用Hibernate的方法,并结合事务管理。
-
- Hibernate
- 1.在数据库中条件查询速度很慢的时候,如何优化?
- 1.建索引
- 2.减少表之间的关联
- 3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
- 4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据
- [2.在hibernate中进行多表查询,每个表中各取几个字段,也就是说查询出来的结果集并没有一个实体类与之对应,如何解决这个问题?
- 解决方案一,按照Object[]数据取出数据,然后自己组bean
- 解决方案二,对每个表的bean写构造函数,比如表一要查出field1,field2两个字段,那么有一个构造函数就是Bean(type1 filed1,type2 field2) ,然后在hql里面就可以直接生成这个bean了。具体怎么用请看相关文档,我说的不是很清楚。
- session.load()和session.get()的区别
- Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。其区别在于:
- 如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException。
- Load方法可返回实体的代理类实例,而get方法永远直接返回实体类。
- load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。
-
- Session在加载实体对象时,将经过的过程:
- 首先,Hibernate中维持了两级缓存。第一级缓存由Session实例维护,其中保持了Session当前所有关联实体的数据,也称为内部缓存。而第二级缓存则存在于SessionFactory层次,由当前所有由本 SessionFactory构造的Session实例共享。出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
- 之后,Session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。 “NonExists”记录了当前Session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当于一个查询黑名单列表)。如此一来,如果 Session中一个无效的查询条件重复出现,即可迅速作出判断,从而获得最佳的性能表现。
- 对于load方法而言,如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。
- 如在缓存中未发现有效数据,则发起数据库查询操作(Select SQL),如经过查询未发现对应记录,则将此次查询的信息在“NonExists”中加以记录,并返回null。
- 根据映射配置和Select SQL得到的ResultSet,创建对应的数据对象。
- 将其数据对象纳入当前Session实体管理容器(一级缓存)。
- 执行Interceptor.onLoad方法(如果有对应的Interceptor)。
- 将数据对象纳入二级缓存。
- 如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
- 返回数据对象。
-
- Hibernate的主键生成机制
- 1) assigned
- 主键由外部程序负责生成,无需Hibernate参与。
- 2) hilo
- 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。
- 3) seqhilo
- 与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle。
- 4) increment
- 主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。
- 5) identity
- 采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。
- 6) sequence
- 采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。
- 7) native
- 由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
- 8) uuid.hex
- 由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
- 9) uuid.string
- 与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。
- 10) foreign
- 使用外部表的字段作为主键。一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。
- 这10中生成OID标识符的方法,increment 比较常用,把标识符生成的权力交给Hibernate处理.但是当同时多个Hibernate应用操作同一个数据库,甚至同一张表的时候.就推荐使用identity 依赖底层数据库实现,但是数据库必须支持自动增长,当然针对不同的数据库选择不同的方法.如果你不能确定你使用的数据库具体支持什么的情况下.可以选择用native 让Hibernate来帮选择identity,sequence,或hilo.
- 另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。
- 不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制
- myeclipse 加入Hibernate的全过程
- 1.Db-browers加入配置连接
- 2.新建工程
- 3.加入hibernate环境,指定*.hbm.xml及HibernateSessionFactory文件所在的位置
- hibernate的核心类是什么,它们的相互关系是什么?重要的方法是什么?
- Configuration
- SessionFactory
- Session如下方法
- Save
- load
- Update
- Delete
- Query q=CreateQuery(“from Customer where customerName=:customerName”)
- beginTransaction
- close
- Transaction
- Commit()
- Hibernate中数据表映射关系主要有什么类型
- one-to-many
- inverse:主控方,外键的关系有谁控制
- inverse=false 是主控方,外键是由它控制的
- inverse=true 是被控方,外键与它没关系
- 要想实现主控方的控制必须将被控方作为主控方的属性
- cascade:级联
- 主表增从表增
- 主表修从表修
- 主表删从表删
- lazy:延迟
- lazy=false:一下将所有的内容取出,不延时(常用)
- lazy=true:取出部分内容,其余内容动态去取
- 通过get可以取出对方的所有内容
- Hibernate中Criteria和DetachedCriteria的作用是什么
- Criteria c=session.createCriteria(Customer.class);
- //设置条件
- c.add(Expression.ge(“字段名”,”值对象”))
- ge:>=
- gt:>
- le:<=
- lt:<
- eq:=
- //排序
- c.addOrder(Order.asc(“字段名”))
- //分页
- c.setFirstResult(1)//从第2行开始提取
- c.setMaxResults(5)//返回5行
- DetachedCriteria产生时不需要session
- DetachedCriteria dc= DetachedCriteria.forClass(Customer.class)
- Criteria c=Dc.getExecutableCriteria(session)
- Hibernate中Query对象的使用
- 1 个或多个属性查询:
- Query query=session.createQuery(”select customername,customerid from Customer”)
- List l=query.list();
- For(int i=0;i {
- Obejct[] object=(Object[])l.get(i);
- Object[0] object[1]
- }
- }
- 分组: “select count(*),productname from Product group by productname order by productname”
- 取值与属性一样
- 配置的查询,在*.hbm.xml中
- from Product where productid=:productid
- ]]>
- Query query=session.getNamedQuery(sql);
- 联接1
- ”from Customer as customer join fetch customer.buySet”:将多的放到buySet属性中,得出的结是Customer有一个,Buy有多个
- 联接2
- “from Customer as customer join customer.buySet”:得出的对象,customer与buy是1对1
- 子查询:
- ”from Customer as customer where (select count(*) from customer.buySet)>1″
- Hibernate如何实现数据表映射的继承关系
- 1、两个表,子类重复父类的属性。
- 2、一个表,子类父类共用一个表
- 3、两个表,子类引用父类的主键,享用公共的字段或属性。
- 批量删除
- Query query=session.createQuery(“update”或”delete”);
- query.executeUpdate();
- jdbc、Hibernate、ibatis的区别
- jdbc:手动
- 手动写sql
- delete、insert、update要将对象的值一个一个取出传到sql中,不能直接传入一个对象。
- select:返回的是一个resultset,要从ResultSet中一行一行、一个字段一个字段的取出,然后封装到一个对象中,不直接返回一个对象。
- ibatis的特点:半自动化
- sql要手动写
- delete、insert、update:直接传入一个对象
- select:直接返回一个对象
- hibernate:全自动
- 不写sql,自动封装
- delete、insert、update:直接传入一个对象
- select:直接返回一个对象
- Detached Object(游离对象)可以传递到任何层直到表现层而不是用任何DTO(Data Transfer Objects). 然后你还可以重新把游离对象赋给另外一个Session.
- Hibernate的三种状态
- 瞬时态(Transient)、 持久态(Persistent)、脱管态(Detached)。处于持久态的对象也称为PO(Persistence Object),瞬时对象和脱管对象也称为VO(Value Object)。
- 瞬时态
- 由new命令开辟内存空间的java对象,
- eg. Person person = new Person(“amigo”, “女”);
- 如果没有变量对该对象进行引用,它将被java虚拟机回收。
- 瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系,在Hibernate中,可通过session的save()或 saveOrUpdate()方法将瞬时对象与数据库相关联,并将数据对应的插入数据库中,此时该瞬时对象转变成持久化对象。
- 持久态
- 处于该状态的对象在数据库中具有对应的记录,并拥有一个持久化标识。如果是用hibernate的delete()方法,对应的持久对象就变成瞬时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联。
- 当一个session执行close()或clear()、evict()之后,持久对象变成脱管对象,此时持久对象会变成脱管对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下。
- 持久对象具有如下特点:
- 1. 和session实例关联;
- 2. 在数据库中有与之关联的记录。
- 脱管态
- 当与某持久对象关联的session被关闭后,该持久对象转变为脱管对象。当脱管对象被重新关联到session上时,并再次转变成持久对象。
- 脱管对象拥有数据库的识别值,可通过update()、saveOrUpdate()等方法,转变成持久对象。
- 脱管对象具有如下特点:
- 1. 本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收;
- 2. 比瞬时对象多了一个数据库记录标识值。
- 1. Hibernate有哪几种查询数据的方式
- 3种:hql、QBC——Query By Criteria API、原生sql (通过createSQLQuery建立)
- 2. 谈谈Hibernate中inverse的作用
- inverse属性默认是false,就是说关系的两端都来维护关系。
- 比如Student和Teacher是多对多关系,用一个中间表TeacherStudent维护。Gp)i
- 如果Student这边inverse=”true”, 那么关系由另一端Teacher维护,就是说当插入Student时,不会操作TeacherStudent表(中间表)。只有Teacher插入或删除时才会触发对中间表的操作。所以两边都inverse=”true”是不对的,会导致任何操作都不触发对中间表的影响;当两边都inverse=”false” 或默认时,会导致在中间表中插入两次关系。
- 3. 说说Hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。
- saveOrUpdate()方法可以实现update()的功能,但会多些步骤,具体如下:
- 如果对象在该session中已经被持久化,不进行操作;对象的标识符属性(identifier property)在数据库中不存在或者是个暂时的值,调用save()方法保存它;如果session中的另一个对象有相同的标识符抛出一个异常;以上皆不符合则调用update()更新之。
- Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。其区别在于:
- 如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException;load方法可返回实体的代理类实例,而get方法永远直接返回实体类;load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。
-
- =====================Hibernate笔试题==========================
- (1)一般情况下,关系数据模型与对象模型之间有哪些匹配关系(多选)
- A)表对应类
- B)记录对应对象
- C)表的字段对应类的属性
- D)表之间的参考关系对应类之间的依赖关系
- (2)以下关于SessionFactory的说法哪些正确?(多选)
- A)对于每个数据库事务,应该创建一个SessionFactory对象
- B)一个SessionFactory对象对应一个数据库存储源。
- C)SessionFactory是重量级的对象,不应该随意创建。如果系统中只有一个数据库存储源,只需要创建一个。
- D)SessionFactory的load()方法用于加载持久化对象
- (3)Customer类中有一个Set类型的orders属性,用来存放Order订单对象,在Customer.hbm.xml文件中,用哪个元素映射orders属性?
- A) B) C) D)
- (4)元素有一个cascade属性,如果希望Hibernate级联保存集合中的对象,casecade属性应该取什么值?(单选)
- A)none
- B)save
- C)delete
- D)save-update
- (5)以下哪些属于Session的方法?
- A)load()
- B)save()
- C)delete()
- D)update()
- E)open()
- F)close()
- (6)以下程序的打印结果是什么?(单选)
- tx = session.beginTransaction();
- Customer c1=(Customer)session.load(Customer.class,new Long(1));
- Customer c2=(Customer)session.load(Customer.class,new Long(1));
- System.out.println(c1==c2);
- tx.commit();
- session.close();
- A)运行出错,抛出异常
- B)打印false
- C)打印true
- (7)以下程序代码对Customer的name属性修改了两次:
- tx = session.beginTransaction();
- Customer customer=(Customer)session.load(Customer.class,
- new Long(1));
- customer.setName(\”Jack\”);
- customer.setName(\”Mike\”);
- tx.commit();
- 执行以上程序,Hibernate需要向数据库提交几条update语句?(单选)
- A)0 B)1 C)2 D)3
- (8)在持久化层,对象分为哪些状态?(多选)
- A)临时状态
- B)独立状态
- C)游离状态
- D)持久化状态
- (9)对于以下程序,Customer对象在第几行变为持久化状态?(单选)
- Customer customer=new Customer(); //line1
- customer.setName(\”Tom\”); //line2
- Session session1=sessionFactory.openSession(); //line3
- Transaction tx1 = session1.beginTransaction(); //line4
- session1.save(customer); //line4
- tx1.commit(); //line5
- session1.close(); //line6
- A) line1 B)line2 C)line3 D)line4 E)line5 F)line6
- (10)对于以下程序,Customer对象在第几行变为游离状态?(单选)
- Customer customer=new Customer(); //line1
- customer.setName(\”Tom\”); //line2
- Session session1=sessionFactory.openSession(); //line3
- Transaction tx1 = session1.beginTransaction(); //line4
- session1.save(customer); //line4
- tx1.commit(); //line5
- session1.close(); //line6
- A) line1 B)line2 C)line3 D)line4 E)line5 F)line6
- (11)以下哪一种检索策略利用了外连结查询?(单选)
- A)立即检索 B)延迟检索 C)迫切左外连结检索
- (12)假设对Customer类的orders集合采用延迟检索策略,编译或运行以下程序,会出现什么情况(单选)
- Session session=sessionFactory.openSession();
- tx = session.beginTransaction();
- Customer customer=(Customer)session.get(Customer.class,new Long(1));
- tx.commit();
- session.close();
- Iterator orderIterator=customer.getOrders().iterator();
- A)编译出错 B)编译通过,并正常运行 C)编译通过,但运行时抛出异常
- (13)关于HQL与SQL,以下哪些说法正确?(多选)
- A)HQL与SQL没什么差别
- B)HQL面向对象,而SQL操纵关系数据库
- C)在HQL与SQL中,都包含select,insert,update,delete语句
- D)HQL仅用于查询数据,不支持insert,update和delete语句
- (14)事务隔离级别是由谁实现的?(单选)
- A)Java应用程序 B)Hibernate C)数据库系统 D)JDBC驱动程序
- (15)悲观锁与乐观锁,哪个具有较好的并发性能?(单选)
- A)悲观锁 B)乐观锁
- 答案:
- (1)A,B,C (2)B,C (3)A (4)D (5)A,B,C,D,F (6)C (7)B (8)A,C,D (9)D (10)F (11)C (12)C (13)B,D (14)C (15)B
- 1.strust的。
- Action是不是线程安全的?如果不是
- 有什么方式可以保证Action的线程安全?如果是,说明原因
- 2.MVC,分析一下struts是如何实现MVC的
- 3.struts中的几个关键对象的作用(说说几个关键对象的作用)
- 4.spring
- 说说AOP和IOC的概念以及在spring中是如何应用的
- 5.Hibernate有哪几种查询数据的方式
- 6.load()和get()的区别
- 1. Struts的工作原理
- 在Struts中,用户的请求一般以*.do作为请求服务名,所有的*.do请求均被指向ActionSevlet, ActionSevlet根据Struts-config.xml中的配置信息,将用户请求封装成一个指定名称的FormBean,并将此 FormBean传至指定名称的ActionBean,由ActionBean完成相应的业务操作,如文件操作,数据库操作等。每一个*.do均有对应的 FormBean名称和ActionBean名称,这些在Struts-config.xml中配置。
- 2. Struts优点与缺点
- Struts是开源软件,使开发者能更深入的了解其内部实现机制。
- Struts 优点:业界”标准”(很多成功案例),学习资源丰富。
- Struts的优点主要集中体现在两个方面:Taglib和页面导航。
- a、利用Struts提供的taglib可以大大节约开发时间。
- b、维护扩展比较方便。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着莫大的好处。
- c、表现与逻辑分离
- d、表单验证解决了请求数据的验证问题,增强了系统健壮性。
- e、便于团队开发
- Struts缺点:a、大量的使用标签,对于初学者难度较大。
- b、ActionForms使用不便、无法进行单元测试(StrutsTestCase只能用于集成)
- 3. Struts提供了几个标签库?都是什么标签库?
- Struts提供了五个标签库,即:HTML、Bean、Logic、Template和Nested。
- HTML 标签 用来创建能够和Struts 框架和其他相应的HTML 标签交互的HTML 输入表单
- Bean 标签 在访问JavaBeans 及其属性,以及定义一个新的bean 时使用
- Logic 标签 管理条件产生的输出和对象集产生的循环
- Template 标签 随着Tiles框架包的出现,此标记已开始减少使用
- Nested 标签 增强对其他的Struts 标签的嵌套使用的能力
- 4. Tiles框架是什么?
- Tiles框架为创建Web页面提供了一种模板机制,它能将网页的布局和内容分离。
- 1、MVC的各个部分都有那些技术来实现?如何实现?
- 答:MVC是Model-View-Controller的简写。“Model”代表的是应用的业务逻辑(通过JavaBean,EJB组件实现),“View”是应用的表示面(由JSP页面产生),“Controller”是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。
- 2、说说Struts的应用。
- 答:Struts 是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。采用Struts能开发出基于MVC设计模式的应用构架。 Struts的主要功能包括:一包含一个controller servlet,能将用户的请求发送到相应的Action对象。二JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。三提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。
- 3、strust的Action是不是线程安全的?如果不是有什么方式可以保证Action的线程安全?如果是请说明原因。
- 答:不是线程安全的,只要不申明类变量就可以保证线程安全。因为只存在一个Action类实例,所有线程会共享类变量。
- 4、应用服务器具体包括那些?
- 答:应用服务器具体包括:BEA WebLogic Server、IBM WebSphere Application Server、Oracle9i Application Server、JBoss和Tomcat等。
1、Vector是多线程安全的,而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
2、两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同的,很多网友说Vector增加原来空间的一倍,ArrayList增加原来空间的50%,其实也差不多是这个意思,不过还有一点点问题可以从源码中看出,一会儿从源码中分析。
3、Vector可以设置增长因子,而ArrayList不可以,最开始看这个的时候,我没理解什么是增量因子,不过通过对比一下两个源码理解了这个,先看看两个类的构造方法:
Java同步机制有4种实现方式:(部分引用网上资源)
① ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile
目的:都是为了解决多线程中的对同一变量的访问冲突
ThreadLocal
ThreadLocal 保证不同线程拥有不同实例,相同线程一定拥有相同的实例,即为每一个使用该
变量的线程提供一个该变量值的副本,每一个线程都可以独立改变自己的副本,而不是与其它线程的副本冲突。
优势:提供了线程安全的共享对象
与其它同步机制的区别:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信;而 ThreadLocal 是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源,这样当然不需要多个线程进行同步了。
volatile
volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。
而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。
优势:这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
缘由:Java 语言规范中指出,为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而
且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某
个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而 volatile 关键字就
是提示 VM :对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用技巧:在两个或者更多的线程访问的成员变量上使用 volatile 。当要访问的变量已在
synchronized 代码块中,或者为常量时,不必使用。
线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的
是B。只在某些动作时才进行A和B的同步,因此存在A和B不一致的情况。volatile就是用来避免这种
情况的。 volatile告诉jvm,它所修饰的变量不保留拷贝,直接访问主内存中的(读操作多时使用
较好;线程间需要通信,本条做不到)
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自
动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的
一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。
您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理
想的线程安全,必须同时满足下面两个条件:
对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的不变式中。
sleep() vs wait()
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监
控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁
定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁
进入运行状态。
(如果变量被声明为volatile,在每次访问时都会和主存一致;如果变量在同步方法或者同步块中
被访问,当在方法或者块的入口处获得锁以及方法或者块退出时释放锁时变量被同步。)
下图单位是bit,非字节 1B=8bit
不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。平常我们定义的String str=”a”;其实和String str=new String(“a”)还是有差异的。
前者默认调用的是String.valueOf来返回String实例对象,至于调用哪个则取决于你的赋值,比如String num=1,调用的是
public static String valueOf(int i) {
return Integer.toString(i);
}
后者则是调用如下部分:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
最后我们的变量都存储在一个char数组中
private final char value[];
String 字符串常量(final修饰,不可被继承),String是常量,当创建之后即不能更改。(可以通过StringBuffer和StringBuilder创建String对象(常用的两个字符串操作类)。)
StringBuffer 字符串变量(线程安全),其也是final类别的,不允许被继承,其中的绝大多数方法都进行了同步处理,包括常用的Append方法也做了同步处理(synchronized修饰)。其自jdk1.0起就已经出现。其toString方法会进行对象缓存,以减少元素复制开销。
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder 字符串变量(非线程安全)其自jdk1.5起开始出现。与StringBuffer一样都继承和实现了同样的接口和类,方法除了没使用synch修饰以外基本一致,不同之处在于最后toString的时候,会直接返回一个新对象。
public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}
ArrayList和LinkedList都实现了List接口,有以下的不同点:
1、ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
2、相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
3、LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
此题考察的是类加载器实例化时进行的操作步骤(加载–>连接->初始化)。
父类静态代变量、
父类静态代码块、
子类静态变量、
子类静态代码块、
父类非静态变量(父类实例成员变量)、
父类构造函数、
子类非静态变量(子类实例成员变量)、
子类构造函数。
测试demo:http://blog.csdn.net/u014042066/article/details/77574956
参阅我的博客《深入理解类加载》:http://blog.csdn.net/u014042066/article/details/77394480
hashMap是线程不安全的,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,采用哈希表来存储的,
参照该链接:https://zhuanlan.zhihu.com/p/21673805
JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
参照:https://yq.aliyun.com/articles/36781
TreeMap和LinkedHashMap是有序的(TreeMap默认升序,LinkedHashMap则记录了插入顺序)。
参照:http://uule.iteye.com/blog/1522291
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;
聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;
参考:http://www.cnblogs.com/jiqing9006/p/5915023.html
IO是面向流的,NIO是面向缓冲区的
参考:https://zhuanlan.zhihu.com/p/23488863
http://developer.51cto.com/art/201103/252367.htm
http://www.jianshu.com/p/3f703d3d804c
参照:http://www.jianshu.com/p/3ea4a6b57f87?amp
http://blog.csdn.net/yongjian1092/article/details/7364451
https://my.oschina.net/gpzhang/blog/486743
Jdk cglib jdk底层是利用反射机制,需要基于接口方式,这是由于
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
Cglib则是基于asm框架,实现了无反射机制进行代理,利用空间来换取了时间,代理效率高于jdk
http://lrd.ele.me/2017/01/09/dynamic_proxy/
同上(基于invocationHandler和methodInterceptor)
同上
类、变量、方法
http://www.importnew.com/7553.html
懒汉式单例,饿汉式单例,双重检查等
参考:https://my.oschina.net/dyyweb/blog/609021
同时复写hashcode和equals方法,优势可以添加自定义逻辑,且不必调用超类的实现。
参照:http://java-min.iteye.com/blog/1416727
访问修饰符,主要标示修饰块的作用域,方便隔离防护
同一个类 同一个包 不同包的子类 不同包的非子类
Private √
Default √ √
Protected √ √ √
Public √ √ √ √
public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不
仅可以跨类访问,而且允许跨包(package)访问。
private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以
及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
protect: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、
属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访
问。
http://www.oschina.net/translate/java-copy-shallow-vs-deep-in-which-you-will-swim
http://blog.csdn.net/snow_wu/article/details/53172721
http://blog.csdn.net/woshixuye/article/details/8230407
同上
类加载无须等到“首次使用该类”时加载,jvm允许预加载某些类。。。。
http://www.cnblogs.com/jasonstorm/p/5663864.html
参考上边试题
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率
http://baike.baidu.com/item/java%E6%B3%9B%E5%9E%8B
hashcode
hashcode()方法提供了对象的hashCode值,是一个native方法,返回的默认值与System.identityHashCode(obj)一致。
通常这个值是对象头部的一部分二进制位组成的数字,具有一定的标识对象的意义存在,但绝不定于地址。
作用是:用一个数字来标识对象。比如在HashMap、HashSet等类似的集合类中,如果用某个对象本身作为Key,即要基于这个对象实现Hash的写入和查找,那么对象本身如何实现这个呢?就是基于hashcode这样一个数字来完成的,只有数字才能完成计算和对比操作。
hashcode是否唯一
hashcode只能说是标识对象,在hash算法中可以将对象相对离散开,这样就可以在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不一定是唯一的,所以hash算法中定位到具体的链表后,需要循环链表,然后通过equals方法来对比Key是否是一样的。
equals与hashcode的关系
equals相等两个对象,则hashcode一定要相等。但是hashcode相等的两个对象不一定equals相等。
https://segmentfault.com/a/1190000004520827
有
底层是基于hashmap实现的
http://wiki.jikexueyuan.com/project/java-collection/hashset.html
什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
http://www.importnew.com/17964.html
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
参照:http://wiki.jikexueyuan.com/project/java-vm/storage.html
eden 和 survior 是按8比1分配的
http://blog.csdn.net/lojze_ly/article/details/49456255
对象诞生即新生代->eden,在进行minor gc过程中,如果依旧存活,移动到from,变成Survivor,进行标记代数,如此检查一定次数后,晋升为老年代,
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html
http://ifeve.com/useful-jvm-flags/
https://wangkang007.gitbooks.io/jvm/content/jvmcan_shu_xiang_jie.html
Serial、parNew、ParallelScavenge、SerialOld、ParallelOld、CMS、G1
https://wangkang007.gitbooks.io/jvm/content/chapter1.html
http://www.importnew.com/13493.html
首先分析是什么类型的内存溢出,对应的调整参数或者优化代码。
https://wangkang007.gitbooks.io/jvm/content/4jvmdiao_you.html
内存屏障:为了保障执行顺序和可见性的一条cpu指令
重排序:为了提高性能,编译器和处理器会对执行进行重拍
happen-before:操作间执行的顺序关系。有些操作先发生。
主内存:共享变量存储的区域即是主内存
工作内存:每个线程copy的本地内存,存储了该线程以读/写共享变量的副本
http://ifeve.com/java-memory-model-1/
http://www.jianshu.com/p/d3fda02d4cae
http://blog.csdn.net/kenzyq/article/details/50918457
类加载器的分类(bootstrap,ext,app,curstom),类加载的流程(load-link-init)
http://blog.csdn.net/gjanyanlig/article/details/6818655/
Java程序在运行状态可以动态的获取类的所有属性和方法,并实例化该类,调用方法的功能
http://baike.baidu.com/link?url=C7p1PeLa3ploAgkfAOK-4XHE8HzQuOAB7K5GPcK_zpbAa_Aw-nO3997K1oir8N–1_wxXZfOThFrEcA0LjVP6wNOwidVTkLBzKlQVK6JvXYvVNhDWV9yF-NIOebtg1hwsnagsjUhOE2wxmiup20RRa#7
-server
Xms6000M
-Xmx6000M
-Xmn500M
-XX:PermSize=500M
-XX:MaxPermSize=500M
-XX:SurvivorRatio=65536
-XX:MaxTenuringThreshold=0
-Xnoclassgc
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSClassUnloadingEnabled
-XX:-CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=90
-XX:SoftRefLRUPolicyMSPerMB=0
-XX:+PrintClassHistogram
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-Xloggc:log/gc.log
Cms是以获取最短回收停顿时间为目标的收集器。基于标记-清除算法实现。比较占用cpu资源,切易造成碎片。
G1是面向服务端的垃圾收集器,是jdk9默认的收集器,基于标记-整理算法实现。可利用多核、多cpu,保留分代,实现可预测停顿,可控。
http://blog.csdn.net/linhu007/article/details/48897597
请解释如下 jvm 参数的含义:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20
XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。
Server模式启动
最小堆内存512m
最大512m
每个线程栈空间1m
永久代256
最大永久代256
最大转为老年代检查次数20
Cms回收开启时机:内存占用80%
命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期
Server- –多个service
Container级别的:–>engine–》host–>context
Listenter
Connector
Logging、Naming、Session、JMX等等
通过WebappClassLoader 加载class
http://www.ibm.com/developerworks/cn/java/j-lo-tomcat1/
http://blog.csdn.net/dc_726/article/details/11873343
http://www.cnblogs.com/xing901022/p/4574961.html
http://www.jianshu.com/p/62ec977996df
硬件上选择,操作系统选择,版本选择,jdk选择,配置jvm参数,配置connector的线程数量,开启gzip压缩,trimSpaces,集群等
http://blog.csdn.net/lifetragedy/article/details/7708724
通过listener入口,核心是在AbstractApplicationContext的refresh方法,在此处进行装载bean工厂,bean,创建bean实例,拦截器,后置处理器等。
https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/
七种传播属性。
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
编程式和声明式
同上
核心组件:bean,context,core,单例注入是通过单例beanFactory进行创建,生命周期是在创建的时候通过接口实现开启,循环注入是通过后置处理器,aop其实就是通过反射进行动态代理,pointcut,advice等。
Aop相关:http://blog.csdn.net/csh624366188/article/details/7651702/
入口是web.xml中配置的ds,ds继承了HttpServletBean,FrameworkServlet,通过其中的init方法进行初始化装载bean和实例,initServletBean是实际完成上下文工作和bean初始化的方法。
http://www.mamicode.com/info-detail-512105.html
Tcp/ip io cpu memory
net.ipv4.tcp_syncookies = 1
#启用syncookies
net.ipv4.tcp_max_syn_backlog = 8192
#SYN队列长度
net.ipv4.tcp_synack_retries=2
#SYN ACK重试次数
net.ipv4.tcp_fin_timeout = 30
#主动关闭方FIN-WAIT-2超时时间
net.ipv4.tcp_keepalive_time = 1200
#TCP发送keepalive消息的频度
net.ipv4.tcp_tw_reuse = 1
#开启TIME-WAIT重用
net.ipv4.tcp_tw_recycle = 1
#开启TIME-WAIT快速回收
net.ipv4.ip_local_port_range = 1024 65000
#向外连接的端口范围
net.ipv4.tcp_max_tw_buckets = 5000
#最大TIME-WAIT数量,超过立即清除
net.ipv4.tcp_syn_retries = 2
#SYN重试次数
echo “fs.file-max=65535” >> /etc/sysctl.conf
sysctl -p
http://www.haiyun.me/category/system/
阻塞式io,非阻塞io,io复用模型,信号驱动io模型,异步io模型。
https://yq.aliyun.com/articles/46404
https://yq.aliyun.com/articles/46402
select的本质是采用32个整数的32位,即32*32= 1024来标识,fd值为1-1024。当fd的值超过1024限制时,就必须修改FD_SETSIZE的大小。这个时候就可以标识32*max值范围的fd。
对于单进程多线程,每个线程处理多个fd的情况,select是不适合的。
1.所有的线程均是从1-32*max进行扫描,每个线程处理的均是一段fd值,这样做有点浪费
2.1024上限问题,一个处理多个用户的进程,fd值远远大于1024
所以这个时候应该采用poll,
poll传递的是数组头指针和该数组的长度,只要数组的长度不是很长,性能还是很不错的,因为poll一次在内核中申请4K(一个页的大小来存放fd),尽量控制在4K以内
epoll还是poll的一种优化,返回后不需要对所有的fd进行遍历,在内核中维持了fd的列表。select和poll是将这个内核列表维持在用户态,然后传递到内核中。但是只有在2.6的内核才支持。
epoll更适合于处理大量的fd ,且活跃fd不是很多的情况,毕竟fd较多还是一个串行的操作
https://yq.aliyun.com/articles/10525
Ls,find,tar,tail,cp,rm,vi,grep,ps,pkill等等
https://yq.aliyun.com/articles/69417?spm=5176.100240.searchblog.18.Zrbh9R
Tail -n 5 filename
ps -ef|grep Java
控制权的转换,根据优先级切换上下文(用户,寄存器,系统)
http://www.cnblogs.com/kkshaq/p/4544426.html
Linux 实现并没有区分这两个概念(进程和线程)
1. 进程:程序的一次执行
2. 线程:CPU的基本调度单位
一个进程可以包含多个线程。
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
实现runable接口,继承thread类。
http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/
Volatile利用内存栅栏机制来保持变量的一致性。不能代替锁,其只具备数据可见性一致性,不具备原子性。
http://blog.csdn.net/gongzi2311/article/details/20715185
新建,可运行,运行中, 睡眠,阻塞,等待,死亡。
http://ifeve.com/thread-status
Sleep是休眠线程,wait是等待,sleep是thread的静态方法,wait则是object的方法。
Sleep依旧持有锁,并在指定时间自动唤醒。wait则释放锁。
http://www.jianshu.com/p/4ec3f4b3903d
首先两者都保持了并发场景下的原子性和可见性,区别则是synchronized的释放锁机制是交由其自身控制,且互斥性在某些场景下不符合逻辑,无法进行干预,不可人为中断等。
而lock常用的则有ReentrantLock和readwritelock两者,添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。
http://blog.csdn.net/vking_wang/article/details/9952063
Synchronized底层是通过监视器的enter和exit实现
https://my.oschina.net/cnarthurs/blog/847801
http://blog.csdn.net/a314773862/article/details/54095819
AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean;基于CAS原语实现 ,比较并交换、加载链接/条件存储,最坏的情况下是旋转锁
https://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html
http://www.jmatrix.org/java/848.html
newSingleThreadExecutor返回以个包含单线程的Executor,将多个任务交给此Exector时,这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。
newFixedThreadPool返回一个包含指定数目线程的线程池,如果任务数量多于线程数目,那么没有没有执行的任务必须等待,直到有任务完成为止。
newCachedThreadPool根据用户的任务数创建相应的线程来处理,该线程池不会对线程数目加以限制,完全依赖于JVM能创建线程的数量,可能引起内存不足。
底层是基于ThreadPoolExecutor实现,借助reentrantlock保证并发。
coreSize核心线程数,maxsize最大线程数。
http://ifeve.com/java-threadpoolexecutor/
Shutdown shutdownNow tryTerminate 清空工作队列,终止线程池中各个线程,销毁线程池
http://blog.csdn.net/xxcupid/article/details/51993235
ScheduledThreadPoolExecutor 设置定时,进行调度。
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
http://ifeve.com/java-scheduledthreadpoolexecutor/
单例
通过单例工厂 DefaultSingletonBeanRegistry实现单例
通过保AsyncTaskExecutor持安全
public static void main(String[] args) {
final String str=”abc”;
ExecutorService executorService= Executors.newFixedThreadPool(3);
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(“1”+str);
}
});executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(“2”+str);
}
});executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(“2”+str);
}
});
}
Threadlocal底层是通过threadlocalMap进行存储键值 每个ThreadLocal类创建一个Map,然后用线程的ID作为Map的key,实例对象作为Map的value,这样就能达到各个线程的值隔离的效果。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
谁设置谁负责移除
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/
Collections.synchronizedList() ConcurrentLinkedQueue
http://blog.csdn.net/xingjiarong/article/details/48046751
LockFree,CAS
基于jdk提供的原子类原语实现,例如AtomicReference
http://blog.csdn.net/b_h_l/article/details/8704480
首先这两个方法只能在同步代码块中调用,wait会释放掉对象锁,等待notify唤醒。
http://blog.csdn.net/ithomer/article/details/7685594
根据具体情况(sleep,wait,join等),酌情选择notifyAll,notify进行线程唤醒。
http://blog.chinaunix.net/uid-122937-id-215913.html
CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它运行一个或者多个线程一直处于等待状态。
CyclicBarrier要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
CyclicBarrier初始化的时候,设置一个屏障数。线程调用await()方法的时候,这个线程就会被阻塞,当调用await()的线程数量到达屏障数的时候,主线程就会取消所有被阻塞线程的状态。
前者是递减,不可循环,后者是递加,可循环用
countdowlatch 基于abq cb基于ReentrantLock Condition
http://www.jianshu.com/p/a101ae9797e3
http://blog.csdn.net/tolcf/article/details/50925145
对象锁和类锁
https://yq.aliyun.com/articles/24226
LinkedBlockingQueue 是一个基于单向链表的、范围任意的(其实是有界的)、FIFO 阻塞队列。
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael & Scott算法上进行了一些修改, Michael & Scott算法的详细信息可以参见参考资料一。
http://ifeve.com/concurrentlinkedqueue/
http://ifeve.com/juc-linkedblockingqueue/
http://blog.csdn.net/xiaohulunb/article/details/38932923
死锁问题是多线程特有的问题,它可以被认为是线程间切换消耗系统性能的一种极端情况。在死锁时,线程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。死锁问题是在多线程开发中应该坚决避免和杜绝的问题。
一般来说,要出现死锁问题需要满足以下条件:
1. 互斥条件:一个资源每次只能被一个线程使用。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏死锁 4 个必要条件之一中的任何一个,死锁问题就能被解决。
https://www.ibm.com/developerworks/cn/java/j-lo-deadlock/
此问题的本质是保持顺序执行。可以使用executors
HTTP 1.0主要有以下几点变化:
请求和相应可以由于多行首部字段构成
响应对象前面添加了一个响应状态行
响应对象不局限于超文本
服务器与客户端之间的连接在每次请求之后都会关闭
实现了Expires等传输内容的缓存控制
内容编码Accept-Encoding、字符集Accept-Charset等协商内容的支持
这时候开始有了请求及返回首部的概念,开始传输不限于文本(其他二进制内容)
HTTP 1.1加入了很多重要的性能优化:持久连接、分块编码传输、字节范围请求、增强的缓存机制、传输编码及请求管道。
http://imweb.io/topic/554c5879718ba1240cc1dd8a
第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND
状态。
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。
发送完毕后,服务器端进入 SYN_RCVD
状态。
第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
发送完毕后,客户端进入 ESTABLISHED
状态,当服务器端接收到这个包时,也进入 ESTABLISHED
状态,TCP 握手结束。
第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。
两次后会重传直到超时。如果多了会有大量半链接阻塞队列。
https://segmentfault.com/a/1190000006885287
https://hit-alibaba.github.io/interview/basic/network/TCP.html
TIME_WAIT状态就是用来重发可能丢失的ACK报文。
TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。
1xx:信息,请求收到,继续处理
2xx:成功,行为被成功地接受、理解和采纳
3xx:重定向,为了完成请求,必须进一步执行的动作
4xx:客户端错误,请求包含语法错误或者请求无法实现
5xx:服务器错误,服务器不能实现一种明显无效的请求
200 ok 一切正常
302 Moved Temporatily 文件临时移出
404 not found
https://my.oschina.net/gavinjin/blog/42856
Dns解析–>端口分析–>tcp请求–>服务器处理请求–>服务器响应–>浏览器解析—>链接关闭
使用序号,对收到的TCP报文段进行排序以及检测重复的数据;使用校验和来检测报文段的错误;使用确认和计时器来检测和纠正丢包或延时。//TCP头部,总长度20字节
typedef struct _tcp_hdr
{
unsigned short src_port; //源端口号
unsigned short dst_port; //目的端口号
unsigned int seq_no; //序列号
unsigned int ack_no; //确认号
#if LITTLE_ENDIAN
unsigned char reserved_1:4; //保留6位中的4位首部长度
unsigned char thl:4; //tcp头部长度
unsigned char flag:6; //6位标志
unsigned char reseverd_2:2; //保留6位中的2位
#else
unsigned char thl:4; //tcp头部长度
unsigned char reserved_1:4; //保留6位中的4位首部长度
unsigned char reseverd_2:2; //保留6位中的2位
unsigned char flag:6; //6位标志
#endif
unsigned short wnd_size; //16位窗口大小
unsigned short chk_sum; //16位TCP检验和
unsigned short urgt_p; //16为紧急指针
}tcp_hdr;
https://zh.bywiki.com/zh-hans/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE
无法被浏览器缓存的请求:
HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
经过HTTPS安全加密的请求(有人也经过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public之后,能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)
POST请求无法被缓存
HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存
http://www.alloyteam.com/2012/03/web-cache-2-browser-cache/
http://www.w3school.com.cn/tags/html_ref_httpmethods.asp
http://www.360doc.com/content/12/0612/14/8093902_217673378.shtml
参考上面
加密方式是tls/ssl,底层是通过对称算法,非对称,hash算法实现
客户端发起HTTPS请求 –》2. 服务端的配置 –》
3. 传送证书 —》4. 客户端解析证书 5. 传送加密信息 6. 服务段解密信息 7. 传输加密后的信息 8. 客户端解密信息
http://www.cnblogs.com/zhuqil/archive/2012/07/23/2604572.html
Cdn缓存,redis缓存,ehcache缓存等
Cdn 图片资源 js等, redis一主一从 echcache缓存数据
final int cacheSize = 100;
Map
Redis生成,mongodb的objectId,zk生成
http://www.cnblogs.com/haoxinyue/p/5208136.html
分流 – 限流–异步–公平性(只能参加一次)–用户体验(第几位,多少分钟,一抢完)
容错处理
Redis 队列 mysql
30分钟关闭 可以借助redis的发布订阅机制 在失效时进行后续操作,其他mq也可以
http://www.infoq.com/cn/articles/yhd-11-11-queuing-system-design
首先分布式锁实现常见的有数据库锁(表记录),缓存锁,基于zk(临时有序节点可以实现的)的三种
Redis适用于对性能要求特别高的场景。redis可以每秒执行10w次,内网延迟不超过1ms
缺点是数据存放于内存,宕机后锁丢失。
锁无法释放?使用Zookeeper可以有效的解决锁无法释放的问题,因为在创建锁的时候,客户端会在ZK中创建一个临时节点,一旦客户端获取到锁之后突然挂掉(Session连接断开),那么这个临时节点就会自动删除掉。其他客户端就可以再次获得锁。
非阻塞锁?使用Zookeeper可以实现阻塞的锁,客户端可以通过在ZK中创建顺序节点,并且在节点上绑定监听器,一旦节点有变化,Zookeeper会通知客户端,客户端可以检查自己创建的节点是不是当前所有节点中序号最小的,如果是,那么自己就获取到锁,便可以执行业务逻辑了。
不可重入?使用Zookeeper也可以有效的解决不可重入的问题,客户端在创建节点的时候,把当前客户端的主机信息和线程信息直接写入到节点中,下次想要获取锁的时候和当前最小的节点中的数据比对一下就可以了。如果和自己的信息一样,那么自己直接获取到锁,如果不一样就再创建一个临时的顺序节点,参与排队。
单点问题?使用Zookeeper可以有效的解决单点问题,ZK是集群部署的,只要集群中有半数以上的机器存活,就可以对外提供服务。
http://www.hollischuang.com/archives/1716
可以使用filter过滤处理
Two Phase commit协议
优点是可以管理多机事务,拥有无线扩展性 确定是易用性难,承担延时风险
JTA,atomiks等
https://yq.aliyun.com/webinar/join/185?spm=5176.8067841.0.0.RL4GDa
一致性hash是一种分布式hash实现算法。满足平衡性 单调性 分散性 和负载。
http://blog.csdn.net/cywosp/article/details/23397179/
REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
http://baike.baidu.com/link?url=fTSAdL-EyYvTp9z7mZsCOdS3kbs4VKKAnpBLg3WS_1Z4cmLMp3S-zrjcy5wakLTO5AIoPTopWVkG-IenloPKxq
服务器内核调优(tcp,文件数),客户端调优,框架选择(netty)
缓存雪崩可能是因为数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。
解决思路:
1,采用加锁计数,或者使用合理的队列数量来避免缓存失效时对数据库造成太大的压力。这种办法虽然能缓解数据库的压力,但是同时又降低了系统的吞吐量。
2,分析用户行为,尽量让失效时间点均匀分布。避免缓存雪崩的出现。
3,如果是因为某台缓存服务器宕机,可以考虑做主备,比如:redis主备,但是双缓存涉及到更新事务的问题,update可能读到脏数据,需要好好解决。
http://www.cnblogs.com/jinjiangongzuoshi/archive/2016/03/03/5240280.html
MESI是四种缓存段状态的首字母缩写,任何多核系统中的缓存段都处于这四种状态之一。我将以相反的顺序逐个讲解,因为这个顺序更合理:
失效(Invalid)缓存段,要么已经不在缓存中,要么它的内容已经过时。为了达到缓存的目的,这种状态的段将会被忽略。一旦缓存段被标记为失效,那效果就等同于它从来没被加载到缓存中。
共享(Shared)缓存段,它是和主内存内容保持一致的一份拷贝,在这种状态下的缓存段只能被读取,不能被写入。多组缓存可以同时拥有针对同一内存地址的共享缓存段,这就是名称的由来。
独占(Exclusive)缓存段,和S状态一样,也是和主内存内容保持一致的一份拷贝。区别在于,如果一个处理器持有了某个E状态的缓存段,那其他处理器就不能同时持有它,所以叫“独占”。这意味着,如果其他处理器原本也持有同一缓存段,那么它会马上变成“失效”状态。
已修改(Modified)缓存段,属于脏段,它们已经被所属的处理器修改了。如果一个段处于已修改状态,那么它在其他处理器缓存中的拷贝马上会变成失效状态,这个规律和E状态一样。此外,已修改缓存段如果被丢弃或标记为失效,那么先要把它的内容回写到内存中——这和回写模式下常规的脏段处理方式一样。
哈希(Hash)算法,即散列函数。 它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。 同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出
MD4 MD5 SHA
http://blog.jobbole.com/106733/
Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的”La”,此人现在在微软研究院)于1990年提出的一种基于消息传递的一致性算法。
http://baike.baidu.com/item/Paxos%20%E7%AE%97%E6%B3%95
ZAB 是 Zookeeper 原子广播协议的简称
整个ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以分为三个阶段,分别是:
发现 Discovery
同步 Synchronization
广播 Broadcast
组成ZAB协议的每一个分布式进程,都会循环执行这三个阶段,将这样一个循环称为一个主进程周期。
https://zzzvvvxxxd.github.io/2016/08/09/ZAB/
点击编辑的时候,利用redis进行加锁setNX完了之后 expire 一下
也可以用版本号进行控制
逐级排查(网络,磁盘,内存,cpu),数据库,日志,中间件等也可通过监控工具排查。
单例, 代理,模板,策略,命令
http://www.jianshu.com/p/bdf65e4afbb0
Dubbo[]是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用。
Random LoadBalance:随机,按权重比率设置随机概率。
RoundRobin LoadBalance:轮循,按公约后的权重比率设置轮循比率。
LeastActive LoadBalance:最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance:一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
快速失败,只发起一次调用,失败立即报错。
https://my.oschina.net/u/1378920/blog/693374
1)服务消费方(client)调用以本地调用方式调用服务;
2)client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
3)client stub找到服务地址,并将消息发送到服务端;
4)server stub收到消息后进行解码;
5)server stub根据解码结果调用本地的服务;
6)本地服务执行并将结果返回给server stub;
7)server stub将返回结果打包成消息并发送至消费方;
8)client stub接收到消息,并进行解码;
9)服务消费方得到最终结果。
异步模式使用与服务器多核,并发严重的场景
可提高服务吞吐量大,不容易受到冲击,可以采用并发策略,提高响应时间
缓存数据过期后的更新如何设计。
失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
命中:应用程序从cache中取数据,取到后返回。
更新:先把数据存到数据库中,成功后,再让缓存失效。
开闭原则(Open Close Principle)
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
里氏代换原则(Liskov Substitution Principle)
子类型必须能够替换掉它们的父类型。
依赖倒转原则(Dependence Inversion Principle)
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程
接口隔离原则(Interface Segregation Principle)
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少
组合/聚合复用原则
说要尽量的使用合成和聚合,而不是继承关系达到复用的目的
迪米特法则(Law Of Demeter)
迪米特法则其根本思想,是强调了类之间的松耦合,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成影响,也就是说,信息的隐藏促进了软件的复用。
单一职责原则(Single Responsibility Principle)
一个类只负责一项职责,应该仅有一个引起它变化的原因
http://www.banzg.com/archives/225.html
MVC 模式,即常见的 MVC 框架。
SSM SSH SSI等
可以通过token值进行防止重复提交,存放到redis中,在表单初始化的时候隐藏在表单中,添加的时候在移除。判断这个状态即可防止重复提交。
如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。本地缓存在并发使用时的注意事项。
·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
·提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
·串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
MYSQL默认是RepeatedRead级别
MyISAM: 拥有较高的插入,查询速度,但不支持事务
InnoDB :5.5版本后Mysql的默认数据库,事务型数据库的首选引擎,支持ACID事务,支持行级锁定
BDB: 源自Berkeley DB,事务型数据库的另一种选择,支持COMMIT和ROLLBACK等其他事务特性
Memory :所有数据置于内存的存储引擎,拥有极高的插入,更新和查询效率。但是会占用和数据量成正比的内存空间。并且其内容会在Mysql重新启动时丢失
Merge :将一定数量的MyISAM表联合而成一个整体,在超大规模数据存储时很有用
Archive :非常适合存储大量的独立的,作为历史记录的数据。因为它们不经常被读取。Archive拥有高效的插入速度,但其对查询的支持相对较差
Federated: 将不同的Mysql服务器联合起来,逻辑上组成一个完整的数据库。非常适合分布式应用
Cluster/NDB :高冗余的存储引擎,用多台数据机器联合提供服务以提高整体性能和安全性。适合数据量大,安全和性能要求高的应用
CSV: 逻辑上由逗号分割数据的存储引擎。它会在数据库子目录里为每个数据表创建一个.CSV文件。这是一种普通文本文件,每个数据行占用一个文本行。CSV存储引擎不支持索引。
BlackHole :黑洞引擎,写入的任何数据都会消失,一般用于记录binlog做复制的中继
另外,Mysql的存储引擎接口定义良好。有兴趣的开发者通过阅读文档编写自己的存储引擎。
http://baike.baidu.com/item/%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E
使用悲观锁 悲观锁本质是当前只有一个线程执行操作,结束了唤醒其他线程进行处理。
也可以缓存队列中锁定主键。
乐观锁是设定每次修改都不会冲突,只在提交的时候去检查,悲观锁设定每次修改都会冲突,持有排他锁。
行级锁分为共享锁和排他锁两种 共享锁又称读锁 排他锁又称写锁
http://www.jianshu.com/p/f40ec03fd0e8
查看慢日志(show [session|gobal] status ),定位慢查询,查看慢查询执行计划 根据执行计划确认优化方案
Explain sql
select_type:表示select类型。常见的取值有SIMPLE(简单表,即不使用连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(union中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个SELECT)等。
talbe:输出结果集的表。
type:表的连接类型。性能由高到底:system(表中仅有一行)、const(表中最多有一个匹配行)、eq_ref、ref、ref_null、index_merge、unique_subquery、index_subquery、range、idnex等
possible_keys:查询时,可能使用的索引
key:实际使用的索引
key_len:索引字段的长度
rows:扫描行的数量
Extra:执行情况的说明和描述
http://blog.csdn.net/hsd2012/article/details/51106285
产生死锁的原因主要是:
(1)系统资源不足。
(2) 进程运行推进的顺序不合适。
(3)资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
这里提供两个解决数据库死锁的方法:
1)重启数据库(谁用谁知道)
2)杀掉抢资源的进程:
先查哪些进程在抢资源:SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
杀掉它们:Kill trx_mysql_thread_id;
索引是通过复杂的算法,提高数据查询性能的手段。从磁盘io到内存io的转变
普通索引,主键,唯一,单列/多列索引建索引的几大原则
1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
http://tech.meituan.com/mysql-index.html
http://www.cnblogs.com/cq-home/p/3482101.html
“聚簇”就是索引和记录紧密在一起。
非聚簇索引 索引文件和数据文件分开存放,索引文件的叶子页只保存了主键值,要定位记录还要去查找相应的数据块。
B+是btree的变种,本质都是btree,btree+与B-Tree相比,B+Tree有以下不同点:
每个节点的指针上限为2d而不是2d+1。
内节点不存储data,只存储key;叶子节点不存储指针。
http://lcbk.net/9602.html
Btree 怎么分裂的,什么时候分裂,为什么是平衡的。
Key 超过1024才分裂B树为甚会分裂? 因为随着数据的增多,一个结点的key满了,为了保持B树的特性,就会产生分裂,就向红黑树和AVL树为了保持树的性质需要进行旋转一样!
A,atomic,原子性,要么都提交,要么都失败,不能一部分成功,一部分失败。
C,consistent,一致性,事物开始及结束后,数据的一致性约束没有被破坏
I,isolation,隔离性,并发事物间相互不影响,互不干扰。
D,durability,持久性,已经提交的事物对数据库所做的更新必须永久保存。即便发生崩溃,也不能被回滚或数据丢失。
避免在where子句中对字段进行is null判断
应尽量避免在where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
避免在where 子句中使用or 来连接条件
in 和not in 也要慎用
Like查询(非左开头)
使用NUM=@num参数这种
where 子句中对字段进行表达式操作num/2=XX
在where子句中对字段进行函数操作
由于复合索引的组合索引,类似多个木板拼接在一起,如果中间断了就无法用了,所以要能用到复合索引,首先开头(第一列)要用上,比如index(a,b) 这种,我们可以select table tname where a=XX 用到第一列索引 如果想用第二列 可以 and b=XX 或者and b like‘TTT%’
mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。
如果查询的两个表大小相当,那么用in和exists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:
not in 和not exists如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
1.EXISTS只返回TRUE或FALSE,不会返回UNKNOWN。
2.IN当遇到包含NULL的情况,那么就会返回UNKNOWN。
在分库分表时可能会生成重复主键 利用自增比例达到唯一 自增1 2,3 等
https://yq.aliyun.com/articles/38438
根据实际情况说明
我们公司用activeMQ 因为业务比较简单 只有转码功能,而amq比较简单
如果是分布式的建议用kafka
http://blog.csdn.net/sunxinhere/article/details/7968886
基本都是对数据进行持久化,多盘存储
集群是保证服务可靠性的一种方式,同时可以通过水平扩展以提升消息吞吐能力。RabbitMQ是用分布式程序设计语言erlang开发的,所以天生就支持集群。接下来,将介绍RabbitMQ分布式消息处理方式、集群模式、节点类型,并动手搭建一个高可用集群环境,最后通过java程序来验证集群的高可用性。
1. 三种分布式消息处理方式
RabbitMQ分布式的消息处理方式有以下三种:
1、Clustering:不支持跨网段,各节点需运行同版本的Erlang和RabbitMQ, 应用于同网段局域网。
2、Federation:允许单台服务器上的Exchange或Queue接收发布到另一台服务器上Exchange或Queue的消息, 应用于广域网,。
3、Shovel:与Federation类似,但工作在更低层次。
RabbitMQ对网络延迟很敏感,在LAN环境建议使用clustering方式;在WAN环境中,则使用Federation或Shovel。我们平时说的RabbitMQ集群,说的就是clustering方式,它是RabbitMQ内嵌的一种消息处理方式,而Federation或Shovel则是以plugin形式存在。
https://my.oschina.net/jiaoyanli/blog/822011
https://www.ibm.com/developerworks/cn/opensource/os-cn-RabbitMQ/
LPUSH LPUSHX RPUSH RPUSHX LPOP RPOP BLPOP BRPOP LLEN LRANGE
https://redis.readthedocs.io/en/2.4/list.html
字符串(strings):存储整数(比如计数器)和字符串(废话。。),有些公司也用来存储json/pb等序列化数据,并不推荐,浪费内存
哈希表(hashes):存储配置,对象(比如用户、商品),优点是可以存取部分key,对于经常变化的或者部分key要求atom操作的适合
列表(lists):可以用来存最新用户动态,时间轴,优点是有序,确定是元素可重复,不去重
集合(sets):无序,唯一,对于要求严格唯一性的可以使用
有序集合(sorted sets):集合的有序版,很好用,对于排名之类的复杂场景可以考虑https://redis.readthedocs.io/en/2.4/list.html
持久化方式:RDB时间点快照 AOF记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。
内存设置 maxmemory used_memory
虚拟内存: vm-enabled yes
3.0采用Cluster方式,
Redis集群相对单机在功能上存在一些限制, 需要开发人员提前了解,
在使用时做好规避。 限制如下:
1) key批量操作支持有限。 如mset、 mget, 目前只支持具有相同slot值的
ke
y执
行批量操作。 对于映射为不同slot值的key由于执行mget、 mget等操作可
能存在于多个节点上因此不被支持。
2) key事务操作支持有限。 同理只支持多key在同一节点上的事务操
作, 当多个key分布在不同的节点上时无法使用事务功能。
3) key作为数据分区的最小粒度, 因此不能将一个大的键值对象如
ha
sh、 list等映射到不同的节点。
4) 不支持多数据库空间。 单机下的Redis可以支持16个数据库, 集群模
式下只能使用一个数据库空间, 即db0。
5) 复制结构只支持一层, 从节点只能复制主节点, 不支持嵌套树状复
制结构。
Redis Cluster是Redis的分布式解决方案, 在3.0版本正式推出, 有效地解
决了Redis分布式方面的需求。 当遇到单机内存、 并发、 流量等瓶颈时, 可
以采用Cluster架构方案达到负载均衡的目的。 之前, Redis分布式方案一般
有两种:
·客户端分区方案, 优点是分区逻辑可控, 缺点是需要自己处理数据路
由、 高可用、 故障转移等问题。
·代理方案, 优点是简化客户端分布式逻辑和升级维护便利, 缺点是加
重架构部署复杂度和性能损耗。
现在官方为我们提供了专有的集群方案: Redis Cluster, 它非常优雅地
解决了Redis集群方面的问题, 因此理解应用好Redis Cluster将极大地解放我
们使用分布式Redis的工作量, 同时它也是学习分布式存储的绝佳案例。
LRU(近期最少使用算法)TTL(超时算法) 去除ttl最大的键值
http://wiki.jikexueyuan.com/project/redis/data-elimination-mechanism.html
http://www.infoq.com/cn/articles/tq-redis-memory-usage-optimization-storage
http://www.redis.cn/topics/cluster-tutorial.html
集群方式的区别,3采用Cluster,2采用客户端分区方案和代理方案
通信过程说明:
1) 集群中的每个节点都会单独开辟一个TCP通道, 用于节点之间彼此
通信, 通信端口号在基础端口上加10000。
2) 每个节点在固定周期内通过特定规则选择几个节点发送ping消息。
3) 接收到ping消息的节点用pong消息作为响应。
当缓存使用 持久化使用
基于libevent的事件处理
内置内存存储方式SLab Allocation机制
并不单一的数据删除机制
基于客户端的分布式系统
变化频繁,具有不稳定性的数据,不需要实时入库, (比如用户在线
状态、在线人数..)
门户网站的新闻等,觉得页面静态化仍不能满足要求,可以放入
到memcache中.(配合jquey的ajax请求)
Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。
Redis的内存管理主要通过源码中zmalloc.h和zmalloc.c两个文件来实现的。
在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。
http://lib.csdn.net/article/redis/55323
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
2.服务器角度,利用setnx实现锁。
MULTI,EXEC,DISCARD,WATCH 四个命令是 Redis 事务的四个基础命令。其中:
MULTI,告诉 Redis 服务器开启一个事务。注意,只是开启,而不是执行
EXEC,告诉 Redis 开始执行事务
DISCARD,告诉 Redis 取消事务
WATCH,监视某一个键值对,它的作用是在事务执行之前如果监视的键值被修改,事务会被取消。
可以利用watch实现cas乐观锁
http://wiki.jikexueyuan.com/project/redis/transaction-mechanism.html
http://www.jianshu.com/p/d777eb9f27df
Raft采用心跳机制触发Leader选举。系统启动后,全部节点初始化为Follower,term为0.节点如果收到了RequestVote或者AppendEntries,就会保持自己的Follower身份。如果一段时间内没收到AppendEntries消息直到选举超时,说明在该节点的超时时间内还没发现Leader,Follower就会转换成Candidate,自己开始竞选Leader。一旦转化为Candidate,该节点立即开始下面几件事情:
1、增加自己的term。
2、启动一个新的定时器。
3、给自己投一票。
4、向所有其他节点发送RequestVote,并等待其他节点的回复。
如果在这过程中收到了其他节点发送的AppendEntries,就说明已经有Leader产生,自己就转换成Follower,选举结束。
如果在计时器超时前,节点收到多数节点的同意投票,就转换成Leader。同时向所有其他节点发送AppendEntries,告知自己成为了Leader。
每个节点在一个term内只能投一票,采取先到先得的策略,Candidate前面说到已经投给了自己,Follower会投给第一个收到RequestVote的节点。每个Follower有一个计时器,在计时器超时时仍然没有接受到来自Leader的心跳RPC, 则自己转换为Candidate, 开始请求投票,就是上面的的竞选Leader步骤。
如果多个Candidate发起投票,每个Candidate都没拿到多数的投票(Split Vote),那么就会等到计时器超时后重新成为Candidate,重复前面竞选Leader步骤。
Raft协议的定时器采取随机超时时间,这是选举Leader的关键。每个节点定时器的超时时间随机设置,随机选取配置时间的1倍到2倍之间。由于随机配置,所以各个Follower同时转成Candidate的时间一般不一样,在同一个term内,先转为Candidate的节点会先发起投票,从而获得多数票。多个节点同时转换为Candidate的可能性很小。即使几个Candidate同时发起投票,在该term内有几个节点获得一样高的票数,只是这个term无法选出Leader。由于各个节点定时器的超时时间随机生成,那么最先进入下一个term的节点,将更有机会成为Leader。连续多次发生在一个term内节点获得一样高票数在理论上几率很小,实际上可以认为完全不可能发生。一般1-2个term类,Leader就会被选出来。
Sentinel的选举流程
Sentinel集群正常运行的时候每个节点epoch相同,当需要故障转移的时候会在集群中选出Leader执行故障转移操作。Sentinel采用了Raft协议实现了Sentinel间选举Leader的算法,不过也不完全跟论文描述的步骤一致。Sentinel集群运行过程中故障转移完成,所有Sentinel又会恢复平等。Leader仅仅是故障转移操作出现的角色。
选举流程
1、某个Sentinel认定master客观下线的节点后,该Sentinel会先看看自己有没有投过票,如果自己已经投过票给其他Sentinel了,在2倍故障转移的超时时间自己就不会成为Leader。相当于它是一个Follower。
2、如果该Sentinel还没投过票,那么它就成为Candidate。
3、和Raft协议描述的一样,成为Candidate,Sentinel需要完成几件事情
1)更新故障转移状态为start
2)当前epoch加1,相当于进入一个新term,在Sentinel中epoch就是Raft协议中的term。
3)更新自己的超时时间为当前时间随机加上一段时间,随机时间为1s内的随机毫秒数。
4)向其他节点发送is-master-down-by-addr命令请求投票。命令会带上自己的epoch。
5)给自己投一票,在Sentinel中,投票的方式是把自己master结构体里的leader和leader_epoch改成投给的Sentinel和它的epoch。
4、其他Sentinel会收到Candidate的is-master-down-by-addr命令。如果Sentinel当前epoch和Candidate传给他的epoch一样,说明他已经把自己master结构体里的leader和leader_epoch改成其他Candidate,相当于把票投给了其他Candidate。投过票给别的Sentinel后,在当前epoch内自己就只能成为Follower。
5、Candidate会不断的统计自己的票数,直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum(quorum可以参考《redis sentinel设计与实现》)。Sentinel比Raft协议增加了quorum,这样一个Sentinel能否当选Leader还取决于它配置的quorum。
6、如果在一个选举时间内,Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了。
7、如果在一个epoch内,没有一个Candidate获得更多的票数。那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票。
8、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader。
9、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel。其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。
http://weizijun.cn/2015/04/30/Raft%E5%8D%8F%E8%AE%AE%E5%AE%9E%E6%88%98%E4%B9%8BRedis%20Sentinel%E7%9A%84%E9%80%89%E4%B8%BELeader%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
RDB 定时快照方式(snapshot): 定时备份,可能会丢失数据
AOF 基于语句追加方式 只追加写操作
AOF 持久化和 RDB 持久化的最主要区别在于,前者记录了数据的变更,而后者是保存了数据本身
redis replication redis-migrate-tool等方式
ElasticSearch(简称ES)是一个分布式、Restful的搜索及分析服务器,设计用于分布式计算;能够达到实时搜索,稳定,可靠,快速。和Apache Solr一样,它也是基于Lucence的索引服务器,而ElasticSearch对比Solr的优点在于:
- 轻量级:安装启动方便,下载文件之后一条命令就可以启动。
- Schema free:可以向服务器提交任意结构的JSON对象,Solr中使用schema.xml指定了索引结构。
- 多索引文件支持:使用不同的index参数就能创建另一个索引文件,Solr中需要另行配置。
- 分布式:Solr Cloud的配置比较复杂。
倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。
使用bulk API
初次索引的时候,把 replica 设置为 0
增大 threadpool.index.queue_size
增大 indices.memory.index_buffer_size
增大 index.translog.flush_threshold_ops
增大 index.translog.sync_interval
增大 index.engine.robin.refresh_interval
http://www.jianshu.com/p/5eeeeb4375d4
索引(Index):
在Lucene中一个索引是放在一个文件夹中的。
如上图,同一文件夹中的所有的文件构成一个Lucene索引。
段(Segment):
一个索引可以包含多个段,段与段之间是独立的,添加新文档可以生成新的段,不同的段可以合并。
如上图,具有相同前缀文件的属同一个段,图中共三个段 “_0” 和 “_1”和“_2”。
segments.gen和segments_X是段的元数据文件,也即它们保存了段的属性信息。
文档(Document):
文档是我们建索引的基本单位,不同的文档是保存在不同的段中的,一个段可以包含多篇文档。
新添加的文档是单独保存在一个新生成的段中,随着段的合并,不同的文档合并到同一个段中。
域(Field):
一篇文档包含不同类型的信息,可以分开索引,比如标题,时间,正文,作者等,都可以保存在不同的域里。
不同域的索引方式可以不同,在真正解析域的存储的时候,我们会详细解读。
词(Term):
词是索引的最小单位,是经过词法分析和语言处理后的字符串。
1. JAVA 中的几种基本数据类型是什么,各自占用多少字节。
数据类型 | 关键字 | 内置类 | 内存占用字节数 |
布尔型 | boolean | Boolean | 1字节 |
字符型 | char | Character | 2字节 |
字节型 | byte | Byte | 1字节 |
短整型 | short | Short | 2字节 |
整形 | int | Integer | 4字节 |
长整型 | long | Long | 8字节 |
单精度型 | float | Float | 4字节 |
双精度型 | double | Double | 8 |
2. String 类能被继承吗,为什么。
3. String,Stringbuffer,StringBuilder的区别。
4. ArrayList 和 LinkedList 有什么区别。
5. 讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候,他们的执行顺序。
6. 用过哪些 Map 类,都有什么区别,HashMap 是线程安全的吗,并发下使用的 Map 是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
7. JAVA8 的 ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
9. 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。
10. 继承和聚合的区别在哪。
11. 讲讲你理解的 nio。他和 bio 的区别是啥,谈谈 reactor 模型。
12. 反射的原理,反射创建类实例的三种方式是什么。
13. 反射中,Class.forName 和 ClassLoader 区别。
14. 描述动态代理的几种实现方式,分别说出相应的优缺点。
15. 动态代理与cglib 实现的区别。
16. 为什么CGlib 方式可以对接口实现代理。
17. final的用途。
18. 出三种单例模式实现。
19. 如何在父类中为子类自动完成所有的 hashcode 和 equals 实现?这么做有何优劣。
20. 请结合 OO 设计理念,谈谈访问修饰符 public、private、protected、default 在应用设计中的作用。
21. 深拷贝和浅拷贝区别。
22. 数组和链表数据结构描述,各自的时间复杂度。
23. error 和 exception 的区别,CheckedException,RuntimeException 的区别。
24. 请列出 5 个运行时异常。
25. 在自己的代码中,如果创建一个 java.lang.String 对象,这个对象是否可以被类加载器加载?为什么。
26. 说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景下需要重新实现这两个方法。
27. 在 jdk1.5 中,引入了泛型,泛型的存在是用来解决什么问题。
28. 这样的 a.hashcode() 有什么用,与 a.equals(b)有什么关系。
29. 有没有可能 2 个不相等的对象有相同的 hashcode。
30. Java 中的 HashSet 内部是如何工作的。
31. 什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
JVM 知识
1. 什么情况下会发生栈内存溢出。
2. JVM 的内存结构,Eden 和 Survivor 比例。
3. jvm 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的 jvm 参数。
4. 你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms,包括原理,流程,优缺点
5. 垃圾回收算法的实现原理。
6. 当出现了内存溢出,你怎么排错。
7. JVM 内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。
8. 简单说说你了解的类加载器。
9. 讲讲 JAVA 的反射机制。
10. 你们线上应用的 JVM 参数有哪些。
11. g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择。
12. 请解释如下 jvm 参数的含义:
系统学习JVM知识这篇文章讲的比较清楚
https://yq.aliyun.com/articles/434362
开源框架知识
1. 简单讲讲 tomcat 结构,以及其类加载器流程。
2. tomcat 如何调优,涉及哪些参数。
3. 讲讲 Spring 加载流程。
4. 讲讲 Spring 事务的传播属性。
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
5. Spring 如何管理事务的。
6. Spring 怎么配置事务(具体说出一些关键的 xml元素)。
7. 说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的。
8. Springmvc 中 DispatcherServlet初始化过程。
操作系统
1. Linux 系统下你关注过哪些内核参数,说说你知道的。
2. Linux 下 IO 模型有几种,各自的含义是什么。
3. epoll 和 poll 有什么区别。
4. 平时用到哪些 Linux 命令。
5. 用一行命令查看文件的最后五行。
6. 用一行命令输出正在运行的 java 进程。
7. 介绍下你理解的操作系统中线程切换过程。
8. 进程和线程的区别。
多线程
1. 多线程的几种实现方式,什么是线程安全。
2. volatile 的原理,作用,能代替锁么。
3. 画一个线程的生命周期状态图。
4. sleep 和 wait 的区别。
5. Lock 与 Synchronized 的区别。
6. synchronized 的原理是什么,解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁。
7. 用过哪些原子类,他们的原理是什么。
8. 用过线程池吗,newCache 和 newFixed 有什么区别,他们的原理简单概括下,构造函数的各个参数的含义是什么,比如 coreSize,maxsize 等。
9. 线程池的关闭方式有几种,各自的区别是什么。
10. 假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有 10 个线程同时调用它,如何做到。
11. spring 的 controller 是单例还是多例,怎么保证并发的安全。
12. 用三个线程按顺序循环打印 abc 三个字母,比如 abcabcabc。
13. ThreadLocal 用过么,用途是什么,原理是什么,用的时候要注意什么。
14. 如果让你实现一个并发安全的链表,你会怎么做。
15. 有哪些无锁数据结构,他们实现的原理是什么。
16. 讲讲 java 同步机制的 wait 和 notify。
17. 多线程如果线程挂住了怎么办。
18. countdowlatch 和 cyclicbarrier的内部原理和用法,以及相互之间的差别。
19. 使用 synchronized 修饰静态方法和非静态方法有什么区别。
20. 简述 ConcurrentLinkedQueue 和 LinkedBlockingQueue 的用处和不同之处。
21. 导致线程死锁的原因?怎么解除线程死锁。
22. 非常多个线程(可能是不同机器),相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案。
23. 正确使用 Volatile 变量
TCP 与 HTTP
1. http1.0 和 http1.1 有什么区别。
2. TCP 三次握手和四次挥手的流程,为什么断开连接要 4次,如果握手只有两次,会出现什么。
3. TIME_WAIT 和 CLOSE_WAIT 的区别。
4. 说说你知道的几种 HTTP 响应码,比如 200, 302, 404。
5. 当你用浏览器打开一个链接的时候,计算机做了哪些工作步骤。
6. TCP/IP 如何保证可靠性,说说 TCP 头的结构。
7. 如何避免浏览器缓存。
8. 简述 Http 请求 get 和 post 的区别以及数据包格式。
9. 简述 HTTP 请求的报文格式。
10. HTTPS 的加密方式是什么,讲讲整个加密解密流程。
架构设计与分布式
1. 常见的缓存策略有哪些,你们项目中用到了什么缓存系统,如何设计的。
2. 用 java 自己实现一个 LRU。
3. 分布式集群下如何做到唯一序列号。
4. 设计一个秒杀系统,30 分钟没付款就自动关闭交易。
5. 如何使用 redis 和 zookeeper 实现分布式锁?有什么区别优缺点,分别适用什么场景。
6. 如果有人恶意创建非法连接,怎么解决。
7. 分布式事务的原理,优缺点,如何使用分布式事务。
8. 什么是一致性 hash。
9. 什么是 restful,讲讲你理解的 restful。
10. 如何设计建立和保持 100w 的长连接。
11. 如何防止缓存雪崩。
12. 解释什么是 MESI 协议(缓存一致性)。
13. 说说你知道的几种 HASH 算法,简单的也可以。
14. 什么是 paxos 算法。
15. 什么是 zab 协议。
16. 一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新。
17. 线上系统突然变得异常缓慢,你如何查找问题。
18. 说说你平时用到的设计模式。
19. Dubbo 的原理,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现。重试转发,快速失败的策略是怎样的。
20. 一次 RPC 请求的流程是什么。
21. 异步模式的用途和意义。
22. 缓存数据过期后的更新如何设计。
23. 编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用。
24. 设计一个社交网站中的“私信”功能,要求高并发、可扩展等等。画一下架构图。
25. MVC 模式,即常见的 MVC 框架。
26. 聊了下曾经参与设计的服务器架构。
27. 应用服务器怎么监控性能,各种方式的区别。
28. 如何设计一套高并发支付方案,架构如何设计。
29. 如何实现负载均衡,有哪些算法可以实现。
30. Zookeeper 的用途,选举的原理是什么。
31. Mybatis 的底层实现原理。
32. 请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存。
33. 请思考一个方案,实现分布式环境下的 countDownLatch。
34. 后台系统怎么防止请求重复提交。
35. 如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。本地缓存在并发使用时的注意事项。
36. 描述一个服务从发布到被消费的详细过程。
37. 讲讲你理解的服务治理。
38. 如何做到接口的幂等性。
39.redis和memcached的区别
算法
1. 10 亿个数字里里面找最小的 10 个。
2. 有 1 亿个数字,其中有 2 个是重复的,快速找到它,时间和空间要最优。
3. 2 亿个随机生成的无序整数,找出中间大小的值。
4. 给一个不知道长度的(可能很大)输入字符串,设计一种方案,将重复的字符排重。
5. 遍历二叉树。
6. 有 3n+1 个数字,其中 3n 个中是重复的,只有 1 个是不重复的,怎么找出来。
7. 写一个字符串反转函数。
8. 常用的排序算法,快排,归并、冒泡。 快排的最优时间复杂度,最差复杂度。冒泡排序的优化方案。
9. 二分查找的时间复杂度,优势。
10. 一个已经构建好的 TreeSet,怎么完成倒排序。
11. 什么是 B+树,B-树,列出实际的使用场景。
SQL
1. 行转列
姓名 课程 分数
---------- ---------- -----------
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94
2. MySQL存储引擎- MyISAM与InnoDB区别
中间件
Dubbo提供了多种均衡策略,缺省为random随机调用。
Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance
一致性Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
集群容错模式:
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。(缺省)
通常用于读操作,但重试会带来更长延迟。
可通过retries="2"来设置重试次数(不含第一次)。正是文章刚开始说的那种情况.
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。
通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。
通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。
通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。
通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
可通过forks="2"来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0开始支持)
通常用于通知所有提供者更新缓存或日志等本地资源信息。
重试次数配置如:(failover集群模式生效)
http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency
Arrays.sort实现原理和Collection实现原理 foreach和while的区别(编译之后) 线程池的种类,区别和使用场景 分析线程池的实现原理和线程的调度过程 线程池如何调优 线程池的最大线程数目根据什么确定 动态代理的几种方式 HashMap的并发问题 了解LinkedHashMap的应用吗 反射的原理,反射创建类实例的三种方式是什么? cloneable接口实现原理,浅拷贝or深拷贝 Java NIO使用 hashtable和hashmap的区别及实现原理,hashmap会问到数组索引,hash碰撞怎么解决 arraylist和linkedlist区别及实现原理 反射中,Class.forName和ClassLoader区别 String,Stringbuffer,StringBuilder的区别? 有没有可能2个不相等的对象有相同的hashcode 简述NIO的最佳实践,比如netty,mina TreeMap的实现原理
类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,他们的执行顺序 JVM内存分代 Java 8的内存分代改进 JVM垃圾回收机制,何时触发MinorGC等操作 jvm中一次完整的GC流程(从ygc到fgc)是怎样的,重点讲讲对象如何晋升到老年代,几种主要的jvm参数等 你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms,g1 新生代和老生代的内存回收策略 Eden和Survivor的比例分配等 深入分析了Classloader,双亲委派机制 JVM的编译优化 对Java内存模型的理解,以及其在并发中的应用 指令重排序,内存栅栏等 OOM错误,stackoverflow错误,permgen space错误 JVM常用参数 tomcat结构,类加载器流程 volatile的语义,它修饰的变量一定线程安全吗 g1和cms区别,吞吐量优先和响应优先的垃圾收集器选择 说一说你对环境变量classpath的理解?如果一个类不在classpath下,为什么会抛出ClassNotFoundException异常,如果在不改变这个类路径的前期下,怎样才能正确加载这个类? 说一下强引用、软引用、弱引用、虚引用以及他们之间和gc的关系
ThreadLocal用过么,原理是什么,用的时候要注意什么 Synchronized和Lock的区别 synchronized 的原理,什么是自旋锁,偏向锁,轻量级锁,什么叫可重入锁,什么叫公平锁和非公平锁 concurrenthashmap具体实现及其原理,jdk8下的改版 用过哪些原子类,他们的参数以及原理是什么 cas是什么,他会产生什么问题(ABA问题的解决,如加入修改次数、版本号) 如果让你实现一个并发安全的链表,你会怎么做 简述ConcurrentLinkedQueue和LinkedBlockingQueue的用处和不同之处 简述AQS的实现原理 countdowlatch和cyclicbarrier的用法,以及相互之间的差别? concurrent包中使用过哪些类?分别说说使用在什么场景?为什么要使用? LockSupport工具 Condition接口及其实现原理 Fork/Join框架的理解 jdk8的parallelStream的理解 分段锁的原理,锁力度减小的思考
Spring AOP与IOC的实现原理 Spring的beanFactory和factoryBean的区别 为什么CGlib方式可以对接口实现代理? RMI与代理模式 Spring的事务隔离级别,实现原理 对Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说aop中的几个术语,它们是怎么相互工作的? Mybatis的底层实现原理 MVC框架原理,他们都是怎么做url路由的 spring boot特性,优势,适用场景等 quartz和timer对比 spring的controller是单例还是多例,怎么保证并发的安全
Dubbo的底层实现原理和机制 描述一个服务从发布到被消费的详细过程 分布式系统怎么做服务治理 接口的幂等性的概念 消息中间件如何解决消息丢失问题 Dubbo的服务请求失败怎么处理 重连机制会不会造成错误 对分布式事务的理解 如何实现负载均衡,有哪些算法可以实现? Zookeeper的用途,选举的原理是什么? 数据的垂直拆分水平拆分。 zookeeper原理和适用场景 zookeeper watch机制 redis/zk节点宕机如何处理 分布式集群下如何做到唯一序列号 如何做一个分布式锁 用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗 MQ系统的数据如何保证不丢失 列举出你能想到的数据库分库分表策略;分库分表后,如何解决全表查询的问题。
海量url去重类问题(布隆过滤器) 数组和链表数据结构描述,各自的时间复杂度 二叉树遍历 快速排序 BTree相关的操作 在工作中遇到过哪些设计模式,是如何应用的 hash算法的有哪几种,优缺点,使用场景 什么是一致性hash paxos算法 在装饰器模式和代理模式之间,你如何抉择,请结合自身实际情况聊聊 代码重构的步骤和原因,如果理解重构到模式?
MySQL InnoDB存储的文件结构 索引树是如何维护的? 数据库自增主键可能的问题 MySQL的几种优化 mysql索引为什么使用B+树 数据库锁表的相关处理 索引失效场景 高并发下如何做到安全的修改同一行数据,乐观锁和悲观锁是什么,INNODB的行级锁有哪2种,解释其含义 数据库会死锁吗,举一个死锁的例子,mysql怎么解决死锁
Redis的并发竞争问题如何解决了解Redis事务的CAS操作吗 缓存机器增删如何对系统影响最小,一致性哈希的实现 Redis持久化的几种方式,优缺点是什么,怎么实现的 Redis的缓存失效策略 缓存穿透的解决办法 redis集群,高可用,原理 mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据 用Redis和任意语言实现一段恶意登录保护的代码,限制1小时内每用户Id最多只能登录5次 redis的数据淘汰策略
http1.0和http1.1有什么区别 TCP/IP协议 TCP三次握手和四次挥手的流程,为什么断开连接要4次,如果握手只有两次,会出现什么 TIME_WAIT和CLOSE_WAIT的区别 说说你知道的几种HTTP响应码 当你用浏览器打开一个链接的时候,计算机做了哪些工作步骤 TCP/IP如何保证可靠性,数据包有哪些数据组成 长连接与短连接 Http请求get和post的区别以及数据包格式 简述tcp建立连接3次握手,和断开连接4次握手的过程;关闭连接时,出现TIMEWAIT过多是由什么原因引起,是出现在主动断开方还是被动断开方。
maven解决依赖冲突,快照版和发行版的区别 Linux下IO模型有几种,各自的含义是什么 实际场景问题,海量登录日志如何排序和处理SQL操作,主要是索引和聚合函数的应用 实际场景问题解决,典型的TOP K问题 线上bug处理流程 如何从线上日志发现问题 linux利用哪些命令,查找哪里出了问题(例如io密集任务,cpu过度) 场景问题,有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有10个线程同时调用它,如何做到。 用三个线程按顺序循环打印abc三个字母,比如abcabcabc。 常见的缓存策略有哪些,你们项目中用到了什么缓存系统,如何设计的 设计一个秒杀系统,30分钟没付款就自动关闭交易(并发会很高) 请列出你所了解的性能测试工具
后台系统怎么防止请求重复提交?
一、Java基础
1.String类为什么是final的。
2.HashMap的源码,实现原理,底层结构。
3.反射中,Class.forName和classloader的区别
4.session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。
5.Java中的队列都有哪些,有什么区别。
6.Java的内存模型以及GC算法
7.Java7、Java8的新特性(baidu问的,好BT)
8.Java数组和链表两种结构的操作效率,在哪些情况下(从开头开始,从结尾开始,从中间开始),哪些操作(插入,查找,删除)的效率高
9.Java内存泄露的问题调查定位:jmap,jstack的使用等等
二、框架
1.struts1和struts2的区别
2.struts2和springMVC的区别
3.spring框架中需要引用哪些jar包,以及这些jar包的用途
4.srpingMVC的原理
5.springMVC注解的意思
6.spring中beanFactory和ApplicationContext的联系和区别
7.spring注入的几种方式
8.spring如何实现事物管理的
9.springIOC和AOP的原理
10.hibernate中的1级和2级缓存的使用方式以及区别原理
11.spring中循环注入的方式
三、多线程
1.Java创建线程之后,直接调用start()方法和run()的区别
2.常用的线程池模式以及不同线程池的使用场景
3.newFixedThreadPool此种线程池如果线程数达到最大值后会怎么办,底层原理。
4.多线程之间通信的同步问题,synchronized锁的是对象,衍伸出和synchronized相关很多的具体问题,例如同一个类不同方法都有synchronized锁,一个对象是否可以同时访问。或者一个类的static构造方法加上synchronized之后的锁的影响。
5.了解可重入锁的含义,以及ReentrantLock 和synchronized的区别
6.同步的数据结构,例如concurrentHashMap的源码理解以及内部实现原理,为什么他是同步的且效率高
7.atomicinteger和volatile等线程安全操作的关键字的理解和使用
8.线程间通信,wait和notify
9.定时线程的使用
10.场景:在一个主线程中,要求有大量(很多很多)子线程执行完之后,主线程才执行完成。多种方式,考虑效率。
四、网络通信
1.http是无状态通信,http的请求方式有哪些,可以自己定义新的请求方式么。
2.socket通信,以及长连接,分包,连接异常断开的处理。
3.socket通信模型的使用,AIO和NIO。
4.socket框架netty的使用,以及NIO的实现原理,为什么是异步非阻塞。
5.同步和异步,阻塞和非阻塞。
五、Linux
1.常用的linux下的命令
2.大的log文件中,统计异常出现的次数、排序,或者指定输出多少行多少列的内容。(主要考察awk)
3.linux下的调查问题思路:内存、CPU、句柄数、过滤、查找、模拟POST和GET请求等等场景
4.shell脚本中#!的作用
六、数据库MySql
1.MySql的存储引擎的不同
2.单个索引、联合索引、主键索引
3.Mysql怎么分表,以及分表后如果想按条件分页查询怎么办(如果不是按分表字段来查询的话,几乎效率低下,无解)
4.分表之后想让一个id多个表是自增的,效率实现
5.MySql的主从实时备份同步的配置,以及原理(从库读主库的binlog),读写分离
6.写SQL语句。。。
7.索引的数据结构,B+树
8.事物的四个特性,以及各自的特点(原子、隔离)等等,项目怎么解决这些问题
七、设计模式(写代码)
1.单例模式:饱汉、饿汉。以及饿汉中的延迟加载
2.工厂模式、装饰者模式、观察者模式。
八、算法
1.使用随机算法产生一个数,要求把1-1000W之间这些数全部生成。(考察高效率,解决产生冲突的问题)
2.两个有序数组的合并排序
3.一个数组的倒序
4.计算一个正整数的正平方根
5.说白了就是常见的那些查找排序算法(排序转载:http://mp.weixin.qq.com/s?__biz=MjM5MTAzMTE4Nw==&mid=204838393&idx=2&sn=e9b50c8ef689e2cb6436110a8dc148a3&scene=5#rd)
九、缓存
1.为什么用缓存,用过哪些缓存,redis和memcache的区别
2.redis的数据结构
3.redis的持久化方式,以及项目中用的哪种,为什么
4.redis集群的理解,怎么动态增加或者删除一个节点,而保证数据不丢失。(一致性哈希问题)
Arrays.sort实现原理和Collection实现原理
foreach和while的区别(编译之后)
线程池的种类,区别和使用场景
分析线程池的实现原理和线程的调度过程
线程池如何调优
线程池的最大线程数目根据什么确定
动态代理的几种方式
HashMap的并发问题
了解LinkedHashMap的应用吗
反射的原理,反射创建类实例的三种方式是什么?
cloneable接口实现原理,浅拷贝or深拷贝
Java NIO使用
hashtable和hashmap的区别及实现原理,hashmap会问到数组索引,hash碰撞怎么解决
arraylist和linkedlist区别及实现原理
反射中,Class.forName和ClassLoader区别
String,Stringbuffer,StringBuilder的区别?
有没有可能2个不相等的对象有相同的hashcode
简述NIO的最佳实践,比如netty,mina
TreeMap的实现原理
JVM相关
类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,他们的执行顺序
JVM内存分代
Java 8的内存分代改进
JVM垃圾回收机制,何时触发MinorGC等操作
jvm中一次完整的GC流程(从ygc到fgc)是怎样的,重点讲讲对象如何晋升到老年代,几种主要的jvm参数等
你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms,g1
新生代和老生代的内存回收策略
Eden和Survivor的比例分配等
深入分析了Classloader,双亲委派机制
JVM的编译优化
对Java内存模型的理解,以及其在并发中的应用
指令重排序,内存栅栏等
OOM错误,stackoverflow错误,permgen space错误
JVM常用参数
tomcat结构,类加载器流程
volatile的语义,它修饰的变量一定线程安全吗
g1和cms区别,吞吐量优先和响应优先的垃圾收集器选择
说一说你对环境变量classpath的理解?如果一个类不在classpath下,为什么会抛出ClassNotFoundException异常,如果在不改变这个类路径的前期下,怎样才能正确加载这个类?
说一下强引用、软引用、弱引用、虚引用以及他们之间和gc的关系
JUC/并发相关
ThreadLocal用过么,原理是什么,用的时候要注意什么
Synchronized和Lock的区别
synchronized 的原理,什么是自旋锁,偏向锁,轻量级锁,什么叫可重入锁,什么叫公平锁和非公平锁
concurrenthashmap具体实现及其原理,jdk8下的改版
用过哪些原子类,他们的参数以及原理是什么
cas是什么,他会产生什么问题(ABA问题的解决,如加入修改次数、版本号)
如果让你实现一个并发安全的链表,你会怎么做
简述ConcurrentLinkedQueue和LinkedBlockingQueue的用处和不同之处
简述AQS的实现原理
countdowlatch和cyclicbarrier的用法,以及相互之间的差别?
concurrent包中使用过哪些类?分别说说使用在什么场景?为什么要使用?
LockSupport工具
Condition接口及其实现原理
Fork/Join框架的理解
jdk8的parallelStream的理解
分段锁的原理,锁力度减小的思考
Spring
Spring AOP与IOC的实现原理
Spring的beanFactory和factoryBean的区别
为什么CGlib方式可以对接口实现代理?
RMI与代理模式
Spring的事务隔离级别,实现原理
对Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说aop中的几个术语,它们是怎么相互工作的?
Mybatis的底层实现原理
MVC框架原理,他们都是怎么做url路由的
spring boot特性,优势,适用场景等
quartz和timer对比
spring的controller是单例还是多例,怎么保证并发的安全
分布式相关
Dubbo的底层实现原理和机制
描述一个服务从发布到被消费的详细过程
分布式系统怎么做服务治理
接口的幂等性的概念
消息中间件如何解决消息丢失问题
Dubbo的服务请求失败怎么处理
重连机制会不会造成错误
对分布式事务的理解
如何实现负载均衡,有哪些算法可以实现?
Zookeeper的用途,选举的原理是什么?
数据的垂直拆分水平拆分。
zookeeper原理和适用场景
zookeeper watch机制
redis/zk节点宕机如何处理
分布式集群下如何做到唯一序列号
如何做一个分布式锁
用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗
MQ系统的数据如何保证不丢失
列举出你能想到的数据库分库分表策略;分库分表后,如何解决全表查询的问题。
算法&数据结构&设计模式
海量url去重类问题(布隆过滤器)
数组和链表数据结构描述,各自的时间复杂度
二叉树遍历
快速排序
BTree相关的操作
在工作中遇到过哪些设计模式,是如何应用的
hash算法的有哪几种,优缺点,使用场景
什么是一致性hash
paxos算法
在装饰器模式和代理模式之间,你如何抉择,请结合自身实际情况聊聊
代码重构的步骤和原因,如果理解重构到模式?
数据库
MySQL InnoDB存储的文件结构
索引树是如何维护的?
数据库自增主键可能的问题
MySQL的几种优化
mysql索引为什么使用B+树
数据库锁表的相关处理
索引失效场景
高并发下如何做到安全的修改同一行数据,乐观锁和悲观锁是什么,INNODB的行级锁有哪2种,解释其含义
数据库会死锁吗,举一个死锁的例子,mysql怎么解决死锁
Redis&缓存相关
Redis的并发竞争问题如何解决了解Redis事务的CAS操作吗
缓存机器增删如何对系统影响最小,一致性哈希的实现
Redis持久化的几种方式,优缺点是什么,怎么实现的
Redis的缓存失效策略
缓存穿透的解决办法
redis集群,高可用,原理
mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
用Redis和任意语言实现一段恶意登录保护的代码,限制1小时内每用户Id最多只能登录5次
redis的数据淘汰策略
网络相关
http1.0和http1.1有什么区别
TCP/IP协议
TCP三次握手和四次挥手的流程,为什么断开连接要4次,如果握手只有两次,会出现什么
TIME_WAIT和CLOSE_WAIT的区别
说说你知道的几种HTTP响应码
当你用浏览器打开一个链接的时候,计算机做了哪些工作步骤
TCP/IP如何保证可靠性,数据包有哪些数据组成
长连接与短连接
Http请求get和post的区别以及数据包格式
简述tcp建立连接3次握手,和断开连接4次握手的过程;关闭连接时,出现TIMEWAIT过多是由什么原因引起,是出现在主动断开方还是被动断开方。
其他
maven解决依赖冲突,快照版和发行版的区别
Linux下IO模型有几种,各自的含义是什么
实际场景问题,海量登录日志如何排序和处理SQL操作,主要是索引和聚合函数的应用
实际场景问题解决,典型的TOP K问题
线上bug处理流程
如何从线上日志发现问题
linux利用哪些命令,查找哪里出了问题(例如io密集任务,cpu过度)
场景问题,有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有10个线程同时调用它,如何做到。
用三个线程按顺序循环打印abc三个字母,比如abcabcabc。
常见的缓存策略有哪些,你们项目中用到了什么缓存系统,如何设计的
设计一个秒杀系统,30分钟没付款就自动关闭交易(并发会很高)
请列出你所了解的性能测试工具
后台系统怎么防止请求重复提交?
周末在微信公共号看到一篇关于《线程的40个道题》的文章,由于今年工作之前参加过几次面试,所以觉得这篇文章总结的很好
只要读者朋友们耐心看完,并且在阅读过程中遇到自己疑惑的地方时自己能动手查一查做一做,我相信以后遇到很多关于线程上的问题都会迎刃而解。
打算用两个部分来写,第一便于读者阅读,第二谁也不能一口吃个胖子,相信我有的知识是需要时间的积累才能获得的,没有时间的积累谁也不能成为专家,只有时间才能将你打磨的更加完美。
原文出自“去哪技术沙龙”公众号 40个多线程问题总结,没有地址实在无法标注原文地址
这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过,但是本文写作的重心就是所有的问题都会按照自己的理解回答一遍,不会去看网上的答案。
一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓”知其然知其所以然”,”会用”只是”知其然”,”为什么用”才是”知其所以然”,只有达到”知其然知其所以然”的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个问题的看法:
(1)发挥多核CPU的优势
随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4核、8核甚至16核的也都不少见,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。单核CPU上所谓的”多线程”那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程”同时”运行罢了。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。
(2)防止阻塞
从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
(3)便于建模
这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。
比较常见的一个问题了,一般就是两种:
(1)继承Thread类
(2)实现Runnable接口
至于哪个好,不用说肯定是后者好,因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,面向接口编程也是设计模式6大原则的核心。
只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。
有点深的问题了,也看出一个Java程序员学习知识的广度。
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。
两个看上去有点像的类,都在java.util.concurrent下,都可以用来表示代码运行到某个点上,二者的区别在于:
(1)CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行
(2)CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务
(3)CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了
一个非常重要的问题,是每个学习、应用多线程的Java程序员都必须掌握的。理解volatile关键字的作用的前提是要理解Java内存模型,这里就不讲Java内存模型了,可以参见第31点,volatile关键字的作用主要有两个:
(1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据
(2)代码底层执行不像我们看到的高级语言—-Java程序这么简单,它的执行是Java代码–>字节码–>根据字节码执行对应的C/C++代码–>C/C++代码被编译成汇编语言–>和硬件电路交互,现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率
从实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger。
又是一个理论的问题,各式各样的答案有很多,我给出一个个人认为解释地最好的:如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。
这个问题有值得一提的地方,就是线程安全也是有几个级别的:
(1)不可变
像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用
(2)绝对线程安全
不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet
(3)相对线程安全
相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。
(4)线程非安全
这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类
死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:
(1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java
(2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid
另外提一点,Thread类提供了一个getStackTrace()方法也可以用于获取线程堆栈。这是一个实例方法,因此此方法是和具体线程实例绑定的,每次获取获取到的是具体某个线程当前运行的堆栈,
如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的
这个问题常问,sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器
这个问题很理论,但是很重要:
(1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用
(2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约
简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了
这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁
wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。
避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。
我也是在网上看到一道多线程面试题才知道有方法可以判断某个线程是否持有对象监视器:Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着“某条线程”指的是当前线程。
synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
(2)ReentrantLock可以获取各种锁的信息
(3)ReentrantLock可以灵活地实现多路通知
另外,二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word,这点我不能确定。
ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对Hashtable的最大优势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据吗?
首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。
因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
1、说一说Servlet生命周期
Servlet生命周期包括三部分:
初始化:Web容器加载servlet,调用init()方法
处理请求:当请求到达时,运行其service()方法。service()自动派遣运行与请求相对应的doXXX(doGet或者doPost)方法。
销毁:服务结束,web容器会调用servlet的distroy()方法销毁servlet。
2、get提交和post提交有何区别
(1)get一般用于从服务器上获取数据,post一般用于向服务器传送数据
(2)请求的时候参数的位置有区别,get的参数是拼接在url后面,用户在浏览器地址栏可以看到。post是放在http包的包体中。
比如说用户注册,你不能把用户提交的注册信息用get的方式吧,那不是说把用户的注册信息都显示在Url上了吗,是不安全的。
(3)能提交的数据有区别,get方式能提交的数据只能是文本,且大小不超过1024个字节,而post不仅可以提交文本还有二进制文件。
所以说想上传文件的话,那我们就需要使用post请求方式
(4)servlet在处理请求的时候分别对应使用doGet和doPost方式进行处理请求
3、JSP与Servlet有什么区别
Servlet是服务器端的程序,动态生成html页面发送到客户端,但是这样程序里会有很多out.println(),java与html语言混在一起
很乱,所以后来sun公司推出了JSP.其实JSP就是Servlet,每次运行的时候JSP都首先被编译成servlet文件,然后再被编译成
.class文件运行。有了jsp,在MVC项目中servlet不再负责动态生成页面,转而去负责控制程序逻辑的作用,控制jsp与javabean
之间的流转。
4、doGet与doPost方法的两个参数是什么
HttpServletRequest:封装了与请求相关的信息
HttpServletResponse:封装了与响应相关的信息
5、request.getAttribute()和request.getParameter
(1)有setAttribute,没有setParameter方法
(2)getParameter获取到的值只能是字符串,不可以是对象,而getAttribute获取到的值是Object类型的。
(3)通过form表单或者url来向另一个页面或者servlet传递参数的时候需要用getParameter获取值;getAttribute只能获取setAttribute的值
(4)setAttribute是应用服务器把这个对象放到该页面所对应的一块内存当中,当你的页面服务器重定向到另一个页面的时候,应用服务器
会把这块内存拷贝到另一个页面对应的内存当中。通过getAttribute可以取得你存下的值,当然这种方法可以用来传对象。
用session也是一样的道理,这是说request和session的生命周期不一样而已。
6、JSP有哪些内置对象,作用是什么?
名称 | 作用 |
request | 包含用户端请求的信息 |
response | 包含服务器传回客户端的响应信息 |
session | 与请求有关的会话期 |
pageContext | 管理网页属性 |
application | 服务器启动时创建,服务器关闭时停止,为多个应用程序保存信息 |
out | 向客户端输出数据 |
config | servlet的架构部件 |
page | 指网页本身 |
exception | 针对错误页面才可使用 |
7、四种会话跟踪技术作用域
(1)page:一个页面
(2)request::一次请求
(3)session:一次会话
(4)application:服务器从启动到停止。
7、JSP中动态INCLUDE和静态INCLUDE有什么区别
include指令用于把另一个页面包含到当前页面中,在什么时候包含的?再转换成servlet的时候包含进去的。
动态INCLUDE用jsp:include动作实现 <jsp:include page="included.jsp" flush="true" />它总是会检查所含文件中的变化,适合用于包含动态页面,
并且可以带参数.
静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面<%@ include file="included.htm" %>
8、forward和redirect的区别
转发与重定向
(1)从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送
的内容从哪里来的,所以它的地址栏还是原来的地址.redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是
新的URL.
(2)从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
(3)从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
(4)从效率来说
forward:高.
redirect:低.
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1) (2) 支持丰富数据类型,支持string,list,set,sorted set,hash (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行 (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 (2) redis的速度比memcached快很多 (3) redis可以持久化其数据
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3… 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
1)、存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,这样能保证数据的持久性。
2)、数据支持类型
Memcache对数据类型支持相对简单。
Redis有复杂的数据类型。
3)、使用底层模型不同
它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4),value大小
redis最大可以达到1GB,而memcache只有1MB
1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内
Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢? 如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点: 1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 2 、Redis支持数据的备份,即master-slave模式的数据备份。 3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。 (1)、会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
(2)、全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
(3)、队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。
(4),排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。
(5)、发布/订阅
最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
高可用(High Availability),是当一台服务器停止服务后,对于业务及用户毫无影响。 停止服务的原因可能由于网卡、路由器、机房、CPU负载过高、内存溢出、自然灾害等不可预期的原因导致,在很多时候也称单点问题。
(1)解决单点问题主要有2种方式:
主备方式 这种通常是一台主机、一台或多台备机,在正常情况下主机对外提供服务,并把数据同步到备机,当主机宕机后,备机立刻开始服务。 Redis HA中使用比较多的是keepalived,它使主机备机对外提供同一个虚拟IP,客户端通过虚拟IP进行数据操作,正常期间主机一直对外提供服务,宕机后VIP自动漂移到备机上。
优点是对客户端毫无影响,仍然通过VIP操作。 缺点也很明显,在绝大多数时间内备机是一直没使用,被浪费着的。
主从方式 这种采取一主多从的办法,主从之间进行数据同步。 当Master宕机后,通过选举算法(Paxos、Raft)从slave中选举出新Master继续对外提供服务,主机恢复后以slave的身份重新加入。 主从另一个目的是进行读写分离,这是当单机读写压力过高的一种通用型解决方案。 其主机的角色只提供写操作或少量的读,把多余读请求通过负载均衡算法分流到单个或多个slave服务器上。
缺点是主机宕机后,Slave虽然被选举成新Master了,但对外提供的IP服务地址却发生变化了,意味着会影响到客户端。 解决这种情况需要一些额外的工作,在当主机地址发生变化后及时通知到客户端,客户端收到新地址后,使用新地址继续发送新请求。
(2)数据同步 无论是主备还是主从都牵扯到数据同步的问题,这也分2种情况:
同步方式:当主机收到客户端写操作后,以同步方式把数据同步到从机上,当从机也成功写入后,主机才返回给客户端成功,也称数据强一致性。 很显然这种方式性能会降低不少,当从机很多时,可以不用每台都同步,主机同步某一台从机后,从机再把数据分发同步到其他从机上,这样提高主机性能分担同步压力。 在redis中是支持这杨配置的,一台master,一台slave,同时这台salve又作为其他slave的master。
异步方式:主机接收到写操作后,直接返回成功,然后在后台用异步方式把数据同步到从机上。 这种同步性能比较好,但无法保证数据的完整性,比如在异步同步过程中主机突然宕机了,也称这种方式为数据弱一致性。
Redis主从同步采用的是异步方式,因此会有少量丢数据的危险。还有种弱一致性的特例叫最终一致性,这块详细内容可参见CAP原理及一致性模型。
(3)方案选择 keepalived方案配置简单、人力成本小,在数据量少、压力小的情况下推荐使用。 如果数据量比较大,不希望过多浪费机器,还希望在宕机后,做一些自定义的措施,比如报警、记日志、数据迁移等操作,推荐使用主从方式,因为和主从搭配的一般还有个管理监控中心。
宕机通知这块,可以集成到客户端组件上,也可单独抽离出来。 Redis官方Sentinel支持故障自动转移、通知等,详情见低成本高可用方案设计(四)。
逻辑图:
分布式(distributed), 是当业务量、数据量增加时,可以通过任意增加减少服务器数量来解决问题。
集群时代 至少部署两台Redis服务器构成一个小的集群,主要有2个目的:
高可用性:在主机挂掉后,自动故障转移,使前端服务对用户无影响。 读写分离:将主机读压力分流到从机上。 可在客户端组件上实现负载均衡,根据不同服务器的运行情况,分担不同比例的读请求压力。
逻辑图:
当缓存数据量不断增加时,单机内存不够使用,需要把数据切分不同部分,分布到多台服务器上。 可在客户端对数据进行分片,数据分片算法详见C#一致性Hash详解、C#之虚拟桶分片。
逻辑图:
大规模分布式集群时代 当数据量持续增加时,应用可根据不同场景下的业务申请对应的分布式集群。 这块最关键的是缓存治理这块,其中最重要的部分是加入了代理服务。 应用通过代理访问真实的Redis服务器进行读写,这样做的好处是:
避免越来越多的客户端直接访问Redis服务器难以管理,而造成风险。 在代理这一层可以做对应的安全措施,比如限流、授权、分片。 避免客户端越来越多的逻辑代码,不但臃肿升级还比较麻烦。 代理这层无状态的,可任意扩展节点,对于客户端来说,访问代理跟访问单机Redis一样。 目前楼主公司使用的是客户端组件和代理两种方案并存,因为通过代理会影响一定的性能。 代理这块对应的方案实现有Twitter的Twemproxy和豌豆荚的codis。
逻辑图:
分布式缓存再向后是云服务缓存,对使用端完全屏蔽细节,各应用自行申请大小、流量方案即可,如淘宝OCS云服务缓存。 分布式缓存对应需要的实现组件有:
一个缓存监控、迁移、管理中心。 一个自定义的客户端组件,上图中的SmartClient。 一个无状态的代理服务。 N台服务器。
5:面向切面的原理
Spring提供了对AOP技术的良好封装,AOP称为面向切面编程,就是系统中有很多各不相干的类的方法,在这些众多的方法中要加入某种系统功能代码,例如:加入日志、权限判断、异常处理等,这种应用成为AOP。
实现AOP功能采用的是代理技术,客户端程序不再调用目标,而调用代理类,代理类与目标类对外具有相同的方法声明,有两种方式可以实现相同的方法声明,一是实现相同的接口,而是作为目标的子类在JDK中采用Proxy类产生动态代理的方式为某个接口生成实现类,如果要为某类个生成子类,则可以用CGLIB。(动态代理这方面的可以参考我的另一篇文章)
在生成的代理类的方法中加入系统功能和调用目标类的相应方法,系统功能的代理以Advice对象进行提供,显然要创建出代理对象,至少需要目标类和Advice类。
1.请讲下Java里面的容器
分两大类,Map和Collection。而Collection又有子接口List(数据存储顺序和插入顺序是一样的)、Set(里面的元素具有唯一性)
Map是存储键值对的,里面的健不可以重复,但值可以重复a. 对于List主要有ArrayList和LinkedList两种实现。实现的数据结构不同,所以主要的区别也都是和数据结构相关的。 ArrayList基于数组,随机访问快,而对于中间元素的插入删除效率比较低,而且需要考虑扩容问题。LinkedList,则 基于链表,和ArrayList提到的正相反,随机访问慢,但对于中间元素的插入和删除更有效率。Set也是一种Collection,和List比起来主要体现在元素唯一性。
2.请说下Iterator的作用
迭代器可以实现Collection接口的方法,可以一个一个地获取集合中的元素在遍历集合时 可判断是否有下一个元素
3.说下ArrayList和LinkedList的区别和联系,并说明什么情况下用它们
区别:ArrayList用于对象的随机访问速度快,没有顺序LinkedList实现机制是链表式的,和顺序有关,速度比ArrayList慢联系:ArrayList和LinkedList都是List接口的实现类当要快速获取一个值时,用ArrayList,用于顺序插入操作时,用LinkedList.
4.说下List,Set,Map三种集合各有什么特征
List集合中的元素可以重复,Set集合中的元素不可以重复Map集合用键-值映射存放对象,Map容器中的键对象不能重复,值对象可以重复
5.HashSet和TreeSet有什么区别,什么时候用它们
区别:HashSet中的元素不能重复,没有顺序TreeSet中的元素不能重复,但有顺序当集合中的元素需要排序时,用TreeSet一般情况下用HashSet,因为不需要排序,速度比TreeSet快
6.什么是泛型,怎么使用的,有什么好处?
答案
定义一个集合时,可以知道里面定义的是什么类型使用:在集合类型后面加< 数据类型 >使用泛型后,从集合中取得元素后就不用再用强转
7.什么是for each循环,它可以循环那些数据类型
答案
也可以叫增强型循环,通过对象拿到集合里的值,因为扩展性比较强,建议多使用可以用来循环集合和数组
8.写一个for each循环看看
for(Object object : list){System.out.println(object);}
9. 什么是强转怎么写的,有什么优缺点,一般要多用还是少用,为什么?
一般在继承的基础上用.一般是范围小的转换成范围大的,譬如byte可以转换成int,还可以子类转换成父类,反之则不行。
Person person=new Student();
Student student=(Student)person;
把运行期的强转为编译期的.
编译期不会出错.运行期容易出错.所以一般少用
10.HashMap和Hashtable有什么区别,一 般情况下常用那个?
HashMap的键-值都可以为空(null)Hashtable的键-值都不可以为空(null),线程安全 ,一般情况下用HashMap
11.Hashtable名字为什么没有驼峰命名
Hashtable的由来比较古老,当时还没有命名规范
12.Collections和Collection有什么区别
Collections是一个工具类,可以直接调用List和Set的方法Collection是一个接口,是List和Set集合的父接口
13.写出Collections的6个方法,并详细解释
sort():对集合进行排序shuffle():打乱集合中的元素顺序addAll():将一个集合添加到另一个集合中max():判断集合中的最大值min():判断集合中的最小值copy():将一个集合中的元素复制到另一个集合中去fill():将一个集合中的元素全部替换成指定的元素
14.Arrays类是做什么的,写出它的常用6个方法
Arrays是数组的一个工具类sort():对数组进行排序binarySearch():搜索数组指定元素的下标copyOf():复制数组中指定长度的元素deepEquals():比较两个数组的深度fill():把数组中的所有元素替换成指定元素equals():比较指定两个数组的元素是否相等
15.比较下集合和数组的优缺点
集合是多个对象的容器,可以将不同数据类型的多个对象组织在一起数组类型是有相同数据类型的数据集合,数组是很多语言都支持的底层数据结构,性能上是最高的
16.如何对一个对象排序,有几种方法
把对象放入List集合中,用Collections工具类调用sort()方法进行排序,但是这个类必须实现Compable接口才行把对象放在Set集合中,用TreeSet()实现类对集合直接排序
17.在集合里面怎么判断两个对象相等,要实现什么方法
equals方法
18.怎么样把集合转化成数组,或把数组转化为集合
19.分别写出List,Set,Map里面的5个常用方法
List:add()新增 clear()清除 contains()判断是否包含某个元素 indexOf()一个元素在集合中首次出现的位置set()把指定下标的元素替换成自定义元素Set:add()新增 clear()清除 contains()判断是否包含某个元素remove():把集合中指定下标的元素删掉size():返回集合长度Map: containsKey()是否包含指定key containsValue()是否包含指定value keySet()返回所有key put()添加元素 Remove()移除
20.HashMap与LinkedHashMap,和TreeMap的区别。
共同点:HashMap,LinkedHashMap,TreeMap都属于Map的实现类. 不同点: 1.HashMap里面存入的键值对在取出的时候是随机的, 2.TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。 3. LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现.21.HashMap怎么实现有序 可以转化放入TreeMap里面。
22.在List里面怎么去掉重复的数?
通过把List里面的数据放入HashSet可以去除重复
23.在List里面有几种排序?
答:两种:实现Comparable<Article>接口,实现里面的CompareTo方法进行排序。还有调用Collections.sort()方法排序!
24.说一下链表跟数组的区别
链表:用一组任意储存单元存放线性表的数据元素,并且通过指针链相接结点的序列称为链表。是一种常见的数据组织形式,它采用了动态分配内存的形式实现。需要时可以用new分配内存空间,不需要时用delete将已分配的空间释放,不会造成内存空间的浪费。不靠数组实现,没有下标。数组必须事先定义固定的长度,不能适应数据动态增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成数据浪费。在使用的时候还要数组初始化,注意数组的下标越界。
25.HashSet的理解
HashSet实现了Set接口,HashSet不保证集合的迭代顺序,允许使用Null元素。HashSet的底层使用了HashMap,使用HashMap实列进行对集合的元素进行操作,然后再封装成HashSet的操作。
26.什么类可以实现有序存储(除ArrayList以外)?一种按照插入的顺序排序,譬如LinkedList,LiskedHashMap,另外一种是插入后重新排序TreeSet,TreeMap
27.HashMap和ArrayList是不是都是线程不安全的?
ArrayList是线程不安全的;HashMap是线程不安全的;还有我们常见的一些JAVA集合都是线程不安全,这样做是为了提高性能
在JDK5以后提供了线程安全的并发包java.util.concurrent并发包,譬如里面的类CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap等
28.ArrayList集合加入1万条数据,应该怎么提高效率
因为ArrayList的底层是数组实现,并且数组的默认值是10,如果插入10000条要不断的扩容,耗费时间,所以我们调用ArrayList的指定容量的构造器方法ArrayList(int size) 就可以实现不扩容,就提高了性能
29.你知道HashMap底层是怎么实现的吗?
简单的说是一个数组,因为数组的性能比较好,数组里面放的是Entry类,HashMap类有一个叫做Entry的内部类。这个Entry类包含了key-value作为实例变量。当存储或者获取对象的时候,就根据哈希算法,对象的hashCode调用得到这个下标,以便实现快速访问.
30.怎样遍历List Set,Map
List 和Set可以通过一般for循环,迭代器循环,或者增强型循环来遍历, 其中一般for循环性能最快,迭代器循环可以判断和得到下一个值,for each增强型循环扩展性强,但性能稍低.
Map循环可以通过keySet得到Key的Set集合,然后遍历这个集合就可以得到所有的Value
31.Set为什么是不允许重复的。
set的实现机制不允许重复的32.ArrayList为什么要用for循环,为什么要用迭代器,又有什么好处。
for循环通过对象拿到集合里的值迭代器可以实现Collection接口的方法,可以一个一个地获取集合中的元素在遍历集合时 可判断是否有下一个元素
33.你对栈与队列了解多少你是怎么用的。
答:队列是一种数据结构,FIFO 先进先出有点类似与栈,只是在队列中第一个插入的数据项也会被最先删除,队列的两个基本操作:一个是插入一个数据项,即把一个数据项放入队尾另一个是移除一个数据项,即移除队头的数据项.
34.如果我要存取很多的数据,但是又不需要重复的,要选择什么容器,说一下为什么使用它,它是哪个的子类?
答:Set容器,它是不允许重复的,它是collection的子类
35.哪种方法可以得到Map的Key?
keySet()方法
Spring MVC Framework有这样一些特点:
- 1。它是基于组件技术的.全部的应用对象,无论控制器和视图,还是业务对象之类的都是java组件.并且和Spring提供的其他基础结构紧密集成.
- 2。不依赖于Servlet API(目标虽是如此,但是在实现的时候确实是依赖于Servlet的)
- 3。可以任意使用各种视图技术,而不仅仅局限于JSP
- 4。支持各种请求资源的映射策略
- 5。它应是易于扩展的
2) SpringMVC的工作流程?
- 1. 用户发送请求至前端控制器DispatcherServlet
- 2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
- 3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
- 4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 5. 执行处理器(Controller,也叫后端控制器)。
- 6. Controller执行完成返回ModelAndView
- 7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- 8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- 9. ViewReslover解析后返回具体View
- 10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
- 11. DispatcherServlet响应用户
3) 如果你也用过struts2.简单介绍下springMVC和struts2的区别有哪些?
- 1. springmvc的入口是一个servlet即前端控制器,而struts2入口是一个filter过虑器。
- 2. springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
- 3. Struts采用值栈存储请求和响应的数据,通过OGNL存取数据, springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。
4) SpringMvc原理图
5) SSM优缺点、使用场景?
- 1. Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
-
- 2. Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
-
- 3. Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
- 4. 总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
Mybatis
1) 简单介绍下你对mybatis的理解?
- 1. mybatis配置
- 2. SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
- 3. mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
- 4. 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
- 5. 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
- 6. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
- 7. Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
- 8. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
- 9. Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
Java网络面试
http是一种无状态的协议
get提交的有大小限制,post没有
LinkedList增加和删除效率优于ArrayList
Redis 可是数据持久化的,但是Memcache不可以
1、什么是TCP协议?UDP协议?区别?
TCP:传输控制协议,面向连接,可靠。保证数据传输成功。
UDP:不可靠。传输速度快。占系统资源少。
2、TCP三次握手?
A——》B 通信请求以及序列号作为起始数据段。
B——》A 收到请求,序列号作为起始数据段。
A——》B 收到请求
Java网络编程
服务端套接字:ServerSocket。accept() bind() close()
客户端套接字:Socket。
网络编程时的同步、异步、阻塞、非阻塞?
同步:函数调用在没得到结果之前,没有调用结果,不返回任何结果。
异步:函数调用在没得到结果之前,没有调用结果,返回状态信息。
阻塞:函数调用在没得到结果之前,当前线程挂起。得到结果后才返回。
非阻塞:函数调用在没得到结果之前,当前线程不会挂起,立即返回结果。
Java如何实现无阻塞方式的Socket编程?
NIO有效解决了多线程服务器存在的线程开销问题。在NIO中使用多线程主要目的不是为了应对每个客户端请求而分配独立的服务线程,而是通过多线程充分利用多个CPU的处理能力和处理中的等待时间,达到提高服务能力的目的。chanel、Buffer、selector。
HTTP和HTTPS
HTTPS=HTTP+SSL。443端口。
http是明文传输,https是密文传输。
https需要到ca申请证书,缴费。
Hibernate并发机制:
a、Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元(即单个事务,单个线程),它通常只使用一次, 然后就丢弃。
- 如果一个Session 实例允许共享的话,那些支持并发运行的,例如Http request,session beans将会导致出现资源争用。
-
- 如果在Http Session中有hibernate的Session的话,就可能会出现同步访问Http Session。只要用户足够快的点击浏览器的“刷新”, 就会导致两个并发运行的线程使用同一个Session。
b、多个事务并发访问同一块资源,可能会引发第一类丢失更新,脏读,幻读,不可重复读,第二类丢失更新一系列的问题。
解决方案:设置事务隔离级别。
Serializable:串行化。隔离级别最高
Repeatable Read:可重复读
Read Committed:已提交数据读
Read Uncommitted:未提交数据读。隔离级别最差
设置锁:乐观锁和悲观锁。
乐观锁:使用版本号或时间戳来检测更新丢失,在的映射中设置 optimistic-lock=”all”可以在没有版本或者时间戳属性映射的情况下实现 版本检查,此时Hibernate将比较一行记录的每个字段的状态 行级悲观锁:Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。类LockMode 定义了Hibernate所需的不同的锁定级别:LockMode.UPGRADE,LockMode.UPGRADE_NOWAIT,LockMode.READ;
update()和saveOrUpdate()是用来对跨Session的PO进行状态管理的。
update()方法操作的对象必须是持久化了的对象。也就是说,如果此对象在数据库中不存在的话,就不能使用update()方法。
saveOrUpdate()方法操作的对象既可以使持久化了的,也可以使没有持久化的对象。如果是持久化了的对象调用saveOrUpdate()则会 更新数据库中的对象;如果是未持久化的对象使用此方法,则save到数据库中。
当对象由瞬时状态(Transient)一save()时,就变成了持久化状态;
当我们在Session里存储对象的时候,实际是在Session的Map里存了一份, 也就是它的缓存里放了一份,然后,又到数据库里存了一份,在缓存里这一份叫持久对象(Persistent)。 Session 一 Close()了,它的缓存也都关闭了,整个Session也就失效了,这个时候,这个对象变成了游离状态(Detached),但数据库中还是存在的。
当游离状态(Detached)update()时,又变为了持久状态(Persistent)。
当持久状态(Persistent)delete()时,又变为了瞬时状态(Transient), 此时,数据库中没有与之对应的记录。
1立即检索;
优点: 对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象;
缺点: 1.select语句太多;2.可能会加载应用程序不需要访问的对象白白浪费许多内存空间;
2延迟检索:
优点: 由应用程序决定需要加载哪些对象,可以避免可执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并且能节省内存空间;
缺点: 应用程序如果希望访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化;
3 迫切左外连接检索
优点: 1对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便地冲一个对象导航到与它关联的对象。2使用了外连接,select语句数目少;
缺点: 1 可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;2复杂的数据库表连接也会影响检索性能;
在定义数据库和数据库属性的文件applicationConfig.xml里面,把hibernate.show_sql 设置为true
这样生成的SQL就会在控制台出现了
注意:这样做会加重系统的负担,不利于性能调优
Read-only: 这种策略适用于那些频繁读取却不会更新的数据,这是目前为止最简单和最有效的缓存策略
* Read/write:这种策略适用于需要被更新的数据,比read-only更耗费资源,在非JTA环境下,每个事务需要在session.close和session.disconnect()被调用
* Nonstrict read/write: 这种策略不保障两个同时进行的事务会修改同一块数据,这种策略适用于那些经常读取但是极少更新的数据
* Transactional: 这种策略是完全事务化得缓存策略,可以用在JTA环境下
sorted collection是在内存中通过Java比较器进行排序的
ordered collection是在数据库中通过order by进行排序的
1.读取并解析配置文件
2.读取并解析映射信息,创建SessionFactory
3.打开Sesssion
4.创建事务Transation
5.持久化操作
6.提交事务
7.关闭Session
8.关闭SesstionFactory
为什么要用:
1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作
hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many、
内部缓存存在Hibernate中又叫一级缓存,属于应用事物级缓存
二级缓存:
a) 应用及缓存
b) 分布式缓存
条件:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非关键数据
c) 第三方缓存的实现
Sql、Criteria,objectcomposition
Hql:
1、 属性查询
2、 参数查询、命名参数查询
3、 关联查询
4、 分页查询
5、 统计函数
1.使用双向一对多关联,不使用单向一对多
2.灵活使用单向一对多关联
3.不用一对一,用多对一取代
4.配置对象缓存,不使用集合缓存
5.一对多集合使用Bag,多对多集合使用Set
6. 继承类使用显式多态
7. 表字段要少,表关联不要怕多,有二级缓存撑腰
3种:hql、条件查询QBC(QueryBy Criteria)、原生sql (通过createSQLQuery建立)
inverse属性默认是false,就是说关系的两端都来维护关系。
比如Student和Teacher是多对多关系,用一个中间表TeacherStudent维护。Gp)i
如果Student这边inverse=”true”, 那么关系由另一端Teacher维护,就是说当插入Student时,不会操作TeacherStudent表(中间表)。只有Teacher插入或删除时才会触发对中间表的操作。所以两边都inverse=”true”是不对的,会导致任何操作都不触发对中间表的影响;当两边都inverse=”false”或默认时,会导致在中间表中插入两次关系。
Detached Object(游离对象)可以传递到任何层直到表现层而不是用任何DTO(DataTransfer Objects). 然后你还可以重新把游离对象赋给另外一个Session.
jdbc:手动
手动写sql
delete、insert、update要将对象的值一个一个取出传到sql中,不能直接传入一个对象。
select:返回的是一个resultset,要从ResultSet中一行一行、一个字段一个字段的取出,然后封装到一个对象中,不直接返回一个对象。
ibatis的特点:半自动化
sql要手动写
delete、insert、update:直接传入一个对象
select:直接返回一个对象
hibernate:全自动
不写sql,自动封装
delete、insert、update:直接传入一个对象
select:直接返回一个对象
1.建索引
2.减少表之间的关联
3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据
SessionFactory 是Hibrenate单例数据存储和线程安全的,以至于可以多线程同时访问。一个SessionFactory 在启动的时候只能建立一次。SessionFactory应该包装各种单例以至于它能很简单的在一个应用代码中储存.
Configuration 接口:配置Hibernate,根据其启动hibernate,创建
SessionFactory 对象;
SessionFactory 接口:初始化Hibernate,充当数据存储源的代理,创建
session 对象,sessionFactory 是线程安全的,意味着它的同一个实例可以被应
用的多个线程共享,是重量级、二级缓存;
Session 接口:负责保存、更新、删除、加载和查询对象,是线程不安全的,
避免多个线程共享同一个session,是轻量级、一级缓存;
Transaction 接口:管理事务;
Query 和Criteria 接口:执行数据库的查询。
Dubbo
头几天瞧到《Java顶尖口试必问-Dubbo口试题汇总》,对于内里得难点本人试着答复少许,有错误得请民众指正。
Dubbo固然大概不革新了,可是背靠阿里得措施能力,中文报告得多样,非常合适很多几中小型分散式类型得开辟。
第一、dubbo
Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。
反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
缺省协议,使用基于 mina 1.1.7
和 hessian 3.2.1
的 tbremoting 交互。
第二、RMI
RMI 协议采用 JDK 标准的 java.rmi.*
实现,采用阻塞式短连接和 JDK 标准序列化方式。
注意:如果正在使用 RMI 提供服务给外部访问 1,同时应用里依赖了老的 common-collections 包 2的情况下,存在反序列化安全风险 3。
第三、hessian
Hessian 1 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:
第四、Http
基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 1
第五、WebService
基于 WebService 的远程调用协议,基于 Apache CXF 1 的 frontend-simple
和 transports-http
实现 2。
可以和原生 WebService 服务互操作,即:
第六、thrift
当前 dubbo 支持 1的 thrift 协议是对 thrift 原生协议 2 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。
使用 dubbo thrift 协议同样需要使用 thrift 的 idl compiler 编译生成相应的 java 代码,后续版本中会在这方面做一些增强。
第七、缓存
Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。
unicast=false
,则广播给订阅者组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255
Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 1。
流程说明:
/dubbo/com.foo.BarService/providers
目录下写入自己的 URL 地址/dubbo/com.foo.BarService/providers
目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers
目录下写入自己的 URL 地址/dubbo/com.foo.BarService
目录下的所有提供者和消费者 URL 地址。支持以下功能:
<dubbo:registry check="false" />
时,记录失败注册和订阅请求,后台定时重试<dubbo:registry username="admin" password="1234" />
设置 zookeeper 登录信息<dubbo:registry group="dubbo" />
设置 zookeeper 的根节点,不设置将使用无根树*
号通配符 <dubbo:reference group="*" version="*" />
,可订阅服务的所有分组和所有版本的提供者使用 Redis 的 Key/Map 结构存储数据结构:
使用 Redis 的 Publish/Subscribe 事件通知数据变更:
register
, unregister
, subscribe
, unsubscribe
register
, unregister
事件psubscribe
功能订阅 /dubbo/*
,会收到所有服务的所有变更事件调用过程:
Key:/dubbo/com.foo.BarService/providers
下,添加当前提供者的地址Channel:/dubbo/com.foo.BarService/providers
发送 register
事件Channel:/dubbo/com.foo.BarService/providers
订阅 register
和 unregister
事件Key:/dubbo/com.foo.BarService/providers
下,添加当前消费者的地址register
和 unregister
事件后,从 Key:/dubbo/com.foo.BarService/providers
下获取提供者地址列表Channel:/dubbo/*
订阅 register
和 unregister
,以及 subscribe
和unsubsribe
事件register
和 unregister
事件后,从 Key:/dubbo/com.foo.BarService/providers
下获取提供者地址列表subscribe
和 unsubsribe
事件后,从 Key:/dubbo/com.foo.BarService/consumers
下获取消费者地址列表Simple 注册中心本身就是一个普通的 Dubbo 服务,可以减少第三方依赖,使整体通讯方式一致。
失败自动切换,当出现失败,重试其它服务器 1。通常用于读操作,但重试会带来更长延迟。可通过 retries="2"
来设置重试次数(不含第一次)。
重试次数配置如下:
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
- <dubbo:reference>
- <dubbo:method name="findFoo" retries="2" />
- </dubbo:reference>
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2"
来设置最大并行数。
广播调用所有提供者,逐个调用,任意一台报错则报错 2。通常用于通知所有提供者更新缓存或日志等本地资源信息。
http://www.njszjw.gov.cn/jrjd/02017082114135.html
1.Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。
Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。
一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。
2.列出一些你常见的运行时异常?
- ArithmeticException(算术异常)
- ClassCastException (类转换异常)
- IllegalArgumentException (非法参数异常)
- IndexOutOfBoundsException (下标越界异常)
- NullPointerException (空指针异常)
- SecurityException (安全异常)
3.Java中的两种异常类型是什么?他们有什么区别?
Java中有两种异常:受检查的(checked)异常和不受检查的(unchecked)异常。不受检查的异常不需要在方法或者是构造函数上声明,就算方法或者是构造函数的执行可能会抛出这样的异常,并且不受检查的异常可以传播到方法或者是构造函数的外面。相反,受检查的异常必须要用throws语句在方法或者是构造函数上声明。这里有Java异常处理的一些小建议。
4.Java中Exception和Error有什么区别?
Exception和Error都是Throwable的子类。
Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Error定义了不期望被用户程序捕获的异常。
Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。Exception用于用户程序可以捕获的异常情况。
5.异常处理的时候,finally代码块的重要性是什么?
无论是否抛出异常,finally代码块总是会被执行。就算是没有catch语句同时又抛出异常的情况下,finally代码块仍然会被执行。最后要说的是,finally代码块主要用来释放资源,比如:I/O缓冲区,数据库连接。
6.异常处理完成以后,Exception对象会发生什么变化?
Exception对象会在下一个垃圾回收过程中被回收掉。
7.finally代码块和finalize()方法有什么区别?
无论是否抛出异常,finally代码块都会执行,它主要是用来释放应用占用的资源。finalize()方法是Object类的一个protected方法,它是在对象被垃圾回收之前由Java虚拟机来调用的。
1.什么是Applet?
java applet是能够被包含在HTML页面中并且能被启用了java的客户端浏览器执行的程序。Applet主要用来创建动态交互的web应用程序。
2.简述Applet的生命周期?
applet可以经历下面的状态:
Init:每次被载入的时候都会被初始化。
Start:开始执行applet。
Stop:结束执行applet。
Destroy:卸载applet之前,做最后的清理工作。
3.当applet被载入的时候会发生什么?
首先,创建applet控制类的实例,然后初始化applet,最后开始运行。
4.Applet和普通的Java应用程序有什么区别?
applet是运行在启用了java的浏览器中,Java应用程序是可以在浏览器之外运行的独立的Java程序。但是,它们都需要有Java虚拟机。
进一步来说,Java应用程序需要一个有特定方法签名的main函数来开始执行。Java applet不需要这样的函数来开始执行。
最后,Java applet一般会使用很严格的安全策略,Java应用一般使用比较宽松的安全策略。
5.Java applet有哪些限制条件?
主要是由于安全的原因,给applet施加了以下的限制:
applet不能够载入类库或者定义本地方法。
applet不能在宿主机上读写文件。
applet不能读取特定的系统属性。
applet不能发起网络连接,除非是跟宿主机。
applet不能够开启宿主机上其他任何的程序。
6.从网络上加载的applet和从本地文件系统加载的applet有什么区别?
当applet是从网络上加载的时候,applet是由applet类加载器载入的,它受applet安全管理器的限制。
当applet是从客户端的本地磁盘载入的时候,applet是由文件系统加载器载入的。
从文件系统载入的applet允许在客户端读文件,写文件,加载类库,并且也允许执行其他程序,但是,却通不过字节码校验。
7.applet类加载器是什么?它会做哪些工作?
当applet是从网络上加载的时候,它是由applet类加载器载入的。类加载器有自己的java名称空间等级结构。类加载器会保证来自文件系统的类有唯一的名称空间,来自网络资源的类有唯一的名称空间。
当浏览器通过网络载入applet的时候,applet的类被放置于和applet的源相关联的私有的名称空间中。然后,那些被类加载器载入进来的类都是通过了验证器验证的。验证器会检查类文件格式是否遵守Java语言规范,确保不会出现堆栈溢出(stack overflow)或者下溢(underflow),传递给字节码指令的参数是正确的。
8.applet安全管理器是什么?它会做哪些工作?
applet安全管理器是给applet施加限制条件的一种机制。浏览器可以只有一个安全管理器。安全管理器在启动的时候被创建,之后不能被替换覆盖或者是扩展。
9.什么是不受信任的applet?
不受信任的applet是不能访问或是执行本地系统文件的Java applet,默认情况下,所有下载的applet都是不受信任的。
1.说出三种支持重绘(painting)的组件。
Canvas, Frame, Panel,和Applet支持重绘。
2.什么是裁剪(clipping)?
限制在一个给定的区域或者形状的绘图操作就做裁剪。
3.MenuItem和CheckboxMenuItem的区别是什么?
CheckboxMenuItem类继承自MenuItem类,支持菜单选项可以选中或者不选中。
4.边缘布局(BorderLayout)里面的元素是如何布局的?
BorderLayout里面的元素是按照容器的东西南北中进行布局的。
5.网格包布局(GridBagLayout)里面的元素是如何布局的?
GridBagLayout里面的元素是按照网格进行布局的。不同大小的元素可能会占据网格的多于1行或一列。因此,行数和列数可以有不同的大小。
6.Window和Frame有什么区别?
Frame类继承了Window类,它定义了一个可以有菜单栏的主应用窗口。
7.裁剪(clipping)和重绘(repainting)有什么联系?
当窗口被AWT重绘线程进行重绘的时候,它会把裁剪区域设置成需要重绘的窗口的区域。
8.事件监听器接口(event-listener interface)和事件适配器(event-adapter)有什么关系?
事件监听器接口定义了对特定的事件,事件处理器必须要实现的方法。事件适配器给事件监听器接口提供了默认的实现。
9.GUI组件如何来处理它自己的事件?
GUI组件可以处理它自己的事件,只要它实现相对应的事件监听器接口,并且把自己作为事件监听器。
10.Java的布局管理器比传统的窗口系统有哪些优势?
Java使用布局管理器以一种一致的方式在所有的窗口平台上摆放组件。因为布局管理器不会和组件的绝对大小和位置相绑定,所以他们能够适应跨窗口系统的特定平台的不同。
11.Java的Swing组件使用了哪种设计模式?
Java中的Swing组件使用了MVC(视图-模型-控制器)设计模式。
12.弹出式选择菜单(Choice)和列表(List)有什么区别?
Choice是以一种紧凑的形式展示的,需要下拉才能看到所有的选项。Choice中一次只能选中一个选项。List同时可以有多个元素可见,支持选中一个或者多个元素。
13.什么是布局管理器?
布局管理器用来在容器中组织组件。
14.滚动条(Scrollbar)和滚动面板(JScrollPane)有什么区别?
Scrollbar是一个组件,不是容器。而ScrollPane是容器。ScrollPane自己处理滚动事件。
15.哪些Swing的方法是线程安全的?
只有3个线程安全的方法: repaint(), revalidate(), and invalidate()。
1.什么是JDBC?
JDBC是允许用户在不同数据库之间做选择的一个抽象层。JDBC允许开发者用JAVA写数据库应用程序,而不需要关心底层特定数据库的细节。
2.解释下驱动(Driver)在JDBC中的角色。
JDBC驱动提供了特定厂商对JDBC API接口类的实现,驱动必须要提供java.sql包下面这些类的实现:Connection, Statement, PreparedStatement,CallableStatement, ResultSet和Driver。
3.Class.forName()方法有什么作用?
这个方法用来载入跟数据库建立连接的驱动。
4.PreparedStatement比Statement有什么优势?
PreparedStatements是预编译的,因此,性能会更好。同时,不同的查询参数值,PreparedStatement可以重用。
5.什么时候使用CallableStatement?用来准备CallableStatement的方法是什么?
CallableStatement用来执行存储过程。存储过程是由数据库存储和提供的。存储过程可以接受输入参数,也可以有返回结果。非常鼓励使用存储过程,因为它提供了安全性和模块化。准备一个CallableStatement的方法是:CallableStament.prepareCall();
6.数据库连接池是什么意思?
像打开关闭数据库连接这种和数据库的交互可能是很费时的,尤其是当客户端数量增加的时候,会消耗大量的资源,成本是非常高的。可以在应用服务器启动的时候建立很多个数据库连接并维护在一个池中。连接请求由池中的连接提供。在连接使用完毕以后,把连接归还到池中,以用于满足将来更多的请求。
1.RMI中的远程接口(Remote Interface)扮演了什么样的角色?
远程接口用来标识哪些方法是可以被非本地虚拟机调用的接口。远程对象必须要直接或者是间接实现远程接口。实现了远程接口的类应该声明被实现的远程接口,给每一个远程对象定义构造函数,给所有远程接口的方法提供实现。
2.java.rmi.Naming类扮演了什么样的角色?
java.rmi.Naming类用来存储和获取在远程对象注册表里面的远程对象的引用。Naming类的每一个方法接收一个URL格式的String对象作为它的参数。
3.RMI的绑定(Binding)是什么意思?
绑定是为了查询找远程对象而给远程对象关联或者是注册以后会用到的名称的过程。远程对象可以使用Naming类的bind()或者rebind()方法跟名称相关联。
4.Naming类的bind()和rebind()方法有什么区别?
bind()方法负责把指定名称绑定给远程对象,rebind()方法负责把指定名称重新绑定到一个新的远程对象。如果那个名称已经绑定过了,先前的绑定会被替换掉。
5.让RMI程序能正确运行有哪些步骤?
为了让RMI程序能正确运行必须要包含以下几个步骤:
编译所有的源文件。
使用rmic生成stub。
启动rmiregistry。
启动RMI服务器。
运行客户端程序。
6.RMI的stub扮演了什么样的角色?
远程对象的stub扮演了远程对象的代表或者代理的角色。调用者在本地stub上调用方法,它负责在远程对象上执行方法。当stub的方法被调用的时候,会经历以下几个步骤:
初始化到包含了远程对象的JVM的连接。
序列化参数到远程的JVM。
等待方法调用和执行的结果。
反序列化返回的值或者是方法没有执行成功情况下的异常。
把值返回给调用者。
7.什么是分布式垃圾回收(DGC)?它是如何工作的?
DGC叫做分布式垃圾回收。RMI使用DGC来做自动垃圾回收。因为RMI包含了跨虚拟机的远程对象的引用,垃圾回收是很困难的。DGC使用引用计数算法来给远程对象提供自动内存管理。
8.RMI中使用RMI安全管理器(RMISecurityManager)的目的是什么?
RMISecurityManager使用下载好的代码提供可被RMI应用程序使用的安全管理器。如果没有设置安全管理器,RMI的类加载器就不会从远程下载任何的类。
9.解释下Marshalling和demarshalling。
当应用程序希望把内存对象跨网络传递到另一台主机或者是持久化到存储的时候,就必须要把对象在内存里面的表示转化成合适的格式。这个过程就叫做Marshalling,反之就是demarshalling。
10.解释下Serialization和Deserialization。
Java提供了一种叫做对象序列化的机制,他把对象表示成一连串的字节,里面包含了对象的数据,对象的类型信息,对象内部的数据的类型信息等等。因此,序列化可以看成是为了把对象存储在磁盘上或者是从磁盘上读出来并重建对象而把对象扁平化的一种方式。反序列化是把对象从扁平状态转化成活动对象的相反的步骤。
11.什么是RMI?
Java远程方法调用(Java RMI)是Java API对远程过程调用(RPC)提供的面向对象的等价形式,支持直接传输序列化的Java对象和分布式垃圾回收。远程方法调用可以看做是激活远程正在运行的对象上的方法的步骤。RMI对调用者是位置透明的,因为调用者感觉方法是执行在本地运行的对象上的。看下RMI的一些注意事项。
12.RMI体系结构的基本原则是什么?
RMI体系结构是基于一个非常重要的行为定义和行为实现相分离的原则。RMI允许定义行为的代码和实现行为的代码相分离,并且运行在不同的JVM上。
13.RMI体系结构分哪几层?
RMI体系结构分以下几层:
存根和骨架层(Stub and Skeleton layer):这一层对程序员是透明的,它主要负责拦截客户端发出的方法调用请求,然后把请求重定向给远程的RMI服务。
远程引用层(Remote Reference Layer):RMI体系结构的第二层用来解析客户端对服务端远程对象的引用。这一层解析并管理客户端对服务端远程对象的引用。连接是点到点的。
传输层(Transport layer):这一层负责连接参与服务的两个JVM。这一层是建立在网络上机器间的TCP/IP连接之上的。它提供了基本的连接服务,还有一些防火墙穿透策略。
1.什么是Servlet?
Servlet是用来处理客户端请求并产生动态网页内容的Java类。Servlet主要是用来处理或者是存储HTML表单提交的数据,产生动态内容,在无状态的HTTP协议下管理状态信息。
2.说一下Servlet的体系结构。
所有的Servlet都必须要实现的核心的接口是javax.servlet.Servlet。每一个Servlet都必须要直接或者是间接实现这个接口,或者是继承javax.servlet.GenericServlet或者javax.servlet.http.HTTPServlet。最后,Servlet使用多线程可以并行的为多个请求服务。
3.Applet和Servlet有什么区别?
Applet是运行在客户端主机的浏览器上的客户端Java程序。而Servlet是运行在web服务器上的服务端的组件。applet可以使用用户界面类,而Servlet没有用户界面,相反,Servlet是等待客户端的HTTP请求,然后为请求产生响应。
4.GenericServlet和HttpServlet有什么区别?
GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。继承自GenericServlet的Servlet应该要覆盖service()方法。最后,为了开发一个能用在网页上服务于使用HTTP协议请求的Servlet,你的Servlet必须要继承自HttpServlet。这里有Servlet的例子。
5.解释下Servlet的生命周期。
对每一个客户端的请求,Servlet引擎载入Servlet,调用它的init()方法,完成Servlet的初始化。然后,Servlet对象通过为每一个请求单独调用service()方法来处理所有随后来自客户端的请求,最后,调用Servlet(译者注:这里应该是Servlet而不是server)的destroy()方法把Servlet删除掉。
6.doGet()方法和doPost()方法有什么区别?
doGet:GET方法会把名值对追加在请求的URL后面。因为URL对字符数目有限制,进而限制了用在客户端请求的参数值的数目。并且请求中的参数值是可见的,因此,敏感信息不能用这种方式传递。
doPOST:POST方法通过把请求参数值放在请求体中来克服GET方法的限制,因此,可以发送的参数的数目是没有限制的。最后,通过POST请求传递的敏感信息对外部客户端是不可见的。
7.什么是Web应用程序?
Web应用程序是对Web或者是应用服务器的动态扩展。有两种类型的Web应用:面向表现的和面向服务的。面向表现的Web应用程序会产生包含了很多种标记语言和动态内容的交互的web页面作为对请求的响应。而面向服务的Web应用实现了Web服务的端点(endpoint)。一般来说,一个Web应用可以看成是一组安装在服务器URL名称空间的特定子集下面的Servlet的集合。
8.什么是服务端包含(Server Side Include)?
服务端包含(SSI)是一种简单的解释型服务端脚本语言,大多数时候仅用在Web上,用servlet标签嵌入进来。SSI最常用的场景把一个或多个文件包含到Web服务器的一个Web页面中。当浏览器访问Web页面的时候,Web服务器会用对应的servlet产生的文本来替换Web页面中的servlet标签。
9.什么是Servlet链(Servlet Chaining)?
Servlet链是把一个Servlet的输出发送给另一个Servlet的方法。第二个Servlet的输出可以发送给第三个Servlet,依次类推。链条上最后一个Servlet负责把响应发送给客户端。
10.如何知道是哪一个客户端的机器正在请求你的Servlet?
ServletRequest类可以找出客户端机器的IP地址或者是主机名。getRemoteAddr()方法获取客户端主机的IP地址,getRemoteHost()可以获取主机名。看下这里的例子。
11.HTTP响应的结构是怎么样的?
HTTP响应由三个部分组成:
状态码(Status Code):描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果Servlet没有返回状态码,默认会返回成功的状态码HttpServletResponse.SC_OK。
HTTP头部(HTTP Header):它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在Serlet中检索HTTP的头部看这里。
主体(Body):它包含了响应的内容。它可以包含HTML代码,图片,等等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。
12.什么是cookie?session和cookie有什么区别?
cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别:
无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。
在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。
13.浏览器和Servlet通信使用的是什么协议?
浏览器和Servlet通信使用的是HTTP协议。
14.什么是HTTP隧道?
HTTP隧道是一种利用HTTP或者是HTTPS把多种网络协议封装起来进行通信的技术。因此,HTTP协议扮演了一个打通用于通信的网络协议的管道的包装器的角色。把其他协议的请求掩盖成HTTP的请求就是HTTP隧道。
15.sendRedirect()和forward()方法有什么区别?
sendRedirect()方法会创建一个新的请求,而forward()方法只是把请求转发到一个新的目标上。重定向(redirect)以后,之前请求作用域范围以内的对象就失效了,因为会产生一个新的请求,而转发(forwarding)以后,之前请求作用域范围以内的对象还是能访问的。一般认为sendRedirect()比forward()要慢。
16.什么是URL编码和URL解码?
URL编码是负责把URL里面的空格和其他的特殊字符替换成对应的十六进制表示,反之就是解码。
1.什么是JSP页面?
JSP页面是一种包含了静态数据和JSP元素两种类型的文本的文本文档。静态数据可以用任何基于文本的格式来表示,比如:HTML或者XML。JSP是一种混合了静态内容和动态产生的内容的技术。这里看下JSP的例子。
2.JSP请求是如何被处理的?
浏览器首先要请求一个以.jsp扩展名结尾的页面,发起JSP请求,然后,Web服务器读取这个请求,使用JSP编译器把JSP页面转化成一个Servlet类。需要注意的是,只有当第一次请求页面或者是JSP文件发生改变的时候JSP文件才会被编译,然后服务器调用servlet类,处理浏览器的请求。一旦请求执行结束,servlet会把响应发送给客户端。这里看下如何在JSP中获取请求参数。
3.JSP有什么优点?
下面列出了使用JSP的优点:
JSP页面是被动态编译成Servlet的,因此,开发者可以很容易的更新展现代码。
JSP页面可以被预编译。
JSP页面可以很容易的和静态模板结合,包括:HTML或者XML,也可以很容易的和产生动态内容的代码结合起来。
开发者可以提供让页面设计者以类XML格式来访问的自定义的JSP标签库。
开发者可以在组件层做逻辑上的改变,而不需要编辑单独使用了应用层逻辑的页面。
4.什么是JSP指令(Directive)?JSP中有哪些不同类型的指令?
Directive是当JSP页面被编译成Servlet的时候,JSP引擎要处理的指令。Directive用来设置页面级别的指令,从外部文件插入数据,指定自定义的标签库。Directive是定义在<%@ 和 %>之间的。下面列出了不同类型的Directive:
包含指令(Include directive):用来包含文件和合并文件内容到当前的页面。
页面指令(Page directive):用来定义JSP页面中特定的属性,比如错误页面和缓冲区。
Taglib指令: 用来声明页面中使用的自定义的标签库。
5.什么是JSP动作(JSP action)?
JSP动作以XML语法的结构来控制Servlet引擎的行为。当JSP页面被请求的时候,JSP动作会被执行。它们可以被动态的插入到文件中,重用JavaBean组件,转发用户到其他的页面,或者是给Java插件产生HTML代码。下面列出了可用的动作:
jsp:include-当JSP页面被请求的时候包含一个文件。
jsp:useBean-找出或者是初始化Javabean。
jsp:setProperty-设置JavaBean的属性。
jsp:getProperty-获取JavaBean的属性。
jsp:forward-把请求转发到新的页面。
jsp:plugin-产生特定浏览器的代码。
6.什么是Scriptlets?
JSP技术中,scriptlet是嵌入在JSP页面中的一段Java代码。scriptlet是位于标签内部的所有的东西,在标签与标签之间,用户可以添加任意有效的scriplet。
7.声明(Decalaration)在哪里?
声明跟Java中的变量声明很相似,它用来声明随后要被表达式或者scriptlet使用的变量。添加的声明必须要用开始和结束标签包起来。
8.什么是表达式(Expression)?
JSP表达式是Web服务器把脚本语言表达式的值转化成一个String对象,插入到返回给客户端的数据流中。表达式是在<%=和%>这两个标签之间定义的。
9.隐含对象是什么意思?有哪些隐含对象?
JSP隐含对象是页面中的一些Java对象,JSP容器让这些Java对象可以为开发者所使用。开发者不用明确的声明就可以直接使用他们。JSP隐含对象也叫做预定义变量。下面列出了JSP页面中的隐含对象:
application
page
request
response
session
exception
out
config
pageContext
如若有错,欢迎留言指正,我会及时更正,不会错误性的误导他人,方便大家学习,谢谢!!!
zookeeper常见面试题
zookeeper是如何保证事务的顺序一致性的
zookeeper采用了递增的事务Id来标识,所有的proposal都在被提出的时候加上了zxid,zxid实际上是一个64位的数字,高32位是epoch用来标识leader是否发生改变,如果有新的leader产生出来,epoch会自增,低32位用来递增计数。当新产生proposal的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行
zookeeper是如何选取主leader的?
当leader崩溃或者leader失去大多数的follower,这时zk进入恢复模式,
zk中znode类型有四种,持久化目录节点 持久化顺序编号目录节点(有顺序 能够在注册机器等许多场景用到) 临时目录节点 临时顺序编号节点
zk的通知机制
client端会对某个znode建立一个watcher事件,当该znode发生变化时,这些client会收到zk的通知,然后client可以根据znode变化来做出业务上的改变等。
zk的配置管理
程序分布式的部署在不同的机器上,将程序的配置信息放在zk的znode下,当有配置发生改变时,也就是znode发生变化时,可以通过改变zk中某个目录节点的内容,利用water通知给各个客户端 从而更改配置。
zk的命名服务
命名服务是指通过指定的名字来获取资源或者服务的地址,利用zk创建一个全局的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
分布式通知和协调
对于系统调度来说:操作人员发送通知实际是通过控制台改变某个节点的状态,然后zk将这些变化发送给注册了这个节点的watcher的所有客户端。
对于执行情况汇报:每个工作进程都在某个目录下创建一个临时节点。并携带工作的进度数据,这样汇总的进程可以监控目录子节点的变化获得工作进度的实时的全局情况。
机器中为什么会有master;
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行master选举。
多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:非常赞同这个观点)
一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。
在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好。举个例子,Servlets比CGI更好,是因为Servlets支持多线程而CGI不支持。
当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。
有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。若想了解更多可以阅读这篇关于如何在Java中创建线程的文章。
当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Waiting,Blocked 和Dead。读这篇文章可以了解更多关于线程生命周期的知识。
当然可以,但是如果我们调用了Thread的run()方法,它的行为就会和普通的方法一样,为了在新的线程中执行我们的代码,必须使用Thread.start()方法。
我们可以使用Thread类的Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。
每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。
线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。
上下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。
我们可以使用Thread类的joint()方法来确保所有程序创建的线程在main()方法退出前结束。这里有一篇文章关于Thread类的joint()方法。
当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。点击这里有更多关于线程wait, notify和notifyAll.
Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法
当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。
Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。
在Java中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。在线程安全教程中,你可以学到更多。
当我们使用volatile关键字去修饰变量的时候,所以线程都会直接读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。
同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。
ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。
每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。在ThreadLocal例子这篇文章中你可以看到一个关于ThreadLocal的小程序。
ThreadGroup是一个类,它的目的是提供关于线程组的信息。
ThreadGroup API比较薄弱,它并没有比Thread提供了更多的功能。它有两个主要的功能:一是获取线程组中处于活跃状态线程的列表;二是设置为线程设置未捕获异常处理器(ncaught exception handler)。但在Java 1.5中Thread类也添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法,所以ThreadGroup是已经过时的,不建议继续使用。
1
2
3
4
5
6
|
t1
.
setUncaughtExceptionHandler
(
new
UncaughtExceptionHandler
(
)
{
@
Override
public
void
uncaughtException
(
Thread
t
,
Throwable
e
)
{
System
.
out
.
println
(
"exception occured:"
+
e
.
getMessage
(
)
)
;
}
}
)
;
|
22. 什么是Java线程转储(Thread Dump),如何得到它?
线程转储是一个JVM活动线程的列表,它对于分析系统瓶颈和死锁非常有用。有很多方法可以获取线程转储——使用Profiler,Kill -3命令,jstack工具等等。我更喜欢jstack工具,因为它容易使用并且是JDK自带的。由于它是一个基于终端的工具,所以我们可以编写一些脚本去定时的产生线程转储以待分析。读这篇文档可以了解更多关于产生线程转储的知识。
死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。
分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。
避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法,阅读这篇文章去学习如何分析死锁。
java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。
java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用Timer去安排它的执行。
这里有关于java Timer的例子。
一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行的任务的队列。
java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池。线程池例子展现了如何创建和使用线程池,或者阅读ScheduledThreadPoolExecutor例子,了解如何创建一个周期任务。
原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。
int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。
为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。到JDK1.5,java.util.concurrent.atomic包提供了int和long类型的装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。可以阅读这篇文章来了解Java的atomic类。
Lock接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。
它的优势有:
阅读更多关于锁的例子
Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入。Executor框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。
无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用Executors框架可以非常方便的创建一个线程池,阅读这篇文章可以了解如何使用Executor框架创建一个线程池。
java.util.concurrent.BlockingQueue的特性是:当队列是空的时,从队列中获取或删除元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。
阻塞队列不接受空值,当你尝试向队列中添加空值的时候,它会抛出NullPointerException。
阻塞队列的实现都是线程安全的,所有的查询方法都是原子的并且使用了内部锁或者其他形式的并发控制。
BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。
阅读这篇文章了解如何使用阻塞队列实现生产者-消费者问题。
Java 5在concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很相似,但它可以返回一个对象或者抛出一个异常。
Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法去在线程池中执行Callable内的任务。由于Callable任务是并行的,我们必须等待它返回的结果。java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它我们可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。
阅读这篇文章了解更多关于Callable,Future的例子。
FutureTask是Future的一个基础实现,我们可以将它同Executors使用处理异步任务。通常我们不需要使用FutureTask类,单当我们打算重写Future接口的一些方法并保持原来基础的实现是,它就变得非常有用。我们可以仅仅继承于它并重写我们需要的方法。阅读Java FutureTask例子,学习如何使用它。
Java集合类都是快速失败的,这就意味着当集合被改变且一个线程在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。
并发容器支持并发的遍历和并发的更新。
主要的类有ConcurrentHashMap, CopyOnWriteArrayList 和CopyOnWriteArraySet,阅读这篇文章了解如何避免ConcurrentModificationException。
Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法。
Executors可以用于方便的创建线程池。
很多核心Java面试题来源于多线程(Multi-Threading)和集合框架(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的。这篇文章收集了 Java 线程方面一些典型的问题,这些问题经常被高级工程师所问到。
0.Java 中多线程同步是什么?
在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个 Java 线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果。
1.解释实现多线程的几种方法?
一 Java 线程可以实现 Runnable 接口或者继承 Thread 类来实现,当你打算多重继承时,优先选择实现 Runnable。
2.Thread.start ()与 Thread.run ()有什么区别?
Thread.start ()方法(native)启动线程,使之进入就绪状态,当 cpu 分配时间该线程时,由 JVM 调度执行 run ()方法。
3.为什么需要 run ()和 start ()方法,我们可以只用 run ()方法来完成任务吗?
我们需要 run ()&start ()这两个方法是因为 JVM 创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的 start 方法来完成,start 由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了 Runnable 接口,这就避免因继承了 Thread 类而造成的 Java 的多继承问题。
4.什么是 ThreadLocal 类,怎么使用它?
ThreadLocal 是一个线程级别的局部变量,并非“本地线程”。ThreadLocal 为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本(译者注)。
下面是线程局部变量(ThreadLocal variables)的关键点:
一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。
ThreadLocal 实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。
当多个线程访问 ThreadLocal 实例时,每个线程维护 ThreadLocal 提供的独立的变量副本。
常用的使用可在 DAO 模式中见到,当 DAO 类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)
ThreadLocal 难于理解,下面这些引用连接有助于你更好的理解它。
《Good article on ThreadLocal on IBM DeveloperWorks 》、《理解 ThreadLocal》、《Managing data : Good example》、《Refer Java API Docs》
5.什么时候抛出 InvalidMonitorStateException 异常,为什么?
调用 wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,那么就会抛出 IllegalMonitorStateException 的异常(也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试调用 wait ()/notify ()/notifyAll ()时)。由于该异常是 RuntimeExcpetion 的子类,所以该异常不一定要捕获(尽管你可以捕获只要你愿意).作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。
6.Sleep ()、suspend ()和 wait ()之间有什么区别?
Thread.sleep ()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt ()方法,它将唤醒那个“睡眠的”线程。
注意:sleep ()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep (),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep (),也是当前线程进入睡眠,而不是t线程。t.suspend ()是过时的方法,使用 suspend ()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend ()容易引起死锁问题。
object.wait ()使当前线程出于“不可运行”状态,和 sleep ()不同的是 wait 是 object 的方法而不是 thread。调用 object.wait ()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用 object.notify (),这样将唤醒原来等待中的线程,然后释放该锁。基本上 wait ()/notify ()与 sleep ()/interrupt ()类似,只是前者需要获取对象锁。
7.在静态方法上使用同步时会发生什么事?
同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。
8.当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?
可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java 没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据 Java 照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。
下面有一个示例说明:Common 类有两个方法 synchronizedMethod1()和 method1(),MyThread 类在独立的线程中调用这两个方法。
这里是程序的输出:
结果表明即使 synchronizedMethod1()方法执行了,method1()也会被调用。
9.在一个对象上两个线程可以调用两个不同的同步实例方法吗?
不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。看下面代码示例非常清晰:Common 类有 synchronizedMethod1()和 synchronizedMethod2()方法,MyThread 调用这两个方法。
10.什么是死锁
死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就 JavaAPI 而言,线程死锁可能发生在一下情况。
11.什么是线程饿死,什么是活锁?
线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。
当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI 中线程活锁可能发生在以下情形:
以下面试题为个人在面试过程中所遇到的,仅供参考!如有错误,望指出。
1、servlet执行流程
客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request、response对象传递给找到的servlet,servlet根据request就可以知道是谁发出的请求,请求信息及其他信息,当servlet处理完业务逻辑后会将信息放入到response并响应到客户端。
2、springMVC的执行流程
springMVC是由dispatchservlet为核心的分层控制框架。首先客户端发出一个请求web服务器解析请求url并去匹配dispatchservlet的映射url,如果匹配上就将这个请求放入到dispatchservlet,dispatchservlet根据mapping映射配置去寻找相对应的handel,然后把处理权交给找到的handel,handel封装了处理业务逻辑的代码,当handel处理完后会返回一个逻辑视图modelandview给dispatchservlet,此时的modelandview是一个逻辑视图不是一个正式视图,所以dispatchservlet会通过viewresource视图资源去解析modelandview,然后将解析后的参数放到view中返回到客户端并展现。
3、给定一个txt文件,如何得到某字符串出现的次数
File file = new File("E://test.txt");
InputStream is = new FileInputStream(file);
byte b[] = new byte[1024];
int a = is.read(b);
String str[] = new String(b,0,a).split("");
int count = 0;
for(int i = 0;i<str.length;i++){
if("a".equals(str[i]))count++;
}
System.out.println(count);
4、Java设计模式思想(单列模式,工厂模式,策略模式,共23种设计模式)
a) 单例模式:单例模式核心只需要new一个实例对象的模式,比如数据库连接,在线人数等,一些网站上看到的在线人数统计就是通过单例模式实现的,把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。
b) 策略模式:就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫,飞,外形这三种方法,如果每个鸭子类都写这三个方法会出现代码的冗余,这时候我们可以把鸭子中的叫,飞,外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强,当用户提出新的需求比如鸭子会游泳,那么对于我们oo程序员来讲就非常简单了我们只需要在鸭父类中加一个游泳的方法,让会游泳的鸭子重写游泳方法就可以了。
c) 工厂模式:简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。
5、冒泡排序、二分查找
a) 冒泡
public static void mp(int a[]) {
int swap = 0;
for (int i = 0; i < a.length; i++) {
for (int j = i; j < a.length; j++) {
if (a[j] > a[i]) {
swap = a[i];
a[i] = a[j];
a[j] = swap;
}
}
}
System.out.println(Arrays.toString(a));
}
b)二分查找public static int ef(int a[], int tag) {
int first = 0;
int end = a.length;
for (int i = 0; i < a.length; i++) {
int middle = (first + end) / 2;
if (tag == a[middle]) {
return middle;
}
if (tag > a[middle]) {
first = middle + 1;
}
if (tag < a[middle]) {
end = middle - 1;
}
}
return 0;
}
6、对ajax的理解
a) Ajax为异步请求,即局部刷新技术,在传统的页面中,用户需要点击按钮或者事件触发请求,到刷新页面,而异步技术为不需要点击即可触发事件,这样使得用户体验感增强,比如商城购物车的异步加载,当你点击商品时无需请求后台而直接动态修改参数。
9、父类与子类之间的调用顺序(打印结果)
a) 父类静态代码块
b) 子类静态代码块
c) 父类构造方法
d) 子类构造方法
e) 子类普通方法
f) 重写父类的方法,则打印重写后的方法
10、内部类与外部类的调用
a) 内部类可以直接调用外部类包括private的成员变量,使用外部类引用的this.关键字调用即可
b) 而外部类调用内部类需要建立内部类对象
11、多线程
a)一个进程是一个独立的运行环境,可以看做是一个程序,而线程可以看做是进程的一个任务,比如QQ是一个进程,而一个QQ窗口是一个线程。
b)在多线程程序中,多线程并发可以提高程序的效率,cpu不会因为某个线程等待资源而进入空闲状态,它会把资源让给其他的线程。
c)用户线程就是我们开发程序是创建的线程,而守护线程为系统线程,如JVM虚拟中的GC
d)线程的优先级别:每一个线程都有优先级别,有限级别高的可以先获取CPU资源使该线程从就绪状态转为运行状态。也可以自定义线程的有限级别
e)死锁:至少两个以上线程争取两个以上cpu资源,避免死锁就避免使用嵌套锁,只需要在他们需要同步的地方加锁和避免无限等待
12、AOP与IOC的概念(即spring的核心)
a) IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。
b) AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
13、hibernate的核心思想
a) Hibernate的核心思想是ROM对象关系映射机制。它是将表与表之间的操作映射成对象与对象之间的操作。也就是从数据库中提取的信息会自动按照你设置的映射要求封装成特定的对象。所以hibernate就是通过将数据表实体类的映射,使得对对象的修改对应数据行的修改。
14、Struts1与Struts2的区别
15、最优删除谋字符串的某个字符
16、Arraylist与linkedlist的区别
a) 都是实现list接口的列表,arraylist是基于数组的数据结构,linkedlist是基于链表的数据结构,当获取特定元素时,ArrayList效率比较快,它通过数组下标即可获取,而linkedlist则需要移动指针。当存储元素与删除元素时linkedlist效率较快,只需要将指针移动指定位置增加或者删除即可,而arraylist需要移动数据。
17、mybaties与ibatise的区别
18、数据库优化
a) 选择合适的字段,比如邮箱字段可以设为char(6),尽量把字段设置为notnull,这样查询的时候数据库就不需要比较null值
b) 使用关联查询( left join on)查询代替子查询
c) 使用union联合查询手动创建临时表
d) 开启事物,当数据库执行多条语句出现错误时,事物会回滚,可以维护数据库的完整性
e) 使用外键,事物可以维护数据的完整性但是它却不能保证数据的关联性,使用外键可以保证数据的关联性
f) 使用索引,索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快的多的速度检索特定的行,特别是对于max,min,order by查询时,效果更明显
g) 优化的查询语句,绝大多数情况下,使用索引可以提高查询的速度,但如果sql语句使用不恰当的话,索引无法发挥它的特性。
19、Tomcat服务器优化(内存,并发连接数,缓存)
a) 内存优化:主要是对Tomcat启动参数进行优化,我们可以在Tomcat启动脚本中修改它的最大内存数等等。
b) 线程数优化:Tomcat的并发连接参数,主要在Tomcat配置文件中server.xml中配置,比如修改最小空闲连接线程数,用于提高系统处理性能等等。
c) 优化缓存:打开压缩功能,修改参数,比如压缩的输出内容大小默认为2KB,可以适当的修改。
20、HTTP协议
a) 常用的请求方法有get、post
b) Get与post的区别:传送数据,get携带参数与访问地址传送,用户可以看见,这的话信息会不安全,导致信息泄露。而post则将字段与对应值封装在实体中传送,这个过程用户是不可见的。Get传递参数有限制,而post无限制。
21、TCP/UDP协议
22、Java集合类框架的基本接口有哪些
a) Collection集合接口,List、set实现Collection接口,arraylist、linkedlist,vector实现list接口,stack继承vector,Map接口,hashtable、hashmap实现map接口
23、类加载的过程
a) 遇到一个新的类时,首先会到方法区去找class文件,如果没有找到就会去硬盘中找class文件,找到后会返回,将class文件加载到方法区中,在类加载的时候,静态成员变量会被分配到方法区的静态区域,非静态成员变量分配到非静态区域,然后开始给静态成员变量初始化,赋默认值,赋完默认值后,会根据静态成员变量书写的位置赋显示值,然后执行静态代码。当所有的静态代码执行完,类加载才算完成。
24、对象的创建
a) 遇到一个新类时,会进行类的加载,定位到class文件
b) 对所有静态成员变量初始化,静态代码块也会执行,而且只在类加载的时候执行一次
c) New 对象时,jvm会在堆中分配一个足够大的存储空间
d) 存储空间清空,为所有的变量赋默认值,所有的对象引用赋值为null
e) 根据书写的位置给字段一些初始化操作
f) 调用构造器方法(没有继承)
25、jvm的优化
a) 设置参数,设置jvm的最大内存数
b) 垃圾回收器的选择
26、高并发处理
a) 了解一点高并发性问题,比如一W人抢一张票时,如何保证票在没买走的情况下所有人都能看见这张票,显然是不能用同步机制,因为synchronize是锁同步一次只能一个人进行。这时候可以用到锁机制,采用乐观锁可以解决这个问题。乐观锁的简单意思是在不锁定表的情况下,利用业务的控制来解决并发问题,这样即保证数据的可读性,又保证保存数据的排他性,保证性能的同时解决了并发带来的脏读数据问题。
27、事物的理解
a) 事物具有原子性,一致性,持久性,隔离性
b) 原子性:是指在一个事物中,要么全部执行成功,要么全部失败回滚。
c) 一致性:事物执行之前和执行之后都处于一致性状态
d) 持久性:事物多数据的操作是永久性
e) 隔离性:当一个事物正在对数据进行操作时,另一个事物不可以对数据进行操作,也就是多个并发事物之间相互隔离。
28、Struts工作流程
a) 客户端发出一个请求到servlet容器
b) 请求经过一些列过滤被filterdispatcher调用,filterdispatch通过actionMapper去找相对应的action。
c) Actionmapper找到对应的action返回给filterdispatch,dispatch把处理权交给actionproxy
d) Actionproxy通过配置文件找到对应的action类
e) Actionproxy创建一个actionIinvocation的实例处理业务逻辑
f) 一旦action处理完毕,actioninvocation负责根据stuts.xml的配置找到对应的返回结果。返回结果通常是jsp页面。
什么是存储过程?有哪些优缺点?
存储过程就像我们编程语言中的函数一样,封装了我们的代码(PLSQL、T-SQL)。
存储过程的优点:
存储过程的缺点:
三个范式是什么
第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。 第二范式(2NF):数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。 第三范式(3NF):在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在”A → B → C”的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非关键字段x → 非关键字段y
上面的文字我们肯定是看不懂的,也不愿意看下去的。接下来我就总结一下:
参考链接:
什么是视图?以及视图的使用场景有哪些?
视图是一种基于数据表的一种虚表
有的时候,我们可能只关系一张数据表中的某些字段,而另外的一些人只关系同一张数据表的某些字段…
那么把全部的字段都都显示给他们看,这是不合理的。
我们应该做到:他们想看到什么样的数据,我们就给他们什么样的数据…一方面就能够让他们只关注自己的数据,另一方面,我们也保证数据表一些保密的数据不会泄露出来…
我们在查询数据的时候,常常需要编写非常长的SQL语句,几乎每次都要写很长很长….上面已经说了,视图就是基于查询的一种虚表,也就是说,视图可以将查询出来的数据进行封装。。。那么我们在使用的时候就会变得非常方便…
值得注意的是:使用视图可以让我们专注与逻辑,但不提高查询效率
drop、delete与truncate分别在什么场景之下使用?
我们来对比一下他们的区别:
drop table
truncate table
delete from
5)删除速度慢,需要逐行删除
不再需要一张表的时候,用drop
索引是什么?有什么作用以及优缺点?
什么是索引【Index】
索引表把数据变成是有序的….
快速定位到硬盘中的数据文件…
rowid的特点
索引的特点
需要注意的是:Oracle是自动帮我们管理索引的,并且如果我们指定了primary key或者unique约束,系统会自动在对应的列上创建索引..
什么时候【要】创建索引
什么时候【不要】创建索引
索引优缺点:
索引分类:
深入理解索引可参考:
什么是事务?
事务简单来说:一个Session中所进行所有的操作,要么同时成功,要么同时失败
ACID — 数据库事务正确执行的四个基本要素
一个支持事务(Transaction)中的数据库系统,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易。
举个例子:A向B转账,转账这个流程中如果出现问题,事务可以让数据恢复成原来一样【A账户的钱没变,B账户的钱也没变】。
事例说明:
-
-
- /*
- * 我们来模拟A向B账号转账的场景
- * A和B账户都有1000块,现在我让A账户向B账号转500块钱
- *
- * */
- //JDBC默认的情况下是关闭事务的,下面我们看看关闭事务去操作转账操作有什么问题
-
- //A账户减去500块
- String sql = "UPDATE a SET money=money-500 ";
- preparedStatement = connection.prepareStatement(sql);
- preparedStatement.executeUpdate();
-
- //B账户多了500块
- String sql2 = "UPDATE b SET money=money+500";
- preparedStatement = connection.prepareStatement(sql2);
- preparedStatement.executeUpdate();
从上面看,我们的确可以发现A向B转账,成功了。可是如果A向B转账的过程中出现了问题呢?下面模拟一下
-
- //A账户减去500块
- String sql = "UPDATE a SET money=money-500 ";
- preparedStatement = connection.prepareStatement(sql);
- preparedStatement.executeUpdate();
-
- //这里模拟出现问题
- int a = 3 / 0;
-
-
- String sql2 = "UPDATE b SET money=money+500";
- preparedStatement = connection.prepareStatement(sql2);
- preparedStatement.executeUpdate();
显然,上面代码是会抛出异常的,我们再来查询一下数据。A账户少了500块钱,B账户的钱没有增加。这明显是不合理的。
我们可以通过事务来解决上面出现的问题
-
- //开启事务,对数据的操作就不会立即生效。
- connection.setAutoCommit(false);
-
- //A账户减去500块
- String sql = "UPDATE a SET money=money-500 ";
- preparedStatement = connection.prepareStatement(sql);
- preparedStatement.executeUpdate();
-
- //在转账过程中出现问题
- int a = 3 / 0;
-
- //B账户多500块
- String sql2 = "UPDATE b SET money=money+500";
- preparedStatement = connection.prepareStatement(sql2);
- preparedStatement.executeUpdate();
-
- //如果程序能执行到这里,没有抛出异常,我们就提交数据
- connection.commit();
-
- //关闭事务【自动提交】
- connection.setAutoCommit(true);
-
-
- } catch (SQLException e) {
- try {
- //如果出现了异常,就会进到这里来,我们就把事务回滚【将数据变成原来那样】
- connection.rollback();
-
- //关闭事务【自动提交】
- connection.setAutoCommit(true);
- } catch (SQLException e1) {
- e1.printStackTrace();
- }
上面的程序也一样抛出了异常,A账户钱没有减少,B账户的钱也没有增加。
注意:当Connection遇到一个未处理的SQLException时,系统会非正常退出,事务也会自动回滚,但如果程序捕获到了异常,是需要在catch中显式回滚事务的。
数据库定义了4个隔离级别:
分别对应Connection类中的4个常量
脏读:一个事务读取到另外一个事务未提交的数据
例子:A向B转账,A执行了转账语句,但A还没有提交事务,B读取数据,发现自己账户钱变多了!B跟A说,我已经收到钱了。A回滚事务【rollback】,等B再查看账户的钱时,发现钱并没有多。
不可重复读:一个事务读取到另外一个事务已经提交的数据,也就是说一个事务可以看到其他事务所做的修改
注:A查询数据库得到数据,B去修改数据库的数据,导致A多次查询数据库的结果都不一样【危害:A每次查询的结果都是受B的影响的,那么A查询出来的信息就没有意思了】
虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
注:和不可重复读类似,但虚读(幻读)会读到其他事务的插入的数据,导致前后读取不一致
简单总结:脏读是不可容忍的,不可重复读和虚读在一定的情况下是可以的【做统计的肯定就不行】。
数据库的乐观锁和悲观锁是什么?
确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性,乐观锁和悲观锁是并发控制主要采用的技术手段。
悲观锁:
乐观锁:
参考资料:
超键、候选键、主键、外键分别是什么?
候选码和主码:
例子:邮寄地址(城市名,街道名,邮政编码,单位名,收件人)
SQL 约束有哪几种?
数据库运行于哪种状态下可以防止数据的丢失?
在archivelog mode(归档模式)只要其归档日志文件不丢失,就可以有效地防止数据丢失。
Mysql的存储引擎有以下几种:
我的是5.7.15版本,默认使用的是Innodb版本!
常用的存储引擎有以下:
同一个数据库也可以使用多种存储引擎的表。如果一个表修改要求比较高的事务处理,可以选择InnoDB。这个数据库中可以将查询要求比较高的表选择MyISAM存储。如果该数据库需要一个用于查询的临时表,可以选择MEMORY存储引擎。
参考资料:
MyIASM和Innodb两种引擎所使用的索引的数据结构是什么?
答案:都是B+树!
MyIASM引擎,B+树的数据结构中存储的内容实际上是实际数据的地址值。也就是说它的索引和实际数据是分开的,只不过使用索引指向了实际数据。这种索引的模式被称为非聚集索引。
Innodb引擎的索引的数据结构也是B+树,只不过数据结构中存储的都是实际的数据,这种索引有被称为聚集索引。
varchar和char的区别
Char是一种固定长度的类型,varchar是一种可变长度的类型
mysql有关权限的表都有哪几个
MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。下面分别介绍一下这些表的结构和内容:
数据表损坏的修复方式有哪些?
使用 myisamchk 来修复,具体步骤:
使用repair table 或者 OPTIMIZE table命令来修复,REPAIR TABLE table_name 修复表 OPTIMIZE TABLE table_name 优化表 REPAIR TABLE 用于修复被破坏的表。
OPTIMIZE TABLE 用于回收闲置的数据库空间,当表上的数据行被删除时,所占据的磁盘空间并没有立即被回收,使用了OPTIMIZE TABLE命令后这些空间将被回收,并且对磁盘上的数据行进行重排(注意:是磁盘上,而非数据库)
MySQL中InnoDB引擎的行锁是通过加在什么上完成
InnoDB是基于索引来完成行锁
例: select * from tab_with_index where id = 1 for update
;
for update
可以根据条件来完成行锁锁定,并且 id 是有索引键的列,
如果 id 不是索引键那么InnoDB将完成表锁,,并发将无从谈起
在我们书写SQL语句的时候,其实书写的顺序、策略会影响到SQL的性能,虽然实现的功能是一样的,但是它们的性能会有些许差别。
因此,下面就讲解在书写SQL的时候,怎么写比较好。
数据库的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表将被最先处理
在FROM子句中包含多个表的情况下:
如果有3个以上的表连接查询:
例如:查询员工的编号,姓名,工资,工资等级,部门名
emp表被引用得最多,记录数也是最多,因此放在form字句的最后面
-
- select emp.empno,emp.ename,emp.sal,salgrade.grade,dept.dname
- from salgrade,dept,emp
- where (emp.deptno = dept.deptno) and (emp.sal between salgrade.losal and salgrade.hisal)
-
数据库采用自右而左的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之左,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的之右。
emp.sal可以过滤多条记录,写在WHERE字句的最右边
-
- select emp.empno,emp.ename,emp.sal,dept.dname
- from dept,emp
- where (emp.deptno = dept.deptno) and (emp.sal > 1500)
我们当时学习的时候,“*”号是可以获取表中全部的字段数据的。
这里仅仅是:删除表的全部记录,除了表结构才这样做。
DELETE是一条一条记录的删除,而Truncate是将整个表删除,保留表结构,这样比DELETE快
例如使用mysql的concat()函数会比使用||来进行拼接快,因为concat()函数已经被mysql优化过了。
如果表或列的名称太长了,使用一些简短的别名也能稍微提高一些SQL的性能。毕竟要扫描的字符长度就变少了。。。
comiit会释放回滚点…
索引就是为了提高我们的查询数据的,当表的记录量非常大的时候,我们就可以使用索引了。
我们在编写SQL 的时候,官方推荐的是使用大写来写关键字,因为Oracle服务器总是先将小写字母转成大写后,才执行
因为Oracle服务器遇到NOT后,他就会停止目前的工作,转而执行全表扫描
WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描,这样会变得变慢
>=
替代 >
- 低效:
- SELECT * FROM EMP WHERE DEPTNO > 3
- 首先定位到DEPTNO=3的记录并且扫描到第一个DEPT大于3的记录
- 高效:
- SELECT * FROM EMP WHERE DEPTNO >= 4
- 直接跳到第一个DEPT等于4的记录
-
- select * from emp where sal = 1500 or sal = 3000 or sal = 800;
- select * from emp where sal in (1500,3000,800);
如果索引是建立在多个列上,只有在它的第一个列被WHERE子句引用时,优化器才会选择使用该索引。 当只引用索引的第二个列时,不引用索引的第一个列时,优化器使用了全表扫描而忽略了索引
-
- create index emp_sal_job_idex
- on emp(sal,job);
- ----------------------------------
- select *
- from emp
- where job != 'SALES';
-
-
- 上边就不使用索引了。
这个么多花钱咯!
下列练习题参考自公众号Java知音:
基本表结构:
- student(sno,sname,sage,ssex)学生表
- course(cno,cname,tno) 课程表
- sc(sno,cno,score) 成绩表
-
- teacher(tno,tname) 教师表
题目:
-
-
- 101,查询课程1的成绩比课程2的成绩高的所有学生的学号
- select a.sno from
- (select sno,score from sc where cno=1) a,
- (select sno,score from sc where cno=2) b
- where a.score>b.score and a.sno=b.sno
-
-
-
- 102,查询平均成绩大于60分的同学的学号和平均成绩
- select a.sno as "学号", avg(a.score) as "平均成绩"
- from
- (select sno,score from sc) a
- group by sno having avg(a.score)>60
-
-
-
- 103,查询所有同学的学号、姓名、选课数、总成绩
- select a.sno as 学号, b.sname as 姓名,
- count(a.cno) as 选课数, sum(a.score) as 总成绩
- from sc a, student b
- where a.sno = b.sno
- group by a.sno, b.sname
-
- 或者:
-
- selectstudent.sno as 学号, student.sname as 姓名,
- count(sc.cno) as 选课数, sum(score) as 总成绩
- from student left Outer join sc on student.sno = sc.sno
- group by student.sno, sname
-
- 104,查询姓“张”的老师的个数
-
- selectcount(distinct(tname)) from teacher where tname like '张%‘
- 或者:
- select tname as "姓名", count(distinct(tname)) as "人数"
- from teacher
- where tname like'张%'
- group by tname
- 105,查询没学过“张三”老师课的同学的学号、姓名
- select student.sno,student.sname from student
- where sno not in (select distinct(sc.sno) from sc,course,teacher
- where sc.cno=course.cno and teacher.tno=course.tno and teacher.tname='张三')
- 106,查询同时学过课程1和课程2的同学的学号、姓名
- select sno, sname from student
- where sno in (select sno from sc where sc.cno = 1)
- and sno in (select sno from sc where sc.cno = 2)
- 或者:
- selectc.sno, c.sname from
- (select sno from sc where sc.cno = 1) a,
- (select sno from sc where sc.cno = 2) b,
- student c
- where a.sno = b.sno and a.sno = c.sno
- 或者:
- select student.sno,student.sname from student,sc where student.sno=sc.sno and sc.cno=1
- and exists( select * from sc as sc_2 where sc_2.sno=sc.sno and sc_2.cno=2)
- 107,查询学过“李四”老师所教所有课程的所有同学的学号、姓名
- select a.sno, a.sname from student a, sc b
- where a.sno = b.sno and b.cno in
- (select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四')
- 或者:
- select a.sno, a.sname from student a, sc b,
- (select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四') e
- where a.sno = b.sno and b.cno = e.cno
- 108,查询课程编号1的成绩比课程编号2的成绩高的所有同学的学号、姓名
- select a.sno, a.sname from student a,
- (select sno, score from sc where cno = 1) b,
- (select sno, score from sc where cno = 2) c
- where b.score > c.score and b.sno = c.sno and a.sno = b.sno
- 109,查询所有课程成绩小于60分的同学的学号、姓名
- select sno,sname from student
- where sno not in (select distinct sno from sc where score > 60)
- 110,查询至少有一门课程与学号为1的同学所学课程相同的同学的学号和姓名
- select distinct a.sno, a.sname
- from student a, sc b
- where a.sno <> 1 and a.sno=b.sno and
- b.cno in (select cno from sc where sno = 1)
- 或者:
- select s.sno,s.sname
- from student s,
- (select sc.sno
- from sc
- where sc.cno in (select sc1.cno from sc sc1 where sc1.sno=1)and sc.sno<>1
- group by sc.sno)r1
- where r1.sno=s.sno
- 111、把“sc”表中“王五”所教课的成绩都更改为此课程的平均成绩
- update sc set score = (select avg(sc_2.score) from sc sc_2 wheresc_2.cno=sc.cno)
- from course,teacher where course.cno=sc.cno and course.tno=teacher.tno andteacher.tname='王五'
- 112、查询和编号为2的同学学习的课程完全相同的其他同学学号和姓名
- 这一题分两步查:
- 1,
- select sno
- from sc
- where sno <> 2
- group by sno
- having sum(cno) = (select sum(cno) from sc where sno = 2)
- 2,
- select b.sno, b.sname
- from sc a, student b
- where b.sno <> 2 and a.sno = b.sno
- group by b.sno, b.sname
- having sum(cno) = (select sum(cno) from sc where sno = 2)
- 113、删除学习“王五”老师课的sc表记录
- delete sc from course, teacher
- where course.cno = sc.cno and course.tno = teacher.tno and tname = '王五'
- 114、向sc表中插入一些记录,这些记录要求符合以下条件:
- 将没有课程3成绩同学的该成绩补齐, 其成绩取所有学生的课程2的平均成绩
- insert sc select sno, 3, (select avg(score) from sc where cno = 2)
- from student
- where sno not in (select sno from sc where cno = 3)
- 115、按平平均分从高到低显示所有学生的如下统计报表:
- -- 学号,企业管理,马克思,UML,数据库,物理,课程数,平均分
- select sno as 学号
- ,max(case when cno = 1 then score end) AS 企业管理
- ,max(case when cno = 2 then score end) AS 马克思
- ,max(case when cno = 3 then score end) AS UML
- ,max(case when cno = 4 then score end) AS 数据库
- ,max(case when cno = 5 then score end) AS 物理
- ,count(cno) AS 课程数
- ,avg(score) AS 平均分
- FROM sc
- GROUP by sno
- ORDER by avg(score) DESC
- 116、查询各科成绩最高分和最低分:
- 以如下形式显示:课程号,最高分,最低分
- select cno as 课程号, max(score) as 最高分, min(score) 最低分
- from sc group by cno
- select course.cno as '课程号'
- ,MAX(score) as '最高分'
- ,MIN(score) as '最低分'
- from sc,course
- where sc.cno=course.cno
- group by course.cno
- 117、按各科平均成绩从低到高和及格率的百分数从高到低顺序
- SELECT t.cno AS 课程号,
- max(course.cname)AS 课程名,
- isnull(AVG(score),0) AS 平均成绩,
- 100 * SUM(CASE WHEN isnull(score,0)>=60 THEN 1 ELSE 0 END)/count(1) AS 及格率
- FROM sc t, course
- where t.cno = course.cno
- GROUP BY t.cno
- ORDER BY 及格率 desc
- 118、查询如下课程平均成绩和及格率的百分数(用"1行"显示):
- 企业管理(001),马克思(002),UML (003),数据库(004)
- select
- avg(case when cno = 1 then score end) as 平均分1,
- avg(case when cno = 2 then score end) as 平均分2,
- avg(case when cno = 3 then score end) as 平均分3,
- avg(case when cno = 4 then score end) as 平均分4,
- 100 * sum(case when cno = 1 and score > 60 then 1 else 0 end) / sum(casewhen cno = 1 then 1 else 0 end) as 及格率1,
- 100 * sum(case when cno = 2 and score > 60 then 1 else 0 end) / sum(casewhen cno = 2 then 1 else 0 end) as 及格率2,
- 100 * sum(case when cno = 3 and score > 60 then 1 else 0 end) / sum(casewhen cno = 3 then 1 else 0 end) as 及格率3,
- 100 * sum(case when cno = 4 and score > 60 then 1 else 0 end) / sum(casewhen cno = 4 then 1 else 0 end) as 及格率4
- from sc
- 119、查询不同老师所教不同课程平均分, 从高到低显示
- select max(c.tname) as 教师, max(b.cname) 课程, avg(a.score) 平均分
- from sc a, course b, teacher c
- where a.cno = b.cno and b.tno = c.tno
- group by a.cno
- order by 平均分 desc
- 或者:
- select r.tname as '教师',r.rname as '课程' , AVG(score) as '平均分'
- from sc,
- (select t.tname,c.cno as rcso,c.cname as rname
- from teacher t ,course c
- where t.tno=c.tno)r
- where sc.cno=r.rcso
- group by sc.cno,r.tname,r.rname
- order by AVG(score) desc
- 120、查询如下课程成绩均在第3名到第6名之间的学生的成绩:
- -- [学生ID],[学生姓名],企业管理,马克思,UML,数据库,平均成绩
- select top 6 max(a.sno) 学号, max(b.sname) 姓名,
- max(case when cno = 1 then score end) as 企业管理,
- max(case when cno = 2 then score end) as 马克思,
- max(case when cno = 3 then score end) as UML,
- max(case when cno = 4 then score end) as 数据库,
- avg(score) as 平均分
- from sc a, student b
- where a.sno not in
- (select top 2 sno from sc where cno = 1 order by score desc)
- and a.sno not in (select top 2 sno from sc where cno = 2 order by scoredesc)
- and a.sno not in (select top 2 sno from sc where cno = 3 order by scoredesc)
- and a.sno not in (select top 2 sno from sc where cno = 4 order by scoredesc)
- and a.sno = b.sno
- group by a.sno
在Mysql中,一个用户下可以创建多个库:
而在Oracle中,Oracle服务器是由两部分组成
一个数据库实例可拥有多个用户,一个用户默认拥有一个表空间。
表空间是存储我们数据库表的地方,表空间内可以有多个文件。
当我们使用Oracle作为我们数据库时,我们需要指定用户、表空间来存储我们所需要的数据!
参考资料:
Hibernate中Session的load和get方法的区别是什么?
- 客户端请求提交到DispatcherServlet
- 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
- DispatcherServlet将请求提交到Controller
- Controller调用业务逻辑处理后,返回ModelAndView
- DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
- 视图负责将结果显示到客户端
#
和$
书写占位符有什么区别? #
将传入的数据都当成一个字符串,会对传入的数据自动加上引号;$
将传入的数据直接显示生成在SQL中。注意:使用$
占位符可能会导致SQL注射攻击,能用#
的地方就不要使用$
,写order by子句的时候应该用$
而不是#
。($:where admin=admin 永远为true,而#:where admin='admin',自动加上引号,不会被sql注入攻击)什么是IoC和DI?DI是如何实现的?
答:IoC叫控制反转,是Inversion of Control的缩写,DI(Dependency Injection)叫依赖注入,是对IoC更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的"控制反转"就是对组件对象控制权的转移,从程序代码本身转移到了外部容器,由容器来创建对象并管理对象之间的依赖关系。IoC体现了好莱坞原则 - "Don’t call me, we will call you"。依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出来,交给容器来完成。DI是对IoC更准确的描述,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
举个例子:一个类A需要用到接口B中的方法,那么就需要为类A和接口B建立关联或依赖关系,最原始的方法是在类A中创建一个接口B的实现类C的实例,但这种方法需要开发人员自行维护二者的依赖关系,也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。如果通过一个容器来管理这些对象以及对象的依赖关系,则只需要在类A中定义好用于关联接口B的方法(构造器或setter方法),将类A和接口B的实现类C放入容器中,通过对容器的配置来实现二者的关联。
依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。
Spring中Bean的作用域有哪些?
答:在Spring的早期版本中,仅有两个作用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype通常翻译为原型。
补充:设计模式中的创建型模式中也有一个原型模式,原型模式也是一个常用的模式,例如做一个室内设计软件,所有的素材都在工具箱中,而每次从工具箱中取出的都是素材对象的一个原型,可以通过对象克隆来实现原型模式。
Spring 2.x中针对WebApplicationContext新增了3个作用域,分别是:request(每次HTTP请求都会创建一个新的Bean)、session(同一个HttpSession共享同一个Bean,不同的HttpSession使用不同的Bean)和globalSession(同一个全局Session共享一个Bean)。
说明:单例模式和原型模式都是重要的设计模式。一般情况下,无状态或状态不可变的类适合使用单例模式。在传统开发中,由于DAO持有Connection这个非线程安全对象因而没有使用单例模式;但在Spring环境下,所有DAO类对可以采用单例模式,因为Spring利用AOP和Java API中的ThreadLocal对非线程安全的对象进行了特殊处理。
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal,顾名思义是线程的一个本地化对象,当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量。
ThreadLocal类非常简单好用,只有四个方法,能用上的也就是下面三个方法:
- void set(T value):设置当前线程的线程局部变量的值。
- T get():获得当前线程所对应的线程局部变量的值。
- void remove():删除当前线程中线程局部变量的值。
ThreadLocal是如何做到为每一个线程维护一份独立的变量副本的呢?在ThreadLocal类中有一个Map,键为线程对象,值是其线程对应的变量的副本,自己要模拟实现一个ThreadLocal类其实并不困难,代码如下所示:
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
-
- public class MyThreadLocal<T> {
- private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());
-
- public void set(T newValue) {
- map.put(Thread.currentThread(), newValue);
- }
-
- public T get() {
- return map.get(Thread.currentThread());
- }
-
- public void remove() {
- map.remove(Thread.currentThread());
- }
- }
说明:从JDK 1.3开始,Java提供了动态代理技术,允许开发者在运行时创建接口的代理实例,主要包括Proxy类和InvocationHandler接口。下面的例子使用动态代理为ArrayList编写一个代理,在添加和删除元素时,在控制台打印添加或删除的元素以及ArrayList的大小:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.util.List;
-
- public class ListProxy<T> implements InvocationHandler {
- private List<T> target;
-
- public ListProxy(List<T> target) {
- this.target = target;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Object retVal = null;
- System.out.println("[" + method.getName() + ": " + args[0] + "]");
- retVal = method.invoke(target, args);
- System.out.println("[size=" + target.size() + "]");
- return retVal;
- }
-
- }
- import java.lang.reflect.Proxy;
- import java.util.ArrayList;
- import java.util.List;
-
- public class ProxyTest2 {
-
- @SuppressWarnings("unchecked")
- public static void main(String[] args) {
- List<String> list = new ArrayList<String>();
- Class<?> clazz = list.getClass();
- ListProxy<String> myProxy = new ListProxy<String>(list);
- List<String> newList = (List<String>)
- Proxy.newProxyInstance(clazz.getClassLoader(),
- clazz.getInterfaces(), myProxy);
- newList.add("apple");
- newList.add("banana");
- newList.add("orange");
- newList.remove("banana");
- }
- }
说明:使用Java的动态代理有一个局限性就是代理的类必须要实现接口,虽然面向接口编程是每个优秀的Java程序都知道的规则,但现实往往不尽如人意,对于没有实现接口的类如何为其生成代理呢?继承!继承是最经典的扩展已有代码能力的手段,虽然继承常常被初学者滥用,但继承也常常被进阶的程序员忽视。CGLib采用非常底层的字节码生成技术,通过为一个类创建子类来生成代理,它弥补了Java动态代理的不足,因此Spring中动态代理和CGLib都是创建代理的重要手段,对于实现了接口的类就用动态代理为其生成代理类,而没有实现接口的类就用CGLib通过继承的方式为其创建代理。
Spring中自动装配的方式有哪些?
答:
- no:不进行自动装配,手动设置Bean的依赖关系。
- byName:根据Bean的名字进行自动装配。
- byType:根据Bean的类型进行自动装配。
- constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
- autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
说明:自动装配没有自定义装配方式那么精确,而且不能自动装配简单属性(基本类型、字符串等),在使用时应注意。
Spring中如何使用注解来配置Bean?有哪些相关的注解?
答:首先需要在Spring配置文件中增加如下配置:
<context:component-scan base-package="org.example"/>
然后可以用@Component、@Controller、@Service、@Repository注解来标注需要由Spring IoC容器进行对象托管的类。这几个注解没有本质区别,只不过@Controller通常用于控制器,@Service通常用于业务逻辑类,@Repository通常用于仓储类(例如我们的DAO实现类),普通的类用@Component来标注。
选择使用Spring框架的原因(Spring框架为企业级开发带来的好处有哪些)?Spring IoC容器配置Bean的方式?
答:
- 基于XML文件进行配置。
- 基于注解进行配置。
- 基于Java程序进行配置(Spring 3+)
- package com.jackfrued.bean;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- @Component
- public class Person {
- private String name;
- private int age;
- @Autowired
- private Car car;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-
- public void setCar(Car car) {
- this.car = car;
- }
-
- @Override
- public String toString() {
- return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
- }
-
- }
- package com.jackfrued.bean;
-
- import org.springframework.stereotype.Component;
-
- @Component
- public class Car {
- private String brand;
- private int maxSpeed;
-
- public Car(String brand, int maxSpeed) {
- this.brand = brand;
- this.maxSpeed = maxSpeed;
- }
-
- @Override
- public String toString() {
- return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
- }
-
- }
- package com.jackfrued.config;
-
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- import com.jackfrued.bean.Car;
- import com.jackfrued.bean.Person;
-
- @Configuration
- public class AppConfig {
-
- @Bean
- public Car car() {
- return new Car("Benz", 320);
- }
-
- @Bean
- public Person person() {
- return new Person("骆昊", 34);
- }
- }
- package com.jackfrued.test;
-
- import org.springframework.context.ConfigurableApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
- import com.jackfrued.bean.Person;
- import com.jackfrued.config.AppConfig;
-
- class Test {
-
- public static void main(String[] args) {
- // TWR (Java 7+)
- try(ConfigurableApplicationContext factory = new AnnotationConfigApplicationContext(AppConfig.class)) {
- Person person = factory.getBean(Person.class);
- System.out.println(person);
- }
- }
- }
在Web项目中如何获得Spring的IoC容器?
答:
- WebApplicationContext ctx =
- WebApplicationContextUtils.getWebApplicationContext(servletContext);
ThreadLocal(线程变量副本)
Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量。
采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean、事务管理、任务调度、AOP等模块都出现了它的身影。
Spring中绝大部分Bean都可以声明成Singleton作用域,采用ThreadLocal进行封装,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。
友情链接:深入研究java.lang.ThreadLocal类
Java内存模型:
Java虚拟机规范中将Java运行时数据分为六种。
1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。
2.Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。
3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。
4.Java堆:所有线程共享的一块内存区域,对象实例几乎都在这分配内存。
5.方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。
6.运行时常量池:代表运行时每个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。
友情链接: Java中JVM虚拟机详解
“你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?”
在什么时候:
1.新生代有一个Eden区和两个survivor区,首先将对象放入Eden区,如果空间不足就向其中的一个survivor区上放,如果仍然放不下就会引发一次发生在新生代的minor GC,将存活的对象放入另一个survivor区中,然后清空Eden和之前的那个survivor区的内存。在某次GC过程中,如果发现仍然又放不下的对象,就将这些对象放入老年代内存里去。
2.大对象以及长期存活的对象直接进入老年区。
3.当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,如果这些马上要到老年区的老年对象的大小超过了老年区的剩余大小,那么执行一次Full GC以尽可能地获得老年区的空间。
对什么东西:从GC Roots搜索不到,而且经过一次标记清理之后仍没有复活的对象。
做什么:
新生代:复制清理;
老年代:标记-清除和标记-压缩算法;
永久代:存放Java中的类和加载类的类加载器本身。
GC Roots都有哪些:
1. 虚拟机栈中的引用的对象
2. 方法区中静态属性引用的对象,常量引用的对象
3. 本地方法栈中JNI(即一般说的Native方法)引用的对象。
友情链接:Java GC的那些事(上)
友情链接:Java GC的那些事(下)
友情链接:CMS垃圾收集器介绍
Synchronized 与Lock都是可重入锁,同一个线程再次进入同步代码的时候.可以使用自己已经获取到的锁。
Synchronized是悲观锁机制,独占锁。而Locks.ReentrantLock是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
ReentrantLock适用场景
友情链接: Synchronized关键字、Lock,并解释它们之间的区别
StringBuffer是线程安全的,每次操作字符串,String会生成一个新的对象,而StringBuffer不会;StringBuilder是非线程安全的
友情链接:String、StringBuffer与StringBuilder之间区别
fail-fast:机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件
happens-before:如果两个操作之间具有happens-before 关系,那么前一个操作的结果就会对后面一个操作可见。
1.程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
2.监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
3.volatile变量规则:对一个volatile域的写,happens- before于任意后续对这个volatile域的读。
4.传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。
5.线程启动规则:Thread对象的start()方法happens- before于此线程的每一个动作。
Volatile和Synchronized四个不同点:
1 粒度不同,前者针对变量 ,后者锁对象和类
2 syn阻塞,volatile线程不阻塞
3 syn保证三大特性,volatile不保证原子性
4 syn编译器优化,volatile不优化
volatile具备两种特性:
1. 保证此变量对所有线程的可见性,指一条线程修改了这个变量的值,新值对于其他线程来说是可见的,但并不是多线程安全的。
2. 禁止指令重排序优化。
Volatile如何保证内存可见性:
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
2.当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
同步:就是一个任务的完成需要依赖另外一个任务,只有等待被依赖的任务完成后,依赖任务才能完成。
异步:不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,只要自己任务完成了就算完成了,被依赖的任务是否完成会通知回来。(异步的特点就是通知)。
打电话和发短信来比喻同步和异步操作。
阻塞:CPU停下来等一个慢的操作完成以后,才会接着完成其他的工作。
非阻塞:非阻塞就是在这个慢的执行时,CPU去做其他工作,等这个慢的完成后,CPU才会接着完成后续的操作。
非阻塞会造成线程切换增加,增加CPU的使用时间能不能补偿系统的切换成本需要考虑。
CAS(Compare And Swap) 无锁算法:
CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
友情链接:非阻塞同步算法与CAS(Compare and Swap)无锁算法
线程池的作用:
在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
常用线程池:ExecutorService 是主要的实现类,其中常用的有
Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。
友情链接:线程池原理
友情链接:线程池原理解析
类加载器工作机制:
1.装载:将Java二进制代码导入jvm中,生成Class文件。
2.连接:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工作。
双亲委派模型:类加载器收到类加载请求,首先将请求委派给父类加载器完成
用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。
友情链接:深入理解Java虚拟机笔记—双亲委派模型
友情链接:JVM类加载的那些事
友情链接:JVM(1):Java 类的加载机制
一致性哈希:
Memcahed缓存:
数据结构:key,value对
使用方法:get,put等方法
Redis数据结构: String—字符串(key-value 类型)
Hash—字典(hashmap) Redis的哈希结构可以使你像在数据库中更新一个属性一样只修改某一项属性值
List—列表 实现消息队列
Set—集合 利用唯一性
Sorted Set—有序集合 可以进行排序
可以实现数据持久化
友情链接: Spring + Redis 实现数据的缓存
索引:B+,B-,全文索引
Mysql的索引是一个数据结构,旨在使数据库高效的查找数据。
常用的数据结构是B+Tree,每个叶子节点不但存放了索引键的相关信息还增加了指向相邻叶子节点的指针,这样就形成了带有顺序访问指针的B+Tree,做这个优化的目的是提高不同区间访问的性能。
什么时候使用索引:
1. 经常出现在group by,order by和distinc关键字后面的字段
2. 经常与其他表进行连接的表,在连接字段上应该建立索引
3. 经常出现在Where子句中的字段
4. 经常出现用作查询选择的字段
友情链接:MySQL索引背后的数据结构及算法原理
Spring IOC (控制反转,依赖注入)
Spring支持三种依赖注入方式,分别是属性(Setter方法)注入,构造注入和接口注入。
在Spring中,那些组成应用的主体及由Spring IOC容器所管理的对象被称之为Bean。
Spring的IOC容器通过反射的机制实例化Bean并建立Bean之间的依赖关系。
简单地讲,Bean就是由Spring IOC容器初始化、装配及被管理的对象。
获取Bean对象的过程,首先通过Resource加载配置文件并启动IOC容器,然后通过getBean方法获取bean对象,就可以调用他的方法。
Spring Bean的作用域:
Singleton:Spring IOC容器中只有一个共享的Bean实例,一般都是Singleton作用域。
Prototype:每一个请求,会产生一个新的Bean实例。
Request:每一次http请求会产生一个新的Bean实例。
友情链接: Spring框架IOC容器和AOP解析
友情链接:浅谈Spring框架注解的用法分析
代理的共有优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。
Java静态代理:
代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,代理对象可以在调用目标对象相应方法前后加上其他业务处理逻辑。
缺点:一个代理类只能代理一个业务类。如果业务类增加方法时,相应的代理类也要增加方法。
Java动态代理:
Java动态代理是写一个类实现InvocationHandler接口,重写Invoke方法,在Invoke方法可以进行增强处理的逻辑的编写,这个公共代理类在运行的时候才能明确自己要代理的对象,同时可以实现该被代理类的方法的实现,然后在实现类方法的时候可以进行增强处理。
实际上:代理对象的方法 = 增强处理 + 被代理对象的方法
JDK和CGLIB生成动态代理类的区别:
JDK动态代理只能针对实现了接口的类生成代理(实例化一个类)。此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑
CGLIB是针对类实现代理,主要是对指定的类生成一个子类(没有实例化一个类),覆盖其中的方法 。
Spring AOP应用场景
性能检测,访问控制,日志管理,事务等。
默认的策略是如果目标类实现接口,则使用JDK动态代理技术,如果目标对象没有实现接口,则默认会采用CGLIB代理
SpringMVC运行原理
1. 客户端请求提交到DispatcherServlet
2. 由DispatcherServlet控制器查询HandlerMapping,找到并分发到指定的Controller中。
4. Controller调用业务逻辑处理后,返回ModelAndView
5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
6. 视图负责将结果显示到客户端
友情链接:Spring:基于注解的Spring MVC(上)
友情链接: Spring:基于注解的Spring MVC(下)
友情链接:SpringMVC与Struts2的对比
一个Http请求
DNS域名解析 –> 发起TCP的三次握手 –> 建立TCP连接后发起http请求 –> 服务器响应http请求,浏览器得到html代码 –> 浏览器解析html代码,并请求html代码中的资源(如javascript、css、图片等) –> 浏览器对页面进行渲染呈现给用户
设计存储海量数据的存储系统:设计一个叫“中间层”的一个逻辑层,在这个层,将数据库的海量数据抓出来,做成缓存,运行在服务器的内存中,同理,当有新的数据到来,也先做成缓存,再想办法,持久化到数据库中,这是一个简单的思路。主要的步骤是负载均衡,将不同用户的请求分发到不同的处理节点上,然后先存入缓存,定时向主数据库更新数据。读写的过程采用类似乐观锁的机制,可以一直读(在写数据的时候也可以),但是每次读的时候会有个版本的标记,如果本次读的版本低于缓存的版本,会重新读数据,这样的情况并不多,可以忍受。
友情链接: HTTP与HTTPS的区别
友情链接: HTTPS 为什么更安全,先看这些
友情链接: HTTP请求报文和HTTP响应报文
友情链接: HTTP 请求方式: GET和POST的比较
Session与Cookie:Cookie可以让服务端跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,则无形的增加了客户端与服务端的数据传输量,
而Session则很好地解决了这个问题,同一个客户端每次和服务端交互时,将数据存储通过Session到服务端,不需要每次都传回所有的Cookie值,而是传回一个ID,每个客户端第一次访问服务器生成的唯一的ID,客户端只要传回这个ID就行了,这个ID通常为NAME为JSESSIONID的一个Cookie。这样服务端就可以通过这个ID,来将存储到服务端的KV值取出了。
Session和Cookie的超时问题,Cookie的安全问题
分布式Session框架
1. 配置服务器,Zookeeper集群管理服务器可以统一管理所有服务器的配置文件
2. 共享这些Session存储在一个分布式缓存中,可以随时写入和读取,而且性能要很好,如Memcache,Tair。
3. 封装一个类继承自HttpSession,将Session存入到这个类中然后再存入分布式缓存中
4. 由于Cookie不能跨域访问,要实现Session同步,要同步SessionID写到不同域名下。
适配器模式:将一个接口适配到另一个接口,Java I/O中InputStreamReader将Reader类适配到InputStream,从而实现了字节流到字符流的准换。
装饰者模式:保持原来的接口,增强原来有的功能。
FileInputStream 实现了InputStream的所有接口,BufferedInputStreams继承自FileInputStream是具体的装饰器实现者,将InputStream读取的内容保存在内存中,而提高读取的性能。
Spring事务配置方法:
1. 切点信息,用于定位实施事物切面的业务类方法
2. 控制事务行为的事务属性,这些属性包括事物隔离级别,事务传播行为,超时时间,回滚规则。
Spring通过aop/tx Schema 命名空间和@Transaction注解技术来进行声明式事物配置。
Mybatis
每一个Mybatis的应用程序都以一个SqlSessionFactory对象的实例为核心。首先用字节流通过Resource将配置文件读入,然后通过SqlSessionFactoryBuilder().build方法创建SqlSessionFactory,然后再通过SqlSessionFactory.openSession()方法创建一个SqlSession为每一个数据库事务服务。
经历了Mybatis初始化 –>创建SqlSession –>运行SQL语句,返回结果三个过程
Servlet和Filter的区别:
整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有如下几个用处:
Filter可以进行对特定的url请求和相应做预处理和后处理。
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
实际上Filter和Servlet极其相似,区别只是Filter不能直接对用户生成响应。实际上Filter里doFilter()方法里的代码就是从多个Servlet的service()方法里抽取的通用代码,通过使用Filter可以实现更好的复用。
Filter和Servlet的生命周期:
1.Filter在web服务器启动时初始化
2.如果某个Servlet配置了 1 ,该Servlet也是在Tomcat(Servlet容器)启动时初始化。
3.如果Servlet没有配置1 ,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。
4.每次请求, Request都会被初始化,响应请求后,请求被销毁。
5.Servlet初始化后,将不会随着请求的结束而注销。
6.关闭Tomcat时,Servlet、Filter依次被注销。
HashMap与HashTable的区别。
1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
3、因为线程安全的问题,HashMap效率比HashTable的要高。
HashMap的实现机制:
1. 维护一个每个元素是一个链表的数组,而且链表中的每个节点是一个Entry[]键值对的数据结构。
2. 实现了数组+链表的特性,查找快,插入删除也快。
3. 对于每个key,他对应的数组索引下标是 int i = hash(key.hashcode)&(len-1);
4. 每个新加入的节点放在链表首,然后该新加入的节点指向原链表首
HashMap和TreeMap区别
友情链接: Java中HashMap和TreeMap的区别深入理解
HashMap冲突
友情链接: HashMap冲突的解决方法以及原理分析
友情链接: HashMap的工作原理
友情链接: HashMap和Hashtable的区别
友情链接: 2种办法让HashMap线程安全
HashMap,ConcurrentHashMap与LinkedHashMap的区别
ConcurrentHashMap应用场景
1:ConcurrentHashMap的应用场景是高并发,但是并不能保证线程安全,而同步的HashMap和HashMap的是锁住整个容器,而加锁之后ConcurrentHashMap不需要锁住整个容器,只需要锁住对应的Segment就好了,所以可以保证高并发同步访问,提升了效率。
2:可以多线程写。
ConcurrentHashMap把HashMap分成若干个Segmenet
1.get时,不加锁,先定位到segment然后在找到头结点进行读取操作。而value是volatile变量,所以可以保证在竞争条件时保证读取最新的值,如果读到的value是null,则可能正在修改,那么就调用ReadValueUnderLock函数,加锁保证读到的数据是正确的。
2.Put时会加锁,一律添加到hash链的头部。
3.Remove时也会加锁,由于next是final类型不可改变,所以必须把删除的节点之前的节点都复制一遍。
4.ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对Hash表的不同Segment进行的修改。
ConcurrentHashMap的应用场景是高并发,但是并不能保证线程安全,而同步的HashMap和HashTable的是锁住整个容器,而加锁之后ConcurrentHashMap不需要锁住整个容器,只需要锁住对应的segment就好了,所以可以保证高并发同步访问,提升了效率。
ConcurrentHashMap能够保证每一次调用都是原子操作,但是并不保证多次调用之间也是原子操作。
友情链接:Java集合—ConcurrentHashMap原理分析
Vector和ArrayList的区别
ExecutorService service = Executors….
ExecutorService service = new ThreadPoolExecutor()
ExecutorService service = new ScheduledThreadPoolExecutor();
ThreadPoolExecutor源码分析
线程池本身的状态:
等待任务队列和工作集:
线程池的主要状态锁:
线程池的存活时间和大小:
1.2 ThreadPoolExecutor 的内部工作原理
有了以上定义好的数据,下面来看看内部是如何实现的 。 Doug Lea 的整个思路总结起来就是 5 句话:
1. 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
2. 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
3. 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
4. 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
5. 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。
Executor包结构
CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。
使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。
Linux常用命令:cd,cp,mv,rm,ps(进程),tar,cat(查看内容),chmod,vim,find,ls
死锁的必要条件
1. 互斥 至少有一个资源处于非共享状态
2. 占有并等待
3. 非抢占
4. 循环等待
解决死锁,第一个是死锁预防,就是不让上面的四个条件同时成立。二是,合理分配资源。
三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。
进程间的通信方式
数据库事务是指作为单个逻辑工作单元执行的一系列操作。
友情链接:数据库事务的四大特性以及事务的隔离级别
Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,当程序调用save(),update(),saveOrUpdate()等方法 及调用查询接口list,filter,iterate时,如Session缓存中还不存在相应的对象,Hibernate会把该对象加入到一级缓存中,当Session关闭的时候缓存也会消失。
Hibernate的一级缓存是Session所内置的,不能被卸载,也不能进行任何配置一级缓存采用的是key-value的Map方式来实现的,在缓存实体对象时,对象的主关键字ID是Map的key,实体对象就是对应的值。
Hibernate二级缓存:把获得的所有数据对象根据ID放入到第二级缓存中。Hibernate二级缓存策略,是针对于ID查询的缓存策略,删除、更新、增加数据的时候,同时更新缓存。
java collection.sort()根据时间排序list
Java 并发工具包 java.util.concurrent 用户指南
进程和线程的区别:
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
在java中要想实现多线程,有三种手段,一种是继续Thread类,另外一种是实现Runable接口,还有就是实现Callable接口。
Switch能否用string做参数?
a.在 Java 7 之前, switch 只能支持byte,short,char,int 或者其对应的封装类以及 Enum 类型。在Java 7中,String 支持被加上了。
Object有哪些公用方法?
a.方法equals测试的是两个对象是否相等
b.方法clone进行对象拷贝
c.方法getClass返回和当前对象相关的Class对象
d.方法notify,notifyall,wait都是用来对给定对象进行线程同步的
Java的四种引用,强弱软虚,以及用到的场景
a.利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。
b.通过软可及对象重获方法实现Java对象的高速缓存:比如我们创建了一Employee的类,如果每次需要查询一个雇员的信息。哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这是需要消耗很多时间的。我们可以通过软引用和 HashMap 的结合,先是保存引用方面:以软引用的方式对一个Employee对象的实例进行引用并保存该引用到HashMap 上,key 为此雇员的 id,value为这个对象的软引用,另一方面是取出引用,缓存中是否有该Employee实例的软引用,如果有,从软引用中取得。如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,并保存对这个新建实例的软引用。
c.强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
d.软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
e.弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。
f.虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
Hashcode的作用,与 equal 有什么区别?
a.同样用于鉴定2个对象是否相等的,java集合中有 list 和 set 两类,其中 set不允许元素重复实现,那个这个不允许重复实现的方法,如果用 equal 去比较的话,如果存在1000个元素,你 new 一个新的元素出来,需要去调用1000次 equal 去逐个和他们比较是否是同一个对象,这样会大大降低效率。hashcode实际上是返回对象的存储地址,如果这个位置上没有元素,就把元素直接存储在上面,如果这个位置上已经存在元素,这个时候才去调用equal方法与新元素进行比较,相同的话就不存了,散列到其他地址上。
Override和Overload的含义以及区别
a.Overload顾名思义是重新加载,它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、返回值、类型不能相同;或者说可以改变参数、类型、返回值但是函数名字依然不变。
b.就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。
具体可前往C++中重载、重写(覆盖)的区别实例分析查看
抽象类和接口的区别
a.一个类只能继承单个类,但是可以实现多个接口
b.抽象类中可以有构造方法,接口中不能有构造方法
c.抽象类中的所有方法并不一定要是抽象的,你可以选择在抽象类中实现一些基本的方法。而接口要求所有的方法都必须是抽象的
d.抽象类中可以包含静态方法,接口中不可以
e.抽象类中可以有普通成员变量,接口中不可以
解析XML的几种方式的原理与特点:DOM、SAX、PULL
a.DOM:消耗内存:先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存。要是数据过大,手机不够牛逼,可能手机直接死机
b.SAX:解析效率高,占用内存少,基于事件驱动的:更加简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
c.PULL:与 SAX 类似,也是基于事件驱动,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。
wait()和sleep()的区别
sleep来自Thread类,和wait来自Object类
调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU
sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒
JAVA 中堆和栈的区别,说下java 的内存机制
a.基本数据类型比变量和对象的引用都是在栈分配的
b.堆内存用来存放由new创建的对象和数组
c.类变量(static修饰的变量),程序在一加载的时候就在堆中为类变量分配内存,堆中的内存地址存放在栈中
d.实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量,是根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”,实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存
e.局部变量: 由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放
JAVA多态的实现原理
a.抽象的来讲,多态的意思就是同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
b.实现的原理是动态绑定,程序调用的方法在运行期才动态绑定,追溯源码可以发现,JVM 通过参数的自动转型来找到合适的办法。
各种Java面经资源
更多内容欢迎关注我的个人公众号
QQ学习交流群(内有干货):
本篇文章会对面试中常遇到的Java技术点进行全面深入的总结,帮助我们在面试中更加得心应手,不参加面试的同学也能够借此机会梳理一下自己的知识体系,进行查漏补缺(阅读本文需要有一定的Java基础)。本文的问题列表来自于www.nowcoder.com/discuss/304…在此感谢原作者的无私分享:)
1. Java中的原始数据类型都有哪些,它们的大小及对应的封装类是什么?
2. 谈一谈”==“与”equals()"的区别。
《Think in Java》中说:“关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系”。
"=="判断的是两个对象的内存地址是否一样,适用于原始数据类型和枚举类型(它们的变量存储的是值本身,而引用类型变量存储的是引用);equals是Object类的方法,Object对它的实现是比较内存地址,我们可以重写这个方法来自定义“相等”这个概念。比如类库中的String、Date等类就对这个方法进行了重写。
综上,对于枚举类型和原始数据类型的相等性比较,应该使用"==";对于引用类型的相等性比较,应该使用equals方法。
3. Java中的四种引用及其应用场景是什么?
强引用: 通常我们使用new操作符创建一个对象时所返回的引用即为强引用
软引用: 若一个对象只能通过软引用到达,那么这个对象在内存不足时会被回收,可用于图片缓存中,内存不足时系统会自动回收不再使用的Bitmap
弱引用: 若一个对象只能通过弱引用到达,那么它就会被回收(即使内存充足),同样可用于图片缓存中,这时候只要Bitmap不再使用就会被回收
虚引用: 虚引用是Java中最“弱”的引用,通过它甚至无法获取被引用的对象,它存在的唯一作用就是当它指向的对象回收时,它本身会被加入到引用队列中,这样我们可以知道它指向的对象何时被销毁。
4. object中定义了哪些方法?
clone(), equals(), hashCode(), toString(), notify(), notifyAll(), wait(), finalize(), getClass()
5. hashCode的作用是什么?
请参见散列表的基本原理与实现
6. ArrayList, LinkedList, Vector的区别是什么?
ArrayList: 内部采用数组存储元素,支持高效随机访问,支持动态调整大小
LinkedList: 内部采用链表来存储元素,支持快速插入/删除元素,但不支持高效地随机访问
Vector: 可以看作线程安全版的ArrayList
7. String, StringBuilder, StringBuffer的区别是什么?
String: 不可变的字符序列,若要向其中添加新字符需要创建一个新的String对象
StringBuilder: 可变字符序列,支持向其中添加新字符(无需创建新对象)
StringBuffer: 可以看作线程安全版的StringBuilder
8. Map, Set, List, Queue、Stack的特点及用法。
Map: Java中存储键值对的数据类型都实现了这个接口,表示“映射表”。支持的两个核心操作是get(Object key)以及put(K key, V value),分别用来获取键对应的值以及向映射表中插入键值对。
Set: 实现了这个接口的集合类型中不允许存在重复的元素,代表数学意义上的“集合”。它所支持的核心操作有add(E e), remove(Object o), contains(Object o),分别用于添加元素,删除元素以及判断给定元素是否存在于集中。
List: Java中集合框架中的列表类型都实现了这个接口,表示一种有序序列。支持get(int index), add(E e)等操作。
Queue: Java集合框架中的队列接口,代表了“先进先出”队列。支持add(E element),remove()等操作。
Stack: Java集合框架中表示堆栈的数据类型,堆栈是一种“后进先出”的数据结构。支持push(E item), pop()等操作。
更详细的说明请参考官方文档,对相关数据结构不太熟悉的同学可以参考《算法导论》或其他相关书籍。
9. HashMap和HashTable的区别
HashTable是线程安全的,而HashMap不是
HashMap中允许存在null键和null值,而HashTable中不允许
10. HashMap的实现原理
简单的说,HashMap的底层实现是“基于拉链法的散列表”。详细分析请参考深入解析HashMap、HashTable
11. ConcurrentHashMap的实现原理
ConcurrentHashMap是支持并发读写的HashMap,它的特点是读取数据时无需加锁,写数据时可以保证加锁粒度尽可能的小。由于其内部采用“分段存储”,只需对要进行写操作的数据所在的“段”进行加锁。关于ConcurrentHashMap底层实现的详细分析请参考Java并发编程:并发容器之ConcurrentHashMap
12. TreeMap, LinkedHashMap, HashMap的区别是什么?
HashMap的底层实现是散列表,因此它内部存储的元素是无序的;
TreeMap的底层实现是红黑树,所以它内部的元素的有序的。排序的依据是自然序或者是创建TreeMap时所提供的比较器(Comparator)对象。
LinkedHashMap可以看作能够记住插入元素的顺序的HashMap。
13. Collection与Collections的区别是什么?
14. 对于“try-catch-finally”,若try语句块中包含“return”语句,finally语句块会执行吗?
会执行。只有两种情况finally块中的语句不会被执行:**
调用了System.exit()方法;
JVM“崩溃”了。
15. Java中的异常层次结构
Java中的异常层次结构如下图所示:
我们可以看到Throwable类是异常层级中的基类。Error类表示内部错误,这类错误使我们无法控制的;Exception表示异常,RuntimeException及其子类属于未检查异常,这类异常包括ArrayIndexOutOfBoundsException、NullPointerException等,我们应该通过条件判断等方式语句避免未检查异常的发生。IOException及其子类属于已检查异常,编译器会检查我们是否为所有可能抛出的已检查异常提供了异常处理器,若没有则会报错。对于未检查异常,我们无需捕获(当然Java也允许我们捕获,但我们应该做的事避免未检查异常的发生)。
16. Java面向对象的三个特征与含义
三大特征:封装、继承、多态。详细介绍请戳Java面向对象三大特性
17. Override, Overload的含义与区别
Override表示“重写”,是子类对父类中同一方法的重新定义
Overload表示“重载”,也就是定义一个与已定义方法名称相同但签名不同的新方法**
18. 接口与抽象类的区别
抽象类本质上是一个类,使用抽象类的代价要比接口大。
接口与抽象类的对比如下:
抽象类中可以包含属性,方法(包含抽象方法与有着具体实现的方法),常量;接口只能包含常量和方法声明。
抽象类中的方法和成员变量可以定义可见性(比如public、private等);而接口中的方法只能为public(缺省为public)。
一个子类只能有一个父类(具体类或抽象类);而一个接口可以继承一个多个接口,一个类也可以实现多个接口。
子类中实现父类中的抽象方法时,可见性可以大于等于父类中的;而接口实现类中的接口 方法的可见性只能与接口中相同(public)。
19. 静态内部类与非静态内部类的区别
静态内部类不会持有外围类的引用,而非静态内部类会隐式持有外围类的一个引用。
20. Java中多态的实现原理
所谓多态,指的就是父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。多态的实现的关键在于“动态绑定”。详细介绍请戳Java动态绑定的内部实现机制
21. 简述Java中创建新线程的两种方法
继承Thread类(假设子类为MyThread),并重写run()方法,然后new一个MyThread对象并对其调用start()即可启动新线程。
实现Runnable接口(假设实现类为MyRunnable),而后将MyRunnable对象作为参数传入Thread构造器,在得到的Thread对象上调用start()方法即可。
22. 简述Java中进行线程同步的方法
volatile: Java Memory Model保证了对同一个volatile变量的写happens before对它的读;
synchronized: 可以来对一个代码块或是对一个方法上锁,被“锁住”的地方称为临界区,进入临界区的线程会获取对象的monitor,这样其他尝试进入临界区的线程会因无法获取monitor而被阻塞。由于等待另一个线程释放monitor而被阻塞的线程无法被中断。
ReentrantLock: 尝试获取锁的线程可以被中断并可以设置超时参数。
23. 简述Java中具有哪几种粒度的锁
Java中可以对类、对象、方法或是代码块上锁。
24. 给出“生产者-消费者”问题的一种解决方案
使用阻塞队列:
- public class BlockingQueueTest {
- private int size = 20;
- private ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(size);
- public static void main(String[] args) {
- BlockingQueueTest test = new BlockingQueueTest();
- Producer producer = test.new Producer();
- Consumer consumer = test.new Consumer();
- producer.start(); consumer.start();
- }
-
- class Consumer extends Thread{
- @Override
- public void run() {
- while(true){
- try {
- //从阻塞队列中取出一个元素
- queue.take();
- System.out.println("队列剩余" + queue.size() + "个元素");
- } catch (InterruptedException e) {
- }
- }
- }
- }
-
- class Producer extends Thread{
- @Override
- public void run() {
- while (true) {
- try {
- //向阻塞队列中插入一个元素
- queue.put(1);
- System.out.println("队列剩余空间:" + (size - queue.size()));
- } catch (InterruptedException e) {
- }
- }
- }
- }
- }
25. ThreadLocal的设计理念与作用
ThreadLocal的作用是提供线程内的局部变量,在多线程环境下访问时能保证各个线程内的ThreadLocal变量各自独立。也就是说,每个线程的ThreadLocal变量是自己专用的,其他线程是访问不到的。ThreadLocal最常用于以下这个场景:多线程环境下存在对非线程安全对象的并发访问,而且该对象不需要在线程间共享,但是我们不想加锁,这时候可以使用ThreadLocal来使得每个线程都持有一个该对象的副本。
26. concurrent包的整体架构
27. ArrayBlockingQueue, CountDownLatch类的作用
CountDownLatch: 允许线程集等待直到计数器为0。适用场景: 当一个或多个线程需要等待指定数目的事件发生后再继续执行。
ArrayBlockingQueue: 一个基于数组实现的阻塞队列,它在构造时需要指定容量。当试图向满队列中添加元素或者从空队列中移除元素时,当前线程会被阻塞。通过阻塞队列,我们可以按以下模式来工作:工作者线程可以周期性的将中间结果放入阻塞队列中,其它线程可以取出中间结果并进行进一步操作。若工作者线程的执行比较慢(还没来得及向队列中插入元素),其他从队列中取元素的线程会等待它(试图从空队列中取元素从而阻塞);若工作者线程执行较快(试图向满队列中插入元素),则它会等待其它线程取出元素再继续执行。
28. wait(),sleep() 的区别
wait(): Object类中定义的实例方法。在指定对象上调用wait方法会让当前线程进入等待状态(前提是当前线程持有该对象的monitor),此时当前线程会释放相应对象的monitor,这样一来其它线程便有机会获取这个对象的monitor了。当其它线程获取了这个对象的monitor并进行了所需操作时,便可以调用notify方法唤醒之前进入等待状态的线程。
sleep(): Thread类中的静态方法,作用是让当前线程进入休眠状态,以便让其他线程有机会执行。进入休眠状态的线程不会释放它所持有的锁。
29. 线程池的用法与优势
优势: 实现对线程的复用,避免了反复创建及销毁线程的开销;使用线程池统一管理线程可以减少并发线程的数目,而线程数过多往往会在线程上下文切换上以及线程同步上浪费过多时间。
用法: 我们可以调用ThreadPoolExecutor的某个构造方法来自己创建一个线程池。但通常情况下我们可以使用Executors类提供给我们的静态工厂方法来更方便的创建一个线程池对象。创建了线程池对象后,我们就可以调用submit方法提交任务到线程池中去执行了;线程池使用完毕后我们要记得调用shutdown方法来关闭它。
30. for-each与常规for循环的效率对比
关于这个问题我们直接看《Effective Java》给我们做的解答:
for-each能够让代码更加清晰,并且减少了出错的机会。
下面的惯用代码适用于集合与数组类型:
for (Element e : elements) { doSomething(e); }使用for-each循环与常规的for循环相比,并不存在性能损失,即使对数组进行迭代也是如此。实际上,在有些场合下它还能带来微小的性能提升,因为它只计算一次数组索引的上限。
31. 简述Java IO与NIO的区别
Java IO是面向流的,这意味着我们需要每次从流中读取一个或多个字节,直到读取完所有字节;NIO是面向缓冲的,也就是说会把数据读取到一个缓冲区中,然后对缓冲区中的数据进行相应处理。
Java IO是阻塞IO,而NIO是非阻塞IO。
Java NIO中存在一个称为选择器(selector)的东西,它允许你把多个通道(channel)注册到一个选择器上,然后使用一个线程来监视这些通道:若这些通道里有某个准备好可以开始进行读或写操作了,则开始对相应的通道进行读写。而在等待某通道变为可读/写期间,请求对通道进行读写操作的线程可以去干别的事情。
32. 反射的作用与原理
反射的作用概括地说是运行时获取类的各种定义信息,比如定义了哪些属性与方法。原理是通过类的class对象来获取它的各种信息。
33. Java中的泛型机制
关于泛型机制的详细介绍请直接戳Java核心技术点之泛型
34. Java 7与Java 8的新特性
这里有两篇总结的非常好的:
Java 7的新特性
Java 8的新特性
35. 常见设计模式
所谓“设计模式”,不过是面向对象编程中一些常用的软件设计手法,并且经过实践的检验,这些设计手法在各自的场景下能解决一些需求,因此它们就成为了如今广为流传的”设计模式“。也就是说,正式因为在某些场景下产生了一些棘手的问题,才催生了相应的设计模式。明确了这一点,我们在学习某种设计模式时要充分理解它产生的背景以及它所解决的主要矛盾是什么。
常用的设计模式可以分为以下三大类:
创建型模式: 包括工厂模式(又可进一步分为简单工厂模式、工厂方法模式、抽象工厂模式)、建造者模式、单例模式。
结构型模式: 包括适配器模式、桥接模式、装饰模式、外观模式、享元模式、代理模式。
行为型模式: 包括命令模式、中介者模式、观察者模式、状态模式、策略模式。
关于每个模式具体的介绍请参考图说设计模式
36. JNI的基本用法
关于JNI,这里有篇好文:Android中的JNI
37. 动态代理的定义、应用场景及原理
请参见十分钟理解Java之动态代理
38. 注解的基本概念与使用
注解可以看作是“增强版的注释”,它可以向编译器、虚拟机说明一些事情。
注解是描述Java代码的代码,它能够被编译器解析,注解处理工具在运行时也能够解析注解。注解本身是“被动”的信息,只有主动解析它才有意义。
除了向编译器/虚拟机传递信息,我们也可以使用注解来生成一些“模板化”的代码。
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
抽象:就是把现实生活中的某一类东西提取出来,用程序代码表示,我们通常叫做类或者接口。抽象包括两个方面:一个是数据抽象,一个是过程抽象。数据抽象也就是对象的属性。过程抽象是对象的行为特征。
封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行封装隐藏。封装分为属性的封装和方法的封装。
继承:是对有着共同特性的多类事物,进行再抽象成一个类。这个类就是多类事物的父类。父类的意义在于抽取多类事物的共性。
多态:允许不同类的对象对同一消息做出响应。方法的重载、类的覆盖正体现了多态。
重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private则子类中就不是重写。
构造器不能被重写,不能用static修饰构造器,只能用public
private protected这三个权限修饰符,且不能有返回语句。
private只有在本类中才能访问;
public在任何地方都能访问;
protected在同包内的类及包外的子类能访问;
默认不写在同包内能访问。
6是否可以继承String类#
String类是final类故不可以继承,一切由final修饰过的都不能继承。
可变性
String类中使用字符数组保存字符串,private
final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[]
value,这两种对象都是可变的。
线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对String 类型进行改变的时候,都会生成一个新的String 对象,然后将指针指向新的String 对象。StringBuffer每次都会对
StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用
StirngBuilder 相比使用
StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
equals相等,hashcode必相等;hashcode相等,equals可能不相等。
语法层次
抽象类和接口分别给出了不同的语法定义。
设计层次
抽象层次不同,抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
跨域不同
抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a"
关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的,仅仅是实现了接口定义的契约而已,"like-a"的关系。
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率,由编译器来完成,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。
泛型,即“参数化类型”。
创建集合时就指定集合元素的类型,该集合只能保存其指定类型的元素,避免使用强制类型转换。
Java编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。泛型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
类型擦除的主要过程如下:
1).将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
2).移除所有的类型参数。
List和Set继承自Collection接口。
Set无序不允许元素重复。HashSet和TreeSet是两个主要的实现类。
List有序且允许元素重复。ArrayList、LinkedList和Vector是三个主要的实现类。
Map也属于集合系统,但和Collection接口没关系。Map是key对value的映射集合,其中key列就是一个集合。key不能重复,但是value可以重复。HashMap、TreeMap和Hashtable是三个主要的实现类。
SortedSet和SortedMap接口对元素按指定规则排序,SortedMap是对key列进行排序。
具体原理参考文章:
zhangshixi.iteye.com/blog/672697
www.admin10000.com/document/33…
具体原理参考文章:
www.cnblogs.com/skywang1234…
blog.csdn.net/chdjj/artic…
1).HashTable的方法前面都有synchronized来同步,是线程安全的;HashMap未经同步,是非线程安全的。
2).HashTable不允许null值(key和value都不可以) ;HashMap允许null值(key和value都可以)。
3).HashTable有一个contains(Object
value)功能和containsValue(Object
value)功能一样。
4).HashTable使用Enumeration进行遍历;HashMap使用Iterator进行遍历。
5).HashTable中hash数组默认大小是11,增加的方式是old*2+1;HashMap中hash数组的默认大小是16,而且一定是2的指数。
6).哈希值的使用不同,HashTable直接使用对象的hashCode; HashMap重新计算hash值,而且用与代替求模。
ArrayList和Vector都实现了List接口,都是通过数组实现的。
Vector是线程安全的,而ArrayList是非线程安全的。
List第一次创建的时候,会有一个初始大小,随着不断向List中增加元素,当List 认为容量不够的时候就会进行扩容。Vector缺省情况下自动增长原来一倍的数组长度,ArrayList增长原来的50%。
区别
ArrayList底层是用数组实现的,可以认为ArrayList是一个可改变大小的数组。随着越来越多的元素被添加到ArrayList中,其规模是动态增加的。
LinkedList底层是通过双向链表实现的, LinkedList和ArrayList相比,增删的速度较快。但是查询和修改值的速度较慢。同时,LinkedList还实现了Queue接口,所以他还提供了offer(),
peek(), poll()等方法。
使用场景
LinkedList更适合从中间插入或者删除(链表的特性)。
ArrayList更适合检索和在末尾插入或删除(数组的特性)。
java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
具体原理参考文章:
www.cnblogs.com/ITtangtang/…
ifeve.com/concurrenth…
Error类和Exception类的父类都是throwable类,他们的区别是:
Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception和Checked Exception,各列举几个#
Unchecked Exception:
a. 指的是程序的瑕疵或逻辑错误,并且在运行时无法恢复。
b. 包括Error与RuntimeException及其子类,如:OutOfMemoryError,
UndeclaredThrowableException, IllegalArgumentException,
IllegalMonitorStateException, NullPointerException, IllegalStateException,
IndexOutOfBoundsException等。
c. 语法上不需要声明抛出异常。
Checked Exception:
a. 代表程序不能直接控制的无效外界情况(如用户输入,数据库问题,网络异常,文件丢失等)
b. 除了Error和RuntimeException及其子类之外,如:ClassNotFoundException,
NamingException, ServletException, SQLException, IOException等。
c. 需要try catch处理或throws声明抛出异常。
JDK动态代理:代理类和目标类实现了共同的接口,用到InvocationHandler接口。
CGLIB动态代理:代理类是目标类的子类,用到MethodInterceptor接口。
继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。
参考文章:
www.cnblogs.com/greta/p/562…
线程安全就是多线程访问同一代码,不会产生不确定的结果。
对非安全的代码进行加锁控制;
使用线程安全的类;
多线程并发情况下,线程共享的变量改为方法级的局部变量。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1). 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2). 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3). 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4). 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。Lock的锁定是通过代码实现的,而synchronized是在JVM层面上实现的,synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。Lock锁的范围有局限性,块范围,而synchronized可以锁住块、对象、类。
void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程。
void wait() 导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法。
void wait(long timeout) 导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量。
void wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
sleep()方法是Thread类中方法,而wait()方法是Object类中的方法。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态,在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备。
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
产生死锁的原因:
一.因为系统资源不足。
二.进程运行推进的顺序不合适。
三.资源分配不当。
产生死锁的四个必要条件:
一.互斥条件:所谓互斥就是进程在某一时间内独占资源。
二.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
三.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
四.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
打破产生死锁的四个必要条件中的一个或几个,保证系统不会进入死锁状态。
一.打破互斥条件。即允许进程同时访问某些资源。但是,有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。所以,这种办法并无实用价值。
二.打破不可抢占条件。即允许进程强行从占有者那里夺取某些资源。就是说,当一个进程已占有了某些资源,它又申请新的资源,但不能立即被满足时,它必须释放所占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难,会降低系统性能。
三.打破占有且申请条件。可以实行资源预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足,则不分配任何资源,此进程暂不运行。只有当系统能够满足当前进程的全部资源需求时,才一次性地将所申请的资源全部分配给该进程。由于运行的进程已占有了它所需的全部资源,所以不会发生占有资源又申请资源的现象,因此不会发生死锁。
四.打破循环等待条件,实行资源有序分配策略。采用这种策略,即把资源事先分类编号,按号分配,使进程在申请,占用资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占用了小号资源,才能申请大号资源,就不会产生环路,从而预防了死锁。
守护线程是为其他线程的运行提供服务的线程。
setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为守护模式,false为用户模式。
参考文章:
www.importnew.com/19011.html
www.cnblogs.com/dolphin0520…
这个内容有点多,参考文章:
并发包诸类概览:www.raychase.net/1912
线程池:www.cnblogs.com/dolphin0520…
锁: www.cnblogs.com/dolphin0520…
集合:www.cnblogs.com/huangfox/ar…
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。
Java语言中的volatile变量可以被看作是一种“程度较轻的
synchronized”;与
synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是synchronized的一部分。锁提供了两种主要特性:互斥(mutual
exclusion)和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的,如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。Volatile变量具有synchronized的可见性特性,但是不具备原子特性。这就是说线程能够自动发现volatile
变量的最新值。
要使volatile变量提供理想的线程安全,必须同时满足下面两个条件:对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的不变式中。
第一个条件的限制使volatile变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而volatile不能提供必须的原子特性。实现正确的操作需要使x 的值在操作期间保持不变,而volatile
变量无法实现这点。
每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。
read and load 从主存复制变量到当前工作内存
use and assign 执行代码,改变共享变量值
store and write 用工作内存数据刷新主存相关内容
其中use and
assign 可以多次出现,但是这一些操作并不是原子性,也就是在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样。
BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理.AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
一.IO是面向流的,NIO是面向缓冲区的。
二.IO的各种流是阻塞的,NIO是非阻塞模式。
三.Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
一.把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
二.在网络上传送对象的字节序列。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
Protobuf, Thrift, Hessian, Kryo
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,出现out of
memory。
内存泄漏是指分配出去的内存不再使用,但是无法回收。
这部分内容很重要,详细阅读《深入理解Java虚拟机》,也可以详细阅读这篇文章hllvm.group.iteye.com/group/wiki/…
一. 可通过命令定期抓取heap dump或者启动参数OOM时自动抓取heap dump文件。
二. 通过对比多个heap dump,以及heap dump的内容,分析代码找出内存占用最多的地方。
三. 分析占用的内存对象,是否是因为错误导致的内存未及时释放,或者数据过多导致的内存溢出。
一. Memory
Analyzer-是一款开源的JAVA内存分析软件,查找内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于Eclipse
RCP(Rich Client Platform),可以下载RCP的独立版本或者Eclipse的插件。
二. JProbe-分析Java的内存泄漏。
三.JProfiler-一个全功能的Java剖析工具,专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中,GUI可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问题。
四. JRockit-用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最高的性能。
五. YourKit-.NET & Java Profiling业界领先的Java和.NET程序性能分析工具。
六.AutomatedQA -AutomatedQA的获奖产品performance profiling和memory debugging工具集的下一代替换产品,支持Microsoft,Borland, Intel, Compaq 和 GNU编译器。可以为.NET和Windows程序生成全面细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net 1.0,1.1,2.0,3.0和Windows 32/64位应用程序。
七.Compuware DevPartner Java Edition-包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块
阅读这篇文章:www.cnblogs.com/hnrainll/ar…
阅读文章:
www.ibm.com/developerwo…
blog.csdn.net/zhoudaxia/a…
一.DOM(JAXP
Crimson解析器)
二.SAX
三.JDOM
四.DOM4J
区别:
一.DOM4J性能最好,连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.
二.JDOM和DOM在性能测试时表现不佳,在测试10M
文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM。虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C
推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
三.SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。
一.PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程
二.使用
Statement 对象。在对数据库只执行一次性存取的时侯,用
Statement 对象进行处理。PreparedStatement
对象的开销比Statement大,对于一次性操作并不会带来额外的好处。
三.statement每次执行sql语句,相关数据库都要执行sql语句的编译,preparedstatement是预编译得,
preparedstatement支持批处理
四.
代码片段1:
String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE
COF_NAME LIKE ′Colombian′";
stmt.executeUpdate(updateString);
代码片段2:
PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET
SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
片断2和片断1的区别在于,后者使用了PreparedStatement对象,而前者是普通的Statement对象。PreparedStatement对象不仅包含了SQL语句,而且大多数情况下这个语句已经被预编译过,因而当其执行时,只需DBMS运行SQL语句,而不必先编译。当你需要执行Statement对象多次的时候,PreparedStatement对象将会大大降低运行时间,当然也加快了访问数据库的速度。
这种转换也给你带来很大的便利,不必重复SQL语句的句法,而只需更改其中变量的值,便可重新执行SQL语句。选择PreparedStatement对象与否,在于相同句法的SQL语句是否执行了多次,而且两次之间的差别仅仅是变量的不同。如果仅仅执行了一次的话,它应该和普通的对象毫无差异,体现不出它预编译的优越性。
五.执行许多SQL语句的JDBC程序产生大量的Statement和PreparedStatement对象。通常认为PreparedStatement对象比Statement对象更有效,特别是如果带有不同参数的同一SQL语句被多次执行的时候。PreparedStatement对象允许数据库预编译SQL语句,这样在随后的运行中可以节省时间并增加代码的可读性。
然而,在Oracle环境中,开发人员实际上有更大的灵活性。当使用Statement或PreparedStatement对象时,Oracle数据库会缓存SQL语句以便以后使用。在一些情况下,由于驱动器自身需要额外的处理和在Java应用程序和Oracle服务器间增加的网络活动,执行PreparedStatement对象实际上会花更长的时间。
然而,除了缓冲的问题之外,至少还有一个更好的原因使我们在企业应用程序中更喜欢使用PreparedStatement对象,那就是安全性。传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。
当处理公共Web站点上的用户传来的数据的时候,安全性的问题就变得极为重要。传递给PreparedStatement的字符串参数会自动被驱动器忽略。最简单的情况下,这就意味着当你的程序试着将字符串“D'Angelo”插入到VARCHAR2中时,该语句将不会识别第一个“,”,从而导致悲惨的失败。几乎很少有必要创建你自己的字符串忽略代码。
在Web环境中,有恶意的用户会利用那些设计不完善的、不能正确处理字符串的应用程序。特别是在公共Web站点上,在没有首先通过PreparedStatement对象处理的情况下,所有的用户输入都不应该传递给SQL语句。此外,在用户有机会修改SQL语句的地方,如HTML的隐藏区域或一个查询字符串上,SQL语句都不应该被显示出来。
参考文章www.cnblogs.com/xuekyo/arch…
参考文章www.cnblogs.com/javawebsoa/…
参考文章blog.csdn.net/hanxuemin12…
(1)JSP经编译后就变成了“类servlet”。
(2)JSP由HTML代码和JSP标签构成,更擅长页面显示;Servlet更擅长流程控制。
(3)JSP中嵌入JAVA代码,而Servlet中嵌入HTML代码。
(1)动态include用jsp:include动作实现,如<jsp:include page="abc.jsp" flush="true" />,它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数。会先解析所要包含的页面,解析后和主页面合并一起显示,即先编译后包含。
(2)静态include用include伪码实现,不会检查所含文件的变化,适用于包含静态页面,如<%@
include file="qq.htm" %>,不会提前解析所要包含的页面,先把要显示的页面包含进来,然后统一编译,即先包含后编译。
参考文章www.cnblogs.com/liuling/p/2…
参考文章www.cnblogs.com/scwyh/artic…
参考文章:
blog.csdn.net/tch918/arti…
blog.csdn.net/chenleixing…
参考文章blog.csdn.net/firejuly/ar…
参考文章blog.csdn.net/windrui/art…
参考文章blog.csdn.net/shimiso/art…
参考文章www.360doc.com/content/15/…
参考文章blessht.iteye.com/blog/116213…
这个主题的参考文章没找到特别好的,blog.csdn.net/trigl/artic…
参考文章blog.csdn.net/lfsf802/art…
参考文章blog.sina.com.cn/s/blog_7ef0…
参考文章aijuans.iteye.com/blog/216014…
参考文章,www.infoq.com/cn/articles…
(1)客户-服务器:客户-服务器约束背后的原则是分离关注点。通过分离用户接口和数据存储这两个关注点,改善了用户接口跨多个平台的可移植性;同时通过简化服务器组件,改善了系统的可伸缩性。
(2)无状态:通信在本质上是无状态的,改善了可见性、可靠性、可伸缩性.
(3)缓存:改善了网络效率减少一系列交互的平均延迟时间,来提高效率、可伸缩性和用户可觉察的性能。
(4)统一接口:REST架构风格区别于其他基于网络的架构风格的核心特征是,它强调组件之间要有一个统一的接口。
Apache:HTTP服务器(WEB服务器),类似IIS,可以用于建立虚拟站点,编译处理静态页面,可以支持SSL技术,支持多个虚拟主机等功能。
Tomcat:Servlet容器,用于解析jsp,Servlet的Servlet容器,是高效,轻量级的容器。缺点是不支持EJB,只能用于java应用。
Jboss:应用服务器,运行EJB的J2EE应用服务器,遵循J2EE规范,能够提供更多平台的支持和更多集成功能,如数据库连接,JCA等,其对Servlet的支持是通过集成其他Servlet容器来实现的,如tomcat和jetty。
(1)性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
(2)内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
(3)Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
参考文章:
blog.csdn.net/zheng0518/a…
blog.csdn.net/nicewuranra…
常见的开源协议有GPL、LGPL、BSD、Apache Licence
vesion 2.0、MIT,详细内容参考文章:
blog.jobbole.com/44175/、http…
XML:
(1)应用广泛,可扩展性强,被广泛应用各种场合;
(2)读取、解析没有JSON快;
(3)可读性强,可描述复杂结构。
JSON:
(1)结构简单,都是键值对;
(2)读取、解析速度快,很多语言支持;
(3)传输数据量小,传输速率大大提高;
(4)描述复杂结构能力较弱。
参考文章:www.cnblogs.com/beijiguangy…
参考文章www.uml.org.cn/sjms/201211…
参考文章www.cnblogs.com/beijiguangy…
参考文章my.oschina.net/heweipo/blo…
推荐看书籍复习!可参考文章:
blog.163.com/zhoumhan_03…
blog.163.com/zhoumhan_03…
blog.csdn.net/andyelvis/a…
driftcloudy.iteye.com/blog/782873
推荐看书籍复习!可参考文章:
www.cnblogs.com/liuling/p/2…
blog.csdn.net/cyuyanenen/…
blog.csdn.net/whuslei/art…
参考www.cnblogs.com/liuling/p/2…
参考www.cnblogs.com/syxchina/ar…
推荐阅读数据复习!参考www.cnblogs.com/c-cloud/p/3…
参考文章:
www.jianshu.com/p/0e28d3160…
my.oschina.net/u/2391658/b…
推荐阅读数据复习!
参考www.jianshu.com/p/03cfc1a72…
推荐阅读书籍复习,参考文章:
www.cnblogs.com/zhaoyl/arch…
blog.csdn.net/rainharder/…
参考blog.sina.com.cn/s/blog_5e36…
参考blog.sina.com.cn/s/blog_5e36…
参考www.open-open.com/lib/view/op…
参考文章:
blog.csdn.net/kennyrose/a…
www.xuebuyuan.com/2216918.htm…
参考文章:
database.51cto.com/art/200904/…
www.cnblogs.com/rootq/archi…
参考文章:
www.cnblogs.com/mxmbk/artic…
www.cnblogs.com/simplefrog/…
www.open-open.com/lib/view/op…
blog.csdn.net/colin_liu20…
www.cnblogs.com/hongfei/arc…
参考文章:
blog.csdn.net/sprintfwate…
www.cnblogs.com/commanderzh…
blog.csdn.net/superjunjin…
参考:
blog.csdn.net/mingli19861…
www.mahaixiang.cn/internet/12…
参考文章:
yq.aliyun.com/articles/44…
www.cnblogs.com/klguang/p/4…
my.oschina.net/orgsky/blog…
参考文章:
www.cnblogs.com/hyddd/archi…
www.jellythink.com/archives/80…
参考www.jellythink.com/archives/80…
阅读www.jianshu.com/p/f7d1010fa…
参考www.cnblogs.com/shiyangxt/a…
参考www.cnblogs.com/wxgblogs/p/…
参考www.cnblogs.com/bizhu/archi…
参考文章:
blog.csdn.net/huwei2003/a…
www.leiphone.com/news/201509
ava 面试随着时间的改变而改变。在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来越高级,面试官问的问题也更深入。 在我初入职场的时候,类似于 Vector 与 Array 的区别、HashMap 与 Hashtable 的区别是最流行的问题,只需要记住它们,就能在面试中获得更好的机会,但这种情形已经不复存在。如今,你将会被问到许多 Java 程序员都没有看过的领域,如 NIO,设计模式,成熟的单元测试,或者那些很难掌握的知识,如并发、算法、数据结构及编码。
由于我喜欢研究面试题,因此我已经收集了许多的面试问题,包括许多许多不同的主题。我已经为这众多的问题准备一段时间了,现在我将它们分享给你们。这里面不但包含经典的面试问题,如线程、集合、equals 和 hashcode、socket,而且还包含了 NIO、数组、字符串、Java 8 等主题。
该列表包含了入门级 Java 程序员和多年经验的高级开发者的问题。无论你是 1、2、3、4、5、6、7、8、9 还是 10 年经验的开发者,你都能在其中找到一些有趣的问题。这里包含了一些超级容易回答的问题,同时包含经验丰富的 Java 程序员也会棘手的问题。
当然你们也是非常幸运的,当今有许多好的书来帮助你准备 Java 面试,其中有一本我觉得特别有用和有趣的是 Markham 的 Java 程序面试揭秘(Java Programming Interview Exposed)。 这本书会告诉你一些 Java 和 JEE 面试中最重要的主题,即使你不是准备 Java 面试,也值得一读。
该问题列表特别长,我们有各个地方的问题,所以,答案必须要短小、简洁、干脆,不拖泥带水。因此,除了这一个段落,你只会听到问题与答案,再无其他内容,没有反馈,也没有评价。为此,我已经写好了一些博文,在这些文章中你可以找到我对某些问题的观点,如我为什么喜欢这个问题,这个问题的挑战是什么?期望从面试者那获取到什么样的答案?
这个列表有一点不同,我鼓励你采用类似的方式去分享问题和答案,这样容易温习。我希望这个列表对面试官和候选人都有很好的用处,面试官可以对这些问题上做一些改变以获取新奇和令人惊奇的元素,这对一次好的面试来说非常重要。而候选者,可以扩展和测试 Java 程序语言和平台关键领域的知识。2015 年,会更多的关注并发概念,JVM 内部,32 位 JVM 和 64 JVM的区别,单元测试及整洁的代码。我确信,如果你读过这个庞大的 Java 面试问题列表,无论是电话面试还是面对面的面试,你都能有很好的表现。
除了你看到的惊人的问题数量,我也尽量保证质量。我不止一次分享各个重要主题中的问题,也确保包含所谓的高级话题,这些话题很多程序员不喜欢准备或者直接放弃,因为他们的工作不会涉及到这些。Java NIO 和 JVM 底层就是最好的例子。你也可以将设计模式划分到这一类中,但是越来越多有经验的程序员了解 GOF 设计模式并应用这些模式。我也尽量在这个列表中包含 2015 年最新的面试问题,这些问题可能是来年关注的核心。为了给你一个大致的了解,下面列出这份 Java 面试问题列表包含的主题:
多线程,并发及线程基础
数据类型转换的基本原则
垃圾回收(GC)
Java 集合框架
数组
字符串
GOF 设计模式
SOLID (单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)设计原则
抽象类与接口
Java 基础,如 equals 和 hashcode
泛型与枚举
Java IO 与 NIO
常用网络协议
Java 中的数据结构和算法
正则表达式
JVM 底层
Java 最佳实践
JDBC
Date, Time 与 Calendar
Java 处理 XML
JUnit
编程
现在是时候给你展示我近 5 年从各种面试中收集来的 120 个问题了。我确定你在自己的面试中见过很多这些问题,很多问题你也能正确回答。
1)Java 中能创建 volatile 数组吗?
能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。我的意思是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。
2)volatile 能使得一个非原子操作变成原子操作吗?
一个典型的例子是在类中有一个 long 类型的成员变量。如果你知道该成员变量会被多个线程访问,如计数器、价格等,你最好是将其设置为 volatile。为什么?因为 Java 中读取 long 类型变量不是原子的,需要分成两步,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。
3)volatile 修饰符的有过什么实践?
一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
4)volatile 类型变量提供什么保证?(答案)
volatile 变量提供顺序和可见性保证,例如,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。某些情况下,volatile 还能提供原子性,如读 64 位数据类型,像 long 和 double 都不是原子的,但 volatile 类型的 double 和 long 就是原子的。
5) 10 个线程和 2 个线程的同步代码,哪个更容易写?
从写代码的角度来说,两者的复杂度是相同的,因为同步代码与线程数量是相互独立的。但是同步策略的选择依赖于线程的数量,因为越多的线程意味着更大的竞争,所以你需要利用同步技术,如锁分离,这要求更复杂的代码和专业知识。
6)你是如何调用 wait()方法的?使用 if 块还是循环?为什么?(答案)
wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。下面是一段标准的使用 wait 和 notify 方法的代码:
// The standard idiom for using the wait method synchronized (obj) { while (condition does not hold) obj.wait(); // (Releases lock, and reacquires on wakeup) ... // Perform action appropriate to condition }
参见 Effective Java 第 69 条,获取更多关于为什么应该在循环中来调用 wait 方法的内容。
7)什么是多线程环境下的伪共享(false sharing)?
伪共享是多线程系统(每个处理器有自己的局部缓存)中一个众所周知的性能问题。伪共享发生在不同处理器的上的线程对变量的修改依赖于相同的缓存行,如下图所示:
伪共享问题很难被发现,因为线程可能访问完全不同的全局变量,内存中却碰巧在很相近的位置上。如其他诸多的并发问题,避免伪共享的最基本方式是仔细审查代码,根据缓存行来调整你的数据结构。
8)什么是 Busy spin?我们为什么要使用它?
Busy spin 是一种在不释放 CPU 的基础上等待事件的技术。它经常用于避免丢失 CPU 缓存中的数据(如果线程先暂停,之后在其他CPU上运行就会丢失)。所以,如果你的工作要求低延迟,并且你的线程目前没有任何顺序,这样你就可以通过循环检测队列中的新消息来代替调用 sleep() 或 wait() 方法。它唯一的好处就是你只需等待很短的时间,如几微秒或几纳秒。LMAX 分布式框架是一个高性能线程间通信的库,该库有一个 BusySpinWaitStrategy 类就是基于这个概念实现的,使用 busy spin 循环 EventProcessors 等待屏障。
9)Java 中怎么获取一份线程 dump 文件?
在 Linux 下,你可以通过命令 kill -3 PID (Java 进程的进程 ID)来获取 Java 应用的 dump 文件。在 Windows 下,你可以按下 Ctrl + Break 来获取。这样 JVM 就会将线程的 dump 文件打印到标准输出或错误文件中,它可能打印在控制台或者日志文件中,具体位置依赖应用的配置。如果你使用Tomcat。
10)Swing 是线程安全的?(答案)
不是,Swing 不是线程安全的。你不能通过任何线程来更新 Swing 组件,如 JTable、JList 或 JPanel,事实上,它们只能通过 GUI 或 AWT 线程来更新。这就是为什么 Swing 提供 invokeAndWait() 和 invokeLater() 方法来获取其他线程的 GUI 更新请求。这些方法将更新请求放入 AWT 的线程队列中,可以一直等待,也可以通过异步更新直接返回结果。你也可以在参考答案中查看和学习到更详细的内容。
11)什么是线程局部变量?(答案)
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
12)用 wait-notify 写一段代码来解决生产者-消费者问题?(答案)
请参考答案中的示例代码。只要记住在同步块中调用 wait() 和 notify()方法,如果阻塞,通过循环来测试等待条件。
13) 用 Java 写一个线程安全的单例模式(Singleton)?(答案)
请参考答案中的示例代码,这里面一步一步教你创建一个线程安全的 Java 单例类。当我们说线程安全时,意思是即使初始化是在多线程环境中,仍然能保证单个实例。Java 中,使用枚举作为单例类是最简单的方式来创建线程安全单例模式的方式。
14)Java 中 sleep 方法和 wait 方法的区别?(答案)
虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁。
15)什么是不可变对象(immutable object)?Java 中怎么创建一个不可变对象?(答案)
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。详情参见答案,一步一步指导你在 Java 中创建一个不可变的类。
16)我们能创建一个包含可变对象的不可变对象吗?
是的,我们是可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。最常见的例子就是对象中包含一个日期对象的引用。
17)Java 中应该使用什么数据类型来代表价格?(答案)
如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义精度的 double 类型。
18)怎么将 byte 转换为 String?(答案)
可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。
19)Java 中怎样将 bytes 转换为 long 类型?
这个问题你来回答 :-)
20)我们能将 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围,将会出现什么现象?
是的,我们可以做强制转换,但是 Java 中 int 是 32 位的,而 byte 是 8 位的,所以,如果强制转化是,int 类型的高 24 位将会被丢弃,byte 类型的范围是从 -128 到 128。
21)存在两个类,B 继承 A,C 继承 B,我们能将 B 转换为 C 么?如 C = (C) B;(answer答案)
22)哪个类包含 clone 方法?是 Cloneable 还是 Object?(答案)
java.lang.Cloneable 是一个标示性接口,不包含任何方法,clone 方法在 object 类中定义。并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ 或 其他本地语言实现的。
23)Java 中 ++ 操作符是线程安全的吗?(答案)
23)不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。
24)a = a + b 与 a += b 的区别(答案)
+= 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两这个整型相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作。如果加法操作的结果比 a 的最大值要大,则 a+b 会出现编译错误,但是 a += b 没问题,如下:
byte a = 127;
byte b = 127;
b = a + b; // error : cannot convert from int to byte
b += a; // ok
(译者注:这个地方应该表述的有误,其实无论 a+b 的值为多少,编译器都会报错,因为 a+b 操作会将 a、b 提升为 int 类型,所以将 int 类型赋值给 byte 就会编译出错)
25)我能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗?(答案)
不行,你不能在没有强制类型转换的前提下将一个 double 值赋值给 long 类型的变量,因为 double 类型的范围比 long 类型更广,所以必须要进行强制转换。
26)3*0.1 == 0.3 将会返回什么?true 还是 false?(答案)
false,因为有些浮点数不能完全精确的表示出来。
27)int 和 Integer 哪个会占用更多的内存?(答案)
Integer 对象会占用更多的内存。Integer 是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。
28)为什么 Java 中的 String 是不可变的(Immutable)?(answer答案)
Java 中的 String 不可变是因为 Java 的设计者认为字符串使用非常频繁,将字符串设置为不可变可以允许多个客户端之间共享相同的字符串。更详细的内容参见答案。
29)我们能在 Switch 中使用 String 吗?(answer答案)
从 Java 7 开始,我们可以在 switch case 中使用字符串,但这仅仅是一个语法糖。内部实现在 switch 中使用字符串的 hash code。
30)Java 中的构造器链是什么?(answer答案)
当你从一个构造器中调用另一个构造器,就是Java 中的构造器链。这种情况只在重载了类的构造器的时候才会出现。
31)64 位 JVM 中,int 的长度是多数?
Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。
32)Serial 与 Parallel GC之间的不同之处?(答案)
Serial 与 Parallel 在GC执行的时候都会引起 stop-the-world。它们之间主要不同 serial 收集器是默认的复制收集器,执行 GC 的时候只有一个线程,而 parallel 收集器使用多个 GC 线程来执行。
33)32 位和 64 位的 JVM,int 类型变量的长度是多数?(答案)
32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4 个字节。
34)Java 中 WeakReference 与 SoftReference的区别?(答案)
虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。
35)WeakHashMap 是怎么工作的?(答案)
WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。
36)JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用?(答案)
当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小,通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。
37)怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位?(答案)
你可以检查某些系统属性如 sun.arch.data.model 或 os.arch 来获取该信息。
38)32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?(答案)
理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。
39)JRE、JDK、JVM 及 JIT 之间有什么不同?(答案)
JRE 代表 Java 运行时(Java run-time),是运行 Java 引用所必须的。JDK 代表 Java 开发工具(Java development kit),是 Java 程序的开发工具,如 Java 编译器,它也包含 JRE。JVM 代表 Java 虚拟机(Java virtual machine),它的责任是运行 Java 应用。JIT 代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。
3 年工作经验的 Java 面试题
40)解释 Java 堆空间及 GC?(答案)
当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的一个进程,回收无效对象的内存用于将来的分配。
41)你能保证 GC 执行吗?(答案)
不能,虽然你可以调用 System.gc() 或者 Runtime.gc(),但是没有办法保证 GC 的执行。
42)怎么获取 Java 程序使用的内存?堆使用的百分比?
可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。
43)Java 中堆和栈有什么区别?(答案)
JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
44)“a==b”和”a.equals(b)”有什么区别?(答案)
如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。
45)a.hashCode() 有什么用?与 a.equals(b) 有什么关系?(答案)
hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。
46)final、finalize 和 finally 的不同之处?(答案)
final 是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变。finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证。finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常。
47)Java 中的编译期常量是什么?使用它又什么风险?
公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里的 public 可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了避免这种情况,当你在更新依赖 JAR 文件时,确保重新编译你的程序。
这部分也包含数据结构、算法及数组的面试问题
48) List、Set、Map 和 Queue 之间的区别(答案)
List 是一个有序集合,允许元素重复。它的某些实现可以提供基于下标值的常量访问时间,但是这不是 List 接口保证的。Set 是一个无序集合。
49)poll() 方法和 remove() 方法的区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
50)Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?(答案)
PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。
51)ArrayList 与 LinkedList 的不区别?(答案)
最明显的区别是 ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构书链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。更多细节的讨论参见答案。
52)用哪两种方式来实现集合的排序?(答案)
你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。
53)Java 中怎么打印数组?(answer答案)
你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println() 方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。
54)Java 中的 LinkedList 是单向链表还是双向链表?(答案)
是双向链表,你可以检查 JDK 的源码。在 Eclipse,你可以使用快捷键 Ctrl + T,直接在编辑器中打开该类。
55)Java 中的 TreeMap 是采用什么树实现的?(答案)
Java 中的 TreeMap 是使用红黑树实现的。
56) Hashtable 与 HashMap 有什么不同之处?(答案)
这两个类有许多不同的地方,下面列出了一部分:
a) Hashtable 是 JDK 1 遗留下来的类,而 HashMap 是后来增加的。
b)Hashtable 是同步的,比较慢,但 HashMap 没有同步策略,所以会更快。
c)Hashtable 不允许有个空的 key,但是 HashMap 允许出现一个 null key。
更多的不同之处参见答案。
57)Java 中的 HashSet,内部是如何工作的?(answer答案)
HashSet 的内部采用 HashMap来实现。由于 Map 需要 key 和 value,所以所有 key 的都有一个默认 value。类似于 HashMap,HashSet 不允许重复的 key,只允许有一个null key,意思就是 HashSet 中只允许存储一个 null 对象。
58)写一段代码在遍历 ArrayList 时移除一个元素?(答案)
该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。这有一段示例代码,是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。
59)我们能自己写一个容器类,然后使用 for-each 循环码?
可以,你可以写一个自己的容器类。如果你想使用 Java 中增强的循环来遍历,你只需要实现 Iterable 接口。如果你实现 Collection 接口,默认就具有该属性。
60)ArrayList 和 HashMap 的默认大小是多数?(答案)
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段:
// from ArrayList.java JDK 1.7 private static final int DEFAULT_CAPACITY = 10; //from HashMap.java JDK 7 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
61)有没有可能两个不相等的对象有有相同的 hashcode?
有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为什么在 hashmap 中会有冲突。相等 hashcode 值的规定只是说如果两个对象相等,必须有相同的hashcode 值,但是没有关于不相等对象的任何规定。
62)两个相同的对象会有不同的的 hash code 吗?
不能,根据 hash code 的规定,这是不可能的。
63)我们可以在 hashcode() 中使用随机数字吗?(答案)
不行,因为对象的 hashcode 值必须是相同的。参见答案获取更多关于 Java 中重写 hashCode() 方法的知识。
64)Java 中,Comparator 与 Comparable 有什么不同?(答案)
Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。
65)为什么在重写 equals 方法的时候需要重写 hashCode 方法?(答案)
因为有强制的规范指定需要同时重写 hashcode 与 equal 是方法,许多容器类,如 HashMap、HashSet 都依赖于 hashcode 与 equals 的规定。
IO 是 Java 面试中一个非常重要的点。你应该很好掌握 Java IO,NIO,NIO2 以及与操作系统,磁盘 IO 相关的基础知识。下面是 Java IO 中经常问的问题。
66)在我 Java 程序中,我有三个 socket,我需要多少个线程来处理?
67)Java 中怎么创建 ByteBuffer?
68)Java 中,怎么读写 ByteBuffer ?
69)Java 采用的是大端还是小端?
70)ByteBuffer 中的字节序是什么?
71)Java 中,直接缓冲区与非直接缓冲器有什么区别?(答案)
72)Java 中的内存映射缓存区是什么?(answer答案)
73)socket 选项 TCP NO DELAY 是指什么?
74)TCP 协议与 UDP 协议有什么区别?(answer答案)
75)Java 中,ByteBuffer 与 StringBuffer有什么区别?(答案)
包含 Java 中各个部分的最佳实践,如集合,字符串,IO,多线程,错误和异常处理,设计模式等等。
76)Java 中,编写多线程程序的时候你会遵循哪些最佳实践?(答案)
这是我在写Java 并发程序的时候遵循的一些最佳实践:
a)给线程命名,这样可以帮助调试。
b)最小化同步的范围,而不是将整个方法同步,只对关键部分做同步。
c)如果可以,更偏向于使用 volatile 而不是 synchronized。
d)使用更高层次的并发工具,而不是使用 wait() 和 notify() 来实现线程间通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。
e)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。
77)说出几点 Java 中使用 Collections 的最佳实践(答案)
这是我在使用 Java 中 Collectionc 类的一些最佳实践:
a)使用正确的集合类,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。
b)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。
c)使用接口代表和访问集合,如使用List存储 ArrayList,使用 Map 存储 HashMap 等等。
d)使用迭代器来循环集合。
e)使用集合的时候使用泛型。
78)说出至少 5 点在 Java 中使用线程的最佳实践。(答案)
这个问题与之前的问题类似,你可以使用上面的答案。对线程来说,你应该:
a)对线程命名
b)将线程和任务分离,使用线程池执行器来执行 Runnable 或 Callable。
c)使用线程池
79)说出 5 条 IO 的最佳实践(答案)
IO 对 Java 应用的性能非常重要。理想情况下,你不应该在你应用的关键路径上避免 IO 操作。下面是一些你应该遵循的 Java IO 最佳实践:
a)使用有缓冲区的 IO 类,而不要单独读取字节或字符。
b)使用 NIO 和 NIO2
c)在 finally 块中关闭流,或者使用 try-with-resource 语句。
d)使用内存映射文件获取更快的 IO。
80)列出 5 个应该遵循的 JDBC 最佳实践(答案)
有很多的最佳实践,你可以根据你的喜好来例举。下面是一些更通用的原则:
a)使用批量的操作来插入和更新数据
b)使用 PreparedStatement 来避免 SQL 异常,并提高性能。
c)使用数据库连接池
d)通过列名来获取结果集,不要使用列的下标来获取。
81)说出几条 Java 中方法重载的最佳实践?(答案)
下面有几条可以遵循的方法重载的最佳实践来避免造成自动装箱的混乱。
a)不要重载这样的方法:一个方法接收 int 参数,而另个方法接收 Integer 参数。
b)不要重载参数数量一致,而只是参数顺序不同的方法。
c)如果重载的方法参数个数多于 5 个,采用可变参数。
82)在多线程环境下,SimpleDateFormat 是线程安全的吗?(答案)
不是,非常不幸,DateFormat 的所有实现,包括 SimpleDateFormat 都不是线程安全的,因此你不应该在多线程序中使用,除非是在对外线程安全的环境中使用,如 将 SimpleDateFormat 限制在 ThreadLocal 中。如果你不这么做,在解析或者格式化日期的时候,可能会获取到一个不正确的结果。因此,从日期、时间处理的所有实践来说,我强力推荐 joda-time 库。
83)Java 中如何格式化一个日期?如格式化为 ddMMyyyy 的形式?(答案)
Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期。DateFormat 类允许你使用多种流行的格式来格式化日期。参见答案中的示例代码,代码中演示了将日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。
84)Java 中,怎么在格式化的日期中显示时区?(答案)
85)Java 中 java.util.Date 与 java.sql.Date 有什么区别?(答案)
86)Java 中,如何计算两个日期之间的差距?(程序)
87)Java 中,如何将字符串 YYYYMMDD 转换为日期?(答案)
89)如何测试静态方法?(答案)
可以使用 PowerMock 库来测试静态方法。
90)怎么利用 JUnit 来测试一个方法的异常?(答案)
91)你使用过哪个单元测试库来测试你的 Java 程序?(答案)
92)@Before 和 @BeforeClass 有什么区别?(答案)
93)怎么检查一个字符串只包含数字?(解决方案)
94)Java 中如何利用泛型写一个 LRU 缓存?(答案<)< p="">
95)写一段 Java 程序将 byte 转换为 long?(答案)
95)在不使用 StringBuffer 的前提下,怎么反转一个字符串?(解决方案)
97)Java 中,怎么获取一个文件中单词出现的最高频率?(解决方案)
98)如何检查出两个给定的字符串是反序的?(解决方案)
99)Java 中,怎么打印出一个字符串的所有排列?(解决方案)
100)Java 中,怎样才能打印出数组中的重复元素?(解决方案)
101)Java 中如何将字符串转换为整数?(解决方案)
102)在没有使用临时变量的情况如何交换两个整数变量的值?(解决方案)
这部分包含 Java 面试过程中关于 SOLID 的设计原则,OOP 基础,如类,对象,接口,继承,多态,封装,抽象以及更高级的一些概念,如组合、聚合及关联。也包含了 GOF 设计模式的问题。
103)接口是什么?为什么要使用接口而不是直接使用具体类?
接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以使用可随机访问的 ArrayList,也可以使用方便插入和删除的 LinkedList。接口中不允许写代码,以此来保证抽象,但是 Java 8 中你可以在接口声明静态的默认方法,这种方法是具体的。
104)Java 中,抽象类与接口之间有什么不同?(答案)
Java 中,抽象类和接口有很多不同之处,但是最重要的一个是 Java 中限制一个类只能继承一个类,但是可以实现多个接口。抽象类可以很好的定义一个家族类的默认行为,而接口能更好的定义类型,有助于后面实现多态机制。关于这个问题的讨论请查看答案。
105)除了单例模式,你在生产环境中还用过什么设计模式?
这需要根据你的经验来回答。一般情况下,你可以说依赖注入,工厂模式,装饰模式或者观察者模式,随意选择你使用过的一种即可。不过你要准备回答接下的基于你选择的模式的问题。
106)你能解释一下里氏替换原则吗?(答案)
107) 什么情况下会违反迪米特法则?为什么会有这个问题?(答案)
迪米特法则建议“只和朋友说话,不要陌生人说话”,以此来减少类之间的耦合。
108)适配器模式是什么?什么时候使用?
适配器模式提供对接口的转换。如果你的客户端使用某些接口,但是你有另外一些接口,你就可以写一个适配去来连接这些接口。
109)什么是“依赖注入”和“控制反转”?为什么有人使用?(答案)
110)抽象类是什么?它与接口有什么区别?你为什么要使用过抽象类?(答案)
111)构造器注入和 setter 依赖注入,那种方式更好?(答案)
每种方式都有它的缺点和优点。构造器注入保证所有的注入都被初始化,但是 setter 注入提供更好的灵活性来设置可选依赖。如果使用 XML 来描述依赖,Setter 注入的可读写会更强。经验法则是强制依赖使用构造器注入,可选依赖使用 setter 注入。
112)依赖注入和工程模式之间有什么不同?(答案)
虽然两种模式都是将对象的创建从应用的逻辑中分离,但是依赖注入比工程模式更清晰。通过依赖注入,你的类就是 POJO,它只知道依赖而不关心它们怎么获取。使用工厂模式,你的类需要通过工厂来获取依赖。因此,使用 DI 会比使用工厂模式更容易测试。关于这个话题的更详细讨论请参见答案。
113)适配器模式和装饰器模式有什么区别?(答案)
虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能。
114)适配器模式和代理模式之前有什么不同?(答案)
这个问题与前面的类似,适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式和代理模式都是封装真正执行动作的类,因此结构是一致的,但是适配器模式用于接口之间的转换,而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问。
115)什么是模板方法模式?(答案)
模板方法提供算法的框架,你可以自己去配置或定义步骤。例如,你可以将排序算法看做是一个模板。它定义了排序的步骤,但是具体的比较,可以使用 Comparable 或者其语言中类似东西,具体策略由你去配置。列出算法概要的方法就是众所周知的模板方法。
116)什么时候使用访问者模式?(答案)
访问者模式用于解决在类的继承层次上增加操作,但是不直接与之关联。这种模式采用双派发的形式来增加中间层。
117)什么时候使用组合模式?(答案)
组合模式使用树结构来展示部分与整体继承关系。它允许客户端采用统一的形式来对待单个对象和对象容器。当你想要展示对象这种部分与整体的继承关系时采用组合模式。
118)继承和组合之间有什么不同?(答案)
虽然两种都可以实现代码复用,但是组合比继承共灵活,因为组合允许你在运行时选择不同的实现。用组合实现的代码也比继承测试起来更加简单。
119)描述 Java 中的重载和重写?(答案)
重载和重写都允许你用相同的名称来实现不同的功能,但是重载是编译时活动,而重写是运行时活动。你可以在同一个类中重载方法,但是只能在子类中重写方法。重写必须要有继承。
120)Java 中,嵌套公共静态类与顶级类有什么不同?(答案)
类的内部可以有多个嵌套公共静态类,但是一个 Java 源文件只能有一个顶级公共类,并且顶级公共类的名称与源文件名称必须一致。
121) OOP 中的 组合、聚合和关联有什么区别?(答案)
如果两个对象彼此有关系,就说他们是彼此相关联的。组合和聚合是面向对象中的两种形式的关联。组合是一种比聚合更强力的关联。组合中,一个对象是另一个的拥有者,而聚合则是指一个对象使用另一个对象。如果对象 A 是由对象 B 组合的,则 A 不存在的话,B一定不存在,但是如果 A 对象聚合了一个对象 B,则即使 A 不存在了,B 也可以单独存在。
122)给我一个符合开闭原则的设计模式的例子?(答案)
开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增加一个新的功能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码。有好几个设计模式是基于开闭原则的,如策略模式,如果你需要一个新的策略,只需要实现接口,增加配置,不需要改变核心逻辑。一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式,遵循开闭原则的,你不需为新的对象修改 sort() 方法,你需要做的仅仅是实现你自己的 Comparator 接口。
123)抽象工厂模式和原型模式之间的区别?(答案)
124)什么时候使用享元模式?(答案)
享元模式通过共享对象来避免创建太多的对象。为了使用享元模式,你需要确保你的对象是不可变的,这样你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。
这部分包含 Java 中关于 XML 的面试题,JDBC 面试题,正则表达式面试题,Java 错误和异常及序列化面试题
125)嵌套静态类与顶级类有什么区别?(答案)
一个公共的顶级类的源文件名称与类名相同,而嵌套静态类没有这个要求。一个嵌套类位于顶级类内部,需要使用顶级类的名称来引用嵌套静态类,如 HashMap.Entry 是一个嵌套静态类,HashMap 是一个顶级类,Entry是一个嵌套静态类。
126)你能写出一个正则表达式来判断一个字符串是否是一个数字吗?(解决方案)
一个数字字符串,只能包含数字,如 0 到 9 以及 +、- 开头,通过这个信息,你可以下一个如下的正则表达式来判断给定的字符串是不是数字。
127)Java 中,受检查异常 和 不受检查异常的区别?(答案)
受检查异常编译器在编译期间检查。对于这种异常,方法强制处理或者通过 throws 子句声明。其中一种情况是 Exception 的子类但不是 RuntimeException 的子类。非受检查是 RuntimeException 的子类,在编译阶段不受编译器的检查。
128)Java 中,throw 和 throws 有什么区别?(答案)
throw 用于抛出 java.lang.Throwable 类的一个实例化对象,意思是说你可以通过关键字 throw 抛出一个 Error 或者 一个Exception,如:
throw new IllegalArgumentException(“size must be multiple of 2″)
而throws 的作用是作为方法声明和签名的一部分,方法被抛出相应的异常以便调用者能处理。Java 中,任何未处理的受检查异常强制在 throws 子句中声明。
129)Java 中,Serializable 与 Externalizable 的区别?(答案)
Serializable 接口是一个序列化 Java 类的接口,以便于它们可以在网络上传输或者可以将它们的状态保存在磁盘上,是 JVM 内嵌的默认序列化方式,成本高、脆弱而且不安全。Externalizable 允许你控制整个序列化过程,指定特定的二进制格式,增加安全机制。
130)Java 中,DOM 和 SAX 解析器有什么不同?(答案)
DOM 解析器将整个 XML 文档加载到内存来创建一棵 DOM 模型树,这样可以更快的查找节点和修改 XML 结构,而 SAX 解析器是一个基于事件的解析器,不会将整个 XML 文档加载到内存。由于这个原因,DOM 比 SAX 更快,也要求更多的内存,不适合于解析大 XML 文件。
131)说出 JDK 1.7 中的三个新特性?(答案)
虽然 JDK 1.7 不像 JDK 5 和 8 一样的大版本,但是,还是有很多新的特性,如 try-with-resource 语句,这样你在使用流或者资源的时候,就不需要手动关闭,Java 会自动关闭。Fork-Join 池某种程度上实现 Java 版的 Map-reduce。允许 Switch 中有 String 变量和文本。菱形操作符(<>)用于类型推断,不再需要在变量声明的右边申明泛型,因此可以写出可读写更强、更简洁的代码。另一个值得一提的特性是改善异常处理,如允许在同一个 catch 块中捕获多个异常。
132)说出 5 个 JDK 1.8 引入的新特性?(答案)
Java 8 在 Java 历史上是一个开创新的版本,下面 JDK 8 中 5 个主要的特性:
Lambda 表达式,允许像对象一样传递匿名函数
Stream API,充分利用现代多核 CPU,可以写出很简洁的代码
Date 与 Time API,最终,有一个稳定、简单的日期和时间库可供你使用
扩展方法,现在,接口中可以有静态、默认方法。
重复注解,现在你可以将相同的注解在同一类型上使用多次。
133)Java 中,Maven 和 ANT 有什么区别?(答案)
虽然两者都是构建工具,都用于创建 Java 应用,但是 Maven 做的事情更多,在基于“约定优于配置”的概念下,提供标准的Java 项目结构,同时能为应用自动管理依赖(应用中所依赖的 JAR 文件),Maven 与 ANT 工具更多的不同之处请参见答案。
这就是所有的面试题,如此之多,是不是?我可以保证,如果你能回答列表中的所有问题,你就可以很轻松的应付任何核心 Java 或者高级 Java 面试。虽然,这里没有涵盖 Servlet、JSP、JSF、JPA,JMS,EJB 及其它 Java EE 技术,也没有包含主流的框架如 Spring MVC,Struts 2.0,Hibernate,也没有包含 SOAP 和 RESTful web service,但是这份列表对做 Java 开发的、准备应聘 Java web 开发职位的人还是同样有用的,因为所有的 Java 面试,开始的问题都是 Java 基础和 JDK API 相关的。如果你认为我这里有任何应该在这份列表中而被我遗漏了的 Java 流行的问题,你可以自由的给我建议。我的目的是从最近的面试中创建一份最新的、最优的 Java 面试问题列表。
Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。
轻量:Spring 是轻量的,基本的版本大约2MB。
控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
容器:Spring 包含并管理应用中对象的生命周期和配置。
MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。
以下是Spring 框架的基本模块:
Core module
Bean module
Context module
Expression Language module
JDBC module
ORM module
OXM module
Java Messaging Service(JMS) module
Transaction module
Web module
Web-Servlet module
Web-Struts module
Web-Portlet module
这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。
Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。
最常用的BeanFactory 实现是XmlBeanFactory 类。
最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。
AOP模块用于发给我们的Spring应用做面向切面的开发, 很多支持由AOP联盟提供,这样就确保了Spring和其他AOP框架的共通性。这个模块将元数据编程引入Spring。
通过使用JDBC抽象和DAO模块,保证数据库代码的简洁,并能避免数据库资源错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一的异常访问层。它还利用Spring的AOP 模块给Spring应用中的对象提供事务管理服务。
Spring 通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS SQL Maps。Spring的事务管理同样支持以上所有ORM框架及JDBC。
Spring的WEB模块是构建在application context 模块基础之上,提供一个适合web应用的上下文。这个模块也包括支持多种面向web的任务,如透明地处理多个文件上传请求和程序级请求参数的绑定到你的业务对象。它也有对Jakarta Struts的支持。
Spring配置文件是个XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
IOC 或 依赖注入把应用的代码量降到最低。它使应用容易测试,单元测试不再需要单例和JNDI查找机制。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持加载服务时的饿汉式初始化和懒加载。
FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。
一个定义了一些功能的接口。
这实现包括属性,它的Setter , getter 方法和函数等。
Spring AOP。
Spring 的XML 配置文件。
使用以上功能的客户端程序。
依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
你两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。
Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中<bean/> 的形式定义。
Spring 框架定义的beans都是单件beans。在bean tag中有个属性”singleton”,如果它被赋为TRUE,bean 就是单件,否则就是一个 prototype bean。默认是TRUE,所以所有在Spring框架中的beans 缺省都是单件。
一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。
这里有三种重要的方法给Spring 容器提供配置元数据。
XML配置文件。
基于注解的配置。
基于java的配置。
当定义一个<bean> 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
Spring框架支持以下五种bean的作用域:
singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
缺省的Spring bean 的作用域是Singleton.
不,Spring框架中的单例bean不是线程安全的。
Spring容器 从XML 文件中读取bean的定义,并实例化bean。
Spring根据bean的定义填充所有的属性。
如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。
如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。
如果bean实现了 DisposableBean,它将调用destroy()方法。
有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。
The bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。
当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring 的 基于XML的 配置元数据中,可以在 <property/>或 <constructor-arg/> 元素内使用<bean/> 元素,内部bean通常是匿名的,它们的Scope一般是prototype。
Spring提供以下几种集合的配置元素:
<list>类型用于注入一列值,允许有相同的值。
<set> 类型用于注入一组值,不允许有相同的值。
<map> 类型用于注入一组键值对,键和值都可以为任意类型。
<props>类型用于注入一组键值对,键和值都只能为String类型。
装配,或bean 装配是指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
Spring 容器能够自动装配相互合作的bean,这意味着容器不需要<constructor-arg>和<property>配置,能通过Bean工厂自动处理bean之间的协作。
有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入。
no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。
byName:通过参数名 自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。
constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。
自动装配的局限性是:
重写: 你仍需用 <constructor-arg>和 <property> 配置来定义依赖,意味着总要重写自动装配。
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
可以。
基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。
以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。
相对于XML文件,注解型的配置依赖于通过字节码元数据装配组件,而非尖括号的声明。
开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。
注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 <context:annotation-config/>元素。
这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
当有多个相同类型的bean却只有一个需要自动装配时,将@Qualifier 注解和@Autowire 注解结合使用以消除这种混淆,指定需要装配的确切的bean。
使用SpringJDBC 框架,资源管理和错误处理的代价都会被减轻。所以开发者只需写statements 和 queries从数据存取数据,JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate (例子见这里here)
JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理。
Spring对数据访问对象(DAO)的支持旨在简化它和数据访问技术如JDBC,Hibernate or JDO 结合使用。这使我们可以方便切换持久层。编码时也不用担心会捕获每种技术特有的异常。
在Spring中有两种方式访问Hibernate:
控制反转 Hibernate Template和 Callback。
继承 HibernateDAOSupport提供一个AOP 拦截器。
Spring支持以下ORM:
Hibernate
iBatis
JPA (Java Persistence API)
TopLink
JDO (Java Data Objects)
OJB
用Spring的 SessionFactory 调用 LocalSessionFactory。集成过程分三步:
配置the Hibernate SessionFactory。
继承HibernateDaoSupport实现一个DAO。
在AOP支持的事务中装配。
Spring支持两种类型的事务管理:
编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
它为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如
它支持声明式事务管理。
它和Spring各种数据访问抽象层很好得集成。
大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。
面向切面的编程,或AOP, 是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。
AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。
关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。
横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。
连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行Spring AOP的位置。
通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。
Spring切面可以应用五种类型的通知:
before:前置通知,在一个方法执行前被调用。
after: 在方法执行之后调用的通知,无论方法执行是否成功。
after-returning: 仅当方法成功完成后执行的通知。
after-throwing: 在方法抛出异常退出时执行的通知。
around: 在方法执行之前和之后调用的通知。
切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。
引入允许我们在已存在的类中增加新的方法和属性。
被一个或者多个切面所通知的对象。它通常是一个代理对象。也指被通知(advised)对象。
代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。
BeanNameAutoProxyCreator
DefaultAdvisorAutoProxyCreator
Metadata autoproxying
织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。
织入可以在编译时,加载时,或运行时完成。
在这种情况下,切面由常规类以及基于XML的配置实现。
在这种情况下(基于@AspectJ的实现),涉及到的切面声明的风格与带有java5标注的普通java类一致。
Spring 配备构建Web 应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring 的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。
Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。
WebApplicationContext 继承了ApplicationContext 并增加了一些WEB应用必备的特有功能,它不同于一般的ApplicationContext ,因为它能处理主题,并找到被关联的servlet。
控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层,允许用户创建多种用途的控制器。
该注解表明该类扮演控制器的角色,Spring不需要你继承任何其他控制器基类或引用Servlet API。
该注解是用来映射一个URL到一个类或一个特定的方处理法上。
1)Java 中能创建 Volatile 数组吗?
能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。我的意思是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。
2)volatile 能使得一个非原子操作变成原子操作吗?
一个典型的例子是在类中有一个 long 类型的成员变量。如果你知道该成员变量会被多个线程访问,如计数器、价格等,你最好是将其设置为 volatile。为什么?因为 Java 中读取 long 类型变量不是原子的,需要分成两步,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。
3)volatile 修饰符的有过什么实践?
一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
4)volatile 类型变量提供什么保证?(答案)
volatile 变量提供顺序和可见性保证,例如,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。某些情况下,volatile 还能提供原子性,如读 64 位数据类型,像 long 和 double 都不是原子的,但 volatile 类型的 double 和 long 就是原子的。
5) 10 个线程和 2 个线程的同步代码,哪个更容易写?
从写代码的角度来说,两者的复杂度是相同的,因为同步代码与线程数量是相互独立的。但是同步策略的选择依赖于线程的数量,因为越多的线程意味着更大的竞争,所以你需要利用同步技术,如锁分离,这要求更复杂的代码和专业知识。
6)你是如何调用 wait()方法的?使用 if 块还是循环?为什么?(答案)
wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。下面是一段标准的使用 wait 和 notify 方法的代码:
- // The standard idiom for using the wait method
- synchronized (obj) {
- while (condition does not hold)
- obj.wait(); // (Releases lock, and reacquires on wakeup)
- ... // Perform action appropriate to condition
- }
参见 Effective Java 第 69 条,获取更多关于为什么应该在循环中来调用 wait 方法的内容。
7)什么是多线程环境下的伪共享(false sharing)?
伪共享是多线程系统(每个处理器有自己的局部缓存)中一个众所周知的性能问题。伪共享发生在不同处理器的上的线程对变量的修改依赖于相同的缓存行,如下图所示:
伪共享问题很难被发现,因为线程可能访问完全不同的全局变量,内存中却碰巧在很相近的位置上。如其他诸多的并发问题,避免伪共享的最基本方式是仔细审查代码,根据缓存行来调整你的数据结构。
8)什么是 Busy spin?我们为什么要使用它?
Busy spin 是一种在不释放 CPU 的基础上等待事件的技术。它经常用于避免丢失 CPU 缓存中的数据(如果线程先暂停,之后在其他CPU上运行就会丢失)。所以,如果你的工作要求低延迟,并且你的线程目前没有任何顺序,这样你就可以通过循环检测队列中的新消息来代替调用 sleep() 或 wait() 方法。它唯一的好处就是你只需等待很短的时间,如几微秒或几纳秒。LMAX 分布式框架是一个高性能线程间通信的库,该库有一个 BusySpinWaitStrategy 类就是基于这个概念实现的,使用 busy spin 循环 EventProcessors 等待屏障。
9)Java 中怎么获取一份线程 dump 文件?
在 Linux 下,你可以通过命令 kill -3 PID (Java 进程的进程 ID)来获取 Java 应用的 dump 文件。在 Windows 下,你可以按下 Ctrl + Break 来获取。这样 JVM 就会将线程的 dump 文件打印到标准输出或错误文件中,它可能打印在控制台或者日志文件中,具体位置依赖应用的配置。如果你使用Tomcat。
10)Swing 是线程安全的?(答案)
不是,Swing 不是线程安全的。你不能通过任何线程来更新 Swing 组件,如 JTable、JList 或 JPanel,事实上,它们只能通过 GUI 或 AWT 线程来更新。这就是为什么 Swing 提供 invokeAndWait() 和 invokeLater() 方法来获取其他线程的 GUI 更新请求。这些方法将更新请求放入 AWT 的线程队列中,可以一直等待,也可以通过异步更新直接返回结果。你也可以在参考答案中查看和学习到更详细的内容。
11)什么是线程局部变量?(答案)
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
12)用 wait-notify 写一段代码来解决生产者-消费者问题?(答案)
请参考答案中的示例代码。只要记住在同步块中调用 wait() 和 notify()方法,如果阻塞,通过循环来测试等待条件。
13) 用 Java 写一个线程安全的单例模式(Singleton)?(答案)
请参考答案中的示例代码,这里面一步一步教你创建一个线程安全的 Java 单例类。当我们说线程安全时,意思是即使初始化是在多线程环境中,仍然能保证单个实例。Java 中,使用枚举作为单例类是最简单的方式来创建线程安全单例模式的方式。
14)Java 中 sleep 方法和 wait 方法的区别?(答案)
虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁。
15)什么是不可变对象(immutable object)?Java 中怎么创建一个不可变对象?(答案)
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。详情参见答案,一步一步指导你在 Java 中创建一个不可变的类。
16)我们能创建一个包含可变对象的不可变对象吗?
是的,我们是可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。最常见的例子就是对象中包含一个日期对象的引用。
17)Java 中应该使用什么数据类型来代表价格?(答案)
如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义精度的 double 类型。
18)怎么将 byte 转换为 String?(答案)
可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。
19)Java 中怎样将 bytes 转换为 long 类型?
这个问题你来回答
20)我们能将 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围,将会出现什么现象?
是的,我们可以做强制转换,但是 Java 中 int 是 32 位的,而 byte 是 8 位的,所以,如果强制转化是,int 类型的高 24 位将会被丢弃,byte 类型的范围是从 -128 到 128。
21)存在两个类,B 继承 A,C 继承 B,我们能将 B 转换为 C 么?如 C = (C) B;(answer答案)
22)哪个类包含 clone 方法?是 Cloneable 还是 Object?(答案)
java.lang.Cloneable 是一个标示性接口,不包含任何方法,clone 方法在 object 类中定义。并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ 或 其他本地语言实现的。
23)Java 中 ++ 操作符是线程安全的吗?(答案)
23)不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。
24)a = a + b 与 a += b 的区别(答案)
+= 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两这个整型相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作。如果加法操作的结果比 a 的最大值要大,则 a+b 会出现编译错误,但是 a += b 没问题,如下:
byte a = 127;
byte b = 127;
b = a + b; // error : cannot convert from int to byte
b += a; // ok
(译者注:这个地方应该表述的有误,其实无论 a+b 的值为多少,编译器都会报错,因为 a+b 操作会将 a、b 提升为 int 类型,所以将 int 类型赋值给 byte 就会编译出错)
25)我能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗?(答案)
不行,你不能在没有强制类型转换的前提下将一个 double 值赋值给 long 类型的变量,因为 double 类型的范围比 long 类型更广,所以必须要进行强制转换。
26)3*0.1 == 0.3 将会返回什么?true 还是 false?(答案)
false,因为有些浮点数不能完全精确的表示出来。
27)int 和 Integer 哪个会占用更多的内存?(答案)
Integer 对象会占用更多的内存。Integer 是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。
28)为什么 Java 中的 String 是不可变的(Immutable)?(answer答案)
Java 中的 String 不可变是因为 Java 的设计者认为字符串使用非常频繁,将字符串设置为不可变可以允许多个客户端之间共享相同的字符串。更详细的内容参见答案。
29)我们能在 Switch 中使用 String 吗?(answer答案)
从 Java 7 开始,我们可以在 switch case 中使用字符串,但这仅仅是一个语法糖。内部实现在 switch 中使用字符串的 hash code。
30)Java 中的构造器链是什么?(answer答案)
当你从一个构造器中调用另一个构造器,就是Java 中的构造器链。这种情况只在重载了类的构造器的时候才会出现。
31)64 位 JVM 中,int 的长度是多数?
Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。
32)Serial 与 Parallel GC之间的不同之处?(答案)
Serial 与 Parallel 在GC执行的时候都会引起 stop-the-world。它们之间主要不同 serial 收集器是默认的复制收集器,执行 GC 的时候只有一个线程,而 parallel 收集器使用多个 GC 线程来执行。
33)32 位和 64 位的 JVM,int 类型变量的长度是多数?(答案)
32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4 个字节。
34)Java 中 WeakReference 与 SoftReference的区别?(答案)
虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。
35)WeakHashMap 是怎么工作的?(答案)
WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。
36)JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用?(答案)
当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小,通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。
37)怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位?(答案)
你可以检查某些系统属性如 sun.arch.data.model 或 os.arch 来获取该信息。
38)32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?(答案)
理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。
39)JRE、JDK、JVM 及 JIT 之间有什么不同?(答案)
JRE 代表 Java 运行时(Java run-time),是运行 Java 引用所必须的。JDK 代表 Java 开发工具(Java development kit),是 Java 程序的开发工具,如 Java 编译器,它也包含 JRE。JVM 代表 Java 虚拟机(Java virtual machine),它的责任是运行 Java 应用。JIT 代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。
3 年工作经验的 Java 面试题
40)解释 Java 堆空间及 GC?(答案)
当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的一个进程,回收无效对象的内存用于将来的分配。
41)你能保证 GC 执行吗?(答案)
不能,虽然你可以调用 System.gc() 或者 Runtime.gc(),但是没有办法保证 GC 的执行。
42)怎么获取 Java 程序使用的内存?堆使用的百分比?
可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。
43)Java 中堆和栈有什么区别?(答案)
JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
44)“a==b”和”a.equals(b)”有什么区别?(答案)
如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。
45)a.hashCode() 有什么用?与 a.equals(b) 有什么关系?(答案)
hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。
46)final、finalize 和 finally 的不同之处?(答案)
final 是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变。finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证。finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常。
47)Java 中的编译期常量是什么?使用它又什么风险?
公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里的 public 可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了避免这种情况,当你在更新依赖 JAR 文件时,确保重新编译你的程序。
这部分也包含数据结构、算法及数组的面试问题
48) List、Set、Map 和 Queue 之间的区别(答案)
List 是一个有序集合,允许元素重复。它的某些实现可以提供基于下标值的常量访问时间,但是这不是 List 接口保证的。Set 是一个无序集合。
49)poll() 方法和 remove() 方法的区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
50)Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?(答案)
PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。
51)ArrayList 与 LinkedList 的不区别?(答案)
最明显的区别是 ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构书链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。更多细节的讨论参见答案。
52)用哪两种方式来实现集合的排序?(答案)
你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。
53)Java 中怎么打印数组?(answer答案)
你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println() 方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。
54)Java 中的 LinkedList 是单向链表还是双向链表?(答案)
是双向链表,你可以检查 JDK 的源码。在 Eclipse,你可以使用快捷键 Ctrl + T,直接在编辑器中打开该类。
55)Java 中的 TreeMap 是采用什么树实现的?(答案)
Java 中的 TreeMap 是使用红黑树实现的。
56) Hashtable 与 HashMap 有什么不同之处?(答案)
这两个类有许多不同的地方,下面列出了一部分:
a) Hashtable 是 JDK 1 遗留下来的类,而 HashMap 是后来增加的。
b)Hashtable 是同步的,比较慢,但 HashMap 没有同步策略,所以会更快。
c)Hashtable 不允许有个空的 key,但是 HashMap 允许出现一个 null key。
更多的不同之处参见答案。
57)Java 中的 HashSet,内部是如何工作的?(answer答案)
HashSet 的内部采用 HashMap来实现。由于 Map 需要 key 和 value,所以所有 key 的都有一个默认 value。类似于 HashMap,HashSet 不允许重复的 key,只允许有一个null key,意思就是 HashSet 中只允许存储一个 null 对象。
58)写一段代码在遍历 ArrayList 时移除一个元素?(答案)
该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。这有一段示例代码,是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。
59)我们能自己写一个容器类,然后使用 for-each 循环码?
可以,你可以写一个自己的容器类。如果你想使用 Java 中增强的循环来遍历,你只需要实现 Iterable 接口。如果你实现 Collection 接口,默认就具有该属性。
60)ArrayList 和 HashMap 的默认大小是多数?(答案)
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段:
- // from ArrayList.java JDK 1.7
- private static final int DEFAULT_CAPACITY = 10;
-
- //from HashMap.java JDK 7
- static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
61)有没有可能两个不相等的对象有有相同的 hashcode?
有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为什么在 hashmap 中会有冲突。相等 hashcode 值的规定只是说如果两个对象相等,必须有相同的hashcode 值,但是没有关于不相等对象的任何规定。
62)两个相同的对象会有不同的的 hash code 吗?
不能,根据 hash code 的规定,这是不可能的。
63)我们可以在 hashcode() 中使用随机数字吗?(答案)
不行,因为对象的 hashcode 值必须是相同的。参见答案获取更多关于 Java 中重写 hashCode() 方法的知识。
64)Java 中,Comparator 与 Comparable 有什么不同?(答案)
Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。
65)为什么在重写 equals 方法的时候需要重写 hashCode 方法?(答案)
因为有强制的规范指定需要同时重写 hashcode 与 equal 是方法,许多容器类,如 HashMap、HashSet 都依赖于 hashcode 与 equals 的规定。
IO 是 Java 面试中一个非常重要的点。你应该很好掌握 Java IO,NIO,NIO2 以及与操作系统,磁盘 IO 相关的基础知识。下面是 Java IO 中经常问的问题。
66)在我 Java 程序中,我有三个 socket,我需要多少个线程来处理?
67)Java 中怎么创建 ByteBuffer?
68)Java 中,怎么读写 ByteBuffer ?
69)Java 采用的是大端还是小端?
70)ByteBuffer 中的字节序是什么?
71)Java 中,直接缓冲区与非直接缓冲器有什么区别?(答案)
72)Java 中的内存映射缓存区是什么?(answer答案)
73)socket 选项 TCP NO DELAY 是指什么?
74)TCP 协议与 UDP 协议有什么区别?(answer答案)
75)Java 中,ByteBuffer 与 StringBuffer有什么区别?(答案)
包含 Java 中各个部分的最佳实践,如集合,字符串,IO,多线程,错误和异常处理,设计模式等等。
76)Java 中,编写多线程程序的时候你会遵循哪些最佳实践?(答案)
这是我在写Java 并发程序的时候遵循的一些最佳实践:
a)给线程命名,这样可以帮助调试。
b)最小化同步的范围,而不是将整个方法同步,只对关键部分做同步。
c)如果可以,更偏向于使用 volatile 而不是 synchronized。
d)使用更高层次的并发工具,而不是使用 wait() 和 notify() 来实现线程间通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。
e)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。
77)说出几点 Java 中使用 Collections 的最佳实践(答案)
这是我在使用 Java 中 Collectionc 类的一些最佳实践:
a)使用正确的集合类,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。
b)优先使用并发集合,而不是对集合进行同步。并发集合提供更好的可扩展性。
c)使用接口代表和访问集合,如使用List存储 ArrayList,使用 Map 存储 HashMap 等等。
d)使用迭代器来循环集合。
e)使用集合的时候使用泛型。
78)说出至少 5 点在 Java 中使用线程的最佳实践。(答案)
这个问题与之前的问题类似,你可以使用上面的答案。对线程来说,你应该:
a)对线程命名
b)将线程和任务分离,使用线程池执行器来执行 Runnable 或 Callable。
c)使用线程池
79)说出 5 条 IO 的最佳实践(答案)
IO 对 Java 应用的性能非常重要。理想情况下,你不应该在你应用的关键路径上避免 IO 操作。下面是一些你应该遵循的 Java IO 最佳实践:
a)使用有缓冲区的 IO 类,而不要单独读取字节或字符。
b)使用 NIO 和 NIO2
c)在 finally 块中关闭流,或者使用 try-with-resource 语句。
d)使用内存映射文件获取更快的 IO。
80)列出 5 个应该遵循的 JDBC 最佳实践(答案)
有很多的最佳实践,你可以根据你的喜好来例举。下面是一些更通用的原则:
a)使用批量的操作来插入和更新数据
b)使用 PreparedStatement 来避免 SQL 异常,并提高性能。
c)使用数据库连接池
d)通过列名来获取结果集,不要使用列的下标来获取。
81)说出几条 Java 中方法重载的最佳实践?(答案)
下面有几条可以遵循的方法重载的最佳实践来避免造成自动装箱的混乱。
a)不要重载这样的方法:一个方法接收 int 参数,而另个方法接收 Integer 参数。
b)不要重载参数数量一致,而只是参数顺序不同的方法。
c)如果重载的方法参数个数多于 5 个,采用可变参数。
82)在多线程环境下,SimpleDateFormat 是线程安全的吗?(答案)
不是,非常不幸,DateFormat 的所有实现,包括 SimpleDateFormat 都不是线程安全的,因此你不应该在多线程序中使用,除非是在对外线程安全的环境中使用,如 将 SimpleDateFormat 限制在 ThreadLocal 中。如果你不这么做,在解析或者格式化日期的时候,可能会获取到一个不正确的结果。因此,从日期、时间处理的所有实践来说,我强力推荐 joda-time 库。
83)Java 中如何格式化一个日期?如格式化为 ddMMyyyy 的形式?(答案)
Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期。DateFormat 类允许你使用多种流行的格式来格式化日期。参见答案中的示例代码,代码中演示了将日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。
84)Java 中,怎么在格式化的日期中显示时区?(答案)
85)Java 中 java.util.Date 与 java.sql.Date 有什么区别?(答案)
86)Java 中,如何计算两个日期之间的差距?(程序)
87)Java 中,如何将字符串 YYYYMMDD 转换为日期?(答案)
89)如何测试静态方法?(答案)
可以使用 PowerMock 库来测试静态方法。
90)怎么利用 JUnit 来测试一个方法的异常?(答案)
91)你使用过哪个单元测试库来测试你的 Java 程序?(答案)
92)@Before 和 @BeforeClass 有什么区别?(答案)
93)怎么检查一个字符串只包含数字?(解决方案)
94)Java 中如何利用泛型写一个 LRU 缓存?(答案<)
95)写一段 Java 程序将 byte 转换为 long?(答案)
95)在不使用 StringBuffer 的前提下,怎么反转一个字符串?(解决方案)
97)Java 中,怎么获取一个文件中单词出现的最高频率?(解决方案)
98)如何检查出两个给定的字符串是反序的?(解决方案)
99)Java 中,怎么打印出一个字符串的所有排列?(解决方案)
100)Java 中,怎样才能打印出数组中的重复元素?(解决方案)
101)Java 中如何将字符串转换为整数?(解决方案)
102)在没有使用临时变量的情况如何交换两个整数变量的值?(解决方案)
这部分包含 Java 面试过程中关于 SOLID 的设计原则,OOP 基础,如类,对象,接口,继承,多态,封装,抽象以及更高级的一些概念,如组合、聚合及关联。也包含了 GOF 设计模式的问题。
103)接口是什么?为什么要使用接口而不是直接使用具体类?
接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以使用可随机访问的 ArrayList,也可以使用方便插入和删除的 LinkedList。接口中不允许写代码,以此来保证抽象,但是 Java 8 中你可以在接口声明静态的默认方法,这种方法是具体的。
104)Java 中,抽象类与接口之间有什么不同?(答案)
Java 中,抽象类和接口有很多不同之处,但是最重要的一个是 Java 中限制一个类只能继承一个类,但是可以实现多个接口。抽象类可以很好的定义一个家族类的默认行为,而接口能更好的定义类型,有助于后面实现多态机制。关于这个问题的讨论请查看答案。
105)除了单例模式,你在生产环境中还用过什么设计模式?
这需要根据你的经验来回答。一般情况下,你可以说依赖注入,工厂模式,装饰模式或者观察者模式,随意选择你使用过的一种即可。不过你要准备回答接下的基于你选择的模式的问题。
106)你能解释一下里氏替换原则吗?(答案)
107) 什么情况下会违反迪米特法则?为什么会有这个问题?(答案)
迪米特法则建议“只和朋友说话,不要陌生人说话”,以此来减少类之间的耦合。
108)适配器模式是什么?什么时候使用?
适配器模式提供对接口的转换。如果你的客户端使用某些接口,但是你有另外一些接口,你就可以写一个适配去来连接这些接口。
109)什么是“依赖注入”和“控制反转”?为什么有人使用?(答案)
110)抽象类是什么?它与接口有什么区别?你为什么要使用过抽象类?(答案)
111)构造器注入和 setter 依赖注入,那种方式更好?(答案)
每种方式都有它的缺点和优点。构造器注入保证所有的注入都被初始化,但是 setter 注入提供更好的灵活性来设置可选依赖。如果使用 XML 来描述依赖,Setter 注入的可读写会更强。经验法则是强制依赖使用构造器注入,可选依赖使用 setter 注入。
112)依赖注入和工程模式之间有什么不同?(答案)
虽然两种模式都是将对象的创建从应用的逻辑中分离,但是依赖注入比工程模式更清晰。通过依赖注入,你的类就是 POJO,它只知道依赖而不关心它们怎么获取。使用工厂模式,你的类需要通过工厂来获取依赖。因此,使用 DI 会比使用工厂模式更容易测试。关于这个话题的更详细讨论请参见答案。
113)适配器模式和装饰器模式有什么区别?(答案)
虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能。
114)适配器模式和代理模式之前有什么不同?(答案)
这个问题与前面的类似,适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式和代理模式都是封装真正执行动作的类,因此结构是一致的,但是适配器模式用于接口之间的转换,而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问。
115)什么是模板方法模式?(答案)
模板方法提供算法的框架,你可以自己去配置或定义步骤。例如,你可以将排序算法看做是一个模板。它定义了排序的步骤,但是具体的比较,可以使用 Comparable 或者其语言中类似东西,具体策略由你去配置。列出算法概要的方法就是众所周知的模板方法。
116)什么时候使用访问者模式?(答案)
访问者模式用于解决在类的继承层次上增加操作,但是不直接与之关联。这种模式采用双派发的形式来增加中间层。
117)什么时候使用组合模式?(答案)
组合模式使用树结构来展示部分与整体继承关系。它允许客户端采用统一的形式来对待单个对象和对象容器。当你想要展示对象这种部分与整体的继承关系时采用组合模式。
118)继承和组合之间有什么不同?(答案)
虽然两种都可以实现代码复用,但是组合比继承共灵活,因为组合允许你在运行时选择不同的实现。用组合实现的代码也比继承测试起来更加简单。
119)描述 Java 中的重载和重写?(答案)
重载和重写都允许你用相同的名称来实现不同的功能,但是重载是编译时活动,而重写是运行时活动。你可以在同一个类中重载方法,但是只能在子类中重写方法。重写必须要有继承。
120)Java 中,嵌套公共静态类与顶级类有什么不同?(答案)
类的内部可以有多个嵌套公共静态类,但是一个 Java 源文件只能有一个顶级公共类,并且顶级公共类的名称与源文件名称必须一致。
121) OOP 中的 组合、聚合和关联有什么区别?(答案)
如果两个对象彼此有关系,就说他们是彼此相关联的。组合和聚合是面向对象中的两种形式的关联。组合是一种比聚合更强力的关联。组合中,一个对象是另一个的拥有者,而聚合则是指一个对象使用另一个对象。如果对象 A 是由对象 B 组合的,则 A 不存在的话,B一定不存在,但是如果 A 对象聚合了一个对象 B,则即使 A 不存在了,B 也可以单独存在。
122)给我一个符合开闭原则的设计模式的例子?(答案)
开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增加一个新的功能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码。有好几个设计模式是基于开闭原则的,如策略模式,如果你需要一个新的策略,只需要实现接口,增加配置,不需要改变核心逻辑。一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式,遵循开闭原则的,你不需为新的对象修改 sort() 方法,你需要做的仅仅是实现你自己的 Comparator 接口。
123)抽象工厂模式和原型模式之间的区别?(答案)
124)什么时候使用享元模式?(答案)
享元模式通过共享对象来避免创建太多的对象。为了使用享元模式,你需要确保你的对象是不可变的,这样你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。
这部分包含 Java 中关于 XML 的面试题,JDBC 面试题,正则表达式面试题,Java 错误和异常及序列化面试题
125)嵌套静态类与顶级类有什么区别?(答案)
一个公共的顶级类的源文件名称与类名相同,而嵌套静态类没有这个要求。一个嵌套类位于顶级类内部,需要使用顶级类的名称来引用嵌套静态类,如 HashMap.Entry 是一个嵌套静态类,HashMap 是一个顶级类,Entry是一个嵌套静态类。
126)你能写出一个正则表达式来判断一个字符串是否是一个数字吗?(解决方案)
一个数字字符串,只能包含数字,如 0 到 9 以及 +、- 开头,通过这个信息,你可以下一个如下的正则表达式来判断给定的字符串是不是数字。
127)Java 中,受检查异常 和 不受检查异常的区别?(答案)
受检查异常编译器在编译期间检查。对于这种异常,方法强制处理或者通过 throws 子句声明。其中一种情况是 Exception 的子类但不是 RuntimeException 的子类。非受检查是 RuntimeException 的子类,在编译阶段不受编译器的检查。
128)Java 中,throw 和 throws 有什么区别?(答案)
throw 用于抛出 java.lang.Throwable 类的一个实例化对象,意思是说你可以通过关键字 throw 抛出一个 Error 或者 一个Exception,如:
throw new IllegalArgumentException(“size must be multiple of 2″)
而throws 的作用是作为方法声明和签名的一部分,方法被抛出相应的异常以便调用者能处理。Java 中,任何未处理的受检查异常强制在 throws 子句中声明。
129)Java 中,Serializable 与 Externalizable 的区别?(答案)
Serializable 接口是一个序列化 Java 类的接口,以便于它们可以在网络上传输或者可以将它们的状态保存在磁盘上,是 JVM 内嵌的默认序列化方式,成本高、脆弱而且不安全。Externalizable 允许你控制整个序列化过程,指定特定的二进制格式,增加安全机制。
130)Java 中,DOM 和 SAX 解析器有什么不同?(答案)
DOM 解析器将整个 XML 文档加载到内存来创建一棵 DOM 模型树,这样可以更快的查找节点和修改 XML 结构,而 SAX 解析器是一个基于事件的解析器,不会将整个 XML 文档加载到内存。由于这个原因,DOM 比 SAX 更快,也要求更多的内存,不适合于解析大 XML 文件。
131)说出 JDK 1.7 中的三个新特性?(答案)
虽然 JDK 1.7 不像 JDK 5 和 8 一样的大版本,但是,还是有很多新的特性,如 try-with-resource 语句,这样你在使用流或者资源的时候,就不需要手动关闭,Java 会自动关闭。Fork-Join 池某种程度上实现 Java 版的 Map-reduce。允许 Switch 中有 String 变量和文本。菱形操作符(<>)用于类型推断,不再需要在变量声明的右边申明泛型,因此可以写出可读写更强、更简洁的代码。另一个值得一提的特性是改善异常处理,如允许在同一个 catch 块中捕获多个异常。
132)说出 5 个 JDK 1.8 引入的新特性?(答案)
Java 8 在 Java 历史上是一个开创新的版本,下面 JDK 8 中 5 个主要的特性:
Lambda 表达式,允许像对象一样传递匿名函数
Stream API,充分利用现代多核 CPU,可以写出很简洁的代码
Date 与 Time API,最终,有一个稳定、简单的日期和时间库可供你使用
扩展方法,现在,接口中可以有静态、默认方法。
重复注解,现在你可以将相同的注解在同一类型上使用多次。
133)Java 中,Maven 和 ANT 有什么区别?(答案)
虽然两者都是构建工具,都用于创建 Java 应用,但是 Maven 做的事情更多,在基于“约定优于配置”的概念下,提供标准的Java 项目结构,同时能为应用自动管理依赖(应用中所依赖的 JAR 文件),Maven 与 ANT 工具更多的不同之处请参见答案。
这就是所有的面试题,如此之多,是不是?我可以保证,如果你能回答列表中的所有问题,你就可以很轻松的应付任何核心 Java 或者高级 Java 面试。虽然,这里没有涵盖 Servlet、JSP、JSF、JPA,JMS,EJB 及其它 Java EE 技术,也没有包含主流的框架如 Spring MVC,Struts 2.0,Hibernate,也没有包含 SOAP 和 RESTful web service,但是这份列表对做 Java 开发的、准备应聘 Java web 开发职位的人还是同样有用的,因为所有的 Java 面试,开始的问题都是 Java 基础和 JDK API 相关的。如果你认为我这里有任何应该在这份列表中而被我遗漏了的 Java 流行的问题,你可以自由的给我建议。我的目的是从最近的面试中创建一份最新的、最优的 Java 面试问题列表。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。