Java高级笔记_java基础知识重点总结

Java高级笔记_java基础知识重点总结Java 高级笔记文章目录 Java 高级编程自学笔记 康师傅 第一章多线程一 基本概念 程序 进程 线程多线程的优点第一章多线程一 基本概念 程序 进程 线程程序 是为完成特定任务 用某种语言编写的一组指令的集合 即指一段静态的代码 静态对象进程 是程序的一次执行过程 或是正在运行的一个程序 是一个动态的过程 有他自身的产生 存在和消亡的过程

Java高级编程自学笔记(康师傅)


文章目录

第一章 多线程

一、基本概念:程序、进程、线程

程序: 是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象

进程: 是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有他自身的产生,存在和消亡的过程。---------生命周期

线程: 进程可进一步细化为线程,是一个程序内部的一条执行路径
>若一个进程同一时间并行执行多个线程,就是支持多线程的
>线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
>一个进程中的多个线程共享相同的内存单/内存地址空间–>他们从同一堆中分配对象,可以访问相 同的变量和对象,使得线程之间通信更简便、高效,但是多个线程操作共享的系统资源可能会发生安全隐患

单核Cpu和多核Cpu的理解:
>单核Cpu:其实是一种假的多线程,因为在一个时间单内,也只能执行一个线程的任务,但是时间单短,导致感觉像是在多线程运行
>如果是多核,才能发挥多线程的效率
>一个Java应用程序java.exe,其实至少有三个线程,main()主线程,gc()垃圾回收线程,异常处理线程

并行与并发:
>并行:多个Cpu同时执行多个任务,比如:多个人同时做不同的事
>并发:一个Cpu(采用时间片)同时执行多个任务,比如:商品秒杀,多个人做同一件事

多线程的优点

背景: 以单核cpu为例,只使用单个线程先后完成多个任务,肯定比用多个线程来完成用的时间更短(单核多线程实际上是短时间的轮换执行程序,是一种假的多线程)

多线程程序的优点:
1.提高应用程序的响应,对图形化界面更有意义,可增强用户体验
2.提高计算机系统CPU的利用率
3.改善程序结构,将即长又复杂的进程分为多个线程,独立运行,利于理解和修改

何时需要多线程:
1.程序需要同时执行两个或多个任务
2.程序需要实现一些需要等待的任务是,如用户输入、文件读写操作、网络操作、搜索等
3.需要一些后台运行的程序时(垃圾回收线程等)

二、多线程的创建与使用

多线程的创建与使用

