java常用开发支持类库

痴心易碎 提交于 2020-07-29 05:38:10

UUID类

UUID是一个生成无重复字符串的程序类(JDK1.5之后出现),这个程序类的主要功能是根据时间戳实现一个自动的无重复的字符串定义(无重复指的是出现重复的概率极低)。

一般在获取UUID时往往都是随机生成一个的内容,可以通过如下方式获取:

  • 获取UUID对象:public static UUID randomUUID()
  • 根据字符串获取UUID内容:public static UUID fromString(String name)
public class JavaAPIDemo {
    public static void main(String[] args) {
        UUID uid = UUID.randomUUID();
        System.out.println(uid.toString());
    }
}
随机生成一个UUID:
d752e1dc-181f-44e7-8ce3-b2369cb14660

对于一些文件进行自动命名处理的情况,UUID非常方便

Optional类

Optional类的主要功能是进行null的相关处理,例如:为了防止程序中出现空指针异常,往往追加有null的验证。

范例:传统的引用传递

interface IMessage{
    String getContent();
}
class IMessageImpl implements IMessage{

    @Override
    public String getContent() {
        return "你好";
    }
}
class MessageUtil{
    private MessageUtil(){}
    public static void useMessage(IMessage msg){
        //如果不进行判断,那么可能会导致空指针异常
		if (msg!=null)
            System.out.println(msg.getContent());
    }
    public static IMessage getMessage(){
//        return new IMessageImpl();
        return null;
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) {
        MessageUtil.useMessage(MessageUtil.getMessage());
    }
}

在引用接收的一方往往都是被动的进行判断,所以为了解决这种被动的处理操作,Java类中就提供了Optional类,这个类可以实现null的处理操作。

  • 返回空数据:public static<T> Optional empty()
  • 获取数据:public T get()
  • 保存数据,但是不允许出现null:public static <T> Optional<T> of(T value),如果出现null,则会抛出NullPointerException异常
  • 保存数据,允许为空:public static <T> Optional<T> ofNullable(T value)
  • 空的时候返回其他数据:public T orElse(T other)

修改上述程序:

import java.util.Optional;

interface IMessage{
    String getContent();
}
class IMessageImpl implements IMessage{

    @Override
    public String getContent() {
        return "你好";
    }
}
class MessageUtil{
    private MessageUtil(){}
    public static void useMessage(IMessage msg){
        if (msg!=null)
            System.out.println(msg.getContent());
    }
    public static Optional<IMessage> getMessage(){
        IMessageImpl iMessage = new IMessageImpl();
        return Optional.of(iMessage);
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) {
        IMessage iMessage = MessageUtil.getMessage().get();
        MessageUtil.useMessage(iMessage);
    }
}
结果:你好

如果现在数据保存的内容为null,则就会在保存处出现空指针异常: 例如:

    public static Optional<IMessage> getMessage(){
        IMessage message=null;
        return Optional.of(message);//有对象
    }

由于Optional类中也有允许保存null的方法(Optional.ofNullable()),所以在数据获取时也可以进行null的处理,如果为null,则在使用get()获取数据时就会出现“NoSuchElementException”异常,所以此时可以更换为orElse()方法

处理null的方法

class MessageUtil{
    private MessageUtil(){}
    public static void useMessage(IMessage msg){
        if (msg!=null)
            System.out.println(msg.getContent());
    }
    public static Optional<IMessage> getMessage(){
        return Optional.ofNullable(null);
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) {
        IMessage iMessage = MessageUtil.getMessage().orElse(new IMessageImpl());
        MessageUtil.useMessage(iMessage);
    }
}

在所有引用数据类型的操作处理之中,null是一个重要的技术问题,所以来说JDK1.8后提供的Optional类对于null的处理很有帮助,同时也是在日后进行项目开发时常用的程序类。

ThreadLocal类

解决核心资源与多线程并发访问的处理情况

范例:消息发送处理


class Channel{//消息发送的通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message msg){
        message = msg;
    }
    public static void send(){
        System.out.println("【消息发送】"+message.getInfo());
    }
}
class Message{//消息实体类
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) {
        Message msg = new Message();//实例化消息主体对象
        msg.setInfo("你好");//设置要发送的内容
        Channel.setMessage(msg);//设置要发送的消息
        Channel.send();//消息发送
    }
}

对于当前的程序采用的是一种单线程的模式来进行处理的;那么如果再多线程的状态下能否实现安全一致的操作效果呢?为此将启动三个线程进行处理。

范例:多线程的影响


class Channel{//消息发送的通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message msg){
        message = msg;
    }
    public static void send(){
        System.out.println(Thread.currentThread().getName()+"【消息发送】"+message.getInfo());
    }
}
class Message{//消息实体类
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) {
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第1个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者A").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第2个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者B").start();
        new Thread(()->{
            Message msg=new Message();//实例化消息主体对象
            msg.setInfo("第3个线程的消息");//设置要发送的内容
            Channel.setMessage(msg);//设置要发送的消息
            Channel.send();//发送消息
        },"消息发送者C").start();
    }
}

运行结果:
消息发送者C【消息发送】第2个线程的消息
消息发送者B【消息发送】第2个线程的消息
消息发送者A【消息发送】第2个线程的消息

可以看出多线程发送产生很大的影响

