//:ArraySize.java
//Initialzation & re-assignment of arrays
package c08;
class Weeble{}//A small mythical creature
public class ArraySize{
public static void main(String[] args){
//Arrays of objects;
Weeble[] a;//Null handle
Weeble[] b=new Weeble[5];//Null handle
Weeble[] c=new Weeble[4];
for(int i=0;i<c.length;i++){
c[i]=new Weeble();
}
Weeble[] d={new Weeble(),new Weeble(),new Weeble()};
//Compile error
//System.out.println("a.length="+a.length);
System.out.println("b.length="+b.length);
//The handle inside the array are automatically initialized to null;
for(int i=0;i<b.length;i++){
System.out.println("b["+i+"]="+b[i]);
}
System.out.println("c.length="+c.length);
System.out.println("d.length="+d.length);
a=d;
System.out.println("a.length="+a.length);
a=new Weeble[]{
new Weeble(),new Weeble()
};
System.out.println("a.length="+a.length);
//Arrays of primitives
int[] e;//Null handle
int[] f=new int[5];
int[] g=new int[4];
for(int i=0;i<g.length;i++){
g[i]=i*i;
}
int[] h={11,47,93};
//Compile error
//System.out.println("e.length="+e.length);
System.out.println("f.length="+f.length);
for(int i=0;i<f.length;i++){
System.out.println("f["+i+"]="+f[i]);
}
System.out.println("g.length="+g.length);
System.out.println("h.length="+h.length);
e=h;
System.out.println("e.length="+e.length);
e=new int[] {1,2};
System.out.println("e.length="+e.length);
}
}
可以在任何地方创建和初始化一个数组对象。
例如,假设hide()方法用于取得一个Weeble 对象数组,那么调用它时传统的方法是:
hide(d); 但在Java 1.1 中,亦可动态创建想作为参数传递的数组,如下所示: hide(new Weeble[] {new Weeble(), new Weeble() });
集合类只能容纳对象句柄。但对一个数组,却既可令其直接容纳基本类型的数据,亦可容纳指向对象的句 柄。利用Integer、Double 之类的“封装器”类,可将基本数据类型的值置入一个集合里。但正如本章后 面会在WordCount.java 例子中讲到的那样,用于基本数据类型的封装器类只是在某些场合下才能发挥作用。 无论将基本类型的数据置入数组,还是将其封装进入位于集合的一个类内,都涉及到执行效率的问题。显 然,若能创建和访问一个基本数据类型数组,那么比起访问一个封装数据的集合,前者的效率会高出许多。 当然,假如准备一种基本数据类型,同时又想要集合的灵活性(在需要的时候可自动扩展,腾出更多的空 间),就不宜使用数组,必须使用由封装的数据构成的一个集合。大家或许认为针对每种基本数据类型,都 应有一种特殊类型的Vector。但Java 并未提供这一特性。某些形式的建模机制或许会在某一天帮助Java 更 好地解决这个问题。
//retry的用法
/*
-----------------------
注释1,输出 0 1 2 3 4
注释2,输出 0 1 2 3 4 6 7 8 9
相当与一个标识符,break,continue,跳到该标识符处
retry在循环外,break retry;//跳出循环(continue retry;效果一样,跳出循环)
retry在循环内,break retry;//结束此次循环,继续下次循环
*/
//testRequest.java
//测试跳出循环以及结束循环
public void testRequest() {
// retry:// 1(行2)
for (int i = 0; i < 10; i++) {
retry:// 2(行4)
while (i == 5) {
continue retry;
}
System.out.print(i + " ");
}
}
//RetryTest.java
//循环嵌套测试
/*
-------------------------
retry相当于一个标记,只用在循环里面,很像goto语句,break到retry字符处。如果retry没有在循环(for,while)里面,在执行到retry时,就会跳出整个循环。如果retry在循环里面,可以理解为跳到了关键字处执行,不管几层循环。continue理解也是一样。
-------------------------
*/
//注释1,输出i=1;j=1;j=2;
//注释2,输出i=1;j=1;j=2;i=2;j=1;j=2;......i=1024;j=1;j=2;......一直打印下去
public void RetryTest() {
int i = 0;
retry: //1
while (true) {
i++;
System.out.println("i=" + i);
int j = 0;
// retry: //2
for (; ; ) {
j++;
System.out.println("j=" + j);
if (j == 2) {
break retry;
}
}
}
}
/*
----------------------------
retry就是一个标记,标记程序跳出循环的时候从哪里开始执行,功能类似于goto。
retry一般情况下:一是常常跟随for循环或while循环出现;
二就是常常跟随continue或是break,其中 break retry;用来表示退出retry循环,继续走循环下面的代码;而continue retry;则表示回到retry代码处,从循环体头开始执行。
retry的命名不是固定,只要符合Java的命名规范即可。例如retry1, retry2等等这样的名称都是合法的
----------------------------
*/
//:IceCream.java
//Returning arrays from methods
public class IceCream{
static String[] flav={
"Chocolate","Strawberry","Vanilla Fudge Swirl","Mint chip","Mocha Almond Fudge","Rum Raisin","Praline Cream","Mud Pie"
};
static String[] flavorSet(int n){
//Force it to positive & within bounds:
n=Math.abs(n)%(flav.length+1);
String[] results=new String[n];
int[] picks=new int[n];
for(int i=0;i<picks.length;i++){
picks[i]=-1;
}
for(int i=0;i<picks.length;i++){
retry:
while(true){
int t=(int)(Math.random()*flav.length);
for(int j=0;j<i;j++){
if(picks[j]==t) continue retry;
}
picks[i]=t;
results[i]=flav[t];
break;
}
}
return results;
}
public static void main(String[] args){
for(int i=0;i<20;i++){
System.out.println("flavorSet("+i+")=");
String[] f1=flavorSet(flav.le);
for(int j=0;j<f1.length;j++)
System.out.println("\t"+f1[j]);
}
}
}
原子性(Atomic):一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。
原子操作:"原子操作(atomic operation)是不需要synchronized",这是多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切 换到另一个线程)。
//:CatsAndDogs.java
//Simple collection example(Vector)
import java.util.*;
class Cat{
private int catNumber;
Cat(int i){
catNumber=i;
}
void print(){
System.out.println("Cat #"+catNumber);
}
}
class Dog{
private int dogNumber;
Dog(int i){
dogNumber=i;
}
void print(){
System.out.println("Dog #"+dogNumber);
}
}
public class CatsAndDogs{
public static void main(String[] args){
Vector cats=new Vector();
for(int i=0;i<7;i++){
cats.addElement(new Cat(i));
}
cats.addElement(new Dog(7));
for(int i=0;i<cats.size();i++)
((Cat)cats.elementAt(i)).print();
//Dog is detected only at run-time
}
}///:
用Vector方法elementAt()获取原本认为是Cat的对象时,实际获得的是指向一个Object的句柄,必须将那个对象cast为Cat在为Cat 调用print()方法之前进行强制造型;否则就会出现一个语法错误。在运行期间,如果试图将Dog 对象造型为Cat,就会得到一个异常(违例)。
//:WorksAnyway.java
//In special cases,things just seem to work correctly
import java.util.*;
class Mouse{
private int mouseNumber;
Mouse(int i){
mouseNumber=i;
}
//Magic method:
public String toString(){
return "This is Mouse #"+mouseNumber;
}
void print(String msg){
if(msg!=null) System.out.println("msg");
System.out.println("Mouse number "+mouseNumber);
}
}
class MouseTrap{
static void caughtYa(Object m){
Mouse mouse=(Mouse)m;//Cast from Object
mouse.print("Caught one!");
}
}
public class WorksAnyway{
public static void main(String[] args){
Vector mice=new Vector();
for(int i=0;i<3;i++){
mice.addElement(new Mouse(i));
}
for(int i=0;i<mice.size;i++){
//No cast necessary,sutomatic call to Object.toString();
System.out.println("Free mouse: "+mice.elementAt(i));
MouseTrap.caughtYa(mice.elementAt(i));
}
}
}
Vector
//Vector
addElement();//插入对象
elementAt();//一次提取一个对象
elements();//获取对序列的一个枚举
//:HamsterMaze.java
//Using an Enumeration
import java.util.*;
class Hamster{
private int hamsterNumber;
Hamster(int i){
hamsterNumber=i;
}
public String toString(){
return "This is Hamster #"+hamsterNumber;
}
}
class Printer{
static void printAll(Enumeration e){
while(e.hasMoreElements())
System.out.println(e.nextElement().toString());
}
}
public class HamsteMaze{
public static void main(String[] args){
Vector v=new Vector();
for(int i=0;i<3;i++){
v.addElement(new Hamster(i));
}
Printer.printAll(v.elements());
}
}
///
//Enumeration
elements();//要求集合为我们提供一个Enumeration
nextElement();//首次调用,返回序列中的第一个元素;获取下一对象
hasMoreElements();//检查序列中是否还有更多的对象
BitSet 实际是由“二进制位”构成的一个Vector。如果希望高效率地保存大量“开-关”信息,就应使用BitSet。它只有从尺寸的角度看才有意义;如果希望的高效率的访问,那么它的速度会比使用一些固有类型的数组慢一些。
//详情请见知乎
但在Java 1.0 中,一旦BitSet 大于64 位,就会出现一些令人迷惑不解的行为。假如我们设置一个只比BitSet 当前分配存储空间大出1 的一个位,它能够正常地扩展。但一旦试图在更高的位置设置位,同时不先接触边界,就会得到一个恼人的违例。这正是由于BitSet 在Java 1.0 里不能正确扩展造成的。本例创建了一个512 位的BitSet。构建 器分配的存储空间是位数的两倍。所以假如设置位1024 或更高的位,同时没有先设置位1023,就会在Java1.0 里得到一个违例。但幸运的是,这个问题已在Java 1.1 得到了改正。所以如果是为Java 1.0 写代码,请尽量避免使用BitSet。
//:Stacks.java
//Demonstration of Stack Class
import java.util.*;
public class Stacks{
static String[] months={
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
public static void main(String[] args){
Stack stk=new Stack();
for(int i=0;i<months.length;i++)
stk.push(months[i]+" ");//压栈
System.out.println("stk = "+stk);
//Treating a stack as a Vector
stk.addElement("The last line");
/*
这可能是由继承的特质决定的——Stack“属于”一种Vector。因此,能对Vector 进行的操作亦可针对Stack 进行,例如elementAt()方法。
*/
System.out.println("element 5= "+stk.elementAt(5));
System.out.println("popping elements: ");
while(!stk.empty())
System.out.println(stk.pop());//出栈
}
}