面试(六)

JVM的类加载机制是什么?有哪些实现方式?

  • 类加载机制:

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构。类的加载最终是在堆区内的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

  • 类加载有三种方式:

1)命令行启动应用时候由JVM初始化加载

2)通过Class.forName()方法动态加载

3)通过ClassLoader.loadClass()方法动态加载

List 和 Set 的区别

1.List和Set都是接口继承于Collection接口。

2.最大的不同就是List是可以重复的。而Set是不能重复的。(注意:元素虽然无放入顺序,但是元素在set 中的位置是有该元素的HashCode决定的,其位置其实是固定的)

3.List接口有三个实现类:LinkedList,ArrayList,Vector ,Set接口有两个实现类:HashSet(底层由HashMap实现),LinkedHashSet

4.List适合经常追加数据,插入,删除数据。但随机取数效率比较低。

5.Set适合经常地随机储存,插入,删除。但是在遍历时效率比较低。

MVC模式完成分页功能的基本思路是什么?

1)页面提交页码(第几页)到Servlet中

2)Servlet接收到页码后,将页码传递给分页工具类(PageBean)

3)Servlet中调用Service层传入PageBean对象

4)Service层调用DAO层传入PageBean对象

5)Servlet中得到查询出来的数据,并setAttribute保存

6)在页面中得到(getAttribute)数据,遍历输出

MyBatis什么情况下用注解绑定,什么情况下用xml绑定?

当Sql语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多

MyBatis实现一对一有几种方式?具体怎么操作的?

有联合查询和嵌套查询

  • 联合查询:联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置association节点配置一对一的类就可以完成;

  • 嵌套查询:嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association配置

MyBatis接口绑定有几种实现方式,分别是怎么实现的?

接口绑定有两种实现方式:注解绑定和通过xml写sql绑定

  • 注解绑定:就是在接口的方法上面加上@Select@Update等注解里面包含Sql语句来绑定,

  • XML绑定:另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.

MyBatis的优势

  • MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。

  • MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

MyBatis里面的动态Sql是怎么设定的?用什么语法?

       MyBatis里面的动态Sql一般是通过if节点来实现,通过OGNL语法来实现,但是如果要写的完整,必须配合where,trim节点,where节点是判断包含节点有内容就插入where,否则不插入,trim节点是用来判断如果动态语句是以and 或or开始,那么会自动把这个and或者or取掉。

MYSQL如何优化?结合你的经验

1.数据库的设计 (尽量把数据库设计的更小的占磁盘空间.)

1).尽可能使用更小的整数类型.(mediumint就比int更合适).

2).尽可能的定义字段为not null,除非这个字段需要null.

3).如果没有用到变长字段的话比如varchar,那就采用固定大小的纪录格式比如char.

4).表的主索引应该尽可能的短.这样的话每条纪录都有名字标志且更高效.

5).只创建确实需要的索引。索引有利于检索记录,但是不利于快速保存记录。如果总是要在表的组合字段上做搜索,那么就在这些字段上创建索引。索引的第一部分必须是最常使用的字段.如果总是需要用到很多字段,首先就应该多复制这些字段,使索引更好的压缩。

6).所有数据都得在保存到数据库前进行处理。

7).所有字段都得有默认值。

8).在某些情况下,把一个频繁扫描的表分成两个速度会快好多。在对动态格式表扫描以取得相关记录时,它可能使用更小的静态格式表的情况下更是如此。

2.系统的用途

1).尽量使用长连接.

2).explain 复杂的SQL语句。

3).如果两个关联表要做比较话,做比较的字段必须类型和长度都一致.

4).LIMIT语句尽量要跟order by或者 distinct.这样可以避免做一次full table scan.

5).如果想要清空表的所有记录,建议用truncate table tablename而不是delete from tablename.

6).能使用STORE PROCEDURE 或者 USER FUNCTION的时候.

7).在一条insert语句中采用多重纪录插入格式.而且使用load data infile来导入大量数据,这比单纯的indert快好多.

8).经常OPTIMIZE TABLE 来整理碎片.

9).还有就是date 类型的数据如果频繁要做比较的话尽量保存在unsigned int 类型比较快。

3.系统的瓶颈

1).磁盘搜索. 并行搜索,把数据分开存放到多个磁盘中,这样能加快搜索时间.

2).磁盘读写(IO) 可以从多个媒介中并行的读取数据。

3).CPU周期 数据存放在主内存中.这样就得增加CPU的个数来处理这些数据。

4).内存带宽 当CPU要将更多的数据存放到CPU的缓存中来的话,内存的带宽就成了瓶颈.

JSP的9个内置对象和4个域对象?

(1)9个内置对象:

①request:封装客户端的请求,其中包含来自GET或POST请求的参数;

②application:封装服务器运行环境的对象;

③response:封装服务器对客户端的响应;

④session:封装用户会话的对象;

⑤page:JSP页面本身(相当于Java程序中的this);

⑥config:Web应用的配置对象;

⑦exception:封装页面抛出异常的对象;

⑧pageContext:通过该对象可以获取其他对象;

⑨out:用来传送回应的输出;

(2)4个域对象

①page(当前页面)

②reques(一次请求)

③session( 一次会话)

④application(整个会话)

Maven的工程类型有哪些?

 

  • POM工程

POM工程是逻辑工程。用在父级工程或聚合工程中。用来做jar包的版本控制。

 

  • JAR工程

将会打包成jar用作jar包使用。即常见的本地工程 - Java Project。

  • WAR工程

 

将会打包成war,发布在服务器上的工程。如网站或服务。即常见的网络工程 - Dynamic Web Project。war工程默认没有WEB-INF目录及web.xml配置文件,IDE通常会显示工程错误,提供完整工程结构可以解决。

 

Maven常用命令有哪些?

  • install 本地安装, 包含编译,打包,安装到本地仓库

  • 编译 - javac

  • 打包 - jar, 将java代码打包为jar文件 安装到本地仓库 - 将打包的jar文件,保存到本地仓库目录中。

  • clean 清除已编译信息。 删除工程中的target目录。

  • compile 只编译。 javac命令

  • deploy 部署。 常见于结合私服使用的命令。 相当于是install+上传jar到私服。 包含编译,打包,安装到本地仓库,上传到私服仓库。

  • package 打包。 包含编译,打包两个功能。

foreach的语句格式:

for(元素类型t 元素变量x : 遍历对象obj){

引用了x的java语句;

}

面向对象都有哪些特性以及你对这些特性的理解

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

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

3) 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调 用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做 两件事:

  1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);

  2. 对象造型(用父类型引用引用子类型对 象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

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

String 类是 final 类,不可以被继承。

为什么函数不能根据返回类型来区分重载?

因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。

char 型变量中能不能存储一个中文汉字,为什么?

char 类型可以存储一个中文汉字,因为Java中使用的编码是 Unicode,一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。

抽象类(abstract class)和接口(interface)有什么异同?

不同点:

抽象类:

1.抽象类中可以定义构造器

2.可以有抽象方法和具体方法

3.接口中的成员全都是public的

4.抽象类中可以定义成员变量

5.有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法

6.抽象类中可以包含静态方法

7.一个类只能继承一个抽象类

接口:

1.接口中不能定义构造器

2.方法全部都是抽象方法

3.抽象类中的成员可以是 private、默认、protected、public

4.接口中定义的成员变量实际上都是常量

5.接口中不能有静态方法

6.一个类可以实现多个接口

相同:

1.不能够实例化

2.可以将抽象类和接口类型作为引用类型

3.一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类

break和continue的区别?

break和continue都是用来控制循环的语句。 break用于完全结束一个循环,跳出循环体执行循环后面的语句。 continue用于跳过本次循环,执行下次循环。

请写出你最常见的5个RuntimeException

1)java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。

2)java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序 试图通过字符串来加载某个类时可能引发异常。

3)java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。

4)java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。

5)java.lang.IllegalArgumentException 方法传递参数错误。

6)java.lang.ClassCastException 数据类型转换异常。

throw和throws的区别

throw:

1)throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。

2)throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw一定是抛出了某种异常。

throws:

1)throws语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。

2)throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。

