花1K内存实现高效I/O的RandomAccessFile类

原文:http://www.ibm.com/developerworks/cn/java/l-javaio/

目前最流行的J2SDK版本是1.3系列。使用该版本的开发人员需文件随机存取,就得使用RandomAccessFile类。其I/O性能较之其它常用开发语言的同类性能差距甚远,严重影响程序的运行效率。

开发人员迫切需要提高效率,下面分析RandomAccessFile等文件类的源代码,找出其中的症结所在,并加以改进优化,创建一个"性/价比"俱佳的随机文件访问类BufferedRandomAccessFile。

在改进之前先做一个基本测试:逐字节COPY一个12兆的文件(这里牵涉到读和写)。

耗用时间(秒)
RandomAccessFileRandomAccessFile95.848
BufferedInputStream + DataInputStreamBufferedOutputStream + DataOutputStream2.935

我们可以看到两者差距约32倍,RandomAccessFile也太慢了。先看看两者关键部分的源代码,对比分析,找出原因。

1.1.[RandomAccessFile]

public class RandomAccessFile implements DataOutput, DataInput {
	public final byte readByte() throws IOException {
		int ch = this.read();
		if (ch < 0)
			throw new EOFException();
		return (byte)(ch);
	}
	public native int read() throws IOException; 
	public final void writeByte(int v) throws IOException {
		write(v);
	} 
	public native void write(int b) throws IOException; 
}

可见,RandomAccessFile每读/写一个字节就需对磁盘进行一次I/O操作。

1.2.[BufferedInputStream]

public class BufferedInputStream extends FilterInputStream {
	private static int defaultBufferSize = 2048; 
	protected byte buf[]; // 建立读缓存区
	public BufferedInputStream(InputStream in, int size) {
		super(in);        
		if (size <= 0) {
			throw new IllegalArgumentException("Buffer size <= 0");
		}
		buf = new byte[size];
	}
	public synchronized int read() throws IOException {
		ensureOpen();
		if (pos >= count) {
			fill();
			if (pos >= count)
				return -1;
		}
		return buf[pos++] & 0xff; // 直接从BUF[]中读取
	} 
	private void fill() throws IOException {
	if (markpos < 0)
	    pos = 0;		/* no mark: throw away the buffer */
	else if (pos >= buf.length)	/* no room left in buffer */
	    if (markpos > 0) {	/* can throw away early part of the buffer */
		int sz = pos - markpos;
		System.arraycopy(buf, markpos, buf, 0, sz);
		pos = sz;
		markpos = 0;
	    } else if (buf.length >= marklimit) {
		markpos = -1;	/* buffer got too big, invalidate mark */
		pos = 0;	/* drop buffer contents */
	    } else {		/* grow buffer */
		int nsz = pos * 2;
		if (nsz > marklimit)
		    nsz = marklimit;
		byte nbuf[] = new byte[nsz];
		System.arraycopy(buf, 0, nbuf, 0, pos);
		buf = nbuf;
	    }
	count = pos;
	int n = in.read(buf, pos, buf.length - pos);
	if (n > 0)
	    count = n + pos;
	}
}

1.3.[BufferedOutputStream]

public class BufferedOutputStream extends FilterOutputStream {
   protected byte buf[]; // 建立写缓存区
   public BufferedOutputStream(OutputStream out, int size) {
		super(out);
		if (size <= 0) {
			throw new IllegalArgumentException("Buffer size <= 0");
		}
		buf = new byte[size];
    } 
public synchronized void write(int b) throws IOException {
		if (count >= buf.length) {
	   		flushBuffer();
		}
		buf[count++] = (byte)b; // 直接从BUF[]中读取
   }
   private void flushBuffer() throws IOException {
		if (count > 0) {
			out.write(buf, 0, count);
			count = 0;
		}
   }
}

可见,Buffered I/O putStream每读/写一个字节,若要操作的数据在BUF中,就直接对内存的buf[]进行读/写操作;否则从磁盘相应位置填充buf[],再直接对内存的buf[]进行读/写操作,绝大部分的读/写操作是对内存buf[]的操作。

1.3.小结

内存存取时间单位是纳秒级(10E-9),磁盘存取时间单位是毫秒级(10E-3),同样操作一次的开销,内存比磁盘快了百万倍。理论上可以预见,即使对内存操作上万次,花费的时间也远少对于磁盘一次I/O的开销。显然后者是通过增加位于内存的BUF存取,减少磁盘I/O的开销,提高存取效率的,当然这样也增加了BUF控制部分的开销。从实际应用来看,存取效率提高了32倍。

