String

返回某索引处的字符

1
char charAt(int index)  //return value[index]

判断是否是空字符串

1
boolean isEmpty();

将所有字符转换为小写

1
String toLowerCase();

将所有字符转换为大写

1
String toUpperCase();

返回副本,忽略前导空白和尾部空白

1
String trim();

比较字符串的内容是否相同

1
boolean equals(Object obj);

比较字符串的内容是否相同(忽略大小写)

1
boolean equalsIgnoreCase(String anotherString);

比较两个字符串的大小

1
int compareTo(String anotherString);

从原字符串中截取一个新字符串

1
String substring(int beginIndex, int endIndex);

判断字符串是否以指定的后缀结束

1
boolean endsWith(String suffix);

判断字符串(从指定索引开始)是否以指定的前缀开始

1
boolean startsWith(String prefix, int toffset);

判断字符串中是否包含指定的char值序列

1
boolean contains(CharSequence s);

返回指定字符串在此字符串中(从指定索引开始)第一次出现处的索引

1
int indexOf(String str, int fromIndex);

返回指定字符串在此字符串中(从指定索引开始)最右边第一次出现的索引

1
int lastIndexOf(String str, int fromIndex);

返回一个新字符串,其中部分被替换

1
2
String replace(char oldChar, char newChar);
String replace(CharSequence target, CharSequence replacement);

String —->基本数据类型、包装类

  • 调用包装类的静态方法:parseXxx(str)

基本数据类型、包装类 —->String

  • 调用String重载的valueOf(xxx)

String与char[]之间的转换

1
2
char[] ch = str.toCharArray();
String str = new String(ch);

String与byte[]之间的转换

1
2
3
4
5
6
7
//编码
byte[] bytes = str.getBytes();//使用默认的字符集,进行转换
byte[] gbks = str.getBytes("gbk");//使用指定的字符集进行编码

//解码
String str2 = new String(bytes);//使用默认的字符集,进行解码
String str3 = new String(gbks, "gbk");

StringBuffer和StringBuilder

String/StringBuffer/StringBuilder 三者的异同

  • String:不可变的字符序列;底层使用char[] 进行存储

  • StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[] 进行存储

  • StringBuilder:可变的字符序列;线程不安全的,效率高;底层使用char[] 进行存储

源码分析:

1
2
3
4
5
6
7
8
String str = new String();//char[] value = new char[0];
String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};

StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];
StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length + 16];
//如果要添加的数据底层数组装不下了,就需要扩容底层的数组
//默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组中
//指导意义:开发中最好使用StringBuffer(int capacity) 或 StringBuilder(int capacity)构造器来构造字符串

