Java学习笔记(持续更新)

JAVA

For循环

Loop : for (){
    for(){
        break Loop;//这里的break为直接跳出Loop后的循环,而非跳出本级循环
        continue Loop;//continue同理
    }
}

String 类

char a[] = {'I','am','java'};
String str = new String(a);

等价于

String str = new String("Iamjava");
new String(a,2,4)//从数组第二个开始截取,取四个字符形成新的字符串 :mjav

indexof()函数

String str = "hello world";
int size = str.indexOf("o");//查找第一次出现该字符时的位置
int size = str.lastIndexOf("d")//查找最后一次出现该字符的位置

charAt()函数

String str = "hello world";
char mychar = str.charAt(6);//查找设定位置中字符时是什么

substring()函数

String str = "xxxcccbbb"
String substr = str.substring(3)//从第三个位置开始截取,形成新得字符串:cccbbb
String substr = str.substring(3,6)//从第三个位置开始,第六个位置结束,截取新得字符串:ccc
	### trim()函数
String str = "    hello world    ";
str.trim();//去掉字符串前面和尾部的空格,保留字符串中的空格
	### replace()函数
String str ;
str.replace(oldchar,newchar);//将str中的字符替换为新得字符,如有多个,就都替换
//前提是oldchar和newchar都必须只是大小写关系比如'a''A'不能是'a''B'

startsWith()与 endsWith()函数

boolean re = str.startsWith("abc");//判断字符串是否是以指定的字符串开头
boolean re = str.endsWith("abc");//判断字符串是否是以指定的字符串结尾
//两者都返回boolean类型

equalsIgnoreCase()函数

equals()函数是对字符串区分大小写的比较

而equalsIgnoreCase()对字符串比较不区分大小写,返回类型依旧是boolean (可用于验证码)

compareTo()函数

比较两个字符串在Unicode中值的大小

比如 b 与 a 比较 返回 1 ,b 与 c 比较 返回 -1 ,相同则为0(仅在equals中返回true时)

toLowerCase()和toUpperCase()

这两个方法的作用分别是将字符串的所有字符转换为小写和大写

split()函数

str.split("分割符");
//如果想要多个分隔符,可使用“|”,例如 “,|=” 表示分割符为“,” 和 “=”
str.split("分割符",分割次数)

分割符为想要以字符串的什么作为分割

如152.122.111.255 如果分割符为“ . ”那么就会被分割成4个数组

如果加上分割次数,例:分割次数为两次 会生成 “152” 和“122.111.255”两个字符串数组

format()函数

String类型的格式化:详见Java从入门到精通清华大学版P92

正则表达式

相关规则详见详见Java从入门到精通清华大学版P92

String regex = ""//正则表达式
str.matches(regex);//判断str是否满足正则表达式 返回true或false

StringBuilder

​ String类每次改变都会重新产生一个新得实例,会在内存中创建新的字符串对象。

​ StringBuilder操作消耗时间更少

StringBuilder str = new StringBuilder();//定义StringBuilder的方式

​ 1、append(content)方法:向字符串中添加数据,content类型可以接收任何类型的数据

​ 2、insert(int affset,arg)方法:用于向支付串中指定的位置插入数据内容,可插入int、float、char、boolean等基本类型或其他对象

​ offset:插入的具体位置;arg:插入的数据

​ 3、delete(int start,int end)方法:删除指定位置的字符,删除到end-1位,如果start等于end,不发生任何更改。


数组

一维数组

int arr[];
int[] arr;//定义数组两种形式
arr = new int[20];//为数组分配内存

二维数组

int arr[][];
int[][] arr;
arr = new int[2][4];//为二维数组分配内存
arr[0] = new int[2];
arr[1] = new int[3];//为二维变量每一维度分配空间
int arr[][] = {{12,0},{22,3}};//为二维数组初始化值
/**
	二维数组的遍历
**/
public class void main(String[] args){
    int a[][] = new int[3][4];
    for(int i = 0;i < a.length;i++){//查看二维数组的长度
        for(int j = 0;j < a[i].length;j++){//查看每一维度的长度
            System.out.println(a[i][j]);
        }
    }
}

fill()填充替换函数

