java面试干货

一、mybatis和hibernate有什么不同

  首先无论是mybatis还是hibernate都是通过配置文件,将数据库的表pojo映射起来的。mybatis和hibernate都可以称为ORM(对象关系映射)框架,只是hibernate是完全面向pojo的,而mybatis则不是,hibernate基本不需要编写sql就可以通过映射关系来操作数据库,是一种全表映射的体现,而mybatis则不同,它需要我们提供sql去运行。由于hibernate无须sql,当多表关联超过三个的时候,通过hibernate的级联会造成太多性能的丢失,而且如果查询语句中的子段是根据特定的条件变化的,而hibernate就无法支持这样的变化。

  1.hibernate

    首先通过hibernate的映射文件,将数据库中的表和pojo映射起来,然后通过对pojo操作,从而影响数据库的表。有自己的hql语句,提高效率

  2.mybatis

    首先mybatis提供了使用Mapper的接口编程,只要一个接口和一个xml就能创建映射。mybatis可以自由的书写sql,支持动态的sql,处理列表,动态的生成表名,支持存储过程,这样就可以灵活的定义查询语句,满足各类需求和性能优化的需求,缺点是工作量稍微大于hibernate。

  

二、java中的设计模式 

  Java 中一般认为有23种设计模式 
  总体来说设计模式分为三大类:
    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

  1.单例者模式(懒汉式和饿汉式)

    所谓的单例设计指的是一个类只允许产生一个实例化对象。 

    饿汉式:构造方法私有化,先创建对象     

      class Singleton {
          /**
          * 在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象
          */
        private static Singleton instance = new Singleton();
          /**
          * private 声明构造
          */
        private Singleton() {

        }
          /**
          * 返回对象实例
          */
        public static Singleton getInstance() {
          return instance;
        }

      }

    懒汉式构造方法私有,当第一使用的时候才去创建对象,存在线程安全问题,所以用到了 synchronized 来实现线程的同步,

      class Singleton {

        /**
        * 声明变量
        */
        private static volatile Singleton singleton = null;

        /**
        * 私有构造方法
        */
        private Singleton() {

        }

        /**
        * 提供对外方法
        */
        public static Singleton getInstance() {
        // 还未实例化
          if (singleton == null) {
            synchronized (Singleton.class) {
              if (singleton == null) {
                singleton = new Singleton();
               }
            }
          }
          return singleton;
        }
      }

  2.工厂设计模式(工厂方法模式和抽象工厂模式)

     1.工厂方法模式      

      (1)普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

interface Sender {
void Send();
}

class AppleSender implements Sender {

@Override
public void Send() {
System.out.println("This is apple sender...");
}
}

class BananaSender implements Sender {

@Override
public void Send() {
System.out.println("This is banana sender...");
}
}

public class FactoryPattern {
public static void main(String[] args) {
Sender sender = produce("apple");
sender.Send();
}
public static Sender produce(String str) {
if ("apple".equals(str)) {
return new AppleSender();
} else if ("banana".equals(str)) {
return new BananaSender();
} else {
System.out.println("没有该类对象.");
return null;
}
}
}

      (2) 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

interface Sender {
void Send();
}

class AppleSender implements Sender {

@Override
public void Send() {
System.out.println("This is apple sender...");
}
}

class BananaSender implements Sender {

@Override
public void Send() {
System.out.println("This is banana sender...");
}
}

class SendFactory {
public Sender produceApple() {
return new AppleSender();
}

public Sender produceBanana() {
return new BananaSender();
}
}

public class FactoryPattern {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceApple();
sender.Send();
}
}      (3) 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

          就是在上边的类SendFactory中的produceApple()方法前加static,然后在public FactoryPattern中直接调用produceApple()即可

    2.抽象工厂模式     

      工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要扩展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,那么这就用到了抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

interface Provider {
Sender produce();
}

interface Sender {
void Send();
}

class AppleSender implements Sender {

@Override
public void Send() {
System.out.println("This is apple sender...");
}
}

class BananaSender implements Sender {

@Override
public void Send() {
System.out.println("This is banana sender...");
}
}

