﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-JavaCode--我爱你,芳儿-随笔分类-JavaBaseFiles</title><link>http://www.blogjava.net/yjjlovewjf/category/27837.html</link><description>JavaStudy--我爱你,芳儿</description><language>zh-cn</language><lastBuildDate>Thu, 03 Jan 2008 20:26:35 GMT</lastBuildDate><pubDate>Thu, 03 Jan 2008 20:26:35 GMT</pubDate><ttl>60</ttl><item><title>Java中的易混问题收集 --转</title><link>http://www.blogjava.net/yjjlovewjf/archive/2008/01/03/172609.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Thu, 03 Jan 2008 15:40:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2008/01/03/172609.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/172609.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2008/01/03/172609.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/172609.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/172609.html</trackback:ping><description><![CDATA[<br />
<font color="red">第一，final, finally, finalize的区别.</font><br />
<br />
final 修饰符（关键字）如果一个类被声明为final，意味着它不能再派生出新的子类，不能作为父类被继承。因此一个类不能既被声明为 abstract的，又被声明为final的。将变量或方法声明为final，<strong></strong>可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同样只能使用，不能重载 <br />
<br />
finally 再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常，那么相匹配的 catch 子句就会执行，然后控制就会进入 finally 块（如果有的话）。 <br />
finalize 方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的，因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。 <br />
<br />
<font color="red">第二，Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否可以implements(实现)interface(接口)? </font><br />
<br />
匿名的内部类是没有名字的内部类。不能extends(继承) 其它类，但一个内部类可以作为一个接口，由另一个内部类实现。 <br />
<br />
<font color="red">第三，Static Nested Class 和 Inner Class的不同，说得越多越好</font><br />
Nested Class （一般是C++的说法），Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http: //www.frontfree.net/articles/services/view.asp?id=704&amp;page=1 <br />
注： 静态内部类（Inner Class）意味着1创建一个static内部类的对象，不需要一个外部类对象，2不能从一个static内部类的一个对象访问一个外部类对象 <br />
<br />
<font color="red">第四，&amp;和&amp;&amp;的区别。</font><br />
<br />
&amp;是位运算符。&amp;&amp;是布尔逻辑运算符。 <br />
<br />
<font color="red">第五，HashMap和Hashtable的区别。</font><br />
<br />
都属于Map接口的类，实现了将惟一键映射到特定的值上。 <br />
HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 <br />
Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，因为它是同步的。 <br />
<br />
<font color="red">第六，Collection 和 Collections的区别。</font> <br />
Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。 <br />
Collection是个java.util下的接口，它是各种集合结构的父接口。 <br />
<br />
<font color="red">第七，什么时候用assert。 </font><br />
断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 true。如果表达式计算为 false，那么系统会报告一个 Assertionerror。它用于调试目的： <br />
assert(a &gt; 0); // throws an Assertionerror if a &lt;= 0 <br />
断言可以有两种形式： <br />
assert Expression1 ; <br />
assert Expression1 : Expression2 ; <br />
Expression1 应该总是产生一个布尔值。 <br />
Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的 String 消息。 <br />
断言在默认情况下是禁用的。要在编译时启用断言，需要使用 source 1.4 标记： <br />
javac -source 1.4 Test.java <br />
要在运行时启用断言，可使用 -enableassertions 或者 -ea 标记。 <br />
要在运行时选择禁用断言，可使用 -da 或者 -disableassertions 标记。 <br />
要系统类中启用断言，可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。 <br />
可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过，断言不应该用于验证传递给公有方法的参数，因为不管是否启用了断言，公有方法都必须检查其参数。不过，既可以在公有方法中，也可以在非公有方法中利用断言测试后置条件。另外，断言不应该以任何方式改变程序的状态。 <br />
<br />
<font color="red">第八，GC是什么? 为什么要有GC? (基础)。</font><br />
<br />
GC是垃圾收集器。Java 程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一： <br />
System.gc() <br />
Runtime.getRuntime().gc() <br />
<br />
<font color="red">第九，String s = new String("xyz");创建了几个String Object? </font><br />
<br />
两个对象，一个是&#8220;xyx&#8221;,一个是指向&#8220;xyx&#8221;的引用对象s。 <br />
<br />
<font color="red">第十，Math.round(11.5)等於多少? Math.round(-11.5)等於多少? </font><br />
<br />
Math.round(11.5)返回（long）12，Math.round(-11.5)返回（long）-11; <br />
<br />
<font color="red">第十一，short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错? </font><br />
<br />
short s1 = 1; s1 = s1 + 1;有错，s1是short型，s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。 <br />
<br />
<font color="red">第十二，sleep() 和 wait() 有什么区别? 搞线程的最爱 </font><br />
sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后，线程不一定立即恢复执行。这是因为在那个时刻，其它线程可能正在运行而且没有被调度为放弃执行，除非(a)&#8220;醒来&#8221;的线程具有更高的优先级 <br />
(b)正在运行的线程因为其它原因而阻塞。 <br />
wait()是线程交互时，如果线程对一个同步对象x 发出一个wait()调用，该线程会暂停执行，被调对象进入等待状态，直到被唤醒或等待时间到。 <br />
<br />
<font color="red">第十三，Java有没有goto? </font><br />
Goto java中的保留字，现在没有在java中使用。 <br />
<br />
<font color="red">第十四，数组有没有length()这个方法? String有没有length()这个方法？</font> <br />
<br />
数组没有length()这个方法，有length的属性。 <br />
String有有length()这个方法。 <br />
<br />
<font color="red">第十五，Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? </font><br />
<br />
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现，重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写 (Overriding)。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被&#8220;屏蔽&#8221;了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同的参数类型，则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 <br />
<br />
<font color="red">第十六，Set里的元素是不能重复的，那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? </font><br />
<br />
Set里的元素是不能重复的，那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 <br />
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖，为的是当两个分离的对象的内容和类型相配的话，返回真值。 <br />
<br />
<font color="red">第十七，给我一个你最常见到的runtime exception。</font> <br />
ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, <br />
ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException&nbsp; <a href="http://www.pass4sure.com/190-802.html">190-802</a>&nbsp;<a href="http://www.pass4sure.com/000-834.html">000-834</a>&nbsp;<a href="http://www.pass4sure.com/000-861.html">000-861</a>&nbsp;<a href="http://www.pass4sure.com/117-102.html">117-102</a>&nbsp;<a href="http://www.pass4sure.com/117-301.html">117-301</a>&nbsp;<a href="http://www.pass4sure.com/190-721.html">190-721</a><br />
<br />
<font color="red">第十八，error和exception有什么区别? </font><br />
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 <br />
exception 表示一种设计或实现问题。也就是说，它表示如果程序运行正常，从不会发生的情况。 <br />
<br />
<font color="red">第十九，List, Set, Map是否继承自Collection接口? </font><br />
List，Set是 <br />
<br />
Map不是 <br />
<br />
<font color="red">第二十，abstract class和interface有什么区别? </font><br />
<br />
声明方法的存在而不去实现它的类被叫做抽象类（abstract class），它用于要创建一个体现某些基本行为的类，并为该类声明方法，但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量，其类型是一个抽象类，并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现，否则它们也是抽象类为。取而代之，在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 <br />
接口（interface）是抽象类的变体。在接口中，所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的，没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似，除了该实现类不能从接口定义中继承行为。当类实现特殊接口时，它定义（即将程序体给予）所有这种接口的方法。然后，它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类，它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换，instanceof 运算符可以用来决定某对象的类是否实现了接口。 <br />
<br />
<font color="red">第二十一，abstract的method是否可同时是static,是否可同时是native，是否可同时是synchronized? </font><br />
<br />
都不能 <br />
<br />
<font color="red">第二十二，接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? </font><br />
接口可以继承接口。抽象类可以实现(implements)接口，抽象类是否可继承实体类，但前提是实体类必须有明确的构造函数。 <br />
<br />
<font color="red">第二十三，启动一个线程是用run()还是start()? </font><br />
启动一个线程是调用start()方法，使线程所代表的虚拟处理机处于可运行状态，这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 <br />
<br />
<font color="red">第二十四，构造器Constructor是否可被override? </font>构造器Constructor不能被继承，因此不能重写Overriding，但可以被重载Overloading。 <br />
<br />
<font color="red">第二十五，是否可以继承String类? </font><br />
String类是final类故不可以继承。 <br />
<br />
<font color="red">第二十六，当一个线程进入一个对象的一个synchronized方法后，其它线程是否可进入此对象的其它方法? </font><br />
不能，一个对象的一个synchronized方法只能由一个线程访问。 <br />
<br />
<font color="red">第二十七，try {}里有一个return语句，那么紧跟在这个try后的finally {}里的code会不会被执行，什么时候被执行，在return前还是后? </font><br />
<br />
会执行，在return前执行。 <br />
<br />
<font color="red">第二十八，编程题: 用最有效率的方法算出2乘以8等於几? </font><br />
<br />
有C背景的程序员特别喜欢问这种问题。 <br />
<br />
2 &lt;&lt; 3 <br />
<br />
<font color="red">第二十九，两个对象值相同(x.equals(y) == true)，但却可有不同的hash code，这句话对不对? </font><br />
不对，有相同的hash code。 <br />
<br />
<font color="red">第三十，当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递? </font><br />
<br />
是值传递。Java 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时，参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变，但对象的引用是永远不会改变的。 <br />
<br />
<font color="red">第三十一，swtich是否能作用在byte上，是否能作用在long上，是否能作用在String上? </font><br />
<br />
switch（expr1）中，expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。 <br />
<br />
<font color="red">第三十二，编程题: 写一个Singleton出来。</font><br />
<br />
Singleton模式主要作用是保证在Java应用程序中，一个类Class只有一个实例存在。 <br />
一般Singleton模式通常有几种种形式: <br />
第一种形式: 定义一个类，它的构造函数为private的，它有一个static的private的该类变量，在类初始化时实例话，通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 <br />
public class Singleton { <br />
　　private Singleton(){} <br />
　　//在自己内部定义自己一个实例，是不是很奇怪？ <br />
　　//注意这是private 只供内部调用 <br />
　　private static Singleton instance = new Singleton(); <br />
　　//这里提供了一个供外部访问本class的静态方法，可以直接访问　　 <br />
　　public static Singleton getInstance() { <br />
　　　　return instance; 　　 <br />
　　 } <br />
} <br />
第二种形式: <br />
public class Singleton { <br />
　　private static Singleton instance = null; <br />
　　public static synchronized Singleton getInstance() { <br />
　　//这个方法比上面有所改进，不用每次都进行生成对象，只是第一次　　　 　 <br />
　　//使用时生成实例，提高了效率！ <br />
　　if (instance==null) <br />
　　　　instance＝new Singleton(); <br />
return instance; 　　} <br />
} <br />
其他形式: <br />
定义一个类，它的构造函数为private的，所有方法为static的。 <br />
一般认为第一种形式要更加安全些 <br />
<br />
<font color="red">第三十三 Hashtable和HashMap </font><br />
Hashtable继承自Dictionary类，而HashMap是Java1.2引进的Map interface的一个实现 <br />
<br />
HashMap允许将null作为一个entry的key或者value，而Hashtable不允许 <br />
<br />
还有就是，HashMap把Hashtable的contains方法去掉了，改成containsvalue和containsKey。因为contains方法容易让人引起误<strong></strong>解。 <br />
<br />
最大的不同是，Hashtable的方法是Synchronize的，而HashMap不是，在 <br />
多个线程访问Hashtable时，不需要自己为它的方法实现同步，而HashMap <br />
就必须为之提供外同步。 <br />
<br />
Hashtable和HashMap采用的hash/rehash算法都大概一样，所以性能不会有很大的差异。
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/172609.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2008-01-03 23:40 <a href="http://www.blogjava.net/yjjlovewjf/archive/2008/01/03/172609.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于static变量对于内存分配的实际验证...</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/20/169151.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Thu, 20 Dec 2007 13:07:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/20/169151.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/169151.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/20/169151.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/169151.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/169151.html</trackback:ping><description><![CDATA[<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Num<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;Integer&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(</span><span style="color: #000000">520</span><span style="color: #000000">);<br />
};</span></div>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Demo<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Num&nbsp;demo1&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Num();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Num&nbsp;demo2&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Num();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(demo1.i&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;demo2.i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">ture</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">false</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
</span></div>
输出为:<span style="color: red">true</span>!表明demo1.i和demo2.i只有一分存储空间.虽然new了两个对象.但只有一份存储空间!<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Num<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;Integer&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(</span><span style="color: #000000">520</span><span style="color: #000000">);<br />
};</span></div>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #000000"><br />
</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Demo<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Num&nbsp;demo1&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Num();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Num&nbsp;demo2&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Num();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(demo1.i&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;demo2.i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">ture</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">false</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
</span></div>
输出为false<br />
关于static变量或方法..只会创建一份空间..无论是否有对象去引用..<br />
下面是更深入的说明!!!<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Num<br />
{<br />
&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;Integer&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(</span><span style="color: #000000">520</span><span style="color: #000000">);<br />
&nbsp;Integer&nbsp;j&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(</span><span style="color: #000000">520</span><span style="color: #000000">);<br />
}</span></div>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #000000"><br />
</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Demo<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Num&nbsp;demo1&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Num();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Num&nbsp;demo2&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Num();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(demo1.i&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;demo2.i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">ture</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">false</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(demo1.j&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;demo2.j)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">ture</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">false</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(Num.i);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
<br />
</span></div>
下面一个例子:<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;F1<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(</span><span style="color: #000000">10</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer&nbsp;j&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Integer(</span><span style="color: #000000">10</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;k&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">20</span><span style="color: #000000">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;l&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">20</span><span style="color: #000000">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(k&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">l);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(i&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;j);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
};</span></div>
上面的例 子表明:对于通过new创建的两个对象的引用i&amp;j,他们所引用的值都相同为10.但是,两个10存储在不同的两个地方,两个引用不同哦.....<br />
上面例 子的结果为:<br />
ture<br />
false<br />
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/169151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-20 21:07 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/20/169151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java初学者容易混淆的几个问题详细解析 --转</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166449.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sun, 09 Dec 2007 07:16:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166449.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166449.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166449.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166449.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166449.html</trackback:ping><description><![CDATA[<div class="postText">１.String类和StringBuffer类 <br />
<br />
<br />
它们都是处理字符串的类,但是它们有一个最大的区别,那就是,String对象是存储你不能改动的文本字符串,相反,如果你希望改动,则应使用StringBuffer类作为替换. <br />
<br />
<br />
eg1: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 String s1="You are hired!"; <br />
<br />
　　 System.out.println(s1.replace(h,f));//用f把字串中的h替换了 <br />
<br />
　　 System.out.println(s1); <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 运行结果: <br />
<br />
　　 You are fired! <br />
<br />
　　 You are hired! <br />
　　 结果分析: <br />
<br />
　　 从结果,明显可知,s1的值并没有被改变,而第一行结果只是屏幕内容的替换. <br />
<br />
　　 eg2: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 StringBuffer s2=new StringBuffer("Hello from Java!"); <br />
<br />
　　 s2.replace(6,10,"to"); <br />
<br />
　　 System.out.println(s2); <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code<br />
<br />
<br />
　　 <br />
<br />
运行结果: <br />
<br />
Hello to Java! <br />
<br />
结果分析: <br />
<br />
显然,s2的值已改变. <br />
<br />
<br />
２.位逻辑与条件逻辑 <br />
<br />
<br />
首先声明, 为了与位逻辑更好区分开来,我把通常所说的逻辑取了个别名叫做条件逻辑. <br />
<br />
<br />
它们都有各自的操作符,位逻辑操作符有:&amp;(与运算),^(异或运算),|(或运算);条件逻辑操作符有:&amp;&amp;(并且),||(或者). <br />
<br />
位逻辑运算通常是针对两个数而言,实行位操作;而条件逻辑运算是针对两个条件表达式而言,实行条件操作.其实,位逻辑操作符一样可以实现条件操作,但是此时有一个重要的区别:用位操作符时,不管操作符两边的 <br />
<br />
条件表达式成不成立,它都要通通进行运算判断,而条件逻辑操作符不一样了,如果通过左侧的操作数就可以进行它们需要的判断,那么它就不会再计算右侧的操作数了,这种情况叫短路.废话少说!且看下例. <br />
<br />
<br />
eg1: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 double value=0; <br />
<br />
　　 if(value!=0 &amp;&amp; 1/value&lt;1000){ <br />
<br />
　　 System.out.println("The value is not too small."); <br />
<br />
　　 } <br />
<br />
　　 else{ <br />
<br />
　　 System.out.println("The value is too small."); <br />
<br />
　　 } <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code<br />
<br />
<br />
　　 <br />
<br />
运行结果: <br />
<br />
The value is too small. <br />
<br />
结果分析: <br />
<br />
照理说应会出现除数为0的错误,但是我刚才说了,由于条件逻辑操作符是短路操作符,显然,value!=0不成立,立即就可作出判断应执行else后的语句,所以它就不再会运算判断1/value&lt;1000了.如果不懂请再看一例: <br />
<br />
<br />
eg2: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 double int1=0,int2=1,int3=1; <br />
<br />
　　 if(int1!=0 &amp; (int2=2)==1){} <br />
<br />
　　 System.out.println("int2="+int2); <br />
<br />
　　 if(int1!=0 &amp;&amp; (int3=2)==1){} <br />
<br />
　　 System.out.println("int3="+int3); <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code<br />
<br />
<br />
运行结果: <br />
<br />
int2=2.0 <br />
<br />
int3=1.0 <br />
<br />
结果分析: <br />
<br />
我想不用我分析了,你应该懂了吧.<br />
<br />
<br />
<br />
<br />
３.实例变量与类变量 <br />
<br />
可以通过两种方法在类中存储数据───作为实例变量和类变量.实例变量是特定于对象的,如果你有两个对象(即一个类的两个实例),每一个对象中的实例变量独立于另一个对象中的实例变量的;另一方面,两个对象的类变量均指向相同的数据,并因此面保存相同的值,换句话说,类变量被类中的所有对象共享.差点忘了,它们在形式上的区别,类变量在声明时比实例变量多一个static. <br />
<br />
<br />
eg: <br />
<br />
　　 class data <br />
<br />
　　 public int intdata=0;//显然,intdata在这儿是实例变量 <br />
<br />
　　 } <br />
<br />
　　 public class exam <br />
<br />
　　 { <br />
<br />
　　 public static void main(String[] args) <br />
<br />
　　 { <br />
<br />
　　 data a,b; <br />
<br />
　　 a=new data(); <br />
<br />
　　 b=new data(); <br />
<br />
　　 a.intdata=1; <br />
<br />
　　 System.out.println("b.indata="+b.intdata); <br />
<br />
　　 } <br />
<br />
　　 }<br />
<br />
<br />
运行结果: <br />
<br />
b.intdata=0 <br />
<br />
结果分析: <br />
<br />
可以看出,a.intdata的值虽然变了,但并没有影响b.intdata.但是如果在data类中声明intdata时,在其前面加上static就变成类变量了(即:public static int intdata=0;),则此时运行结果会变为: <br />
<br />
b.intdata=1 <br />
<br />
这次a.intdata值的改变可把b.intdata影响了,事实上,对象a和b的类变量均指向相同的数据,所有值一样,这就是类变量的作用. <br />
<br />
<br />
４.实例方法,类方法,构造器方法 <br />
<br />
<br />
我们通常所说的方法系指实例方法,就像c语言中的函数一样,其具体方法我就不用说了,在这里我主要是用它来区分类方法和构造器方法.类方法与实例方法最大的区别是:在形式上类方法多一个static,在用法上,不必创建对象就可直接调用类方法(而实例方法却一定要先创建对象,再通过对象调用). <br />
<br />
<br />
eg: <br />
<br />
　　 class add <br />
<br />
　　 { <br />
<br />
　　 static int addem(int op1,int op2) <br />
<br />
　　 { <br />
　　 return op1+op2; <br />
<br />
　　 } <br />
<br />
　　 } <br />
<br />
　　 public class xxf <br />
<br />
　　 { <br />
<br />
　　 public static void main(String[] args) <br />
<br />
　　 { <br />
<br />
　　 System.out.println("addem(2,2)="+add.addem(2,2)); <br />
<br />
　　 } //直接用类名作为对象调用类方法 <br />
<br />
　　 }<br />
<br />
<br />
　　 <br />
<br />
注: 也可按通常的方法,即先创建对象,再调用方法,不过,这时static就无任何意义了. <br />
<br />
<br />
再说说构造器方法,它是用来初始化对象中的数据的一种方法,创建很容易,只需在类中加上一个与这个类同名的方法,不需要在前面加任何访问说明符或者返回类型,另外,构造器也一样可以向方法一样传递参数. <br />
<br />
<br />
eg: <br />
<br />
　 class data <br />
<br />
　　 { <br />
<br />
　　 private String data1;//事先声明 <br />
　　 data(String s) <br />
<br />
　　 { <br />
<br />
　　 data1=s; /*通过接收数据来初始化变量.(注:不能在构造器内 <br />
<br />
　　 声明变量,事先在外就要声明.)*/ <br />
<br />
　　 } <br />
<br />
　　 public String getdata() <br />
<br />
　　 { <br />
<br />
　　 return data1; <br />
<br />
　　 } <br />
<br />
　　 } <br />
<br />
　　 public class xxf <br />
<br />
　　 { <br />
<br />
　　 public static void main(String[] args) <br />
<br />
　　 { <br />
<br />
　　 System.out.println((new data("I love you")).getdata());<br />
/*通过传递参数调用构造器新建一个对象,再通过对象调用方法得到数据*/ <br />
<br />
　　 } <br />
<br />
　　 }<br />
<br />
<br />
５.接口与类 <br />
<br />
类是对一类特定对象的规格说明,我们可以类定义创建对象,通过创建对象来组合所有属于该类的组件,而接口不能这样做.而接口实质上就是一个常量和抽象方法的集合,要使用一个接口,就需要在类中实现这个接口,然后作为类定义的一部分,编写接口中声明的每一个方法,接口中的方法永远是public,abstract,接口中的常量永远是public static和final,因此不需要为它们说明属性.因为在Java中不支持多重继承,但是,可以用接口来实现类似的功能,这是接口的重要作用之一. <br />
<br />
<br />
eg: <br />
<br />
　　 interface anyone //定义一个接口 <br />
<br />
　　 { <br />
<br />
　　 final double PI=3.1416; <br />
<br />
　　 void setNumber(int number); <br />
<br />
　　 int getNumber(); <br />
<br />
　　 } <br />
<br />
　　 interface anyother //定义另一个接口 <br />
<br />
　　 { <br />
<br />
　　 void setString(String str); <br />
<br />
　　 String getString(); <br />
<br />
　　 } <br />
<br />
　　 class xxf implement anyone,anyother //定义一个类,并使用两个接口 <br />
<br />
　　 { <br />
<br />
　　 int number; <br />
<br />
　　 String str; <br />
<br />
　　 public xxf(){} <br />
<br />
　　 void setNumber(int number) <br />
<br />
　　 { <br />
<br />
　　 this.number=number; <br />
<br />
　　 } <br />
<br />
　　 void setString(String str) <br />
<br />
　　 { <br />
<br />
　　 this.str=str; <br />
<br />
　　 } <br />
<br />
　　 void int getNumber(){}//可以为一个空实现. <br />
<br />
　　 void String getString(){} <br />
<br />
　　 }<br />
<br />
<br />
//在类中必须实现接口中声明的所有方法.(当然也可不必,但是要用到适配器类或用抽象类)<br />
</div>
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166449.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-09 15:16 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166449.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>this与super的应用 --转</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166442.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sun, 09 Dec 2007 06:50:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166442.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166442.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166442.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166442.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166442.html</trackback:ping><description><![CDATA[<div class="postText">通过用static来定义方法或成员，为我们编程提供了某种便利，从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是，并不是说有了这种便利，你便可以随处使用，如果那样的话，你便需要认真考虑一下自己是否在用面向对象的思想编程，自己的程序是否是面向对象的。好了，现在开始讨论this&amp;super这两个关键字的意义和用法。<br />
　　在Java中，this通常指当前对象，super则指父类的。当你想要引用当前对象的某种东西，比如当前对象的某个方法，或当前对象的某个成员，你便可以利用this来实现这个目的，当然，this的另一个用途是调用当前对象的另一个构造函数，这些马上就要讨论。如果你想引用父类的某种东西，则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系，所以我们在这一块儿来讨论，希望能帮助你区分和掌握它们两个。<br />
　　在一般方法中<br />
　　最普遍的情况就是，在你的方法中的某个形参名与当前对象的某个成员有相同的名字，这时为了不至于混淆，你便需要明确使用this关键字来指明你要使用某个成员，使用方法是&#8220;this.成员名&#8221;，而不带this的那个便是形参。另外，还可以用&#8220;this.方法名&#8221;来引用当前对象的某个方法，但这时this就不是必须的了，你可以直接用方法名来访问那个方法，编译器会知道你要调用的是那一个。下面的代码演示了上面的用法：<br />
public class DemoThis<br />
{　<br />
private String name;　<br />
private int age;　<br />
DemoThis(String name,int age){　　<br />
setName(name); <br />
//你可以加上this来调用方法，像这样：this.setName(name);但这并不是必须的　　<br />
setAge(age);　　<br />
this.print(); br&gt; }　　 <br />
public void setName(String name){　　<br />
this.name=name;//此处必须指明你要引用成员变量　<br />
} <br />
public void etAge(int age){　 <br />
this.age=age;　<br />
}　<br />
public void print(){　　<br />
System.out.println("Name="+name+" ge="+age);<br />
//在此行中并不需要用this，因为没有会导致混淆的东西　<br />
}　<br />
public static void main(String[] args){　　<br />
DemoThis dt=new DemoThis("Kevin","22");<br />
　　这段代码很简单，不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它，两者效果一样。下面我们修改这个程序，来演示super的用法。<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Person<br />
{　<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;c;　<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;String&nbsp;name;　<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;age;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;setName(String&nbsp;name)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{　　<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">this</span><span style="color: #000000">.name</span><span style="color: #000000">=</span><span style="color: #000000">name;　<br />
&nbsp;&nbsp;&nbsp;&nbsp;}　<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;setAge(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;age)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{　&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">this</span><span style="color: #000000">.age</span><span style="color: #000000">=</span><span style="color: #000000">age;<br />
　&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">protected</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{　　<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">Name=</span><span style="color: #000000">"</span><span style="color: #000000">+</span><span style="color: #000000">name</span><span style="color: #000000">+</span><span style="color: #000000">"</span><span style="color: #000000">&nbsp;Age=</span><span style="color: #000000">"</span><span style="color: #000000">+</span><span style="color: #000000">age);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;DemoSuper&nbsp;</span><span style="color: #0000ff">extends</span><span style="color: #000000">&nbsp;Person<br />
{　<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print()<br />
&nbsp;&nbsp;&nbsp;&nbsp;{　　<br />
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">DemoSuper:</span><span style="color: #000000">"</span><span style="color: #000000">);　&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">super</span><span style="color: #000000">.print();&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}　<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main(String[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{　&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DemoSuper&nbsp;ds</span><span style="color: #000000">=</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;DemoSuper();　&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds.setName(</span><span style="color: #000000">"</span><span style="color: #000000">kevin</span><span style="color: #000000">"</span><span style="color: #000000">);　&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds.setAge(</span><span style="color: #000000">22</span><span style="color: #000000">);　&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds.print();&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
　　在DemoSuper中，重新定义的print方法覆写了父类的print方法，它首先做一些自己的事情，然后调用父类的那个被覆写了的方法。输出结果说明了这一点：<br />
　　DemoSuper:<br />
Name=kevin Age=22<br />
<br />
　　这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问，那你可以像使用this一样使用它，用&#8220;super.父类中的成员名&#8221;的方式，但常常你并不是这样来访问父类中的成员名的。<br />
　　在构造函数中构造函数是一种特殊的方法，在对象初始化的时候自动调用。在构造函数中，this和super也有上面说的种种使用方式，并且它还有特殊的地方，请看下面的例子：<br />
<br />
　　<br />
class Person{　<br />
<br />
public static void prt(String s){　　<br />
System.out.println(s);　<br />
}　<br />
Person(){　 <br />
prt("A Person.");　<br />
} <br />
Person(String name){　<br />
　prt("A person name is:"+name);　<br />
<br />
}<br />
}<br />
public class Chinese extends Person{<br />
　Chinese(){　　<br />
super();　//调用父类构造函数（1）　 <br />
prt("A chinese.");//(4) <br />
}　<br />
Chinese(String name){　　<br />
super(name);//调用父类具有相同形参的构造函数（2）　　<br />
prt("his name is:"+name); <br />
} <br />
Chinese(String name,int age){　　<br />
this(name);//调用当前具有相同形参的构造函数（3）　 <br />
prt("his age is:"+age); <br />
} <br />
public static void main(String[] args){　 <br />
Chinese cn=new Chinese();　　<br />
cn=new Chinese("kevin");　 <br />
cn=new Chinese("kevin",22); <br />
}<br />
}<br />
　　在这段程序中，this和super不再是像以前那样用&#8220;.&#8221;连接一个方法或成员，而是直接在其后跟<br />
　　上适当的参数，因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的<br />
　　构造函数，如1和2处。this后加参数则调用的是当前具有相同参数的构造函数，如3处。当然，在<br />
　　Chinese的各个重载构造函数中，this和super在一般方法中的各种用法也仍可使用，比如4处，你<br />
　　可以将它替换为&#8220;this.prt&#8221;(因为它继承了父类中的那个方法）或者是&#8220;super.prt&#8221;（因为它<br />
　　是父类中的方法且可被子类访问），它照样可以正确运行。但这样似乎就有点画蛇添足的味道<br />
　　了。<br />
　　最后，写了这么多，如果你能对&#8220;this通常指代当前对象，super通常指代父类&#8221;这句话牢记在<br />
　　心，那么本篇便达到了目的，其它的你自会在以后的编程实践当中慢慢体会、掌握。 </div>
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-09 14:50 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解Java ClassLoader机制 --转</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166418.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sun, 09 Dec 2007 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166418.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166418.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166418.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166418.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166418.html</trackback:ping><description><![CDATA[当JVM（Java虚拟机）启动时，会形成由三个类加载器组成的初始类加载器层次结构：<br />
<br />
&nbsp; &nbsp;&nbsp; &nbsp; bootstrap classloader<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; |<br />
&nbsp; &nbsp;&nbsp; &nbsp; extension classloader<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; |<br />
&nbsp; &nbsp;&nbsp; &nbsp; system classloader<br />
<br />
bootstrap classloader －引导（也称为原始）类加载器，它负责加载Java的核心类。在Sun的JVM中，在执行java的命令中使用-Xbootclasspath选项或使用 - D选项指定sun.boot.class.path系统属性值可以指定附加的类。这个加载器的是非常特殊的，它实际上不是 java.lang.ClassLoader的子类，而是由JVM自身实现的。大家可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; urls.length; i++) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; System.out.println(urls[i].toExternalForm());<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />
在我的计算机上的结果为：<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/jce.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar<br />
文件:/C:/j2sdk1.4.1_01/jre/classes<br />
这时大家知道了为什么我们不需要在系统属性CLASSPATH中指定这些类库了吧，因为JVM在启动的时候就自动加载它们了。<br />
<br />
extension classloader －扩展类加载器，它负责加载JRE的扩展目录（JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的）中JAR的类包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的，所以放入这个目录的 JAR类包对所有的JVM和system classloader都是可见的。在这个实例上调用方法getParent()总是返回空值null，因为引导加载器bootstrap classloader不是一个真正的ClassLoader实例。所以当大家执行以下代码时：<br />
&nbsp; &nbsp;System.out.println(System.getProperty("java.ext.dirs"));<br />
&nbsp; &nbsp;ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();<br />
&nbsp; &nbsp;System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());<br />
结果为：<br />
C:\j2sdk1.4.1_01\jre\lib\ext<br />
the parent of extension classloader : null<br />
extension classloader是system classloader的parent，而bootstrap classloader是extension classloader的parent，但它不是一个实际的classloader，所以为null。<br />
<br />
system classloader －系统（也称为应用）类加载器，它负责在JVM被启动时，加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH*作系统属性所指定的JAR类包和类路径。总能通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。如果没有特别指定，则用户自定义的任何类加载器都将该类加载器作为它的父加载器。执行以下代码即可获得：<br />
&nbsp; &nbsp;System.out.println(System.getProperty("java.class.path"));<br />
输出结果则为用户在系统属性里面设置的CLASSPATH。<br />
classloader 加载类用的是全盘负责委托机制。所谓全盘负责，即是当一个classloader加载一个Class的时候，这个Class所依赖的和引用的所有 Class也由这个classloader负责载入，除非是显式的使用另外一个classloader载入；委托机制则是先让parent（父）类加载器 (而不是super，它与parent classloader类不是继承关系)寻找，只有在parent找不到的时候才从自己的类路径中去寻找。此外类加载还采用了cache机制，也就是如果 cache中保存了这个Class就直接返回它，如果没有才从文件中读取和转换成Class，并存入cache，这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。<br />
<br />
每个ClassLoader加载Class的过程是：<br />
1.检测此Class是否载入过（即在cache中是否有此Class），如果有到8,如果没有到2<br />
2.如果parent classloader不存在（没有parent，那parent一定是bootstrap classloader了），到4<br />
3.请求parent classloader载入，如果成功到8，不成功到5<br />
4.请求jvm从bootstrap classloader中载入，如果成功到8<br />
5.寻找Class文件（从与此classloader相关的类路径中寻找）。如果找不到则到7.<br />
6.从文件中载入Class，到8.<br />
7.抛出ClassNotFoundException.<br />
8.返回Class.<br />
<br />
其中5.6步我们可以通过覆盖ClassLoader的findClass方法来实现自己的载入策略。甚至覆盖loadClass方法来实现自己的载入过程。<br />
<br />
类加载器的顺序是：<br />
先是bootstrap classloader，然后是extension classloader，最后才是system classloader。大家会发现加载的Class越是重要的越在靠前面。这样做的原因是出于安全性的考虑，试想如果system classloader&#8220;亲自&#8221;加载了一个具有破坏性的&#8220;java.lang.System&#8221;类的后果吧。这种委托机制保证了用户即使具有一个这样的类，也把它加入到了类路径中，但是它永远不会被载入，因为这个类总是由bootstrap classloader来加载的。大家可以执行一下以下的代码：<br />
&nbsp; &nbsp;System.out.println(System.class.getClassLoader());<br />
将会看到结果是null，这就表明java.lang.System是由bootstrap classloader加载的，因为bootstrap classloader不是一个真正的ClassLoader实例，而是由JVM实现的，正如前面已经说过的。<br />
<br />
下面就让我们来看看JVM是如何来为我们来建立类加载器的结构的：<br />
sun.misc.Launcher，顾名思义，当你执行java命令的时候，JVM会先使用bootstrap classloader载入并初始化一个Launcher，执行下来代码：<br />
&nbsp;&nbsp;System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());<br />
结果为：<br />
&nbsp;&nbsp;the Launcher's classloader is null (因为是用bootstrap classloader加载,所以class loader为null)<br />
Launcher 会根据系统和命令设定初始化好class loader结构，JVM就用它来获得extension classloader和system classloader,并载入所有的需要载入的Class，最后执行java命令指定的带有静态的main方法的Class。extension classloader实际上是sun.misc.Launcher$ExtClassLoader类的一个实例，system classloader实际上是sun.misc.Launcher$AppClassLoader类的一个实例。并且都是 java.net.URLClassLoader的子类。<br />
<br />
让我们来看看Launcher初试化的过程的部分代码。<br />
<br />
Launcher的部分代码：<br />
public class Launcher&nbsp;&nbsp;{<br />
&nbsp; &nbsp;public Launcher() {<br />
&nbsp; &nbsp;&nbsp; &nbsp; ExtClassLoader extclassloader;<br />
&nbsp; &nbsp;&nbsp; &nbsp; try {<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;//初始化extension classloader<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;extclassloader = ExtClassLoader.getExtClassLoader();<br />
&nbsp; &nbsp;&nbsp; &nbsp; } catch(IOException ioexception) {<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;throw new InternalError("Could not create extension class loader");<br />
&nbsp; &nbsp;&nbsp; &nbsp; }<br />
&nbsp; &nbsp;&nbsp; &nbsp; try {<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;//初始化system classloader，parent是extension classloader<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;loader = AppClassLoader.getAppClassLoader(extclassloader);<br />
&nbsp; &nbsp;&nbsp; &nbsp; } catch(IOException ioexception1) {<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;throw new InternalError("Could not create application class loader");<br />
&nbsp; &nbsp;&nbsp; &nbsp; }<br />
&nbsp; &nbsp;&nbsp; &nbsp; //将system classloader设置成当前线程的context classloader（将在后面加以介绍）<br />
&nbsp; &nbsp;&nbsp; &nbsp; Thread.currentThread().setContextClassLoader(loader);<br />
&nbsp; &nbsp;&nbsp; &nbsp; ......<br />
&nbsp; &nbsp;}<br />
&nbsp; &nbsp;public ClassLoader getClassLoader() {<br />
&nbsp; &nbsp;&nbsp; &nbsp; //返回system classloader<br />
&nbsp; &nbsp;&nbsp; &nbsp; return loader;<br />
&nbsp; &nbsp;}<br />
}<br />
<br />
extension classloader的部分代码：<br />
static class Launcher$ExtClassLoader extends URLClassLoader {<br />
<br />
&nbsp; &nbsp;public static Launcher$ExtClassLoader getExtClassLoader()<br />
&nbsp; &nbsp;&nbsp; &nbsp; throws IOException<br />
&nbsp; &nbsp;{<br />
&nbsp; &nbsp;&nbsp; &nbsp; File afile[] = getExtDirs();<br />
&nbsp; &nbsp;&nbsp; &nbsp; return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));<br />
&nbsp; &nbsp;}<br />
&nbsp;&nbsp;private static File[] getExtDirs() {<br />
&nbsp; &nbsp;&nbsp; &nbsp; //获得系统属性&#8220;java.ext.dirs&#8221;<br />
&nbsp; &nbsp;&nbsp; &nbsp; String s = System.getProperty("java.ext.dirs");<br />
&nbsp; &nbsp;&nbsp; &nbsp; File afile[];<br />
&nbsp; &nbsp;&nbsp; &nbsp; if(s != null) {<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;int i = stringtokenizer.countTokens();<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;afile = new File;<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;for(int j = 0; j &lt; i; j++)<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;afile[j] = new File(stringtokenizer.nextToken());<br />
<br />
&nbsp; &nbsp;&nbsp; &nbsp; } else {<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;afile = new File[0];<br />
&nbsp; &nbsp;&nbsp; &nbsp; }<br />
&nbsp; &nbsp;&nbsp; &nbsp; return afile;<br />
&nbsp; &nbsp;}<br />
}<br />
<br />
system classloader的部分代码：<br />
static class Launcher$AppClassLoader extends URLClassLoader<br />
{<br />
<br />
&nbsp; &nbsp;public static ClassLoader getAppClassLoader(ClassLoader classloader)<br />
&nbsp; &nbsp;&nbsp; &nbsp; throws IOException<br />
&nbsp; &nbsp;{<br />
&nbsp; &nbsp;&nbsp; &nbsp; //获得系统属性&#8220;java.class.path&#8221;<br />
&nbsp; &nbsp;&nbsp; &nbsp; String s = System.getProperty("java.class.path");<br />
&nbsp; &nbsp;&nbsp; &nbsp; File afile[] = s != null ? Launcher.access$200(s) : new File[0];<br />
&nbsp; &nbsp;&nbsp; &nbsp; return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));<br />
&nbsp; &nbsp;}<br />
}<br />
<br />
看了源代码大家就清楚了吧，extension classloader是使用系统属性&#8220;java.ext.dirs&#8221;设置类搜索路径的，并且没有parent。system classloader是使用系统属性&#8220;java.class.path&#8221;设置类搜索路径的，并且有一个parent classloader。Launcher初始化extension classloader，system classloader，并将system classloader设置成为context classloader，但是仅仅返回system classloader给JVM。<br />
<br />
　　这里怎么又出来一个context classloader呢？它有什么用呢？我们在建立一个线程Thread的时候，可以为这个线程通过setContextClassLoader方法来指定一个合适的classloader作为这个线程的context classloader，当此线程运行的时候，我们可以通过getContextClassLoader方法来获得此context classloader，就可以用它来载入我们所需要的Class。默认的是system classloader。利用这个特性，我们可以&#8220;打破&#8221;classloader委托机制了，父classloader可以获得当前线程的context classloader，而这个context classloader可以是它的子classloader或者其他的classloader，那么父classloader就可以从其获得所需的 Class，这就打破了只能向父classloader请求的限制了。这个机制可以满足当我们的classpath是在运行时才确定,并由定制的 classloader加载的时候,由system classloader(即在jvm classpath中)加载的class可以通过context classloader获得定制的classloader并加载入特定的class(通常是抽象类和接口,定制的classloader中是其实现),例如web应用中的servlet就是用这种机制加载的.<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;好了，现在我们了解了classloader的结构和工作原理，那么我们如何实现在运行时的动态载入和更新呢？只要我们能够动态改变类搜索路径和清除 classloader的cache中已经载入的Class就行了，有两个方案，一是我们继承一个classloader，覆盖loadclass方法，动态的寻找Class文件并使用defineClass方法来；另一个则非常简单实用，只要重新使用一个新的类搜索路径来new一个 classloader就行了，这样即更新了类搜索路径以便来载入新的Class，也重新生成了一个空白的cache(当然,类搜索路径不一定必须更改)。噢，太好了，我们几乎不用做什么工作，java.netURLClassLoader正是一个符合我们要求的classloader！我们可以直接使用或者继承它就可以了！<br />
<br />
这是j2se1.4 API的doc中URLClassLoader的两个构造器的描述：<br />
URLClassLoader(URL[] urls)<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.<br />
URLClassLoader(URL[] urls, ClassLoader parent)<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Constructs a new URLClassLoader for the given URLs.<br />
其中URL[] urls就是我们要设置的类搜索路径，parent就是这个classloader的parent classloader，默认的是system classloader。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;好，现在我们能够动态的载入Class了，这样我们就可以利用newInstance方法来获得一个Object。但我们如何将此Object造型呢？可以将此Object造型成它本身的Class吗？<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先让我们来分析一下java源文件的编译，运行吧！javac命令是调用&#8220;JAVA_HOME/lib/tools.jar&#8221;中的&#8220;com.sun.tools.javac.Main&#8221;的compile方法来编译：<br />
<br />
&nbsp; &nbsp;public static int compile(String as[]);<br />
<br />
&nbsp; &nbsp;public static int compile(String as[], PrintWriter printwriter);<br />
<br />
返回0表示编译成功，字符串数组as则是我们用javac命令编译时的参数，以空格划分。例如：<br />
javac -classpath c:\foo\bar.jar;. -d c:\ c:\Some.java<br />
则字符串数组as为{"-classpath","c:\\foo\\bar.jar;.","-d","c:\\","c:\Some.java"}，如果带有PrintWriter参数，则会把编译信息出到这个指定的printWriter中。默认的输出是 System.err。<br />
<br />
其中 Main是由JVM使用Launcher初始化的system classloader载入的，根据全盘负责原则，编译器在解析这个java源文件时所发现的它所依赖和引用的所有Class也将由system classloader载入，如果system classloader不能载入某个Class时，编译器将抛出一个&#8220;cannot resolve symbol&#8221;错误。<br />
<br />
所以首先编译就通不过，也就是编译器无法编译一个引用了不在CLASSPATH中的未知Class的java源文件，而由于拼写错误或者没有把所需类库放到CLASSPATH中，大家一定经常看到这个&#8220;cannot resolve symbol&#8221;这个编译错误吧！<br />
<br />
其次，就是我们把这个Class放到编译路径中，成功的进行了编译，然后在运行的时候不把它放入到CLASSPATH中而利用我们自己的 classloader来动态载入这个Class，这时候也会出现&#8220;java.lang.NoClassDefFoundError&#8221;的违例，为什么呢？<br />
<br />
我们再来分析一下，首先调用这个造型语句的可执行的Class一定是由JVM使用Launcher初始化的system classloader载入的，根据全盘负责原则，当我们进行造型的时候，JVM也会使用system classloader来尝试载入这个Class来对实例进行造型，自然在system classloader寻找不到这个Class时就会抛出&#8220;java.lang.NoClassDefFoundError&#8221;的违例。<br />
<br />
OK，现在让我们来总结一下，java文件的编译和Class的载入执行，都是使用Launcher初始化的system classloader作为类载入器的，我们无法动态的改变system classloader，更无法让JVM使用我们自己的classloader来替换system classloader，根据全盘负责原则，就限制了编译和运行时，我们无法直接显式的使用一个system classloader寻找不到的Class，即我们只能使用Java核心类库，扩展类库和CLASSPATH中的类库中的Class。<br />
<br />
还不死心！再尝试一下这种情况，我们把这个Class也放入到CLASSPATH中，让system classloader能够识别和载入。然后我们通过自己的classloader来从指定的class文件中载入这个Class（不能够委托 parent载入，因为这样会被system classloader从CLASSPATH中将其载入），然后实例化一个Object，并造型成这个Class，这样JVM也识别这个Class（因为 system classloader能够定位和载入这个Class从CLASSPATH中），载入的也不是CLASSPATH中的这个Class，而是从 CLASSPATH外动态载入的，这样总行了吧！十分不幸的是，这时会出现&#8220;java.lang.ClassCastException&#8221;违例。<br />
<br />
为什么呢？我们也来分析一下，不错，我们虽然从CLASSPATH外使用我们自己的classloader动态载入了这个Class，但将它的实例造型的时候是JVM会使用system classloader来再次载入这个Class，并尝试将使用我们的自己的classloader载入的Class的一个实例造型为system classloader载入的这个Class（另外的一个）。大家发现什么问题了吗？也就是我们尝试将从一个classloader载入的Class的一个实例造型为另外一个classloader载入的Class，虽然这两个Class的名字一样，甚至是从同一个class文件中载入。但不幸的是JVM 却认为这个两个Class是不同的，即JVM认为不同的classloader载入的相同的名字的Class（即使是从同一个class文件中载入的）是不同的！这样做的原因我想大概也是主要出于安全性考虑，这样就保证所有的核心Java类都是system classloader载入的，我们无法用自己的classloader载入的相同名字的Class的实例来替换它们的实例。<br />
<br />
看到这里，聪明的读者一定想到了该如何动态载入我们的Class，实例化，造型并调用了吧！<br />
<br />
那就是利用面向对象的基本特性之一的多形性。我们把我们动态载入的Class的实例造型成它的一个system classloader所能识别的父类就行了！这是为什么呢？我们还是要再来分析一次。当我们用我们自己的classloader来动态载入这我们只要把这个Class的时候，发现它有一个父类Class，在载入它之前JVM先会载入这个父类Class，这个父类Class是system classloader所能识别的，根据委托机制，它将由system classloader载入，然后我们的classloader再载入这个Class，创建一个实例，造型为这个父类Class，注意了，造型成这个父类 Class的时候（也就是上溯）是面向对象的java语言所允许的并且JVM也支持的，JVM就使用system classloader再次载入这个父类Class，然后将此实例造型为这个父类Class。大家可以从这个过程发现这个父类Class都是由 system classloader载入的，也就是同一个class loader载入的同一个Class，所以造型的时候不会出现任何异常。而根据多形性，调用这个父类的方法时，真正执行的是这个Class（非父类 Class）的覆盖了父类方法的方法。这些方法中也可以引用system classloader不能识别的Class，因为根据全盘负责原则，只要载入这个Class的classloader即我们自己定义的 classloader能够定位和载入这些Class就行了。<br />
<br />
这样我们就可以事先定义好一组接口或者基类并放入CLASSPATH中，然后在执行的时候动态的载入实现或者继承了这些接口或基类的子类。还不明白吗？让我们来想一想Servlet吧，web application server能够载入任何继承了Servlet的Class并正确的执行它们，不管它实际的Class是什么，就是都把它们实例化成为一个Servlet Class，然后执行Servlet的init，doPost，doGet和destroy等方法的,而不管这个Servlet是从web- inf/lib和web-inf/classes下由system classloader的子classloader(即定制的classloader)动态载入。说了这么多希望大家都明白了。在applet,ejb等容器中,都是采用了这种机制.<br />
<br />
对于以上各种情况，希望大家实际编写一些example来实验一下。<br />
<br />
最后我再说点别的， classloader虽然称为类加载器，但并不意味着只能用来加载Class，我们还可以利用它也获得图片，音频文件等资源的URL，当然，这些资源必须在CLASSPATH中的jar类库中或目录下。我们来看API的doc中关于ClassLoader的两个寻找资源和Class的方法描述吧：<br />
　　　　　　　　public URL getResource(String name)<br />
　　　　　　　　用指定的名字来查找资源，一个资源是一些能够被class代码访问的在某种程度上依赖于代码位置的数据（图片，音频，文本等等）。<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;一个资源的名字是以'/'号分隔确定资源的路径名的。<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;这个方法将先请求parent classloader搜索资源，如果没有parent，则会在内置在虚拟机中的classloader（即bootstrap classloader）的路径中搜索。如果失败，这个方法将调用findResource(String)来寻找资源。<br />
　　　　　　　　public static URL getSystemResource(String name)<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;从用来载入类的搜索路径中查找一个指定名字的资源。这个方法使用system class loader来定位资源。即相当于ClassLoader.getSystemClassLoader().getResource(name)。<br />
<br />
例如：<br />
&nbsp; &nbsp;System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));<br />
的结果为：<br />
&nbsp; &nbsp;jar:文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class<br />
表明String.class文件在rt.jar的java/lang目录中。<br />
因此我们可以将图片等资源随同Class一同打包到jar类库中（当然，也可单独打包这些资源）并添加它们到class loader的搜索路径中，我们就可以无需关心这些资源的具体位置，让class loader来帮我们寻找了！ 
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166418.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-09 11:32 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/09/166418.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 中的堆和栈－－转</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166336.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 13:09:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166336.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166336.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166336.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166336.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166336.html</trackback:ping><description><![CDATA[<strong><span style="font-size: 10pt"><strong>Java 中的堆和栈</strong><br />
<br />
简单的说：<br />
Java把内存划分成两种：一种是栈内存，一种是堆内存。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp;&nbsp;在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp;&nbsp;当在一段代码块定义一个变量时，Java就在栈中为这个变量分配内存空间，当超过变量的作用域后，Java会自动释放掉为该变量所分配的内存空间，该内存空间可以立即被另作他用。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp;&nbsp;堆内存用来存放由new创建的对象和数组。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp;&nbsp;在堆中分配的内存，由Java虚拟机的自动垃圾回收器来管理。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp;&nbsp;在堆中产生了一个数组或对象后，还可以在栈中定义一个特殊的变量，让栈中这个变量的取值等于数组或对象在堆内存中的首地址，栈中的这个变量就成了数组或对象的引用变量。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
&nbsp;&nbsp;引用变量就相当于是为数组或对象起的一个名称，以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。&nbsp; &nbsp;<br />
&nbsp; &nbsp; <br />
<br />
<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<br />
<br />
具体的说：<br />
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同，Java自动管理栈和堆，程序员不能直接地设置栈或堆。 <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立，它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的，堆的优势是可以动态地分配内存大小，生存期也不必事先告诉编译器，因为它是在运行时动态分配内存的，Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是，由于要在运行时动态分配内存，存取速度较慢。 <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;栈的优势是，存取速度比堆要快，仅次于寄存器，栈数据可以共享。但缺点是，存在栈中的数据大小与生存期必须是确定的，缺乏灵活性。栈中主要存放一些基本类型的变量（,int, short, long, byte, float, double, boolean, char）和对象句柄。 <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;栈有一个很重要的特殊性，就是存在栈中的数据可以共享。假设我们同时定义： <br />
int a = 3; <br />
int b = 3； <br />
编译器先处理int a = 3；首先它会在栈中创建一个变量为a的引用，然后查找栈中是否有3这个值，如果没找到，就将3存放进来，然后将a指向3。接着处理int b = 3；在创建完b的引用变量后，因为在栈中已经有3这个值，便将b直接指向3。这样，就出现了a与b同时均指向3的情况。这时，如果再令a=4；那么编译器会重新搜索栈中是否有4值，如果没有，则将4存放进来，并令a指向4；如果已经有了，则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的，因为这种情况a的修改并不会影响到b, 它是由编译器完成的，它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态，会影响到另一个对象引用变量。 <br />
<br />
String是一个特殊的包装类数据。可以用： <br />
String str = new String("abc"); <br />
String str = "abc"; <br />
两种的形式来创建，第一种是用new()来新建对象的，它会在存放于堆中。每调用一次就会创建一个新的对象。 <br />
而第二种是先在栈中创建一个对String类的对象引用变量str，然后查找栈中有没有存放"abc"，如果没有，则将"abc"存放进栈，并令str指向&#8221;abc&#8221;，如果已经有&#8221;abc&#8221; 则直接令str指向&#8220;abc&#8221;。 <br />
<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;比较类里面的数值是否相等时，用equals()方法；当测试两个包装类的引用是否指向同一个对象时，用==，下面用例子说明上面的理论。 <br />
String str1 = "abc"; <br />
String str2 = "abc"; <br />
System.out.println(str1==str2); //true <br />
可以看出str1和str2是指向同一个对象的。 <br />
<br />
String str1 =new String ("abc"); <br />
String str2 =new String ("abc"); <br />
System.out.println(str1==str2); // false <br />
用new的方式是生成不同的对象。每一次生成一个。 <br />
&nbsp; &nbsp;&nbsp; &nbsp; 因此用第一种方式创建多个&#8221;abc&#8221;字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度，因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc")；的代码，则一概在堆中创建新对象，而不管其字符串值是否相等，是否有必要创建新对象，从而加重了程序的负担。 <br />
&nbsp; &nbsp;&nbsp; &nbsp; 另一方面, 要注意: 我们在使用诸如String str = "abc"；的格式定义类时，总是想当然地认为，创建了String类的对象str。担心陷阱！对象可能并没有被创建！而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质，当String变量需要经常变换其值时，应该考虑使用StringBuffer类，以提高程序效率。<br />
<br />
<br />
<br />
<br />
java中内存分配策略及堆和栈的比较 <br />
2.1 内存分配策略 <br />
按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. <br />
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求. <br />
栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。 <br />
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放. <br />
<br />
2.2 堆和栈的比较 <br />
上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈: <br />
从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的，栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的: <br />
在编程中，例如C/C++中，所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候，修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时. <br />
堆是应用程序在运行的时候请求操作系统分配给自己内存，由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间，因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间，也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中，要求创建一个对象时，只需用 new命令编制相关的代码即可。执行这些代码时，会在堆里自动进行数据的保存.当然，为达到这种灵活性，必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间！这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点(晕~). <br />
<br />
<br />
2.3 JVM中的堆和栈 <br />
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说，它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。 <br />
我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的. <br />
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域，该区域具有先进后出的特性。 <br />
每一个Java应用都唯一对应一个JVM实例，每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同，Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的，但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存，在堆中分配的内存实际建立这个对象，而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 </span></strong>
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166336.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 21:09 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166336.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机体系结构概述 （转）</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166293.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166293.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166293.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166293.html</trackback:ping><description><![CDATA[Java虚拟机之所以称为&#8220;虚拟&#8221;，就是因为它仅仅是由一个规范来定义的抽象计算机。要运行某个Java程序，首先需要一个符合该规范的具体实现。下面主要讨论这个规范本身。
<p style="line-height: 150%"><font color="#0000ff"><strong>Java虚拟机是什么</strong></font> <br />
&nbsp;&nbsp;&nbsp;要理解Java虚拟机，你必须意识到，当你说&#8220;Java虚拟机&#8221;时，可能指的是如下三种不同的东西：</p>
<ul>
    <li>抽象规范&nbsp;
    <li>一个具体的实现&nbsp;
    <li>一个运行中的虚拟机实例 </li>
</ul>
<p style="line-height: 150%">Java虚拟机抽象规范仅仅是个概念。该规范的具体实现，可能来自多个提供商，并存在多个平台上。它或者完全用软件实现，或者以硬件和软件相结合的方式来实现。当运行一个Java程序的同时，也就在运行了一个Java虚拟机实例。</p>
<p style="line-height: 150%"><font color="#0000ff"><strong>Java虚拟机的生命周期</strong></font> <br />
&nbsp;&nbsp; 一个运行时的Java虚拟机实例的天职就是：负责运行一个Java程序。当启动一个Java程序时，一个虚拟机实例也就诞生了。当该程序关闭推出，这个虚拟机实例也就随之消亡。每个Java程序都运行在于自己的Java虚拟机实例中。Java虚拟机实例通过调用某个初始类的main()方法来运行一个Java程序。而这个main()方法必须是public,static,返回值为void。main()方法作为该程序初始线程的起点，任何其他的线程都是由这个初始线程启动的。 <br />
&nbsp;&nbsp; Java虚拟机内部有两种线程：守护线程和非守护线程。守护线程通常由虚拟机自己使用的，比如执行垃圾收集任务的线程。但是，Java程序也可以把它的创建的任何线程标记为守护线程。而Java程序中的初始线程，就是开始于main()的那个，是非守护线程。只要有非守护线程在运行，那么这个Java程序也在继续运行，只有该程序中所有的非守护线程都终止时，虚拟机实例将自动退出。 </p>
<p style="line-height: 150%"><strong><font color="#0000ff">Java虚拟机的体系结构</font></strong> <br />
&nbsp;&nbsp;Java虚拟机的结构分为：类装载子系统，运行时数据区，执行引擎，本地方法接口。其中运行时数据区又分为：方法区，堆，Java栈，PC寄存器，本地方法栈。 </p>
<p style="line-height: 150%"><strong><font color="#0000ff">类装载子系统</font></strong> <br />
&nbsp;&nbsp;Java虚拟机中，负责查找并装载类型的那部分称为类装载子系统。 <br />
&nbsp;&nbsp;Java虚拟机有两种类装载器：启动类装载器和用户自定义类装载器。启动类装载器是Java虚拟机实现的一部分。用户自定义类装载器是Java程序的一部分。 <br />
&nbsp;&nbsp;类装载器的动作： </p>
<ol>
    <li>装载---查找并装载类型的二进制数据
    <li>连接---执行验证，准备，以及解析（可选） <br />
    验证：确保被导入类型的正确性 <br />
    准备：为类变量分配内存，并将其初始化为默认值 <br />
    把类型中的符号引用换为直接引用
    <li>初始化---把类变量初始化为正确的初始值 </li>
</ol>
<p style="line-height: 150%"><font color="#0000ff"><strong>方法区</strong></font> <br />
&nbsp;&nbsp;在Java虚拟机中，被装载类型的信息存储在一个逻辑上被称为方法区的内存中。当虚拟机装载某个类型时，它使用类装载器定位相应的class文件，然后读入这个class文件，然后将它传输到虚拟机中，紧接着虚拟机提取其中的类型信息，并将这些信息存储到方法区。该类型中的类（静态）变量同样也是存储在方法区中。方法区的大小不必固定，可以根据需要动态调整。方法区也可以被垃圾收集，因为虚拟机允许通过用户定义的类装载器来动态扩展Java程序，因此，一些类也会成为&#8220;不再引用&#8221;的类。&nbsp; <br />
&nbsp;&nbsp;对于每个装载的类型，虚拟机都会在方法区中存储以下类型信息： </p>
<ul>
    <li>这个类型的全限定名。
    <li>这个类型的直接超类的全限定名（除非是java.lang.Object,无超类）
    <li>这个类型是类类型还是接口类型。
    <li>这个类型的访问修饰符（public,abstract ...)
    <li>任何直接超接口的全限定名的有序列表 </li>
</ul>
<p>除了上面列出的基本类型信息外，虚拟机还为每个被装载的类型存储以下信息 </p>
<ul>
    <li>该类型的常量池
    <li>字段信息
    <li>方法信息
    <li>除了常量以外所有类（静态）变量
    <li>一个到类ClassLoader的引用
    <li>一个到Class类的引用 </li>
</ul>
<p>http://blog.csdn.net/mimicimim/archive/2007/10/08/1815880.aspx</p>
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 15:53 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从Raylong - 睿狼的blog中收到的小知识..积累了</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166291.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 07:49:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166291.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166291.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166291.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166291.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166291.html</trackback:ping><description><![CDATA[对象的存储：Java中所有对象的存储空间都是在堆中分配的，但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存，在堆中分配的内存实际建立这个对象，而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。堆的特点是灵活性，但为此牺牲了高效性，可以在运行时动态地分配存储；堆栈的特点是高效性，但缺乏灵活性，在编译时刻必须知道所要分配的空间大小。堆像个大馒头，可以根据你的食量随便吃，吃饱了算；堆栈像是吃大锅饭，每个人都是定食定量的，你必须告诉厨子你的饭量，厨子据此做饭，然后你们就排队打饭吧。它们没有孰优孰劣之分，各自不同特点有不同的应用。<br />
<br />
<br />
字符串的连接。<br />
int i=1,j=2,k=3;<br />
System.out.println(i+j+k);<br />
输出了6。<br />
int i=1,j=2,k=3;<br />
System.out.println(""+i+j+k);<br />
输出了123。有趣吧？这是因为从左到右的运算顺序。 <a class="weblogtitle" id="Header1_HeaderTitle" href="http://www.blogjava.net/raylong1982/">Raylong - 睿狼</a><br />
<br />
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166291.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 15:49 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166291.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 方法</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166284.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166284.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166284.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166284.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166284.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166284.html</trackback:ping><description><![CDATA[Java语法总结 - 方法<br />
原文：<a href="http://www.blogjava.net/raylong1982/">http://www.blogjava.net/raylong1982/</a>以下几篇都是的....<br />
一、方法的重写。<br />
<br />
1、重写只能出现在继承关系之中。当一个类继承它的父类方法时，都有机会重写该父类的方法。一个特例是父类的方法被标识为final。重写的主要优点是能够定义某个子类型特有的行为。<br />
&nbsp;&nbsp; &nbsp;class Animal {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Animal is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;class Horse extends Animal{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
2、对于从父类继承来的抽象方法，要么在子类用重写的方式设计该方法，要么把子类也标识为抽象的。所以抽象方法可以说是必须要被重写的方法。<br />
<br />
3、重写的意义。<br />
重写方法可以实现多态，用父类的引用来操纵子类对象，但是在实际运行中对象将运行其自己特有的方法。<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Animal h = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;h.eat();&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
&nbsp;&nbsp; &nbsp;class Animal {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Animal is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;class Horse extends Animal{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void buck(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
一个原则是：使用了什么引用，编译器就会只调用引用类所拥有的方法。如果调用子类特有的方法，如上例的h.buck(); 编译器会抱怨的。也就是说，编译器只看引用类型，而不是对象类型。<br />
<br />
4、重写方法的规则。<br />
若想实现一个合格重写方法，而不是重载，那么必须同时满足下面的要求！<br />
<br />
A、重写规则之一：重写方法不能比被重写方法限制有更严格的访问级别。<br />
（但是可以更广泛，比如父类方法是包访问权限，子类的重写方法是public访问权限。）<br />
比如：Object类有个toString()方法，开始重写这个方法的时候我们总容易忘记public修饰符，编译器当然不会放过任何教训我们的机会。出错的原因就是：没有加任何访问修饰符的方法具有包访问权限，包访问权限比public当然要严格了，所以编译器会报错的。<br />
<br />
B、重写规则之二：参数列表必须与被重写方法的相同。<br />
重写有个孪生的弟弟叫重载，也就是后面要出场的。如果子类方法的参数与父类对应的方法不同，那么就是你认错人了，那是重载，不是重写。<br />
<br />
C、重写规则之三：返回类型必须与被重写方法的返回类型相同。<br />
父类方法A：void eat(){}&nbsp; 子类方法B：int eat(){}&nbsp; 两者虽然参数相同，可是返回类型不同，所以不是重写。<br />
父类方法A：int eat(){}&nbsp;&nbsp; 子类方法B：long eat(){}&nbsp; 返回类型虽然兼容父类，但是不同就是不同，所以不是重写。<br />
<br />
D、重写规则之四：重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少，更有限或者不抛出异常。<br />
&nbsp;&nbsp; &nbsp;import java.io.*;<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Animal h = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;h.eat();&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (Exception e) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
&nbsp;&nbsp; &nbsp;class Animal {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat() throws Exception{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Animal is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new Exception();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;class Horse extends Animal{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat() throws IOException{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new IOException();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
这个例子中，父类抛出了检查异常Exception，子类抛出的IOException是Exception的子类，也即是比被重写的方法抛出了更有限的异常，这是可以的。如果反过来，父类抛出IOException，子类抛出更为宽泛的Exception，那么不会通过编译的。<br />
注意：这种限制只是针对检查异常，至于运行时异常RuntimeException及其子类不再这个限制之中。<br />
<br />
E、重写规则之五：不能重写被标识为final的方法。<br />
<br />
F、重写规则之六：如果一个方法不能被继承，则不能重写它。<br />
比较典型的就是父类的private方法。下例会产生一个有趣的现象。<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//Animal h = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Horse h = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;h.eat();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
&nbsp;&nbsp; &nbsp;class Animal {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;private void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Animal is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;class Horse extends Animal{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is eating.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
这段代码是能通过编译的。表面上看来违反了第六条规则，但实际上那是一点巧合。Animal类的eat()方法不能被继承，因此Horse类中的eat()方法是一个全新的方法，不是重写也不是重载，只是一个只属于Horse类的全新的方法！这点让很多人迷惑了，但是也不是那么难以理解。<br />
main()方法如果是这样：<br />
&nbsp;&nbsp; &nbsp;Animal h = new Horse();<br />
&nbsp;&nbsp; &nbsp;//Horse h = new Horse();<br />
&nbsp;&nbsp; &nbsp;h.eat();<br />
编译器会报错，为什么呢？Horse类的eat()方法是public的啊！应该可以调用啊！请牢记，多态只看父类引用的方法，而不看子类对象的方法！<br />
<br />
<br />
二、方法的重载。<br />
重载是有好的，它不要求你在调用一个方法之前转换数据类型，它会自动地寻找匹配的方法。方法的重载是在编译时刻就决定调用哪个方法了，和重写不同。最最常用的地方就是构造器的重载。<br />
<br />
1、基本数据类型参数的重载。<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(byte b){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("method:byte");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(short s){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("method:short");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(int i){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("method:int");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(float f){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("method:float");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(double d){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("method:double");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method((byte)1);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method('c');<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(1);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(1L);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(1.1);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(1.1f);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
输出结果：<br />
method:byte<br />
method:int<br />
method:int<br />
method:float<br />
method:double<br />
method:float<br />
<br />
可以看出：首先要寻找的是数据类型正好匹配方法。如果找不到，那么就提升为表达能力更强的数据类型，如上例没有正好容纳long的整数类型，那么就转换为float类型的。如果通过提升也不能找到合适的兼容类型，那么编译器就会报错。反正是不会自动转换为较小的数据类型的，必须自己强制转换，自己来承担转变后果。<br />
<br />
char类型比较特殊，如果找不到正好匹配的类型，它会转化为int而不是short，虽然char是16位的。<br />
<br />
<br />
2、重载方法的规则。<br />
<br />
A、被重载的方法必须改变参数列表。<br />
参数必须不同，这是最重要的！不同有两个方面，参数的个数，参数的类型，参数的顺序。<br />
<br />
B、被重载的方法与返回类型无关。<br />
也就是说，不能通过返回类型来区分重载方法。<br />
<br />
C、被重载的方法可以改变访问修饰符。<br />
没有重写方法那样严格的限制。<br />
<br />
D、被重载的方法可以声明新的或者更广的检查异常。<br />
没有重写方法那样严格的限制。<br />
<br />
E、方法能够在一个类中或者在一个子类中被重载。<br />
<br />
<br />
3、带对象引用参数的方法重载。<br />
&nbsp;&nbsp; &nbsp;class Animal {}<br />
&nbsp;&nbsp; &nbsp;class Horse extends Animal{}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(Animal a){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Animal is called.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void method(Horse h){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is called.");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Animal a = new Animal();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Horse h = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Animal ah = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(a);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(h);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;method(ah);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
输出结果是：<br />
Animal is called.<br />
Horse is called.<br />
Animal is called.<br />
前两个输出没有任何问题。第三个方法为什么不是输出&#8220;Horse is called.&#8221;呢？还是那句老话，要看引用类型而不是对象类型，方法重载是在编译时刻就决定的了，引用类型决定了调用哪个版本的重载方法。<br />
<br />
<br />
4、重载和重写方法区别的小结。<br />
如果能彻底弄明白下面的例子，说明你对重载和重写非常了解了，可以结束这节的复习了。<br />
&nbsp;&nbsp; &nbsp;class Animal {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Animal is eating.");&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;class Horse extends Animal{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is eating.");&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void eat(String food){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("Horse is eating " + food);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Animal a = new Animal();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Horse h = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Animal ah = new Horse();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;a.eat();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;h.eat();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;h.eat("apple");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ah.eat();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//a.eat("apple");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//ah.eat("apple");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
四个输出分别是什么？被注释的两条语句为什么不能通过编译？<br />
第一条：a.eat(); 普通的方法调用，没有多态，没什么技术含量。调用了Animal类的eat()方法，输出：Animal is eating.<br />
第二条：h.eat(); 普通的方法调用，也没什么技术含量。调用了Horse类的eat()方法，输出：Horse is eating.<br />
第三条：h.eat("apple"); 重载。Horse类的两个eat()方法重载。调用了Horse类的eat(String food)方法，输出：Horse is eating apple<br />
第四条：ah.eat(); 多态。前面有例子了，不难理解。输出：Horse is eating.<br />
第五条：a.eat("apple"); 低级的错误，Animal类中没有eat(String food)方法。因此不能通过编译。<br />
第六条：ah.eat("apple"); 关键点就在这里。解决的方法还是那句老话，不能看对象类型，要看引用类型。Animal类中没有eat(String food)方法。因此不能通过编译。<br />
<br />
小结一下：多态不决定调用哪个重载版本；多态只有在决定哪个重写版本时才起作用。<br />
重载对应编译时，重写对应运行时。够简洁的了吧！<br />
<br />
<br />
三、构造方法。<br />
构造方法是一种特殊的方法，没有构造方法就不能创建一个新对象。实际上，不仅要调用对象实际类型的构造方法，还要调用其父类的构造方法，向上追溯，直到Object类。构造方法不必显式地调用，当使用new关键字时，相应的构造方法会自动被调用。<br />
<br />
1、构造方法的规则。<br />
A、构造方法能使用任何访问修饰符。包括private，事实上java类库有很多都是这样的，设计者不希望使用者创建该类的对象。<br />
<br />
B、构造方法的名称必须与类名相同。这样使得构造方法与众不同，如果我们遵守sun的编码规范，似乎只有构造方法的首字母是大写的。<br />
<br />
C、构造方法不能有返回类型。<br />
反过来说，有返回类型的不是构造方法<br />
&nbsp;&nbsp; &nbsp;public class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;int Test(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return 1;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
这个方法是什么东西？一个冒充李逵的李鬼而已，int Test()和其他任何普通方法没什么两样，就是普通的方法！只不过看起来很恶心，类似恶心的东西在考试卷子里比较多。<br />
<br />
D、如果不在类中创建自己的构造方法，编译器会自动生成默认的不带参数的构造函数。<br />
这点很容易验证！写一个这样简单的类，编译。<br />
class Test {<br />
}<br />
对生成的Test.class文件反编译：javap Test，可以看到：<br />
D:"JavaCode"bin&gt;javap Test<br />
Compiled from "Test.java"<br />
class Test extends java.lang.Object{<br />
&nbsp;&nbsp;&nbsp; Test();<br />
}<br />
看到编译器自动添加的默认构造函数了吧！<br />
<br />
E、如果只创建了带参数的构造方法，那么编译器不会自动添加无参的构造方法的！<br />
<br />
F、在每个构造方法中，如果使用了重载构造函数this()方法，或者父类的构造方法super()方法，那么this()方法或者super()方法必须放在第一行。而且这两个方法只能选择一个，因此它们之间没有顺序问题。<br />
<br />
G、除了编译器生成的构造方法，而且没有显式地调用super()方法，那么编译器会插入一个super()无参调用。<br />
<br />
H、抽象类有构造方法。<br />
<br />
<br />
四、静态方法的重载与重写（覆盖）。<br />
<br />
1、静态方法是不能被覆盖的。可以分两种情况讨论：<br />
<br />
A、子类的非静态方法&#8220;覆盖&#8221;父类的静态方法。<br />
这种情况下，是不能通过编译的。<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Father{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println&nbsp;(</span><span style="color: #000000">"</span><span style="color: #000000">in&nbsp;father&nbsp;&nbsp;method</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Child&nbsp;</span><span style="color: #0000ff">extends</span><span style="color: #000000">&nbsp;Father{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println&nbsp;(</span><span style="color: #000000">"</span><span style="color: #000000">in&nbsp;child&nbsp;method</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
static方法表示该方法不关联具体的类的对象，可以通过类名直接调用，也就是编译的前期就绑定了，不存在后期动态绑定，也就是不能实现多态。子类的非静态方法是与具体的对象绑定的，两者有着不同的含义。<br />
<br />
B、子类的静态方法&#8220;覆盖&#8221;父类静态方法。<br />
这个覆盖依然是带引号的。事实上把上面那个例子Child类的print方法前面加上static修饰符，确实能通过编译！但是不要以为这就是多态！多态的特点是动态绑定，看下面的例子：<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Father{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println&nbsp;(</span><span style="color: #000000">"</span><span style="color: #000000">in&nbsp;father&nbsp;&nbsp;method</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Child&nbsp;</span><span style="color: #0000ff">extends</span><span style="color: #000000">&nbsp;Father{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println&nbsp;(</span><span style="color: #000000">"</span><span style="color: #000000">in&nbsp;child&nbsp;method</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Test{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">static</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;main&nbsp;(String[]&nbsp;args)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Father&nbsp;f&nbsp;</span><span style="color: #000000">=</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Child();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.print();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
输出结果是：in father&nbsp; method<br />
从这个结果可以看出，并没有实现多态。<br />
但是这种形式很迷惑人，貌似多态，实际编程中千万不要这样搞，会把大家搞懵的！<br />
它不符合覆盖表现出来的特性，不应该算是覆盖！<br />
总而言之，静态方法不能被覆盖。<br />
<br />
2、静态方法可以和非静态方法一样被重载。<br />
这样的例子太多了，我不想写例程了。看看java类库中很多这样的例子。<br />
如java.util.Arrays类的一堆重载的binarySearch方法。<br />
在这里提一下是因为查资料时看到这样的话&#8220;sun的SL275课程说，静态方法只能控制静态变量（他们本身没有），静态方法不能被重载和覆盖&#8230;&#8230;&#8221; <br />
大家不要相信啊！可以重载的。而且静态与非静态方法可以重载。<br />
<br />
从重载的机制很容易就理解了，重载是在编译时刻就决定的了，非静态方法都可以，静态方法怎么可能不会呢？<br />
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166284.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 15:05 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166284.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 线程</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166283.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166283.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166283.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166283.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166283.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166283.html</trackback:ping><description><![CDATA[Java语法总结 - 线程<br />
<br />
一提到线程好像是件很麻烦很复杂的事，事实上确实如此，涉及到线程的编程是很讲究技巧的。这就需要我们变换思维方式，了解线程机制的比较通用的技巧，写出高效的、不依赖于某个JVM实现的程序来。毕竟仅仅就Java而言，各个虚拟机的实现是不同的。学习线程时，最令我印象深刻的就是那种不确定性、没有保障性，各个线程的运行完全是以不可预料的方式和速度推进，有的一个程序运行了N次，其结果差异性很大。<br />
<br />
<br />
1、什么是线程？线程是彼此互相独立的、能独立运行的子任务，并且每个线程都有自己的调用栈。所谓的多任务是通过周期性地将CPU时间片切换到不同的子任务，虽然从微观上看来，单核的CPU上同时只运行一个子任务，但是从宏观来看，每个子任务似乎是同时连续运行的。（但是JAVA的线程不是按时间片分配的，在本文的最后引用了一段网友翻译的JAVA原著中对线程的理解。）<br />
<br />
2、在java中，线程指两个不同的内容：一是java.lang.Thread类的一个对象；另外也可以指线程的执行。线程对象和其他的对象一样，在堆上创建、运行、死亡。但不同之处是线程的执行是一个轻量级的进程，有它自己的调用栈。<br />
可以这样想，每个调用栈都对应一个线程，每个线程又对应一个调用栈。<br />
我们运行java程序时有一个入口函数main()函数，它对应的线程被称为主线程。一个新线程一旦被创建，就产生一个新调用栈，从原主线程中脱离，也就是与主线程并发执行。<br />
<br />
<br />
4、当提到线程时，很少是有保障的。我们必须了解到什么是有保障的操作，什么是无保障的操作，以便设计的程序在各种jvm上都能很好地工作。比如，在某些jvm实现中，把java线程映射为本地操作系统的线程。这是java核心的一部分。<br />
<br />
5、线程的创建。<br />
创建线程有两种方式：<br />
A、继承java.lang.Thread类。<br />
&nbsp;&nbsp; &nbsp;class ThreadTest extends Thread{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public void run() {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("someting run here！");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public void run(String s){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("string in run is " + s);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThreadTest tt = new ThreadTest();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;tt.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;tt.run("it won't auto run!");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
输出的结果比较有趣：<br />
string in run is it won't auto run!<br />
someting run here！<br />
注意输出的顺序：好像与我们想象的顺序相反了！为什么呢？<br />
一旦调用start()方法，必须给JVM点时间，让它配置进程。而在它配置完成之前，重载的run(String s)方法被调用了，结果反而先输出了&#8220;string in run is it won't auto run!&#8221;，这时tt线程完成了配置，输出了&#8220;someting run here！&#8221;。<br />
这个结论是比较容易验证的：<br />
修改上面的程序，在tt.start();后面加上语句for (int i = 0; i&lt;10000; i++); 这样主线程开始执行运算量比较大的for循环了，只有执行完for循环才能运行后面的tt.run("it won't auto run!");语句。此时，tt线程和主线程并行执行了，已经有足够的时间完成线程的配置！因此先到一步！修改后的程序运行结果如下：<br />
someting run here！<br />
string in run is it won't auto run!<br />
注意：这种输出结果的顺序是没有保障的！不要依赖这种结论！<br />
<br />
没有参数的run()方法是自动被调用的，而带参数的run()是被重载的，必须显式调用。<br />
这种方式的限制是：这种方式很简单，但不是个好的方案。如果继承了Thread类，那么就不能继承其他的类了，java是单继承结构的，应该把继承的机会留给别的类。除非因为你有线程特有的更多的操作。<br />
Thread类中有许多管理线程的方法，包括创建、启动和暂停它们。所有的操作都是从run()方法开始，并且在run()方法内编写需要在独立线程内执行的代码。run()方法可以调用其他方法，但是执行的线程总是通过调用run()。<br />
<br />
B、实现java.lang.Runnable接口。<br />
&nbsp;&nbsp; &nbsp;class ThreadTest implements Runnable {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public void run() {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("someting run here");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThreadTest tt = new ThreadTest();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t1 = new Thread(tt);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t2 = new Thread(tt);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t1.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t2.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//new Thread(tt).start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
比第一种方法复杂一点，为了使代码被独立的线程运行，还需要一个Thread对象。这样就把线程相关的代码和线程要执行的代码分离开来。<br />
<br />
另一种方式是：参数形式的匿名内部类创建方式，也是比较常见的。<br />
&nbsp;&nbsp; &nbsp;class ThreadTest{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t = new Thread(new Runnable(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void run(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("anonymous thread");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;});&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
如果你对此方式的声明不感冒，请参看本人总结的内部类。<br />
<br />
第一种方式使用无参构造函数创建线程，则当线程开始工作时，它将调用自己的run()方法。<br />
第二种方式使用带参数的构造函数创建线程，因为你要告诉这个新线程使用你的run()方法，而不是它自己的。<br />
如上例，可以把一个目标赋给多个线程，这意味着几个执行线程将运行完全相同的作业。<br />
<br />
6、什么时候线程是活的？<br />
在调用start()方法开始执行线程之前，线程的状态还不是活的。测试程序如下：<br />
&nbsp;&nbsp; &nbsp;class ThreadTest implements Runnable {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public void run() {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("someting run here");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThreadTest tt = new ThreadTest();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t1 = new Thread(tt);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println (t1.isAlive());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t1.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println (t1.isAlive());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
结果输出：<br />
false<br />
true<br />
isAlive方法是确定一个线程是否已经启动，而且还没完成run()方法内代码的最好方法。<br />
<br />
7、启动新线程。<br />
线程的启动要调用start()方法，只有这样才能创建新的调用栈。而直接调用run()方法的话，就不会创建新的调用栈，也就不会创建新的线程，run()方法就与普通的方法没什么两样了！<br />
<br />
8、给线程起个有意义的名字。<br />
没有该线程命名的话，线程会有一个默认的名字，格式是：&#8220;Thread-&#8221;加上线程的序号，如：Thread-0<br />
这看起来可读性不好，不能从名字分辨出该线程具有什么功能。下面是给线程命名的方式。<br />
第一种：用setName()函数<br />
第二种：选用带线程命名的构造器<br />
&nbsp;&nbsp; &nbsp;class ThreadTest implements Runnable{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void run(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println (Thread.currentThread().getName());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThreadTest tt = new ThreadTest();&nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//Thread t = new Thread (tt,"eat apple");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t = new Thread (tt);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t.setName("eat apple");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
9、&#8220;没有保障&#8221;的多线程的运行。下面的代码可能令人印象深刻。<br />
&nbsp;&nbsp; &nbsp;class ThreadTest implements Runnable{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void run(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println (Thread.currentThread().getName());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThreadTest tt = new ThreadTest();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread[] ts =new Thread[10];<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;for (int i =0; i &lt; ts.length; i++)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ts[i] = new Thread(tt);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;for (Thread t : ts) <br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
在我的电脑上运行的结果是：<br />
Thread-0<br />
Thread-1<br />
Thread-3<br />
Thread-5<br />
Thread-2<br />
Thread-7<br />
Thread-4<br />
Thread-9<br />
Thread-6<br />
Thread-8<br />
而且每次运行的结果都是不同的！继续引用前面的话，一旦涉及到线程，其运行多半是没有保障。这个保障是指线程的运行完全是由调度程序控制的，我们没法控制它的执行顺序，持续时间也没有保障，有着不可预料的结果。<br />
<br />
<br />
10、线程的状态。<br />
A、新状态。<br />
实例化Thread对象，但没有调用start()方法时的状态。<br />
ThreadTest tt = new ThreadTest();&nbsp;&nbsp;&nbsp; &nbsp;<br />
或者Thread t = new Thread (tt);<br />
此时虽然创建了Thread对象，如前所述，但是它们不是活的，不能通过isAlive()测试。<br />
<br />
B、就绪状态。<br />
线程有资格运行，但调度程序还没有把它选为运行线程所处的状态。也就是具备了运行的条件，一旦被选中马上就能运行。<br />
也是调用start()方法后但没运行的状态。此时虽然没在运行，但是被认为是活的，能通过isAlive()测试。而且在线程运行之后、或者被阻塞、等待或者睡眠状态回来之后，线程首先进入就绪状态。<br />
<br />
C、运行状态。<br />
从就绪状态池（注意不是队列，是池）中选择一个为当前执行进程时，该线程所处的状态。<br />
<br />
D、等待、阻塞、睡眠状态。<br />
这三种状态有一个共同点：线程依然是活的，但是缺少运行的条件，一旦具备了条就就可以转为就绪状态（不能直接转为运行状态）。另外，suspend()和stop()方法已经被废弃了，比较危险，不要再用了。<br />
<br />
E、死亡状态。<br />
一个线程的run()方法运行结束，那么该线程完成其历史使命，它的栈结构将解散，也就是死亡了。但是它仍然是一个Thread对象，我们仍可以引用它，就像其他对象一样！它也不会被垃圾回收器回收了，因为对该对象的引用仍然存在。<br />
如此说来，即使run()方法运行结束线程也没有死啊！事实是，一旦线程死去，它就永远不能重新启动了，也就是说，不能再用start()方法让它运行起来！如果强来的话会抛出IllegalThreadStateException异常。如：<br />
t.start();<br />
t.start();<br />
放弃吧，人工呼吸或者心脏起搏器都无济于事&#8230;&#8230;线程也属于一次性用品。<br />
<br />
11、阻止线程运行。<br />
A、睡眠。sleep()方法<br />
让线程睡眠的理由很多，比如：认为该线程运行得太快，需要减缓一下，以便和其他线程协调；查询当时的股票价格，每睡5分钟查询一次，可以节省带宽，而且即时性要求也不那么高。<br />
用Thread的静态方法可以实现Thread.sleep(5*60*1000); 睡上5分钟吧。sleep的参数是毫秒。但是要注意sleep()方法会抛出检查异常InterruptedException，对于检查异常，我们要么声明，要么使用处理程序。<br />
&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread.sleep(20000);<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;catch (InterruptedException ie) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ie.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;}<br />
既然有了sleep()方法，我们是不是可以控制线程的执行顺序了！每个线程执行完毕都睡上一觉？这样就能控制线程的运行顺序了，下面是书上的一个例子：<br />
&nbsp;&nbsp; &nbsp;class ThreadTest implements Runnable{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void run(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;for (int i = 1; i&lt;4; i++){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println (Thread.currentThread().getName());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread.sleep(1000);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (InterruptedException ie) { }<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main (String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThreadTest tt = new ThreadTest();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t0 = new Thread(tt,"Thread 0");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t1 = new Thread(tt,"Thread 1");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Thread t2 = new Thread(tt,"Thread 2");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t0.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t1.start();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;t2.start();&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
并且给出了结果：<br />
Thread 0<br />
Thread 1<br />
Thread 2<br />
Thread 0<br />
Thread 1<br />
Thread 2<br />
Thread 0<br />
Thread 1<br />
Thread 2<br />
也就是Thread 0&nbsp; Thread 1 Thread 2 按照这个顺序交替出现，作者指出虽然结果和我们预料的似乎相同，但是这个结果是不可靠的。果然被我的双核电脑验证了：<br />
Thread 0<br />
Thread 1<br />
Thread 2<br />
Thread 2<br />
Thread 0<br />
Thread 1<br />
Thread 1<br />
Thread 0<br />
Thread 2<br />
看来线程真的很不可靠啊。但是尽管如此，sleep()方法仍然是保证所有线程都有运行机会的最好方法。至少它保证了一个线程进入运行之后不会一直到运行完位置。<br />
<br />
时间的精确性。再强调一下，线程醒来之后不会进入运行状态，而是进入就绪状态。因此sleep()中指定的时间不是线程不运行的精确时间！不能依赖sleep()方法提供十分精确的定时。我们可以看到很多应用程序用sleep()作为定时器，而且没什么不好的，确实如此，但是我们一定要知道sleep()不能保证线程醒来就能马上进入运行状态，是不精确的。<br />
<br />
sleep()方法是一个静态的方法，它所指的是当前正在执行的线程休眠一个毫秒数。看到某些书上的Thread.currentThread().sleep(1000); ，其实是不必要的。Thread.sleep(1000);就可以了。类似于getName()方法不是静态方法，它必须针对具体某个线程对象，这时用取得当前线程的方法Thread.currentThread().getName();<br />
<br />
B、线程优先级和让步。<br />
线程的优先级。在大多数jvm实现中调度程序使用基于线程优先级的抢先调度机制。如果一个线程进入可运行状态，并且它比池中的任何其他线程和当前运行的进程的具有更高的优先级，则优先级较低的线程进入可运行状态，最高优先级的线程被选择去执行。<br />
<br />
于是就有了这样的结论：当前运行线程的优先级通常不会比池中任何线程的优先级低。但是并不是所有的jvm的调度都这样，因此一定不能依赖于线程优先级来保证程序的正确操作，这仍然是没有保障的，要把线程优先级用作一种提高程序效率的方法，并且这种方法也不能依赖优先级的操作。<br />
<br />
另外一个没有保障的操作是：当前运行的线程与池中的线程，或者池中的线程具有相同的优先级时，JVM的调度实现会选择它喜欢的线程。也许是选择一个去运行，直至其完成；或者用分配时间片的方式，为每个线程提供均等的机会。<br />
<br />
优先级用正整数设置，通常为1-10，JVM从不会改变一个线程的优先级。默认情况下，优先级是5。Thread类具有三个定义线程优先级范围的静态最终常量：Thread.MIN_PRIORITY （为1） Thread.NORM_PRIORITY （为5） Thread.MAX_PRIORITY （为10）<br />
<br />
静态Thread.yield()方法。<br />
它的作用是让当前运行的线程回到可运行状态，以便让具有同等优先级的其他线程运行。用yield()方法的目的是让同等优先级的线程能适当地轮转。但是，并不能保证达到此效果！因为，即使当前变成可运行状态，可是还有可能再次被JVM选中！也就是连任。<br />
<br />
非静态join()方法。<br />
让一个线程加入到另一个线程的尾部。让B线程加入A线程，意味着在A线程运行完成之前，B线程不会进入可运行状态。<br />
&nbsp;&nbsp; &nbsp;Thread t = new Thread();<br />
&nbsp;&nbsp; &nbsp;t.start();<br />
&nbsp;&nbsp; &nbsp;t.join;<br />
这段代码的意思是取得当前的线程，把它加入到t线程的尾部，等t线程运行完毕之后，原线程继续运行。书中的例子在我的电脑里效果很糟糕，看不出什么效果来。也许是CPU太快了，而且是双核的；也许是JDK1.6的原因？<br />
<br />
12、没总结完。线程这部分很重要，内容也很多，看太快容易消化不良，偶要慢慢地消化掉&#8230;&#8230;<br />
<br />
<br />
<br />
附： java原著中对线程的解释。<br />
<br />
e文原文：<br />
<p><strong>Thread Scheduling</strong></p>
<p>In Java technology,threads are usually preemptive,but not necessarily Time-sliced(the process of giving each thread an equal amount of CPU time).It is common mistake to believe that "preemptive" is a fancy word for "does time-slicing". </p>
<p>For the runtime on a Solaris Operating Environment platform,Java technology does not preempt threads of the same priority.However,the runtime on Microsoft Windows platforms uses time-slicing,so it preempts threads of the same priority and even threads of higher priority.Preemption is not guaranteed;however,most JVM implementations result in behavior that appears to be strictly preemptive.Across JVM implementations,there is no absolute guarantee of preemption or time-slicing.The only guarantees lie in the coder&#8217;s use of wait and sleep. </p>
<p>The model of a preemptive scheduler is that many threads might be runnable,but only one thread is actually running.This thread continues to run until it ceases to be runnable or another thread of higher priority becomes runnable.In the latter case,the lower priority thread is preempted by the thread of higher priority,which gets a chance to run instead. </p>
<p>A thread might cease to runnable (that is,because blocked) for a variety of reasons.The thread&#8217;s code can execute a Thread.sleep() call,deliberately asking the thread to pause for a fixed period of time.The thread might have to wait to access a resource and cannot continue until that resource become available. </p>
<p>All thread that are runnable are kept in pools according to priority.When a blocked thread becomes runnable,it is placed back into the appropriate runnable pool.Threads from the highest priority nonempty pool are given CPU time. </p>
<p>The last sentence is worded loosed because: <br />
(1) In most JVM implementations,priorities seem to work in a preemptive manner,although there is no guarantee that priorities have any meaning at all; <br />
(2) Microsoft Window&#8217;s values affect thread behavior so that it is possible that a Java Priority 4 thread might be running,in spite of the fact that a runnable Java Priority 5 thread is waiting for the CPU. <br />
In reality,many JVMs implement pool as queues,but this is not guaranteed hehavior. </p>
<br />
<strong>热心网友翻译的版本：<br />
<br />
</strong>在java技术中，线程通常是抢占式的而不需要时间片分配进程（分配给每个线程相等的cpu时间的进程）。一个经常犯的错误是认为&#8220;抢占&#8221;就是&#8220;分配时间片&#8221;。<br />
在Solaris平台上的运行环境中，相同优先级的线程不能相互抢占对方的cpu时间。但是，在使用时间片的windows平台运行环境中，可以抢占相同甚至更高优先级的线程的cpu时间。抢占并不是绝对的，可是大多数的JVM的实现结果在行为上表现出了严格的抢占。纵观JVM的实现，并没有绝对的抢占或是时间片，而是依赖于编码者对wait和sleep这两个方法的使用。<br />
抢占式调度模型就是许多线程属于可以运行状态（等待状态），但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态（等待状态）或是另一个具有更高优先级的线程变成可运行状态。在后一种情况下，底优先级的线程被高优先级的线程抢占，高优先级的线程获得运行的机会。<br />
线程可以因为各种各样的原因终止并进入可运行状态（因为堵塞）。例如，线程的代码可以在适当时候执行Thread.sleep()方法，故意让线程中止；线程可能为了访问资源而不得不等待直到该资源可用为止。<br />
所有可运行的线程根据优先级保持在不同的池中。一旦被堵塞的线程进入可运行状态，它将会被放回适当的可运行池中。非空最高优先级的池中的线程将获得cpu时间。<br />
最后一个句子是不精确的，因为：<br />
（1）在大多数的JVM实现中，虽然不能保证说优先级有任何意义，但优先级看起来象是用抢占方式工作。<br />
（2）微软windows的评价影响线程的行为，以至尽管一个处于可运行状态的优先级为5的java线程正在等待cpu时间，但是一个优先级为4的java线程却可能正在运行。<br />
实际上，许多JVM用队列来实现池，但没有保证行为。<br />
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166283.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 15:03 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166283.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 内部类</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166277.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166277.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166277.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166277.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166277.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166277.html</trackback:ping><description><![CDATA[从Java1.1开始引入了内部类以来，它就引起了人们的激烈争论。其实任何优秀的语言特性用得不好就是滥用，内部类用得不好就会导致代码像迷宫一样，导致出现毫无重用的综合征。<br />
<br />
1、内部类分为成员内部类、静态嵌套类、方法内部类、匿名内部类。<br />
几种内部类的共性：<br />
A、内部类仍然是一个独立的类，在编译之后会内部类会被编译成独立的.class文件，但是前面冠以外部类的类命和$符号。<br />
B、内部类不能用普通的方式访问。内部类是外部类的一个成员，因此内部类可以自由地访问外部类的成员变量，无论是否是private的。<br />
<br />
2、成员内部类：形式如下<br />
&nbsp;&nbsp; &nbsp;class Outer {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;class Inner{}<br />
&nbsp;&nbsp; &nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />
编译上述代码会产生两个文件：Outer.class和Outer$Inner.class。<br />
成员内部类内不允许有任何静态声明！下面代码不能通过编译。<br />
&nbsp;&nbsp; &nbsp;class Inner{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static int a = 10;<br />
&nbsp;&nbsp; &nbsp;} <br />
能够访问成员内部类的唯一途径就是通过外部类的对象！<br />
<br />
A、从外部类的非静态方法中实例化内部类对象。<br />
<br />
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Outer&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">10</span><span style="color: #000000">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;makeInner(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Inner&nbsp;in&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">new</span><span style="color: #000000">&nbsp;Inner();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in.seeOuter();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;Inner{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;seeOuter(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.print(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
表面上，我们并没有创建外部类的对象就实例化了内部类对象，和上面的话矛盾。事实上，如果不创建外部类对象也就不可能调用makeInner()方法，所以到头来还是要创建外部类对象的。<br />
你可能试图把makeInner()方法修饰为静态方法，即static public void makeInner()。这样不创建外部类就可以实例化外部类了！但是在一个静态方法里能访问非静态成员和方法吗？显然不能。它没有this引用。没能跳出那条规则！但是如果在这个静态方法中实例化一个外部类对象，再用这个对象实例化外部类呢？完全可以！也就是下一条的内容。<br />
<br />
B、从外部类的静态方法中实例化内部类对象。<br />
&nbsp;&nbsp; &nbsp;class Outer {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;private int i = 10;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;class Inner{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void seeOuter(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.print(i);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Outer out = new Outer();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Outer.Inner in = out.new Inner();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//Outer.Inner in = new Outer().new Inner();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;in.seeOuter();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
被注释掉的那行是它上面两行的合并形式，一条简洁的语句。<br />
对比一下：在外部类的非静态方法中实例化内部类对象是普通的new方式：Inner in = new Inner();<br />
在外部类的静态方法中实例化内部类对象，必须先创建外部类对象：Outer.Inner in = new Outer().new Inner();<br />
<br />
C、内部类的this引用。<br />
普通的类可以用this引用当前的对象，内部类也是如此。但是假若内部类想引用外部类当前的对象呢？用&#8220;外部类名&#8221;.this；的形式,如下例的Outer.this。<br />
&nbsp;&nbsp; &nbsp;class Outer {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;class Inner{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void seeOuter(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println(this);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println(Outer.this);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
D、成员内部类的修饰符。<br />
对于普通的类，可用的修饰符有final、abstract、strictfp、public和默认的包访问。<br />
但是成员内部类更像一个成员变量和方法。<br />
可用的修饰符有：final、abstract、public、private、protected、strictfp和static。<br />
一旦用static修饰内部类，它就变成静态内部类了。<br />
<br />
<br />
3、方法内部类。<br />
顾名思义，把类放在方法内。<br />
&nbsp;&nbsp; &nbsp;class Outer {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void doSomething(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;class Inner{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void seeOuter(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
A、方法内部类只能在定义该内部类的方法内实例化，不可以在此方法外对其实例化。<br />
<br />
B、方法内部类对象不能使用该内部类所在方法的非final局部变量。<br />
因为方法的局部变量位于栈上，只存在于该方法的生命期内。当一个方法结束，其栈结构被删除，局部变量成为历史。但是该方法结束之后，在方法内创建的内部类对象可能仍然存在于堆中！例如，如果对它的引用被传递到其他某些代码，并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长，所以内部类对象不能使用它们。<br />
下面是完整的例子：<br />
&nbsp;&nbsp; &nbsp;class Outer {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void doSomething(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;final int a =10;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;class Inner{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void seeOuter(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println(a);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Inner in = new Inner();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;in.seeOuter(); <br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Outer out = new Outer();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;out.doSomething();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp;&nbsp; &nbsp;}<br />
<br />
C、方法内部类的修饰符。<br />
与成员内部类不同，方法内部类更像一个局部变量。<br />
可以用于修饰方法内部类的只有final和abstract。<br />
<br />
D、静态方法内的方法内部类。<br />
静态方法是没有this引用的，因此在静态方法内的内部类遭受同样的待遇，即：只能访问外部类的静态成员。<br />
<br />
<br />
4、匿名内部类。<br />
顾名思义，没有名字的内部类。表面上看起来它们似乎有名字，实际那不是它们的名字。<br />
<br />
A、继承式的匿名内部类。<br />
&nbsp;&nbsp; &nbsp;class Car {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void drive(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println("Driving a car!");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;class Test{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Car car = new Car(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void drive(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println("Driving another car!");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;};<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;car.drive();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
结果输出了：Driving another car! Car引用变量不是引用Car对象，而是Car匿名子类的对象。<br />
建立匿名内部类的关键点是重写父类的一个或多个方法。再强调一下，是重写父类的方法，而不是创建新的方法。因为用父类的引用不可能调用父类本身没有的方法！创建新的方法是多余的。简言之，参考多态。<br />
<br />
B、接口式的匿名内部类。<br />
&nbsp;&nbsp; &nbsp;interface&nbsp; Vehicle {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void drive();<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;class Test{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Vehicle v = new Vehicle(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void drive(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println("Driving a car!");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;};<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;v.drive();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
上面的代码很怪，好像是在实例化一个接口。事实并非如此，接口式的匿名内部类是实现了一个接口的匿名类。而且只能实现一个接口。<br />
<br />
C、参数式的匿名内部类。<br />
class Bar{<br />
&nbsp;&nbsp; &nbsp;void doStuff(Foo f){}<br />
}<br />
<br />
interface Foo{<br />
&nbsp;&nbsp; &nbsp;void foo();<br />
}<br />
<br />
class Test{<br />
&nbsp;&nbsp; &nbsp;static void go(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Bar b = new Bar();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;b.doStuff(new Foo(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public void foo(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println("foofy");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;});<br />
&nbsp;&nbsp; &nbsp;}<br />
}<br />
<br />
<br />
5、静态嵌套类。<br />
从技术上讲，静态嵌套类不属于内部类。因为内部类与外部类共享一种特殊关系，更确切地说是对实例的共享关系。而静态嵌套类则没有上述关系。它只是位置在另一个类的内部，因此也被称为顶级嵌套类。<br />
静态的含义是该内部类可以像其他静态成员一样，没有外部类对象时，也能够访问它。静态嵌套类不能访问外部类的成员和方法。<br />
&nbsp;&nbsp; &nbsp;class Outer{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static class Inner{}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;class Test {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main(String[] args){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Outer.Inner n = new Outer.Inner();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;} <br />
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166277.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 14:52 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166277.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 异常</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166276.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 06:48:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166276.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166276.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166276.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166276.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166276.html</trackback:ping><description><![CDATA[<br />
<br />
软件开发中一个古老的说法是：80%的工作使用20%的时间。80%是指检查和处理错误所付出的努力。在许多语言中，编写检查和处理错误的程序代码很乏味，并使应用程序代码变得冗长。原因之一就是它们的错误处理方式不是语言的一部分。尽管如此，错误检测和处理仍然是任何健壮应用程序最重要的组成部分。<br />
<br />
Java提供了一种很好的机制，用强制规定的形式来消除错误处理过程中随心所欲的因素：异常处理。它的优秀之处在于不用编写特殊代码检测返回值就能很容易地检测错误。而且它让我们把异常处理代码明确地与异常产生代码分开，代码变得更有条理。异常处理也是Java中唯一正式的错误报告机制。<br />
<br />
第一部分&nbsp;&nbsp; &nbsp;异常<br />
<br />
1、抛出异常。所有的标准异常类都有两个构造器：一个是缺省构造器，一个是带参数的构造器，以便把相关信息放入异常对象中。<br />
&nbsp;&nbsp;&nbsp; throw new NullPointerException();<br />
&nbsp;&nbsp;&nbsp; throw new NullPointerException("t = null");<br />
<br />
2、如果有一个或者多个catch块，则它们必须紧跟在try块之后，而且这些catch块必须互相紧跟着，不能有其他任何代码。C++没有这样的限制，所以C++的异常处理处理不好就会写得很乱，抛来拋去的。<br />
<br />
3、使用try块把可能出现异常的代码包含在其中，这么做的好处是：处理某种指定的异常的代码，只需编写一次。作业没写完的同学到走廊罚站去，这符合我们处理问题的方式，不用挨个地告诉。<br />
<br />
4、无论是否抛出异常，finally块封装的代码总能够在try块之后的某点执行。<br />
例子：<br />
&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;finally{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.print("You can't jump out of my hand!");&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
甚至你在try块内用return语句想跳过去都不可以！finally内的输出语句还是执行了！别想逃出我的手掌心！<br />
<br />
5、catch块和finally块是可选的，你可以只使用try。但是这么做有意思吗？<br />
<br />
6、推卸责任。Java允许你推卸责任，没有必要从相应的try块为每个可能的异常都编写catch子句。Java2类库中很多方法都会抛出异常，就是是把异常处理的权限交给了我们用户。毕竟，Java不知道你的自行车被偷了之后，你会去报案还是会忍气吞声自认倒霉，或者偷别人的自行车。我们需要这种处理异常的自由度。<br />
<br />
7、调用栈。调用栈是程序执行以访问当前方法的方法链。被调用的最后一个方法在栈的顶部，它将被最先执行完毕，然后弹出；第一个调用方法位于底部，也就是main函数。在catch子句中使用printStackTrace()方法打调用栈信息是比较常用的定位异常的方法。printStackTrace()继承自Throwable。<br />
<br />
8、异常的传播。在一个方法A中，如果一个异常没有得到处理，它就会被自动抛到调用A方法的B方法中。如果B方法也没有处理这个异常，他就会被继续依次向上抛，直到main方法。如果main也没有理会它，那么异常将导致JVM停止，程序就中止了。你被同学揍了，先去告诉老师。老师不理你你就去告诉教导处主任，教导处主任也不管那只能告诉校长，校长还不管！没有比他更大的了，于是你崩溃了，学业中止了&#8230;&#8230;下面这段程序记录了悲惨的辍学历史：<br />
&nbsp;&nbsp; &nbsp;class ExceptionDemo {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void student() throws Exception{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;teacher();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void teacher() throws Exception{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; schoolmaster();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static void schoolmaster() throws Exception{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new Exception();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; student();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (Exception e) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; &nbsp;}<br />
输出结果是：<br />
java.lang.Exception<br />
&nbsp;&nbsp;&nbsp; at ExceptionDemo.schoolmaster(ExceptionDemo.java:9)<br />
&nbsp;&nbsp;&nbsp; at ExceptionDemo.teacher(ExceptionDemo.java:6)<br />
&nbsp;&nbsp;&nbsp; at ExceptionDemo.student(ExceptionDemo.java:3)<br />
&nbsp;&nbsp;&nbsp; at ExceptionDemo.main(ExceptionDemo.java:13)<br />
可以看出函数的调用栈，一级一级地哭诉&#8230;&#8230;<br />
<br />
9、异常的层次结构及Error。<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Object<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Throwable<br />
&nbsp;&nbsp; &nbsp;Error&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Exception<br />
Throwable继承自Object，Error和Exception继承自Throwable。Error比较特殊，它对应于我们常说的不可抗拒的外力，房屋中介的合同上总有一条，如遇不可抗拒的外力本合同中止，返还乙方押金。我不安地问：不可抗拒的外力指什么？中介回答：比如战争、彗星撞击地球等。对Java来说Error是指JVM内存耗尽等这类不是程序错误或者其他事情引起的特殊情况。一般地，程序不能从Error中恢复，因此你可以能眼睁睁地看着程序崩溃而不必责怪自己。严格来讲，Error不是异常，因为它不是继承自Exception。<br />
<br />
10、谁之错？一般地，异常不是我们程序员的错，不是程序设计上的缺陷。比如读取一个重要文件，这个文件被用户误删了；正上着网呢，网线被用户的宠物咬断了。为了程序的健壮性，我们尽量考虑出现可能性大的异常，并处理，但我们不能穷尽。<br />
<br />
11、异常的捕获之一。catch子句的参数是某种类型异常的对象，如果抛出的异常是该参数的子类，那么这个异常将被它捕获。也就是说被抛出的异常不会精确地寻找最匹配的捕获者（catch子句），只要是它的继承结构的直系上层就可以捕获它。<br />
<br />
按照这个逻辑，catch(Exception e) 不就能捕获所有的异常吗？事实上，确实如此。 但是一般地，不建议使用这种一站式的异常处理。因为这样就丢失了具体的异常信息，不能为某个具体的异常编写相应的异常处理代码，失去了异常处理的意义。从哲学角度来讲，具体问题要具体分析，能治百病的万能药一般都是无效的保健品。<br />
<br />
Java在此处为什么这么设计呢？因为有另一种机制的存在，请看下条分解。<br />
<br />
12、异常的捕获之二。当抛出一个异常时，Java试图寻找一个能捕获它的catch子句，如果没找到就会沿着栈向下传播。这个过程就是异常匹配。Java规定：最具体的异常处理程序必须总是放在更普通异常处理程序的前面。这条规定再合理不过了，试想如果把catch(Exception e)放在最上面，那么下面的catch子句岂不是永远不能执行了？如果你非要把更普遍的异常处理放在前面，对不起，通不过编译！虽然编译器不会这样报错：&#8220;It is so stupid to do like that!&#8221;&#8230;&#8230;<br />
<br />
13、捕获或声明规则。如果在一个方法中抛出异常，你有两个选择：要么用catch子句捕获所有的异常，要么在方法中声明将要抛出的异常,否则编译器不会让你得逞的。<br />
方案一：处理异常<br />
&nbsp;&nbsp; &nbsp;void ex(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new Exception();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (Exception e) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;e.printStackTrace();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
方案二：抛出去<br />
&nbsp;&nbsp; &nbsp;void ex() throws Exception{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new Exception();<br />
&nbsp;&nbsp; &nbsp;}<br />
比较一下行数就知道了，在代码的世界里推卸责任也是那么简单，一个throws关键字包含了多少人生哲理啊&#8230;&#8230;现实生活中我们有很多角色，儿女、父母、学生、老师、老板、员工&#8230;&#8230;每个人都占了几条。可是你能尽到所有责任吗？按照古代的孝道，父母尚在人世就不能远行。各种责任是有矛盾的，顾此失彼啊。<br />
<br />
但是这条规则有个特例。一个继承自Exception名为RuntimeException的子类，也就是运行时异常，不受上述规则的限制。下面的代码完全能编译，只不过调用之后在运行时会抛出异常。<br />
&nbsp;&nbsp; &nbsp;void ex(){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new RuntimeException();<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
14、throw和thrwos关键字。throw用在方法体中抛出异常，后面是一个具体的异常对象。throws用在方法参数列表括号的后面，用来声明此方法会抛出的异常种类，后面跟着一个异常类。<br />
<br />
15、非检查异常。RuntimeException、Error以及它们的子类都是非检查异常，不要求定义或处理非检查异常。Java2类库中有很多方法抛出检查异常，因此会常常编写异常处理程序来处理不是你编写的方法产生的异常。这种机制强制开发人员处理错误，使得Java程序更加健壮，安全。<br />
<br />
16、自定义异常类型。觉得现有的异常无法描述你想抛出的异常，ok！Java允许你自定义异常类型，只需要继承Exception或者它的子类，然后换上有个性的名字。<br />
&nbsp;&nbsp; &nbsp;class NotEnoughMoney extends Exception {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public NotEnoughMoney() {}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public NotEnoughMoney(String msg) { super(msg); }<br />
&nbsp;&nbsp; &nbsp;}<br />
希望大家在生活里不要抛出类似的异常。<br />
<br />
17、重新抛出异常。一个很无聊的话题，纯粹的语法研究，实际意义不大。当catch子句捕获到异常之后可以重新抛出，那么它所在的方法必须声明该异常。<br />
&nbsp;&nbsp; &nbsp;void ex() throws Exception{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new Exception();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;catch (Exception mex) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw me;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
18、异常处理机制的效率。待补充&#8230;&#8230;<br />
<br />
19、终止与恢复模型。异常处理理论上有两种模型：<br />
一、终止模型。错误很关键且无法挽回，再执行下去也没意义，只能中止。&#8220;罗密欧，我们分手吧！&#8221;&#8220;好吧，朱丽叶！&#8221;<br />
二、恢复模型。经过错误修正重新尝试调用原来出问题的方法。&#8220;罗密欧，我们分手吧！&#8221;&#8220;朱丽叶，我错了！请再原谅我一次吧！&#8221;&#8220;好的，再原谅你最后一次！&#8221;<br />
显然我们更喜欢恢复模型，但在实际中，这种模式是不易实现和维护的。<br />
例子：用户输入了非法的字符，分别按照两种模式处理<br />
一、终止模型。输出出错信息而已，一旦用户手一抖眼一花你的代码就崩溃了<br />
&nbsp;&nbsp; &nbsp;double number;<br />
&nbsp;&nbsp; &nbsp;String sNumber = "";<br />
&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;sNumber = bf.readLine();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;number = Double.parseDouble(sNumber);&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;} catch (IOException ioe) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("some IOException");<br />
&nbsp;&nbsp; &nbsp;} catch (NumberFormatException nfe) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println(sNumber + " is Not a legal number!");<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;//System.out.println(number);<br />
<br />
二、恢复模型。小样！不输入正确的数据类型就别想离开！<br />
&nbsp;&nbsp; &nbsp;double number = 0;<br />
&nbsp;&nbsp; &nbsp;String sNumber = "";<br />
&nbsp;&nbsp; &nbsp;while(true){<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;sNumber = bf.readLine();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;number = Double.parseDouble(sNumber);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;break;&nbsp;&nbsp; &nbsp;//如果代码能执行到这一行，就说明没有抛出异常<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (IOException ioe) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("some IOException");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (NumberFormatException nfe) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println(sNumber + " is Not a legal number!");<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;System.out.println(number);<br />
<br />
直到用户输入正确的信息才会被该代码放过。这是一种简单的恢复模型的实现，挺耐看的，我很喜欢！<br />
<br />
20、try、catch、finally内变量的作用域和可见性。<br />
在try块内定义的变量，它在catch或者finally块内都是无法访问到的，并且在整个异常处理语句之外也是不可见的。<br />
补充一点初始化：第一个例中最后一句被注释掉了。number是在运行时由用户输入而初始化的，但是在编译时刻并没有初始化，编译器会抱怨的。<br />
<br />
21、输出异常信息。捕捉到异常之后，通常我们会输出相关的信息，以便更加明确异常。<br />
&nbsp;&nbsp; &nbsp;catch (Exception mex) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.err.println("caught a exception!");<br />
&nbsp;&nbsp; &nbsp;}<br />
用标准错误流System.err比System.out要好。因为System.out也许会被重定向，System.err则不会。<br />
<br />
22、更高级的话题我会补充上的，但是我的肚子抛出了Hungry异常，我必须catch然后调用eat()方法补充能量。昨晚的鱿鱼盖浇饭很好吃&#8230;&#8230;<br />
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166276.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 14:48 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166276.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 字符串</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166273.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 06:34:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166273.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166273.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166273.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166273.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166273.html</trackback:ping><description><![CDATA[<br />
<br />
Java的String太特别了，也太常用了，所以重要。我初学Java就被它搞蒙了，太多混淆的概念了，比如它的不变性。所以必须深入机制地去理解它。<br />
<br />
<br />
1、String中的每个字符都是一个16位的Unicode字符，用Unicode很容易表达丰富的国际化字符集，比如很好的中文支持。甚至Java的标识符都可以用汉字，但是没人会用吧（只在一本清华的《Java2实用教程》看过）。<br />
<br />
2、判断空字符串。根据需要自己选择某个或者它们的组合<br />
&nbsp;&nbsp; &nbsp;if ( s == null )&nbsp;&nbsp; &nbsp;//从引用的角度<br />
&nbsp;&nbsp; &nbsp;if ( s.length() == 0 ) &nbsp;&nbsp; &nbsp;//从长度判别<br />
&nbsp;&nbsp; &nbsp;if ( s.trim().length () == 0 ) &nbsp;&nbsp; &nbsp;//是否有多个空白字符<br />
trim()方法的作用是是移除前导和尾部的Unicode值小于'"u0020'的字符，并返回&#8220;修剪&#8221;好的字符串。这种方法很常用，比如需要用户输入用户名，用户不小心加了前导或者尾部空格，一个好的程序应该知道用户不是故意的，即使是故意的也应该智能点地处理。<br />
判断空串是很常用的操作，但是Java类库直到1.6才提供了isEmpty()方法。当且仅当 length() 为 0 时返回 true。<br />
<br />
3、未初始化、空串""与null。它们是不同的概念。对未初始化的对象操作会被编译器挡在门外；null是一个特殊的初始化值，是一个不指向任何对象的引用，对引用为null的对象操作会在运行时抛出异常NullPointerException；而空串是长度为0的字符串，和别的字符串的唯一区别就是长度为0。<br />
例子：<br />
&nbsp;&nbsp; &nbsp;public class StringTest{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;static String s1;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String s2;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String s3 = "";<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.print(s1.isEmpty());&nbsp;&nbsp; &nbsp; //运行时异常<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.print(s2.isEmpty());&nbsp;&nbsp; &nbsp; //编译出错<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.print(s3.isEmpty());&nbsp;&nbsp; &nbsp; //ok！输出true<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}<br />
<br />
4、String类的方法很多，在编写相关代码的时候看看JDK文档时有好处的，要不然花了大量时间实现一个已经存在的方法是很不值得的，因为编写、测试、维护自己的代码使项目的成本增加，利润减少，严重的话会导致开不出工资&#8230;&#8230;<br />
<br />
5、字符串的比较。<br />
Java不允许自定义操作符重载，因此字符串的比较要用compareTo() 或者 compareToIgnoreCase()。s1.compareTo(s2)，返回值大于0则，则前者大；等于0，一般大；小于0，后者大。比较的依据是字符串中各个字符的Unicode值。<br />
<br />
6、toString()方法。<br />
Java的任何对象都有toString()方法，是从Object对象继承而来的。它的作用就是让对象在输出时看起来更有意义，而不是奇怪的对象的内存地址。对测试也是很有帮助的。<br />
<br />
7、String对象是不变的！可以变化的是String对象的引用。<br />
String name = "ray";<br />
name.concat("long");&nbsp; //字符串连接<br />
System.out.println(name); //输出name，ok，还是"ray"<br />
name = name.concat("long");&nbsp; //把字符串对象连接的结果赋给了name引用<br />
System.out.println(name);&nbsp; //输出name，oh！，变成了"raylong"<br />
上述三条语句其实产生了3个String对象，"ray"，"long"，"raylong"。第2条语句确实产生了"raylong"字符串，但是没有指定把该字符串的引用赋给谁，因此没有改变name引用。第3条语句根据不变性，并没有改变"ray"，JVM创建了一个新的对象，把"ray"，"long"的连接赋给了name引用，因此引用变了，但是原对象没变。<br />
<br />
8、String的不变性的机制显然会在String常量内有大量的冗余。如："1" + "2" + "3" +......+ "n" 产生了n+(n+1)个String对象！因此Java为了更有效地使用内存，JVM留出一块特殊的内存区域，被称为&#8220;String常量池&#8221;。对String多么照顾啊！当编译器遇见String常量的时候，它检查该池内是否已经存在相同的String常量。如果找到，就把新常量的引用指向现有的String，不创建任何新的String常量对象。<br />
<br />
那么就可能出现多个引用指向同一个String常量，会不会有别名的危险呢？No problem！String对象的不变性可以保证不会出现别名问题！这是String对象与普通对象的一点区别。<br />
<br />
乍看起来这是底层的机制，对我们编程没什么影响。而且这种机制会大幅度提高String的效率，实际上却不是这样。为连接n个字符串使用字符串连接操作时，要消耗的时间是n的平方级！因为每两个字符串连接，它们的内容都要被复制。因此在处理大量的字符串连接时，而且要求性能时，我们不要用String，StringBuffer是更好的选择。<br />
<br />
8、StringBuffer类。StringBuffer类是可变的，不会在字符串常量池中，而是在堆中，不会留下一大堆无用的对象。而且它可将字符串缓冲区安全地用于多个线程。每个StringBuffer对象都有一定的容量。只要StringBuffer对象所包含的字符序列的长度没有超出此容量，就无需分配新的内部缓冲区数组。如果内部缓冲区溢出，则此容量自动增大。这个固定的容量是16个字符。我给这种算法起个名字叫&#8220;添饭算法&#8221;。先给你一满碗饭，不够了再给你一满碗饭。<br />
例子：<br />
&nbsp;&nbsp; &nbsp;StringBuffer sb = new StringBuffer();&nbsp;&nbsp; &nbsp;//初始容量为 16 个字符<br />
&nbsp;&nbsp; &nbsp;sb.append("1234");&nbsp;&nbsp; &nbsp;//这是4个字符，那么16个字符的容量就足够了，没有溢出<br />
&nbsp;&nbsp; &nbsp;System.out.println(sb.length());&nbsp;&nbsp; &nbsp;//输出字符串长度是4<br />
&nbsp;&nbsp; &nbsp;System.out.println(sb.capacity());&nbsp;&nbsp; &nbsp;//输出该字符串缓冲区的容量是16<br />
<br />
&nbsp;&nbsp; &nbsp;sb.append("12345678901234567");&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//这是17个字符，16个字符的容量不够了，扩容为17+16个字符的容量<br />
&nbsp;&nbsp; &nbsp;System.out.println(sb.length());&nbsp;&nbsp; &nbsp;//输出字符串长度是17<br />
&nbsp;&nbsp; &nbsp;System.out.println(sb.capacity());&nbsp;&nbsp; &nbsp;//输出该字符串缓冲区的容量是34<br />
<br />
&nbsp;&nbsp; &nbsp;sb.append("890").reverse().insert(10,"-");&nbsp;&nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;System.out.println(sb);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//输出0987654321-09876543214321<br />
<br />
字符串的长度和字符缓冲区的容量是两个概念，注意区别。<br />
还有串联的方式看起来是不是很酷！用返回值连接起来可以实现这种简洁和优雅。<br />
<br />
10、StringBuilder类。 从J2SE 5.0 提供了StringBuilder类，它和StringBuffer类是孪生兄弟，很像。它存在的价值在于：对字符串操作的效率更高。不足的是线程安全无法保证，不保证同步。那么两者性能到底差多少呢？很多！<br />
请参阅：http://book.csdn.net/bookfiles/135/1001354628.shtml<br />
实践：<br />
单个线程的时候使用StringBuilder类，以提高效率，而且它的API和StringBuffer兼容，不需要额外的学习成本，物美价廉。多线程时使用StringBuffer，以保证安全。<br />
<br />
11、字符串的比较。<br />
下面这条可能会让你晕，所以你可以选择看或者不看。它不会对你的职业生涯造成任何影响。而且谨记一条，比较字符串要用equals()就ok了！一旦用了&#8220;==&#8221;就会出现很怪异的现象。之所以把这部分放在最后，是想节省大家的时间，因为这条又臭又长。推荐三种人：一、没事闲着型。二、想深入地理解Java的字符串，即使明明知道学了也没用。三、和我一样爱好研究&#8220;茴&#8221;字有几种写法。<br />
<br />
还是那句老话，String太特殊了，以至于某些规则对String不起作用。个人感觉这种特殊性并不好。看例子：<br />
例子A：<br />
&nbsp;&nbsp; &nbsp;String str1 = "java";<br />
&nbsp;&nbsp; &nbsp;String str2 = "java";<br />
&nbsp;&nbsp; &nbsp;System.out.print(str1==str2);<br />
地球上有点Java基础的人都知道会输出false，因为==比较的是引用，equals比较的是内容。不是我忽悠大家，你们可以在自己的机子上运行一下，结果是true！原因很简单，String对象被放进常量池里了，再次出现&#8220;java&#8221;字符串的时候，JVM很兴奋地把str2的引用也指向了&#8220;java&#8221;对象，它认为自己节省了内存开销。不难理解吧 呵呵<br />
例子B：<br />
&nbsp;&nbsp; &nbsp;String str1 = new String("java");<br />
&nbsp;&nbsp; &nbsp;String str2 = new String("java");<br />
&nbsp;&nbsp; &nbsp;System.out.print(str1==str2);<br />
看过上例的都学聪明了，这次肯定会输出true！很不幸，JVM并没有这么做，结果是false。原因很简单，例子A中那种声明的方式确实是在String常量池创建&#8220;java&#8221;对象，但是一旦看到new关键字，JVM会在堆中为String分配空间。两者声明方式貌合神离，这也是我把&#8220;如何创建字符串对象&#8221;放到后面来讲的原因。大家要沉住气，还有一个例子。<br />
例子C：<br />
&nbsp;&nbsp; &nbsp;String str1 = "java";<br />
&nbsp;&nbsp; &nbsp;String str2 = "blog";<br />
&nbsp;&nbsp; &nbsp;String s = str1+str2;<br />
&nbsp;&nbsp; &nbsp;System.out.print(s=="javablog");<br />
再看这个例子，很多同志不敢妄言是true还是false了吧。爱玩脑筋急转弯的人会说是false吧&#8230;&#8230;恭喜你，你会抢答了！把那个&#8220;吧&#8221;字去掉你就完全正确。原因很简单，JVM确实会对型如String str1 = "java"; 的String对象放在字符串常量池里，但是它是在编译时刻那么做的，而String s = str1+str2; 是在运行时刻才能知道（我们当然一眼就看穿了，可是Java必须在运行时才知道的，人脑和电脑的结构不同），也就是说str1+str2是在堆里创建的，s引用当然不可能指向字符串常量池里的对象。没崩溃的人继续看例子D。<br />
例子D：<br />
&nbsp;&nbsp; &nbsp;String s1 = "java";<br />
&nbsp;&nbsp; &nbsp;String s2 = new String("java");<br />
&nbsp;&nbsp; &nbsp;System.out.print(s1.intern()==s2.intern());<br />
intern()是什么东东？反正结果是true。如果没用过这个方法，而且训练有素的程序员会去看JDK文档了。简单点说就是用intern()方法就可以用&#8220;==&#8221;比较字符串的内容了。在我看到intern()方法到底有什么用之前，我认为它太多余了。其实我写的这一条也很多余，intern()方法还存在诸多的问题，如效率、实现上的不统一&#8230;&#8230;<br />
例子E：<br />
&nbsp;&nbsp; &nbsp;String str1 = "java";<br />
&nbsp;&nbsp; &nbsp;String str2 = new String("java");<br />
&nbsp;&nbsp; &nbsp;System.out.print(str1.equals(str2));<br />
无论在常量池还是堆中的对象，用equals()方法比较的就是内容，就这么简单！看完此条的人一定很后悔，但是在开始我劝你别看了&#8230;&#8230;<br />
<br />
后记：用彪哥的话说&#8220;有意思吗？&#8221;，确实没劲。在写这段的时候我也是思量再三，感觉自己像孔乙己炫耀&#8220;茴&#8221;字有几种写法。我查了一下茴 ，回，囘，囬，还有一种是&#8220;口&#8221;字里面有个&#8220;目&#8221;字，后面这四个都加上草字头&#8230;&#8230;<br />
<img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166273.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 14:34 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166273.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 数组</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166272.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 06:32:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166272.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166272.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166272.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166272.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166272.html</trackback:ping><description><![CDATA[<br />
<br />
数组（array）是相同类型变量的集合，可以使用共同的名字引用它。数组可被定义为任何类型，可以是一维或多维。数组中的一个特别要素是通过下标来访问它。数组提供了一种将有联系的信息分组的便利方法。注意：如果你熟悉C/C++，请注意， Java数组的工作原理与它们不同。<br />
<br />
1、数组不是集合，它只能保存同种类型的多个原始类型或者对象的引用。数组保存的仅仅是对象的引用，而不是对象本身。<br />
<br />
2、数组本身就是对象，Java中对象是在堆中的，因此数组无论保存原始类型还是其他对象类型，数组对象本身是在堆中的。<br />
<br />
3、数组声明的两种形式：一、int[] arr; 二、int arr[];&nbsp; 推荐使用前者，这符合Sun的命名规范，而且容易了解到关键点，这是一个int数组对象，而不是一个int原始类型。<br />
<br />
4、在数组声明中包含数组长度永远是不合法的！如：int[5] arr; 。因为，声明的时候并没有实例化任何对象，只有在实例化数组对象时，JVM才分配空间，这时才与长度有关。<br />
<br />
5、在数组构造的时候必须指定长度，因为JVM要知道需要在堆上分配多少空间。反例：int[] arr = new int[];<br />
<br />
6、多维数组的声明。int[][][] arr; 是三维int型数组。<br />
<br />
7、一维数组的构造。形如：String[] sa = new String[5]; 或者分成两句：String[] sa;&nbsp; sa = new String[5];<br />
<br />
8、原始类型数组元素的默认值。对于原始类型数组，在用new构造完成而没有初始化时，JVM自动对其进行初始化。默认值：byte、short、 int、long--0&nbsp; float--0.0f double--0.0&nbsp; boolean--false&nbsp; char--'"u0000'。（无论该数组是成员变量还是局部变量）<br />
<br />
9、对象类型数组中的引用被默认初始化为null。如：Car[] myCar = new Car[10]; 相当于从myCar[0]到myCar[9]都这样被自动初始化为myCar[i] = null;<br />
<br />
10、对象类型的数组虽然被默认初始化了，但是并没有调用其构造函数。也就是说：Car[] myCar = new Car[10];只创建了一个myCar数组对象！并没有创建Car对象的任何实例！<br />
<br />
11、多维数组的构造。float[][] ratings = new float[9][]; 第一维的长度必须给出，其余的可以不写，因为JVM只需要知道赋给变量ratings的对象的长度。<br />
<br />
12、数组索引的范围。数组中各个元素的索引是从0开始的，到length-1。每个数组对象都有一个length属性，它保存了该数组对象的长度。（注意和String对象的length()方法区分开来，这两者没有统一起来是很遗憾的。）<br />
<br />
13、Java有数组下标检查，当访问超出索引范围时，将产生ArrayIndexOutOfBoundsException运行时异常。注意，这种下标检查不是在编译时刻进行的，而是在运行时！也就是说int[] arr = new int[10];&nbsp; arr[100] = 100; 这么明显的错误可以通过编译，但在运行时抛出！Java的数组下标检查是需要额外开销的，但是出于安全的权衡还是值得的，因为很多语言在使用数组时是不安全的，可以任意访问自身内存块外的数组，编译运行都不会报错，产生难以预料的后果！<br />
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166272.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 14:32 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166272.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java语法总结 - 基本数据类型</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166270.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 06:27:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166270.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166270.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166270.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166270.html</trackback:ping><description><![CDATA[<p>Java不是纯的面向对象的语言，不纯的地方就是这些基本数据类型不是对象。当然初期Java的运行速度很慢，基本数据类型能在一定程度上改善性能。如果你想编写纯的面向对象的程序，用包装器类是取代基本数据类型就可以了。<br />
<br />
1、基本类型的存储空间。byte--8位，short--16位，int--32位，long--64位，float--32位，double--64位。这六种数字类型都是有符号的。固定的存储空间正是Java可移植性、跨平台的原因之一！<br />
<br />
2、基本类型的存在导致了Java OOP的不纯粹性。因为基本类型不是对象，一切皆对象是个小小的谎言。这是出于执行效率的权衡。<br />
<br />
3、使用公式-2的（位数-1）次幂到2的（位数-1）次幂-1确定整数类型的范围（byte、short、int、long）。<br />
<br />
4、char是16位Unicode字符或者说是16位无符号整数，范围从0到65535。即便如此，可以强制转换非法的数据，如：char c1 = (char) 10000;&nbsp; char c2 = (char) -200;。可以从二进制存储的角度理解这点。<br />
<br />
5、整数有八进制（以0开头的整数）、十进制、十六进制（以0x或0X开头的整数）表示。<br />
<br />
6、char可以用单引号表示单个字符，如：'良'。也可以用unicode值'"ucafe'（四位十六进制数）。<br />
<br />
7、布尔型boolean。布尔型只能是true或者false，并且测试它为真还是假。它不能进行任何其他的运算，或者转化为其他类型。<br />
正例：boolean b1 = 1 &gt; 2;&nbsp;&nbsp;&nbsp; 反例：int seen = button.isVisible();<br />
实践：简洁是美德，请不要这样写：if ( is == true &amp;&amp; done == false ) ，只有新手才那么写。<br />
对于任何程序员 if ( whether &amp;&amp; !done ) 都不难理解吧。所以去掉所有的==fasle 和 ==true。<br />
<br />
8、默认的浮点类型是双精度（double），要想要一个float必须在浮点数后面加F或者f。如：float pi = 3.14;是错误的。<br />
<br />
9、默认的整数类型是int型，要想使用长整型可在后面加&#8220;l&#8221;或&#8220;L&#8221;，如：1000L。（小写l容易被误认为1，不推荐用）<br />
<br />
10、float可以精确到7位有效数字，第8位的数字是第9位数字四舍五入上取得的；double可以精确到16位有效数字，第17位的数字是第18位数字四舍五入上取得的。盖茨到底有多少钱？要用double表示，用float是装不下的&#8230;&#8230;<br />
<br />
11、如果要求精确的答案，请不要使用float和double，因为它们是为了在广域数值范围上提供较为精确的快速近似运算而精心设计的。然而，它们没有提供完全精确的结果。尤其是对货币计算尤为不适合，因为要让一个float或double精确地表达0.1（或者10的任何）<br />
<br />
12、BigInteger支持任意精度的整数。BigDecimal支持任意精度的定点数。<br />
<br />
13、初始化无论怎么强调都不过分！Java为所有的成员变量提供了默认初始化：byte、short、 int、long--0&nbsp; float--0.0f double--0.0&nbsp; boolean--false&nbsp; char--'"u0000'，特别地对象类型的引用全被初始化为null。（注意！除了数组之外的局部变量是得不到这种优待的，需要你自己初始化。另外，默认初始化的值是你想要的吗？所以最好明确地对变量进行初始化，一般是在构造函数中。）<br />
<br />
14、基本类型之间的转化。Java的类型检查很严格，从低精度转换到高精度是无须显式转换的，double d = 123;。但是反过来，进行窄化转换，由高精度向低精度，或者一种类型到另一种类型，则必须使用强制类型转化。Java提供了安全转化机制，但是结果是否是期望的，你自己保证吧。<br />
double d = 12.5;<br />
float f = (int) d; //结果不是13，而是12！<br />
浮点型转化为整型时，不进行四舍五入，直接截断小数点后面的数。<br />
<br />
15、提升。各种基本数据类型进行混合运算，结果会是表达能力最强的那种。如：int和long运算，结果是long，整型和浮点型运算结果是浮点型。特殊的一点是：只要类型比int小（如char、byte、short），那么在运算之前，这些值会自动地转换成int。例子：<br />
byte b1 = 12;<br />
byte b2 = b1 + 1; //在编译时出错了！因为b1+1已经是int型了！切记！<br />
<br />
16、浮点类型的科学表示法。在数学中e代表自然对数（Math.E给出了double值），而在Java中e代表10的幂次。浮点型的数可以这样表示float f = 1e-27f; 代表1乘以10的负27次幂。<br />
</p>
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 14:27 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java API introduce</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166230.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Sat, 08 Dec 2007 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166230.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/166230.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166230.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/166230.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/166230.html</trackback:ping><description><![CDATA[<span style="color: red">Java类库由以下几个包组成：<br />
java.lang<br />
java.io<br />
java.util<br />
java.awt<br />
java.applet<br />
java.net<br />
<br />
java基础类库在java.lang包中：<br />
Object 　class<br />
method:<br />
1.protected Object clone()<br />
2.public boolean equals(Object obj)<br />
3.public final class getClass()<br />
4.protected void finalize()<br />
5.public String toString()<br />
6.public final void notify()<br />
<br />
Math&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class<br />
method:<br />
1.public static double abs(double d)<br />
2.public static double exp(double d)求e的d次幂<br />
3.public static double floor(double d)求不大于d的最大整数<br />
4.public static double log(doulble d)求d 的自然对数<br />
5.public static double max(double d1,double d2)<br />
6.public static double min(double d1,double d2)<br />
7.public static double pow(double d1,double d2)d1的d2次方<br />
8.public static double random()获取0~1中的随机数<br />
9.public static double rint(double d)求d的４舍５入<br />
10.pulblic static double sqrt(double d)<br />
11.public static double sin(double d)<br />
12.public static double cos(double d)<br />
13.public static double tan(double d)<br />
此全为静态变量..可用Math.method()表示<br />
<br />
<br />
System&nbsp;&nbsp;&nbsp; class<br />
属性:<br />
public static InputStream in;<br />
pulbic static PrintStream out;<br />
public static PrintStream err;<br />
method:<br />
public static long currentTimeMillis()<br />
public static void exit(int status)<br />
public static void gc()强制调用虚拟机的垃圾回收功能<br />
<br />
<br />
String&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class<br />
分为String 类和StringBuffer类<br />
String:<br />
构造函数:<br />
public String();<br />
public Stirng(StringValue);<br />
public String(StringBuffer buffer);<br />
public String(char value[]);<br />
method:<br />
public int length();<br />
public boolean startsWith(String prefix)<br />
public boolean endsWith(Stirng suffix)<br />
查找字符串中指定的字符<br />
public int indexOf(char ch)<br />
public int indexOf(char ch,int fromIndex)<br />
public int lastIndexOf(char ch)<br />
public int lastInderOf(char ch,int fromIndex)<br />
查找字符串中指定的子串<br />
public int indexOf(String str)<br />
public int indexOf(String str,int fromIndex)<br />
public int lastIndexOf(String str)<br />
public int lastInderOf(String str,int fromIndex)<br />
比较两字符串:<br />
public int compareTo(String str)<br />
public boolean equals(Object obj)<br />
public&nbsp;boolean equalsIgnoreCase(String str)<br />
连接两字符串<br />
public String concat(String str)<br />
求字符串中的字符和子串<br />
public char chaAt(int index)<br />
public String substirng(int startIndex,int endIndex)<br />
public String substring(int startIndex)<br />
<br />
StringBuffer:<br />
构造函数：<br />
public StringBuffer()<br />
public StringBuffer(int length)<br />
public StringBuffer(String str)<br />
method:<br />
public int length()<br />
public int capacity()<br />
public void setLength(int newLength)<br />
public charAt(int index)<br />
public void setChartAt(int index,char ch)<br />
public StringBuffer append(&lt;参数类型&gt; &lt;参数名&gt;)<br />
public StringBuffer insert(int &lt;输入位置&gt;,&lt;参数类型&gt; &lt;参数名&gt;)<br />
<br />
</span>
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/166230.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-08 11:30 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/08/166230.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA如何调用DOS命令 </title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165450.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Wed, 05 Dec 2007 04:30:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165450.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/165450.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165450.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/165450.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/165450.html</trackback:ping><description><![CDATA[JAVA如何调用DOS命令 <br />
用Java编写应用时，有时需要在程序中调用另一个现成的可执行程序戒系统命令，这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。下面是一种比较典型的程序模式： ... Process process = Runtime.getRuntime().exec(".\\p.exe"); process.waitfor( ); ... 在上面的程序中，第一行的&#8220;.\\p.exe&#8221;是要执行的程序名，Runtime.getRuntime()返回当前应用程序的Runtime对象，该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序，并返回不该子进程对应的Process对象实例。通过Process可以控制该子进程的执行戒获取该子进程的信息。第二条语句的目的等待子进程完成再往下执行。 但在windows平台上，如果处理丌当，有时并丌能得到预期的结果。下面是笔者在实际编程中总结的几种需要注意的情冴： 1、执行DOS的内部命令 如果要执行一条DOS内部命令，有两种方法。一种方法是把命令解释器包含在exec()的参数中。例如，执行dir命令，在NT上， 可写成exec("cmd.exe /c dir")，在windows 95/98下，可写成&#8220;command.exe /c dir&#8221;，其中参数&#8220;/c&#8221;表示命令执行后关闭Dos立即关闭窗口。另一种方法是，把内部命令放在一个批命令my_dir.bat文件中，在Java程序中写成exec("my_dir.bat")。如果仅仅写成exec("dir")，Java虚拟机则会报运行时错误。前一种方法要保证程序的可移植性，需要在程序中读取运行的操作系统平台，以调用丌同的命令解释器。后一种方法则丌需要做更多的处理。 2、打开一个丌可执行的文件 打开一个丌可执行的文件，但该文件存在关联的应用程序，则可以有两种方式。 以打开一个word文档a.doc文件为例，Java中可以有以下两种写法： exec("start .\\a.doc"); exec(" c:\\Program Files\\Microsoft Office\\office\\winword.exe .\\a.doc"); 显然，前一种方法更为简捷方便。 3、执行一个有标准输出的DOS可执行程序<br />
在windows平台上，运行被调用程序的DOS窗口在程序执行完毕后往往并丌会自动关闭，从而导致Java应用程序阻塞在waitfor( )。导致该现象的一个可能的原因是，该可执行程序的标准输出比较多，而运行窗口的标准输出缓冲区丌够大。解决的办法是，利用Java提供的Process类提供的方法让Java虚拟机截获被调用程序的DOS运行窗口的标准输出，在waitfor()命令之前读出窗口的标准输出缓冲区中的内容。一段典型的程序如下： ... String ls_1; Process process = Runtime.getRuntime().exec("cmd /c dir \\windows"); BufferedReader bufferedReader = new BufferedReader( \ new InputStreamReader(process.getInputStream()); while ( (ls_1=bufferedReader.readLine()) != null) System.out.println(ls_1);  process.waitfor( ); ... <br />
以上内容为转载~下面内容为原创！ 今天在做客户端程序的自动更新，简单描述一下，就是从服务器上将更新包下载下来，然后在本地解压缩，最后删掉~功能很简单~ 但是问题出在使用JAVA的ZIP模块做文件的解压缩丌是想象的那么简单，资源需要释放，一个丌小心就没有办法删除掉原有ZIP文件了~资源的占用确实是个大问题，但是好在，客户端程序更新完是要重启的，一切都烟消云散了~对于删除丌掉ZIP文件的问题，我也流氓一下~用DEL硬删除~此处一定要注意！ Process process = Runtime.getRuntime().exec("cmd /c del f:\\aaa.doc"); 这样的调用是没有问题~<br />
Process process = Runtime.getRuntime().exec("del f:\\aaa.doc"); 这样写是丌可能对的~ 记录一下，警告一下后人！
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/165450.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-05 12:30 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165450.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AWT, SWT, Swing: Java GUI Clean Up</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165449.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Wed, 05 Dec 2007 04:25:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165449.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/165449.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165449.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/165449.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/165449.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: AWT, SWT, Swing: Java GUI Clean Up (1) [翻] 原文：http://blogs.sun.com/Swing/entry/awt_swt_swing_java_gui 作者：williamchen 译者：Matthew Chen 备注：本文是翻译，由于部分文本网上有提供，就直接使用了。作者的初稿，不是很规范的英文，但是内容值得一读，所以翻译了出来，一系列共有...&nbsp;&nbsp;<a href='http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165449.html'>阅读全文</a><img src ="http://www.blogjava.net/yjjlovewjf/aggbug/165449.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-05 12:25 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/05/165449.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA 初学单词表</title><link>http://www.blogjava.net/yjjlovewjf/archive/2007/12/04/165341.html</link><dc:creator>wǒ愛伱--咾婆 </dc:creator><author>wǒ愛伱--咾婆 </author><pubDate>Tue, 04 Dec 2007 15:34:00 GMT</pubDate><guid>http://www.blogjava.net/yjjlovewjf/archive/2007/12/04/165341.html</guid><wfw:comment>http://www.blogjava.net/yjjlovewjf/comments/165341.html</wfw:comment><comments>http://www.blogjava.net/yjjlovewjf/archive/2007/12/04/165341.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yjjlovewjf/comments/commentRss/165341.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yjjlovewjf/services/trackbacks/165341.html</trackback:ping><description><![CDATA[<div class="t_msgfont" id="postmessage_978834">Abstract window toolkit(AWT)　抽象窗口工具包<br />
Abstraction 抽象<br />
Anonymous class 匿名类<br />
Anonymous inner class 匿名内部类<br />
Application programming interface(API) 应用<span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%E7%A8%8B%E5%BA%8F">程序</span>接口<br />
Array 数组<br />
Attribute 属性<br />
Base class 父类<br />
Byte stream 字节流<br />
Casting 类型转换<br />
Character <span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%E5%AD%97%E7%AC%A6">字符</span><br />
Character stream 字符流<br />
Child class 子类<br />
Class 类<br />
Class member 类成员<br />
Class method 类方法<br />
Class variable 类变量<br />
Collection interface Collection接口<br />
Constructor 构造方法<br />
Container 容器<br />
Data Definition Language(DDL) 数据定义<span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%E8%AF%AD%E8%A8%80">语言</span><br />
Data source <span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%E6%95%B0%E6%8D%AE%E6%BA%90">数据源</span><br />
Database Management System(DBMS)　<span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%E6%95%B0%E6%8D%AE%E5%BA%93">数据库</span>管理系统<br />
Declaration 声名<br />
Derived class派生类、子类<br />
Encapsulation封装<br />
Event事件<br />
Event source事件源<br />
Exception 异常<br />
Exception handing 异常处理<br />
Garbage collection 垃圾回收机指<br />
Generalization 一般化，<br />
Graphics User Interface(GUI)　图形用户界面<br />
Identifier 标识符<br />
Inheritance 继承<br />
Inner class 内部类<br />
Instance 实例<br />
Integrated Development Environment(IDE)集成开发环境<br />
Interface 接口<br />
J2EE java2平台企业版<br />
JDBC java数据库连接<br />
JDK java开发工具包<br />
JFC java基础类<br />
JRM java运行时环境<br />
JVM java虚拟机<br />
Keyword　关键字<br />
Layout manager 布局管理器<br />
Local variable 局部变量<br />
Member 成员<br />
Meta data 元数据<br />
Method 方法<br />
Modifier 修饰符<br />
Multithread 多线程<br />
Object 对象<br />
OOP　面向对象编成<br />
ODBC　开放式数据库连接<br />
Overloaded method 重载方法<br />
Overridden method 重写方法<br />
Package 包<br />
Parent class 父类<br />
Platform independent 跨平台<br />
polymorphism 多态<br />
Runtime exception 运行时异常<br />
Structured Query Language(SQL) 结构化<span class="t_tag" onclick="tagshow(event)" href="tag.php?name=%E6%9F%A5%E8%AF%A2">查询</span>语言<br />
<span class="t_tag" onclick="tagshow(event)" href="tag.php?name=Sub">Sub</span> class 子类<br />
Super class 超类，父类<br />
Synchronized method 同步方法<br />
Thread 线程<br />
Uniform Resource Locator(URL) 统一资源定位器<br />
</div>
 <img src ="http://www.blogjava.net/yjjlovewjf/aggbug/165341.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yjjlovewjf/" target="_blank">wǒ愛伱--咾婆 </a> 2007-12-04 23:34 <a href="http://www.blogjava.net/yjjlovewjf/archive/2007/12/04/165341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>