Java基础学习16之定时器、加密、File类 Java基础

Java基础学习16之定时器、加密、File类

「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战」。

关于作者

  • 作者介绍

🍓 博客主页:作者主页

🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆。

🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻。


定时器

推动整个计算机硬件的发展的核心关键性技术就是时钟。所以在企业开发中定时操作往往成为开发重点。而在JDK本身也支持这种定时调度的处理操作,这种操作不会直接使用。还是和多线程有关。

如果想要定时调度处理操作,需要两个类

  • 定时调度任务:java.util.TimerTask
  • 定时调度操作:java.util.Timer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码package com.day14.demo;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class MyTask extends TimerTask{

@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS").format(new Date()));
}

}
public class TaskDemo {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new MyTask(), 1000,2000);
}
}

UUID类

UUID类是根据你当前的地址还有时间戳自动生成一个几乎不会重复的字符串。

1
2
3
4
5
6
7
8
9
10
java复制代码package com.day14.demo;

import java.util.UUID;

public class UUIDDemo {
public static void main(String[] args) {
UUID uuid = UUID.randomUUID();
System.out.println(uuid);
}
}

以后再文件上传等类似的操作中通过UUID类来进行简单的文件名称,以防止重名的问题。

Base64加密处理

Base64是一种数据加密算法,使用整个算法可以使我们的数据进行安全处理。如果要想进行加密处理可以使用两个:加密器、解密器。

加密处理

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码package com.day14.demo;

import java.util.Base64;

public class Base64Demo {
public static void main(String[] args) {
String msg = "123456";
String emsg = Base64.getEncoder().encodeToString(msg.getBytes());
System.out.println("加密:" + emsg);
byte[] decode = Base64.getDecoder().decode(emsg);
System.out.println("解密:" + new String(decode));
}
}

多次加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码package com.day14.demo;

import java.util.Base64;

public class Base64Demo {
public static void main(String[] args) {
String msg = encode("123456");
String emsg = encode(encode(encode(msg)));
System.out.println("加密:" + emsg);
byte[] decode = Base64.getDecoder().decode(emsg);
System.out.println("解密:" + new String(decode));
}
public static String encode(String code){
return Base64.getEncoder().encodeToString(code.getBytes());
}
}

还有一个做法就是种子树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码package com.day14.demo;

import java.util.Base64;

public class Base64Demo {
public static void main(String[] args) {
String sed = encode("zsr--rsz");
String msg = "123456"+sed;
String emsg = encode(msg);
System.out.println("加密:" + emsg);
byte[] decode = Base64.getDecoder().decode(emsg);
System.out.println("解密:" + new String(decode));
}
public static String encode(String code){
return Base64.getEncoder().encodeToString(code.getBytes());
}
}

必须保证长度,以后的开发将Base64和MD5一起开发,长度是32位。

ThreadLocal类

TheadLocal类不继承Thread类,也不实现Runable接口,ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有自己独立的变量。

ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象独享化。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

ThreadLocal的实现是这样的:每个Thread维护了一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身。Value是真正需要存储的变量。也就是说,ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。注意,ThreadLocalMap是使用ThreadLocal的弱引用作为key的,弱引用的对象在GC时会被回收。

通过给方法传递参数,调用两个线程输出不同的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码package com.day14.demo;
class Message{
private String note;



public String getNote() {
return note;
}

public void setNote(String note) {
this.note = note;
};
}
class GetMessage{
public void print(Message msg){
System.out.println(Thread.currentThread().getName() + msg.getNote());
}
}
public class ThreadLocalDemo {
public static void main(String[] args) {
new Thread(()->{
Message message = new Message();
message.setNote("Hello,world!!");
new GetMessage().print(message);
},"用户A").start();

new Thread(()->{
Message message = new Message();
message.setNote("Hello,world!!zsr");
new GetMessage().print(message);
},"用户B").start();
}
}

但是我现在的需求是不希望通过传递传输给GetMessage的print方法,还希望实现相同的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
java复制代码package com.day14.demo;
class Message{
private String note;



public String getNote() {
return note;
}

public void setNote(String note) {
this.note = note;
};
}
class GetMessage{
public void print(){
System.out.println(Thread.currentThread().getName() + MyUtil.message.getNote());
}
}
class MyUtil{
public static Message message;
}
public class ThreadLocalDemo {
public static void main(String[] args) {
new Thread(()->{
Message msg = new Message();
msg.setNote("Hello,world!!");
MyUtil.message = msg;
new GetMessage().print();
},"用户A").start();

new Thread(()->{
Message msg = new Message();
msg.setNote("Hello,world!!zsr");
MyUtil.message = msg;
new GetMessage().print();
},"用户B").start();
}
}

