第五、六周学习总结

目录

一.集合(下)

二.I/O流

三.多线程

四.网络编程

五.反射


一.集合(下)

Set接口下的集合不能存储重复元素,并且是无序的,存储顺序和遍历顺序不相同
Set接口常用的实现类有HashSet和TreeSet

  • HashSet类

HashSet底层依靠HashMap<K,V>实现
它可以保证存储的数据唯一不重复,依靠Object类的equals和hashCode()来实现,所以对于自定义类,必须重写这两个方法,HashSet才可以识别出重复元素

常用方法:

//继承并重写自
Collectionboolean add(E e)//将指定元素添加到集合中
clear()//清空集合中所有元素
boolean contains(Object o)//判断集合中是否包含该元素
boolean isEmpty()//判断集合中是否为空
Iterator<E> iterator()//返回该集合的迭代器
int size()//返回该集合中元素的个数
  • TreeSet<E>类

TreeSet<E>底层依靠TreeMap<K,V>实现
TreeSet<E>不仅可以保证存储的数据唯一,而且可以对其进行排序.
但是需要E(存储的类)实现Comparable接口中的compare方法(该方法返回一个int数据,负数表示新数据大于当前数据,往后排;0表示两个数据相同,新数据不插入;正数表示新数据小于当前数据,往前排);
或者在定义集合时,将自定义的比较器对象(Comparator类的具体子类对象)作为参数传递进去,也可以实现去重和排序.(如果比较的逻辑只使用一次,建议使用匿名内部类/lambda表达式)

TreeSet的排序底层数据结构为红黑二叉树结构,以第一个存储的数据为根节点,新进数据依次与根节点/次级节点比较


遍历时,优先查找最左侧节点,然后依次为上级节点,上级节点的右子树的最左侧节点,以此类推

常用方法:

//与HashSet基本相同
  • 双例集合的根接口为Map(图),它的常用子实现类有HashMap<K,V>和TreeMap<K,V>

Map集合中的数据以键值对的形式存在,其中键(K/Key)为主要元素,必须唯一,TreeMap在对键值对排序时,本质就是对K的排序
对Map的遍历有两种方法:
1)通过keySet()方法获取键集,然后遍历该键集,使用,get(K k)方法获取键对应的值
2)通过entrySet()方法获取键值对集,然后遍历该集,使用getKey()/getValue()方法获取键和值

  • HashMap类

HashSet底层依靠HashMap实现,所以两者类似,无序且键唯一

常用方法:

//继承并重写自Map的方法
V put(K key, V value)//给集合中添加一个键值对的映射并返回值
V remove(Object key)//从集合中删除key对应的映射,并返回V
boolean remove(Object key, Object value)//从集合中删除该K,V对应的映射
int size()//返回集合中存储的键值映射数
Set<Map.Entry<K,V>> entrySet()//返回集合的键值对集,可用于遍历
Set<K> keySet()//返回键集,可用于遍历
boolean isEmpty()//判断集合是否为空
V replace(K key, V value)//修改K对应的V,返回原来的V
boolean replace(K key, V oldValue, V newValue)//修改K对应的V
  • TreeMap类

TreeSet底层依赖TreeMap实现,所以两者类似,无序,键唯一且排序
要实现对键的排序,需要的条件与TreeSet相同

常用方法

//与HashMap类似

二.I/O流

  • 什么是I/O流

I/O流指的是数据在外部和程序内部之间的流通过程.
按流向可以分为输入流(从外部流入程序)和输出流(从程序流向外部);
按流中数据类型分为字节流(适用于所有类型文件)和字符流(适用于文本类型的文件);
按是否存在缓冲分为缓冲流和非缓冲流.

  • File类

File类用于对磁盘文件进行复制/粘贴/删除等基本操作

常用方法:

File(String pathname)//File类的构造方法,传一个路径名,可以是文件夹,也可以是文件
boolean createNewFile()//如果文件不存在,创建
boolean delete()//删除当前对象代表的目录或文件,仅删除空文件夹,非空文件夹可以递归删除
boolean exists()//测试当前对象代表的目录或文件夹是否存在
String getName()//返回当前对象代表的目录或文件夹的名字
String getPath()//返回当前对象代表的目录或文件夹路径的字符串
boolean isDirectory()//测试当前对象是否文件夹
boolean isFile()//测试当前对象是否文件
String[] list()//返回当前对象代表的文件夹下所有的文件夹和文件的名字字符串数组,不包含子文件夹中的
File[] listFiles()//返回当前对象代表的文件夹下所有的文件夹和文件的File类型数组,不包含子文件夹中的
boolean mkdir()//创建单级目录,之前目录必须存在
boolean mkdirs()//创建多级目录
  • 字节流

