Java 基础总结。
1.为什么要用装箱与拆箱
Java是面向对象语言。基本数据类型不具有面向对象的特性。
2.== 与 Equals方法有什么区别
== 用来判断像个变量之间的值是否相等,变量分为基本数据类型对象与引用类型对象。
如果是基本数据类型变量直接比较而引用类型比较对应的引用内存的首地址。
equals 用来比较两个对象长的是否一样,判断两个对象某些特征是否一样。
3.String 与 StringBuffer 区别 ?
String 是内容不可变得字符串。String 底层使用一个不可变得字符数组(Final chart[])
※ FInal如果修饰一个类就不能被继承,如果修饰一个变量那么被赋值就不能再改变
StringBuffer与StringBuilder是内容可以改变的字符串。底层使用一个可变的字符数组。
4.StringBuffer与StringBuilder区别?
StringBuilder 是线程不安全的效率较高。
StringBuffer 是线程安全的效率较低、添加一个同步锁。
最经典的拼接字符串.
1.String进行拼接,String c = "a" + "b";
2.StringBuffer与StringBuilder 拼接。 通过append进行拼接。
字符串池的优缺点:
优点:字符串池的优点避免相同字符串的创建节省内存,省去创建相同字符串的的时间提高性能
缺点:Jvm在常量池遍历对象花费的时间,不过时间成本较少
/** * 情景一:字符串池 * JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象; * 并且可以被共享使用,因此它提高了效率。 * 由于String类是final的,它的值一经创建就不可改变。 * 字符串池由String类维护,我们可以调用intern()方法来访问字符串池。 */ String s1 = "abc"; //↑ 在字符串池创建了一个对象 String s2 = "abc"; //↑ 字符串pool已经存在对象“abc”(共享),所以创建0个对象,累计创建一个对象 System.out.println("s1 == s2 : "+(s1==s2)); //↑ true 指向同一个对象, System.out.println("s1.equals(s2) : " + (s1.equals(s2))); //↑ true 值相等 //↑------------------------------------------------------over /** * 情景二:关于new String("") * */ String s3 = new String("abc"); //↑ 创建了两个对象,一个存放在字符串池中,一个存在与堆区中; //↑ 还有一个对象引用s3存放在栈中 String s4 = new String("abc"); //↑ 字符串池中已经存在“abc”对象,所以只在堆中创建了一个对象 System.out.println("s3 == s4 : "+(s3==s4)); //↑false s3和s4栈区的地址不同,指向堆区的不同地址; System.out.println("s3.equals(s4) : "+(s3.equals(s4))); //↑true s3和s4的值相同 System.out.println("s1 == s3 : "+(s1==s3)); //↑false 存放的地区多不同,一个栈区,一个堆区 System.out.println("s1.equals(s3) : "+(s1.equals(s3))); //↑true 值相同 //↑------------------------------------------------------over /** * 情景三: * 由于常量的值在编译的时候就被确定(优化)了。 * 在这里,"ab"和"cd"都是常量,因此变量str3的值在编译时就可以确定。 * 这行代码编译后的效果等同于: String str3 = "abcd"; */ String str1 = "ab" + "cd"; //1个对象 String str11 = "abcd"; System.out.println("str1 = str11 : "+ (str1 == str11)); //↑------------------------------------------------------over /** * 情景四: * 局部变量str2,str3存储的是存储两个拘留字符串对象(intern字符串对象)的地址。 * * 第三行代码原理(str2+str3): * 运行期JVM首先会在堆中创建一个StringBuilder类, * 同时用str2指向的拘留字符串对象完成初始化, * 然后调用append方法完成对str3所指向的拘留字符串的合并, * 接着调用StringBuilder的toString()方法在堆中创建一个String对象, * 最后将刚生成的String对象的堆地址存放在局部变量str3中。 * * 而str5存储的是字符串池中"abcd"所对应的拘留字符串对象的地址。 * str4与str5地址当然不一样了。 * * 内存中实际上有五个字符串对象: * 三个拘留字符串对象、一个String对象和一个StringBuilder对象。 */ String str2 = "ab"; //1个对象 String str3 = "cd"; //1个对象 String str4 = str2+str3; String str5 = "abcd"; System.out.println("str4 = str5 : " + (str4==str5)); // false //↑------------------------------------------------------over /** * 情景五: * JAVA编译器对string + 基本类型/常量 是当成常量表达式直接求值来优化的。 * 运行期的两个string相加,会产生新的对象的,存储在堆(heap)中 */ String str6 = "b"; String str7 = "a" + str6; String str67 = "ab"; System.out.println("str7 = str67 : "+ (str7 == str67)); //↑str6为变量,在运行期才会被解析。 final String str8 = "b"; String str9 = "a" + str8; String str89 = "ab"; System.out.println("str9 = str89 : "+ (str9 == str89)); //↑str8为常量变量,编译期会被优化 //↑------------------------------------------------------over
5.Java 中的集合
Java集合分为 value 与 key - value (Collection Map)两种
存储值得有 list和Set
List是有序的可以重复
Set是无序的不可以重复
存储Key - value 有Map
6.ArrayList 和LinkList 区别
ArrayList 底层使用是数组
※ 数组具有索引查询特定元素较快。而插入删除较慢。(数组在内存中是一块连续的内存。如果插入删除需要移动内存)
LinkList 底层使用链表
※链表内存是不需要连续的。在当前元素中存放下一个或上一个元素地址。查询需要从头开始找。效率低。
拆入是不需要移动内存。只需要改变引用指向即可。所以插入或者删除效率高
7.HashMap和HashTable区别。HashMap和ConcurrentHashMap区别
相同点:HashMap和HashTable都可以存储key - value的数据
不同点:
1.HashMap是可以将NULL作为key或者Value ,HashTable是不可以的
2.HashMap是线程不安全的,效率较高。而HashTable是线程安全的。效率较低
我想线程安全又想效率高?
ConcurrentHashMap 通过整个Map分为N个segment(类似HashMap)。
可以提供相同的线程安全。但效率提高N倍。默认是16倍。
8.拷贝一个文件工具类使用字节流还是字符流?
拷贝文件不确定是否包含字符流。有可能有字节。(图片、声音、图形) 考虑通用性使用字节流。
字节流附加:
1.IO流概述 概述:
IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间的数据传输,Java对于数据的操作都是通过流实现,而java用于操作流的对象都在IO包中。 分类: 按操作数据分为:字节流和字符流。 如:Reader和InpurStream 按流向分:输入流和输出流。如:InputStream和OutputStream IO流常用的基类: * InputStream , OutputStream 字符流的抽象基类: * Reader , Writer 由上面四个类派生的子类名称都是以其父类名作为子类的后缀:如:FileReader和FileInputStream
1. 字符流简介: * 字符流中的对象融合了编码表,也就是系统默认的编码表。我们的系统一般都是GBK编码。 * 字符流只用来处理文本数据,字节流用来处理媒体数据。 * 数据最常见的表现方式是文件,字符流用于操作文件的子类一般是FileReader和FileWriter。 2.字符流读写: 注意事项: * 写入文件后必须要用flush()刷新。 * 用完流后记得要关闭流 * 使用流对象要抛出IO异常 * 定义文件路径时,可以用“/”或者“\\”。 * 在创建一个文件时,如果目录下有同名文件将被覆盖。 * 在读取文件时,必须保证该文件已存在,否则出异常
import java.io.*; import java.util.Scanner; class CopyText { public static void main(String[] args) throws IOException { sop("请输入要拷贝的文件的路径:"); Scanner in = new Scanner(System.in); String source = in.next(); sop("请输入需要拷贝到那个位置的路径以及生成的文件名:"); String destination = in.next(); in.close(); CopyTextDemo(source,destination); } /*****************文件Copy*********************/ private static void CopyTextDemo(String source,String destination) { try { FileWriter fw = new FileWriter(destination); FileReader fr = new FileReader(source); char [] buf = new char[1024]; //将Denmo中的文件读取到buf数组中。 int num = 0; while((num = fr.read(buf))!=-1) { //String(char[] value , int offest,int count) 分配一个新的String,包含从offest开始的count个字符 fw.write(new String(buf,0,num)); } fr.close(); fw.close(); } catch (IOException e) { sop(e.toString()); } } /**********************Println************************/ private static void sop(Object obj) { System.out.println(obj); } }
1. 字符流的缓冲区:BufferedReader和BufferedWreiter * 缓冲区的出现时为了提高流的操作效率而出现的. * 需要被提高效率的流作为参数传递给缓冲区的构造函数 * 在缓冲区中封装了一个数组,存入数据后一次取出 BufferedReader示例: 读取流缓冲区提供了一个一次读一行的方法readline,方便对文本数据的获取。 readline()只返回回车符前面的字符,不返回回车符。如果是复制的话,必须加入newLine(),写入回车符 newLine()是java提供的多平台换行符写入方法。
import java.io.*; class BufferedWriterDemo { public static void main(String[] args) throws IOException { //创建一个字符写入流对象 FileWriter fw = new FileWriter("buf.txt"); //为了提高字符写入效率,加入了缓冲技术。 //只要将需要被提高效率的流作为参数传递给缓冲区的构造函数即可 BufferedWriter bfw = new BufferedWriter(fw); //bfw.write("abc\r\nde"); //bfw.newLine(); 这行代码等价于bfw.write("\r\n"),相当于一个跨平台的换行符 //用到缓冲区就必须要刷新 for(int x = 1; x < 5; x++) { bfw.write("abc"); bfw.newLine(); //java提供了一个跨平台的换行符newLine(); bfw.flush(); } bfw.flush(); //刷新缓冲区 bfw.close(); //关闭缓冲区,但是必须要先刷新 //注意,关闭缓冲区就是在关闭缓冲中的流对象 fw.close(); //关闭输入流对象 } }
字节流:
1、字节流和字符流的基本操作是相同的,但是要想操作媒体流就需要用到字节流。 2、字节流因为操作的是字节,所以可以用来操作媒体文件。(媒体文件也是以字节存储的) 3、读写字节流:InputStream 输入流(读)和OutputStream 输出流(写) 4、字节流操作可以不用刷新流操作。 5、InputStream特有方法: int available();//返回文件中的字节个数 注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。
所以,此方法慎用,当文件不大时,可以使用。
import java.io.*; class CopyPic { public static void main(String[] args){ copyBmp(); System.out.println("复制完成"); } public static void copyBmp() { FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("F:\\java_Demo\\day9_28\\1.bmp"); //写入流关联文件 fos = new FileOutputStream("F:\\java_Demo\\day9_28\\2.bmp"); //读取流关联文件 byte[] copy = new byte[1024]; int len = 0; while((len=fis.read(copy))!=-1) { fos.write(copy,0,len); } } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("复制文件异常"); } finally { try { if(fis!=null) fis.close(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("读取流"); } } } }
9.线程实现方式?
1.继承Thread;类实现一个线程
2.实现Runnable接口实现一个线程
继承过展性不强。Java只支持单继承。
3.怎么启动?
Thread thread = new Thread(继承Thread的对象/实现Runnable对象)
thread.start();
启动线程使用start方法。而启动以后执行了run方法。
4.怎么区分线程?
thread.setName("设置一个线程名称")。
线程:
1:什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点。欲了解更多详细信息请点击这里。 2:线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。更多详细信息请点击这里。 3:如何在Java中实现线程? 在语言层面有两种方式。java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。更多详细信息请点击这里. 4:用Runnable还是Thread? 这个问题是上题的后续,大家都知道我们可以通过继承Thread类或者调用Runnable接口来实现线程,问题是,那个方法更好呢?什么情况下使用它?这个问题很容易回答,如果你知道Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口好了。更多详细信息请点击这里。 5:Thread 类中的start(:和 run(:方法有什么区别? 这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。更多讨论请点击这里 6:Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call(:方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。我的博客有更详细的说明。 7:Java中CyclicBarrier 和 CountDownLatch有什么不同? CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。点此查看更多信息和示例代码。 8:Java内存模型是什么? Java内存模型规定和指引Java程序在不同的内存架构、CPU和操作系统间有确定性地行为。它在多线程的情况下尤其重要。Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。这个关系定义了一些规则让程序员在并发编程时思路更清晰。比如,先行发生关系确保了: 线程内的代码能够按先后顺序执行,这被称为程序次序规则。 对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管程锁定规则。 前一个对volatile的写操作在后一个volatile的读操作之前,也叫volatile变量规则。 一个线程内的任何操作必需在这个线程的start()调用之后,也叫作线程启动规则。 一个线程的所有操作都会在线程终止之前,线程终止规则。 一个对象的终结操作必需在这个对象构造完成之后,也叫对象终结规则。 可传递性 我强烈建议大家阅读《Java并发编程实践》第十六章来加深对Java内存模型的理解。 9:Java中的volatile 变量是什么? volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。volatile变量可以保证下一个读取操作会在前一个写操作之后发生,就是上一题的volatile变量规则。点击这里查看更多volatile的相关内容。 10:什么是线程安全?Vector是一个线程安全类吗? 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。 11:Java中什么是竞态条件? 竞态条件会导致程序在并发情况下出现一些bugs。多线程对一些资源的竞争的时候就会产生竞态条件,如果首先要执行的程序竞争失败排到后面执行了,那么整个程序就会出现一些不确定的bugs。这种bugs很难发现而且会重复出现,因为线程间的随机竞争。一个例子就是无序处理,详见答案。 12:Java中如何停止一个线程? Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend(:和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run(:或者 call(:方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。点击这里查看示例代码。 13:一个线程运行时发生异常会怎样? 这是我在一次面试中遇到的一个很***钻的Java面试题, 简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。 14:如何在两个线程间共享数据? 你可以通过共享对象来实现这个目的,或者是使用像阻塞队列这样并发的数据结构。这篇教程《Java线程间通信》(涉及到在两个线程间共享对象)用wait和notify方法实现了生产者消费者模型。 15:Java中notify 和 notifyAll有什么区别? 这又是一个***钻的问题,因为多线程可以等待单监控锁,Java API 的设计人员提供了一些方法当等待条件改变的时候通知它们,但是这些方法没有完全实现。notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。我的博客有更详细的资料和示例代码。 16:为什么wait, notify 和 notifyAll这些方法不在thread类里面? 这是个设计相关的问题,它考察的是面试者对现有系统和一些普遍存在但看起来不合理的事物的看法。回答这些问题的时候,你要说明为什么把这些方法放在Object类里是有意义的,还有不把它放在Thread类里的原因。一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。你也可以查看这篇文章了解更多。 17:什么是ThreadLocal变量? ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。它是为创建代价高昂的对象获取线程安全的好方法,比如你可以用ThreadLocal让SimpleDateFormat变成线程安全的,因为那个类创建代价高昂且每次调用都需要创建不同的实例所以不值得在局部范围使用它,如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。线程局部变量的另一个不错的例子是ThreadLocalRandom类,它在多线程环境中减少了创建代价高昂的Random对象的个数。查看答案了解更多。 18:什么是FutureTask? 在Java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。 19:Java中interrupted 和 isInterruptedd方法的区别? interrupted(:和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变。 20:为什么wait和notify方法要在同步块中调用? 主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。 21:为什么你应该在循环中检查等待条件? 处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。因此,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的原因,你可以在Eclipse中创建模板调用wait和notify试一试。如果你想了解更多关于这个问题的内容,我推荐你阅读《Effective Java》这本书中的线程和同步章节。 22:Java中的同步集合与并发集合有什么区别? 同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。更多内容详见答案。 23:Java中堆和栈有什么不同? 为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。 24:什么是线程池? 为什么要使用它? 创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。更多内容详见这篇文章。 25:如何写代码来解决生产者消费者问题? 在现实中你解决的许多线程问题都属于生产者消费者模型,就是一个线程生产任务供其它线程进行消费,你必须知道怎么进行线程间通信来解决这个问题。比较低级的办法是用wait和notify来解决这个问题,比较赞的办法是用Semaphore 或者 BlockingQueue来实现生产者消费者模型,这篇教程有实现它。 26:如何避免死锁? Java多线程中的死锁 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件: 互斥条件:一个资源每次只能被一个进程使用。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。这篇教程有代码示例和避免死锁的讨论细节。 27:Java中活锁和死锁有什么区别? 这是上题的扩展,活锁和死锁类似,不同之处在于处于活锁的线程或进程的状态是不断改变的,活锁可以认为是一种特殊的饥饿。一个现实的活锁例子是两个人在狭小的走廊碰到,两个人都试着避让对方好让彼此通过,但是因为避让的方向都一样导致最后谁都不能通过走廊。简单的说就是,活锁和死锁的主要区别是前者进程的状态可以改变但是却不能继续执行。 28:怎么检测一个线程是否拥有锁? 我一直不知道我们竟然可以检测一个线程是否拥有锁,直到我参加了一次电话面试。在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。你可以查看这篇文章了解更多。 29:你如何在Java中获取线程堆栈? 对于不同的操作系统,有多种方法来获得Java进程的线程堆栈。当你获取线程堆栈时,JVM会把所有线程的状态存到日志文件或者输出到控制台。在Windows你可以使用Ctrl + Break组合键来获取线程堆栈,Linux下用kill -3命令。你也可以用jstack这个工具来获取,它对线程id进行操作,你可以用jps这个工具找到id。 30:JVM中哪个参数是用来控制线程的栈堆栈小的 这个问题很简单, -Xss参数用来控制线程的堆栈大小。你可以查看JVM配置列表来了解这个参数的更多信息。 31:Java中synchronized 和 ReentrantLock 有什么不同? Java在过去很长一段时间只能通过synchronized关键字来实现互斥,它有一些缺点。比如你不能扩展锁之外的方法或者块边界,尝试获取锁时不能中途取消等。Java 5 通过Lock接口提供了更复杂的控制来解决这些问题。 ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义且它还具有可扩展性。你可以查看这篇文章了解更多 32:有三个线程T1,T2,T3,怎么确保它们按顺序执行? 在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。你可以查看这篇文章了解更多。 33:Thread类中的yield方法有什么作用? Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行。点击这里查看更多yield方法的相关内容。 34:Java中ConcurrentHashMap的并发度是什么? ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。欲了解更多并发度和内部大小调整请阅读我的文章How ConcurrentHashMap works in Java。 35:Java中Semaphore是什么? Java中的Semaphore是一种新的同步类,它是一个计数信号。从概念上讲,从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。更多详细信息请点击这里。 36:如果你提交任务时,线程池队列已满。会时发会生什么? 这个问题问得很狡猾,许多程序员会认为该任务会阻塞直到线程池队列有空位。事实上如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。 37:Java线程池中submit(:和 execute()方法有什么区别? 两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。更多详细信息请点击这里。 38:什么是阻塞式方法? 阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。更多详细信息请点击这里。 39:Swing是线程安全的吗? 为什么? 你可以很肯定的给出回答,Swing不是线程安全的,但是你应该解释这么回答的原因即便面试官没有问你为什么。当我们说swing不是线程安全的常常提到它的组件,这些组件不能在多线程中进行修改,所有对GUI组件的更新都要在AWT线程中完成,而Swing提供了同步和异步两种回调方法来进行更新。点击这里查看更多swing和线程安全的相关内容。 41:Java中invokeAndWait 和 invokeLater有什么区别? 这两个方法是Swing API 提供给Java开发者用来从当前线程而不是事件派发线程更新GUI组件用的。InvokeAndWait()同步更新GUI组件,比如一个进度条,一旦进度更新了,进度条也要做出相应改变。如果进度被多个线程跟踪,那么就调用invokeAndWait()方法请求事件派发线程对组件进行相应更新。而invokeLater()方法是异步调用更新组件的。更多详细信息请点击这里。 42:Swing API中那些方法是线程安全的? 这个问题又提到了swing和线程安全,虽然组件不是线程安全的但是有一些方法是可以被多线程安全调用的,比如repaint(), revalidate()。 JTextComponent的setText()方法和JTextArea的insert(:和 append(:方法也是线程安全的。 43:如何在Java中创建Immutable对象? 这个问题看起来和多线程没什么关系, 但不变性有助于简化已经很复杂的并发程序。Immutable对象可以在没有同步的情况下共享,降低了对该对象进行并发访问时的同步化开销。可是Java没有@Immutable这个注解符,要创建不可变类,要实现下面几个步骤:通过构造方法初始化所有成员、对变量不要提供setter方法、将所有的成员声明为私有的,这样就不允许直接访问这些成员、在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝。我的文章how to make an object Immutable in Java有详细的教程,看完你可以充满自信。 44:Java中的ReadWriteLock是什么? 一般而言,读写锁是用来提升并发程序性能的锁分离技术的成果。Java中的ReadWriteLock是Java 5 中新增的一个接口,一个ReadWriteLock维护一对关联的锁,一个用于只读操作一个用于写。在没有写线程的情况下一个读锁可能会同时被多个读线程持有。写锁是独占的,你可以使用JDK中的ReentrantReadWriteLock来实现这个规则,它最多支持65535个写锁和65535个读锁。 45:多线程中的忙循环是什么? 忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep(:或 yield(:它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。你可以查看这篇文章获得更多信息。 46)volatile 变量和 atomic 变量有什么不同? 这是个有趣的问题。首先,volatile 变量和 atomic 变量看起来很像,但功能却不一样。Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操作就不是原子性的。而AtomicInteger类提供的atomic方法可以让这种操作具有原子性如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作。 47:如果同步块内的线程抛出异常会发生什么? 这个问题坑了很多Java程序员,若你能想到锁是否释放这条线索来回答还有点希望答对。无论你的同步块是正常还是异常退出的,里面的线程都会释放锁,所以对比锁接口我更喜欢同步块,因为它不用我花费精力去释放锁,该功能可以在finally block里释放锁实现。 48:单例模式的双检锁是什么? 这个问题在Java面试中经常被问到,但是面试官对回答此问题的满意度仅为50%。一半的人写不出双检锁还有一半的人说不出它的隐患和Java1.5是如何对它修正的。它其实是一个用来创建线程安全的单例的老方法,当单例实例第一次被创建时它试图用单个锁进行性能优化,但是由于太过于复杂在JDK1.4中它是失败的,我个人也不喜欢它。无论如何,即便你也不喜欢它但是还是要了解一下,因为它经常被问到。你可以查看how double checked locking on Singleton works这篇文章获得更多信息。 49:如何在Java中创建线程安全的Singleton? 这是上面那个问题的后续,如果你不喜欢双检锁而面试官问了创建Singleton类的替代方法,你可以利用JVM的类加载和静态变量初始化特征来创建Singleton实例,或者是利用枚举类型来创建Singleton,我很喜欢用这种方法。你可以查看这篇文章获得更多信息。 50:如何强制启动一个线程? 这个问题就像是如何强制进行Java垃圾回收,目前还没有觉得方法,虽然你可以使用System.gc()来进行垃圾回收,但是不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。 51:Java中的fork join框架是什么? fork join框架是JDK7中出现的一款高效的工具,Java开发人员可以通过它充分利用现代服务器上的多处理器。它是专门为了那些可以递归划分成许多子模块设计的,目的是将所有可用的处理能力用来提升程序的性能。fork join框架一个巨大的优势是它使用了工作窃取算法,可以完成更多任务的工作线程可以从其它线程中窃取任务来执行。你可以查看这篇文章获得更多信息。 52:Java多线程中调用wait(:和 sleep()方法有什么不同? Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。
10.使用线程并发库?
java.util.current包对于线程优化。管理各项操作。使线程变得得心应手。包括线程的运行、线程池的创建、线程生命周期的控制
Java通过Executors四种静态方法创建四种线程池
newCachedThreadPool() | -缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中 -缓存型池子通常用于执行一些生存期很短的异步型任务 因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。 -能reuse的线程,必须是timeout IDLE内的池中线程,缺省 timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。 注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
|
newFixedThreadPool(int) | -newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程 -其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子 -和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器 -从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同: fixed池线程数固定,并且是0秒IDLE(无IDLE) cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE
|
newScheduledThreadPool(int) | -调度型线程池 -这个池子里的线程可以按schedule依次delay执行,或周期执行 |
SingleThreadExecutor() | -单例线程,任意时间池中只能有一个线程 -用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE) |
10.1线程池的作用?
1.限制线程的个数,不会导致由于线程过多导致系统运行缓慢或崩溃
2.线程池不需要每次都去创建或销毁。节约了资源
3.线程池不需要每次都去创建。响应时间更快。
11.常用的设计模式有哪些?
设计模式就是应该前任无数次实践总结出的。设计过程中可以反复使用的。可以解决待定问题的设计方法
单例模式(饱汉模式,饥汉模式)
1.构造方法私有化。让出自己类创建其他地方都不能创建
2.在自己类中创建一个单实例(饱汉模式是一出来就创建单例,而饥汉模式需要时候才创建)
3.提供一个方法获取实例对象
饱汉模式 package Jzen.Util; public class PersionFactory { private PersionFactory(){ } private static PersionFactory instance = new PersionFactory(); public static PersionFactory getInstance(){ return instance; } }
饥汉模式 package Jzen.Util; public class PersionFactory { private PersionFactory(){ } private static PersionFactory instance = null; public synchronized static PersionFactory getInstance(){ if(instance == null){ instance = new PersionFactory(); } return instance; } }
工厂模式 Spring ioc使用工厂模式
对象创建交给一个工厂去创建。
代理模式 Spring aop就是使用的动态代理
12.http get和Post请求区别?
1.get与post不同的请求对yrl不同的操作。GET一般用于获取查询信息,而POST一般用于更新资源信息
2.GET 请求提交参数会在地址栏显示而POST不会再地址栏显示出来。
3.传输数据大小
4.安全性。POST的安全性比GET 高
13.Servlet 的理解。或者Servlet是什么?
主要用于交互式游览和修改数据。是Java服务端的程序。
HTTPServlet重写doGet或者DoPost重写来处理。
14.Servlet生命周期?
Servlet有良好的生命周期。包括加载与实例化、初始化、处理请求、服务结束。
这个生命周期有init、services和destroy方法表达。
1. init()方法
在Servlet的生命周期中,仅执行一次init()方法,它是在服务器装入Servlet时执行的,可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init();
2. service()方法
它是Servlet的核心,每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
3. destroy()方法
仅执行一次,在服务器端停止且卸载Servlet时执行该方法,有点类似于C++的delete方法。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
下面来谈谈Servlet的生命周期,Servlet的生命周期是由Servlet容器来控制的,它始于装入Web服务器的内存时,并在终止或重新装入Servlet时结束。这项操作一般是动态执行的。然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet。
15.Servlet 中Forword() 与Redirect()区别?
1.从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
2.从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
3.从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
4.从效率来说
forward:高.
redirect:低.
转发是服务器行为,重定向是客户端行为
转发过程:客户浏览器发送http请求----》web服务器接受此请求--》调用内部的一个方法在容器内部完成请求处理和转发动作----》将目标资源 发送给客户;在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客 户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向过程:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器--》客户浏览器发现 是302响应,则自动再发送一个新的http请求,请求url是新的location地址----》服务器根据此请求寻找资源并发送给客户。在这里 location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的 路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。
15.1 直接转发和间接转发的原理及区别是什么?
答:Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。对应到代码里,分别是RequestDispatcher类的forward()方法和HttpServletRequest类的sendRedirect()方法。
对于间接方式,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。它本质上是两次HTTP请求,对应两个request对象。
对于直接方式,客户端浏览器只发出一次请求,Servlet把请求转发给Servlet、HTML、JSP或其它信息资源,由第2个信息资源响应该请求,两个信息资源共享同一个request对象。
16.Jsp和Servlet 相同点和不同点。
Jsp是Servlet的扩展。jsp文件都会继承HTTPServlet服务(tomcat进行)。也就是jsp最终也是一个Servlet.
这个Servlet对外提供服务。这就是相同点
Servlet要实现html部分必须使用writer输出html比较麻烦。
Servlet的应用逻辑是在Java中。并且完全从变现层中分离出来。
Jsp是Java+Html组合一个扩展名为jsp文件。JSP侧重于视图。Servlet侧重控制逻辑
17.Jsp内置对象作用以及用法?
out对象:用来传送回应的输出。
request对象:封装了来自客户端、浏览器的请求信息。
response对象:封装了服务器的响应信息。
exception对象:封装了jsp程序执行过程中发生的异常和错误信息。
config对象:封装了应用程序的配置信息。
page对象:指向了当前jsp程序本身。
session对象:与请求有关的会话期
application对象:正在执行的内容
pageContext对象:页面属性管理
四大作用域
1.PageContext
2.request
3.session
4.application
可以通过jstl从自大作用域取值。jsp传递值。request、session、application、cookie也能传值。
18.Session Cookie 区别。使用点?
Session 和 Cookie都是会话跟踪技术。
Cookie通过客户端记录信息确认用户身份。
Session通过服务端记录信息确认用户身份。
但是Session实现依赖于Cookie。SessionId(Session唯一标识存放在客户端)
Session Cookie 区别
1.Cookie 数据存放在客户端。Session数据存放在服务端。
2.Cookie很不安全。别人可以解析存放在本地的Cookie并进行Cookie欺骗考虑到安全应当使用Session
3.Session一定时间内保存服务器上。访问增多。会占用服务器性能。考虑性能则使用Cookie
4.单个cookie保存数据不能超过4k。很多游览器控制一个站点最多保存20个cookie
购物车最好使用Cookie。但是Cookie游览器是可以禁用的。这时候我们要使用cookie+数据库的形式。
当从Cookie取不出数据。则从数据库获取。
数据库
1.数据库分类及常用数据库
数据库分为关系型数据库与非关系性数据库
关系型:mysql oracle SqlServer等。
菲关系型:Redis mogodb hadoop等。
2.关系型数据库与非关系性数据库区别
非关系型数据库的优势:
1. 性能
NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。
2. 可扩展性
同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。
关系型数据库的优势:
1. 复杂查询
可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
2. 事务支持
使得对于安全性能很高的数据访问要求得以实现
Mysql limit m,n分页语句:
Oracle 分页
select * from (select a1.*,rownum rn from (select * from student) a1 where rownum <=5) where rn>=2;
select a1.* from (select student.*,rownum rn from student where rownum <=5) a1 where rn >=3;
select a1.* from (select student.*,rownum rn from student) a1 where rn between 3 and 5;
6.存储过程的优点
1.存储过程值在创建时进行编译。以后每次执行存储过程都不需要重新编译。而一般SQL语言每执行一次
就编译一次。因此使用存储过程可以大大提高数据库执行速度。
2.复杂的业务逻辑需要多天SQL语句。会产生大量网络传输。如果使用存储过程会大大减少。
3.存储过程可以重复使用。减少开发工作量
4.安全性高。存储过程屏蔽底层数据库对象的直接访问。
7.通过JDBC调用存储过程?
package xzg; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; public class JDBCtest { public static void main(String[] args) { //test(); //test2(); test3(); } /* * 命令行创建的存储过程函数为: create procedure all_user() select * from user; * 创建一个查询所有内容的存储过程 * 调用无参存储过程 */ static void test() { Connection conn = Dbutil.open(); try { //存储过程函数固定格式:{call xxx} CallableStatement cs = conn.prepareCall("{call all_user()}"); ResultSet rs = cs.executeQuery(); while (rs.next()) { int id = rs.getInt(1); String name = rs.getString(2); int age = rs.getInt(3); System.out.println(id + "," + name + "," + age); } } catch (SQLException e) { e.printStackTrace(); } finally { Dbutil.close(conn); } } /* * 命令行创建的存储过程函数为: * create procedure insert_user(in myname varchar(20), * in myage tinyint(20)) insert user(username,age) values(myname,myemail); * 表示创建一个插入myname,数据类型为varchar(20); myage,数据类型为tinyint(20)的存储过程 * 调用输入带参存储过程 */ static void test2() { Connection conn = Dbutil.open(); try { CallableStatement cs = conn.prepareCall("{call insert_user(?,?)}"); cs.setString(1, "jack"); cs.setInt(2, 10); cs.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { Dbutil.close(conn); } } /* * 命令行创建的存储过程函数为: * create procedure getnamebyid(in cid int, * out return_name varchar(20)) select username into return_name * from user where id =cid; * 表示创建一个如果id为cid,那么就输出返回一个return_name * 调用输入、输出带参存储过程 */ static void test3() { Connection conn = Dbutil.open(); try { CallableStatement cs = conn.prepareCall("{call getnamebyid(?,?)}"); cs.setInt(1, 3); //索引1,第3个id //输出参数的话要注册 cs.registerOutParameter(2, Types.CHAR); //注册后要更新 cs.execute(); String name =cs.getString(2); //这个是索引的意思 cs.executeQuery(); System.out.println(name); } catch (SQLException e) { e.printStackTrace(); } finally { Dbutil.close(conn); } } }
8.对于JDBC的理解
Java databases connection Java数据库连接。数据库关系系统是很多。每个数据库管理系统支持测令是不一样的。
Java之定义接口让数据库厂商自己实现接口,只需要导入厂商开发的实现就可。
9.写一个访问JDBC的程序?
加载驱动、获取参数、设置参数、执行、释放连接。
10.JDBC中的PreparedStatement相比Statement的好处
11.数据库连接池作用
1.限定数据库的个数。不会导致由于数据库连接过多导致程序缓慢或者崩溃、
2. 数据库连接不需要每次都去创建或销毁,节约资源。
3.数据库连接不需要每次都去创建。响应更快。
前段部分
1.HTML css JavaScript 在网页开发的定位?
2.简单介绍一下Ajax
什么是Ajax? AJax就是异步的JavaScript和xml。可以不用刷新页面就可以完成数据交互
作用是什么? 通过Ajax与服务器进行数据交换。AJax实现页面局部更新。也就是说可以在不重新加载
页面情况下。对网页部分进行更新。
怎么来实现?XmlHttpRequest对象。使用这个对象可以异步向服务器发送请求。获取响应完成拒不更新。
3.JS与JQuery区别?
Jquery是js 的一个框架。封装js的属性和方法。增强js的功能。开发更加方便。
js需要处理一些兼容问题。Jquery进行封装就可以不用处理兼容问题、
元素js的dom和事件绑定操作麻烦。Jquery封装后操作方便。
MVC是Model View Controller 是模型 视图 控制器的缩写。
在Struts2框架中的处理大概分为以下的步骤
1 用户发送请求;
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action ;
4 如果需要处理,ActionMapper会通知FilterDispatcher,需要处理这个请求,FilterDispatcher会停止过滤器链以后的部分,(这也就是为什么,FilterDispatcher应该出现在过滤器链的最后的原因)。FilterDispatcher把请求的处理交给ActionProxy ;
5 ActionProxy通过Configuration Manager询问框架的配置文件struts.xml,找到需要调用的Action类 。(在服务器启动的时候,ConfigurationManager就会把struts.xml中的所有信息读到内存里,并缓存,当ActionProxy带着URL向他询问要运行哪个Action的时候,就可以直接匹配、查找并回答了)
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到一系列相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。
9 最后,ActionInvocation对象倒序执行拦截器。
10.ActionInvocation对象执行完毕后,响应用户。
注意:2.1.3之后的核心过滤器由FilterDispatcher换成StrutsPrepareAndExecuteFilter。
谈谈拦截器与过滤器的区别:
1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。
SpringMVC流程
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
声明事务和编程事务
隔离级别:
- DEFAULT使用数据库默认的隔离级别
- READ_UNCOMMITTED会出现脏读,不可重复读和幻影读问题
- READ_COMMITTED会出现重复读和幻影读
- REPEATABLE_READ会出现幻影读
- SERIALIZABLE最安全,但是代价最大,性能影响极其严重
和传播行:
- REQUIRED存在事务就融入该事务,不存在就创建事务
- SUPPORTS存在事务就融入事务,不存在则不创建事务
- MANDATORY存在事务则融入该事务,不存在,抛异常
- REQUIRES_NEW总是创建新事务
- NOT_SUPPORTED存在事务则挂起,一直执行非事务操作
- NEVER总是执行非事务,如果当前存在事务则抛异常
- NESTED嵌入式事务
slow_query_log_file 慢查询日志存放的位置(这个目录需要MySQL的运行帐号的可写权限,一般设置为MySQL的数据存放目录)
long_query_time 查询超过多少秒才记录
查看慢查询相关参数
mysql> show variables like 'slow_query%'; +---------------------------+----------------------------------+ | Variable_name | Value | +---------------------------+----------------------------------+ | slow_query_log | OFF | | slow_query_log_file | /mysql/data/localhost-slow.log | +---------------------------+----------------------------------+ mysql> show variables like 'long_query_time'; +-----------------+-----------+ | Variable_name | Value | +-----------------+-----------+ | long_query_time | 10.000000 | +-----------------+-----------+
设置方法
方法一:全局变量设置
将 slow_query_log 全局变量设置为“ON”状态
mysql> set global slow_query_log='ON';
设置慢查询日志存放的位置
mysql> set global slow_query_log_file='/usr/local/mysql/data/slow.log';
查询超过1秒就记录
mysql> set global long_query_time=1;
方法二:配置文件设置
修改配置文件my.cnf,在[mysqld]下的下方加入
[mysqld] slow_query_log = ON slow_query_log_file = /usr/local/mysql/data/slow.log long_query_time = 1
重启MySQL服务
service mysqld restart
查看设置后的参数
mysql> show variables like 'slow_query%'; +---------------------+--------------------------------+ | Variable_name | Value | +---------------------+--------------------------------+ | slow_query_log | ON | | slow_query_log_file | /usr/local/mysql/data/slow.log | +---------------------+--------------------------------+ mysql> show variables like 'long_query_time'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | long_query_time | 1.000000 | +-----------------+----------+
测试
1.执行一条慢查询SQL语句
mysql> select sleep(2);
2.查看是否生成慢查询日志
ls /usr/local/mysql/data/slow.log
新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。
Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
ServivorTo:保留了一次MinorGC过程中的幸存者。
ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。
MinorGC的过程:MinorGC采用复制算法。首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区);然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。
2.老年代:
主要存放应用程序中生命周期长的内存对象。
老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。
MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。
3.永久代:
指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。
在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。
※:元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存