面试Java基础面试专题

不是简单的Java面试知识点汇总

2019-03-01  本文已影响125人  im青禾

面向对象的三大特征

面向对象和面向过程的区别

面向过程:
面向对象:

Java代码的运行

源代码->编译器将源代码编译为字节码->JVM(Java虚拟机) 解释器将字节码解释为可执行的二进制机器码->程序运行

JDK与JRE

JDK是Sun Microsystems针对Java开发人员发布的免费软件开发工具包(SDK,Software development kit)。除JRE(Java Runtime Environment),也就是Java运行环境外还包含提供给开发者使用的javac(编译器)、jar(打包器)、javadoc(文档生成器)等工具包。

重载(overloading)与重写(overriding)

equals与==的区别

1.对于字符串变量来说,使用“==”和“equals()”方法比较字符串时,其比较方法不同。“==”比较两个变量本身的值,即两个对象在内存中的首地址。“equals()”比较字符串中所包含的内容是否相同。
2.对于非字符串变量来说,"=="和"equals"方法的作用是相同的都是用来比较其对象在堆内存的首地址,即用来比较两个引用变量是否指向同一个对象。

String、StringBuff、StringBuild的区别

类成员访问修饰符

访问修饰符 同一个类 同包 不同包,子类 不同包,非子类
private
protected
public
默认

”static”关键字是什么意思?Java中是否可以覆盖(override)一个static方法?

“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

Java语言支持的8中基本数据类型对应的长度、对应的包装类

基本数据类型 长度(字节) 包装类 默认值
boolean 1 Boolean false
byte 1 Byte (byte)0
char 2 Character '/uoooo'(null)
short 2 Short (short)0
int 4 Integer 0
long 8 Long 0L
float 4 Float 0.0f
double 8 Double 0.0d

接口和抽象类的区别

final、finally、finalize

native方法是什么?

native方法是非Java代码实现的方法。

如何原地交换两个变量的值?

int a = 5;
int b = 10;
a = a + b;
b = a - b;
a = a - b;
同理可用乘除法。

用最有效率的方法计算 2 乘以 8

2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。

集合框架中的泛型有什么优点?

Java1.5 引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型。因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现 ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和 instanceOf 操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。

Java 集合框架的基础接口有哪些?

Iterator 和 ListIterator 的区别是什么?

Java 中的 HashMap 的工作原理是什么?

HashMap数据结构

简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

HashMap 和 HashTable 有什么区别?

ConcurrentHashMap的并发度是什么?

ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,ConcurrentHashMap的并发度就是segment的大小,默认值为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这样在多线程情况下就能避免争用。

快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

值传递与引用传递

  1. 值传递
    对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。我们可以来看下面的一个例子:
        public class Break {
            public static void change(int a) {
                a = 1;
            }
            public static void main(String[] args) {
                int a = 2;
                System.out.println(a);
                change(a);
                System.out.println(a);
            }
        }

输出结果是: 2 2
这个只是传递一份拷贝,和a的值没有什么关系,也可以看成是方法change的值没有一个变量来接收。
2.引用传递
对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。

        public class Break {
            public static void change(int[] a) {
                a[0] = 3;
            }
            public static void main(String[] args) {
                int[] a = {1, 2};
                System.out.println(a[0]);
                change(a);
                System.out.println(a[0]);
            }
        }

输出结果是: 1 3
这个传递的,就是实际传递的是引用的地址值。所以a[0]的值会改变。

什么是线程?线程和进程区别在哪?

Java中用到的线程调度算法是什么?

抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。

如何实现多线程?

java.lang.Thread 类的实例就是一个线程,但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口,所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。

用Thread还是用Runnable?

大家都知道我们可以通过继承Thread类或者调用Runnable接口来实现线程,问题是,那个方法更好呢?什么情况下使用它?这个问题很容易回答,如果你知道Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口好了。

Thread 类中的start() 和 run() 方法有什么区别?

这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

Runnable和Callable有什么不同?

