JVA核心类库随堂笔记

1.常用类库

1.用String类对象进行字符串拼接能避免尽量避免,因为进行字符串加法拼接时,会产生很多内存垃圾。内存垃圾存储在字符串常量池中,也就是堆内存的老年代中,不会被GC回收掉。因此拼接字符串药使用:Stringbuilder和Stringbuffer这两个类对象,调用方法实现。

2.集合

1.Arraylist是数组结构,查找快,增删慢。底层是动态数组,扩容机制跟数组扩容一样。如果存储非常多的数据,建议使用带参数的构造方法,初始化集合的容量,节省扩容导致的内存占用。

2.面试题:

ArrayList集合通过无参构造方法创建时,其集合容量为0;当我们调用add方法添加元素时,会自动的进行扩容,扩容容量为10。

3.LinkedList是双向链表结构,增删快,查找慢。

4.foreach:增强for循环,用于迭代数组或者集合(单值集合Collection),foreach会自动选用最优的迭代算法(迭代器)。

5.HashSet集合:散列存放,无序存储,内部存储时利用HashMap实现的。

6.了解:Comparator接口也可以实现比较 ,Arrays.sort(T[], Comparator<? super T>)就可以传入参数为Comparator的接口,实现排序。

7.哈希表概述:对象数组+链表的结构(对象数组中包含链表)。初始桶的数量(对象数组长度):16 ,散列因子(默认加载因子):0.75(哈希表存储的容量超过0.75已经被利用时,就会进行桶的扩容)。

(hashcode)哈希值%桶的数量=哈希桶下标,根据下标存储数据。每个哈希桶的链表数量大于8时,从链表转为红黑二叉树;当哈希桶中的数据量减少到6时,从红黑二叉树转换为链表。

8.HashMap:线程不安全,效率高。HashTable:线程安全,效率低。(只有一个排队的队伍)ConcurrentHashMap:采用分段锁机制,保证线程安全,效率又比较高。(排队的队伍不止一个)

9.注意:自定义对象存储在HashMap集合的键中时,不能轻易去改变对象的属性。因为对象属性发生改变后,其存储时的HashCode值会发生改变进行重新计算,会找不到之前的对象存储的位置。

3.IO

1.文件加密或解密方法:任何数据相同的数字两次,结果就是其本身。例如:fos.write(b10);通过两次异或就可以实现加密和解密的操作。

2.字符流:用来读取单个文字为单位的数据,解决字节流只能读取字节为单位的数据。

3.utf-8编码:是一种可变化长度的字符编码,我们没办法确定一个文字由多少字节组成,而且其编码中的第一个字节兼容ASCII编码。

4.用字符输入流读取字符时,通过一次读取多个字符到char[]中保存的方式时,char[]数组里面有默认值的,默认值为空白字符。

5.使用字符输出流时,在流的管道中,会存在类似缓冲的机制,把写出的字节缓冲起来组合成单个字符,我们只有调用flush()方法,进行刷新管道,字符才会被写到硬盘中。其实close()方法在执行之前,也会自动调用flush()方法,我们也可以不写flush()方法。

6.对象以及对象的属性必须实现Serializable接口,才可以被序列化。这是一个标记的接口,里面没有任何的实现。

7.jdk 9之后,处理异常和关闭流的方法:try-with-resource

