一曲新词酒一杯,去年天气旧亭台,夕阳西下几时回? 无可奈何花落去,似曾相识燕归来,小园香径独徘徊。

发布新日志

  • 2007-01-15 | java的异常处理(1)【转】

    caicai1724 发布于 2007-04-27 22:00:56

    浅析Java语言中两种异常的差别

    Java提供了两类主要的异常:runtime exception和checked exception。所有的checked exception是从java.lang.Exception类衍生出来的,而runtime exception则是从java.lang.RuntimeException或java.lang.Error类衍生出来的。

      它们的不同之处表现在两方面:机制上和逻辑上。

      一、机制上

      它们在机制上的不同表现在两点:1.如何定义方法;2. 如何处理抛出的异常。请看下面CheckedException的定义:

      public class CheckedException extends Exception
      {
      public CheckedException() {}
      public CheckedException( String message )
      {
      super( message );
      }
      }

      以及一个使用exception的例子:

      public class ExceptionalClass
      {
      public void method1()
      throws CheckedException
      {
       // ... throw new CheckedException("...出错了");
      }
      public void method2( String arg )
      {
       if( arg == null )
       {
        throw new NullPointerException("method2的参数arg是null!");
       }
      }
      public void method3() throws CheckedException
      {
       method1();
      }
      }

      你可能已经注意到了,两个方法method1()和method2()都会抛出exception,可是只有method1()做了声明。另外,method3()本身并不会抛出exception,可是它却声明会抛出CheckedException。在向你解释之前,让我们先来看看这个类的main()方法:

      public static void main( String[] args )
      {
      ExceptionalClass example = new ExceptionalClass();
      try
      {
      example.method1();
      example.method3();
      }
      catch( CheckedException ex ) { } example.method2( null );
      }

      在main()方法中,如果要调用method1(),你必须把这个调用放在try/catch程序块当中,因为它会抛出Checked exception。

    相比之下,当你调用method2()时,则不需要把它放在try/catch程序块当中,因为它会抛出的exception不是checked exception,而是runtime exception。会抛出runtime exception的方法在定义时不必声明它会抛出exception。

      现在,让我们再来看看method3()。它调用了method1()却没有把这个调用放在try/catch程序块当中。它是通过声明它会抛出method1()会抛出的exception来避免这样做的。它没有捕获这个exception,而是把它传递下去。实际上main()方法也可以这样做,通过声明它会抛出Checked exception来避免使用try/catch程序块(当然我们反对这种做法)。

      小结一下:

      * Runtime exceptions:

      在定义方法时不需要声明会抛出runtime exception;

      在调用这个方法时不需要捕获这个runtime exception;

      runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。

      * Checked exceptions:

      定义方法时必须声明所有可能会抛出的checked exception;

      在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;

      checked exception是从java.lang.Exception类衍生出来的。

      二、逻辑上

      从逻辑的角度来说,checked exceptions和runtime exception是有不同的使用目的的。checked exception用来指示一种调用方能够直接处理的异常情况。而runtime exception则用来指示一种调用方本身无法处理或恢复的程序错误。

      checked exception迫使你捕获它并处理这种异常情况。以java.net.URL类的构建器(constructor)为例,它的每一个构建器都会抛出MalformedURLException。MalformedURLException就是一种checked exception。设想一下,你有一个简单的程序,用来提示用户输入一个URL,然后通过这个URL去下载一个网页。如果用户输入的URL有错误,构建器就会抛出一个exception。既然这个exception是checked exception,你的程序就可以捕获它并正确处理:比如说提示用户重新输入。

      再看下面这个例子:

      public void method()
      {
      int [] numbers = { 1, 2, 3 };
      int sum = numbers[0] numbers[3];
      }

      在运行方法method()时会遇到ArrayIndexOutOfBoundsException(因为数组numbers的成员是从0到2)。对于这个异常,调用方无法处理/纠正。这个方法method()和上面的method2()一样,都是runtime exception的情形。上面我已经提到,runtime exception用来指示一种调用方本身无法处理/恢复的程序错误。而程序错误通常是无法在运行过程中处理的,必须改正程序代码。

      总而言之,在程序的运行过程中一个checked exception被抛出的时候,只有能够适当处理这个异常的调用方才应该用try/catch来捕获它。而对于runtime exception,则不应当在程序中捕获它。如果你要捕获它的话,你就会冒这样一个风险:程序代码的错误(bug)被掩盖在运行当中无法被察觉。因为在程序测试过程中,系统打印出来的调用堆栈路径(StackTrace)往往使你更快找到并修改代码中的错误。有些程序员建议捕获runtime exception并纪录在log中,我反对这样做。这样做的坏处是你必须通过浏览log来找出问题,而用来测试程序的测试系统(比如Unit Test)却无法直接捕获问题并报告出来。

      在程序中捕获runtime exception还会带来更多的问题:要捕获哪些runtime exception?什么时候捕获?runtime exception是不需要声明的,你怎样知道有没有runtime exception要捕获?你想看到在程序中每一次调用方法时,都使用try/catch程序块吗?

  • 2007-01-13 | java的内部类研究(2)【转】

    caicai1724 发布于 2007-04-27 21:59:28

    Java内部类

    简单的内部类定义形如这样:

    class A{

    class B{}

    }这样的类被称为内部类,又 被称为内隐类.

    从简单到深入一步一步的分析内部类的特点.

     class OuterClass

    {   static class A //静态内部类

        {   public A( ) 

            { System.out.println("Test$A !"); }    }

        class B  //非静态内部类

        {    public B( )   

             { System.out.println("Test$B !"); }    }

       public void disp( )

        {      

            final int a=10;    int b;

             class C  //成员函数中的局部内部类

             {  public C( )

                {     System.out.println(“in class C a="+a);

                      //System.out.println(b);      

                  }

             }

             C c=new C( );

    }  

    public class Test extends OuterClass

    {

       public static void main(String args[])

      {     OuterClass.A a=new OuterClass.A( );

        //建立静态内部类对象

            B b=new OuterClass( ).new B( );

        //建立非静态内部类的对象

        //注意这个OuterClass().new B();相当于生成一个外部类的对象,然后在利用外部类对象生成内部类对象

            OuterClass t=new OuterClass( );

             t.disp( );

        //通过外部对象调用一个对象方法的形式,新建立了对象C.

      }

    }

    注意在上面的b在运行时会为0,因为是类属性.

    class OuterClass

           { 

              static class A  {   } //静态内部类

                   class B  {   } //非静态内部类

                     public void disp( )

                     {

                        class C{ } //局部内部类

                     }

            }

    编译后的结果:

    OuterClass.class

            OuterClass$A.class

            OutClass$B.class

           OuterClass$1$C.class

    记住以下几句话:

    1,一个内部类的对象能够访问创建它的外部类对象的所有属性及方法(包括私有部分)。

    //可以闭上眼镜,把这个内部类等同于一个类的一个方法,当然就可以访问这个外部类的

    //所有方法和属性,私有方法和属性是属于外部类的,当然也就等同于内部类的.

    2,对于同一个包中的其它类来说,内部类能够隐藏起来。(将内部类用private修饰即可)

    //只有在内部类中,才能定义一个为private类型的class,因为这时编译器已经把这个类看作这个类的成员了,但是在一般使用时,就是所谓的”顶级类时”,不能使用private,只能是public 或者是friendly.

    如果要是想保证一个类不产生任何的对象,请在构造函数中,把构造函数声明成private.

    3, 内部类可定义在方法中,称为局部内部类,但它只能使用方法中的final常量。

    //定义在一个方法内的类,又被成为局部内部类,这个类只能使用在方法中的final常量,注意,这个常量是在一个方法中的,那么能否使用一个类中的常量呢?

    当然是可以的,因为类中的常量在在一个方法中是可见的.

    //

    如果把一个类写在了一个if中,比如这样:

    class A{

        int a = 10;

        if(a!=10){

            class B{

                B(){

                    System.out.println(a);

                }

            }

        }

    }

    在编译后会有几个错误呢?

    首先那个a没有被定义为final,你有一次上了圈套.

    类B同样会被生成出来,只是离开了if域就失效了.

    4,内部类可以被定义为抽象类

    // abstract 类同样可以在内部类中

    5, 非静态内部类不能声明本类的static成员

    //只有一个静态的内部类,才可以声明一个static成员,

    class A{

        static  class B{

     

           //如果这里不是一个static类,是不可以被声明这个gg方法的.

           static void gg(){

           int a = 100;

           System.out.println(a);

           }

        }

    }

    class aa{

        public static void main(String args[]){  

        A.B hh = new A.B();

        hh.gg();      

        }

    }

    使用内部类可以非常方便的编写事件驱动程序

    这个在写事件驱动时,会有很好的解释.

    匿名内部类

    在某些情况下,我们只需要内部类的一个对象,那么我们就没有必要给内部类命名,没有名字的内部类我们称为匿名内部类

    public class A extends Applet

    {     public void init( )

          {  addMouseListener( new B( ) );

          }

          class B extends MouseAdapter

          {      public void mousePressed(MouseEvent me)

                 {       showStatus("Mouse Pressed.");         }

           }

    }

    用匿名内隐类修改:

    import java.applet.*;

    import java.awt.event.*;

    import java.awt.*;

    public class AnonymousInnerClassDemo extends Applet

    {   public void init( )

         {   addMouseListener( new MouseAdapter( )

             {   public void mousePressed(MouseEvent me)

           {  showStatus("Mouse Pressed");   }     }    );

        }

    }

    下面是一个think in java里的例子

    public class Pr{

        public Concents cont(){

            return new Concents() 

                private int i= 11;

                public int value (){return i;}

            };//这里是有一个逗号

        }

    }

    这个Contents是通过默认的构造函数产生的,匿名类的产生也就是一个新类向上转型到父类的过程.

    那么如果一个父类没有一个默认的构造函数,应该什么办呢?

    那就只有调用一个super()了.

    class noname{

             Warpping wrap(int x){

            return new Warpping(x){

                public int value(){

                    return super.value()*47;

                }

            };

        }

        public static void main(String s[]){ 

            noname p = new noname();

            Warpping w = p.wrap(10);

        }

    }

    如果在匿名内部类中用到了外部对象

    就必须保证这个外部对象是final的

    public class PP{

        public DD dest(final String dest, final float price){

            return new DD(){

                private int cost;

                {

                    cost = Math.round(price);

                    if(cost>100)

                        System.out.println("Over budget");

                }

            };

        }

        publc static void main(String []arg){   

            PP h = new PP();

            DD d = h.dest("haha",11.111);

        }

    }

  • 2007-01-13 | java的内部类研究(1)【转】

    caicai1724 发布于 2007-04-27 21:57:58

    介绍嵌套类和内部类 [java虫虫 发表于 2005-10-18 11:13:45]

    在另一个类中定义的类就是嵌套类(nested classes)。嵌套类的范围由装入它的类的范围限制。这样,如果类B被定义在类A之内,那么B为A所知,然而不被A的外面所知。嵌套类可以访问嵌套它的类的成员,包括private 成员。但是,包围类不能访问嵌套类的成员。

    嵌套类一般有2种类型:前面加static标识符的和不加static 标识符的。一个static的嵌套类有static修饰符。因为它是static,所以只能通过对象来访问它包围类的成员。也就是说,它不能直接引用它包围类的成员。因为有这个限制,所以static嵌套类很少使用。

    嵌套类最重要的类型是内部类(inner class)。内部类是非static的嵌套类。它可以访问它的外部类的所有变量和方法,它可以直接引用它们,就像外部类中的其他非static成员的功能一样。这样,一个内部类完全在它的包围类的范围之内。

    下面的程序示例了如何定义和使用一个内部类。名为Outer 的类有一个名为outer_x 的示例变量,一个名为test()的实例方法,并且定义了一个名为Inner 的内部类。

    // Demonstrate an inner class.
    class Outer {
    int outer_x = 100;

    void test() {
    Inner inner = new Inner();
    inner.display();

    }

    // this is an inner class class Inner { void display() {System.out.println("display: outer_x = " + outer_x); }}}

    class InnerClassDemo {

    public static void main(String args[]) {
    Outer ōuter = new Outer();
    outer.test();

    }
    }

    该程序的输出如下所示:

    display: outer_x = 100

    在本程序中,内部类Inner 定义在Outer 类的范围之内。因此,在Inner 类之内的任何代码可以直接访问变量outer_x 。实例方法display() 定义在Inner 的内部,该方法以标准的输出流显示 outer_x 。InnerClassDemo 的main( ) 方法创建类Outer 的一个实例并调用它的test( )方法。创建类Inner 和display() 方法的一个实例的方法被调用。

    认识到Inner 类只有在类Outer 的范围内才是可知的是很重要的。如果在类Outer 之外的任何代码试图实例化Inner 类,Java 编译器会产生一条错误消息。总体来说,一个嵌套类和其他任何另外的编程元素没有什么不同:它仅仅在它的包围范围内是可知的。

    我们解释过,一个内部类可以访问它的包围类的成员,但是反过来就不成立了。内部类的成员只有在内部类的范围之内是可知的,而且不能被外部类使用。例如:

    // This program will not compile.
    class Outer {
    int outer_x = 100;

    void test() {
    Inner inner = new Inner();
    inner.display();

    }

    // this is an inner class
    class Inner {
    int y = 10; // y is local to Inner
    void display() {

    System.out.println("display: outer_x = " + outer_x);
    }
    }

    void showy() { System.out.println(y); // error,y not known here!}}

    class InnerClassDemo {

    public static void main(String args[]) {
    Outer ōuter = new Outer();
    outer.test();

    }
    }

    这里,y是作为Inner 的一个实例变量来声明的。这样对于该类的外部它就是不可知的,因此不能被showy() 使用。

    尽管我们强调嵌套类在它的外部类的范围之内声明,但在几个程序块的范围之内定义内部类是可能的。例如,在由方法定义的块中,或甚至在for 循环体内部,你也可以定义嵌套类,如下面的程序所示:

    // Define an inner class within a for loop.
    class Outer {
    int outer_x = 100;

    void test() { for(int i=0; i<10; i++) { class Inner { void display() {System.out.println("display: outer_x = " + outer_x);

    }
    }
    Inner inner = new Inner();
    inner.display();

    }
    }
    }

    class InnerClassDemo {

    public static void main(String args[]) {
    Outer ōuter = new Outer();
    outer.test();

    }
    }

    该程序的这个版本的输出如下所示。

    display: outer_x = 100
    display: outer_x = 100
    display: outer_x = 100

    display: outer_x = 100
    display: outer_x = 100
    display: outer_x = 100
    display: outer_x = 100
    display: outer_x = 100
    display: outer_x = 100
    display: outer_x = 100

    尽管嵌套类在日常的大多数编程中不使用,但当处理applet (小应用程序)时是特别有帮助的。在第20章中我们将继续嵌套类的话题。在那里你将看到对于某些类型的事件内部类如何被用来简化代码。你也将了解匿名内部类(anonymous inner classes),它是一个没有名字的内部类。

  • 2007-01-10 | 工作中MYSQL资料整理

    caicai1724 发布于 2007-04-27 21:55:34

    MYSQL资料整理:
    MYSQL的安装
    Mysql 安装目录:
    用户权限设置
    Mysql的权限系统的主要功能是验证从假定主机来的用户连接.以及检查用户对数据库的select,insert,update和delete操作权限.
    Mysql的用户名和口令可以设置
    MySQL提供的权限:权限信息存放在mysql数据库(即数据库的名字叫mysql)的user, db, host, tables_priv and columns_priv表中.
    user表
    定义了哪些用户允许连接到数据库服务器,口令以及可以连接的主机。%可做通配符。
    Db表和host表
    控制用户可以使用哪些数据库,在这些数据库中可以使用哪些操作。
    tables_priv表和columns_priv表
    允许数据库管理员分别限制在数据库中访问具体的表和一个表中具体的列。
    操作数据库及表
    Mysql启动:/usr/local/mysql/bin/mysqld_safe –user=mysql &
         停止:/usr/local/mysql/bin/mysqladmin shutdown
         重起:service mysqld restart
    查看服务:ps –ef|grep mysql
    查看服务状态:mysqladmin status(服务器正常运行时间,每秒查询数量,当前打开表的数量等)。
    查看版本信息:mysqladmin version
    查看客户机连接状态:mysql> show processlist;
    Mysql服务错误日志:/usr/local/mysql/var/ServerName.err
    链接数据库:shell> mysql [-h 主机名] [-u 用户名] [-p口令]
                 如果在命令行没有指定连接参数,Mysql将使用缺省值:
    ·缺省的主机名是localhost.
    ·缺省的用户名是你在Unix中的登录名.
    ·如果没有-p参数则将不提供口令.
    在系统重起时自动启动:vi /etc/rc.d/rc.local 将启动语句写入

    数据备份,数据还原
    备份database或table    mysqldump database > dump.sql
                          mysqldump database table1 table2 > dump.sql
    恢复database或table    mysqladmin create target_db_name
    mysql database < dump.sql
                          Mysql database table1 table2<dump.sql
    常用脚本命令
    创建数据库            create database TestTable;
    删除数据库            drop database TestTable;
    创建表          create table test_table (id INT(3),name VARCHAR(8),pass VARCHAR(20));
    主键(唯一的)                   create table test_table (id INT(3),name VARCHAR(8),pass VARCHAR(20),primary key  (id));可以一个单独的字段或多字段的组合。
    索引                   create table test_table (id INT(3),name VARCHAR(8),pass VARCHAR(20),index(id));
    使用数据库            use TestTable;
    使用某数据库的某表    use TestTable.test_table;
    提供表的列信息        describe table1
    复制表结构            create table test_table1 select * from test_table;
                          create table test_table1 select id,name from test_table;
    增加字段              alter table test_table1 add email varchar(255) not null;
    删除字段              alter table test_table1 drop email;
    重命名                alter table test_table rename to test_table1;
    删除表                drop table test_table;
    插入数据              insert into table (value1,value2,value3) values (‘a’,’b’,’c’);
    更新数据              update table set value1=’a’ where value2=’b’;
    删除数据              delete from table where value1=’a’;
    检索记录              select * from table limit 5;
                          select value1,value2 from talbe;
                          select * from table where value(>,=,or,and)’a’;
                          select MAX(value1) from table;
                          select MIN(value2) from talbe;
                          select count(value3) from table;
                          select * from talble order by value1;

  • 2007-01-10 | Oracle和mysql的一些简单命令比较【转】

    caicai1724 发布于 2007-04-27 21:45:42

    oracle 和 mysql 的一些简单命令对比参照

    oracle mysql
    对比版本 personal oracle7 release 7.3.4.0.0 mysql 3.22.34-shareware-debug
    默认安装目录 c:\orawin95 c:\mysql
    各种实用程序所在目录 c:\orawin95\bin c:\mysql\bin
    控制台工具 svrmgr.exe
    svrmgr23.exe
    mysqladmin.exe
    数据库启动程序 0start73.exe screen mysqld-shareware.exe
    关闭数据库命令 ostop73.exe mysqladmin.exe -u root shutdown
    客户程序 sql*plus mysql
    启动命令 c:\orawin95\bin\sqlplus.exe c:\mysql\bin\mysql.exe
    带用户启动方式
    (直接连库方式)
    c:\orawin95\bin\sqlplus.exe system/manager@tns c:\mysql\bin\mysql.exe test
    c:\mysql\bin\mysql.exe -u root test
    安装后系统默认用户(库) sys
    system
    scott
    mysql
    test
    显示所有用户(库) sql >select * from all_users; c:\mysql\bin>mysqlshow
    c:\mysql\bin>mysqlshow --status
    mysql> show databases;
    退出命令 sql> exit
    sql> quit
    mysql> exit
    mysql> quit
    改变连接用户(库) sql> conn 用户名/密码@主机字符串 mysql> use 库名
    查询当前所有的表 sql> select * from tab;
    sql> select * from cat;
    mysql> show tables;
    c:\mysql\bin>mysqlshow 库名
    显示当前连接用户(库) sql> show user mysql> connect
    查看帮助 sql> ? mysql> help
    显示表结构 sql> desc 表名
    sql> describe 表名
    mysql> desc 表名;
    mysql> describe 表名;
    mysql> show columns from 表名;
    c:\mysql\bin>mysqlshow 库名 表名
    日期函数 sql> select sysdate from dual; mysql> select now();
    mysql> select sysdate();
    mysql> select curdate();
    mysql> select current_date;
    mysql> select curtime();
    mysql> select current_time;
    日期格式化 sql> select to_char(sysdate,'yyyy-mm-dd') from dual;
    sql> select to_char(sysdate,'hh24-mi-ss') from dual;
    mysql> select date_format(now(),'%y-%m-%d');
    mysql> select time_format(now(),'%h-%i-%s');
    日期函数
    (增加一个月)
    sql> select to_char(add_months(to_date('20000101','yyyymmdd'),1),'yyyy-mm-dd') from dual;
    结果:2000-02-01
    sql> select to_char(add_months(to_date('20000101','yyyymmdd'),5),'yyyy-mm-dd') from dual;
    结果:2000-06-01
    mysql> select date_add('2000-01-01',interval 1 month);
    结果:2000-02-01
    mysql> select date_add('2000-01-01',interval 5 month);
    结果:2000-06-01
    别名 sql> select 1 a from dual; mysql> select 1 as a;
    字符串截取函数 sql> select substr('abcdefg',1,5) from dual;
    sql> select substrb('abcdefg',1,5) from dual;
    结果:abcde
    mysql> select substring('abcdefg',2,3);
    结果:bcd
    mysql> select mid('abcdefg',2,3);
    结果:bcd
    mysql> select substring('abcdefg',2);
    结果:bcdefg
    mysql> select substring('abcdefg' from 2);
    结果:bcdefg
    另有substring_index(str,delim,count)函数
    返回从字符串str的第count个出现的分隔符delim之后的子串。
    如果count是正数,返回最后的分隔符到左边(从左边数) 的所有字符。
    如果count是负数,返回最后的分隔符到右边的所有字符(从右边数)。
    执行外部脚本命令 sql >@a.sql 1:mysql> source a.sql
    2:c:\mysql\bin>mysql <a.sql
    3:c:\mysql\bin>mysql 库名 <a.sql
    导入、导出工具 exp.exe
    exp73.exe
    imp.exe
    imp73.exe
    mysqldump.exe
    mysqlimport.exe
    改表名 sql> rename a to b; mysql> alter table a rename b;
    执行命令 ;<回车>
    /
    r
    run
    ;<回车>
    go
    ego
    distinct用法 sql> select distinct 列1 from 表1;
    sql> select distinct 列1,列2 from 表1;
    mysql> select distinct 列1 from 表1;
    mysql> select distinct 列1,列2 from 表1;
    注释 --
    /*与*/
    #
    --
    /*与*/
    当作计算器 sql> select 1+1 from dual; mysql> select 1+1;
    限制返回记录条数 sql> select * from 表名 where rownum<5; mysql> select * from 表名 limit 5;
    新建用户(库) sql> create user 用户名 identified by 密码; mysql> create database 库名;
    删用户(库) sql> drop user 用户名; mysql> drop database 库名;
    外连接 使用(+) 使用left join
    查询索引 sql> select index_name,table_name from user_indexes; mysql> show index from 表名 [from 库名];
    通配符 “%” “%”和“_”
    sql语法 select selection_list 选择哪些列
    from table_list 从何处选择行
    where primary_constraint 行必须满足什么条件
    group by grouping_columns 怎样对结果分组
    having secondary_constraint 行必须满足的第二条件
    order by sorting_columns 怎样对结果排序
    select selection_list 选择哪些列
    from table_list 从何处选择行
    where primary_constraint 行必须满足什么条件
    group by grouping_columns 怎样对结果分组
    having secondary_constraint 行必须满足的第二条件
    order by sorting_columns 怎样对结果排序
    limit count 结果限定
  • 精妙SQL语句学习

    abens0426 发布于 2007-04-18 13:30:07

                              精妙SQL语句学习

    SQL分类:
    DDL—
    数据定义语言(CREATEALTERDROPDECLARE)
    DML—
    数据操纵语言(SELECTDELETEUPDATEINSERT)
    DCL—
    数据控制语言(GRANTREVOKECOMMITROLLBACK)

    首先,简要介绍基础语句:
    1
    、说明:创建数据库
    CREATE DATABASE database-name
    2
    、说明:删除数据库
    drop database dbname
    3
    、说明:备份sql server
    ---
    创建 备份数据的 device
    USE master
    EXEC sp_addumpdevice 'disk', 'testBack',  'c:\mssql7backup\MyNwind_1.dat'
    ---
    开始 备份
    BACKUP DATABASE pubs TO testBack  
    4
    、说明:创建新表
    create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
    根据已有的表创建新表:
       A
    create table tab_new like tab_old (使用旧表创建新表)
       B
    create table tab_new as select col1,col2… from tab_old definition only
    5
    、说明:删除新表drop table tabname
    6
    、说明:增加一个列
    Alter table tabname add column col type
    注:列增加后将不能删除。DB2中列加上后数据类型也不能改变,唯一能改变的是增加varchar类型的长度。
    7
    、说明:添加主键: Alter table tabname add primary key(col)
    说明:删除主键: Alter table tabname drop primary key(col)
    8
    、说明:创建索引:create [unique] index idxname on tabname(col….) 
    删除索引:drop index idxname
    注:索引是不可更改的,想更改必须删除重新建。
    9
    、说明:创建视图:create view viewname as select statement
    删除视图:drop view viewname
    10
    、说明:几个简单的基本的sql语句
    选择:select * from table1 where 范围
    插入:insert into table1(field1,field2) values(value1,value2)
    删除:delete from table1 where 范围
    更新:update table1 set field1=value1 where 范围
    查找:select * from table1 where field1 like ’%value1%’ ---like的语法很精妙,查资料!
    排序:select * from table1 order by field1,field2 [desc]
    总数:select count(*) as totalcount from table1
    求和:select sum(field1) as sumvalue from table1
    平均:select avg(field1) as avgvalue from table1
    最大:select max(field1) as maxvalue from table1
    最小:select min(field1) as minvalue from table1
    11
    、说明:几个高级查询运算词
    A
    UNION 运算符
    UNION
    运算符通过组合其他两个结果表(例如 TABLE1 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2
    B
    EXCEPT 运算符
    EXCEPT
    运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。 
    C
    INTERSECT 运算符
    INTERSECT
    运算符通过只包括 TABLE1 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。
    注:使用运算词的几个查询结果行必须是一致的。
    12
    、说明:使用外连接
    A
    left outer join
    左外连接(左连接):结果集几包括连接表的匹配行,也包括左连接表的所有行。
    B
    right outer join:
    右外连接(右连接):结果集既包括连接表的匹配连接行,也包括右连接表的所有行。
    C
    full outer join
    全外连接:不仅包括符号连接表的匹配行,还包括两个连接表中的所有记录。


    其次,大家来看一些不错的sql语句
    1
    、说明:复制表(只复制结构,源表名:a 新表名:b) (Access可用)
    法一:select * into b from a where 1<>1
    法二:select top 0 * into b from a

    2、说明:拷贝表(拷贝数据,源表名:a 目标表名:b) (Access可用)
    insert into b(a, b, c) select d,e,f from b;

    3、说明:跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用)
    insert into b(a, b, c) select d,e,f from b in ‘
    具体数据库’  where 条件
    例子:..from b in '"&Server.MapPath(".")&"\data.mdb" &"' where..

    4、说明:子查询(表名1a 表名2b)
    select a,b,c from a where a IN (select d from b ) 
    或者:  select a,b,c from a where a IN (1,2,3)

    5、说明:显示文章、提交人和最后回复时间
    select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b

    6、说明:外连接查询(表名1a 表名2b)
    select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c

    7、说明:在线视图查询(表名1a )
    select * from (SELECT a,b,c FROM a) T where t.a > 1;

    8、说明:between的用法,between限制查询数据范围时包括了边界值,not between不包括
    select * from table1 where time between time1 and time2
    select a,b,c, from table1 where a not between
    数值1 and 数值2

    9、说明:in 的使用方法
    select * from table1 where a [not] in (‘
    1’,’2’,’4’,’6’)

    10、说明:两张关联表,删除主表中已经在副表中没有的信息
    delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 )

    11、说明:四表联查问题:
    select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....

    12、说明:日程安排提前五分钟提醒
    SQL: select * from
    日程安排 where datediff('minute',f开始时间,getdate())>5

    13、说明:一条sql 语句搞定数据库分页
    select top 10 b.* from (select top 20
    主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段

    14、说明:前10条记录
    select top 10 * form table1 where
    范围

    15、说明:选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.)
    select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)

    16、说明:包括所有在 TableA 中但不在 TableBTableC 中的行并消除所有重复行而派生出一个结果表
    (select a from tableA ) except (select a from tableB) except (select a from tableC)

    17、说明:随机取出10条数据
    select top 10 * from tablename order by newid()

    18、说明:随机选择记录
    select newid()

    19、说明:删除重复记录
    Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...)

  • SQL常用命令使用方法

    abens0426 发布于 2007-04-18 13:28:32

    |SQL常用命令使用方法:|

    (1) 数据记录筛选:

    sql="select * from 数据表 where 字段名=字段值 order by 字段名 [desc]"

    sql="select * from 数据表 where 字段名 like '%字段值%' order by 字段名 [desc]"

    sql="select top 10 * from 数据表 where 字段名 order by 字段名 [desc]"

    sql="select * from 数据表 where 字段名 in ('1','2','3')"

    sql="select * from 数据表 where 字段名 between 1 and 2"

    (2) 更新数据记录:

    sql="update 数据表 set 字段名=字段值 where 条件表达式"

    sql="update 数据表 set 字段1=1,字段2=2 …… 字段n=n where 条件表达式"

    (3) 删除数据记录:

    sql="delete from 数据表 where 条件表达式"

    sql="delete from 数据表" (将数据表所有记录删除)

    (4) 添加数据记录:

    2insert语句:1)带字段名2)不带字段名(即针对所有的字段,并进行赋值,值不确定为null

    sql="insert into 数据表 (字段1,字段2,字段3 …) values (1,2,3 …)"

    也可以不写上字段名:

    如果加入表的所有列的数据,不用写字段名了(ID是自动排列的不用写!字符型的数据不要加引号!除非是浮点型的数据)

    sql=”insert into 包装 values('009','恐龙',2.3,null,null)”//前提:列名或所提供值的数目与表定义必须匹配

    sql="insert into 目标数据表 select * from 源数据表" (把源数据表的记录添加到目标数据表)   //前提:列名或所提供值的数目与表定义必须匹配

    (5) 数据记录统计函数:

    AVG(字段名) 得出一个表格栏平均值

    COUNT(*¦字段名) 对数据行数的统计或对某一栏有值的数据行数统计

    MAX(字段名) 取得一个表格栏最大的值

    MIN(字段名) 取得一个表格栏最小的值

    SUM(字段名) 把数据栏的值相加

    引用以上函数的方法:

    sql="select sum(字段名) as 别名 from 数据表 where 条件表达式"

    set rs=conn.excute(sql)

    rs("别名") 获取统的计值,其它函数运用同上。

    (5) 数据表的建立和删除:

    CREATE TABLE 数据表名称(字段1 类型1(长度),字段2 类型2(长度) …… )

    例:CREATE TABLE tab01(name varchar(50),datetime default now())

    DROP TABLE 数据表名称 (永久性删除一个数据表)

    //字段类型  char:数字,用于ID,比较短的名称如州,邮编,电话等char(3)

                Varchar:字符,用于文字等,varchar(20)||varchar(20) null

                Money:价格,money|money null可允许为空||money not null不允许为空

                Image:照片,image null

                Int:年份,销售数量,int

                Smallint:月份,samllint

                Datetime:日期,datetime||datetime null

    另一资料,sql基本语句.

    掌握SQL四条最基本的数据操作语句:InsertSelectUpdateDelete

       练掌握SQL是数据库用户的宝贵财 富。在本文中,我们将引导你掌握四条最基本的数据操作语句—SQL的核心功能来依次介绍比较操作符、选择断言以及三值逻辑。当你完成这些学习后,显然你已经开始算是精通SQL了。

      在我们开始之前,先使用CREATE TABLE语句来创建一个表(如图1所示)。DDL语句对数据库对象如表、列和视进行定义。它们并不对表中的行进行处理,这是因为DDL语句并不处理数据库中实际的数据。这些工作由另一类SQL语句数据操作语言(DML)语句进行处理。

      SQL中有四种基本的DML操作:INSERTSELECTUPDATEDELETE。由于这是大多数SQL用户经常用到的,我们有必要在此对它们进行一一说明。在图1中我们给出了一个名为EMPLOYEES的表。其中的每一行对应一个特定的雇员记录。请熟悉这张表,我们在后面的例子中将要用到它。

      INSERT语句

      用户可以用INSERT语句将一行记录插入到指定的一个表中。例如,要将雇员John Smith的记录插入到本例的表中,可以使用如下语句:

      INSERT INTO EMPLOYEES VALUES

       ('Smith','John','1980-06-10',

       'Los Angles',16,45000);

      通过这样的INSERT语句,系统将试着将这些值填入到相应的列中。这些列按照我们创建表时定义的顺序排列。在本例中,第一个值“Smith”将填到第一个列LAST_NAME中;第二个值“John”将填到第二列FIRST_NAME……以此类推。

      我们说过系统会试着将值填入,除了执行规则之外它还要进行类型检查。如果类型不符(如将一个字符串填入到类型为数字的列中),系统将拒绝这一次操作并返回一个错误信息。

      如果SQL拒绝了你所填入的一列值,语句中其他各列的值也不会填入。这是因为SQL提供对事务的支持。一次事务将数据库从一种一致性转移到另一种一致性。如果事务的某一部分失败,则整个事务都会失败,系统将会被恢复(或称之为回退)到此事务之前的状态。

       回到原来的INSERT的例子,请注意所有的整形十进制数都不需要用单引号引起来,而字符串和日期类型的值都要用单引号来区别。为了增加可读性而在数字间插入逗号将会引起错误。记住,在SQL中逗号是元素的分隔符。

      同样要注意输入文字值时要使用单引号。双引号用来封装限界标识符。

      对于日期类型,我们必须使用SQL标准日期格式(yyyy-mm-dd),但是在系统中可以进行定义,以接受其他的格式。当然,2000年临近,请你最好还是使用四位来表示年份。

      既然你已经理解了INSERT语句是怎样工作的了,让我们转到EMPLOYEES表中的其他部分:

      INSERT INTO EMPLOYEES VALUES

       ('Bunyan','Paul','1970-07-04',

       'Boston',12,70000);

      INSERT INTO EMPLOYEES VALUES

       ('John','Adams','1992-01-21',

       'Boston',20,100000);

      INSERT INTO EMPLOYEES VALUES

       ('Smith','Pocahontas','1976-04-06',

       'Los Angles',12,100000);

      INSERT INTO EMPLOYEES VALUES

       ('Smith','Bessie','1940-05-02',

       'Boston',5,200000);

      INSERT INTO EMPLOYEES VALUES

       ('Jones','Davy','1970-10-10',

       'Boston',8,45000);

      INSERT INTO EMPLOYEES VALUES

       ('Jones','Indiana','1992-02-01',

       'Chicago',NULL,NULL);

      在最后一项中,我们不知道Jones先生的工薪级别和年薪,所以我们输入NULL(不要引号)。NULLSQL中的一种特殊情况,我们以后将进行详细的讨论。现在我们只需认为NULL表示一种未知的值。

      有时,像我们刚才所讨论的情况,我们可能希望对某一些而不是全部的列进行赋值。除了对要省略的列输入NULL外,还可以采用另外一种INSERT语句,如下:

      INSERT INTO EMPLOYEES(

       FIRST_NAME, LAST_NAME,

       HIRE_DATE, BRANCH_OFFICE)

      VALUES(

       'Indiana','Jones',

       '1992-02-01','Indianapolis');

      这样,我们先在表名之后列出一系列列名。未列出的列中将自动填入缺省值,如果没有设置缺省值则填入NULL。请注意我们改变了列的顺序,而值的顺序要对应新的列的顺序。如果该语句中省略了FIRST_NAMELAST_NAME项(这两项规定不能为空),SQL操作将失败。

      让我们来看一看上述INSERT语句的语法图:

      INSERT INTO table

       [(column { ,column})]

      VALUES

       (columnvalue [{,columnvalue}]);

      和前一篇文章中一样,我们用方括号来表示可选项,大括号表示可以重复任意次数的项(不能在实际的SQL语句中使用这些特殊字符)。VALUE子句和可选的列名列表中必须使用圆括号。

      SELECT语句

      SELECT语句可以从一个或多个表中选取特定的行和列。因为查询和检索数据是数据库管理中最重要的功能,所以SELECT语句在SQL中是工作量最大的部分。实际上,仅仅是访问数据库来分析数据并生成报表的人可以对其他SQL语句一窍不通。

      SELECT语句的结果通常是生成另外一个表。在执行过程中系统根据用户的标准从数据库中选出匹配的行和列,并将结果放到临时的表中。在直接SQLdirect SQL)中,它将结果显示在终端的显示屏上,或者将结果送到打印机或文件中。也可以结合其他SQL语句来将结果放到一个已知名称的表中。

      SELECT语句功能强大。虽然表面上看来它只用来完成本文第一部分中提到的关系代数运算选择(或称限制),但实际上它也可以完成其他两种关系运算—“投影连接SELECT语句还可以完成聚合计算并对数据进行排序。

      SELECT语句最简单的语法如下:

      SELECT columns FROM tables;

      当我们以这种形式执行一条SELECT语句时,系统返回由所选择的列以及用户选择的表中所有指定的行组成的一个结果表。这就是实现关系投影运算的一个形式。

      让我们看一下使用图1EMPLOYEES表的一些例子(这个表是我们以后所有SELECT语句实例都要使用的。而我们在图2和图3中给出了查询的实际结果。我们将在其他的例子中使用这些结果)。

      假设你想查看雇员工作部门的列表。那下面就是你所需要编写的SQL查询:

      SELECT BRANCH_OFFICE FROM EMPLOYEES;

      以上SELECT语句的执行将产生如图2中表2所示的结果。

      由于我们在SELECT语句中只指定了一个列,所以我们的结果表中也只有一个列。注意结果表中具有重复的行,这是因为有多个雇员在同一部门工作(记住SQL从所选的所有行中将值返回)。要消除结果中的重复行,只要在SELECT语句中加上DISTINCT子句:(目的:去掉查询结果中相同的项,仅保留着一项)

      SELECT DISTINCT BRANCH_OFFICE

      FROM EMPLOYEES;

      这次查询的结果如表3所示。

      现在已经消除了重复的行,但结果并不是按照顺序排列的。如果你希望以字母表顺序将结果列出又该怎么做呢?只要使用ORDER BY子句就可以按照升序或降序来排列结果:

      SELECT DISTINCT BRANCH_OFFICE

      FROM EMPLOYEES

      ORDER BY BRANCH_OFFICE ASC;

      这一查询的结果如表4所示。请注意在ORDER BY之后是如何放置列名BRANCH _OFFICE的,这就是我们想要对其进行排序的列。为什么即使是结果表中只有一个列时我们也必须指出列名呢?这是因为我们还能够按照表中其他列进行排序,即使它们并不显示出来。列名BRANCH_ OFFICE 查看(1398) 评论(0) 收藏 分享 管理

  • 数据库并发问题详述

    风在吹 发布于 2006-12-06 10:36:41

    问题背景及特点:

        我们在使用多用户数据库时常常会碰到数据更新失败、删除失等情况,如果有多个用户且同时访问一个数据库则当他们的事务同时使用相同的数据时可能会发生并发问题。

        并发问题包括:

        1.丢失或覆盖更新。(幻像读)

        2.未确认的相关性(脏读)。

        3.不一致的分析(非重复读)。

        详细描述:

        1.丢失更新

        当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。

        例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。

        2.未确认的相关性(脏读)

        当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。

        例如,一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为目前所做的更改是错误的,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该问题。

        3.不一致的分析(非重复读)

        当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。

        例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

        4.幻像读

        当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。

        例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。
    -----------------------------------------------------------------------------------------------
        从上面可以看到,解决并发主要是用到了锁和事务。

        锁  :给记录或表加上锁是为了对当前操作对象加上一个状态表示位,让其它用户在获取编辑权限时有了判断。

        事务:是为了保证一组操作的完整性。(要么就全部成功,要么就全部失败)

    -----------------------------------------------------------------------------------------------
        一般处理并发问题时我这样做:

        1.开启事务。

        2.申请写权限,也就是给对象(表或记录)加锁。

        3.如果失败,则结束事务,过一会重试。

        4.如果成功,也就是给对象加锁成功,防止其它用户再用同样的方式打开。

        5.进行编辑操作。

        6.写入所进行的编辑结果。

        7.如果写入成功,则提交事务,完成操作。

        8.如果写入失败,则回滚事务,取消提交。

        9.(7.8)两步操作已释放了锁定的对象,恢复到操作前的状态。

  • SQLServer update语句用法

    jzhao 发布于 2007-03-30 09:47:57

    UPDATE语句用于创建一个更新查询,根据指定的条件更改指定表中的字段值。UPDATE语句不生成结果集,而且当使用更新查询更新记录之后,不能取消这次操作。

    语法:UPDATE table SET newvalue WHERE criteria

    说明:table参数指定表的名称,其中包含要更改的数据。
    newvalue参数为一个表达式,用来计算要插入更新记录中特定字段的值。
    criteria参数为一个表达式,用来指定被更新的记录。只有符合表达式的记录才会被更新。

    名称

    UPDATE — 替换表中列/字段的数值

    语法

    UPDATE table SET col = expression [, ...]
        [ FROM fromlist ]
        [ WHERE condition ]

    输入

    table
    现存表的名称.
    column
    table 中列/字段的名.
    expression
    赋予列/字段的一个有效的值或表达式.
    fromlist
    Postgres 的一个非标准的扩展,允许别的表中的列/字段出现在 WHERE 条件里.
    condition
    请参考 SELECT 语句获得 WHERE 子句的进一步描述.

    输出

    UPDATE #
    成功的返回信息.# 意味着更新的行数.如果 # 等于 0 则没有行被更新.

    描述

    UPDATE 改变满足条件的所有行的声明了的列/字段的值。只有要更改的列/字段需要在语句中出现.

    数组引用使用与 SELECT 里一样的语法.也就是说,单个数组元素,数组元素的一个范围或者是整个数组都可以用一个查询语句更新.

    要更改表,你必须对它有写权限,同样对 WHERE 条件里提到的任何表也要有读权限.

    用法

    把字段 kind 里的词 "Drama" 用 "Dramatic" 代替:
    UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama';
    SELECT * FROM films WHERE kind = 'Dramatic' OR kind = 'Drama';

    code  |     title     | did | date_prod  |   kind   | len
    -------+---------------+-----+------------+----------+-------
    BL101 | The Third Man | 101 | 1949-12-23 | Dramatic | 01:44
    P_302 | Becket        | 103 | 1964-02-03 | Dramatic | 02:28
    M_401 | War and Peace | 104 | 1967-02-12 | Dramatic | 05:57
    T_601 | Yojimbo       | 106 | 1961-06-16 | Dramatic | 01:50
    DA101 | Das Boot      | 110 | 1981-11-11 | Dramatic | 02:29

    兼容性

    SQL92

    SQL92 在定位的 UPDATE 语句上定义了一些不同的语法:
    UPDATE table SET column = expression [, ...]
        WHERE CURRENT OF cursor
    这里 cursor 表示一个打开的游标.
  • 男人35岁前成功的12条黄金法则

    Spark.lee 发布于 2007-05-11 09:02:15

    第一章:一个目标

    一艘没有航行目标的船,任何方向的风都是逆风
    1、你为什么是穷人,第一点就是你没有立下成为富人的目标
    2、你的人生核心目标是什么?
    杰出人士与平庸之辈的根本差别并不是天赋、机遇,而在于有无目标。
    3、起跑领先一步,人生领先一大步:成功从选定目标开始
    4、贾金斯式的人永远不会成功
    为什么大多数人没有成功?真正能完成自己计划的人只有5%,大多数人不是将自己的目标舍弃,就是沦为缺乏行动的空想
    5、 如果你想在35岁以前成功,你一定在25至30岁之间确立好你的人生目标
    6、 每日、每月、每年都要问自己:我是否达到了自己定下的目标

    第二章:两个成功基点

    站好位置,调正心态,努力冲刺,35岁以前成功
    (一)人生定位
    1、 人怕入错行:你的核心竞争力是什么?
    2、 成功者找方法,失败者找借口
    3、 从三百六十行中选择你的最爱
    人人都可以创业,但却不是人人都能创业成功
    4、 寻找自己的黄金宝地
    (二)永恒的真理:心态决定命运,35岁以前的心态决定你一生的命运
    1、 不满现状的人才能成为富翁
    2、 敢于梦想,勇于梦想,这个世界永远属于追梦的人
    3、 35岁以前不要怕,35岁以后不要悔
    4、 出身贫民,并非一辈子是贫民,只要你永远保持那颗进取的心。中国成功人士大多来自小地方
    5、 做一个积极的思维者
    6、 不要败给悲观的自己
    有的人比你富有一千倍,他们也会比你聪明一千倍么?不会,他们只是年轻时心气比你高一千倍。
    人生的好多次失败,最后并不是败给别人,而是败给了悲观的自己。
    7、 成功者不过是爬起来比倒下去多一次
    8、 宁可去碰壁,也不要在家里面壁
    克服你的失败、消极的心态
    (1) 找个地方喝点酒
    (2) 找个迪厅跳跳舞
    (3) 找帮朋友侃侃山
    (4) 积极行动起来

    第三章:三大技巧

    1、管理时间:你的时间在哪里,你的成就就在哪里。
    把一小时看成60分钟的人,比看作一小时的人多60倍
    2、你不理财,财不理你
    3、自我管理,游刃有余
    (1) 创业不怕本小,脑子一定要好
    (2) 可以开家特色店
    (3) 做别人不愿做的生意

    第四章:四项安身立命的理念

    35岁以前一定要形成个人风格
    1、做人优于做事
    做事失败可以重来,做人失败却不能重来
    (1) 做人要讲义气
    (2) 永不气馁
    2、豁达的男人有财运,豁达的女人有帮夫运
    35岁以前搞定婚姻生活
    3、忠诚的原则:35岁以前你还没有建立起忠诚美誉,这一缺点将要困扰你的一生
    4、把小事做细,但不要耍小聪明
    中国人想做大事的人太多,而愿把小事做完美的人太少

    第五章:五分运气

    比尔·盖茨说:人生是不公平的,习惯去接受它吧
    1、人生的确有很多运气的成人:谋事在人,成事在天:中国的古训说明各占一半
    2、机会时常意外地降临,但属于那些不应决不放弃的人
    3、抓住人生的每一次机会
    机会就像一只小鸟,如果你不抓住,它就会飞得无影无踪
    4、 者早一步,愚者晚一步

    第六章:六项要求

    1、智慧
    (1)别人可你以拿走你的一切,但拿不走你的智慧
    (2)巧妙运用自己的智慧
    (3)智者与愚者的区别
    2、勇气
    (1)勇气的力量有时会让你成为"超人"
    (2)敢于放弃,敢于"舍得"
    3、培养自己的"领导才能、领袖气质"
    (1) 激情感染别人
    (2) "三o七法则"实现领袖气质
    (3) 拍板决断能力
    (4) 人格魅力
    4、创造性:不要做循规蹈矩的人
    25-35岁是人生最有创造性的阶段,很多成功人士也都产生在这一阶段
    5、明智
    (1) 知道自己的长处、短处,定向聚焦
    (2) 尽量在自己的熟悉的领域努力
    6、持之以恒的行动力:在你选定行业坚持十年,你一定会成为大赢家

    第七章:七分学习

    1、知识改变命运
    2、35岁以前学会你行业中必要的一切知识
    a) 每天淘汰你自己
    b) 在商言商
    3、太相信的书人,只能成为打工仔
    4、思考、实践、再思考、再实践

    第八章:八分交际

    朋友多了路好走
    1、智商很重要,情商更重要:35岁以前建立起人际关系网
    2、人脉即财脉:如何搞好人际关系
    3、交友有原则
    4、善于沟通:35岁以前要锻炼出自己的演讲才能

    第九章:九分习惯

    习惯的力量是惊人的,35岁以前养成的习惯决定着你的成功的大小
    1、积极思维的好习惯
    2、养成高效工作的好习惯
    (1) 办公室
    (2) 生活可以不拘小节,但要把工作做细
    (3) 学习聆听,不打断别人说话
    3、养成锻炼身体的好习惯
    4、广泛爱好的好习惯
    5、快速行动的好习惯

    第十章:十分自信

    1、自信是成功的精神支柱
    2、自信方能赢得别人的信任
    3、把自信建立在创造价值的基础上
    4、如何建立自信
    (1) 为自己确立目标
    (2) 发挥自己的长处
    (3) 做事要有计划
    (4) 做事不拖拉
    (5) 轻易不要放弃
    (6) 学会自我激励
    (7) 不要让自己成为别人

    第十一章 11个需要避开的成功陷阱

    1、只有功劳,没有苦劳
    2、不要"怀才不遇",而要寻找机遇
    3、不要想发横财
    4、不要为钱而工作,而让钱为你工作
    5、 盲目跟风,人云亦云,人做我也做
    6、 小富即安,不思进取,知足常乐
    7、 承认错误而非掩饰错误
    8、 脚踏实地而非想入非非
    9、 野心太大而不是信心十足
    10、反复跳槽不可取
    11、眼高手低
    12、不择手段

    第十二章 十二分努力

    没有人能随随便便成功
    1、小不是成功,大不是成功,由小变大才是成功
    2、中国社会进入微利时代:巧干+敢干+实干=成功
    3、努力尝试就有成功的可能
    4、做任何事情,尽最大努力
    5、把事情当成事业来做
  • 软件测试之中文网络资源总汇

    海之梦 发布于 2007-05-04 15:38:58

    51Testing软件测试网  www.51testing.com
    测试时代  www.testage.net
    CSDN——软件测试频道  testing.csdn.net
    希赛网——软件测试频道  testing.csai.cn
    中国软件测试联盟  www.iceshi.com
    一起测试网  www.17testing.com
    北大测试  www.btesting.com
    中国软件测试基地 www.cntesting.com
    中国软件评测中心  www.cstc.org.cn
    中国软件质量网  www.rjzl.gov.cn

     

    测试时代论坛  www.testage.net/bbs

    中国软件测试社区 http://www.sztest.net/forum/

    海松宝的小屋  http://www1.testage.net/haisongbao/

    Alan工作室  http://alanzhou.nease.net/index.htm

    软件工程专家网  http://www.51cmm.com/

    51testing软件测试网(慧谷-博为峰软件测试工作室)  www.51testing.com

    中国软件测试在线  http://www.softtest.cn/

    杨柳清风论坛  http://www.kaiyuanlaw.com/dvbbs/

    天极网的软件测试板块  

    http://www.yesky.com/SoftChannel/72342393369657344/index.shtml

    测试工程师  http://opentest.51.net/index.htm

    自由龙(好像是珠海的)   http://www.freedragon.net/

  • Oracle SQL 性能优化技巧

    zengyi2008 发布于 2007-04-28 13:06:46

    选用适合的ORACLE优化器
    ORACLE
    的优化器共有3

    A
    RULE (基于规则) bCOST (基于成本) cCHOOSE (选择性)

       
    设置缺省的优化器,可以通过对init.ora文件中OPTIMIZER_MODE参数的各种声明,如RULECOSTCHOOSEALL_ROWSFIRST_ROWS 你当然也在SQL句级或是会话(session)级对其进行覆盖。

       
    为了使用基于成本的优化器(CBO Cost-Based Optimizer) 你必须经常运行analyze 命令,以增加数据库中的对象统计信息(object statistics)的准确性。

       
    如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关。 如果table已经被analyze过, 优化器模式将自动成为CBO 反之,数据库将采用RULE形式的优化器。

       
    在缺省情况下,ORACLE采用CHOOSE优化器, 为了避免那些不必要的全表扫描(full table scan) 你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器。


    2.
    访问Table的方式
    ORACLE
    采用两种访问表中记录的方式:

    A
    全表扫描

    全表扫描就是顺序地访问表中每条记录。ORACLE采用一次读入多个数据块(database block)的方式优化全表扫描。

    B
    通过ROWID访问表

       
    你可以采用基于ROWID的访问方式情况,提高访问表的效率, ROWID包含了表中记录的物理位置信息。ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系。通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高。


    3.
    共享SQL语句
       
    为了不重复解析相同的SQL语句,在第一次解析之后,ORACLESQL语句存放在内存中。这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享。 因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的执行路径。ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用。

       
    可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering),这个功能并不适用于多表连接查询。

       
    数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了。

       
    当你向ORACLE提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句。这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格,换行等)

       
    数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了。

    共享的语句必须满足三个条件:

    A
    字符级的比较: 当前被执行的语句和共享池中的语句必须完全相同。

    B
    两个语句所指的对象必须完全相同:

    C
    两个SQL语句中必须使用相同的名字的绑定变量(bind variables)


    4.
    选择最有效率的表名顺序(只在基于规则的优化器中有效)
        ORACLE
    的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理。在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。当ORACLE处理多个表时, 会运用排序及合并的方式连接它们。首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。

       
    如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表。


    5.WHERE
    子句中的连接顺序
        ORACLE
    采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。


    6.SELECT
    子句中避免使用 ' * '
       
    当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 '*' 是一个方便的方法。不幸的是,这是一个非常低效的方法。实际上,ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。


    7.
    减少访问数据库的次数
       
    当执行每条SQL语句时,ORACLE在内部执行了许多工作:解析SQL语句,估算索引的利用率,绑定变量,读数据块等等。由此可见,减少访问数据库的次数,就能实际上减少ORACLE的工作量。


    8.
    使用DECODE函数来减少处理时间
       
    使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表。


    9.
    整合简单,无关联的数据库访问
       
    如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系)


    10.
    删除重复记录

    11.
    TRUNCATE替代DELETE
       
    当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息。 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况)

       
    而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息。当命令运行后,数据不能被恢复。因此很少的资源被调用,执行时间也会很短。


    12.
    尽量多使用COMMIT
       
    只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少

    COMMIT
    所释放的资源:

    A
    回滚段上用于恢复数据的信息。

    B
    、被程序语句获得的锁。

    C
    redo log buffer 中的空间。

    D
    ORACLE为管理上述3种资源中的内部花费。


    13.
    计算记录条数
       
    和一般的观点相反,count(*) count(1)稍快,当然如果可以通过索引检索,对索引列的计数仍旧是最快的。例如 COUNT(EMPNO)


    14.
    Where子句替换HAVING子句
       
    避免使用HAVING子句,HAVING 只会在检索出所有记录之后才对结果集进行过滤。 这个处理需要排序,总计等操作。如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。


    15.
    减少对表的查询
       
    在含有子查询的SQL语句中,要特别注意减少对表的查询。


    16.
    通过内部函数提高SQL效率。

    17.
    使用表的别名(Alias)
       
    当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上。这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。


    18.
    EXISTS替代IN
       
    在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下,使用EXISTS(NOT EXISTS)通常将提高查询的效率。


    19.
    NOT EXISTS替代NOT IN
       
    在子查询中,NOT IN子句将执行一个内部的排序和合并。 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历)。为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)NOT EXISTS


    20.
    用表连接替换EXISTS
       
    通常来说,采用表连接的方式比EXISTS更有效率


    21.
    EXISTS替换DISTINCT
       
    当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT 一般可以考虑用EXIST替换

  • 2007-01-18 | java的死锁【转】

    caicai1724 发布于 2007-04-27 22:10:42

    在几乎所有编程语言中,由于多线程引发的错误都有着难以再现的特点,程序的死锁或其它多线程错误可能只在某些特殊的情形下才出现,或在不同的VM上运行同一个程序时错误表现不同。因此,在编写多线程程序时,事先认识和防范可能出现的错误特别重要。

      无论是客户端还是服务器端多线程Java程序,最常见的多线程问题包括死锁、隐性死锁和数据竞争。

      死锁

      死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

      导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。

      由于这个原因,在使用“synchronized”关键词时,很容易出现两个线程互相等待对方做出某个动作的情形。代码一是一个导致死锁的简单例子。

    //代码一
    class Deadlocker {
     int field_1;
     private Object lock_1 = new int[1];
     int field_2;
     private Object lock_2 = new int[1];
    
     public void method1(int value) {
      “synchronized” (lock_1) {
       “synchronized” (lock_2) {
        field_1 = 0; field_2 = 0;
       }
      }
     }
    
     public void method2(int value) {
      “synchronized” (lock_2) {
       “synchronized” (lock_1) {
        field_1 = 0; field_2 = 0;
       }
      }
     }
    }

      参考代码一,考虑下面的过程:

      ◆ 一个线程(ThreadA)调用method1()。

      ◆ ThreadA在lock_1上同步,但允许被抢先执行。

      ◆ 另一个线程(ThreadB)开始执行。

      ◆ ThreadB调用method2()。

      ◆ ThreadB获得lock_2,继续执行,企图获得lock_1。但ThreadB不能获得lock_1,因为ThreadA占有lock_1。

      ◆ 现在,ThreadB阻塞,因为它在等待ThreadA释放lock_1。

      ◆ 现在轮到ThreadA继续执行。ThreadA试图获得lock_2,但不能成功,因为lock_2已经被ThreadB占有了。

      ◆ ThreadA和ThreadB都被阻塞,程序死锁。

      当然,大多数的死锁不会这么显而易见,需要仔细分析代码才能看出,对于规模较大的多线程程序来说尤其如此。好的线程分析工具,例如JProbe Threadalyzer能够分析死锁并指出产生问题的代码位置。

      隐性死锁

      隐性死锁由于不规范的编程方式引起,但不一定每次测试运行时都会出现程序死锁的情形。由于这个原因,一些隐性死锁可能要到应用正式发布之后才会被发现,因此它的危害性比普通死锁更大。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待。

      加锁次序

      当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁。考虑下面的情形,ThreadA和ThreadB两个线程分别需要同时拥有lock_1、lock_2两个锁,加锁过程可能如下:

      ◆ ThreadA获得lock_1;

      ◆ ThreadA被抢占,VM调度程序转到ThreadB;

      ◆ ThreadB获得lock_2;

      ◆ ThreadB被抢占,VM调度程序转到ThreadA;

      ◆ ThreadA试图获得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞;

      ◆ 调度程序转到ThreadB;

      ◆ ThreadB试图获得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞;

      ◆ ThreadA和ThreadB死锁。

      必须指出的是,在代码丝毫不做变动的情况下,有些时候上述死锁过程不会出现,VM调度程序可能让其中一个线程同时获得lock_1和lock_2两个锁,即线程获取两个锁的过程没有被中断。在这种情形下,常规的死锁检测很难确定错误所在。

      占有并等待

      如果一个线程获得了一个锁之后还要等待来自另一个线程的通知,可能出现另一种隐性死锁,考虑代码二。

    //代码二
    public class queue {
     static java.lang.Object queueLock_;
     Producer producer_;
     Consumer consumer_;
    
     public class Producer {
      void produce() {
       while (!done) {
        “synchronized” (queueLock_) {
         produceItemAndAddItToQueue();
         “synchronized” (consumer_) {
          consumer_.notify();
         }
        }
       }
      }
    
      public class Consumer {
       consume() {
        while (!done) {
         “synchronized” (queueLock_) {
          “synchronized” (consumer_) {
           consumer_.wait();
          }
          removeItemFromQueueAndProcessIt();
         }
        }
       }
      }
     }
    }

      在代码二中,Producer向队列加入一项新的内容后通知Consumer,以便它处理新的内容。问题在于,Consumer可能保持加在队列上的锁,阻止Producer访问队列,甚至在Consumer等待Producer的通知时也会继续保持锁。这样,由于Producer不能向队列添加新的内容,而Consumer却在等待Producer加入新内容的通知,结果就导致了死锁。

      在等待时占有的锁是一种隐性的死锁,这是因为事情可能按照比较理想的情况发展—Producer线程不需要被Consumer占据的锁。尽管如此,除非有绝对可靠的理由肯定Producer线程永远不需要该锁,否则这种编程方式仍是不安全的。有时“占有并等待”还可能引发一连串的线程等待,例如,线程A占有线程B需要的锁并等待,而线程B又占有线程C需要的锁并等待等。

      要改正代码二的错误,只需修改Consumer类,把wait()移出“synchronized”()即可。

      数据竞争

      数据竞争是由于访问共享资源(例如变量)时缺乏或不适当地运用同步机制引起。如果没有正确地限定某一时刻某一个线程可以访问变量,就会出现数据竞争,此时赢得竞争的线程获得访问许可,但会导致不可预知的结果。

      由于线程的运行可以在任何时候被中断(即运行机会被其它线程抢占),所以不能假定先开始运行的线程总是比后开始运行的线程先访问到两者共享的数据。另外,在不同的VM上,线程的调度方式也可能不同,从而使数据竞争问题更加复杂。

      有时,数据竞争不会影响程序的最终运行结果,但在另一些时候,有可能导致不可预料的结果。

      良性数据竞争

      并非所有的数据竞争都是错误。考虑代码三的例子。假设getHouse()向所有的线程返回同一House,可以看出,这里会出现竞争:BrickLayer从House.foundationReady_读取,而FoundationPourer写入到House.foundationReady_。

    //代码三
    public class House {
     public volatile boolean foundationReady_ = false;
    }
    
    public class FoundationPourer extends Thread {
     public void run() {
      House a = getHouse();
      a.foundationReady_ = true;
     }
    }
    
    public class BrickLayer extends Thread {
     public void run() {
      House a = getHouse();
       while (!a.foundationReady_) {
        try {
         Thread.sleep(500);
        }
        catch (Exception e) {
         System.err.println(“Exception:” + e);
        }
       }
      }
     } 
    }

      尽管存在竞争,但根据Java VM规范,Boolean数据的读取和写入都是原则性的,也就是说,VM不能中断线程的读取或写入操作。一旦数据改动成功,不存在将它改回原来数据的必要(不需要“回退”),所以代码三的数据竞争是良性竞争,代码是安全的。

      恶性数据竞争

      首先看一下代码四的例子。

    //代码四
    public class Account {
     private int balance_; // 账户余额
     public int getBalance(void) {
      return balance_;
     }
     public void setBalance(int setting) {
      balance_ = setting;
     }
    }
    
    public class CustomerInfo {
     private int numAccounts_;
     private Account[] accounts_;
     public void withdraw(int accountNumber, int amount) {
      int temp = accounts_[accountNumber].getBalance();
      temp = temp - amount;
      accounts_[accountNumber].setBalance(temp);
     }
     public void deposit(int accountNumber, int amount) {
      int temp = accounts_[accountNumber].getBalance();
      temp = temp + amount;
      accounts_[accountNumber].setBalance(temp);
     }
    }

      如果丈夫A和妻子B试图通过不同的银行柜员机同时向同一账户存钱,会发生什么事情?让我们假设账户的初始余额是100元,看看程序的一种可能的执行经过。

      B存钱25元,她的柜员机开始执行deposit()。首先取得当前余额100,把这个余额保存在本地的临时变量,然后把临时变量加25,临时变量的值变成125。现在,在调用setBalance()之前,线程调度器中断了该线程。

      A存入50元。当B的线程仍处于挂起状态时,A这面开始执行deposit():getBalance()返回100(因为这时B的线程尚未把修改后的余额写入),A的线程在现有余额的基础上加50得到150,并把150这个值保存到临时变量。接着,A的线程在调用setBalance()之前,也被中断执行。

      现在,B的线程接着运行,把保存在临时变量中的值(125)写入到余额,柜员机告诉B说交易完成,账户余额是125元。接下来,A的线程继续运行,把临时变量的值(150)写入到余额,柜员机告诉A说交易完成,账户余额是150元。

      最后得到的结果是什么?B的存款消失不见,就像B根本没有存过钱一样。

      也许有人会认为,可以把getBalance()和setBalance()改成同步方法保护Account.balance_,解决数据竞争问题。其实这种办法是行不通的。“synchronized”关键词可以确保同一时刻只有一个线程执行getBalance()或setBalance()方法,但这不能在一个线程操作期间阻止另一个线程修改账户余额。

    要正确运用“synchronized”关键词,就必须认识到这里要保护的是整个交易过程不被另一个线程干扰,而不仅仅是对数据访问的某一个步骤进行保护。

      所以,本例的关键是当一个线程获得当前余额之后,要保证其它的线程不能修改余额,直到第一个线程的余额处理工作全部完成。正确的修改方法是把deposit()和withdraw()改成同步方法。

      死锁、隐性死锁和数据竞争是Java多线程编程中最常见的错误。要写出健壮的多线程代码,正确理解和运用“synchronized”关键词是很重要的。另外,好的线程分析工具,例如JProbe Threadalyzer,能够极大地简化错误检测。对于分析那些不一定每次执行时都会出现的错误,分析工具尤其有用。

  • 2007-01-18 | java的线程和多线程(2)【转】

    caicai1724 发布于 2007-04-27 22:09:55

    线程的创建和启动

    java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就结束了。一旦一个线程执行完毕,这个实例就不能再重新启动,只能重新生成一个新实例,再启动一个新线程。

    Thread类是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法:

    Thread t = new Thread();
    t.start();

    start()方法是一个native方法,它将启动一个新线程,并执行run()方法。Thread类默认的run()方法什么也不做就退出了。注意:直接调用run()方法并不会启动一个新线程,它和调用一个普通的java方法没有什么区别。

    因此,有两个方法可以实现自己的线程:

    方法1:自己的类extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

    public class MyThread extends Thread {
    public run() {
    System.out.println("MyThread.run()");
    }
    }

    在合适的地方启动线程:new MyThread().start();

    方法2:如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口:

    public class MyThread extends OtherClass implements Runnable {
    public run() {
    System.out.println("MyThread.run()");
    }
    }

    为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:

    MyThread myt = new MyThread();
    Thread t = new Thread(myt);
    t.start();

    事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:

    public void run() {
    if (target != null) {
    target.run();
    }
    }

    线程还有一些Name, ThreadGroup, isDaemon等设置,由于和线程设计模式关联很少,这里就不多说了。

    线程同步

    由于同一进程内的多个线程共享内存空间,在Java中,就是共享实例,当多个线程试图同时修改某个实例的内容时,就会造成冲突,因此,线程必须实现共享互斥,使多线程同步。

    最简单的同步是将一个方法标记为synchronized,对同一个实例来说,任一时刻只能有一个synchronized方法在执行。当一个方法正在执行某个synchronized方法时,其他线程如果想要执行这个实例的任意一个synchronized方法,都必须等待当前执行synchronized方法的线程退出此方法后,才能依次执行。

    但是,非synchronized方法不受影响,不管当前有没有执行synchronized方法,非synchronized方法都可以被多个线程同时执行。

    此外,必须注意,只有同一实例的synchronized方法同一时间只能被一个线程执行,不同实例的synchronized方法是可以并发的。例如,class A定义了synchronized方法sync(),则不同实例a1.sync()和a2.sync()可以同时由两个线程来执行。

    Java锁机制

    多线程同步的实现最终依赖锁机制。我们可以想象某一共享资源是一间屋子,每个人都是一个线程。当A希望进入房间时,他必须获得门锁,一旦A获得门锁,他进去后就立刻将门锁上,于是B,C,D...就不得不在门外等待,直到A释放锁出来后,B,C,D...中的某一人抢到了该锁(具体抢法依赖于JVM的实现,可以先到先得,也可以随机挑选),然后进屋又将门锁上。这样,任一时刻最多有一人在屋内(使用共享资源)。

    Java语言规范内置了对多线程的支持。对于Java程序来说,每一个对象实例都有一把“锁”,一旦某个线程获得了该锁,别的线程如果希望获得该锁,只能等待这个线程释放锁之后。获得锁的方法只有一个,就是synchronized关键字。例如:

    public class SharedResource {
    private int count = 0;

    public int getCount() { return count; }

    public synchronized void setCount(int count) { this.count = count; }

    }

    同步方法public synchronized void setCount(int count) { this.count = count; } 事实上相当于:

    public void setCount(int count) {
    synchronized(this) { // 在此获得this锁
    this.count = count;
    } // 在此释放this锁
    }

    红色部分表示需要同步的代码段,该区域为“危险区域”,如果两个以上的线程同时执行,会引发冲突,因此,要更改SharedResource的内部状态,必须先获得SharedResource实例的锁。

    退出synchronized块时,线程拥有的锁自动释放,于是,别的线程又可以获取该锁了。

    为了提高性能,不一定要锁定this,例如,SharedResource有两个独立变化的变量:

    public class SharedResouce {
    private int a = 0;
    private int b = 0;

    public synchronized void setA(int a) { this.a = a; }

    public synchronized void setB(int b) { this.b = b; }
    }

    若同步整个方法,则setA()的时候无法setB(),setB()时无法setA()。为了提高性能,可以使用不同对象的锁:

    public class SharedResouce {
    private int a = 0;
    private int b = 0;
    private Object sync_a = new Object();
    private Object sync_b = new Object();

    public void setA(int a) {
    synchronized(sync_a) {
    this.a = a;
    }
    }

    public synchronized void setB(int b) {
    synchronized(sync_b) {
    this.b = b;
    }
    }
    }
  • 2007-01-18 | java的线程和多线程(1)【转】

    caicai1724 发布于 2007-04-27 22:08:59

    Java多线程编程详解 1/2

    一:理解多线程

    多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。

    线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由*作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

    多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。

    多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。

    二:在Java中实现多线程

    我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!

    真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。

    那么如何提供给 Java 我们要线程执行的代码呢?让我们来看一看 Thread 类。Thread 类最重要的方法是 run() ,它为Thread 类的方法 start() 所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!

    方法一:继承 Thread 类,覆盖方法 run()

    我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。
    下面是一个例子:

    public class MyThread extends Thread { int count= 1, number; public MyThread(int num) { number = num; System.out.println("创建线程 " + number); } public void run() { while(true) { System.out.println("线程 " + number + ":计数 " + count); if(++count== 6) return; } } public static void main(String args[]) { for(int i = 0; i < 5; i++) new MyThread(i+1).start(); }}


    这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,这时如果我们又不想建立一个新的类,应该怎么办呢?

    我们不妨来探索一种新的方法:我们不创建 Thread 类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给 Thread 类的实例,有点类似回调函数。但是 Java 没有指针,我们只能传递一个包含这个方法的类的实例。那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)


    Java 提供了接口 java.lang.Runnable 来支持这种方法。

    方法二:实现 Runnable 接口


    Runnable 接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。

    但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数
    public Thread(Runnable target);来实现。

    下面是一个例子:

    public class MyThread implements Runnable { int count= 1, number; public MyThread(int num) { number = num; System.out.println("创建线程 " + number); } public void run() { while(true) { System.out.println("线程 " + number + ":计数 " + count); if(++count== 6) return; } } public static void main(String args[]) { for(int i = 0; i < 5; i++) new Thread(new MyThread(i+1)).start(); }}


    严格地说,创建 Thread 子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖 Thread 类的 run 方法,否则该线程执行的将是子类的 run 方法,而不是我

    们用以实现Runnable 接口的类的 run 方法,对此大家不妨试验一下。

    使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。


    综上所述,两种方法各有千秋,大家可以灵活运用。

    下面让我们一起来研究一下多线程使用中的一些问题。

    三:线程的四种状态

    1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。

    2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。

    3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。

    4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。

    四:线程的优先级

    线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。


    你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。


    五:线程的同步

    由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。


    由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

    1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:

    public synchronized void accessVal(int newVal);


    synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方

    法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。


    在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
    synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

    2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
    synchronized(syncObject) {
    //允许访问控制的代码\r
    }

    synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

    六:线程的阻塞

     

    为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。

    阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

     

    1. sleep() 方法:sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。

    典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

    2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。

    3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

     

    4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

          初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。

               

    上述的核心区别导致了一系列的细节上的区别。

        首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

        其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

       

    wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

       

    关于 wait() 和 notify() 方法最后再说明两点:

        第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

        第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

     

    谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

       

    以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify() 方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

     

    七:守护线程

     

        守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。

        可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程,也可以调用方法 setDaemon() 来将一个线程设为守护线程。

     

    八:线程组

     

        线程组是一个 Java 特有的概念,在 Java 中,线程组是类ThreadGroup 的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。你可以通过调用包含 ThreadGroup 类型参数的 Thread 类构造函数来指定线程属的线程组,若没有指定,则线程缺省地隶属于名为 system 的系统线程组。

        在 Java 中,除了预建的系统线程组外,所有线程组都必须显式创建。

     

        在 Java 中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在创建线程组时指定其所隶属的线程组,若没有指定,则缺省地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。

        Java 允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。

     

        Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java 的 ThreadGroup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。

     

    九:总结

     

        在这一讲中,我们一起学习了 Java 多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?

        多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。

     

        假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使用多线程才是值得的。

  • 正确使用内存(转载)

    SWeiNi 发布于 2007-05-07 14:41:37

    对于初学者来说,内存是个神秘的空间。程序的绝大部分错误,也是在于内存的使用不当造成的,而且这些错误有些都是隐藏很深的。所以,如何掌握内存的使用,通晓系统对内存的管理手段,将是软件成功的一个非常关键的因素。
           首先我们要了解内存的分配方式。一般来说,内存的分配方式有三种:
    1.从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
    2.在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
    3.从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
           以上三种分配方式,我们要注意内存生命期的问题:
    1.静态分配的区域的生命期是整个软件运行期,就是说从软件运行开始到软件终止退出。只有软件终止运行后,这块内存才会被系统回收
    2.在栈中分配的空间的生命期与这个变量所在的函数和类相关。如果是函数中定义的局部变量,那么它的生命期就是函数被调用时,如果函数运行结束,那么这块内存就会被回收。如果是类中的成员变量,则它的生命期与类实例的生命期相同
    3.在堆上分配的内存,生命期是从调用new或者malloc开始,到调用delete或者free结束。如果不掉用delete或者free。则这块空间必须到软件运行结束后才能被系统回收。
    下面我们再看看,在使用内存的过程中,我们经常发生一些什么样的错误。以及我们应该采取哪些对策。
    发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。
    常见的内存错误及其对策如下:
    1 内存分配未成功,却使用了它。
    编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。
    2 内存分配虽然成功,但是尚未初始化就引用它。
    犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。
    内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
    3 内存分配成功并且已经初始化,但操作越过了内存的边界。
    例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。
    4 忘记了释放内存,造成内存泄露。
    含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
    动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。
    5 释放了内存却继续使用它。
    有三种情况:
    1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
    2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
    3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
    综上所述,我们应该注意:
    1.用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
    2.不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
    3.避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
    4.动态内存的申请与释放必须配对,防止内存泄漏。
    5.用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”
    下面举几个经典的错误例子,大家不要犯同样的错误:
    1. 返回栈内存指针
    char *GetString(void)
    {
    char a[]="hello world";
    char *p =a;
    return p;
    }
    char* pGet = GetString();
    这段程序编译时没有错误,运行也没有错误,但是你却无法使得返回的pGet指针指向的数据是你想要的“hello world”,因为指针p的生命期是函数GetString内,运行完函数GetString后,p分配的栈空间马上被系统回收了。虽然pGet指向了p当初分配的内存地址,但是那块地址已经没有内容了。
    2.这是一个出现频率非常高的错误
    char* pChar = new char;
    ……
    int a ;
    pChar = &a;
    ……
    delete pChar;
    当然这是一个例子,具体的程序各有不同。
    这段程序有两个问题。一是pChar = &a;将导致pChar原先分配的空间无法再被获取,就象我们的丢失了朋友的电话号码一样,无法再联系这个朋友了。这就造成了内存泄漏。如果内存泄漏多了,可能导致系统的崩溃,因为可用的资源将越来越少,直到枯竭为止。第二个问题是delete pChar将导致异常发生,因为这时的pChar已经不是指向动态分配的内存了,而是指向了a分配的栈空间,而栈空间是不能使用delete来回收的,因此将导致内存异常。
  • web基础理论

    SWeiNi 发布于 2007-03-12 10:25:36

    1. http协议

    除了TCP/IP协议,http可以说是最重要,且使用最多的网络协议了。本节简要介绍一下http协议的工作原理。

    假设现在有一个html文件:http.html, 存放在Web服务器上,其URLwww.myweb.com/http.html ,文件内容为:
    HTML 代码:
    <html>
    <head>
    <title>http.html</title>
    </head>
    <body>
    hello, http
    </body>
    </html>

    现在,一个用户通过IE访问该地址,IE首先将此地址的域名通过DNS转换为一个IP地址,然后通过一个Web服务器开放的端口(默认为80,不为80需在域名后加上“:端口号,例如www.myweb.com:81)与其连接, 然后传送一个类似这样的http请求(使用flashget等下载软件下载文件时,在详细信息里也可以看到类似的信息):

    GET /http.html HTTP/1.1
    Host: www.myweb.com
    Accept: */*
    User-Agent: Mozilla/4.0 (compatible; MSIE.6.0; Windows NT 5.1)
    Pragma: no-cache
    Cache-Control: no-cache
    Connection: close
    [空行]

    请求的第一行为请求内容, 表示通过GET方法向服务器请求资源,/http.html为请求资源名称,HTTP/1.1 表示使用http协议,版本1.1。然后接下来的几行称为请求信息的标头(header),其中描述了请求的一些其他信息,比如客户端浏览器标识等。最后一个空行表示请求结束。

    Web服务器接收到该请求时,服务器检查所请求的资源是否有效,且是否有相应的权限。如果没有问题,则服务器会传回类似如下的http响应信息:

    HTTP/1.1 200 OK
    Server: Microsoft-IIS/5.0
    Date: Thursday, March 31, 2005 17:15:23 GMT
    Content-Type: text/html
    Content-Length: 88
    [空行]
    <html>
    <head>
    <title>http.html</title>
    </head>
    <body>
    hello, http
    </body>
    </html>

    其中第一行的“200”是一个状态码,表示服务器成功完成该请求,如果不成功会返回其他状态码。Content-Type表示返回的数据类型,Content-Length表示返回的数据长度。空行表示标头结束,下面则是浏览器根据请求返回的数据内容,这里是http.html的文件内容,浏览器解析html源代码,将Web页面呈现给用户,到这里就完成了一次成功的http通信。

    以上内容是Web通信的基础,就和Windows消息机制一样,你可能不会用到它,但是你必须了解它,你得知道那些高级的东西隐藏了哪些低级的内容,这样对你理解和使用那些高级的东西都有非常大的帮助:)

    2. html form

    前面的http.html文件是一个最简单的静态html页面,但作为一个Web程序,它实在是太简陋了,它不接受用户输入,永远显示一样的内容。我们需要能够根据用户输入来返回相应的数据。

    看下面的html代码:
    <html>
    <head>
    <title>form.html</title>
    </head>
    <body>
        <form method=”get”>
              <input type=”text” name=”p” />
              <input type=”submit” value=”submit” />
        </form>
    </body>
    </html>

    观察这段代码,其中有一个html form,其内容包括在<form></form>之间, 其中有一个提交按钮(<input type=”submit” value=”submit” />),当用户点击该按钮时,浏览器将html form中的所有输入提交给Web服务器,form标签的method属性指定了提交的方式,这里为get,这个get对应http请求中的GET请求方法,form中的输入均以查询字符串的方式附加在URL, 在文本框里输入一个字符串,比如“form”,然后观察浏览器的地址栏,会变成类似 http://www.myweb.com/form.html?p=form ,这是因为浏览器发出了这样的GET请求:

    GET /form.html?p=form HTTP/1.1
    ...
    ...
    [空行]

    假如<form>标签的method属性为”post”,即令浏览器使用post方法发送该请求,当使用post方法时,用户的输入并不是通过URL来传输的,而是浏览器将内容放在POST请求的标头之后发送给Web服务器的:
    POST /form.html HTTP/1.1
    ...
    ...
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 6
    [空行]
    p=form

    浏览器将用户输入使用GET或者POST方法发送给Web服务器,这个过程称为回发(Postback)”。这个概念相当重要,在Web应用程序中经常涉及到回发。

     

  • 认真把好七道关 保证Web数据库安全

    SWeiNi 发布于 2007-03-27 17:47:53

    关于网络数据库里一些商业数据被盗窃后公布于网上;公司商业网站的产品价格数据又被恶意修改……类似这样的案例,在网上搜索了一下,实在不少。其原因只有一个,就是来自网络上对Web数据库攻击。那么,在Web环境下的数据库是否能有足够的安全为企业服务呢?答案是肯定的。

      Web数据库是基于Internet/Intranet的应用系统,由于互连网开放性和通信协议的安全缺陷,以及在网络环境中数据存储和对其访问与处理的分布性特点,网上传输的数据容易受到破坏、窃取、篡改、转移和丢失。这些危害通常是对网络的攻击引起的。

      到现在,针对Web数据库的应用级入侵已经变得越来越猖獗,如SQL注入、跨站点脚本攻击和未经授权的用户访问等。

      所有这些入侵都有可能绕过前台安全系统并对数据库系统攻击。如何保证Web数据库的安全性已成为新的课题。

      第一关、对用户安全管理

      Web数据库是个极为复杂的系统,因此很难进行正确的配置和安全维护,当然,必须首先要保证的就是数据库用户的权限的安全性。

      当用户通过Web方式要对数据库中的对象(表、视图、触发器、存储过程等)进行操作时,必须通过数据库访问的身份认证。多数数据库系统还有众所周知的默认账号和密码,可支持对数据库资源的各级访问。因此,很多重要的数据库系统很可能受到威协。

      用户存取权限是指不同的用户对于不同的数据对象有不同的操作权限。存取权限由两个要素组成:数据对象和操作类型。定义一个用户的存取权限就是要定义这个用户可以在哪些数据对象上进行哪些类型的操作。

      权限分系统权限和对象权限两种。系统权限由DBA授予某些数据库用户,只有得到系统权限,才能成为数据库用户。对象权限是授予数据库用户对某些数据对象进行某些操作的权限,它既可由DBA授权,也可由数据对象的创建者授予。

      第二关、定义视图

      为不同的用户定义不同的视图,可以限制用户的访问范围。通过视图机制把需要保密的数据对无权存取这些数据的用户隐藏起来,可以对数据库提供一定程度的安全保护。实际应用中常将视图机制与授权机制结合起来使用,首先用视图机制屏蔽一部分保密数据,然后在视图上进一步进行授权。

      第三关、数据加密

      数据安全隐患无处不在。一些机密数据库、商业数据等必须防止它人非法访问、修改、拷贝。如何保证数据安全?数据加密是应用最广、成本最低廉而相对最可靠的方法。数据加密是保护数据在存储和传递过程中不被窃取或修改的有效手段。

      数据加密系统包括对系统的不同部分要选择何种加密算法、需要多高的安全级别、各算法之间如何协作等因素。在系统的不同部分要综合考虑执行效率与安全性之间的平衡。因为一般来讲安全性总是以牺牲系统效率为代价的。

      如果要在Internet上的两个客户端传递安全数据,这就要求客户端之间可以彼此判断对方的身份,传递的数据必须加密,当数据在传输中被更改时可以被发觉。

      第四关、事务管理和故障恢复

      事务管理和故障恢复主要是对付系统内发生的自然因素故障,保证数据和事务的一致性和完整性。

      故障恢复的主要措施是进行日志记录和数据复制。在网络数据库系统中,分布事务首先要分解为多个子事务到各个站点上去执行,各个服务器之间还必须采取合理的算法进行分布式并发控制和提交,以保证事务的完整性。

      事务运行的每一步结果都记录在系统日志文件中,并且对重要数据进行复制,发生故障时根据日志文件利用数据副本准确地完成事务的恢复。

      第五关、数据库备份与恢复

      计算机同其他设备一样,都可能发生故障。计算机故障的原因多种多样,包括磁盘故障、电源故障、软件故障、灾害故障以及人为破坏等。一旦发生这种情况,就可能造成数据库的数据丢失。

      因此数据库系统必须采取必要的措施,以保证发生故障时,可以恢复数据库。数据库管理系统的备份和恢复机制就是保证在数据库系统出故障时,能够将数据库系统还原到正常状态。

      加强数据备份非常重要,数据库拥有很多关键的数据,这些数据一旦遭到破坏后果不堪设想,而这往往是入侵者真正关心的东西。不少管理员在这点上作得并不好,不是备份不完全,就是备份不及时。

      数据备份需要仔细计划,制定出一个策略测试后再去实施,备份计划也需要不断地调整。

      第六关、审计追踪机制

      审计追踪机制是指系统设置相应的日志记录,特别是对数据更新、删除、修改的记录,以便日后查证。日志记录的内容可以包括操作人员的名称、使用的密码、用户的IP地址、登录时间、操作内容等。若发现系统的数据遭到破坏,可以根据日志记录追究责任,或者从日志记录中判断密码是否被盗,以便修改密码,重新分配权限,确保系统的安全。

      第七关、重点在服务器

      Web数据库的三层体系结构中,数据存放在数据库服务器中,大部分的事务处理及商业逻辑处理在应用服务器中进行,由应用服务器提出对数据库的操作请求。

      理论上,既可以通过Web页面调用业务处理程序来访问数据库,也可以绕过业务处理程序,使用一些数据库客户端工具直接登录数据库服务器,存取操作其中的数据。所以,数据库服务器的安全设置至关重要。

      用IDS(入侵检测系统)保卫数据库安全逐步普及,这种安全技术将传统的网络和操作系统级入侵探测系统(IDS)概念应用于数据库。应用IDS提供主动的、针对SQL的保护和监视,可以保护预先包装或自行开发的Web应用。

  • [转]什么是shell

    Joan2005 发布于 2007-04-30 09:07:12

        操作系统与外部最主要的接口就叫做shell。shell是操作系统最外面的一层。shell管理你与操作系统之间的交互:等待你输入,向操作系统解释你的输入,并且处理各种各样的操作系统的输出结果。
      shell提供了你与操作系统之间通讯的方式。这种通讯可以以交互方式(从键盘输入,并且可以立即得到响应),或者以shell scrīpt(非交互)方式执行。shell scrīpt是放在文件中的一串shell和操作系统命令,它们可以被重复使用。本质上,shell scrīpt是命令行命令简单的组合到一个文件里面。
      Shell基本上是一个命令解释器,类似于DOS下的command.com。它接收用户命令(如ls等),然后调用相应的应用程序。较为通用的shell有标准的Bourne shell (sh)和C shell (csh)。

    交互式shell和非交互式shell
    交互式模式就是shell等待你的输入,并且执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当你签退后,shell也终止了。
    shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。

    shell的类型
    在UNIX中主要有两大类shell
    Bourne shell (包括 sh, ksh, and bash)
    Bourne shell ( sh)
    Korn shell ( ksh)
    Bourne Again shell ( bash)
    POSIX shell ( sh)
    C shell (包括 csh and tcsh)
    C shell ( csh)
    TENEX/TOPS C shell ( tcsh)

    Bourne Shell
    最初的UNIX shell是由Stephen R. Bourne于20世纪70年代中期在新泽西的AT&T贝尔实验室编写的,这就是Bourne shell。Bourne shell 是一个交换式的命令解释器和命令编程语言。Bourne shell 可以运行为login shell或者login shell的子shell(subshell)。只有login命令可以调用Bourne shell作为一个login shell。此时,shell先读取/etc/profile文件和$HOME/.profile文件。/etc/profile文件为所有的用户定制环境,$HOME/.profile文件为本用户定制环境。最后,shell会等待读取你的输入。

    C Shell
    Bill Joy于20世纪80年代早期,在Berkeley的加利福尼亚大学开发了C shell。它主要是为了让用户更容易的使用交互式功能,并把ALGOL风格的语法结构变成了C语言风格。它新增了命令历史、别名、文件名替换、作业控制等功能。

    Korn Shell
    有很长一段时间,只有两类shell供人们选择,Bourne shell用来编程,C shell用来交互。为了改变这种状况,AT&T的bell实验室David Korn开发了Korn shell。ksh结合了所有的C shell的交互式特性,并融入了Bourne shell的语法。因此,Korn shell广受用户的欢迎。它还新增了数学计算,进程协作(coprocess)、行内编辑(inline editing)等功能。Korn Shell 是一个交互式的命令解释器和命令编程语言.它符合POSIX——一个操作系统的国际标准.POSIX不是一个操作系统,而是一个目标在于应用程序的移植性的标准——在源程序一级跨越多种平台。

    Bourne Again Shell (bash)
    bash是GNU计划的一部分,用来替代Bourne shell。它用于基于GNU的系统如Linux.大多数的Linux(Red Hat, Slackware, Caldera)都以bash作为缺省的shell,并且运行sh时,其实调用的是bash。

    POSIX Shell
    POSIX shell 是Korn shell的一个变种. 当前提供POSIX shell的最大卖主是Hewlett-Packard。在HP-UX 11.0 , POSIX shell 就是/bin/sh,而bsh是/usr/old/bin/sh.

    各主要操作系统下缺省的shell:
    AIX 下是Korn Shell.
    Solaris和FreeBSD缺省的是Bourne shell.
    HP-UX缺省的是POSIX shell.
    Linux是Bourne Again shell.

    【TIP】
    #!/usr/bin/sh的用途
    shell scrīpt的第一行一般都是#!/usr/bin/sh或#!/usr/bin/ksh等,它的用途就是指出本脚本是用的哪种shell写的,执行时系统应该用哪种shell来解释执行它。

    附:LINUX系统的shell原理

      Linux系统的shell作为操作系统的外壳,为用户提供使用操作系统的接口。它是命令语言、命令解释程序及程序设计语言的统称。

      shell是用户和Linux内核之间的接口程序,如果把Linux内核想象成一个球体的中心,shell就是围绕内核的外层。当从shell或其他程序向Linux传递命令时,内核会做出相应的反应。   shell是一个命令语言解释器,它拥有自己内建的shell命令集,shell也能被系统中其他应用程序所调用。用户在提示符下输入的命令都由shell先解释然后传给Linux核心。

      有一些命令,比如改变工作目录命令cd,是包含在shell内部的。还有一些命令,例如拷贝命令cp和移动命令rm,是存在于文件系统中某个目录下的单独的程序。对用户而言,不必关心一个命令是建立在shell内部还是一个单独的程序。

      shell首先检查命令是否是内部命令,若不是再检查是否是一个应用程序(这里的应用程序可以是Linux本身的实用程序,如ls和rm,也可以是购买的商业程序,如xv,或者是自由软件,如emacs)。然后shell在搜索路径里寻找这些应用程序(搜索路径就是一个能找到可执行程序的目录列表)。如果键入的命令不是一个内部命令并且在路径里没有找到这个可执行文件,将会显示一条错误信息。如果能够成功找到命令,该内部命令或应用程序将被分解为系统调用并传给Linux内核。

      shell的另一个重要特性是它自身就是一个解释型的程序设计语言,shell程序设计语言支持绝大多数在高级语言中能见到的程序元素,如函数、变量、数组和程序控制结构。shell编程语言简单易学,任何在提示符中能键入的命令都能放到一个可执行的shell程序中。

      当普通用户成功登录,系统将执行一个称为shell的程序。正是shell进程提供了命令行提示符。作为默认值(TurboLinux系统默认的shell是BASH),对普通用户用“$”作提示符,对超级用户(root)用“#”作提示符。

      一旦出现了shell提示符,就可以键入命令名称及命令所需要的参数。shell将执行这些命令。如果一条命令花费了很长的时间来运行,或者在屏幕上产生了大量的输出,可以从键盘上按ctrl+c发出中断信号来中断它(在正常结束之前,中止它的执行)。

      当用户准备结束登录对话进程时,可以键入logout命令、exit命令或文件结束符(EOF)(按ctrl+d实现),结束登录。

      我们来实习一下shell是如何工作的。

      $ make work

      make:***No rule to make target ‘work’. Stop.

      $

      注释:make是系统中一个命令的名字,后面跟着命令参数。在接收到这个命令后,shell便执行它。本例中,由于输入的命令参数不正确,系统返回信息后停止该命令的执行。

      在例子中,shell会寻找名为make的程序,并以work为参数执行它。make是一个经常被用来编译大程序的程序,它以参数作为目标来进行编译。在“make work”中,make编译的目标是work。因为make找不到以work为名字的目标,它便给出错误信息表示运行失败,用户又回到系统提示符下。

      另外,用户键入有关命令行后,如果shell找不到以其中的命令名为名字的程序,就会给出错误信息。例如,如果用户键入:

      $ myprog

      bash:myprog:command not found

      $

      可以看到,用户得到了一个没有找到该命令的错误信息。用户敲错命令后,系统一般会给出这样的错误信息.
  • 10058错误

    Spark.lee 发布于 2007-05-09 10:31:35

    有时候在录制回放的时候,第一次回放会很成功,
    但是如果你把他的迭代次数设置超过2的时候,
    就会出现10058这样的错误;

    lrs_create_socket
           ("socket4", "TCP", "RemoteHost=60.191.70.75:9000",    LrsLastArg);

    lrs_send("socket4", "buf0", LrsLastArg);-----
    迭代2次回放出现问题

    lrs_receive("socket4", "buf1", LrsLastArg);


    10058的错误描述是这样的:
    Can't send after socket shutdown

    就是说该端口已经被挂起了,无法向他发送请求了!

    在这里我们只要修改一下脚本就可以了,出现这个问题其脚本中应该有这样的一句:

    lrs_disable_socket("socket4", DISABLE_SEND);

    语句解释:Disables an operation for a socket

    int lrs_disable_socket ( char *s_desc, int operation );

    s_desc
    A descrīptor identifying a socket.
    operation
    The operation to disable: SEND, RECEIVE, SEND-RECEIVE.

    The lrs_disable_socket function disables an operation for a socket. The operation to disable is specified with the operation argument. To disable receiving data, pass DISABLE_RECV as the operation . To disable sending data, pass DISABLE_SEND. To disable all activity, pass DISABLE_SEND_RECV.

    我们把她给注销掉就可以了!!!

    我们也可以用别的语句:  lrs_close_socket("socket4");


我的栏目

数据统计

  • 访问量: 24173
  • 日志数: 35
  • 图片数: 3
  • 建立时间: 2007-05-08
  • 更新时间: 2008-05-26

RSS订阅

Open Toolbar