常用方法(两者类似,以StringBuffer为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StringBuffer s1 = new StringBuffer("abasgsc");

//append(xxx);
s1.append(1);
//delete(int start, int end);
s1.delete(2, 4);
//replace(int start, int end, String str);
s1.replace(1, 3, "sdf");
//insert(int offset, xxx);
s1.insert(2, "sojfo");
//reverse();
s1.reverse();
//indexOf(String str);返回str在字符串中首次出现的位置
//String substring(int start, int end);返回一个从start开始到end索引结束的左闭右开区间的子字符串
//char charAt(int n);返回指定索引处字符值
//setCharAt(int n, char ch);将指定索引处的字符修改为指定字符

jdk 8之前的日期时间的API

System类中currentTimeMillis();

java.util.Date类

1
2
3
4
5
Date date1 = new Date();//构造器一:创建一个对应当前时间的Date对象
Date date1 = new Date(long date);//构造器二:创建指定毫秒数的Date对象

date1.getTime();//方法一:返回当前data对象对应的时间戳
date1.toString();//方法二:返回当前的年、月、日、时、分、秒

java.sql.Date类

  • 对应着数据库中的日期类型的变量
1
2
java.sql.Date date1 = new java.sql.Date(long date);//构造器一:创建一个指定毫秒数的sql.Date对象
java.sql.Date date1 = new java.sql.Date(date.getTime());//如何将util.Date对象转换为sql.Date对象

SimpleDateFormat

  • SimpleDateFormat对日期Date类的格式化和解析
1
2
3
4
5
6
7
8
9
SimpleDateFormat sdf = new SimpleDateFormat();
//常用的date格式 "yyyy-MM-dd hh:mm:ss"

Date date = new Date();//Sat May 28 09:19:07 CST 2022
//格式化:日期-->字符串
String format = sdf.format(date);//22-5-28 上午9:19
//解析:格式化的逆过程:字符串-->日期
String str = new String("22-5-28 上午9:19");//默认构造器
String parse = sdf.parse(str);

java.util.Calendar类

1
2
3
4
5
6
7
8
9
//实例化方法一:创建其子类的对象(GregorianCalendar)
//方法二:调用静态方法getInstance()
Calendar calendar = Calendar.getInstance();

//常用方法
//get()
//add()
//getTime()
//setTime()

jdk 8中日期时间的API

LocalDate/LocalTime/LocalDateTime

  • LocalDateTime使用频率比其他要高
1
2
3
4
5
6
7
//now():获取当前的日期、时间、日期+时间
LocalDateTime localdatetime = LocalDateTime.now();
//of():设置指定的年、月、日、时、分、秒。没有偏移量

//getXxx():获取相关的属性
//withXxx():设置相关的属性
//plusXxx()/minusXxx():加上或减去一个值

Instant类

1
2
3
4
5
6
7
8
9
10
11
12
13
//now():获取本初子午线处的时间
Instant instant = Instant.now();
System.out.println(instant);

//添加时间的偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);

//获取对应的毫秒数
long time = instant.toEpochMilli();

//通过给定的毫秒数,获取Instant实例
Instant instant = Instant.ofEpochMilli(1653706858013L);

DateTimeFormatter

  • 格式化或解析日期、时间,类似于SimpleDateFormat
  • 实例化的三种方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//方式一:预定义的标准格式
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化:日期--->字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);
//解析:字符串-->日期
TemporalAccessor parse = formatter.parse(str1);
System.out.println(parse);

//方式二:本地化相关的格式。如:ofLocalizedDateTime()
//FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT:适用于LocalDateTime
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
//格式化
String str1 = formatter1.format(localDateTime);
System.out.println(str1);

//方式三:自定义的格式(通常使用这种方式)
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str2 = formatter2.format(localDateTime);
System.out.println(str2);
//解析 略

Comparable和Comparator

自然排序:Comparable接口的使用

  • 像String、包装类等实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小的方式

  • 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法

    在compareTo(obj)方法中指明如何排序

定制排序:Comparator接口的使用

1
2
3
4
5
6
Arrays.sort(arr, new Comparator(){
public int compare(Object o1, Object o2){
//比较的方式
return 0;
}
});

System类

  • System类内部包含in,out,err三个成员变量
  • 该类的构造器是private的,所以无法创建对象和实例化

native long currentTimeMillis()

  • 返回当前的计算机时间

void exit(int status)

  • 退出程序。status等于0代表正常退出,非0代表异常退出

void gc()

  • 请求系统进行垃圾回收

String getProperty(String key)

  • 获取系统中属性名为key的属性对应的值

Math类

BigInteger和BigDecimal

  • BigInteger可以表示不可变的任意精度的整数
  • BigDecimal支持不可变的、任意精度的有符号十进制定点数

枚举类

  • 当需要定义一组常量时,强烈建议使用枚举类

jdk5.0之前,自定义枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//1.声明Season对象的属性:使用private final修饰
private final String seasonName;
private final String seasonDesc;

//2.私有化类的构造器,并给对象属性赋值
private Season(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}

//3.提供当前枚举类的多个对象:public static final来修饰
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "夏日炎炎");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "冰天雪地");

//4.其他诉求:获取枚举类对象的属性

public String getSeasonName(){
return seasonName;
}

public String getSeasonDesc(){
return seasonDesc;
}

//5.其他诉求2:提供toString()


@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}

jdk5.0,使用enum关键字定义枚举类

  • 使用enum关键字定义的枚举类默认继承java.lang.Enum类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//	使用enum关键字枚举类