3)throws表示出现异常的一种可能性,并不一定会发生这种异常。

final、finally、finalize的区别

1)final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。

2)finally:异常处理语句结构的一部分,表示总是执行。

3)finalize:Object类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等

Math.round(11.5)等于多少?Math.round(- 11.5) 又等于多少?

Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加 0.5然后进行取整。

数组没有 length()方法,而是有 length 的属性。String 有 length()方法

short s1 = 1; s1 = s1 + 1; 有错吗?short s1 = 1; s1 += 1 有错吗;

前者不正确,后者正确。对于 short s1 = 1; s1 = s1 + 1;由于1是 int 类型,因此 s1+1 运算结果也是 int 型, 需要强制转换类型才能赋值给 short 型。而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

下面Integer类型的数值比较输出的结果为?

首先需要注意的是 f1、f2、f3、f4 四个变量都是 Integer 对象引用,所以下面的==运算比较的不是值而是引用。

如果整型字面量的值在-128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池中的 Integer 对象,所以上面的面试题中 f1==f2 的结果是 true,而 f3==f4 的结果是 false。

String、StringBuffer、StringBuilder的区别?

(1)、可变不可变

String:字符串常量,在修改时不会改变自身;若修改,等于重新生成新的字符串对象。

StringBuffer:在修改时会改变对象自身,每次操作都是对StringBuffer对象本身进行修改,不是生成新的对象;使用场景:对字符串经常改变情况下,主要方法:append(),insert()等。

2)、线程是否安全

String:对象定义后不可变,线程安全。

StringBuffer:是线程安全的(对调用方法加入同步锁),执行效率较慢,适用于多线程下操作字符串缓冲区大量数据。

StringBuilder:是线程不安全的,适用于单线程下操作字符串缓冲区大量数据。

3)、共同点

StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。 StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是 StringBuffer 会在方法上加 synchronized 关键字,进行同步。最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。

Java中有几种类型的流

  • 按照流的方向:输入流(inputStream)和输出流(outputStream)。
  • 按照实现功能分:节点流和处理流。

节点流:可以从或向一个特定的地方(节点)读写数据。如 FileReader

处理流:对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。

  • 按照处理数据的单位:字节流和字符流。

字节流继承于 InputStream 和 OutputStream,字符流继承于InputStreamReader 和OutputStreamWriter。

字节流如何转为字符流

  • 字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象。

  • 字节输出流转字符输出流通过OutputStreamWriter实现,该类的构造函数可以传入OutputStream对象。

如何将一个java对象序列化到文件里

在java中能够被序列化的类必须先实现Serializable接口,该接口没有任何抽象方法只是起到一个标记作用。

  1. //对象输出流 
    ObjectOutputStream objectOutputStream =
        new ObjectOutputStream(new FileOutputStream(new File("D://obj")));
     objectOutputStream.writeObject(new User("zhangsan", 100)); 
        objectOutputStream.close();

     

  2. //对象输入流 
    ObjectInputStream objectInputStream = 
      new ObjectInputStream(new FileInputStream(new File("D://obj"))); 
     User user = (User)objectInputStream.readObject(); 
       System.out.println(user); 
       objectInputStream.close();

字节流和字符流的区别

  • 字节流可以处理所有类型数据,如:图片,MP3,AVI 视频文件,而字符流只能处理字符数据。

  • 只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。

  • 字节流主要是操作 byte类型数据,以 byte 数组为准,主要操作类就是OutputStream、 InputStream

  • 字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。

List的三个子类的特点

ArrayList 底层结构是数组,底层查询快,增删慢。

LinkedList 底层结构是链表型的,增删快,查询慢。

voctor 底层结构是数组 线程安全的,增删慢,查询慢。

List和Map、Set的区别

结构特点
  • List 和 Set 是存储单列数据的集合,Map 是存储键和值这样的双列数据的集合;
  • List 中存储的数据是有顺序,并且允许重复;
  • Map 中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的,Set 中存储的数据是无序的,且不允许有重复,但元素在集合中的位置由元素的hashcode决定,位置是固定的
实现类
  • List接口有三个实现类(LinkedList:基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢;ArrayList:基于数组实现,非线程安全的,效率高,便于索引,但不便于插入删除;Vector:基于数组实现,线程安全的,效率低)。

  • Map 接口有三个实现类(HashMap:基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null键;HashTable:线程安全,低效,不支持null值和null键;LinkedHashMap:是 HashMap的一个子类,保存了记录的插入顺序;SortMap接口:TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序)。

  • Set 接口有两个实现类(HashSet:底层是由 HashMap 实现,不允许集合中有重复的值,使用该方式时需要重写 equals()和 hashCode()方法;LinkedHashSet:继承与 HashSet,同时又基于 LinkedHashMap 来进行实现,底层使用的是LinkedHashMp)。

    区别
  • List 集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get(i)方法来获取集合中的元素;

  • Map中的每一个元素包含一个键和一个值,成对出现,键对象不可以重复,值对象可以重复;

  • Set集合中的对象不按照特定的方式排序,并且没有重复对象,但它的实现类能对集合中的对象按照特定的方式排序,例如TreeSet类,可以按照默认顺序,也可以通过实现Java.util.Comparator<Type>接口来自定义排序方式

数组和链表的区别

数组:数组是将元素在内存中连续存储的

优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高

缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据两比较的时候,有可能会出现越界的情况,数据比较的时候,又有可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低 。

链表:动态申请内存空间,不需要像数组需要提前申请好内存的大小

链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系 。

List a=new ArrayList()和ArrayList a =new ArrayList()的区别?

List list = new ArrayList();这句创建了一个ArrayList的对象后把上溯到了List。此时它是一个List对象了,有些ArrayList 有但是 List 没有的属性和方法,它就不能再用了。

而 ArrayList list=new ArrayList();创建一对象则保留了ArrayList的所有属性。 所以需要用到ArrayList独有的方法的时候不能用前者。

要对集合更新操作时,ArrayList和LinkedList哪个更适合?

1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

2.如果集合数据是对于集合随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。

3.如果集合数据是对于集合新增和删除操作 add 和 remove,LinedList 比较占优势,因为 ArrayList 要移动数据。

多线程的创建方式

(1)、继承 Thread类:但Thread本质上也是实现了Runnable 接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

(2)、实现Runnable接口的方式实现多线程,并且实例化Thread,传入自己的Thread实例,调用run( )方法

在 java 中 wait 和 sleep 方法的不同?

最大的不同是在等待时wait会释放锁,而sleep一直持有锁。wait通常被用于线程间交互,sleep通常被用于暂停执行。

synchronized 和 volatile 关键字的作用

1.volatile仅能使用在变量级别; synchronized则可以使用在变量、方法、和类级别的

2.volatile仅能实现变量的修改可见性,并不能保证原子性; synchronized则可以保证变量的修改可见性和原子性

3.volatile不会造成线程的阻塞; synchronized可能会造成线程的阻塞。

4.volatile标记的变量不会被编译器优化; synchronized标记的变量可以被编译器优化

常用的线程池有哪些

newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。

newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。

newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

请叙述一下您对线程池的理解

(如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)

合理利用线程池能够带来三个好处:

 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

什么是线程池,如何使用

线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。 在JDK的java.util.concurrent.Executors中提供了生成多种线程池的静态方法。

ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);

ScheduledExecutorService newScheduledThreadPool =Executors.newScheduledThreadPool

ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();

然后调用他们的 execute 方法即可。

线程池的启动策略?

1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

2、当调用execute() 方法添加一个任务时,线程池会做如下判断:

                a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

                b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。

                c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;

                d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。

3、当一个线程完成任务时,它会从队列中取下一个任务来执行。

4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

如何控制某个方法允许并发访问线程的个数

可以使用Semaphore控制

同一个类中的 2 个方法都加了同步锁,多个线程能同时访问同一个类中的这两个方法吗?

这个问题需要考虑到Lock与synchronized 两种实现锁的不同情形。

  • Lock可以让等待锁的线程响应中断,Lock获取锁,之后需要释放锁,多个线程不可 访问同一个类中的2个加了Lock锁的方法。

  • 使用synchronized时,当我们访问同一个类对象的时候,是同一把锁,所以可以访问 该对象的其他synchronized方法。