public class Demo15 {
    public static void main(String[] args) throws FileNotFoundException {
        //try-with-resources
        //jdk1.7之前
        /*try {
            FileReader fr = new FileReader("c://book.txt");
            int c = fr.read();
            System.out.println((char)c);
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }*/

        //jdk9
        FileReader fr = new FileReader("c://book.txt");
        PrintWriter pw = new PrintWriter("c://book.txt");
        try(fr;pw){  //传入的流对象必须实现Closeable接口,这段代码自动的执行close方法
            int c = fr.read();
            System.out.println((char)c);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4.多线程技术

1.每个线程都有自己的栈空间,共用一份堆内存。

2.让线程中断:可以在主线程中调用interrupt()方法,给线程添加中断标记,然后当主线程执行完以后,其他线程发现了此中断操作,此线程执行的任务就会跳到catch块,我们在catch块中加入关闭资源的方法,然后最后一行加上返回语句,此线程就会被中断。这种中断方式是比较合理的,而使用线程的stop方法会造成线程的不安全,可能会导致某些资源来不及释放。

package thread;


public class Demo5 {
    public static void main(String[] args) {
        //线程中断
        //y一个线程是一个独立的执行路径,它是否结束应该由其自身决定
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //给线程t1添加中断标记
        t1.interrupt();
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
                try {
                    Thread.sleep(1000); //调用sleep方法会抛出异常,但我们只能将其catch,因为Runnable接口没有抛出异常,而且只有在使用下列中的方法时,我们才可以选择中断线程
                   /**
                    java.lang.Object#wait()
                     java.lang.Object#wait(long)
                      java.lang.Object#wait(long, int)
                      java.lang.Thread#sleep(long)
                        java.lang.Thread#interrupt()
                         java.lang.Thread#interrupted()*/
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    System.out.println("发现了中断标记,线程自杀");
                    return;
                }
            }
        }
    }
}

3.线程:分为守护线程和用户线程

用户线程:当一个进程不包含任何的存活的用户线程时,进行结束。

守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。

//设置用户线程
t1.setDaemon(true);//当主线程死亡时,守护线程也会停止

4.线程不安全的问题:

多个线程执行一个任务的时候,交替抢夺到时间片,争抢同一个数据,会导致数据错误的问题。(其他线程执行完毕,但另一个线程还未执行完毕的时候,数据变量已经被其他线程改变)。

5.线程同步-线程安全解决方法:

*同步代码块:

package thread;


//线程同步synchronized

public class Demo8 {
    public static void main(String[] args) {
        Object o = new Object();
        //线程不安全
        //解决方案1  同步代码块
        //格式:synchronized(锁对象){
        //
        //
        //      }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            //Object o = new Object();    //这里不是同一把锁,所以锁不住,多个线程必须看同一把锁。
                while (true) {
                    synchronized (o) {
                        if (count > 0) {
                         //卖票
                            System.out.println("正在准备卖票");
                            try {
                            Thread.sleep(1000);
                            } catch (InterruptedException e) {
                            e.printStackTrace();
                            }
                            count--;
                            System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                        }else {
                            break;
                        }

                }
            }
        }
    }
}

*同步方法:

package thread;



//线程同步synchronized

public class Demo9 {
    public static void main(String[] args) {
        Object o = new Object();
        //线程不安全
        //解决方案2  同步方法
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {
			
            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){  //当前锁是this  如果synchronized前边有static修饰,锁对象就是Ticket.class
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                return true;
            }
                return false;

        }
    }
}

*显式锁

*公平锁:多个线程排队执行,先到先得


//同步代码块和同步方法都属于隐式锁
//线程同步lock

public class Demo10 {
    public static void main(String[] args) {
        Object o = new Object();
        //线程不安全
        //解决方案1   显示锁  Lock  子类 ReentrantLock

        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock(); //锁住
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }
                    l.unlock(); //解锁
            }
        }
    }
}

*线程死锁:我在等你,你在等我

package thread;


public class Demo11 {
    public static void main(String[] args) {
        //线程死锁
        Culprit c = new Culprit();
        Police p = new Police();
        new MyThread(c,p).start();
        c.say(p);
    }

    static class MyThread extends Thread{
        private Culprit c;
        private Police p;
        MyThread(Culprit c,Police p){
            this.c = c;
            this.p = p;
        }

        @Override
        public void run() {
            p.say(c);
        }
    }
    static class Culprit{
        public synchronized void say(Police p){
            System.out.println("罪犯:你放了我,我放了人质");
            p.fun();
        }
        public synchronized void fun(){
            System.out.println("罪犯被放了,罪犯也放了人质");
        }
    }
    static class Police{
        public synchronized void say(Culprit c){
            System.out.println("警察:你放了人质,我放了你");
            c.fun();
        }
        public synchronized void fun(){
            System.out.println("警察救了人质,但是罪犯跑了");
        }
    }
}

