前面介绍的文件I/O,不管是写入文本还是写入对象,文件中的数据基本是原来的模样,用记事本之类的文本编辑软件都能浏览个大概.这么存储数据,要说方便确实方便,只是不够经济划算,原因有二:其一,写入的数据可能存在大量重复的信息,但依原样写到文件的话,无疑保留了不少冗余数据,造成空间浪费:其二,写入的数据多以明文方式保存,容易产生信息泄露,安全性不高.为此Java提供了简单的压缩和解压工具,在将数据写入文件之前,先对数据进行压缩,再将压缩后的结果写到文件:同样读取压缩文件之时,先读出已压缩的数据,再将这…
前面介绍了线程的基本用法,按理说足够一般的场合使用了,只是每次开辟新线程,都得单独定义专门的线程类,着实开销不小.注意到新线程内部真正需要开发者重写的仅有run方法,其实就是一段代码块,分线程启动之后也单单执行该代码段而已.因而完全可以把这段代码抽出来,把它定义为类似方法的一串任务代码,这样能够像调用公共方法一样多次调用这段代码,也就无需另外定义新的线程类,只需命令已有的Thread去执行该代码段就好了.在Java中定义某个代码段,则要借助于接口Runnable,它是个函数式接口,唯一需要实现的…
前面的加减乘除四则运算,计算结果通过等号输出给指定变量,注意此时代码把变量放到等号左边.而在算术课本里,加法运算的完整写法类似于“1+1=2”这样,运算结果应该跟在等号右边.不过代数课本里的方程式存在“x=y+1”的写法,表示等号两边的结果数值是一样的,因此变量放在等号左边也是可以理解的.然而Java编程里的“=”并非数学上的相等涵义,而是一种赋值操作,所谓“赋值”,指的是将某一数值赋给某个变量的过程.计算机程序在运行的时候,不管操作什么类型的数据,都要有处地方保存运算前后的数值.变量在参与计算…
现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的特种官吏Java开发笔记(四)Java帝国的度量衡 第二章 数值变量Java开发笔记(五)数值变量的类型Java开发笔记(六)特殊数字的表达Java开发笔记(七)强制类型转换的风险 第三章 算术运算Java开发笔记(八)五种算术运算符Java开发笔记(九)赋值运算符及其演化Java开发笔记(十)一元…
前面在<Java开发笔记(九)赋值运算符及其演化>中提到,Java编程中的等号“=”表示赋值操作,并非数学上的等式涵义.Java通过等式符号“==”表示左右两边相等,对应数学的等号“=”:通过不等符号“!=”表示左右两边不等,对应数学的不等号“≠”.可是一个等式真的就一定成立吗?譬如半斤八两这个成语,用Java等式改写的话变为“半斤==八两”.话说当年秦始皇统一中国,不但推行“书同文.车同轨”,而且也制定了重量单位的换算标准,当时规定十六两为一斤,从此沿用了两千多年.直到公元1959年,为了与…
前面介绍了线程的几种运行方式,不管哪种方式,一旦调用了线程实例的start方法,都会立即启动线程的事务处理.然而某些业务场景在事务执行时间方面有特殊需求,例如期望延迟若干时间之后才开始事务运行,又如期望每隔若干时间依次启动事务处理,如此种种都要求在指定的时间才能启动线程任务,也就是俗称的定时功能.有别于一般的线程,Java为定时功能设计了专门的定时任务TimerTask,以及定时器Timer.其中TimerTask用来描述时刻到达后的事务处理,而Timer用来调度定时任务,包括何时启动定时任务.…
前面介绍了普通线程池的用法,就大多数任务而言,它们对具体的执行时机并无特殊要求,最多是希望早点跑完早点出结果.不过对于需要定时执行的任务来说,它们要求在特定的时间点运行,并且往往不止运行一次,还要周期性地反复运行.由于普通线程池满足不了此类定时运行的需求,因此Java又提供了定时器线程池来实现定时与周期执行任务的功能.普通线程池的工具类名叫ExecutorService,定时器线程池的工具类则叫做ScheduledExecutorService,添加了Scheduled前缀,表示它是一种有计划的…
有些时候,开发者想把程序运行过程中的数据临时保存到文件,可是前面介绍的字符流和字节流,要么用来读写文本字符串,要么用来读写字节数组,并不能直接保存某个对象信息,因为对象里面包括成员属性和成员方法,单就属性而言,每个属性又有各自的数据类型及其具体数值,这些复杂的信息既不能通过字符串表达,也不能通过简单的字节数组表达.虽然现有手段不容易往文件中写入对象信息,但是该想法无疑极具吸引力,倘若能够自如地对文件读写某个对象数据,必定会给程序员的开发工作带来巨大便利,况且内存都能存放对象信息,为何磁盘反而无法…
文件输出流FileOutputStream跟FileWriter同样有个毛病,每次调用write方法都会直接写到磁盘,使得频繁的写操作性能极其低下.正如FileWriter搭上了缓存兄弟BufferedWriter那样,FileOutputStream也有自己的缓存兄弟BufferedOutputStream,这个缓存输出流的用法与缓存写入器非常相似,主要体现在如下三点:1.每次创建缓存输出流对象之前,都要先构建文件输出流对象,然后据此构建缓存输出流对象:2.它的write方法先把数据写到缓存,…
早期的编程语言为了节约计算机的内存,给数字变量定义了各种存储规格的数值类型,比如字节型byte只占用一个字节大小,短整型short占用两个字节大小,整型int占用四个字节大小,长整型long占用八个字节大小.但是长整型也只能表达到负2的63次方~2的63次方-1,超出这个范围的巨大整数,竟连long类型也放不下.何况现在不管手机还是电脑的内存都是以GB计量,因此原先锱铢计较几个字节的数值类型便不合时宜了.为此Java又设计了一种大整数类型BigInteger,这个BigInteger能够表示任意…
Date是Java最早的日期工具,编程中经常通过它来获取系统的当前时间.当然使用Date也很简单,只要一个new关键字就能创建日期实例,就像以下代码示范的那样: // 创建一个新的日期实例,默认保存的是系统时间 Date date = new Date(); 有了这个日期实例,再来调用getYear(获取年份).getMonth(获取月份).getDate(获取日子).getDay(获取星期几).getHours(获取时钟).getMinutes(获取分钟).getSeconds(获取秒钟)等方…
前面介绍了如何从Bird类继承而来Swallow类,按道理子类应当继承父类的所有要素,但是对于构造方法来说,Swallow类仅仅继承了Bird类的默认构造方法,并未自动继承带参数的构造方法.如果子类想继续使用父类的其它构造方法,就得自己重写心仪的构造方法.例如老鹰属于鸟类,那么可以编写继承自Bird类的Eagle类,同时要在Eagle类内部重新定义拥有多个输入参数的构造方法,由此得到如下所示的Eagle类代码: //定义了一个继承自鸟类的老鹰类 public class Eagle extend…
前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8以前,接口内部的所有方法都必须是抽象方法”,如此说来,在Java8之后,接口的内部方法也可能不是抽象方法了吗?之所以Java8对接口的定义规则发生变化,是因为原来的接口定义存在先天不足导致的,例如下列几点需求就难以满足:1.Java8以前规定接口的内部方法只能是抽象方法,在该接口的实现类里面全部都要重写.这个规定明显太霸道了,为什么非得所有都重写呢?有的行为分明是通用的,比如呼吸动作,凡是陆上动物都用鼻子呼吸,把新鲜空气吸进去…
前面从泛型方法的用法介绍到了泛型的起源,既然单个方法允许拥有泛化的参数类型,那么一个类也应当支持类级别的泛化类型,例如各种容器类型ArrayList.HashMap等等.一旦某个类的定义代码在类名称后面添加“<T>”这种泛型声明,该类就变成了泛型类(也称模板类).况且泛型类不单单支持一种泛型参数,还支持同时声明多种泛型参数,像“<T>”表示当前类存在唯一一种泛型参数:若想声明当前类拥有两种泛型参数,则可使用“<T, R>”这种以逗号隔开的泛型列表:同时声明三种泛型参数的…
早在介绍多态的时候,曾经提到公鸡实例的性别属性可能被篡改为雌性,不过面向对象的三大特性包含了封装.继承和多态,只要把性别属性设置为private私有级别,也不提供setSex这样的性别修改方法,那么性别属性就被严严实实地封装了起来,不但外部无法修改性别属性,连公鸡类的子类都无法修改.如此一来,公鸡实例的性别属性可谓防护周全,压根不存在被篡改的可能性.但是Java给面向对象留了个后门,也就是反射技术,利用反射技术竟然能够攻破封装的防护网,使得篡改私有属性从理想变成了现实,赶紧来看看反射技术是怎样做…
前面介绍了如何利用Runnable接口构建线程任务,该方式确实方便了线程代码的复用与共享,然而Runnable不像公共方法那样有返回值,也就无法将线程代码的处理结果传给外部,造成外部既不知晓该线程是否已经执行完毕,也不了解该线程的运算结果是什么,总之无法跟踪分线程的行动踪迹.这里显然是不完美的,调用方法都有返回值,为何通过Runnable启动线程就无法获得返回值呢?为此Java又提供了另一种开启线程的方式,即利用Callable接口构建任务代码,实现该接口需要重写call方法,call方法类似r…
每启动一个程序,操作系统的内存中通常会驻留该程序的一个进程,进程包含了程序的完整代码逻辑.一旦程序退出,进程也就随之结束:反之,一旦强行结束进程,程序也会跟着退出.普通的程序代码是从上往下执行的,遇到分支语句则进入满足条件的分支,遇到循环语句总有跳出循环的时候,遇到方法调用则调用完毕仍然返回原处,之后继续执行控制语句或者方法调用下面的代码.总之一件事情接着一件事情处理,前一件事情处理完了才能处理后一件事情,这种运行方式被称作"串行处理".串行处理的代码结构清晰,但同一时刻只能执行某段代…
NIO不但引进了高效的文件通道,而且新增了更加好用的文件工具家族,包括路径组工具Paths.路径工具Path.文件组工具Files.先看路径组工具Paths,该工具提供了静态方法get,输入某个文件的路径字符串,输出该文件路径的路径对象Path.通过get方法获取路径对象的代码示例如下: // 根据指定的文件路径字符串获得对应的Path对象 Path path = Paths.get(mDirName); 有了Path对象之后,就能调用它的各种实例方法了,常见的几个方法说明如下:getParen…
前面介绍了字节缓存的一堆概念,可能有的朋友还来不及消化,虽然文件通道的用法比起传统I/O有所简化,可是平白多了个操控繁琐的字节缓存,分明比较传统I/O更加复杂了.尽管字节缓存享有缓存方面的性能优势,但传统I/O也有缓存输入输出流呀,大家都有缓存机制,凭什么说NIO的文件处理更高效?之所以目前还看不出文件通道的性能优势,是因为前面介绍的仅限于它的基本用法,尚未涉及到高级特性,接下来阐述文件通道的真正杀手锏:使用通道复制文件.复制文件的常规做法很简单,从源文件中读出数据,再将数据写进目标文件.采取文…
前面介绍了文件通道的读写操作,其中用到字节缓存ByteBuffer,它是位于通道内部的存储空间,也是通道唯一可用的存储形式.ByteBuffer有两种构建方式,一种是调用静态方法wrap,根据输入的字节数组生成对应的缓存对象:另一种是调用静态方法allocateDirect,根据输入的数值分配指定大小的空缓存.字节缓存又是一种特殊的存储空间,因为它可能会被多次读写,所以为了有效地控制读写操作,Java给它设计了下列五种概念:容量.当前限制量.当前位置.本次剩余空间.标记位置,分别说明如下:1.容…
前面介绍的各色流式IO在功能方面着实强大,处理文件的时候该具备的操作应有尽有,可流式IO在性能方面不尽如人意,它的设计原理使得实际运行效率偏低,为此从Java4开始增加了NIO技术,通过全新的架构体系带来了可观的性能提升.NIO是“Non-blocking IO”的缩写,意思是非阻塞的IO,与之相对应,传统的流式IO又被称作BIO(“Blocking IO”的缩写),意即阻塞的IO.所谓阻塞与非阻塞,说起来挺拗口,令人不知所云,这都是设计师脑袋短路惹的祸,发明了这么难懂的词汇,害得初学者一脸懵逼…
前面介绍了各种SQL语句的调用过程,虽然例子代码写死了每个SQL串,但是完全可以把查询条件作为方法参数传进来.比如现在想删除某个课程的教师记录,那么在编写删除方法时,就把课程名称作为该方法的一个输入参数.据此编写的方法代码示例如下: // 删除记录 private static void deleteRecord(Statement stmt, String course) throws SQLException { String sql = String.format("delete from…
前面介绍了JSON格式的报文解析,虽然json串短小精悍,也能有效表达层次结构,但是每个元素只能找到对应的元素值,不能体现更丰富的样式特征.比如某个元素除了要传输它的字符串文本,还想传输该文本的类型.字体大小.字体颜色等特征,且这些额外的风格样式与业务逻辑无关,自然不适合为它们单独设立参数字段.倘若采用JSON格式定义包括样式特征在内的文本元素,要么摒弃风格样式这种附加属性,要么将风格样式单列为专门的字段参数,然而不管哪种做法,都未能妥善解决附加属性的表达问题.可见轻量级的JSON格式依然存在力…
前面介绍了如何在窗口上添加一个按钮,可是每个软件界面都包含了许多控件,这些控件又是按照什么规则在界面上排列的呢?仍以按钮为例,假如要在窗口上依次添加多个按钮,那么界面会怎样显示这些按钮?想当然的话,按钮们可能从左往右排列,也可能从上往下排列,也可能后面的按钮在原处覆盖掉前面的按钮,究竟AWT会以哪种方式显示多个按钮,还得具体编码开展实地验证才行.下面便是往程序窗口先后添加五个按钮的代码片段: frame.setSize(400, 120); // 必须设置宽高,否则没有窗体 Panel pane…
Swing的输入框仍然分成两类:单行输入框和多行输入框,但与AWT的同类控件相比,它们在若干细节上有所调整.首先说单行输入框,AWT的单行输入框名叫TextField,平时输入什么字符它便显示什么字符,可一旦调用了setEchoChar方法设置回显字符,TextField马上变成只显示密文字符了.然而尴尬之处在于,设置回显字符之后,就没有办法取消原来的回显设置,输入框对象从此只能傻乎乎地显示密文了.如此一来,程序代码难以判断某个输入框究竟会显示明文还是密文,也无法确定输入框文字的加密与否.这不可…
循着Swing的旧例,JavaFX仍然提供了三种文本输入框,分别是单行输入框TextField.密码输入框PasswordField.多行输入框TextArea.这些输入框都由抽象类TextInputControl派生而来,因此拥有共同的编辑方法,常用的主要有下列两个:setEditable:设置输入框能否编辑.为true表示能够编辑,为false表示不能编辑.setPromptText:设置输入框的提示语,用来提示用户可以输入什么样的文本.文本输入框与文本标签的区别在于,输入框内的文字允许编辑…
前面介绍了处理字符串的常用方法,还有一种分割字符串的场景也很常见,也就是按照某个规则将字符串切割为若干子串.分割规则通常是指定某个分隔符,根据字符串内部的分隔符将字符串进行分割,例如逗号.空格等等都可以作为字符串的分隔符.正好String类型提供了split方法用于切割字符串,只要字符串变量调用split方法,并把分隔符作为输入参数,该方法即可返回分割好的字符串数组.下面的split调用代码例子演示了如何按照逗号和空格切割字符串: // 通过逗号分割字符串 private static void…
前面依次介绍了普通线程池和定时器线程池的用法,这两种线程池有个共同点,就是线程池的内部线程之间并无什么关联,然而某些情况下的各线程间存在着前因后果关系.譬如人口普查工作,大家都知道我国总人口为14亿左右,可是14亿的数目是怎么数出来呢?倘若只有一个人去统计,从小数到老都数不完.好比一个线程老牛破车干不了多少事情,既然如此,不妨多起一些线程呗.于是人口普查工作就由中央分解到各个省份,各省又分派到下面的市县,再由市县分派到更下面的街道或乡镇,每个街道和乡镇统计完本辖区内的人口数量后,分别上报给对应的…
前面介绍了如何使用字符流读写文件,并指出字符流工具的处理局限,进而给出随机文件工具加以改进.随机文件工具除了支持访问文件内部的任意位置,更关键的一点是通过字节数组读写文件数据,采取字节方式比起字符方式有下列两个好处:1.文件长度以字节为单位计量,可以分配等长的字节数组,却无法分配合适长度的字符数组,因此采用字节方式便于从文件中读取数据.2.字符流工具主要以字符为单位处理数据,意味着它适合用来读写文本文件,不适用于二进制文件(包括图片文件.音频文件.视频文件等等),而字节方式不存在此类限制.虽说随…
前面介绍了字符流读写文件的两种方式,包括文件字符流和缓存字符流,但是它们的写操作都存在一个问题:不管是write方法还是append方法,都只能从文件开头写入,而不能追加到文件末尾或者在文件中间某个位置写入.这个问题真不好办,它意味着每次写操作都会覆盖掉原来的文件内容,注意是直接覆盖而非局部修改,可大多数的业务场景需要在原文件基础上追加或者修改的.倘若坚持使用字符流修改文件内容,也不是不可以,那样得把原来的文件内容全部读到某个字符串,再对该字符串进行修改操作,最后把改后的字符串重新写入原文件.这…