紫薇星上的Java——开发者支持类库

简单整理一下开发者支持类库的内容。


UUID类

UUID是一种生成无重复字符串的一种程序类,这种程序类的主要功能是根据时间戳实现一个自动的无重复字符串定义。

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

  • 获取UUID对象:public static UUID randomUUID();
  • 根据字符串获取UUID的对象:public static UUID fromString(String name);
public class TestDemo {
	public static void main(String[] args) throws Exception {
		System.out.println(UUID.randomUUID().toString());
	}
}

在对一些文件进行自动命名的时候,UUID类型非常好用。


Optional类

Optional这个类主要功能是进行null的相关处理,在以前程序开发的过程中,为了防止程序中出现空指向的异常,往往会追加有null的验证。举一个传统的引用传递:

class MessageUtil{
	private MessageUtil() {}
	public static IMessage getMessage() {
		return null;
	}
	public static void useMessage(IMessage msg) {
		if(msg != null) {
		System.out.println(msg.getContent()); //可能出现null而导致空指向
		}
	}
}

interface IMessage{
	public String getContent();
}

class MessageImpl implements IMessage{
	@Override
	public String getContent() {
		return "www.csdn.com";
	}
}

public class TestDemo {
	public static void main(String[] args) throws Exception {
		MessageUtil.useMessage(MessageUtil.getMessage());
	}
}

这段代码没有问题,如果MessageImpl传入的是一个null对象,那么就不会输出任何东西。在引用接收的一方往往都是被动的进行判断,所以为了解决这种被动地处理操作,在Java类中提供有一个Optional类,这个类可以实现null的处理操作,在这个类中提供有如下的一些操作方法:

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

我们来按照正规的结构来修改一下程序:

class MessageUtil{
	private MessageUtil() {}
	public static Optional<IMessage> getMessage() {
		return Optional.of(new MessageImpl()); //有对象
	}
	public static void useMessage(IMessage msg) {
		if(msg != null) {
		System.out.println(msg.getContent()); //可能出现null而导致空指向
		}
	}
}

interface IMessage{
	public String getContent();
}

class MessageImpl implements IMessage{
	@Override
	public String getContent() {
		return "www.csdn.com";
	}
}

public class TestDemo {
	public static void main(String[] args) throws Exception {
		MessageUtil.useMessage(MessageUtil.getMessage().get());
	}
}

这样就没有问题了,但是如果现在保存的是null,就会在保存处出现异常:

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

因为是of(),所以不允许出现null,所以会报错:

Exception in thread "main" java.lang.NullPointerException
	at java.base/java.util.Objects.requireNonNull(Objects.java:221)
	at java.base/java.util.Optional.<init>(Optional.java:106)
	at java.base/java.util.Optional.of(Optional.java:119)
	at com.zijun.wang1.MessageUtil.getMessage(TestDemo.java:16)
	at com.zijun.wang1.TestDemo.main(TestDemo.java:38)

报错信息很清楚的告诉我们,在MessageUtil.getMessage()中出现错误,所以我们可以用ofNullable()方法来处理:

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

但因为Optional中允许保存null的内容,所以在数据获取的时候也可以进行null的处理:

Exception in thread "main" java.util.NoSuchElementException: No value present
	at java.base/java.util.Optional.get(Optional.java:151)
	at com.zijun.wang1.TestDemo.main(TestDemo.java:38)

这时如果为null,就会在使用get()方法获取数据的时候就已经出现错误信息,所以可以更换为orElse()方法:

public class TestDemo {
	public static void main(String[] args) throws Exception {
		MessageUtil.useMessage(MessageUtil.getMessage().orElse(new MessageImpl()));
	}
}

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


ThreadLocal类

ThreadLocal类是在开发中一个至关重要的类,主要解决了核心资源与多线程并发访问的处理情况。

在真正了解Threadlocal类作用的时候像编写一个简单的程序做一个分析:

class Message{ //要发送的一个消息体
	 private String info;
	 public void setInfo(String info) {
		 this.info = info;
	 }
	 public String getinfo() {
		 return this.info;
	 }
}

class Channel{ //消息的发送通道
	private static Message message;
	public static void setMessage(Message m) {
		message = m;
	}
	public static void send() { //发送消息
		System.out.println("【消息发送】" + message.getinfo());
	}
}

