JAVA面试宝典-3

2021/12/19 22:19:26

本文主要是介绍JAVA面试宝典-3,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、基础

Java 多态的优缺点分别是什么?
多态的优点:

  • 提高了代码的维护性(继承保证);
  • 提高了代码的扩展性(由多态保证);

多态的缺点:

  • 不能使用子类的特有功能(非要使用只能通过不优雅的创建子类对象方式,但是占用内存,其次就是使用强转类型,也容易出现问题);
  • 向下转型(把父类转换为子类型)中有可能会出现异常;

Java 常见的内部类有哪几种,简单说说其特征?
静态内部类、成员内部类、方法内部类(局部内部类)、匿名内部类。

  • 静态内部类是定义在另一个类里面用 static 修饰 class 的类,静态内部类不需要依赖于外部类(与类的静态成员属性类似)且无法使用其外部类的非 static 属性或方法,(因为在没有外部类对象的情况下可以直接创建静态内部类的对象,如果允许访问外部类的非 static 属性或者方法就会产生矛盾)
public class Boxer1{ 
	Integer i; 
	int x; 
	public Boxer1(int y) { 
		x = i+y; 
		System.out.println(x); 
	} 
	
	public static void main(String[] args) { 
		new Boxer1(new Integer(4)); 
	} 
} 
  • 成员内部类是没有用 static 修饰且定义在在外部类类体中的类,是最普通的内部类
    可以看做是外部类 的成员,可以无条件访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员),而外部类无法直接访问成员内部类的成员和属性;要想访问必须得先创建一个成员内部类的对象然后通过指向这个对象的引用来访问,当成员内部类拥有和外部类同名的成员变量或者方法时会发生隐藏现象(即默认情况下访问的是成员内部类的成员,如果要访问外部类的同名成员需要通过 OutClass.this.XXX 形式访问),成员内部类的 class 前面可以有 private 等修饰符存在。

  • 方法内部类(局部内部类)是定义在一个方法里面的类,和成员内部类的区别在于方法内部类的访问仅限于方法内;方法内部类就像是方法里面的一个局部变量一样,所以其类 class 前面是不能有 public、protected、private、static 修饰符的,也不可以在此方法外对其实例化使用。

  • 匿名内部类是一种没有构造器的类(实质是继承类或实现接口的子类匿名对象),由于没有构造器所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调,匿名内部类在编译的时候由系统自动起名为 OutClass$1.class,一般匿名内部类用于继承其他类或实现接口且不需要增加额外方法的场景(只是对继承方法的实现或是重写);匿名内部类的 class 前面不能有 pravite 等修饰符和 static 修饰符;匿名内部类访问外部类的成员属性时外部类的成员属性需要添加 fifinal 修饰(1.8 开始可以不用)。

下面关于Java类的构造方法的说法中正确的是:()

A. 一个类至少有一个构造方法 
B. 构造方法的返回值类型必须是void 
C. 构造方法的可见性修饰符必须是public 
D. 构造方法必须显式的定义 
正确答案: A 

解析:

  • 构造方法是一种特殊的方法:它必须具备和所在类相同的名字;没有返回值类型,甚至连void也没有;
    构造方法是在创建一个对象使用new操作符时调用的,作用是初始化对象。
  • 造方法可以默认提供而不显式定义。
  • 简单的说就是,每个类至少有一个构造方法,即使没有写出来,即可以不用显示定义。她没有返回值,
    连void都没有,但是也可以使用private修饰构造方法。

Java 构造方法能否被重写和重载?
重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父 类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行,所以 Java 的构造方法是不能 被重写的。而重载是针对同一个的,所以构造方法可以被重载。

下面那几个函数是public void method(){̷}的重载函数?

A.public void method( int m){̷} 
B.public int method(){̷} 
C.public void method2(){̷} 
D.public int method(int m,flfloat f ){̷} 
答案:A D 

解析:

  • 重载就是方法名一样(必须的),里面执行的内容不一样,但是呢,又出现一个问题,你写的两个或者多 个方法要能让JVM(JAVA 虚拟机)认识是唯一的,所以,这里就和返回的类型无关了,因为如果返回类型 不一样,其他都一样的话,那JVM是不知道到底是调用哪个方法的。所以方法名一样,入参的类型,个数,顺序(术语:方法的签名)只要有一个不同就是方法的重载了。

简单说说什么是二叉树?什么是完全二叉树?什么是满二叉 树?什么是排序二叉树?

  • 二叉树是一种从上往下分叉的一种数据结构,其每个节点最多有两个孩子节点,一左一右,左边的称为左孩子,右边的称为右孩子。
  • 完全二叉树是普通二叉树除最后一层外,在每一层上的结点数均达到最大值,在最后一层上只缺少右边若干结点的二叉树
  • 满二叉树是普通二叉树中的每个结点恰好有两个孩子结点且所有叶子结点都在同一层的二叉树
    若普通二叉树每个节点满足左子树所有节点值小于它的根节点值且右子树所有节点值大于它的根节点值,则这样的二叉树就是排序二叉树

面向对象的三大特征是什么?请详细介绍他们各自的部分原理?

  • 特征可以说有三种,继承、封装、多态,也可以说有四种,继承、封装、多态、抽象。
  • 继承性是类的一种层次模型,其提供了一种明确表述共性的方法,对象的新类可以从现有的类中继承派 生,类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需 要。
  • 封装性是把过程和数据包围起来,使得数据的访问只能通过已定义的接口,保证了对象被访问的隐私性 和可靠性。
  • 多态性是对象在不同时刻表现出来的多种状态,是一种编译时期状态和运行时期状态不一致的现象,多 态性包括参数化多态性和包含多态性。
    抽象性是指对一类事物的高度提炼以得到共同的共性部分,抽象不需要了解全部细节,而只是一种通用的描述约束,抽象可以是过程抽象或者数据抽象。

使用fifinal关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

  • 使用fifinal关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:
fifinalStringBuffffer a=new StringBuffffer("immutable"); 
执行如下语句将报告编译期错误: 
a=new StringBuffffer(""); 
但是,执行如下语句则可以通过编译: 
a.append(" broken!"); 
有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象: 
public void method(fifinal StringBuffffer param){} 
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象: 
param.append("a");

接口是否可继承接口?抽象类是否可实现(implements)接口?抽 象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?

  • 接口可以继承接口。
  • 抽象类可以实现(implements)接口,抽象类可以继承具体类。
  • 抽象类中可以有静态 的main方法。
  • 备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是java语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了。
    只要记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。

Java 中堆和栈有什么区别?

  • JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在 堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
  • 栈:在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。
  • 堆:堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。

request.getAttribute()和 request.getParameter()有何区别?

  • 1,request.getParameter()取得是通过容器的实现来取得通过类似post,get等方式传入的数据。
    request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段。
  • 2,getAttribute是返回对象,getParameter返回字符串
  • 3,getAttribute()一向是和setAttribute()一起使用的,只有先用setAttribute()设置之后,才能够通过 getAttribute()来获得值,它们传递的是Object类型的数据。而且必须在同一个request对象中使用才有效。,而getParameter()是接收表单的get或者post提交过来的参数

解释Spring支持的几种bean的作用域。
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的生命周期。

  • 1,Spring容器 从XML 文件中读取bean的定义,并实例化bean。
  • 2,Spring根据bean的定义填充所有的属性。
  • 3,如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
  • 4,如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。
  • 5,如果有任何与bean相关联的BeanPostProcessors,Spring会在
    postProcesserBeforeInitialization()方法内调用它们。
  • 6,如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
  • 7,如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。
  • 8,如果bean实现了 DisposableBean,它将调用destroy()方法。

解释不同方式的自动装配 。
有五种自动装配的方式,用来指导Spring容器用自动装配方式进行依赖注入。

  • no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。
  • byName:通过参数名 自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成
  • byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
  • byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。
  • constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
  • autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。

什么是线程死锁?死锁如何产生?如何避免线程死锁?
死锁的介绍:

  • 线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前 往执行。
  • 当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。
  • 当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

死锁的产生的一些特定条件:

  • 1、互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 。
  • 2、请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
  • 3、不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用。
  • 4、循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

如何避免:

  • 1、加锁顺序:
    当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程 都是按照相同的顺序获得锁,那么死锁就不会发生。当然这种方式需要你事先知道所有可能会用到的锁,然而总有些时候是无法预知的。
  • 2、加锁时限:
    加上一个超时时间,若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。但是如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。
  • 3、死锁检测:
    死锁检测即每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。
    除此之外,每当有线程请求锁,也需要记录在这个数据结构中。死锁检测是一个更好的死锁预防机制, 它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

什么是视图?
视图是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有一 个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。

如下两种场景一般会使用到视图:

  • 1)不希望访问者获取整个表的信息,只暴露部分字段给访问者,所以就建一个虚表,就是视图。
  • 2)查询的数据来源于不同的表,而查询者希望以统一的方式查询,这样也可以建立一个视图,
    把多个表查询结果联合起来,查询者只需要直接从视图中获取数据,不必考虑数据来源于不同表所
    带来的差异。
    注:这个视图是在数据库中创建的 而不是用代码创建的。

根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。

  • 1、Java虚拟机栈:
    线程私有;每个方法在执行的时候会创建一个栈帧,存储了局部变量表,操作数栈,动态连接,方法返回地址等;每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
  • 2、堆:
    线程共享;被所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例。
  • 3、方法区:
    线程共享;被所有线程共享的一块内存区域;用于存储已被虚拟机加载的类信息,常量,静态变量等。
  • 4、程序计数器:
    线程私有;是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。
  • 5、本地方法栈:
    线程私有;主要为虚拟机使用到的Native方法服务。

