Java开发基础(三)

Java开发基础(三)

系观看尚硅谷视频笔记,侵删


part1

1. 多线程

1.1 多线程概念

  1. Java 创建线程的方式有四种,解决线程安全问题有3种方法。
  2. 理解并行与并发

1.2 多线程的创建

方式一:继承于Thread类
1.创建一个继承于Thread类的子类
2.重写Thread类的run() --> 将此线程执行的操作声明在run()中
3.创建Thread类的子类的对象
4.通过此对象调用start()
注意:
1.不能通过直接调用run()方式启动线程
2.再启动一个线程,是不可以的,不能让已经start()的线程去执行。可以再造一个新对象
3. 还可以通过创建匿名对象的方式来简写

1.3 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 millitime):让当前线程“睡眠"指定的millitime亳秒。在指定的millitime亳秒时间内,当前
    线程是阻塞状态。
  10. isAlive():判断当前线程是 否存活

1.3 线程的优先级

  1. 分级
    MAX PRIORITY: 10
    MIN_ PRIORITY: 1
    NORM_ PRIORITY: 5 --> 默认优先级
  2. 如何获取和设置当前线程的优先级:
    getPriority():获取线程的优先级
    setPriority(int p): 设置线程的优先级
  3. 说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下
    被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

1.4 多线程的创建

方式二:实现Runnable接口

  1. 创建一个实现了Runnable接口的类
  2. 实现类去实现Runnable中的抽象方法: run()
  3. 创建实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类的对象 调用start()

1.5 比较创建线程的两种方式。

开发中:优先选择:实现Runnable接口的方式
原因: 1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。
联系: public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

1.6 线程的生命周期

在这里插入图片描述

1.7 线程安全问题:

方式一、同步代码块
synchronized(同步监视器){
// 需要被同步的代码
}
说明: 1. 需要共享数据的代码,即为需要被同步的代码。 不能包含代码多了,也不能包含代码少了。
2. 共享数据: 多个线程共同操作的变量。 比如: ticket 就是共享的数据
3. 同步监视器,俗称:锁。任何一个类的对象都可以充当锁
要求: 多个线程必须要共用同一把锁。保证唯一性。可以考虑用this。也可以用类,类也是对象。

方式二、同步方法
如果操作共享数据的代码完整的声明在一个方法中, 我们不妨将此方法声明同步的。
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要 我们显式的声 明。
2.非静态的同步方法,同步监视器是: this (用在实现接口的那种多线程) 直接加synchronized
静态的同步方法,同步监视器是:当前类本身 (用在继承那种多线程) 加static synchronized

同步虽然解决了线程安全问题(好处),但是操作代码时,只能有一个线程参与,相当于单线程,效率低。

死锁问题:
1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃, 都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2.说明:
1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
2)我们使用同步时,要避免出现死锁。

解决线程安全问题的方式三: Lock锁— JDK5.0新增
1.面试题: synchronized 与Lock的异同?
相同:二者都可以解决线程安全问题
不同: synchronized 机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock() )
2.优先使用顺序:
Lock → 同步代码块(已经进入了方法体,分配了相应资源) →同步方法(在方法体之外)

1.8 线程的通信

线程通信的例子:使用两个线程打印1-100。 线程1,线程2交替打印
涉及到的三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的线程
notifyAll():-旦执行此方法,就会唤醒所有被wait的线程。
说明:

  1. wait(), notify(), notifyAll()三个方法必须使用在同步代码块或同步方法中。
  2. wait(), notify(), notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器
    否则,会出现ILlegalMonitorStateException异常
  3. wait(), notify(), notifyALl()三个方法是定义在java. Lang. object类中。

面试题: sleep() 和wait()的异同?
1.相同点:一且执行方法,都可以使得当前的线程进入阻塞状态。
2.不同点: 1)两个方法声明的位置不同: Thread类中声明sLeep() 。 0bject类中声明wait()
2)调用的要求不同: sleep()可以在任何需要的场景下周用。 wait()必须使用在同步代码块或者同步方法中
3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中, sLeep()不会释放锁,wait()会释放锁

线程通信的应用:经典例题:生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
分析:
1.是否是多线程问题?是,生产者线程,消费者线程
2.是否有共享数据?是,店员(或产品)
3. 如何解决线程的安全问题?同步机制,有三种方法
4.是否涉及线程的通信?是