我们发现两个线程的内容并没有同步输出,所以我们会想到通过ThreadLocal类来解决此数据不同步的问题。

image-20210818114329415

1
java复制代码public class ThreadLocal<T> extends Object

这个类里面有几个重要的方法:

  • 取得数据:public T get()
  • 存放数据:public void set(T value)
  • 删除数据:public void remove()

利用ThreadLocal来解决当前我问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
java复制代码package com.day14.demo;
class Message{
private String note;



public String getNote() {
return note;
}

public void setNote(String note) {
this.note = note;
};
}
class GetMessage{
public void print(){
System.out.println(Thread.currentThread().getName() + MyUtil.get().getNote());
}
}
class MyUtil{
private static ThreadLocal<Message> tl = new ThreadLocal<Message>();
public static void set(Message msg){
tl.set(msg);
}
public static Message get(){
return tl.get();
}
}
public class ThreadLocalDemo {
public static void main(String[] args) {
new Thread(()->{
Message msg = new Message();
msg.setNote("Hello,world!!");
MyUtil.set(msg);
new GetMessage().print();
},"用户A").start();

new Thread(()->{
Message msg = new Message();
msg.setNote("Hello,world!!zsr");
MyUtil.set(msg);
new GetMessage().print();
},"用户B").start();
}
}

这样我们就解决了数据不同步的问题了。

IO—File类

基本操作

如果要想学好IO,必须要清楚抽象类、IO的操作部分掌握两个代码模型。IO的核心组成五个类(File、OutputStream、InputStream、Writer、Reader)一个接口(Serializable)。

再java.io是一个与文本本身操作有关的程序(创建、删除、信息取得—)

如果要想使用File类操作文件的话,那么肯定要通过构造方法实例化File类对象,而实例化File类对象的过程之中主要使用以下两种构造方法:

  1. 方法一:public File(String pathname);
  2. 方法二:public File(File parent,String child).

文件的基本操作,主要有以下几种功能:

  • 创建一个新文件:public boolean createNewFile() throws IOException
  • 删除文件:public Boolean delete();
  • 判断路径是否存在:public Boolean exists();

创建新文件

1
2
3
4
5
6
7
8
9
10
11
java复制代码package com.day14.demo;

import java.io.File;
import java.io.IOException;

public class FileDemo {
public static void main(String[] args) throws IOException {
File file = new File("f:\\hello.txt");
file.createNewFile();
}
}

如果文件存在进行删除,不存在进行创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码package com.day14.demo;

import java.io.File;
import java.io.IOException;

public class FileDemo {
public static void main(String[] args) throws IOException {
File file = new File("f:\\hello.txt");
if(file.exists()){//文件存在
file.delete();
}else{//文件不存在
file.createNewFile();//创建文件
}
}
}

本程序操作就表示如果文件存在则删除,如果不存在则创建一个新的文件,此时基本功能是实现了,不过这个程序此时存在三个问题:

问题一:关于路径的分隔符

在windows操作之中,使用“\”作为路径分隔符,而在Linux系统下使用“/”作为路径分隔符,而实际的开发而言,大部分情况下会在windows中做开发,而后将项目部署在Linus,那么此时,路径的分隔符都需要进行修改,这样实在是国语麻烦,为此在File类之中提供了一个常量:public static final String separator(按照Java的命名规范来讲,对于全局常量应该使用大写字母的方式定义,而此处使用的是小写,是由Java的发展历史所带来的问题)。

1
java复制代码//由不同的操作系统的JVM来决定最终的separator是什么内容File file = new File("f:" + File.separator + "hello.txt");

问题二:是有可能会出现的延迟问题

发现程序执行完成之后,对于文件的创建或者是删除是会存在一些操作上的延迟,如果现在假设先删除了一个文件,而后立刻判断此文件是否存在,那么可能得到的结果就是错误的(为true),一位所有的*.class文件都要通过JVM与操作系统间接操作,这样就有可能会出现延迟的问题。

image-20210818141728897