*生产者与消费

//等待唤醒机制
void notify() 唤醒正在此对象监视器上等待的单个线程。  
void notifyAll() 唤醒等待此对象监视器的所有线程。 
void wait() 导致当前线程等待它被唤醒,通常是 通知或 中断 。  
void wait​(long timeoutMillis) 导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。  
void wait​(long timeoutMillis, int nanos) 导致当前线程等待它被唤醒,通常是 通知或 中断 ,或者直到经过一定量的实时。  
package com.java.demo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo4  {

    /**
     * 多线程通信问题, 生产者与消费者问题
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndSaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();//唤醒当前对象下等待的所有线程
                try {
                    this.wait();//使当前线程等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

*线程的状态:state

QQ图片20201117211243

6.Callable:线程创建的第三种方式(了解课件)

Runnable Callable

Runnable Callable****的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable Callable****的不同点

  • Runnable没有返回值;Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

Callable****获取返回值

Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

接口定义

//Callable接口 

public interface Callable<V> { 

V call() throws Exception; 

}

//Runnable接口 

public interface Runnable { 

public abstract void run(); 

}

Callable使用步骤

  1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> { 

@Override 

public <T> call() throws Exception { 

return T; 

} 

} 
  1. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable); 
  1. 通过Thread,启动线程
new Thread(future).start();

7.线程池:看课件(了解)

线程池 Executors

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池的好处

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性。

Java中的四种线程池 . ExecutorService

1. 缓存线程池

/**

* 缓存线程池. 

* (长度无限制) 

* 执行流程: 

* 1. 判断线程池是否存在空闲线程 
* 2. 存在则使用 

* 3. 不存在,则创建线程 并放入线程池, 然后使用 

*/ 

ExecutorService service = Executors.newCachedThreadPool(); 

//向线程池中 加入 新的任务 

service.execute(new Runnable() { 

@Override 

public void run() { 

System.out.println("线程的名称:"+Thread.currentThread().getName()); 

} 

}); 

service.execute(new Runnable() { 

@Override 

public void run() { 

System.out.println("线程的名称:"+Thread.currentThread().getName()); 

} 

}); 

service.execute(new Runnable() { 

@Override 

public void run() { 

System.out.println("线程的名称:"+Thread.currentThread().getName()); 

} 

}); 

2. 定长线程池

public class Demo14 {
    /*定长线程池
    长度是指定的线程池
    加入任务后的执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }
}

3.单线程线程池

public class Demo15 {
    /*单线程线程池
    执行流程
        1 判断线程池的那个线程是否空闲
        2 空闲则使用
        3 不空闲则等待它空闲后再使用
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }

4.周期性任务定长线程池

public class Demo16 {
    /*周期任务  定长线程池
    执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程

        周期性任务执行时
                定时执行 当某个任务触发时  自动执行某任务
    **/
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定时执行一次
        //参数1:定时执行的任务
        //参数2:时长数字
        //参数3:2的时间单位    Timeunit的常量指定
       /* scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5, TimeUnit.SECONDS);      //5秒钟后执行*/

        /*
        周期性执行任务
            参数1:任务
            参数2:延迟时长数字(第一次在执行上面时间以后)
            参数3:周期时长数字(没隔多久执行一次)
            参数4:时长数字的单位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5,1,TimeUnit.SECONDS);
    }
}

8.Lambda表达式

  • 函数式编程思想概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRW4hOea-1605874612518)(E:/JAVA/JAVA资料/Java基础/07.会员版(2.0)]-就业课(2.0)-异常与多线程/18.【线程池、Lambda表达式】-笔记/就业班-day07-线程池、Lambda表达式/img/03-Overview.png)

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做

面向对象的思想:

​ 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.

函数式编程思想:

​ 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

  • Lambda的使用前提

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法
    无论是JDK内置的RunnableComparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
  2. 使用Lambda必须具有上下文推断
    也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口

代码例子:
public class Demo17 {
    /*
    lambda表达式
    函数式编程思想
    **/
    public static void main(String[] args) {
        //冗余的Runnable编写方式
       /* Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("锄禾日当午");
            }
        });
        t.start();*/

        Thread t = new Thread(() -> System.out.println("锄禾日当午"));
        t.start();
    }


}

5-网络编程

5-1 网络编程概述

5-2 TCP程序

TCP协议的C/S程序

需要使用到两个类, 来编写TCP协议的 CS程序 . 

1.ServerSocket 搭建服务器 

2.Socket 搭建客户端 

两方使用socket(套接字 , 通信端点) 进行交流

ServerSocket

用于创建服务器 . 创建完毕后, 会绑定一个端口号. 

然后此服务器可以等待客户端连接 . 

每连接一个客户端 , 服务器就会得到一个新的Socket对象, 用于跟客户端进行通信 
常用构造方法:
ServerSocket(int port); 

创建一个基于TCP/IP协议的服务器 , 并绑定指定的端口号. 

注意: 参数port的范围是: 0-65535 (建议1025-65535) 
常用方法:
Socket accept();  

等待客户端连接 . 

此方法会导致线程的阻塞! 

直到一个新的客户端连接成功, return Socket对象后, 线程在继续执行. 

void close(); 

释放占用的端口号 , 关闭服务器

Socket

是两台计算机之间通信的端点 , 是网络驱动提供给应用程序编程的一种接口 一套标准, 一种机制 .
构造方法:
Socket(String ip,int port)  

创建一个套接字, 并连接指定ip和端口号的 服务器. 

参数1. 服务器的ip地址 

参数2. 服务器软件的端口号.. 
常用方法:
- OutputStream getOutputStream(); 

返回的是 , 指向通信的另一端点的输出流 

- InputStream getInputStream(); 

返回的是 , 指向通信的另一端点的输入流 

- void close(); 

关闭套接字 

注意:

在网络编程时, 获取输入输出流的操作 ,对于客户端与服务器来说是相对的 

客户端的输入流, 输入的是服务器的输出流 输出的内容. 

客户端的暑促刘, 输出到了服务器的输入流中. 

所以 在使用时, 需要注意以下一点规则: 

客户端与服务器获取流的顺序必须是相反的: 

例如:

客户端先得到了输入流 , 那服务器必须先获取输出流

案例:

服务器端:

package com.kaikeba;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
 
public class SeverDemo {
    // 服务器
    public static void main(String[] args) throws IOException {
        // 搭建服务器
        ServerSocket server = new ServerSocket(55555);
        System.out.println("服务器启动了");
        // 等待客户端的连接
        Socket socket = server.accept();
        System.out.println("服务器连接了");
 
        // 获得输出流
        OutputStream op = socket.getOutputStream();
        // 转换为打印流
        PrintStream ps = new PrintStream(op);
        // 发送消息
        ps.println("欢迎连接服务器");
 
        System.out.println("服务器程序执行结束");
    }
}

客户端:

package com.kaikeba;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
 
public class ClientDemo {
    // 客户端
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 55555);
        InputStream is = socket.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String text = br.readLine();
        System.out.println("客户端收到:" + text);
    }
}