1.9 创建线程的方式三:实现Callable接口。

1.创建一个实现CallabLe的实现类
2.实现call方法,将此线程需要执行的操作声明在call()中
3.创建CalLabLe接口实现类的对象
4.将此CaLLabLe接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象, 并调用start()
6.获取Callable中call方法的返回值

如何理解实现CaL lable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?

  1. call() 可以有返回值的。
  2. call() 可以抛出异常,被外面的操作捕获,获取异常的信息
  3. Callable是 支持泛型的

1.10 线程池

  1. 提供指定线程数量的线程池
  2. 执行指定的线程的操作。需要提供实现Runnable接口或CalLable接口实现类的对象
  3. 关闭连接池(线程池)
    ExecutorService service = Executors.newFixedThreadPool( nThreads: 10);
    service. execute(new NumberThread());//适合适用于Runnable
    service. execute( new NumberThread1());//适合适用 于Runnable
    service. submit(Callable callable);//适合 使用于Callable
    service . shutdown( );
    开发中常用线程池

好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolsize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

创建多线程有4种方式。

2. 常用类

2.1 String 类

2.1.1 String 类基础

String:字符串,使用一对""引起来表示。

  1. String声明为final的,不可被继承
  2. String实现了Serializable接口:表示字符串是支持序列化的。
    实现了Comparable接口:表示String 可以比较大小.
  3. String内部定义了finaL char[] value用于存储字符串数据
  4. String:代表不可变的字符序列。简称:不可变性。
    体现: 1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
    2.当对现有的字符事进行连接操作时,也需要重新指定内存区域赋值,
    3.当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,
  5. 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
  6. 字符串常量池中是不会存储相同内容的字符串的。
  7. string的实例化方式:
    方式一:通过字面量定义的方式
    方式二:通过new +构造器的方式
  8. 结论:
    1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
    2.只要其中有一个是变量,结果就在堆中。
    3.如果拼接的结果调用intern()方法,返回值就在常量池中
2.1.2 String 常用方法

注意:

  1. 对String操作的时候一定注意不可变性。
  2. String严格区分大小写

常用方法一:
int length(): 返回字符串的长度: return value. length
char charAt(int index): 返回某索引处的字符return vaLue[ index]
boolean isEmpty(): 判断是否是空字符串: return value. length ==日
String toLowerCase(): 使用默认语言环境,将String 中的所有字符转换为小写
String toUpperCase(): 使用默认语言环境,将String 中的所有字符转换为大写
String trim(): 返回字符串的副本,忽略前导空白和尾部空白
boolean equals(Object obj): 比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString): 与equals方法类似,忽略大小写
String concat(String str): 将指定字符串连接到此字符串的结尾。 等价于用“+”
int compareTo(String anotherString): 比较两个字符串的大小
String substring(int beginIndex): 返回-一个新的字符事, 它是此字符串的从beginIndex开始截取
String substring( int beginIndex,int endIndex) :返回一个新字符串

常用方法二:
boolean endsWith(String suffix): 测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始
boolean startsWi th(String prefix, int toffset): 测试此字符串从指定索引开始的子字符串

boolean contains (CharSequence s): 当且仅当此字符串包含指定的char 值序列时,返回true
int index0f(String str): 返回指定子字符串在此字符串中第一次出现处的索引
int index0f(String str, int fromIndex): 返回指定子字符串在此字符串中第- 次出现处的索引
int LastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int LastIndexOf(String str, int fromIndex): 返回指定子字符串在此字符事中最后一次出现处
注: indexOf和LastIndex0f方法如果未找到都是返回-1

常用方法三:
替换:
String replace(char oLdChar, char newChar):返回一一个新的字符串,它是通过用newChar
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): 根据匹配给定的正则表达式来拆分此字符串,

String与char[]之间的转换
String --> char[]: 调用String的toCharArray()
char[] --> String:调用String的构造器

String与byte[]之间的转换
编码: String --> byte[]: 调用String的getBytes()
解码: byte[] --> String:

2.1.3 stringbuffer 和 stringbuilder

String、StringBuffer. StringBuilder三者的异同?
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
StringBuilder:可变的字符序列; jdk5.θ新增的, 线程不安全的,效率高;底层使用char[]存储