package threadtest; / * 多线程的创建:方式一:继承于Thread类 * 1. 创建一个继承与Thread的子类 * 2.重写Thread类的run()方法 --> 将此线程执行的操作声明在run() * 3.创建Thread类的子类的对象 * 4.通过此对象调用start()方法 * * * 例子:遍历100内的偶数 * * @author 昭浅 * @create 2022/1/20-20:22 */ //1.创建一个集成于Thread类的子类 class MyThread extends Thread { 
    //2.重写Thread类的run() ①启动当前线程 ②调用当前线程的run() @Override public void run() { 
    for(int i=0;i<=100;i++){ 
    if(i%2==0){ 
    System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class ThreadTest { 
    public static void main(String[] args) { 
    //3.创建Thread类的子类的对象 MyThread t1 = new MyThread(); //4.通过此对象调用start() t1.start(); //问题一:我们不能通过直接调用run()的方式启动线程,需要使用start()启动线程 // t1.run(); //问题二:再启动一个线程:遍历100内的偶数 ,不可以还让已经调用start()的线程去执行 会报错--IllegalThreadStateException // t1.start(); //我们需要重新创建一个线程的对象 MyThread t2 = new MyThread(); t2.start(); //测试主线程与其他线程同时执行的顺序 //如下操作仍然是在main()线程中执行 for(int i=0;i<=100;i++){ 
    if(i%2!=0){ 
    System.out.println(Thread.currentThread().getName()+":"+i+"*main()*"); } } } } 

Thread中的常用方法

package threadtest; / * 测试Thread中常用的方法 * 1.start():启动当前线程,调用当前线程的run() * 2.run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 * 3.currentThread(): 静态方法,返执行当前代码的线程 * 4.getName(): 返回当前线程的名字 * 5.setName(): 设置当前线程的名字 也可使用构造器声明线程名字 * 6.yield(): 释放当前cpu的执行权,不会释放锁 但是释放后也有可能CPU再次分配执行权给此线程,强制使当前线程进入就绪状态,之后也有可能重 * 新获取CPU的执行权 * 7.join(): 在线程a中调用线程b的join(), 此时线程a就进入了阻塞状态,直到线程b完全执行完以后, * 线程a才结束阻塞状态 * 8.stop(): 已过时 当执行此方法时,强制结束当前线程 * 9.sleep(long:millitime):让当前线程“睡眠”指定的millitime毫秒,在指定的millitime毫秒时间内,当前线程是阻塞状态,其情况可用作进度条、倒计时等,该方法不会释放锁 * 10.isAlive(): 判断当前线程是否存活 * @author 昭浅 * @create 2022/1/20-21:56 */ class HelloThread extends Thread{ 
    public HelloThread(String name){ 
    super(name); } @Override public void run() { 
    for (int i = 0;i <100; i++) { 
    if(i%2==0){ 
    try { 
    sleep(100);//在指定的100毫秒内,当前线程进入阻塞状态 //导致该线程一直处于阻塞状态,需要其他线程执行完毕,再执行此线程 } catch (InterruptedException e) { 
    e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+i); } if(i%20==0){ 
    yield();//此处省略了this 或 Thread.currentThread } } } } public class ThreadMethod { 
    public static void main(String[] args) { 
    HelloThread h1 = new HelloThread("线程----"); // HelloThread h1 = new HelloThread(); // h1.setName("线程一"); h1.start(); //返回当前线程并设置名字 Thread.currentThread().setName("主线程"); for (int i = 0;i <100; i++) { 
    if(i%2==0){ 
    System.out.println(Thread.currentThread().getName()+":"+i); } if(i==40){ 
    try { 
    h1.join(); } catch (InterruptedException e) { 
    e.printStackTrace(); } } } //判断h1线程是否还存活 System.out.println(h1.isAlive()); } } 

线程的优先级

package threadtest; / * 线程的优先级 * 1.MAX_PRIORITY:10 最大优先级 * MIN_PRIORITY:1 最小优先级 * NORM_PRIORITY:5 --》默认优先级 * 2.如何获取和设置当前线程的优先级 * getPriority():获取当前线程的优先级 * setPriority():设置当前线程的优先级 * * 说明:高优先级的线程要抢占低优先级线程cpu的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下 * 被执行,并不是意味着只有当高优先级的线程执行完成以后,低优先级的线程才会被执行 * * * * @author 昭浅 * @create 2022/1/21-14:48 */ class HiThread extends Thread{ 
    @Override public void run() { 
    for (int i = 0; i < 50; i++) { 
    System.out.println(getName()+"-"+getPriority()+":"+i); } } } public class ThreadPriority { 
    public static void main(String[] args) { 
    HiThread h1=new HiThread(); //将分线程的优先级设置为最高 h1.setPriority(Thread.MAX_PRIORITY); h1.setName("线程一"); h1.start(); //为主线程的优先级设置为最低 Thread.currentThread().setPriority(Thread.MIN_PRIORITY); Thread.currentThread().setName("主线程"); for (int i = 50; i <100; i++) { 
    System.out.println(Thread.currentThread().getName()+"-"+Thread.currentThread().getPriority()+":"+i); } } } 

实现Runnable接口创建多线程

package threadtest; / * 创建多线程的方式二: 实现Runnable接口 * 1.创建一个实现了Runnable接口的类 * 2.实现类去实现Runnable中的抽象方法:run() * 3.创建实现类的对象 * 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 * 5.通过Thread类的对象调用start()开启线程 * * @author 昭浅 * @create 2022/1/21-15:50 */ //1.创建一个实现Runnable接口的实现类 class MThread implements Runnable{ 
    @Override //2.实现类去实现Runnable中的抽象方法:run() public void run() { 
    for (int i = 0; i < 100; i++) { 
    System.out.println(Thread.currentThread().getName()+":"+i); } } } public class RunnableTest { 
    public static void main(String[] args) { 
    // 3.创建实现类的对象 MThread m = new MThread(); //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread t1 = new Thread(m); t1.setName("线程一"); Thread t2 = new Thread(m); t2.setName("线程二"); //5.通过Thread类的对象调用start()开启线程 t1.start(); t2.start(); } } 

比较创建线程的两种方式

 * 比较创建线程的两种方式 * 开发中,优先选择:实现Runnable接口的方式 * 原因:1.实现的方式还没有类的单继承性的局限性 * 2.实现的方式更适合来处理多个线程有共享数据的情况 * 联系:public Thread implemnets Runnable * 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()

三、线程的生命周期

在这里插入图片描述

四、线程的同步

方式一:同步代码块解决线程同步

package threadtest; / * 例子:创建三个窗口同时售卖100张票 ----实现Runnable接口的方式 * * 1.问题:买票过程中,出现了重票、错票---->线程安全的问题 * 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票 * 3.如何解决:当一个线程a在操作ticket时,其他线程不能参与进来,知道线程a操作完成ticket时,其他线程 * 才可以开始操作ticket,这种情况即使线程a出现了阻塞,也不能被改变 * 4.在Java中,我们通过同步机制,来解决线程的安全问题 * 方式一:同步代码块 * * synchronized(同步监视器){ * //需要被同步的代码 * } * 说明:1.操作共享数据的代码,即为需要被同步的代码 * 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据 * 3.同步监视器:俗称:锁,任何一个类的对象,都可以充当锁 * 要求:多个线程必须要用同一把锁 * 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器 * 方式二:同步方法: * * 5.同步的方式:解决了线程的安全问题 ---好处 * 操作同步代码块时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程。效率低 ---局限性 * @author 昭浅 * @create 2022/1/22-16:03 */ class Windows2 implements Runnable { 
    private int ticket = 100; Object obj = new Object(); @Override public void run() { 
    while (true) { 
    synchronized (this){ 
   //此时的this:唯一的Windows2的对象 //synchronized (obj){ 
    if (ticket > 0) { 
    // try { 
    // Thread.sleep(100); // } catch (InterruptedException e) { 
    // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + ":" + ticket); ticket--; } else { 
    break; } } } } } public class ThreadSafe { 
    public static void main(String[] args) { 
    Windows2 w = new Windows2(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("线程一"); t2.setName("线程二"); t3.setName("线程三"); t1.start(); t2.start(); t3.start(); } } 
package threadtest; / * 例子:创建三个窗口同时售卖100张票 ----继承Thread类的方式 * <p> * 使用同步代码块解决继承Thread类的方式的线程安全问题 * * 说明:在继承Thread类创建多线程的方式中,慎用this充当同步监视器,可以考虑用当前类充当同步监视器 * @author 昭浅 * @create 2022/1/22-16:47 */ class Window2 extends Thread { 
    private static int ticket = 100;//设置ticket为静态变量 所有对象的票都是它 private static Object obj = new Object();//所有线程的同步锁必须是一个,所以要加static @Override public void run() { 
    while (true) { 
    synchronized (Window2.class) { 
   //Class c = Window2.class Window2.class只会加载一次 // synchronized (obj){ 
    // 错误的:synchronized (this){ 因为此时的对象不是唯一,表示w1 w2 w3 if (ticket > 0) { 
    try { 
    Thread.sleep(100); } catch (InterruptedException e) { 
    e.printStackTrace(); } System.out.println(getName() + ":" + ticket); ticket--; } else { 
    break; } } } } } public class ThreadSafe2 { 
    public static void main(String[] args) { 
    Window2 w1 = new Window2(); Window2 w2 = new Window2(); Window2 w3 = new Window2(); w1.setName("窗口一"); w2.setName("窗口二"); w3.setName("窗口三"); w1.start(); w2.start(); w3.start(); } } 

方式二:同步方法解决线程同步

package threadtest; / * 使用同步方法解决实现Runnable接口的线程安全问题 * * 关于同步方法的总结 * 1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明 * 2.非静态的同步方法,同步监视器是this * 静态的同步方法,同步监视器是当前类本身 * * @author 昭浅 * @create 2022/1/22-17:20 */ class Windows1 implements Runnable { 
    private int ticket = 100; @Override public void run() { 
    while (ticket>0) { 
    show(); } } private synchronized void show(){ 
   //同步监视器:this if (ticket > 0) { 
    // try { 
    // Thread.sleep(100); // } catch (InterruptedException e) { 
    // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + ":" + ticket); ticket--; } } } public class ThreadSafe3 { 
    public static void main(String[] args) { 
    Windows1 w = new Windows1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("线程一"); t2.setName("线程二"); t3.setName("线程三"); t1.start(); t2.start(); t3.start(); } } 
package threadtest; / * 使用同步方法来使用Thread类的线程安全问题 * * @author 昭浅 * @create 2022/1/22-17:30 */ class Window3 extends Thread { 
    private static int ticket = 100;//设置ticket为静态变量 所有对象的票都是它 private static Object obj = new Object();//所有线程的同步锁必须是一个,所以要加static @Override public void run() { 
    while (ticket>0) { 
    show(); } } private static synchronized void show(){ 
   //同步监视器:Window3.class 此方法为静态方法,调用时是用类调用而非对象 // private synchronized void show(){//错误,此时的同步监视器是 t1 t2 t3 不是同一把锁 if (ticket > 0) { 
    // try { 
    // Thread.sleep(100); // } catch (InterruptedException e) { 
    // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + ":" + ticket); ticket--; } } } public class ThreadSafe4 { 
    public static void main(String[] args) { 
    Window3 w1 = new Window3(); Window3 w2 = new Window3(); Window3 w3 = new Window3(); w1.setName("窗口一"); w2.setName("窗口二"); w3.setName("窗口三"); w1.start(); w2.start(); w3.start(); } } 

方式三:Lock锁方式解决线程安全问题

package threadtest; import sun.java2d.ReentrantContextProviderTL; import java.util.concurrent.locks.ReentrantLock; / * 解决线程安全问题的方式三:lock锁 * * 1.面试题:synchronized和Lock的异同 * 相同:二者都可以解决线程安全问题 * 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器 * lock需要手动的自动同步(lock()),同时结束时也需要手动的实现(unlock()) * * 2.优先使用顺序 * lock-->同步代码块(已经进入方法体,分配了相应的资源)-->同步方法(在方法体之外) * @author 昭浅 * @create 2022/1/22-17:30 */ class Windows3 implements Runnable { 
    private int ticket = 100; //1.实例化Reentrantlock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { 
    while (ticket > 0) { 
    //调用锁定方法lock() lock.lock(); try { 
    if (ticket > 0) { 
    // try { 
    // Thread.sleep(100); // } catch (InterruptedException e) { 
    // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + ":" + ticket); ticket--; } else { 
    break; } }finally{ 
    //3.调用解锁方法 lock.unlock(); } } } } public class ThreadSafe5 { 
    public static void main(String[] args) { 
    Windows3 w = new Windows3(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("线程一"); t2.setName("线程二"); t3.setName("线程三"); t1.start(); t2.start(); t3.start(); } } 

五、线程的死锁

package threadtest; / * 演示进程的死锁问题 * * 解决方法: * 专门的算法、原则 * 尽量减少同步资源的定义 * 尽量避免嵌套同步 * @author 昭浅 * @create 2022/1/22-18:23 */ public class DeadLockTest { 
    public static void main(String[] args) { 
    StringBuffer s1 = new StringBuffer(); StringBuffer s2 = new StringBuffer(); new Thread() { 
    @Override public void run() { 
    synchronized (s1) { 
    s1.append("a"); s2.append("1"); try { 
    Thread.sleep(100); } catch (InterruptedException e) { 
    e.printStackTrace(); } synchronized (s2) { 
    s1.append("b"); s2.append("2"); System.out.println(s1); System.out.println(s2); } } } }.start(); new Thread(new Runnable() { 
    @Override public void run() { 
    synchronized (s2) { 
    s1.append("c"); s2.append("3"); try { 
    Thread.sleep(100); } catch (InterruptedException e) { 
    e.printStackTrace(); } synchronized (s1) { 
    s1.append("d"); s2.append("4"); System.out.println(s1); System.out.println(s2); } } } }).start(); } } 

六、线程的通信

package threadtest; / * 线程通信的例子:使用两个线程打印1-100,线程1 线程2 交替打印 * * 涉及到的三个方法: * wait():一旦执行此方法。当前线程就会进入阻塞状态,并释放同步监视器 * notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个,优先级相同就随机唤醒 * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程 * * 说明: * 1.wait()、notify()、notifyAll()这三个方法必须使用在同步方法或同步代码块当中 * 2.wait()、notify()、notifyAll()三个方法的调用者必须是同步代码块或同步方法的同步监视器 * 否组会出现IllegalMonitorStateException * 3.wait()、notify()、notifyAll()三个方法是定义在java.lang.Objet类中的 * @author 昭浅 * @create 2022/1/23-21:04 */ class NumPrint implements Runnable { 
    private int num = 1; private Object obj =new Object(); @Override public void run() { 
    while (true) { 
    synchronized (this) { 
   //synchronized (obj) { 
    notify();//此前省略的是this// obj.notify(); if (num < 100) { 
    System.out.println(Thread.currentThread().getName() + ":" + num); num++; try { 
    wait();//此前省略的是this } catch (InterruptedException e) { 
    e.printStackTrace(); } } else { 
    break; } } } } } public class CommunicationTest { 
    public static void main(String[] args) { 
    NumPrint n = new NumPrint(); Thread t1 = new Thread(n); Thread t2 = new Thread(n); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } } 

七、jdk5.0新特性—线程创建的两种方式

1.实现Callable接口创建线程

package threadtest; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; / * 创建线程的方式三:实现Callable接口 ------jdk5.0新增 * * 如何理解Callable接口的方式创建多线程比实现Runnable接口方式强大? * 1.call()可以有返回值 * 2.call()可以抛出异常,被外面的操作捕获,获取异常的信息 * 3.Callable是支持泛型的 * @author 昭浅 * @create 2022/1/25-11:15 */ //1.创建一个实现Callable接口的实现类 class Num implements Callable { 
    //2.实现call()方法,将此线程需要执行的操作声明在call()中 public Object call(){ 
    int sum=0; for (int i = 0; i < 100; i++) { 
    if(i%2==0){ 
    sum+=i; } } return sum; } } public class CallableTest { 
    public static void main(String[] args) { 
    //3.创建Callable接口实现类的对象 Num n = new Num(); //4.将此Callable接口实现类的对象作为参数传递到FutureTask的构造器中,创建FutureTask的对象 FutureTask f = new FutureTask(n); //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() Thread t = new Thread(f); t.start(); try { 
    //6.获取Callable中的call()的返回值 //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值 Object sum=f.get(); System.out.println(sum); } catch (InterruptedException e) { 
    e.printStackTrace(); } catch (ExecutionException e) { 
    e.printStackTrace(); } } } 

2.使用线程池创建线程

package threadtest; import javax.xml.ws.Service; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; / * 创建线程的方式四:线程池 * * 好处: * 1.提高响应速度(减少了创建新线程的时间 * 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)) * 3.便于线程管理 * corePoolSize:核心池的大小 * maximumPoolSize:最大线程数 * keepAliveTime:线程没有任务时最多保持多长时间后会终止 * * 面试题:创建线程的方式有几种,分别是什么 * * 线程池相关API *  JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors *  ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor *  void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行 * Runnable *  <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行 * Callable *  void shutdown() :关闭连接池 *  Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池 *  Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池 *  Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池 *  Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池 *  Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运 * 行命令或者定期地执行。 * @author 昭浅 * @create 2022/1/25-12:00 */ 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 ThreadPool { 
    public static void main(String[] args) { 
    //1.提供指定线程数量的线程池 ExecutorService service =Executors.newFixedThreadPool(10);//ExecutorService是一个接口,newFixedThreadPool()返回一个匿名实现类的对象 多态 //该匿名实现类的对象是由ThreadPoolExecutor类实现的 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();//适合适用于Callbale //3.关闭线程池 service.shutdown();//关闭线程池 } } 

第二章 常用类

一、String类

package StringTest; import org.junit.Test; import java.sql.SQLOutput; / * @author 昭浅 * @create 2022/1/26-21:27 */ public class StringTest { 
    /* 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.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中 6.字符串常量池中是不会存储相同内容的字符串的 */ @Test //@org.junit.Test public void method() { 
    String s1 = "abc"; String s2 = "abc"; s1 = "hello"; System.out.println(s1 == s2);//比较两个变量的地址值 false System.out.println(s1);//hello System.out.println(s2);//abc System.out.println(""); String s3 = "abc"; s3 += "def"; System.out.println(s3 == s2); System.out.println(s3);//abcdef System.out.println(s2);//abc System.out.println(""); String s4 = "abc"; String s5 = s4.replace('a', 'm'); System.out.println(s4);//abc System.out.println(s5);//mbc } /* String的实例化方式 方式一:通过字面量定义的方式 方式二:通过new+构造器的方式 面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象? 答:两个,一个是堆空间中new结构,里一个是char[]对应的常量池中的数据:“abc” */ @Test public void method2(){ 
    //通过字面量定义的方式,此时的s1和s2的数据javase声明在方法区中的字符串常量池中 String s1="javase"; String s2="javase"; //通过new+构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值 String s3=new String("javase"); String s4=new String("javase"); System.out.println(s1==s2);//true System.out.println(s1 == s3);//false System.out.println(s1 == s4);//false System.out.println(s3 == s4);//false System.out.println(""); Person p1 =new Person("Tom",12); Person p2 =new Person("Tom",12); System.out.println(p1.name.equals(p2.name));//true equals比较的是内容 System.out.println(p1.name == p2.name);//true 字符串存储在常量池中Tom的地址是一样的 p1.name="Jerry"; System.out.println(p2.name);//Tom System.out.println(p1.name == p2.name);//false } /* String字符串的拼接 结论: 1.常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量 2.只要其中有一个是变量,结果就在堆中 3.如果拼接的结果调用intern()方法,返回值就在常量池中 */ @Test public void method3(){ 
    String s1="javaEE"; String s2="hadoop"; String s3="javaEEhadoop"; String s4="javaEE"+"hadoop"; String s5=s1+"hadoop"; String s6="javaEE"+s2; String s7=s1+s2; System.out.println(s3 == s4);//true System.out.println(s3 == s5);//false System.out.println(s3 == s6);//false System.out.println(s3 == s7);//false System.out.println(s5 == s7);//false System.out.println(s6 == s7);//false String s8=s5.intern();//该方法返回值得到的s8使用的常量池中已经存在的javaEEhadoop System.out.println(s3 == s8);//true s1+="hadoop";//此步骤相当于是变量拼接,在堆中进行创建对象,而不是常量池 } } class Person{ 
    String name; int age; public Person() { 
    } public Person(String name, int age) { 
    this.name = name; this.age = age; } } 

二、String类的常用方法

package stringtest; import org.junit.Test; /* int length():返回字符串的长度: return value.length char charAt(int index): 返回某索引处的字符return value[index] boolean isEmpty():判断是否是空字符串:return value.length == 0 String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写 String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写 String trim():返回字符串的副本,忽略前导空白和尾部空白 boolean equals(Object obj):比较字符串的内容是否相同 boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大 小写 String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+” int compareTo(String anotherString):比较两个字符串的大小 String substring(int beginIndex):返回一个新的字符串,它是此字符串的从 beginIndex开始截取到最后的一个子字 符串。 String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字 符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。 */ public class StringTest2 { 
    @Test public void test(){ 
    String s1="HelloWorld"; System.out.println(s1.length()); System.out.println(s1.charAt(3)); System.out.println(s1.isEmpty()); String s2=s1.toLowerCase(); System.out.println(s2); System.out.println(s1);//s1不可变,仍为原先的字符串 String s3=" hello "; String s4=s3.trim(); System.out.println(s4);//输出hello,去掉收尾空格 System.out.println(s1.compareTo(s2));//将每个字符转换为ASCll码然后进行比较大小 } } 
package stringtest; import org.junit.Test; / *  boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束 *  boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始 *  boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的 * 子字符串是否以指定前缀开始 * * *  boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列 * 时,返回 true *  int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引 *  int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出 * 现处的索引,从指定的索引开始 *  int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引 *  int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后 * 一次出现处的索引,从指定的索引开始反向搜索 * 注:indexOf和lastIndexOf方法如果未找到都是返回-1 * * 什么情况下 indexOf(str)和lastIndexOf(str)的返回值相同 * 情况一:存在唯一的str * 情况二:不存在str 返回-1 * @author 昭浅 * @create 2022/1/28-14:39 */ public class StringTest3 { 
    @Test public void method(){ 
    String str1="helloworld"; boolean b1=str1.endsWith("rld"); System.out.println(b1);//true boolean b2=str1.startsWith("He"); System.out.println(b2);//false boolean b3=str1.startsWith("ll",2); System.out.println(b3);//true } @Test public void method2(){ 
    String str1="helloworld"; System.out.println(str1.contains("llo"));//true System.out.println(str1.indexOf("lo"));//3 System.out.println(str1.indexOf("lo", 4));//-1 没有找到 System.out.println(str1.lastIndexOf("wo"));//5 System.out.println(str1.lastIndexOf("wo", 4));//-1 } } 
package stringtest; import org.junit.Test; / *  String replace(char oldChar, char newChar):返回一个新的字符串,它是 * 通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 *  String replace(CharSequence target, CharSequence replacement):使 * 用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 *  String replaceAll(String regex, String replacement) : 使 用 给 定 的 * replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 *  String replaceFirst(String regex, String replacement) : 使 用 给 定 的 * replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 * *  boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。 * *  String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。 *  String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此 * 字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个素中。 * @author 昭浅 * @create 2022/1/28-14:53 */ public class StringTest4 { 
    @Test public void test(){ 
    String str1 ="我喜欢动漫"; String str2 =str1.replace('我','你'); System.out.println(str1); System.out.println(str2); String str3 = str1.replace("喜欢","想看"); System.out.println(str3); System.out.println(""); String str = "12hello34world5java7891mysql456"; //把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉 String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", ""); System.out.println(string); System.out.println(""); str = "12345"; //判断str字符串中是否全部有数字组成,即有1-n个数字组成 boolean matches = str.matches("\\d+"); System.out.println(matches); System.out.println(""); String tel = "0571-"; //判断这是否是一个杭州的固定电话 boolean result = tel.matches("0571-\\d{7,8}"); System.out.println(result); System.out.println(""); str = "hello|world|java"; String[] strs = str.split("\\|"); for (int i = 0; i < strs.length; i++) { 
    System.out.println(strs[i]); } System.out.println(); str2 = "hello.world.java"; String[] strs2 = str2.split("\\."); for (int i = 0; i < strs2.length; i++) { 
    System.out.println(strs2[i]); } } } 

三、String与char[]数组、byte[]数组的相互转换

package stringtest; import org.junit.Test; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.Arrays; / * @author 昭浅 * @create 2022/1/28-15:21 */ public class StringTest5 { 
    /* String 与 char[] 之间的转换 String --> char[]:调用String的toCharArray(); char[] --> String:调用String的构造器 */ @Test public void test(){ 
    String str1 ="abc123"; char[] charArray = str1.toCharArray(); for (int i = 0; i < charArray.length; i++) { 
    System.out.println(charArray[i]); } char[] arr = new char[]{ 
   'h','e','l','l','o'}; String str2 =new String(arr); System.out.println(str2);//hello } /* String 与 byte[] 之间的转换 编码:String --->byte[]:调用String的getBytes() 解码:byte[] --->String:调用String的构造器 编码:字符串-->字节(看得懂 --->看不懂的二进制数) 解码:字节--->字符串(看不懂的二进制数-->看得懂) 说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码 */ @Test public void test2() throws UnsupportedEncodingException { 
    String str1 = "abc123中国"; byte[] bytes = str1.getBytes();//使用默认的字符集进行转换 utf-8 System.out.println(Arrays.toString(bytes)); byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码 System.out.println(Arrays.toString(gbks)); System.out.println(""); String s = new String(bytes); System.out.println(s);//abc123中国 String s2 = new String(gbks); System.out.println(s2);//abc123�й� 乱码 解码用的是默认的字符集 //编码集和解码集不一致 String s3 = new String(gbks, "gbk"); System.out.println(s3); } } 

四、StringBuffer、StringBuilder类的使用

package stringtest; /关于StringBuffer、StringBuilder的使用 * * @author 昭浅 * @create 2022/2/10-15:24 */ public class StringBufferBulider { 
    /* String、StringBuffer、StringBuilder三者的异同 String:不可变的字符序列:底层使用char[]存储 StringBuffer:可变的字符序列:线程安全的,效率低,底层使用char[]存储 方法使用synchronized进行加锁处理 StringBuilder:可变的字符序列:jdk5.0新增 线程不安全的,效率高,底层使用char[]存储 源码分析: String str = new String(); //char[] value = new char[0]; String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'}; StringBuffer sb1= new StringBuffer(); //char[] value = new char[16]; 底层创建了一个长度是16的数组 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(sb1.length()); //0 //问题二:扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组 默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的素复制到新的数组中 指导意义:开发中建议大家使用:StringBuffer(int capacity)或StringBuilder(int capacity) 对比String、StringBuffer、StringBuilder三者的效率 从高到低:StringBuilder、StringBuffer、String */ } 

五、StringBuffer、StringBuilder类的常用方法

package stringtest; import org.junit.Test; / * @author 昭浅 * @create 2022/2/10-16:08 */ public class BufferBuildMethod { 
    /* StringBuffer的常用方法:(StringBuilder通用) 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 reveser():把当前字符序列逆转 public int indexOf(String str) public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串 public int length() public charAt(int n) public void setCharAt(int n,char ch) 总结: 增:append(xxx) 删:delete(int start,int end) 改:setCharAt(int n,char ch) / replace(int start,int end,String str) 查:charAt(int n) 插:insert(int offset,xxx) 长度:length() 遍历:for+charAt(int n) / toString() */ @Test public void test(){ 
    StringBuffer s1 = new StringBuffer("abc"); s1.append(1); s1.append("1"); System.out.println(s1); // s1.delete(3,5); // s1.replace(3,5,"def"); // s1.insert(2,"as"); // s1.reverse(); System.out.println(s1); String s2 = s1.substring(1, 3); System.out.println(s1); System.out.println(s1.length()); System.out.println(s2); } } 

六、jdk8之前日期和时间的API测试

package datetimetest; import org.junit.Test; import java.util.Date; / * jdk8之前日期和时间的API测试 * * @author 昭浅 * @create 2022/2/10-16:34 */ public class DateTimeTest { 
    //1.System类中的currentTimeMillis() //返回的是当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差 //时间戳 @Test public void test(){ 
    long time = System.currentTimeMillis(); System.out.println(time); } /* java.util.Date类 |----java.sql.Date类 1.两个构造器的使用 >构造器一:Date():创建一个对应当前时间的Date对象 >构造器二:创建指定毫秒数的Date对象 2.两个方法的使用 >toString():显示当前的年、月、日、时、分、秒 >getTime():获取当前Date对象对应的毫秒数(时间戳) 3.java.sql.Date:对应着数据库中的日期类型的变量 >如何实例化 >如何将java.util.Date对象转换为java.sql.Date对象 */ @Test public void test1(){ 
    //构造器一: Date():创建一个对应当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString());//Thu Feb 10 16:51:46 CST 2022 System.out.println(date1.getTime()); //15 //>构造器二:创建指定毫秒数的Date对象 Date date2 = new Date(15l); System.out.println(date2.toString()); //创建java.sql.Date对象 java.sql.Date date3 = new java.sql.Date(1L); System.out.println(date3);//1979-11-25 //如何将java.util.Date对象转换为java.sql.Date对象 //情况一 Date date4=new java.sql.Date(312L); java.sql.Date date5 = (java.sql.Date)date4; //情况二 Date date6=new Date(312L); // java.sql.Date date7 = (java.sql.Date)date6;//报错 java.sql.Date date7 =new java.sql.Date(date6.getTime());//正确 } } 
package datetimetest; import org.junit.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; / * jdk8 之前的日期时间的API测试 * 1.System类中的currentTimeMills(); * 2.java.util.Date和子类java.sql.Date * 3.SimpleDateFormat * 4.Calendar * * @author 昭浅 * @create 2022/2/12-16:17 */ public class DateTimeTest2 { 
    /* SimpleDateFormat的使用:SimpleDateFormat对日期Date类的格式化和解析 1.两个操作: 1.1 格式化:日期-->字符串 1.2 解析:格式化的逆过程:字符串-->日期 */ @Test public void testSimpleDateFormat() throws ParseException { 
    //实例化SimpleDateFormat SimpleDateFormat sdf = new SimpleDateFormat(); //格式化:日期 --->字符串 Date date = new Date(); System.out.println(date); String format = sdf.format(date); System.out.println(format); //解析:格式化的逆过程:字符串--->日期 String str = "22-2-12 下午4:22"; Date date1 = sdf.parse(str); System.out.println(date1); //按照指定的方式来格式化和解析:调用带参的构造器 SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //格式化 String format1=sdf1.format(date); System.out.println(format1); //解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器的参数体现) //否则,抛异常 Date date2 = sdf1.parse(format1); System.out.println(date2); } } 
package datetimetest; import org.junit.Test; import java.util.Calendar; import java.util.Date; / * Calendar日历类(抽象类)的使用 * * @author 昭浅 * @create 2022/2/12-17:02 */ public class DateTimeTest3 { 
    @Test public void testCalendar(){ 
    //1.实例化 //方式一:创建其子类(GregorianCalendar)的对象 //方式二:调用其静态方法getInstance() Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getClass()); //2.常用方法 //get() int days =calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); //set() calendar.set(Calendar.DAY_OF_MONTH,22); int days2 =calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days2); //add() calendar.add(Calendar.DAY_OF_MONTH,3); days=calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); //getTime():日历类-->Date Date date = calendar.getTime(); System.out.println(date); //setTime():Date--->日历类 Date date1=new Date(); calendar.setTime(date1); days=calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days); } } 

七、jdk8中日期和时间的API测试

package datetimetest; import javafx.util.converter.LocalDateStringConverter; import org.junit.Test; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Locale; / * jdk8 中时间日期API的使用 * @author 昭浅 * @create 2022/2/13-14:15 */ public class TimeTest { 
    /* LocalDate、LocalTime、LocalDateTim的使用 说明: 1.使用频率:LocalDateTime>LocalDate、LocalTime 2.类似于Calendar */ @Test public void test(){ 
    //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 localDateTime1 = LocalDateTime.of(2022, 2, 13, 14, 22); System.out.println(localDateTime1); //getXXX():获取相关属性 System.out.println(localDateTime.getDayOfMonth()); System.out.println(localDateTime.getDayOfWeek()); System.out.println(localDateTime.getMonth()); System.out.println(localDateTime.getMonthValue()); System.out.println(localDateTime.getMinute()); //体现不可变性 //withXxx():设置相关属性 LocalDate localDate1=localDate.withDayOfMonth(22); System.out.println(localDate); System.out.println(localDate1); //plusXxx():加相关属性 LocalDateTime localDateTime2 = localDateTime.plusMonths(3); System.out.println(localDateTime); System.out.println(localDateTime2); //minusXxx:减相关属性 localDateTime2 = localDateTime.minusMonths(2); System.out.println(localDateTime); System.out.println(localDateTime2); } } 
package datetimetest; import org.junit.Test; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.time.temporal.TemporalAccessor; / * @author 昭浅 * @create 2022/2/13-15:08 */ public class DateTimeFormatterTest { 
    /* DateTimeFormatter:格式化或解析日期、时间 类似于SimpleDateFormat */ @Test public void test(){ 
    // 方式一: 预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; //格式化:日期-->字符串 LocalDateTime localDateTime = LocalDateTime.now(); String str1 = formatter.format(localDateTime); System.out.println(localDateTime); System.out.println(str1); //解析:字符串-->日期 TemporalAccessor parse = formatter.parse("2022-02-13T15:15:12.182"); System.out.println(parse); // 方式二: 本地化相关的格式。如:ofLocalizedDateTime() // FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); //格式化 String format1 = formatter1.format(localDateTime); System.out.println(format1); // 本地化相关的格式。如:ofLocalizedDate() // FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); String str3 = formatter2.format(LocalDate.now()); System.out.println(str3); // 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”) DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); //格式化 String str4 = formatter3.format(LocalDateTime.now()); System.out.println(str4); //解析 TemporalAccessor parse1 = formatter3.parse("2022-02-13 03:25:47"); System.out.println(parse1); } } 

在这里插入图片描述

八、Java比较器

Comparable接口的使用

package compare; import org.junit.Test; import java.util.Arrays; / * 一、说明Java中的对象,正常情况下,只能进行比较 == 或!= 不能使用 > 或 < 的 * 但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小 * 如何实现? 使用两个接口中的任何一个:Comparable 和 Comparator * * 二、Comparable接口的使用 * * * 三、Comparable接口和Comparator接口的对比: * Comparable接口的方式一但指定,能够保证Comparable接口实现类的对象在任何位置都可以比较大小 * Comparator接口属于临时性的比较 * * @author 昭浅 * @create 2022/2/13-15:51 */ public class ComparableTest { 
    /* Comparable的使用举例: 自然排序 1.像String、包装类的能够实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象的方式 2.像String、包装类重写compareTo()方法以后,进行从小到大的排列 3.重写compareTo()的规则: 如果当前对象this大于形参对象obj,则返回正整数, 如果当前对象this小于形参对象obj,则返回负整数, 如果当前对象this等于形参对象obj,则返回零。 4.对于自定义类来说,如果需要排序,我们可以让自定一类实现Comparable接口,重写compareTo()方法 在compareTo(obj)方法中指明如何排序 */ @Test public void test(){ 
    String[] arrs = new String[]{ 
   "AA","CC","BB","EE","DD"}; Arrays.sort(arrs); System.out.println(Arrays.toString(arrs)); } @Test public void test2(){ 
    Goods[] goods = new Goods[5]; goods[0]=new Goods("xiaomi",23); goods[1]=new Goods("huawei",46); goods[2]=new Goods("oppo",12); goods[3]=new Goods("iphone",73); goods[4]=new Goods("vivo",43); Arrays.sort(goods); System.out.println(Arrays.toString(goods)); } } 
package compare; import org.junit.Test; import java.util.Arrays; / * @author 昭浅 * @create 2022/2/13-16:15 */ public class Goods implements Comparable{ 
    private String name; private double price; public Goods(String name, double price) { 
    this.name = name; this.price = price; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public double getPrice() { 
    return price; } public void setPrice(double price) { 
    this.price = price; } @Override public String toString() { 
    return "Goods{" + "name='" + name + '\'' + ", price=" + price + '}'; } //指明商品比较大小的方式:按照价格从低到高的方式排序 @Override public int compareTo(Object o) { 
    if(o instanceof Goods){ 
    Goods goods=(Goods) o; if(this.getPrice()>goods.getPrice()){ 
    return 1; }else if (this.getPrice()<goods.getPrice()){ 
    return -1; }else{ 
    return 0; } //方式二: // return Double.compare(this.getPrice(),goods.getPrice()); } return 0; } } 

Comparator接口的使用

package compare; import org.junit.Test; import java.util.Arrays; import java.util.Comparator; / * * @author 昭浅 * @create 2022/2/13-17:26 */ public class ComparatorTest{ 
    /* Comparator接口的使用:定制排序 1.背景: 当素的类型没有实现java.lang.Comparable接口而又不方便修改代码, 或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那 么可以考虑使用 Comparator 的对象来排序 2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小: 如果方法返回正整数,则表示o1大于o2; 如果返回0,表示相等; 返回负整数,表示o1小于o2。 */ @Test public void test(){ 
    String[] arrs = new String[]{ 
   "AA","CC","BB","EE","DD"}; Arrays.sort(arrs,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(arrs)); } @Test public void test2(){ 
    Goods[] goods = new Goods[6]; goods[0]=new Goods("xiaomi",23); goods[1]=new Goods("huawei",46); goods[2]=new Goods("oppo",12); goods[3]=new Goods("iphone",73); goods[4]=new Goods("vivo",43); goods[5]=new Goods("oppo",342); Arrays.sort(goods, new Comparator(){ 
    //指明商品比较大小的方式:按照产品名称从低到高的方式排序,再按照价格从高到低排序 @Override public int compare(Object o1, Object o2) { 
    if(o1 instanceof Goods && o2 instanceof Goods){ 
    Goods g1 = (Goods) o1; Goods g2 = (Goods) o2; if(g1.getName().equals(g2.getName())){ 
    return -Double.compare(g1.getPrice(), g2.getPrice()); }else { 
    return g1.getName().compareTo(g2.getName()); } } // return 0; throw new RuntimeException("输入的类型不一致"); } }); System.out.println(Arrays.toString(goods)); } } 

九、其他常用类

package otherclass; import org.junit.Test; import java.math.BigDecimal; import java.math.BigInteger; / * 其他常用类的使用: * 1.System * 2.Math * 3.BigInteger与BigDecimal * @author 昭浅 * @create 2022/2/13-18:59 */ public class OtherClass { 
    //1.System @Test public void systemTest(){ 
    String javaVersion = System.getProperty("java.version"); System.out.println("java的version:" + javaVersion); String javaHome = System.getProperty("java.home"); System.out.println("java的home:" + javaHome); String osName = System.getProperty("os.name"); System.out.println("os的name:" + osName); String osVersion = System.getProperty("os.version"); System.out.println("os的version:" + osVersion); String userName = System.getProperty("user.name"); System.out.println("user的name:" + userName); String userHome = System.getProperty("user.home"); System.out.println("user的home:" + userHome); String userDir = System.getProperty("user.dir"); System.out.println("user的dir:" + userDir); } //2.Math @Test public void mathTest(){ 
    } //3.BigInteger与BigDecimal @Test public void bigTest(){ 
    BigInteger b1=new BigInteger(""); BigDecimal bd1 = new BigDecimal(".213"); BigDecimal bd2 = new BigDecimal("11"); System.out.println(b1); // System.out.println(bd.divide(bd2)); System.out.println(bd1.divide(bd2, BigDecimal.ROUND_HALF_UP));//BigDecimal.ROUND_HALF_UP四舍五入方式 System.out.println(bd1.divide(bd2, 15, BigDecimal.ROUND_HALF_UP)); } } 

第三章 枚举类和注释

一、枚举类

1、枚举类的使用

package enumtest; / * 一、枚举类的使用 * 1.枚举类的理解:累的对象只有限个,确定的,我们称此为枚举类 * 2.当需要定义一组常量时,强烈建议使用枚举类 * 3,如果枚举类中只有一个对象,则可以作为单例模式的实现方式 * <p> * 二、如何定义枚举类 * 方式一:jdk5.0之前,自定义枚举类 * 方式二:jdk5..0,可以使用enum关键字定义枚举类 * * @author 昭浅 * @create 2022/2/14-16:41 */ public class EnumTest { 
    public static void main(String[] args) { 
    Season s = Season.AUTUMN; System.out.println(s.toString()); } } //自定义枚举类 class Season { 
    //1.声明Season对象的属性:使用private final修饰 private final String seasonName; private final String seasonDec; //2.私有化类的构造器,不可再类的外部主动建立对象 private Season(String seasonName, String seasonDec) { 
    this.seasonName = seasonName; this.seasonDec = seasonDec; } //3.提供当前枚举类的对个对象 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.其他诉求:获取枚举类对象的属性 public String getSeasonName() { 
    return seasonName; } public String getSeasonDec() { 
    return seasonDec; } //可选择是否提供toString() @Override public String toString() { 
    return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDec='" + seasonDec + '\'' + '}'; } } 
package enumtest; import java.util.Arrays; / * 方式二:jdk5..0,使用enum关键字定义枚举类 * 一、 说明:定义的枚举类默认继承于class java.lang.Enum * 二、Enum类的主要方法: * values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的 * 枚举值。 * valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符 * 串必须是枚举类对象的“名字”。如不是,会有运行时异常: * IllegalArgumentException。 * toString():返回当前枚举类对象常量的名称 * 三、使用enum关键字定义的枚举类实现接口的情况 * 情况一:实现接口,在enumm类中实现抽象方法 * 情况二:让枚举类的对象分别实现接口中的抽象方法 * * @author 昭浅 * @create 2022/2/14-16:56 */ public class EnumTest2 { 
    public static void main(String[] args) { 
    Season1 spring = Season1.SPRING; //toString() System.out.println(spring.toString());//SPRING 没有重写toString() System.out.println(Season1.class.getSuperclass()); System.out.println(""); //values() Season1[] values = Season1.values(); System.out.println(Arrays.toString(values)); //valueOf(String objName):返回枚举类中对象名是objName的对象 Season1 winter = Season1.valueOf("WINTER"); System.out.println(winter); spring.show(); } } interface InFo{ 
    public abstract 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("冬天"); } }; //2.声明Season对象的属性:使用private final修饰 private final String seasonName; private final String seasonDec; //3.私有化类的构造器,不可再类的外部主动建立对象 private Season1(String seasonName, String seasonDec) { 
    this.seasonName = seasonName; this.seasonDec = seasonDec; } //4.其他诉求:获取枚举类对象的属性 public String getSeasonName() { 
    return seasonName; } public String getSeasonDec() { 
    return seasonDec; } //可选择是否提供toString(),一般时不去重写 // @Override // public String toString() { 
    // return "Season{" + // "seasonName='" + seasonName + '\'' + // ", seasonDec='" + seasonDec + '\'' + // '}'; // } @Override public void show() { 
    System.out.println("这是一个季节"); } } 

二、注释

package notetest; import org.junit.Test; import sun.plugin2.message.ShowDocumentMessage; import java.lang.annotation.Annotation; import java.lang.annotation.Target; import java.util.Arrays; / * 注解的使用 * * 1.理解Annotation * ① jdk5.0新增的功能 * * ② Annotation 其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取 * 并执行相应的处理,通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中 * 嵌入一些补充信息 * * ③在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等,在JavaEE/Android * 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码 * 和XML配置等 * 2.Annotation的使用实例: * 示例一:生成文档相关的注解: * * 示例二:在编译时进行格式检查(JDK内置的三个基本注解) * >@Override:限定重写父类方法,该注解只能用于方法 * >@Deprecated:用于表示所修饰的素(类、方法等)已过时,通常是因为所修饰的结构危险或存在更好的选择 * >@SuppressWarnings: 抑制编译器警告 * * * 3.如何自定义注解:参照@SuppressWarnings定义 * ①注解声明为:@interface * ②内部定义成员:通常用value表示 * ③可以指定成员的默认值,使用default定义 * ④如果自定义的注解没有成员,表明是一个标识作用 * 如果注解有成员,在使用注解时,需要指明成员的值 * 自定义注解必须配上注解的信息处理流程(使用反射)才有意义 * 自定义注解通过都会指明两个注解:Retention、Target * * 4.jdk提供的四种注解: * 注解:对现有注解进行解释说明的注解 * Retention:指定所修饰的Annotation的生命周期:SOURCE、CLASS(默认行为)、RUNTIME * 只有声明为RUNTIME生命周期的注解,才能通过反射获取 * Target:用于指定被修饰的Annotation,能用于修饰哪些程序素 * * 出现频率较低注解 * Documented:表示所修饰的注解在被javadoc解析时,保留下来,例如:Deprecated * Inherited:被他修饰的Annotation将具有继承性 * 5.通过反射获取注解信息---- * * 6.jdk 8 中注解的新特性:可重复注解、类型注解 * * 6.1 可重复注解:① 在MyAnnotation上声明Repeatable,成员值为MyAnnotations.class * ② MyAnnotation的Target和Retention与MyAnnotations的要相同 * 6.2 类型注解: * ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中 * ElementType.TYPE_USE 表示该注解能卸载使用类型的任何语句中 * @author 昭浅 * @create 2022/2/15-15:28 */ public class AnnotationTest { 
    //测试子类Student有没有继承父类Person的注释 @Test public void testInheritedAnnotation(){ 
    Class c = Student.class; Annotation[] annotations = c.getAnnotations(); System.out.println(Arrays.toString(annotations)); } } //jdk8之前可重复注解 //@MyAnnotations({@MyAnnotation(value="hi"),@MyAnnotation(value="hello")}) //jdk8 可重复 @MyAnnotation(value="hi")//自定义注解 @MyAnnotation(value="hi") class Person{ 
    private String name; private int age; public Person(String name, int age) { 
    this.name = name; this.age = age; } public Person() { 
    } public void walk(){ 
    System.out.println("人走路"); } public void eat(){ 
    System.out.println("人吃饭"); } @SuppressWarnings("unused") int num; } interface Info{ 
    void show(); } class Student extends Person implements Info{ 
    @Override public void walk() { 
    super.walk(); } @Override public void show() { 
    } } 

自定义注解

package notetest; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; / * @author 昭浅 * @create 2022/2/16-14:06 */ @Repeatable(MyAnnotations.class) @Retention(RetentionPolicy.RUNTIME)//表示注解的“生命周期” @Target({ 
   TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})//表示该自定义注释可修饰的素 @Inherited//表示该自定义注释可被继承 @interface MyAnnotation { 
    String value() default "hello";//defult表示在使用注解时,需要指明成员的值,当成员变量没有赋值时,默认值为defult的 } 

可重复注解

package notetest; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; / * @author 昭浅 * @create 2022/2/16-15:20 */ @Retention(RetentionPolicy.RUNTIME)//表示注解的“生命周期” @Target({ 
   TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})//表示该自定义注释可修饰的素 @Inherited//表示该自定义注释可被继承 public @interface MyAnnotations { 
    MyAnnotation[] value(); } 

第三章 集合

package collectiontest; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; import java.util.ArrayList; import java.util.Collection; import java.util.Date; /一、集合框架的概述 * * 1.集合、数组都是对多个数据进行存储操作的结构,简称Java容器 * 说明:此时的存储,主要指的都是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中) * * 2.1 数组在存储多个数据方面的特点 * > 一旦初始化以后,其长度就确定了 * > 数组一旦定义好,其素的类型也就确定了,我们也就只能操作指定类型的数据, * 比如:String[] arr;int[] arr1,Object[] obj * 2.2 数组在存储多个数据方面的缺点 * > 一旦初始化以后,其长度就不可更改 * > 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高 * > 获取数据中实际素的个数的需求,数组没有现成的属性或方法可用 * > 数组存储数据的特点:有序、可重复,对于无序、不可重复的需求,不能满足 * * 二、集合框架 * |-----Collection接口:单列集合,用来存储一个一个的对象 * |-----list接口:存储有序的、可重复的数据 -->"动态"数组 * |---ArrayList、LinkedList、Vector * |-----set接口:存储无序的、不可重复的数据 --->高中所讲的“集合” * |---HashSet、LinkedHashSet、TreeSet * |------Map接口:双列集合,用来存储一对(key-value)一对的数据,类似于Python的字典 * |-----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties * * * 三、Collection接口中方法的使用 * @author 昭浅 * @create 2022/2/16-16:20 */ public class CollectionTest { 
    @org.junit.Test public void test(){ 
    Collection coll = new ArrayList(); //add(Object e):将素e添加到集合coll中 coll.add("abc"); coll.add(123); coll.add(new Date()); //size():返回添加的素的个数 System.out.println(coll.size()); //addAll(Collection c) 将coll1集合中的素添加到当前的集合中 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("def"); coll.addAll(coll1); System.out.println(coll); //clear():清除集合素 coll.clear(); // isEmpty():判断集合是否为空 System.out.println(coll.isEmpty()); } } 

Collection接口的常用方法

package collectiontest; import org.junit.Test; import java.util.*; / * Collection接口中声明的方法的测试 * * 要求: * 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals() * 才能使用remove()、contains()等方法 * @author 昭浅 * @create 2022/2/16-17:56 */ public class CollectionTest2 { 
    @Test public void test() { 
    Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); //1.contains(object obj):判断当前集合中是否包含obj //我们在判断时,我们会调用obj对象所在类的equals() System.out.println(coll.contains(123));//true System.out.println(coll.contains(new String("Tom")));//true //当比较对象是否在集合中时,会调用该对象的equals()方法,如果该对象所在的类没有重写equals() //就会调用其父类Object中的equals()来比较其地址 System.out.println(coll.contains(new Person("Jerry",15)));//true //2.containsAll(Collection coll1):盘对形参coll1中的所有素是否都存在于当前集合中 Collection coll1= Arrays.asList(123,456); System.out.println(coll.containsAll(coll1)); } @Test public void test2(){ 
    //3.remove(Object obj):从当前集合中移除obj素,返回值为布尔型 Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); coll.remove(123); System.out.println(coll); coll.remove(new Person("Jerry",15)); System.out.println(coll); //4.removeAll(Collection coll1):差集:从当前集合中移除coll1中的所有素 Collection coll1=Arrays.asList(1234,456); coll.removeAll(coll1); System.out.println(coll); } @Test public void test3(){ 
    Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); //retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合 // Collection coll1=Arrays.asList(123,456,789); // coll.retainAll(coll1); // System.out.println(coll); // //equals(Object obj):比较当前集合与形参集合的异同 Collection coll2 =new ArrayList();//ArrayList有序集合,集合素调换位置时,返回false coll2.add(123); coll2.add(456); coll2.add(new String("Tom")); coll2.add(new Person("Jerry",15)); coll.equals(coll2); System.out.println(coll); } @Test public void test4(){ 
    Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); //7.hashCode():返回当前对象的哈希值 System.out.println(coll.hashCode()); //8.集合--->数组:toArray() Object[] arr = coll.toArray(); System.out.println(Arrays.toString(arr)); //拓展:数组--->集合:调用Arrays类的静态方法asList() List list = Arrays.asList(new String[]{ 
   "AA", "BB", "CC"}); System.out.println(list); List list1 =Arrays.asList(new int[]{ 
   123,456}); //当转换的数组为int等基本数据类型时,会将该数组视为一个素 System.out.println(list1);//[[I@3d82c5f3] System.out.println(list1.size());//1 List list2=Arrays.asList(new Integer[]{ 
   123,456}); System.out.println(list2); } } class Person { 
    private String name; private int age; public Person() { 
    } public Person(String name, int age) { 
    this.name = name; this.age = age; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return age; } public void setAge(int age) { 
    this.age = age; } @Override public boolean equals(Object o) { 
    if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { 
    return Objects.hash(name, age); } @Override public String toString() { 
    return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } 

集合的遍历:Iterator接口

package collectiontest; import jdk.nashorn.internal.runtime.arrays.IteratorAction; import org.junit.Test; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; / * 集合素的遍历操作:使用迭代器Iterator接口 * 1.内部的方法:hasNext()、Next() * 2.结合对象每次调用iterator()方法都会得到一个全新的迭代器对象 * 默认游标都在集合的第一个素之前 * 3.内部定义了remove(),可以在遍历的时候,删除集合中的素,此方法不同于集合,直接调用remove() * * 测试Iterator接口中remove()方法: * 注意: * 如果还未调用next()或在上一次调用 next() 方法之后已经调用了remove()方法 * 在调用remove()都会报错:IllegalStateException * * 快速失败机制: * * @author 昭浅 * @create 2022/2/17-14:50 */ public class IteratorTest { 
    @Test public void test(){ 
    Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); Iterator iterator = coll.iterator();//每当调用iterator(),都会创建一个新的Iterator对象 //方式一 System.out.println(iterator.next());//123 System.out.println(iterator.next());//456 System.out.println(iterator.next());//Tom System.out.println(iterator.next());//Person{name='Jerry', age=15} //报异常:NoSuchElementException // System.out.println(iterator.next());//123 //方式二:不推荐 // for (int i=0;i<coll.size();i++){ 
    // System.out.println(iterator.next()); // } //方式三:推荐 while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } } //错误遍历方式: @Test public void test2(){ 
    Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); Iterator iterator = coll.iterator(); //错误一: // while (iterator.next()!=null){ 
    // System.out.println(iterator.next()); // } //错误二: // while (coll.iterator().hasNext()){ 
    // System.out.println(coll.iterator().next()); // } } //测试Iterator接口中的remove() @Test public void test3(){ 
    Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); Iterator iterator = coll.iterator(); while (iterator.hasNext()){ 
    Object obj = iterator.next(); if("Tom".equals(obj)){ 
    iterator.remove(); } } iterator=coll.iterator(); while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } } } 

jdk5.0 新增foreach循环,用于遍历集合、数组

package collectiontest; import org.junit.Test; import java.util.ArrayList; import java.util.Collection; / * jdk5.0 新增foreach循环,用于遍历集合、数组 * 1.for(集合中素的类型 局部变量 : 集合对象) * 2.for(数组素的类型 局部变量 : 数组对象) * 3.forEach(System.out::println):forEach()方法 * * 注意:foreach循环是将对象每一个素的值分别赋给局部变量并操作, * 而不是操作对象内素值本身 * @author 昭浅 * @create 2022/2/17-15:39 */ public class ForTest { 
    @Test public void test(){ 
    Collection coll =new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(new Person("Jerry",15)); //for(集合中素的类型 局部变量 : 集合对象) for(Object obj : coll){ 
   //内部调用的还是迭代器Interator System.out.println(obj); } } @Test public void test2(){ 
    int[] arr=new int[]{ 
   1,2,3,4,5,6}; //for(数组素的类型 局部变量 : 数组对象) for(int i : arr){ 
    System.out.println(i); } } } 

List接口的使用

package collectiontest; import org.junit.Test; import java.util.*; / * 1.List接口框架 * |-----Collection接口:单列集合,用来存储一个一个的对象 * |-----list接口:存储有序的、可重复的数据 -->"动态"数组,替换原有数组 * |---ArrayList:作为List接口的主要实现类:线程不安全的,效率高;底层使用Object[] elementData存储 * |---LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高,线程不安全,底层使用双向链表存储 * |---Vector :作为List接口的古老实现类:线程安全的,效率低;底层使用object[] elementData存储 * * * 2.ArrayList的源码分析: * 2.1 jdk 7情况下 * ArrayList list =new ArrayList();//底层创建了长度是10的Object[] 数组elementData * list.add(123);//elementData[0] = new Integer(123); * ... * list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容 * 默认情况下。扩容为原来的容量的1.5倍,同时需要将原有数组中的数组复制得到新的数组中 * * 结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity) * * 2.2 jdk 8 中ArrayList的变化 * ArrayList list = new ArrayList();//底层Object[] elementData初始化为{} 并没有创建长度为10 的数组 * * list.add(123);//第一次add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0] * ... * 哦后续的添加和扩容操作与jdk 7 无异 * * 2.3 小结:jdk7这种的ArrayList的对象创建类似于单例的饿汉式,而jdk 8 中的ArrayList的对象 * 的创建类似于单例的懒汉式,延迟了数组的创建,节省内存 * * * 3.LinkedList的源码分析 * LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null 线程不安全 * list.add(123);//将123封装到Node中,创建了Node对象 * * 其中,Node定义为:体现了LinkedList的双向链表的说法 * private static class Node<E> { * E item; * Node<E> next; * Node<E> prev; * * Node(Node<E> prev, E element, Node<E> next) { * this.item = element; * this.next = next; * this.prev = prev; * } * } * 4.Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象是。底层都创建了长度为10的数组 * 在扩容方面,默认扩容为原来的数组长度的2倍 * * 5.Lisk接口的常用方法: * void add(int index, Object ele):在index位置插入ele素 * boolean addAll(int index, Collection eles):从index位置开始将eles中 * 的所有素添加进来 * Object get(int index):获取指定index位置的素 * int indexOf(Object obj):返回obj在集合中首次出现的位置 * int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 * Object remove(int index):移除指定index位置的素,并返回此素 * Object set(int index, Object ele):设置指定index位置的素为ele * List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex * 位置的子集合 * 总结:常用方法 * 增:add() * 删:remove(int index)/remove(Object obj) * 改:set(int index, Object ele) * 查:get(int index) * 插:add(int index, Object ele) * 长度:size() * 遍历:Iterator迭代器、曾江for循环、不同循环 * 面试题:ArrayList、LinkedList、Vector三者的异同? * 同:三个类都是事先了list接口,存储数据的特点相同:存储有序的、可重复的数据 * 不同: * * @author 昭浅 * @create 2022/2/17-16:05 */ public class ListTest { 
    @Test public void test(){ 
    ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); list.add(new Person("TOm",14)); System.out.println(list); //add(int index, Object ele) list.add(1,"BB"); System.out.println(list); //addAll(int index, Collection eles) List list1 = Arrays.asList(1, 2, 3); list.addAll(list1); System.out.println(list.size());//8 // list.add(list1);//7 add()方法会将集合list1看成是一个素 //get(int index) System.out.println(list.get(0)); } @Test public void test2(){ 
    ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); list.add(new Person("TOm",14)); //indexOf(Object obj):如果不存在,则返回-1 int index = list.indexOf(456); System.out.println(index); //lastIndexOf(Object obj) System.out.println(list.lastIndexOf("AA")); //remove(int index):返回删除的素, Object obj = list.remove(0); System.out.println(obj); System.out.println(list); //set(int index, Object ele) list.set(2,"cc"); //subList(int fromIndex, int toIndex) List list1 = list.subList(1, 3); System.out.println(list1); System.out.println(list); } @Test public void test3(){ 
    ArrayList arrayList = new ArrayList(); arrayList.add(1); arrayList.add("AA"); arrayList.add(3); //方式一:Iterator迭代器 Iterator iterator = arrayList.iterator(); while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } //方式二:foreach循环 for(Object obj : arrayList){ 
    System.out.println(obj); } //方式三:普通for循环 for (int i = 0; i < arrayList.size(); i++) { 
    System.out.println(arrayList.get(i)); } } } 