InputStream / OutputStream 两个抽象类是所有字节流具体类的父类
它们常用的子类有两种,一种是简单字节流FileInputStream / FileOutputStream;
另外一种是缓冲字节流BufferedInputStream / BufferedOutputStream;
区别在于简单字节流没有缓冲区,故读取较慢;
缓冲字节流存在一个默认大小为8192字节的缓冲区,读写速度较快;

常用方法:

public FileInputStream(File file)//FileInputStream的构造方法,读取对象为指定File对象
public FileInputStream(String name)//FileInputStream的构造方法,读取对象为指定路径的文件
public BufferedInputStream(InputStream in)//BufferedInputStream的构造方法,需要一个InputStream对象
//输出流具有与以上相同的构造方法,当输出流指定路径的文件不存在时,会自动创建

public FileOutputStream(String name,boolean append)//append为true表示以扩展的方式输出(不会覆盖文件原有内容)
public int read()//一次读入一个字节,如果到达文件末尾,返回-1
public int read(byte[] b)//一次读入一个字节数组,返回值为实际读入的字节数,如果到达文件末尾,返回-1
public void write(int b)//写入一个字节
public void write(byte[] b)//写入一个字节数组
public void write(byte[] b,int off, int len)//基于off的偏移量写入字节数组中len长度的内容public void flush()//刷新缓冲区,将缓冲区中的内容写入
public void close()//关闭流//具有缓冲区的类必须刷新或者关闭流才能将缓冲区中的数据真正写入
  • 字符流

Reader / Writer 两个抽象类是所有字符流具体类的父类
它们常用的子类有三种,一种是简单字符流 FileReader / FileWriter;
一种是转换字符流InputStreamReader / OutputStreamWriter,用于在字节流和字符流之间进行转换;
还有一种是缓冲字符流BufferedReader / BufferedWriter,拥有一个默认大小为8162个字符的缓冲区

常用方法:

public FileReader(String fileName)//FileReader的构造方法
public InputStreamReader(InputStream in)//InputStreamReader的构造方法,需要一个InputStream对象
public BufferedReader(Reader in)//BufferedReader的构造方法,需要一个Reader对象
//输出流具有与以上相同的构造方法,当输出流指定路径的文件不存在时,会自动创建

public FileWriter(String fileName, boolean append)//append为true表示以扩展的方式输出(不会覆盖文件原有内容)
public int read()//一次读取一个字符,阻塞式方法,读取到末尾,返回-1
public int read(char[] cbuf)//一次读取一个字节数组,返回实际读取到的字符数,如果读取到文件末尾,返回-1
public String readLine()//BufferedReader类的特有方法,一次读取一行,以换行符/回车符终止,读到文件末尾返回null
public void write(char[] cbuf, int off, int len)//基于off的偏移量写入字符数组中len长度的内容
public void write(String str,int off, int len)//基于off的偏移量写入字符串中len长度的内容
public void newLine()//BufferedWriter的特有方法,换行
public void flush()//刷新缓冲区,将缓冲区中的内容写入
public void close()//关闭流//具有缓冲区的类必须刷新或者关闭流才能将缓冲区中的数据真正写入

三.多线程

  • 进程与线程

一个应用程序就是一个进程,一个进程中,至少有一个线程(主线程),根据需要,可能同时执行多个线程
cpu在执行多线程任务时,在一个时间片上执行哪个线程是随机的,由cpu自行分配

  • java开启多线程的方法

1)子类继承Thread类,重写run()方法,在主线程中使用子类对象调用start()方法开启子线程;
这种方法占用了子类的继承位,无法再继承其他类,局限性较大

2)子实现类实现Runnable接口,实现run()方法,在主线程中将子实现类对象作为Thread类构造方法的参数,获取Thread类对象,调用start()方法开启子线程
这种方法实际上利用了静态代理模式(代理人和任务人实现同一个接口,任务人专注于自己的任务,代理人调用任务人的任务方法,并进行扩展,在主函数中实际调用代理人的方法)

3)线程池技术:
线程池的真正实现类是 ThreadPoolExecutor,它的构造方法需要七大参数:
corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。
定长线程池使用方法:
通过Executors工具类的newThreadPool(int nthreads)方法,创建定长线程池对象,参数为核心线程数量
子实现类实现Callable接口的call方法,使用线程池对象.execute(Callable call)方法,执行线程,参数为子实现类对象

  • 同步锁机制

