大部分浮点数据在计算机中是不精确
浮点数不管是float和double,在计算机中都是要转为二进制数进行存储,而大部分浮点数是没法用有限个二进
制数表示的,所以不管是32位还是64位,存储的都是有限个数据。(用BigDecimal)
// 推荐这样写(重要)
BigDecimal a1 = BigDecimal.valueOf(0.1);
BigDecimal a2 = BigDecimal.valueOf(0.03);
// 注意:如果是无穷数,要进行保留几位小数
// BigDecimal divisor, 除数 int scale, 保留几位小数 RoundingMode roundingMode 舍弃的方法
System.out.println(a1.divide(a2, 3, RoundingMode.HALF_UP)); // 四舍五入 3.333
一、日期类:
从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:
- LocalDate:不包含具体时间的日期。
- LocalTime:不含日期的时间。
- LocalDateTime:包含了日期及时间。
- Instant:代表的是时间戳。
- DateTimeFormatter: 用于做时间的格式化和解析的
- Duration:用于计算两个“时间”间隔
- Period:用于计算两个“日期”间隔
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime);
// 日期格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dtf.format(dateTime));
System.out.println(dateTime.format(dtf));
// 字符串解析
LocalDateTime dateTime1 = LocalDateTime.parse("2019-12-12 11:11:11", dtf);
System.out.println(dateTime1);
二、正则表达式
String类的match方法可以匹配正则表达式
方法名 | 说明 |
---|---|
public String replaceAll(String regex,String newStr) | 按照正则表达式匹配的内容进行替换 |
public String[] split(String regex) | 按照正则表达式匹配的内容进行分割字符串,返回一个字符串数组。 |
三、Lambda表达式
是JDK 8开始后的一种新语法形式,Lambda表达式只能简化函数式接口的匿名内部类的写法形式。
函数式接口: 有@FunctionalInterface注解,必须是接口、其次接口中有且仅有一个抽象方法的形式(不包括继承父类的)
- 参数类型可以省略不写
- 如果只有一个参数,参数类型可以省略,同时()也可以省略:
e -> System.out.println("xxx")
- 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号
- 如果这行代码是return语句,必须省略return不写,同时也必须省略分号不写:
Arrays.sort((o1, o2) -> o2 - o1)
- 如果这行代码是return语句,必须省略return不写,同时也必须省略分号不写:
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer t1, Integer t2) {
return t2 - t1; // 降序
}
});
// Lambda表达式的写法1
Arrays.sort(arr, (t1, t2) -> {
return t2 - t1; // 降序
});
// Lambda表达式的写法2
Arrays.sort(arr, (t1, t2) -> t2 - t1);
四、集合体系
Collection集合的遍历方式:
- 迭代器:
Iterator<E> iterator()
- foreach/增强for循环
- lambda表达式:
default void forEach(Consumer<? super T> action):
List系列集合
-
ArrayList:底层数据结构是数组,根据索引定位元素快(查询快),增删需要做元素的移位操作
-
LinkedList:底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API
重点:ArrayList和LinkedList底层原理
/**
* 数组转为list集合常用的方法
*/
Integer[] nums = new Integer[]{1,2,3,4,5,6,7,8,9};
// 1.集合的asList方法
List<Integer> arrayList1 = new ArrayList<>(Arrays.asList(nums));
System.out.println(arrayList1);
// 2.Stream流的方法
List<Integer> arrayList2 = Stream.of(nums).collect(Collectors.toList());
System.out.println(arrayList2);
// 3.Collections的addAll方法(优先)
List<Integer> arrayList3 = new ArrayList<>();
Collections.addAll(arrayList3, nums);
System.out.println(arrayList3);
/**
* list集合转为数组最常用的方法
*/
// 1.集合的toArray方法(优先)
String[] strings = arrayList.toArray(new String[arrayList.size()]);
System.out.println(Arrays.toString(strings));
// 2.stream流方法
String[] strings1 = arrayList.stream().toArray(String[]::new);
System.out.println(Arrays.toString(strings1));
Set系列集合
- HashSet:采取哈希表存储的数据。
- 哈希值:JDK根据对象的虚拟地址,按照某种规则算出来的int类型的数值,同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 哈希表:JDK8前,底层使用数组+链表;JDK8后,底层使用数组+链表+红黑树
- LinkedList:采取哈希表存储的数据,加上了一个双链表的机制。实际用的就是HashMap的键
- TreeSet:按照元素大小升序排序。重点:存储的是对象元素如何进行排序?
- 自定义的类实现Comparable接口,重写方法。注意:
return this.weight - 0.weigth >=0 ? 1 : -1
保留相同重量 - (优先)集合自带有参数构造器,可以设置Comparator接口对应的比较器对象:lambda表达式
- 自定义的类实现Comparable接口,重写方法。注意:
Map键值对
- HashMap:键无序,不重复,无索引。采取哈希表存储的数据,加上了一个双链表的机制。遍历方式:
- 先获取键:Set<String> keys = maps.keySet();
- 获取键值对:Set<Map. Entry<String, Integer>> entries = maps. entrySet();
- lambda表达式:maps.forEach()
- LinkedHashMap:有序、不重复、无索引
- TreeMap:按照键排序
- Properties代表的是一个属性文件,后缀是.properties结尾,里面的内容都是key=value
不可变集合
即:不可被修改的集合。集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。
五、Stream流
JDK8后的Stream流,是结合了Lambda表达式,简化了集合、数组操作的API。
// 集合类获取流
Collection<String> stringArrayList = new ArrayList<>();
Stream<String> stream = stringArrayList.stream();
Map<String, Integer> map = new HashMap<>();
Stream<String> stream1 = map.keySet().stream();
Stream<Integer> stream2 = map.values().stream();
Stream<Map.Entry<String, Integer>> stream3 = map.entrySet().stream();
// 数组获取流
String[] arr = {"Java", "HTML"};
Stream<String> stream4 = Arrays.stream(arr);
Stream<String> stream5 = Stream.of(arr);
System.out.println(stream4);
注意:
- 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
- 终极方法:forEach,count
- 在Stream流中无法直接修改集合、数组中的数据。
- Stream流只能使用一次
Stream流的收集:把stream流转为集合使用:arrList.stream().limit(2).collect(Collectors.toSet())
List<String> arrList = new ArrayList<>();
Collections.addAll(arrList, "Java", "Python", "C++", "GO", "C");
// 过滤filter
arrList.stream().filter(s -> s.startsWith("C")).forEach(System.out::println);
// limit获取前几个元素
arrList.stream().filter(s -> s.startsWith("C")).limit(1).forEach(System.out::println);
// map加工
arrList.stream().map(s -> s + "学习").forEach(System.out::println);
// 把stream流处理过的数据,重新得到集合
List<String> list = arrList.stream().map(s -> s + "学习").collect(Collectors.toList());
Set<String> set = arrList.stream().map(s -> s + "学习").collect(Collectors.toSet());
六、异常体系
- throw:在方法内部直接创建一个异常对象,并从此点抛出。
throw new MyException(age +"is illegal!")
- throws:用在方法申明上的,抛出方法内部的异常。
throws MyException
七、泛型
JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。作用:统一数据类型,把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来
-
泛型的定义:<数据类型>; 注意:泛型只能支持引用数据类型
- 泛型类:
public class MyArrayList<T> { }
- 泛型方法:
public static <T> void show(T t) { }
,注意<T>的位置 - 泛型接口:
public interface Data<E>{ }
- 泛型类:
-
泛型的通配符和上下限
- ? 可以在**“使用泛型”**的时候代表一切类型,E T K V 是在定义泛型的时候使用的
- 上下限:
public static void go(ArrayList<? extends Car> cars){
- 泛型上限<? extends Car>:表示 ? 必须是Car或者其子类
- 泛型上限<? super Car> :表示 ? 必须是Car或者其父类
- 上下限:
- ? 可以在**“使用泛型”**的时候代表一切类型,E T K V 是在定义泛型的时候使用的
八、日志技术
Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好,是基于slf4j的日志规范实现的框架。一般情况,Logback日志框架只需要下载slf4j-api、logback-core、logback-classic这三个jar包即可。Logback主要分为三个技术模块:
- logback-core:logback-core 模块为其他两个模块奠定了基础,必须有
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API
- logback-access:模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能
九、File操作
- commons-io是apache提供的有关IO操作的jar包类库,可以提高IO功能开发的效率;两个主要的类:FileUtils,IOUtils
- JDK7后也提供了有关IO操作的包nio
字符集
字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:ASCII字符集、GBK字符集、Unicode(UTF-8)字符集等。
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。使用一个字节存储一个字符
- GBK是中国的码表,包含了几万个汉字等字符,同时也兼容ASCII编码,GBK编码中一个中文字符一般以两个字节的形式存储
- Unicode统一码,也叫万国码,是计算机科学领域里的一项业界标准,UTF-8是Unicode的一种常见编码方式。UTF-8编码后一个中文一般以三个字节的形式存储,同时也兼容ASCII编码表,技术人员都应该使用UTF-8的字符集编码
// 1、编码:把文字转换成字节(使用指定的编码)
String name ="abc我爱你中国";
byte[]bytes = name.getBytes();// 以当前代码默认字符集进行编码(UTF-8)
byte[]bytes = name.getBytes("GBK"); // 指定编码方式
System.out.println(bytes.length);
System.out.println(Arrays.toString(bytes));
// 2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码)
String rs = new String(bytes); // 默认的UTF-8编码
String rs = new String(bytes,"GBK"); // 指定GBK解码
System.out.println(rs);
IO流
- 字节流适合做一切文件数据的拷贝(音视频,文本),不适合读取中文内容输出;字符流适合做文本文件的操作(读,写)
资源释放:try-catch-finally
- finally:放在try-catch后面的,无论是正常执行还是异常执行代码,最后一定要执行,除非JVM退出。优先级高于return
/**
* 使用字节流完成文件的复制操作,了解自动释放资源的写法
*/
public class Test4 {
public static void main(String[] args) {
try (
// 这里面定义资源对象:即实现了Closeable/AutoCloseable接口的对象
InputStream inputStream = new FileInputStream("basic/xxx.txt");
// 字节缓冲流
InputStream bis = new BufferedInputStream(inputStream)
OutputStream outputStream = new FileOutputStream("basic/yyy.txt");
OutputStream ops = new BufferedOutputStream(outputStream);
){
// 定义一个1024个字节的桶,即每次转移1K大小
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes)) != -1)
{
ops.write(bytes, 0, len);
}
System.out.println("复制完成了");
} catch (Exception e) {
e.printStackTrace();
}
}
}
缓冲流
缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。作用:缓冲流自带缓冲区,可以提高原始字节流、字符流读写数据的性能。
-
字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能较好
-
字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能极高了
-
字节缓冲流:输入流: BufferedInputStream;输出流:BufferedOutputStream
-
字符缓冲流:输入流:BufferedReader;输出流:BufferedWriter
对象序列化
Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即:这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。
- 使用ObjectOutputStream把内存中的对象存入到磁盘文件中(序列化),ObjectInputStream实现反序列化
- 类要实现Serializable接口
- transient修饰的成员变量不参与序列化
- private static final long serialVersionUID = 1L;序列化和反序列化的”版本号“
十、拓展
测试
/**
* 测试分类:
* 1.黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值
* 2.白盒测试:需要写代码,关注程序具体的执行流程。
* Junit使用:白盒测试
* 给测试方法加上注解@Test即可,测试成功与否看是红勾还是黄叉
* 一般用断言操作来处理结果:Assert.assertEquals(期望结果, 实际结果);
* @Before:
* 修饰的方法会在测试方法之前被自动执行
* @After:
* 修饰的方法会在测试方法执行之后自动被执行
*/
public class CalculatorTest {
//初始化方法:所有的测试方法在执行之前都会先执行该方法
@Before
public void init()
{
System.out.println("init....");
}
@Test
public void testAdd()
{
Calculator calculator = new Calculator();
int add = calculator.add(1, 3);
System.out.println(add);
//断言
Assert.assertEquals(4,add);
}
//释放资源的方法:在所有测试方法执行完后都会自动执行该方法
@After
public void close()
{
System.out.println("close....");
}
}
反射
/*
反射:框架设计的灵魂。在运行时可以动态获取类信息以及类中成分,并将类的各个组成部分封装为其他对象,这就是反射机制。
获取Class对象的方式:
1.Class.forName("全类名"):多用于配置文件(注解配置),将全类名定义在配置文件中,读取文件,加载类
2.类名.Class:多用于参数的传递
3.对象名.getClass():多用于对象的获取字节码的方式
结论:同一个字节码(*.class)文件,在一次程序运行过程中只会被加载一次,所以每种方式获取出来的Class对象都是同一个。
Class对象的功能:
获取成员变量,获取构造方法,获取成员方法
get - Fields Constructors Methods
getDeclared - Fields Constructors Methods
暴力反射:忽略访问权限修饰符的安全检查,可以让私有的成员变量修改值。
setAccessible(true);
1.获取到的成员变量:Field 有set和get方法
2.获取到的构造器:Constructor 有newInstance方法
3.获取到的成员方法:Method 有invoke执行方法
*/
/**
* 框架类:可以在不改变任何类的代码的前提下,创建任意类的对象,可以执行任意方法
* 实现:
* 1.把需要创建的对象的全类名和方法定义在配置文件中
* 2.在程序中加载读取配置文件
* 3.使用反射技术来加载类文件进内存
* 4.创建对象
* 5.执行方法
*/
public class ReflectTest {
public static void main(String[] args) {
try {
//1.加载配置文件
//获取这个框架类的类加载器,用类加载器把资源文件转换成流的形式
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream in = classLoader.getResourceAsStream("config.properties"); //资源文件要放在src下
//然后再把流加载进去
Properties pro = new Properties();
pro.load(in);
//2.读取配置文件
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载类进内存(反射技术)
Class aClass = Class.forName(className);
//4.用加载好的类创建对象和获取方法
Object obj = aClass.getDeclaredConstructor().newInstance();
Method method = aClass.getMethod(methodName);
//5.执行方法
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注解
从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,
- 这些标记可以在编译,类加载,运行时被读取,并执行相应的处理
- 通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息
* 4.自定义注解
* 格式:
* 元注解
* public @interface 注解名称{
* 属性列表;
* }
*
* 本质:注解本质上就是一个接口,默认继承Annotation
* public interface MyAnnotation extends java.lang.annotation.Annotation{}
* 属性:接口中的抽象方法
* 要求:
* 1.属性的返回值类型:基本类型(四类八种),String,枚举,注解,以上类型的数组
* 2.定义了属性(可以使用default默认初始化值),在使用注解时要给属性赋值
* 3.如果只有value属性需要赋值,则value=可以省略,直接写值就可以
* 4.数组复制用{}包裹,如果数组只有一个值,{}省略
*
* 元注解:用来描述注解的注解
* @Retention - 标识这个注解怎么保存,只在代码中,或编入class文件中,或在运行时可以通过反射访问(默认Class)
* 参数只有RetentionPolicy 是 Enum 枚举类型
* @Documented - 标记这些注解是否包含在用户文档API中。
* @Target - 标记这个注解能够作用的位置。(默认全部位置)
* 参数只有ElementType 是 Enum 枚举类型
* @Inherited - 标记这个注解是否被子类继承(默认 注解并没有被任何子类继承)
*/
@Target(ElementType.TYPE) //表示MyAnnotation注解只能作用于类上
@Retention(RetentionPolicy.RUNTIME) //表示MyAnnotation注解可以保留到JVM虚拟机读入
@Documented //表示MyAnnotation注解可以写到API文档里面
public @interface MyAnnotation {
//描述需要执行的类名和方法名
String className();
String methodName();
}
XML文件
可扩展标记语言(Extensible Markup Language)的缩写,是一种数据表示格式,可以用于自定义数据格式。XML文档约束方式:DTD约束,schema约束。Dom4J用来解析XML文件,Xpath用来检索XML文件
<?xml version="1.0" encoding="UTF-8"?>
<javaCode>
<![CDATA[
if(a>b){
System.out.print(true);
}else{
System.out.print(false);
}
]]>
</javaCode>