学习笔记-JavaSE(2)

7. 集合(存储引用类型)

主要有三种集合:

  • List: 有序集合,可以放重复的数据
  • Set:无序集合,不能重复
  • Map:无序集合,键值对

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CK4j5u8o-1612419431401)(C:\Users\LiTangMM\AppData\Roaming\Typora\typora-user-images\image-20191202110439361.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uaXFAq5S-1612419431402)(C:\Users\LiTangMM\AppData\Roaming\Typora\typora-user-images\image-20191202111325514.png)]

Collection接口

public class CollectionTest(){
    boolean add(E element);
    void clear();
    boolean isEmpty();
    int size();
    Object[] toArray();
    
    Iterator<E> iterator(); //返回集合类依赖的迭代器对象
    boolean contains(Object o); //调用集合元素equals方法比较
    boolean remove(Object o); 
    //建议:使用迭代器删除集合元素
    // 增强for循环
    
}

泛型

一种未知的数据类型,当我们不知道用什么类型的时候,可以使用泛型

泛型也可以是个变量,用来接收数据类型

好处:

  • 避免了类型转换的麻烦,存储的是什么类型,去除的就是出什么类型
  • 把运行期异常提升到了编译期

在创建对象时,会把数据类型作为参数传递,确定数据类型

// 泛型类
pubilc class GenericClass<E> {
    private E name;
    public E getName(){
        return name;
    }
    public void setName(E name){
        this.name = name;
    }
}
// 泛型方法
// 定义在修饰符和返回类型之间
public class GenericMethod{
    public <M> void method01(M m){
        sout(m);
    }
    public static <M> void method02(M m){
    	sout(m)
    }
}
// 泛型接口
//...

//泛型通配符 ?

//ArrayList<Interger>:ArrayList<?>:ArrayList<String>
public static void printArray(ArrayList<?> list){
    Interator<?> it = list.iterator();
    while(it.hasNext()){
        Object o = it.next();
        sout(0);
    }
}

//泛型型上下限
//泛型没有继承
<? extends E>:代表使用的泛型只能是E类型的子类或自己
<? super E>:只能是E类型的父类或自己

mainclass{
    main{
        GenericClass<String> gc = new GenericClass<>();
        GenericClass<Integer> gc = new GenericClass<>();
        GenericMethod gcc = new GenericMethod();
        gcc.method("nihao");
        gcc.method(123);
    }
}

List集合

List 接口

java.util.List extends Coolection

特点:

  • 有序的集合
  • 有索引
  • 允许存储重复的元素

特有方法(与索引有关)注意索引越界异常:

List Methods{
    public void add(int index,E element);
    public E get(int index);
    public E remove(int index);
    public E set(int index,E element);
}
ArrayList集合

底层是数组,线程不安全的

添加时会复制原数组再添加

LinkedList集合

底层是双向链表(有首尾指针),线程不安全的

LinkedList methods{
    public E getFist();
    public E getLast();
	public void addFirst(E e);
    public E removeFirst();
    public void addLast(E e);
    public E removeLast();
    public push(E e);
    public E pop();
}
Vector集合

可以实现可怎张的对象数组,与ArrayList类似,线程安全的

Set集合

Set接口

java.util.Set extends Collection

  1. 不允许存储重复的元素(元素重写了hashCode equals方法)
  2. 没有索引,无带索引的方法

add方法会调用对象的hashCode方法,计算哈希值,在集合中找有没有改哈希值的元素,没有则存储;有则调用equals方法,查找是否有相等的元素,没有则存储.

HashSet集合

java.util.HashSet implements Set

哈希表结构,无序

jdk1.8版本之前 哈希表=数组+链表

jdk1.8版本之后 哈希表=数组+红黑树(链表长度超过8个元素自动转换,提高查询速度)

LinkedHashSet集合

java.util.LinkedHashSet implements HashSet

底层是一个哈希表+链表. 多了一条链表,保证元素有序

可变参数(数组)

jdk1.5之后出现的新特性

使用前提:

​ 当方法的参数列表已经确定,但是参数个数不确定,就可以使用可变参数.

注意事项:

  1. 一个方法的参数列表,只能有一个可变参数.
  2. 如果方法的参数有多个,可变参数必须写在参数列表的末尾
public class Demo01VarArgs{
    public static void main(String[] args){
        add(1,2,3,4,5); //将会创建一个长度为5的数组
    }
    
    public static int add(int...arr){
        int res = 0;
        for(int var:arr){
            res += var;
        }
        return res
    }
}

Map集合

双列集合

常用子集

HashMap: 存数据采用的哈希表结构元素的存取顺序不能保证一致. 由于要保证键的唯一 不重复,需要重写简单hashCode()方法 equals()方法

