java进阶

java进阶

第一章 继承

1.概述

子类可以继承父类中有的成员变量和成员方法

好处 : 提高代码的复用性

2.继承的机制

关键字  extends
构造方法不会被继承,子类要写自己的构造方法
父类中私有的成员可以被继承,被继承的成员依然是只能被父类访问;

3.继承时成员访问顺序

变量或者方法被调用时,系统会先在本类中寻找,本类中没有就会访问父类中的变量

4.this和super

1.修饰变量
this  调用本类成员变量
super 调用父类成员变量
2.修饰方法
 ```java

this 本类方法的引用,谁调用,this代表调用的处对象 this.method(); 相当于method
super 父类方法的引用, super.method();
```

3.修饰构方法
this();  调用本类构造方法
super(); 调用父类构造方法
tip 
(1) thissuper 只能写在构造方法的第一行
(2) 如果构造方法没有写明,构造方法第一行默认有一句super();

5.继承的内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jc797JP6-1631766334597)(3.继承的内存图.png)]

在内存中,创建子类对象时,会在子类对象内存空间先创建父类对象, 供子类对象使用

6.方法的重写

1.子类中的方法与父类中的一模一样(参数,返回值类型,方法名,修饰符)
2.重写后子类调用时有限使用子类自己的方法
3.这样使得子类即可以继承父类的方法又可以有自己特有的功能

7.继承的注意事项

1.继承必须有类的所属于父类的关系,子类属于父类才能继承

2.类只支持单继承,一个类只能有一个父类

3.类可以多层继承,一个类有父类,父类还可以有自己的父类,多层继承依然遵循继承的使用规则

第二章 抽象类

1.概述

1.方法体不是具体的

2.没有实例,不能创建对象

3.关键字  abstract

2.格式

public abstract class 类名{
    
}

3.抽象类的成员

普通类能写的都能写
    成员变量
    构造方法
    成员方法
抽象方法
    public abstract 返回值类型 method();
只有方法的定义,没有方法体

4.注意事项

1.抽象类中可以有抽象方法,也可以没有

2.有抽象方法的类必须是抽象类

3.抽象类不能创建对象

4.抽象类一定是一个父类,抽象类的子类要么重写抽象方法,要么也是抽象方法

5.抽象类不能创建对象,构造方法用来给子类创建对象调用

5.抽象类的作用

给子类提供抽象方法,子类可以通过重写方法定义具体的实现方式

6.final关键字

1.final修饰变量

基本数据类型 存的是数据值

不能再次被赋值,变量定义为常量 Math.PI

引用数据类型 存的是地址值

引用地址不可以改变,地址值

2.final修饰方法

被final修饰的方法不能被重写

final和abstract排斥 private 和 abstract

3.final 修饰类

被修饰的类不能被继承

第三章 接口和多态

一.static

1.作用
1.1修饰成员变量
作用:static修饰的成员变量属于类,被类的所有对象共享
调用: 对象.成员变量
      类名.成员变量
      推荐 类名调用 (static修饰的成员属于类,在内存中存放在静态方法区)
1.2修饰成员方法
作用: static修饰成员方法,方便调用
调用: 对象.成员方法
      类名.成员方法
      推荐 类名调用
2.访问规则
静态方法不能直接访问(间接创建对象)非静态成员,成员方法可以直接访问类的静态成员和非静态成员
3.内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wK1jUcGT-1631766334603)(D:/java/%E7%AC%94%E8%AE%B0/se%E8%BF%9B%E9%98%B6/2.%E9%9D%99%E6%80%81%E7%9A%84%E5%86%85%E5%AD%98%E5%9B%BE.png)]

二.接口

1.概述
接口是一种规范
定义格式
 public interface 接口名{
     
 }
实现
public class 类名 implements{
    
}
2.接口的成员
1.抽象方法    public abstract 返回值 方法名(); (public abstract 可以省略)
2.常量        默认被public static final 修饰;
JDK8加入
3.默认方法    public default 返回值类型  方法名 (){ ( public 可以省略)
    
}
(JDK9加入
4.私有方法  私有成员方法,私有静态方法  
3.接口的注意事项
1.常量默认修饰符   public static fianl 
2.抽象方法默认修饰符  public abstract  
3.接口不能创建对象
4.接口不能有构造方法
5,接口的实现类必须重写抽象方法或者实现类也是抽象类
4.接口使用场景
作用: 定义一种规范
在在开发中定义好方法的返回值,方法名,参数

三.多态

1.概述
一种事务的多种形态  
student 既是Student类也是Person类
java中 父类引用指向子类对象
2.多态使用前提
必须有继承extends 或者 实现  implements 关系
3.访问规则
成员变量  编译看左边,执行看左边
成员方法  编译看左边,执行看右边
4.多态优缺点
缺点:
    不能使用子类特有的成员(成员变量和成员方法)
优点:
    提高了程序的扩展性
    父类作为方法的参数,可以传入子类对象调用子类重写的方法
5.多态的类型转换
1.向上转型 (自动转换)
    多态  父类引用指向子类对象
    Animal a = new Cat();
2.向下转型 (强制转换)
    cat c = (cat)a;

四.内部类

1.成员内部类
格式:
public class AAA{
    int i = 10;
    class BBB{
        int i=20;
        public void method(){
            int i =30;
            System.out.println(i);//30
            System.out.println(this.i);//20
            System.out.println(AAA.this.i);30
        }
    }
}
创建内部类对象   
 外部类.内部类 对象名 =  new 外部类().new 内部类 
 AAA.BBB ab = new AAA().new BBB();
内部类访问外部类成员变量
  System.out.println(i);//30
  System.out.println(this.i);//20
  System.out.println(AAA.this.i);30
2.局部内部类
public class AAA{
    public void method(){
        class BBB{
            int a=10;
        }
    }
}
用处不大
3.匿名内部类
1.格式
Person p=new Person{
	public void method(){
	重写父类方法
	}
};
2.使用场景

方法的参数是抽象方法或者接口,可以通过匿名内部类的方式创建抽象方法或者接口的子类

3.作用

可以简化代码

4.每一个类都有编译后都会生成一个单独的class文件

内部类文件名 外部类名$内部类名.class

匿名内部类,文件名 外部类$数字.class

第四章 代码块 和常用API

一.权限修饰符

public > protected > 默认 > private

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOAltEKT-1631766334606)(四种权限修饰符.png)]

二.代码块