fill(int[] a,int value);
//a:需要替换填充的数组 value:要储存进入数组的值
Arrays.fill(arr,8);//将arr数组中的元素全部替换为8
fill(int[] a,int start,int end,int value);
//a:为要替换的数组 start:开始替换的位置 end:结束替换的后一个位置 value:要替换的元素

sort()排序函数

Arrays.sort(数组);//将数组从小到大进行排序

​ Java中的String类型数组的排序算法是根据字典编排顺序排序的,因此数字排在字母前面,大写字母排在小写字母前面

copyOf()函数

int arr[] = {1,2,3};
int newarr[] = Arrays.copyOf(arr,5);//arr表示需要赋值的数组,5表示复制几个从第一个开始计算,如果超出arr长度,以0补全
int newarr[] = Arrays.copyOfRange(arr,0,3);
//arr作用同上,0表示从第几个开始,3表示复制复制到第几位前

数组查询

​ Arrays类的binarySearch()方法,可使用二分搜索法来搜索指定数组。

binarySearch([] a ,key);//a为搜索的数组,key为要搜索的值,返回搜索值的索引位置

必须在调用此函数前给数组排序:sort()函数,而且数组带有多个指定搜索的数,则值是不确定的。

binsarySearch([] a,start,end,key);//start和end为指定搜索的区间索引

数组排序算法

冒泡排序

​ 基本思想:对比相邻的元素值,如果满足就交换元素值,把较小的元素移动到数组前面,把较大的元素移动到数组后面

public class BubbleSort{
    public static void main(String[] args){
        //创建一个数组,这个数组元素顺序是混乱的
        int[] array = {63,4,24,1,3,15};
        //创建冒泡排序类的对象
        BubbleSort sorter = new BubbleSort();
        //调用排序方法将数组排序
        sorter.sort(array);
    }
    
