java面试题

用java构建二叉排序树,实现先序,中序和后序遍历

package cn.edu.ahui;

 

import org.omg.CORBA.PUBLIC_MEMBER;

 

//二叉树的数据结果

class BinaryTree{

int val;//根节点数据

BinaryTree left;//左子树

BinaryTree right;//右子树

public BinaryTree(int val){

this.val = val;

this.left = null;

this.right = null;

}

}

 

public class BinaryTreeBuilder {

//向二叉树中插入子树

public void insert(BinaryTree root, int data){

//二叉排序树的右节点都比根节点大

if(data > root.val){

if(root.right == null)

root.right = new BinaryTree(data);

else

this.insert(root.right,data);//递归插入子节点

}

//二叉排序树的左节点都比根节点小

else{

if(root.left == null)

root.left = new BinaryTree(data);

else

this.insert(root.left, data);//递归插入子节点

}

}

 

 

//先序遍历

public static void preOrder(BinaryTree root){

if(root != null){

System.out.print(root.val+"-");

preOrder(root.left);

preOrder(root.right);

}

}

 

//中序遍历

public static void inOrder(BinaryTree root){

if(root != null){

inOrder(root.left);

System.out.print(root.val+"-");

inOrder(root.right);

}

}

 

//后序遍历

public static void postOrder(BinaryTree root){

if(root != null){

postOrder(root.left);

postOrder(root.right);

System.out.print(root.val+"-");

}

}

 

public static void main(String[] args){

int[] a = {12,76,35,22,16,48,90,46,9,40};

BinaryTreeBuilder btb = new BinaryTreeBuilder();

BinaryTree root = new BinaryTree(a[0]);

for(int i = 1; i<a.length; i++){

btb.insert(root, a[i]);

}

System.out.print("先序遍历:");

preOrder(root);

System.out.println("");

System.out.print("中序遍历:");

inOrder(root);

System.out.println("");

System.out.print("后序遍历:");

postOrder(root);

}}

2.用Java编写100内的质数