1.静态代码块
static{
    语句体
}
类被加载的时候自动执行,只执行一次

用于静态变量的初始化

2.构造代码块( 初始化代码块 )
{
    语句体
}
每次创建对象的时候被自动调用

作用: 构造方法有相同语句体的时候可以放在构造代码块

3.局部代码块

写在方法中 ,用来约束变量

作用: 方法中用不到的变量通过{ }可以在方法执行完之前释放内存,

三.Object 类

1.equals 方法
默认  object中     this == o( 参数 )  比较的是地址值
在对象所属类中重写之后比较对象的属性
//学生类
@Override
    //逻辑严谨
    public boolean equals(Object o) {
        if(this==o) return true;
        if(o==null||getClass()!=o.getClass()) return false;
        Student stu = (Student)o;
        if(age!=stu.age) return false;
        return  name!=null?name.equals(stu.name):stu.name==null;
    }
2.toString 方法
默认  object中    输出 类型.类名+@+十六进制地址值
重写后输出自定义内容

3.native 本地的 关键词

native修饰的方法没有方法体,调用了其他语言写的调用系统资源的代码

四.date 类

1.Date 日期类
2.构造方法
new Date(); 获取当前时间
new Date(long time)  设置时间  从基准时间开始
计算机中的基准时间  197011
3.常用方法
方法名作用
long getTime( )返回基准时间到调用对象的毫秒值
after( );判断是调用对象否在参数
before( );判断

五.SmipleDateFormat 类

1.日期格式化类
2.构造方法

new SmipleDateFormat( String pattern );

3.常用方法
方法名作用
String format( Date date);把日期类转换为String
Date parse(String source)把String解析为 Date

六.Calendar 日历类

1.构造方法

Calendar.getInstence();

2.常用方法
方法名作用
get( int field );根据字段返回对应的值
set(int field );设置字段的值
add(int field,int values);字段加减操作

七.Arrays 数组工具类

1.工具类
private修饰构造方法,不能创建对象,
所有的成员都用static修饰,用类名调用
2.常用方法
sort();        排序
toString();    把数组输出为字符串

八.System类

常用方法

exit(int status);     退出虚拟机
currentTimeMillis()   获取当前系统时间
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
复制数组   src 源数组 srcPos 源数组复制的起始位置, dest 目标数组 destPos 目标数组的起始位置, length 复制的长度

第五章 集合

1.集合体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z7pDzVjx-1631766334610)(D:\java\笔记\se进阶\集合体系.png)]

1.Collection

1.Collection是一个接口;
2.Collection中的抽象方法
方法名用法
public boolean add(E e);添加元素
public boolean remove(E e);删除元素
public boolean contains(E e);判断集合是否包含参数
public int size();返回集合的长度

2.iterator迭代器

1.概述 : 没有索引的集合,可以使用迭代器进行遍历
2.获取迭代器

Collection系的集合可以使用集合对象调用Iterator方法

3.方法
hasNext();    判断是否还有下一个元素
Next();       返回下一个元素;
4.遍历用法
while(iterator.hasNext()){
    iterator.Next();
}
5.并发修改异常

原因 : 使用迭代器遍历数组时,如果在遍历中使用中集合删除或增加元素,就会引起并发修改异常

措施 : 使用iterator的remove方法

6.for增强

底层是迭代器

格式

for(数据类型 变量名:集合名){
    此时的变量就代表集合中的数据
}

3.泛型

1.概念 泛型指广泛的类型, 当我们不确定使用什么类型的时候就可以使用泛型
2.应用于类,方法,接口

1.作用于类

定义在类名的后边 , 具体确定类型是在创建对象的时候

2.作用于方法

定义在返回值之前, 具体确定类型是在调用方法的时候

3.作用于接口

定义在接口的接口名之后,继承类可以确定具体.类型,或者继承类也定义相同的泛型;继承类的实现类创建对象时确定具体类型

3.泛型的通配符
<?> 表示可以接受任意类型 <? extends A> 表示可以接受A和A的子类 <? super A> 表示可以接受A和A的父类 用于定义,<?>用于使用具体的类型

4.数据结构

1.栈和队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7WcNKZP-1631766334613)(D:\java\黑马java\java基础进阶\day05\笔记\3.栈结构.png)]

栈 只有一个开口,先进后出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZg0QnWF-1631766334614)(D:\java\黑马java\java基础进阶\day05\笔记\4.队列结构.png)]

队列 有两个开口,先进先出

2.数组

查找快,增删慢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DXdI32JN-1631766334616)(D:\java\黑马java\java基础进阶\day05\笔记\5.数组结构.png)]

3.链表

增删快,查找慢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQgclnCG-1631766334617)(D:\java\黑马java\java基础进阶\day05\笔记\6.链表结构.png)]

5.List接口

1.list的特点 : 有序, 有索引,可以重复
2.方法
方法名用法
boolean add(int index , E e );根据索引添加元素(插队)
E get( int index )获取索引位置的元素
E remove(int index)根据索引删除元素
E set(int index, E e)设置索引位置的元素
3.ArrayLIst集合

继承自Collection 和 List 的方法

底层用数组实现,元素增删慢,查找快

4.LinkedList 集合

特有方法,对首尾元素进行添加获取的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rEBBukfc-1631766334619)(D:\java\笔记\se进阶\Snipaste_2021-09-03_20-45-49.png)]

6.可变参数,参数个数可以改变的参数

1.格式

public void  sum (int ... a){
    
}

2.注意事项

(1)传入实参的时候可以传任意个数的元素,也可以传入数组;

(2)可变参数的底层是数组, 在方法中可以把a当做一个数组来使用

(3) 形参列表中只能定义一个可变参数,多个参数的情况可变参数要定义在参数列表最后一个;

1.Collections工具类

1.Collections类有什么用?

Collections是集合的工具类,所有方法都是静态方法

2.Collections有哪些方法?

方法说明
static void shuffle(List<?> list)随机打乱集合元素的顺序
static void sort(List list)集合的排序(从小到大)
static void addAll(Collection<> coll, T… t )给集合一次添加多个元素
static void sort(List list,Comparator<? super T> )按照指定的方式排序

import java.util.ArrayList;
import java.util.Collections;

