目录
StringBuilder和StringBuffer的区别(了解)
是跟的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
}
}
一般子类会进行重写,但是重写的话,hashCode
与equals
都要进行重写,要不都不进行重写
注意:如果一个对象,调用
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
创建方式
直接使用双引号 “ ...... ” 这种使用最多
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的区别(了解)
在线程安全上 : –
StringBuffer
是旧版本就提供的,线程安全的。@since JDK1.0
–StringBuilder
是jdk1.5
后产生,线程不安全的。@since 1.5
在执行效率上,
StringBuilder
>StringBuffer
>String
源码体现:本质上都是在调用父类抽象类
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) | 转换一个double 成BigDecimal |
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
表示两个时间点之间的时间长度,可以计算出两个时间点之间相差的小时数、分钟数、秒数,纳秒数等信息。支持
LocalTime
,LocalDateTime
,Instant
等时间
方法名 | 说明 |
---|---|
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]);
}
}
}
对象数组排序
数组中存储的是对象,该怎么排序呢
让该对象的类实现
Comparable
(比较规则)接口,然后重写compareTo
方法,自己来顶置比较规则使用下面的这个
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);
}
}
原文链接:https://blog.csdn.net/qq_52845451/article/details/132330818
此处评论已关闭