enum Season1{
// 1.提供当前枚举类的对象,多个对象之间使用“,“隔开,分号结束
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "冰天雪地");

// 2.声明Season对象的属性:private final修饰
private final String seasonName;
private final String seasonDesc;

// 3.私有化类的构造器,并给对象属性赋值
private Season1(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}

// 4.其他诉求:获取枚举类对象的属性
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
}
  • Enum类的常用方法
1
2
3
4
5
/*
values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值
valueof(String objName):返回枚举类中对象名是objName的对象。如果没找到,抛异常。
toString():返回当前枚举类对象常量的名称
*/

Annotation

生成文档相关的注解

1
2
3
4
5
@author
@version
@see
@param
...

JDK内置的三个基本注解

  • 在编译时进行格式检查
1
2
3
@Override:限定重写父类方法,该注解只能用于方法
@Deprecated:用于表示所修饰的元素已过时
@SuppressWarnings:抑制编译器警报

跟踪代码依赖性,实现替代配置文件的功能

自定义注解

  • 参照@Suppresswarnings定义
1
2
3
4
5
6
7
8
9
10
/*
1.注解声明为:@interface
2.内部定义成员,同城使用value表示
3.可以指定成员的默认值,使用default来定义
4.如果自定义注解没有成员,表明是一个标识作用

如果注解有成员,在使用注解时,需要指明成员的值
自定义注解必须配上注解的信息处理流程(使用反射)才有意义
自定义注解通常都会指定两个元注解:Retention,Target
*/

JDK中的四个元注解

  • meta-annotation:对现有的注解进行注解的注解
  • (元数据:对现有数据进行修饰的数据)

@Retention

  • 指定所修饰的Annotation的生命周期:SOURCE\CLASS(默认行为)\RUNTIME

    (只有声明为RUNTIME生命周期的注解,才能通过反射获取)

@Target

  • 指定被修饰的Annotation能用于修饰哪些程序元素

@Documented(出现的频率较低)

  • 表示所修饰的注解在被javadoc解析时,保留下来

@Inherited(出现的频率较低)

  • 被它修饰的Annotation将具有继承性

JDK 8中注解的新特性

  • 可重复注解

  • 类型注解

集合

Collection接口

Collection接口的常用方法

注意:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals();

  • add(Object obj);
  • size();
  • addAll();
  • clear();
  • isEmpty();
  • contains(Object obj):判断集合中是否包含obj;
  • containsAll(Collection coll):判断形参coll中的所有元素是否都存在与当前集合中;
  • remove(Object obj):要求obj所在类要重写equals();
  • removeAll(Collection coll):从当前集合中移除coll中所有的元素;
  • retainAll(Collection coll):求交集;
  • equals(Object obj);
  • hashCode():返回当前对象的哈希值;
  • toArray():集合转换为数组;
  • Arrays.asList():数组转换为集合;
  • iterator():返回Iterator接口的实例,用于遍历集合元素;
    使用内部方法:hasNext()/next()/remove()
  • foreach():for(集合元素类型 局部变量 :集合对象)

Collection子接口:List接口

  • ArrayList:线程不安全的,效率高;底层用数组实现
  • LinkedList:对于频繁的插入、删除操作,底层使用双向链表存储
  • Vector:线程安全的,效率低;底层用数组实现(已经被淘汰了)

    List接口中的常用方法

  • void add(int index, Object ele):在index位置插入ele元素
  • boolean addAll(int index, Collection eles):从Index位置开始将eles中所有元素添加进来
  • Object get(int index):获取指定位置index位置的元素
  • int indexOf(Object obj):返回obj在当前集合中首次出现的位置,如果不存在返回-1
  • int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
  • Object remove(int index):移除指定index位置的元素,并返回此元素
  • Object set(int index, Object ele):设置指定index位置的元素为ele
  • List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开子集合

    List的遍历方法

  • 增强for循环
  • Iterator迭代器方式
  • 普通循环

