Java琐碎知识点

知识点1:

1 String str1 = "abc";// 没有创建任何对象
2 String str2 = new String("abc");// 创建了2个对象
3 String str3 = new String("abc");// 创建了1个对象
4 System.out.println(str1 == str2);// 输出:false
5 System.out.println(str1 == str3);// 输出:false
6 System.out.println(str2 == str3);// 输出:false

分析:

  1. String str1 = "abc";执行此句时,首先还是在String Pool中查找有没有字符串常量”abc”,有则直接将s1作为String Pool中”abc”的一个引用,因此没有创建任何对象;
  2. String str2 = new String("abc");执行此句时,首先在String Pool(字符常量池)中查找有没有字符常量”abc”,没有则在String Pool中创建”abc”的对象;而当执行new String(“abc”)时则在java的堆中创建一个”abc”对象,而s则是该对象的引用,因此共创建2个对象。
  3. String str3 = new String("abc");执行此句时,依旧在String Pool中查找有没有字符串常量”abc”,有则不进行再次创建,由于这里用了new关键字,所有便在java堆中又创建了一个”abc”对象(地址与上一句在堆中创建的地址不同),而s2则是这个对象的引用,因此执行此句时只创建了1个对象。
  4. 我们知道”==”是判断对象的,因此由于str1指向的则是String Pool中的”abc”对象,而str2指向的是java堆中的”abc”对象,所以输出false。 后两句判断同理。

知识点2:

Java编程如何避免内存溢出?

  1. 尽早释放无用对象的引用(XX = null;)
  2. 谨慎使用集合数据类型,如数组,树,图,链表等数据结构,这些数据结构对GC来说回收更复杂。
  3. 避免显式申请数组空间,不得不显式申请时,尽量准确估计其合理值。
  4. 尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其自类的构造器时造成不必要的内存资源浪费
  5. 尽量避免强制系统做垃圾内存的回收,增长系统做垃圾回收的最终时间
  6. 尽量做远程方法调用类应用开发时使用瞬间值变量,除非远程调用端需要获取该瞬间值变量的值。
  7. 尽量在合适的场景下使用对象池技术以提高系统性能

知识点3:

常用数据结构:

Hashtable:

  1. 继承自:public class Hashtable extends Dictionary implements Map;
  2. Hashtable 中的方法是同步的,即线程安全的,在多线程并发的环境下,可以直接使用Hashtable;
  3. Hashtable中,key和value都不允许出现null值;
  4. 在遍历方式的内部实现上,使用了 Iterator,但由于历史原因,Hashtable还使用了Enumeration的方式;
  5. 在使用哈希值时,HashTable直接使用对象的hashCode;
  6. 关于内部实现方式的数组的初始大小和扩容的方式,HashTable中hash数组默认大小是11,增加的方式是 old*2+1。

HashMap:

  1. 继承自:public class HashMap extends AbstractMap implements Map
  2. HashMap中的方法在缺省情况下是非同步的,即非线程安全的,在多线程并发的环境下,要使用HashMap的话就要自己增加同步处理;
  3. 在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而且HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解,因此应该用containsKey()方法来判断是否存在某个键;
  4. 在遍历方式的内部实现上,使用了 Iterator;
  5. 在使用哈希值时,HashMap重新计算hash值;
  6. 关于内部实现方式的数组的初始大小和扩容的方式,HashMap中hash数组的默认大小是16,而且一定是2的指数。

HashSet:

  1. HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的

?ArrayList:

  1. 基于动态数组的实现;
  2. 有频繁的随机访问操作,使用ArrayList;
  3. ArrayList在内存不够时默认是扩展50% + 1个。

LinkedList:

  1. 基于链表的实现;
  2. 有频繁的新增和删除操作,使用LinkedList;

Vector:

  1. 基于动态数组的实现;
  2. 支持线程的同步,即线程安全,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢;
  3. Vector在内存不够时默认扩展1倍;
  4. Vector提供indexOf(obj, start)方法。

知识点4:

字节流与和字符流:

所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛;在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。

从字节流转化为字符流 = byte[] 转化为String

从字符流转化为字节流 = String 转化为byte[]