什么情况下导致线程死锁,遇到线程死锁该怎么解决

  • 首先,死锁的定义:所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

  • 死锁产生的必要条件:

互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。

不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。

请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。

  • 如何避免死锁 :

1)加锁顺序(线程按照一定的顺序加锁)

2)加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

Java 中多线程间的通信怎么实现?

线程通信的方式:

1.共享变量 。线程间通信可以通过发送信号,发送信号的一个简单方式是在共享对象的变量里设置信号值。线程A在一个同步块里设置boolean型成员变量hasDataToProcess为true,线程B也在同步块里读取hasDataToProcess这个成员变量。

2.wait/notify机制 。以资源为例,生产者生产一个资源,通知消费者就消费掉一个资源,生产者继续生产资源,消费者消费资源,以此循环。

同步线程及线程调度相关的方法?

  • wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;

  • notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;

  • notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁 的线程才能进入就绪状态;

启动一个线程是调用 run()方法还是 start()方法

启动一个线程是调用 start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。 run()方法是线程启动后要进行回调(callback)的方法

说说你对Java中反射的理解

Java 中的反射首先是能够获取到 Java 中要反射类的字节码,获取字节码有三种方法:

 1.Class.forName(className)

2.类名.class

3.this.getClass()。

然后将字节码中的方法,变量,构造函数等映射成相应的Method、Filed、Constructor等类,这些类提供了丰富的方法可以被我们所使用

写一个ArrayList的动态代理类(笔试题)

final List<String> list = new ArrayList<String>();

List<String> proxyInstance =(List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(),

list.getClass().getInterfaces(),

new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(list, args);

}

});

proxyInstance.add("你好");

System.out.println(list);

动静态代理的区别,什么场景使用

           静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。 动态代理是实现JDK里的 InvocationHandler接口的 invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。 还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。 AOP编程就是基于动态代理实现的,比如著名的Spring框架、Hibernate框架等等都是动态代理的使用例子。

单例设计模式

最好理解的一种设计模式,分为懒汉式和饿汉式。

懒汉式:

 

public 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; 
 } 
}

 

 

饿汉式:

 

public class Singleton { 
// 直接创建对象 
  public static Singleton instance = new Singleton(); 
 // 私有化构造函数 
  private Singleton() { 
 } 
// 返回对象实例 
  public static Singleton getInstance() { 
    return instance; 
  } 
}

 

工厂设计模式

工厂模式分为工厂方法模式和抽象工厂模式。

  • 工厂方法模式:

 

工厂方法模式分为三种:

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

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

静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

 

  • 抽象工厂模式:

 

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

 

建造者模式

      工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的 Test 结合起来得到的。

适配器设计模式

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

装饰模式

装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

策略模式

       策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

观察者模式

      观察者模式很好理解,类似于邮件订阅和 RSS 订阅,当我们浏览一些博客或 wiki 时,经常会看到 RSS 图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

java内存分配

1、基础数据类型直接在空间分配;

2、方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;

3、引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;

​4、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;

5、局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC 回收;

6、方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放;

7、字符串常量在 DATA 区域分配 ,this 在堆空间分配;

8、数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小

Java中引用类型都有哪些?(重要)

Java中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

  • 强引用:如果一个对象被被人拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题

  • 软引用:如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。软引用是主要用于内存敏感的高速缓存。在 jvm 报告内存不足之前会清除所有的软引用,这样以来 gc 就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。

  • 弱引用:如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

  • 虚引用:"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

heap(堆)和stack(栈)有什么区别

  1. 申请方式

    stack:由系统自动分配。

heap:需要程序员自己申请,并指明大小。

  1. 申请后系统的响应

stack:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

        3.申请大小的限制

stack:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

  1. 申请效率的比较

 

stack:由系统自动分配,速度较快。但程序员是无法控制的。

heap:由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

 

  1. heap和stack中的存储内容

stack: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

heap:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

  1. 数据结构层面的区别

这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。

解释内存中的栈 (stack) 、堆 (heap) 和方法区 (method area) 的用法

           通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用 JVM 中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为 Eden、Survivor(又可分为 From Survivor 和 To Survivor、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT 编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的 100、"hello"和常量都是放在常量池中,常量池是方法区的一部分。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过 JVM 的启动参数来进行调整,栈空间用光了会引发 StackOverflowError,而堆和常量池空间不足则会引发 OutOfMemoryError。 String str = new String("hello"); 上面的语句中变量 str 放在栈上,用 new 创建出来的字符串对象放在堆上,而"hello"这个字面量是放在方法区 的。

Java的类加载器的种类都有哪些?

1、根类加载器(Bootstrap) --C++写的 ,看不到源码

2、扩展类加载器(Extension) --加载位置 :jre\lib\ext中

3、系统(应用)类加载器(System\App) --加载位置 :classpath中

4、自定义加载器(必须继承ClassLoader)

类什么时候被初始化?

1)创建类的实例,也就是new一个对象

2)访问某个类或接口的静态变量,或者对该静态变量赋值

3)调用类的静态方法

4)反射(Class.forName("com.lyj.load"))

5)初始化一个类的子类(会首先初始化子类的父类)

6)JVM启动时标明的启动类,即文件名和类名相同的那个类

只有这6中情况才会导致类的类的初始化。

类的初始化步骤:

1)如果这个类还没有被加载和链接,那先进行加载和链接

2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)

3)假如类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。

java 是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是:

1) 类加载体系

2) .class文件检验器

3) 内置于Java虚拟机(及语言)的安全特性

4) 安全管理器及Java API

类的加载体系:

 

java程序中的 .java文件编译完会生成 .class文件,而 .class文件就是通过被称为类加载器的ClassLoader加载的,而ClassLoder在加载过程中会使用“双亲委派机制”来加载 .class文件。

前面谈到了ClassLoader的几类加载器,而ClassLoader使用双亲委派机制来加载class文件的。ClassLoader的双亲委派机制是这样的(这里先忽略掉自定义类加载器CustomClassLoader):

1)当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

2)当 ExtClassLoader 加载一个 class 时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

3)如果 BootStrapClassLoader 加载失败(例如在$JAVA_HOME$/jre/lib 里未查找到该 class),会使用ExtClassLoader来尝试加载;

4)若 ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。

描述一下JVM加载class

JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java 中的类加载器是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于 Java 的跨平台性,经过编译的 Java 源程序并不是一个可执行程序,而是一个或多个类文件。当 Java 程序需要使用某个类时,JVM 会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class 文件中的数据读入到内存中,通常是创建一个字节数组读入.class 文件,然后产生与所加载类对应的 Class 对象。加载完成后,Class 对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备 (为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后 JVM 对类进行初始化,包括: 如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类; 如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader 的子类)。

获得一个类对象有哪些方式

  • 类型.class,例如:String.class
  • 对象.getClass(),例如:”hello”.getClass()
  • Class.forName(),例如:Class.forName(“java.lang.String”)

既然有GC机制,为什么还会有内存泄露的情况

      理论上 Java 因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是 Java 被广泛使用于服务器端编程的一个重要原因)。然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被 GC 回收,因此也会导致内存泄露的发生。

例如 hibernate 的 Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。

Java中为什么会有GC机制呢?

• 安全性考虑;

• 减少内存泄露;

• 减少程序员工作量。

对于Java的GC哪些内存需要回收

 

            内存运行时 JVM 会有一个运行时数据区来管理内存。它主要包括 5 大部分:程序计数器、虚拟机栈、本地方法栈、方法区、堆. 而其中程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡。例如栈中每一个栈帧中分配多少内存基本上在类结构确定是哪个时就已知了,因此这 3 个区域的内存分配和回收都是确定的,无需考虑内存回收的问题。 但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,我们只有在程序运行期间才会知道会创 建哪些对象,这部分内存的分配和回收都是动态的,GC主要关注的是这部分内存。 总而言之,GC 主要进行回收的内存是JVM中的方法区和堆;

 