使用ExecutorService、Callable、Future可以实现有返回结果的多线程。
Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。

常用线程池

  1. newCachedThreadPool创建一个可缓存线程池程
  2. newFixedThreadPool 创建一个定长线程池
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
  4. newSingleThreadExecutor 创建一个单线程化的线程池


    线程池工作流程图.png

sleep和wait的区别

如何强制启动一个线程?

这个问题就像是如何强制进行Java垃圾回收,目前还没有可靠方法,虽然你可以使用System.gc()来进行垃圾回收,但是不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。

volatile关键字的作用是什么?

什么是乐观锁和悲观锁?

  1. 乐观锁:对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-设置这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
  2. 悲观锁:对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,直接对操作资源上了锁。

共享锁和排它锁

select * from table lock in share mode
select * from table for update

可重入锁、可中断锁、公平锁、读写锁

  1. 可重入锁实际是指锁的分配机制:基于线程的分配,而不是基于方法调用的分配。synchronized和Lock都具备可重入性。
  2. 可中断锁,顾名思义,就是可以相应中断的锁。在Java中,synchronized就不是可中断锁,而Lock是可中断锁。
  3. 公平锁即尽量以请求锁的顺序来获取锁。在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。
  4. 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。

表锁、页锁、行锁

在 Mysql 中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql 语句操作了主键索引,Mysql 就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
InnoDB 行锁是通过给索引项加锁实现的,如果没有索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到某一条记录就得扫描全表,要扫描全表,就得锁定表。

synchronized与lock的区别

public interface Lock {
    void lock(); //用来获取锁。如果锁已被其他线程获取,则进行等待。
    void lockInterruptibly() throws InterruptedException;//当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
    boolean tryLock();//方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
    void unlock();//解锁
    Condition newCondition();
}
  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  2. synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
  3. Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
  4. 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
  5. Lock可以提高多个线程进行读操作的效率。

在静态方法和非静态方法上加 Synchronized的区别

jdk1.8新特性

常见算法

  1. 二分查找
            public static int binSearch(int srcArray[], int key) {
                int mid = srcArray.length / 2;
                if (key == srcArray[mid]) {
                    return mid;
                }
                int start = 0;
                int end = srcArray.length - 1;
                while (start <= end) {
                    mid = (end - start) / 2 + start;
                    if (key < srcArray[mid]) {
                        end = mid - 1;
                    } else if (key > srcArray[mid]) {
                        start = mid + 1;
                    } else {
                        return mid;
                    }
                }
                return -1;
            }
  1. 冒泡排序
    public static void bubbleSort(int srcArray[]) {
    for (int i = 0; i < srcArray.length - 1; i++) {//外层循环控制排序趟数
      for (int j = 0; j < srcArray.length - 1 - i; j++) {//内层循环控制每一趟排序多少次
        if (srcArray[j] > srcArray[j + 1]) {
          int temp = srcArray[j];
          srcArray[j] = srcArray[j + 1];
          srcArray[j + 1] = temp;
        }
      }
    }
    }
  1. 快速排序
public static void quickSort(int[] arr, int low, int high) {
                int i, j, temp, t;
                if (low > high) {
                    return;
                }
                i = low;
                j = high;
                //temp就是基准位
                temp = arr[low];

                while (i < j) {
                    //先看右边,依次往左递减
                    while (temp <= arr[j] && i < j) {
                        j--;
                    }
                    //再看左边,依次往右递增
                    while (temp >= arr[i] && i < j) {
                        i++;
                    }
                    //如果满足条件则交换
                    if (i < j) {
                        t = arr[j];
                        arr[j] = arr[i];
                        arr[i] = t;
                    }

                }
                //最后将基准为与i和j相等位置的数字交换
                arr[low] = arr[i];
                arr[i] = temp;
                //递归调用左半数组
                quickSort(arr, low, j - 1);
                //递归调用右半数组
                quickSort(arr, j + 1, high);
            }

常见数据结构