对应关系如下:
Reader            InputStream
   ├BufferedReader      BufferedInputStream
   │  └LineNumberReader  LineNumberReader
   ├CharArrayReader     ByteArrayInputStream
   ├InputStreamReader     (none)
   │  └FileReader     FileInputStream
   ├FilterReader       FilterInputStream
   │  └PushbackReader   PushbackInputStream
   ├PipedReader        PipedInputStream
   └StringReader       StringBufferInputStream

Write            OutputStream
  ├BufferedWriter       BufferedOutputStream
  ├CharArrayWriter       ByteArrayOutputStream
  ├OutputStreamWriter     (none)
  │  └FileWriter       FileOutputStream
  ├FilterWriter         FilterOutputStream
  ├PrintWriter         PrintStream
  ├PipedWriter         PipedOutputStream
  └StringWriter         (none)

字节流:

  1. 抽象类 – InputStream和OutputStream;
  2. 具体实现 — 如FileInputStream和FileOutputStream;
  3. 输出时直接操作文件,没有使用缓冲区;
  4. 字节流处理单元为1个字节,操作字节和字节数组;
  5. 如果是音频文件、图片、歌曲,用字节流比较好;
  6. 字节流可用于任何类型的对象,包括二进制对象;
  7. 字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符;

字符流:

  1. 抽象类 Reader和Writer;
  2. 具体实现 — 如FileReader和FileWriter;
  3. 在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据,再从缓存写入文件;
  4. 字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的;
  5. 如果是关系到中文(文本)或支持多国语言的,用字符流比较好;
  6. ?字符流只能处理字符或者字符串;
  7. ?字符流可以直接处理Unicode字符。

例子:使用字节流时,即使不关闭输出流,也不影响输出:

 1 public class FileOutputStreamTest {
 2     public static void main(String[] args) throws Exception {
 3         File filePath = new File("d:" + File.separator + "test.txt");
 4         OutputStream out = new FileOutputStream(filePath);
 5         String str = "Hello World!!!";
 6         byte bytes[] = str.getBytes();// 字符串转byte数组
 7         out.write(bytes);
 8         // 不关闭输出流
 9 //      out.close();
10     }
11 }

例子:使用字符流时,不关闭输出流时,有必要进行强制刷新(flush),否则无法正确写入内容:

 1 public class FileWriterTest {
 2     public static void main(String[] args) throws Exception {
 3         File filePath = new File("d:" + File.separator + "test.txt");
 4         Writer out = new FileWriter(filePath);
 5         String str = "Hello World!!!";
 6         out.write(str);
 7         // 强制性清空缓冲区中的内容
 8 //      out.flush();
 9
10         // 不关闭输出流
11 //      out.close();
12     }
13 }

知识点5:

基本数据类型 大小 最小值 最大值
boolean —– —– ——
char 16-bit Unicode 0 Unicode 2^16-1
byte 8-bit -128 +127
short 16-bit -2^15 +2^15-1
int 32-bit -2^31 +2^31-1
long 64-bit -2^63 +2^63-1
float 32-bit IEEE754 IEEE754
double 64-bit IEEE754 IEEE754
 1 public class BasicTypeTest {
 2     public static void main(String[] args) {
 3         // byte
 4         System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
 5         System.out.println("包装类:java.lang.Byte");
 6         System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
 7         System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
 8         System.out.println();
 9
10         // short
11         System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
12         System.out.println("包装类:java.lang.Short");
13         System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
14         System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
15         System.out.println();
16
17         // int
18         System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
19         System.out.println("包装类:java.lang.Integer");
20         System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
21         System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
22         System.out.println();
23
24         // long
25         System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
26         System.out.println("包装类:java.lang.Long");
27         System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
28         System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
29         System.out.println();
30
31         // float
32         System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
33         System.out.println("包装类:java.lang.Float");
34         System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
35         System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
36         System.out.println();
37
38         // double
39         System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
40         System.out.println("包装类:java.lang.Double");
41         System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
42         System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
43         System.out.println();
44
45         // char
46         System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
47         System.out.println("包装类:java.lang.Character");
48         System.out.println("最小值:Character.MIN_VALUE=" + (int) Character.MIN_VALUE);//以数值形式而不是字符形式输出
49         System.out.println("最大值:Character.MAX_VALUE=" + (int) Character.MAX_VALUE);//以数值形式而不是字符形式输出
50     }
51 }

知识点6:

常见的final类:

String、Math;Integer 、Long、Character 等包装类。