LinkedHashMap: HahsMap下的一个子类,采用哈希表接口+链表结构

常用方法

public put(K key,V value)

public remove(Object key)

public get(Object key)

public Set<K> keySet()

public Set<Map.Entry<K,V>> entrySet()

package demo.hashmap;

import java.util.HashMap;

public class learnTest {
    public static void main(String[] args) {
        String s = "aabbccddeeffaabbddddefda";
        HashMap<Character,Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(map.containsKey(c)){
                map.put(c,map.get(c).intValue()+1);
            }else{
                map.put(c,1);
            }
        }
        for (Character key:map.keySet()) {
            System.out.println(key+":"+map.get(key));
        }
    }
}

8. 多线程

并发与并行

并发:指多个事件在同一时间发生(单核)

并行:至多个事件在同一时刻发生(多核)

线程

创建多线程程序的第一种方式:创建Thread类的子类

package demo.Thread;
/*
    创建多线程程序的第一种方式:创建Threa的子类
    java.lang.Thread类:是描述线程的类,要继承Thread类

    实现步骤:
        1. 创建一个Thred类的子类
        2. 在Thread类的子类中重写Thread的run方法,设置线程任务
        3. 创建Thread类的子类对象
        4. 调用start方法,开启新的线程去执行run方法
            结果1是两个线程并发执行
            多次启动一个线程是非法的.特别是当线程已经执行结束后,不能再重新启动
        java程序属于抢占式调度,哪个优先级包
 */
public class Demo01Thread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main:"+i);

        }
    }
}

package demo.Thread;
import java.lang.Thread;

public class MyThread extends Thread {

    @Override
    public void run(){
        for (int i = 0; i < 20; i++) {
            System.out.println("thread:"+i);
        }
    }
}

获取线程的名称
  • 使用Thread类中的getName方法

  • 可以先获取当前正在执行的线程,使用线程中的方法getName()获取线程的名称

    ​ String getName() : 返回线程的名称

    ​ static Thread currentThread() : 返回对当前正在执行的线程对象的引用

  • 默认名称:

    ​ 主线程: main 其他: Thread-0,Thread-1,Thread-2

设置线程名称
  • 使用Thread类中的方法setName(名字)
    • void setName(String name) 改变线程名称
  • 创建一个带参数的构造函数,参数传递线程的名字;调用父类的带参数构造方法
    • Thread(String name)
Thread类中常用方法
public class Thread{
    public Thread();
    public Thread(String name);
    public Thread(Runnable target);
    public Thread(Runnable target,String name);
    
    public String getName();
    public void start(); //外部调用
    public void run(); // 需要重写
    public static void sleep(long millis); //暂停指定毫秒数
    public static Thread currentThread(); 
}
创建线程方式二:
  1. 定义Runnable接口的实现类,并重写该方法的run()方法

  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象

  3. 调用Thread对象的start方法启动线程

    public class MyRunnable implements Runnable{
        @Override
        public void run(){
            for(int i = 0;i < 20;i++){
                sout(i);
            }
        }
    }
    
    public class test{
        
        public static void main(String[] args){
            MyRunnable mr = new MyRunnable();
            Thread th = new Thread(mr);
            th.start();
            
            
        }
    }
    
Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享. 但是如果实现了Runable接口的话,则容易实现资源共享.

优势:

  1. 适合多个相同的程序代码线程共享同一个资源
  2. 可以避免java中的单继承的局限性
  3. 增加线程的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
匿名内部类实现线程的创建

作用:简化代码

public class Demo{
    public static void main(String[] args){
        new Thread(){
            @Override
            public void run(){
               	//动作
            }
        }.start();
        
        Runnable r = new Runnable(){
            @Override
            public void run(){
				//动作
            }
        };
        new Thread(r).start();
        
        new Thread(
        		new Runnable(){
            	@Override
            	public void run(){
					//动作
            	}
        	}
        ).start();
    }
}

线程安全

多个线程同时访问共享资源

解决线程安全的方法:

  1. 使用同步代码块

    synchronized(锁对象){
        可能出现线程安全的代码;
    }
    

    注意:

    1. 通过代码块中的锁对象,可以使用任意对象
    2. 但是必须保证多个线程使用的锁对象是同一个
    3. 作用:把同步代码块锁住,只让一个线程在同步代码块中执行

    原理:

    1. 使用了锁对象,也叫对象监视器
    2. 线程抢夺cpu执行权,遇到synchronize代码块,如果检查到锁对象,则会获取锁对象,知道代码块代码执行完成.
  2. 使用同步方法

    ​ 把访问了共享数据的代码抽取出来,作为一个新的方法,使用synchronized修饰.

    ​ 锁对象为this.

    public synchronized ....{
        //共享数据
    }
    

    使用静态同步方法

    锁对象不是this(静态方法优先于对象), 是本类的class属性.

  3. 使用Lock

    java.util.concurrent.locks.Lock接口

    Lock接口的方法:

    ​ void lock() 获取锁

    ​ void unlock() 释放锁

    java.util.concurrent.locks.ReentrantLock implements Lock

    使用步骤:

    1. 在成员位置创建一个ReentrantLock对象
    2. 在可能出现线程安全的代码前调用lock
    3. 在可能出现线程安全的代码后调用unlock

