se-高级

158 阅读15分钟

多线程

概念

程序 概念:完成特定任务、用某种语言编写的一组指令集合。一段静态代码

进程 概念:程序的一次执行过程

线程 概念:进程可进一步细化为线程,是一个程序内部的一条执行路径

并行 概念:多个cpu同时执行多个任务

并发 概念:一个cpu同时执行多个任务

创建多线程的两种方式

方式一:继承Thread类的方式

①:创建继承于Thread类的子类
②:重写Thread类的run() --> 将此线程执行的操作声明在run()中
③:创建Thread类的子类对象
④:通过此对象调用start():启动当前线程->调用run()

方式二:实现Runnable接口的方法

①:创建一个实现Runnable接口的类
②:实现类去实现Runnable中的抽象方法:run()
③:创建实现类的对象
④:将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
⑤:通过Thread类的对象调用start()

两种方式的对比

开发中:优先选择:实现Runnable接口的方式

原因:

①:实现的方式没有类的单继承性的局限性
②:实现的方式更适合来处理多个线程共享数据的情况

Thread类中常用方法

   1.start():启动当前线程,调用当前线程的run()

   2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中

   3.currentThread():静态方法,返回执行当前代码的线程

   4.getName():获取当前线程名字

   5.setName():设置当前线程名字

   6.yield():释放当前cpu的执行权

   7.join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态

   8.stop():已过时,当执行此方法时,强制结束当前线程

   9.sleep(long milltime):让当前线程“睡眠”,进入阻塞状态

   10.isAlive():判断当前线程是否存活

线程的优先级

1.  -  MAX_PRIORITY:10
    -  MIN _PRIORITY:1
    -  NORM_PRIORITY:5  -->默认优先级
2.如何获取和设置当前线程的优先级:
     getPriority():获取线程的优先级
     setPriority(int p):设置线程的优先级

说明:优先级高的线程要抢占优先级低的线程cpu执行权。但是是从概率上将,优先级高的执行,并不意味着执行完后,低优先级线程才执行

线程通信

wait()、notify()、notifyAll():此三个方法定义在Object类中

线程的声明周期

线程的声明周期1.bmp

线程的同步机制

方式一:同步代码块
    synchronized(同步监视器){
        //需要被同步的代码
    }    
    说明:
        1.操作共享数据的代码,即为需要被同步的代码
        2.共享数据:多个线程共同操作的变量
        3.同步监视器:锁,任何一个类的对象都可以充当锁 要求:多个线程必须要把共同一把锁
    补充:
         在实现Runnable接口创建多个线程的方式中,我们可以考虑使用this充当同步监视器(锁)
         在继承Thread类创建多线程的方式中,慎用this充当同步监视器
 方式二:同步方法
     如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的
     关于不同方法的总结:
         1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明
         2.非静态的同步方法,同步监视器是:this
            静态的同步方法,同步监视器是: 当前类本身
  方式三:Lock锁 -- JDK5新增
      1. 面试题:synchronized 与 Lock的异同
         相同:两耳都可以解决线程安全问题
         不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
         lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
         

线程安全的单例模式(懒汉式)

class Bank{
    private Bank(){
    }
    
    private static Bank instance = null;
    
    public static Bank getInstance(){
        if(null == instance){
            synchronized(Bank.class){
                instance = new Bank();
            }
        }
    }
    return instance;
}

死锁问题

    1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
    2.说明:
        ①:出现死锁后,不会出现异常,不会出现提示,只是锁的线程都处于阻塞状态,无法继续
        ②:我们使用同步时,要避免出现死锁
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.printStatckTrace();
                }
                
                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.printStatckTrace();
                }
                
                synchronized(s1){
                    s1.append("d");
                    s2.append("4");
                    
                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }
    }).start();
    

}

线程通信

1.线程通信涉及到的三个方法:
    wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器
    notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程wait,就唤醒优先级高的
    notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
 2.说明:
     ①.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
     ②.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
       否则,会出现IllegalMonitorStateException异常
     ③.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。   
  3.面试题:sleep() 和 wait()的异同?
     ①.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
     ②.不同点:1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
              2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
              3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。  
           

