Java 知识总结

bluetaoren

贡献于2013-03-27

字数:33091 关键词: Java开发 Java

1》 》》Synchronized 在Java应用中,使用多线程进行工作的需要是越来越多,使用多线程进行工作,大大的提高了系统的工作效率,然而因此而产生的问题也是层出不穷,而且因为多线程而产生的问题跟踪是一个难题。 同步的概念: 同步分为 方法同步 和 同步块 两种方式。 使用同步的原因 1. 在系统中对访类要使用多线程进行访问; 2. 在该类中有 类变量, 或者是 在类的方法中有访问 公共资源(如一个外部文件的读写)。 同步所锁定的内容是什么? 无论你将Synchronized加在方法前还是加在一个变量前,其锁定的都是一个 类对象。 每一个对象都只有一个锁与之相关联。 下例中分情况的列举各种情况下的同步效果 1. Synchronized 加在方法上, (方法同步) public synchronized void function1(){ ……} public void function2(){ synchronized (this){……} ……} 这两种写法的效果是一样的,锁定的都是类实例对象。 如果有一个 类实例对象: inst = new ClassSynInst(), 另外有两个线程: threada,threadb,都调用了inst 对象,那么,在同一时间,如果 threada调用了inst.function1(),则threadb在该时间内不能访问inst.function1() 和 inst.function2(); 因为threada把inst这个对象的锁使用了,所以无法分给其它线程使用 但是,如果threada调用 inst1.function1(), threadb调用 inst2.function1(), 则可以同时进行,因为它们调用的是不同的ClassSynInst类对象实例。 2. Synchronized 加在变量上, (同步块) Object a = new Object(); Object b = new Object(); public void function1(){ synchronized (a){……} ……} public void function2(){ synchronized (b){……} ……} 这种情况下,是实现代码块锁定,锁定的对象是 变量 a 或 b; (注意,a 、b 都是非static 的) 如果有一个 类实例对象: inst = new ClassSynInst(), 另外有两个线程: threada,threadb,都调用了inst 对象,那么,在同一时间,如果 threada调用了inst.function1(),则threadb在该时间内可以访问inst.function2(); 但不能访问 inst.function1() 的同步块, 因为a被 threada锁定了。 3. Synchronized 锁定的是 类变量 ,即static 变量 class Test{ static Object o= new Object(); public static synchronized void f1(){ ……} public static void f2(){ synchronized(Test.class){ ……} } public static void f3(){ try { synchronized (Class.forName("Test")) { ……} } catch (ClassNotFoundException ex) { } } public static void f4(){ synchronized(o){ ……} } } 以上4个方法中实现的效果都是一样的,其锁定的对象都是类Test,而不是类实例对象 ,即在多线程中,其共享的资源是属于类的,而不是属于类对象的。 在这种情况下,如果threada 访问了这4个方法中的任何一个, 在同一时间内其它的线程都不能访问 这4个方法。 4. 类的方法中访问了多线程共同的资源, 且该资源是可变的,这种情况下也是需要进行同步的 例: class test { static String path = “ file path”; public void readConfiFile(){ synchronized (path){ // 读取该path指定的文件。 } } public void writeConfiFile(){ synchronized (path){ // 写信息到该path指定的文件。 } } } 这种情况下,必须锁定为 类变量,而不能进行锁定类实例对象,因为这是变象的一种类资源共享,而不是类实例对象资源共享。 线程,成也其,败也其,用好了可以提升性能,用不好则会使系统后患无穷。 PS: 进行线程同步需要很大的系统开销, 所以,在使用时,如果不是必须的,则尽量不使用同步功能。 2》 》 详解java中的抽象类和接口的区别     在Java语言中, abstract class 和interface 是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进 行抽象类定义时对于abstract class和interface的选择显得比较随意。其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对 于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析,试图给开发者提供一个在二者之间进行选择的依据。   理解抽象类   abstract class和interface在Java语言中都是用来进行抽象类(本文 中的抽象类并非从abstract class翻译而来,它表示的是一个抽象体,而abstract class为Java语言中用于定义抽象类的一种方法, 请读者注意区分)定义的,那么什么是抽象类,使用抽象类能为我们带来什么好处呢?   在 面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。并不是 所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、 设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。比如:如果我们进行一个图形编辑软件的开发,就会发现问题领域存在着圆、 三角形这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念 在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。   在面向对象领域,抽象类主要用来进行类型隐藏。 我们可以构造出一个固定的一组行为的抽象描 述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个 抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知 道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。   从语法定义层面看abstract class 和 interface   在语法层面,Java语言对于abstract class和interface给出了不同的定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。   使用abstract class的方式定义Demo抽象类的方式如下: 1. abstract class Demo{    2. abstract void method1();    3. abstract void method2();    4. …    5. }          使用interface的方式定义Demo抽象类的方式如下: 1. interface Demo{    2. void method1();    3. void method2();    4. …    5. }          在abstract class方式中,Demo可以有自己的数据成员,也可以有非 abstract的成员方法,而在interface方式的实现中,Demo只能够有静态的不能被修改的数据成员(也就是必须是static final 的,不过在interface中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface是一种特殊形式的 abstract class。   从编程的角度来看,abstract class和interface都可以用来实现 "design by contract" 的思想。但是在具体的使用上面还是有一些区别的。   首先,abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系(因为Java不支持多继承 -- 转注)。但是,一个类却可以实现多个interface。也许,这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。   其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会增加一些复杂性,有时会造成很大的麻烦。   在 抽象类中不能定义默认行为还存在另一个比较严重的问题,那就是可能会造成维护上的麻烦。因 为如果后来想修改类的界面(一般通过 abstract class 或者interface来表示)以适应新的情况(比如,添加新的方法或者给已用的方法中添 加新的参数)时,就会非常的麻烦,可能要花费很多的时间(对于派生类很多的情况,尤为如此)。但是如果界面是通过abstract class来实现的,那 么可能就只需要修改定义在abstract class中的默认行为就可以了。   同样,如果不能在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了 "one rule,one place" 原则,造成代码重复,同样不利于以后的维护。因此,在abstract class和interface间进行选择时要非常的小心。   从设计理念层面看 abstract class 和 interface   上面主要从语法定义和编程的角度论述了abstract class和interface的区 别,这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面:abstract class和interface所反映出的设计理念,来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概念的本质所在。   前面已经提到过,abstract class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a"关系,即父类和派生类在概念本质上应该是相同的。对于interface来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的, 仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。   考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示:   使用abstract class方式定义Door: 1. abstract class Door{    2. abstract void open();    3. abstract void close();    4. }          使用interface方式定义Door: 1.    2. interface Door{    3. void open();    4. void close();    5. }           其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。   如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中, 主要是为了展示 abstract class 和interface 反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)?下面将罗列出可能的解 决方案,并从设计理念层面对这些不同的方案进行分析。   解决方案一:   简单的在Door的定义中增加一个alarm方法,如下: 1. abstract class Door{    2. abstract void open();    3. abstract void close();    4. abstract void alarm();    5. }       或者 1. interface Door{    2. void open();    3. void close();    4. void alarm();    }        那么具有报警功能的AlarmDoor的定义方式如下: 1. class AlarmDoor extends Door{    2. void open(){…}    3. void close(){…}    4. void alarm(){…}    5. }        或者 1. class AlarmDoor implements Door{    2. void open(){…}    3. void close(){…}    4. void alarm(){…}    5. }          这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方 法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反 之依然。   解决方案二:   既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定 义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用 abstract class 方式定义;两个概念都使用interface方式定义;一个概念 使用 abstract class 方式定义,另一个概念使用interface方式定义。   显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。   如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有 理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分 析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用 interface方式定义)反映不出上述含义。   如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报 警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系 在本质上是"is-a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说 明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示: 1.    2. abstract class Door{    3. abstract void open();    4. abstract void close();    5. }    6. interface Alarm{    7. void alarm();    8. }    9. class AlarmDoor extends Door implements Alarm{    10. void open(){…}    11. void close(){…}    12. void alarm(){…}    13. }           这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其 实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。   小结     1.abstract class 在 Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。     2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的成员方法都是abstract的。     3.abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。     4.实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。     5.接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值。     6.抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。     7.接口中的方法默认都是 public,abstract 类型的。   结论   abstract class 和 interface 是 Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系(虽然都能够实现需求的功能)。这其实也是语言的一种的惯用法 3》 》 一、操作符优化 1、IN 操作符 用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格。但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别: ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。 推荐方案:在业务密集的SQL当中尽量不采用IN操作符,用EXISTS 方案代替。 2、NOT IN操作符 此操作是强列不推荐使用的,因为它不能应用表的索引。 推荐方案:用NOT EXISTS 方案代替 3、IS NULL 或IS NOT NULL操作(判断字段是否为空) 判断字段是否为空一般是不会应用索引的,因为索引是不索引空值的。 推荐方案:用其它相同功能的操作运算代替,如:a is not null 改为 a>0 或a>’’等。不允许字段为空,而用一个缺省值代替空值,如申请中状态字段不允许为空,缺省为申请。 4、> 及 < 操作符(大于或小于操作符) 大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段A,30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。 5、LIKE操作符 LIKE操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。 一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。 6、UNION操作符 UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如: select * from gc_dfys union select * from ls_jg_dfys 这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。 推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。 select * from gc_dfys union all select * from ls_jg_dfys 二、SQL书写的影响 1、同一功能同一性能不同写法SQL的影响。 如一个SQL在A程序员写的为  Select * from zl_yhjbqk B程序员写的为 Select * from dlyx.zl_yhjbqk(带表所有者的前缀) C程序员写的为 Select * from DLYX.ZLYHJBQK(大写表名) D程序员写的为 Select *  from DLYX.ZLYHJBQK(中间多了空格) 以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同,则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。 2、WHERE后面的条件顺序影响 WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响。如: Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1 Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下' 以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = '1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。 3、查询表顺序的影响 在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下,ORACLE会按表出现的顺序进行链接,由此可见表的顺序不对时会产生十分耗服物器资源的数据交叉。(注:如果对表进行了统计分析,ORACLE会自动先进小表的链接,再进行大表的链接) 三、SQL语句索引的利用 1、操作符优化(同上) 2、对条件字段的一些优化 采用函数处理的字段不能利用索引,如: substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’ trunc(sk_rq)=trunc(sysdate), 优化处理:sk_rq>=trunc(sysdate) and sk_rq50,优化处理:ss_df>30 ‘X’ || hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’ sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5 hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。 条件内包括了多个本表的字段运算时不能进行索引,如:ys_df>cx_df,无法进行优化 qc_bh || kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’ 四、其他 ORACLE的提示功能是比较强的功能,也是比较复杂的应用,并且提示只是给ORACLE执行的一个建议,有时如果出于成本方面的考虑ORACLE也可能不会按提示进行。根据实践应用,一般不建议开发人员应用ORACLE提示,因为各个数据库及服务器性能情况不一样,很可能一个地方性能提升了,但另一个地方却下降了,ORACLE在SQL执行分析方面已经比较成熟,如果分析执行的路径不对首先应在数据库结构(主要是索引)、服务器当前性能(共享内存、磁盘文件碎片)、数据库对象(表、索引)统计信息是否正确这几方面分析。 4》 》 Having 在select 语句中可以使用group by 子句将行划分成较小的组,然后,使用聚组函数返回每一个组的汇总信息,另外,可以使用having子句限制返回的结果集。group by 子句可以将查询结果分组,并返回行的汇总信息Oracle 按照group by 子句中指定的表达式的值分组查询结果。    在带有group by 子句的查询语句中,在select 列表中指定的列要么是group by 子句中指定的列,要么包含聚组函数    select max(sal),job emp group by job;    (注意max(sal), job 的job并非一定要出现,但有意义 )    查询语句的select 和group by ,having 子句是聚组函数唯一出现的地方,在where 子句中不能使用聚组函数。   select deptno,sum(sal) from emp where sal>1200 group by deptno having sum(sal)>8500 order by deptno;   当在gropu by 子句中使用having 子句时,查询结果中只返回满足having条件的组。在一个sql语句中可以有where子句和having子句。having 与where 子句类似,均用于设置限定条件     where 子句的作用是在对查询结果进行分组前,将不符合where条件的行去掉,即在分组之前过滤数据,条件中不能包含聚组函数,使用where条件显示特定的行。   having 子句的作用是筛选满足条件的组,即在分组之后过滤数据,条件中经常包含聚组函数,使用having 条件显示特定的组,也可以使用多个分组标准进行分组。   查询每个部门的每种职位的雇员数   select deptno,job,count(*) from emp group by deptno,job;     如果你对何时应该使用WHERE,何时使用HAVING仍旧很迷惑,请遵照下面的说明:     WHERE语句在GROUP BY语句之前;SQL会在分组之前计算WHERE语句。     HAVING语句在GROUP BY语句之后;SQL会在分组之后计算HAVING语句。 SQL HAVING 语法 SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name HAVING aggregate_function(column_name) operator value 5》 》 数组排序 6》 Equals和==的区别 == 操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。 equals操作表示的两个变量是否是对同一个对象的引用,即堆中的内容是否相同。 ==比较的是2个对象的地址,而equals比较的是2个对象的内容。显然,当equals为true时,==不一定为true 7》 String.parseLong()与String.valueof(Long); 拆箱 装箱 8》 String、StringBuffer、StringBuilder 9》 String str1 = “monday”; String str2 = “monday”; String str3 = new String(“monday”); Str1==str2;str1==str3;str2==str3; Str1.equals(str2);Str1.equals(str2);str2.equals(str3); 10》 创建一个枚举:public enum  EnumOne {One,TWO,Three} 11》 String str = “”; For(int i=0; i>重定向? 验证码 19》》 怎样获取某类的所有异常信息 使用spring 的AOP切面编程原理写一个异常类,让其他的继承这个异常类。 20 》》 Spring 大数据量的查询 缓存的懒加载 集合类型的延迟加载:在set中指定lazy=true 21.》》 Java的所有修饰public、protected、private、abstract、static和final 5.abstract 使用对象:类、接口、方法 介绍:类中包括没有实现的方法,不能被实例化。如果是一个abstract方法,则方法体 为空,该方 法的实现在子类中被定义,并且包含一个abstract方法的类必须是一个abstract类 6.protected 使用对象:成员 介绍:成员只能在定义它的包中被访问,如果在其他包中被访问,则实现这个方法的类 必须是该成 员所属类的子类。 7.native 使用对象:成员 介绍:与操作平台相关,定义时并不定义其方法,方法的实现被一个外部的库实现。 8.strictfp 使用对象:类、方法 介绍:strictfp修饰的类中所有的方法都隐藏了strictfp修饰词,方法执行的所有浮点 计算遵守 IEEE 754标准,所有取值包括中间的结果都必须表示为float或double类型,而不能利用 由本地平台浮 点格式或硬件提供的额外精度或表示范围。 9.synchronized 使用对象:方法 介绍:对于一个静态的方法,在执行之前jvm把它所在的类锁定;对于一个非静态类的方 法,执行 前把某个特定对象实例锁定。 10.volatile 使用对象:字段 介绍:因为异步线程可以访问字段,所以有些优化操作是一定不能作用在字段上的。 volatile修饰变量。在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 看看Java Language Specification中的例子。 条件:一个线程不停的调用方法one(),一个线程不停的调用方法two()。我测试过多次,这种情况好像一直没有出现。 22》》 Struts1和hibernate工作原理和优缺点? 23》》 使用spring+hibernate比只使用hibernate节约了多少的代码量 50%到70% 24》》 Group by GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组。 SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP BY column_name GROUP BY是SELECT语句的从句,用来指定查询分组条件,主要用来对查询的结果进行分组,相同组合的分组条件在结果集中只显示一行记录。使用GROUP BY从句时候,通过添加聚合函数(主要有COUNT()、SUM、MAX()、MIN()等)可以使数据聚合。 GROUP BY插叙列中使用聚合函数是针对每个分组的。 GROUP BY用来指定分组条件,是一个数学集合的概念,比如针对一列进行分组,则组合分组条件的集合数为1。如果有两个分组条件,则组合分组条件的集合数为2。因此带有GROUP BY的查询一般成为分组查询,也叫组合查询。组合记录的多少取决于组合集合(不包含重复元素)中元素的个数。例如,组合条件为一列,则查询结果集记录条数应该等于这个列所有字段所组成(数学意义上的)集合的元素个数(NULL字段也算一个)。如果有两个组合列,则记录数等于实际中存在的两个列所组合的数目 GROUP BY组合列必须出现查询的SELECT关键字后面,相同组合条件的情况下仅仅保留一个。因此,通过SELECT...GROUP BY查询出的各个列都应该是数目相同,要达到相同的目的,有两种途径:一种是将要查询的字段方到组合条件中,一种是在非组合条件的字段上使用聚合函数,当然也可以在组合列上聚合函数。处子之外,别无它法!如果查询的各个列结果数目不相等,则结果集会出现“不能对齐”的错误。 因此,将非组合条件的列在不使用聚合函数条件下放到要查询的列中,这种做法是完全错误的。 GROUP BY在做组合查询的时候,会对NULL的分组单独形成一行,进行统计。参看上面的SQL。   GROUP BY对组合条件列来说,本身就会自动分组(剔除重复的列),因此在组合条件的列上应用DISTINCT关键字是多于的。但是用在非组合条件(都有聚合函数)的列上使用DISTINCT却不是多余的。 GROUP BY不但可以对列组合,还可以对列的表达式进行组合。 可以在SELECT ... GROUP BY 分组后筛选数据。筛选的关键字是HAVING。HAVING的作用和WHERE类似。都是用来过滤查询的中间记录。但是,HAVING从句指定的每个列规范必须出现在一个聚合函数内,或者出现在GROUP BY从句命名的列中。与WHERE不同的是:WHERE是在分组前(查询后)筛选数据;HAVING是在分组后筛选数据。 25》》 Hibernate 中的get 和load的区别 1)如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException。 (2)load()方法可返回实体的代理类实例;而get方法永远直接返回实体类。 (3)load方法可以充分利用内部缓存(session缓存)和二级缓存中的现有数据,它将会使用代理来延迟加载该对象,然后当用到对象中的其他属性数 据时才查询数据库,查找方式:load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。所以当id对应的记录在据库中存 在就可以使用load方法来实现延迟加载;而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读 取。 结论:load方法采用Hibernate代理方式实现延迟加载(存在lazy),记录不存在返回ObjectNotFoundException;get 方法更接近于对数据库的直接访问,当然先看要查找的对象或代理是否跟session绑定,找不到就返回null;即load先访问一级缓存(Session),如果没有再访问二级缓存(SessionFactory),还没有直接查询数据库;get 先访问一级缓存,如果没有,就直接访问数据库了! 26》》 触发器、存储过程 27》》 Spring的事物 Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。     DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。     具体如下图:     根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:     第一种方式:每个Bean都有一个代理                                                                                                                                                                                                               PROPAGATION_REQUIRED                                      第二种方式:所有Bean共享一个代理基类                                                                                                                                                     PROPAGATION_REQUIRED                                                                                               第三种方式:使用拦截器                                                                                                                                          PROPAGATION_REQUIRED                                                                                          *Dao                                                                     transactionInterceptor                                                               第四种方式:使用tx标签配置的拦截器                                                                                                                                                               第五种方式:全注解                                                                                 此时在DAO上需加上@Transactional注解,如下: package com.bluesky.spring.dao; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; import com.bluesky.spring.domain.User; @Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao {     public List listUsers() {         return this.getSession().createQuery("from User").list();     }           } 28》》 Struts 的validate Validator主要依赖两个jar包 Jakarta-oro.jar:-提供一组处理文本的类,具有文本替换、过滤、和分割功能。 Commons-validator.jar:提供了一个简单、可扩展的验证框架,包含了通用的验证方法和验证规则。 在用Struts中用这个框架,需加入这两个包,用起来感觉还不错。 下面介绍一下。 Validator采用基于两个xml文件的方式来配置验证规则,分别为validation.xml,validator-rules.xml。在struts应用中,需放到web-inf目录下。 1.validator-rules.xml 这个文件包含了一组验证规则,对所有struts应用都适用。一般情况不用修改这个文件,除非要修改或扩展默认规则。如果要给应放到另一个xml文件中,而不是直接添加到validator-rules.xml文件中,这样当Validator升级时,不用修改validator-rules.xml文件。 2.validator.xml文件 这个文件是针对于具体struts应用的,他可以为应用中的ActionForm配置验证规则。而不用编码实现验证。 例如一个验证登陆form的例子,要求用户名必须填写,秘密要求必填、最大、最小长度及其内容的要求。 代码 pwd ^w+$
mask ^w+$ minlength 6 maxlength 20
validator.xml和validator-rules.xml文件的语法不在这里说明。 3.Validator插件 为了在struts中用validator,可以用插件方式加载Validator框架,需在struts配置文件中配置ValidatorPlugIn插件,同时写明两个xml文件的路径。 应用启动时,Strust会加载这个插件,并调用他的init()方法,init()方法根据pathnames加载 相应的validator-xml,validator.xml文件,把验证信息读入到内存中。 代码 4.Validator和ActionForm Validator框架不能直接org.apache.struts.action.ActionForm。应采用ActionForm的两个子类 ValidatorActionForm和ValidatorForm DynaValidatorActionFrom继续自DynaValidatorForm 支持动态在ActionFrom中使用Validator框架 ValidatorFormActionForm继承ValidatorForm 支持在标准ActionForm中使用Validator框架。 DynaValidtaorForom和ValidatorForm类都实现了validator()方法,如果验证失败,就会返回包含错误消息的ActionMessage对象,并把该对象添加到ActionErrors集合对象中,由validator方法最后返回ActionErrors对象。 ValidatorForm有一个子类ValidatorActionForm ValidatorForm Strust配置文件的元素的name属------>validator.xml文件 窗体顶端 元素的name属性 ValidatorActionForm Strust配置文件的元素的path属------>validator.xml文件 窗体顶端 元素的name属性 Validator框架提供这两个类目的在于可以更加精确的控制执行验证的条件。 例如 loginForm表单对应两个验证规则A、B。对应login、cancel两个动作。 如果对于这两个动作都要执行规则A、B。则可以扩展一个ValidatorFom类loginForm然后配置 代码
验证规则A 验证规则B .
….
如果对于用户的login动作,执行规则A。对于cancel动作执行B规则。则可以扩展一个ValidatorActionFom类 loginForm 然后配置 代码
验证规则A .
验证规则B .
而DynaValidatorForm和DynaValidatorActionForm,区别和ValidatorFrom和ValidatorActionForm的区别一样 5.在validator框架中使用JavaScript 默认情况下,validator框架在服务器端执行表单验证。也可以在客户端验证。 如果在客户端验证,需要使用struts的标签,他能在jsp页面中生成用于客户端验证 的JavaScript的脚本。 1) 在validator-rules.xml文件的元素中配置元素默认struts框架的validator-rules.xml文件已经包含了javascript子元素。也可以将这些javascript集中放到一个validator.js的文件中,也页面中引入代码 < script language="Javascript1.1" src="js/validator.js">< /script> 2) 在Jsp页面中包含< html:javascript> < html:javascript formName=”loginForm”> 这个标签的form的name属性制定需要验证的表单名字, 他能够访问为表单配置的验证规则的javascript元素,把包含的脚本写到jsp页面中,生成validateLoginForm的函数,负责执行验证逻辑 3) 对需要验证的表单定义onsubmit事件 4)代码 < html:form action="manageContract.do" onsubmit="return validateContractForm(this);"> 5) 用户在表单上提交后,就会调用 标签生成的js脚本validateLoginForm函数,执行验证。如果验证失败,就会弹出对话框提示, 不会提交的服务器端。 小结 使用步骤 1) 创建扩展ValidatorForm或ValidatorActionForm类的ActionForm,如果使用动态ActionForm, 则不用扩展DynaValidatorForm或DynaValidatorActionForm类的子类,可以直接进行第二步 2) 在struts文件中配置form和action元素 3) 把validator框架使用的文本信息添加到应用的Resource Bundle中 4) 代码 < message-resources parameter="com.web.ApplicationResources" /> 5) 在validatorxml文件中为表单配置验证规则 6) 在struts配置文件中配置validatorPlugIn插件 代码 29》》 struts 的dispatchAction 思考题: 有100匹马和100块石头,1匹大型马一次运2块石头,1匹中型马一次运1块石头,2匹小型马一次运1块石头。问有多少匹大型马、中型马、小型马?有多少种组合方式 一个人要去谎言国。现在有两个人,一个来自诚实国,一个来自谎言国。来自诚实国的的人总说实话,谎言国的人总说谎话。有两条路,一条去诚实国,一条去谎言国。请问要怎么问才可以找到正确的路 30》》 HashMap 和 HashTable 的区别 就HashMap与HashTable主要从三方面来说。 一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现 二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的 三.值:只有HashMap可以让你将空值作为一个表的条目的key或value 引: ArrayList和Vector的区别 一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的 二.数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半 31》》 Linux权限数字代表解释 从左至右,第一位数字代表文件所有者的权限,第二位数字代表同组用户的权限,第三位数字代表其他用户的权限。 以755为例: 第一位7等于4+2+1,rwx,所有者具有读取、写入、执行权限; 第二位5等于4+1+0,r-x,同组用户具有读取、执行权限但没有写入权限; 第三位5,同上,也是r-x,其他用户具有读取、执行权限但没有写入权限。 下面列出常用的linux文件权限: 444 r--r--r-- 600 rw------- 644 rw-r--r-- 666 rw-rw-rw- 700 rwx------ 744 rwxr--r-- 755 rwxr-xr-x 777 rwxrwxrwx 32》》 tar 命令 格式: tar [选项] [文件目录列表] 功能: 对文件目录进行打包备份 选项: -c 建立新的归档文件 -r 向归档文件末尾追加文件 -x 从归档文件中解出文件 -O 将文件解开到标准输出 -v 处理过程中输出相关信息 -f 对普通文件操作 -z 调用gzip来压缩归档文件,与-x联用时调用gzip完成解压缩 -Z 调用compress来压缩归档文件,与-x联用时调用compress完成解压缩 1.用tar打包一个目录下的文件:#tar -cvf /mnt/lgx/a1.doc 生成一个以.tar为扩展名的打包文件 2.用tar解开打包文件:#tar -xvf /mnt/lgx/a1.doc.tar 通常情况下,tar打包与gzip(压缩)经常联合使用。方法: 首先用tar打包,如:#tar -cvf /mnt/lgx/a1.doc (产生a1.doc.tar文件) 然后用gzip压缩a1.doc.tar文件,如:#gzip /mnt/lgx/a1.doc.tar (产生a1.doc.tar.gz文件) 3.解压a1.doc.tar.gz文件 方法1: #gzip -dc /mnt/lgx/a1.doc.tar.gz (产生a1.doc.tar文件) #tar -xvf /mnt/lgx/a1.doc.tar (产生a1.doc文件) 这两次命令也可使用管道功能,把两个命令合二为一: #gzip -dc /mnt/lgx/a1.doc.tar.gz | tar -xvf 方法2:使用tar提供的自动调用gzip解压缩功能 #tar -xzvf /mnt/lgx/a1.doc.tar.gz 经过tar打包后,也可用compress命令压缩(注:gzip比compress压缩更加有效),产生一个以.tar.Z的文件,在解包时,可先用 “uncompress 文件名”格式解压,然后用“tar -xvf 文件名”解包。也可直接调用“tar -Zxvf 文件名”解包。 33》》 public class Test1 { { System.out.println("4"); } static { System.out.println("5"); } { System.out.println("6"); } } public class Test2 extends Test1 { { System.out.println("2"); } } public class Test3 { { System.out.println("0"); } static { System.out.println("1"); } { System.out.println("3"); } public static void main(String[] args) { new Test2(); } } 输出:1 5 4 6 2 public class FatherClass { { System.out.println("FatcherClass Create"); } } public class ChildClass extends FatherClass { { System.out.println("ChildClass Create"); } public static void main(String[] args) { FatherClass fatherClass = new FatherClass(); ChildClass childClass = new ChildClass(); } } 输出: FatcherClass Create FatcherClass Create ChildClass Create 34》》 column1 column 2 column3 column4 a b c d a b c d 1 2 3 4 1 2 3 4 用一条SQl语句删除重复的记录 35》》 delete from table_name truncate TABLE name 速度快,而且效率高 TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。 DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。 TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。 TRUNCATE TABLE 不能用于参与了索引视图的表。 对用TRUNCATE TABLE删除数据的表上增加数据时,要使用UPDATE STATISTICS来维护索引信息。 如果有ROLLBACK语句,DELETE操作将被撤销,但TRUNCATE不会撤销。 truncate命令是会把自增的字段还原为从1开始的。 36》》 OOP的方法论(高内聚、低耦合、易复用、易扩展、易维护): 1. 代码重用(通常用继承和聚合实现) 2. 低耦合(模块与模块之间,类与类之间依赖程度低) 3. 高内聚(模块或类内部依赖程度高,负责处理相关的工作,避免“狗拿耗子”) 4. 易扩充(在不改变原有代码,或改变很小的情况下增加功能) 5. 易维护(代码结构清晰,容易管理和修改代码) OOP: Object Oriented Programming,面向对象的程序设计。所谓“对象”就是一个或一组数据以及处理这些数据的方法和过程的集合。 OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。OOP 主要有以下的概念和组件: 组件 - 数据和功能一起在运行着的计算机程序中形成的单元,组件在 OOP 计算机程序中是模块和结构化的基础。   抽象性 - 程序有能力忽略正在处理中信息的某些方面,即对信息主要方面关注的能力。   封装 - 也叫做信息封装:确保组件不会以不可预期的方式改变其它组件的内部状态;只有在那些提供了内部状态改变方法的组件中,才可以访问其内部状态。每类组件都提供了一个与其它组件联系的接口,并规定了其它组件进行调用的方法。   多态性 - 组件的引用和类集会涉及到其它许多不同类型的组件,而且引用组件所产生的结果得依据实际调用的类型。   继承性 - 允许在现存的组件基础上创建子类组件,这统一并增强了多态性和封装性。典型地来说就是用类来对组件进行分组,而且还可以定义新类为现存的类的扩展,这样就可以将类组织成树形或网状结构,这体现了动作的通用性。 国际上一般认为OPP一共有七大原则,分别是以下七大原则: 一 OCP(Open-Closed Principle) 开放-封闭原则 软件实体应该扩展开放、修改封闭: 1. “对于扩展是开放的” (Open for extension)。这意味着模块的行为是可以扩展的,当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为,换句话说,我们可以改变模块的功能。 2. “对于更改是封闭的” (Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码,模块的二进制可执行版本,无论是可链接的库、DLL或JAVA 的.jar文件,都无需改动。 实现:合理划分构件,一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里;一种可变性不应当和另一个可变性混合在一起。 二 DIP(Dependency-Inversion Principles)依赖倒置原则 第1点:高层模块不依赖底层模块,两者都依赖抽象。 第2点:抽象不应该依赖于细节,细节应该依赖于抽象。 每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现这些抽象接口,每个高层类都通过该抽象接口使用下一层的服务,接口属于高层,低层要实现高层的接口,因此现在是低层依赖于高层是依赖关系倒置和接口所有权的倒置。 实现:应该通过抽象耦合的方式,使具体类最大可能的仅和其抽象类(接口)发生耦合;程式在需要引用一个对象时,应当尽可能的使用抽象类型作为变量的静态类型,这就是针对接口编程的含义。 三 LSP(Liskov Substitution Principle) 替换原则 继承思想的基础。“只有当衍生类能够替换掉基类,软件单位的功能不会受到影响时,基类才真正被复用,而衍生类也才能够在基类的基础上增加新的行为。 四 ISP(Interface Insolation Principle)接口隔离原则 接口功能单一,避免接口污染。 一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染----角色隔离原则。 将接口理解成为侠义的java接口,这样一来,接口隔离原则讲的就是为同一个角色提供宽、窄不同的接口,以对付不同的客户端。这种办法在服务行业中叫做定制服务---定制服务。 实现:一个类对另外一个类的依赖性应当是建立在最小的接口上的。使用多个专门的接口比使用单一的总接口要好。 (动机:当我们设计应用程序的时候,如果一个模块包含多个子模块,那么我们应该小心对该模块做出抽象。设想该模块由一个类实现,我们可以把系统抽象成一个接口。但 是当我们想要添加一个新的模块扩展程序时,如果要添加的模块只包含原系统中的一些子模块,那么就会强迫我们实现接口中的所有方法,并且还要编写一些哑方 法。这样的接口被称为胖接口或者叫被污染的接口,使用这样的接口将会给系统引入一些不正确的行为。) 五 SRP(Single Resposibility Principle) 单一职责原则 就一个类而言,应该仅有一个引起他变化的原因。 假如一个类的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会抑止这个类完成其他职责的能力。 单一职责原则的好处: 第一、有助于我们分析和编码的思路的清晰。(当你的代码里有了三层或以上的if语句或for语句的嵌套的时候,你不要跟我说,你已经把问题分析得很清楚了。多层嵌套的if或for语句只能说明你还没有把问题分析清楚。) 第二、使我们的编码、测试和维护变得简单。 第三、将一个个复杂的问题简单化以后,易于代码的重用。(当你的代码的多个功能搅和在一起的时候,你是没办法考虑代码的重用的,因为你的每一处代码都有不同。) 第四、易于系统的扩展 六 LoD (Law of Demeter)迪米特法则 即最少知识原则。一个对象应当对其他对象有尽可能少的了解。只和最直接的类交互,对第三方可以通过转达交互,从而减少对象间的耦合性。 七 CARP(Composite/Aggregate Reuse Principle)合成/聚合复用原则 就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。简而言之就是:要尽量使用合成/聚合,尽量不要使用继承。 聚合:用来表示“拥有”关系或者整体和部分的关系。 合成:用来表示一种强得多的“拥有”关系。在一个合成里,部分和整体的生命周期是一样的。一个合成的新对象完全拥有对其组成部分的支配权,包括它们的创建和泯灭等。 当然想要很深刻的理解OOP原则光是看点简介是远远不够的,在这里我就拿其中的一个原则 LSP(Liskov Substitution Principle) 替换原则通过举例稍微深入分析一下。 对于依赖倒置原则,说的是父类不能依赖子类,它们都要依赖抽象类。这种依赖是我们实现代码扩展和运行期内绑定(多态)的基础。因为一旦类的使用者依赖某个具体的类,那么对该依赖的扩展就无从谈起;而依赖某个抽象类,则只要实现了该抽象类的子类,都可以被类的使用者使用,从而实现了系统的扩展。

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 6 金币 [ 分享文档获得金币 ]
0 人已下载

下载文档

相关文档