Java核心技术

回调:一种常见的程序设计模式,在这种设计模式中,可以指出某种特定事件发生时应该采取的动作。(监听)

1、在指定事件做某事;

2、在指定事件发生时做某事;

对象对于克隆很偏执,如果一个对象请求克隆,但没有实现这个接口,就会生成一个受查异常。

克隆的时候即使使用clone的默认浅拷贝方法可以实现需求,但是还是需要实现Cloneable接口,将clone重新定义为public,再调用super.clone()方法。

深拷贝需要克隆对象中可变的实例域,String类型,对象类型。浅拷贝只是指向一个引用。clone不是很常用,标准库中只有不到5%的类实现了clone。

lambda表达式

背景:1、按指定时间间隔完成工作,需要传递一个带有指定功能的对象。2、按指定规则排序,需要像sort方法传入一个Comparator对象。

都是将一个代码块传递到某个对象(一个定时器,或者一个sort方法),这个代码块会在将来某个时间被调用。但是在java中传递一个代码块并不容易,不能直接传递代码块。java是一个面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码。

lambda表达式是一个可传递的代码块,可以在以后执行一次或者多次。

对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口。

接口,lambda表达式和内部类,lambda表达式的重点是延迟执行。如果想要立即执行的话,完全可以直接执行,而不需使用lambda表达式。使用lambda表达式的原因。

1、在一个单独的线程中运行代码

2、多次运行代码

3、在算法的适当位置运行代码(eg:排序中的有序操作)

4、发生某种情况时执行的代码(监听,定时)

5、只在必要时才运行代码

函数式接口:仅只有一个函数的接口。

proxy代理

代理:某个事物代替另外一个事物去执行某一个动作的过程。

静态代理:程序运行的过程代码由程序员创建或者程序开发工具生成,在编译期间就已经把目标接口,被代理类,代理类的class文件生成。

动态代理:代理类在程序运行时创建。

用途:利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。

1、何时使用

假设有一个表示接口的Class对象,它的确切类型在编译时无法知道。要想构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器。但是,不能实例化一个接口,需要在程序处于运行状态时定义一个新类。

所有的代理类都扩展于Proxy类。一个代理类只有一个实例域——调用处理器,它定义在Proxy的超类中。为了履行代理对象的职责,所需要的任何附加数据的偶必须存储在调用处理器中。

克隆和代理是库设计者和工具构造者感兴趣的高级技术,对应用开发来说,他们并不十分重要。

代理类是在程序运行过程中创建。然而,一旦被创建,就变成了常规类,与虚拟机中的任何其他类没有什么区别。

assert断言

断言可以理解成if,但是如果程序中存在大量的if检查语句,程序运行起来会非常的慢。断言机制就是允许在测试期间向代码中插入一些检查语句。当代码发布时,这些插入的检测语句将会被自动地移走。简而言之就是,断言是会移走的if语句。

问题:在正式发布的时候移走判断语句,那么如果当出现按不常规流程执行代码的时候,程序是不是会出现异常?既然不会出现其他程序执行情况,为什么又要使用断言呢?是不是会多此一举?

java的错误处理机制:1、异常;2、日志;3、断言

att:

1、断言的失败是致命的,不可恢复的错误;

2、断言检查只用于开发和测试阶段;

结论:断言只用于在测试阶段确定程序内部的错误位置;

T泛型

集合

1、集合类的基本接口是Collection

视图

可以类比于数据库中视图的概念,就是将集合类对象中的数据重新映射到一个数据集合中,但这个集合不是一个物理上存在的对象实体,而是对原集合类对象的再映射,数据物理地址未变,只是访问数据的接口变了。

java中的视图,可以说其实就是一个具有限制的集合对象,只不过这里的不是集合对象,而是一个视图对象。

例如:这里有一个Test类

Test[] tests = new Test[10];

List testList = Arrays.asList(tests);这里返回的对象不是ArrayList它是一个视图对象,具有访问底层数组的set和get的方法。但是如果调用改变数组的方法就会抛出异常。所以可以说视图对象可以说是具有限制的集合对象。

利用java类库中的方法我们可以获得不可更改视图,子视图等等,这些视图对于原对象具有不同的操作权限。

并发线程

1、什么是线程

多任务:在同一时刻运行多个程序的能力。

线程:每一个任务称为一个线程,它是线程控制的简称。

多线程程序:可以同时运行一个以上线程的程序称为多线程程序。

多线程和多进程的区别:一个进程由多个线程组成。每个进程拥有自己的一整套变量,而线程则共享数据。

