🦥 File对象
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
 
文件是非常重要的存储方式,Java的标准库java.io提供了File对象来操作文件和目录。
要构造一个File对象,需要传入文件路径:
构造File对象时,既可以传入绝对路径,也可以传入相对路径。绝对路径是以根目录开头的完整路径:
传入相对路径时,相对路径前面加上当前目录就是绝对路径:
可以用.表示当前目录,..表示上级目录。
 
Map和Set
type
status
date
slug
summary
tags
category
icon
password
Property
 
JavaScript的默认对象表示方式{}可以视为其他语言中的MapDictionary的数据结构,即一组键值对。
但是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。
 

Map

Map是一组键值对的结构,具有极快的查找速度。
举个例子,假设要根据同学的名字查找对应的成绩,如果用Array实现,需要两个Array
给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,Array越长,耗时越长。
如果用Map实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。用JavaScript写一个Map如下:
初始化Map需要一个二维数组,或者直接初始化一个空MapMap具有以下方法:
🦛 wait和notify
type
status
date
slug
summary
tags
category
icon
password
Property
 
在Java程序中,synchronized解决了多线程竞争的问题。例如,对于一个任务管理器,多个线程同时往队列中添加任务,可以用synchronized加锁:
上述代码看上去没有问题:getTask()内部先判断队列是否为空,如果为空,就循环等待,直到另一个线程往队列中放入了一个任务,while()循环退出,就可以返回队列的元素了。
但实际上while()循环永远不会退出。因为线程在执行while()循环时,已经在getTask()入口获取了this锁,其他线程根本无法调用addTask(),因为addTask()执行条件也是获取this锁。
因此,执行上述代码,线程会在getTask()中因为死循环而100%占用CPU资源。
如果深入思考一下,我们想要的执行效果是:
  • 线程1可以调用addTask()不断往队列中添加任务;
  • 线程2可以调用getTask()从队列中获取任务。如果队列为空,则getTask()应该等待,直到队列中至少有一个任务时再返回。
因此,多线程协调运行的原则就是:当条件不满足时,线程进入等待状态;当条件满足时,线程被唤醒,继续执行任务。
对于上述TaskQueue,我们先改造getTask()方法,在条件不满足时,线程进入等待状态:
嵌入JS的三种方式
type
status
date
slug
summary
tags
category
icon
password
Property
 
 

行间事件

JS是一门事件驱动型的编程语言,依靠事件去驱动,然后执行对应的程序。
在JS中有很多事件,其中有一个事件叫做:鼠标单击(click)。并且任何事件都会对应一个事件句柄叫做:onclick。
事件和事件句柄的区别是:事件句柄是在事件单词前添加一个on,而事件句柄是以HTML标签的属性存在的
 
onclick="js代码",执行原理是什么? 页面打开的时候,js代码并不会执行,只是把这段JS代码注册到按钮的click事件上了。等这个按钮发生click事件之后,注册在onclick后面的js代码会被浏览器自动调用。
 
怎么使用JS代码弹出消息框? 在JS中有一个内置的对象叫做window,全部小写,可以直接拿来使用,window代表的是浏览器对象 window对象有一个函数叫做:alert,用法是:window.alert("消息");这样就可以弹窗了
 
🦛 中断线程
type
status
date
slug
summary
tags
category
icon
password
Property
 
如果线程需要执行一个长时间任务,就可能需要能中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后结束执行run()方法,使得自身线程能立刻结束运行。
假设从网络下载一个100M的文件,如果网速很慢,用户等得不耐烦,就可能在下载过程中点“取消”,这时,程序就需要中断下载线程的执行。
中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。
仔细看上述代码,main线程通过调用t.interrupt()方法中断t线程,但是要注意,interrupt()方法仅仅向t线程发出了“中断请求”,至于t线程是否能立刻响应,要看具体代码。而t线程的while循环会检测isInterrupted(),所以上述代码能正确响应interrupt()请求,使得自身立刻结束运行run()方法。
如果线程处于等待状态,例如,t.join()会让main线程进入等待状态,此时,如果对main线程调用interrupt()join()方法会立刻抛出InterruptedException,因此,目标线程只要捕获到join()方法抛出的InterruptedException,就说明有其他线程对其调用了interrupt()方法,通常情况下该线程应该立刻结束运行。
main线程通过调用t.interrupt()从而通知t线程中断,而此时t线程正位于hello.join()的等待中,此方法会立刻结束等待并抛出InterruptedException。由于我们在t线程中捕获了InterruptedException,因此,就可以准备结束该线程。在t线程结束前,对hello线程也进行了interrupt()调用通知其中断。如果去掉这一行代码,可以发现hello线程仍然会继续运行,且JVM不会退出。
另一个常用的中断线程的方法是设置标志位。我们通常会用一个running标志位来标识线程是否应该继续运行,在外部线程中,通过把HelloThread.running置为false,就可以让线程结束:
注意到HelloThread的标志位boolean running是一个线程间共享的变量。线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。
图片
type
status
date
slug
summary
tags
category
icon
password
Property
 
 
🦛 ReadWriteLock
type
status
date
slug
summary
tags
category
icon
password
Property
 
