java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

这篇具有很好参考价值的文章主要介绍了java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

正则表达式

正则表达式验证网站

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

限定符

1、? :表示前边这个字符可以出现0次或者1次。例如下边/used? 既可以匹配use也可以匹配used
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
2、*:匹配0个或者多个字符,*号代表前边这个字符可以出现0次或者多次。例如/ab*c可以匹配ac、abc、abbbbc

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
3、+:与*号不同的是,+需要前面这个字符出现1次或者多次。当使用+号时,/ab+c就无法匹配ac了,因为b至少的出现一次。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
4、如果想要实现对字符出现次数更加精确的限定,可以使用花括号+数字,指定。
例如:

  • /ab{2,}c:b出现2次以及以上
  • /ab{2,6}c:b出现[2,6]次
  • /ab{6}c:b出现6次

5、如果要匹配多个字符的重复出现次数,可以使用小括号()将字符扩起来。

例如/(ab)+表示前面这个ab出现1次或者多次。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

运算符与字符类

1、或运算符|
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
2、字符类。
[abc]+:由abc组成的字符
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

-:按照字母顺序的可以使用-
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

3、非运算符^:除了列出字符之外的。
例如:/[^0-9]+:非数字字符。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

元字符

\d:digit,数字字符,等同于[0-9]
\D:非数字字符
\w:word,单词字符,英文、数字及其下划线。
\W:非单词字符
\s:space,空表字符,包含tab和换行符。
\S:非空白字符
.:代表任意字符,但不包含换行符
^:匹配行首的字符:例如/^a,只匹配行首的a
$:匹配行尾的字符:例如/a$,只匹配行尾的a

贪婪匹配与懒惰匹配

默认的匹配规则,是贪婪匹配,即尽可能地匹配多个字符。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
使用?切换贪婪匹配为懒惰匹配。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

案例

1、匹配16进制颜色rgb值。

  • 首先都以#开头。
  • 16进制,因此,字符从0-9,a-f组成。
  • 字符出现次数为6次,使用花括号,指定6次。
  • 使用单词结尾字符\b表示结束。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
2、ipv4地址匹配。
三个点,与四个数字,数字范围为[0,255],可以把点与数字放一块,分为三个数字+点,和一个数字。

数字范围分情况讨论,3位数,2位数,1位数。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

线程并发

多线程

多线程的创建

  1. 方法一:继承Thread类,创建步骤:
    • 定义子类MyThread继承Thread,重写run()方法
    • 创建MyThread类对象
    • 调用线程对象的start()方法启动线程(启动后执行的是run()方法)

这种创建方式因为已经继承了Thread类,无法继承其他类,不利于拓展。

  1. 方法二:声明一个实现Runnable接口的类。
    • 定义定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法。
    • 创建MyRunnable对象
    • 把MyRunnable对象交给Thread处理
    • 调用Thread对象的start方法启动

方法二只实现接口,可以继续继承类和实现接口,扩展性更强。但缺点是编程多一层包装(runnable对象还需要再传给thread构造thread对象)
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
方法一实现:

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; ++i) {
            System.out.println("子线程执行输出:" + i);
        }
    }
}
 Thread t1 = new MyThread();
 t1.start();

方法二实现:

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
});
for (int i = 0; i < 10; i++) {
    System.out.println("主线程:" + i);
}

当然,可以用lambda表达式简写。

 Thread t = new Thread(() -> {
     for (int i = 0; i < 10; i++) {
         System.out.println( "子线程" + i);
     }
 });
 for (int i = 0; i < 10; i++) {
     System.out.println("主线程:" + i);
 }
  1. 方式3
    前两种创建方式都存在一个问题:
    • 他们重写的run()方法均不能直接返回结果
    • 不适合需要返回线程执行结果的业务场景

jdk5利用Callable、FutureTask接口实现上述功能。

创建步骤:

  • 定义类实现Callable接口,重写call方法,封装要做的事情。
  • 用FutureTask把Callable对象封装成线程任务对象。
  • 把线程任务对象交给Thread处理
  • 调用Thread的start方法启动线程,执行任务。
  • 线程执行完毕后,通过FutureTask的get()方法去获取任务执行的结果。