Set接口的使用

package collectiontest2; import collectiontest.IteratorTest; import jdk.nashorn.internal.runtime.arrays.IteratorAction; import org.junit.Test; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; / * 1.Set接口的框架: * |-----Collection接口:单列集合,用来存储一个一个的对象 * |-----set接口:存储无序的、不可重复的数据 --->高中所讲的“集合” * |---HashSet:作为Set接口的主要实现类,线程不安全的,可以存储null值 * |---LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历 * LinkedHashSet效率高于HashSet * |---TreeSet:可以按照添加对象的指定属性进行排序 * * 2.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法 * * 3.要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()方法,这样不同类型的对象才能进行比较 * 要求:重写的hashCode()和equals()尽可能要有一致性:相等的对象必须拥有相等的散列码 * 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() * 方法的返回值也应相等。 * 对象中用作 equals() 方法比较的 Field(属性),都应该用来计算 hashCode 值 * * HashSet的底层:数组+链表的结构 * @author 昭浅 * * @create 2022/2/20-10:36 */ public class SetTest { 
    /* 一、Set:存储无序的、不可重复的数据 以HashSet为例: 1.无序性:不等于随机性,存储的数据在底层数据中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的 2.不可重复性:保证添加的素按照equals()判断时,不能返回true,即:相同的素只能添加一个 二、添加素的过程:以HashSet为例: 我们向HashSet中添加素a,首先调用素a所在类的hashCode()方法,计算素a的哈希值 此哈希值急着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置) 判断数组此位置上是否已经有素: 如果此位置上没有其他素,则素a添加成功 --->情况一 如果此位置上有其他素b(或以链表形式存在的多个素),则比较素a与素b(或以链表形式存在的多个素)的hash值 如果hash值不相同,则素a添加成功 --->情况二 如果hash值相同,进而需要调用素a所在类的equals()方法 equals()返回true,素a添加失败 equals()返回false,素添加成功 --->情况三 对于添加成功的情况2和情况3而言:素a 与已经存在指定索引位置上的数据以链表的方式存储 jdk 7:素a放到数组中,指向原来的素 jdk 8:原来的素在数组中,指向素a 总结:七上八下 */ @Test public void test(){ 
    Set set=new HashSet(); set.add(111); set.add("AA"); set.add(new User("Tom",12)); set.add(new User("Tom",12)); Iterator iterator=set.iterator(); while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } } //LinkedHashSet的使用 //LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数组还维护了两个引用,记录此数据前一个和后一个数据 //优点:对于频繁的便利操作,LinkedHashSet效率高于HashSet @Test public void test2(){ 
    Set set=new LinkedHashSet(); set.add(111); set.add("AA"); set.add(new User("Tom",12)); set.add(new User("Tom",12)); Iterator iterator=set.iterator(); while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } } } class User{ 
    private String name; private int age; public User(String name, int age) { 
    this.name = name; this.age = age; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return age; } public void setAge(int age) { 
    this.age = age; } @Override public boolean equals(Object o) { 
    if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (age != user.age) return false; return name != null ? name.equals(user.name) : user.name == null; } @Override public int hashCode() { 
    int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } @Override public String toString() { 
    return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } } 