强引用,软引用和弱引用的区别

  • 强引用:
    只有这个引用被释放之后,对象才会被释放掉,只要引用存在,垃圾回收器永远不会回收,这是最常见的New出来的对象。
  • 软引用:
    内存溢出之前通过代码回收的引用。软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
  • 弱引用:
    第二次垃圾回收时回收的引用,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回 收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过 弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

数组在内存中如何分配

  • 1、简单的值类型的数组,每个数组成员是一个引用(指针),引用到栈上的空间(因为值类型变量的内存分配在栈上)
  • 2、引用类型,类类型的数组,每个数组成员仍是一个引用(指针),引用到堆上的空间(因为类的实例的内存分配在堆上)

说说http,https协议

  • HTTP:
    是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(
    TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
  • HTTPS:
    是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。

区别:

  • 1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  • 2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  • 3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • 4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

说说tcp/ip协议族
TCP/IP协议族是一个四层协议系统,自底而上分别是数据链路层、网络层、传输层和应用层。每一层完成不同的功能,且通过若干协议来实现,上层协议使用下层协议提供的服务。

  • 1、数据链路层负责帧数据的传递。
  • 2、网络层责数据怎样传递过去。
  • 3、传输层负责传输数据的控制(准确性、安全性)
  • 4、应用层负责数据的展示和获取。

tcp五层网络协议

  • 物理层:
    为数据端设备提供传送数据的通路,数据通路可以是一个物理媒体,也可以是多个物理媒体连接而成。
  • 数据链路层:
    为网络层提供数据传送服务。
  • 网络层:
    路由选择和中继、激活,终止网络连接、在一条数据链路上复用多条网络连接,多采取分时复用技术 、差错检测与恢复、排序,流量控制、服务选择、网络管理 。
  • 传输层:
    传输层是两台计算机经过网络进行数据通信时,第一个端到端的层次,具有缓冲作用。
  • 应用层:
    应用层向应用程序提供服务

TCP与UDP的区别

1、基于连接与无连接 
2、TCP要求系统资源较多,UDP较少; 
3、UDP程序结构较简单 
4、流模式(TCP)与数据报模式(UDP); 
5、TCP保证数据正确性,UDP可能丢包 
6、TCP保证数据顺序,UDP不保证 

分布式环境下的session(举例两种):

  • 服务器session复制
  • 原理:任何一个服务器上的session发生改变(增删改),该节点会把这个 session的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要session,以此来保证Session同步。
  • 优点:可容错,各个服务器间session能够实时响应。
  • 缺点:会对网络负荷造成一定压力,如果session量大的话可能会造成网络堵塞,拖慢服务器性能。
  • session共享机制
  • 使用分布式缓存方案比如memcached、redis,但是要求Memcached或Redis必须是集群。

GIT和SVN的区别

1、GIT是分布式的,SVN不是。 
2、GIT把内容按元数据方式存储,而SVN是按文件。 
3、GIT分支和SVN的分支不同。 
4、GIT没有一个全局的版本号,而SVN有。 
5、GIT的内容完整性要优于SVN。 
(一般问会不会用,知道这些区别貌似也没卵用)

BIO、NIO和AIO的区别

  • Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

  • Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

  • Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

  • NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间)

  • AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。

适用场景分析:

  • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应
    用中,JDK1.4以前的唯一选择,但程序直观简单易理解,如之前在Apache中使用。
  • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用
    中,编程比较复杂,JDK1.4开始支持,如在 Nginx,Netty中使用。
  • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与
    并发操作,编程比较复杂,JDK7开始支持,在成长中,Netty曾经使用过,后来放弃。

java中常说的堆和栈,分别是什么数据结构;另外,为 什么要分为堆和栈来存储数据

  • 栈是一种具有后进先出性质的数据结构,也就是说后存放的先取,先存放的后取。
  • 堆是一种经过排序的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。
  • 堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常 用来实现优先队列,堆的存取是随意的。

为什么要划分堆和栈

  • 1、从软件设计的角度,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。
  • 2、堆与栈的分离,使得堆中的内容可以被多个栈共享。一方面这种共享提供了一种有效的数据交
    互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。
  • 3、栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上
    增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因
    此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
  • 4、体现了Java面向对象这一核心特点(也可以继续说一些自己的理解)

为什么要用线程池
那先要明白什么是线程池
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。

使用线程池的好处

  • 1、线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,
    应用程序可以直接拿来使用而不用新建一个线程。
  • 2、线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收
    资源。
  • 3、线程池根据当前在系统中运行的进程来优化线程时间片。
  • 4、线程池允许我们开启多个任务而不用为每个线程设置属性。
  • 5、线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。
  • 6、线程池可以用来解决处理一个特定请求最大线程数量限制问题。

msyql优化经验

  • 1、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索
    引。
  • 2、应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
  • 3、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的
    性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而
    对于数字型而言只需要比较一次就够了。
  • 4、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
  • 5、避免频繁创建和删除临时表,以减少系统表资源的消耗。诸如此类,等等等等…

悲观锁和乐观锁的区别,怎么实现

  • 悲观锁:一段执行逻辑加上悲观锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待, 直到锁被释放。
  • 乐观锁:一段执行逻辑加上乐观锁,不同线程同时执行时,可以同时进入执行,在最后更新数据的时候要检查这些数据是否被其他线程修改了(版本和执行初是否相同),没有修改则进行更新,否则放弃本次操作。
悲观锁的实现: 
0.开始事务 
begin;/begin work;/start transaction; (三者选一就可以) 
//1.查询出商品信息 
select status from t_goods where id=1 for update; 
//2.根据商品信息生成订单 
insert into t_orders (id,goods_id) values (null,1); 
//3.修改商品status为2 
update t_goods set status=2; 
//4.提交事务 
commit;/commit work; 
乐观锁的实现 
1.查询出商品信息 
select (status,status,version) from t_goods where id=#{id} 
2.根据商品信息生成订单 
3.修改商品status为2 
update t_goods 
set status=2,version=version+1 
where id=#{id} and version=#{version}; 

GC何时开始:

  • 所有的回收器类型都是基于分代技术来实现的,那就必须要清楚对象按其生命周期是如何划分的。
  • 年轻代:划分为三个区域:原始区(Eden)和两个小的存活区(Survivor),两个存活区按功能分为From和 To。绝大多数的对象都在原始区分配,超过一个垃圾回收操作仍然存活的对象放到存活区。垃圾回收绝大部分发生在年轻代。
  • 年老代:存储年轻代中经过多个回收周期仍然存活的对象,对于一些大的内存分配,也可能直接分配到永久代。
  • 持久代:存储类、方法以及它们的描述信息,这里基本不产生垃圾回收。

有了以上这些铺垫之后开始回答GC何时开始:

  • Eden内存满了之后,开始Minor GC(从年轻代空间回收内存被称为 Minor GC);升到老年代的对象 所需空间大于老年代剩余空间时开始Full GC(但也可能小于剩余空间时,被HandlePromotionFailure 参数强制Full GC)

对什么东西操作,即垃圾回收的对象是什么:

  • 从root开始搜索没有可达对象,而且经过第一次标记、清理后,仍然没有复活的对象。

做了什么东西:

  • 主要做了清理对象,整理内存的工作。具体的引申如下
垃圾回收器的类型: 
串行垃圾回收器( Serial Garbage Collector ) 
并行垃圾回收器( Parallel Garbage Collector ) 
并发标记扫描垃圾回收器( CMS Garbage Collector ) 
G1垃圾回收器( G1 Garbage Collector ) 
垃圾回收算法: 
引用计数法 
标记清除法 
复制算法 
标记压缩算法 
分代算法 
分区算法 

类在虚拟机中的加载过程

  • 加载Loading:
    通过一个类的全限定名来获取一个二进制字节流、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
  • 验证Verifification:
    确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机的自身安全。
  • 准备Preparation:
    正式为类变量分配内存并设置类变量初始值。
  • 解析Resolution:
    虚拟机将常量池内的符号引用替换为直接引用的过程。
  • 初始化Initialization:
    类加载过程的最后一步,到了这个阶段才真正开始执行类中定义的Java程序代码。
  • 使用Using:
    根据你写的程序代码定义的行为执行。
  • 卸载Unloading:
    GC负责卸载,这部分一般不用讨论。

强引用、软引用、弱引用、虚引用与GC的关系

强引用:new出的对象之类的引用,只要强引用还在,永远不会回收。 
软引用:引用但非必须的对象,内存溢出异常之前回收。 
弱引用:非必须的对象,对象只能生存到下一次垃圾收集发生之前。 
虚引用:对生存时间无影响,在垃圾回收时得到通知。

面向对象的特征有哪些方面?

  • 抽象:
    抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。
    抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

  • 继承:
    继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类;得到继承信息的类
    被称为子类。
    继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。

  • 封装:
    通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对
    象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。
    我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封
    装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。

  • 多态:
    多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调
    用同样的方法但是做了不同的事情。
    多态性分为编译时的多态性和运行时的多态性。方法重载实现的是编译时的多态性,而方法重写实
    现的是运行时的多态性。

说一下你了解的几种进程间的通信方式

  • 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程
    间使用。进程的亲缘关系通常是指父子进程关系。
  • 高级管道popen:将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的
    子进程,这种方式我们成为高级管道方式。
  • 有名管道named pipe :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  • 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。
    消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由 一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信 方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
  • 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以
    及同一进程内不同线程之间的同步手段。
  • 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其
    间的进程通信。
  • 信号sinal: 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