方法三的实现:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadDemo3 {
    public static void main(String[] args) {
        Callable<String> call1 = new MyCallable(100000);
        FutureTask<String> f1 = new FutureTask<>(call1);
        Thread t1 = new Thread(f1);
        t1.start();

        Callable<String> call2 = new MyCallable(100000);
        FutureTask<String> f2 = new FutureTask<>(call2);
        Thread t2 = new Thread(f2);
        t2.start();

        try {
            // 如果f1没有执行完毕,那么get这里会等待,直至完成
            String r1 =  f1.get();
            System.out.println("第一个结果:" + r1);
        } catch (Exception e) {
            e.printStackTrace();
        }
            // 如果f2没有执行完毕,那么get这里会等待,直至完成
        try {
            String r2 =  f2.get();
            System.out.println("第二个结果:" + r2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<String> {
    private int n;
    public MyCallable (int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "子线程执行的结果是:" + sum;
    }
}

Thread常用API

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

线程同步与通信

当多线程访问共享资源时,可能出现线程安全问题。解决线程安全问题的方法有线程同步和线程通信。

线程同步:单例模式的三种写法

线程同步的思想是对共享资源加锁,将多个线程实现先后依次访问共享资源,这样就解决了线程安全问题。

线程同步的方式:

  • 同步代码块
  • 同步方法
  • Lock锁
同步代码块
  • 作用:把出现线程安全问题的核心代码给上锁。
  • 原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。

synchronized(同步锁对象){
操作共享资源的代码
}

锁对象规范:

  • 建议使用共享资源作为锁对象
  • 对于实例方法建议使用this作为锁对象。
  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象。

同步代码块实现的懒汉单例模式:
核心:私有化构造函数,静态化单例成员,在获取单例的静态方法中,如果检测到实例未创建,使用synchronized构建同步代码块,因为是静态方法,所以使用类的字节码(类名.class)对象作为synchronized的锁对象。

class SingleInstance {
    private SingleInstance() {
    }
    private static SingleInstance singleInstance;
    public static SingleInstance getInstance() {
        if (singleInstance == null) {
            synchronized (SingleInstance.class) {
                if (singleInstance == null) {
                    singleInstance = new SingleInstance();
                }
            }
        }
        return singleInstance;
    }
}
同步方法

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
基于同步方法实现的懒汉单例:
核心:私有化构造、静态化单例对象
获取实例的静态方法加synchronized修饰。

class Single {
    private Single(){
        
    }
    private static Single single;
    public static synchronized Single GetInstance() {
        if (single == null) {
            single = new Single();
        }
        return single;
    }
}

同步方法的底层其实是隐式锁对象,只是锁的范围是整个方法代码,如果方法是实例方法,同步方法默认用this作为锁对象。

从性能上讲,同步代码块锁的范围更小,性能更高。

java中静态内部类不会自动初始化,只有在调用静态内部类的方法时才会加载静态内部类。

利用这种静态内部类,我们可以实现一个比使用同步代码块和同步方法,性能上更加优秀的懒汉单例。

核心:私有化构造函数,构建静态内部类,类中成员初始化时调用构造函数,使用static决定它的全局性 ,使用final,决定它只会初始化一次。在获取单例的方法中,返回内部类的成员。

class SingleByInner{
    private SingleByInner() {
		
    }
    static class Inner {
        private static final SingleByInner INSTANCE = new SingleByInner();
    }
    public static SingleByInner getInstance() {
        return Inner.INSTANCE;
    }
}
Lock锁
  • 为了更加清晰的表达如何加锁和释放锁,JDK5之后提供了一个新的锁对象Lock,更加灵活,方便。
  • Lock实现提供比使用synchronize方法和语句可以获得更广泛的锁定操作。
  • Lock是接口不能直接实例化,这里采用它的实现类ReetrantLock来构建Lock锁对象。

线程通信

什么是线程通信、如何实现?

  • 所谓线程通信就是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现。
  • 线程间会根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做。

线程通信常见模型

  • 生产者与消费者模式:生产者线程负责生成数据,消费者线程负责消费数据。
  • 要求:生产者生产完数据后,唤醒消费者,然后等待自己;消费者消费完数据后,唤醒生产者,然后等待自己。

线程通信的关键是object类的等待和唤醒方法java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM上述所有方法应该使用当前同步锁对象进行调用。

this.notifyAll();  // 唤醒所有线程
this.wait();  // 锁对象,让当前线程进入等待。

经典面试题:sleep()和wait()的区别是什么?
1、来自不同类:sleep()来着Thread,wait()来自Object。
2、sleep()不会释放锁,wait()会释放锁。
3、使用范围不同:wait,notify和notifyAll只能在同步控制方法或者同步代码块中使用,sleep可以在任何地方使用。

线程池

线程池是一个可以复用线程的技术。因为创建新线程的开销很大,使用线程池可以重复利用线程,可提高程序性能。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

获取线程池对象

JDK5.0起提供了代表线程池的接口:ExecutorService
如何获取线程池对象?

  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象,这种方法最为灵活。

  • 方式二:使用Executor(线程池的工具类)调用方法返回不同特点的线程池对象。

ThreadPoolExecutor

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
临时线程什么时候创建?

  • 新任务提交时,核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
    什么时候会开始拒绝任务?
  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来时才会开始拒绝任务。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

线程池处理runnable任务

        ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        Runnable target = () -> {
            try {
                Thread.sleep(100000);
                System.out.println(Thread.currentThread().getName() + "输出");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        // 三个核心线程
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        // 五个在等待队列
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        // 等待队列满了,新加两个临时线程
        pool.execute(target);
        pool.execute(target);
        
        // 拒绝任务,抛出异常
        pool.execute(target);

    }

线程池处理callable任务

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
execute 执行runnable类任务,submit处理callable任务。

Executors

Executors是jdk内置的线程池工具类,可以返回下边四种类型的线程池:

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

定时器

Timer

Timer和TimerTask是用于在后台线程中调度任务的java util类。简单地说,TimerTask是要执行的任务,Timer是调度器。

1.自定义一个类继承于TimerTask的类,并重写其run()方法即可。

2.可以采取匿名类的形式,直接重写其run()方法。

TimeTask有一抽象方法run(),其作用就是用来放我们处理的逻辑任务。

Timer有一schedule()方法,重载参数和另外两个方法如下表:
可以看到schedule()可以接收Data参数,指定某个时刻执行,也可接收long类型的毫秒参数,延迟多少毫秒执行。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

    public static void usingTimer() {
        Timer timer = new Timer("Timer");
        long delay = 2000L;
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("Task performed on: " + new Date() + "n" +
                        "Thread's name: " + Thread.currentThread().getName());
            }
        }, delay, 3*1000);  // 每3秒执行一次定时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(new Date());  
            }
        },delay); // 只执行一次
    }