根据1.3得出的结论,现试着对RandomAccessFile类也加上缓冲读写机制。

随机访问类与顺序类不同,前者是通过实现DataInput/DataOutput接口创建的,而后者是扩展FilterInputStream/FilterOutputStream创建的,不能直接照搬。

2.1.开辟缓冲区BUF[默认:1024字节],用作读/写的共用缓冲区。

2.2.先实现读缓冲。

读缓冲逻辑的基本原理:

A 欲读文件POS位置的一个字节。

B 查BUF中是否存在?若有,直接从BUF中读取,并返回该字符BYTE。

C 若没有,则BUF重新定位到该POS所在的位置并把该位置附近的BUFSIZE的字节的文件内容填充BUFFER,返回B。

以下给出关键部分代码及其说明:

public class BufferedRandomAccessFile extends RandomAccessFile {
//  byte read(long pos):读取当前文件POS位置所在的字节
//  bufstartpos、bufendpos代表BUF映射在当前文件的首/尾偏移地址。
//  curpos指当前类文件指针的偏移地址。
    public byte read(long pos) throws IOException {
        if (pos < this.bufstartpos || pos > this.bufendpos ) {
            this.flushbuf();
            this.seek(pos);
            if ((pos < this.bufstartpos) || (pos > this.bufendpos)) 
                throw new IOException();
        }
        this.curpos = pos;
        return this.buf[(int)(pos - this.bufstartpos)];
    }
// void flushbuf():bufdirty为真,把buf[]中尚未写入磁盘的数据,写入磁盘。
    private void flushbuf() throws IOException {
        if (this.bufdirty == true) {
            if (super.getFilePointer() != this.bufstartpos) {
                super.seek(this.bufstartpos);
            }
            super.write(this.buf, 0, this.bufusedsize);
            this.bufdirty = false;
        }
    }
// void seek(long pos):移动文件指针到pos位置,并把buf[]映射填充至POS
所在的文件块。
    public void seek(long pos) throws IOException {
        if ((pos < this.bufstartpos) || (pos > this.bufendpos)) { // seek pos not in buf
            this.flushbuf();
            if ((pos >= 0) && (pos <= this.fileendpos) && (this.fileendpos != 0)) 
{   // seek pos in file (file length > 0)
            	  this.bufstartpos =  pos * bufbitlen / bufbitlen;
                this.bufusedsize = this.fillbuf();
            } else if (((pos == 0) && (this.fileendpos == 0)) 
|| (pos == this.fileendpos + 1)) 
{   // seek pos is append pos
                this.bufstartpos = pos;
                this.bufusedsize = 0;
            }
            this.bufendpos = this.bufstartpos + this.bufsize - 1;
        }
        this.curpos = pos;
    }
// int fillbuf():根据bufstartpos,填充buf[]。
    private int fillbuf() throws IOException {
        super.seek(this.bufstartpos);
        this.bufdirty = false;
        return super.read(this.buf);
    }
}

至此缓冲读基本实现,逐字节COPY一个12兆的文件(这里牵涉到读和写,用BufferedRandomAccessFile试一下读的速度):

耗用时间(秒)
RandomAccessFileRandomAccessFile95.848
BufferedRandomAccessFileBufferedOutputStream +DataOutputStream2.813
BufferedInputStream +DataInputStreamBufferedOutputStream +DataOutputStream2.935

可见速度显著提高,与BufferedInputStream+DataInputStream不相上下。

2.3.实现写缓冲。

写缓冲逻辑的基本原理:

A欲写文件POS位置的一个字节。

B 查BUF中是否有该映射?若有,直接向BUF中写入,并返回true。

C若没有,则BUF重新定位到该POS所在的位置,并把该位置附近的BUFSIZE字节的文件内容填充BUFFER,返回B。

下面给出关键部分代码及其说明:

// boolean write(byte bw, long pos):向当前文件POS位置写入字节BW。
// 根据POS的不同及BUF的位置:存在修改、追加、BUF中、BUF外等情
况。在逻辑判断时,把最可能出现的情况,最先判断,这样可提高速度。
// fileendpos:指示当前文件的尾偏移地址,主要考虑到追加因素
    public boolean write(byte bw, long pos) throws IOException {
        if ((pos >= this.bufstartpos) && (pos <= this.bufendpos)) { 
// write pos in buf
            this.buf[(int)(pos - this.bufstartpos)] = bw;
            this.bufdirty = true;
            if (pos == this.fileendpos + 1) { // write pos is append pos
                this.fileendpos++;
                this.bufusedsize++;
            }
        } else { // write pos not in buf
            this.seek(pos);
            if ((pos >= 0) && (pos <= this.fileendpos) && (this.fileendpos != 0)) 
{ // write pos is modify file
                this.buf[(int)(pos - this.bufstartpos)] = bw;
            } else if (((pos == 0) && (this.fileendpos == 0)) 
|| (pos == this.fileendpos + 1)) { // write pos is append pos
                this.buf[0] = bw;
                this.fileendpos++;
                this.bufusedsize = 1;
            } else {
                throw new IndexOutOfBoundsException();
            }
            this.bufdirty = true;
        }
        this.curpos = pos;
        return true;
    }
	

至此缓冲写基本实现,逐字节COPY一个12兆的文件,(这里牵涉到读和写,结合缓冲读,用BufferedRandomAccessFile试一下读/写的速度):

耗用时间(秒)
RandomAccessFileRandomAccessFile95.848
BufferedInputStream +DataInputStreamBufferedOutputStream +DataOutputStream2.935
BufferedRandomAccessFileBufferedOutputStream +DataOutputStream2.813
BufferedRandomAccessFileBufferedRandomAccessFile2.453

可见综合读/写速度已超越BufferedInput/OutputStream+DataInput/OutputStream。

优化BufferedRandomAccessFile。

优化原则:

  • 调用频繁的语句最需要优化,且优化的效果最明显。
  • 多重嵌套逻辑判断时,最可能出现的判断,应放在最外层。
  • 减少不必要的NEW。

这里举一典型的例子:

   public void seek(long pos) throws IOException {
		...
this.bufstartpos =  pos * bufbitlen / bufbitlen; 
// bufbitlen指buf[]的位长,例:若bufsize=1024,则bufbitlen=10。
...
}

seek函数使用在各函数中,调用非常频繁,上面加重的这行语句根据pos和bufsize确定buf[]对应当前文件的映射位置,用"*"、"/"确定,显然不是一个好方法。

优化一:this.bufstartpos = (pos << bufbitlen) >> bufbitlen;

优化二:this.bufstartpos = pos & bufmask;// this.bufmask = ~((long)this.bufsize - 1);

两者效率都比原来好,但后者显然更好,因为前者需要两次移位运算、后者只需一次逻辑与运算(bufmask可以预先得出)。

至此优化基本实现,逐字节COPY一个12兆的文件,(这里牵涉到读和写,结合缓冲读,用优化后BufferedRandomAccessFile试一下读/写的速度):

耗用时间(秒)
RandomAccessFileRandomAccessFile95.848
BufferedInputStream +DataInputStreamBufferedOutputStream +DataOutputStream2.935
BufferedRandomAccessFileBufferedOutputStream +DataOutputStream2.813
BufferedRandomAccessFileBufferedRandomAccessFile2.453
BufferedRandomAccessFile优BufferedRandomAccessFile优2.197

可见优化尽管不明显,还是比未优化前快了一些,也许这种效果在老式机上会更明显。

以上比较的是顺序存取,即使是随机存取,在绝大多数情况下也不止一个BYTE,所以缓冲机制依然有效。而一般的顺序存取类要实现随机存取就不怎么容易了。

需要完善的地方

提供文件追加功能:

    public boolean append(byte bw) throws IOException {
        return this.write(bw, this.fileendpos + 1);
    }

提供文件当前位置修改功能:

    public boolean write(byte bw) throws IOException {
        return this.write(bw, this.curpos);
    }
	

返回文件长度(由于BUF读写的原因,与原来的RandomAccessFile类有所不同):

    public long length() throws IOException {
        return this.max(this.fileendpos + 1, this.initfilelen);
    }
	

返回文件当前指针(由于是通过BUF读写的原因,与原来的RandomAccessFile类有所不同):

    public long getFilePointer() throws IOException {
        return this.curpos;
    }
	