TreeSet实现类的使用

package collectiontest2; import jdk.nashorn.internal.runtime.arrays.IteratorAction; import org.junit.Test; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; / * @author 昭浅 * @create 2022/2/20-14:30 */ public class TreeSetTest { 
    /* 1.向TreeSet当中添加的数据,要求是相同类的对象 2.两种排序:自然排序(实现Comparable接口) 和 定制排序(Comparator) 3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals() */ @Test public void test(){ 
    TreeSet set=new TreeSet(); //不能添加不同类的对象 // set.add(121); // set.add("AA"); // set.add(311); // set.add(31); //举例二: // set.add(121); // set.add(131); // set.add(311); // set.add(31); // // Iterator iterator=set.iterator(); // while (iterator.hasNext()){ 
    // System.out.println(iterator.next()); // } //举例二: set.add(new User("Tom",42)); set.add(new User("Joker",43)); set.add(new User("Kuga",33)); set.add(new User("Decade",33)); set.add(new User("Decade",13)); Iterator iterator=set.iterator(); while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } } @Test public void test2(){ 
    //按照年龄从小到大排序 Comparator comparator = new Comparator() { 
    @Override public int compare(Object o1, Object o2) { 
    if(o1 instanceof User && o2 instanceof User){ 
    User u1 =(User) o1; User u2 =(User) o2; return Integer.compare(u1.getAge(),u2.getAge()); } else{ 
    throw new RuntimeException("数据类型不一致"); } } }; TreeSet set=new TreeSet(comparator); set.add(new User("Tom",42)); set.add(new User("Joker",43)); set.add(new User("Kuga",43)); set.add(new User("Decade",33)); set.add(new User("Decade",13)); Iterator iterator=set.iterator(); while (iterator.hasNext()){ 
    System.out.println(iterator.next()); } } } 

Map接口的使用

HashMap实现类

package maptest; / * |----Map:双列数据,存储key-value对的数据 --->类似于高中的函数:y=f(x) * |----HashMap:作为Map的主要实现类,线程不安全的,效率高,存储null的key和value * |----LinkedHashMap:保证在遍历Map素时,可以按照添加的顺序实现遍历 * 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个素 * 对于频繁的便利操作,此类执行效率高于HashMap * |----TreeMap:保证按照添加的kry-value对进行排序,实现排序遍历,此时使用key来进行自然排序和定制排序 * 底层使用红黑树:http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html * |----Hashtable:作为古老的实现类,线程安全的,效率低,不能存储null的key和value * |----Properties:常用来处理配置文件,key和value都是String类型 * * HashMap的底层:数组+链表 (jdk 7 及之前) * 数组+链表+红黑树 (jdk 8) * * 面试题: * 1.HashMap的底层实现原理? * 2.HashMap 和 Hashtable 的异同 * 3.CurrentHashMap 与 HashMap 的异同? * * 二、Map结构的理解: * Map中的key:无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写equals()和hashCode()(以HashMap为例) * Map中的value:无序的、可重复的,使用Collection存储所有的value ---> value所在的类要重写equals() * 一个键值对:key-value构成一个Entry对象 * Map中的entry:无序的、不可重复的,使用Set存储所有的entry * * 三、HashMap的底层实现原理:以jdk 7 为例说明: * HashMap map = new HashMap(); * 在实例化以后,底层创建了长度是16的一维数组Entry[] table * ...可能已经执行过多次put()... * map.put(key1,value1); * 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置 * 如果此位置上的数据为空,此时的key1-value1添加成功 ----情况1 * 如果此位置上的数据不为空,(意味值此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据 * 的哈希值: * 如果key1的哈希值与已经存在的数据哈希值都不相同,此时key-value添加成功 ----情况2 * 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较,调用key1所在类的equlas(key2) * 如果equals()返回false:此时key1-value添加成功 ---情况3 * 如果equals()返回true:使用value1替换value2 * * 补充:关于情况2和情况3:在添加键值对时的位置已有数据的话,此时的key1-value1和原来的数据以链表的方式存储 * * 在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空时),扩容,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来 * * DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16 * DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75 * threshold:扩容的临界值,=容量*填充因子 16*0.75=12 * TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树 * MIN_TREEIFY_CAPACITY:桶中Node被树化时最小的hash表容量 * * jdk8 相较于 jdk7 在底层实现方面的不同: * 1.new HashMap(); 底层没有创建一个长度为16的数组 * 2.jdk 8底层的数组是 Node[] ,而非Entry[] * 3.首次调用put()方法时,底层创建长度为16的数组 ---类似于ArrayList * 4.jdk7 底层结构只有:数组+链表,jdk 8中底层结构:数组+链表+红黑树 * 4.1 形成链表时,七上八下(jdk7:新的素指向旧的素 jdk8:旧的素指向新的素) * 4.2 当数组的某一个索引位置上的素以链表的形式存在的数据个数 > 8 且当前数组的长度 > 64时 * 此时此索引位置上的所有数据改为使用红黑树存储 * * 5.HashMap在1.8中引入了红黑树,大大提高了查询效率,对树的遍历查找效率高于链表 * * * * * @author 昭浅 * @create 2022/2/21-12:46 */ public class MapTest { 
    } 

Map常用方法