public static void main(String[] args) {

  //如果要求200以内的质数 将100改为两百即可

     for (int i = 2; i < 100; i++) { boolean zhishu = true;

     //每个数除以它之前的数,是否能整出

    for (int j = 2; j < i - 1; j++) { if (i % j == 0) {

       zhishu = false; break;//可以省去,但是效率会下降 } }

        if (zhishu) {

     System.out.println(i); } } }


3.String,stringbuilder,Stringbuffer的区别

     String为字符串常量,而StringBuilder和StringBuffer均为字符串变量

  在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的

     String:适用于少量的字符串操作的情况

  StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

  StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

4.Struts2和springmvc的本质区别

1.springmvc入口是一个servlet前端控制器(DispatcherServlet),struts2入口是一filter过滤器(StrutsPrepareAndExecuteFilter).

2.struts2通过在action类中定义成员变量接收参数,(属性驱动和模型驱动),它只能.

使用多例模式管理action.

 springmvc通过在controller方法中定义形参接收参数,springmvc可以使用单例模式管理controller.      

 3.springmvc是基于方法开发的,注解开发中使用requestMapping将url和方法进行 映射,如果根据url找到controller类的方法生成一个handler处理器对象(只包括一个method).

struts2是基于类开发的,每个请求过来创建一个action实例,实例对象中有若干个方法.

开发中建议使用springmvc,springmvc方法更类似service业务方法.

 4.struts2采用值栈存储请求和相应的数据,通过OGNL存取数据,springmvc通过参数绑定期将request请求内容解析,并给方法形参赋值.

 5.struts2和springmvc的速度是相当的,由于struts2的漏洞较多,跟多企业使用springmvc

 

5.mybatis与hibernate的关系和区别?

  • 两者相同点

Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。

Hibernate和MyBatis都支持JDBC和JTA事务处理。

1.Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。

  2.Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。

3.   Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。

   4.Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

  5. MyBatis方便对sql语句的优化,适用于复杂语句较多的,性能比hibernate好

6. Hibernate是将数据库中的数据表映射为持久层的Java对象,对sql语句进行修改和优化比较困难;MyBatis是将sql语句中的输入参数和输出参数映射为java对象,sql修改和优化比较方便.

hibernate优点

  1,消除了代码的映射规则,它全部分离到了xml或者注解里面去配置。

    2,无需在管理数据库连接,它也配置到xml里面了。

    3,一个会话中不需要操作多个对象,只需要操作Session对象。

4,关闭资源只需要关闭一个Session便可。

5. hibernate拥有完整的日志系统, 

hibernate缺点

1,全表映射带来的不便,比如更新时需要发送所有的字段。

    2,无法根据不同的条件组装不同的SQL。

    3,对多表关联和复杂的sql查询支持较差,需要自己写sql,返回后,需要自己将数据封装为pojo。

    4,不能有效的支持存储过程。

    5,虽然有HQL,但是性能较差,大型互联网系统往往需要优化sql,而hibernate做不到。

mybatis优点

  1. sql进行优化,
  2. 通过配置决定你的sql映射规则,
  3. 也能支持存储过程

mybatis缺点

  1. 数据库移植性不好
  2. 本身提供的缓存机制不佳
  3. mybatis则欠缺一些
  4.  

6.UML是什么?写出3种UML图,在系统过程中分析其作用。

UML是一种建模语言,

状态图(State Diagram)用来描述一个特定对象在其生存周期或在某段时间内的所有可能的状态及其引起状态转移的事件。一个状态图包括一系列的状态以及状态之间的改变。例如订单的状态变化等,在实时系统中用得较多,还可以用于辅助设计用户界面。
顺序图(Sequence Diagram)清晰地描述一组对象之间动态的交互关系、时间的约束关系,着重描述对象间消息传递的时间顺序,所以顺序图在实时系统中被大量使用。
当参与交互的对象数目增加,交互关系复杂时用顺序图描述会显得杂乱,协作图(Collaboration Diagram)从另一个角度来更好地描述相互协作的对象间的交互关系和链接(Link)关系。着重体现交互对象间的静态链接关系和协作关系。协作图也可以从顺序图生成。
活动图(Activity Diagram)是由状态图变化而来的,从系统任务的观点来看,系统的执行过程是由一系列有序活动组成的。活动图可以有效地描述整个系统的流程,描述了系统的全局的动态行为,且只有活动图是唯一能够描述并发活动的UML图。

 

7.写出单例模式(两种方式),观察者模式

单例模式:

饿汉

public class A{

private static A a=new A();

public static A getInstance(){

return a;

}

}

懒汉:

public class A{

   private static A a=null;

   public static A getInstance(){

       if(a==null){

      a=new A();

      }

     return a;

    }

}

观察者模式:

首先理解一下观察者模式的定义:

      定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

(1)观察者对象User  一个接口类,必须实现update方法(获取更新消息)

/**
 * 微信用户是具体观察对象
 * @author xiazheng
 *
 */
package com.Observer;
 
import java.util.Observable;
import java.util.Observer;
 
public class User implements Observer{
    private String name;
    public User(String name){
        this.name = name;
    }
 
    @Override
    public void update(Observable o, Object arg) {
        // TODO Auto-generated method stub
         System.out.println("微信用户:"+name+",您的微信公众号更新这些内容:"+arg);
    }
    
}

(2)被观察者对象OfficialAccount ,需要重写一个消息通知方法(publishnewInfo)

/*
 * OfficialAccount是具体被观察的对象:
 *     当它更新时,所有监听该对象的观察者(微信用户)都会接收到通知
 * */
package com.Observer;
 
import java.util.Observable;
 
public class OfficialAccount extends Observable{
 
    /**
     * 
     * @param info  要给观察者的信息
     */
    public void publishNewInfo(String info){
 
        //标识这个Observable对象已经改变了,更具体来将就是把Observable中属性changed置为true.
        setChanged();
        //在通知所有观察者之前,需要判断Observable中属性changed是否为true,如若不为则不会发出通知。
        //具体可以看源码,蛮好理解的。
        notifyObservers(info);
    }
    
 
}

(3)测试主类

package com.Observer;
 
public class Test {
 
    public static void main(String[] args) {
 
        //被观察的角色
        OfficialAccount officialAccount = new OfficialAccount();
 
        //观察者
        User userBob = new User("WeChat User-Bob");
        User userTom = new User("WeChat User-Tom");
        User userMe = new User("WeChat User-Me");
 
        //将观察者注册到可观察对象的观察者列表中
        officialAccount.addObserver(userBob);
        officialAccount.addObserver(userTom);
        officialAccount.addObserver(userMe);
 
        //发布消息
        officialAccount.publishNewInfo("...新内容...");
        officialAccount.deleteObserver(userMe);
        System.out.println("##########################################");
        officialAccount.publishNewInfo("...新内容123...");
 
    }
 
}

八,剩下最后一件商品,被同时购买怎么办

1.设置事务,在保存库存后若库存<0,进行回滚,根据返回的库存数对客户进行提示是否购买成功,这是在spring中解决问题 。

2.UPDATE goods SET a = a-1 WHERE goodNo = 1 AND a>0。数据库中解决问题,商品>0时才会进行减操作,根据返回的库存数对客户进行提示是否购买成功。

3.设置同步方法,并且在每次进行减库存操作前查询是否库存数>0。
九,画出或描述从订单提交到支付的·的整个流程

java面试题

十,spring事务四大特性,几种隔离级别

原子性 (atomicity):强调事务的不可分割.
一致性 (consistency):事务的执行的前后数据的完整性保持一致.
隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰
持久性(durability) :事务一旦结束,数据就持久到数据库
隔离级别;

DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

读未提交(read uncommited) :脏读,不可重复读,虚读都有可能发生
读已提交 (read commited):避免脏读。但是不可重复读和虚读有可能发生
重复读(repeatable read) :避免脏读和不可重复读.但是虚读有可能发生.
串行化的 (serializable) :避免以上所有读问题. (最高隔离级别)

十一,tomcat 如何增加内存

修改 catalina.bat

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%
这行下面前面加上
JAVA_OPTS='-Xms512m -Xmx1024m'
JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=64M -XX:MaxPermSize=256m"

其中 JAVA_OPTS='-Xms512m -Xmx1024m' 是设置Tomcat使用的内存的大小.

-XX:PermSize=64M -XX:MaxPermSize=256m 指定类空间(用于加载类)的内存大小

十二,group by 与 having 的区别与用法

1. GROUP BY 是分组查询, 一般 GROUP BY 是和聚合函数配合使用

group by 有一个原则,就是 select 后面的所有列中,没有使用聚合函数的列,必须出现在 group by 后面(重要)

having 子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包含聚组函数,使用having 条件显示特定的组,也可以使用多个分组标准进行分组。

having 子句被限制子已经在SELECT语句中定义的列和聚合表达式上。通常,你需要通过在HAVING子句中重复聚合函数表达式来引用聚合值,就如你在SELECT语句中做的那样。(用于对where和group by查询出来的分组经行过滤)例如:

SELECT A COUNT(B) FROM TABLE GROUP BY A HAVING COUNT(B)>2

十三,查一张表如果查的慢,解决措施

1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 
2、I/O吞吐量小,形成了瓶颈效应。 
3、没有创建计算列导致查询不优化。 
4、内存不足 
5、网络速度慢 
6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量) 
7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷) 
8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。 
9、返回了不必要的行和列 
10、查询语句不好,没有优化 