Java的GC什么时候回收垃圾(如何判断一个对象已经死去)

 

     在Java,C#等语言中,比较主流的判定一个对象已死的方法是:可达性分析(Reachability Analysis). 所有生成的对象都是一个称为"GC Roots"的根的子树。从 GC Roots 开始向下搜索,搜索所经过的路径称为引用链,当一个对象到GC Roots没有任何引用链可以到达时,就称这个对象是不可达的(不可引用的),也就是可以被GC 回收了。 无论是引用计数器还是可达性分析,判定对象是否存活都与引用有关!那么,如何定义对象的引用呢?

 

     我们希望给出这样一类描述:当内存空间还够时,能够保存在内存中;如果进行了垃圾回收之后内存空间仍旧非常紧张,则可以抛弃这些对象。所以根据不同的需求,给出如下四种引用,根据引用类型的不同,GC回收时也会有不同的操作:

1)强引用(Strong Reference):Object obj = new Object();只要强引用还存在,GC永远不会回收掉被引用的对象。

2)软引用(Soft Reference):描述一些还有用但非必需的对象。在系统将会发生内存溢出之前,会把这些对象列入回收范围进行二次回收(即系统将会发生内存溢出了,才会对他们进行回收。

3)弱引用(Weak Reference):程度比软引用还要弱一些。这些对象只能生存到下次GC之前。当GC 工作时,无论内存是否足够都会将其回收(即只要进行GC,就会对他们进行回收。

4)虚引用(Phantom Reference):一个对象是否存在虚引用,完全不会对其生存时间构成影响。

  • 关于方法区中需要回收的是一些废弃的常量和无用的类。

1.废弃的常量的回收。这里看引用计数就可以了。没有对象引用该常量就可以放心的回收了。

2.无用的类的回收。什么是无用的类呢? A.该类所有的实例都已经被回收。也就是Java堆中不存在该类的任何实例; B.加载该类的ClassLoader已经被回收; C.该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

总而言之: 对于堆中的对象,主要用可达性分析判断一个对象是否还存在引用,如果该对象没有任何引用就应该被回收。而根据我们实际对引用的不同需求,又分成了4中引用,每种引用的回收机制也是不同的。 对于方法区中的常量和类,当一个常量没有任何对象引用它,它就可以被回收了。而对于类,如果可以判定它为无用类,就可以被回收了。

Java8中的lambda表达式要点

要点1:lambda表达式的使用位置

预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,可以用作返回类型,或lambda目标代码的参数。

要点2:lambda表达式和方法引用

lambda 表达式内可以使用方法引用,仅当该方法不修改 lambda 表达式提供的参数。

若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式

要点3:lambda表达式内部引用资源

lambda内部可以使用静态、非静态和局部变量,这称为lambda内的变量捕获。

要点4:lambda表达式也称闭包

Lambda表达式在Java中又称为闭包或匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。

要点5:lambda表达式的编译方式

Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。

要点6:lambda表达式的限制

lambda 表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在 lambda 内部修改定义在域外的变量。

在开发中遇到过内存溢出么?原因有哪些?解决方法有哪些?

引起内存溢出的原因有很多种,常见的有以下几种:

1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;

2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;

3.代码中存在死循环或循环产生过多重复的对象实体;

4.使用的第三方软件中的BUG; 5.启动参数内存值设定的过小;

内存溢出的解决方案:

  • 第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

  • 第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

  • 第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

重点排查以下几点:

1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。

2.检查代码中是否有死循环或递归调用。

3.检查是否有大循环重复产生新对象实体

4.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收

  • 第四步,使用内存查看工具动态查看内存使用情况。

说下原生jdbc操作数据库流程?

第一步:加载驱动 Class.forName()

第二步:连接数据库

第三步:获取statement对象

第四步:执行sql语句

第五步:关闭数据库

什么要使用PreparedStatement?

1、 PreparedStatement接口继承Statement, PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要于 Statement 对象。

2 、 作 为 Statement 的 子 类 , PreparedStatement 继承了 Statement 的 所 有 功 能 。

3、在 JDBC应用中,在任何时候都不要使用Statement,原因如下:

一、代码的可读性和可维护性:Statement需要不断地拼接,而PreparedStatement不会。

二、PreparedStatement尽最大可能提高性能:DB有缓存机制,相同的预编译语句再次被调用不会再次需要编译。

三、最重要的一点是极大地提高了安全性:Statement 容易被 SQL 注入,而 PreparedStatementc 传入的内容不会和sql语句发生任何匹配关系。

关系数据库中连接池的机制是什么?

前提:为数据库连接建立一个缓冲池。

1:从连接池获取或创建可用连接

2:使用完毕之后,把连接返回给连接池

3:在系统关闭前,断开所有连接并释放连接占用的系统资源

4:能够处理无效连接,限制连接池中的连接总数不低于或者不超过某个限定值。

其中有几个概念需要大家理解: 最小连接数是连接池一直保持的数据连接。如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费掉。 最大连接数是连接池能申请的最大连接数。如果数据连接请求超过此数,后面的数据连接请求将被加入到等待队列中,这会影响之后的数据库操作。

综上所述:数据库池连接数量一直保持一个不少于最小连接数的数量,当数量不够时,数据库会创建一些连接,直到一个最大连接数,之后连接数据库就会等待。

http常见的状态码有哪些

200 OK //客户端请求成功

301 Moved Permanently(永久移除),请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置

302 found 重定向

400 Bad Request //客户端请求有语法错误,不能被服务器所理解

401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用

403 Forbidden //服务器收到请求,但是拒绝提供服务

404 Not Found //请求资源不存在,eg:输入了错误的URL 500 Internal Server Error //服务器发生不可预期的错误

503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?name=zhagnsan&password=123456。POST把提交的数据则放置在是HTTP包的包体中

http中重定向和请求转发的区别

本质区别:转发是服务器行为,重定向是客户端行为。

重定向特点:两次请求,浏览器地址发生变化,可以访问自己web之外的资源,传输的数据会丢失。

请求转发特点:一次强求,浏览器地址不变,访问的是自己本身的web资源,传输的数据不会丢失。

Cookie和Session的区别

Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个 web 服务器存储 cookie。以后浏览器再给特定的web服务器发送请求时,同时会发送所有为该服务器存储的cookie。 Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。

Cookie和session的不同点:

1、无论客户端做怎样的设置,session都能够正常工作。当客户端禁用cookie时将无法使用cookie。

2、在存储的数据量方面:session能够存储任意的java对象,cookie只能存储String类型的对象。

session共享怎么做的(分布式如何实现session共享)

1、服务器实现的session复制或session共享,这类型的共享session是和服务器紧密相关的,比如webSphere或JBOSS在搭建集群时候可以配置实现session复制或session共享,但是这种方式有一个致命的缺点,就是不好扩展和移植,比如我们更换服务器,那么就要修改服务器配置。

2、利用成熟的技术做session复制,比如12306使用的gemfire,比如常见的内存数据库如redis或memorycache,这类方案虽然比较普适,但是严重依赖于第三方,这样当第三方服务器出现问题的时候,那么将是应用的灾难。

3、将 session维护在客户端,很容易想到就是利用cookie,但是客户端存在风险,数据不安全,而且可以存放的数据量比较小,所以将session维护在客户端还要对session中的信息加密。

         我们实现的方案可以说是第二种方案和第三种方案的合体,可以利用 gemfire 实现 session 复制共享,还可以将session维护在redis中实现session共享,同时可以将session维护在客户端的cookie中,但是前提是数据要加密。 这三种方式可以迅速切换,而不影响应用正常执行。我们在实践中,首选 gemfire 或者 redis 作为 session 共享的载体,一旦session不稳定出现问题的时候,可以紧急切换cookie维护session作为备用,不影响应用提供服务。

在单点登录中,如果cookie被禁用了怎么办

      单点登录的原理是后端生成一个 session ID,然后设置到 cookie,后面的所有请求浏览器都会带上 cookie,然后服务端从 cookie 里获取 session ID,再查询到用户信息。所以,保持登录的关键不是 cookie,而是通过 cookie 保存和传输的 session ID,其本质是能获取用户信息的数据。除了 cookie,还通常使用 HTTP 请求头来传输。但是这个请求头浏览器不会像 cookie 一样自动携带,需要手工处理。