数据结构的物理存储结构只有两种:顺序存储结构和链式存储结构(像栈,队列,树,图等是从逻辑结构去抽象的,映射到内存中,也这两种物理组织形式)。

  1. 线性表
    1. 数组
      采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查找等方式,可将查找复杂度提高为O(logn);对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度也为O(n)。
    2. 链表
      对于链表的新增,删除等操作(在找到指定操作位置后),仅需处理结点间的引用即可,时间复杂度为O(1),而查找操作需要遍历链表逐一进行比对,复杂度为O(n)。
  2. 栈与队列
  3. 树与二叉树
    1. 二叉树基本概念
    2. 二叉查找树
    3. 平衡二叉树
    4. 红黑树
      对一棵相对平衡的有序二叉树,对其进行插入,查找,删除等操作,平均复杂度均为O(logn)。
  4. Hash表
    在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下,仅需一次定位即可完成,时间复杂度为O(1)。
    哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法。

常用设计模式

  1. 单例模式
    单例设计模式简单说就是无论程序如何运行,采用单例设计模式的类(Singleton类)永远只会有一个实例化对象产生。
        public class Singleton {
            private Singleton() {
            }

            private static class SingletonHolder {
                private final static Singleton instance = new Singleton();
            }

            public static Singleton getInstance() {
                return SingletonHolder.instance;
            }
        }
  1. 工厂模式
    程序在接口和子类之间加入了一个过渡端,通过此过渡端可以动态取得实现了共同接口的子类实例化对象。
  2. 代理模式
    指由一个代理主题来操作真实主题,真实主题执行具体的业务操作,而代理主题负责其他相关业务的处理。比如生活中的通过代理访问网络,客户通过网络代理连接网络(具体业务),由代理服务器完成用户权限和访问限制等与上网相关的其他操作(相关业务)。

对称加密与非对称加密

何谓RESTful?

RESTful(Representational State Transfer)架构风格,是一个Web自身的架构风格,底层主要基于HTTP协议(ps:提出者就是HTTP协议的作者),是分布式应用架构的伟大实践理论。RESTful架构是无状态的,表现为请求-响应的形式,有别于基于Bower的SessionId不同。

何谓MVC?

SpringMVC工作流程

SpringMVC工作流程图.jpg
  1. 客户端请求提交到DispatcherServlet
  2. 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
  3. DispatcherServlet将请求提交到Controller
  4. Controller调用业务逻辑处理后,返回ModelAndView
  5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
  6. 视图负责将结果显示到客户端

strus2与Spring MVC的区别

  1. Struts2是类级别上的拦截,一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文。而且Struts过滤后是去Struts配置文件中找Action,而SpringMVC过滤后是去controller中找对应于@RequestMapping注解的url绑定的方法。
  2. 因为拦截器原因,导致Struts2的action比较乱,因为它要定义属性来获取请求中参数的数据,而属性在一个类的方法间是共享的(方法间不能独享request、response数据),所以会有点乱。而SpringMVC中请求参数与controller中方法的形参自动配对(在名字相同,或请求参数与形参的属性名相同,或通过@RequestParam注解指定条件下会自动将请求参数的值赋给形参)方法间可以独享request、response数据。
  3. SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。

SpringMVC常用参数绑定注解

  1. @RequestParam
  2. @RequestBody
  3. @RequestHeader
  4. @CookieValue
  5. @PathVariable

SpringMVC怎样设定重定向和转发的?

  1. 在返回值前面加"forward:"就可以让结果转发,譬如"forward:user.do?name=jianshu"
  2. 在返回值前面加"redirect:"就可以让返回值重定向,譬如"redirect:http://www.baidu.com"

SpringIOC容器

Spring IOC负责创建对象、管理对象(通过依赖注入)、整合对象、配置对象以及管理这些对象的生命周期,在Spring中BeanFactory是IOC容器的实际代表者。