JDK5新增线程创建方式

方式一:实现Callable接口

//1.创建一个实现Callable的实现类
class NumThread implements Callable{
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception{
        int sum = 0;
        for(int i = 0; i<= 100; i++){
            if(i % 2 ==0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew{
    public static void main(String[] args){
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();
        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask();
        //5.将FutureTack的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();
        
        try{
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTack构造器参数实现类重写的call()的返回值
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        }catch(InterruptedException e){
            e.printStackTrace();
        }catch(ExceutionException e){
            e.printStackTrace();
        }
    }
}

说明:
 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
 1. call()可以返回值的。
 2. call()可以抛出异常,被外面的操作捕获,获取异常的信息
 3. Callable是支持泛型的

方式二:使用线程池

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{

    @Overrider
    public void run(){
        for(int i = 0; i <= 100; i++){
            System.out.println(Thread.currentThread().getName + ":" + i);
        }
    }
}


pbulic class ThreadPool{

    public static void main(String[] args){
        //1.提供指定线程数量的线程池
        ExecutorSerive serive = Executors.newFixedThreadPllo(10);
        ThreadPoolRxecutor service1 = (ThreadPoolExecutor) service;
        
        //2.执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());
        service.execute(new NumberThread1());
        
        //3.关闭连接池
        service.shutdown();
    }
}
说明:
 好处:
 1.提高响应速度(减少了创建新线程的时间)
 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 3.便于线程管理
      corePoolSize:核心池的大小
      maximumPoolSize:最大线程数
      keepAliveTime:线程没任务时最多保持多长时间后会终止

集合

集合1.bmp

set集合

1. 存储的数据特点:无序的、不可重复的元素
具体的:
以HashSet为例说明:
1. 无序性:不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的。
2. 不可重复性:保证添加的元素照equals()判断时,不能返回true.即:相同的元素只能添加一个。

map集合

1.常用实现类结构
|----Map:双列数据,存储key-value对的数据   ---类似于高中的函数:y = f(x)
       |----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
              |----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。
                    原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
                    对于频繁的遍历操作,此类执行效率高于HashMap。
       |----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
                      底层使用红黑树
       |----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
              |----Properties:常用来处理配置文件。key和value都是String类型
    
      HashMap的底层:数组+链表  (jdk7及之前)
                    数组+链表+红黑树 (jdk 8)

IO流

File类的使用

1.File类的理解
 1. File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
 2. File类声明在java.io包下
 3. File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,
    并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。
 4. 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点".
2.File的实例化
2.1 常用构造器
File(String filePath)
File(String parentPath,String childPath)
File(File parentFile,String childPath)

2.2 路径的分类
相对路径:相较于某个路径下,指明的路径。
绝对路径:包含盘符在内的文件或文件目录的路径

说明:
IDEA中:
如果大家开发使用JUnit中的单元测试方法测试,相对路径即为当前Module下。
如果大家使用main()测试,相对路径即为当前的Project下。

2.3 路径分隔符
windows和DOS系统默认使用“\”来表示
UNIX和URL使用“/”来表示
3.File类的常用方法
3.File类的常用方法

file方法1.bmp

file方法2.bmp

file方法3.bmp

IO流的概述

1.流的分类
     1.操作数据单位:字节流、字符流
     2.数据的流向:输入流、输出流
     3.流的角色:节点流、处理流

图示:

io1.bmp

2.流的体系结构

io2.bmp

    说明:红框对应的是IO流中的4个抽象基类。
    蓝框的流需要大家重点关注。
3.重点说明的几个流结构

io3.bmp

4.输入、输出的标准化过程
    4.1 输入过程
        ① 创建File类的对象,指明读取的数据的来源。(要求此文件一定要存在)
        ② 创建相应的输入流,将File类的对象作为参数,传入流的构造器中
        ③ 具体的读入过程:
            创建相应的byte[] 或 char[]。
        ④ 关闭流资源
        说明:程序中出现的异常需要使用try-catch-finally处理。
    4.2 输出过程
        ① 创建File类的对象,指明写出的数据的位置。(不要求此文件一定要存在)
        ② 创建相应的输出流,将File类的对象作为参数,传入流的构造器中
        ③ 具体的写出过程:
            write(char[]/byte[] buffer,0,len)
        ④ 关闭流资源
        说明:程序中出现的异常需要使用try-catch-finally处理。

节点流(或文件流)

1.FileReader/FileWriter的使用:
1.1 FileReader的使用

    将day09下的hello.txt文件内容读入程序中,并输出到控制台

    说明点:
    1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
    2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
    3. 读入的文件一定要存在,否则就会报FileNotFoundException。
    @Test
        public void testFileReader1()  {
            FileReader fr = null;
            try {
                //1.File类的实例化
                File file = new File("hello.txt");

                //2.FileReader流的实例化
                fr = new FileReader(file);

                //3.读入的操作
                //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
                char[] cbuf = new char[5];
                int len;
                while((len = fr.read(cbuf)) != -1){
                    //正确的写法
                    String str = new String(cbuf,0,len);
                    System.out.print(str);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(fr != null){
                    //4.资源的关闭
                    try {
                        fr.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }

        }
1.2 FileWriter的使用

    从内存中写出数据到硬盘的文件里。

    说明:
    1. 输出操作,对应的File可以不存在的。并不会报异常
    2.
         File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
         File对应的硬盘中的文件如果存在:
               如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原文件的覆盖
               如果流使用的构造器是:FileWriter(file,true):不会对原文件覆盖,而是在原文件基础上追加内容
    @Test
    public void testFileWriter() {
        FileWriter fw = null;
        try {
            //1.提供File类的对象,指明写出到的文件
            File file = new File("hello1.txt");

            //2.提供FileWriter的对象,用于数据的写出
            fw = new FileWriter(file,false);

            //3.写出的操作
            fw.write("I have a dream!\n");
            fw.write("you need to have a dream!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流资源的关闭
            if(fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    1.3 文本文件的复制:
    @Test
        public void testFileReaderFileWriter() {
            FileReader fr = null;
            FileWriter fw = null;
            try {
                //1.创建File类的对象,指明读入和写出的文件
                File srcFile = new File("hello.txt");
                File destFile = new File("hello2.txt");

                //2.创建输入流和输出流的对象
                 fr = new FileReader(srcFile);
                fw = new FileWriter(destFile);

                //3.数据的读入和写出操作
                char[] cbuf = new char[5];
                int len;//记录每次读入到cbuf数组中的字符的个数
                while((len = fr.read(cbuf)) != -1){
                    //每次写出len个字符
                    fw.write(cbuf,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //4.关闭流资源
                try {
                    if(fw != null)
                        fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                try {
                    if(fr != null)
                        fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
2.FileInputStream / FileOutputStream的使用:
 1. 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
 2. 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理
    /*
    实现对图片的复制操作
     */
    @Test
    public void testFileInputOutputStream()  {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1.造文件
            File srcFile = new File("爱情与友情.jpg");
            File destFile = new File("爱情与友情2.jpg");

            //2.造流
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //3.复制的过程
            byte[] buffer = new byte[5];
            int len;
            while((len = fis.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                //4.关闭流
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
【注意】
相对路径在IDEA和Eclipse中使用的区别?
    IDEA:
        如果使用单元测试方法,相对路径基于当前的Module的。
        如果使用main()测试,相对路径基于当前Project的。

    Eclipse:
        单元测试方法还是main(),相对路径都是基于当前Project的。

缓冲流的使用

1.缓冲流涉及到的类:
 BufferedInputStream
 BufferedOutputStream
 BufferedReader
 BufferedWriter
2.作用:

作用:提供流的读取、写入的速度 提高读写速度的原因:内部提供了一个缓冲区。默认情况下是8kb

缓冲流1.bmp

3.典型代码
3.1 使用BufferedInputStream和BufferedOutputStream:处理非文本文件
//实现文件复制的方法
    public void copyFileWithBuffered(String srcPath,String destPath){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1.造文件
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream((srcFile));
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、写入
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
//        fos.close();
//        fis.close();
        }
    }
3.2 使用BufferedReader和 BufferedWriter:处理文本文件
@Test
    public void testBufferedReaderBufferedWriter(){
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //创建文件和相应的流
            br = new BufferedReader(new FileReader(new File("dbcp.txt")));
            bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));         

            //方式二:使用String
            String data;
            while((data = br.readLine()) != null){
                //方法一:
//                bw.write(data + "\n");//data中不包含换行符
                //方法二:
                bw.write(data);//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();
                }
            }
        }
    }

转换流的使用

1.转换流涉及到的类:属于字符流
    InputStreamReader:将一个字节的输入流转换为字符的输入流
    解码:字节、字节数组  --->字符数组、字符串

    OutputStreamWriter:将一个字符的输出流转换为字节的输出流
    编码:字符数组、字符串 ---> 字节、字节数组

    说明:编码决定了解码的方式

    2.作用:提供字节流与字符流之间的转换

    3.图示:

转换流1.bmp

    4.典型实现:
            public void test1() throws IOException {

                FileInputStream fis = new FileInputStream("dbcp.txt");
        //        InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集
                //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集
                InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用系统默认的字符集

                char[] cbuf = new char[20];
                int len;
                while((len = isr.read(cbuf)) != -1){
                    String str = new String(cbuf,0,len);
                    System.out.print(str);
                }

                isr.close();

            }

        /*
        此时处理异常的话,仍然应该使用try-catch-finally

        综合使用InputStreamReader和OutputStreamWriter
         */
        @Test
        public void test2() throws Exception {
            //1.造文件、造流
            File file1 = new File("dbcp.txt");
            File file2 = new File("dbcp_gbk.txt");

            FileInputStream fis = new FileInputStream(file1);
            FileOutputStream fos = new FileOutputStream(file2);

            InputStreamReader isr = new InputStreamReader(fis,"utf-8");
            OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");

            //2.读写过程
            char[] cbuf = new char[20];
            int len;
            while((len = isr.read(cbuf)) != -1){
                osw.write(cbuf,0,len);
            }

            //3.关闭资源
            isr.close();
            osw.close();


        }
    5.说明:
    //文件编码的方式(比如:GBK),决定了解析时使用的字符集(也只能是GBK)。

Path、Paths、Files的使用

1.NIO的使用说明:
>Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java 
IO AP。
>NIO与原来的IO同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于
通道的IO操作。
>NIO将以更加高效的方式进行文件的读写操作。
>随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。
2.Path的使用 ---jdk7提供
2.1Path的说明:
Path替换原有的File类。
2.2如何实例化:

1.bmp

 2.3常用方法:

2.bmp

3.Files工具类 ---jdk7提供
3.1作用:
操作文件或文件目录的工具类
3.2常用方法:

3.bmp

4.bmp

网络编程

InetAddress类的使用

一、实现网络通信需要解决的两个问题
* 1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
* 2.找到主机后如何可靠高效地进行数据传输
二、网络通信的两个要素:
* 1.对应问题一:IP和端口号
* 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)
三、通信要素一:IP和端口号
1.IP的理解
* 1. IP:唯一的标识 Internet 上的计算机(通信实体)
* 2. 在Java中使用InetAddress类代表IP
* 3. IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
* 4. 域名:   www.baidu.com   www.mi.com  www.sina.com  www.jd.com
*           
     域名解析:域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析
* 5. 本地回路地址:127.0.0.1 对应着:localhost
*

2.InetAddress类:此类的一个对象就代表着一个具体的IP地址
2.1实例化
getByName(String host) 、 getLocalHost()

2.2常用方法
getHostName() / getHostAddress()

3.端口号:正在计算机上运行的进程。
* 要求:不同的进程不同的端口号
* 范围:被规定为一个 16 位的整数 0~65535。

端口号与IP地址的组合得出一个网络套接字:Socket
四、通信要素二:网络通信协议
1. 分型模型

2.TCP和UDP的区别

网络编程1.bmp 3.TCP三次握手和四次挥手

网络编程2.bmp

网络编程3.bmp

TCP网络编程

代码示例1:客户端发送信息给服务端,服务端将数据显示在控制台上
//客户端
    @Test
    public void client()  {
        Socket socket = null;
        OutputStream os = null;
        try {
            //1.创建Socket对象,指明服务器端的ip和端口号
            InetAddress inet = InetAddress.getByName("192.168.14.100");
            socket = new Socket(inet,8899);
            //2.获取一个输出流,用于输出数据
            os = socket.getOutputStream();
            //3.写出数据的操作
            os.write("你好,我是客户端mm".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源的关闭
            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 baos = null;
        try {
            //1.创建服务器端的ServerSocket,指明自己的端口号
            ss = new ServerSocket(8899);
            //2.调用accept()表示接收来自于客户端的socket
            socket = ss.accept();
            //3.获取输入流
            is = socket.getInputStream();

            //不建议这样写,可能会乱码
//        byte[] buffer = new byte[1024];
//        int len;
//        while((len = is.read(buffer)) != -1){
//            String str = new String(buffer,0,len);
//            System.out.print(str);
//        }
            //4.读取输入流中的数据
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[5];
            int len;
            while((len = is.read(buffer)) != -1){
                baos.write(buffer,0,len);
            }

            System.out.println(baos.toString());

            System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress() + "的数据");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(baos != null){
                //5.关闭资源
                try {
                    baos.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();
                }
            }

        }

    }

代码示例2:客户端发送文件给服务端,服务端将文件保存在本地。
/*
这里涉及到的异常,应该使用try-catch-finally处理
 */
@Test
public void client() throws IOException {
    //1.
    Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
    //2.
    OutputStream os = socket.getOutputStream();
    //3.
    FileInputStream fis = new FileInputStream(new File("beauty.jpg"));
    //4.
    byte[] buffer = new byte[1024];
    int len;
    while((len = fis.read(buffer)) != -1){
        os.write(buffer,0,len);
    }
    //5.
    fis.close();
    os.close();
    socket.close();
}

/*
这里涉及到的异常,应该使用try-catch-finally处理
 */
@Test
public void server() throws IOException {
    //1.
    ServerSocket ss = new ServerSocket(9090);
    //2.
    Socket socket = ss.accept();
    //3.
    InputStream is = socket.getInputStream();
    //4.
    FileOutputStream fos = new FileOutputStream(new File("beauty1.jpg"));
    //5.
    byte[] buffer = new byte[1024];
    int len;
    while((len = is.read(buffer)) != -1){
        fos.write(buffer,0,len);
    }
    //6.
    fos.close();
    is.close();
    socket.close();
    ss.close();

}

代码示例3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。并关闭相应的连接。

/*
    这里涉及到的异常,应该使用try-catch-finally处理
     */
@Test
public void client() throws IOException {
    //1.
    Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
    //2.
    OutputStream os = socket.getOutputStream();
    //3.
    FileInputStream fis = new FileInputStream(new File("beauty.jpg"));
    //4.
    byte[] buffer = new byte[1024];
    int len;
    while((len = fis.read(buffer)) != -1){
        os.write(buffer,0,len);
    }
    //关闭数据的输出
    socket.shutdownOutput();

    //5.接收来自于服务器端的数据,并显示到控制台上
    InputStream is = socket.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] bufferr = new byte[20];
    int len1;
    while((len1 = is.read(buffer)) != -1){
        baos.write(buffer,0,len1);
    }

    System.out.println(baos.toString());

    //6.
    fis.close();
    os.close();
    socket.close();
    baos.close();
}

/*
这里涉及到的异常,应该使用try-catch-finally处理
 */
@Test
public void server() throws IOException {
    //1.
    ServerSocket ss = new ServerSocket(9090);
    //2.
    Socket socket = ss.accept();
    //3.
    InputStream is = socket.getInputStream();
    //4.
    FileOutputStream fos = new FileOutputStream(new File("beauty2.jpg"));
    //5.
    byte[] buffer = new byte[1024];
    int len;
    while((len = is.read(buffer)) != -1){
        fos.write(buffer,0,len);
    }

    System.out.println("图片传输完成");

    //6.服务器端给予客户端反馈
    OutputStream os = socket.getOutputStream();
    os.write("你好,美女,照片我已收到,非常漂亮!".getBytes());

    //7.
    fos.close();
    is.close();
    socket.close();
    ss.close();
    os.close();

}

URL 编程

1.URL(Uniform Resource Locator)的理解:
统一资源定位符,对应着互联网的某一资源地址
2.URL的5个基本结构:
*  http://localhost:8080/examples/beauty.jpg?username=Tom
*  协议   主机名    端口号  资源地址           参数列表
3.如何实例化:
URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");
4.常用方法:

url1.bmp

5.可以读取、下载对应的url资源:
    public static void main(String[] args) {

        HttpURLConnection urlConnection = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            URL url = new URL("http://localhost:8080/examples/beauty.jpg");

            urlConnection = (HttpURLConnection) url.openConnection();

            urlConnection.connect();

            is = urlConnection.getInputStream();
            fos = new FileOutputStream("day10\\beauty3.jpg");

            byte[] buffer = new byte[1024];
            int len;
            while((len = is.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }

            System.out.println("下载完成");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(urlConnection != null){
                urlConnection.disconnect();
            }
        }
    }

反射机制

获取运行时类的完整结构

@Test
public void test1(){

    Class clazz = Person.class;

    //获取属性结构
    //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
    Field[] fields = clazz.getFields();
    for(Field f : fields){
        System.out.println(f);
    }
    System.out.println();

    //getDeclaredFields():获取当前运行时类中声明的所属性。(不包含父类中声明的属性
    Field[] declaredFields = clazz.getDeclaredFields();
    for(Field f : declaredFields){
        System.out.println(f);
    }
}

@Test
public void test1(){

    Class clazz = Person.class;

    //getMethods():获取当前运行时类及其所父类中声明为public权限的方法
    Method[] methods = clazz.getMethods();
    for(Method m : methods){
        System.out.println(m);
    }
    System.out.println();
    //getDeclaredMethods():获取当前运行时类中声明的所方法。(不包含父类中声明的方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for(Method m : declaredMethods){
        System.out.println(m);
    }
}

/*
    获取构造器结构

     */
    @Test
    public void test1(){

        Class clazz = Person.class;
        //getConstructors():获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():获取当前运行时类中声明的所的构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
            System.out.println(c);
        }

    }

    /*
    获取运行时类的父类

     */
    @Test
    public void test2(){
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

    /*
    获取运行时类的带泛型的父类

     */
    @Test
    public void test3(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

    /*
    获取运行时类的带泛型的父类的泛型

    代码:逻辑性代码  vs 功能性代码
     */
    @Test
    public void test4(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //获取泛型类型
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
//        System.out.println(actualTypeArguments[0].getTypeName());
        System.out.println(((Class)actualTypeArguments[0]).getName());
    }

    /*
    获取运行时类实现的接口
     */
    @Test
    public void test5(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }

        System.out.println();
        //获取运行时类的父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
            System.out.println(c);
        }

    }
    /*
        获取运行时类所在的包

     */
    @Test
    public void test6(){
        Class clazz = Person.class;

        Package pack = clazz.getPackage();
        System.out.println(pack);
    }

    /*
        获取运行时类声明的注解

     */
    @Test
    public void test7(){
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation annos : annotations){
            System.out.println(annos);
        }
    }

调用运行时类的指定结构

   @Test
public void testField1() throws Exception {
    Class clazz = Person.class;

    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();

    //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    Field name = clazz.getDeclaredField("name");

    //2.保证当前属性是可访问的
    name.setAccessible(true);
    //3.获取、设置指定对象的此属性值
    name.set(p,"Tom");

    System.out.println(name.get(p));
}
调用指定的方法:
 @Test
    public void testMethod() throws Exception {

        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
         */
        Method show = clazz.getDeclaredMethod("show", String.class);
        //2.保证当前方法是可访问的
        show.setAccessible(true);

        /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
        System.out.println(returnValue);

        System.out.println("*************如何调用静态方法*****************");

        // private static void showDesc()

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //如果调用的运行时类中的方法没返回值,则此invoke()返回null
//        Object returnVal = showDesc.invoke(null);
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);//null

    }

调用指定的构造器:
@Test
public void testConstructor() throws Exception {
    Class clazz = Person.class;

    //private Person(String name)
    /*
    1.获取指定的构造器
    getDeclaredConstructor():参数:指明构造器的参数列表
     */

    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    //2.保证此构造器是可访问的
    constructor.setAccessible(true);

    //3.调用此构造器创建运行时类的对象
    Person per = (Person) constructor.newInstance("Tom");
    System.out.println(per);
} 

JDK8 新特性

Lambda表达式

1.Lambda表达式使用前后的对比:
    @Test
    public void test1(){

        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱北京天安门");
            }
        };

        r1.run();

        System.out.println("***********************");

        Runnable r2 = () -> System.out.println("我爱北京故宫");

        r2.run();
    }

    举例二:
    @Test
    public void test2(){

        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };

        int compare1 = com1.compare(12,21);
        System.out.println(compare1);

        System.out.println("***********************");
        //Lambda表达式的写法
        Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);

        int compare2 = com2.compare(32,21);
        System.out.println(compare2);


        System.out.println("***********************");
        //方法引用
        Comparator<Integer> com3 = Integer :: compare;

        int compare3 = com3.compare(32,21);
        System.out.println(compare3);
    }
2.Lambda表达式的基本语法:
 1.举例: (o1,o2) -> Integer.compare(o1,o2);
 2.格式:
      -> :lambda操作符 或 箭头操作符
      ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表
      ->右边:lambda体 (其实就是重写的抽象方法的方法体
3.如何使用:分为六种情况

lambda1.bmp

lambda2.bmp

总结六种情况:
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只一个参数,其一对()也可以省略
->右边:lambda体应该使用一对{}包裹;如果lambda体只一条执行语句(可能是return语句,省略这一对{}和return关键字

Stream

Stream实例化
    //创建 Stream方式一:通过集合
        @Test
        public void test1(){
            List<Employee> employees = EmployeeData.getEmployees();

    //        default Stream<E> stream() : 返回一个顺序流
            Stream<Employee> stream = employees.stream();

    //        default Stream<E> parallelStream() : 返回一个并行流
            Stream<Employee> parallelStream = employees.parallelStream();

        }

        //创建 Stream方式二:通过数组
        @Test
        public void test2(){
            int[] arr = new int[]{1,2,3,4,5,6};
            //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
            IntStream stream = Arrays.stream(arr);

            Employee e1 = new Employee(1001,"Tom");
            Employee e2 = new Employee(1002,"Jerry");
            Employee[] arr1 = new Employee[]{e1,e2};
            Stream<Employee> stream1 = Arrays.stream(arr1);

        }
        //创建 Stream方式三:通过Stream的of()
        @Test
        public void test3(){

            Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

        }

        //创建 Stream方式四:创建无限流
        @Test
        public void test4(){

    //      迭代
    //      public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
            //遍历前10个偶数
            Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);


    //      生成
    //      public static<T> Stream<T> generate(Supplier<T> s)
            Stream.generate(Math::random).limit(10).forEach(System.out::println);

        }

中间操作

stream1.bmp

stream2.bmp

stream3.bmp

终止操作

stream4.bmp

stream5.bmp

stream6.bmp

stream7.bmp

Collector需要使用Collectors提供实例

stream9.bmp