下列叙述中错误的是( )

A.线性表是由n个元素组成的一个有限序列 
B.线性表是一种线性结构 
C.线性表的所有结点有且仅有一个前件和后件 
D.线性表可以是空表 

答案:C
线性表是一种线性结构,由n(n≥0)个元素组成,所以线性表可以是空表。但是在线性表中,第一个结点没有前驱,最后一个结点没有后继,其他结点有且只有一个前驱和后继,所以选项C是错误的。

已知一棵二叉树前序遍历和中序遍历分别为ABDEGCFH和DBGEACHF,则该二叉树的后序遍历为( )

A.GEDHFBCA 
B.DGEBHFCA 
C.ABCDEFGH 
D.ACBFEDHG 

答案:B
二叉树的遍历有3种:前序、中序和后序。
先序:先访问根结点、左结点、右结点
中序:先访问左结点、根结点、右结点
后序:先访问左结点、右结点、根结点
本题根据前序遍历和中序遍历的结果可以得出二叉树的结构,然后再对其进行后序遍历。

栈和队列的共同点是( )

A.都是先进先出 
B.都是先进后出 
C.只允许在端点处插入和删除元素 
D.没有共同特点 

答案:C
栈是只允许在表的一端进行插入和删除的操作,队列是允许在表的一端进行插入,另一端进行删除的操作。

下列关于修饰符混用的说法,错误的是()

A.abstract不能与fifinal并列修饰同一个类 
B.abstract类中不可以有private的成员 
C.abstract方法必须在abstract类中 
D.static方法中能处理非static的数据 

答案:D
静态方法是属于类的,而普通方法是属于对象的。
属于类的静态方法可以在对象不存在的时候就能访问到,而普通方法必须先new一个对象才能用这个对象访问。
当我们访问调用静态方法的时候(使用类名.静态方法名)这个时候就没有对象创建,所以普通方法是访问不到的。为了避免这种错误,所以java就不允许在静态方法中访问非静态方法。

Java能不能不通过构造函数创建对象()

A、能 B、不能 

答案:A
Java创建对象的几种方式:
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者java.lang.reflflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法。
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函
数 ;(4)是从文件中还原类的对象,也不会调用构造函数。

下列哪个叙述是正确的()

A.子类继承父类的构造方法。 
B.abstract类的子类必须是非abstract类。 
C.子类继承的方法只能操作子类继承和隐藏的成员变量。 
D.子类重写或新增的方法也能直接操作被子类隐藏的成员变量。 

答案:C
子类是不继承父类的构造方法的,而是必须调用其父类的构造方法。
abstract类的子类可以是abstract类,如果是非abstract类,就必须重写父类中所有的abstract方法。
D中子类新增的方法是不能直接操作被子类隐藏的成员变量的。

下列选项中,不属于模块间耦合的是( )

A.数据耦合 
B.标记耦合 
C.异构耦合 
D.公共耦合 

答案:C
模块之间的耦合程度反映了模块的独立性,也反映了系统分解后的复杂程度。 按照耦合程度从弱
到强,可以将其分成7级。
分别是非直接耦合、数据耦合、标记耦合、控制 耦合、外部耦合、公共耦合和内容耦合。其中没
有异构耦合这种方式。

下列说法正确的是()

A.一个文件里可以同时存在两个public修饰的类 
B.构造函数可以被重写(override) 
C.子类不能访问父类非public和protected修饰的属性 
D.fifinal修饰的类可以被继承 

答案:C
一个Java源文件中最多只能有一个public类,当有一个public类时,源文件名必须与之一致,否则 无法编译,如果源文件中没有一个public类,则文件名与类中没有一致性要求。至于main()不是必须要放在public类中才能运行程序。
重写是子类继承父类后对父类的方法进行修改。方法名,参数,返回值必须一样。 不能重写被标示
为fifinal的方法。如果不能继承一个方法,则不能重写这个方法。

扩展:重写override,重载overload的区别

  • java的方法重载
    就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通 过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法,而且返回值类型可以相同也可以不相

  • java的方法重写
    父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名 称和参数,我们说该方法被重写 (Overriding)。

  • 在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继 承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
    若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。

  • 如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。子类函数的访问修饰权 限不能少于父类的;

  • 重写方法只能存在于具有继承关系中,重写方法只能重写父类非私有的方法。

关于 JAVA 堆,下面说法错误的是( )

A.所有类的实例和数组都是在堆上分配内存的 
B.对象所占的堆内存是由自动内存管理系统回收 
C.堆内存由存活和死亡的对象,空闲碎片区组成 
D.数组是分配在栈中的 

答案:D
首先数组是分配在堆中的,故D的说法不正确。
Java堆的结构:JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的 时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。
堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。
一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。

在使用super 和this关键字时,以下描述正确的是()

A.在子类构造方法中使用super()显示调用父类的构造方法; 
super()必须写在子类构造方法的第一行,否则编译不通过 
B.super()和this()不一定要放在构造方法内第一行 
C.this()和super()可以同时出现在一个构造函数中 
D.this()和super()可以在static环境中使用,包括static方法和static语句块 

答案:A
Java关键字this只能用于方法方法体内。当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一 个引用自身的指针,这个指针的名字就是this。
因此,this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现this。
super关键和this作用类似,是被屏蔽的成员变量或者成员方法或变为可见,或者说用来引用被屏蔽的 成员变量和成员成员方法。
不过super是用在子类中,目的是访问直接父类中被屏蔽的成员,注意是直接父类(就是类之上最近的超类)

下列语句哪一个正确()

A.Java程序经编译后会产生machine code 
B.Java程序经编译后会产生byte code 
C.Java程序经编译后会产生DLL 
D.以上都不正确 

答案:B
Java字节码是Java源文件编译产生的中间文件
java虚拟机是可运行java字节码的假想计算机 java的跨平台性也是相对与其他编程语言而言的 。
先介绍一下c语言的编译过程:c的文件经过C编译程序编译后生成windows可执行文件exe文件然后在windows中执行。
再介绍java的编译过程:java的文件由java编译程序将java字节码文件就是class文件在java虚拟机中执行。机器码是由CPU来执行的;Java编译后是字节码。
电脑只能运行机器码。Java在运行的时候把字节码变成机器码。C/C++在编译的时候直接编译成机器码

下列哪一种叙述是正确的()

A.abstract修饰符可修饰字段、方法和类 
B.抽象方法的body部分必须用一对大括号{ }包住 
C.声明抽象方法,大括号可有可无 
D.声明抽象方法不可写出大括号 

答案:D
abstract修饰符用来修饰类和成员方法
用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化。
用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不 提供具体的实现。
Abstract是Java中的一个重要关键字,可以用来修饰一个类或者一个方法。
修饰一个方法时,表示该方法只有特征签名(signature),没有具体实现,而是把具体实现留给继承该类的子类,所以不能有大括号。

将元数据配置到Spring容器,下面哪个方法是不正确的()

A,通过编组与解组对象 
B,注解基础配置 
C,Java基础配置 
D,XML基础配置 

正确答案:A
有三种方式向Spring 容器提供元数据:1,XML配置文件;2,基于注解配置;3,基于Java的配置,死概念记住即可。

以下关于被private修饰的成员变量的说法正确的是

A、只能被同一个包中的类访问 
B、只能被该类自身所访问和修改 
C、可以被两种类访问和引用:该类本身、该类的所有子类 
D、可以被三种类所引用:该类自身、与它在同一个包中的其他类、在其他包中的该类的子类 

正确答案:B
扩展:

  • public:
    具有最大的访问权限,可以访问任何一个在classpath下的类、接口、异常等。它往往用于对外的情
    况,也就是对象或类对外的一种接口的形式。
  • protected:
    主要的作用就是用来保护子类的。它的含义在于子类可以用它修饰的成员,其他的不可以,它相当于传递给子类的一种继承的东西
  • default:
    有时候也称为friendly,它是针对本包访问而设计的,任何处于本包下的类、接口、异常等,都可以相互访问,即使是父类没有用protected修饰的成员也可以。
  • private:
    访问权限仅限于类的内部,是一种封装的体现,例如,大多数成员变量都是修饰符为private的,它们不希望被其他任何外部的类访问。

二、多线程并发

Java内存模型

  • JMM规定了线程的工作内存和主内存的交互关系,以及线程之间的可见性和程序的执行顺序。一方面,要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能地放松。JMM对程序员屏蔽了CPU以及OS内存的使用问题,能够使程序在不同的CPU和OS内存上都能够达到预期的效果。

  • Java采用内存共享的模式来实现线程之间的通信。编译器和处理器可以对程序进行重排序优化处理,但是需要遵守一些规则,不能随意重排序。

  • 原子性:一个操作或者多个操作要么全部执行要么全部不执行;

  • 可见性:当多个线程同时访问一个共享变量时,如果其中某个线程更改了该共享变量,其他线程应
    该可以立刻看到这个改变;

  • 有序性:程序的执行要按照代码的先后顺序执行;

  • 在并发编程模式中,势必会遇到上面三个概念,JMM对原子性并没有提供确切的解决方案,但是JMM解决了可见性和有序性,至于原子性则需要通过锁或者Synchronized来解决了。

  • 如果一个操作A的操作结果需要对操作B可见,那么我们就认为操作A和操作B之间存在happens-before关系,即A happens-before B。

  • happens-before原则是JMM中非常重要的一个原则,它是判断数据是否存在竞争、线程是否安全的主要依据,依靠这个原则,我们可以解决在并发环境下两个操作之间是否存在冲突的所有问题。

  • JMM规定,两个操作存在happens-before关系并不一定要A操作先于B操作执行,只要A操作的结果对B操作可见即可。

  • 在程序运行过程中,为了执行的效率,编译器和处理器是可以对程序进行一定的重排序,但是他们必须要满足两个条件:1 执行的结果保持不变,2 存在数据依赖的不能重排序。重排序是引起多线程不安全的一个重要因素。

  • 同时顺序一致性是一个比较理想化的参考模型,它为我们提供了强大而又有力的内存可见性保证,他主要有两个特征:
    1 一个线程中的所有操作必须按照程序的顺序来执行;
    2 所有线程都只能看到一个单一的操作执行顺序,在顺序一致性模型中,每个操作都必须原则执行且立刻对所有线程可见。