5-3 在服务器中加入多线程

package com.kaikeba;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
 
public class SeverDemo {
    // 服务器
    public static void main(String[] args) throws IOException {
        // 搭建服务器
        ServerSocket server = new ServerSocket(55555);
        System.out.println("服务器启动了");
        while (true) {
            // 等待客户端的连接
            Socket socket = server.accept();
            System.out.println("服务器连接了");
            new Thread(){   //开启一个线程,让该线程与客户端交流。主线程就可以继续下一次循环
                @Override
                public void run() {
                    try {
                        InputStream is = socket.getInputStream();
                        OutputStream os = socket.getOutputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
 
    }
}

5-4相关类和API

UDP协议程序(了解)

用户数据报协议, 与tcp协议不同, UDP的连接是不可信的. 数据发送的成功与失败 与 数据报是无关的. 

使用到两个类: 

1.数据报套接字: DatagramSocket 

用于发送 与 接收数据包的Socket 

- 构造方法: 

- DatagramSocket(int port); 

- 参数: 端口号 

- 常用方法: 

- close() : 关闭套接字. 

- send(DatagramPacket dp) 

将一个数据包dp 发送出去 

- receive(DatagramPacket dp) 

接收一个数据包, 并存储到参数dp中. 

2.数据包 DatagramPacket 

用于发送或接收数据时, 盛放数据的对象! 

- 构造方法: 

1.用于发送数据时, 组装数据的 构造方法. 

DatagramPacket(byte[] bytes,int startIndex,int len,InetAddress ip,int port); 

参数1. 要发送的数据, 是字节数组的形式 

参数2. 有效数据 在数组中的起始位置 

参数3. 有效数据 在数组中的长度 

参数4. 当前这个数据包, 准备发送到的IP地址, InetAddress 这个类的对象, 用于描述 

IP . 

得到InetAddress对象的方式: 

- InetAddress ip = InetAddress.getByName("192.168.102.228"); 

参数5. 当前这个数据包, 准备发送到目标计算机的哪个端口号. 

2.用于接收数据时, 存储数据的 构造方法. 

创建的是 不包含数据的数据包, 用于在接收到数据后, 存储数据 ! 

DatagramPacket(byte[] bytes,int len) 

参数1. 用于存储数据的 数组 

参数2. 允许存储的最大长度 

- 常用方法: 

byte[] getData()
用于获取数据包中的有效字节数组 
int getLength 
用于获取数据包中的有效数据的长度.

InetAddress描述IP地址的类

InetAddress 这个类的对象, 用于描述IP . 

得到InetAddress对象的方式: 

InetAddress ip = InetAddress.getByName("192.168.102.228"); 

在UDP协议中. 通过数据包DatagramPacket的getAddress方法, 可以得到数据包来自哪个ip 

在TCP协议中, 通过套接字Socket的getInetAddress方法, 可以得到套接字连接的ip地址. 

- 常用方法: 

1.String getHostAddress() 

ip地址字符串 

2.String getHostName() 

计算机名称, 当名称无法获取时, 获取的为ip地址.

URL类(了解)

案例1:下载文件
public class ClientDemo { 

public static void main(String[] args) throws Exception { 

Scanner input = new Scanner(System.in); 

System.out.println("欢迎使用嘿嘿雷下载器"); 

System.out.println("请输入要下载的文件网址:"); 

String urlString = input.nextLine(); 

System.out.println("请输入要保存的文件名称:"); 

System.out.println("(文件默认下载位置:d盘download文件夹中)"); 

String fileString = input.nextLine(); 

//1. 确保文件夹存在 

File dir = new File("d://download"); 

if(!dir.exists()) { 

dir.mkdirs(); 

}

//2. 创建一个文件输出流, 用于输出数据 

FileOutputStream fos = new FileOutputStream(new File(dir,fileString)); 

//3. 今天学习的新内容

//3.1 创建一个网址对象(统一资源定位符) 

URL url = new URL(urlString); 

//3.2 打开链接 , 并得到链接对象 

URLConnection conn = url.openConnection(); 

//3.3 通过连接对象, 获取连接到的文件的输入流 

InputStream is = conn.getInputStream(); 

//[3.4] 获取网址指向文件的 大小 

long fileLength = conn.getContentLengthLong(); 

//3.5 循环读取 并写出到fos中 

//用于存储 每次读取的数据 

byte[] bytes = new byte[1024*1024]; 

//用于存储每次读取的数据长度 

int len = -1; 

//用于存储已读取的所有数据的长度 

int count = 0; 

while((len = is.read(bytes))!=-1) { 

//将每次循环读取的bytes 写出到文件中 

fos.write(bytes,0,len); 

count+=len; 

System.out.println("下载中:"+(count/(fileLength/100))+"%"); 

}

is.close(); 

System.out.println("文件下载完毕"); 

} 

}
案例2:传输参数,并下载数据
static Scanner input = new Scanner(System.in); 

public static void main(String[] args) throws Exception { 

System.out.println("自动P图小程序:"); 

System.out.println("请选择菜单:"); 

int menu = menu(); 

System.out.println("请输入名字:"); 

String name = input.nextLine(); 

String name2 = URLEncoder.encode(name, "UTF-8"); 

//1. 得到网址, 这个网址指向的内容 是另一个图片的网址 

String urlString = "http://itdage.cn/B/img?id="+menu+"&s1="+name2; 

URL url = new URL(urlString); 

//2. 打开链接 

URLConnection conn = url.openConnection(); 

//3. 得到输入流 

//3.1 因为我们这个网址的内容 只是一个图片的地址, 也就是一行文字, 所以我们将这个流转换为逐行读取流, 

读取一行文本就可以了 

InputStream is = conn.getInputStream(); 

//3.2 转换为字符流 

InputStreamReader isr = new InputStreamReader(is); 

//3.3 转换为逐行读取流 

BufferedReader br = new BufferedReader(isr);

String imgUrlString = br.readLine(); 

System.out.println("图片已制作完成 , 地址:"+imgUrlString); 

}

public static int menu(){ 

System.out.println("1. 捐*补助");//1 

System.out.println("2. 登月插旗");//2 娃娃 

System.out.println("3. 娃娃订单");//3 娃娃 

System.out.println("4. 相思癌");//4相思癌 

System.out.println("5. 孕检证明"); 

System.out.println("6. 玛莎拉蒂订单");//6.玛莎拉蒂订单 

System.out.println("7. 马云湖畔大学");// 

System.out.println("122. 男举牌 娶你");// 

System.out.println("123. 女举牌 生猴子");// 

String text = input.nextLine(); 

int m = -1; 

try {

m = Integer.parseInt(text); 

}catch(Exception e) { 

}

if(m<1 || (m>7&&m<122) || m>123) { 

return menu(); 

}

return m; 

}

);

String name = input.nextLine();

String name2 = URLEncoder.encode(name, “UTF-8”);

//1. 得到网址, 这个网址指向的内容 是另一个图片的网址

String urlString = “http://itdage.cn/B/img?id=”+menu+"&s1="+name2;

URL url = new URL(urlString);

//2. 打开链接

URLConnection conn = url.openConnection();

//3. 得到输入流

//3.1 因为我们这个网址的内容 只是一个图片的地址, 也就是一行文字, 所以我们将这个流转换为逐行读取流,

读取一行文本就可以了

InputStream is = conn.getInputStream();

//3.2 转换为字符流

InputStreamReader isr = new InputStreamReader(is);

//3.3 转换为逐行读取流

BufferedReader br = new BufferedReader(isr);

String imgUrlString = br.readLine();

System.out.println(“图片已制作完成 , 地址:”+imgUrlString);

}

public static int menu(){

System.out.println(“1. 捐*补助”);//1

System.out.println(“2. 登月插旗”);//2 娃娃

System.out.println(“3. 娃娃订单”);//3 娃娃

System.out.println(“4. 相思癌”);//4相思癌

System.out.println(“5. 孕检证明”);

System.out.println(“6. 玛莎拉蒂订单”);//6.玛莎拉蒂订单

System.out.println(“7. 马云湖畔大学”);//

System.out.println(“122. 男举牌 娶你”);//

System.out.println(“123. 女举牌 生猴子”);//

String text = input.nextLine();

int m = -1;

try {

m = Integer.parseInt(text);

}catch(Exception e) {

}

if(m<1 || (m>7&&m<122) || m>123) {

return menu();

}

return m;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值