jdk中包含了jre,jdk是java开发工具包,jre是java运行环境。
面向对象注重的是完成一个功能的参与者,和参与者各自的功能是什么。
面向对象的三大特性:
封装:隐藏内部细节并向外部暴露响应的接口,让外界不能修改我的内部逻辑。比如实体类、
mybatis都用到了封装的思想。
继承:通过extends关键字,子类可以继承父类,拥有父类公开的方法和属性,并且可以开展自己
的属性和方法。
多态:编译看左边,运行看右边,Diamnal d = new Dog() 左边是父类,右边是子类就是多态。创
建集合也是多态,比如List list=new ArrayList()。实现多态的三个必要条件:继承、子类重写父类
中的方法、父类引用指向子类对象。
重写发生在类和子类中,子类重写父类中的方法,方法名和参数列表必须相同,抛出的异常范围要
小于父类,修饰符的访问权限要大于等于父类。
重载发生在一个类中,方法名必须相同,参数,参数类型、参数个数、参数顺序不同,返回值和修
饰符可以不同。只是返回值不同,不能代表发生了重载。
public:当前类、子类、当前包和其他包下都可以访问,是最高的权限;
protected:当前类、子类、当前包下可以访问,其他包不可以访问;
默认:当前类、本包可以访问,子类何其他包不可以访问
private:只有在当前类下可以访问
(1)抽象类使用abstract修饰,接口使用interface修饰;
(2)抽象类只能单继承,接口可以多实现;
(3)抽象类存在构造函数,接口不存在构造函数,抽象类存在构造函数也不能被实例化;
(4)抽象类中的方法可以用public、protected、default修饰,接口必须用public修饰。
会执行,且在return后执行。
final是java中的关键字,可以修饰类、方法和属性;finally是跟在try...catch后面的,是一定会执行
的代码,常常用来释放资源;finalize是object中定义的方法,重写finalize方法可以整理系统资源和
执行其他清理工作,由垃圾收集器调用。
throw用于在方法内部抛出一个新的异常(Error或者Exception)对象,自己抛自己处理。而throws
用于在方法声明时,向上一个方法调用者抛出异常,由上一个方法调用者去处理异常。
throwable,又分为error异常和exception异常
exception又包含运行时异常和编译时异常
编译时异常:IOException,FileNotFoundException,ClassNotFoundException:
运行时异常:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException
NumberFormatException,ArithmeticException等
wait()、notify()、notifyAll()、toString()、equals()、hashcode()、getClass()、finalize()
在S1+1运算时会自动提升表达式为int类型,如果再将它赋予short类型,会出现类型转换异常,而
+=是java的运算符,会做特殊处理,能正常编译
static修饰变量,该变量随着类的加载而初始化,内存中只有一个,jvm只为它分配一次内存,所用
类共用静态变量;
修饰方法,在类加载的时候就存在,不依赖任何实例;
修饰代码块,在类加载完就会执行代码块中的类容;
父类静态代码块->子类静态代码块->父类非静态代码块->父类构造函数->子类非静态代码块->子类构造函数
不可以,因为static变量是属于类的,在类加载的时候就被初始化了,这时候非静态变量并没有加
载,故非静态变量不能访问。
==比较基本数据类型时比较的是值,比较引用数据类型时比较的是地址,equals比较基本数据类型
时比较的是值,比较引用数据类型时,如果重写了equals,比较的是值,没有重写,比较的就是地
址。
Integer、String等这些基本类型的JAVA封装类都已经实现了Comparable接口,这些类对象本身就
支持自比较,直接调用Collections.sort()就可以对集合中元素的排序,无需自己去实现Comparable
接口。
而有些自定义类的List序列,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可
以写一个比较器来完成两个对象之间大小的比较,也就是指定使用Comparator(临时规则排序,
也称作专门规则排序),如果不指定Comparator,那么就用自然规则排序,这里的自然顺序就是
实现Comparable接口设定的排序方式
实现方式,comparable:
package cn.lxx.test;
public class User implements Comparable { //该类实现Comparable接口,参数为User
private String name;
private int age;
private boolean sex;
public User() {
}
public User(String name, int age, boolean sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
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 isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
/**
* 重写compareTo方法,实现年龄升序,年龄相同则姓名升序
**/
@Override
public int compareTo(User user) {
if (this.age == user.age) return this.name.compareTo(user.name);
return this.age - user.age; //将this想像成一排不变的对象(已经按照要求排好序的),而User就是当前要插入的对象,只有user属性小于this属性才插入从而升序,个人理解,希望有所帮助
}
}
class Test {
public static void main(String[] args) {
User user1 = new User("ake", 25, true);
User user2 = new User("reo", 24, false);
User user3 = new User("fg", 24, false);
List list = new ArrayList();
list.add(user1);
list.add(user2);
list.add(user3);
Collections.sort(list);
System.out.println("Comparable:" + list);
}
实现comparator:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class User implements Comparator {
private String name;
private int age;
private boolean sex;
public User() {
}
public User(String name, int age, boolean sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
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 isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
@Override
public int compare(User o1, User o2) {
return o1.age-o2.age;
}
}
class Test{
public static void main(String[] args) {
User user1=new User("dingli",25,true);
User user2=new User("huxiaojuan",24,false);
User user3=new User("xxx",24,false);
List list=new ArrayList();
list.add(user1);
list.add(user2);
list.add(user3);
Collections.sort(list, new User()); //类实现了的Comparator能满足需求
System.out.println("类自身实现Comparator:"+list);
//现在我想要按照名字升序,显然类中实现的不能满足要求,于是可以在类外自己实现想要的比较器
Collections.sort(list, new Comparator() {
@Override
public int compare(User o1, User o2) {
return o1.getName().compareTo(o2.getName()); //按照名字升序
}
});
System.out.println("匿名内部类方式:"+list);
//由于Comparator接口是一个函数式接口,因此根据jdk1.8新特性,我们可以采用Lambda表达式简化代码
Collections.sort(list,(u1,u2)->{return u1.getName().compareTo(u2.getName());});
System.out.println("Lambda表达式方式:"+list);
}
}
21.内部类
静态内部类(定义在类中,有static修饰)
静态内部类可以访问外部类的静态属性
成员内部类(定义在类中),可以访问外部类的成员属性,即使是private修饰的
局部内部类(定义在方法中)可以任意访问该方法中的局部变量
匿名内部类()声明在方法中,抽象类或者接口 本不可以直接创建对象,可使用匿名内部类方法直接创建
因为Static修饰的方法是编译时静态绑定的,而重写是在运行时动态绑定,两个不是一个概念
自动类型转换的顺序:byte -> short -> int -> long -> float ->double
序列化和反序列化_JFS_Study的博客-CSDN博客
(1)使用+进行拼接,如果有空字符串,null值也会作为字符串拼接在其中。
(2)使用String.join() 静态方法拼接z,也会带有null值输出。如下
String[] values = {"I ", "want to ", "splicing ", "some ", "charactors ", null};
String result = String.join("", values);
// 结果
I want to splicing some charactors null
(3)使用String.concat()
(4)使用StringBuilder
StringBuilder 类提供了很多有用且方便的 String 构建方法。其中比较常用的是 append() 方法,使
用 append() 来拼接字符串,同时结合 nullToString() 方法来避免 null 值。
String[] values = {"I ", "want to ", "splicing ", "some ", "charactors ", null};
StringBuilder result = new StringBuilder();
for (String value : values) {
result = result.append(nullToString(value));
}
// 结果
I want to splicing some charactors
(5)使用StringJoiner
tringJoiner 类是 Java 8以后加入的一种新的方法。
StringJoiner 类提供了更强大的字符串拼接功能,不仅可以指定拼接时的分隔符,还可以指定拼接
时的前缀和后缀,这里我们可以使用它的 add()方法来拼接字符串。同样的会用 nullToString() 方法
来避免 null 值。
String[] values = {"I ", "want to ", "splicing ", "some ", "charactors ", null};
StringJoiner result = new StringJoiner("");
for (String value : values) {
result = result.add(nullToString(value));
}
// 结果
I want to splicing some charactors
(6)使用Streams.filter()
Stream API 是 Java 8 引入的功能强大的流式操作类,可以进行常见的过滤、映射、遍历、分组、
统计等操作。其中的过滤操作 filter 可以接收一个 Predicate 函数,Predicate 函数接口同 Function
(opens new window)接口一样,是一个函数式接口,它可以接受一个泛型 参数,返回值为布尔类
型,Predicate 常用于数据过滤。
因此,可以定义一个Predicate 来检查为 null 的字符串,然后传递给 Stream API 的 filter() 方法。
最后再使用 Collectors.joining() 方法拼接剩余的非 null 字符串。
String[] values = {"I ", "want to ", "splicing ", "some ", "charactors ", null};
String result = Arrays.stream(values).filter(Objects::nonNull).collect(Collectors.joining());
int length() :返回某字符串的长度
char charAt(int index):返回某索引出的字符
boolean isEmpty() :判断是否是空字符串
String toLowerCase() :将字符串全部转为小写
String toUpperCase() :将字符串全部转为大写
String trim() :去除字符串前后端空格
boolean equals(Object obj):判断两个字符串内容是否相同
boolean equalsIgnoreCase(String anotherString):判断两个字符串内容是否相同,忽略大小写
String concat(String str):将指定字符串拼接到此字符串的后面
int compareTo(String str) :比较两个字符串大小
String substring(int beginIndex):截取字符串,从beginIndex索引出截取到最后一个字符串
String substring(int beginIndex,int endIndex):截取字符串,从beginIndex索引出截取到endIndex索引处,不包含 endIndex。
boolean endsWith(String sufix):测定字符串是否已指定字符串作为后缀
boolean startsWith(String sufix):测定字符串是否已指定字符串作为前缀
boolean startsWith(String sufix,int,toffset):测定字符串以指定索引开始字符串是否已指定字符串作为前缀
boolean contains(char c):测定字符串中是否包含指定的char
int indexof(String str):返回指定字符串在此字符串中第一次出现位置的索引
int indexof(String str,int fromIndex):返回指定字符串在此字符串中第一次出现位置的索引,从fromIndex开始。
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
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个,如果超过了,剩下的全部都放到最后一个元素中。
String --> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)
基本数据类型、包装类 --> String:调用String重载的valueOf(xxx)
@Test
public void test1(){
String str1 = "123";
// int num = (int)str1;//错误的
int num = Integer.parseInt(str1);
String str2 = String.valueOf(num);//"123"
String str3 = num + "";
System.out.println(str1 == str3);
}
String是用final修饰的,不能被改变,也不能被继承,每次对String的操作都会产生新的对象。
String Buffer、String Build都是对原对象进行操作,是可变的,并且String Buffer中的方法是使用
synchronize修饰的,是线程安全的,执行效率:String>String Build>String Buffer
StringBuffer reverse():反转字符串
StringBuffer append(String str):在尾部添加字符串
StringBuffer delete(int start, int end) :删除字符串中索引在[start,end)区间的字符
int charAt(int index):查询某个字符
StringBuff insert(int offset, String str):在offset插入字符str
线程是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。一
条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不
同的任务。
(1)继承Thread类重写run方法:
public class main(){
public static void main(String[] args) {
new Thread1().start();
}
}
class Thread1 extends Thread{
@override
public void run(){
sout("");
}
}
(2)实现runnable接口,重写run方法,然后使用thread类来包装:
public class main(){
public static void main(String[] args) {
Runnable1 runnable = new Runnable1();
new Thread(runnable).start();
}
}
class Runnable1 implement Runnable{
@override
public void run(){
sout();
}
}
(3)实现callable接口 重写call方法,然后包装成FutureTask,再包装Thread:
public class main(){
public static void main(String[] args) {
Callable1 callable1 = new Callable1();
FutureTask futureTask = new FutureTask<>(callable1);
new Thread(futureTask)).start();
}
}
class Callable1 implement Callable{
@override
public Interger call(){
sout();
}
}
Thread是类只能单继承,而Runnable类是接口,可以多实现,使用runnable容易实现资源共享,可以避免java中资源共享的局限性,而且线程池中只能放入实现runnable和callable接口的类,不能放继承Thread类的类
(1)实现callable接口的线程可以返回执行结果,实现runnable接口的线程不可以返回执行结果
(2)Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
start():启动当前线程,调用当前线程的run方法
run():通常要重写Thread类中的此方法,将创建的线程要执行的操作声明在这个方法中
currentThread():返回正在被执行的线程的信息
public class Run1{
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());
}
}
getName():获取当前线程的名称
setName():设置当前线程的名称
yield():释放当前cpu执行权,让当前线程回到就绪状态,以允许相同优先级的其他线程获得运
行机会,但是让步的线程可能再被线程调度程序再次选中
join():是其他线程进入阻塞转态直到自己线程执行完成,可以保证线程的执行顺序
public class ThreadStartOrderTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
System.out.println("this is thread1");
});
Thread thread2 = new Thread(()->{
System.out.println("this is thread2");
});
Thread thread3 = new Thread(()->{
System.out.println("this is thread3");
});
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
thread3.join();
}
}
sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
isAlive():判断当前线程是否存活
线程通常来说有五种状态,创建、就绪、运行、阻塞和死亡状态。
生命周期
(1)创建线程
(2)其他线程调用了start()方法,线程进入就绪状态
(3)就绪状态的线程得到了cpu的执行权,变为运行状态
(4)线程可能因为某种原因停止了cpu的使用权,进入了阻塞状态,阻塞状态的线程只有先进入就绪状态,才有机会转到运行状态
(5)线程执行完毕,或者因为异常退出了run()方法,该线程结束,死亡。
如果由多个线程在同时运行,而这些线程可能会同时运行这段代码,程序每次运行结果和单线程运行的结果一样,而且其他的变量的值也是和预期的一样,就是线程安全的。
通过同步机制解决线程安全问题
(1)同步代码块
说明:
操作共享数据的代码,即为需要被同步的代码。
共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
锁,任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
//线程类
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源
private int ticket =100;
//创建一个锁对象
Object obj = new Object();
//设置线程任务 卖票
@Override
public void run() {
//使用死循环 让卖票操作重复执行
while (true){
//同步代码块
synchronized (this){
//先判断是否存在票
if(ticket>0){
//提高安全问题出现的概率 让线程休眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票存在,卖票ticket--
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
ticket--;
}
if(ticket ==0){
return ;
}
}
}
}
}
//测试方法
public class Test {
public static void main(String[] args) {
//创建线程任务对象
RunnableImpl ticket = new RunnableImpl();
//创建3个窗口对象
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
//同时卖票
t1.start();
t2.start();
t3.start();
}
(2)同步方法
public class Ticket implements Runnable {
private int ticket =100;
/*
卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口永远打开
while (true){
sellTicket();
}
}
//封装卖票方法
public synchronized void sellTicket(){
if(ticket>0){ //有票 可以卖
//出票操作 用sleep模拟
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
//获取当前线程对象的名称
System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--);
}
}
}
(3)锁
同步代码块/同步方法具有的功能lock都有,除此之外更强大,更体现面向对象。编写也更加简单。
Public void lock() 加同步锁
Public void unlock() 释放同步锁
使用步骤:
两个线程,一个是main主线程,一个是垃圾回收机制
(1)lock是一个接口,而Synchronized 是java的一个关键字,、
(2)Synchronized在发生异常是会自动释放占用的锁,因此不会出现死锁;lock在发送异常时不会自动释放锁,必须手动释放锁,可能会发生死锁。
相同点:两个方法都可以使线程进入阻塞状态
不同点:
wait()
notify():唤醒一个被wait()的线程,如果有多个线程被wait(),优先唤醒优先级高的线程
notifyAll():唤醒所有被wait的线程
使用在同步代码块或同步方法中
线程池就是创建若干个可执行线程放入一个池中,需要的时候就获取,使用完也不用销毁再放入线程池中供下回调用,减少线程创建和销毁成本。
线程池管理器:用于创建线程池,销毁线程池,添加新任务。
工作线程:线程池中线程,可循环执行任务,在没有任务时处于等待状态。
任务队列:用于存放没有处理的任务,一种缓存机制。
任务接口:每个任务必须实现的接口,供工作线程调度任务的执行,主要规定了任务的开始和收尾工作,和任务的状态。
corePoolSize:线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为 corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
maximumPoolSize:线程数的上限;
keepAliveTime:线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于 corePoolSize 时才有用;
unit:keepAliveTime的时间单位;
workQueue:用来保存等待被执行的任务的阻塞队列,且任务必须实现 Runable 接口,在 JDK 中提供了如下阻塞队列:
操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene;
threadFactory:线程工厂。定义如何启动一个线程,可以设置线程名称,并且可以确认是否是后台线程等。
handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了 4 种策略:
当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
线程池接口是java.util.concurrent.ExecutorService,使用线程池对象的方法:
Public Future submit(Runnable task) 获取线程池中的某一个线程对象,并执行,Futrue接口:用来记录线程执行任务完毕后产生的结果,线程池创建于使用。
(1)newSingleThreadExecutor:单线程池。
顾名思义就是一个池中只有一个线程在运行,该线程永不超时,而且由于是一个线程,当有多个任务需要处理时,会将它们放置到一个无界阻塞队列中(LinkedBlockingQueue)逐个处理,能保证所提交任务的顺序执行,实现代码如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue
简单用例
public static void main(String[] args) throws ExecutionException,InterruptedException {
// 创建单线程执行器
ExecutorService es = Executors.newSingleThreadExecutor();
// 执行一个任务
Future future = es.submit(new Callable() {
@Override
public String call() throws Exception {
return "";
}
});
// 获得任务执行后的返回值
System.out.println("返回值:" + future.get());
// 关闭执行器
es.shutdown();
}
2)newCachedThreadPool:缓冲功能的线程。
初始化一个可以缓存线程的线程池,默认缓存60s,一旦一个线程在60秒内一直处于等待状态时(也就是一分钟无事可做)则会被终止,线程数量可达到Integer的最大值,内部使用SynchronousQueue作为阻塞队列,其源码如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
(3)newFixedThreadPool:固定线程数量的线程池。
在初始化时已经决定了线程的最大数量,若任务添加的能力超出了线程的处理能力,则建立阻塞队列(linkedBlockingQueue)容纳多余的任务,其源码如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
简单实例
Runnable实现类:
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了:"+Thread.currentThread().getName());
System.out.println("教我游泳,教完后,教练回到了游泳池");
}
}
测试类
public class ThreadPoolDome {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2); //包含2个线程
//创建runnable示例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
//Thread thread = new Thread(r);
//thread.start();
//从线程池中获取线程对象,调用MyRunnable中的run()
service.submit(r);
//再获取一个线程,调用MyRunnable中的run()
service.submit(r);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭
//关闭线程池 一般不做
//service.shutdown();
}
}
(4)newScheduledThreadPool()
特点:初始化的线程池可以在指定时间内周期性执行所提交任务,在实际业务场景中可以使用该线程池定期同步数据。
创建线程池对象步骤:
1.创建线程池对象
2.创建Runnable接口实现类对象(task)
3.提交Runnable接口实现类对象,就会执行run方法
4.关闭线程池(一般不做)
集合包含单列集合collection和双列集合map;collection包含list和set;list是无序可重复的元素,
set是有序不可重复的元素;
list常用的接口实现类有arraylist,linkedlist和vector;arraylist底层是动态数组实现的,linkedlist底
层是双向链表实现的,arraylist比linkedlist查询效率高,因为linkedlist需要移动指针从前往后依次
查找找,在添加和删除元素时,linkedlist比arraylist效率要高,因为arraylist需要改变数组类元素的
下标;arraylist初始容量为10,以1.5倍扩容,同时将原数组拷贝到新数组里面,vector和arraylist
基本相同,只是vector是以两倍扩容,底层用synchronized是用来修饰,保证了线程的安全。
set常用的实现类有HashSet,linkedhashset和treeset;hashset底层哈希表实现,无序且唯一,唯
一靠的是所存储元素的类型是否重写了equals和hashcode保证;linkedhashset底层是链表加哈希
表实现,无序且唯一,保证了元素的顺序和存储顺序相同;treeset有序且唯一,底层是二叉树实现
的。
map常用的实现类用hashmap,hashtable,linkedhashmap,currenthashmap和treemap;
hashmap底层是数组加链表加红黑树实现的,当数组长度大于64,链表长度大于8,转为红黑树,
在jdk1.8后,允许key和value为null,初始容量为16,以两倍扩容,扩展因子0.75,当容量达到最
大容量的75%就会触发扩容;
hashtable不允许存储key和value为null的值,初始容量为11,以两倍加1扩容,底层通过
synchronized修饰,保证了线程的安全;
treemap底层红黑树实现的,继承sortmap,对元素进行了排序;currenthashmap底层是数组加链
表加分段锁方式实现,分段锁继承lock锁,将全局加锁改成了局部加锁,提高了并发环境加的操作
速度,即内部拥有一个数组,数组的每个元素是一个链表,有包含一个lock锁。
collection是最基本的接口,有set和list两个子接口;collections是一个包装类,包含了操作集合的
静态方法,比如搜索、排序,是一个工具类,是服务于collection和map集合。
frequency(),sort(),max(),min(),replaceAll(),copy(),reverse(),shuffle(),swap()
reverse(list):反转list集合中元素的数据;
shuffle(list):对list集合中的元素进行随机排序
sort(list):根据元素的自然顺序对list集合中的元素进行升序排序
sort(list,Comprator):根据Comprator指定的元素顺序对list集合中的元素进行排序
swap(list,int i ,int j):将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 对象的所旧值
数组长度固定,无法扩容,只能存储一种数据类型;集合可以扩容,可以存储不止一种数据类型
Iterator iterator = coll.iterator();
if(iterator.hasNext()){//判断是否还有下一个元素
iterator.next() //将指针下移,并获取到下一个元素
}
list中的增删改查插:add(), remove() ,set(), get().
遍历的方式有iterater、增强for、普通for循环。
map中的增删改查:put(), remove(), put(), get()
长度查询:size()
遍历的方式有keyset、entryset、迭代器。
(1)ketSet方式
for(String key:map.keySet()){
Objectvalue=map.get(key);
sout(key+value);
}
(2)entrySet
for(Entry entey:map.entrySet){
sout(entry)
}
(3)迭代器遍历
Iterator> it = map.entrySet().iterator() ;
while(it.hasNext())){
sout(it.next);
}
守护线程和普通线程是多线程编程中的两种不同类型的线程。
普通线程是一种常见的线程类型,它的运行不会影响整个程序的退出。当所有的普通线程执行完毕后,程序才会退出。
守护线程是一种特殊的线程类型,它的存在依赖于其他非守护线程。当所有的非守护线程执行完毕后,无论守护线程是否执行完毕,程序都会退出,并且守护线程会被强制终止。
守护线程通常用于在后台执行一些任务,比如定时任务、日志记录等。它们经常被用来处理一些不需要阻塞整个程序的任务。
需要注意的是,守护线程不能用于执行一些可能导致数据不一致或资源泄漏的任务,因为它们可能会在程序退出时被强制终止,从而无法完成清理工作。
总结来说,守护线程和普通线程在程序退出时的行为不同,守护线程会被强制终止,而普通线程不会。