JavaSE知识总结之集合和多线程

本文详细介绍了Java中的集合框架,包括ArrayList、Vector和LinkedList的特点及线程安全性,以及HashSet和List的去重策略。此外,讨论了final、finalize和对象的生命周期,讲解了TreeSet的排序方式、Map的使用、Collections工具类的方法。还涵盖了异常处理,如throws和throw的区别,自定义异常以及异常注意事项。最后深入探讨了多线程相关概念,如join()、yield()、线程池和同步机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

List集合三个子实现类的特点

ArrayList

底层数据结构是数组,查询快,增删慢
从线程角度:线程不安全的,不同步的-----执行效率高!

Vector

底层数据结构是数组,查询快,增删慢
线程角度:线程安全----同步的------执行效率低(可变因子)

LinkedList

底层数据结构是链接列表,查询慢,增删快
线程角度:线程不安全的----不同步的-----执行效率高!

HashSet集合存储自定义对象如何去重

不重复的,能够保证元素唯一,不能保证迭代顺序恒久不变!
HashSet
HashSet的添加功能add---->HashMap集合put方法
hashCode()和equals方法

对于自定义对象所在的类必须重写hashCode()和equals方法()

List集合存储自定义对象如何去重

本身:可重复的,存储和取出一致
List
使用新集合的思想:使用新集合判断元素是否包含在该集合中!
contains()方法---->底层依赖于equals方法()

List:自定义对象所在类重写equals()方法/重写hashCode()

集合和数组,StringBuffer的区别

三者都是容器
StringBuffer:存储的可变的字符序列(构造字符串缓冲区),最终是以"字符串"展示
线程角度:是一个线程安全的,同步----执行效率低---->单线程程序中,使用StringBuilder替代StringBuffer

集合:单例集合(Collection)
长度可变,可以存储任意类型的元素,它只能引用数类型
数组:
长度固定同一种类型的元素,它可以存储基本类型/引用类型

应用场景:大部分时候会使用集合去操作!
查询所有/查询一些部分数据----存储到集合中List

集合的遍历方式

Object[] toArray
Collection的迭代器:Iterator iterator()
List列表迭代器:ListIterator listiterator()
size()和get(int index)相结合,普通for循环
增强for循环:替代迭代器,简化书写格式:foreach语句 (推荐)

final,finalize的区别

final:状态修饰符,修饰类,该类不能被继承
修饰变量,此时变量是常量
修饰成员方法,该方法不能被重写

finalize:是Object类中方法,主要用来开启垃圾回收器,会调用finalize()方法,
回收没有更多引用的对象(和GC有关系!)

TreeSet集合

本质基于TreeMap的底层实现(红黑树结构---->自平衡"的二叉树结构")
public TreeSet():默认的自然顺序排序
由于TreeSet属于Set集合(本身保证元素唯一,不重复的元素!),还可以将元素按照自然顺序排序

有两种排序方式:

**自然排序:**
        要实现自然排序,必须实现接口:Comparable
        重写Comparable接口中的compareTo方法

源码分析:

interface Collection{}

interface Set extends Collection{
}
class TreeSet implements Set{

	//成员位置
	private transient NavigableMap<E,Object> m;

	public boolean add(E e) {//20,18,24,17,16,23,22,24
        return m.put(e, PRESENT)==null;
    }
}


interface NavigableMap<K,V> extends Map<K,V>{
		
		 V put(K key, V value);
}
//Entry<K,v>实现了接口:Map.entry<K,V>:键值对对象,以一种内部类的方式出现在TreeMap集合汇总
//将put进行重写
public class TreeMap<K,V> implements NavigableMap<K,V>{
		public V put(K key, V value) {//key =20,18,24,17,16,23,22,24
        Entry<K,V> t = root;  //root---->是一个键对对象 (根节点)
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent; //parent:是一个父节点
        // split comparator and comparable paths//分了两种方式
        
        
        //核心代码...
        
        //comparator接口:比较器排序
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
        	//自然排序:通过构造方法 
        	
            if (key == null) //如果存储的元素为空
                throw new NullPointerException();//一定抛出空指针异常!
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key); //20,18,17,24,23,22,16,17,24....
                if (cmp < 0)
                    t = t.left;  //左节点
                else if (cmp > 0)
                    t = t.right; //右节点
                else
                    return t.setValue(value); //重复元素直接存储一个!
            } while (t != null); //如果根节点不为空
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