Collection子接口:Set接口

  1. Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
  2. 要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
    要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相同的散列码
  3. 无序性:不等于随机性。存储的数据在底层数组中并非按照索引的顺序进行添加,而是根据数据的哈希值决定的。
  4. 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个
  5. 向HashSet中添加元素的过程:向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
    如果此位置上没有其他元素,则元素a添加成功。——>情况1
    如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a和元素b的哈希值,如果hash值不相同,则元素a添加成功。——>情况2
    如果hash值相同,进而需要调用元素a所在类的equals()方法,返回true,元素a添加失败。
    返回false,则元素a添加成功。——>情况3

对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
jdk 7:元素a放入数组中,指向原来的元素
jdk 8:原来的元素在数组中,指向元素a
七上八下

  • HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
  • LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。对于频繁的遍历操作,LinkedHashSet效率高于HashSet
  • TreeSet:1. 可以按照添加对象的指定属性,进行排序;
    2.向TreeSet中添加的数据,要求是相同类的对象;
    3.两种排序方式:自然排序和定制排序
    4.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
    5.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()

Map接口

Map接口的多个实现类

  • HashMap:作为Map的主要实现类;线程不安全的,效率高;可以存储null的key和value
    • LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历;对于频繁的遍历操作,此类的执行效率要高于HashMap
  • TreeMap:保证按照添加的key-value对进行排序,实现排序遍历;考虑key的自然排序和定制排序;底层使用红黑树
  • Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
    • Properties:常用来处理配置文件;key和value都是String类型

Map结构的理解

  • Map中的key:无序的,不可重复的,使用Set存储所有的key —->key所在类要重写equals()和hashCode()(以HashMap为例)
  • Map中的value:无序的,可重复的,使用Collection存储所有的value —->
  • 一个键值对:key-value构成了一个Entry对象
  • Map中的Entry:无序的,不可重复的,使用Set存储所有的Entry

HashMap的底层实现原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
以jdk7为例
HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table
...已经执行过多次put...
map.put(key1, value1):
首先调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
如果此位置上的数据为空,此时的key1-value1添加成功 ----情况1
如果此位置上的数据不为空,说明此位置上存在一个或多个数据(以链表的形式存在),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在数据的哈希值都不相同,此时key1-value1添加成功。 ----情况2
如果key1的哈希值与已经存在的某一个数据的哈希值相同,继续比较:调用key1所在类的equals()方法比较:
如果equals()返回false:此时key1-value1添加成功 ----情况3
如果equals()返回true:使用value1替换相同key的value值


补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储

在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来

jdk8 相较于jdk7在底层实现方面的不同
1.new HashMap():底层没有创建长度为16的数组
2.jdk 8 底层的数组是:Node[],而非Entry[]
3.首次调用put()方法时,底层创建长度为16的Node[]数组
4.Jdk7底层结构只有数组+链表;jdk8中底层结构:数组+链表+红黑树
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所有数据改为使用红黑树存储

Map中定义的方法

添加、删除、修改操作:

  • Object put(Object key, Object value):将指定key-value添加(修改)到当前map对象中
  • void putAll(Map m):将m中所有key-value对存放到当前map中
  • Object remove(Object key):移除指定key的key-value对,并返回value
  • void claer(): 清空当前map中的所有数据

元素查询:

  • Object get(Object key):获取指定key对应的value
  • boolean containsKey(Object key):是否包含指定的key
  • boolean containsValue(Object value):是否包含指定的value
  • int size():返回map中key-value对的个数
  • boolean is Empty():判断当前map是否为空
  • boolean equals(Object obj):判断当前map和参数对象obj是否相等

元视图操作的方法:

  • Set keySet():返回所有key构成的Set集合

  • Collection values():返回所有value构成的Collection集合

  • Set entrySet():返回所有key-value对构成Set集合

Collections工具类

排序:

  • reverse(List):反转List中元素的顺序
  • shuffle(List):对List集合元素进行随机排序
  • sort(List, Comparator):排序
  • swap(List, int, int):将指定list集合中的i处元素和j处元素进行交换

查找、替换:

  • Object max(Collection):根据元素的自然顺序,返回给定集合中最大元素
  • Object max(Collection, Comparator):略
  • Object min..
  • int frequency(Collection, Object):返回指定集合中指定元素的出现次数
  • void copy(List dest, List src):将src中的内容复制到dest中
  • boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值