什么是jsp,什么是Servlet?jsp和Servlet有什么区别?

     jsp本质上就是一个Servlet,它是Servlet的一种特殊形式 ,每个jsp页面都是一个servlet实例。 Servlet是由 Java提供用于开发 web服务器应用程序的一个组件,运行在服务端,由servlet 容器管理,用来生成动态内容。一个 servlet 实例是实现了特殊接口 Servlet 的 Java 类,所有自定义的 servlet 均必须实现 Servlet 接口。 区别: jsp是html页面中内嵌的Java代码,侧重页面显示; Servlet是html代码和Java代码分离,侧重逻辑控制,mvc设计思想中jsp位于视图层,servlet位于控制层。

Jsp运行机制:如下图

 

 

 

 

   JVM 只能识别 Java 类,并不能识别 jsp 代码!web 容器收到以.jsp 为扩展名的 url 请求时,会将访问请求交给tomcat 中 jsp 引擎处理,每个 jsp 页面第一次被访问时,jsp 引擎将 jsp 代码解释为一个 servlet 源程序,接着编译servlet源程序生成.class文件,再有web容器servlet引擎去装载执行servlet程序,实现页面交互。

jsp有哪些域对象和内置对象及他们的作用

   四大域对象:

(1)pageContext page域-指当前页面,在当前jsp页面有效,跳到其它页面失效

(2)request request域-指一次请求范围内有效,从http请求到服务器处理结束,返回响应的整个过程。在这个过程中使用forward(请求转发)方式跳转多个jsp,在这些页面里你都可以使用这个变量

(3)session session域-指当前会话有效范围,浏览器从打开到关闭过程中,转发、重定向均可以使用

(4)application context域-指只能在同一个web中使用,服务器未关闭或者重启,数据就有效

九大内置对象:

什么是xml,使用xml的优缺点,xml的解析器有哪几种,分别有什么区别?

  • xml是一种可扩展性标记语言,支持自定义标签(使用前必须预定义)使用DTD和XML Schema标准化XML结构。

  • 优点:用于配置文件,格式统一,符合标准;用于在互不兼容的系统间交互数据,共享数据方便;

  • 缺点:xml文件格式复杂,数据传输占流量,服务端和客户端解析xml文件占用大量资源且不易维护

Xml常用解析器有2种,分别是:DOM和SAX; 主要区别在于它们解析xml文档的方式不同。使用DOM解析,xml文档以DOM 树形结构加载入内存,而SAX采用的是事件模型

谈谈你对ajax的认识?

Ajax是一种创建交互式网页应用的的网页开发技术

Ajax的优势: 通过异步模式,提升了用户体验。 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用。 Ajax引擎在客户端运行,承担了一部分本来由服务器承担的工作,从而减少了大用户量下的服务器负载。 Ajax的最大特点: 可以实现局部刷新,在不更新整个页面的前提下维护数据,提升用户体验度。

jsonp原理

      JavaScript是一种在Web开发中经常使用的前端动态脚本技术。在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。 JavaScript 这个安全策略在进行多 iframe 或多窗口编程、以及 Ajax 编程时显得尤为重要。根据这个策略,在 baidu.com 下的页面中包含的 JavaScript 代码,不能访问在 google.com 域名下的页面内容;甚至不同的子域名之间的页面也不能通过 JavaScript 代码互相访问。对于 Ajax 的影响在于,通过 XMLHttpRequest 实现的Ajax 请求,不能向不同的域提交请求,例如,在 abc.example.com 下的页面,不能向 def.example.com 提交Ajax请求,等等。 然而,当进行一些比较深入的前端编程的时候,不可避免地需要进行跨域操作,这时候“同源策略”就显得过于苛刻。JSONP跨域GET请求是一个常用的解决方案,下面我们来看一下JSONP跨域是如何实现的,并且探讨下JSONP跨域的原理。 jsonp 的最基本的原理是:动态添加一个<script>标签,使用 script 标签的 src 属性没有跨域的限制的特点实现跨域。首先在客户端注册一个 callback, 然后把 callback 的名字传给服务器。此时,服务器先生成 json 数据。 然后以 javascript 语法的方式,生成一个 function , function 名字就是传递上来的参数 jsonp。最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。 客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义 好的 callback 函数里。

说一下常用的Linux命令

列出文件列表:ls 【参数 -a -l】

创建目录和移除目录:mkdir rmdir

用于显示文件后几行内容:tail

打包:tar -xvf

打包并压缩:tar -zcvf

查找字符串:grep

显示当前所在目录:pwd

创建空文件:touch

编辑器:vim vi

Linux中如何查看日志?

动态打印日志信息:tail –f 日志文件

Linux怎么关闭进程

通常用ps 查看进程PID ,用kill命令终止进程。

ps 命令用于查看当前正在运行的进程。 grep 是搜索 例如: ps -ef | grep java 表示查看所有进程里CMD是java的进程信息。 ps -aux | grep java -aux 显示所有状态 kill 命令用于终止进程。 例如: kill -9 [PID]

-9表示强迫进程立即停止。

事务的四大特征是什么?

数据库事务transanction正确执行的四个基本要素。ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)。

(1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

(2)一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。

(3)隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行 相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆, 必须串行化或序列化请 求,使得在同一时间仅有一个请求用于同一数据。

(4)持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

Mysql 中四种隔离级别分别是什么

读未提交(READ UNCOMMITTED):未提交读隔离级别也叫读脏,就是事务可以读取其它事务未提交的数据。

读已提交(READ COMMITTED):在其它数据库系统比如SQL Server默认的隔离级别就是提交读,已提交读隔离级别就是在事务未提交之前所做的修改其它事务是不可见的。

可重复读(REPEATABLE READ):保证同一个事务中的多次相同的查询的结果是一致的,比如一个事务一开始查询了一条记录然后过了几秒钟又执行了相同的查询,保证两次查询的结果是相同的,可重复读也是mysql的默认隔离级别。

可串行化(SERIALIZABLE):可串行化就是保证读取的范围内没有新的数据插入,比如事务第一次查询得到某个范围的数据,第二次查询也同样得到了相同范围的数据,中间没有新的数据插入到该范围中。

MySQL怎么创建存储过程

一、创建MySQL存储过程 下面代码创建了一个叫pr_add 的MySQL 存储过程,这个MySQL 存储过程有两个int 类型的输入参数 “a”、 “b”,返回这两个参数的和。

1)drop procedure if exists pr_add; (备注:如果存在pr_add的存储过程,则先删掉)

2)计算两个数之和(备注:实现计算两个整数之和的功能)

create procedure pr_add ( a int, b int ) begin declare c int;

if a is null then set a = 0;

end if; if b is null then set b = 0;

end if;

set c = a + b;

select c as sum;

二、调用 MySQL 存储过程

call pr_add(10, 20);

MySQL触发器怎么写

MySQL包含对触发器的支持。触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将 调用该对象,即表的操作事件触发表上的触发器的执行。 在MySQL中,创建触发器语法如下:

CREATE TRIGGER trigger_name

trigger_time

trigger_event ON tbl_name

FOR EACH ROW

trigger_stmt 

其中:

trigger_name:标识触发器名称,用户自行指定;

trigger_time:标识触发时机,取值为 BEFORE 或 AFTER;

trigger_event:标识触发事件,取值为 INSERT、UPDATE 或 DELETE;

tbl_name:标识建立触发器的表名,即在哪张表上建立触发器;

trigger_stmt:触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句。

由此可见,可以建立6种触发器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE

另外有一个限制是不能同时在一个表上建立2个相同类型的触发器,因此在一个表上最多建立6个触发器。

什么是存储过程,使用存储过程的好处

       存储过程(Stored Procedure )是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。

优点:

(1)允许模块化程序设计,就是说只需要创建一次过程,以后在程序中就可以调用该过程任意次。

(2)允许更快执行,如果某操作需要执行大量 SQL 语句或重复执行,存储过程比 SQL 语句执行的要快。

(3)减少网络流量,例如一个需要数百行的 SQL 代码的操作有一条执行语句完成,不需要在网络中发 送数百行代码。

(4) 更好的安全机制,对于没有权限执行存储过程的用户,也可授权他们执行存储过程。