调度可重复执行任务

有两种方式:

  • 固定延迟:schedule()方法还有两个重载,每个重载都使用一个额外的period参数来表示以毫秒为单位的周期性。
  • 固定频率:有两个scheduleAtFixedRate()方法,它们的周期也是以毫秒为单位的。

注意:
1、如果一个任务的执行时间超过了执行周期,那么无论我们使用固定延迟还是固定速率,它都会延迟整个执行链。
2、更有甚者如果定时调度器中一个定时任务出现异常,会同时影响其他任务的进行。
这就是使用Timer定时器的缺点所在,Timer本身是单线程的,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。可能因为其中的某个任务异常,影响后续任务。

取消定时器

1、调用Timer.cancel()方法。
2、在TimerTask的run()方法中使用cancle取消。

ScheduleExecutorService

ScheduleExecutorService内部是线程池来处理定时任务,如果某个任务失败,那么这个线程会挂掉,不会影响其他线程的任务。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

并发与并行

  • 正在运行的程序(软件)就是一个独立的进程,线程是属于进程的,多个线程其实是并发与并行同时进行的。

并发的理解:

  • CPU同时处理线程的数量有限。
  • CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们感觉这些线程在同时执行,这就是并发。

并行的理解:

  • 在同一个时刻,多个线程同时执行