泛型

  • 自定义泛型结构:泛型类、泛型接口;泛型方法
  • 泛型不同的引用不能相互赋值
  • 异常类不能声明为泛型类
  • 静态方法中不能使用类的泛型

泛型方法

  • 泛型方法所属的类是不是泛型类都没有关系
  • 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的,并非是在实例化类时确定的

    通配符的使用

IO流

File类

  • 路径分隔符
    • windows:\
    • unix:/

常用构造器

  • public File(String pathname):

  • public File(String parent, String child):

  • public File(File parent, String child):

常用方法

获取

  • public String getAbsolutePath():获取绝对路径
  • public String getPath():获取路径
  • public String getName():获取名称
  • public String getParent():获取上层文件目录路径
  • public long length():获取文件长度
  • public long lastModified():获取最后一次的修改时间,毫秒值

下面的两个方法适用于文件目录

  • public String[] list():获取指定目录下的所有文件或者文件目录的名称数组
  • public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组

重命名

  • public boolean renameTo(File dest):把文件重命名为指定的文件路径

判断

  • public boolean isDirectory():判断是否是文件目录
  • public boolean isFile():判断是否是文件
  • public boolean exists():判断是否存在
  • public boolean canRead():判断是否可读
  • public boolean canWrite():判断是否可写
  • public boolean isHidden():判断是否隐藏

创建

  • public boolean createNewFile():创建文件。若文件存在,则不创建,返回false
  • public boolean mkdir():创建文件目录,如果存在,则不创建
  • public boolean mkdirs():创建文件目录,如果存在,则不创建,如果上层目录不存在,一并创建

删除

注意:java中的删除不走回收站

  • public boolean delete():删除文件或文件夹

IO流原理及流的分类

缓冲流

  • 按操作数据单位分为:字节流(8 bit),字符流(16 bit)
  • 按数据流的流向分为:输入流、输出流
  • 按流的角色不同分为:节点流(作用在文件上)、处理流(作用在已有的流上)
  • 抽象基类:InputStream, Reader, OutputStream, Writer
  • 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
  • 对于文本文件(.txt, .java, .c, .cpp),使用字符流处理;对于非文本文件,使用字节流处理
  • 资源关闭:先关闭外层的流,再关闭内层的流;在关闭外层流的同时,内层流的关闭可以省略

转换流

  • 提供了在字符流和字节流之间的转换

  • 属于字符流

  • InputStreamReader:将一个字节的输入流转换为字符的输入流
  • OutputStreamWriter:将一个字符的输出流转换为字节的输出流
  • 字符集

字符集

  • ASCII:美国标准信息交换码
  • ISO8859-1:拉丁码表。欧洲码表
  • GB2312:中国的中文编码表。
  • GBK:中国的中文编码表升级。
  • Unicode:国际标准码,融合了目前人类使用的所有字符。
  • UTF-8:变长的编码方式,可用1-4个字节来表示一个字符
  • ANSI编码,通常指的是平台的默认编码,例如英文操作系统中是ISO-8859-1,中文系统是GBK
  • Unicode字符集只是定义了字符的集合和唯一编号,Unicode编码,则是对UTF-8等具体编码方案的统称而已,而不是具体的编码方案

标准输入输出流

打印流

数据流

  • 用来读取和写出基本数据类型和String的数据

对象流

  • ObjectInputStream和ObjectOutputStream
  • 用于存储和读取基本数据类型或对象的处理流。还可以将Java中的对象写入到数据源中,也能把对象从数据源中还原回来
  • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
  • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
  • 对象的序列化机制
  • 要想一个Java对象是可序列化的,需要满足相应的要求。
1
2
3
4
5
1.必须实现接口:Serializable 
2.必须当前类提供一个全局常量:serialVersionUID
public static final long serialVersionUID
3.除了当前类需要实现Serializable接口之外,还必须保证内部的所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
4.不能序列化static和transient修饰的属性
  • serialVersionUID用来表明类的不同版本间的兼容性。