线程状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOEQTM1E-1612419431404)(C:\Users\LiTangMM\AppData\Roaming\Typora\typora-user-images\image-20191212095514418.png)]

Object.wait()

Object.notify()

Thread.sleep()

等待唤醒
public class Test {
    public static void main(String[] args) {
        // 共同锁对象
        Object object = new Object();
        Producer producer = new Producer(object);
        Consumer consumer = new Consumer(object);

        consumer.start();
        producer.start();
    }
}

public class Consumer extends Thread{
    private Object lockobj;
    @Override
    public void run() {
        while (true){
            synchronized (lockobj){
                System.out.println("顾客线程获取锁对象");
                System.out.println("顾客:我需要买东西(进入等待状态)");
                // 进入等待状态
                try {
                    lockobj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 被唤醒后执行
                System.out.println("顾客被唤醒,执行wait后的代码");
                System.out.println("顾客:这是我要的东西,谢谢!");
            }
        }
    }

    Consumer(Object object){
        super();
        lockobj = object;
    }
}

public class Producer extends Thread{
    private Object lockobj;

    @Override
    public void run() {
        while(true){
            synchronized (lockobj){
                System.out.println("老板获取锁对象,等待5秒");
                // 等待5秒
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("老板:东西准备好了(唤醒顾客)");
                lockobj.notify();
            }
            // 保证顾客拿到锁
            try {
                System.out.println("------------");
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    Producer(Object object){
        super();
        lockobj = object;
    }
}

等待唤醒机制

线程间通信
等待唤醒机制

如果能获取锁,线程就从WAITING状态变成RUNNABLE状态

否则,从wait set出来,又进入entry set,线程进入阻塞状态.

调用wait和notify方法注意:

  1. wait方法和notify方法必须要由同一个锁对象调用. 因为: 对应的锁对象可以通过notify唤醒使用同一个锁对象调用wait方法后的线程
  2. wait方法与notify方法是属于Object类的方法. 因为: 锁对象可以是任意对象, 而任意对象的所属类都是继承了Object类的.
  3. wait方法与notify方法必须要在同步代码块或者同步函数中使用,因为: 必须要通过锁对象调用这2个方法.

线程池

底层实现LinedList<Thread>

创建线程池:

java.util.concurrent.Executors

static ExecutorService newFixedThradPoll(int nThreads)

返回一个接口对象,通过调用接口方法获取线程

sumbit(Runnable task): 提交一个Runnable任务用于执行

shutdown() : 关闭/销毁线程池

使用步骤:

  1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
  2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
  3. 调用ExecutorService中的submint,传递线程任务,开启线程

9. Lambda表达式

lambda表达式的标准格式

由三部分组成:

1. 一些参数
2. 箭头
3. 一行代码

格式:

​ (参数列表) -> (一些重写方法的代码)

解释说明:

​ ():接口中抽象方法的参数列表

​ ->: 传递的意思

​ {}: 重写接口的抽象方法的方法体

lambda简写

10. File

文件和目录路径的抽象表示形式

File类的两个静态变量

public class File{
    static String pathSeparator; //路径分割符 win; linux:
    static String separator; //文件名称分隔符 win\ linux/
}
绝对路径 相对路径

File类的构造方法

public class File{
    public File(String pathstring);
    // 只封装File对象,不考虑路径的真假情况
    public File(String parent, String child);
    // parent:父路径 child:子路径
    public File(File parent, String child);
    // parent为File类,可使用File的方法对路径进行一些操作
}

File类常用方法–获取

public class File{                      // 文件        文件夹
    public String getAbsolutePath();    // 绝对路径     绝对路径
    public String getPath();            // 构造路径     构造路径
    public String getName();            // 文件名称     目录名称    
    public long lenght();               // 文件大小(B)  目录大小(0B)
}

File类常用方法–判断

public class File{
    public boolean exits();
    public boolean isDirectory();
    public boolean isFile();
}

File类常用方法–创建删除

public class File{
	public boolean createNeaFile();
    public boolean delete();
    public boolean mkdir();
    public boolean mkdirs();
}

目录遍历

public class File{
	public String[] list();
    public File[] listFiles():
}

文件过滤器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值