public class TestDemo {
	public static void main(String[] args) throws Exception {
		Message msg = new Message(); //实例化消息主体对象
		msg.setInfo("www.csdn.com"); //设置要发送的内容
		Channel.setMessage(msg); //设置要发送的消息
		Channel.send(); //发送消息
	}
}

这个程序非常简单,实例化对象,设置内容,发送消息,但是现在的程序是单线程的模式来处理的,如果在多线程的状态下能否实现同样的效果?

class Message{ //要发送的一个消息体
	 private String info;
	 public void setInfo(String info) {
		 this.info = info;
	 }
	 public String getinfo() {
		 return this.info;
	 }
}

class Channel{ //消息的发送通道
	private static Message message;
	public static void setMessage(Message m) {
		message = m;
	}
	public static void send() { //发送消息
		System.out.println("【" + Thread.currentThread().getName() + "消息发送】" + message.getinfo());
	}
}

public class TestDemo {
	public static void main(String[] args) throws Exception {
		new Thread(()->{
			Message msg = new Message(); //实例化消息主体对象
			msg.setInfo("第一个线程的消息"); //设置要发送的内容
			Channel.setMessage(msg); //设置要发送的消息
			Channel.send(); //发送消息
		}, "发送消息者A").start();
		new Thread(()->{
			Message msg = new Message(); //实例化消息主体对象
			msg.setInfo("第二个线程的消息"); //设置要发送的内容
			Channel.setMessage(msg); //设置要发送的消息
			Channel.send(); //发送消息
		}, "发送消息者B").start();
		new Thread(()->{
			Message msg = new Message(); //实例化消息主体对象
			msg.setInfo("第三个线程的消息"); //设置要发送的内容
			Channel.setMessage(msg); //设置要发送的消息
			Channel.send(); //发送消息
		}, "发送消息者C").start();
	}
}
【发送消息者C消息发送】第二个线程的消息
【发送消息者A消息发送】第二个线程的消息
【发送消息者B消息发送】第二个线程的消息

这时候我们会发现,当有多个线程同时发送消息的时候,消息之间产生了影响。为什么会有影响?因为这三个线程通过Channel时,会将消息保存在message中,而且保存方式是private static Message message;这时候当一个线程进来,保存消息,还没来得及发出去第二个线程又进来了,这时候message自然就出现了不同步的问题,或者说数据覆盖。

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

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

 

class Message{ //要发送的一个消息体
	 private String info;
	 public void setInfo(String info) {
		 this.info = info;
	 }
	 public String getinfo() {
		 return this.info;
	 }
}

class Channel{ //消息的发送通道
	private static final ThreadLocal<Message> THREADLOCAL = new ThreadLocal<Message>();
	public static void setMessage(Message m) {
		THREADLOCAL.set(m); //向ThreadLocal中存储数据
	}
	public static void send() { //发送消息
		System.out.println("【" + Thread.currentThread().getName() + "消息发送】" 
	+ THREADLOCAL.get().getinfo());
	}
}

public class TestDemo {
	public static void main(String[] args) throws Exception {
		new Thread(()->{
			Message msg = new Message(); //实例化消息主体对象
			msg.setInfo("第一个线程的消息"); //设置要发送的内容
			Channel.setMessage(msg); //设置要发送的消息
			Channel.send(); //发送消息
		}, "发送消息者A").start();
		new Thread(()->{
			Message msg = new Message(); //实例化消息主体对象
			msg.setInfo("第二个线程的消息"); //设置要发送的内容
			Channel.setMessage(msg); //设置要发送的消息
			Channel.send(); //发送消息
		}, "发送消息者B").start();
		new Thread(()->{
			Message msg = new Message(); //实例化消息主体对象
			msg.setInfo("第三个线程的消息"); //设置要发送的内容
			Channel.setMessage(msg); //设置要发送的消息
			Channel.send(); //发送消息
		}, "发送消息者C").start();
	}
}

每一个线程通过ThreadLocal只允许保存一个数据,在开发中它可以解决资源的引用传递的多线程安全的解决方案。


定时调度

定时器的主要操作就是进行定时任务的一个处理,就像每天早晨的闹铃铃声一样。在Java中提供有定时任务的支持,但是这种任务的处理只是实现了一种叫做间隔触发的操作。

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

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