Oracle中字符串用什么连接

Oracle中使用 || 这个符号连接字符串 如 ‘abc’ || ‘d’ 的结果是abcd。

Oracle中是如何进行分页查询的

Oracle 中使用rownum来进行分页, 这个是效率最好的分页方法,hibernate 也是使用 rownum 来进行Oralce分页的

select * from

( select rownum r,a from tabName where rownum <= 20 )

where r > 10

Oracle存储过程与SQL的对比?

优势:

1、提高性能

2、降低网络开销

3、便于进行代码移植

4、更强的安全性

劣势:

1、存储过程需要专门的数据库开发人员进行维护,但实际情况是,往往由程序开发员人员兼职

2、设计逻辑变更,修改存储过程没有SQL灵活

你觉得存储过程和SQL语句该使用哪个?

1、在一些高效率或者规范性要求比较高的项目,建议采用存储过程

2、对于一般项目建议采用参数化命令方式,是存储过程与SQL语句一种折中的方式

3、对于一些算法要求比较高,涉及多条数据逻辑,建议采用存储过程

Oracle触发器的作用有哪些

1)触发器可通过数据库中的相关表实现级联更改;通过级联引用完整性约束可以更有效地执行这些更改。

2)触发器可以强制比用 CHECK 约束定义的约束更为复杂的约束。与 CHECK 约束不同,触发器可以引用其它表中的列。例如,触发器可以使用另一个表中的 SELECT 比较插入或更新的数据,以及执行其它操作,如修改数据或显示用户定义错误信息。

3)触发器还可以强制执行业务规则

4)触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。

在千万级的数据库查询中,如何提高效率?

1)数据库设计方面

a. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

b. 应尽量避免在 where 子句中对字段进行 null 值判断, 可以在num上设置默认值0,确保表中num列没有null值

c. 索引并不是越多越好

d. 应尽可能的避免更新索引数据列

e. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型

f. 尽量使用表变量来代替临时表

g. 避免频繁创建和删除临时表,以减少系统表资源的消耗。

h. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

i. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

2)SQL语句方面

a. 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

b. 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描.

c. in 和 not in 也要慎用,否则会导致全表扫描

d. 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。

e. 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描

f. 不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用 索引。

g.尽量避免使用游标,因为游标的效率较差

h.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

i. 尽量避免大事务操作,提高系统并发能力。

3)java方面:重点内容

a.尽可能的少造对象。

b.合理摆正系统设计的位置。大量数据操作,和少量数据操作一定是分开的。大量的数据操作,肯定不是ORM框架搞定的。

c.使用jDBC链接数据库操作数据

d.控制好内存,让数据流起来,而不是全部读到内存再处理,而是边读取边处理;

e.合理利用内存,有的数据要缓存

SpringMVC的工作原理

a. 用户向服务器发送请求,请求被springMVC前端控制器DispatchServlet捕获;

b. DispatcherServle对请求URL进行解析,得到请求资源标识符(URL),然后根据该URL调用HandlerMapping将请求映射到处理器HandlerExcutionChain;

c. DispatchServlet根据获得Handler选择一个合适的HandlerAdapter适配器处理;

d. Handler对数据处理完成以后将返回一个ModelAndView()对象给DisPatchServlet;

e. Handler 返回的 ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet 通过ViewResolver试图解析器将逻辑视图转化为真正的视图View;

f. DispatcherServle 通过 model 解析出 ModelAndView()中的参数进行解析最终展现出完整的 view 并返回给客户端;

SpringMVC常用注解都有哪些

@requestMapping 用于请求 url 映射。


@RequestBody 注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。

@ResponseBody 注解实现将 controller 方法返回对象转化为 json 响应给客户。

如何开启注解处理器和适配器

我们在项目中一般会在 springmvc.xml 中通过开启 mvc:annotation-driven来实现注解处 理器和适配器的开启。

如何解决get和post乱码问题

解决 post 请求乱码:我们可以在 web.xml 里边配置一个 CharacterEncodingFilter 过滤器。 设置为 utf-8. 解决 get 请求的乱码:有两种方法。

  1. 修改 tomcat 配置文件添加编码与工程编码一致。

  2. 另 外 一 种 方 法 对 参 数 进 行 重 新 编 码 String userName = New String(Request.getParameter(“userName”).getBytes(“ISO8859-1”), “utf-8”);

谈谈你对Spring的理解

Spring是一个开源框架,为简化企业级应用开发而生。Spring可以是使简单的JavaBean实现以前只有EJB才能实现的功能。Spring是一个IOC和AOP容器框架。

Spring容器的主要核心是:

控制反转(IOC),传统的java开发模式中,当需要一个对象时,我们会自己使用new或者getInstance等直接或者间接调用构造方法创建一个对象。而在spring开发模式中,spring容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用spring提供的对象就可以了,这是控制反转的思想。

依赖注入(DI),spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。

面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP底层是动态代理,如果是接口采用JDK动态代理,如果是类采用CGLIB方式实现动态代理。

Spring中的设计模式

a. 单例模式——spring中两种代理方式,若目标对象实现了若干接口,spring使用jdk的java.lang.reflect.Proxy类代理。若目标兑现没有实现任何接口,spring使用CGLIB库生成目标类的子类。 单例模式——在spring的配置文件中设置bean默认为单例模式。

b. 模板方式模式——用来解决代码重复的问题。 比如:RestTemplate、JmsTemplate、JpaTemplate

c. 前端控制器模式——spring提供了前端控制器DispatherServlet来对请求进行分发。

d. 试图帮助(view helper)——spring提供了一系列的JSP标签,高效宏来帮助将分散的代码整合在试图中。

e. 依赖注入——贯穿于BeanFactory/ApplacationContext 接口的核心理念。

f. 工厂模式——在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring中使用beanFactory来创建对象的实例。

Spring的常用注解

注解装配在 spring 中默认是关闭的。所以需要在 spring 的核心配置文件中配置一下才能使用基于注解的装配模式。

配置方式如下: <context:annotation-config />

常用的注解: @Required:该注解应用于设值方法

@Autowired:该注解应用于有值设值方法、非设值方法、构造方法和变量。

@Qualifier:该注解和@Autowired搭配使用,用于消除特定bean自动装配的歧义。

Spring能帮我们做什么

a. Spring能帮我们根据配置文件创建及组装对象之间的依赖关系。 Spring根据配置文件来进行创建及组装对象间依赖关系,只需要改配置文件即可

b. Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。 Spring 面向切面编程能提供一种更好的方式来完成,一般通过配置方式,而且不需要在现有代码中添加任何额外代码,现有代码专注业务逻辑。

c. Spring能非常简单的帮我们管理数据库事务。 采用Spring,我们只需获取连接,执行SQL,其他事物相关的都交给Spring来管理了。

d. Spring还能与第三方数据库访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套JDBC访问模板,来方便数据库访问。