需要注意的是:

  • 对于final修饰的基本类型和String类型,编译器会认为它是稳定态(Immutable Status),所以在编译时就直接把值编译到字节码中了,避免了在运行期引用(Run-time Reference),以提高代码的执行效率;
    因此,单纯的修改静态变量是没用的,还要重新编译,不然改动不会生效。
  • 对于非final修饰的类(即非基本类型),编译器认为它是不稳定态(Mutable Status),在编译时建立的则是引用关系(该类型也叫做Soft Final);
    因此,如果引入的常量是一个类或实例,即使不重新编译也能使改动生效。

知识点7:

按特定的时间格式获取其毫秒数:

1 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
2 Date startDate = sdf.parse("2014-07-11 00:00:00");
3 Date endDate = sdf.parse("2014-07-11 23:59:59");
4 long startTime = startDate.getTime();//单位:毫秒
5 long endTime = endDate.getTime();//单位:毫秒

知识点8:

Java常见的两种解析XML的方式:

DOM-基于文档树结构的解析
例子:

 1 private static void readXmlByDOM(String filePath)throws ParserConfigurationException, SAXException, IOException {
 2     File file = new File(filePath);
 3     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();// step
 4                                                                         // 1:获得DOM解析器工厂,作用是创建具体的解析器
 5     DocumentBuilder db = dbf.newDocumentBuilder();// step 2:获得具体的dom解析器
 6     Document document = db.parse(file);// step 3:解析一个xml文档,获得Document对象(根节点)
 7     // 根据标签名字访问节点
 8     NodeList list = document.getElementsByTagName("test");
 9     System.out.println("list length: " + list.getLength());
10     // 遍历每一个节点
11     for (int i = 0; i < list.getLength(); ++i) {
12         System.out.println("----------------------");
13         Element element = (Element) list.item(i);
14         String test = element.getElementsByTagName("name").item(0)
15                 .getNodeValue();
16         System.out.println(test);// 此处输出:null
17         // 节点getNodeValue的值永远为null
18         // 解决方法:加上getFirstChild()
19         Node node1 = element.getElementsByTagName("name").item(0)
20                 .getFirstChild();
21         String content = node1.getNodeValue();
22         System.out.println("name: " + content);
23
24         Node node2 = element.getElementsByTagName("sex").item(0)
25                 .getFirstChild();
26         content = node2.getNodeValue();
27         System.out.println("author: " + content);
28
29         Node node3 = element.getElementsByTagName("age").item(0)
30                 .getFirstChild();
31         content = node3.getNodeValue();
32         System.out.println("year: " + content);
33     }
34 }

SAX-基于事件流的解析
例子:

 1 // 需导入dom4j包
 2 private static void readXmlBySAX(String filePath) throws DocumentException {
 3     SAXReader reader = new SAXReader();
 4     File file = new File(filePath);
 5     Document doc = (Document) reader.read(file);
 6     Element root = doc.getRootElement();
 7     Iterator iterator = root.elementIterator("test");
 8     while (iterator.hasNext()) {
 9         Element element = (Element) iterator.next();
10         String name = element.elementText("name");
11         String sex = element.elementText("sex");
12         Integer age = Integer.parseInt(element.elementText("age"));
13         System.out.println("----------------------");
14         System.out.println("name: " + name);
15         System.out.println("sex: " + sex);
16         System.out.println("age: " + age);
17     }
18 }

知识点9:

Java的算术右移">>"和逻辑右移">>>":

  1. 算术右移">>"
    使用最高位填充移位后左侧的空位,不改变原数的符号;
    右移的结果为:每移一位,第一个操作数被2除一次,移动的次数由第二个操作数确定。
  2. 逻辑右移">>>"
    或叫无符号右移,只对位进行操作,没有算术含义,用0填充左侧的空位;
    无法保证不改变原数的符号。

例子:

 1 System.out.println("=============算术右移 >> ===========");
 2 int i = 0xC0000000;
 3 System.out.println("移位前:i= " + i + " = " + Integer.toBinaryString(i) + "(B)");
 4 i = i >> 28;
 5 System.out.println("移位后:i= " + i + " = " + Integer.toBinaryString(i) + "(B)");
 6
 7 System.out.println("---------------------------------");
 8 int j = 0x0C000000;
 9 System.out.println("移位前:j= " + j + " = " + Integer.toBinaryString(j) + "(B)");