class MyTask extends TimerTask{ //任务主体
	@Override
	public void run() { //多线程处理方法
		System.out.println(Thread.currentThread().getName() + "定时任务执行、当前时间:"
				+ System.currentTimeMillis());
	}
}
public class TestDemo {
	public static void main(String[] args) throws Exception {
		Timer timer = new Timer(); //定时任务
		timer.schedule(new MyTask(), 0); //延迟时间设置为0毫秒,表示立即启动
	}
}

这是一个很简单的定时任务,但是启动执行之后就没有动静了,我们可以给他设置间隔执行:

class MyTask extends TimerTask{ //任务主体
	@Override
	public void run() { //多线程处理方法
		System.out.println(Thread.currentThread().getName() + "定时任务执行、当前时间:"
				+ System.currentTimeMillis());
	}
}
public class TestDemo {
	public static void main(String[] args) throws Exception {
		Timer timer = new Timer(); //定时任务
		timer.scheduleAtFixedRate(new MyTask(), 100, 1000); //间隔100毫秒执行一次
	}
}

这种定时是由JDK最原始的方式提供的支持,实际上开发中利用此类方式进行的定时处理会非常复杂。


Base64加密与解密

正常来讲,加密基本都是伴随解密,所围的加密或者解密往往都有一些所谓的规则。在JDK1.8开始,提供有一组新的加密处理操作类,叫做Base64处理,在这个类中有两个内部类:

  • Base64.Encoder:进行加密处理
  •  |-加密处理:public byte[] encode(byte[] src);
  • Base64.Decoder:进行解密处理
  •  |-解密处理:public byte[] decode(byte[] src);
public class TestDemo {
	public static void main(String[] args) throws Exception {
		String msg = "www.csdn.com"; //要发送的信息内容
		String enmsg = new String(Base64.getEncoder().encode(msg.getBytes())); //数据加密
		System.out.println(enmsg);
		String demsg = new String(Base64.getDecoder().decode(enmsg.getBytes())); //数据解密
		System.out.println(demsg);
	}
}
d3d3LmNzZG4uY29t
www.csdn.com

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

public class TestDemo {
	public static void main(String[] args) throws Exception {
		String salt = "csdnjava";
		String msg = "www.csdn.com" + "{" + salt + "}"; //要发送的信息内容
		String enmsg = new String(Base64.getEncoder().encode(msg.getBytes())); //数据加密
		System.out.println(enmsg);
		String demsg = new String(Base64.getDecoder().decode(enmsg.getBytes())); //数据解密
		System.out.println(demsg);
	}
}
d3d3LmNzZG4uY29te2NzZG5qYXZhfQ==
www.csdn.com{csdnjava}

加上盐值之后发现加密效果也不是很好,最好的做法是多次加密:

class StringUtil{
	private static String SALT = "csdnjava"; //公共盐值
	private static int REPEAT = 5; //加密次数
	/**
	 * 加密处理
	 * @param str 要加密的字符串,要与盐值整合
	 * @return 加密后的数据
	 */ 
	public static String encode(String str) { //加密处理
		String temp = str + "{" + SALT + "}"; //盐值对外不公布
		byte data[] = temp.getBytes(); //将字符串变为字节数组
		for(int i = 0; i < REPEAT; i ++) {
			data = Base64.getEncoder().encode(data); //每次加密重新赋值
		}
		return new String(data);
	}
	/**
	 * 解密处理
	 * @param str 要解密的内容
	 * @return 解密后的数据
	 */
	public static String decode(String str) {
		byte data[] = str.getBytes(); //将字符串变为字节数组
		for(int i = 0; i < REPEAT; i ++) {
			data = Base64.getDecoder().decode(data); //每次加密重新赋值
		}
		return new String(data).replaceAll("\\{\\w+\\}", "");
	}
}

public class TestDemo {
	public static void main(String[] args) throws Exception {
		String str = StringUtil.encode("www.csdn.com");
		System.out.println(StringUtil.decode(str));
	}
}

这就是复杂加密,最好的方法就是使用2-3种加密程序,并且加一些完全不可解密的操作,这样的加密是最合适的。


整理了一些开发者类库,也就是Util里面的常用工具类,下次我们整理比较器、红黑二叉树的知识点,我们下次见👋

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值