线程的生命周期

线程的生命周期主要有以下6种状态:

  • New(新创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed Waiting(计时等待)
  • Terminated(被终止)

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
New表示线程被创建,尚未启动的状态,即new Thread(),新建一个线程但是还没有执行start()方法。当执行start()后,就进入了Runnable状态。

Runnable

Java中的Runnable状态对应操作系统线程状态中的两种状态分别是RunningReady,也就是说Java中处于Runnable状态的线程可能是正在执行,也可能是在等待CPU分配资源。

阻塞状态

Blocked(被阻塞)、Waiting(等待)、TimedWaiting(计时等待)这三种状态统称为阻塞状态。

Blocked:没获得锁被阻塞

从Runnable状态进入到Blocked状态只有一种途径,那就是当进入到synchronized代码块中时,未能获得相应的锁。

相应的,当Blocked状态的线程获取到锁时,此线程就会进入到Runnable状态中参与CPUT资源的抢夺。

Waiting等待状态

waiting状态有三种情况:

  • 当线程中调用了没有设置timeout参数的object.wait()方法。
  • 当线程调用了没有设置timeout参数的thread.join()方法。
  • 当线程调用了LockSupport.park()方法。

Blocked与Waiting的区别

  • Blocked是在等待其他线程释放锁
  • Waiting则是在等待某个条件,比如join的线程执行完毕,或者notify()/notifyAll().

Time Waiting计时等待状态

Time Waiting状态与Waiting状态非常相似,其中的区别就在于是否有时间的限制,在Timed Waiting 状态时会等待超时,之后由系统唤醒,或者也可以提前被通知唤醒如notify

进程状态之间的转换

WaitTimeWaiting状态被notify或者notify_all后,如果拿到锁,那么进入Runnable,如果没有拿到锁,则进入BlockedRunnable执行不带时间参数的wait进入waiting状态,执行带时间参数的wait或者sleep后进入TimeWaiting状态。

sleep和wait的区别

  • 来自不同的对象:sleep来自Thread,wait来自Object
  • 使用场景不同:wait只能在同步代码块或者同步方法中使用,执行wait前应该使用notify或者notify_all唤醒其他线程。执行wait后会释放自己的锁。sleep则没有使用场景的限制,sleep执行后并不会释放锁。

Junit单元测试框架

编写Junit测试的步骤:

  1. 一般而言IDEA整合了Junit框架,不需要另外导入,但是如果IDEA没有整合,需要手工导入Junit的两个Jar包。
  2. 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法
  3. 在测试方法上使用@Test注解:标注该方法是一个测试方法。
  4. 在测试方法中完成被测试方法的预期正确性测试。
  5. 选中测试方法,选择“Junit运行”,如果测试良好则是绿色,如果测试失败,则是红色。

例子:
对两个业务代码的正确性编写单元测试:
业务用例:

public class UserService {
    public String loginName(String loginName, String passWard) {
        if ("admin".equals(loginName) && "123456".equals(passWard)) {
            return "登录成功";
        } else {
            return "用户或者密码有问题";
        }
    }

    public void selectName() {
        System.out.println(10 / 0);
    }
}

测试案例:

public class TestUserService {
    /**测试方法
     * 1、必须是公开的,无参数,无返回值的方法
     * 2、测试方法必须使用@Test注解标记
     */

    @Test
    public void testLoginName() {
        UserService userService = new UserService();
        String rs = userService.loginName("admin", "123456");
        Assert.assertEquals("用户或者密码有问题", "登录成功", rs);
    }

    @Test
    public void testSelectNames() {
        UserService userService = new UserService();
        userService.selectName();
    }
}

测试结果:
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

反射

反射的概述:

  • 反射是指对于任何一个Class类,在“运行的时候”都可以直接得到这个类的全部成分。
    • 在运行时,可以直接得到这个类的构造对象:Constructor
    • 在运行时,可以直接得到这个类的成员变量对象:Field
    • 在运行时,可以直接得到这个类的成员方法对象:Method
  • 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制

反射的关键:
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
HelloWorld.java -> javac -> HelloWorld.class Class c = HelloWorld.class;

反射获取Class类的全部成分

获取Class类对象

一、反射第一步:获取Class对象,有下边三种方法:

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

  • 在源代码阶段:Class类中的静态方法:forName(String className)
  • 在Class对象阶段,通过类名.class
  • 在Runtime运行时阶段,通过对象.getClass()

回顾我们在编写同步代码块实现的单例对象时。便是通过了上述的方法二,获取到类对象来作为锁的互斥资源:
synchronized (SingleInstance.class){}

public static SingleInstance getInstance() {
   if (singleInstance == null) {
       synchronized (SingleInstance.class) {
           if (singleInstance == null) {
               singleInstance = new SingleInstance();
           }
       }
   }

下边我们编写一个Student类,尝试用上边的三种方法获取到Class对象:

        // 1、Class.forName(全限名) 全限名:包名 + 类名
        Class C1 = Class.forName("com.heima2.model.Student");
        System.out.println(C1);

        // 2、通过 类名.class
        Class C2 = Student.class;
        System.out.println(C2);
        
        // 3、通过 对象.getClass
        
        Student s = new Student();
        Class C3 = s.getClass();
        System.out.println(C3);

三个打印结果相同,因为编辑器只会编译出一份类对象。

获取构造器(Constructor)、成员(Field)、成员函数(Method)

Constuctor

通过class对象的.getConstructors()获取全部构造器
获取每个构造器,打印它的名字+构造器的参数个数。

        Student s = new Student();
        Class C3 = s.getClass();

        Constructor[] constructors = C3.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.getName() + "-->" + c.getParameterCount());
        }

