7.序列化和反序列化
序列化和反序列化最常用的方式是将某个类实现Serializable接口,使用对象流进行相应的序列化和反序列化
重点需要注意的是
-
显式定义一个private static final long serialVersionUID且不要轻易修改这个值(达到向下兼容的目的)
-
使用transient修饰的变量,在序列化的时候会忽略他的值,在反序列化的时候会将其值设为初始值,int为0,引用类型为null
/**
*序列化和反序列化代码
* 重要的话说三遍,在定义某个类实现Serializable接口后,需要显式定义一个serialVersionUID,防止后面修改该类之后导致原先序列化之后的对象在反序列化之后出现错误,因为如果不显示声明的话,在反序列化时原来序列化的类的该属性会与此时的类的该属性进行比较,不一样的话报错
*/
class Data implements Serializable {
private static final long serialVersionUID = -1018436805097712788L;
private int n;
public Data(int n) { this.n = n; }
@Override
public String toString() { return Integer.toString(n); }
}
class Worm implements Serializable {
public static void main(String[] args)
throws ClassNotFoundException, IOException { // 序列话读入和写入Object可能会有这两个异常
// 将你要序列化的object,保留到一个文件中
Random rand = new Random();
Data d = new Data(rand.nextInt(10)); //构建你需要序列话的Object
System.out.println("d = " + d);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out")); // 准备写入的文件
out.writeObject(d);
out.flush();
out.close(); // 执行到这里你可以看见worm.out这个文件,
// 以下的代码读出你刚刚写入Object
ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out")); // 读你刚刚写入的文件
Data d2 = (Data)in.readObject(); // 重新构建你刚刚写入的Object
System.out.println("d2 = " + d2);
}
}
8.static 关键字
- 父类的静态代码块(若有多个,从上到下)
- 子类的静态代码块(若有多个,从上到下)
(上面两部只有类第一次初始化的时候会执行) - 父类的成员变量初始化
- 父类的初始化代码块(若有多个,从上到下)
- 父类的构造函数
- 子类的成员变量初始化
- 子类的初始化代码块(若有多个,从上到下)
- 子类的构造函数
- 总结:在第一次加载某个类时,先加载父类的静态代码块;实例化的时候先加载父类的成员变量、初始化代码块、构造函数,再加载子类的
class StaticExample {
public StaticExample() {
System.out.println("hello");
}
//static block
static{
//can be used to initialize resources when class is loaded
System.out.println("StaticExample static block");
//can access only static variables and methods
str="Test";
setCount(2);
}
//multiple static blocks in same class
static{
System.out.println("StaticExample static block2");
}
//static variable example
private static int count; //kept private to control it's value through setter
public static String str;
public int getCount() {
return count;
}
//static method example
public static void setCount(int count) {
if(count == 0)
StaticExample.count = count;
}
//static util method
public static int addInts(int i, int...js){
int sum=i;
for(int x : js) sum+=x;
return sum;
}
//static class example - used for packaging convenience only
public static class MyStaticClass{
public int count;
}
}
class TestStatic {
public static void main(String[] args) {
StaticExample.setCount(5);
//non-private static variables can be accessed with class name
StaticExample.str ="abc";
StaticExample se = new StaticExample();
System.out.println(se.getCount());
//class and instance static variables are same
System.out.println(StaticExample.str +" is same as "+se.str);
System.out.println(StaticExample.str == se.str);
//static nested classes are like normal top-level classes
StaticExample.MyStaticClass myStaticClass = new StaticExample.MyStaticClass();
myStaticClass.count=10;
StaticExample.MyStaticClass myStaticClass1 = new StaticExample.MyStaticClass();
myStaticClass1.count=20;
System.out.println(myStaticClass.count);
System.out.println(myStaticClass1.count);
}
}
最后执行的结果
StaticExample static block
StaticExample static block2
hello
0
abc is same as abc
true
10
20
9.注解
- @Target:表示这个注解用在什么地方
- @Retention:保留策略,有SOURCE,CLASS,RUNTIME
- @Dcoumented:将此注解保留在javadoc中
- @Inherited:是否可以被继承
- @Repeatable:是否允许一个注解在一个元素上出现多次
- @Native:修饰成员变量,表示成员变量可以被本地代码使用
使用@interface
//获取一个类上的注解信息
//关键方法:isAnnotationPresent判断是否存在注解
// getAnnotation获得注解
Class<?> clz = bean.getClass();
Method[] methods = clz.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(EnableAuth.class)) {
String name = method.getAnnotation(EnableAuth.class).name();
}
}
10.泛型
-
定义:泛型是JDK5引入的一个新特性,允许在定义类和接口的时候使用类型参数。声明的参数类型在调用时传入具体的类型。常用于集合类型框架中。最大的好处就是提高代码的复用性,如需要在List中放Integer,String,使用泛型只需要定义一个公共的接口
-
泛型擦除:指的是在编译的时候将泛型去掉,最终留下最基本的原生类型,如方法重载是基于泛型的话,在编译的时候会进行泛型擦除,导致编译报错
public class GenericTypes {
public static void method(List<String> list) {
System.out.println("invoke method(List<String> list)");
}
public static void method(List<Integer> list) {
System.out.println("invoke method(List<Integer> list)");
}
}
1. T - Type(Java 类)
2. K - Key(键)
3. V - Value(值)
4. N - Number(数值类型)
5. ? - 表示不确定的java类型(无限制通配符类型)
6. S、U、V - 2nd、3rd、4th types
7. Object - 是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T、E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。
-
上界:<? extends T> 类型必须为T类型或其子类
-
下界:<? super T> 类型必须为T类型或其父类
-
泛型必须使用限定内的类型进行初始化,否则会编译出错
-
非限定通配符表示可以使用任意泛型类型来代替
-
extends 和 super的用法,在使用泛型时,存取元素时用super,获取元素时,用extends。频繁往外读取内容的,适合用上界Extends。经常往里插入的,适合用下界Super。
public class Food {}
public class Fruit extends Food {}
public class Apple extends Fruit {}
public class Banana extends Fruit{}
public class GenericTest {
public void testExtends(List<? extends Fruit> list){
//报错,extends为上界通配符,只能取值,不能放.
//因为Fruit的子类不只有Apple还有Banana,这里不能确定具体的泛型到底是Apple还是Banana,所以放入任何一种类型都会报错
//list.add(new Apple());
//可以正常获取
Fruit fruit = list.get(1);
}
public void testSuper(List<? super Fruit> list){
//super为下界通配符,可以存放元素,但是也只能存放当前类或者子类的实例,以当前的例子来讲,
//无法确定Fruit的父类是否只有Food一个(Object是超级父类)
//因此放入Food的实例编译不通过
list.add(new Apple());
// list.add(new Food());
Object object = list.get(1);
}
}
Regarding List<Object> not accepting a List<String>, that makes sense because a String is not Object; it is a subclass of Object. The fix is to declare public static void test(List<? extends Object> set) .... But then the extends Object is redundant, because every class directly or indirectly extends Object.
11.错误和异常
-
https://blog.csdn.net/m0_37602175/article/details/80271647
-
Error和Exception继承自Throwable类,异常指的是在程序中可能出现并且应该被捕获的情况(分为受检异常和非受检异常,非受检异常又称为运行时异常),Error指的是不大可能出现的情况,不便于也不需要进行捕获。
-
正确处理异常:将异常的范围尽可能地缩小,捕获异常之后进行相应的处理(自己处理或向上抛给调用者)
-
try{}catch{}finally中
-
- 如果try{}里有return,会在执行finally之前将返回变量的内存地址复制一份,最后返回的是复制好的这份地址
-
- finally里面对return的变量做了修改,如果是基本数据类型,直接操作值,即使修改了,return的值还是原来的值
-
- finally里面对return的变量做了修改,如果是引用数据类型,复制的是对象的地址,修改了,return的值指向修改后的值,所以最终会表现出被修改
13.时间
针对Java8之前时间API的设计差,Java8之后引入了新的时间包,新的时间及⽇期API位于java.time包中
-
Instant: 时间戳
-
Duration: 持续时间, 时间差
-
LocalDate: 只包含⽇期, ⽐如: 2016-10-20
-
LocalTime: 只包含时间, ⽐如: 231210
-
LocalDateTime: 包含⽇期和时间, ⽐如: 2016-10-20 231421
-
Period: 时间段
-
ZoneOffset: 时区偏移量, ⽐如: +8:00
-
ZonedDateTime: 带时区的时间
-
Clock: 时钟, ⽐如获取⽬前美国纽约的时间
//获取当前日期
LocalDate today = LocalDate.now();
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
System.out.printf("Year : %d Month : %d day : %d t %n", year,month, day);
//获取指定日期
LocalDate date = LocalDate.of(2018, 01, 01);
//判断闰年
LocalDate nowDate = LocalDate.now();
boolean leapYear = nowDate.isLeapYear();
//获得两个时间之间的差值
Period period = Period.between(LocalDate.of(2018, 1, 5),LocalDate.of(2018, 2, 5));
//输出0
System.out.println(period.getDays());
14.并发和并行
并行-两个线程再两个CPU上同时运行互不干扰;并发-某个时间段有多个线程运行,看起来是同时运行的,但细化到某个时刻,还是串行执行的
15.Unicode 和 GBK
-
Unicode是一个字符集,定义和ASCII(只包含了256个字符)相同,Unicode虽然定义了编码方式,但是没有定义如何进行存储。而utf-8和utf-16是对于Unicode编码的存储规范(Unicode Transformation Format),最常用的utf-8使用可变存储长度的方案,英文字符使用一个字节进行存储,大部分汉字使用3个字节进行存储
-
GBK是国内的字符集通用标准
-
URL解编码:RFC标准规定只有字母和数字[0-9a-zA-Z]、一些特殊符号“$-_.+!*’(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL;除此以外的其他字符在转化为url的过程中会经过编码。关于URL编码的博客
16.BigDecimal的使用
17.lambda表达式
- lambda表达式也称为闭包,允许把一个函数作为参数传递到方法中,作用是使得代码更加的紧凑简洁
(parameters) -> expression
或
(parameters) ->{ statements; }
包含了以下4个关键特征:
1. 可选的参数圆括号,一个参数无需定义圆括号,多个参数需要定义圆括号
2. 可选的大括号,如果主体包含了一个语句,则不需要大括号
3.可选的返回关键字,如果主体只有一个表达式返回值,则编译器会自动返回值,大括号需要指定表达式返回了一个数值
4.可选的类型声明,不需要声明参数类型,编译器可以统一识别参数类型
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
18.Stream
- 简介
- 特点
-
不是一种数据结构,是数据源的一个视图
-
函数式编程,对Stream的操作不会修改原来的数据,而是会产生一个新的Stream
-
惰式执行,对Stream的操作不会立即执行,需要时才会执行
-
Stream只能被消费一次,一旦执行完全就会失效,想要结果需要重新执行
- 使用
- 对于流的操作有3步,流的创建、中间操作、最终操作
- 创建
1. 基于原有集合类的增强,为原有的集合类添加stream方法
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
Stream<String> stream = strings.stream();
2. 使用Stream类的方法直接创建流
Stream<String> stream = Stream.of("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
- 中间操作
1. filter: filter items according to a given predicate
List<String> strings = Arrays.asList("Hollis", "", "HollisChuang", "H", "hollis");
strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);
//Hollis, HollisChuang, H, hollis
2. map: processes items and transform
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().map(i -> i*i).forEach(System.out::println);
//9,4,4,9,49,9,25
3. limit: limit the results
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().limit(4).forEach(System.out::println);
//3,2,2,3
4. sorted: sorted items inside stream
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().sorted().forEach(System.out::println);
//2,2,3,3,3,5,7
5. distinct: remove duplicate items according to equals method
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);
//3,2,7,5
3.最终操作
- forEach(): for each item, outputs something
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
- count(): counts currrent items
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis", "Hollis666", "Hello", "HelloWorld", "Hollis");
System.out.println(strings.stream().count());
//7
- collect(): reduce the stream into a desired collection
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis","Hollis666", "Hello", "HelloWorld", "Hollis");
strings = strings.stream().filter(string -> string.startsWith("Hollis")).collect(Collectors.toList());
System.out.println(strings);
//Hollis, HollisChuang, Hollis666, Hollis