提供对当前位置的多个字节的缓冲写功能:

    public void write(byte b[], int off, int len) throws IOException {
        long writeendpos = this.curpos + len - 1;
        if (writeendpos <= this.bufendpos) { // b[] in cur buf
System.arraycopy(b, off, this.buf, (int)(this.curpos - this.bufstartpos), 
len);
            this.bufdirty = true;
            this.bufusedsize = (int)(writeendpos - this.bufstartpos + 1);
        } else { // b[] not in cur buf
            super.seek(this.curpos);
            super.write(b, off, len);
        }
        if (writeendpos > this.fileendpos)
            this.fileendpos = writeendpos;
        this.seek(writeendpos+1);
}
    public void write(byte b[]) throws IOException {
        this.write(b, 0, b.length);
    }

提供对当前位置的多个字节的缓冲读功能:

public int read(byte b[], int off, int len) throws IOException {
long readendpos = this.curpos + len - 1;
   if (readendpos <= this.bufendpos && readendpos <= this.fileendpos ) { 
// read in buf
     	System.arraycopy(this.buf, (int)(this.curpos - this.bufstartpos), 
b, off, len);
   } else { // read b[] size > buf[]
   		if (readendpos > this.fileendpos) { // read b[] part in file
      		len = (int)(this.length() - this.curpos + 1);
       }
       super.seek(this.curpos);
       len = super.read(b, off, len);
       readendpos = this.curpos + len - 1;
   }
       this.seek(readendpos + 1);
       return len;
}
   public int read(byte b[]) throws IOException {
        return this.read(b, 0, b.length);
   }
public void setLength(long newLength) throws IOException {
        if (newLength > 0) {
            this.fileendpos = newLength - 1;
        } else {
            this.fileendpos = 0;
        }
        super.setLength(newLength);
}
    
public void close() throws IOException {
        this.flushbuf();
        super.close();
   	}
	

至此完善工作基本完成,试一下新增的多字节读/写功能,通过同时读/写1024个字节,来COPY一个12兆的文件,(这里牵涉到读和写,用完善后BufferedRandomAccessFile试一下读/写的速度):

耗用时间(秒)
RandomAccessFileRandomAccessFile95.848
BufferedInputStream +DataInputStreamBufferedOutputStream +DataOutputStream2.935
BufferedRandomAccessFileBufferedOutputStream +DataOutputStream2.813
BufferedRandomAccessFileBufferedRandomAccessFile2.453
BufferedRandomAccessFile优BufferedRandomAccessFile优2.197
BufferedRandomAccessFile完BufferedRandomAccessFile完0.401

与JDK1.4新类MappedByteBuffer+RandomAccessFile的对比?

JDK1.4提供了NIO类 ,其中MappedByteBuffer类用于映射缓冲,也可以映射随机文件访问,可见JAVA设计者也看到了RandomAccessFile的问题,并加以改进。怎么通过MappedByteBuffer+RandomAccessFile拷贝文件呢?下面就是测试程序的主要部分:

RandomAccessFile rafi = new RandomAccessFile(SrcFile, "r");
   RandomAccessFile rafo = new RandomAccessFile(DesFile, "rw");
	FileChannel fci = rafi.getChannel();
FileChannel fco = rafo.getChannel();
	long size = fci.size();
	MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
long start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
            byte b = mbbi.get(i);
            mbbo.put(i, b);
}
fcin.close();
fcout.close();
rafi.close();
rafo.close();
System.out.println("Spend: "+(double)(System.currentTimeMillis()-start) / 1000 + "s");

试一下JDK1.4的映射缓冲读/写功能,逐字节COPY一个12兆的文件,(这里牵涉到读和写):

耗用时间(秒)
RandomAccessFileRandomAccessFile95.848
BufferedInputStream +DataInputStreamBufferedOutputStream +DataOutputStream2.935
BufferedRandomAccessFileBufferedOutputStream +DataOutputStream2.813
BufferedRandomAccessFileBufferedRandomAccessFile2.453
BufferedRandomAccessFile优BufferedRandomAccessFile优2.197
BufferedRandomAccessFile完BufferedRandomAccessFile完0.401
MappedByteBuffer+RandomAccessFileMappedByteBuffer+RandomAccessFile1.209

确实不错,看来JDK1.4比1.3有了极大的进步。如果以后采用1.4版本开发软件时,需要对文件进行随机访问,建议采用MappedByteBuffer+RandomAccessFile的方式。但鉴于目前采用JDK1.3及以前的版本开发的程序占绝大多数的实际情况,如果您开发的JAVA程序使用了RandomAccessFile类来随机访问文件,并因其性能不佳,而担心遭用户诟病,请试用本文所提供的BufferedRandomAccessFile类,不必推翻重写,只需IMPORT 本类,把所有的RandomAccessFile改为BufferedRandomAccessFile,您的程序的性能将得到极大的提升,您所要做的就这么简单。