结论:
如果使用TreeSet集合存储自定义对象,那么要进行自然排序?
那么该自定义对象所在的类必须实现Comparable接口,重写Comparaeable接口
CompareTo(T t)方法!

存储TreeSet: TreeSet() :空参构造(默认自然排序)
由于Integer类它已经实现了Compareable接口中的compareTo方法 使用三元运算符进行判断
如果要使用比较器排序,那么自定义对象所在的类必须实现comparator接口,重写接口中:compare方法

选择器排序

使用比较器排序完成
TreeSet集合有参构造方法

public TreeSet(Comparator<? super E> comparator)

创建TreeSet集合
方式1:直接给了创建了接口子实现类

TreeSet<Student> ts = new TreeSet<Student>(new MyComparator()) ;

形式参数是一个接口类型,需要传递该接口子实现类对象

方式2:形参参数如果接口:传入接口的匿名内部类(本质:接口的子实现类)
匿名内部类(推荐的方式)

匿名内部类的格式:

new 类名/接口名(){
				重写抽象类中或者接口中的抽象方法...
};

Map集合

java.util.Map<K,V>:接口
Map集合的特点:

一个键值对元素(一个键对应一个值,值是可以重复,但是键必须唯一) 只针对键有效,跟值无关! (看成是:夫妻对)

Map<K key,V value>集合:
双列集合,键映射到值的对象 ,键必须保证唯一,可以看成是一个(夫妻对)
遍历方式和Collection集合遍历方式不同
Collection集合:
单列集合:只能存储一种引用类型,里面的set集合依赖于Map集合的实现 (理解为:光棍)

  HashSet---->HashMap里面put方法
  TreeSet---->TreeMap里面的put方法

Map集合的功能:
添加功能
V put(K key, V value) :添加一个键值对元素
问题:返回值是什么意思
如果当前键是一次存储的时候,返回的结果null
如果键是不是第一次存储,后面重复,将当前对应的值把以前的值覆盖掉并保存下来,返回以前的值!

刪除功能
V remove(Object key):刪除指定的键,返回的是跟键关联的值,如果没有映射关系,则返回null
void clear():删除全部的键以及值
判断功能
boolean containsKey(Object key):是否包含指定的键
boolean containsValue(Object value):是否包含指定的值
boolean isEmpty():判断Map集合是否为空
高级功能
方式1遍历:(推荐的方式:Map常用的方式)
Set keySet():获取所有的键的集合
V get(Object key):通过键获取对应的值
方式2遍历
Set<Map.Entry<K,V>> entrySet():获取当前Map集合中所有的键值对对象
K getKey() :通过键值对对象获取键
V getValue():通过键值对对象获取值
TreeMap:红黑树结构
构造方法
public TreeMap():默认的自然顺序排序
public TreeMap(Comparator<? super K> comparator):是一种比较器排序(推荐)
TreeSet集合存储自定义类型,什么情况下自然排序(Comparable),什么情况下是选择器排序(Comparator)

  1. 执行无参构造方法:TreeSet():无参构造----是自然排序:
要求当前自定义类型需要实现Comparable接口,重写comparesTo方法
  1. 执行有参构造方法:TreeSet(Comparator com):
方式1:自定义一个类实现Comparator接口中 的compare(T t1,T t2)方法
		方式2:通过接口匿名内部类实现

