Java学习笔记-05-常用API

目录

Object

常见方法

toString()

hashCode()

equals(Object obj)

getClass()

clone()

Objects

包装类

Integer

定义方式

拆箱&封箱

方法

String

创建方式

构造函数

常用方法

案例

注意点

String

StringBuffer/StringBuilder

常用方法

扩展

StringBuilder和StringBuffer的区别(了解)

StringJoiner

Math

常用方法

BigDecimal

构造器,常见方法

System

常见方法

Runtime

常见方法

Date

LocalDate

常用方法

LocalTime

常用方法

LocalDateTime

常用方法

小汇总

ZoneID 

ZonedDateTime

Instant

常见方法

作用

DateTimeFormatter

Period

Duration

Array

常用方法

对象数组排序

Lambda表达式

Lambda表达式

Lambda简化规则

方法引用::

静态方法的引用

实例方法的引用

特定类型方法的引用

构造器引用


是跟的B站的黑马Java视频做的笔记,视频地址:视频地址

Object

  • Object类是Java中所有类的祖宗类,因此,Java中的所有类都可以直接使用Object类中提供的一些公共方法

常见方法

toString()
  • 直接打印对象,对象自动会调用toString方法。所以重写toString方法后,直接输出对象,就会输出toString重写后的,不会输出地址了

toString(),默认返回对象的内存地址,一般子类都会进行重写,生成对象属性的详细信息

  • 重写快捷方法,右键点Generate...,里面有toString方法

Test.java

// 所有的类间接或者直接继承Object类,Object是所有类的祖先类
public class Test {
    String name = "张三";
    int age = 25;
    // 重写Object类的toString方法
    @Override
    public String toString() {
        return "Test{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Mian.java

public class Main {
    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.toString()); // Test{name='张三', age=25}
        // 跟下面这些写是一样的,因为打印对象,它会自动调用toString方法
        System.out.println(test); // Test{name='张三', age=25}
    }
}
hashCode()

hashCode,用于返回对象的哈希码值(就是经过哈希算法计算出来的值)

public class Main {
    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.toString()); // 如果子类不重写,返回该对象的内存地址
        System.out.println(test.hashCode()); // 返回对象的哈希码值
    }
}
equals(Object obj)

equals,会比较两个对象的内存地址。底层是用 == 作比较,== 对于对象比较的就是内存地址

public class Main {
    public static void main(String[] args) {
        Test test = new Test();
        Test test2 = new Test();
        // 会比较两个对象的内存地址
        System.out.println(test.equals(test)); // true
        System.out.println(test.equals(test2)); // false
    }
}

一般子类会进行重写,但是重写的话,hashCodeequals都要进行重写,要不都不进行重写

  • 注意:如果一个对象,调用equals方法,直接比较的值,那么这个产出这个对象的类肯定是重写了toString方法。例如String

Test.java

import java.util.Objects;
public class Test {
    String name;
    int 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;
    }
​
    public Test() {
    }
​
    public Test(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Test test = (Test) o;
        return age == test.age && Objects.equals(name, test.name);
    }
​
    @Override
    public int hashCode() {
        // 根据传入的属性进行哈希值的生成,这样只要对象的属性相同,则返回的哈希值也是相同的
        return Objects.hash(name, age);
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Test t1 = new Test("张三",25);
        Test t2 = new Test("张三",25);
        Test t3 = new Test();
        // hashCode如果是一样的属性值,则返回的哈希码是一样的
        System.out.println(t1.hashCode()); // 24022545
        System.out.println(t2.hashCode()); // 24022545
        System.out.println(t3.hashCode()); // 961
        // equals()默认会比较两个对象的内存地址,重写后是比较对象的值
        System.out.println(t1.equals(t2)); // true
    }
}
getClass()

getClass,会返回改对象运行时的类(也就是属于那个类,是哪个类new出来的对象)

Test.java

import java.util.Objects;
public class Test { // ... }

Main.java

public class Main {
    public static void main(String[] args) {
        Test t1 = new Test();
        Test t2 = new Test();
        // getClass() 返回此 Object 的运行时类。只要是同一个类实例化出来的对象,就是true
        System.out.println(t1.getClass() == t2.getClass()); // true
    }
}
clone()

protected Object clone()

  • 创建并返回此对象,对象克隆。默认是浅拷贝

  • 克隆分为深浅拷贝

    • 深拷贝:拷贝原对象的值,并且在堆内存中开辟了一块新的内存空间。不管什么操作,都不会影响原对象

    • 浅拷贝:即返回一个新的对象,但是新对象里的引用类型变量地址指向的还是原对象内引用类型地址,会互相影响

Animal.java

package Demo;
// 默认会继承Object类,Cloneable接口中什么都没有,把这种什么内容都没有的,叫做标记接口
public class Animal implements Cloneable {
    public String name;

    public String getName() {
        return name;
    }

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

    public Animal() {
    }