问题三:目录问题

之前进行文件创建的时候都是在根目录下创建完成的,如果说现在要创建的文件有目录呢?例如,现在要创建一个f:\hello\hello.txt文件,而此时在执行程序的时候hello目录不存在,这个时候执行的话就会出现错误提示:

1
java复制代码Exception in thread "main" java.io.IOException: 系统找不到指定的路径。

因为现在目录不存在,所以不能创建,那么这个时候必须首先判断要创建文件的父路径是否存在,如果不存在应该创建一个目录,之后再进行文件的创建,而要想完成这样的操作,需要一下几个方法支持:

找到一个指定文件的父路径:public File getParentFile();

创建目录:public boolean mldirs()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码package com.day14.demo;

import java.io.File;
import java.io.IOException;

public class FileDemo {
public static void main(String[] args) throws IOException {
//由不同的操作系统的JVM来决定最终的separator是什么内容
File file = new File("f:" + File.separator +"hello" + File.separator + "hello.txt");
if(!file.getParentFile().exists()){//父目录
file.getParentFile().mkdirs();//创建父目录
}
if(file.exists()){//文件存在
file.delete();
}else{//文件不存在
file.createNewFile();//创建文件
}
}
}

以后在任何的java.io.File类开发过程之中,都一定要考虑文件目录的问题。

取得文件信息

在File类之中还可以通过以下的方法取得一些文件的基本信息:

方法名称 描述
public String getName() 取得文件的名称
public boolean isDirectory() 给定的路径是否为文件夹
public boolean isFile() 给定的路径是否是文件
public boolean isHidden() 是否是隐藏文件
public long lastModified() 文件的最后一次修改日期
public long length() 取得文件大小,是以字节为单位返回的。

获取文件的大小信息以及核心信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码package com.day14.demo;
import java.io.File;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
class MyMath{
public static double round(double num, int scale){
return Math.round(num * Math.pow(10, scale)) / Math.pow(10, scale);
}
}
public class FileDemo2 {
public static void main(String[] args) {
File file = new File("f:" + File.separator + "701_03_支付宝沙箱使用.avi");
if(file.exists() && file.isFile()){
System.out.println("1文件大小为:" + MyMath.round(file.length() / (double) 1024 / 1024, 2));
System.out.println("最后一次修改日期:"+ new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date(file.lastModified())));
System.out.println("2文件大小为:" + new BigDecimal(file.length() / (double)1024 /1024).divide(new BigDecimal(1), 2, BigDecimal.ROUND_HALF_UP).doubleValue());
}
}
}

列出目录内容:public File [] listFiles(),此方法将目录中的所有文件以File对象数组的方式返回;

列出目录的所有结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码package com.day14.demo;

import java.io.File;
import java.util.Arrays;

public class FileDemo3 {
public static void main(String[] args) {
File file = new File("f:" + File.separator);
if(file.exists() && file.isDirectory()){
File[] listFiles = file.listFiles();
System.out.println(Arrays.toString(listFiles));
}
}
}

综合案例:目录列表

现在希望把一个目录下的全部文件都列出来,那么这种情况下只能采用递归:因为列出一个目录下的全部文件或着文件夹之后,如果发现列出的内容是文件夹,则应该向后继续列出。

列出指定目录下的所有文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码package com.day14.demo;

import java.io.File;

public class FileListDemo {
public static void main(String[] args) {
File file = new File("f:" + File.separator);
list(file);
}
public static void list(File file){
if(file.isDirectory()){
File[] files = file.listFiles();
if(files != null){
for (int i = 0; i < files.length; i++) {
list(files[i]);//继续列出
}
}
}
System.out.println(file);
}
}

线程阻塞问题

现在代码都在main线程下执行,如果该程序执行完成后才能继续执行下一条语句,要想解决这种耗时的问题。最好产生一个新的线程进行列出。

1
java复制代码package com.day14.demo;import java.io.File;public class FileListDemo {	public static void main(String[] args) {		new Thread(()->{			File file = new File("f:" + File.separator);		list(file);		},"列出线程").start();				System.out.println("开始进行文件信息列出。。。。");	}	public static void list(File file){		if(file.isDirectory()){			File[] files = file.listFiles();			if(files != null){				for (int i = 0; i < files.length; i++) {				list(files[i]);//继续列出				}			}			}		System.out.println(file);	}}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%