极*Java速成教程 - (8)
Java高级特性
注解
注解可以在代码之外添加更多的信息,更加完整地描述程序,帮助编译器进行工作,或者实现某些特定的Java代码之外的功能。
注解可以简化某些重复的流程,自动化那些过程。
注解的使用
注解的使用与其他修饰符的使用没有区别。Java提供了三种标准注解:
- @Override
表明该方法将要覆盖父类的方法,当方法签名与被覆盖方法不同时,编译器就会报错。 - @Deprecated
如果开发者使用了被注解的元素,那么编译器就会发出警告。 - @SuppressWarnings
关闭不当的编译器警告信息。
注解的定义
在使用注解前,需要对注解进行定义。注解不支持继承,注解的定义像一个空的接口,使用@interface
的修饰,定义注解时,上方需要一些元注解。
元注解有四种,用于注解其他的注解:
- @Target 用于定义你的注解应用于什么地方,比如一个方法或者一个域
- CONSTRUCTOR 构造器的声明
- FIELD 域声明
- LOCAL_VARIABLE 局部变量声明
- METHOD 方法声明
- PACKAGE 包声明
- PARAMETER 参数声明
- TYPE 类、接口(包括注解类型)或enum声明
- @Retention 用来定义注解在哪一个级别可以用,如源代码中、类文件中或运行时。
- SOURCE 注解将被编译器丢弃
- CLASS 注解在class文件中可用,但会被VM丢弃
- RUNTIME VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
- @Documented 将此注解包含在Javadoc中
- @Inherited 允许子类继承父类中的注解
注解中可以包含一些元素以表示某些值,如果没有元素,那么这种注解叫标记注解。注解的元素可以有默认值。
注解的定义和使用如下:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
}
import java.util.*;
public class PasswordUtils {
@UseCase(id = 47, description =
"Passwords must contain at least one numeric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
}
在设置了注解后,还需要创建注解处理器,对注解进行处理。可以使用类的getDeclaredMethods()获取本类除继承以外的所有方法,getAnnotation()方法获取指定类型的方法注解。
注解元素
注解中可以使用注解元素来记录注解信息。
注解中可以使用如下类型:
- 所有基本类型
- String
- Class
- enum
- Annotation
- 以上类型的数组
元素必须有默认值,否则就必须在使用注解时提供值。对于非基本类型元素,不能以null值为其值(但是空字符串""
和特定含义的负值是允许的)
Java的apt工具
在Java中,可以使用apt工具调用注解处理器,使其进行针对源代码的注解处理。必须提供一个工厂类或者工厂类的路径。
但在Java7以上,推荐使用javax.annotation.processing工具进行注解处理器的开发。相关内容参考注解处理器详解
并发
并行的方法
使用runnable接口
线程可以驱动任务,任务可以通过实现Runnable接口并编写run()方法来描述。
将Runnable对象提交给Thread构造器,可以创建一个线程来驱动任务。然后使用Thread类的start()方法,就可以启动任务。
通过java.util.concurrent包中的Executor类,可以对Thread对象进行管理。
通过newCachedThreadPool()方法创建线程池,然后对ExecutorService对象调用execute()方法注册任务,即可进行并行处理。调用shutdown()方法即可停止接受新任务。
除了CachedThreadPool,还有FixedThreadPool等线程池可以使用,FixedThreadPool可以一次性预先执行代价高昂的线程分配任务,同时对线程数量也可以进行限制。SingleThreadExecutor同时只会运行一个线程,这个执行器会序列化提交的任务,同时维护一个任务队列,依次执行。
在所有线程池中,在现有线程可能的情况下,都会被自动复用。
使用Callable接口
通过实现Callable接口,类也可以并发执行,通过call()方法可以在任务完成后返回一个返回值。这种方法必须使用ExecutorService对象的submit()方法提交任务。
submit()方法会产生Future对象,该对象可以用isDone()方法查询是否已经完成,当任务完成时,可以使用get()方法获取返回结果。get()方法同时也是一个阻塞方法。
可以在类的内部,通过内部类的方法继承Thread类或者是实现runnable接口,达到多线程的效果。
并行的控制
通过Thread.sleep()方法或者是TimeUnit.MILLISECONDS.sleep()方法,可以使线程休眠。
在run()方法内(在构造器中设置无效)使用线程对象的setPriority()方法可以设置线程优先级,getPriority()方法可以获取线程优先级,JDK的优先级和操作系统优先级并不对应,可移植的方法是使用Thread.MIN_PRIORITY,Thread.MAX_PRIORITY和Thread.NORM_PRIORITY。
使用yield()可以暗示在线程调度中可以做出让步(并不保证实现)。
在线程启动前,使用Thread对象的setDeamon()方法可以设置线程为后台线程。通过实现ThreadFactory接口,可以在接口内的newThread()方法中对创建的Thread进行setDeamon()设置。可以通过isDeamon()查询线程是否为后台线程。
对线程对象调用join()方法,可以将本线程挂起,直到超时或目标线程t结束才恢复。interrupt()方法会中断join,并且设置中断标志。isInterrupted()方法根据中断标志返回值,然而中断时会抛出异常,异常被捕获时会清理这个标志。
并行的异常处理
可以通过在ThreadFactory类中通过setUncaughtExceptionHandler()方法将一个实现了Thread.UncaughtExceptionHandler接口的异常处理器附加到线程上,如果线程抛出异常,那就交由异常处理器进行处理。还可以使用setDefaultUncaughExceptionHandler()方法设置线程的默认异常处理器。
并行的竞争控制
可以通过synchronized关键词对共享资源进行修饰,Java即可自动检查资源是否有锁,是否可用。在使用synchronized关键字时,域应该时private的,否则关键字不能保证其他任务不会访问域造成冲突。
还可以通过synchronized关键词对代码块进行同步控制,语法如下,this可替换为进行同步的对象:
synchronized(this){
XXX;
}
还可以使用java.util.concurrent类库中的locks类显式地加锁。lock类可以通过lock();tryLock()和unlock()方法加锁去锁。ReentrantLock允许尝试获取但是最终没有获取锁,当没有获取到锁时可以离开进行其他处理而不是等待。
原子性和易变性
long和double以外的基本数据类型的简单操作具有原子性,对long和double变量加以volatile修饰,可以使其具有赋值和返回操作的原子性。
volatile保证所修饰的对象是在所有处理器缓存和主存中是同步的,这对于多个线程同时访问一个对象很有意义。
除非是真正的专家,否则不要依赖原子性。
使用原子类
AtomicInteger等相关类等特殊原子性变量类可以保证原子性,但是被设计用来构建java.util.concurrent中的类,因此尽可能不要用,用了也要确保不会出现其他问题,依赖于锁更安全。
线程本地储存
使用ThreadLocal类可以为每个线程储存其独一份的对象,因此不会出现竞争。ThreadLocal类常当作静态域储存。
结束任务
在进程任务中,有时候不得不突然性地打断任务,可以(但是最好不要)使用Thread的interrupt()或interrupted()方法,也可以使用Executor的shutdownNow()方法关闭执行器上的所有线程任务,或者通过持有任务的Future对象,调用其cancel()方法打断执行器上某一个线程的任务。
但是I/O任务和synchronized块上的等待不可打断。此时为了中断阻塞的线程,只能通过关闭底层资源来释放锁。使用ReentrantLock锁可以被打断。
并行任务的协作
可以通过wait()和notify()/notifyAll()来实现进程间的握手问题。Java还提供了具有await()和signal()方法的Condition对象,通过锁上的Condition对象进行协作通信,这是一种更安全的方式。
当线程wait()时,将会释放对象的锁。而搭配使用notifyAll()的时候,只有等待特定锁的任务才会被唤醒。
在进行协作时,必须对一个特定的检查条件来对wait()操作条件进行检查,否则可能因为操作顺序的原因导致死锁的发生。
while(Something){
wait()
}
同步队列
java.util.concurrent.BlockingQueue接口提供了同步队列,任何时刻都只允许一个任务插入或移除元素,这个接口有LinkedBlockingQueue和ArrayBlockingQueue(固定尺寸)等实现。
管道
通过使用PipedWriter(允许任务向管道写)和PipedReader(允许不同任务从管道中读)类,可以通过管道,让多个线程从中进行通信。
没有数据传输时,管道自动阻塞。管道I/O可以被interrupt中断,这一点不同与系统I/O。
一些关于并发的支持
CountDownLatch
这个类可以设置一个计数,线程每次调用countDown()方法都会使计数-1,而await()方法在计数归零前会持续阻塞。
CyclicBarrier
这个类提供一个计数功能,该类的对象进行await()可以使计数-1,当计数归零时,CyclicBarrier会执行一次某一特定任务,然后await()所在的线程被唤醒。
DelayQueue
该类可以存放实现了Delayed接口的对象,该对象有一个延迟时间,只有在到期时才能从队列中取走,到期时间越长的排的越前。如果没有到期的,那么队列就没有头元素,poll()将返回空。
PriorityBlockingQueue
优先级队列,可以进行阻塞性的读取操作。
ScheduledExecutor
ScheduledThreadPoolExecutor类提供了schedule()(在延迟后运行一次任务)和scheduleAtFixedRate()(每隔一段时间重复执行任务)方法。
Semaphore
信号量机制,最多可以签发n个许可,允许n个任务同时访问这个资源,当没有资源时会进行阻塞。可以通过acquire()方法获取许可,通过release()方法解锁许可。
Exchanger
该类可以对两个线程持有的对象进行互换。在执行exchange()方法之前两个进程是互斥等待的。
免锁容器
CopyOnWriteArrayList在写入的时候创建原数组的副本进行写入,写入完成后使用一个原子性的操作将新的数组换入,不影响在写的同时读数据。
CopyOnWriteArraySet基于CopyOnWriteArrayList实现免锁行为
ConcurrentHashMap和ConcurrentLinkedQueue使用了类似计数,可以并发读取和写入,但是容器中只有部分内容而不是整个容器能被复制和修改,在修改完成前,读取者依然不能看到它们。
乐观锁
针对Aromic对象,可以执行decreamentAndGet()这样的原子方法,但是某些类允许使用compareAndSet()这样的方法,该方法会乐观地认为在操作期间没有其他人修改当前对象,该方法会同时提交新值和旧值,如果旧值不同,那么就需要开发者进行一些相应的处理了。
ReadWriteLock
读写锁对不频繁写入但是频繁读取的情况进行了优惠,可以同时拥有多个读取者,只要他们不写入即可,但是如果有人获得了写锁,那么在写锁释放前所有读者都不可以访问这个对象。
极*Java速成教程 - (8)的更多相关文章
- 极*Java速成教程 - (1)
序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...
- 极*Java速成教程 - (2)
Java语言基础 Java的一切都是以对象为基础,对象是生是死的生命周期由虚拟机管理,但是在创生和消亡阶段,需要我们去管理这个类怎么生,怎么死.我们也以此为契机,慢慢接触Java的诸多细节和具体实现. ...
- 极*Java速成教程 - (7)
Java高级特性 数组 在Java中,数组是一串连续的,不可改变长度的,对象被固定的,类型固定的连续空间.数组中的随机访问非常迅速,但为了速度放弃了灵活性.而效率也是数组最大的优点. 在使用泛型的容器 ...
- 极*Java速成教程 - (6)
Java高级特性 String String是Java中的字符串类型,字符串类型在内存中是一个不可变的对象.如果要对字符串对象进行修改,如果是较少的修改可以使用+运算符,Java会自动进行优化,但如果 ...
- 极*Java速成教程 - (5)
Java语言基础 容器 这个世界是有序的,将Java对象零散地放到内存中是不符合世界常理的,特别是有一大组相似的甚至不知道有多少数据的时候.把Java对象装进盒子里可以有序收纳,这个盒子就叫容器. 初 ...
- 极*Java速成教程 - (4)
Java语言基础 多态 多态是面向对象的一大重要特性,如果说封装是隐藏一个类怎么做,继承是确定一系列的类做什么,那多态就是通过手段去分离做什么和怎么做. 向上转型与收窄 在开发者将一类事物封装成类以后 ...
- 极*Java速成教程 - (3)
Java语言基础 访问权限控制 Java是一个面向对象的语言,当你不是它所设计的要面向的对象时,它就不会给你看你不该看到的东西,也就是"访问权限控制". 亲疏有别,才能权限控制 包 ...
- 极·Java速成教程 - (1)
序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...
- Java IO教程
1 Java IO 教程 2 Java IO 概述 3 Java IO: 文件 4 Java IO: 管道 5 Java IO: 网络 6 Java IO: 字节和字符数组 7 Java IO: S ...
随机推荐
- 【SpringBoot】spring-session-data-redis 解决集群环境下session共享
为什么会产生Session共享问题 集群情况下,session保存在各自的服务器的tomcat中,当分发地址至不同服务时,导致sesson取不到,就会产生session共享问题. 解决方案 负载均 ...
- 1222/2516. Kup
题目描述 Description 首先你们得承认今天的题目很短很简洁... 然后,你们还得承认接下来这个题目的描述更加简洁!!! Task:给出一个N*N(1≤N≤2000)的矩阵,还给出一个整数K. ...
- caffe 安装
安装caffe 拉取镜像 nvidia/cuda:9.0-cudnn7-devel-centos7 1,换源 安装https://blog.csdn.net/tuomen5867/article/de ...
- 2018百度之星初赛B轮 p1m2
p1m2 Accepts: 954 Submissions: 4063 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/1310 ...
- c++ 初学者 慢慢成长中
C++书籍推荐 从上往下 Essential C++ C++ Primer 中文版 Effeetive C++ More Effeetive C++ C++ 标准程序库 深度探索c++对象模型 C11
- Oracle-SQL程序优化4
从事一年DBA工作,经验尚浅,但是遇到问题总还是能够解决,今天就谈下我遇到的一个比较奇葩的问题. 运维人员告知我下午过后ETL一直卡住没有继续,那时我以为又是什么兼容性问题引起的,就重跑一下ETL,谁 ...
- 配置Nginx和Apache允许指定域名CORS跨域访问
前后端分离开发,导致前端项目需要跨域请求后端接口,解决方法有很多,本文只介绍两个: 1. 修改后端程序代码实现允许跨域请求 2. 修改服务器配置文件实现允许跨域请求 正文: 方法1:修改后端程序代码实 ...
- 地图服务 纬度、经度对应坐标轴x,y
记下,供自己参考,中国地区的经纬度,经度大,纬度小 如上海经纬度为:(经度, 纬度)(y, x)(lon, lat) 121.48 31.22 纬度---lat----x轴 经度---lon---y轴
- shell命令别名
~/.bashrc文件 [root@linuxzgf ~]# vi ~/.bashrc 在alias cp='cp -i'前加上"#"注释,重新登录即可实现复 ...
- VS2008重置默认环境
Microsoft Visual Studio 2008 -> Visual Studio Tools -> Visual Studio 2008命令提示 进入Common7\IDE,然后 ...