十四,线程加锁的两种方式

方法1:目标访问方法加锁:

 public  synchronized void add(){
        j++;
    System.out.println(Thread.currentThread().getName()+":j++:"+j);
    }

方法2:类对象实例上锁:

 public void add(){

   synchronized (this) {

     j++;

  System.out.println(Thread.currentThread().getName()+":j++:"+j);

} }
十五,redis中的存储类型有哪些/

1string 2list链表操作 3Set集合类型 4Zset数据类型 5 Hash数据类型

十六,商城项目中那些地方用到了redis,为什么

1. 各种计数,商品维度计数和用户维度计数

说起电商,肯定离不开商品,而附带商品有各种计数(喜欢数,评论数,鉴定数,浏览数,etc),Redis的命令都是原子性的,你可以轻松地利用INCR,DECR等命令来计数。

2. 存储社交关系

譬如将用戶的好友/粉丝/关注,可以存在一个sorted set中,score可以是timestamp,这样求两个人的共同好友的操作,可能就只需要用求交集命令即可。

3. 用作缓存代替memcached(商品列表,评论列表,@提示列表,etc)

相对memcached 简单的key-value存储来说,redis众多的数据结构(list,set,sorted set,hash, etc)可以更方便cache各种业务数据,性能也不亚于memcached。

4. 反spam系统(评论,发布商品,论坛发贴,etc)

