一.死锁:
1.当两个或者更多隔线程互相持有对象想要的资源不放手,就会死锁
2.当同步代码快或同步方法中出现了嵌套使用
(1)同步代码快中,出现了另一个同步代码快,或调用了另一个同步方法
(2)同步方法中,出现了同步代码快,或者调用了另一个同步方法,而且两者之间有使用对象要的锁对象等资源时,就有可能出现死锁状态,尽量避免
如果出现了,要么手动停止,或者说我们要制造异常,让线程从死锁状态出来,然后再去抢夺资源。
2.sleep方法和wait方法有什么区别?
(1)sleep方法在Thread类中声明,是静态方法,通过Thread类.sleep()调用
(2)wait方法在Object类中声明,是非静态方法,通过锁对象.wait() 调用
wait方法和notify为什么要在Object中声明“
因为wait方法必须是对象调用,但是锁对象是任意类型的对象
那么意味着任意类型的对象都必须具备wait方法,这样的方法只能放在根父类中声明
(2)sleep方法是必须制定时间,等待时间到了,或中途被interrupt了,就会自动醒来
wait方法,一种是指定时间,一种是无限时等待如果无限时等待必须notify唤醒之后才能继续
(3)当前线程遇到了sleep方法不会释放锁得,一直要等当前线程结束,才会释放锁
public class TestSleepWait {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
m1.start();
m2.start();
}
}
class MyThread extends Thread{
private int num = 1;
public void run(){
synchronized (MyThread.class) {
System.out.println(num++);
/* try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
try {
MyThread.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
二、常用类之数学
1.java.long,math类
(1)public static double sqrt(double a):求x的平方根
(2)public static double random)(): 返回带正号的double 值,大于等于0.0且小于1.0
(3)public static double pow (double a, double b):返回第一个参数的第二个参数次幂,即a的b次方
(4)public static double abs (double a ) 返回绝对值
(5)public static double max(double a, double b):求a和b的最大值
(6)public static double min(double a, double b):求a和b的最小值
(7)public static double floor (double a);向下取整
public static double ceil (double a);向上取整
public static long round (double a);四舍五入取整(最接近的整数)
-x.5比较特殊
2.java.math包
java.math,BigDecimal:定点小数和大的小数,可以精确到小数点后n位,如果可以整除,可以用该方法,如果不能整除,说明保留小数点后多少位
float 和double :浮点数,不精确.
java.math.BigInerger:表示大整数
用法 BigInteger ig1 =new Big Integer(参数);
3.java.util,Random类:产生随机数
可以产生随机的整数、小数、boolean等所有数据类型
Math.random只能产生【0,1】之间的随机小数
Random random = new Random();
三、日期时间API
1.jdk中的日期时间API经历了三代
第一代:java.util.Date等
第二代:java.util.Calendar等
第三代:java.time包及其子包的类型
2.java.util.Date类
(1)Date() 获取当前系统时间
(2)Date(long time),根据指定的毫秒值,获取对应的日期时间
(2)Long getTime()获取当前系统时间距离1970-1-1凌晨的毫秒值
等同于 System.currentTimeMillis()
问题:为什么开始于1970年
Unix操作系统在1970年设计数来,把1970-1-1凌晨作为计算机时间的原始起点
Date类型大部分都是已过时的方法
3.java.util.Calendar类
他是一个抽象类,
如何创建对象:
(1)创建它子类对象
(2)在Calendar类中提供静态方法
Date和Calendar:
(1)使用麻烦
无法获取满足本地人阅读习惯的日期时间对象
需要借助DateFormat,它是抽象类,他有一非常
(2)可变对象:在开发中,日期对象,某个对象某个瞬间的日期时间,是不应该变的
(3)没有考虑闰秒
4.java8引入,java.time包:包含值对象的基础包
(1)本地的日期类型的API
LocalDate:日期、
Localtime:时间
LocalDateTime:日期和时间
LocalDateTime.now()方法指当前时间
LocalDateTime.of()方法可以指定时间
.isLeapYear():判断指定时间是不是闰年
修改日期或时间对象会返回一个新的对象结果,必须接受
5.java.long.System
(1)System.out 对象
(2)System.in 对象
(3)public static long currenTimeMillis():获取距离1970年的时间
(4)public static void arraycopy(Object src,int srcPos,Object,destPos,int length)
src:原数组
srcPos:原数组的起始位置
dest:目标数组
destPos:目标数组的起始位置
length:要挪动的元素的长度
total个元素, arr.length长度 假设length = 10, total = 7, 元素的下标[0,6]
i=2 移动[3],[4],[5],[6]
删除arr数组的[i] 元素: System.arraycopy(arr, i+1, arr, i, total-i-1)
i=2 移动[2],[3],[4],[5],[6]
插入arr数组的[i]元素: System.arraycopy(arr, i, arr, i+1, total-i)
(5)public static void gc():通知GC工作,进行垃圾回收,通知后,不代表GC线程立刻工作。
(6)public static void exit(int status):终止当前运行的Java虚拟机。 该参数作为状态代码; 按照惯例,非零状态码表示异常终止。
(7)public static Properties getProperties():获取所有的系统数属性
6.java.lang.Runtiam类
runtime.getRrntime()代表JVM的运行环境
四、数组的算法升华
1.数组的反转
方案一:
先新建一个数组,然后把元素按照反转之后的正确的顺序放进去,指向新的数组
缺点:浪费空间
方案二:
首尾交换
public class TestArray {
@Test
public void test01(){
int[] arr = {1,2,3,4,5};
//反转之后:arr中{5,4,3,2,1}
//方案一:先新建一个数组,然后把元素按照反转之后的正确的顺序放到新数组中,让arr指向新数组
int[] newArr = new int[arr.length];
//把arr中的元素逆序放到newArr
for (int i = 0; i < newArr.length; i++) {
/*newArr[0] = arr[arr.length-1];
newArr[1] = arr[arr.length-2];*/
newArr[i] = arr[arr.length-1-i];
}
//让arr指向新的数组
arr = newArr;
//遍历
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
@Test
public void test02(){
int[] arr = {1,2,3,4,5};
//首尾交换
/*
1 2 3 4 5
1和5换
2和4换
5个元素换2次
10个元素:5次
交换的次数 = 数组的长度/2(整数与整数相除只保留整数部分)
*/
//循环的次数 = 交换的次数
for (int i = 0; i < arr.length/2; i++) {
//交换的过程
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
//遍历
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
2.数组的扩容
public class TestArray2 {
@Test
public void test01(){
int[] arr = {1,2,3,4,5};
//我想要在当前数组的基础上,增加一个元素6,放在最后
/*
思路:
(1)创建新数组
(2)把原来的元素复制过程
(3)把新元素放进去
(4)让arr指向新数组
*/
int[] newArr = new int[arr.length+1];
for (int i=0; i <arr.length; i++){
newArr[i] = arr[i];
}
// newArr[newArr.length-1] = 6;
arr = newArr;
arr[arr.length-1] = 6;
}
}
3、数组的插入
ublic class TestArray3 {
@Test
public void test01(){
int[] arr = {1,2,3,4,5};
//需求:要在arr[1]的位置插入6,结果{1,6,2,3,4,5}
/*
(1)先创建一个新数组newArr,新数组的长度 > 旧数组
(2)把arr中[0,index-1]的元素放到newArr中
(3)把新元素放到newArr中[index]
(4)把arr中[index,arr.length-1]放到newArr中
(5)最后arr指向newArr
*/
int[] newArr = new int[arr.length+1];
int index = 1;//插入的位置
int value = 6;//插入的值
for (int i=0; i<index; i++){
newArr[i] = arr[i];
}
newArr[index] = value;
for(int i=index; i<arr.length; i++){
newArr[i+1] = arr[i];
}
arr = newArr;
//遍历
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
@Test
public void test02(){
String[] arr = {"hello","java","world",null,null};
/*
要在index=1的位置插入"atguigu",结果{"hello","atguigu","java","world",null};
思路:
(1)把index及其后面的元素后移
(2)然后把新元素放到[index]
*/
int total = 3;
int index = 1;//插入的位置
System.arraycopy(arr, index, arr, index+1, total-index);
arr[index] = "atguigu";
total++;
//遍历
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
/*
在arr[index]位置插入value
*/
public int[] insert(int[] arr,int index, int value){
/*
方案一:
(1)先创建新数组,扩容
(2)把arr的所有元素,原封不动的赋值到新数组newArr
(3)再把index位置及其后面的元素后移
(4)把value放到index位置
(5)返回新数组
*/
int[] newArr = new int[arr.length+1];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
System.arraycopy(newArr, index, newArr, index+1, arr.length-index);
newArr[index]=value;
return newArr;
}
@Test
public void test03(){
int[] arr = {1,2,3,4,5};
int index = 1;
int value = 6;
arr = insert(arr, index, value);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
4、数组的元素删除
public class TestArray4 {
@Test
public void test01(){
String[] all = {"hello","java","world",null,null};
//删除index=1位置的元素
int index = 1;
int total = 3;
//把index+1后面的元素往前移动
System.arraycopy(all, index+1, all, index, total-index-1);
all[total-1] = null;
//遍历
for (int i = 0; i < all.length; i++) {
System.out.println(all[i]);
}
}
}
5.数组的二分查找
之前:数组的查找,是顺序查找,从[0]开始,挨个比较是否目标对象,如果是,找到了,如果不是,一直找到最后
现在:二分查找,又称为折半查找
二分查找有要求,数组必须是有序的
public class TestArray5 {
@Test
public void test01(){
int[] arr = {2,5,7,8,10,15,18,20,22,25,28};//数组是有序的
int value = 18;
//查找value是否在arr中,如果在,打印下标,如果不在,说明不在
int left = 0;
int right = arr.length-1;
int mid = (left +right)/2;
int index = -1;
while(left <= right){
if(arr[mid] == value){
index = mid;
break;
}else if(value > arr[mid]){
left = mid +1;
}else{
right = mid - 1;
}
mid = (left + right) / 2;
}
if(index==-1){
System.out.println("没找到");
}else{
System.out.println("找到了,下标是:" + index);
}
}
}
6.数组的直接排序=冒泡
public class TestArray6 {
@Test
public void test01(){
int[] arr = {49,38,65,97,76,13,27,49};
/*
直接选择排序:
思路:每一轮,找出本轮未排序元素中的最小/最大值,看它是否在正确的位置,如果不在,就与该正确位置的元素交换,这样经过多轮之后,实现排序
最坏的情况:arr.length-1轮
{49,38,65,97,76,13,27,49} 从小到大
第一轮:在[0,arr.length-1]范围的最小值 arr[5]=13是最小的,它应该在arr[0],所以要arr[0]与arr[5]
{13,38,65,97,76,49,27,49}
第二轮:在[1, arr.length-1]范围找最小值 arr[6]=27是最小的,它应该在arr[1],所以要arr[1]与arr[6]
{13,27,65,97,76,49,38,49}
。。。。
*/
for (int i=0; i<arr.length-1; i++){
//第1轮:i=0, j=0到arr.length-1
//第2轮:i=1, j=1到arr.length-1
//第3轮:i=2, j=2到arr.length-1
//...
//第n轮:i=arr.length-2, j=arr.length-2到arr.length-1
//j=i
int min = arr[i];//假设本轮的第一个元素最小
int index = i;//本轮最小值的下标
for(int j=i; j<=arr.length-1; j++){
if(arr[j] < min){
min = arr[j];
index = j;
}
}
if(index != i){
//交换 arr[index] 和 arr[i]
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
//结果
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
@Test
public void test02() {
int[] arr = {49, 38, 65, 97, 76, 13, 27, 49};
/*
第一轮:拿所有元素与arr[0]位置的元素比较,有比它小的,就交换
第一次:{38, 49, 65, 97, 76, 13, 27, 49};
第二次:
第三次:
第四次:
第五次:{13, 49, 65, 97, 76, 38, 27, 49};
第六次:
第七次:
第二轮:拿所有元素与arr[1]位置的元素比较,有比它小的,就交换
。。。
*/
for(int i=0; i<arr.length-1; i++){
for(int j=i; j<=arr.length-1; j++){
if(arr[j] < arr[i]){
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
@Test
public void test03(){
int[] arr = {49,38,65,97,76,13,27,49};
for (int i=0; i<arr.length-1; i++){
int index = i;//本轮最小值的下标
for(int j=i; j<=arr.length-1; j++){
if(arr[j] < arr[index]){
index = j;
}
}
if(index != i){
//交换 arr[index] 和 arr[i]
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
//结果
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
五、数组的工具类
java.util.Arrays工具类:此类包含用来操作数组(比如排序和搜索)的各种方法。
开发中:能够用API的方法,肯定是最简洁的,
面试中,需要知道底层是如何实现的,甚至有些算法要手动会实现。
System.arraycopy这个方法和数组关系,为啥在System类中,不在Arrays类?
因为版本的问题。JDK1.0时候,还没有Arrays工具类,但是arraycopy这个方法已经在使用了,
当时没地方放这个方法,只好放在系统类中。后期所有和数组有关的方法都在Arrays类中。
(1)public static int binarySearch(int[] a, int key):二分查找
如果它包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。插入点 被定义为将键插入数组的那一点:
它有很多重载形式,形参的类型不同
(2)public static int[] copyOf(int[] original,int newLength):复制数组
它有很多重载形式,形参的类型不同
(3)public static int[] copyOfRange(int[] original,int from, int to)
它有很多重载形式,形参的类型不同
(4)public static boolean equals(int[] a, int[] a2):比较两个数组是否相同
它有很多重载形式,形参的类型不同
(5)public static void fill(int[] a,int val):用val填充整个数组
它有很多重载形式,形参的类型不同
(6)public static void sort(int[] a) :对指定的 int 型数组按数字升序进行排序。该排序算法是一个经过调优的快速排序法
它有很多重载形式,形参的类型不同
(7)public static void sort(Object[] a):给对象数组排序,按升序排列,要求元素必须是java.lang.Comparable
public static void sort(Object[] a, Comparator c):按升序排列,需要一个Comparator实现类对象
(8)public static String toString(int[] a)
它有很多重载形式,形参的类型不同