3-1、2、3
使用BigDecimal出现精读和除法除不尽问题
package com.imooc.java.escape;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* <h1>数值计算和时间计算</h1>
* */
@SuppressWarnings("all")
public class NumberAndTime {
/**
* <h2>scale 需要与小数位匹配</h2>
* */
private static void scaleProblem() {
BigDecimal decimal = new BigDecimal("12.222");
// BigDecimal result = decimal.setScale(12);
// System.out.println(result);
BigDecimal result = decimal.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(result);
}
/**
* <h2>BigDecimal 做除法时出现除不尽的情况</h2>
* */
private static void divideProblem() {
// System.out.println(new BigDecimal(30).divide(new BigDecimal(7)));
System.out.println(
new BigDecimal(30).divide(new BigDecimal(7), 2,
BigDecimal.ROUND_HALF_UP)
);
}
/**
* <h2>精度问题导致比较结果和预期的不一致</h2>
* */
private static void equalProblem() {
BigDecimal bd1 = new BigDecimal("0");
BigDecimal bd2 = new BigDecimal("0.0");
System.out.println(bd1.equals(bd2));
System.out.println(bd1.compareTo(bd2) == 0);
}
/**
* <h2>SimpleDateFormat 可以解析大于/等于它定义的时间精度</h2>
* */
private static void formatPrecision() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String time_x = "2020-03-01 10:00:00";
String time = "2020-03";
System.out.println(sdf.parse(time_x));
System.out.println(sdf.parse(time));
}
/**
* <h2>SimplleDateFormat 存在线程安全问题</h2>
* */
private static void threadSafety() {
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(
10, 100, 1, TimeUnit.MINUTES,
new LinkedBlockingDeque<>(1000)
);
while (true) {
threadPoolExecutor.execute(() -> {
String dateString = "2020-03-01 10:00:00";
try {
Date parseDate = sdf.parse(dateString);
String dateString2 = sdf.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException ex) {
ex.printStackTrace();
}
});
}
}
public static void main(String[] args) throws Exception {
// scaleProblem();
// divideProblem();
// equalProblem();
// formatPrecision();
threadSafety();
}
}
3-4、5
增强for循环的使用
3-6
判定等于的逻辑需要重写equals和hashcode
package com.imooc.java.escape;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <h1>如果不好好判等, 集合存储就会乱套</h1>
* */
public class EqualOrElse {
public static class User implements Comparable<User> {
private String name;
private Integer age;
public User() {}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof User) {
User user = (User) obj;
return this.name.equals(user.name) && this.age == user.age;
}
return false;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + age;
return result;
}
@Override
public int compareTo(User o) {
return (this.age - o.age) + this.name.compareTo(o.name);
}
}
/**
* <h2>实现/不实现 equals 方法和 hashcode 对于判等的影响</h2>
* */
private static void equalsAndHashcode() {
User user1 = new User("qinyi", 19);
User user2 = new User("qinyi", 19);
// System.out.println(user1.equals(user2));
Set<User> userSet = new HashSet<>();
userSet.add(user1);
userSet.add(user2);
Map<User, Integer> userIntegerMap = new HashMap<>();
userIntegerMap.put(user1, 0);
userIntegerMap.put(user2, 0);
System.out.println(userSet.size());
System.out.println(userIntegerMap.size());
}
/**
* <h2>集合元素索引与 equals 方法相关</h2>
* */
private static void compareToAndEquals() {
List<User> users = new ArrayList<>();
users.add(new User("qinyi", 10));
users.add(new User("qinyi", 20));
User user = new User("qinyi", 20);
int index1 = users.indexOf(user);
int index2 = Collections.binarySearch(users, user);
System.out.println(index1);
System.out.println(index2);
}
public static void main(String[] args) {
// equalsAndHashcode();
compareToAndEquals();
}
}
indexOf() 找到元素对应的索引;
compareTo和equals都要实现逻辑上的判等,需同步
3-7
1、单字母驼峰 lombok 会解析为全小写
需要极力避免
2、@Data 注解在子类默认不比较父类里的属性,导致equals会不符合预期
注意:
如果父类没有@Data 则没有重写 equals 方法(比如父类只用了 @Getter和@Setter),会导致继承的子类出现两种情况
1、@EqualsAndHashCode(callSuper = false) 时,也是默认不写,两个 “形态上” 相等的数据,会被认为相等
,应为不会比较父类,父类没重写也没关系
2、@EqualsAndHashCode(callSuper = true) 时,代表会比较父类,因为父类没重写,会导致认为不相等
,很有可能违背业务初衷(一般业务里,从数据库读到两条完全相等的数据会被认为需要去重,使用流的distinct在这里会失效。)
结论:父类应打上@Data注解。
3-8:避免抽象类和接口选择失误
共同有状态的属性 用 抽象类
:起床、上下班
特有的独立的属性用 接口
:程序员,部分程序员
不能new 抽象类和接口,此是语法错误
3-9:java8新增的 默认方法和静态方法
默认方法是为了配合jdk函数是接口实现的更有拓展性
3-10:lambda表达式
看方法的返回值:返回stream的大多是 中间操作
package com.imooc.java.escape.function_interface_lambda;
import java.util.HashMap;
import java.util.Map;
/**
* <h1>函数式接口的使用</h1>
* */
@SuppressWarnings("all")
public class Main {
private static final Map<Long, Worker> id2WorkerMap = new HashMap<>();
static {
id2WorkerMap.put(1L, new Worker(1L, "qinyi", 19));
}
public static void main(String[] args) {
// IFindWorker findWorker = id -> id2WorkerMap.get(id);
IFindWorker findWorker = id2WorkerMap::get;
System.out.println(findWorker.findWorkerById(1L));
}
}
package com.imooc.java.escape.function_interface_lambda;
import java.util.HashMap;
import java.util.Map;
/**
* <h1>函数式接口的使用</h1>
* */
@SuppressWarnings("all")
public class Main {
private static final Map<Long, Worker> id2WorkerMap = new HashMap<>();
static {
id2WorkerMap.put(1L, new Worker(1L, "qinyi", 19));
}
public static void main(String[] args) {
// IFindWorker findWorker = id -> id2WorkerMap.get(id);
IFindWorker findWorker = id2WorkerMap::get;
System.out.println(findWorker.findWorkerById(1L));
}
}
package com.imooc.java.escape.function_interface_lambda;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* <h1>Lambda 表达式的使用</h1>
* */
public class StudyLambda {
/**
* <h2>Java 1.8 之前创建线程</h2>
* */
private static void baseUse() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Anonymous Class Thread run()");
}
}).start();
}
/**
* <h2>Java8 创建线程</h2>
* */
private static void easyUseLambda() {
new Thread(() -> System.out.println("Anonymous Class Thread run()")).start();
}
/**
* <h2>按照字符串长度进行排序</h2>
* */
private static void myCompare() {
// java8 之前
List<String> list = Arrays.asList("z", "y", "x", "a");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
if (s1 == null)
return -1;
if (s2 == null)
return 1;
return s1.length() - s2.length();
}
});
// java8 使用 lambda 表达式去实现
Collections.sort(list, (s1, s2) -> {
if (s1 == null)
return -1;
if (s2 == null)
return 1;
return s1.length() - s2.length();
});
}
/**
* <h2>要理解 stream 的中间操作和结束操作</h2>
* */
private static void howToUseLambda() {
List<String> names = Arrays.asList("qinyi", "imooc");
List<String> newNames =
names.stream().filter(n -> n.startsWith("q"))
.map(n -> n.toUpperCase())
.collect(Collectors.toList());
System.out.println(newNames);
}
/**
* <h2>Stream 和 lambda 可能导致计算低效</h2>
* */
private static void badUseLambda() {
List<String> names = Arrays.asList("qinyi", "imooc");
int longestNameSize =
names.stream()
.filter(s -> s.startsWith("q"))
.mapToInt(String::length)
.max()
.getAsInt();
int longest = 0;
for (String str : names) {
if (str.startsWith("q")) {
int len = str.length();
longest = Math.max(len, longest);
}
}
System.out.println(longest);
}
}
package com.imooc.java.escape.function_interface_lambda;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <h1>Java Object</h1>
* */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Worker {
private Long id;
private String name;
private Integer age;
}
自定义的带参数带返回值的函数式接口
package com.imooc.java.escape.function_interface_lambda;
import java.util.HashMap;
import java.util.Map;
/**
* <h1>函数式接口的使用</h1>
* */
@SuppressWarnings("all")
public class Main {
private static final Map<Long, Worker> id2WorkerMap = new HashMap<>();
static {
id2WorkerMap.put(1L, new Worker(1L, "qinyi", 19));
id2WorkerMap.put(2L, new Worker(2L, "qi222nyi", 19));
}
// 使用自定义的函数式接口作为方法参数
private static Worker doSomething(IFindWorker inter, Long id) {
return inter.findWorkerById(id); // 调用自定义的函数式接口方法
}
public static void main(String[] args) {
// IFindWorker findWorker = id -> id2WorkerMap.get(id);
// IFindWorker findWorker = id2WorkerMap::get;
System.out.println("worker = " + doSomething(
(id) -> id2WorkerMap.get(id),2L
));
// System.out.println(findWorker.findWorkerById(1L));
}
}
接口定义
==4-1、2:序列化相关 ==
1、当父类没序列化时,如果父类没有提供无参构造函数则报错;如果提供,则可以
2、引用对象和自身都必须实现序列化才行
3、会影响。java序列化算法 不会重复多次序列化
4-3:泛型
类型擦除
java的泛型是伪泛型
在字节码会被擦除
通过反射
在运行时 将不是 int型的数据插入到 list
package com.imooc.java.escape;
import java.util.ArrayList;
import java.util.List;
/**
* <h1>理解泛型</h1>
* */
@SuppressWarnings("all")
public class Genericity {
/**
* <h2>简单使用泛型</h2>
* */
private static void easyUse() throws Exception {
List<String> left = new ArrayList<>();
List<Integer> right = new ArrayList<>();
// System.out.println(left.getClass());
// System.out.println(left.getClass() == right.getClass());
// if (left instanceof ArrayList<Double>) {}
// if (left instanceof ArrayList) {
//
// }
//
// if (left instanceof ArrayList<?>) {}
List<Integer> list = new ArrayList<>();
list.add(1);
list.getClass().getMethod("add", Object.class).invoke(list, "abcd");
list.getClass().getMethod("add", Object.class).invoke(list, 1.2);
for (int i = 0; i != list.size(); ++i) {
System.out.println(list.get(i));
}
}
/**
* <h2>泛型是先检查再编译</h2>
* */
private static void checkAndCompile() {
ArrayList<String> list = new ArrayList<>();
list.add("1234");
// list.add(123);
}
/**
* <h2>泛型不支持继承</h2>
* */
private static void genericityCanNotExtend() {
// 第一类错误
// ArrayList<String> first = new ArrayList<Object>();
//
ArrayList<Object> list1 = new ArrayList<>();
// list1.add(new Object());
// ArrayList<String> list2 = list1;
// 第二类错误
// ArrayList<Object> second = new ArrayList<String>();
//
// ArrayList<String> list1 = new ArrayList<>();
// list1.add(new String());
// ArrayList<Object> list2 = list1;
}
/**
* <h2>泛型类型变量不能是基本数据类型</h2>
* */
private static void baseTypeCanNotUseGenericity() {
// List<int> invalid = new ArrayList<>();
}
/**
* <h2>泛型的类型参数只能是类类型, 不能是简单类型</h2>
* */
private static <T> void doSomething(T... values) {
for (T value : values) {
System.out.println(value);
}
}
public static void main(String[] args) throws Exception {
// easyUse();
Integer[] ints1 = new Integer[]{1, 2, 3};
int[] ints2 = new int[]{1, 2, 3};
doSomething(ints1);
System.out.println("----------------");
doSomething(ints2);
}
}
且不要用 原始类型:比如 List list = new ArrayList<>();
然后往里面丢任何类型的东西
4-6:反射
1、数据类型需要一致
private static void reflectDeclaredMethod() throws Exception {
Class<Boss> clz = Boss.class;
Method[] methods = clz.getDeclaredMethods();
// Method method = clz.getDeclaredMethod("numeric", int.class);
Method method = clz.getDeclaredMethod("numeric", Integer.class);
System.out.println(method.invoke(clz.newInstance(), 19));
}
public class Boss extends Worker {
public String boss(String hello) {
return Boss.class.getName() + ": " + hello;
}
public String numeric(Integer age) {
return Boss.class.getName() + ": " + age;
}
}
4-8:StringBuilder
不要依赖于编译器的自动优化
加入了 synchronized 关键字 同步锁
4-9、10:深浅拷贝
友情链接
对引用对象是否发生了新地址的拷贝
1、类没有实现 Cloneable 接口会报错
object提供的方法即是浅拷贝
2、实现深拷贝的方法
简单的new后返回
使用native的clone改进
3、通过序列化的方式实现深拷贝
public Worker clone() {
Worker worker = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
worker = (Worker) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return worker;
}
需要特别注意:
string 不是基本数据类型,但是在浅拷贝里,worker2不会改变worker1的值,原因是 string在set用法时是放进常量值,不是通过new出来的;