欢迎光临散文网 会员登陆 & 注册

黑马程序员Java零基础视频教程_下部(Java入门,含斯坦福大学练习题+力扣算

2023-03-15 19:35 作者:别降下这么合适的雨a  | 我要投稿


Map加入集合也是无序的

put方法的作用添加和覆盖

  1. 如果在添加数据的时候,键值不存在,则会直接添加到Map集合中,此时返回值是null。
  2. 如果键值存在,则直接覆盖,但是会返回原有的键值的值。

remove方法不能单独删除value

Map集合第一种遍历方式(健找值):

将Map集合中的key单独拿出来,放到set集合中。利用的是keySet方法,返回值就是一个Set集合。然后再用Iterator,增强for或者lambda来遍历就行。然后利用get方法获取遍历到的key中value的值。

Map集合第二种遍历方式(键值对):

这里涉及到泛型嵌套,既泛型中再去指定泛型的泛型。

利用entrySet方法,将Map集合保存为一个Set集合,但是这次Set和的泛型指定为Map.Entry类型。

再用Iterator、增强for、lambda表达式遍历,利用getKey与getValue方法分别得到键值对的值。


lambda表达式的底层其实是增强for循环,


HashMap的底层原理与HashSet一样,也是hash表的形式(数组+链表+红黑树)。

但是HashMap只比较健的hash值,只保证健的唯一

LinkedHashMap



hashMap默认的空参构造方法,只是加载加载因子用的

HashMap刚创建的时候,数组啥的是不存在的,只有当添加元素的时候才会建立 。


触发扩容机制之后,会将原有的数据移动到或者复制到新的数组中

要存入的元素,先计算出hash值,再与数组长度进行与运算,就是当前元素应存入的位置,如果当前位置没有元素,就是数组当前位置为null,则直接存入数组中。

当添加的元素hash值与当前数组中的元素相同时


当hash值相同,并且key值也相同时,会执行覆盖

hash值相同,key值不同,还是回挂在链表的下面

...:表示可变参数;底层是一个数组

且一个方法的形参中只能由一个可变参数;

如果参数列表中除了可变参数,还有其他类型的参数。则可变参数要写在最后,不然方法调用时识别不出,其他类型变量的值。

Collections

addAll返回值是collection只能给单列集合批量添加

// copy方法不能直接创建集合进行复制,而是先将新建的集合批量加入,并指定长度之后才能复制
// 否则会下标越界


shift+F6:批量改名



双列集合不能直接使用stream流,要先通过entrySet等转为单列集合。


对stream流中数据进行修改,只会影响流中的数据,对原来集合或者数组中的数据没有影响。

concat方法:

当合并两个流时,如果两个流的数据类型不一致,则会选择两个流数据类型共同的父类作为流输出。


收集器如果是Collector.toList()则,集合里面有重复数据也会保留;如果是Collector.toSet则,集合里面不会有重复的数据

如果将集合收集到map里面,需要指定键的规则和值的规则,而且键要保证不能重复。

Lambda表达式的方式书写键值规则:

new Function(键的规则(l流当中数据的类型,键的类型),值的规则(l流当中数据的类型,值的类型))

将集合中的数据封装到对象中,组成对象集合。

stream().map()中,map是用来进行类型转换的。



异常:程序中可能出现的问题


编译时异常:java文件通过javac进行编译时出现的异常

作用在于提醒程序员检查本地信息,如日期解析异常。

与运行时异常:字节码运行时出现的异常,代码出错出现的异常


invoke:调用

异常的作用:

1.用来查询bug的关键参考信息;

2.作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况。

此时,调用者可以选择自己在后台进行处理,或者打印到控制台。

如果程序出现异常,那么虚拟机就会创建一个相应异常的对象,如果此时使用try-catch去捕获这个异常,则会执行catch中的代码。当catch里面的代码执行完毕,就继续执行try-catch下面其他的代码。

如果要捕获多个异常,那么他们共同的父类也要捕获的话,要写在子异常的下面。

如果try中的一行代码出了问题,那么这行代码以下的代码都不会执行。直接进行捕获或者JVM去创建异常对象


System.err.println(要打印的语句);

将要打印的语句以红色字体打印在控制台

throws:写在方法上

throw new:写在方法里,并且结束方法运行

运行时异常用throws在方法上进行抛出时,可以省略不写。

抛出:方法将产生的异常传给调用者。

捕获:方法调用者将方法抛出的异常拿到。


自定义异常类,包括一个无参的构造方法,一个有参的message提示方法

其中,如果是运行时异常,要继承RuntimeException

编译时异常要继承Exception

这样,在catch里面捕获就行

public class 异常类名{

public 异常类构造方法(){

}

public 异常类构造方法(参数列表){

}

}

IO流

程序在读写IO流中的数据

输出流:程序->文件

输入流:文件->程序



文件输出流,就是把程序中的数据写入到文件

步骤:

1.创建文件输出流对象

既通过 FileOutputStream fos = new FileOutputStream("指定文件存放的路径");建立程序与文件的传输通道。

2.写数据

写入数据就相当于数据在这条通道上进行行驶,再到终点。

3.释放资源

就是将这条通道关闭。

FileOutputStream

创建输出流对象,参数可以是用字符串表示的路径,或者new一个File对象再把路径给File对象也可以。

如果指定路径中的文件不存在,则会创建新文件,但是要保证这个文件所在的文件夹存在,不像Linux一样可以多级创建。

如果文件已经存在,则会覆盖文件内容。


FileOutputStream底层,在创建对象的时候会new一个File对象和一个boolean的append参数,append默认时false(也就是默认是覆盖的),改为true之后就不会覆盖,而是续写了。

这样再次执行程序写入时,文件中的内容就不会被覆盖了。

FileInputStream

1.创建字节输入流对象

FileInputStream fis = new FileInputStream("文件路径");

2.读取数据

int i = fis.read();

返回的是一个int型的变量,可以强转成char

read方法更像是有个指针,一次读一个

3.释放资源

fis.close();





GBK字母存储

GBK汉字的存储

一个字节不够,三个字节太多,所以用两个字节就可以,可以标识65535个汉字;为了与英文区分开。


UTF-8:可变字符长度(1-4字节),其中中文用三个字节表示;ASCII用一个字节。是unicode字符集的编码方式

解码时,是先将前面的固定格式去除,再去拼接为汉字就是两个字节的二进制位。再去查询Unicode编码表。

如果一开始用Unicode去编码,然后再用GBK去解码,此时解码时查询的是GBK编码表,GBK用两个字节表示一个汉字,而Unicode用三个字节,所以读完两个字节之后剩下的一个字节就不读了,所以一个汉字会出现读不全的问题。


字符流

除了对象名不同,其他跟字节流差不多


FileReader fr = new FileReader("D:\\My Work National\\IJ_IDEA\\Day05\\a.txt");

int ch = 0;
while ((ch = fr.read()) != -1){
    System.out.print((char)ch);
}

fr.close();


java中的换行其实是两个转义字符\r\n


字符流底层原理

字符流有缓冲区,字节流没有

缓冲流

缓冲流只是一个中间态的东西,还是要关联四大基本流来进行操作

释放资源的时候,缓冲流在底层会先释放基本流资源。不需要自己关。

基本流会先读取数据,放入缓冲区当中;

变量b会去读缓冲区中的数据,再把读到的数据放入当,输出流的缓冲区当中。

newLine方法底层会去判断使用的操作系统是什么样的,然后输出相应的换行。

转换流

字节流转换为字符流

通常用于字节流转字符流之后,用字符流里面的方法。


多线程

提高程序的运行效率。

应用场景:聊天软件,拷贝、迁移大文件,加载大量资源文件,后台服务程序。

并发:同一时刻,多个指令在单个CPU上交替执行。

并行:同一时刻,多个指令在多个CPU上同时进行。

并发和并行

线程的启动,new对象,用对象的引用去调用start()方法

继承Thread父类

public class MyTread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "Hello World");
        }
    }
}
main方法中
/*MyTread m1 = new MyTread();
m1.setName("线程1");
m1.start();
MyTread m2 = new MyTread();
m2.setName("线程2");
m2.start();*/


