Java基础
zsr6135 人气:01、定时器
推动整个计算机硬件的发展的核心关键性技术就是时钟。所以在企业开发中定时操作往往成为开发重点。而在JDK本身也支持这种定时调度的处理操作,这种操作不会直接使用。还是和多线程有关。
- 如果想要定时调度处理操作,需要两个类
- 定时调度任务:java.util.TimerTask定时调度操作:java.util.Timer
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); } }
2、UUID类
UUID类是根据你当前的地址还有时间戳自动生成一个几乎不会重复的字符串。
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类来进行简单的文件名称,以防止重名的问题。
3、Base64加密处理
Base64是一种数据加密算法,使用整个算法可以使我们的数据进行安全处理。如果要想进行加密处理可以使用两个:加密器、解密器。
加密处理
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)); } }
多次加密
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()); } }
还有一个做法就是种子树
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位。
4、ThreadLocal类
TheadLocal类不继承Thread类,也不实现Runable接口,ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有自己独立的变量。
ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象独享化。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
ThreadLocal的实现是这样的:每个Thread维护了一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身。Value是真正需要存储的变量。也就是说,ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。注意,ThreadLocalMap是使用ThreadLocal的弱引用作为key的,弱引用的对象在GC时会被回收。
通过给方法传递参数,调用两个线程输出不同的信息
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方法,还希望实现相同的功能。
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(); } }
我们发现两个线程的内容并没有同步输出,所以我们会想到通过ThreadLocal类来解决此数据不同步的问题。
public class ThreadLocal<T> extends Object
这个类里面有几个重要的方法:
- 取得数据:public T get()
- 存放数据:public void set(T value)
- 删除数据:public void remove()
利用ThreadLocal来解决当前我问题
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(); } }
这样我们就解决了数据不同步的问题了。
5、IO—File类
5.1 基本操作
如果要想学好IO,必须要清楚抽象类、IO的操作部分掌握两个代码模型。IO的核心组成五个类(File、OutputStream、InputStream、Writer、Reader)一个接口(Serializable)。
再java.io是一个与文本本身操作有关的程序(创建、删除、信息取得—)
如果要想使用File类操作文件的话,那么肯定要通过构造方法实例化File类对象,而实例化File类对象的过程之中主要使用以下两种构造方法:
- 方法一:public File(String pathname);
- 方法二:public File(File parent,String child).
文件的基本操作,主要有以下几种功能:
- **创建一个新文件:**public boolean createNewFile() throws IOException
- **删除文件:**public Boolean delete();
- **判断路径是否存在:**public Boolean exists();
创建新文件
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(); } }
如果文件存在进行删除,不存在进行创建
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的发展历史所带来的问题)。
//由不同的操作系统的JVM来决定最终的separator是什么内容File file = new File("f:" + File.separator + "hello.txt");
问题二:是有可能会出现的延迟问题
发现程序执行完成之后,对于文件的创建或者是删除是会存在一些操作上的延迟,如果现在假设先删除了一个文件,而后立刻判断此文件是否存在,那么可能得到的结果就是错误的(为true),一位所有的*.class文件都要通过JVM与操作系统间接操作,这样就有可能会出现延迟的问题。
问题三:目录问题
之前进行文件创建的时候都是在根目录下创建完成的,如果说现在要创建的文件有目录呢?例如,现在要创建一个f:\hello\hello.txt文件,而此时在执行程序的时候hello目录不存在,这个时候执行的话就会出现错误提示:
Exception in thread "main" java.io.IOException: 系统找不到指定的路径。
因为现在目录不存在,所以不能创建,那么这个时候必须首先判断要创建文件的父路径是否存在,如果不存在应该创建一个目录,之后再进行文件的创建,而要想完成这样的操作,需要一下几个方法支持:
**找到一个指定文件的父路径:**public File getParentFile();
**创建目录:**public boolean mldirs()。
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类开发过程之中,都一定要考虑文件目录的问题。
5.2 取得文件信息
在File类之中还可以通过以下的方法取得一些文件的基本信息:
方法名称 | 描述 |
public String getName() | 取得文件的名称 |
public boolean isDirectory() | 给定的路径是否为文件夹 |
public boolean isFile() | 给定的路径是否是文件 |
public boolean isHidden() | 是否是隐藏文件 |
public long lastModified() | 文件的最后一次修改日期 |
public long length() | 取得文件大小,是以字节为单位返回的。 |
获取文件的大小信息以及核心信息
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对象数组的方式返回;
列出目录的所有结构
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)); } } }
5.3 综合案例:目录列表
现在希望把一个目录下的全部文件都列出来,那么这种情况下只能采用递归:因为列出一个目录下的全部文件或着文件夹之后,如果发现列出的内容是文件夹,则应该向后继续列出。
列出指定目录下的所有文件
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线程下执行,如果该程序执行完成后才能继续执行下一条语句,要想解决这种耗时的问题。最好产生一个新的线程进行列出。
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); }}
6、IO— 字节流与字符流
字符流和字节流最本质的区别就是只有一个字节流是原生的操作,二字符流是经过处理后的操作。经过磁盘数据保存所保存的支持的数据类型只有:字节,所有磁盘中的数据必须先读到内存后才可以操作,内存里面会帮助我们把字节变为字符。字符更加适合处理中文。
**字节操作流:**OutputStream,InputStream;
**字符操作流:**Writer,Reader。
但是不管是字节流还是字符流的操作,本身都表示资源操作,而执行所有的资源都会按照如下几个步骤进行,下面以文件操作为例(对文件进行读,写操作):
- 要根据文件创建File对象
- 根据字节流或字符流的子类实例化我们的父类对象
- 进行数据的读取、写入操作
- 关闭流(clone())
对于IO操作属于资源处理,所有的对于资源的处理进行处理完成后必须进行关闭,否则资源就再也无法执行。
6.1 字节输出流:OutputStream
Java.io.OutputStream主要的功能是进行字节数据的输出的,而这个类的定义如下:
public abstract class OutputStream extends Object implements Closeable, Flushable
发现OutputStream类定义的时候实现了两个接口:Closeable,Flushable,这两个接口定义如下:
Closeable: | Flushable: |
public interface Closeable extends AutoCloseable{ public void close() throws IOException; } |
public interface Closeable{ public void flush() throws IOException; } |
当取得了OutputStream类实例化对象之后,下面肯定要进行输出操作,在OutputStream类之中定义了三个方法:
**输出单个字节数组数据:**public abstract void write(int b) throws IOException
**输出一组字节数组数据:**public void write(byte[] b) throws IOException
**输出部分字节数组数据:**public void write(byte[] b,int off,int len) throws IOException
提示:对于Closeable继承的AutoCloseable接口
AuotCloseable实在JDK1.7的时候又增加了一个新的接口,但是这个接口的定义和Closeable定义是完全一样的,我个人认为:有可能在一些其他的类上出现了自动的关闭功能,Closeable是手工关闭,AutoCloseable属于自动关闭。
但是对于Closeable和Flushable这两个接口实话而言不需要关注,因为从最早的习惯对于flush()和close()连个方法都是直接在OutputStream类之中定义的,所以很少去关心这些父接口问题。
对于OutputStream类而言发现其本身定义的是一个抽象类(abstract class),按照抽象类的使用原则来讲,如果要想为父类实例化,那么就需要使用子类,就需要定义抽象的子类,而现在如果要执行的是文件操作,则可以使用FileOutputStream子类完成。如果按照面向对象的开发原则,子类要为抽象类进行对象的实例化,而后调用的方法以父类中定义的方法为主,而具体的实现找实例化这个父类的子类完成,也就是说在整个的操作之中,用户最关心的只有子类的构造方法:
**实例化FileOutputStream(新建数据):**public FileOutputStream([File file) throws FileNotFoundException
**实例化FileOutputStream(追加数据):**public FileOutputStream(File file,boolean append) throws FileNotFoundException
实现文件内容的输出
package com.day14.demo; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; public class OutputDemo { public static void main(String[] args) throws Exception { //1.定义文件路径 File file = new File("f:" + File.separator + "test" + File.separator + "hello.txt"); if(!file.getParentFile().exists()){//父路径不存在 file.getParentFile().mkdirs();//创建父目录 } //2.要输出的数据 String str = "Hello,zsr!!!!"; //3.实例化对象 FileOutputStream stream = new FileOutputStream(file); //4.将内容写进stream stream.write(str.getBytes());//输出数据,要将输出的数据变为字节数组输出 //5.关闭流 stream.close(); } }
这里默认执行时进行文档内容的覆写,如果不希望进行文档内容的覆写可以直接将FileOutputStream改为
FileOutputStream stream = new FileOutputStream(file,true);
如果对写入的内容需要换行操作必须使用\r\n进行换行操作。
6.2 字节输入流:InputStream
如果现在要从指定的数据源之中读取数据,使用InputStream,而这个类的定义如下:
public abstract class InputStreamextends Objectimplements Closeable
发现InputStream只实现了Closeable接口
在InputStream之中定义了三个读取数据的方法:
**读取单个字节:**public abstract int read() throws IOException
**说明:**每次执行read()方法都会读取一个数据源的指定数据,如果现在发现已经读取到了结尾则返回-1.
**读取多个字节:**public int read(byte[] b) throws IOException
**说明:**如果现在要读取的数据小于开辟的字节数组,这个时候read()方法的返回值int返回的是数据个数;如果现在开辟的字节数组小于读取的长度,则这个时候返回就是长度;如果数据已经读完了,则这个时候的int返回的是-1.
**读取指定多个字节:**public int read(byte[] b,int off,int len) throws IOException
**说明:**每次只读取传递数组的部分内容,如果读取满了,返回就是长度;如果没有读取满,返回的就是读取的个数;如果读取到最后没有数据了,就返回-1
既然InputStream为抽象类,那么这个抽象类要使用就必须有子类,现在是通过文件读取内容,肯定使用FileInputStream子类进行操作,与OutputStream类的使用一样,对于FileInputStream也只关心构造方法:
**FileInputStream****类构造方法:public FileInputStream(File file) throws FileNotFoundException
文件信息的读取
package com.day14.demo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; public class InputDemo { public static void main(String[] args) throws Exception { File file = new File("f:" + File.separator + "test" + File.separator + "hello.txt"); if(!file.exists()){ System.out.println("指定文件不存在!!"); }else{ FileInputStream is = new FileInputStream(file); byte[] result = new byte[1024]; int length = is.read(result); System.out.println("读取的内容为:" + new String(result,0,length)); } } }
6.3 字符输出流:Writer
Writer类也是一个专门用来数据输出的操作类,对于中文数据来说是比较友好的,这个类定义:
public abstract class Writer extends Object implements Appendable, Closeable, Flushable
在Writer类之中定义的writer()方法都是以字符数据为主,但是在这些方法之中,只关心一个:
**输出一个字符串:**public void write(String str) throws IOException
如果要操作文件肯定使用FileWriter子类。
package com.day14.demo; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class WriterDemo { public static void main(String[] args) throws IOException { //1.定义文件路径 File file = new File("f:" + File.separator + "test" + File.separator + "hello.txt"); if(!file.getParentFile().exists()){//父路径不存在 file.getParentFile().mkdirs();//创建父目录 } //2.要输出的数据 String str = "我正在学java这门课程!!!!\r\n"; //如果想要进行内容不覆盖的直接使用true就可以了 //FileWriter out = new FileWriter(file,true); FileWriter out = new FileWriter(file); out.write(str); out.close(); } }
6.4 字符输入流:Reader
Reader是进行字符数据读取的一个操作类,其定义:
public abstract class Reader extends Object implements Readable, Closeable
在Writer类之中存在了直接输出一个字符串数据的方法,可是在Reader类之中并没有定义这样的方法,只是定义了三个按照字符串读取的方法?为什么会这样?
因为在使用OutputStream输出数据的时候,其程序可以输出的大小一定是程序可以承受的数据的大小,那么如果说使用InputStream读取的时候,可能被读取的数据非常大,那么如果一次性全读进来,就会出现问题,所以只能一个一个的进行读取。
Reader依然是抽象类,那么如果从文件读取,依然使用FileReader类
package com.day14.demo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class ReadDemo { public static void main(String[] args) throws IOException { File file = new File("f:" + File.separator + "test" + File.separator + "hello.txt"); if(!file.exists()){ System.out.println("指定文件不存在!!"); }else{ FileReader reader = new FileReader(file); char[] result = new char[1024]; int length = reader.read(result); System.out.println("读取的内容为:" + new String(result,0,length)); } } }
字符比字节的好处就是在于字符串数据的支持上,而这个好处还只是在Writer()类中体现,所以与字节流相比,字符流的操作并不是对等的关系。
6.5 字节流与字符流区别
通过我们一系统的分析,可以发现字节流和字符流的代码操作区别不大,如果从我们实际的使用,我们字节流是优先考虑,只有再我们使用中文的时候才考虑使用字符流,因为所有的字符都需要通过内存缓冲来进行处理。
既然读数据需要缓存的处理,那么写数据也同样需要。如果使用字符流没有进行刷新,那么我们的内容可能再缓存之中,所以必须进行强制刷新才能得到完整的数据内容。
package com.day14.demo; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class WriterDemo { public static void main(String[] args) throws IOException { //1.定义文件路径 File file = new File("f:" + File.separator + "test" + File.separator + "hello.txt"); if(!file.getParentFile().exists()){//父路径不存在 file.getParentFile().mkdirs();//创建父目录 } //2.要输出的数据 String str = "我正在学java这门课程!!!!\r\n"; //如果想要进行内容不覆盖的直接使用true就可以了 //FileWriter out = new FileWriter(file,true); FileWriter out = new FileWriter(file); out.write(str); out.flush(); } }
在以后的IO处理的时候,如果处理的是图片、音乐、文字都可以使用字节流,只有再处理中文的时候才会使用字符流。
7、转换流
现在对于IO操作就存在了字节流和字符流两种操作,那么对于这两种操作流之间也是可以进行转换的,而转换的操作类有两个:
将字节输出流变为字符输出流(OutputStream->Writer)——OutputStreamWriter;
将字节输入流变为字符输入流(InputStream->Reader)——InputStreaReader。
OutputStreamWriter | InputStreamReader |
public class OutputStreamWriter extends Writer | public class InputStreamReader extends Reader |
public OutputStreamWriter(OutputStream out) | public InputStreamReader(InputStream in) |
通过以上的继承结构和构造方法可以清楚发现,既然OutputStreamWriter是Writer的子类,那么必然OutputStreamWriter可以通过Writer类执行对象的向上转型进行接收,而同时这个OutputStreamWriter类的构造方法可以接收OutputStream,这样就可以完成转型。
将字节输出流变为字符输出流
package com.day14.demo; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; public class OutWriterDemo { public static void main(String[] args) throws Exception { //1.定义文件路径 File file = new File("f:" + File.separator + "test" + File.separator + "hello.txt"); if(!file.getParentFile().exists()){//父路径不存在 file.getParentFile().mkdirs();//创建父目录 } //2.要输出的数据 String str = "Hello,world!!!!"; //3.实例化对象 OutputStream stream = new FileOutputStream(file,true); //4.将内容写进stream Writer out = new OutputStreamWriter(stream); out.write(str); //5.关闭流 out.close(); } }
对于文件操作可以使用FileInputStream,FileOutputStream,FileReader,FileWriter四个类,那么下面分别观察这四个类的继承结构。
观察FileInputStream,FileOutoutStream类的继承结构
FileInputStream | FileOutoutStream |
java.lang.Object java.io.InputStream java.io.FilterInputStream |
java.lang.Object java.io.OutputStream java.io.FileOutputStream |
观察FileReader,FileWriter类的继承结构
FileReader | FileWrite |
java.lang.Object java.io.Reader java.io.InputStreamReader java.io.FileReader |
java.lang.Object java.io.Writer java.io.OutputStreamWriter java.io.FileWriter |
通过以上的继承关系也可以发现,实际上所有的字符数据都是需要进行转换的,依靠转换流完成,以后真正保存或者是传输的数据是不可能有字符的,全部都是字节,而字节只是在电脑之中处理后的结果。
加载全部内容