BeanFactory 接口和 ApplicationContext 接口有什么区别 ?

  1. ApplicationContext 接口继承BeanFactory接口,Spring核心工厂是BeanFactory ,BeanFactory采取延迟加载,第一次getBean时才会初始化Bean, ApplicationContext是会在加载配置文件时初始化Bean。
  2. ApplicationContext是对BeanFactory扩展,它可以进行国际化处理、事件传递和bean自动装配以及各种不同应用层的Context实现。
    开发中基本都在使用ApplicationContext, web项目使用WebApplicationContext ,很少用到BeanFactory。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
IHelloService helloService = (IHelloService) beanFactory.getBean("helloService");
helloService.sayHello();

IOC、DI

依赖注入的几种方式

  1. set注入
    控制层代码:
private OrderServiceImp orderService;
    
public void setOrderService(OrderServiceImp orderService) {
       this.orderService = orderService;
}

Spring配置XML文件:其中配置声明OrderAction类存在属性orderService。程序运行时候,会将已经实例化的orderService对象调用setOrderService方式注入。

<bean name="orderAction" class="com.pec.action.OrderAction">
        <property name="orderService" ref="orderService"></property>
</bean>
<bean name="orderService" class="com.pec.service.imp.OrderServiceImp"></bean>
  1. 构造器注入
    控制层代码:
private OrderServiceImp orderService;
    
public OrderAction(OrderServiceImp orderService) {
        this.orderService = orderService;
    }

Spring配置XML文件:

<bean name="orderAction" class="com.pec.action.OrderAction">
      <constructor-arg ref="orderService"></constructor-arg>
</bean>
<bean name="orderService" class="com.pec.service.imp.OrderServiceImp"></bean>
  1. 注解注入

Spring中bean实例化有几种方式?

  1. 使用类构造器实例化(默认无参数)
    <bean id="bean1" class="cn.jianshu.Bean1"></bean>
  2. 静态工厂
    <bean id="bean2" class="cn.jianshu.Bean2Factory" factory-method="getBean2"></bean>
  3. 实例工厂
    <bean id="bean3Factory" class="cn.jianshu.Bean3Factory"></bean>
    <bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>

简单说下Bean的生命周期

  1. bean定义
  2. bean初始化
    有两种方式初始化:
    1. 在配置文件中通过指定init-method属性来完成
    2. 实现org.springframwork.beans.factory.InitializingBean接口
  3. bean调用
    三种方式获得bean实例(见上题)
  4. bean销毁
    有两种方式销毁:
    1. 使用配置文件指定的destroy-method属性
    2. 实现org.springframwork.bean.factory.DisposeableBean接口
      **注意:
      在配置 <bean> 元素,通过 init-method 指定Bean的初始化方法,通过 destroy-method 指定Bean销毁方法
      <beanid="lifeCycleBean"class="cn.jianshu.LifeCycleBean"init-method="setup"destroy-method="teardown"></bean>
    • destroy-method 只对 scope="singleton" 有效
    • 销毁方法,必须关闭ApplicationContext对象(手动调用),才会被调用
      ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      applicationContext.close();**

Bean的作用域

AOP的理解

Spring里面的applicationContext.xml文件能不能改成其他名字?

ContextLoaderListener是一个ServletContextListener, 它在你的web应用启动的时候初始化。缺省情况下, 它会在WEB-INF/applicationContext.xml文件找Spring的配置。 你可以通过定义一个<context-param>元素名字为”contextConfigLocation”来改变Spring配置文件的 位置。示例如下:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/jianshu.xml</param-value>
</context-param>
</listener-class>
</listener>

Spring如何处理线程并发问题?

Spring使用ThreadLocal解决线程安全问题
我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

如何解决GET、POST请求中文乱码问题?

GET请求中文乱码问题解决
String name= new String(request.getParamter("name").getBytes("ISO8859-1"),"utf-8")
<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="utf-8"/>
POST请求中文乱码问题解决
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

过滤器、监听器、拦截器

过滤器