e. Spring还能与第三方Web(如Struts、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。

f. Spring能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。

BeanFactory 常用的实现类有哪些?

Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。常用的BeanFactory 实现有DefaultListableBeanFactory 、 XmlBeanFactory 、 ApplicationContext等

简单介绍一下Spring WEB 模块

Spring的WEB模块是构建在application context 模块基础之上,提供一个适合web应用的上下文。这个模块也包括支持多种面向web的任务,如透明地处理多个文件上传请求和程序级请求参数的绑定到你的业务对象。它也有对Jakarta Struts的支持。

解释Spring JDBC、Spring DAO和Spring ORM

  • Spring-DAO 并非 Spring 的一个模块,它实际上是指示你写 DAO 操作、写好 DAO 操作的一些规范。因此,对于访问你的数据它既没有提供接口也没有提供实现更没有提供模板。在写一个 DAO 的时候,你应该使用 @Repository 对其进行注解,这样底层技术(JDBC,Hibernate,JPA,等等)的相关异常才能一致性地翻译为相应的 DataAccessException 子类。
  • Spring-JDBC 提供了 Jdbc 模板类,它移除了连接代码以帮你专注于 SQL 查询和相关参数。Spring-JDBC 还提供了一个 JdbcDaoSupport,这样你可以对你的 DAO 进行扩展开发。它主要定义了两个属性:一个 DataSource 和一个 JdbcTemplate,它们都可以用来实现 DAO 方法。JdbcDaoSupport 还提供了一个将 SQL 异常转换为 Spring DataAccessExceptions 的异常翻译器。
  • Spring-ORM 是一个囊括了很多持久层技术(JPA,JDO,Hibernate,iBatis)的总括模块。对于这些技术中的每一个,Spring 都提供了集成类,这样每一种技术都能够在遵循 Spring 的配置原则下进行使用,并平稳地和 Spring 事务管理进行集成。

Spring配置文件有什么作用

Spring配置文件是个XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。

什么是Spring IOC 容器

IOC 控制反转:Spring IOC 负责创建对象,管理对象。通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。

有哪些不同类型的IOC(依赖注入)方式

Spring提供了多种依赖注入的方式。

1.Set注入

2.构造器注入

3.静态工厂的方法注入

4.实例工厂的方法注入

什么是Spring beans

Spring beans 是那些形成Spring应用的主干的java对象。它们被Spring IOC 容器初始化,装配,和管理。这些beans通过容器中配置的元数据创建。比如,以XML文件中<bean/> 的形式定义。 Spring 框架定义的beans都是单例 beans。

一个 Spring Beans的定义需要包含什么

一个 Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个 bean,它的生命周期详情及它的依赖。

你怎样定义类的作用域

当定义一个<bean> 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当 Spring 要在需要的时候每次生产一个新的 bean 实例,bean 的 scope 属性被指定为 prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性必须设为 singleton

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是线程安全的吗?

Spring框架中的单例bean不是线程安全的。

什么是Spring的内部bean

当一个 bean 仅被用作另一个 bean 的属性时,它能被声明为一个内部 bean,为了定义 inner bean,在Spring 的 基于XML的 配置元数据中,可以在 <property/><constructor-arg/> 元素内使用<bean/> 元素,内部bean通常是匿名的,它们的Scope一般是prototype

在 Spring中如何注入一个java集合

Spring提供以下几种集合的配置元素:

<list>类型用于注入一列值,允许有相同的值

<set> 类型用于注入一组值,不允许有相同的值

<props>类型用于注入一组键值对,键和值都只能为String类型。

<map>型用于注入一组键值对,键和值都可以为任意类型。

什么是bean的自动装配?

无须在Spring配置文件中描述javaBean之间的依赖关系(IOC容器会自动建立javabean之间的关联关系。

解释不同方式的自动装配

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

1)no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。

2)byName:通过参数名自动装配,Spring 容器在配置文件中发现 bean 的autowire 属性被设置成 byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。

3)byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。

4)constructor:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。

5)autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。

什么是基于Java的Spring注解配置? 给一些注解的例子

基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。 以@Configuration 注解为例,它用来标记类可以当做一个 bean 的定义,被 Spring IOC 容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。

什么是基于注解的容器配置?

相对于XML文件,注解型的配置依赖于通过字节码元数据装配组件,而非尖括号的声明。开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。

怎样开启注解装配

注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在 Spring 配置文件中配 context:annotation-config/元素

在Spring框架中如何更有效地使用JDBC?

使用 SpringJDBC 框架,资源管理和错误处理的代价都会被减轻。所以开发者只需写 statements 和 queries 从数据存取数据,JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate 。 JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变为基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理。

使用Spring通过什么方式访问Hibernate?

在Spring中有两种方式访问Hibernate:

1)控制反转 HibernateTemplate和 Callback。

2)继承 HibernateDAOSupport提供一个AOP 拦截器。

Spring支持的ORM框架有哪些?

Spring支持以下ORM: Hibernate、iBatis、JPA (Java Persistence API)、TopLink、JDO (Java Data Objects)、OJB

Spring的通知是什么?有哪几种类型?

通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。

Spring切面可以应用五种类型的通知:

1.前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。

2.正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。

3.异常返回通知[After throwing advice]:在连接点抛出异常后执行。

4.返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。

5.环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。

Mybatis中#和$的区别?

#相当于对数据 加上 双引号,$相当于直接显示数据

  1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id".

  2. $将传入的数据直接显示生成在 sql 中。如:order by $user_id$,如果传入的值是 111,那么解析成 sql 时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.

  1. #方式能够很大程度防止sql注入。

  2. $方式无法防止Sql注入。

  3. $方式一般用于传入数据库对象,例如传入表名.

  4. 一般能用#的就别用$.

Mybatis的编程步骤是什么样的

1、创建SqlSessionFactory

2、通过SqlSessionFactory创建 SqlSession

3、通过sqlsession执行数据库操作

4、调用session.commit()提交事务

5、调用session.close()关闭会话

JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的

  • 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

 

解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。

  • Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

 

解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

 

  • 向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数一一对应。

解决: Mybatis自动将java对象映射至sql语句。

  • 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

 

解决:Mybatis自动将sql执行结果映射至java对象。

 

使用MyBatis的mapper接口调用时有哪些要求

  1. Mapper接口方法名和mapper.xml中定义的每个sql的id相同

  2. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同

  3. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

  4. Mapper.xml文件中的namespace即是mapper接口的类路径。

Mybatis中一级缓存与二级缓存

  1. 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。

  2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。作用域为namespance指对该namespance对应的配置文件中所有的select操作结果都缓存,这样不同线程之间就可以共用二级缓存。启动二级缓存:在mapper配置文件中:<cache />

MyBatis在insert插入操作时返回主键ID

数据库为MySql时:

<insert id="insert" parameterType="com.test.User" keyProperty="userId" useGeneratedKeys="true" >

“keyProperty”表示返回的id要保存到对象的那个属性中,“useGeneratedKeys”表示主键id为自增长模式。MySQL中做以上配置就OK了

简单介绍一下Struts2

Struts2 框架是一个按照 MVC 设计模式设计的 WEB 层框架,是在 struts 1 和 WebWork 的技术基础上进行了合并的全新的框架。其全新的 Struts 2 的体系结构与 Struts 1 的体 系结构差别巨大。

Struts 2 以 WebWork 为核心,采用拦截器的机制来处理用户的请求, 这样的设计也使得业务逻辑控制器能够与 ServletAPI 完全脱离开。 
 我们可以把 struts2 理解为一个大大的 servlet,而这个 servlet 就是ActionServlet。struts2 在处理客户端请求时, 会先读取 web.xml 配置文件,根据前端控制器将符合条件的请求 分给各个不同的 Action 处理。 在此之前,会把 ActionServlet 会把数据封装成一个 javaBean。 
 Struts2 框架提供了许多的拦截器,在封装数据的过程中,我们可以对数据进行一些操 作,例如:数据校验等等。

当 Action 执行完后要返回一个结果视图,这个结果视图可以跟据 struts2 的配置文件中 配置,选择转发或者重定向。

Struts2中Action配置的注意事项有哪些

  1. name 包名称,在 struts2 的配置文件中,包名不能重复,name 并不是真正包名,只是为了管理 Action


  1. namespace 和 <action>的 name 属性,决定 Action 的访问路径 (以/开始 )

  2. extends 继承哪个包,通常开发中继承 struts-default 包 (struts-default 包 在 struts-default.xml 中定义 )【可以使用包中默认的拦截器和结果集】

拦截器和过滤器有哪些区别?

  • 拦截器是基于 java 的反射机制的,而过滤器是基于函数回调

  • 拦截器不依赖与 servlet 容器,而过滤器依赖与 servlet 容器


  • 拦截器只能对 action 请求起作用,而过滤器则可以对几乎所有的请求起作用

  • 拦截器可以访问 action 上下文、值栈里的对象,而过滤器不能

  • 在 action 的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一 次

Struts2的封装方式有哪些

一、属性封装

  1. 在action中设置成员变量,变量名与表单中的name属性值相同

  2. 生成变量的set方法

二、模型驱动(常用)

  1. action实现 ModeDriven 接口

  2. 在action里创建实体类对象

  3. 实现接口的 getModel方法并返回所创建的对象

三、表达式封装

  1. 在 action 中声明实体类

  2. 生成实体类的 set 和 get 方法

  3. 在表单输入项的 name 属性值里面写表达式