10 j = j >> 24;
11 System.out.println("移位后:j= " + j + " = " + Integer.toBinaryString(j) + "(B)");
12
13 System.out.println("==============逻辑右移 >>> =============");
14 int m = 0xC0000000;
15 System.out.println("移位前:m= " + m + " = " + Integer.toBinaryString(m) + "(B)");
16 m = m >>> 28;
17 System.out.println("移位后:m= " + m + " = " + Integer.toBinaryString(m) + "(B)");
18
19 System.out.println("---------------------------------");
20 int n = 0x0C000000;
21 System.out.println("移位前:n= " + n + " = " + Integer.toBinaryString(n) + "(B)");
22 n = n >> 24;
23 System.out.println("移位后:n= " + n + " = " + Integer.toBinaryString(n) + "(B)");
24
25 System.out.println("\n");
26 System.out.println("==============移位符号的取模===============");
27 int a = 0xCC000000;
28 System.out.println("移位前:a= " + a + " = " + Integer.toBinaryString(a) + "(B)");
29 int a1 = a >> 32;
30 int a2 = a >>> 32;
31 System.out.println("算术右移32位:a1=" + a1 + " = " + Integer.toBinaryString(a1) + "(B)");
32 System.out.println("逻辑右移32位:a2=" + a2 + " = " + Integer.toBinaryString(a2) + "(B)");

知识点10:

关于Java的eqauls与= =:

= = 是面向过程的操作符;equals是面向对象的操作符
= =不属于任何类,equals则是任何类(在Java中)都实现(或继承)了的一个方法;

因此,
如果要比较两个基本类型的值是否相等,请用= =;//注:基本类型无equals方法可调用
如果要比较两个对象的地址是不是一样,请用= =;
如果要比较两个对象(非基本类型)的值是否相等,请用equals;


知识点11:

特殊字符及其转义:

1.八进制转义序列:
\ + 1到3位5数字;范围‘\000‘~‘\377‘
\0:空字符

2.Unicode转义字符:
\u + 四个十六进制数字;0~65535
\u0000:空字符

3.特殊字符:3个
\":双引号
\‘:单引号
\\:反斜线

4.控制字符:5个
\‘ 单引号字符
\\ 反斜杠字符
\r 回车
\n 换行
\f 走纸换页
\t 横向跳格
\b 退格

