1、多线程
概念
在一个时间段上可以同时运行多个程序,对CPU进行资源的轮流抢占。所以在同一个时间段上会有多个程序依次执行,但是在同一个时间点上CPU只会执行一个进程
1.1、多线程编程
Thread实现多线程
Java中的Java.lang.Thread,只要有一个类继承了此类就便是这个类是线程的主体类。但还需覆写Thread类中提供的一个run()方法,而这个方法就是线程的主方法。
但是必须使用Start()方法进行启动
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
package com.ice.L1;
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//总结:注意,线程开启不一定立即执行,由cpu调度
public class D1 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码————"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
D1 d1 = new D1();
//调用start()方法开启线程
d1.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}
基于Runnable接口实现多线程
因为Java程序里对于继承永远都存在有单继承局限的;
可以实现Runnable接口。但是这样就没有了Thread.start()方法,所以在Thread类中:
- 构造方法:public Thread(Runnable target)
使用lambda表达式
package Thread;
public class ThreadDemo {
public static void main(String[] args) {
for (int i = 0; i <3 ; i++) {
String title = "线程对象"+i;
new Thread(()->{
for (int j = 0; j < 10; j++) {
System.out.println(title + "运行,j = " + j);
}
}).start();
}
}
}
在以后的开发中,优先考虑的就是Runnable接口实现,并且永恒都是通过Thread类对象启动多线程。
Thread与Runnable关系
- 继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
- 实现Runnable接口
- 实现接口Runnable具备多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对戏被多个线程使用
多线程的设计中,使用了代理设计模式的结构,用户自定义的线程主体只是负责项目核心功能的实现,其余全部交给Thread类来处理。
Callable(了解)
可以定义返回值
可以抛出异常
解释Runnable 与 Callable的区别?
- Runnable是在JKD1.0的时候提出的多线程实现接口,而Callable是在JDK1.5的时候提出的
- java.lang.Runnable接口中只提供有一个run()方法,并且没有返回值
- java.util.concurrent.Callable接口提供有call()方法,可以有放回值
1.2、线程常用操作方法
线程的命名与取得
因为多线程的运行状态是不确定的,那么在程序的开发中为了可以获取操作,那么线程名字是一个重要的概念,这样在Thread类中就有提供线程名的操作
- 构造方法:public Thread(Runnable target,String name);
- 设置名字:public final void setName(String name);
- 取得名字:public final String getName();
获取当前线程:public static Thread currentThread();
线程的休眠
如果希望某个线程可以暂缓执行,那么就可以使用休眠的处理;
- 休眠:public static void sleep(long millis) throws InterruptedException;
- 休眠:public static void sleep(long millis, int nanos) throws InterruptedException
休眠必须会有异常抛出。
package Thread;
public class ThreadDemo {
public static void main(String[] args) {
new Thread(()->{
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName() + "、x = " + j);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"线程对象").start();
}
}
线程中断
在Thread类里面提供有这种中断执行的处理方法:
- 判断线程是否被中断:public boolean isInterrupted()
- 中断线程执行:public void interrupt()
线程强制执行
当满足某些条件后,某一个线程对象将可以一直独占资源,一直到该线程程序执行结束。
- 强制执行:public final void join() throws InterruptedException
线程礼让
线程的礼让是指事先将资源让出去,让别的进程先执行
- 礼让:public static void yield()
礼让执行的时候每一次调用yield()方法都只会礼让一次当前的资源。
线程的优先级
从理论上讲线程的优先级越高越有可能先执行(先抢到资源)。
- 设置优先级:public final void setPriority(int newPriority)
- 获取优先级:public final int getPriority()
在进行优先级定义的时候都是通过int型数字来完成的,
- 最高优先级:public static final int MAX_PRIORITY 10
- 中等优先级:public static final int NORM_PRIORITY 5
- 最低优先级:public static final int MIN_PRIORITY 1
主线程和默认创建的线程都是中等优先级
1.3、线程的同步与死锁
当多个线程访问同一资源的时候如果处理不当就会产生数据的不同步。
线程同步处理
解决同步问题的关键是——锁 指的是当某一个线程执行操作的时候,其他线程在外面等待;
如果想在程序中实现这把锁的功能,就可以使用synchronized关键字来实现。利用此关键字可以定义同步方法和同步代码块,在同步代码块的操作里面的代码只允许一个线程执行。
synchronized(同步对象){
同步代码操作;
}
加入同步处理后,程序的整体的性能下降了。同步实际上会造成性能的降低。
程序死锁
死锁是在进行多线程同步的处理之中有可能产生的一种问题,所谓的死锁是指若干个线程彼此相互等待的状态。
若干个线程访问同一资源时一定要进行同步处理,而过多的同步会造成死锁。
线程等待与唤醒
主要依靠的时Object类中提供的方法处理的:
- 等待:
- 死等:public final void wait() throws InterruptedException
- 可以设置唤醒时间:public final void wait(long timeoutMillis) throws InterruptedException
- 设置等待时间:public final void wait(long timeoutMillis, int nanos) throws InterruptedException
- 唤醒:
- 唤醒第一个等待线程:public final void notify()
- 唤醒全部的等待线程:public final void notifyAll()
守护线程
如果现在主线程的程序或者其他的线程还在执行的时候,那么守护线程讲一直存在,并且运行在后台状态
- 设置守护线程:public final void setDaemon(boolean on)
- 判断是否为守护线程:public final boolean isDaemon()
所有的守护线程多是围绕在主线程的周围,如果程序执行完毕了,说胡线程也就消失了。在JVM中最大的守护线程就是GC线程
volatile关键字
在多线程的定义中, volatile关键字主要是在属性定义上使用的,表示此属性为直接数据操作,而不进行副本的拷贝处理。
范例
package Thread;
public class TestDemo {
public static void main(String[] args) {
Resource res = new Resource();
AddThread at = new AddThread(res);
SubThread st = new SubThread(res);
new Thread(at,"加法线程-A").start();
new Thread(at,"加法线程-B").start();
new Thread(st,"减法线程-X").start();
new Thread(st,"减法线程-Y").start();
}
}
class AddThread implements Runnable{
private Resource resource;
public AddThread(Resource resource){
this.resource=resource;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
this.resource.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class SubThread implements Runnable{
private Resource resource;
public SubThread(Resource resource){
this.resource=resource;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
this.resource.sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Resource{//定义一个操作的资源
private int num = 0;//这个要进行加减操作的数据
private boolean flag = true; //加减的切换
//flag = true 表示可以进行加法操作,但是无法进行减法操作
//flag = false 表示可以进行剑法操作,但是无法进行加法操作
public synchronized void add() throws InterruptedException {//执行加法操作
if (this.flag == false){
super.wait();
}
Thread.sleep(100);
this.num++;
System.out.println("【加法操作"+Thread.currentThread().getName()+"】num="+this.num);
this.flag = false;
super.notifyAll();
}
public synchronized void sub() throws InterruptedException {//执行减法操作
if (this.flag == true){
super.wait();
}
Thread.sleep(200);
this.num--;
System.out.println("【减法操作"+Thread.currentThread().getName()+"】num="+this.num);
this.flag = true;
super.notify();
}
}
2、Java常用类库
2.1、Java基础类库
StringBuffer类
我们经常使用String类,但是String类有一个最大的弊端:内容不允许修改,虽然大部分的情况下都不会涉及修改,但依然会有这种情况出现
然而就出现了StringBuffer类。
- 构造方法:public StringBuffer() public StringBuffer(String str)
- 追加方法:public StringBuffer append(数据类型 变量),相当于字符串中的“+”
插入数据:public StringBuffer insert(int offset, 数据类型 b)
删除指定范围的数据:public StringBuffer delete(int start, int end)
字符串内容反转:public StringBuffer reverse()
在Java类库中还有一个StringBuilder类,其实与StringBuffer的功能相同,但是最大的区别在于StringBuffer类中的方法属于线程安全的,全部使用synchronized关键字进行标注,而StringBuilder类属于非线程安全的。
CharSequence接口
字符序列
描述字符串接口的接口,常用接口类:
String类 | StringBuffer类 | StringBuilder类 |
---|---|---|
public final class String extends Object implements Serializable, Comparable, CharSequence | public final class StringBuffer extends Object implements Serializable, Comparable, CharSequence | public final class StringBuilder extends Object implements Serializable, Comparable, CharSequence |
只要有字符串就可以为CharSequence接口实例化
AutoCloseable接口
实现自动关闭
主要应用与进行资源开发的处理上,以实现资源的自动关闭(释放资源)
只有一个方法:
- 关闭:public void close() throws Exception
必须在程序上使用异常的明确定义后,才会自动使用关闭方法
Runtime类
描述的是运行时的状态。
由于每一个JVM进程里面只允许提供一个Runtime类对象,所以这个类的构造方法默认私有化了,那么就使用的是单例设计模式,并且单例设计模式一定会提供一个static方法设置默认对象
获得对象:public static Runtime getRuntime()
- 获取最大可用内存空间:public long maxMemory(),默认的配置是本机系统内存的4分之一
- 获取可用内存空间:public long totalMemory(),默认的配置为本机系统内存的64分之一
- 获取空闲内存空间:public long freeMemory()
- 手工进行GC处理:public void gc()
GC(Garbage Collector)垃圾收集器,是可以由系统自动调用的垃圾释放功能,或者使用Runtime中的gc()手工调用
System类
- 数组拷贝:public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
- 获取当前的日期时间数值:public static long currentTimeMillis()
- 进行垃圾回收:public static void gc()——方法内部 Runtime.getRuntime().gc()
Cleaner类
在JDK1.9后提供的一个对象清理操作,主要功能是进行finialize()方法的替代。
用户收尾操作,每一个实例化对象在回收之前的一系列操作,最初实现对象首位方法的是Object的finialize()方法。
对象克隆
所谓对象克隆指的就是对象的复制,而且属于权限的复制,即:使用已有的对象内容克隆一个新的对象,克隆需要使用Object类中的 clone()方法
- protected Object clone() throws CloneNotSupportedException
所有的类都继承Object父类,所以所有的类都有clone()方法,但是并不是所有的类都希望被克隆,所以如果希望实现对象可能,那么对象所在的类需要实现一个Cloneable接口,此接口并没有任何的方法提供,是因为它描述的是一种能力。
但是由于是protected 所以对象必须覆写clone()方法 调用父类方法才可以使用。
2.2、数字操作类
Math数学计算类
进行数学计算的操作类,该类的构造方法都私有化了,但是不是单例模式,而且所有方法都是static型的方法,通过类名称静态导入
Random类
Java.util.Random类的主要功能是产生随机数。
- 产生一个不大于边界的随机正整数:public int nextInt(int bound)
大数字操作类
可以实现海量数字的计算(基础计算)
很大的数字计算 当double无法表示时,就必须使用String来表示,这需要逐位拆分,每一位自己计算,然后独立控制进位。
所以就有了两个大数字的操作类,BigInteger、BigDecimal
两个类都继承 Number类,所以可以进行装箱和拆箱处理
2.3、日期操作类
Date日期处理类
Java.util.Date类,如果直接实例化,就可以得到当前的日期时间。
其实Date类中只是对long数据的一种包装
- 将long转为Date:public Date(long date)
- 将Date转为long:public long getTime()
long之中保存毫秒的数据级,这样方便程序处理
SimpleDateFormat日期处理类
日期格式化
Java.text.SimpleDateFormat
继承DateFormat 抽象类,Format抽象类
- 【DateFormat】将日期格式化:public final String format(Date date)
- 【DateFormat】将字符串转为日期:public Date parse(String source) throws ParseException
- 构造方法:public SimpleDateFormat(String pattern)
- 日期格式:年(yyyy)、月(MM)、日(dd)、时(HH)、分(mm)、秒(ss)、毫秒(SSS)
2.4、正则表达式
认识
正则表达式最早是从Perl语言里面发展而来的,在JDK1.4以前需要引入jar包,在JDK1.4以后,正则表达式被JDK默认支持。并且提供由Java.util.regex开发包,同时针对String类也进行修改,可以直接对正则处理,
使用正则最大的特点在于方便进行验证处理,以及方便进行复杂字符串的修改
正则常用标记(背)
在JDK1.4后,Java.util.regex里的包里提供有一个Pattern程序类,在这个程序类里面定义了所有支持正则标记
- 【数量:单个】字符匹配
- 任意字符:表示由任意字符组成;
- \\:匹配“\”;
- \n:匹配换行;
- \t:匹配制表符;
- 【数量:单个】字符集(可以从里面任选一个字符)
- [abc]:表示可能是字符a、b、c中的任意一个;
- [^abc]:表示不是字符a、b、c中的任意一个;
- [a-zA-Z]:表示由一个任意字母组成,不区分大小写;
- [0-9]:表示由以为数字所组成
- 【数量:单个】简化字符集
- .:表示任意的一个字符;
- \d:等价于“[0-9]"范围
- \D:等价于"[^0-9]"范围
- \s:匹配任意的一位空格,可能是空格、换行、制表符
- \S:匹配任意的非空格数据;
- \w:匹配字母、数字、下划线,等价于”[a-zA-Z_0-9]“
- \W:匹配非字母、数字、下划线,等价于”[^a-zA-Z_0-9]“
- 【边界匹配】:
- ^:匹配边界开始
- $:匹配边界结束
- 【数量表示】默认情况下只有添加了数量单位才可以匹配多位字符
- 表达式?:该正则可以出现0次或1次
- 表达式*:该正则可以出现0次、1次或多次
- 表达式+:该正则可以出现1次或多次
- 表达式{n}:表达式的长度正好位n次
- 表达式{n,}:表达式的长度位n次以上
- 表达式{n,m}:表达式的长度在n~m次
- 【逻辑表达式】可以连接多个正则
- 表达式X表达式Y:X表达式之后紧跟上Y表达式
- 表达式X|表达式Y:由一个表达式满足即可
- (表达式):位表达式设置一个整体描述,可以为整体描述设置数量单位。
String类对正则的支持
进行正则表达式大部分处理的情况下都会基于String类来完成。
只要方法内带有 String类中方法参数带有 regex的都是支持正则的
- 将指定字符串进行正则判断:public boolean matches(String regex)
- 替换全部:public String replaceAll(String regex, String replacement)
- 替换首个:public String replaceFirst(String regex, String replacement)
- 正则拆分:public String[] split(String regex)
- 正则拆分:public String[] split(String regex, int limit)
需要注意的是,正则表达式无法对里面的内容进行判断,只能够对格式进行判断
Java.util.regex包支持
虽然大部分的情况下都可以利用String类来实现正则的操作,但是有些复杂的情况下无法使用String类,那么就可以使用Java.util.regex的正则处理类,这个包中有两个类:Pattern(正则表达式编译)、Matcher(匹配)
2.5、国际化程序实现
原理
所谓国际化程序指的是同一个程序代码可以根据不同的国家实现不同的语言描述,但程序处理的核心业务是相同的。
Locale类
Java.util 里面提供有一个专门描述区域和语言编码的类:Locale类
构造方法:
public Locale(String language)
public Locale(String language, String country)
如果说现在想要自动获得当前的运行环境,那么现在就可以利用Locale类本身默认的方式进行实例化
- 读取本地默认环境:public static Locale getDefault()
也设置了常量
读取资源文件:ResourceBundle
java.util.ResourceBundle
抽象类:
public abstract class ResourceBundle
extends Object
ResourceBundle是一个抽象类,如果现在要进行此类对象的实例化,可以用static方法
- 获取ResourceBundle类对象:public static final ResourceBundle getBundle(String baseName)
- baseName:描述的是资源文件的名称,但没有后缀
- 根据key读取资源内容:public final String getString(String key)
2.6、开发支持类库
UUID类
UUID 是一种生成无重复字符串的一种程序类,这种程序类的主要功能是根据时间戳实现一个自动的无重复的字符串定义。
- 获取UUID对象:public static UUID randomUUID()
- 根据字符串获取UUID内容:public static UUID fromString(String name)
Optional类
进行null 的相关处理。
在引用接收一方往往都是被动的进行判断,为了解决这种被动的判断,Java.util.Option这个类可以实现null的处理操作:
- 返回空数据:public static Optional empty()
- 获取数据:public T get()
- 保存数据,但是不允许出现null:public static Optional of(T value)
- 如果在保存数据的时候存在null ,则会抛出NullPointException异常;
- 保存数据允许为null:public static Optional ofNullable(T value)
- 空的时候返回其他数据:public T orElse(T other)
ThreadLocal类
package cn.mldn.demo;
public class JavaAPIDemo {
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() ;
}
}
class Channel { // 消息的发送通道
private static final ThreadLocal<Message> THREADLOCAL = new ThreadLocal<Message>() ;
private Channel() {}
public static void setMessage(Message m) {
THREADLOCAL.set(m); // 向ThreadLocal中保存数据
}
public static void send() { // 发送消息
System.out.println("【" + Thread.currentThread().getName() + "、消息发送】" + THREADLOCAL.get().getInfo());
}
}
class Message { // 要发送的消息体
private String info ;
public void setInfo(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
}
定时器
进行定时任务的处理,实现一种间隔触发的操作。
如果想要实现定时的处理操作主要需要有一个定时操作主题类,以及定时任务的控制:
- java.util.TimerTask类:实现定时任务的处理
- java.util.Timer类:任务的启动,方法:
- 任务启动:public void schedule(TimerTask task, long delay) 延迟单位为毫秒
- 间隔触发:public void scheduleAtFixedRate(TimerTask task, long delay, long period)
这种定时是由JDK最原始的方式提供的支持。
Base64加密工具
Java.util.Base64加密处理类,在这个类里面由两个内部类:
- Base64.Encoder:进行加密处理
- 加密处理:public byte[] encode(byte[] src)
- Base64.Decoder:进行解密处理
- 解密处理:public byte[] decode(String src)
package cn.mldn.demo;import java.util.Base64;class StringUtil { private static final String SALT = "mldnjava" ; // 公共的盐值 private static final int REPEAT = 5 ; // 加密次数 /** * 加密处理 * @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) ; } return new String(data).replaceAll("\\{\\w+\\}", "") ; }} public class JavaAPIDemo { public static void main(String[] args) throws Exception { String str = StringUtil.encode("www.mldn.cn") ; System.out.println(StringUtil.decode(str)); }}
2.7、比较器
概念
所谓的比较器指的是进行大小关系的确定判断。
任意的一个类默认情况下是无法进行数值排序的,自己定义的类没有比较的定义(没有比较规则),所以在Java里面为例同一比较规则的定义,所以提供有比较器的接口 :Comparable
Comparable比较器
如果要实现对象的比较就必须要有比较的规则,就必须实现Comparable接口 其基本定义结构:
-
接口定义:public interface Comparable
- 方法:public int compareTo(T o)
当前数据比传入对象小返回负数,如果大于返回正数,如果等于返回0
class Person implements Comparable<Person> { private String name ; private int age ; public Person(String name, int age) { this.name = name; this.age = age; } @Override public int compareTo(Person per) { return this.age - per.age ; } // 无参构造、setter、getter略 @Override public String toString() { return "【Person类对象】姓名:" + this.name + "、年龄:" + this.age + "\n" ; }}
Comparator比较器(挽救)
Comparator属于一种挽救的比较器支持,其主要的目的为了解决一些没有使用Comparable排序的类的对象数组
解释Comparable与Comparator区别?
- java.lang.Comparable 是在类定义的时候实现的父接口,主要用于定义排序规则,里面只有一个comparato()方法
- java.util.Comparator是挽救的比较器操作,需要设置单独的比较器规则类实现排序,里面有compara()方法
3、IO操作
3.1、文件操作
File类
java.io.File类是唯一一个与文件本身操作有关的类(创建、删除、重命名)
- 构造方法:public File(String pathname) , 设置要操作完整路径;
- 构造方法:public File(File parent, String child). 设置父路径与子目录
对文件的基本操作 方法如下:
- 创建新的文件:public boolean createNewFile() throws IOException
- 判断文件是否存在:public boolean exists()
- 删除文件:public boolean delete()
使用File类创建文件
package com.ice.fileD;import java.io.File;import java.io.IOException;import java.lang.reflect.Field;public class FIleDome { public static void main(String[] args) throws IOException { File file = new File("D:\\ice.txt"); if (file.exists()){ file.delete(); }else { System.out.println(file.createNewFile()); } }}
File类就是对文件的处理
File类操作深入
- 分隔符 File类提供有一个常量表示分隔符
public static final String separator;
File file = new File("D:"+File.separator+"ice.txt");
但是现在也可以不用常量表示 多个系统可以通用
-
尽量不要重名
-
文件父路径:
- 获取父路径
public File getParentFile();
- 创建目录
public boolean mkdirs();
package com.ice.fileD;import java.io.File;import java.io.IOException;import java.lang.reflect.Field;public class FIleDome { public static void main(String[] args) throws IOException { File file = new File("D:"+File.separator+"com"+File.separator+"ice"+File.separator+"ice.txt"); if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } if (file.exists()){ file.delete(); }else { System.out.println(file.createNewFile()); } }}
获取文件信息
-
文件是否可读:public boolean canRead()
-
文件是否可写:public boolean canWrite()
-
获取文件长度:public long length() 返回一个long型,字节长度
-
最后一次修改时间:public long lastModified()
-
判断是否是目录:public boolean isDirectory()
-
判断是否为文件:public boolean isFile()
package com.ice.fileD;import java.io.File;import java.text.SimpleDateFormat;public class Dome2 { public static void main(String[] args) { File file = new File("D:" + File.separator + "www.txt"); System.out.println("文件是否可读:"+file.canRead()); System.out.println("文件是否可写:"+file.canWrite()); System.out.println("文件大小:"+file.length()); System.out.println("最后的修改时间"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.lastModified())); System.out.println("是目录吗"+file.isDirectory()); System.out.println("是文件吗"+file.isFile()); }}
- 列出目录文件:public File[] listFiles() 返回对象数组
package com.ice.fileD;import java.io.File;public class Dome2 { public static void main(String[] args) { File file = new File("D:" + File.separator ); File[] files = file.listFiles(); if (file.isDirectory()){ for (int i = 0; i < files.length; i++) { System.out.println(files[i]); } } }}
列出指定目录中的全部文件
package com.ice.fileD;import java.io.File;public class printDirectory { public static void main(String[] args) { File file = new File("D:" + File.separator+"音乐"); listDir(file); } private static void listDir(File file) { if (file.isDirectory()){ File[] files = file.listFiles(); if (files!=null){ for (int i = 0; i < files.length; i++) { listDir(files[i]); } } } System.out.println(file); }}
3.2、字节流和字符流
流的基本概念
java.io包下的File类是唯一一个与文件本身有关的程序处理类
但是在实际的开发之中IO操作的核心意义在于:输入与输出操作。
对于服务器或者是客户端而言实际上传递的就是一种数据流的处理形式,而所谓的数据流指的就是字节数据,字节流
java.io包里有两类的支持:
- 字节处理流:OutputStream(输出字节流)、InputStream(输入字节流)
- 字符处理流:Writer(输入字符流)、Reader(输入字符流)
统一步骤
- 通过 File类找到一个文件的目录和文件名
- 通过字节流或字符流的子类为父类对象实例化
- 利用字节流或字符流中的方法实现数据的输入与输出操作
- 流的操作属于资源操作,资源操作必须进行关闭处理
OutputStream 字节输出流
字节的数据是以byte类型为主的实现操作
public abstract class OutputStream extends Object implements Closeable, Flushable
实现了两个接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UusUA3zf-1634647148307)(https://i.loli.net/2021/08/07/K5GSOd1QqFhHVUu.png)]
OutputStream类定义的是一个公共的输出操作标准,一共定义的了三个操作方法
No. | 方法名称 | 类型 | 作用 |
---|---|---|---|
01 | public abstract void write(int b) throws IOException | 普通 | 输出单个字节数据 |
02 | public void write(byte[] b) throws IOException | 普通 | 输出一组字节数据 |
03 | public void write(byte[] b, int off, int len) throws IOException | 普通 | 输出部分字节数据 |
因为OutputStream类是一个抽象类,所有应该有一个子类完成向上转型 。文件处理操作 FileOutputStream
构造方法
-
【覆盖】:
public FileOutputStream(File file)throws FileNotFoundException
-
【追加】
public FileOutputStream(File file,boolean append)throws FileNotFoundException
实现内容输出
package com.ice.L1;import java.io.*;public class D1 { public static void main(String[] args) { File file = new File("D:" + File.separator + "ice" + File.separator + "ice.txt"); //1.指定要操作的文件的路径 if (!file.getParentFile().exists()){ //文件不存在 file.getParentFile().mkdirs(); //创建父目录 } try(OutputStream output = new FileOutputStream(file,true)) { //2、通过子类实例化 String str = "金银花甘露\r\n"; //要输出的文件内容 output.write(str.getBytes()); //3、将字符串变为字节数组 } catch (IOException e) { e.printStackTrace(); } }}
InputStream字节输入流
与OutputStream相对应
public abstract class InputStreamextends Objectimplements Closeable
三个核心方法
No. | 方法名称 | 类型 | 作用 |
---|---|---|---|
01 | public abstract int read() throws IOException | 普通 | 读取单个字节数据,如果读取到底了,返回-1 |
02 | public int read(byte[] b) throws IOException | 普通 | 读取一组字节数据,返回的是读取的个数,如果没有数据返回-1 |
03 | public int read(byte[] b, int off, int len) throws IOException | 普通 | 读取一组字节数据(数组的部分) |
使用FileInputStream子类实例化Input Stream抽象类
构造方法
public FileInputStream(File file)throws FileNotFoundException
package com.ice.L1;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;public class D2 { public static void main(String[] args) throws IOException { File file = new File("D:" + File.separator + "ice" + File.separator + "ice.txt"); //1.指定要操作的文件的路径 InputStream input = new FileInputStream(file); byte[] bytes = new byte[1024]; int len = input.read(bytes); System.out.println("【"+new String(bytes,0,len)+"】"); input.close(); }}
Writer字符输出流
对于java.io包而言,在JDK1.1的时候推出了字符输出流:Writer 定义如下:
public abstract class Writerextends Objectimplements Appendable, Closeable, Flushable
Appendable追加
输出方法 重点两个:
- 输出字符数组:public void write(char[] cbuf) throws IOException
- 输出字符串:public void write(String str) throws IOException
package com.ice.L1;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.Writer;public class D3 { public static void main(String[] args) throws IOException { File file = new File("D:" + File.separator + "ice" + File.separator + "ice.txt"); //指定要操作的文件的路径 if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } Writer writer = new FileWriter(file); String str = "杨枝甘露"; writer.write(str); writer.append(" 可以吧"); //追加操作 writer.close(); }}
使用Writer输出的最大优势在于可以直接利用字符串完成。Writer是字符流,优势在于中文数据
Reader 字符输入流
是实现字符输入流
public abstract class Readerextends Objectimplements Readable, Closeable
Readable 关于缓冲流、
Reader没有像Writer类一样提供有整个字符串的输入处理操作,只能够用字符数组来实现我们的接收
- 接收数据:public int read(char[] cbuf) throws IOException
package com.ice.L1;import java.io.*;public class D4 { public static void main(String[] args) throws IOException { File file = new File("D:" + File.separator + "ice" + File.separator + "ice.txt"); //指定要操作的文件的路径 if (file.exists()){ Reader re = new FileReader(file); char data[] = new char[1024]; int len = re.read(data); System.out.println("读取内容:"+ new String(data,0,len)); re.close(); } }}
字符流读取的时候只能够按照数组的形式来实现处理操作
字节流和字符流的区别
关于用OutputStream 和 Writer 来说 在结束时都使用了Close 关闭操作
但是如果使用OutputStream么有使用Close的话是可以进行数据输出,但Writer却无法进行输出,这是因为Writer使用了缓冲区
如果使用close方法后 会有强制刷新缓冲区的情况,所以这个时候会进行输出操作,如果没有关闭,那么不能进行输出操作,
但是可以使用flush()方法强行刷新缓冲区
转换流
可以实现字节流和字符流操作的功能转换
InputStreamReader:
public class InputStreamReaderextends Reader;//构造方法public InputStreamReader(InputStream in);
OutputStreamWriter :
public class OutputStreamWriterextends Writer;//构造方法public OutputStreamWriter(OutputStream out);
就是将接收的字节流程序转换成字符流
3.3、IO深入操作
字符编码
计算机内部有很多的编码,如果在进行编码时要想显示正确的内容那么就一定要用一定的解码工具,如果解密和编码不统一,那么就会出现乱码问题
在实际开发中就几种常用的编码:
- GBK/GB2312:国标编码,可以描述中文信息,其中GB2312只描述简体中文,GBK包含简体中文和繁体中文
- ISO8859-1:国际通用编码,可以用其描述所有的字母信息,如果是象形文字则需要进行编码处理
- UNICODE编码:采用十六进制存储,可以描述所有文字信息
- UTF编码:象形文字部分使用十六进制编码,而普通的字母采用的是ISO8859-1,适合快速传输,节约带宽,所以就成为了我们开发中首选的编码——UTF-8
项目中出现的乱码问题就是编码和解码标准不统一,而最好的解决乱码的问题是所有的编码都设置为UTF-8
内存操作流
略
管道流
实现两个线程之间的IO操作
对于管道流也分为两类:
- 字节管道流:PipedOutputStream、PipedInputStream;
- 连接处理:public void connect(PipedInputStream snk) throws IOException
- 字符管道流:PipedWriter、PipedReader;
- 连接处理:public void connect(PipedWriter src) throws IOException
RandomAccessFile随机读取类
java.io.RandomAccessFile 可以实现文件的跳跃式读取,可以只读取文件中间的部分内容(前提:需要有一个完善的保存形式);数据保存的位数都要确定好。
3.4、输入与输出支持
打印流
OutputStream输出流操作,功能有限,所有的数据必须转成字节数组来输出,就有局限性。
所以为OutputStream包装一个类——打印流
java.io包里提供有打印流:PrintSream、PrintWriter
PrintSream | PrintWriter |
---|---|
public class PrintStream extends FilterOutputStream implements Appendable, Closeable | public class PrintWriter extends Writer |
public PrintStream(OutputStream out) | public PrintWriter(OutputStream out) public PrintWriter(Writer out) |
System类对IO的支持
系统类,三个常量:
- 标准输出(显示器输出):public static final PrintStream out
- 错误输出:public static final PrintStream err
- 标准输入(键盘):public static final InputStream in
BufferedReader类
缓冲字符输入流,可以很好的解决字符输入流的读取问题
- 读取一行数据:public String readLine() throws IOException
Scanner类
为了解决输入流的访问问题,是上一个类的替代功能类,方法如下:
- 构造方法:public Scanner(InputStream source)
- 判读是否有数据:public boolean hasNext()
- 取出数据:public String next()
- 设置分隔符:public Scanner useDelimiter(String pattern)
使用Scanner输入数据还可以使用正则验证
3.5、对象序列化
概念
对象序列化指的是将内存中保存的对象以二进制数据流的形式进行处理,可以实现对象的保存或者是网络传输
如果对象需要序列化那么 序列化类必须实现java.io.Serializable父接口,作为序列化的标记。
这个接口没有任何方法,因为它描述的是一种能力
序列化和反序列化
序列化:ObjectOutputStream
public class ObjectOutputStreamextends OutputStreamimplements ObjectOutput, ObjectStreamConstants
- 构造:public ObjectOutputStream(OutputStream out) throws IOException
- 操作方法:public final void writeObject(Object obj) throws IOException
反序列化:ObjectInputStream
public class ObjectInputStreamextends InputStreamimplements ObjectInput, ObjectStreamConstants
- 构造:public ObjectInputStream(InputStream in) throws IOException
- 操作方法:public final Object readObject() throws IOException, ClassNotFoundException
transient关键字
有些对象属性不需要进行序列化操作,那么在属性定义上就使用transient关键字。
4、反射
4.1、反射机制
简介
所有的技术实现的目标只有一点:重用性。
getClass()可以帮助使用者找到对象的根源
Class类对象的三种实例化模式
反射中所有的核心操作都是通过Class类对象展开的,可以说Class类是反射操作的根源所在,但是这个类的实例化可以采用三种方式完成,java.lang.Class定义:
public final class Class<T> extends Objectimplements Serializable, GenericDeclaration, Type, AnnotatedElement
三种实例化形式:
-
【Object类支持】Object类可以根据实例化对象获取Class对象
-
public final Class<?> getClass()
-
**缺点:**如果现在只是想获得Class类对象,则必须产生指定类对象后才可以获得
- 【JVM直接支持】采用“类.Class”来进行
- 必须要导包
- 【Class类支持】在Class类里面提供有一个static方法:
- 加载类:public static Class<?> forName(String className) throws ClassNotFoundException
4.2、反射应用
反射实例化对象
Class对象提供有一个对象反射实例化方法(代替了关键字 new ):
-
JDK1.9以前的实例化:
@Deprecated(since="9") public T newInstance() throws InstantiationException, IllegalAccessException
-
JDK1.9以后:
clazz.getDeclaredConstructor().newInstance();
现在通过反射的对象实例化处理,依然要调用类中的无参构造方法,其本质等价于 “new()”
反射与工厂设计模式
为什么要提供一个反射的实例化?
到底是使用关键字new 还是 反射 ?
工厂设计模式特点:客户端的程序类,不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象
package cn.mldn.demo;public class JavaAPIDemo { public static void main(String[] args) throws Exception { IMessage msg = Factory.getInstance("cn.mldn.demo.NetMessage",IMessage.class) ; msg.send(); IService service = Factory.getInstance("cn.mldn.demo.HouseService",IService.class) ; service.service(); }}class Factory { private Factory() {} // 没有产生实例化对象的意义,所以构造方法私有化 /** * 获取接口实例化对象 * @param className 接口的子类 * @param clazz 描述的是一个接口的类型 * @return 如果子类存在则返回指定接口实例化对象 */ @SuppressWarnings("unchecked") public static <T> T getInstance(String className,Class<T> clazz) { T instance = null ; try { instance = (T) Class.forName(className).getDeclaredConstructor().newInstance() ; } catch (Exception e) { e.printStackTrace(); } return instance ; } }interface IService { public void service() ;}class HouseService implements IService { @Override public void service() { System.out.println("【服务】为您的住宿提供服务。"); }}interface IMessage { public void send() ; // 消息发送}class NetMessage implements IMessage { public void send() { System.out.println("【网络消息发送】www.mldn.cn"); }}
反射与单例设计模式
单例设计模式的本质:类内部的构造方法私有化,在类的内部产生类的实例化对象通过static方法调用对象进行类中的结构化调用
完整懒汉式单例设计模式
package cn.mldn.demo;public class JavaAPIDemo { public static void main(String[] args) throws Exception { for (int x = 0 ; x < 3 ; x ++) { new Thread(()->{ Singleton.getInstance().print(); },"单例消费端-" + x).start(); } }}class Singleton { private static volatile Singleton instance = null ; private Singleton() { System.out.println("【" + Thread.currentThread().getName() + "】****** 实例化Singleton类对象 ******"); } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton() ; } } } return instance ; } public void print() { System.out.println("www.mldn.cn"); }}
4.3、反射与类操作
反射获取类结构信息
一个类的基本信息主要包括类所在的包名称、父类的定义、父接口的定义。
- 获取包名称:public Package getPackage()
- 获取继承父类:public Class<? super T> getSuperclass()
- 获取实现接口:public Class<?>[] getInterfaces()
反射调用构造方法
在一个类中除了有继承的关系之外最重要的操作就是类中的结构处理。
- 实例化方法替代:clazz.getDeclaredConstructor().newInstance()
所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义方法:
- 获取所有构造方法:public Constructor<?>[] getDeclaredConstructors() throws SecurityException
- 获取指定构造方法:public Constructor getDeclaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException
- 获取所有构造方法:public Constructor<?>[] getConstructors() throws SecurityException
- 获取指定构造方法:public Constructor getConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException
java.lang.reflect.Constructor
这些方法使用 后都会获得一个Constructor的类对象,调用Constructor类之中提供的实例化方法操作:
public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
反射调用普通方法
在进行反射处理的适合也可以通过反射来获取类中的全部方法,但是类之中要提供有实例化对象。
在Class类里面提供有如下操作可以获取方法对象:
- 获取全部方法:public Method[] getMethods() throws SecurityException
- 获取指定方法:public Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException
- 获取本类全部方法:public Method[] getDeclaredMethods() throws SecurityException
- 获取本类指定方法:public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException
java.lang.reflect.Method
Method中有一个重要的方法:invoke——(反射调用类中的方法)
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
反射调用成员
获取成员属性:
- 获取本类全部成员:public Field[] getDeclaredFields() throws SecurityExceptionpublic Field[] getDeclaredFields() throws SecurityException
- 获取本类指定成员:public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
- 获取父类全部成员:public Field[] getFields() throws SecurityException
- 获取父类指定成员:public Field getField(String name) throws NoSuchFieldException, SecurityException
java.lang.reflect.Field
Field类中的方法:
- 设置属性内容:public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
- 获取属性内容:public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
- 解除封装:public void setAccessible(boolean flag)
所有的成员是在对象实例化之后进行空间分配的,所以此时一定要现有实例化对象后才能进行成员的操作
因为成员属性都进行了封装,无法调用,必须解除封装后才能进行。
Class<?> cls = Class.forName("cn.mldn.vo.Person") ; // 获取指定类的Class对象 Object obj = cls.getConstructor().newInstance() ; // 实例化对象(分配成员空间) Field nameField = cls.getDeclaredField("name") ; // 获取成员对象 nameField.setAccessible(true); // 没有封装了 nameField.set(obj, "番茄强"); // 等价于:Person对象.name = "番茄强" ; System.out.println(nameField.get(obj)); // 等价于:Person对象.name
因为对于成员输出的操作都应该用setter或getter 进行处理,所以上面的方法都不常用,实际应用方法是:
- 获取成员类型:public Class<?> getType()
Class<?> cls = Class.forName("cn.mldn.vo.Person") ; // 获取指定类的Class对象 Field nameField = cls.getDeclaredField("name") ; // 获取成员对象 System.out.println(nameField.getType().getName());//获取完整类名称“包.类”System.out.println(nameField.getType().getSimpleName());//获取类名称
Unsafe工具类
反射是java的第一大特点,一旦打开了反射的大门就可以有了更加丰富的类设计形式。
sun.misc.Unsafe类(不安全的操作),这个类的主要特点是可以利用反射来获取对象,并且直接使用底层的C++来代替JVM执行,即:可以绕过JVM的相关对象的管理机制,所以一旦使用了Unsafe类,那么项目中就无法使用JVM的内存管理机制以及垃圾回收处理。
4.4、反射与简单Java类
代码——通过实现传入由 “属性:内容|属性:内容” 的字符串 实现对简单Java类的自动赋值操作。
- 单级属性赋值
- 设置多种数据类型
- 级联对象实例化
- 级联属性赋值
4.5、ClassLoader类加载器
4.6、反射与代理设计模式
动态代理模式
InvocationHandler接口,这个接口规定了代理方法的执行
这里面只有一个方法
/** * 代理方法的调用,代理主题类里面执行的方法最终都是此方法 * @param proxy 要代理的对象 * @param method 要执行的接口方法名称 * @param args 传递的参数 * @return 某一个方法的返回值 * @throws Throwable 方法调用时出现的错误继续向上抛出 */public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
java.lang.reflect.proxy
- 创建代理对象:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- ClassLoader loader :获取当前真实主题类的ClassLoader;
- Class<?>[] interfaces:代理是围绕接口进行的,所以一定要获取真实主题类的接口信息;
- InvocationHandler h:代理处理的方法
package cn.mldn.demo;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JavaAPIDemo { public static void main(String[] args) throws Exception { IMessage msg = (IMessage) new MLDNProxy().bind(new MessageReal()) ; msg.send(); } }class MLDNProxy implements InvocationHandler { private Object target ; // 保存真实业务对象 /** * 进行真实业务对象与代理业务对象之间的绑定处理 * @param target 真实业务对象 * @return Proxy生成的代理业务对象 */ public Object bind(Object target) { this.target = target ; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public boolean connect() { System.out.println("【消息代理】进行消息发送通道的连接。"); return true ; } public void close() { System.out.println("【消息代理】关闭消息通道。"); } @Override public Object invoke(Object pro, Method method, Object[] args) throws Throwable { System.out.println("****** 【执行方法:】" + method); Object returnData = null ; if (this.connect()) { returnData = method.invoke(this.target, args) ; this.close() ; } return returnData; }}interface IMessage { // 传统代理设计必须有接口 public void send() ; // 业务方法}class MessageReal implements IMessage { @Override public void send() { System.out.println("【发送消息】www.mldn.cn"); }}
4.7、反射与Annotation
反射取得Annotation
在进行类或者方法定义的适合都可以使用一系列的Annotation进行声明,如果想要获取这些Annotation的信息就可以直接通过反射来完成。
在java.lang.reflect里面的一个AccessibleObject类,类中有获取Annotation类的方法。
- 获取全部Annotation:public Annotation[] getAnnotations()
- 获取指定Annotation: public T getAnnotation(Class annotationClass)
内置注解
@Override //重写@Deprecated //已过时的 不推荐使用,但可以用@SupperessWarnings //镇压警告 抑制编译时的警告信息 带参数
元注解
作用:负责注解其他注解,Java定义了4个标准的meta-annotation 类型,他们被用来提供对其他annotation类型作说明,
这些类型和它们所支持的类在Java.lang.annotation包中可以找到。
- @Target:表示我们的注解可以用在哪些地方。
- @Retention:表示我们的注解在什么地方还有效。(SOURCE<CLASS<RUNTIME)
- @Document:表示是否将我们的注解生成在Javadoc中
- @Inherited:表示子类可以继承父类的注解
自定义Annotation
可以使用@interfacce自定义注解 ,自动继承了java.lang.annotation.Annotation接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWi6vNBb-1634647148314)(https://i.loli.net/2021/08/10/bZ6SurfoGR28wY9.png)]
注解的参数:参数类型+参数名();
用default设置默认值
可以通过反射机制编程实现对这些元数据的访问
使用Annotation之后最大特点是可以接口反射机制实现程序的处理
5、java类集框架
5.1、简介
类集指的就是一套动态对象数组的实现方案
类集中提供有如下几个核心接口:Collection、List、Set、Map、Itreator、Enumeration、Queue、ListIterator。
Collection接口
java.util.Collection是单值集合操作的最大的父接口,在该接口中定义有所以的单值数据的处理操作。核心操作方法:
- 向集合保存数据:boolean add(E e)
- 追加一组数据:boolean addAll(Collection<? extends E> c)
- 清空集合,让根节点为空:void clear()
- 查询数据是否存在,需要equals()方法:boolean contains(Object o)
- 数据删除,需要equals()方法:boolean remove(Object o)
- 获取数据长度:int size()
- 将集合变为对象数组返回:Object[] toArray()
- 将集合变为Iterator接口:Iterator iterator()
有两个方法最为常用:【增加】add(E e)、【输出】iterator()
如今我们选择的是Collection的两个子接口 允许重复的List子接口和不允许重复的Set子接口
5.2、List集合
简介
List是Collection的子接口,其最大的特点是允许保存重复元素的集合;
public interface List<E>extends Collection<E>
对Collection接口进行了方法扩充
- 获取指定索引上的数据:E get(int index)
- 修改指定索引数据:E set(int index, E element)
- 返回ListIterator接口对象:ListIterator listIterator()
但是List本身依然属于一个接口,那么想要使用就一定要是有子类来完成定义,三个常用的子类:ArrayList、Vector、LinkedList。
ArrayList子类
ArrayList是List子接口使用最多的一个子类,定义:
public class ArrayList<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable
问ArrayList与LinkedList有什么区别?
- ArrayList是数组实现的集合操作,而LinkedList是链表实现的集合操作;
- 在使用List集合中的get()方法根据索引获取数据时,ArrayList的时间复杂度为O(1),而LinkedList的时间复杂度为O(n)(n为集合的长度)
- ArrayList在使用的时候默认的初始化对象数组的大小为10,如果空间不足则成二倍增长,如果保存大数据量的时候有可能会造成垃圾的产生以及性能的下降,这个时候就可以使用LinkList
Vector类中的操作方法采用的都是synchronized同步处理,Vector类中的方法在多线程访问的时候属于线程安全的,但是性能不如ArrayList高。
5.3、Set接口
Set集合最大的特点就是不允许保存重复元素,也是Collection子接口
简介
定义:
public interface Set<E>extends Collection<E>
Set没有像List一样扩充了许多新方法,没有get()方法,所以无法实现指定索引数据的获取。
有两个常用子类:HashSet、TreeSet
HashSet
HashSet是Set接口使用最多的一个子类,其最大的特点就是保存的数据是无序的,并且无重复的
public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, Serializable
TreeSet
与HashSet最大的区别在于TreeSet是有序的
public class TreeSet<E>extends AbstractSet<E>implements NavigableSet<E>, Cloneable, Serializable
当利用TreeSet保存数据的时候所有的数据都将按照数据的升序进行自动排序处理。
自定义类必须实现Comparable接口才能保存数据
**提示:**TreeSet本质是利用TreeMap子类实现的集合数据的存储,而TreeMap(树)就需要实现Comparable接口
重复元素消除说明
TreeSet子类是利用了Comparable接口来实现了重复元素的判断,但是HashSet判断重复元素的方式并不是利用Comparable接口完成的,它是利用Object类中的两个方法判断的:
- 对象编码:public int hashCode()
- 对象比较:public boolean equals(Object obj)
5.4、集合输出
对于集合操作而言,一共定有四种输出方式:Iterator迭代输出、ListIterator双向迭代输出,Enumeration枚举输出、foreach输出
Iterator迭代输出
获取Iterator的接口对象:public interface Iterator
方法:
- 判断是否有数据:public boolean hasNext()
- 取出当前数据:public E next()
- 删除:public default void remove()
实际上,java.util.Scanner就是Iterator的子类
问:Collection.remove()和Iterator.remove()的区别?
- 在进行迭代输出的时候如果使用了Collection.remove()则会造成并发更新的异常,Iterator.remove()方法实现正常的删除处理。
ListIterator双向迭代输出
使用Iterator进行的迭代输出操作有一个特点:只允许由前向后实现输出,而如果需要进行双向迭代处理就必须依靠Iterator的子接口ListIterator来实现
操作方法:
- 判断是否有前一个元素:public boolean hasPrevious()
- 获取当前元素:public E previous()
要想实现由前向后,必须先实现有后向前。
5.5、Map集合
前面的Collection接口以及其对应的子接口,主要是进行单个对象的保存,关于进行二元偶对象的保存(key=value)的形式来存储,而二元偶对象的核心意义在于,需要通过key获取对象的value
在开发中,Collection集合保存数据的目的是为了输出,Map集合保存数据的目的是为了进行key的查找。
简介
Map接口是进行二元偶对象保存的最大父接口,定义:
public interface Map<K,V>
核心操作方法:
- 向集合中保存数据: public V put(K key, V value)
- 根据key查询数据: public V get(Object key)
- 将Map集合转为Set集合: public Set<Map.Entry<K,V>> entrySet()
- 查询指定的key是否存在:public boolean containsKey(Object key)
- 将Map中的key转为Set集合: public Set keySet()
- 根据key删除指定的数据:public V remove(Object key)
在Map集合中数据保存就是按照”key=value“的形式存储的,并且使用of()的时候key是不能够重复的,也不能为空,
常用的三个子类:HashMap、Hashtable、TreeMap、LinkedHashMap
HashMap子类
定义:无序存储
public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable
HashMap可以针对key或value保存null的数据,同时key可以重复,不会出错,只会将内容进行替换。
但是对于Map接口中提供的put()方法本身是提供了返回值的,那么这个返回值指的是在重复key的情况下返回旧的value。
Map.Entry接口
有一个核心问题:Map集合里面是如何存储的
HashMap里面可以见到Node类型定义,通过源代码定义可以发现,HashMap里的Node的内部类实现了Map.Entry接口
static class Node<K,V> implements Map.Entry<K,V> {}
所以可以得出结论:所有的key和value的数据都被封装在Map.Entry接口之中。
public static interface Map.Entry<K,V>
这个内部接口里面提供了两个重要操作方法:
- 获取key:public K getKey()
- 获取value:public V getValue()
Map接口中有一个方法:
- 创建Map.Entry对象:public static <K,V> Map.Entry<K,V> entry(K k, V v)
Map.Entry的主要作用就是作为一个Key和Value的包装类型使用。大部分情况下在进行数据存储的时候都会将key和value包装为一个Map.Entry对象进行使用
利用Iterator输出Map集合
对于集合的输出而言,最标准的做法就是利用Iterator接口来完成,
也就是说步骤如下:
- 利用Map接口中提供的entrySet()方法将Map集合转为Set集合;
- 利用Set接口中的iterator()方法将Set集合转为Iterator接口实例;
- 利用Iterator进行迭代输出获取每一组的Map.Entry对象,随后通过getKey()和getValue()进行输出。
5.6、集合工具类
Stack栈操作
栈是一种先进后出的数据结构。
定义:
public class Stack<E>extends Vector<E>
两个方法:
- 入栈:public E push(E item)
- 出栈:public E pop()
出栈无数据,出现空栈异常。
Queue队列
Queue描述的是一个队列,而队列的主要特点是实现先进先出的操作形式。队列的实现可以使用LinkedList子类来完成。
方法:
- 向队列中追加数据:public boolean offer(E e) ,可以直接使用add()
- 通过队列获取数据:public E poll() ,弹出后删除数据。
优先级队列
PriorityQueue
public class PriorityQueue<E>extends AbstractQueue<E>implements Serializable
存入数据后将自动进行排序处理
Properties属性操作
资源文件(*.properties),这类文件的存储结构是按照“key=value”的形式保存的,跟Map集合很相似,但是唯一的区别在于其保存的内容只能够的String类型的数据。在java.util包中提供有Properties类型,此类是Hashtable的子类。
public class Propertiesextends Hashtable<Object,Object>
方法:
- 设置属性:public Object setProperty(String key, String value)
- 取得属性,没有返回null:public String getProperty(String key)
- 取得属性,没有返回默认值:public String getProperty(String key, String defaultValue)
- 输出属性内容:public void store(Writer writer, String comments) throws IOException
- 通过输入流读取属性内容:public void load(InputStream inStream) throws IOException
主要用于读取配置资源信息,主要是在标准设计之中作程序初始化准备的时候使用。
Collections工具类
Collections是java提供的一组集合数据的操作工具类。可以实现各个集合的操作。
为集合操作做一些支持。
5.7、Stream数据流
Stream基本操作
在JDK1.8之后,java进入了大数据的时代,所以在类集里面也支持有数据的流式分析处理操作,为此专门提供了一个Stream的接口,同时在Collection接口里面专门设置了关于Stream的实例化方法。
- 获得Stream接口对象:public default Stream stream()
MapReduce基础模型
在进行数据分析的处理中有一个最重要的基础模型:MapReduce模型,这个模型分为两个部分:Map处理部分,Reduce分析部分。就是说在进行数据分析之前必须要对数据进行合理的处理,而后才可以作统计分析操作。