到目前位置,介绍了线程离开运行状态的3种方法:

  • 1、调用 Thread.sleep() :使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断)。
  • 2、调用 Thread.yield() :不能保障太多事情,尽管通常它会让当前运行线程回到可运行性状态,使得有相同优先级的线程有机会执行。
  • 3、调用 join() 方法:保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有存活,则当前线程不需要停止。

除了以上三种方式外,还有下面几种特殊情况可能使线程离开运行状态:

  • 1、线程的 run() 方法完成。
  • 2、在对象上调用 wait() 方法(不是在线程上调用)。
  • 3、线程不能在对象上获得锁定,它正试图运行该对象的方法代码。
  • 4、线程调度程序可以决定将当前运行状态移动到可运行状态,以便让另一个线程获得运行机会,而不需要任何理由。

一些常见问题

  • 1、线程的名字,一个运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是 mian ,非主线程的名字不确定。
  • 2、线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。
  • 3、获取当前线程的对象的方法是: Thread.currentThread() ;
  • 4、在上面的代码中,只能保证:每个线程都将启动,每个线程都将运行直到完成。一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。
  • 5、当线程目标 run() 方法结束时该线程完成。
  • 6、一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。
  • 7、线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。
    众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。
  • 8、尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列的事实。
  • 9、尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。

ServerSocket
在客户/服务器通信模式中,服务器端需要创建监听特定端口的 Serversocket,Serversocke t负责接收客户连接请求。 java 提供了一个 ServerSocket 类表示服务器 Socket 。
服务器 Socket 在服务器上运行,监听入站ftp连接。每个服务器Socket监听服务器上的一个特定端口。
当远程注解上的一个客户端尝试这个端口时,服务器就会被唤醒,协商建立客户端与服务器端的连接,
并返回一个常规的 Socket 对象,表示2台主机之间的 Socket 。
也是就说服务器端 Socket 接受到客户端 Socket 发送过来的连接时,服务器端会生成一个常规的
Socket对象,用于向客户端发送数据,数据总是通过常规socket进行传输。

ServerSocket生命周期
ServerSocket 服务器的基本生命周期包含以下几个:

  • 1.使用一个 ServerSocket() 构造函数在一个特定的端口创建一个新的 ServerSocket 对象。
  • 2.ServerSocket 使用他的 accept() 方法来监听这个端口的入站连接。 accept 会一直阻塞,直到一个客户端尝试与服务器建立连接,此时 accept 将返回一个连接客户端和服务器 Socket对象。
  • 3.根据服务器的类型,会调用 Socket 对象的 getInputStream 或 getOutputStream 方法,或者这两个方法都调用,以获得客户端通信的输入和输出流。
  • 4.服务器和客户端根据已经协商的协议交互,直到要关闭连接。
  • 5.服务器或客户端关闭连接。
  • 6.服务器返回到第2步 accept ,等待下一次连接

出现阻塞的情况大体分为如下5种:

  1. 线程调用 sleep 方法,主动放弃占用的处理器资源。
  2. 线程调用了阻塞式 IO 方法,在该方法返回前,该线程被阻塞。
  3. 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。
  4. 线程等待某个通知。
  5. 程序调用了 suspend 方法将该线程挂起。此方法容易导致死锁,尽量避免使用该方法。
    run() 方法运行结束后进入销毁阶段,整个线程执行完毕。

每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞
队列存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调度;反之,一个线程被
wait 后,就会进入阻塞队列,等待下一次被唤醒。
注意:wait,notify必须使用在synchronized同步方法或者代码块内。


三、数据库

索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。
索引的原理很简单,就是把无序的数据变成有序的查询

1、把创建了索引的列的内容进行排序 
2、对排序结果生成倒排表 
3、在倒排表内容上拼上数据地址链 
4、在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据 

创建索引的原则(重中之重)
索引虽好,但也不是无限制的使用,最好符合一下几个原则

  • 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)较频繁作为查询条件的字段才去创建索引
  • 3)更新频繁字段不适合创建索引
  • 4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
  • 5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
  • 6)定义有外键的数据列一定要建立索引。
  • 7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
  • 8)对于定义为text、image和bit的数据类型的列不要建立索引。

百万级别或以上的数据如何删除
关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。
所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。

  1. 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
  2. 然后删除其中无用数据(此过程需要不到两分钟)
  3. 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
  4. 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。
    来源:https://www.cnblogs.com/a8457013

视图概述(技术文):

1)什么是视图?
视图是基于 SQL 语句的结果集的可视化的表。
视图包含行和列,就像一个真实的表。视图中的字段就是来自一个或多个数据库中的真实的表中的字
段。视图并不在数据库中以存储的数据值集形式存在,而是存在于实际引用的数据库表中,视图的构成可以是单表查询,多表联合查询,分组查询以及计算(表达式)查询等。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。

2)视图的优点:

  • a、简化查询语句(视图机制使用户可以将注意力集中在所关心地数据上。如果这些数据不是直接来自基本表,则可以通过定义视图,使数据库看起来结构简单、清晰,并且可以简化用户的的数据查询操作。
  • b、可以进行权限控制把表的权限封闭,但是开放相应的视图权限,视图里只开放部分数据列等。
  • c、大数据表分表的时候,比如某张表的数据有100万条,那么可以将这张表分成四个视图。
    按照对id取余计算
  • d、用户能以多种角度看待同一数据:
    使不同的用户以不同的方式看待同一数据,当许多不同种类的用户共享同一个数据库时,这种灵活性是非常必要的。
  • e、对重构数据库提供了一定程度的逻辑独立性: 视图可以使应用程序和数据库表在一定程度上独立。

3)视图的缺点:

  • 1)性能差:
    把视图查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,sql server也要把它变成一个复杂的结合体,需要花费一定的时间。
  • 2)修改限制:当用户试图修改试图的某些信息时,数据库必须把它转化为对基本表的某些信息的修改,对于简单的试图来说,这是很方便的,但是,对于比较复杂的试图,可能是不可修改的。

4)视图使用场景(其实就是需要用到视图上面的几个优点的时候):

  • 1) 需要权限控制的时候。
  • 2)如果某个查询结果出现的非常频繁,就是要经常拿这个查询结果来做子查询,使用视图会更加方
    便。
  • 3)关键信息来源于多个复杂关联表,可以创建视图提取我们需要的信息,简化操作;

5)视图的分类:

  • 1)关系视图:它属于数据库对象的一种,也就是最常见的一种关联查询;
  • 2)内嵌视图:它不属于任何用户,也不是对象,创建方式与普通视图完全不同,不具有可复用性,不能通过数据字典获取数据;
  • 3)对象视图:
    它是基于表对象类型的视图,特性是继承、封装等可根据需要构建对象类型封装复杂查询(官方:为了迎合对象类型而重建数据表是不实现的);
  • 4)物化视图:
    它主要用于数据库的容灾(备份),实体化的视图可存储和查询,通过DBLink连接在主数据库物化视图中复制,当主库异常备库接管实现容灾;
视图的使用: 
1、创建视图 
1. create or replace view v_test asselect * fromuser; 
加上OR REPLACE表示该语句还能替换已有的视图 

2、调取视图 
1. select * from v_test; 

3、修改视图 
1. alter view v_test asselect * from user1; 

4、删除视图 
1. drop view if exists v_test; 

5、查看视图 
1. show tables; 
视图放在information_schema数据库下的views表里 

6、查看视图的定义 
show table status from companys like'v_test'; 

在这之前,我们必须明确!增删改最终都是修 改到基础表。且视图中虽然可以更新数据,但 是有很多的限制。一般情况下,最好将视图作为查询数据的虚拟表,而不要通过视图更新数据。因为,使用视图更新数据时,如果没有全 面考虑在视图中更新数据的限制,就可能会造 成数据更新失败。

视图的算法——存在两种执行的算法

  • a、Merge:合并的执行方式,每当执行的时候,先将我们的视图的sql语句与外部查询视图的sql语句,混合在一起,最终执行。
  • b、Temptable:临时表模式,每当查询的时候,将视图所使用的select语句生成一个结果的临时表,再在当当前临时表内进行查询。

四、视图使用注意点:

  • 1)修改操作时要非常非常小心,不然不经意间你已经修改了基本表里的多条数据;
  • 2)视图中的查询语句性能要调到最优;
  • 3)虽说上面讲到,视图有些是可以修改的。但是更多的是禁止修改视图。

对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系或者特殊的没有约束的一对多字段。还有一些特定的其他结构,这类结构会使得视图不可更新。