点的转义:. ==> u002E
美元符号的转义:$ ==> u0024
乘方符号的转义:^ ==> u005E
左大括号的转义:{ ==> u007B
左方括号的转义:[ ==> u005B
左圆括号的转义:( ==> u0028
竖线的转义:| ==> u007C
右圆括号的转义:) ==> u0029
星号的转义:* ==> u002A
加号的转义:+ ==> u002B
问号的转义:? ==> u003F
反斜杠的转义: ==> u005C


知识点12:

对象锁:

  • 所有对象都自动含有单一的锁;
  • JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增;
  • 只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁;
  • 一个线程可以多次对同一个对象上锁。对于每一个对象,都有一个独自的加锁计数器交由JVM维护,线程每获得一次该对象,计数器就加1,每释放一次,计数器就减 1,当计数器值为0时,锁就被完全释放了,此时别的任务就可以使用此资源。

类锁:

  • 对于同步静态方法/静态变量互斥体,由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只由一份。
    所以,一旦一个静态的方法被申明为synchronized,此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。
  • 一旦一个静态变量被作为synchronized block的互斥体。进入此同步区域时,都要先获得此静态变量的对象锁;
  • 其实系统中并不存在什么类锁。当虚拟机装载一个class文件的时候,它就会创建一个java.lang.Class类的实例。当锁住一个对象的时候,实际上锁住的是那个类的Class对象。
    因此,当一个同步静态方法被调用时,系统获取的其实就是代表该类的类对象的对象锁。

若要同时获取两种锁,同时获取类锁和对象锁是允许的,并不会产生任何问题,
但使用类锁时一定要注意,一旦产生类锁的嵌套获取的话,就会产生死锁,因为每个class在内存中都只能生成一个Class实例对象。



Java琐碎知识点

时间: 2024-10-03 14:15:03

Java琐碎知识点的相关文章

Thinking in java 琐碎知识点之 I/O流 、对象序列化

Java I/O流 .对象序列化 1.File类 此类的实例可能表示(也可能不表示)实际文件系统对象,如文件或目录. File类可以新建.删除和重命名文件和目录,但是File不能访问文件本身的内容,这要使用IO流. File对象的createNewFile()方法在磁盘上创建真实的文件 例程:FileTest.java import java.io.*; public class FileTest { public static void main(String[] args) throws I

Tinking in java 琐碎知识点之反射

刚开始工作的这段时间,使用公司的成熟的开发框架,感觉越用越害怕.框架是提高了开发效率,但是也使得自己 对基础知识越来越陌生,基本都要忘光了.所以,为了告别这种心理上的害怕,最近开始学习Spring. 公司的开发框架是基于SpringMVC和hibernate封装而成,很多框架的细节的也都被屏蔽了,所以,自己连spring都 不会使用,这个很危险.所以,我自己也先从spring开始学习.曾经大体学过一点点spring,现在再回来学习,发现更 容易理解了,特别是IOC的概念,也可能是在使用公司的框架

Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过start方法启动线程--->线程变为可运行可执行状态,然后通过数据产生共享,线程产生互斥---->线程状态变为阻塞状态---->阻塞状态想打开的话可以调用notify方法. 这里Java5中提供了封装好的类,可以直接调用然后构造阻塞状态,以保证数据的原子性. 2.如何实现? 主要是实现Blo

Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger

本文将要介绍的内容都是Java5中的新特性,一个是倒计时记数器---CountDownLatch,另一个是用于线程间数据交换的Exchanger. 一.CountDownLatch 1.什么是CountDownLatch? 倒计时计数器,调用CountDownLatch对象的CountDown()方法就将计数器减一,当计数到达0时,则所有等待者或者全部等待者开始执行. 2.如何用? new CountDownLatch(1); 直接new,其构造函数必须传一个int类型的参数,参数的意思是: c

Scala琐碎知识点

tuple scala中的tuple数据结构,是用来包含不同数据类型的容器,定义如下: 访问tuple中的元素,需要使用._n的语法,索引从1开始: scala中的tuple的长度最多为22,超过22报错: 上述定义scala中的tuple的方式是下面这种方式的缩写: scala中的cons(:: 和 :::  ) 首先要知道在Scala中Nil代表的是空的列表List cons定义: x :: xs 如下就是把一个Int类型添加到Int类型的List中 ::: 这个运算符在scala中表示的是

JAVA基础知识点总结(全集)

1.JAVA简介 1.1java体系结构:j2se,javaweb,j2ee 1.2java特点:平台无关(虚拟机),垃圾回收(使得java更加稳定) 1.3 JDK与JRE,JDK:java开发环境,JRE:java运行环境 1.4第一个java程序:HelloWorld,java的入口是main(public static void main(String[] args)) 1.5java程序的开发步骤:.java编译(javac)成.class运行(java).class文件 2.基本数据

初学Java 精简知识点总结

面对Java丰富的知识资料,很多初学者难免觉得迷惘,该学什么,怎么去学?下面给大家讲Java基础知识做了精简总结,来帮助你梳理学习思路,赶快看看吧! 方法/步骤1 对象的初始化(1) 非静态对象的初始化在创建对象时,对象所在类的所有数据成员会首先进行初始化.基本类型:int型,初始化为0.如果为对象:这些对象会按顺序初始化.※在所有类成员初始化完成之后,才调用本类的构造方法创建对象.构造方法的作用就是初始化.(2) 静态对象的初始化程序中主类的静态变量会在main方法执行前初始化.不仅第一次创建

Java入门知识点:

1.跨平台性主要原理是:在需要运行的java应用程序的操作系统上安装了一个对应操作系统对应版本的JVM(Java Virtual Machine)java虚拟机即可,由JVM来负责Java程序的在该系统中执行. JVM分为Windows版本JVM,linux版本JVM,Mac版本JVM,因为有了JVM,所以同一个程序可以在三个不同的操作系统中都可以执行. 2.JVM,Jre,JDK的区别 JRE(Java Runtime Environment):Java运行环境 包括Java虚拟机(JVM)和

Java 多线程知识点整理

1.如何停止一个正在运行的线程? 使用共享变量,用volatile变量修饰,线程循环去访问该变量.如果线程因为发生某些事件进行阻塞,可以使用Thread的interrupt方法.可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码. class MyThread extends Thread { volatile boolean stop = false; public void run() { while (!stop) { System.out.println(g