HashMap

  1. HashMap<K,V>是Map集合的子实现类,里面哈希表结构,保证(键唯一)
  2. Map集合只只针对键有效
  3. 自定义的对象存储在值的位置,是可以重复的!,如果是在键位置,不能重复!
  4. 对于Map存储的键如果是自定义对象:该自定义对象的所在类必须重写Object:equals()和hashCode()
  5. 会依赖于HashMap的put方法
  6. hash()---->依赖于hashCode():算出每一个对象哈希码值一样
  7. putValue()---->equals()方法:哈希码值一样,还有比较每一个成员信息是否相同!

Collections类

可以针对Collection集合进行操作的工具类!
public static int binarySearch(List<?> list,T key):针对集合的二分查询方法:查询key在集合中出现的索引值
public static T max(Collection<? extends T> list):获取集合中最大值
public static T min(Collection<? extends T> list):获取集合中最小值
public static void sort(List list):针对List集合进行排序:升序排序
public static void sort(List list,Comparator com):比较器排序
public static void shuffle(List<?> list):针对List集合的元素进行随机置换

获取字节码文件的方式

  1. Object类中的getClass()方法

  2. 任意Java类型的class属性

  3. 反射:Class.forName(“类的全限定名称”)…

异常

程序出现了问题,使用"异常"来描述问题

java.lang.Throwable:异常类( Java 语言中所有错误或异常的超类(父类))
 			error:严重错误(严重问题)	
				比如:内存异常!OOM(Out Of Memory) 
			通过加入内存条...或者将Linux虚拟机的内存空间设置小一点!
			
 Exception: 可以捕获合理的程序!
			编译时期异常:只要不是RuntimeException中的子类都是属于编译时期
						必须要处理的,不处理编译通不了,没有办法运行的!
				运行时期异常RuntimeException:
						程序在执行过程中出现的问题
						代码逻辑不够严谨导致的!

异常的处理方案
1)try…catch…finally:标准格式 捕获异常

方式1:捕获异常的变形格式
 * 		try...catch...
 * 		try...catch...catch...
 * 		try...finally...  (线程中:Lock锁)
 * 
 * 格式:流程
 * 		try{
 * 				...
 * 				...
 * 				可能出现问题代码
 * 		}catch(异常类名 变量名){//具体异常的类
 * 			//处理异常
 * 			//自己手动处理
 * 			//Throwable类中一些方:处理方式(jvm将异常信息输出在控制台并且可以跟踪异常类源码)
 * 		}
 * 

2)throws:抛出异常 (抛出到方法上)
抛出在方法声明上,如果方法中的语句体出现了异常,将异常交给jvm,jvm将异常信息输出在控制台上…
编译时期异常和运行时期的区别

  • 编译时期的异常:

    举例:
    String—Date—解析的方法parse(String source) throws ParseException{}

     	IO流:IOException
    

开发者必须要进行显示处理,否则编译通过不了!

  • 运行时期异常:

     	1)代码逻辑的问题,不进行显示处理,在代码中加入一些逻辑判断
     	2)也可以进行显示处理:try...catch...
    

关于Throwable中的一些方法

  • public String getMessage():获取异常的消息字符串

  • public String toString():获取异常信息的简单描述

  • 异常的全限定名称:包名.类名: 调用该异常对象的getMessage()详细的消息字符串

  • public void printStackTrace():

  • 将此对象的信息追踪到堆栈中,具体获取到具体源码中哪一块出问题了

throws和throw的区别

  1. throws:抛出
    1)书写位置是在方法声明上
    2)throws的后面跟的异常类名,而且可以跟多个异常类名,中间使用逗号隔开
    3)对于throws它是由调用者进行处理,必须要对带throws的方法进行处理!
    4)throws表示抛出异常的一种肯能性(这个方法可能出现问题/可能没有问题!)
  2. throw:抛出
    1)书写位置是在方法体中
    2)throw的后面跟的异常对象,一般都是匿名对象 new XXXException()而且只能跟一个异常对象
    3)由方法中逻辑判断进行处理(由方法体中语句进行处理)
    4)throw:表示抛出异常一种肯定性(执行到逻辑的时候,一定会执行这个异常!)