public class Demo01 {
    public static void main(String[] args) {
        //Collections的方法

        //创建两个集合
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(123);
        list1.add(456);
        list1.add(789);
        list1.add(100);
        System.out.println("初始:" +  list1);

        //static void shuffle(List<?> list)
        //随机打乱集合元素的顺序
        Collections.shuffle(list1);

        System.out.println("打乱" + list1);

        //static <T> void sort(List<T> list)
        //集合的排序(从小到大)
        Collections.sort(list1);

        System.out.println("排序" + list1);

        //static <T>  void addAll(Collection<> coll, T... t )
        //给集合一次添加多个元素
        Collections.addAll(list1,20,30,40);

        System.out.println("添加" + list1);
    }
}

2.Comparator比较器

1.比较器有什么作用?怎么用?

参数:
    Integer o1 : 代表要比较的元素
    Integer o2 : 代表已经比较完的元素
返回值:
    int:
        如果返回值是正数,代表要把o1放在o2的后面
        如果返回值是负数,代表要把o1放在o2的前面
        如果返回值是零,代表要把o1和o2位置不变,默认o1在后面
记住结论:
     o2-o1就是从大到小    o1-o2就是从小到大

2.整数从大到先排序


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo02 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(123);
        list1.add(456);
        list1.add(789);
        list1.add(100);

        //匿名内部类
        Comparator<Integer> c = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        };
        //排序
        Collections.sort(list1,c);
        System.out.println(list1); //[789, 456, 123, 100]
    }

}

3.字符串的从大到小


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo05 {
    public static void main(String[] args) {

        //创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //添加元素
        list.add("anc");
        list.add("abc");
        list.add("cba");
        list.add("wcba");
        list.add("nba");
        list.add("mba");

        //创建比较器
        Comparator<String> c = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //int i = o1.compareTo(o2); //从小到大
                int i2 = o2.compareTo(o1);  //从大到小
                return i2;
            }
        };
        //排序
        Collections.sort(list,c);

        //打印
        System.out.println(list);

    }
}

3.Set接口及子类

1 HashSet集合有什么特点?

hashSet没有索引,元素存取无序,元素不能重复

2.什么是哈希表结构?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2oX3GGT-1631766334620)(D:/java/%E9%BB%91%E9%A9%ACjava/java%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6/day06%E9%9B%86%E5%90%882/%E7%AC%94%E8%AE%B0/2.%E5%93%88%E5%B8%8C%E8%A1%A8%E7%BB%93%E6%9E%84.png)]

3. 什么是hash值?

每个对象都可以调用hashCode()方法,获取这个对象的哈希值
哈希值是一个整数

如果哈希值不同,说明两个对象一定不同
如果哈希值相同,两个对象不一定相同

public class Demo02 {
    public static void main(String[] args) {
        String s1 = "ab";
        String s2 = "abc";
        String s3 = "Aa";
        String s4 = "BB";

        System.out.println(s1.hashCode());   //3105
        System.out.println(s2.hashCode());   //96354
        System.out.println(s3.hashCode());   //2112
        System.out.println(s4.hashCode());   //2112

        //底层计算哈希值的时候,想要给每个不同的对象生成一个不同的整数哈希值,所以搞比较复杂的计算方法
        /*
            int hash = 0;
            循环遍历字符串{
                hash = hash * 31 + 获取每个字符;
            }
            hash的值就是这个字符串的hash结果
         */
    }
}

4.HashSet如何保证的元素不重复?

HashSet在添加元素的时候,会调用元素的hashCode()equals()方法
String s0 = "abc";
String s1 = "abc";  
String s2 = "Aa";  
String s3 = "BB";  
String s4 = new String("BB"); 

    
p.hash == this.hash && (p.key == this.key || (key != null && key.equals(k)))
    
先判断两个元素的哈希值
	如果两个元素哈希值不同,就直接知道两个元素不重复!
    如果两个元素哈希值相同,就去判断两个元素的地址值
    	如果两个元素地址值相同,就直接知道两个元素重复!
    	如果两个元素地址值不同,就判断两个元素的内容
    		如果两个元素内容相同,就重复!
    		如果两个元素内容不同,就不重复!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jC5cI47Z-1631766334621)(D:/java/%E9%BB%91%E9%A9%ACjava/java%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6/day06%E9%9B%86%E5%90%882/%E7%AC%94%E8%AE%B0/3.HashSet%E7%9A%84%E5%AD%98%E5%82%A8.png)]

5. HashSet存储自定义类型演示

如果要存储自定义类型,需要重写hashCode()和equals()方法

package com.itheima02;

import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //重写方法

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

package com.itheima02;

import java.util.HashSet;

public class Demo04 {
    public static void main(String[] args) {
        //创建集合
        HashSet<Student> set = new HashSet<>();

        //创建学生对象
        Student s1 = new Student("马冬梅",23);
        Student s2 = new Student("马冬梅",23);
        Student s3 = new Student("夏洛",28);

        //添加到集合
        set.add(s1);
        set.add(s2);
        set.add(s3);

        //打印
        System.out.println(set);
    }
}

6.LinkedHashSet集合有什么特点?

元素没有索引,元素不能重复,元素存取有序
package com.itheima02;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

public class Demo06 {
    public static void main(String[] args) {

        //创建集合
        LinkedHashSet<String> set = new LinkedHashSet<>();

        set.add("123");
        set.add("abc");
        set.add("abc");
        set.add("edf");

        System.out.println(set); //[123, abc, edf]
    }
}

4.单列集合总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-30IY4Kk0-1631766334623)(D:/java/%E9%BB%91%E9%A9%ACjava/java%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6/day06%E9%9B%86%E5%90%882/%E7%AC%94%E8%AE%B0/4.%E5%8D%95%E5%88%97%E9%9B%86%E5%90%88%E6%80%BB%E7%BB%93.png)]

5. 双列集合Map

1. Map集合有什么作用?

Map集合称为双列集合,保存的是一对儿一对儿的数据,一对儿的数据也叫键值对

2. Map集合体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysZrc3aI-1631766334624)(D:/java/%E9%BB%91%E9%A9%ACjava/java%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6/day06%E9%9B%86%E5%90%882/%E7%AC%94%E8%AE%B0/5.Map%E9%9B%86%E5%90%88%E4%BD%93%E7%B3%BB.png)]

3. Map常用方法

方法说明
V put(K key, V value)添加键值对
V remove(Object key)根据键删除对应的键值对
V get(Object key)根据键获取值
boolean containsKey(Object key)判断是否包含某个键
boolean containsValue(Object value)判断是否包含某个值
int size()获取集合长度

import java.util.HashMap;
import java.util.Map;

