JDBC部分
终端进入mysql: mysql -u root回车
终端进入oracle: telnet 192.168.0.23  然后sqlplus openlab/open123
MySQL常用命令: show databases; use tableName;  show tables;




一、概述JDBC
    JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。
    从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。


    JDBC API 使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。
    ODBC:基于C语言的数据库访问接口。
    JDBC:是Java版的ODBC。
    JDBC 特性:高度的一致性、简单性(常用的接口只有4、5个)。


驱动程序按照工作方式分为四类:
    1、JDBC-ODBC bridge + ODBC 驱动
       JDBC-ODBC bridge桥驱动将JDBC调用翻译成ODBC调用,再由ODBC驱动翻译成访问数据库命令。
       优点:可以利用现存的ODBC数据源来访问数据库。
       缺点:从效率和安全性的角度来说的比较差。不适合用于实际项目。
    2、基于本地API的部分Java驱动
       我们应用程序通过本地协议跟数据库打交道。然后将数据库执行的结果通过驱动程序中的Java部分返回给客户端程序。
       优点:效率较高。
       缺点:安全性较差。
    3、纯Java的网络驱动
       (中间协议)            (本地协议)
       app    JDBC     纯Java                 中间服务器               DB
       缺点:两段通信,效率比较差
       优点:安全信较好
    4、纯Java本地协议:通过本地协议用纯Java直接访问数据库。
       特点:效率高,安全性好。


二、JDBC 编程的步骤
        import java.sql.*;
    0.参数化
        String driverName = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/test"; //协议;库或服务器名称;服务器IP,端口
        String username = "root";
        String password="";
            /* Oracle的连接
            String driverName = "oracle.jdbc.driver.OracleDriver";
            String url = "jdbc:oracle:thin:@192.168.0.23:1521:ora10g"; 
            String username = "openlab";
            String password="open123";*/
        //以下这些都需要写在有异常的代码块里,所以需要提取出来。
        Connection conn = null;
        Statement stmt = null; //建议用PreparedStatement
        ResultSet rs = null;
    1.加载和注册数据库驱动
        Class.forName(driverName);//自动注册;需要把驱动的jar包导进来;需处理异常
            /*方法二:实例化具体的Driver驱动,这写法一般不用(不能参数化驱动名,不够灵活)
            Driver driver = new com.mysql.jdbc.Driver();
            DriverManager.registerDriver(driver); //将驱动交于DriverManager托管*/
            /*方法三:Dos运行时,java -Djdbc.drives = oracle.jdbc.driver.OracleDriver; --可多个 */
    2.连接数据库
        conn = DriverManager.getConnection(url, username, password);//需处理异常
        //Connection返回数据库连接,如:“com.mysql.jdbc.Connection@1ffb8dc”;连接不成功则返回 null
    3.创建Statement对象 //为了类型安全和批量更新的效率,改用PreparedStatement
        stmt = conn.createStatement();//需处理异常
        //返回其生成结果的对象"oracle.jdbc.driver.OracleStatement@198dfaf"
    4.操作数据库,执行SQL语句
        String sql = "select * from tableName";//SQL语句里不需要写分号
        rs = stmt.executeQuery(sql); //executeQuery(sqlString) 查询 返回查询结果集
            /* String sql = "insert into tableName values(?,?)"; // ?占位符
            int number = stmt.executeUpdate(sql);//更新,再返回int(更新、修改影响的条数) */
    5.处理数据(游标) 
        StringBuffer sb = new StringBuffer(); //缓存;用它可提高读取速度。当然,不用也可以。
        ResultSetMetaData md = rs.getMetaData(); //ResultSetMetaData可获取列的类型和属性信息
        int col = md.getColumnCount(); //获取列的数目
        while(rs.next()){ //rs.next()使游标下移一位,返回boolean,没有下一个结果则返回false
            for(int i=1; i<=col;i++){ // index(JDBC 的下标从1开始)  
                sb.append(md.getColumnName(i)+"="+rs.getString(i)+"  ");
            } sb.append("/n");
        }System.out.println(sb);
            //1.游标的初始位置在第一条记录的前面,使第一次调用next()后,刚好拿到第一个结果。
            //2.游标的最终位置在最后一条记录的后面(结果集的前面和后面留空,真正存在) 
    6.释放资源,断开与数据库的连接
        //先判断是否有引用资源,再释放(释放空资源会抛异常);注意顺序
        if(rs!=null)try{rs.close();}catch(Exception e){e.printStackTrace();}
        if(stmt!=null)try{stmt.close();}catch(Exception e){e.printStackTrace();}
        if(conn!=null)try{conn.close();}catch(Exception e){e.printStackTrace();}
        //这些异常没法处理,处理只为方便调试。所以这些异常处理也只是打印。
        /*要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,
        因为ResultSet需要Statement和Connection连接时才可以用的;Statement也需要Connection才可用;
        结束Statement之后有可能其它的Statement还需要连接,因此不能先关闭Connection。ResultSet同理。*/


    步骤 1、2、6 每次都一样,可以重构。
       因为加载驱动是个一次性工作,所以可以采用静态初始化块来加载驱动;
       连接数据库的方法应该自己负责,获取数据库连接信息和驱动的信息,并处理相关异常;
       释放数据库资源的方法要考虑到ResultSet、Statement、Connection的不同情况,并处理相关异常。


三、JDBC几个重要接口重点讲解
    在JDBC 中包括了两个包:java.sql和javax.sql。
        ① java.sql   基本功能。
           这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查询等。
           同时也有一些高级的处理,比如批处理更新、事务隔离和可滚动结果集等。
        ② javax.sql  扩展功能。
           它主要为数据库方面的高级操作提供了接口和类。
           如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。


        API                        说明
    Connection            与特定数据库的连接(会话)。能够通过getMetaData方法获得数据库提供的信息、
                          所支持的SQL语法、存储过程和此连接的功能等信息。代表了数据库。
    Driver                每个驱动程序类必需实现的接口,每个数据库驱动程序也都应该提供一个实现Driver接口的类。 
    DriverManager(Class)  管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载
                          在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。
    Statement             用于执行静态SQL语句并返回其生成结果的对象。
    PreparedStatement     继承Statement接口,表示预编译的SQL语句的对象,SQL语句被预编译并且存储
                          在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。
    CallableStatement     用来访问数据库中的存储过程。它提供了一些方法来指定语句所使用的输入/输出参数。
    ResultSet             指的是查询返回的数据库结果集。
    ResultSetMetaData     可用于获取关于ResultSet对象中列的类型和属性信息的对象。
    注:除了标出的Class,其它均为接口。每个都是“java.sql.”包下的。




    1. Statement  —— SQL语句执行接口
       代表了一个数据库的状态,在向数据库发送相应的SQL语句时,都需要创建Statement接口或PreparedStatement接口。
       在具体应用中,Statement主要用于操作不带参数(可以直接运行)的SQL语句,比如删除语句、添加或更新。


    2. PreparedStatement:预编译的Statement
        第一步:通过连接获得PreparedStatement对象,用带占位符(?)的sql语句构造。
            PreparedStatement  pstm = con.preparedStatement(“select * from test where id=?”);
        第二步:设置参数 
            pstm.setString(1,“ganbin”);//第一个字段是“ganbin”;需一个个字段写
        第三步:执行sql语句
            Rs  =  pstm.excuteQuery();
        statement发送完整的Sql语句到数据库不是直接执行而是由数据库先编译,再运行。每次都需要编译。
        而PreparedStatement是先发送带参数的Sql语句,由数据库先编译,再发送一组组参数值。(同构时不需重复编译) 
        如果是同构的sql语句,PreparedStatement的效率要比statement高。而对于异构的sql则两者效率差不多。
        一般都用PreparedStatement代替Statement,因为它是类型安全的。Statement对参数类型不作检查,故不够安全。
            同构:两个Sql语句可编译部分是相同的,只有参数值不同。
            异构:整个sql语句的格式是不同的
        注意点:1、使用预编译的Statement编译多条Sql语句一次执行
              2、可以跨数据库使用,编写通用程序
              3、能用预编译时尽量用预编译
              4、如果第二个SQL语句与前一个是异构的,需要再次编译“ps = con.prepareStatement(sql);“


    3. ResultSet —— 结果集操作接口
       ResultSet接口是查询结果集接口,它对返回的结果集进行处理。ResultSet是程序员进行JDBC操作的必需接口。


    4. ResultSetMetaData —— 元数据操作接口
       ResultSetMetaData是对元数据进行操作的接口,可以实现很多高级功能。
       Hibernate运行数据库的操作,大部分都是通过此接口。可以认为,此接口是SQL查询语言的一种反射机制。
       ResultSetMetaData接口可以通过数组的形式,遍历数据库的各个字段的属性,对于开发者来说,此机制的意义重大。


       JDBC通过元数据(MetaData)来获得具体的表的相关信息,例如,可以查询数据库中有哪些表,表有哪些字段,以及字段的
       属性等。MetaData中通过一系列getXXX将这些信息返回给我们。      
       数据库元数据 Database MetaData 用connection.getMetaData()获得;包含了关于数据库整体元数据信息。
       结果集元数据 ResultSet MetaData 用resultSet.getMetaData()获得;比较重要的是获得表的列名,列数等信息。
                结果集元数据对象:ResultSetMetaData meta = rs.getMetaData();
                字段个数:meta.getColomnCount();
                字段名字:meta.getColumnName();
                字段JDBC类型:meta.getColumnType(); 
                字段数据库类型:meta.getColumnTypeName();


       数据库元数据对象:DatabaseMetaData dbmd = con.getMetaData();
                数据库名:dbmd.getDatabaseProductName();
                数据库版本号:dbmd.getDatabaseProductVersion();
                数据库驱动名:dbmd.getDriverName();
                数据库驱动版本号:dbmd.getDriverVersion();
                数据库Url:dbmd.getURL();
                该连接的登陆名:dbmd.getUserName();


四、JDBC 中使用Transaction编程(事务编程) 
     1. 事务是具备以下特征(ACID)的工作单元:
        原子性(Atomicity)—— 如果因故障而中断,则所有结果均被撤消;
        一致性(Consistency)—— 事务的结果保留不变;
        孤立性(Isolation)—— 中间状态对其它事务是不可见的;
        持久性(Durability)—— 已完成的事务结果上持久的。
        原子操作,也就是不可分割的操作,必须一起成功一起失败。


     2. 事务处理三步曲:(事务是一个边界) 
        ① connection.setAutoCommit(false); //把自动提交关闭;在创建Statement对象之前。
        ② 正常的DB操作                       //若有一条SQL语句失败了,自动回滚
        ③ connection.commit()              //主动提交
        和 connection.rollback()            //主动回滚,一般写在catch语句里,而前三个都写在try语句里


/*********事务的代码片段:*************/
try{
    con.setAutoCommit(false);   //step① 把自动提交关闭
    Statement stm = con.createStatement();    //step② 正常的DB操作
    stm.executeUpdate("insert into person(id, name, age) values(520, 'X-Man', 18)");
    stm.executeUpdate("insert into Person(id, name, age) values(521, 'Super', 19)");
    con.commit();               //step③ 成功主动提交
} catch(SQLException e){
    try{con.rollback();        //如果中途发生异常,则roolback;这语句也会抛异常
    }catch(Exception e){e.printStackTrace();}    //step③ 失败则主动回滚
/************************************/


     3.JDBC事务并发产生的问题和事务隔离级别(难,建议用例子学习) 
     JDBC事务并发产生的问题:
        ① 脏读(Dirty Reads) 一个事务读取了另一个并行事务还未提交的数据。(产生原因:读-写)
        ② 不可重复读(UnPrpeatable Read)一个事务前后两次读取数据时,得到的数据不一致,被另一个已提交的事务修改。
        ③ 幻读(Phantom Read) 一个事务再次查询,记录中的量变化了。(仅对统计有影响)
     为了避免以上三种情况的出现,则采用事务隔离级别:
        TRANSACTION_NONE                不使用事务(不可能用,只是理论的)
        TRANSACTION_READ_UNCOMMITTED    可以读取未提交数据(允许脏读,也不可能)
        TRANSACTION_READ_COMMITTED      只读提交的数据:可防止脏读;大部分数据库的默认隔离级别
        TRANSACTION_REPEATABLE_READ     重复读取;只可以避免脏读
        TRANSACTION_SERIALIZABLE        事务串行化:可以避免脏读,重复读取和幻读,但会降低数据库效率(最常用)
     以上的五个事务隔离级别都是在Connection类中定义的静态常量。隔离级别越高,数据越安全,并发能力越差。
     使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。
        比如:con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);


五、JDBC 2.0新特性:
    1、 Scrollability 结果集可滚动
        滚动:可双向支持绝对与相对滚动,对结果集可进行多次迭代。
            Con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
            //上句的 SCROLL 再到 CONCUR;不可以写反,编译器无法检测到,因为他们都是int类型的。
        TYPE_FORWARD_ONLY:(单向,一般不用)该常量指示指针只能向前移动的 ResultSet 对象的类型。
        TYPE_SCROOL_INSENSITIVE:(双向、不敏感)可滚动但不受其他更改影响的 ResultSet 对象的类型。
        TYPE_SCROLL_SENSITIVE:(双向、敏感)该常量指示可滚动并且通常受其他的更改影响的 ResultSet 对象的类型。
        CONCUR_READ_ONLY:(只读)该常量指示只可以读取的 ResultSet 对象的并发模式。
        CONCUR_UPDATABLE:(可更新)该常量指示可以更新的 ResultSet 对象的并发模式。


        绝对定位:boolean absolute(int row)将游标移动到指定位置。(row指记录的序号,没有这位置则返回false) 
                void afterLast() 将游标指向最后一条记录的后一位(有这位置,但记录为空)。
                void beforeFirst()将游标指向最前一条记录的前一位。
                boolean first()将游标移动到结果集最前。
                boolean last()将游标移动到结果集末尾。
        相对定位:boolean next()指向下一个。
                boolean previous()指向前一个。
                boolean relative(int) 向next()方向移动 int 位(int 可负)。
        判位函数:boolean isAfterLast() 是否在最后一条的后一位。
                boolean isBeforeFirst() 是否最前一条记录的前一位。
                boolean isFirst() 是否最前位置。
                boolean isLast() 是否最后位置。


    2、 Updatability 结果集可更新。(主要应用于桌面应用)
        更新:rs.updateString(“name”,”Tony”);//前面一个是字段的名字或者序号
        rs.updateInt(1,”122323”);修改
        rs.deleteRow();删除
        rs.updateRow();
     注:只有在必要的时候(如桌面应用)才用结果集更新数据库,因为使用结果集更新数据库效率低下。
        可更新结果集还要看数据库驱动程序是否支持,如Oracle就支持,MySql不支持。
        并且只能针对一张表做结果集更新(不能子查询)。而且不能有join操作。
        必须有主健,必须把所有非空没有默认值的字段查出。
        处理可更新结果集时不能用select *来查询语句,必须指出具体要查询的字段。(不能使用通配符)


    3、 Batch updates 可批量更新。
        将多组对数据库的更新操作发送到数据库统一执行(数据库支持并发执行操作),以提高效率。
        主要是通过减少数据(Sql语句或参数)在网络上传输的次数来节省时间。//数据有两组以上都应该用这批量更新


        (1)对于Statement的批量更新处理:
            stm.addBatch(Sql);
            int[] ResultSet=stm.executeBatch();   


        (2)对于PreparedStatement的批量跟新处理
             pstm.setInt(1,12);pstm.setString(2,”gaga”);……..
             pstm.addBatch();
             if(i%100==0) int[] ResultSet=pstm.executeBatch();//每个包50~200组数据,包太大也影响速度


        注:int[] 中每一个数表示该Sql语句影响到的记录条数。
        PreparedStatement的更新操作比Statement的更新操作多了一个设置参数的过程。


六、SQL 3.0规范中的新类型:
        Blob,大的二进制数据文件,最多存储2G。
        Clob,大文本文件对象,最多存储2G。
    在使用上述大对象的时候,在使用JDBC插入记录时要先插入一个空的占位对象,
        "insert into tableName valuse(?,?,empty_blob())"//在数据库制造一个空的blob对象字段值
        然后使用"select blobdata from t_blob where id = ? for update"对获得的大对象进行实际的写入操作
        Blod通过getBinaryOutputStream()方法获取流进行写入。
        getBinaryStream()方法获得流来获取Blob中存储的数据。 
    Clob的操作也和、Blob相同。
        getAsciiStream()用于读取存储的文本对象,getAsciiOutputStream()方法之获得流用来向文件对象写入的。


    BLOB与CLOB的异同点:
        ① 都可以存储大量超长的数据;
        ② BLOB (Binary Large Object) 以二进制格式保存,特别适合保存图片、视频文件、音频文件、程序文件等;
        ③ CLOB (Character Large Object) 以Character格式保存于数据库中,适合保存比较长的文本文件。


七、JDBC 2.0扩展
    (一)JNDI(命名目录服务器):
        定义:是Java的命名目录服务器。而JDBC是Java的数据库访问接口。
            跟JDBC是平级的关系,是两个独立的JNDI;JDBC存储的数据都是以二维表的接口来大规模存储数据。
            而JNDI存储的是差异性比较大的Java对象。JDBC取数据时用Sql语言访问数据。JNDI只用lookup和bind读写
            JDBC API依赖于驱动程序,而JNDI依赖于服务提供者。
            JDBC一般把数据存储到关系型数据库,而JNDI一般把数据存储到小型数据库、文件、甚至是注册表中。
            JNDI相当于一个电话本。允许程序将一个对象和一个命名绑定到目录树上。
               (JNDI的方法是在javax.naming包下,接口是Context实现类是InitialContext)


        bind(String name, Object obj) 将名称绑定到对象资源,建立指定的字符串和对象资源的关联
        lookup(String name) ,通过指定的字符串获得先前绑定的资源


        /*********以下是将资源和JNDI命名绑定的方法**************/
        public static void bind(String context, Object obj) throws NamingException{
            Properties pro = new Properties();
            //Weblogic的JNDI服务器参数
        pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");
            Context ctx = new InitialContext(pro);//连接服务器
            ctx.bind(context, obj);//存储
        }


    (二)DataSourse(数据源)
        1、包含了连接数据库所需的信息,可以通过数据源获得数据库连接,
           有时由于某些连接数据库的信息会变更,所以经常使用包含数据库连接信息的数据源。
        2、一个标准的数据库连接工厂,作为DriverManager的替代项,保存与数据库相关的信息,
           可以将数据库的连接信息放在一个共享的空间进行提取,不用在本地安装。
           支持JNDI的绑定,支持连接池,支持分布式服务,用getConnection方法可获得与数据库的连接。
           数据源应该由管理员创建(目的是为了保证数据库的安全)。所以数据源对象一般放在JNDI服务器中。


        /*********通过JNDI获得绑定的资源**************/
        public static Object lookup(String context) throws NamingException{
            Properties pro = new Properties();
            //Weblogic的JNDI服务器参数
        pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
            pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");
            Context ctx = new InitialContext(pro);
            return ctx.lookup(context);//查找;通过指定的字符串获得先前绑定的资源。
        }


    (三)连接池:
        在内存中用来保存一个个数据库连接的对象。
        访问数据库时,建立连接和拆连接需要花费较长时间,通过以连接池直连的方式获取连接,不需要注册驱动程序,可以大量
        的节省销毁和创建连接的资源消耗,提高访问数据库的效率。
     注:通过连接池获得的Connection,当执行con.close()时,不是关闭连接,而是表示将连接释放回连接池。
         连接池是一个很复杂的算法,由服务器厂商实现。


    (四)分布式的事务管理器JTA
        分布式事务是通过多个异地数据库执行一组相关的操作,要保证原子操作的不可分,
        也不用再自己写commit,和rollback,全部都交给中间服务器(TM)来处理。
        (两阶段提交),也就是在中间服务器发送sql语句等待数据库回应,都回应操作成功才提交,否则同时回滚。
                         ----------------   commit
            con1 ------->| TM(事务管理器) | -----------> DB1
            con2 ------->|    commit    | -----------> DB2
                         ----------------   commit
        1、regester  
        2、TM->execute()  
        3、commit->TM 
        4、TM->commit->DB


    (五)RowSet
                行集,这是一个JavaBean(事件机制),它增强了ResultSet的功能,包装了Connection、Statement、
        ResultSet、DriverManage。通过RowSet可以获得数据源,设置隔离级别,也可以发送查寻语句,也实现了离线的操
        作遍历,RowSet也支持预编译的Statement。是结果集的子接口,为快速开发而设(目前还不够成熟,没人用)。
        RowSet中的方法大致上和ResultSet相同,当需要使用时请查阅JAVA API参考文档。


八、JDBC应用的分层(DAO)
    分层就是对功能的隔离,降低层与层之间的耦合性。


    软件的分层初步:
        JSP          Struts
      View(界面) --> Controlle --> Atio ---> Service/Biz --> DAO ---->  DB
       重新封装        可复用       封装信息      懂业务逻辑    数据访问层    数据层
                                  调业务       无技术难度    与业务无关
    谁依赖谁就看谁调用谁。
    软件的分层设计,便于任务的划分、降低层间的耦合。
    结合PMS的设计方法,思考这样分层的好处。
    并且,使代码尽量减少重复,可复用性好,扩展余地加大,而且尽量减少硬编码。
    需求:实现对Person类的数据库持久化基本操作(CRUD)。


    BS架构和CS架构:
    C-S架构:两层体系结构,主要应用于局域网中。
    B-S架构:三层体系结构,表现层+业务逻辑层+数据存储层
         注:层面越多,软件越复杂,但更灵活。分层是必须的但是要有个度。
            层次一但确定,数据必须按层访问,不能跨层访问。
            层与层之间最好时单向依赖(单向调用)。


    纵向划分:按功能划分。分成三层体系结构(也有两层的)。
    横向划分:按抽象划分。分成抽象部分和实现部分。


 


/***以下老师没讲的内容******/
一、JDBC异常处理:
   JDBC中,和异常相关的两个类是SQLException和SQLWarning。
      1.SQLException类:用来处理较为严重的异常情况。
        比如:① 传输的SQL语句语法的错误;
             ② JDBC程序连接断开;
             ③ SQL语句中使用了错误的函数。
        SQLException提供以下方法:
          getNextException() —— 用来返回异常栈中的下一个相关异常;
          getErrorCode() —— 用来返回代表异常的整数代码 (error code);
          getMessage() —— 用来返回异常的描述信息 (error message)。


      2.SQLWarning类:用来处理不太严重的异常情况,也就是一些警告性的异常。
        其提供的方法和使用与SQLException基本相似。


    结合异常的两种处理方式,明确何时采用哪种。
      A. throws        处理不了,或者要让调用者知道;
      B. try … catch   能自行处理,就进行异常处理。


二、JavaBean的定义:
    1、是一个普通的Java类
    2、在结构上没有预先的规定,不需要容器,不需要继承类或实现接口
    3、要求必须放在包中,要求实现Serializable接口
    4、要求有一个无参的构造方法.
    5、属性的类型必须保持唯一,get方法返回值必须和set方法参数类型一致
    6、对每个属性要有对应的get和set方法。注:隐藏属性可以没有
    7、可以有外观作为显示控制,事件机制。


三、SQL数据类型及其相应的Java数据类型
   SQL数据类型             Java数据类型              说明
  ---------------------------------------------------------------------------------------
   INTEGER或者INT            int               通常是个32位整数
   SMALLINT                 short             通常是个16位整数
   NUMBER(m,n)            Java.sql.Numeric    合计位数是m的定点十进制数,小数后面有n位数
   DECIMAL(m,n)              同上
   DEC(m,n)               Java.sql.Numeric    合计位数是m的定点十进制数,小数后面有n位数
   FLOAT(n)                  double           运算精度为n位二进制数的浮点数
   REAL                      float            通常是32位浮点数
   DOUBLE                    double           通常是64位浮点数
   CHAR(n)                   String           长度为n的固定长度字符串
   CHARACTER(n)              同上
   VARCHAR(n)                String           最大长度为n的可变长度字符串
   BOOLEAN                   boolean          布尔值
   DATE                   Java.sql.Date       根据具体设备而实现的日历日期
   TIME                   Java.sql.Time       根据具体设备而实现的时戳
   TIMESTAMP              Java.sql.Timestamp  根据具体设备而实现的当日日期和时间
   BLOB                   Java.sql.Blob       二进制大型对象
   CLOB                   Java.sql.Clob       字符大型对象
   ARRAY                  Java.sql.Array


四、 面向对象的数据库设计
    类的关联,继承在数据库中的体现:
      类定义―――>表定义
      类属性―――>表字段
      类关系―――>表关系
      对  象―――>表记录
    注: Oid(对象id)―――>业务无关
        在数据库中每一条记录都对应一个唯一的id;
        Id通常是用来表示记录的唯一性的,通常会使用业务无关的数字类型
        字段的个数不会影响数据库的性能,表则越多性能越低。


    (一)类继承关系对应表,
        1、 为每一个类建一张表。通过父类的Oid来体现继承关系。
            特点:在子类表中引用父类表的主建作为自己的外建。
            优点:方便查询。属性没有冗余。支持多态。
            缺点:表多,读写效率低。生成报表比较麻烦。
        2、 为每一个具体实现类建一个表
            特点:父类的属性被分配到每一个子类表中。
            优点:报表比较容易
            缺点:如果父类发生改变会引起所有子类表随之更改。并且不支持多态。数据有少量冗余。
        3、 所有的类在一张表中体现,加一个类型辨别字段
            特点:效率高,查询不方便,用于字段不多时。
            优点:支持多态,生成报表很简单。
            缺点:如果任何一个类发生变化,必须改表。字段多,难以维护。


    (二)类关联关系对应表
        1、 一对一关联,类关系对应成表时有两种做法:
            一是引用主键,也就是一方引用另一方的主键既作为外键有作为自身的主键。
            二是外键引用,一方引用另一方的主键作为自身的外键,并且自己拥有主键。
        2、 一对多关联,也就是多端引用一端的主键当作外键,多端自身拥有主键。
        3、 多对多关系,多对多关系是通过中间表来实现的,中间表引用两表的主键当作联合主键,就可以实现多对多关联。


J2ee | 评论:0 | Trackbacks:0 | 阅读:68


07. Xml note
Submitted by cnetsa on 2009, July 29, 2:22 PM




XML(eXtensible Markup Language)是万维网联盟(World Wide Web Consortium W3C)定义的一种可扩展标志语言。
    可扩展性指允许用户按照XML规则自定义标记(tags 标签)。
强项:轻松表达多层结构的数据;可扩展。
优点:平台无关,语言无关。设计目标是描述数据并集中于数据的内容,与显示分离。
提醒:不能用XML来直接写网页。即便是包含了XML数据,依然要转换成HTML格式才能在浏览器上显示。


语法规则:
    XML文件有且仅有一个根标记,其他标记必须封装在根标记中,文件的标记必须形成树状结构。
    大小写敏感。
    标记的属性必须用""或''括起来。


XML细节:
一、 声明
    大多数XML文档以XML声明作为开始,它向解析器提供了关于文档的基本信息。
    建议使用XML声明,但它不是必需的。如果有的话,那么它一定是文档的第一行内容。
      如:<?xml  version="1.0"  encoding="UTF-8" standalone="no"?>
    声明最多可以包含三个名称-值对(许多人称它们为属性,尽管在技术上它们并不是)。
      <?xml 问号与xml之间不能有空格。
    1)version 是使用的XML 版本:1.0, 1.1
    2)encoding 是该文档所使用的字符集。该声明中引用的ISO-8859-1 字符集包括大多数西欧语言用到的所有字符。
      默认字符在UTF-8字符集中,这是一个几乎支持世界上所有语言的字符和象形文字的Unicode 标准。
    3)standalone(可以是yes 或no)定义了是否孤立处理该文档。
      如果XML文档没有引用任何其它文件,则可以指定 standalone="yes"。
      如果XML文档引用其它描述该文档可以包含什么的文件(如DTD),则 standalone="no"。默认值为"no"


二、 标记
    左尖括号“<“和右尖括号“>“之间的文本
      1. 在<  >中的称为开始标记;在</  >中的称为结束标记
      2. 空标记:不包含元素的标记。空标签必须以“/>”结束。格式: <空标记的名称/> <空标记的名称 属性列表/>
    注意:
      除空标记外,标签必须成对:有始有终。所有的开始标签和结束标签必须匹配。
      在标记符“<“和"标记的名称"之间不能含有空格。在标记符"/>"前面可以有空格或回行。
      标签必须嵌套正确。
    XML标记必须遵循下面的命名规则:
     1.名字中可以包含字母、数字以及其它字母或文字;还可包含下划线(_)、点(.)、连字符(-)
     2.名字不能以数字开头;可以用字母、文字或者下划线开头。
     3.名字不能以字母xml (或XML 或Xml ..) 开头;
     4.名字中不能包含空格。


三、 元素
    位于开始标记与结束标记间
    一份文档有且只有一个根元素。
    根元素下的所有元素叫“子元素”。
    标签必须嵌套正确。
    不包含自子元素的元素叫“叶子”;包含子元素的元素叫“分支”。
    如: <eric>…… </eric>


四、 属性
    一个元素的开始标志中的名称-值对
    所有的属性值必须位于单引号或双引号中。
    每一个元素的属性不允许出现超过一次。
    开始标志内,类似赋值语句
    如:<eric age="80">……</eric>


五、 注释
    注释可以出现在文档的任何位置。(但不建议放在声明前面,部分浏览器会报错)
    注释以 <!-- 开始,以 -->  结束。
    注释内不能包含双连字符(--);除此之外,注释可以包含任何内容。
    注释内的任何标记都被忽略


六、 处理指令
    处理指令是为使用一段特殊代码而设计的标记,简称为PI。
    大多数XML 文档都是以XML 声明开始,该声明本身就是特殊的处理指令。
    处理指令对应用程序特定的数据进行编码。一条处理指令包含一个目标,后跟数据。用<?和?>定界符将处理指令包起来。
    目标确定应用程序,而对应用程序不能识别的目标,其会忽略这些处理指令。


七、 实体
    XML 规范预定义了五个实体。
      &lt;   ==== <
      &gt;   ==== >
      &quot; ==== ”
      &apos; ==== ‘
      &amp;  ==== &
    自定义实体:在DTD中定义 <!ENTITY 实体标志 "实体内容">
      在xml中引用自定义实体,用  &实体标志;  代表实体内容。
    另外,无法从键盘输入的字符可以使用字符引用,就是用字符的Unicode代码点来引用该字符。
      以"&#x"开始字符引用,以分号结尾,x必须为小写,使用十六进制。如: &#x003D; 表示等于号。
      也可以使用字符引用来引用 <,>,',",&  "
      查看字符的代码点(附件-> 系统工具-> 字符映射表)。