    public Animal(String name) {
        this.name = name;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void print() {
        System.out.println("name:" + name);
    }
}

Main.java

import Demo.Animal;

public class Main {
    public static void main(String[] args) {
        Animal a = new Animal("张三");
        try {
            Animal cloneA = (Animal) a.clone();
            System.out.println(a == a.clone()); // false,说明地址不一样
            // 但是clone方法,默认是浅拷贝
            // 为什么这里是深拷贝的呢?其实并不是,现在看来是深拷贝,我下面再做一个测试,就看出来了
            cloneA.setName("李四");
            System.out.println(a.getName()); // 张三
            System.out.println(cloneA.getName()); // 李四
            a.print(); // name:张三
            cloneA.print(); // name:李四
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

深浅拷贝测试

Run.java

package Demo;

public class Run {
    private String speed;

    public String getSpeed() {
        return speed;
    }

    public void setSpeed(String speed) {
        this.speed = speed;
    }

    public Run() {
    }

    public Run(String speed) {
        this.speed = speed;
    }
}

Animal.java

package Demo;

public class Animal implements Cloneable {
    private String name;
    // 这里定义了一个Run类型的变量
    private Run run;

    public Animal() {
    }

    public Animal(String name, Run run) {
        this.name = name;
        this.run = run;
    }

    public String getName() {
        return name;
    }

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

    public Run getRun() {
        return run;
    }

    public void setRun(Run run) {
        this.run = run;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Main.java

import Demo.Animal;
import Demo.Run;

public class Main {
    public static void main(String[] args) {
        Animal a = new Animal("张三",new Run());
        try {
            Animal cloneA = (Animal) a.clone();
            // 这里用克隆出来的新对象,来修改run变量(堆内存地址)里面的speed变量
            Run r = cloneA.getRun();
            r.setSpeed("100");
            System.out.println(a.getRun().getSpeed()); // 100 发现原对象的值也变了,受到克隆对象的影响了,所以是浅拷贝
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

看到这里,其实也看出来浅拷贝原因了

如果原对象定义了一个引用数据类型的变量,克隆的新对象会把原对象引用数据类型的堆内存地址复制过来。所以就是浅拷贝

那我们如何实现深拷贝呢?(网上找的实现方法)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Cloneable{
	
	private String name;
	private Address address;
	
	/**
	 * 因为Address不是基础数据类型或者String类型
	 * 所以需要先重写Address类的clone方法才能完成深拷贝
	 * 并且重写clone方法时可以将protected改为public 将返回类型改为重写的类
	 */
	@Override
	public User clone() throws CloneNotSupportedException {
		User obj = (User) super.clone();
		obj.setAddress(this.address.clone());
		return obj;
	}
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address implements Cloneable{
	
	private String addr;
	
	@Override
	public Address clone() throws CloneNotSupportedException {
		return (Address) super.clone();
	}
}

public class Main {

	public static void main(String[] args) throws CloneNotSupportedException {
		Address address = new Address("China");
		User user01 = new User("name", address);
		User user02 = user01.clone();
		System.out.println("u1 -> " + user01);	// User(name=name, address=Address(addr=China))
		System.out.println("u2 -> " + user02);	// User(name=name, address=Address(addr=China))
		address.setAddr("US");
		System.out.println("u1 -> " + user01);	// User(name=name, address=Address(addr=US))
		System.out.println("u2 -> " + user02);	// User(name=name, address=Address(addr=China))
	}

}

我自己做了下测试,确实可以

Run.java

package Demo;

public class Run implements Cloneable {
    private String speed;

    public String getSpeed() {
        return speed;
    }

    public void setSpeed(String speed) {
        this.speed = speed;
    }

    public Run() {
    }

    public Run(String speed) {
        this.speed = speed;
    }

    @Override
    public Run clone() throws CloneNotSupportedException {
        return (Run) super.clone();
    }
}

Animal.java

package Demo;

public class Animal implements Cloneable {
    private String name;
    // 这里定义了一个Run类型的变量
    private Run run;

    public Animal() {
    }

    public Animal(String name, Run run) {
        this.name = name;
        this.run = run;
    }

    public String getName() {
        return name;
    }

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

    public Run getRun() {
        return run;
    }

    public void setRun(Run run) {
        this.run = run;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Animal a = (Animal) super.clone();
        a.setRun(this.run.clone());
        return a;
    }
}

Main.java

import Demo.Animal;
import Demo.Run;

public class Main {
    public static void main(String[] args) {
        Animal a = new Animal("张三",new Run());
        try {
            Animal cloneA = (Animal) a.clone();
            // 这里用克隆出来的新对象,来修改run变量(堆内存地址)里面的speed变量
            Run r = cloneA.getRun();
            r.setSpeed("100");
            System.out.println(a.getRun().getSpeed()); // null 并没有受到克隆对象的影响,所以是深拷贝
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

但是也引发了我一个想法

如果Run.java中也声明了一个引用数据类型呢?恐怕又带再处理下吧。前端中可以用递归实现完全的深拷贝,但是Java中我就不太清楚了

Objects

Objects类是 jdk 1.7 开始之后才有的。提供的方法都是静态方法

  • equals(Object a,Object b) 先做非空判断,再比较两个对象的内存地址。与对象的equals方法相比,会多一步判空(判断对象是否为null),这个很重要,正是有了这一步判空,就不会报空指针异常

  • isNull(Object obj) 判断变量是否为null,为null返回true

  • nonNull(Object obj) 判断对象是否为null,不为null则返回true,反之,为null则返回false

Main.java

import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        Test t1 = new Test("张三",25);
        Test t2 = new Test("张三",25);
        // Objects的equals()方法,是比较的两个对象的地址,主要是用于比较是不是有null
//        public static boolean equals(Object a, Object b) {
//            return (a == b) || (a != null && a.equals(b));
//        }
        System.out.println(Objects.equals(t1, t2)); // false
        // 而Object的equals()方法,比较的也是两个对象的内存地址,但是没有处理null
        System.out.println(t1.equals(t2)); // false
        int a = 10;
        int b = 10;
        Integer c = 10;
        System.out.println(Objects.equals(a, b)); // true
        System.out.println(Objects.equals(a, c)); // true
        System.out.println(a == b); // true
        System.out.println(a == c); // true
        System.out.println(Objects.isNull(null)); // true
        
        String name = null;
        String name1 = "张三";
        // 如果有值为null,就会出现空指针异常,但是Objects的equals方法就不会报异常
        // System.out.println(name.equals(name1)); 
        System.out.println(Objects.equals(name, name1)); // false
    }
}

包装类

包装类就是把基本数据类型包装成复杂数据类型。因为有的不支持传入基本数据类型,所以就要把基本数据类型包装成复杂数据类型,例如泛型

基本数据类型:一共有8种

基本数据类型 对应的包装类(引用数据类型)
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean

Integer

其他基本都跟Integer同理,这里就只介绍Integer了

定义方式

Main.java

public class Main {
    public static void main(String[] args) {
        // 简单类型包装成复杂数据类型,有两种方式
        Integer num = 100;
        System.out.println(num); // 100
        // 第二种方式 Integer.valueOf(int i)
        Integer num1 = Integer.valueOf(100);
        System.out.println(num1); // 100
    }
}
拆箱&封箱

Main.java

public class Main {
    public static void main(String[] args) {
        // 自动封箱。可以自动把基本类型的数据转为对象
        Integer num = 100;
        // 自动拆箱。可以自动把包装类型的对象转换成对应的基本数据类型
        int num1 = num;
    }
}
方法

int类型的数据转为字符串类型

public static String toString(int i)

public String toString()

Main.java

public class Main {
    public static void main(String[] args) {
        // int类型的转为字符串
        System.out.println(Integer.toString(100)); // "100"
        Integer n = 100;
        System.out.println(n.toString().equals("100")); // true
    }
}

把字符串类型的数值转换为本身对应的数据类型

public static int parseInt(String s)

public static Integer valueOf(String s)

Main.java

public class Main {
    public static void main(String[] args) {
        // 把字符串的类型转为原先的类型
        String s = "100";
        System.out.println(Integer.valueOf(s)); // 100
        System.out.println(Integer.parseInt(s)); // 100
    }
}

String

创建方式

  1. 直接使用双引号 “ ...... ”   这种使用最多

  2. new String类,调用构造器初始化字符串对象

构造函数

// 1. 创建一个空白字符串对象
String s1 = new String();
// 2. 根据传入的字符串内容,来创建字符串对象
String s2 = new String("Hello");
// 3. 根据传入的字符数组的内容,来创建字符串对象
char[] c = {'a','b','c'};
String s3 = new String(c);
System.out.println(c); // "abc"
// 4. 根据传入字节数组的内容,来创建字符串对象
byte[] b = {97, 98, 99};
String s4 = new String(b);
System.out.println(s4); // "abc"
// 5. "" 创建字符串对象
String name = "张三";
System.out.println(name ); // "张三"

常用方法

方法名 说明
public int length() 获取字符串的长度返回(就是字符个数)
public char charAt(int index) 获取某个索引位置处的字符返回(通过索引获取数据)
public toCharArray(): 将当前字符串转换成字符数组返回
public boolean equals(Object anObject) 判断当前字符串与另一个字符串的内容一样,一样返回true
public boolean equalsIgnoreCase(String anotherString) 判断当前字符串与另一个字符串的内容是否一样(忽略大小写)
public String substring(int beginIndex, int endIndex) 根据开始和结束索引进行截取,得到新的字符串(包前不包后)
public String substring(int beginIndex) 从传入的索引处截取,截取到末尾,得到新的字符串返回
public String replace(CharSequence target, CharSequence replacement) 使用新值,将字符串中的旧值替换,得到新的字符串
public boolean contains(CharSequence s) 判断字符串中是否包含了某个字符串
public boolean startsWith(String prefix) 判断字符串是否以某个字符串内容开头,开头返回true
public String[] split(String regex) 把字符串按照某个字符串内容分割,并返回字符串数组

案例

String s = "hello";
// 1. 获取字符串长度
int length = s.length();
System.out.println(length); // 5
// 2. 根据索引,获取值
char s1 = s.charAt(1);
System.out.println(s1); // e
// 可以通过charAt()这个方法进行字符串的遍历
for (int i = 0; i < s.length(); i++) {
    System.out.println(s.charAt(i)); // 数组通过下标,字符串就只能通过这个
}
// 3. 把字符串转成数组
char[] c = s.toCharArray();
System.out.println(c[0]); // h
// 4. 比较两个字符串的值是否相等
String s3 = "world";
boolean flag = s.equals(s3);
System.out.println(flag); // false
// 5. 比较两个字符串的值是否相等(忽略大小写)
String s4 = "HELLO";
boolean flag2 = s.equalsIgnoreCase(s4);
System.out.println(flag2); // true
// 6. 截取字符串,按照索引。substring(index,end) 包前不包后
String s5 = s.substring(1,3);
System.out.println(s5); // el
// 7. 截取字符串,截取到最后。substring(index) 只传一个参数,会直接截取到最后
String s6 = s.substring(2);
System.out.println(s6); // llo
// 8. 替换字符串
String s7 = s.replace('l','*');
System.out.println(s7); // he**o
// 9. 字符串中是否包含某个字符 contains()
boolean res = s.contains("ll");
System.out.println(res); // true
// 10. 是否以某个字符开头
boolean is = s.startsWith("he");
System.out.println(is); // true
// 11. 把字符串转成数组
String s8 = "h-e-l-l-o";
String[] arr = s8.split("-");
System.out.println(arr[0]); // h

注意点

  • 字符串是不可变的

String

String是一个封装char[]数组的对象,字符串不可变

如果是new出来的字符串对象,会存放在堆内存中。如果是直接 "" 赋值的,则会存放在堆内存的变量池中,同样的取值只会存一份

Main.java

public class Main {
    public static void main(String[] args) {
        // 创建字符串对象方式1,这种会存放在堆内存中的常量池中,相同的值只会存一份
        String s1 = "hello";
        // 创建字符串对象方式2,这种会存放在堆内存中,每次new都会存一份
        String s2 = "world";
    }
}

常见方法

public class Main {
    public static void main(String[] args) {
        // 创建字符串对象方式1,这种会存放在堆内存中的常量池中,相同的值只会存一份
        String s1 = "hello";
        // 创建字符串对象方式2,这种会存放在堆内存中,每次new都会存一份
        String s2 = "World";
        // 返回此字符串的哈希码
        System.out.println(s1.hashCode());
        // 所有字符都转换为大写
        System.out.println(s1.toUpperCase()); // HELLO
        // 所有字符都转换为小写
        System.out.println(s2.toLowerCase()); // world
        // 测试此字符串是否以指定的元素开头
        System.out.println(s1.startsWith("he")); // true
        // 测试此字符串是否以指定的字符串结束
        System.out.println(s2.endsWith("d")); // true
        // 返回指定字符在此字符串中第一次出现处的索引
        System.out.println(s1.indexOf("o")); // 4
        // 返回指定字符在此字符串中最后一次出现处的索引
        System.out.println(s1.lastIndexOf("l")); // 3
        // 拼接数组,不会改变原数组
        System.out.println(s1.concat(s2)); // helloWorld
        // 去除首尾空格
        String s3 = "   两侧有空白   ";
        System.out.println(s3.trim()); // 两侧有空白
        // 把数字转成字符串,静态方法
        System.out.println(String.valueOf(100)); // "100"
        System.out.println(String.valueOf(100) instanceof String); // true
        // String重写了Object的hashCode方法,是根绝字符串的值生成的哈希码
        // 所以只要字符串的值相等,则他俩返回的哈希码也一样
        String s5 = "hello";
        System.out.println(s5.hashCode() == s1.hashCode()); // true
    }
}

StringBuffer/StringBuilder

  • 封装了char[]数组,默认长度为16

  • 是可变的字符序列

  • 常用append()来代替字符串做字符串连接+

  • 适合做字符串拼接特别多的情况,可以大幅度提高性能

Main.java

public class Main {
    public static void main(String[] args) {
        // 拼接字符串
        StringBuffer s1 = new StringBuffer("hello");
        StringBuilder s2 = new StringBuilder();
        System.out.println(s1.append("world")); // "helloworld"
        System.out.println(s2.append("hello")); // "hello"
    }
}

拼接字符串案例

Main.java

public class Main {
    public static void main(String[] args) {
        String s = "你干嘛~";
        // 1. 测试StringBuffer
        // 记录下开始事件
//        System.out.println("开始时间是:" + System.currentTimeMillis());
//        m1(s,"哎呦");
        // 记录下结束时间
//        System.out.println("结束时间是:" + System.currentTimeMillis());
        // 2. 测试StringBuilder
        System.out.println("开始时间是:" + System.currentTimeMillis());
        m2(s,"哎呦");
        // 记录下结束时间
        System.out.println("结束时间是:" + System.currentTimeMillis());
    }
​
    // StringBuffer
    public static String m1(String s,String addStr) {
        StringBuffer s1 = new StringBuffer(s);
        StringBuffer res = s1.append(addStr);
        return res.toString();
    }
​
    // StringBuilder
    public static String m2(String s,String addStr) {
        StringBuilder s1 = new StringBuilder(s);
        StringBuilder res = s1.append(addStr);
        return res.toString();
    }
}
常用方法

Main.java

public class Main {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("hello");
        // 1. append(任意类型)  添加元素  支持链式调用
        sb.append("world");
        System.out.println(sb); // helloworld
        // 2. reverse()  内容反转
        System.out.println(sb.reverse()); // dlrowolleh
        // 3. length()  获取对象内容长度
        System.out.println(sb.length()); // 10
        // 4. toString()  可以把StringBuffer或者StringBuilder类型的转为String
        String s = sb.toString();
        System.out.println(s); // dlrowolleh
    }
}

为什么要用StringBuffer或者StringBuilder呢?而不用原来学过的String

  • 首先String是不可变的,不如StringBuffer或者StringBuilder,因为它俩的字符串是可变的

  • 拼接或者修改字符串比较多时,StringBuffer或者StringBuilder性能明显优于String

扩展

==

  • 如果是基本数据类型,则直接比较的值。如果是引用数据类型,则比较的是内存地址

equals

  • 默认比较的是内存地址。可以通过重写,来比较里面的值

  • equals方法的底层是通过==来实现的

Main.java

public class Main {
    public static void main(String[] args) {
        String s = "你干嘛~";
        String s1 = "你干嘛~";
        // true 因为它俩都存放在堆内存中的常量池中,只有没有的时候才会创建一份,有的话就会指向那一份
        System.out.println(s == s1);
        System.out.println(s.equals(s1)); // true,字符串对象重写了equals方法,比较的是值了
    }
}

StringBuilder和StringBuffer的区别(了解)

  1. 在线程安全上 : –StringBuffer是旧版本就提供的,线程安全的。@since JDK1.0StringBuilderjdk1.5后产生,线程不安全的。@since 1.5

  2. 在执行效率上,StringBuilder > StringBuffer > String

  3. 源码体现:本质上都是在调用父类抽象类AbstractStringBuilder来干活,只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全问题

StringJoiner

  • JDK8开始才有的,跟StringBuilder一样,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的

  • 好处:不仅能够提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁(比如指定字符串间隔符的情况)

构造器 说明
public StringJoiner(间隔符号) 创建一个StringJoiner对象,指定拼接时的间隔符号
public StringJoiner(间隔符号,开始符号,结束符号) 创建一个StringJoiner对象,指定拼接时的间隔符号,开始符号,结束符号
方法名称 说明
public StringJoiner add(添加的内容) 添加数据,并返回对象本身(只能添加字符串)
public int length() 返回长度
public String toString() 返回一个字符串

Main.java

import java.util.StringJoiner;
​
public class Main {
    public static void main(String[] args) {
        int[] list = {1,2,3,4,5};
        System.out.println(getArrStr(list)); // [1,2,3,4,5]
    }
​
    public static String getArrStr(int[] arr) {
        StringJoiner sj = new StringJoiner(",","[","]");
        for (int i = 0; i < arr.length; i++) {
            sj.add(Integer.valueOf(arr[i]).toString());
        }
        return sj.toString();
    }
}

Math

Math没有构造方法类的成员都是静态的static修饰),通过类名就可以直接调用

常用方法

Main.java

public class Main {
    public static void main(String[] args) {
        double n = 9.99;
        // 1. abs() 获取绝对值
        System.out.println(Math.abs(n)); // 9.99
        // 2. ceil() 向上取整
        System.out.println(Math.ceil(n)); // 10.0
        // 3. floor() 向下取整
        System.out.println(Math.floor(n)); // 9.0
        // 4. round() 四舍五入
        System.out.println(Math.round(n)); // 10
        // 5. max() 最大值
        System.out.println(Math.max(10, 9)); // 10
        // 6. min() 最小值
        System.out.println(Math.min(9, 2)); // 2
        // 7. pow() 幂次方
        System.out.println(Math.pow(2, 3)); // 8.0
        // 8. random() [0,1) 0-1之间的随机数,但是不包含1
        System.out.println(Math.random());
    }
}

BigDecimal

  • 用于解决浮点型运算时,出现结果失真的问题

public class Main {
    public static void main(String[] args) {
        // 浮点数运算时,直接+ - * / 可能会出现运算结果失真
        System.out.println(0.1 + 0.2); // 0.30000000000000004
        System.out.println(1.0 - 0.32); // 0.6799999999999999
        System.out.println(1.015 * 100); // 101.49999999999999
        System.out.println(1.301 / 100); // 0.013009999999999999
    }
}
构造器,常见方法
构造器 说明
public BigDecimal(double val) 别用这种,这种还是浮点数,计算起来还是有精度问题 double转换为BugDecimal
public BigDecimal(String val) String转成BigDecimal
方法名 说明
public static BigDecimal valueOf(double val) 转换一个doubleBigDecimal
public BigDecimal add(BigDecimal b) 加法
public BigDecimal subtract(BigDecimal b) 减法
public BigDecimal multiply(BigDecimal b) 乘法
public BigDecimal divide(BigDecimal b) 除法
public BigDecimal divide(BigDecimal b,精确几位,舍入几位) 除法,可以控制精度到小数点几位(防止有除不尽的)
public double dobuleValue() BigDecimal转换为double

Main.java

import java.math.BigDecimal;
import java.math.RoundingMode;
​
public class Main {
    public static void main(String[] args) {
        // 创建BigDecimal对象的两种方式
        // 1. new BigDecimal(数字字符串)
        BigDecimal num1 = new BigDecimal(100.00 + "");
        // 2. BigDecimal.valueOf(double d) 推荐这种
        BigDecimal num2 = BigDecimal.valueOf(100.00);
        System.out.println(num1.equals(num2)); // true
        // 3. 加add
        System.out.println(num1.add(num2)); // 200.0
        // 4. 减subtract
        System.out.println(num1.subtract(num2)); // 0.0
        // 5. 乘multiply
        System.out.println(num1.multiply(num2)); // 10000.00
        // 6. 除divide
        System.out.println(num1.divide(num2)); // 1
        // 7. 除不尽的,保留多少位  divide(BigDecimal对象, 保留几位, 舍入几位)
        BigDecimal n1 = BigDecimal.valueOf(3.00);
        BigDecimal n2 = BigDecimal.valueOf(1.00);
        BigDecimal res = n2.divide(n1, 2, RoundingMode.HALF_UP);
        System.out.println(res); // 0.33
        // 8. 把BigDecimal类型的再转回double
        double v = res.doubleValue();
        System.out.println(v); // 0.33
    }
}

舍入方式解析

ROUND_HALF_UP 四舍五入,五入 如:4.4结果是4; 4.5结果是5

ROUND_HALF_DOWN 五舍六入,五不入 如:4.5结果是4; 4.6结果是5

ROUND_HALF_EVEN 公平舍入(银行常用) 比如:在5和6之间,靠近5就舍弃成5,靠近6就进位成6,如果是5.5,就找偶数,变成6

ROUND_UP 直接进位,不算0.1还是0.9,都进位

ROUND_DOWN 直接舍弃,不算0.1还是0.9,都舍弃

ROUND_CEILING(天花板) 向上取整,取实际值的大值 朝正无穷方向round 如果为正数,行为和round_up一样,如果为负数,行为和round_down一样

ROUND_FLOOR(地板) 向下取整,取实际值的小值 朝负无穷方向round 如果为正数,行为和round_down一样,如果为负数,行为和round_up一样

 版权声明:本文为CSDN博主「程序媛 泡泡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_43884234/article/details/116865138

System

System代表程序所在的系统,也是一个工具类

System类不能被实例化

System.in 标准输入流

System.out 标准输出流

System.err 标准错误输出流

常见方法

System.currentTimeMillis() 返回当前时间毫秒值

System.exit(0) 终止JVM(Java虚拟机),非0是终止异常

Runtime

  • 代表程序所在的运行环境

  • Runtime是一个单例类

常见方法

方法名 说明
public static Runtime getRuntime() 返回与当前Java应用程序关联的运行时对象
public void exit(int status) 终止当前运行的虚拟机
public int availableProcessors() 返回Java虚拟机可用的处理器数
public long totalMemory() 返回Java虚拟机中的内存总量
public long freeMemory() 返回Java虚拟机中的可用内存
public Process exec(String command) 启动某个程序,并返回代表该程序的对象

Main.java

import java.io.IOException;
​
public class Main {
    public static void main(String[] args) throws IOException {
        // 1. getRuntime() 返回与当前Java应用程序关联的运行时对象
        Runtime r = Runtime.getRuntime();
        // 2. exit(int status)  终止当前运行的虚拟机
        // 3. availableProcessors()  返回Java虚拟机可用的处理器数
        System.out.println(r.availableProcessors()); // 12(我的电脑是12个处理器)
        // 4. totalMemory()  返回Java虚拟机中的内存总量
        System.out.println(r.totalMemory() / 1024 / 1024 + "MB"); // 246MB
        // 5. freeMemory()  返回Java虚拟机中的可用内存
        System.out.println(r.freeMemory() / 1024 / 1024 + "MB"); // 242MB
        // 6. exec(String command)  启动某个程序,并返回代表该程序的对象
        // Process p = r.exec("C:\\Users\\Public\\Desktop\\腾讯QQ.exe");
        // 关闭程序
        // p.destory();
    }
}

Date

JDK 8中提供了java.time包用于日期和时间的处理。以前的API大多过时了,所以就不看了

LocalDate: 代表本地日期(年、月、日、星期)

LocalTime: 代表本地时间(时、分、秒、纳秒)

LocalDateTime: 代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)

它们都有一个now()方法:用于获取当时事件对应的改对象

方法名 实例
public static Xxxx now() LocalDate ld = LocalDate.now();
public static Xxxx now() LocalTime lt = LocalTime.now();
public static Xxxx now() LocalDateTime ldt = LocalDateTime.now();

LocalDate

  • 表示不带时区的日期对象,可以表示年月日信息

常用方法

Mian.java

import java.time.LocalDate;
​
public class Main {
    public static void main(String[] args) {
        // 1. 静态工厂方法,创建当前日期
        LocalDate now = LocalDate.now(); // 年月日
        System.out.println(now); // 2023-08-14
        // 2. of(int year,int month,int dayOfMonth) 静态工厂方法,根绝指定的年月日创建LocalDate对象
        LocalDate of = LocalDate.of(2023, 8, 8);
        System.out.println(of); // 2023-08-08
        // 3. getYear()  获取该日期的年份
        System.out.println(now.getYear()); // 2023
        // 4. getMonth()  获取该日期的月份
        System.out.println(now.getMonth()); // AUGUST (八月)
        // 5. getDayOfMonth()  获取该日期在当前月份的第几天,也就是日
        System.out.println(now.getDayOfMonth()); // 14
        // 6. getDayOfYear()  获取一年中的第几天
        System.out.println(now.getDayOfYear()); // 226
        // 7. getDayOfWeek()  在这周的第几天,也就是星期几
        System.out.println(now.getDayOfWeek()); // MONDAY(周一)
        // 8. 把某个信息加多少,并返回新的日期对象 plusYears plusMonths plusDays plusWeeks
        System.out.println(now.plusDays(3)); // 2023-08-17
        // 9. 把某个信息减多少,并返回新的日期对象 minusYears minusMonths minusDays minusWeeks
        System.out.println(now.minusDays(3)); // 2023-08-11
        // 10. isEqual(LocalDate other)  判断该日期是否与另一个日期相等
        System.out.println(now.isEqual(of)); // false
        // 11. isLeapYear()  判断该日期所在的年份是否为闰年
        System.out.println(now.isLeapYear()); // false
        // 12. 直接修改某个信息:withYear  withMonth  withDayOfMonth  withDayOfYear
        System.out.println(now.withYear(2030)); // 2030-07-24
        // 13. 判断2哥日期对象是否相等,在前还是在后  equals  isBefore  isAfter
        System.out.println(now.equals(of)); // false
        System.out.println(now.isBefore(of)); // false
        System.out.println(now.isAfter(of)); // true 8月14确实在8月8后面
    }
}

LocalTime

  • 表示不带时区的时间对象,可以表示时分秒毫秒纳秒信息

常用方法

Mian.java

import java.time.LocalTime;
​
public class Main {
    public static void main(String[] args) {
        // 1. 静态工厂方法,创建当前时间,含毫秒数
        LocalTime now = LocalTime.now();
        System.out.println(now); // 13:23:13.416510800
        // 2. of(int hour,int minute,int second,int nanoOfSecond) 静态工厂方法,根据指定的时分秒纳秒创建LocalTime对象
        LocalTime of = LocalTime.of(8,8,8,8);
        System.out.println(of); // 08:08:08.000000008
        // 3. getHour()  获取该时间的小时数
        System.out.println(now.getHour()); // 13
        // 4. getMinute()  获取该时间的分钟数
        System.out.println(now.getMinute()); // 23
        // 5. getSecond()  获取该时间的秒数
        System.out.println(now.getSecond()); // 13
        // 6. getNano()  获取该事件的纳秒
        System.out.println(now.getNano()); // 纳秒
        // 其他方法同上,大差不差
    }
}

LocalDateTime

  • 表示不带时区的日期时间对象,包含年月日时分秒毫秒纳秒信息

常用方法

Mian.java

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
​
public class Main {
    public static void main(String[] args) {
        // 1. 静态工厂方法,创建当前日期时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now); // 2023-07-24T11:21:02.640402500
        // 2. of(int year,int month,int dayOfMonth,int hour,int minute) 静态工厂方法,根据指定的年月日时分创建LocalDateTime对象
        LocalDateTime of = LocalDateTime.of(2023, 8, 8,8,8);
        System.out.println(of); // 2023-08-08T08:08
        // 3. getYear()  获取该日期的年份
        System.out.println(now.getYear()); // 2023
        // 4. getMonth()  获取该日期的月份
        System.out.println(now.getMonth()); // JULY 七月
        // 5. getDayOfMonth()  获取该日期在当前月份的第几天,也就是日
        System.out.println(now.getDayOfMonth()); // 24
        // 6. getHour()  获取该日期时间的小时数
        System.out.println(now.getHour()); // 11
        // 7. getMinute()  获取该日期时间的分钟数
        System.out.println(now.getMinute()); // 21
        // 8. plusDays(long daysToAdd)  将指定天数添加到当前日期时间并返回新的日期时间对象
        System.out.println(now.plusDays(3)); // 2023-07-27T11:21:02.640402500
        // 9. minusDays(long daysToSubtract)  从当前日期时间中减去指定的天数并返回新的日期时间对象
        System.out.println(now.minusDays(3)); // 2023-07-21T11:21:02.640402500
        // 10. isEqual(LocalDateTime other)  判断该日期时间是否与另一个日期时间相等
        System.out.println(now.isEqual(of)); // false
        // 11. isAfter(LocalDateTime other)  判断该日期时间是否在另一个日期时间之后
        System.out.println(now.isAfter(of)); // false
        // 获取当天0点
        System.out.println(LocalTime.MIN); // 00:00
        // 指定的日期表示为当天的最早时间
        LocalDate now1 = LocalDate.now();
        LocalDateTime of1 = LocalDateTime.of(now1, LocalTime.MIN);
        System.out.println(of1); // 2023-07-24T00:00
        // 将LocalDateTime对象转换为LocalData或者LocalTime
        LocalDate localDate = now.toLocalDate();
        System.out.println(localDate);
        LocalTime localTime = now.toLocalTime();
        System.out.println(localTime);
        // 当然,也可以把localDate和localTime拼成LocalDateTime对象
        LocalDateTime of2 = LocalDateTime.of(localDate, localTime);
        System.out.println(of2);
    }
}

小汇总

共有的方法

LocalDate的常用API(都是处理年、月、日、星期相关的)

LocalTime的常用API(都是处理时、分、秒、纳秒相关的)

LocalDateTime常见的API

ZoneID 

  • 表示时区Id

常见方法

方法名 说明
public static ZoneId systemDefault() 获取系统默认的时区
public static Set<String> getAvailableZoneIds() 获取Java支持的全部时区Id
public static ZoneId of(String zoneId) 把某个时区id封装成ZoneId对象

Main.java

import java.time.ZoneId;
import java.util.Set;
​
public class Main {
    public static void main(String[] args) {
        // 1. 获取系统默认的时区 ZoneId.systemDefault()
        ZoneId zi = ZoneId.systemDefault();
        System.out.println(zi); // Asia/Shanghai
        // 2. zoneId对象.getId() 获取时区
        String id = zi.getId();
        System.out.println(id); // Asia/Shanghai
        // 3. 获取Java支持的全部时区Id,如果忘了时区怎么写的,可以用这个。ZoneId.getAvailableZoneIds();
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        System.out.println(availableZoneIds); // [全部时区...]
        // 4. 自己传入一个时区,并生成一个这个时区的ZoneId对象
        ZoneId New_York = ZoneId.of("America/New_York"); // 美国纽约的时区Id
        System.out.println(New_York); // America/New_York
    }
}

ZonedDateTime

  • 表示带时区的日期时间对象,包含年月日时分秒毫秒纳秒和对应时区信息。可以看成LocalDateTime的增强版(LocalDateTime的方法它都能用的)

常见方法

方法名 说明
public static ZonedDateTime now(ZoneId zone) 获取某个时区的ZonedDateTime对象
public static ZonedDateTime now() 获取系统默认时区的ZonedDateTime对象

Main.java

import java.time.Clock;
import java.time.ZoneId;
import java.time.ZonedDateTime;
​
public class Main {
    public static void main(String[] args) {
        // 自己传入一个时区,并生成一个这个时区的ZoneId对象
        ZoneId New_York = ZoneId.of("America/New_York"); // 美国纽约的时区Id
        System.out.println(New_York); // America/New_York
        // 查看这个时区的时间
        ZonedDateTime now = ZonedDateTime.now(New_York);
        System.out.println(now); // 2023-08-14T02:37:17.520633300-04:00[America/New_York]
        // 查看当前系统默认时间
        ZonedDateTime now1 = ZonedDateTime.now();
        System.out.println(now1); // 2023-08-14T14:37:17.523623500+08:00[Asia/Shanghai]
        // 世界标准时间
        ZonedDateTime now2 = ZonedDateTime.now(Clock.systemUTC());
        System.out.println(now2); // 2023-08-14T06:37:17.523623500Z
    }
}

Instant

  • 时间线上的某个时刻(时间戳)

  • 通过获取Instant的对象可以拿到此刻的时间,该事件由两部分组成

    • 1970-01-01 00:00:00开始走到刺客的总秒数 + 不到1s的纳米数

    • 1s = 1000毫秒 1毫秒 = 1000微秒 1微秒 = 1000纳秒

常见方法
方法名 说明
public static Instant now() 获取当前时间的Instant对象(标准时间)
public long getEpochSecond() 获取从1970-01-01 00:00开始记录的秒数
public int getNano() 从时间线开始,获取从第二个开始的纳秒数
plusMillis plusSeconds plusNanos 增加时间系列的方法
minusMillis minusSeconds minusNanos 减少时间系列的方法
equals isBefore isAfter 判断系列的方法(是否相同,在后面,在前面)

Main.java

import java.time.Instant;
​
public class Main {
    public static void main(String[] args) {
        // 1. 获取当前时间的Instant对象(标准时间)
        Instant now = Instant.now();
        System.out.println(now); // 2023-08-15T01:26:13.432905300Z
        // 2. 获取时间戳
        long epochSecond = now.getEpochSecond();
        System.out.println(epochSecond); // 1692062773
        // 3. 获取纳秒数
        int nano = now.getNano();
        System.out.println(nano); // 283758800
        // 4. plusMillis增加毫秒数 plusSeconds增加秒数 plusNanos增加纳秒数
        Instant instant = now.plusMillis(10);
        Instant instant1 = now.plusSeconds(10);
        Instant instant2 = now.plusNanos(10);
        // 5. minusMillis减少毫秒数 minusSeconds减少秒数 minusNanos减少纳秒数
        Instant instant3 = now.minusMillis(10);
        Instant instant4 = now.minusSeconds(10);
        Instant instant5 = now.minusNanos(10);
    }
}
作用
  • 做代码性能分析,或者记录用户的操作时间点

import java.time.Instant;
​
public class Main {
    public static void main(String[] args) {
        Instant start = Instant.now();
        // 代码执行...
        Instant end = Instant.now();
    }
}

DateTimeFormatter

  • 用于日期和时间格式化,可以将日期时间对象转换为指定格式的字符串,或者将字符串转换为日期时间对象

DateTimeFormatter提供的格式化,解析时间的方法

方法名 说明
public static DateTimeFormatter ofPattern(时间格式) 获取格式化对象
public String format(时间对象) 格式化时间

LocalDateTime提供的格式化,解析时间的方法

方法名 说明
public String format(DateTimeFormatter formatter) 格式化时间
public static LocalDateTime parse(CharSwquence text,DateTimeFormatter formatter) 解析时间(反向解析:时间字符串 -> 时间)

案例:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
​
public class Main {
    public static void main(String[] args) {
        // 时间格式化对象
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        // 对时间进行格式化
        LocalDateTime now = LocalDateTime.now();
        String format = df.format(now);
        System.out.println(format); // 2023年08月16日 09:26:32
        // 格式化时间其实还有另外一种方法 利用LocalDateTime的format方法
        String format1 = now.format(df);
        System.out.println(format1); // 2023年08月16日 09:26:32
        // 解析时间,解析时间一般使用LocalDateTime提供的解析方法来解析
        String dateStr = "2023年08月08日 12:00:00";
        LocalDateTime parse = LocalDateTime.parse(dateStr, df);
        System.out.println(parse); // 2023-08-08T12:00
    }
}

Period

  • 用于计算两个LocalDate对象相差的年数,月数,天数

方法名 说明
public static between(LocalDate start,LocalDate end) 传入2个日期对象,得到一个Period对象
public int getYears() 计算隔几年,并返回
public int getMonths() 计算隔几月,并返回
public int getDays() 计算隔多少天,并返回

Main.java

import java.time.LocalDate;
import java.time.Period;

public class Main {
    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2023, 8, 16);
        LocalDate end = LocalDate.of(2023, 10, 1);
        // 创建一个 Period 对象 可以计算两个日期之间间隔的年数,月数,天数
        Period between = Period.between(start, end);
        // 间隔的年数
        int years = between.getYears();
        System.out.println(years); // 0
        // 间隔的月数
        int months = between.getMonths();
        System.out.println(months); // 1
        // 间隔的天数
        int days = between.getDays();
        System.out.println(days); // 15
    }
}

Duration

  • 表示两个时间点之间的时间长度,可以计算出两个时间点之间相差的小时数、分钟数、秒数,纳秒数等信息。支持LocalTimeLocalDateTimeInstant等时间

方法名 说明
public static Duration between(开始时间对象,截止时间对象) 传入2个时间对象,得到Duration对象
public long toDays() 计算隔多少天,并返回
public long toHours() 计算隔多少小时,并返回
public long toMinutes() 计算隔多少分,并返回
public long toSeconds() 计算隔多少秒,并返回
public long toMillis() 计算隔多少毫秒,并返回
public long toNanos() 计算隔多少纳秒,并返回

Main.java

import java.time.Duration;
import java.time.LocalDateTime;

public class Main {
    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2023, 8, 16,12,0,0);
        LocalDateTime end = LocalDateTime.of(2023, 10, 1,12,0,0);
        // 创建一个 Duration 对象 可以计算两个日期之间间隔的天数,小时数,分钟数,秒数,毫秒数,纳秒数
        Duration between = Duration.between(start, end);
        // 间隔的天数
        long days = between.toDays();
        System.out.println(days); // 46
        // 间隔的小时数
        long hours = between.toHours();
        System.out.println(hours); //
        // 间隔的分钟数
        long minutes = between.toMinutes();
        System.out.println(minutes); //
        // 间隔的秒数
        long seconds = between.toSeconds();
        System.out.println(seconds); //
        // 间隔的毫秒数
        long millis = between.toMillis();
        System.out.println(millis); //
        // 间隔的纳秒数
        long nanos = between.toNanos();
        System.out.println(nanos); //
    }
}

Array

用来操作数组的一个工具类

常用方法

Main.java

import java.util.Arrays;
import java.util.function.IntToDoubleFunction;
​
public class Main {
    public static void main(String[] args) {
        int[] list = { 1 ,2 ,3 ,4 , 5 };
        // 1. public static String toString(类型[] arr) 返回数组的内容
        String s = Arrays.toString(list);
        System.out.println(s); // "[1, 2, 3, 4, 5]"
        // 2. public static int[] copyOfRange(类型[] arr,起始索引,结束索引) 拷贝数组(指定范围,包前不包后)
        int[] ints = Arrays.copyOfRange(list, 0, 2);
        // 3. public static copyOf(类型[] arr,int newLength) 拷贝数组,可以指定新数组的长度
        int[] ints1 = Arrays.copyOf(list, 3);
        for (int i = 0; i < ints1.length; i++) {
            System.out.println(ints1[i]); // 1 2 3
        }
        // 4. public static setAll(double[] array,intToDoubleFunction generator) 把数组中的元数据改为新数据又存进去
        double[] price = { 9.9, 88, 70, 100, 60 }; // 价格打8折
        Arrays.setAll(price, new IntToDoubleFunction() {
            @Override
            public double applyAsDouble(int value) { // value: 0 1 2
                return price[value] * 0.8;
            }
        });
        String s1 = Arrays.toString(price);
        System.out.println(s1); // [7.920000000000001, 70.4, 56.0, 80.0, 48.0]
        // 5. public static void sort(类型[] arr) 对数组进行排序(默认是升序排序)
         Arrays.sort(list);
        for (int i = 0; i < list.length; i++) {
            System.out.println(list[i]);
        }
    }
}

对象数组排序

数组中存储的是对象,该怎么排序呢

  1. 让该对象的类实现Comparable(比较规则)接口,然后重写compareTo方法,自己来顶置比较规则

  2. 使用下面的这个sort方法,创建Comparator比较器接口的匿名内部类对象,然后自己指定比较规则

public static <T> void sort(T[] arr,Comparator<? super T>c) 对数组进行排序(支持自定义排序规则)

第一种

Student.java

package Demo;
// 1. 该对象的类实现Comparable接口
public class Student implements Comparable<Student> {
    private String name;
    private int 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;
    }
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 2. 重写compareTo方法,自己定义比较规则   拿 this 和 o 进行对比
    // 规则有一些约束:认为左边对象 > 右边对象 就返回正整数
    //              认为左边对象 < 右边对象 就返回负整数
    //              认为左边对象 = 右边对象 就返回0
    @Override
    public int compareTo(Student o) {
        // 按照年龄排序
//        if(this.age > o.age) {
//            return 1;
//        } else if(this.age < o.age) {
//            return -1;
//        }
//        return 0;
        // 可以简写上面的
        return this.age - o.age;
    }
}

Main.java

import Demo.Student;
import java.util.Arrays;
​
public class Main {
    public static void main(String[] args) {
        Student[] list = new Student[3];
        list[0] = new Student("张三",12);
        list[1] = new Student("李四",13);
        list[2] = new Student("王五",14);
        // 3. 调用重写后的排序方法
        Arrays.sort(list);
        System.out.println(Arrays.toString(list)); //
    }
}

第二种

Student.java

package Demo;
​
public class Student {
    private String name;
    private int 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;
    }
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Main.java

import Demo.Student;
import java.util.Arrays;
import java.util.Comparator;
​
public class Main {
    public static void main(String[] args) {
        Student[] list = new Student[3];
        list[0] = new Student("张三",12);
        list[1] = new Student("李四",13);
        list[2] = new Student("王五",14);
        // 3. 调用重写后的排序方法
        Arrays.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // 第一种比较方式
                // if (o1.getAge() > o2.getAge()) {
                //     return 1;
                // } else if(o1.getAge() < o2.getAge()) {
                //     return -1;
                // }
                // return 0;
                // 第二种比较方式
                // return o1.getAge() - o2.getAge();
                // 第三种比较方式
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        });
        System.out.println(Arrays.toString(list)); // "[Student{name='张三', age=12}, Student{name='李四', age=13}, Student{name='王五', age=14}]"
    }
}

Lambda表达式

Lambda表达式

