Java面试的一些问题

数据库

如何实现数据库的分页?

Mysql:可用这个模式select * from table where 条件 limit 当前页码*页面容量 - 1,每页的数据量;e.g:select * from user where age = 35 limit 11,6;其中:当前页码为2,页面容量为6。

Oracle:select * from (select A.* from (select * from user order by id) A where id > 5) B where B.id < 20;这种方式对于mysql也是适用的,因此不能说是Oracle独有的方式

谈一谈存储过程?

存储过程是一组为了完成特定功能的SQL语句集,其语法如下:

CREATE PROCEDURE  过程名([[IN|OUT|INOUT] 参数名 数据类型[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 ...] 过程体
一个存储过程如下
DELIMITER //
  CREATE PROCEDURE myproc(OUT s int)
    BEGIN
      SELECT COUNT(*) INTO s FROM students;
    END
    //
DELIMITER ;

其中类型参数有多个取值。其取值说明如下:
. LANGUAGE SQL:说明routine_body部分是由SQL语言的语句组成,这也是数据库系统默认的语言。
. [NOT] DETERMINISTIC:指明存储过程的执行结果是否是确定的。DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC表示结果是非确定的,相同的输入可能得到不同的输出。默认情况下,结果是非确定的。
. { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }:指明子程序使用SQL语句的限制。CONTAINS SQL表示子程序包含SQL语句,但不包含读或写数据的语句;NO SQL表示子程序中不包含SQL语句;READS SQL DATA表示子程序中包含读数据的语句;MODIFIES SQL DATA表示子程序中包含写数据的语句。默认情况下,系统会指定为CONTAINS SQL。
. SQL SECURITY { DEFINER | INVOKER }:指明谁有权限来执行。DEFINER表示只有定义者自己才能够执行;INVOKER表示调用者可以执行。默认情况下,系统指定的权限是DEFINER。
. COMMENT 'string':注释信息。

举个例子,如下

mysql> DELIMITER &&//将&&改为默认的换行
mysql> create procedure SearchUser
    -> (IN user_id INT,OUT count INT)
    -> READS SQL DATA
    -> BEGIN
    -> select count(*) into count from user
    -> where id=user_id;
    -> END &&
mysql>DELIMITER ;//把默认的换行再改成';'
mysql>call SearchUser(1,@count);//调用存储过程
mysql>select @count;//显示返回的结果

存储函数的格式如下

CREATE FUNCTION sp_name ([func_parameter[,...]])  
        RETURNS type  
        [characteristic ...] routine_body 

举个例子

mysql> delimiter &&
mysql> create function select_name(age int)
    -> returns varchar(20)
    -> begin
    -> return(select userName
    -> from user
    -> where userAge=age);
    -> end &&
mysql>delimiter ;
mysql>select select_name(42);
Inner Join和Outer Join的区别

Inner Join:取两个表查询结果的交集
Outer Join:

  1. Left Join:以左边的表为全集,右边的表中匹配则有值,不匹配则没有值
  2. Right Join:以右边的表为全集,左边的表中匹配则有值,不匹配则没有值
  3. Full Join:取左右两边的表的并集,两个表都是匹配则有值,不匹配则没有值
描述一下Oracle的体系结构

组成:数据库、表空间、逻辑对象、数据段,数据区间和数据块

描述一下数据库结构化和非结构化数据

结构化数据:可以被数据或统一的结构表示的数据,像数字和符号
非结构化数据:不能被统一的结构表示的数据,像所有格式的文档,XML,HTML和图像和音视频等。

描述一下SQL是如何注入的?
如何保证数据库的安全读写?
什么是数据库的死锁,哪些情形会发生死锁,如何避免数据库的死锁

数据库死锁:当多个线程同时访问数据库的情况下,比如两个线程,假如需要获取两个资源才能访问数据库的话,假设这两个资源分别为A和B,假如一个线程获取A,另一个线程获取B,那么在这种情况下会出现数据库无法访问的情形。
发生死锁的必要条件

1. 互斥条件:对数据库的写是独占式的,就是说一个线程在对数据库的写操作的过程中必须排斥其它线程对数据库
的任何读写行为
2. 请求和保持条件:一个线程获得了访问数据库所必须的一个资源,但是有需要获得其它资源才能访问,而其它资
源又被其它线程所拥有,那么此时可能(在其它资源不是互斥资源的情况下,互斥条件一定会死锁)会发生死锁
3. 不剥夺条件:线程在获取资源的情况下,它的资源不会被剥夺
4. 环路等待条件:指一个获取资源的线程集合中前一个线程依次获取下一个线程所需的资源,这种情况会发生死锁

避免数据库的死锁
一般来说破坏死锁形成的必要条件之一就可以避免死锁,从发生死锁的必要条件来说说

1. 破坏互斥条件:在数据库的操作中,假如我们对数据库的修改是任何时刻任意修改的,那么就没有必要加锁来
进行访问了,于是此时就永远不会发生死锁,但是这样访问数据的行为非常危险,所以这种做法不可取
2. 破坏请求和保持条件:也就是说我们可以将锁的个数减为1,那么就可以确保一个时间只有一个线程在访问锁,
也可以使用银行家算法来设计资源的数量来避免死锁,这种方法是可行的,而且我觉得可以适应更复杂的情形
3. 不剥夺条件:就是说我们可以控制获取锁的时间限制,等到一定时间必须释放锁,这个条件也不错,但是有风险
假如一个操作所花的时间过长,而设的时间又比较短,那么就可能出现事务的执行经常会失败的情形
4. 环路等待条件:比如一个集合{p0,p1...p5},其中p0获取p1所需要的资源,p1获取p2所需要的资源,以此类
推,那么假如我们修改其中一个获取资源的方式,比如把p0改为获取p5所需的资源,那么此时p5就会和p0同时获取
同一个资源,但是其它线程总能因为存在一个资源必然能够获得而不会发生阻塞
描述一下数据库连接池

数据库连接池是通过对数据库连接复用的方式来提升数据库访问效率的一种技术,假如我们需要使用一个连接池来提高我们的多线程程序访问数据库的效率,我们可以这样来设计我们的数据库连接池:

  1. 创建一个包含一个上限值为特定大小数据库连接的连接池,这个连接池的实现可以通过用一个队列来管理要打开的连接。对于连接池中的连接,我们可以通过延迟创建连接的方式来进行管理,即我们先遍历连接池中的连接,假如连接是空闲的,我们不用创建连接,而是直接使用连接;假如所有连接都被使用,那么我们看连接池中的连接数是否达到我们设定的上限,没有设定上限的话,我们就创建一个连接并使用,到达上限的话,那么请求就会被阻塞直到有数据库连接被空闲下来
  2. 连接可以通过动态代理来进行创建

Java基础

类的加载过程
类加载器树状图

假如我们在加载一个类,我们首先使用的是ExtensionClassLoader类加载器来加载类,当这个类加载器没有该类的class字节码时,则采用SystemClassLoader来加载,如果这个类加载器也没有这个class字节码,则给ClassLoader(应用类加载器)进行加载,如果加载失败,则抛出ClassNotFoundException

利用ArrayList实现栈和队列效果

栈:一个简单的做法就是在push的时候直接添加ArrayList.add(e),在出栈的时候获取并移除最后一个元素ArrayList.remove(list.length() - 1);
队列:一个简单的做法就是在push的时候直接添加ArrayList.add(e),在出栈的时候获取并移除第一个元素ArrayList.remove(0);

Vector和ArrayList的区别

1、Vector是线程安全的,ArrayList是线程非安全的,因此Vector的性能会更好
2、当Vector的容量超出其初始容量时,它所要求的空间会拓展至原来的一倍,而ArrayList只会拓展为原来的50%,因此ArrayList会更节省空间

说一说常用的并发工具类

CountDownLatch:用来同步一个或多个任务,让它们等待由其它任务执行的一组操作完成
CyclicBarrier:创建一个任务,它们并行地执行工作,然后在进行下一个步骤之前等待
DelayQueue:其中的对象只能在其到期时才能从队列中取走
ReetrantLock:重入锁,允许对一个锁的再次获取
ReetrantReadWriteLock:读写锁,允许多个线程读,只允许一个线程写
Semaphore:信号量,允许多个线程同时访问一个资源

HashMap和TreeMap的区别

HashMap:遍历时的顺序是无序的,但是性能要更高
TreeMap:元素是按照自然排序获取自定义排序的方式进行存储的,因此遍历的时候会得到一个有序的结果

Java 的annotation的作用

注解为我们代码中添加信息提供了一种形式化的方法,一般有下面几个作用

1. 生成文档,
2. 跟踪代码的依赖性,实现替代配置文件功能
3. 在编译时进行格式检查,比如@Override,我们可以确定我们要覆写的方法名是正确的而不是一个新的方法

下面是一个自定义的注解,源码来自《Java编程思想》
UseCase.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    public int id();
    public String description() default  "no description";
}

PasswordUtil.java

public class PasswordUtil {
    @UseCase(id = 47,description = "Passwords must contain at one numberic")
    public boolean validatePassword(String password){
        return password.matches("\\w*\\d\\w*");
    }

    @UseCase(id = 48)
    public String encryptPassword(String password){
       return new StringBuilder(password).reverse().toString();
    }

    @UseCase(id = 49,description = "New password can't equal previousli used one")
    public boolean checkForNewPassword(List<String> prepassword, String password){
        return !prepassword.contains(password);
    }
}
描述一下MVC模式

即模型-视图-控制器模式,如下
模型:应用对象
视图:数据的展示
控制器:逻辑处理
大型web系统这样的架构有利于功能上的解耦和代码维护

什么是同步调用?什么是异步调用?什么是回调?Java如何将异步调用转化为同步调用?

同步调用:阻塞式调用,调用方需要等待被调用方执行完才返回
异步调用:一种类似消息和通知的机制,接口的服务在接收到通知之后,会主动通知客户方。
回调:被调用方在接口被调用时也会调用调用方的接口
异步调用转同步调用:可以使用互斥锁的机制来实现异步调用转化为同步调用,在异步调用中,不同的调用线程没有相互的联系,我们可以加入一个竞争锁的方式使得其他调用必须等到当前的调用完成方才继续,这是我的一个想法。

如何优化Java程序的性能,请详细地说一下
. 尽量将类的描述符为final,这样Java编译器会寻找机会内联(不是特别明白)
. 尽量重用对象,这是因为对象在没有被引用的时候会出发虚拟机的垃圾回收机制
. 尽量使用局部变量,因为局部变量存储在栈中,栈的请求比堆的请求更有效率
. 不要重复初始化变量
. 在Java + Oracle的过程中,SQL尽量大写,这样可以省去Oracle的解析时间
. Java在进行数据库的操作过程中,对I/O的操作是应该尽量在使用完之后关闭流
. 对象在使用完之后,尽量将其设为null,以免内存泄漏
. 减少变量的计算
. 采用懒加载机制
. 不要用异常,尽量使用try-catch,因为异常会创建一个异常对象
. StringBuffer在使用时应尽量设置一个合理的大小,因为在长度不足而进行拓展的时候,拓展的长度是所需长度的2倍再加上2个字节
. 复制大量数据时,采用System.copy()
. 尽量用移位代替乘法
. JSP中关闭无用的会话
. 不要将数组设为public static final,因为这会占用栈中的空间
. HashMap使用entry进行遍历而不是先获取Key的Set,再对Set中的成员使用get方法
. 尽量使用StringBuilder和ArrayList,而不是StringTable和Vector,后者是线程安全的,但是会影响效率
. 在for循环的外部使用try-catch,而不是在循环内部使用
. 利用设计模式重构,增加拓展性和可读性
cookie和session的区别
1、cookie数据放在客户端浏览器上,session数据放在服务器上
2、cookie不是很安全,session的安全性更高
3、session会保留在你的机器中一段时间,访问增多会占用更多的机器性能,考虑性能的时候应尽量使用cookie
4、单个cookie的大小不能超过4K,很多浏览器规定cookie的个数不能超过20个
5、建议:
      登录信息保留在session中
      其它信息如需保留可以放在cookie中
forward和redirect的区别
1. 地址栏:forward因为是在服务器中请求数据,而根本没有通知浏览器,所以浏览器不知道里面发生了什么变化,所以地址栏不变,redirect则是给浏览器发送一条返回码,让浏览器跳转到服务器对应的地址上
2. 数据共享:因为forward是在服务端做的操作,所以request的数据是可以进行共享的,而redirect则是两次请求,则其的request数据不可共享
3. 用的场景:forward一般在登录时根据表单跳转到对应的界面上,而redirect则是在系统退出登录时跳转到主界面上
4. 效率上:forward效率高,redirect效率较低
描述一下Java的代理模式和反射机制,说说那些框架上用到了这些

代理模式:简单的来说就是通过调用代理类中和目标类中相同的方法来实现有条件的调用目标类中的对应的方法,代理模式包括静态代理,动态代理以及Spring中的Cglib代理模式
反射机制:程序通过在运行中,可以得到任意类中的信息,包括类中的方法和属性
Spring框架中的IoC机制用到了反射机制
hibernate中的sql的拼装也使用到了Java的反射机制

说一说Servlet生命周期的具体情况

初始化阶段:

  1. Servlet容器把Servlet类加载到内存中去
  2. Servlet容器创建一个ServletConfig对象,用来设置运行时的初始化配置,并将ServletConfig对象和
当前的ServletContext对象相关联
  3. 创建一个Servlet对象
  4. 调用Servlet的初始化方法init(ServletConfig config)

处理请求阶段:初始化阶段完成之后,Servlet容器会处理请求,在处理请求的时候,会有这样的操作

  1. 会创建ServletRequest和ServletRespose对象,这两个对象分别是来处理客户端提出的请求和返回给客
户端的响应的对象
  2. 调用Servlet的service方法,这个上面我们创建的ServletRequest和ServletRespose对象是这个方法
的参数,客户端的请求最终都会被这个方法进行处理

结束阶段:当我们退出服务器程序的时候,Servlet容器的做法是这样的

  1. 首先Servlet容器会调用destroy()方法,一般来说,这个方法是Servlet编程人员所要进行编写考虑的,
在这个方法中,我们可以实现对文件流的关闭以及其它的一些回收处理
  2. Servlet会回收Servlet对象,然后回收ServletConfig对象

上面的初始化和销毁在一个Servlet的生命周期中只做一次,那么我们修改配置的时候,我们可能需要重启Servlet才可生效

描述一下文件的IO操作和NIO的操作的区别
  1. 处理对象:IO的处理对象是面向流的,也就是说我们会把文件按照字节一个一个或者一次多个地去读取或者写入处理,这个过程是顺序的,假如我们需要对某些数据进行特殊处理,那么我们得为这些数据建立缓存,但是NIO对数据的处理是可以回退操作的
  2. 是否阻塞:IO的处理是阻塞的,也就是说在读取数据文件的时候其它的操作会被阻塞,NIO是非阻塞的,在进行文件操作的时候可以进行其它的操作
  3. 数据处理:我们可以使用IO流的装饰器来对数据进行一些特定规则的处理,比如文件按行读取,但是由于NIO的设计是面向缓冲器的,所以NIO的数据处理并不是非常的灵活
  4. 文件处理速度:NIO的处理的速度更加快,而且我们可以针对不同文件的处理设计不同大小的缓冲器,这样就可以获取最高的效率,也更加灵活
  5. 处理场景:传统的IO在一个文件的读写中的流程都是这样的:打开文件流->读写文件流->关闭文件流,这种方式在文件小、高并发的场景中不适用,此时使用NIO就可以进行更好的操作,因为NIO中有Selector这个组件,它可以管理多个处理通道
描述一下计算机的层次结构

7层模型:从下到上:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层(OSI参考模型)
TCP/IP模型:从下到上:网络接入层,网际互联层,传输层,应用层

描述一下Java内存回收的时机?

Java内存回收是在Java虚拟机中自动进行的,Java虚拟机把堆内存分为年轻代和老年代,年轻代又可以被分为Eden区和Survivor区,我们在创建Java对象的时候虚拟机一般会先在Eden区进行内存的分配,当Eden区分配完了之后,就意味着Eden区需要对内存进行重整,这个时候会把清理之后还存活的对象放到Survivor区,这个回收被称为Minor GC,在一定周期数量的Minor GC完成之后或者Survivor区里面的内存被用完之后,会触发将年轻代中保存下来的对象放到老年代中,这被称为Major GC,调整堆中年轻代和老年代的区域比可以对Java程序的运行性能进行改善,还有一种是Full GC的模式,这种模式会启动Minor GC和Major GC,一般不要使用这种方式,这会很消耗系统的性能。我们可以调用System.gc()来通知虚拟机进行垃圾回收,但是这个操作并不是强制性的而是对Java虚拟机的一个建议,因此存在这个建议不一定会被java虚拟机接受,这就与Java虚拟机的具体实现有关了。

描述一下负载均衡,以及负载均衡的几种策略

负载均衡:是将客户端的请求分摊到多个操作单元上去进行,这个操作的关键在于操作是否是均匀的
负载均衡的方法:服务器的负载均衡分为几个方面的负载均衡,对于不同的负载均衡有不同的解决方法:

客户端->反向代理层:在将域名解析为ip的过程中,我们可以将所有请求解析为数量大致相等的ip,这些ip是反向
代理服务器的ip,这种方法为DNS轮询。
反向代理层->站点层:反向代理服务器将请求均匀的分布到处理具体请求的服务器中去,最简单的是通过服务器的
ip进行DNS的轮询的方式来分布上去,也就是说每个服务器的处理请求数量是一致的,但是由于不同的服务器处理能
力不同,因此可以采用最少路由的方式进行处理,这更加符合效率最优的原则
站点层->服务层:在一台服务器上可能会开启多个处理请求的线程,那么充分利用服务器的多线程优势就成为了解决
这个问题的关键,这个时候我们可以创建服务连接池的方式来实现这个方法
服务层->数据层:在线程访问数据库的时候如何做到提供数据访问的服务器的满载是问题的关键,可以采用两种方式
  1. 将数据切分为多个大小相等的数据库,划分依据为id(或者其它可以划分不同数据的键值)的范围值
  2. 将数据的状况进行划分,比如说按存储的数据记录的id的hash值
描述一下http和https的区别

https是基于SSL加密协议的HTTP协议,区别在于HTTPS多了一个建立安全连接的步骤,这是怎么回事呢?我们看一下HTTPS建立连接的过程

1. 客户端向服务器发送HTTPS的请求,希望请求服务器与自己进行连接
2. 服务器对这种请求一般不会拒绝,一般这时候会把客户端请求的网站中的证书给发回客户端
3. 客户端收到证书之后,会拿到证书里面的公钥
4. 此时客户端会生成一个会话密钥,这个密钥一般是对称密钥,生成之后,对会话密钥将使用从证书中拿到的公钥
进行加密,并将加密的结果传给服务器
5. 服务器利用与证书中相对应的私钥进行解密,这会拿到客户端生成的会话密钥了
6. 做完这一切之后,客户端向服务器发送HTTP请求,那么问题来了,这与一般的HTTP请求有何区别,区别就是
HTTP请求的报文会用会话密钥进行加密
描述一下安全证书

安全证书是认证网站的发布者是不是一个合法的网址的一个证明文件,一个证书文件包括了一下的一些信息

1. 证书的颁布机构
2. 证书的有效期
3. 证书的所有者(一般为网址)
4. 证书中的公钥
5. 签名所使用的算法
6. 指纹及指纹算法

在HTTPS的通信过程中,客户端可以使用证书来认证一个网站是不是合法的,同时可以使用证书中的公钥来加密发送给服务器的会话密钥

说一说JSP的内置对象
request:请求对象
response:响应对象
pageContext:页面上下文对象
Application:应用程序对象
session:会话对象
out:输出对象
config:配置对象
page:页面对象
exception:例外对象
描述一下多态的两个特征,重载和重写

重载:是针对同一种方法名的,不同方法参数和方法返回值的多个同名方法的实现,任何同名方法,只要它们的方法参数和返回值中有任意一个不一样,那么我们就说这实现了类的多态,重载是动态绑定的一种,即在运行时自动寻找匹配的方法
重写:需要对父类的方法进行覆写的一种做法,

描述一下OOP和AOP的区别

OOP:是针对业务处理过程中实例的属性和行为进行抽象封装,已获得更加清晰高效地逻辑单元
AOP:从业务逻辑的角度对系统的业务模块进行分离从而达到统一管理的目的,比如日志管理,性能分析这些操作我们可以将这些大多数类的操作从类中进行抽离
区别就是:OOP针对的实体的抽象和封装,AOP这是对相同的业务逻辑进行分离

谈一谈Executor框架的实现原理?
谈一谈synchronize的几种方法的区别
1. 修饰普通方法:对当前的类实例进行加锁,其它线程不能访问该对象
2. 修饰静态方法:对当前的类进行加锁,其它线程不能访问该类的所有对象
3. 修饰代码块:对同步的块里面的对象进行线程独享,其它的线程不能访问同步块中的对象或类对象

设计模式和框架

描述一下B/S架构和C/S架构的各自的特点和优缺点

硬件环境:C/S一般建立在专用的网络上,B/S一般建立在广域网的基础之上,只需要操作系统和浏览器就行
程序架构和安全:C/S更加注重流程,可以定制很多安全措施,而B/S需要考虑安全性和效率,那么B/S则需要建立在更加优化的基础之上
系统的维护性:C/S架构的系统的维护需要从多方面进行考虑,维护时需要考虑操作系统的变更,B/S因为是基于浏览器构建的,那么我们可以根据浏览器的部分变更来进行维护即可,可以在升级时实现无缝对接
处理的问题:C/S是面向固定的客户群的,操作系统相关,安全性能高,而B/S架构是面向未知的用户群的,因此,与操作系统关系不大

说一下Spring中的bean的生命周期
1. 寻找所有bean,并根据bean定义的信息实例化bean
2. 使用依赖注入,按bean所定义的配置初始化bean的属性
3. 若bean实现了BeanNameAware接口,那么工厂调用bean的setBeanName()方法,传递bean的id
4. 若bean实现了BeanFactoryAware接口,bean调用setBeanFactory(),调用工厂自身
5. 若bean实现了ApplicationContextAware接口,那么setApplicationContext()方法被调用
6. 若BeanPostProcessor和bean关联,那么它的postProcessBeforeInitialization()方法被调用
7. 若bean指定了init-method="init",那么它将被调用
8. 若BeanPostProcessor和bean关联,那么它的postProcessAfterInitialization()将被调用
9. 若bean实现了DisposableBean接口,那么destroy()将被调用
10. 如果指定了destroy-method="close",那么这个方法将被调用
Spring中有哪两种事务

事务是一系列具有acid特征的操作集合,其中acid特征如下

原子性:事务要求操作的原子性,要么操作都成功,要么操作都失败
一致性:事务操作的结果对整个业务模型必须是一致的,不能对一部分业务成功,一部分业务失败
隔离性:可能有许多事务同时处理相同的数据,那么每个事务都要与其它事务隔离开来
持久性:假如事务完成,无论发生什么样的状况,它的结果都不应该受到影响
spring并不管理事务,而是提供多种事务管理器来管理事务,spring的事务类型有
. JDBC事务
. Hibernate事务
. JPA事务
. JTA事务
谈一谈Spring的IoC原理

假如两个类有相互依赖的行为,比如在一个类中创建另一个类的对象,这样的做法会使系统的耦合度增高,IoC会将思想是将将有相互依赖关系的对象通过spring容器进行构建,简单来说,就是对象原来之间相互控制,现在都交由spring容器控制,这就是控制反转

谈一谈Spring中的AOP原理

AoP是将许多业务处理过程中的关注点进行分离的一种技术,这种技术可以防止耦合的发生

描述一下访问者模式

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作看下面的代码

package com.fan;


import java.util.ArrayList;
import java.util.List;
import java.util.Random;

abstract class Element{
    public abstract void accept(IVistor vistor);
    public abstract void doSomething();
}

interface IVistor{
    public void visit(Element element);
}

class Vistor implements IVistor{

    @Override
    public void visit(Element element) {
        element.doSomething();
    }
}

class ConcreateElement1 extends Element{

    @Override
    public void accept(IVistor vistor) {
       vistor.visit(this);
    }

    @Override
    public void doSomething() {
        System.out.println("这是元素1");
    }
}

class ConcreateElement2 extends Element{

    @Override
    public void accept(IVistor vistor) {
        vistor.visit(this);
    }

    @Override
    public void doSomething() {
        System.out.println("这是元素2");
    }
}

class ObjectStructure{
    public static List<Element> getList(){
        List<Element> list = new ArrayList<Element>();
        Random rand = new Random(34);
        for(int i = 0;i < 10;i++){
            int a = rand.nextInt(100);
            if(a > 50)
                list.add(new ConcreateElement1());
            else
                list.add(new ConcreateElement2());
        }
        return list;
    }
}

class Client{
    public static void test(){
        List<Element> list = ObjectStructure.getList();
        for(Element e : list)
            e.accept(new Vistor());
    }
}
/**
 * Created by Alpha on 17/4/18.
 */
public class VistorMode {
    public static void TestVistorMode(){
        Client.test();
    }
}

我们可以看到,在我们的客户端的代码不变的前提下,我们可以通过增加元素接口的实现来达添加新的功能,这样有利于我们面向接口进行编程,如上的代码,我们的客户端的访问逻辑完全植根于对Element接口而丝毫与Element的实现没有关系,Element中的方法也只与IVistor中定义的接口有关,这样就实现了业务逻辑框架和业务实现相分离的结果

说一说依赖注入
描述一下观察者模式
描述一下红黑树和B+树之间的区别,最好说一下具体的实现?

JavaScript

HTTP

说一说一个http的请求过程
  1. 连接
由于浏览器需要和服务器之间建立Socket的连接,那么必须获取服务器的ip地址和port,通过DNS对请求的地
址进行解析,我们就可以得到服务器的ip和端口号,DNS是树状结构的,因此会在解析不出域名的情况下会一直
往上提交直到根部
  1. 请求
连接成功后往服务器发送请求,请求一般是GET、POST或者HEAD类型,后面接上服务器上的文件地址和协议
  1. 应答
服务器会根据请求的信息返回一个包含http头部和http体的信息给请求者,应答的组成具体如下
http头部
    HTTP 1.0 200 ok:这是返回的请求码
    MIME_Version:1.0:这是一个MIME的版本
    content_type:返回的内容类型,一般为text/html
    content_length:返回的内容长度
http内容体
    经过编码的数据
  1. 关闭连接
响应完浏览器的请求之后,服务器会关闭与浏览器之间的连接
描述一下HTTP返回值为301和302之间的区别?
Servlet是单例模式生成的吗?

是的,但是是有条件的,只有相同业务的请求是单例模式的,而不同业务的请求则不是单例模式

代码坑

下面这个代码的输出是什么
    /**
     * 测试HashMap的输出问题
     */
    public static void TestHashMapOutput(){
        Map<Integer,String> map = new HashMap<Integer, String>();
        map.put(1,"Out1");
        map.put(3,"Out3");
        map.put(2,"Out2");
        map.put(4,"Out4");
        List<String> list = new ArrayList<String>();
        for(Map.Entry<Integer,String> entry : map.entrySet()){
            list.add(entry.getValue());
        }
        System.out.println(list);
    }

输出结果如下


原因:当我们把元素存入HashMap之中时,存入的键值对会以的hash的方式进行顺序排列,当我们通过迭代器来获取Map里面的值的时候,我们会得到根据键的hash排列结果,而一个int的整数的hash值为自身,所以键值会从小到大排列,置于为什么输出的结果是[Out1,Out2,Out3,Out4],可以去查看List的toString方法

下面这个代码有什么问题
public static void findCodeError(){
        float pi = 3.14,double r;
        int i = 0;
        List<double> all = null;
        while (i < 3){
            System.out.println("输入圆的半径:");
            Scanner scanner = new Scanner(System.in);
            r = scanner.nextDouble();
            double S = pi * r * r;
            all.put(S);
        }
        
        Collections.sort(all, new Comparator() {
            public int compare(Double a, Double b) {
                return (int)a - b;
            }
        });

        System.out.println("面积从大到小依次为:");
        for (double one: all
             ) {
            System.out.println(one);
        }
    }

问题

1. 3.14默认为double型的,float不能初始化为一个double,要么强制转化,要么写为`float pi = 3.14f`
2. double型和float变量的定义不能用一个‘,’进行分隔
3. List的泛型不能是基本类型,只能是类,可以改为List<Double>
4. 循环是一个死循环,无法跳出
5. List对象没有初始化就赋值,而且赋值的方式不对,应该为list.add(S)
6. sort方法里面的Comparator内部类实现没有用泛型,也没有指定具体的泛型
7. compare的返回有问题,可以改为 return a > b? 1:-1;
8. 面积从大到小错误,sort是从小到大的,当然这取决于compare的具体实现

算法

谈一下

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

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,015评论 11 349
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,295评论 18 399
  • 作者: Desmond Chen, 发布日期: 2014-10-08,修改日期: 2014-10-09 本篇博文主...
    Vincent_He阅读 1,806评论 2 14
  • 那一袭流淌而下的素丽 在婀娜摆柳的漫步中 拖曳出一抹皎洁的月色 那一瀑批洒而下的青丝 在盈盈一握的柳腰间 飘荡起一...
    茗香酒影阅读 187评论 1 2