随机存取文件流

  • 创建RandomAccessFile类实例需要指定一个mode参数,该参数指定RandomAccessFile的访问模式:

  • r:以只读方式打开

  • rw:打开以便读取和写入

  • rwd:打开以便读取和写入:同步文件内容的更新

  • rws:打开以便读取和写入:同步文件内容和元数据的更新

  • 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建;

    如果写出到的文件存在,则会对原有文件内容进行覆盖(默认从头覆盖)

网络编程

通信要素一:IP和端口

  • java中使用InetAddress类代表IP

  • 域名解析服务器:DNS。将域名解析成IP

  • 本地回路地址:127.0.0.1 对应着:localhost

  • 如何实例化InetAddress:两个方法:getByName(String host), getLocalHost();

    两个常用方法:getHostName()/getHostAddress()

  • 端口号标识正在计算机上运行的进程:不同的进程有不同的端口号;被规定为一个16位的整数0~65535;

  • 端口分类:

    • 公认端口:0~1023。被预先定义的服务通信占用
    • 注册端口:1024~49151.分配给用户进程或应用程序
    • 动态/私有端口:49152~65535。
  • 端口号与IP地址的组合得出一个网络套接字:Socket

通信要素二:网络协议

  • TCP/IP协议簇:传输控制协议(TCP)和网络互联协议(IP)
  • 传输层的两个协议:TCP和UDP(TCP:打电话,UDP:发送短信,发送电报)
    • TCP:1. 传输前,采用三次握手的方式,点对点通信是可靠的。2.在连接中可进行大数据量的传输。3.传输完毕,需要释放已建立的连接,四次挥手,效率低
    • UDP:1.将数据、源、目的封装成数据包,不需要建立连接。2.不管对方是否准备好,接收方也不确认,是不可靠的。3.可以广播发送。4.发送数据结束后无需释放资源,开销小,速度快。

TCP和UDP的网络编程

URL编程

  • URL(Uniform Resource Locator):统一资源定位符,它表示Internet上某一资源的地址
  • 基本结构:<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表

反射

  • Reflection是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
  • Java可以称为“准动态语言”
  • 反射能提供的功能:
    • 在运行时判断任意一个对象所属的类
    • 构造任意一个类的对象
    • 判断任意一个类所具有的成员变量和方法
    • 获取泛型信息
    • 调用任意一个对象的成员变量和方法
    • 处理注解
    • 生成动态代理

关于java.lang.Class类的理解

  1. 类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中 。此过程就称为类的加载。加载到内存中的类,就称为运行时类,此运行时类,就作为Class的一个实例。
  2. 换句话说,Class的实例就对应着一个运行时类。
  3. 获取Class的实例的方式:
1
2
3
4
5
6
7
8
9
10
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
//方式二:通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
//方式三:调用Class的静态方法:forName(String classPath)(用的比较多)
Class clazz3 = Class.forName("Reflection.Person" );
//方式四:使用类的加载器(了解即可)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("Reflection.Person");
  1. 加载到内存中的运行时类,会缓存一定的时间。在此时间内,我们可以通过不同的方式来获取此运行时类。
  2. 哪些类型可以有Class对象:

    1. class:外部类,成员,局部内部类,匿名内部类
    2. interface:接口
    3. []:数组
    4. enum:枚举
    5. annotation:注解@interface
    6. primitive type:基本数据类型
    7. void

    ClassLoader的理解

读取配置文件的两种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Properties pros = new Properties();
//此时的文件默认在当前的Module下

//读取配置文件的方式一:
//FileInputStream fis = new FileInputStream("jdbc.properties");//此时读取的配置文件在module下
//pros.load(fis);

//方式二:
ClassLoader classLoader = ClassLoadertest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");//此时读取的配置文件在src下
pros.load(is);

String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user = " + user + ", password = " + password);

创建运行时类的对象

1
2
3
4
5
6
7
8
Class clazz = Class.forName("Reflection.Person");
//调用newInstrance()创建对应的运行时类的对象,内部调用了运行时类的空参的构造器
//要想此方法正常的创建运行时类的对象,要求:
//1.运行时类必须提供空参的构造器
//2.空参的构造器的访问权限得够。通常,设置为public