自定义的异常

  • 1)自定义一个类,继承自Exception或者RuntimeException
  • 2)提供无参构造方法 /有参构造 传递String参数(消息字符串)

异常的注意事项:

  • 1)子类继承父类的时候,对父类的成员方法进行重写,如果父类的成员方法有异常,那么子类的该方法针对异常处理,要么是跟父类的异常类名一致,要么是父类异常类的子类
  • 2)子类继承父类重写父类的方法的时候父类的成员方法没有抛出异常,子类的该方法不能抛出异常,只能try…catch…

finally

捕获的标准格式
 * 		try{
 * 			//可能出现问题的代码
 * 		}catch(异常类名 对象名){
 * 		  对象名.printStackTrice() ;
 * 		}finally{
 * 			//释放相关系统资源close()
 *			//举例:IO流中: createNewfile():创建文件的方法
 *			//举例:JDBC:java 连接数据库  
 *			//Connection: 连接对象---- 使用完毕需要关闭相关的系统资源!
 *			//Statement:执行对象(增删查改的操作):---使用完了也需要释放系统资源
 * 		}
 * 
 * try中代码如果不存在问题,代码正常执行,需要执行finally语句,释放系统资源!
 * finally特点:
 * 			释放相关的系统资源,而且finally中的代码一定会执行的;除非在执行finally语句之前
 * JVM退出了! System.exit(0) ;

多线程

线程
线程是依赖于进程的,将线程看成是进程中某个"任务"

进程:
本地开启一个客户端,就是一个进程 “任务管理器”!
能够调用系统资源的独立单位

多进程的意义
多进程的意义:就是为了提高cpu的使用率!
线程
线程---->理解为 进程的中的任务
多线程的意义
每个线程为了抢占CPU的执行权,他们的抢占过程中具有随机性!

jvm:是假想计算机—开启对应进程
jvm:里面至少有两条线程
main方法: 主线程----“用户线程”
垃圾回收线程:产生很多对象的时候,需要通过gc回收没有更多引用的对象!

			public staic void main(String[] args){
			
				new Object() ;
				new Object() ;
				....
				FileInputStream fis = new FileInputStream("d:\\a.txt") ;
			}

如何实现一个多线程程序

  • 要实现多线程—线程是依赖于进程,创建进程---->创建系统资源
  • 但是Java语言不能够直接创建系统的,JDK提供一个类:Thread线程类
  • jvm是可以运行多个线程并发的执行
  • 并发:在同一个时间点同时发生的!
    设置线程的名称
  • public final void setName(String name)
    获取线程的名称
  • public final String getName()
    方式1
  • 1)自定义一个类 继承Thread类 :线程类
  • 2)重写Thread类中的run方法
  • 3)在当前用户线程中(main)中创建该类对象,
  • 4)启动线程 :start()方法
    弊端
    继承关系的弊端:
    局限性

它不能够体现出"数据共享"的概念!
方式2

  • 1)自定义一个类(资源类),这个类实现接口:Runnable接口—用到了Java设计模式之代理模式!
  • 2)重写Runnable接口中的run方法
  • 3)在主线程中main
  •  3.1)创建当前资源类对象,
    
  •  3.2)在创建Thread类对象,将3.2中的资源类对象作为参数进行传递
    
  • 4)启动线程 start方法启动

优势:静态代理
能够体现出 “资源共享”
方法
public final void setDaemon(boolean on):设置守护线程.在启动线程之前,调用这个方法才能设置为守护线程 (注意事项!)(如果当前运行中线程都是守护线程,JVM退出)
线程的生命周期/线程的状态
State:线程的状态:是Thread类内部枚举

    NEW,	线程新建状态			---->public static final...
    RUNNABLE,线程运行状态
    BLOCKED,线程阻塞状态(sleep(),等待)  
    WAITING,//死死等待  
    TIMED_WAITING,//超时等待  等待到一定时间,不等了
    TERMINATED;//线程终止!