package maptest; import org.junit.Test; import java.util.*; / Map接口的方法 * 添加、删除、修改操作: * Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中 * void putAll(Map m):将m中的所有key-value对存放到当前map中 * Object remove(Object key):移除指定key的key-value对,并返回value * void clear():清空当前map中的所有数据 * 素查询的操作: * Object get(Object key):获取指定key对应的value * boolean containsKey(Object key):是否包含指定的key * boolean containsValue(Object value):是否包含指定的value * int size():返回map中key-value对的个数 * boolean isEmpty():判断当前map是否为空 * boolean equals(Object obj):判断当前map和参数对象obj是否相等 * 视图操作的方法: * Set keySet():返回所有key构成的Set集合 * Collection values():返回所有value构成的Collection集合 * Set entrySet():返回所有key-value对构成的Set集合 * * * 总结: * 增:put(Object key,Object value) * 删:remove(Object key) * 改:put(Object key,Object value) * 查:get(Object key) * 长度:size() * 遍历:keySet() / values() / entrySet() * @author 昭浅 * @create 2022/2/27-15:44 */ public class MapMethod { 
    /* 添加、删除、修改操作: * Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中 * void putAll(Map m):将m中的所有key-value对存放到当前map中 * Object remove(Object key):移除指定key的key-value对,并返回value * void clear():清空当前map中的所有数据 */ @Test public void test1(){ 
    HashMap map=new HashMap(); //添加:put(Object key,Object value) map.put("AA",123); map.put("45",123); map.put("BB",1213); //修改:put(Object key,Object value) map.put("AA",13); System.out.println(map); HashMap map1=new HashMap(); map1.put("CC",123); map1.put("DD",123); //putAll(Map m) map.putAll(map1); System.out.println(map); //remove(Object key) map.remove("45"); System.out.println(map); //clear() map.clear(); System.out.println(map); System.out.println(map.size()); } /* 素查询的操作: * Object get(Object key):获取指定key对应的value * boolean containsKey(Object key):是否包含指定的key * boolean containsValue(Object value):是否包含指定的value * int size():返回map中key-value对的个数 * boolean isEmpty():判断当前map是否为空 * boolean equals(Object obj):判断当前map和参数对象obj是否相等 */ @Test public void test2(){ 
    HashMap map=new HashMap(); map.put("AA",123); map.put("45",123); map.put("BB",1213); //get(Object key):获取指定key对应的value System.out.println(map.get("AA")); //containsKey(Object key):是否包含指定的key System.out.println(map.containsKey("AA")); //containsValue(Object value):是否包含指定的value System.out.println(map.containsValue(123)); //isEmpty():判断当前map是否为空 System.out.println(map.isEmpty()); } /* 视图操作的方法: * Set keySet():返回所有key构成的Set集合 * Collection values():返回所有value构成的Collection集合 * Set entrySet():返回所有key-value对构成的Set集合 */ @Test public void test3(){ 
    HashMap map=new HashMap(); map.put("AA",123); map.put("45",123); map.put("BB",1213); //遍历所有key集:keySet() Set set = map.keySet(); Iterator iterator=set.iterator(); while (iterator.hasNext()) { 
    System.out.println(iterator.next()); } System.out.println(); //遍历所有value集:values() Collection values = map.values(); Iterator iterator1 = values.iterator(); while (iterator1.hasNext()) { 
    System.out.println(iterator1.next()); } //遍历所有key-value:entrySet() //方式一 Set entrySet = map.entrySet(); Iterator iterator2 = entrySet.iterator(); while (iterator2.hasNext()) { 
    Object obj = iterator2.next(); Map.Entry entry=(Map.Entry)obj; System.out.println(entry.getKey()+"=="+entry.getValue()); } // 方式二 // Set set = map.keySet(); iterator = set.iterator(); while (iterator.hasNext()) { 
    Object key=iterator.next(); Object value=map.get(key); System.out.println(key+"==="+value); } } } 

TreeMap实现类

package maptest; import org.junit.Test; import java.util.Comparator; import java.util.TreeMap; / * @author 昭浅 * @create 2022/2/27-17:07 */ public class TreeMapTest { 
    /* 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象 因为要按照key进行排序: 自然排序、定制排序 */ //自然排序:按名字大<-小 @Test public void test(){ 
    TreeMap map = new TreeMap(); User u1=new User("Tom",13); User u2=new User("Jerry",13); User u3=new User("Li",13); User u4=new User("Joker",13); map.put(u1,14); map.put(u2,14); map.put(u3,14); map.put(u4,14); System.out.println(map); } //定制排序:按年龄大->小 @Test public void test2(){ 
    Comparator comparator =new Comparator() { 
    @Override public int compare(Object o1, Object o2) { 
    if (o1 instanceof User && o2 instanceof User) { 
    User u1 =(User)o1; User u2 =(User)o2; return -Integer.compare(u1.getAge(),u2.getAge()); } return 0; } }; TreeMap map = new TreeMap(comparator); User u1=new User("Tom",15); User u2=new User("Jerry",10); User u3=new User("Li",33); User u4=new User("Joker",13); map.put(u1,14); map.put(u2,14); map.put(u3,14); map.put(u4,14); System.out.println(map); } } class User implements Comparable{ 
    private String name; private int age; public User(String name, int age) { 
    this.name = name; this.age = age; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return age; } public void setAge(int age) { 
    this.age = age; } @Override public boolean equals(Object o) { 
    if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (age != user.age) return false; return name != null ? name.equals(user.name) : user.name == null; } @Override public int hashCode() { 
    int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } @Override public String toString() { 
    return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } // 按照姓名从小到大 @Override public int compareTo(Object o) { 
    if(o instanceof User){ 
    User user = (User)o; int compare=this.name.compareTo(user.name); if(compare!=0){ 
    return compare; }else{ 
    return Integer.compare(this.age,user.age); } } return 0; } } 

Properties实现类

package propertiestest; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Properties; / * @author 昭浅 * @create 2022/2/27-17:44 */ public class PropertiesTest { 
    //Properties:常用来处理配置文件,key和value都是String类型 public static void main(String[] args) { 
    FileInputStream fis = null; try { 
    Properties properties = new Properties(); fis = new FileInputStream("user.properties"); properties.load(fis); String name = properties.getProperty("name"); String password = properties.getProperty("password"); System.out.println(name + " " + password); } catch (Exception e) { 
    e.printStackTrace(); } finally { 
    if (fis != null) { 
    try { 
    fis.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 

Collections工具类的使用

package collectionstest; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /Collections:操作Collection、Map的工具类 * * 排序操作:(均为static方法) * reverse(List):反转 List 中素的顺序 * shuffle(List):对 List 集合素进行随机排序 * sort(List):根据素的自然顺序对指定 List 集合素按升序排序 * sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合素进行排序 * swap(List,int, int):将指定 list 集合中的 i 处素和 j 处素进行交换 * * 查找、替换 * Object max(Collection):根据素的自然顺序,返回给定集合中的最大素 * Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回 * 给定集合中的最大素 * Object min(Collection) * Object min(Collection,Comparator) * int frequency(Collection,Object):返回指定集合中指定素的出现次数 * void copy(List dest,List src):将src中的内容复制到dest中 * boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 * List 对象的所有旧值 * * * Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 * 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全 * 问题 * * 面试题:Collection和Collections的区别? * @author 昭浅 * @create 2022/3/1-20:22 */ public class ConllectionsTest { 
    /* * reverse(List):反转 List 中素的顺序 * shuffle(List):对 List 集合素进行随机排序 * sort(List):根据素的自然顺序对指定 List 集合素按升序排序 * sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合素进行排序 * swap(List,int, int):将指定 list 集合中的 i 处素和 j 处素进行交换 */ @Test public void test(){ 
    ArrayList list=new ArrayList(); list.add(123); list.add(134); list.add(11); list.add(153); list.add(133); System.out.println(list); // reverse(List) Collections.reverse(list); System.out.println(list); //shuffle(List) Collections.shuffle(list); System.out.println(list); //sort(List) Collections.sort(list); System.out.println(list); //swap(List,int, int) Collections.swap(list,2,3); System.out.println(list); } /* 查找、替换 * Object max(Collection):根据素的自然顺序,返回给定集合中的最大素 * Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回 * 给定集合中的最大素 * Object min(Collection) * Object min(Collection,Comparator) * int frequency(Collection,Object):返回指定集合中指定素的出现次数 * void copy(List dest,List src):将src中的内容复制到dest中 * boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 * List 对象的所有旧值 */ @Test public void test2(){ 
    ArrayList list=new ArrayList(); list.add(123); list.add(134); list.add(11); list.add(153); list.add(133); //frequency(Collection,Object):返回指定集合中指定素的出现次数 System.out.println(Collections.frequency(list, 133)); //copy(List dest,List src) //错误 // ArrayList list2=new ArrayList(); // Collections.copy(list2,list); //正确: List list2= Arrays.asList(new Object[list.size()]); Collections.copy(list2,list); System.out.println(list2); } /* Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全 问题 */ @Test public void test3(){ 
    ArrayList list=new ArrayList(); list.add(123); list.add(134); list.add(11); list.add(153); list.add(133); Collections.synchronizedList(list); } } 

第四章 泛型

package generictest; import com.sun.org.apache.xpath.internal.axes.SelfIteratorNoPredicate; import org.junit.Test; import sun.rmi.server.MarshalOutputStream; import java.io.ObjectOutputStream; import java.util.*; / * 泛型的使用 * 1.jdk 5.0新增特性 * * 2.在集合中使用泛型: * 总结: * ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构 * ② 在实例化集合类时,可以指明具体的泛型类型 * ③ 指明以后,在集合类或接口中,凡是定义类或接口时,内部结构使用到类的泛型的位置,都指定为实例化时泛型的类型 * 比如 :add(E e) --->实例化以后:add(Integer e) * ④ 注意点:泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,那包装类替换 * ⑤ 如果实例化时,没有指明泛型的类型,默认类型为java.lang..Object类型 * * 3.如何自定义泛型结构:泛型类,泛型接口、泛型方法(见GenericTest2) * * @author 昭浅 * @create 2022/3/6-14:35 */ public class GenericTest { 
    //在集合中使用泛型之前的情况 @org.junit.Test public void test(){ 
    ArrayList list =new ArrayList(); //需求,存放学生的成绩 list.add(87); list.add(81); list.add(57); list.add(67); //问题一 // list.add("Tom"); for(Object score : list){ 
    //问题二:强转时,可能出现ClassCastException int stuScore= (int) score; System.out.println(stuScore); } } //在集合中使用泛型之后的情况 @Test public void test2(){ 
    ArrayList<Integer> list =new ArrayList<Integer>(); //需求,存放学生的成绩 list.add(87); list.add(81); list.add(57); list.add(67); //编译时,会进行类型检查,保证数据的安全 // list.add("Tom"); //方式一: for(Integer score : list){ 
    //避免了强转操作 int stuScore=score; System.out.println(score); } //方式二: Iterator<Integer> iterator=list.iterator(); while (iterator.hasNext()) { 
    Integer next = iterator.next(); System.out.println(next); } } //在集合中使用泛型的情况:以HashMap为例 @Test public void test3(){ 
    // Map<String,Integer> map =new HashMap<String,Integer>(); //jdk 7.0新特性:类型推断 Map<String,Integer> map =new HashMap<>(); map.put("Tom",78); map.put("Joker",78); map.put("Jerry",78); map.put("Kiva",98); // map.put(1,2); //泛型的嵌套 Set<Map.Entry<String, Integer>> entries = map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); while (iterator.hasNext()){ 
    Map.Entry<String,Integer> e=iterator.next(); String name=e.getKey(); Integer score=e.getValue(); System.out.println(name+"--"+ score); } } } 
