能够有多个类,但只能有一个public的类,且public的类名必须与文件名相一致。html
Java中的保留字,如今未在Java中使用。java
&和&&均可以用做逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,不然,只要有一方为false,则结果为false。程序员
&&还具备短路的功能,即若是第一个表达式为false,则再也不计算第二个表达式,例如,对于if(str!= null&& !str.equals(s))表达式,当str为null时,后面的表达式不会执行,因此不会出现NullPointerException若是将&&改成&,则会抛出NullPointerException异常。if(x==33 &++y>0) y会增加,if(x==33 && ++y>0)不会增加。web
&还能够用做位运算符,当&操做符两边的表达式不是boolean类型时,&表示按位与操做,咱们一般使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31 & 0x0f的结果为0x01。面试
在Java中,要想跳出多重循环,能够在外面的循环语句前定义一个标号,而后再里层循环体的代码中使用带有标号的break语句,便可跳出外层循环。算法
test:for(int i=0;i<10;i++){ for(intj=0;j<10;j++){ System.out.println(“i=” + i + “,j=” + j); if(j == 5) break test; } }
另外,我我的一般并不使用标号这种方式,而是让外层的循环条件表达式的结果能够受到里层循环体代码的控制,例如,要在二维数组中查找到某个数字。sql
int arr[][] ={{1,2,3},{4,5,6,7},{9}}; boolean found = false; for(int i=0;i<arr.length&&!found;i++) { for(intj=0;j<arr[i].length;j++){ System.out.println(“i=” + i + “,j=” + j); if(arr[i][j] ==5) { found =true; break; } } }
在 switch(e) 中,e 只能是一个整数表达式或者枚举常量(更大字体),整数表达式能够使int或Integer,因为byte,short,char均可以隐式转换为int,因此,这些类型及其包装类也是能够的。特例,Java1.7之后,switch语句支持String。综上可知,long类型不符合switch的语法规定,而且不能隐式转换成int类型,故不能做用于switch语句。shell
对于short s1= 1; s1 = s1 + 1;因为s1+1运算时会自动提高表达式的类型,因此结果是int型,再赋值给short类型s1时,编译器将报告须要强制转换类型的错误。数据库
对于short s1= 1; s1 += 1;因为 +=是java语言规定的运算符,java编译器会对它进行特殊处理,所以能够正确编译。编程
char型变量用来存储Unicode编码的字符,Unicode编码字符集中包含了汉字,故能够存储。不过,个别特殊汉字未被包含,即不可存储某些特殊汉字。补充说明:Unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
2 << 3。左移三位,由于将一个数左移n位,就至关于乘以了2的n次方,且位运算cpu直接支持的,效率最高。
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容仍是能够改变的。例如,对于以下语句:
final StringBuffer a=new StringBuffer("immutable");
执行以下语句将报告编译期错误:
a=new StringBuffer("");
可是,执行以下语句则能够经过编译:
a.append(" broken!");
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须建立了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,因此也称为类变量,只要程序加载了类的字节码,不用建立任何实例对象,静态变量就会被分配空间,静态变量就能够被使用了。总之,实例变量必须建立对象后才能够经过这个对象来使用,静态变量则能够直接使用类名来引用。
例如,对于下面的程序,不管建立多少个实例对象,永远都只分配了一个staticVar变量,而且每建立一个实例对象,这个staticVar就会加1;可是,每建立一个实例对象,就会分配一个instanceVar,便可能分配多个instanceVar,而且每一个instanceVar的值都只自加了1次。
public class VariantTest{ publicstatic int staticVar = 0; publicint instanceVar = 0; publicVariantTest(){ staticVar++; instanceVar++; System.out.println(staticVar +instanceVar); } }
不能够。由于非static方法是要与对象关联在一块儿的,必须建立一个对象后,才能够在该对象上进行方法调用,而static方法调用时不须要建立对象,能够直接调用。也就是说,当一个static方法被调用时,可能尚未建立任何实例对象,若是从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪一个对象上的呢?这个逻辑没法成立,因此,一个static方法内部发出对非static方法的调用。
int是java提供的8种原始数据类型之一。Java为每一个原始类型提供了封装类,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,即Integer能够区分出未赋值和值为0的区别,int则没法表达出未赋值的状况。
例如:要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。
Integer提供了多个与整数相关的操做方法,例如,将一个字符串转换成整数,Integer中还定义了表示整数的最大值和最小值的常量。
Math类中提供了三个与取整有关的方法:ceil 向上取整、floor 向下取整 、round 四舍五入,这些方法的做用与它们的英文名称的含义相对应。故,Math.round(11.5) 输出结果为 12 , Math.round(-11.5) 输出结果为 -11。
Overload是重载的意思,Override是覆盖的意思,也就是重写。
重载Overload表示同一个类中能够有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不一样)。
重写Override表示子类中的方法能够与父类中的某个方法的名称和参数彻底相同,经过子类建立的实例对象调用这个方法时,将调用子类中的定义方法,这至关于把父类中定义的那个彻底相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,由于子类能够解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。若是父类的方法是private类型,那么,子类则不存在覆盖的限制,至关于子类中增长了一个全新的方法。
至于Overloaded的方法是否能够改变返回值的类型这个问题,要看你倒底想问什么呢?这个题目很模糊。若是几个Overloaded的方法的参数列表不同,它们的返回者类型固然也能够不同。但我估计你想问的问题是:若是两个方法的参数列表彻底同样,是否可让它们的返回值不一样来实现重载Overload。这是不行的,咱们能够用反证法来讲明这个问题,由于咱们有时候调用一个方法时也能够不定义返回结果变量,即不要关心其返回结果,例如,咱们调用map.remove(key)方法时,虽然remove方法有返回值,可是咱们一般都不会定义接收返回结果的变量,这时候假设该类中有两个名称和参数列表彻底相同的方法,仅仅是返回类型不一样,java就没法肯定编程者倒底是想调用哪一个方法了,由于它没法经过返回结果类型来判断。
override能够翻译为覆盖,从字面就能够知道,它是覆盖了一个方法而且对其重写,以求达到不一样的做用。对咱们来讲最熟悉的覆盖就是对接口方法的实现,在接口中通常只是对方法进行了声明,而咱们在实现时,就须要实现接口声明的全部方法。除了这个典型的用法之外,咱们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意如下的几点:
一、覆盖的方法的标志必需要和被覆盖的方法的标志彻底匹配,才能达到覆盖的效果;
二、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
三、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
四、被覆盖的方法不能为private,不然在其子类中只是新定义了一个方法,并无对其进行覆盖。
Overload对咱们来讲可能比较熟悉,能够翻译为重载,它是指咱们能够定义一些名称相同的方法,经过定义不一样的输入参数来区分这些方法,而后再调用时,VM就会根据不一样的参数样式,来选择合适的方法执行。在使用重载要注意如下的几点:
一、在使用重载时只能经过不一样的参数样式。例如,不一样的参数类型,不一样的参数个数,不一样的参数顺序(固然,同一方法内的几个参数类型必须不同,例如能够是fun(int,float),可是不能为fun(int,int));
二、不能经过访问权限、返回类型、抛出的异常进行重载;
三、方法的异常类型和数目不会对重载形成影响;
四、对于继承来讲,若是某一方法在父类中是访问权限是priavte,那么就不能在子类对其进行重载,若是定义的话,也只是定义了一个新方法,而不会达到重载的效果。
接口能够继承接口。抽象类能够实现接口,抽象类能够继承具体类。抽象类中能够有静态的main方法。
注:抽象类与普通类的惟一区别就是不能建立实例对象和容许有abstract方法。
靠的是父类或接口定义的引用变量能够指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是 引用变量所指向的具体实例对象的方法,即 内存中正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
1.抽象类能够有构造方法,接口中不能有构造方法。
2.抽象类中能够有普通成员变量,接口中没有普通成员变量。
3.抽象类中能够包含非抽象的普通方法,Java 1.8后,接口能够包含一个default的实现方法,其他必须为抽象的普通方法。(java.util.List接口,新增sort(),可直接传参比较器Comparator)。
4. 抽象类中的抽象方法的访问类型能够是public,protected,但接口中的抽象方法只能是public类型的,而且默认即为public abstract类型。
5. 抽象类中能够包含静态方法,接口中不能包含静态方法。
6. 抽象类和接口中均可以包含静态成员变量,抽象类中的静态成员变量的访问类型能够任意,但接口中定义的变量只能是public static final类型,而且默认即为public static final类型。
7. 一个类能够实现多个接口,但只能继承一个抽象类。
abstract的method不能够是static的,由于抽象的方法是要被子类实现的,而static与子类扯不上关系!
native方法表示该方法要用另一种依赖平台的编程语言实现的,不存在着被子类实现的问题,因此,它也不能是抽象的,不能与abstract混用。
关于synchronized与abstract合用的问题,我以为也不行,由于在我几年的学习和开发中,历来没见到过这种状况,而且我以为synchronized应该是做用在一个具体的方法上才有意义。并且,方法上的synchronized同步所使用的同步锁对象是this,而抽象方法上没法肯定this是什么。(话术)
彻底能够。若是不是静态内部类,那没有什么限制!
若是你把静态嵌套类看成内部类的一种特例,那在这种状况下不能够访问外部类的普通成员变量,而只能访问外部类中的静态成员,例如,下面的代码:
class Outer { static int x; static class Inner { voidtest() { syso(x); } } }
没有。由于String被设计成不可变(immutable)类,因此它的全部对象都是不可变对象。在这段代码中,s原先指向一个String对象,内容是 "Hello",而后咱们对s进行了+操做,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另外一个 String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量再也不指向它了。
经过上面的说明,咱们很容易导出另外一个结论,若是常常对字符串进行各类各样的修改,或者说,不可预见的修改,那么使用String来表明字符串的话会引发很大的内存开销。由于String对象创建以后不能再改变,因此对于每个不一样的字符串,都须要一个String对象来表示。这时,应该考虑使用StringBuffer类,它容许修改,而不是每一个不一样的字符串都要生成一个新的对象。而且,这两种类的对象转换十分容易。
同时,咱们还能够知道,若是要使用内容相同的字符串,没必要每次都new一个String。例如咱们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样作:
public class Demo { private String s; ... public Demo { s = "Initial Value"; } ... } 而非 s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,而且没有意义,由于String对象不可改变,因此对于内容相同的字符串,只要一个String对象来表示就能够了。也就说,屡次调用上面的构造器建立多个对象,他们的String类型属性s都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,若是内容相同,Java认为它们表明同一个String对象。而用关键字new调用构造器,老是会建立一个新的对象,不管内容是否相同。
至于为何要把String类设计成不可变类,是它的用途决定的。其实不仅String,不少Java标准类库中的类都是不可变的。在开发一个系统的时候,咱们有时候也须要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优势,好比由于它的对象是只读的,因此多线程并发访问也不会有任何问题。固然也有一些缺点,好比每一个不一样的状态都要一个对象来表明,可能会形成性能上的问题。因此Java标准类库还提供了一个可变版本,即StringBuffer。
这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,至关于一种动态的数组,咱们之后能够按位置索引号取出某个元素,而且其中的数据是容许重复的,这是与HashSet之类的集合的最大不一样处,HashSet之类的集合不能够按索引号去检索其中的元素,也不容许有重复的元素。
ArrayList与Vector的区别主要包括两个方面:
(1)同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不一样步的。若是只有一个线程会访问到集合,那最好是使用ArrayList,由于它不考虑线程安全,效率会高些;若是有多个线程会访问到集合,那最好是使用Vector,由于不须要咱们本身再去考虑和编写线程安全的代码。
(2)数据增加:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就须要增长ArrayList与Vector的存储空间,每次要增长存储空间时,不是只增长一个存储单元,而是增长多个存储单元,每次增长的存储单元的个数在内存空间利用与程序效率之间要取得必定的平衡。Vector默认增加为原来两倍,而ArrayList的增加策略在文档中没有明确规定(从源代码看到的是增加为原来的1.5倍)。ArrayList与Vector均可以设置初始的空间大小,Vector还能够设置增加的空间大小,而ArrayList没有提供设置增加空间的方法。总结:即Vector增加原来的一倍,ArrayList增长原来的0.5倍。
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap容许空(null)键值(key),因为非线程安全,在只有一个线程访问的状况下,效率要高于Hashtable。
HashMap容许将null做为一个entry的key或者value,而Hashtable不容许。
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
最大的不一样是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不须要本身为它的方法实现同步,而HashMap就必须为之提供同步。
就HashMap与HashTable主要从三方面来讲。
一.历史缘由:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的。
三.值:只有HashMap可让你将空值做为一个表的条目的key或value。
一个是存储单列数据的集合,另外一个是存储键和值这样的双列数据的集合,List中存储的数据是有顺序,而且容许重复;Map中存储的数据是没有顺序的,其键是不能重复的,它的值是能够有重复的。
List,Set是,Map不是。
首先,List与Set具备类似性,它们都是单列元素的集合,因此,它们有一个共同的父接口,叫Collection。Set里面不容许有重复的元素,即不能有两个相等(注意,不是仅仅是相同)的对象,即假设Set集合中有了一个A对象,如今我要向Set集合再存入一个B对象,但B对象与A对象equals相等,则B对象存储不进去,因此,Set集合的add方法有一个boolean的返回值,当集合中没有某个元素,此时add方法可成功加入该元素时,则返回true,当集合含有与某个元素equals相等的元素时,此时add方法没法加入该元素,返回结果为false。Set取元素时,不能细说要取第几个,只能以Iterator接口取得全部的元素,再逐一遍历各个元素。
List表示有前后顺序的集合,注意,不是那种按年龄、按大小、按价格之类的排序。当咱们屡次调用add(Obje)方法时,每次加入的对象就像火车站买票有排队顺序同样,按先来后到的顺序排序。有时候,也能够插队,即调用add(intindex,Obj e)方法,就能够指定当前对象在集合中的存放位置。一个对象能够被反复存储进List中,每调用一次add方法,这个对象就被插入进集合中一次,其实,并非把这个对象自己存储进了集合中,而是在集合中用一个索引变量指向这个对象,当这个对象被add屡次时,即至关于集合中有多个索引指向了这个对象,如图x所示。List除了能够用Iterator接口取得全部的元素,再逐一遍历各个元素以外,还能够调用get(index i)来明确说明取第几个。
Map与List和Set不一样,它是双列的集合,其中有put方法,定义以下:put(obj key,obj value),每次存储时,要存储一对key/value,不能存储重复的key,这个重复的规则也是按equals比较相等。取则能够根据key得到相应的value,即get(Object key)返回值为key所对应的value。另外,也能够得到全部的key的结合,还能够得到全部的value的结合,还能够得到key和value组合成的Map.Entry对象的集合。
List以特定次序来持有元素,可有重复元素。Set没法拥有重复元素,内部排序。Map保存key-value值,value可多值。
ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,它们都容许直接按序号索引元素,可是插入元素要涉及数组元素移动等内存操做,因此索引数据快而插入数据慢,Vector因为使用了synchronized方法(线程安全),一般性能上较ArrayList差。而LinkedList使用双向链表实现存储,按序号索引数据须要进行前向或后向遍历,索引就变慢了,可是插入数据时只须要记录本项的先后项便可,因此插入速度较快。
LinkedList也是线程不安全的,LinkedList提供了一些方法,使得LinkedList能够被看成堆栈和队列来使用。
Vector newVector = new Vector(); for (int i=0;i<vector.size();i++) { Object obj = vector.get(i); if(!newVector.contains(obj) newVector.add(obj); }
还有一种简单的方式,利用了Set不容许重复元素:
HashSet set = new HashSet(vector);
Collection是集合类的上级接口,继承他的接口主要有Set和List。
Collections是针对集合类的一个工具类,他提供一系列静态方法实现对各类集合的搜索、排序、线程安全化等操做。
Set里的元素是不能重复的,元素重复与否是使用equals()方法进行判断的。
==操做符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操做符。
equals方法是用于比较两个独立对象的内容是否相同,就比如去比较两我的的长相是否相同,它比较的两个对象是独立的。
好比:两条new语句建立了两个对象,而后用a/b这两个变量分别指向了其中一个对象,这是两个不一样的对象,它们的首地址是不一样的,即a和b中存储的数值是不相同的,因此,表达式a==b将返回false,而这两个对象中的内容是相同的,因此,表达式a.equals(b)将返回true。
最经常使用的集合类是 List 和 Map。 List的具体实现包括 ArrayList和 Vector,它们是可变大小的列表,比较适合构建、存储和操做任何类型对象的元素列表。 List适用于按数值索引访问元素的情形。
Map 提供了一个更通用的元素存储方法。 Map集合类用于存储元素对(称做"键"和"值"),其中每一个键映射到一个值。
它们都有增删改查的方法。
对于set,大概的方法是add,remove, contains等。
对于map,大概的方法就是put,remove,contains等。
List类会有get(int index)这样的方法,由于它能够按顺序取元素,而set类中没有get(int index)这样的方法。List和set均可以迭代出全部元素,迭代时先要获得一个iterator对象,因此,set和list类都有一个iterator方法,用于返回那个iterator对象。map能够返回三个集合,一个是返回全部的key的集合,另一个返回的是全部value的集合,再一个返回的key和value组合成的EntrySet对象的集合,map也有get方法,参数是key,返回值是key对应的value。
两个或一个都有可能,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”无论出现多少遍,都是缓冲区中的那一个。new String每写一遍,就建立一个新的对象,它使用常量”xyz”对象的内容来建立出一个新String对象。若是之前就用过’xyz’,那么这里就不会建立”xyz”了,直接从缓冲区拿,这时建立了一个StringObject;但若是之前没有用过"xyz",那么此时就会建立一个对象并放入缓冲区,这种状况它建立两个对象。至于String类是否继承,答案是否认的,由于String默认final修饰,是不可继承的。
它们能够储存和操做字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串能够进行修改。当你知道字符数据要改变的时候你就能够使用StringBuffer。即,你能够使用StringBuffer来动态构造字符数据。
对于以下代码:
String s1 = "a"; String s2 = s1 + "b"; String s3 = "a" + "b"; System.out.println(s2 == "ab"); System.out.println(s3 == "ab");
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译能够对字符串常量直接相加的表达式进行优化,没必要要等到运行期再去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后,至关于直接定义了一个”abcd”的字符串,因此,上面的代码应该只建立了一个String对象。写以下两行代码,
String s ="a" + "b" +"c" + "d"; System.out.println(s== "abcd");
最终打印的结果应该为true。
首先,finally{} 中的语句必定会执行。但其再也不return以前,也再也不return以后,而是在return中间执行。参考下列程序:
public classTest { public static void main(String[]args) { System.out.println(newTest().test());; } static int test() { intx = 1; try { returnx; } finally{ ++x; } } } ---------执行结果 --------- 1
运行结果是1,为何呢?主函数调用子函数并获得结果的过程,比如主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,而后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话以前放进罐子里的。
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。内部类要访问局部变量,局部变量必须定义成final类型。
finally是异常处理语句结构的一部分,表示老是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,能够覆盖此方法提供垃圾收集时的其余资源回收,例如关闭文件等。可是JVM不保证此方法总被调用。
异常表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,可是并不要求必须声明抛出未被捕获的运行时异常。
error 表示恢复不是不可能但很困难的状况下的一种严重问题。好比说内存溢出。不可能期望程序能处理这样的状况。exception表示一种设计或实现问题。也就是说,它表示若是程序运行正常,从不会发生的状况。
异常是指java程序运行时(非编译)所发生的非正常状况或错误,与现实生活中的事件很类似,现实生活中的事件能够包含事件发生的时间、地点、人物、情节等信息,能够用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每一个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。
Java对异常进行了分类,不一样类型的异常分别用不一样的Java类表示,全部异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:
Error和Exception,Error表示应用程序自己没法克服和恢复的一种严重问题,程序只有奔溃了,例如,说内存溢出和线程死锁等系统问题。
Exception表示程序还可以克服和恢复的问题,其中又分为系统异常和普通异常:
系统异常是软件自己缺陷所致使的问题,也就是软件开发人员考虑不周所致使的问题,软件使用者没法克服和恢复这种问题,但在这种问题下还可让软件系统继续运行或者让软件挂掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);
普通异常是运行环境的变化或异常所致使的问题,是用户可以克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不该该死掉。
Java为系统异常和普通异常提供了不一样的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,因此普通异常也称为checked异常,而系统异常能够处理也能够不处理,因此,编译器不强制用try..catch处理或用throws声明,因此系统异常也称为unchecked异常。
JVM 中堆和栈属于不一样的内存区域,使用目的也不一样。栈经常使用于保存方法帧和局部变量,而对象老是在堆上分配。栈一般都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的全部线程共享。
栈:在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的做用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间能够当即被另做它用。
堆:堆内存用来存放由 new 建立的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象以后,还能够在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,之后就能够在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就至关因而为数组或者对象起的一个名称。
能够作强制转换,可是 Java 中 int 是 32 位的,而 byte 是 8 位的,因此,若是强制转化,int 类型的高 24 位将会被丢弃,由于byte 类型的范围是从 -128 到 127。
hashCode() 方法对应对象整型的 hash 值。它经常使用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具备相同的 hash code。
要把一段二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一段二进制数据,无论输入输出设备是什么,咱们要用统一的方式来完成这些操做,用一种抽象的方式进行描述,这个抽象描述方式起名为IO流,对应的抽象类为OutputStream和InputStream,不一样的实现类就表明不一样的输入和输出设备,它们都是针对字节进行操做的。
计算机中的一切最终都是二进制的字节形式存在。对于常常用到的中文字符,首先要获得其对应的字节,而后将字节写入到输出流。读取时,首先读到的是字节,但是咱们要把它显示为字符,咱们须要将字节转换成字符。因为这样的需求很普遍,Java专门提供了字符流包装类。
底层设备永远只接受字节数据,有时候要写字符串到底层设备,须要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为咱们向IO设备写入或读取字符串提供了一点点方便。
字符向字节转换时,要注意编码的问题,由于字符串转成字节数组,实际上是转成该字符的某种编码的字节形式,读取也是反之的道理。
实际应用中,有时需将一个Java对象转换为字节流的形式传出去或者从一个字节流中恢复成一个Java对象,例如,要将Java对象存储到硬盘或者传送给网络上的其余计算机,这个过程须要将一个Java对象转换为某种格式的字节流再传输。
并且,JRE自己就提供了这种支持,能够调用ObjectOutputStream的writeObject方法来作,这时就须要被传输的对象必须实现Serializable接口,如此,javac编译时就会进行特殊处理,编译的类才能够被writeObject方法操做,即所谓的序列化。须要被序列化的类必须实现Serializable接口,该接口是一个mini接口,其中没有须要实现的方法,implements Serializable只是为了标注该对象是可被序列化的。
例如,在web开发中,若是对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬盘,这个对象就必须实现Serializable接口。若是对象要通过分布式系统进行网络传输,被传输的对象就必须实现Serializable接口。
JVM中类的装在是由ClassLoader和它的子类来实现的,Java ClassLoader是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件中的类。
Java的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。
堆是与栈做用不一样的内存,通常用于存放不在当前方法栈中的那些数据,例如,使用new建立的对象都放在堆里,因此,它不会随方法的结束而消失。方法中的局部变量使用final修饰后,放在堆中,而不是栈中。
GC即Garbage Collection,内存处理是开发人员容易出现问题的地方,忘记或者错误的内存回收会致使程序或系统的不稳定甚至崩溃,Java提供的GC功能能够自动检测对象是否超过做用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显式操做方法。
Java语言中一个显著的特色就是引入了垃圾回收机制,使C++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候再也不过多考虑内存管理。因为垃圾回收机制,Java中的对象再也不有"做用域"的概念,只有对象的引用才有"做用域"。
垃圾回收能够有效的防止内存泄露,有效的使用能够使用的内存。垃圾回收器一般是做为一个单独的低级别的线程运行,不可预知的状况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或全部对象进行垃圾回收。
回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
对于GC来讲,当程序员建立对象时,GC就开始监控这个对象的地址、大小以及使用状况。一般,GC采用有向图的方式记录和管理堆(heap)中的全部对象。经过这种方式肯定哪些对象是"可达的",哪些对象是"不可达的"。当GC肯定一些对象为"不可达"时,GC就有责任回收这些内存空间。
System.gc()和Runtime.getRuntime().gc(),但两者的行为没有任何不一样,前者仅能够理解为后者的简写。其实基本没有什么机会用获得这个命令,由于这个命令只是建议JVM安排GC运行,还有可能彻底被拒绝。GC自己是会周期性的自动运行的,由JVM决定运行的时机,并且如今的版本有多种更智能的模式能够选择,还会根据运行的机器自动去作选择,就算真的有性能上的需求,也应该去对GC的运行机制进行微调,而不是经过使用这个命令来实现性能的优化。
throw 用于抛出 java.lang.Throwable 类的一个实例化对象,意思是说你能够经过关键字 throw 抛出一个Exception,如:
throw new IllegalArgumentException(“XXXXXXXXX″);
而throws 的做用是做为方法声明和签名的一部分,方法被抛出相应的异常以便调用者能处理。Java 中,任何未处理的受检查异常强制在 throws 子句中声明。
所谓内存泄露就是指一个再也不被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它能够保证当对象再也不被引用的时候,对象将自动被垃圾回收器从内存中清除掉。
因为Java使用有向图的方式进行垃圾回收管理,能够消除引用循环的问题,例若有两个对象,相互引用,只要它们和根进程不可达,那么GC也是能够回收它们的。
Java中的内存泄露的状况:长生命周期的对象持有短生命周期对象的引用就极可能发生内存泄露,尽管短生命周期对象已经再也不须要,可是由于长生命周期对象持有它的引用而致使不能被回收,这就是Java中内存泄露的发生场景,通俗地说,就是程序员可能建立了一个对象,之后一直再也不使用这个对象,这个对象却一直被引用,即这个对象无用可是却没法被垃圾回收器回收的,这就是java中可能出现内存泄露的状况,例如,缓存系统,咱们加载了一个对象放在缓存中(例如放在一个全局map对象中),而后一直再也不使用它,这个对象一直被缓存引用,但却再也不被使用。
同时,本地资源的释放,如Opencv中的VideoCapture、Mat等,IO、Connection等流资源的释放,都是内存泄漏的源头。
Servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init(),service()和destroy方法表达。
Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。
web容器加载servlet,生命周期开始。经过调用servlet的init()方法进行servlet的初始化。经过调用service()方法实现,根据请求的不一样调用不一样的do***()方法。结束服务,web容器调用servlet的destroy()方法。
Cookie是会话技术,将用户的信息保存到浏览器的对象。
区别:
1.Cookie数据存放在客户的浏览器上,session数据放在服务器上
2.Cookie不是很安全,别人能够分析存放在本地的Cookie并进行Cookie欺骗,若是主要考虑到安全应当使用Session
3.Session会在必定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,若是主要考虑到减轻服务器性能方面,应当使用Cookie
所以,将登录信息等重要信息存放为SESSION;其余信息若是须要保留,能够放在COOKIE中。
当容器启动时,会读取在webapps目录下全部的web应用中的web.xml文件,而后对xml文件进行解析,并读取servlet注册信息。而后,将每一个应用中注册的servlet类都进行加载,并经过反射的方式实例化。(有时候也是在第一次请求时实例化)
在servlet注册时加上<load-on-startup>1</load-on-startup>若是为正数,则在一开始就实例化,若是不写或为负数,则第一次请求实例化。
1.加载驱动
2.经过DriverManager对象获取链接对象Connection
3.经过链接对象获取会话
4.经过会话进行数据的增删改查,封装对象
5.关闭资源
1.事务是做为单个逻辑工做单元执行的一系列操做
2.一个逻辑工做单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务
事务处理步骤:
3.conn.setAutoComit(false);设置提交方式为手工提交
4.conn.commit()提交事务
5.出现异常,回滚 conn.rollback()
1.数据库链接是一件费时的操做,链接池能够使多个操做共享一个链接
2.数据库链接池的基本思想就是为数据库链接创建一个“缓冲池”。预先在缓冲池中放入必定数量的链接,当须要创建数据库链接时,只需从“缓冲池”中取出一个,使用完毕以后再放回去。咱们能够经过设定链接池最大链接数来防止系统无尽的与数据库链接。更为重要的是咱们能够经过链接池的管理机制监视数据库的链接的数量、使用状况,为系统开发,测试及性能调整提供依据
3.使用链接池是为了提升对数据库链接资源的管理
JDBC的DriverManager是一个工厂类,咱们经过它来建立数据库链接。当JDBC的Driver类被加载进来时,它会本身注册到DriverManager类里面;而后咱们会把数据库配置信息传成DriverManager.getConnection()方法,DriverManager会使用注册到它里面的驱动来获取数据库链接,并返回给调用的程序。
1.Spring是实现了工厂模式的工厂类,这个类名为BeanFactory(其实是一个接口),在程序中一般BeanFactory的子类ApplicationContext。Spring至关于一个大的工厂类,在其配置文件中经过<bean>元素配置用于建立实例对象的类名和实例对象的属性。
2. Spring提供了对IOC良好支持,IOC是一种编程思想,是一种架构艺术,利用这种思想能够很好地实现模块之间的解耦,IOC也称为DI(Depency Injection)。
3. Spring提供了对AOP技术的良好封装, AOP称为面向切面编程,就是系统中有不少各不相干的类的方法,在这些众多方法中要加入某种系统功能的代码,例如,加入日志,加入权限判断,加入异常处理,这种应用称为AOP。
实现AOP功能采用的是代理技术,客户端程序再也不调用目标,而调用代理类,代理类与目标类对外具备相同的方法声明,有两种方式能够实现相同的方法声明,一是实现相同的接口,二是做为目标的子类。
在JDK中采用Proxy类产生动态代理的方式为某个接口生成实现类,若是要为某个类生成子类,则能够用cglib。在生成的代理类的方法中加入系统功能和调用目标类的相应方法,系统功能的代理以Advice对象进行提供,显然要建立出代理对象,至少须要目标类和Advice类。Spring提供了这种支持,只须要在Spring配置文件中配置这两个元素便可实现代理和AOP功能。
轻量:Spring 是轻量的,基本的版本大约2MB。
控制反转:Spring经过控制反转实现了松散耦合,对象们给出它们的依赖,而不是建立或查找依赖的对象们。
面向切面的编程(AOP):Spring支持面向切面的编程,而且把应用业务逻辑和系统服务分开。
容器:Spring 包含并管理应用中对象的生命周期和配置。
MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
事务管理:Spring 提供一个持续的事务管理接口,能够扩展到上至本地事务下至全局事务(JTA)。
异常处理:Spring 提供方便的API把具体技术相关的异常(好比由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。
FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你须要正确设置classpath由于这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的全部bean。
Spring框架支持如下五种bean的做用域:
singleton : bean在每一个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义能够有多个实例。
request:每次http请求都会建立一个bean,该做用域仅在基于web的Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该做用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该做用域仅在基于web的Spring ApplicationContext情形下有效。
缺省的Spring bean的做用域是Singleton。
一、Spring容器从XML文件中读取bean的定义,或者扫描实现@Component的类,并实例化bean。
二、Spring根据bean的定义填充全部的属性。
三、若是bean实现了BeanNameAware接口,Spring传递bean的ID到setBeanName方法。
四、若是bean实现了BeanFactoryAware接口,Spring传递beanFactory给setBeanFactory方法。
五、若是有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization方法内调用它们。
六、若是bean实现了IntializingBean,调用它的afterPropertySet方法,若是bean声明了初始化方法,调用此初始化方法。
七、若是有BeanPostProcessors和bean关联,这些bean的postProcesserAfterInitialization方法将被调用。
八、若是bean实现了DisposableBean,它将调用destroy方法。
#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;
$将传入的数据直接显示生成在SQL中。
注意:使用$占位符可能会致使SQL注射攻击,能用#的地方就不要使用$。而$方式通常用于传入数据库对象,例如传入表名或列名,尤为是在写order by子句的时候应该用$而不是#。如:order by ${user_id},若是传入的值是111,那么解析成sql时的值为order by 111, 若是传入的值是id,则解析成的sql为order by id,而用#则为order by 'id',能够经过执行,但无效。
在大型项目中,可能存在大量的SQL语句,这时候为每一个SQL语句起一个惟一的标识(ID)就变得并不容易了。为了解决这个问题,在MyBatis中,能够为每一个映射文件起一个惟一的命名空间,这样定义在这个映射文件中的每一个SQL语句就成了定义在这个命名空间中的一个ID。只要咱们可以保证每一个命名空间中这个ID是惟一的,即便在不一样映射文件中的语句ID相同,也不会再产生冲突了。命名空间,通常命名为xml对应Mapper接口的参考路径名 reference path,com.rosetta.image.mapper.UserMapper。
对于一些复杂的查询,可能会指定多个查询条件,可是这些条件可能存在也可能不存在,若是不使用持久层框架咱们可能须要本身拼装SQL语句,不过MyBatis提供了动态SQL的功能来解决这个问题。MyBatis中用于实现动态SQL的元素主要有:
if - choose / when / otherwise - trim - where - set - foreach
用法举例:
<select id="foo" parameterType="Blog" resultType="Blog"> select * from t_blog where 1 = 1 <if test="title != null"> and title = #{title} </if> <if test="content != null"> and content = #{content} </if> <if test="owner != null"> and owner = #{owner} </if> </select>
1.JDBC:数据库连接建立、释放频繁形成系统资源浪费从而影响系统性能,若是使用数据库连接池可解决此问题。
MyBatis:在SqlMapConfig.xml中配置数据连接池,使用链接池管理数据库连接。
2.JDBC:Sql语句写在代码中形成代码不易维护,实际应用sql变化的可能较大,sql变更须要改变java代码。
MyBatis:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3.JDBC:向sql语句传参数麻烦,由于sql语句的where条件不必定,可能多也可能少,占位符须要和参数一一对应。
MyBatis: Mybatis自动将java对象映射至sql语句。
4.JDBC:对结果集解析麻烦,sql变化致使解析代码变化,且解析前须要遍历,若是能将数据库记录封装成pojo对象解析比较方便。
MyBatis:Mybatis自动将sql执行结果映射至java对象。
Mybatis首先去缓存中查询结果集,若是没有则查询数据库,若是有则从缓存取出返回结果集就不走数据库。Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象。
Mybatis的二级缓存即查询缓存,它的做用域是一个mapper的namespace,即在同一个namespace中查询sql能够从缓存中获取数据。二级缓存是能够跨SqlSession的。
student(sno,sname,sage,ssex)学生表
course(cno,cname,tno) 课程表
sc(sno,cno,score) 成绩表
teacher(tno,tname) 教师表
select a.sno from (select sno,score from sc where cno=1) a, (select sno,score from sc where cno=2) b where a.score>b.score and a.sno=b.sno
select a.sno as "学号", avg(a.score) as "平均成绩" from (select sno,score from sc) a group by sno having avg(a.score)>60
select a.sno as 学号, b.sname as 姓名, count(a.cno) as 选课数, sum(a.score) as 总成绩 from sc a, student b where a.sno = b.sno group by a.sno, b.sname 或者: selectstudent.sno as 学号, student.sname as 姓名, count(sc.cno) as 选课数, sum(score) as 总成绩 from student left Outer join sc on student.sno = sc.sno group by student.sno, sname
select student.sno,student.sname from student where sno not in (select distinct(sc.sno) from sc,course,teacher where sc.cno=course.cno and teacher.tno=course.tno and teacher.tname='张三')
select sno, sname from student where sno in (select sno from sc where sc.cno = 1) and sno in (select sno from sc where sc.cno = 2) 或者: selectc.sno, c.sname from (select sno from sc where sc.cno = 1) a, (select sno from sc where sc.cno = 2) b, student c where a.sno = b.sno and a.sno = c.sno 或者: select student.sno,student.sname from student,sc where student.sno=sc.sno and sc.cno=1 and exists( select * from sc as sc_2 where sc_2.sno=sc.sno and sc_2.cno=2)
select a.sno, a.sname from student a, sc b where a.sno = b.sno and b.cno in (select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四') 或者: select a.sno, a.sname from student a, sc b, (select c.cno from course c, teacher d where c.tno = d.tno and d.tname = '李四') e where a.sno = b.sno and b.cno = e.cno
select a.sno, a.sname from student a, (select sno, score from sc where cno = 1) b, (select sno, score from sc where cno = 2) c where b.score > c.score and b.sno = c.sno and a.sno = b.sno
select distinct a.sno, a.sname from student a, sc b where a.sno <> 1 and a.sno=b.sno and b.cno in (select cno from sc where sno = 1) 或者: select s.sno,s.sname from student s, (select sc.sno from sc where sc.cno in (select sc1.cno from sc sc1 where sc1.sno=1)and sc.sno<>1 group by sc.sno)r1 where r1.sno=s.sno
update sc set score = (select avg(sc_2.score) from sc sc_2 wheresc_2.cno=sc.cno) from course,teacher where course.cno=sc.cno and course.tno=teacher.tno andteacher.tname='王五'
delete sc from course, teacher where course.cno = sc.cno and course.tno = teacher.tno and tname = '王五'
insert sc select sno, 3, (select avg(score) from sc where cno = 2) from student where sno not in (select sno from sc where cno = 3)
select sno as 学号 ,max(case when cno = 1 then score end) AS 企业管理 ,max(case when cno = 2 then score end) AS 马克思 ,max(case when cno = 3 then score end) AS UML ,max(case when cno = 4 then score end) AS 数据库 ,max(case when cno = 5 then score end) AS 物理 ,count(cno) AS 课程数 ,avg(score) AS 平均分 FROM sc GROUP by sno ORDER by avg(score) DESC
SELECT t.cno AS 课程号, max(course.cname)AS 课程名, isnull(AVG(score),0) AS 平均成绩, 100 * SUM(CASE WHEN isnull(score,0)>=60 THEN 1 ELSE 0 END)/count(1) AS 及格率 FROM sc t, course where t.cno = course.cno GROUP BY t.cno ORDER BY 及格率 desc
select avg(case when cno = 1 then score end) as 平均分1, avg(case when cno = 2 then score end) as 平均分2, avg(case when cno = 3 then score end) as 平均分3, avg(case when cno = 4 then score end) as 平均分4, 100 * sum(case when cno = 1 and score > 60 then 1 else 0 end) / sum(casewhen cno = 1 then 1 else 0 end) as 及格率1, 100 * sum(case when cno = 2 and score > 60 then 1 else 0 end) / sum(casewhen cno = 2 then 1 else 0 end) as 及格率2, 100 * sum(case when cno = 3 and score > 60 then 1 else 0 end) / sum(casewhen cno = 3 then 1 else 0 end) as 及格率3, 100 * sum(case when cno = 4 and score > 60 then 1 else 0 end) / sum(casewhen cno = 4 then 1 else 0 end) as 及格率4 from sc
select max(c.tname) as 教师, max(b.cname) 课程, avg(a.score) 平均分 from sc a, course b, teacher c where a.cno = b.cno and b.tno = c.tno group by a.cno order by 平均分 desc 或者: select r.tname as '教师',r.rname as '课程' , AVG(score) as '平均分' from sc, (select t.tname,c.cno as rcso,c.cname as rname from teacher t ,course c where t.tno=c.tno)r where sc.cno=r.rcso group by sc.cno,r.tname,r.rname order by AVG(score) desc
select top 6 max(a.sno) 学号, max(b.sname) 姓名, max(case when cno = 1 then score end) as 企业管理, max(case when cno = 2 then score end) as 马克思, max(case when cno = 3 then score end) as UML, max(case when cno = 4 then score end) as 数据库, avg(score) as 平均分 from sc a, student b where a.sno not in (select top 2 sno from sc where cno = 1 order by score desc) and a.sno not in (select top 2 sno from sc where cno = 2 order by scoredesc) and a.sno not in (select top 2 sno from sc where cno = 3 order by scoredesc) and a.sno not in (select top 2 sno from sc where cno = 4 order by scoredesc) and a.sno = b.sno group by a.sno
线程是操做系统可以进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运做单位。程序员能够经过它进行多处理器编程,能够使用多线程对运算密集型任务提速。
线程是进程的子集,一个进程能够有不少线程,每条线程并行执行不一样的任务。不一样的进程使用不一样的内存空间,而全部的线程共享一片共同的内存空间。每一个线程都拥有单独的栈内存用来存储本地数据。
两种方式:java.lang.Thread 类的实例就是一个线程可是它须要调用java.lang.Runnable接口来执行,因为线程类自己就是调用的Runnable接口因此你能够继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。
1.volatile
它所修饰的变量不保留拷贝,直接访问主内存中的。
在Java内存模型中,有主内存,每一个线程也有本身的内存(例如寄存器、栈内存)。为了性能,一个线程会在本身的内存中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程内存中的值能够与另外一个线程内存中的值,或者主内存中的值不一致的状况。一个变量声明为volatile,就意味着这个变量是随时会被其它线程修改的,所以不能将它缓存在线程内存中。
2.synchronized
当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多只有一个线程执行该段代码。
a、当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一个时间内只能有一个线程获得执行。另外一个线程必须等待当前线程执行完这个代码块之后才能执行该代码块。
b、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该object中的非synchronized(this)同步代码块。
c、尤为关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其余线程对object中全部其它synchronized(this)同步代码块的访问将被阻塞。
d、当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞。
e、以上规则对其它对象锁一样适用。
在Java程序中新建一个线程时,它的状态是New。当调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间而且将它们的状态改变为Running。其余的线程状态还有Waiting,Blocked 和Dead。
每个线程都是有优先级的,通常来讲,高优先级的线程在运行时会具备优先权,但这依赖于线程调度的实现,这个实现是和操做系统相关的(OS dependent)。咱们能够定义线程的优先级,可是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1表明最低优先级,10表明最高优先级。
死锁是指两个以上的线程永远阻塞的状况,这种状况的产生至少须要两个以上的线程和两个以上的资源。
分析死锁,咱们须要查看Java应用程序的线程转储。须要找出那些状态为BLOCKED的线程和它们等待的资源。每一个资源都有一个惟一的id,用这个id咱们能够找出哪些线程已经拥有了它的对象锁。
避免嵌套锁,只在须要的地方使用锁和避免无限期等待是避免死锁的一般办法。
若是代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。若是每次运行结果和单线程运行的结果是同样的,并且其余的变量的值也和预期的是同样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的状况下也不会出现计算失误。很显然你能够将集合类分红两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它类似的ArrayList不是线程安全的。
Java提供了很丰富的API但没有为中止线程提供API。JDK 1.0原本有一些像stop(), suspend() 和 resume()的控制方法可是因为潜在的死锁威胁所以在后续的JDK版本中他们被弃用了,以后Java API的设计者就没有提供一个兼容且线程安全的方法来中止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,若是要手动结束一个线程,你能够用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。
ThreadLocal用于建立线程的本地变量,一个对象的全部线程会共享它的全局变量,因此这些变量不是线程安全的,这时能够使用同步技术。可是当不想使用同步的时候,能够选择ThreadLocal变量。
每一个线程都会拥有自身的ThreadLocal变量,它们能够使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例一般是但愿它们同线程状态关联起来是private static属性。
Thread.sleep()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。好比一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。若是另外一线程调用了interrupt()方法,它将唤醒那个“睡眠的”线程。
注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep(),(这里的t是一个不一样于当前线程的线程)。即使是执行t.sleep(),也是当前线程进入睡眠,而不是t线程。t.suspend()是过期的方法,使用suspend()致使线程进入停滞状态,该线程会一直持有对象的监视器,suspend()容易引发死锁问题。
Object.wait()使当前线程出于“不可运行”状态,和sleep()不一样的是wait是object的方法而不是thread。调用object.wait()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另外一线程能够同步同一个对象锁来调用object.notify(),这样将唤醒原来等待中的线程,而后释放该锁。基本上wait()/notify()与sleep()/interrupt()相似,只是前者须要获取对象锁。
线程饿死,指当全部线程阻塞、或者因为须要的资源无效而不能处理,不存在非阻塞线程使资源可用的状况。
JavaAPI中线程活锁可能发生在如下情形:
1.当全部线程在程序中执行Object.wait(0),参数为0的wait方法。程序将发生活锁直到在相应的对象上有线程调用Object.notify()或者Object.notifyAll()。
2.当全部线程卡在无限循环中。
同步块是更好的选择,由于它不会锁住整个对象(固然也可让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这一般会致使他们中止执行并须要等待得到这个对象上的锁。
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,即所操做的数据类型被指定为一个参数。
好处:
1.类型安全,提供编译期间的类型检测
2.先后兼容
3.泛化代码,代码能够更多的重复利用
4.性能较高,用GJ(泛型Java : Genericity Java)编写的代码能够为Java编译器和虚拟机带来更多的类型信息,这些信息对Java程序作进一步优化提供条件。
1.类型检查:在生成字节码以前提供类型检查
2.类型擦除:全部类型参数都用它们的限定类型替换,包括类、变量和方法
3.若是类型擦除和多态性发生了冲突时,则在子类中生成桥方法解决
4.若是调用泛型方法的返回类型被擦除,则在调用该方法时插入强制类型转换
类型擦除:全部类型参数都用他们的限定类型替换 ,好比:
T -> Object ? extends BaseClass -> BaseClass;
如何工做:
泛型是经过类型擦除来实现的,编译器在编译时擦除了全部类型相关的信息,因此在运行时不存在任何类型相关的信息。例如 List<String>在运行时仅用一个List来表示。这样作的目的,是确保能和Java 5以前的版本开发二进制类库进行兼容。你没法在运行时访问到类型参数,由于编译器已经把泛型类型转换成了原始类型。根据你对这个泛型问题的回答状况,你会获得一些后续提问,好比为何泛型是由类型擦除来实现的或者给你展现一些会致使编译器出错的错误泛型代码。
不能够,由于List<Object>能够存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储String s。
List<Object> objectList; List<String> stringList; objectList = stringList; //compilation error incompatible types
1.根据须要选择正确的集合类型。好比,若是指定了大小,咱们会选用Array而非ArrayList(Array不支持泛型,平常使用推荐List)。若是咱们想根据插入顺序遍历一个Map,咱们须要使用TreeMap。若是咱们不想重复,咱们应该使用Set。
2.一些集合类容许指定初始容量,因此若是咱们可以估计到存储元素的数量,咱们能够使用它,就避免了从新哈希或大小调整。
3.基于接口编程,而非基于实现编程,它容许咱们后来轻易地改变实现。
4.老是使用类型安全的泛型,避免在运行时出现ClassCastException。
5.使用JDK提供的不可变类做为Map的key,能够避免本身实现hashCode()和equals()。
6.尽量使用Collections工具类以及Apache commons工具包,或者获取只读、同步或空的集合,而非编写本身的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性。
Java内存模型规定和指引Java程序在不一样的内存架构、CPU和操做系统间有肯定性的行为。它在多线程的状况下尤为重要。Java内存模型对一个线程所作的变更能被其它线程可见提供了保证,它们之间是先行发生原则。这个关系定义了一些规则,编译并发编程的代码实现。好比,先行发生原则确保了:
线程内的代码可以按前后顺序执行,即程序次序规则。
对于同一个锁,一个解锁操做必定要发生在时间上后发生的另外一个锁定操做以前,即管程锁定规则。
前一个对volatile的写操做在后一个volatile的读操做以前,即volatile变量规则。
一个线程内的任何操做必须在这个线程的start()调用以后,即线程启动规则。
一个线程的全部操做都会在线程终止以前,即线程终止规则。
一个对象的终结操做必须在这个对象构造完成以后,即对象终结原则。
同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5以前程序员们只有同步集合来用且在多线程并发的时候会致使争用,阻碍了系统的扩展性。Java1.5提供了并发集合像ConcurrentHashMap,不只提供线程安全还用锁分离和内部分区等现代技术提供了可扩展性。
无论是同步集合仍是并发集合他们都支持线程安全,他们之间主要的区别体如今性能和可扩展性,还有他们如何实现的线程安全上。
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他们并发的实现(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)会慢得多。主要缘由是锁,同步集合会把整个Map或List锁起来,而并发集合不会。并发集合实现线程安全经过使用锁剥离等新手段。
好比ConcurrentHashMap会把整个Map划分红几个片断,只对相关的几个片断上锁,同时容许多线程访问其余未上锁的片断。
一样的,CopyOnWriteArrayList容许多个线程以非同步的方式读,当有线程写的时候它会将整个List复制一个副本给它。
若是在读多写少这种对并发集合有利的条件下使用并发集合,这会比使用同步集合更具备可伸缩性。
建立线程要花费昂贵的资源和时间,若是任务来了才建立线程那么响应时间会变长,并且一个进程能建立的线程数有限。为了不这些问题,在程序启动的时候就建立若干线程来响应处理,它们被称为线程池,里面的线程叫工做线程。从JDK1.5开始,Java API提供了Executor框架让你能够建立不一样的线程池。好比单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合不少生存期短的任务的程序的可扩展线程池)。
线程池的做用,就是在调用线程的时候初始化必定数量的线程,有线程过来的时候,先检测初始化的线程还有空的没有,没有就再看当前运行中的线程数是否是已经达到了最大数,若是没有,就新分配一个线程去处理。
线程池的优势就是能够管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会由于大量的并发而由于资源不足挂掉。
活锁:一个线程有时会响应其它线程的活动。若是其余线程也会响应另外一个线程的活动,那么就有可能发生活锁。同死锁同样,发生活锁的线程没法继续执行。然而线程并无阻塞——他们在忙于响应对方没法恢复工做。这就至关于两个在走廊相遇的人:甲向他本身的左边靠想让乙过去,而乙向他的右边靠想让甲过去。可见他们阻塞了对方。甲向他的右边靠,而乙向他的左边靠,他们仍是阻塞了对方。
死锁:两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁一般发生在多个线程同时但以不一样的顺序请求同一组锁的时候,死锁会让你的程序挂起没法完成任务。
死锁的发生必须知足如下四个条件:
互斥条件:一个资源每次只能被一个线程使用
请求与保持条件:一个线程因请求资源而阻塞时,对已得到的资源保持不放
不剥夺条件:线程已得到的资源,在未使用完以前,不能强行剥夺
循环等待条件:若干线程之间造成一种首尾相接的循环等待资源关系
三种用于避免死锁的技术:
加锁顺序(线程按照必定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上必定的时限,超过期限则放弃对该锁的请求,并释放本身占有的锁)
死锁检测
1.notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
2.void notify() :唤醒一个正在等待该对象的线程。
3.void notifyAll() :唤醒全部正在等待该对象的线程。
二者的最大区别在于:
notifyAll 使全部原来在该线程上等待被notify的线程通通退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,它们将会去竞争。
notify 只是选择一个wait状态线程进行通知,并使它得到该对象上的锁,但不惊动其余一样在等待被该对象notify的线程,当第一个线程运行完毕之后释放对象上的锁,此时若是该对象没有再次使用notify语句,即便该对象已经空闲,其它wait状态等待的线程因为没有获得该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
java.util.concurrent.Lock中的Lock框架是锁的一个抽象,它容许把锁的实现做为Java类,而不是做为语言的特性来实现。这为Lock的多种实现提供了空间,不一样的实现能够有不一样的调度算法、性能特性或者锁语义。
ReentrantLock类实现了Lock,它拥有与 synchronized 相同的并发行和内存语义,可是添加了相似锁投票、定时锁等候和可中断锁等候的特性。此外,它还提供了在激烈争用状况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM能够花更少的时间来调度线程,把更多时间用在执行线程上。)
ReentrantLock意味着什么呢?它有一个与锁相关的获取计数器,若是拥有锁的某个线程再次获得锁,那么获取计数器就加一,而后锁须要被释放两次才能得到真正释放。这模仿了synchronized的语义:若是线程进入由线程已经拥有的监控器保护的synchronized块,就容许线程继续执行,当线程退出第二个(或者后续)synchronized块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized块时,才释放锁。
读写锁能够用于 “多读少写” 的场景,读写锁支持多个读操做并发执行,写操做只能由一个线程来操做。
ReadWriteLock对向数据结构相对不频繁地写入,可是有多个任务要常常读取这个数据结构的这类状况进行了优化。ReadWriteLock使得你能够同时有多个读取者,只要它们都不试图写入便可。若是写锁已经被其余任务持有,那么任何读取者都不能访问,直至这个写锁被释放为止。
ReadWriteLock对程序性能的提升主要受制于以下几个因素:
1.数据被读取的频率与被修改的频率相比较的结果
2.读取和写入的时间
3.有多少线程竞争
4.是否在多处理机器上运行
概念:
栈(stack)是为了执行线程留出的内存空间。当函数被调用的时候,栈顶为局部变量和一些“簿记(bookkeeping)”数据预留块。当函数执行完毕,块就没有了,可能在下次的函数调用的时候再被使用。栈一般用后进先出的方式预留空间;所以最近的保留块一般最早被释放。这样作能够使跟踪堆栈变得简单;从栈中释放块只不过是指针的偏移而已。
堆(heap)是为动态分配预留的内存空间。和栈不同,从堆上分配和从新分配块没有固定模式;你能够在任什么时候候分配和释放它。这样使得跟踪哪部分堆已经被分配和被释放变的异常复杂;有许多定制的堆分配策略来为不一样的使用模式下调整堆的性能。
区别:
内存分配:
栈:由编译器自动分配和释放,存放函数的参数、局部变量、临时变量、函数返回地址等。
堆:通常人为分配和释放,对Java而言是由系统释放回收,但对于C++等,必须手动释放,若是没有手动释放会引发内存泄漏。
系统响应:
栈:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,不然将报异常提示栈溢出。
堆:在记录空闲内存地址的链表中寻找一个空间大于所申请空间的堆节点,而后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。
大小限制:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的,在 windows下,栈的大小是2M,若是申请的空间超过栈的剩余空间时,将提示overflow。所以,能从栈得到的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是因为系统是用链表来存储的空闲内存地址的,天然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。
结论:堆得到的空间比较灵活,也比较大。
分配效率:
栈:由系统自动分配,速度较快,没法人为控制。
堆:由new分配的内存,通常速度比较慢,并且容易产生内存碎片,不过用起来最方便。
存储内容:
栈:在栈中,第一个进栈的是主函数下一条指令的地址,而后是函数的各个参数,在大多数编译器中,参数是由右向左入栈,而后是函数中的局部变量。注意,静态变量不入栈。
堆:通常在堆的头部用一个字节存放堆的大小,具体内容受人为控制。
比较:
1.稳定性比较
插入排序、冒泡排序、二叉树排序、二路归并排序及其它线形排序是稳定的
选择排序、希尔排序、快速排序、堆排序是不稳定的
2.时间复杂度比较
插入排序、冒泡排序、选择排序的时间复杂度为 O(n2)
其它非线形排序的时间复杂度为 O(nlog2n)
线形排序的时间复杂度为O(n)
3.辅助空间的比较
线形排序、二路归并排序的辅助空间为O(n)
其它排序的辅助空间为O(1)
4.其它比较
*插入、冒泡排序的速度较慢,但参加排序的序列局部或总体有序时,这种排序能达到较快的速度,可是在这种状况下,快速排序反而慢了。
*当n较小时,对稳定性不做要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。
*若待排序的记录的关键字在一个明显有限范围内时,且空间容许是用桶排序。
*当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。
*当n较大时,关键字元素可能出现自己是有序的,对稳定性有要求时,空间容许的状况下宜用归并排序。
*当n较大时,关键字元素可能出现自己是有序的,对稳定性没有要求时宜用堆排序。
常见的排序算法:
选择排序
public class SelectionSort { public void selectionSort(int[] array) { int temp; for (int i = 0; i < array.length - 1; i++) { for (int j = i + 1; j <= array.length - 1; j++) { if (array[i] > array[j]) { // 注意和冒泡排序的区别,这里是i和j比较。 temp = array[i]; array[i] = array[j]; array[j] = temp; } } // 打印每趟排序结果 for (int m = 0; m <= array.length - 1; m++) { System.out.print(array[m] + "\t"); } System.out.println(); } } public static void main(String[] args) { SelectionSort selectionSort = new SelectionSort(); int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 }; selectionSort.selectionSort(array); for (int m = 0; m <= array.length - 1; m++) { System.out.print(array[m] + "\t"); } } }
插入排序
public class InsertSort { public void insertSort(int[] array, int first, int last) { int temp, i, j; for (i = first + 1; i <= last - 1; i++) { temp = array[i]; j = i - 1; while (j >= first && array[j] > temp) { array[j + 1] = array[j]; j--; } array[j + 1] = temp; // 打印每次排序结果 for (int m = 0; m <= array.length - 1; m++) { System.out.print(array[m] + "\t"); } System.out.println(); } } public static void main(String[] args) { InsertSort insertSort = new InsertSort(); int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 }; insertSort.insertSort(array, 0, array.length); for (int i = 0; i <= array.length - 1; i++) { System.out.print(array[i] + "\t"); } } }
快速排序
public class QuickSort { public int partition(int[] sortArray, int low, int height) { int key = sortArray[low]; while (low < height) { while (low < height && sortArray[height] >= key) height--; sortArray[low] = sortArray[height]; while (low < height && sortArray[low] <= key) low++; sortArray[height] = sortArray[low]; } sortArray[low] = key; // 打印每次排序结果 for (int i = 0; i <= sortArray.length - 1; i++) { System.out.print(sortArray[i] + "\t"); } System.out.println(); return low; } public void sort(int[] sortArray, int low, int height) { if (low < height) { int result = partition(sortArray, low, height); sort(sortArray, low, result - 1); sort(sortArray, result + 1, height); } } public static void main(String[] args) { QuickSort quickSort = new QuickSort(); int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 }; for (int i = 0; i <= array.length - 1; i++) { System.out.print(array[i] + "\t"); } System.out.println(); quickSort.sort(array, 0, 8); for (int i = 0; i <= array.length - 1; i++) { System.out.print(array[i] + "\t"); } } }
希尔排序
public class ShellSort { public void shellSort(int[] array, int n) { int i, j, gap; int temp; for (gap = n / 2; gap > 0; gap /= 2) { for (i = gap; i < n; i++) { for (j = i - gap; j >= 0 && array[j] > array[j + gap]; j -= gap) { temp = array[j]; array[j] = array[j + gap]; array[j + gap] = temp; } // 打印每趟排序结果 for (int m = 0; m <= array.length - 1; m++) { System.out.print(array[m] + "\t"); } System.out.println(); } } } public static void main(String[] args) { ShellSort shellSort = new ShellSort(); int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 }; shellSort.shellSort(array, array.length); for (int m = 0; m <= array.length - 1; m++) { System.out.print(array[m] + "\t"); } } }
ArithmeticException, 数学运算异常,eg:整数除以零
ArrayStoreException, 数组保存异常,eg:数组类型不匹配
BufferOverflowException, 缓冲区上溢异常,eg:ByteBuffer.allocate(2);分配的两个字节而put了三个
BufferUnderflowException, 缓冲区下溢异常,eg:ByteBuffer中保存了一个字节,而请求两个字节
ClassCastException, 类转换异常
ConcurrentModificationException, 对Vector、ArrayList迭代同时对其修改抛该异常,缘由:成员内部类中成员变量更新不及时,参见:https://www.cnblogs.com/dolphin0520/p/3933551.html
IllegalArgumentException, 非法参数异常
IllegalMonitorStateException,
抛出这个异常代表线程尝试等待一个对象的监视器或者去通知其余正在等待这个对象监视器的线程时,可是没有拥有这个监视器的全部权。
1>当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法; 2>当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。 3>当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。
IndexOutOfBoundsException, 角标越界异常
MissingResourceException, 找不到指定的配置文件
NegativeArraySizeException, 建立大小为负的数组,抛出该异常
NoSuchElementException, 多见流操做,hasNext()判断后再next()
NullPointerException, 空指针异常
UndeclaredThrowableException, 使用jdk动态代理接口时,
若方法执行过程当中抛出了受检异常但方法签名又没有声明该异常时则会被代理 类包装成UndeclaredThrowableException抛出
UnmodifiableSetException, 当因为设置不可修改而没法执行请求的操做时,抛出该异常。
UnsupportedOperationException 常见数组转集合后,给集合add/remove时报错。
这是由于Arrays.asList(String[] aa)转换的类型为其内部类ArrayList,不包含add/remove
超键:在关系中能惟一标识元组的属性集称为关系模式的超键。一个属性能够为做为一个超键,多个属性组合在一块儿也能够做为一个超键。超键包含候选键和主键。
候选键:是最小超键,即没有冗余元素的超键。
主键:数据库表中对储存数据对象予以惟一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null)。
外键:在一个表中存在的另外一个表的主键称此表的外键。
事务:就是被绑定在一块儿做为一个逻辑工做单元的 SQL 语句分组,若是任何一个语句操做失败那么整个操做就被失败,之后操做就会回滚到操做前状态,或者是上有个节点。为了确保要么执行,要么不执行,就能够使用事务。要将有组语句做为事务考虑,就须要经过 ACID 测试,即原子性,一致性,隔离性和持久性。
锁:在全部的 DBMS 中,锁是实现事务的关键,锁能够保证事务的完整性和并发性。与现实生活中锁同样,它能够使某些数据的拥有者,在某段时间内不能使用某些数据或数据结构。固然锁还分级别的。
原子性:整个事务中的全部操做,要么所有完成,要么所有不完成,不可能停滞在中间某个环节。事务在执行过程当中发生错误,会被回滚(Rollback)到事务开始前的状态。
一致性:在事务开始以前和事务结束之后,数据库的完整性约束没有被破坏。
隔离性:隔离状态执行事务,使其好像是系统在给定时间内执行的惟一操做。若是有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一个事务在系统中认为只有该事务在使用系统。这种属性有时成为串行化,为了防止事务操做间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
持久性:在事务完成之后,该事务对数据库所作的更改便保存在数据库之中,并不会被回滚。
视图是一种虚拟的表,具备和物理表相同的功能。能够对视图进行增,改,查,操做,试图一般是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得咱们获取数据更容易,相比多表查询。
以下两种场景通常会使用到视图:
(1)不但愿访问者获取整个表的信息,只暴露部分字段给访问者,因此就建一个虚表,就是视图。
(2)查询的数据来源于不一样的表,而查询者但愿以统一的方式查询,这样也能够创建一个视图,把多个表查询结果联合起来,查询者只须要直接从视图中获取数据,没必要考虑数据来源于不一样表所带来的差别。
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现一般使用B树及其变种B+树。
在数据以外,数据库系统还维护着知足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就能够在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
为表设置索引要付出代价的:一是增长了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(由于索引也要随之变更)。
建立索引能够大大提升系统的性能(优势):
第一,经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。
第二,能够大大加快数据的检索速度,这也是建立索引的最主要的缘由。
第三,能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序子句进行数据检索时,一样能够显著减小查询中分组和排序的时间。
第五,经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。
增长索引也有许多不利的方面:
第一,建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。
第二,索引须要占物理空间,除了数据表占数据空间以外,每个索引还要占必定的物理空间,若是要创建聚簇索引,那么须要的空间就会更大。
第三,当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这样就下降了数据的维护速度。
索引是创建在数据库表中的某些列的上面。在建立索引的时候,应该考虑在哪些列上能够建立索引,在哪些列上不能建立索引。
通常来讲,应该在这些列上建立索引:
(1)在常常须要搜索的列上,能够加快搜索的速度;
(2)在做为主键的列上,强制该列的惟一性和组织表中数据的排列结构;
(3)在常常用在链接的列上,这些列主要是一些外键,能够加快链接的速度;
(4)在常常须要根据范围进行搜索的列上建立索引,由于索引已经排序,其指定的范围是连续的;
(5)在常常须要排序的列上建立索引,由于索引已经排序,这样查询能够利用索引的排序,加快排序查询时间;
(6)在常用在WHERE子句中的列上面建立索引,加快条件的判断速度。
一样,对于有些列不该该建立索引:
第一,对于那些在查询中不多使用或者参考的列不该该建立索引。这是由于,既然这些列不多使用到,所以有索引或者无索引,并不能提升查询速度。相反,因为增长了索引,反而下降了系统的维护速度和增大了空间需求。
第二,对于那些只有不多数据值的列也不该该增长索引。这是由于,因为这些列的取值不多,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即须要在表中搜索的数据行的比例很大。增长索引,并不能明显加快检索速度。
第三,对于那些定义为text, image和bit数据类型的列不该该增长索引。这是由于,这些列的数据量要么至关大,要么取值不多。
第四,当修改性能远远大于检索性能时,不该该建立索引。这是由于,修改性能和检索性能是互相矛盾的。当增长索引时,会提升检索性能,可是会下降修改性能。当减小索引时,会提升修改性能,下降检索性能。所以,当修改性能远远大于检索性能时,不该该建立索引。
(1) DELETE语句执行删除的过程是每次从表中删除一行,而且同时将该行的删除操做做为事务记录在日志中保存以便进行进行回滚操做。TRUNCATE TABLE 则一次性地从表中删除全部的数据并不把单独的删除操做记录记入日志保存,删除行是不能恢复的。而且在删除的过程当中不会激活与表有关的删除触发器。执行速度快。
(2) 表和索引所占空间。当表被TRUNCATE 后,这个表和索引所占用的空间会恢复到初始大小,而DELETE操做不会减小表或索引所占用的空间。drop语句将表所占用的空间全释放掉。
(3) 通常而言,drop > truncate > delete
(4) 应用范围。TRUNCATE 只能对TABLE;DELETE能够是table和view
(5) TRUNCATE 和DELETE只删除数据,而DROP则删除整个表(结构和数据)。
(6) truncate与不带where的delete :只删除数据,而不删除表的结构(定义)truncate语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid(无效的)。
(7) delete语句为DML(data maintain Language),这个操做会被放到 rollback segment中,事务提交后才生效。若是有相应的 tigger,执行的时候将被触发。
(8) truncate、drop是DLL(data define language),操做当即生效,原数据不放到 rollback segment中,不能回滚。
(9) 在没有备份状况下,谨慎使用 drop 与 truncate。要删除部分数据行采用delete且注意结合where来约束影响范围。回滚段要足够大。要删除表用drop;若想保留表而将表中数据删除,若是于事务无关,用truncate便可实现。若是和事务有关,或须要触发trigger,仍是用delete。
(10) Truncate table 表名 速度快,并且效率高,由于:
truncate table 在功能上与不带 WHERE 子句的 DELETE 语句相同:两者均删除表中的所有行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 经过释放存储表数据所用的数据页来删除数据,而且只在事务日志中记录页的释放。
(11) TRUNCATE TABLE 删除表中的全部行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。若是想保留标识计数值,请改用 DELETE。若是要删除表定义及其数据,请使用 DROP TABLE 语句。
(12) 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。因为 TRUNCATE TABLE 不记录在日志中,因此它不能激活触发器。
一、Read uncommitted(读未提交)就是一个事务能够读取另外一个未提交事务的数据。
二、Read committed(读提交)就是一个事务要等另外一个事务提交后才能读取数据。
三、Repeatable read(重复读)就是在开始读取数据(事务开启)时,再也不容许修改操做。
四、Serializable(序列化)在该级别下,事务串行化顺序执行,能够避免脏读、不可重复读与幻读。是最高的事务隔离级别,可是这种事务隔离级别效率低下,比较耗数据库性能,通常不使用。
事务的做用就是保证数据的一致性、完整性。事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大,所以不少时候必须在并发性和性能之间作一个权衡。因此设立了几种事务隔离级别,以便让不一样的项目能够根据本身项目的并发状况选择合适的事务隔离级别,对于在事务隔离级别以外会产生的并发问题,在代码中作补偿。
MySQL支持单向、异步复制,复制过程当中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。
MySQL复制是基于主服务器在二进制日志中跟踪全部对数据库的更改。所以,要进行复制,必须在服务器上启用二进制日志。每一个从服务器从主服务器接收主服务器已经记录到日志的数据。
当一个从服务器链接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新;并在本机上执行相同的更新。而后封锁并等待主服务器通知新的更新。从服务器执行备份不会干扰主服务器,在备份过程当中主服务器能够继续处理更新。