实现Runable接口

public class MyRunable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //Thread.currentThread.getName 获取当前线程的线程名
            System.out.println(Thread.currentThread().getName()+"你好!");
        }
    }
}
//main方法中
//创建对象
MyRunable myRunable = new MyRunable();
//创建线程对象
Thread t1 = new Thread(myRunable,"线程1");
t1.start();

Thread t2 = new Thread(myRunable,"线程2");
t2.start();


Callable接口如果指定泛型,则call方法的返回值就是这个泛型的返回类型。如果不指定默认是Object类型。

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
       
        return sum;
    }
}


JVM虚拟机在启动的时候,会默认创建多条线程。

其中一条就是main线程,作用是调用main方法,执行里面的代码

关于优先级:

不指定优先级默认就是5

优先级的取值范围:1~10,越大优先级越高

守护线程

当把一个线程定义为守护线程之后,当其他非守护线程结束之后守护线程也会陆续结束,但不是立马,因为JVM跟线程之间也有通信时间。

设置一个程序为守护进程:

Thread t1 = new Thread(程序,设置程序名);

t1.setDaemon(true);

所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。 因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。


如果其他线程的优先级太小,会导致设置的守护进程先执行完了,而其他非守护进程还在执行。


线程的生命周期

线程在就绪状态,会不断地跟其他线程抢夺执行权,当抢夺到之后,程序进入运行状态,期间如果被sleep,则会进入阻塞状态,直到sleep的时间到了,会进入就绪继续上面的步骤,直到run方法中的代码执行完。