class SendAppleFactory implements Provider {

public Sender produce() {
return new AppleSender();
}
}

class SendBananaFactory implements Provider {

public Sender produce() {
return new BananaSender();
}
}


public class FactoryPattern {
public static void main(String[] args) {
Provider provider = new SendAppleFactory();
Sender sender = provider.produce();
sender.Send();
}
}

   3.建造者模式

    工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种类集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。

public class TicketHelper{

  public void buildAdult(String info){

    System.out.println("构建成年人票逻辑"+info);

  }

 public void buildChildren(String info){

    System.out.println("构建儿童票逻辑"+info);

  }

 public void buildElderly(String info){

    System.out.println("构建老年人票逻辑"+info);

  }

}

public class TicketBuilder{

  public static Object builder(TicketHelper helper){

    System.out.println("通过TicketHelper构建套票信息");

  }

  //测试

  public static void mian(String [] args){

    TicketHelper helper=new TicketHepler();

    helper.buildAdult("成人票");

    helper.buildChildren("儿童票");

    helper.buildElderly("老年票");

    Object ticket=TicketBuilder.builder(helper);

  }

}

  4.装饰者模式

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。装饰者模式就是再创一个类来加强主类的功能,这样的话就不用为每个子类创建子类来加强那个子类; 具体方法就是在新创的类中,声明一个主类,就像声明成员变量那样(主类名 变量名),然后重写新类的构造方法, 构造方法中将参数类型设置为主类的类型,然后写要加强的方法。调用时就直接调用新类并创建对象,然后调用新类的方法 就能实现加强的目的。 

 

interface Shape {
void draw();
}

/**
* 实现接口的实体类
*/
class Rectangle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Rectangle...");
}
}

class Circle implements Shape {

@Override
public void draw() {
System.out.println("Shape: Circle...");
}
}

/**
* 创建实现了 Shape 接口的抽象装饰类。
*/
abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;

public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}

@Override
public void draw() {
decoratedShape.draw();
}
}

/**
* 创建扩展自 ShapeDecorator 类的实体装饰类。
*/
class RedShapeDecorator extends ShapeDecorator {

public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}

@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}

private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color: Red");
}
}

  5.代理模式(静态代理和动态代理)

    代理模式指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理可以分为静态代理和动态代理。 
通过代理模式,可以利用代理对象为被代理对象添加额外的功能,以此来拓展被代理对象的功能。可以用于计算某个方法执行时间,在某个方法执行前后记录日志等操作。

    1. 静态代理

      静态代理需要我们写出代理类和被代理类,而且一个代理类和一个被代理类一一对应。代理类和被代理类需要实现同一个接口,通过聚合使得代理对象中有被代理对象的引用,以此实现代理对象控制被代理对象的目的。

/**
* 代理类和被代理类共同实现的接口
*/
interface IService {

void service();
}


/**
* 被代理类
*/
class Service implements IService{

@Override
public void service() {
System.out.println("被代理对象执行相关操作");
}
}

/**
* 代理类
*/
class ProxyService implements IService{
/**
* 持有被代理对象的引用
*/
private IService service;

/**
* 默认代理Service类
*/
public ProxyService() {
this.service = new Service();
}

/**
* 也可以代理实现相同接口的其他类
* @param service
*/
public ProxyService(IService service) {
this.service = service;
}

@Override
public void service() {
System.out.println("开始执行service()方法");
service.service();
System.out.println("service()方法执行完毕");
}
}


//测试类
public class ProxyPattern {

public static void main(String[] args) {
IService service = new Service();
//传入被代理类的对象
ProxyService proxyService = new ProxyService(service);
proxyService.service();
}
}

    2. 动态代理(jdk动态代理和cglib动态代理)一下使用的jdk动态代理和cglb动态代理的区别就是 jdk动态代理需要一个接口
      JDK 1.3 之后,Java通过java.lang.reflect包中的三个类Proxy、InvocationHandler、Method来支持动态代理。动态代理常用于有若干个被代理的对象,且为每个被代理对象添加的功能是相同的(例如在每个方法运行前后记录日志)。

      动态代理的代理类不需要我们编写,由Java自动产生代理类源代码并进行编译最后生成代理对象。
      创建动态代理对象的步骤:
        1. 指明一系列的接口来创建一个代理对象
        2. 创建一个调用处理器(InvocationHandler)对象
        3. 将这个代理指定为某个其他对象的代理对象
        4. 在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要的操作

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* 代理类和被代理类共同实现的接口
*/
interface IService {
void service();
}