/**

  • 1、不推荐继承Thread类,构造子类的对象,然后调用start方法,因为要将并行运行的任务与运行机制解耦和。

  • 如果有很多任务,要为每个任务创建一个独立的线程所付出的代价太大了。

  • 2、不要调用Thread类和Runnable接口的run方法。直接调用run方法,只会执行同一个线程的任务,而不会启动新线程。

  • 应该调用Thread.start()方法,这个方法将创建一个执行run方法的新线程。

  • Thread(Runnable target) 构造一个新线程,用于调用给定目标的run()方法。

  • void start() 启动这个线程,将引发调用run()方法。这个方法将立即返回,并且新线程将并发运行。

  • void run() 调用关联Runnable的run方法。

Thread类使用Runnable接口初始化,Runnable接口中的run方法是一定要重写覆盖的。

中断线程

当线程的run方法执行最后一条语句,并执行return返回时;或者出现未捕获异常时,线程将终止。(在早期的版本中还有stop函数可以用来终止线程,后来被废弃)。因此没有可以强制终止线程的办法,只有请求中断的办法(interrupt方法)。当调用这个方法时,线程的中断状态将被置位。每个线程都有该状态,都会时刻检查该状态,以判断线程是否被中断。

interrupt,像线程发送中断请求。中断状态被置为true。如果当前线程被sleep阻塞,那么抛出InterruptedException异常。

interrupted,检查当前线程是否被中断,将当前线程的中断状态置为false。

isInterrupted,检查线程是否被中断。

线程被终止的原因:

1、run方法正常退出而自然终止

2、出现了未捕获的异常而意外终止

线程属性

1、线程优先级

2、守护线程(守护线程为其他线程提供服务,eg:计时线程定时发送信号 setDaemon()方法)

3、未捕获异常处理器

可以用官方提供的setDefaultUncaughtExceptionHandler为所有线程按照以恶搞默认的处理器。该处理器可以处理未捕获的异常。

线程组(一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组,但是,也会建立其他的组。现在引入了更好的特性用于线程集合的操作,所以建议不要在自己的程序中使用线程组)

同步

两个即以上的线程需要共享对同一数据的存取。

线程不安全的原因:线程是并发执行的,即是没有顺序的,有可能两个线程同时操作同一个数据,也有可能线程在执行的过程中由于某些原因出现中断,中断出现的话,会导致其他线程对共享数据造成修改,造成数据的丢失。

锁对象

防止代码块受并发机制的干扰的方法:

1、使用synchronized关键字

2、lock()、unlock()

Att:

1、锁——任何时刻都只能有一个线程执行被保护的代码。

2、锁可以管理试图进入被保护代码段的线程。

3、锁可以拥有一个或者多个相关的条件对象。

4、每个条件对象管理那些已经进入了被保护的代码段但是由于某种原因无法被成功执行的线程。

java中的每一个对象都有一个内部锁,如果使用synchronized关键字来定义方法

public synchronized void method(){} 等价于

public void method(){

this.intrinsiLock.lock();

try{}

finally{this.intrinsiLock.unlock();}

}

由锁来管理那些试图进入synchronized方法的进程,由条件对象来管理那些调用wait的线程。如果不满足某条件,则条件对象.await,之后再条件对象.signalAll();(仅单一条件,等价于wait和notifyAll)

内部锁的条件存在一些局限:

1、不能中断一个正在试图获得锁的线程。
2、试图获得锁时不能设置超时时间。
3、每个锁仅仅只有单一的条件,当有多个条件限制时是不够的。

在代码中应该使用Lock和Condition对象,还是同步方法:

1、最好都不要用,而是使用java.util.concurrent包中的机制;
2、如果synchronized适合则使用;
3、特别需要Lock和Condition对象时才使用;

java获得锁的方式:

1、调用synchronized方法
2、进入一个同步阻塞 synchronized(obj) <表明obj对象已经获取锁,如果其他对象再获取,则进入阻塞状态>

监视器概念

背景:在不需要程序员考虑如何加锁的情况下,就可以保证多线程的安全性。

Volatile域

将一个域用关键字volatile修饰,表明该域可能被另一个线程并发更新。

tryLock()方法用来获取锁,获取的到返回true,否则false。

读/写锁:

ReentrantReadWriteLock成员方法.readLock()返回Lock对象;

ReentrantReadWriteLock成员方法.writeLock()返回Lock对象;

线程中被弃用的函数:

stop(天生就不安全,立刻终止线程的运行,会导致对象处于不一致的状态);

suspend(经常导致死锁,线程被挂起之前已经获得锁,那么会导致另外的线程无法获得该锁导致死锁);

resume(),suspend已被弃用,没必要使用resume 将一个挂起的线程复活后继续执行。

阻塞队列

可以理解成缓冲区的形式,使用一个或者多个队列的形式来优雅安全的将其形式化,生产者向队列中插入元素,然后消费者取出。对象由java.util.concurrent包提供。

LinkedBlockingDeque、LinkedBlockingQueue

对应集合list、map等,也有线程安全的集合,由java.util.concurrent提供,比如ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue

对并发散列映射的批操作

在并发场景下使用的map数据集合

其实这部分如果有需要直接看api就好,书中写的杂乱无章没头没尾的,花了时间看也看不懂。

并发集视图

如果想要一个大的线程安全的集而不是映射。并没有ConcurrentHashSet类,可以使用ConcurrentHashMap<K,Boolean>,用key序列开表示集。也就是将map的value值部分置为无意义的,直接使用key来存储数据,相当于map的一种变形吧,借鉴思想。

Callable

Runnable封装一个异步运行的任务,可以将其视为一个没有参数和返回值的异步方法。Callable和Runnable类似,但是有返回值。Callable接口是一个参数化的类型,只有一个call方法。

//表示一个最终返回Integer对象的异步计算

public interface Callable<V>{
    V call() throws Exception;
}

Future接口

//保存异步计算的结果。可以启动一个Callable,重写call方法,用callable初始化Future对象,将future对象交给某个线程,运行线程,调用future的isDone()方法来判断计算是否完成。

FutureTask<Chuju> task = new FutureTask<>(Callable<Chuju>);
public interface Future<V>{
}

 
public static void main(String[] args) throws InterruptedException, ExecutionException {
    long startTime = System.currentTimeMillis();

    // 第一步 网购厨具
    Callable<Chuju> onlineShopping = ()->{
            System.out.println("第一步:下单");
            System.out.println("第一步:等待送货");
            Thread.sleep(5000);  // 模拟送货时间
            System.out.println("第一步:快递送到");
            return new Chuju();
    };
    FutureTask<Chuju> task = new FutureTask<>(onlineShopping);
    new Thread(task).start();
    // 第二步 去超市购买食材
    Thread.sleep(2000);  // 模拟购买食材时间
    Shicai shicai = new Shicai();
    System.out.println("第二步:食材到位");
    // 第三步 用厨具烹饪食材
    if (!task.isDone()) {  // 联系快递员,询问是否到货
        System.out.println("第三步:厨具还没到,心情好就等着(心情不好就调用cancel方法取消订单)");
    }
    Chuju chuju = task.get();
    System.out.println("第三步:厨具到位,开始展现厨艺");
    cook(chuju, shicai);

    System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}

FutureTask包装器可以同时将Callable转换成Future和Runnable,它同时实现二者的接口。

执行器

线程池中包含了许多准备运行的空闲线程,这些线程可以接受Runnable方法,然后运行,运行之后不结束,继续留着接受其他的Runnable方法。

构建线程需要代价,因为设计到与操作系统的交互,创建大量线程会降低性能甚至使jvm崩溃。

执行器Executor类有许多静态工厂方法用来构建线程池。
线程池目前使用类:java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类。

1、Executors执行器、ThreadPoolExecutor线程池。ExecutorService pool = Executors.newCachedThreadPool();
2、ScheduledExecutorService预定执行,是一种允许线程池机制的java.util.Timer的泛化。
3、控制任务组 --> eg:在执行器中使用shutdownNow方法取消所有的任务。
4、Fork-Join框架,解决应用可能对每个处理器内核分别使用一个线程,来完成计算密集型任务,如图像或视频处理。

同步器

1、信号量(类似PV操作信号量)

2、倒计时门栓(让线程集等待直到计数变为0。倒计时门栓是一次性的,一旦计数为0,就不能重用了。计数值为1,线程在门外等候直到另外一个线程将计数器值置为0)

3、障栅(考虑大量线程都运行‘一段代码‘的不同部分的情形,结果是所有结果的组合,当一个线程完成了自己的部分时,就让它运行到障栅处,一旦所有线程都执行完成到达障栅,就撤销障栅,使程序继续向下执行)

4、交换器(当两个线程在同一个数据缓冲区的两个实例上工作的时候。比如一个线程向缓冲区填入数据,一个线程消耗这些数据。当它们都完成以后,相互交换缓冲区)

5、同步队列(将生产者和消费者线程配对的机制,当一个线程调用SynchronousQueue的put方法时,它会阻塞直到另一个线程调用take方法为止)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值