    public void sort(int[] array){
        for(int i = 1;i<array.length;i++){
            //比较相邻两个元素,较大的数往后冒泡
            for(int j = 0;j<array.length-i;j++){
                if(array[j] > array[j + 1]){
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }
    }
}

直接选择排序

​ 基本思想:找出数组中最大或者最小的,从数组中拿出来放到最前面或最后面,然后继续从剩下的数组中找出依次放到最前面或最后面,直到结束

public void sort(int[] array){
    int index;
    for(int i = 1; i < array.length;i++){
        index = 0;
        for(int j = 1;j <= array.length - i;j++){//找到数组中最大的数的下标
            if(array[j] > array[index]){
                index = j;
            }
        }
        //交换在位置array.length-i和index(最大值)上的两个数
        int temp = array[array.length - i];
        array[array.length - i] = array[index];
        array[index] = temp;
    }
}

反转排序

​ 基本思想:把数组顺序颠倒,比如123456排序后为654321

public void sort(int[] array){
    int temp;
    int len = array.length;
    for(int i = 0;i < len / 2;i++){
        temp = array[i];
        array[i] = array[len - 1 - i];
        array[len - 1 - i] = temp;
    }
}

类和对象

类是世间事物的抽象称呼,而对象是这个事物相对应的实体

面向对象程序设计特点:封装、继承、多态

private protected public
本类 O O O
同包或子类 O O
其他包或子类 O

​ 当声明不使用权限修饰符时,声明的类预设为包存取范围,及只有本包的类可以调用这个类的成员变量和方法,如果类中的方法声明为public等权限符,无效

访问权限依旧和类权限相同。

局部变量:局部变量在方法执行时创建、在方法执行结束时销毁。

this除了可以调用成员变量或成员方法外,还可以作为返回值

return this;//返回该类

静态变量static

类名.变量名/方法名//调用静态元素

规定:

1、在静态方法中不可以使用this关键字

2、静态方法中不可以直接调用非静态方法

3、不能在常规方法中定义静态局部变量

==和equals()的不同

​ ==是比较两个字符串引用的地址是否相同,equals()是比较两个字符串的字符是否相同

包装类

Integer、Boolean、Byte、Character、Double、Number

Integer

两种构造方法:

1、Integer number = new Integer(7);
2、Integer number = new Integer("7");//要用数字的字符串,不然会报NumberFormatException错误

面试常见问题

HashMap

​ 工作原理:将键值对传递给put()方法,利用hashcode()方法计算hashcode,找到bucket位置存储对象,get()方法得到对象时,利用equals()方法找到正确的键值对,然后返回。

原理:键值对(Entry)存储在数组中,HashMap每一个元素的初始值都是NULL

put方法实现原理

使用Hash函数确定键值对插入位置,设为index,然后将键值对插入到数组索引为index的位置

当index值发生冲突的时候,利用链表解决,将新元素插入到对应点的链表当中

需要注意的是!!:新插入的键值对使用的是头插法,因为HashMap的发明者认为,新插入的节点被查找的概率大于以前的元素。

Get方法实现原理

根据Key值查找对应的value,当调用get方法时,同样根据key值进行一次hash映射,找到对应的index值

如果该值存在hash冲突,这时就要从链表的头节点开始,往下查找,找到对应的key值

例:

​ 第一步,算出Key值“apple”的hash值,假设为2。
​ 第二步,在数组中查找索引为2的位置,此时找到头节点为Entry6,Entry6的Key值是banana,不 是我们要找的值。
​ 第三步,查找Entry6的Next节点,这里为Entry1,它的Key值为apple,是我们要查找的值,这样 就找到了对应的键值对,结束。

HashMap默认长度

HashMap默认长度时16,而且,每次扩展或者手动初始化的时候,长度必须时2的次幂

原因:在执行hashcode算法的时候,长度为16位可以使计算后的索引分布均匀,不会出现有一部分索引不会出现的情况

HashMap的死锁

hashmap是非线程安全的

原因:HashMap容量是有限的,碰撞会出现的频繁

所以为了解决这个问题,HashMap设计了一个阈值,为0.75,当HashMap所用容量超过了阈值后,就会自动扩容。

HashMap 和 HashTable

HashMap不同步,所以适合单线程的项目

HashTable是同步的,适合多线程的工作

多线程问题

实现方式

继承Thread类

重写thread类中的run()方法,通过此对象调用start()

class MyThread extends Thread{
    //重写run方法
    public void run(){
        for(int i = 0;i<100;i++){
            system.out.print(i);
        }
    }
}
public class ThreadTest{
    public static void main(String[] args){
        MyThread t1 = new MyThread();
        //开启线程:1、启动当前线程、2、调用线程的run方法
        ti.start();
    }
}
  • 不能直接调用run方法,这样的话就只是new了一个对象,执行该对象的run方法,没有为它分配线程

  • 一个线程类已经start了,就不能再start了,需要重新new一个再start

Thread类中的相关方法:

void start();//启动线程,并执行run方法
run();//线程启动后要执行的操作
String getName();//返回线程的名称
void setName(String name);//设置线程的名称
static Thread currentThread();//返回执行当前代码的线程,在Thread子类中就是this,同时用于主线程和Runnable实现类
yield();//暂时释放该线程,就是不占用cpu,但有可能在下一刻又抢到了线程
join();//再线程A中调用线程B的join方法,此时线程A进入阻塞状态,知道线程B完全执行完后,线程A才结束阻塞状态 
stop();//强制结束此线程(不建议使用,已经过时)
sleep(毫秒);//阻塞该线程多少时间,静态方法,在其他地方可以直接Thread.sleep()调用
isAlive();//判断当前线程是否存活

线程优先级

#最高优先级
MAX_PRIOPITY:10 
#最低优先级
MIN_PRIORITY:1
#默认优先级
NORM_PRIORITY:5
getPriority();//得到当前线程优先级
setPriority(int newPriority);//设置当前线程优先级

一般设置要求再start之前设置

说明:高优先级的线程要抢占低优先级线程cpu的执行权,但是知识从概率上讲,高优先级的线程该概率的情况下被执行,并不意味着只有高优先级的线程执行完之后,低优先级的线程才执行。

最高优先级不代表一定比低优先级的线程先执行,有可能低优先级还是会先执行

卖票问题Thread

不考虑线程安全问题:

/*
例子:创建3个窗口卖票,总票数为100张
*/
class Window extends Thread{
    private static int ticket = 100;//静态变量

    @Override
    public void run() {
        while (ticket > 0){
            System.out.println(getName()+":卖票,票号为"+ticket);
            ticket--;
        }
    }
}
public class windowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

实现Runnable接口

//1、创建一个实现了Runnable接口的类
class MThread implements Runnable{
    //2、实现类去实现Runnable类中的run()方法
    @Override
    public void run() {
        for (int i = 0;i < 100;i++){
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

public class RunnableTest {

    public static void main(String[] args) {
        //3、创建实现类的对象
        MThread m1 = new MThread();
        //4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(m1);
        //5、通过Thread类的对象调用start()方法 1、启用线程 2、调用当前线程的run方法
        t1.start();
        //在启动一个线程
        Thread t2 = new Thread(m1);
        t2.start();

    }

}

需要注意的是:上述Thread的方法在继承Thread时可以直接使用,但是在实现Runnable的时候不能直接使用,Runnable内没有定义这些方法的接口,但因为Thread内的这些方法是静态的,所以可以通过Thread.currentThread(). XXX来调用函数

卖票问题Runnable

//1、创建一个实现了Runnable接口的类
class RunnableTest implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while (ticket > 0){
            System.out.println(Thread.currentThread().getName()+":卖票,票号为"+ticket);
            ticket--;
        }
    }
}

public class windowTest2 {
    public static void main(String[] args) {
        RunnableTest r1 = new RunnableTest();
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r1);
        Thread t3 = new Thread(r1);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}

使用Runnable接口实现方式,还需要将实现类new的对象放入Thread类中,这样做,实现类中当某些资源需要多个线程公用的时候,不需要将变量设置为静态变量,因为new的多个线程都是公用的一个Runnable对象

Thread和Runnable区别

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

生命周期

Thread.state定义了线程的多个状态:新建、就绪、运行、阻塞、死亡

image-20210425133504349

线程同步

当一个线程在操作共享的数据时候,其他前程不能参与进来,知道该线程操作完,其他线程才可以操作,这种情况及时线程出现阻塞,也不能改变。

在Java中通过同步机制、来解决

方式一:同步代码块

synchronized(同步监视器){
    //需要被通过的代码
}

操作共享数据的代码,即为需要被同步的代码

同步监视器:俗称:锁。任何一个类的对象都可以充当同步监视器

//1、用对象方式
private Object obj = new Object();
synchronized(obj){   
}
//2、用this的方式
synchronized(this){ 
}

要求多个线程必须使用同一把锁!!! (用实现Runnable的方式就可以很轻易实现一把锁)

用同步代码块解决继承Thread类的同步:

//1、将锁变成静态的对象
private static Boject obj = new Object();
//2、使用多线程继承类的class
public test extends Thread{
	public void run(){
        synchronized(test.class){//直接使用本类充当锁
            
        }
    }
}

方式二:同步方法

​ 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

​ 再将该方法放在run方法里面执行。

Runnable:

private synchronized void function(){//同步监视器:默认this
    
}

Thread:

private static synchronized void function(){//将该方法写为静态的,这个时候它的同步监视器就是当前的类(继承Thread的类)
    
}

死锁问题

死锁:不同的线程分别占用对方需要的资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

出现死锁后,不会出现异常,不会出现提示,知识所有的线程都处于阻塞状态,无法就绪

解决方法:

  • 专门的算法、原则
  • 尽量减少同步资源的定义
  • 尽量避免嵌套同步

Lock(锁)

Java.Util.concurrent.locks.Lock接口 是控制多个线程对共享资源进行访问的工具

ReentrantLock类实现了Lock接口,它拥有与synchronized相同的并发性和内存语义

/**
 * 解决线程安全问题方式三:Lock锁
 *
 */

class Window2 implements Runnable{
    private int ticket = 100;
    //1、创建lock对象
    private ReentrantLock lock = new ReentrantLock(true);//参数默认为false,表示是否使每个线程公平的占用资源,类似与先进先出的概念,不会出现一个线                                                            程多次抢占资源成功
    @Override
    public void run() {
        while(true){
            try{
                //2、调用lock()
                lock.lock();
                if (ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":售票,票号为:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }finally {
                //3、调用解锁方法unlock()
                lock.unlock();
            }
        }
    }
}

如果是继承Thread类的话,ReentrantLock对象应该设置为静态

面试题

1、synchronized方式和lock方式的异同?

- 相同:二者都可已解决线程安全问题
  • 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器

    ​ Lock需要手动的启动同步(lock()),通过是结束同步也需要手动实现(unlock())

2、如何解决线程安全问题?有几种方式?

  • 同步代码块
  • 同步方法
  • Lock

p441

posted @ 2021-04-25 17:14  GSCicode  阅读(89)  评论(0)    收藏  举报