Person obj = (Person) clazz.newInstance();
System.out.println(obj);

获取运行时类的属性结构及其内部结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Class clazz = Class.forName("Reflection2.Person");

//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();

for(Field f : fields){
System.out.println(f);
}

//getDeclaredFields():获取当前运行时类当中的所有属性(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
System.out.println(f);
}

Class clazz = Class.forName("Reflection2.Person");
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
//1.权限修饰符
int modifiers = f.getModifiers();
System.out.println(Modifi2 er.toString(modifiers));

//2.数据类型
Class type = f.getType();
System.out.println(type.getName());

//3.变量名
String fName = f.getName();
System.out.println(fName);

System.out.println();

}

获取运行时类的方法结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class clazz = Class.forName("Reflection2.Person");

//getMethods():获取当前运行时类及其所有父类中声明为public 权限的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}

System.out.println();

//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的)
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : methods){
System.out.println(m);
}

调用运行时类的指定结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//如何操作运行时类中的指定的属性
Class clazz = Class.forName("Reflection2.Person");
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性时可访问的
name.setAccessible(true);
//3.获取、设置指定对象的属性值
name.set(p, "Tom");

System.out.println(name.get(p));
//如何操作运行时类中的指定的方法
Class clazz = Class.forName("Reflection2.Person");
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1.获取指定的某个方法
//getDeclaredMethod():参数1:指明获取的方法的名称;参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保证当前的方法时可访问的
show.setAccessible(true);
//3.invoke():参数1:方法的调用者;参数2:方法的实参
//invoke()的返回值即为对应类中调用方法的返回值
//如果调用的运行时类没有返回值,则invoke()返回null
Object chn = show.invoke(p, "CHN");

//调用静态方法
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object returnVal = showDesc.invoke(clazz);//写null
System.out.println(returnVal);

动态代理

学框架的时候再回过头来看

Java 8 新特性(最重要的改变,一个是Lambda表达式,一个是Stream API)

Lambda表达式(这部分以及下面的方法引用部分需要熟练掌握前面的内容才能明白)

格式

  • ->:lambda操作符或箭头操作符
  • ->左边:lambda形参列表(其实就是接口中的抽象方法的形参列表)
  • ->右边:lambda体(其实就是重写的抽象方法的方法体)

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//语法格式一:无参、无返回值
@Test
public void test1(){
Runnable r1 = () -> System.out.println("我爱北京故宫");
}

//二:需要一个参数,但是没有返回值
@Test
public void test2(){
Consumer<String> con1 = (String s) -> System.out.println();

con1.accept("一个是");
}

//三:数据类型可以省略,因为可由编译器推断得出,称为类型推断
@Test
public void test3(){
Consumer<String> con1 = s -> System.out.println();

con1.accept("算法");
}

//四:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test4(){
Comparator<Integer> com1 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
}

//六:当Lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test5(){
Comparator<Integer> com1 = (o1, o2) -> o1.compareTo(o2);
}

本质

  • lambda表达式的本质:作为函数式接口(Functional Interface)的实例
  • 如果一个接口中,只声明了一个抽象方法,就称为函数式接口
  • 我们可以使用@FunctionnalInterface来注解一个接口,来检查是否是一个函数式接口
  • 以后函数式接口的匿名实现类都可以使用Lambda表达式来写

函数式接口

  • Consumer:消费型接口;void accept(T t);
  • Supplier:供给型接口;T get();
  • Function:函数型接口;R apply(T t);
  • Predicate:断定型接口;boolean test(T t);

方法引用(Method References)

  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
  • 本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。

使用格式

  • 类(对象) : : 方法名
  • 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同

具体使用的三种情况

  • 对象 ::非静态方法
  • 类::静态方法
  • 类::非静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");

System.out.println("****************************");

PrintStream ps = System.out;
Consumer<String> con2 = ps :: println;
con2.accept("北京");
====================================================================
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com1.compare(12, 21));

System.out.println("**************************");