源码分析:
String str = new String();//char[] value = new char[0];
String str1 = new String(“abc”);//char[] value = new char[]{‘a’, ‘b’,‘c’};
StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度为16
System.out .println(sb1. Length());//
sb1. append( ‘a’);//value[0] = ‘a’;
sb1. append(‘b’);//value[1] = ‘b’;
StringBuffer sb2 = new StringBuffer( “abc”);//char[] value = new char[ “abc”. Length() + 16]
//问题1. System. out . println(sb2. Length());//3
//问题2.扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
默认情况下,扩容为原来容量的2倍+ 2,同时将原有数组中的元素复制到新的数组中。

指导意义:开发中建议大家使用: StringBuffer(int capacity) 或StringBuilder(int capacity)

StringBuffer的常用方法:
StringBuffer append(xxx): 提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start, int end): 删除指定位置的内容
StringBuffer replace(int start, int end, String str): 把[start, end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int index0f(String str)
public String substring(int start, int end): 返回一个从start开始到end索引结束的左闭右开区间的子字符串
public int Length()
public char charAt(int n )
pubLic void setCharAt(int n , char ch)

对比String、StringBuffer、 StringBuilder. 三者的效率:
从高到低排列: StringBuilder > StringBuffer > String

2.2 日期时间的API

2.2.1

System.类中的currentTimeMillis()
java. utiL.Date类
/—java. sql.Date类
1.两个构造器的使用
->构造器一: Date(): 创建-一个对应当前时间的Date对象
->构造器二:创建指定毫秒数的Date对象
2.两个方法的使用
->toString():显示当前的年、月、日、时、分、秒
->getTime():获取当前Date对象对应的毫秒数。(时间戳)
3. java. sqL. Date对应着数据库中的日期类型的变量
->.如何实例化
->如何将java. util. Date对象转换为java. sql. Date对象

2.2.2 SimpleDateFormat类的使用

SimpleDateFormat 对日期Date类的格式化和解析
1.两个操作:
1.1格式化:日期—>字符串
1.2解析:格式化的逆过程,字符串—>日期
2. SimpleDateFormat的实例化
3. 方法 format parse

2.2.3 Calendar日历类(抽象类)的使用

//1.实例化
//方式一:创建其子类. (GregorianCalendar)的对象
//方式二:调用其静态方法getInstance()
Calendar calendar = Calendar . getInstance();
2. 常用方法
get()
set()
add()
getTime()
setTime()

2.2.4 JDK8之后的日期时间

LocalDate、LocaLTime、 LocaLDateTime的使用
说明:

  1. LocalDateTime相较于LocalDate、LocalTime,使用频率要高
    now():获取当前的日期、时间、日期+时间
    of():设置指定的年、月日、时、分秒。没有偏移量
  2. instant
    now()获取本初子午线对应的标准时间
  3. DateTimeFormatter 格式化活着解析日期时间。类似于上面的SimpleDataFormat

2.3 Java比较器

说明: Java中的对象,正常情况下,只能进行比较: ==或!=。不能使用>或<的但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。如何实现?使用两个接口中的任何-一个: Comparable或Comparator

  1. comparable 接口的使用
    Comparable接口的使用举例: (自然排序)
    1.像String、包装类等实现了Comparable接口,重写了compareTo()方法,
    2.重写compareTo()的规则:
    如果当前对象this大于形参对象obj,则返回正整数,
    如果当前对象this小于形参对象obj,则返回负整数,
    如果当前对象this等于形参对象obj,则返回零。
    3.对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法,在compareTo(obj).方法中指明如何排序。
  2. Comparator接口的使用:定制排序
    1.背景:
    当元素的类型没有实现java. Lang. Comparable接口而又不方便修改代码,
    或者实现了java. lang . Comparable接口的排序规则不适合当前的操作,
    那么可以考虑使用Comparator的对象来排序
    2.重写compare(Object o1, Object o2)方法,比较o1和o2的大小:
    如果方法返回正整数,则表示o1大于o2;
    如果返回e,表示相等;
    返回负整数,表示o1小于o2。

Comparable接口与Comparator的使用的对比:
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小
Comparator接口属于临时性的比较。

Part2

注解和反射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值