public class Demo01 {
    public static void main(String[] args) {
        //Map双列集合

        //创建Map集合
        Map<String,String> map = new HashMap<>();

        //V put(K key, V value)
        //添加键值对
        map.put("夏洛","秋雅");
        map.put("贾乃亮","小璐");
        map.put("王宝强","马蓉");

        System.out.println(map);  //{贾乃亮=小璐, 王宝强=马蓉, 夏洛=秋雅}

        //V remove(Object key)
        //根据键删除对应的键值对
        map.remove("夏洛");

        System.out.println(map); //{贾乃亮=小璐, 王宝强=马蓉}

        //V get(Object key)
        //根据键获取值
        String s = map.get("贾乃亮");
        System.out.println("值是" + s);

        //boolean containsKey(Object key)
        //判断是否包含某个键
        boolean b1 = map.containsKey("王宝强");
        System.out.println(b1);

        //boolean containsValue(Object value)
        //判断是否包含某个值
        boolean b2 = map.containsValue("马蓉");
        System.out.println(b2);

        //int size()
        //获取集合长度
        int size = map.size();
        System.out.println(size);
    }
}

4. Map集合如何遍历?

1. keySet()键找值方式

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo02 {
    public static void main(String[] args) {
        //创建Map集合
        Map<String,String> map = new HashMap<>();
        //V put(K key, V value)
        //添加键值对
        map.put("夏洛","秋雅");
        map.put("贾乃亮","小璐");
        map.put("王宝强","马蓉");

        //keySet()
        Set<String> set = map.keySet();

        //增强for
        for (String s : set) {
            //map获取值
            String v = map.get(s);
            //打印
            System.out.println(s+"-"+v);
        }
    }
}
2.entrySet()键值对方式

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo03 {
    public static void main(String[] args) {
        //创建Map集合
        Map<String,String> map = new HashMap<>();
        //V put(K key, V value)
        //添加键值对
        map.put("夏洛","秋雅");
        map.put("贾乃亮","小璐");
        map.put("王宝强","马蓉");

        //entrySet()
        Set<Map.Entry<String, String>> set = map.entrySet();

        //增强for
        for (Map.Entry<String, String> entry : set) {
            //获取键
            String k = entry.getKey();
            //获取值
            String v = entry.getValue();
            //打印
            System.out.println(k + "-" + v);
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V5QZP5cT-1631766334625)(D:/java/%E9%BB%91%E9%A9%ACjava/java%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6/day06%E9%9B%86%E5%90%882/%E7%AC%94%E8%AE%B0/6.Map%E9%9B%86%E5%90%88%E7%9A%84keySet%E9%81%8D%E5%8E%86.png)]

5. HashMap集合有什么特点?

元素存取无序,元素键不能重复

6. LinkedHashMap集合有什么特点?

元素存取有序,元素键不能重复

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

public class Demo04 {
    public static void main(String[] args) {
        //创建集合对象
        HashMap<String,String> map1 = new HashMap<>();
        map1.put("小璐","贾乃亮");
        map1.put("秋雅","夏洛");
        map1.put("马蓉","王宝强");
        //如果键重复了,会用新的值覆盖旧的值
        map1.put("马蓉","宋喆");

        System.out.println(map1);  //存取无序,元素键不能重复


        LinkedHashMap<String,String> map2 = new LinkedHashMap<>();
        map2.put("小璐","贾乃亮");
        map2.put("秋雅","夏洛");
        map2.put("马蓉","王宝强");
        //如果键重复了,会用新的值覆盖旧的值
        map2.put("马蓉","宋喆");

        System.out.println(map2);  //存取有序,元素键不能重复
    }
}

7.练习题

需求

​ 输入一个字符串判断里面每个字符出现次数。

代码实现:

import java.util.LinkedHashMap;
import java.util.Scanner;

public class Demo01 {
    public static void main(String[] args) {
        //1.键盘输入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String s = sc.next();

        //2.创建一个Map集合
        LinkedHashMap<Character,Integer> map = new LinkedHashMap<>();

        //3.遍历字符串
        for (int i = 0; i < s.length(); i++) {
            //4.获取字符串的每个字符
            char ch = s.charAt(i);
            //5.判断集合的键中是否包含字符
            boolean b = map.containsKey(ch);
            //6.如果不包含就添加字符次数是1
            if(b==false){
                map.put(ch,1);
            }else {
                //7.如果包含就获取原来的值+1再放回去
                Integer count = map.get(ch);
                map.put(ch,count+1);
            }
        }

        //8.打印
        System.out.println(map);
    }
}

6.模拟斗地主发牌

图解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRLvEfjY-1631766334626)(D:/java/%E9%BB%91%E9%A9%ACjava/java%E5%9F%BA%E7%A1%80%E8%BF%9B%E9%98%B6/day06%E9%9B%86%E5%90%882/%E7%AC%94%E8%AE%B0/7.%E6%96%97%E5%9C%B0%E4%B8%BB%E5%88%86%E6%9E%90.png)]

代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

public class Demo02 {
    public static void main(String[] args) {
        //创建Map集合
        Map<Integer,String> map = new LinkedHashMap<>();
        //创建ArrayList集合
        ArrayList<Integer> list = new ArrayList<>();

        //创建花色数组
        String[] color = {"♥","♣","♠","♦"};
        //创建数字数组
        String[] num = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        //定义变量
        int i = 1;
        //遍历数组
        for (String n : num) {
            for (String c : color) {
                //给集合添加
                map.put(i,c+n);
                list.add(i);
                i++;
            }
        }
        //单独添加王
        map.put(53,"😊");
        map.put(54,"🤡");
        list.add(53);
        list.add(54);

        //洗牌
        Collections.shuffle(list);

        //创建4个集合
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();
        ArrayList<Integer> list3 = new ArrayList<>();
        ArrayList<Integer> list4 = new ArrayList<>();

        //发牌
        for (int j = 0; j < list.size(); j++) {
            //获取元素
            Integer a = list.get(j);
            if(j>50){
                list4.add(a);
            }else if(j%3==0){
                list1.add(a);
            }else if(j%3==1){
                list2.add(a);
            }else{
                list3.add(a);
            }
        }

        //排序
        Collections.sort(list1);
        Collections.sort(list2);
        Collections.sort(list3);
        
        //看牌,拿数字去map里面找牌
        method(list1,map,"旭旭宝宝");
        method(list2,map,"车友车行");
        method(list3,map,"卢本伟  ");
        method(list4,map,"底牌    ");

    }