所谓过滤器顾名思义是用来过滤的,Java的过滤器能够为我们提供系统级别的过滤,也就是说,能过滤所有的web请求,这一点,是拦截器无法做到的。在Java Web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或
者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。filter 流程是线性的,url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收。

监听器

Java的监听器,也是系统级别的监听。监听器随web应用的启动而启动。Java的监听器在c/s模式里面经常用到,它会对特定的事件产生产生一个处理。监听在很多模式下用到,比如说观察者模式,就是一个使用监听器来实现的,在比如统计网站的在线人数。又比如struts2可以用监听来启动。Servlet监听器用于监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。

拦截器

java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性。Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现的动态代理。它依赖于具体的接口,在运行期间动态生成字节码。拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或者之后加入某些操作。java的拦截器主要是用在插件上,扩展件上比如Hibernate Spring Struts2等,有点类似面向切片的技术,在用之前先要在配置文件即xml,文件里声明一段的那个东西。

拦截器和过滤器的区别

  1. 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
  2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  6. 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
  7. 拦截器需要在Spring配置文件中配置,过滤器只需要在web.xml中配置

为什么要有事物传播行为?

为什么要有事物传播行为图.png

spring管理事务有几种方式?

  1. 编程式事务,在代码中硬编码。(不推荐使用)
  2. 声明式事务,在配置文件中配置(推荐使用)
    声明式事务又分为两种:
    1. 基于XML的声明式事务
    2. 基于注解的声明式事务

事务

事务图.png

'#{}'和'${}'的区别是什么?

'#{}'是预编译处理,{}是字符串替换。 Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值; Mybatis在处理{}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。

一对一、一对多关联查询

<mapper namespace="com.jianshu.userMapper">  
    <!--association  一对一关联查询 -->  
    <select id="getClass" parameterType="int" resultMap="ClassesResultMap">  
        select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}  
    </select>  
 
    <resultMap type="com.jianshu.Classes" id="ClassesResultMap">  
        <!-- 实体类的字段名和数据表的字段名映射 -->  
        <id property="id" column="c_id"/>  
        <result property="name" column="c_name"/>  
        <association property="teacher" javaType="com.jianshu.Teacher">  
            <id property="id" column="t_id"/>  
            <result property="name" column="t_name"/>  
        </association>  
    </resultMap>  

    <!--collection  一对多关联查询 -->  
    <select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">  
        select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}  
    </select>  
 
    <resultMap type="com.jianshu.Classes" id="ClassesResultMap2">  
        <id property="id" column="c_id"/>  
        <result property="name" column="c_name"/>  
        <association property="teacher" javaType="com.jianshu.Teacher">  
            <id property="id" column="t_id"/>  
            <result property="name" column="t_name"/>  
        </association>  
 
        <collection property="student" ofType="com.jianshu.Student">  
            <id property="id" column="s_id"/>  
            <result property="name" column="s_name"/>  
        </collection>  
    </resultMap>  
</mapper> 

MyBatis缓存

  1. 总配置文件开启二级缓存。
  2. 映射文件添加<cache>标签。
  3. 实体类实现序列化接口。

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

核心注解是启动类上的@SpringBootApplication
它由以下四个注解组成:

  1. @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
  2. @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能。
  3. @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
  4. @ComponentScan:Spring组件扫描。

SpringCloud五大组件

  1. 服务发现
    Netflix Eureka
  2. 客户端负载均衡
    Netflix Ribbon
  3. 断路器
    Netflix Hystri
  4. 服务网关
    Netflix Zuul
  5. 分布式配置
    Spring Cloud Config

dubbo支持的通信协议

  1. dubbo://
  2. rmi://
  3. hessian://
  4. http://
  5. webservice://
  6. thrift://
  7. memcached://
  8. redis://
    底层采用socket进行通信

CAP理论

任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

分布式环境下的session处理策略

  1. 粘性session
