高级部分
多线程
概念
程序:为完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态的对象
进程:是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生,存在和消亡的过程
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程
线程作为调度和执行单位,每个线程拥有独立的运行栈和程序计数器
多个线程操作共享的系统资源可能会带来的安全隐患
线程得创建和使用
四种方式
方式一:
1.创建一个继承于Thread类的子类
* 2.重写Thread类的run()方法
* 3.创建Thread类的子类的对象
* 4.通过此对象调用start();1.启动当前线程,2.调用当前线程的run()
//不能通过 myThread.run()启动线程
//不可以还让已经start()的线程去执行
//需要重新创建一个线程的对象调用start()
创建多线程的方式二: 实现Runnable接口
* 1. 创建一个实现了Runnable接口的类
* 2. 实现类去实现Runnable中的抽象方法: run()
* 3. 创建实现类对象
* 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5. 通过Thread类的对象调用start();
测试Thread中的常用方法:
1. start() :启动当前线程,调用当前线程的run()
2. run() : 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3. currentThread : 静态方法,返回执行当前代码的线程
4. getName() : 获取当前线程的名字
5. setName() : 设置当前线程的名字
6. yield() : 释放当前cpu的执行权
7. join() : 在线程a中调用线程b的join(),此时线程a就进入阻塞状态,知道线程b完全执行后,线程a才结束阻塞状态
8. stop() :已过时,当执行此方法时, 强制执行当前线程
9. sleep( Long millitime) : 让当前线程睡醒指定的毫秒数
10. isAlive() ; 判断当前线程是否还存活
线程的优先级:
1. MAX_PRIORITY : 10
1. MIN_PRIORITY : 1
1. NORM_PRIORITY : 5
1. getPriority( ) :返回线程优先级
1. setPriority(int newPriority): 改变线程的优先级
说明: 高优先级的线程要抢占低优先级线程cup的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行,
并不意味着只有当高优先级的线程执行完成以后,低优先级的线程才执行
比较创建线程的两种方式:
开发中:优先选择:实现Runnable接口的方式
原因:
-
实现的方式没有类的单继承局限性
-
实现的方式更适合来处理多个线程有共享数据的情况
联系: public class Thread implement Runnable
相同点:都需要重写run() ,将创建的线程要执行的逻辑声明在run()中。
线程得的生命周期
JDK用Thread.State类定义了线程的几种状态
新建 — (调用start()) 就绪 ---- 运行 ---- 阻塞(人为挂起或执行输入输出操作,不是线程的最终状态) ----- 死亡(执行run();调用线程的stop()方法;出现Error或Exception)
线程的同步
解决线程的安全问题 三种方式
* 创建3个窗口售票, 总票数100张 使用Runnable()的方式实现
* 卖票过程中出现了重票和错票
* 问题出现原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
* 解决:当一个线程在操作共享数据的时候其他线程不能参与进来,
* 直到线程A操作完共享数据时其他线程才可以操作线程,即使线程A出现了阻塞,也不能被改变
* 在java中,通过同步机制,解决线程安全的问题
* 方式一: 同步代码块
* synchronized( 同步监视器){
* //需要被同步的代码
* }
* 说明:操作共享数据的代码,即为同步代码
* 共享数据:多个线程共同操作的变量。
* 同步监视器:俗称,锁;任何一个类的对象,都可以充当一个锁
* 要求:多个线程必须共同用一把锁,可以考虑this充当同步监视器
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的
总结:
1. 同步方法仍然涉及到同步监视器,只是不需要我们显式声明
2. 非静态的同步方法,同步监视器时this
3. 静态的同步方法的同步监视器是:当前类本身
同步方式:解决了线程安全的问题,操作同步代码时只能有一个线程参与,
其他线程等待,相当于一个单线程过程,效率低---局限性
线程的死锁问题
死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
出现死锁后,不会出现异常,不会出现提示,只是所有的都线程处于阻塞状态,无法继续
解决方法:
专门的算法,原则
尽量减少同步资源的定义
尽量避免嵌套的同步
synchronized 方式与Lock的异同:
相同:二者都可以解决线程安全问题
不同点:synchronized 机制在执行完相应的同步代码后,自动地释放同步监视
Lock需要手动的启动同步( Lock()),同时结束同步也需要手动的实现(unlock())
class Window implements Runnable {
private int ticket = 100;
//实例化lock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//2.调用lock方法 锁定
lock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket);
ticket--;
} else {
break;
}
} finally {
//3.解锁
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
使用的优先顺序:
Lock —>同步代码块(已经进入了方法体,分配了相应的资源) ---->同步方法体在方法外
线程的通信
/* @description: 线程通信的例子: 使用两个线程打印1-100,线程1,线程2 交替打印
* 涉及到的三个方法:
* wait() : 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器.
* notify() : 一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait则唤醒优先级高的那个
* notifyAll() : 一旦执行此方法,就唤醒所有被wait的线程
* 注意:
* 1. wait() ,notify(), notifyAll()这三个方法必须使用在同步代码块或同步方法中
* 2. 这三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现异常
* 3. wait() ,notify(), notifyAll() 三个方法定义在java.lang.Object类中
* @Date: 2022/5/6 15:39
* @Version: 1.8.0_131
*/
class Number implements Runnable {
private int number = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
//唤醒一个 ;notifyAll()唤醒所有的
notifyAll();
if (number <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
//使得调用如下wait()方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程一");
t2.setName("线程二");
t1.start();
t2.start();
}
}
sleep()方法 和 wait()方法的异同:
相同点:一旦执行方法,都可以使得当前线程进入阻塞状态
不同点:
-
两个方法声明的位置不一样:Tread类中声明sleep(); Object类中 声明wait()
2. 调用的范围(要求)不同: sleep()可以在任何需要的场景下调用.wait()必须使用在同步代码块或同步方法 2. 关于是否释放同步监视器得问题:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁(同步监视)
JDK 5.0新增线程创建方式
新增方式一:
实现Callable接口
与使用Runnable相比,Callable功能更强大些
1. 相比run()方法,可以有返回值
1. 方法可以抛出异常
1. 支持泛型返回值
1. 需要借助Future Task类,比如获取返回值
Future 接口:
可以对具体Runnable ,Callable任务的执行结果进行取消,查询是否完成,获取结果等
Future Task 是Future 接口唯一的实现类
Future Task同时实现了Runnable,Future 接口,它既可以作为Runnable被线程执行,又可以作为Future 得到Callable的返回值
* @description: 创建线程的方式三: 实现Callable接口, ---->jdk5.0新增
* 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大
* 1.call()方法可以有返回值
* 2.call()方法可以抛出异常,被外面的操作捕获,获取异常的信息
* 3.Callable支持泛型
*
*
* @Date: 2022/5/6 17:26
* @Version: 1.8.0_131
*/
//1.创建一个实现Callable接口的实现类
class NumThread implements Callable {
//2.实现一个call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++){
if (i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口的类对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask类的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//获取Callable中call方法的返回值
//get方法的返回值即为Future Task构造器参数Callable实现类重写的call()的返回值
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
新增方式二:
使用线程池
背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创好多个线程,放入线程池中,使用时直接获取,使用完放回池中.可以避免频繁创建销毁,实现重复利用,类似生活中的公共交通工具
好处:
1.提高影响速度(为了减少创建线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize : 最大线程数
keepAliveTime : 线程没有任务时最多保持多长时间后终止
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMrCO5ru-1652776738295)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\线程池.png)]
* @description: 创建线程的方式四:使用线程池
* @Date: 2022/5/6 18:25
* @Version: 1.8.0_131
*/
class NumberThread implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
public class TreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
System.out.println(service.getClass());
service1.setCorePoolSize(15);
//service1.setKeepAliveTime();
//2.执行指定的线程操作.需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable
service.execute(new NumberThread1());//适合适用于Runnable
//service.submit();//适合适用于Callable, 可以得到返回值
service.shutdown();//关闭线程
}
}
Java常用类
字符串相关类
String是一个final,代表不可变的序列
//day02
/*
String : 字符串使用一对双引号表示
1.String声明为final ,不可被继承
2.String实现了Serializable接口:表示字符串是支持序列化的
实现了Comparable接口:表示String可以比较大小
3.String在内部定义了final char[] value用于存储字符串
4.String:代表一个不可变的字符序列。简称:可以变的特性
体现:
1.当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值
2.当对现有的字符串进行操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
3.当调用String的replace()方法修改内部指定的字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
5.通过字面量的方式(区别于)给一个字符串赋值,此时的字符串值声明在字符串常量池中,
6.字符串常量池中是不会存储相同内容字符串的
String的实例化方式
方式一:字面量定义的方式
方式二:通过new+构造器的方式
常量与常量的连接结果在常量池,且常量池中不会存在相同内容的常量
只要其中有一个是变量,结果就在堆空间中
如果拼接结果调用intern()方法,返回值就在常量池中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emzDlNlV-1652776738296)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\String常用方法1.png)]
/*
String 与基本数据类型,包装类之间的转换
String 基本数据类型,包装类:调用包装类中的静态方法:parseXxx(str)
基本数据类型,包装类---> String 调用String重载的ValueOf(XXX)
*/
@Test
public void test3(){
String s1 ="123";
//int num = (int)s1;//错误的
int num = Integer.parseInt(s1);
System.out.println(num);
String str2 = String.valueOf(num);//"123"
}
String 与 char[] 之间的转换
String 与 char[] 之间的转换
String ---> char[] 调用toCharArray
char[] ----> String 调用String的构造器
/*
String 与 byte[]之间的转换
String ---> byte[] 调用String 的 getBytes()
byte[] ---> String 调用String 的构造器
*/
StringBuffer 与StringBuilder
/*
String ,StringBuffer 与StringBuilder
String: 不可变的字符序列 底层结构用char型数组
StringBuffer : 可变的字符序列 线程安全的,效率偏低
StringBuilder : 可变的字符序列 JDK5.0新增 线程不安全,效率高
源码分析:
String str = new String();//new char[0];
String str1 = new String("abc");//new char[]{'a','b','c'}
StringBuffer sb1 = new StringBuffer();//new char[16];底层创建了一个长度是16的char型数组
System.out.println(sb1.length());//
sb1.append('a');//value[0] ='a';
sb1.append('b');value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length()+16];
//问题一,System.out.println(sb2.length());//3
//问题二:扩容问题:如果添加的数据地层数组盛不下了,那就需要扩容地层的数组
默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组中
开发中建议使用 StringBuffer(int capacity)
*/
/*
StringBuffer append(xxx) 提供了很多append()方法,用于进行字符串拼接
StringBuffer delete(int start ,int end):删除指定位置的内容
StringBuffer replace(int start ,int end ,String str)把[start,end]位置替换为str
StringBuffer insert(int offset,xxx) 在指定位置插入xxx
StringBuffer reverse() 把当前字符序列逆转
public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串
public int length();
public char charAt(int n);
public void setCharAt(int n ,char ch);
总结:
增:append(xxx)
删:delete(int start ,int end)
改: replace(int start ,int end ,String str)
查:charAt(int n)
长度:length()
遍历 for
*/
@Test
public void test02(){
StringBuffer s1 = new StringBuffer("abc");
s1.append(1);
s1.append('1');
System.out.println(s1);//abc11
//s1.delete(2, 4);
//System.out.println(s1);//ab1`
// s1.replace(2, 4, "hello");
// System.out.println(s1);//abhello1
s1.insert(2, false);
System.out.println(s1);//abfalsec11
System.out.println(s1.length());//10
}
JDK 8之前的日期时间API
/*
java.util.Date类
java.sql.Date类
1.两个构造器的使用
2.两个方法的使用
> toString();显示当前的年,月,日,时,分。秒
> getTime():获取当前date()对象的时间戳
3.java.sql.Date对应着数据库中的日期类型的变量
*/
@Test
public void test02(){
//构造器一: Date() 创建了一个对应当前时间得Date对象
Date date = new Date();
System.out.println(date.toString());//Sat May 07 17:07:35 CST 2022
System.out.println(date.getTime());//1651914474640
//构造器二 创建指定毫秒数的Date对象
Date date1 = new Date(1651914474640L);
System.out.println(date1.toString());//翻译成对应的年月日时分秒
}
//1.System类中的currentTimeMillis()
@Test
public void test01(){
long l = System.currentTimeMillis();
//1970年1月1日0时0分0秒到目前的毫秒时间差
//称为时间戳
System.out.println(l);
}
JDK 8中新增的日期时间API
-
java.time 包含值对象的基础包
-
java.time.chrono 提供对不同的日历系统的访问
-
java.time.format 格式化和解析时间和日期
-
java.time.temporal 包括地层框架和扩展特性
-
java.time.zone 包含时区支持的类
public class JDK8DateTimeTest {
/*
LocalDate,LocalTime,LocalDateTime 的使用
说明:
LocalDateTime使用频率相对较高
类似于Calendar
*/
@Test
public void test1() {
//now()
LocalDate localdate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localdate);
System.out.println(localTime);
System.out.println(localDateTime);
//of():设置指定的年月日时分秒没有时间偏移量
LocalDateTime of = LocalDateTime.of(2022, 5, 8, 15, 21);
System.out.println(of);
//getXxx()//获取属性
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getMinute());
System.out.println(localDateTime.getMonthValue());
//withXxx() 设置时间
// 体现不可变性
LocalDateTime localDateTime1 = localDateTime.withDayOfMonth(22);
System.out.println(localDateTime);//原数据不变
System.out.println(localDateTime1);//改变后的值
}
@Test
public void test() {
Date date = new Date(2022 - 1900, 5 - 1, 8);
System.out.println(date);//Thu Jun 08 00:00:00 CST 3922
}
/*
Instant使用
类似于java.lang.util 的Date类
*/
@Test
public void test2() {
//实例化 now()获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);//2022-05-08T07:37:55.404Z
//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);//2022-05-08T15:37:55.404+08:00
//获取对应的毫秒数,1970年1月1日0时0分0秒距目前 --->Date类的getTime()
long l = instant.toEpochMilli();
System.out.println(l);
//通过给定的毫秒数,获取Instant实例 ---> Date(long millis)
Instant instant1 = instant.ofEpochMilli(l);
System.out.println(instant1);
}
/*
DateTimeFormatter:格式化或解析日期,时间
类似于SimpleDateFormat
*/
@Test
public void test4(){
//方式一:预定义标准格式
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
//格式化 日期 ----> 字符串
LocalDateTime now = LocalDateTime.now();
String format = formatter.format(now);
System.out.println(now);
System.out.println(format);
//解析 字符串 -->日期
TemporalAccessor parse = formatter.parse("2022-05-08T15:49:21.727");
System.out.println(parse);
//方式二:本地化相关的格式
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String format1 = formatter1.format(now);
String format2 = formatter2.format(now);
System.out.println(format1);
System.out.println(format2);
//重点:方式三:自定义的格式
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
String s = formatter3.format(now);
System.out.println(s);//2022-05-08 03:57:58
//解析
TemporalAccessor parse1 = formatter3.parse("2022-05-08 03:57:58");
System.out.println(parse1);
}
}
JAVA 比较器
使用两个接口中的任何一个 Comparable 或 Comparator
Comparable 接口的方式一旦指定,保证Comparable 接口实现类的对象在任何位置都可以比较大小
Comparator 接口属于临时性的比较
/*java中的对象,正常情况下只能进行比较操作 == 或 != ;不能使用 > 或 < 的
* 但是在开发过程中.我们需要多个对象进行排序,言外之意,需要比较对象的大小
*如何实现?
* 使用两个接口中的任何一个 Comparable 或 Comparator
*/
public class CompareTest {
/*
Comparable使用举例 (自然排序)
1.像String,包装类等实现了Comparable接口,重写了compareTO()方法,给出了比较两个对象大小
2.像String,包装类重写compareTo()方法后,进行了从小到大的排列
3.重写compareTo(obj)的规则
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数
如果当前对象this等于形参对象obj,则返零
4.对于自定义类来说,如果需要排序,我们可以让自定义类实现comparable接口,重写compareTo(obj)
在compareTo(obj)中指明如何排序 自然排序
*/
@Test
public void test1(){
String[] arr = new String[]{"AA","CC","KK","GG","JJ","DD"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
/*
定制排序: java.util.Comparator
Comparator 接口的使用: 定制排序
1.重写:compare(Object o1,Object o2)方法,比较o1和o2的大小,
如果返回正整数,则o1大于o2
如果返回负整数,则o1小于o2
如果返零,则相等
*/
@Test
public void test3(){
String[] arr = new String[]{"AA","CC","KK","GG","JJ","DD"};
Arrays.sort(arr, new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof String && o2 instanceof String){
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
//return 0;
throw new RuntimeException("输入的数据类型不一致");
}
} );
System.out.println(Arrays.toString(arr));
}
}
System类
系统类
成员方法:native long currentTimeMillis();返回当前计算机的时间
void exit(int status) 退出程序,status的值为0表示正常退出,非零代表异常退出
void gc() 请求系统进行垃圾回收
Math类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o2b90vg9-1652776738297)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\Math类.png)]
BigInteger 与 BigDecimal
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TbFejoy-1652776738298)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\BigInteger.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1EzR6CH-1652776738301)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\BigInteger常用方法.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yrlTbxX6-1652776738302)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\BigDecimal.png)]
枚举类&注解
枚举类的使用
如何自定义枚举类
* 一、枚举类的使用
* 1.枚举类的理解: 类的对象只有有限个,确定个。我们称此类为枚举类
* 2.当需要定义一组常量时,强烈建议使用枚举类
* 3.如果枚举类中只有一个对象,则可以作为单例模式的实现方式
* 二、如何定义枚举类
* 方式一:JDK5.0之前,自定义枚举类
* 方式二:JDK5.0之时,可以使用Enum关键字定义枚举类 SeasonTest1.class
* @Date: 2022/5/9 8:43
* @Version: 1.8.0_131
*/
public class SeasonTest {
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
}
}
//自定定义一个枚举类
class Season{
//1.声明Season对象的属性 private final 修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Season(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个方法 public static final 修饰
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "夏日绵绵");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "冰天雪地");
//4.其他诉求:1.获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//4.其他诉求:2.提供toString()方法
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
如何使用关键字enum定义枚举类
* 使用Enum关键字自定义枚举类
* 说明:定义的枚举类默认继承于 java.lang.Enum类
* @Date: 2022/5/9 9:05
* @Version: 1.8.0_131
*/
public class SeasonTest1 {
public static void main(String[] args) {
Season1 autumn = Season1.AUTUMN;
System.out.println(autumn);
System.out.println(Season1.class.getSuperclass());//class java.lang.Enum
}
}
//使用enum关键字定义枚举类
enum Season1 {
//1.提供当前枚举类的多个对象,对象之间使用逗号","隔开,最后一个使用;结束
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日绵绵"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "冰天雪地");
//2.声明Season对象的属性 private final 修饰
String seasonName;
String seasonDesc;
//3.私有化类的构造器,并给对象属性赋值
private Season1(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//4.其他诉求:1.获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
//4.其他诉求:2.提供toString()方法
// @Override
// public String toString() {
// return "Season{" +
// "seasonName='" + seasonName + '\'' +
// ", seasonDesc='" + seasonDesc + '\'' +
// '}';
// }
}
Enum类的主要方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcPvU1oJ-1652776738303)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\Enum类主要方法.png)]
//toString() 返回枚举类对象的名称
System.out.println(autumn);
//System.out.println(Season1.class.getSuperclass());//class java.lang.Enum
System.out.println("=============================");
//values() : 查看枚举类的状态,返回数组类型
Season1[] values = Season1.values();
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]);
}
System.out.println("=============================");
Thread.State[] values1 = Thread.State.values();
for (int i = 0; i < values1.length; i++) {
System.out.println(values1[i]);
}
System.out.println("=============================");
//valueOf(String objName) : 根据提供的objName,返回枚举类中对象名是objName的对象
//如果没有objName的枚举类对象则抛异常
Season1 winter = Season1.valueOf("WINTER");
System.out.println(winter);
使用enum关键字定义的枚举类实现接口的情况
情况一: 实现接口,在enum类中实现抽象方法
情况二: 让枚举类的对象分别实现接口中的抽象方法,使每个枚举类的抽象方法不一样
interface Info {
void show();
}
enum Season1 implements Info {
//1.提供当前枚举类的多个对象,对象之间使用逗号","隔开,最后一个使用;结束
SPRING("春天", "春暖花开"){
@Override
public void show() {
System.out.println("春天在哪里");
}
},
SUMMER("夏天", "夏日绵绵"){
@Override
public void show() {
System.out.println("宁静的夏天");
}
},
AUTUMN("秋天", "秋高气爽"){
@Override
public void show() {
System.out.println("收割的季节");
}
},
WINTER("冬天", "冰天雪地"){
@Override
public void show() {
System.out.println("大约在冬季");
}
};
注解的使用
Annotation 代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理
Annotation 可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量,
/* Annotation理解:
* 1.JDK5.0新增的功能
* 2.Annotation 代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理
* 程序员可以在不改变原有逻辑的情况下,在源代码中嵌入一些补充信息
* 3.在JAVAEE/Android中占用了更重要的角色
* <p>
* 二、使用实例
* 示例一:生成文档相关的注解
* 示例二:在编译时进行格式检查(JDK内置的三个基本注解)
* 1.@override:限定重写父类的方法,该注解只能用余方法
* 2.@Deprecated:用于表示所修饰的元素已过时。通常是因为所修饰的结构危险或存在更好的选择
* 3.@SuppressWarnings:抑制编译器警告
* 示例三: 跟踪代码的依赖性,实现替代配置文件功能
*/
如何自定义注解? 参照@SuppressWarnings(),
注解用@interface修饰
内部定义成员,通常使用value表示
可以指定成员的默认值,使用default定义
如果自定义注解成员没有成员,表明是一个标识作用
说明:如果注解有成员,在使用注解时,需要指明成员的值
自定义注解必须配上注解的信息处理流程(使用放射)才有意义
JDK提供的4种元注解
JDK提供的4种元注解(对现有注解进行解释说明)
* Retention 指定该Annotation的生命周期: SOURCE\CLASS(默认行为) \ RUNTIME
* 只有声明为RUNTIME生命周期的注解,才能通过反射获取
* Target 用于指定被修饰的Annotation 能用于修饰哪些程序元素
* Documented 表示所修饰的注解在被Javadoc解析时,保留下来
* Inherited 被他修饰的Annotation 将具有继承性
Java集合
集合框架的概述:
1.集合,数组都是对多个数据进行存储操作的结构,简称Java容器
说明:此时的存储,主要是内存层面的存储,不涉及到持久化的存储
2.1数组在存储多个数据方面的特点:
> 一旦初始化以后,其长度就确定了
> 一旦定义好后,其元素的类型也就确定了。我们就只能操作指定类型的数据。
比如:String[] arr; int[ ] arr1; Object[ ] arr2;
2.2数组在存储多个数据方面的缺点:
> 一旦初始化后,其长度就不可修改。
> 数组中提供的方法非常有限,对于添加,删除,插入数组等操作,非常不便
> 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
> 数组存储数据的特点:有序,可重复,对于无序,不可重复的需求,不能满足
Collection接口方法
单例集合框架,用来存储一个一个的对象
add(Object obj),
addAll(Collection coll),
size(),
isEmpty(),
clear();
contains(Object obj),
containsAll(Collection coll),
remove(Object obj),
removeAll(Collection coll),
retainsAll(Collection coll),
equals(Object obj);
hasCode(),
toArray(),
iterator();
Itertor迭代器接口
元素遍历操作,使用迭代器Iterator接口
内部方法:hasNext() 和 next()
* 内部方法:hasNext() 和 next()
* 内部定义了一个remove方法,可以在遍历的时候,删除集合中的元素
* @Date: 2022/5/10 8:43
* @Version: 1.8.0_131
*/
public class IteratorTest {
@Test
public void test1() {
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("TOM"));
collection.add(new Person("Jerry", 20));
collection.add(false);
Iterator iterator = collection.iterator();
// //方式一:
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// //方式二: 不推荐
// for (int i = 0; i < collection.size(); i++) {
// System.out.println(iterator.next());
// }
//方式三 :推荐
//hasNext() 判断是否还有写一个元素
while (iterator.hasNext()) {
//next():指针下移,将下移以后集合位置上的元素返回
Object obj = iterator.next();
//删除集合中TOM
if ("TOM".equals(obj)){
iterator.remove();
}
}
System.out.println("=============");
//重写遍历集合
Iterator iterator1 = collection.iterator();
while (iterator1.hasNext()) {
//next():指针下移,将下移以后集合位置上的元素返回
System.out.println( iterator1.next());
}
}
//JDK5.0新增了foreach循环,用于遍历集合,数组
@Test
public void test3() {
// //遍历数组
// int[] arr = new int[]{10,20,30,4,50,21,65};
//for(数组元素类型 局部变量 : 数组对象)
// for (int i:arr) {
// System.out.println(i);
// }
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("TOM"));
collection.add(new Person("Jerry", 20));
collection.add(false);
//遍历集合,for(集合元素的类型 局部变量 :集合对象)
//内部仍然调用了迭代器
for (Object obj: collection
) {
System.out.println(obj);
}
}
}
Collection子接口一:List
存储有序的,可重复的数据 —> "动态"数组,替换原有的数组
ArrayList :作为List接口的主要实现类,线程不安全,效率高 ,底层使用Object[]
LinkedList :对于频繁的插入,删除操作,使用此类效率比ArrayList高: 底层使用双向链表存储
Vector :作为List接口的古老实现类,线程安全的,效率低, 底层使用Object[]
总结:
增:add(Abject obj)
删:remove(int index) / remove(Object obj)
改:set(int index , Object obj)
查:get(int index )
插:add(int index , Object obj)
长度:size()
遍历:1.Iterator 迭代器方式
2. for- each 增强for循环
Collection子接口二:Set
存储无序的,不可重复的数据 ----> “集合”
HashSet , LinkedHashSet , TreeSet
/* @description: set接口 无序的,不可重复的
* HashSet :set接口的主要实现类,线程不安全的,可以存储null值
* LinkedHashSet :作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
* TreeSet : 可以按照添加的对象的指定属性,进行排序.
* <p>
* 1.Set接口中没有额外定义新的方法,使用的都是Collection中声明的方法
* 要求:向set中添加的数据,其所在的类一定要重写hashCode() 和 equals()
* 重写的hasCode() 和 equals()尽可能保持一直:相等的对象必须具有相等的散列码
* 重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值.
* @Date: 2022/5/10 14:59
* @Version: 1.8.0_131
*/
public class SetTest {
/*
Set :存储无序不可重复的数据
以HashSet为例说明:
1.无序性 :不等于随机性.存储的数据在底层数组中并非按照数组索引的顺序添加.而是根据数据的哈希值决定的
2.不可重复性 : 保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个
添加元素的过程
以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在类的HashCode()方法,计算元素a的哈希值
此哈希值接着通过某种算法计算出在HashSet底层数组中存放的位置(即为:索引位置),判断数组
此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功 --->情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不同,则添加成功 ----> 情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法;
equals()返回true,元素a添加失败
equals()返回false,元素a添加成功 ---->情况3
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储.
jdk 7 :元素a放到数组中,指向原来的元素
jdk 8:原来的元素在数组中,指向元素a
*/
@Test
public void test() {
Set set = new HashSet();
set.add(456);
set.add(123);
set.add(123);
set.add("AA");
set.add("cc");
set.add(new Person("Tom", 12));
set.add(new Person("Tom", 12));
set.add(1289);
//使用迭代器遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
//LinkedHashSet的使用
//LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录数据前一个
//数据和后一个数据,
//优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
@Test
public void test1() {
Set set = new LinkedHashSet();
set.add(456);
set.add(123);
set.add(123);
set.add("AA");
set.add("cc");
set.add(new Person("Tom", 12));
set.add(new Person("Tom", 12));
set.add(1289);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
//TreeSet的使用
/*
可以按照对象的指定对象排序,
1.向TreeSet中添加的数据,要求是相同类的对象,
2.两种排序方式,自然排序(实现Comparable接口),和 定制排序(Comparator)
3.自然排序中比较两个对象是否相同的标注为:compareTO()返回0,不再是equals()
4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()
*/
@Test
public void test2() {
TreeSet set = new TreeSet();
//不能添加不同类的对象
// set.add(456);
// set.add(123);
// set.add("AA");
// set.add(new Person("Tom",12));
// //举例一
// set.add(34);
// set.add(-34);
// set.add(56);
// Iterator iterator = set.iterator();
// while (iterator.hasNext()) {
// System.out.println(iterator.next());
// }
//举例二
set.add(new Person("Tom",12));
set.add(new Person("Marry",18));
set.add(new Person("Jim",10));
set.add(new Person("Mark",20));
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
@Test
public void test3(){
Comparator comparator = new Comparator() {
//按照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o2 instanceof Person){
Person p1 = (Person) o1;
Person p2 = (Person) o2;
return Integer.compare(p1.getAge(), p2.getAge());
}else {
throw new RuntimeException("输入的类型数据不匹配");
}
}
};
TreeSet set = new TreeSet(comparator);
set.add(new Person("Tom",12));
set.add(new Person("Marry",18));
set.add(new Person("Jim",10));
set.add(new Person("Mark",20));
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
Map接口
双列集合,用来存储一对(key - value)一对的数据(键值对) ----> 函数f(x) = y
HashMap , LinkedHashMap , TreeMap , Hashtable , Properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mR7c4HXc-1652776738303)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\Map接口常用方法.png)]
/* @description:
* 一.Map实现类的结构
* Map:双列数据,存储key - value 对象得数据 类似于高中的函数y = f(x)
* HashMap: 作为Map得主要实现类,线程不安全得,效率高,存储null的key和value
* LinkedHashMap:保证遍历mao元素时可以按照添加的顺序实现遍历
* 原因: 在原有的HashMap底层结构上,添加了一对指针,指向前一个和后一个元素
* 对于频繁的遍历操作,此类执行效率高于HashNap
* TreeMap:保证按照添加的key-value对进行排序,实现排序遍历.此时考虑key的自然排序或定制排序
* Hashtable: Map得古老实现类,线程安全,效率低,不能存储null的key和value
* Properties:常用来处理配置文件,key和value都是String类型
* HashMap的底层:数组+链表(Jdk7及之前)
* 数组+链表+红黑树(jdk8)
* 面试题:
* HashMap的底层原理?
* HashMap和Hashtable的异同?
* CurrentHashMap 与 HashTable的区别?
*二.Map结构的理解
* Map中的key :无序的,不可重复的,使用Set存储所有的key ---> key所在的类重写equals() 和 hashCode() (以hashMap为例)
* Map中的value:无序的,可重复的,使用Collection存储所有的value---->value所在的类重写equals()
* 一个键值对:key - value 构成了一个Entry对象
* Map中的Entry:无序的,不可重复的,使用Set存储所有的Entry
*三. HashMap的底层原理?以jdk7为例
* hashMap map = new HashMap();
* 在实例化以后,底层创建了长度是16的一维数组Entry[] table.
* map.put(key1,value1);//添加数据
* 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
* 如果此位置上的数据为空,此时的key1-value1添加成功 --->情况一
* 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表的形式存在)),比较key1和已经存在的一个或多个数据
* 的哈希值:
* 如果key1的哈希值与已经存在的哈希值都不相同,此时key1 - value1 添加成功 --->情况二
* 如果key1的哈希值与已经存在的某一个数据(key2 - value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法
* 比较: 如果equals()返回false,此时key1 - value1 添加成功 ---->情况三
* 如果equals()返回true:使用value1替换相同key的value2.
* 补充:关于情况二和情况三
* 此时key1 - value1 和原来的数据以链表的方式存储.
*
* 在不断地添加过程中会涉及到扩容问题,默认的扩容方式,扩容为原来的2倍,并将原来的数据复制过来.
*
* jdk8相比较jdk7在底层实现方面不同:
* 1.new HashMap() :底层没有创建以一个长度为16的数组
* 2.jdk8底层的数组时:Node[] ,而非Entry[]
* 3.首次调用put()方法时,底层创建长度为16的数组
* 4.jdk7 底层结构只有数组+链表. 数组+链表+红黑树(jdk8)
* 当数组的某个索引位置上的元素以链表形式存在的数据个数 > 8 且当期数组的长度 > 64 时,
* 此时索引位置上的所有数据改为用红黑树存储
* HashMap底层属性的属性说明:
* DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
* DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
* threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
* TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
* MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
*四.LinkedHashMap的底层实现原理(了解)
*
*
* 五.Map中定义的方法
*
* @Date: 2022/5/11 9:14
* @Version: 1.8.0_131
*/
public class MapTest {
/*
元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values(): 返回所有value构成的Collection集合
Set entrySet():返回所有key-value 对构成的Set集合
*/
@Test
public void test5(){
Map map = new HashMap();
map.put("AA", 123);//添加
map.put(45, 123);
map.put("BB", 56);
//遍历所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value集:values()
Collection values = map.values();
for (Object obj:values
) {
System.out.println(obj);
}
//遍历所有的key-value
//entrySet()
Set set1 = map.entrySet();
Iterator iterator1 = set1.iterator();
while (iterator1.hasNext()){
Object next = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)next;
System.out.println(entry.getKey()+"---->"+entry.getValue());
}
}
@Test
public void test4(){
Map map = new HashMap();
map.put("AA", 123);//添加
map.put(45, 123);
map.put("BB", 56);
System.out.println(map.get(45));//获取指定kay的value
boolean isExist = map.containsKey("00");//是否包含指定的value
System.out.println(isExist);//false
}
//Map中定义的方法
@Test
public void test3(){
Map map = new HashMap();
map.put("AA", 123);//添加
map.put(45, 123);
map.put("BB", 56);
map.put("AA", 87);//修改
System.out.println(map);
Map map1 = new HashMap();
map1.put("CC", 123);
map1.put("DD", 123);
map.putAll(map1);
System.out.println(map);
//remove(Object key)
Object cc = map.remove("CC");
System.out.println(cc);
System.out.println(map);
boolean empty = map.isEmpty();//返回是否为空
System.out.println(empty);
//clear()
map.clear();
System.out.println(map.size());//0 获取map中key - value的对数
}
@Test
public void test2(){
// Map map = new HashMap();
Map map = new LinkedHashMap();
map.put(123, "AA");
map.put(456, "BB");
map.put(12, "CC");
System.out.println(map);
}
@Test
public void test(){
Map map = new HashMap();
// Map map1 = new Hashtable();不能存储null的key和value
map.put(null,null);
}
}
Collections工具类
操作Collection 和Mapd的工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4AbjW3WQ-1652776738304)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\Collections工具类常用方法.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5VD7Kn7x-1652776738305)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\Collections工具类常用方法2.png)]
/*
常用方法:
*/
@Test
public void test(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add(78);
list.add(3);
list.add(563);
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
Collections.shuffle(list);
System.out.println(list);
}
泛型
为什么要有泛型
泛型:标签 把元素的类型设计成一个参数,这个类型参数叫做泛型.Collection,List,ArrayList 这个就是类型参数,即泛型.
所谓泛型,就是允许在定义类,接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型.这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量,创建对象时) 确定(即传入实际的类型参数,也称为类型实参)
在集合中使用泛型
/* 2.在集合中使用泛型
* 总结:
* 1.集合接口或集合类在jdk5.0时都修改为带泛型的结构
* 2.在实例化集合类时可以指明具体的泛型类型
* 3.指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(方法,构造器,属性)使用到类的泛型的位置,
* 都指定为实例化的泛型类型.
* 比如:add(E e) ----->实例化后add(Integer e)
* 4.注意点:泛型的类型必须是类,不能是基本数据类型.需要用到基本数据类型的位置,拿包装类替换
* 5.实例化时,没有指明泛型的类型.默认类型为java.lang.Object类型.
* @Date: 2022/5/12 8:20
* @Version: 1.8.0_131
*/
public class GenericTest {
//在集合使用泛型之前的情况
@Test
public void test() {
ArrayList list = new ArrayList();
//需求:存放学生成绩
list.add(78);
list.add(56);
list.add(89);
list.add("Tom");//问题一:类型不安全
for (Object score : list
) {
int stuScore = (Integer) score;//问题二:强转时有可能出现ClassCastException
System.out.println(stuScore);
}
}
//在集合中使用泛型之前的情况:以ArrayList为例
@Test
public void test1() {
//使用泛型,参数类型不能时基本数据类型只能是包装类
ArrayList<Integer> list = new ArrayList<Integer>();//使用泛型限制添加数据的类型
list.add(78);
list.add(56);
list.add(89);
//list.add("Tom");//编译报错
//方式一:
for (Integer score : list
) {
int stuScore = score;//避免了强转操作
System.out.println(stuScore);
}
//方式二:
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
int stuScore = iterator.next();
System.out.println(stuScore);
}
}
@Test
public void test2() {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom", 23);
map.put("Marry", 18);
//map.put(123, "ABC");//编译报错
//泛型嵌套
Set<Map.Entry<String, Integer>> entries = map.entrySet();
//System.out.println(entries);
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> next = iterator.next();
String key = next.getKey();
Integer value = next.getValue();
System.out.println(key + "----->" + value);
}
}
}
自定义泛型结构
1.自定义泛型类,泛型接口
public class Order<T> {
String name;
int orderId;
//类的内部结够可以使用类的泛型
T orderT;
public Order() {
}
public Order(String name, int orderId, T orderT) {
this.name = name;
this.orderId = orderId;
this.orderT = orderT;
}
public T getOrderT() {
return orderT;
}
public void setOrderT(T orderT) {
this.orderT = orderT;
}
@Test
public void test2() {
//由于子类在继承泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型
SubOrder subOrder = new SubOrder();
subOrder.setOrderT(123);
}
}
//指明了Integer类型
public class SubOrder extends Order<Integer>{
}
public class GenericTest1 {
@Test
public void test1(){
//如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
//要求:如果大家定了类是泛型的,建议在实例化时要指明类的泛型
Order order = new Order();
order.setOrderT(123);
order.setOrderT("ABC");
//建议:实例化时指明类的泛型
Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");
order1.setOrderT("AA");
//order1.setOrderT(123);//编译报错
}
}
//静态方法不能是泛型的
//自定义异常类不能是泛型的
2.自定义泛型方法
//泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系.
//泛型方法所属的类是不是泛型类都没有关系
//泛型方法可以声明为静态的,原有:泛型参数是在调用泛型方法时确定的.并非在实例化类时确定.
public static., <E>List<E> copyFromArray(E[] arr){
ArrayList<E> list = new ArrayList<>();
for (E e:arr
) {
list.add(e);
}
return list;
}
@Test
public void test4(){
Order<String> order = new Order<>();
Integer[] arr = new Integer[]{1,2,3,4,5};
//泛型方法在调用时,指明泛型类型
List<Integer> integers = order.copyFromArray(arr);
System.out.println(integers);
}
泛型在继承上的体现
/*
泛型在继承方面的体现
虽然类A是类B的父类,G<A>和G<B>二者不具备子父类关系,或二者是并列关系
补充:类A是类B的父类(接口),A<G> 是 B<G>的父类
*/
@Test
public void test5() {
Object object = null;
String str = null;
object = str;
Object[] arr = null;
String[] arr1 = null;
arr = arr1;
List<Object> list1 = null;
List<String> list2 = null;
//list1 = list2;//编译报错,不具备子父类关系,不能赋值
}
通配符的使用
/*
通配符的使用
通配符: ?
类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
*/
@Test
public void test6() {
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
// print(list1);
// print(list2);
//
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
//添加:对于list<?>就不能向其内部田间数据
//除了添加null之外,
// list.add("DD");
list.add(null);
//获取(读取):允许读取数据,读取的数据类型为Object
Object o = list.get(0);
System.out.println(o);
}
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
}
泛型应用举例
IO流
File类的使用
文件或文件目录路径的抽象表示形式,与平台无关.
一、 如何创建File类的实现
File(String filePath)
File(String parentPath,String childPath)
File(File parentFile,String childPath)
2.相对路径:相较于某个路径下,指明的路径
绝对路径:包含盘符在内的文件或文件目录的路径
二、常用方法:
1.获取功能:
public String getAbsolutePath() : 获取绝对路径
public String getPath() : 获取路径
public String getName() : 获取名称
public String getParent() : 获取上层文件目录路径.若无返回null
public long length() : 获取文件长度(即,字节数).不能获取目录的长度
public long lastModified() : 获取最后一次的修改时间,毫秒数
public String list() : 获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() : 获取指定目录下的所有文件或文件目录的File数组
public boolean renameTo(File dest) : 把文件命名为指定的文件路径
2.判断功能
public boolean isDirectory() : 判断是否是文件目录
public boolean isFile() : 判断是否是文件
public boolean exists() : 判断是否存在
public boolean canRead() : 判断是否可读
public boolean canWrite() : 判断是否可写
public boolean isHidden() : 判断是否隐藏
三、File类的创建功能:
public boolean createNewFile(): 创建文件.若文件存在,则不创建,返回false
public boolean mkdir() 创建文件目录,如果此文件目录存在就不创建了,如果此文件的上层目录也不存在,则也不创建
public boolean mkdirs() 创建文件目录,如果上层文件目录不存在,一并创建
四、File类的删除功能:
public boolean delete() 删除文件或文件夹
IO流原理及流的分类
用于处理设备之间的数据传输。
按操作的数据单位不同分为:字节流(8 bit),字符流(16 bit)适合处理文本数据
按数据流的流向不同:分为输入流和输出流
按流的角色的不同分为:节点流,处理流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JJpGDwNG-1652776738306)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\IO流体系.png)]
/* 抽象基类 节点流(或文件流) 缓冲流(提高文件的读写效率)
* //字节流
* InputStream FileInputStream (read(byte[] buff)) BufferedInputStream(read(byte[] buff))
* OutputStream FileOutputStream(write(byte[] buff,0,len)) BufferedOutputStream(write(byte[] buff,0,len))
* //字符流
* Reader FileReader(read(char[] cbuf)) BufferedReader(read(char[] cbuf))
* Writer FileWriter(write(char[] cbuf,0,len)) BufferedWriter(write(char[] cbuf,0,len))
*/
public class FileReaderWriterTest {
/*
将hello.txt文件内容读入程序中,并输出到控制台
说明:
异常的处理,为了保证流资源一定可以执行关闭操作,需要使用try - catch -finally处理
*/
@Test
public void testFileReader() {
//1.实例化File类对象,指明需要操作的文件
File file = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");//相对于当前Module
FileReader fileReader = null;
try {
//2.提供具体的流
fileReader = new FileReader(file);
//3.数据的读入
//read()返回读入的字符,如果达到文件末尾,返回-1
//方式一
// int read = fileReader.read();
// while (read != -1){
// System.out.print((char) read);
// read = fileReader.read();
// }
//方式二
int data;
while ((data = fileReader.read()) != -1) {
System.out.println((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
//4.流的关闭操作
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//对read()操作升级,使用read()的重载方法
@Test
public void testFileReader2() {
//1.实例化File类对象,指明需要操作的文件
File file = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");//相对于当前Module
FileReader fileReader = null;
try {
//2.提供具体的流
fileReader = new FileReader(file);
//3.读入的操作
// read(char[] cbuf) 返回每次读入cbuf数组中得字符得个数.如果达到文件末尾,返回-1
char[] cbuf = new char[5];
int len;
while ((len = fileReader.read(cbuf)) != -1) {
//错误的写法
// for (int i = 0; i < cbuf.length; i++) {
// System.out.println(cbuf[i]);
// }
//正确的写法
for (int i = 0; i < len; i++) {
System.out.print(cbuf[i]);
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
//4.资源的关闭
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
从内存中写出数据到硬盘的容量
说明:
1.输出操作中,对应的File可以不存在的,并不会报异常
当文件不存在时,在输出的过程中,新建文件
文件本身存在时,
如果流使用的构造器是:FileWriter(file,false); / FileWriter(file),对原文件覆盖
FileWriter(file,true); 在原有文件基础上追加
*/
@Test
public void testFileWriter() throws IOException {
//1.提供File类的对象,指明写到的文件
File file = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
FileWriter fileWriter = new FileWriter(file, false);//覆盖
//FileWriter fileWriter = new FileWriter(file,true);//添加
//3.写出的操作
fileWriter.write("I have a Dream\n");
fileWriter.write("you need to have a Dream\n");
//4.流资源的关闭
fileWriter.close();
}
@Test
public void test4() {
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
//1.创建File类对象:指明读入或写出的文件
File srcFile = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");
File destFile = new File("hello2.txt");
//2.创建输入流和输出流的对象
fileReader = new FileReader(srcFile);
fileWriter = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//每次读入到cbuf数组中的字符的个数
while ((len = fileReader.read(cbuf)) != -1) {
//每次写出len个字符
fileWriter.write(cbuf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
节点流(或文件流)
package com.senior.day09;
import org.testng.annotations.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @Author: 小郑
* @description: 测试InputStream 和OutputStream的使用
* <p>
* 结论:
* 对于文本文件用字符流处理
* 对于字节流文件使用字节流处理
* @Date: 2022/5/13 15:30
* @Version: 1.8.0_131
*/
public class FileInputOutputStreamTEst {
@Test
public void testFileInputStream() {
FileInputStream fileInputStream = null;
try {
//1.实例化File类对象,指明需要操作的文件
File file = new File("G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\123.jpg");
//2.提供具体的流
fileInputStream = new FileInputStream(file);
//3.读数据
byte[] bytes = new byte[5];
int len;//记录每次读取的字节的个数
while ((len = fileInputStream.read(bytes)) != -1) {
String s = new String(bytes, 0, len);
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testOutputStream() {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
File file = new File("G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\123.jpg");
File destFile = new File("G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\1234.jpg");
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(destFile);
//复制的过程
byte[] buffer = new byte[5];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (fileInputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//指定路径下的文件复制
public void copyFile(String srcPath, String destPath) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
File file = new File(srcPath);
File destFile = new File(destPath);
fileInputStream = new FileInputStream(file);
fileOutputStream = new FileOutputStream(destFile);
//复制的过程
byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (fileInputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testCopyFile() {
long start = System.currentTimeMillis();
String srcPath = "G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\123.jpg";
String destPath = "G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\1234.jpg";
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("复制操作花费的时间为:"+(end - start));
}
}
缓冲流
package com.senior.day09;
import org.testng.annotations.Test;
import java.io.*;
/**
* @Author: 小郑
* @description: 一.流的分类
* 1.操作数据的单位:字节流,字符流
* 2.数据的流向:输入流,输出流
* 3.流的角色:节点流,处理流
* 二.流的体系结构
* 抽象基类 节点流(或文件流) 缓冲流(提高文件的读写效率)
* //字节流
* InputStream FileInputStream (read(byte[] buff)) BufferedInputStream(read(byte[] buff))
* OutputStream FileOutputStream(write(byte[] buff,0,len)) BufferedOutputStream(write(byte[] buff,0,len))
* //字符流
* Reader FileReader(read(char[] cbuf)) BufferedReader(read(char[] cbuf))
* Writer FileWriter(write(char[] cbuf,0,len)) BufferedWriter(write(char[] cbuf,0,len))
*
* @Date: 2022/5/13 9:07
* @Version: 1.8.0_131
*/
public class FileReaderWriterTest {
/*
将hello.txt文件内容读入程序中,并输出到控制台
说明:
异常的处理,为了保证流资源一定可以执行关闭操作,需要使用try - catch -finally处理
*/
@Test
public void testFileReader() {
//1.实例化File类对象,指明需要操作的文件
File file = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");//相对于当前Module
FileReader fileReader = null;
try {
//2.提供具体的流
fileReader = new FileReader(file);
//3.数据的读入
//read()返回读入的字符,如果达到文件末尾,返回-1
//方式一
// int read = fileReader.read();
// while (read != -1){
// System.out.print((char) read);
// read = fileReader.read();
// }
//方式二
int data;
while ((data = fileReader.read()) != -1) {
System.out.println((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
//4.流的关闭操作
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//对read()操作升级,使用read()的重载方法
@Test
public void testFileReader2() {
//1.实例化File类对象,指明需要操作的文件
File file = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");//相对于当前Module
FileReader fileReader = null;
try {
//2.提供具体的流
fileReader = new FileReader(file);
//3.读入的操作
// read(char[] cbuf) 返回每次读入cbuf数组中得字符得个数.如果达到文件末尾,返回-1
char[] cbuf = new char[5];
int len;
while ((len = fileReader.read(cbuf)) != -1) {
//错误的写法
// for (int i = 0; i < cbuf.length; i++) {
// System.out.println(cbuf[i]);
// }
//正确的写法
for (int i = 0; i < len; i++) {
System.out.print(cbuf[i]);
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
//4.资源的关闭
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
从内存中写出数据到硬盘的容量
说明:
1.输出操作中,对应的File可以不存在的,并不会报异常
当文件不存在时,在输出的过程中,新建文件
文件本身存在时,
如果流使用的构造器是:FileWriter(file,false); / FileWriter(file),对原文件覆盖
FileWriter(file,true); 在原有文件基础上追加
*/
@Test
public void testFileWriter() throws IOException {
//1.提供File类的对象,指明写到的文件
File file = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
FileWriter fileWriter = new FileWriter(file, false);//覆盖
//FileWriter fileWriter = new FileWriter(file,true);//添加
//3.写出的操作
fileWriter.write("I have a Dream\n");
fileWriter.write("you need to have a Dream\n");
//4.流资源的关闭
fileWriter.close();
}
@Test
public void test4() {
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
//1.创建File类对象:指明读入或写出的文件
File srcFile = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");
File destFile = new File("hello2.txt");
//2.创建输入流和输出流的对象
fileReader = new FileReader(srcFile);
fileWriter = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//每次读入到cbuf数组中的字符的个数
while ((len = fileReader.read(cbuf)) != -1) {
//每次写出len个字符
fileWriter.write(cbuf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
转换流
转换流提供了字节流和字符流之间的转换
InputStreamReader :将InputStream 转换为 Reader
OutputStreamWriter ; 将Wirter装换为 OutputStream
字节流中的数字都是字符是,转成字符流操作更高效
package com.senior.day09;
import org.testng.annotations.Test;
import java.io.*;
/**
* @Author: 小郑
* @description: 处理流之二:转换流的使用
* InputStreamReader : 将一个字节的输入流转为字符的输入流
* OutputStreamWriter : 将字符的输出流转换为字节的输出流
* <p>
* 作用: 提供字节流与字符流之间的转换
* <p>
* 3.解码:字节,字节数组 ---> 字符数组,字符串 InputStreamReader
* 编码:字符数组,字符串 ---> 字节,字节数组 OutputStreamWriter
* <p>
* 4.字符集
* @Date: 2022/5/13 17:04
* @Version: 1.8.0_131
*/
public class InputStreamReaderTest {
/*
InputStreamReader 的使用,实现字节的输入流到字符的输入流的转换
*/
@Test
public void test() {
InputStreamReader inputStreamReader = null;
try {
FileInputStream fis = new FileInputStream(new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt"));
inputStreamReader = new InputStreamReader(fis);//使用系统默认的字符集
//指明了参数类型的字符集,具体使用什么字符集,取决于文件保存时使用的字符集
//inputStreamReader = new InputStreamReader(fis,"UTF-8");
char[] cbuf = new char[1024];
int len;
while ((len = inputStreamReader.read(cbuf)) != -1){
String str = new String(cbuf,0,len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStreamReader != null){
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
综合使用
InputStreamReader 和 OutputStreamWriter
*/
@Test
public void test1(){
InputStreamReader inputStreamReader = null;
OutputStreamWriter outputStreamWriter = null;
try {
File file1 = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt");
File file2 = new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello_gbk.txt");
FileInputStream fileInputStream = new FileInputStream(file1);
FileOutputStream fileOutputStream = new FileOutputStream(file2);
inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");
outputStreamWriter = new OutputStreamWriter(fileOutputStream,"gbk");
char[] cbuf = new char[20];
int len;
while ((len = inputStreamReader.read(cbuf)) != -1){
outputStreamWriter.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStreamReader != null){
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (outputStreamWriter != null)
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
标准输入,输出流
System.in 和System.out 分别代表了系统标准的输入和输出设备
默认输入设备是:键盘,输出设备是:显示器
System.in的类型是InputStream
System.out的类型是PrintStream,其是OutoutStream的子类
FilterOutputStream的子类
重定向:通过System类的seltln,setOut方法对默认设备进行更改
public static void setln(InputStream in)
public static void setout(PrintStream out)
import org.testng.annotations.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Locale;
/**
* @Author: 小郑
* @description: 其他流的使用
* 1.标准的输入,输出流
* 2.打印流
* 3.数据流
* @Date: 2022/5/13 17:53
* @Version: 1.8.0_131
*/
public class OtherStreamTest {
/*
1.标准的输入,输出流
1.1
System.in 默认的从键盘输入
System.out 默认的从控制台输出
1.2
System类的setIn(InputStream is) / setOut(PrintStream ps) 方式重新指定输入和输出的流
1.3练习
从键盘输入字符串,要求将读取到的整行字符串转成大写输出.然后继续进行输入操作
直至输入"e" 或者 "exit"时,退出程序.
方法一: 使用Scanner实现 调用next()返回一个字符串
方法二: 使用System.in实现 System.in ----> 转换流--> BufferedReader 的readLine()
*/
public static void main(String[] args) {
BufferedReader bufferedReader = null;
try {
System.out.println("请输入字符串");
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
bufferedReader = new BufferedReader(inputStreamReader);
while (true){
String data = bufferedReader.readLine();
if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)){
System.out.println("程序结束");
break;
}
String s = data.toUpperCase();
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
打印流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTr7vWH8-1652776738307)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\打印流.png)]
数据流’’
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8iWNhpY-1652776738307)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\数据流.png)]
对象流
对象的序列化机制,允许把内存中的java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的Java对象
package com.senior.day10;
import org.testng.annotations.Test;
import java.io.*;
/**
* @Author: 小郑
* @description:
* 对象流的使用:
* 1.ObjectInputStream 和 ObjectOutputStream
* 2.作用:用于存储和读取基本数据类型数据或对象的处理流
* ObjectOutputStream 内存中的对象 ---> 存储中的文件,通过网络传输出去
* ObjectInputStream 存储中的文件,通过网络接收过来 ---> 内存中的对象
* @Date: 2022/5/14 8:46
* @Version: 1.8.0_131
*/
public class ObjectInputOutputStreamTest {
/*
序列化过程:
将内存中的Java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
*/
@Test
public void test(){
ObjectOutputStream outputStream = null;
try {
//1.造流造对象
outputStream = new ObjectOutputStream(new FileOutputStream("object.bat"));//参数文件名
//2.写入数据
outputStream.writeObject(new String("我爱北京天安门"));//写入内容
outputStream.flush();//刷新操作
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(outputStream != null){
//3.关闭资源
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
反序列化过程 :将磁盘文件中的对象还原为内存中的一个Java对象
使用ObjectInputStream实现
*/
@Test
public void testObjectInputStream(){
ObjectInputStream o= null;
try {
o = new ObjectInputStream(new FileInputStream("object.bat"));//参数文件名
Object object = o.readObject();
String str = (String)object;
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (o != null){
o.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
自定义对象流:
要求:
1. 实现接口:Serializable
2. 需要当前类提供一个全局常量 : long serialVersionUID = 46846384368L
3. 除了当前类需要实现Serializable接口之外,还必须保证其内部所有属性也必须式可序列化的。
(默认情况下,基本数据类型可序列化)
补充:
ObjectInputStream 和 ObjectOutputStream 不能序列化static 和 transient修饰的成员变量
随机存储文件流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IH7jsVek-1652776738308)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\随机存储文件流.png)]
package com.senior.day10;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* @Author: 小郑
* @description: RandomAccessFile的使用:
* 1.直接继承于java.lang.Object类,实现了DataInput 和 DataOutput接口
* 2.既可以作为一个输入流,也可以作为一个输出流
* 3.如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建
* 如果写出到的文件存在,则会对原有内容进行覆盖,(默认从头开始覆盖)
* 4.可通过相关的操作,实现RandomAccessFile"插入" 数据的效果
* @Date: 2022/5/14 9:25
* @Version: 1.8.0_131
*/
public class RandomAccessFileTest {
/*
既可以作为一个输入流,也可以作为一个输出流
复制操作
*/
@Test
public void test() {
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
//1.创建流的对象
raf1 = new RandomAccessFile(new File("G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\123.jpg"), "r");// r 只读方式打开
raf2 = new RandomAccessFile(new File("G:\\Sguigu_study_project\\basis\\java" +
"\\JAVASE\\src\\com\\senior\\day09\\12323.jpg"), "rw");//rw 打开可读可写
//2.读写过程
byte[] bytes = new byte[1024];
int len;
while ((len = raf1.read(bytes)) != -1) {
raf2.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//3.关闭流
if (raf1 != null) {
raf1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (raf2 != null) {
raf2.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
3.如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建
* 如果写出到的文件存在,则会对原有内容进行覆盖,(默认从头开始覆盖)
*/
@Test
public void test1() {
RandomAccessFile raf1 = null;
try {
//1.创建流的对象
raf1 = new RandomAccessFile("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt", "rw");
//2.操作过程
raf1.write("Hel".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (raf1 != null) {
raf1.close();//3.关闭流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
使用RandomAccessFile实现插入效果
*/
@Test
public void test2() {
RandomAccessFile raf1 = null;
try {
raf1 = new RandomAccessFile("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt", "rw");
raf1.seek(3);//指针调到角标为3的位置
//保存指针3后面的所有数据到StringBuilder
StringBuilder stringBuilder = new StringBuilder((int) new File("G:\\Sguigu_study_project\\basis" +
"\\java\\JAVASE\\src\\com\\senior\\day09\\hello.txt").length());
byte[] bytes = new byte[1024];
int len;
while ((len = raf1.read(bytes)) != -1) {
stringBuilder.append(new String(bytes, 0, len));
}
raf1.seek(3);//调回指针,写入“xyz”
raf1.write("xyz".getBytes());
//将StringBuilder 中的数据写入到文件中
raf1.write(stringBuilder.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (raf1 != null) {
raf1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
NIO.2中Path,Paths,Files类的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fY1KK1sD-1652776738309)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\NIO.2.png)]
网络编程
网络编程的概述
网络编程的目的:
直接或间接地通过网络协议与其他计算机实现数据交换,进行通讯
网络编程中的两个主要问题:
如何准确定位网络上一台或多台主机;定位主机上的特定应用
找到主机后如何可靠高效地进行数据传输
网络通信要素概述
通信要素1:IP和端口号
package com.senior.day11;
import org.testng.annotations.Test;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @Author: 小郑
* @description:
* 网络编程的两个要素:
* 对应的问题一:IP 和端口号
* 对应得问题二:提供网络通信协议:TCP/IP 参考模型(应用层,传输层,网络层,物理+数据链路层)
*
* 通信要素一:IP和端口号
* 在java中使用InetAddress类代表IP
* IP分类:IPV4 IPV6 ;万维网和局域网
* 域名:
* 本地回路地址:127.0.0.1 对应着localhost
* 两个实例化InetAddress方法:getByName(String host),getLocalHost()
* 两个常用方法:getHostName() 和 getHostAddress()
* 端口号:正在计算机上运行的进程
* 要求:不同的进程有不同的端口好
* 范围:0-65535
* 端口号与IP地址的组合得出一个网络套接字:Socket
*
* 通信要素二:网络通信协议(TCP/IP协议簇)
* 传输控制协议:TCP
* 用户数据报协议:UDP
* @Date: 2022/5/14 14:46
* @Version: 1.8.0_131
*/
public class InetAddressTest {
public static void main(String[] args) {
try {
// InetAddress name = InetAddress.getByName("192.168.0.1");
// System.out.println(name);
InetAddress byName = InetAddress.getByName("www.baidu.com");
System.out.println(byName);
// InetAddress byName2 = InetAddress.getByName("127.0.0.1");
// System.out.println(byName);
//获取本地IP地址
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
//getHostName()
String hostName = localHost.getHostName();
System.out.println(hostName);
//getHostAddress()
byte[] address = localHost.getAddress();
System.out.println(address);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
通信要素2:网络协议
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ALrnIFUA-1652776738309)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\通信要素2.网络通信协议.png)]
TCP网络编程
package com.senior.day11;
import org.testng.annotations.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Author: 小郑
* @description:
* 实现TCP的网络编程 可靠的数据传输(三次握手)
* 例子1:客户端发信息给服务端,服务端将信息显示在控制台上
*
* @Date: 2022/5/14 15:26
* @Version: 1.8.0_131
*/
public class TCPTest1 {
//客户端
@Test
public void client(){
Socket socket = null;
OutputStream outputStream = null;
InputStream inputStream = null;
try {
//1.创建Socket对象,指明服务器端的IP和端口号
InetAddress inetAddress = InetAddress.getByName("192.168.1.106");
socket = new Socket(inetAddress,8899);
//2.获取一个输出流,用于输出数据
outputStream = socket.getOutputStream();
//3.写出数据
outputStream.write("你好,我是客户端".getBytes());
//关闭数据的输出
socket.shutdownOutput();
//接收服务端数据
inputStream = socket.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int len1;
while ((len1 = inputStream.read(b)) != -1){
byteArrayOutputStream.write(b,0,len1);
}
System.out.println(byteArrayOutputStream.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源的关闭
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (inputStream != null);
{
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//服务端
@Test
public void server(){
ServerSocket serverSocket = null;
InputStream inputStream = null;
ByteArrayOutputStream outputStream = null;
OutputStream outputStream1 = null;
try {
//创建服务器端的ServerSocket,指明自己的端口号
serverSocket = new ServerSocket(8899);
//2.调用accept()表示接收来自客户端的socket
Socket accept = serverSocket.accept();
//3.获取数入流
inputStream = accept.getInputStream();
//不建议写,有乱码
// byte[] bytes = new byte[1024];
// int len;
// while ((len = inputStream.read(bytes)) != -1){
// String str = new String(bytes, 0, len);
// System.out.print(str);
// }
//4.读取输入流中的数据
outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[5];
int len;
while ((len = inputStream.read(bytes)) != -1){
outputStream.write(bytes,0, len);
}
System.out.println(outputStream.toString());
//System.out.println("收到了来自:"+serverSocket.getInetAddress().getHostAddress());
//服务器端给予客户端反馈
outputStream1 = accept.getOutputStream();
outputStream1.write("接收成功".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(inputStream != null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (serverSocket != null){
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (outputStream != null){
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(outputStream1 != null){
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP网络编程
package com.senior.day11;
import org.testng.annotations.Test;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
/**
* @Author: 小郑
* @description:
* UDPd协议的网络编程 (不可靠) 以数据报的形式发送,数据报限定为64K;效率高
*
* @Date: 2022/5/14 16:30
* @Version: 1.8.0_131
*/
public class UDPTest {
//发送端
@Test
public void sent() throws Exception{
DatagramSocket socket = new DatagramSocket();
String str = "我是UDP方式发送的";
byte[] bytes = str.getBytes();
InetAddress localHost = InetAddress.getLocalHost();
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, localHost, 9090);
socket.send(datagramPacket);
socket.close();
}
//接收端
@Test
public void receiver() throws Exception{
DatagramSocket socket = new DatagramSocket(9090);
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes,0,bytes.length );
socket.receive(datagramPacket);
System.out.println(new String(datagramPacket.getData(),0,datagramPacket.getLength()));
socket.close();
}
}
URL 编程
package com.senior.day11;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @Author: 小郑
* @description:
* URL统一资源定位符,对应着互联网上的某一资源地址.
* 2.格式
* https://www.baidu.com
* 协议 主机名
* @Date: 2022/5/14 16:50
* @Version: 1.8.0_131
*/
public class URLTest {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com/s?ie=UTF-8&wd=baidu");
System.out.println(url.getProtocol()); //获取该URL的协议名
System.out.println(url.getHost());//获取该url的主机名
System.out.println(url.getPort());//获取该url的端口号
System.out.println(url.getPath());//获取该url的文件路径
System.out.println(url.getFile());//获取该url的文件名
System.out.println(url.getQuery());//获取该url的查询名
} catch (MalformedURLException e) {
e.printStackTrace();
} finally {
}
}
}
反射
Java反射机制概述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFIaEda0-1652776738310)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\反射的概述.png)]
反射相关的主要API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field : 代表类的成员变量
java.lang.reflect.Construction :代表类的构造器
package com.senior.day12;
import org.testng.annotations.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author: 小郑
* @description:
* @Date: 2022/5/15 9:03
* @Version: 1.8.0_131
*/
public class ReflectionTest {
//反射之前对于Person类的操作
@Test
public void test1(){
//1.创建Person对象
Person person = new Person("tom",12);
//2.通过对象,调用其内部的属性,方法
person.setAge(10);
System.out.println(person.toString());
person.show();
//在Person类外部不能通过Person对象调其内部私有结构
//比如:name,showNation(),以及私有的构造器
}
//反射后,对于Person的操作
@Test
public void test2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class clazz = Person.class;
//1.通过反射创建Person类对象
Constructor constructor = clazz.getConstructor(String.class, int.class);
Object tom = constructor.newInstance("Tom", 12);
Person person = (Person) tom;
System.out.println(person.toString());
//2.通过反射,调用对象指定的属性,方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(person, 10);
System.out.println(person.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(person);
System.out.println("---------------------------------------");
//通过反射可以调用Person类的私有结构.比如:私有的构造去,方法,属性
//调用私有的构造器
Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Person jerry = (Person)constructor1.newInstance("Jerry");
System.out.println(jerry);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(jerry, "Jon");
System.out.println(jerry);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(jerry, "中国");//相当于jerry.showNation("中国");
System.out.println(nation);
}
理解Class类并获取Class实例
/*
疑问:
一.通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪一个?
建议:直接用new的方式
什么时候会使用:反射的方式,反射的特征:动态性
二.反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
不矛盾.
关于java.lang.Class类的理解
1.类的加载过程
程序经过javac.exe 命令后,会生成一个或多个字节码文件(.class结尾),
接着使用Java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,
此过程就称为类的加载.加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class类的一个实例.
2.换句话说:Class实例就对应着一个运行时类
3.加载到内存中的运行时类,会缓存一定的时间,在此时间内,我们可以通过不同的方式获取此运行时类
*/
//获取Class的实例方式(前三种方式需要掌握)
@Test
public void test3() throws ClassNotFoundException {
//方式一 调用运行时类的属性 .class
Class<Person> clazz = Person.class;
//方式二 通过运行时类的对象,调用getClass()
Person person = new Person();
Class clazz1 = person.getClass();
System.out.println(clazz1);
//方式三: 调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.senior.day12.Person");//参数以全类名的方式
System.out.println(clazz3 );
System.out.println(clazz == clazz1);
System.out.println(clazz == clazz3);
//方式四 使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.senior.day12.Person");
System.out.println(clazz4);
System.out.println(clazz == clazz4);
}
类的加载与ClassLoader的理解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fK1Gcn3N-1652776738316)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\Class类的实例对象.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mlc6b8gr-1652776738317)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\类的加载器.png)]
创建运行时类的对象
package com.senior.day12;
import org.testng.annotations.Test;
import java.util.Random;
/**
* @Author: 小郑
* @description: 通过反射创建对应的运行时类的对象
* @Date: 2022/5/15 14:34
* @Version: 1.8.0_131
*/
public class NewInstanceTest {
@Test
public void test1() throws InstantiationException, IllegalAccessException {
Class<Person> clazz = Person.class;
/*
newInstance() 调用子方法,创建对应的运行时类的对象.内部调用了运行时类的空参构造器
要想此方法正常创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造的访问权限得够.通常设置为public
在Javabean中要求提供一个public的空参构造器.原因:
1.通过反射,创建运行时类的对象
2.便于子类继承与运行时类时,默认调用super()时,保证父类有此构造器
*/
Person obj = clazz.newInstance();
System.out.println(obj);
}
//体会反射的动态性,只用通过运行时,才能看出创建的哪个空参构造器
@Test
public void test2(){
int num = new Random().nextInt(3);//0,1,2
String classPath = " ";
switch (num){
case 0 :
classPath = "java.util.Date";
break;
case 1 :
classPath = "java.lang.Object";
break;
case 2 :
classPath = "com.senior.day12.Person";
break;
}
try {
Object obj = getInstance(classPath);
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
*@description: 创建一个指定类的对象
*@parameter: [classpath] 指定类的全类名
*@return: java.lang.Object
*@author: 小郑
*@date: 2022/5/15
*/
public Object getInstance(String classpath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> aClass = Class.forName(classpath);
return aClass.newInstance();
}
}
获取运行时类的完整结构
获取属性结构:
getFields():获取当前运行时类及其父类中声明为public访问权限的属性
getDeclaredFields() : 获取当前运行时类中声明的所有属性,不包含父类中声明的属性
getModifiers() 获取属性的权限修饰符
getType() 获取数据类型
getName() 获取属性名
获取运行时类的方法结构:
getMethods( ) 获取当前运行时类及其所有父类中的所有声明为public权限的方法
getDeclaredMehods() :获取当前运行时类中声明的所有方法(不包含父类中声明的方法)
getAnnotations() 获取方法声明的注解
getModifiers() 获取方法的权限修饰符
getReturnType() 获取方法的返回值类型
getName() 获取方法的方法名
getParameterTypes( ) 获取形参列表
getExceptionTypes 获取抛出的异常
获取运行时类的构造器结构:
getConstructors() 获取当前运行时类中声明为public的构造器
getDeclaredConstructors() :获取当前运行时类中声明的所有构造器
获取运行时类的父类及父类的泛型:
getSuoerClass() 获取运行时类的父类
getGenericSpuerClass() 获取运行时类的带泛型的父类
调用运行时类的指定结构
获取指定的属性: 要求运行时类的属性声明为public
getField(String name) //不建议使用
getDeclaredField(String fieldname) 获取运行时类中指定变量名的属性
fieldname.setAccessible(true);5
fieldname.set(p,“string”);
如何操作运行时类中的指定方法:
//1.创建运行时对象
//获取指定的某个方法
getDeclaredMethod() ;//参数1指明获取方法的名称,参数2:指明获取方法的形参列表
方法名.invoke(p," ") // 参数1:方法的调用者,参数2形参赋值的实参 返回值及为对应类中调用的方法的返回值
如何操作运行时类中的指定构造器:
//1.获取指定的构造器
getDeclaredConstructor(String.class); 参数指明构造器的参数列表
//2.保证此构造器是可访问的
fieldname.setAccessible(true);
//3.调用此构造器创建运行时类的对象
constructor.newInstance(" String");
反射的应用:动态代理
是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代里对象
使用场合:
调试
远程方法调用
package com.senior.day13;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Handler;
/**
* @Author: 小郑
* @description: 动态代理举例
* @Date: 2022/5/16 8:22
* @Version: 1.8.0_131
*/
interface Human {
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human {
@Override
public String getBelief() {
return "I believe I can fly.";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/*
要想实现动态代理,需要解决的问题?
1.如何根据加载到内存中的被代理类,动态的创建一个代里类及其对象.
2.当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法.
*/
class ProxyFactory{
//调用此方法,返回一个代理类的对象,解决问题1
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object object;//赋值时也需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.object = obj;
}
//当我们通过代理类的对象,调用方法A时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法A的功能声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method()即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//object:被代理类的对象
Object invoke = method.invoke(object, args);
//上述方法的返回值作为当前类中invoke()方法的反返回值
return invoke;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance 代里类的对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理类调用方法时,会自动的调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("麻辣烫");
}
}
package com.senior.day13;
/**
* @Author: 小郑
* @description:
* 静态代理举例
* 特点:代理类和被代理类在编译期间,就确定下来了
* @Date: 2022/5/16 8:12
* @Version: 1.8.0_131
*/
interface ClothFactory{
void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//就拿被代理类对象进行实例化
public ProxyClothFactory() {
}
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代里工厂做一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做一些后续的收尾工作");
}
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("NIKE工厂生产一批运动服");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理类的对象
NikeClothFactory nikeClothFactory = new NikeClothFactory();
//创建代理类的对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
//
proxyClothFactory.produceCloth();
}
}
Java8 的其他新特性
Lambda表达式
是一个匿名函数,可以理解为一段可以传递的代码.
package com.senior.day13;
import org.testng.annotations.Test;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* @Author: 小郑
* @description: Lambda表达式的使用:
* 1.举例(o1,o2) -> Integer.compare(o1,o2);
* 2.格式:
* -> :Lambda操作符 或 箭头操作符
* ->左边:Lambda的形参列表(其实就是接口中的抽象方法的形参列表)
* ->右边:Lambda体(其实就是重写的抽象方法的方法体)
* 3.Lambda表达式的使用
* 语法一: 无参,无返回值
* 语法格式二: Lambda需要一个参数,但是没有返回值
* 语法格式三: 数据类型可以省略,因为可由编译器推断得出,称为"类型推断".
* 语法格式四: Lambda若只需要一个参数式,参数的小扩号可以省略
* 语法格式五: Lambda 需要两个或以上的参数时,多条执行语句,并且可以没有返回值
* 语法格式六: 当Lambda 体只有一条语句时,return与大括号若有,都可以省略
*
* 总结:
* ->左边:形参列表的参数类型可以省略(类型推断);如果参数列表只有一个参数,其一对()也可以省略
* ->右边:Lambda体应该使用一对{} 包裹;如果Lambda体只有一条执行语句(也可能是return语句),可以省略{} 和return语句
*
* 4.Lambda 表达式的本质: 作为函数式接口的实例
* 5.如果一个接口中之声明了一个抽象方法,则此接口就称为函数式接口
* @Date: 2022/5/16 9:52
* @Version: 1.8.0_131
*/
public class LambdaTest1 {
// 语法一: 无参,无返回值
@Test
public void test1() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
runnable.run();
System.out.println("-----------------------");
//Lambda表达式
Runnable runnable1 = () -> {
System.out.println("我爱北京故宫");
};
runnable1.run();
}
//Lambda需要一个参数,但是没有返回值
@Test
public void test2() {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("谎言和誓言的区别是什么");
System.out.println("---------------------");
Consumer<String> consumer1 = (String s) -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了");
}
//数据类型可以省略,因为可由编译器推断得出,称为"类型推断".
@Test
public void test3() {
Consumer<String> consumer1 = (String s) -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了");
System.out.println("=============================");
Consumer<String> consumer2 = (s) -> {//类型推断
System.out.println(s);
};
consumer2.accept("一个是听的人当真了,一个是说的人当真了");
}
//语法格式四: Lambda若只需要一个参数式,参数的小扩号可以省略
@Test
public void test4() {
Consumer<String> consumer1 = (s) -> {//类型推断
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了");
System.out.println("=============================");
Consumer<String> consumer2 = s -> {//类型推断
System.out.println(s);
};
consumer2.accept("一个是听的人当真了,一个是说的人当真了");
}
//语法格式五: Lambda 需要两个或以上的参数时,多条执行语句,并且可以没有返回值
@Test
public void test5() {
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(12, 65));
System.out.println("=============================");
Comparator<Integer> comparator1 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(comparator1.compare(12, 65));
}
;
//语法格式六: 当Lambda 体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test6() {
Comparator<Integer> comparator1 = (o1, o2) -> {
return o1.compareTo(o2);
};
System.out.println(comparator1.compare(12, 65));
System.out.println("=============================");
Comparator<Integer> comparator2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(12, 65));
}
}
函数式(Functional)接口
如果一个接口中之声明了一个抽象方法,则此接口就称为函数式接口
可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检 查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个 接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口
在Java8中,Lambda表达式就是一个函数式接口的实例。这就是 Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口 的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
函数式接口举例:
自定义函数式接口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bQTzx0Tn-1652776738318)(G:\java 学习(尚硅谷)\尚硅谷博客\高级\自定义函数式接口.png)]
Java 内置四大核心函数式接口
方法引用与构造器引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就 是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向 一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的 方法的参数列表和返回值类型保持一致!
格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
如下三种主要使用情况:
对象::实例方法名
类::静态方法名
类::实例方法名
强大的Stream API
package com.senior.day13;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @Author: 小郑
* @description:
* 1.Stream关注的是对数据的运算,与CPU打交道
* 集合关注的是数据的存储,与内存打交道
* 2.Stream 自己不会存储数据
* Stream 不会改变源对象.相反,他们会返回一个持有结构的新的Stream
* Stream 操作时延迟执行的,这意味着他们会等到需要结果的时候才执行
* 3.Stream执行流程
* 1.创建Stream 一个数据源(集合,数组) ,获取一个流
* 2.中间操作 一个中间操作链,对数据源的数据进行处理
* 3.终止操作(终端操作) 一旦终止操作,就执行中间操作链,并产生结果.之后,就不会被使用
*
* 测试Stream的实例化
* @Date: 2022/5/16 15:25
* @Version: 1.8.0_131
*/
public class StreamAPiTest {
//创建Stream方式一: 通过集合
@Test
public void test1(){
//default Stream<E> stream() : 返回一个顺序流
//default Stream<E> parallelStream():返回一个并行流
}
//创建Stream方式二:通过数组
@Test
public void test2(){
int[] num = new int[]{1,2,3,4,5,6};
//调用Arrays类的static<T> Stream<T> stream(T[] array):返回一个流
IntStream stream = Arrays.stream(num);
}
//创建Stream方式三:通过Stream的of()
@Test
public void test3(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7);
}
//创建Stream方式四:创建无限流
@Test
public void test4(){
//迭代
//public static<T> Stream<T> iterate(final T seed , final UnaryOperator<T> f)
//遍历10个偶数
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
//生成
//public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
Optional类
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表 这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不 存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在 则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
创建Optional类对象的方法:
Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):t可以为null
package com.senior.day14;
import org.testng.annotations.Test;
import java.util.Optional;
/**
* @Author: 小郑
* @description:
* @Date: 2022/5/17 8:52
* @Version: 1.8.0_131
*/
public class OptionalTest {
@Test
public void test1() {
Optional<Object> o = Optional.empty();
if (!o.isPresent()) {//判断Optional封装的数据是否包含数据
System.out.println("数据为空");
}
// System.out.println(o.get());
System.out.println(o);
}
@Test
public void test2() {
String str = "hello";
//of(T t) 封装数据t生成Optional对象,要求t非空,否则报错
Optional<String> optionalS = Optional.of(str);
//get() 取封装的数据,如果Optional封装的数据value为空则get()报错,否则返回value.
//get()通常与of()搭配使用,用于获取内部的封装的数据value
System.out.println(optionalS.get());
}
@Test
public void test3() {
String str = "北京";
//ofNullable(T t) 封装数据t赋给Optional内部的value.不要求t非空
Optional<String> o = Optional.ofNullable(str);
//orElse(T t1) 如果Optional内部的数据value非空,则返回此value值.如果value为空则返回t1
String s = o.orElse("上海");
System.out.println(s);
}
}
判断Optional容器中是否包含对象:
boolean isPresent() : 判断是否包含对象
void ifPresent(Consumer consumer) :如果有值,就执行Consumer 接口的实现代码,并且该值会作为参数传给它。
获取Optional容器的对象:
T get(): 如果调用对象包含值,返回该值,否则抛异常
T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
T orElseGet(Supplier other) :如果有值则将其返回,否则返回由 Supplier接口实现提供的对象。
T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返 回,否则抛出由Supplier接口实现提供的异常。