    //定义看牌方法
    public static void method(ArrayList<Integer> list,Map<Integer,String> map,String name){
        //遍历list集合
        System.out.print(name + ":");
        for (Integer a : list) {
            //把每个数字当做键从map里面找值
            String s = map.get(a);
            //打印值
            System.out.print(s+"");
        }
        System.out.println();
    }

}

第六章 异常

1.异常的概念:

​ 1).当我们的程序遇到无法处理的数据,使程序无法继续运行时,这种情况就叫"异常"
2).当JVM执行代码过程中,发生了异常情况,程序会被迫终止——这对于最终用户,非常不友好!!

2.异常的处理:

​ 1).Java中提供了一种"处理异常"的语法、机制,可以让代码产生异常时,可以跳过异常的代码,继续健康的运行下去

3.JVM在遇到异常情况下的处理步骤:

​ 1).JVM执行到有异常的代码
2).JVM会识别出异常情况
3).JVM会到"类库"中查找"描述这种异常的异常类",并创建一个对象。
Java针对任何异常,都有对应的"异常类"
4).JVM会查看我们的代码是否希望"捕获"这个异常(catch(InputMismatchException e)),
a).如果没有catch(InputMismatchException e):向控制台打印异常类名 + 异常的位置,然后结束程序
b).如果有catch(InputMismatchException e):会将之前产生的异常对象传递到catch代码中,并执行catch中的代码,程序就不会死了。

4.异常类结构体系

Java类库中有很多的"异常类",每种异常类都描述了一种异常情况,这些异常类的体系结构:
Throwable(顶层异常类)
|–Error(严重的错误):指程序中不需要处理,也是无法处理的一些很严重的异常情况
类名后缀:XxxxxError
示例:
1).定义一个Student类:
public class Student {
int[] arr = new int[1024 * 1024];//4M空间
}

​ 2).定义一个测试类:
public static void main(String[] args) {
//Error:程序不需要处理的非常严重的错误
ArrayList stuList = new ArrayList<>();
while (true){
stuList.add(new Student());
}
}
|–Exception(异常):
|–RuntimeException(运行时异常):比较普通的异常,如果程序员比较负责任,完全可以避免的异常情况。
1).NullPointerException:空指针异常
2).IndexOutOfBoundsException:索引越界异常(使用ArrayList索引超出范围时)
3).ArrayIndexOutOfBoundsException:数组索引越界异常(使用数组时,索引超出范围)
4).StringIndexOutOfBoundsException:字符串的索引越界异常(使用String时,例如:charAt(index)),索引超出范围)
5).InputMismatchException:Scanner接收到一个与预期不符的数据时,抛出的异常

|–除RuntimeException(及子类)以外的其它异常:非运行时异常,也叫:编译期异常。这种异常通常需要我们使用try…catch进行处理,如果不处理,编译不通过
1).ParseException:使用SimpleDateFormat的parse方法时,抛出的异常

5.Java异常处理机制最基本的代码:

​ try{
//写可能出现异常的代码
}catch(异常类名 变量名){
//如果try中出现了和"异常类名"一样的异常,
//将会执行此catch中的代码
}
注意:
1).如果try中多行代码,只要某一行代码出现异常,try中的后续代码将全部被跳过
2).catch(异常类名)中的"异常类名"必须要和try中产生的异常"一致",否则无法"捕获"

​ 3).catch中的"异常类型"尽量"精确",尽量不要使用"父类异常类型"
但是,建议以下这种写法:
try {
Scanner sc = new Scanner(System.in);
System.out.println(“请输入年龄:”);
try {//try:尝试
int age = sc.nextInt();//出现异常,下面代码将不会被执行
System.out.println(“你的年龄是:” + age);//上面的代码出异常,这一行不会被执行

​ } catch (InputMismatchException e) {//catch:捕获异常对象
System.out.println(“你的输入有误!但是程序没有死,仍然可以继续健康的运行下去”);
}
System.out.println(“后续代码…”);

​ } catch (Exception e) {
System.out.println(“出现未知异常,请联系系统管理员!”);

6.try中可能产生多种异常的情况

在try中如果可能产生多种异常,可以使用以下语句:
try{

}catch(异常类型1 变量名){

}catch(异常类型2 变量名){

}
注意:
1).多个catch()里面的"异常类型"通常是"平级关系",都是子类。
2).多个catch()里面的"异常类型"可以是"子父关系",但"父类的异常类型"必须声明在最后面
3).JDK7开始,又有一种新的写法:【不常见】【当多种异常,都使用一种处理方式,可以使用这个语法】
try{

}catch(ArrayIndexOutOfBoundsException | NullPointerException | 其它异常类型… 变量名){
//注意:多个异常类型是"或"的关系,所以这里不能出现"父类"类型
//try中只要出现这几种异常的一种,都会执行此catch,在这里如果需要,可以判断具体是哪种类型,比较麻烦
}

7.第三种语法 finally:

​ try{

}catch(异常类型 变量名){
//如果try中出现"异常类型"的异常,会执行这里
}finally{
//无论try中是否出现异常,都会被执行的代码
//通常做一些"资源释放"的操作,当try和catch中都执行完毕,需要释放资源时,可以将代码写在这里
//这里不要修改try/catch使用的变量,return也不是finally中修改的值。
//可以不return 值[建议],也可以return 值[不建议]
}
应用场景:通常用在某个"方法"中,而且当try/catch中有return语句时,在return之前,无论是否出现异常,

​ 在return之前都需要被执行的代码,可以写在finally中。

try、catch、finally三个关键字有以下几种组合方式:
1).try…catch
2).try…catch…catch
3).try…catch…finally
4).try…catch…catch…finally
5).try…finally(try中如果出现异常,会执行finally,但由于没有catch,所以程序仍然会终止)

8.throws 处理

throws:声明抛出异常,异常的处理方式之一(另一种是try/catch)
        它是一个关键字,在"方法"的声明上处理异常,
        表示:告诉JVM,如果此方法中产生了声明的异常,让JVM将异常对象抛给"调用的代码"(谁调用我,我就抛给谁)

注意:
    如果throws抛出的是:
       a).RuntimeException(或者它的子类对象):调用的代码不用强制try...catch,可以编译通过,
                                           但是,如果出现异常,程序仍然是死掉
       b).编译期异常(除Runtime及其子类以外的异常):调用的代码必须try...catch或者继续throws抛出这个异常,否则编译错误!