upstream mycluster{
    #这里添加的是上面启动好的两台Tomcat服务器
    ip_hash;#粘性Session
     server 192.168.22.229:8080 weight=1;
     server 192.168.22.230:8080 weight=1;
}
  1. 服务器session复制
  2. session共享机制
    1. 粘性session处理方式


    2. 非粘性session处理方式


  3. session持久化到数据库
  4. terracotta实现session复制


分布式事务

ByteTCC、LCN
阿里分布式事务框架GTS开源了一个免费社区版Fescar

FESCAR管理分布式事务的典型生命周期图.png

分布式锁

基于数据库做分布式锁
  1. 基于表主键唯一做分布式锁
  2. 基于表字段版本号做分布式锁
  3. 基于数据库排他锁做分布式锁
基于 Redis 做分布式锁
public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

}
public class RedisTool {

    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}
  1. 基于 REDIS 的 SETNX()、EXPIRE() 方法做分布式锁
  2. 基于 REDIS 的 SETNX()、GET()、GETSET()方法做分布式锁
  3. 基于 REDLOCK 做分布式锁
  4. 基于 REDISSON 做分布式锁
基于 ZooKeeper 做分布式锁
基于 Consul 做分布式锁

RabbitMQ

fanout
direct
topic
headers

MyISAM与InnoDB的区别

  1. 存储结构
  2. 存储空间
  3. 可移值性、备份、恢复
  4. 事务支持
  5. AUTO_INCREMENT
  6. 表锁差异
  7. 全文索引
  8. 表主键
  9. 表的具体行数
  10. CURD操作
  11. 外键
    MySQL默认采用的是MyISAM。

索引底层实现原理

  1. 索引的本质


  2. 二叉树


  3. B树



    4.B+树


  4. 带有顺序访问指针的B+Tree


SQL优化

避免全表扫描,改为索引扫描。

  1. 适当的索引。
  2. 尽量不要有空判断的语句,因为空判断将导致全表扫描,而不是索引扫描。尽量不要有空判断的语句,因为空判断将导致全表扫描,而不是索引扫描。 对于空判断这种情况,可以考虑对这个列创建数据库默认值。
  3. 尽量不要使用不等于条件,因为,这会导致全表扫描,对于不等于这种情况,考虑改为范围查询解决。
  4. 尽量不要使用or条件,因为,这会导致全表扫描,对于or这种情况,可以改为 分别查询,然后 union all。
  5. 尽量不要使用左右模糊查询,因为,这会导致全表扫描, 对于左右模糊查询的情况,试着改为右侧模糊查询,这样是可以索引查找的。
  6. 尽量不要在执行算数运算后的比较,因为,函数、算术运算或其他表达式运算通常将导致全表扫描,对于这种情况,可以考虑冗余部分数据到表中。
  7. 尽量使用exists代替in。
  8. 尽量避免一次性返回大数据量,可以考虑分页返回。

union与union all

  1. union :得到两个查询结果的并集,并且自动去掉重复行。不会排序。
  2. union all:得到两个查询结果的并集,不会去掉重复行。也不会排序。

Oracle与MySQL分页查询的写法

  1. Oracle
        SELECT * FROM
                (
                        SELECT A. *, ROWNUM RN
                        FROM(SELECT * FROM TABLE_NAME)A
                        WHERE ROWNUM <= 40
                )
        WHERE RN >=21

2.MySQL

SELECT * FROM TABLE LIMIT 5, 10;

SQL小贴士

SELECT NOW(),CURDATE(),CURTIME() FROM DUAL

结果:

NOW() CURDATE() CURTIME()
2008-12-29 16:25:46 2008-12-29 16:25:46
mysql> SELECT DATEDIFF('2017-08-08','2017-08-17');
+-------------------------------------+
| DATEDIFF('2017-08-08','2017-08-17') |
+-------------------------------------+
|                                  -9 |
+-------------------------------------+
1 row in set

查看数据库引擎命令

show variables like '%storage_engine%';

Linux下查询所有tomcat进程命令

ps -ef|grep tomcat

上一篇下一篇

猜你喜欢

热点阅读