1.System
System类位于package java.lang下面,凡是此package下面的类我们可以直接引用无需先import进来,因为JVM缺省就load了这下面的所有class。
System包含了一些我们常用的方法与成员变量。 System不能被实例化, 所有的方法都可以直接引用。 主要作用大致有:
输入输出流: (PrintStream) System.out(标准终端输出流),
(PrintStream) System.err(标准错误输出流),
(InputStream) System.in(标准输入流)。
取当前时间: System.currentTimeMillis() 所取到的时间是从1970/01/01以来1/1000秒计算的long型值。这个值可以转换至Date或Timestamp值。它一般还可以用来计算程序执行的时间。
数组拷贝: System.arraycopy(Object src, int src_position, Object dst, int dst_position, int length)
src: 源数组。
src_position: 源数组拷贝的起始位置。
dst: 目标数组
dst_position: 拷贝至目标数组的起始位置
length: 拷贝元素的长度
例:
int[] array1 = {1, 2, 3, 4, 5};
int[] array2 = {4, 5, 6, 7, 8};
int array3 = new int[8];
System.arraycopy(array1, 0, array3, 0, 5);
System.arraycopy(array2, 2, array3, 5, 3);
此时array3 = {1, 2, 3, 4, 5, 6, 7, 8}
此时array3 = {1, 2, 3, 4, 5, 6, 7, 8}。这比用for循环来进行赋值效率要高。
存取系统的Properties: System.getProperties():取得当前所有Properties。
System.setProperties(Properties props):设置系统的Properties。
System.getProperty(String key): 根据一个键值来取得一个Property。
System.setProperty(String key, String value): 设置系统的一个Property。
其它
System. loadLibrary(String libname): 加载native的动态库。 可以用C写JNI的库, 然后在java中通过native方法来调用。
System.setSecurityManager(SecurityManager s)
System.getSecurityManager(): 设置与取得系统的security class。
2.Class, ClassLoader
Java是一种介于解释与编绎之间的语言, Java代码首先编绎成字节码, 在运行的时候再翻译成机器码。 这样在运行的时候我们就可以通过Java提供的反射方法(reflect)来得到一个Object的Class的额外信息, 灵活性很大,可以简化很多操作。
Class:任何一个Object都能通过getClass()这个方法得到它在运行期间的Class。得到这个Class之后可做的事情就多了,比如动态得到它的构造函数,成员变量,方法等等。还可以再生成一份新的实例,下面只给出几个常用的方法。
Class Class.forName(String className) throws ClassNotFoundException: 这是个静态方法, 通过一个Class的全称来得到这个Class。
String getName() 取得这个Class的全称, 包括package名。
Object newInstance() 得到一个实例,调用缺省的构造函数。
例如,我们有一个类: com.some.util.MyClass 如果得到它的一个实例呢? 可能有以下两种方法:
MyClass myClass = new MyClass(), 直接通过操作符new生成;
或者:
MyClass myClass = (MyClass) Class.forName(“com.some.util.MyClass”).newInstance();
ClassLoader: ClassLoader是一个抽象类,一般的系统有一个缺省的ClassLoader用来装载Class, 用ClassLoader.getSystemClassLoader()可以得到。不过有时候为了安全或有其它的特殊需要我们可以自定义自己的ClassLoader来进行loader一些我们需要的Class, 比如有的产品它用了自己的ClassLoader可以指定Class只从它指定的特定的JAR文件里面来loader,如果你想通过覆盖ClassPath方法来想让它用你的Class是行不通的。
3. Java IO系统
InputStream的类型:
1. 字节数组
2. String对象
3. 文件
4. 管道, 可以从另外一个输出流得到一个输入流
5. 一系列的其他流, 可以将这些流统一收集到单独的一个流内。
6. 其他起源(如socket流等)
还有一个是File类, Java中一个目录也是一个文件,可以用file.isFile()和file.isDirectory()来进行判断是文件还是目录。File对象可能作为参数转换为文件流进行操作。
常用方法:
/**
* 从控制台读取输入的数据, System.in (InputStream) 先转换至InputStreamReader再用
* BufferedReader进行读取.
*
*/
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = "";
while (str != null) {
str = in.readLine();
// process(str);
}
/**
* 从文件中按行进行读取数据处理
*/
BufferedReader in = new BufferedReader(new FileReader("infilename"));
String str;
while ((str = in.readLine()) != null) {
// process(str);
}
in.close();
/**
* 写数据至一个文件中去.
*/
BufferedWriter out = new BufferedWriter(new FileWriter("filename", true/false));
out.write("aString");
out.close();
/**
* 以指定的编码方式从文件中读取数据
*/
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("filename"), "UTF8"));
String str = in.readLine();
/**
* 以指定的编码方式写数据到文件中
*/
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("filename"), "UTF8"));
out.write("a String");
out.close();
可以看出, 流之间可以互相转换, 如果想对流进行字符操作最好将之转换成BufferedReader与BufferedWriter这样可以提高读写的效率。需要注意的是一般来说流的大小是得不到的, 虽然一般的InputStream都有一个available()的方法可以返回这个流可以读到的字节数, 不过这个方法有时候不会很准确, 比如在读取网络传输的流的时候, 取到的长度并不一定是真实有效的长度。
4.ResourceBundle, Properties
ResourceBundle:开发一个项目, 配置文件是少不了的, 一些需要根据环境进行修改的参数, 都有得放到配置文件中去, 在Java中一般是通过一个properties文件来实现的, 这个文件以properties结尾。 内部结构是二维的, 以key=value的形式存在。 如下:
options.column.name.case=1
options.column.bean.serializable=1
ResourceBundle用来解析这样的文件, 它的功能是可以根据你的Locale来进行解析配置文件,如果一个产品需要进行多语言支持,比如在不同语种的系统上,会显示根据它的语言显示相应的界面语言, 就可以定义多份的properties文件,每个文件的key是一样的,只是value不一样,然后在application起动的时候,可以判别本机的Locale来解析相应的properties文件。Properties文件里面的数据得要是Unicode。 在jdk下面可以用native2ascii这个命令进行转换。 例: native2ascii Message.txt Message.properties 会生成一个Unicode的文件。
Properties: Properties这个类其实就是从Hashtable继承下来的,也就是说它是一个散列表,区别在于它的key与value都是String型的,另外也加了几个常用的方法:
String getProperty(String key) 取得一个property
String getProperty(String key, String defaultValue)
取property,如果不存在则返回defaultValue。
void list(PrintStream out) 向out输出所有的properties
void list(PrintWriter out)
Enumeration propertyNames() 将所有的property key名以Enumeration形式返回
Object setProperty(String key, String value) 设置一个property
ResourceBundle与Properties一般结合起来使用。 它们的用法很简单, 由ResourceBundle解析出来的key与value然后放至到一个静态的Properties成员变量里面去, 然后就可以通过访问Properties的方法进行读取Property。 下面给个简单的例子:
public class PropertyManager implements Serializable {
/** 定义一个静态的Properties变量 */
private static Properties properties = new Properties();
/**
* 通过一个类似于类名的参数进行Property文件的初期化
* 比如现在有一个文件叫Message.properties, 它存放在
* ejb/util下面并且, 这个目录在运行的classpath下面
* 则in就为ejb.util.Message
*
*/
public static void init(String in) throws MissingResourceException {
ResourceBundle bundle = ResourceBundle.getBundle(in);
Enumeration enum = bundle.getKeys();
Object key = null;
Object value = null;
while (enum.hasMoreElements()) {
key = enum.nextElement();
value = bundle.getString(key.toString());
properties.put(key, value);
}
}
/**
* 取得一个Property值
*/
public static String getProperty(String key) {
return properties.get(key);
}
/**
* 设置一个Property值
*/
public static void setProperty(String key, String value) {
properties.put(key, value);
}
}
不过现在越来越倾向于用XML替换Properties文件来进行配置。 XML配置具有层次结构清楚的优点。
5. JDBC类库
有了 JDBC,向各种关系数据库发送 SQL 语句就是一件很容易的事。将 Java 和 JDBC 结合起来将使得只须写一遍程序就可让它在任何平台上运行。
下面为常用的处理流程:
DriverManager
Connection
Statement
PreparedStatement
CallableStatement
ResultSet
简单地说,JDBC 可做三件事:
1.与数据库建立连接
2.发送 SQL 语句
3.处理结果
DriverManager:
DriverManager类是JDBC的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接。另外,DriverManager类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。对于简单的应用程序,一般程序员需要在此类中直接使用的唯一方法是DriverManager.getConnection。正如名称所示,该方法将建立与数据库的连接。
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
Connection conn = DriverManager.getConnection (
"jdbc:oracle:thin:@eai-sol:1521:eai_db", "csc2", "csc2");
其中第一句话的作用是在当前的环境中load一个DB Driver,DriverManager可以从load的classes里面找到注册过的driver,然后使用它所找到的第一个可以成功连接到给定URL的驱动程序。第二句话的三个参数分别是URL,User,Password。Driver不一样,URL可能也不一样。
Statement:
Statement对象用于将SQL语句发送到数据库中。实际上有三种Statement对象,它们都为在给定连接上执行SQL语句的包容器:Statement、PreparedStatement(它从Statement承而来)和CallableStatement(它从PreparedStatement 继承而来)。
它们都专用于发送定类型的SQL语句:
Statement 对象用于执行不带参数的简单SQL语句;
PreparedStatement 对象用于执行带或不带IN参数的预编译SQL语句;CallableStatement 对象用于执行对数据库已存储过程的调用。
Statement接口提供了执行语句和获取结果的基本方法。PreparedStatement接口添加了处理IN参数的方法;而CallableStatement添加了处理OUT参数的方法。
创建Statement对象:
Statement对象用Connection的方法createStatement创建,如下列代码段中所示:Statement stmt = conn.createStatement();
为了执行Statement对象,被发送到数据库的SQL语句将被作为参数提供给Statement的方法:
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM table1");
Statement接口提供了三种执行SQL语句的方法:executeQuery、executeUpdate和execute。使用哪一个方法由SQL语句所产生的内容决定。
方法executeQuery用于产生单个结果集的语句,例如SELECT语句。
方法executeUpdate用于执行INSERT、UPDATE或DELETE语句以及SQL DDL(数据定义语言)语句。INSERT、UPDATE或DELETE语句的效果是修改表中零行或多行中的一列或多列。executeUpdate的返回值是一个整数,指示受影响的行数(即更新计数)。对于CREATE TABLE或DROP TABLE等不操作行的语句,executeUpdate的返回值总为零。
方法execute用于执行返回多个结果集、多个更新计数或二者组合的语句。
语句完成:
当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的executeQuery方法,在检索完ResultSet对象的所有行时该语句完成。对于方法executeUpdate,当它执行时语句即完成。但在少数调用方法execute的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。
关闭 Statement 对象:
Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要 Statement 对象时显式地关闭它们。这将立即释放 DBMS 资源,有助于避免潜在的内存问题。 关闭Statement用 stmt.close()方法。
ResultSet:
ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。ResultSet.next方法用于移动到ResultSet中的下一行,使下一行成为当前行。结果集一般是一个表,其中有查询所返回的列标题及相应的值。
下面的代码段是执行SQL语句的示例。该SQL语句将返回行集合,其中列1为int,列2为String,而列3则为日期型:
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int i = rs.getInt("a");
String s = rs.getString("b");
Timestamp t = rs.getTimestamp("c");
}
行和光标:ResultSet维护指向其当前数据行的光标。每调用一次next方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用next将把光标置于第一行上,使它成为当前行。随着每次调用next导致光标向下移动一行,按照从上至下的次序获取ResultSet行。在ResultSet对象或其父辈Statement对象关闭之前,光标一直保持有效。
列:
方法getXXX提供了获取当前行中某列值的途径。在每一行内,可按任何次序获取列值。但为了保证可移植性,应该从左至右获取列值,并且一次性地读取列值。列名或列号可用于标识要从中获取数据的列。
用作getXXX方法的输入的列名不区分大小写。为了代码的可维护性与可读性,应该禁止用index的方法来取值,要用读列名的方法。
NULL 结果值:
要确定给定结果值是否是JDBC NULL,必须先读取该列,然后使用ResultSet.wasNull方法检查该次读取是否返回JDBC NULL。
PreparedStatement:
PreparedStatement接口继承Statement,并与之在两方面有所不同:
PreparedStatement实例包含已编译的SQL语句。这就是使语句“准备好”。包含于PreparedStatement对象中的SQL语句可具有一个或多个IN参数。IN参数的值在SQL语句创建时未被指定。相反的,该语句为每个IN参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX方法来提供。
由于PreparedStatement对象已预编译过,所以其执行速度要快于Statement对象。因此,多次执行的SQL语句经常创建为PreparedStatement对象,以提高效率。
作为Statement的子类,PreparedStatement继承了Statement的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代IN参数占位符的值。同时,三种方法execute、executeQuery和executeUpdate已被更改以使之不再需要参数。
创建PreparedStatement对象:
以下的代码段(其中conn 是Connection对象)创建包含带两个IN参数占位符的SQL语句的PreparedStatement对象:
PreparedStatement pstmt=conn.prepareStatement("UPDATE table1 SET a = ? WHERE b = ?");
pstmt对象包含语句 " UPDATE table1 SET a = ? WHERE b = ?",它已发送给DBMS,并为执行作好了准备。
传递IN参数:
在执行PreparedStatement对象之前,必须设置每个?参数的值。这可通过调用setXXX方法来完成,其中XXX是与该参数相应的类型。setXXX方法的第一个参数是要设置的参数的序数位置,第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为123456789,第二个参数设为100000000:
pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);
CallableStatement:
CallableStatement对象为所有的DBMS提供了一种以标准形式调用已储存过程(也就是SP)的方法。已储存过程储存在数据库中。对已储存过程的调用是CallableStatement对象所含的内容。有两种形式:一种形式带结果参数,另一种形式不带结果参数。结果参数是一种输出(OUT)参数,是已储存过程的返回值。两种形式都可带有数量可变的输入(IN参数)、输出(OUT参数)或输入和输出(INOUT参数)的参数。问号将用作参数的占位符。
在JDBC中调用已储存过程的语法如下所示。注意,方括号表示其间的内容是可选项;方括号本身并不是语法的组成部份。
{call 过程名[(?, ?, ...)]}
返回结果参数的过程的语法为:
{? = call 过程名[(?, ?, ...)]}
不带参数的已储存过程的语法类似:
{call 过程名}
CallableStatement继承Statement的方法(它们用于处理一般的SQL语句),还继承了PreparedStatement的方法(它们用于处理IN参数)。CallableStatement中定义的所有方法都用于处理OUT参数或INOUT参数的输出部分:注册OUT参数的JDBC类型(一般SQL类型)、从这些参数中检索结果,或者检查所返回的值是否为JDBC NULL。
创建CallableStatement对象:
CallableStatement对象是用Connection方法prepareCall创建的。下例创建CallableStatement的实例,其中含有对已储存过程Csc_ GetCustomId调用。该过程有两个变量,但不含结果参数:
CallableStatement cstmt = con.prepareCall("{call CSC_GetCustomId (?, ?, ?)}");
其中?占位符为IN OUT还是INOUT参数,取决于已储存过程 Csc_ GetCustomId。
IN 和 OUT 参数:
将IN参数传给CallableStatement对象是通过setXXX方法完成的。该方法继承自PreparedStatement。所传入参数的类型决定了所用的setXXX方法(例如,用setFloat来传入float值等)。
如果已储存过程返回OUT参数,则在执行CallableStatement对象以前必须先注册每个OUT参数的JDBC类型(这是必需的,因为某些DBMS要求JDBC类型)。注册JDBC类型是用registerOutParameter方法来完成的。语句执行完后,CallableStatement的getXXX 方法将取回参数值。正确的getXXX方法是为各参数所注册的JDBC类型所对应的Java类型也就是说,registerOutParameter使用的是JDBC类型(因此它与数据库返回的JDBC类型匹配),而getXXX将之转换为Java类型。下面给出CSC中的一个例子:
String sqlSp = "{call CSC_GetCustomId(?, ?, ?)}";
cstmt = conn.prepareCall(sqlSp.toString());
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.registerOutParameter(2, Types.NUMERIC);
cstmt.registerOutParameter(3, Types.VARCHAR);
cstmt.execute();
long customerID = cstmt.getLong(1);
long lRet = cstmt.getLong(2);
String sErr = cstmt.getString(3);
INOUT参数:
既支持输入又接受输出的参数(INOUT参数)除了调用registerOutParameter方法外,还要求调用适当的setXXX方法(该方法是从PreparedStatement继承来的)。setXXX方法将参数值设置为输入参数,而registerOutParameter方法将它的JDBC类型注册为输出参数。setXXX方法提供一个Java值,而驱动程序先把这个值转换为JDBC值,然后将它送到数据库中。这种IN值的JDBC类型和提供给registerOutParameter方法的JDBC类型应该相同。然后,要检索输出值,就要用对应的getXXX方法。例如,Java类型为byte的参数应该使用方法setByte来赋输入值。应该给registerOutParameter提供类型为TINYINT的JDBC类型,同时应使用getByte来检索输出值。下例假设有一个已储存过程reviseTotal,其唯一参数是INOUT参数。方法setByte把此参数设为25,驱动程序将把它作为JDBC TINYINT类型送到数据库中。接着,registerOutParameter将该参数注册为JDBC TINYINT。执行完该已储存过程后,将返回一个新的JDBC TINYINT值。方法getByte将把这个新值作为Java byte类型检索。
CallableStatement cstmt = con.prepareCall("{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);