获取主线程中名称
public static Thread currentThread():表示当前正在运行的线程
getName():获取线程名称
setName(String name):设置当前线程名称
public final void join() throws InterruptedException:等待该线程终止

public final void setPriority(int newPriority)设置线程的优先级
public final int getPriority():获取线程的优先级
线程的优先级: 常量值

  • public static final int MAX_PRIORITY:最大优先级 10
  • public static final int MIN_PRIORITY:最小优先级 1
  • public static final int NORM_PRIORITY:默认优先级 5
  • 优先级越大的线程抢占CPU的执行权几率大
  • 优先级越小的线程抢占CPU的执行几率小
  • 符号线程的执行具有随机性
    public static void sleep(long millis) throws InterruptedException:线程睡眠 (阻塞式方法!)参数为时间毫秒值!
    public final void stop():已过时的方法,但是可以使用!强迫线程停止执行
    public void interrupt():中断线程 :中断线程的一种状态!(6种状态)
    public static void yield():暂停当前正在执行的线程对象,并执行其他线程!理解为"谦让"
    检验多线程安全问题的标准是什么
  • 1)查看当前环境是否是多线程环境
  • 2)当前程序中是否存在共享数据
  • 3)是否有多条语句对共享数据进行操作
    为了解决这个问题:Java提供了一个关键字:synchronized
 *  		同步代码块
 *  		synchronized(锁对象){ //门的开/关	
 *  			多条语句对共享数据进行操作
 *  		}
 *  

同步方法:非静态的 锁对象:this
方法声明上加入一个synchronized

  • 锁对象:多个线程应该使用的是通一把锁!
    代理: 让别人帮助我们完成一些事情!
    代理分类:
  • 静态代理
  • 1)真实角色只专注于自己的事情
  • 2)找一个代理类—代理角色来完成真实角色完成不了的事情
  • 代理角色和真实角色都需要实现同一个接口 (Thread类:多线程的实现方式2)
  • Tread类:真实角色----启动线程(完成自己的事情):系统资源启动
  • 自定义类:MyRunnable implements Runnable{} 代理角色
  • 代理角色重写run方法()
  • 完成代理的业务…
  • 动态代理
  • JDK动态代理
  • Cglib动态代理

join()和yield()方法的区别

join():等待该线程终止!
		底层依赖于 wait(long time),参数为0,等待状态:WATTING
								参数为整数,到底一定时间,等待结束!
		 wait(long time):Object类中方法 (本地方法实现)

yield():暂定当前正在执行的线程,执行其他线程  "谦让"

锁对象

非静态的同步方法的锁对象:this— 当前这个所在类的对象的地址值引用!
静态的同步方法和反射相关-----跟类的字节码文件对象 xxx.class, static随着类的加载而加载 ,锁对象:当前类名.class

java.util.concurrent.locks.Lock
juc包下的接口
Lock接口提供一些相关的功能,比synchronized有具体的锁定操作(具体的功能
获取锁/释放锁)

Lock不能直接实例化:
具体的子实现类ReentrantLock

功能:

  • public void lock():获取锁
  • public void unlock():试图释放锁
    如果通用的方式:synchronized同步代码块/同步方法解决线程安全问题
    但是可能会出现一种死锁!
    死锁:加入同步之后,两个或者两个以上的线程在互相抢占资源的时候出现一种互相等待的情况!

线程组:ThreadGroup:

一个线程的集合!
加粗样式

  • Thread类中:

  • public final ThreadGroup getThreadGroup():获取当前线程所属的线程组

  • Thread类中

  •  Thread(ThreadGroup th,String name)
    
  • ThreadGroup

  •  	  public final String getName() :获取线程组名称
    
  • 线程组中包含很多个线程,每一个线程默认的线程组名称:main

  • 线程启动过程: start()---->当前所有启动的线程添加到线程组中了

  • 将所有的线程添加线程组之后,线程执行完毕了,变成垃圾—>被回收