当多个线程操作相同数据时,因为cpu资源随机分配的策略,可能导致数据不安全,产生紊乱
因此,有必要引入锁机制,保护数据安全
具体方法是为被多个线程共享的数据加上锁,线程要操作数据时,需要先获取锁,也就是数据的操作权限,此时,未能获取到锁的线程将阻塞,等到具有权限的线程释放锁之后,再重新争抢锁

加锁的方法有两种:

  1. synchronize关键字
    synchronize关键字可以加在方法内(静态/成员均可)的代码块前,也可加在方法声明上(静态/成员均可);
    当synchronize关键字加在成员方法内部时,需要指明锁对象;可以是任意java类型对象,同一个对象对应同一把锁;
    当synchronize关键字加在成员方法声明上时,锁对象默认是this;
    当synchronize关键字加在静态方法上时,锁对象是当前类的Class类对象

2)Lock接口
Lock接口的使用更加灵活,未获得锁时仍然可以执行其他任务
使用时需要在成员位置创建锁对象,在需要加锁的位置调用lock()/trylock()方法来获取/尝试获取锁对象(前者未获取锁时会阻塞,后者不会);
然后使用try...catch...finally...语句操作数据,并在finally中调用unlock()方法释放锁

线程控制的方法:

//继承自Object类的方法
public final void wait()//在某个线程内使用锁对象调用此方法,可以使当前线程立刻释放锁并休眠,无参则一直休眠直到被唤醒,传入long类型的毫秒值参数表示休眠的最大时间
public final void notify()//在某个线程内使用锁对象调用此方法,可以唤醒正在等待此锁的随机一个线程,并等待锁被释放
public final void notifyAll()//在某个线程内使用锁对象调用此方法,可以唤醒正在等待此锁的所有线程,并等待锁被释放
//注:以上三个方法只能在同步代码块内部被调用

//Thread类特有方法
public static Thread currentThread()//Thread类静态方法,返回当前线程对象
public static void sleep(long millis)//Thread类的静态方法,当前线程休眠millis毫秒值public static void yield()//Thread类的静态方法,表示当前线程愿意让出cpu资源,但是否成功由cpu决定
public final void setName(String name)//更改此线程的名字
public final String getName()//返回此线程名称
public Thread.State getState()//返回当前线程状态
public void interrupt()//中断此线程(将中断标记更改为true),如果该线程正常执行,不会对线程产生影响,如果正处于sleep(),wait(),join()产生的等待状态,将被唤醒
public final void join()//当前线程(该方法所在的线程)等待此线程(调用该方法的线程)执行完之后再执行,它的重载方法可以传入毫秒值参数,为等待的最大时间,超时自动苏醒
public final void setPriority(int newPriority)//更改此线程的优先级
public final int getPriority()//返回此线程的优先级
  • 线程的生命周期

官方文档为线程定义了六种状态:
1)NEW:当线程刚被创建,还没有开启时,处于此状态,即新建状态
2)RUNNABLE:线程调用了start()方法后,进入此状态,即运行状态;
需要注意的是运行状态并不代表线程正在运行,它可以细分为READY(就绪)和RUNNING(运行中)两种状态;
当线程获取到cpu的时间片时,会进入RUNNING状态,否则会在READY状态等待cpu为其分配时间片
3)WAITING:当线程被调用wait()/join()方法,并未指定等待时间时,会进入此状态,即等待状态,直到线程被唤醒,才会重新进入RUNNABLE状态
4)TIMED_WAITING:当线程被调用wait(long)/join(long)/sleep(long)方法时,会进入此状态,即超时等待状态;等到时间结束或被唤醒,就会进入RUNNABLE状态
5)BLOCKED:当线程未能获取到锁对象时,将处于此状态,即阻塞状态;等到其他线程释放锁后,如果该线程未抢到锁,继续阻塞,否则将进入RUNNABLE状态
6)TERMINATED:当线程执行完之后,将进入此状态,即死亡状态;线程在死亡时会默认调用notifyAll()方法

  • 生产者消费者设计模式

当两个线程操作同一个数据资源时,其中一个线程是用来产生数据的,另一个线程使用数据
此时为了不使数据紊乱,需要使用生产者消费者设计模式,核心思想是:
对资源类进行判断,如果没有数据,则消费者阻塞,由生产者产生数据,然后唤醒消费者,并阻塞自身;
然后由消费者处理数据,处理完成之后,唤醒消费者,并阻塞自身,重新等待消费者的唤醒