  • Lambda表达式是JDK8开始新增的一种语法形式;作用:用于简化匿名内部类的代码写法

格式:

(被重写方法的形参列表篇) -> {

        被重写方法的方法体代码;

}

注意:Lambda只能简化函数式接口的匿名内部类

函数式接口是什么?

  • 有且只有一个抽象方法接口

Main.java

import Demo.IAnimal;
​
public class Main {
    public static void main(String[] args) {
        // 正常的匿名内部类写法(实现接口)
        IAnimal a = new IAnimal() {
            @Override
            public void print() {
                System.out.println("匿名内部普通写法");
            }
        };
        a.print(); // 匿名内部普通写法
​
        // 使用Lambda表达式简化匿名内部类
        IAnimal a2 = () -> {
            System.out.println("Lambda表达式简化匿名内部类写法");
        };
        a2.print(); // Lambda表达式简化匿名内部类写法
    }
}

注意:

  • 以后见到的大部分函数式接口,上面都可能会有一个@FunctionallInterface的注解,有该注解的接口就必定是函数式接口

Lambda简化规则

  • 参数类型可以省略不写

  • 如果只有一个参数,参数类型可以省略,同时()也可以省略

  • 如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写

Main.java

import Demo.IAnimal;
​
public class Main {
    public static void main(String[] args) {
        // 1. 参数类型可以不写
        IAnimal a2 = (content) -> {
            return content;
        };
        a2.print("参数1");
        // 2. 如果只有一个参数,参数类型可以省略,同时()也可以省略
        IAnimal a3 = content -> {
            return content;
        };
        // 3.如果Lambda表达式中的方法体代码只有一行,可以省略大括号不写,同时要省略分号!
        // 此时,如果这行代码是return语句,也必须去掉return不写
        IAnimal a4 = content -> content;
    }
}

方法引用::