使用.getConstructor(T ...)方法按照参数获取指定的构造器并构造对象:

Constructor con1 = C3.getConstructor();  // 获取无参构造器
Student sbycon1 = (Student) con1.newInstance();

Constructor con2 = C3.getConstructor(String.class, String.class);  // 获取有参构造器
Student sbycon2 = (Student) con2.newInstance("张三", "01217");

注意:上边两种方法无法获取到声明为private的构造器

如果要拿到private的构造器,需要使用.getDeclaredConstructor()方法,拿到私有构造器后并不能直接构建对象,需要打开权限:setAccessible(true);

        Constructor con3 = C3.getDeclaredConstructor(Integer.class);
        con3.setAccessible(true);
        Student sbycon3 = (Student) con3.newInstance(12);

Field

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
Method
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

反射的作用

  • 反射可以绕过编译阶段为集合添加数据,此时集合的泛型将不能产生约束,可以为集合添加任意类型的元素。
  • 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段时,类型都是ArrayList,泛型相当于被擦除了。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

  • 反射的另外一个重要作用是,做通用框架的底层实现基础。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
为了实现上述功能,我们需要利用反射构造一个工具类:
这个工具类的主要思路就是接收一个对象,利用反射获取对象的成员名字和成员的值,使用打印流将内容保存到指定位置。