四.网络编程

  • 网络基本知识

ip地址用于唯一标识网络中的每台计算机,ipv4的表示形式为点分十进制 xx.xx.xx.xx
每个十进制数值的范围为0~255

ip地址记忆不方便,所以通过http协议一一映射成域名,如www.baidu.com
为了能精准的访问计算机上的某个网络软件,需要直到其所占的端口号
端口号为0~65535的整数,0~1024已被占用

  • 网络通信协议示意图

  • TCP协议与UDP协议

1)TCP协议
使用TCP协议前,需要建立TCP连接,形成客户端和服务器端之间的数据传输通道
通道的建立需要经历"三次握手",才能建立起可靠的数据连接
建立通道之后,可以进行大量数据的传输
传输完成之后需要释放连接,效率较慢
2)UDP协议
将数据,来源,目标封装成一个大小不超过64k数据包,然后直接将数据包发送给目标,所以不适合大量数据的传输
这种方法不需要建立连接,无法确认对方是否收到,因此是不可靠的,但是也正因为此, 无序释放资源,执行效率较高

  • InetAddress类

该类用于解析ip地址等之类的操作

常用方法:

public static InetAddress getLocalHost()//获取本地主机的InetAddress对象
public static InetAddress getByName(String host)//根据IP地址/域名获取目标主机InetAddress对象
public String getHostAddress()//返回该对象所指主机的ip地址
public String getHostName()//返回该对象所指主机的主机名,主机不允许获取,则返回ip
  • socket类

socket又称为套接字
它是网络通信的端点,用于连接网络数据通道和应用程序;互相通信的两台主机都需要有socket
应用程序可以通过socket将网络链接作为流处理,也就是说数据在socket之间通过IO传输

  • TCP/UDP通信的代码实现
    TCP通信:
//客户端
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

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

        //创建socket对象
        Socket s = new Socket(InetAddress.getLocalHost(),10086);

        //获取socket的字节输入输出流
        InputStream ips = s.getInputStream();
        OutputStream ops = s.getOutputStream();

        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);

        //创建发送语句字符串
        String str = "";

        //创建接收的字节数组
        byte[] bytes = new byte[1024];
        int len = 0;

        System.out.println("可以开始聊天了");

        //while循环对话
        while (!str.equals("再见")){

            //发送数据
            str = sc.next();
            ops.write(str.getBytes());
            ops.flush();

            //接收数据并展示
            len = ips.read(bytes);
            String string = new String(bytes,0,len);
            System.out.println(string);
        }

        //关闭资源
        s.close();
    }
}
//服务端
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

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

        //创建socket对象
        ServerSocket ss = new ServerSocket(10086);

        //等待连接
        Socket s = ss.accept();
        System.out.println("连接建立成功");

        //获取socket的字节输入输出流
        InputStream ips = s.getInputStream();
        OutputStream ops = s.getOutputStream();

        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);

        //创建发送语句字符串
        String str = null;

        //创建接收的字节数组
        byte[] bytes = new byte[1024];
        int len = 0;

        //while循环对话
        while (!"再见".equals(new String(bytes,0,len))){

            //接收数据并展示
            len = ips.read(bytes);
            String string = new String(bytes,0,len);
            System.out.println(string);

            //发送数据
            str = sc.next();
            ops.write(str.getBytes());
            ops.flush();
        }

        //关闭资源
        s.close();
    }
}

UDP通信:

//发送信息端
public class SendDemo {
    public static void main(String[] args) throws IOException {
    
         //建立通讯socket
        DatagramSocket ds=new DatagramSocket();
 
        //读取键盘输入流
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
       
       String line;
        
        while((line=br.readLine())!=null){
        
            if("886".equals(line)){
                break;
            }
            
            byte[] bys=line.getBytes();
            
            //建立数据包,声明长度,接收端主机,端口号
            DatagramPacket dp=new DatagramPacket(bys,bys.length,
                InetAddress.getByName("202.199.6.226"),12345);
           
           //发送数据
           ds.send(dp);
        }
        
        //关闭资源
        ds.close();
    }
}
//接收信息端
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
    
        //接收端口号的消息
        DatagramSocket ds=new DatagramSocket(12345); 
        
        while(true){
        
            byte[] bys=new byte[1024];
            
            //建立信息包
            DatagramPacket dp=new DatagramPacket(bys,bys.length);
            
            //将socket的信息接收到dp里
            ds.receive(dp);
            
            System.out.println("输入数据为:"+new String(dp.getData(),0,dp.getLength()));
        }
        ds.close();
    }
}