  • 作用:进一步简化Lambda表达式

静态方法的引用
  • 类名::静态方法

使用场景:

  • 如果某个Lambda表达式里只有调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用

Student.java

package Demo;
​
public class Student {
    private String name;
    private int 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;
    }
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

CompareByData.java

package Demo;
​
public class CompareByData {
    // 比较前后的值
    public static int CompareByAge(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}

Main.java

import Demo.CompareByData;
import Demo.Student;
import java.util.Arrays;
import java.util.Comparator;
​
public class Main {
    public static void main(String[] args) {
        Student[] list = new Student[3];
        list[0] = new Student("张三",12);
        list[1] = new Student("李四",19);
        list[2] = new Student("王五",14);
        // 对象按年龄排序
        // Arrays.sort(list, new Comparator<Student>() {
        //     @Override
        //     public int compare(Student o1, Student o2) {
        //         return o1.getAge() - o2.getAge();
        //     }
        // });
        // Lambda表达式简化匿名内部类写法
        // Arrays.sort(list, (Student o1, Student o2) -> o1.getAge() - o2.getAge());
        // 在编辑器中可以看到,list后面是灰色的,说明可以接着简化,正好它满足 静态方法的引用 的条件
        // Arrays.sort(list, (Student o1, Student o2) -> CompareByData.CompareByAge(o1, o2));
        // 使用静态方法的引用(上面这个是为了方便理解写的,不是静态方法引入)  类名::静态方法
        Arrays.sort(list,CompareByData::CompareByAge); // 这才是静态方法引入
        System.out.println(Arrays.toString(list));
    }
}
实例方法的引用
  • 对象名::实例方法

使用场景:

  • 如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用

  • 跟上面的静态方法一样呀,只是这个调用的实例方法(对象方法),上面调用的静态方法

Student.java

package Demo;
​
public class Student {
    private String name;
    private int 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;
    }
​
    public Student() {
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

CompareByData.java

package Demo;
​
public class CompareByData {
    public int CompareByAge(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}

Main.java

import Demo.CompareByData;
import Demo.Student;
import java.util.Arrays;
​
public class Main {
    public static void main(String[] args) {
        Student[] list = new Student[3];
        list[0] = new Student("张三",12);
        list[1] = new Student("李四",19);
        list[2] = new Student("王五",14);
        // 对象按年龄排序
        // Arrays.sort(list, new Comparator<Student>() {
        //     @Override
        //     public int compare(Student o1, Student o2) {
        //         return o1.getAge() - o2.getAge();
        //     }
        // });
        // Lambda表达式简化匿名内部类写法
        // Arrays.sort(list, (Student o1, Student o2) -> o1.getAge() - o2.getAge());
        // 在编辑器中可以看到,list后面是灰色的,说明可以接着简化,正好它满足 实例方法的引用 的条件
        CompareByData compare = new CompareByData();
        // Arrays.sort(list, (Student o1, Student o2) -> compare.CompareByAge(o1, o2));
        // 使用实例方法的引用(上面这个是为了方便理解写的,不是实例方法引入)  对象名::实例方法
        Arrays.sort(list,compare::CompareByAge); // 这才是实例方法引入
        System.out.println(Arrays.toString(list));
    }
}
特定类型方法的引用
  • 类型::方法

使用场景

  • 如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用

Main.java

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] names = { "Andy", "join", "angela", "Kiki" };
        Arrays.sort(names);
        // 会发现,它是按照首字符大小写进行排序的
        // System.out.println(Arrays.toString(names)); // [Andy, Kiki, angela, join]
        // 我们想要让它忽略大小写排序
        // Arrays.sort(names, new Comparator<String>() {
        //     @Override
        //     public int compare(String o1, String o2) {
        //         return o1.compareToIgnoreCase(o2);
        //     }
        // });
        // System.out.println(Arrays.toString(names)); // [Andy, angela, join, Kiki]
        // 对匿名内部类进行简化,使用 Lambda 表达式
        // Arrays.sort(names, (String o1, String o2) -> o1.compareToIgnoreCase(o2));
        // System.out.println(Arrays.toString(names)); // [Andy, angela, join, Kiki]
        // 满足特定类型方法的引用
        Arrays.sort(names, String::compareToIgnoreCase);
        System.out.println(Arrays.toString(names)); // [Andy, angela, join, Kiki]
    }
}
构造器引用
  • 类名::new

使用场景:

  • 如果某个Lambda表达式里只是在创建对象,并且前后参数情况一样,就可以使用构造器引用

下面的例子没啥意义,就是展示下用法

Car.java

package Demo;

public class Car {
    private String name;
    private double price;

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Car() {
    }

    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }

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

Main.java

import Demo.Car;

public class Main {
    public static void main(String[] args) {
        // 匿名内部类简化步骤
        // CreateCar cc = new CreateCar() {
        //     @Override
        //     public Car create(String name, double price) {
        //         return new Car("宝马",9999.00);
        //     }
        // };
        // Lambda表达式简化
        // CreateCar cc = (String name, double price) -> new Car("宝马",9999.00);
        // 构造器引用简化Lambda
        CreateCar cc = Car::new;
        Car c = cc.create("红旗",9999.00);
        System.out.println(c.toString()); // Car{name='红旗', price=9999.0}
    }

    interface CreateCar {
        Car create(String name, double price);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_52845451/article/details/132330818
今日推荐