在保持Channel(所有的发送通道)核心结构不改变的情况下,需要到考虑到每个线程的独立操作问题。那么这样的情况下,对于Channel类而言,除了要保留有发送的消息之外,还需要存放一个每一个线程的标记(当前线程),这时可以通过ThreadLocal类来实现。在ThreadLocal类中提供有如下操作方法:

  • 构造方法:public ThreadLocal()
  • 设置属性:public void set(T value
  • 取出数据:public T get()
  • 移除数据:public void remove()

只需要改这一个类,其他的不变

class Channel{//消息发送的通道
    private Channel(){}
    public static final ThreadLocal<Message> THREAD_LOCAL = new ThreadLocal<>();
    public static void setMessage(Message msg){
        THREAD_LOCAL.set(msg);
    }
    public static void send(){
        System.out.println(Thread.currentThread().getName()+"【消息发送】"+THREAD_LOCAL.get().getInfo());
    }
}
运行结果:
消息发送者C【消息发送】第3个线程的消息
消息发送者A【消息发送】第1个线程的消息
消息发送者B【消息发送】第2个线程的消息

每一个线程通过ThreadLocal只允许保存一个数据。

定时器 Timer与TimerTask

定时器的主要操作就是进行定时任务的处理。在JDK1.3后提供了定时任务的支持,但是这种任务的处理只实现了间隔触发的操作效果。

如果要实现定时的处理操作主要需要有一个定时操作的主体类,以及一个定时任务的控制。可以使用两个类实现:

  • java.util.TimerTask类:实现定时任务处理;
  • java.util.Timer类:进行任务的启动,启动的方法:
    • 任务启动:public void schedule(TimerTask task, long delay)、延迟单位为毫秒;
    • 间隔触发:`public void schedule(TimerTask task, long delay, long period)``;

  time.schedule(new MyTask(),1000);//1s后启动
        //间隔触发
        time.scheduleAtFixedRate(new MyTask(),100,1000);//100毫秒后启动,每秒执行一次
    }
}

这种定时是由JDK最原始的方式提供的支持,但是如果使用此类设置定时会非常复杂。

Base64加密工具

一般来说,加密往往都伴随着解密,所谓的加密或者解密都需要有一些规则。在JDK1.8开始提供有一组新的加密处理操作类,Base64处理。

  • java.util.Base64.Encoder:进行加密操作;
    • 加密处理:`public byte[] encode(byte[] src)``;
  • java.util.Base64.Decoder:进行解密操作;
    • 解密处理:public byte[] decode(byte[] src)

范例:实现加密与解密操作

import java.util.Base64;

public class JavaAPIDemo {
    public static void main(String[] args) {
        String msg = "你好";//要发送的信息
        //Base64操作的是字节数组
        //加密处理
        String encMsg = new String(Base64.getEncoder().encode(msg.getBytes()));
        System.out.println(encMsg);
        //解密处理
        String oldMsg = new String(Base64.getDecoder().decode(encMsg));
        System.out.println(oldMsg);
    }
}
运行结果:
5L2g5aW9
你好

注意:使用字符串接收encMsg的时候,必须使用new String()将加密后的字节码数组保存,不能使用toString()方法,因为Base64加解密是一种转换编码格式的原理

toString()与new String()的区别

  • str.toString是调用了b这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]。
  • new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
  • 使用
    • new String()一般使用字符转码的时候,byte[]数组的时候
    • toString()将对象打印的时候使用

虽然Base64可以实现加密和解密的处理,但是其由于是一个公版的算法,所以如果直接对数据进行加密并不安全,所以可以对其进行盐值操作。

import java.util.Base64;

public class JavaAPIDemo {
    public static void main(String[] args) {
        String salt = "java";
        String msg = "你好"+"【"+salt+"】";//要发送的信息
        //Base64操作的是字节数组
        //加密处理
        String encMsg = new String(Base64.getEncoder().encode(msg.getBytes()));
        System.out.println(encMsg);
        //解密处理
        String oldMsg = new String(Base64.getDecoder().decode(encMsg));
        System.out.println(oldMsg);
    }
}
运行结果:
5L2g5aW944CQamF2YeOAkQ==
你好【java】

即便现在有盐值实际上发现加密的效果也不是很好,更好的做法是多次加密。

范例:复杂加密

import java.util.Base64;
class StringUtil{
    private static final String SALT = "java";//公共盐值,一旦使用,不可更改
    private static final int REPEAT = 4;//重复加密次数

    /**
     * 加密处理
     * @param str 要加密的字符串
     * @return 加密后的数据
     */
    public static String encode(String str){
        String temp = str+"【"+SALT+"】";
        byte[] data = temp.getBytes();//将字符串变为字节数组
        for (int x = 0; x < REPEAT; x++) {
            data = Base64.getEncoder().encode(data);
        }
        return new String(data);
    }

    /**
     * 解密处理
     * @param str 要解密的字符串
     * @return 解密后的结果
     */
    public static String decode(String str){
        byte[] data = str.getBytes();//将字符串转化为字节数组
        for (int x = 0; x < REPEAT; x++) {
            data = Base64.getDecoder().decode(data);
        }
        String decode = new String(data);
        return decode.replaceAll("\\【\\w+\\】","");//正则处理,或者字符串切割去掉盐值
    }
}
public class JavaAPIDemo {
    public static void main(String[] args) {
        String msg = "你好";//要发送的信息
        String encode = StringUtil.encode(msg);
        //加密
        System.out.println(encode);
        System.out.println("================");
        //解密
        String decode = StringUtil.decode(encode);
        System.out.println(decode);
    }
}

运行结果:
VGxWM2VWcDZWbWhXZW1zd1RrVk9VbGxYTVVkTmJHeHNWREJHY2xWVU1Eaz0=
================
你好

最好的做法就是使用2-3种加密程序,同时再找到一些完全不可解密的加密算法。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!