五.反射

  • 什么是反射

反射指的是程序在执行阶段通过Reflection API获取到任何类的Class类对象,
进一步可以获得该类的任意内部信息(静态/成员变量,静态/成员属性,构造方法),
以此可以创建对象,为属性赋值,调用方法,即使这些成员是私有的.
这样做的好处是可以动态的创建和使用对象,在代码上解耦,不需要更改源码,只需要更改配置文件就可以实现不同的效果
缺点是执行效率较低

  • 反射原理示意图

  • 获取类对象的方法

1)Class类的静态方法:Class.forName(类的全限定名称).这种方法主要用于借助配置文件获取任意类的Class类对象
2)任意java类型.class属性.这种方法一般用于将目标类的Class类对象作为参数传递
3)继承自Object类的方法:任意java对象.getClass().可以获取任意java对象的Class类对象

  • 配置文件properties

配置文件中的后缀名为(.properties),其中存储的都是String类型,格式为Key=Value,不能有空格,双引号等其他符号
配置文件的读取需要用到Properties类,Properties类本质上是一种Map集合,只是键值对都是String类型

//加载配置文件的方法
//第一种,通过类加载器读取配置文件,这种方法的配置文件只能放在src目录下,与com包同级
Properties prop = new Proerties();
InputStream inpts = 当前类名.class.getClassLoader().getResourceAsStream(String name);//参数name为配置文件全名
prop.load(inpts);
//第二种,通过普通输入流读取配置文件,这种方法的配置文件可以放在任意目录下(包括项目外)
Properties prop = new Proerties();
prop.load(new FileReader(配置文件绝对路径));

//properties常用方法
public String getProperty(String key)//根据key获取value
public void load(InputStream inStream)//通过字节输入流加载配置文件,参数若为Reader reader,意味通过字符输入流加载
public Object setProperty(String key,String value)//将键值对存储到配置文件中,此方法只是将其存储到集合中,并未输出到配置文件中
public void store(Writer writer, String comments)//将prop对象中的键值对存储到配置文件中,writer也可以换成OutputStream,comments为配置文件的说明


//第三种,调用ResourceBundle的静态方法getBundle()获取ResourceBundle对象,调用getString(String key)方法获取values,这种方法的配置文件只能放在src目录下
ResourceBundle rb = ResourceBundle.getBundle(配置文件名);//只需要配置文件名,不用加后缀
String value = rb.getString(String key);
  • 反射相关主要类

java.lang.Class:代表类的类,其对象为某个类在加载之后在堆中的对象
java.lang.reflect.Method:代表方法的类,其对象为某个类中的方法
java.lang.reflect.Field:代表属性的类,其对象为某个类中的属性
java.lang.reflect.Constructor:代表构造方法的类,其对象为某个类的构造方法

  • 反射相关主要方法
//Class类
public static Class<?> forName(String className)//Class类静态方法,用于通过类的全限定名称获取其Class类对象
public ClassLoader getClassLoader()//获得该类的类加载器
public T newInstance()//通过该类的无参构造方法(如存在)创建该类对象
public String getName()//返回该类的全限定名称

public Constructor<T> getConstructor(类<?>... parameterTypes)//根据参数列表返回该类的public构造方法对象
public Constructor<?>[] getConstructors()//返回该类所有的public构造方法对象数组
public Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)//根据参数列表返回该类的构造方法对象,无论其访问权限多大
public Constructor<?>[] getDeclaredConstructors()//返回该类所有的构造方法对象数组,无论其访问权限多大
//方法(Method)和属性(Field)拥有与构造器完全相同的上边四个方法

//AccessibleObject类
//该类是构造器类,方法类,属性类的父类,其中的方法可以被这三者调用
public void setAccessible(boolean flag)//用于解除构造器对象/方法对象/属性对象的访问权限(true),设为false表示需要足够的权限
public static void setAccessible(AccessibleObject[] array, boolean flag)//批量设置解除访问权限

//Constructor<T>类
public T newInstance(Object... initargs)//创建该类的实例,如果当前对象非public,需要先解除访问权限
//Method类
public Object invoke(Object obj,Object... args)//调用obj的该Method对象表示的方法,args为需要使用的参数,如果是静态方法,将忽略第一个参数
//Field类
public Object getXXX(Object obj)//根据属性的类型,返回obj对象的当前Field对象表示的属性的值
public void setXXX(Object obj, Object value)//为obj的该Field对象所表示的属性赋值,XXX应与实际属性的类型,value的类型一致
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值