未来的考虑

读者可在此基础上建立多页缓存及缓存淘汰机制,以应付对随机访问强度大的应用。


已标记关键词 清除标记
相关推荐
<p> <b><span style=&quot;background-color:#FFE500;&quot;>【超实用课程内容】</span></b> </p> <p> <br /> </p> <p> <br /> </p> <p> 本课程内容包含讲解<span>解读Nginx的基础知识,</span><span>解读Nginx的核心知识、带领学员进行</span>高并发环境下的Nginx性能优化实战,让学生能够快速将所学融合到企业应用中。 </p> <p> <br /> </p> <p style=&quot;font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> <b><br /> </b> </p> <p style=&quot;font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> <b><span style=&quot;background-color:#FFE500;&quot;>【课程如何观看?】</span></b> </p> <p style=&quot;font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> PC端:<a href=&quot;https://edu.csdn.net/course/detail/26277&quot;><span id=&quot;__kindeditor_bookmark_start_21__&quot;></span></a><a href=&quot;https://edu.csdn.net/course/detail/27216&quot;>https://edu.csdn.net/course/detail/27216</a> </p> <p style=&quot;font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> 移动端:CSDN 学院APP(注意不是CSDN APP哦) </p> <p style=&quot;font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> 本课程为录播课,课程永久有效观看时长,大家可以抓紧时间学习后一起讨论哦~ </p> <p style=&quot;font-family:&quot;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> <br /> </p> <p class=&quot;ql-long-24357476&quot; style=&quot;font-family:&quot;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> <strong><span style=&quot;background-color:#FFE500;&quot;>【学员专享增值服务】</span></strong> </p> <p class=&quot;ql-long-24357476&quot; style=&quot;font-family:&quot;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> <b>源码开放</b> </p> <p class=&quot;ql-long-24357476&quot; style=&quot;font-family:&quot;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 </p> <p class=&quot;ql-long-24357476&quot; style=&quot;font-family:&quot;color:#3A4151;font-size:14px;background-color:#FFFFFF;&quot;> 下载方式:电脑登录<a href=&quot;https://edu.csdn.net/course/detail/26277&quot;></a><a href=&quot;https://edu.csdn.net/course/detail/27216&quot;>https://edu.csdn.net/course/detail/27216</a>,播放页面右侧点击课件进行资料打包下载 </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p>
<p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>会用Python分析金融数据 or 金融行业会用Python</span></strong> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>职场竞争力更高</span></strong> </p> <p> <br /> </p> <p> <img src=&quot;https://img-bss.csdnimg.cn/202012231042221925.png&quot; alt=&quot;&quot; /> </p> <p> <br /> </p> <p> <br /> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>Python金融数据分析入门到实战</span></strong> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>Get&radic;金融行业数据分析必备技能</span></strong> </p> <p> <img src=&quot;https://img-bss.csdnimg.cn/202012231042438069.png&quot; alt=&quot;&quot; /> </p> <p> <br /> </p> <p> <br /> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>以股票量化交易为应用场景</span></strong> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>完成技术指标实现的全过程</span></strong> </p> <p> <br /> </p> <p> <span style=&quot;font-size:14px;&quot;>课程选取股票量化交易为应用场景,由股票数据的获取、技术指标的实现,逐步进阶到策略的设计</span><span style=&quot;font-size:14px;&quot;>和回测,由浅入深、由技术到思维地为同学们讲解Python金融数据分析在股票量化交易中的应用</span><span style=&quot;font-size:14px;&quot;>。</span> </p> <p> <br /> </p> <p> <span style=&quot;font-size:14px;&quot;><br /> </span> </p> <p> <img src=&quot;https://img-bss.csdnimg.cn/202012231043183686.png&quot; alt=&quot;&quot; /> </p> <p> <br /> </p> <p> <br /> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>以Python为编程语言</span></strong> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>解锁3大主流数据分析工具</span></strong> </p> <p> <br /> </p> <p> <span style=&quot;font-size:14px;&quot;>Python做金融具有先天优势,课程提取了Python数据分析工具NumPy、Pandas及可视化工具</span><span style=&quot;font-size:14px;&quot;>Matplotlib的关键点详细讲解,帮助同学掌握数据分析的关键技能。</span> </p> <p> <img src=&quot;https://img-bss.csdnimg.cn/202012231043472858.png&quot; alt=&quot;&quot; /> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;><br /> </span></strong> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;>2大购课福利</span></strong> </p> <p> <strong><span style=&quot;font-size:16px;color:#003399;&quot;><br /> </span></strong> </p> <p> <img src=&quot;https://img-bss.csdnimg.cn/202012300628195864.png&quot; alt=&quot;&quot; /> </p>
目 录 第1章 Java语言概述 1 实例001 输出&ldquo;Hello World&rdquo; 2 实例002 输出控制台传递的参数 2 实例003 输出由&ldquo;*&rdquo;组成的三角形 3 实例004 输出符号表情 5 第2章 Eclipse开发工具 6 实例005 下载并运行Eclipse工具 7 实例006 为Eclipse安装汉化包 8 实例007 使用Eclipse注释代码 10 实例008 使用Eclipse格式化代码 11 实例009 安装WindowBuilder插件 12 实例010 开发计算器界面 14 第3章 Java语言基础 15 实例011 输出错误信息与调试信息 16 实例012 从控制台接收输入字符 16 实例013 重定向输出流实现程序日志 17 实例014 自动型转换与强制型转换 19 实例015 加密可以这样简单(位运算) 20 实例016 用三元运算符判断奇数和偶数 21 实例017 不用乘法运算符实现2&times;16 22 实例018 实现两个变量的互换 (不借助第3个变量) 23 第4章 流程控制 25 实例019 判断某一年是否为闰年 26 实例020 验证登录信息的合法性 27 实例021 为新员工分配部门 28 实例022 用switch语句根据消费 金额计算折扣 29 实例023 判断用户输入月份的季节 31 实例024 使用while循环语句与自增 运算符循环遍历数组 33 实例025 使用for循环输出杨辉三角形 34 实例026 使用嵌套循环在控制台上 输出九九乘法表 35 实例027 使用while循环计算1+ 1/2!+1/3!&hellip;1/20! 36 实例028 使用for循环输出空心的菱形 38 实例029 终止循环体 39 实例030 循环体的过滤器 41 第5章 数组及其常用操作 43 实例031 获取一维数组的最小值 44 实例032 将二维数组中的行列互换 45 实例033 利用数组随机抽取幸运观众 47 实例034 用数组设置JTable表格的 列名与列宽 49 实例035 使用按钮控件数组实现 计算器界面 51 实例036 通过复选框控件数组实现 添加多个复选框控件 52 实例037 使用选择排序法对数组排序 53 实例038 使用冒泡排序法对数组排序 55 实例039 使用快速排序法对数组排序 57 实例040 使用直接插入法对数组排序 59 实例041 使用sort()方法对数组排序 61 实例042 反转数组中元素的顺序 63 第6章 面向对象入门 65 实例043 自定义图书 66 实例044 温度单位转换工具 67 实例045 成员变量的默认初始化值 68 实例046 单例模式的应用 69 实例047 汉诺塔问题求解 70 实例048 编写同名的方法 71 实例049 构造方法的应用 72 实例050 统计图书的销售量 73 实例051 两只完全相同的宠物 74 实例052 重新计算对象的哈希码 76 实例053 使用字符串输出对象 77 实例054 Java对象的假克隆 78 实例055 Java对象的浅克隆 80 实例056 Java对象的深克隆 82 实例057 序列化与对象克隆 84 实例058 深克隆效率的比较 87 第7章 面向对象进阶 89 实例059 经理与员工的差异 90 实例060 重写父中的方法 92 实例061 计算几何图形的面积 93 实例062 简单的汽车销售商场 95 实例063 使用Comparable接口自定 义排序 96 实例064 策略模式的简单应用 98 实例065 适配器模式的简单应用 100 实例066 普通内部的简单应用 102 实例067 局部内部的简单应用 103 实例068 匿名内部的简单应用 104 实例069 静态内部的简单应用 105 实例070 实例化Class的几种方式 107 实例071 查看的声明 108 实例072 查看的成员 110 实例073 查看内部信息 112 实例074 动态设置的私有域 113 实例075 动态调用中方法 115 实例076 动态实例化 116 实例077 创建长度可变的数组 117 实例078 利用反射重写toString()方法 119 第8章 字符串与包装 121 实例079 将数字格式化为货币字符串 122 实例080 货币金额大写格式 123 实例081 String格式化当前日期 125 实例082 字符串大小写转换 126 实例083 字符与Unicode码
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页