9.throws 和 try{} catch的区别

//throws 处理后异常程序出现异常后中断执行
//try catch 方法处理后可以按照自己的逻辑在catch中对异常进行处理

10.异常的操作:

​ 1.创建异常
2.抛出异常 1+2两个步骤叫做产生了异常
3.处理异常

         - 声明throws抛出异常
         - try/catch捕获异常

throw关键字:用在方法内部,用于:抛出一个异常对象

注意:
1).如果throw抛出的是"运行时异常",可以不处理异常,即不声明抛出也不try…catch捕获
2).如果throw抛出的是"非运行时异常",方法必须处理,声明抛出或者try…catch捕获

11.怎样自定义异常:

1.自定义类,继承自Exception或者是它的某个子类
注意:自定义类 extends Exception :自定义类就是:编译期异常
自定义类 extends RuntimeException(子类):自定义类就是:运行时异常

2.在某个类的方法中
throw new 自定义异常对象(“异常信息”)

========================================================================
//异常对象的三个常用方法
//1.获取异常信息
System.out.println(“异常信息:” +
e.getMessage());
//2.toString()
System.out.println(“toString:” +
e.toString());//异常类名 + 异常信息

//3.打印异常的完整信息
e.printStackTrace();//红色的异常类名 + 异常信息 + 异常的位置

多线程

第七章 1.进程和线程

进程是正在执行的程序

线程是程序可以独立执行的单元

进程:是操作系统中的概念,指一个独立运行的程序就是一个"进程"
线程:由"进程"创建的,与主进程(即理解为主线程)“同时执行”,提高程序的效率,同时做多件事情。
java程序中,main方法执行在主线程中。

2.多线程:

​ 指我们的程序可以将一部分代码剥离出去,与主程序"同时执行"——这样可以使我们的程序"同时"做多件事情,提高程序的效率!!

3.注意:

1).之前我们编写的程序都是"单线程"的程序,代码从main()开始,后面的代码都要等待前面的代码执行完毕,然后才能被执行。

2).并发:一颗CPU,两个线程
并行:两颗CPU,两个线程,每个线程占用一颗CPU。

上述是在"硬件"层面上的"并发"与"并行"

4.创建线程的两种方式
第一种方式:继承Thread

1).自定类 extends Thread
2).重写run()方法。线程中要做的事情写在run()方法
即定义这个线程要执行的代码
3).启动线程
创建一个线程类对象:自定义Thread 变量 = new 自定义Thread();
调用线程对象的:start()来启动线程
注意:
1).重写的是run(),但启动线程调用的是start()
2).一个线程对象,只能start()一次,执行完毕,这个线程对象自动销毁,不能再次的start()
3).一个线程类,可以创建多个线程对象,每个线程对象都可以独立start(),以独立的线程的方式运行。

第二种方式 implements Runnable :

1.自定义Runnableimplements Runnable接口
2.重写run()方法
3.启动线程:
    创建一个自定义类对象
    Thread t = new Thread(自定义Runnable类对象);
    t.start();

======================================================

5.两种实现线程方式的区别:

1).第一种需要子类 继承 Thread类,由于Java是单继承,所以对子类形成了限制。
2).第二种需要子类 实现 Runnable接口,对于子类就比较灵活,子类仍然可以继承其它类,
并且实现其它接口。

6.Thread中的方法

第一种实现线程的方式:自定义类 extends Thread

我们可以从Thread类中继承的方法:
1).run():必须重写,我们的线程中需要做的事情写到这里;
2).start():启动线程。不要重写(可以重写),重写后就无法启动线程了
3).getName():获取当前的线程名称。
每个线程都有一个默认的线程名称:Thread-索引
4).setName(String s):设置线程的名称。
--------------以下是一些静态方法--------------------------
5).public static Thread currentThread():获取当前的线程对象。
6).public static void sleep(long m):让当前线程休息m毫秒

第八章 线程安全性

1.多线程在内存中的运行机制:

1).多线程运行时,各自线程进入各自的栈区,如果每个线程做的事情不相关,这样没什么问题,

但,如果多个线程访问同一个资源(变量、文件、数据库的数据…),就会产生多线程的并发问题,
使数据跟最终预想的不一样。

   多线程安全问题前提:
   1.多个线程
   2.访问同一个数据
   3.必须有修改/添加/删除元素的逻辑
2).多线程情况下,可能出现的问题有三种:

​ 1).对同一个变量访问的:可见性

多线程的问题一:变量的可见性

​ 当两个线程共同访问一个变量时,如果一个线程将变量的值更改,
而另一个线程由于访问速度过快,而没有看到这个变量被修改,这就是:
由多线程运行机制导致的:变量的可见性问题。

​ 2).对同一段代码访问的:有序性

​ 程序在编译期间,虚拟机有可能把没有上下逻辑关系的代码进行代码顺序的打乱,叫重排.
一个线程的重排可能对其他线程造成影响,叫有序性问题.

编译器经常在不影响最终结果的前提下,为了提高编 译效率,会对一些代码进行"重排——不按照我们编写的顺序"
例如:
//我们编写的代码
int a = 10;
int b = 20;
int c = a + b;
//编译器为了提高编译效率,很可能在不影响最终结果的前提下,对上述代码进行"重排"
int b = 20;
int a = 10;
int c = a + b;
但是,在多线程情况下,这种"代码重排"可能就会使结果不确定!!!
所以,多线程情况下,我们不希望代码进行重排!!!

​ 3).对同一个变量/一段代码访问的:原子性

2.volatile关键字

1)保证内存的可见性,

2)禁止指令重排序,有序性

可以解决可见性的问题,只要给变量加上volatile,变量就会每次都重新获取

3.多线程原子类

从JDK1.5开始,Java提供了一个新的包:java.util.concurrent(JUC包),这个包中提供了大量的"多线程"的工具类。

1).java.util.concurrent.atomic.AtomicInteger:可以保证多线程对一个int数值访问的原子性。
    它内部定义了一个变量,来存储多线程操作的int值:
        private volatile int value;
    这样可以首先保证变量的:可见性、有序性;
    然后通过getAndIncrement()和incrementAndGet()方法中的一些"机制"保证访问变量的"原子性"
    什么机制:CAS机制(比较并交换)

2).java.util.concurrent.atomic.AtomicBoolean:对boolean变量访问的原子类
3).java.util.concurrent.atomic.AtomicLong:对long变量访问的原子类
4).java.util.concurrent.atomic.AtomicReference:对引用类型访问的原子类

