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)的更多相关文章

  1. 极*Java速成教程 - (1)

    序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...

  2. 极*Java速成教程 - (2)

    Java语言基础 Java的一切都是以对象为基础,对象是生是死的生命周期由虚拟机管理,但是在创生和消亡阶段,需要我们去管理这个类怎么生,怎么死.我们也以此为契机,慢慢接触Java的诸多细节和具体实现. ...

  3. 极*Java速成教程 - (7)

    Java高级特性 数组 在Java中,数组是一串连续的,不可改变长度的,对象被固定的,类型固定的连续空间.数组中的随机访问非常迅速,但为了速度放弃了灵活性.而效率也是数组最大的优点. 在使用泛型的容器 ...

  4. 极*Java速成教程 - (6)

    Java高级特性 String String是Java中的字符串类型,字符串类型在内存中是一个不可变的对象.如果要对字符串对象进行修改,如果是较少的修改可以使用+运算符,Java会自动进行优化,但如果 ...

  5. 极*Java速成教程 - (5)

    Java语言基础 容器 这个世界是有序的,将Java对象零散地放到内存中是不符合世界常理的,特别是有一大组相似的甚至不知道有多少数据的时候.把Java对象装进盒子里可以有序收纳,这个盒子就叫容器. 初 ...

  6. 极*Java速成教程 - (4)

    Java语言基础 多态 多态是面向对象的一大重要特性,如果说封装是隐藏一个类怎么做,继承是确定一系列的类做什么,那多态就是通过手段去分离做什么和怎么做. 向上转型与收窄 在开发者将一类事物封装成类以后 ...

  7. 极*Java速成教程 - (3)

    Java语言基础 访问权限控制 Java是一个面向对象的语言,当你不是它所设计的要面向的对象时,它就不会给你看你不该看到的东西,也就是"访问权限控制". 亲疏有别,才能权限控制 包 ...

  8. 极&#183;Java速成教程 - (1)

    序言 众所周知,程序员需要快速学习新知识,所以就有了<21天精通C++>和<MySQL-从删库到跑路>这样的书籍,Java作为更"高级"的语言也不应该落后, ...

  9. Java IO教程

    1  Java IO 教程 2 Java IO 概述 3 Java IO: 文件 4 Java IO: 管道 5 Java IO: 网络 6 Java IO: 字节和字符数组 7 Java IO: S ...

随机推荐

  1. jvm——参数解释

    https://www.oracle.com/technetwork/java/tuning-139912.html#section4.2.5 https://docs.oracle.com/java ...

  2. Python 变量类型 Ⅱ

    Python字符串 字符串或串(String)是由数字.字母.下划线组成的一串字符. 一般记为 : s="a1a2···an"(n>=0) 它是编程语言中表示文本的数据类型. ...

  3. Javascript高级程序设计第三版-笔记

    1.JS数值最大值最小值: >Number.MIN_VALUE <5e-324 >Number.MAX_VALUE <1.7976931348623157e+308 判断数值是 ...

  4. Ubuntu16.04 同时连接无线网络和以太网端口

    背景: 激光雷达VLP16通过以太网线连接电脑.在本博客所述的设置之前,一旦连接以太网线,本机(dell笔记本)的无线网络立即断开,即无法同时连接无线网络和以太网端口. 问题查找: 命令行  $ ip ...

  5. nginx报错 nginx: [alert] kill(25903, 1) failed (3: No such process)

    当nginx 中报错 时 nginx报错 nginx: [alert] kill(25903, 1) failed (3: No such process) 通过在nginx/sbin,目录下 运行命 ...

  6. 【转】jqprint打印时自定义页眉页脚

    需求:自定义页眉,实现打印时分页时每页页眉都显示相同的信息 打印所用插件jqprint 解决方法: <div class="divHeader"> <span s ...

  7. pycharm selenium 安装firefox驱动和Google驱动教程

    一.下载Firefox浏览器或Google浏览器 下载渠道有很多,直接下载最新版的就可以了. 二.下载驱动 Firefox驱动 地址:https://github.com/mozilla/geckod ...

  8. Springboot application 本地HTTPS配置

    使用keytool 命令,生成一个数字证书: keytool -genkey -alias tomcathttps -keyalg RSA -keysize 2048 -keystore key.p1 ...

  9. [LeetCode]-DataBase-Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  10. Java日期时间格式转换

    1.Date转String 将日期格式化成指定的格式 public static String stampToDate(Date date) { SimpleDateFormat simpleDate ...