1、public、private、protected、Friendly的区别与作用域
public,protected,friendly,private的访问权限如下:
第一句错,第二句不错。
Java规范:
a.高位转低位需要强制转换
b.低位转高位自动转.
复合赋值(E1 op=E2)等价于简单赋值(E1=(T)((E1) op (E2))),而(s1 += 1)表达式使用的是复合赋值操作符,复合赋值表达式自动地将所执行计算的结果转型为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
Error和Exception的联系
-
继承结构:Error和Exception都是继承于Throwable,RuntimeException继承自Exception。
-
Error和RuntimeException及其子类称为未检查异常(Unchecked exception),其它异常成为受检查异常(Checked Exception)。
Error和Exception的区别
-
Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。如Java.lang.*Error和Java.lang.OutOfMemoryError。对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。
-
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
运行时异常和受检查的异常
Exception又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception )。
-
RuntimeException:其特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try……catch捕获,也没有用throws抛出,还是会编译通过,如除数为零的ArithmeticException、错误的类型转换、数组越界访问和试图访问空指针等。处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。
-
受检查的异常(IOException等):这类异常如果没有try……catch也没有throws抛出,编译是通不过的。这类异常一般是外部错误,例如文件找不到、试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
-
ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦
-
LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引 但是缺点就是查找非常麻烦 要丛第一个索引开始
-
ArrayList和Vector都是用数组方式存储数据,此数组元素数要大于实际的存储空间以便进行元素增加和插入操作,他们都允许直接用序号索引元素,但是插入数据元素涉及到元素移动等内存操作,所以索引数据快而插入数据慢.
-
Vector使用了sychronized方法(线程安全),所以在性能上比ArrayList要差些.
-
LinkedList使用双向链表方式存储数据,按序号索引数据需要前向或后向遍历数据,所以索引数据慢,是插入数据时只需要记录前后项即可,所以插入的速度快.
arraylist和vector的区别?
1).同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程不安全的,不是同步的
2).数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半
1.heap是堆,stack是栈。
2.stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。
3.stack空间有限,heap的空间是很大的*区。在Java中,若只是声明一个对象,则先在栈内存中为其分配地址空间,若再new一下,实例化它,则在堆内存中为其分配地址。
4.举例:数据类型 变量名;这样定义的东西在栈区。如:Object a =null; 只在栈内存中分配空间。new 数据类型();或者malloc(长度); 这样定义的东西就在堆区如:Object b =new Object(); 则在堆内存中分配空间
-
throws用在方法声明中,表明当前方法在运行时会有异常抛出,需要在调用该方法的时候注意控制异常
-
throw用在方法体内,手动制造一个异常,中断代码的继续执行
-
try-catch-finally是一起使用的
当某段代码在运行期间可能会发生异常而终止执行时,使用。
结构为
try
{
//可能发生异常的代码
}
catch (异常类型 )
{
//异常发生时的处理方式
}
finally
{
//其他必须执行的语句
}
当try语句块中的代码在执行时发生异常,就会被catch捕获,进入catch语句块进行处理,如果没有发生异常就继续执行
finally语句块中的代码是一些必须执行的语句,这里的代码无论try中是否发生异常都会被执行。
- 在 Java 中,每个异常都是一个对象,它是 Throwable 类或其它子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws 和 finally。一般情况下是用 try 来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;
-
try 用来指定一块预防所有“异常”的程序;
catch 子句紧跟在 try 块后面,用来指定你想要捕捉的“异常”的类型;
throw 语句用来明确地抛出一个“异常”;
throws 用来标明一个成员函数可能抛出的各种“异常”;
Finally 为确保一段代码不管发生什么“异常”都被执行一段代码;
- throws关键字通常被应用在声明方法时,用来指定可能抛出的异常。多个异常可以使用逗号隔开。
-
throw关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即停止,它后面的语句都不执行。通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法声明中指明要跑出的异常;如果要捕捉throw抛出的异常,则必须使用try—catch语句。可见,throw可以主动抛出异常,也就是可以在try中抛出异常。
类的多少没有限制,不过一个java源文件最多只能有一个public 修饰的 class,没有也行,如果有这个.java文件的文件名得是public类的类名,编译.java文件的时候,它会给每一个类生成一个.class文件。
内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象变成了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。
java中的内存泄露的情况:
1.长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
检查java中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。
2.如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
3.当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。
Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变化的流均是由它们派生出来的.
字符流和字节流是根据处理数据的不同来区分的。字节流按照8位传输,字节流是最基本的,所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。
1.字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;
2. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。
读文本的时候用字符流,例如txt文件。读非文本文件的时候用字节流,例如mp3。理论上任何文件都能够用字节流读取,但当读取的是文本数据时,为了能还原成文本你必须再经过一个转换的工序,相对来说字符流就省了这个麻烦,可以有方法直接读取。
字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节, 操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!
1.字节流:继承于InputStream \ OutputStream。
OutputStream提供的方法:
void write(int b):写入一个字节的数据
void write(byte[] buffer):将数组buffer的数据写入流
void write(byte[] buffer,int offset,int len):从buffer[offset]开始,写入len个字节的数据
void flush():强制将buffer内的数据写入流
void close():关闭流
InputStream提供的方法:
int read():读出一个字节的数据,如果已达文件的末端,返回值为-1
int read(byte[] buffer):读出buffer大小的数据,返回值为实际所读出的字节数
int read(byte[] buffer,int offset,int len)
int available():返回流内可供读取的字节数目
long skip(long n):跳过n个字节的数据,返回值为实际所跳过的数据数
void close():关闭流
2.字符流,继承于InputStreamReader \ OutputStreamWriter。
字符流的类:1),BufferedReader是一种过滤器(filter)(extends FilterReader)。过滤
器用来将流的数据加以处理再输出。构造函数为:
BufferedReader(Reader in):生成一个缓冲的字符输入流,in为一个读取器
BufferedReader(Reader in,int size):生成一个缓冲的字符输入流,并指定缓冲区的大小为size
public class IOStreamDemo { public void samples() throws IOException { //1. 这是从键盘读入一行数据,返回的是一个字符串 BufferedReader stdin =new BufferedReader(new InputStreamReader(System.in)); System.out.print("Enter a line:"); System.out.println(stdin.readLine());
//2. 这是从文件中逐行读入数据
BufferedReader in = new BufferedReader(new FileReader("IOStreamDemo.java")); String s, s2 = new String(); while((s = in.readLine())!= null) s2 += s + "\n"; in.close();
//3. 这是从一个字符串中逐个读入字节 StringReader in1 = new StringReader(s2); int c; while((c = in1.read()) != -1) System.out.print((char)c);
//4. 这是将一个字符串写入文件 try { BufferedReader in2 = new BufferedReader(new StringReader(s2)); PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("IODemo.out"))); int lineCount = 1; while((s = in2.readLine()) != null ) out1.println(lineCount++ + ": " + s); out1.close(); } catch(EOFException e) { System.err.println("End of stream"); } } }
对于上面的例子,需要说明的有以下几点:
1. InputStreamReader是InputStream和Reader之间的桥梁,由于System.in是字节流,需要用它来包装之后变为字符流供给BufferedReader使用。
3. PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("IODemo.out")));
这句话体现了Java输入输出系统的一个特点,为了达到某个目的,需要包装好几层。首先,输出目的地是文件IODemo.out,所以最内层包装的是FileWriter,建立一个输出文件流,接下来,我们希望这个流是缓冲的,所以用BufferedWriter来包装它以达到目的,最后,我们需要格式化输出结果,于是将PrintWriter包在最外层。
Java流有着另一个重要的用途,那就是利用对象流对对象进行序列化。
在一个程序运行的时候,其中的变量数据是保存在内存中的,一旦程序结束这些数据将不会被保存,一种解决的办法是将数据写入文件,而Java中提供了一种机制,它可以将程序中的对象写入文件,之后再从文件中把对象读出来重新建立。这就是所谓的对象序列化。Java中引入它主要是为了RMI(Remote Method Invocation)和Java Bean所用,不过在平时应用中,它也是很有用的一种技术。
不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个static方法内部发出对非static方法的调用。
在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。Clone 有缺省行为,super.clone();他负责产生正确大小的空间,并逐位复制。使用clone()来复制一个对象,clone()从Object类继承。所有具有clone功能的类都有一个特性,那就是它直接或间接地实现了Cloneable接口。
protected native Object clone() throws CloneNotSupportedException;
可以看出它是一个protected方法,所以我们不能简单地调用它;关键字native,表明这个方法使用Java以外的语言实现。
对于 object x,
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
以上返回的值都为true
要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。
1.浅复制与深复制概念
⑴浅复制(浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
⑵深复制(深克隆)
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
- java多线程实现方法主要有3种:继承Thread类、实现Runnable接口和使用ExecutorService、callable、future实现有返回结果的多线程。其中前2方式线程执行完后都没有返回值,只有最后一种有返回值。
- 继承Thread类启动线程的唯一方法是通过Thread类的start()实例方法。start()方法是一种native方法,它将启动一个新线程并执行run方法。这种方法通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
Java中交互方式分为同步和异步两种:
-
同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;
-
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。 区别:一个需要等待,一个不需要等待,在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。
-
哪些情况建议使用同步交互呢?比如银行的转账系统,对数据库的保存操作等等,都会使用同步交互操作,其余情况都优先使用异步交互。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
1、 mybatis配SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过 Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过 Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
工作流程:mybatis通过sqlSessionFactory创建sqlSession,sqlSession表示应用程序与数据库的会话,当用户使用mapper.xml文件中配置的的方法时,mybatis首先会解析sql动态标签为对应数据库sql的形式,并将其封装进MapperStatement对象,然后通过executor将sql注入数据库执行,并返回结果
(1)这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类;sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
(2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的 运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。 sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断;
(1)JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。另外,String实现了equals方法;而StringBuffer没有实现equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。
(2)StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改;通过某些方法调用可以改变该序列的长度和内容,可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致;
(3)StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。(如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。但将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。);
(4)HashMap是Hashtable的轻量级实现(非同步非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。
(1)基本数据类型包括byte、int、char、long、float、double、boolean和short;java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类;
(2)int i = Integer.parseInt([String]);i = Integer.parseInt([String],[int radix]);或者int i = Integer.valueOf(my_str).intValue();int转StringString s = String.valueOf(i);String s = Integer.toString(i);String s = "" + i;
(1)区别:Ajax是一门技术,它提供了异步更新的机制,使用客户端与服务器间交换数据而非整个页面文档,实现页面的局部更新。jQuery是一个框架,它对JS(JS是一门前端语言。)进行了封装,使其更方便使用。jQuery使得JS与Ajax的使用更方便
jsp负责客户端显示
servlet负责控制,转向等
(2)生命周期:Servlet 生命周期:Servlet 加载--->实例化--->服务--->销毁;
init():在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。
service():它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法 就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在 HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
destroy(): 仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个 Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
Ajax:ajax负责客户端与服务器直接联系,而不用jsp提交表单。一般ajax与servlet联系,也可直接与javabean联系
工作机制:客户端请求提交到DispatcherServlet
由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
DispatcherServlet将请求提交到Controller
Controller调用业务逻辑处理后,返回ModelAndView
DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
视图负责将结果显示到客户端
视图的好处:视图对象的定义比实体定义还要简单,不需要注解,不需要映射,但是唯一不同的是我们需要额外构造一个带有字段初始化的构造函数
1.Spring事务机制主要包括声明式事务和编程式事务,使用最多的就是声明式事务。声明式事物是具有AOP来实现的,但你并不需要真正精通AOP技术也能掌握声明式事务而编程式事物提供了一致的事务编程风格,通过模块化操作一致性地管理事务。
2.在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性:
事务的四种属性
Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事务管理的核心接口。
4.Spring事务管理的优势在哪
使用JDBC的时候开启事务使用Connection,而使用Hibernate时开启事务使用的是Session。所以如果我们使用传统的事务编程策略时,程序代码必须和具体的事务操作代码耦合。这样在不同的事务策略之间切换时开发者必须手动改代码。而如果使用Spring来管理事务则不会出现这种情况。
5.Spring如何管理事务(典型的策略模式:行为的抽象)
Spring事务策略是通过PlatformTransactionManager接口实现的,它是整个Spring事务的核心。它是对事务策略的一个高度抽象,不依赖于任何具体的事务策略,而对于底层的具体的事务策略它相应的有不同的实现类。而对于不同的事务策略的切换通常由Spring容器来负责管理,应用程序既无须与具体的事务API耦合,也无须与特定的实现类耦合而将应用和持久化技术,事务API彻底分离开来。
6.Spring管理事务的几个核心类
Spring事务管理的策略模式体现在哪?PlatformTransactionManager代表事务管理接口,但它并不知道底层到底如何管理事务的,而它只要求事务管理提供开始事务(getTransaction(TransactionDefinition definition)),提交事务(commit())和回滚事务(rollback())三个方法,具体的实现交给子类来完成。
SQL 注入简介:
SQL注入是最常见的攻击方式之一,它不是利用操作系统或其它系统的漏洞来实现攻击的,而是程序员因为没有做好判断,被不法用户钻了SQL的空子。
SQL注入攻击的总体思路
1.寻找到SQL注入的位置;
2.判断服务器类型和后台数据库类型;
3.针对不通的服务器和数据库特点进行SQL注入攻击。
下面我们先来看下什么是SQL注入:
比如在一个登陆界面,要求用户输入用户名和密码:
用户名: \' or 1=1 --
密 码:
点登陆,如若没有做特殊处理,而只是一条带条件的查询语句如:
String sql="select * from users where username=\'"+userName+"\' and password=\'"+password+"\' "
那么这个非法用户就很得意的登陆进去了.(当然现在的有些语言的数据库API已经处理了这些问题)
这是为什么呢?我们来看看这条语句,将用户输入的数据替换后得到这样一条语句:
select * from users where username=\'\' or 1=1 --\' and password=\'\'
为了更明白些,可以将其复制到SQL分析器中,将会发现,这条语句会将数据库的数据全部读出来,为什么呢?
很简单,看到条件后面 username=\'\' or 1=1 用户名等于 \'\' 或 1=1 那么这个条件一定会成功,然后后面加两个-,这意味着什么?没错,注释,它将后面的语句注释,让他们不起作用,这样就可以顺利的把数据库中的数据读取出来了。
这还是比较温柔的,如果是执行
select * from users where username=\'\' ;DROP Database (DB Name) --\' and password=\'\'
.......其他的您可以自己想象。。。
那么我们怎么来处理这种情况呢?下面我以Java为列给大家两种简单的方法:
第一种采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setString方法传值即可:
String sql= "select * from users where username=? and password=?;
PreparedStatement preState = conn.prepareStatement(sql);
preState.setString(1, userName);
preState.setString(2, password);
ResultSet rs = preState.executeQuery();
...
第二种是采用正则表达式,将包含有单引号(\'),分号(;) 和 注释符号(--)的语句给替换掉来防止SQL注入
public static String TransactSQLInjection(String str)
{
return str.replaceAll(".*([\';]+|(--)+).*", " ");
// 我认为 应该是return str.replaceAll("([\';])+|(--)+","");
}
userName=TransactSQLInjection(userName);
password=TransactSQLInjection(password);
Statement sta = conn.createStatement();
ResultSet rs = sta.executeQuery(sql);
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁
格式 SELECT…FOR UPDATE
例:select * from account where name="Erica" for update
这条 sql 语句锁定了 account 表中所有符合检索条件( name=“Erica” )的记录。 本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。
格式 SELECT…FOR UPDATE NOWAIT
该关键字的含义是“不用等待,立即返回”,如果当前请求的资源被其他会话锁定时,会发生阻塞,nowait可以避免这一阻塞。
乐观锁
乐观锁,大多是基于数据版本 ( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。
读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
总结:
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");
泛型好处:
泛型简单易用
类型安全 泛型的主要目标是实现Java的类型安全。 泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一个高的程度上验证这个类型
消除了强制类型转换 使得代码可读性好,减少了很多出错的机会
泛型的实现原理
泛型的实现是靠类型擦除技术 类型擦除是在编译期完成的 也就是在编译期 编译器会将泛型的类型参数都擦除成它的限定类型,如果没有则擦除为object类型之后在获取的时候再强制类型转换为对应的类型。 在运行期间并没有泛型的任何信息,因此也没有优化。
AVA的容器---List,Map,Set
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection接口
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个 Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的两个接口是List和Set。
List接口
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
LinkedList类
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。
Stack 类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
Set接口
Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
Map接口
请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
Hashtable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));
要取出一个数,比如2,用相应的key:
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);
由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
Hashtable是同步的。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap 的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
总结
如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
Java 集合类 map set list arraylist hashmap hashtable(转)
Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Hashtable和HashMap
它们的性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。
ArrayList和LinkedList
对 于处理一列数据项,Java提供了两个类ArrayList和LinkedList,ArrayList的内部实现是基于内部数组Object[],所以 从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别。
(1) 从上面的分析可知,在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列 数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能。
(2)而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
(3)如果在编程中,1,2两种情形交替出现,这时,你可以考虑使用List这样的通用接口,而不用关心具体的实现,在具体的情形下,它的性能由具体的实现来保证。
设置集合类的初始大小
在Java 集合框架中的大部分类的大小是可以随着元素个数的增加而相应的增加的,我们似乎不用关心它的初始大小,但如果我们考虑类的性能问题时,就一定要考虑尽可能 地设置好集合对象的初始大小,这将大大提高代码的性能,比如,Hashtable缺省的初始大小为101,载入因子为0.75,即如果其中的元素个数超过 75个,它就必须增加大小并重新组织元素,所以,如果你知道在创建一个新的Hashtable对象时就知道元素的确切数目如为110,那么,就应将其初始 大小设为110/0.75=148,这样,就可以避免重新组织内存并增加大小。
26、错误和异常的区别(Error vs Exception)
1) java.lang.Error: Throwable的子类,用于标记严重错误。合理的应用程序不应该去try/catch这种错误。绝大多数的错误都是非正常的,就根本不该出现的。
java.lang.Exception: Throwable的子类,用于指示一种合理的程序想去catch的条件。即它仅仅是一种程序运行条件,而非严重错误,并且鼓励用户程序去catch它。
2) Error和RuntimeException 及其子类都是未检查的异常(unchecked exceptions),而所有其他的Exception类都是检查了的异常(checked exceptions).
checked exceptions: 通常是从一个可以恢复的程序中抛出来的,并且最好能够从这种异常中使用程序恢复。比如FileNotFoundException, ParseException等。检查了的异常发生在编译阶段,必须要使用try…catch(或者throws)否则编译不通过。
unchecked exceptions: 通常是如果一切正常的话本不该发生的异常,但是的确发生了。发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的。比如ArrayIndexOutOfBoundException, ClassCastException等。从语言本身的角度讲,程序不该去catch这类异常,虽然能够从诸如RuntimeException这样的异常中catch并恢复,但是并不鼓励终端程序员这么做,因为完全没要必要。因为这类错误本身就是bug,应该被修复,出现此类错误时程序就应该立即停止执行。 因此,面对Errors和unchecked exceptions应该让程序自动终止执行,程序员不该做诸如try/catch这样的事情,而是应该查明原因,修改代码逻辑。
RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。
处理RuntimeException的原则是:如果出现 RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。其他(IOException等等)checked异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
Java的线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
在Java当中,线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。
第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
第五是死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。
1、写一个类继承自Thread类,重写run方法。用start方法启动线程
Thread Test = new Thread();
Test.start();
2、写一个类实现Runnable接口,实现run方法。用new Thread(Runnable target).start()方法来启动Test impelements Runnable;
Test t = new Test();
Thread test = new Thread(t);
test.start();
start()和run()方法的区别
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
答案:不能
解析:volatile关键字用在多线程同步中,可保证读取的可见性,JVM只是保证从主内存加载到线程工作内存的值是最新的读取值,而非cache中。但多个线程对
volatile的写操作,无法保证线程安全。例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。
A 是 B 否
答案:A
解析:Java创建对象的几种方式(重要):
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法。
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数。
31、
下面哪些是对称加密算法()
A DES B AES C DSA D RSA
答案:AB
解析:常用的对称加密算法有:DES、3DES、RC2、RC4、AES
常用的非对称加密算法有:RSA、DSA、ECC
使用单向散列函数的加密算法:MD5、SHA
32、抽象类和接口
抽象类遵循的原则:
(1)接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的。
(2)abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface,实现多重继承。接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用。
(3)在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是 static final的,不过在 interface中一般不定义数据成员),所有的成员方法默认都是 public abstract 类型的。
(4)abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"has-a"关系。
(5)实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法,一般的应用里,最*的是接口,然后是抽象类实现接口,最后才到具体类实现。抽象类中可以有非抽象方法。接口中则不能有实现方法。
(6)接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以在子类中重新赋值。
Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:
- 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
- 类可以实现很多个接口,但是只能继承一个抽象类
- 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
- 抽象类可以在不提供接口方法实现的情况下实现接口。
- Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
- Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
- 接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。
33、同步方法和同步代码块的区别是什么?
在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。
34、如何确保N个线程可以访问N个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
35、数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?
下面列出了Array和ArrayList的不同点:
- Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
- Array大小是固定的,ArrayList的大小是动态变化的。
- ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
- 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
ArrayList和LinkedList有什么区别?
ArrayList和LinkedList都实现了List接口,他们有以下的不同点:
-
ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
-
相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
-
LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
36、多线程的安全问题
总结来说,多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的。线程安全问 题多是由全局变量和静态变量引起的,当多个线程对共享数据只执行读操作,不执行写操作时,一般是线程安全的;当多个线程都执行写操作时,需要考虑线程同步来解决线程安全问题。
说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用, 从而保证了该变量的唯一性和准确性
37、java常见的数据类型是什么和对应的包装类是什么?java的隐式类型?assert什么时候使用?
(1)int, double, float, long, short, boolean, byte, char
(2)Integer.Double,Float,Long,Short,Boolean,Byte,Characher.
(3)java的隐式类型是int,byte,short,char都可以隐含转换为int
(4)一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的;
38、线程中sleep和wait的区别
(1)这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类;sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
(2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。 sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断;
39、string、stringbuffer和stringbuilder的区别?哪个更安全?为什么?hashmap为什么查询的比较快?
(1)JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffer来动态构造字符数据。另外,String实现了equals方法;而StringBuffer没有实现equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。
(2)StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改;通过某些方法调用可以改变该序列的长度和内容,可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致;
(3)StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。(如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。但将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。);
(4)HashMap是Hashtable的轻量级实现(非同步非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。
40、hibernat和mybaitis的优势和区别?haibernate的缓存机制有没有了解过?如何使用hibernate将数据永久保存到数据库中?mybaitates是如何实现查询的?hibernate有很多数据操作时候如何优化?
(1)mybaitis的优势:
· MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
· MyBatis容易掌握,而Hibernate门槛较高。
(2)hibernate的优势:
· Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射;
· Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便;
· Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
· Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
(3)区别:Hibernate功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。· iBATIS入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。
(4)Hibernate一级缓存是Session缓存,利用好一级缓存就需要对Session的生命周期进行管理好。建议在一个Action操作中使用一个Session。一级缓存需要对Session进行严格管理。Hibernate二级缓存是SessionFactory级的缓存。 SessionFactory的缓存分为内置缓存和外置缓存。内置缓存中存放的是SessionFactory对象的一些集合属性包含的数据(映射元素据及预定SQL语句等),对于应用程序来说,它是只读的。外置缓存中存放的是数据库数据的副本,其作用和一级缓存类似.二级缓存除了以内存作为存储介质外,还可以选用硬盘等外部存储设备。二级缓存称为进程级缓存或SessionFactory级缓存,它可以被所有session共享,它的生命周期伴随着SessionFactory的生命周期存在和消亡。
(5)mybatitis的查询:Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。mybatis是数据映射器(数据映射器层:用于在对象和数据库之间搬运数据,同时保证对象、数据库和数据映射器层本身相对独立。Martin Fowler 《企业应用架构模式》)把sql语句的参数与结果(即输入与输出)映射为类。为如何在类和数据库间建立映射带来了更大的灵活性。同时也更好的隔离了数据库设计和应用程序中使用的对象模型。
(6)hibernate 的持久化:Hibernate采用了更自然的面向对象的视角来持久化 Java 应用中的数据。使用 Hibernate 的开发者应该总是关注对象的状态(state),不必考虑 SQL 语句的执行。这部分细节已经由 Hibernate 掌管妥当,只有开发者在进行系统性能调优的时候才需要进行了解。
(6)mybatis的调优:MyBatis在Session方面和Hibernate的Session生命周期是一致的,同样需要合理的Session管理机制。MyBatis同样具有二级缓存机制。 MyBatis可以进行详细的SQL优化设计。
(7)hibernate如何实现优化:
· 制定合理的缓存策略;
· 尽量使用延迟加载特性;
· 采用合理的Session管理机制;
· 使用批量抓取,设定合理的批处理参数(batch_size);
· 进行合理的O/R映射设计
41、spring、hibernate、Struts的工作原理,为什么使用这些框架?简单说下struts2中数据传递情况? hibernate的三种状态和彼此的区别?使用hibernate如何分页?spring的事务隔离级别?ssh如何整合(spring核心流程)?
(1)spring的工作原理:
I.Spring内部最核心的就是IOC了,动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射,反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,根据xml Spring的配置文件来动态的创建对象,和调用对象里的方法。
II.Spring工作原理还有一个核心就是Aop这个就是面向切面的编程,可以为某一类对象进行监督和控制(也就是在调用这类对象的具体方法的前后去调用你指定的模块)从而达到对一个模块扩充的功能这些都是通过配置类达到的。
III.Spring目的:就是让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的(主要是通过反射机制)。
(2)hibernate的工作原理:
I.读取并解析hibernate.cfg.xml配置文件
II.读取并解析映射信息
III.创建SessionFactory
IV.负责被持久化对象CRUD操作,打开Sesssion
V.创建并启动事务Transation
VI.操作数据,持久化操作
VII.提交事务,关闭Session,关闭SesstionFactory;
(3)struts的工作原理同下:
工作流程:
(1)客户端提交一个HttpServletRequest请求(action或JSP页面)。
(2)请求被提交到一系列Filter过滤器,如ActionCleanUp和FilterDispatcher等。
(3)FilterDispatcher是Struts2控制器的核心,它通常是过滤器链中的最后一个过滤器.
(4)请求被发送到FilterDispatcher后,FilterDispatcher询问ActionMapper时候需要调用某个action来处理这个Request。
(5)如果ActionMapper决定需要调用某个action,FilterDispatcher则把请求交给ActionProxy进行处理.
(6)ActionProxy通过Configuration Manager询问框架的配置文件struts.xml,找到调用的action类。
(7)ActionProxy创建一个ActionInvocation实例,通过代理模式调用Action。
(8)action执行完毕后,返回一个result字符串,此时再按相反的顺序通过Intercepter拦截器。
(9)最后ActionInvocation实例,负责根据struts.xml中配置result元素,找到与之相对应的result,决定进一步输出。
简要基本流程:
I.客户端浏览器发出HTTP请求。II.根据web.xml配置,该请求被FilterDispatcher接收。III.根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton。IV.Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。V.Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面。VI.返回HTTP响应到客户端浏览器。
(4)使用原因:
struts是开源软件。使用Struts的目的是为了帮助我们减少在运用MVC设计模型来开发Web应用的时间。如果我们想混合使用Servlets和JSP的优点来建立可扩展的应用,struts是一个不错的选择。
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任;
Spring:Spring是一个开源框架,它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益.
(5)struts数据传递:可以直接基于应用程序域对象转移,验证数据,数据绑定,动作把从请求接收到的所有数据放在简单的JavaBean上。除了单独接受每个数 据,struts2最优雅的地方时可以创建一个对象来放置这些琐碎的数据,我们可以把复杂对象本身提供给平台的数据转移机制。这不但节省时间,而且也能节 省工作量。
(6)hibernate的三种状态和区别:
瞬时(transient):也叫*状态.数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象。
持久(persistent):数据库中可能有数据与之对应(save),当前与session有关联,并且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)。
脱管(detached):也叫游离状态.数据库中可能有数据与之对应,但当前没有session与之关联,但是有oid;托管对象状态发生改变,hibernate不能检测到。
区别:Transient状态的实体缺乏与数据库表记录之间的联系,而Detached状态的实体恰恰相反.只不过是脱离了session这个数据库操作平台而已.
(7)hibernate分页:1.HQL查询:Query query=session.createQuery(from 实体类);2.从第几条开始:query.setFirstResult(mini);3.每次最多提取多少条数据:query.setMaxResults(max);接收返回的结果集:List<实体类> HousesList=query.list();
代码如下(Query query = session.createQuery(hql);
query.setParameter(0, userId);
query.setMaxResults(maxCount);
query.setFirstResult(firstResult);
return query.list();
)
(8)ssh整合步骤:
1.导入struts2的jar包以及struts2-spring-plugin-2.0.11.2.jar;
2.在web.xml中分别配置struts2与spring相关信息;
3.struts中原有的Action需要继承ActionSupport;
4.在Spring配置Action Bean;
5.在Struts2的配置文件中,调用Spring中配置的Bean;
6.Spring与Hibernate整合,只需要在Spring配置文件配置SessionFactory即可
(9)Spring的隔离级别:Default默认的事务隔离级别
READ_UNCOMMITTED读未提交,一个事务可以操作另外一个未提交的事务,不能避免脏读,不可重复读,幻读,隔离级别最低,并发性 能最高
READ_COMMITTED读已提交,一个事务不可以操作另外一个未提交的事务, 能防止脏读,不能避免不可重复读,幻读。
repeatable_read能够避免脏读,不可重复读,不能避免幻读
SERIALIZABLE隔离级别最高,消耗资源最低,代价最高,能够防止脏读, 不可重复读,幻读。
42、谈谈Spring和Spring MVC的流程和事务;spring配置文件都写什么?
(1)spring提供两种管理事务的方式:一种是声明式事务,一种是编程式事务。
Spring的声明式事务管理,基于Spring的AOP,基于Spring AOP实现,几乎就是xml文件的配置,不再需要不停地写commit,rollback,(但Spring仍然没有放弃编程式的事务管理策略)。
Spring的编程式事务管理,统一的事务编码风格,几乎是一个模板化的。
为我们提供了一个TransactionTemplate,使用回调机制,将应用代码从样板式的资源获取和释放代码中解放出来,不再有大量的try/catch/finally/try/catch代码块
(2)springMVC的流程和事务:动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射 ,反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,跟xml Spring的配置文件来动态的创建对象,和调用对象里的方法的 。
Spring还有一个核心就是AOP这个就是面向切面编程,可以为某一类对象 进行监督和控制(也就是在调用这类对象的具体方法的前后去调用你指定的 模块)从而达到对一个模块扩充的功能。这些都是通过 配置类达到的。Spring目的:就是让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的(Spring根据这些配置 内部通过反射去动态的组装对象)
要记住:Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。
(3)spring的配置文件:一、引用外部属性文件;二、常用数据源的配置;三、配置事务管理器;四、context:component-scan<!-- 对包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能 -->;五、aop注解支持;六、缓存配置;七、<!-- Spring、MyBatis的整合,需要在 Spring 应用上下文中定义至少两样东西:一个SqlSessionFactory和至少一个数据映射器类(UserMapper->iocContext.xml)。 -->;
43、SSH和SSM的区别
(1)基于MVC三层架构,使用ssh框架 or ssm框架 or ssi框架,采用面向接口的方式编程。
共同点是struts、spring,不同点是hibernate和mybatis、ibatis。
(2)相对Hibernate“O/R”而言,iBATIS 是一种“Sql Mapping”的ORM实现。
(3)由于hibernate是完全面向对象的编程,在实现dao中就非常的方便,而且不重复;当mybatis在.java代码中也是可以做到不重复,麻烦一点的是,每个映射文件都必须编写几乎相同的配置,除了resultType不一样。
(4)hibernate在实际编程中可以把基础的CRUD封装,比如BaseDao类。其它类只要去继承BaseDao就能执行所有的基础的CRUD。这样就非常方便。这个带来的好处还有,可以建立BaseService和BaseAction。
由于mybatis的映射文件中,虽然SQL语句中的表名可以通过parameterType指定,但是resultType必须定死,不能以参数 的形式给予指定。导致的结果就是所有的DAO类的每个CRUD都必须和指定的映射文件绑定在一起,以至于不可能存在BaseDao类。当然也就不能建立 BaseService和BaseAction。
44、数据库对象关系映射
1.对“对象关系映射”的理解
[java]
Department
int
id;
String
name
;
Set
employees=new HashSet();
Employee
int
id;
String
name
;
Department dept;
[sql]
create
department(
id
int
primary
key
,
name
varchar
(20)
);
[sql]
create
employee(
id
int
primary
key
,
name
varchar
(20),
dept_id
int
,
constraint
dept_id_FK
foreign
key
(dept_id)
references
department(id)
);
[sql]
create
table
teacher(
id
int
primary
key
,
name
varchar
(20)
);
create
table
student(
id
int
primary
key
,
name
varchar
(20)
);
[sql]
create
table
student_teacher(
student_id
int
,
teacher_id
int
,
constraint
student_teacher_PK
primary
key
(student_id,teacher_id),
constraint
student_id_FK
foreign
key
(student_id)
references
student(id),
constraint
teacher_id_FK
foreign
key
(teacher_id)
references
teacher(id)
);
[java]
Person
int
id;
String
name
;
IdCard ic;
IdCard
int
id;
String address;
[sql]
create
table
person(
id
int
primary
key
,
name
varchar
(20)
);
create
table
idcard(
id
int
primary
key
,
address
varchar
(40),
constraint
id_FK
foreign
key
(id)
references
person(id)
);
注:对于一对一关系的两张表,分主表与从表,从表的存在必须依赖于主表,主表可以不依赖于从表。从表的设计上其主键字段同时也是外键字段。
备注:有时在设计表时,会特意把两张表合在一张表中,虽然会造成数据冗余,但是却可以不因为联表查询而造成查询性能有所降低。这是用空间来换时间的做法。
线程安全
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。 线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
关于线程安全:
1) 常量始终是线程安全的,因为只存在读操作。
2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
有状态和无状态对象
有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。
无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
无状态对象一般没有实例变量,或者有实例变量,不能改写,如private的,对外只暴露只读操作,无写操作。
单例类可以是有状态的(stateful),也可以是无状态的。无状态的单例模式,是线程安全的。有状态的单例模式,是非线程安全的。
spring中的有状态(Stateful)和无状态(Stateless)
1.通过上面的分析,相信大家已经对有状态和无状态有了一定的理解。无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。
2.默认情况下,从Spring bean工厂所取得的实例为singleton(scope属性为singleton),容器只存在一个共享的bean实例。
3.理解了两者的关系,那么scope选择的原则就很容易了:有状态的bean都使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
4.如Service层、Dao层用默认singleton就行,虽然Service类也有dao这样的属性,但dao这些类都是没有状态信息的,也就是 相当于不变(immutable)类,所以不影响。Struts2中的Action因为会有User、BizEntity这样的实例对象,是有状态信息 的,在多线程环境下是不安全的,所以Struts2默认的实现是Prototype模式。在Spring中,Struts2的Action中,scope 要配成prototype作用域。
46、
47、
48、
49、
50、