4.多线程数组操作原子类

1).java.util.concurrent.atomic.AtomicIntegerArray :可以对一个int[]数组做:原子操作。【主要演示】

2).java.util.concurrent.atomic.AtomicLongArray:对long[]数组操作的原子类
3).java.util.concurrent.atomic.AtomicReferenceArray:对引用类型数组操作的原子类

5.synchornized 关键字

1).之前我们学习了多线程并发的三个问题:

​ 1).可见性
2).有序性
3).原子性

2).volatile可以解决变量的:可见性、有序性;
3).对变量的原子性操作:

​ 1).AtomicInteger:对int变量进行原子操作;
2).AtomicIntegerArray:对int[]数组进行原子操作;

4).synchronized(同步的)

如果需要将一段代码进行"原子操作"——一个线程执行这段代码时,其它线程不允许执行这段代码,要在外面排队
必须保证当前正在执行的线程将这段代码全部执行完毕,其它线程才可以执行这段代码。

Java提供一个重量级的对代码进行原子操作的关键字:synchronized(同步的)

5).synchronized使用的两种方式:
A).同步代码块:

​ public void show(){
synchronized(锁对象){



}
}
“锁对象”:它是一个"对象"——任何类的对象都可以。
重点:但必须保证"多个线程共同拥有同一个<锁对象>"

B).同步方法:[常用]

​ public synchronized void show(){

​ }
同步方法:
public synchronized void show(){//当一个线程访问此方法时,立即加锁,其它线程全部在外部等待。
当执行的线程将此方法全部执行完毕,会自动释放锁,其它线程才可以进入

​ //方法中所有的代码都是"同步代码"
}

​ 同步方法的锁,是this~!!

​ 因为只new了一个runnable,供三个线程使用。
所以三个线程共享同一个对象,该对象就是锁

​ 静态同步方法的锁对象是类名.class

6.Lock锁

从JDK5开始,Java提供了另一种代替synchronized的锁:Lock锁

1).java.util.concurrent.atomic.locks.Lock(接口):做"锁"的。

​ API文档中的示例代码:
Lock l = …;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}

2).Lock锁和synchronized锁的区别:

​ 1).Lock锁中的代码如果出现异常,不会自动释放锁,程序就跟死了一样。
Lock锁的功能要比synchronized更强大,使用更灵活。在JUC包(java工具并发包)中的工具类,使用Lock锁的比较多

​ 2).synchronized中的代码如果出现异常,会自动释放锁!在其它包中使用synchronized的比较多

7.对集合做原子操作

1).对int变量进行原子操作:AtomicInteger
2).对int[]数组进行原子操作:AtomicIntegerArray
3).对集合对象进行原子操作:
1).List集合:java.util.concurrent.CopyOnWriteArrayList

​ 面试题:
1).java.util.Vector:它是一个List集合,内部是用数组实现,从JDK1.0开始的
2).java.util.ArrayList:它也是一个List集合,内部也是用数组实现,从JDK1.2开始
3).java.util.concurrent.CopyOnWriteArrayList:它也是一个List集合,内部也是用数组实现,从JDK1.5开始

​ 这三个类有什么区别:
1).最大的区别:
ArrayList是:线程不安全的;
Vector和CopyOnWriteArrayList:是线程安全的。
2).Vector和CopyOnWriteArrayList的区别:
1).Vector内部是用:synchronized实现的,synchronized被称为是:重量级锁(悲观锁)——效率比较低;无论是否有多线程竞争,都会拿锁,有额外的操作。
2).CopyOnWriteArrayList:内部是使用:CAS机制实现的:轻量级锁(乐观锁)——效率高。如果没有多线程竞争,不需要自旋,一次就可以修改成功。只有当有多线程竞争时,才有自旋的产生。

2).Set集合: java.util.concurrent.CopyOnWriteArraySet:
3).Map集合:java.util.concurrent.ConcurrentHashMap<K,V>:

ArrayList 线程不安全的
Vector 老的,线程安全的 → 大部分方法均是Synchronized修饰的同步方法
CopyOnWriteArrayList 最新的,大部分方法,提供了线程安全的处理,如lock锁 底层使用CAS机制

C:compare 比较
A:and 和
S:swap 交换

CAS指通过逻辑,完成乐观锁的安全判断

8.CAS机制 compare and swap

悲观锁:无论别的线程有没有修改变量,我都认为修改了,要做安全处理
乐观锁:通过代码逻辑,在要判断同步安全问题的时候判断,不用判断的就不判断
具体的过程:
a线程 和 b线程 同时操作共享数据 (以a线程的视角)
a线程预先记录下共享数据的值
a线程调用方法访问集合元素时,会先获取当前的共享数据的值
情况1
预先值 == 当前值 (比较过程C)
没有其他线程操作过该元素,正常操作元素
情况2
预先值 != 当前值 (比较过程C)
终止当前操作,因为期间有其他线程修改了该共享数据
将预先值交换成当前值(交换过程S)
继续重头开始集合操作

第九章 Callable Timer lambda

1.线程池

1).概念:它是一种"容器",用于缓存一些"线程对象"的。
2).场景:

​ 一个线程对象不能反复的start(),只能start()一次
如果想再次使用此线程,需要再次创建此线程对象
作用:
1).可以缓存一些线程对象,让我们每次执行一个线程不需要创建新的对象,提高创建线程的效率。
2).可以限制多个线程的"并发数量"

3).API:

​ Executors是一个"工具类",静态方法:newFixedThreadPool(int n):可以获取一个有固定容量的线程池对象n为线程个数
ExecutorService:线程池类
submit(Runnable run) 提交执行目标方法:向线程池提交要执行的内容。线程池分配线程执行。
提问:不传入Runnable,传入Thread可以么? 可以
shutdown() 关闭线程池

4).实现线程的第三种方式:实现Callable接口的方式

1).从JDK1.5开始的
2).之前我们实现线程的两种方式:继承Thread,实现Runnable接口,然后重写run()方法,这两种方式都有两个重要的缺点:
1).run()方法都不能返回值:
2).run()方法不能抛出"编译期异常"
3).实现Callable接口的好处,需要重写call(),此方法就可以:
1).返回值
Callable的call方法会有返回值Future对象,该对象包括了返回值结果,并且提供了一些有用的方法
Future的isDone方法,判断当前callable是否调用结束
Future的get方法,从对象中获取方法真正的返回值
2).可以抛出任何异常
4).实现方式:
第一步:自定义线程类,实现Callable接口,并重写call()方法
第二步:启动线程——必须使用"线程池"启动