package generictest; import org.junit.Test; / * @author 昭浅 * @create 2022/3/6-16:06 */ public class GenericTest2 { 
    @Test public void test(){ 
    //如果定义了泛型类,实例化没有指明类的泛型,则认为泛型类型为Object类型 //要求:如果定义了类是带泛型的,建议在实例化时要指明类的泛型 Order order=new Order(); order.setOrderT("abc"); order.setOrderT(123); //建议:实例化时指明类的泛型 Order<String> order1=new Order<>("AA",1,"yes"); order.setOrderT("yes"); } @Test public void test2(){ 
    SubClass sub1=new SubClass(); //由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象是,不再需要指明泛型 sub1.setOrderT(123); SubOrder1<String> sub2=new SubOrder1<>(); sub2.setOrderT("yes"); } } 
package generictest; / * @author 昭浅 * @create 2022/3/6-16:07 */ public class Order<T> { 
   // E T K V 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 String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getOrderId() { 
    return orderId; } public void setOrderId(int orderId) { 
    this.orderId = orderId; } public T getOrderT() { 
    return orderT; } public void setOrderT(T orderT) { 
    this.orderT = orderT; } } ********************************* public class SubClass extends Order<Integer>{ 
   //SubClass 不是泛型类 } public class SubOrder1<T> extends Order<T>{ 
   //SubOrder1<T>仍然是泛型类 } 

泛型使用的注意点

package generictest; import org.junit.Test; public class GenericTest2 { 
    @Test public void test(){ 
    //如果定义了泛型类,实例化没有指明类的泛型,则认为泛型类型为Object类型 //要求:如果定义了类是带泛型的,建议在实例化时要指明类的泛型 Order order=new Order(); order.setOrderT("abc"); order.setOrderT(123); //建议:实例化时指明类的泛型 Order<String> order1=new Order<>("AA",1,"yes"); order.setOrderT("yes"); } @Test public void test2(){ 
    SubClass sub1=new SubClass(); //由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象是,不再需要指明泛型 sub1.setOrderT(123); SubOrder1<String> sub2=new SubOrder1<>(); sub2.setOrderT("yes"); } } 
package generictest; import org.junit.Test; import java.util.ArrayList; public class GenericTest3 { 
    //泛型的注意点 //泛型不同的引用不能相互赋值 @Test public void test(){ 
    ArrayList<String> list1=null; ArrayList<Integer> list2=null; //泛型不同的引用不能相互赋值 // list1=list2 } //静态方法不能使用类的泛型 // public static void show(T orderT){ //静态方法的使用,要早于实例对象的创建 // System.out.println(orderT); // } //异常类不能是泛型的 // class MyException<String> extends Exception{ 
    // } //catch不可使用泛型 // public void show(){ 
    // try{ 
    // // } catch(T t){ 
    // // } // } } class Stu<T>{ 
    //使用泛型创建未知数据类型数组 private Stu(){ 
    //编译不通过 // T[] arr=new T[10]; T[] arr=(T[])new Object[10]; } } 

自定义泛型结构:泛型类、泛型接口

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

泛型方法

package generictest3; import org.junit.Test; import java.util.AbstractList; import java.util.ArrayList; import java.util.Date; import java.util.List; / * @author 昭浅 * @create 2022/3/7-20:17 */ public class GenericTest { 
    /* 1.泛型在继承方面的体现 虽然类A是类B的父类,但是G<A> 和G<B> 二者不具备子父类关系,二者是并列关系 补充:类A是类B的父类 ,A<G> 是 B<G>的父类 */ @Test public void test(){ 
    Object obj =null; String str = null; obj=str; Object[] arr1=null; String[] arr2=null; arr1=arr2; //编译不通过 // Date date=new Date(); // str=date; List<Object> list1=null; List<String> list2=new ArrayList<String>(); //此时的list1和list2的类不具有子父类关系 //编译不通过 // list1=list2; /* 反证法: 假设list1 =list2 */ show(list1); show1(list2); } public void show1(List<String> list){ 
    } public void show(List<Object> list){ 
    } @Test public void test2(){ 
    AbstractList<String> list1=null; List<String> list2 =null; ArrayList<String> list3=null; list1=list3; list2=list3; } } 

通配符的使用

package generictest3; import org.junit.Test; import java.util.ArrayList; import java.util.Iterator; import java.util.List; / * 通配符的使用 * * 通配符的个人理解:,在泛型中,防止数据类型的繁冗容易出错的情况下,定义了泛型 * 在定义方法时,方法的形参也要防止方法出错的情况下,也定义成与该类实例对象的泛型一致的情况, * 所以在声明多个实例对象且有着不同的泛型类型时,我们需要定义多个重载方法(方法相同,形参不同) * 这时,我们就需要通配符的使用了,例如print(List<?> list)方法, * @author 昭浅 * @create 2022/3/19-11:32 */ public class GenericTest2 { 
    @Test public void test(){ 
    //通配符的使用‘?’ List<String> list=new ArrayList<>(); list.add("AA"); list.add("BB"); list.add("CC"); List<Object> list2=new ArrayList<>(); list2.add(1); list2.add(2); list2.add(3); print(list); print(list2); List<?> list3=null; list3=list; //添加(写入),对于List<?> 类型,不能向其内部添加数据 // list3.add("DD");//报错 // list3.add(1); //只可以向其中添加null list3.add(null); //获取(读取):允许读取数据,读取的数据类型为Object Object o=list3.get(0); System.out.println(o); } public void print(List<?> list){ 
    Iterator<?> iterator = list.iterator(); while (iterator.hasNext()){ 
    Object o=iterator.next(); System.out.println(o); } } } 

限制性通配符的使用

package generictest4; import org.junit.Test; import java.util.ArrayList; import java.util.List; / * 有限制性条件的通配符的使用: * 1.关于限制通配符的使用: * <? extends Person>: * 表明声明该泛型的类,可赋值的数据类型只能是"≤Person"类的 * <? super Person>: * 表明声明该泛型的类,可赋值的数据类型只能是"≥Person"类的 * * 2.限制性通配符的读取和写入: * <? extends Person>: * 读取:例如List<? extends Person>,在读取数据时,由于读取到的数据类型≤Person类,没有下限 * 所以编译器在拿出数据后不明确拿出的数据到底是什么类型,所以只能由Person引用来接收 * 写入:不能写入!! 例如List<? extends Person>,在写入数据时,由于声明的类型为≤Person类,没有下限 * 所以在写入数据时,编译器不明确将add()的形参传给什么类型的实参,所以不可写入 * * <? super Person> * 读取:例如List<? super Person>,在读取数据时,由于读取到的数据类型≥Person类,没有上限 * 所以编译器在拿出数据后不明确拿出的数据到底是什么类型,正常情况下是不能读取的,但是由于Object类是所有类的父类, * 所以只能由Object类来接收 * 写入:例如List<? super Person>,由于声明的数据类型为<? super Person>,没有上限,所以在写入数据时 * 编译器不明确将编译器不明确将add()的形参传给什么类型的实参,所以只能写入Person类型以及Person的子类 * * @author 昭浅 * @create 2022/3/20-10:25 */ public class GenericTest2 { 
    /* 有限执行条件的通配符的使用 ? extends A: G<? extedns A>可以作为G<A>和G<B>的父类,其中B是A的子类 ? super A: G<? super A>可以作为G<A>和G<B>的父类,其中B是A的父类 */ @Test public void test(){ 
    List<? extends Person> list1=null; List<? super Person> list2=null; List<Student> list3=new ArrayList<Student>(); List<Person> list4=new ArrayList<Person>(); List<Object> list5=new ArrayList<Object>(); list1=list3; list1=list4; // list1=list5; // list2=list3; list2=list4; list2=list5; //读取数据: list1=list3; Person p=list1.get(0); //编译不通过 // Student s =list1.get(); //写入数据 //编译不通过 // list1.add(new Student()); list2.add(new Person()); list2.add(new Student()); } } class Person{ 
   } class Student extends Person{ 
   } 

File类的使用

package filetest; import org.junit.Test; import java.io.File; import java.io.IOException; import java.util.Date; / * File 类的使用: * * 1.File类的UI个对象,代表一个文件或一个文件目录(俗称:文件夹) * 2.File类声明在java.io包下 * 3.File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法 * 并未涉及到写入或都文件内容的操作,如果需要读取或写入文件内容,必须使用IO流来完成 * 4.后续File类的对象常会作为参数传递流的构造器中,指明读取或写入的终点 * * * @author 昭浅 * @create 2022/3/22-14:10 */ public class FileTest { 
    /* 1. 如何创建File类的实例 File(String filePath) FIle(String parentPath,childPath) File(File parentPath,String childPath) 2.路径: 绝对路径:包含盘符在内的文件或文件目录的路径 相对路径:相对于某个路径下,指明的路径 IDEA中: 开发使用Junit单测试方法测试时,相对路径为当前的moudle下 使用main方法时,相对路径为当前工程下 Eclipse中:相对路径为 当前的项目下 3.路径分隔符: windows和DOS :" \ " Unix和URL:“ / ” Final String separator:智能分隔符,不同操作系统都适用 */ @Test public void test(){ 
    //构造器1 File file1=new File("hello.txt");//相对于当前的moudle File file2=new File("D:\\IDEA\\workSpace\\Project\\FileTest\\he.txt");//绝对路径 System.out.println(file1); System.out.println(file2); //构造器2 File file3=new File("D:\\IDEA\\workSpace\\Project\\FileTest","test"); System.out.println(file3); //构造器3 File file4=new File(file3,"hi.txt"); System.out.println(file4); } /* 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数组 */ @Test public void test2(){ 
    File file1=new File("hello.txt"); File file2=new File("D:\\IDEA\\workSpace\\Project\\FileTest\\he.txt"); System.out.println(file1.getAbsoluteFile()); System.out.println(file1.getPath()); System.out.println(file1.getName()); System.out.println(file1.getParent()); System.out.println(file1.length()); System.out.println(new Date(file1.lastModified())); System.out.println(); System.out.println(file2.getAbsoluteFile()); System.out.println(file2.getPath()); System.out.println(file2.getName()); System.out.println(file2.getParent()); System.out.println(file2.length()); System.out.println(file2.lastModified()); } @Test public void test3(){ 
    File file =new File("D:\\IDEA\\workSpace\\Project"); String[] list=file.list(); for(String s:list){ 
    System.out.println(s); } File[] files=file.listFiles(); for(File f:files){ 
    System.out.println(f); } } /* public boolean renameTo(File dest):把文件重命名为指定的文件路径 比如:file1.renameTo(file2)为例: 要想保证返回true,需要file1在硬盘中是存在的,且file2不能再硬盘中存在 */ @Test public void test4(){ 
    File file1=new File ("hello.txt"); File file2=new File ("D:\\IDEA\\workSpace\\Project\\FileTest\\hi.txt"); boolean isReName=file1.renameTo(file2); System.out.println(isReName); } /* public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏 */ @Test public void test5(){ 
    File file1=new File("hello.txt"); System.out.println(file1.isDirectory()); System.out.println(file1.isFile()); System.out.println(file1.exists()); System.out.println(file1.canRead()); System.out.println(file1.canWrite()); System.out.println(file1.isHidden()); System.out.println(); file1=new File("D:\\IDEA\\workSpace\\Project\\FileTest\\hello.txt"); System.out.println(file1.isDirectory()); System.out.println(file1.isFile()); System.out.println(file1.exists()); System.out.println(file1.canRead()); System.out.println(file1.canWrite()); System.out.println(file1.isHidden()); } /* public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建 注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目 路径下。 public boolean delete():删除文件或者文件夹 删除注意事项: Java中的删除不走回收站。 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录 */ @Test public void test6() throws IOException { 
    File file1 =new File("hi.txt"); if(!file1.exists()){ 
    file1.createNewFile(); System.out.println("创建成功"); }else{ 
   //文件存在 file1.delete(); System.out.println("删除成功"); } } @Test public void test7(){ 
    //文件目录的创建‘’ File file1=new File("D:\\IDEA\\workSpace\\Project\\FileTest\\t1\\test"); File file2=new File("D:\\IDEA\\workSpace\\Project\\FileTest\\t1\\test"); boolean mkdir=file1.mkdir(); if(mkdir){ 
    System.out.println("创建成功1"); } boolean mkdir2=file2.mkdirs(); if(mkdir2){ 
    System.out.println("创建成功2"); } } } 

第五章 IO流

一、什么是IO流

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

IO流的使用(以文件流举例)

package iotest; import org.junit.Test; import java.io.*; / * 一、流的分类 * 1.操作数据单位:字节流、字符流 * 2.数据的流向:输入流、输出流 * 3.流的角色:节点流、处理流 * * 二、流的体系结构 * 抽象基类 节点流(或文件流) 缓冲流(处理流的一种) * InputStream FileInputStream (read(byte[] buffer)) BufferedInputStream (read(byte[] buffer)) * OutputStream FileOutputStream (write(byte[] buffer,0,len)) BufferedOutputStream (write(byte[] buffer,0,len)) / flush() * Reader FileReader (read(char[] cbuf)) BufferedReader (read(char[] cbuf)) * Writer FileWriter write(char[] cbuf,0,len) BufferedWriter write(char[] cbuf,0,len) / flush() * * * * @author 昭浅 * @create 2022/3/30-14:36 */ public class FileReaderWriterTest { 
    /* 将文件hello.txt读入,并输出到控制台 说明点: 1.read()的理解:返回读入的第一个字符(返回int类型,需要自行强转为char类型),如果达到文件末尾,返回-1 2.异常的处理:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理 3.读入的文件一定要存在,否则就会报FileNotFoundException */ @Test public void testFileReader() { 
    FileReader fr= null; try { 
    //1.实例化File类的对象,指明要操作的文件 File file=new File("hello.txt"); //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的第一个字符,如果达到文件末尾,返回-1 int data=fr.read(); while(data!=-1){ 
    System.out.println((char)data); data=fr.read(); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    try{ 
    //4.流的关闭 if(fr!=null) fr.close(); }catch (IOException e){ 
    e.printStackTrace(); } } } //对read()操作升级,使用read()的重载方法 @Test public void testFileReader2(){ 
    FileReader fr = null; try { 
    //1.实例化File类的对象,指明要操作的文件 File file=new File("hello.txt"); //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的第一个字符,如果达到文件末尾,返回-1 char[] charbuffs=new char[5]; int len; while((len=fr.read(charbuffs))!=-1){ 
    //遍历:方式一 //错误写法 // for(int i=0;i<charbuffs.length;i++){ 
    // System.out.print(charbuffs[i]); // } //正确写法 for(int i=0;i<len;i++){ 
    System.out.print(charbuffs[i]); } //遍历:方式二 //错误的写法,对应着方式一的错误写法 // String str=new String(charbuffs); // System.out.println(str);//helloworld123ld //正确的写法 String str=new String(charbuffs,0,len); System.out.println(str); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    try { 
    //4.流的关闭 if (fr != null) fr.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } /* 从内存中写出数据到硬盘的文件里 说明: 1.输出操作,对应File可以不存在 File对应的硬盘文件如果不存在,在输出的过程中,会自动创建此文件 File对应的硬盘文件如果存在: 如果流使用的构造器是:FillWriter(file.false) / FileWriter(file):对原有文件进行覆盖 如果流使用的构造器是:FillWriter(file.true):不会对原有文件覆盖,而是在原有文件基础上追加内容 */ @Test public void testFileWriter(){ 
    FileWriter fw= null; try { 
    //1.提供File类的对象,指明写出的文件 File file =new File("test.txt"); //2.提供FileWriter的对象,用于数据的写出 fw = new FileWriter(file); //3.写出的操作 fw.write("I hava a dream\n"); fw.write("I hava a dream too\n"); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //4.流资源的关闭 try { 
    fw.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } 

文件的复制

package iotest; import org.junit.Test; import java.io.*; / * @author 昭浅 * @create 2022/3/31-21:33 */ public class IOTest2 { 
    /* 文件的读取与写入(文件的复制) */ @Test public void FileCopy() { 
    FileReader fr= null; FileWriter fw= null; try { 
    //1.创建File文件对象,指明读取和写出的文件 File file=new File("hello.txt"); File file1=new File("world.txt"); //2.创建输入流和输出流的对象 fr = new FileReader(file); fw = new FileWriter(file1); //3.数据的读取与写出操作 char[] cbuf=new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while ((len=fr.read(cbuf))!=-1){ 
    fw.write(cbuf,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //4.关闭流资源 //方式一 // try { 
    // fw.close(); // } catch (IOException e) { 
    // e.printStackTrace(); // } // try { 
    // fr.close(); // } catch (IOException e) { 
    // e.printStackTrace(); // } //方式二 try { 
    fw.close(); } catch (IOException e) { 
    e.printStackTrace(); }finally{ 
    try { 
    fr.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 测试对图片等字节文件的复制 结果:使用字符流对字节文件进行读取写入操作时,会将文件损坏 */ @Test public void imgCopyTest(){ 
    FileReader fr= null; FileWriter fw= null; try { 
    //1.创建File文件对象,指明读取和写出的文件 File file=new File("test.jpg"); File file1=new File("test1.jpg"); //2.创建输入流和输出流的对象 fr = new FileReader(file); fw = new FileWriter(file1); //3.数据的读取与写出操作 char[] cbuf=new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while ((len=fr.read(cbuf))!=-1){ 
    fw.write(cbuf,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    try { 
    fw.close(); } catch (IOException e) { 
    e.printStackTrace(); }finally{ 
    try { 
    fr.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 

字符流不能对字节文件的操作

package iotest; import org.junit.Test; import java.io.*; / * 测试FileInputStream和FileOutputStreamd的使用 * * 结论: * 1.对于文本文件('.txt','.java','.c','.cpp',...),使用字符流处理 * 2.对于非文本文件('.jpg','.mp3','.mp4','.avi','.doc','.ppt,...),使用字节流处理 * @author 昭浅 * @create 2022/3/31-22:09 */ public class FileInputOutputTest { 
    /* 图片文件的复制 */ @Test public void imgCopyTest() { 
    FileInputStream inputStream= null; FileOutputStream outputStream= null; try { 
    File file=new File("test.jpg"); File file1=new File("test2.jpg"); inputStream = new FileInputStream(file); outputStream = new FileOutputStream(file1); byte[] buffer=new byte[5]; int len; while((len=inputStream.read(buffer))!=-1){ 
    outputStream.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(inputStream!=null){ 
    try { 
    inputStream.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(outputStream!=null){ 
    try { 
    outputStream.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 使用字节流对文本文件进行操作,可能会乱码 */ @Test public void testFileInputStream() { 
    FileInputStream fileIn= null; FileOutputStream fileOut= null; try { 
    //1.提供File类的对象,指明写出的文件 File file=new File("hello.txt"); File file1=new File("hello2.txt"); //2.提供 FileInputStream和FileWriter的对象,用于数据的读取和写出 fileIn = new FileInputStream("hello.txt"); fileOut = new FileOutputStream("hello2.txt"); //3.复制的操作 byte[] buffer=new byte[5]; int len;//记录每次读取的字节个数 while((len=fileIn.read(buffer))!=-1){ 
    fileOut.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //4.流资源的关闭 try { 
    fileOut.close(); } catch (IOException e) { 
    e.printStackTrace(); } try { 
    fileIn.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } /* 创建方法,实现指定路径下的复制 说明:对文本文件只进行复制操作,不会产生影响 */ @Test public void copyTest(){ 
    String srcPath="C:\\Users\\ZhaoQian\\Desktop\\双周.jpg"; String destPath="C:\\Users\\ZhaoQian\\Desktop\\yes.jpg"; long start=System.currentTimeMillis(); copyFile(srcPath,destPath); long end=System.currentTimeMillis(); System.out.println("复制时长为:"+(end-start));//复制时长为:120 } public void copyFile(String srcPath,String destPath){ 
    FileInputStream inputStream= null; FileOutputStream outputStream= null; try { 
    File file=new File(srcPath); File file1=new File(destPath); inputStream = new FileInputStream(file); outputStream = new FileOutputStream(file1); byte[] buffer=new byte[5]; int len; while((len=inputStream.read(buffer))!=-1){ 
    outputStream.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(inputStream!=null){ 
    try { 
    inputStream.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(outputStream!=null){ 
    try { 
    outputStream.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 

缓冲流的使用(处理流之一)

package iotest; import org.junit.Test; import java.io.*; / * 处理流之一:缓冲流的作用 * * 1.缓冲流: * BufferedInputStream * BufferedOutputStream * BufferedReader * BufferedWriter * * 2.作用:提供流的读取、写入的速度 * 提高读写速度的原因:内部提供了一个缓冲区 * * BufferedOutputStream flush(): 刷新缓冲区,每当调用此方法,内部缓冲区会将以已缓冲数据写入文件 * ,缓冲区满时,会自动调用该方法 * * 3.处理流,就是“套接”在已有的流的基础上 * @author 昭浅 * @create 2022/4/1-16:41 */ public class BufferedTest { 
    /* 使用处理流,实现非文本文件的复制 */ @Test public void BufferedStreamTest(){ 
    FileInputStream fis= null; FileOutputStream fos= null; BufferedInputStream bis= null; BufferedOutputStream bos= null; try { 
    //1.创建文件对象 File file=new File("test.jpg"); File file1=new File("test3.jpg"); //2.创建节点流 fis = new FileInputStream(file); fos = new FileOutputStream(file1); //3.创建缓冲流(处理流) bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); byte[] buffer=new byte[5]; int len; while((len=bis.read(buffer))!=-1){ 
    bos.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //流资源关闭 //要求,先关闭外层的流,在关闭里层的流 //说明:在关闭外层流的同时,内层流也会自动的关闭,关于内层流的关闭,我们可以省略 if(bis!=null){ 
    try { 
    bis.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(bos!=null){ 
    try { 
    bos.close(); } catch (IOException e) { 
    e.printStackTrace(); } } // fis.close(); // fos.close(); } } //创建方法:实现文件的复制 @Test public void copyTest(){ 
    String srcPath="C:\\Users\\ZhaoQian\\Desktop\\双周.jpg"; String destPath="C:\\Users\\ZhaoQian\\Desktop\\no.jpg"; long start=System.currentTimeMillis(); copyFileWithBuffered(srcPath,destPath); long end=System.currentTimeMillis(); System.out.println("复制时长为:"+(end-start));//复制时长为:9 } public void copyFileWithBuffered(String srcPath,String destPath){ 
    FileInputStream fis= null; FileOutputStream fos= null; BufferedInputStream bis= null; BufferedOutputStream bos= null; try { 
    //1.创建文件对象 File file=new File(srcPath); File file1=new File(destPath); //2.创建节点流 fis = new FileInputStream(file); fos = new FileOutputStream(file1); //3.创建缓冲流(处理流) bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); byte[] buffer=new byte[5]; int len; while((len=bis.read(buffer))!=-1){ 
    bos.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //流资源关闭 //要求,先关闭外层的流,在关闭里层的流 //说明:在关闭外层流的同时,内层流也会自动的关闭,关于内层流的关闭,我们可以省略 if(bis!=null){ 
    try { 
    bis.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(bos!=null){ 
    try { 
    bos.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 使用BufferedReader和BufferedWriter实现文本文件的复制 */ @Test public void BufferedReaderWriterTest(){ 
    BufferedReader br= null; BufferedWriter bw= null; try { 
    //创建文件对象及相应的流 //简易写法 br = new BufferedReader(new FileReader(new File("hello.txt"))); bw = new BufferedWriter(new FileWriter(new File("hello3.txt"))); //文件的读写 //方式一:使用char数组 // char[] cbuf=new char[5]; // int len; // while((len=br.read(cbuf))!=-1){ 
    // bw.write(cbuf,0,len); // } //方式二:使用String String data; while ((data=br.readLine())!=null){ 
    //方法一 // bw.write(data+"\n");//data中不包含换行,需要自行添加 //方法二 bw.write(data); bw.newLine();//提供换行操作 } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //流的关闭 if(bw!=null){ 
    try { 
    bw.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(br!=null){ 
    try { 
    br.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 

转换流的使用(处理流之二)

package inputstreamreadertest; import jdk.nashorn.internal.ir.WhileNode; import org.junit.Test; import java.io.*; / * 处理流之二:转换流的使用 * 1.转换流:属于字符流 * InputStreamReader:将一个字节的输入流转换为字符的输入流 * OutputStreamWriter:将一个字符的输出流转换为字节的输出流 * * 2.作用:提供字节流与字符流之间的转换 * * 3.解码:字节、字节数组 --->字符数组、字符串 * 编码:字符数组、字符串 --->字节、字节数组 * * 编码决定解码的方式 * * 4.字符集 * @author 昭浅 * @create 2022/4/2-20:24 */ public class InputStreamReaderTest { 
    /* 此时处理异常的话,仍应该使用try-catch-finally */ @Test public void InputStreamReaderTest() throws IOException { 
    FileInputStream fis=new FileInputStream("hello.txt"); InputStreamReader isr=new InputStreamReader(fis);//使用系统默认的字符集 //参数2指明了字符集,具体使用哪个字符集,取决于文件"hello.txt"保存时使用的字符集 InputStreamReader isr2=new InputStreamReader(fis,"utf-8"); char[] cbuf=new char[24]; int len; while ((len=isr2.read(cbuf))!=-1){ 
    String str=new String(cbuf,0,len); System.out.println(str); } isr.close(); isr2.close(); } /* 综合使用InputStreamReader和OutputStreamWriter 此时处理异常的话,仍应该使用try-catch-finally */ @Test public void test() throws IOException { 
    File file=new File("hello.txt"); File file1=new File("hello_gbk.txt"); FileInputStream fr= new FileInputStream(file); FileOutputStream fw= new FileOutputStream(file1); InputStreamReader isr=new InputStreamReader(fr,"utf-8"); OutputStreamWriter osw=new OutputStreamWriter(fw,"gbk"); char[] cbuf=new char[24]; int len; while((len=isr.read(cbuf))!=-1){ 
    osw.write(cbuf,0,len); } isr.close(); osw.close(); } } 

其他流的使用

package otherIOTest; import org.junit.Test; import org.junit.experimental.theories.suppliers.TestedOn; import java.io.*; / * 其他流的使用 * 1.标准的输入、输出流 * 2.打印流 * 3.数据流 * * @author 昭浅 * @create 2022/4/3-21:24 */ public class OtherIOTest { 
    /* 1.标准的输入、输出流 1.1 System.in:标准的输入流,默认从键盘输入 System.out:标准的输出流,默认从控制台输出 1.2 System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流 1.3 练习: 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续 进行输入操作,直至当输入“e”或者“exit”时,退出程序。 */ public static void main(String[] args) { 
    //方式一:使用Scanner类 //方式二:使用System.in System.in --->转换流 BufferedReader的readLine() BufferedReader br = null; try { 
    InputStreamReader is = new InputStreamReader(System.in); br = new BufferedReader(is); while (true) { 
    String data = br.readLine(); if (data.equalsIgnoreCase("e") || data.equalsIgnoreCase("exit")) { 
    System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if (br != null) { 
    try { 
    br.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 2.打印流 PrintStream和PrintWriter 2.1 提供了一系列重载的print()和 println() 2.2 练习:重新创建打印流,使数据打印至文件内 实现将基本数据类型的数据格式转化为字符串输出 打印流:PrintStream(字节)和PrintWriter(字符)  提供了一系列重载的print()和println()方法,用于多种数据类型的输出  PrintStream和PrintWriter的输出不会抛出IOException异常  PrintStream和PrintWriter有自动flush功能  PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。 在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。  System.out返回的是PrintStream的实例 */ @Test public void PrintStreamtest() { 
    PrintStream ps = null; try { 
    FileOutputStream fos = new FileOutputStream(new File("PrintStreamtest.txt")); // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区) ps = new PrintStream(fos, true); if (ps != null) { 
   // 把标准输出流(控制台输出)改成文件 System.setOut(ps); } for (int i = 0; i <= 255; i++) { 
    // 输出ASCII字符 System.out.print((char) i); if (i % 50 == 0) { 
    // 每50个数据一行 System.out.println(); // 换行 } } } catch (FileNotFoundException e) { 
    e.printStackTrace(); } finally { 
    if (ps != null) { 
    ps.close(); } } } /* 3.数据流 3.1 DataInputStream 和DataOutputStream 3.2 作用:用于读取或写出基本数据类型的变量或字符串 练习:将内存中的字符串,基本数据类型的变量写出到文件中 注意:处理异常的话,仍在应该使用try-catch-finally处理 */ @Test public void DataOutputStreamtest() throws IOException { 
    DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt")); dos.writeUTF("赵茜"); dos.flush(); dos.writeInt(21); dos.flush(); dos.writeBoolean(true); dos.flush(); dos.close(); } //将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中 //注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!! @Test public void DataInputStramtest() throws IOException { 
    //1 DataInputStream dis=new DataInputStream(new FileInputStream("data.txt")); //2 String name = dis.readUTF(); int age = dis.readInt(); boolean b = dis.readBoolean(); System.out.println("name:"+name); System.out.println("age:"+age); System.out.println("boolean:"+b); dis.close(); } } 

对象流的使用

package objectinoutputstreamTest; import org.junit.Test; import java.io.*; / * 对象流的使用 * 1.ObjectInputStream 和 ObjectOutputSteam * 2.作用:用于存储和读取基本数据类型数据或对象的处理流,它的强大之处就是可 * 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来 * * 3.要想一个java对象是可序列话的,需要满足相应的需求 ,见Person.java * @author 昭浅 * @create 2022/4/19-19:05 */ public class ObjectInputOutputStreamTest { 
    /* 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去 使用ObjectOutputStream实现 */ @Test public void ObjectOutputStreamTest(){ 
    ObjectOutputStream oos= null; try { 
    oos = new ObjectOutputStream(new FileOutputStream("object.dat")); oos.writeObject(new String("我爱北京")); oos.writeObject(new Person("网",12,true)); oos.flush(); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(oos!=null){ 
    try { 
    oos.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 反序列化过程:将磁盘文件中的对象还原为内存中的一个java对象 */ @Test public void ObjectInputStreamTest(){ 
    ObjectInputStream ois= null; String s="1"; try { 
    ois = new ObjectInputStream(new FileInputStream("object.dat")); Object o=ois.readObject(); Person p=(Person) ois.readObject(); s=(String) o; System.out.println(s); System.out.println("name:"+p.getName()); System.out.println("age:"+p.getAge()); System.out.println("isMale:"+p.isMale()); } catch (IOException e) { 
    e.printStackTrace(); } catch (ClassNotFoundException e) { 
    e.printStackTrace(); } finally { 
    if(ois!=null){ 
    try { 
    ois.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 
package objectinoutputstreamTest; import java.io.Serializable; / * Person需要满足如下的要求方可序列化 * 1.实现 Serializable接口(标识接口) * 2.当前类定义一个全局常量,long serialVersionUID * @author 昭浅 * @create 2022/4/19-21:49 */ public class Person implements Serializable{ 
    public static final long serialVersionUID = L;//自定义异常类中也需要定义此类号码 private String name; private int age; private boolean isMale; public Person(){ 
    } public Person(String name,int age,boolean isMale){ 
    this.name=name; this.age=age; this.isMale=isMale; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return age; } public void setAge(int age) { 
    this.age = age; } public boolean isMale() { 
    return isMale; } public void setMale(boolean male) { 
    isMale = male; } } 

随机(任意)存取文件流的使用(RandomAccessStream)

package randomaccessfiletest; import org.junit.Test; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; / * RandomAccessFile的使用 * 1.RandomAccessFile直接继承于java.lang.Object类,实现了DataInpu和Dataoutput接口 * 2.RandomAccessFile即可以作为一个输出流,又可以作为一个输入流 * <p> * 3.如果RandomAccessFile作为输出时,写出到的文件如果不存在,则在执行过程中自动创建 * 如果写出到的文件存在,则会对原有文件内容进行覆盖(默认情况下,从头覆盖) * <p> * 4. raf.seek(int pos);//将文件指针调到pos的位置,写入时,在pos的位置进行操作 * * @author 昭浅 * @create 2022/4/20-9:40 */ public class RandomAccessFileTest { 
    @Test public void RandomAccessFileTest() { 
    RandomAccessFile raf1 = null; RandomAccessFile raf2 = null; try { 
    raf1 = new RandomAccessFile(new File("test1.jpg"), "r"); raf2 = new RandomAccessFile(new File("test2.jpg"), "rw"); byte[] buffers = new byte[1024]; int len; while ((len = raf1.read(buffers)) != -1) { 
    raf2.write(buffers, 0, len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if (raf1 != null) { 
    try { 
    raf1.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if (raf2 != null) { 
    try { 
    raf2.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 测试 RandomAccessFile写出文件时,对文件的具体操作 */ @Test public void test2() { 
    RandomAccessFile raf1 = null; try { 
    // raf1 = new RandomAccessFile(new File("hello.txt"), "rw"); raf1 = new RandomAccessFile(new File("hello1.txt"), "rw"); raf1.write("abcdefg".getBytes()); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if (raf1 != null) { 
    try { 
    raf1.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } /* 在文件的指定位置进行覆盖 */ @Test public void test3() throws IOException { 
    RandomAccessFile raf = new RandomAccessFile(new File("hello.txt"), "rw"); raf.seek(3);//将文件指针调到3的位置,写入时,在3的位置进行操作 raf.write("bnm".getBytes()); raf.close(); } /* 对文件的插入 */ @Test public void test4() throws IOException { 
    RandomAccessFile raf = new RandomAccessFile(new File("hello.txt"), "rw"); raf.seek(3);//将文件指针调到3的位置,写入时,在3的位置进行操作 StringBuilder str = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while ((len = raf.read(buffer)) != -1) { 
    str.append(new String(buffer, 0, len));//将指针后的文件内容保存到字符串中 } raf.seek(3); raf.write("xyz".getBytes()); raf.write(str.toString().getBytes()); raf.close(); } //自行研究将StringBuilder替换成ByteArrayOutputStream } 

第六章 网络编程的使用

一、网络通信要素

package inetAddresstest; import java.net.InetAddress; import java.net.UnknownHostException; / * 一、网络编程中有两个主要问题 * 1.如何准确的定位网络上一台或多台主机,定位主机上的特定的应用 * 2。找到主机后如何可靠高效的进行数据传输 * * 二、网络编程中的两个要素: * 1.对应问题已:IP和端口号 * 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层); * * 三:通信要素一:IP和端口号 * 1.IP:唯一的标识 Internet上的计算机(通信实体) * 2.在Java中使用InetAddress类代表IP * 3.IP分类:IPv4、IPv6; 万维网、局域网 * 4.域名: www.baidu.com www.mi.com ....... * 5.本地回路地址:127.0。0.1 对应着:localhost * * 6.如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost() * 两个常用方法:getHostName() / getHostAddress() * * 7.端口号:正在计算机上运行的进程 * 要求:不同进程有不同的端口号 * 范围:被规定为一个16位的整数0-65535 * 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23) * 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。 * 动态/私有端口:49152~65535。 * * 8.端口号与IP地址的组合得出一个网络套接字:Socket * @author 昭浅 * @create 2022/4/20-16:58 */ public class InetAddressTest { 
    public static void main(String[] args) { 
    try { 
    InetAddress inet1=InetAddress.getByName("192.158.5.1"); System.out.println(inet1); InetAddress inet2=InetAddress.getByName("www.atguigu.com"); System.out.println(inet2); InetAddress inet3=InetAddress.getByName("127.0.0.1"); System.out.println(inet3); //获取本机ip InetAddress inet4=InetAddress.getLocalHost(); System.out.println(inet4); //getHostName System.out.println(inet2.getHostName()); //getHostAdress System.out.println(inet4.getHostAddress()); } catch (UnknownHostException e) { 
    e.printStackTrace(); } } } 

二、TCP网络编程

package tcptest; import inetAddresstest.InetAddressTest; import org.junit.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; import java.net.UnknownHostException; / * 实现Tcp网络编程 * 例题1:客户端发送信息给服务端,服务端将数据显示在控制台 * @author 昭浅 * @create 2022/4/20-17:40 */ public class TcpTest { 
    //客户端 @Test public void client() { 
    Socket socket= null; OutputStream os = null; try { 
    //1.创建Socket对象,指明服务器端的ip和端口号 InetAddress inet=InetAddress.getByName("127.0.0.1"); socket = new Socket(inet,8989); //获取一个输出流,用于输出数据 os = socket.getOutputStream(); //3.写出数据的操作 os.write("你好,我是客户端".getBytes()); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    //资源的关闭 if(os!=null){ 
    try { 
    os.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(socket!=null){ 
    try { 
    socket.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } //服务器端 @Test public void Server(){ 
    ServerSocket ss= null; Socket socket= null; InputStream is= null; ByteArrayOutputStream bos= null; try { 
    ss = new ServerSocket(8989); socket = ss.accept(); is = socket.getInputStream(); //不建议这样写,可能会有乱码 // byte[] buffer=new byte[4]; // int len; // while((len=is.read(buffer))!=-1){ 
    // String str=new String(buffer,0,len); // System.out.println(str); // } // bos = new ByteArrayOutputStream(); byte[] buffers=new byte[20]; int len; while((len=is.read(buffers))!=-1){ 
    bos.write(buffers,0,len); } System.out.println(bos.toString()); System.out.println("收到来自于"+socket.getInetAddress().getHostAddress()+"的数据"); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(bos!=null){ 
    try { 
    bos.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(is!=null){ 
    try { 
    is.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(socket!=null){ 
    try { 
    socket.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(ss!=null){ 
    try { 
    ss.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 
package tcptest; import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; / * 实现TCP的网络编程 * 例题2:客户端发送文件给服务器,服务端将文件保存在本地 * * @author 昭浅 * @create 2022/4/20-18:30 */ public class TcpTest2 { 
    //客户端 @Test public void client() { 
    Socket socket = null; OutputStream os = null; FileInputStream fis=null; try { 
    InetAddress inet = InetAddress.getByName("127.0.0.1"); socket = new Socket(inet, 8989); os = socket.getOutputStream(); fis=new FileInputStream("test1.jpg"); byte[] buffer=new byte[20]; int len; while((len=fis.read(buffer))!=-1){ 
    os.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if (os != null) { 
    try { 
    os.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if (socket != null) { 
    try { 
    socket.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(fis!=null){ 
    try { 
    fis.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } //服务端 @Test public void Server() { 
    ServerSocket ss= null; Socket socket = null; InputStream is= null; FileOutputStream fos = null; try { 
    ss = new ServerSocket(8989); socket = ss.accept(); is = socket.getInputStream(); fos = new FileOutputStream(new File("test2.jpg")); byte[] buffer=new byte[20]; int len; while ((len=is.read(buffer))!=-1) { 
    fos.write(buffer,0,len); } } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(fos!=null){ 
    try { 
    fos.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(is!=null){ 
    try { 
    is.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(socket!=null){ 
    try { 
    socket.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(ss!=null){ 
    try { 
    ss.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 
package tcptest; import org.junit.Test; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; / * 从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给 * 客户端。并关闭相应的连接。 * @author 昭浅 * @create 2022/4/20-20:48 */ public class TCPTest3 { 
    //客户端 @Test public void client() { 
    Socket socket = null; OutputStream os = null; FileInputStream fis=null; InputStream is=null; ByteArrayOutputStream bos=null; try { 
    InetAddress inet = InetAddress.getByName("127.0.0.1"); socket = new Socket(inet, 8989); os = socket.getOutputStream(); fis=new FileInputStream("test1.jpg"); byte[] buffer=new byte[20]; int len; while((len=fis.read(buffer))!=-1){ 
    os.write(buffer,0,len); } socket.shutdownOutput();//关闭客户端输出流 is = socket.getInputStream(); bos=new ByteArrayOutputStream(); byte[] buffer1=new byte[20]; int len1; while ((len1=is.read(buffer1))!=-1){ 
    bos.write(buffer1,0,len1); } System.out.println(bos.toString()); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if (os != null) { 
    try { 
    os.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if (socket != null) { 
    try { 
    socket.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(fis!=null){ 
    try { 
    fis.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } //服务端 @Test public void Server() { 
    ServerSocket ss= null; Socket socket = null; InputStream is= null; FileOutputStream fos = null; OutputStream os=null; try { 
    ss = new ServerSocket(8989); socket = ss.accept(); is = socket.getInputStream(); fos = new FileOutputStream(new File("test3.jpg")); byte[] buffer=new byte[20]; int len; while ((len=is.read(buffer))!=-1) { 
    fos.write(buffer,0,len); } os = socket.getOutputStream(); os.write("服务器端:已接收到数据".getBytes()); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(fos!=null){ 
    try { 
    fos.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(is!=null){ 
    try { 
    is.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(socket!=null){ 
    try { 
    socket.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(ss!=null){ 
    try { 
    ss.close(); } catch (IOException e) { 
    e.printStackTrace(); } } if(os!=null){ 
    try { 
    os.close(); } catch (IOException e) { 
    e.printStackTrace(); } } } } } 

三、UDP协议的网络编程

package udptest; import org.junit.Test; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; / * UDP协议的网络编程 * * @author 昭浅 * @create 2022/4/20-21:23 */ public class UDPTest { 
    //发送端 @Test public void sender(){ 
    DatagramSocket socket = null; try { 
    socket = new DatagramSocket(); String str="我是UDP方式发送的消息"; byte[] data=str.getBytes(); InetAddress inet = InetAddress.getByName("127.0.0.1"); DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,8989); socket.send(packet); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(socket!=null){ 
    socket.close(); } } } //接收端 @Test public void receiver(){ 
    DatagramSocket socket = null; try { 
    socket = new DatagramSocket(8989); byte[] buffer=new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(),0,packet.getLength())); } catch (IOException e) { 
    e.printStackTrace(); } finally { 
    if(socket!=null){ 
    socket.close(); } } } } 

第七章 反射

一、反射的使用情况

package reflectionTest; import org.junit.Test; import java.lang.annotation.ElementType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; / * 疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中用哪个 * 建议:直接new的方式 * 疑问2:什么时候回使用,反射的方式 :反射的体征:动态性 * * 疑问3:反射机制与面向对象的封装行是不是矛盾的,如何看待两个技术 * 不矛盾,封装性可以理解为不建议调用私有的结构,而反射强调的是能不能调用的问题 * * 关于java.lang.Class类的理解 * 1.类的加载过程 * 程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾) * 接着我们使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件 * 加载到内存中,此过程就成为类的加载,加载到内存中的类,我们就称为运行时类,此 * 运行时类就作为Class的一个实例 * 2.换句话说,Class的实例就对应着一个运行时类 * * 3.加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式 * 来获取此运行时类 * * * 4.哪些类型可以有Class对象? * (1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类 * (2)interface:接口 * (3)[]:数组 * (4)enum:枚举 * (5)annotation:注解@interface * (6)primitive type:基本数据类型 * (7)void * * 5.体现万事万物皆对象? 对象.xxx 、File 、 URL 、 反射 、前段、数据库操作 * @author 昭浅 * @create 2022/4/21-10:05 */ public class ReflectionTest { 
    //反射前,对Person类的实例化 @Test public void test1(){ 
    Person p=new Person("Tom",13); System.out.println(p.age); System.out.println(p.toString()); p.show(); //在Person类外部,不可以通过Person类的对象调用其内部私有结构 //比如:name、showNation()、以及私有构造器 } //反射之后,对于Person的操作 @Test public void test2() throws Exception{ 
    Class classP=Person.class; //1.通过反射创建Person类的对象 Constructor cons = classP.getConstructor(String.class, int.class); Object obj = cons.newInstance("Tom",13); Person p=(Person)obj; System.out.println(obj.toString()); //2.通过反射。调用对象制定的属性和方法 Field age = classP.getDeclaredField("age"); age.set(p,10); System.out.println(p.toString()); //调用方法 Method show = classP.getDeclaredMethod("show"); show.invoke(p); //通过反射,可以调用Person类的私有结构,比如:私有的构造器,属性、方法 //调用私有构造器 Constructor cons2 = classP.getDeclaredConstructor(String.class); cons2.setAccessible(true); Person p2= (Person) cons2.newInstance("Jerry"); System.out.println(p2.toString()); //调用私有属性 Field name = classP.getDeclaredField("name"); name.setAccessible(true); name.set(p2,"Han"); System.out.println(p2.toString()); //调用私有方法 Method showNation = classP.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(p2, "中国"); System.out.println(nation); } //获取Class实例的方式(前三种需要掌握) @Test public void test3() throws ClassNotFoundException { 
    //方式一:调用运行时类的对象 Class class1=Person.class; System.out.println(class1); //方式二:通过运行时类的对象,调用其属性getClass() Person p=new Person("ton",12); Class class2=p.getClass(); System.out.println(class2); //方式三:调用Class的静态方法:forName(String classPath) 使用频率高 Class class3=Class.forName("reflectionTest.Person"); System.out.println(class3); //方式四 使用类的加载器: ClassLoder (了解) ClassLoader classLoader=ReflectionTest.class.getClassLoader(); Class<?> class4 = classLoader.loadClass("reflectionTest.Person"); System.out.println(class4); //测试运行时类实例是否是唯一的 比较地址是否一样 System.out.println(class1==class2);//true System.out.println(class2 == class3);//true } //测试那些类型可以获取Class实例 @Test public void test4(){ 
    Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // 只要素类型与维度一样,就是同一个Class System.out.println(c10 == c11); } } 
package reflectionTest; / * @author 昭浅 * @create 2022/4/21-10:05 */ public class Person { 
    private String name; int age; public Person(String name,int age){ 
    this.name= name; this.age=age; } private Person(String name){ 
    this.name=name; this.age=15; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return age; } public void setAge(int age) { 
    this.age = age; } @Override public String toString() { 
    return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void show(){ 
    System.out.println("我是一个人"); } private String showNation(String nation){ 
    System.out.println("我的国籍是:"+nation); return nation; } } 

二、了解类的加载器

package classLoaderTest; import org.junit.Test; import java.io.InputStream; import java.util.Properties; / * 了解类的加载器 * @author 昭浅 * @create 2022/4/21-12:15 */ public class ClassLoaderTest { 
    @Test public void test1(){ 
    //对于自定义类:使用系统类加载器进行加载 ClassLoader classLoader=ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //调用系统类加载器的getParent():获取扩展类加载器 ClassLoader classLoader1=classLoader.getParent(); System.out.println(classLoader1); //调用扩展类加载器的getParent():无法获取引导类加载器 //引导类加载器主要负责加载java的核心类库,无法加载自定义类的 ClassLoader classLoader3=String.class.getClassLoader(); System.out.println(classLoader3); } /* Properties:用来读取配置文件 */ @Test public void test2() throws Exception{ 
    Properties pros=new Properties(); //此时的文件默认在当前的moudle下 //读取配置文件的方式一 // FileInputStream fis=new FileInputStream("jdbc.properties"); // FileInputStream fis=new FileInputStream("src\\jdbc.properties"); // pros.load(fis); //读取配置文件的方式二 //配置文件默认识别为:当前moudle的src下 ClassLoader classLoader=ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); pros.load(is); String user = pros.getProperty("user"); String passwd = pros.getProperty("passwd"); System.out.println(user+"\n"+passwd); } } 

三、通过反射动态创建运行时类的对象

package newInstanceTest; import org.junit.Test; import reflectionTest.Person; import java.util.Random; / * 通过反射创建对应的运行时类的对象 * * @author 昭浅 * @create 2022/4/21-16:29 */ public class NewInstanceTest { 
    @Test public void test() throws InstantiationException, IllegalAccessException { 
    Class<Person> classp= Person.class; /* newInstance():调用此方法,创建对应的运行时类的对象 1.运行时类必须提供空参的构造器 2.空参的构造器的访问权限要满足调用,通常,设置为public 在javabean中要求提供一个public的空参构造器,原因: 1.便于通过反射,创建运行时类的对象 2.便于子类继承此运行时类时,默认调用super()时,保证父类由此构造器 */ Person p = classp.newInstance(); System.out.println(p); } /* 深入了解反射的动态性 */ @Test public void test2() throws ClassNotFoundException, InstantiationException, IllegalAccessException { 
    String classPath=""; for(int i=0;i<100;i++){ 
    int num= new Random().nextInt(3);//0,1,2 switch(num){ 
    case 0: classPath="java.util.Date"; break; case 1: classPath="java.lang.Object"; break; case 2: classPath="reflectionTest.Person"; break; } Object obj = getInstance(classPath); System.out.println(obj); } } public Object getInstance(String classPath) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 
    Class class1=Class.forName(classPath); Object obj = class1.newInstance(); return obj; } } 

四、获取运行时类的完整结构

1、获取运行时类的父类及其泛型

package getsupperclasstest; import getfieldsTest.Person; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; / * @author 昭浅 * @create 2022/4/21-19:06 */ public class GetSupperClassTest { 
    //获取运行时类的父类 @Test public void test() { 
    Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass); } //获取运行时类的带泛型的父类 @Test public void test2() { 
    Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); } //获取运行时类的带泛型的父类的泛型 @Test public void test3() { 
    Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = paramType.getActualTypeArguments(); System.out.println(((Class) actualTypeArguments[0]).getName()); } //获取运行时类实现的接口 @Test public void test4() { 
    Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for (Class i : interfaces) { 
    System.out.println(i); } System.out.println(); //获取运行时类父类的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for (Class c : interfaces1) { 
    System.out.println(c); } } /* 获取运行时类所在的包 */ @Test public void test5() { 
    Class clazz = Person.class; Package aPackage = clazz.getPackage(); System.out.println(aPackage); } /* 获取运行时类声明的注解 */ @Test public void test6() { 
    Class clazz = Person.class; Annotation[] annotations =clazz.getAnnotations(); for(Annotation a:annotations){ 
    System.out.println(a); } } } 

2、获取运行时类属性的结构

package getfieldsTest; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Modifier; / * @author 昭浅 * @create 2022/4/21-17:34 */ public class FieldsTest { 
    @Test public void test(){ 
    Class class1=Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = class1.getFields(); for(Field f:fields){ 
    System.out.println(f); } System.out.println("------------------------------"); //declaredFields():获取当前运行时类声明过的所有属性 Field[] declaredFields = class1.getDeclaredFields(); for(Field f:declaredFields){ 
    System.out.println(f); } } //权限修饰符 数据类型 变量名 @Test public void test2(){ 
    Class class1=Person.class; Field[] declaredFields = class1.getDeclaredFields(); //获取当前运行式类的属性的权限修饰符 for (Field f:declaredFields){ 
    int modifier = f.getModifiers(); System.out.println(Modifier.toString(modifier)); } System.out.println("------------------------"); //数据类型 for (Field f:declaredFields){ 
    Class<?> type = f.getType(); System.out.println(type.getName()); } System.out.println("------------------------"); //变量名 for (Field f:declaredFields){ 
    String name= f.getName(); System.out.println(name); } } } 

3、获取运行时类方法的完整结构

package getfieldsTest; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; / * @author 昭浅 * @create 2022/4/21-17:52 */ public class MethodTest { 
    @Test public void test1(){ 
    Class class1=Person.class; //获取当前运行时类及其所有父类中声明为public权限的方法 Method[] methods = class1.getMethods(); for(Method m:methods){ 
    System.out.println(m); } System.out.println("-----------------------"); //获取当前运行时类中声明的所有方法(不包括父类) Method[] declaredMethods = class1.getDeclaredMethods(); for (Method m:declaredMethods){ 
    System.out.println(m); } } /* @注解 权限修饰符 返回值类型 方法名(参数类型1 形参名1....)throws xxxException{} */ @Test public void test2(){ 
    Class class1=Person.class; Method[] methods = class1.getDeclaredMethods(); for(Method m:methods){ 
    //获取方法声明的注解 Annotation[] annotation = m.getAnnotations(); for (Annotation a:annotation){ 
    System.out.println(a); } //2.权限修饰符 System.out.print(Modifier.toString(m.getModifiers())+"\t"); //3.返回值类型 System.out.print(m.getReturnType().getName()+"\t"); //4.方法名 System.out.print(m.getName()); System.out.print("("); //参数列表 Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes==null && parameterTypes.length==0)){ 
    for(int i=0;i<parameterTypes.length;i++){ 
    if(i==parameterTypes.length-1){ 
    System.out.print(parameterTypes[i].getName()+"arg_"+i); break; } System.out.print(parameterTypes[i].getName()+"arg_"+i+","); } } System.out.print(")"); //6.抛出的异常 Class[] exceptionTypes = m.getExceptionTypes(); if(exceptionTypes.length>0){ 
    System.out.print("throws "); for(int i=0;i<exceptionTypes.length;i++){ 
    if(i==exceptionTypes.length-1){ 
    System.out.print(exceptionTypes[i].getName()); break; } System.out.print(exceptionTypes[i].getName()+","); } } System.out.println(); } } } 
package getfieldsTest; import java.io.Serializable; / * @author 昭浅 * @create 2022/4/21-17:25 */ public class Creature<T> implements Serializable { 
    private char gender; public double weight; private void breath(){ 
    System.out.println("生物呼吸"); } public void eat(){ 
    System.out.println("生物吃饭"); } } 
package getfieldsTest; / * @author 昭浅 * @create 2022/4/21-10:05 */ @MyAnnotation(value = "hi") public class Person extends Creature<String> implements Comparable<String>,MyInterface{ 
    private String name; int age; public int id; public Person(){ 
    } Person(String name, int age){ 
    this.name= name; this.age=age; } private Person(String name){ 
    this.name=name; } public String getName() { 
    return name; } public void setName(String name) { 
    this.name = name; } public int getAge() { 
    return age; } public void setAge(int age) { 
    this.age = age; } @Override public String toString() { 
    return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @MyAnnotation(value = "hi") public static void show() throws NullPointerException,ClassCastException { 
    System.out.println("我是一个人"); } @MyAnnotation(value = "hi") private String showNation(String nation){ 
    System.out.println("我的国籍是:"+nation); return nation; } public String display(String interests){ 
    return interests; } @Override public void info() { 
    System.out.println("我是一个人"); } @Override public int compareTo(String o) { 
    return 0; } } 
package getfieldsTest; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE; / * @author 昭浅 * @create 2022/4/21-17:31 */ @Target({ 
   TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { 
    String value() default "hello"; } 
package getfieldsTest; / * @author 昭浅 * @create 2022/4/21-17:28 */ public interface MyInterface { 
    abstract void info(); } 

五、调用运行时类的指定结构

package reflectionTest2; import getfieldsTest.Person; import org.junit.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; / * @author 昭浅 * @create 2022/4/21-20:06 */ public class ReflectionTest2 { 
    //如何操作运行时类的指定属性 不需要掌握 @Test public void test1() throws Exception { 
    Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //获取指定属性:要求当前运行时类属性声明为public //通常不采用此方法 Field id = clazz.getField("id"); //设置当前属性的值 //set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少 id.set(p, 1001); //获取当前属性的值 //get() 参数1:指明获取哪个对象的属性的值 System.out.println(id.get(p)); } /* 如何操作运行时类的指定属性 */ @Test public void test2() throws Exception { 
    Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //getDeclaredField(String name)获取指定变量名的属性 //通常不采用此方法 Field name = clazz.getDeclaredField("name"); //保证当前属性是可访问的 name.setAccessible(true); //设置当前属性的值 //set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少 name.set(p, "Tom"); //获取当前属性的值 //get() 参数1:指明获取哪个对象的属性的值 System.out.println(name.get(p)); } /* 如何操作运行时类的指定方法 */ @Test public void test3() throws Exception { 
    Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); /* 1.获取指定的某个方法 */ Method showNation = clazz.getDeclaredMethod("showNation", String.class); // 2.保证当前属性是可访问的 showNation.setAccessible(true); //3.invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参 //invoke()的返回值即为对象类中调用方法的返回值 showNation.invoke(p,"CHN"); String returnVal= (String) showNation.invoke(p,"CHN"); System.out.println(returnVal); System.out.println("如何调用静态方法*"); Method show = clazz.getDeclaredMethod("show"); show.setAccessible(true); show.invoke(Person.class); // show.invoke(null); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null String returnV= (String) show.invoke(Person.class); System.out.println(returnV);//null } /* 如何调用运行时类中指定的构造器 */ @Test public void test4() throws Exception { 
    Class clazz = Person.class; //private Person(String name) //1.获取指定的构造器 //getDeclaredConstructor():参数:指明构造器的参数列表 Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class); //保证此构造器是可访问的 declaredConstructor.setAccessible(true); //3.调用此构造器运行时类的对象 Person per=(Person) declaredConstructor.newInstance("Tom"); System.out.println(per); } } 

六、动态代理

1、静态代理举例

package proxytest; / * 静态代理举例 * @author 昭浅 * @create 2022/4/22-14:21 */ interface ClothFactory{ 
    void produceCloth(); } //代理类 class ProxyClothFactory implements ClothFactory{ 
    private ClothFactory factory; public ProxyClothFactory(ClothFactory factory){ 
    this.factory=factory; } @Override public void produceCloth() { 
    System.out.println("开始做准备"); factory.produceCloth();; System.out.println("工作完成"); } } class Nike implements ClothFactory{ 
    @Override public void produceCloth() { 
    System.out.println("Nike开始制造衣服"); } } public class StaticProxyTest { 
    public static void main(String[] args) { 
    Nike nike=new Nike(); ProxyClothFactory proxy=new ProxyClothFactory(nike); proxy.produceCloth(); } } 

2、动态代理

package proxytest; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; / * 动态代理举例 * * @author 昭浅 * @create 2022/4/22-14:26 */ interface Human { 
    void getBelief(); String eat(String food); } //被代理类 class SuperMan implements Human { 
    @Override public void getBelief() { 
    System.out.println("超人强大无比,没有信仰"); } @Override public String eat(String food) { 
    return "喜欢吃" + food; } } /* 要想实现动态代理,需要解决的问题? 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类的同名方法 */ //代理类 class ProxyFactory { 
    //调用此方法,返回一个代理类的对象,解决问题一 public static Object getProxyInstance(Object obj) { 
   //obj:被代理类的对象 MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); myInvocationHandler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), myInvocationHandler); } } class MyInvocationHandler implements InvocationHandler { 
    private Object obj;//obj:被代理类的对象 public void bind(Object obj) { 
    this.obj = obj; } //当我们通过代理类的对象,调用方法a时,就会自动调用如下的方法:invoke() //将被代理类要执行的方法a的功能声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
    //method:即为代理类对象调用的方法,此方法也就作为了被代理类要调用的方法 //obj:被代理类的对象 Object returnValue = method.invoke(obj, args); //上述方法的返回值就做为当前类中invoke()方法的返回值 return returnValue; } } public class ProxyTest { 
    public static void main(String[] args) { 
    SuperMan superMan = new SuperMan(); //代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //当通过代理类调用方法时,会自动调用被代理类中的同名方法 String eat = proxyInstance.eat("面条"); System.out.println(eat); proxyInstance.getBelief(); System.out.println(""); //体现动态创建代理类 Nike nike=new Nike(); ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(nike); proxyInstance1.produceCloth(); } } 
编程小号
上一篇 2024-12-07 17:20
下一篇 2024-12-07 17:16

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://sigusoft.com/bj/4713.html