class Service implements IService{

@Override
public void service() {
System.out.println("被代理对象执行相关操作");
}
}

class ServiceInvocationHandler implements InvocationHandler {

/**
* 被代理的对象

*代理对象和被代理对象之间的依赖关系
*/
private Object srcObject;

public ServiceInvocationHandler(Object srcObject) {
this.srcObject = srcObject;
}

/**

*代理逻辑

*/

@Override
public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
System.out.println("开始执行"+method.getName()+"方法");
//执行原对象的相关操作,容易忘记
Object returnObj = method.invoke(srcObject,args);
System.out.println(method.getName()+"方法执行完毕");
return returnObj;
}
}

public class ProxyPattern {
public static void main(String[] args) {
IService service = new Service();
Class<? extends IService> clazz = service.getClass();

IService proxyService = (IService) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(), new ServiceInvocationHandler(service));
proxyService.service();
}
}

三、mybatis执行流程和底层原理

  mybatis执行流程

    1.在lib下导入java包

    2.编写数据库中的表对应的pojo

    3.编写mybatis和核心配置文件(加载mapper映射文件)

    4.编写接口和对应的mapper映射文件(映射文件中的nameSpace是对应的接口的权限定名,查询语句中的id是接口中的方法名,parameterType是接口中方法的参数类型,resultType是接口中的方法的返回值,也可以是resultMap编写的pojo中的成员变量和数据库表字段的对应关系)

    5.然后通过核心配置文件中的mapper和mapper映射文件实现映射器,主要pojo属性和表字段对应,就可以自动映射。

    1.在查询语句中可以使用注解传递多个参数 @param  如果数据过多的话  使用pojo

    2.#和$  如果表名是变化的则必须使用$,order  by 后必须使用$

    3.一对一的级联 在mapper映射文件中 通过association 元素代表一对一级联的开始,property代表映射到的pojo属性,column代表sql列,select配置对应的命名空间,这样便可以指出对应mapper的sql,mybatis就会通过对应的sql将数据查询回来

      <asscoiation property="task" column="task_id"  select="task的mapper的接口的全路径名称下的要查询的那个方法"  /asscoiation>

      一对多的级联 在mapper映射文件中 通过collection配置,并且在pojo中 一的一方配置多的一方的组合用集合的方式,property代表一的一方中组合的属性名,

    4.延迟加载  lazyLoadingEnabled默认是false,和 aggressiveLazyLoading 默认false

    5.动态sql,choose,when,otherwise, trim, where,set,foreach

  底层原理

    通过动态代理实现mapper自动映射

四、Spring (spring IOC 和 Spring AOP)

    spring依赖注入的三种方式:1.构造器注入   2.setter注入    3.接口注入  

    spring装配bean的三种方式:1.在xml中显示配置    2.在java接口和类中实现配置     3.隐式Bean的发现机制和自动装配原则

      使用xml装配bean,只需要在配置文件中编写 bean标签,里边配置property  的name 和 value

      通过注解装配bean,@Component,代表 spring ioc会把这个类扫描成bean实例,这样spring ioc 并不知道去哪里扫描,所以就有@ComponentScan   代表全表扫描,默认是当前包的路径。   -----这个一般不使用

        @Autowired(required="false|true")默认是true就是必须注入成功, 自动装配,如果一个接口下不只有一个实现类的话,使用在动装配存在歧义,所以使用@Primary和@Qualifier,注解@Primary写在具体的实现类上,代表这个类优先注入,但是这并不能解决实际问题,可以 使用@Qualifier(“在具体的实现类中起的别名”)写在@Autowired的下边

      Spring IOC 底层采用是反射,IOC的设计是基于BeanFactory

原文地址:https://www.cnblogs.com/xp0813/p/11310932.html