锁对象是唯一的,什么都行,Object的也行,但是必须是唯一的

唯一:一把锁对应一道门

public class PayTicket implements Runnable{
    static private int ticket = 1;

    static Object o = new Object();

    @Override
    public void run() {
        while (true){
//这个锁一般定义为当前类的字节码文件。
            synchronized (PayTicket.class) {
                if (ticket <= 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                } else {
                    break;
                }
                ticket++;
            }
        }
    }
}



线程同步四种方式:

  • 一、通过Object的wait和notify
  • 二、通过Condition的awiat和signal
  • 三、通过一个阻塞队列
  • 四、通过两个阻塞队列


StringBuffer和StringBuilder的区别:

StringBuffer是线程安全的,因为每个方法都加了sychronized同步锁。

如果程序是单线程的,不需要考虑数据安全性问题,则可以用StringBuilder;

如果是多线程的,需要考虑数据的安全,则可以用StringBuffer。

Lock锁

public void run() {
    //synchronized (PayTicketWithThread.class){
        while (true) {
            lock.lock();
            if (ticket == 100) {
                break;
            } else {
                ticket++;
                System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
            }
            //如果unlock释放锁加在这里,则当ticket==100成立时,直接break跳出程序,没有执行释放锁的操作
            lock.unlock();
        }
    //}
}



public class Disk {
    /**
     *
     * 用于控制生产者和消费者的执行
     *
     *
     */

    //是否有面条;有:1;没有:0;默认:0
    public static int foodFlag = 0;

    //面条总数
    public static int count = 10;

    //锁对象
    public static Object lock = new Object();
}


public class Foodie extends Thread{

    @Override
    public void run() {
        //先写循环
        while(true){
            synchronized (Disk.lock){
                if (Disk.count == 0){
                    break;
                }else {
                    //判断是否有面条,0表示没有则去等待
                    if (Disk.foodFlag == 0){
                        try {
                            Disk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        //面条-1
                        Disk.count--;
                        System.out.println("还有"+Disk.count+"碗");
                        //吃完之后唤醒厨师
                        Disk.lock.notify();
                        //修改面条改为没有0
                        Disk.foodFlag = 0;
                    }
                }
            }
        }
    }
}


public class Cook extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Disk.lock){
                if (Disk.count == 0){
                    break;
                }else {
                    if (Disk.foodFlag == 1){
                        try {
                            //如果桌上有面条,则lock要等待
                            Disk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        //如果没有,则生产一碗面条
                        Disk.foodFlag = 1;
                        //唤醒消费者去吃
                        Disk.lock.notify();
                    }
                }
            }
        }
    }
}


Cook c = new Cook();
Foodie f = new Foodie();

c.setName("厨师");
f.setName("吃货");

c.start();
f.start();


线程池

将跟连wifi一样,指定最大连接数


//获取线程池对象

ExcutorService pool = Excutors.newCachedThreadPool(可以指定线程的数量);

//提交任务

pool.submit(new 类名);

//关闭资源

pool.shutdown



这时,cpu会创建临时线程处理任务7和任务8

此时任务10会拒绝服务

主要是第一个拒绝策略。









黑马程序员Java零基础视频教程_下部(Java入门,含斯坦福大学练习题+力扣算的评论 (共 条)

分享到微博请遵守国家法律