不可更改的情况如下:视图中含有以下的都不可被修改了。 
(一)聚合函数(SUM(), MIN(), MAX(), COUNT()等)。 
(二)DISTINCT。如下错误。 
(三)GROUP BY 
(四)HAVING 
(五)UNION或UNION ALL 
(六)位于选择列表中的子查询 
(八)FROM子句中的不可更新视图 
(九)WHERE子句中的子查询,引用FROM子句中的表。 
(十)ALGORITHM = TEMPTABLE(使用临时表总会使视图成为不可更新的)。

什么是主从复制:
主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。

主从复制的作用(好处,或者说为什么要 做主从)重点!:

  • 1、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
  • 2、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。
  • 3、读写分离,使数据库能支撑更大的并发。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。

主从复制的原理(重中之重,面试必问):

  • 1.数据库有个bin-log二进制文件,记录了所有sql语句。
  • 2.我们的目标就是把主数据库的bin-log文件的sql语句复制过来。
  • 3.让其在从数据的relay-log重做日志文件中再执行一次这些sql语句即可。
  • 4.下面的主从配置就是围绕这个原理配置
  • 5.具体需要三个线程来操作:
    binlog输出线程。每当有从库连接到主库的时候,主库都会创建一个线程然后发送binlog内容到从库。
    在从库里,当复制开始的时候,从库就会创建两个线程进行处理: 从库I/O线程。
    当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog里面的更新记录到从库上。
    从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,其中包括relay log文件。
    从库的SQL线程。从库创建一个SQL线程,这个线程读取从库I/O线程写到relay log的更新事件并执行。
    可以知道,对于每一个主从复制的连接,都有三个线程。
    拥有多个从库的主库为每一个连接到主库的从库创建一个binlog输出线程,每一个从库都有它自己的I/O线程和SQL线程。

步骤一:主库db的更新事件(update、insert、delete)被写到binlog
步骤二:从库发起连接,连接到主库
步骤三:此时主库创建一个binlog dump thread,把binlog的内容发送到从库
步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log
步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db

需要知道的是,面试过程中原理不会让你讲那 么久,一般的,只要把1234点讲出来,然后说简略说下三个线程,这就满分了!

三步轻松构建主从:

一、Master主服务器上的配置(103.251.237.42) 
1.编辑my.cnf (命令查找文件位置:fifind / -name my.cnf) 
在[mysqld]中注释掉 bind-address = 127.0.0.1 不然mysql无法远程 
server-id = 1 中 1 是可以自己定义的,但是需要保持它的唯一性,是服务器的唯一标识 
1.log_bin 启动MySQL二进制日志 
2.binlog_do_db 指定记录二进制日志的数据库 
3.binlog_ignore_db 指定不记录二进制日志的数据库。 
注释掉 binlog_do_db 和 binlog_ignore_db ,则表示备份全部数据库做完这些后,重启下数据库 
2.登陆主服务器mysql 创建从服务器用到的账户和权限; 
@之后IP可访问主服务器,这里值定从服务器IP 
新建密码为masterbackup的masterbackup 用户,并赋予replication slave 权限 
可以看到用户masterbackup 已经添加 
3.查看主数据库的状态 
记录 mysql-bin.000007 以及 276,编写以下命令待用; 
change master to 
master_host='103.251.237.42',master_port=3306,master_user='masterbackup',master_password 
='masterbackup',master_log_fifile='mysql-bin.000007',master_log_pos=276; 
二、Slave从服务器配置上的配置(103.251.237.45) 
1.编辑my.cnf(命令查找文件位置:fifind / -name my.cnf) 
在[mysqld]中 
relay-log = slave-relay-bin 
relay-log-index = slave-relay-bin.index 
暂时不清楚这是做什么的。加入这两条。 
重启mysql服务 
登陆mysql,停止同步命令 
执行用上面准备的命令; 登录Slave从服务器,连接Master主服务器: 
重新启动数据同步; 
查看Slave信息;如图两句都为yes,则状态正常 
三、从主从服务器测试结果 
在主服务器创建一个数据库 
在从服务器上查看刚才创建的数据库 
可以查到,主从服务器配置完成。(技术文)当然,还有主主复制,如果有感兴趣的朋友可以留言。 
其实主从复制也存在一些问题: 
1. 负载均衡,由于复制的时间差,不能保证同步读,而且写仍然单点,没法多点写,我对这个理解就是半吊子的读写均衡。 
2. 容灾,基本都是有损容灾,因为数据不同步,谁用谁知道,半吊子的容灾。 
可能只是提供一种成本较低的数据备份方案加不完美的容灾和负载均衡吧,这种方案注定是一种过渡方案,个人认为必须更新了。当然,在不是体量巨大的情况下,还是不失为一个优化的解决办法。 

面试题干货分析(如果问到数据库主从问题,必问以下问题):
1、主从的好处是?
2、主从的原理是?
3、从数据库的读的延迟问题了解吗?如何解决?
4、做主从后主服务器挂了怎么办?


四、常见面试题

TCP 粘包/拆包的原因及解决方法?

  • TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。

TCP粘包/分包的原因:

  • 应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象;
  • 进行MSS大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包
  • 以太网帧的payload(净荷)大于MTU(1500字节)进行ip分片。

解决方法

  • 消息定长:FixedLengthFrameDecoder类
  • 包尾增加特殊字符分割:行分隔符类:LineBasedFrameDecoder或自定义分隔符类 :
    DelimiterBasedFrameDecoder
  • 将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。

了解哪几种序列化协议?

  • 序列化(编码)是将对象序列化为二进制形式(字节数组),主要用于网络传输、数据持久化等;而反序列化(解码)则是将从网络、磁盘等读取的字节数组还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。
  • 影响序列化性能的关键因素:序列化后的码流大小(网络带宽的占用)、序列化的性能(CPU资源占用);是否支持跨语言(异构系统的对接和开发语言切换)。
  • Java默认提供的序列化:无法跨语言、序列化后的码流太大、序列化的性能差

XML

  • 优点:人机可读性好,可指定元素或特性的名称。
  • 缺点:序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方法;文件庞大,文件格式复杂,传输占带宽。
  • 适用场景:当做配置文件存储数据,实时数据转换。

JSON,是一种轻量级的数据交换格式。

  • 优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与XMl相比,其协议比较简单,解析速度比较快。
  • 缺点:数据的描述性比XML差、不适合性能要求为ms级别的情况、额外空间开销比较大。
  • 适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于Web browser的Ajax请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。

Fastjson,采用一种“假定有序快速匹配”的算法。

  • 优点:接口简单易用、目前java语言中最快的json库。
  • 缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全。
  • 适用场景:协议交互、Web 输出、Android客户端

