⑴ java阻塞队列 线程同步合作
Queue接口与List Set同一级别 都是继承了Collection接口 LinkedList实现了Queue接口 Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时 就完全只能访问Queue接口所定义的方法了 而不能直接访问 LinkedList的非Queue的方法) 以使得只有恰当的方法才可以使用 BlockingQueue 继承了Queue接口
队列是一种数据结构.它有两个基本操作 在队列尾部加人一个元素 和从队列头部移除一个元素就是说 队列以一种先进先出的方式管理数据 如果你试图向一个已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索 将导致线程阻塞.在多线程进行合作时 阻塞队列是很有用的工具 工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线线程把中间结果取出并在将来修改它们 队列会自动平衡负载 如果第一个线程集运行得比第二个慢 则第二个线程集在等待结果时就会阻塞 如果第一个线程集运行得快 那么它将等待第二个线程集赶上来 下表显示了jdk 中的阻塞队列的操作
add 增加一个元索 如果队列已春慎桥满 则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空 则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空 则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满 则返回false
poll 移除并返问队列头部的元素 如果队列扒猛为空 则返回null
peek 返回队列头部的元素 如果队列为空 则返回null
put 添加一个元素 如果队列满 则阻塞
take 移除并返回队列头部的元素 如果队列为空 则阻塞
remove element offer poll peek 其实是属于Queue接口
阻塞队列的操作可以根据它们的响应方式分为以下三类 aad removee和element操作在你试图为一个已满的队列增加元素或从空队列取得元素时抛出异常 当然 在多线程程序中 队列在任何时间都可能变成满的或空的 所以你可能想使用offer poll peek方法 这些方法在无法完成任务时只是给出一个出错示而不会抛出异常
注意 poll和peek方法出错进返回null 因此 向队列中插入null值是不合法的
还有带超时的offer和poll方法变种 例如 下面的调用
boolean success = q offer(x TimeUnit MILLISECONDS);
尝试在 毫秒内向队列尾部插入一个元素 如果成功 立即返回true 否则 当到达超时进 返回false 同样地 调用
Object head = q poll( TimeUnit MILLISECONDS);
如果在 毫秒内成功地移除了队列头元素 则立即返回头元素 否则在到达超时时 返回null
最后 我们有阻塞操作put和take put方法在队列满时阻塞 take方法在队列空时阻塞
ncurrent包提供了阻塞队列的 个变种 默认情况下 LinkedBlockingQueue的容量是没有上限的(说的不准确 在不指定时容量为Integer MAX_VALUE 不要然的话在put时怎么会受阻呢) 但是也可以选择指定其最大容量 它是基孝仔于链表的队列 此队列按 FIFO(先进先出)排序元素
ArrayBlockingQueue在构造时需要指定容量 并可以选择是否需要公平性 如果公平参数被设置true 等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来达到这种公平性的 即等待时间最长的线程会先操作) 通常 公平性会使你在性能上付出代价 只有在的确非常需要的时候再使用它 它是基于数组的阻塞循环队列 此队列按 FIFO(先进先出)原则对元素进行排序
PriorityBlockingQueue是一个带优先级的队列 而不是先进先出队列 元素按优先级顺序被移除 该队列也没有上限(看了一下源码 PriorityBlockingQueue是对PriorityQueue的再次包装 是基于堆数据结构的 而PriorityQueue是没有容量限制的 与ArrayList一样 所以在优先阻塞队列上put时是不会受阻的 虽然此队列逻辑上是无界的 但是由于资源被耗尽 所以试图执行添加操作可能会导致 OutOfMemoryError) 但是如果队列为空 那么取元素的操作take就会阻塞 所以它的检索操作take是受阻的 另外 往入该队列中的元素要具有比较能力
最后 DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列 只有在延迟期满时才能从中提取元素 该队列的头部是延迟期满后保存时间最长的 Delayed 元素 如果延迟都还没有期满 则队列没有头部 并且poll将返回null 当一个元素的 getDelay(TimeUnit NANOSECONDS) 方法返回一个小于或等于零的值时 则出现期满 poll就以移除这个元素了 此队列不允许使用 null 元素 下面是延迟接口
Java代码
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
放入DelayQueue的元素还将要实现pareTo方法 DelayQueue使用这个来为元素排序
下面的实例展示了如何使用阻塞队列来控制线程集 程序在一个目录及它的所有子目录下搜索所有文件 打印出包含指定关键字的文件列表 从下面实例可以看出 使用阻塞队列两个显着的好处就是 多线程操作共同的队列时不需要额外的同步 另外就是队列会自动平衡负载 即那边(生产与消费两边)处理快了就会被阻塞掉 从而减少两边的处理速度差距 下面是具体实现
Java代码
public class BlockingQueueTest {
public static void main(String[] args) {
Scanner in = new Scanner(System in);
System out print( Enter base directory (e g /usr/local/jdk /src): );
String directory = in nextLine();
System out print( Enter keyword (e g volatile): );
String keyword = in nextLine();
final int FILE_QUEUE_SIZE = ;// 阻塞队列大小
final int SEARCH_THREADS = ;// 关键字搜索线程个数
// 基于ArrayBlockingQueue的阻塞队列
BlockingQueue<File> queue = new ArrayBlockingQueue<File>(
FILE_QUEUE_SIZE);
//只启动一个线程来搜索目录
FileEnumerationTask enumerator = new FileEnumerationTask(queue
new File(directory));
new Thread(enumerator) start();
//启动 个线程用来在文件中搜索指定的关键字
for (int i = ; i <= SEARCH_THREADS; i++)
new Thread(new SearchTask(queue keyword)) start();
}
}
class FileEnumerationTask implements Runnable {
//哑元文件对象 放在阻塞队列最后 用来标示文件已被遍历完
public static File DUMMY = new File( );
private BlockingQueue<File> queue;
private File startingDirectory;
public FileEnumerationTask(BlockingQueue<File> queue File startingDirectory) {
this queue = queue;
this startingDirectory = startingDirectory;
}
public void run() {
try {
enumerate(startingDirectory);
queue put(DUMMY);//执行到这里说明指定的目录下文件已被遍历完
} catch (InterruptedException e) {
}
}
// 将指定目录下的所有文件以File对象的形式放入阻塞队列中
public void enumerate(File directory) throws InterruptedException {
File[] files = directory listFiles();
for (File file : files) {
if (file isDirectory())
enumerate(file);
else
//将元素放入队尾 如果队列满 则阻塞
queue put(file);
}
}
}
class SearchTask implements Runnable {
private BlockingQueue<File> queue;
private String keyword;
public SearchTask(BlockingQueue<File> queue String keyword) {
this queue = queue;
this keyword = keyword;
}
public void run() {
try {
boolean done = false;
while (!done) {
//取出队首元素 如果队列为空 则阻塞
File file = queue take();
if (file == FileEnumerationTask DUMMY) {
//取出来后重新放入 好让其他线程读到它时也很快的结束
queue put(file);
done = true;
} else
search(file);
}
} catch (IOException e) {
e printStackTrace();
} catch (InterruptedException e) {
}
}
public void search(File file) throws IOException {
Scanner in = new Scanner(new FileInputStream(file));
int lineNumber = ;
while (in hasNextLine()) {
lineNumber++;
String line = in nextLine();
if (ntains(keyword))
System out printf( %s:%d:%s%n file getPath() lineNumber
line);
}
in close();
}
lishixin/Article/program/Java/hx/201311/26657
⑵ JAVA中哪个能同时满足 先进先出(增减操作非常频繁)和同步安全的容器集合并在性能上不至于太差
public class ConcurrentLinkedQueue<E>
extends AbstractQueue<E>
implements Queue<E>, Serializable
一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素。
此实现采用了有效的“无等待 (wait-free)”算法,该算法基于 Maged M. Michael 和 Michael L. Scott 合着的 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms 中描述的算法。
需要小心的是,与大多数 collection 不同,size 方法不是 一个固定时间操作。由于这些队列的异步特性,确定当前元素的数量需要遍历这些元素。
此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。
内存一致性效果:当存在其他并发 collection 时,将对象放入 ConcurrentLinkedQueue 之前的线程中的操作 happen-before 随后通过另一线程从 ConcurrentLinkedQueue 访问或移除该元素的操作。
注意:ConcurrentLinkedQueue的size()是要遍历一遍集合的!因此,若不能满足你,可以基于 LinkedList(先进先出),自己加上同步,要性能控制住,需要尽可能小力度加同步 。
⑶ 在JAVA中怎么实现消息队列
java中的消息队列
消息队列是线程间通讯的手段:
importjava.util.*
publicclassMsgQueue{
privateVectorqueue=null;
publicMsgQueue(){
queue=newVector();
}
publicsynchronizedvoidsend(Objecto)
{
queue.addElement(o);
}
publicsynchronizedObjectrecv()
{
if(queue.size()==0)
returnnull;
Objecto=queue.firstElement();
queue.removeElementAt(0);//orqueue[0]=nullcanalsowork
returno;
}
}
因为java中是lockedbyobject的所以添加synchronized就可以用于线程同步锁定对象
可以作为多线程处理多任务的存放task的队列。他的client包括封装好的task类以及thread类
Java的多线程-线程间的通信2009-08-2521:58
1.线程的几种状态
线程有四种状态,任何一个线程肯定处于这四种状态中的一种:
1)产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一个线程对象后没对它调用start()函数之前。
2)可执行(Runnable):每个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并启动它。当一个线程处于可执行状态时,表示它可能正处于线程池中等待排排程器启动它;也可能它已正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态,但显而易见的是此时线程不一定正在执行中。
3)死亡(Dead):当一个线程正常结束,它便处于死亡状态。如一个线程的run()函数执行完毕后线程就进入死亡状态。
4)停滞(Blocked):当一个线程处于停滞状态时,系统排程器就会忽略它,不对它进行排程。当处于停滞状态的线程重新回到可执行状态时,它有可能重新执行。如通过对一个线程调用wait()函数后,线程就进入停滞状态,只有当两次对该线程调用notify或notifyAll后它才能两次回到可执行状态。
2.classThread下的常用函数函数
2.1suspend()、resume()
1)通过suspend()函数,可使线程进入停滞状态。通过suspend()使线程进入停滞状态后,除非收到resume()消息,否则该线程不会变回可执行状态。
2)当调用suspend()函数后,线程不会释放它的“锁标志”。
例11:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
if(shareVar==0){
for(inti=0;i<5;i++){
shareVar++;
if(shareVar==5){
this.suspend();//(1)
}}}
else{
System.out.print(Thread.currentThread().getName());
System.out.println("shareVar="+shareVar);
this.resume();//(2)
}}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();//(5)
//t1.start();//(3)
t2.start();//(4)
}}
运行结果为:
t2shareVar=5
i.当代码(5)的t1所产生的线程运行到代码(1)处时,该线程进入停滞状态。然后排程器从线程池中唤起代码(4)的t2所产生的线程,此时shareVar值不为0,所以执行else中的语句。
ii.也许你会问,那执行代码(2)后为什么不会使t1进入可执行状态呢?正如前面所说,t1和t2是两个不同对象的线程,而代码(1)和(2)都只对当前对象进行操作,所以t1所产生的线程执行代码(1)的结果是对象t1的当前线程进入停滞状态;而t2所产生的线程执行代码(2)的结果是把对象t2中的所有处于停滞状态的线程调回到可执行状态。
iii.那现在把代码(4)注释掉,并去掉代码(3)的注释,是不是就能使t1重新回到可执行状态呢?运行结果是什么也不输出。为什么会这样呢?也许你会认为,当代码(5)所产生的线程执行到代码(1)时,它进入停滞状态;而代码(3)所产生的线程和代码(5)所产生的线程是属于同一个对象的,那么就当代码(3)所产生的线程执行到代码(2)时,就可使代码(5)所产生的线程执行回到可执行状态。但是要清楚,suspend()函数只是让当前线程进入停滞状态,但并不释放当前线程所获得的“锁标志”。所以当代码(5)所产生的线程进入停滞状态时,代码(3)所产生的线程仍不能启动,因为当前对象的“锁标志”仍被代码(5)所产生的线程占有。
#p#2.2sleep()
1)sleep()函数有一个参数,通过参数可使线程在指定的时间内进入停滞状态,当指定的时间过后,线程则自动进入可执行状态。
2)当调用sleep()函数后,线程不会释放它的“锁标志”。
例12:
{
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
for(inti=0;i<3;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
try{
Thread.sleep(100);//(4)
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}}}
}
publicclassTestThread{publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();(1)
t1.start();(2)
//t2.start();(3)
}}
运行结果为:
t1:0
t1:1
t1:2
t1:0
t1:1
t1:2
由结果可证明,虽然在run()中执行了sleep(),但是它不会释放对象的“锁标志”,所以除非代码(1)的线程执行完run()函数并释放对象的“锁标志”,否则代码(2)的线程永远不会执行。
如果把代码(2)注释掉,并去掉代码(3)的注释,结果将变为:
t1:0
t2:0
t1:1
t2:1
t1:2
t2:2
由于t1和t2是两个对象的线程,所以当线程t1通过sleep()进入停滞时,排程器会从线程池中调用其它的可执行线程,从而t2线程被启动。
例13:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
for(inti=0;i<5;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
try{
if(Thread.currentThread().getName().equals("t1"))
Thread.sleep(200);
else
Thread.sleep(100);
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}}
}}
publicclassTestThread{publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();
//t1.start();
t2.start();
}}
运行结果为:
t1:0
t2:0
t2:1
t1:1
t2:2
t2:3
t1:2
t2:4
t1:3
t1:4
由于线程t1调用了sleep(200),而线程t2调用了sleep(100),所以线程t2处于停滞状态的时间是线程t1的一半,从从结果反映出来的就是线程t2打印两倍次线程t1才打印一次。
#p#2.3yield()
1)通过yield()函数,可使线程进入可执行状态,排程器从可执行状态的线程中重新进行排程。所以调用了yield()的函数也有可能马上被执行。
2)当调用yield()函数后,线程不会释放它的“锁标志”。
例14:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){super(name);
}
publicsynchronizedvoidrun(){for(inti=0;i<4;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
Thread.yield();
}}
}
publicclassTestThread{publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();
t1.start();//(1)
//t2.start();(2)
}
}
运行结果为:
t1:0
t1:1
t1:2
t1:3
t1:0
t1:1
t1:2
t1:3
从结果可知调用yield()时并不会释放对象的“锁标志”。
如果把代码(1)注释掉,并去掉代码(2)的注释,结果为:
t1:0
t1:1
t2:0
t1:2
t2:1
t1:3
t2:2
t2:3
从结果可知,虽然t1线程调用了yield(),但它马上又被执行了。
2.4sleep()和yield()的区别
1)sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
2)sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
例15:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicvoidrun(){
for(inti=0;i<4;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
//Thread.yield();(1)
/*(2)*/
try{
Thread.sleep(3000);
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}}}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
运行结果为:
t1:0
t1:1
t2:0
t1:2
t2:1
t1:3
t2:2
t2:3
由结果可见,通过sleep()可使优先级较低的线程有执行的机会。注释掉代码(2),并去掉代码(1)的注释,结果为:
t1:0
t1:1
t1:2
t1:3
t2:0
t2:1
t2:2
t2:3
可见,调用yield(),不同优先级的线程永远不会得到执行机会。
2.5join()
使调用join()的线程执行完毕后才能执行其它线程,在一定意义上,它可以实现同步的功能。
例16:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicvoidrun(){
for(inti=0;i<4;i++){
System.out.println(""+i);
try{
Thread.sleep(3000);
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}
}
}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
t1.start();
try{
t1.join();
}
catch(InterruptedExceptione){}
t1.start();
}
}
运行结果为:
0
1
2
3
0
1
2
3
#p#3.classObject下常用的线程函数
wait()、notify()和notifyAll()这三个函数由java.lang.Object类提供,用于协调多个线程对共享数据的存取。
3.1wait()、notify()和notifyAll()
1)wait()函数有两种形式:第一种形式接受一个毫秒值,用于在指定时间长度内暂停线程,使线程进入停滞状态。第二种形式为不带参数,代表waite()在notify()或notifyAll()之前会持续停滞。
2)当对一个对象执行notify()时,会从线程等待池中移走该任意一个线程,并把它放到锁标志等待池中;当对一个对象执行notifyAll()时,会从线程等待池中移走所有该对象的所有线程,并把它们放到锁标志等待池中。
3)当调用wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
例17:
下面,我们将对例11中的例子进行修改
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
if(shareVar==0){
for(inti=0;i<10;i++){
shareVar++;
if(shareVar==5){
try{
this.wait();//(4)
}
catch(InterruptedExceptione){}
}
}
}
if(shareVar!=0){
System.out.print(Thread.currentThread().getName());
System.out.println("shareVar="+shareVar);
this.notify();//(5)
}
}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();//(1)
//t1.start();(2)
t2.start();//(3)
}}
运行结果为:
t2shareVar=5
因为t1和t2是两个不同对象,所以线程t2调用代码(5)不能唤起线程t1。如果去掉代码(2)的注释,并注释掉代码(3),结果为:
t1shareVar=5
t1shareVar=10
这是因为,当代码(1)的线程执行到代码(4)时,它进入停滞状态,并释放对象的锁状态。接着,代码(2)的线程执行run(),由于此时shareVar值为5,所以执行打印语句并调用代码(5)使代码(1)的线程进入可执行状态,然后代码(2)的线程结束。当代码(1)的线程重新执行后,它接着执行for()循环一直到shareVar=10,然后打印shareVar。
#p#3.2wait()、notify()和synchronized
waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronizedblock中进行调用。如果在non-synchronized函数或non-synchronizedblock中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
例18:
{
publicintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
newNotifier(this);
}
publicsynchronizedvoidrun(){
if(shareVar==0){
for(inti=0;i<5;i++){
shareVar++;
System.out.println("i="+shareVar);
try{
System.out.println("wait......");
this.wait();
}
catch(InterruptedExceptione){}
}}
}
}
classNotifierextendsThread{
privateTestThreadMethodttm;
Notifier(TestThreadMethodt){
ttm=t;
start();
}
publicvoidrun(){
while(true){
try{
sleep(2000);
}
catch(InterruptedExceptione){}
/*1要同步的不是当前对象的做法*/
synchronized(ttm){
System.out.println("notify......");
ttm.notify();
}}
}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
t1.start();
}
}
运行结果为:
i=1
wait......
notify......
i=2
wait......
notify......
i=3
wait......
notify......
i=4
wait......
notify......
i=5
wait......
notify......
4.wait()、notify()、notifyAll()和suspend()、resume()、sleep()的讨论
4.1这两组函数的区别
1)wait()使当前线程进入停滞状态时,还会释放当前线程所占有的“锁标志”,从而使线程对象中的synchronized资源可被对象中别的线程使用;而suspend()和sleep()使当前线程进入停滞状态时不会释放当前线程所占有的“锁标志”。
2)前一组函数必须在synchronized函数或synchronizedblock中调用,否则在运行时会产生错误;而后一组函数可以non-synchronized函数和synchronizedblock中调用。
4.2这两组函数的取舍
Java2已不建议使用后一组函数。因为在调用suspend()时不会释放当前线程所取得的“锁标志”,这样很容易造成“死锁”。
⑷ java 分布式数据同步通过什么实现的
Java是一种跨平台,适合于分布式计算环境的面向对象编程语言。
具体来说,它具有如下特性:
简单性、面向对象、分布式、解释型、可靠、安全、平台无关、可移植、高性能、多线程、动态性等。
下面我们将重点介绍Java语言的面向对象、平台无关、分布式、多线程、可靠和安全等特性。
1.面向对象
面向对象其实是现实世界模型的自然延伸。现实世界中任何实体都可以看作是对象。对象之间通过消息相互作用。另外,现实世界中任何实体都可归属于某类事物,任何对象都是某一类事物的实例。如果说传统的过程式编程语言是以过程为中心以算法为驱动的话,面向对象的编程语言则是以对象为中心以消息为驱动。用公式表示,过程式编程语言为:程序=算法+数据;面向对象编程语言为:程序=对象+消息。
所有面向对象编程语言都支持三个概念:封装、多态性和继承,Java也不例外。现实世界中的对象均有属性和行为,映射到计算机程序上,属性则表示对象的数据,行为表示对象的方法(其作用是处理数据或同外界交互)。所谓封装,就是用一个自主式框架把对象的数据和方法联在一起形成一个整体。可以说,对象是支持封装的手段,是封装的基本单位。Java语言的封装性较强,因为Java无全程变量,无主函数,在Java中绝大部分成员是对象,只有简单的数字类型、字符类型和布尔类型除外。而对于这些类型,Java也提供了相应的对象类型以便与其他对象交互操作。
多态性就是多种表现形式,具体来说,可以用“一个对外接口,多个内在实现方法”表示。举一个例子,计算机中的堆栈可以存储各种格式的数据,包括整型,浮点或字符。不管存储的是何种数据,堆栈的算法实现是一样的。针对不同的数据类型,编程人员不必手工选择,只需使用统一接口名,系统可自动选择。运算符重载(operatoroverload)一直被认为是一种优秀的多态机制体现,但由于考虑到它会使程序变得难以理解,所以Java最后还是把它取消了。
继承是指一个对象直接使用另一对象的属性和方法。事实上,我们遇到的很多实体都有继承的含义。例如,若把汽车看成一个实体,它可以分成多个子实体,如:卡车、公共汽车等。这些子实体都具有汽车的特性,因此,汽车是它们的“父亲”,而这些子实体则是汽车的“孩子”。Java提供给用户一系列类(class),Java的类有层次结构,子类可以继承父类的属性和方法。与另外一些面向对象编程语言不同,Java只支持单一继承。
2平台无关性
Java是平台无关的语言是指用Java写的应用程序不用修改就可在不同的软硬件平台上运行。平台无关有两种:源代码级和目标代码级。C和C++具有一定程度的源代码级平台无关,表明用C或C++写的应用程序不用修改只需重新编译就可以在不同平台上运行。
Java主要靠Java虚拟机(JVM)在目标码级实现平台无关性。JVM是一种抽象机器,它附着在具体操作系统之上,本身具有一套虚机器指令,并有自己的栈、寄存器组等。但JVM通常是在软件上而不是在硬件上实现。(目前,SUN系统公司已经设计实现了Java芯片,主要使用在网络计算机NC上。
另外,Java芯片的出现也会使Java更容易嵌入到家用电器中。)JVM是Java平台无关的基础,在JVM上,有一个Java解释器用来解释Java编译器编译后的程序。Java编程人员在编写完软件后,通过Java编译器将Java源程序编译为JVM的字节代码。任何一台机器只要配备了Java解释器,就可以运行这个程序,而不管这种字节码是在何种平台上生成的。另外,Java采用的是基于IEEE标准的数据类型。通过JVM保证数据类型的一致性,也确保了Java的平台无关性。
Java的平台无关性具有深远意义。首先,它使得编程人员所梦寐以求的事情(开发一次软件在任意平台上运行)变成事实,这将大大加快和促进软件产品的开发。其次Java的平台无关性正好迎合了“网络计算机”思想。如果大量常用的应用软件(如字处理软件等)都用Java重新编写,并且放在某个Internet服务器上,那么具有NC的用户将不需要占用大量空间安装软件,他们只需要一个
Java解释器,每当需要使用某种应用软件时,下载该软件的字节代码即可,运行结果也可以发回服务器。目前,已有数家公司开始使用这种新型的计算模式构筑自己的企业信息系统。
3分布式
分布式包括数据分布和操作分布。数据分布是指数据可以分散在网络的不同主机上,操作分布是指把一个计算分散在不同主机上处理。
Java支持WWW客户机/服务器计算模式,因此,它支持这两种分布性。对于前者,Java提供了一个叫作URL的对象,利用这个对象,你可以打开并访问具有相同URL地址上的对象,访问方式与访问本地文件系统相同。对于后者,Java的applet小程序可以从服务器下载到客户端,即部分计算在客户端进行,提高系统执行效率。
Java提供了一整套网络类库,开发人员可以利用类库进行网络程序设计,方便得实现Java的分布式特性。
4可靠性和安全性
Java最初设计目的是应用于电子类消费产品,因此要求较高的可靠性。Java虽然源于C++,但它消除了许多C++不可靠因素,可以防止许多编程错误。首先,Java是强类型的语言,要求显式的方法声明,这保证了编译器可以发现方法调用错误,保证程序更加可靠;其次,Java不支持指针,这杜绝了内存的非法访问;第三,Java的自动单元收集防止了内存丢失等动态内存分配导致的问题;第四,Java解释器运行时实施检查,可以发现数组和字符串访问的越界,最后,Java提供了异常处理机制,程序员可以把一组错误代码放在一个地方,这样可以简化错误处理任务便于恢复。
由于Java主要用于网络应用程序开发,因此对安全性有较高的要求。如果没有安全保证,用户从网络下载程序执行就非常危险。Java通过自己的安全机制防止了病毒程序的产生和下载程序对本地系统的威胁破坏。当Java字节码进入解释器时,首先必须经过字节码校验器的检查,然后,Java解释器将决定程序中类的内存布局,随后,类装载器负责把来自网络的类装载到单独的内存区域,避免应用程序之间相互干扰破坏。最后,客户端用户还可以限制从网络上装载的类只能访问某些文件系统。
上述几种机制结合起来,使得Java成为安全的编程语言。
5多线程
线程是操作系统的一种新概念,它又被称作轻量进程,是比传统进程更小的可并发执行的单位。
C和C++采用单线程体系结构,而Java却提供了多线程支持。
Java在两方面支持多线程。一方面,Java环境本身就是多线程的。若干个系统线程运行负责必要的无用单元回收,系统维护等系统级操作;另一方面,Java语言内置多线程控制,可以大大简化多线程应用程序开发。Java提供了一个类Thread,由它负责启动运行,终止线程,并可检查线程状态。Java的线程还包括一组同步原语。这些原语负责对线程实行并发控制。利用Java的多线程编程接口,开发人员可以方便得写出支持多线程的应用程序,提高程序执行效率。必须注意地是,Java的多线程支持在一定程度上受运行时支持平台的限制。例如,如果操作系统本身不支持多线程,Java的多线程特性可能就表现不出来。
希望对你有帮助!