5).线程的状态:

​ 当我们操作线程的某些方法时,会使线程处于某些状态下(见图1)
1).新建
start();
2).可运行:
3).计时等待(sleep)
4).锁阻塞(执行到被锁的方法)
5).无限等待(wait())
6).被终止(run()执行完毕)

6). Object中的wait()和notify()
等待(wait)和唤醒(notify): 它用于两个线程之间的配合
notify(), 没有线程咋等待的时候可以空唤醒
notifyAll() 唤醒所有线程 
假如:
第一个线程先启动
   工作...
   发现一些问题...
   wait()释放锁,进入到(无限等状态,等待被唤醒)
第二个线程拿到锁
   工作...
   工作完毕notify()唤醒在这个锁上等待的线程(不会释放锁)
   释放锁
第一个线程拿到锁
   恢复工作...

两个线程相互配合一个前提:必须共享同一把锁

2.timer 定时器

Java类库中有一个线程的应用:定时器,可以让程序启动后,在指定的时间/日期时执行一次某个任务,也可以间隔指定的时间反复的执行某个任务

实现定时器:
    1).java.util.TimerTask(抽象类):用于定义定时任务。我们需要继承此类,并重写它内部的run()方法,编写我们的任务。
    2).java.util.Timer():用于启动定时任务的。
        1).构造方法:
            1).Timer():无参构造
        2).成员方法:
            1).void schedule(TimerTask task, long delay) 在指定的延迟之后安排指定的任务执行。【只执行一次】
            2).void schedule(TimerTask task, long delay, long period):在delay延迟时间后执行task任务,每间隔period时间,重复执行此任务【往复执行】

            3).void schedule(TimerTask task, Date time) 在指定的日期-时间安排指定的任务执行。【只执行一次】
            4).void schedule(TimerTask task, Date firstTime, long period):在firstTime时间开始执行task任务,每间隔period时间,重复执行此任务【往复执行】
            5).cancel,用于取消定时器

3.lambda 表达式

1).概念

Lambda表达式:从JDK1.8开始的,它是一种"代替方案"——代替之前当我们调用一个方法,但方法的形参是一个接口,而且接口中只有一个抽象方法时(函数式接口),
此时,我们需要传入一个"子类",Lambda表达式就可以代替这个子类。

2).好处:

​ 1).直接写方法体,编写简单
2).Lambda不是语法糖,编译后不是匿名内部类,整个过程没有定义类,没有创建对象,所以效率比较高。

3).弊端:

​ 1).可读性差。
2).不能重用。
3).破坏了Java面向对象的思想。
4).应用面很窄:必须面向"只有一个抽象方法的接口",必须是接口,而且必须有、且只有一个必须被子类重写的抽象方法(可以有其它的静态方法、默认方法…)。
不能是抽象类。

4).Lambda的标准格式:

​ 1).一对小括号():方法的形参部分
2).一个右箭头->:Lambda专属语法
3).一对大括号{}:重写的方法的方法体

Lambda表达式必须要面向"函数式接口":
对于这种"函数式接口"为了保证它的语法正确,通常会加注解:@FunctionalInterface——告诉编译器,下面的接口是一个函数式接口,如果不符合函数式接口的语法,则会编译错误
    例如:类库中的Runnable接口:
            @FunctionalInterface
            public interface Runnable {
                public abstract void run();
            }
    例如:自定义接口:
            @FunctionalInterface
            interface IA{
                public abstract void eat();//子类必须重写
                public abstract String toString();//写上这个抽象方法,就没有异常,仍然是一个合法的函数式接口。因为在Object类中有这个方法,任何类都会继承,所以子类可以不重写这个方法。
                public boolean equals(Object o);
            }
5).简化lambda语法:

​ 如果参数只有一个,可以同时省略:数据类型,一对小括号。
如果方法体中只有一句话,可以同时省略:语句后的分号、一对大括号、return关键字(如果有)

4.Stream 流

1).概念

​ java.util.stream.Stream(接口):Stream流是JDK8新增的,它提供了很多方法结合一些函数式接口,可以对大量的数据进行多次的筛选,过滤、统计…操作。
我们可以将Stream流看做一个高级的迭代器,结合Lambda表达式
对大量数据进行遍历、筛选、过滤、统计等操作非常方便。

2).方法
(1).forEach(…):用于遍历元素的。
 参数接收一个Consumer函数式接口,方法为accept(T t),代表对参数的处理
forEach内部会做一个循环,将每个元素作为"实参"调用一次accept(T t)方法

Consumer的使用可以简化为lambda方式使用

注意,每个stream只能使用一次,类比迭代器
(2).filter(…):用于过滤元素。

​ 参数接收一个Predicate函数式接口,方法为boolean test(T t),代表对参数的处理,返回true则符合条件,false则不符合条件
filter内部会做一个循环,将每个元素作为"实参"调用一次test(T t)方法

​ Predicate的使用可以简化为lambda方式使用

Lambda的一个特点 :

​ 惰性求值——只有当我们执行"终结方法"时,之前的"拼接方法"才会被执行。
forEach()方法就是:终结方法,这种方法通常是最后执行。这种方法通常"没有返回值"。
filter()方法就是:拼接方法,这种方法通常在"终结方法"之前被调用。这种方法通常返回Stream流
注意:拼接方法后需要使用过滤后新返回的stream对象,再次使用。同一个stream只能使用一次

链式编程:对象在调用一个方法,返回对象后,再使用返回的对象调用方法,如此反复

(3).终结方法count():统计流中元素的数量
(4).拼接方法limit(int c):获取流中前c个元素
(5).拼接方法skip(int c):跳过前c个元素,返回一个新流
(6).Stream流的map(…):

用于将一种泛型的流(Stream)转换为另一种泛型(Stream)的流。该方法为拼接方法。 参数接收一个Function函数式接口,方法为R apply(T t),代表对T型参数的处理,返回R型数据 map(…)内部会做一个循环,将每个元素作为"实参"调用一次R apply(T t)方法

(7).Stream 的静态方法

static concat(Stream s1,Stream s2):将参数的两个流合并为一个流,返回。
collect(...):将流中的数据,转换为List集合、Set集合、数组。
        参数接收一个Collector的toXXX方法,最终返回XXX容器
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值