八、 CDATA
    当一段文本中出现很多实体引用和字符引用时,会导致文本数据的读写困难,CDATA段就是为了解决这一问题引入的。
    DATA区段开始于 "<![CDATA["  结束于  "]]>" 
    CDATA内部的所有东西都会被解析器忽略解析,不用检查它的格式。
    但是CDATA段中不能嵌套另一个CDATA段。


九、 属性
    属性是标记的属性,可以为标记添加附加信息。
    (1)属性的组成
       属性是一个名值对,必须由名称和值组成,属性必须在标记的开始标记或空标记中声明,用"="为属性指定一个值。
       语法如下:
           <标记名称 属性列表/> 
           <标记名称 属性列表>XXX</标记名称> 
       例如: <桌子 width="40" height='100'/> 
    (2)使有属性的原则
       属性不体现数据的结构,只是数据的附加信息;
       一个信息是作为一个标记的属性或子标记,取决于具体问题,不要因为属性的频繁使用破坏XML的数据结构。
       下面是一个结构清晰的XML文件:
           <楼房 height="23m" width="12m"> 
               <结构>混凝土</结构> 
               <类别>商用</类别> 
           </楼房>
      下面是一个结构不清晰的XML文件:
          <楼房 height="23m" width="12m" 结构="混凝土" 建筑商="华海集团" 类别="商用"></楼房>


十、 名称空间/包
    XML文件允许自定义标记,所以可能出现同名字的标记,为了区分这些标记,就需要使用名称空间。
    名称空间的目的是有效的区分相同的标记,其实并不真实存在。
    语法: 声明有前缀的名称空间  xmlns:前缀名=名称空间的名字
          声明无前缀的名称空间  xmlns=名称空间的名字  (缺省) 
    注意:当且仅当它们的名字相同时称二个名称空间相同,也就是说,对于有前缀的名称空间,如果二个名称空间的名字相同,即使前缀不相同,也是相同的名称空间,返之同然。前缀只是方便引用而已。


 


基本术语
    一、序言Prolog:包括XML声明(XML Declaration)和文档类型声明(Document Type Declaration)。
    二、良构(well-formed 规范的):符合W3C定义的XML文档。


验证
    为什么需要验证?
    对XML文件施加额外的约束,以便交流。


一、DTD验证
    文档类型定义(Document Type Definition)
    DTD定义了XML文档内容的结构,保证XML以一致的格式存储数据。精确的定义词汇表,对XML的内容施加约束。
    符合DTD的规范XML文档称为有效的文档。由DTD定义的词汇表以及文档语法,XML解析器可以检查XML文档内容的有效性。
    规范的XML文件不一定是有效的;有效的一定是规范的。


1、 DTD声明
    1) DTD声明可以在单独的一个文件中
    2) DTD声明可以内嵌在XML文件中
    3) DTD声明可以一部分在单独的文件中,另一部分内嵌在XML文件中


2、 引入外部DTD文件
    <!DOCTYPE data SYSTEM "Client.dtd">
    Data:根节点名称
    Client.dtd:dtd文件路径