public class MybatisUtils {
    public static void save(Object obj) {
        try(PrintStream ps = new PrintStream(new FileOutputStream("F:\\java\\heima2\\src\\model\\data.txt", true))) {
            Class c = obj.getClass();
            ps.println("=============" + c.getSimpleName() + "=============");
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                field.setAccessible(true);
                String value = field.get(obj) + "";
                ps.println(name + "=" + value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

测试代码:

public class mainActivity {
    public static void main(String[] args) throws Exception {
        Student a = new Student("张三", "01217");
        MybatisUtils.save(a);
        Teacher t = new Teacher("李四", 24);
        MybatisUtils.save(t);
    }
}

结果:
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

注解

java注解是jdk5引入的一种注释机制,java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注,然后进行特殊处理。

自定义注解

格式:

public @interface 注解名称 {
	public 属性类型 属性名 () default 默认值;
}

特殊属性

  • value属性,如果只有一个value属性的情况下,使用value属性可以省略value的名称
  • 但是如果有多个属性,且属性没有默认值,那么value属性是不能省略的
public @interface Book {
    String value();
}
@Book("fa")

如果处理value属性,其他属性有默认值,这种写法也是正确的。

public @interface Book {
    String value();
    String name() default "aaa";
}
@Book("fa")

元注解

元注解:就是注解的注解

元注解有两个:
@Target:约束自定义注解只能在哪些地方使用
@Retention:申明注解的生命周期
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

注解的解析

注解通常需要解析,判断是否存在注解,存在就解析出内容。

与注解解析相关的接口:

  • Annotation:注解的顶级接口,注解都是Annotation类型的对象
  • AnnotatedElement:该接口定义了与注解解析相关的解析方法
  • 所有的类成分Class、Method、Field、Constructor都实现了AnnotatedElement接口,他们都拥有解析注解的功能。

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

注解解析案例

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
首先,我们编写一个名为Book注解,添加元注解:@Target({ElementType.TYPE, ElementType.METHOD})
使得可以在类型和方法上添加注解
@Retention(RetentionPolicy.RUNTIME)
使得注解一直存在

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    String name();
    String[] authors();
    double price();
}

然后我们编写一个类BookStore,给他加上Book注解:

@Book(name = "《java虚拟机》", authors = {"a","b"}, price = 9.9)
public class BookStore {
    @Book(name = "《C++prime》", authors = {"c","b"}, price = 19.9)
    public void test() {

    }
}

最后我们写个测试案例:获取并解析类上和方法上的这两个注解:

public static void main(String[] args) throws NoSuchMethodException {
        // a、先得到类对象
        Class c = BookStore.class;
        // 判断是否存在注解,如果存在,取出注解内容
        if (c.isAnnotationPresent(Book.class)) {
            Book bookFromClass = (Book) c.getDeclaredAnnotation(Book.class);
            System.out.println("类:---" + c.getSimpleName() + "---的注解");
            System.out.println(bookFromClass.name());
            System.out.println(Arrays.toString(bookFromClass.authors()));
            System.out.println(bookFromClass.price());
        }
        // 由对象获取到方法,再获取方法的注解
        Method m = c.getDeclaredMethod("test");
        if (m.isAnnotationPresent(Book.class)) {
            System.out.println("方法:---" + m.getName() + "---的注解");
            Book bookFromMethod = (Book) m.getDeclaredAnnotation(Book.class);
            System.out.println(bookFromMethod.name());
            System.out.println(Arrays.toString(bookFromMethod.authors()));
            System.out.println(bookFromMethod.price());
        }
    }

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

模拟Junit的注解案例

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
首先自定义一个注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {

}

在测试类中,我们给某些方法定义注解,在main函数中,我们通过反射获取到类的方法,检查是否有注解,如果有注解就,通过方法的invoke(Object,...)方法执行该方法。

public class AnnotationDemo {
    @MyTest
    public void test1() {
        System.out.println("===test1===");
    }

    public void test2() {
        System.out.println("===test2===");
    }
    @MyTest
    public void test3() {
        System.out.println("===test3===");
    }

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        AnnotationDemo a = new AnnotationDemo();
        Class c = a.getClass();
        Method[] ms = c.getDeclaredMethods();
        for (Method m : ms) {
            if (m.isAnnotationPresent(MyTest.class)) {
                m.invoke(a);
            }
        }
    }
}

动态代理

代理:某些场景下,对象会找一个代理对象,来辅助自己完成一些工作。
java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
在java中实现动态代理的步骤:

  • 必须存在接口
  • 被代理对象实现接口
  • 使用Proxy类提供的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)

通过代理调用方法的执行流程:

  • 先走向代理
  • 代理可以为方法额外做一些辅助工作
  • 开始正在触发对象方法的执行
  • 回到代理中,由代理负责返回结果给方法的调用者。

下边是一个例子:
首先有一个接口Skill

public interface Skill {
    void dance();
    void sing();
}

被代理对象Star实现了上述接口:

public class Star implements Skill{
    private String name;
    public Star(String name) {
        this.name = name;
    }
    @Override
    public void dance() {
        System.out.println(name + "跳舞");
    }
    @Override
    public void sing() {
        System.out.println(name + "唱歌");
    }
}

创建代理类,构建一个创建代理的静态方法,该方法返回的是接口对象Skill,入参是我们的被代理对象Star

方法体就是利用Proxy类提供的newProxyInstance代理实例方法,这个方法实际上是通过反射的方法,获取到了被代理类实现的方法,并且在方法体中invoke它。

public class StarAgentProxy {
    public static Skill getProxy(Star s) {
        return (Skill) Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("收首付款。。。");
                Object rs =  method.invoke(s);
                System.out.println("收尾款。。。");
                return rs;
            }
        });
    }
}

动态代理的案例

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

第一步:编写接口

public interface UserService {
    void login(String userName, String passWard);
    String deleteUser();
    String selectUsers();
}

第二步:编写被代理类实现上述接口:

package proxy2;

