java基础知识点



java基础知识点。

今天查看别人的java代码,发现自己对一下关键字,语法不是很透彻,在网上查了查,现在将总结整理的东西贴上来。供以后自己或他人参考。

   题为’java基础知识’,所以有关它的东西将陆续帖子此博中。

一.JAVA序列化(对象持久化)

    列化是将对象转换为容易传输的格式的过程,就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间或存储在存储设备上。即序列化是一种对象持久化的过程。

   java对象持久化有两种方式:

   1.实现java.io.Serializable接口(序列化)

       将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。

例子如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;

public class Main implements Serializable {

    private static final long serialVersionUID = -5836283489677344417L;
   private transient int classValue = 10;
   private transient Date date = new Date();
   private transient static int staticValue = 10;

    public static void main(String[] args) throws Exception {
       Main m = new Main();
       m.classValue = 11;
       Main.staticValue = 11;
      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(“0xjh000″)));
       out.writeObject(m);

        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(“0xjh000″)));
       Main m1 = (Main) in.readObject();
       in.close();

        System.out.println(m1.classValue);

        System.out.println(m1.staticValue);
       System.out.println((m1.date == null ? “date is null”:”date is not null”));
   }
}

上面例子中有个serialVersionUID常量,下面就对该常量做也行解释: 
   serialVersionUID的作用是,序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
   serialVersionUID的两种生成方式:
   一个是默认的1L,比如:private static final long serialVersionUID = 1L;
   一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
   private static final long serialVersionUID = xxxxL;

   

   当你一个类实现了Serializable接口,如果没有定义serialVersionUID,Eclipse会提供这个提示功能告诉你去定义 。想有取消这个提示有类方法:

   a.在实现了Serializable接口的类中定义serialVersionUID;

   b.在实现了Serializable接口的类中做 @SuppressWarnings(“serial”) 的注释

   c.设置Eclipse相关配置,取消该提示。

   设置如下:Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==>Potential programming problems 将Serializable class without serialVersionUID的warning改成ignore即可。

  

   2.实现java.io.Externalizable接口(外部化)

       Externalizable继承自Serialization ,外部化的实现方式大体和序列化相同。唯一不同点是java.io.Externalizable的所有实现者必须提供读取和写出方法的实现。即实现接口voidreadExternal(ObjectInput in)和void writeExternal(ObjectOutput out)。

例子如下:

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class ExternalizableTest implements Externalizable {

    private transient String content = “我将会被序列化,不管我是是否被transient关键字修饰”;

    @Override
   public void writeExternal(ObjectOutput out) throws IOException {
       out.writeObject(content);
   }

    @Override
   public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
       content = (String) in.readObject();
   }

    public static void main(String[] args) throws Exception {
       ExternalizableTest et = new ExternalizableTest();
       ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File(“ext0000″)));
       out.writeObject(et);

        ObjectInput in = new ObjectInputStream(new FileInputStream(new File(“ext0000″)));
       ExternalizableTest et1 = (ExternalizableTest) in.readObject();
       System.out.println(et1.content);

        out.close();
       in.close();
   }
}

   总结两个接口的优点和缺点:

   Serializable接口

   · 优点:内建支持

   · 优点:易于实现

   · 缺点:占用空间过大

   · 缺点:由于额外的开销导致速度变比较慢


   Externalizable接口

   · 优点:开销较少(程序员决定存储什么)

   · 优点:可能的速度提升

   · 缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上。

    在两者之间如何选择要根据应用程序的需求来定。Serializable通常是最简单的解决方案,但是它可能会导致出现不可接受的性能问题或空间问题;在出现这些问题的情况下,Externalizable可能是一条可行之路。

要记住一点,如果一个类是可外部化的(Externalizable),那么Externalizable方法将被用于序列化类的实例,即使这个类型提供了Serializable方法:private void writeObject()和private void readObject()。

二.JAVA Transient关键字

   1.transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量(方法内的变量)是不能被transient关键字修饰的。
   2.被transient关键字修饰的变量不再能被序列化,但静态变量不管是否被transient修饰,均能被序列化。
   3.一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。也可以认为在将持久化的对象反序列化后,被transient修饰的变量将按照普通类成员变量一样被初始化。

   参考上面的例子。

三.JAVA native关键字

    native是java语言中的一种方法修饰符。Native方法是由另外一种语言(如c/c++,FORTRAN,汇编)实现的本地方法。

   JAVA本地方法适用的情况:
1.为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问
2.为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的
3.为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。

例子如下:

   可以将native方法比作Java程序同C程序的接口,其实现步骤:
1.在Java中声明native()方法,然后编译;
2.用javah产生一个.h文件;
3.写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
4.将第三步的.cpp文件编译成动态链接库文件;
5.在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。
首先写好JAVA文件
package com.hode.hodeframework.modelupdate;
public class CheckFile {
  public native void displayHelloWorld();
  static {
      System.loadLibrary(“test”);
  }
  public static void main(String[] args) {
      new CheckFile().displayHelloWorld();
  }
}
然后根据写好的文件编译成CLASS文件,

在classes或bin之类的class根目录下执行javah -jni com.hode.hodeframework.modelupdate.CheckFile,  

   就会在根目录下得到一个com_hode_hodeframework_modelupdate_CheckFile.h的文件,

   然后根据头文件的内容编写com_hode_hodeframework_modelupdate_CheckFile.c文件

#include “CheckFile.h”
#include
#include
JNIEXPORT void JNICALL Java_com_hode_hodeframework_modelupdate_CheckFile_displayHelloWorld (JNIEnv *env, jobject obj){
 printf(“Hello world!\n”);
 return;

   之后编译生成DLL文件如“test.dll”,名称与System.loadLibrary(“test”)中的名称一致 
   vc的编译方法:cl -I%java_home%\include -I%java_home%\include\win32 -LD com_hode_hodeframework_modelupdate_CheckFile.c -Fetest.dll 最后在运行时加参数-Djava.library.path=[dll存放的路径]

四.JAVA多线程实现和应用总结 
   JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。

   1.继承Thread类实现多线程
       继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

例子如下:
   public class MyThread extends Thread {
     public void run() {
      System.out.println(“MyThread.run()”);
     }
   }
   在合适的地方启动线程如下:
   MyThread myThread1 = new MyThread();
   MyThread myThread2 = new MyThread();
   myThread1.start();
   myThread2.start();

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

例子如下:
   public class MyThread extends OtherClass implements Runnable {
     public void run() {
      System.out.println(“MyThread.run()”);
     }
   }
   为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
   MyThread myThread = new MyThread();
   Thread thread = new Thread(myThread);
   thread.start();
   事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),

   参考JDK源代码:
   public void run() {
     if (target != null) {
      target.run();
     }
   }

   3.使用ExecutorService、Callable、Future实现有返回结果的多线程
     ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。想要详细了解Executor框架的可以访问http://www.javaeye.com/topic/366591 ,这里面对该框架做了很详细的解释。返回结果的线程是在JDK1.5中引入的新特征,确实很实用,有了这种特征我就不需要再为了得到返回值而大费周折了,而且即便实现了也可能漏洞百出。可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。

下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:

   import java.util.concurrent.*;
   import java.util.Date;
   import java.util.List;
   import java.util.ArrayList;

  
   @SuppressWarnings(“unchecked”)
   public class Test {
   public static void main(String[] args) throws ExecutionException,InterruptedException {
      System.out.println(“—-程序开始运行—-”);
      Date date1 = new Date();

      int taskSize = 5;
      // 创建一个线程池
      ExecutorService pool = Executors.newFixedThreadPool(taskSize);
      // 创建多个有返回值的任务
      List<Future> list = new ArrayList<Future>();
      for (int i = 0; i < taskSize; i++) {
        Callable c = new MyCallable(i + ” “);
        // 执行任务并获取Future对象
        Future f = pool.submit(c);
        // System.out.println(“>>>” + f.get().toString());
        list.add(f);
      }
      // 关闭线程池
      pool.shutdown();

      // 获取所有并发任务的运行结果
      for (Future f : list) {
        // 从Future对象上获取任务的返回值,并输出到控制台
        System.out.println(“>>>” + f.get().toString());
      }

      Date date2 = new Date();
      System.out.println(“—-程序结束运行—-,程序运行时间【”+ (date2.getTime() – date1.getTime()) + “毫秒】”);
   }
   }

   class MyCallable implements Callable<Object> {
   private String taskNum;

   MyCallable(String taskNum) {
      this.taskNum = taskNum;
   }

   public Object call() throws Exception {
      System.out.println(“>>>” + taskNum + “任务启动”);
      Date dateTmp1 = new Date();
      Thread.sleep(1000);
      Date dateTmp2 = new Date();
      long time = dateTmp2.getTime() – dateTmp1.getTime();
      System.out.println(“>>>” + taskNum + “任务终止”);
      return taskNum + “任务返回运行结果,当前任务时间【” + time + “毫秒】”;
   }
  }
   代码说明:
   上述代码中Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
   public static ExecutorService newFixedThreadPool(int nThreads) 
   创建固定数目线程的线程池。
   public static ExecutorService newCachedThreadPool() 
   创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
   public static ExecutorService newSingleThreadExecutor() 
   创建一个单线程化的Executor。
   public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 
   创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

   ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。