作为一个电商网站被各种spam攻击是少不免(垃圾评论、发布垃圾商品、广告、刷自家商品排名等),针对这些spam制定一系列anti-spam规则,其中有些规则可以利用redis做实时分析,譬如:1分钟评论不得超过2次、5分钟评论少于5次等(更多机制/规则需要结合drools )。 采用sorted set将最近一天用户操作记录起来(为什么不全部记录?节省memory,全部操作会记录到log,后续利用hadoop进行更全面分析统计),通过ZRANGEBYSCORE user:200000:operation:comment 61307510405600 +inf 获得1分钟内的操作记录, redis> ZADD user:200000:operation:comment 61307510402300 "这是一条评论" //score 为timestamp (integer) 1 redis> ZRANGEBYSCORE user:200000:operation:comment 61307510405600 +inf//获得1分钟内的操作记录 1) "这是一条评论"

5. 用户Timeline/Feeds

 有个类似微博的栏目我关注,里面包括关注的人、主题、品牌的动态。redis在这边主要当作cache使用。

6. 最新列表&排行榜(用户刚刚喜欢的商品,etc)

这里采用Redis的List数据结构或sorted set 结构, 方便实现最新列表or排行榜 等业务场景。

7. 消息通知

其实这业务场景也可以算在计数上,也是采用Hash

8. 将Redis用作消息队列

当在集群环境时候,java ConcurrentLinkedQueue 就无法满足我们需求,此时可以采用Redis的List数据结构实现分布式的消息队列。

十七,如何在删除主表记录时,一并删除从表相关联的记录

1.子表在创建外键的时候指定了ON DELETE CASCADE,则直接从主表中删除相关记录,子表中数据也会一起删除

2.如果是编程实现,java框架hibernate的cascade可以模拟做到删主去重

十八,post和get的区别,什么请求分别怎么使用。

1.GET和POST是HTTP请求的两种基本方法 GET把参数包含在URL中,POST通过request body传递参数。

2.GET请求在URL中传送的参数是有长度限制的,而POST没有。

3.对参数的数据类型,GET只接受ASCII字符,而POST没有限制。

4.GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

5.GET请求只能进行url编码,而POST支持多种编码方式。

6.GET在浏览器回退时是无害的,而POST会再次提交请求。

7.GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

十九,说出购物车的几种实现方式

三种:cookie  或是session中,或是结合 数据库存储

二十,什么是悲观锁和乐观锁

悲观锁(Pessimistic Lock)每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock),每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

二十一,用户线程和守护线程有什么区别?

用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护线程还在运行,则会马上结束。很好理解,守护线程是用来辅助用户线程的,如公司的保安和员工,各司其职,当员工都离开后,保安自然下班了。

二十二,springboot,springmvc和spring有什么区别?

spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc。

spring mvc 是只是spring 处理web层请求的一个模块。

因此他们的关系大概就是这样:

spring mvc < spring <springboot。

用最简练的语言概括就是:

Spring 是一个“引擎”;

Spring MVC 是基于Spring的一个 MVC 框架 ;

Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

二十三,说说springcloud微服务的架构过程。

java面试题

 

二十四,什么是控制反转,什么是依赖注入?依赖注入有几种方式?

   控制反转:用来减低计算机代码之间的耦合度,创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是你在xml文件控制,侧重于原理。
依赖注入:创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。

二十五,谈谈你对vue技术的了解

vue是构建用户界面的渐进式框架,与jquery相比,vue采用双向数据绑定的机制,简化了dom的操作(不用重写大量的  html标签)性能和体验也有一定的优化,让使用者更加专注于业务逻辑,使用起来也更加方便。

 二十六,拦截器和过滤器的区别?

①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
  ②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  ③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  ④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  ⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

  ⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

二十七,hibernate一级缓存和二级缓存

Hibernate中的一级缓存,也叫做session的缓存,它可以在session范围内减少数据库的访问次数,只在session范围内有效,session关闭,一级缓存失败;

Hibernate提供的缓存

有一级缓存、二级缓存。 目的是为了减少对数据库的访问次数,提升程序执行效率!

.二级缓存是SessionFactory级别的,二级缓存我们通常使用其他的一些开源组件,比如hibernate经常使用的就是ehcache,这个缓存在整个应用服务器中都会有效的。