线程池----多线程的第三种实现方式

  • 创建一个固定的可重用的线程数,当这些线程使用完毕终止了,它(线程对象)不会被回收掉归还到线程池中,下次在利用!

  • ExecutorService 接口:不能实例化

  • 间接通过:

  • java.util.concurrent 类 Executors :线程池的工厂类

  • 23种设计模式(思想)---- 创建型设计模式中一种 “静态工厂方法模式”

  • 工厂类中一些静态功能:返回值 ExecutorService
    public static ExecutorService newFixedThreadPool(int nThreads)

  • 提交异步任务(执行)

  •  <T> Future<T> submit(Callable<T> task)
    
  •  <T> Future<T> submit(Runnable<T> task)
    
  • Future:

  •  	V get():获取当前计算结果的值
    
  • 返回值:表示异步计算的结果

  •  void shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务
    

什么是同步机制,什么是等待唤醒机制

所有的多线程中资源冲突问题,都使用同步代码块/同步方法,
		给当前对资源共享的数据上一道锁
			格式:
				
				synchronized(锁对象){
					对象资源共享数据的操作
				}
				
				权限修饰符 synchronized 返回值类型 方法名(形参列表) {} :同步方法
				
				
				NEW			--- 未进入start
				RUNNABLE	--- 开启线程---运行状态
				BLOCKED		---锁阻塞 某个线程被其他线程持有锁对象了之后进入阻塞
				WATTING		--一直等待 (唤醒:线程持有锁对象之后,如果他锁对象被是否,就可以唤醒对方线程)
				TIMEED_WATTING	---超时等待:时间的限制 sleep(long time)
				TERMINATED		---线程执行完毕,jvm 退出了
				
		等待唤醒机制:
				生产者消费者模式---信号法
				生产者不断的产生数据,需要等待wait()消费者线程使用数据,
							使用完毕之后,没有数据了,需要通知生产者线程产生数据(notify())
							
				封装资源数据:Student
					标记值:boolean flag; 默认false:没有数据,true:存在数据!

notify()和wait()为什么不定义在Thread类中呢而是Object类中!

notify()和wait()----底层存在一种监视器(一种锁对象状态的变化) ----都是本地方法实现!
这个两个都是由锁对象来访问的,锁对象----可以任意Java类对象
wait()方法调用的时候,会立即释放锁

sleep()和wait方法的区别

sleep(long time)
wait()  
不同点:
	1)是否立即释放锁
		sleep(long time)方法的调用, ---等待休眠时间到了,计时等待,不会释放锁对象!
					释放的是一种锁状态
		wait()----该方法调用会立即释放锁对象,然后后面才能使用锁对象调用唤醒对方线程!
		
	2)来源不同:
		sleep(long time):来源于Thread类
		wait():来源于Object类,wait()和锁对象息息相关!
共同点
	都会抛出中断异常: 任何线程中断了当前线程的这种状态,就会产生中断异常!

线程匿名内部类的方式

 * new 类名/接口名(){
 * 		重写方法
 * };
 * 
  • 匿名内部类的本质:

  •  	继承了该类或者实现了该接口子类的对象
    
  • 匿名内部类的好处----降低了耦合性!(解耦)

Timer:定时器

java.util.Timer: 定时器工具

  • 可以重复执行某个任务/或者执行一次任务

  • 构造方法

  • public Timer():构造一个新的计时器

  • 成员方法:

  • public void cancel():终止定时器

  • String日期文本—>Date日期格式:

  • public void schedule(TimerTask task,Date time)

  • 在指定日期时间内容将执行这个 任务!

  • public void schedule(TimerTask task, long delay):在指定时间后执行当前这个task任务

  • TimerTask:由定时器Timer来安排是执行一次/重复执行当前任务

  • public abstract void run():任务要执行的操作

设计模式

java23种设计模式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值