Java线程的6种状态
NEW
至今尚未启动的线程处于这种状态。
RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器。
BLOCKED
受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。某一线程因为调用下列方法之一而处于等待状态:
1、不带超时值的 Object.wait
2、不带超时值的 Thread.join
3、LockSupport.park
处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
TIMED_WAITING
具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
1、Thread.sleep
2、带有超时值的 Object.wait
3、带有超时值的 Thread.join
4、LockSupport.parkNanos
5、LockSupport.parkUntil
TERMINATED
已终止线程的线程状态。线程已经结束执行。
Java无法编写?通过JNI本地编写来解决
在您的程序中使用JNI并不是一件容易的事情,然而,JNI的性能和使用原有代码的能力将会为您的Java程序添加更多的功能并且能胜任更多的挑战。
Java本地接口(JNI——Java Native Interface)允许运行在Java虚拟机(JVM——Java Virtual Machine)上的代码调用本地程序和类库,或者被它们调用,这些程序和类库可以是其它语言编写的,比如C、C++或者汇编语言。当一个程序无法完全使用Java编写时,开发者可以通过JNI来编写本地方法,比如标准Java类库并不支持的依赖于平台的特色或者程序库。JNI还可以用于修改现有的使用其它语言编写的程序,使它们可以通过Java编写的程序来访问。很多基本类库都依赖JNI来为开发者和用户提供服务,比如文件的输入/输出和音频功能。在基本类库中包含的对于性能和平台敏感的API可以允许所有的Java程序以安全和平台无关的方式来使用这些功能,在采用JNI之前,开发者需要明确这些功能并不是已经包含在Java标准类库中的,在这篇文章中,我将会讲解JNI是如何工作的以及本地类型是如何映射到Java的类型和类库的。
JNI工作原理
在JNI中,本地函数是通过一个独立的.c或.cpp文件来实现的(C++为JNI提供的界面会更简洁一些)。当JVM调用该函数时,它传递了一个JNIEnv指针、一个jobject指针和通过Java方法定义的Java参数,JNI函数的形式如下:
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobjectobj) { //Method native implemenation } |
env指针是一个包含了JVM接口的结构,它包含了与JVM进行交互以及与Java对象协同工作所必需的函数,示例中的JNI函数可以在本地数组和Java数组类型之间、本地字符串和Java字符串类型之间进行转换,其功能还包括对象的实例化、抛出异常等。基本上您可以使用JNIEnv来实现所有Java能做到的事情,虽然要简单很多。更加正式的解释是这样的,本地代码通过调用JNI的函数来访问JVM,这是通过一个界面指针实现的(界面指针实际上是指向指针的指针),该指针指向一个指针数组,数组中的每个指针都指向了一个界面函数,而每个界面函数都是在数组中预先定义过的。本地方法将JNI界面指针当作一个参数,如果在同一个Java线程中,出现对该本地方法的多重调用,JVM则保证传递相同的界面指针到本地方法。不过,一个本地方法可以被不同的Java线程调用,因而也可能会收到不同的JNI界面指针。
本地方法是通过System.loadLibrary方法加载的,在以下的例子中,类的初始化方法加载了一个指定平台的本地类库,该类库定义了本地方法:
packagepkg;
class Cls
{
native double f(inti, String s);
static
{
System.loadLibrary(pkg_Cls");
}
}
System.loadLibrary方法的参数是一个类库的名称,它可以由程序员任意选取,系统则遵循一个标准的本地化平台的方式来转换类库的名称到一个本地类库的名称。例如,在Solaris操作系统中会将pkg_Cls转换为libpkg_Cls.so,而Win32系统则会将同样的pkg_Cls转换为pkg_Cls.dll。
动态指针会根据它们的名字来进行解析,一个本地方法的名称是按照组件进行连接的,它包含了:前缀“Java_”、一个分离的合法的类名称和一个分离的方法名称。注意:微软的JVM有相同的机制从Java调用本地Windows代码,该机制被称为原始本地接口(Raw Native Interface (RNI))。基本类型,比如整型、字符等等,是在Java和本地代码间进行拷贝的,而其他的自定义Java对象则是通过引用来传递的。这个表格展示了Java和本地代码之间的类型映射,这些类型是可以互换的,您可以在您使用int类型的位置使用jint类型,当然反过来也一样,而且不需要任何类型转化。但是,Java的字符串和数组类型和本地的字符串与数组类型之间的转换就比较困难了,如果您使用的jstring类型中出现了字符“*”,您的代码会造成JVM的崩溃,以下的例子说明了您应当如何正确使用字符串:
JNIEXPORT void JNICALL
Java_ClassName_MethodName
(JNIEnv *env, jobjectobj, jstringjavaString)
{
//Get the native string from
Java string const char
*nativeString =
env->GetStringUTFChars(env,javaString, 0);
printf("%s", nativeString);
env->ReleaseStringUTFChars(env,javaString, nativeString);
}
您需要使用界面指针env来操作Java对象。
Eclipse 3.5提前发布 新增两千四百万行代码
Eclipse 3.5已经于6月24日发布,比Eclipse官网公布的预计发布日期提前了整整两天。
本次Eclipse伽利略是有史以来Eclipse最大的更新,包括33个项目和2千4百多万行代码。来自44个不同组织的超过380个贡献者都参与了这次发布。
Eclipse的本身只是一个框架平台,但是众多插件的支持使得Eclipse拥有其他功能相对固定的IDE软件很难具有的灵活性。许多软件开发商以Eclipse为框架开发自己的IDE。
Eclipse 3.5新特性概述
◆Solaris x86已经加入支持行列
◆Eclipse富客户端平台现在包含了一个OSGi声明式服务(Declarative Services——DS)的实现
◆增强目标编辑器,以支持目标定义中的新特性
◆有一个新的目标平台偏好页面
本次更新包括了很多企业方面的升级。更新主要有三个主要趋势:
1. 企业使用方面的扩展
2. Eclipse模型技术的创新
3. Eclipse runtime技术的增强
同时官方网站的Blog区域,在6月24日也同时发布多个关于Eclipse 3.5的文章。
Java的文件名必须和公共类名相同的原因
首先Java是被解释执行的。它在运行时并不是将所有的class文件全都放到内存中。而是在遇到import的时候才去相应的文件目录找相应的class文件。
对于一个public类,它是可以被项目中任何一个类所引用的,只需在使用它前import一下它所对应的class文件即可。将类名与文件名一一对应就可以方便虚拟机
在相应的路径(包名)中找到相应的类的信息。如果不这么做的话,就很难去找,而且开销也会很大。
一下是我写的一个比较“恶心”的程序来说明这个问题,大家在使用前先要将自己JDK的bin目录设置到环境变量path中去:
import java.io.FileOutputStream; import java.io.IOException; import java.io.FileNotFoundException; public class Test { public static void main(String[] args) { Object b = null; try { b = Class.forName("Hello").newInstance(); } catch(InstantiationException e) { } catch(IllegalAccessException e) { } catch(ClassNotFoundException e) { try { FileOutputStream file = new FileOutputStream("Hello.java"); String s = "public class Hello { public Hello() { Syst" + "em.out.println("Hello, world!"); } } "; file.write(s.getBytes()); Process p = Runtime.getRuntime().exec("javac Hello.java"); System.out.println("The return value is: " + p.waitFor()); b = Class.forName("Hello").newInstance(); } catch(FileNotFoundException ee) { } catch(IOException ee) { } catch(InterruptedException ee) { } catch(InstantiationException ee) { } catch(IllegalAccessException ee) { } catch(ClassNotFoundException ee) { } } } } |
Java线程:并发协作-死锁
线程发生死锁可能性很小,即使看似可能发生死锁的代码,在运行时发生死锁的可能性也是小之又小。
发生死锁的原因一般是两个对象的锁相互等待造成的。
在《Java线程:线程的同步与锁》一文中,简述死锁的概念与简单例子,但是所给的例子是不完整的,这里给出一个完整的例子。
/** * Java线程:并发协作-死锁 * * @author Administrator 2009-11-4 22:06:13 */ public class Test { public static void main(String[] args) { DeadlockRisk dead = new DeadlockRisk(); MyThread t1 = new MyThread(dead, 1, 2); MyThread t2 = new MyThread(dead, 3, 4); MyThread t3 = new MyThread(dead, 5, 6); MyThread t4 = new MyThread(dead, 7, 8); t1.start(); t2.start(); t3.start(); t4.start(); } } class MyThread extends Thread { private DeadlockRisk dead; private int a, b; MyThread(DeadlockRisk dead, int a, int b) { this.dead = dead; this.a = a; this.b = b; } @Override public void run() { dead.read(); dead.write(a, b); } } class DeadlockRisk { private static class Resource { public int value; } private Resource resourceA = new Resource(); private Resource resourceB = new Resource(); public int read() { synchronized (resourceA) { System.out.println("read():" + Thread.currentThread().getName() + "获取了resourceA的锁!"); synchronized (resourceB) { System.out.println("read():" + Thread.currentThread().getName() + "获取了resourceB的锁!"); return resourceB.value + resourceA.value; } } } public void write(int a, int b) { synchronized (resourceB) { System.out.println("write():" + Thread.currentThread().getName() + "获取了resourceA的锁!"); synchronized (resourceA) { System.out.println("write():" + Thread.currentThread().getName() + "获取了resourceB的锁!"); resourceA.value = a; resourceB.value = b; } } } } |
下面死锁的情况发生了,真是难得一见啊:
Java对MY SQL的连接仿方法介绍
对于数据库的访问,现在对数据库进行设置。
ف.首先打开TCP服务,在打开一个端口。
2.新建一个用户,对用户进行映射。(有时候会碰到一个错误,可能是服务器身份验证为windows身份验证模式,将其改为SQL验证模式)
ك.下载JDBC数据库驱动程序。解压将jdbc.jar放到根目录下。
ل.在Eclipse中导入JDBC驱动。
م.在Eclpse中建立一个JAVA Project。
import java.sql.*; public class Test { public static void main(String[] srg) { String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; //加载JDBC驱动 String dbURL = "jdbc:sqlserver://localhost:1433; DatabaseName=sample"; //连接服务器和数据库sample String userName = "sa"; //默认用户名 String userPwd = "123456"; //密码 Connection dbConn; try { Class.forName(driverName); dbConn = DriverManager.getConnection(dbURL, userName, userPwd); System.out.println("Connection Successful!"); //如果连接成功 控制台输出Connection Successful! } catch (Exception e) { e.printStackTrace(); } } } |
Java入门:面向对象的思维方法
我是从学习Java编程开始接触OOP(面向对象编程),刚开始使用Java编写程序的时候感觉很别扭,因为我早以习惯用C来编写程序,很欣赏C的简洁性和高效性,喜欢C简练而表达能力丰富的风格,特别忍受不了Java运行起来慢吞吞的速度,相对冗长的代码,而且一个很简单的事情,要写好多类,一个类调用一个类,心里的抵触情绪很强。
我对Java的面向对象的特性琢磨良久,自认为有所领悟,也开始有意识的运用OOP风格来写程序,然而还是经常会觉得不知道应该怎样提炼类,面对一个具体的问题的时候,会觉得脑子里千头万绪的,不知道怎么下手,一不小心,又会回到原来的思路上去。
举个例子,要发广告邮件,广告邮件列表存在数据库里面。倘若用C来写的话,一般会这样思考,先把邮件内容读入,然后连接数据库,循环取邮件地址,调用本机的qmail的sendmail命令发送。
然后考虑用Java来实现,既然是OOP,就不能什么代码都塞到main过程里面,于是就设计了三个类:
一个类是负责读取数据库,取邮件地址,调用qmail的sendmail命令发送;
一个类是读邮件内容,MIME编码成HTML格式的,再加上邮件头;
一个主类负责从命令读参数,处理命令行参数,调用发email的类。
把一件工作按照功能划分为3个模块分别处理,每个类完成一件模块任务。
仔细的分析一下,就会发现这样的设计完全是从程序员实现程序功能的角度来设计的,或者说,设计类的时候,是自低向上的,从机器的角度到现实世界的角度来分析问题的。因此在设计的时候,就已经把程序编程实现的细节都考虑进去了,企图从底层实现程序这样的出发点来达到满足现实世界的软件需求的目标。
这样的分析方法其实是不适用于Java这样面向对象的编程语言,因为,如果改用C语言,封装两个C函数,都会比Java实现起来轻松的多,逻辑上也清楚的多。
我觉得面向对象的精髓在于考虑问题的思路是从现实世界的人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。
举一个非常简单的例子:假使现在需要写一个网页计数器,客户访问一次页面,网页计数器加1,计数器是这样来访问的http://hostname/count.cgi?id=xxx
后台有一个数据库表,保存每个id(一个id对应一个被统计访问次数的页面)的计数器当前值,请求页面一次,对应id的计数器的字段加1(这里我们忽略并发更新数据库表,出现的表锁定的问题)。
如果按照一般从程序实现的角度来分析,我们会这样考虑:首先是从HTTP GET请求取到id,然后按照id查数据库表,获得某id对应的访问计数值,然后加1,更新数据库,最后向页面显示访问计数。
现在假设一个没有程序设计经验的人,他会怎样来思考这个问题的呢?他会提出什么样的需求呢?他很可能会这样想:
我需要有一个计数器,这个计数器应该有这样的功能,刷新一次页面,访问量就会加1,另外最好还有一个计数器清0的功能,当然计数器如果有一个可以设为任意值的功能的话,我就可以作弊了。
做为一个没有程序设计经验的人来说,他完全不会想到对数据库应该如何操作,对于HTTP变量该如何传递,他考虑问题的角度就是我有什么需求,我的业务逻辑是什么,软件应该有什么功能。
按照这样的思路(请注意,他的思路其实就是我们平时在生活中习惯的思维方式),我们知道需要有一个计数器类 Counter,有一个必须的和两个可选的方法:
getCount() // 取计数器值方法 resetCounter() // 计数器清0方法 setCount() // 设计数器为相应的值方法 把Counter类完整的定义如下: public class Counter { public int getCount(int id) { } public void resetCounter(int id) { } public void setCount (int id, int currentCount) { } } |
解决问题的框架已经有了,来看一下如何使用Counter。在count.cgi里面调用Counter来计数,程序片断如下:
// 这里从HTTP环境里面取id值
...
Counter myCounter = new Counter();
// 获得计数器
int currentCount = myCounter.getCount(id);
// 从计数器中取计数
// 这里向客户浏览器输出
...
程序的框架全都写好了,剩下的就是实现Counter类方法里面具体的代码了,此时才去考虑具体的程序语言实现的细节,比如,在getCount()方法里面访问数据库,更新计数值。 从上面的例子中看到,面向对象的思维方法其实就是我们在现实生活中习惯的思维方式,是从人类考虑问题的角度出发,把人类解决问题的思维方式逐步翻译成程序能够理解的思维方式的过程,在这个翻译的过程中,软件也就逐步被设计好了。 在运用面向对象的思维方法进行软件设计的过程中,最容易犯的错误就是开始分析的时候,就想到了程序代码实现的细节,因此封装的类完全是基于程序实现逻辑,而不是基于解决问题的业务逻辑。 学习JDBC编程的经典错误问法是:“我怎样封装对数据库的select操作?” 面向对象的设计是基于解决业务问题的设计,而不是基于具体编程技术的设计。我不会去封装select语句的,我只封装解决问题的业务逻辑,对数据库的读取是在业务逻辑的编码实现阶段才去考虑的问题。 回过头看上面那个发广告邮件的例子,应该如何应用面向对象的思维方法呢? 对于一个邮件来说,有邮件头,邮件体,和邮件地址这三个属性,发送邮件,需要一个发送的方法,另外还需要一个能把所有邮件地址列出来的方法。所以应该如下设计: 类JunkMail 属性:
- head
- body
- address
方法:
public class JunkMail { private String head; private String body; private String address; public JunkMain() { // 默认的类构造器 // 从外部配置文件读邮件头和邮件体 this.head=...; this.body=...; } public static boolean sendMail(String address) { // 调用qmail,发送email } public static Collection listAllMail() { // 访问数据库,返回一个邮件地址集合 } } |
当把JunkMail设计好了以后,再调用JunkMail类完成邮件的发送,将是非常轻松的事情。 如果说传统的面向过程的编程是符合机器运行指令的流程的话,那么面向对象的思维方法就是符合现实生活中人类解决问题的思维过程。 在面向对象的软件分析和设计的时候,要提醒自己,不要一上来就去想程序代码的实现,应该抛开具体编程语言的束缚,集中精力分析我们要实现的软件的业务逻辑,分析软件的业务流程,思考应该如何去描述和实现软件的业务。毕竟软件只是一个载体,业务才是我们真正要实现的目标。 但是在设计过程中,心里却往往在担心,如果我完全不去考虑程序代码的实现的话,那么我怎么知道我的设计一定合理呢?我怎么知道我设计的类、接口一定可以实现呢?所以经常可以看到的现象就是: 在设计过程中,虽然知道不能过早考虑代码实现,但是每设计一个类,一个接口,心里都要不知不觉的用自己熟悉的编程语言大概的评估一下,看看能否编出来,因此,一不小心,就会又回到按照程序功能实现的思路进行设计的老路上去了。 举个例子来说明,在做Web程序设计的时候,经常要遇到分页显示数据的情况。比如说需要把系统中所有的用户都列出来这样的功能。假设使用User类来表示用户,增加用户addUser(),删除用户deleteUser(),查询所有用户listUsers()方法。而数据库中有一个user表,一条记录是一个用户的信息。下面考虑一下User类的方法的实现: addUser()和deleteUser()方法都好实现,就是对数据库增加记录和删除记录。对于listUsers()方法,其实就是对user表的select,取出一个记录集。但是该怎么从listUsers()方法中得到所有用户的列表呢? 一个方法调用的返回值只有一个,没有多个,所以很多情况下采用的办法就是返回值定义为集合类型,比如Vector。这样就可以在listUsers()方法的具体代码实现的时候,从数据库依次取出一个个记录,插入到Vector里面来。在主程序里面,调用listUsers()方法可以返回一个Vector,然后再对Vector遍历操作,就可以得到用户列表了。
public class User { public static void addUser(...) { // 数据库insert一条记录 } public static void deleteUser(...) { // 数据库delete一条记录 } public Vector listUsers(...) { // 数据库select结果放到一个集合里面 } } |
这样的设计基本合理,但是仍然有点小问题。因为在设计的时候,就考虑到了用Java的集合类Vector来实现对不定长数据集的存放,因而违反了面向对象设计的一个原则:在设计的时候不应过早的考虑具体程序语言的实现。所以必须用抽象的方法,和具体实现无关的方法来表达业务逻辑。 我们知道,通常对具有集合特征的数据结构进行遍历通常可以使用next和hasNext方法,next实现取下一个用户,hasNext判断是否还有元素。 因此我们定义一个接口Iterator,这个接口中定义两个方法next和hasNext:
public interface Iterator { public boolean hasNext() { } public Object next() { } } |
而User类的listUses方法返回值
改为Iterator接口的实现类:
public class User { ... public Iterator listUsers() { } ... } |
这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()和hasNext()方法的类都可以做为listUsers的返回值,都可以被用来表达“用户列表”,而不仅仅可以使用Vector而已。 比如,我可以用ArrayList来表达用户列表,因为ArrayList也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()和hasNext()方法就行了。 这样在具体的编写代码的时候,程序员具有了最大的灵活性,可以根据具体的情况,采用不同的编程方法来存放用户列表。特别是降低了程序的耦合度,提高了程序的可移植性。对于上面那个JunkMail的listAllMail()方法也同样应该改为接口类型。 然后,在主程序里面就这样来使用User类的listUsers方法:
User myUser = new User(); Iterator iterator = myUser.listUsers(); while (iterator.hasNext()) { iterator.next(); } |
这样就可以完全不用考虑程序代码实现了,从高层次上把功能抽象出来,定义成为接口,同时又可以把系统设计的很合理,完全根据业务的需求来进行设计。 结语 通过上面的几个例子的设计说明,使用面向对象的思维方法,其实是一个把业务逻辑从具体的编程技术当中抽象出来的过程,而这个抽象的过程是自上而下的,非常符合人类的思维习惯,也就是先不考虑问题解决的细节,把问题的最主要的方面抽象成为一个简单的框架,集中精力思考如何解决主要矛盾,然后在解决问题的过程中,再把问题的细节分割成一个一个小问题,再专门去解决细节问题。 因而一旦牢牢的抓住了这一点,你就会发现在软件设计和开发过程中,你自己总是会不知不觉的运用面向对象的思维方法来设计和编写程序,并且程序的设计和开发也变得不再那么枯燥,而一个合理运用面向对象技术进行设计和架构的软件,更是具备了思维的艺术美感。最后,愿面向对象的思维方法也能给您的程序设计之路带来创作的乐趣。
Java构造函数的执行情况
Java 在创建对象的时候会要执行它的构造函数。不仅如此,Java 还要执行父类的构造函数,往上一级一级直到没有父类为止。对于初学者来说,有三个问题不容易搞懂:
1、父类的构造函数是否一定会执行?
2、是先执行子类的构造函数还是先执行父类的构造函数?
3、如果父类有多个构造函数,那么 Java 会选择哪一个?
- 父类的构造函数是否一定会执行?
是的,父类的构造函数一定会执行。所以如果某个类的层次很深,那么它创建对象时就会要执行一大堆的构造函数。
- 是先执行子类的构造函数还是先执行父类的构造函数?
Java 会顺着继承结构往上一直找到 Object,然后从 Object 开始往下依次执行构造函数。先执行父类的构造函数,那么子类的构造函数执行的时候就不需要担心父类的成员是否初始化好了。
- 如果父类有多个构造函数,那么 Java 会选择哪一个?
如果父类有多个构造函数,那么子类可以在构造函数中选择其中一个(且最多只能选择一个)来执行。如果子类没有选择,那么 Java 将会执行父类的缺省构造函数。下面是一个例子:
父类:
view plaincopy to clipboardprint? public class SuperClass { public SuperClass() { System.out.println("super class constructed."); } public SuperClass(String name) { System.out.println("super class constructed with name: " + name); } } public class SuperClass { public SuperClass() { System.out.println("super class constructed."); } public SuperClass(String name) { System.out.println("super class constructed with name: " + name); } } |
子类:
view plaincopy to clipboardprint? public class SubClass extends SuperClass { public SubClass() { System.out.println("sub class constructed."); } public SubClass(String name) { //这一句在语法上不是必须的。但有没有这一句,执行起来不一样。有了这一句,就表示子类的构造过程中调用 //的是父类的带字符串参数的构造函数,也就是 public SuperClass(String name) 这个方法。如果没有这一 //句,就表示子类的构造过程中调用的是父类的缺省构造函数。 super(name); System.out.println("sub class constructed with name: " + name); } public static void main(String[] args) { new SubClass(); new SubClass("world"); } } public class SubClass extends SuperClass { public SubClass() { System.out.println("sub class constructed."); } public SubClass(String name) { super(name); System.out.println("sub class constructed with name: " + name); } public static void main(String[] args) { new SubClass(); new SubClass("world"); } } |
Java中JTable的用法简介
Swing颇受欢迎的JTable类为显示大块数据提供了一种简单的机制。JTable有很多东西是用于数据的生成和编辑,其中的很多东西还可以自定义,从而更进一步增强其功能。本文会引导你一步步地进入JTable的世界。
Listing A包含了一个简单示例的代码,这个示例会说明常用JTable的行为。用户能够更改JTable的布局、拖放它的栏,或者通过拖动标题的分隔线来改变其大小。
这些列被保存在一个String数组里:
String[] columnNames = {"Product","Number of Boxes","Price"}; |
数据被初始化并保存在一个二维的对象数组里:
Object[][] data = { {"Apples", new Integer(5),"5.00"}, {"Oranges", new Integer(3),"6.00"}, {"Pears", new Integer(2),"4.00"}, {"Grapes", new Integer(3),"2.00"}, }; |
JTable是使用data和columnNames构成的:
JTable table = new JTable(data, columnNames); |
查看JTable
JTable的高度和宽度按照下面的方法来设定:
table.setPreferredScrollableViewportSize(new Dimension(300, 80)); |
如果JTable的一个列或者JTable窗口自身的大小被重新确定,那么其他列会被相应的缩小或者放大,以适应新的窗口。使用setAutoResizeMode()方法就能够控制这种行为:
table.setAutoResizeMode(int mode); |
mode整数字段可能的值有:
AUTO_RESIZE_OFF AUTO_RESIZE_NEXT_COLUMN AUTO_RESIZE_SUBSEQUENT_COLUMNS AUTO_RESIZE_LAST_COLUMN AUTO_RESIZE_ALL_COLUMNS |
表格的缺省值
单元格内方格坐标线的缺省颜色是Color.gray。要更改这些方格坐标线的颜色,就要用到:
table.setGridColor(Color.black); |
你可以用下面的方法来改变行的高度:
table.setRowHeight(intpixelHeight); |
各个单元格的高度将等于行的高度减去行间的距离。
在缺省情况下,内容的前景颜色和背景颜色的选择都是由Swing的所见即所得的实现来确定的。你可以使用下面的方法来更改选择的颜色:
table.setSelectionBackground(Color.black); table.setSelectionForeground(Color.white); |
你也可以隐藏单元格的方格坐标线,就像下面这样:
table.setShowHorizontalLines(false); table.setShowVerticalLines(false); |
列的宽度
JTable组件有几个控制表格特性的类和接口。TableColumn会不断追踪列的宽度,并负责列大小的调整,包括最大和最小宽度。
TableColumnModel管理着TableColumns的集合以及列的选择。要设置某个列的宽度,就要为表格列的模型设置一个参照。然后,取得想要的TableColumn并调用其setPreferredWidth()方法:
TableColumncolumn = table.getColumnModel().getColumn(0); column.setPreferredWidth(100); |
当用户拖放列的时候,列的索引并不会发生改变。getColumn(0)方法会一直返回正确的列,无论它出现在屏幕的哪个地方。
标题
JtableHeader会处理JTable标题的显示。你可以细分JtableHeader以获得自定义的布局。例如,如果你的应用程序需要一个跨越多个列的标题,那么只用简单地细分JtableHeader并将它集成到你的JTable里就行了。
你可以通过为当前JTable的JtableHeader设置一个参照或者调用其setReorderingAllowed()方法,来指定标题的重新排序是否被允许:
table.getTableHeader().setReorderingAllowed(false); |
类似地,你可以确信列不会因为在列标题之间拖动而改变大小。要达到这个目的,你就要使用setResizingAllowed()方法:
table.getTableHeader().setResizingAllowed(false); |
选择模式
在缺省状况下,当用户在JTable里选择一个单元格的时候,整个行都被选中了。有多种方法能够让用户自定义选择的方式。利用ListSelectionModel接口,你可以允许用户选择单个或者多个行:
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
ListSelectionModel有下面这些字段:
SINGLE_SELECTION允许一次选择一行。
SINGLE_INTERVAL_SELECTION允许选择相邻的一系列行。
MULTIPLE_INTERVAL_SELECTION也允许选择相邻的列,但是带有扩展功能。它允许用户使用[Ctrl]键进行多个互不相邻的选择(即选择不相邻的行)。
setCellSelectionEnabled()方法让用户能够同时选择单个单元格或者整个行:
table.setCellSelectionEnabled(true);
如果被设置为是,setCellSelectionEnabled()方法还会允许在选择行和单个单元格的同时选择列,如果图B所示。
编辑单元格
我们这个简单的表格允许用户编辑表格里的任何单元格。Listing B列出了一个表格,它允许由程序员来决定哪些单元格能够被编辑。第一步是创建一个自定义的TableModel:
class SimpleTableModel extends AbstractTableModel {} |
数据被封装在TableModel里,当JTable初始化的时候,自定义的TableModel就被作为一个参数传递给JTable的构造函数而不是那个二维的对象数组:
SimpleTableModelmyModel = new SimpleTableModel(); JTable table = new JTable(myModel); |
如果想让第二列和第三列也变得可以编辑,并把第一列变成恒定的,那么你就要强制替代TableModel的isCellEditable()方法:
public booleanisCellEditable(int row, intcol){ if (col == 0) {return false;} else {return true; } } |
简单的表格验证
你需要确保用户只输入整数值,假如说,向第二列(“盒子的数量”这一列)输入值来强制替代setValueAt()方法,并将验证逻辑包括进这个新方法里。首先,你要检查列是否是整数,以及这个列是否只应该包含整数值:
if (data[0][col] instanceof Integer && !(value instanceof Integer)) {… } else { data[row][col] = value;} |
然后,检查被插入的值是否是个整数。如果它不是的,那么这个字段就不应该被更新,而且应该要显示一条错误信息:
try { data[row][col] = new Integer(value.toString()); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(SimpleTable.this, "Please enter only integer values."); } |
背景颜色
Listing C包含了用于ColorTable.java的代码,它说明了如何向JTable加入颜色。你可以通过强制替代其prepareRenderer()方法来向JTable加入背景颜色:
JTable table = new JTable(data, columnNames){ public Component prepareRenderer(TableCellRenderer r, int row, intcol){} }; |
然后,插入决定哪些列应该有颜色以及应该是什么颜色的逻辑:
if (col == 2 && !isCellSelected(row, col)){ Color bg = new Color(200, 100, 30); c.setBackground(bg); c.setForeground(Color.white); |
}要注意,当你更改单元格背景颜色的时候,你还应该更该单元格里所显示的文本的颜色,让其变得更加易读。图C显示了一个第一列和第二列加上了颜色的JTable。
一切皆在掌握中
我们的例子只是JTable其他部分的基础。通过使用这些工具,你能够快速和轻易地掌控对Java应用程序所生成的表格的格式化,这样就能够让你的用户在进行正常使用的时候不碰到障碍。
----------------------------------------------------------------------------------------------------------------------------------
import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JPanel; import javax.swing.JTable; import java.awt.Color; import java.awt.GridLayout; import javax.swing.table.TableColumn; public class JTableDemo { public static void main (String[] args) { /* 构造函数有很多下面先介绍几个: JTable() JTable(int numRows, int numColumns) JTable(Object[][] rowData, Object[] columnNames) */ JTable example1 = new JTable ();//看不到但存在 JTable example2 = new JTable (8, 6); final Object[] columnNames = {"姓名", "性别", "家庭地址",//列名最好用final修饰 "电话号码", "生日", "工作", "收入", "婚姻状况","恋爱状况"}; Object[][] rowData = { {"ddd", "男", "江苏南京", "1378313210", "03/24/1985", "学生", "寄生中", "未婚", "没"}, {"eee", "女", "江苏南京", "13645181705", "xx/xx/1985", "家教", "未知", "未婚", "好象没"}, {"fff", "男", "江苏南京", "13585331486", "12/08/1985", "汽车推销员", "不确定", "未婚", "有"}, {"ggg", "女", "江苏南京", "81513779", "xx/xx/1986", "宾馆服务员", "确定但未知", "未婚", "有"}, {"hhh", "男", "江苏南京", "13651545936", "xx/xx/1985", "学生", "流放中", "未婚", "无数次分手后没有"} }; JTable friends = new JTable (rowData, columnNames); friends.setPreferredScrollableViewportSize(new Dimension(600, 100));//设置表格的大小 friends.setRowHeight (30);//设置每行的高度为20 friends.setRowHeight (0, 20);//设置第1行的高度为15 friends.setRowMargin (5);//设置相邻两行单元格的距离 friends.setRowSelectionAllowed (true);//设置可否被选择.默认为false friends.setSelectionBackground (Color.white);//设置所选择行的背景色 friends.setSelectionForeground (Color.red);//设置所选择行的前景色 friends.setGridColor (Color.black);//设置网格线的颜色 friends.selectAll ();//选择所有行 friends.setRowSelectionInterval (0,2);//设置初始的选择行,这里是1到3行都处于选择状态 friends.clearSelection ();//取消选择 friends.setDragEnabled (false);//不懂这个 friends.setShowGrid (false);//是否显示网格线 friends.setShowHorizontalLines (false);//是否显示水平的网格线 friends.setShowVerticalLines (true);//是否显示垂直的网格线 friends.setValueAt ("tt", 0, 0);//设置某个单元格的值,这个值是一个对象 friends.doLayout (); friends.setBackground (Color.lightGray); JScrollPane pane1 = new JScrollPane (example1);//JTable最好加在JScrollPane上 JScrollPane pane2 = new JScrollPane (example2); JScrollPane pane3 = new JScrollPane (friends); JPanel panel = new JPanel (new GridLayout (0, 1)); panel.setPreferredSize (new Dimension (600,400)); panel.setBackground (Color.black); panel.add (pane1); panel.add (pane2); panel.add (pane3); JFrame frame = new JFrame ("JTableDemo"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); frame.setContentPane (panel); frame.pack(); frame.show(); }; |
Java之父首度谈论离开甲骨文原因
Java之父詹姆斯-高斯林(James Gosling)近日在接受eWEEK专访时,首度公开谈论了他为什么离开甲骨文。
昔日当詹姆斯-高斯 林带领团队创造Java语言和平台时,太阳计算机系统公司(Sun Microsystems)的发展正如日中天,Java也成为一项革命性的技术。但是财务问题却让太阳计算机系统公司陷入困境,并最终被甲骨文所收购。但 无论如何,创造Java的高斯林理应得到起码的尊重,然而,据高斯林透露,他在甲骨文的境遇却恰恰相反。
早在今年4月,高斯林就在其博客上宣布了从甲骨文离职的消息,但是他当时并没有解释离职的原因:“至于我为什么选择离开,这个问题一时间难以作答:如果我说出真相,恐怕有百害而无一利。”然而,恰逢甲骨文本周首次举办JavaOne会议之际,高斯林终于摒弃顾虑,接受eWEEK专访,首次畅所欲言地谈起了自己离开甲骨文的原因。
“实际上,促使我离开甲骨文的因素有很多。”高斯林说,“其中,甲骨文给我开的薪水对我是一个沉重的打击。它们只给了太阳计算机系统公司原先给我的底薪。”他说。要知道,在太阳计算机系统公司,除了底薪之外,副总裁及以上的任何高管都会根据公司业绩获得额外的奖金。“在一般年份,报酬也还不错;遇到好的年成,报酬相当可观”。
但是报酬本身还不足以磨灭高斯林的工作热情。事实上,尽管工资水平大大降低,高斯林当初还是选择了继续在甲骨文工作。然而,按下葫芦浮起瓢,刚刚勉强接受屈辱的工资,又来了一件烦心的事情。高斯林说,甲骨文没有给他资深工程师的头衔,或者相当于其在太阳计算机系统公司的级别。“在甲骨文给我的职位中,我的级别被大大降低了。”他说。
然而,即使这样,他也没有离开甲骨文。压垮高斯林的“最后一根稻草”也许是甲骨文限制他权力的举动。事实上,甲骨文收购太阳计算机系统公司,就等于拥有了Java,也拥有了Java的创始人及其知识产权,因此,甲骨文有权决定高斯林或其他人对于Java的发言权。
“我的决定权在甲骨文被大大削减了。”高斯林说,“甲骨文事无巨细都会过问,我和我在Java领域工作的同事,均无权决定任何事情。我们的一切决定权都被剥夺了。”
这种做法削弱了高斯林继续工作的决心,但还没有到让他死心的地步。让他心灰意冷,决定彻底与甲骨文决裂的原因,可能是“我的工作似乎只是代表甲骨文的Java出席各种公开活动。而我的性格是不适合做这个的。”
事已至此,高斯林别无选择,只好离开。高斯林说,他宁愿太阳计算机系统公司被IBM公司收购,也不愿它被甲骨文收购。
甲骨文发言人称该公司尚未对高斯林的言论作出任何评论。
- 默认分类(20)
- J2EE(25)
- Java(56)
- PHP(55)
- SEO(10)
- 网页设计(20)
- 网站建设(37)
- 数据库(7)
- JavaScript(17)
- JQuery(6)
- MySQL(20)
- SQL Server(6)
- Access(1)
- Oracle(6)
- office(6)
- Dreamweaver(4)
- Photoshop(12)
- Flash(9)
- Fireworks(13)
- CSS(14)
- HTML(4)
- .NET(7)
- ASP(2)
- DB2(1)
- Ajax(2)
- Linux(12)
- Struts(7)
- Hibernate(8)
- Spring(2)
- Jsp(22)
- Asp(8)
- C#(3)
- C++(1)
- 网络安全(5)
- 软件工程(7)
- XML(1)
- English(2)
- 计算机等级考试(2)
- 计算机病毒(4)
- 个人日志(76)
- 互联网(15)
- ActionScript(10)
- Android(3)
- 数据结构与算法(1)
- 游戏策略(3)
- 美文翻译(2)
- 编程开发(19)
- 计算机应用(4)
- 计算机(10)
- Unity3d(6)
- 其他(1)
- egret(1)