SpringMVC和Struts2的区别?

1、Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为 Struts2 中 Action 的一个方法可以对应一个 url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。

2、由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过 ModelMap 交回给框架,方法之间不共享变量,而 Struts2 搞的就比较乱,虽然方法之间也是独立的,但其所有 Action 变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。

3、由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。

4、 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。

5、SpringMVC 的入口是servlet,而Struts2 是 filter(这里要指出,filter 和servlet 是不同的。),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。

6、SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在 Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。

7、SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。

8、Spring MVC 和 Spring 是无缝的。从这个项目的管理和安全上也比 Struts2 高(当然 Struts2 也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。

9、 设计思想上,Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。

10、SpringMVC开发效率和性能高于Struts2。 11、SpringMVC可以认为已经100%零配置。

Struts2中的 # 和 % 分别是做什么的?

Struts2中有哪些常用结果类型

1)dispatcher :默认的请求转发的结果类型,Action 转发给 JSP

2) chain :Action转发到另一个Action (同一次请求)

3) redirect : 重定向,重定向到一个路径信息,路径信息没有限制(不在一个请求中),Action重定向到 JSP

4) redirectAction :Action重定向到另一个Action

5)stream :将原始数据作为流传递回浏览器端,该结果类型对下载的内容和图片非常有用。

6)freemarker :呈现freemarker模板。 7)plaintext :返回普通文本内容。

简述一下hibernate的开发流程

第一步:加载 hibernate 的配置文件,读取配置文件的参数(jdbc 连接参数,数据 库方言,hbm 表与对象关系映射文件) 


第二步:创建 SessionFactory 会话工厂(内部有连接池) 


第三步:打开 session 获取连接,构造 session 对象(一次会话维持一个数据连接, 
也是一级缓存) 


第四步:开启事务 


第五步:进行操作 
 第六步:提交事务 
 第七步:关闭 session(会话)将连接释放 
 第八步:关闭连接池 


hibernate中对象的三种状态

  • 瞬时态(临时态、自由态):不存在持久化标识 OID,尚未与 Hibernate Session 关联对象, 被认为处于瞬时态,失去引用将被 JVM 回收
  • 持久态:存在持久化标识 OID,与当前 session 有关联,并且相关联的 session 没有关闭 , 并且事务未提交 

  • 脱管态(离线态、游离态):存在持久化标识 OID,但没有与当前 session 关联,脱管状态 改变 hibernate 不能检测到


hibernate的缓存机制。

Hibernate缓存分为两层:Hibernate的一级缓存和Hibernate二级缓存。

1.Hibernate一级缓存(Session的缓存):

(1)Session实现了第一级Cache,属于事务级数据缓冲。一旦事务结束,缓存随之失效。一个Session的生命周期对应一个数据库事务或一个程序事务。

(2)Session-Cache总是被打开并且不能被关闭的。

(3)Session-Cache保证一个Session中两次请求同一个对象时,取得的对象是同一个Java实例,有时它可以避免不必要的数据冲突。

               a.在对于同一个对象进行循环引用时,不至于产生堆栈溢出。

               b.当数据库事务结束时,对于同一数据表行,不会产生数据冲突。因为对于数据库中的一行,最多有一个对象来表示它。

               c.一个事务中可能会有很多个处理单元,在每一个处理单元中做的操作都会立即被其他的数据单元得知。

2.Hibernate二级缓存(SessionFactory的缓存):

(1)二级缓存是SessionFactory范围内的缓存,所有的 Session共享同一个二级缓存。在二级缓存中保存持久化实例的散装形式的数据。

(2)持久化不同的数据需要不同的Cache策略,比如一些因素将影响Cache策略的选择:数据的读/写比例、数据表是否能被其他的应用程序所访问等。

(3)设置Hibernate二级缓存需要分两步:首先,确认使用什么数据并发策略。然后,配置缓存过期时间并设置Cache提供器。

Hibernate的查询方式有哪些

Hibernate的查询方式常见的主要分为三种: HQL, QBC(命名查询), 以及使用原生SQL查询(SqlQuery)

Hibernate和Mybatis的区别

两者相同点:

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

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

Mybatis优势:

1)MyBatis可以进行更为细致的SQL优化,可以减少查询字段。

2)MyBatis容易掌握,而Hibernate门槛较高。

Hibernate优势:

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

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

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

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

Hibernate和JDBC优缺点对比

相同点:

1)两者都是java数据库操作的中间件

2)两者对数据库进行直接操作的对象都是线程不安全的,都需要及时关闭。

3)两者都可对数据库的更新操作进行显式的事务处理。

不同点:

JDBC是SUN公司提供一套操作数据库的规范,使用java代码操作数据库。Hibernate是一个基于jdbc的主流持久化框架,对JDBC访问数据库的代码做了封装。

使用的SQL语言不同:JDBC使用的是基于关系型数据库的标准SQL语言,Hibernate使用的是HQL(Hibernate query language)语言。

操作的对象不同:JDBC 操作的是数据,将数据通过 SQL 语句直接传送到数据库中执行,Hibernate 操作的是持久化对象,由底层持久化对象的数据更新到数据库中。

数据状态不同:JDBC操作的数据是“瞬时”的,变量的值无法与数据库中的值保持一致,而Hibernate操作的数据是可持久的,即持久化对象的数据属性的值是可以跟数据库中的值保持一致的。

关于Hibernate的orm思想你了解多少?

ORM 指的是对象关系型映射(Object RelationShip Mapping ),指的就是我们通过创建实体类对象和数据库中的表关系进行一 一对应,来实现通过操作实体类对象来更改数据库里边的数据信息。这里边起到关键作用的是通过Hibernate的映射文件+Hibernate的核心配置文件

如何进行Hibernate的优化

(1)数据库设计调整。

(2)HQL优化。

(3)API的正确使用(如根据不同的业务类型选用不同的集合及查询API)。

(4)主配置参数(日志,查询缓存,fetch_size, batch_size等)。

(5)映射文件优化(ID生成策略,二级缓存,延迟加载,关联优化)。

(6)一级缓存的管理。

(7)针对二级缓存,还有许多特有的策略。

(8)事务控制策略

什么是 Hibernate 延迟加载?

          延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要 数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及 对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。 延迟加载的过程:通过代理(Proxy)机制来实现延迟加载。Hibernate从数据库获取某一个对象数据时、获取某一个对象的集合属性值时,或获取某一个对象所关联的另一个对象时,由于没有使用该对象的数据(除标识符外),Hibernate 并不从数据库加载真正的数据,而只是为该对象创建一个代理对象来代表这个对象,这个对象上的所有属性都为默认值;只有在真正需要使用该对象的数据时才创建这个真正的对象,真正从数据库中加载它的数据。

hibernate中get和load的区别?

(1)get是立即加载,load是延时加载。

(2)get 会先查一级缓存,再查二级缓存,然后查数据库;load 会先查一级缓存,如果没有找到,就创建代理对象,等需要的时候去查询二级缓存和数据库。(这里就体现load的延迟加载的特性。)

(3)get如果没有找到会返回null,load如果没有找到会抛出异常。

(4)当我们使用 session.load()方法来加载一个对象时,此时并不会发出 sql 语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出 sql 语句,从数据库中去查询我们的对象;相对于 load 的延迟加载方式,get 就直接的多,当我们使用session.get()方法来得到一个对象时,不管我们使不使用这个对象,此时都会发出sql语句去从数据库中查询出来。

比较Hibernate三种检索策略的优缺点

1、立即检索

优点:对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象;

缺点:   

  • select语句太多;
  • 可能会加载应用程序不需要访问的对象白白浪费许多内存空间;

2、延迟检索

优点:由应用程序决定需要加载哪些对象,可以避免可执行多余的 select 语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并且能节省内存空间;

缺点:应用程序如果希望访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化;

3、 迫切左外连接检索

优点:

  • 对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便地冲一个对象导航到与它关联的对象。
  • 使用了外连接,select语句数目少; 缺点:1、可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;
  • 复杂的数据库表连接也会影响检索性能;

 

posted @ 2019-10-08 11:48  一个名  阅读(130)  评论(0编辑  收藏  举报