Thrift,不仅是序列化协议,还是一个RPC框架。

  • 优点:序列化后的体积小, 速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。
  • 缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使用(例如HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。
  • 适用场景:分布式系统的RPC解决方案

Avro,Hadoop的一个子项目,解决了JSON的冗长和没有IDL的问题。

  • 优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现。
  • 缺点:对于习惯于静态类型语言的用户不直观。
  • 适用场景:在Hadoop中做Hive、Pig和MapReduce的持久化数据格式。

Protobuf,将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。

  • 优点:序列化后码流小,性能高、结构化数据存储格式(XML JSON等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护。
  • 缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持Java 、C++ 、python。
  • 适用场景:对性能要求高的RPC调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化

其它

  • protostuffff 基于protobuf协议,但不需要配置proto文件,直接导包即可
  • Jboss marshaling 可以直接序列化java类, 无须实java.io.Serializable接口
  • Message pack 一个高效的二进制序列化格式
  • Hessian 采用二进制协议的轻量级remoting onhttp工具
  • kryo 基于protobuf协议,只支持java语言,需要注册(Registration),然后序列化(Output),反序列化(Input)

说一下 JVM 有哪些垃圾回收算法?

  • 标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
  • 标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内
    存。
  • 复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,
    然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
  • 分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

说一下 JVM 有哪些垃圾回收器?

  • Serial:最早的单线程串行垃圾回收器。
  • Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
  • ParNew:是 Serial 的多线程版本。
  • Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。
  • Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
  • CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
  • G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。

详细介绍一下 CMS 垃圾回收器?

  • CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
  • CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。

新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1
  • 新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

说一下 JVM 的主要组成部分?及其作用?

类加载器(ClassLoader) 
运行时数据区(Runtime Data Area) 
执行引擎(Execution Engine) 
本地库接口(Native Interface) 

组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区
(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

说一下堆栈的区别?

功能方面:堆是用来存放对象的,栈是用来执行程序的。 
共享性:堆是线程共享的,栈是线程私有的。 
空间大小:堆大小远远大于栈。 

队列和栈是什么?有什么区别?

队列和栈都是被用来预存储数据的。 
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。 
栈和队列很相似,但它运行对元素进行后进先出进行检索。

说一下 JVM 运行时数据区?
不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:

  • 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
  • Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
  • 本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
  • Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
  • 方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

说一下类装载的执行过程?
类装载分为以下 5 个步骤:

  • 加载:根据查找路径找到相应的 class 文件然后导入;
  • 检查:检查加载的 class 文件的正确性;
  • 准备:给类中的静态变量分配内存空间;
  • 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
  • 初始化:对静态变量和静态代码块执行初始化工作。

Hibernate工作原理及为什么要使用Hibernate?

工作原理: 
1.读取并解析配置文件 
2.读取并解析映射信息,创建SessionFactory 
3.打开Session 
4.创建事务Transation 
5.持久化操作 
6.提交事务 
7.关闭Session 
8.关闭SesstionFactory 

为什么要使用Hibernate(即它的优点):

  1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
  2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化
    DAO层的编码工作
  3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。
  4. hibernate映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关
    系。

Hibernate中get和load方法的区别

  • hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果
    在使用过程中发现了问题,只能抛异常;
  • hibernate对于get方法,hibernate一定要获取到真实的数据,否则返回null。

具体介绍:

  • 对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,
    然后在二级缓存中查找,还没有就查询数据库,数据库中没有就返回null。
  • load方法加载实体对象的时候,根据映射文件上类级别的lazy属性的配置(默认为true)。
  • 分情况讨论:
    (1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加
    载,返回实体的代理类对象(该代理类为实体类的子类,由CGLIB动态生成)。等到具体使用该对象
    (除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一个
    ObjectNotFoundException。
    (2)若为false,就跟get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个
    ObjectNotFoundException。

Hibernate是如何延迟加载?
Hibernate3 提供了属性的延迟加载功能。当Hibernate在查询数据的时候,数据并没有存在于内存之 中,而是当程序真正对数据的操作时,对象才存在于内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。

Hibernate中怎样实现类之间的关系?
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们在程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many进 行操作。

Hibernate中的update()和saveOrUpdate()的区别?

saveOrUpdate(): 
1,如果对象已经在本session中持久化了,不做任何事 
2,如果另一个与本session关联的对象拥有相同的持久化标识(identififier),抛出一个异常 
3,如果对象没有持久化标识(identififier)属性,对其调用save() 
4,如果对象的持久标识(identififier)表明其是一个新实例化的对象,对其调用save() 
5,如果对象是附带版本信息的(通过 <version> 或 <timestamp> )并且版本属性的值表明其是 
一个新实例化的对象,调用save()。否则update() 这个对象。 
update() : 
是将一个游离状态的实体对象直接更新。 

说说Hibernate的缓存机制。

  • 一级缓存:内部缓存存在Hibernate中,属于应用事物级缓存。
  • 二级缓存:应用级缓存、 分布式缓存。
    使用场景:数据不会被第三方修改、数据大小在可接受范围、数据更新频率低、同一数据被系统频繁使用、非关键数据
  • 引入第三方缓存(如ehcache等)。

如何优化Hibernate?

1.使用双向一对多关联,不使用单向一对多 
2.灵活使用单向一对多关联 
3.不用一对一,用多对一取代 
4.配置对象缓存,不使用集合缓存 
5.一对多集合使用Bag,多对多集合使用Set 
6. 继承类使用显式多态 
7. 表字段要少,表关联不要怕多,有二级缓存撑腰 

谈谈hibernate的延迟加载和openSessionInView

  • 延迟加载要在session范围内,用到的时候再加载;
  • opensessioninview是在web层写了一个fifilter来打开和关闭session,这样就表示在一次request过程中session一直开着,保证了延迟加载在session中的这个前提。

简要阐述struts2的工作流程

1、客户端浏览器发出HTTP请求。 
2、根据web.xml配置,该请求被FilterDispatcher接收。 
3、根据struts.xml配置,找到需要调用的Action类和方法,并通过IoC方式,将值注入给Aciton。 
4、Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。 
5、Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面。 
6、返回HTTP响应到客户端浏览器。

说下Struts的设计模式
MVC模式

1,web应用程序启动时就会加载并初始化ActionServlet; 
2,用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据; 
3,ActionServlet根据Struts-confifig.xml文件配置好的设置决定是否需要表单验证,如果需要就调用ActionForm的Validate()验证后选择将请求发送到哪个Action,如果Action不存在,ActionServlet会先创建这个对象,然后调用Action的execute()方法; 
4,Execute()从ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指定的jsp组件; 
5,ActionForward对象指定的jsp生成动 态的网页,返回给客户。 

Struts的优缺点
优点:

  • 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现.
  • 有丰富的tag可以用 ,Struts的标记库(Taglib),如能灵活动用,则能大大提高开发效率。另外,就目前国内的JSP开发者而言,除了使用JSP自带的常用标记外,很少开发自己的标记,或许Struts是一个很好的起点。
  • 页面导航.页面导航将是今后的一个发展方向,事实上,这样做,使系统的脉络更加清晰。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时,这种优势体现得更加明显。
  • 提供Exception处理机制 .
  • 数据库链接池管理
  • 支持I18N

缺点:

  • 转到展示层时,需要配置forward,每一次转到展示层,相信大多数都是直接转到jsp,而涉
    及到转向, 需要配置forward,如果有十个展示层的jsp,需要配置十次struts,而且还不包
    括有时候目录、文件变更,需要重新修改forward。
    注意,每次修改配置之后,要求重新部署整个项目,而tomcate这样的服务器,还必须重新
    启动服务器,如果业务变更复杂频繁的系统,这样的操作简单不可想象。现在就是这样,几
    十上百个人同时在线使用我们的系统,大家可以想象一下,我的烦恼有多大。
  • Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以
    action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。
  • 测试不方便. Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。
  • 类型的转换. Struts的FormBean把所有的数据都作为String类型,它可以使用工具
    Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。
  • 对Servlet的依赖性过强. Struts处理Action时必需要依赖ServletRequest 和
    ServletResponse,所有它摆脱不了Servlet容器。
  • 前端表达式语言方面.Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可
    是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。
  • 对Action执行的控制困难. Struts创建一个Action,如果想控制它的执行顺序将会非常困难。
    甚至你要重新去写Servlet来实现你的这个功能需求。
  • 对Action 执行前和后的处理. Struts处理Action的时候是基于class的hierarchies,很难在
    action处理前和后进行操作。
  • 对事件支持不够. 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),
    换一句话说:在Struts中实际是一个表单只能 对应一个事件,struts这种事件方式称为
    applicationevent,application event和component event相比是一种粗粒度的事件。

五、实际面试整理

short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?

  • 对于 short s1 = 1; s1 = s1 + 1; 由于 s1+1 运算时会自动提升表达式的类型,所以结果是 int 型,
    再赋值给 short 类型 s1 时,编译器将报告需要强制转换类型的错误。
  • 对于 short s1 = 1; s1 += 1;由于 += 是 java 语言规定的运算符,java 编译器会对它进行特殊处理,因此可以正确编译。

是否可从一个 static 方法内发出对非 static 方法的调用?
不可以。

  • 因为非 static 方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对
    象上进行方法调用,而 static 方法调用时不需要创建对象,可以直接调用。
  • 也就是说,当一 个 static 方法被调用时,可能还没有创建任何实例对象,如果从一个 static 方法中发出对非 static 方法的调用,那个非 static 方法是关联到哪个对象上的呢?
  • 这个逻辑无法成立,所以,一个 static 方法内部发出对非 static 方法的调用。

ClassLoader 如何加载 class ?
jvm 里有多个类加载,每个类加载可以负责加载特定位置的类,例如,bootstrap 类加载负责加载 jre/lib/rt.jar 中的类, 我们平时用的 jdk 中的类都位于 rt.jar 中。extclassloader 负责加载ar/lib/ext/*.jar 中的类,appclassloader 负责 classpath 指定的目录或 jar 中的类。除了 bootstrap之外,其他的类加载器本身也都是 java 类,它们的父类是 ClassLoader。

Servlet 的生命周期?

  • Servlet 被服务器实例化后,容器运行其 init 方法,请求到达时运行其 service 方法,service被方法自动派遣运行与请求对应的 doXXX 方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其 destroy 方法。

  • 与 cgi 的区别在于 servlet 处于服务器进程中,它通过多线程方式运行其 service 方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而 CGI 对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于 servlet

构造器 Constructor 是否可被 override?
构造器 Constructor 不能被继承,因此不能重写 Override,但可以被重载 Overload。

接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)? 抽象类中是否可以有静态的 main 方法?

  • 接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承具体类。抽象类
    中可以有静态的 main 方法。

只有记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有 abstract 方法。

Overload 和 Override 的区别。Overloaded 的方法是否可以改变返回值的类型?
Overload 是重载的意思,Override 是覆盖的意思,也就是重写。

  • 重载 Overload 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相
    同(即参数个数或类型不同)。
  • 重写 Override 表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类
    创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个
    完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法
    时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决
    父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能
    更小。如果父类的方法是 private 类型,那么,子类则不存在覆盖的限制,相当于子类中增
    加了一个全新的方法。

至于 Overloaded 的方法是否可以改变返回值的类型这个问题,要看你倒底想问什么呢?
这个题目很模糊。如果几个 Overloaded 的方法的参数列表不一样,它们的返回者类型当然也
可以不一样。
但我估计你想问的问题是:如果两个方法的参数列表完全一样,是否可以让它 们的返回值不同来实现重载 Overload。
这是不行的,我们可以用反证法来说明这个问题,因为我们有时候调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调用 map.remove(key)方法时,虽然 remove 方法有返回值,但是我们通常都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java 就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。
override 可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:

  • 1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
  • 2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
  • 3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
  • 4、被覆盖的方法不能为 private,否则在其子类中只是新定义了一个方法,并没有对其进行
    覆盖。

overload 对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM 就会根据不同的参数样式,来选择合适的方法执行。在使用重载要注意以下的几点:

  • 1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不
    同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是 fun(int,float),
    但是不能为 fun(int,int));
  • 2、不能通过访问权限、返回类型、抛出的异常进行重载;
  • 3、方法的异常类型和数目不会对重载造成影响;
  • 4、对于继承来说,如果某一方法在父类中是访问权限是 priavte,那么就不能在子类对其进
    行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

java 中实现多态的机制是什么?
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

abstract class 和 interface 有什么区别?

  • 含有 abstract 修饰符的 class 即为抽象类,abstract 类不能创建的实例对象。
  • 含有 abstract 方法的类必须定义为 abstract class,abstract class 类中的方法不必是抽象的。
  • abstract class 类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为 abstract 类型。
  • 接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。
  • 接口中的方法定义默认为 public abstract 类型,接口中的成员变量类型默认为 public static final。

下面比较一下两者的语法区别:

  • 1.抽象类可以有构造方法,接口中不能有构造方法。
  • 2.抽象类中可以有普通成员变量,接口中没有普通成员变量
  • 3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象
    的普通方法。
  • 4.抽象类中的抽象方法的访问类型可以是 public,protected 和(默认类型,虽然 eclipse 下不报
    错,但应该也不行),但接口抽象方法只能是 public类型的,并且默认即为 public abstract类型。
  • 5.抽象类中可以包含静态方法,接口中不能包含静态方法
  • 6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是 public static final 类型,并且默认即为 public static final 类型。
  • 7.一个类可以实现多个接口,但只能继承一个抽象类。

下面接着再说说两者在应用上的区别:

  • 接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。
  • 而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个
    典型应用,假设某个项目的所有 Servlet 类都要用相同的方式进行权限判断、记录访问日志和处理异常,那么就可以定义一个抽象的基类,让所有的 Servlet 都继承这个抽象基类,在抽象基类的service 方法中完成权限判断、记录访问日志和处理异常的代码,在各个子类中只是完成各自的业务逻辑代码,父类方法中间的某段代码不确定,留给子类干,就用模板方法设计模式。

备注:这道题的思路是先从总体解释抽象类和接口的基本概念,然后再比较两者的语法细节,最后再说两者的应用区别。比较两者语法细节区别的条理是:先从一个类中的构造方法、普通成员变量和方法(包括抽象方法),静态变量和方法,继承性等 6 个方面逐一去比较回答,接着从第三者继承的角度的回答,特别是最后用了一个典型的例子来展现自己深厚的技术功底。

内部类可以引用它的包含类的成员吗?有没有什么限制?

完全可以。如果不是静态内部类,那没有什么限制! 
如果你把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员 
变量,而只能访问外部类中的静态成员,例如,下面的代码: 
class Outer { 
	static int x; 
	static class Inner { 
		void test() { 
			syso(x); 
		} 
	} 
}

答题时,也要能察言观色,揣摩提问者的心思,显然人家希望你说的是静态内部类不能访问外部类的成员,但你一上来就顶牛,这不好,要先顺着人家,让人家满意,然后再说特殊情况,让人家吃惊。

Anonymous Inner Class (匿名内部类) 是否可继承其它类,是否可以 implements 接口?
可以继承其他类或实现其他接口。不仅是可以,而是必须!因为匿名内部类就是在抽象类和
接口的基础上发展起来的。

String s = “Hello”;s = s + " world!";这两行代码执行后,原始的 String 对象中的内容到底变了没有?

没有
  • 因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。
  • 这段代码中,s 原先指向一个 String 对象,内容是 “Hello”,然后我们对 s 进行了+操作,那么 s 所指向那个对象是否发生了改变呢?答案是没有。
  • 这时,s 不指向原来那个对象,而指向了另一个 String 对象,内容为"Hello world!",原来那个对象还存在于内存中,只是 s 这个引用变量不再指向它了。

通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用 String 来代表字符串的话会引起很大的内存开销。
因为 String 对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来 表示。这时,应该考虑使用 StringBuffer 类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。
例如我们要在构造器中对一个名叫 s 的 String 引用变量进行初始化,把它设置为初始值,应当这样做:

public class Demo { 
	private String s; 
	public Demo() { 
		s = "Initial Value"; 
	}
}
而非 s = new String("Initial Value"); 
  • 后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String
    对象不可改变,所以对于内容相同的字符串,只要一个 String 对象来表示就可以了。
  • 也就说,多次调用上面的构造器创建多个对象,他们的 String 类型属性 s 都指向同一个对象。
  • 上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同一个 String 对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。
  • 至于为什么要把 String 类设计成不可变类,是它的用途决定的。
  • 其实不只 String,很多Java 标准类库中的类都是不可变的。
  • 在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。
  • 不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。
  • 当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。
  • 所以 Java 标准类库还提供了一个可变版本,即 StringBuffer。

String s = new String(“xyz”);创建了几个 String Object? 二者之间有什么区别?
两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少遍,都是缓冲区中的那一个。New String 每写一遍,就创建一个新的对象,它一句那个常量”xyz”对象的内容来创建出一个新 String 对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己了,直接从缓冲区拿。

数组有没有 length()这个方法? String 有没有 length()这个方法?
数组没有 length()这个方法,有 length 的属性。String 有有 length()这个方法。

error 和 exception 有什么区别?

  • error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
  • Error 表示有 JVM 进行处理的,是 JVM 出错.
    = Exctption 是可以用程序进行处理的,使用 try…catch 进行处理.

启动一个线程是用 run()还是 start()*
启动一个线程是调用 start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。

当一个线程进入一个对象的一个 synchronized 方法后,其它线程是否可进入此对象其它方法?

分几种情况: 
1.其他方法前是否加了 synchronized 关键字,如果没加,则能。 
2.如果这个方法内部调用了 wait,则可以进入其他 synchronized 方法。 
3.如果其他个方法都加了 synchronized 关键字,并且内部没有调用 wait,则不能。 
4.如果其他方法是 static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是 this。

链表和数组的区别?

  • 创建数组时,必须明确说明数组的长度,(即数组中元素的个数),以便在内存中留出一块空间存放所有的数组元素,数组中各数据元素在内存中是顺序存放的。
  • 创建链表时,不需要给出链表中元素(称为节点)的个数,可以先只创建一个链表头,其他元素在需要时动态地创建并加入到链表,链表的数据无素在内存中不是连续存放的。

两个对象值相同(x.equals(y) == true),但却可有不同的 hash code,这句话对不对?

  • 如果对象要保存在 HashSet 或 HashMap 中,它们的 equals 相等,那么,它们的 hashcode值就必须相等。
  • 如果不是要保存在 HashSet 或 HashMap,则与 hashcode 没有什么关系了,这时候 hashcode不等是可以的,例如 arrayList 存储的对象就不用实现 hashcode,当然,我们没有理由不实现,通常都会去实现的。

能不能自己写个类,也叫 java.lang.String?
可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载 jre.jar 包中的那个 java.lang.String。
由于在 tomcat 的 web 应用程序中,都是由 webapp 自己的类加载器先自己加载 WEB-INF/classess 目录中的类,然后才委托上级的类加载器加载,如果我们在 tomcat 的 web 应用程序中写一个java.lang.String,这时候 Servlet 程序加载的就是我们自己写的 java.lang.String,但是这么干就会出很多潜在的问题,原来所有用了java.lang.String 类的都将出现问题。
虽然 java 提供了 endorsed 技术,可以覆盖 jdk 中的某些类。但是,能够被覆盖的类是有限制范围,反正不包括 java.lang 这样的包中的类。

HTTP 请求的 GET 与 POST 方式的区别
Form 中的 get 和 post 方法,在数据传输过程中分别对应了 HTTP 协议中的 GET 和 POST 方法。二者主要区别如下:

  • 1)Get 是用来从服务器上获得数据,而 Post 是用来向服务器上传递数据;
  • 2)Get 将表单中数据按照 variable=value 的形式,添加到 action 所指向的 URL 后面,并且两者使用“?”连接,而各个变量之间使用“&”连接;Post 是将表单中的数据放在 form的数据体中,按照变量和值相对应的方式,传递到 action 所指向 URL;
  • 3)Get 是不安全的,因为在传输过程,数据被放在请求的 URL 中;Post 的所有操作对用户来说都是不可见的;
  • 4)Get 传输的数据量小,这主要是因为受 URL 长度限制;而 Post 可以传输大量的数据,所以在上传文件只能使用 Post;
  • 5)Get 限制 Form 表单的数据集必须为 ASCII 字符,而 Post 支持整个 ISO10646 字符集;
  • 6)Get 是 Form 的默认方法。

说一说 Servlet 的生命周期?

servlet 有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。 
这个生存期由 javax.servlet.Servlet 接口的 init,service 和 destroy 方法表达。
  • Servlet 被服务器实例化后,容器运行其 init 方法,请求到达时运行其 service 方法,service
    方法自动派遣运行与请求对应的 doXXX 方法(doGet,doPost)等,当服务器决定将实例销
    毁的时候调用其 destroy 方法。
  • web 容器加载 servlet,生命周期开始。通过调用 servlet 的 init()方法进行 servlet 的初始化。
  • 通过调用 service()方法实现,根据请求的不同调用不同的 do***()方法。结束服务,web 容器调用 servlet 的 destroy()方法。

Servlet API 中 forward()与 redirect()的区别?
前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。
这样,从浏览器的地址栏中可以看到跳转后的链接地址。
所以,前者更加高效,在前者可以满足需要时,尽量使用 forward()方法,并且,这样也有助于隐藏实际的链接。
在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用 sendRedirect()方法。

详细描述 MVC
基于 Java 的 Web 应用系统采用 MVC 架构模式,即 model(模型)、view(视图)、control(控制)分离设计;这是目前 WEB 应用服务系统的主流设计方向。

  • Model:即处理业务逻辑的模块,每一种处理一个模块;
  • View:负责页面显示,显示 MODEL 处理结果给用户,主要实现数据到页面转换过程;
  • Control:负责每个请求的分发,把 FORM 数据传递给 MODEL 处理,把处理结果的数
    据传递给 VIEW 显示。

MVC 的各个部分都有那些技术来实现?如何实现?
MVC 是 Model-View-Controller 的简写。
Model 代表的是应用的业务逻辑(通过JavaBean,EJB 组件实现)
View 是应用的表示面(由 JSP 页面产生)
Controller 是提供应用的处理过程控制(一般是一个 Servlet)
通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现
这些组件可以进行交互和重用

** javascript 的优缺点和内置对象。**

  • 1)优点:简单易用,与 Java 有类似的语法,可以使用任何文本编辑工具编写,只需要浏览器就可执行程序,并且事先不用编译,逐行执行,无需进行严格的变量声明,而且内置大量现成对象,编写少量程序可以完成目标;
  • 2)缺点:不适合开发大型应用程序;
  • 3)Javascript 有 11 种内置对象: Array、String、Date、Math、Boolean、Number、
    Function、Global、Error、RegExp、Object。

谈谈你对 Hibernate 的理解。

  • 面向对象设计的软件内部运行过程可以理解成就是在不断创建各种新对象、建立对象之间的关系,调用对象的方法来改变各个对象的状态和对象消亡的过程,不管程序运行的过程和操作怎么样,本质上都是要得到一个结果,程序上一个时刻和下一个时刻的运行结果的差异就表现在内存中的对象状态发生了变化。
  • 为了在关机和内存空间不够的状况下,保持程序的运行状态,需要将内存中的对象状态保存到持久化设备和从持久化设备中恢复出对象的状态,通常都是保存到关系数据库来保存大量对象信息。从 Java 程序的运行功能上来讲,保存对象状态的功能相比系统运行的其他功能来说,应该是一个很不起眼的附属功能,java 采用 jdbc 来实现这个功能,这个不起眼的功能却要编写大量的代码,而做的事情仅仅是保存对象和恢复对象,并且那些大量的 jdbc代码并没有什么技术含量,基本上是采用一套例行公事的标准代码模板来编写,是一种苦活和重复性的工作。
  • 通过数据库保存 java 程序运行时产生的对象和恢复对象,其实就是实现了 java 对象与关系数据库记录的映射关系,称为 ORM(即 Object Relation Mapping),人们可以通过封装 JDBC 代码来实现了这种功能,封装出来的产品称之为 ORM 框架,Hibernate 就是其中的一种流行 ORM 框架。使用 Hibernate 框架,不用写 JDBC 代码,仅仅是调用一个 save 方法,就可以将对象保存到关系数据库中,仅仅是调用一个 get 方法,就可以从数据库中加载出一个对象。
  • 使用 Hibernate 的基本流程是:配置 Configuration 对象、产生 SessionFactory、创建 session 对象,启动事务,完成 CRUD 操作,提交事务,关闭 session。
  • 使用 Hibernate 时,先要配置 hibernate.cfg.xml 文件,其中配置数据库连接信息和方言等, 还要为每个实体配置相应的 hbm.xml 文件,hibernate.cfg.xml 文件中需要登记每个 hbm.xml 文件。
  • 在应用 Hibernate 时,重点要了解 Session 的缓存原理,级联,延迟加载和 hql 查询。

AOP 的作用

  • 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足除了类(classes)以外,AOP 提供了切面。切面对关注点进行模块化,例如横切多个类型和对象的事务管理
  • Spring 的一个关键的组件就是 AOP 框架,可以自由选择是否使用 AOP 提供声明式企业服务,特别是为了替代 EJB 声明式服务。最重要的服务是声明性事务管理,这个服务建立在 Spring 的抽象事物管理之上允许用户实现自定义切面,用 AOP 来完善 OOP 的使用.可以把 Spring AOP 看作是对 Spring的一种增强

Hibernate 有哪 5 个核心接口?

  • Configuration 接口:配置 Hibernate,根据其启动 hibernate,创建 SessionFactory 对象
  • SessionFactory 接口:初始化 Hibernate,充当数据存储源的代理,创建 session 对象
  • sessionFactory 是线程安全的,意味着它的同一个实例可以被应用的多个线程共享,是重量级、二级缓存
  • Session 接口:负责保存、更新、删除、加载和查询对象,是线程不安全的,避免多个线程共享同一个 session,是轻量级、一级缓存
  • Transaction 接口:管理事务
  • Query 和 Criteria 接口:执行数据库的查询

什么是 ORM?

  • 对象关系映射(Object—Relational Mapping,简称 ORM)
  • 是一种为了解决面向对象与面向关系数据库存在的互不匹配的现象的技术
  • 简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,将 java 程序中的对象自动持久化到关系数据库中
  • 本质上就是将数据从一种形式转换到另外一种形式

名词解释:①WEB SERVICE ②JAXP ③JAXM ④SOAP ⑤UDDI ⑥WSDL

  • Web Service:Web Service 是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得 Web Service 能与其他兼容的组件进行互操作。
  • JAXP:(Java API for XML Parsing) 定义了在 Java 中使用 DOM, SAX, XSLT 的通用的接口。 这样在你的程序中你只要使用这些通用的接口,当你需要改变具体的实现时候也不需要修改代码。
  • JAXM:(Java API for XML Messaging) 是为 SOAP 通信提供访问方法和传输机制的 API。
  • SOAP:即简单对象访问协议(Simple Object Access Protocol),它是用于交换 XML 编码信息的轻量级协议。
  • UDDI:的目的是为电子商务建立标准;UDDI 是一套基于 Web 的、分布式的、为 Web Service 提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的 Web Service 注册,以使别的企业能够发现的访问协议的实现标准。
  • WSDL:是一种 XML 格式,用于将网络服务描述为一组端点,这些端点对包含面向文档信息或面向过程信息的消息进行操作。这种格式首先对操作和消息进行抽象描述,然后将其绑定到具体的网络协议和消息格式上以定义端点。相关的具体端点即组合成为抽象端点(服务)。

BS 与 CS 的联系与区别。
C/S 是 Client/Server 的缩写。服务器通常采用高性能的 PC、工作站或小型机,并采用大型数据库系统,如 Oracle、Sybase、InFORMix 或 SQL Server。客户端需要安装专用的客户端软件。
B/S是Brower/Server的缩写,客户机上只要安装一个浏览器(Browser),如Netscape Navigator 或 Internet Explorer,服务器安装 Oracle、Sybase、InFORMix 或 SQL Server 等数据库。在这种结构下,用户界面完全通过 WWW 浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现。浏览器通过Web Server 同数据库进行数据交互。

C/S 与 B/S 区别:

  • 1.硬件环境不同:
    C/S 一般建立在专用的网络上, 小范围里的网络环境, 局域网之间再通过专门服务器提供连接和数据交换服务.
    B/S 建立在广域网之上的, 不必是专门的网络硬件环境,例与电话上网, 租用设备. 信息自己管理. 有比 C/S 更强的适应范围, 一般只要有操作系统和浏览器就行
  • 2.对安全要求不同
    C/S 一般面向相对固定的用户群, 对信息安全的控制能力很强. 一般高度机密的信息系统采用 C/S 结构适宜. 可以通过 B/S 发布部分可公开信息.
    B/S 建立在广域网之上, 对安全的控制能力相对弱, 可能面向不可知的用户。
  • 3.对程序架构不同
    C/S 程序可以更加注重流程, 可以对权限多层次校验, 对系统运行速度可以较少考虑. B/S 对安全以及访问速度的多重的考虑, 建立在需要更加优化的基础之上. 比 C/S 有更高的要求 B/S 结构的程序架构是发展的趋势, 从 MS 的.Net 系列的 BizTalk 2000 Exchange 2000 等, 全面支持网络的构件搭建的系统. SUN 和 IBM 推的 JavaBean 构件技术等,使 B/S 更加成熟.
  • 4.软件重用不同
    C/S 程序可以不可避免的整体性考虑, 构件的重用性不如在B/S要求下的构件的重用性好.
    B/S 对的多重结构,要求构件相对独立的功能. 能够相对较好的重用.就入买来的餐桌可以再利用,而不是做在墙上的石头桌子
  • 5.系统维护不同
    C/S 程序由于整体性, 必须整体考察, 处理出现的问题以及系统升级. 升级难. 可能是再做一个全新的系统
    B/S 构件组成,方面构件个别的更换,实现系统的无缝升级. 系统维护开销减到最小.用户从网上自己下载安装就可以实现升级.
  • 6.处理问题不同
    C/S 程序可以处理用户面固定, 并且在相同区域, 安全要求高需求, 与操作系统相关.应该都是相同的系统
    B/S 建立在广域网上, 面向不同的用户群, 分散地域, 这是 C/S 无法作到的. 与操作系统平台关系最小.
  • 7.用户接口不同
    C/S 多是建立的 Window 平台上,表现方法有限,对程序员普遍要求较高
    B/S 建立在浏览器上, 有更加丰富和生动的表现方式与用户交流. 并且大部分难度减低, 减低开发成本.
  • 8.信息流不同
    C/S 程序一般是典型的中央集权的机械式处理, 交互性相对低
    B/S 信息流向可变化, B-B B-C B-G 等信息、流向的变化, 更像交易中心。


这篇关于JAVA面试宝典-3的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程