Comparator<Integer> com2 = Integer :: compare;
System.out.println(com2.compare(21, 21));
=====================================================================
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("fasd", "fads"));

System.out.println("***************************");

Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("as", "tewa"));

构造器引用和项目引用

Stream API

  • Stream API 对集合数据进行操作,就类似于使用SQL执行的数据库查询

    Stream执行流程

  1. Stream的实例化
  2. 一系列的中间操作(过滤、映射…)
  3. 终止操作

说明

  • 一个中间操作链,对数据源的数据进行处理
  • 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

Stream的实例化

  1. 通过集合
1
2
3
4
5
List<Employee> employees = EmployeeData.getEmployees();
//返回一个顺序流
Stream<Employee> stream = employees.stream();
//返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
  1. 通过数组
1
2
Employee[] arr = new Employee[]{e1, e2, e3};
Stream<Employee> stream = Arrays.stream(arr);
  1. 通过Stream的of()
1
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
  1. 创建无限流
1
2
3
4
//迭代
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out :: println);
//生成
Stream.generate(Math :: random).limit(10).forEach(System.out :: println);

Stream的中间操作

  1. 筛选与切片
1
2
3
4
5
6
7
8
9
10
11
12
13
//filter(Predicate p) -- 接收Lambda,从流中排除某些元素
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
stream.filter(e -> e.getSalary() > 7000).forEach(System.out :: println);

//limit(n) -- 截断流,使其元素不超过给定数量
list.stream().limit(3).forEach(System.out :: println);

//skip(n) -- 跳过元素,返回一个扔掉了前n个元素的流
list.stream().skip(3).forEach(System.out :: println);

//distinct() -- 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
list.stream().distinct().forEach(System.out :: println);
  1. 映射
1
2
3
4
5
6
7
8
9
10
11
12
//map(Function f) -- 接收一个函数作为参数,将元素转换成其他形式或提取信息,
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);

List<Employee> employees = EmployeeData.getEmployees();
Stream<String> namesStream = employees.stream().map(Employee :: getName);
namesStream.filter(name -> name.length() > 3).forEach(System.out :: println);

//flatMap(Function f) -- 将流中的每个值都换成另一个流,然后把所有的流连接成一个流
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest::fromStringToStream);
characterStream.forEach(System.out :: println);
  1. 排序
1
2
3
4
5
6
//sorted():自然排序
List<Integer> list = Arrays.asList(12, 421, 123, 24, 541, 23, 12, 531);
list.stream().sorted().forEach(System.out :: println);
//sorted(Comparator com):定制排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()).forEach(System.out :: println);

Stream的终止操作

  1. 匹配与查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//allMatch(Prredicate p) -- 检查是否匹配所有元素
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
//anyMatch(Predicate p) -- 检查是否至少匹配一个元素
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
//noneMatch(Predicate p) -- 检查是否没有匹配的元素
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
//findFirst -- 返回第一个元素
Optional<Employee> employee = employees.stream().findFirst();
//findAny -- 返回当前流中的任意元素
Optional<Employee> employee1 = employees.parallelStream().findAny();
//count -- 返回流中元素的总个数
long count = employees.stream().count();
//max(Comparator c) -- 返回流中的最大值
Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Optional<Double> maxSalary = salaryStream.max(Double::compare);
//min(Comparator c) -- 返回流中的最小值
Optional<Double> minSalary = salaryStream.min(Double::compare);
//forEach(Consumer c) -- 内部迭代
employees.stream().forEach(System.out :: println);
  1. 归约
1
2
3
4
5
6
7
//reduce(T identity, BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);

//reduce(BinaryOperator) -- 可以将流中的元素反复结合起来,得到一个值
Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
salaryStream.reduce(Double :: sum);
  1. 收集
1
2
3
4
//collet(Collector c) -- 将流转换为其他形式
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out :: println);

Optional类

  • Optional类:为了在程序中避免出现空指针异常

  • 创建Optional的三种方式
    Optional.of(T t):t必须非空
    Optional.empty():创建一个空的Optional实例
    Optional.ofNullable(T t):t可以为null