public class UserServiceImpl implements UserService{
    @Override
    public void login(String userName, String passWard) {
        String rs = "登录名和密码错误";
        if ("admin".equals(userName) && "123456".equals(passWard)) {
            rs = "登录成功";
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String deleteUser() {
        try {
            System.out.println("正在删除用户.....");
            Thread.sleep(2500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "deleteUser";
    }

    @Override
    public String selectUsers() {
        try {
            System.out.println("正在搜索用户.....");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        return "selectUsers";
    }
}

第三步,编写代理类,返回接口,在代理中调用接口的方法,并对方法进行耗时分析。

    public static UserService getProxy(UserService obj) {
        return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startTime = System.currentTimeMillis();
                Object rs = method.invoke(obj, args);
                long endTime = System.currentTimeMillis();
                System.out.println(method.getName() + "耗时" + (endTime - startTime) / 1000.0 + "s");
                return rs;
            }
        });
    }

第四步:
编写测试用例,构建被代理类对象,交由代理调用方法;

    public static void main(String[] args) throws Exception {
        UserServiceImpl userService = new UserServiceImpl();
        UserService u = ProxyUtil.getProxy(userService);
        u.login("admin", "123456");
        u.deleteUser();
        u.selectUsers();
    }

动态代理的优点

  • 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。
  • 简化了编程工作,提高了开发效率,同时提高了软件系统的可拓展性。
  • 可以为被代理对象的所有方法做代理。
  • 非常的灵活,支持接口类型的实现类对象做代理,也可以直接为接口本身做代理。

XML

  • XML是可拓展标记语言(eXtensible Markup Language)的缩写,它是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。

XML的几个特点和使用场景

  • 一是纯文本,默认使用UTF-8编码,二是可嵌套
  • 如果把XML内容存为文件,那么它就是一个XML文件
  • XML的使用场景,XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的信息。

XML类似html语言,由可以嵌套的开闭标签构成:

<?xml version="1.0" encoding="UTF-8" ?>
<student>
    <name>张三</name>
    <gender></gender>
    <info>
        <age>24</age>
        <address>武汉</address>
    </info>>
</student>

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM
为了避免与标记符’>’ '<'冲突,XML语言对于特殊字符有另外的表述方法,当然也可以声明一个区域,CDATA,在这个区域的大于小于符号就不会与标记符号产生冲突。在IDEA中可以直接输入CD然后tab

java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM文章来源地址https://www.toymoban.com/news/detail-457471.html

JVM

语言发展历史

  1. C/C++
  • 手动管理堆内存:malloc/free / new / delete
  • 可能导致的问题:申请了内存,忘记释放 --> memory leak 内存泄露 --> 内存泄露越来越多时,可用的堆空间越来越小,很有可能进一步导致某次申请空间时,没办法分配 即out of memory 内存溢出
  • 由于编程时需要考虑底层的内存分配,导致开发效率极低。
  1. Java Python Go
  • 方便内存管理的语言
  • 自带GC - Garbage Collector(垃圾回收器),程序运行时自动启动垃圾回收线程,垃圾回收器负责回收在堆中申请的内存
  • 自带垃圾收集器使得程序员业务开发时,不再需要关注底层的内存问题,大大降低了程序员门槛,提高了开发的效率
  • 由于需要额外的垃圾回收线程,执行效率偏低,此外这些语言也没有解决空指针问题,需要程序中额外判断。

到了这里,关于java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java高级技术:单元测试、反射、注解

    目录 单元测试 单元测试概述 单元测试快速入门 单元测试常用注解 反射 反射概述 反射获取类对象 反射获取构造器对象 反射获取成员变量对象 反射获取方法对象 反射的作用-绕过编译阶段为集合添加数据 反射的作用-通用框架的底层原理 注解 注解概述 自定义注解 元注解

    2024年01月16日
    浏览(51)
  • Java中的单元测试,反射和枚举

    2024年02月05日
    浏览(53)
  • Java SE 学习笔记(十七)—— 单元测试、反射

    开发好的系统中存在很多方法,如何对这些方法进行测试? 以前我们都是将代码全部写完再进行测试。其实这样并不是很好。在以后工作的时候,都是写完一部分代码,就测试一部分。这样,代码中的问题可以得到及时修复。也避免了由于代码过多,从而无法准确定位到错误

    2024年02月06日
    浏览(54)
  • 【Java并发编程】线程中断机制(辅以常见案例)

    本文由浅入深介绍了中断机制、中断的常见案例和使用场景。 因为一些原因需要取消原本正在执行的线程。我们举几个栗子: 假设踢足球点球时,A队前4轮中了4个球,B队前4轮只中了2个球,此时胜负已分,第5轮这个点球就不用踢了,此时需要停止A队的线程和B队的线程(共

    2024年02月13日
    浏览(38)
  • Day20-【Java SE高级】单元测试 反射 注解 动态代理

    就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。 1. 咱们之前是如何进行单元测试的?有啥问题? 只能在main方法编写测试代码,去调用其他方法进行测试。 无法实现自动化测试,一个方法测试失败,可能影响其他方法的测试。 无法得到测试的报告,需要程

    2024年04月17日
    浏览(51)
  • 探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 我们继续总结学习 Java基础知识 ,温故知新。 LockSupport 是 Java SE 9 及以上版本中引入的一个线程同步工具类,用

    2024年02月16日
    浏览(53)
  • Day14:单元测试、Junit单元测试框架、反射、注解

    针对最小的功能单元(方法)进行正确性测试 编写正规的单元测试框架 传统的无法执行自动化测试,且无法得到测试报告 Junit的作用: 测试类取名:原类名+Test(大驼峰) 测试方法取名:test+原函数名称(小驼峰) 测试方法:必须public,无参,无返回值 测试方法上面必须加

    2024年04月14日
    浏览(63)
  • 25.单元测试、反射

    单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法。因此,单元测试就是针对Java方法进行的测试,进而检查方法的正确性。 只有一个main方法,如果一个方法的测试失败了,其他测试方法会受到影响。 无法得到测试的结果报告,需要程序员自己

    2024年02月10日
    浏览(35)
  • 单元测试&反射&注解

             就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。             可以用来对方法进行测试,它是由Junit公司开源出来的            反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。             

    2024年02月07日
    浏览(39)
  • mockito+junit 单元测试 测试私有方法利用反射去调用提升覆盖率 反射调用时传入参数为 null

    今天公司安排我写单元测试 因为要通过三级认证 公司要求是覆盖率必须达到100% 而在写单元测试的时候 对于是否要测试私有方法一直是一个争议 公司规范 对私有方法也进行测试 代码如下: JudgeUtils 是公司封装的一个工具类 让我们看看 isNull 方法的内部 代码如下: 我现在需

    2024年02月11日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包