ReentrantLock保证了只有一个线程可以执行临界区代码:
但是有些时候,这种保护有点过头。因为我们发现,任何时刻,只允许一个线程修改,也就是调用inc()方法是必须获取锁,但是,get()方法只读取数据,不修改数据,它实际上允许多个线程同时调用。
实际上我们想要的是:允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待:
notion image
使用ReadWriteLock可以解决这个问题,它保证:
  • 只允许一个线程写入(其他线程既不能写入也不能读取);
  • 没有写入时,多个线程允许同时读(提高性能)。
ReadWriteLock实现这个功能十分容易。我们需要创建一个ReadWriteLock实例,然后分别获取读锁和写锁:
把读写操作分别用读锁和写锁来加锁,在读取时,多个线程可以同时获得读锁,这样就大大提高了并发读的执行效率。
🦥 使用Files
type
status
date
slug
summary
tags
category
icon
password
Property
 
从Java 7开始,提供了FilesPaths这两个工具类,能极大地方便我们读写文件。
虽然FilesPathsjava.nio包里面的类,但他俩封装了很多读写文件的简单方法,例如,要把一个文件的全部内容读取为一个byte[],可以这么写:
如果是文本文件,可以把一个文件的全部内容读取为String
写入文件也非常方便:
此外,Files工具类还有copy()delete()exists()move()等快捷方法操作文件和目录。
最后需要特别注意的是,Files提供的读写方法,受内存限制,只能读写小文件,例如配置文件等,不可一次读入几个G的大文件。读写大型文件仍然要使用文件流,每次只读写一部分文件内容。
 
🦥 IO
type
status
date
slug
summary
tags
category
icon
password
Property
 
IO是指Input/Output,即输入和输出。以内存为中心:
  • Input指从外部读入数据到内存,例如,把文件从磁盘读取到内存,从网络读取数据到内存等等。
  • Output指把数据从内存输出到外部,例如,把数据从内存写入到文件,把数据从内存输出到网络等等。
 
从Java代码来看,输入实际上就是从外部,例如,硬盘上的某个文件,把内容读到内存,并且以Java提供的某种数据类型表示,例如,byte[]String,这样,后续代码才能处理这些数据。
因为内存有“易失性”的特点,所以必须把处理后的数据以某种方式输出,例如,写入到文件。Output实际上就是把Java表示的数据格式,例如,byte[]String等输出到某个地方。
IO流是一种顺序读写数据的模式,它的特点是单向流动。数据类似自来水一样在水管中流动,所以我们把它称为IO流。
 

InputStream / OutputStream

IO流以byte(字节)为最小单位,因此也称为字节流。例如从磁盘读入一个文件,包含6个字节,就相当于读入了6个字节的数据:
notion image
🐼 Collections
type
status
date
slug
summary
tags
category
icon
password
Property
 
注意Collections结尾多了一个s,不是Collection!
Collections是JDK提供的工具类,同样位于java.util包中。它提供了一系列静态方法,能更方便地操作各种集合
一般看方法名和参数就可以确认Collections提供的该方法的功能。例如,对于以下静态方法:
addAll()方法可以给一个Collection类型的集合添加若干元素。因为方法签名是Collection,所以可以传入ListSet等各种集合类型。
 

创建空集合

Collections提供了一系列方法来创建空集合:
  • 创建空List:List<T> emptyList()
  • 创建空Map:Map<K, V> emptyMap()
  • 创建空Set:Set<T> emptySet()