3、 DTD四种标记声明
    元素(ELEMENT)、属性(ATTLIST)、实体(ENTITY)、符号(NOTATION)


  1) 元素(ELEMENT) XML元素类型声明
     声明元素: <!ELEMENT elementName (contentModel)>
     元素的内容通过内容模式来描述。
     DTD 内容模式的种类有:
         EMPTY   元素不能包含任何数据,但可以有属性(前提是必须声明其属性)。
                 不能有子元素。不能有文本数据(包括空白,换行符)。
                 DTD中定义: <!ELEMENT elementName EMPTY>
                 XML中:<elementName/>(推荐) 或者:<elementName></elementName>
       (#PCDATA) 规定元素只包含已析的字符数据,而不包含任何类型的子元素的内容类型。
                 DTD中定义: <!ELEMENT student (#PCDATA)>
                 XML中合法内容: <student>watching TV</student>
      (Elements) 元素由内容模式部件指定。
                 <!ELEMENT  name  (child particles) > 
                 内容模式部件可以是下表列出的内容。
                    <!ELEMENT name (a,b)>  子元素a、b必须出现,且按照列表的顺序
                    <!ELEMENT name (a|b)>  选择;子元素a、b只能出现一个
                    <!ELEMENT name (a)  >  子元素a只能且必须出现一次
                    <!ELEMENT name (a)+ >  子元素a出现一次或多次
                    <!ELEMENT name (a)* >  子元素a出现任意次(包括零次、一次及多次)
                    <!ELEMENT name (a)? >  子元素a出现一次或不出现
        Mixed    混合模式:子元素中既可有文本数据又可有下级子元素。
                 <!ELEMENT rn (#PCDATA| an | en)*>“|”和“*”必须写。
                 上句表示在 rn 内,字符数据 或 en及an 可以出现任意多次,顺序不限。
                 优先写(#PCDATA)  如:(#PCDATA|name)* 正确   (name|#PCDATA)* 错误
         ANY     元素可以包含任何类型的数据。子元素(必须在DTD中有定义) 和 文本数据(包括空白)。
                 DTD中定义: <!ELEMENT a ANY> <!ELEMENT b ANY>
                 XML中合法内容: <a>somngthing</a> 或者 <a/> 或者 <a><b>oo</b></a>


   2) 属性(ATTLIST) 特定元素类型可设置的属性&属性的允许值声明
        <!ATTLIST elementName
        attributeName1 attributeType attributeDefault
        .......
        attributeNameN attributeType attributeDefault>
     属性类型 (Attribute Type):
        CDATA该属性只能包含字符数据(注意与CDATA段、PCDATA的区别)
        NMTOKEN  是CDATA的子集,它的字符只能是字母,数字,句点,破折号,下划线或冒号。
        NMTOKENS 类似NMTOKEN,但这个可以包含多个值,每个值之间用空格隔开。
        ID       该属性的取值在同一文档内是唯一的。一个元素只能有一个ID类型的属性。
        IDREF    类似指针,指向文档中其他地方声明的ID值。如果该属性取值和指向的ID值不匹配,则返回错误。
        IDREFS   类似IDREF,但它可以具有由空格分隔开的多个引用。
        ENTITY   该属性的值必须对应一个在文档内部声明的但还没有分析过的实体。
        ENTITYS  类似ENTITY,但它可以包含由空格分隔开的多个实体。
        NOTATION 该属性的值必须引用在文档中其他地方声明的某个注释的名称。
        (enumerated) 类似枚举的变量,该属性必须匹配所列的值。各值用“|”分隔开。
                 如: (春|夏|秋|冬) 实际内容文档只能从中取一个。
     属性特性 (Attribute Default) :
        #REQUIRED   必须有且只能有一个属性。
        #IMPLIED    可有可无。
        #FIXED      在DTD中定义默认值,XML中可以不指定,指定则必须等于该默认值。
        attribute-value 如果不指定则用DTD定义的默认值,指定则用指定的值。


<![CDATA[############ 属性(ATTLIST)的举例 ############## ]]>
例一(#REQUIRED)
    DTD中: <!ELEMENT el (#PCDATA)> <!ATTLIST el at1 NMTOKENS #REQUIRED  at2 CDATA #REQUIRED>
    XML中,正确: <el at1 = "10 20"   at2="10" >something</el>
    XML中,错误: <el at="10">something</el>  (没有写另一个#REQUIRED的属性 at2 )


例二(#IMPLIED,#FIXED)
    DTD中: <!ELEMENT el (#PCDATA)> <!ATTLIST el at CDATA #FIXED "10"  at2 CDATA #IMPLIED >
    XML中,正确: <el   at2="20" >something</el> (at有默认值"10",at2 可写可不写)
    XML中,错误: <el at="11" >something</el>(at要么不写,要写只能写成跟默认值相同的)


例三(attribute-value)
    DTD中:<!ELEMENT el (#PCDATA)> <!ATTLIST el at CDATA "10" at2 CDATA "20" >
    XML中,正确: <el at="11" >something</el>


例四(enumerated + attribute-value)
    DTD中:<!ELEMENT el (#PCDATA)> <!ATTLIST el at (10|20|30) "10">
    XML中,正确: <el at="20">something</el>  (at要么不写,默认值 10;要么在(10|20|30)中选一个写)
<![CDATA[############ 属性(ATTLIST)举例 完毕 ############## ]]>


  3) 实体(ENTITY)   可重用的内容声明
     在DTD中定义 <!ENTITY 实体标志 "实体内容">
     在xml中引用自定义的实体,用  &实体标志;  代表实体内容。
      4) 符号(NOTATION) 不要解析的外部内容的格式声明。
    


3、 内部实体:在xml文件里面写(少用)
    外部实体:另外在xml同一文件夹下建立一个dtd文件(提倡)
<!--**************** 内外部的实体举例 ***************** -->
外部的:
      <?xml  version="1.0"  encoding="UTF-8" standalone="no"?>
      <!DOCTYPE root SYSTEM "goodsInfo.dtd"><!--用这句引用外部dtd-->
      <root><goodsInfo>
          <goodsName>goodsName</goodsName>
          <goodsPrice>goodsPrice</goodsPrice>
      </goodsInfo></root>


      以下是名为"goodsInfo.dtd"文件
      <!ELEMENT root   (goodsInfo)>
      <!ELEMENT goodsInfo  (goodsName,goodsPrice)>
      <!ELEMENT goodsName  (#PCDATA)>
      <!ELEMENT goodsPrice (#PCDATA)>


内部的:
      <?xml  version="1.0"?>
      <!DOCTYPE root [
          <!ELEMENT root(student)>
          <!ELEMENT student (#PCDATA)>
          <!ENTITY CCTV  "中央电视台">
      ]>  <!--把DTD文件写在体内-->
      <root><student>
          student watch &CCTV;<!--使用自定义实体 CCTV-->
      </student></root>
<!--***************** 内外部的实体举例 完毕 ********************** -->




XML处理模式
一、 DOM 文档对象模式
    1.DOM特点:
      以树型结构访问XML文档。 一棵DOM树包含全部元素节点和文本节点。可以前后遍历树中的每一个节点。
      整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能。
      将整个文档调入内存(包括无用的节点),浪费时间和空间。
      一旦解析了文档还需多次访问这些数据;硬件资源充足(内存、CPU)情况下使用。
    2.DOM树与节点
      XML文档被解析成树型结构。
      树由节点组成。共有12种不同的节点。
      节点可以包含其他节点(依赖于节点的类型)。
      父节点包含子节点。叶子节点没有子节点。
    3.节点类型
      Document node   包含:一个根Element节点。一个或多个处理指令节点。
      Document Fragment node
      Element node包含:其他Element节点。若干个Text节点。若干个Attribute节点。
      Attribute node  包含:一个Text节点。
      Text node
      Comment node
      Processing instruction node
      Document type node
      Entity node
      Entity reference node
      CDATA section node
      Notation node




二、 SAX 基于事件处理模式
    解析器向一个事件处理程序发送事件,比如元素开始和元素结束,而事件处理器则处理该信息。
    然后应用程序本身就能够处理该数据。原始的文档仍然保留完好无损。


<![CDATA[################## SAX处理器(遍历XML) ###########################]]>
import java.io.IOException;


import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;


public class TestSAXParser {
    /** 基于SAX方式解析XML文档 */
    public static void main(String[] args)
    throws SAXException,ParserConfigurationException,IOException{
        SAXParserFactory factory = SAXParserFactory.newInstance(); //创建SAX解析器工厂
        factory.setValidating(true);                               //让error方法生效
        SAXParser parser = factory.newSAXParser();                 //生成一个具体的SAX解析器
        parser.parse("src/file/student.xml",new XMLreader());      //开始解析
}}


class XMLreader extends DefaultHandler {
    // 只需覆盖我们感兴趣的方法
    private int counter = 0;// 定义一个计数器,保存XML文档触发事件的次数
   
    @Override   // 文档开始事件触发
    public void startDocument() throws SAXException {
        counter++;
        System.out.println(counter + ".解析XML文件开始...");}
    
    @Override  // 文档结束事件触发
    public void endDocument() throws SAXException {
    counter++;
    System.out.println("/r/n"+counter + ".解析XML文件结束...");}
    
    @Override  // 元素开始事件触发
    public void startElement(String uri, String localName, String qName,
    Attributes atts) throws SAXException {
      counter++;
      System.out.print(counter+".<"+qName);
        for(int i=0; i<atts.getLength();i++){ //读取标志的所有属性
          System.out.print(" "+atts.getLocalName(i)+"="+atts.getValue(i));
      }System.out.print(">"); }
    
    @Override  // 元素结束事件触发
    public void endElement(String uri, String localName, String qName) throws SAXException {
        counter++;
        System.out.print(counter +".</"+qName+">");}
    
    @Override // 文本事件触发  打印时尽量不要换行,否则很难看
    public void characters(char[] ch, int start, int length)throws SAXException {
        counter++;
        String text = new String(ch, start, length); // 当前元素的文本值
        System.out.print(counter + ".Text=" + text);}
    
    @Override //这是可恢复错误。需在SAXParserFactory设置有效性错误才能生效
    public void error(SAXParseException e) throws SAXException {
        System.out.println("xml文档有效性错误:"+e);}
    
    @Override //严重错误
    public void fatalError(SAXParseException e) throws SAXException {
        System.out.println("xml文档严重的有效性错误:"+e);}
}
<![CDATA[################## SAX处理器(遍历XML)结束 ###########################]]>


三、 DOM
<![CDATA[######################### DOM遍历方式 ###########################]]>
import java.io.File;
import java.io.IOException;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;


import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/**基于DOM的解析XML文档*/
public class TestDOMParser {
 public static void main(String[] args)
 throws ParserConfigurationException,SAXException,IOException{
  //创建一个DOM解析器工厂
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
  //从工厂中生成一个DOM解析器; throws ParserConfigurationException
  DocumentBuilder builder = factory.newDocumentBuilder();  
  //绑定需要解析的XML文件
  File xmlFile = new File("src/file/student.xml");//相对地址,相对于这个工程  
  //开始解析 ;throws SAXException,IOException
  Document document = builder.parse(xmlFile);
  //取出唯一的根元素
  Element rootElement = document.getDocumentElement();
        //调用业务方法: 遍历根元素
  printElement(rootElement); 
 }
 
 /** 遍历元素,包含: 子元素、属性、文本内容 */
 private static void printElement(Element e){
  //打印出元素的标签名
  System.out.print("<"+e.getTagName());
  //获取开始标签的属性
  NamedNodeMap attMap = e.getAttributes();
  //循环遍历所有的属性
  for (int i=0;i<attMap.getLength();i++){
   Attr attr = (Attr)attMap.item(i);
   System.out.print(" "+attr.getName()+"="+attr.getValue());}
  System.out.print(">");
  
  //获取当前元素的所有子节点
  NodeList nl = e.getChildNodes();
  for (int j=0;j<nl.getLength();j++){
   Node n = nl.item(j);
   if (Node.ELEMENT_NODE==n.getNodeType()){
    printElement((Element)n);//递归调用,以遍历下一个元素
   } else {
    System.out.print(n.getTextContent());
   }
  }
  //打印结束标签
  System.out.print("</"+e.getTagName()+">");
}}
<![CDATA[ ###################### DOM遍历 完毕 ##########################]]>


比较DOM与SAX:
    DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问
    优点:1.提供随机定义元素操作,来回移动指针
         2.将整个XML文件一次性加载到内存,形成虚的内存树
    缺点:1.如果XML文件较大,内存空间占用较大
         2.强制将较大的XML文件加载到内存中,有可能损害文件
         3.功能通用性


    SAX:不同于DOM,SAX是事件驱动型的XML解析方式。它顺序逐行读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问


J2ee | 评论:0 | Trackbacks:0 | 阅读:65


06. OOAD & UML note
Submitted by cnetsa on 2009, July 29, 2:21 PM




day01:
面向对象
    通过封装、继承、多态,把程序的耦合度降低,使程序灵活、容易修改、容易复用。
    面向对象=对象 + 类 + 消息 + 继承 + 多态
    面向对象方法是一种把面向对象的思想运用于软件开发过程,指导开发活动的系统方法,包括分析、设计和实现等活动


    软件开发组越大,组中每个成员的生产率就越低
                    --Philippe Kahn, Borland
       Ln = 15000/(n^-3)( LOC / year )
       构造大型软件不能靠堆人


    敏捷开发:
        1. 思路先行  //先用注释把思路记录下来
        2. 实现随后




  可维护性:预见需求(预见多年后的事) 
  可重用: 
          代码可重用(最低级别):粒度:方法(常用代码块),类,包,组件(类库) 
          设计的可重用:框架(半成品,如Hibernate);产品(开发软件);算法、设计模式
          分析的可重用(最高级别,见不到代码):文档、规范、标准(ISO:CMM,CMMI) 
  可扩展性:




UML图
    类(class) 用矩形框表示,分三层
        第一层:类名,抽象类用斜体字
        第二层:类的特性:字段和属性
        第三层:类的操作:方法或行为
             方法前的符号:“+”表示public,“-”表示private,“#”表示protected


    接口:右边的“飞翔”
        顶端有<<interface>>,第一层是接口名字,第二层是方法
    接口的另一种表示法:俗称棒棒糖表示法,就是类上面的一根棒棒糖(圆圈+实线)
        圆圈旁为接口名称,接口方法在实现类中出现


    继承:用空心三角+实线
    实现接口:空心三角+虚线


 




类与对象之间的关系(6种):
    关联 Association:一个类受另一个类影响(实线) 
    聚合关联 Aggregation:弱'拥有'关系,A对象可以包含B对象,但B不是A的一部分(空心菱形+实线箭头)[DPE]
        (DPE 表示这句话来自《设计模式》) 
    组合关联 Composition:(也叫合成,组成)是一种强的‘拥有’关系,体现严格的部分和整体的关系,
        部分和整体的生命周期一样[DPE](实心菱形+实线箭头) 
      组合图形的基数:表明这一端的类可以有几个实例,(一只鸟有两个翅膀)如果有无数个实例,则用n表示
        关联关系、聚合关系也可以有基数
    依赖 Dependency :一个类需要另外一个类(X需要Y,则X的修改Y也要跟着修改) (虚线箭头) 
    泛化(继承) (实心菱形+实线) 
    实现  (实心菱形+虚线) 
   


 




UML 4+1 图
    1:用例图    描述系统中有哪些用户可用的功能
    4:逻辑图    将问题中的一些名词提取出来,形成系统中对应的类,表示之间的关系。
       过程图    表示系统对象间的交互
       实现图    系统中组件与组件之间交互
       部署图    软件系统真实运行过程的物理描述


 


静态(系统结构):
        类图
        对象图
        构建图
        部署图
动态(系统行为):
        顺序图    (时序图)
        协作图
        状态图
        活动图
        用例图
   


 


 


day02:
面向对象的7大基本设计原则


程序设计:没有最好,只有最适合。寻找平衡点。


1. LSP(The Liskov Substitution Principle ,替换原则)
父类出现的地方,子类都可出现。
子类或实现类与父类都是可以互换的。
    子类不能添加任何父类没有的附加约束
    子类对象必须可以替换父类对象


2. OCP (The Open-Close Principle,开闭原则)
要关联抽象,不要关联具体,抽象可扩展。
    扩展是开放的,更改是封闭的


3. SRP(The Single Responsibility Principle,单一职责原则)
依赖不同的具体类,不要将不相关的方法放到一个具体类中,然后具体类再关联。
    一个类,应该仅有一个引起它变化的原因
    当需求变化时,该变化会反映为类的职责的变化(如果有多个职责,引起变化的原因就会有多个)


4. ISP(The Interface Segregation Principle,接口隔离原则)
具体类不要实现无关接口中的方法,应使用具体类实现多个接口。
    避免肥接口,以一个类实现多个接口,而各客户仅仅获知必须的接口
    本质:
        使用多个专门的接口比使用单一的接口好
        一个类对另一个类的依赖性应当最小化
        避免接口污染(Interface Pollution)(使用不必要的功能)


5. DIP(The Dependency Inversion Principle,依赖倒置原则)
高层依赖于抽象,底层继承/实现于抽象。
    高层模块不应该依赖于低层模块,二者都应该依赖于抽象
    细节应该依赖于抽象,而抽象不应该依赖于细节
    针对接口编程,不是针对实现编程


6. CARP(Composite/Aggregate Reuse Principle,组合/聚合复用原则)
尽量使用组合/聚合,而不是使用继承来达到复用目的
    继承的缺点:会带来不必要的方法
    组合/聚合的解决方案
        组合:部分的更改会影响整体的生命
        ***:部分的更改对整体的影响不大


7. LoD(Law of Demeter,迪米特法则)
类间最少通信原则,采用中间类。
    也称最少知识原则。一个对象或模块应该和其它对象和模块尽量少的通信


 


GoF(Gang of Fout) 23种经典设计模式
        创建型                     结构型               行为型
类   Factory Method 工厂方法     Adapter_Class       Interpreter
                                                   Template Method
对象 Abstract Factory 抽象工厂   Adapter_Object      Chain of Responsibility
    Builder                    Bridge              Command
    Prototype 原型              Composite           Iterator
    Singleton 单例              Decorator 装饰       Mediator
                               Facade              Memento
                               Flyweight           Observer
                               Proxy               State    状态
                                                   Strategy
                                                   Visitor


单例模式:
    当多个对象需要共享同一个对象时;
原型模式:
    对扩展开发,对修改关闭;
工厂模式:
    客户需要某个产品,能够根据客户要求取得产品给客户;
状态模式:
    当需要对某个对象内部状态改变时,使用;
装饰模式:
    当需要对某个对象动态添加新功能时,可以用;
适配器模式:
    只需要对接口中的一小部分方法重新定义,又不希望将接口中的所有方法实现,
    这时可以使用;
观察者模式:
    当主题对象改变时,需要通知所有的观察者,这时可以使用;
命令模式:
    将用户发出命令以对象形式传递,通过参数可改变命令对象的状态;


J2ee | 评论:0 | Trackbacks:0 | 阅读:66


05. Core Java note
Submitted by cnetsa on 2009, July 29, 2:21 PM


sun考试:  SCJP:只考core java   SCJD:+jdbc+swing   
         SCWCD:+servlet+jsp(JAVA EE)   SCEA:+EJB+Webserver(架构师)


必须养成优秀程序员的编写习惯:缩进(用空格)、注释、命名约定。
大小写敏感。
单独的“;”代表一条空语句。
main函数是我们整个程序的执行入口所以必须是静态公开的。
       必须写成这样:  public static void main(String[]args){...}


生成jar包:
    在eclipse里,选中要打包的几个文件,右键-Export-写文件名-Next-Next-选main方法的class-finish
    在jar包的同一文件夹下,新建一个空文档,写“java -jar ./文件名.jar”,再把这文档改成“文件名.sh”
    把这sh的属性-权限 改成“允许以程序执行文件”。以后双击这个sh即可运行


文本注释 Comments:
    注释必须写上,以便其他人阅读、引用和维护。
    单行注释  //...
    多行注释  /* ....*/
    文档注释  /** ... */ 
    文档注释,可以使用JDK的javadoc工具从原文件中抽取这种注释形成程序的帮助文档。
    使用javadoc命令建立HTML格式的程序文档:
     javadoc[options][packagenames][sourcefiles][@files]


标示符:
    用来给一个类、变量或方法命名的符号
  标示符命名规则:
    1. 以字母,“_”和“$”开头。可以包含字母、数字、“_”和“$”。
    2. 大小写敏感
    3. 不能与保留关键字冲突
    4. 没有长度限制(暗示使用长的标示符,以便阅读。长名字可使用工具输入) 
    5. 建议使用JavaBeans规则命名,并根据方法的目的,以 set、get、is、add 或 remove 开头。
  标示符命名约定:
    1. 类名、接口名:每个单词的首字母应该大写,尤其第一个单词的首字母应该大写。(驼峰规则)
        class  MyFirstClass
        interface  Weapon
    2. 字段、方法以及对象:第一个单词首字母应小写,其他单词首字母大写。(以便跟上面的有所区别)
        boolean isWoman
        void setName(String name)
    3. 常量:全部用大写字母表示。如果由几个单词组成,则由下画线连接。
        public final int  GREEN    
        public final int  HEAD_ COUNT
    4. Java包(Package):全部用小写字母。
        package  java.awt.event




java.lang.System.gc();  /  java.lang.Runtime.gc(); 
    垃圾回收的建议语句,只能建议而不能强制回收
    注意: System.gc(); 是静态方法,可直接调用。
          java.lang.Runtime.gc(); 不是静态方法,不能直接在main方法里调用


package 包
    目的:命名冲突,便于管理类
    运行时,先找到包所在目录,再执行“ 包名.类名”
import 导入。导入包内的类
    定义包之后,执行时:javac  -d 包的路径  类名.java
                    java  包名.类名
    import java.util.*; //表示导入java.util里面的所有类;但 import java.*; 则什么类都导不进
    用“*”表示导入当前包的类,不包括子包的类(可把包看作目录)。


声明规则
    * 一个源代码文件最多只能有一个公共(public)类。
    * 如果源文件包含公共类,则该文件名称应该与公共类名称相同。
    * 一个文件只能有一个包语句,但是,可以有多个导入语句。
    * 包语句(如果有的话)必须位于源文件的第一行。
    * 导入语句(如果有的话)必须位于包之后,并且在类声明之前。
    * 如果没有包语句,则导入语句必须是源文件最前面的语句。
    * 包和导入语句应用于该文件中的所有类。
    * 一个文件能够拥有多个非公共类。
    * 没有公共类的文件没有任何命名限制。


输入:使用Scanner 获取输入
    在J2SE 5.0中,可以使用java.util.Scanner类别取得使用者的输入
    可以使用这个工具的 next() 功能,来获取用户的输入
       Scanner s = new Scanner(System.in);
     System.out.printf("您输入了字符:  %s /n",  s.next());
     System.out.printf("您输入了数字: %d /n",  s.nextInt());
输入:使用 BufferedReader 取得输入//5.0之前的读取键盘的方法
    BufferedReader建构时接受java.io.Reader物件
    可使用java.io.InputStreamReader
    例: import java.io.InputStreamReader;
        import java.io.BufferedReader;
      class n{
    public static void main(String[] args){
       System.out.println("请输入一列文字,包括空格:");
       BufferedReader s = new BufferedReader(new InputStreamReader(System.in));
       String next;
       try{next = s.readLine();//此语句会抛异常,需处理
          System.out.println("您输入了文字:" + next);
       }catch(Exception e){}
        }}


数值保存方式:
    正数=  二进制
    负数=  补码
    补码=  反码 +1     正数=负数的补码(反码+1) 
    反码=  非(二进制数)


八进制数,零开头        011(八进制)=9(十进制)
十六进制数,零x开头    0x55(十六进制)=5*16+5(十进制)


类型:数据都必须有类型
    boolean (8bit,不定的)只有true和false两个值
    char    16bit,   0~2^16-1     (2^16=6万6)
    byte    8bit,   -2^7~2^7-1    (2^7=128; 注意:两个 byte 数相加,变 int 型) 
    short   16bit,  -2^15~2^15-1  (2^15=32768)
    int     32bit,  -2^31~2^31-1  (2147483648,20亿,10位有效数字) 
    long    64bit,  -2^63~2^63-1  (900亿亿,20位有效数字) 
    float   32bit,  9位有效数字,含小数(四舍五入)(小数点算一位,正负号不算) 
    double  64bit,  18位有效数字
             注:float 和 double 的小数部分不可能精确,只能近似。
                比较小数时,用 double i=0.01; if ( i - 0.01 < 1E-6) ...
                不能直接 if (i==0.01)...


默认,整数是int类型,小数是double类型
long类型值,需跟L或l在数据后;float类型要跟f或F;或强制类型转换
科学计数法:12.5E3


    类型转换默认序列:
    byte  >  short   > int  >  long   >  float  >  double
                   char 」
    注意:默认类型转换(自动类型提升)会丢失精度,但只有三种情况:
    int>float; long>float; long>double.   看一下他们的有效位就明白。
    二进制是无法精确的表示 0.1 的。
    进行高精度运算可以用java.math包中BigDecimal类中的方法。
    自动类型提升又称作隐式类型转换。


    强制类型转换:int ti;  (byte) ti ;
    强制转换,丢弃高位


    宣告变量名称的同时,加上“final”关键词来限定,这个变量一但指定了值,就不可以再改变它的值
    如:final int n1= 10;   n1=20;  这就会报错




输出命令:
   System.out.println()  会自动换行的打印
   System.out.print()    直接打印,不会自动换行
   System.out.printf()   可插入带 % 的输入类型,前两种只可以插入转义符, 不能插入 % 的数据或字符串
   在 printf 里面,输出有5个部分  %[argument_index$][flags][width][.precision]conversion
         以“%”开头,[第几个数值$][flags][宽度][.精确度][格式]
   printf()的引入是为了照顾c语言程序员的感情需要
         格式化输出 Formatter;格式化输入 Scanner;正则表达式


输出格式控制:
   转义符:
   /ddd     1到3位8进制数指定Unicode字符输出(ddd)
   /uxxxx   1到4位16进制数指定Unicode字符输出(xxxx)
   //       / 
   /'       ' 
   /"       "  
   /b       退格(光标向左走一格)
   /f       走纸转页,换页
   /n       换行
   /r       光标回到行首,不换行
   /t       跳格


   %%       %  
   %d       输出10进位整数,只能输出Byte、Short、 Integer、Long、或BigInteger类型。(输出其他类型会抛异常) 
   %f       以10进位输出浮点数,提供的数必须是Float、Double或 BigDecimal (输出Integer类型也抛异常) 
   %e,%E    以10进位输出浮点数,并使用科学记号,提供的数必须是Float、 Double或BigDecimal
   %a,%A    用科学记号输出浮点数,以16进位输出整数部份,以10进位输出指数部份,数据类型要求同上。
   %o       (字母o)以8进位整数方式输出,限数据类型:Byte,Short,Integer,Long或BigInteger
   %x,%X    将浮点数以16进位方式输出,数据类型要求同上
   %s,%S    将字符串格式化输出(可输出任何类型)  
   %c,%C    以字符方式输出,提供的数必须是Byte、Short、Character或 Integer
   %b,%B    输出"true"或"false"(%B输出"TRUE"或"FALSE");另外,非空值输出true,空值输出 false
   %t,%T    输出日期/时间的前置,详请看在线API文件


/********找出各字符的Unicode值*******************/
class Test{
    public static void main(String[] args) {
        String s= ""+0+'a'; //0=48,9=57
              //A=65,Z=90;a=97,z=122;空格=32
        int i = s.codePointAt(0);
        int j = s.codePointAt(1);
            //利用这codePointAt(int index)方法
        System.out.printf("%d %d",i,j);
}}
/**********************************************/
       
字符串的拼接:
    字符串+数值=字符串
    数值+字符串=字符串
    如:str+10+20 ==str1020   而 10+20+str ==30str
    "+" 和 "+=" 都被重载了,具有合并字符串的能力,相当于 String 类里的 concat();


运算:
    算术运算:   加( +)   减(-)    乘( * )     除( / )     取余( % )
        % 取余运算: 2%3=2     100%3=1 
    赋值运算符:
      =     +=    -=    *=     /=     %=   
      (先运行完右边的,再跟左边的进行赋值运算;如 int i=10;i-=3*5;结果-5) 
      <<=     >>=    
    比较、条件运算:
        大于>   不小于>=    小于<   不大于<=    等于==    不等于 !=
    逻辑运算:
        短路运算(且 &&    或 ||  )      非短路运算(&   |  )      反相 ! 
        短路运算:当前面一个表达式可以决定结果时,后面的语句不用再判断。非短路运算时,还照样判断后面的
    位运算:
      &(AND)    |(OR)    ^(XOR异或)       ~(补码)按位取反 = 加1再取反(全 1 的补码是-1)
    移位运算:
     >>   <<    >>>     
     
     >>右移:全部向右移动,移到右段的低位被舍弃,最高位则移入原来最高位的值。右移一位相当于除2取商。
     >>>同上,只是最高位移入0(不带符号)。因为最高位是符号位,所以负数跟 >> 有区别,正数没区别。
     12>>>33  为12>>(33%32) = 12>>1  =6;因为int 型只有32位,认为全移走后就没意义
     1 <<32 为1
    instanceof():用户判断某一个对象是否属于某一个类的实例。
    “==”双等于号,比较数值是否相等。还可以用于比较两个引用,看他们引用的地址是否相等。
    在 Object 类里 equals() 跟“==”功能一样;但可以重载定义一个比较两者意义是否相等的方法。
    在java里可以把赋值语句连在一起写,如: x=y=z=5;   这样就x,y,z都得到同样的数值 5




两个数相运算时,默认是 int 类型
如果有更高级的,就按高级的那个类型
   if(其中一个是double型)double型;
   else if(其中一个是float型)float型;
   else if(其中一个是long型)long型;
   else int 型。


选择:
    if(...){...}else{...}
    if(...){...}else if(...){...}
    if(...){... if(...){...}}
    
    三重以上的选择,建议使用 switch
    switch(char c){
        case c1: ...; break;
        case c2: ...; break;
        ...
        default :...;
    } /*switch的括号里只能用 int 和 枚举类型
        能隐式转换为 int 的也可以:byte,short,char,Integer,Short,Character,Byte等。
        不能用 long、小数类型(float,double) 和 String。
        case后的值必须是常量。而包装类变量(Integer,Character)不会被视作常量。*/




循环:
   for(初始表达式;  布尔表达式 ;  步进 ) 循环语句;
     跟C的 for 一样,for 的初始化条件、结束条件、增量都可以不写。
    但条件判断部分只能是boolean值,所以只能是一条条件判断语句。
     for 循环一般用在循环次数已知的情况。
   while (<boolean expr>)...;
   do...; while (<condition>);   注意:do 后最好用“{}”,while 后的分号不可忘。


break 和 continue
   break 退出当前的循环体,在嵌套循环中,只退出当前的一层循环。
   continue 结束当前本次循环,继续进行下一轮的循环。可以说,只是本次忽略循环内后面的语句。
   continue 只能在循环体内用。break 可以用在任意代码块中,表示退出当前程序块(配合标签使用,很好用)
这两个相当于JAVA里的 goto 语句。


注意:(个人归结的)
    循环体内申明的变量,在循环体结束后立即释放,循环体外无法使用。
    但在另外一个循环体内可以再次申明一个跟前面同名的变量,互相不影响。
        如for内定义的 i:  for(int i=0;i<10;i++){...}
        则在上式 for 循环结束后无法再调用 i 值,还会报错。
      for(int i=0;i<10;i++){...} 和后面的 for(int i=0;i<3;i++){...} 互不影响
    若想循环体外还可以调用 for 循环体内的值,应先在体外定义。
        如:  int i;   for (i=0; i<10; i++){...}   则for 循环后再调用 i 值,其值为10




关键字列表:
   abstract  boolean   break   byte     case    catch       char    class
   continue  default   do      double   else    extends     enum    false
   final     finally   float   for      if      implements  import  instanceof
   int       interface long    native   new     null        package private
   protected public    return  short    static  super       switch  synchronized
   this      throw     throws  transient true   try         void    volatile  while 
Java 中 true、false不是关键字,而是boolean类型的字面量。但也不能当作变量用。
所有的关键字都是小写,friendly,sizeof不是java的关键字 
保留字:const,goto :这两个已经削去意义,但同样不能用作变量名。




 第三章   对象
名词
    对象:
    类:  一类属性相同的对象
    属性:是什么样
    方法:能做什么(C 中叫作函数)


对象:
    声明:Student s ;
        这时我们只是说明s是一个能够指向Student类型的引用(相当于C++中的指针),并没有创建一个对象。
        所以我们此时不能对s做任何操作。
    初始化:s = new Student();
        向系统申请一块存储空间(地址空间),该地址空间保存的是一个Student类型的数据。
        而s中保存的就是该地址空间的首地址。
    变量:内存空间中一块具有固定长度的,用来保存数据的地址空间。(s也是一个变量)
    一个对象可以有多个引用指向。
      Student[] s = new Student[3]  只是相当于声明一个长度为 3 的Student类型的数组。 
        
实例变量和局部变量
实例变量:
    1、在一个类中,任何方法之外定义的变量;
    2、从面向对象的思想来说我们又把实例变量看成一个类的属性。
    3、实例变量在没有符初值时系统会自动帮我们做初始化:
          整型数据初始化为 0,布尔型数据初始化为 false,对象类型初始化为 null。
    实例变量的作用域在本类中完全有效,当被其他的类调用的时候也可能有效。
局部变量:
    1、在方法内定义的变量叫局部变量。
    2、局部变量使用前必须初始化,系统不会自动给局部变量做初始化。
    3、局部变量的生命范围在他所在的代码块,在重合的作用域范围内不允许两个局部变量命名冲突。
注:局部变量与实例变量允许同名,在局部变量的作用域内,其优先级高于实例变量。
     我们可以用  this.实例变量名  以区分局部变量。




    第四章  数组
数组:
    数组也是对象
    数组中保存着多个相同类型的元素
    数组中的每一个元素都是变量
        可以创建数组对象,但数组里只能放对象的引用,不能直接放对象进去


数组的创建:
    1. 声明一个int数组变量,数组变量是数组对象的遥控器
       int[] nums;
    2. 创建大小为7的数组,并将它赋值给变量nums
       nums = new int[7];
    3. 赋于int数组每一个元素一个int值
       nums[0] = 6;  nums[1] = 34;  nums[2] = 23;  nums[3] = 4;  
多维数组:
    1.  定义方式:type 维数 arrayName;
        如:  int[][]  b = new int [2] [1];
    2.  分配内存空间,有两种方法:
        直接为每一维分配空间:   int[][] a = new int[2][3];
        分别为每一维分配空间    int[][] a = new int[2][ ]; //列数可以没有,行数则一定要有
            a[0] = new int[3];   a[1] = new int[5];    //a[][]  看成一维数组
         可以为每行设置为空间大小不同的数组。
    3. 初始化,有两种方式:
        先定义数组,分配空间,然后直接对每个元素进行赋值(一个个写,或用for函数)
        在定义数组的同时进行初始化。
              如:int a[][] = {{2,3}, {1,5}, {3,4}};
    java实质上把多维数组看作一维数组,但数组里的元素也是一个数组,即数组的数组
    多维数组的长度 = 行数;  (a.length=行数; a[0].length=列数)


创建数组对象的另外几种方式:
   Int[] nums = {6,34,23,4,15,0, 57}; (java 形式) 
          这方法只能在初始化定义的时候可以,以后再想定义nums={...}就不行了
   Int[] nums = new int[] {6,34,23,4,15,0, 57};  
           这句的后一个 int[] 内不能填数字,怕人弄错数目;
           这句可以先 int[] nums;以后再另外定义 nums = new int[]{...} 
    []可以换换位置,如:
      Int nums[]; (C 和 C++ 形式) 
   注意: short  [] z [] []; //这是合法的,定义一个三维数组
         声明数组时,不能定义其大小;只有 new 数组时可以定大小。


数组元素的默认值:
   byte  short   int   long  为 0
   float  double 为 0.0
   char  为 ‘/0’
   boolean  为 false
    引用类型为null


数组的 length 属性:
    表示数组的长度,是指这个数组最多能保存的元素个数
     length属性只能被读取,不能被修改
     java.lang.ArrayIndexOutOfBoundsException:  (这是数组下标越界的报错)


随机数:
   Math.random(); //可以产生随机的0~1 的小数,不需导包
   java.util.Random;  //可以产生更加多种的随机数
0~100的一个随机整数(包括0,但不包括100):
   Double d = 100*Math.random(); int r = d.intValue();  //方法一
   Random r = new Random();   int num = r.nextInt(100); //方法二;需要 import java.util.Random;
   可以直接在程序中写这句,而临时导入  int i = new java.util.Random().nextInt(100);


Arrays.sort(数组名)
    排序算法。需导入 impor java.util.Arrays;


数组的拷贝:
    1. 用 for 语句,将数组的元素逐个赋值。直接如果直接将数组 a = 数组b;则是将b的指针赋给a
    2. 用System.arraycopy();
     arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 
       src - 源数组。 
       srcPos - 源数组中的起始位置。 
       dest - 目标数组。 
       destPos - 目标数据中的起始位置。 
       length - 要复制的数组元素的数量。
    如:System.arraycopy(a, 0, b, 0, a.length);  //把数组 a 全部复制到数组 b 中




在java中对面向对象(OO)的要求
    1.对象是客观存在的,万物皆对象。
        (注:看不见的对象并不表示该对象不存在,比如说事件);
    2.简单性:采用面向对象方法可以使系统各部分各司其职各尽所能。
    3.复用性:对象的功能越简单其可重用性越高。
    4.弱耦合性:各司其职各尽所能。
    5.高内聚性:一个对象独立完成一个功能的能力
    6.类是一类事务的共性,是人类主观认识的一种抽象,是对象的模板。


面向过程与面向对象的对比
    面向过程:先有算法,后有数据结构。先考虑怎么做。
    面向对象:先有数据结构,后有算法。先考虑用什么做。


 


 第六章  构造方法
方法的声明(分为五个部分)
    1.方法的修饰符(可以有多个,且顺序无关)
    2.方法的返回值类型
    3.方法名
    4.方法的参数列表
       如果方法有参数,一定要以正确的数量、类型、和顺序传递参数;可以将变量当作参数传入,但要类型相符
    5.方法允许抛出的例外(异常)
注:编译器只能做语法上的检查,而不能进行逻辑上的检查。
     Java中不允许有废话,永远不会执行的语句不允许写。
    1. 声明格式:
       <modifiers><return_type><name>([argument_list>])[throws <exception>]{<block>}
       例如:public String getName(){return name;} 
    2. 当没有返回值时,返回类型必须被定义为 void。
    3. 构造方法没有返回类型。
    4. 返回类型必须与方法名相邻,其他修饰符号可以调换位置。




参数传递 
    在java方法传参过程中简单类型是按值传递,对象类型是按引用传递。
    按值传递传递的是数据的副本。
        按引用传递 传递的是保存该数据的地址
      Java语言总是使用传值调用,这意味着方法得到的只是所有参数值的拷贝。
        因此,方法不能修改传递给它的任何参数变量的内容。
    对象类型的参数传递的也是该对象的引用值
    方法中并不能改变对象变量,但能通过该变量调用对象的方法或修改对象的成员。


    方法的参数基本上与局部变量相同,但你不需要直接初始化它
    编译器会确保方法调用时会有与声明相符的参数传进来,且参数会自动被赋值


形参 VS 实参:
    形参(形式参数):相当于函数(Java中也把函数称之为方法)中的局部变量
         在函数被调用时创建,并以传入的实参作为起始值,函数调用结束时被释放
         不会影响主程序中其他的变量(即使有变量跟他们同名),因为他们是不同作用域的变量,互不干扰。
    实参:调用函数时,实际传给函数形式参数的数据。




重载(Overload) 
    在同一个类中,允许同时存在一个以上的同名函数,只要他们的参数列表不同即可。
    参数列表不同,可以是参数的类型或个数不同,也可以是不同类型参数的顺序不同。
    1、相同方法名,不同参数表。
    2、方法重载时,对于参数的匹配有个向上就近原则。(这样可以节省栈空间资源);
    3、为什么面向对象中要有方法重载?
       方法的重载使同一类方法由于参数造成的差异对于对象的使用者是透明的。
       对象的使用者只负责把参数交给对象,而具体怎么实现由对象内部决定。
    4、Java中的运算符重载
      java中唯一重载的运算符是String类型的“+”号,任何类型+String类型结果都为Stirng类型。
    5、注意点:重载不仅出现在同一个类中,也可以出现在父子类中。
    重载的方法只是刚好有相同名字的不同方法
    
方法的覆盖 (Override) 重写
    继承之后,想改变由父类继承下来的方法。
    1. 同样的方法名、参数列表、返回类型(从Java 5 起,返回类型可以是子类型) 
    2. 访问权限不能更小
    3. 异常不能更宽 (可以抛出更少或是更窄的检查异常,或者任何非检查异常)


重构 (extract Method)
    消除代码的重复,提高代码的可维护性。整理凌乱的代码,把可重用的代码块包装起来。
    常用重复的方法封装成工具类(工具类太多则做成工具箱),一般都是静态方法和常量,没有属性。
    在eclipse里,选中要重构的代码,右键Refactor-Extract Mathod 或(Shift+Alt+M)


创建对象的步骤
    1、分配空间
    2、初始化属性
    3、调用构造方法
    注:构造方法不能手工调用,在对象的生命周期内构造方法只调用一次。


构造方法     (参考day05的 TestCat.java)
    构造方法是在生成对象的过程中调用的方法,但构造方法并不能创建对象。
      new 对象的时候需要调用构造方法。
    1、特点:没有返回值(连void也没有),方法名与类名相同。(如果加上 void 会变成普通方法。)
    2、在不写构造方法时,系统会自动生成一个无参的构造方法。
    3、请养成在每个类中自己加上无参构造方法的习惯。


    格式为:public ClassName(){}
        构造方法也可以是其他的限制符――private protected default
      private 一般用在 singleton 模式中。
    在一个对象的生成周期中构造方法只用一次,一旦这个对象生成,那么这个构造方法失效。
    * 接口不能创建实例,因为没有构造方法


    可以构造多个构造方法,但多个构造方法的参数表一定不同,或参数顺序不同
    即属于不同的构造方法:-----------------------> 构造方法的重载


    使用构造方法来初始化对象的状态:把初始化代码放到构造方法中,并且把构造方法设定成需要参数的
    编译器一定会帮你写出没有参数的构造方法吗?不会
        如果你已经写了一个有参数的构造方法,并且你需要一个没有参数的构造方法,则你必须自己动手写
        如果类有一个以上的构造方法,则参数列表一定要不一样,我们可以认为这几个构造方法形成重载关系
        如果我们提供了有参的构造方法,那么系统不会再提供无参的构造方法了。
        这样当被子类继承时,如果子类构造方法不人为调用父类的有参构造方法就会出现异常。
    构造方法可以通过 this 调用另外一个构造方法(this 此时必须在第一行语句)


匿名对象:
    创建对象时,直接调用对象的方法而不定义对象的句柄。
        如: person p1 = new person; p1.shout();
        改写成: new person.shout();   //此方法执行完,此匿名对象也就变成了垃圾。
    使用匿名对象的情况:
    1. 此对象只需要一次方法调用。
    2. 此对象作为实参传给一个函数调用。




this 当前对象  (参考day05 的TestThis.java)
    谁调用该方法,在这一时刻谁就是该方法的当前对象;是个隐式参数,代表被构造的对象。
    用this来区分实例变量和局部变量。
      this.实例变量名 = 局部变量名    (将局部变量赋值给实例变量)
   this()表示调用本类的其他构造方法,且只能放在一个方法中的第一行第一句。
        构造方法可以通过this调用另外一个构造方法(this此时必须在第一行语句)
*super 关键字也是个隐形参数,代表被构造对象的父类。
    同样也必须在构造方法的第一行




对象和对象引用的区别
   对象好比一台电视机,对象引用好比电视机遥控。对象引用 中存的是对象的地址。
   多个对象引用中存放的是同一个地址,表示该对象被多个对象引用所引用。




面向对象的三大特性:
    封装(Encapsulation)、继承(Inheritance)、多态polymiorphism


封装:
    1.定义:指一个对象的内部状态对外界是透明的,对象与对象之间只关心对方有什么方法,而不关心属性。
       封装使实现的改变对架构的影响最小化。封装后的代码更具有安全性、可扩展性和可维护性。
    2.原则:封装使对象的属性尽可能的私有,根据需要配上相应的get/set方法,对象的方法尽可能的公开。
           该隐藏的一定要隐藏,该公开的一定要公开。
    3.方法公开的是声明而不是实现。使方法实现的改变对架构的影响最小化。
    4.访问权限控制从严到宽
       private  :仅本类成员可见
       default  :本类+同包类可见(默认)
       protected:本类+同包+不同包的子类
       public   :公开
        注:这里的同包指的是跟父类所在的包相同。
    5、完全封装:属性全部私有,并提供相应的get/set方法。
    优点:
    1.事物的内部实现细节隐藏起来
    2.对外提供一致的公共的接口――间接访问隐藏数据
    3.可维护性


一、继承:
    1 定义:基于一个已存在的类构造一个新类。
      继承已存在的类就是复用这些类的方法和属性,在此基础上,还可以在新类中添加一些新的方法和属性。
    2 父类到子类是从一般到特殊的关系。
    3 继承用关键字extends
      dog extends Animal :表示狗类继承了动物类
    4 Java中只允许单继承(java简单性的体现) 
      父子类之间的关系是树状关系。(而多继承是网状关系)
    5 父类中的私有属性可以继承但是不能访问。
      也可以说父类中的私有属性子类不能继承。
    6 原则:父类放共性,子类放个性。
    7 构造方法不能被子类继承。


    父类的成员能否继承到子类?
      private:本类内部可以访问  不能继承到子类
     (default):本类内部可以访问,同包其他类也可以访问
            能否继承到子类? 不一定:同包的可继承,不同包则不可继承。
      protected:本类内部可以访问,不同包的子类也可以访问, 同包其他类也可以访问
            能继承到子类       
      public:任何地方都可以访问  能继承到子类


    继承的意义:
    1. 避免了重复的程序代码,提高了程序的可重用性
    2. 定义出共同的协议


二、带继承关系的对象创建的过程
    1.递归的构造父类对象
    2.分配空间
    3.初始化属性
    4.调用本类的构造方法


三、super 关键字
    1.super()表示调用父类的构造方法
    2.super()也和this一样必须放在构造方法的第一行第一句。不是构造方法则不用第一行。
    3.super.表示调用父类的方法或属性。例:super.m();
    4.super 可以屏蔽子类属性和父类属性重名时带来的冲突
    5.在子类的构造函数中如果没有指定调用父类的哪一个构造方法,就会调用父类的无参构造方法,即super()


    指向父类的引用
     super.age
     super.addAge()
    调用父类的构造方法
     super();
     super(“wangcai”,8);


一个对象的创建过程
    1. 当构造一个对象的时候,系统先构造父类对象,再构造子类对象。
    2. 构造一个对象的顺序:(注意:构造父类对象的时候也是这几步)
       递归地创建父类的 static 成员(即使只调用其子类静态成员,也会先创建父类静态成员);
       顺序地创建本类的 static 成员(只要调用这个类的属性或方法都需创建一次);
       递归地创建父类对象(先创建父类非静态成员,再调用父类构造方法);
       顺序地创建本类非静态成员(包括属性和方法);
       调用本类的构造方法(它可调用本类或父类的成员,也可调用本类的其他构造方法)。
       创建完成了(更多详情参看下面:类加载的顺序)


四、白箱复用和黑箱复用
    1.白箱复用:又叫继承复用,子类会继承父类所有的东西,
        从某种程度上说白箱复用破坏了封装。是一种 is a 的关系。
    例:class Liucy{
          public void teachCpp(){System.out.println("Teach Cpp");}
          public void chimogu(){ }
        }
       class Huxy extends Liucy{}


    2、黑箱复用:又叫组合复用,是一种 has a 的关系。
    例:class Liucy{
          public void teachCpp(){System.out.println("Teach Cpp");}
          public void chimogu(){    }
        }
       class Huxy {
          private Liucy liucy = new Liucy();
          public void teachCpp(){liucy.teachCpp();}
        }
    原则:组合复用取代继承复用原则。
         使我们可以有机会选择该复用的功能。


多态
    1.定义:是指一个对象可以有多种形态,换句话说多态使我们可以把一个子类对象看作是一个父类对象类型
       (例:father A = new child() )。
       多态指的是编译时的类型变化,而运行时类型不变。
    2.多态分为两种:编译时多态和运行时多态。
       编译时类型:定义时类型(主观概念)把它看作什么。
       运行时类型:真实类型(客观概念) 实际上他是什么。
       重载是编译时多态,覆盖是运行时多态。在方法重载的情况下,参数类型决定于编译时类型。
    3.作用:在需要一类对象的共性时,可以很容易的抽取。并且可以屏蔽不同子类对象之间所不关心的差异。
       多态方便写出更通用的代码,以适应需求的不断变化
    4.多态常见的用法:
       (1)、多态用在方法的参数上
       (2)、多态用在方法的返回类型上
    5.运行时多态的三原则:
       (1)、对象不变(改变的是主观认识)
       (2)、对于对象的调用只能限于编译时类型的方法。
       (3)、在程序的运行时,动态类型判定。运行时调用运行时类型,即他调用覆盖后的方法。
    注意:多态时,只有一般方法是动态调用的;而 static 方法和 final 方法依然是静态调用的;属性全是静态调用的。
     如:father A = new child(); 则A的一般方法是child的方法;但A的属性是father的属性。
    同样:child C = new child(); 则 (father)C 的一般方法还是child的方法,但属性是father的属性。


 if(cat instanceof Animal){ ... }  //如果cat属于Animal类型则执行
强制类型转换,在迫不得已的时候再用。因为很容易出错。


 


第八章:高级语言特性
静态变量 static
    一个类只有一个静态变量,跟对象没有关系。被类的所有实例共享;如果子类没有覆盖,也共享父类的静态成员。
    一般直接使用类名来访问 “类名.静态变量名”。可以在没有任何实例时调用。
    在某种意义上类似于全局变量(Java里没有全局变量,这只是C和C++的说法) 
    不能在 static 方法或代码块里访问非 static 成员(变量或方法) 
    能继承和覆盖,但覆盖 static 方法必须也是 static 的方法。


    1.可以修饰属性、方法、初始代码块,成为类变量、静态方法、静态初始化代码块。
       注:初始代码块是在类中而不是在任何方法之内的代码块。
    2.类变量、静态方法、静态初始化代码块与具体的某个对象无关,只与类相关,是全类公有的。 在类加载时初始化。
    3.类加载:JVM通过CLASSPATH找到字节码文件,并将字节码文件中的内容通过I/O流读到JVM并保存的过程
       在虚拟机的生命周期中一个类只被加载一次。
       注:Java命令的作用是启动JVM (Java Virtual Mechine)。
    4.tatic 定义的是一块为整个类共有的一块存储区域,其发生变化时访问到的数据都是经过变化的。
    5.为什么主方法必须是静态的?
       主方法是整个应用程序的入口,JVM只能通过类名去调用主方法。
    6.类变量和静态方法可以在没有对象的情况下用:类名.方法名(或属性名)来访问。
    7.静态方法不可被覆盖(允许在子类中定义同名的静态方法,但是没有多态)
       父类如果是静态方法,子类不能覆盖为非静态方法。父类如果是非静态方法,子类不能覆盖为静态方法。
       争论:静态方法可以覆盖但是没有多态。
       思考:没有多态的覆盖叫覆盖吗?
       在静态方法中不允许调用本类中的非静态成员。
    8.静态初始化代码块只在类加载的时候运行一次,以再也不执行了。所以静态代码块一般被用来初始化静态成员。
    9.不加static为动态初始化代码块,在创建对象时被调用(在构造函数之前)。
    10.最后要注意的一点就是 static 不能修饰局部变量。


什么时候类加载
    第一次需要使用类信息时加载。
    类加载的原则:延迟加载,能不加载就不加载。


触发类加载的几种情况:
    (1)、调用静态成员时,会加载静态成员真正所在的类及其父类。
         通过子类调用父类的静态成员时,只会加载父类而不会加载子类。
    (2)、第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。 
    (3)、加载子类会先加载父类。
        注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。
           例:public static final int a =123;
        但是如果上面的等式右值改成表达式(且该表达式在编译时不能确定其值)时则会加载类。
           例:public static final int a = math.PI
        如果访问的是类的公开静态常量,那么如果编译器在编译的时候能确定这个常量的值,就不会被加载;
        如果编译时不能确定其值的话,则运行时加载




类加载的顺序:
 1.加载静态成员/代码块:
   先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员。
   同一个类里的静态成员/代码块,按写代码的顺序加载。
   如果其间调用静态方法,则调用时会先运行静态方法,再继续加载。同一个类里调用静态方法时,可以不理会写代码的顺序。
   调用父类的静态成员,可以像调用自己的一样;但调用其子类的静态成员,必须使用“子类名.成员名”来调用。
 2.加载非静态成员/代码块:
   先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。
   同一个类里的非静态成员/代码块,按写代码的顺序加载。同一个类里调用方法时,可以不理会写代码的顺序。
   但调用属性时,必须注意加载顺序。一般编译不通过,如果能在加载前调用,值为默认初始值(如:null 或者 0)。
   调用父类的非静态成员(private 除外),也可以像调用自己的一样。
 3.调用构造方法:
   先递归地调用父类的构造方法(Object的最先);默认调用父类空参的,也可在第一行写明调用父类某个带参的。
   再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。
 注意:如果加载时遇到 override 的成员,可看作是所需创建的类型赋值给当前类型。
   其调用按多态用法:只有非静态方法有多态;而静态方法、静态属性、非静态属性都没有多态。
   假设子类override父类的所有成员,包括静态成员、非静态属性和非静态方法。
   由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;
   由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。


成员,包括变量和方法
成员变量,包括实例变量和静态变量
   


final 修饰符
    (最终的、最后的)当final修饰时,不能被改变,不能被继承
    1.final 可以用来修饰类、属性和方法、局部变量。
    2.final 修饰一个属性时,该属性成为常量。
     (1)对于在构造方法中利用final进行赋值时,此时在构造之前系统设置的默认值相对于构造方法失效。
     (2)对于实例常量的赋值有两次机会
         在初始化的时候通过声明赋值
         在构造的时候(构造方法里)赋值
         注:不能在声明时赋值一次,在构造时再赋值一次。
         注意:当final修饰实例变量时,实例变量不会自动初始化为0;但必须给他赋值才能通过编译。
    3.final 修饰方法时,该方法成为一个不可覆盖的方法。这样可以保持方法的稳定性。
       如果一个方法前有修饰词private或static,则系统会自动在前面加上final。
       即 private 和 static 方法默认均为 final 方法。
    4.final 常常和 static、public 配合来修饰一个实例变量,表示为一个全类公有的公开静态常量。
       例: public static final int a = 33;
       在这种情况下属性虽然公开了,但由于是一个静态常量所以并不算破坏类的封装。
    5.final 修饰类时,此类不可被继承,即final类没有子类。
       一个 final 类中的所有方法默认全是final方法。
       final 不能修饰构造方法,构造方法不能被继承更谈不上被子类方法覆盖。


关于 final 的设计模式:不变模式
    1、不变模式:一个对象一旦产生就不可能再修改( string 就是典型的不变模式);
         通过不变模式可以做到对象共享;
    2、池化思想:用一个存储区域来存放一些公用资源以减少存储空间的开销。
         有池的类型:boolean,byte,int,short,long,char,(池范围在-127~128之间)
         (float,double 等小数没有池) 
         例:在String类中有个串池(在代码区)。
         池:堆里的一片独立空间。目的是拿空间换时间,让运算效率更高。
     (1)如果用Stirng str = “abc” 来创建一个对象时,则系统会先在“串池”中寻找有没有“abc”这个字符串
         如果有则直接将对象指向串池中对应的地址,如果没有则在串池中创建一个“abc”字符串。
         所以:String str1 = “abc”;
         String str2 = “abc”;
         Str1 == str2 返回值是ture;他们的地址是一样的。
           也就是说str1和str2都指向了代码空间中相同的一个地址,而这个地址空间保存就是是字符串"abc"
         字符串是不可改变的类型,所以可以共享。所以串池里不会有相同的两个字符串。


     (2)如果用String str = new String("abc")则直接开辟一块内存放"abc"这个字符串。
         所以上面这语句,创建两个"abc",一个在池,一个是对象
         String str2 = new String("abc");
         Str == str2 返回值是false;他们的地址是不一样的。
         即是说str和str2分别指向了堆空间中不同的两个地址,而这两个地址空间保存的都是字符串"abc"


StringBuffer 类(java.lang下的)。
    对于字符串连接
     String str=”1”+”2”+”3”+”4”;
         产生:
          12    123    1234
    这在串池中产生多余对象,而我们真正需要的只有最后一个对象,这种方式在时间和空间上都造成相当大的浪费。
    所以我们应该使用 StringBuffer(线程安全的) 或者 StringBuilder(线程不安全的)来解决
         解决方案:    
      StringBuffer sb = new StringBuffer(“1”);
      Sb.append(“2”);
      Sb.append(“3”);
      Sb.append(“4”);
      S = sb.toString();
    解决后的方案比解决前在运行的时间上快2个数量级。
StringBuilder (1.5版本后出现的)
    线程不安全的,在多线程并发时会出现问题。但仍是字符串合并的首选。
    运行效率比 StringBuffer 快一倍。




abstract 抽象
    1.可用来修饰类、方法
    2.abstract 修饰类时,则该类成为一个抽象类。
       抽象类不可生成对象(但可以有构造方法留给子类使用),必须被继承使用。
       抽象类可以声明,作为编译时类型,但不能作为运行时类型。
       abstract 永远不会和 private,static,final 同时出现。( 因为抽象必须被继承。)
    3.abstract 修饰方法时,则该方法成为一个抽象方法,抽象方法不能有实现;由子类覆盖后实现。
       比较:private void print(){};表示方法的空实现
       abstract void print();表示方法为抽象方法,没有实现
    4.抽象方法从某中意义上来说是制定了一个标准,父类并不实现,留给子类去实现。
       注:抽象类中不一定要有抽象方法,但有抽象方法的类一定是抽象类。
       抽象类可以有抽象方法和非抽象方法。实现抽象类的第一个具体类必须实现其所有抽象方法。
    5.关于抽象类的设计模式:模板方法
        灵活性和不变性


interface 接口
    1、定义:接口不是类,而是一组对类需求的描述,这些类要遵从接口描述的统一格式进行定义。
       定义一个接口用关键字 interface。
       例:public interface a{……}
    2、接口是一种特殊的抽象类。
       在一个接口中,所有的方法为公开、抽象的方法,所有的属性都是公开、静态、常量。
       所以接口中的所有属性可省略修饰符:public static final。也只能用这三个修饰符。
       接口中所有的方法可省略修饰符:public abstract。但这些都是默认存在的。
    3、一个类实现一个接口必须实现接口中所有的方法,否则其为一抽象类。而且实现类的方法需要 public
       实现接口用关键字 implements. 
       所谓实现一个接口就是实现接口中所有的方法。
       例:class Aimple implements A{……..};
    4、一个类除了继承另一个类外(且只能继承一个类),还可以实现多个接口(接口之间用逗号分割)。
       接口可以实现变相的多继承。
       例:class Aimple extends Arrylist implements A,B,C{…}
    5、不能用“new 接口名”来实例化一个接口,但可以声明一个接口。
    6、接口与接口之间可以多继承。
       例:interface face1 extends face2,face3{}
       接口的继承相当于接口的合并
    7、接口的作用
     (1)、间接实现多继承。
           用接口来实现多继承并不会增加类关系的复杂度。因为接口不是类,是在类的基础上的再次抽象。
           父类:主类型     接口:副类型
           典例:父亲(主)  和  老师(副)
     (2)、允许我们为一个类定义出混合类型。
     (3)、通过接口制定标准
            接      口:标准的定义  定义标准
            接口的调用者:标准的使用  使用标准
            接口的实现类:标准的实现  实现标准
        接口的回调:先有接口的使用者,再有接口的实现者,最后把接口的实现者的对象传到接口的使用者中,
                 并由接口的使用者通过接口来调用接口实现者的方法。
        例:sun公司提供一套访问数据库的接口(标准),java程序员访问数据库时针对数据库接口编程。
           接口由各个数据库厂商负责实现。
     (4)、解耦合作用:采用接口可以最大限度的做到弱耦合,将标准的实现者与标准的制定者隔离
         (例:我们通过JDBC接口来屏蔽底层数据库的差异)
    8、接口的编程设计原则
     (1)、尽量针对接口编程(能用接口就尽量用接口)
     (2)、接口隔离原则(用若干个小接口取代一个大接口)
           这样可以只暴露想暴露的方法,实现一个更高层次的封装。
    9、注意点:
     (1)、一个文件只能有一个 public 接口,且与文件名相同。
     (2)、在一个文件中不能同时定义一个 public 接口和一个 public 类。
     (3)、接口与实体类之间只有实现关系,没有继承关系;
          抽象类与类之间只有继承关系没有实现关系。接口与接口之间只有继承关系,且允许多继承。
     (4)、接口中可以不写 public,但在子类实现接口的过程中 public 不可省略。


接口 VS 抽象类
    1、接口中不能有具体的实现,但抽象类可以。
    2、一个类要实现一个接口必须实现其里面所有的方法,而抽象类不必。
    3、通过接口可以实现多继承,而抽象类做不到。
    4、接口不能有构造方法,而抽象类可以。
    5、实体类与接口之间只有实现关系,而实体类与抽象类只有继承关系
      抽象类与接口之间既有实现关系又有继承关系。
    6、接口中的方法默认都是公开抽象方法,属性默认都是公开静态常量,而抽象类不是。


修饰符的使用情况:
(Y表可用;不写表示不可用) 
    修饰符         类       属性        方法      局部变量(所有局部变量都不能用修饰符)
    public        Y         Y          Y         
    protected               Y          Y                                     
    (default)     Y         Y          Y                      
    private                 Y          Y        
    static                  Y          Y                           
    final         Y         Y          Y         Y
    abstract      Y                    Y


访问权限控制:
    修饰符      同一个类    同一个包  (不同包)子类  其他包
    public        Y         Y          Y       Y
    protected     Y         Y          Y   
    (default)     Y         Y            
    private       Y      




Object类
   1、object类是类层次结构的根类,他是所有类默认的父类。
   2、object类中的其中三个方法。
   (1)、finalize()
        当一个对象被垃圾收集的时候,最后会由JVM调用这个对象的finalize方法;
        注意:这个方法一般不用,也不能将释放资源的代码放在这个方法里;只有调用C程序时,才可能要用到


   (2)、toString()
        存放对象地址的哈希码值。
        返回一个对象的字符串表示形式。打印一个对象其实就是打印这个对象toString方法的返回值。
        可以覆盖类的toString()方法,从而打印我们需要的数据。 Public String toString(){……}


    (3)、equals(Object obj)
        用来判断对象的值是否相等。前提是覆盖了equals方法。Object类中的equals方法判断的依然是地址
        注意:String类已经覆盖了equals方法,所以能用equals来判断String对象的值是否相等。
下面是覆盖equals方法的标准流程:
/*************************************************************************/
  public boolean equals(Object obj){
    //第一步:现判断两个对象地址是否相等
    if(this == obj)  return   true;
    //第二步:如果参数是null的话直接返回false;
    if(obj == null)  return   false;
    //第三步:如果两个对象不是同一个类型直接返回false
    if (getClass() != obj.getClass()) return false;
    //?? if(!(this.getClass.getName().equals(o.getClass.getName())) return false;
    //第四步:将待比较对象强转成指定类型,然后自定义比较规则
       Student s = (Student) obj;
    if(s.name.equals(this.name)&&s.age==this.age) return true;
    else return false;
}
/*************************************************************************/
         覆盖equals的原则: 1.自反性(自己=自己)、 2.对称性(y=x则x=y)、
                          3.一致性(多次调用,结果一致)、 4.传递性(A=B,B=C则A=C)。
         非空原则: t1.equals(Null)返回False;(如果t1不等于空)


    (4)、clone 克隆,拷贝
        一个对象参与序列化过程,那么对象流会记录该对象的状态,当再次序列化时,
        会重复序列化前面记录的对象初始状态,我们可以用对象克隆技术来解决这个问题
         1 类中覆盖父类的clone方法,提升protected为public
         2 类实现Cloneable接口
        浅拷贝:只简单复制对象的属性
        深拷贝:如果被复制对象的属性也是一个对象,则还会复制这个属性对象
               这种复制是一个递归的过程,通过对象序列化实现


 


《Java5.0新特性》
 四大点(枚举、泛型、注释、..);5 小点(包装类、静态应用、可变长参数、for-each、..) 
一、自动装箱 和 自动解箱技术
    装箱Autoboxing,也翻译作 封箱;解箱Unautoboxing(也译作 解封)
    1、自动装箱技术:编译器会自动将简单类型转换成封装类型。
    2、编译器会自动将封装类型转换成简单类型
    3、注意:自动装箱和自动解箱只会在必要的情况下执行。
       int 能隐式提升成 long;但Integer不能隐式提升成Long,只能提升成Number
       封装之后就成类,只能由子类转成父类;而Integer和Long是Number的不同子类。
       如: int i; short s; Integer II; Short SS;
         可以 i=SS;   但不可以 II=s; //赋值时,右边的数先转成左边数的对应类型,再进行隐式类型提升




二、静态引用概念:
    用 import static 节省以后的书写。
        引入静态属性 import static java.lang.System.out;
        引入静态方法 import static java.lang.Math.random;
        import static 只能引入静态的方法或属性;不能只引入类或非静态的方法。
    如:import static java.lang.System.*;
     out.println(“a”);  //等于System.out.println("a"); 由于out是一个字段,所以不能更节省了
     如果 import static java.lang.System.gc; 则可以直接在程序中用 gc(); //等于System.gc();




三、可变长参数
    一个方法的参数列表中最多只能有一个可变长参数,而且这个变长参数必须是最后一个参数
    方法调用时只在必要时去匹配变长参数。
/**********变长参数的例子*************************************/
import static java.lang.System.*;//节省书写,System.out直接写out
public class TestVararg {
   public static void main(String... args){
      m();
      m("Liucy");
      m("Liucy","Hiloo");
    }
   static void m(String... s){out.println("m(String...)");}
   //s可以看作是一个字符串数组String[] s
   static void m(){out.println("m()");}
   static void m(String s){out.println("m(String)");}
}  //m(String... s) 是最后匹配的
/**********************************************************/




四、枚举 enum
    1、定义:枚举是一个具有特定值的类型,对用户来说只能任取其一。
       对于面向对象来说时一个类的对象已经创建好,用户不能新生枚举对象,只能选择一个已经生成的对象。
    2、枚举本质上也是一个类。枚举值之间用逗号分开,以分号结束(如果后面没有其他语句,分号可不写)。
    3、枚举分为两种:类型安全的枚举模式和类型不安全的枚举模式
    4、枚举的超类(父类)是:Java.lang.Enum。枚举是 final 类所以不能继承或被继承。但可以实现接口。
       枚举中可以写构造方法,但构造方法必需是私有的,而且默认也是 私有的 private
    5、一个枚举值实际上是一个公开静态的常量,也是这个类的一个对象。
    6、枚举中可以定义抽象方法,但实现在各个枚举值中(匿名内部类的方式隐含继承)
       由于枚举默认是 final 型,不能被继承,所以不能直接用抽象方法(抽象方法必须被继承)
       在枚举中定义抽象方法后,需要在自己的每个枚举值中实现抽象方法。
        
    枚举是编译期语法,编译后生成类型安全的普通类
    values()静态方法,返回枚举的元素数组
    name方法


/**********************************************************/
final class Season1{    //用 final 不让人继承
   private Season1(){}  //用 private 构造方法,不让人 new 出来
   public static final Season1 SPRING=new Season1("春");
   public static final Season1 SUMMER=new Season1("夏");
   public static final Season1 AUTUMN=new Season1("秋");
   public static final Season1 WINTER=new Season1("冬");
   String name; //将"春夏秋冬"设为本类型,而不是24种基本类型,为防止值被更改
   private Season1(String name){
      this.name=name;
    }
   public String getName(){
      return this.name;
}}
/********上面是以前版本时自定义的枚举,下面是新版的枚举写法********/
enum Season2{
   SPRING("春"), SUMMER("夏"),  AUTUMN("秋"),  WINTER("冬");
   String name;
   Season2(String name){ this.name=name; }
   public String getName(){return this.name;}
}//注意:枚举类是有序的;如:Season2.SPRING.ordinal()
/**********************************************************/
/*******关于枚举的例子****************************************/
import static java.lang.System.*;
public class TestTeacher {
    public static void main(String[] args) {
        for(TarenaTeacher t:TarenaTeacher.values()){
            t.teach();
}}}
enum TarenaTeacher{
    LIUCY("liuchunyang"){void teach(){out.println(name+" teach UC");}},
    CHENZQ("chenzongquan"){void teach(){out.println(name+" teach C++");}},
    HAIGE("wanghaige"){void teach(){out.println(name+" teach OOAD");}};
    String name;
    TarenaTeacher(String name){this.name=name;}
    abstract void teach();
}
/**********************************************************/
enum Animals {
    DOG ("WangWang") ,  CAT("meow") ,  FISH("burble");
    String  sound;
    Animals ( String s ) { sound = s; }
}
    class TestEnum {
    static  Animals  a;
        public static void main ( String[] args ) {
            System.out.println ( a.DOG.sound + " " + a.FISH.sound );
}}
/**********************************************************/


五、新型 for 循环 for—each,用于追求数组与集合的遍历方式统一
    1、数组举例:
      int[]  ss  =  {1,2,3,4,5,6};
      for(int i=0; i<ss.length; i++){
         System.out.print(ss[i]);
      }  //以上是以前的 for 循环遍历,比较下面的for—each
      System.out.println();
      for(int i : ss){
          System.out.print(i);
    2、集合举例:
       List  ll  =  new ArrayList();
       for(Object  o : ll ){
          System.out.println(o);
        }
    注:凡是实现了java.lang.Iterable接口的类就能用 for—each遍历
    用 for—each时,不能用list.remove()删除,因为他内部的迭代器无法调用,造成多线程出错。
    这时只能用 for 配合迭代器使用。


六、泛型 Generic 
    1、为了解决类型安全的集合问题引入了泛型。
       泛型是编译检查时的依据,也是编译期语法。
      (编译期语法:编译期有效,编译后擦除,不存在于运行期)
    2、简单的范型应用:集合(ArrayList, Set, Map, Iterator, Comparable) 
       List<String>  l  =  new  ArrayList<String>();
       <String>:表示该集合中只能存放String类型对象。
    3、使用了泛型技术的集合在编译时会有类型检查,不再需要强制类型转换。
       String str  =  l.get(2);  //因为List<String>  l, 所以 Error
       注:一个集合所允许的类型就是这个泛型的类型或这个泛型的子类型。
    4、List<Number>  l  =  new  ArrayList<Integer>  //Error
       List<Integer> l = new ArrayList<Integer>  //Right
        必须类型一致,泛型没有多态
    5、泛型的通配符<?>
       泛型的通配符表示该集合可以存放任意类型的对象。但只有访问,不可以修改。
        static void print( Cllection<?> c ){
         for( Object o : c )
         out.println(o);
        }
    6、带范围的泛型通配符
      泛型的声明约定T表示类型,E表示元素
        (1)、上界通配符,向下匹配:<?  extends  Number>    表明“extends”或“implements”,认为是 final 的
             表示该集合元素可以为Number类型及其子类型(包括接口),例如 Number,Integer,Double
             此时集合可以进行访问但不能修改。即不允许调用此对象的add,set等方法;但可以使用 for-each 或 get.
        (2)、下界通配符,向上匹配:<?  super  Number>
             表示该集合元素可以为Number类型及其父类型,直至 Object。
             可以使用 for-each,add,addAll,set,get等方法
        (3)、接口实现:<? extends Comparable>
             表示该集合元素可以为实现了Comparable接口的类
    7、泛型方法
        在返回类型与修饰符之间可以定义一个泛型方法,令后面的泛型统一
        这里只能用 extends 定义,不能用 super ;后面可以跟类(但只能有一个,且要放在首位)其余是接口
        符号只有   &    //“&”表示“与”;逗号表示后面的另一部分
        静态方法里面,不能使用类定义的泛型,只能用自己定义的;因为静态方法可以直接调用;
        所以普通方法可以使用类定义的及自己定义的泛型
         public static <T> void copy(T[] array,Stack<T> sta){……}
         public static <T,E extends T> void copy (T[] array,Stack<E> sta){…..}
         public static <T extends Number&Comparable> void copy(List<T> list,T[] t);
    8、不能使用泛型的情况:
      (1)、带泛型的类不能成为 Throwable 类和 Exception 类的子类
            因为cathc()中不能出现泛型。
      (2)、不能用泛型来 new 一个对象
            如:T t = new T();
      (3)、静态方法不能使用类的泛型,因为静态方法中没有对象的概念。
    9、在使用接口的时候指明泛型。
     class Student  implements  Comparable<Student>{…….}
     
    10、泛型类
    /********************************************************************/
   class MyClass<T>{
      public void m1(T t){}
      public T m2(){
        return null;
   }}
    /********************************************************************/


 


第九章:
内部类(nested classes) (非重点)
    1.定义:定义在其他类中的类,叫内部类(内置类)。内部类是一种编译时的语法,编译后生成
       的两个类是独立的两个类。内部类配合接口使用,来强制做到弱耦合(局部内部类,或私有成员内部类)。
    2.内部类存在的意义在于可以自由的访问外部类的任何成员(包括私有成员),但外部类不能直接访问内部类的
       成员。所有使用内部类的地方都可以不使用内部类;使用内部类可以使程序更加的简洁(但牺牲可读性),
       便于命名规范和划分层次结构。
    3.内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖。
    4.内部类可用 static,protected 和 private 修饰。(而外部类只能使用 public 和 default)。
    5.内部类的分类:成员内部类、局部内部类、静态内部类、匿名内部类。
          (注意:前三种内部类与变量类似,可以对照参考变量)
        ① 成员内部类(实例内部类):作为外部类的一个成员存在,与外部类的属性、方法并列。
            成员内部类可看作外部类的实例变量。
            在内部类中访问实例变量:this.属性
            在内部类访问外部类的实例变量:外部类名.this.属性。
            对于一个名为outer 的外部类和其内部定义的名为inner 的内部类。
                编译完成后出现outer.class 和outer$inner.class 两类。
            不可以有静态属性和方法(final 的除外),因为 static 在加载类的时候创建,这时内部类还没被创建
            如果在外部类的外部访问内部类,使用out.inner.***
        建立内部类对象时应注意:
            在创建成员内部类的实例时,外部类的实例必须存在:
            在外部类的内部可以直接使用inner s=new inner();  因为外部类知道 inner 是哪个类。
            而在外部类的外部,要生成一个内部类对象,需要通过外部类对象生成。
                Outer.Inner in = new Outer().new Inner();
                相当于:Outer out = new Outer(); Outer.Inner in = out.new Inner();
            错误的定义方式:Outer.Inner in=new Outer.Inner()。
        ② 局部内部类:在方法中定义的内部类称为局部内部类。
            类似局部变量,不可加修饰符 public、protected 和 private,其范围为定义它的代码块。
            可以访问外部类的所有成员,此外,还可以访问所在方法中的 final 类型的参数和变量。
            在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。
            要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。
            局部内部类不能声明接口和枚举。
        ③ 静态内部类:(也叫嵌套类)
            静态内部类定义在类中,在任何方法外,用 static 定义。
            静态内部类能直接访问外部类的静态成员;
            不能直接访问外部类的实例成员,但可通过外部类的实例(new 对象)来访问。
            静态内部类里面可以定义静态成员(其他内部类不可以)。
            生成(new)一个静态内部类不需要外部类成员,这是静态内部类和成员内部类的区别。
                静态内部类的对象可以直接生成: Outer.Inner in=new Outer.Inner();
                对比成员内部类:Outer.Inner in = Outer.new Inner();
                而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。
            静态内部类不可用 private 来进行定义。例子:
对于两个类,拥有相同的方法:
/*************************************************************************/
   /*class People{void run();}
   interface Machine{void run();}
       此时有一个robot类:
   class Robot extends People implement Machine.
       此时run()不可直接实现。*/
interface Machine{   void run();}
class Person{   void run(){System.out.println("run");}}


class Robot extends Person{
   private class MachineHeart implements Machine{
      public void run(){System.out.println("heart run");}
    }
   public void run(){System.out.println("Robot run");}
   Machine getMachine(){return new MachineHeart();}
}


class Test{
   public static void main(String[] args){
      Robot robot=new Robot();
      Machine m=robot.getMachine();
      m.run();
      robot.run();
}}
/*************************************************************************/
            注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。
            这是唯一一种必须使用内部类的情况。
            用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
        ④ 匿名内部类:
             【1】匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
             【2】不同的是他是用一种隐含的方式实现一个接口或继承一个类,而且他只需要一个对象
             【3】在继承这个类时,根本就没有打算添加任何方法。
             【4】匿名内部类大部分情况都是为了实现接口的回调。
        注:匿名内部类一定是在 new 的后面
           其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。
            因其为局部内部类,那么局部内部类的所有限制都对其生效。
            匿名内部类是唯一一种无构造方法类。
            注:这是因为构造器的名字必须和类名相同,而匿名内部类没有类名。
            匿名内部类在编译的时候由系统自动起名Out$1.class。
            因匿名内部类无构造方法,所以其使用范围非常的有限。
            结尾需加上分号。


匿名内部类的例子:
    /*************************************************************************/
public class test{
  public static void main(String[] args){
     B.print(new A(){
    public void getConnection(){ System.out.println("Connection....");}
          });
}}


interface A{ void getConnection();}
class B{
   public static void print(A a){ a.getConnection();}
}
    /*************************************************************************/


    枚举和接口可以在类的内部定义,但不能在方法内部定义。
    接口里面还可以定义多重接口和类。
    类放在什么位置,就相当于什么成员。


内部类的用途:
    封装类型:把标准公开,把标准的实现者作为内部类隐藏起来,
            强制要求使用者通过标准访问标准的实现者,从而强制做到弱耦合!
    直接访问外部类的成员
    配合接口,实现多继承,当父类和接口方法定义发生冲突的时候,就必须借助内部类来区分
    模板回调
    
从内部类继承:
    由于直接构造实例内部类时,JVM会自动使内部类实例引用它的外部类实例。
    但如果下面Sample类通过以下方式构造对象时:Sample s = new Sample();
    JVM无法决定Sample实例引用哪个Outer实例,为了避免这种错误的发生,在编译阶段,java编译器会要求Sample类的构造方法必须通过参数传递一个Outer实例的引用,然后在构造方法中调用super语句来建立Sample实例与Outer实例的关联关系。
/*************************************************************************/
public class Sample extends Outer.Inner{
  //public Sample(){} //编译错误
  public Sample(Outer o){  o.super();  }
  public static void main(String args[]){
     Outer outer1=new Outer(1);
     Outer outer2=new Outer(2);
     Outer.Inner in=outer1.new Inner();
     in.print();
     Sample s1=new Sample(outer1);
     Sample s2=new Sample(outer2);
     s1.print();  //打印a=1
     s2.print();  //打印a=2
}}
/*************************************************************************/
  
内部接口:
    在一个类中也可以定义内部接口
    在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。
    在接口中还可以定义接口,这种接口默认也是public static 的,如Map.Entry就是这种接口


 


第八章 Collection FrameWork
《集合框架》
     ((Iterator接口  ←-  Iterable接口 )) ← Collection接口
                                                 ↑                 
          ┌--------------------------------┬---------------┐    
       Set接口                           List接口         Queue接口
          ↑                               ↑                ↑      
     ┌----------┐            ┌-----------+---------┐ ┌-----------┐           
  HashSet   SortedSet接口    Vector    ArrayList   LinkedList   PriorityQueue
                 ↑ 
               TreeSet
          
     Map接口
       ↑
     ┌----------┐ 
   HashMap  SortedMap接口
                 ↑ 
              TreeMap


各接口的主要方法:
   Iterable:     +iterator() 
   Iterator:     +hasNext()    +next()        +remove() 
   Collection:   +add()        +remove()      +clear()         +isEmpty() +size()  +contains() 
   List:         +get()        +set()         +remove() 
   Queue:        +element()    +offer()       +peek()          +poll() 
   Set:
     SortedSet:  +comparator() +first()       +last()          +headSet() +tailSet() 
   Map:          +clear()      +containsKey() +containsValue() +get()     +keySet() 
                 +isEmpty()    +remove()      +put()会替换重复键 +size()    +values() 
     SortedMap:  +comparator() +firstKey()    +lastKey()       +headMap() +tailMap()


一、集合(容器,持有对象):是一个用于管理其他多个对象的对象,且只能保存对象的引用,不是放对象。
   1、Collection:   集合中每一个元素为一个对象,这个接口将这些对象组织在一起,形成一维结构。
   2、List:         有序、可重复。
      ArrayList:    数组。查询快,增删慢。(List是链表)
      Vector:       线程安全,但效率很差(现实中基本不用)
   3、Set:          无序,且不可重复(不是意义上的重复)。(正好与List 对应)
      HashSet:      用 hashCode() 加 equals() 比较是否重复
      SortedSet:    会按照数字将元素排列,为“可排序集合”默认升序。
      TreeSet:      按二叉树排序(效率非常高); 按Comparable接口的 compareTo() 比较是否重复
   4、Map:          其中每一个元素都是一个键值对( Key-Value)。键不能重复。可有一个空键。
      SortedMap:    根据 key 值排序的 Map。
      HashMap:      用 hashCode() 加 equals() 比较是否重复
   5、Queue:         队列:先进先出。
      PriorityQueue:   优先队列:元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序


注意:在“集合框架”中,Map 和Collection 没有任何亲缘关系。 
    Map的典型应用是访问按关键字存储的值。它支持一系列集合操作,但操作的是键-值对,而不是独立的元素
    因此 Map 需要支持 get() 和 put() 的基本操作,而 Set 不需要。


    
《常用集合列表》
'         存放元素   存放顺序      元素可否重复      遍历方式   排序方式       各自实现类
List      Object    有序         可              迭代       (2)      ArrayList, TreeSet
Set       Object    无序         不可             迭代      SortedSet    HashSet
SortedSet Object    无序         不可             迭代      已排序        TreeSet
Map       (1)       Key无序      Key不可,value可  对Key迭代  SortedMap    HashMap
SortedMap (1)       无序,有排序   Key不可,value可  对Key迭代  已对键值排序   TreeMap
     (1)Object(key)—Object(value); 
     (2)Collections.sort();


注:以上有序的意思是指输出的顺序与输入元素的顺序一致
    HashSet、HashMap通过hashCode(),equals()来判断重复元素
    在java中指定排序规则的方式只有两种:1、实现java.util包下的Comparator接口
                                  2、实现java.lang包下的Comparable接口


二、迭代器:Iterator
    1、使用Iterator接口方法,您可以从头至尾遍历集合,并安全的从底层Collection中除去元素
    2、remove() 由底层集合有选择的支持。底层集合支持并调用该方法时,最近一次next()返回的元素将被删
    3、Collection 接口的iterator() 方法返回一个Iterator
    4、Iterator中的hasNext()用于判断元素右边是否有数据,返回True则有。然后就可以调用next()动作。
    5、Iterator中的next()方法会将游标移到下一个元素,并返回它所跨过的元素。(通常这样遍历集合) 
    6、用于常规Collection 的Iterator 接口代码如下:
    /*迭代遍历*/
      List l = new ArrayList();
     Iterator it = l.iterator();
     while(it.hasNext()){
         Object obj = it.next();
         System.out.println(obj);
        }
注:工具类是指所有的方法都是公开静态方法的类。
    Java.util.collections就是一个工具类;


三、对集合的排序
    1、我们可以用Java.util.collections中的sort(List l)方法对指定的List集合进行排序;
    但是如果List中存放的是自定义对象时,这个方法就行不通了,必须实现Comparable接口并且指定排序规则。
        这里我们再来看一下sort(List l)方法的内部实现;
/**********************************************************/
class Collections2{
   public static void sort(List l){
      for(int i=0;i<l.size()-1;i++){
     for(int j=i+1;j<l.size();j++){
        Object o1 = l.get(i);
        Object o2 = l.get(j);
        Comparable c1 = (Comparable)o1;
        Comparable c2 = (Comparable)o2;
        if(c1.compareTo(c2)>0){
          Collections.swap(l,i,j);
}}}}}  //其实用的算法就是个冒泡排序。
/******************************************************/


    2、实现Java.lang.Comparable接口,其实就是实现他的 public int compareTo(Object obj)方法;
       比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
          其规则是当前对象与obj 对象进行比较,其返回一个 int 值,系统根据此值来进行排序。
          如当前对象 > obj,则返回值>0;
          如当前对象 = obj,则返回值=0;
          如当前对象 < obj,则返回值<0。


    注意:String类型已经实现了这个接口,所以可以直接排序;
    /******************************************************/
class Student implements Comparable{
   private String name;
      private int age;
      public Student(String name, int age) {
     this.name = name;
     this.age = age;
    }
      public int compareTo(Object obj) {
     Student s = (Student)obj;
     return s.age-this.age;
}}
    /******************************************************/


四、ArrayList和LinkedList集合
    1、ArrayList 底层是object 数组,所以ArrayList 具有数组的查询速度快的优点以及增删速度慢的缺点。
      Vector 底层实现也是数组,但他是一个线程安全的重量级组件。
    2、而在LinkedList 的底层是一种双向循环链表。
      在此链表上每一个数据节点都由三部分组成:
        前指针(指向前面的节点的位置)、 数据、 后指针(指向后面的节点的位置)。
      最后一个节点的后指针指向第一个节点的前指针,形成一个循环。
    3、双向循环链表的查询效率低但是增删效率高。所以LinkedList 具有查询效率低但增删效率高的特点。
    4、ArrayList 和LinkedList 在用法上没有区别,但是在功能上还是有区别的。
      LinkedList 经常用在增删操作较多而查询操作很少的情况下:队列和堆栈。
      队列:先进先出的数据结构。
      堆栈:后进先出的数据结构。
     (堆栈就是一种只有增删没有查询的数据结构)
      注意:使用堆栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。
    
      LinkedList 提供以下方法:(ArrayList 无此类方法)
      addFirst(); +removeFirst(); +addLast(); +removeLast();
        在堆栈中,push 为入栈操作,pop 为出栈操作。
      Push 用addFirst();pop 用removeFirst(),实现后进先出。
        用isEmpty()--其父类的方法,来判断栈是否为空。
        在队列中,put 为入队列操作,get 为出队列操作。
      Put 用addFirst(),get 用removeLast()实现队列。
      List 接口的实现类 Vector 与ArrayList 相似,区别是Vector 是重量级组件,消耗的资源较多。
        结论:在考虑并发的情况下用Vector(保证线程的安全)。
        在不考虑并发的情况下用ArrayList(不能保证线程的安全)。
    
    5、面试经验(知识点):
      java.util.stack(stack 即为堆栈)的父类为Vector。可是stack 的父类是最不应该为Vector 的。
      因为Vector的底层是数组,且Vector有get方法(意味着它可能访问任意位置的元素,很不安全)。
      对于堆栈和队列只能用push 类和get 类。(这是早期的某个java编写工程师的失误造成) 
      Stack 类以后不要轻易使用。实现堆栈一定要用LinkedList。
        (在JAVA1.5 中,collection 有queue 来实现队列。)


五、HashSet集合
   1、HashSet是无序的,没有下标这个概念。HashSet集合中元素不可重复(元素的内容不可重复);
   2、HashSet 底层用的也是数组。
   3、HashSet如何保证元素不重复?Hash算法和equals方法。
      当向数组中利用add(Object obj)添加对象的时候,系统先找对象的hashCode:
      int hc=obj.hashCode(); 返回的hashCode 为整数值。
      int I=hc%n;(n 为数组的长度),取得余数后,利用余数向数组中相应的位置添加数据,以n 为6 为例,
      如果I=0则放在数组a[0]位置,如果I=1则放在数组a[1]位置。
      如果equals()返回true,则说明数据重复。如果equals()返回false,则再找其他的位置进行比较。
      这样的机制就导致两个相同的对象有可能重复地添加到数组中,因为他们的hashCode 不同。
      如果我们能够使两个相同的对象具有相同hashcode,才能在equals()返回为真。
        
      在实例中,定义student 对象时覆盖它的hashcode。
      因为String类会自动覆盖,所以比较String 类的对象时,不会出现相同的string 对象的情况。
      现在,在大部分的JDK 中,都已经要求覆盖了hashCode。 
    结论:如将自定义类用hashSet 来添加对象,一定要覆盖hashcode()和equals(),
      覆盖的原则是保证当两个对象hashcode 返回相同的整数,而且equals()返回值为True。
      如果偷懒,直接将hashCode方法的返回值设为常量;虽然结果相同,但会多次地调用equals(),影响效率。
      我们要保证相同对象的返回的hashCode 一定相同,也要保证不相同的对象的hashCode 尽可能不同
      (因为数组的边界性,hashCode 还是有极微几率相同的)。




六、TreeSet集合
    1、TreeSet是SortedSet的实现类TreeSet通过实现Comparable接口的compareTo来实现元素不重复。
    2、TreeSet由于每次插入元素时都会进行一次排序,因此效率不高。
    3、java.lang.ClassCastException是类型转换异常。
    4、在我们给一个类用CompareTo()实现排序规则时
    5、从集合中以有序的方式抽取元素时,可用TreeSet,添加到TreeSet的元素必须是可排序的。
      “集合框架”添加对Comparable 元素的支持。
      一般说来,先把元素添加到HashSet,再把集合转换为TreeSet 来进行有序遍历会更快。


七、Map
   1、HashMap集合
     (1)HashMap就是用hash算法来实现的Map
     (2)在实际开发中一般不会用自定义的类型作为Map的Key。做Key的无非是八中封装类。
     (3)HashMap的三组操作:
       【1】改变操作,允许从映射中添加和除去键-值对。键和值都可以为null。
           不能把Map作为一个键或值添加给自身。
         –Object put(Object key, Object value) 
         –Object remove(Object key) 
         –void clear() 
       【2】查询操作允许您检查映射内容:
         –Object get(Object key)
         –intsize()
         –boolean isEmpty()
       【3】最后一组方法允许您把键或值的组作为集合来处理。
         –public Set KeySet(); 
         –public Collection values() 
    (4)HashMap和HashTable的区别等同于ArrayList和Vector的区别。
       只不过HashTable中的Key和Value不能为空,而HashMap可以。
    (5)HashMap底层也是用数组,HashSet底层实际上也是HashMap,HashSet类中有HashMap属性(查API)。
       HashSet 实际上为(key.null)类型的HashMap。有key 值而没有value 值。


   2、HashMap 类和TreeMap 类
     ?集合框架提供两种常规Map 实现:HashMap和TreeMap。
     ?在Map 中插入、删除和定位元素,HashMap 是最好选择。
     ?如果要按顺序遍历键,那么TreeMap 会更好。
     ?根据集合大小,先把元素添加到HashMap,再把这种映射转换成一个用于有序键遍历的TreeMap 可能更快。
     ?使用HashMap 要求添加的键类明确定义了hashCode() 实现。
     ?有了TreeMap 实现,添加到映射的元素一定是可排序的
     ?HashMap和TreeMap 都实现Cloneable 接口。




异常 Exception
1.概念: JAVA将所有的错误封装成为一个对象,其根本父类为Throwable。异常处理可以提高我们系统的容错性。
  Throwable 有两个子类:Error 和Exception。


  Error:一般是底层的不可恢复的错误。
           Object 
             ↑ 
          Throwable 
             ↑ 
        ┌---------┐
     Error      Exception 
                   ↑                非RuntimeException
           ┌-----------------------┬---------------┐ 
        RuntimeException   InterruptedException  IOException
              ↑ 
        ┌----------------------┬-----------...
   NullpointerException  ArrayIndexOutOfBoundsException


2.Exception分类:
   Runtime exception(未检查异常)和 非Runtime exception(已检查异常)。
   未检查异常是因为程序员没有进行必要的检查,因为他的疏忽和错误而引起的异常。可避免。
    几个常见的未检查异常:
     ①java.lang.ArithmeticException           //如:分母为0; 
     ②java.lang.NullPointerException          //如:空指针操作; 
     ③java.lang.ArrayIndexoutofBoundsExceptio //如:数组越界;数组没有这元素;
     ④java.lang.ClassCastException            //如:类型转换异常; 
    已检查异常是不可避免的,对于已检查异常必须处理。


3、异常对象的传递。
   当一个方法中出现了异常而又没做任何处理,则这个方法会返回该异常对象。
   异常依次向上层调用者传递,直到传到JVM,虚拟机退出。  (用一句话说就是沿着方法调用链反向传递)
   应该在合适的位置处理异常,不让它一直上抛到 main 方法,应遵循的规则:
      谁知情谁处理,谁负责谁处理,谁导致谁处理。


4、如何来处理异常(这里主要是针对已检查异常)
    【1】throws 消极处理异常的方式。
        方法名(参数表)throws 后面接要往上层抛的异常。
        表示该方法对指定的异常不作任何处理,直接抛往上一层。
    【2】积极处理方式 try、catch
        try {可能出现错误的代码块} catch(exception e){进行处理的代码} ;
         一个异常捕获只会匹配一次 try,catch.
         一个异常一旦被捕获就不存在了。
        catch 中要求必须先捕获子类异常再捕获父类异常。 catch 要求有零到多个,但异常的名字不能重复。
    【3】finally (紧接在 catch 代码块后面)
        finally 的代码块是无论如何都会被执行的(除非虚拟机退出),所以里面一般写释放资源的代码。
        return 也无法阻止 finally,但System.exit(0):退出虚拟机则可以。
     
      有 finally 的 try/catch 流程
      如果 try 块失败了,抛出异常,程序马上转移到 catch 块,完成后执行 finally 块,再执行其后程序。
      如果 try 块成功,程序跳过 catch 块并去到 finally 块,finally 块完成后,继续执行其后程序。
      如果 try 或 catch 块有 return 语句,finally 还是会执行;程序会跳去执行 finally 块然后再回到 return 语句
/********** 使用try/catch的例子 ********************************************/
class MyException{
     void myException(){
        System.out.println("MyException");
//        if(1==1)return;   //测试没有异常时
        System.out.println(1/0);
    }     
    public static void main(String[] args) {
        MyException me = new MyException();
        try{
            System.out.println("try");
//            if(1==1)return;
            me.myException();
        }catch(ArithmeticException ae){
            System.out.println("cath");
        }finally{
            System.out.println("finally");
}}}
/*************************************************************************/


5、自定义异常(与一般异常的用法没有区别)
class MyException extends Exception{
   public MyException(String message){ super(message);}
   public MyException(){}
}


6、如何控制 try 的范围
   根据操作的连动性和相关性,如果前面的程序代码块抛出的错误影响了后面程序代码的运行,
   那么这个我们就说这两个程序代码存在关联,应该放在同一个 try 中。


7、不允许子类比父类抛出更多的异常。


8、断言:只能用于代码调试时用。
  一般没什么用,只在测试程序时用
    /********* 断言的列子 ******************************************************/
public class TestAssertion {
   public static void main(String[] args){
      int i = Integer.parseInt(args[0]);
      assert i==1:"ABCDEFG";   //格式: assert (布尔表达式/布尔值) : String;
      /*断言语句:(表示断言该boolean语句返回值一定为真,如果断言结果为false就会报Error错误) 
        “ : ”后面跟出现断言错误时要打印的断言信息。 */
      System.out.println(i);
}}


//javac -source 1.4 TestAssertion.java     //表示用1.4的新特性来编译该程序。
//java -ea TestAssertion 0                 //表示运行时要用到断言工具
    /*************************************************************************/


 


Module 10  Reflection And Annotation
一、 Reflection 反射
  1、反射主要用于工具的开发。所有的重要Java技术底层都会用到反射。反射是一个底层技术。
     是在运行时动态分析或使用一个类的工具(是一个能够分析类能力的程序)
  2、反射使我们能够在运行时决定对象的生成和对象的调用。
  3、Class
    (1)定义:在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。
            虚拟机利用类型标识选用相应的方法执行。
            可以通过专门的类访问这些信息。保存这些信息的类被称为Class(类类)
    (2)类对象(类类:用于存储和一个类有关的所有信息),用来描述一个类的类。
        类信息通过流读到虚拟机中并以类对象的方式保存。
        一个类的类对象在堆里只有一个。
        注:简单类型也有类对象。
        在反射中凡是有类型的东西,全部用类对象来表示。
  4.获得类对象的3种方式:
    (1)通过类名获得类对象 Class c1 = String.class; //类.Class;
    (2)通过对象获得类对象 Class c2 = s.getClass(); //类对象.getClass();
    (3)通过Class.forName(“类的全名”)来获得类对象    //Class.forName(包名.类名);
        Class c3 = Class.forName(“Java.lang.String”);//这会强行加载类到内存里,前两种不加载
        注:第三种方式是最常用最灵活的方式。第三种方式又叫强制类加载。
  5.java.lang.reflect .Field       对象,描述属性信息。
  6.java.lang.reflect .Constructor 描述构造方法信息。
  7.java.lang.reflect .Method      描述方法信息。
  8.在反射中用什么来表示参数表?
     Class[] cs2 = {StringBuffer.class};//表示一个参数表
     Constructor c = c1.getConstructor(cs2);//返回一个唯一确定的构造方法。
     Class[] cs2 = {String.class,int.class}
     Method m = c1.getMethod(methodName,cs3);
  9.可以通过类对象来生成一个类的对象。
     Object o = c.newInstance();
  10、反射是一个运行时的概念。反射可以大大提高程序的通用性。
一个关于反射的例子:
/*********************************************************/
import java.lang.reflect.*;
public class TestClass2 {
    public static void main(String[] args) throws Exception{
        //0.获得在命令行输入的类的类对象
        Class  c=Class.forName(args[0]);//需处理异常(ClassNotFoundException)
        //Object o=c.newInstance();
        //1.得到构造方法对象
        Class[] cs1={String.class};
        Constructor con=c.getConstructor(cs1);
        //2.通过构造方法对象去构造对象
        Object[] os1={args[1]};
        Object o=con.newInstance(os1);
        //3.得到方法对象
        String methodName=args[2];
        Class[] cs2={String.class};
        Method m=c.getMethod(methodName,cs2);
        //4.调用方法
        Object[] os2={args[3]};
        m.invoke(o,os2);


        /* 以上相当于知道类的情况时,这样直接用
         Student s=new Student("Liucy");
         s.study("CoreJava"); */
}}
/**********************************************************/


    下面是用反射调用私有方法的一个例子:
    /**********************************************************/
public class TestClass2 {
    public static void main(String[] args) throws Exception{
        System.out.println("请输入需要读取的类名:");
        Scanner scanner = new Scanner(System.in);
        String str = scanner.next();  //输入“AA”
        Class c = Class.forName(str);
        Method[] m = c.getDeclaredMethods();//读取它的全部方法
        Method m1 = m[0];//拿其中的第一个方法
        m1.setAccessible(true);//把private的属性设成可访问,否则不能访问
        AA b = new AA();
        m1.invoke(b);
}}


class AA{
    private void print(){
        System.out.println("print()");
}}
     /**********************************************************/
要求学会的内容:
    概念:类类,类对象,类的对象,对象类(Object类)
         类对象:Class,指向类的对象。
         类对象包括:属性对象 Feild,方法对象Method,构造方法对象Constructor。
    类对象能做什么:探查类定义的所有信息:父类,实现的接口,所有属性及方法,以及构造方法。
                 类的修饰符,属性以及方法的修饰符,方法的返回类型,方法的
                 ...
                 构造一个类的对象(类对象.newInstance())
                 强制修改和访问一个对象的所有属性(包括私有属性)
                 调用一个对象的方法(普通方法,静态方法)
                   Method.invoke(方法所在的对象(类对象,null),给方法传参数
                 ...
                 构造数组的另一种用法(动态构造数组,不定长度)




注释 Annotation
1、定义:Annotation描述代码的代码(给机器看的)。
     区别:描述代码的文字,给人看的,英语里叫Comments。
     任何地方都可以使用Annotation注释,它相当于一段代码,可用来作自动检测。
     一个注释其实是一种类型(类class,接口interface,枚举enum,注释Annotation),注释本质上是接口。
     定义注释 public @interface Test{},注释都是Annotation接口的子接口
2、注释的分类:
  (1)、标记注释:没有任何属性的注释。@注释名
  (2)、单值注释:只有一个属性的注释。@注释名(value="***")
       在单值注释中如果只有一个属性且属性名就是value,则"value="可以省略。
  (3)、多值注释:有多个属性的注释。多值注释又叫普通注释。
       @注释名(多个属性附值,中间用逗号隔开)
3、内置注释(java.lang):
  (1)、@Override(只能用来注释方法)
       表示一个方法声明打算重写超类中的另一个方法声明。
       如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。
  (2)、@Deprecated
       有 @Deprecated 注释的程序元素,不鼓励程序员使用,通常是因为它很危险或存在更好的选择。
       在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。 
  (3)、@SuppressWarnings(抑制警告,该注释效果与版本相关)
        指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
4、自定义注释
  (1)、定义注释类型
        自定义注释默认就是java.lang.annotation.Annotation接口的子接口。注释本质上就是一个接口。
        public @interface TestAnnotation {}
  (2)、为注释添加注释
        import java.lang.annotation.*;
        @Documented //能在帮助文档里出现
        @Inherited  //能否被继承下去
        @Retention(value = {RetentionPolicy.RUNTIME}) //注释该注释运行时仍然保留
         //@Retention默认是CLASS(保留到编译期),最短期是SOURCE(原代码级,编译时丢弃) 
        @Target(value={ElementType.METHOD,ElementType.FIELD})
        /*用来注释该注释能用来注释方法和属性,还可以定义它用来注释其他的,如类、注释、构造方法等等*/
        /*如果不写Target,默认是可以注释任何东西*/
        public @interface TestAnnotation {...}
  (3)、为注释添加属性方法
        import java.lang.annotation.*;
        @Target(value={ElementType.TYPE})
        public @interface TestAnnotation {
        //如果一个注释不是标记注释,则还要定义属性;这属性同时也是方法,但不可能有参数,只可以有默认值
        String parameter() default "liucy";
        //给属性方法parameter添加一个默认值"liucy"
        //parameter()括号里不能写其他东西,类型只能是24种基本类型之一
        //24种类型:8种基本数据类型、String、枚举、注释、Class、以及它们的一维数组        
        }
        
        @TestAnnotation("haha")
        public class MyClass{...}
5、注释的注释:(元注释 meta annotation)
   都在 java.lang. annotation 包中
  (1)、Target:指示注释类型所适用的程序元素的种类。
        一个注释只能出现在其该出现的位置,Target是给注释定位的。
        例:@Target(value = {ElementType.METHOD}); //说明该注释用来修饰方法。
  (2)、Retention:指示注释类型的注释要保留多久。
        如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS。
        例:Retention(value = {RetentionPolicy.xxx})
        当x为CLASS表示保留到类文件中,运行时抛弃。
        当x为RUNTIME表示运行时仍保留(最常用)
        当x为SOURCE时表示编译后丢弃。
  (3)、Documented:指示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化。
        应使用此类型来注释这些类型的声明:其注释会影响由其客户端注释的元素的使用。
  (4)、Inherited:指示注释类型被自动继承。
        如果在注释类型声明中存在 Inherited 元注释,并且用户在某一类声明中查询该注释类型,
        同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。
   注:在注释中,一个属性既是属性又是方法。


使用注释
/*********************************************/
Class c = Class.forName(args[0]);
Object o = c.newInstance();
Method[] ms = c.getMethods();
    for(Method m:ms){
    //判断m方法上有没有Test注释
        if (m.isAnnotationPresent(Test.class)){
    //得到m之上Test注释parameter属性值
            Test t=m.getAnnotation(Test.class);
            String parameter=t.parameter();
            m.invoke(o,parameter);
}}
/*********************************************/


 


图型界面(非重要:不常用、难学)
1、Awt:抽象窗口工具箱,它由三部分组成:
   ①组件:界面元素;
   ②容器:装载组件的容器(例如窗体);
   ③布局管理器:负责决定容器中组件的摆放位置。
2、图形界面的应用分四步:
   ① 选择一个容器:
     ⑴window:带标题的容器(如Frame);
     ⑵Panel:面板通过add()向容器中添加组件。
       注:Panel不能作为顶层容器。
       Java 的图形界面依然是跨平台的。但是调用了窗体之后只生成窗体;必须有事件的处理,关闭按钮才工作。
   ②设置一个布局管理器:用setLayout();
   ③向容器中添加组件;
     jdk1.4用getContentPare()方法添加主件。
   ③ 添加组件的事务处理。
     Panel 也是一种容器:但是不可见的,很容易忘记设置它们的可见性。
     Panel pan=new Panel;
     Fp.setLayout(null);//表示不要布局管理器。
3、五种布局管理器:
  (1)、Flow Layout(流式布局):按照组件添加到容器中的顺序,顺序排放组件位置。
        默认为水平排列,如果越界那么会向下排列。排列的位置随着容器大小的改变而改变。
        FlowLayout layout = new FlowLayout(FlowLayout.LEFT);//流式布局,可设对齐方式
        Panel 默认的布局管理器为Flow Layout。
  (2)、BorderLayout:会将容器分成五个区域:东西南北中。
        语句:Button b1=new Botton(“north”);//botton 上的文字
        f.add(b1,”North”);//表示b1 这个botton 放在north 位置
        f.add(b1, BorderLayout.NORTH);//这句跟上句是一样的效果,不写方位默认放中间,并覆盖
        注:一个区域只能放置一个组件,如果想在一个区域放置多个组件就需要使用Panel 来装载。
        Frame 和Dialog 的默认布局管理器是Border Layout。
  (3)、Grid Layout(网格布局管理器):将容器生成等长等大的条列格,每个块中放置一个组件。
        f.setLayout GridLayout(5,2,10,10)//表示条列格为5 行2 列,后面为格间距
  (4)、CardLayout(卡片布局管理器):一个容器可以放置多个组件,但每次只有一个组件可见(组件重叠)。
        使用first(),last(),next()可以决定哪个组件可见。可以用于将一系列的面板有顺序地呈现给用户。
  (5)、GridBag Layout(复杂的网格布局管理器):
        在Grid中可指定一个组件占据多行多列,GridBag的设置非常烦琐。
   注:添加滚动条:JScrollPane jsp = new JScrollPane(ll);
4、常用的组件:
        (1)、JTextArea:用作多行文本域
        (2)、JTextField:作单行文本
        (3)、JButton:按钮
        (4)、JComboBox:从下拉框中选择记录
        (5)、JList:在界面上显示多条记录并可多重选择的列表
        (6)、JMenuBar:菜单栏
        (7)、JScrollPane:滚动条
/***********************************************************/
//最简单的图形用户界面,学会其中的四大步骤
import java.awt.*;
import javax.swing.*;
class FirstFrame{
   public static void main(String[] args){
      //1、选择容器
      JFrame f = new JFrame();//在JFrame()的括号里可以填写窗口标题
      //2、选择布局管理器
      LayoutManager lm = new BorderLayout();
      f.setLayout(lm);
      //3、添加组件
      JButton b = new JButton("确定");
      f.add(b);
      //4、添加事件,显示
      JOptionPane.showMessageDialog(null, "哈哈,你好!");//对话窗口
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用窗口的开关控制程序的结束
      f.setSize(400, 300);//窗口的大小
      f.setVisible(true); //让窗口可见,默认不可见的
}}
/***********************************************************/


/*****************例题 画出计算器的界面*****************************
 界面如下:
     1   2   3   +
     4   5   6   -
     7   8   9   *
     0   .   =   /
 *******************/
import java.awt.*;
import javax.swing.*;
class Calculator {
   public static void main(String[] args){
      JTextField text = new JTextField();
      JFrame f = new JFrame("计算器");
      Font font = new Font("宋体", Font.BOLD, 25);//"宋体"想写成默认,则写“null”
      text.setFont(font); //定义字体
      text.setHorizontalAlignment(JTextField.RIGHT);//令text的文字从右边起
      text.setEditable(false);//设置文本不可修改,默认可修改(true)
      f.add(text, BorderLayout.NORTH);//Frame和Dialog的默认布局管理器是Border Layout
      JPanel buttonPanel = new JPanel();//设法把计算器键盘放到这个Jpanel按钮上
      String op = "123+456-789*0.=/";
      GridLayout gridlayout = new GridLayout(4,4,10,10);
      buttonPanel.setLayout(gridlayout);//把计算器键盘放到buttonPanel按钮上
      for(int i=0; i<op.length(); i++){
          char c = op.charAt(i);
          JButton b = new JButton(c+"");
          buttonPanel.add(b);
      }//这个循环很值得学习,很常用
      f.add(buttonPanel/*, BorderLayout.CENTER*/);  //默认添加到CENTER位置
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关窗口时结束程序
      f.setSize(300, 250);
      f.setVisible(true);//这句要放到最后,等事件完成后再显示
}}
/******学过事件之后,可以实现计算器的具体功能*******************************/


观察者模式:
事件源一旦产生事件,监察者立即作出相应处理。
    事件源:产生一个事件的对象
    事件对象:由事件源产生的一个对象
    事件监听者(观察者):处理这个事件对象的对象
    事件注册:给一个事件源注册一个(或多个)事件监听者


事件模型(重点)
    1.定义: 事件模型指的是对象之间进行通信的设计模式。
        事件模型是在观察者模式基础上发展来的。
    2.对象1 给对象2 发送一个信息相当于对象1 引用对象2 的方法。
    3.事件对象分为三种:
       (1)事件源:发出事件者;
       (2)事件对象:发出的事件本身(事件对象中会包含事件源对象)
           事件对象继承:java.util.EventObjct类.
       (3)事件监听器:提供处理事件指定的方法。
           标记接口:没有任何方法的接口;如EventListene接口
           监听器接口必须继承java.util.EventListener接口。
           监听接口中每一个方法都会以相应的事件对象作为参数。
    4.授权:Java AWT 事件模型也称为授权事件模型,指事件源可以和监听器之间事先建立一种授权关系:
        约定那些事件如何处理,由谁去进行处理。这种约定称为授权。
        当事件条件满足时事件源会给事件监听器发送一个事件对象,由事件监听器去处理。
        事件源和事件监听器是完全弱偶合的。 
        一个事件源可以授权多个监听者(授权也称为监听者的注册);事件源也可以是多个事件的事件源。
        监听器可以注册在多个事件源当中。监听者对于事件源的发出的事件作出响应。
        在java.util 中有EventListener 接口:所有事件监听者都要实现这个接口。
        java.util 中有EventObject 类:所有的事件都为其子类。
        注意:接口因对不同的事件监听器对其处理可能不同,所以只能建立监听的功能,而无法实现处理。
//监听器接口要定义监听器所具备的功能,定义方法
/************下面程序建立监听功能***************************/
import java.awt.*;
import javax.swing.*;
public class TestEvent {
    public static void main(String[] args) {
        JFrame f = new JFrame("测试事件");
        JButton b = new JButton("点击");//事件源:鼠标点击
        JTextArea textArea = new JTextArea();
        textArea.setFont(new Font(null, Font.BOLD+Font.ITALIC, 26));
        JScrollPane scrollPane = new JScrollPane(textArea);
        f.add(scrollPane, "Center");
        ButtonActionListener listener = new ButtonActionListener(textArea);
        b.addActionListener(listener);
        f.add(b, BorderLayout.SOUTH);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(400, 300);
        f.setLocation(250, 250);
        f.setVisible(true);
}}
//事件对象,可以直接调用系统写好的
/*class ActionEvent extends EventObject{
    public ActionEvent(Object source) {
        super(source);
}}*/


//监听接口,同样可以调用系统写好的
/*interface ActionListener extends EventListener{
    public void actionPerformed(ActionEvent event);
}*/


//监听者
class ButtonActionListener implements ActionListener{
    private JTextArea textArea;
    public ButtonActionListener(JTextArea textArea) {
        this.textArea = textArea;
    }
    public void actionPerformed(ActionEvent e) {//必须覆盖它的actionPerformed()
        //JOptionPane.showMessageDialog(null, "按钮被按了一下");
        textArea.append("哈哈,放了几个字/n");
        textArea.append("哈哈,又放了几个字/n");
}}
/*********************************************************/
    注意查看参考书:事件的设置模式,如何实现授权模型。
    事件模式的实现步骤:开发事件对象(事件发送者)——接口——接口实现类——设置监听对象
    重点:学会处理对一个事件源有多个事件的监听器(在发送消息时监听器收到消息的排名不分先后)。
        事件监听的响应顺序是不分先后的,不是谁先注册谁就先响应。
        事件监听由两个部分组成(接口和接口的实现类)。
        
        一定要理解透彻Gril.java 程序。
        事件源    事件对象        事件监听者
        gril     EmotinEvent   EmotionListener(接口)、Boy(接口的实现类)
        鼠标事件:MouseEvent,接口:MouseListener。
    注意在写程序的时候:import java.awt.*;以及import java.awt.event.*注意两者的不同。
    
    在生成一个窗体的时候,点击窗体的右上角关闭按钮激发窗体事件的方法:
    窗体Frame 为事件源,用WindowsListener 接口调用Windowsclosing()。
    为了配合后面的实现,必须实现WindowsListener所有的方法;除了Windowsclosing方法,其余的方法均为空实现。这样的程序中实现了许多不必要的实现类,虽然是空实现。为了避免那些无用的实现,可以利用WindowEvent 的一个WindowEvent 类,还是利用windowsListener。WindowAdapter类,实现于WindowsListener。它给出的全是空实现,那就可以只覆盖其中想实现的方法,不必再写空实现。
/******练习:写一个带button 窗体,点关闭按钮退出。*************/
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class TestAdapter {
    public static void main(String[] args) {
        JFrame f = new JFrame("测试适配器");
        MyWindowListener listener = new MyWindowListener();
        //f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //现在有上面这一句话,就可以不必再创建一个类了
        f.addWindowListener(listener);
        f.setSize(400, 300);
        f.setVisible(true);
    }
    private static class MyWindowListener extends WindowAdapter{
        public void windowClosing(WindowEvent e) {
            System.exit(0);
}}}
/*********************************************************/
注意:监听过多,会抛tooManyListener 例外。
缺省试配设计模式:如果一个接口有太多的方法,我们可以为这个接口配上一个对应的抽象类。


 


《多线程》
一.线程:线程是一个并发执行的顺序流,一个进程包括多个顺序执行流程,这执行流程称为线程。
    线程是一个操作系统创建并维护的一个资源,对操作系统来说JVM就是一个进程。
    对于单个CPU系统来说,某一个时刻只可能由一个线程在运行。
       一个Thread对象就表示一个线程。
    线程由三部分组成:
     (1).CPU分配给线程的时间片
     (2).线程代码(写在run方法中)
     (3).线程数据
   进程是独立的数据空间,线程是共享的数据空间.
   线程对象存在于虚拟机进程空间的一块连续的地址空间(静态)
   //main()也是一个线程。
   
   注意: 
   1.线程是动态的,与线程对象是两回事.
   2.线程对象与其他对象不同的是线程对象能够到底层去申请管理一个线程资源。
   3.只有对线程对象调用start()方法才是到底层去申请管理一个线程资源。
   4.任务并发执行是一个宏观概念,微观上是串行的。


二.进程的调度
    进程的调度由OS负责(有的系统为独占式(Windows),有的系统为共享式(Unix),根据重要性,进程有优先级)
    由OS 将时间分为若干个时间片。JAVA 在语言级支持多线程。分配时间的仍然是OS。
    
三.线程有两种实现方式:
第一种方式:
    class MyThread extends Thread{
        public void run(){
            //...需要进行执行的代码,如循环。
    }}
    public class TestThread{
        public static void main(String[]args){
            Thread t1=new Mythread();
            t1.start();
    }}
只有等到所有的线程全部结束之后,进程才退出。
第二种方式:通过接口实现继承
    Class MyThread implements Runnable{
        Public void run(){
            Runnable target=new MyThread();
            Thread t3=new Thread(target);
            t3.start();//启动线程,这种方式跟前者一样,只是可以继承
    }}
    
/******************多线程事例*******************************/
class TestThread{
    public static void main(String[] args){
        System.out.println("Main Thread Start!");
        //Thread t = new MyThread(); 
        Runnable r = new MyRunnable();
        Thread t1 = new Thread(r);//启动另一个带任务的线程
        Thread t = new MyThread(t1);//这个线程需要join(t1),否则用上面没参的一句
        t.start();//启动一个线程
        t1.setPriority(10);//设置优先级
        t1.start();        
        System.out.println("Main Thread End!");
}}
class MyThread extends Thread{
    private Thread t;
    public MyThread (){}
    public MyThread (Thread t){this.t = t;}
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println(i+" $$$$");
            if(i==65){  //join()加入其他线程,等其运行完后再运行
                try{t.join();} 
                catch(Exception e){e.printStackTrace();}
            } 
            //当i=50时,放弃CPU占用,让其他程序或线程使用
            if(i==50){Thread.yield();}//没有sleep睡眠方法时,此句才可看出效果
            //阻塞,睡眠5毫秒,中途会被打断而抛异常
            try{Thread.sleep(5);} catch(Exception e){}        
}}}
class MyRunnable implements Runnable{ 
    //Runnable是线程任务接口,让你可以继承其他类;Thread是类,不能继承
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println(i+" ****");
            try{Thread.sleep(5);} catch(Exception e){}
}}}
/*********************************************************/
四.线程中的7 种非常重要的状态: 初始New、可运行Runnable、运行Running、阻塞Blocked、
                            锁池lock_pool、等待队列wait_pool、结束Dead
    有的书上认为只有五种状态:将“锁池”和“等待队列”都看成是“阻塞”状态的特殊情况:这种认识也是正确的。
    将“锁池”和“等待队列”单独分离出来有利于对程序的理解。
                                         
                 ┌--------------------< 阻塞
                 ↓                    (1)(2)(3)        结束
                ①②③        OS调度         ↑             ↑
    初始-------> 可运行 ?---------------? 运行 >-----------┤
    t.start()启动 ↑                        ↓              ↓o.wait()
                 └-----< 锁池 ←---------<┘←-------< 等待队列
                  获得锁标志   synchronized(o)
注意:图中标记依次为
    ①输入完毕;    ②wake up    ③t1 退出
    ⑴等待输入(输入设备进行处理,而CUP 不处理),则放入阻塞,直到输入完毕。
    ⑵线程休眠sleep()
    ⑶t1.join()将t1 加入运行队列,直到t1 退出,当前线程才继续。
        特别注意:①②③与⑴⑵⑶是一一对应的。
    进程的休眠:Thread.sleep(1000);//括号中以毫秒为单位
    当线程运行完毕,即使在结束时时间片还没有用完,CPU 也放弃此时间片,继续运行其他程序。
    T1.join 实际上是把并发的线程编成并行运行。


五.线程的优先级:    
    设置线程优先级:setPriority(Thread. MAX_PRIORITY);
    setPriority(10); //设置优先级,独占式的操作系统按优先级分配CPU,共享式操作系统按等待时长分配
    JAVA的优先级可以是 1~10,默认是5,数据越大,优先级越高。//windows只有6个优先级,会自动转换来分配
    为了跨平台,最好不要使用优先级决定线程的执行顺序。
    //跨平台性的含义:除了程序能够正常运行,还必须保证运行的结果。
    线程对象调用yield()时会马上交出执行权,交由一个高优先级的线程进入可运行状态;自己等待再次调用。
    程序员需要关注的线程同步和互斥的问题。
    多线程的并发一般不是程序员决定,而是由容器决定。


六.多线程出现故障的原因:
    (1).多个线程同时访问一个数据资源(该资源称为临界资源),形成数据发生不一致和不完整。
    (2).数据的不一致往往是因为一个线程中的多个关联的操作(这几个操作合成原子操作)未全部完成。
    避免以上的问题可采用对数据进行加锁的方法,如下


七.对象锁 Synchronized
    防止打断原子操作,解决并发访问的故障。
    //原子操作:不可分割的几个操作,要么一起不做,要么不能被干扰地完成。
    1.互斥锁标记:每个对象除了属性和方法,都有一个monitor(互斥锁标记),
       用来将这个对象交给一个线程,只有拿到monitor 的线程才能够访问这个对象。
    2.Synchronized:这个修饰词可以用来修饰方法和代码块
       Object obj;    Obj.setValue(123);
       Synchronized用来修饰代码块时,该代码块成为同步代码块。
       Synchronized 用来修饰方法,表示当某个线程调用这个方法之后,其他的事件不能再调用这个方法。
            只有拿到obj 标记的线程才能够执行代码块。
注意:
    (1)Synchronized 一定使用在一个方法中。
    (2)锁标记是对象的概念,加锁是对对象加锁,目的是在线程之间进行协调。
    (3)当用Synchronized 修饰某个方法的时候,表示该方法都对当前对象加锁。
    (4)给方法加Synchronized 和用Synchronized 修饰对象的效果是一致的。
    (5)一个线程可以拿到多个锁标记,一个对象最多只能将monitor 给一个线程。
    (6)构造方法和抽象方法不能加synchronized;
    (7)一般方法和静态方法可以加synchronized同步。//静态方法把类看作对象时可加锁
    (8)Synchronized 是以牺牲程序运行的效率为代价的,因此应该尽量控制互斥代码块的范围。
    (9)方法的Synchronized 特性本身不会被继承,只能覆盖。


八.锁池
    1.定义:线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池。
    2.每个对象都有自己的锁池的空间,用于放置等待运行的线程。这些线程中哪个线程拿到锁标记由系统决定。
    3.死锁:线程互相等待其他线程释放锁标记,而又不释放自己的;造成无休止地等待。
       死锁的问题可通过线程间的通信解决。


九.线程间通信:
    1. 线程间通信机制实际上也就是协调机制。
        线程间通信使用的空间称之为对象的等待队列,这个队列也属于对象的空间。
        注:现在,我们已经知道一个对象除了由属性和方法之外还有互斥锁标记、锁池空间和等待队列空间。
    2. wait()
        在运行状态中,线程调用wait(),表示这线程将释放自己所有的锁标记,同时进入这个对象的等待队列。
        等待队列的状态也是阻塞状态,只不过线程释放自己的锁标记。
        用notify()方法叫出之后,紧跟着刚才wait();的位置往下执行。
    3. Notify()
        如果一个线程调用对象的notify(),就是通知对象等待队列的一个线程出列。进入锁池。
        如果使用notifyall()则通知等待队列中所有的线程出列。
    注意:只能对加锁的资源(Synchronized方法里)进行wait()和notify()。
         我们应该用notifyall取代notify,因为我们用notify释放出的一个线程是不确定的,由OS决定。
         释放锁标记只有在Synchronized 代码结束或者调用wait()。
         锁标记是自己不会自动释放,必须有通知。
    注意:在程序中判定一个条件是否成立时要注意使用WHILE 要比使用IF 更严密。
         //WHILE 循环会再次回来判断,避免造成越界异常。
    4.  补充知识:
        suspend()将运行状态推到阻塞状态(注意不释放锁标记)。恢复状态用resume()。Stop()释放全部。
        这几个方法上都有Deprecated 标志,说明这个方法不推荐使用。
        一般来说,主方法main()结束的时候线程结束,可是也可能出现需要中断线程的情况。
        对于多线程一般每个线程都是一个循环,如果中断线程我们必须想办法使其退出。
        如果想结束阻塞中的线程(如sleep 或wait),可以由其他线程对其对象调用interrupt()。
        用于对阻塞(或锁池)会抛出例外Interrupted。
    5.Exception。
        这个例外会使线程中断并执行catch 中代码。


十.5.0的新方法: 
//参看 java.util.concurrent.*;包下的Callable,ExecutorService,Executors;
    1.  ExecutorService代替Thread,它不会销毁线程,效率更高,用空间换时间,适用于服务器。
        ExecutorService exec = Executors.newFixedThreadPool(3);//创建3个等待调用的线程
        Callable<String> c1 = new Task();//用Callable的Task子类实现任务,其call()代替run()
        Future<String> f1 = exec.submit(c1);//Futrue获得运行线程后的结果,这线程与主程序分开
            String s1 = f1.get();//Futrue的获得结果的方法,会返回异常
    2.  用Callable接口代替Runnable
        因为Runnable不能抛异常,且没有返回值。  
    3.  Lock对象 替代Synchronized标志符,获得的更广泛的锁定操作,允许更灵活的结构。
        还有tryLock()尝试加锁。
        解锁用unLock();可以主动控制解锁,方便复杂的方法调用。
        //下列方法参考 java.util.concurrent.locks
        在这里wait()用await()替代;Notify(),notifyall()用signal(),signalAll()替代。
        ReadWriteLock读写锁:
            writeLock()写锁,排他的,一旦加上,其他任何人都不能来读写。
            readLock()读锁,共享的,加上之后,别人可以读而不能写。可以加多个读锁。
    
        
重点: 实现多线程的两种方式, Synchronized, 生产者和消费者问题
练习:① 存车位的停开车的次序输出问题。
     ② 写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z。打印顺序为12A34B56C……5152Z。要求用线程间的通信。
       注:分别给两个对象构造一个对象o,数字每打印两个或字母每打印一个就执行o.wait()。
       在o.wait()之前不要忘了写o.notify()。
补充:通过Synchronized,可知Vector与ArrayList的区别就是Vector几乎所有的方法都有Synchronized。
     所以Vector 更为安全,但效率非常低下。 同样:Hashtable 与HashMap 比较也是如此。
/****************多线程间的锁及通信**************************/
//生产者-消费者问题,见P272
public class TestProducerAndConsumer {
    public static void main(String[] args) {
        MyStack ms = new MyStack();
        Thread producer = new ProducerThread(ms);
        Thread consumer = new ConsumerThread(ms);
        producer.start();
        consumer.start();
        Thread producer2 = new ProducerThread(ms);
        producer2.start();//用第2个生产者,说明while循环判断比if更严密
}}
class MyStack{ //工厂
    private char[] cs = new char[6];//仓库只能容6个产品
    private int index = 0;
    public synchronized void push(char c){    
        //用while来循环判断,避免多个push进程时的下标越界异常。如果用if,只能在一个push进程时能正常
        while(index==6){try{wait();}catch(Exception e){}}
        cs[index] = c;
        System.out.println(cs[index]+ " pushed!");
        index++;
    }
    public synchronized void pop(){
        while(index==0){try{wait();}catch(Exception e){}}
        index--;
        System.out.println(cs[index]+" poped!");
        notify();
        cs[index] = '/0';
    }
    public void print(){
        for(int i=0; i<index; i++){System.out.print(cs[i]+"/t");}
        System.out.println();
}}
class ProducerThread extends Thread{ //生产者
    private MyStack s;
    public ProducerThread(MyStack s){this.s = s;}
    public void run(){
        for(char c='A'; c<='Z'; c++){
            s.push(c);
            try{Thread.sleep(20);}
            catch(Exception e){e.printStackTrace();}
            s.print();
}}}
class ConsumerThread extends Thread{ //消费者
    private MyStack s;
    public ConsumerThread(MyStack s){
        this.s = s;
    }
    public void run(){
        for(int i=0; i<26*2; i++){
            s.pop();
            try{Thread.sleep(40);}
            catch(Exception e){e.printStackTrace();}
            s.print();
}}}
/*********************************************************/


Daemon Threads(daemon 线程)
    是服务线程,当其他线程全部结束,只剩下daemon线程时,虚拟机会立即退出。
    Thread t = new DaemonThread();
    t.setDaemon(true);//setDaemon(true)把线程标志为daemon,其他的都跟一般线程一样
    t.start();//一定要先setDaemon(true),再启动线程
    在daemon线程内启动的线程,都定为daemon线程


 


day20 I/O流
一. I/O 流(java 如何实现与外界数据的交流)
    1. Input/Output:指跨越出了JVM 的边界,与外界数据的源头或者目标数据源进行数据交换。
        注意:输入/输出是针对JVM 而言。
    2. 流的分类:
        按流向分为输入流和输出流;
        按传输单位分为字节流和字符流;
        按功能还可以分为节点流和过滤流。(以Stream结尾的类都是字节流。)
            节点流:负责数据源和程序之间建立连接;(相当于电线中的铜线,过滤流相当于电线的塑料皮)
            过滤流:用于给节点增加功能。(相当于功能零部件)
        过滤流的构造方式是以其他流位参数构造,没有空参构造方法(这样的设计模式称为装饰模式)。
        注:I/O流使用完后建议调用close()方法关闭流并释放资源。
           在关闭流时只需关闭最外层的流,会自动关闭内层的流。
    3. File 类(java.io.*)可表示一个文件,也有可能是一个目录
        在JAVA 中文件和目录都属于这个类中,而且区分不是非常的明显。
    4. Java.io 下的方法是对磁盘上的文件进行磁盘操作,但是无法读取文件的内容。
    注意:创建一个文件对象和创建一个文件在JAVA 中是两个不同的概念。前者是在虚拟机中创建了一个文件,但却并没有将它真  正地创建到OS 的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。而创建一个文件是指在系统中真正地建立一个文件。
        例如:File f=new File(“11.txt”);//创建一个名为11.txt 的文件对象
             f.CreateNewFile(); //这才真正地创建文件
             f.CreateMkdir();//创建目录
             f.delete();//删除文件
             getAbsolutePath();//打印文件绝对路径
             getPath();//打印文件相对路径
             f.deleteOnExit();//在进程退出的时候删除文件,这样的操作通常用在临时文件的删除。
    5. 对于跨平台:File f2=new file("d://abc//789//1.txt") 
        //这文件路径是windows的,"/"有转义功能,所以要两个
        这个命令不具备跨平台性,因为不同的OS的文件系统很不相同。
        如果想要跨平台,在file 类下有separtor(),返回锁出平台的文件分隔符。
        File.fdir=new File(File.separator);
        String str=”abc”+File.separator+”789”;
    6. List():显示文件的名(相对路径)
        ListFiles():返回Files 类型数组,可以用getName()来访问到文件名。
        使用isDirectory()和isFile()来判断究竟是文件还是目录。
        使用I/O流访问file中的内容。
        JVM与外界通过数据通道进行数据交换。


二. 字节流
    1. 字节输入流:io包中的InputStream为所有字节输入流的父类。
        Int read();//读入一个字节(每次一个);
        可先使用new byte[]=数组,调用read(byte[] b)
        read (byte[])返回值可以表示有效数;read (byte[])返回值为-1 表示结束。
    2. 字节输出流:io包中的OutputStream为所有字节输入流的父类。
        Write和输入流中的read相对应。
    3. 在流中close()方法由程序员控制。因为输入输出流已经超越了JVM的边界,所以有时可能无法回收资源。
        原则:凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
    4. 以Stream结尾的类都是字节流。
        FileOutputStream f = new FileOutputStream("1.txt");//如果之前有这文件,将会覆盖
        FileOutputStream f = new FileOutputStream("1.txt",true);//如果有这文件,只会追加
        DataOutputStream,DataInputStream:可以对八种基本类型加上String类型进行写入。
        因为每种数据类型的不同,所以可能会输出错误。
        所有对于:DataOutputStream  DataInputStream两者的输入顺序必须一致。
    /**输入、输出流;字节流**************************/
    public static void main(String[]args) throws IOException{
      FileOutputStream fos=null;//输出流,字节流
        fos = new FileOutputStream("a.txt");//会抛异常;如果不用绝对路径,将生成所在类的路径中
        for(int i = 32; i<=126 ;i++) fos.write(i);//直接写进
        // String s = "ABCDE";byte[] ss = s.getBytes();fos.write(ss,0,4);//直观地写进
        fos.close();
      FileInputStream fis = new FileInputStream("a.txt");//输入流
      //for(int i=0;(i=fis.read())!=-1;) System.out.print((char) i);
      //上句是直接读取;fis.read()类似next().
      byte[] b= new byte[1024];//在内存划分空间以便装载从文件读来的数据
      fis.read(b);fis.close();
      for(int i=0;i<b.length;i++) System.out.print((char)b[i]);
    }//打印出键盘上的所有数字、大小写字母和标准符号
    /********************************************/


过滤流:(装饰模式,油漆工模式,修饰字节流) 
        bufferedOutputStream
        bufferedInputStream
        在JVM的内部建立一个缓冲区,数据先写入缓冲区,直到缓冲区写满再一次性写出,效率提高很多。
        使用带缓冲区的输入输出流(节点流)会大幅提高速度,缓冲区越大,效率越高。(典型的牺牲空间换时间) 
    切记:使用带缓冲区的流,如果数据数据输入完毕,使用flush方法将缓冲区中的内容一次性写入到外部数据源。
        用close()也可以达到相同的效果,因为每次close前都会使用flush。一定要注意关闭外部的过滤流。


管道流(非重点):也是一种节点流,用于给两个线程交换数据。
        PipedOutputStream
        PipedInputStream
输出流: connect(输入流)
        RondomAccessFile 类允许随机访问文件同时拥有读和写的功能。
        GetFilepoint()可以知道文件中的指针位置,使用seek()定位。
        Mode(“r”:随机读;”w”:随机写;”rw”:随机读写)
字符流: reader/write 只能输纯文本文件。
        FileReader 类:字符文件的输出


三、字节流的字符编码:
        字符编码把字符转换成数字存储到计算机中,按ASCii 将字母映射为整数。
        把数字从计算机转换成相应的字符的过程称为解码。
        乱码的根源是编码方式不统一。任何一种编码方式中都会向上兼容ASCII码。所以英文没有乱码。
编码方式的分类:
        ASCII(数字、英文):1个字符占一个字节(所有的编码集都兼容ASCII)
        ISO8859-1(欧洲,拉丁语派):1个字符占一个字节
        GB-2312/GBK:1 个字符占两个字节。GB代表国家标准。
        GBK是在GB-2312上增加的一类新的编码方式,也是现在最常用的汉字编码方式。
        Unicode: 1 个字符占两个字节(网络传输速度慢)
        UTF-8:变长字节,对于英文一个字节,对于汉字两个或三个字节。
    原则:保证编解码方式的统一,才能不至于出现错误。
    I/O学习种常犯的两个错误:1.忘了flush     2.没有加换行。


四、字符流
        以reader或write结尾的流为字符流。 Reader和Write是所有字符流的父类。
        Io 包的InputStreamReader 称为从字节流到字符流的桥转换类。这个类可以设定字符解码方式。
        OutputStreamred:字符到字节
        Bufferread 有readline()使得字符输入更加方便。
        在I/O 流中,所有输入方法都是阻塞方法。
        最常用的读入流是BufferedReader.没有PrintReader。
        Bufferwrite 给输出字符加缓冲,因为它的方法很少,所以使用父类PrintWriter,它可以使用字节流对象构造,省了桥转换这一步,而且方法很多。注:他是带缓冲的。最常用的输出流。


对象的持久化:把对象保存文件,数据库
对象的序列化:把对象放到流中进行传输
    对象的持久化经常需要通过序列化来实现。


四大主流:InputStream,OutputStream,Read,Write




day21
 一.对象序列化
    1.定义:把一个对象通过I/O流写到文件(持久性介质)上的过程叫做对象的序列化。
    2.序列化接口:Serializable
        此接口没有任何的方法,这样的接口称为标记接口。
    3.不是所有对象都能序列化的,只有实现了Serializable的类,他的实例对象才是可序列化的。
    4.Java种定义了一套序列化规范,对象的编码和解码方式都是已经定义好的。
    5.class ObjectOutputStream和ObjectInputStream也是带缓冲的过滤流,使节点流直接获得输出对象
        可以用来向文件中写入八种基本数据类型和对象类型。
    最有用的方法:
    (1)writeObject(Object b)
    (2)readObject()返回读到的一个对象,但是需要我们注意的是,该方法不会以返回null表示读到文件末尾。
        而是当读到文件末尾时会抛出一个IOException;
    6.序列化一个对象并不一定会序列化该对象的父类对象
    7.瞬间属性(临时属性)不参与序列化过程。
    8.所有属性必须都是可序列化的,特别是当有些属性本身也是对象的时候,要尤其注意这一点。
       序列化的集合就要求集合中的每一个元素都是可序列化的。
    9.用两次序列化把两个对象写到文件中(以追加的方式),与用一次序列化把两个对象写进文件的大小是不同的。
       因为每次追加时都会在文件中加入开始标记和结束标记。所以对象的序列化不能以追加的方式写到文件中。


二、transient关键字
    1.用transient修饰的属性为临时属性。


三.分析字符串工具java.util. StringTokenizer;
    1.string tokenizer 类允许应用程序将字符串分解为标记
    2.可以在创建时指定,也可以根据每个标记来指定分隔符(分隔标记的字符)集合。
    3.StringTokenizer(s,”:”) 用“:”隔开字符,s 为String对象。
/*********************************************************/
import java.util.StringTokenizer;
public class TestStringTokenizer {
    public static void main(String[] args) {
        String s = "Hello:Tarena:Chenzq";
        StringTokenizer st = new StringTokenizer(s,":");
        while(st.hasMoreTokens()){
            String str = st.nextToken();
            System.out.println(str);
}}}
/********************************************************/




四、网络的基础知识:
    1、ip:主机在网络中的唯一标识,是一个逻辑地址。
        127.0.0.1 表示本机地址。(没有网卡该地址仍然可以用)
    2、端口:端口是一个软件抽象的概念。如果把Ip地址看作是一个电话号码的话,端口就相当于分机号。
        进程一定要和一个端口建立绑定监听关系。端口号占两个字节。
    3、协议:通讯双方为了完成预先制定好的功能而达成的约定。
    4、TCP/IP网络七层模型:
        物理层Physical(硬件)、 数据链路层DataLink(二进制) 、网络层Network(IP协议:寻址和路由)
        传输层Transport(TCP协议,UDP协议) 、会话层Session(端口)
        表示层Presentation、应用层Application(HTTP,FTP,TELET,SMTP,POPS,DNS)
        注:层与层之间是单向依赖关系。对等层之间会有一条虚连接。Java中的网络编程就是针对传输层编程
    5、网络通信的本质是进程间通信。
    6、Tcp协议和UDP协议
        TCP:开销大,用于可靠性要求高的场合。TCP的过程相当于打电话的过程。面向连接,可靠,低效
        UDP:用在对实时性要求比较高的场合。UDP的过程相当于写信的过程。无连接,不可靠,效率高


五、网络套节字Socket(TCP)
    1、一个Socket相当于一个电话机。
        OutputStream相当于话筒
        InputStream相当于听筒
    2、服务器端要创建的对象:java。Net。ServerSocket
    3、创建一个TCP服务器端程序的步骤:
       1). 创建一个ServerSocket
       2). 从ServerSocket接受客户连接请求
       3). 创建一个服务线程处理新的连接
       4). 在服务线程中,从socket中获得I/O流
       5). 对I/O流进行读写操作,完成与客户的交互
       6). 关闭I/O流
       7). 关闭Socket
/***********************************************************/
import java.net.*;
import java.io.*;
public class TcpServer{//服务器端
    public static void main(String[] args) {
        ServerSocket ss = null;
        Socket s = null;
        try{   ss= new ServerSocket(10222);
               s = ss.accept();//客户端连上后返回Socket,监听端口
               OutputStream os = s.getOutputStream();
               PrintWriter pw = new PrintWriter(os);
               pw.println("欢迎欢迎!");//要换行,否则不能读取
               pw.flush();//从内存输出去
        }catch(Exception e){}
        finally{if(s!=null )try{s.close(); }catch(Exception e){}
                if(ss!=null)try{ss.close();}catch(Exception e){}
}}}


public class TcpClient {//接受端
    public static void main(String[] args) throws Exception {
        Socket s = new Socket("10.3.1.79", 10222);
        BufferedReader br = new BufferedReader(new InputStreamReader
            (s.getInputStream()));
        System.out.println(br.readLine());
        s.close();
}}
/***********************************************************/


    4、建立TCP客户端
    创建一个TCP客户端程序的步骤:
      1). 创建Socket
      2). 获得I/O流
      3). 对I/O流进行读写操作
      4). 关闭I/O流
      5). 关闭Socket


    5、网络套节字Socket(UDP)
       1.UDP编程必须先由客户端发出信息。
       2.一个客户端就是一封信,Socket相当于美国式邮筒(信件的收发都在一个邮筒中)。
       3.端口与协议相关,所以TCP的3000端口与UDP的3000端口不是同一个端口
    6、URL:统一资源定位器
       唯一的定位一个网络上的资源
       如:http://www.tarena.com.cn:8088
/***下载程序**************************************************************/
import java.net.*;
import java.io.*;
class TestUrl{
    public static void main(String[] args) throws Exception{
        String str = "http://192.168.0.23:8080/project_document.zip";
        URL url = new URL(str);//上句指定下载的地址和文件
        URLConnection urlConn = url.openConnection();
        urlConn.connect();
        InputStream is = urlConn.getInputStream();
        FileOutputStream fos = new FileOutputStream("/home/sd0807/down.zip");
        byte[] buf = new byte[4096];            //上句指定下载的地址和下载后的名称
        int length = 0;
        while((length=is.read(buf))!=-1){
            fos.write(buf, 0, length);
        }
        fos.close();
        is.close();
}}
/***************************************************************************/


 


 


 


 


 


 


 


 


 


 


 




JAVAC的帮助,输入[sd0807@localhost ~]$ javac
编译:javac ***.java
    用法: javac <options> <source files>
    用法:javac <选项> <源文件>


其中,可能的选项包括:
  -g                      生成所有调试信息
  -g:none                 不生成任何调试信息
  -g:{lines,vars,source}  只生成某些调试信息
  -nowarn                 不生成任何警告
  -verbose                输出有关编译器正在执行的操作的消息
  -deprecation            输出使用已过时的 API 的源位置
  -classpath <路径>        指定查找用户类文件和注释处理程序的位置
  -cp <路径>               同上(是 classpath 的缩写) 
  -sourcepath <路径>       指定查找输入源文件的位置
  -bootclasspath <路径>    覆盖引导类文件的位置
  -extdirs <目录>          覆盖安装的扩展目录的位置
  -endorseddirs <目录>     覆盖签名的标准路径的位置
  -proc:{none,only}       控制是否执行注释处理和/或编译。
  -processor <class1>[,<class2>,<class3>...]
                          要运行的注释处理程序的名称;绕过默认的搜索进程
  -processorpath <路径>    指定查找注释处理程序的位置
  -d <目录>                指定存放生成的类文件的位置
  -s <目录>                指定存放生成的源文件的位置
  -implicit:{none,class}  指定是否为隐式引用文件生成类文件 
  -encoding <编码>         指定源文件使用的字符编码
  -source <版本>           提供与指定版本的源兼容性
  -target <版本>           生成特定 VM 版本的类文件
  -version                版本信息
  -help                   输出标准选项的提要
  -Akey[=value]           传递给注释处理程序的选项
  -X                      输出非标准选项的提要
  -J<标志>                 直接将 <标志> 传递给运行时系统




输入[sd0807@localhost ~]$ java
运行(虚拟机): java ***


Usage: java [-options] class [args...]
       (to execute a class)
   or  java [-options] -jar jarfile [args...]
       (to execute a jar file)


where options include:
    -d32          use a 32-bit data model if available
    -d64          use a 64-bit data model if available
    -client       to select the "client" VM
    -server       to select the "server" VM
    -hotspot   is a synonym for the "client" VM [deprecated] The default VM is client.
    -cp <class search path of directories and zip/jar files>
    -classpath <class search path of directories and zip/jar files>
                  A : separated list of directories, JAR archives,
                  and ZIP archives to search for class files.
    -D<name>=<value>          set a system property
    -verbose[:class|gc|jni]   enable verbose output
    -version                  print product version and exit
    -version:<value>          require the specified version to run
    -showversion  print product version and continue
    -jre-restrict-search | -jre-no-restrict-search
                  include/exclude user private JREs in the version search
    -? -help                  print this help message
    -X                        print help on non-standard options
    -ea[:<packagename>...|:<classname>]
    -enableassertions[:<packagename>...|:<classname>]     enable assertions
    -da[:<packagename>...|:<classname>]
    -disableassertions[:<packagename>...|:<classname>]    disable assertions
    -esa | -enablesystemassertions                        enable system assertions
    -dsa | -disablesystemassertions                       disable system assertions
    -agentlib:<libname>[=<options>]
                 load native agent library <libname>, e.g. -agentlib:hprof
                 see also, -agentlib:jdwp=help and -agentlib:hprof=help
    -agentpath:<pathname>[=<options>]     load native agent library by full pathname
    -javaagent:<jarpath>[=<options>]
                 load Java programming language agent, see java.lang.instrument
    -splash:<imagepath>                   show splash screen with specified image


 


 


J2ee | 评论:0 | Trackbacks:0 | 阅读:102


05. Core Java exercise
Submitted by cnetsa on 2009, July 29, 2:20 PM




1,编写程序,判断给定的某个年份是否是闰年。
      闰年的判断规则如下:
      (1)若某个年份能被4整除但不能被100整除,则是闰年。
      (2)若某个年份能被400整除,则也是闰年。


import java.util.Scanner;
class Bissextile{
    public static void main(String[] arge){
        System.out.print("请输入年份");
    int year;    //定义输入的年份名字为“year”
    Scanner scanner = new Scanner(System.in);
    year = scanner.nextInt();
    if (year<0||year>3000){
        System.out.println("年份有误,程序退出!");
        System.exit(0);
        }
    if ((year%4==0)&&(year%100!=0)||(year%400==0)) 
        System.out.println(year+" is bissextile");
    else 
        System.out.println(year+" is not bissextile ");
    }
}




2,给定一个百分制的分数,输出相应的等级。
        90分以上        A级
        80~89          B级
        70~79          C级
        60~69          D级
        60分以下        E级


import java.util.Scanner;
class Mark{
    public static void main(String[] args){
        System.out.println("请输入一个分数");
        //定义输入的分数为“mark”,且分数会有小数
        double mark;
        Scanner scanner = new Scanner(System.in);
        mark = scanner.nextDouble();


        //判断是否有输入错误。
        if(mark<0||mark>100){
           System.out.println("输入有误! ");
           System.exit(0);
        }
        /*判断分数的等级
        90分以上者A级, 80~89分者 B级,70~79分者 C级, 60~69者 D级,60分以下 E级 */
        if (mark>=90) System.out.println("this mark is grade /'A/' ");
        else if (mark>=80) System.out.println("this mark is grade /'B/' ");
        else if (mark>=70) System.out.println("this mark is grade /'C/' ");
        else if (mark>=60) System.out.println("this mark is grade /'D/' ");
        else  System.out.println("this mark is grade /'E/' ");
    }
}




3,编写程序求 1+3+5+7+……+99 的和值。


class he{
    public static void main(String[] args){
        int number = 1;  //初始值1,以后再+2递增上去
        int sum = 0;
        for ( ; number <100; number+=2 ){ sum += number; }
        System.out.println("1+3+5+7+……+99= " +sum);
    }
}




4、利用for循环打印 9*9  表?
1*1=1
1*2=2  2*2=4
1*3=3  2*3=6  3*3=9
1*4=4  2*4=8  3*4=12  4*4=16
1*5=5  2*5=10  3*5=15  4*5=20  5*5=25
1*6=6  2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
1*7=7  2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
1*8=8  2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
1*9=9  2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81


//循环嵌套,打印九九乘法表
public class NineNine{
    public static void main(String[]args){
    System.out.println();
    for (int j=1;j<10;j++){
        for(int k=1;k<10;k++) {   //老师的做法,判断语句里的 k<=j,省去下列的if语句。
            if (k>j) break;       //此处用 continue也可以,只是效率低一点
            System.out.print(" "+k+"X"+j+"="+j*k);
         }
        System.out.println();
        }
    }
}


 


6、输出所有的水仙花数,把谓水仙花数是指一个数3位数,其各各位数字立方和等于其本身,
   例如: 153 = 1*1*1 + 3*3*3 + 5*5*5 


class DafodilNumber{
    public static void main(String[] args){
        System.out.println("以下是所有的水仙花数");
    int number = 100;     // 由于水仙花数是三位数,故由100开始算起


    int i, j, k;     // i  j  k  分别为number 的百位、十位、个位
    for (int sum; number<1000; number++){
        i=number/100;  j=(number-i*100)/10;  k=number-i*100-j*10;
        sum=i*i*i+j*j*j+k*k*k;
        if (sum==number) System.out.println(number+" is a dafodil number! ");
        }
    }
}




7、求  a+aa+aaa+.......+aaaaaaaaa=?
      其中a为1至9之中的一个数,项数也要可以指定。


import java.util.Scanner;
class Multinomial{
    public static void main(String[] args){
        int  a;      //定义输入的 a 
        int  howMany;   //定义最后的一项有多少个数字
        Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个 1~9 的 a 值");
        a = scanner.nextInt();
            System.out.println("请问要相加多少项?");
        howMany = scanner.nextInt();
        int sum=0;
        int a1=a;  // 用来保存 a 的初始值
        for (int i=1; i<=howMany; i++){
            sum+= a; 
            a = 10*a +a1;   // 这表示a 的下一项
        // 每次 a 的下一项都等于前一项*10,再加上刚输入时的 a ;注意,这时的 a 已经变化了。
            }
        System.out.println("sum="+sum);
    }
}




8、求 2/1+3/2+5/3+8/5+13/8.....前20项之和?


class Sum{
    public static void main(Sting[] args){
        double sum=0;
        double fenZi=2.0, fenMu=1.0;    //初始的分子 (fenZi)=2,分母(fenMu)=1 
        for(int i=1; i<=20; i++){
            sum += fenZi / fenMu ;
            fenMu = fenZi;           //下一项的分母 = 上一项的分子
            fenZi += fenMu;         //下一项的分子 = 上一项的分子加分母
        }
        System.out.println("sum= "sum);
    }
}




9、利用程序输出如下图形:
   *
   * * *
   * * * * *
   * * * * * * *
   * * * * *
   * * *
   *


class Asterisk{ 
    public static void main(String[] args){
        for (int i=1; i<=13; i+=2){
            for(int j=1; j<=i && i+j<= 14; j++){System.out.print("* ");}
            System.out.println();  // 换行
        }
    }
}




11、计算圆周率
  PI=4-4/3+4/5-4/7.......
  打印出第一个大于 3.1415小于 3.1416的值


class Pi {
    public static void main(String[] args){
        double pi =0;  //定义初始值
        double fenZi = 4;    //分子为4 
        double fenMu = 1;  //第一个4,可看作分母为1 的分式,以后的分母每次递增2
        for (int i = 0; i < 1000000000; i++){ //运行老久,减少循环次数会快很多,只是精确度小些
            pi += (fenZi/fenMu) ;
            fenZi *= -1.0;    //每项分子的变化是+4,-4,+4,-4 ....
            fenMu += 2.0;    //分母的变化是1,3,5,7, ....   每项递加2
            }
        System.out.println(pi);
    }
}
输出结果为pi = 3.1415926525880504,应该不精确




12、输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值
  1  1  2  3  5  8  13  21  34
  规律:一个数等于前两个数之和
//计算斐波那契数列(Fibonacci)的第n个值
public class Fibonacci{
    public static void main(String args[]){
        int n = Integer.parseInt(args[0]);
        int n1 = 1;//第一个数
        int n2 = 1;//第二个数
        int sum = 0;//和
        if(n<=0){
            System.out.println("参数错误!");
            return;
        }
        if(n<=2){
            sum = 1;            
        }else{
            for(int i=3;i<=n;i++){
                sum = n1+n2;
                n1 = n2;
                n2 = sum;
            }
        }
        System.out.println(sum);
    }
}




//计算斐波那契数列(Fibonacci)的第n个值
//并把整个数列打印出来
public class FibonacciPrint{
    public static void main(String args[]){
        int n = Integer.parseInt(args[0]);
        FibonacciPrint t = new FibonacciPrint();
        for(int i=1;i<=n;i++){
            t.print(i);
        }
    }
    public void print(int n){
        int n1 = 1;//第一个数
        int n2 = 1;//第二个数
        int sum = 0;//和
        if(n<=0){
            System.out.println("参数错误!");
            return;
        }
        if(n<=2){
            sum = 1;            
        }else{
            for(int i=3;i<=n;i++){
                sum = n1+n2;
                n1 = n2;
                n2 = sum;
            }
        }
        System.out.println(sum);
    }
}


13、求1-1/3+1/5-1/7+1/9......的值。
  a,求出前50项和值。
  b,求出最后一项绝对值小于1e-5的和值。


 


15、在屏幕上打印出n行的金字塔图案,如,若n=5,则图案如下:
        *
       ***
      *****
     *******
    *********


//打印金字塔图案
public class PrintStar{
    public static void main(String args[]){
        int col = Integer.parseInt(args[0]);
        for(int i=1;i<=col;i++){//i表示行数
            //打印空格
            for(int k=0;k<col-i;k++){
                System.out.print(" ");
            }
            //打印星星
            for(int m=0;m<2*i-1;m++){
                System.out.print("*");
            }
            System.out.println();
        }
    }
}


16、歌德巴赫猜想,任何一个大于六的偶数可以拆分成两个质数的和
  打印出所有的可能


//任何一个大于六的偶数可以拆分成两个质数的和
//打印出所有的可能
public class Gedebahe{
    public static void main(String args[]){
        int num = Integer.parseInt(args[0]);
        if(num<=6){
            System.out.println("参数错误!");
            return;
        }
        if(num%2!=0){
            System.out.println("参数错误!");
            return;
        }
        Gedebahe g = new Gedebahe();
        //1不是质数,2是偶数,因此从3开始循环
        for(int i=3;i<=num/2;i++){
            if(i%2==0){//如果为偶数,退出本次循环
                continue;
            }
            //当i与num-i都为质数时,满足条件,打印
            if(g.isPrime(i) && g.isPrime(num-i)){
                System.out.println(i+" + "+(num-i)+" = "+num);
            }        
        }
    }


 


 


 


 


 


 


第4章 数组
1. 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,然后求出所有元素的最大值,
最小值,平均值,和值,并输出出来。


class ArrayNumber{
    public static void main(String[] args){
        int[] arrayNumber;
        arrayNumber = new int[10];
    System.out.println("以下是随机的10个整数:");
        // 填入随机的 10个整数
        for (int i =0; i<arrayNumber.length; i++){
            arrayNumber[i] = (int)(100*Math.random());
            System.out.print(arrayNumber[i]+" ");
            }
        System.out.println();
        int max = arrayNumber[0];
        int min = arrayNumber[0];
        int sum = 0;
        for (int i =0; i<arrayNumber.length; i++){
            if(max < arrayNumber[i])
                max = arrayNumber[i];  //求最大值
            if(min > arrayNumber[i])
                min = arrayNumber[i];   //求最小值
            sum += arrayNumber[i];
            }
    System.out.println("其中 Max="+max+",Min="+min+",Sum="+sum+",Avg="+sum/10.0);
    }
}




2.定义一个int型的一维数组,包含10个元素,分别赋值为1~10, 然后将数组中的元素都向前移一个位置,
即,a[0]=a[1],a[1]=a[2],…最后一个元素的值是原来第一个元素的值,然后输出这个数组。


3. 定义一个int型的一维数组,包含40个元素,用来存储每个学员的成绩,循环产生40个0~100之间的随机整数,
将它们存储到一维数组中,然后统计成绩低于平均分的学员的人数,并输出出来。


4. (选做)承上题,将这40个成绩按照从高到低的顺序输出出来。


5,(选做)编写程序,将一个数组中的元素倒排过来。例如原数组为1,2,3,4,5;则倒排后数组中的值
为5,4,3,2,1。


6,要求定义一个int型数组a,包含100个元素,保存100个随机的4位数。再定义一个
   int型数组b,包含10个元素。统计a数组中的元素对10求余等于0的个数,保存
   到b[0]中;对10求余等于1的个数,保存到b[1]中,……依此类推。


class Remain{
    public  static void main( String[] args){
        int[] a = new int[100];


        //保存100个随机4位数到 a 中
        for (int i = 0;  i < a.length;  i++){
            a[i] = (int) (1000*Math.random());
        }


        //统计 a 数组中的元素对 10 求余的各个的数目
        int[] b = new int[10];
        int k,sum;
        for (int j = 0;  j < b.length;  j++){
            for (k=0,sum=0;  k < a.length;  k++){
                if ((a[k]%10)==j) sum++;
            }
            b[j] = sum;
            System.out.printf("b[%d]=%d/n",j,b[j]);
        }
    }
}




7,定义一个20*5的二维数组,用来存储某班级20位学员的5门课的成绩;这5门课
   按存储顺序依次为:core C++,coreJava,Servlet,JSP和EJB。
   (1)循环给二维数组的每一个元素赋0~100之间的随机整数。
   (2)按照列表的方式输出这些学员的每门课程的成绩。
   (3)要求编写程序求每个学员的总分,将其保留在另外一个一维数组中。
   (4)要求编写程序求所有学员的某门课程的平均分。


class Student{
    public static void main(String[] args ){
        int[][] mark = new int[20][5];
        // 给学生赋分数值,随机生成
        for ( int i = 0;  )
    }
}//未完成




 8,完成九宫格程序
    在井字形的格局中(只能是奇数格局),放入数字(数字由),使每行每列以及斜角线的和都相等


    经验规则:从 1 开始按顺序逐个填写; 1  放在第一行的中间位置;下一个数往右上角45度处填写;
        如果单边越界则按头尾相接地填;如果有填写冲突,则填到刚才位置的底下一格;
        如果有两边越界,则填到刚才位置的底下一格。


    个人认为,可以先把最中间的数填到九宫格的最中间位置;再按上面的规则逐个填写,而且
        填的时候还可以把头尾对应的数填到对应的格子中。(第 n 个值跟倒数第 n 个值对应,格局上以最中
        间格为轴心对应)
        这样就可以同时填两个数,效率比之前更高;其正确性有待数学论证(但多次实验之后都没发现有错)。
    九宫格的 1 至少还可以填在另外的三个位置,只是接下来的填写顺序需要相应改变;
    再根据九宫格的对称性,至少可以有8种不同的填写方式


import java.util.Scanner;
class NinePalace{
    public static void main(String[] args){
        // 定义 N 为九宫格的行列数,需要输入
        System.out.println("请输入九宫格的行列规模(只能是奇数的)");
        Scanner n = new Scanner(System.in);
        int N;


        //判断格局是否奇数 (可判断出偶数、负数 及小数)
        double d;
        while (true){
            d = n.nextDouble();
            N = (int)d;
            if ((d-N)>1.0E-4||N%2==0||N<0)
                {System.out.println("输入出错,格局只能是正奇数。请重新输入");}
            else break;
        }


        //老师的九宫格填写方法
        int[][] result = new int[N][N];   //定义保存九宫格的数组
        int row = 0; //行 初始位置
        int col = N/2; //列 初始位置,因为列由0开始,故N/2是中间位置
        for (int i=1;  i<=N*N; i++){
            result [row][col] = i;
            row--;
            col++;
            if (row<0&&col>=N){col--;row+=2;} //行列都越界
            else if (row<0){ row = N-1;}   //行越界
            else if (col>=N){col = 0;}  //列越界
            else if (result[row][col] != 0){col--;row+=2;}  //有冲突
        }


        //打印出九宫格
        for (int i=0;  i<N;  i++){
            for(int j=0;  j<N; j++){System.out.print(result[i][j]+"/t");}
            System.out.println();
        }


        //我个人的填格方式
        int[][] result2 = new int[N][N];  //为免冲突,重新 new 一个数组
        result2[N/2][N/2] = (N*N+1)/2;  //先把中间值赋予中间位置
        row = 0;   //定义行及列的初始赋值位置。之前赋值的for对两个值有影响,故需重新定位
        col = N/2;
        for (int i=1; i<=N*N/2; i++){
            result2[row][col] = i;
            //下面这句是把跟 i 对应的值放到格局对应的位置上
            result2[N-row-1][N-col-1] = N*N+1-i;
            row--;
            col++;
            if (row<0){ row = N-1;}   //行越界
            else if (col>=N){col = 0;}  //列越界
            else if (result2[row][col] != 0){col--;row+=2;}  //有冲突
            //这方法不可能出现行列两边都越界的情况,详情需要数学论证
        }


        System.out.println();
        //再次打印出九宫格,以对比验证
        for (int i=0;  i<N;  i++){
            for(int j=0;  j<N; j++){System.out.print(result2[i][j]+"/t");}
            System.out.println();
        }


    }
}


 


 


9,求一个3*3矩阵对角线元素之和 




10,打印杨辉三角




11. 约梭芬杀人法
   把犯人围成一圈,每次从固定位置开始算起,杀掉第7个人,直到剩下最后一个。


11_2、用数组实现约瑟夫出圈问题。 n个人排成一圈,从第一个人开始报数,从1开始报,报到m的人出圈,剩下的人继续开始从1报数,直到所有的人都出圈为止。对于给定的n,m,求出所有人的出圈顺序。


 


12. 判断随机整数是否是素数
产生100个0-999之间的随机整数,然后判断这100个随机整数哪些是素数,哪些不是?


public class PrimeTest{
    public static void main(String args[]){
        for(int i=0;i<100;i++){
            int num = (int)(Math.random()*1000);
            PrimeTest t = new PrimeTest();
            if(t.isPrime(num)){
                System.out.println(num+" 是素数!");
            }else{
                System.out.println(num+" 不是素数!");
            }
            System.out.println();
        }
    }
    public boolean isPrime(int num){
        for(int i=2;i<=num/2;i++){
            if(num%i==0){
                System.out.println(num+"第一个被"+i+"整除!");
                return false;
            }
        }
        return true;
    }
}


 


 


 


冒泡排序法:
//按从大到小的排序
int tmp = a[0];
for (int i=0; i < a.length; i++){
    for (int j=0; j < a.length - i -1; j++){
        if (a[j] < a[j+1]) {
            tmp = a[j];
            a[j] = a[j+1];
            a[j+1] = tmp;
        }
    }
}


 




day06 练习
某公司的雇员分为以下若干类:
Employee:这是所有员工总的父类,属性:员工的姓名和生日月份。
方法:getSalary(int month) 根据参数月份来确定工资,如果该月员工过生日,
则公司会额外奖励100元。
SalariedEmployee:Employee的子类,拿固定工资的员工。属性:月薪
HourlyEmployee:Employee的子类,按小时拿工资的员工,每月工作超出160
小时的部分按照1.5倍工资发放
属性:每小时的工资、每月工作的小时数
SalesEmployee:Employee的子类,销售人员,工资由月销售额和提成率决定
属性:月销售额、提成率
BasePlusSalesEmployee:SalesEmployee的子类,有固定底薪的销售人员,
工资由底薪加上销售提成部分     属性:底薪。


?public class TestEmployee{
    public static void main(String[]args){
        Employee[] es = new Employee[5];
        es[0] = new Employee("赵君",2);
        es[1] = new SalariedEmployee("宋婕", 1, 8000);
        es[2] = new HourlyEmployee("王超", 5, 10, 300);
        es[3] = new SalesEmployee("秋娥", 2, 200000, 0.05);
        es[4] = new BaseSalarySalesEmployee("郭镫鸿", 1, 1000000, 0.1, 10000);
        int month = 2;//本月为2月
        System.out.println("宇宙集团"+month+"月工资表:");
        for(int i=0; i<es.length; i++){
            System.out.println(es[i].getName()+":"+es[i].getSalary(month));
        }
    }
}


class Employee{
    private String name;
    private int birth;
    public String getName(){
        return name;
    }
    public Employee(String name, int birth){
        this.name = name;
        this.birth = birth;
    }
    public double getSalary(int month){
        if(month==birth){
            return 100;
        }
        return 0;
    }
}


class SalariedEmployee extends Employee{
    private double salary;
    public SalariedEmployee(String name, int birth, double salary){
        super(name, birth);
        this.salary = salary;
    }
    public double getSalary(int month){
        return salary + super.getSalary(month);
    }
}


class HourlyEmployee extends Employee{
    private double hourSalary;
    private int hour;
    public HourlyEmployee(String name, int birth, double hourSalary, int hour){
        super(name, birth);
        this.hourSalary = hourSalary;
        this.hour = hour;
    }
    public double getSalary(int month){
        if(hour<=160){
            return hourSalary*hour+super.getSalary(month);
        }else{
            return 160*hourSalary+(hour-160)*hourSalary*1.5+super.getSalary(month);
        }
    }
}


class SalesEmployee extends Employee{
    private double sales;
    private double pre;
    public SalesEmployee(String name, int birth, double sales, double pre){
        super(name, birth);
        this.sales = sales;
        this.pre = pre;
    }
    public double getSalary(int month){
        return sales*pre+super.getSalary(month);
    }
}


class BaseSalarySalesEmployee extends SalesEmployee{
    private double baseSalary;
    public BaseSalarySalesEmployee(String name, int birth, double sales, double pre, double baseSalary){
        super(name, birth, sales, pre);
        this.baseSalary = baseSalary;
    }
    public double getSalary(int month){
        return baseSalary+super.getSalary(month);
    }
}


 


/**
 * 在原有的雇员练习上修改代码
 * 公司会给SalaryEmployee每月另外发放2000元加班费,给
 * BasePlusSalesEmployee发放1000元加班费
 * 改写原有代码,加入以上的逻辑
 * 并写一个方法,打印出本月公司总共发放了多少加班费
 * @author Administrator
 *
 */
public class EmployeeTest {


    /**
     * @param args
     */
    public static void main(String[] args) {    
        Employee e[] = new Employee[4];
        e[0] = new SalariedEmployee("魏威",10,5000);
        e[1] = new HourlyEmployee("段利峰",8,80,242);
        e[2] = new SalesEmployee("林龙",11,300000,0.1);
        e[3] = new BasedPlusSalesEmployee("华溪",1,100000,0.15,1500);
        for(int i=0;i<e.length;i++){
            System.out.println(e[i].getName()+": "+e[i].getSalary(11));
        }
        
        //统计加班费
        int result = 0;
//        for(int i=0;i<e.length;i++){
//            if(e[i] instanceof SalariedEmployee){
//                SalariedEmployee s = (SalariedEmployee)e[i];
//                result += s.getAddtionalSalary();
//            }
//            if(e[i] instanceof BasedPlusSalesEmployee){
//                BasedPlusSalesEmployee b = (BasedPlusSalesEmployee)e[i];
//                result += b.getAddtionalSalary();
//            }
//        }
        
        for(int i=0;i<e.length;i++){
            result += e[i].getAddtionalSalary();
        }
        System.out.println("加班费: "+result);
    }
}


interface AddtionalSalary{
    int getAddtionalSalary();
}


class Employee implements AddtionalSalary{
    private String name;//员工姓名
    private int birth;//员工生日月份
    public Employee(String name,int birth){
        this.name = name;
        this.birth = birth;
    }
    public int getSalary(int month){
        int result = 0;
        if(month==birth)
            result = 100;
        return result;
    }
    public String getName(){
        return name;
    }
    
    public int getAddtionalSalary(){
        return 0;
    }
}


class SalariedEmployee extends Employee{
    private int salaryPerMonth;
    public SalariedEmployee(String name,int birth,int salaryPerMonth){
        super(name,birth);
        this.salaryPerMonth = salaryPerMonth;
    }
    public int getSalary(int month){
        return this.salaryPerMonth + super.getSalary(month)+
            this.getAddtionalSalary();
    }
    public int getAddtionalSalary(){        
        return 2000;
    }
}


class HourlyEmployee extends Employee{
    private int salaryPerHour;
    private int hoursPerMonth;
    public HourlyEmployee(String name,int birth,int salaryPerHour,int hoursPerMonth){
        super(name,birth);
        this.salaryPerHour = salaryPerHour;
        this.hoursPerMonth = hoursPerMonth;
    }
    public int getSalary(int month){
        int result = 0;
        if(this.hoursPerMonth<=160){
            result = hoursPerMonth*salaryPerHour;
        }else{
            result = 160*salaryPerHour + 
            (int)((hoursPerMonth-160)*1.5*salaryPerHour);
        }
        return result+super.getSalary(month);
    }
}


class SalesEmployee extends Employee{
    private int sales;
    private double rate;
    public SalesEmployee(String name,int birth,int sales,double rate){
        super(name,birth);
        this.sales = sales;
        this.rate = rate;
    }
    public int getSalary(int month){
        return (int)(sales*rate)+super.getSalary(month);
    }
}


class BasedPlusSalesEmployee extends SalesEmployee{
    private int basedSalary;
    public BasedPlusSalesEmployee(String name,int birth,int sales,double rate,int basedSalary){
        super(name,birth,sales,rate);
        this.basedSalary = basedSalary;
    }
    public int getSalary(int month){
        return this.basedSalary+super.getSalary(month) +
        this.getAddtionalSalary();
    }
    public int getAddtionalSalary(){        
        return 1000;
    }
}




经典算法:
1. 某学校为学生分配宿舍,每6个人一间房(不考虑性别差异),问需要多少房?
答案:  (x+5)/6
注意理解int类型数值。


2. 让数值在 0~9 之间循环。
public class test{
    public static void main(String[] args){
        int i=0;
        while(true){
            i = (i+1)%10;
            System.out.println(i);
        }
    }
}


 


作业:
1. 写一个数组类(放对象):
     功能包括:添加(添加不限制多少项)、修改、插入、删除、查询
 class MyArray{
         private Object[] os = new Object[10];
         public void add(Object o);
         public void set(int index, Object o);
         public void insert(int index, Objecto);
         public void remove(int index);
         public void remove(Object o);
         public Object get(int index);
         
 }
 
 public class TestMyArray{
     public static void main(String[]args){
         MyArray ma = new MyArray();
         ma.add("aaa");
         ma.add("bbb");
         ma.add("ccc");
         Object o = ma.get(1);
         Iterator it = ma.iterator();
         while(it.hasNext()){
             Object o1 = it.next();
             System.out.println(o1);
         }
     }
 }


 




作业 10-08
1. 随机产生 20 个整数(10以内的),放入一个ArrayList中, 用迭代器遍历这个ArrayList
2. 并删除其中为 5 的数
3. 再产生 3 个整数,插入到位置 4 处
4. 把所有值为 1 的数都变成 10


import java.util.ArrayList;
class ArrayList{
         private Object[] os = new Object[20];


}


 public class TestArray{
     public static void main(String[]args){
         ArrayList a = new ArrayList();
         
         ma.add("aaa");
         ma.add("bbb");
         ma.add("ccc");
         Object o = ma.get(1);
         Iterator it = ma.iterator();
         while(it.hasNext()){
             Object o1 = it.next();
             System.out.println(o1);
         }
     }
 }




1. 产生 3000 个 10 以内的数,放入 hashSet
2. 遍历它,打印每一个值
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
public class TestHashSet {
    public static void main(String[] args) {
        Random r = new Random();
        HashSet hs1 = new HashSet();
        for(int i=0; i<3000; i++){
            hs1.add(r.nextInt(10));
        }
        Iterator it1 = hs1.iterator();
        while(it1.hasNext()){
            System.out.print(it1.next()+" ");
        }
    }
}
//由于 HashSet 不能重复,所以只有10个数在里面,按哈希排序
2 4 9 8 6 1 3 7 5 0


 


 


 


/*
 * 测试TreeSet 的比较器,
 * 在有自己的比较器的情况下,如何实现Comparable接口
 */


import java.util.*;
class Teacher{
    int id;
    String name;
    int age;    
    public Teacher() {}
    public Teacher(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public int getId() {    return id;    }
    public void setId(int id) {this.id = id;    }
    public String getName() {    return name;}
    public void setName(String name) {    this.name = name;}
    public int getAge() {return age;}    
    public void setAge(int age) {this.age = age;}
    
    public int TeacherComparator(Object o){
        Teacher t1 = (Teacher) o;
        if(t1.getId() > id){return 1;}
        else if (t1.getId() < id){return -1;}
        return 0;
    }
}
class TreeSet{
    
}


class Test {
    public static void main(String[] args) {
        String s1 = new String("aaa");
        String s2 = new String("bbb");
        String s3 = new String("aaa");
        System.out.println(s1==s3);
        System.out.println(s1.equals(s3));
        
        HashSet hs = new HashSet();
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        Iterator it = hs.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        System.out.printf("%x/n",s1.hashCode());
        System.out.printf("%x/n",s2.hashCode());
        System.out.printf("%x/n",s3.hashCode());
    }
}


 


1. 在Map中,以name作Key,以Student类 作Velue,写一个HashMap
import java.util.*;
class Student{
    int id;
    String name;
    int age;    
    public Student() {}
    public Student( int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}    
    public void setAge(int age) {this.age = age;}
}


class TestHashMap{
    public static void main(String[] args) {
        HashMap hm = new HashMap();
        Student s1 = new Student(1,"jacky",19);
        hm.put("jacky",s1);
        hm.put("tom",new Student(2,"tom",21));
        hm.put("kitty",new Student(3,"kitty",17));
        
        Iterator it = hm.keySet().iterator();
        while(it.hasNext()){
            Object key =  it.next();
            Student value = (Student) hm.get(key);
            System.out.println(key+":id="+value.id+",age="+value.age);
        }
        System.out.println("=============================");
        
        //比较 KeySet() 和 entrySet() 两种迭代方式
        for(Iterator i1 = hm.entrySet().iterator(); i1.hasNext(); )
        { Map.Entry me = (Map.Entry) i1.next();
       Student s = (Student) me.getValue();
            System.out.println(me.getKey()+": id="+s.id+" age="+s.age);
        }        
    }
}


 




day13 homework
1.
/**********************************************************************************
自己写一个栈:     ( 先进后出 )
     建议底层用LinkedList实现
参照 java.util.Stack
方法:  boolean empty()  测试堆栈是否为空。
    E  peek()        查看栈顶对象而不移除它。
    E  pop()         移除栈顶对象并作为此函数的值返回该对象。
    E  push(E item)  把项压入栈顶。
    int     search(Object o)     返回对象在栈中的位置,以 1 为基数。
***************************************************************************************/
//不能用继承,因为它破坏封装。只需调用即可
import java.util.LinkedList;
class MyStack<E>{
    private LinkedList<E> list = new LinkedList<E>();
    public  boolean empty()     {return list.isEmpty();}
    public  E peek()            {return list.peek();   }
    public  E pop()             {return list.poll();   }
    public  void push(E o)      {list.addFirst(o);     }    
    
    //int    indexOf(Object o) 返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
    public  int search(Object o){return list.indexOf(o);}
}


 


2. 
/***************************************************************************************
定义以下类,完成后面的问题,并验证。
Exam类   考试类
属性: 若干学生  一张考卷
提示:学生采用HashSet存放


Paper类   考卷类  
属性:若干试题
提示:试题采用HashMap存放,key为String,表示题号,value为试题对象


Student类     学生类
属性:姓名   一张答卷   一张考卷  考试成绩


Question类    试题类
属性:题号 题目描述    若干选项    正确答案
提示:若干选项用ArrayList


AnswerSheet类    答卷类
属性:每道题的答案    
提示:答卷中每道题的答案用HashMap存放,key为String,表示题号,value为学生的答案


问题:为Exam类添加一个方法,用来为所有学生判卷,并打印成绩排名(名次、姓名、成绩)
***************************************************************************************/


 


 


 


 


 


 


 


 


3. 
/***************************************************************************************
项目:商品管理系统
功能:增删改查 (可按各种属性查)
商品属性:名称、价格(两位小数)、种类
***************************************************************************************/


 


 


 


 


 


 


 


 


 


 


day17 图形界面
1. 计算器
/*****************例题 画出计算器的界面*****************************
界面如下:
     1   2   3   +
     4   5   6   -
     7   8   9   *
     0   .   =   /
 *******************/
import java.awt.*;
import javax.swing.*;


class Calculator {
   public static void main(String[] args){
      JTextField text = new JTextField();
      JFrame f = new JFrame("计算器");
      Font font = new Font("宋体", Font.BOLD, 25);//"宋体"想写成默认,则写“null”
      text.setFont(font); //定义字体
      text.setHorizontalAlignment(JTextField.RIGHT);//令text的文字从右边起
      text.setEditable(false);//设置文本不可修改,默认可修改(true)
      f.add(text, BorderLayout.NORTH);//Frame和Dialog的默认布局管理器是Border Layout
      ButtonActionListener listener = new ButtonActionListener(text);//事件反应在text中
      JPanel buttonPanel = new JPanel();//设法把计算器键盘放到这个Jpanel按钮上
      String op = "123+456-789*0.=/";
      GridLayout gridlayout = new GridLayout(4,4,10,10);
      buttonPanel.setLayout(gridlayout);//把计算器键盘放到buttonPanel按钮上
      for(int i=0; i<op.length(); i++){
          char c = op.charAt(i); //拿到字符串的第i个字符
          JButton b = new JButton(c+"");//把字符放到按钮上
          b.addActionListener(listener);//在按钮上放置监听器,每次按都会有反应
          buttonPanel.add(b);//把按钮放到buttonPanel上
      }//这个循环很值得学习,很常用
      f.add(buttonPanel/*, BorderLayout.CENTER*/);  //默认添加到CENTER位置
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.setSize(300, 250);
      f.setVisible(true);//这句要放到最后,等事件完成后再显示
}}


//监听者
class ButtonActionListener implements ActionListener{
    private JTextField textField;
    public ButtonActionListener(JTextField textField) {
        this.textField = textField;
    }
    public void actionPerformed(ActionEvent e) {//必须覆盖它的actionPerformed()
        
        textField.append("哈哈,放了几个字/n");
}}
/*********************未实现计算器的具体功能*******************************/


 


2. 扫雷游戏


 


3. 俄罗斯方块


 


 


 


 


day19 多线程
写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z。打印顺序为12A34B56C……5152Z。要求用线程间的通信。
       注:分别给两个对象构造一个对象o,数字每打印两个或字母每打印一个就执行o.wait()。
       在o.wait()之前不要忘了写o.notify()。
class Test{
    public static void main(String[] args) {
        Printer p = new Printer();
        Thread t1 = new NumberPrinter(p);
        Thread t2 = new LetterPrinter(p);
        t1.start();
        t2.start();
    }    
}
class Printer{
    private int index = 1;//设为1,方便计算3的倍数
    //打印数字的构造方法,每打印两个数字,等待打印一个字母
    public synchronized void print(int i){
        while(index%3==0){try{wait();}catch(Exception e){}}
        System.out.print(" "+i);
        index++;
        notifyAll();
    }
    //打印字母,每打印一个字母,等待打印两个数字
    public synchronized void print(char c){
        while(index%3!=0){try{wait();}catch(Exception e){}}
        System.out.print(" "+c);
        index++;
        notifyAll();
    }    
}
//打印数字的线程
class NumberPrinter extends Thread{
    private Printer p;
    public NumberPrinter(Printer p){this.p = p;}
    public void run(){
        for(int i = 1; i<=52; i++){
            p.print(i);
        }
    }
}
//打印字母的线程
class LetterPrinter extends Thread{
    private Printer p;
    public LetterPrinter(Printer p){this.p = p;}
    public void run(){
        for(char c='A'; c<='Z'; c++){
            p.print(c);
        }
    }
}
/*如果这题中,想保存需要打印的结果,可在Printer类里定义一个成员变量
String s = ""; //不写“”的话是null,null跟没有东西是不一样的,它会把null当成字符 =_=
然后在两个print()方法里面,while循环后分别加上 s = s + " "+i; 以及 s = s +" "+ c;*/


 


 


 


 


 


J2ee | 评论:0 | Trackbacks:0 | 阅读:66


04. CVS note
Submitted by cnetsa on 2009, July 29, 2:19 PM


cvs login  密码


Elipse   Host:192.168.0.23    Connection type:pserver
         Repository path:/user/cvs/openlab    User:sd08077    Password:u0


启用mysql: 直接在终端输入 mysql -u root
第一次创建项目:
cvs import project_name  tarena start (创建沙箱--项目)


 


CVS commands are:
        add          Add a new file/directory to the repository
        admin        Administration front end for rcs
        annotate     Show last revision where each line was modified
        checkout     Checkout sources for editing
        commit       Check files into the repository提交
        diff         Show differences between revisions查看两版本间的差异。
                     用法:diff 版本1 版本2    分左右两页显示:diff side-by-side 版本1 版本2
        edit         Get ready to edit a watched file
        editors      See who is editing a watched file
        export       Export sources from CVS, similar to checkout
        history      Show repository access history
        import       Import sources into CVS, using vendor branches
        init         Create a CVS repository if it doesn't exist
        log          Print out history information for files
        login        Prompt for password for authenticating server
        logout       Removes entry in .cvspass for remote repository
        pserver      Password server mode
        rannotate    Show last revision where each line of module was modified
        rdiff        Create 'patch' format diffs between releases
        release      Indicate that a Module is no longer in use
        remove       Remove an entry from the repository
        rlog         Print out history information for a module
        rtag         Add a symbolic tag to a module
        server       Server mode
        status       Display status information on checked out files
        tag          Add a symbolic tag to checked out version of files
        unedit       Undo an edit command
        update       Bring work tree in sync with repository
        version      Show current CVS version(s)
        watch        Set watches
        watchers     See who is watching a file
(Specify the --help option for a list of other help options)


J2ee | 评论:0 | Trackbacks:0 | 阅读:65


03. PL SQL笔记
Submitted by cnetsa on 2009, July 29, 2:19 PM




大小写不敏感。


打印单引号时,需加多一个单引号作转义符。即连用两个单引号才可以打印一个。
    Begin dbms_output.put_line('I''m leaning');
    End;


注释:
单行注释:由两个连字符开始,到行尾。
   --Available 
多行注释:由“/*”开头,“*/” 结尾。
 如果在一行代码还没有写完前(指分号结束此语句之前),一定要使用多行注释格式。
 SQL中的一行代码,不是按手写的格式定义的,而是按计算机解释格式(没有分号终止前,即使按了回车也都解释为一行)




      PL/SQL 块语法
[DECLARE]
        ---declaration statements
BEGIN
        ---executable statements
[EXCEPTION]
        ---exception statements
END




变量声明:Declare
    变量名  Type [Constant] [Not Null] [:=value];
 “:=”是赋值,而“=”是比较语句。


数据类型:
        %Type       可以取出某字段的类型;如:s_emp.last_name%Type  --相当于Varchar2。
        %Rowtype    返回一整行的记录类型;如:s_emp%Rowtype       --这得注意各字段的顺序。


数字型:
Number:
Binary_Integer:


BoolLean类型:只有 True 和 False 两类。
        其中,Dull = False。


程序执行顺序的类型可分三种:
顺序
选择
        If ... Then ...
        Elsif  ... Then ...
        Else ...
        End If;
循环
一、LOOP循环:
  1.   Loop ... ;  Exit When boolean_expr ; End Loop;
  2.   Loop  If boolean_expr  Then  Exit;  End If;
            ...;  End Loop;


二、While循环:
       While  boolean_expression
          Loop  ...  End Loop;
        其中boolean_expression 值为False则立即退出循环。免另外写结束条件。
        可以使用Exit或Exit When 语句终止循环处理。


三、For循环:
       For loop_count IN [Reverse] low_bound..high_bound
             Loop   ...;   End Loop;
        免声明,默认loop_count为数值型。简化结束条件。
        IN Reverse 表示倒过来,由大值自减到小值
        例:        For cnt  IN  1..5  Loop  ...;   End Loop;        /*由1到5*/


 


常用PLSQL语句:
    DBMS_OUTPUT.PUT_LINE('v_Num_3 = ' || v_Num_3); --文件中要打印一些内容
    set serveroutput on; --使终端可打印出文件的结果; 使输出无效:set serveroutput off;


MySQL 常用语句:
 终端登录: mysql -u username -p password  --进入本机的mysql;没设密码就留空
    执行脚本: mysql -u 用户名 -密码 </.../xxx.sql 
 执行脚本2:先登录,再 source /.../xxx.sql
    show variables like '%char%';  --查看数据库的字符集;utf8或gbk的则可支持中文
    show databases; --显示所有数据库目录
    use 数据库名; --进入某个数据库 (可以有很多个,我把这些数据库看作目录,这点不同于oracle) 
    show tables; --显示此目录下各表格的名字
    desc 表名; --我们可以查看某个表中的数据类型结构
    create database 数据库名;  --建数据库目录
    drop database 数据库名;  --删除数据库整个目录
    create table 表名;  --建表(同oracle) 
    drop table 表名; --连删表(删多个表用逗号隔开)


 


Note that all text commands must be first on line and end with ';'
?         (/?) Synonym for 'help'.
clear     (/c) Clear command.
connect   (/r) Reconnect to the server. Optional arguments are db and host.
delimiter (/d) Set statement delimiter. NOTE: Takes the rest of the line as new delimiter.
edit      (/e) Edit command with $EDITOR.
ego       (/G) Send command to mysql server, display result vertically.
exit      (/q) Exit mysql. Same as quit.
go        (/g) Send command to mysql server.
help      (/h) Display this help.
nopager   (/n) Disable pager, print to stdout.
notee     (/t) Donot write into outfile.
pager     (/P) Set PAGER [to_pager]. Print the query results via PAGER.
print     (/p) Print current command.
prompt    (/R) Change your mysql prompt.
quit      (/q) Quit mysql.
rehash    (/#) Rebuild completion hash.
source    (/.) Execute an SQL script file. Takes a file name as an argument.
status    (/s) Get status information from the server.
system    (/!) Execute a system shell command.
tee       (/T) Set outfile [to_outfile]. Append everything into given outfile.
use       (/u) Use another database. Takes database name as argument.
charset   (/C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
warnings  (/W) Show warnings after every statement.
nowarning (/w) Donot show warnings after every statement.


 


J2ee | 评论:0 | Trackbacks:0 | 阅读:64


02. SQL 笔记
Submitted by cnetsa on 2009, July 29, 2:18 PM


先登陆服务器:    telnet 192.168.0.23    公帐号: openlab-open123    tarena-tarena
再进入SQL:sqlplus sd0807/sd0807        帐号:sd0807-密码同样  公帐号:openlab-open123


设置环境变量:
   ORACLE_SID=oral10g/    --变局部变量
   export ORACLE_SID      --变全局变量
   unset ORACLE_SID       --卸载环境变量
   ORACLE_HOME=...        --安装路径;直接用一句语句也可以,如下
   export ORACLE_HOME=/oracledata/.../bin:


一、注意事项:
    大小写不敏感,即不区分大小写。提倡关键字大写,便于阅读和调式。
    “!”在SQL环境下执行Unix命令。
    SQL语句是由简单的英语单词构成;这些英语单词称为关键字/保留字,不做它用。SQL由多个关键字构成。
    SQL语句由子句构成,有些子句是必须的,有些是可选的。
    在处理SQL语句时,其中所有的空格都被忽略(空格只用来分开单词,连续多个空格当一个用)。
    SQL语句可以在一行上写出,建议多行写出,便于阅读和调试。
    多条SQL语句必须以分号分隔。多数DBMS不需要在单条SQL语句后加分号,但特定的DBMS可能必须在单条SQL语句后加分号。
    SQL语句的最后一句要以 “;”号结束


二、写子句顺序
    Select column,group_function
    From  table
    [Where  condition]
    [Group by  group_by_expression]
    [Having    group_condition]
    ……
    [Order by  column];    --最后


三、常用简单语句:
    clear screen:清屏
    edit:编辑刚才的一句。
    desc/describe:(列出所有列名称)
        用法: DESCRIBE [schema.]object[@db_link]
    dual:亚表,临时用。如:desc dual;/from dual;
    rollback:回溯,回溯到上次操作前的状态,把这次事务操作作废,只有一次(DDL和DCL语句会自动提交,不能回溯)。
        可以用commit语句提交,这样就回溯不回了。
 set pause on/off :设置分屏(设置不分屏) 
 set pause "please put an enter key" 且 set pause on:设置带有提示的分屏
 oerr ora 904 :查看错误
 set head off :去掉表头
 set feed off :去掉表尾
 保存在oracle数据库中的所有操作细节:
     spool oracleday01.txt :开始记录
     spool off :开始保存细节


四、SELECT语句:选择操作、投影操作。
select:从一个或多个表中检索一个或多个数据列。包含信息:想选择什么表,从什么地方选择。必须要有From子句。(最常用)
        当从多张表里查询的时候,会产生笛卡尔积;可用条件过滤它。
        当两个表有相同字段时必须加前缀,列名前需加表名和“.”,如“s_emp.id”。
    1、用法:SELECT  columns,prod2,prod3<列>  FROM Table1,table2<表名>  分号结束
       如: select id from s_emp;
           select last_name,name from s_emp,s_dept where s_emp.dept_id=s_dept.id;--列表每人所在部门
           SELECT *  FROM Products;    --检索所有列。
           数据太多时,最好别使用上句,会使DBMS降低检索和应用程序的性能。(*通配符)
    2、对数据类型的列可进行运算(如加减乘除)。
    3、对列起别名:有直接起别名,加AS起别名,用双引号起别名等三种方法
       (单引号,引起字符串;双引号,引起别名。起别名有符号,或者区分大小写时,必须用双引号)
        多表查询时,可给表起别名。(给列起别名,列<空格>列别名;给表起别名,表<空格>表别名;)。
        如:Select first_name EMPLOYEES, 12*(salary+100) AS MONEY, manager_id "ID1" From s_emp E;
    4、字段的拼接,可用双竖线(双竖线只能用于select语句里)。不同的DBMS可能使用不同的操作符;拼接的字段同样可以起别名。
        如:Select  first_name ||' '|| last_name || ', '|| title "Employees" From s_emp;


排他锁:Select id,salary  From s_emp where id=1  For Update;
   可以阻止他人并发的修改,直到你解锁。
   如果已有锁则自动退出:Select id,salary From s_emp where id=1 For Update NoWait;
   FOR UPDATE :可以再加 OF 精确到某格。如:   ... For Update  OF salary ...  
   注意要解锁。


五、ORDER BY 子句,排序
Order by:按某排序列表(默认升序 asc,由低到高;可加 desc,改成降序由高到低)
    检索返回数据的顺序没有特殊意义,为了明确地排序用 SELECT 语句检索出的数据,可使用 ORDER BY 子句。
    ORDER BY 子句取一个或多个列的名字。
    对空值,按无穷大处理(升序中,空值排最后;降序中排最前)。
    1、用法:Select prod_id,prod_price,prod_name From Products  Order By  prod_price,prod_name; 
      (从左到右执行排序,先排price)
       ORDER BY子句中使用的列将是为显示所选择的列,但是实际上并不一定要这样,用非检索的列排序数据是完全合法的。
       为了按多个列排序,列名之间用逗号分开。
    2、支持按相对列位置进行排序。
       输入 SELECT prod_id,prod_price,prod_name
       FROM  Products
       ORDER BY 2,3    --(2指price,3指name)
    3、升序、降序。默认是升序(asc,从小到大排序),想降序时用desc。
       如:SELECT prod_id,prod_price,prod_name FROM  Products ORDER BY prod_price DESC;
      注意:DESC 关键字只应用到直接位于其前面的列名。如果想在多个列上进行排序,必须对每个列指定DESC关键字。
         升序是默认的,可不写,但降序必须写。




六、WHERE子句,选择、过滤
    其后只能跟逻辑语句,返回值只有ture或false
    如: select last_name,salary from s_emp where salary=1000;--找出工资1000的人


WHERE子句操作符:
    1、逻辑比较运算符
        =        等于
        !=       不等于,还有(<>  ^=   这两个同样表示不等于)
        >         大于
        >=        大于等于
        <         小于
        <=        小于等于


    2、SQL 比较运算符
    between…and…    :在两者之间。(BETWEEN 小值 AND 大值)
        如:select last_name,salary from s_emp where salary between 1000 and 1500;
          --工资1000到1500的人,包括1000和1500。
    in(列表):在列表里面的。
        如:select last_name,dept_id from s_emp where dept_id in(41,42);第41、42部门的人
    like    : 包含某内容的。模糊查询
        可以利用通配符创建比较特定数据的搜索模式,通配符只能用于文本,非文本数据类型不能使用通配符。
        通配符在搜索模式中任意位置使用,并且可以使用多个通配符。
        通配符%表示任何字符出现任意次数;还能代表搜索模式中给定位置的0个或多个字符。下划线匹配单个任意字符。
        如:select table_name from user_tables where table_name like 'S/_%' escape'/';
        '  找出“S_“开头的,由于下划线有任意字符的含义,故需另外定义转移符。
           但习惯用“/”,为方便其他程序员阅读和检测,一般不改用其他的。
        like 'M%':M开头的        like '_a%':第二个字符是a的    like '%a%'所有含a的
            (“_”表示一个任意字符;“%”表示任意多个任意字符。)
        单引号里面的内容,大小写敏感。单引号用来限定字符串,
        如果将值与串类型的列进行比较,则需要限定引号;用来与数值列进行比较时,不用引号。
    is null:是空。(NULL表示不包含值。与空格、0是不同的。)
        如:SELECT prod_name,prod_price FROM Products WHERE prod_price IS NULL;




七、高级检索(逻辑运算符):
    通常我们需要根据多个条件检索数据。可以使用AND或OR、NOT等连接相关的条件
    计算次序可以通过圆括号()来明确地分组。不要过分依赖默认计算次序,使用圆括号()没有坏处,它能消除二义性。
    
    and:条件与
       如 SELECT prod_id,prod_price,prod_name FROM Products WHERE prod_price<4 AND vend_id=‘DELL’
    or:条件或    (注: and 的优先级比 or 更高,改变优先级可用括号)
       如 SELECT prod_id,prod_price,prod_name FROM Products WHERE prod_price<4 OR vend_id=‘DELL’
    not:条件非。否定它之后所跟的任何条件
        否定的SQL 比较运算符: NOT BETWEEN; NOT IN; NOT LIKE; IS NOT NULL:
         (注意,按英语习惯用 is not,而不是 not is)
        NOT 与 IN 在一起使用时,NOT 是找出与条件列表不匹配的行。
        IN 列表里有 NULL 时不处理,不影响结果;用 NOT IN 时,有 NULL 则出错,必须排除空值再运算。
    in :选择列表的条件
        使用IN操作符的优点: 在长的选项清单时,语法直观; 计算的次序容易管理;
        比 OR 操作符清单执行更快;最大优点是可以包含其他 SELECT 语句,使用能够动态地建立 WHERE 子句。
     如 SELECT prod_id,prod_price,prod_name FROM Products WHERE vend_id IN(‘DELL’,’RBER’,’TTSR’);




八、单行函数:
    函数一般在数据上执行,它给数据的转换和处理提供了方便。不同的DBMS提供的函数不同。
    函数可能会带来系统的不可移植性(可移植性:所编写的代码可以在多个系统上运行)。
    加入注释是一个使用函数的好习惯。
    大多数SQL实现支持以下类型的函数: 文本处理, 算术运算, 日期和时间, 数值处理。


Null:空值
    空值当成无穷大处理,所有空值参与的运算皆为空。
    空值与空值并不相等,因为空值不能直接运算。
    如:prod_price=""     这种写法是错的(不要受到corejava的影响)
    prod_price=NULL      这种写法是错的(不要受到corejava的影响)
    prod_price IS NULL   这种写法才是对的
NVL:处理空值,把空值转化为指定值。可转化为日期、字符、数值等三种(注意:转化时,两参数必须要同类型)
    如:NVL(date, '01-JAN-95')    NVL(title,'NO Title Yet')        NVL(salary,0)
    错误写法:
     Select last_name,title,salary*commission_pct/100 COMM From s_emp;--没提成的人没法显示工资
    正确写法:
     Select last_name,title,salary*NVL(commission_pct,0)/100 COMM From s_emp;--把提成是空值的转化为0


DISTINCT:过滤重复
    把重复的行过滤掉;多个字段组合时,只排除组合重复的。
    DISTINCT必须使用列名,不能使用计算或者表达式。
    所有的聚合函数都可以使用。如果指定列名,则DISTINCT只能用于COUNT(列名),DISTINCT不能用于COUNT(*)。
    如:Select  Distinct  name  From  s_dept; Select Distinct dept_id,title  From s_emp;


文本处理:
TRIM()/LTRIM()/RTIRM():去空格。只能去掉头和尾的空格,中间的不理。
     trim('   heo Are  fdou   ')  -->  heo Are  fdou
     输入:select trim('   heo Are  fdou   ')  from dual; -->:heo Are  fdou
LOWER:转小写
    lower('SQL Course') --> sql course
UPPER:转大写
    upper('    SQL Course') --->SQL COURSE
INITCAP:首字母转大写,其余转小写
    initcap(SQL Course')  '--> Sql Course 


CONCAT:合成。双竖线只能在select语句里面用,这个可用于任何语句。
    Concat('Good','String') --> GoodString
SUBSTR:截取。
    Substr('String', 1 ,3)  --> Str
        第一个数字“1”,表示从第几个开始截取;若要从倒数第几个开始,用负数,如“-2”表示倒数第2个。
        上式中第2个数字“3”表示截取多少个。
LENGTH:统计长度。
    Length('String') --> 6
NVL:转换空值


日期和时间处理:
    Oracle日期格式:DD-MMM-YYYY        (D代表日期date,M代表月month,Y代表年year)
    如:SELECT prod_name              (DAY表示完整的星期几,DY显示星期的前三个字母)
        FROM Products
        WHERE prod_time BETWEEN
            to_date(’01-JAN-2008’)
        AND to_date(’31-DEC-2008’);
    日期可以进行加减,默认单位是1天。日期与日期可以相减,得出天数;日期与日期但不能相加。
sysdate   ->  系统的当天
Months_Between('01-Sep-95','11-Jan-94')  --> 19.774194    相差多少个月,Between里面也可以填函数。
Add_months('11-Jan-94',6)                -->  11-Jul-94   增加多少个月
Next_day('01-Sep-95','Friday')   --> '08-Sep-95'    下一个星期五。其中的'Friday'可用6替代,因为星期日=1
Last_day('01-Sep-95')            -->  '30-Sep-95'   这个月的最后一天




数值处理:可以运用于代数,三角,几何
ROUND:四舍五入
    Round(45.925,2)  -> 45.93        Round(45.925,0)  -> 46        Round(45.925,-1)  -> 50
    逗号前一个数是要处理的数据源,后一个参数表示保留多少位小数。
    后一参数是负数时,表示舍去小数点前的几位,例3是舍去个位及其后的。不写后一参数时,默认不保留小数。
TRUNC:舍去末位。直接舍去,不会进位。
    Trung(45.925,2)  -> 45.92       Trung(45.925,2)  -> 45.92       Trung(45.925,2)  -> 45.92
日期的舍取:




常用的数值处理函数有:
    ABS()    绝对值        ABS(-5741.5854) --> 5741.5854
    PI()     圆周率        注意:oracle中不支持 PI()函数;MYSql 支持PI()函数。
    SIN()    正统值             Oracle还支持COS()、ASIN()、ACOS()函数
    SQRT()   平方根




  转化:
TO_CHAR(number,'fmt'):把数值转换成字符串
    显示数字的命令
    9:正常显示数字;
    0:显示包括0的数值形式,空位强制补0;
    $:以美元符号显示货币;
    L:按当前环境显示相关的货币符号;
    . 和,:在固定位置出现“.”点 和“,”逗号;不够位时,四舍五入。
   例题:SQL> select 'Order'||To_char(id)||
  2  'was filled for a total of'
  3  ||To_char(total,'fm$9,999,999')
  4  from s_ord
  5  where ship_date ='21-SEP-92';


TO_NUMBER(char):把字符转换成数字




九、链接
内链接:严格匹配两表的记录。
外链接分左链接和右链接:
    会使用一方表中的所有记录去和另一格表中的记录按条件匹配,空值也会匹配,这个表中的所有记录都会显示,
    数据库会模拟出记录去和那些不匹配的记录匹配。
左链接  加号在右面 
    如:有 TABLE1   TABLE2
        1的一条记录在2里面没有匹配上,那么1里面的记录保留
        2的一条记录在1里面没有匹配上 ,那么2丢弃
右链接正好相反
    --例题:哪些人是领导。
    select distinct b.id,b.last_name manager
    from s_emp a,s_emp b
    where a.manager_id=b.id(+);
左右顺序有区别,这是另外新建一个表,要显示的是第二个表格的内容。
+放在没有匹配行的表一侧,令表格能完整显示出来。


标准写法:内连接用INNER,左连接用LEFT,右连接用RIGHT。
    select distinct b.id,b.last_name manager
    from s_emp a LEFT join s_emp b
    ON a.manager_id=b.id;




十、组函数:
    分组允许将数据分为多个逻辑组,以便能对每个组进行聚集计算。
Group:分组
Group by:分组。(默认按升序对所分的组排序;想要降序要用 order by)可以包括任意数目的列。
    如果嵌入了分组,数据将在最后规定的分组上进行汇总。
    GROUP BY 子句中列出的每个列都必须是检索列或有效的表达式,但不能是聚集函数。
    *如果在SELECT 中使用表达式,则必须在GROUP BY子句中指定相同的表达式,不能使用别名。
    除聚合计算语句外,SELECT语句中的每个列都必须在GROUP BY子句中给出。
    如果分组列中具有NULL值,则NULL将作为一个分组返回。如果列中有多行NULL,它们将分为一组。


Having:过滤。分组之后,不能再用where,要用having 选择过滤。Having不能单独存在,必须跟在group by后面。
    WHERE在数据分组前进行过滤,HAVING在数据分组后过滤。
    可以在SQL中同时使用 WHERE和HAVING,先执行WHERE,再执行HAVING。




  聚合函数:
AVG:平均值    (忽略值为NULL的行,但不能用 AVG(*))
COUNT:计数    (Count(列)不计算空值;但 COUNT(*)表示统计表中所有行数,包含空值)
MAX:最大值    (忽略列值为 NULL 的行。但有些DBMS还允许返回文本列中的最大值,
               在作用于文本数据时,如果数据按照相应的列排序,则 MAX()返回最后一行。)
MIN:最小值    (忽略值为 NULL 的行。不能用 MIN(*)。一般是找出数值或者日期值的最小值。
               但有些DBMS还允许返回文本列中的最小值,这时返回文本最前一行)
SUM:求和      (忽略值为 NULL 的值。SUM 不能作用于字符串类型,而 MAX(),MIN()函数能。也不能 SUM(*))




子查询:查询语句的嵌套
    可以用于任意select 语句里面,但子查询不能出现 order by。
    子查询总是从内向外处理。作为子查询的SELECT 语句只能查询单个列,企图检索多个列,将会错误。
    如:找出工资最低的人select min(last_name),min(salary) from s_emp;
       或者用子查询select last_name,salary from s_emp where salary=(select min(salary) from s_emp);


E-R图:属性: E(Entity) -R(Relationship)
        * (Mandatory marked 强制的)   强制的非空属性
        o (Optional marked 可选的)    可选属性(可以有值也可以没有)
        #* (Primary marked )         表示此属性唯一且非空 


约束:针对表中的字段进行定义的。
PK:primary key (主键约束,PK=UK+NN)保证实体的完整性,保证记录的唯一
    主键约束,唯一且非空,并且每一个表中只能有一个主键,有两个字段联合作为主键,
    只有两个字段放在一起唯一标识记录,叫做联合主键(Composite Primary Key)。
FK:foreign key (外建约束)保证引用的完整性,外键约束,外键的取值是受另外一张表中的主键或唯一值的约束,不能够取其他值,
    只能够引用主键会唯一键的值,被引用的表,叫做parent table(父表),引用方的表叫做child table(子表);
    child table(子表),要想创建子表,就要先创建父表,后创建子表,记录的插入也是如此,先父表后子表,
    删除记录,要先删除子表记录,后删除父表记录,
    要修改记录,如果要修改父表的记录要保证没有被子表引用。要删表时,要先删子表,后删除父表。
U:unique key(唯一键 UK),值为唯一,不能重复。
    在有唯一性约束的列,可以有多个空值,因为空值不相等。
NN:NOT NULL,不能为空。
    index(索引)是数据库特有的一类对象,实际应用中一定要考虑索引,view(示图)


    数量关系:  一对一关系
              多对一关系
              一对多关系
              多对多关系


范式:
 好处:降低数据冗余;减少完整性问题;标识实体,关系和表
    第一范式(First normal form:1Nf),每一个属性说一件事情。所有的属性都必须是单值,也就是属性只表示单一的意义。
                   (记录可以重复,会有大量冗余,没有任何限制)
    第二范式(2N范式),最少有一个属性要求唯一且非空PK,其他跟他有关联(记录不可重复,但是数据可能会出现冗余)。
    第三范式(3N范式),非主属性只能依赖于主属性,不能依赖于其他非主属性。(解决数据冗余问题,不能存在推理能得出的数据)
                   一般情况会做到第三范式。




创建表: Create Table 表名
        (字段名1 类型(数据长度)(default ...) 约束条件,
        字段名2  类型(数据长度) 约束条件 );
建表的名称:
    必须字母开头;最多30字符;只能使用“A~Z、a~z、0~9、_、$、#”;
    同一目录下不能有同名的表;表名不能跟关键字、特殊含意字符同样。
    如:create table number_1 (n1 number(2,4), n2 number(3,-1), n3 number);
       create table t_sd0808(id number(12) primary key,name varchar(30) not null);
    MySQL的: create table student (oid int primary key, ACTNO varchar(20) not null unique,
       BALANCE double); --MySQL的number类型分小类了,Oracle只有number,且MySQL的数值型不用定大小
    Oracle的: create table t_ad (oid number(15) primary key, 
       ACTNO varchar(20) not null unique,BALANCE number(20));


INSERT:插入(或添加)行到数据库表中的关键字。
    插入方式有以下几种:插入完整的行;插入行的一部分;插入某些查询的结果。
    对于INSERT操作,可能需要客户机/服务器的DBMS中的特定的安全权限。
     插入行(方式一)   INSERT INTO products VALUES(2008,’TV’,222.22,’US’);
    依赖于表中定义的顺序,不提倡使用。有空值时需要自己补上。
     插入行(方式二)   INSERT INTO products(id,name,price,vend_name) VALUES(2008,’TV’,222.22,’US’);
    依赖于逻辑顺序,会自动补上空值,提倡使用。


    插入检索出的数据:可以插入多条行到数据库表中
        INSERT INTO products(*,*,*,*)
        SELECT *,*,*,*
        FROM products_copy;
    如果这个表为空,则没有行被插入,不会产生错误,因为操作是合法的。
    可以使用WHERE加以行过滤。


复制表:   将一个表的内容复制到一个全新的表(在运行中创建,开始可以不存在)
    CREATE TABLE 新表名  AS
    SELECT *
    FROM 表名;


    INSERT INTO 与 CREATE TABLE AS SELECT 不同,前者是导入数据,而后者是导入表。
    任何SELECT选项和子句都可以使用,包括WHERE和GROUP BY。
    可利用联接从多个表插入数据。不管从多少个表中检索数据,数据都只能插入到单个表中。


更新数据 UPDATE 语句
            需要提供以下信息:要更新的表;列名和新值;确定要更新的哪些行的过滤条件。
    UPDATE 表名
    SET    vend_name = ‘HP’,
           prod_name = ‘NEWCOMPUTER’
    WHERE  vend_name = ‘IBM’;
    --UPDATE 语句中可以使用子查询,使得能用SELECT语句检索出的数据更新列数据。也可以将一个列值更新为 NULL。


删除数据 DELETE 语句
    DELETE
    FROM products
    WHERE prod_name = ‘COMPUTER’;
    全行删除,不要省略WHERE,注意安全。
    DELETE不需要列名或通配符。删除整行而不是删除列。DELETE是删除表的内容而不是删除表。
    如果想从表中删除所有内容,可以使用TRUNCATE TABLE语句(清空表格),它更快。


数字字典表:
Sequence:排列。存储物理地址


Index:索引。依附于表,为提高检索速度。


View:视图。看到表的一部分数据。
    限制数据访问。简化查询。数据独立性。本质上是一个sql查询语句。
    Create[or Relace][Force|noForce]  View  视图名
         [(alias[,alias]…)]    别名列表
        As subquery
    [With Check Option [Constraint ……]]
    [With Read Only]
    注意:有些DBMS不允许分组或排序视图,不能有 Order by 语句。可以有 Select 语句。
    删除视图:    DROP VIEW 视图名


Rownum:纬列。内存里排序的前N个。
    在where语句中,可以用=1,和<=N 或 <N;但不能用=N 或 >N。
    因为这是内存读取,没有1就丢弃再新建1。只能从1开始。需要从中间开始时,需二重子rownum语句需取别名。
经典应用: Top-n Analysis  (求前N名或最后N名)
          Select [查询列表], Rownum
          From (Select  [查询列表(要对应)]
                   From 表
                   Order by  Top-N_字段)
          Where Rownum <= N


分页显示:
    --取工资第5~10名的员工(二重子rownum语句,取别名)
    select  rn,id,last_name,salary 
    From ( select  id,last_name,salary,Rownum rn
           From (Select id,last_name,salary 
                     from s_emp
                     order by salary desc)
    where rownum <= 10)
    where rn between 5 and 10;




Union:合并表
    Select …   Union   Select…    把两个Select语句的表合并。
    要求两表的字段数目和类型按顺序对应。合并后的表,自动过滤重复的行。
Intersect:交。    同上例,把两个Select表相交。
Minus:减。        把相交的内容减去。
not exists        除运算。




添加字段(列):
    Alter Table 表名
    Add (column dataype [Default expr][Not Null]
         [,column datatype]…);
    添加有非空限制的字段时,要加Default语句
    字段名字不可以直接改名,需要添加新字段,再复制旧字段后删除旧字段。
      添加约束:    Alter Table 表名
                  Add [CONSTRAINT constraint] type (column);
    添加非空约束时,要用Modify语句。
    查看约束名时,可以违反约束再看出错提示;或者查看约束字典desc user_constraints


减少字段:
    Alter Table 表名
    Drop (column [,column]…);
      删除约束:    Alter Table 表名
                  Drop CONSTRAINT  column;
      或:   Alter Table 表名
            Drop  Primary Key  Cascade;


暂时关闭约束,并非删除:
    Alter Table 表名
    Disable CONSTRAINT  column  Cascade;
打开刚才关闭的约束:
    Alter Table 表名
    Enable  CONSTRAINTcolumn;


修改字段:
    Alter Table 表名
    Modify  (column dataype [Default expr][Not Null]
             [,column datatype]…);
修改字段的类型、大小、约束、非空限制、空值转换。


删除表:
会删除表的所有数据,所有索引也会删除,约束条件也删除,不可以roll back恢复。
    Drop Table 表名 [Cascade Constraints];
    加 [Cascade Constraints] 把子表的约束条件也删除;但只加 [Cascade]会把子表也删除。


改表名:
Rename 原表名 To 新表名;


清空表格:
    TRUNCATE TABLE 表名;
    相比Delete,Truncate Table清空很快,但不可恢复。清空后释放内存。
    Delete 删除后可以roll back。清空后不释放内存。


 


Logo

更多推荐